From 6a8e5a4b96e0c99300d503216f66c2833fa68097 Mon Sep 17 00:00:00 2001 From: "Alam, SahibeX" Date: Tue, 30 Jan 2024 01:17:42 +0530 Subject: [PATCH] ASB - February 2024 Security Patches integration Integrating Security Patches Test done: STS r23 TCs Passed Tracked-On: OAM-115406 Signed-off-by: Alam, SahibeX --- ...4-Update-security_patch_level-string.patch | 2 +- ...hange-open-jpeg-folder-name.bulletin.patch | 35 + ...hrome-114-0-5735-130-pdfium.bulletin.patch | 580941 +++++++++++++++ ...34-Update-mtp-packet-buffer.bulletin.patch | 38 + ...ce-to-launch-activity-from-.bulletin.patch | 316 + ...bb-sanbox-on-shared-storage.bulletin.patch | 543 + ...d-TileService-onNullBinding.bulletin.patch | 116 + ...is-running-in-the-backgroun.bulletin.patch | 83 + ...ssion-of-Autofill-icon-URIs.bulletin.patch | 162 + ...dDocumentsXxx-implementatio.bulletin.patch | 101 + ...se-and-attp_build_value_cmd.bulletin.patch | 47 + ...uild_read_by_type_value_cmd.bulletin.patch | 41 + ...hmem-dev-is-backed-by-memfd.bulletin.patch | 74 + 13 files changed, 582498 insertions(+), 1 deletion(-) create mode 100644 aosp_diff/preliminary/cts/0001-Change-open-jpeg-folder-name.bulletin.patch create mode 100644 aosp_diff/preliminary/external/pdfium/0001-Update-pdfium-to-Chrome-114-0-5735-130-pdfium.bulletin.patch create mode 100644 aosp_diff/preliminary/frameworks/av/34_0034-Update-mtp-packet-buffer.bulletin.patch create mode 100644 aosp_diff/preliminary/frameworks/base/99_0231-DO-NOT-MERGE-Disallow-Wallpaper-service-to-launch-activity-from-.bulletin.patch create mode 100644 aosp_diff/preliminary/frameworks/base/99_0232-DO-NOT-MERGE-Hide-Android-data-obb-sanbox-on-shared-storage.bulletin.patch create mode 100644 aosp_diff/preliminary/frameworks/base/99_0233-Unbind-TileService-onNullBinding.bulletin.patch create mode 100644 aosp_diff/preliminary/frameworks/base/99_0234-Restrict-activity-launch-when-caller-is-running-in-the-backgroun.bulletin.patch create mode 100644 aosp_diff/preliminary/frameworks/base/99_0235--RESTRICT-AUTOMERGE-Check-permission-of-Autofill-icon-URIs.bulletin.patch create mode 100644 aosp_diff/preliminary/packages/providers/DownloadProvider/0003-DO-NOT-MERGE-Consolidate-queryChildDocumentsXxx-implementatio.bulletin.patch create mode 100644 aosp_diff/preliminary/system/bt/50_0050-Fix-an-OOB-bug-in-btif_to_bta_response-and-attp_build_value_cmd.bulletin.patch create mode 100644 aosp_diff/preliminary/system/bt/51_0051-Fix-an-OOB-write-bug-in-attp_build_read_by_type_value_cmd.bulletin.patch create mode 100644 aosp_diff/preliminary/system/core/03_0003-Add-seal-if-ashmem-dev-is-backed-by-memfd.bulletin.patch diff --git a/aosp_diff/preliminary/build/make/04_0004-Update-security_patch_level-string.patch b/aosp_diff/preliminary/build/make/04_0004-Update-security_patch_level-string.patch index 4e0803d7d7..4632563a95 100644 --- a/aosp_diff/preliminary/build/make/04_0004-Update-security_patch_level-string.patch +++ b/aosp_diff/preliminary/build/make/04_0004-Update-security_patch_level-string.patch @@ -20,7 +20,7 @@ index 47bb92c142..2d0ac256a4 100644 # It must match one of the Android Security Patch Level strings of the Public Security Bulletins. # If there is no $PLATFORM_SECURITY_PATCH set, keep it empty. - PLATFORM_SECURITY_PATCH := 2022-02-05 -+ PLATFORM_SECURITY_PATCH := 2024-01-01 ++ PLATFORM_SECURITY_PATCH := 2024-02-01 endif .KATI_READONLY := PLATFORM_SECURITY_PATCH diff --git a/aosp_diff/preliminary/cts/0001-Change-open-jpeg-folder-name.bulletin.patch b/aosp_diff/preliminary/cts/0001-Change-open-jpeg-folder-name.bulletin.patch new file mode 100644 index 0000000000..79c9da6347 --- /dev/null +++ b/aosp_diff/preliminary/cts/0001-Change-open-jpeg-folder-name.bulletin.patch @@ -0,0 +1,35 @@ +From 439eeb3b8723f9c06c4d0caeb9f86c97d712cbae Mon Sep 17 00:00:00 2001 +From: kumarashishg +Date: Wed, 28 Jun 2023 07:15:27 +0000 +Subject: [PATCH] Change open jpeg folder name + +Replace libopenjpeg20 to libopenjpeg as folder name got updated in the +pdfium library + +Bug: 279055389 +Test: Build the code and flash the device and check Print functionality +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:75bd8508b7040f33aed77945d5dfe2dfed4a071c) +Merged-In: I5b3d5740128f6ecc23dd8f9446a4f40c8550fd88 +Change-Id: I5b3d5740128f6ecc23dd8f9446a4f40c8550fd88 +--- + .../securitybulletin/securityPatch/CVE-2016-8332/Android.bp | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-8332/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-8332/Android.bp +index 382d629cdb3..aa676df02d2 100644 +--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-8332/Android.bp ++++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-8332/Android.bp +@@ -30,8 +30,9 @@ cc_test { + ], + static_libs: [ + "libpdfium-libopenjpeg2", ++ "libpdfium-fxcrt", + ], + include_dirs: [ +- "external/pdfium/third_party/libopenjpeg20", ++ "external/pdfium/third_party/libopenjpeg", + ], + } +-- +2.43.0.195.gebba966016-goog + diff --git a/aosp_diff/preliminary/external/pdfium/0001-Update-pdfium-to-Chrome-114-0-5735-130-pdfium.bulletin.patch b/aosp_diff/preliminary/external/pdfium/0001-Update-pdfium-to-Chrome-114-0-5735-130-pdfium.bulletin.patch new file mode 100644 index 0000000000..5aa865a910 --- /dev/null +++ b/aosp_diff/preliminary/external/pdfium/0001-Update-pdfium-to-Chrome-114-0-5735-130-pdfium.bulletin.patch @@ -0,0 +1,580941 @@ +From 72f2f60e36517ab4104ed7c00adc6d57e54a4677 Mon Sep 17 00:00:00 2001 +From: kumarashishg +Date: Fri, 23 Jun 2023 13:21:22 +0000 +Subject: [PATCH] Update pdfium to Chrome 114.0.5735.130 pdfium + +pdfium last commit id: 9505810f6 + +Bug: 279055389 +Test: Build the code and flash the device and check Print functionality +Test: atest FrameworksCoreTests +Test: atest CtsPrintTestCases +Test: atest CtsPdfTestCases +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:9be8c8c38972f6dbca94e38e0ce1e8a816639126) +Merged-In: I2efabeec0d0fa3925bcbeebf36031cee6f7f9fc4 +Change-Id: I2efabeec0d0fa3925bcbeebf36031cee6f7f9fc4 +--- + .clang-format | 10 + + .gitattributes | 1 + + .gn | 46 +- + .style.yapf | 2 + + .vpython => .vpython3 | 40 +- + AUTHORS | 41 +- + Android.bp | 5 +- + BUILD.gn | 310 +- + CONTRIBUTING.md | 205 + + DEPS | 617 +- + DIR_METADATA | 3 + + LICENSE | 2 +- + METADATA | 9 - + PRESUBMIT.py | 281 +- + PRESUBMIT_test.py | 86 + + PRESUBMIT_test_mocks.py | 78 + + README.md | 90 +- + build/build_config.h | 391 +- + build/buildflag.h | 48 + + build_overrides/BUILDCONFIG.gn | 627 + + build_overrides/build.gni | 20 +- + build_overrides/compiler/BUILD.gn | 25 + + build_overrides/gtest.gni | 4 +- + build_overrides/partition_alloc.gni | 11 + + build_overrides/pdfium.gni | 23 +- + codereview.settings | 2 +- + constants/Android.bp | 6 +- + constants/BUILD.gn | 17 +- + constants/access_permissions.h | 21 + + constants/annotation_common.cpp | 37 + + constants/annotation_common.h | 39 +- + constants/annotation_flags.h | 4 +- + constants/appearance.cpp | 23 + + constants/appearance.h | 24 + + constants/ascii.h | 30 + + constants/font_encodings.cpp | 17 + + constants/font_encodings.h | 19 + + constants/form_fields.cpp | 38 + + constants/form_fields.h | 38 +- + constants/form_flags.h | 2 +- + constants/page_object.cpp | 24 + + constants/page_object.h | 25 +- + constants/stream_dict_common.cpp | 22 + + constants/stream_dict_common.h | 19 +- + constants/transparency.cpp | 48 + + constants/transparency.h | 70 +- + core/fdrm/BUILD.gn | 7 +- + core/fdrm/fx_crypt.cpp | 35 +- + core/fdrm/fx_crypt.h | 25 +- + core/fdrm/fx_crypt_aes.cpp | 110 +- + core/fdrm/fx_crypt_sha.cpp | 77 +- + core/fdrm/fx_crypt_unittest.cpp | 175 +- + core/fpdfapi/cmaps/BUILD.gn | 7 +- + core/fpdfapi/cmaps/CNS1/Adobe-CNS1-UCS2_5.cpp | 8 +- + core/fpdfapi/cmaps/CNS1/B5pc-H_0.cpp | 8 +- + core/fpdfapi/cmaps/CNS1/B5pc-V_0.cpp | 8 +- + core/fpdfapi/cmaps/CNS1/CNS-EUC-H_0.cpp | 10 +- + core/fpdfapi/cmaps/CNS1/CNS-EUC-V_0.cpp | 10 +- + core/fpdfapi/cmaps/CNS1/ETen-B5-H_0.cpp | 8 +- + core/fpdfapi/cmaps/CNS1/ETen-B5-V_0.cpp | 8 +- + core/fpdfapi/cmaps/CNS1/ETenms-B5-H_0.cpp | 8 +- + core/fpdfapi/cmaps/CNS1/ETenms-B5-V_0.cpp | 8 +- + core/fpdfapi/cmaps/CNS1/HKscs-B5-H_5.cpp | 8 +- + core/fpdfapi/cmaps/CNS1/HKscs-B5-V_5.cpp | 8 +- + core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-H_3.cpp | 8 +- + core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-V_3.cpp | 8 +- + core/fpdfapi/cmaps/CNS1/UniCNS-UTF16-H_0.cpp | 8 +- + core/fpdfapi/cmaps/CNS1/cmaps_cns1.cpp | 51 +- + core/fpdfapi/cmaps/CNS1/cmaps_cns1.h | 45 +- + core/fpdfapi/cmaps/GB1/Adobe-GB1-UCS2_5.cpp | 8 +- + core/fpdfapi/cmaps/GB1/GB-EUC-H_0.cpp | 8 +- + core/fpdfapi/cmaps/GB1/GB-EUC-V_0.cpp | 8 +- + core/fpdfapi/cmaps/GB1/GBK-EUC-H_2.cpp | 8 +- + core/fpdfapi/cmaps/GB1/GBK-EUC-V_2.cpp | 8 +- + core/fpdfapi/cmaps/GB1/GBK2K-H_5.cpp | 10 +- + core/fpdfapi/cmaps/GB1/GBK2K-V_5.cpp | 8 +- + core/fpdfapi/cmaps/GB1/GBKp-EUC-H_2.cpp | 8 +- + core/fpdfapi/cmaps/GB1/GBKp-EUC-V_2.cpp | 8 +- + core/fpdfapi/cmaps/GB1/GBpc-EUC-H_0.cpp | 8 +- + core/fpdfapi/cmaps/GB1/GBpc-EUC-V_0.cpp | 8 +- + core/fpdfapi/cmaps/GB1/UniGB-UCS2-H_4.cpp | 8 +- + core/fpdfapi/cmaps/GB1/UniGB-UCS2-V_4.cpp | 8 +- + core/fpdfapi/cmaps/GB1/cmaps_gb1.cpp | 50 +- + core/fpdfapi/cmaps/GB1/cmaps_gb1.h | 41 +- + core/fpdfapi/cmaps/Japan1/83pv-RKSJ-H_1.cpp | 8 +- + core/fpdfapi/cmaps/Japan1/90ms-RKSJ-H_2.cpp | 8 +- + core/fpdfapi/cmaps/Japan1/90ms-RKSJ-V_2.cpp | 8 +- + core/fpdfapi/cmaps/Japan1/90msp-RKSJ-H_2.cpp | 8 +- + core/fpdfapi/cmaps/Japan1/90msp-RKSJ-V_2.cpp | 8 +- + core/fpdfapi/cmaps/Japan1/90pv-RKSJ-H_1.cpp | 8 +- + core/fpdfapi/cmaps/Japan1/Add-RKSJ-H_1.cpp | 8 +- + core/fpdfapi/cmaps/Japan1/Add-RKSJ-V_1.cpp | 8 +- + .../cmaps/Japan1/Adobe-Japan1-UCS2_4.cpp | 8 +- + core/fpdfapi/cmaps/Japan1/EUC-H_1.cpp | 8 +- + core/fpdfapi/cmaps/Japan1/EUC-V_1.cpp | 8 +- + core/fpdfapi/cmaps/Japan1/Ext-RKSJ-H_2.cpp | 8 +- + core/fpdfapi/cmaps/Japan1/Ext-RKSJ-V_2.cpp | 8 +- + core/fpdfapi/cmaps/Japan1/H_1.cpp | 8 +- + .../cmaps/Japan1/UniJIS-UCS2-HW-H_4.cpp | 8 +- + .../cmaps/Japan1/UniJIS-UCS2-HW-V_4.cpp | 8 +- + core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-H_4.cpp | 8 +- + core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-V_4.cpp | 8 +- + core/fpdfapi/cmaps/Japan1/V_1.cpp | 8 +- + core/fpdfapi/cmaps/Japan1/cmaps_japan1.cpp | 66 +- + core/fpdfapi/cmaps/Japan1/cmaps_japan1.h | 57 +- + .../cmaps/Korea1/Adobe-Korea1-UCS2_2.cpp | 8 +- + core/fpdfapi/cmaps/Korea1/KSC-EUC-H_0.cpp | 8 +- + core/fpdfapi/cmaps/Korea1/KSC-EUC-V_0.cpp | 8 +- + .../fpdfapi/cmaps/Korea1/KSCms-UHC-HW-H_1.cpp | 8 +- + .../fpdfapi/cmaps/Korea1/KSCms-UHC-HW-V_1.cpp | 8 +- + core/fpdfapi/cmaps/Korea1/KSCms-UHC-H_1.cpp | 8 +- + core/fpdfapi/cmaps/Korea1/KSCms-UHC-V_1.cpp | 8 +- + core/fpdfapi/cmaps/Korea1/KSCpc-EUC-H_0.cpp | 8 +- + core/fpdfapi/cmaps/Korea1/UniKS-UCS2-H_1.cpp | 8 +- + core/fpdfapi/cmaps/Korea1/UniKS-UCS2-V_1.cpp | 8 +- + core/fpdfapi/cmaps/Korea1/UniKS-UTF16-H_0.cpp | 8 +- + core/fpdfapi/cmaps/Korea1/cmaps_korea1.cpp | 44 +- + core/fpdfapi/cmaps/Korea1/cmaps_korea1.h | 35 +- + core/fpdfapi/cmaps/fpdf_cmaps.cpp | 47 +- + core/fpdfapi/cmaps/fpdf_cmaps.h | 27 +- + core/fpdfapi/edit/Android.bp | 5 +- + core/fpdfapi/edit/BUILD.gn | 9 +- + .../edit/cpdf_contentstream_write_utils.cpp | 36 +- + .../edit/cpdf_contentstream_write_utils.h | 9 +- + core/fpdfapi/edit/cpdf_creator.cpp | 221 +- + core/fpdfapi/edit/cpdf_creator.h | 7 +- + .../edit/cpdf_creator_embeddertest.cpp | 22 +- + .../edit/cpdf_pagecontentgenerator.cpp | 442 +- + core/fpdfapi/edit/cpdf_pagecontentgenerator.h | 47 +- + .../cpdf_pagecontentgenerator_unittest.cpp | 289 +- + core/fpdfapi/edit/cpdf_pagecontentmanager.cpp | 260 +- + core/fpdfapi/edit/cpdf_pagecontentmanager.h | 47 +- + .../fpdfapi/edit/cpdf_stringarchivestream.cpp | 27 +- + core/fpdfapi/edit/cpdf_stringarchivestream.h | 14 +- + core/fpdfapi/font/BUILD.gn | 17 +- + core/fpdfapi/font/cfx_cttgsubtable.cpp | 191 +- + core/fpdfapi/font/cfx_cttgsubtable.h | 82 +- + core/fpdfapi/font/cfx_stockfontarray.cpp | 21 +- + core/fpdfapi/font/cfx_stockfontarray.h | 7 +- + core/fpdfapi/font/cpdf_cid2unicodemap.cpp | 2 +- + core/fpdfapi/font/cpdf_cid2unicodemap.h | 2 +- + core/fpdfapi/font/cpdf_cidfont.cpp | 494 +- + core/fpdfapi/font/cpdf_cidfont.h | 41 +- + core/fpdfapi/font/cpdf_cidfont_unittest.cpp | 20 +- + core/fpdfapi/font/cpdf_cmap.cpp | 122 +- + core/fpdfapi/font/cpdf_cmap.h | 47 +- + core/fpdfapi/font/cpdf_cmapmanager.cpp | 48 - + core/fpdfapi/font/cpdf_cmapmanager.h | 30 - + core/fpdfapi/font/cpdf_cmapparser.cpp | 32 +- + core/fpdfapi/font/cpdf_cmapparser.h | 11 +- + .../fpdfapi/font/cpdf_cmapparser_unittest.cpp | 4 +- + core/fpdfapi/font/cpdf_font.cpp | 156 +- + core/fpdfapi/font/cpdf_font.h | 74 +- + core/fpdfapi/font/cpdf_fontencoding.cpp | 201 +- + core/fpdfapi/font/cpdf_fontencoding.h | 40 +- + core/fpdfapi/font/cpdf_fontglobals.cpp | 77 +- + core/fpdfapi/font/cpdf_fontglobals.h | 29 +- + core/fpdfapi/font/cpdf_simplefont.cpp | 194 +- + core/fpdfapi/font/cpdf_simplefont.h | 26 +- + core/fpdfapi/font/cpdf_tounicodemap.cpp | 111 +- + core/fpdfapi/font/cpdf_tounicodemap.h | 26 +- + .../font/cpdf_tounicodemap_unittest.cpp | 72 +- + core/fpdfapi/font/cpdf_truetypefont.cpp | 235 +- + core/fpdfapi/font/cpdf_truetypefont.h | 18 +- + core/fpdfapi/font/cpdf_type1font.cpp | 211 +- + core/fpdfapi/font/cpdf_type1font.h | 21 +- + core/fpdfapi/font/cpdf_type3char.cpp | 12 +- + core/fpdfapi/font/cpdf_type3char.h | 13 +- + core/fpdfapi/font/cpdf_type3font.cpp | 45 +- + core/fpdfapi/font/cpdf_type3font.h | 17 +- + core/fpdfapi/page/Android.bp | 5 +- + core/fpdfapi/page/BUILD.gn | 36 +- + core/fpdfapi/page/cpdf_allstates.cpp | 56 +- + core/fpdfapi/page/cpdf_allstates.h | 8 +- + core/fpdfapi/page/cpdf_annotcontext.cpp | 28 +- + core/fpdfapi/page/cpdf_annotcontext.h | 15 +- + core/fpdfapi/page/cpdf_basedcs.cpp | 17 + + core/fpdfapi/page/cpdf_basedcs.h | 29 + + core/fpdfapi/page/cpdf_clippath.cpp | 27 +- + core/fpdfapi/page/cpdf_clippath.h | 16 +- + core/fpdfapi/page/cpdf_color.cpp | 53 +- + core/fpdfapi/page/cpdf_color.h | 17 +- + core/fpdfapi/page/cpdf_colorspace.cpp | 786 +- + core/fpdfapi/page/cpdf_colorspace.h | 108 +- + .../fpdfapi/page/cpdf_colorspace_unittest.cpp | 52 + + core/fpdfapi/page/cpdf_colorstate.cpp | 91 +- + core/fpdfapi/page/cpdf_colorstate.h | 34 +- + core/fpdfapi/page/cpdf_contentmarkitem.cpp | 23 +- + core/fpdfapi/page/cpdf_contentmarkitem.h | 14 +- + core/fpdfapi/page/cpdf_contentmarks.cpp | 60 +- + core/fpdfapi/page/cpdf_contentmarks.h | 16 +- + core/fpdfapi/page/cpdf_contentparser.cpp | 145 +- + core/fpdfapi/page/cpdf_contentparser.h | 37 +- + core/fpdfapi/page/cpdf_devicecs.cpp | 101 +- + core/fpdfapi/page/cpdf_devicecs.h | 17 +- + core/fpdfapi/page/cpdf_devicecs_unittest.cpp | 11 +- + core/fpdfapi/page/cpdf_dib.cpp | 1185 +- + core/fpdfapi/page/cpdf_dib.h | 116 +- + core/fpdfapi/page/cpdf_docpagedata.cpp | 343 +- + core/fpdfapi/page/cpdf_docpagedata.h | 59 +- + core/fpdfapi/page/cpdf_expintfunc.cpp | 38 +- + core/fpdfapi/page/cpdf_expintfunc.h | 31 +- + core/fpdfapi/page/cpdf_form.cpp | 59 +- + core/fpdfapi/page/cpdf_form.h | 19 +- + core/fpdfapi/page/cpdf_formobject.cpp | 12 +- + core/fpdfapi/page/cpdf_formobject.h | 3 +- + core/fpdfapi/page/cpdf_function.cpp | 122 +- + core/fpdfapi/page/cpdf_function.h | 38 +- + core/fpdfapi/page/cpdf_function_unittest.cpp | 43 + + core/fpdfapi/page/cpdf_generalstate.cpp | 49 +- + core/fpdfapi/page/cpdf_generalstate.h | 29 +- + core/fpdfapi/page/cpdf_graphicstates.cpp | 6 +- + core/fpdfapi/page/cpdf_graphicstates.h | 2 +- + core/fpdfapi/page/cpdf_iccprofile.cpp | 27 +- + core/fpdfapi/page/cpdf_iccprofile.h | 26 +- + core/fpdfapi/page/cpdf_image.cpp | 233 +- + core/fpdfapi/page/cpdf_image.h | 42 +- + core/fpdfapi/page/cpdf_imageloader.cpp | 80 + + .../{render => page}/cpdf_imageloader.h | 31 +- + core/fpdfapi/page/cpdf_imageobject.cpp | 39 +- + core/fpdfapi/page/cpdf_imageobject.h | 6 +- + core/fpdfapi/page/cpdf_indexedcs.cpp | 109 + + core/fpdfapi/page/cpdf_indexedcs.h | 43 + + core/fpdfapi/page/cpdf_meshstream.cpp | 77 +- + core/fpdfapi/page/cpdf_meshstream.h | 47 +- + core/fpdfapi/page/cpdf_occontext.cpp | 131 +- + core/fpdfapi/page/cpdf_occontext.h | 18 +- + core/fpdfapi/page/cpdf_page.cpp | 91 +- + core/fpdfapi/page/cpdf_page.h | 73 +- + core/fpdfapi/page/cpdf_pageimagecache.cpp | 262 + + core/fpdfapi/page/cpdf_pageimagecache.h | 107 + + .../page/cpdf_pageimagecache_unittest.cpp | 81 + + core/fpdfapi/page/cpdf_pagemodule.cpp | 34 +- + core/fpdfapi/page/cpdf_pagemodule.h | 6 +- + core/fpdfapi/page/cpdf_pageobject.cpp | 4 +- + core/fpdfapi/page/cpdf_pageobject.h | 49 +- + core/fpdfapi/page/cpdf_pageobjectholder.cpp | 76 +- + core/fpdfapi/page/cpdf_pageobjectholder.h | 56 +- + .../page/cpdf_pageobjectholder_unittest.cpp | 19 +- + core/fpdfapi/page/cpdf_path.cpp | 31 +- + core/fpdfapi/page/cpdf_path.h | 19 +- + core/fpdfapi/page/cpdf_pathobject.cpp | 12 +- + core/fpdfapi/page/cpdf_pathobject.h | 44 +- + core/fpdfapi/page/cpdf_pattern.cpp | 17 +- + core/fpdfapi/page/cpdf_pattern.h | 16 +- + core/fpdfapi/page/cpdf_patterncs.cpp | 26 +- + core/fpdfapi/page/cpdf_patterncs.h | 29 +- + core/fpdfapi/page/cpdf_psengine.cpp | 72 +- + core/fpdfapi/page/cpdf_psengine.h | 15 +- + core/fpdfapi/page/cpdf_psengine_unittest.cpp | 104 +- + core/fpdfapi/page/cpdf_psfunc.cpp | 13 +- + core/fpdfapi/page/cpdf_psfunc.h | 12 +- + core/fpdfapi/page/cpdf_sampledfunc.cpp | 46 +- + core/fpdfapi/page/cpdf_sampledfunc.h | 17 +- + core/fpdfapi/page/cpdf_shadingobject.cpp | 24 +- + core/fpdfapi/page/cpdf_shadingobject.h | 4 +- + core/fpdfapi/page/cpdf_shadingpattern.cpp | 46 +- + core/fpdfapi/page/cpdf_shadingpattern.h | 14 +- + core/fpdfapi/page/cpdf_stitchfunc.cpp | 45 +- + core/fpdfapi/page/cpdf_stitchfunc.h | 11 +- + .../fpdfapi/page/cpdf_streamcontentparser.cpp | 809 +- + core/fpdfapi/page/cpdf_streamcontentparser.h | 82 +- + .../cpdf_streamcontentparser_unittest.cpp | 2 +- + core/fpdfapi/page/cpdf_streamparser.cpp | 228 +- + core/fpdfapi/page/cpdf_streamparser.h | 17 +- + .../page/cpdf_streamparser_unittest.cpp | 2 +- + core/fpdfapi/page/cpdf_textobject.cpp | 244 +- + core/fpdfapi/page/cpdf_textobject.h | 39 +- + core/fpdfapi/page/cpdf_textstate.cpp | 40 +- + core/fpdfapi/page/cpdf_textstate.h | 32 +- + core/fpdfapi/page/cpdf_tilingpattern.cpp | 25 +- + core/fpdfapi/page/cpdf_tilingpattern.h | 14 +- + core/fpdfapi/page/cpdf_transferfunc.cpp | 36 +- + core/fpdfapi/page/cpdf_transferfunc.h | 32 +- + core/fpdfapi/page/cpdf_transferfuncdib.cpp | 179 +- + core/fpdfapi/page/cpdf_transferfuncdib.h | 33 +- + core/fpdfapi/page/cpdf_transparency.cpp | 5 +- + core/fpdfapi/page/cpdf_transparency.h | 3 +- + core/fpdfapi/page/ipdf_page.h | 8 +- + core/fpdfapi/page/test_with_page_module.cpp | 15 + + core/fpdfapi/page/test_with_page_module.h | 16 + + core/fpdfapi/parser/Android.bp | 5 +- + core/fpdfapi/parser/BUILD.gn | 38 +- + core/fpdfapi/parser/cfdf_document.cpp | 41 +- + core/fpdfapi/parser/cfdf_document.h | 5 +- + core/fpdfapi/parser/cpdf_array.cpp | 193 +- + core/fpdfapi/parser/cpdf_array.h | 151 +- + core/fpdfapi/parser/cpdf_array_unittest.cpp | 172 +- + core/fpdfapi/parser/cpdf_boolean.cpp | 13 +- + core/fpdfapi/parser/cpdf_boolean.h | 17 +- + core/fpdfapi/parser/cpdf_cross_ref_avail.cpp | 86 +- + core/fpdfapi/parser/cpdf_cross_ref_avail.h | 14 +- + .../parser/cpdf_cross_ref_avail_unittest.cpp | 105 +- + core/fpdfapi/parser/cpdf_cross_ref_table.cpp | 26 +- + core/fpdfapi/parser/cpdf_cross_ref_table.h | 41 +- + core/fpdfapi/parser/cpdf_crypto_handler.cpp | 251 +- + core/fpdfapi/parser/cpdf_crypto_handler.h | 52 +- + core/fpdfapi/parser/cpdf_data_avail.cpp | 561 +- + core/fpdfapi/parser/cpdf_data_avail.h | 96 +- + core/fpdfapi/parser/cpdf_dictionary.cpp | 273 +- + core/fpdfapi/parser/cpdf_dictionary.h | 130 +- + .../parser/cpdf_dictionary_unittest.cpp | 44 + + core/fpdfapi/parser/cpdf_document.cpp | 385 +- + core/fpdfapi/parser/cpdf_document.h | 111 +- + .../fpdfapi/parser/cpdf_document_unittest.cpp | 273 +- + core/fpdfapi/parser/cpdf_encryptor.cpp | 20 +- + core/fpdfapi/parser/cpdf_encryptor.h | 11 +- + core/fpdfapi/parser/cpdf_flateencoder.cpp | 68 +- + core/fpdfapi/parser/cpdf_flateencoder.h | 35 +- + core/fpdfapi/parser/cpdf_hint_tables.cpp | 60 +- + core/fpdfapi/parser/cpdf_hint_tables.h | 19 +- + .../parser/cpdf_hint_tables_unittest.cpp | 58 +- + .../parser/cpdf_indirect_object_holder.cpp | 58 +- + .../parser/cpdf_indirect_object_holder.h | 56 +- + .../cpdf_indirect_object_holder_unittest.cpp | 36 +- + .../fpdfapi/parser/cpdf_linearized_header.cpp | 17 +- + core/fpdfapi/parser/cpdf_linearized_header.h | 9 +- + core/fpdfapi/parser/cpdf_name.cpp | 22 +- + core/fpdfapi/parser/cpdf_name.h | 22 +- + core/fpdfapi/parser/cpdf_null.cpp | 13 +- + core/fpdfapi/parser/cpdf_null.h | 10 +- + core/fpdfapi/parser/cpdf_number.cpp | 17 +- + core/fpdfapi/parser/cpdf_number.h | 25 +- + core/fpdfapi/parser/cpdf_object.cpp | 117 +- + core/fpdfapi/parser/cpdf_object.h | 115 +- + core/fpdfapi/parser/cpdf_object_avail.cpp | 75 +- + core/fpdfapi/parser/cpdf_object_avail.h | 17 +- + .../parser/cpdf_object_avail_unittest.cpp | 148 +- + core/fpdfapi/parser/cpdf_object_stream.cpp | 83 +- + core/fpdfapi/parser/cpdf_object_stream.h | 44 +- + .../parser/cpdf_object_stream_unittest.cpp | 509 + + core/fpdfapi/parser/cpdf_object_unittest.cpp | 372 +- + core/fpdfapi/parser/cpdf_object_walker.cpp | 87 +- + core/fpdfapi/parser/cpdf_object_walker.h | 24 +- + .../parser/cpdf_object_walker_unittest.cpp | 70 +- + .../fpdfapi/parser/cpdf_page_object_avail.cpp | 9 +- + core/fpdfapi/parser/cpdf_page_object_avail.h | 2 +- + .../cpdf_page_object_avail_unittest.cpp | 42 +- + core/fpdfapi/parser/cpdf_parser.cpp | 657 +- + core/fpdfapi/parser/cpdf_parser.h | 70 +- + .../parser/cpdf_parser_embeddertest.cpp | 47 +- + core/fpdfapi/parser/cpdf_parser_unittest.cpp | 203 +- + core/fpdfapi/parser/cpdf_read_validator.cpp | 52 +- + core/fpdfapi/parser/cpdf_read_validator.h | 45 +- + .../parser/cpdf_read_validator_unittest.cpp | 128 +- + core/fpdfapi/parser/cpdf_reference.cpp | 73 +- + core/fpdfapi/parser/cpdf_reference.h | 38 +- + core/fpdfapi/parser/cpdf_security_handler.cpp | 106 +- + core/fpdfapi/parser/cpdf_security_handler.h | 29 +- + .../cpdf_security_handler_embeddertest.cpp | 59 +- + .../parser/cpdf_seekablemultistream.cpp | 49 +- + .../fpdfapi/parser/cpdf_seekablemultistream.h | 18 +- + .../cpdf_seekablemultistream_unittest.cpp | 76 +- + core/fpdfapi/parser/cpdf_simple_parser.cpp | 8 +- + core/fpdfapi/parser/cpdf_simple_parser.h | 7 +- + .../parser/cpdf_simple_parser_unittest.cpp | 6 +- + core/fpdfapi/parser/cpdf_stream.cpp | 182 +- + core/fpdfapi/parser/cpdf_stream.h | 94 +- + core/fpdfapi/parser/cpdf_stream_acc.cpp | 127 +- + core/fpdfapi/parser/cpdf_stream_acc.h | 40 +- + .../parser/cpdf_stream_acc_unittest.cpp | 23 +- + core/fpdfapi/parser/cpdf_string.cpp | 31 +- + core/fpdfapi/parser/cpdf_string.h | 22 +- + core/fpdfapi/parser/cpdf_syntax_parser.cpp | 368 +- + core/fpdfapi/parser/cpdf_syntax_parser.h | 53 +- + .../parser/cpdf_syntax_parser_unittest.cpp | 74 +- + core/fpdfapi/parser/cpdf_test_document.cpp | 19 + + core/fpdfapi/parser/cpdf_test_document.h | 20 + + core/fpdfapi/parser/fpdf_parser_decode.cpp | 156 +- + core/fpdfapi/parser/fpdf_parser_decode.h | 50 +- + .../fpdf_parser_decode_embeddertest.cpp | 114 +- + .../parser/fpdf_parser_decode_unittest.cpp | 274 +- + core/fpdfapi/parser/fpdf_parser_utility.cpp | 74 +- + core/fpdfapi/parser/fpdf_parser_utility.h | 22 +- + .../parser/fpdf_parser_utility_unittest.cpp | 42 +- + .../parser/object_tree_traversal_util.cpp | 221 + + .../parser/object_tree_traversal_util.h | 46 + + ...bject_tree_traversal_util_embeddertest.cpp | 96 + + core/fpdfapi/render/Android.bp | 5 +- + core/fpdfapi/render/BUILD.gn | 22 +- + core/fpdfapi/render/charposlist.cpp | 196 + + core/fpdfapi/render/charposlist.h | 24 + + core/fpdfapi/render/cpdf_charposlist.cpp | 140 - + core/fpdfapi/render/cpdf_charposlist.h | 31 - + core/fpdfapi/render/cpdf_devicebuffer.cpp | 11 +- + core/fpdfapi/render/cpdf_devicebuffer.h | 2 +- + core/fpdfapi/render/cpdf_docrenderdata.cpp | 73 +- + core/fpdfapi/render/cpdf_docrenderdata.h | 30 +- + .../render/cpdf_docrenderdata_unittest.cpp | 145 +- + core/fpdfapi/render/cpdf_imagecacheentry.cpp | 124 - + core/fpdfapi/render/cpdf_imagecacheentry.h | 62 - + core/fpdfapi/render/cpdf_imageloader.cpp | 79 - + core/fpdfapi/render/cpdf_imagerenderer.cpp | 294 +- + core/fpdfapi/render/cpdf_imagerenderer.h | 31 +- + core/fpdfapi/render/cpdf_pagerendercache.cpp | 133 - + core/fpdfapi/render/cpdf_pagerendercache.h | 60 - + .../fpdfapi/render/cpdf_pagerendercontext.cpp | 6 +- + core/fpdfapi/render/cpdf_pagerendercontext.h | 4 +- + .../render/cpdf_progressiverenderer.cpp | 29 +- + .../fpdfapi/render/cpdf_progressiverenderer.h | 10 +- + core/fpdfapi/render/cpdf_rendercontext.cpp | 45 +- + core/fpdfapi/render/cpdf_rendercontext.h | 47 +- + core/fpdfapi/render/cpdf_renderoptions.cpp | 33 +- + core/fpdfapi/render/cpdf_renderoptions.h | 41 +- + core/fpdfapi/render/cpdf_rendershading.cpp | 594 +- + core/fpdfapi/render/cpdf_rendershading.h | 2 +- + core/fpdfapi/render/cpdf_renderstatus.cpp | 1028 +- + core/fpdfapi/render/cpdf_renderstatus.h | 102 +- + core/fpdfapi/render/cpdf_rendertiling.cpp | 250 + + core/fpdfapi/render/cpdf_rendertiling.h | 35 + + .../render/cpdf_scaledrenderbuffer.cpp | 34 +- + core/fpdfapi/render/cpdf_scaledrenderbuffer.h | 6 +- + core/fpdfapi/render/cpdf_textrenderer.cpp | 113 +- + core/fpdfapi/render/cpdf_textrenderer.h | 25 +- + core/fpdfapi/render/cpdf_type3cache.cpp | 116 +- + core/fpdfapi/render/cpdf_type3cache.h | 19 +- + core/fpdfapi/render/cpdf_type3glyphmap.cpp | 8 +- + core/fpdfapi/render/cpdf_type3glyphmap.h | 6 +- + .../render/cpdf_windowsrenderdevice.cpp | 10 +- + .../fpdfapi/render/cpdf_windowsrenderdevice.h | 6 +- + .../fpdf_progressive_render_embeddertest.cpp | 410 +- + .../fpdf_render_pattern_embeddertest.cpp | 9 +- + core/fpdfdoc/Android.bp | 5 +- + core/fpdfdoc/BUILD.gn | 35 +- + core/fpdfdoc/cba_fontmap.cpp | 493 - + core/fpdfdoc/cline.cpp | 38 - + core/fpdfdoc/cline.h | 26 - + core/fpdfdoc/cpdf_aaction.cpp | 15 +- + core/fpdfdoc/cpdf_aaction.h | 11 +- + core/fpdfdoc/cpdf_action.cpp | 160 +- + core/fpdfdoc/cpdf_action.h | 57 +- + core/fpdfdoc/cpdf_action_unittest.cpp | 127 + + core/fpdfdoc/cpdf_annot.cpp | 227 +- + core/fpdfdoc/cpdf_annot.h | 63 +- + core/fpdfdoc/cpdf_annot_unittest.cpp | 11 +- + core/fpdfdoc/cpdf_annotlist.cpp | 123 +- + core/fpdfdoc/cpdf_annotlist.h | 32 +- + core/fpdfdoc/cpdf_apsettings.cpp | 109 +- + core/fpdfdoc/cpdf_apsettings.h | 50 +- + core/fpdfdoc/cpdf_bafontmap.cpp | 432 + + .../{cba_fontmap.h => cpdf_bafontmap.h} | 57 +- + core/fpdfdoc/cpdf_bafontmap_unittest.cpp | 63 + + core/fpdfdoc/cpdf_bookmark.cpp | 41 +- + core/fpdfdoc/cpdf_bookmark.h | 7 +- + core/fpdfdoc/cpdf_bookmarktree.cpp | 37 +- + core/fpdfdoc/cpdf_bookmarktree.h | 11 +- + core/fpdfdoc/cpdf_color_utils.cpp | 31 +- + core/fpdfdoc/cpdf_color_utils.h | 2 +- + core/fpdfdoc/cpdf_defaultappearance.cpp | 92 +- + core/fpdfdoc/cpdf_defaultappearance.h | 34 +- + .../cpdf_defaultappearance_unittest.cpp | 12 +- + core/fpdfdoc/cpdf_dest.cpp | 72 +- + core/fpdfdoc/cpdf_dest.h | 23 +- + core/fpdfdoc/cpdf_dest_unittest.cpp | 35 +- + core/fpdfdoc/cpdf_filespec.cpp | 83 +- + core/fpdfdoc/cpdf_filespec.h | 20 +- + core/fpdfdoc/cpdf_filespec_unittest.cpp | 110 +- + core/fpdfdoc/cpdf_formcontrol.cpp | 192 +- + core/fpdfdoc/cpdf_formcontrol.h | 77 +- + core/fpdfdoc/cpdf_formfield.cpp | 608 +- + core/fpdfdoc/cpdf_formfield.h | 73 +- + core/fpdfdoc/cpdf_formfield_unittest.cpp | 314 +- + ...pvt_generateap.cpp => cpdf_generateap.cpp} | 557 +- + .../{cpvt_generateap.h => cpdf_generateap.h} | 17 +- + core/fpdfdoc/cpdf_icon.cpp | 15 +- + core/fpdfdoc/cpdf_icon.h | 8 +- + core/fpdfdoc/cpdf_iconfit.cpp | 81 +- + core/fpdfdoc/cpdf_iconfit.h | 15 +- + core/fpdfdoc/cpdf_interactiveform.cpp | 942 +- + core/fpdfdoc/cpdf_interactiveform.h | 82 +- + core/fpdfdoc/cpdf_link.cpp | 21 +- + core/fpdfdoc/cpdf_link.h | 9 +- + core/fpdfdoc/cpdf_linklist.cpp | 55 +- + core/fpdfdoc/cpdf_linklist.h | 13 +- + core/fpdfdoc/cpdf_metadata.cpp | 15 +- + core/fpdfdoc/cpdf_metadata.h | 4 +- + core/fpdfdoc/cpdf_metadata_unittest.cpp | 18 +- + core/fpdfdoc/cpdf_nametree.cpp | 470 +- + core/fpdfdoc/cpdf_nametree.h | 40 +- + core/fpdfdoc/cpdf_nametree_unittest.cpp | 444 +- + core/fpdfdoc/cpdf_numbertree.cpp | 25 +- + core/fpdfdoc/cpdf_numbertree.h | 6 +- + core/fpdfdoc/cpdf_pagelabel.cpp | 38 +- + core/fpdfdoc/cpdf_pagelabel.h | 9 +- + core/fpdfdoc/cpdf_structelement.cpp | 142 +- + core/fpdfdoc/cpdf_structelement.h | 72 +- + core/fpdfdoc/cpdf_structtree.cpp | 86 +- + core/fpdfdoc/cpdf_structtree.h | 29 +- + core/fpdfdoc/cpdf_variabletext.cpp | 937 -- + core/fpdfdoc/cpdf_viewerpreferences.cpp | 36 +- + core/fpdfdoc/cpdf_viewerpreferences.h | 16 +- + core/fpdfdoc/cpvt_floatrect.h | 2 +- + core/fpdfdoc/cpvt_fontmap.cpp | 64 +- + core/fpdfdoc/cpvt_fontmap.h | 18 +- + core/fpdfdoc/cpvt_line.h | 3 +- + core/fpdfdoc/cpvt_lineinfo.h | 35 +- + .../{ctypeset.cpp => cpvt_section.cpp} | 464 +- + core/fpdfdoc/cpvt_section.h | 90 + + core/fpdfdoc/cpvt_variabletext.cpp | 894 ++ + ...pdf_variabletext.h => cpvt_variabletext.h} | 89 +- + core/fpdfdoc/cpvt_word.h | 10 +- + core/fpdfdoc/cpvt_wordinfo.cpp | 12 +- + core/fpdfdoc/cpvt_wordinfo.h | 10 +- + core/fpdfdoc/cpvt_wordplace.h | 12 +- + core/fpdfdoc/cpvt_wordrange.h | 3 +- + core/fpdfdoc/csection.cpp | 252 - + core/fpdfdoc/csection.h | 63 - + core/fpdfdoc/ctypeset.h | 35 - + core/fpdfdoc/ipvt_fontmap.h | 10 +- + core/fpdftext/BUILD.gn | 4 +- + core/fpdftext/cpdf_linkextract.cpp | 81 +- + core/fpdftext/cpdf_linkextract.h | 28 +- + core/fpdftext/cpdf_linkextract_unittest.cpp | 60 +- + core/fpdftext/cpdf_textpage.cpp | 486 +- + core/fpdftext/cpdf_textpage.h | 59 +- + core/fpdftext/cpdf_textpagefind.cpp | 45 +- + core/fpdftext/cpdf_textpagefind.h | 17 +- + core/fpdftext/unicodenormalizationdata.cpp | 12 +- + core/fpdftext/unicodenormalizationdata.h | 12 +- + core/fxcodec/Android.bp | 3 +- + core/fxcodec/BUILD.gn | 58 +- + core/fxcodec/basic/a85_unittest.cpp | 121 +- + core/fxcodec/basic/basicmodule.cpp | 180 +- + core/fxcodec/basic/basicmodule.h | 16 +- + core/fxcodec/basic/rle_unittest.cpp | 77 +- + core/fxcodec/bmp/bmp_decoder.cpp | 75 + + core/fxcodec/bmp/bmp_decoder.h | 61 + + core/fxcodec/bmp/bmp_progressive_decoder.cpp | 33 + + core/fxcodec/bmp/bmp_progressive_decoder.h | 39 + + core/fxcodec/bmp/bmpmodule.cpp | 70 - + core/fxcodec/bmp/bmpmodule.h | 56 - + core/fxcodec/bmp/cfx_bmpcontext.cpp | 7 +- + core/fxcodec/bmp/cfx_bmpcontext.h | 11 +- + core/fxcodec/bmp/cfx_bmpdecompressor.cpp | 396 +- + core/fxcodec/bmp/cfx_bmpdecompressor.h | 42 +- + core/fxcodec/bmp/fx_bmp.h | 2 +- + core/fxcodec/cfx_codec_memory.cpp | 12 +- + core/fxcodec/cfx_codec_memory.h | 15 +- + core/fxcodec/fax/faxmodule.cpp | 191 +- + core/fxcodec/fax/faxmodule.h | 25 +- + core/fxcodec/flate/flatemodule.cpp | 191 +- + core/fxcodec/flate/flatemodule.h | 11 +- + core/fxcodec/fx_codec.cpp | 91 +- + core/fxcodec/fx_codec.h | 116 +- + core/fxcodec/fx_codec_def.h | 30 +- + core/fxcodec/gif/cfx_gif.cpp | 6 +- + core/fxcodec/gif/cfx_gif.h | 23 +- + core/fxcodec/gif/cfx_gifcontext.cpp | 269 +- + core/fxcodec/gif/cfx_gifcontext.h | 41 +- + core/fxcodec/gif/cfx_gifcontext_unittest.cpp | 63 +- + core/fxcodec/gif/cfx_lzwdecompressor.h | 64 - + core/fxcodec/gif/gif_decoder.cpp | 74 + + core/fxcodec/gif/gif_decoder.h | 70 + + core/fxcodec/gif/gif_progressive_decoder.cpp | 33 + + core/fxcodec/gif/gif_progressive_decoder.h | 39 + + core/fxcodec/gif/gifmodule.cpp | 71 - + core/fxcodec/gif/gifmodule.h | 62 - + ...wdecompressor.cpp => lzw_decompressor.cpp} | 96 +- + core/fxcodec/gif/lzw_decompressor.h | 79 + + ...test.cpp => lzw_decompressor_unittest.cpp} | 173 +- + .../icc/{iccmodule.cpp => icc_transform.cpp} | 81 +- + core/fxcodec/icc/icc_transform.h | 55 + + core/fxcodec/icc/iccmodule.h | 68 - + core/fxcodec/jbig2/JBig2_ArithDecoder.cpp | 16 +- + core/fxcodec/jbig2/JBig2_ArithDecoder.h | 4 +- + core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp | 21 +- + core/fxcodec/jbig2/JBig2_ArithIntDecoder.h | 5 +- + core/fxcodec/jbig2/JBig2_BitStream.cpp | 24 +- + core/fxcodec/jbig2/JBig2_BitStream.h | 10 +- + .../jbig2/JBig2_BitStream_unittest.cpp | 6 +- + core/fxcodec/jbig2/JBig2_Context.cpp | 280 +- + core/fxcodec/jbig2/JBig2_Context.h | 27 +- + core/fxcodec/jbig2/JBig2_Define.h | 16 +- + core/fxcodec/jbig2/JBig2_DocumentContext.cpp | 2 +- + core/fxcodec/jbig2/JBig2_DocumentContext.h | 7 +- + core/fxcodec/jbig2/JBig2_GrdProc.cpp | 207 +- + core/fxcodec/jbig2/JBig2_GrdProc.h | 7 +- + core/fxcodec/jbig2/JBig2_GrrdProc.cpp | 17 +- + core/fxcodec/jbig2/JBig2_GrrdProc.h | 5 +- + core/fxcodec/jbig2/JBig2_HtrdProc.cpp | 11 +- + core/fxcodec/jbig2/JBig2_HtrdProc.h | 5 +- + core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp | 8 +- + core/fxcodec/jbig2/JBig2_HuffmanDecoder.h | 2 +- + core/fxcodec/jbig2/JBig2_HuffmanTable.cpp | 54 +- + core/fxcodec/jbig2/JBig2_HuffmanTable.h | 6 +- + core/fxcodec/jbig2/JBig2_Image.cpp | 46 +- + core/fxcodec/jbig2/JBig2_Image.h | 8 +- + core/fxcodec/jbig2/JBig2_Image_unittest.cpp | 113 +- + core/fxcodec/jbig2/JBig2_Page.h | 7 +- + core/fxcodec/jbig2/JBig2_PatternDict.cpp | 4 +- + core/fxcodec/jbig2/JBig2_PatternDict.h | 2 +- + core/fxcodec/jbig2/JBig2_PddProc.cpp | 17 +- + core/fxcodec/jbig2/JBig2_PddProc.h | 6 +- + core/fxcodec/jbig2/JBig2_SddProc.cpp | 414 +- + core/fxcodec/jbig2/JBig2_SddProc.h | 13 +- + core/fxcodec/jbig2/JBig2_Segment.cpp | 17 +- + core/fxcodec/jbig2/JBig2_Segment.h | 23 +- + core/fxcodec/jbig2/JBig2_SymbolDict.cpp | 9 +- + core/fxcodec/jbig2/JBig2_SymbolDict.h | 2 +- + core/fxcodec/jbig2/JBig2_TrdProc.cpp | 104 +- + core/fxcodec/jbig2/JBig2_TrdProc.h | 5 +- + .../{jbig2module.cpp => jbig2_decoder.cpp} | 83 +- + .../jbig2/{jbig2module.h => jbig2_decoder.h} | 39 +- + core/fxcodec/jbig2/jbig2_embeddertest.cpp | 8 +- + core/fxcodec/jpeg/jpeg_common.cpp | 27 + + core/fxcodec/jpeg/jpeg_common.h | 45 + + .../fxcodec/jpeg/jpeg_progressive_decoder.cpp | 178 + + core/fxcodec/jpeg/jpeg_progressive_decoder.h | 54 + + core/fxcodec/jpeg/jpegmodule.cpp | 331 +- + core/fxcodec/jpeg/jpegmodule.h | 58 +- + core/fxcodec/jpx/DEPS | 2 +- + core/fxcodec/jpx/cjpx_decoder.cpp | 180 +- + core/fxcodec/jpx/cjpx_decoder.h | 46 +- + core/fxcodec/jpx/jpx_decode_utils.cpp | 4 +- + core/fxcodec/jpx/jpx_decode_utils.h | 4 +- + core/fxcodec/jpx/jpx_unittest.cpp | 5 +- + core/fxcodec/jpx/jpxmodule.cpp | 24 - + core/fxcodec/jpx/jpxmodule.h | 34 - + core/fxcodec/png/DEPS | 2 +- + .../png/{pngmodule.cpp => png_decoder.cpp} | 57 +- + .../png/{pngmodule.h => png_decoder.h} | 37 +- + ...ivedecoder.cpp => progressive_decoder.cpp} | 1626 +- + ...ressivedecoder.h => progressive_decoder.h} | 199 +- + ...le_iface.h => progressive_decoder_iface.h} | 29 +- + core/fxcodec/progressive_decoder_unittest.cpp | 477 + + core/fxcodec/progressivedecoder_unittest.cpp | 406 - + core/fxcodec/scanlinedecoder.cpp | 22 +- + core/fxcodec/scanlinedecoder.h | 16 +- + .../tiff/{tiffmodule.cpp => tiff_decoder.cpp} | 180 +- + core/fxcodec/tiff/tiff_decoder.h | 50 + + core/fxcodec/tiff/tiffmodule.h | 46 - + core/fxcrt/Android.bp | 7 +- + core/fxcrt/BUILD.gn | 105 +- + core/fxcrt/DEPS | 1 + + core/fxcrt/autonuller.h | 32 + + core/fxcrt/autonuller_unittest.cpp | 53 + + core/fxcrt/autorestorer.h | 6 +- + core/fxcrt/autorestorer_unittest.cpp | 2 +- + core/fxcrt/binary_buffer.cpp | 122 + + core/fxcrt/binary_buffer.h | 63 + + core/fxcrt/binary_buffer_unittest.cpp | 152 + + core/fxcrt/byteorder.h | 2 +- + core/fxcrt/byteorder_unittest.cpp | 42 +- + core/fxcrt/bytestring.cpp | 140 +- + core/fxcrt/bytestring.h | 65 +- + core/fxcrt/bytestring_unittest.cpp | 170 +- + core/fxcrt/cfx_binarybuf.cpp | 86 - + core/fxcrt/cfx_binarybuf.h | 57 - + core/fxcrt/cfx_bitstream.cpp | 24 +- + core/fxcrt/cfx_bitstream.h | 16 +- + core/fxcrt/cfx_bitstream_unittest.cpp | 63 +- + core/fxcrt/cfx_datetime.cpp | 112 +- + core/fxcrt/cfx_datetime.h | 34 +- + core/fxcrt/cfx_datetime_unittest.cpp | 18 + + core/fxcrt/cfx_fileaccess_posix.cpp | 39 +- + core/fxcrt/cfx_fileaccess_posix.h | 15 +- + core/fxcrt/cfx_fileaccess_windows.cpp | 50 +- + core/fxcrt/cfx_fileaccess_windows.h | 14 +- + core/fxcrt/cfx_fixedbufgrow.h | 31 - + core/fxcrt/cfx_memorystream.cpp | 67 +- + core/fxcrt/cfx_memorystream.h | 31 +- + core/fxcrt/cfx_memorystream_unittest.cpp | 46 +- + core/fxcrt/cfx_read_only_span_stream.cpp | 36 + + core/fxcrt/cfx_read_only_span_stream.h | 30 + + core/fxcrt/cfx_read_only_string_stream.cpp | 25 + + core/fxcrt/cfx_read_only_string_stream.h | 31 + + core/fxcrt/cfx_read_only_vector_stream.cpp | 30 + + core/fxcrt/cfx_read_only_vector_stream.h | 37 + + core/fxcrt/cfx_readonlymemorystream.cpp | 42 - + core/fxcrt/cfx_readonlymemorystream.h | 38 - + core/fxcrt/cfx_seekablestreamproxy.cpp | 105 +- + core/fxcrt/cfx_seekablestreamproxy.h | 23 +- + .../cfx_seekablestreamproxy_unittest.cpp | 28 +- + core/fxcrt/cfx_timer.cpp | 21 +- + core/fxcrt/cfx_timer.h | 31 +- + core/fxcrt/cfx_timer_unittest.cpp | 18 +- + core/fxcrt/cfx_utf8decoder.cpp | 20 +- + core/fxcrt/cfx_utf8decoder.h | 17 +- + core/fxcrt/cfx_utf8encoder.cpp | 2 +- + core/fxcrt/cfx_utf8encoder.h | 10 +- + core/fxcrt/cfx_widetextbuf.cpp | 71 - + core/fxcrt/cfx_widetextbuf.h | 45 - + core/fxcrt/cfx_widetextbuf_unittest.cpp | 45 - + core/fxcrt/css/BUILD.gn | 28 +- + core/fxcrt/css/cfx_css.h | 133 +- + core/fxcrt/css/cfx_csscolorvalue.cpp | 6 +- + core/fxcrt/css/cfx_csscolorvalue.h | 4 +- + core/fxcrt/css/cfx_csscomputedstyle.cpp | 66 +- + core/fxcrt/css/cfx_csscomputedstyle.h | 59 +- + core/fxcrt/css/cfx_csscustomproperty.cpp | 4 +- + core/fxcrt/css/cfx_csscustomproperty.h | 4 +- + core/fxcrt/css/cfx_cssdata.cpp | 265 +- + core/fxcrt/css/cfx_cssdata.h | 14 +- + core/fxcrt/css/cfx_cssdata_unittest.cpp | 34 + + core/fxcrt/css/cfx_cssdeclaration.cpp | 264 +- + core/fxcrt/css/cfx_cssdeclaration.h | 28 +- + .../fxcrt/css/cfx_cssdeclaration_unittest.cpp | 2 +- + core/fxcrt/css/cfx_cssenumvalue.cpp | 6 +- + core/fxcrt/css/cfx_cssenumvalue.h | 2 +- + core/fxcrt/css/cfx_cssexttextbuf.cpp | 17 - + core/fxcrt/css/cfx_cssexttextbuf.h | 34 - + core/fxcrt/css/cfx_cssinputtextbuf.cpp | 11 + + core/fxcrt/css/cfx_cssinputtextbuf.h | 29 + + core/fxcrt/css/cfx_cssnumbervalue.cpp | 34 +- + core/fxcrt/css/cfx_cssnumbervalue.h | 38 +- + core/fxcrt/css/cfx_cssoutputtextbuf.cpp | 28 + + core/fxcrt/css/cfx_cssoutputtextbuf.h | 27 + + core/fxcrt/css/cfx_csspropertyholder.cpp | 6 +- + core/fxcrt/css/cfx_csspropertyholder.h | 2 +- + core/fxcrt/css/cfx_cssrulecollection.cpp | 36 +- + core/fxcrt/css/cfx_cssrulecollection.h | 12 +- + core/fxcrt/css/cfx_cssselector.cpp | 88 +- + core/fxcrt/css/cfx_cssselector.h | 29 +- + core/fxcrt/css/cfx_cssstringvalue.cpp | 6 +- + core/fxcrt/css/cfx_cssstringvalue.h | 2 +- + core/fxcrt/css/cfx_cssstylerule.cpp | 13 +- + core/fxcrt/css/cfx_cssstylerule.h | 4 +- + core/fxcrt/css/cfx_cssstyleselector.cpp | 185 +- + core/fxcrt/css/cfx_cssstyleselector.h | 20 +- + core/fxcrt/css/cfx_cssstylesheet.cpp | 104 +- + core/fxcrt/css/cfx_cssstylesheet.h | 18 +- + core/fxcrt/css/cfx_cssstylesheet_unittest.cpp | 307 +- + core/fxcrt/css/cfx_csssyntaxparser.cpp | 333 +- + core/fxcrt/css/cfx_csssyntaxparser.h | 85 +- + .../css/cfx_csssyntaxparser_unittest.cpp | 227 + + core/fxcrt/css/cfx_csstextbuf.cpp | 49 - + core/fxcrt/css/cfx_csstextbuf.h | 35 - + core/fxcrt/css/cfx_cssvalue.cpp | 6 +- + core/fxcrt/css/cfx_cssvalue.h | 19 +- + core/fxcrt/css/cfx_cssvaluelist.cpp | 16 +- + core/fxcrt/css/cfx_cssvaluelist.h | 10 +- + core/fxcrt/css/cfx_cssvaluelistparser.cpp | 68 +- + core/fxcrt/css/cfx_cssvaluelistparser.h | 16 +- + .../css/cfx_cssvaluelistparser_unittest.cpp | 98 +- + core/fxcrt/css/properties.inc | 188 + + core/fxcrt/css/property_values.inc | 50 + + core/fxcrt/data_vector.h | 21 + + core/fxcrt/fake_time_test.cpp | 18 + + core/fxcrt/fake_time_test.h | 16 + + core/fxcrt/fileaccess_iface.h | 7 +- + core/fxcrt/fixed_size_data_vector.h | 100 + + .../fixed_try_alloc_zeroed_data_vector.h | 17 + + ..._try_alloc_zeroed_data_vector_unittest.cpp | 108 + + core/fxcrt/fixed_uninit_data_vector.h | 14 + + .../fixed_uninit_data_vector_unittest.cpp | 96 + + core/fxcrt/fixed_zeroed_data_vector.h | 14 + + .../fixed_zeroed_data_vector_unittest.cpp | 97 + + core/fxcrt/fx_2d_size.h | 17 + + core/fxcrt/fx_bidi.cpp | 49 +- + core/fxcrt/fx_bidi.h | 13 +- + core/fxcrt/fx_bidi_unittest.cpp | 299 +- + core/fxcrt/fx_codepage.cpp | 232 +- + core/fxcrt/fx_codepage.h | 190 +- + core/fxcrt/fx_codepage_forward.h | 17 + + core/fxcrt/fx_coordinates.cpp | 132 +- + core/fxcrt/fx_coordinates.h | 104 +- + core/fxcrt/fx_coordinates_unittest.cpp | 29 +- + core/fxcrt/fx_extension.cpp | 71 +- + core/fxcrt/fx_extension.h | 47 +- + core/fxcrt/fx_extension_unittest.cpp | 79 +- + core/fxcrt/fx_folder.h | 24 + + core/fxcrt/fx_folder_posix.cpp | 66 + + core/fxcrt/fx_folder_windows.cpp | 63 + + core/fxcrt/fx_memory.cpp | 132 +- + core/fxcrt/fx_memory.h | 96 +- + core/fxcrt/fx_memory_malloc.cpp | 83 + + core/fxcrt/fx_memory_pa.cpp | 142 + + core/fxcrt/fx_memory_unittest.cpp | 43 +- + core/fxcrt/fx_memory_wrappers.h | 62 +- + core/fxcrt/fx_memory_wrappers_unittest.cpp | 35 +- + core/fxcrt/fx_number.cpp | 40 +- + core/fxcrt/fx_number.h | 10 +- + core/fxcrt/fx_number_unittest.cpp | 75 +- + core/fxcrt/fx_random.cpp | 10 +- + core/fxcrt/fx_random.h | 2 +- + core/fxcrt/fx_random_unittest.cpp | 2 +- + core/fxcrt/fx_safe_types.h | 7 +- + core/fxcrt/fx_safe_types_unittest.cpp | 22 + + core/fxcrt/fx_stream.cpp | 165 +- + core/fxcrt/fx_stream.h | 63 +- + core/fxcrt/fx_string.cpp | 54 +- + core/fxcrt/fx_string.h | 25 +- + core/fxcrt/fx_string_unittest.cpp | 11 +- + core/fxcrt/fx_string_wrappers.h | 25 + + core/fxcrt/fx_string_wrappers_unittest.cpp | 26 + + core/fxcrt/fx_system.cpp | 59 +- + core/fxcrt/fx_system.h | 205 +- + core/fxcrt/fx_system_unittest.cpp | 17 +- + core/fxcrt/fx_types.h | 22 + + core/fxcrt/fx_unicode.cpp | 42 +- + core/fxcrt/fx_unicode.h | 28 +- + core/fxcrt/mask.h | 118 + + core/fxcrt/mask_unittest.cpp | 170 + + core/fxcrt/maybe_owned.h | 92 +- + core/fxcrt/maybe_owned_unittest.cpp | 73 +- + core/fxcrt/observed_ptr.cpp | 21 +- + core/fxcrt/observed_ptr.h | 30 +- + core/fxcrt/observed_ptr_unittest.cpp | 40 +- + core/fxcrt/pauseindicator_iface.h | 2 +- + core/fxcrt/pdfium_span_unittest.cpp | 48 +- + core/fxcrt/retain_ptr.h | 151 +- + core/fxcrt/retain_ptr_unittest.cpp | 297 +- + core/fxcrt/retained_tree_node.h | 81 - + core/fxcrt/retained_tree_node_unittest.cpp | 188 - + core/fxcrt/scoped_set_insertion.h | 40 + + core/fxcrt/scoped_set_insertion_unittest.cpp | 24 + + core/fxcrt/shared_copy_on_write.h | 6 +- + core/fxcrt/shared_copy_on_write_unittest.cpp | 10 +- + core/fxcrt/small_buffer.h | 49 + + core/fxcrt/small_buffer_unittest.cpp | 56 + + core/fxcrt/span_util.h | 47 + + core/fxcrt/span_util_unittest.cpp | 91 + + core/fxcrt/stl_util.h | 45 + + core/fxcrt/string_data_template.cpp | 103 + + core/fxcrt/string_data_template.h | 81 +- + core/fxcrt/string_pool_template.h | 2 +- + core/fxcrt/string_pool_template_unittest.cpp | 26 +- + core/fxcrt/string_view_template.h | 76 +- + core/fxcrt/timerhandler_iface.h | 29 - + core/fxcrt/tree_node.h | 149 +- + core/fxcrt/tree_node_unittest.cpp | 97 +- + core/fxcrt/unowned_ptr.h | 126 +- + core/fxcrt/unowned_ptr_unittest.cpp | 200 +- + core/fxcrt/weak_ptr.h | 6 +- + core/fxcrt/weak_ptr_unittest.cpp | 12 +- + core/fxcrt/widestring.cpp | 197 +- + core/fxcrt/widestring.h | 71 +- + core/fxcrt/widestring_unittest.cpp | 145 +- + core/fxcrt/widetext_buffer.cpp | 86 + + core/fxcrt/widetext_buffer.h | 48 + + core/fxcrt/widetext_buffer_unittest.cpp | 62 + + core/fxcrt/xml/cfx_xmlchardata.cpp | 4 +- + core/fxcrt/xml/cfx_xmlchardata.h | 6 +- + core/fxcrt/xml/cfx_xmlchardata_unittest.cpp | 2 +- + core/fxcrt/xml/cfx_xmldocument.cpp | 8 +- + core/fxcrt/xml/cfx_xmldocument.h | 9 +- + core/fxcrt/xml/cfx_xmldocument_unittest.cpp | 2 +- + core/fxcrt/xml/cfx_xmlelement.cpp | 20 +- + core/fxcrt/xml/cfx_xmlelement.h | 7 +- + core/fxcrt/xml/cfx_xmlelement_unittest.cpp | 2 +- + core/fxcrt/xml/cfx_xmlinstruction.cpp | 6 +- + core/fxcrt/xml/cfx_xmlinstruction.h | 6 +- + .../fxcrt/xml/cfx_xmlinstruction_unittest.cpp | 8 +- + core/fxcrt/xml/cfx_xmlnode.cpp | 12 +- + core/fxcrt/xml/cfx_xmlnode.h | 8 +- + core/fxcrt/xml/cfx_xmlnode_unittest.cpp | 4 +- + core/fxcrt/xml/cfx_xmlparser.cpp | 68 +- + core/fxcrt/xml/cfx_xmlparser.h | 13 +- + core/fxcrt/xml/cfx_xmlparser_unittest.cpp | 27 +- + core/fxcrt/xml/cfx_xmltext.cpp | 6 +- + core/fxcrt/xml/cfx_xmltext.h | 6 +- + core/fxcrt/xml/cfx_xmltext_unittest.cpp | 2 +- + core/fxge/Android.bp | 1 + + core/fxge/BUILD.gn | 102 +- + core/fxge/agg/fx_agg_driver.cpp | 844 +- + core/fxge/agg/fx_agg_driver.h | 58 +- + core/fxge/android/cfpf_skiadevicemodule.cpp | 9 +- + core/fxge/android/cfpf_skiadevicemodule.h | 2 +- + core/fxge/android/cfpf_skiafont.cpp | 67 +- + core/fxge/android/cfpf_skiafont.h | 17 +- + core/fxge/android/cfpf_skiafontmgr.cpp | 156 +- + core/fxge/android/cfpf_skiafontmgr.h | 9 +- + core/fxge/android/cfpf_skiapathfont.cpp | 2 +- + core/fxge/android/cfpf_skiapathfont.h | 5 +- + core/fxge/android/cfx_androidfontinfo.cpp | 24 +- + core/fxge/android/cfx_androidfontinfo.h | 19 +- + ...fx_android_imp.cpp => fx_android_impl.cpp} | 19 +- + core/fxge/apple/apple_int.h | 48 - + core/fxge/apple/fx_apple_impl.cpp | 181 + + core/fxge/apple/fx_apple_platform.cpp | 297 +- + core/fxge/apple/fx_apple_platform.h | 28 + + core/fxge/apple/fx_mac_imp.cpp | 159 - + core/fxge/apple/fx_quartz_device.cpp | 41 +- + core/fxge/apple/fx_quartz_device.h | 36 + + core/fxge/calculate_pitch.cpp | 59 + + core/fxge/calculate_pitch.h | 23 + + core/fxge/cfx_cliprgn.cpp | 92 +- + core/fxge/cfx_cliprgn.h | 10 +- + core/fxge/cfx_color.cpp | 110 +- + core/fxge/cfx_color.h | 46 +- + core/fxge/cfx_defaultrenderdevice.cpp | 84 + + core/fxge/cfx_defaultrenderdevice.h | 71 +- + .../fxge/cfx_defaultrenderdevice_unittest.cpp | 80 + + core/fxge/cfx_drawutils.cpp | 41 + + core/fxge/cfx_drawutils.h | 24 + + core/fxge/cfx_face.cpp | 24 +- + core/fxge/cfx_face.h | 10 +- + core/fxge/cfx_fillrenderoptions.h | 91 + + core/fxge/cfx_folderfontinfo.cpp | 178 +- + core/fxge/cfx_folderfontinfo.h | 38 +- + core/fxge/cfx_folderfontinfo_unittest.cpp | 135 + + core/fxge/cfx_font.cpp | 551 +- + core/fxge/cfx_font.h | 151 +- + core/fxge/cfx_fontcache.cpp | 10 +- + core/fxge/cfx_fontcache.h | 8 +- + core/fxge/cfx_fontmapper.cpp | 871 +- + core/fxge/cfx_fontmapper.h | 97 +- + core/fxge/cfx_fontmapper_unittest.cpp | 217 +- + core/fxge/cfx_fontmgr.cpp | 146 +- + core/fxge/cfx_fontmgr.h | 75 +- + core/fxge/cfx_gemodule.cpp | 16 +- + core/fxge/cfx_gemodule.h | 18 +- + core/fxge/cfx_glyphbitmap.cpp | 2 +- + core/fxge/cfx_glyphbitmap.h | 4 +- + core/fxge/cfx_glyphcache.cpp | 192 +- + core/fxge/cfx_glyphcache.h | 51 +- + core/fxge/cfx_graphstate.cpp | 30 +- + core/fxge/cfx_graphstate.h | 8 +- + core/fxge/cfx_graphstatedata.cpp | 9 +- + core/fxge/cfx_graphstatedata.h | 34 +- + core/fxge/cfx_path.cpp | 452 + + core/fxge/cfx_path.h | 85 + + core/fxge/cfx_path_unittest.cpp | 407 + + core/fxge/cfx_pathdata.cpp | 522 - + core/fxge/cfx_pathdata.h | 88 - + core/fxge/cfx_renderdevice.cpp | 1126 +- + core/fxge/cfx_renderdevice.h | 92 +- + core/fxge/cfx_substfont.cpp | 19 +- + core/fxge/cfx_substfont.h | 17 +- + core/fxge/cfx_textrenderoptions.h | 57 + + core/fxge/cfx_unicodeencoding.cpp | 11 +- + core/fxge/cfx_unicodeencoding.h | 6 +- + core/fxge/cfx_unicodeencodingex.cpp | 73 +- + core/fxge/cfx_unicodeencodingex.h | 5 +- + core/fxge/cfx_windowsrenderdevice.cpp | 55 + + core/fxge/cfx_windowsrenderdevice.h | 39 +- + ... cfx_windowsrenderdevice_embeddertest.cpp} | 44 +- + core/fxge/dib/cfx_bitmapcomposer.cpp | 157 +- + core/fxge/dib/cfx_bitmapcomposer.h | 41 +- + core/fxge/dib/cfx_bitmapstorer.cpp | 36 +- + core/fxge/dib/cfx_bitmapstorer.h | 12 +- + core/fxge/dib/cfx_cmyk_to_srgb.cpp | 11 +- + core/fxge/dib/cfx_cmyk_to_srgb.h | 2 +- + core/fxge/dib/cfx_cmyk_to_srgb_unittest.cpp | 2 +- + core/fxge/dib/cfx_dibbase.cpp | 1023 +- + core/fxge/dib/cfx_dibbase.h | 117 +- + core/fxge/dib/cfx_dibbase_unittest.cpp | 163 + + core/fxge/dib/cfx_dibextractor.cpp | 18 +- + core/fxge/dib/cfx_dibextractor.h | 2 +- + core/fxge/dib/cfx_dibitmap.cpp | 940 +- + core/fxge/dib/cfx_dibitmap.h | 106 +- + core/fxge/dib/cfx_dibitmap_unittest.cpp | 193 +- + core/fxge/dib/cfx_imagerenderer.cpp | 41 +- + core/fxge/dib/cfx_imagerenderer.h | 6 +- + core/fxge/dib/cfx_imagestretcher.cpp | 138 +- + core/fxge/dib/cfx_imagestretcher.h | 18 +- + core/fxge/dib/cfx_imagetransformer.cpp | 490 +- + core/fxge/dib/cfx_imagetransformer.h | 33 +- + core/fxge/dib/cfx_scanlinecompositor.cpp | 2533 ++- + core/fxge/dib/cfx_scanlinecompositor.h | 68 +- + core/fxge/dib/cstretchengine.cpp | 712 +- + core/fxge/dib/cstretchengine.h | 146 +- + core/fxge/dib/cstretchengine_unittest.cpp | 146 +- + core/fxge/dib/fx_dib.cpp | 60 + + core/fxge/{ => dib}/fx_dib.h | 92 +- + core/fxge/dib/fx_dib_main.cpp | 158 - + core/fxge/dib/scanlinecomposer_iface.h | 12 +- + .../fontdata/chromefontdata/FoxitDingbats.cpp | 4 +- + .../fontdata/chromefontdata/FoxitFixed.cpp | 4 +- + .../chromefontdata/FoxitFixedBold.cpp | 4 +- + .../chromefontdata/FoxitFixedBoldItalic.cpp | 4 +- + .../chromefontdata/FoxitFixedItalic.cpp | 4 +- + .../fontdata/chromefontdata/FoxitSans.cpp | 4 +- + .../fontdata/chromefontdata/FoxitSansBold.cpp | 4 +- + .../chromefontdata/FoxitSansBoldItalic.cpp | 4 +- + .../chromefontdata/FoxitSansItalic.cpp | 4 +- + .../fontdata/chromefontdata/FoxitSansMM.cpp | 4 +- + .../fontdata/chromefontdata/FoxitSerif.cpp | 4 +- + .../chromefontdata/FoxitSerifBold.cpp | 4 +- + .../chromefontdata/FoxitSerifBoldItalic.cpp | 4 +- + .../chromefontdata/FoxitSerifItalic.cpp | 4 +- + .../fontdata/chromefontdata/FoxitSerifMM.cpp | 4 +- + .../fontdata/chromefontdata/FoxitSymbol.cpp | 4 +- + .../fontdata/chromefontdata/chromefontdata.h | 34 +- + core/fxge/freetype/fx_freetype.cpp | 50 +- + core/fxge/{ => freetype}/fx_freetype.h | 24 +- + core/fxge/fx_font.cpp | 49 +- + core/fxge/fx_font.h | 22 +- + core/fxge/fx_font_unittest.cpp | 38 +- + core/fxge/fx_ge_fontmap.cpp | 21 - + core/fxge/fx_ge_text_embeddertest.cpp | 20 +- + .../fx_linux_impl.cpp} | 91 +- + core/fxge/render_defines.h | 22 +- + core/fxge/renderdevicedriver_iface.cpp | 37 +- + core/fxge/renderdevicedriver_iface.h | 45 +- + core/fxge/scoped_font_transform.cpp | 2 +- + core/fxge/scoped_font_transform.h | 8 +- + core/fxge/skia/fx_skia_device.cpp | 2861 ++-- + core/fxge/skia/fx_skia_device.h | 157 +- + .../fxge/skia/fx_skia_device_embeddertest.cpp | 82 +- + core/fxge/systemfontinfo_iface.h | 25 +- + core/fxge/text_char_pos.cpp | 12 +- + core/fxge/text_char_pos.h | 11 +- + core/fxge/text_glyph_pos.cpp | 9 +- + core/fxge/text_glyph_pos.h | 8 +- + core/fxge/win32/DEPS | 5 + + core/fxge/win32/cfx_psfonttracker.cpp | 31 + + core/fxge/win32/cfx_psfonttracker.h | 35 + + core/fxge/win32/cfx_psrenderer.cpp | 800 +- + core/fxge/win32/cfx_psrenderer.h | 148 +- + core/fxge/win32/cfx_psrenderer_unittest.cpp | 213 + + core/fxge/win32/cfx_windowsdib.h | 52 - + core/fxge/win32/cgdi_device_driver.cpp | 749 + + core/fxge/win32/cgdi_device_driver.h | 80 + + core/fxge/win32/cgdi_display_driver.cpp | 229 + + core/fxge/win32/cgdi_display_driver.h | 66 + + ...fx_win32_gdipext.cpp => cgdi_plus_ext.cpp} | 625 +- + core/fxge/win32/cgdi_plus_ext.h | 55 + + core/fxge/win32/cgdi_printer_driver.cpp | 178 + + core/fxge/win32/cgdi_printer_driver.h | 57 + + core/fxge/win32/cps_printer_driver.cpp | 226 + + core/fxge/win32/cps_printer_driver.h | 88 + + core/fxge/win32/cpsoutput.cpp | 14 +- + core/fxge/win32/cpsoutput.h | 9 +- + core/fxge/win32/ctext_only_printer_driver.cpp | 194 + + core/fxge/win32/ctext_only_printer_driver.h | 80 + + core/fxge/win32/cwin32_platform.cpp | 490 + + core/fxge/win32/cwin32_platform.h | 27 + + core/fxge/win32/fx_win32_device.cpp | 1385 -- + core/fxge/win32/fx_win32_dib.cpp | 218 - + core/fxge/win32/fx_win32_print.cpp | 673 - + core/fxge/win32/win32_int.h | 353 - + docs/getting-started.md | 6 +- + docs/safetynet.md | 3 - + fpdfsdk/Android.bp | 5 +- + fpdfsdk/BUILD.gn | 39 +- + fpdfsdk/DEPS | 5 +- + fpdfsdk/PRESUBMIT.py | 11 +- + fpdfsdk/cpdfsdk_actionhandler.cpp | 426 - + fpdfsdk/cpdfsdk_actionhandler.h | 104 - + fpdfsdk/cpdfsdk_annot.cpp | 121 +- + fpdfsdk/cpdfsdk_annot.h | 115 +- + fpdfsdk/cpdfsdk_annothandlermgr.cpp | 332 - + fpdfsdk/cpdfsdk_annothandlermgr.h | 133 - + fpdfsdk/cpdfsdk_annotiteration.cpp | 45 +- + fpdfsdk/cpdfsdk_annotiteration.h | 17 +- + fpdfsdk/cpdfsdk_annotiterator.cpp | 79 +- + fpdfsdk/cpdfsdk_annotiterator.h | 25 +- + .../cpdfsdk_annotiterator_embeddertest.cpp | 28 +- + fpdfsdk/cpdfsdk_appstream.cpp | 1203 +- + fpdfsdk/cpdfsdk_appstream.h | 11 +- + fpdfsdk/cpdfsdk_baannot.cpp | 342 +- + fpdfsdk/cpdfsdk_baannot.h | 74 +- + fpdfsdk/cpdfsdk_baannot_embeddertest.cpp | 93 + + fpdfsdk/cpdfsdk_baannothandler.cpp | 227 - + fpdfsdk/cpdfsdk_baannothandler.h | 103 - + .../cpdfsdk_baannothandler_embeddertest.cpp | 88 - + fpdfsdk/cpdfsdk_customaccess.cpp | 20 +- + fpdfsdk/cpdfsdk_customaccess.h | 11 +- + fpdfsdk/cpdfsdk_fieldaction.cpp | 11 - + fpdfsdk/cpdfsdk_filewriteadapter.cpp | 19 +- + fpdfsdk/cpdfsdk_filewriteadapter.h | 8 +- + fpdfsdk/cpdfsdk_formfillenvironment.cpp | 895 +- + fpdfsdk/cpdfsdk_formfillenvironment.h | 193 +- + fpdfsdk/cpdfsdk_helpers.cpp | 300 +- + fpdfsdk/cpdfsdk_helpers.h | 93 +- + fpdfsdk/cpdfsdk_helpers_unittest.cpp | 88 + + fpdfsdk/cpdfsdk_interactiveform.cpp | 220 +- + fpdfsdk/cpdfsdk_interactiveform.h | 29 +- + fpdfsdk/cpdfsdk_pageview.cpp | 507 +- + fpdfsdk/cpdfsdk_pageview.h | 84 +- + fpdfsdk/cpdfsdk_pauseadapter.cpp | 4 +- + fpdfsdk/cpdfsdk_pauseadapter.h | 3 +- + fpdfsdk/cpdfsdk_renderpage.cpp | 47 +- + fpdfsdk/cpdfsdk_renderpage.h | 8 +- + fpdfsdk/cpdfsdk_widget.cpp | 631 +- + fpdfsdk/cpdfsdk_widget.h | 131 +- + fpdfsdk/cpdfsdk_widgethandler.cpp | 293 - + fpdfsdk/cpdfsdk_widgethandler.h | 107 - + fpdfsdk/formfiller/Android.bp | 5 +- + fpdfsdk/formfiller/BUILD.gn | 25 +- + fpdfsdk/formfiller/cffl_button.cpp | 70 +- + fpdfsdk/formfiller/cffl_button.h | 31 +- + fpdfsdk/formfiller/cffl_checkbox.cpp | 111 +- + fpdfsdk/formfiller/cffl_checkbox.h | 25 +- + fpdfsdk/formfiller/cffl_combobox.cpp | 125 +- + fpdfsdk/formfiller/cffl_combobox.h | 43 +- + .../formfiller/cffl_combobox_embeddertest.cpp | 50 + + fpdfsdk/formfiller/cffl_fieldaction.cpp | 11 + + .../cffl_fieldaction.h} | 18 +- + fpdfsdk/formfiller/cffl_formfield.cpp | 590 + + fpdfsdk/formfiller/cffl_formfield.h | 174 + + fpdfsdk/formfiller/cffl_formfiller.cpp | 547 - + fpdfsdk/formfiller/cffl_formfiller.h | 157 - + .../formfiller/cffl_interactiveformfiller.cpp | 978 +- + .../formfiller/cffl_interactiveformfiller.h | 267 +- + fpdfsdk/formfiller/cffl_listbox.cpp | 102 +- + fpdfsdk/formfiller/cffl_listbox.h | 28 +- + fpdfsdk/formfiller/cffl_perwindowdata.cpp | 30 + + fpdfsdk/formfiller/cffl_perwindowdata.h | 52 + + fpdfsdk/formfiller/cffl_pushbutton.cpp | 13 +- + fpdfsdk/formfiller/cffl_pushbutton.h | 8 +- + fpdfsdk/formfiller/cffl_radiobutton.cpp | 88 +- + fpdfsdk/formfiller/cffl_radiobutton.h | 26 +- + fpdfsdk/formfiller/cffl_textfield.cpp | 134 +- + fpdfsdk/formfiller/cffl_textfield.h | 41 +- + fpdfsdk/formfiller/cffl_textobject.cpp | 46 +- + fpdfsdk/formfiller/cffl_textobject.h | 23 +- + fpdfsdk/fpdf_annot.cpp | 756 +- + fpdfsdk/fpdf_annot_embeddertest.cpp | 1668 +- + fpdfsdk/fpdf_annot_unittest.cpp | 137 - + fpdfsdk/fpdf_attachment.cpp | 118 +- + fpdfsdk/fpdf_attachment_embeddertest.cpp | 162 +- + fpdfsdk/fpdf_catalog.cpp | 4 +- + fpdfsdk/fpdf_catalog_unittest.cpp | 30 +- + fpdfsdk/fpdf_dataavail.cpp | 116 +- + fpdfsdk/fpdf_dataavail_embeddertest.cpp | 131 +- + fpdfsdk/fpdf_doc.cpp | 199 +- + fpdfsdk/fpdf_doc_embeddertest.cpp | 388 +- + fpdfsdk/fpdf_doc_unittest.cpp | 117 +- + fpdfsdk/fpdf_edit_embeddertest.cpp | 2547 ++- + fpdfsdk/fpdf_edit_unittest.cpp | 9 +- + fpdfsdk/fpdf_editimg.cpp | 245 +- + ...test.cpp => fpdf_editimg_embeddertest.cpp} | 89 +- + fpdfsdk/fpdf_editpage.cpp | 381 +- + fpdfsdk/fpdf_editpage_embeddertest.cpp | 332 +- + fpdfsdk/fpdf_editpath.cpp | 105 +- + fpdfsdk/fpdf_editpath_embeddertest.cpp | 5 +- + fpdfsdk/fpdf_edittext.cpp | 464 +- + fpdfsdk/fpdf_ext.cpp | 5 +- + fpdfsdk/fpdf_ext_embeddertest.cpp | 6 +- + fpdfsdk/fpdf_flatten.cpp | 135 +- + fpdfsdk/fpdf_flatten_embeddertest.cpp | 105 +- + fpdfsdk/fpdf_formfill.cpp | 301 +- + fpdfsdk/fpdf_formfill_embeddertest.cpp | 1261 +- + fpdfsdk/fpdf_javascript.cpp | 27 +- + fpdfsdk/fpdf_javascript_embeddertest.cpp | 14 +- + fpdfsdk/fpdf_ppo.cpp | 495 +- + fpdfsdk/fpdf_ppo_embeddertest.cpp | 471 +- + fpdfsdk/fpdf_progressive.cpp | 84 +- + fpdfsdk/fpdf_save.cpp | 62 +- + fpdfsdk/fpdf_save_embeddertest.cpp | 126 +- + fpdfsdk/fpdf_searchex.cpp | 2 +- + fpdfsdk/fpdf_searchex_embeddertest.cpp | 2 +- + fpdfsdk/fpdf_signature.cpp | 222 + + fpdfsdk/fpdf_signature_embeddertest.cpp | 203 + + fpdfsdk/fpdf_structtree.cpp | 343 +- + fpdfsdk/fpdf_structtree_embeddertest.cpp | 716 +- + fpdfsdk/fpdf_sysfontinfo.cpp | 115 +- + fpdfsdk/fpdf_sysfontinfo_embeddertest.cpp | 41 +- + fpdfsdk/fpdf_text.cpp | 153 +- + fpdfsdk/fpdf_text_embeddertest.cpp | 367 +- + fpdfsdk/fpdf_thumbnail.cpp | 43 +- + fpdfsdk/fpdf_thumbnail_embeddertest.cpp | 39 +- + fpdfsdk/fpdf_transformpage.cpp | 152 +- + fpdfsdk/fpdf_transformpage_embeddertest.cpp | 116 +- + fpdfsdk/fpdf_view.cpp | 654 +- + fpdfsdk/fpdf_view_c_api_test.c | 114 +- + fpdfsdk/fpdf_view_c_api_test.h | 2 +- + fpdfsdk/fpdf_view_embeddertest.cpp | 1038 +- + fpdfsdk/fpdf_view_unittest.cpp | 2 +- + fpdfsdk/fpdfxfa/BUILD.gn | 25 +- + fpdfsdk/fpdfxfa/cpdfxfa_context.cpp | 190 +- + fpdfsdk/fpdfxfa/cpdfxfa_context.h | 93 +- + .../fpdfxfa/cpdfxfa_context_embeddertest.cpp | 20 + + fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp | 159 +- + fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h | 30 +- + .../cpdfxfa_docenvironment_embeddertest.cpp | 2 +- + fpdfsdk/fpdfxfa/cpdfxfa_page.cpp | 177 +- + fpdfsdk/fpdfxfa/cpdfxfa_page.h | 20 +- + fpdfsdk/fpdfxfa/cpdfxfa_widget.cpp | 432 +- + fpdfsdk/fpdfxfa/cpdfxfa_widget.h | 67 +- + fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.cpp | 667 - + fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h | 115 - + fpdfsdk/ipdfsdk_annothandler.h | 105 - + fpdfsdk/pwl/Android.bp | 5 +- + fpdfsdk/pwl/BUILD.gn | 44 +- + fpdfsdk/pwl/README.md | 1 - + fpdfsdk/pwl/cpwl_button.cpp | 16 +- + fpdfsdk/pwl/cpwl_button.h | 11 +- + fpdfsdk/pwl/cpwl_caret.cpp | 29 +- + fpdfsdk/pwl/cpwl_caret.h | 6 +- + fpdfsdk/pwl/cpwl_cbbutton.cpp | 79 + + fpdfsdk/pwl/cpwl_cbbutton.h | 30 + + fpdfsdk/pwl/cpwl_cblistbox.cpp | 93 + + fpdfsdk/pwl/cpwl_cblistbox.h | 32 + + fpdfsdk/pwl/cpwl_combo_box.cpp | 337 +- + fpdfsdk/pwl/cpwl_combo_box.h | 56 +- + .../pwl/cpwl_combo_box_edit_embeddertest.cpp | 283 + + fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp | 367 +- + fpdfsdk/pwl/cpwl_combo_box_embeddertest.h | 48 + + fpdfsdk/pwl/cpwl_edit.cpp | 873 +- + fpdfsdk/pwl/cpwl_edit.h | 156 +- + fpdfsdk/pwl/cpwl_edit_ctrl.cpp | 415 - + fpdfsdk/pwl/cpwl_edit_ctrl.h | 94 - + fpdfsdk/pwl/cpwl_edit_embeddertest.cpp | 83 +- + fpdfsdk/pwl/cpwl_edit_impl.cpp | 872 +- + fpdfsdk/pwl/cpwl_edit_impl.h | 510 +- + fpdfsdk/pwl/cpwl_icon.cpp | 110 - + fpdfsdk/pwl/cpwl_icon.h | 44 - + fpdfsdk/pwl/cpwl_list_box.cpp | 270 +- + fpdfsdk/pwl/cpwl_list_box.h | 80 +- + ...{cpwl_list_impl.cpp => cpwl_list_ctrl.cpp} | 143 +- + .../{cpwl_list_impl.h => cpwl_list_ctrl.h} | 102 +- + fpdfsdk/pwl/cpwl_sbbutton.cpp | 148 + + fpdfsdk/pwl/cpwl_sbbutton.h | 37 + + fpdfsdk/pwl/cpwl_scroll_bar.cpp | 583 +- + fpdfsdk/pwl/cpwl_scroll_bar.h | 45 +- + fpdfsdk/pwl/cpwl_special_button.cpp | 24 +- + fpdfsdk/pwl/cpwl_special_button.h | 16 +- + .../pwl/cpwl_special_button_embeddertest.cpp | 147 + + fpdfsdk/pwl/cpwl_wnd.cpp | 346 +- + fpdfsdk/pwl/cpwl_wnd.h | 217 +- + fpdfsdk/pwl/ipwl_fillernotify.h | 69 + + fpdfsdk/pwl/ipwl_systemhandler.h | 35 - + fxbarcode/BC_Library.cpp | 2 +- + fxbarcode/BC_Library.h | 48 +- + fxbarcode/BC_TwoDimWriter.cpp | 30 +- + fxbarcode/BC_TwoDimWriter.h | 4 +- + fxbarcode/BC_Writer.cpp | 15 +- + fxbarcode/BC_Writer.h | 22 +- + fxbarcode/BUILD.gn | 5 +- + fxbarcode/cbc_codabar.cpp | 28 +- + fxbarcode/cbc_codabar.h | 9 +- + fxbarcode/cbc_code128.cpp | 28 +- + fxbarcode/cbc_code128.h | 9 +- + fxbarcode/cbc_code39.cpp | 26 +- + fxbarcode/cbc_code39.h | 11 +- + fxbarcode/cbc_codebase.cpp | 16 +- + fxbarcode/cbc_codebase.h | 21 +- + fxbarcode/cbc_datamatrix.cpp | 17 +- + fxbarcode/cbc_datamatrix.h | 9 +- + fxbarcode/cbc_ean13.cpp | 12 +- + fxbarcode/cbc_ean13.h | 10 +- + fxbarcode/cbc_ean8.cpp | 11 +- + fxbarcode/cbc_ean8.h | 10 +- + fxbarcode/cbc_eancode.cpp | 19 +- + fxbarcode/cbc_eancode.h | 8 +- + fxbarcode/cbc_onecode.cpp | 12 +- + fxbarcode/cbc_onecode.h | 14 +- + fxbarcode/cbc_pdf417i.cpp | 18 +- + fxbarcode/cbc_pdf417i.h | 9 +- + fxbarcode/cbc_pdf417i_unittest.cpp | 2 +- + fxbarcode/cbc_qrcode.cpp | 17 +- + fxbarcode/cbc_qrcode.h | 9 +- + fxbarcode/cbc_upca.cpp | 11 +- + fxbarcode/cbc_upca.h | 10 +- + fxbarcode/cfx_barcode.cpp | 229 +- + fxbarcode/cfx_barcode.h | 23 +- + fxbarcode/cfx_barcode_unittest.cpp | 90 +- + fxbarcode/common/BC_CommonBitMatrix.cpp | 36 +- + fxbarcode/common/BC_CommonBitMatrix.h | 24 +- + fxbarcode/common/BC_CommonByteMatrix.cpp | 33 +- + fxbarcode/common/BC_CommonByteMatrix.h | 26 +- + .../common/reedsolomon/BC_ReedSolomon.cpp | 14 +- + fxbarcode/common/reedsolomon/BC_ReedSolomon.h | 2 +- + .../reedsolomon/BC_ReedSolomonGF256.cpp | 19 +- + .../common/reedsolomon/BC_ReedSolomonGF256.h | 6 +- + .../reedsolomon/BC_ReedSolomonGF256Poly.cpp | 23 +- + .../reedsolomon/BC_ReedSolomonGF256Poly.h | 2 +- + fxbarcode/datamatrix/BC_ASCIIEncoder.cpp | 16 +- + fxbarcode/datamatrix/BC_ASCIIEncoder.h | 2 +- + fxbarcode/datamatrix/BC_Base256Encoder.cpp | 4 +- + fxbarcode/datamatrix/BC_Base256Encoder.h | 2 +- + fxbarcode/datamatrix/BC_C40Encoder.cpp | 15 +- + fxbarcode/datamatrix/BC_C40Encoder.h | 2 +- + .../datamatrix/BC_DataMatrixSymbolInfo144.cpp | 15 +- + .../datamatrix/BC_DataMatrixSymbolInfo144.h | 4 +- + fxbarcode/datamatrix/BC_DataMatrixWriter.cpp | 74 +- + fxbarcode/datamatrix/BC_DataMatrixWriter.h | 12 +- + .../BC_DataMatrixWriter_unittest.cpp | 40 +- + fxbarcode/datamatrix/BC_DefaultPlacement.cpp | 193 +- + fxbarcode/datamatrix/BC_DefaultPlacement.h | 40 +- + fxbarcode/datamatrix/BC_EdifactEncoder.cpp | 24 +- + fxbarcode/datamatrix/BC_EdifactEncoder.h | 2 +- + fxbarcode/datamatrix/BC_Encoder.cpp | 7 +- + fxbarcode/datamatrix/BC_Encoder.h | 2 +- + fxbarcode/datamatrix/BC_EncoderContext.cpp | 5 +- + fxbarcode/datamatrix/BC_EncoderContext.h | 2 +- + fxbarcode/datamatrix/BC_ErrorCorrection.cpp | 49 +- + fxbarcode/datamatrix/BC_ErrorCorrection.h | 2 +- + fxbarcode/datamatrix/BC_HighLevelEncoder.cpp | 28 +- + fxbarcode/datamatrix/BC_HighLevelEncoder.h | 4 +- + fxbarcode/datamatrix/BC_SymbolInfo.cpp | 141 +- + fxbarcode/datamatrix/BC_SymbolInfo.h | 79 +- + fxbarcode/datamatrix/BC_TextEncoder.cpp | 6 +- + fxbarcode/datamatrix/BC_TextEncoder.h | 2 +- + fxbarcode/datamatrix/BC_X12Encoder.cpp | 6 +- + fxbarcode/datamatrix/BC_X12Encoder.h | 2 +- + fxbarcode/oned/BC_OneDimWriter.cpp | 160 +- + fxbarcode/oned/BC_OneDimWriter.h | 69 +- + fxbarcode/oned/BC_OnedCodaBarWriter.cpp | 83 +- + fxbarcode/oned/BC_OnedCodaBarWriter.h | 21 +- + .../oned/BC_OnedCodaBarWriter_unittest.cpp | 87 +- + fxbarcode/oned/BC_OnedCode128Writer.cpp | 69 +- + fxbarcode/oned/BC_OnedCode128Writer.h | 15 +- + .../oned/BC_OnedCode128Writer_unittest.cpp | 20 +- + fxbarcode/oned/BC_OnedCode39Writer.cpp | 130 +- + fxbarcode/oned/BC_OnedCode39Writer.h | 21 +- + .../oned/BC_OnedCode39Writer_unittest.cpp | 132 +- + fxbarcode/oned/BC_OnedEAN13Writer.cpp | 140 +- + fxbarcode/oned/BC_OnedEAN13Writer.h | 19 +- + .../oned/BC_OnedEAN13Writer_unittest.cpp | 57 +- + fxbarcode/oned/BC_OnedEAN8Writer.cpp | 116 +- + fxbarcode/oned/BC_OnedEAN8Writer.h | 21 +- + fxbarcode/oned/BC_OnedEAN8Writer_unittest.cpp | 58 +- + fxbarcode/oned/BC_OnedEANChecksum.cpp | 2 +- + fxbarcode/oned/BC_OnedEANChecksum.h | 4 +- + fxbarcode/oned/BC_OnedEANWriter.cpp | 2 +- + fxbarcode/oned/BC_OnedEANWriter.h | 2 +- + fxbarcode/oned/BC_OnedUPCAWriter.cpp | 144 +- + fxbarcode/oned/BC_OnedUPCAWriter.h | 17 +- + fxbarcode/oned/BC_OnedUPCAWriter_unittest.cpp | 54 +- + fxbarcode/pdf417/BC_PDF417.cpp | 24 +- + fxbarcode/pdf417/BC_PDF417.h | 4 +- + fxbarcode/pdf417/BC_PDF417BarcodeMatrix.cpp | 26 +- + fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h | 8 +- + fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp | 22 +- + fxbarcode/pdf417/BC_PDF417BarcodeRow.h | 13 +- + fxbarcode/pdf417/BC_PDF417ErrorCorrection.cpp | 12 +- + fxbarcode/pdf417/BC_PDF417ErrorCorrection.h | 8 +- + .../pdf417/BC_PDF417HighLevelEncoder.cpp | 35 +- + fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h | 14 +- + .../BC_PDF417HighLevelEncoder_unittest.cpp | 16 +- + fxbarcode/pdf417/BC_PDF417Writer.cpp | 26 +- + fxbarcode/pdf417/BC_PDF417Writer.h | 17 +- + fxbarcode/pdf417/BC_PDF417Writer_unittest.cpp | 17 +- + fxbarcode/qrcode/BC_QRCodeWriter.cpp | 27 +- + fxbarcode/qrcode/BC_QRCodeWriter.h | 14 +- + fxbarcode/qrcode/BC_QRCodeWriter_unittest.cpp | 54 +- + fxbarcode/qrcode/BC_QRCoder.cpp | 12 +- + fxbarcode/qrcode/BC_QRCoder.h | 5 +- + fxbarcode/qrcode/BC_QRCoderBitVector.cpp | 18 +- + fxbarcode/qrcode/BC_QRCoderBitVector.h | 9 +- + fxbarcode/qrcode/BC_QRCoderECBlocks.cpp | 4 +- + fxbarcode/qrcode/BC_QRCoderECBlocks.h | 2 +- + fxbarcode/qrcode/BC_QRCoderECBlocksData.cpp | 8 +- + fxbarcode/qrcode/BC_QRCoderECBlocksData.h | 8 +- + fxbarcode/qrcode/BC_QRCoderEncoder.cpp | 134 +- + fxbarcode/qrcode/BC_QRCoderEncoder.h | 4 +- + .../qrcode/BC_QRCoderErrorCorrectionLevel.cpp | 2 +- + .../qrcode/BC_QRCoderErrorCorrectionLevel.h | 2 +- + fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp | 61 +- + fxbarcode/qrcode/BC_QRCoderMaskUtil.h | 2 +- + fxbarcode/qrcode/BC_QRCoderMatrixUtil.cpp | 23 +- + fxbarcode/qrcode/BC_QRCoderMatrixUtil.h | 2 +- + fxbarcode/qrcode/BC_QRCoderMode.cpp | 33 +- + fxbarcode/qrcode/BC_QRCoderMode.h | 9 +- + fxbarcode/qrcode/BC_QRCoderVersion.cpp | 13 +- + fxbarcode/qrcode/BC_QRCoderVersion.h | 2 +- + fxbarcode/utils.h | 25 - + fxjs/Android.bp | 5 +- + fxjs/BUILD.gn | 75 +- + fxjs/README | 18 +- + fxjs/cfx_globaldata.cpp | 106 +- + fxjs/cfx_globaldata.h | 22 +- + fxjs/cfx_globaldata_unittest.cpp | 46 +- + fxjs/cfx_keyvalue.cpp | 2 +- + fxjs/cfx_keyvalue.h | 20 +- + fxjs/cfx_v8.cpp | 157 +- + fxjs/cfx_v8.h | 27 +- + fxjs/cfx_v8_array_buffer_allocator.cpp | 53 + + fxjs/cfx_v8_array_buffer_allocator.h | 35 + + fxjs/cfx_v8_unittest.cpp | 75 +- + fxjs/cfxjs_engine.cpp | 318 +- + fxjs/cfxjs_engine.h | 77 +- + fxjs/cfxjs_engine_embeddertest.cpp | 31 +- + fxjs/cfxjs_engine_unittest.cpp | 37 +- + fxjs/cjs_annot.cpp | 21 +- + fxjs/cjs_annot.h | 11 +- + fxjs/cjs_app.cpp | 95 +- + fxjs/cjs_app.h | 6 +- + fxjs/cjs_border.cpp | 4 +- + fxjs/cjs_border.h | 4 +- + fxjs/cjs_color.cpp | 71 +- + fxjs/cjs_color.h | 7 +- + fxjs/cjs_console.cpp | 7 +- + fxjs/cjs_console.h | 6 +- + fxjs/cjs_delaydata.cpp | 4 +- + fxjs/cjs_delaydata.h | 6 +- + fxjs/cjs_display.cpp | 4 +- + fxjs/cjs_display.h | 4 +- + fxjs/cjs_document.cpp | 220 +- + fxjs/cjs_document.h | 10 +- + fxjs/cjs_event.cpp | 71 +- + fxjs/cjs_event.h | 6 +- + fxjs/cjs_event_context.cpp | 440 +- + fxjs/cjs_event_context.h | 157 +- + fxjs/cjs_event_context_stub.cpp | 8 +- + fxjs/cjs_event_context_stub.h | 67 +- + fxjs/cjs_eventrecorder.cpp | 558 - + fxjs/cjs_eventrecorder.h | 201 - + fxjs/cjs_field.cpp | 650 +- + fxjs/cjs_field.h | 11 +- + fxjs/cjs_font.cpp | 4 +- + fxjs/cjs_font.h | 4 +- + fxjs/cjs_global.cpp | 423 +- + fxjs/cjs_global.h | 51 +- + fxjs/cjs_globalarrays.cpp | 43 +- + fxjs/cjs_globalarrays.h | 2 +- + fxjs/cjs_globalconsts.cpp | 17 +- + fxjs/cjs_globalconsts.h | 2 +- + fxjs/cjs_highlight.cpp | 4 +- + fxjs/cjs_highlight.h | 4 +- + fxjs/cjs_icon.cpp | 6 +- + fxjs/cjs_icon.h | 6 +- + fxjs/cjs_object.cpp | 21 +- + fxjs/cjs_object.h | 14 +- + fxjs/cjs_position.cpp | 4 +- + fxjs/cjs_position.h | 4 +- + fxjs/cjs_publicmethods.cpp | 288 +- + fxjs/cjs_publicmethods.h | 11 +- + fxjs/cjs_publicmethods_embeddertest.cpp | 66 +- + fxjs/cjs_publicmethods_unittest.cpp | 6 +- + fxjs/cjs_result.cpp | 4 +- + fxjs/cjs_result.h | 20 +- + fxjs/cjs_runtime.cpp | 57 +- + fxjs/cjs_runtime.h | 16 +- + fxjs/cjs_runtimestub.cpp | 11 +- + fxjs/cjs_runtimestub.h | 6 +- + fxjs/cjs_scalehow.cpp | 4 +- + fxjs/cjs_scalehow.h | 4 +- + fxjs/cjs_scalewhen.cpp | 4 +- + fxjs/cjs_scalewhen.h | 4 +- + fxjs/cjs_style.cpp | 4 +- + fxjs/cjs_style.h | 4 +- + fxjs/cjs_timerobj.cpp | 6 +- + fxjs/cjs_timerobj.h | 6 +- + fxjs/cjs_util.cpp | 137 +- + fxjs/cjs_util.h | 21 +- + fxjs/cjs_util_unittest.cpp | 116 +- + fxjs/cjs_zoomtype.cpp | 4 +- + fxjs/cjs_zoomtype.h | 4 +- + fxjs/fx_date_helpers.cpp | 33 +- + fxjs/fx_date_helpers.h | 4 +- + fxjs/fx_date_helpers_unittest.cpp | 49 +- + fxjs/fxv8.cpp | 337 + + fxjs/fxv8.h | 110 + + fxjs/gc/container_trace.h | 66 + + fxjs/gc/container_trace_unittest.cpp | 89 + + fxjs/gc/gced_tree_node.h | 47 + + fxjs/gc/gced_tree_node_mixin.h | 48 + + fxjs/gc/gced_tree_node_mixin_unittest.cpp | 149 + + fxjs/gc/gced_tree_node_unittest.cpp | 143 + + fxjs/gc/heap.cpp | 98 + + fxjs/gc/heap.h | 36 + + fxjs/gc/heap_unittest.cpp | 175 + + fxjs/gc/move_unittest.cpp | 62 + + fxjs/global_timer.cpp | 13 +- + fxjs/global_timer.h | 5 +- + fxjs/ijs_event_context.h | 73 +- + fxjs/ijs_runtime.cpp | 28 +- + fxjs/ijs_runtime.h | 19 +- + fxjs/js_define.cpp | 70 +- + fxjs/js_define.h | 28 +- + fxjs/js_resources.cpp | 15 +- + fxjs/js_resources.h | 5 +- + fxjs/xfa/cfxjse_app_embeddertest.cpp | 2 +- + fxjs/xfa/cfxjse_arguments.cpp | 59 - + fxjs/xfa/cfxjse_arguments.h | 37 - + fxjs/xfa/cfxjse_class.cpp | 266 +- + fxjs/xfa/cfxjse_class.h | 22 +- + fxjs/xfa/cfxjse_context.cpp | 186 +- + fxjs/xfa/cfxjse_context.h | 33 +- + fxjs/xfa/cfxjse_engine.cpp | 873 +- + fxjs/xfa/cfxjse_engine.h | 217 +- + fxjs/xfa/cfxjse_formcalc_context.cpp | 4843 +++--- + fxjs/xfa/cfxjse_formcalc_context.h | 590 +- + .../cfxjse_formcalc_context_embeddertest.cpp | 1766 +-- + fxjs/xfa/cfxjse_formcalc_context_unittest.cpp | 74 + + fxjs/xfa/cfxjse_isolatetracker.cpp | 30 +- + fxjs/xfa/cfxjse_isolatetracker.h | 69 +- + fxjs/xfa/cfxjse_mapmodule.cpp | 78 + + fxjs/xfa/cfxjse_mapmodule.h | 44 + + fxjs/xfa/cfxjse_mapmodule_unittest.cpp | 124 + + .../xfa/cfxjse_nodehelper.cpp | 33 +- + .../xfa/cfxjse_nodehelper.h | 29 +- + fxjs/xfa/cfxjse_resolveprocessor.cpp | 519 +- + fxjs/xfa/cfxjse_resolveprocessor.h | 84 +- + fxjs/xfa/cfxjse_runtimedata.cpp | 33 +- + fxjs/xfa/cfxjse_runtimedata.h | 23 +- + fxjs/xfa/cfxjse_value.cpp | 454 +- + fxjs/xfa/cfxjse_value.h | 121 +- + fxjs/xfa/cfxjse_value_embeddertest.cpp | 29 +- + fxjs/xfa/cjx_boolean.cpp | 21 +- + fxjs/xfa/cjx_boolean.h | 6 +- + fxjs/xfa/cjx_container.cpp | 19 +- + fxjs/xfa/cjx_container.h | 7 +- + fxjs/xfa/cjx_datawindow.cpp | 24 +- + fxjs/xfa/cjx_datawindow.h | 7 +- + fxjs/xfa/cjx_delta.cpp | 15 +- + fxjs/xfa/cjx_delta.h | 6 +- + fxjs/xfa/cjx_desc.cpp | 7 +- + fxjs/xfa/cjx_desc.h | 6 +- + fxjs/xfa/cjx_draw.cpp | 31 +- + fxjs/xfa/cjx_draw.h | 6 +- + fxjs/xfa/cjx_encrypt.cpp | 5 +- + fxjs/xfa/cjx_encrypt.h | 6 +- + fxjs/xfa/cjx_eventpseudomodel.cpp | 183 +- + fxjs/xfa/cjx_eventpseudomodel.h | 12 +- + fxjs/xfa/cjx_exclgroup.cpp | 52 +- + fxjs/xfa/cjx_exclgroup.h | 6 +- + fxjs/xfa/cjx_extras.cpp | 5 +- + fxjs/xfa/cjx_extras.h | 6 +- + fxjs/xfa/cjx_field.cpp | 131 +- + fxjs/xfa/cjx_field.h | 6 +- + fxjs/xfa/cjx_form.cpp | 53 +- + fxjs/xfa/cjx_form.h | 6 +- + fxjs/xfa/cjx_handler.cpp | 5 +- + fxjs/xfa/cjx_handler.h | 6 +- + fxjs/xfa/cjx_hostpseudomodel.cpp | 278 +- + fxjs/xfa/cjx_hostpseudomodel.h | 7 +- + fxjs/xfa/cjx_hostpseudomodel_embeddertest.cpp | 2 +- + fxjs/xfa/cjx_instancemanager.cpp | 127 +- + fxjs/xfa/cjx_instancemanager.h | 11 +- + fxjs/xfa/cjx_layoutpseudomodel.cpp | 158 +- + fxjs/xfa/cjx_layoutpseudomodel.h | 27 +- + fxjs/xfa/cjx_list.cpp | 46 +- + fxjs/xfa/cjx_list.h | 7 +- + fxjs/xfa/cjx_list_embeddertest.cpp | 2 +- + fxjs/xfa/cjx_logpseudomodel.cpp | 14 +- + fxjs/xfa/cjx_logpseudomodel.h | 6 +- + fxjs/xfa/cjx_manifest.cpp | 7 +- + fxjs/xfa/cjx_manifest.h | 6 +- + fxjs/xfa/cjx_model.cpp | 25 +- + fxjs/xfa/cjx_model.h | 7 +- + fxjs/xfa/cjx_node.cpp | 257 +- + fxjs/xfa/cjx_node.h | 8 +- + fxjs/xfa/cjx_object.cpp | 1166 +- + fxjs/xfa/cjx_object.h | 251 +- + fxjs/xfa/cjx_object_embeddertest.cpp | 20 + + fxjs/xfa/cjx_occur.cpp | 21 +- + fxjs/xfa/cjx_occur.h | 6 +- + fxjs/xfa/cjx_packet.cpp | 32 +- + fxjs/xfa/cjx_packet.h | 6 +- + fxjs/xfa/cjx_script.cpp | 12 +- + fxjs/xfa/cjx_script.h | 6 +- + fxjs/xfa/cjx_signaturepseudomodel.cpp | 15 +- + fxjs/xfa/cjx_signaturepseudomodel.h | 6 +- + fxjs/xfa/cjx_source.cpp | 41 +- + fxjs/xfa/cjx_source.h | 6 +- + fxjs/xfa/cjx_subform.cpp | 44 +- + fxjs/xfa/cjx_subform.h | 8 +- + fxjs/xfa/cjx_template.cpp | 19 +- + fxjs/xfa/cjx_template.h | 6 +- + fxjs/xfa/cjx_textnode.cpp | 14 +- + fxjs/xfa/cjx_textnode.h | 7 +- + fxjs/xfa/cjx_tree.cpp | 243 +- + fxjs/xfa/cjx_tree.h | 16 +- + fxjs/xfa/cjx_treelist.cpp | 12 +- + fxjs/xfa/cjx_treelist.h | 6 +- + fxjs/xfa/cjx_wsdlconnection.cpp | 7 +- + fxjs/xfa/cjx_wsdlconnection.h | 6 +- + fxjs/xfa/cjx_xfa.cpp | 16 +- + fxjs/xfa/cjx_xfa.h | 6 +- + fxjs/xfa/fxjse.cpp | 29 +- + fxjs/xfa/fxjse.h | 79 +- + fxjs/xfa/jse_define.h | 25 +- + navbar.md | 2 +- + pdfium.gni | 36 +- + public/PRESUBMIT.py | 11 +- + public/README | 2 + + public/cpp/fpdf_deleters.h | 2 +- + public/cpp/fpdf_scopers.h | 2 +- + public/fpdf_annot.h | 322 +- + public/fpdf_attachment.h | 25 +- + public/fpdf_catalog.h | 2 +- + public/fpdf_dataavail.h | 3 +- + public/fpdf_doc.h | 107 +- + public/fpdf_edit.h | 475 +- + public/fpdf_ext.h | 2 +- + public/fpdf_flatten.h | 2 +- + public/fpdf_formfill.h | 259 +- + public/fpdf_fwlevent.h | 2 +- + public/fpdf_javascript.h | 2 +- + public/fpdf_ppo.h | 57 +- + public/fpdf_progressive.h | 48 +- + public/fpdf_save.h | 2 +- + public/fpdf_searchex.h | 2 +- + public/fpdf_signature.h | 155 + + public/fpdf_structtree.h | 311 +- + public/fpdf_sysfontinfo.h | 6 +- + public/fpdf_text.h | 52 +- + public/fpdf_thumbnail.h | 2 +- + public/fpdf_transformpage.h | 14 +- + public/fpdfview.h | 276 +- + samples/BUILD.gn | 23 +- + samples/chromium_support/DEPS | 3 + + .../discardable_memory_allocator.cc | 16 + + .../discardable_memory_allocator.h | 14 + + samples/pdfium_test.cc | 873 +- + samples/pdfium_test_dump_helper.cc | 119 +- + samples/pdfium_test_dump_helper.h | 2 +- + samples/pdfium_test_event_helper.cc | 69 +- + samples/pdfium_test_event_helper.h | 6 +- + samples/pdfium_test_write_helper.cc | 276 +- + samples/pdfium_test_write_helper.h | 15 +- + samples/simple_no_v8.c | 52 + + samples/simple_with_v8.cc | 78 + + skia/BUILD.gn | 276 +- + skia/OWNERS | 2 + + .../{SkUserConfig.h => SkPdfiumUserConfig.h} | 39 +- + skia/ext/google_logging.cc | 27 +- + skia/features.gni | 2 +- + testing/BUILD.gn | 141 +- + testing/SUPPRESSIONS | 645 +- + testing/SUPPRESSIONS_EXACT_MATCHING | 28 + + testing/SUPPRESSIONS_IMAGE_DIFF | 21 +- + testing/command_line_helpers.cpp | 23 + + testing/command_line_helpers.h | 23 + + testing/embedder_test.cpp | 530 +- + testing/embedder_test.h | 130 +- + testing/embedder_test_constants.cpp | 68 + + testing/embedder_test_constants.h | 36 + + testing/embedder_test_environment.cpp | 93 + + testing/embedder_test_environment.h | 39 + + testing/embedder_test_main.cpp | 84 +- + testing/embedder_test_mock_delegate.h | 19 +- + .../embedder_test_timer_handling_delegate.h | 4 +- + testing/external_engine_embedder_test.cpp | 37 + + testing/external_engine_embedder_test.h | 35 + + testing/fake_file_access.cpp | 17 +- + testing/fake_file_access.h | 2 +- + testing/font_renamer.cpp | 91 + + testing/font_renamer.h | 22 + + testing/free_deleter.h | 2 +- + testing/fuzzers/BUILD.gn | 172 +- + testing/fuzzers/DEPS | 3 + + testing/fuzzers/component_fuzzer_template.cc | 6 +- + testing/fuzzers/pdf_bidi_fuzzer.cc | 26 +- + .../pdf_cfgas_stringformatter_fuzzer.cc | 83 +- + testing/fuzzers/pdf_cfx_barcode_fuzzer.cc | 7 +- + testing/fuzzers/pdf_cjs_util_fuzzer.cc | 2 +- + testing/fuzzers/pdf_cmap_fuzzer.cc | 5 +- + testing/fuzzers/pdf_codec_a85_fuzzer.cc | 8 +- + testing/fuzzers/pdf_codec_bmp_fuzzer.cc | 2 +- + testing/fuzzers/pdf_codec_fax_fuzzer.cc | 4 +- + testing/fuzzers/pdf_codec_gif_fuzzer.cc | 2 +- + testing/fuzzers/pdf_codec_icc_fuzzer.cc | 22 +- + testing/fuzzers/pdf_codec_jbig2_fuzzer.cc | 20 +- + testing/fuzzers/pdf_codec_jpeg_fuzzer.cc | 2 +- + testing/fuzzers/pdf_codec_png_fuzzer.cc | 2 +- + testing/fuzzers/pdf_codec_rle_fuzzer.cc | 8 +- + testing/fuzzers/pdf_codec_tiff_fuzzer.cc | 2 +- + .../fuzzers/pdf_cpdf_tounicodemap_fuzzer.cc | 38 + + testing/fuzzers/pdf_css_fuzzer.cc | 12 +- + testing/fuzzers/pdf_fm2js_fuzzer.cc | 23 - + testing/fuzzers/pdf_font_fuzzer.cc | 12 +- + .../fuzzers/pdf_formcalc_context_fuzzer.cc | 12 +- + testing/fuzzers/pdf_formcalc_fuzzer.cc | 14 +- + .../fuzzers/pdf_formcalc_translate_fuzzer.cc | 20 + + testing/fuzzers/pdf_fuzzer_init.cc | 2 +- + testing/fuzzers/pdf_fuzzer_init_public.cc | 90 +- + testing/fuzzers/pdf_fuzzer_init_public.h | 50 + + testing/fuzzers/pdf_fuzzer_templates.h | 52 + + testing/fuzzers/pdf_fx_date_helpers_fuzzer.cc | 4 +- + testing/fuzzers/pdf_hint_table_fuzzer.cc | 9 +- + testing/fuzzers/pdf_jpx_fuzzer.cc | 36 +- + testing/fuzzers/pdf_lzw_fuzzer.cc | 22 +- + testing/fuzzers/pdf_nametree_fuzzer.cc | 11 +- + testing/fuzzers/pdf_psengine_fuzzer.cc | 4 +- + .../fuzzers/pdf_scanlinecompositor_fuzzer.cc | 48 +- + testing/fuzzers/pdf_streamparser_fuzzer.cc | 5 +- + testing/fuzzers/pdf_xfa_fdp_fuzzer.cc | 914 ++ + testing/fuzzers/pdf_xfa_raw_fuzzer.cc | 101 + + testing/fuzzers/pdf_xfa_xdp_fdp_fuzzer.cc | 206 + + testing/fuzzers/pdf_xml_fuzzer.cc | 8 +- + testing/fuzzers/pdfium_fuzzer.cc | 14 +- + testing/fuzzers/pdfium_fuzzer_helper.cc | 10 +- + testing/fuzzers/pdfium_fuzzer_helper.h | 5 +- + testing/fuzzers/pdfium_fuzzer_util.cc | 14 +- + testing/fuzzers/pdfium_fuzzer_util.h | 14 +- + testing/fuzzers/pdfium_xfa_fuzzer.cc | 2 +- + testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.cc | 2 +- + testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h | 2 +- + testing/fuzzers/xfa_codec_fuzzer.h | 28 +- + testing/fuzzers/xfa_process_state.cc | 25 + + testing/fuzzers/xfa_process_state.h | 33 + + testing/fx_string_testhelpers.cpp | 18 +- + testing/fx_string_testhelpers.h | 7 +- + testing/fxgc_unittest.cpp | 39 + + testing/fxgc_unittest.h | 28 + + testing/fxv8_unittest.cpp | 26 + + .../fxv8_unittest.h | 10 +- + testing/gmock/BUILD.gn | 21 +- + testing/gmock/include/gmock/gmock-actions.h | 2 +- + .../gmock/gmock-generated-function-mockers.h | 2 +- + testing/gmock/include/gmock/gmock-matchers.h | 2 +- + testing/gmock/include/gmock/gmock.h | 2 +- + testing/gtest/BUILD.gn | 24 +- + testing/gtest/empty.cc | 2 +- + .../gtest/include/gtest/gtest-death-test.h | 10 + + testing/gtest/include/gtest/gtest-message.h | 10 + + .../gtest/include/gtest/gtest-param-test.h | 10 + + testing/gtest/include/gtest/gtest-spi.h | 10 + + testing/gtest/include/gtest/gtest.h | 4 +- + testing/gtest/include/gtest/gtest_prod.h | 4 +- + testing/gtest_mac.h | 3 +- + testing/gtest_mac.mm | 3 +- + testing/image_diff/BUILD.gn | 7 +- + testing/image_diff/DEPS | 2 +- + testing/image_diff/image_diff.cpp | 149 +- + testing/image_diff/image_diff_png.cpp | 35 +- + testing/image_diff/image_diff_png.h | 2 +- + testing/invalid_seekable_read_stream.cpp | 7 +- + testing/invalid_seekable_read_stream.h | 11 +- + testing/js_embedder_test.cpp | 37 +- + testing/js_embedder_test.h | 23 +- + testing/pdf_test_environment.cpp | 20 + + testing/pdf_test_environment.h | 24 + + testing/pseudo_retainable.h | 12 +- + testing/range_set.cpp | 15 +- + testing/range_set.h | 2 +- + testing/resources/CMYK-alpha.jpf | Bin 0 -> 713 bytes + testing/resources/CMYK.jpf | Bin 0 -> 624 bytes + testing/resources/RGB-alpha.jp2 | Bin 0 -> 373 bytes + testing/resources/RGB.jp2 | Bin 0 -> 288 bytes + testing/resources/annot_javascript.in | 42 + + testing/resources/annot_javascript.pdf | 53 + + .../resources/{link_annots.in => annots.in} | 43 +- + .../resources/{link_annots.pdf => annots.pdf} | 97 +- + testing/resources/annots_action_handling.in | 132 + + testing/resources/annots_action_handling.pdf | 153 + + testing/resources/bad_dict_keys.in | 23 + + testing/resources/bad_dict_keys.pdf | 33 + + testing/resources/bigtable_mini.in | 113 + + testing/resources/bigtable_mini.pdf | 128 + + testing/resources/bookmarks.in | 114 +- + testing/resources/bookmarks.pdf | 157 +- + testing/resources/bug_1055869.in | 62 + + testing/resources/bug_1055869.pdf | 271 + + testing/resources/bug_1058653.in | 73 + + testing/resources/bug_1058653.pdf | 282 + + testing/resources/bug_1124998.pdf | Bin 0 -> 3018 bytes + testing/resources/bug_1229106.in | 68 + + testing/resources/bug_1229106.pdf | 81 + + testing/resources/bug_1296920.in | 115 + + testing/resources/bug_1296920.pdf | 136 + + testing/resources/bug_1301.pdf | 2 +- + testing/resources/bug_1302455.in | 75 + + testing/resources/bug_1302455.pdf | 90 + + testing/resources/bug_1324189.in | 16 + + testing/resources/bug_1324189.pdf | 28 + + testing/resources/bug_1324503.in | 16 + + testing/resources/bug_1324503.pdf | 28 + + testing/resources/bug_1327884.pdf | 6 + + testing/resources/bug_1328389.in | 24 + + testing/resources/bug_1328389.pdf | 34 + + testing/resources/bug_1333298.in | 28 + + testing/resources/bug_1333298.pdf | 237 + + testing/resources/bug_1388_2.pdf | 80 + + testing/resources/bug_1396264.in | 86 + + testing/resources/bug_1396264.pdf | 99 + + testing/resources/bug_1469.jp2 | Bin 0 -> 945 bytes + testing/resources/bug_1506.in | 60 + + testing/resources/bug_1506.pdf | 74 + + testing/resources/bug_1549.in | 63 + + testing/resources/bug_1549.pdf | 76 + + testing/resources/bug_1558.in | 54 + + testing/resources/bug_1558.pdf | 66 + + testing/resources/bug_1574.in | 60 + + testing/resources/bug_1574.pdf | 72 + + testing/resources/bug_1591.in | 106 + + testing/resources/bug_1591.pdf | 122 + + testing/resources/bug_1646.pdf | 77 + + .../resources/bug_1752_truetype_font.fragment | 184 + + testing/resources/bug_1768.in | 317 + + testing/resources/bug_1768.pdf | 364 + + testing/resources/bug_1769.in | 72 + + testing/resources/bug_1769.pdf | 85 + + testing/resources/bug_1919.pdf | Bin 0 -> 9554 bytes + testing/resources/bug_306123.pdf | 2 +- + testing/resources/bug_481363.in | 2 +- + testing/resources/bug_481363.pdf | 8 +- + testing/resources/bug_674771.in | 56 + + testing/resources/bug_674771.pdf | 68 + + testing/resources/bug_680376.in | 4 +- + testing/resources/bug_680376.pdf | 16 +- + testing/resources/bug_765384.in | 2 +- + testing/resources/bug_765384.pdf | 2 +- + testing/resources/bug_821454.in | 48 +- + testing/resources/bug_821454.pdf | 82 +- + testing/resources/bug_889099.in | 64 + + testing/resources/bug_889099.pdf | 78 + + testing/resources/bug_921.in | 2 +- + testing/resources/bug_921.pdf | 9 +- + testing/resources/bug_925981.in | 2 +- + testing/resources/bug_925981.pdf | 6 +- + testing/resources/bug_972518.pdf | 2 +- + testing/resources/dashed_lines.in | 37 + + testing/resources/dashed_lines.pdf | 48 + + testing/resources/docmdp.in | 88 + + testing/resources/docmdp.pdf | 102 + + .../embedded_attachments_invalid_data.in | 41 + + .../embedded_attachments_invalid_data.pdf | 54 + + testing/resources/find_text_consecutive.in | 2 +- + testing/resources/find_text_consecutive.pdf | 4 +- + testing/resources/fonts/SymbolNeu.ttf | Bin 0 -> 69248 bytes + testing/resources/get_page_aaction.in | 50 + + testing/resources/get_page_aaction.pdf | 61 + + testing/resources/gotoe_action.in | 48 + + testing/resources/gotoe_action.pdf | 60 + + testing/resources/gray-alpha.jp2 | Bin 0 -> 279 bytes + testing/resources/gray.jp2 | Bin 0 -> 211 bytes + testing/resources/hebrew_mirrored.in | 160 + + testing/resources/hebrew_mirrored.pdf | 175 + + testing/resources/hello_world.in | 5 +- + testing/resources/hello_world.pdf | 20 +- + testing/resources/hello_world_2_pages.in | 67 + + testing/resources/hello_world_2_pages.pdf | 81 + + ...llo_world_2_pages_shared_resources_dict.in | 64 + + ...lo_world_2_pages_shared_resources_dict.pdf | 79 + + .../hello_world_2_pages_split_streams.in | 75 + + .../hello_world_2_pages_split_streams.pdf | 91 + + testing/resources/ink_annot.in | 48 + + testing/resources/ink_annot.pdf | 60 + + .../resources/javascript/annot_properties.in | 9 + + .../javascript/annot_properties_expected.txt | 2 + + testing/resources/javascript/app_methods.in | 6 +- + .../javascript/app_methods_expected.txt | 4 +- + .../resources/javascript/app_properties.in | 6 +- + testing/resources/javascript/apply.in | 3 +- + testing/resources/javascript/array_buffer.in | 3 +- + testing/resources/javascript/bug_1098213.in | 65 + + .../javascript/bug_1098213_expected.txt | 1 + + testing/resources/javascript/bug_1142688.in | 78 + + .../javascript/bug_1142688_expected.txt | 1 + + testing/resources/javascript/bug_1314658.in | 29 + + .../javascript/bug_1314658_expected.txt | 1 + + testing/resources/javascript/bug_1335681.in | 38 + + .../javascript/bug_1335681_expected.txt | 1 + + testing/resources/javascript/bug_1358075.in | 39 + + .../javascript/bug_1358075_expected.txt | 1 + + testing/resources/javascript/bug_1445426.evt | 3 + + testing/resources/javascript/bug_1445426.in | 114 + + testing/resources/javascript/bug_361.in | 5 +- + testing/resources/javascript/bug_492_1.in | 2 +- + testing/resources/javascript/bug_494057.in | 2 +- + testing/resources/javascript/bug_679642.in | 2 +- + testing/resources/javascript/bug_679643.in | 2 +- + testing/resources/javascript/bug_695826.in | 3 +- + testing/resources/javascript/bug_735912.in | 6 +- + testing/resources/javascript/bug_740166.in | 5 +- + testing/resources/javascript/bug_959274_1.in | 1 + + testing/resources/javascript/color_methods.in | 2 +- + .../resources/javascript/color_properties.in | 3 +- + .../resources/javascript/console_methods.in | 2 +- + testing/resources/javascript/constructor.in | 36 +- + testing/resources/javascript/constructor.js | 49 + + .../javascript/constructor_expected.txt | 4 + + testing/resources/javascript/consts.in | 3 +- + .../resources/javascript/document_methods.in | 6 +- + .../javascript/document_properties.in | 6 +- + .../document_properties_expected.txt | 16 +- + testing/resources/javascript/expect.js | 4 + + testing/resources/javascript/field.fragment | 18 + + testing/resources/javascript/field_methods.in | 13 +- + .../javascript/field_methods_expected.txt | 12 +- + .../resources/javascript/field_properties.in | 22 +- + .../javascript/field_properties_expected.txt | 16 +- + .../resources/javascript/foreground_task.in | 43 + + .../javascript/foreground_task_expected.txt | 2 + + testing/resources/javascript/globals.in | 9 +- + .../resources/javascript/globals_expected.txt | 47 +- + .../resources/javascript/immutable_proto.in | 43 + + .../javascript/immutable_proto_expected.txt | 4 + + testing/resources/javascript/mouse_events.in | 3 + + testing/resources/javascript/named_action.in | 33 + + .../javascript/named_action_expected.txt | 1 + + .../javascript/property_test_helpers.js | 2 +- + .../javascript/public_methods_expected.txt | 32 +- + testing/resources/javascript/unsupported.in | 3 +- + .../resources/javascript/util_bytetochar.in | 3 +- + testing/resources/javascript/util_printd.in | 3 +- + testing/resources/javascript/util_printx.in | 3 +- + testing/resources/javascript/util_scand.in | 2 +- + testing/resources/javascript/v8_features.in | 3 +- + .../javascript/xfa_specific/bug_1004106.in | 65 + + .../javascript/xfa_specific/bug_1026991.evt | 5 + + .../javascript/xfa_specific/bug_1026991.in | 65 + + .../javascript/xfa_specific/bug_1039629.evt | 3 + + .../javascript/xfa_specific/bug_1039629.in | 188 + + .../javascript/xfa_specific/bug_1040329.in | 99 + + .../javascript/xfa_specific/bug_1042915.evt | 8 + + .../javascript/xfa_specific/bug_1042915.pdf | Bin 0 -> 938 bytes + .../xfa_specific/bug_1042915_expected.txt | 0 + .../javascript/xfa_specific/bug_1043508.in | 62 + + .../javascript/xfa_specific/bug_1043510.evt | 2 +- + .../javascript/xfa_specific/bug_1047914.evt | 4 + + .../javascript/xfa_specific/bug_1047914.in | 47 + + .../javascript/xfa_specific/bug_1052651.evt | 4 + + .../javascript/xfa_specific/bug_1052651.in | 168 + + .../javascript/xfa_specific/bug_1052786.in | 168 + + .../javascript/xfa_specific/bug_1053617.in | 155 + + .../javascript/xfa_specific/bug_1054429.evt | 2 + + .../javascript/xfa_specific/bug_1054429.in | 147 + + .../javascript/xfa_specific/bug_1060549.evt | 3 + + .../javascript/xfa_specific/bug_1060549.in | 59 + + .../javascript/xfa_specific/bug_1069700.evt | 3 + + .../javascript/xfa_specific/bug_1069700.in | 73 + + .../javascript/xfa_specific/bug_1069789.evt | 1 + + .../javascript/xfa_specific/bug_1069789.in | 51 + + .../javascript/xfa_specific/bug_1082597.in | 150 + + .../xfa_specific/bug_1082597_expected.txt | 3 + + .../javascript/xfa_specific/bug_1109108.evt | 1 + + .../javascript/xfa_specific/bug_1109108.in | 64 + + .../xfa_specific/bug_1109108_expected.txt | 2 + + .../javascript/xfa_specific/bug_1137668.evt | 9 + + .../javascript/xfa_specific/bug_1137668.in | 338 + + .../javascript/xfa_specific/bug_1164158.in | 37 + + .../javascript/xfa_specific/bug_1248901.in | 47 + + .../xfa_specific/bug_1248901_expected.txt | 1 + + .../javascript/xfa_specific/bug_1312736.in | 40 + + .../xfa_specific/bug_1312736_expected.txt | 1 + + .../javascript/xfa_specific/bug_1444238.evt | 3 + + .../javascript/xfa_specific/bug_1444238.in | 149 + + .../javascript/xfa_specific/bug_980116.evt | 4 + + .../javascript/xfa_specific/bug_980116.in | 58 + + .../javascript/xfa_specific/bug_980161.evt | 4 + + .../javascript/xfa_specific/bug_980161.in | 55 + + .../xfa_specific/cross_engine_apply.in | 1 + + .../javascript/xfa_specific/dump_tree.js | 22 + + .../xfa_specific/instance_manager.in | 99 + + .../instance_manager_expected.txt | 39 + + .../javascript/xfa_specific/mixed_widgets.evt | 5 + + .../javascript/xfa_specific/mixed_widgets.in | 48 + + .../javascript/xfa_specific/popup_menu.evt | 5 + + .../javascript/xfa_specific/popup_menu.in | 40 + + .../xfa_specific/popup_menu_expected.txt | 1 + + .../javascript/xfa_specific/xfa_exclgroup.in | 86 + + .../xfa_specific/xfa_exclgroup_expected.txt | 32 + + .../javascript/xfa_specific/xfa_field.in | 92 + + .../xfa_specific/xfa_field_expected.txt | 47 + + .../javascript/xfa_specific/xfa_form.in | 61 + + .../xfa_specific/xfa_form_expected.txt | 20 + + .../xfa_specific/xfa_globalobject.in | 41 + + .../xfa_globalobject_expected.txt | 4 + + .../xfa_host_pseudomodel_expected.txt | 2 +- + .../javascript/xfa_specific/xfa_items.in | 196 + + .../xfa_specific/xfa_items_expected.txt | 80 + + .../xfa_specific/xfa_log_pseudomodel.in | 1 + + .../javascript/xfa_specific/xfa_node.in | 37 +- + .../xfa_specific/xfa_node_expected.txt | 213 + + .../javascript/xfa_specific/xfa_template.in | 60 + + .../xfa_specific/xfa_template_expected.txt | 15 + + .../javascript/xfa_specific/xfa_variables.in | 76 + + .../xfa_specific/xfa_variables_expected.txt | 11 + + testing/resources/jpx_lzw.in | 55 + + testing/resources/jpx_lzw.pdf | 67 + + testing/resources/js.pdf | 7 - + testing/resources/line_annot.in | 50 + + testing/resources/line_annot.pdf | 62 + + testing/resources/listbox_form.in | 49 +- + testing/resources/listbox_form.pdf | 79 +- + testing/resources/marked_content_id.in | 235 + + testing/resources/marked_content_id.pdf | Bin 13109 -> 5625 bytes + testing/resources/matte.in | 172 + + testing/resources/matte.pdf | 191 + + testing/resources/mona_lisa.fragment | 109 + + testing/resources/multiple_form_types.in | 131 + + testing/resources/multiple_form_types.pdf | 151 + + testing/resources/named_dests.in | 4 +- + testing/resources/named_dests.pdf | 19 +- + testing/resources/named_dests_old_style.in | 55 + + testing/resources/named_dests_old_style.pdf | 68 + + testing/resources/no_page_count.in | 50 + + testing/resources/no_page_count.pdf | 63 + + testing/resources/non_hex_file_id.in | 26 + + testing/resources/non_hex_file_id.pdf | 33 + + testing/resources/page_tree_empty_node.in | 40 + + testing/resources/page_tree_empty_node.pdf | 53 + + ...t_border_no_extend_expected_skia.pdf.0.png | Bin 0 -> 732 bytes + testing/resources/pixel/bug_1012369.in | 4 +- + testing/resources/pixel/bug_1015233.in | 113 + + .../pixel/bug_1015233_expected.pdf.0.png | Bin 0 -> 596 bytes + .../pixel/bug_1015233_expected_skia.pdf.0.png | Bin 0 -> 669 bytes + .../pixel/bug_1021762_expected.pdf.0.png | Bin 737 -> 713 bytes + testing/resources/pixel/bug_1072440.in | 39 + + .../pixel/bug_1072440_expected.pdf.0.png | Bin 0 -> 515 bytes + .../pixel/bug_1072440_expected_mac.pdf.0.png | Bin 0 -> 444 bytes + .../pixel/bug_1072440_expected_skia.pdf.0.png | Bin 0 -> 463 bytes + testing/resources/pixel/bug_1099446.in | 65 + + .../pixel/bug_1099446_expected.pdf.0.png | Bin 0 -> 113 bytes + testing/resources/pixel/bug_1116869.in | 117 + + .../pixel/bug_1116869_expected.pdf.0.png | Bin 0 -> 114 bytes + testing/resources/pixel/bug_1128284.in | 94 + + .../pixel/bug_1128284_expected.pdf.0.png | Bin 0 -> 147 bytes + .../pixel/bug_113910_expected.pdf.0.png | Bin 1609 -> 1042 bytes + .../pixel/bug_113910_expected_mac.pdf.0.png | Bin 1564 -> 879 bytes + .../pixel/bug_113910_expected_skia.pdf.0.png | Bin 0 -> 849 bytes + .../pixel/bug_113910_expected_win.pdf.0.png | Bin 987 -> 0 bytes + testing/resources/pixel/bug_1161.in | 24 +- + .../pixel/bug_1161_expected_skia.pdf.0.png | Bin 0 -> 494 bytes + testing/resources/pixel/bug_1236805.in | 86 + + .../pixel/bug_1236805_expected.pdf.0.png | Bin 0 -> 1045 bytes + .../pixel/bug_1236_expected_skia.pdf.0.png | Bin 0 -> 259 bytes + testing/resources/pixel/bug_1237898.in | 60 + + .../pixel/bug_1237898_expected.pdf.0.png | Bin 0 -> 91 bytes + testing/resources/pixel/bug_1241587.in | 78 + + .../pixel/bug_1241587_expected.pdf.0.png | Bin 0 -> 91 bytes + testing/resources/pixel/bug_1258634.in | 52 + + .../pixel/bug_1258634_expected.pdf.0.png | Bin 0 -> 593 bytes + testing/resources/pixel/bug_1258968.in | 211 + + .../pixel/bug_1258968_expected.pdf.0.png | Bin 0 -> 1830 bytes + .../pixel/bug_1258968_expected_skia.pdf.0.png | Bin 0 -> 1746 bytes + testing/resources/pixel/bug_1271578.in | 95 + + .../pixel/bug_1271578_expected.pdf.0.png | Bin 0 -> 186 bytes + .../bug_1271578_expected_agg_mac.pdf.0.png | Bin 0 -> 165 bytes + .../pixel/bug_1286_expected_skia.pdf.0.png | Bin 0 -> 1215 bytes + testing/resources/pixel/bug_1287409.pdf | Bin 0 -> 1150 bytes + .../pixel/bug_1287409_expected.pdf.0.png | Bin 0 -> 1214 bytes + .../pixel/bug_1287409_expected_skia.pdf.0.png | Bin 0 -> 1231 bytes + .../pixel/bug_1288_1_expected_skia.pdf.0.png | Bin 0 -> 227 bytes + .../pixel/bug_1288_2_expected_skia.pdf.0.png | Bin 0 -> 356 bytes + .../pixel/bug_1296_expected_skia.pdf.0.png | Bin 0 -> 1700 bytes + testing/resources/pixel/bug_1304714.in | 107 + + .../pixel/bug_1304714_expected.pdf.0.png | Bin 0 -> 163 bytes + .../pixel/bug_1308_1_expected.pdf.0.png | Bin 288 -> 240 bytes + .../pixel/bug_1308_1_expected_win.pdf.0.png | Bin 289 -> 0 bytes + .../pixel/bug_1308_expected.pdf.0.png | Bin 1872 -> 1928 bytes + .../pixel/bug_1308_expected_mac.pdf.0.png | Bin 2090 -> 0 bytes + .../pixel/bug_1308_expected_win.pdf.0.png | Bin 1913 -> 0 bytes + .../pixel/bug_1330_expected_skia.pdf.0.png | Bin 0 -> 5855 bytes + .../pixel/bug_1338_expected_skia.pdf.0.png | Bin 0 -> 3293 bytes + testing/resources/pixel/bug_1355.in | 85 + + .../pixel/bug_1355_expected.pdf.0.png | Bin 0 -> 891 bytes + .../pixel/bug_1355_expected_skia.pdf.0.png | Bin 0 -> 897 bytes + testing/resources/pixel/bug_1356149.in | 216 + + .../pixel/bug_1356149_expected.pdf.0.png | Bin 0 -> 396 bytes + testing/resources/pixel/bug_1372651.evt | 4 + + testing/resources/pixel/bug_1372651.in | 71 + + .../pixel/bug_1372651_expected.pdf.0.png | Bin 0 -> 2537 bytes + .../pixel/bug_1372651_expected_mac.pdf.0.png | Bin 0 -> 2415 bytes + .../pixel/bug_1372651_expected_skia.pdf.0.png | Bin 0 -> 2468 bytes + testing/resources/pixel/bug_1383708.in | 100 + + .../pixel/bug_1383708_expected.pdf.0.png | Bin 0 -> 2201 bytes + .../pixel/bug_1383708_expected_skia.pdf.0.png | Bin 0 -> 2202 bytes + .../pixel/bug_1388_2_expected.pdf.0.png | Bin 439 -> 418 bytes + .../bug_1388_2_expected_agg_mac.pdf.0.png | Bin 0 -> 408 bytes + .../pixel/bug_1388_2_expected_mac.pdf.0.png | Bin 694 -> 0 bytes + .../pixel/bug_1388_2_expected_win.pdf.0.png | Bin 439 -> 0 bytes + .../bug_1388_3_expected_agg_mac.pdf.0.png | Bin 0 -> 216 bytes + .../pixel/bug_1388_3_expected_mac.pdf.0.png | Bin 238 -> 0 bytes + .../pixel/bug_1388_expected_skia.pdf.0.png | Bin 0 -> 375 bytes + testing/resources/pixel/bug_1395648.in | 67 + + .../pixel/bug_1395648_expected.pdf.0.png | Bin 0 -> 398 bytes + .../pixel/bug_1395648_expected_skia.pdf.0.png | Bin 0 -> 499 bytes + testing/resources/pixel/bug_1396266.in | 98 + + .../pixel/bug_1396266_expected.pdf.0.png | Bin 0 -> 1319 bytes + .../pixel/bug_1396266_expected_skia.pdf.0.png | Bin 0 -> 1322 bytes + .../pixel/bug_1402_expected.pdf.0.png | Bin 582 -> 704 bytes + .../pixel/bug_1402_expected_mac.pdf.0.png | Bin 621 -> 0 bytes + .../pixel/bug_1402_expected_skia.pdf.0.png | Bin 0 -> 708 bytes + testing/resources/pixel/bug_1430333.in | 44 + + .../pixel/bug_1430333_expected.pdf.0.png | Bin 0 -> 309 bytes + .../pixel/bug_1430333_expected_skia.pdf.0.png | Bin 0 -> 363 bytes + testing/resources/pixel/bug_1469.in | 98 + + .../pixel/bug_1469_expected.pdf.0.png | Bin 0 -> 160 bytes + testing/resources/pixel/bug_1491.in | 44 + + .../pixel/bug_1491_expected.pdf.0.png | Bin 0 -> 221 bytes + .../pixel/bug_1491_expected_skia.pdf.0.png | Bin 0 -> 216 bytes + testing/resources/pixel/bug_1519.in | 48 + + .../pixel/bug_1519_expected.pdf.0.png | Bin 0 -> 1410 bytes + testing/resources/pixel/bug_1546.in | 77 + + .../pixel/bug_1546_expected.pdf.0.png | Bin 0 -> 2201 bytes + testing/resources/pixel/bug_1571.in | 112 + + .../pixel/bug_1571_expected.pdf.0.png | Bin 0 -> 225 bytes + testing/resources/pixel/bug_1638.in | 57 + + .../pixel/bug_1638_expected.pdf.0.png | Bin 0 -> 1192 bytes + .../pixel/bug_1638_expected_skia.pdf.0.png | Bin 0 -> 1159 bytes + testing/resources/pixel/bug_1639_1.in | 50 + + .../pixel/bug_1639_1_expected.pdf.0.png | Bin 0 -> 593 bytes + .../pixel/bug_1639_1_expected_skia.pdf.0.png | Bin 0 -> 561 bytes + testing/resources/pixel/bug_1693.in | 101 + + .../pixel/bug_1693_expected.pdf.0.png | Bin 0 -> 109 bytes + testing/resources/pixel/bug_1723.in | 61 + + .../pixel/bug_1723_expected.pdf.0.png | Bin 0 -> 363 bytes + testing/resources/pixel/bug_1733.in | 107 + + .../pixel/bug_1733_expected.pdf.0.png | Bin 0 -> 91 bytes + .../pixel/bug_1733_expected.pdf.1.png | Bin 0 -> 91 bytes + testing/resources/pixel/bug_1746.in | 122 + + .../pixel/bug_1746_expected.pdf.0.png | Bin 0 -> 1977 bytes + .../pixel/bug_1746_expected_skia.pdf.0.png | Bin 0 -> 1977 bytes + testing/resources/pixel/bug_1750.in | 83 + + .../pixel/bug_1750_expected.pdf.0.png | Bin 0 -> 168 bytes + testing/resources/pixel/bug_1752.in | 75 + + .../pixel/bug_1752_expected.pdf.0.png | Bin 0 -> 2235 bytes + .../pixel/bug_1752_expected_mac.pdf.0.png | Bin 0 -> 2205 bytes + .../pixel/bug_1752_expected_skia.pdf.0.png | Bin 0 -> 1710 bytes + testing/resources/pixel/bug_1772.in | 93 + + .../pixel/bug_1772_expected.pdf.0.png | Bin 0 -> 793 bytes + .../pixel/bug_1772_expected_skia.pdf.0.png | Bin 0 -> 488 bytes + testing/resources/pixel/bug_1774.in | 47 + + .../pixel/bug_1774_expected.pdf.0.png | Bin 0 -> 129 bytes + .../pixel/bug_1774_expected_skia.pdf.0.png | Bin 0 -> 157 bytes + testing/resources/pixel/bug_1822.in | 34 + + .../pixel/bug_1822_expected.pdf.0.png | Bin 0 -> 453 bytes + .../pixel/bug_1822_expected_skia.pdf.0.png | Bin 0 -> 672 bytes + testing/resources/pixel/bug_1845.in | 80 + + .../pixel/bug_1845_expected.pdf.0.png | Bin 0 -> 130 bytes + testing/resources/pixel/bug_1847.in | 65 + + .../pixel/bug_1847_expected.pdf.0.png | Bin 0 -> 524 bytes + .../pixel/bug_1847_expected_skia.pdf.0.png | Bin 0 -> 532 bytes + testing/resources/pixel/bug_1883.in | 92 + + .../pixel/bug_1883_expected.pdf.0.png | Bin 0 -> 1919 bytes + .../pixel/bug_1883_expected.pdf.1.png | Bin 0 -> 2019 bytes + .../pixel/bug_1883_expected.pdf.2.png | Bin 0 -> 1980 bytes + .../pixel/bug_1883_expected.pdf.3.png | Bin 0 -> 2001 bytes + .../pixel/bug_1883_expected_skia.pdf.0.png | Bin 0 -> 2085 bytes + .../pixel/bug_1883_expected_skia.pdf.1.png | Bin 0 -> 2280 bytes + .../pixel/bug_1883_expected_skia.pdf.2.png | Bin 0 -> 2130 bytes + .../pixel/bug_1883_expected_skia.pdf.3.png | Bin 0 -> 2323 bytes + testing/resources/pixel/bug_1922.in | 66 + + .../pixel/bug_1922_expected.pdf.0.png | Bin 0 -> 2732 bytes + .../pixel/bug_1922_expected_agg_mac.pdf.0.png | Bin 0 -> 835 bytes + testing/resources/pixel/bug_1949.in | 133 + + .../pixel/bug_1949_expected.pdf.0.png | Bin 0 -> 107 bytes + .../pixel/bug_1949_expected_skia.pdf.0.png | Bin 0 -> 107 bytes + testing/resources/pixel/bug_1963.in | 73 + + .../pixel/bug_1963_expected.pdf.0.png | Bin 0 -> 236 bytes + .../pixel/bug_1963_expected_skia.pdf.0.png | Bin 0 -> 250 bytes + testing/resources/pixel/bug_1966.in | 121 + + .../pixel/bug_1966_expected.pdf.0.png | Bin 0 -> 325 bytes + .../pixel/bug_1966_expected_skia.pdf.0.png | Bin 0 -> 324 bytes + testing/resources/pixel/bug_1972_1.in | 36 + + .../pixel/bug_1972_1_expected.pdf.0.png | Bin 0 -> 882 bytes + testing/resources/pixel/bug_1972_2.in | 36 + + .../pixel/bug_1972_2_expected.pdf.0.png | Bin 0 -> 696 bytes + testing/resources/pixel/bug_1972_3.in | 36 + + .../pixel/bug_1972_3_expected.pdf.0.png | Bin 0 -> 534 bytes + testing/resources/pixel/bug_1973.in | 81 + + .../pixel/bug_1973_expected.pdf.0.png | Bin 0 -> 105 bytes + testing/resources/pixel/bug_1976.in | 83 + + .../pixel/bug_1976_expected.pdf.0.png | Bin 0 -> 104 bytes + testing/resources/pixel/bug_1983.in | 43 + + .../pixel/bug_1983_expected.pdf.0.png | Bin 0 -> 294 bytes + .../pixel/bug_1983_expected_skia.pdf.0.png | Bin 0 -> 302 bytes + testing/resources/pixel/bug_1986.in | 61 + + .../pixel/bug_1986_expected.pdf.0.png | Bin 0 -> 154 bytes + testing/resources/pixel/bug_1995.in | 81 + + .../pixel/bug_1995_expected.pdf.0.png | Bin 0 -> 123 bytes + .../pixel/bug_1995_expected_skia.pdf.0.png | Bin 0 -> 130 bytes + testing/resources/pixel/bug_2001.pdf | Bin 0 -> 2453 bytes + .../pixel/bug_2001_expected.pdf.0.png | Bin 0 -> 6880 bytes + testing/resources/pixel/bug_237527_1.in | 37 + + .../pixel/bug_237527_1_expected.pdf.0.png | Bin 0 -> 157 bytes + .../bug_237527_1_expected_skia.pdf.0.png | Bin 0 -> 209 bytes + testing/resources/pixel/bug_237527_2.in | 42 + + .../pixel/bug_237527_2_expected.pdf.0.png | Bin 0 -> 106 bytes + .../pixel/bug_304_expected.pdf.0.png | Bin 154 -> 242 bytes + .../pixel/bug_451366_expected_skia.pdf.0.png | Bin 0 -> 230 bytes + .../bug_491_invisible_expected_skia.pdf.0.png | Bin 0 -> 156 bytes + ...ug_491_unspecified_expected_skia.pdf.0.png | Bin 0 -> 163 bytes + .../bug_491_visible_expected_skia.pdf.0.png | Bin 0 -> 163 bytes + testing/resources/pixel/bug_492.in | 2 +- + .../pixel/bug_524043_1_expected.pdf.0.png | Bin 5315 -> 1805 bytes + .../bug_524043_1_expected_agg_mac.pdf.0.png | Bin 0 -> 1665 bytes + .../pixel/bug_524043_1_expected_mac.pdf.0.png | Bin 4379 -> 0 bytes + .../pixel/bug_524043_1_expected_win.pdf.0.png | Bin 1716 -> 0 bytes + .../pixel/bug_524043_2_expected.pdf.0.png | Bin 2174 -> 761 bytes + .../bug_524043_2_expected_agg_mac.pdf.0.png | Bin 0 -> 1969 bytes + .../pixel/bug_524043_2_expected_mac.pdf.0.png | Bin 2028 -> 0 bytes + .../pixel/bug_524043_2_expected_win.pdf.0.png | Bin 739 -> 0 bytes + .../pixel/bug_524043_3_expected.pdf.0.png | Bin 5315 -> 1805 bytes + .../bug_524043_3_expected_agg_mac.pdf.0.png | Bin 0 -> 4632 bytes + .../pixel/bug_524043_3_expected_mac.pdf.0.png | Bin 4379 -> 0 bytes + .../pixel/bug_524043_3_expected_win.pdf.0.png | Bin 1716 -> 0 bytes + .../pixel/bug_524043_4_expected.pdf.0.png | Bin 5315 -> 1805 bytes + .../bug_524043_4_expected_agg_mac.pdf.0.png | Bin 0 -> 4632 bytes + .../pixel/bug_524043_4_expected_mac.pdf.0.png | Bin 4379 -> 0 bytes + .../pixel/bug_524043_4_expected_win.pdf.0.png | Bin 1716 -> 0 bytes + .../pixel/bug_524043_5_expected.pdf.0.png | Bin 5315 -> 1805 bytes + .../bug_524043_5_expected_agg_mac.pdf.0.png | Bin 0 -> 4632 bytes + .../pixel/bug_524043_5_expected_mac.pdf.0.png | Bin 4379 -> 0 bytes + .../pixel/bug_524043_5_expected_win.pdf.0.png | Bin 1716 -> 0 bytes + .../pixel/bug_524043_6_expected_mac.pdf.0.png | Bin 590 -> 0 bytes + .../pixel/bug_524043_7_expected.pdf.0.png | Bin 2174 -> 761 bytes + .../bug_524043_7_expected_agg_mac.pdf.0.png | Bin 0 -> 1969 bytes + .../pixel/bug_524043_7_expected_mac.pdf.0.png | Bin 2028 -> 0 bytes + .../pixel/bug_524043_7_expected_win.pdf.0.png | Bin 739 -> 0 bytes + .../pixel/bug_528103_expected.pdf.0.png | Bin 4191 -> 1542 bytes + .../bug_528103_expected_agg_mac.pdf.0.png | Bin 0 -> 3837 bytes + .../pixel/bug_528103_expected_mac.pdf.0.png | Bin 3572 -> 0 bytes + .../pixel/bug_528103_expected_win.pdf.0.png | Bin 1510 -> 0 bytes + .../pixel/bug_543018_1_expected.pdf.0.png | Bin 5315 -> 1805 bytes + .../bug_543018_1_expected_agg_mac.pdf.0.png | Bin 0 -> 4632 bytes + .../pixel/bug_543018_1_expected_mac.pdf.0.png | Bin 4379 -> 0 bytes + .../pixel/bug_543018_1_expected_win.pdf.0.png | Bin 1716 -> 0 bytes + .../pixel/bug_543018_2_expected.pdf.0.png | Bin 5315 -> 1805 bytes + .../bug_543018_2_expected_agg_mac.pdf.0.png | Bin 0 -> 4632 bytes + .../pixel/bug_543018_2_expected_mac.pdf.0.png | Bin 4379 -> 0 bytes + .../pixel/bug_543018_2_expected_win.pdf.0.png | Bin 1716 -> 0 bytes + .../pixel/bug_551258_1_expected.pdf.0.png | Bin 5315 -> 1805 bytes + .../bug_551258_1_expected_agg_mac.pdf.0.png | Bin 0 -> 4632 bytes + .../pixel/bug_551258_1_expected_mac.pdf.0.png | Bin 4379 -> 0 bytes + .../pixel/bug_551258_1_expected_win.pdf.0.png | Bin 1716 -> 0 bytes + .../pixel/bug_554151_expected.pdf.0.png | Bin 154 -> 154 bytes + testing/resources/pixel/bug_557223.in | 2 +- + .../pixel/bug_585_expected_skia.pdf.0.png | Bin 0 -> 1614 bytes + .../pixel/bug_591137_expected.pdf.0.png | Bin 3837 -> 1502 bytes + .../pixel/bug_601362_expected.pdf.0.png | Bin 602 -> 596 bytes + .../pixel/bug_601362_expected_skia.pdf.0.png | Bin 0 -> 581 bytes + .../pixel/bug_632_expected.pdf.0.png | Bin 142 -> 102 bytes + .../pixel/bug_632_expected.pdf.1.png | Bin 729 -> 731 bytes + .../pixel/bug_632_expected_skia.pdf.1.png | Bin 0 -> 591 bytes + .../pixel/bug_660850_expected_skia.pdf.0.png | Bin 0 -> 168 bytes + testing/resources/pixel/bug_665467.in | 96 +- + .../pixel/bug_665467_expected.pdf.0.png | Bin 467 -> 223 bytes + .../pixel/bug_665467_expected_mac.pdf.0.png | Bin 509 -> 553 bytes + .../pixel/bug_665467_expected_skia.pdf.0.png | Bin 0 -> 202 bytes + .../pixel/bug_665467_expected_win.pdf.0.png | Bin 187 -> 0 bytes + .../pixel/bug_714187_expected_skia.pdf.0.png | Bin 0 -> 1318 bytes + testing/resources/pixel/bug_725389.in | 58 + + .../pixel/bug_725389_expected.pdf.0.png | Bin 0 -> 521 bytes + .../pixel/bug_725389_expected_mac.pdf.0.png | Bin 0 -> 512 bytes + .../pixel/bug_725389_expected_skia.pdf.0.png | Bin 0 -> 442 bytes + .../bug_725389_expected_skia_mac.pdf.0.png | Bin 0 -> 522 bytes + .../pixel/bug_733528_expected.pdf.0.png | Bin 3465 -> 2633 bytes + .../pixel/bug_733528_expected_mac.pdf.0.png | Bin 3349 -> 3335 bytes + .../pixel/bug_733528_expected_skia.pdf.0.png | Bin 0 -> 1195 bytes + .../pixel/bug_733528_expected_win.pdf.0.png | Bin 2550 -> 0 bytes + .../pixel/bug_736695_1_expected.pdf.0.png | Bin 2055 -> 1003 bytes + .../bug_736695_1_expected_skia.pdf.0.png | Bin 0 -> 937 bytes + .../pixel/bug_736695_2_expected.pdf.0.png | Bin 4404 -> 2910 bytes + .../pixel/bug_736695_2_expected_mac.pdf.0.png | Bin 4347 -> 4367 bytes + .../bug_736695_2_expected_skia.pdf.0.png | Bin 0 -> 1555 bytes + .../pixel/bug_736695_2_expected_win.pdf.0.png | Bin 2831 -> 0 bytes + .../pixel/bug_736695_3_expected.pdf.0.png | Bin 2860 -> 1661 bytes + .../pixel/bug_736695_3_expected_mac.pdf.0.png | Bin 2811 -> 2849 bytes + .../bug_736695_3_expected_skia.pdf.0.png | Bin 0 -> 1461 bytes + .../pixel/bug_736695_3_expected_win.pdf.0.png | Bin 1602 -> 0 bytes + .../pixel/bug_736695_4_expected.pdf.0.png | Bin 2055 -> 1003 bytes + .../bug_736695_4_expected_skia.pdf.0.png | Bin 0 -> 937 bytes + testing/resources/pixel/bug_736703.in | 73 + + .../pixel/bug_736703_expected.pdf.0.png | Bin 0 -> 1615 bytes + .../pixel/bug_820345_expected.pdf.0.png | Bin 1649 -> 1612 bytes + .../bug_820345_expected_agg_mac.pdf.0.png | Bin 0 -> 4165 bytes + .../pixel/bug_820345_expected_mac.pdf.0.png | Bin 2528 -> 0 bytes + .../pixel/bug_820345_expected_win.pdf.0.png | Bin 1589 -> 0 bytes + testing/resources/pixel/bug_842.in | 2 +- + .../pixel/bug_842_expected_skia.pdf.0.png | Bin 0 -> 1140 bytes + .../pixel/bug_843_expected_skia.pdf.0.png | Bin 0 -> 351 bytes + .../pixel/bug_845697_expected.pdf.0.png | Bin 839 -> 788 bytes + testing/resources/pixel/bug_846.in | 160 + + .../pixel/bug_846_expected.pdf.0.png | Bin 0 -> 1329 bytes + .../pixel/bug_846_expected_mac.pdf.0.png | Bin 0 -> 1023 bytes + .../pixel/bug_846_expected_skia.pdf.0.png | Bin 0 -> 1233 bytes + .../pixel/bug_846_expected_skia_mac.pdf.0.png | Bin 0 -> 1175 bytes + .../pixel/bug_846_expected_skia_win.pdf.0.png | Bin 0 -> 1175 bytes + .../pixel/bug_909762_expected.pdf.0.png | Bin 748 -> 751 bytes + .../pixel/bug_909762_expected_win.pdf.0.png | Bin 725 -> 0 bytes + .../bug_925736_expected_agg_mac.pdf.0.png | Bin 0 -> 1637 bytes + .../pixel/bug_925736_expected_mac.pdf.0.png | Bin 2003 -> 0 bytes + testing/resources/pixel/bug_966263.in | 132 + + .../pixel/bug_966263_expected.pdf.0.png | Bin 0 -> 135 bytes + testing/resources/pixel/bug_972999.in | 603 + + .../pixel/bug_972999_expected.pdf.0.png | Bin 0 -> 404 bytes + .../pixel/bug_972999_expected_skia.pdf.0.png | Bin 0 -> 564 bytes + testing/resources/pixel/bug_983289.in | 35 + + .../pixel/bug_983289_expected.pdf.0.png | Bin 0 -> 4355 bytes + .../bug_984811_expected_agg_mac.pdf.0.png | Bin 0 -> 1143 bytes + .../pixel/bug_984811_expected_mac.pdf.0.png | Bin 1625 -> 0 bytes + testing/resources/pixel/bug_986108.in | 122 + + .../pixel/bug_986108_expected.pdf.0.png | Bin 0 -> 100 bytes + .../resources/pixel/checkbox_radiobutton.evt | 11 + + .../pixel/checkbox_radiobutton.fragment | 187 + + .../resources/pixel/checkbox_radiobutton.in | 14 + + .../checkbox_radiobutton_expected.pdf.0.png | Bin 0 -> 1567 bytes + ...eckbox_radiobutton_expected_skia.pdf.0.png | Bin 0 -> 1251 bytes + .../pixel/checkbox_radiobutton_hide.in | 23 + + ...eckbox_radiobutton_hide_expected.pdf.0.png | Bin 0 -> 1248 bytes + ...x_radiobutton_hide_expected_skia.pdf.0.png | Bin 0 -> 1062 bytes + .../pixel/checkbox_radiobutton_reset.in | 25 + + ...ckbox_radiobutton_reset_expected.pdf.0.png | Bin 0 -> 1291 bytes + ..._radiobutton_reset_expected_skia.pdf.0.png | Bin 0 -> 1162 bytes + testing/resources/pixel/combobox_form.evt | 10 + + testing/resources/pixel/combobox_form.in | 35 + + .../pixel/combobox_form_expected.pdf.0.png | Bin 0 -> 1163 bytes + .../combobox_form_expected_skia.pdf.0.png | Bin 0 -> 1169 bytes + .../pixel/font_size_expected.pdf.0.png | Bin 11961 -> 3705 bytes + .../font_size_expected_agg_mac.pdf.0.png | Bin 0 -> 10451 bytes + .../pixel/font_size_expected_mac.pdf.0.png | Bin 10012 -> 0 bytes + .../pixel/font_size_expected_win.pdf.0.png | Bin 3615 -> 0 bytes + .../generation_numbers1_expected.pdf.0.png | Bin 1537 -> 1506 bytes + ...ration_numbers1_expected_agg_mac.pdf.0.png | Bin 0 -> 3964 bytes + ...generation_numbers1_expected_mac.pdf.0.png | Bin 2375 -> 0 bytes + ...generation_numbers1_expected_win.pdf.0.png | Bin 1482 -> 0 bytes + .../generation_numbers2_expected.pdf.0.png | Bin 1537 -> 1506 bytes + ...ration_numbers2_expected_agg_mac.pdf.0.png | Bin 0 -> 3964 bytes + ...generation_numbers2_expected_mac.pdf.0.png | Bin 2375 -> 0 bytes + ...generation_numbers2_expected_win.pdf.0.png | Bin 1482 -> 0 bytes + .../pixel/image_transformer_other.in | 128 + + ...image_transformer_other_expected.pdf.0.png | Bin 0 -> 1320 bytes + ..._transformer_other_expected_skia.pdf.0.png | Bin 0 -> 1161 bytes + testing/resources/pixel/jpxdecode.in | 177 + + .../pixel/jpxdecode_expected.pdf.0.png | Bin 0 -> 290 bytes + .../jpxdecode_with_mismatch_colorspace.in | 180 + + ...ith_mismatch_colorspace_expected.pdf.0.png | Bin 0 -> 91 bytes + .../jpxdecode_without_bitspercomponent.in | 171 + + ...ithout_bitspercomponent_expected.pdf.0.png | Bin 0 -> 290 bytes + .../pixel/jpxdecode_without_colorspace.in | 171 + + ...code_without_colorspace_expected.pdf.0.png | Bin 0 -> 290 bytes + .../pixel/jpxdecode_without_smaskindata.in | 111 + + ...ode_without_smaskindata_expected.pdf.0.png | Bin 0 -> 179 bytes + testing/resources/pixel/long_dashed_line.in | 33 + + .../pixel/long_dashed_line_expected.pdf.0.png | Bin 0 -> 4400 bytes + .../long_dashed_line_expected_skia.pdf.0.png | Bin 0 -> 4401 bytes + .../pixel/matte_expected_skia.pdf.0.png | Bin 0 -> 316 bytes + testing/resources/pixel/password.evt | 26 + + testing/resources/pixel/password.in | 54 + + .../pixel/password_expected.pdf.0.png | Bin 0 -> 2239 bytes + .../pixel/password_expected_mac.pdf.0.png | Bin 0 -> 3117 bytes + .../pixel/password_expected_skia.pdf.0.png | Bin 0 -> 2049 bytes + ...ng_point_at_border_expected_skia.pdf.0.png | Bin 0 -> 4188 bytes + ...t_border_no_extend_expected_skia.pdf.0.png | Bin 0 -> 7106 bytes + ...ng_point_at_center_expected_skia.pdf.0.png | Bin 0 -> 13150 bytes + testing/resources/pixel/reset_button.evt | 10 + + testing/resources/pixel/reset_button.in | 97 + + .../pixel/reset_button_expected.pdf.0.png | Bin 0 -> 401 bytes + .../resources/pixel/scrollable_widgets1.evt | 29 + + .../resources/pixel/scrollable_widgets1.in | 52 + + .../scrollable_widgets1_expected.pdf.0.png | Bin 0 -> 3335 bytes + ...scrollable_widgets1_expected_mac.pdf.0.png | Bin 0 -> 5131 bytes + ...crollable_widgets1_expected_skia.pdf.0.png | Bin 0 -> 3240 bytes + .../resources/pixel/scrollable_widgets2.evt | 34 + + .../resources/pixel/scrollable_widgets2.in | 52 + + .../scrollable_widgets2_expected.pdf.0.png | Bin 0 -> 3493 bytes + ...scrollable_widgets2_expected_mac.pdf.0.png | Bin 0 -> 5494 bytes + ...crollable_widgets2_expected_skia.pdf.0.png | Bin 0 -> 3476 bytes + .../resources/pixel/text_form_custom_font.in | 81 + + .../text_form_custom_font_expected.pdf.0.png | Bin 0 -> 464 bytes + ...xt_form_custom_font_expected_mac.pdf.0.png | Bin 0 -> 396 bytes + ...t_form_custom_font_expected_skia.pdf.0.png | Bin 0 -> 451 bytes + .../resources/pixel/type3_expected.pdf.0.png | Bin 194 -> 194 bytes + .../resources/pixel/use_symbolneu/bug_1449.in | 47 + + .../use_symbolneu/bug_1449_expected.pdf.0.png | Bin 0 -> 91 bytes + .../barcode_test_expected_skia.pdf.0.png | Bin 0 -> 7424 bytes + .../barcode_test_expected_win.pdf.0.png | Bin 7471 -> 0 bytes + .../bug_1282_expected_skia.pdf.0.png | Bin 0 -> 322 bytes + .../pixel/xfa_specific/bug_1286970.in | 31 + + .../bug_1286970_expected.pdf.0.png | Bin 0 -> 86 bytes + .../resources/pixel/xfa_specific/bug_1337.in | 38 + + .../xfa_specific/bug_1337_expected.pdf.0.png | Bin 0 -> 324 bytes + .../bug_983137_expected_skia.pdf.0.png | Bin 0 -> 335 bytes + ...multiple_selection_expected_skia.pdf.0.png | Bin 0 -> 10089 bytes + ...iple_selection_expected_skia_mac.pdf.0.png | Bin 0 -> 10091 bytes + ...d_field_background_fill_expected.pdf.0.png | Bin 33277 -> 32765 bytes + ...ld_background_fill_expected_skia.pdf.0.png | Bin 0 -> 33017 bytes + ...eld_background_fill_expected_win.pdf.0.png | Bin 33277 -> 0 bytes + ...le_color_and_width_expected_skia.pdf.0.png | Bin 0 -> 12728 bytes + ...ble_color_and_width_expected_win.pdf.0.png | Bin 12534 -> 0 bytes + .../resolve_nodes_0_expected.pdf.0.png | Bin 61384 -> 61348 bytes + .../resolve_nodes_0_expected_skia.pdf.0.png | Bin 0 -> 62206 bytes + .../standard_symbols_expected.pdf.0.png | Bin 0 -> 23942 bytes + .../standard_symbols_expected.pdf.1.png | Bin 0 -> 28454 bytes + ...ic_list_box_caption_expected_mac.pdf.0.png | Bin 12298 -> 12917 bytes + ...c_list_box_caption_expected_skia.pdf.0.png | Bin 0 -> 6943 bytes + ...ic_list_box_caption_expected_win.pdf.0.png | Bin 17874 -> 0 bytes + ...ssword_field_rotate_expected_mac.pdf.0.png | Bin 29781 -> 33039 bytes + ...ssword_field_rotate_expected_mac.pdf.1.png | Bin 25926 -> 29697 bytes + ...sword_field_rotate_expected_skia.pdf.0.png | Bin 0 -> 33033 bytes + ...sword_field_rotate_expected_skia.pdf.1.png | Bin 0 -> 30246 bytes + ...ssword_field_rotate_expected_win.pdf.0.png | Bin 33445 -> 0 bytes + ...ssword_field_rotate_expected_win.pdf.1.png | Bin 30509 -> 0 bytes + .../pixel/xfa_specific/use_ahem/README.md | 2 + + .../pixel/xfa_specific/use_ahem/bug_997412.in | 35 + + .../use_ahem/bug_997412_expected.pdf.0.png | Bin 0 -> 154 bytes + .../use_ahem/bug_997412_expected.pdf.1.png | Bin 0 -> 1083 bytes + .../bug_997412_expected_skia.pdf.1.png | Bin 0 -> 597 bytes + .../xfa_example_expected_skia.pdf.0.png | Bin 0 -> 1089 bytes + .../xfa_textfield_expected_skia.pdf.0.png | Bin 0 -> 1177 bytes + .../pixel/xfa_specific/xfa_bmp_image.in | 2 +- + .../xfa_bmp_image_expected_skia.pdf.0.png | Bin 0 -> 1137 bytes + .../xfa_gif_image_expected_skia.pdf.0.png | Bin 0 -> 1137 bytes + .../xfa_jpg_image_expected_skia.pdf.0.png | Bin 0 -> 1137 bytes + .../xfa_node_caption_expected.pdf.0.png | Bin 17715 -> 18137 bytes + .../xfa_node_caption_expected.pdf.1.png | Bin 20733 -> 21578 bytes + .../xfa_node_caption_expected_mac.pdf.0.png | Bin 0 -> 17218 bytes + .../xfa_node_caption_expected_mac.pdf.1.png | Bin 0 -> 20375 bytes + .../xfa_node_caption_expected_skia.pdf.0.png | Bin 0 -> 18985 bytes + .../xfa_node_caption_expected_skia.pdf.1.png | Bin 0 -> 22312 bytes + ...a_node_caption_expected_skia_mac.pdf.0.png | Bin 0 -> 18169 bytes + ...a_node_caption_expected_skia_mac.pdf.1.png | Bin 0 -> 21094 bytes + ...a_node_caption_expected_skia_win.pdf.0.png | Bin 0 -> 19003 bytes + ...a_node_caption_expected_skia_win.pdf.1.png | Bin 0 -> 21831 bytes + .../xfa_node_caption_expected_win.pdf.0.png | Bin 0 -> 18108 bytes + .../xfa_node_caption_expected_win.pdf.1.png | Bin 0 -> 21159 bytes + .../xfa_png_image_expected_skia.pdf.0.png | Bin 0 -> 1137 bytes + ...xfa_rectangle_node_expected_skia.pdf.0.png | Bin 0 -> 1514 bytes + .../xfa_specific/xfa_tiff_deflate_image.in | 39 - + .../xfa_tiff_deflate_image_expected.pdf.0.png | Bin 3776 -> 0 bytes + .../xfa_tiff_image_expected_skia.pdf.0.png | Bin 0 -> 1137 bytes + ...xfa_tiff_lzw_image_expected_skia.pdf.0.png | Bin 0 -> 1137 bytes + ...iff_packbits_image_expected_skia.pdf.0.png | Bin 0 -> 1137 bytes + testing/resources/polygon_annot.in | 48 + + testing/resources/polygon_annot.pdf | 60 + + testing/resources/rectangles.in | 5 +- + testing/resources/rectangles.pdf | 16 +- + testing/resources/redact_annot.in | 44 + + testing/resources/redact_annot.pdf | 56 + + testing/resources/rotated_image.in | 52 + + testing/resources/rotated_image.pdf | 64 + + testing/resources/signature_no_sub_filter.in | 100 + + testing/resources/signature_no_sub_filter.pdf | 124 + + testing/resources/signature_reason.in | 101 + + testing/resources/signature_reason.pdf | 125 + + testing/resources/simple_xfa.in | 1 + + testing/resources/simple_xfa.pdf | 15 +- + testing/resources/split_streams.in | 2 +- + testing/resources/tagged_actual_text.in | 162 + + testing/resources/tagged_actual_text.pdf | 183 + + testing/resources/tagged_marked_content.in | 140 + + testing/resources/tagged_marked_content.pdf | 159 + + testing/resources/tagged_mcr_objr.in | 160 + + testing/resources/tagged_mcr_objr.pdf | 182 + + testing/resources/tagged_nested.in | 346 + + testing/resources/tagged_nested.pdf | 395 + + testing/resources/tagged_table.in | 217 + + testing/resources/tagged_table.pdf | 241 + + testing/resources/tagged_table_bad_elem.in | 153 + + testing/resources/tagged_table_bad_elem.pdf | 172 + + testing/resources/text_in_page_marked.in | 2 +- + .../resources/text_in_page_marked_indirect.in | 2 +- + .../resources/trailer_end_trailing_space.in | 86 + + .../resources/trailer_end_trailing_space.pdf | 99 + + testing/resources/two_signatures.in | 155 + + testing/resources/two_signatures.pdf | 195 + + testing/resources/uri_action_nonascii.in | 44 + + testing/resources/uri_action_nonascii.pdf | 56 + + testing/resources/viewer_pref_types.in | 36 + + testing/resources/viewer_pref_types.pdf | 46 + + .../resources/xfa/xfa_break_before_after.in | 54 + + .../resources/xfa/xfa_break_before_after.pdf | 262 + + testing/resources/xfa/xfa_combobox.pdf | 2 +- + testing/resources/xfa/xfa_date_time_edit.pdf | 2 +- + testing/resources/xfa/xfa_image_edit.in | 4 +- + testing/resources/xfa/xfa_image_edit.pdf | 126 +- + .../resources/xfa/xfa_multiline_textfield.pdf | 2 +- + testing/resources/xfa_pages_8_0.fragment | 2 +- + testing/scoped_set_tz.cpp | 44 + + testing/scoped_set_tz.h | 26 + + testing/string_write_stream.cpp | 23 +- + testing/string_write_stream.h | 13 +- + testing/test.gni | 143 +- + testing/test_fonts.cpp | 116 + + testing/test_fonts.h | 27 + + testing/test_loader.cpp | 4 +- + testing/test_loader.h | 2 +- + testing/test_support.cpp | 15 - + testing/test_support.h | 5 +- + testing/tools/.style.yapf | 2 - + testing/tools/PRESUBMIT.py | 8 +- + testing/tools/api_check.py | 10 +- + testing/tools/common.py | 74 +- + testing/tools/coverage/coverage_report.py | 71 +- + testing/tools/encode_pdf_filter.py | 260 +- + testing/tools/fixup_pdf_template.py | 103 +- + testing/tools/githelper.py | 9 +- + testing/tools/gold.py | 284 - + testing/tools/libcxx_check.py | 65 + + testing/tools/make_expected.sh | 6 +- + testing/tools/pdfium_root.py | 53 + + testing/tools/pngdiffer.py | 280 +- + testing/tools/run_corpus_tests.py | 6 +- + testing/tools/run_javascript_tests.py | 5 +- + testing/tools/run_pixel_tests.py | 5 +- + testing/tools/safetynet_compare.py | 12 +- + testing/tools/safetynet_conclusions.py | 26 +- + testing/tools/safetynet_image.py | 13 +- + testing/tools/safetynet_job.py | 13 +- + testing/tools/safetynet_measure.py | 15 +- + testing/tools/skia_gold/__init__.py | 7 + + .../skia_gold/pdfium_skia_gold_properties.py | 27 + + .../skia_gold/pdfium_skia_gold_session.py | 29 + + .../pdfium_skia_gold_session_manager.py | 21 + + testing/tools/skia_gold/skia_gold.py | 221 + + testing/tools/strip_jp2_comments.py | 124 + + testing/tools/suppressor.py | 45 +- + testing/tools/test_runner.py | 1124 +- + testing/tools/text_diff.py | 8 +- + testing/unit_test_main.cpp | 51 +- + testing/utils/bitmap_saver.cpp | 9 +- + testing/utils/bitmap_saver.h | 2 +- + testing/utils/file_util.cpp | 5 +- + testing/utils/file_util.h | 2 +- + testing/utils/hash.cpp | 6 +- + testing/utils/hash.h | 6 +- + testing/utils/path_service.cpp | 53 +- + testing/utils/path_service.h | 6 +- + testing/v8_initializer.cpp | 38 +- + testing/v8_initializer.h | 6 +- + testing/v8_test_environment.cpp | 76 + + testing/v8_test_environment.h | 51 + + testing/xfa_js_embedder_test.cpp | 87 +- + testing/xfa_js_embedder_test.h | 30 +- + testing/xfa_test_environment.cpp | 40 + + testing/xfa_test_environment.h | 24 + + testing/xfa_unit_test_support.cpp | 55 - + testing/xfa_unit_test_support.h | 18 - + third_party/Android.bp | 6 +- + third_party/BUILD.gn | 428 +- + third_party/NotoSansCJK/LICENSE | 78 + + .../NotoSansCJK/NotoSansSC-Regular.subset.otf | Bin 0 -> 3628 bytes + third_party/NotoSansCJK/README.pdfium | 23 + + third_party/abseil-cpp/absl/base/attributes.h | 783 + + third_party/abseil-cpp/absl/base/config.h | 954 ++ + .../abseil-cpp/absl/base/internal/identity.h | 37 + + .../absl/base/internal/inline_variable.h | 107 + + .../abseil-cpp/absl/base/internal/invoke.h | 241 + + third_party/abseil-cpp/absl/base/options.h | 232 + + .../abseil-cpp/absl/base/policy_checks.h | 113 + + .../abseil-cpp/absl/meta/type_traits.h | 889 ++ + third_party/abseil-cpp/absl/types/optional.h | 779 + + third_party/abseil-cpp/absl/types/variant.h | 866 + + third_party/abseil-cpp/absl/utility/utility.h | 350 + + third_party/agg23/0000-bug-466.patch | 8 +- + third_party/agg23/0008-namespace.patch | 508 + + third_party/agg23/0009-infinite-loop.patch | 25 + + third_party/agg23/0010-math.patch | 12 + + .../agg23/0011-path-storage-move-ctor.patch | 67 + + third_party/agg23/0012-infinite-loop.patch | 25 + + third_party/agg23/0013-cxx20.patch | 56 + + .../agg23/0014-ubsan-render-line.patch | 35 + + third_party/agg23/README.pdfium | 10 +- + third_party/agg23/agg_array.h | 7 +- + third_party/agg23/agg_basics.h | 6 +- + third_party/agg23/agg_clip_liang_barsky.h | 3 + + third_party/agg23/agg_color_gray.h | 3 + + third_party/agg23/agg_conv_adaptor_vcgen.h | 3 + + third_party/agg23/agg_conv_dash.h | 3 + + third_party/agg23/agg_conv_stroke.h | 3 + + third_party/agg23/agg_curves.cpp | 3 + + third_party/agg23/agg_curves.h | 26 +- + third_party/agg23/agg_math.h | 3 + + third_party/agg23/agg_math_stroke.h | 18 +- + third_party/agg23/agg_path_storage.cpp | 27 +- + third_party/agg23/agg_path_storage.h | 35 +- + third_party/agg23/agg_pixfmt_gray.h | 3 + + .../agg23/agg_rasterizer_scanline_aa.cpp | 28 +- + .../agg23/agg_rasterizer_scanline_aa.h | 5 +- + third_party/agg23/agg_render_scanlines.h | 3 + + third_party/agg23/agg_renderer_base.h | 3 + + third_party/agg23/agg_renderer_scanline.h | 3 + + third_party/agg23/agg_rendering_buffer.h | 3 + + third_party/agg23/agg_scanline_u.h | 3 + + third_party/agg23/agg_shorten_path.h | 3 + + third_party/agg23/agg_vcgen_dash.cpp | 8 + + third_party/agg23/agg_vcgen_dash.h | 3 + + third_party/agg23/agg_vcgen_stroke.cpp | 7 +- + third_party/agg23/agg_vcgen_stroke.h | 3 + + third_party/agg23/agg_vertex_sequence.h | 3 + + .../address_space_randomization.cc | 70 - + .../address_space_randomization.h | 203 - + .../base/allocator/partition_allocator/oom.h | 40 - + .../partition_allocator/oom_callback.cc | 29 - + .../partition_allocator/oom_callback.h | 26 - + .../partition_allocator/page_allocator.cc | 257 - + .../partition_allocator/page_allocator.h | 195 - + .../page_allocator_constants.h | 50 - + .../page_allocator_internal.h | 20 - + .../page_allocator_internals_posix.h | 216 - + .../page_allocator_internals_win.h | 145 - + .../partition_allocator/partition_alloc.cc | 846 - + .../partition_allocator/partition_alloc.h | 529 - + .../partition_alloc_constants.h | 189 - + .../partition_allocator/partition_bucket.cc | 569 - + .../partition_allocator/partition_bucket.h | 130 - + .../partition_allocator/partition_cookie.h | 72 - + .../partition_direct_map_extent.h | 35 - + .../partition_freelist_entry.h | 73 - + .../partition_allocator/partition_oom.cc | 26 - + .../partition_allocator/partition_oom.h | 28 - + .../partition_allocator/partition_page.cc | 165 - + .../partition_allocator/partition_page.h | 294 - + .../partition_root_base.cc | 42 - + .../partition_allocator/partition_root_base.h | 198 - + .../allocator/partition_allocator/random.cc | 96 - + .../allocator/partition_allocator/random.h | 29 - + .../partition_allocator/spin_lock.cc | 106 - + .../allocator/partition_allocator/spin_lock.h | 52 - + third_party/base/bits.h | 158 +- + third_party/base/check.h | 36 + + third_party/base/check_op.h | 24 + + third_party/base/compiler_specific.h | 165 +- + third_party/base/component_export.h | 76 + + third_party/base/containers/adapters.h | 54 + + third_party/base/containers/contains.h | 114 + + third_party/base/cxx17_backports.h | 34 + + third_party/base/immediate_crash.h | 154 + + third_party/base/logging.h | 125 - + third_party/base/memory/aligned_memory.cc | 50 + + third_party/base/memory/aligned_memory.h | 85 + + third_party/base/no_destructor.h | 76 +- + third_party/base/notreached.h | 24 + + third_party/base/numerics/README.pdfium | 6 + + third_party/base/numerics/checked_math.h | 382 + + third_party/base/numerics/checked_math_impl.h | 595 + + third_party/base/numerics/clamped_math.h | 262 + + third_party/base/numerics/clamped_math_impl.h | 342 + + third_party/base/numerics/safe_conversions.h | 121 +- + .../base/numerics/safe_conversions_arm_impl.h | 4 +- + .../base/numerics/safe_conversions_impl.h | 89 +- + third_party/base/numerics/safe_math.h | 506 +- + .../base/numerics/safe_math_arm_impl.h | 127 + + .../base/numerics/safe_math_clang_gcc_impl.h | 159 + + third_party/base/numerics/safe_math_impl.h | 647 - + .../base/numerics/safe_math_shared_impl.h | 218 + + third_party/base/optional.h | 511 - + third_party/base/ptr_util.h | 52 - + third_party/base/span.h | 76 +- + third_party/base/stl_util.h | 101 - + third_party/base/sys_byteorder.h | 2 +- + third_party/base/template_util.h | 114 +- + third_party/base/win/scoped_select_object.h | 45 + + third_party/bigint/BigInteger.cc | 2 +- + third_party/bigint/BigInteger.hh | 2 +- + third_party/bigint/BigIntegerLibrary.hh | 2 +- + third_party/bigint/BigIntegerUtils.cc | 4 +- + third_party/bigint/BigIntegerUtils.hh | 4 +- + third_party/bigint/BigUnsigned.cc | 2 +- + third_party/bigint/BigUnsigned.hh | 2 +- + third_party/bigint/BigUnsignedInABase.cc | 2 +- + third_party/bigint/BigUnsignedInABase.hh | 2 +- + third_party/bigint/NumberlikeArray.hh | 2 +- + third_party/eu-strip/README.pdfium | 24 - + third_party/eu-strip/bin/eu-strip | Bin 105120 -> 0 bytes + third_party/freetype/0000-include.patch | 6 +- + third_party/freetype/README.pdfium | 9 +- + .../include/freetype-custom-config/ftmodule.h | 2 - + .../include/freetype-custom-config/ftoption.h | 18 + + third_party/freetype/include/pstables.h | 2 +- + third_party/freetype/roll-freetype.sh | 17 +- + third_party/fuchsia-sdk/DIR_METADATA | 2 + + third_party/fuchsia-sdk/README.chromium | 11 + + third_party/googletest/BUILD.gn | 95 +- + .../custom/gtest/internal/custom/gtest.h | 23 + + .../internal/custom/gtest_port_wrapper.cc | 151 + + .../internal/custom/pdfium_custom_temp_dir.cc | 82 + + .../internal/custom/pdfium_custom_temp_dir.h | 15 + + third_party/lcms/0000-cmserr-changes.patch | 2 +- + .../lcms/0002-old-performance-fix.patch | 13 - + .../lcms/0005-old-fix-e-with-tilde.patch | 61 - + ...008-infinite-loop-GrowNamedColorList.patch | 25 - + third_party/lcms/0019-utf8.patch | 98 - + .../0026-more-unsupported-characters.patch | 41 - + .../0027-changes-from-beginning-of-time.patch | 39 +- + .../lcms/0029-drop-register-keyword.patch | 25 +- + third_party/lcms/0030-const-data.patch | 79 +- + .../lcms/0031-wrong-tag-element-count.patch | 12 - + third_party/lcms/0032-cgats-allocation.patch | 24 - + third_party/lcms/0034-dead-code.patch | 14 + + third_party/lcms/README.pdfium | 10 +- + third_party/lcms/include/lcms2.h | 85 +- + third_party/lcms/include/lcms2_plugin.h | 69 +- + third_party/lcms/src/cmsalpha.c | 149 +- + third_party/lcms/src/cmscam02.c | 4 +- + third_party/lcms/src/cmscgats.c | 431 +- + third_party/lcms/src/cmscnvrt.c | 134 +- + third_party/lcms/src/cmserr.c | 53 +- + third_party/lcms/src/cmsgamma.c | 174 +- + third_party/lcms/src/cmsgmt.c | 103 +- + third_party/lcms/src/cmshalf.c | 8 +- + third_party/lcms/src/cmsintrp.c | 716 +- + third_party/lcms/src/cmsio0.c | 261 +- + third_party/lcms/src/cmsio1.c | 34 +- + third_party/lcms/src/cmslut.c | 59 +- + third_party/lcms/src/cmsmd5.c | 32 +- + third_party/lcms/src/cmsmtrx.c | 2 +- + third_party/lcms/src/cmsnamed.c | 67 +- + third_party/lcms/src/cmsopt.c | 141 +- + third_party/lcms/src/cmspack.c | 1515 +- + third_party/lcms/src/cmspcs.c | 18 +- + third_party/lcms/src/cmsplugin.c | 239 +- + third_party/lcms/src/cmsps2.c | 217 +- + third_party/lcms/src/cmssamp.c | 17 +- + third_party/lcms/src/cmssm.c | 2 +- + third_party/lcms/src/cmstypes.c | 478 +- + third_party/lcms/src/cmsvirt.c | 42 +- + third_party/lcms/src/cmswtpnt.c | 33 +- + third_party/lcms/src/cmsxform.c | 206 +- + third_party/lcms/src/lcms2_internal.h | 181 +- + .../0003-dwt-decode.patch | 131 +- + .../0005-jp2_apply_pclr.patch | 14 +- + .../0006-tcd_init_tile.patch | 10 +- + .../0007-jp2_read_cmap.patch | 12 +- + .../0009-opj_pi_next.patch | 32 +- + .../0011-j2k_update_image_data.patch | 10 +- + .../0012-mct_sse.patch | 37 +- + .../0014-opj_jp2_read_ihdr_leak.patch | 12 +- + .../0015-read_SPCod_SPCoc_overflow.patch | 10 +- + .../0016-read_SQcd_SQcc_overflow.patch | 10 +- + .../0019-tcd_init_tile.patch | 10 +- + .../0022-jp2_apply_pclr_overflow.patch | 12 +- + .../0023-opj_j2k_read_mct_records.patch | 14 +- + .../0025-opj_j2k_add_mct_null_data.patch | 12 +- + .../0026-use_opj_uint_ceildiv.patch | 35 +- + third_party/libopenjpeg/0034-opj_malloc.patch | 88 + + .../0035-opj_image_data_free.patch | 10 +- + .../libopenjpeg/0039-opj_mqc_renorme.patch | 16 + + .../libopenjpeg/0041-remove_opj_clock.patch | 12 + + .../0042-popcnt-windows-arm64.patch | 22 + + third_party/libopenjpeg/0043-mel_init.patch | 59 + + .../0044-opj_t1_allocate_buffers.patch | 28 + + ...-replace-sprintf-calls-with-snprintf.patch | 82 + + .../README.pdfium | 12 +- + .../{libopenjpeg20 => libopenjpeg}/bio.c | 0 + .../{libopenjpeg20 => libopenjpeg}/bio.h | 0 + .../{libopenjpeg20 => libopenjpeg}/cio.c | 0 + .../{libopenjpeg20 => libopenjpeg}/cio.h | 2 +- + .../{libopenjpeg20 => libopenjpeg}/dwt.c | 1804 ++- + .../{libopenjpeg20 => libopenjpeg}/dwt.h | 20 +- + .../{libopenjpeg20 => libopenjpeg}/event.c | 0 + .../{libopenjpeg20 => libopenjpeg}/event.h | 0 + .../function_list.c | 0 + .../function_list.h | 0 + third_party/libopenjpeg/ht_dec.c | 2653 ++++ + .../{libopenjpeg20 => libopenjpeg}/image.c | 1 - + .../{libopenjpeg20 => libopenjpeg}/image.h | 0 + .../{libopenjpeg20 => libopenjpeg}/invert.c | 0 + .../{libopenjpeg20 => libopenjpeg}/invert.h | 0 + .../{libopenjpeg20 => libopenjpeg}/j2k.c | 1477 +- + .../{libopenjpeg20 => libopenjpeg}/j2k.h | 45 +- + .../{libopenjpeg20 => libopenjpeg}/jp2.c | 47 +- + .../{libopenjpeg20 => libopenjpeg}/jp2.h | 23 + + .../{libopenjpeg20 => libopenjpeg}/mct.c | 218 +- + .../{libopenjpeg20 => libopenjpeg}/mct.h | 5 +- + .../{libopenjpeg20 => libopenjpeg}/mqc.c | 171 +- + .../{libopenjpeg20 => libopenjpeg}/mqc.h | 9 +- + .../{libopenjpeg20 => libopenjpeg}/mqc_inl.h | 90 +- + .../{libopenjpeg20 => libopenjpeg}/openjpeg.c | 108 +- + .../{libopenjpeg20 => libopenjpeg}/openjpeg.h | 130 +- + .../opj_clock.c | 0 + .../opj_clock.h | 0 + .../opj_codec.h | 8 + + .../opj_common.h | 6 + + .../opj_config.h | 6 +- + .../opj_config_private.h | 4 +- + .../opj_includes.h | 1 - + .../opj_intmath.h | 48 + + .../opj_inttypes.h | 0 + third_party/libopenjpeg/opj_malloc.cc | 42 + + third_party/libopenjpeg/opj_malloc.h | 112 + + .../opj_stdint.h | 0 + .../{libopenjpeg20 => libopenjpeg}/pi.c | 473 +- + .../{libopenjpeg20 => libopenjpeg}/pi.h | 25 +- + .../sparse_array.c | 4 +- + .../sparse_array.h | 0 + .../{libopenjpeg20 => libopenjpeg}/t1.c | 924 +- + .../{libopenjpeg20 => libopenjpeg}/t1.h | 7 +- + .../t1_generate_luts.c | 12 + + third_party/libopenjpeg/t1_ht_luts.h | 261 + + .../{libopenjpeg20 => libopenjpeg}/t1_luts.h | 0 + .../{libopenjpeg20 => libopenjpeg}/t2.c | 193 +- + .../{libopenjpeg20 => libopenjpeg}/t2.h | 2 + + .../{libopenjpeg20 => libopenjpeg}/tcd.c | 135 +- + .../{libopenjpeg20 => libopenjpeg}/tcd.h | 46 +- + .../{libopenjpeg20 => libopenjpeg}/tgt.c | 0 + .../{libopenjpeg20 => libopenjpeg}/tgt.h | 0 + .../{libopenjpeg20 => libopenjpeg}/thread.c | 0 + .../{libopenjpeg20 => libopenjpeg}/thread.h | 0 + .../{libopenjpeg20 => libopenjpeg}/tls_keys.h | 0 + .../libopenjpeg20/0034-opj_malloc.patch | 170 - + ...0036-opj_j2k_update_image_dimensions.patch | 49 - + .../libopenjpeg20/0037-tcd_init_tile.patch | 31 - + third_party/libopenjpeg20/opj_malloc.h | 200 - + third_party/libpng16/0000-build-config.patch | 31 - + third_party/libpng16/0002-static-png-gt.patch | 14 - + third_party/libpng16/LICENSE | 134 - + third_party/libpng16/README.pdfium | 19 - + third_party/libpng16/arm/arm_init.c | 136 - + third_party/libpng16/arm/filter_neon.S | 253 - + .../libpng16/arm/filter_neon_intrinsics.c | 402 - + .../libpng16/arm/palette_neon_intrinsics.c | 149 - + .../libpng16/intel/filter_sse2_intrinsics.c | 391 - + third_party/libpng16/intel/intel_init.c | 52 - + third_party/libpng16/png.c | 4608 ------ + third_party/libpng16/png.h | 3247 ---- + third_party/libpng16/pngconf.h | 623 - + third_party/libpng16/pngdebug.h | 153 - + third_party/libpng16/pngerror.c | 963 -- + third_party/libpng16/pngget.c | 1249 -- + third_party/libpng16/pnginfo.h | 267 - + third_party/libpng16/pnglibconf.h | 236 - + third_party/libpng16/pngmem.c | 287 - + third_party/libpng16/pngpread.c | 1096 -- + third_party/libpng16/pngprefix.h | 461 - + third_party/libpng16/pngpriv.h | 2152 --- + third_party/libpng16/pngread.c | 4225 ----- + third_party/libpng16/pngrio.c | 120 - + third_party/libpng16/pngrtran.c | 5044 ------ + third_party/libpng16/pngrutil.c | 4681 ------ + third_party/libpng16/pngset.c | 1802 --- + third_party/libpng16/pngstruct.h | 489 - + third_party/libpng16/pngtrans.c | 864 - + third_party/libpng16/pngwio.c | 168 - + third_party/libpng16/pngwrite.c | 2395 --- + third_party/libpng16/pngwtran.c | 575 - + third_party/libpng16/pngwutil.c | 2781 ---- + third_party/libtiff/0000-build-config.patch | 118 +- + .../0017-safe_skews_in_gtTileContig.patch | 123 +- + third_party/libtiff/0027-build-config.patch | 40 - + third_party/libtiff/0028-nstrips-OOM.patch | 39 +- + .../0031-safe_size_ingtStripContig.patch | 58 +- + .../libtiff/0033-avail-out-overflow.patch | 13 + + third_party/libtiff/README.pdfium | 5 +- + third_party/libtiff/t4.h | 464 +- + third_party/libtiff/tif_aux.c | 651 +- + third_party/libtiff/tif_close.c | 164 +- + third_party/libtiff/tif_codec.c | 142 +- + third_party/libtiff/tif_color.c | 421 +- + third_party/libtiff/tif_compress.c | 428 +- + third_party/libtiff/tif_dir.c | 3538 +++-- + third_party/libtiff/tif_dir.h | 459 +- + third_party/libtiff/tif_dirinfo.c | 2343 +-- + third_party/libtiff/tif_dirread.c | 13035 +++++++++------- + third_party/libtiff/tif_dirwrite.c | 5593 +++---- + third_party/libtiff/tif_dumpmode.c | 159 +- + third_party/libtiff/tif_error.c | 148 +- + third_party/libtiff/tif_extension.c | 62 +- + third_party/libtiff/tif_fax3.c | 2771 ++-- + third_party/libtiff/tif_fax3.h | 872 +- + third_party/libtiff/tif_fax3sm.c | 1 + + third_party/libtiff/tif_flush.c | 135 +- + third_party/libtiff/tif_getimage.c | 4966 +++--- + third_party/libtiff/tif_hash_set.c | 603 + + third_party/libtiff/tif_hash_set.h | 100 + + third_party/libtiff/tif_jpeg.c | 4244 ++--- + third_party/libtiff/tif_luv.c | 2769 ++-- + third_party/libtiff/tif_lzw.c | 2293 +-- + third_party/libtiff/tif_next.c | 301 +- + third_party/libtiff/tif_ojpeg.c | 2621 ---- + third_party/libtiff/tif_open.c | 1235 +- + third_party/libtiff/tif_packbits.c | 516 +- + third_party/libtiff/tif_pixarlog.c | 2589 +-- + third_party/libtiff/tif_predict.c | 1517 +- + third_party/libtiff/tif_predict.h | 55 +- + third_party/libtiff/tif_print.c | 1365 +- + third_party/libtiff/tif_read.c | 2551 ++- + third_party/libtiff/tif_strip.c | 489 +- + third_party/libtiff/tif_swab.c | 445 +- + third_party/libtiff/tif_thunder.c | 290 +- + third_party/libtiff/tif_tile.c | 415 +- + third_party/libtiff/tif_version.c | 29 +- + third_party/libtiff/tif_warning.c | 122 +- + third_party/libtiff/tif_write.c | 1390 +- + third_party/libtiff/tif_zip.c | 456 - + third_party/libtiff/tiff.h | 1369 +- + third_party/libtiff/tiffconf.h | 61 +- + third_party/libtiff/tiffio.h | 865 +- + third_party/libtiff/tiffiop.h | 685 +- + third_party/libtiff/tiffvers.h | 25 +- + third_party/libtiff/uvcode.h | 269 +- + third_party/ninja/README.pdfium | 17 + + third_party/skia_shared/SkFloatToDecimal.cpp | 31 +- + third_party/yasm/BUILD.gn | 524 - + third_party/yasm/CHROMIUM.diff | 102 - + third_party/yasm/README.pdfium | 126 - + third_party/yasm/run_yasm.py | 61 - + third_party/yasm/source/config/Makefile | 9 - + .../yasm/source/config/android/config.h | 173 - + .../source/config/android/libyasm-stdint.h | 9 - + third_party/yasm/source/config/ios/config.h | 173 - + .../yasm/source/config/ios/libyasm-stdint.h | 9 - + third_party/yasm/source/config/linux/config.h | 173 - + .../yasm/source/config/linux/libyasm-stdint.h | 9 - + third_party/yasm/source/config/mac/config.h | 173 - + .../yasm/source/config/mac/libyasm-stdint.h | 9 - + .../yasm/source/config/openbsd/config.h | 165 - + .../source/config/openbsd/libyasm-stdint.h | 9 - + third_party/yasm/source/config/win/config.h | 173 - + .../yasm/source/config/win/libyasm-stdint.h | 9 - + third_party/yasm/yasm_assemble.gni | 199 - + tools/android/md5sum/BUILD.gn | 10 + + tools/modules/graph_modules.sh | 2 +- + tools/msan/{blacklist.txt => ignorelist.txt} | 0 + tools/ubsan/{blacklist.txt => ignorelist.txt} | 4 +- + ..._blacklist.txt => security_ignorelist.txt} | 16 +- + ...vptr_blacklist.txt => vptr_ignorelist.txt} | 10 +- + xfa/BUILD.gn | 4 +- + xfa/DEPS | 4 +- + xfa/fde/BUILD.gn | 22 +- + xfa/fde/cfde_data.h | 2 +- + xfa/fde/cfde_texteditengine.cpp | 152 +- + xfa/fde/cfde_texteditengine.h | 96 +- + xfa/fde/cfde_texteditengine_unittest.cpp | 119 +- + xfa/fde/cfde_textout.cpp | 330 +- + xfa/fde/cfde_textout.h | 86 +- + xfa/fde/cfde_textout_unittest.cpp | 169 + + xfa/fde/cfde_wordbreak_data.cpp | 10 +- + xfa/fde/cfde_wordbreak_data.h | 2 +- + xfa/fgas/BUILD.gn | 61 - + xfa/fgas/crt/BUILD.gn | 43 + + xfa/fgas/crt/cfgas_decimal.cpp | 16 +- + xfa/fgas/crt/cfgas_decimal.h | 4 +- + xfa/fgas/crt/cfgas_decimal_unittest.cpp | 4 +- + xfa/fgas/crt/cfgas_stringformatter.cpp | 429 +- + xfa/fgas/crt/cfgas_stringformatter.h | 65 +- + .../crt/cfgas_stringformatter_unittest.cpp | 239 +- + xfa/fgas/crt/locale_iface.h | 64 +- + xfa/fgas/crt/locale_mgr_iface.h | 4 +- + xfa/fgas/font/BUILD.gn | 47 + + xfa/fgas/font/cfgas_defaultfontmanager.cpp | 34 +- + xfa/fgas/font/cfgas_defaultfontmanager.h | 12 +- + xfa/fgas/font/cfgas_fontmgr.cpp | 521 +- + xfa/fgas/font/cfgas_fontmgr.h | 97 +- + xfa/fgas/font/cfgas_gefont.cpp | 136 +- + xfa/fgas/font/cfgas_gefont.h | 45 +- + xfa/fgas/font/cfgas_gemodule.cpp | 39 + + xfa/fgas/font/cfgas_gemodule.h | 27 + + xfa/fgas/font/cfgas_pdffontmgr.cpp | 52 +- + xfa/fgas/font/cfgas_pdffontmgr.h | 18 +- + xfa/fgas/font/cfx_fontsourceenum_file.cpp | 97 - + xfa/fgas/font/cfx_fontsourceenum_file.h | 49 - + xfa/fgas/font/fgas_fontutils.cpp | 3663 +++-- + xfa/fgas/font/fgas_fontutils.h | 14 +- + xfa/fgas/font/fgas_fontutils_unittest.cpp | 60 + + xfa/fgas/graphics/BUILD.gn | 32 + + xfa/fgas/graphics/cfgas_gecolor.cpp | 22 + + xfa/fgas/graphics/cfgas_gecolor.h | 51 + + .../graphics/cfgas_gegraphics.cpp} | 256 +- + xfa/fgas/graphics/cfgas_gegraphics.h | 95 + + xfa/fgas/graphics/cfgas_gepath.cpp | 151 + + .../graphics/cfgas_gepath.h} | 27 +- + xfa/fgas/graphics/cfgas_gepattern.cpp | 14 + + xfa/fgas/graphics/cfgas_gepattern.h | 36 + + xfa/fgas/graphics/cfgas_geshading.cpp | 73 + + xfa/fgas/graphics/cfgas_geshading.h | 63 + + xfa/fgas/layout/BUILD.gn | 58 +- + .../layout/{cfx_break.cpp => cfgas_break.cpp} | 84 +- + .../layout/{cfx_break.h => cfgas_break.h} | 67 +- + xfa/fgas/layout/cfgas_breakline.cpp | 41 + + .../{cfx_breakline.h => cfgas_breakline.h} | 24 +- + xfa/fgas/layout/cfgas_breakpiece.cpp | 51 + + xfa/fgas/layout/cfgas_breakpiece.h | 93 + + .../layout/{cfx_char.cpp => cfgas_char.cpp} | 114 +- + xfa/fgas/layout/{cfx_char.h => cfgas_char.h} | 45 +- + ...inkuserdata.cpp => cfgas_linkuserdata.cpp} | 8 +- + xfa/fgas/layout/cfgas_linkuserdata.h | 26 + + .../{cfx_rtfbreak.cpp => cfgas_rtfbreak.cpp} | 461 +- + xfa/fgas/layout/cfgas_rtfbreak.h | 78 + + ...ittest.cpp => cfgas_rtfbreak_unittest.cpp} | 58 +- + ...{cfx_textpiece.cpp => cfgas_textpiece.cpp} | 8 +- + .../{cfx_textpiece.h => cfgas_textpiece.h} | 26 +- + ...extuserdata.cpp => cfgas_textuserdata.cpp} | 14 +- + xfa/fgas/layout/cfgas_textuserdata.h | 29 + + .../{cfx_txtbreak.cpp => cfgas_txtbreak.cpp} | 660 +- + xfa/fgas/layout/cfgas_txtbreak.h | 105 + + ...ittest.cpp => cfgas_txtbreak_unittest.cpp} | 27 +- + xfa/fgas/layout/cfx_breakline.cpp | 38 - + xfa/fgas/layout/cfx_breakpiece.cpp | 55 - + xfa/fgas/layout/cfx_breakpiece.h | 48 - + xfa/fgas/layout/cfx_linkuserdata.h | 28 - + xfa/fgas/layout/cfx_rtfbreak.h | 79 - + xfa/fgas/layout/cfx_textuserdata.h | 30 - + xfa/fgas/layout/cfx_txtbreak.h | 101 - + .../layout/{fx_arabic.cpp => fgas_arabic.cpp} | 77 +- + xfa/fgas/layout/fgas_arabic.h | 31 + + .../{fx_linebreak.cpp => fgas_linebreak.cpp} | 16 +- + .../{fx_linebreak.h => fgas_linebreak.h} | 11 +- + xfa/fgas/layout/fx_arabic.h | 26 - + xfa/fwl/BUILD.gn | 30 +- + xfa/fwl/DEPS | 4 + + xfa/fwl/cfwl_app.cpp | 24 +- + xfa/fwl/cfwl_app.h | 58 +- + xfa/fwl/cfwl_barcode.cpp | 100 +- + xfa/fwl/cfwl_barcode.h | 52 +- + xfa/fwl/cfwl_caret.cpp | 51 +- + xfa/fwl/cfwl_caret.h | 22 +- + xfa/fwl/cfwl_checkbox.cpp | 261 +- + xfa/fwl/cfwl_checkbox.h | 38 +- + xfa/fwl/cfwl_combobox.cpp | 371 +- + xfa/fwl/cfwl_combobox.h | 65 +- + xfa/fwl/cfwl_comboedit.cpp | 44 +- + xfa/fwl/cfwl_comboedit.h | 19 +- + xfa/fwl/cfwl_combolist.cpp | 106 +- + xfa/fwl/cfwl_combolist.h | 15 +- + xfa/fwl/cfwl_datetimeedit.cpp | 28 +- + xfa/fwl/cfwl_datetimeedit.h | 16 +- + xfa/fwl/cfwl_datetimepicker.cpp | 315 +- + xfa/fwl/cfwl_datetimepicker.h | 51 +- + xfa/fwl/cfwl_edit.cpp | 766 +- + xfa/fwl/cfwl_edit.h | 65 +- + xfa/fwl/cfwl_edit_embeddertest.cpp | 185 +- + xfa/fwl/cfwl_event.cpp | 2 +- + xfa/fwl/cfwl_event.h | 17 +- + xfa/fwl/cfwl_eventmouse.cpp | 13 +- + xfa/fwl/cfwl_eventmouse.h | 12 +- + xfa/fwl/cfwl_eventscroll.cpp | 12 +- + xfa/fwl/cfwl_eventscroll.h | 12 +- + xfa/fwl/cfwl_eventselectchanged.cpp | 23 +- + xfa/fwl/cfwl_eventselectchanged.h | 24 +- + xfa/fwl/cfwl_eventtarget.cpp | 31 - + xfa/fwl/cfwl_eventtarget.h | 34 - + xfa/fwl/cfwl_eventtextwillchange.cpp | 15 +- + xfa/fwl/cfwl_eventtextwillchange.h | 38 +- + xfa/fwl/cfwl_eventvalidate.cpp | 10 +- + xfa/fwl/cfwl_eventvalidate.h | 14 +- + xfa/fwl/cfwl_listbox.cpp | 608 +- + xfa/fwl/cfwl_listbox.h | 128 +- + xfa/fwl/cfwl_listitem.cpp | 11 - + xfa/fwl/cfwl_listitem.h | 32 - + xfa/fwl/cfwl_message.cpp | 8 +- + xfa/fwl/cfwl_message.h | 26 +- + xfa/fwl/cfwl_messagekey.cpp | 16 +- + xfa/fwl/cfwl_messagekey.h | 22 +- + xfa/fwl/cfwl_messagekillfocus.cpp | 14 +- + xfa/fwl/cfwl_messagekillfocus.h | 9 +- + xfa/fwl/cfwl_messagemouse.cpp | 17 +- + xfa/fwl/cfwl_messagemouse.h | 41 +- + xfa/fwl/cfwl_messagemousewheel.cpp | 20 +- + xfa/fwl/cfwl_messagemousewheel.h | 22 +- + xfa/fwl/cfwl_messagesetfocus.cpp | 12 +- + xfa/fwl/cfwl_messagesetfocus.h | 6 +- + xfa/fwl/cfwl_monthcalendar.cpp | 640 +- + xfa/fwl/cfwl_monthcalendar.h | 123 +- + xfa/fwl/cfwl_notedriver.cpp | 160 +- + xfa/fwl/cfwl_notedriver.h | 63 +- + xfa/fwl/cfwl_picturebox.cpp | 26 +- + xfa/fwl/cfwl_picturebox.h | 19 +- + xfa/fwl/cfwl_pushbutton.cpp | 172 +- + xfa/fwl/cfwl_pushbutton.h | 33 +- + xfa/fwl/cfwl_scrollbar.cpp | 351 +- + xfa/fwl/cfwl_scrollbar.h | 84 +- + xfa/fwl/cfwl_themebackground.cpp | 14 + + xfa/fwl/cfwl_themebackground.h | 26 +- + xfa/fwl/cfwl_themepart.cpp | 23 +- + xfa/fwl/cfwl_themepart.h | 157 +- + xfa/fwl/cfwl_themetext.cpp | 14 + + xfa/fwl/cfwl_themetext.h | 23 +- + xfa/fwl/cfwl_widget.cpp | 207 +- + xfa/fwl/cfwl_widget.h | 125 +- + xfa/fwl/cfwl_widgetmgr.cpp | 293 +- + xfa/fwl/cfwl_widgetmgr.h | 82 +- + xfa/fwl/cfwl_widgetproperties.cpp | 11 - + xfa/fwl/cfwl_widgetproperties.h | 31 - + xfa/fwl/fwl_widgetdef.h | 32 +- + xfa/fwl/fwl_widgethit.h | 2 +- + xfa/fwl/ifwl_themeprovider.cpp | 89 + + xfa/fwl/ifwl_themeprovider.h | 37 +- + xfa/fwl/ifwl_widgetdelegate.h | 10 +- + xfa/fwl/theme/cfwl_barcodetp.cpp | 16 +- + xfa/fwl/theme/cfwl_barcodetp.h | 8 +- + xfa/fwl/theme/cfwl_carettp.cpp | 28 +- + xfa/fwl/theme/cfwl_carettp.h | 12 +- + xfa/fwl/theme/cfwl_checkboxtp.cpp | 260 +- + xfa/fwl/theme/cfwl_checkboxtp.h | 29 +- + xfa/fwl/theme/cfwl_comboboxtp.cpp | 76 +- + xfa/fwl/theme/cfwl_comboboxtp.h | 11 +- + xfa/fwl/theme/cfwl_datetimepickertp.cpp | 48 +- + xfa/fwl/theme/cfwl_datetimepickertp.h | 10 +- + xfa/fwl/theme/cfwl_edittp.cpp | 83 +- + xfa/fwl/theme/cfwl_edittp.h | 10 +- + xfa/fwl/theme/cfwl_listboxtp.cpp | 56 +- + xfa/fwl/theme/cfwl_listboxtp.h | 14 +- + xfa/fwl/theme/cfwl_monthcalendartp.cpp | 236 +- + xfa/fwl/theme/cfwl_monthcalendartp.h | 10 +- + xfa/fwl/theme/cfwl_pictureboxtp.cpp | 12 +- + xfa/fwl/theme/cfwl_pictureboxtp.h | 10 +- + xfa/fwl/theme/cfwl_pushbuttontp.cpp | 83 +- + xfa/fwl/theme/cfwl_pushbuttontp.h | 19 +- + xfa/fwl/theme/cfwl_scrollbartp.cpp | 238 +- + xfa/fwl/theme/cfwl_scrollbartp.h | 23 +- + xfa/fwl/theme/cfwl_utils.h | 31 +- + xfa/fwl/theme/cfwl_widgettp.cpp | 188 +- + xfa/fwl/theme/cfwl_widgettp.h | 77 +- + xfa/fxfa/BUILD.gn | 26 +- + xfa/fxfa/DEPS | 3 +- + xfa/fxfa/cxfa_eventparam.cpp | 7 +- + xfa/fxfa/cxfa_eventparam.h | 11 +- + xfa/fxfa/cxfa_ffapp.cpp | 76 +- + xfa/fxfa/cxfa_ffapp.h | 171 +- + xfa/fxfa/cxfa_ffarc.cpp | 6 +- + xfa/fxfa/cxfa_ffarc.h | 9 +- + xfa/fxfa/cxfa_ffbarcode.cpp | 302 +- + xfa/fxfa/cxfa_ffbarcode.h | 96 +- + xfa/fxfa/cxfa_ffbarcode_unittest.cpp | 24 +- + xfa/fxfa/cxfa_ffcheckbutton.cpp | 160 +- + xfa/fxfa/cxfa_ffcheckbutton.h | 27 +- + xfa/fxfa/cxfa_ffcombobox.cpp | 90 +- + xfa/fxfa/cxfa_ffcombobox.h | 28 +- + xfa/fxfa/cxfa_ffdatetimeedit.cpp | 82 +- + xfa/fxfa/cxfa_ffdatetimeedit.h | 16 +- + xfa/fxfa/cxfa_ffdoc.cpp | 275 +- + xfa/fxfa/cxfa_ffdoc.h | 169 +- + xfa/fxfa/cxfa_ffdocview.cpp | 262 +- + xfa/fxfa/cxfa_ffdocview.h | 103 +- + xfa/fxfa/cxfa_ffdropdown.cpp | 2 +- + xfa/fxfa/cxfa_ffdropdown.h | 3 +- + xfa/fxfa/cxfa_ffexclgroup.cpp | 6 +- + xfa/fxfa/cxfa_ffexclgroup.h | 9 +- + xfa/fxfa/cxfa_fffield.cpp | 418 +- + xfa/fxfa/cxfa_fffield.h | 84 +- + xfa/fxfa/cxfa_ffimage.cpp | 35 +- + xfa/fxfa/cxfa_ffimage.h | 16 +- + xfa/fxfa/cxfa_ffimageedit.cpp | 71 +- + xfa/fxfa/cxfa_ffimageedit.h | 32 +- + xfa/fxfa/cxfa_ffline.cpp | 29 +- + xfa/fxfa/cxfa_ffline.h | 8 +- + xfa/fxfa/cxfa_fflistbox.cpp | 73 +- + xfa/fxfa/cxfa_fflistbox.h | 22 +- + xfa/fxfa/cxfa_ffnotify.cpp | 200 +- + xfa/fxfa/cxfa_ffnotify.h | 48 +- + xfa/fxfa/cxfa_ffnumericedit.cpp | 36 +- + xfa/fxfa/cxfa_ffnumericedit.h | 6 +- + xfa/fxfa/cxfa_ffpageview.cpp | 496 +- + xfa/fxfa/cxfa_ffpageview.h | 102 +- + xfa/fxfa/cxfa_ffpasswordedit.cpp | 28 +- + xfa/fxfa/cxfa_ffpasswordedit.h | 14 +- + xfa/fxfa/cxfa_ffpushbutton.cpp | 90 +- + xfa/fxfa/cxfa_ffpushbutton.h | 30 +- + xfa/fxfa/cxfa_ffrectangle.cpp | 6 +- + xfa/fxfa/cxfa_ffrectangle.h | 9 +- + xfa/fxfa/cxfa_ffsignature.cpp | 58 +- + xfa/fxfa/cxfa_ffsignature.h | 50 +- + xfa/fxfa/cxfa_fftext.cpp | 78 +- + xfa/fxfa/cxfa_fftext.h | 27 +- + xfa/fxfa/cxfa_fftextedit.cpp | 205 +- + xfa/fxfa/cxfa_fftextedit.h | 47 +- + xfa/fxfa/cxfa_ffwidget.cpp | 245 +- + xfa/fxfa/cxfa_ffwidget.h | 154 +- + xfa/fxfa/cxfa_ffwidget_type.h | 2 +- + xfa/fxfa/cxfa_ffwidgethandler.cpp | 151 +- + xfa/fxfa/cxfa_ffwidgethandler.h | 59 +- + xfa/fxfa/cxfa_fontmgr.cpp | 48 +- + xfa/fxfa/cxfa_fontmgr.h | 17 +- + xfa/fxfa/cxfa_fwladapterwidgetmgr.cpp | 13 +- + xfa/fxfa/cxfa_fwladapterwidgetmgr.h | 18 +- + xfa/fxfa/cxfa_fwltheme.cpp | 165 +- + xfa/fxfa/cxfa_fwltheme.h | 57 +- + xfa/fxfa/cxfa_imagerenderer.cpp | 47 +- + xfa/fxfa/cxfa_imagerenderer.h | 10 +- + xfa/fxfa/cxfa_loadercontext.cpp | 13 - + xfa/fxfa/cxfa_loadercontext.h | 45 - + xfa/fxfa/cxfa_pieceline.cpp | 13 - + xfa/fxfa/cxfa_pieceline.h | 24 - + xfa/fxfa/cxfa_readynodeiterator.cpp | 6 +- + xfa/fxfa/cxfa_readynodeiterator.h | 7 +- + xfa/fxfa/cxfa_rendercontext.cpp | 33 - + xfa/fxfa/cxfa_rendercontext.h | 36 - + xfa/fxfa/cxfa_textlayout.cpp | 557 +- + xfa/fxfa/cxfa_textlayout.h | 124 +- + xfa/fxfa/cxfa_textparsecontext.cpp | 16 - + xfa/fxfa/cxfa_textparsecontext.h | 38 - + xfa/fxfa/cxfa_textparser.cpp | 186 +- + xfa/fxfa/cxfa_textparser.h | 112 +- + xfa/fxfa/cxfa_textparser_unittest.cpp | 58 +- + xfa/fxfa/cxfa_textpiece.cpp | 13 - + xfa/fxfa/cxfa_textpiece.h | 29 - + xfa/fxfa/cxfa_textprovider.cpp | 68 +- + xfa/fxfa/cxfa_textprovider.h | 44 +- + xfa/fxfa/cxfa_texttabstopscontext.cpp | 9 +- + xfa/fxfa/cxfa_texttabstopscontext.h | 10 +- + xfa/fxfa/fm2js/DEPS | 6 - + xfa/fxfa/fm2js/cxfa_fmexpression.cpp | 448 - + xfa/fxfa/fm2js/cxfa_fmexpression.h | 187 - + xfa/fxfa/fm2js/cxfa_fmexpression_unittest.cpp | 68 - + xfa/fxfa/fm2js/cxfa_fmlexer_unittest.cpp | 292 - + xfa/fxfa/fm2js/cxfa_fmparser.cpp | 1138 -- + xfa/fxfa/fm2js/cxfa_fmparser.h | 67 - + xfa/fxfa/fm2js/cxfa_fmsimpleexpression.cpp | 692 - + xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h | 325 - + .../cxfa_fmsimpleexpression_unittest.cpp | 87 - + xfa/fxfa/{fm2js => formcalc}/BUILD.gn | 20 +- + xfa/fxfa/formcalc/DEPS | 3 + + xfa/fxfa/formcalc/cxfa_fmexpression.cpp | 1192 ++ + xfa/fxfa/formcalc/cxfa_fmexpression.h | 608 + + .../formcalc/cxfa_fmexpression_unittest.cpp | 170 + + xfa/fxfa/{fm2js => formcalc}/cxfa_fmlexer.cpp | 134 +- + xfa/fxfa/{fm2js => formcalc}/cxfa_fmlexer.h | 50 +- + xfa/fxfa/formcalc/cxfa_fmlexer_unittest.cpp | 317 + + xfa/fxfa/formcalc/cxfa_fmparser.cpp | 1103 ++ + xfa/fxfa/formcalc/cxfa_fmparser.h | 74 + + .../cxfa_fmparser_unittest.cpp | 306 +- + .../cxfa_fmtojavascriptdepth.cpp | 4 +- + .../cxfa_fmtojavascriptdepth.h | 8 +- + xfa/fxfa/fxfa.h | 228 +- + xfa/fxfa/fxfa_basic.h | 46 +- + xfa/fxfa/fxfa_basic_unittest.cpp | 6 +- + xfa/fxfa/layout/BUILD.gn | 15 +- + xfa/fxfa/layout/DEPS | 4 + + xfa/fxfa/layout/cxfa_contentlayoutitem.cpp | 30 +- + xfa/fxfa/layout/cxfa_contentlayoutitem.h | 47 +- + .../layout/cxfa_contentlayoutprocessor.cpp | 672 +- + xfa/fxfa/layout/cxfa_contentlayoutprocessor.h | 150 +- + xfa/fxfa/layout/cxfa_layoutitem.cpp | 44 +- + xfa/fxfa/layout/cxfa_layoutitem.h | 31 +- + .../layout/cxfa_layoutitem_embeddertest.cpp | 49 +- + xfa/fxfa/layout/cxfa_layoutprocessor.cpp | 56 +- + xfa/fxfa/layout/cxfa_layoutprocessor.h | 41 +- + .../layout/cxfa_traversestrategy_layoutitem.h | 5 +- + xfa/fxfa/layout/cxfa_viewlayoutitem.cpp | 24 +- + xfa/fxfa/layout/cxfa_viewlayoutitem.h | 28 +- + xfa/fxfa/layout/cxfa_viewlayoutprocessor.cpp | 442 +- + xfa/fxfa/layout/cxfa_viewlayoutprocessor.h | 96 +- + xfa/fxfa/parser/BUILD.gn | 36 +- + xfa/fxfa/parser/DEPS | 1 - + xfa/fxfa/parser/attribute_values.inc | 2 +- + xfa/fxfa/parser/attributes.inc | 2 +- + xfa/fxfa/parser/cscript_datawindow.cpp | 15 +- + xfa/fxfa/parser/cscript_datawindow.h | 2 +- + xfa/fxfa/parser/cscript_eventpseudomodel.cpp | 12 +- + xfa/fxfa/parser/cscript_eventpseudomodel.h | 2 +- + xfa/fxfa/parser/cscript_hostpseudomodel.cpp | 12 +- + xfa/fxfa/parser/cscript_hostpseudomodel.h | 2 +- + xfa/fxfa/parser/cscript_layoutpseudomodel.cpp | 12 +- + xfa/fxfa/parser/cscript_layoutpseudomodel.h | 2 +- + xfa/fxfa/parser/cscript_logpseudomodel.cpp | 14 +- + xfa/fxfa/parser/cscript_logpseudomodel.h | 2 +- + .../parser/cscript_signaturepseudomodel.cpp | 13 +- + .../parser/cscript_signaturepseudomodel.h | 2 +- + xfa/fxfa/parser/cxfa_accessiblecontent.cpp | 10 +- + xfa/fxfa/parser/cxfa_accessiblecontent.h | 7 +- + xfa/fxfa/parser/cxfa_acrobat.cpp | 20 +- + xfa/fxfa/parser/cxfa_acrobat.h | 7 +- + xfa/fxfa/parser/cxfa_acrobat7.cpp | 12 +- + xfa/fxfa/parser/cxfa_acrobat7.h | 7 +- + xfa/fxfa/parser/cxfa_adbe_jsconsole.cpp | 10 +- + xfa/fxfa/parser/cxfa_adbe_jsconsole.h | 7 +- + xfa/fxfa/parser/cxfa_adbe_jsdebugger.cpp | 10 +- + xfa/fxfa/parser/cxfa_adbe_jsdebugger.h | 7 +- + xfa/fxfa/parser/cxfa_addsilentprint.cpp | 10 +- + xfa/fxfa/parser/cxfa_addsilentprint.h | 7 +- + xfa/fxfa/parser/cxfa_addviewerpreferences.cpp | 10 +- + xfa/fxfa/parser/cxfa_addviewerpreferences.h | 7 +- + xfa/fxfa/parser/cxfa_adjustdata.cpp | 10 +- + xfa/fxfa/parser/cxfa_adjustdata.h | 7 +- + xfa/fxfa/parser/cxfa_adobeextensionlevel.cpp | 10 +- + xfa/fxfa/parser/cxfa_adobeextensionlevel.h | 7 +- + xfa/fxfa/parser/cxfa_agent.cpp | 10 +- + xfa/fxfa/parser/cxfa_agent.h | 7 +- + xfa/fxfa/parser/cxfa_alwaysembed.cpp | 10 +- + xfa/fxfa/parser/cxfa_alwaysembed.h | 7 +- + xfa/fxfa/parser/cxfa_amd.cpp | 10 +- + xfa/fxfa/parser/cxfa_amd.h | 7 +- + xfa/fxfa/parser/cxfa_appearancefilter.cpp | 10 +- + xfa/fxfa/parser/cxfa_appearancefilter.h | 7 +- + xfa/fxfa/parser/cxfa_arc.cpp | 21 +- + xfa/fxfa/parser/cxfa_arc.h | 9 +- + xfa/fxfa/parser/cxfa_area.cpp | 28 +- + xfa/fxfa/parser/cxfa_area.h | 7 +- + xfa/fxfa/parser/cxfa_arraynodelist.cpp | 24 +- + xfa/fxfa/parser/cxfa_arraynodelist.h | 16 +- + xfa/fxfa/parser/cxfa_assist.cpp | 14 +- + xfa/fxfa/parser/cxfa_assist.h | 7 +- + xfa/fxfa/parser/cxfa_attachnodelist.cpp | 16 +- + xfa/fxfa/parser/cxfa_attachnodelist.h | 14 +- + xfa/fxfa/parser/cxfa_attributes.cpp | 10 +- + xfa/fxfa/parser/cxfa_attributes.h | 7 +- + xfa/fxfa/parser/cxfa_autosave.cpp | 10 +- + xfa/fxfa/parser/cxfa_autosave.h | 7 +- + xfa/fxfa/parser/cxfa_barcode.cpp | 120 +- + xfa/fxfa/parser/cxfa_barcode.h | 35 +- + xfa/fxfa/parser/cxfa_base.cpp | 10 +- + xfa/fxfa/parser/cxfa_base.h | 7 +- + xfa/fxfa/parser/cxfa_batchoutput.cpp | 10 +- + xfa/fxfa/parser/cxfa_batchoutput.h | 7 +- + xfa/fxfa/parser/cxfa_behavioroverride.cpp | 10 +- + xfa/fxfa/parser/cxfa_behavioroverride.h | 7 +- + xfa/fxfa/parser/cxfa_bind.cpp | 19 +- + xfa/fxfa/parser/cxfa_bind.h | 9 +- + xfa/fxfa/parser/cxfa_binditems.cpp | 10 +- + xfa/fxfa/parser/cxfa_binditems.h | 7 +- + xfa/fxfa/parser/cxfa_bookend.cpp | 10 +- + xfa/fxfa/parser/cxfa_bookend.h | 7 +- + xfa/fxfa/parser/cxfa_boolean.cpp | 14 +- + xfa/fxfa/parser/cxfa_boolean.h | 7 +- + xfa/fxfa/parser/cxfa_border.cpp | 16 +- + xfa/fxfa/parser/cxfa_border.h | 7 +- + xfa/fxfa/parser/cxfa_box.cpp | 118 +- + xfa/fxfa/parser/cxfa_box.h | 29 +- + xfa/fxfa/parser/cxfa_break.cpp | 12 +- + xfa/fxfa/parser/cxfa_break.h | 7 +- + xfa/fxfa/parser/cxfa_breakafter.cpp | 12 +- + xfa/fxfa/parser/cxfa_breakafter.h | 7 +- + xfa/fxfa/parser/cxfa_breakbefore.cpp | 12 +- + xfa/fxfa/parser/cxfa_breakbefore.h | 7 +- + xfa/fxfa/parser/cxfa_button.cpp | 19 +- + xfa/fxfa/parser/cxfa_button.h | 9 +- + xfa/fxfa/parser/cxfa_cache.cpp | 14 +- + xfa/fxfa/parser/cxfa_cache.h | 7 +- + xfa/fxfa/parser/cxfa_calculate.cpp | 22 +- + xfa/fxfa/parser/cxfa_calculate.h | 9 +- + xfa/fxfa/parser/cxfa_calendarsymbols.cpp | 18 +- + xfa/fxfa/parser/cxfa_calendarsymbols.h | 7 +- + xfa/fxfa/parser/cxfa_caption.cpp | 16 +- + xfa/fxfa/parser/cxfa_caption.h | 7 +- + xfa/fxfa/parser/cxfa_certificate.cpp | 10 +- + xfa/fxfa/parser/cxfa_certificate.h | 7 +- + xfa/fxfa/parser/cxfa_certificates.cpp | 16 +- + xfa/fxfa/parser/cxfa_certificates.h | 7 +- + xfa/fxfa/parser/cxfa_change.cpp | 10 +- + xfa/fxfa/parser/cxfa_change.h | 7 +- + xfa/fxfa/parser/cxfa_checkbutton.cpp | 23 +- + xfa/fxfa/parser/cxfa_checkbutton.h | 9 +- + xfa/fxfa/parser/cxfa_choicelist.cpp | 16 +- + xfa/fxfa/parser/cxfa_choicelist.h | 7 +- + xfa/fxfa/parser/cxfa_color.cpp | 83 +- + xfa/fxfa/parser/cxfa_color.h | 13 +- + xfa/fxfa/parser/cxfa_comb.cpp | 12 +- + xfa/fxfa/parser/cxfa_comb.h | 7 +- + xfa/fxfa/parser/cxfa_command.cpp | 18 +- + xfa/fxfa/parser/cxfa_command.h | 7 +- + xfa/fxfa/parser/cxfa_common.cpp | 26 +- + xfa/fxfa/parser/cxfa_common.h | 7 +- + xfa/fxfa/parser/cxfa_compress.cpp | 10 +- + xfa/fxfa/parser/cxfa_compress.h | 7 +- + xfa/fxfa/parser/cxfa_compression.cpp | 18 +- + xfa/fxfa/parser/cxfa_compression.h | 7 +- + .../parser/cxfa_compresslogicalstructure.cpp | 10 +- + .../parser/cxfa_compresslogicalstructure.h | 7 +- + xfa/fxfa/parser/cxfa_compressobjectstream.cpp | 10 +- + xfa/fxfa/parser/cxfa_compressobjectstream.h | 7 +- + xfa/fxfa/parser/cxfa_config.cpp | 16 +- + xfa/fxfa/parser/cxfa_config.h | 7 +- + xfa/fxfa/parser/cxfa_conformance.cpp | 10 +- + xfa/fxfa/parser/cxfa_conformance.h | 7 +- + xfa/fxfa/parser/cxfa_connect.cpp | 20 +- + xfa/fxfa/parser/cxfa_connect.h | 7 +- + xfa/fxfa/parser/cxfa_connectionset.cpp | 10 +- + xfa/fxfa/parser/cxfa_connectionset.h | 7 +- + xfa/fxfa/parser/cxfa_connectstring.cpp | 10 +- + xfa/fxfa/parser/cxfa_connectstring.h | 7 +- + xfa/fxfa/parser/cxfa_contentarea.cpp | 14 +- + xfa/fxfa/parser/cxfa_contentarea.h | 7 +- + xfa/fxfa/parser/cxfa_contentcopy.cpp | 10 +- + xfa/fxfa/parser/cxfa_contentcopy.h | 7 +- + xfa/fxfa/parser/cxfa_copies.cpp | 10 +- + xfa/fxfa/parser/cxfa_copies.h | 7 +- + xfa/fxfa/parser/cxfa_corner.cpp | 14 +- + xfa/fxfa/parser/cxfa_corner.h | 7 +- + xfa/fxfa/parser/cxfa_creator.cpp | 10 +- + xfa/fxfa/parser/cxfa_creator.h | 7 +- + xfa/fxfa/parser/cxfa_currencysymbol.cpp | 10 +- + xfa/fxfa/parser/cxfa_currencysymbol.h | 7 +- + xfa/fxfa/parser/cxfa_currencysymbols.cpp | 12 +- + xfa/fxfa/parser/cxfa_currencysymbols.h | 7 +- + xfa/fxfa/parser/cxfa_currentpage.cpp | 10 +- + xfa/fxfa/parser/cxfa_currentpage.h | 7 +- + xfa/fxfa/parser/cxfa_data.cpp | 20 +- + xfa/fxfa/parser/cxfa_data.h | 7 +- + xfa/fxfa/parser/cxfa_dataexporter.cpp | 12 +- + xfa/fxfa/parser/cxfa_dataexporter.h | 4 +- + xfa/fxfa/parser/cxfa_datagroup.cpp | 10 +- + xfa/fxfa/parser/cxfa_datagroup.h | 7 +- + xfa/fxfa/parser/cxfa_datamodel.cpp | 10 +- + xfa/fxfa/parser/cxfa_datamodel.h | 7 +- + xfa/fxfa/parser/cxfa_datavalue.cpp | 10 +- + xfa/fxfa/parser/cxfa_datavalue.h | 7 +- + xfa/fxfa/parser/cxfa_date.cpp | 10 +- + xfa/fxfa/parser/cxfa_date.h | 7 +- + xfa/fxfa/parser/cxfa_datepattern.cpp | 10 +- + xfa/fxfa/parser/cxfa_datepattern.h | 7 +- + xfa/fxfa/parser/cxfa_datepatterns.cpp | 12 +- + xfa/fxfa/parser/cxfa_datepatterns.h | 7 +- + xfa/fxfa/parser/cxfa_datetime.cpp | 10 +- + xfa/fxfa/parser/cxfa_datetime.h | 7 +- + xfa/fxfa/parser/cxfa_datetimeedit.cpp | 18 +- + xfa/fxfa/parser/cxfa_datetimeedit.h | 7 +- + xfa/fxfa/parser/cxfa_datetimesymbols.cpp | 10 +- + xfa/fxfa/parser/cxfa_datetimesymbols.h | 7 +- + xfa/fxfa/parser/cxfa_day.cpp | 10 +- + xfa/fxfa/parser/cxfa_day.h | 7 +- + xfa/fxfa/parser/cxfa_daynames.cpp | 12 +- + xfa/fxfa/parser/cxfa_daynames.h | 7 +- + xfa/fxfa/parser/cxfa_debug.cpp | 12 +- + xfa/fxfa/parser/cxfa_debug.h | 7 +- + xfa/fxfa/parser/cxfa_decimal.cpp | 10 +- + xfa/fxfa/parser/cxfa_decimal.h | 7 +- + xfa/fxfa/parser/cxfa_defaulttypeface.cpp | 10 +- + xfa/fxfa/parser/cxfa_defaulttypeface.h | 7 +- + xfa/fxfa/parser/cxfa_defaultui.cpp | 12 +- + xfa/fxfa/parser/cxfa_defaultui.h | 7 +- + xfa/fxfa/parser/cxfa_delete.cpp | 10 +- + xfa/fxfa/parser/cxfa_delete.h | 7 +- + xfa/fxfa/parser/cxfa_delta.cpp | 10 +- + xfa/fxfa/parser/cxfa_delta.h | 7 +- + xfa/fxfa/parser/cxfa_deltas.cpp | 15 - + xfa/fxfa/parser/cxfa_deltas.h | 18 - + xfa/fxfa/parser/cxfa_desc.cpp | 20 +- + xfa/fxfa/parser/cxfa_desc.h | 7 +- + xfa/fxfa/parser/cxfa_destination.cpp | 10 +- + xfa/fxfa/parser/cxfa_destination.h | 7 +- + xfa/fxfa/parser/cxfa_digestmethod.cpp | 10 +- + xfa/fxfa/parser/cxfa_digestmethod.h | 7 +- + xfa/fxfa/parser/cxfa_digestmethods.cpp | 10 +- + xfa/fxfa/parser/cxfa_digestmethods.h | 7 +- + xfa/fxfa/parser/cxfa_document.cpp | 464 +- + xfa/fxfa/parser/cxfa_document.h | 129 +- + ...t_parser.cpp => cxfa_document_builder.cpp} | 392 +- + ...ument_parser.h => cxfa_document_builder.h} | 48 +- + ...=> cxfa_document_builder_embeddertest.cpp} | 10 +- + .../parser/cxfa_document_builder_unittest.cpp | 146 + + .../parser/cxfa_document_parser_unittest.cpp | 124 - + xfa/fxfa/parser/cxfa_document_unittest.cpp | 174 + + xfa/fxfa/parser/cxfa_documentassembly.cpp | 10 +- + xfa/fxfa/parser/cxfa_documentassembly.h | 7 +- + xfa/fxfa/parser/cxfa_draw.cpp | 22 +- + xfa/fxfa/parser/cxfa_draw.h | 7 +- + xfa/fxfa/parser/cxfa_driver.cpp | 14 +- + xfa/fxfa/parser/cxfa_driver.h | 7 +- + xfa/fxfa/parser/cxfa_dsigdata.cpp | 10 +- + xfa/fxfa/parser/cxfa_dsigdata.h | 7 +- + xfa/fxfa/parser/cxfa_duplexoption.cpp | 10 +- + xfa/fxfa/parser/cxfa_duplexoption.h | 7 +- + xfa/fxfa/parser/cxfa_dynamicrender.cpp | 10 +- + xfa/fxfa/parser/cxfa_dynamicrender.h | 7 +- + xfa/fxfa/parser/cxfa_edge.cpp | 14 +- + xfa/fxfa/parser/cxfa_edge.h | 7 +- + xfa/fxfa/parser/cxfa_effectiveinputpolicy.cpp | 10 +- + xfa/fxfa/parser/cxfa_effectiveinputpolicy.h | 7 +- + .../parser/cxfa_effectiveoutputpolicy.cpp | 10 +- + xfa/fxfa/parser/cxfa_effectiveoutputpolicy.h | 7 +- + xfa/fxfa/parser/cxfa_embed.cpp | 10 +- + xfa/fxfa/parser/cxfa_embed.h | 7 +- + xfa/fxfa/parser/cxfa_encoding.cpp | 10 +- + xfa/fxfa/parser/cxfa_encoding.h | 7 +- + xfa/fxfa/parser/cxfa_encodings.cpp | 10 +- + xfa/fxfa/parser/cxfa_encodings.h | 7 +- + xfa/fxfa/parser/cxfa_encrypt.cpp | 26 +- + xfa/fxfa/parser/cxfa_encrypt.h | 7 +- + xfa/fxfa/parser/cxfa_encryption.cpp | 16 +- + xfa/fxfa/parser/cxfa_encryption.h | 7 +- + xfa/fxfa/parser/cxfa_encryptionlevel.cpp | 10 +- + xfa/fxfa/parser/cxfa_encryptionlevel.h | 7 +- + xfa/fxfa/parser/cxfa_encryptionmethod.cpp | 10 +- + xfa/fxfa/parser/cxfa_encryptionmethod.h | 7 +- + xfa/fxfa/parser/cxfa_encryptionmethods.cpp | 10 +- + xfa/fxfa/parser/cxfa_encryptionmethods.h | 7 +- + xfa/fxfa/parser/cxfa_enforce.cpp | 10 +- + xfa/fxfa/parser/cxfa_enforce.h | 7 +- + xfa/fxfa/parser/cxfa_equate.cpp | 10 +- + xfa/fxfa/parser/cxfa_equate.h | 7 +- + xfa/fxfa/parser/cxfa_equaterange.cpp | 10 +- + xfa/fxfa/parser/cxfa_equaterange.h | 7 +- + xfa/fxfa/parser/cxfa_era.cpp | 10 +- + xfa/fxfa/parser/cxfa_era.h | 7 +- + xfa/fxfa/parser/cxfa_eranames.cpp | 12 +- + xfa/fxfa/parser/cxfa_eranames.h | 7 +- + xfa/fxfa/parser/cxfa_event.cpp | 20 +- + xfa/fxfa/parser/cxfa_event.h | 7 +- + xfa/fxfa/parser/cxfa_exclgroup.cpp | 22 +- + xfa/fxfa/parser/cxfa_exclgroup.h | 7 +- + xfa/fxfa/parser/cxfa_exclude.cpp | 10 +- + xfa/fxfa/parser/cxfa_exclude.h | 7 +- + xfa/fxfa/parser/cxfa_excludens.cpp | 10 +- + xfa/fxfa/parser/cxfa_excludens.h | 7 +- + xfa/fxfa/parser/cxfa_exdata.cpp | 19 +- + xfa/fxfa/parser/cxfa_exdata.h | 9 +- + xfa/fxfa/parser/cxfa_execute.cpp | 10 +- + xfa/fxfa/parser/cxfa_execute.h | 7 +- + xfa/fxfa/parser/cxfa_exobject.cpp | 12 +- + xfa/fxfa/parser/cxfa_exobject.h | 7 +- + xfa/fxfa/parser/cxfa_extras.cpp | 12 +- + xfa/fxfa/parser/cxfa_extras.h | 7 +- + xfa/fxfa/parser/cxfa_field.cpp | 28 +- + xfa/fxfa/parser/cxfa_field.h | 7 +- + xfa/fxfa/parser/cxfa_fill.cpp | 78 +- + xfa/fxfa/parser/cxfa_fill.h | 39 +- + xfa/fxfa/parser/cxfa_filter.cpp | 20 +- + xfa/fxfa/parser/cxfa_filter.h | 7 +- + xfa/fxfa/parser/cxfa_fliplabel.cpp | 10 +- + xfa/fxfa/parser/cxfa_fliplabel.h | 7 +- + xfa/fxfa/parser/cxfa_float.cpp | 10 +- + xfa/fxfa/parser/cxfa_float.h | 7 +- + xfa/fxfa/parser/cxfa_font.cpp | 34 +- + xfa/fxfa/parser/cxfa_font.h | 11 +- + xfa/fxfa/parser/cxfa_fontinfo.cpp | 16 +- + xfa/fxfa/parser/cxfa_fontinfo.h | 7 +- + xfa/fxfa/parser/cxfa_form.cpp | 10 +- + xfa/fxfa/parser/cxfa_form.h | 7 +- + xfa/fxfa/parser/cxfa_format.cpp | 14 +- + xfa/fxfa/parser/cxfa_format.h | 7 +- + xfa/fxfa/parser/cxfa_formfieldfilling.cpp | 10 +- + xfa/fxfa/parser/cxfa_formfieldfilling.h | 7 +- + xfa/fxfa/parser/cxfa_groupparent.cpp | 10 +- + xfa/fxfa/parser/cxfa_groupparent.h | 7 +- + xfa/fxfa/parser/cxfa_handler.cpp | 10 +- + xfa/fxfa/parser/cxfa_handler.h | 7 +- + xfa/fxfa/parser/cxfa_hyphenation.cpp | 10 +- + xfa/fxfa/parser/cxfa_hyphenation.h | 7 +- + xfa/fxfa/parser/cxfa_ifempty.cpp | 10 +- + xfa/fxfa/parser/cxfa_ifempty.h | 7 +- + xfa/fxfa/parser/cxfa_image.cpp | 21 +- + xfa/fxfa/parser/cxfa_image.h | 9 +- + xfa/fxfa/parser/cxfa_imageedit.cpp | 16 +- + xfa/fxfa/parser/cxfa_imageedit.h | 7 +- + xfa/fxfa/parser/cxfa_includexdpcontent.cpp | 10 +- + xfa/fxfa/parser/cxfa_includexdpcontent.h | 7 +- + xfa/fxfa/parser/cxfa_incrementalload.cpp | 10 +- + xfa/fxfa/parser/cxfa_incrementalload.h | 7 +- + xfa/fxfa/parser/cxfa_incrementalmerge.cpp | 10 +- + xfa/fxfa/parser/cxfa_incrementalmerge.h | 7 +- + xfa/fxfa/parser/cxfa_insert.cpp | 10 +- + xfa/fxfa/parser/cxfa_insert.h | 7 +- + xfa/fxfa/parser/cxfa_instancemanager.cpp | 12 +- + xfa/fxfa/parser/cxfa_instancemanager.h | 7 +- + xfa/fxfa/parser/cxfa_integer.cpp | 12 +- + xfa/fxfa/parser/cxfa_integer.h | 7 +- + xfa/fxfa/parser/cxfa_interactive.cpp | 12 +- + xfa/fxfa/parser/cxfa_interactive.h | 7 +- + xfa/fxfa/parser/cxfa_issuers.cpp | 10 +- + xfa/fxfa/parser/cxfa_issuers.h | 7 +- + xfa/fxfa/parser/cxfa_items.cpp | 10 +- + xfa/fxfa/parser/cxfa_items.h | 7 +- + xfa/fxfa/parser/cxfa_jog.cpp | 10 +- + xfa/fxfa/parser/cxfa_jog.h | 7 +- + xfa/fxfa/parser/cxfa_keep.cpp | 12 +- + xfa/fxfa/parser/cxfa_keep.h | 7 +- + xfa/fxfa/parser/cxfa_keyusage.cpp | 10 +- + xfa/fxfa/parser/cxfa_keyusage.h | 7 +- + xfa/fxfa/parser/cxfa_labelprinter.cpp | 18 +- + xfa/fxfa/parser/cxfa_labelprinter.h | 7 +- + xfa/fxfa/parser/cxfa_layout.cpp | 10 +- + xfa/fxfa/parser/cxfa_layout.h | 7 +- + xfa/fxfa/parser/cxfa_level.cpp | 10 +- + xfa/fxfa/parser/cxfa_level.h | 7 +- + xfa/fxfa/parser/cxfa_line.cpp | 19 +- + xfa/fxfa/parser/cxfa_line.h | 9 +- + xfa/fxfa/parser/cxfa_linear.cpp | 32 +- + xfa/fxfa/parser/cxfa_linear.h | 14 +- + xfa/fxfa/parser/cxfa_linearized.cpp | 10 +- + xfa/fxfa/parser/cxfa_linearized.h | 7 +- + xfa/fxfa/parser/cxfa_list.cpp | 20 +- + xfa/fxfa/parser/cxfa_list.h | 14 +- + xfa/fxfa/parser/cxfa_locale.cpp | 22 +- + xfa/fxfa/parser/cxfa_locale.h | 7 +- + xfa/fxfa/parser/cxfa_localemgr.cpp | 274 +- + xfa/fxfa/parser/cxfa_localemgr.h | 72 +- + xfa/fxfa/parser/cxfa_localeset.cpp | 10 +- + xfa/fxfa/parser/cxfa_localeset.h | 7 +- + xfa/fxfa/parser/cxfa_localevalue.cpp | 267 +- + xfa/fxfa/parser/cxfa_localevalue.h | 64 +- + xfa/fxfa/parser/cxfa_localevalue_unittest.cpp | 5 +- + xfa/fxfa/parser/cxfa_lockdocument.cpp | 10 +- + xfa/fxfa/parser/cxfa_lockdocument.h | 7 +- + xfa/fxfa/parser/cxfa_log.cpp | 18 +- + xfa/fxfa/parser/cxfa_log.h | 7 +- + xfa/fxfa/parser/cxfa_manifest.cpp | 12 +- + xfa/fxfa/parser/cxfa_manifest.h | 7 +- + xfa/fxfa/parser/cxfa_map.cpp | 10 +- + xfa/fxfa/parser/cxfa_map.h | 7 +- + xfa/fxfa/parser/cxfa_margin.cpp | 20 +- + xfa/fxfa/parser/cxfa_margin.h | 15 +- + xfa/fxfa/parser/cxfa_mdp.cpp | 10 +- + xfa/fxfa/parser/cxfa_mdp.h | 7 +- + xfa/fxfa/parser/cxfa_measurement.cpp | 20 +- + xfa/fxfa/parser/cxfa_measurement.h | 9 +- + xfa/fxfa/parser/cxfa_measurement_unittest.cpp | 32 +- + xfa/fxfa/parser/cxfa_medium.cpp | 10 +- + xfa/fxfa/parser/cxfa_medium.h | 7 +- + xfa/fxfa/parser/cxfa_mediuminfo.cpp | 12 +- + xfa/fxfa/parser/cxfa_mediuminfo.h | 7 +- + xfa/fxfa/parser/cxfa_meridiem.cpp | 10 +- + xfa/fxfa/parser/cxfa_meridiem.h | 7 +- + xfa/fxfa/parser/cxfa_meridiemnames.cpp | 12 +- + xfa/fxfa/parser/cxfa_meridiemnames.h | 7 +- + xfa/fxfa/parser/cxfa_message.cpp | 28 +- + xfa/fxfa/parser/cxfa_message.h | 7 +- + xfa/fxfa/parser/cxfa_messaging.cpp | 10 +- + xfa/fxfa/parser/cxfa_messaging.h | 7 +- + xfa/fxfa/parser/cxfa_mode.cpp | 10 +- + xfa/fxfa/parser/cxfa_mode.h | 7 +- + xfa/fxfa/parser/cxfa_modifyannots.cpp | 10 +- + xfa/fxfa/parser/cxfa_modifyannots.h | 7 +- + xfa/fxfa/parser/cxfa_month.cpp | 10 +- + xfa/fxfa/parser/cxfa_month.h | 7 +- + xfa/fxfa/parser/cxfa_monthnames.cpp | 12 +- + xfa/fxfa/parser/cxfa_monthnames.h | 7 +- + xfa/fxfa/parser/cxfa_msgid.cpp | 10 +- + xfa/fxfa/parser/cxfa_msgid.h | 7 +- + xfa/fxfa/parser/cxfa_nameattr.cpp | 10 +- + xfa/fxfa/parser/cxfa_nameattr.h | 7 +- + xfa/fxfa/parser/cxfa_neverembed.cpp | 10 +- + xfa/fxfa/parser/cxfa_neverembed.h | 7 +- + xfa/fxfa/parser/cxfa_node.cpp | 2393 +-- + xfa/fxfa/parser/cxfa_node.h | 261 +- + xfa/fxfa/parser/cxfa_node_unittest.cpp | 120 +- + xfa/fxfa/parser/cxfa_nodeiteratortemplate.h | 35 +- + .../cxfa_nodeiteratortemplate_unittest.cpp | 71 +- + xfa/fxfa/parser/cxfa_nodelocale.cpp | 92 +- + xfa/fxfa/parser/cxfa_nodelocale.h | 28 +- + xfa/fxfa/parser/cxfa_nodeowner.cpp | 23 +- + xfa/fxfa/parser/cxfa_nodeowner.h | 24 +- + xfa/fxfa/parser/cxfa_numberofcopies.cpp | 10 +- + xfa/fxfa/parser/cxfa_numberofcopies.h | 7 +- + xfa/fxfa/parser/cxfa_numberpattern.cpp | 10 +- + xfa/fxfa/parser/cxfa_numberpattern.h | 7 +- + xfa/fxfa/parser/cxfa_numberpatterns.cpp | 12 +- + xfa/fxfa/parser/cxfa_numberpatterns.h | 7 +- + xfa/fxfa/parser/cxfa_numbersymbol.cpp | 10 +- + xfa/fxfa/parser/cxfa_numbersymbol.h | 7 +- + xfa/fxfa/parser/cxfa_numbersymbols.cpp | 12 +- + xfa/fxfa/parser/cxfa_numbersymbols.h | 7 +- + xfa/fxfa/parser/cxfa_numericedit.cpp | 18 +- + xfa/fxfa/parser/cxfa_numericedit.h | 7 +- + xfa/fxfa/parser/cxfa_object.cpp | 24 +- + xfa/fxfa/parser/cxfa_object.h | 28 +- + xfa/fxfa/parser/cxfa_occur.cpp | 27 +- + xfa/fxfa/parser/cxfa_occur.h | 7 +- + xfa/fxfa/parser/cxfa_oid.cpp | 10 +- + xfa/fxfa/parser/cxfa_oid.h | 7 +- + xfa/fxfa/parser/cxfa_oids.cpp | 10 +- + xfa/fxfa/parser/cxfa_oids.h | 7 +- + xfa/fxfa/parser/cxfa_openaction.cpp | 12 +- + xfa/fxfa/parser/cxfa_openaction.h | 7 +- + xfa/fxfa/parser/cxfa_operation.cpp | 10 +- + xfa/fxfa/parser/cxfa_operation.h | 7 +- + xfa/fxfa/parser/cxfa_output.cpp | 16 +- + xfa/fxfa/parser/cxfa_output.h | 7 +- + xfa/fxfa/parser/cxfa_outputbin.cpp | 10 +- + xfa/fxfa/parser/cxfa_outputbin.h | 7 +- + xfa/fxfa/parser/cxfa_outputxsl.cpp | 12 +- + xfa/fxfa/parser/cxfa_outputxsl.h | 7 +- + xfa/fxfa/parser/cxfa_overflow.cpp | 10 +- + xfa/fxfa/parser/cxfa_overflow.h | 7 +- + xfa/fxfa/parser/cxfa_overprint.cpp | 10 +- + xfa/fxfa/parser/cxfa_overprint.h | 7 +- + xfa/fxfa/parser/cxfa_packet.cpp | 10 +- + xfa/fxfa/parser/cxfa_packet.h | 7 +- + xfa/fxfa/parser/cxfa_packets.cpp | 10 +- + xfa/fxfa/parser/cxfa_packets.h | 7 +- + xfa/fxfa/parser/cxfa_pagearea.cpp | 18 +- + xfa/fxfa/parser/cxfa_pagearea.h | 7 +- + xfa/fxfa/parser/cxfa_pageoffset.cpp | 10 +- + xfa/fxfa/parser/cxfa_pageoffset.h | 7 +- + xfa/fxfa/parser/cxfa_pagerange.cpp | 10 +- + xfa/fxfa/parser/cxfa_pagerange.h | 7 +- + xfa/fxfa/parser/cxfa_pageset.cpp | 14 +- + xfa/fxfa/parser/cxfa_pageset.h | 7 +- + xfa/fxfa/parser/cxfa_pagination.cpp | 10 +- + xfa/fxfa/parser/cxfa_pagination.h | 7 +- + xfa/fxfa/parser/cxfa_paginationoverride.cpp | 10 +- + xfa/fxfa/parser/cxfa_paginationoverride.h | 7 +- + xfa/fxfa/parser/cxfa_para.cpp | 12 +- + xfa/fxfa/parser/cxfa_para.h | 7 +- + xfa/fxfa/parser/cxfa_part.cpp | 10 +- + xfa/fxfa/parser/cxfa_part.h | 7 +- + xfa/fxfa/parser/cxfa_password.cpp | 10 +- + xfa/fxfa/parser/cxfa_password.h | 7 +- + xfa/fxfa/parser/cxfa_passwordedit.cpp | 23 +- + xfa/fxfa/parser/cxfa_passwordedit.h | 9 +- + xfa/fxfa/parser/cxfa_pattern.cpp | 45 +- + xfa/fxfa/parser/cxfa_pattern.h | 14 +- + xfa/fxfa/parser/cxfa_pcl.cpp | 18 +- + xfa/fxfa/parser/cxfa_pcl.h | 7 +- + xfa/fxfa/parser/cxfa_pdf.cpp | 48 +- + xfa/fxfa/parser/cxfa_pdf.h | 7 +- + xfa/fxfa/parser/cxfa_pdfa.cpp | 18 +- + xfa/fxfa/parser/cxfa_pdfa.h | 7 +- + xfa/fxfa/parser/cxfa_permissions.cpp | 28 +- + xfa/fxfa/parser/cxfa_permissions.h | 7 +- + xfa/fxfa/parser/cxfa_picktraybypdfsize.cpp | 10 +- + xfa/fxfa/parser/cxfa_picktraybypdfsize.h | 7 +- + xfa/fxfa/parser/cxfa_picture.cpp | 24 +- + xfa/fxfa/parser/cxfa_picture.h | 7 +- + xfa/fxfa/parser/cxfa_plaintextmetadata.cpp | 10 +- + xfa/fxfa/parser/cxfa_plaintextmetadata.h | 7 +- + xfa/fxfa/parser/cxfa_presence.cpp | 10 +- + xfa/fxfa/parser/cxfa_presence.h | 7 +- + xfa/fxfa/parser/cxfa_present.cpp | 38 +- + xfa/fxfa/parser/cxfa_present.h | 7 +- + xfa/fxfa/parser/cxfa_print.cpp | 10 +- + xfa/fxfa/parser/cxfa_print.h | 7 +- + xfa/fxfa/parser/cxfa_printername.cpp | 10 +- + xfa/fxfa/parser/cxfa_printername.h | 7 +- + xfa/fxfa/parser/cxfa_printhighquality.cpp | 10 +- + xfa/fxfa/parser/cxfa_printhighquality.h | 7 +- + xfa/fxfa/parser/cxfa_printscaling.cpp | 10 +- + xfa/fxfa/parser/cxfa_printscaling.h | 7 +- + xfa/fxfa/parser/cxfa_producer.cpp | 10 +- + xfa/fxfa/parser/cxfa_producer.h | 7 +- + xfa/fxfa/parser/cxfa_proto.cpp | 10 +- + xfa/fxfa/parser/cxfa_proto.h | 7 +- + xfa/fxfa/parser/cxfa_ps.cpp | 18 +- + xfa/fxfa/parser/cxfa_ps.h | 7 +- + xfa/fxfa/parser/cxfa_psmap.cpp | 10 +- + xfa/fxfa/parser/cxfa_psmap.h | 7 +- + xfa/fxfa/parser/cxfa_query.cpp | 14 +- + xfa/fxfa/parser/cxfa_query.h | 7 +- + xfa/fxfa/parser/cxfa_radial.cpp | 39 +- + xfa/fxfa/parser/cxfa_radial.h | 14 +- + xfa/fxfa/parser/cxfa_range.cpp | 10 +- + xfa/fxfa/parser/cxfa_range.h | 7 +- + xfa/fxfa/parser/cxfa_reason.cpp | 10 +- + xfa/fxfa/parser/cxfa_reason.h | 7 +- + xfa/fxfa/parser/cxfa_reasons.cpp | 10 +- + xfa/fxfa/parser/cxfa_reasons.h | 7 +- + xfa/fxfa/parser/cxfa_record.cpp | 10 +- + xfa/fxfa/parser/cxfa_record.h | 7 +- + xfa/fxfa/parser/cxfa_recordset.cpp | 10 +- + xfa/fxfa/parser/cxfa_recordset.h | 7 +- + xfa/fxfa/parser/cxfa_rectangle.cpp | 226 +- + xfa/fxfa/parser/cxfa_rectangle.h | 33 +- + xfa/fxfa/parser/cxfa_ref.cpp | 10 +- + xfa/fxfa/parser/cxfa_ref.h | 7 +- + xfa/fxfa/parser/cxfa_relevant.cpp | 10 +- + xfa/fxfa/parser/cxfa_relevant.h | 7 +- + xfa/fxfa/parser/cxfa_rename.cpp | 10 +- + xfa/fxfa/parser/cxfa_rename.h | 7 +- + xfa/fxfa/parser/cxfa_renderpolicy.cpp | 10 +- + xfa/fxfa/parser/cxfa_renderpolicy.h | 7 +- + xfa/fxfa/parser/cxfa_rootelement.cpp | 10 +- + xfa/fxfa/parser/cxfa_rootelement.h | 7 +- + xfa/fxfa/parser/cxfa_runscripts.cpp | 10 +- + xfa/fxfa/parser/cxfa_runscripts.h | 7 +- + xfa/fxfa/parser/cxfa_script.cpp | 39 +- + xfa/fxfa/parser/cxfa_script.h | 9 +- + xfa/fxfa/parser/cxfa_scriptmodel.cpp | 10 +- + xfa/fxfa/parser/cxfa_scriptmodel.h | 7 +- + xfa/fxfa/parser/cxfa_select.cpp | 10 +- + xfa/fxfa/parser/cxfa_select.h | 7 +- + xfa/fxfa/parser/cxfa_setproperty.cpp | 10 +- + xfa/fxfa/parser/cxfa_setproperty.h | 7 +- + xfa/fxfa/parser/cxfa_severity.cpp | 10 +- + xfa/fxfa/parser/cxfa_severity.h | 7 +- + xfa/fxfa/parser/cxfa_sharptext.cpp | 14 +- + xfa/fxfa/parser/cxfa_sharptext.h | 7 +- + xfa/fxfa/parser/cxfa_sharpxhtml.cpp | 14 +- + xfa/fxfa/parser/cxfa_sharpxhtml.h | 7 +- + xfa/fxfa/parser/cxfa_sharpxml.cpp | 10 +- + xfa/fxfa/parser/cxfa_sharpxml.h | 7 +- + xfa/fxfa/parser/cxfa_signature.cpp | 16 +- + xfa/fxfa/parser/cxfa_signature.h | 7 +- + xfa/fxfa/parser/cxfa_signatureproperties.cpp | 10 +- + xfa/fxfa/parser/cxfa_signatureproperties.h | 7 +- + xfa/fxfa/parser/cxfa_signdata.cpp | 14 +- + xfa/fxfa/parser/cxfa_signdata.h | 7 +- + xfa/fxfa/parser/cxfa_signing.cpp | 10 +- + xfa/fxfa/parser/cxfa_signing.h | 7 +- + xfa/fxfa/parser/cxfa_silentprint.cpp | 14 +- + xfa/fxfa/parser/cxfa_silentprint.h | 7 +- + xfa/fxfa/parser/cxfa_soapaction.cpp | 10 +- + xfa/fxfa/parser/cxfa_soapaction.h | 7 +- + xfa/fxfa/parser/cxfa_soapaddress.cpp | 10 +- + xfa/fxfa/parser/cxfa_soapaddress.h | 7 +- + xfa/fxfa/parser/cxfa_solid.cpp | 12 +- + xfa/fxfa/parser/cxfa_solid.h | 7 +- + xfa/fxfa/parser/cxfa_source.cpp | 12 +- + xfa/fxfa/parser/cxfa_source.h | 7 +- + xfa/fxfa/parser/cxfa_sourceset.cpp | 10 +- + xfa/fxfa/parser/cxfa_sourceset.h | 7 +- + xfa/fxfa/parser/cxfa_speak.cpp | 10 +- + xfa/fxfa/parser/cxfa_speak.h | 7 +- + xfa/fxfa/parser/cxfa_staple.cpp | 10 +- + xfa/fxfa/parser/cxfa_staple.h | 7 +- + xfa/fxfa/parser/cxfa_startnode.cpp | 10 +- + xfa/fxfa/parser/cxfa_startnode.h | 7 +- + xfa/fxfa/parser/cxfa_startpage.cpp | 10 +- + xfa/fxfa/parser/cxfa_startpage.h | 7 +- + xfa/fxfa/parser/cxfa_stipple.cpp | 27 +- + xfa/fxfa/parser/cxfa_stipple.h | 14 +- + xfa/fxfa/parser/cxfa_stroke.cpp | 52 +- + xfa/fxfa/parser/cxfa_stroke.h | 35 +- + xfa/fxfa/parser/cxfa_subform.cpp | 35 +- + xfa/fxfa/parser/cxfa_subform.h | 9 +- + xfa/fxfa/parser/cxfa_subformset.cpp | 16 +- + xfa/fxfa/parser/cxfa_subformset.h | 7 +- + xfa/fxfa/parser/cxfa_subjectdn.cpp | 10 +- + xfa/fxfa/parser/cxfa_subjectdn.h | 7 +- + xfa/fxfa/parser/cxfa_subjectdns.cpp | 10 +- + xfa/fxfa/parser/cxfa_subjectdns.h | 7 +- + xfa/fxfa/parser/cxfa_submit.cpp | 12 +- + xfa/fxfa/parser/cxfa_submit.h | 7 +- + xfa/fxfa/parser/cxfa_submitformat.cpp | 10 +- + xfa/fxfa/parser/cxfa_submitformat.h | 7 +- + xfa/fxfa/parser/cxfa_submiturl.cpp | 10 +- + xfa/fxfa/parser/cxfa_submiturl.h | 7 +- + xfa/fxfa/parser/cxfa_subsetbelow.cpp | 10 +- + xfa/fxfa/parser/cxfa_subsetbelow.h | 7 +- + xfa/fxfa/parser/cxfa_suppressbanner.cpp | 10 +- + xfa/fxfa/parser/cxfa_suppressbanner.h | 7 +- + xfa/fxfa/parser/cxfa_tagged.cpp | 10 +- + xfa/fxfa/parser/cxfa_tagged.h | 7 +- + xfa/fxfa/parser/cxfa_template.cpp | 30 +- + xfa/fxfa/parser/cxfa_template.h | 7 +- + xfa/fxfa/parser/cxfa_templatecache.cpp | 10 +- + xfa/fxfa/parser/cxfa_templatecache.h | 7 +- + xfa/fxfa/parser/cxfa_text.cpp | 21 +- + xfa/fxfa/parser/cxfa_text.h | 13 +- + xfa/fxfa/parser/cxfa_textedit.cpp | 18 +- + xfa/fxfa/parser/cxfa_textedit.h | 7 +- + xfa/fxfa/parser/cxfa_thisproxy.cpp | 24 +- + xfa/fxfa/parser/cxfa_thisproxy.h | 20 +- + xfa/fxfa/parser/cxfa_threshold.cpp | 10 +- + xfa/fxfa/parser/cxfa_threshold.h | 7 +- + xfa/fxfa/parser/cxfa_time.cpp | 10 +- + xfa/fxfa/parser/cxfa_time.h | 7 +- + xfa/fxfa/parser/cxfa_timepattern.cpp | 10 +- + xfa/fxfa/parser/cxfa_timepattern.h | 7 +- + xfa/fxfa/parser/cxfa_timepatterns.cpp | 12 +- + xfa/fxfa/parser/cxfa_timepatterns.h | 7 +- + xfa/fxfa/parser/cxfa_timestamp.cpp | 10 +- + xfa/fxfa/parser/cxfa_timestamp.h | 7 +- + xfa/fxfa/parser/cxfa_timezoneprovider.cpp | 28 +- + xfa/fxfa/parser/cxfa_timezoneprovider.h | 8 +- + .../parser/cxfa_timezoneprovider_unittest.cpp | 58 + + xfa/fxfa/parser/cxfa_to.cpp | 10 +- + xfa/fxfa/parser/cxfa_to.h | 7 +- + xfa/fxfa/parser/cxfa_tooltip.cpp | 10 +- + xfa/fxfa/parser/cxfa_tooltip.h | 7 +- + xfa/fxfa/parser/cxfa_trace.cpp | 10 +- + xfa/fxfa/parser/cxfa_trace.h | 7 +- + xfa/fxfa/parser/cxfa_transform.cpp | 18 +- + xfa/fxfa/parser/cxfa_transform.h | 7 +- + xfa/fxfa/parser/cxfa_traversal.cpp | 12 +- + xfa/fxfa/parser/cxfa_traversal.h | 7 +- + xfa/fxfa/parser/cxfa_traverse.cpp | 16 +- + xfa/fxfa/parser/cxfa_traverse.h | 7 +- + .../cxfa_traversestrategy_xfacontainernode.h | 2 +- + .../parser/cxfa_traversestrategy_xfanode.h | 2 +- + xfa/fxfa/parser/cxfa_treelist.cpp | 16 +- + xfa/fxfa/parser/cxfa_treelist.h | 9 +- + xfa/fxfa/parser/cxfa_type.cpp | 10 +- + xfa/fxfa/parser/cxfa_type.h | 7 +- + xfa/fxfa/parser/cxfa_typeface.cpp | 10 +- + xfa/fxfa/parser/cxfa_typeface.h | 7 +- + xfa/fxfa/parser/cxfa_typefaces.cpp | 10 +- + xfa/fxfa/parser/cxfa_typefaces.h | 7 +- + xfa/fxfa/parser/cxfa_ui.cpp | 40 +- + xfa/fxfa/parser/cxfa_ui.h | 7 +- + xfa/fxfa/parser/cxfa_update.cpp | 10 +- + xfa/fxfa/parser/cxfa_update.h | 7 +- + xfa/fxfa/parser/cxfa_uri.cpp | 10 +- + xfa/fxfa/parser/cxfa_uri.h | 7 +- + xfa/fxfa/parser/cxfa_user.cpp | 10 +- + xfa/fxfa/parser/cxfa_user.h | 7 +- + xfa/fxfa/parser/cxfa_validate.cpp | 46 +- + xfa/fxfa/parser/cxfa_validate.h | 8 +- + .../cxfa_validateapprovalsignatures.cpp | 10 +- + .../parser/cxfa_validateapprovalsignatures.h | 7 +- + xfa/fxfa/parser/cxfa_validationmessaging.cpp | 10 +- + xfa/fxfa/parser/cxfa_validationmessaging.h | 7 +- + xfa/fxfa/parser/cxfa_value.cpp | 70 +- + xfa/fxfa/parser/cxfa_value.h | 9 +- + xfa/fxfa/parser/cxfa_variables.cpp | 17 +- + xfa/fxfa/parser/cxfa_variables.h | 9 +- + xfa/fxfa/parser/cxfa_version.cpp | 10 +- + xfa/fxfa/parser/cxfa_version.h | 7 +- + xfa/fxfa/parser/cxfa_versioncontrol.cpp | 10 +- + xfa/fxfa/parser/cxfa_versioncontrol.h | 7 +- + xfa/fxfa/parser/cxfa_viewerpreferences.cpp | 28 +- + xfa/fxfa/parser/cxfa_viewerpreferences.h | 7 +- + xfa/fxfa/parser/cxfa_webclient.cpp | 14 +- + xfa/fxfa/parser/cxfa_webclient.h | 7 +- + xfa/fxfa/parser/cxfa_whitespace.cpp | 10 +- + xfa/fxfa/parser/cxfa_whitespace.h | 7 +- + xfa/fxfa/parser/cxfa_window.cpp | 10 +- + xfa/fxfa/parser/cxfa_window.h | 7 +- + xfa/fxfa/parser/cxfa_wsdladdress.cpp | 10 +- + xfa/fxfa/parser/cxfa_wsdladdress.h | 7 +- + xfa/fxfa/parser/cxfa_wsdlconnection.cpp | 22 +- + xfa/fxfa/parser/cxfa_wsdlconnection.h | 7 +- + xfa/fxfa/parser/cxfa_xdc.cpp | 14 +- + xfa/fxfa/parser/cxfa_xdc.h | 7 +- + xfa/fxfa/parser/cxfa_xdp.cpp | 12 +- + xfa/fxfa/parser/cxfa_xdp.h | 7 +- + xfa/fxfa/parser/cxfa_xfa.cpp | 10 +- + xfa/fxfa/parser/cxfa_xfa.h | 7 +- + xfa/fxfa/parser/cxfa_xmlconnection.cpp | 12 +- + xfa/fxfa/parser/cxfa_xmlconnection.h | 7 +- + xfa/fxfa/parser/cxfa_xmllocale.cpp | 66 +- + xfa/fxfa/parser/cxfa_xmllocale.h | 31 +- + xfa/fxfa/parser/cxfa_xmllocale_unittest.cpp | 90 +- + xfa/fxfa/parser/cxfa_xsdconnection.cpp | 14 +- + xfa/fxfa/parser/cxfa_xsdconnection.h | 7 +- + xfa/fxfa/parser/cxfa_xsl.cpp | 14 +- + xfa/fxfa/parser/cxfa_xsl.h | 7 +- + xfa/fxfa/parser/cxfa_zpl.cpp | 18 +- + xfa/fxfa/parser/cxfa_zpl.h | 7 +- + xfa/fxfa/parser/element_attributes.inc | 2 +- + xfa/fxfa/parser/elements.inc | 4 +- + xfa/fxfa/parser/gced_locale_iface.h | 19 + + xfa/fxfa/parser/packets.inc | 33 +- + xfa/fxfa/parser/xfa_basic_data.cpp | 194 +- + xfa/fxfa/parser/xfa_basic_data.h | 37 +- + xfa/fxfa/parser/xfa_basic_data_unittest.cpp | 14 +- + .../parser/xfa_document_datamerger_imp.cpp | 8 +- + xfa/fxfa/parser/xfa_document_datamerger_imp.h | 2 +- + xfa/fxfa/parser/xfa_resolvenode_rs.h | 39 - + xfa/fxfa/parser/xfa_utils.cpp | 191 +- + xfa/fxfa/parser/xfa_utils.h | 12 +- + xfa/fxfa/parser/xfa_utils_unittest.cpp | 6 +- + xfa/fxgraphics/BUILD.gn | 31 - + xfa/fxgraphics/cxfa_gecolor.cpp | 21 - + xfa/fxgraphics/cxfa_gecolor.h | 49 - + xfa/fxgraphics/cxfa_gepath.cpp | 150 - + xfa/fxgraphics/cxfa_gepattern.cpp | 14 - + xfa/fxgraphics/cxfa_gepattern.h | 33 - + xfa/fxgraphics/cxfa_geshading.cpp | 78 - + xfa/fxgraphics/cxfa_geshading.h | 57 - + xfa/fxgraphics/cxfa_graphics.h | 90 - + 3881 files changed, 199496 insertions(+), 180981 deletions(-) + create mode 100644 .style.yapf + rename .vpython => .vpython3 (56%) + create mode 100644 CONTRIBUTING.md + create mode 100644 DIR_METADATA + delete mode 100644 METADATA + create mode 100755 PRESUBMIT_test.py + create mode 100644 PRESUBMIT_test_mocks.py + create mode 100644 build/buildflag.h + create mode 100644 build_overrides/BUILDCONFIG.gn + create mode 100644 build_overrides/compiler/BUILD.gn + create mode 100644 build_overrides/partition_alloc.gni + create mode 100644 constants/access_permissions.h + create mode 100644 constants/annotation_common.cpp + create mode 100644 constants/appearance.cpp + create mode 100644 constants/appearance.h + create mode 100644 constants/ascii.h + create mode 100644 constants/font_encodings.cpp + create mode 100644 constants/font_encodings.h + create mode 100644 constants/form_fields.cpp + create mode 100644 constants/page_object.cpp + create mode 100644 constants/stream_dict_common.cpp + create mode 100644 constants/transparency.cpp + delete mode 100644 core/fpdfapi/font/cpdf_cmapmanager.cpp + delete mode 100644 core/fpdfapi/font/cpdf_cmapmanager.h + create mode 100644 core/fpdfapi/page/cpdf_basedcs.cpp + create mode 100644 core/fpdfapi/page/cpdf_basedcs.h + create mode 100644 core/fpdfapi/page/cpdf_colorspace_unittest.cpp + create mode 100644 core/fpdfapi/page/cpdf_function_unittest.cpp + create mode 100644 core/fpdfapi/page/cpdf_imageloader.cpp + rename core/fpdfapi/{render => page}/cpdf_imageloader.h (53%) + create mode 100644 core/fpdfapi/page/cpdf_indexedcs.cpp + create mode 100644 core/fpdfapi/page/cpdf_indexedcs.h + create mode 100644 core/fpdfapi/page/cpdf_pageimagecache.cpp + create mode 100644 core/fpdfapi/page/cpdf_pageimagecache.h + create mode 100644 core/fpdfapi/page/cpdf_pageimagecache_unittest.cpp + create mode 100644 core/fpdfapi/page/test_with_page_module.cpp + create mode 100644 core/fpdfapi/page/test_with_page_module.h + create mode 100644 core/fpdfapi/parser/cpdf_dictionary_unittest.cpp + create mode 100644 core/fpdfapi/parser/cpdf_object_stream_unittest.cpp + create mode 100644 core/fpdfapi/parser/cpdf_test_document.cpp + create mode 100644 core/fpdfapi/parser/cpdf_test_document.h + create mode 100644 core/fpdfapi/parser/object_tree_traversal_util.cpp + create mode 100644 core/fpdfapi/parser/object_tree_traversal_util.h + create mode 100644 core/fpdfapi/parser/object_tree_traversal_util_embeddertest.cpp + create mode 100644 core/fpdfapi/render/charposlist.cpp + create mode 100644 core/fpdfapi/render/charposlist.h + delete mode 100644 core/fpdfapi/render/cpdf_charposlist.cpp + delete mode 100644 core/fpdfapi/render/cpdf_charposlist.h + delete mode 100644 core/fpdfapi/render/cpdf_imagecacheentry.cpp + delete mode 100644 core/fpdfapi/render/cpdf_imagecacheentry.h + delete mode 100644 core/fpdfapi/render/cpdf_imageloader.cpp + delete mode 100644 core/fpdfapi/render/cpdf_pagerendercache.cpp + delete mode 100644 core/fpdfapi/render/cpdf_pagerendercache.h + create mode 100644 core/fpdfapi/render/cpdf_rendertiling.cpp + create mode 100644 core/fpdfapi/render/cpdf_rendertiling.h + delete mode 100644 core/fpdfdoc/cba_fontmap.cpp + delete mode 100644 core/fpdfdoc/cline.cpp + delete mode 100644 core/fpdfdoc/cline.h + create mode 100644 core/fpdfdoc/cpdf_action_unittest.cpp + create mode 100644 core/fpdfdoc/cpdf_bafontmap.cpp + rename core/fpdfdoc/{cba_fontmap.h => cpdf_bafontmap.h} (58%) + create mode 100644 core/fpdfdoc/cpdf_bafontmap_unittest.cpp + rename core/fpdfdoc/{cpvt_generateap.cpp => cpdf_generateap.cpp} (72%) + rename core/fpdfdoc/{cpvt_generateap.h => cpdf_generateap.h} (66%) + delete mode 100644 core/fpdfdoc/cpdf_variabletext.cpp + rename core/fpdfdoc/{ctypeset.cpp => cpvt_section.cpp} (50%) + create mode 100644 core/fpdfdoc/cpvt_section.h + create mode 100644 core/fpdfdoc/cpvt_variabletext.cpp + rename core/fpdfdoc/{cpdf_variabletext.h => cpvt_variabletext.h} (73%) + delete mode 100644 core/fpdfdoc/csection.cpp + delete mode 100644 core/fpdfdoc/csection.h + delete mode 100644 core/fpdfdoc/ctypeset.h + create mode 100644 core/fxcodec/bmp/bmp_decoder.cpp + create mode 100644 core/fxcodec/bmp/bmp_decoder.h + create mode 100644 core/fxcodec/bmp/bmp_progressive_decoder.cpp + create mode 100644 core/fxcodec/bmp/bmp_progressive_decoder.h + delete mode 100644 core/fxcodec/bmp/bmpmodule.cpp + delete mode 100644 core/fxcodec/bmp/bmpmodule.h + delete mode 100644 core/fxcodec/gif/cfx_lzwdecompressor.h + create mode 100644 core/fxcodec/gif/gif_decoder.cpp + create mode 100644 core/fxcodec/gif/gif_decoder.h + create mode 100644 core/fxcodec/gif/gif_progressive_decoder.cpp + create mode 100644 core/fxcodec/gif/gif_progressive_decoder.h + delete mode 100644 core/fxcodec/gif/gifmodule.cpp + delete mode 100644 core/fxcodec/gif/gifmodule.h + rename core/fxcodec/gif/{cfx_lzwdecompressor.cpp => lzw_decompressor.cpp} (67%) + create mode 100644 core/fxcodec/gif/lzw_decompressor.h + rename core/fxcodec/gif/{cfx_lzwdecompressor_unittest.cpp => lzw_decompressor_unittest.cpp} (52%) + rename core/fxcodec/icc/{iccmodule.cpp => icc_transform.cpp} (59%) + create mode 100644 core/fxcodec/icc/icc_transform.h + delete mode 100644 core/fxcodec/icc/iccmodule.h + rename core/fxcodec/jbig2/{jbig2module.cpp => jbig2_decoder.cpp} (52%) + rename core/fxcodec/jbig2/{jbig2module.h => jbig2_decoder.h} (57%) + create mode 100644 core/fxcodec/jpeg/jpeg_common.cpp + create mode 100644 core/fxcodec/jpeg/jpeg_common.h + create mode 100644 core/fxcodec/jpeg/jpeg_progressive_decoder.cpp + create mode 100644 core/fxcodec/jpeg/jpeg_progressive_decoder.h + delete mode 100644 core/fxcodec/jpx/jpxmodule.cpp + delete mode 100644 core/fxcodec/jpx/jpxmodule.h + rename core/fxcodec/png/{pngmodule.cpp => png_decoder.cpp} (81%) + rename core/fxcodec/png/{pngmodule.h => png_decoder.h} (50%) + rename core/fxcodec/{progressivedecoder.cpp => progressive_decoder.cpp} (55%) + rename core/fxcodec/{progressivedecoder.h => progressive_decoder.h} (63%) + rename core/fxcodec/{codec_module_iface.h => progressive_decoder_iface.h} (50%) + create mode 100644 core/fxcodec/progressive_decoder_unittest.cpp + delete mode 100644 core/fxcodec/progressivedecoder_unittest.cpp + rename core/fxcodec/tiff/{tiffmodule.cpp => tiff_decoder.cpp} (73%) + create mode 100644 core/fxcodec/tiff/tiff_decoder.h + delete mode 100644 core/fxcodec/tiff/tiffmodule.h + create mode 100644 core/fxcrt/autonuller.h + create mode 100644 core/fxcrt/autonuller_unittest.cpp + create mode 100644 core/fxcrt/binary_buffer.cpp + create mode 100644 core/fxcrt/binary_buffer.h + create mode 100644 core/fxcrt/binary_buffer_unittest.cpp + delete mode 100644 core/fxcrt/cfx_binarybuf.cpp + delete mode 100644 core/fxcrt/cfx_binarybuf.h + create mode 100644 core/fxcrt/cfx_datetime_unittest.cpp + delete mode 100644 core/fxcrt/cfx_fixedbufgrow.h + create mode 100644 core/fxcrt/cfx_read_only_span_stream.cpp + create mode 100644 core/fxcrt/cfx_read_only_span_stream.h + create mode 100644 core/fxcrt/cfx_read_only_string_stream.cpp + create mode 100644 core/fxcrt/cfx_read_only_string_stream.h + create mode 100644 core/fxcrt/cfx_read_only_vector_stream.cpp + create mode 100644 core/fxcrt/cfx_read_only_vector_stream.h + delete mode 100644 core/fxcrt/cfx_readonlymemorystream.cpp + delete mode 100644 core/fxcrt/cfx_readonlymemorystream.h + delete mode 100644 core/fxcrt/cfx_widetextbuf.cpp + delete mode 100644 core/fxcrt/cfx_widetextbuf.h + delete mode 100644 core/fxcrt/cfx_widetextbuf_unittest.cpp + create mode 100644 core/fxcrt/css/cfx_cssdata_unittest.cpp + delete mode 100644 core/fxcrt/css/cfx_cssexttextbuf.cpp + delete mode 100644 core/fxcrt/css/cfx_cssexttextbuf.h + create mode 100644 core/fxcrt/css/cfx_cssinputtextbuf.cpp + create mode 100644 core/fxcrt/css/cfx_cssinputtextbuf.h + create mode 100644 core/fxcrt/css/cfx_cssoutputtextbuf.cpp + create mode 100644 core/fxcrt/css/cfx_cssoutputtextbuf.h + create mode 100644 core/fxcrt/css/cfx_csssyntaxparser_unittest.cpp + delete mode 100644 core/fxcrt/css/cfx_csstextbuf.cpp + delete mode 100644 core/fxcrt/css/cfx_csstextbuf.h + create mode 100644 core/fxcrt/css/properties.inc + create mode 100644 core/fxcrt/css/property_values.inc + create mode 100644 core/fxcrt/data_vector.h + create mode 100644 core/fxcrt/fake_time_test.cpp + create mode 100644 core/fxcrt/fake_time_test.h + create mode 100644 core/fxcrt/fixed_size_data_vector.h + create mode 100644 core/fxcrt/fixed_try_alloc_zeroed_data_vector.h + create mode 100644 core/fxcrt/fixed_try_alloc_zeroed_data_vector_unittest.cpp + create mode 100644 core/fxcrt/fixed_uninit_data_vector.h + create mode 100644 core/fxcrt/fixed_uninit_data_vector_unittest.cpp + create mode 100644 core/fxcrt/fixed_zeroed_data_vector.h + create mode 100644 core/fxcrt/fixed_zeroed_data_vector_unittest.cpp + create mode 100644 core/fxcrt/fx_2d_size.h + create mode 100644 core/fxcrt/fx_codepage_forward.h + create mode 100644 core/fxcrt/fx_folder.h + create mode 100644 core/fxcrt/fx_folder_posix.cpp + create mode 100644 core/fxcrt/fx_folder_windows.cpp + create mode 100644 core/fxcrt/fx_memory_malloc.cpp + create mode 100644 core/fxcrt/fx_memory_pa.cpp + create mode 100644 core/fxcrt/fx_safe_types_unittest.cpp + create mode 100644 core/fxcrt/fx_string_wrappers.h + create mode 100644 core/fxcrt/fx_string_wrappers_unittest.cpp + create mode 100644 core/fxcrt/fx_types.h + create mode 100644 core/fxcrt/mask.h + create mode 100644 core/fxcrt/mask_unittest.cpp + delete mode 100644 core/fxcrt/retained_tree_node.h + delete mode 100644 core/fxcrt/retained_tree_node_unittest.cpp + create mode 100644 core/fxcrt/scoped_set_insertion.h + create mode 100644 core/fxcrt/scoped_set_insertion_unittest.cpp + create mode 100644 core/fxcrt/small_buffer.h + create mode 100644 core/fxcrt/small_buffer_unittest.cpp + create mode 100644 core/fxcrt/span_util.h + create mode 100644 core/fxcrt/span_util_unittest.cpp + create mode 100644 core/fxcrt/stl_util.h + create mode 100644 core/fxcrt/string_data_template.cpp + delete mode 100644 core/fxcrt/timerhandler_iface.h + create mode 100644 core/fxcrt/widetext_buffer.cpp + create mode 100644 core/fxcrt/widetext_buffer.h + create mode 100644 core/fxcrt/widetext_buffer_unittest.cpp + rename core/fxge/android/{fx_android_imp.cpp => fx_android_impl.cpp} (70%) + delete mode 100644 core/fxge/apple/apple_int.h + create mode 100644 core/fxge/apple/fx_apple_impl.cpp + create mode 100644 core/fxge/apple/fx_apple_platform.h + delete mode 100644 core/fxge/apple/fx_mac_imp.cpp + create mode 100644 core/fxge/apple/fx_quartz_device.h + create mode 100644 core/fxge/calculate_pitch.cpp + create mode 100644 core/fxge/calculate_pitch.h + create mode 100644 core/fxge/cfx_defaultrenderdevice.cpp + create mode 100644 core/fxge/cfx_defaultrenderdevice_unittest.cpp + create mode 100644 core/fxge/cfx_drawutils.cpp + create mode 100644 core/fxge/cfx_drawutils.h + create mode 100644 core/fxge/cfx_fillrenderoptions.h + create mode 100644 core/fxge/cfx_folderfontinfo_unittest.cpp + create mode 100644 core/fxge/cfx_path.cpp + create mode 100644 core/fxge/cfx_path.h + create mode 100644 core/fxge/cfx_path_unittest.cpp + delete mode 100644 core/fxge/cfx_pathdata.cpp + delete mode 100644 core/fxge/cfx_pathdata.h + create mode 100644 core/fxge/cfx_textrenderoptions.h + create mode 100644 core/fxge/cfx_windowsrenderdevice.cpp + rename core/fxge/{win32/fx_win32_device_embeddertest.cpp => cfx_windowsrenderdevice_embeddertest.cpp} (63%) + create mode 100644 core/fxge/dib/cfx_dibbase_unittest.cpp + create mode 100644 core/fxge/dib/fx_dib.cpp + rename core/fxge/{ => dib}/fx_dib.h (67%) + delete mode 100644 core/fxge/dib/fx_dib_main.cpp + rename core/fxge/{ => freetype}/fx_freetype.h (85%) + delete mode 100644 core/fxge/fx_ge_fontmap.cpp + rename core/fxge/{fx_ge_linux.cpp => linux/fx_linux_impl.cpp} (61%) + create mode 100644 core/fxge/win32/DEPS + create mode 100644 core/fxge/win32/cfx_psfonttracker.cpp + create mode 100644 core/fxge/win32/cfx_psfonttracker.h + create mode 100644 core/fxge/win32/cfx_psrenderer_unittest.cpp + delete mode 100644 core/fxge/win32/cfx_windowsdib.h + create mode 100644 core/fxge/win32/cgdi_device_driver.cpp + create mode 100644 core/fxge/win32/cgdi_device_driver.h + create mode 100644 core/fxge/win32/cgdi_display_driver.cpp + create mode 100644 core/fxge/win32/cgdi_display_driver.h + rename core/fxge/win32/{fx_win32_gdipext.cpp => cgdi_plus_ext.cpp} (51%) + create mode 100644 core/fxge/win32/cgdi_plus_ext.h + create mode 100644 core/fxge/win32/cgdi_printer_driver.cpp + create mode 100644 core/fxge/win32/cgdi_printer_driver.h + create mode 100644 core/fxge/win32/cps_printer_driver.cpp + create mode 100644 core/fxge/win32/cps_printer_driver.h + create mode 100644 core/fxge/win32/ctext_only_printer_driver.cpp + create mode 100644 core/fxge/win32/ctext_only_printer_driver.h + create mode 100644 core/fxge/win32/cwin32_platform.cpp + create mode 100644 core/fxge/win32/cwin32_platform.h + delete mode 100644 core/fxge/win32/fx_win32_device.cpp + delete mode 100644 core/fxge/win32/fx_win32_dib.cpp + delete mode 100644 core/fxge/win32/fx_win32_print.cpp + delete mode 100644 core/fxge/win32/win32_int.h + delete mode 100644 fpdfsdk/cpdfsdk_actionhandler.cpp + delete mode 100644 fpdfsdk/cpdfsdk_actionhandler.h + delete mode 100644 fpdfsdk/cpdfsdk_annothandlermgr.cpp + delete mode 100644 fpdfsdk/cpdfsdk_annothandlermgr.h + create mode 100644 fpdfsdk/cpdfsdk_baannot_embeddertest.cpp + delete mode 100644 fpdfsdk/cpdfsdk_baannothandler.cpp + delete mode 100644 fpdfsdk/cpdfsdk_baannothandler.h + delete mode 100644 fpdfsdk/cpdfsdk_baannothandler_embeddertest.cpp + delete mode 100644 fpdfsdk/cpdfsdk_fieldaction.cpp + create mode 100644 fpdfsdk/cpdfsdk_helpers_unittest.cpp + delete mode 100644 fpdfsdk/cpdfsdk_widgethandler.cpp + delete mode 100644 fpdfsdk/cpdfsdk_widgethandler.h + create mode 100644 fpdfsdk/formfiller/cffl_combobox_embeddertest.cpp + create mode 100644 fpdfsdk/formfiller/cffl_fieldaction.cpp + rename fpdfsdk/{cpdfsdk_fieldaction.h => formfiller/cffl_fieldaction.h} (55%) + create mode 100644 fpdfsdk/formfiller/cffl_formfield.cpp + create mode 100644 fpdfsdk/formfiller/cffl_formfield.h + delete mode 100644 fpdfsdk/formfiller/cffl_formfiller.cpp + delete mode 100644 fpdfsdk/formfiller/cffl_formfiller.h + create mode 100644 fpdfsdk/formfiller/cffl_perwindowdata.cpp + create mode 100644 fpdfsdk/formfiller/cffl_perwindowdata.h + delete mode 100644 fpdfsdk/fpdf_annot_unittest.cpp + rename fpdfsdk/{fpdf_editimg_unittest.cpp => fpdf_editimg_embeddertest.cpp} (60%) + create mode 100644 fpdfsdk/fpdf_signature.cpp + create mode 100644 fpdfsdk/fpdf_signature_embeddertest.cpp + create mode 100644 fpdfsdk/fpdfxfa/cpdfxfa_context_embeddertest.cpp + delete mode 100644 fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.cpp + delete mode 100644 fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h + delete mode 100644 fpdfsdk/ipdfsdk_annothandler.h + create mode 100644 fpdfsdk/pwl/cpwl_cbbutton.cpp + create mode 100644 fpdfsdk/pwl/cpwl_cbbutton.h + create mode 100644 fpdfsdk/pwl/cpwl_cblistbox.cpp + create mode 100644 fpdfsdk/pwl/cpwl_cblistbox.h + create mode 100644 fpdfsdk/pwl/cpwl_combo_box_edit_embeddertest.cpp + create mode 100644 fpdfsdk/pwl/cpwl_combo_box_embeddertest.h + delete mode 100644 fpdfsdk/pwl/cpwl_edit_ctrl.cpp + delete mode 100644 fpdfsdk/pwl/cpwl_edit_ctrl.h + delete mode 100644 fpdfsdk/pwl/cpwl_icon.cpp + delete mode 100644 fpdfsdk/pwl/cpwl_icon.h + rename fpdfsdk/pwl/{cpwl_list_impl.cpp => cpwl_list_ctrl.cpp} (81%) + rename fpdfsdk/pwl/{cpwl_list_impl.h => cpwl_list_ctrl.h} (71%) + create mode 100644 fpdfsdk/pwl/cpwl_sbbutton.cpp + create mode 100644 fpdfsdk/pwl/cpwl_sbbutton.h + create mode 100644 fpdfsdk/pwl/cpwl_special_button_embeddertest.cpp + create mode 100644 fpdfsdk/pwl/ipwl_fillernotify.h + delete mode 100644 fpdfsdk/pwl/ipwl_systemhandler.h + delete mode 100644 fxbarcode/utils.h + create mode 100644 fxjs/cfx_v8_array_buffer_allocator.cpp + create mode 100644 fxjs/cfx_v8_array_buffer_allocator.h + delete mode 100644 fxjs/cjs_eventrecorder.cpp + delete mode 100644 fxjs/cjs_eventrecorder.h + create mode 100644 fxjs/fxv8.cpp + create mode 100644 fxjs/fxv8.h + create mode 100644 fxjs/gc/container_trace.h + create mode 100644 fxjs/gc/container_trace_unittest.cpp + create mode 100644 fxjs/gc/gced_tree_node.h + create mode 100644 fxjs/gc/gced_tree_node_mixin.h + create mode 100644 fxjs/gc/gced_tree_node_mixin_unittest.cpp + create mode 100644 fxjs/gc/gced_tree_node_unittest.cpp + create mode 100644 fxjs/gc/heap.cpp + create mode 100644 fxjs/gc/heap.h + create mode 100644 fxjs/gc/heap_unittest.cpp + create mode 100644 fxjs/gc/move_unittest.cpp + delete mode 100644 fxjs/xfa/cfxjse_arguments.cpp + delete mode 100644 fxjs/xfa/cfxjse_arguments.h + create mode 100644 fxjs/xfa/cfxjse_formcalc_context_unittest.cpp + create mode 100644 fxjs/xfa/cfxjse_mapmodule.cpp + create mode 100644 fxjs/xfa/cfxjse_mapmodule.h + create mode 100644 fxjs/xfa/cfxjse_mapmodule_unittest.cpp + rename xfa/fxfa/parser/cxfa_nodehelper.cpp => fxjs/xfa/cfxjse_nodehelper.cpp (77%) + rename xfa/fxfa/parser/cxfa_nodehelper.h => fxjs/xfa/cfxjse_nodehelper.h (54%) + create mode 100644 fxjs/xfa/cjx_object_embeddertest.cpp + create mode 100644 public/fpdf_signature.h + create mode 100644 samples/chromium_support/DEPS + create mode 100644 samples/chromium_support/discardable_memory_allocator.cc + create mode 100644 samples/chromium_support/discardable_memory_allocator.h + create mode 100644 samples/simple_no_v8.c + create mode 100644 samples/simple_with_v8.cc + create mode 100644 skia/OWNERS + rename skia/config/{SkUserConfig.h => SkPdfiumUserConfig.h} (87%) + create mode 100644 testing/SUPPRESSIONS_EXACT_MATCHING + create mode 100644 testing/command_line_helpers.cpp + create mode 100644 testing/command_line_helpers.h + create mode 100644 testing/embedder_test_constants.cpp + create mode 100644 testing/embedder_test_constants.h + create mode 100644 testing/embedder_test_environment.cpp + create mode 100644 testing/embedder_test_environment.h + create mode 100644 testing/external_engine_embedder_test.cpp + create mode 100644 testing/external_engine_embedder_test.h + create mode 100644 testing/font_renamer.cpp + create mode 100644 testing/font_renamer.h + create mode 100644 testing/fuzzers/pdf_cpdf_tounicodemap_fuzzer.cc + delete mode 100644 testing/fuzzers/pdf_fm2js_fuzzer.cc + create mode 100644 testing/fuzzers/pdf_formcalc_translate_fuzzer.cc + create mode 100644 testing/fuzzers/pdf_fuzzer_init_public.h + create mode 100644 testing/fuzzers/pdf_fuzzer_templates.h + create mode 100644 testing/fuzzers/pdf_xfa_fdp_fuzzer.cc + create mode 100644 testing/fuzzers/pdf_xfa_raw_fuzzer.cc + create mode 100644 testing/fuzzers/pdf_xfa_xdp_fdp_fuzzer.cc + create mode 100644 testing/fuzzers/xfa_process_state.cc + create mode 100644 testing/fuzzers/xfa_process_state.h + create mode 100644 testing/fxgc_unittest.cpp + create mode 100644 testing/fxgc_unittest.h + create mode 100644 testing/fxv8_unittest.cpp + rename fxjs/cfx_v8_unittest.h => testing/fxv8_unittest.h (74%) + create mode 100644 testing/gtest/include/gtest/gtest-death-test.h + create mode 100644 testing/gtest/include/gtest/gtest-message.h + create mode 100644 testing/gtest/include/gtest/gtest-param-test.h + create mode 100644 testing/gtest/include/gtest/gtest-spi.h + create mode 100644 testing/pdf_test_environment.cpp + create mode 100644 testing/pdf_test_environment.h + create mode 100644 testing/resources/CMYK-alpha.jpf + create mode 100644 testing/resources/CMYK.jpf + create mode 100644 testing/resources/RGB-alpha.jp2 + create mode 100644 testing/resources/RGB.jp2 + create mode 100644 testing/resources/annot_javascript.in + create mode 100644 testing/resources/annot_javascript.pdf + rename testing/resources/{link_annots.in => annots.in} (90%) + rename testing/resources/{link_annots.pdf => annots.pdf} (81%) + create mode 100644 testing/resources/annots_action_handling.in + create mode 100644 testing/resources/annots_action_handling.pdf + create mode 100644 testing/resources/bad_dict_keys.in + create mode 100644 testing/resources/bad_dict_keys.pdf + create mode 100644 testing/resources/bigtable_mini.in + create mode 100644 testing/resources/bigtable_mini.pdf + create mode 100644 testing/resources/bug_1055869.in + create mode 100644 testing/resources/bug_1055869.pdf + create mode 100644 testing/resources/bug_1058653.in + create mode 100644 testing/resources/bug_1058653.pdf + create mode 100644 testing/resources/bug_1124998.pdf + create mode 100644 testing/resources/bug_1229106.in + create mode 100644 testing/resources/bug_1229106.pdf + create mode 100644 testing/resources/bug_1296920.in + create mode 100644 testing/resources/bug_1296920.pdf + create mode 100644 testing/resources/bug_1302455.in + create mode 100644 testing/resources/bug_1302455.pdf + create mode 100644 testing/resources/bug_1324189.in + create mode 100644 testing/resources/bug_1324189.pdf + create mode 100644 testing/resources/bug_1324503.in + create mode 100644 testing/resources/bug_1324503.pdf + create mode 100644 testing/resources/bug_1327884.pdf + create mode 100644 testing/resources/bug_1328389.in + create mode 100644 testing/resources/bug_1328389.pdf + create mode 100644 testing/resources/bug_1333298.in + create mode 100644 testing/resources/bug_1333298.pdf + create mode 100644 testing/resources/bug_1388_2.pdf + create mode 100644 testing/resources/bug_1396264.in + create mode 100644 testing/resources/bug_1396264.pdf + create mode 100644 testing/resources/bug_1469.jp2 + create mode 100644 testing/resources/bug_1506.in + create mode 100644 testing/resources/bug_1506.pdf + create mode 100644 testing/resources/bug_1549.in + create mode 100644 testing/resources/bug_1549.pdf + create mode 100644 testing/resources/bug_1558.in + create mode 100644 testing/resources/bug_1558.pdf + create mode 100644 testing/resources/bug_1574.in + create mode 100644 testing/resources/bug_1574.pdf + create mode 100644 testing/resources/bug_1591.in + create mode 100644 testing/resources/bug_1591.pdf + create mode 100644 testing/resources/bug_1646.pdf + create mode 100644 testing/resources/bug_1752_truetype_font.fragment + create mode 100644 testing/resources/bug_1768.in + create mode 100644 testing/resources/bug_1768.pdf + create mode 100644 testing/resources/bug_1769.in + create mode 100644 testing/resources/bug_1769.pdf + create mode 100644 testing/resources/bug_1919.pdf + create mode 100644 testing/resources/bug_674771.in + create mode 100644 testing/resources/bug_674771.pdf + create mode 100644 testing/resources/bug_889099.in + create mode 100644 testing/resources/bug_889099.pdf + create mode 100644 testing/resources/dashed_lines.in + create mode 100644 testing/resources/dashed_lines.pdf + create mode 100644 testing/resources/docmdp.in + create mode 100644 testing/resources/docmdp.pdf + create mode 100644 testing/resources/embedded_attachments_invalid_data.in + create mode 100644 testing/resources/embedded_attachments_invalid_data.pdf + create mode 100644 testing/resources/fonts/SymbolNeu.ttf + create mode 100644 testing/resources/get_page_aaction.in + create mode 100644 testing/resources/get_page_aaction.pdf + create mode 100644 testing/resources/gotoe_action.in + create mode 100644 testing/resources/gotoe_action.pdf + create mode 100644 testing/resources/gray-alpha.jp2 + create mode 100644 testing/resources/gray.jp2 + create mode 100644 testing/resources/hebrew_mirrored.in + create mode 100644 testing/resources/hebrew_mirrored.pdf + create mode 100644 testing/resources/hello_world_2_pages.in + create mode 100644 testing/resources/hello_world_2_pages.pdf + create mode 100644 testing/resources/hello_world_2_pages_shared_resources_dict.in + create mode 100644 testing/resources/hello_world_2_pages_shared_resources_dict.pdf + create mode 100644 testing/resources/hello_world_2_pages_split_streams.in + create mode 100644 testing/resources/hello_world_2_pages_split_streams.pdf + create mode 100644 testing/resources/ink_annot.in + create mode 100644 testing/resources/ink_annot.pdf + create mode 100644 testing/resources/javascript/bug_1098213.in + create mode 100644 testing/resources/javascript/bug_1098213_expected.txt + create mode 100644 testing/resources/javascript/bug_1142688.in + create mode 100644 testing/resources/javascript/bug_1142688_expected.txt + create mode 100644 testing/resources/javascript/bug_1314658.in + create mode 100644 testing/resources/javascript/bug_1314658_expected.txt + create mode 100644 testing/resources/javascript/bug_1335681.in + create mode 100644 testing/resources/javascript/bug_1335681_expected.txt + create mode 100644 testing/resources/javascript/bug_1358075.in + create mode 100644 testing/resources/javascript/bug_1358075_expected.txt + create mode 100644 testing/resources/javascript/bug_1445426.evt + create mode 100644 testing/resources/javascript/bug_1445426.in + create mode 100644 testing/resources/javascript/constructor.js + create mode 100644 testing/resources/javascript/foreground_task.in + create mode 100644 testing/resources/javascript/foreground_task_expected.txt + create mode 100644 testing/resources/javascript/immutable_proto.in + create mode 100644 testing/resources/javascript/immutable_proto_expected.txt + create mode 100644 testing/resources/javascript/named_action.in + create mode 100644 testing/resources/javascript/named_action_expected.txt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1004106.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1026991.evt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1026991.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1039629.evt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1039629.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1040329.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1042915.evt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1042915.pdf + create mode 100644 testing/resources/javascript/xfa_specific/bug_1042915_expected.txt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1043508.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1047914.evt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1047914.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1052651.evt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1052651.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1052786.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1053617.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1054429.evt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1054429.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1060549.evt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1060549.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1069700.evt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1069700.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1069789.evt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1069789.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1082597.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1082597_expected.txt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1109108.evt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1109108.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1109108_expected.txt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1137668.evt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1137668.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1164158.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1248901.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1248901_expected.txt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1312736.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_1312736_expected.txt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1444238.evt + create mode 100644 testing/resources/javascript/xfa_specific/bug_1444238.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_980116.evt + create mode 100644 testing/resources/javascript/xfa_specific/bug_980116.in + create mode 100644 testing/resources/javascript/xfa_specific/bug_980161.evt + create mode 100644 testing/resources/javascript/xfa_specific/bug_980161.in + create mode 100644 testing/resources/javascript/xfa_specific/dump_tree.js + create mode 100644 testing/resources/javascript/xfa_specific/instance_manager.in + create mode 100644 testing/resources/javascript/xfa_specific/instance_manager_expected.txt + create mode 100644 testing/resources/javascript/xfa_specific/mixed_widgets.evt + create mode 100644 testing/resources/javascript/xfa_specific/mixed_widgets.in + create mode 100644 testing/resources/javascript/xfa_specific/popup_menu.evt + create mode 100644 testing/resources/javascript/xfa_specific/popup_menu.in + create mode 100644 testing/resources/javascript/xfa_specific/popup_menu_expected.txt + create mode 100644 testing/resources/javascript/xfa_specific/xfa_exclgroup.in + create mode 100644 testing/resources/javascript/xfa_specific/xfa_exclgroup_expected.txt + create mode 100644 testing/resources/javascript/xfa_specific/xfa_field.in + create mode 100644 testing/resources/javascript/xfa_specific/xfa_field_expected.txt + create mode 100644 testing/resources/javascript/xfa_specific/xfa_form.in + create mode 100644 testing/resources/javascript/xfa_specific/xfa_form_expected.txt + create mode 100644 testing/resources/javascript/xfa_specific/xfa_globalobject.in + create mode 100644 testing/resources/javascript/xfa_specific/xfa_globalobject_expected.txt + create mode 100644 testing/resources/javascript/xfa_specific/xfa_items.in + create mode 100644 testing/resources/javascript/xfa_specific/xfa_items_expected.txt + create mode 100644 testing/resources/javascript/xfa_specific/xfa_template.in + create mode 100644 testing/resources/javascript/xfa_specific/xfa_template_expected.txt + create mode 100644 testing/resources/javascript/xfa_specific/xfa_variables.in + create mode 100644 testing/resources/javascript/xfa_specific/xfa_variables_expected.txt + create mode 100644 testing/resources/jpx_lzw.in + create mode 100644 testing/resources/jpx_lzw.pdf + create mode 100644 testing/resources/line_annot.in + create mode 100644 testing/resources/line_annot.pdf + create mode 100644 testing/resources/marked_content_id.in + create mode 100644 testing/resources/matte.in + create mode 100644 testing/resources/matte.pdf + create mode 100644 testing/resources/mona_lisa.fragment + create mode 100644 testing/resources/multiple_form_types.in + create mode 100644 testing/resources/multiple_form_types.pdf + create mode 100644 testing/resources/named_dests_old_style.in + create mode 100644 testing/resources/named_dests_old_style.pdf + create mode 100644 testing/resources/no_page_count.in + create mode 100644 testing/resources/no_page_count.pdf + create mode 100644 testing/resources/non_hex_file_id.in + create mode 100644 testing/resources/non_hex_file_id.pdf + create mode 100644 testing/resources/page_tree_empty_node.in + create mode 100644 testing/resources/page_tree_empty_node.pdf + create mode 100644 testing/resources/pixel/axial_shading_point_at_border_no_extend_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1015233.in + create mode 100644 testing/resources/pixel/bug_1015233_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1015233_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1072440.in + create mode 100644 testing/resources/pixel/bug_1072440_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1072440_expected_mac.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1072440_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1099446.in + create mode 100644 testing/resources/pixel/bug_1099446_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1116869.in + create mode 100644 testing/resources/pixel/bug_1116869_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1128284.in + create mode 100644 testing/resources/pixel/bug_1128284_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_113910_expected_skia.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_113910_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1161_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1236805.in + create mode 100644 testing/resources/pixel/bug_1236805_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1236_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1237898.in + create mode 100644 testing/resources/pixel/bug_1237898_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1241587.in + create mode 100644 testing/resources/pixel/bug_1241587_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1258634.in + create mode 100644 testing/resources/pixel/bug_1258634_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1258968.in + create mode 100644 testing/resources/pixel/bug_1258968_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1258968_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1271578.in + create mode 100644 testing/resources/pixel/bug_1271578_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1271578_expected_agg_mac.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1286_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1287409.pdf + create mode 100644 testing/resources/pixel/bug_1287409_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1287409_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1288_1_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1288_2_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1296_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1304714.in + create mode 100644 testing/resources/pixel/bug_1304714_expected.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_1308_1_expected_win.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_1308_expected_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_1308_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1330_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1338_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1355.in + create mode 100644 testing/resources/pixel/bug_1355_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1355_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1356149.in + create mode 100644 testing/resources/pixel/bug_1356149_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1372651.evt + create mode 100644 testing/resources/pixel/bug_1372651.in + create mode 100644 testing/resources/pixel/bug_1372651_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1372651_expected_mac.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1372651_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1383708.in + create mode 100644 testing/resources/pixel/bug_1383708_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1383708_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1388_2_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_1388_2_expected_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_1388_2_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1388_3_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_1388_3_expected_mac.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1388_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1395648.in + create mode 100644 testing/resources/pixel/bug_1395648_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1395648_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1396266.in + create mode 100644 testing/resources/pixel/bug_1396266_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1396266_expected_skia.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_1402_expected_mac.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1402_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1430333.in + create mode 100644 testing/resources/pixel/bug_1430333_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1430333_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1469.in + create mode 100644 testing/resources/pixel/bug_1469_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1491.in + create mode 100644 testing/resources/pixel/bug_1491_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1491_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1519.in + create mode 100644 testing/resources/pixel/bug_1519_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1546.in + create mode 100644 testing/resources/pixel/bug_1546_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1571.in + create mode 100644 testing/resources/pixel/bug_1571_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1638.in + create mode 100644 testing/resources/pixel/bug_1638_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1638_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1639_1.in + create mode 100644 testing/resources/pixel/bug_1639_1_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1639_1_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1693.in + create mode 100644 testing/resources/pixel/bug_1693_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1723.in + create mode 100644 testing/resources/pixel/bug_1723_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1733.in + create mode 100644 testing/resources/pixel/bug_1733_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1733_expected.pdf.1.png + create mode 100644 testing/resources/pixel/bug_1746.in + create mode 100644 testing/resources/pixel/bug_1746_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1746_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1750.in + create mode 100644 testing/resources/pixel/bug_1750_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1752.in + create mode 100644 testing/resources/pixel/bug_1752_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1752_expected_mac.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1752_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1772.in + create mode 100644 testing/resources/pixel/bug_1772_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1772_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1774.in + create mode 100644 testing/resources/pixel/bug_1774_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1774_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1822.in + create mode 100644 testing/resources/pixel/bug_1822_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1822_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1845.in + create mode 100644 testing/resources/pixel/bug_1845_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1847.in + create mode 100644 testing/resources/pixel/bug_1847_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1847_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1883.in + create mode 100644 testing/resources/pixel/bug_1883_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1883_expected.pdf.1.png + create mode 100644 testing/resources/pixel/bug_1883_expected.pdf.2.png + create mode 100644 testing/resources/pixel/bug_1883_expected.pdf.3.png + create mode 100644 testing/resources/pixel/bug_1883_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1883_expected_skia.pdf.1.png + create mode 100644 testing/resources/pixel/bug_1883_expected_skia.pdf.2.png + create mode 100644 testing/resources/pixel/bug_1883_expected_skia.pdf.3.png + create mode 100644 testing/resources/pixel/bug_1922.in + create mode 100644 testing/resources/pixel/bug_1922_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1922_expected_agg_mac.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1949.in + create mode 100644 testing/resources/pixel/bug_1949_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1949_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1963.in + create mode 100644 testing/resources/pixel/bug_1963_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1963_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1966.in + create mode 100644 testing/resources/pixel/bug_1966_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1966_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1972_1.in + create mode 100644 testing/resources/pixel/bug_1972_1_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1972_2.in + create mode 100644 testing/resources/pixel/bug_1972_2_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1972_3.in + create mode 100644 testing/resources/pixel/bug_1972_3_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1973.in + create mode 100644 testing/resources/pixel/bug_1973_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1976.in + create mode 100644 testing/resources/pixel/bug_1976_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1983.in + create mode 100644 testing/resources/pixel/bug_1983_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1983_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1986.in + create mode 100644 testing/resources/pixel/bug_1986_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1995.in + create mode 100644 testing/resources/pixel/bug_1995_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_1995_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_2001.pdf + create mode 100644 testing/resources/pixel/bug_2001_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_237527_1.in + create mode 100644 testing/resources/pixel/bug_237527_1_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_237527_1_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_237527_2.in + create mode 100644 testing/resources/pixel/bug_237527_2_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_451366_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_491_invisible_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_491_unspecified_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_491_visible_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_524043_1_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_524043_1_expected_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_524043_1_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_524043_2_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_524043_2_expected_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_524043_2_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_524043_3_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_524043_3_expected_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_524043_3_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_524043_4_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_524043_4_expected_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_524043_4_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_524043_5_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_524043_5_expected_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_524043_5_expected_win.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_524043_6_expected_mac.pdf.0.png + create mode 100644 testing/resources/pixel/bug_524043_7_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_524043_7_expected_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_524043_7_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_528103_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_528103_expected_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_528103_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_543018_1_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_543018_1_expected_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_543018_1_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_543018_2_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_543018_2_expected_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_543018_2_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_551258_1_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_551258_1_expected_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_551258_1_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_585_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_601362_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_632_expected_skia.pdf.1.png + create mode 100644 testing/resources/pixel/bug_660850_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_665467_expected_skia.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_665467_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_714187_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_725389.in + create mode 100644 testing/resources/pixel/bug_725389_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_725389_expected_mac.pdf.0.png + create mode 100644 testing/resources/pixel/bug_725389_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_725389_expected_skia_mac.pdf.0.png + create mode 100644 testing/resources/pixel/bug_733528_expected_skia.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_733528_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_736695_1_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_736695_2_expected_skia.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_736695_2_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_736695_3_expected_skia.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_736695_3_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_736695_4_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_736703.in + create mode 100644 testing/resources/pixel/bug_736703_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_820345_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_820345_expected_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_820345_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_842_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_843_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_846.in + create mode 100644 testing/resources/pixel/bug_846_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_846_expected_mac.pdf.0.png + create mode 100644 testing/resources/pixel/bug_846_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_846_expected_skia_mac.pdf.0.png + create mode 100644 testing/resources/pixel/bug_846_expected_skia_win.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_909762_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/bug_925736_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_925736_expected_mac.pdf.0.png + create mode 100644 testing/resources/pixel/bug_966263.in + create mode 100644 testing/resources/pixel/bug_966263_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_972999.in + create mode 100644 testing/resources/pixel/bug_972999_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_972999_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/bug_983289.in + create mode 100644 testing/resources/pixel/bug_983289_expected.pdf.0.png + create mode 100644 testing/resources/pixel/bug_984811_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/bug_984811_expected_mac.pdf.0.png + create mode 100644 testing/resources/pixel/bug_986108.in + create mode 100644 testing/resources/pixel/bug_986108_expected.pdf.0.png + create mode 100644 testing/resources/pixel/checkbox_radiobutton.evt + create mode 100644 testing/resources/pixel/checkbox_radiobutton.fragment + create mode 100644 testing/resources/pixel/checkbox_radiobutton.in + create mode 100644 testing/resources/pixel/checkbox_radiobutton_expected.pdf.0.png + create mode 100644 testing/resources/pixel/checkbox_radiobutton_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/checkbox_radiobutton_hide.in + create mode 100644 testing/resources/pixel/checkbox_radiobutton_hide_expected.pdf.0.png + create mode 100644 testing/resources/pixel/checkbox_radiobutton_hide_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/checkbox_radiobutton_reset.in + create mode 100644 testing/resources/pixel/checkbox_radiobutton_reset_expected.pdf.0.png + create mode 100644 testing/resources/pixel/checkbox_radiobutton_reset_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/combobox_form.evt + create mode 100644 testing/resources/pixel/combobox_form.in + create mode 100644 testing/resources/pixel/combobox_form_expected.pdf.0.png + create mode 100644 testing/resources/pixel/combobox_form_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/font_size_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/font_size_expected_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/font_size_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/generation_numbers1_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/generation_numbers1_expected_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/generation_numbers1_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/generation_numbers2_expected_agg_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/generation_numbers2_expected_mac.pdf.0.png + delete mode 100644 testing/resources/pixel/generation_numbers2_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/image_transformer_other.in + create mode 100644 testing/resources/pixel/image_transformer_other_expected.pdf.0.png + create mode 100644 testing/resources/pixel/image_transformer_other_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/jpxdecode.in + create mode 100644 testing/resources/pixel/jpxdecode_expected.pdf.0.png + create mode 100644 testing/resources/pixel/jpxdecode_with_mismatch_colorspace.in + create mode 100644 testing/resources/pixel/jpxdecode_with_mismatch_colorspace_expected.pdf.0.png + create mode 100644 testing/resources/pixel/jpxdecode_without_bitspercomponent.in + create mode 100644 testing/resources/pixel/jpxdecode_without_bitspercomponent_expected.pdf.0.png + create mode 100644 testing/resources/pixel/jpxdecode_without_colorspace.in + create mode 100644 testing/resources/pixel/jpxdecode_without_colorspace_expected.pdf.0.png + create mode 100644 testing/resources/pixel/jpxdecode_without_smaskindata.in + create mode 100644 testing/resources/pixel/jpxdecode_without_smaskindata_expected.pdf.0.png + create mode 100644 testing/resources/pixel/long_dashed_line.in + create mode 100644 testing/resources/pixel/long_dashed_line_expected.pdf.0.png + create mode 100644 testing/resources/pixel/long_dashed_line_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/matte_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/password.evt + create mode 100644 testing/resources/pixel/password.in + create mode 100644 testing/resources/pixel/password_expected.pdf.0.png + create mode 100644 testing/resources/pixel/password_expected_mac.pdf.0.png + create mode 100644 testing/resources/pixel/password_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/radial_shading_point_at_border_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/radial_shading_point_at_border_no_extend_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/radial_shading_point_at_center_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/reset_button.evt + create mode 100644 testing/resources/pixel/reset_button.in + create mode 100644 testing/resources/pixel/reset_button_expected.pdf.0.png + create mode 100644 testing/resources/pixel/scrollable_widgets1.evt + create mode 100644 testing/resources/pixel/scrollable_widgets1.in + create mode 100644 testing/resources/pixel/scrollable_widgets1_expected.pdf.0.png + create mode 100644 testing/resources/pixel/scrollable_widgets1_expected_mac.pdf.0.png + create mode 100644 testing/resources/pixel/scrollable_widgets1_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/scrollable_widgets2.evt + create mode 100644 testing/resources/pixel/scrollable_widgets2.in + create mode 100644 testing/resources/pixel/scrollable_widgets2_expected.pdf.0.png + create mode 100644 testing/resources/pixel/scrollable_widgets2_expected_mac.pdf.0.png + create mode 100644 testing/resources/pixel/scrollable_widgets2_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/text_form_custom_font.in + create mode 100644 testing/resources/pixel/text_form_custom_font_expected.pdf.0.png + create mode 100644 testing/resources/pixel/text_form_custom_font_expected_mac.pdf.0.png + create mode 100644 testing/resources/pixel/text_form_custom_font_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/use_symbolneu/bug_1449.in + create mode 100644 testing/resources/pixel/use_symbolneu/bug_1449_expected.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/barcode_test_expected_skia.pdf.0.png + delete mode 100644 testing/resources/pixel/xfa_specific/barcode_test_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/bug_1282_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/bug_1286970.in + create mode 100644 testing/resources/pixel/xfa_specific/bug_1286970_expected.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/bug_1337.in + create mode 100644 testing/resources/pixel/xfa_specific/bug_1337_expected.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/bug_983137_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_skia_mac.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_skia.pdf.0.png + delete mode 100644 testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_skia.pdf.0.png + delete mode 100644 testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/resolve_nodes_0_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/standard_symbols_expected.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/standard_symbols_expected.pdf.1.png + create mode 100644 testing/resources/pixel/xfa_specific/static_list_box_caption_expected_skia.pdf.0.png + delete mode 100644 testing/resources/pixel/xfa_specific/static_list_box_caption_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia.pdf.1.png + delete mode 100644 testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.0.png + delete mode 100644 testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.1.png + create mode 100644 testing/resources/pixel/xfa_specific/use_ahem/README.md + create mode 100644 testing/resources/pixel/xfa_specific/use_ahem/bug_997412.in + create mode 100644 testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected.pdf.1.png + create mode 100644 testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected_skia.pdf.1.png + create mode 100644 testing/resources/pixel/xfa_specific/use_ahem/xfa_example_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_bmp_image_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_gif_image_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_jpg_image_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_node_caption_expected_mac.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_node_caption_expected_mac.pdf.1.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia.pdf.1.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_mac.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_mac.pdf.1.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.1.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_node_caption_expected_win.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_node_caption_expected_win.pdf.1.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_png_image_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_rectangle_node_expected_skia.pdf.0.png + delete mode 100644 testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image.in + delete mode 100644 testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image_expected.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_tiff_image_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_tiff_lzw_image_expected_skia.pdf.0.png + create mode 100644 testing/resources/pixel/xfa_specific/xfa_tiff_packbits_image_expected_skia.pdf.0.png + create mode 100644 testing/resources/polygon_annot.in + create mode 100644 testing/resources/polygon_annot.pdf + create mode 100644 testing/resources/redact_annot.in + create mode 100644 testing/resources/redact_annot.pdf + create mode 100644 testing/resources/rotated_image.in + create mode 100644 testing/resources/rotated_image.pdf + create mode 100644 testing/resources/signature_no_sub_filter.in + create mode 100644 testing/resources/signature_no_sub_filter.pdf + create mode 100644 testing/resources/signature_reason.in + create mode 100644 testing/resources/signature_reason.pdf + create mode 100644 testing/resources/tagged_actual_text.in + create mode 100644 testing/resources/tagged_actual_text.pdf + create mode 100644 testing/resources/tagged_marked_content.in + create mode 100644 testing/resources/tagged_marked_content.pdf + create mode 100644 testing/resources/tagged_mcr_objr.in + create mode 100644 testing/resources/tagged_mcr_objr.pdf + create mode 100644 testing/resources/tagged_nested.in + create mode 100644 testing/resources/tagged_nested.pdf + create mode 100644 testing/resources/tagged_table.in + create mode 100644 testing/resources/tagged_table.pdf + create mode 100644 testing/resources/tagged_table_bad_elem.in + create mode 100644 testing/resources/tagged_table_bad_elem.pdf + create mode 100644 testing/resources/trailer_end_trailing_space.in + create mode 100644 testing/resources/trailer_end_trailing_space.pdf + create mode 100644 testing/resources/two_signatures.in + create mode 100644 testing/resources/two_signatures.pdf + create mode 100644 testing/resources/uri_action_nonascii.in + create mode 100644 testing/resources/uri_action_nonascii.pdf + create mode 100644 testing/resources/viewer_pref_types.in + create mode 100644 testing/resources/viewer_pref_types.pdf + create mode 100644 testing/resources/xfa/xfa_break_before_after.in + create mode 100644 testing/resources/xfa/xfa_break_before_after.pdf + create mode 100644 testing/scoped_set_tz.cpp + create mode 100644 testing/scoped_set_tz.h + create mode 100644 testing/test_fonts.cpp + create mode 100644 testing/test_fonts.h + delete mode 100644 testing/test_support.cpp + delete mode 100644 testing/tools/.style.yapf + delete mode 100644 testing/tools/gold.py + create mode 100755 testing/tools/libcxx_check.py + create mode 100644 testing/tools/pdfium_root.py + create mode 100644 testing/tools/skia_gold/__init__.py + create mode 100644 testing/tools/skia_gold/pdfium_skia_gold_properties.py + create mode 100644 testing/tools/skia_gold/pdfium_skia_gold_session.py + create mode 100644 testing/tools/skia_gold/pdfium_skia_gold_session_manager.py + create mode 100644 testing/tools/skia_gold/skia_gold.py + create mode 100755 testing/tools/strip_jp2_comments.py + create mode 100644 testing/v8_test_environment.cpp + create mode 100644 testing/v8_test_environment.h + create mode 100644 testing/xfa_test_environment.cpp + create mode 100644 testing/xfa_test_environment.h + delete mode 100644 testing/xfa_unit_test_support.cpp + delete mode 100644 testing/xfa_unit_test_support.h + create mode 100644 third_party/NotoSansCJK/LICENSE + create mode 100644 third_party/NotoSansCJK/NotoSansSC-Regular.subset.otf + create mode 100644 third_party/NotoSansCJK/README.pdfium + create mode 100644 third_party/abseil-cpp/absl/base/attributes.h + create mode 100644 third_party/abseil-cpp/absl/base/config.h + create mode 100644 third_party/abseil-cpp/absl/base/internal/identity.h + create mode 100644 third_party/abseil-cpp/absl/base/internal/inline_variable.h + create mode 100644 third_party/abseil-cpp/absl/base/internal/invoke.h + create mode 100644 third_party/abseil-cpp/absl/base/options.h + create mode 100644 third_party/abseil-cpp/absl/base/policy_checks.h + create mode 100644 third_party/abseil-cpp/absl/meta/type_traits.h + create mode 100644 third_party/abseil-cpp/absl/types/optional.h + create mode 100644 third_party/abseil-cpp/absl/types/variant.h + create mode 100644 third_party/abseil-cpp/absl/utility/utility.h + create mode 100644 third_party/agg23/0008-namespace.patch + create mode 100644 third_party/agg23/0009-infinite-loop.patch + create mode 100644 third_party/agg23/0010-math.patch + create mode 100644 third_party/agg23/0011-path-storage-move-ctor.patch + create mode 100644 third_party/agg23/0012-infinite-loop.patch + create mode 100644 third_party/agg23/0013-cxx20.patch + create mode 100644 third_party/agg23/0014-ubsan-render-line.patch + delete mode 100644 third_party/base/allocator/partition_allocator/address_space_randomization.cc + delete mode 100644 third_party/base/allocator/partition_allocator/address_space_randomization.h + delete mode 100644 third_party/base/allocator/partition_allocator/oom.h + delete mode 100644 third_party/base/allocator/partition_allocator/oom_callback.cc + delete mode 100644 third_party/base/allocator/partition_allocator/oom_callback.h + delete mode 100644 third_party/base/allocator/partition_allocator/page_allocator.cc + delete mode 100644 third_party/base/allocator/partition_allocator/page_allocator.h + delete mode 100644 third_party/base/allocator/partition_allocator/page_allocator_constants.h + delete mode 100644 third_party/base/allocator/partition_allocator/page_allocator_internal.h + delete mode 100644 third_party/base/allocator/partition_allocator/page_allocator_internals_posix.h + delete mode 100644 third_party/base/allocator/partition_allocator/page_allocator_internals_win.h + delete mode 100644 third_party/base/allocator/partition_allocator/partition_alloc.cc + delete mode 100644 third_party/base/allocator/partition_allocator/partition_alloc.h + delete mode 100644 third_party/base/allocator/partition_allocator/partition_alloc_constants.h + delete mode 100644 third_party/base/allocator/partition_allocator/partition_bucket.cc + delete mode 100644 third_party/base/allocator/partition_allocator/partition_bucket.h + delete mode 100644 third_party/base/allocator/partition_allocator/partition_cookie.h + delete mode 100644 third_party/base/allocator/partition_allocator/partition_direct_map_extent.h + delete mode 100644 third_party/base/allocator/partition_allocator/partition_freelist_entry.h + delete mode 100644 third_party/base/allocator/partition_allocator/partition_oom.cc + delete mode 100644 third_party/base/allocator/partition_allocator/partition_oom.h + delete mode 100644 third_party/base/allocator/partition_allocator/partition_page.cc + delete mode 100644 third_party/base/allocator/partition_allocator/partition_page.h + delete mode 100644 third_party/base/allocator/partition_allocator/partition_root_base.cc + delete mode 100644 third_party/base/allocator/partition_allocator/partition_root_base.h + delete mode 100644 third_party/base/allocator/partition_allocator/random.cc + delete mode 100644 third_party/base/allocator/partition_allocator/random.h + delete mode 100644 third_party/base/allocator/partition_allocator/spin_lock.cc + delete mode 100644 third_party/base/allocator/partition_allocator/spin_lock.h + create mode 100644 third_party/base/check.h + create mode 100644 third_party/base/check_op.h + create mode 100644 third_party/base/component_export.h + create mode 100644 third_party/base/containers/adapters.h + create mode 100644 third_party/base/containers/contains.h + create mode 100644 third_party/base/cxx17_backports.h + create mode 100644 third_party/base/immediate_crash.h + delete mode 100644 third_party/base/logging.h + create mode 100644 third_party/base/memory/aligned_memory.cc + create mode 100644 third_party/base/memory/aligned_memory.h + create mode 100644 third_party/base/notreached.h + create mode 100644 third_party/base/numerics/README.pdfium + create mode 100644 third_party/base/numerics/checked_math.h + create mode 100644 third_party/base/numerics/checked_math_impl.h + create mode 100644 third_party/base/numerics/clamped_math.h + create mode 100644 third_party/base/numerics/clamped_math_impl.h + create mode 100644 third_party/base/numerics/safe_math_arm_impl.h + create mode 100644 third_party/base/numerics/safe_math_clang_gcc_impl.h + delete mode 100644 third_party/base/numerics/safe_math_impl.h + create mode 100644 third_party/base/numerics/safe_math_shared_impl.h + delete mode 100644 third_party/base/optional.h + delete mode 100644 third_party/base/stl_util.h + create mode 100644 third_party/base/win/scoped_select_object.h + delete mode 100644 third_party/eu-strip/README.pdfium + delete mode 100755 third_party/eu-strip/bin/eu-strip + create mode 100644 third_party/fuchsia-sdk/DIR_METADATA + create mode 100644 third_party/fuchsia-sdk/README.chromium + create mode 100644 third_party/googletest/custom/gtest/internal/custom/gtest.h + create mode 100644 third_party/googletest/custom/gtest/internal/custom/gtest_port_wrapper.cc + create mode 100644 third_party/googletest/custom/gtest/internal/custom/pdfium_custom_temp_dir.cc + create mode 100644 third_party/googletest/custom/gtest/internal/custom/pdfium_custom_temp_dir.h + delete mode 100644 third_party/lcms/0002-old-performance-fix.patch + delete mode 100644 third_party/lcms/0005-old-fix-e-with-tilde.patch + delete mode 100644 third_party/lcms/0008-infinite-loop-GrowNamedColorList.patch + delete mode 100644 third_party/lcms/0019-utf8.patch + delete mode 100644 third_party/lcms/0026-more-unsupported-characters.patch + delete mode 100644 third_party/lcms/0031-wrong-tag-element-count.patch + delete mode 100644 third_party/lcms/0032-cgats-allocation.patch + create mode 100644 third_party/lcms/0034-dead-code.patch + rename third_party/{libopenjpeg20 => libopenjpeg}/0003-dwt-decode.patch (52%) + rename third_party/{libopenjpeg20 => libopenjpeg}/0005-jp2_apply_pclr.patch (71%) + rename third_party/{libopenjpeg20 => libopenjpeg}/0006-tcd_init_tile.patch (66%) + rename third_party/{libopenjpeg20 => libopenjpeg}/0007-jp2_read_cmap.patch (51%) + rename third_party/{libopenjpeg20 => libopenjpeg}/0009-opj_pi_next.patch (50%) + rename third_party/{libopenjpeg20 => libopenjpeg}/0011-j2k_update_image_data.patch (63%) + rename third_party/{libopenjpeg20 => libopenjpeg}/0012-mct_sse.patch (58%) + rename third_party/{libopenjpeg20 => libopenjpeg}/0014-opj_jp2_read_ihdr_leak.patch (60%) + rename third_party/{libopenjpeg20 => libopenjpeg}/0015-read_SPCod_SPCoc_overflow.patch (50%) + rename third_party/{libopenjpeg20 => libopenjpeg}/0016-read_SQcd_SQcc_overflow.patch (54%) + rename third_party/{libopenjpeg20 => libopenjpeg}/0019-tcd_init_tile.patch (73%) + rename third_party/{libopenjpeg20 => libopenjpeg}/0022-jp2_apply_pclr_overflow.patch (81%) + rename third_party/{libopenjpeg20 => libopenjpeg}/0023-opj_j2k_read_mct_records.patch (58%) + rename third_party/{libopenjpeg20 => libopenjpeg}/0025-opj_j2k_add_mct_null_data.patch (70%) + rename third_party/{libopenjpeg20 => libopenjpeg}/0026-use_opj_uint_ceildiv.patch (74%) + create mode 100644 third_party/libopenjpeg/0034-opj_malloc.patch + rename third_party/{libopenjpeg20 => libopenjpeg}/0035-opj_image_data_free.patch (54%) + create mode 100644 third_party/libopenjpeg/0039-opj_mqc_renorme.patch + create mode 100644 third_party/libopenjpeg/0041-remove_opj_clock.patch + create mode 100644 third_party/libopenjpeg/0042-popcnt-windows-arm64.patch + create mode 100644 third_party/libopenjpeg/0043-mel_init.patch + create mode 100644 third_party/libopenjpeg/0044-opj_t1_allocate_buffers.patch + create mode 100644 third_party/libopenjpeg/0045-openjp2-j2k-replace-sprintf-calls-with-snprintf.patch + rename third_party/{libopenjpeg20 => libopenjpeg}/README.pdfium (70%) + rename third_party/{libopenjpeg20 => libopenjpeg}/bio.c (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/bio.h (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/cio.c (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/cio.h (99%) + rename third_party/{libopenjpeg20 => libopenjpeg}/dwt.c (62%) + rename third_party/{libopenjpeg20 => libopenjpeg}/dwt.h (87%) + rename third_party/{libopenjpeg20 => libopenjpeg}/event.c (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/event.h (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/function_list.c (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/function_list.h (100%) + create mode 100644 third_party/libopenjpeg/ht_dec.c + rename third_party/{libopenjpeg20 => libopenjpeg}/image.c (99%) + rename third_party/{libopenjpeg20 => libopenjpeg}/image.h (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/invert.c (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/invert.h (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/j2k.c (89%) + rename third_party/{libopenjpeg20 => libopenjpeg}/j2k.h (95%) + rename third_party/{libopenjpeg20 => libopenjpeg}/jp2.c (98%) + rename third_party/{libopenjpeg20 => libopenjpeg}/jp2.h (95%) + rename third_party/{libopenjpeg20 => libopenjpeg}/mct.c (65%) + rename third_party/{libopenjpeg20 => libopenjpeg}/mct.h (97%) + rename third_party/{libopenjpeg20 => libopenjpeg}/mqc.c (92%) + rename third_party/{libopenjpeg20 => libopenjpeg}/mqc.h (98%) + rename third_party/{libopenjpeg20 => libopenjpeg}/mqc_inl.h (74%) + rename third_party/{libopenjpeg20 => libopenjpeg}/openjpeg.c (90%) + rename third_party/{libopenjpeg20 => libopenjpeg}/openjpeg.h (91%) + rename third_party/{libopenjpeg20 => libopenjpeg}/opj_clock.c (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/opj_clock.h (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/opj_codec.h (96%) + rename third_party/{libopenjpeg20 => libopenjpeg}/opj_common.h (90%) + rename third_party/{libopenjpeg20 => libopenjpeg}/opj_config.h (80%) + rename third_party/{libopenjpeg20 => libopenjpeg}/opj_config_private.h (92%) + rename third_party/{libopenjpeg20 => libopenjpeg}/opj_includes.h (99%) + rename third_party/{libopenjpeg20 => libopenjpeg}/opj_intmath.h (86%) + rename third_party/{libopenjpeg20 => libopenjpeg}/opj_inttypes.h (100%) + create mode 100644 third_party/libopenjpeg/opj_malloc.cc + create mode 100644 third_party/libopenjpeg/opj_malloc.h + rename third_party/{libopenjpeg20 => libopenjpeg}/opj_stdint.h (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/pi.c (83%) + rename third_party/{libopenjpeg20 => libopenjpeg}/pi.h (90%) + rename third_party/{libopenjpeg20 => libopenjpeg}/sparse_array.c (98%) + rename third_party/{libopenjpeg20 => libopenjpeg}/sparse_array.h (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/t1.c (76%) + rename third_party/{libopenjpeg20 => libopenjpeg}/t1.h (98%) + rename third_party/{libopenjpeg20 => libopenjpeg}/t1_generate_luts.c (96%) + create mode 100644 third_party/libopenjpeg/t1_ht_luts.h + rename third_party/{libopenjpeg20 => libopenjpeg}/t1_luts.h (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/t2.c (87%) + rename third_party/{libopenjpeg20 => libopenjpeg}/t2.h (97%) + rename third_party/{libopenjpeg20 => libopenjpeg}/tcd.c (96%) + rename third_party/{libopenjpeg20 => libopenjpeg}/tcd.h (91%) + rename third_party/{libopenjpeg20 => libopenjpeg}/tgt.c (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/tgt.h (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/thread.c (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/thread.h (100%) + rename third_party/{libopenjpeg20 => libopenjpeg}/tls_keys.h (100%) + delete mode 100644 third_party/libopenjpeg20/0034-opj_malloc.patch + delete mode 100644 third_party/libopenjpeg20/0036-opj_j2k_update_image_dimensions.patch + delete mode 100644 third_party/libopenjpeg20/0037-tcd_init_tile.patch + delete mode 100644 third_party/libopenjpeg20/opj_malloc.h + delete mode 100644 third_party/libpng16/0000-build-config.patch + delete mode 100644 third_party/libpng16/0002-static-png-gt.patch + delete mode 100644 third_party/libpng16/LICENSE + delete mode 100644 third_party/libpng16/README.pdfium + delete mode 100644 third_party/libpng16/arm/arm_init.c + delete mode 100644 third_party/libpng16/arm/filter_neon.S + delete mode 100644 third_party/libpng16/arm/filter_neon_intrinsics.c + delete mode 100644 third_party/libpng16/arm/palette_neon_intrinsics.c + delete mode 100644 third_party/libpng16/intel/filter_sse2_intrinsics.c + delete mode 100644 third_party/libpng16/intel/intel_init.c + delete mode 100644 third_party/libpng16/png.c + delete mode 100644 third_party/libpng16/png.h + delete mode 100644 third_party/libpng16/pngconf.h + delete mode 100644 third_party/libpng16/pngdebug.h + delete mode 100644 third_party/libpng16/pngerror.c + delete mode 100644 third_party/libpng16/pngget.c + delete mode 100644 third_party/libpng16/pnginfo.h + delete mode 100644 third_party/libpng16/pnglibconf.h + delete mode 100644 third_party/libpng16/pngmem.c + delete mode 100644 third_party/libpng16/pngpread.c + delete mode 100644 third_party/libpng16/pngprefix.h + delete mode 100644 third_party/libpng16/pngpriv.h + delete mode 100644 third_party/libpng16/pngread.c + delete mode 100644 third_party/libpng16/pngrio.c + delete mode 100644 third_party/libpng16/pngrtran.c + delete mode 100644 third_party/libpng16/pngrutil.c + delete mode 100644 third_party/libpng16/pngset.c + delete mode 100644 third_party/libpng16/pngstruct.h + delete mode 100644 third_party/libpng16/pngtrans.c + delete mode 100644 third_party/libpng16/pngwio.c + delete mode 100644 third_party/libpng16/pngwrite.c + delete mode 100644 third_party/libpng16/pngwtran.c + delete mode 100644 third_party/libpng16/pngwutil.c + delete mode 100644 third_party/libtiff/0027-build-config.patch + create mode 100644 third_party/libtiff/0033-avail-out-overflow.patch + create mode 100644 third_party/libtiff/tif_hash_set.c + create mode 100644 third_party/libtiff/tif_hash_set.h + delete mode 100644 third_party/libtiff/tif_ojpeg.c + delete mode 100644 third_party/libtiff/tif_zip.c + create mode 100644 third_party/ninja/README.pdfium + delete mode 100644 third_party/yasm/BUILD.gn + delete mode 100644 third_party/yasm/CHROMIUM.diff + delete mode 100644 third_party/yasm/README.pdfium + delete mode 100644 third_party/yasm/run_yasm.py + delete mode 100644 third_party/yasm/source/config/Makefile + delete mode 100644 third_party/yasm/source/config/android/config.h + delete mode 100644 third_party/yasm/source/config/android/libyasm-stdint.h + delete mode 100644 third_party/yasm/source/config/ios/config.h + delete mode 100644 third_party/yasm/source/config/ios/libyasm-stdint.h + delete mode 100644 third_party/yasm/source/config/linux/config.h + delete mode 100644 third_party/yasm/source/config/linux/libyasm-stdint.h + delete mode 100644 third_party/yasm/source/config/mac/config.h + delete mode 100644 third_party/yasm/source/config/mac/libyasm-stdint.h + delete mode 100644 third_party/yasm/source/config/openbsd/config.h + delete mode 100644 third_party/yasm/source/config/openbsd/libyasm-stdint.h + delete mode 100644 third_party/yasm/source/config/win/config.h + delete mode 100644 third_party/yasm/source/config/win/libyasm-stdint.h + delete mode 100644 third_party/yasm/yasm_assemble.gni + create mode 100644 tools/android/md5sum/BUILD.gn + rename tools/msan/{blacklist.txt => ignorelist.txt} (100%) + rename tools/ubsan/{blacklist.txt => ignorelist.txt} (96%) + rename tools/ubsan/{security_blacklist.txt => security_ignorelist.txt} (80%) + rename tools/ubsan/{vptr_blacklist.txt => vptr_ignorelist.txt} (65%) + create mode 100644 xfa/fde/cfde_textout_unittest.cpp + delete mode 100644 xfa/fgas/BUILD.gn + create mode 100644 xfa/fgas/crt/BUILD.gn + create mode 100644 xfa/fgas/font/BUILD.gn + create mode 100644 xfa/fgas/font/cfgas_gemodule.cpp + create mode 100644 xfa/fgas/font/cfgas_gemodule.h + delete mode 100644 xfa/fgas/font/cfx_fontsourceenum_file.cpp + delete mode 100644 xfa/fgas/font/cfx_fontsourceenum_file.h + create mode 100644 xfa/fgas/font/fgas_fontutils_unittest.cpp + create mode 100644 xfa/fgas/graphics/BUILD.gn + create mode 100644 xfa/fgas/graphics/cfgas_gecolor.cpp + create mode 100644 xfa/fgas/graphics/cfgas_gecolor.h + rename xfa/{fxgraphics/cxfa_graphics.cpp => fgas/graphics/cfgas_gegraphics.cpp} (59%) + create mode 100644 xfa/fgas/graphics/cfgas_gegraphics.h + create mode 100644 xfa/fgas/graphics/cfgas_gepath.cpp + rename xfa/{fxgraphics/cxfa_gepath.h => fgas/graphics/cfgas_gepath.h} (67%) + create mode 100644 xfa/fgas/graphics/cfgas_gepattern.cpp + create mode 100644 xfa/fgas/graphics/cfgas_gepattern.h + create mode 100644 xfa/fgas/graphics/cfgas_geshading.cpp + create mode 100644 xfa/fgas/graphics/cfgas_geshading.h + rename xfa/fgas/layout/{cfx_break.cpp => cfgas_break.cpp} (54%) + rename xfa/fgas/layout/{cfx_break.h => cfgas_break.h} (57%) + create mode 100644 xfa/fgas/layout/cfgas_breakline.cpp + rename xfa/fgas/layout/{cfx_breakline.h => cfgas_breakline.h} (52%) + create mode 100644 xfa/fgas/layout/cfgas_breakpiece.cpp + create mode 100644 xfa/fgas/layout/cfgas_breakpiece.h + rename xfa/fgas/layout/{cfx_char.cpp => cfgas_char.cpp} (84%) + rename xfa/fgas/layout/{cfx_char.h => cfgas_char.h} (56%) + rename xfa/fgas/layout/{cfx_linkuserdata.cpp => cfgas_linkuserdata.cpp} (52%) + create mode 100644 xfa/fgas/layout/cfgas_linkuserdata.h + rename xfa/fgas/layout/{cfx_rtfbreak.cpp => cfgas_rtfbreak.cpp} (59%) + create mode 100644 xfa/fgas/layout/cfgas_rtfbreak.h + rename xfa/fgas/layout/{cfx_rtfbreak_unittest.cpp => cfgas_rtfbreak_unittest.cpp} (51%) + rename xfa/fgas/layout/{cfx_textpiece.cpp => cfgas_textpiece.cpp} (55%) + rename xfa/fgas/layout/{cfx_textpiece.h => cfgas_textpiece.h} (51%) + rename xfa/fgas/layout/{cfx_textuserdata.cpp => cfgas_textuserdata.cpp} (61%) + create mode 100644 xfa/fgas/layout/cfgas_textuserdata.h + rename xfa/fgas/layout/{cfx_txtbreak.cpp => cfgas_txtbreak.cpp} (54%) + create mode 100644 xfa/fgas/layout/cfgas_txtbreak.h + rename xfa/fgas/layout/{cfx_txtbreak_unittest.cpp => cfgas_txtbreak_unittest.cpp} (56%) + delete mode 100644 xfa/fgas/layout/cfx_breakline.cpp + delete mode 100644 xfa/fgas/layout/cfx_breakpiece.cpp + delete mode 100644 xfa/fgas/layout/cfx_breakpiece.h + delete mode 100644 xfa/fgas/layout/cfx_linkuserdata.h + delete mode 100644 xfa/fgas/layout/cfx_rtfbreak.h + delete mode 100644 xfa/fgas/layout/cfx_textuserdata.h + delete mode 100644 xfa/fgas/layout/cfx_txtbreak.h + rename xfa/fgas/layout/{fx_arabic.cpp => fgas_arabic.cpp} (85%) + create mode 100644 xfa/fgas/layout/fgas_arabic.h + rename xfa/fgas/layout/{fx_linebreak.cpp => fgas_linebreak.cpp} (97%) + rename xfa/fgas/layout/{fx_linebreak.h => fgas_linebreak.h} (73%) + delete mode 100644 xfa/fgas/layout/fx_arabic.h + create mode 100644 xfa/fwl/DEPS + delete mode 100644 xfa/fwl/cfwl_eventtarget.cpp + delete mode 100644 xfa/fwl/cfwl_eventtarget.h + delete mode 100644 xfa/fwl/cfwl_listitem.cpp + delete mode 100644 xfa/fwl/cfwl_listitem.h + create mode 100644 xfa/fwl/cfwl_themebackground.cpp + create mode 100644 xfa/fwl/cfwl_themetext.cpp + delete mode 100644 xfa/fwl/cfwl_widgetproperties.cpp + delete mode 100644 xfa/fwl/cfwl_widgetproperties.h + create mode 100644 xfa/fwl/ifwl_themeprovider.cpp + delete mode 100644 xfa/fxfa/cxfa_loadercontext.cpp + delete mode 100644 xfa/fxfa/cxfa_loadercontext.h + delete mode 100644 xfa/fxfa/cxfa_pieceline.cpp + delete mode 100644 xfa/fxfa/cxfa_pieceline.h + delete mode 100644 xfa/fxfa/cxfa_rendercontext.cpp + delete mode 100644 xfa/fxfa/cxfa_rendercontext.h + delete mode 100644 xfa/fxfa/cxfa_textparsecontext.cpp + delete mode 100644 xfa/fxfa/cxfa_textparsecontext.h + delete mode 100644 xfa/fxfa/cxfa_textpiece.cpp + delete mode 100644 xfa/fxfa/cxfa_textpiece.h + delete mode 100644 xfa/fxfa/fm2js/DEPS + delete mode 100644 xfa/fxfa/fm2js/cxfa_fmexpression.cpp + delete mode 100644 xfa/fxfa/fm2js/cxfa_fmexpression.h + delete mode 100644 xfa/fxfa/fm2js/cxfa_fmexpression_unittest.cpp + delete mode 100644 xfa/fxfa/fm2js/cxfa_fmlexer_unittest.cpp + delete mode 100644 xfa/fxfa/fm2js/cxfa_fmparser.cpp + delete mode 100644 xfa/fxfa/fm2js/cxfa_fmparser.h + delete mode 100644 xfa/fxfa/fm2js/cxfa_fmsimpleexpression.cpp + delete mode 100644 xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h + delete mode 100644 xfa/fxfa/fm2js/cxfa_fmsimpleexpression_unittest.cpp + rename xfa/fxfa/{fm2js => formcalc}/BUILD.gn (71%) + create mode 100644 xfa/fxfa/formcalc/DEPS + create mode 100644 xfa/fxfa/formcalc/cxfa_fmexpression.cpp + create mode 100644 xfa/fxfa/formcalc/cxfa_fmexpression.h + create mode 100644 xfa/fxfa/formcalc/cxfa_fmexpression_unittest.cpp + rename xfa/fxfa/{fm2js => formcalc}/cxfa_fmlexer.cpp (76%) + rename xfa/fxfa/{fm2js => formcalc}/cxfa_fmlexer.h (63%) + create mode 100644 xfa/fxfa/formcalc/cxfa_fmlexer_unittest.cpp + create mode 100644 xfa/fxfa/formcalc/cxfa_fmparser.cpp + create mode 100644 xfa/fxfa/formcalc/cxfa_fmparser.h + rename xfa/fxfa/{fm2js => formcalc}/cxfa_fmparser_unittest.cpp (57%) + rename xfa/fxfa/{fm2js => formcalc}/cxfa_fmtojavascriptdepth.cpp (66%) + rename xfa/fxfa/{fm2js => formcalc}/cxfa_fmtojavascriptdepth.h (73%) + create mode 100644 xfa/fxfa/layout/DEPS + delete mode 100644 xfa/fxfa/parser/cxfa_deltas.cpp + delete mode 100644 xfa/fxfa/parser/cxfa_deltas.h + rename xfa/fxfa/parser/{cxfa_document_parser.cpp => cxfa_document_builder.cpp} (72%) + rename xfa/fxfa/parser/{cxfa_document_parser.h => cxfa_document_builder.h} (63%) + rename xfa/fxfa/parser/{cxfa_document_parser_embeddertest.cpp => cxfa_document_builder_embeddertest.cpp} (69%) + create mode 100644 xfa/fxfa/parser/cxfa_document_builder_unittest.cpp + delete mode 100644 xfa/fxfa/parser/cxfa_document_parser_unittest.cpp + create mode 100644 xfa/fxfa/parser/cxfa_document_unittest.cpp + create mode 100644 xfa/fxfa/parser/cxfa_timezoneprovider_unittest.cpp + create mode 100644 xfa/fxfa/parser/gced_locale_iface.h + delete mode 100644 xfa/fxfa/parser/xfa_resolvenode_rs.h + delete mode 100644 xfa/fxgraphics/BUILD.gn + delete mode 100644 xfa/fxgraphics/cxfa_gecolor.cpp + delete mode 100644 xfa/fxgraphics/cxfa_gecolor.h + delete mode 100644 xfa/fxgraphics/cxfa_gepath.cpp + delete mode 100644 xfa/fxgraphics/cxfa_gepattern.cpp + delete mode 100644 xfa/fxgraphics/cxfa_gepattern.h + delete mode 100644 xfa/fxgraphics/cxfa_geshading.cpp + delete mode 100644 xfa/fxgraphics/cxfa_geshading.h + delete mode 100644 xfa/fxgraphics/cxfa_graphics.h + +diff --git a/.clang-format b/.clang-format +index 2fb833a5d..30ed2de89 100644 +--- a/.clang-format ++++ b/.clang-format +@@ -1,2 +1,12 @@ ++# Defines the Chromium style for automatic reformatting. + # http://clang.llvm.org/docs/ClangFormatStyleOptions.html + BasedOnStyle: Chromium ++# This defaults to 'Auto'. Explicitly set it for a while, so that ++# 'vector >' in existing files gets formatted to ++# 'vector>'. ('Auto' means that clang-format will only use ++# 'int>>' if the file already contains at least one such instance.) ++Standard: Cpp11 ++ ++# TODO(crbug.com/1392808): Remove when InsertBraces has been upstreamed into ++# the Chromium style (is implied by BasedOnStyle: Chromium). ++InsertBraces: true +diff --git a/.gitattributes b/.gitattributes +index c772c32db..953c317a7 100644 +--- a/.gitattributes ++++ b/.gitattributes +@@ -3,6 +3,7 @@ + *.c text eol=lf + *.cc text eol=lf + *.cpp text eol=lf ++*.evt text eol=lf + *.gn text eol=lf + *.gni text eol=lf + *.h text eol=lf +diff --git a/.gn b/.gn +index c97435710..b2bf99d0c 100644 +--- a/.gn ++++ b/.gn +@@ -1,35 +1,33 @@ +-# Copyright 2016 PDFium Authors. All rights reserved. ++# Copyright 2016 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +-buildconfig = "//build/config/BUILDCONFIG.gn" ++# TODO(crbug.com/pdfium/1932): Switch back to //build/config/BUILDCONFIG.gn if ++# appropriate. ++buildconfig = "//build_overrides/BUILDCONFIG.gn" ++ ++# The python interpreter to use by default. On Windows, this will look ++# for python3.exe and python3.bat. ++script_executable = "python3" + + default_args = { +- v8_extra_library_files = [] +- v8_experimental_extra_library_files = [] ++ # PDFs only need to run JavaScript. ++ v8_enable_webassembly = false + + # Turns on compiler optimizations in V8 in Debug build. + v8_optimized_debug = true ++ ++ # PDFium is currently incompatible with the V8 Sandbox. ++ # See https://crbug.com/v8/13014 for details. ++ v8_enable_sandbox = false + } + +-check_targets = [ +- ":pdfium", +- ":pdfium_embeddertests", +- ":pdfium_unittests", +- "//constants/*", +- "//core/*", +- "//fpdfsdk/*", +- "//fxbarcode/*", +- "//fxjs/*", +- "//samples/*", +- "//skia/*", +- "//testing/:*", +- "//testing/fuzzers/*", +- "//testing/image_diff/*", +- "//third_party:bigint", +- "//third_party:fx_agg", +- "//third_party:fx_freetype", +- "//third_party:pdfium_base", +- "//third_party:skia_shared", +- "//xfa/*", ++no_check_targets = [ ++ # See https://crbug.com/v8/7330 and/or check if these entries exist in ++ # Chromium's //.gn file. ++ "//v8:cppgc_base", ++ "//v8:v8_internal_headers", ++ "//v8/src/inspector:inspector", ++ "//v8/test/cctest:cctest_sources", ++ "//v8/test/unittests:inspector_unittests_sources", + ] +diff --git a/.style.yapf b/.style.yapf +new file mode 100644 +index 000000000..fdd07237c +--- /dev/null ++++ b/.style.yapf +@@ -0,0 +1,2 @@ ++[style] ++based_on_style = yapf +diff --git a/.vpython b/.vpython3 +similarity index 56% +rename from .vpython +rename to .vpython3 +index efe51d4d3..b6cb1b556 100644 +--- a/.vpython ++++ b/.vpython3 +@@ -9,7 +9,7 @@ + # CIPD (the "Chrome Infrastructure Package Deployer" service). Unlike `pip`, + # this never requires the end-user machine to have a working python extension + # compilation environment. All of these packages are built using: +-# https://chromium.googlesource.com/infra/infra/+/master/infra/tools/dockerbuild/ ++# https://chromium.googlesource.com/infra/infra/+/main/infra/tools/dockerbuild/ + # + # All python scripts in the repo share this same spec, to avoid dependency + # fragmentation. +@@ -20,19 +20,35 @@ + # vpython path/to/script.py some --arguments + # + # Read more about `vpython` and how to modify this file here: +-# https://chromium.googlesource.com/infra/infra/+/master/doc/users/vpython.md ++# https://chromium.googlesource.com/infra/infra/+/main/doc/users/vpython.md + +-python_version: "2.7" ++python_version: "3.8" + +-# Used by build/toolchain/win/tool_wrapper.py ++# Used by build/skia_gold_common/output_managerless_skia_gold_session.py ++# Used by tools/code_coverage/coverage.py + wheel: < +- name: "infra/python/wheels/pypiwin32/${vpython_platform}" +- version: "version:219" +- match_tag: < +- platform: "win32" +- > +- match_tag: < +- platform: "win_amd64" +- > ++ name: "infra/python/wheels/six-py2_py3" ++ version: "version:1.15.0" + > + ++# Used by build/util/lib/results/result_sink.py ++wheel: < ++ name: "infra/python/wheels/certifi-py2_py3" ++ version: "version:2021.5.30" ++> ++wheel: < ++ name: "infra/python/wheels/charset_normalizer-py3" ++ version: "version:2.0.4" ++> ++wheel: < ++ name: "infra/python/wheels/idna-py2_py3" ++ version: "version:2.10" ++> ++wheel: < ++ name: "infra/python/wheels/requests-py2_py3" ++ version: "version:2.26.0" ++> ++wheel: < ++ name: "infra/python/wheels/urllib3-py2_py3" ++ version: "version:1.26.6" ++> +diff --git a/AUTHORS b/AUTHORS +index 3c9723713..f8e3ed9ea 100644 +--- a/AUTHORS ++++ b/AUTHORS +@@ -7,48 +7,39 @@ + # Organization + # + # See python fnmatch module documentation for more information. ++# ++# Please keep the list sorted. + +-Andrey Khalyavin ++# BEGIN individuals section. ++Abdelkarim Sellamna ++Aleksei Skotnikov + Antonio Gomes +-Brett Wilson +-Bruce Dawson + Chery Cherian + Claudio DeSouza +-Chris Palmer +-Dan Sinclair ++Dan Ilan + Felix Kauselmann +-Finnur Thorarinsson + GiWan Go +-Henrique Nakashima + Huy Ngo + Jiang Jiang +-Jochen Eisinger +-John Abd-El-Malek +-Julien Tinnes + Ke Liu +-Kostya Serebryany +-Lei Zhang +-Lucas Nihlen + Luật Nguyễn +-Matt Giuca ++Manuel Geißer + Michael Doppler + Miklos Vajna + Minh Trần +-Nico Weber +-Nicolás Peña +-Peter Kasting ++Peter Varga + Ralf Sippl +-Raymes Khoury +-Reid Kleckner +-Ryan Harrison ++Robert Collyer + Ryan Wiley +-Robert Sesek +-Sam Clegg +-Thomas Sepez ++Stephan Hartmann ++Tibor Dusnoki + Wang Qing + Zhuo Qingliang ++# END individuals section. + +-Collabora Ltd. <*@collabora.co.uk> ++# BEGIN organizations section. ++Ada Logics Ltd. <*@adalogics.com> ++Collabora Ltd. <*@collabora.com> + DocsCorp Pty Ltd. <*@docscorp.com> + Dropbox <*@dropbox.com> + Foxit Software Inc <*@foxitsoftware.com> +@@ -57,3 +48,5 @@ LG Electronics, Inc. <*@lge.com> + Loongson Technology Corporation Limited. <*@loongson.cn> + Microsoft <*@microsoft.com> + PSPDFKit GmbH <*@pspdfkit.com> ++The Chromium Authors <*@chromium.org> ++# END organizations section. +diff --git a/Android.bp b/Android.bp +index 4af25048f..85f449bf8 100644 +--- a/Android.bp ++++ b/Android.bp +@@ -109,10 +109,6 @@ cc_library_shared { + name: "libpdfium", + defaults: ["pdfium-core"], + +- header_libs: [ +- "libpdfium-constants", +- ], +- + whole_static_libs: [ + "libpdfium-fpdfsdk", + ], +@@ -122,6 +118,7 @@ cc_library_shared { + static_libs: [ + "libpdfium-agg", + "libpdfium-cmaps", ++ "libpdfium-constants", + "libpdfium-edit", + "libpdfium-fdrm", + "libpdfium-font", +diff --git a/BUILD.gn b/BUILD.gn +index 62f09e580..cb1da47df 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -1,10 +1,17 @@ +-# Copyright 2016 The Chromium Authors. All rights reserved. ++# Copyright 2016 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + ++import("//build/config/clang/clang.gni") ++import("//build/config/gclient_args.gni") + import("//testing/test.gni") + import("pdfium.gni") + ++group("default") { ++ testonly = true ++ deps = [ ":pdfium_all" ] ++} ++ + group("freetype_common") { + public_deps = [] + if (pdf_bundle_freetype) { +@@ -16,12 +23,10 @@ group("freetype_common") { + + config("pdfium_common_config") { + cflags = [] ++ cflags_cc = [] + ldflags = [] + include_dirs = [ "." ] +- defines = [ +- "PNG_PREFIX", +- "PNG_USE_READ_MACROS", +- ] ++ defines = [] + + if (!use_system_libopenjpeg2) { + defines += [ "OPJ_STATIC" ] +@@ -35,13 +40,208 @@ config("pdfium_common_config") { + defines += [ "_SKIA_SUPPORT_" ] + } + +- if (pdf_use_skia_paths) { +- defines += [ "_SKIA_SUPPORT_PATHS_" ] ++ if (pdf_use_partition_alloc) { ++ defines += [ "PDF_USE_PARTITION_ALLOC" ] + } + + if (is_win) { + # Assume UTF-8 by default to avoid code page dependencies. + cflags += [ "/utf-8" ] ++ ++ if (!is_clang) { ++ cflags += [ ++ # Warnings permanently disabled: ++ ++ # C4091: 'typedef ': ignored on left of 'X' when no variable is ++ # declared. ++ # This happens in a number of Windows headers. Dumb. ++ "/wd4091", ++ ++ # C4127: conditional expression is constant ++ # This warning can in theory catch dead code and other problems, but ++ # triggers in far too many desirable cases where the conditional ++ # expression is either set by macros or corresponds some legitimate ++ # compile-time constant expression (due to constant template args, ++ # conditionals comparing the sizes of different types, etc.). Some of ++ # these can be worked around, but it's not worth it. ++ "/wd4127", ++ ++ # C4251: 'identifier' : class 'type' needs to have dll-interface to be ++ # used by clients of class 'type2' ++ # This is necessary for the shared library build. ++ "/wd4251", ++ ++ # C4275: non dll-interface class used as base for dll-interface class ++ # This points out a potential (but rare) problem with referencing static ++ # fields of a non-exported base, through the base's non-exported inline ++ # functions, or directly. The warning is subtle enough that people just ++ # suppressed it when they saw it, so it's not worth it. ++ "/wd4275", ++ ++ # C4312 is a VS 2015 64-bit warning for integer to larger pointer. ++ # TODO(brucedawson): fix warnings, crbug.com/554200 ++ "/wd4312", ++ ++ # C4324 warns when padding is added to fulfill alignas requirements, ++ # but can trigger in benign cases that are difficult to individually ++ # suppress. ++ "/wd4324", ++ ++ # C4351: new behavior: elements of array 'array' will be default ++ # initialized ++ # This is a silly "warning" that basically just alerts you that the ++ # compiler is going to actually follow the language spec like it's ++ # supposed to, instead of not following it like old buggy versions did. ++ # There's absolutely no reason to turn this on. ++ "/wd4351", ++ ++ # C4355: 'this': used in base member initializer list ++ # It's commonly useful to pass |this| to objects in a class' initializer ++ # list. While this warning can catch real bugs, most of the time the ++ # constructors in question don't attempt to call methods on the passed-in ++ # pointer (until later), and annotating every legit usage of this is ++ # simply more hassle than the warning is worth. ++ "/wd4355", ++ ++ # C4503: 'identifier': decorated name length exceeded, name was ++ # truncated ++ # This only means that some long error messages might have truncated ++ # identifiers in the presence of lots of templates. It has no effect on ++ # program correctness and there's no real reason to waste time trying to ++ # prevent it. ++ "/wd4503", ++ ++ # Warning C4589 says: "Constructor of abstract class ignores ++ # initializer for virtual base class." Disable this warning because it ++ # is flaky in VS 2015 RTM. It triggers on compiler generated ++ # copy-constructors in some cases. ++ "/wd4589", ++ ++ # C4611: interaction between 'function' and C++ object destruction is ++ # non-portable ++ # This warning is unavoidable when using e.g. setjmp/longjmp. MSDN ++ # suggests using exceptions instead of setjmp/longjmp for C++, but ++ # Chromium code compiles without exception support. We therefore have to ++ # use setjmp/longjmp for e.g. JPEG decode error handling, which means we ++ # have to turn off this warning (and be careful about how object ++ # destruction happens in such cases). ++ "/wd4611", ++ ++ # Warnings to evaluate and possibly fix/reenable later: ++ ++ "/wd4100", # Unreferenced formal function parameter. ++ "/wd4121", # Alignment of a member was sensitive to packing. ++ "/wd4244", # Conversion: possible loss of data. ++ "/wd4505", # Unreferenced local function has been removed. ++ "/wd4510", # Default constructor could not be generated. ++ "/wd4512", # Assignment operator could not be generated. ++ "/wd4610", # Class can never be instantiated, constructor required. ++ "/wd4838", # Narrowing conversion. Doesn't seem to be very useful. ++ "/wd4995", # 'X': name was marked as #pragma deprecated ++ "/wd4996", # Deprecated function warning. ++ ++ # These are variable shadowing warnings that are new in VS2015. We ++ # should work through these at some point -- they may be removed from ++ # the RTM release in the /W4 set. ++ "/wd4456", ++ "/wd4457", ++ "/wd4458", ++ "/wd4459", ++ ++ # All of our compilers support the extensions below. ++ "/wd4200", # nonstandard extension used: zero-sized array in ++ # struct/union ++ "/wd4201", # nonstandard extension used: nameless struct/union ++ "/wd4204", # nonstandard extension used : non-constant aggregate ++ # initializer ++ ++ "/wd4221", # nonstandard extension used : 'identifier' : cannot be ++ # initialized using address of automatic variable ++ ++ # http://crbug.com/588506 - Conversion suppressions waiting on Clang ++ # -Wconversion. ++ "/wd4245", # 'conversion' : conversion from 'type1' to 'type2', ++ # signed/unsigned mismatch ++ ++ "/wd4267", # 'var' : conversion from 'size_t' to 'type', possible loss ++ # of ++ # data ++ ++ "/wd4305", # 'identifier' : truncation from 'type1' to 'type2' ++ "/wd4389", # 'operator' : signed/unsigned mismatch ++ ++ "/wd4702", # unreachable code ++ ++ # http://crbug.com/848979 - MSVC is more conservative than Clang with ++ # regards to variables initialized and consumed in different branches. ++ "/wd4701", # Potentially uninitialized local variable 'name' used ++ "/wd4703", # Potentially uninitialized local pointer variable 'name' ++ # used ++ ++ # http://crbug.com/848979 - Remaining Clang permitted warnings. ++ "/wd4661", # 'identifier' : no suitable definition provided for ++ # explicit ++ # template instantiation request ++ ++ "/wd4706", # assignment within conditional expression ++ # MSVC is stricter and requires a boolean expression. ++ ++ "/wd4715", # 'function' : not all control paths return a value' ++ # MSVC does not analyze switch (enum) for completeness. ++ ] ++ ++ cflags_cc += [ ++ # Allow "noexcept" annotations even though we compile with exceptions ++ # disabled. ++ "/wd4577", ++ ] ++ ++ if (current_cpu == "x86") { ++ cflags += [ ++ # VC++ 2015 changes 32-bit size_t truncation warnings from 4244 to ++ # 4267. Example: short TruncTest(size_t x) { return x; } ++ # Since we disable 4244 we need to disable 4267 during migration. ++ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. ++ "/wd4267", ++ ] ++ ++ if (msvc_use_sse2) { ++ cflags += [ "/arch:SSE2" ] ++ } ++ } ++ } ++ } ++ ++ if (is_clang) { ++ # Override -Wno-c++11-narrowing. ++ cflags += [ "-Wc++11-narrowing" ] ++ ++ # TODO(crbug.com/1213098): Remove once this is in //build. ++ cflags += [ "-Wdeprecated-copy" ] ++ ++ # May flag some issues when converting int to size_t. ++ cflags += [ "-Wtautological-unsigned-zero-compare" ] ++ ++ # Catch misuse of cppgc in XFA. ++ if (pdf_enable_xfa && clang_use_chrome_plugins) { ++ cflags += [ ++ "-Xclang", ++ "-add-plugin", ++ "-Xclang", ++ "blink-gc-plugin", ++ ] ++ } ++ } ++ ++ if (!is_win && !is_clang) { ++ cflags += [ ++ # Override -Wno-narrowing for GCC. ++ "-Wnarrowing", ++ ++ # GCC assumes that control can get past an exhaustive switch and then ++ # warns if there's no return there. ++ "-Wno-return-type", ++ ] + } + } + +@@ -72,10 +272,6 @@ config("pdfium_public_config") { + } + } + } +- +- if (pdf_use_win32_gdi) { +- defines += [ "PDFIUM_PRINT_TEXT_WITH_GDI" ] +- } + } + + config("pdfium_core_config") { +@@ -86,21 +282,27 @@ config("pdfium_core_config") { + "//build/config/compiler:noshadowing", + ] + defines = [] +- if (is_linux) { +- if (current_cpu == "x64") { +- defines += [ "_FX_CPU_=_FX_X64_" ] +- cflags += [ "-fPIC" ] +- } else if (current_cpu == "x86") { +- defines += [ "_FX_CPU_=_FX_X86_" ] +- } +- } + if (is_win) { + cflags += [ + "/wd4324", + "/wd4577", + ] + } +- configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] ++} ++ ++config("pdfium_strict_config") { ++ configs = [ ++ ":pdfium_core_config", ++ "//build/config/compiler:wexit_time_destructors", ++ "//build/config/compiler:wglobal_constructors", ++ ] ++} ++ ++config("pdfium_noshorten_config") { ++ cflags = [] ++ if (is_clang) { ++ cflags += [ "-Wshorten-64-to-32" ] ++ } + } + + source_set("pdfium_public_headers_impl") { +@@ -122,6 +324,7 @@ source_set("pdfium_public_headers_impl") { + "public/fpdf_progressive.h", + "public/fpdf_save.h", + "public/fpdf_searchex.h", ++ "public/fpdf_signature.h", + "public/fpdf_structtree.h", + "public/fpdf_sysfontinfo.h", + "public/fpdf_text.h", +@@ -139,8 +342,9 @@ group("pdfium_public_headers") { + } + + component("pdfium") { ++ output_name = "pdfium" + libs = [] +- configs += [ ":pdfium_core_config" ] ++ configs += [ ":pdfium_strict_config" ] + public_configs = [ ":pdfium_public_config" ] + + deps = [ +@@ -180,7 +384,7 @@ component("pdfium") { + } + + if (is_mac) { +- libs += [ ++ frameworks = [ + "AppKit.framework", + "CoreFoundation.framework", + ] +@@ -191,24 +395,16 @@ component("pdfium") { + complete_static_lib = true + configs -= [ "//build/config/compiler:thin_archive" ] + } +- +- if (is_component_build) { +- deps += [ "testing/fuzzers:fuzzer_impls" ] +- } + } + +-# Targets below this are only visible within this file (and to the +-# top-level gn_visibility target used to help gn_all build everything). +-visibility = [ +- ":*", +- "//:gn_visibility", +-] ++# Targets below this are only visible within this file. ++visibility = [ ":*" ] + + group("pdfium_unittest_deps") { + testonly = true + public_deps = [ + "core/fxcrt", +- "testing:test_support", ++ "testing:unit_test_support", + "//testing/gmock", + "//testing/gtest", + ] +@@ -238,7 +434,6 @@ test("pdfium_unittests") { + "core/fxcrt:unittests", + "core/fxge:unittests", + "fpdfsdk:unittests", +- "testing:test_support", + "testing:unit_test_support", + "//testing/gmock", + "//testing/gtest", +@@ -255,18 +450,20 @@ test("pdfium_unittests") { + "fxjs:unittests", + "//v8", + ] +- } + +- if (pdf_enable_xfa) { +- deps += [ +- "fxbarcode:unittests", +- "xfa/fde:unittests", +- "xfa/fgas:unittests", +- "xfa/fgas/layout:unittests", +- "xfa/fxfa:unittests", +- "xfa/fxfa/fm2js:unittests", +- "xfa/fxfa/parser:unittests", +- ] ++ if (pdf_enable_xfa) { ++ deps += [ ++ "core/fxcrt/css:unittests", ++ "fxbarcode:unittests", ++ "xfa/fde:unittests", ++ "xfa/fgas/crt:unittests", ++ "xfa/fgas/font:unittests", ++ "xfa/fgas/layout:unittests", ++ "xfa/fxfa:unittests", ++ "xfa/fxfa/formcalc:unittests", ++ "xfa/fxfa/parser:unittests", ++ ] ++ } + } + } + +@@ -276,7 +473,6 @@ group("pdfium_embeddertest_deps") { + ":pdfium_public_headers", + "core/fxcrt", + "testing:embedder_test_support", +- "testing:test_support", + "third_party:pdfium_base_test_support", + "//testing/gmock", + "//testing/gtest", +@@ -293,6 +489,7 @@ test("pdfium_embeddertests") { + testonly = true + sources = [ "testing/embedder_test_main.cpp" ] + deps = [ ++ ":pdfium_embeddertest_deps", + "core/fpdfapi/edit:embeddertests", + "core/fpdfapi/parser:embeddertests", + "core/fpdfapi/render:embeddertests", +@@ -300,8 +497,8 @@ test("pdfium_embeddertests") { + "core/fxcrt", + "core/fxge:embeddertests", + "fpdfsdk:embeddertests", ++ "fpdfsdk/formfiller:embeddertests", + "fpdfsdk/pwl:embeddertests", +- "testing:test_support", + "testing/image_diff", + "//testing/gmock", + "//testing/gtest", +@@ -336,14 +533,12 @@ executable("pdfium_diff") { + testonly = true + sources = [ "testing/image_diff/image_diff.cpp" ] + deps = [ +- ":pdfium", + "core/fxcrt", ++ "testing:path_service", + "testing/image_diff", + "//build/win:default_exe_manifest", + ] +- configs -= [ "//build/config/compiler:chromium_code" ] +- configs += [ "//build/config/compiler:no_chromium_code" ] +- configs += [ ":pdfium_core_config" ] ++ configs += [ ":pdfium_strict_config" ] + } + + if (pdf_is_standalone) { +@@ -372,3 +567,16 @@ group("pdfium_all") { + ] + } + } ++ ++# Makes additional targets reachable only for "gn check". These are not always ++# built by the "all" Ninja target, which uses the "default" group, which in turn ++# depends on the "pdfium_all" group. ++group("gn_check") { ++ deps = [] ++ ++ # TODO(crbug.com/pdfium/1832): Remove !is_android when //third_party/expat is ++ # available. ++ if (defined(checkout_skia) && checkout_skia && !is_android) { ++ deps += [ "//skia" ] ++ } ++} +diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md +new file mode 100644 +index 000000000..f23251b66 +--- /dev/null ++++ b/CONTRIBUTING.md +@@ -0,0 +1,205 @@ ++# CONTRIBUTING ++In general, we follow the ++[Chromium Contributing](https://chromium.googlesource.com/chromium/src/+/main/docs/contributing.md) ++guidelines in PDFium. The code review process, and the build tools are all very ++similar to Chromium. The PDFium ++[README](https://pdfium.googlesource.com/pdfium/+/refs/heads/main/README.md) ++outlines specific build and test information for PDFium. ++ ++This document focuses on how the PDFium project operates and how we’d like it ++to operate in the future. This is a living document, please file bugs if you ++think there are changes/updates which could be put in place to make it easier ++to contribute to PDFium. ++ ++## Communication ++When writing a new feature or fixing an existing bug, get a second opinion ++before investing effort in coding. Coordinating up front makes it much easier ++to avoid frustration later on. ++ ++If it‘s a new feature, or updating existing code, first propose it to the ++[mailing list](https://groups.google.com/forum/#!forum/pdfium). ++ ++ * If a change needs further context outside the CL, it should be tracked in ++ the [bug system](https://bugs.chromium.org/p/pdfium). Bugs are the right ++ place for long histories, discussion and debate, attaching screenshots, and ++ linking to other associated bugs. Bugs are unnecessary for changes isolated ++ enough to not need any of these. ++ * If the work being implemented is especially complex or large a design ++ document may be warranted. The document should be linked to the filled bug ++ and be set to publicly viewable. ++ * If there isn't a bug and there should be one, please file a new bug. ++ * Just because there is a bug in the bug system doesn't necessarily mean that ++ a patch will be accepted. ++ ++## Public APIs ++The public API of PDFium has grown over time. There are multiple mechanisms in ++place to support this growth from the stability requirements to the versioning ++fields. Along with those there are several other factors to be considered when ++adding public APIs. ++ ++ * _Consistency_. We try to keep the APIs consistent with each other, this ++ includes things like naming, parameter ordering and how parameters are ++ handled. ++ * _Generality_. PDFium is used in several places outside the browser. This ++ could be server side, or in user applications. APIs should be designed to ++ work in the general case, or such that they can be expanded to the general ++ case if possible. ++ * _Documentation_. All public APIs should be documented to include information ++ on ownership of passed parameters, valid values being provided, error ++ conditions and return values. ++ * _Differentiate error conditions_. If at all possible, it should be possible ++ to tell the difference between a valid failure and an error. ++ * _Avoid global state_. APIs should receive the objects to be operated on ++ instead of assuming they exist in a global context. ++ ++### Stability ++There are a lot of consumers of PDFium outside of Chromium. These include ++LibreOffice, Android and offline conversion tooling. As such, a lot of care is ++taken around the code in the ++[public](https://pdfium.googlesource.com/pdfium/+/refs/heads/main/public/) ++folder. When planning on changing the public API, the change should be preceded ++by a bug being created and an email to the mailing list to gather feedback from ++other PDFium embedders. ++ ++The only stability guarantees that PDFium provides are around the APIs in the ++public folder. Any other interface in the system can be changed without notice. ++If there are features needed which are not exposed through the public headers ++you'll need to file a bug to get it added to the public APIs. ++ ++#### Experimental ++All APIs start as Experimental. The experimental status is a documentation tag ++which is added to the API, the first line of the API documentation should be ++`// Experimental API.` ++ ++Experimental APIs may be changed or removed entirely without formal notice to ++the community. ++ ++#### Stable ++APIs eventually graduate to stable. This is done by removing the ++`// Experimental API.` marker in the documentation. We endeavor to not change ++stable APIs without notice to the community. ++ ++NOTE, the process of migrating from experimental to stable isn’t well defined ++at this point. We have experimental APIs which have been that way for multiple ++years. We should work to better define how this transition happens. ++ ++#### Deprecated ++If the API is retired, it is marked as deprecated and will eventually be removed. ++API deprecation should, ideally, come with a better replacement API and have a ++6-12 months deprecation period. The pending removal should be recorded in the ++documentation comment for the API and should also be recorded in the README with ++the target removal timeframe. All deprecations should have an associated bug ++attached to them. ++ ++### Versioning ++In order to allow the public API to expand there are `version` fields in some ++structures. When the versioned structures are expanded those version fields ++need to be incremented to cover the new additions. The code then needs to guard ++against the structure being received having the required version number in ++order to validate the new additions are available. ++ ++## Trybot Access ++Changes must pass the try bots before they are merged into the repo. For your ++first few CLs the try bots will need to be triggered by a committer. After ++you've submitted 2-3 CLs you can request try bot access by emailing one of the ++OWNERS and requesting try bot access. This will allow you to trigger the bots ++on your own changes without needing a committer. ++ ++## Committers ++All changes committed to PDFium must be reviewed by a committer. Committers ++have done significant work in the PDFium code base and have a good overall ++understanding of the system. ++ ++Contributors can become committers as they exhibit a strong understanding ++of the code base. There is a general requirement for ~10 non-trivial CLs to be ++written by the contributor before being considered for committership. The ++contributor is then nominated by an existing committer and if the nomination is ++accepted by two other committers they receive committer status. ++ ++## OWNERS ++The OWNERS files list long time committers to the project and have a broad ++understanding of the code base and how the various pieces interact. In the ++event of a code review stalling with a committer, the OWNERS are the first line ++of escalation. The OWNERS files inherit up the tree, so an OWNER in a top-level ++folder has OWNERS in the folders subdirectories. ++ ++There are a limited number of OWNERS files in PDFium at this time due to the ++inherent interconnectedness of the code. We are hoping to expand the number of ++OWNERS files to make them more targeted as the code quality progresses. ++ ++Committers can be added to OWNERS files when they exhibit a strong ++understanding of the PDFium code base. This typically involves a combination of ++significant CLs, code review on other contributor CLs, and working with the ++other OWNERs to work through design and development considerations for the code. ++An OWNER must be committed to upholding the principles for the long term health ++of the project, take on a responsibility for reviewing future work, and ++mentor new contributors. Once you are a committer, you should feel free to reach ++out to the OWNERS who have reviewed your patches to ask what else they’d like to ++see from you to be comfortable nominating you as an OWNER. Once nominated, ++OWNERS are added or removed by rough consensus of the existing OWNERS. ++ ++## Escalations ++There are times when reviews stall due to differences between reviewers, ++developers and OWNERS. If this happens, please escalate the situation to one of ++the people in the top-level OWNERS file (or another of the owners if already ++discussing with a top-level owner). If the disagreement has moved up through ++all the OWNERS files in the PDFium repo, the escalation should then proceed to ++the Chromium ++[ATL_OWNERS](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/ATL_OWNERS) ++as the final deciders. ++ ++The ++[Standard of Code Review](https://google.github.io/eng-practices/review/reviewer/standard.html) ++document has some good guidance on resolving conflicts during code review. ++ ++## CLA ++All contributors must complete the Google contributor license agreement. For ++individual contributors, please complete the ++[Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual?csw=1) ++online. Corporate contributors must fill out the ++[Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate?csw=1) ++and send it to us as described on that page. ++ ++Your first CL should add yourself to the ++[AUTHORS](https://pdfium.googlesource.com/pdfium/+/refs/heads/main/AUTHORS) ++file (unless you’re covered by one of the blanket entries). ++ ++### External contributor checklist for reviewers ++Before LGTMing a change, ensure that the contribution can be accepted: ++ * Definition: The "author" is the email address that owns the code review ++ request on ++ [https://pdfium-review.googlesource.com](https://pdfium-review.googlesource.com/) ++ * Ensure the author is already listed in ++ [AUTHORS](https://pdfium.googlesource.com/pdfium/+/refs/heads/main/AUTHORS). ++ In some cases, the author's company might have a wildcard rule ++ (e.g. \*@google.com). ++ * If the author or their company is not listed, the CL should include a new ++ AUTHORS entry. ++ * Ensure the new entry is reviewed by a reviewer who works for Google. ++ * Contributor License Agreement can be verified by Googlers at ++ [http://go/cla](http://go/cla) ++ * If there is a corporate CLA for the author‘s company, it must list the ++ person explicitly (or the list of authorized contributors must say ++ something like "All employees"). If the author is not on their company’s ++ roster, do not accept the change. ++ ++## Legacy Code ++The PDFium code base has been around in one form or another for a long time. As ++such, there is a lot of legacy hidden in the existing code. There are surprising ++interactions and untested corners of the code. We are actively working on ++increasing code coverage on the existing code, and especially welcome additions ++which move the coverage upwards. All new code should come with tests (either ++unit tests or integration tests depending on the feature). ++ ++As part of this legacy nature, there is a good chance the code you’re working ++with wasn’t designed to do what you need it to do. There are often refactorings ++and bug fixes that end up happening along with feature development. Those ++fixes/refactorings should be pulled out to their own changes with the ++appropriate tests. This will make reviews a lot easier as, currently, it can be ++hard to tell if there are far reaching effects of a given change. ++ ++There is a lot of existing technical debt that is being paid down in PDFium, ++anything we can do here to make future development easier is a great benefit to ++the project. This debt means means code reviews can take a bit longer if ++research is needed to determine how a feature change will interact with the ++rest of the system. +diff --git a/DEPS b/DEPS +index a93928220..e5aa4d851 100644 +--- a/DEPS ++++ b/DEPS +@@ -1,132 +1,429 @@ + use_relative_paths = True + ++gclient_gn_args_file = 'build/config/gclient_args.gni' ++gclient_gn_args = [ ++ 'checkout_skia', ++] ++ + vars = { + # By default, we should check out everything needed to run on the main +- # chromium waterfalls. This var can be also be set to "small", in order +- # to skip things are not strictly needed to build chromium for development +- # purposes. ++ # pdfium waterfalls. This var can be also be set to 'small', in order to skip ++ # things are not strictly needed to build pdfium for development purposes, ++ # by adding the following line to the .gclient file inside a solutions entry: ++ # "custom_vars": { "checkout_configuration": "small" }, ++ # Similarly, this var can be set to 'minimal' to also skip the Skia and V8 ++ # checkouts for the smallest possible checkout, where some features will not ++ # work. + 'checkout_configuration': 'default', + +- 'checkout_instrumented_libraries': 'checkout_linux and checkout_configuration != "small"', ++ 'checkout_instrumented_libraries': 'checkout_linux and checkout_configuration != "small" and checkout_configuration != "minimal"', ++ ++ 'checkout_skia': 'checkout_configuration != "minimal"', ++ ++ 'checkout_testing_corpus': 'checkout_configuration != "small" and checkout_configuration != "minimal"', ++ ++ 'checkout_v8': 'checkout_configuration != "minimal"', ++ ++ # By default, download the fuchsia sdk from the public sdk directory. ++ 'fuchsia_sdk_cipd_prefix': 'fuchsia/sdk/gn/', + + 'chromium_git': 'https://chromium.googlesource.com', + 'pdfium_git': 'https://pdfium.googlesource.com', ++ 'skia_git': 'https://skia.googlesource.com', + +- 'android_ndk_revision': '27c0a8d090c666a50e40fceb4ee5b40b1a2d3f87', +- 'binutils_revision': '01aa7745b0bab64ae22600f09fd6483c60f22ebf', +- 'build_revision': '1bee638a8c4a9481ea06df4982d69488d0a5626d', +- 'buildtools_revision': '1f38b432e5630619f3aba0a22b9b63d606aee35a', +- 'catapult_revision': 'f7d73bb520283d2a06b8fde8a1b02aa33414fcd0', +- 'clang_revision': '42fbdfef1ce265b09dc6bda2ed90d83324c97481', +- 'code_coverage_revision': 'c7a868bacaccf4f52848e04564fb7de0671e0727', +- 'depot_tools_revision': 'e9730d75a00548a22e4392567243969d85c02dd4', +- 'freetype_revision': 'e5038be70414cf66da6c4d5ce4e30375884c30d8', +- 'gtest_revision': '5395345ca4f0c596110188688ed990e0de5a181c', +- 'icu_revision': 'dbd3825b31041d782c5b504c59dcfb5ac7dda08c', +- 'instrumented_lib_revision': '4dca59c6a614b08b394ed6154a8fcded9298b07e', +- 'jinja2_revision': 'b41863e42637544c2941b574c7877d3e1f663e25', +- 'jpeg_turbo_revision': 'ce0e57e8e636f5132fe6f0590a4dba91f92fd935', +- 'markupsafe_revision': '8f45f5cfa0009d2a70589bcda0349b8cb2b72783', +- 'pdfium_tests_revision': '02dd653ec62649b6f1aa4e4526071cc32d903f54', +- 'skia_revision': 'd50cc95872a8a832faea0154f7ea1fd56cebc775', +- 'tools_memory_revision': 'f7b00daf4df7f6c469f5fbc68d7f40f6bd15d6e6', +- 'trace_event_revision': '81c050f857a0e3c960cfd87f37e3d30d2ef78718', +- 'v8_revision': 'cd34145326def51cb6dcf87aed7d0caf9f62bb4f', +- 'yasm_source_revision': '720b70524a4424b15fc57e82263568c8ba0496ad', +- 'zlib_revision': '814da1f383b625955149c3845db62af3f29a4ffe', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling abseil ++ # and whatever else without interference from each other. ++ 'abseil_revision': '20c8ae002db022653b94e80dec69306558818ebf', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling android_ndk ++ # and whatever else without interference from each other. ++ 'android_ndk_revision': '8388a2be5421311dc75c5f937aae13d821a27f3d', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling build ++ # and whatever else without interference from each other. ++ 'build_revision': 'c44ccbfc028a136e7d39bf79e45a92a91d7b5beb', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling buildtools ++ # and whatever else without interference from each other. ++ 'buildtools_revision': '3ee69a5c6bc8d115dea09bbf8e536f529e7e12a8', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling catapult ++ # and whatever else without interference from each other. ++ 'catapult_revision': 'ac30cc4bc7d30e574625e1f6a77fba5df0719ed6', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling clang format ++ # and whatever else without interference from each other. ++ 'clang_format_revision': 'f97059df7f8b205064625cdb5f97b56668a125ef', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling clang ++ # and whatever else without interference from each other. ++ 'clang_revision': 'f6862472607fa628642713b615a9e119d51bd43d', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling code_coverage ++ # and whatever else without interference from each other. ++ 'code_coverage_revision': '9b51524c4f7575ee90d9173507e6f13f814d7001', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling depot_tools ++ # and whatever else without interference from each other. ++ 'depot_tools_revision': 'ab117384e6c0384ba5aab66c13d8f1008d964655', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling freetype ++ # and whatever else without interference from each other. ++ 'freetype_revision': 'b0a4f99278aa7e14bd1d0d9e40ad28dce543fde6', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling Fuchsia sdk ++ # and whatever else without interference from each other. ++ 'fuchsia_version': 'version:12.20230330.3.1', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling GN CIPD package version ++ # and whatever else without interference from each other. ++ 'gn_version': 'git_revision:41fef642de70ecdcaaa26be96d56a0398f95abd4', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling gtest ++ # and whatever else without interference from each other. ++ 'gtest_revision': 'af29db7ec28d6df1c7f0f745186884091e602e07', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling icu ++ # and whatever else without interference from each other. ++ 'icu_revision': '1e49ac26ddc712b1ab702f69023cbc57e9ae6628', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling instrumented_lib ++ # and whatever else without interference from each other. ++ 'instrumented_lib_revision': '0f536d22dbed454b1254c7e6d7130eab28fba1fa', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling jinja2 ++ # and whatever else without interference from each other. ++ 'jinja2_revision': '264c07d7e64f2874434a3b8039e101ddf1b01e7e', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling jpeg_turbo ++ # and whatever else without interference from each other. ++ 'jpeg_turbo_revision': 'aa4075f116e4312537d0d3e9dbd5e31096539f94', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling libc++ ++ # and whatever else without interference from each other. ++ # If you change this, also update the libc++ revision in ++ # //buildtools/deps_revisions.gni. ++ 'libcxx_revision': 'ab37483b426c16ce33f8f0064be571513d5a8c34', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling libc++abi ++ # and whatever else without interference from each other. ++ 'libcxxabi_revision': '4a9d0560b481a96821bec591325b50a5063f4a32', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling libpng ++ # and whatever else without interference from each other. ++ 'libpng_revision': '805df541c44099bb20d425ac47c666e29b1f7a80', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling libunwind ++ # and whatever else without interference from each other. ++ 'libunwind_revision': 'f3464caa6aa83a5eb924a5ae3779e974bd1bebf4', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling lss ++ # and whatever else without interference from each other. ++ 'lss_revision': 'ce877209e11aa69dcfffbd53ef90ea1d07136521', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling markupsafe ++ # and whatever else without interference from each other. ++ 'markupsafe_revision': '13f4e8c9e206567eeb13bf585406ddc574005748', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling nasm_source ++ # and whatever else without interference from each other. ++ 'nasm_source_revision': '7fc833e889d1afda72c06220e5bed8fb43b2e5ce', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling Ninja CIPD package version ++ # and whatever else without interference from each other. ++ 'ninja_version': 'version:2@1.11.1.chromium.6', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling partition_allocator ++ # and whatever else without interference from each other. ++ 'partition_allocator_revision': '118936d6793d0c259bd9023717d37367a7b04320', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling pdfium_tests ++ # and whatever else without interference from each other. ++ 'pdfium_tests_revision': 'd1386521f5d606b9110143aa40b23651b17aded2', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling resultdb ++ # and whatever else without interference from each other. ++ 'resultdb_version': 'git_revision:ebc74d10fa0d64057daa6f128e89f3672eeeec95', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling skia ++ # and whatever else without interference from each other. ++ 'skia_revision': 'ad459a5b8df474d881209696b3fb4f038e0d3a55', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling test_fonts ++ # and whatever else without interference from each other. ++ 'test_fonts_revision': '7f51783942943e965cd56facf786544ccfc07713', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling tools_memory ++ # and whatever else without interference from each other. ++ 'tools_memory_revision': '13f0b81ce581364c5f0f2e9e16d6120073dc56a6', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling trace_event ++ # and whatever else without interference from each other. ++ 'trace_event_revision': '147f65333c38ddd1ebf554e89965c243c8ce50b3', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling v8 ++ # and whatever else without interference from each other. ++ 'v8_revision': 'f66b0bc93b1edc09a6ff66c2e0ae0f2848d90be7', ++ # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling zlib ++ # and whatever else without interference from each other. ++ 'zlib_revision': 'b890619bc2b193b8fbe9c1c053f4cd19a9791d92', + } + ++# Only these hosts are allowed for dependencies in this DEPS file. ++# If you need to add a new host, and the new host is not in Chromium's DEPS ++# file's allowed_hosts list, contact Chrome infrastructure team. ++allowed_hosts = [ ++ 'chromium.googlesource.com', ++ 'pdfium.googlesource.com', ++ 'skia.googlesource.com', ++] ++ + deps = { +- "base/trace_event/common": +- Var('chromium_git') + "/chromium/src/base/trace_event/common.git@" + ++ 'base/allocator/partition_allocator': ++ Var('chromium_git') + ++ '/chromium/src/base/allocator/partition_allocator.git@' + ++ Var('partition_allocator_revision'), ++ ++ 'base/trace_event/common': ++ Var('chromium_git') + '/chromium/src/base/trace_event/common.git@' + + Var('trace_event_revision'), + +- "build": +- Var('chromium_git') + "/chromium/src/build.git@" + Var('build_revision'), ++ 'build': ++ Var('chromium_git') + '/chromium/src/build.git@' + Var('build_revision'), + +- "buildtools": +- Var('chromium_git') + "/chromium/src/buildtools.git@" + ++ 'buildtools': ++ Var('chromium_git') + '/chromium/src/buildtools.git@' + + Var('buildtools_revision'), + +- "testing/corpus": +- Var('pdfium_git') + "/pdfium_tests@" + Var('pdfium_tests_revision'), ++ 'buildtools/clang_format/script': ++ Var('chromium_git') + ++ '/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@' + ++ Var('clang_format_revision'), + +- "third_party/android_ndk": { +- 'url': Var('chromium_git') + "/android_ndk.git@" + Var('android_ndk_revision'), +- 'condition': 'checkout_android', ++ 'buildtools/linux64': { ++ 'packages': [ ++ { ++ 'package': 'gn/gn/linux-amd64', ++ 'version': Var('gn_version'), ++ } ++ ], ++ 'dep_type': 'cipd', ++ 'condition': 'host_os == "linux"', ++ }, ++ ++ 'buildtools/mac': { ++ 'packages': [ ++ { ++ 'package': 'gn/gn/mac-${{arch}}', ++ 'version': Var('gn_version'), ++ } ++ ], ++ 'dep_type': 'cipd', ++ 'condition': 'host_os == "mac"', ++ }, ++ ++ 'buildtools/third_party/libc++/trunk': ++ Var('chromium_git') + ++ '/external/github.com/llvm/llvm-project/libcxx.git@' + ++ Var('libcxx_revision'), ++ ++ 'buildtools/third_party/libc++abi/trunk': ++ Var('chromium_git') + ++ '/external/github.com/llvm/llvm-project/libcxxabi.git@' + ++ Var('libcxxabi_revision'), ++ ++ 'buildtools/third_party/libunwind/trunk': ++ Var('chromium_git') + ++ '/external/github.com/llvm/llvm-project/libunwind.git@' + ++ Var('libunwind_revision'), ++ ++ 'buildtools/win': { ++ 'packages': [ ++ { ++ 'package': 'gn/gn/windows-amd64', ++ 'version': Var('gn_version'), ++ } ++ ], ++ 'dep_type': 'cipd', ++ 'condition': 'host_os == "win"', + }, + +- "third_party/binutils": +- Var('chromium_git') + "/chromium/src/third_party/binutils.git@" + +- Var('binutils_revision'), ++ 'testing/corpus': { ++ 'url': Var('pdfium_git') + '/pdfium_tests@' + Var('pdfium_tests_revision'), ++ 'condition': 'checkout_testing_corpus', ++ }, ++ ++ 'third_party/abseil-cpp': ++ Var('chromium_git') + '/chromium/src/third_party/abseil-cpp.git@' + ++ Var('abseil_revision'), + +- "third_party/catapult": { +- 'url': Var('chromium_git') + '/catapult.git' + '@' + Var('catapult_revision'), ++ 'third_party/android_ndk': { ++ 'url': Var('chromium_git') + '/android_ndk.git@' + ++ Var('android_ndk_revision'), ++ 'condition': 'checkout_android', ++ }, ++ ++ 'third_party/catapult': { ++ 'url': Var('chromium_git') + '/catapult.git@' + Var('catapult_revision'), + 'condition': 'checkout_android', + }, + + 'third_party/depot_tools': +- Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + ++ Var('chromium_git') + '/chromium/tools/depot_tools.git@' + + Var('depot_tools_revision'), + +- "third_party/freetype/src": ++ 'third_party/freetype/src': + Var('chromium_git') + '/chromium/src/third_party/freetype2.git@' + + Var('freetype_revision'), + +- "third_party/googletest/src": +- Var('chromium_git') + '/external/github.com/google/googletest.git' + '@' + ++ 'third_party/fuchsia-sdk/sdk': { ++ 'packages': [ ++ { ++ 'package': Var('fuchsia_sdk_cipd_prefix') + '${{platform}}', ++ 'version': Var('fuchsia_version'), ++ }, ++ ], ++ 'condition': 'checkout_fuchsia', ++ 'dep_type': 'cipd', ++ }, ++ ++ 'third_party/googletest/src': ++ Var('chromium_git') + '/external/github.com/google/googletest.git@' + + Var('gtest_revision'), + +- "third_party/icu": +- Var('chromium_git') + "/chromium/deps/icu.git@" + Var('icu_revision'), ++ 'third_party/icu': ++ Var('chromium_git') + '/chromium/deps/icu.git@' + Var('icu_revision'), + +- "third_party/instrumented_libraries": ++ 'third_party/instrumented_libraries': + Var('chromium_git') + +- "/chromium/src/third_party/instrumented_libraries.git@" + ++ '/chromium/src/third_party/instrumented_libraries.git@' + + Var('instrumented_lib_revision'), + +- "third_party/jinja2": +- Var('chromium_git') + "/chromium/src/third_party/jinja2.git@" + ++ 'third_party/jinja2': ++ Var('chromium_git') + '/chromium/src/third_party/jinja2.git@' + + Var('jinja2_revision'), + +- "third_party/markupsafe": +- Var('chromium_git') + "/chromium/src/third_party/markupsafe.git@" + ++ 'third_party/libjpeg_turbo': ++ Var('chromium_git') + '/chromium/deps/libjpeg_turbo.git@' + ++ Var('jpeg_turbo_revision'), ++ ++ 'third_party/libpng': ++ Var('chromium_git') + '/chromium/src/third_party/libpng.git@' + ++ Var('libpng_revision'), ++ ++ 'third_party/lss': { ++ 'url': Var('chromium_git') + '/linux-syscall-support.git' + '@' + Var('lss_revision'), ++ 'condition': 'checkout_android or checkout_linux', ++ }, ++ ++ 'third_party/markupsafe': ++ Var('chromium_git') + '/chromium/src/third_party/markupsafe.git@' + + Var('markupsafe_revision'), + +- "third_party/libjpeg_turbo": +- Var('chromium_git') + "/chromium/deps/libjpeg_turbo.git@" + +- Var('jpeg_turbo_revision'), ++ 'third_party/nasm': ++ Var('chromium_git') + '/chromium/deps/nasm.git@' + ++ Var('nasm_source_revision'), + +- "third_party/skia": +- Var('chromium_git') + '/skia.git@' + Var('skia_revision'), ++ 'third_party/ninja': { ++ 'packages': [ ++ { ++ # https://chrome-infra-packages.appspot.com/p/infra/3pp/tools/ninja ++ 'package': 'infra/3pp/tools/ninja/${{platform}}', ++ 'version': Var('ninja_version'), ++ } ++ ], ++ 'dep_type': 'cipd', ++ }, + +- "third_party/zlib": +- Var('chromium_git') + "/chromium/src/third_party/zlib.git@" + +- Var('zlib_revision'), ++ 'third_party/skia': { ++ 'url': Var('skia_git') + '/skia.git@' + Var('skia_revision'), ++ 'condition': 'checkout_skia', ++ }, + +- 'third_party/yasm/source/patched-yasm': +- Var('chromium_git') + '/chromium/deps/yasm/patched-yasm.git@' + +- Var('yasm_source_revision'), ++ 'third_party/test_fonts': ++ Var('chromium_git') + '/chromium/src/third_party/test_fonts.git@' + ++ Var('test_fonts_revision'), + +- "tools/clang": +- Var('chromium_git') + "/chromium/src/tools/clang@" + Var('clang_revision'), ++ 'third_party/zlib': ++ Var('chromium_git') + '/chromium/src/third_party/zlib.git@' + ++ Var('zlib_revision'), ++ ++ 'tools/clang': ++ Var('chromium_git') + '/chromium/src/tools/clang@' + Var('clang_revision'), + +- "tools/code_coverage": +- Var('chromium_git') + "/chromium/src/tools/code_coverage.git@" + ++ 'tools/code_coverage': ++ Var('chromium_git') + '/chromium/src/tools/code_coverage.git@' + + Var('code_coverage_revision'), + +- "tools/memory": +- Var('chromium_git') + "/chromium/src/tools/memory@" + ++ 'tools/memory': ++ Var('chromium_git') + '/chromium/src/tools/memory@' + + Var('tools_memory_revision'), + +- "v8": +- Var('chromium_git') + "/v8/v8.git@" + Var('v8_revision'), ++ 'tools/resultdb': { ++ 'packages': [ ++ { ++ 'package': 'infra/tools/result_adapter/${{platform}}', ++ 'version': Var('resultdb_version'), ++ }, ++ ], ++ 'dep_type': 'cipd', ++ }, ++ ++ # TODO(crbug.com/pdfium/1650): Set up autorollers for goldctl. ++ 'tools/skia_goldctl/linux': { ++ 'packages': [ ++ { ++ 'package': 'skia/tools/goldctl/linux-amd64', ++ 'version': 'eZ3k373CYgRxlu4JKph6e-_7xkP02swy_jePFFMiyIQC', ++ } ++ ], ++ 'dep_type': 'cipd', ++ 'condition': 'checkout_linux', ++ }, ++ ++ 'tools/skia_goldctl/mac_amd64': { ++ 'packages': [ ++ { ++ 'package': 'skia/tools/goldctl/mac-amd64', ++ 'version': 'nHUjLIViYsLxRjv-zDdmzqT8p1R3VoyHq5gdGkKeMYwC', ++ } ++ ], ++ 'dep_type': 'cipd', ++ 'condition': 'checkout_mac', ++ }, ++ ++ 'tools/skia_goldctl/mac_arm64': { ++ 'packages': [ ++ { ++ 'package': 'skia/tools/goldctl/mac-arm64', ++ 'version': '-mc865SGfJAqreLZM6fkn8tgCJ7u5QLk5zm7r-ZRJ9gC', ++ } ++ ], ++ 'dep_type': 'cipd', ++ 'condition': 'checkout_mac', ++ }, ++ ++ 'tools/skia_goldctl/win': { ++ 'packages': [ ++ { ++ 'package': 'skia/tools/goldctl/windows-amd64', ++ 'version': 'iEqqRADI7znrc6pG-MVnc5pBZwD25koILREPC6x2AFAC', ++ } ++ ], ++ 'dep_type': 'cipd', ++ 'condition': 'checkout_win', ++ }, ++ ++ 'v8': { ++ 'url': Var('chromium_git') + '/v8/v8.git@' + Var('v8_revision'), ++ 'condition': 'checkout_v8', ++ }, ++ + } + +-recursedeps = [ +- # buildtools provides clang_format, libc++, and libc++abi +- 'buildtools', +-] ++recursedeps = [] + + include_rules = [ + # Basic stuff that everyone can use. +@@ -135,27 +432,45 @@ include_rules = [ + '+constants', + '+testing', + '+third_party/base', ++ ++ # Abseil features must be allowlisted explicitly for now. See Chromium's ++ # //styleguide/c++/c++11.html. Allowed features' headers will be listed ++ # explicitly here. ++ '-absl', ++ '-third_party/abseil-cpp', ++ '+third_party/abseil-cpp/absl/types/optional.h', ++ '+third_party/abseil-cpp/absl/types/variant.h', + ] + + specific_include_rules = { + # Allow embedder tests to use public APIs. +- "(.*embeddertest\.cpp)": [ +- "+public", ++ '(.*embeddertest\.cpp)': [ ++ '+public', + ] + } + + hooks = [ ++ { ++ # Ensure that the DEPS'd "depot_tools" has its self-update capability ++ # disabled. ++ 'name': 'disable_depot_tools_selfupdate', ++ 'pattern': '.', ++ 'action': [ 'python3', ++ 'third_party/depot_tools/update_depot_tools_toggle.py', ++ '--disable', ++ ], ++ }, + { + # Case-insensitivity for the Win SDK. Must run before win_toolchain below. + 'name': 'ciopfs_linux', + 'pattern': '.', + 'condition': 'checkout_win and host_os == "linux"', +- 'action': [ 'python', +- 'pdfium/third_party/depot_tools/download_from_google_storage.py', ++ 'action': [ 'python3', ++ 'third_party/depot_tools/download_from_google_storage.py', + '--no_resume', + '--no_auth', + '--bucket', 'chromium-browser-clang/ciopfs', +- '-s', 'pdfium/build/ciopfs.sha1', ++ '-s', 'build/ciopfs.sha1', + ] + }, + { +@@ -163,129 +478,177 @@ hooks = [ + 'name': 'win_toolchain', + 'pattern': '.', + 'condition': 'checkout_win', +- 'action': ['python', 'pdfium/build/vs_toolchain.py', 'update', '--force'], ++ 'action': ['python3', 'build/vs_toolchain.py', 'update', '--force'], + }, + { + # Update the Mac toolchain if necessary. + 'name': 'mac_toolchain', + 'pattern': '.', +- 'action': ['python', 'pdfium/build/mac_toolchain.py'], ++ 'condition': 'checkout_mac', ++ 'action': ['python3', 'build/mac_toolchain.py'], + }, ++ # Pull dsymutil binaries using checked-in hashes. ++ { ++ 'name': 'dsymutil_mac_arm64', ++ 'pattern': '.', ++ 'condition': 'host_os == "mac" and host_cpu == "arm64"', ++ 'action': [ 'python3', ++ 'third_party/depot_tools/download_from_google_storage.py', ++ '--no_resume', ++ '--no_auth', ++ '--bucket', 'chromium-browser-clang', ++ '-s', 'tools/clang/dsymutil/bin/dsymutil.arm64.sha1', ++ '-o', 'tools/clang/dsymutil/bin/dsymutil', ++ ], ++ }, ++ { ++ 'name': 'dsymutil_mac_x64', ++ 'pattern': '.', ++ 'condition': 'host_os == "mac" and host_cpu == "x64"', ++ 'action': [ 'python3', ++ 'third_party/depot_tools/download_from_google_storage.py', ++ '--no_resume', ++ '--no_auth', ++ '--bucket', 'chromium-browser-clang', ++ '-s', 'tools/clang/dsymutil/bin/dsymutil.x64.sha1', ++ '-o', 'tools/clang/dsymutil/bin/dsymutil', ++ ], ++ }, ++ # Pull clang-format binaries using checked-in hashes. + { +- # Pull clang-format binaries using checked-in hashes. + 'name': 'clang_format_win', + 'pattern': '.', +- 'action': [ 'download_from_google_storage', ++ 'condition': 'host_os == "win"', ++ 'action': [ 'python3', ++ 'third_party/depot_tools/download_from_google_storage.py', + '--no_resume', +- '--platform=win32', + '--no_auth', + '--bucket', 'chromium-clang-format', +- '-s', 'pdfium/buildtools/win/clang-format.exe.sha1', ++ '-s', 'buildtools/win/clang-format.exe.sha1', + ], + }, + { +- 'name': 'clang_format_mac', ++ 'name': 'clang_format_mac_x64', + 'pattern': '.', +- 'action': [ 'download_from_google_storage', ++ 'condition': 'host_os == "mac" and host_cpu == "x64"', ++ 'action': [ 'python3', ++ 'third_party/depot_tools/download_from_google_storage.py', + '--no_resume', +- '--platform=darwin', + '--no_auth', + '--bucket', 'chromium-clang-format', +- '-s', 'pdfium/buildtools/mac/clang-format.sha1', ++ '-s', 'buildtools/mac/clang-format.x64.sha1', ++ '-o', 'buildtools/mac/clang-format', + ], + }, + { +- 'name': 'clang_format_linux', ++ 'name': 'clang_format_mac_arm64', + 'pattern': '.', +- 'action': [ 'download_from_google_storage', ++ 'condition': 'host_os == "mac" and host_cpu == "arm64"', ++ 'action': [ 'python3', ++ 'third_party/depot_tools/download_from_google_storage.py', + '--no_resume', +- '--platform=linux*', + '--no_auth', + '--bucket', 'chromium-clang-format', +- '-s', 'pdfium/buildtools/linux64/clang-format.sha1', ++ '-s', 'buildtools/mac/clang-format.arm64.sha1', ++ '-o', 'buildtools/mac/clang-format', + ], + }, + { +- # Note: On Win, this should run after win_toolchain, as it may use it. +- 'name': 'clang', ++ 'name': 'clang_format_linux', + 'pattern': '.', +- 'action': ['python', +- 'pdfium/tools/clang/scripts/update.py' ++ 'condition': 'host_os == "linux"', ++ 'action': [ 'python3', ++ 'third_party/depot_tools/download_from_google_storage.py', ++ '--no_resume', ++ '--no_auth', ++ '--bucket', 'chromium-clang-format', ++ '-s', 'buildtools/linux64/clang-format.sha1', + ], + }, + { +- 'name': 'binutils', +- 'pattern': 'src/third_party/binutils', +- 'condition': 'host_os == "linux"', +- 'action': [ +- 'python', +- 'pdfium/third_party/binutils/download.py', ++ # Note: On Win, this should run after win_toolchain, as it may use it. ++ 'name': 'clang', ++ 'pattern': '.', ++ 'action': ['python3', ++ 'tools/clang/scripts/update.py' + ], + }, + { + 'name': 'sysroot_arm', + 'pattern': '.', + 'condition': 'checkout_linux and checkout_arm', +- 'action': ['python', 'pdfium/build/linux/sysroot_scripts/install-sysroot.py', ++ 'action': ['python3', 'build/linux/sysroot_scripts/install-sysroot.py', + '--arch=arm'], + }, + { + 'name': 'sysroot_arm64', + 'pattern': '.', + 'condition': 'checkout_linux and checkout_arm64', +- 'action': ['python', 'pdfium/build/linux/sysroot_scripts/install-sysroot.py', ++ 'action': ['python3', 'build/linux/sysroot_scripts/install-sysroot.py', + '--arch=arm64'], + }, + { + 'name': 'sysroot_x86', + 'pattern': '.', + 'condition': 'checkout_linux and (checkout_x86 or checkout_x64)', +- 'action': ['python', 'pdfium/build/linux/sysroot_scripts/install-sysroot.py', ++ 'action': ['python3', 'build/linux/sysroot_scripts/install-sysroot.py', + '--arch=x86'], + }, + { + 'name': 'sysroot_mips', + 'pattern': '.', + 'condition': 'checkout_linux and checkout_mips', +- 'action': ['python', 'pdfium/build/linux/sysroot_scripts/install-sysroot.py', ++ 'action': ['python3', 'build/linux/sysroot_scripts/install-sysroot.py', + '--arch=mips'], + }, + { + 'name': 'sysroot_x64', + 'pattern': '.', + 'condition': 'checkout_linux and checkout_x64', +- 'action': ['python', 'pdfium/build/linux/sysroot_scripts/install-sysroot.py', ++ 'action': ['python3', 'build/linux/sysroot_scripts/install-sysroot.py', + '--arch=x64'], + }, + { +- 'name': 'msan_chained_origins', ++ 'name': 'test_fonts', ++ 'pattern': '.', ++ 'action': [ 'python3', ++ 'third_party/depot_tools/download_from_google_storage.py', ++ '--no_resume', ++ '--extract', ++ '--no_auth', ++ '--bucket', 'chromium-fonts', ++ '-s', 'third_party/test_fonts/test_fonts.tar.gz.sha1', ++ ], ++ }, ++ { ++ 'name': 'msan_chained_origins_focal', + 'pattern': '.', + 'condition': 'checkout_instrumented_libraries', +- 'action': [ 'python', +- 'pdfium/third_party/depot_tools/download_from_google_storage.py', +- "--no_resume", +- "--no_auth", +- "--bucket", "chromium-instrumented-libraries", +- "-s", "pdfium/third_party/instrumented_libraries/binaries/msan-chained-origins-trusty.tgz.sha1", ++ 'action': [ 'python3', ++ 'third_party/depot_tools/download_from_google_storage.py', ++ '--no_resume', ++ '--no_auth', ++ '--bucket', 'chromium-instrumented-libraries', ++ '-s', 'third_party/instrumented_libraries/binaries/msan-chained-origins-focal.tgz.sha1', + ], + }, + { +- 'name': 'msan_no_origins', ++ 'name': 'msan_no_origins_focal', + 'pattern': '.', + 'condition': 'checkout_instrumented_libraries', +- 'action': [ 'python', +- 'pdfium/third_party/depot_tools/download_from_google_storage.py', +- "--no_resume", +- "--no_auth", +- "--bucket", "chromium-instrumented-libraries", +- "-s", "pdfium/third_party/instrumented_libraries/binaries/msan-no-origins-trusty.tgz.sha1", ++ 'action': [ 'python3', ++ 'third_party/depot_tools/download_from_google_storage.py', ++ '--no_resume', ++ '--no_auth', ++ '--bucket', 'chromium-instrumented-libraries', ++ '-s', 'third_party/instrumented_libraries/binaries/msan-no-origins-focal.tgz.sha1', + ], + }, + { + # Update LASTCHANGE. + 'name': 'lastchange', + 'pattern': '.', +- 'action': ['python', 'pdfium/build/util/lastchange.py', +- '-o', 'pdfium/build/util/LASTCHANGE'], ++ 'action': ['python3', 'build/util/lastchange.py', ++ '-o', 'build/util/LASTCHANGE'], + }, + ] +diff --git a/DIR_METADATA b/DIR_METADATA +new file mode 100644 +index 000000000..507a6cad6 +--- /dev/null ++++ b/DIR_METADATA +@@ -0,0 +1,3 @@ ++monorail { ++ component: "Internals>Plugins>PDF" ++} +diff --git a/LICENSE b/LICENSE +index bb158cbf4..161ca160d 100644 +--- a/LICENSE ++++ b/LICENSE +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // + // Redistribution and use in source and binary forms, with or without + // modification, are permitted provided that the following conditions are +diff --git a/METADATA b/METADATA +deleted file mode 100644 +index 2cfbeccd6..000000000 +--- a/METADATA ++++ /dev/null +@@ -1,9 +0,0 @@ +-# *** THIS PACKAGE HAS SPECIAL LICENSING CONDITIONS. PLEASE +-# CONSULT THE OWNERS AND opensource-licensing@google.com BEFORE +-# DEPENDING ON IT IN YOUR PROJECT. *** +-third_party { +- # would be NOTICE save for OFL 1.1 in: +- # testing/resources/pixel/bug_925736.pdf +- # testing/resources/text_font.pdf +- license_type: BY_EXCEPTION_ONLY +-} +diff --git a/PRESUBMIT.py b/PRESUBMIT.py +index 9ad4abe32..236f8964e 100644 +--- a/PRESUBMIT.py ++++ b/PRESUBMIT.py +@@ -1,4 +1,4 @@ +-# Copyright 2015 The Chromium Authors. All rights reserved. ++# Copyright 2015 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -8,6 +8,10 @@ See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts + for more details about the presubmit API built into depot_tools. + """ + ++PRESUBMIT_VERSION = '2.0.0' ++ ++USE_PYTHON3 = True ++ + LINT_FILTERS = [ + # Rvalue ref checks are unreliable. + '-build/c++11', +@@ -34,6 +38,115 @@ _INCLUDE_ORDER_WARNING = ( + 'cppguide.html#Names_and_Order_of_Includes') + + ++# Bypass the AUTHORS check for these accounts. ++_KNOWN_ROBOTS = set() | set( ++ '%s@skia-public.iam.gserviceaccount.com' % s for s in ('pdfium-autoroll',)) ++ ++_THIRD_PARTY = 'third_party/' ++ ++# Format: Sequence of tuples containing: ++# * String pattern or, if starting with a slash, a regular expression. ++# * Sequence of strings to show when the pattern matches. ++# * Error flag. True if a match is a presubmit error, otherwise it's a warning. ++# * Sequence of paths to *not* check (regexps). ++_BANNED_CPP_FUNCTIONS = ( ++ ( ++ r'/\busing namespace ', ++ ( ++ 'Using directives ("using namespace x") are banned by the Google', ++ 'Style Guide (', ++ 'https://google.github.io/styleguide/cppguide.html#Namespaces ).', ++ 'Explicitly qualify symbols or use using declarations ("using', ++ 'x::foo").', ++ ), ++ True, ++ [_THIRD_PARTY], ++ ), ++ ( ++ r'/v8::Isolate::(?:|Try)GetCurrent()', ++ ( ++ 'v8::Isolate::GetCurrent() and v8::Isolate::TryGetCurrent() are', ++ 'banned. Hold a pointer to the v8::Isolate that was entered. Use', ++ 'v8::Isolate::IsCurrent() to check whether a given v8::Isolate is', ++ 'entered.', ++ ), ++ True, ++ (), ++ ), ++) ++ ++ ++def _CheckNoBannedFunctions(input_api, output_api): ++ """Makes sure that banned functions are not used.""" ++ warnings = [] ++ errors = [] ++ ++ def _GetMessageForMatchingType(input_api, affected_file, line_number, line, ++ type_name, message): ++ """Returns an string composed of the name of the file, the line number where ++ the match has been found and the additional text passed as `message` in case ++ the target type name matches the text inside the line passed as parameter. ++ """ ++ result = [] ++ ++ if input_api.re.search(r"^ *//", ++ line): # Ignore comments about banned types. ++ return result ++ if line.endswith( ++ " nocheck"): # A // nocheck comment will bypass this error. ++ return result ++ ++ matched = False ++ if type_name[0:1] == '/': ++ regex = type_name[1:] ++ if input_api.re.search(regex, line): ++ matched = True ++ elif type_name in line: ++ matched = True ++ ++ if matched: ++ result.append(' %s:%d:' % (affected_file.LocalPath(), line_number)) ++ for message_line in message: ++ result.append(' %s' % message_line) ++ ++ return result ++ ++ def IsExcludedFile(affected_file, excluded_paths): ++ local_path = affected_file.LocalPath() ++ for item in excluded_paths: ++ if input_api.re.match(item, local_path): ++ return True ++ return False ++ ++ def CheckForMatch(affected_file, line_num, line, func_name, message, error): ++ problems = _GetMessageForMatchingType(input_api, f, line_num, line, ++ func_name, message) ++ if problems: ++ if error: ++ errors.extend(problems) ++ else: ++ warnings.extend(problems) ++ ++ file_filter = lambda f: f.LocalPath().endswith(('.cc', '.cpp', '.h')) ++ for f in input_api.AffectedFiles(file_filter=file_filter): ++ for line_num, line in f.ChangedContents(): ++ for func_name, message, error, excluded_paths in _BANNED_CPP_FUNCTIONS: ++ if IsExcludedFile(f, excluded_paths): ++ continue ++ CheckForMatch(f, line_num, line, func_name, message, error) ++ ++ result = [] ++ if (warnings): ++ result.append( ++ output_api.PresubmitPromptWarning('Banned functions were used.\n' + ++ '\n'.join(warnings))) ++ if (errors): ++ result.append( ++ output_api.PresubmitError('Banned functions were used.\n' + ++ '\n'.join(errors))) ++ return result ++ ++ + def _CheckUnwantedDependencies(input_api, output_api): + """Runs checkdeps on #include statements added in this + change. Breaking - rules is an error, breaking ! rules is a +@@ -246,12 +359,8 @@ def _CheckIncludeOrder(input_api, output_api): + Each region separated by #if, #elif, #else, #endif, #define and #undef follows + these rules separately. + """ +- def FileFilterIncludeOrder(affected_file): +- black_list = (input_api.DEFAULT_BLACK_LIST) +- return input_api.FilterSourceFile(affected_file, black_list=black_list) +- + warnings = [] +- for f in input_api.AffectedFiles(file_filter=FileFilterIncludeOrder): ++ for f in input_api.AffectedFiles(file_filter=input_api.FilterSourceFile): + if f.LocalPath().endswith(('.cc', '.cpp', '.h', '.mm')): + changed_linenums = set(line_num for line_num, _ in f.ChangedContents()) + warnings.extend(_CheckIncludeOrderInFile(input_api, f, changed_linenums)) +@@ -262,6 +371,29 @@ def _CheckIncludeOrder(input_api, output_api): + warnings)) + return results + ++ ++def _CheckLibcxxRevision(input_api, output_api): ++ """Makes sure that libcxx_revision is set correctly.""" ++ if 'DEPS' not in [f.LocalPath() for f in input_api.AffectedFiles()]: ++ return [] ++ ++ script_path = input_api.os_path.join('testing', 'tools', 'libcxx_check.py') ++ buildtools_deps_path = input_api.os_path.join('buildtools', ++ 'deps_revisions.gni') ++ ++ try: ++ errors = input_api.subprocess.check_output( ++ [script_path, 'DEPS', buildtools_deps_path]) ++ except input_api.subprocess.CalledProcessError as error: ++ msg = 'libcxx_check.py failed:' ++ long_text = error.output.decode('utf-8', 'ignore') ++ return [output_api.PresubmitError(msg, long_text=long_text)] ++ ++ if errors: ++ return [output_api.PresubmitError(errors)] ++ return [] ++ ++ + def _CheckTestDuplicates(input_api, output_api): + """Checks that pixel and javascript tests don't contain duplicates. + We use .in and .pdf files, having both can cause race conditions on the bots, +@@ -291,33 +423,136 @@ def _CheckTestDuplicates(input_api, output_api): + tests_added.append(path) + return results + +-def _CheckPNGFormat(input_api, output_api): +- """Checks that .png files have a format that will be considered valid by our +- test runners. If a file ends with .png, then it must be of the form +- NAME_expected(_(win|mac|linux))?.pdf.#.png""" ++ ++def _CheckPngNames(input_api, output_api): ++ """Checks that .png files have the right file name format, which must be in ++ the form: ++ ++ NAME_expected(_(agg|skia))?(_(linux|mac|win))?.pdf.\d+.png ++ ++ This must be the same format as the one in testing/corpus's PRESUBMIT.py. ++ """ + expected_pattern = input_api.re.compile( +- r'.+_expected(_(win|mac|linux))?\.pdf\.\d+.png') ++ r'.+_expected(_(agg|skia))?(_(linux|mac|win))?\.pdf\.\d+.png') + results = [] + for f in input_api.AffectedFiles(include_deletes=False): + if not f.LocalPath().endswith('.png'): + continue + if expected_pattern.match(f.LocalPath()): + continue +- results.append(output_api.PresubmitError( +- 'PNG file %s does not have the correct format' % f.LocalPath())) ++ results.append( ++ output_api.PresubmitError( ++ 'PNG file %s does not have the correct format' % f.LocalPath())) + return results + +-def CheckChangeOnUpload(input_api, output_api): +- cpp_source_filter = lambda x: input_api.FilterSourceFile( +- x, white_list=(r'\.(?:c|cc|cpp|h)$',)) + ++def _CheckUselessForwardDeclarations(input_api, output_api): ++ """Checks that added or removed lines in non third party affected ++ header files do not lead to new useless class or struct forward ++ declaration. ++ """ + results = [] +- results += _CheckUnwantedDependencies(input_api, output_api) +- results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api) +- results += input_api.canned_checks.CheckChangeLintsClean( +- input_api, output_api, cpp_source_filter, LINT_FILTERS) +- results += _CheckIncludeOrder(input_api, output_api) +- results += _CheckTestDuplicates(input_api, output_api) +- results += _CheckPNGFormat(input_api, output_api) ++ class_pattern = input_api.re.compile(r'^class\s+(\w+);$', ++ input_api.re.MULTILINE) ++ struct_pattern = input_api.re.compile(r'^struct\s+(\w+);$', ++ input_api.re.MULTILINE) ++ for f in input_api.AffectedFiles(include_deletes=False): ++ if f.LocalPath().startswith('third_party'): ++ continue ++ ++ if not f.LocalPath().endswith('.h'): ++ continue ++ ++ contents = input_api.ReadFile(f) ++ fwd_decls = input_api.re.findall(class_pattern, contents) ++ fwd_decls.extend(input_api.re.findall(struct_pattern, contents)) ++ ++ useless_fwd_decls = [] ++ for decl in fwd_decls: ++ count = sum( ++ 1 ++ for _ in input_api.re.finditer(r'\b%s\b' % ++ input_api.re.escape(decl), contents)) ++ if count == 1: ++ useless_fwd_decls.append(decl) ++ ++ if not useless_fwd_decls: ++ continue ++ ++ for line in f.GenerateScmDiff().splitlines(): ++ if (line.startswith('-') and not line.startswith('--') or ++ line.startswith('+') and not line.startswith('++')): ++ for decl in useless_fwd_decls: ++ if input_api.re.search(r'\b%s\b' % decl, line[1:]): ++ results.append( ++ output_api.PresubmitPromptWarning( ++ '%s: %s forward declaration is no longer needed' % ++ (f.LocalPath(), decl))) ++ useless_fwd_decls.remove(decl) ++ ++ return results ++ ++ ++def ChecksCommon(input_api, output_api): ++ results = [] ++ ++ results.extend( ++ input_api.canned_checks.PanProjectChecks( ++ input_api, output_api, project_name='PDFium')) ++ ++ # PanProjectChecks() doesn't consider .gn/.gni files, so check those, too. ++ files_to_check = ( ++ r'.*\.gn$', ++ r'.*\.gni$', ++ ) ++ results.extend( ++ input_api.canned_checks.CheckLicense( ++ input_api, ++ output_api, ++ project_name='PDFium', ++ source_file_filter=lambda x: input_api.FilterSourceFile( ++ x, files_to_check=files_to_check))) ++ ++ return results ++ ++ ++def CheckChangeOnUpload(input_api, output_api): ++ results = [] ++ results.extend(_CheckNoBannedFunctions(input_api, output_api)) ++ results.extend(_CheckUnwantedDependencies(input_api, output_api)) ++ results.extend( ++ input_api.canned_checks.CheckPatchFormatted(input_api, output_api)) ++ results.extend( ++ input_api.canned_checks.CheckChangeLintsClean( ++ input_api, output_api, lint_filters=LINT_FILTERS)) ++ results.extend(_CheckIncludeOrder(input_api, output_api)) ++ results.extend(_CheckLibcxxRevision(input_api, output_api)) ++ results.extend(_CheckTestDuplicates(input_api, output_api)) ++ results.extend(_CheckPngNames(input_api, output_api)) ++ results.extend(_CheckUselessForwardDeclarations(input_api, output_api)) ++ ++ author = input_api.change.author_email ++ if author and author not in _KNOWN_ROBOTS: ++ results.extend( ++ input_api.canned_checks.CheckAuthorizedAuthor(input_api, output_api)) ++ ++ for f in input_api.AffectedFiles(): ++ path, name = input_api.os_path.split(f.LocalPath()) ++ if name == 'PRESUBMIT.py': ++ full_path = input_api.os_path.join(input_api.PresubmitLocalPath(), path) ++ test_file = input_api.os_path.join(path, 'PRESUBMIT_test.py') ++ if f.Action() != 'D' and input_api.os_path.exists(test_file): ++ # The PRESUBMIT.py file (and the directory containing it) might ++ # have been affected by being moved or removed, so only try to ++ # run the tests if they still exist. ++ results.extend( ++ input_api.canned_checks.RunUnitTestsInDirectory( ++ input_api, ++ output_api, ++ full_path, ++ files_to_check=[r'^PRESUBMIT_test\.py$'], ++ run_on_python2=not USE_PYTHON3, ++ run_on_python3=USE_PYTHON3, ++ skip_shebang_check=True)) + + return results +diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py +new file mode 100755 +index 000000000..c5f965043 +--- /dev/null ++++ b/PRESUBMIT_test.py +@@ -0,0 +1,86 @@ ++#!/usr/bin/env python3 ++# Copyright 2020 The PDFium Authors ++# Use of this source code is governed by a BSD-style license that can be ++# found in the LICENSE file. ++ ++import unittest ++ ++import PRESUBMIT ++from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi, MockFile ++ ++ ++class BannedTypeCheckTest(unittest.TestCase): ++ ++ def testBannedCppFunctions(self): ++ input_api = MockInputApi() ++ input_api.files = [ ++ MockFile('some/cpp/problematic/file.cc', ['using namespace std;']), ++ MockFile('third_party/some/cpp/problematic/file.cc', ++ ['using namespace std;']), ++ MockFile('some/cpp/ok/file.cc', ['using std::string;']), ++ MockFile('some/cpp/nocheck/file.cc', ++ ['using namespace std; // nocheck']), ++ MockFile('some/cpp/comment/file.cc', ++ [' // A comment about `using namespace std;`']), ++ MockFile('some/cpp/v8/get-current.cc', ['v8::Isolate::GetCurrent()']), ++ MockFile('some/cpp/v8/try-get-current.cc', ++ ['v8::Isolate::TryGetCurrent()']), ++ ] ++ ++ results = PRESUBMIT._CheckNoBannedFunctions(input_api, MockOutputApi()) ++ ++ # There are no warnings to test, so add an empty warning to keep the test ++ # extendable for the future. This block can be removed once warnings are ++ # added. ++ self.assertEqual(1, len(results)) ++ results.insert(0, MockOutputApi().PresubmitPromptWarning('')) ++ ++ # warnings are results[0], errors are results[1] ++ self.assertEqual(2, len(results)) ++ self.assertTrue('some/cpp/problematic/file.cc' in results[1].message) ++ self.assertFalse( ++ 'third_party/some/cpp/problematic/file.cc' in results[1].message) ++ self.assertFalse('some/cpp/ok/file.cc' in results[1].message) ++ self.assertFalse('some/cpp/nocheck/file.cc' in results[0].message) ++ self.assertFalse('some/cpp/nocheck/file.cc' in results[1].message) ++ self.assertFalse('some/cpp/comment/file.cc' in results[0].message) ++ self.assertFalse('some/cpp/comment/file.cc' in results[1].message) ++ self.assertTrue('some/cpp/v8/get-current.cc' in results[1].message) ++ self.assertTrue('some/cpp/v8/try-get-current.cc' in results[1].message) ++ ++ ++class CheckChangeOnUploadTest(unittest.TestCase): ++ ++ def testCheckPngNames(self): ++ correct_paths = [ ++ 'test_expected.pdf.0.png', ++ 'test_expected_win.pdf.1.png', ++ 'test_expected_agg.pdf.3.png', ++ 'test_expected_agg_linux.pdf.3.png', ++ 'test_expected_skia.pdf.2.png', ++ 'test_expected_skia_mac.pdf.4.png', ++ 'notpng.cc', # Check will be skipped for non-PNG files ++ ] ++ wrong_paths = [ ++ 'expected.pdf.0.png', # Missing '_expected' ++ 'test1_expected.0.png', # Missing '.pdf' ++ 'test2_expected.pdf.png', # Missing page number ++ 'test3_expected.pdf.x.png', # Wrong character for page number ++ 'test4_expected_linux_agg.pdf.0.png', # Wrong order of keywords ++ 'test4_expected_mac_skia.pdf.0.png', # Wrong order of keywords ++ 'test5_expected_useskia.pdf.0.png', # Wrong keyword ++ ] ++ mock_input_api = MockInputApi() ++ mock_output_api = MockOutputApi() ++ mock_input_api.files = map(MockFile, correct_paths + wrong_paths) ++ errors = list( ++ map(str, PRESUBMIT._CheckPngNames(mock_input_api, mock_output_api))) ++ ++ self.assertEqual(len(wrong_paths), len(errors)) ++ self.assertFalse('notpng.cc' in errors[0]) ++ for path, error in zip(wrong_paths, errors): ++ self.assertIn(path, error) ++ ++ ++if __name__ == '__main__': ++ unittest.main() +diff --git a/PRESUBMIT_test_mocks.py b/PRESUBMIT_test_mocks.py +new file mode 100644 +index 000000000..23e394635 +--- /dev/null ++++ b/PRESUBMIT_test_mocks.py +@@ -0,0 +1,78 @@ ++# Copyright 2020 The PDFium Authors ++# Use of this source code is governed by a BSD-style license that can be ++# found in the LICENSE file. ++ ++import re ++ ++ ++class MockInputApi(object): ++ """Mock class for the InputApi class. ++ ++ This class can be used for unittests for presubmit by initializing the files ++ attribute as the list of changed files. ++ """ ++ ++ def __init__(self): ++ self.files = [] ++ self.re = re ++ ++ def AffectedFiles(self, file_filter=None, include_deletes=False): ++ # pylint: disable=unused-argument ++ return self.files ++ ++ ++class MockOutputApi(object): ++ """Mock class for the OutputApi class. ++ ++ An instance of this class can be passed to presubmit unittests for outputting ++ various types of results. ++ """ ++ ++ class PresubmitResult(object): ++ ++ def __init__(self, message, items=None, long_text=''): ++ self.message = message ++ self.items = items ++ self.long_text = long_text ++ ++ def __repr__(self): ++ return self.message ++ ++ class PresubmitError(PresubmitResult): ++ ++ def __init__(self, message, items=None, long_text=''): ++ MockOutputApi.PresubmitResult.__init__(self, message, items, long_text) ++ self.type = 'error' ++ ++ class PresubmitPromptWarning(PresubmitResult): ++ ++ def __init__(self, message, items=None, long_text=''): ++ MockOutputApi.PresubmitResult.__init__(self, message, items, long_text) ++ self.type = 'warning' ++ ++ ++class MockFile(object): ++ """Mock class for the File class. ++ ++ This class can be used to form the mock list of changed files in ++ MockInputApi for presubmit unittests. ++ """ ++ ++ def __init__(self, ++ local_path, ++ new_contents=None, ++ old_contents=None, ++ action='A'): ++ self._local_path = local_path ++ if new_contents is None: ++ new_contents = [] ++ self._new_contents = new_contents ++ self._changed_contents = [(i + 1, l) for i, l in enumerate(new_contents)] ++ self._action = action ++ self._old_contents = old_contents ++ ++ def ChangedContents(self): ++ return self._changed_contents ++ ++ def LocalPath(self): ++ return self._local_path +diff --git a/README.md b/README.md +index a79e18d8f..657cfda9b 100644 +--- a/README.md ++++ b/README.md +@@ -2,27 +2,13 @@ + + ## Prerequisites + +-Get the Chromium depot\_tools via the +-[instructions](https://www.chromium.org/developers/how-tos/install-depot-tools). +-This provides the gclient utility needed below and many other tools needed for +-PDFium development. ++PDFium uses the same build tooling as Chromium. See the platform-specific ++Chromium build instructions to get started, but replace Chromium's ++"Get the code" instructions with [PDFium's](#get-the-code). + +-Also install Python, Subversion, and Git and make sure they're in your path. +- +- +-### Windows development +- +-PDFium uses the same build tool as Chromium: +- +-#### Open source contributors +-Please refer to +-[Chromium's Visual Studio set up](https://chromium.googlesource.com/chromium/src/+/master/docs/windows_build_instructions.md#visual-studio) +-for requirements and instructions on build environment configuration. +- +-Run `set DEPOT_TOOLS_WIN_TOOLCHAIN=0`, or set that variable in your global +-environment. +- +-Compilation is done through Ninja, **not** Visual Studio. ++* [Chromium Linux build instructions](https://chromium.googlesource.com/chromium/src/+/main/docs/linux/build_instructions.md) ++* [Chromium Mac build instructions](https://chromium.googlesource.com/chromium/src/+/main/docs/mac_build_instructions.md) ++* [Chromium Windows build instructions](https://chromium.googlesource.com/chromium/src/+/main/docs/windows_build_instructions.md) + + ### CPU Architectures supported + +@@ -42,7 +28,7 @@ authentication instructions. **Note that you must authenticate with your + @google.com credentials**. Enter "0" if asked for a project-id. + + Once you've done this, the toolchain will be installed automatically for +-you in the [Generate the build files](#GenBuild) step below. ++you in the [Generate the build files](#generate-the-build-files) step below. + + The toolchain will be in `depot_tools\win_toolchain\vs_files\`, and + windbg can be found in +@@ -53,9 +39,10 @@ it separately, but this is optional and not needed for building PDFium. + + ## Get the code + +-The name of the top-level directory does not matter. In our examples, we use +-"repo". This directory must not have been used before by `gclient config` as +-each directory can only house a single gclient configuration. ++The name of the top-level directory does not matter. In the following example, ++the directory name is "repo". This directory must not have been used before by ++`gclient config` as each directory can only house a single gclient ++configuration. + + ``` + mkdir repo +@@ -65,8 +52,8 @@ gclient sync + cd pdfium + ``` + +-Additional build dependencies need to be installed by running the following from +-the `pdfium` directory. ++On Linux, additional build dependencies need to be installed by running the ++following from the `pdfium` directory. + + ``` + ./build/install-build-deps.sh +@@ -74,7 +61,7 @@ the `pdfium` directory. + + ## Generate the build files + +-We use GN to generate the build files and [Ninja](https://ninja-build.org/) ++PDFium uses GN to generate the build files and [Ninja](https://ninja-build.org/) + to execute the build files. Both of these are included with the + depot\_tools checkout. + +@@ -96,30 +83,17 @@ is_debug = true # Enable debugging features. + + # Set true to enable experimental Skia backend. + pdf_use_skia = false +-# Set true to enable experimental Skia backend (paths only). +-pdf_use_skia_paths = false + + pdf_enable_xfa = true # Set false to remove XFA support (implies JS support). + pdf_enable_v8 = true # Set false to remove Javascript support. + pdf_is_standalone = true # Set for a non-embedded build. + is_component_build = false # Disable component build (Though it should work) +- +-clang_use_chrome_plugins = false # Currently must be false. + ``` + + For sample applications like `pdfium_test` to build, one must set + `pdf_is_standalone = true`. + +-By default, the entire project builds with C++14, because features like V8 +-support, XFA support, and the Skia backend all have dependencies on libraries +-that require C++14. If one does not need any of those features, and need to fall +-back to building in C++11 mode, then set `use_cxx11 = true`. This fallback is +-temporary and will go away in the future when PDFium fully transitions to C++14. +-See [this bug](https://crbug.com/pdfium/1407) for details. +- +-When building with the experimental Skia backend, Skia itself it built with +-C++17. There is no configuration for this. One just has to use a build toolchain +-that supports C++17. ++By default, the entire project builds with C++17. + + When complete the arguments will be stored in `/args.gn`, and + GN will automatically use the new arguments to generate build files. +@@ -190,7 +164,7 @@ easier by replacing the byte offsets with certain keywords. + This saves space and also allows an easy way to reduce the test case to the + essentials as you can simply remove everything that is not necessary. + +-A simple example can be found [here](https://pdfium.googlesource.com/pdfium/+/refs/heads/master/testing/resources/rectangles.in). ++A simple example can be found [here](https://pdfium.googlesource.com/pdfium/+/refs/heads/main/testing/resources/rectangles.in). + + To transform this into a PDF, you can use the `fixup_pdf_template.py` tool: + +@@ -208,7 +182,8 @@ make object identification easier. + ## Embedding PDFium in your own projects + + The public/ directory contains header files for the APIs available for use by +-embedders of PDFium. We endeavor to keep these as stable as possible. ++embedders of PDFium. The PDFium project endeavors to keep these as stable as ++possible. + + Outside of the public/ directory, code may change at any time, and embedders + should not directly call these routines. +@@ -223,13 +198,6 @@ Chromium provides code coverage reports for PDFium + `third_party/pdfium` in Chromium's source code. + This includes code coverage from PDFium's fuzzers. + +-## Profiling +- +-Valgrind and other profiling tools do not work correctly with the standard build +-setup that PDFium uses. You will need to add +-`ro_segment_workaround_for_valgrind=true` to `args.gn` to get symbols to +-correctly appear. +- + ## Waterfall + + The current health of the source tree can be found +@@ -247,26 +215,12 @@ Note, the Reviews and Bugs lists are typically read-only. + + ## Bugs + +- We use this +-[bug tracker](https://bugs.chromium.org/p/pdfium/issues/list), but for security +-bugs, please use ++PDFium uses this [bug tracker](https://bugs.chromium.org/p/pdfium/issues/list), ++but for security bugs, please use + [Chromium's security bug template](https://bugs.chromium.org/p/chromium/issues/entry?template=Security%20Bug) + and add the "Cr-Internals-Plugins-PDF" label. + + ## Contributing code + +-For contributing code, we will follow +-[Chromium's process](https://chromium.googlesource.com/chromium/src/+/master/docs/contributing.md) +-as much as possible. The main exceptions are: +- +-1. Code has to conform to the existing style and not Chromium/Google style. +-2. PDFium uses a different Gerrit instance for code reviews, and credentials for +-this Gerrit instance need to be generated before uploading changes. +-3. PDFium is transitioning to C++14, but still supports C++11 compatibility +-for the duration of the transition period. Prefer to use only C++11 features, +-though technically C++14 is allowed in code that is only built when V8, XFA, or +-Skia is turned on. +- +-Before submitting a fix for a bug, it can help if you create an issue in the +-bug tracker. This allows easier discussion about the problem and also helps +-with statistics tracking. ++See the [CONTRIBUTING](CONTRIBUTING.md) document for more information on ++contributing to the PDFium project. +diff --git a/build/build_config.h b/build/build_config.h +index 8165eee47..478c4cf0d 100644 +--- a/build/build_config.h ++++ b/build/build_config.h +@@ -1,7 +1,390 @@ +-#define OS_POSIX +-#define OS_ANDROID ++// Copyright 2012 The Chromium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// This file doesn't belong to any GN target by design for faster build and ++// less developer overhead. ++ ++// This file adds build flags about the OS we're currently building on. They are ++// defined directly in this file instead of via a `buildflag_header` target in a ++// GN file for faster build. They are defined using the corresponding OS defines ++// (e.g. OS_WIN) which are also defined in this file (except for OS_CHROMEOS, ++// which is set by the build system). These defines are deprecated and should ++// NOT be used directly. For example: ++// Please Use: #if BUILDFLAG(IS_WIN) ++// Deprecated: #if defined(OS_WIN) ++// ++// Operating System: ++// IS_AIX / IS_ANDROID / IS_ASMJS / IS_CHROMEOS / IS_FREEBSD / IS_FUCHSIA / ++// IS_IOS / IS_IOS_MACCATALYST / IS_LINUX / IS_MAC / IS_NACL / IS_NETBSD / ++// IS_OPENBSD / IS_QNX / IS_SOLARIS / IS_WIN ++// Operating System family: ++// IS_APPLE: IOS or MAC or IOS_MACCATALYST ++// IS_BSD: FREEBSD or NETBSD or OPENBSD ++// IS_POSIX: AIX or ANDROID or ASMJS or CHROMEOS or FREEBSD or IOS or LINUX ++// or MAC or NACL or NETBSD or OPENBSD or QNX or SOLARIS ++ ++// This file also adds defines specific to the platform, architecture etc. ++// ++// Platform: ++// IS_OZONE ++// ++// Compiler: ++// COMPILER_MSVC / COMPILER_GCC ++// ++// Processor: ++// ARCH_CPU_ARM64 / ARCH_CPU_ARMEL / ARCH_CPU_LOONGARCH32 / ++// ARCH_CPU_LOONGARCH64 / ARCH_CPU_MIPS / ARCH_CPU_MIPS64 / ++// ARCH_CPU_MIPS64EL / ARCH_CPU_MIPSEL / ARCH_CPU_PPC64 / ARCH_CPU_S390 / ++// ARCH_CPU_S390X / ARCH_CPU_X86 / ARCH_CPU_X86_64 / ARCH_CPU_RISCV64 ++// Processor family: ++// ARCH_CPU_ARM_FAMILY: ARMEL or ARM64 ++// ARCH_CPU_LOONGARCH_FAMILY: LOONGARCH32 or LOONGARCH64 ++// ARCH_CPU_MIPS_FAMILY: MIPS64EL or MIPSEL or MIPS64 or MIPS ++// ARCH_CPU_PPC64_FAMILY: PPC64 ++// ARCH_CPU_S390_FAMILY: S390 or S390X ++// ARCH_CPU_X86_FAMILY: X86 or X86_64 ++// ARCH_CPU_RISCV_FAMILY: Riscv64 ++// Processor features: ++// ARCH_CPU_31_BITS / ARCH_CPU_32_BITS / ARCH_CPU_64_BITS ++// ARCH_CPU_BIG_ENDIAN / ARCH_CPU_LITTLE_ENDIAN ++ ++#ifndef BUILD_BUILD_CONFIG_H_ ++#define BUILD_BUILD_CONFIG_H_ ++ ++#include "build/buildflag.h" // IWYU pragma: export ++ ++// A set of macros to use for platform detection. ++#if defined(__native_client__) ++// __native_client__ must be first, so that other OS_ defines are not set. ++#define OS_NACL 1 ++#elif defined(ANDROID) ++#define OS_ANDROID 1 ++#elif defined(__APPLE__) ++// Only include TargetConditionals after testing ANDROID as some Android builds ++// on the Mac have this header available and it's not needed unless the target ++// is really an Apple platform. ++#include ++#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE ++#define OS_IOS 1 ++// Catalyst is the technology that allows running iOS apps on macOS. These ++// builds are both OS_IOS and OS_IOS_MACCATALYST. ++#if defined(TARGET_OS_MACCATALYST) && TARGET_OS_MACCATALYST ++#define OS_IOS_MACCATALYST ++#endif // defined(TARGET_OS_MACCATALYST) && TARGET_OS_MACCATALYST ++#else ++#define OS_MAC 1 ++#endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE ++#elif defined(__linux__) ++#if !defined(OS_CHROMEOS) ++// Do not define OS_LINUX on Chrome OS build. ++// The OS_CHROMEOS macro is defined in GN. ++#define OS_LINUX 1 ++#endif // !defined(OS_CHROMEOS) ++// Include a system header to pull in features.h for glibc/uclibc macros. ++#include ++#if defined(__GLIBC__) && !defined(__UCLIBC__) ++// We really are using glibc, not uClibc pretending to be glibc. ++#define LIBC_GLIBC 1 ++#endif ++#elif defined(_WIN32) ++#define OS_WIN 1 ++#elif defined(__Fuchsia__) ++#define OS_FUCHSIA 1 ++#elif defined(__FreeBSD__) ++#define OS_FREEBSD 1 ++#elif defined(__NetBSD__) ++#define OS_NETBSD 1 ++#elif defined(__OpenBSD__) ++#define OS_OPENBSD 1 ++#elif defined(__sun) ++#define OS_SOLARIS 1 ++#elif defined(__QNXNTO__) ++#define OS_QNX 1 ++#elif defined(_AIX) ++#define OS_AIX 1 ++#elif defined(__asmjs__) || defined(__wasm__) ++#define OS_ASMJS 1 ++#elif defined(__MVS__) ++#define OS_ZOS 1 ++#else ++#error Please add support for your platform in build/build_config.h ++#endif ++// NOTE: Adding a new port? Please follow ++// https://chromium.googlesource.com/chromium/src/+/main/docs/new_port_policy.md ++ ++#if defined(OS_MAC) || defined(OS_IOS) ++#define OS_APPLE 1 ++#endif ++ ++// For access to standard BSD features, use OS_BSD instead of a ++// more specific macro. ++#if defined(OS_FREEBSD) || defined(OS_NETBSD) || defined(OS_OPENBSD) ++#define OS_BSD 1 ++#endif ++ ++// For access to standard POSIXish features, use OS_POSIX instead of a ++// more specific macro. ++#if defined(OS_AIX) || defined(OS_ANDROID) || defined(OS_ASMJS) || \ ++ defined(OS_FREEBSD) || defined(OS_IOS) || defined(OS_LINUX) || \ ++ defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_NACL) || \ ++ defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_QNX) || \ ++ defined(OS_SOLARIS) || defined(OS_ZOS) ++#define OS_POSIX 1 ++#endif ++ ++// OS build flags ++#if defined(OS_AIX) ++#define BUILDFLAG_INTERNAL_IS_AIX() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_AIX() (0) ++#endif ++ ++#if defined(OS_ANDROID) ++#define BUILDFLAG_INTERNAL_IS_ANDROID() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_ANDROID() (0) ++#endif ++ ++#if defined(OS_APPLE) ++#define BUILDFLAG_INTERNAL_IS_APPLE() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_APPLE() (0) ++#endif ++ ++#if defined(OS_ASMJS) ++#define BUILDFLAG_INTERNAL_IS_ASMJS() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_ASMJS() (0) ++#endif ++ ++#if defined(OS_BSD) ++#define BUILDFLAG_INTERNAL_IS_BSD() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_BSD() (0) ++#endif ++ ++#if defined(OS_CHROMEOS) ++#define BUILDFLAG_INTERNAL_IS_CHROMEOS() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_CHROMEOS() (0) ++#endif ++ ++#if defined(OS_FREEBSD) ++#define BUILDFLAG_INTERNAL_IS_FREEBSD() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_FREEBSD() (0) ++#endif ++ ++#if defined(OS_FUCHSIA) ++#define BUILDFLAG_INTERNAL_IS_FUCHSIA() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_FUCHSIA() (0) ++#endif ++ ++#if defined(OS_IOS) ++#define BUILDFLAG_INTERNAL_IS_IOS() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_IOS() (0) ++#endif ++ ++#if defined(OS_IOS_MACCATALYST) ++#define BUILDFLAG_INTERNAL_IS_IOS_MACCATALYST() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_IOS_MACCATALYST() (0) ++#endif ++ ++#if defined(OS_LINUX) ++#define BUILDFLAG_INTERNAL_IS_LINUX() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_LINUX() (0) ++#endif ++ ++#if defined(OS_MAC) ++#define BUILDFLAG_INTERNAL_IS_MAC() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_MAC() (0) ++#endif ++ ++#if defined(OS_NACL) ++#define BUILDFLAG_INTERNAL_IS_NACL() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_NACL() (0) ++#endif ++ ++#if defined(OS_NETBSD) ++#define BUILDFLAG_INTERNAL_IS_NETBSD() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_NETBSD() (0) ++#endif ++ ++#if defined(OS_OPENBSD) ++#define BUILDFLAG_INTERNAL_IS_OPENBSD() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_OPENBSD() (0) ++#endif ++ ++#if defined(OS_POSIX) ++#define BUILDFLAG_INTERNAL_IS_POSIX() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_POSIX() (0) ++#endif ++ ++#if defined(OS_QNX) ++#define BUILDFLAG_INTERNAL_IS_QNX() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_QNX() (0) ++#endif ++ ++#if defined(OS_SOLARIS) ++#define BUILDFLAG_INTERNAL_IS_SOLARIS() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_SOLARIS() (0) ++#endif ++ ++#if defined(OS_WIN) ++#define BUILDFLAG_INTERNAL_IS_WIN() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_WIN() (0) ++#endif ++ ++#if defined(USE_OZONE) ++#define BUILDFLAG_INTERNAL_IS_OZONE() (1) ++#else ++#define BUILDFLAG_INTERNAL_IS_OZONE() (0) ++#endif ++ ++// Compiler detection. Note: clang masquerades as GCC on POSIX and as MSVC on ++// Windows. ++#if defined(__GNUC__) ++#define COMPILER_GCC 1 ++#elif defined(_MSC_VER) ++#define COMPILER_MSVC 1 ++#else ++#error Please add support for your compiler in build/build_config.h ++#endif ++ ++// Processor architecture detection. For more info on what's defined, see: ++// http://msdn.microsoft.com/en-us/library/b0084kay.aspx ++// http://www.agner.org/optimize/calling_conventions.pdf ++// or with gcc, run: "echo | gcc -E -dM -" ++#if defined(_M_X64) || defined(__x86_64__) ++#define ARCH_CPU_X86_FAMILY 1 ++#define ARCH_CPU_X86_64 1 ++#define ARCH_CPU_64_BITS 1 ++#define ARCH_CPU_LITTLE_ENDIAN 1 ++#elif defined(_M_IX86) || defined(__i386__) ++#define ARCH_CPU_X86_FAMILY 1 ++#define ARCH_CPU_X86 1 ++#define ARCH_CPU_32_BITS 1 ++#define ARCH_CPU_LITTLE_ENDIAN 1 ++#elif defined(__s390x__) ++#define ARCH_CPU_S390_FAMILY 1 ++#define ARCH_CPU_S390X 1 ++#define ARCH_CPU_64_BITS 1 ++#define ARCH_CPU_BIG_ENDIAN 1 ++#elif defined(__s390__) ++#define ARCH_CPU_S390_FAMILY 1 ++#define ARCH_CPU_S390 1 ++#define ARCH_CPU_31_BITS 1 ++#define ARCH_CPU_BIG_ENDIAN 1 ++#elif (defined(__PPC64__) || defined(__PPC__)) && defined(__BIG_ENDIAN__) ++#define ARCH_CPU_PPC64_FAMILY 1 ++#define ARCH_CPU_PPC64 1 ++#define ARCH_CPU_64_BITS 1 ++#define ARCH_CPU_BIG_ENDIAN 1 ++#elif defined(__PPC64__) ++#define ARCH_CPU_PPC64_FAMILY 1 ++#define ARCH_CPU_PPC64 1 ++#define ARCH_CPU_64_BITS 1 ++#define ARCH_CPU_LITTLE_ENDIAN 1 ++#elif defined(__ARMEL__) ++#define ARCH_CPU_ARM_FAMILY 1 ++#define ARCH_CPU_ARMEL 1 ++#define ARCH_CPU_32_BITS 1 ++#define ARCH_CPU_LITTLE_ENDIAN 1 ++#elif defined(__aarch64__) || defined(_M_ARM64) ++#define ARCH_CPU_ARM_FAMILY 1 ++#define ARCH_CPU_ARM64 1 ++#define ARCH_CPU_64_BITS 1 ++#define ARCH_CPU_LITTLE_ENDIAN 1 ++#elif defined(__pnacl__) || defined(__asmjs__) || defined(__wasm__) ++#define ARCH_CPU_32_BITS 1 ++#define ARCH_CPU_LITTLE_ENDIAN 1 ++#elif defined(__MIPSEL__) ++#if defined(__LP64__) ++#define ARCH_CPU_MIPS_FAMILY 1 ++#define ARCH_CPU_MIPS64EL 1 ++#define ARCH_CPU_64_BITS 1 ++#define ARCH_CPU_LITTLE_ENDIAN 1 ++#else ++#define ARCH_CPU_MIPS_FAMILY 1 ++#define ARCH_CPU_MIPSEL 1 ++#define ARCH_CPU_32_BITS 1 ++#define ARCH_CPU_LITTLE_ENDIAN 1 ++#endif ++#elif defined(__MIPSEB__) ++#if defined(__LP64__) ++#define ARCH_CPU_MIPS_FAMILY 1 ++#define ARCH_CPU_MIPS64 1 ++#define ARCH_CPU_64_BITS 1 ++#define ARCH_CPU_BIG_ENDIAN 1 ++#else ++#define ARCH_CPU_MIPS_FAMILY 1 ++#define ARCH_CPU_MIPS 1 ++#define ARCH_CPU_32_BITS 1 ++#define ARCH_CPU_BIG_ENDIAN 1 ++#endif ++#elif defined(__loongarch__) ++#define ARCH_CPU_LOONGARCH_FAMILY 1 ++#define ARCH_CPU_LITTLE_ENDIAN 1 ++#if __loongarch_grlen == 64 ++#define ARCH_CPU_LOONGARCH64 1 ++#define ARCH_CPU_64_BITS 1 ++#else ++#define ARCH_CPU_LOONGARCH32 1 ++#define ARCH_CPU_32_BITS 1 ++#endif ++#elif defined(__riscv) && (__riscv_xlen == 64) ++#define ARCH_CPU_RISCV_FAMILY 1 ++#define ARCH_CPU_RISCV64 1 ++#define ARCH_CPU_64_BITS 1 ++#define ARCH_CPU_LITTLE_ENDIAN 1 ++#else ++#error Please add support for your architecture in build/build_config.h ++#endif ++ ++// Type detection for wchar_t. ++#if defined(OS_WIN) ++#define WCHAR_T_IS_UTF16 ++#elif defined(OS_FUCHSIA) ++#define WCHAR_T_IS_UTF32 ++#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \ ++ (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff) ++#define WCHAR_T_IS_UTF32 ++#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \ ++ (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff) ++// On Posix, we'll detect short wchar_t, but projects aren't guaranteed to ++// compile in this mode (in particular, Chrome doesn't). This is intended for ++// other projects using base who manage their own dependencies and make sure ++// short wchar works for them. ++#define WCHAR_T_IS_UTF16 ++#else ++#error Please add support for your compiler in build/build_config.h ++#endif ++ ++#if defined(OS_ANDROID) ++// The compiler thinks std::string::const_iterator and "const char*" are ++// equivalent types. ++#define STD_STRING_ITERATOR_IS_CHAR_POINTER ++// The compiler thinks std::u16string::const_iterator and "char16*" are ++// equivalent types. ++#define BASE_STRING16_ITERATOR_IS_CHAR16_POINTER ++#endif ++ + #define USE_SYSTEM_ICUUC + #define USE_SYSTEM_LIBJPEG + #define USE_SYSTEM_ZLIB +-// Makes base/logging.h and base/bits.h work. +-#define COMPILER_GCC ++ ++#endif // BUILD_BUILD_CONFIG_H_ ++ +diff --git a/build/buildflag.h b/build/buildflag.h +new file mode 100644 +index 000000000..d87a22001 +--- /dev/null ++++ b/build/buildflag.h +@@ -0,0 +1,48 @@ ++// Copyright 2015 The Chromium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef BUILD_BUILDFLAG_H_ ++#define BUILD_BUILDFLAG_H_ ++ ++// These macros un-mangle the names of the build flags in a way that looks ++// natural, and gives errors if the flag is not defined. Normally in the ++// preprocessor it's easy to make mistakes that interpret "you haven't done ++// the setup to know what the flag is" as "flag is off". Normally you would ++// include the generated header rather than include this file directly. ++// ++// This is for use with generated headers. See build/buildflag_header.gni. ++ ++// This dance of two macros does a concatenation of two preprocessor args using ++// ## doubly indirectly because using ## directly prevents macros in that ++// parameter from being expanded. ++#define BUILDFLAG_CAT_INDIRECT(a, b) a ## b ++#define BUILDFLAG_CAT(a, b) BUILDFLAG_CAT_INDIRECT(a, b) ++ ++// Accessor for build flags. ++// ++// To test for a value, if the build file specifies: ++// ++// ENABLE_FOO=true ++// ++// Then you would check at build-time in source code with: ++// ++// #include "foo_flags.h" // The header the build file specified. ++// ++// #if BUILDFLAG(ENABLE_FOO) ++// ... ++// #endif ++// ++// There will no #define called ENABLE_FOO so if you accidentally test for ++// whether that is defined, it will always be negative. You can also use ++// the value in expressions: ++// ++// const char kSpamServerName[] = BUILDFLAG(SPAM_SERVER_NAME); ++// ++// Because the flag is accessed as a preprocessor macro with (), an error ++// will be thrown if the proper header defining the internal flag value has ++// not been included. ++#define BUILDFLAG(flag) (BUILDFLAG_CAT(BUILDFLAG_INTERNAL_, flag)()) ++ ++#endif // BUILD_BUILDFLAG_H_ ++ +diff --git a/build_overrides/BUILDCONFIG.gn b/build_overrides/BUILDCONFIG.gn +new file mode 100644 +index 000000000..2b9bbd611 +--- /dev/null ++++ b/build_overrides/BUILDCONFIG.gn +@@ -0,0 +1,627 @@ ++# Copyright 2022 The PDFium Authors ++# Use of this source code is governed by a BSD-style license that can be ++# found in the LICENSE file. ++ ++# ============================================================================= ++# WHAT IS THIS FILE? ++# ============================================================================= ++# ++# This is a copy of //build/config/BUILDCONFIG.gn. The difference is it adds an ++# extra default_compiler_configs to use PDFium's desired default compiler ++# config. See "PDFIUM MODIFICATIONS" below. ++# ++# This is the main GN build configuration. This file is loaded after the ++# build args (args.gn) for the build directory and after the toplevel ".gn" ++# file (which points to this file as the build configuration). ++# ++# This file will be executed and the resulting context will be used to execute ++# every other file in the build. So variables declared here (that don't start ++# with an underscore) will be implicitly global. ++ ++# ============================================================================= ++# PLATFORM SELECTION ++# ============================================================================= ++# ++# There are two main things to set: "os" and "cpu". The "toolchain" is the name ++# of the GN thing that encodes combinations of these things. ++# ++# Users typically only set the variables "target_os" and "target_cpu" in "gn ++# args", the rest are set up by our build and internal to GN. ++# ++# There are three different types of each of these things: The "host" ++# represents the computer doing the compile and never changes. The "target" ++# represents the main thing we're trying to build. The "current" represents ++# which configuration is currently being defined, which can be either the ++# host, the target, or something completely different (like nacl). GN will ++# run the same build file multiple times for the different required ++# configuration in the same build. ++# ++# This gives the following variables: ++# - host_os, host_cpu, host_toolchain ++# - target_os, target_cpu, default_toolchain ++# - current_os, current_cpu, current_toolchain. ++# ++# Note the default_toolchain isn't symmetrical (you would expect ++# target_toolchain). This is because the "default" toolchain is a GN built-in ++# concept, and "target" is something our build sets up that's symmetrical with ++# its GYP counterpart. Potentially the built-in default_toolchain variable ++# could be renamed in the future. ++# ++# When writing build files, to do something only for the host: ++# if (current_toolchain == host_toolchain) { ... ++ ++if (target_os == "") { ++ target_os = host_os ++} ++ ++if (target_cpu == "") { ++ if (target_os == "android") { ++ # If we're building for Android, we should assume that we want to ++ # build for ARM by default, not the host_cpu (which is likely x64). ++ # This allows us to not have to specify both target_os and target_cpu ++ # on the command line. ++ target_cpu = "arm" ++ } else { ++ target_cpu = host_cpu ++ } ++} ++ ++if (current_cpu == "") { ++ current_cpu = target_cpu ++} ++if (current_os == "") { ++ current_os = target_os ++} ++ ++# ============================================================================= ++# BUILD FLAGS ++# ============================================================================= ++# ++# This block lists input arguments to the build, along with their default ++# values. ++# ++# If a value is specified on the command line, it will overwrite the defaults ++# given in a declare_args block, otherwise the default will be used. ++# ++# YOU SHOULD ALMOST NEVER NEED TO ADD FLAGS TO THIS FILE. GN allows any file in ++# the build to declare build flags. If you need a flag for a single component, ++# you can just declare it in the corresponding BUILD.gn file. ++# ++# - If your feature is a single target, say //components/foo, you can put ++# a declare_args() block in //components/foo/BUILD.gn and use it there. ++# Nobody else in the build needs to see the flag. ++# ++# - Defines based on build variables should be implemented via the generated ++# build flag header system. See //build/buildflag_header.gni. You can put ++# the buildflag_header target in the same file as the build flag itself. You ++# should almost never set "defines" directly. ++# ++# - If your flag toggles a target on and off or toggles between different ++# versions of similar things, write a "group" target that forwards to the ++# right target (or no target) depending on the value of the build flag. This ++# group can be in the same BUILD.gn file as the build flag, and targets can ++# depend unconditionally on the group rather than duplicating flag checks ++# across many targets. ++# ++# - If a semi-random set of build files REALLY needs to know about a define and ++# the above pattern for isolating the build logic in a forwarding group ++# doesn't work, you can put the argument in a .gni file. This should be put ++# in the lowest level of the build that knows about this feature (which should ++# almost always be outside of the //build directory!). ++# ++# Other flag advice: ++# ++# - Use boolean values when possible. If you need a default value that expands ++# to some complex thing in the default case (like the location of the ++# compiler which would be computed by a script), use a default value of -1 or ++# the empty string. Outside of the declare_args block, conditionally expand ++# the default value as necessary. ++# ++# - Use a name like "use_foo" or "is_foo" (whatever is more appropriate for ++# your feature) rather than just "foo". ++# ++# - Write good comments directly above the declaration with no blank line. ++# These comments will appear as documentation in "gn args --list". ++# ++# - Don't call exec_script inside declare_args. This will execute the script ++# even if the value is overridden, which is wasteful. See first bullet. ++ ++declare_args() { ++ # Set to enable the official build level of optimization. This has nothing ++ # to do with branding, but enables an additional level of optimization above ++ # release (!is_debug). This might be better expressed as a tri-state ++ # (debug, release, official) but for historical reasons there are two ++ # separate flags. ++ # ++ # IMPORTANT NOTE: (!is_debug) is *not* sufficient to get satisfying ++ # performance. In particular, DCHECK()s are still enabled for release builds, ++ # which can halve overall performance, and do increase memory usage. Always ++ # set "is_official_build" to true for any build intended to ship to end-users. ++ is_official_build = false ++ ++ # Set to true when compiling with the Clang compiler. ++ is_clang = current_os != "linux" || ++ (current_cpu != "s390x" && current_cpu != "s390" && ++ current_cpu != "ppc64" && current_cpu != "ppc" && ++ current_cpu != "mips" && current_cpu != "mips64" && ++ current_cpu != "riscv64") ++ ++ # Allows the path to a custom target toolchain to be injected as a single ++ # argument, and set as the default toolchain. ++ custom_toolchain = "" ++ ++ # This should not normally be set as a build argument. It's here so that ++ # every toolchain can pass through the "global" value via toolchain_args(). ++ host_toolchain = "" ++ ++ # Do not set this directly. ++ # It should be set only by //build/toolchains/android:robolectric_x64. ++ # True when compiling native code for use with robolectric_binary(). ++ is_robolectric = false ++ ++ # DON'T ADD MORE FLAGS HERE. Read the comment above. ++} ++ ++declare_args() { ++ # Debug build. Enabling official builds automatically sets is_debug to false. ++ is_debug = !is_official_build ++} ++ ++declare_args() { ++ # Component build. Setting to true compiles targets declared as "components" ++ # as shared libraries loaded dynamically. This speeds up development time. ++ # When false, components will be linked statically. ++ # ++ # For more information see ++ # https://chromium.googlesource.com/chromium/src/+/main/docs/component_build.md ++ is_component_build = is_debug && current_os != "ios" ++} ++ ++assert(!(is_debug && is_official_build), "Can't do official debug builds") ++assert(!(current_os == "ios" && is_component_build), ++ "Can't use component build on iOS") ++ ++# ============================================================================== ++# TOOLCHAIN SETUP ++# ============================================================================== ++# ++# Here we set the default toolchain, as well as the variable host_toolchain ++# which will identify the toolchain corresponding to the local system when ++# doing cross-compiles. When not cross-compiling, this will be the same as the ++# default toolchain. ++# ++# We do this before anything else to make sure we complain about any ++# unsupported os/cpu combinations as early as possible. ++ ++if (host_toolchain == "") { ++ # This should only happen in the top-level context. ++ # In a specific toolchain context, the toolchain_args() ++ # block should have propagated a value down. ++ # TODO(dpranke): Add some sort of assert here that verifies that ++ # no toolchain omitted host_toolchain from its toolchain_args(). ++ ++ if (host_os == "linux") { ++ if (target_os != "linux") { ++ host_toolchain = "//build/toolchain/linux:clang_$host_cpu" ++ } else if (is_clang) { ++ host_toolchain = "//build/toolchain/linux:clang_$host_cpu" ++ } else { ++ host_toolchain = "//build/toolchain/linux:$host_cpu" ++ } ++ } else if (host_os == "mac") { ++ host_toolchain = "//build/toolchain/mac:clang_$host_cpu" ++ } else if (host_os == "win") { ++ # On Windows always use the target CPU for host builds for x86/x64. On the ++ # configurations we support this will always work and it saves build steps. ++ # Windows ARM64 targets require an x64 host for cross build. ++ if (target_cpu == "x86" || target_cpu == "x64") { ++ if (is_clang) { ++ host_toolchain = "//build/toolchain/win:win_clang_$target_cpu" ++ } else { ++ host_toolchain = "//build/toolchain/win:$target_cpu" ++ } ++ } else if (is_clang) { ++ host_toolchain = "//build/toolchain/win:win_clang_$host_cpu" ++ } else { ++ host_toolchain = "//build/toolchain/win:$host_cpu" ++ } ++ } else if (host_os == "aix") { ++ host_toolchain = "//build/toolchain/aix:$host_cpu" ++ } else if (host_os == "zos") { ++ host_toolchain = "//build/toolchain/zos:$host_cpu" ++ } else { ++ assert(false, "Unsupported host_os: $host_os") ++ } ++} ++ ++_default_toolchain = "" ++ ++if (target_os == "android") { ++ assert(host_os == "linux", "Android builds are only supported on Linux.") ++ _default_toolchain = "//build/toolchain/android:android_clang_$target_cpu" ++} else if (target_os == "chromeos" || target_os == "linux") { ++ # See comments in build/toolchain/cros/BUILD.gn about board compiles. ++ if (is_clang) { ++ _default_toolchain = "//build/toolchain/linux:clang_$target_cpu" ++ } else { ++ _default_toolchain = "//build/toolchain/linux:$target_cpu" ++ } ++} else if (target_os == "fuchsia") { ++ _default_toolchain = "//build/toolchain/fuchsia:$target_cpu" ++} else if (target_os == "ios") { ++ _default_toolchain = "//build/toolchain/ios:ios_clang_$target_cpu" ++} else if (target_os == "mac") { ++ assert(host_os == "mac" || host_os == "linux", ++ "Mac cross-compiles are unsupported.") ++ _default_toolchain = "//build/toolchain/mac:clang_$target_cpu" ++} else if (target_os == "win") { ++ # On Windows, we use the same toolchain for host and target by default. ++ # Beware, win cross builds have some caveats, see docs/win_cross.md ++ if (is_clang) { ++ _default_toolchain = "//build/toolchain/win:win_clang_$target_cpu" ++ } else { ++ _default_toolchain = "//build/toolchain/win:$target_cpu" ++ } ++} else if (target_os == "winuwp") { ++ # Only target WinUWP on for a Windows store application and only ++ # x86, x64 and arm are supported target CPUs. ++ assert(target_cpu == "x86" || target_cpu == "x64" || target_cpu == "arm" || ++ target_cpu == "arm64") ++ _default_toolchain = "//build/toolchain/win:uwp_$target_cpu" ++} else if (target_os == "aix") { ++ _default_toolchain = "//build/toolchain/aix:$target_cpu" ++} else if (target_os == "zos") { ++ _default_toolchain = "//build/toolchain/zos:$target_cpu" ++} else { ++ assert(false, "Unsupported target_os: $target_os") ++} ++ ++# If a custom toolchain has been set in the args, set it as default. Otherwise, ++# set the default toolchain for the platform (if any). ++if (custom_toolchain != "") { ++ set_default_toolchain(custom_toolchain) ++} else if (_default_toolchain != "") { ++ set_default_toolchain(_default_toolchain) ++} ++ ++# ============================================================================= ++# OS DEFINITIONS ++# ============================================================================= ++# ++# We set these various is_FOO booleans for convenience in writing OS-based ++# conditions. ++# ++# - is_android, is_chromeos, is_ios, and is_win should be obvious. ++# - is_mac is set only for desktop Mac. It is not set on iOS. ++# - is_posix is true for mac and any Unix-like system (basically everything ++# except Fuchsia and Windows). ++# - is_linux is true for desktop Linux, but not for ChromeOS nor Android (which ++# is generally too different despite being based on the Linux kernel). ++# ++# Do not add more is_* variants here for random lesser-used Unix systems like ++# aix or one of the BSDs. If you need to check these, just check the ++# current_os value directly. ++ ++is_android = current_os == "android" ++is_chromeos = current_os == "chromeos" ++is_fuchsia = current_os == "fuchsia" ++is_ios = current_os == "ios" ++is_linux = current_os == "linux" ++is_mac = current_os == "mac" ++is_nacl = current_os == "nacl" ++is_win = current_os == "win" || current_os == "winuwp" ++ ++is_apple = is_ios || is_mac ++is_posix = !is_win && !is_fuchsia ++ ++# ============================================================================= ++# TARGET DEFAULTS ++# ============================================================================= ++# ++# Set up the default configuration for every build target of the given type. ++# The values configured here will be automatically set on the scope of the ++# corresponding target. Target definitions can add or remove to the settings ++# here as needed. ++# ++# WHAT GOES HERE? ++# ++# Other than the main compiler and linker configs, the only reason for a config ++# to be in this list is if some targets need to explicitly override that config ++# by removing it. This is how targets opt-out of flags. If you don't have that ++# requirement and just need to add a config everywhere, reference it as a ++# sub-config of an existing one, most commonly the main "compiler" one. ++ ++# Holds all configs used for running the compiler. ++default_compiler_configs = [ ++ "//build/config:feature_flags", ++ "//build/config/compiler:afdo", ++ "//build/config/compiler:afdo_optimize_size", ++ "//build/config/compiler:cet_shadow_stack", ++ "//build/config/compiler:chromium_code", ++ "//build/config/compiler:compiler", ++ "//build/config/compiler:compiler_arm_fpu", ++ "//build/config/compiler:compiler_arm_thumb", ++ "//build/config/compiler:default_include_dirs", ++ "//build/config/compiler:default_init_stack_vars", ++ "//build/config/compiler:default_optimization", ++ "//build/config/compiler:default_stack_frames", ++ "//build/config/compiler:default_symbols", ++ "//build/config/compiler:export_dynamic", ++ "//build/config/compiler:no_exceptions", ++ "//build/config/compiler:no_rtti", ++ "//build/config/compiler:no_unresolved_symbols", ++ "//build/config/compiler:runtime_library", ++ "//build/config/compiler:thin_archive", ++ "//build/config/compiler:thinlto_optimize_default", ++ "//build/config/compiler/pgo:default_pgo_flags", ++ "//build/config/coverage:default_coverage", ++ "//build/config/sanitizers:default_sanitizer_flags", ++] ++ ++if (is_win) { ++ default_compiler_configs += [ ++ "//build/config/win:default_cfg_compiler", ++ "//build/config/win:default_crt", ++ "//build/config/win:lean_and_mean", ++ "//build/config/win:nominmax", ++ "//build/config/win:unicode", ++ "//build/config/win:winver", ++ ] ++} ++ ++if (is_posix) { ++ if (current_os != "aix") { ++ default_compiler_configs += ++ [ "//build/config/gcc:symbol_visibility_hidden" ] ++ } ++} ++ ++if (is_fuchsia) { ++ default_compiler_configs += [ "//build/config/gcc:symbol_visibility_hidden" ] ++} ++ ++if (is_android) { ++ default_compiler_configs += ++ [ "//build/config/android:default_orderfile_instrumentation" ] ++} ++ ++if (is_clang && !is_nacl) { ++ default_compiler_configs += [ ++ "//build/config/clang:find_bad_constructs", ++ "//build/config/clang:extra_warnings", ++ ] ++} ++ ++# Debug/release-related defines. ++if (is_debug) { ++ default_compiler_configs += [ "//build/config:debug" ] ++} else { ++ default_compiler_configs += [ "//build/config:release" ] ++} ++ ++# ============================================================================= ++# Begin PDFIUM MODIFICATIONS ++# ============================================================================= ++import("//pdfium.gni") ++if (!pdf_use_cxx20) { ++ if (is_win && !is_clang) { ++ msvc_use_cxx17 = true ++ } else { ++ default_compiler_configs += [ "//build_overrides/compiler:force_cxx17" ] ++ } ++} ++ ++# ============================================================================= ++# End PDFIUM MODIFICATIONS ++# ============================================================================= ++ ++# Static libraries and source sets use only the compiler ones. ++set_defaults("static_library") { ++ configs = default_compiler_configs ++} ++set_defaults("source_set") { ++ configs = default_compiler_configs ++} ++set_defaults("rust_library") { ++ configs = default_compiler_configs ++} ++set_defaults("rust_proc_macro") { ++ configs = default_compiler_configs ++} ++ ++# Compute the set of configs common to all linked targets (shared libraries, ++# loadable modules, executables) to avoid duplication below. ++if (is_win) { ++ # Many targets remove these configs, so they are not contained within ++ # //build/config:executable_config for easy removal. ++ _linker_configs = [ ++ "//build/config/win:default_incremental_linking", ++ ++ # Default to console-mode apps. Most of our targets are tests and such ++ # that shouldn't use the windows subsystem. ++ "//build/config/win:console", ++ ] ++} else if (is_mac) { ++ _linker_configs = [ "//build/config/apple:strip_all" ] ++} else { ++ _linker_configs = [] ++} ++ ++# Executable defaults. ++default_executable_configs = default_compiler_configs + [ ++ "//build/config:default_libs", ++ "//build/config:executable_config", ++ ] + _linker_configs ++ ++if (is_win) { ++ # Turn on linker CFI for executables, and position it so it can be removed ++ # if needed. ++ default_executable_configs += [ "//build/config/win:cfi_linker" ] ++} ++ ++set_defaults("executable") { ++ configs = default_executable_configs ++} ++ ++# Shared library and loadable module defaults (also for components in component ++# mode). ++default_shared_library_configs = default_compiler_configs + [ ++ "//build/config:default_libs", ++ "//build/config:shared_library_config", ++ ] + _linker_configs ++if (is_win) { ++ # Turn on linker CFI for DLLs, and position it so it can be removed if needed. ++ default_shared_library_configs += [ "//build/config/win:cfi_linker" ] ++} ++ ++if (is_android) { ++ # Strip native JNI exports from shared libraries by default. Binaries that ++ # want this can remove this config. ++ default_shared_library_configs += ++ [ "//build/config/android:hide_all_but_jni_onload" ] ++} ++set_defaults("shared_library") { ++ configs = default_shared_library_configs ++} ++set_defaults("loadable_module") { ++ configs = default_shared_library_configs ++ ++ # loadable_modules are generally used by other libs, not just via JNI. ++ if (is_android) { ++ configs -= [ "//build/config/android:hide_all_but_jni_onload" ] ++ } ++} ++ ++# A helper for forwarding testonly and visibility. ++# Forwarding "*" does not include variables from outer scopes (to avoid copying ++# all globals into each template invocation), so it will not pick up ++# file-scoped or outer-template-scoped variables. Normally this behavior is ++# desired, but "visibility" and "testonly" are commonly defined in outer scopes. ++# Explicitly forwarding them in forward_variables_from() works around this ++# nuance. See //build/docs/writing_gn_templates.md#using-forward_variables_from ++TESTONLY_AND_VISIBILITY = [ ++ "testonly", ++ "visibility", ++] ++ ++# Sets default dependencies for executable and shared_library targets. ++# ++# Variables ++# no_default_deps: If true, no standard dependencies will be added. ++# Targets that set this usually also want to remove ++# "//build/config/compiler:runtime_library" from configs (to remove ++# its subconfig "//build/config/c++:runtime_library"). ++foreach(_target_type, ++ [ ++ "executable", ++ "loadable_module", ++ "shared_library", ++ ]) { ++ template(_target_type) { ++ # Alias "target_name" because it is clobbered by forward_variables_from(). ++ _target_name = target_name ++ target(_target_type, _target_name) { ++ forward_variables_from(invoker, ++ "*", ++ TESTONLY_AND_VISIBILITY + [ "no_default_deps" ]) ++ forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) ++ if (!defined(deps)) { ++ deps = [] ++ } ++ if (!defined(invoker.no_default_deps) || !invoker.no_default_deps) { ++ # This pulls in one of: ++ # //build/config:executable_deps ++ # //build/config:loadable_module_deps ++ # //build/config:shared_library_deps ++ # (This explicit list is so that grepping for these configs finds where ++ # they are used.) ++ deps += [ "//build/config:${_target_type}_deps" ] ++ } ++ ++ # On Android, write shared library output file to metadata. We will use ++ # this information to, for instance, collect all shared libraries that ++ # should be packaged into an APK. ++ if (!defined(invoker.metadata) && (is_android || is_robolectric) && ++ (_target_type == "shared_library" || ++ _target_type == "loadable_module")) { ++ _output_name = _target_name ++ if (defined(invoker.output_name)) { ++ _output_name = invoker.output_name ++ } ++ ++ # Remove 'lib' prefix from output name if it exists. ++ _magic_prefix = "$0x01$0x01" ++ _output_name = string_replace("${_magic_prefix}${_output_name}", ++ "${_magic_prefix}lib", ++ _magic_prefix, ++ 1) ++ _output_name = string_replace(_output_name, _magic_prefix, "", 1) ++ ++ if (defined(output_extension)) { ++ _shlib_extension = ".$output_extension" ++ } else if (is_component_build && _target_type != "loadable_module") { ++ _shlib_extension = ".cr.so" ++ } else { ++ _shlib_extension = ".so" ++ } ++ ++ metadata = { ++ shared_libraries = ++ [ "$root_out_dir/lib${_output_name}${_shlib_extension}" ] ++ } ++ } ++ } ++ } ++} ++ ++# ============================================================================== ++# COMPONENT SETUP ++# ============================================================================== ++ ++# Defines a component, which equates to a shared_library when ++# is_component_build == true and a static_library otherwise. ++# ++# Use static libraries for the static build rather than source sets because ++# many of of our test binaries link many large dependencies but often don't ++# use large portions of them. The static libraries are much more efficient to ++# link in this situation since only the necessary object files are linked. ++# ++# The invoker can override the type of the target in the non-component-build ++# case by setting static_component_type to either "source_set" or ++# "static_library". If unset, the default will be used. ++template("component") { ++ if (is_component_build) { ++ _component_mode = "shared_library" ++ } else if (defined(invoker.static_component_type)) { ++ assert(invoker.static_component_type == "static_library" || ++ invoker.static_component_type == "source_set") ++ _component_mode = invoker.static_component_type ++ } else if (!defined(invoker.sources) || invoker.sources == []) { ++ # When there are no sources defined, use a source set to avoid creating ++ # an empty static library (which generally don't work). ++ _component_mode = "source_set" ++ } else { ++ _component_mode = "static_library" ++ } ++ target(_component_mode, target_name) { ++ forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) ++ forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY) ++ } ++} ++ ++# Component defaults ++# Set a variable since we also want to make this available ++# to mixed_component.gni ++if (is_component_build) { ++ default_component_configs = default_shared_library_configs ++ if (is_android) { ++ default_component_configs -= ++ [ "//build/config/android:hide_all_but_jni_onload" ] ++ } ++} else { ++ default_component_configs = default_compiler_configs ++} ++ ++set_defaults("component") { ++ configs = default_component_configs ++} +diff --git a/build_overrides/build.gni b/build_overrides/build.gni +index bb1525380..446fa8989 100644 +--- a/build_overrides/build.gni ++++ b/build_overrides/build.gni +@@ -1,15 +1,7 @@ +-# Copyright 2016 The Chromium Authors. All rights reserved. ++# Copyright 2016 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +-# See https://bugs.chromium.org/p/webrtc/issues/detail?id=5453. +-# Some WebRTC targets require the 10.7 deployment version of the Mac SDK +-# and a 10.11 min SDK, but those targets are only used in non-Chromium +-# builds. We can remove this when Chromium drops 10.6 support and also +-# requires 10.7. +-mac_sdk_min_build_override = "10.10" +-mac_deployment_target_build_override = "10.7" +- + # Variable that can be used to support multiple build scenarios, like having + # Chromium specific targets in a client project's GN file etc. + build_with_chromium = false +@@ -22,14 +14,14 @@ default_android_ndk_major_version = 16 + # PDFium builds don't support building java targets. + enable_java_templates = false + ++# Enables assertions on safety checks in libc++. ++enable_safe_libcxx = true ++ + # Whether to use the neon FPU instruction set or not. + if (current_cpu == "arm") { + arm_use_neon = true + } + +-# PDFium builds don't use Chromium's third_party/binutils. +-linux_use_bundled_binutils_override = false +- + # PDFium just uses the Chromium suppression files for now. + asan_suppressions_file = "//build/sanitizers/asan_suppressions.cc" + lsan_suppressions_file = "//build/sanitizers/lsan_suppressions.cc" +@@ -47,6 +39,10 @@ declare_args() { + # obtained with gclient sync after setting the environment variable + # FORCE_MAC_TOOLCHAIN]. + use_system_xcode = "" ++ ++ # Allows googletest to pretty-print various absl types. ++ # Assumes //third_party/abseil-cpp is an available dependency for googletest. ++ gtest_enable_absl_printers = true + } + + if (use_system_xcode == "") { +diff --git a/build_overrides/compiler/BUILD.gn b/build_overrides/compiler/BUILD.gn +new file mode 100644 +index 000000000..b6cc64195 +--- /dev/null ++++ b/build_overrides/compiler/BUILD.gn +@@ -0,0 +1,25 @@ ++# Copyright 2022 The PDFium Authors ++# Use of this source code is governed by a BSD-style license that can be ++# found in the LICENSE file. ++ ++# A simplified config to consistently use C++17 and override the config ++# in //build/config/compiler, which is using C++20 by default on many platforms. ++ ++assert(!is_nacl) ++ ++config("force_cxx17") { ++ cflags_cc = [] ++ ++ if (is_linux || is_chromeos || is_android || current_os == "aix") { ++ if (is_clang) { ++ standard_prefix = "c" ++ } else { ++ standard_prefix = "gnu" ++ } ++ cflags_cc += [ "-std=${standard_prefix}++17" ] ++ } else if (is_win) { ++ cflags_cc += [ "/std:c++17" ] ++ } else { ++ cflags_cc += [ "-std=c++17" ] ++ } ++} +diff --git a/build_overrides/gtest.gni b/build_overrides/gtest.gni +index bf30fb1d7..6afd879fc 100644 +--- a/build_overrides/gtest.gni ++++ b/build_overrides/gtest.gni +@@ -1,4 +1,4 @@ +-# Copyright 2016 The Chromium Authors. All rights reserved. ++# Copyright 2016 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -8,7 +8,7 @@ gtest_include_multiprocess = false + # Exclude support for platform-specific operations across unit tests. + gtest_include_platform_test = false + +-# Exclude support for testing Objective C code on OS X and iOS. ++# Exclude support for testing Objective C code on macOS and iOS. + gtest_include_objc_support = false + + # Exclude support for flushing coverage files on iOS. +diff --git a/build_overrides/partition_alloc.gni b/build_overrides/partition_alloc.gni +new file mode 100644 +index 000000000..0cf700902 +--- /dev/null ++++ b/build_overrides/partition_alloc.gni +@@ -0,0 +1,11 @@ ++# Copyright 2022 The PDFium Authors ++# Use of this source code is governed by a BSD-style license that can be ++# found in the LICENSE file. ++ ++# See base/allocator/partition_allocator/external_builds.md ++use_partition_alloc_as_malloc_default = false ++enable_mte_checked_ptr_support_default = false ++enable_backup_ref_ptr_support_default = false ++put_ref_count_in_previous_slot_default = false ++enable_backup_ref_ptr_slow_checks_default = false ++enable_dangling_raw_ptr_checks_default = false +diff --git a/build_overrides/pdfium.gni b/build_overrides/pdfium.gni +index ab89cd997..b7dc9e0c5 100644 +--- a/build_overrides/pdfium.gni ++++ b/build_overrides/pdfium.gni +@@ -1,4 +1,4 @@ +-# Copyright 2016 The Chromium Authors. All rights reserved. ++# Copyright 2016 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -17,16 +17,13 @@ pdf_enable_v8_override = true + # Default: Without XFA support. + pdf_enable_xfa_override = false + +-# Build PDFium against skia (experimental) rather than agg, replacing all PDFium +-# graphics. +-# Default: Use agg. +-pdf_use_skia_override = false +- +-# Build PDFium against skia (experimental) rather than agg, adding only path +-# support. +-# Default: Use agg. +-pdf_use_skia_paths_override = false ++# Build PDFium with PartitionAlloc support, directing `fxcrt` to use ++# it as its memory allocator in lieu of `malloc()`. ++# Default: Use PartitionAlloc when building with Clang. ++pdf_use_partition_alloc_override = is_clang + +-# Build PDFium either with or without experimental win32 GDI APIs. +-# Default: Without experimental win32 GDI APIs. +-pdf_use_win32_gdi_override = false ++# Build PDFium to use Skia (experimental) for all PDFium graphics. ++# If enabled, coexists in build with AGG graphics and the default ++# renderer is selectable at runtime. ++# The default is to use AGG only when `pdf_use_skia_override` is false. ++pdf_use_skia_override = false +diff --git a/codereview.settings b/codereview.settings +index 77265c947..a23bae517 100644 +--- a/codereview.settings ++++ b/codereview.settings +@@ -1,6 +1,6 @@ + # This file is used by git cl to get repository specific information. ++BUG_PREFIX: pdfium: + CC_LIST: pdfium-reviews@googlegroups.com +-CODE_REVIEW_SERVER: codereview.chromium.org + GERRIT_HOST: True + PROJECT: pdfium + STATUS: http://pdfium-status.appspot.com/status +diff --git a/constants/Android.bp b/constants/Android.bp +index caa06a7c9..9acfe5187 100644 +--- a/constants/Android.bp ++++ b/constants/Android.bp +@@ -7,8 +7,12 @@ package { + default_applicable_licenses: ["external_pdfium_license"], + } + +-cc_library_headers { ++cc_library_static { + name: "libpdfium-constants", ++ defaults: ["pdfium-core"], + export_include_dirs: ["."], + visibility: ["//external/pdfium:__subpackages__"], ++ srcs: [ ++ "*.cpp", ++ ], + } +diff --git a/constants/BUILD.gn b/constants/BUILD.gn +index 4c10fed71..34247437f 100644 +--- a/constants/BUILD.gn ++++ b/constants/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -6,12 +6,27 @@ import("../pdfium.gni") + + source_set("constants") { + sources = [ ++ "access_permissions.h", ++ "annotation_common.cpp", + "annotation_common.h", + "annotation_flags.h", ++ "appearance.cpp", ++ "appearance.h", ++ "ascii.h", ++ "font_encodings.cpp", ++ "font_encodings.h", ++ "form_fields.cpp", + "form_fields.h", + "form_flags.h", ++ "page_object.cpp", + "page_object.h", ++ "stream_dict_common.cpp", + "stream_dict_common.h", ++ "transparency.cpp", + "transparency.h", + ] ++ configs += [ ++ "../:pdfium_strict_config", ++ "../:pdfium_noshorten_config", ++ ] + } +diff --git a/constants/access_permissions.h b/constants/access_permissions.h +new file mode 100644 +index 000000000..b95f7e94a +--- /dev/null ++++ b/constants/access_permissions.h +@@ -0,0 +1,21 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CONSTANTS_ACCESS_PERMISSIONS_H_ ++#define CONSTANTS_ACCESS_PERMISSIONS_H_ ++ ++namespace pdfium { ++namespace access_permissions { ++ ++// PDF 1.7 spec, table 3.20. ++// User access permissions. ++constexpr uint32_t kModifyContent = 1 << 3; ++constexpr uint32_t kModifyAnnotation = 1 << 5; ++constexpr uint32_t kFillForm = 1 << 8; ++constexpr uint32_t kExtractForAccessibility = 1 << 9; ++ ++} // namespace access_permissions ++} // namespace pdfium ++ ++#endif // CONSTANTS_ACCESS_PERMISSIONS_H_ +diff --git a/constants/annotation_common.cpp b/constants/annotation_common.cpp +new file mode 100644 +index 000000000..6110e381c +--- /dev/null ++++ b/constants/annotation_common.cpp +@@ -0,0 +1,37 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "constants/annotation_common.h" ++ ++namespace pdfium { ++namespace annotation { ++ ++// PDF 1.7 spec, table 8.15. ++// Entries common to all annotation dictionaries. ++const char kType[] = "Type"; ++const char kSubtype[] = "Subtype"; ++const char kRect[] = "Rect"; ++const char kContents[] = "Contents"; ++const char kP[] = "P"; ++const char kNM[] = "NM"; ++const char kM[] = "M"; ++const char kF[] = "F"; ++const char kAP[] = "AP"; ++const char kAS[] = "AS"; ++const char kBorder[] = "Border"; ++const char kC[] = "C"; ++const char kStructParent[] = "StructParent"; ++const char kOC[] = "OC"; ++ ++// Entries for polygon and polyline annotations. ++const char kVertices[] = "Vertices"; ++ ++// Entries for ink annotations ++const char kInkList[] = "InkList"; ++ ++// Entries for line annotations ++const char kL[] = "L"; ++ ++} // namespace annotation ++} // namespace pdfium +diff --git a/constants/annotation_common.h b/constants/annotation_common.h +index 471d24407..baf067748 100644 +--- a/constants/annotation_common.h ++++ b/constants/annotation_common.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,23 +8,26 @@ + namespace pdfium { + namespace annotation { + +-// PDF 1.7 spec, table 8.15. +-// Entries common to all annotation dictionaries. +- +-constexpr char kType[] = "Type"; +-constexpr char kSubtype[] = "Subtype"; +-constexpr char kRect[] = "Rect"; +-constexpr char kContents[] = "Contents"; +-constexpr char kP[] = "P"; +-constexpr char kNM[] = "NM"; +-constexpr char kM[] = "M"; +-constexpr char kF[] = "F"; +-constexpr char kAP[] = "AP"; +-constexpr char kAS[] = "AS"; +-constexpr char kBorder[] = "Border"; +-constexpr char kC[] = "C"; +-constexpr char kStructParent[] = "StructParent"; +-constexpr char kOC[] = "OC"; ++extern const char kType[]; ++extern const char kSubtype[]; ++extern const char kRect[]; ++extern const char kContents[]; ++extern const char kP[]; ++extern const char kNM[]; ++extern const char kM[]; ++extern const char kF[]; ++extern const char kAP[]; ++extern const char kAS[]; ++extern const char kBorder[]; ++extern const char kC[]; ++extern const char kStructParent[]; ++extern const char kOC[]; ++ ++extern const char kVertices[]; ++ ++extern const char kInkList[]; ++ ++extern const char kL[]; + + } // namespace annotation + } // namespace pdfium +diff --git a/constants/annotation_flags.h b/constants/annotation_flags.h +index d2731dac7..d44a0c13c 100644 +--- a/constants/annotation_flags.h ++++ b/constants/annotation_flags.h +@@ -1,10 +1,12 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #ifndef CONSTANTS_ANNOTATION_FLAGS_H_ + #define CONSTANTS_ANNOTATION_FLAGS_H_ + ++#include ++ + namespace pdfium { + namespace annotation_flags { + +diff --git a/constants/appearance.cpp b/constants/appearance.cpp +new file mode 100644 +index 000000000..3ccdddd9f +--- /dev/null ++++ b/constants/appearance.cpp +@@ -0,0 +1,23 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "constants/appearance.h" ++ ++namespace pdfium { ++namespace appearance { ++ ++// ISO 32000-1:2008 spec, table 189. ++// Entries in an appearance characteristics dictionary. ++const char kR[] = "R"; ++const char kBC[] = "BC"; ++const char kBG[] = "BG"; ++const char kCA[] = "CA"; ++const char kRC[] = "RC"; ++const char kAC[] = "AC"; ++const char kI[] = "I"; ++const char kRI[] = "RI"; ++const char kIX[] = "IX"; ++ ++} // namespace appearance ++} // namespace pdfium +diff --git a/constants/appearance.h b/constants/appearance.h +new file mode 100644 +index 000000000..2a5b75224 +--- /dev/null ++++ b/constants/appearance.h +@@ -0,0 +1,24 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CONSTANTS_APPEARANCE_H_ ++#define CONSTANTS_APPEARANCE_H_ ++ ++namespace pdfium { ++namespace appearance { ++ ++extern const char kR[]; ++extern const char kBC[]; ++extern const char kBG[]; ++extern const char kCA[]; ++extern const char kRC[]; ++extern const char kAC[]; ++extern const char kI[]; ++extern const char kRI[]; ++extern const char kIX[]; ++ ++} // namespace appearance ++} // namespace pdfium ++ ++#endif // CONSTANTS_APPEARANCE_H_ +diff --git a/constants/ascii.h b/constants/ascii.h +new file mode 100644 +index 000000000..2f64419d9 +--- /dev/null ++++ b/constants/ascii.h +@@ -0,0 +1,30 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CONSTANTS_ASCII_H_ ++#define CONSTANTS_ASCII_H_ ++ ++#include ++ ++namespace pdfium { ++namespace ascii { ++ ++constexpr uint8_t kNul = 0x00; ++constexpr uint8_t kControlA = 0x01; ++constexpr uint8_t kControlB = 0x02; ++constexpr uint8_t kControlC = 0x03; ++constexpr uint8_t kBackspace = 0x08; ++constexpr uint8_t kTab = 0x09; ++constexpr uint8_t kNewline = 0x0a; ++constexpr uint8_t kReturn = 0x0d; ++constexpr uint8_t kControlV = 0x16; ++constexpr uint8_t kControlX = 0x18; ++constexpr uint8_t kControlZ = 0x1a; ++constexpr uint8_t kEscape = 0x1b; ++constexpr uint8_t kSpace = 0x20; ++ ++} // namespace ascii ++} // namespace pdfium ++ ++#endif // CONSTANTS_ASCII_H_ +diff --git a/constants/font_encodings.cpp b/constants/font_encodings.cpp +new file mode 100644 +index 000000000..435970170 +--- /dev/null ++++ b/constants/font_encodings.cpp +@@ -0,0 +1,17 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "constants/font_encodings.h" ++ ++namespace pdfium { ++namespace font_encodings { ++ ++// ISO 32000-1:2008 spec, table D1. ++const char kMacRomanEncoding[] = "MacRomanEncoding"; ++const char kWinAnsiEncoding[] = "WinAnsiEncoding"; ++const char kPDFDocEncoding[] = "PDFDocEncoding"; ++const char kMacExpertEncoding[] = "MacExpertEncoding"; ++ ++} // namespace font_encodings ++} // namespace pdfium +diff --git a/constants/font_encodings.h b/constants/font_encodings.h +new file mode 100644 +index 000000000..aefd9f1cf +--- /dev/null ++++ b/constants/font_encodings.h +@@ -0,0 +1,19 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CONSTANTS_FONT_ENCODINGS_H_ ++#define CONSTANTS_FONT_ENCODINGS_H_ ++ ++namespace pdfium { ++namespace font_encodings { ++ ++extern const char kMacRomanEncoding[]; ++extern const char kWinAnsiEncoding[]; ++extern const char kPDFDocEncoding[]; ++extern const char kMacExpertEncoding[]; ++ ++} // namespace font_encodings ++} // namespace pdfium ++ ++#endif // CONSTANTS_FONT_ENCODINGS_H_ +diff --git a/constants/form_fields.cpp b/constants/form_fields.cpp +new file mode 100644 +index 000000000..32ef84ce8 +--- /dev/null ++++ b/constants/form_fields.cpp +@@ -0,0 +1,38 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "constants/form_fields.h" ++ ++namespace pdfium { ++namespace form_fields { ++ ++// ISO 32000-1:2008 table 220. ++// Entries common to all field dictionaries. ++const char kFT[] = "FT"; ++const char kParent[] = "Parent"; ++const char kKids[] = "Kids"; ++const char kT[] = "T"; ++const char kTU[] = "TU"; ++const char kTM[] = "TM"; ++const char kFf[] = "Ff"; ++const char kV[] = "V"; ++const char kDV[] = "DV"; ++const char kAA[] = "AA"; ++ ++// ISO 32000-1:2008 table 220. ++// Values for FT keyword. ++const char kBtn[] = "Btn"; ++const char kTx[] = "Tx"; ++const char kCh[] = "Ch"; ++const char kSig[] = "Sig"; ++ ++// ISO 32000-1:2008 table 222. ++// Entries common to fields containing variable text. ++const char kDA[] = "DA"; ++const char kQ[] = "Q"; ++const char kDS[] = "DS"; ++const char kRV[] = "RV"; ++ ++} // namespace form_fields ++} // namespace pdfium +diff --git a/constants/form_fields.h b/constants/form_fields.h +index 5b7c169c4..129bbd3c7 100644 +--- a/constants/form_fields.h ++++ b/constants/form_fields.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,24 +8,26 @@ + namespace pdfium { + namespace form_fields { + +-// PDF 1.7 spec, table 8.69. +-// Entries common to all field dictionaries. +-constexpr char kFT[] = "FT"; +-constexpr char kParent[] = "Parent"; +-constexpr char kKids[] = "Kids"; +-constexpr char kT[] = "T"; +-constexpr char kTU[] = "TU"; +-constexpr char kTM[] = "TM"; +-constexpr char kFf[] = "Ff"; +-constexpr char kV[] = "V"; +-constexpr char kDV[] = "DV"; +-constexpr char kAA[] = "AA"; ++extern const char kFT[]; ++extern const char kParent[]; ++extern const char kKids[]; ++extern const char kT[]; ++extern const char kTU[]; ++extern const char kTM[]; ++extern const char kFf[]; ++extern const char kV[]; ++extern const char kDV[]; ++extern const char kAA[]; + +-// FT values from PDF 1.7 spec, table 8.69. +-constexpr char kBtn[] = "Btn"; +-constexpr char kTx[] = "Tx"; +-constexpr char kCh[] = "Ch"; +-constexpr char kSig[] = "Sig"; ++extern const char kBtn[]; ++extern const char kTx[]; ++extern const char kCh[]; ++extern const char kSig[]; ++ ++extern const char kDA[]; ++extern const char kQ[]; ++extern const char kDS[]; ++extern const char kRV[]; + + } // namespace form_fields + } // namespace pdfium +diff --git a/constants/form_flags.h b/constants/form_flags.h +index 148bb4ce2..602a164f4 100644 +--- a/constants/form_flags.h ++++ b/constants/form_flags.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/constants/page_object.cpp b/constants/page_object.cpp +new file mode 100644 +index 000000000..0a3e068ac +--- /dev/null ++++ b/constants/page_object.cpp +@@ -0,0 +1,24 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "constants/page_object.h" ++ ++namespace pdfium { ++namespace page_object { ++ ++// PDF 1.7 spec, table 3.27. ++// Entries in a page object. ++const char kType[] = "Type"; ++const char kParent[] = "Parent"; ++const char kResources[] = "Resources"; ++const char kMediaBox[] = "MediaBox"; ++const char kCropBox[] = "CropBox"; ++const char kBleedBox[] = "BleedBox"; ++const char kTrimBox[] = "TrimBox"; ++const char kArtBox[] = "ArtBox"; ++const char kContents[] = "Contents"; ++const char kRotate[] = "Rotate"; ++ ++} // namespace page_object ++} // namespace pdfium +diff --git a/constants/page_object.h b/constants/page_object.h +index 8a41b8cf5..6fb7d681e 100644 +--- a/constants/page_object.h ++++ b/constants/page_object.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,19 +8,16 @@ + namespace pdfium { + namespace page_object { + +-// PDF 1.7 spec, table 3.27. +-// Entries in a page object. +- +-constexpr char kType[] = "Type"; +-constexpr char kParent[] = "Parent"; +-constexpr char kResources[] = "Resources"; +-constexpr char kMediaBox[] = "MediaBox"; +-constexpr char kCropBox[] = "CropBox"; +-constexpr char kBleedBox[] = "BleedBox"; +-constexpr char kTrimBox[] = "TrimBox"; +-constexpr char kArtBox[] = "ArtBox"; +-constexpr char kContents[] = "Contents"; +-constexpr char kRotate[] = "Rotate"; ++extern const char kType[]; ++extern const char kParent[]; ++extern const char kResources[]; ++extern const char kMediaBox[]; ++extern const char kCropBox[]; ++extern const char kBleedBox[]; ++extern const char kTrimBox[]; ++extern const char kArtBox[]; ++extern const char kContents[]; ++extern const char kRotate[]; + + } // namespace page_object + } // namespace pdfium +diff --git a/constants/stream_dict_common.cpp b/constants/stream_dict_common.cpp +new file mode 100644 +index 000000000..5d294c436 +--- /dev/null ++++ b/constants/stream_dict_common.cpp +@@ -0,0 +1,22 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "constants/stream_dict_common.h" ++ ++namespace pdfium { ++namespace stream { ++ ++// PDF 1.7 spec, table 3.4. ++// Entries common to all stream dictionaries. ++// ++// TODO(https://crbug.com/pdfium/1049): Examine all usages of "Length", ++// "Filter", and "F". ++const char kLength[] = "Length"; ++const char kFilter[] = "Filter"; ++const char kDecodeParms[] = "DecodeParms"; ++const char kF[] = "F"; ++const char kDL[] = "DL"; ++ ++} // namespace stream ++} // namespace pdfium +diff --git a/constants/stream_dict_common.h b/constants/stream_dict_common.h +index fc12622b6..feb887ae4 100644 +--- a/constants/stream_dict_common.h ++++ b/constants/stream_dict_common.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,18 +8,11 @@ + namespace pdfium { + namespace stream { + +-// PDF 1.7 spec, table 3.4. +-// Entries common to all stream dictionaries. +- +-// TODO(https://crbug.com/pdfium/1049): Examine all usages of "Length", +-// "Filter", and "F". +-constexpr char kLength[] = "Length"; +-constexpr char kFilter[] = "Filter"; +-constexpr char kDecodeParms[] = "DecodeParms"; +-constexpr char kF[] = "F"; +-// constexpr char kFFilter[] = "FFilter"; +-// constexpr char kFDecodeParms[] = "FDecodeParms"; +-constexpr char kDL[] = "DL"; ++extern const char kLength[]; ++extern const char kFilter[]; ++extern const char kDecodeParms[]; ++extern const char kF[]; ++extern const char kDL[]; + + } // namespace stream + } // namespace pdfium +diff --git a/constants/transparency.cpp b/constants/transparency.cpp +new file mode 100644 +index 000000000..11b8703fe +--- /dev/null ++++ b/constants/transparency.cpp +@@ -0,0 +1,48 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "constants/transparency.h" ++ ++namespace pdfium { ++namespace transparency { ++ ++// PDF 1.7 spec, table 7.2. ++// Standard separable blend modes. ++const char kNormal[] = "Normal"; ++const char kMultiply[] = "Multiply"; ++const char kScreen[] = "Screen"; ++const char kOverlay[] = "Overlay"; ++const char kDarken[] = "Darken"; ++const char kLighten[] = "Lighten"; ++const char kColorDodge[] = "ColorDodge"; ++const char kColorBurn[] = "ColorBurn"; ++const char kHardLight[] = "HardLight"; ++const char kSoftLight[] = "SoftLight"; ++const char kDifference[] = "Difference"; ++const char kExclusion[] = "Exclusion"; ++ ++// PDF 1.7 spec, table 7.3. ++// Standard nonseparable blend modes. ++const char kHue[] = "Hue"; ++const char kSaturation[] = "Saturation"; ++const char kColor[] = "Color"; ++const char kLuminosity[] = "Luminosity"; ++ ++// PDF 1.7 spec, table 7.10. ++// Entries in a soft-mask dictionary. ++const char kSoftMaskSubType[] = "S"; ++const char kAlpha[] = "Alpha"; ++const char kG[] = "G"; ++const char kBC[] = "BC"; ++const char kTR[] = "TR"; ++ ++// PDF 1.7 spec, table 7.13. ++// Additional entries specific to a transparency group attributes dictionary. ++const char kGroupSubType[] = "S"; ++const char kTransparency[] = "Transparency"; ++const char kCS[] = "CS"; ++const char kI[] = "I"; ++ ++} // namespace transparency ++} // namespace pdfium +diff --git a/constants/transparency.h b/constants/transparency.h +index 653286817..21b22c49c 100644 +--- a/constants/transparency.h ++++ b/constants/transparency.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,46 +8,34 @@ + namespace pdfium { + namespace transparency { + +-// PDF 1.7 spec, table 7.2. +-// Standard separable blend modes. +- +-constexpr char kNormal[] = "Normal"; +-constexpr char kMultiply[] = "Multiply"; +-constexpr char kScreen[] = "Screen"; +-constexpr char kOverlay[] = "Overlay"; +-constexpr char kDarken[] = "Darken"; +-constexpr char kLighten[] = "Lighten"; +-constexpr char kColorDodge[] = "ColorDodge"; +-constexpr char kColorBurn[] = "ColorBurn"; +-constexpr char kHardLight[] = "HardLight"; +-constexpr char kSoftLight[] = "SoftLight"; +-constexpr char kDifference[] = "Difference"; +-constexpr char kExclusion[] = "Exclusion"; +- +-// PDF 1.7 spec, table 7.3. +-// Standard nonseparable blend modes. +- +-constexpr char kHue[] = "Hue"; +-constexpr char kSaturation[] = "Saturation"; +-constexpr char kColor[] = "Color"; +-constexpr char kLuminosity[] = "Luminosity"; +- +-// PDF 1.7 spec, table 7.10. +-// Entries in a soft-mask dictionary. +- +-constexpr char kSoftMaskSubType[] = "S"; +-constexpr char kAlpha[] = "Alpha"; +-constexpr char kG[] = "G"; +-constexpr char kBC[] = "BC"; +-constexpr char kTR[] = "TR"; +- +-// PDF 1.7 spec, table 7.13. +-// Additional entries specific to a transparency group attributes dictionary. +- +-constexpr char kGroupSubType[] = "S"; +-constexpr char kTransparency[] = "Transparency"; +-constexpr char kCS[] = "CS"; +-constexpr char kI[] = "I"; ++extern const char kNormal[]; ++extern const char kMultiply[]; ++extern const char kScreen[]; ++extern const char kOverlay[]; ++extern const char kDarken[]; ++extern const char kLighten[]; ++extern const char kColorDodge[]; ++extern const char kColorBurn[]; ++extern const char kHardLight[]; ++extern const char kSoftLight[]; ++extern const char kDifference[]; ++extern const char kExclusion[]; ++ ++extern const char kHue[]; ++extern const char kSaturation[]; ++extern const char kColor[]; ++extern const char kLuminosity[]; ++ ++extern const char kSoftMaskSubType[]; ++extern const char kAlpha[]; ++extern const char kG[]; ++extern const char kBC[]; ++extern const char kTR[]; ++ ++extern const char kGroupSubType[]; ++extern const char kTransparency[]; ++extern const char kCS[]; ++extern const char kI[]; + + } // namespace transparency + } // namespace pdfium +diff --git a/core/fdrm/BUILD.gn b/core/fdrm/BUILD.gn +index 888bb9276..1ee316277 100644 +--- a/core/fdrm/BUILD.gn ++++ b/core/fdrm/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -12,7 +12,10 @@ source_set("fdrm") { + "fx_crypt_aes.cpp", + "fx_crypt_sha.cpp", + ] +- configs += [ "../../:pdfium_core_config" ] ++ configs += [ ++ "../../:pdfium_strict_config", ++ "../../:pdfium_noshorten_config", ++ ] + deps = [ "../fxcrt" ] + visibility = [ "../../*" ] + } +diff --git a/core/fdrm/fx_crypt.cpp b/core/fdrm/fx_crypt.cpp +index 7cc0bc0d5..c702f929d 100644 +--- a/core/fdrm/fx_crypt.cpp ++++ b/core/fdrm/fx_crypt.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,6 +8,8 @@ + + #include + ++#include "core/fxcrt/span_util.h" ++ + #define GET_UINT32(n, b, i) \ + { \ + (n) = (uint32_t)((uint8_t*)b)[(i)] | \ +@@ -31,7 +33,7 @@ const uint8_t md5_padding[64] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + void md5_process(CRYPT_md5_context* ctx, const uint8_t data[64]) { +- uint32_t A, B, C, D, X[16]; ++ uint32_t X[16]; + GET_UINT32(X[0], data, 0); + GET_UINT32(X[1], data, 4); + GET_UINT32(X[2], data, 8); +@@ -48,16 +50,16 @@ void md5_process(CRYPT_md5_context* ctx, const uint8_t data[64]) { + GET_UINT32(X[13], data, 52); + GET_UINT32(X[14], data, 56); + GET_UINT32(X[15], data, 60); ++ uint32_t A = ctx->state[0]; ++ uint32_t B = ctx->state[1]; ++ uint32_t C = ctx->state[2]; ++ uint32_t D = ctx->state[3]; + #define S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + #define P(a, b, c, d, k, s, t) \ + { \ + a += F(b, c, d) + X[k] + t; \ + a = S(a, s) + b; \ + } +- A = ctx->state[0]; +- B = ctx->state[1]; +- C = ctx->state[2]; +- D = ctx->state[3]; + #define F(x, y, z) (z ^ (x & (y ^ z))) + P(A, B, C, D, 0, 7, 0xD76AA478); + P(D, A, B, C, 1, 12, 0xE8C7B756); +@@ -142,11 +144,11 @@ void CRYPT_ArcFourSetup(CRYPT_rc4_context* context, + pdfium::span key) { + context->x = 0; + context->y = 0; +- for (int i = 0; i < kRC4ContextPermutationLength; ++i) ++ for (int i = 0; i < CRYPT_rc4_context::kPermutationLength; ++i) + context->m[i] = i; + + int j = 0; +- for (int i = 0; i < kRC4ContextPermutationLength; ++i) { ++ for (int i = 0; i < CRYPT_rc4_context::kPermutationLength; ++i) { + size_t size = key.size(); + j = (j + context->m[i] + (size ? key[i % size] : 0)) & 0xFF; + std::swap(context->m[i], context->m[j]); +@@ -193,21 +195,20 @@ void CRYPT_MD5Update(CRYPT_md5_context* context, + context->total[1] += data.size() >> 29; + context->total[0] &= 0xFFFFFFFF; + context->total[1] += context->total[0] < data.size() << 3; ++ ++ const pdfium::span buffer_span = pdfium::make_span(context->buffer); + if (left && data.size() >= fill) { +- auto next_data = data.subspan(fill); +- memcpy(context->buffer + left, data.data(), fill); ++ fxcrt::spancpy(buffer_span.subspan(left), data.first(fill)); + md5_process(context, context->buffer); ++ data = data.subspan(fill); + left = 0; +- data = next_data; + } + while (data.size() >= 64) { +- auto next_data = data.subspan(64); + md5_process(context, data.data()); +- data = next_data; ++ data = data.subspan(64); + } +- size_t remaining = data.size(); +- if (remaining) +- memcpy(context->buffer + left, data.data(), remaining); ++ if (!data.empty()) ++ fxcrt::spancpy(buffer_span.subspan(left), data); + } + + void CRYPT_MD5Finish(CRYPT_md5_context* context, uint8_t digest[16]) { +@@ -216,7 +217,7 @@ void CRYPT_MD5Finish(CRYPT_md5_context* context, uint8_t digest[16]) { + PUT_UINT32(context->total[1], msglen, 4); + uint32_t last = (context->total[0] >> 3) & 0x3F; + uint32_t padn = (last < 56) ? (56 - last) : (120 - last); +- CRYPT_MD5Update(context, {md5_padding, padn}); ++ CRYPT_MD5Update(context, pdfium::make_span(md5_padding).first(padn)); + CRYPT_MD5Update(context, msglen); + PUT_UINT32(context->state[0], digest, 0); + PUT_UINT32(context->state[1], digest, 4); +diff --git a/core/fdrm/fx_crypt.h b/core/fdrm/fx_crypt.h +index f3a91f464..f3171c7c0 100644 +--- a/core/fdrm/fx_crypt.h ++++ b/core/fdrm/fx_crypt.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,24 +7,28 @@ + #ifndef CORE_FDRM_FX_CRYPT_H_ + #define CORE_FDRM_FX_CRYPT_H_ + +-#include "core/fxcrt/fx_system.h" ++#include ++ + #include "third_party/base/span.h" + +-constexpr int32_t kRC4ContextPermutationLength = 256; + struct CRYPT_rc4_context { ++ static constexpr int32_t kPermutationLength = 256; ++ + int32_t x; + int32_t y; +- int32_t m[kRC4ContextPermutationLength]; ++ int32_t m[kPermutationLength]; + }; + +-#define MAX_NR 14 +-#define MAX_NB 8 + struct CRYPT_aes_context { ++ static constexpr int kMaxNb = 8; ++ static constexpr int kMaxNr = 14; ++ static constexpr int kSchedSize = (kMaxNr + 1) * kMaxNb; ++ + int Nb; + int Nr; +- unsigned int keysched[(MAX_NR + 1) * MAX_NB]; +- unsigned int invkeysched[(MAX_NR + 1) * MAX_NB]; +- unsigned int iv[MAX_NB]; ++ unsigned int keysched[kSchedSize]; ++ unsigned int invkeysched[kSchedSize]; ++ unsigned int iv[kMaxNb]; + }; + + struct CRYPT_md5_context { +@@ -54,8 +58,7 @@ void CRYPT_ArcFourCrypt(CRYPT_rc4_context* context, pdfium::span data); + + void CRYPT_AESSetKey(CRYPT_aes_context* context, + const uint8_t* key, +- uint32_t keylen, +- bool bEncrypt); ++ uint32_t keylen); + void CRYPT_AESSetIV(CRYPT_aes_context* context, const uint8_t* iv); + void CRYPT_AESDecrypt(CRYPT_aes_context* context, + uint8_t* dest, +diff --git a/core/fdrm/fx_crypt_aes.cpp b/core/fdrm/fx_crypt_aes.cpp +index d4e446fd2..3fab02f1c 100644 +--- a/core/fdrm/fx_crypt_aes.cpp ++++ b/core/fdrm/fx_crypt_aes.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,12 +6,13 @@ + + #include "core/fdrm/fx_crypt.h" + ++#include ++ ++#include "core/fxcrt/fx_system.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++ + #define mulby2(x) (((x & 0x7F) << 1) ^ (x & 0x80 ? 0x1B : 0)) +-#define GET_32BIT_MSB_FIRST(cp) \ +- (((unsigned long)(unsigned char)(cp)[3]) | \ +- ((unsigned long)(unsigned char)(cp)[2] << 8) | \ +- ((unsigned long)(unsigned char)(cp)[1] << 16) | \ +- ((unsigned long)(unsigned char)(cp)[0] << 24)) + #define PUT_32BIT_MSB_FIRST(cp, value) \ + do { \ + (cp)[3] = (value); \ +@@ -428,12 +429,11 @@ const unsigned int D3[256] = { + 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, + 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0, + }; +-#define ADD_ROUND_KEY_4 \ ++#define ADD_ROUND_KEY_4() \ + (block[0] ^= *keysched++, block[1] ^= *keysched++, block[2] ^= *keysched++, \ + block[3] ^= *keysched++) + #define MOVEWORD(i) (block[i] = newstate[i]) +-#undef MAKEWORD +-#define MAKEWORD(i) \ ++#define FMAKEWORD(i) \ + (newstate[i] = (E0[(block[i] >> 24) & 0xFF] ^ \ + E1[(block[(i + C1) % Nb] >> 16) & 0xFF] ^ \ + E2[(block[(i + C2) % Nb] >> 8) & 0xFF] ^ \ +@@ -446,21 +446,24 @@ const unsigned int D3[256] = { + + void aes_encrypt_nb_4(CRYPT_aes_context* ctx, unsigned int* block) { + int i; +- const int C1 = 1, C2 = 2, C3 = 3, Nb = 4; ++ const int C1 = 1; ++ const int C2 = 2; ++ const int C3 = 3; ++ const int Nb = 4; + unsigned int* keysched = ctx->keysched; + unsigned int newstate[4]; + for (i = 0; i < ctx->Nr - 1; i++) { +- ADD_ROUND_KEY_4; +- MAKEWORD(0); +- MAKEWORD(1); +- MAKEWORD(2); +- MAKEWORD(3); ++ ADD_ROUND_KEY_4(); ++ FMAKEWORD(0); ++ FMAKEWORD(1); ++ FMAKEWORD(2); ++ FMAKEWORD(3); + MOVEWORD(0); + MOVEWORD(1); + MOVEWORD(2); + MOVEWORD(3); + } +- ADD_ROUND_KEY_4; ++ ADD_ROUND_KEY_4(); + LASTWORD(0); + LASTWORD(1); + LASTWORD(2); +@@ -469,12 +472,12 @@ void aes_encrypt_nb_4(CRYPT_aes_context* ctx, unsigned int* block) { + MOVEWORD(1); + MOVEWORD(2); + MOVEWORD(3); +- ADD_ROUND_KEY_4; ++ ADD_ROUND_KEY_4(); + } +-#undef MAKEWORD ++#undef FMAKEWORD + #undef LASTWORD + +-#define MAKEWORD(i) \ ++#define FMAKEWORD(i) \ + (newstate[i] = (D0[(block[i] >> 24) & 0xFF] ^ \ + D1[(block[(i + C1) % Nb] >> 16) & 0xFF] ^ \ + D2[(block[(i + C2) % Nb] >> 8) & 0xFF] ^ \ +@@ -487,21 +490,24 @@ void aes_encrypt_nb_4(CRYPT_aes_context* ctx, unsigned int* block) { + + void aes_decrypt_nb_4(CRYPT_aes_context* ctx, unsigned int* block) { + int i; +- const int C1 = 4 - 1, C2 = 4 - 2, C3 = 4 - 3, Nb = 4; ++ const int C1 = 4 - 1; ++ const int C2 = 4 - 2; ++ const int C3 = 4 - 3; ++ const int Nb = 4; + unsigned int* keysched = ctx->invkeysched; + unsigned int newstate[4]; + for (i = 0; i < ctx->Nr - 1; i++) { +- ADD_ROUND_KEY_4; +- MAKEWORD(0); +- MAKEWORD(1); +- MAKEWORD(2); +- MAKEWORD(3); ++ ADD_ROUND_KEY_4(); ++ FMAKEWORD(0); ++ FMAKEWORD(1); ++ FMAKEWORD(2); ++ FMAKEWORD(3); + MOVEWORD(0); + MOVEWORD(1); + MOVEWORD(2); + MOVEWORD(3); + } +- ADD_ROUND_KEY_4; ++ ADD_ROUND_KEY_4(); + LASTWORD(0); + LASTWORD(1); + LASTWORD(2); +@@ -510,39 +516,37 @@ void aes_decrypt_nb_4(CRYPT_aes_context* ctx, unsigned int* block) { + MOVEWORD(1); + MOVEWORD(2); + MOVEWORD(3); +- ADD_ROUND_KEY_4; ++ ADD_ROUND_KEY_4(); + } +-#undef MAKEWORD ++#undef FMAKEWORD + #undef LASTWORD + + void aes_setup(CRYPT_aes_context* ctx, const unsigned char* key, int keylen) { +- ASSERT(keylen == 16 || keylen == 24 || keylen == 32); ++ DCHECK(keylen == 16 || keylen == 24 || keylen == 32); + int Nk = keylen / 4; + ctx->Nb = 4; + ctx->Nr = 6 + (ctx->Nb > Nk ? ctx->Nb : Nk); + int rconst = 1; + for (int i = 0; i < (ctx->Nr + 1) * ctx->Nb; i++) { + if (i < Nk) { +- ctx->keysched[i] = GET_32BIT_MSB_FIRST(key + 4 * i); ++ ctx->keysched[i] = FXSYS_UINT32_GET_MSBFIRST(key + 4 * i); + } else { + unsigned int temp = ctx->keysched[i - 1]; + if (i % Nk == 0) { +- int a, b, c, d; +- a = (temp >> 16) & 0xFF; +- b = (temp >> 8) & 0xFF; +- c = (temp >> 0) & 0xFF; +- d = (temp >> 24) & 0xFF; ++ int a = (temp >> 16) & 0xFF; ++ int b = (temp >> 8) & 0xFF; ++ int c = (temp >> 0) & 0xFF; ++ int d = (temp >> 24) & 0xFF; + temp = Sbox[a] ^ rconst; + temp = (temp << 8) | Sbox[b]; + temp = (temp << 8) | Sbox[c]; + temp = (temp << 8) | Sbox[d]; + rconst = mulby2(rconst); + } else if (i % Nk == 4 && Nk > 6) { +- int a, b, c, d; +- a = (temp >> 24) & 0xFF; +- b = (temp >> 16) & 0xFF; +- c = (temp >> 8) & 0xFF; +- d = (temp >> 0) & 0xFF; ++ int a = (temp >> 24) & 0xFF; ++ int b = (temp >> 16) & 0xFF; ++ int c = (temp >> 8) & 0xFF; ++ int d = (temp >> 0) & 0xFF; + temp = Sbox[a]; + temp = (temp << 8) | Sbox[b]; + temp = (temp << 8) | Sbox[c]; +@@ -556,11 +560,10 @@ void aes_setup(CRYPT_aes_context* ctx, const unsigned char* key, int keylen) { + unsigned int temp; + temp = ctx->keysched[(ctx->Nr - i) * ctx->Nb + j]; + if (i != 0 && i != ctx->Nr) { +- int a, b, c, d; +- a = (temp >> 24) & 0xFF; +- b = (temp >> 16) & 0xFF; +- c = (temp >> 8) & 0xFF; +- d = (temp >> 0) & 0xFF; ++ int a = (temp >> 24) & 0xFF; ++ int b = (temp >> 16) & 0xFF; ++ int c = (temp >> 8) & 0xFF; ++ int d = (temp >> 0) & 0xFF; + temp = D0[Sbox[a]]; + temp ^= D1[Sbox[b]]; + temp ^= D2[Sbox[c]]; +@@ -579,13 +582,15 @@ void aes_decrypt_cbc(unsigned char* dest, + const unsigned char* src, + int len, + CRYPT_aes_context* ctx) { +- unsigned int iv[4], x[4], ct[4]; ++ unsigned int iv[4]; ++ unsigned int x[4]; ++ unsigned int ct[4]; + int i; +- ASSERT((len & 15) == 0); ++ DCHECK_EQ((len & 15), 0); + memcpy(iv, ctx->iv, sizeof(iv)); + while (len > 0) { + for (i = 0; i < 4; i++) { +- x[i] = ct[i] = GET_32BIT_MSB_FIRST(src + 4 * i); ++ x[i] = ct[i] = FXSYS_UINT32_GET_MSBFIRST(src + 4 * i); + } + aes_decrypt(ctx, x); + for (i = 0; i < 4; i++) { +@@ -609,11 +614,11 @@ void aes_encrypt_cbc(unsigned char* dest, + CRYPT_aes_context* ctx) { + unsigned int iv[4]; + int i; +- ASSERT((len & 15) == 0); ++ DCHECK_EQ((len & 15), 0); + memcpy(iv, ctx->iv, sizeof(iv)); + while (len > 0) { + for (i = 0; i < 4; i++) { +- iv[i] ^= GET_32BIT_MSB_FIRST(src + 4 * i); ++ iv[i] ^= FXSYS_UINT32_GET_MSBFIRST(src + 4 * i); + } + aes_encrypt(ctx, iv); + for (i = 0; i < 4; i++) { +@@ -630,14 +635,13 @@ void aes_encrypt_cbc(unsigned char* dest, + + void CRYPT_AESSetKey(CRYPT_aes_context* context, + const uint8_t* key, +- uint32_t keylen, +- bool bEncrypt) { ++ uint32_t keylen) { + aes_setup(context, key, keylen); + } + + void CRYPT_AESSetIV(CRYPT_aes_context* context, const uint8_t* iv) { + for (int i = 0; i < context->Nb; i++) +- context->iv[i] = GET_32BIT_MSB_FIRST(iv + 4 * i); ++ context->iv[i] = FXSYS_UINT32_GET_MSBFIRST(iv + 4 * i); + } + + void CRYPT_AESDecrypt(CRYPT_aes_context* context, +diff --git a/core/fdrm/fx_crypt_sha.cpp b/core/fdrm/fx_crypt_sha.cpp +index 03716853e..b0dc8b595 100644 +--- a/core/fdrm/fx_crypt_sha.cpp ++++ b/core/fdrm/fx_crypt_sha.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,8 @@ + + #include "core/fdrm/fx_crypt.h" + ++#include ++ + #define SHA_GET_UINT32(n, b, i) \ + { \ + (n) = ((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | \ +@@ -48,12 +50,12 @@ + (SHA384_ROTR(x, 28) ^ SHA384_ROTR(x, 34) ^ SHA384_ROTR(x, 39)) + #define SHA384_S3(x) \ + (SHA384_ROTR(x, 14) ^ SHA384_ROTR(x, 18) ^ SHA384_ROTR(x, 41)) +-#define SHA384_P(a, b, c, d, e, f, g, h, x, K) \ +- { \ +- temp1 = h + SHA384_S3(e) + SHA384_F1(e, f, g) + K + x; \ +- temp2 = SHA384_S2(a) + SHA384_F0(a, b, c); \ +- d += temp1; \ +- h = temp1 + temp2; \ ++#define SHA384_P(a, b, c, d, e, f, g, h, x, K) \ ++ { \ ++ uint64_t temp1 = h + SHA384_S3(e) + SHA384_F1(e, f, g) + K + x; \ ++ uint64_t temp2 = SHA384_S2(a) + SHA384_F0(a, b, c); \ ++ d += temp1; \ ++ h = temp1 + temp2; \ + } + #define SHA384_R(t) \ + (W[t] = SHA384_S1(W[t - 2]) + W[t - 7] + SHA384_S0(W[t - 15]) + W[t - 16]) +@@ -68,12 +70,12 @@ + #define F0(x, y, z) ((x & y) | (z & (x | y))) + #define F1(x, y, z) (z ^ (x & (y ^ z))) + #define R(t) (W[t] = S1(W[t - 2]) + W[t - 7] + S0(W[t - 15]) + W[t - 16]) +-#define PS(a, b, c, d, e, f, g, h, x, K) \ +- { \ +- temp1 = h + S3(e) + F1(e, f, g) + K + x; \ +- temp2 = S2(a) + F0(a, b, c); \ +- d += temp1; \ +- h = temp1 + temp2; \ ++#define PS(a, b, c, d, e, f, g, h, x, K) \ ++ { \ ++ uint32_t temp1 = h + S3(e) + F1(e, f, g) + K + x; \ ++ uint32_t temp2 = S2(a) + F0(a, b, c); \ ++ d += temp1; \ ++ h = temp1 + temp2; \ + } + + namespace { +@@ -88,7 +90,6 @@ void SHA_Core_Init(unsigned int h[5]) { + + void SHATransform(unsigned int* digest, unsigned int* block) { + unsigned int w[80]; +- unsigned int a, b, c, d, e; + int t; + for (t = 0; t < 16; t++) { + w[t] = block[t]; +@@ -97,11 +98,11 @@ void SHATransform(unsigned int* digest, unsigned int* block) { + unsigned int tmp = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16]; + w[t] = rol(tmp, 1); + } +- a = digest[0]; +- b = digest[1]; +- c = digest[2]; +- d = digest[3]; +- e = digest[4]; ++ unsigned int a = digest[0]; ++ unsigned int b = digest[1]; ++ unsigned int c = digest[2]; ++ unsigned int d = digest[3]; ++ unsigned int e = digest[4]; + for (t = 0; t < 20; t++) { + unsigned int tmp = rol(a, 5) + ((b & c) | (d & ~b)) + e + w[t] + 0x5a827999; + e = d; +@@ -161,16 +162,14 @@ void sha256_process(CRYPT_sha2_context* ctx, const uint8_t data[64]) { + SHA_GET_UINT32(W[14], data, 56); + SHA_GET_UINT32(W[15], data, 60); + +- uint32_t temp1; +- uint32_t temp2; +- uint32_t A = ctx->state[0]; +- uint32_t B = ctx->state[1]; +- uint32_t C = ctx->state[2]; +- uint32_t D = ctx->state[3]; +- uint32_t E = ctx->state[4]; +- uint32_t F = ctx->state[5]; +- uint32_t G = ctx->state[6]; +- uint32_t H = ctx->state[7]; ++ uint32_t A = static_cast(ctx->state[0]); ++ uint32_t B = static_cast(ctx->state[1]); ++ uint32_t C = static_cast(ctx->state[2]); ++ uint32_t D = static_cast(ctx->state[3]); ++ uint32_t E = static_cast(ctx->state[4]); ++ uint32_t F = static_cast(ctx->state[5]); ++ uint32_t G = static_cast(ctx->state[6]); ++ uint32_t H = static_cast(ctx->state[7]); + PS(A, B, C, D, E, F, G, H, W[0], 0x428A2F98); + PS(H, A, B, C, D, E, F, G, W[1], 0x71374491); + PS(G, H, A, B, C, D, E, F, W[2], 0xB5C0FBCF); +@@ -290,8 +289,6 @@ uint64_t const constants[] = { + }; + + void sha384_process(CRYPT_sha2_context* ctx, const uint8_t data[128]) { +- uint64_t temp1, temp2; +- uint64_t A, B, C, D, E, F, G, H; + uint64_t W[80]; + SHA_GET_UINT64(W[0], data, 0); + SHA_GET_UINT64(W[1], data, 8); +@@ -309,14 +306,14 @@ void sha384_process(CRYPT_sha2_context* ctx, const uint8_t data[128]) { + SHA_GET_UINT64(W[13], data, 104); + SHA_GET_UINT64(W[14], data, 112); + SHA_GET_UINT64(W[15], data, 120); +- A = ctx->state[0]; +- B = ctx->state[1]; +- C = ctx->state[2]; +- D = ctx->state[3]; +- E = ctx->state[4]; +- F = ctx->state[5]; +- G = ctx->state[6]; +- H = ctx->state[7]; ++ uint64_t A = ctx->state[0]; ++ uint64_t B = ctx->state[1]; ++ uint64_t C = ctx->state[2]; ++ uint64_t D = ctx->state[3]; ++ uint64_t E = ctx->state[4]; ++ uint64_t F = ctx->state[5]; ++ uint64_t G = ctx->state[6]; ++ uint64_t H = ctx->state[7]; + for (int i = 0; i < 10; ++i) { + uint64_t temp[8]; + if (i < 2) { +@@ -552,7 +549,7 @@ void CRYPT_SHA384Finish(CRYPT_sha2_context* context, uint8_t digest[48]) { + + void CRYPT_SHA384Generate(const uint8_t* data, + uint32_t size, +- uint8_t digest[64]) { ++ uint8_t digest[48]) { + CRYPT_sha2_context context; + CRYPT_SHA384Start(&context); + CRYPT_SHA384Update(&context, data, size); +diff --git a/core/fdrm/fx_crypt_unittest.cpp b/core/fdrm/fx_crypt_unittest.cpp +index de76cd05c..f9dd66dfd 100644 +--- a/core/fdrm/fx_crypt_unittest.cpp ++++ b/core/fdrm/fx_crypt_unittest.cpp +@@ -1,20 +1,21 @@ +-// Copyright (c) 2016 The Chromium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fdrm/fx_crypt.h" + +-#include ++#include + #include ++#include + +-#include "core/fxcrt/fx_memory.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/utils/hash.h" + + namespace { + + std::string CRYPT_MD5String(const char* str) { +- return GenerateMD5Base16(reinterpret_cast(str), strlen(str)); ++ return GenerateMD5Base16( ++ {reinterpret_cast(str), strlen(str)}); + } + + void CheckArcFourContext(const CRYPT_rc4_context& context, +@@ -23,7 +24,7 @@ void CheckArcFourContext(const CRYPT_rc4_context& context, + const uint8_t* expected_permutation) { + EXPECT_EQ(expected_x, context.x); + EXPECT_EQ(expected_y, context.y); +- for (int32_t i = 0; i < kRC4ContextPermutationLength; ++i) ++ for (int32_t i = 0; i < CRYPT_rc4_context::kPermutationLength; ++i) + EXPECT_EQ(expected_permutation[i], context.m[i]) << i; + } + +@@ -199,7 +200,7 @@ TEST(FXCRYPT, Sha1Empty) { + CRYPT_SHA1Generate(reinterpret_cast(kInput), strlen(kInput), + actual); + +- for (size_t i = 0; i < FX_ArraySize(kExpected); i++) ++ for (size_t i = 0; i < std::size(kExpected); i++) + EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i; + } + +@@ -214,7 +215,7 @@ TEST(FXCRYPT, Sha1TestA1) { + CRYPT_SHA1Generate(reinterpret_cast(kInput), strlen(kInput), + actual); + +- for (size_t i = 0; i < FX_ArraySize(kExpected); i++) ++ for (size_t i = 0; i < std::size(kExpected); i++) + EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i; + } + +@@ -230,7 +231,7 @@ TEST(FXCRYPT, Sha1TestA2) { + CRYPT_SHA1Generate(reinterpret_cast(kInput), strlen(kInput), + actual); + +- for (size_t i = 0; i < FX_ArraySize(kExpected); i++) ++ for (size_t i = 0; i < std::size(kExpected); i++) + EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i; + } + +@@ -243,7 +244,7 @@ TEST(FXCRYPT, Sha256Empty) { + uint8_t actual[32]; + CRYPT_SHA256Generate(reinterpret_cast(kInput), strlen(kInput), + actual); +- for (size_t i = 0; i < FX_ArraySize(kExpected); ++i) ++ for (size_t i = 0; i < std::size(kExpected); ++i) + EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i; + } + +@@ -257,7 +258,7 @@ TEST(FXCRYPT, Sha256TestB1) { + uint8_t actual[32]; + CRYPT_SHA256Generate(reinterpret_cast(kInput), strlen(kInput), + actual); +- for (size_t i = 0; i < FX_ArraySize(kExpected); ++i) ++ for (size_t i = 0; i < std::size(kExpected); ++i) + EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i; + } + +@@ -272,60 +273,64 @@ TEST(FXCRYPT, Sha256TestB2) { + uint8_t actual[32]; + CRYPT_SHA256Generate(reinterpret_cast(kInput), strlen(kInput), + actual); +- for (size_t i = 0; i < FX_ArraySize(kExpected); ++i) ++ for (size_t i = 0; i < std::size(kExpected); ++i) + EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i; + } + + TEST(FXCRYPT, CRYPT_ArcFourSetup) { + { +- static const uint8_t kNullPermutation[kRC4ContextPermutationLength] = { +- 0, 35, 3, 43, 9, 11, 65, 229, 32, 36, 134, 98, 59, 34, +- 173, 153, 214, 200, 64, 161, 191, 62, 6, 25, 56, 234, 49, 246, +- 69, 133, 203, 194, 10, 42, 228, 198, 195, 245, 236, 91, 206, 23, +- 235, 27, 138, 18, 143, 250, 244, 76, 123, 217, 132, 249, 72, 127, +- 94, 151, 33, 60, 248, 85, 177, 210, 142, 83, 110, 140, 41, 135, +- 196, 238, 156, 242, 141, 67, 5, 185, 131, 63, 137, 37, 172, 121, +- 70, 144, 237, 130, 17, 44, 253, 166, 78, 201, 12, 119, 215, 7, +- 126, 114, 97, 192, 53, 4, 254, 45, 102, 122, 230, 88, 193, 129, +- 160, 124, 84, 108, 239, 189, 152, 120, 115, 207, 50, 176, 86, 157, +- 164, 187, 71, 1, 15, 58, 29, 21, 46, 145, 247, 162, 95, 183, +- 13, 226, 159, 175, 221, 100, 96, 202, 101, 178, 154, 47, 205, 106, +- 148, 104, 93, 112, 26, 165, 128, 186, 146, 218, 66, 211, 171, 90, +- 252, 19, 40, 99, 223, 174, 255, 51, 77, 227, 48, 220, 168, 118, +- 224, 103, 75, 105, 125, 199, 73, 82, 57, 181, 81, 149, 68, 52, +- 232, 22, 2, 216, 113, 30, 109, 163, 92, 61, 14, 8, 38, 225, +- 79, 231, 170, 240, 20, 219, 204, 150, 180, 188, 116, 190, 241, 197, +- 179, 87, 74, 147, 80, 54, 212, 16, 167, 222, 136, 213, 55, 182, +- 139, 24, 209, 251, 208, 28, 111, 89, 158, 155, 243, 107, 233, 169, +- 117, 184, 31, 39}; ++ static const uint8_t ++ kNullPermutation[CRYPT_rc4_context::kPermutationLength] = { ++ 0, 35, 3, 43, 9, 11, 65, 229, 32, 36, 134, 98, 59, ++ 34, 173, 153, 214, 200, 64, 161, 191, 62, 6, 25, 56, 234, ++ 49, 246, 69, 133, 203, 194, 10, 42, 228, 198, 195, 245, 236, ++ 91, 206, 23, 235, 27, 138, 18, 143, 250, 244, 76, 123, 217, ++ 132, 249, 72, 127, 94, 151, 33, 60, 248, 85, 177, 210, 142, ++ 83, 110, 140, 41, 135, 196, 238, 156, 242, 141, 67, 5, 185, ++ 131, 63, 137, 37, 172, 121, 70, 144, 237, 130, 17, 44, 253, ++ 166, 78, 201, 12, 119, 215, 7, 126, 114, 97, 192, 53, 4, ++ 254, 45, 102, 122, 230, 88, 193, 129, 160, 124, 84, 108, 239, ++ 189, 152, 120, 115, 207, 50, 176, 86, 157, 164, 187, 71, 1, ++ 15, 58, 29, 21, 46, 145, 247, 162, 95, 183, 13, 226, 159, ++ 175, 221, 100, 96, 202, 101, 178, 154, 47, 205, 106, 148, 104, ++ 93, 112, 26, 165, 128, 186, 146, 218, 66, 211, 171, 90, 252, ++ 19, 40, 99, 223, 174, 255, 51, 77, 227, 48, 220, 168, 118, ++ 224, 103, 75, 105, 125, 199, 73, 82, 57, 181, 81, 149, 68, ++ 52, 232, 22, 2, 216, 113, 30, 109, 163, 92, 61, 14, 8, ++ 38, 225, 79, 231, 170, 240, 20, 219, 204, 150, 180, 188, 116, ++ 190, 241, 197, 179, 87, 74, 147, 80, 54, 212, 16, 167, 222, ++ 136, 213, 55, 182, 139, 24, 209, 251, 208, 28, 111, 89, 158, ++ 155, 243, 107, 233, 169, 117, 184, 31, 39}; + CRYPT_rc4_context context; + CRYPT_ArcFourSetup(&context, {}); + CheckArcFourContext(context, 0, 0, kNullPermutation); + } + { +- static const uint8_t kFoobarPermutation[kRC4ContextPermutationLength] = { +- 102, 214, 39, 49, 17, 132, 244, 106, 114, 76, 183, 212, 116, 73, +- 42, 103, 128, 246, 139, 199, 31, 234, 25, 109, 48, 19, 121, 4, +- 20, 54, 134, 77, 163, 38, 61, 101, 145, 78, 215, 96, 92, 80, +- 224, 168, 243, 210, 82, 252, 113, 56, 217, 62, 218, 129, 125, 33, +- 99, 9, 153, 59, 43, 13, 206, 124, 131, 18, 213, 118, 173, 122, +- 193, 172, 177, 105, 148, 207, 186, 5, 85, 32, 68, 220, 79, 84, +- 169, 209, 150, 7, 133, 63, 147, 93, 26, 130, 60, 117, 250, 57, +- 24, 247, 200, 127, 136, 66, 112, 107, 140, 154, 70, 170, 185, 138, +- 248, 236, 88, 86, 44, 216, 241, 35, 100, 151, 156, 74, 119, 55, +- 245, 46, 227, 208, 229, 16, 249, 149, 53, 157, 201, 75, 58, 28, +- 142, 238, 182, 180, 179, 144, 12, 6, 176, 10, 90, 239, 104, 40, +- 181, 194, 137, 69, 221, 205, 165, 188, 191, 87, 1, 91, 2, 171, +- 232, 34, 162, 166, 160, 126, 225, 167, 123, 197, 223, 195, 22, 203, +- 189, 237, 37, 27, 222, 175, 23, 143, 152, 192, 21, 231, 228, 141, +- 30, 204, 158, 240, 120, 98, 89, 83, 135, 251, 81, 196, 161, 3, +- 8, 230, 52, 219, 41, 242, 36, 97, 15, 155, 65, 187, 254, 64, +- 159, 67, 211, 108, 178, 146, 202, 11, 164, 226, 184, 50, 190, 174, +- 71, 233, 235, 198, 95, 51, 110, 255, 253, 72, 115, 0, 47, 94, +- 29, 45, 14, 111}; ++ static const uint8_t ++ kFoobarPermutation[CRYPT_rc4_context::kPermutationLength] = { ++ 102, 214, 39, 49, 17, 132, 244, 106, 114, 76, 183, 212, 116, ++ 73, 42, 103, 128, 246, 139, 199, 31, 234, 25, 109, 48, 19, ++ 121, 4, 20, 54, 134, 77, 163, 38, 61, 101, 145, 78, 215, ++ 96, 92, 80, 224, 168, 243, 210, 82, 252, 113, 56, 217, 62, ++ 218, 129, 125, 33, 99, 9, 153, 59, 43, 13, 206, 124, 131, ++ 18, 213, 118, 173, 122, 193, 172, 177, 105, 148, 207, 186, 5, ++ 85, 32, 68, 220, 79, 84, 169, 209, 150, 7, 133, 63, 147, ++ 93, 26, 130, 60, 117, 250, 57, 24, 247, 200, 127, 136, 66, ++ 112, 107, 140, 154, 70, 170, 185, 138, 248, 236, 88, 86, 44, ++ 216, 241, 35, 100, 151, 156, 74, 119, 55, 245, 46, 227, 208, ++ 229, 16, 249, 149, 53, 157, 201, 75, 58, 28, 142, 238, 182, ++ 180, 179, 144, 12, 6, 176, 10, 90, 239, 104, 40, 181, 194, ++ 137, 69, 221, 205, 165, 188, 191, 87, 1, 91, 2, 171, 232, ++ 34, 162, 166, 160, 126, 225, 167, 123, 197, 223, 195, 22, 203, ++ 189, 237, 37, 27, 222, 175, 23, 143, 152, 192, 21, 231, 228, ++ 141, 30, 204, 158, 240, 120, 98, 89, 83, 135, 251, 81, 196, ++ 161, 3, 8, 230, 52, 219, 41, 242, 36, 97, 15, 155, 65, ++ 187, 254, 64, 159, 67, 211, 108, 178, 146, 202, 11, 164, 226, ++ 184, 50, 190, 174, 71, 233, 235, 198, 95, 51, 110, 255, 253, ++ 72, 115, 0, 47, 94, 29, 45, 14, 111}; + CRYPT_rc4_context context; + static const uint8_t kFooBar[] = "foobar"; +- CRYPT_ArcFourSetup(&context, {kFooBar, FX_ArraySize(kFooBar) - 1}); ++ CRYPT_ArcFourSetup(&context, {kFooBar, std::size(kFooBar) - 1}); + CheckArcFourContext(context, 0, 0, kFoobarPermutation); + } + } +@@ -343,21 +348,21 @@ TEST(FXCRYPT, CRYPT_ArcFourCrypt) { + CRYPT_rc4_context context; + CRYPT_ArcFourSetup(&context, {}); + +- uint8_t data_short[FX_ArraySize(kDataShort)]; +- memcpy(data_short, kDataShort, FX_ArraySize(kDataShort)); ++ uint8_t data_short[std::size(kDataShort)]; ++ memcpy(data_short, kDataShort, std::size(kDataShort)); + static const uint8_t kExpectedEncryptedDataShort[] = { + 138, 112, 236, 97, 242, 66, 52, 89, 225, 38, 88, 8, + 47, 78, 216, 24, 170, 106, 26, 199, 208, 131, 157, 242, + 55, 11, 25, 90, 66, 182, 19, 255, 210, 181, 85, 69, + 31, 240, 206, 171, 97, 62, 202, 172, 30, 252}; + static_assert( +- FX_ArraySize(kExpectedEncryptedDataShort) == FX_ArraySize(data_short), ++ std::size(kExpectedEncryptedDataShort) == std::size(data_short), + "data_short mismatch"); + CRYPT_ArcFourCrypt(&context, data_short); +- for (size_t i = 0; i < FX_ArraySize(data_short); ++i) ++ for (size_t i = 0; i < std::size(data_short); ++i) + EXPECT_EQ(kExpectedEncryptedDataShort[i], data_short[i]) << i; + +- static const uint8_t kPermutation[kRC4ContextPermutationLength] = { ++ static const uint8_t kPermutation[CRYPT_rc4_context::kPermutationLength] = { + 0, 198, 10, 37, 253, 192, 171, 183, 99, 8, 144, 103, 208, 191, + 149, 9, 228, 243, 94, 150, 169, 151, 210, 206, 221, 235, 32, 186, + 212, 122, 72, 200, 236, 138, 244, 217, 158, 213, 139, 242, 17, 143, +@@ -383,8 +388,8 @@ TEST(FXCRYPT, CRYPT_ArcFourCrypt) { + CRYPT_rc4_context context; + CRYPT_ArcFourSetup(&context, {}); + +- uint8_t data_long[FX_ArraySize(kDataLong)]; +- memcpy(data_long, kDataLong, FX_ArraySize(kDataLong)); ++ uint8_t data_long[std::size(kDataLong)]; ++ memcpy(data_long, kDataLong, std::size(kDataLong)); + static const uint8_t kExpectedEncryptedDataLong[] = { + 138, 112, 236, 97, 242, 66, 52, 89, 225, 38, 88, 8, 47, 78, + 216, 24, 170, 106, 26, 199, 208, 131, 157, 242, 55, 11, 25, 90, +@@ -406,15 +411,14 @@ TEST(FXCRYPT, CRYPT_ArcFourCrypt) { + 208, 161, 105, 226, 164, 114, 80, 137, 58, 107, 109, 42, 110, 100, + 202, 170, 224, 89, 28, 5, 138, 19, 253, 105, 220, 105, 24, 187, + 109, 89, 205, 89, 202}; +- static_assert( +- FX_ArraySize(kExpectedEncryptedDataLong) == FX_ArraySize(data_long), +- "data_long mismatch"); +- static_assert(FX_ArraySize(data_long) > 256, "too short"); ++ static_assert(std::size(kExpectedEncryptedDataLong) == std::size(data_long), ++ "data_long mismatch"); ++ static_assert(std::size(data_long) > 256, "too short"); + CRYPT_ArcFourCrypt(&context, data_long); +- for (size_t i = 0; i < FX_ArraySize(data_long); ++i) ++ for (size_t i = 0; i < std::size(data_long); ++i) + EXPECT_EQ(kExpectedEncryptedDataLong[i], data_long[i]) << i; + +- static const uint8_t kPermutation[kRC4ContextPermutationLength] = { ++ static const uint8_t kPermutation[CRYPT_rc4_context::kPermutationLength] = { + 172, 59, 196, 72, 101, 21, 215, 210, 212, 52, 243, 73, 47, 213, + 211, 50, 228, 144, 66, 93, 169, 31, 237, 206, 221, 235, 222, 250, + 97, 87, 174, 164, 190, 111, 27, 217, 173, 189, 65, 11, 115, 171, +@@ -439,23 +443,23 @@ TEST(FXCRYPT, CRYPT_ArcFourCrypt) { + { + CRYPT_rc4_context context; + static const uint8_t kFooBar[] = "foobar"; +- CRYPT_ArcFourSetup(&context, {kFooBar, FX_ArraySize(kFooBar) - 1}); ++ CRYPT_ArcFourSetup(&context, {kFooBar, std::size(kFooBar) - 1}); + +- uint8_t data_short[FX_ArraySize(kDataShort)]; +- memcpy(data_short, kDataShort, FX_ArraySize(kDataShort)); ++ uint8_t data_short[std::size(kDataShort)]; ++ memcpy(data_short, kDataShort, std::size(kDataShort)); + static const uint8_t kExpectedEncryptedDataShort[] = { + 59, 193, 117, 206, 167, 54, 218, 7, 229, 214, 188, 55, + 90, 205, 196, 25, 36, 114, 199, 218, 161, 107, 122, 119, + 106, 167, 44, 175, 240, 123, 192, 102, 174, 167, 105, 187, + 202, 70, 121, 81, 17, 30, 5, 138, 116, 166}; + static_assert( +- FX_ArraySize(kExpectedEncryptedDataShort) == FX_ArraySize(data_short), ++ std::size(kExpectedEncryptedDataShort) == std::size(data_short), + "data_short mismatch"); + CRYPT_ArcFourCrypt(&context, data_short); +- for (size_t i = 0; i < FX_ArraySize(data_short); ++i) ++ for (size_t i = 0; i < std::size(data_short); ++i) + EXPECT_EQ(kExpectedEncryptedDataShort[i], data_short[i]) << i; + +- static const uint8_t kPermutation[kRC4ContextPermutationLength] = { ++ static const uint8_t kPermutation[CRYPT_rc4_context::kPermutationLength] = { + 102, 41, 45, 82, 124, 141, 237, 38, 6, 64, 90, 140, 254, 96, + 220, 109, 99, 49, 27, 227, 205, 75, 191, 37, 17, 54, 83, 196, + 108, 79, 31, 190, 180, 0, 125, 194, 243, 156, 224, 246, 253, 193, +@@ -480,10 +484,10 @@ TEST(FXCRYPT, CRYPT_ArcFourCrypt) { + { + CRYPT_rc4_context context; + static const uint8_t kFooBar[] = "foobar"; +- CRYPT_ArcFourSetup(&context, {kFooBar, FX_ArraySize(kFooBar) - 1}); ++ CRYPT_ArcFourSetup(&context, {kFooBar, std::size(kFooBar) - 1}); + +- uint8_t data_long[FX_ArraySize(kDataLong)]; +- memcpy(data_long, kDataLong, FX_ArraySize(kDataLong)); ++ uint8_t data_long[std::size(kDataLong)]; ++ memcpy(data_long, kDataLong, std::size(kDataLong)); + static const uint8_t kExpectedEncryptedDataLong[] = { + 59, 193, 117, 206, 167, 54, 218, 7, 229, 214, 188, 55, 90, 205, + 196, 25, 36, 114, 199, 218, 161, 107, 122, 119, 106, 167, 44, 175, +@@ -505,15 +509,14 @@ TEST(FXCRYPT, CRYPT_ArcFourCrypt) { + 22, 110, 43, 56, 94, 127, 48, 96, 47, 172, 3, 31, 130, 249, + 243, 73, 206, 89, 9, 93, 156, 167, 205, 166, 75, 227, 36, 34, + 81, 124, 195, 246, 152}; +- static_assert( +- FX_ArraySize(kExpectedEncryptedDataLong) == FX_ArraySize(data_long), +- "data_long mismatch"); +- static_assert(FX_ArraySize(data_long) > 256, "too short"); ++ static_assert(std::size(kExpectedEncryptedDataLong) == std::size(data_long), ++ "data_long mismatch"); ++ static_assert(std::size(data_long) > 256, "too short"); + CRYPT_ArcFourCrypt(&context, data_long); +- for (size_t i = 0; i < FX_ArraySize(data_long); ++i) ++ for (size_t i = 0; i < std::size(data_long); ++i) + EXPECT_EQ(kExpectedEncryptedDataLong[i], data_long[i]) << i; + +- static const uint8_t kPermutation[kRC4ContextPermutationLength] = { ++ static const uint8_t kPermutation[CRYPT_rc4_context::kPermutationLength] = { + 188, 12, 81, 130, 228, 58, 124, 218, 72, 210, 50, 70, 166, 38, + 110, 111, 73, 49, 27, 227, 249, 21, 1, 226, 17, 54, 53, 16, + 108, 51, 31, 123, 221, 23, 125, 148, 5, 200, 208, 246, 253, 193, +@@ -547,7 +550,7 @@ TEST(FXCRYPT, Sha384Empty) { + uint8_t actual[48]; + CRYPT_SHA384Generate(reinterpret_cast(kInput), strlen(kInput), + actual); +- for (size_t i = 0; i < FX_ArraySize(kExpected); ++i) ++ for (size_t i = 0; i < std::size(kExpected); ++i) + EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i; + } + +@@ -563,7 +566,7 @@ TEST(FXCRYPT, Sha384Test) { + uint8_t actual[48]; + CRYPT_SHA384Generate(reinterpret_cast(kInput), strlen(kInput), + actual); +- for (size_t i = 0; i < FX_ArraySize(kExpected); ++i) ++ for (size_t i = 0; i < std::size(kExpected); ++i) + EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i; + } + +@@ -581,7 +584,7 @@ TEST(FXCRYPT, Sha384Pad112) { + EXPECT_EQ(112u, strlen(kInput)); + CRYPT_SHA384Generate(reinterpret_cast(kInput), strlen(kInput), + actual); +- for (size_t i = 0; i < FX_ArraySize(kExpected); ++i) ++ for (size_t i = 0; i < std::size(kExpected); ++i) + EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i; + } + +@@ -597,7 +600,7 @@ TEST(FXCRYPT, Sha512Empty) { + uint8_t actual[64]; + CRYPT_SHA512Generate(reinterpret_cast(kInput), strlen(kInput), + actual); +- for (size_t i = 0; i < FX_ArraySize(kExpected); ++i) ++ for (size_t i = 0; i < std::size(kExpected); ++i) + EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i; + } + +@@ -615,7 +618,7 @@ TEST(FXCRYPT, Sha512Test) { + uint8_t actual[64]; + CRYPT_SHA512Generate(reinterpret_cast(kInput), strlen(kInput), + actual); +- for (size_t i = 0; i < FX_ArraySize(kExpected); ++i) ++ for (size_t i = 0; i < std::size(kExpected); ++i) + EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i; + } + +@@ -635,6 +638,6 @@ TEST(FXCRYPT, Sha512Pad112) { + EXPECT_EQ(112u, strlen(kInput)); + CRYPT_SHA512Generate(reinterpret_cast(kInput), strlen(kInput), + actual); +- for (size_t i = 0; i < FX_ArraySize(kExpected); ++i) ++ for (size_t i = 0; i < std::size(kExpected); ++i) + EXPECT_EQ(kExpected[i], actual[i]) << " at byte " << i; + } +diff --git a/core/fpdfapi/cmaps/BUILD.gn b/core/fpdfapi/cmaps/BUILD.gn +index e7fddbfc0..23079c854 100644 +--- a/core/fpdfapi/cmaps/BUILD.gn ++++ b/core/fpdfapi/cmaps/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -70,7 +70,10 @@ source_set("cmaps") { + "fpdf_cmaps.cpp", + "fpdf_cmaps.h", + ] +- configs += [ "../../../:pdfium_core_config" ] ++ configs += [ ++ "../../../:pdfium_strict_config", ++ "../../../:pdfium_noshorten_config", ++ ] + deps = [ "../../fxcrt" ] + visibility = [ "../../../*" ] + } +diff --git a/core/fpdfapi/cmaps/CNS1/Adobe-CNS1-UCS2_5.cpp b/core/fpdfapi/cmaps/CNS1/Adobe-CNS1-UCS2_5.cpp +index c9042399c..154233b84 100644 +--- a/core/fpdfapi/cmaps/CNS1/Adobe-CNS1-UCS2_5.cpp ++++ b/core/fpdfapi/cmaps/CNS1/Adobe-CNS1-UCS2_5.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h" + +-const uint16_t g_FXCMAP_CNS1CID2Unicode_5[19088] = { ++namespace fxcmap { ++ ++const uint16_t kCNS1CID2Unicode_5[19088] = { + 0xFFFD, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, + 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, +@@ -2129,3 +2131,5 @@ const uint16_t g_FXCMAP_CNS1CID2Unicode_5[19088] = { + 0x0000, 0x0000, 0x456D, 0x38D4, 0x0000, 0x4561, 0x451B, 0x4D89, 0x4C7B, + 0x4D76, 0x45EA, 0x3FC8, 0x0000, 0x3661, 0x44DE, 0x44BD, 0x41ED, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/CNS1/B5pc-H_0.cpp b/core/fpdfapi/cmaps/CNS1/B5pc-H_0.cpp +index 0c9606513..d24620ca8 100644 +--- a/core/fpdfapi/cmaps/CNS1/B5pc-H_0.cpp ++++ b/core/fpdfapi/cmaps/CNS1/B5pc-H_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h" + +-const uint16_t g_FXCMAP_B5pc_H_0[247 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kB5pc_H_0[247 * 3] = { + 0x0020, 0x007E, 0x0001, 0x0080, 0x0080, 0x003D, 0x00FD, 0x00FF, 0x0060, + 0xA140, 0xA158, 0x0063, 0xA159, 0xA15C, 0x35AF, 0xA15D, 0xA17E, 0x0080, + 0xA1A1, 0xA1F5, 0x00A2, 0xA1F6, 0xA1F6, 0x00F8, 0xA1F7, 0xA1F7, 0x00F7, +@@ -91,3 +93,5 @@ const uint16_t g_FXCMAP_B5pc_H_0[247 * 3] = { + 0xF9C5, 0xF9C5, 0x353D, 0xF9C6, 0xF9C6, 0x3549, 0xF9C7, 0xF9D1, 0x353E, + 0xF9D2, 0xF9D5, 0x354A, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/CNS1/B5pc-V_0.cpp b/core/fpdfapi/cmaps/CNS1/B5pc-V_0.cpp +index 88768d692..b7a17a522 100644 +--- a/core/fpdfapi/cmaps/CNS1/B5pc-V_0.cpp ++++ b/core/fpdfapi/cmaps/CNS1/B5pc-V_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,9 +6,13 @@ + + #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h" + +-const uint16_t g_FXCMAP_B5pc_V_0[12 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kB5pc_V_0[12 * 3] = { + 0xA14B, 0xA14B, 0x354E, 0xA15A, 0xA15A, 0x35AF, 0xA15C, 0xA15C, 0x35B1, + 0xA15D, 0xA15E, 0x0082, 0xA161, 0xA162, 0x0086, 0xA165, 0xA166, 0x008A, + 0xA169, 0xA16A, 0x008E, 0xA16D, 0xA16E, 0x0092, 0xA171, 0xA172, 0x0096, + 0xA175, 0xA176, 0x009A, 0xA179, 0xA17A, 0x009E, 0xA1E3, 0xA1E3, 0x354F, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/CNS1/CNS-EUC-H_0.cpp b/core/fpdfapi/cmaps/CNS1/CNS-EUC-H_0.cpp +index 9d0cee8c6..cfa4eac2f 100644 +--- a/core/fpdfapi/cmaps/CNS1/CNS-EUC-H_0.cpp ++++ b/core/fpdfapi/cmaps/CNS1/CNS-EUC-H_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h" + +-const uint16_t g_FXCMAP_CNS_EUC_H_0[157 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kCNS_EUC_H_0[157 * 3] = { + 0x0020, 0x007E, 0x3550, 0xA1A1, 0xA1FE, 0x0063, 0xA2A1, 0xA2FE, 0x00C1, + 0xA3A1, 0xA3CE, 0x011F, 0xA4A1, 0xA4FE, 0x014D, 0xA5A1, 0xA5EC, 0x01AB, + 0xA5EE, 0xA5F0, 0x01F7, 0xA6A1, 0xA6BE, 0x01FA, 0xA7A1, 0xA7A1, 0x0253, +@@ -62,7 +64,7 @@ const uint16_t g_FXCMAP_CNS_EUC_H_0[157 * 3] = { + 0xFDA1, 0xFDCB, 0x1741, + }; + +-const FXCMAP_DWordCIDMap g_FXCMAP_CNS_EUC_H_0_DWord[238] = { ++const DWordCIDMap kCNS_EUC_H_0_DWord[238] = { + {0x8EA1, 0xA1A1, 0xA1FE, 0x0063}, {0x8EA1, 0xA2A1, 0xA2FE, 0x00C1}, + {0x8EA1, 0xA3A1, 0xA3CE, 0x011F}, {0x8EA1, 0xA4A1, 0xA4FE, 0x014D}, + {0x8EA1, 0xA5A1, 0xA5EC, 0x01AB}, {0x8EA1, 0xA5EE, 0xA5F0, 0x01F7}, +@@ -183,3 +185,5 @@ const FXCMAP_DWordCIDMap g_FXCMAP_CNS_EUC_H_0_DWord[238] = { + {0x8EA2, 0xEFA1, 0xEFFE, 0x3410}, {0x8EA2, 0xF0A1, 0xF0FE, 0x346E}, + {0x8EA2, 0xF1A1, 0xF1FE, 0x34CC}, {0x8EA2, 0xF2A1, 0xF2C4, 0x352A}, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/CNS1/CNS-EUC-V_0.cpp b/core/fpdfapi/cmaps/CNS1/CNS-EUC-V_0.cpp +index 16981068a..97834d938 100644 +--- a/core/fpdfapi/cmaps/CNS1/CNS-EUC-V_0.cpp ++++ b/core/fpdfapi/cmaps/CNS1/CNS-EUC-V_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h" + +-const uint16_t g_FXCMAP_CNS_EUC_V_0[180 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kCNS_EUC_V_0[180 * 3] = { + 0x0020, 0x007E, 0x3550, 0xA1A1, 0xA1AB, 0x0063, 0xA1AC, 0xA1AC, 0x354E, + 0xA1AD, 0xA1BA, 0x006F, 0xA1BB, 0xA1BB, 0x007C, 0xA1BC, 0xA1BC, 0x007E, + 0xA1BD, 0xA1BD, 0x007E, 0xA1BE, 0xA1BF, 0x0082, 0xA1C0, 0xA1C1, 0x0082, +@@ -69,7 +71,7 @@ const uint16_t g_FXCMAP_CNS_EUC_V_0[180 * 3] = { + 0xFBA1, 0xFBFE, 0x1685, 0xFCA1, 0xFCFE, 0x16E3, 0xFDA1, 0xFDCB, 0x1741, + }; + +-const FXCMAP_DWordCIDMap g_FXCMAP_CNS_EUC_V_0_DWord[261] = { ++const DWordCIDMap kCNS_EUC_V_0_DWord[261] = { + {0x8EA1, 0xA1A1, 0xA1AB, 0x0063}, {0x8EA1, 0xA1AC, 0xA1AC, 0x354E}, + {0x8EA1, 0xA1AD, 0xA1BA, 0x006F}, {0x8EA1, 0xA1BB, 0xA1BB, 0x007C}, + {0x8EA1, 0xA1BC, 0xA1BC, 0x007E}, {0x8EA1, 0xA1BD, 0xA1BD, 0x007E}, +@@ -202,3 +204,5 @@ const FXCMAP_DWordCIDMap g_FXCMAP_CNS_EUC_V_0_DWord[261] = { + {0x8EA2, 0xF0A1, 0xF0FE, 0x346E}, {0x8EA2, 0xF1A1, 0xF1FE, 0x34CC}, + {0x8EA2, 0xF2A1, 0xF2C4, 0x352A}, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/CNS1/ETen-B5-H_0.cpp b/core/fpdfapi/cmaps/CNS1/ETen-B5-H_0.cpp +index 3138ae99f..44687c13b 100644 +--- a/core/fpdfapi/cmaps/CNS1/ETen-B5-H_0.cpp ++++ b/core/fpdfapi/cmaps/CNS1/ETen-B5-H_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h" + +-const uint16_t g_FXCMAP_ETen_B5_H_0[254 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kETen_B5_H_0[254 * 3] = { + 0x0020, 0x007E, 0x3550, 0xA140, 0xA158, 0x0063, 0xA159, 0xA15C, 0x35AF, + 0xA15D, 0xA17E, 0x0080, 0xA1A1, 0xA1F5, 0x00A2, 0xA1F6, 0xA1F6, 0x00F8, + 0xA1F7, 0xA1F7, 0x00F7, 0xA1F8, 0xA1FE, 0x00F9, 0xA240, 0xA27E, 0x0100, +@@ -93,3 +95,5 @@ const uint16_t g_FXCMAP_ETen_B5_H_0[254 * 3] = { + 0xF9C5, 0xF9C5, 0x353D, 0xF9C6, 0xF9C6, 0x3549, 0xF9C7, 0xF9D1, 0x353E, + 0xF9D2, 0xF9D5, 0x354A, 0xF9D6, 0xF9FE, 0x36E8, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/CNS1/ETen-B5-V_0.cpp b/core/fpdfapi/cmaps/CNS1/ETen-B5-V_0.cpp +index 622f2ff25..38359e8d5 100644 +--- a/core/fpdfapi/cmaps/CNS1/ETen-B5-V_0.cpp ++++ b/core/fpdfapi/cmaps/CNS1/ETen-B5-V_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,10 +6,14 @@ + + #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h" + +-const uint16_t g_FXCMAP_ETen_B5_V_0[13 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kETen_B5_V_0[13 * 3] = { + 0xA14B, 0xA14B, 0x354E, 0xA15A, 0xA15A, 0x35AF, 0xA15C, 0xA15C, + 0x35B1, 0xA15D, 0xA15E, 0x0082, 0xA161, 0xA162, 0x0086, 0xA165, + 0xA166, 0x008A, 0xA169, 0xA16A, 0x008E, 0xA16D, 0xA16E, 0x0092, + 0xA171, 0xA172, 0x0096, 0xA175, 0xA176, 0x009A, 0xA179, 0xA17A, + 0x009E, 0xA1E3, 0xA1E3, 0x354F, 0xC6E4, 0xC6E5, 0x3711, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/CNS1/ETenms-B5-H_0.cpp b/core/fpdfapi/cmaps/CNS1/ETenms-B5-H_0.cpp +index 3cc5573d7..0f44566f0 100644 +--- a/core/fpdfapi/cmaps/CNS1/ETenms-B5-H_0.cpp ++++ b/core/fpdfapi/cmaps/CNS1/ETenms-B5-H_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,8 +6,12 @@ + + #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h" + +-const uint16_t g_FXCMAP_ETenms_B5_H_0[1 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kETenms_B5_H_0[1 * 3] = { + 0x0020, + 0x007E, + 0x0001, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/CNS1/ETenms-B5-V_0.cpp b/core/fpdfapi/cmaps/CNS1/ETenms-B5-V_0.cpp +index de922490c..5aafd1629 100644 +--- a/core/fpdfapi/cmaps/CNS1/ETenms-B5-V_0.cpp ++++ b/core/fpdfapi/cmaps/CNS1/ETenms-B5-V_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h" + +-const uint16_t g_FXCMAP_ETenms_B5_V_0[18 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kETenms_B5_V_0[18 * 3] = { + 0xA14B, 0xA14B, 0x354E, 0xA14C, 0xA14C, 0x006D, 0xA156, 0xA156, 0x0138, + 0xA158, 0xA158, 0x007A, 0xA15A, 0xA15A, 0x35AF, 0xA15C, 0xA15C, 0x35B1, + 0xA15D, 0xA15E, 0x0082, 0xA161, 0xA162, 0x0086, 0xA165, 0xA166, 0x008A, +@@ -14,3 +16,5 @@ const uint16_t g_FXCMAP_ETenms_B5_V_0[18 * 3] = { + 0xA175, 0xA176, 0x009A, 0xA179, 0xA17A, 0x009E, 0xA17D, 0xA17E, 0x0082, + 0xA1A1, 0xA1A2, 0x0086, 0xA1A3, 0xA1A4, 0x008A, 0xC6E4, 0xC6E5, 0x3711, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/CNS1/HKscs-B5-H_5.cpp b/core/fpdfapi/cmaps/CNS1/HKscs-B5-H_5.cpp +index 47f7d6ac8..e073f3e82 100644 +--- a/core/fpdfapi/cmaps/CNS1/HKscs-B5-H_5.cpp ++++ b/core/fpdfapi/cmaps/CNS1/HKscs-B5-H_5.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h" + +-const uint16_t g_FXCMAP_HKscs_B5_H_5[1210 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kHKscs_B5_H_5[1210 * 3] = { + 0x0020, 0x007E, 0x0001, 0x8740, 0x8765, 0x4A15, 0x8767, 0x8779, 0x4A3B, + 0x8840, 0x8855, 0x44C9, 0x8856, 0x887E, 0x4961, 0x88A1, 0x88A8, 0x498A, + 0x88A9, 0x88AA, 0x499C, 0x8940, 0x8941, 0x4534, 0x8943, 0x8943, 0x4536, +@@ -412,3 +414,5 @@ const uint16_t g_FXCMAP_HKscs_B5_H_5[1210 * 3] = { + 0xFEDE, 0xFEDF, 0x495D, 0xFEE0, 0xFEEC, 0x42EB, 0xFEED, 0xFEEE, 0x495F, + 0xFEEF, 0xFEFE, 0x42F8, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/CNS1/HKscs-B5-V_5.cpp b/core/fpdfapi/cmaps/CNS1/HKscs-B5-V_5.cpp +index a833497f6..ab6418ce0 100644 +--- a/core/fpdfapi/cmaps/CNS1/HKscs-B5-V_5.cpp ++++ b/core/fpdfapi/cmaps/CNS1/HKscs-B5-V_5.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,10 +6,14 @@ + + #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h" + +-const uint16_t g_FXCMAP_HKscs_B5_V_5[13 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kHKscs_B5_V_5[13 * 3] = { + 0xA14B, 0xA14B, 0x354E, 0xA15A, 0xA15A, 0x35AF, 0xA15C, 0xA15C, + 0x35B1, 0xA15D, 0xA15E, 0x0082, 0xA161, 0xA162, 0x0086, 0xA165, + 0xA166, 0x008A, 0xA169, 0xA16A, 0x008E, 0xA16D, 0xA16E, 0x0092, + 0xA171, 0xA172, 0x0096, 0xA175, 0xA176, 0x009A, 0xA179, 0xA17A, + 0x009E, 0xA1E3, 0xA1E3, 0x354F, 0xC6E4, 0xC6E5, 0x3711, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-H_3.cpp b/core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-H_3.cpp +index bfe6a3425..fb13a37ff 100644 +--- a/core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-H_3.cpp ++++ b/core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-H_3.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h" + +-const uint16_t g_FXCMAP_UniCNS_UCS2_H_3[16418 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kUniCNS_UCS2_H_3[16418 * 3] = { + 0x0020, 0x007E, 0x0001, 0x00A2, 0x00A3, 0x0106, 0x00A5, 0x00A5, 0x0104, + 0x00A7, 0x00A7, 0x00B2, 0x00A8, 0x00A8, 0x35B3, 0x00AC, 0x00AC, 0x36E1, + 0x00B0, 0x00B0, 0x0118, 0x00B1, 0x00B1, 0x00D4, 0x00B7, 0x00B7, 0x0073, +@@ -5481,3 +5483,5 @@ const uint16_t g_FXCMAP_UniCNS_UCS2_H_3[16418 * 3] = { + 0xFF5C, 0xFF5C, 0x0078, 0xFF5D, 0xFF5D, 0x0085, 0xFF64, 0xFF64, 0x0071, + 0xFFE2, 0xFFE2, 0x36E1, 0xFFE4, 0xFFE4, 0x36E2, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-V_3.cpp b/core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-V_3.cpp +index dfa7b879d..36cb2a6d4 100644 +--- a/core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-V_3.cpp ++++ b/core/fpdfapi/cmaps/CNS1/UniCNS-UCS2-V_3.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,10 +6,14 @@ + + #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h" + +-const uint16_t g_FXCMAP_UniCNS_UCS2_V_3[13 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kUniCNS_UCS2_V_3[13 * 3] = { + 0x2013, 0x2013, 0x0078, 0x2014, 0x2014, 0x007A, 0x2025, 0x2025, + 0x006D, 0x3008, 0x3009, 0x0096, 0x300A, 0x300B, 0x0092, 0x300C, + 0x300D, 0x009A, 0x300E, 0x300F, 0x009E, 0x3010, 0x3011, 0x008E, + 0x3014, 0x3015, 0x008A, 0xFE4F, 0xFE4F, 0x35B1, 0xFF08, 0xFF09, + 0x0082, 0xFF5B, 0xFF5B, 0x0086, 0xFF5D, 0xFF5D, 0x0087, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/CNS1/UniCNS-UTF16-H_0.cpp b/core/fpdfapi/cmaps/CNS1/UniCNS-UTF16-H_0.cpp +index a033c13dc..05fc71500 100644 +--- a/core/fpdfapi/cmaps/CNS1/UniCNS-UTF16-H_0.cpp ++++ b/core/fpdfapi/cmaps/CNS1/UniCNS-UTF16-H_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h" + +-const uint16_t g_FXCMAP_UniCNS_UTF16_H_0[14557 * 2] = { ++namespace fxcmap { ++ ++const uint16_t kUniCNS_UTF16_H_0[14557 * 2] = { + 0x0020, 0x0001, 0x0021, 0x0002, 0x0022, 0x0003, 0x0023, 0x0004, 0x0024, + 0x0005, 0x0025, 0x0006, 0x0026, 0x0007, 0x0027, 0x0008, 0x0028, 0x0009, + 0x0029, 0x000A, 0x002A, 0x000B, 0x002B, 0x000C, 0x002C, 0x000D, 0x002D, +@@ -3243,3 +3245,5 @@ const uint16_t g_FXCMAP_UniCNS_UTF16_H_0[14557 * 2] = { + 0x0085, 0xFF5E, 0x00E4, 0xFF64, 0x0071, 0xFFE0, 0x0106, 0xFFE1, 0x0107, + 0xFFE2, 0x36E1, 0xFFE3, 0x00C4, 0xFFE4, 0x36E2, 0xFFE5, 0x0104, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/CNS1/cmaps_cns1.cpp b/core/fpdfapi/cmaps/CNS1/cmaps_cns1.cpp +index 810104eee..18a1970d4 100644 +--- a/core/fpdfapi/cmaps/CNS1/cmaps_cns1.cpp ++++ b/core/fpdfapi/cmaps/CNS1/cmaps_cns1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,33 +6,32 @@ + + #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h" + +-#include "core/fxcrt/fx_memory.h" ++#include + +-const FXCMAP_CMap g_FXCMAP_CNS1_cmaps[] = { +- {"B5pc-H", g_FXCMAP_B5pc_H_0, nullptr, 247, 0, FXCMAP_CMap::Range, 0}, +- {"B5pc-V", g_FXCMAP_B5pc_V_0, nullptr, 12, 0, FXCMAP_CMap::Range, -1}, +- {"HKscs-B5-H", g_FXCMAP_HKscs_B5_H_5, nullptr, 1210, 0, FXCMAP_CMap::Range, ++namespace fxcmap { ++ ++const CMap kCNS1_cmaps[] = { ++ {"B5pc-H", kB5pc_H_0, nullptr, 247, 0, CMap::Type::kRange, 0}, ++ {"B5pc-V", kB5pc_V_0, nullptr, 12, 0, CMap::Type::kRange, -1}, ++ {"HKscs-B5-H", kHKscs_B5_H_5, nullptr, 1210, 0, CMap::Type::kRange, 0}, ++ {"HKscs-B5-V", kHKscs_B5_V_5, nullptr, 13, 0, CMap::Type::kRange, -1}, ++ {"ETen-B5-H", kETen_B5_H_0, nullptr, 254, 0, CMap::Type::kRange, 0}, ++ {"ETen-B5-V", kETen_B5_V_0, nullptr, 13, 0, CMap::Type::kRange, -1}, ++ {"ETenms-B5-H", kETenms_B5_H_0, nullptr, 1, 0, CMap::Type::kRange, -2}, ++ {"ETenms-B5-V", kETenms_B5_V_0, nullptr, 18, 0, CMap::Type::kRange, -1}, ++ {"CNS-EUC-H", kCNS_EUC_H_0, kCNS_EUC_H_0_DWord, 157, 238, ++ CMap::Type::kRange, 0}, ++ {"CNS-EUC-V", kCNS_EUC_V_0, kCNS_EUC_V_0_DWord, 180, 261, ++ CMap::Type::kRange, 0}, ++ {"UniCNS-UCS2-H", kUniCNS_UCS2_H_3, nullptr, 16418, 0, CMap::Type::kRange, + 0}, +- {"HKscs-B5-V", g_FXCMAP_HKscs_B5_V_5, nullptr, 13, 0, FXCMAP_CMap::Range, +- -1}, +- {"ETen-B5-H", g_FXCMAP_ETen_B5_H_0, nullptr, 254, 0, FXCMAP_CMap::Range, 0}, +- {"ETen-B5-V", g_FXCMAP_ETen_B5_V_0, nullptr, 13, 0, FXCMAP_CMap::Range, -1}, +- {"ETenms-B5-H", g_FXCMAP_ETenms_B5_H_0, nullptr, 1, 0, FXCMAP_CMap::Range, +- -2}, +- {"ETenms-B5-V", g_FXCMAP_ETenms_B5_V_0, nullptr, 18, 0, FXCMAP_CMap::Range, ++ {"UniCNS-UCS2-V", kUniCNS_UCS2_V_3, nullptr, 13, 0, CMap::Type::kRange, -1}, ++ {"UniCNS-UTF16-H", kUniCNS_UTF16_H_0, nullptr, 14557, 0, ++ CMap::Type::kSingle, 0}, ++ {"UniCNS-UTF16-V", kUniCNS_UCS2_V_3, nullptr, 13, 0, CMap::Type::kRange, + -1}, +- {"CNS-EUC-H", g_FXCMAP_CNS_EUC_H_0, g_FXCMAP_CNS_EUC_H_0_DWord, 157, 238, +- FXCMAP_CMap::Range, 0}, +- {"CNS-EUC-V", g_FXCMAP_CNS_EUC_V_0, g_FXCMAP_CNS_EUC_V_0_DWord, 180, 261, +- FXCMAP_CMap::Range, 0}, +- {"UniCNS-UCS2-H", g_FXCMAP_UniCNS_UCS2_H_3, nullptr, 16418, 0, +- FXCMAP_CMap::Range, 0}, +- {"UniCNS-UCS2-V", g_FXCMAP_UniCNS_UCS2_V_3, nullptr, 13, 0, +- FXCMAP_CMap::Range, -1}, +- {"UniCNS-UTF16-H", g_FXCMAP_UniCNS_UTF16_H_0, nullptr, 14557, 0, +- FXCMAP_CMap::Single, 0}, +- {"UniCNS-UTF16-V", g_FXCMAP_UniCNS_UCS2_V_3, nullptr, 13, 0, +- FXCMAP_CMap::Range, -1}, + }; + +-const size_t g_FXCMAP_CNS1_cmaps_size = FX_ArraySize(g_FXCMAP_CNS1_cmaps); ++const size_t kCNS1_cmaps_size = std::size(kCNS1_cmaps); ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/CNS1/cmaps_cns1.h b/core/fpdfapi/cmaps/CNS1/cmaps_cns1.h +index 64f6b8b54..75b31cbf1 100644 +--- a/core/fpdfapi/cmaps/CNS1/cmaps_cns1.h ++++ b/core/fpdfapi/cmaps/CNS1/cmaps_cns1.h +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,25 +7,32 @@ + #ifndef CORE_FPDFAPI_CMAPS_CNS1_CMAPS_CNS1_H_ + #define CORE_FPDFAPI_CMAPS_CNS1_CMAPS_CNS1_H_ + ++#include ++#include ++ + #include "core/fpdfapi/cmaps/fpdf_cmaps.h" + +-extern const uint16_t g_FXCMAP_B5pc_H_0[]; +-extern const uint16_t g_FXCMAP_B5pc_V_0[]; +-extern const uint16_t g_FXCMAP_HKscs_B5_H_5[]; +-extern const uint16_t g_FXCMAP_HKscs_B5_V_5[]; +-extern const uint16_t g_FXCMAP_ETen_B5_H_0[]; +-extern const uint16_t g_FXCMAP_ETen_B5_V_0[]; +-extern const uint16_t g_FXCMAP_ETenms_B5_H_0[]; +-extern const uint16_t g_FXCMAP_ETenms_B5_V_0[]; +-extern const uint16_t g_FXCMAP_CNS_EUC_H_0[]; +-extern const FXCMAP_DWordCIDMap g_FXCMAP_CNS_EUC_H_0_DWord[]; +-extern const uint16_t g_FXCMAP_CNS_EUC_V_0[]; +-extern const FXCMAP_DWordCIDMap g_FXCMAP_CNS_EUC_V_0_DWord[]; +-extern const uint16_t g_FXCMAP_UniCNS_UCS2_H_3[]; +-extern const uint16_t g_FXCMAP_UniCNS_UCS2_V_3[]; +-extern const uint16_t g_FXCMAP_UniCNS_UTF16_H_0[]; +-extern const uint16_t g_FXCMAP_CNS1CID2Unicode_5[19088]; +-extern const FXCMAP_CMap g_FXCMAP_CNS1_cmaps[]; +-extern const size_t g_FXCMAP_CNS1_cmaps_size; ++namespace fxcmap { ++ ++extern const uint16_t kB5pc_H_0[]; ++extern const uint16_t kB5pc_V_0[]; ++extern const uint16_t kHKscs_B5_H_5[]; ++extern const uint16_t kHKscs_B5_V_5[]; ++extern const uint16_t kETen_B5_H_0[]; ++extern const uint16_t kETen_B5_V_0[]; ++extern const uint16_t kETenms_B5_H_0[]; ++extern const uint16_t kETenms_B5_V_0[]; ++extern const uint16_t kCNS_EUC_H_0[]; ++extern const DWordCIDMap kCNS_EUC_H_0_DWord[]; ++extern const uint16_t kCNS_EUC_V_0[]; ++extern const DWordCIDMap kCNS_EUC_V_0_DWord[]; ++extern const uint16_t kUniCNS_UCS2_H_3[]; ++extern const uint16_t kUniCNS_UCS2_V_3[]; ++extern const uint16_t kUniCNS_UTF16_H_0[]; ++extern const uint16_t kCNS1CID2Unicode_5[19088]; ++extern const CMap kCNS1_cmaps[]; ++extern const size_t kCNS1_cmaps_size; ++ ++} // namespace fxcmap + + #endif // CORE_FPDFAPI_CMAPS_CNS1_CMAPS_CNS1_H_ +diff --git a/core/fpdfapi/cmaps/GB1/Adobe-GB1-UCS2_5.cpp b/core/fpdfapi/cmaps/GB1/Adobe-GB1-UCS2_5.cpp +index 5a5ae048a..b987a24d5 100644 +--- a/core/fpdfapi/cmaps/GB1/Adobe-GB1-UCS2_5.cpp ++++ b/core/fpdfapi/cmaps/GB1/Adobe-GB1-UCS2_5.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h" + +-const uint16_t g_FXCMAP_GB1CID2Unicode_5[30284] = { ++namespace fxcmap { ++ ++const uint16_t kGB1CID2Unicode_5[30284] = { + 0xFFFD, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, + 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, +@@ -3373,3 +3375,5 @@ const uint16_t g_FXCMAP_GB1CID2Unicode_5[30284] = { + 0xA4B6, 0xA4B7, 0xA4B8, 0xA4B9, 0xA4BA, 0xA4BB, 0xA4BC, 0xA4BD, 0xA4BE, + 0xA4BF, 0xA4C0, 0xA4C1, 0xA4C2, 0xA4C3, 0xA4C4, 0xA4C5, 0xA4C6, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/GB1/GB-EUC-H_0.cpp b/core/fpdfapi/cmaps/GB1/GB-EUC-H_0.cpp +index 9bbfe32ec..6abfa336f 100644 +--- a/core/fpdfapi/cmaps/GB1/GB-EUC-H_0.cpp ++++ b/core/fpdfapi/cmaps/GB1/GB-EUC-H_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h" + +-const uint16_t g_FXCMAP_GB_EUC_H_0[90 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kGB_EUC_H_0[90 * 3] = { + 0x0020, 0x0020, 0x1E24, 0x0021, 0x007E, 0x032E, 0xA1A1, 0xA1FE, 0x0060, + 0xA2B1, 0xA2E2, 0x00BE, 0xA2E5, 0xA2EE, 0x00F0, 0xA2F1, 0xA2FC, 0x00FA, + 0xA3A1, 0xA3FE, 0x0106, 0xA4A1, 0xA4F3, 0x0164, 0xA5A1, 0xA5F6, 0x01B7, +@@ -38,3 +40,5 @@ const uint16_t g_FXCMAP_GB_EUC_H_0[90 * 3] = { + 0xF2A1, 0xF2FE, 0x1BE3, 0xF3A1, 0xF3FE, 0x1C41, 0xF4A1, 0xF4FE, 0x1C9F, + 0xF5A1, 0xF5FE, 0x1CFD, 0xF6A1, 0xF6FE, 0x1D5B, 0xF7A1, 0xF7FE, 0x1DB9, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/GB1/GB-EUC-V_0.cpp b/core/fpdfapi/cmaps/GB1/GB-EUC-V_0.cpp +index 19bd4182f..0f3d13635 100644 +--- a/core/fpdfapi/cmaps/GB1/GB-EUC-V_0.cpp ++++ b/core/fpdfapi/cmaps/GB1/GB-EUC-V_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h" + +-const uint16_t g_FXCMAP_GB_EUC_V_0[20 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kGB_EUC_V_0[20 * 3] = { + 0xA1A2, 0xA1A2, 0x023F, 0xA1A3, 0xA1A3, 0x023E, 0xA1AA, 0xA1AA, 0x0256, + 0xA1AB, 0xA1AC, 0x1E18, 0xA1AD, 0xA1AD, 0x0257, 0xA1B2, 0xA1BF, 0x0246, + 0xA1FE, 0xA1FE, 0x1E1A, 0xA3A1, 0xA3A1, 0x0242, 0xA3A8, 0xA3A9, 0x0244, +@@ -15,3 +17,5 @@ const uint16_t g_FXCMAP_GB_EUC_V_0[20 * 3] = { + 0xA3DD, 0xA3DD, 0x1E1E, 0xA3DF, 0xA3DF, 0x0258, 0xA3FB, 0xA3FB, 0x0254, + 0xA3FD, 0xA3FD, 0x0255, 0xA3FE, 0xA3FE, 0x1E1F, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/GB1/GBK-EUC-H_2.cpp b/core/fpdfapi/cmaps/GB1/GBK-EUC-H_2.cpp +index 617f020b8..5cfc25fe7 100644 +--- a/core/fpdfapi/cmaps/GB1/GBK-EUC-H_2.cpp ++++ b/core/fpdfapi/cmaps/GB1/GBK-EUC-H_2.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h" + +-const uint16_t g_FXCMAP_GBK_EUC_H_2[4071 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kGBK_EUC_H_2[4071 * 3] = { + 0x0020, 0x0020, 0x1E24, 0x0021, 0x007E, 0x032E, 0x8140, 0x8178, 0x2758, + 0x8179, 0x8179, 0x2059, 0x817A, 0x817E, 0x2791, 0x8180, 0x8185, 0x2796, + 0x8186, 0x8186, 0x21F1, 0x8187, 0x81EC, 0x279C, 0x81ED, 0x81ED, 0x1FF2, +@@ -1365,3 +1367,5 @@ const uint16_t g_FXCMAP_GBK_EUC_H_2[4071 * 3] = { + 0xFD9E, 0xFD9E, 0x40D3, 0xFD9F, 0xFD9F, 0x200C, 0xFDA0, 0xFDA0, 0x5083, + 0xFE40, 0xFE40, 0x1259, 0xFE41, 0xFE7E, 0x5610, 0xFE80, 0xFEA0, 0x564E, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/GB1/GBK-EUC-V_2.cpp b/core/fpdfapi/cmaps/GB1/GBK-EUC-V_2.cpp +index 87e53a7b7..ea59b4171 100644 +--- a/core/fpdfapi/cmaps/GB1/GBK-EUC-V_2.cpp ++++ b/core/fpdfapi/cmaps/GB1/GBK-EUC-V_2.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h" + +-const uint16_t g_FXCMAP_GBK_EUC_V_2[20 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kGBK_EUC_V_2[20 * 3] = { + 0xA1A2, 0xA1A2, 0x023F, 0xA1A3, 0xA1A3, 0x023E, 0xA1AA, 0xA1AA, 0x0256, + 0xA1AB, 0xA1AC, 0x1E18, 0xA1AD, 0xA1AD, 0x0257, 0xA1B2, 0xA1BF, 0x0246, + 0xA1FE, 0xA1FE, 0x1E1A, 0xA3A1, 0xA3A1, 0x0242, 0xA3A8, 0xA3A9, 0x0244, +@@ -15,3 +17,5 @@ const uint16_t g_FXCMAP_GBK_EUC_V_2[20 * 3] = { + 0xA3DD, 0xA3DD, 0x1E1E, 0xA3DF, 0xA3DF, 0x0258, 0xA3FB, 0xA3FB, 0x0254, + 0xA3FD, 0xA3FD, 0x0255, 0xA3FE, 0xA3FE, 0x1E1F, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/GB1/GBK2K-H_5.cpp b/core/fpdfapi/cmaps/GB1/GBK2K-H_5.cpp +index 7d7b48acd..4ac5804f8 100644 +--- a/core/fpdfapi/cmaps/GB1/GBK2K-H_5.cpp ++++ b/core/fpdfapi/cmaps/GB1/GBK2K-H_5.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h" + +-const uint16_t g_FXCMAP_GBK2K_H_5[4071 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kGBK2K_H_5[4071 * 3] = { + 0x0020, 0x007E, 0x0001, 0x8140, 0x8178, 0x2758, 0x8179, 0x8179, 0x2059, + 0x817A, 0x817E, 0x2791, 0x8180, 0x8185, 0x2796, 0x8186, 0x8186, 0x21F1, + 0x8187, 0x81EC, 0x279C, 0x81ED, 0x81ED, 0x1FF2, 0x81EE, 0x81F5, 0x2802, +@@ -1366,7 +1368,7 @@ const uint16_t g_FXCMAP_GBK2K_H_5[4071 * 3] = { + 0xFE40, 0xFE40, 0x1259, 0xFE41, 0xFE7E, 0x5610, 0xFE80, 0xFEA0, 0x564E, + }; + +-const FXCMAP_DWordCIDMap g_FXCMAP_GBK2K_H_5_DWord[1017] = { ++const DWordCIDMap kGBK2K_H_5_DWord[1017] = { + {0x8130, 0x8436, 0x8436, 0x5752}, {0x8138, 0xFD38, 0xFD39, 0x579C}, + {0x8138, 0xFE30, 0xFE39, 0x579E}, {0x8139, 0x8130, 0x8137, 0x57A8}, + {0x8139, 0x8139, 0x8139, 0x57B0}, {0x8139, 0x8230, 0x8239, 0x57B1}, +@@ -1877,3 +1879,5 @@ const FXCMAP_DWordCIDMap g_FXCMAP_GBK2K_H_5_DWord[1017] = { + {0x8236, 0x9230, 0x9239, 0x7632}, {0x8236, 0x9330, 0x9339, 0x763C}, + {0x8236, 0x9430, 0x9435, 0x7646}, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/GB1/GBK2K-V_5.cpp b/core/fpdfapi/cmaps/GB1/GBK2K-V_5.cpp +index 8efedd063..904253f8b 100644 +--- a/core/fpdfapi/cmaps/GB1/GBK2K-V_5.cpp ++++ b/core/fpdfapi/cmaps/GB1/GBK2K-V_5.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h" + +-const uint16_t g_FXCMAP_GBK2K_V_5[41 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kGBK2K_V_5[41 * 3] = { + 0xA1A2, 0xA1A2, 0x023F, 0xA1A3, 0xA1A3, 0x023E, 0xA1AA, 0xA1AA, 0x0256, + 0xA1AB, 0xA1AC, 0x1E18, 0xA1AD, 0xA1AD, 0x0257, 0xA1B2, 0xA1BF, 0x0246, + 0xA1FE, 0xA1FE, 0x1E1A, 0xA3A1, 0xA3A1, 0x0242, 0xA3A8, 0xA3A9, 0x0244, +@@ -22,3 +24,5 @@ const uint16_t g_FXCMAP_GBK2K_V_5[41 * 3] = { + 0xA5E3, 0xA5E3, 0x5773, 0xA5E5, 0xA5E5, 0x5775, 0xA5E7, 0xA5E7, 0x5774, + 0xA5EE, 0xA5EE, 0x5772, 0xA960, 0xA960, 0x577A, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/GB1/GBKp-EUC-H_2.cpp b/core/fpdfapi/cmaps/GB1/GBKp-EUC-H_2.cpp +index 3145980b3..2de5aeadd 100644 +--- a/core/fpdfapi/cmaps/GB1/GBKp-EUC-H_2.cpp ++++ b/core/fpdfapi/cmaps/GB1/GBKp-EUC-H_2.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h" + +-const uint16_t g_FXCMAP_GBKp_EUC_H_2[4070 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kGBKp_EUC_H_2[4070 * 3] = { + 0x0020, 0x007E, 0x0001, 0x8140, 0x8178, 0x2758, 0x8179, 0x8179, 0x2059, + 0x817A, 0x817E, 0x2791, 0x8180, 0x8185, 0x2796, 0x8186, 0x8186, 0x21F1, + 0x8187, 0x81EC, 0x279C, 0x81ED, 0x81ED, 0x1FF2, 0x81EE, 0x81F5, 0x2802, +@@ -1365,3 +1367,5 @@ const uint16_t g_FXCMAP_GBKp_EUC_H_2[4070 * 3] = { + 0xFD9F, 0xFD9F, 0x200C, 0xFDA0, 0xFDA0, 0x5083, 0xFE40, 0xFE40, 0x1259, + 0xFE41, 0xFE7E, 0x5610, 0xFE80, 0xFEA0, 0x564E, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/GB1/GBKp-EUC-V_2.cpp b/core/fpdfapi/cmaps/GB1/GBKp-EUC-V_2.cpp +index 014ae70a1..aa55cf802 100644 +--- a/core/fpdfapi/cmaps/GB1/GBKp-EUC-V_2.cpp ++++ b/core/fpdfapi/cmaps/GB1/GBKp-EUC-V_2.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h" + +-const uint16_t g_FXCMAP_GBKp_EUC_V_2[20 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kGBKp_EUC_V_2[20 * 3] = { + 0xA1A2, 0xA1A2, 0x023F, 0xA1A3, 0xA1A3, 0x023E, 0xA1AA, 0xA1AA, 0x0256, + 0xA1AB, 0xA1AC, 0x1E18, 0xA1AD, 0xA1AD, 0x0257, 0xA1B2, 0xA1BF, 0x0246, + 0xA1FE, 0xA1FE, 0x1E1A, 0xA3A1, 0xA3A1, 0x0242, 0xA3A8, 0xA3A9, 0x0244, +@@ -15,3 +17,5 @@ const uint16_t g_FXCMAP_GBKp_EUC_V_2[20 * 3] = { + 0xA3DD, 0xA3DD, 0x1E1E, 0xA3DF, 0xA3DF, 0x0258, 0xA3FB, 0xA3FB, 0x0254, + 0xA3FD, 0xA3FD, 0x0255, 0xA3FE, 0xA3FE, 0x1E1F, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/GB1/GBpc-EUC-H_0.cpp b/core/fpdfapi/cmaps/GB1/GBpc-EUC-H_0.cpp +index ff437d725..f1f6dd31f 100644 +--- a/core/fpdfapi/cmaps/GB1/GBpc-EUC-H_0.cpp ++++ b/core/fpdfapi/cmaps/GB1/GBpc-EUC-H_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h" + +-const uint16_t g_FXCMAP_GBpc_EUC_H_0[91 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kGBpc_EUC_H_0[91 * 3] = { + 0x0020, 0x007E, 0x0001, 0x0080, 0x0080, 0x1E20, 0x00FD, 0x00FF, 0x1E21, + 0xA1A1, 0xA1FE, 0x0060, 0xA2B1, 0xA2E2, 0x00BE, 0xA2E5, 0xA2EE, 0x00F0, + 0xA2F1, 0xA2FC, 0x00FA, 0xA3A1, 0xA3FE, 0x0106, 0xA4A1, 0xA4F3, 0x0164, +@@ -39,3 +41,5 @@ const uint16_t g_FXCMAP_GBpc_EUC_H_0[91 * 3] = { + 0xF4A1, 0xF4FE, 0x1C9F, 0xF5A1, 0xF5FE, 0x1CFD, 0xF6A1, 0xF6FE, 0x1D5B, + 0xF7A1, 0xF7FE, 0x1DB9, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/GB1/GBpc-EUC-V_0.cpp b/core/fpdfapi/cmaps/GB1/GBpc-EUC-V_0.cpp +index a35858f21..3b53117f6 100644 +--- a/core/fpdfapi/cmaps/GB1/GBpc-EUC-V_0.cpp ++++ b/core/fpdfapi/cmaps/GB1/GBpc-EUC-V_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h" + +-const uint16_t g_FXCMAP_GBpc_EUC_V_0[20 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kGBpc_EUC_V_0[20 * 3] = { + 0xA1A2, 0xA1A2, 0x023F, 0xA1A3, 0xA1A3, 0x023E, 0xA1AA, 0xA1AA, 0x0256, + 0xA1AB, 0xA1AC, 0x1E18, 0xA1AD, 0xA1AD, 0x0257, 0xA1B2, 0xA1BF, 0x0246, + 0xA1FE, 0xA1FE, 0x1E1A, 0xA3A1, 0xA3A1, 0x0242, 0xA3A8, 0xA3A9, 0x0244, +@@ -15,3 +17,5 @@ const uint16_t g_FXCMAP_GBpc_EUC_V_0[20 * 3] = { + 0xA3DD, 0xA3DD, 0x1E1E, 0xA3DF, 0xA3DF, 0x0258, 0xA3FB, 0xA3FB, 0x0254, + 0xA3FD, 0xA3FD, 0x0255, 0xA3FE, 0xA3FE, 0x1E1F, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/GB1/UniGB-UCS2-H_4.cpp b/core/fpdfapi/cmaps/GB1/UniGB-UCS2-H_4.cpp +index e135f40da..a787a3455 100644 +--- a/core/fpdfapi/cmaps/GB1/UniGB-UCS2-H_4.cpp ++++ b/core/fpdfapi/cmaps/GB1/UniGB-UCS2-H_4.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h" + +-const uint16_t g_FXCMAP_UniGB_UCS2_H_4[13825 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kUniGB_UCS2_H_4[13825 * 3] = { + 0x0020, 0x007E, 0x0001, 0x00A4, 0x00A4, 0x00A7, 0x00A5, 0x00A5, 0x5752, + 0x00A7, 0x00A7, 0x00AB, 0x00A8, 0x00A8, 0x0066, 0x00B0, 0x00B0, 0x00A2, + 0x00B1, 0x00B1, 0x007F, 0x00D7, 0x00D7, 0x0080, 0x00E0, 0x00E0, 0x029F, +@@ -4617,3 +4619,5 @@ const uint16_t g_FXCMAP_UniGB_UCS2_H_4[13825 * 3] = { + 0xFFE2, 0xFFE2, 0x271E, 0xFFE3, 0xFFE3, 0x0163, 0xFFE4, 0xFFE4, 0x271F, + 0xFFE5, 0xFFE5, 0x0109, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/GB1/UniGB-UCS2-V_4.cpp b/core/fpdfapi/cmaps/GB1/UniGB-UCS2-V_4.cpp +index 4ec5405a3..34ab6e752 100644 +--- a/core/fpdfapi/cmaps/GB1/UniGB-UCS2-V_4.cpp ++++ b/core/fpdfapi/cmaps/GB1/UniGB-UCS2-V_4.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h" + +-const uint16_t g_FXCMAP_UniGB_UCS2_V_4[24 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kUniGB_UCS2_V_4[24 * 3] = { + 0x2014, 0x2014, 0x0256, 0x2026, 0x2026, 0x0257, 0x2225, 0x2225, 0x1E1C, + 0x3001, 0x3001, 0x023F, 0x3002, 0x3002, 0x023E, 0x3008, 0x300F, 0x0248, + 0x3010, 0x3011, 0x0252, 0x3013, 0x3013, 0x1E1A, 0x3014, 0x3015, 0x0246, +@@ -16,3 +18,5 @@ const uint16_t g_FXCMAP_UniGB_UCS2_V_4[24 * 3] = { + 0xFF3D, 0xFF3D, 0x1E1E, 0xFF3F, 0xFF3F, 0x0258, 0xFF5B, 0xFF5B, 0x0254, + 0xFF5D, 0xFF5D, 0x0255, 0xFF5E, 0xFF5E, 0x1E18, 0xFFE3, 0xFFE3, 0x1E1F, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/GB1/cmaps_gb1.cpp b/core/fpdfapi/cmaps/GB1/cmaps_gb1.cpp +index a5980917e..facf1237b 100644 +--- a/core/fpdfapi/cmaps/GB1/cmaps_gb1.cpp ++++ b/core/fpdfapi/cmaps/GB1/cmaps_gb1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,33 +6,29 @@ + + #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h" + +-#include "core/fxcrt/fx_memory.h" ++#include + +-const FXCMAP_CMap g_FXCMAP_GB1_cmaps[] = { +- {"GB-EUC-H", g_FXCMAP_GB_EUC_H_0, nullptr, 90, 0, FXCMAP_CMap::Range, 0}, +- {"GB-EUC-V", g_FXCMAP_GB_EUC_V_0, nullptr, 20, 0, FXCMAP_CMap::Range, -1}, +- {"GBpc-EUC-H", g_FXCMAP_GBpc_EUC_H_0, nullptr, 91, 0, FXCMAP_CMap::Range, +- 0}, +- {"GBpc-EUC-V", g_FXCMAP_GBpc_EUC_V_0, nullptr, 20, 0, FXCMAP_CMap::Range, +- -1}, +- {"GBK-EUC-H", g_FXCMAP_GBK_EUC_H_2, nullptr, 4071, 0, FXCMAP_CMap::Range, ++namespace fxcmap { ++ ++const CMap kGB1_cmaps[] = { ++ {"GB-EUC-H", kGB_EUC_H_0, nullptr, 90, 0, CMap::Type::kRange, 0}, ++ {"GB-EUC-V", kGB_EUC_V_0, nullptr, 20, 0, CMap::Type::kRange, -1}, ++ {"GBpc-EUC-H", kGBpc_EUC_H_0, nullptr, 91, 0, CMap::Type::kRange, 0}, ++ {"GBpc-EUC-V", kGBpc_EUC_V_0, nullptr, 20, 0, CMap::Type::kRange, -1}, ++ {"GBK-EUC-H", kGBK_EUC_H_2, nullptr, 4071, 0, CMap::Type::kRange, 0}, ++ {"GBK-EUC-V", kGBK_EUC_V_2, nullptr, 20, 0, CMap::Type::kRange, -1}, ++ {"GBKp-EUC-H", kGBKp_EUC_H_2, nullptr, 4070, 0, CMap::Type::kRange, -2}, ++ {"GBKp-EUC-V", kGBKp_EUC_V_2, nullptr, 20, 0, CMap::Type::kRange, -1}, ++ {"GBK2K-H", kGBK2K_H_5, kGBK2K_H_5_DWord, 4071, 1017, CMap::Type::kRange, ++ -4}, ++ {"GBK2K-V", kGBK2K_V_5, nullptr, 41, 0, CMap::Type::kRange, -1}, ++ {"UniGB-UCS2-H", kUniGB_UCS2_H_4, nullptr, 13825, 0, CMap::Type::kRange, 0}, ++ {"UniGB-UCS2-V", kUniGB_UCS2_V_4, nullptr, 24, 0, CMap::Type::kRange, -1}, ++ {"UniGB-UTF16-H", kUniGB_UCS2_H_4, nullptr, 13825, 0, CMap::Type::kRange, + 0}, +- {"GBK-EUC-V", g_FXCMAP_GBK_EUC_V_2, nullptr, 20, 0, FXCMAP_CMap::Range, -1}, +- {"GBKp-EUC-H", g_FXCMAP_GBKp_EUC_H_2, nullptr, 4070, 0, FXCMAP_CMap::Range, +- -2}, +- {"GBKp-EUC-V", g_FXCMAP_GBKp_EUC_V_2, nullptr, 20, 0, FXCMAP_CMap::Range, +- -1}, +- {"GBK2K-H", g_FXCMAP_GBK2K_H_5, g_FXCMAP_GBK2K_H_5_DWord, 4071, 1017, +- FXCMAP_CMap::Range, -4}, +- {"GBK2K-V", g_FXCMAP_GBK2K_V_5, nullptr, 41, 0, FXCMAP_CMap::Range, -1}, +- {"UniGB-UCS2-H", g_FXCMAP_UniGB_UCS2_H_4, nullptr, 13825, 0, +- FXCMAP_CMap::Range, 0}, +- {"UniGB-UCS2-V", g_FXCMAP_UniGB_UCS2_V_4, nullptr, 24, 0, +- FXCMAP_CMap::Range, -1}, +- {"UniGB-UTF16-H", g_FXCMAP_UniGB_UCS2_H_4, nullptr, 13825, 0, +- FXCMAP_CMap::Range, 0}, +- {"UniGB-UTF16-V", g_FXCMAP_UniGB_UCS2_V_4, nullptr, 24, 0, +- FXCMAP_CMap::Range, -1}, ++ {"UniGB-UTF16-V", kUniGB_UCS2_V_4, nullptr, 24, 0, CMap::Type::kRange, -1}, + }; + +-const size_t g_FXCMAP_GB1_cmaps_size = FX_ArraySize(g_FXCMAP_GB1_cmaps); ++const size_t kGB1_cmaps_size = std::size(kGB1_cmaps); ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/GB1/cmaps_gb1.h b/core/fpdfapi/cmaps/GB1/cmaps_gb1.h +index 7873f6454..a22626665 100644 +--- a/core/fpdfapi/cmaps/GB1/cmaps_gb1.h ++++ b/core/fpdfapi/cmaps/GB1/cmaps_gb1.h +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,23 +7,30 @@ + #ifndef CORE_FPDFAPI_CMAPS_GB1_CMAPS_GB1_H_ + #define CORE_FPDFAPI_CMAPS_GB1_CMAPS_GB1_H_ + ++#include ++#include ++ + #include "core/fpdfapi/cmaps/fpdf_cmaps.h" + +-extern const uint16_t g_FXCMAP_GB_EUC_H_0[]; +-extern const uint16_t g_FXCMAP_GB_EUC_V_0[]; +-extern const uint16_t g_FXCMAP_GBpc_EUC_H_0[]; +-extern const uint16_t g_FXCMAP_GBpc_EUC_V_0[]; +-extern const uint16_t g_FXCMAP_GBK_EUC_H_2[]; +-extern const uint16_t g_FXCMAP_GBK_EUC_V_2[]; +-extern const uint16_t g_FXCMAP_GBKp_EUC_H_2[]; +-extern const uint16_t g_FXCMAP_GBKp_EUC_V_2[]; +-extern const uint16_t g_FXCMAP_GBK2K_H_5[]; +-extern const FXCMAP_DWordCIDMap g_FXCMAP_GBK2K_H_5_DWord[]; +-extern const uint16_t g_FXCMAP_GBK2K_V_5[]; +-extern const uint16_t g_FXCMAP_UniGB_UCS2_H_4[]; +-extern const uint16_t g_FXCMAP_UniGB_UCS2_V_4[]; +-extern const uint16_t g_FXCMAP_GB1CID2Unicode_5[30284]; +-extern const FXCMAP_CMap g_FXCMAP_GB1_cmaps[]; +-extern const size_t g_FXCMAP_GB1_cmaps_size; ++namespace fxcmap { ++ ++extern const uint16_t kGB_EUC_H_0[]; ++extern const uint16_t kGB_EUC_V_0[]; ++extern const uint16_t kGBpc_EUC_H_0[]; ++extern const uint16_t kGBpc_EUC_V_0[]; ++extern const uint16_t kGBK_EUC_H_2[]; ++extern const uint16_t kGBK_EUC_V_2[]; ++extern const uint16_t kGBKp_EUC_H_2[]; ++extern const uint16_t kGBKp_EUC_V_2[]; ++extern const uint16_t kGBK2K_H_5[]; ++extern const DWordCIDMap kGBK2K_H_5_DWord[]; ++extern const uint16_t kGBK2K_V_5[]; ++extern const uint16_t kUniGB_UCS2_H_4[]; ++extern const uint16_t kUniGB_UCS2_V_4[]; ++extern const uint16_t kGB1CID2Unicode_5[30284]; ++extern const CMap kGB1_cmaps[]; ++extern const size_t kGB1_cmaps_size; ++ ++} // namespace fxcmap + + #endif // CORE_FPDFAPI_CMAPS_GB1_CMAPS_GB1_H_ +diff --git a/core/fpdfapi/cmaps/Japan1/83pv-RKSJ-H_1.cpp b/core/fpdfapi/cmaps/Japan1/83pv-RKSJ-H_1.cpp +index b3447af96..51b49e2fd 100644 +--- a/core/fpdfapi/cmaps/Japan1/83pv-RKSJ-H_1.cpp ++++ b/core/fpdfapi/cmaps/Japan1/83pv-RKSJ-H_1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_83pv_RKSJ_H_1[222 * 3] = { ++namespace fxcmap { ++ ++const uint16_t k83pv_RKSJ_H_1[222 * 3] = { + 0x0020, 0x007E, 0x0001, 0x0080, 0x0080, 0x0061, 0x00A0, 0x00DF, 0x0146, + 0x00FD, 0x00FD, 0x0098, 0x00FE, 0x00FE, 0x00E4, 0x00FF, 0x00FF, 0x007C, + 0x8140, 0x817E, 0x0279, 0x8180, 0x81AC, 0x02B8, 0x81B8, 0x81BF, 0x02E5, +@@ -82,3 +84,5 @@ const uint16_t g_FXCMAP_83pv_RKSJ_H_1[222 * 3] = { + 0xEE90, 0xEE90, 0x02FA, 0xEE91, 0xEE91, 0x02F9, 0xEE92, 0xEE92, 0x0301, + 0xEE93, 0xEE99, 0x1DC8, 0xEE9A, 0xEE9A, 0x0300, 0xEE9B, 0xEE9C, 0x1DCF, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/90ms-RKSJ-H_2.cpp b/core/fpdfapi/cmaps/Japan1/90ms-RKSJ-H_2.cpp +index d5d29d6ed..212f809f2 100644 +--- a/core/fpdfapi/cmaps/Japan1/90ms-RKSJ-H_2.cpp ++++ b/core/fpdfapi/cmaps/Japan1/90ms-RKSJ-H_2.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_90ms_RKSJ_H_2[171 * 3] = { ++namespace fxcmap { ++ ++const uint16_t k90ms_RKSJ_H_2[171 * 3] = { + 0x0020, 0x007D, 0x00E7, 0x007E, 0x007E, 0x0277, 0x00A0, 0x00DF, 0x0146, + 0x8140, 0x817E, 0x0279, 0x8180, 0x81AC, 0x02B8, 0x81B8, 0x81BF, 0x02E5, + 0x81C8, 0x81CE, 0x02ED, 0x81DA, 0x81E8, 0x02F4, 0x81F0, 0x81F7, 0x0303, +@@ -65,3 +67,5 @@ const uint16_t g_FXCMAP_90ms_RKSJ_H_2[171 * 3] = { + 0xFA80, 0xFACF, 0x20CA, 0xFAD0, 0xFAD0, 0x07C9, 0xFAD1, 0xFAFC, 0x211A, + 0xFB40, 0xFB7E, 0x2146, 0xFB80, 0xFBFC, 0x2185, 0xFC40, 0xFC4B, 0x2202, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/90ms-RKSJ-V_2.cpp b/core/fpdfapi/cmaps/Japan1/90ms-RKSJ-V_2.cpp +index 1fb0c3302..5303c72c2 100644 +--- a/core/fpdfapi/cmaps/Japan1/90ms-RKSJ-V_2.cpp ++++ b/core/fpdfapi/cmaps/Japan1/90ms-RKSJ-V_2.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_90ms_RKSJ_V_2[78 * 3] = { ++namespace fxcmap { ++ ++const uint16_t k90ms_RKSJ_V_2[78 * 3] = { + 0x8141, 0x8142, 0x1ECF, 0x8143, 0x8143, 0x204C, 0x8144, 0x8144, 0x2052, + 0x8150, 0x8151, 0x1ED1, 0x815B, 0x815D, 0x1ED3, 0x8160, 0x8164, 0x1ED6, + 0x8169, 0x817A, 0x1EDB, 0x8181, 0x8181, 0x1EED, 0x81A8, 0x81A8, 0x02E3, +@@ -34,3 +36,5 @@ const uint16_t g_FXCMAP_90ms_RKSJ_V_2[78 * 3] = { + 0x8768, 0x8768, 0x2098, 0x8769, 0x876A, 0x1F0E, 0x876B, 0x876B, 0x209C, + 0x876C, 0x876D, 0x1F11, 0x876E, 0x876E, 0x209D, 0x8780, 0x8781, 0x1F14, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/90msp-RKSJ-H_2.cpp b/core/fpdfapi/cmaps/Japan1/90msp-RKSJ-H_2.cpp +index 65081c825..a77d0e14f 100644 +--- a/core/fpdfapi/cmaps/Japan1/90msp-RKSJ-H_2.cpp ++++ b/core/fpdfapi/cmaps/Japan1/90msp-RKSJ-H_2.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_90msp_RKSJ_H_2[170 * 3] = { ++namespace fxcmap { ++ ++const uint16_t k90msp_RKSJ_H_2[170 * 3] = { + 0x0020, 0x007E, 0x0001, 0x00A0, 0x00DF, 0x0146, 0x8140, 0x817E, 0x0279, + 0x8180, 0x81AC, 0x02B8, 0x81B8, 0x81BF, 0x02E5, 0x81C8, 0x81CE, 0x02ED, + 0x81DA, 0x81E8, 0x02F4, 0x81F0, 0x81F7, 0x0303, 0x81FC, 0x81FC, 0x030B, +@@ -65,3 +67,5 @@ const uint16_t g_FXCMAP_90msp_RKSJ_H_2[170 * 3] = { + 0xFAD0, 0xFAD0, 0x07C9, 0xFAD1, 0xFAFC, 0x211A, 0xFB40, 0xFB7E, 0x2146, + 0xFB80, 0xFBFC, 0x2185, 0xFC40, 0xFC4B, 0x2202, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/90msp-RKSJ-V_2.cpp b/core/fpdfapi/cmaps/Japan1/90msp-RKSJ-V_2.cpp +index 5a5e457d6..ee2f7997c 100644 +--- a/core/fpdfapi/cmaps/Japan1/90msp-RKSJ-V_2.cpp ++++ b/core/fpdfapi/cmaps/Japan1/90msp-RKSJ-V_2.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_90msp_RKSJ_V_2[78 * 3] = { ++namespace fxcmap { ++ ++const uint16_t k90msp_RKSJ_V_2[78 * 3] = { + 0x8141, 0x8142, 0x1ECF, 0x8143, 0x8143, 0x204C, 0x8144, 0x8144, 0x2052, + 0x8150, 0x8151, 0x1ED1, 0x815B, 0x815D, 0x1ED3, 0x8160, 0x8164, 0x1ED6, + 0x8169, 0x817A, 0x1EDB, 0x8181, 0x8181, 0x1EED, 0x81A8, 0x81A8, 0x02E3, +@@ -34,3 +36,5 @@ const uint16_t g_FXCMAP_90msp_RKSJ_V_2[78 * 3] = { + 0x8768, 0x8768, 0x2098, 0x8769, 0x876A, 0x1F0E, 0x876B, 0x876B, 0x209C, + 0x876C, 0x876D, 0x1F11, 0x876E, 0x876E, 0x209D, 0x8780, 0x8781, 0x1F14, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/90pv-RKSJ-H_1.cpp b/core/fpdfapi/cmaps/Japan1/90pv-RKSJ-H_1.cpp +index e41b83918..0bf3f2f30 100644 +--- a/core/fpdfapi/cmaps/Japan1/90pv-RKSJ-H_1.cpp ++++ b/core/fpdfapi/cmaps/Japan1/90pv-RKSJ-H_1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_90pv_RKSJ_H_1[263 * 3] = { ++namespace fxcmap { ++ ++const uint16_t k90pv_RKSJ_H_1[263 * 3] = { + 0x0020, 0x007E, 0x0001, 0x0080, 0x0080, 0x0061, 0x00A0, 0x00DF, 0x0146, + 0x00FD, 0x00FD, 0x0098, 0x00FE, 0x00FE, 0x00E4, 0x00FF, 0x00FF, 0x007C, + 0x8140, 0x817E, 0x0279, 0x8180, 0x81AC, 0x02B8, 0x81B8, 0x81BF, 0x02E5, +@@ -96,3 +98,5 @@ const uint16_t g_FXCMAP_90pv_RKSJ_H_1[263 * 3] = { + 0xED83, 0xED83, 0x1EFE, 0xED85, 0xED85, 0x1EFF, 0xED87, 0xED87, 0x1F00, + 0xED8E, 0xED8E, 0x1F01, 0xED95, 0xED96, 0x1F02, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/Add-RKSJ-H_1.cpp b/core/fpdfapi/cmaps/Japan1/Add-RKSJ-H_1.cpp +index 53dc7548a..69af12751 100644 +--- a/core/fpdfapi/cmaps/Japan1/Add-RKSJ-H_1.cpp ++++ b/core/fpdfapi/cmaps/Japan1/Add-RKSJ-H_1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_Add_RKSJ_H_1[635 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kAdd_RKSJ_H_1[635 * 3] = { + 0x0020, 0x007E, 0x00E7, 0x00A0, 0x00DF, 0x0146, 0x8140, 0x817E, 0x0279, + 0x8180, 0x81AC, 0x02B8, 0x81B8, 0x81BF, 0x02E5, 0x81C8, 0x81CE, 0x02ED, + 0x81DA, 0x81E8, 0x02F4, 0x81F0, 0x81F7, 0x0303, 0x81FC, 0x81FC, 0x030B, +@@ -220,3 +222,5 @@ const uint16_t g_FXCMAP_Add_RKSJ_H_1[635 * 3] = { + 0xEF52, 0xEF63, 0x1EDB, 0xEF64, 0xEF79, 0x1EEE, 0xEF7A, 0xEF7B, 0x2048, + 0xEF8D, 0xEF90, 0x02E0, 0xEF91, 0xEF94, 0x1FF6, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/Add-RKSJ-V_1.cpp b/core/fpdfapi/cmaps/Japan1/Add-RKSJ-V_1.cpp +index 47be9311e..ce351ffb2 100644 +--- a/core/fpdfapi/cmaps/Japan1/Add-RKSJ-V_1.cpp ++++ b/core/fpdfapi/cmaps/Japan1/Add-RKSJ-V_1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_Add_RKSJ_V_1[57 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kAdd_RKSJ_V_1[57 * 3] = { + 0x8141, 0x8142, 0x1ECF, 0x8143, 0x8143, 0x204C, 0x8144, 0x8144, 0x2052, + 0x8150, 0x8151, 0x1ED1, 0x815B, 0x815D, 0x1ED3, 0x8160, 0x8164, 0x1ED6, + 0x8165, 0x8165, 0x205A, 0x8166, 0x8166, 0x2053, 0x8167, 0x8167, 0x2058, +@@ -27,3 +29,5 @@ const uint16_t g_FXCMAP_Add_RKSJ_V_1[57 * 3] = { + 0xEC8D, 0xEC8D, 0x20A6, 0xEC8E, 0xEC8E, 0x20A5, 0xEC8F, 0xEC8F, 0x20A1, + 0xEC90, 0xEC90, 0x20A4, 0xEC95, 0xEC95, 0x2084, 0xEF92, 0xEF92, 0x208D, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/Adobe-Japan1-UCS2_4.cpp b/core/fpdfapi/cmaps/Japan1/Adobe-Japan1-UCS2_4.cpp +index fbef3f140..758bfebd0 100644 +--- a/core/fpdfapi/cmaps/Japan1/Adobe-Japan1-UCS2_4.cpp ++++ b/core/fpdfapi/cmaps/Japan1/Adobe-Japan1-UCS2_4.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_Japan1CID2Unicode_4[15444] = { ++namespace fxcmap { ++ ++const uint16_t kJapan1CID2Unicode_4[15444] = { + 0xFFFD, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, + 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, +@@ -1724,3 +1726,5 @@ const uint16_t g_FXCMAP_Japan1CID2Unicode_4[15444] = { + 0x440C, 0x3E8A, 0xFFFD, 0xFFFD, 0x4BE8, 0xFFFD, 0x3EDA, 0x3B22, 0xFFFD, + 0x457A, 0x4093, 0xFFFD, 0x4665, 0x4103, 0x4293, 0x46AE, 0x3488, 0xFFFD, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/EUC-H_1.cpp b/core/fpdfapi/cmaps/Japan1/EUC-H_1.cpp +index ddb1bcf4c..c4675be05 100644 +--- a/core/fpdfapi/cmaps/Japan1/EUC-H_1.cpp ++++ b/core/fpdfapi/cmaps/Japan1/EUC-H_1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_EUC_H_1[120 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kEUC_H_1[120 * 3] = { + 0x0020, 0x007E, 0x00E7, 0x8EA0, 0x8EDF, 0x0146, 0xA1A1, 0xA1FE, 0x0279, + 0xA2A1, 0xA2AE, 0x02D7, 0xA2BA, 0xA2C1, 0x02E5, 0xA2CA, 0xA2D0, 0x02ED, + 0xA2DC, 0xA2EA, 0x02F4, 0xA2F2, 0xA2F9, 0x0303, 0xA2FE, 0xA2FE, 0x030B, +@@ -48,3 +50,5 @@ const uint16_t g_FXCMAP_EUC_H_1[120 * 3] = { + 0xF0A1, 0xF0FE, 0x1BBA, 0xF1A1, 0xF1FE, 0x1C18, 0xF2A1, 0xF2FE, 0x1C76, + 0xF3A1, 0xF3FE, 0x1CD4, 0xF4A1, 0xF4A4, 0x1D32, 0xF4A5, 0xF4A6, 0x205C, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/EUC-V_1.cpp b/core/fpdfapi/cmaps/Japan1/EUC-V_1.cpp +index d5a70c6ac..c50765677 100644 +--- a/core/fpdfapi/cmaps/Japan1/EUC-V_1.cpp ++++ b/core/fpdfapi/cmaps/Japan1/EUC-V_1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_EUC_V_1[27 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kEUC_V_1[27 * 3] = { + 0xA1A2, 0xA1A3, 0x1ECF, 0xA1B1, 0xA1B2, 0x1ED1, 0xA1BC, 0xA1BE, 0x1ED3, + 0xA1C1, 0xA1C5, 0x1ED6, 0xA1CA, 0xA1DB, 0x1EDB, 0xA1E1, 0xA1E1, 0x1EED, + 0xA4A1, 0xA4A1, 0x1EEE, 0xA4A3, 0xA4A3, 0x1EEF, 0xA4A5, 0xA4A5, 0x1EF0, +@@ -17,3 +19,5 @@ const uint16_t g_FXCMAP_EUC_V_1[27 * 3] = { + 0xA5C3, 0xA5C3, 0x1EFD, 0xA5E3, 0xA5E3, 0x1EFE, 0xA5E5, 0xA5E5, 0x1EFF, + 0xA5E7, 0xA5E7, 0x1F00, 0xA5EE, 0xA5EE, 0x1F01, 0xA5F5, 0xA5F6, 0x1F02, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/Ext-RKSJ-H_2.cpp b/core/fpdfapi/cmaps/Japan1/Ext-RKSJ-H_2.cpp +index b251c5dc8..80c0b4837 100644 +--- a/core/fpdfapi/cmaps/Japan1/Ext-RKSJ-H_2.cpp ++++ b/core/fpdfapi/cmaps/Japan1/Ext-RKSJ-H_2.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_Ext_RKSJ_H_2[665 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kExt_RKSJ_H_2[665 * 3] = { + 0x0020, 0x007E, 0x00E7, 0x00A0, 0x00DF, 0x0146, 0x8140, 0x817E, 0x0279, + 0x8180, 0x8188, 0x02B8, 0x8189, 0x8189, 0x1D36, 0x818A, 0x81AC, 0x02C2, + 0x824F, 0x8258, 0x030C, 0x8260, 0x8279, 0x0316, 0x8281, 0x829A, 0x0330, +@@ -230,3 +232,5 @@ const uint16_t g_FXCMAP_Ext_RKSJ_H_2[665 * 3] = { + 0xEE40, 0xEE7E, 0x2162, 0xEE80, 0xEEEC, 0x21A1, 0xEEEF, 0xEEF8, 0x1F9C, + 0xEEF9, 0xEEF9, 0x02EF, 0xEEFA, 0xEEFC, 0x1F45, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/Ext-RKSJ-V_2.cpp b/core/fpdfapi/cmaps/Japan1/Ext-RKSJ-V_2.cpp +index b62e5a5de..f3833bc6d 100644 +--- a/core/fpdfapi/cmaps/Japan1/Ext-RKSJ-V_2.cpp ++++ b/core/fpdfapi/cmaps/Japan1/Ext-RKSJ-V_2.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_Ext_RKSJ_V_2[39 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kExt_RKSJ_V_2[39 * 3] = { + 0x8141, 0x8142, 0x1ECF, 0x8143, 0x8143, 0x204C, 0x8144, 0x8144, 0x2052, + 0x814A, 0x814A, 0x2050, 0x814B, 0x814B, 0x204F, 0x815B, 0x815D, 0x1ED3, + 0x8160, 0x8164, 0x1ED6, 0x8165, 0x8165, 0x2059, 0x8166, 0x8166, 0x2054, +@@ -21,3 +23,5 @@ const uint16_t g_FXCMAP_Ext_RKSJ_V_2[39 * 3] = { + 0x8385, 0x8385, 0x1EFF, 0x8387, 0x8387, 0x1F00, 0x838E, 0x838E, 0x1F01, + 0x8395, 0x8396, 0x1F02, 0x875F, 0x876E, 0x1F04, 0x8780, 0x8781, 0x1F14, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/H_1.cpp b/core/fpdfapi/cmaps/Japan1/H_1.cpp +index be32b5cb7..be3694d15 100644 +--- a/core/fpdfapi/cmaps/Japan1/H_1.cpp ++++ b/core/fpdfapi/cmaps/Japan1/H_1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_H_1[118 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kH_1[118 * 3] = { + 0x2121, 0x217E, 0x0279, 0x2221, 0x222E, 0x02D7, 0x223A, 0x2241, 0x02E5, + 0x224A, 0x2250, 0x02ED, 0x225C, 0x226A, 0x02F4, 0x2272, 0x2279, 0x0303, + 0x227E, 0x227E, 0x030B, 0x2330, 0x2339, 0x030C, 0x2341, 0x235A, 0x0316, +@@ -48,3 +50,5 @@ const uint16_t g_FXCMAP_H_1[118 * 3] = { + 0x7221, 0x727E, 0x1C76, 0x7321, 0x737E, 0x1CD4, 0x7421, 0x7424, 0x1D32, + 0x7425, 0x7426, 0x205C, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-H_4.cpp b/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-H_4.cpp +index e03c13963..aa8565d10 100644 +--- a/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-H_4.cpp ++++ b/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-H_4.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,11 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_UniJIS_UCS2_HW_H_4[4 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kUniJIS_UCS2_HW_H_4[4 * 3] = { + 0x0020, 0x005B, 0x00E7, 0x005C, 0x005C, 0x220F, + 0x005D, 0x007E, 0x0124, 0x00A5, 0x00A5, 0x0123, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-V_4.cpp b/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-V_4.cpp +index 3f3a2a4b4..aed1f6306 100644 +--- a/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-V_4.cpp ++++ b/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-HW-V_4.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_UniJIS_UCS2_HW_V_4[199 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kUniJIS_UCS2_HW_V_4[199 * 3] = { + 0x0020, 0x005B, 0x00E7, 0x005C, 0x005C, 0x220F, 0x005D, 0x007E, 0x0124, + 0x00A5, 0x00A5, 0x0123, 0x00B0, 0x00B0, 0x204D, 0x2010, 0x2010, 0x1ED5, + 0x2015, 0x2015, 0x1ED4, 0x2016, 0x2016, 0x1ED7, 0x2018, 0x2019, 0x2059, +@@ -75,3 +77,5 @@ const uint16_t g_FXCMAP_UniJIS_UCS2_HW_V_4[199 * 3] = { + 0xFF5C, 0xFF5C, 0x1ED8, 0xFF5D, 0xFF5D, 0x1EE2, 0xFF5E, 0xFF5E, 0x1ED6, + 0xFFE3, 0xFFE3, 0x1ED1, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-H_4.cpp b/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-H_4.cpp +index a8c846111..54967063d 100644 +--- a/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-H_4.cpp ++++ b/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-H_4.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_UniJIS_UCS2_H_4[9772 * 2] = { ++namespace fxcmap { ++ ++const uint16_t kUniJIS_UCS2_H_4[9772 * 2] = { + 0x0020, 0x0001, 0x0021, 0x0002, 0x0022, 0x0003, 0x0023, 0x0004, 0x0024, + 0x0005, 0x0025, 0x0006, 0x0026, 0x0007, 0x0027, 0x0008, 0x0028, 0x0009, + 0x0029, 0x000A, 0x002A, 0x000B, 0x002B, 0x000C, 0x002C, 0x000D, 0x002D, +@@ -2180,3 +2182,5 @@ const uint16_t g_FXCMAP_UniJIS_UCS2_H_4[9772 * 2] = { + 0xFFE0, 0x02C9, 0xFFE1, 0x02CA, 0xFFE2, 0x02EF, 0xFFE3, 0x0289, 0xFFE4, + 0x1F45, 0xFFE5, 0x02C7, 0xFFE8, 0x0143, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-V_4.cpp b/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-V_4.cpp +index 0e6b21564..8c2cc8978 100644 +--- a/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-V_4.cpp ++++ b/core/fpdfapi/cmaps/Japan1/UniJIS-UCS2-V_4.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_UniJIS_UCS2_V_4[251 * 2] = { ++namespace fxcmap { ++ ++const uint16_t kUniJIS_UCS2_V_4[251 * 2] = { + 0x00B0, 0x204D, 0x2010, 0x1ED5, 0x2015, 0x1ED4, 0x2016, 0x1ED7, 0x2018, + 0x2059, 0x2019, 0x205A, 0x201C, 0x2057, 0x201D, 0x2058, 0x2025, 0x1EDA, + 0x2026, 0x1ED9, 0x2032, 0x2051, 0x2033, 0x205B, 0x2190, 0x02E2, 0x2191, +@@ -64,3 +66,5 @@ const uint16_t g_FXCMAP_UniJIS_UCS2_V_4[251 * 2] = { + 0xFF3B, 0x1EDF, 0xFF3D, 0x1EE0, 0xFF3F, 0x1ED2, 0xFF5B, 0x1EE1, 0xFF5C, + 0x1ED8, 0xFF5D, 0x1EE2, 0xFF5E, 0x1ED6, 0xFFE3, 0x1ED1, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/V_1.cpp b/core/fpdfapi/cmaps/Japan1/V_1.cpp +index 645e1effc..8fcc59f75 100644 +--- a/core/fpdfapi/cmaps/Japan1/V_1.cpp ++++ b/core/fpdfapi/cmaps/Japan1/V_1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-const uint16_t g_FXCMAP_V_1[27 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kV_1[27 * 3] = { + 0x2122, 0x2123, 0x1ECF, 0x2131, 0x2132, 0x1ED1, 0x213C, 0x213E, 0x1ED3, + 0x2141, 0x2145, 0x1ED6, 0x214A, 0x215B, 0x1EDB, 0x2161, 0x2161, 0x1EED, + 0x2421, 0x2421, 0x1EEE, 0x2423, 0x2423, 0x1EEF, 0x2425, 0x2425, 0x1EF0, +@@ -17,3 +19,5 @@ const uint16_t g_FXCMAP_V_1[27 * 3] = { + 0x2543, 0x2543, 0x1EFD, 0x2563, 0x2563, 0x1EFE, 0x2565, 0x2565, 0x1EFF, + 0x2567, 0x2567, 0x1F00, 0x256E, 0x256E, 0x1F01, 0x2575, 0x2576, 0x1F02, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/cmaps_japan1.cpp b/core/fpdfapi/cmaps/Japan1/cmaps_japan1.cpp +index 5a48c771d..947585446 100644 +--- a/core/fpdfapi/cmaps/Japan1/cmaps_japan1.cpp ++++ b/core/fpdfapi/cmaps/Japan1/cmaps_japan1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,45 +6,39 @@ + + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + +-#include "core/fxcrt/fx_memory.h" ++#include + +-const FXCMAP_CMap g_FXCMAP_Japan1_cmaps[] = { +- {"83pv-RKSJ-H", g_FXCMAP_83pv_RKSJ_H_1, nullptr, 222, 0, FXCMAP_CMap::Range, +- 0}, +- {"90ms-RKSJ-H", g_FXCMAP_90ms_RKSJ_H_2, nullptr, 171, 0, FXCMAP_CMap::Range, ++namespace fxcmap { ++ ++const CMap kJapan1_cmaps[] = { ++ {"83pv-RKSJ-H", k83pv_RKSJ_H_1, nullptr, 222, 0, CMap::Type::kRange, 0}, ++ {"90ms-RKSJ-H", k90ms_RKSJ_H_2, nullptr, 171, 0, CMap::Type::kRange, 0}, ++ {"90ms-RKSJ-V", k90ms_RKSJ_V_2, nullptr, 78, 0, CMap::Type::kRange, -1}, ++ {"90msp-RKSJ-H", k90msp_RKSJ_H_2, nullptr, 170, 0, CMap::Type::kRange, -2}, ++ {"90msp-RKSJ-V", k90msp_RKSJ_V_2, nullptr, 78, 0, CMap::Type::kRange, -1}, ++ {"90pv-RKSJ-H", k90pv_RKSJ_H_1, nullptr, 263, 0, CMap::Type::kRange, 0}, ++ {"Add-RKSJ-H", kAdd_RKSJ_H_1, nullptr, 635, 0, CMap::Type::kRange, 0}, ++ {"Add-RKSJ-V", kAdd_RKSJ_V_1, nullptr, 57, 0, CMap::Type::kRange, -1}, ++ {"EUC-H", kEUC_H_1, nullptr, 120, 0, CMap::Type::kRange, 0}, ++ {"EUC-V", kEUC_V_1, nullptr, 27, 0, CMap::Type::kRange, -1}, ++ {"Ext-RKSJ-H", kExt_RKSJ_H_2, nullptr, 665, 0, CMap::Type::kRange, -4}, ++ {"Ext-RKSJ-V", kExt_RKSJ_V_2, nullptr, 39, 0, CMap::Type::kRange, -1}, ++ {"H", kH_1, nullptr, 118, 0, CMap::Type::kRange, 0}, ++ {"V", kV_1, nullptr, 27, 0, CMap::Type::kRange, -1}, ++ {"UniJIS-UCS2-H", kUniJIS_UCS2_H_4, nullptr, 9772, 0, CMap::Type::kSingle, + 0}, +- {"90ms-RKSJ-V", g_FXCMAP_90ms_RKSJ_V_2, nullptr, 78, 0, FXCMAP_CMap::Range, ++ {"UniJIS-UCS2-V", kUniJIS_UCS2_V_4, nullptr, 251, 0, CMap::Type::kSingle, + -1}, +- {"90msp-RKSJ-H", g_FXCMAP_90msp_RKSJ_H_2, nullptr, 170, 0, +- FXCMAP_CMap::Range, -2}, +- {"90msp-RKSJ-V", g_FXCMAP_90msp_RKSJ_V_2, nullptr, 78, 0, +- FXCMAP_CMap::Range, -1}, +- {"90pv-RKSJ-H", g_FXCMAP_90pv_RKSJ_H_1, nullptr, 263, 0, FXCMAP_CMap::Range, +- 0}, +- {"Add-RKSJ-H", g_FXCMAP_Add_RKSJ_H_1, nullptr, 635, 0, FXCMAP_CMap::Range, ++ {"UniJIS-UCS2-HW-H", kUniJIS_UCS2_HW_H_4, nullptr, 4, 0, CMap::Type::kRange, ++ -2}, ++ {"UniJIS-UCS2-HW-V", kUniJIS_UCS2_HW_V_4, nullptr, 199, 0, ++ CMap::Type::kRange, -1}, ++ {"UniJIS-UTF16-H", kUniJIS_UCS2_H_4, nullptr, 9772, 0, CMap::Type::kSingle, + 0}, +- {"Add-RKSJ-V", g_FXCMAP_Add_RKSJ_V_1, nullptr, 57, 0, FXCMAP_CMap::Range, ++ {"UniJIS-UTF16-V", kUniJIS_UCS2_V_4, nullptr, 251, 0, CMap::Type::kSingle, + -1}, +- {"EUC-H", g_FXCMAP_EUC_H_1, nullptr, 120, 0, FXCMAP_CMap::Range, 0}, +- {"EUC-V", g_FXCMAP_EUC_V_1, nullptr, 27, 0, FXCMAP_CMap::Range, -1}, +- {"Ext-RKSJ-H", g_FXCMAP_Ext_RKSJ_H_2, nullptr, 665, 0, FXCMAP_CMap::Range, +- -4}, +- {"Ext-RKSJ-V", g_FXCMAP_Ext_RKSJ_V_2, nullptr, 39, 0, FXCMAP_CMap::Range, +- -1}, +- {"H", g_FXCMAP_H_1, nullptr, 118, 0, FXCMAP_CMap::Range, 0}, +- {"V", g_FXCMAP_V_1, nullptr, 27, 0, FXCMAP_CMap::Range, -1}, +- {"UniJIS-UCS2-H", g_FXCMAP_UniJIS_UCS2_H_4, nullptr, 9772, 0, +- FXCMAP_CMap::Single, 0}, +- {"UniJIS-UCS2-V", g_FXCMAP_UniJIS_UCS2_V_4, nullptr, 251, 0, +- FXCMAP_CMap::Single, -1}, +- {"UniJIS-UCS2-HW-H", g_FXCMAP_UniJIS_UCS2_HW_H_4, nullptr, 4, 0, +- FXCMAP_CMap::Range, -2}, +- {"UniJIS-UCS2-HW-V", g_FXCMAP_UniJIS_UCS2_HW_V_4, nullptr, 199, 0, +- FXCMAP_CMap::Range, -1}, +- {"UniJIS-UTF16-H", g_FXCMAP_UniJIS_UCS2_H_4, nullptr, 9772, 0, +- FXCMAP_CMap::Single, 0}, +- {"UniJIS-UTF16-V", g_FXCMAP_UniJIS_UCS2_V_4, nullptr, 251, 0, +- FXCMAP_CMap::Single, -1}, + }; + +-const size_t g_FXCMAP_Japan1_cmaps_size = FX_ArraySize(g_FXCMAP_Japan1_cmaps); ++const size_t kJapan1_cmaps_size = std::size(kJapan1_cmaps); ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Japan1/cmaps_japan1.h b/core/fpdfapi/cmaps/Japan1/cmaps_japan1.h +index 267a9fc23..45df22df4 100644 +--- a/core/fpdfapi/cmaps/Japan1/cmaps_japan1.h ++++ b/core/fpdfapi/cmaps/Japan1/cmaps_japan1.h +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,31 +7,38 @@ + #ifndef CORE_FPDFAPI_CMAPS_JAPAN1_CMAPS_JAPAN1_H_ + #define CORE_FPDFAPI_CMAPS_JAPAN1_CMAPS_JAPAN1_H_ + ++#include ++#include ++ + #include "core/fpdfapi/cmaps/fpdf_cmaps.h" + +-extern const uint16_t g_FXCMAP_83pv_RKSJ_H_1[]; +-extern const uint16_t g_FXCMAP_90ms_RKSJ_H_2[]; +-extern const uint16_t g_FXCMAP_90ms_RKSJ_V_2[]; +-extern const uint16_t g_FXCMAP_90msp_RKSJ_H_2[]; +-extern const uint16_t g_FXCMAP_90msp_RKSJ_V_2[]; +-extern const uint16_t g_FXCMAP_90pv_RKSJ_H_1[]; +-extern const uint16_t g_FXCMAP_Add_RKSJ_H_1[]; +-extern const uint16_t g_FXCMAP_Add_RKSJ_V_1[]; +-extern const uint16_t g_FXCMAP_EUC_H_1[]; +-extern const uint16_t g_FXCMAP_EUC_V_1[]; +-extern const uint16_t g_FXCMAP_Ext_RKSJ_H_2[]; +-extern const uint16_t g_FXCMAP_Ext_RKSJ_V_2[]; +-extern const uint16_t g_FXCMAP_H_1[]; +-extern const uint16_t g_FXCMAP_V_1[]; +-extern const uint16_t g_FXCMAP_UniJIS_UCS2_H_4[]; +-extern const uint16_t g_FXCMAP_UniJIS_UCS2_V_4[]; +-extern const uint16_t g_FXCMAP_UniJIS_UCS2_HW_H_4[]; +-extern const uint16_t g_FXCMAP_UniJIS_UCS2_HW_V_4[]; +-extern const uint16_t g_FXCMAP_UniJIS_UTF16_H_0[]; +-extern const uint16_t g_FXCMAP_UniJIS_UTF16_H_0_DWord[]; +-extern const uint16_t g_FXCMAP_UniJIS_UTF16_V_0[]; +-extern const uint16_t g_FXCMAP_Japan1CID2Unicode_4[15444]; +-extern const FXCMAP_CMap g_FXCMAP_Japan1_cmaps[]; +-extern const size_t g_FXCMAP_Japan1_cmaps_size; ++namespace fxcmap { ++ ++extern const uint16_t k83pv_RKSJ_H_1[]; ++extern const uint16_t k90ms_RKSJ_H_2[]; ++extern const uint16_t k90ms_RKSJ_V_2[]; ++extern const uint16_t k90msp_RKSJ_H_2[]; ++extern const uint16_t k90msp_RKSJ_V_2[]; ++extern const uint16_t k90pv_RKSJ_H_1[]; ++extern const uint16_t kAdd_RKSJ_H_1[]; ++extern const uint16_t kAdd_RKSJ_V_1[]; ++extern const uint16_t kEUC_H_1[]; ++extern const uint16_t kEUC_V_1[]; ++extern const uint16_t kExt_RKSJ_H_2[]; ++extern const uint16_t kExt_RKSJ_V_2[]; ++extern const uint16_t kH_1[]; ++extern const uint16_t kV_1[]; ++extern const uint16_t kUniJIS_UCS2_H_4[]; ++extern const uint16_t kUniJIS_UCS2_V_4[]; ++extern const uint16_t kUniJIS_UCS2_HW_H_4[]; ++extern const uint16_t kUniJIS_UCS2_HW_V_4[]; ++extern const uint16_t kUniJIS_UTF16_H_0[]; ++extern const uint16_t kUniJIS_UTF16_H_0_DWord[]; ++extern const uint16_t kUniJIS_UTF16_V_0[]; ++extern const uint16_t kJapan1CID2Unicode_4[15444]; ++extern const CMap kJapan1_cmaps[]; ++extern const size_t kJapan1_cmaps_size; ++ ++} // namespace fxcmap + + #endif // CORE_FPDFAPI_CMAPS_JAPAN1_CMAPS_JAPAN1_H_ +diff --git a/core/fpdfapi/cmaps/Korea1/Adobe-Korea1-UCS2_2.cpp b/core/fpdfapi/cmaps/Korea1/Adobe-Korea1-UCS2_2.cpp +index 3f81ff944..e7657cf32 100644 +--- a/core/fpdfapi/cmaps/Korea1/Adobe-Korea1-UCS2_2.cpp ++++ b/core/fpdfapi/cmaps/Korea1/Adobe-Korea1-UCS2_2.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h" + +-const uint16_t g_FXCMAP_Korea1CID2Unicode_2[18352] = { ++namespace fxcmap { ++ ++const uint16_t kKorea1CID2Unicode_2[18352] = { + 0xFFFD, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, + 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, +@@ -2048,3 +2050,5 @@ const uint16_t g_FXCMAP_Korea1CID2Unicode_2[18352] = { + 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x203E, 0x007E, + 0x005C, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Korea1/KSC-EUC-H_0.cpp b/core/fpdfapi/cmaps/Korea1/KSC-EUC-H_0.cpp +index cce7899a8..2ba31b49b 100644 +--- a/core/fpdfapi/cmaps/Korea1/KSC-EUC-H_0.cpp ++++ b/core/fpdfapi/cmaps/Korea1/KSC-EUC-H_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h" + +-const uint16_t g_FXCMAP_KSC_EUC_H_0[467 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kKSC_EUC_H_0[467 * 3] = { + 0x0020, 0x007E, 0x1F9E, 0xA1A1, 0xA1FE, 0x0065, 0xA2A1, 0xA2E5, 0x00C3, + 0xA3A1, 0xA3FE, 0x0108, 0xA4A1, 0xA4D3, 0x0166, 0xA4D5, 0xA4FE, 0x0199, + 0xA5A1, 0xA5AA, 0x01C3, 0xA5B0, 0xA5B9, 0x01CD, 0xA5C1, 0xA5D8, 0x01D7, +@@ -164,3 +166,5 @@ const uint16_t g_FXCMAP_KSC_EUC_H_0[467 * 3] = { + 0xFBA1, 0xFBFE, 0x1E5F, 0xFCA1, 0xFCA8, 0x1EBD, 0xFCA9, 0xFCA9, 0x0EE7, + 0xFCAA, 0xFCFE, 0x1EC5, 0xFDA1, 0xFDFE, 0x1F1A, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Korea1/KSC-EUC-V_0.cpp b/core/fpdfapi/cmaps/Korea1/KSC-EUC-V_0.cpp +index 7408f9e59..b16607594 100644 +--- a/core/fpdfapi/cmaps/Korea1/KSC-EUC-V_0.cpp ++++ b/core/fpdfapi/cmaps/Korea1/KSC-EUC-V_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h" + +-const uint16_t g_FXCMAP_KSC_EUC_V_0[16 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kKSC_EUC_V_0[16 * 3] = { + 0xA1A2, 0xA1A3, 0x1F78, 0xA1A5, 0xA1A5, 0x1F7A, 0xA1A6, 0xA1A6, + 0x2080, 0xA1A9, 0xA1AB, 0x1F7B, 0xA1AD, 0xA1AD, 0x1F7E, 0xA1B2, + 0xA1BD, 0x1F7F, 0xA1EB, 0xA1EB, 0x1F8B, 0xA3A1, 0xA3A1, 0x1F8C, +@@ -14,3 +16,5 @@ const uint16_t g_FXCMAP_KSC_EUC_V_0[16 * 3] = { + 0x1F90, 0xA3BA, 0xA3BF, 0x1F91, 0xA3DB, 0xA3DB, 0x1F97, 0xA3DD, + 0xA3DD, 0x1F98, 0xA3DF, 0xA3DF, 0x1F99, 0xA3FB, 0xA3FE, 0x1F9A, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-H_1.cpp b/core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-H_1.cpp +index fbbdee806..161198d28 100644 +--- a/core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-H_1.cpp ++++ b/core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-H_1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h" + +-const uint16_t g_FXCMAP_KSCms_UHC_HW_H_1[675 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kKSCms_UHC_HW_H_1[675 * 3] = { + 0x0020, 0x007E, 0x1F9E, 0x8141, 0x815A, 0x2475, 0x8161, 0x817A, 0x248F, + 0x8181, 0x81FE, 0x24A9, 0x8241, 0x825A, 0x2527, 0x8261, 0x827A, 0x2541, + 0x8281, 0x82FE, 0x255B, 0x8341, 0x835A, 0x25D9, 0x8361, 0x837A, 0x25F3, +@@ -233,3 +235,5 @@ const uint16_t g_FXCMAP_KSCms_UHC_HW_H_1[675 * 3] = { + 0xFAE7, 0xFAFE, 0x1E47, 0xFBA1, 0xFBFE, 0x1E5F, 0xFCA1, 0xFCA8, 0x1EBD, + 0xFCA9, 0xFCA9, 0x0EE7, 0xFCAA, 0xFCFE, 0x1EC5, 0xFDA1, 0xFDFE, 0x1F1A, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-V_1.cpp b/core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-V_1.cpp +index 16ddafa2b..575a27df1 100644 +--- a/core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-V_1.cpp ++++ b/core/fpdfapi/cmaps/Korea1/KSCms-UHC-HW-V_1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h" + +-const uint16_t g_FXCMAP_KSCms_UHC_HW_V_1[16 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kKSCms_UHC_HW_V_1[16 * 3] = { + 0xA1A2, 0xA1A3, 0x1F78, 0xA1A5, 0xA1A5, 0x1F7A, 0xA1A6, 0xA1A6, + 0x2080, 0xA1A9, 0xA1AB, 0x1F7B, 0xA1AD, 0xA1AD, 0x1F7E, 0xA1B2, + 0xA1BD, 0x1F7F, 0xA1EB, 0xA1EB, 0x1F8B, 0xA3A1, 0xA3A1, 0x1F8C, +@@ -14,3 +16,5 @@ const uint16_t g_FXCMAP_KSCms_UHC_HW_V_1[16 * 3] = { + 0x1F90, 0xA3BA, 0xA3BF, 0x1F91, 0xA3DB, 0xA3DB, 0x1F97, 0xA3DD, + 0xA3DD, 0x1F98, 0xA3DF, 0xA3DF, 0x1F99, 0xA3FB, 0xA3FE, 0x1F9A, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Korea1/KSCms-UHC-H_1.cpp b/core/fpdfapi/cmaps/Korea1/KSCms-UHC-H_1.cpp +index 986c87922..7d855c695 100644 +--- a/core/fpdfapi/cmaps/Korea1/KSCms-UHC-H_1.cpp ++++ b/core/fpdfapi/cmaps/Korea1/KSCms-UHC-H_1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h" + +-const uint16_t g_FXCMAP_KSCms_UHC_H_1[675 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kKSCms_UHC_H_1[675 * 3] = { + 0x0020, 0x007E, 0x0001, 0x8141, 0x815A, 0x2475, 0x8161, 0x817A, 0x248F, + 0x8181, 0x81FE, 0x24A9, 0x8241, 0x825A, 0x2527, 0x8261, 0x827A, 0x2541, + 0x8281, 0x82FE, 0x255B, 0x8341, 0x835A, 0x25D9, 0x8361, 0x837A, 0x25F3, +@@ -233,3 +235,5 @@ const uint16_t g_FXCMAP_KSCms_UHC_H_1[675 * 3] = { + 0xFAE7, 0xFAFE, 0x1E47, 0xFBA1, 0xFBFE, 0x1E5F, 0xFCA1, 0xFCA8, 0x1EBD, + 0xFCA9, 0xFCA9, 0x0EE7, 0xFCAA, 0xFCFE, 0x1EC5, 0xFDA1, 0xFDFE, 0x1F1A, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Korea1/KSCms-UHC-V_1.cpp b/core/fpdfapi/cmaps/Korea1/KSCms-UHC-V_1.cpp +index e04044456..7b18417a3 100644 +--- a/core/fpdfapi/cmaps/Korea1/KSCms-UHC-V_1.cpp ++++ b/core/fpdfapi/cmaps/Korea1/KSCms-UHC-V_1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h" + +-const uint16_t g_FXCMAP_KSCms_UHC_V_1[16 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kKSCms_UHC_V_1[16 * 3] = { + 0xA1A2, 0xA1A3, 0x1F78, 0xA1A5, 0xA1A5, 0x1F7A, 0xA1A6, 0xA1A6, + 0x2080, 0xA1A9, 0xA1AB, 0x1F7B, 0xA1AD, 0xA1AD, 0x1F7E, 0xA1B2, + 0xA1BD, 0x1F7F, 0xA1EB, 0xA1EB, 0x1F8B, 0xA3A1, 0xA3A1, 0x1F8C, +@@ -14,3 +16,5 @@ const uint16_t g_FXCMAP_KSCms_UHC_V_1[16 * 3] = { + 0x1F90, 0xA3BA, 0xA3BF, 0x1F91, 0xA3DB, 0xA3DB, 0x1F97, 0xA3DD, + 0xA3DD, 0x1F98, 0xA3DF, 0xA3DF, 0x1F99, 0xA3FB, 0xA3FE, 0x1F9A, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Korea1/KSCpc-EUC-H_0.cpp b/core/fpdfapi/cmaps/Korea1/KSCpc-EUC-H_0.cpp +index 12fa16ddd..87e8aa55e 100644 +--- a/core/fpdfapi/cmaps/Korea1/KSCpc-EUC-H_0.cpp ++++ b/core/fpdfapi/cmaps/Korea1/KSCpc-EUC-H_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h" + +-const uint16_t g_FXCMAP_KSCpc_EUC_H_0[509 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kKSCpc_EUC_H_0[509 * 3] = { + 0x0020, 0x007E, 0x0001, 0x0081, 0x0083, 0x0060, 0x00FE, 0x00FF, 0x0063, + 0xA141, 0xA17D, 0x1FFF, 0xA181, 0xA19A, 0x203C, 0xA19C, 0xA1A0, 0x2056, + 0xA1A1, 0xA1A1, 0x0065, 0xA1A2, 0xA1A3, 0x205B, 0xA1A4, 0xA1FE, 0x0068, +@@ -178,3 +180,5 @@ const uint16_t g_FXCMAP_KSCpc_EUC_H_0[509 * 3] = { + 0xFBA1, 0xFBFE, 0x1E5F, 0xFCA1, 0xFCA8, 0x1EBD, 0xFCA9, 0xFCA9, 0x0EE7, + 0xFCAA, 0xFCFE, 0x1EC5, 0xFDA1, 0xFDFE, 0x1F1A, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Korea1/UniKS-UCS2-H_1.cpp b/core/fpdfapi/cmaps/Korea1/UniKS-UCS2-H_1.cpp +index 8ed9fbe3f..0d8d3fc01 100644 +--- a/core/fpdfapi/cmaps/Korea1/UniKS-UCS2-H_1.cpp ++++ b/core/fpdfapi/cmaps/Korea1/UniKS-UCS2-H_1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h" + +-const uint16_t g_FXCMAP_UniKS_UCS2_H_1[8394 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kUniKS_UCS2_H_1[8394 * 3] = { + 0x0020, 0x007E, 0x0001, 0x00A1, 0x00A1, 0x00D0, 0x00A4, 0x00A4, 0x00D6, + 0x00A7, 0x00A7, 0x009B, 0x00A8, 0x00A8, 0x006B, 0x00AA, 0x00AA, 0x029C, + 0x00AB, 0x00AB, 0x00B0, 0x00B0, 0x00B0, 0x008A, 0x00B1, 0x00B1, 0x0082, +@@ -2806,3 +2808,5 @@ const uint16_t g_FXCMAP_UniKS_UCS2_H_1[8394 * 3] = { + 0xFF5E, 0xFF5E, 0x0071, 0xFFE0, 0xFFE1, 0x008F, 0xFFE2, 0xFFE2, 0x00C2, + 0xFFE3, 0xFFE3, 0x0165, 0xFFE5, 0xFFE5, 0x0091, 0xFFE6, 0xFFE6, 0x0143, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Korea1/UniKS-UCS2-V_1.cpp b/core/fpdfapi/cmaps/Korea1/UniKS-UCS2-V_1.cpp +index 46759c51a..a550b175f 100644 +--- a/core/fpdfapi/cmaps/Korea1/UniKS-UCS2-V_1.cpp ++++ b/core/fpdfapi/cmaps/Korea1/UniKS-UCS2-V_1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h" + +-const uint16_t g_FXCMAP_UniKS_UCS2_V_1[18 * 3] = { ++namespace fxcmap { ++ ++const uint16_t kUniKS_UCS2_V_1[18 * 3] = { + 0x2013, 0x2014, 0x1F7B, 0x2016, 0x2016, 0x1F7D, 0x2025, 0x2025, 0x1F7A, + 0x3001, 0x3002, 0x1F78, 0x3008, 0x3011, 0x1F81, 0x3013, 0x3013, 0x1F8B, + 0x3014, 0x3015, 0x1F7F, 0xFF01, 0xFF01, 0x1F8C, 0xFF08, 0xFF09, 0x1F8D, +@@ -14,3 +16,5 @@ const uint16_t g_FXCMAP_UniKS_UCS2_V_1[18 * 3] = { + 0xFF3B, 0xFF3B, 0x1F97, 0xFF3D, 0xFF3D, 0x1F98, 0xFF3F, 0xFF3F, 0x1F99, + 0xFF5B, 0xFF5D, 0x1F9A, 0xFF5E, 0xFF5E, 0x1F7E, 0xFFE3, 0xFFE3, 0x1F9D, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Korea1/UniKS-UTF16-H_0.cpp b/core/fpdfapi/cmaps/Korea1/UniKS-UTF16-H_0.cpp +index 21018059f..2d42617c7 100644 +--- a/core/fpdfapi/cmaps/Korea1/UniKS-UTF16-H_0.cpp ++++ b/core/fpdfapi/cmaps/Korea1/UniKS-UTF16-H_0.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h" + +-const uint16_t g_FXCMAP_UniKS_UTF16_H_0[158 * 2] = { ++namespace fxcmap { ++ ++const uint16_t kUniKS_UTF16_H_0[158 * 2] = { + 0x00A9, 0x0062, 0x2010, 0x0061, 0x20A9, 0x0060, 0x2F00, 0x193C, 0x2F04, + 0x18EC, 0x2F06, 0x190D, 0x2F08, 0x192B, 0x2F0A, 0x194D, 0x2F0B, 0x1D4B, + 0x2F11, 0x10AE, 0x2F12, 0x116A, 0x2F14, 0x143F, 0x2F17, 0x168C, 0x2F18, +@@ -44,3 +46,5 @@ const uint16_t g_FXCMAP_UniKS_UTF16_H_0[158 * 2] = { + 0x2FD0, 0x1466, 0x2FD1, 0x1A7D, 0x2FD2, 0x1CBF, 0x2FD3, 0x11D3, 0x2FD4, + 0x0F6A, + }; ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Korea1/cmaps_korea1.cpp b/core/fpdfapi/cmaps/Korea1/cmaps_korea1.cpp +index 7362ff859..2109e0435 100644 +--- a/core/fpdfapi/cmaps/Korea1/cmaps_korea1.cpp ++++ b/core/fpdfapi/cmaps/Korea1/cmaps_korea1.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,29 +6,27 @@ + + #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h" + +-#include "core/fxcrt/fx_memory.h" ++#include + +-const FXCMAP_CMap g_FXCMAP_Korea1_cmaps[] = { +- {"KSC-EUC-H", g_FXCMAP_KSC_EUC_H_0, nullptr, 467, 0, FXCMAP_CMap::Range, 0}, +- {"KSC-EUC-V", g_FXCMAP_KSC_EUC_V_0, nullptr, 16, 0, FXCMAP_CMap::Range, -1}, +- {"KSCms-UHC-H", g_FXCMAP_KSCms_UHC_H_1, nullptr, 675, 0, FXCMAP_CMap::Range, +- -2}, +- {"KSCms-UHC-V", g_FXCMAP_KSCms_UHC_V_1, nullptr, 16, 0, FXCMAP_CMap::Range, ++namespace fxcmap { ++ ++const CMap kKorea1_cmaps[] = { ++ {"KSC-EUC-H", kKSC_EUC_H_0, nullptr, 467, 0, CMap::Type::kRange, 0}, ++ {"KSC-EUC-V", kKSC_EUC_V_0, nullptr, 16, 0, CMap::Type::kRange, -1}, ++ {"KSCms-UHC-H", kKSCms_UHC_H_1, nullptr, 675, 0, CMap::Type::kRange, -2}, ++ {"KSCms-UHC-V", kKSCms_UHC_V_1, nullptr, 16, 0, CMap::Type::kRange, -1}, ++ {"KSCms-UHC-HW-H", kKSCms_UHC_HW_H_1, nullptr, 675, 0, CMap::Type::kRange, ++ 0}, ++ {"KSCms-UHC-HW-V", kKSCms_UHC_HW_V_1, nullptr, 16, 0, CMap::Type::kRange, + -1}, +- {"KSCms-UHC-HW-H", g_FXCMAP_KSCms_UHC_HW_H_1, nullptr, 675, 0, +- FXCMAP_CMap::Range, 0}, +- {"KSCms-UHC-HW-V", g_FXCMAP_KSCms_UHC_HW_V_1, nullptr, 16, 0, +- FXCMAP_CMap::Range, -1}, +- {"KSCpc-EUC-H", g_FXCMAP_KSCpc_EUC_H_0, nullptr, 509, 0, FXCMAP_CMap::Range, +- -6}, +- {"UniKS-UCS2-H", g_FXCMAP_UniKS_UCS2_H_1, nullptr, 8394, 0, +- FXCMAP_CMap::Range, 0}, +- {"UniKS-UCS2-V", g_FXCMAP_UniKS_UCS2_V_1, nullptr, 18, 0, +- FXCMAP_CMap::Range, -1}, +- {"UniKS-UTF16-H", g_FXCMAP_UniKS_UTF16_H_0, nullptr, 158, 0, +- FXCMAP_CMap::Single, -2}, +- {"UniKS-UTF16-V", g_FXCMAP_UniKS_UCS2_V_1, nullptr, 18, 0, +- FXCMAP_CMap::Range, -1}, ++ {"KSCpc-EUC-H", kKSCpc_EUC_H_0, nullptr, 509, 0, CMap::Type::kRange, -6}, ++ {"UniKS-UCS2-H", kUniKS_UCS2_H_1, nullptr, 8394, 0, CMap::Type::kRange, 0}, ++ {"UniKS-UCS2-V", kUniKS_UCS2_V_1, nullptr, 18, 0, CMap::Type::kRange, -1}, ++ {"UniKS-UTF16-H", kUniKS_UTF16_H_0, nullptr, 158, 0, CMap::Type::kSingle, ++ -2}, ++ {"UniKS-UTF16-V", kUniKS_UCS2_V_1, nullptr, 18, 0, CMap::Type::kRange, -1}, + }; + +-const size_t g_FXCMAP_Korea1_cmaps_size = FX_ArraySize(g_FXCMAP_Korea1_cmaps); ++const size_t kKorea1_cmaps_size = std::size(kKorea1_cmaps); ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/Korea1/cmaps_korea1.h b/core/fpdfapi/cmaps/Korea1/cmaps_korea1.h +index d54d15663..f75ded9e9 100644 +--- a/core/fpdfapi/cmaps/Korea1/cmaps_korea1.h ++++ b/core/fpdfapi/cmaps/Korea1/cmaps_korea1.h +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,20 +7,27 @@ + #ifndef CORE_FPDFAPI_CMAPS_KOREA1_CMAPS_KOREA1_H_ + #define CORE_FPDFAPI_CMAPS_KOREA1_CMAPS_KOREA1_H_ + ++#include ++#include ++ + #include "core/fpdfapi/cmaps/fpdf_cmaps.h" + +-extern const uint16_t g_FXCMAP_KSC_EUC_H_0[]; +-extern const uint16_t g_FXCMAP_KSC_EUC_V_0[]; +-extern const uint16_t g_FXCMAP_KSCms_UHC_H_1[]; +-extern const uint16_t g_FXCMAP_KSCms_UHC_V_1[]; +-extern const uint16_t g_FXCMAP_KSCms_UHC_HW_H_1[]; +-extern const uint16_t g_FXCMAP_KSCms_UHC_HW_V_1[]; +-extern const uint16_t g_FXCMAP_KSCpc_EUC_H_0[]; +-extern const uint16_t g_FXCMAP_UniKS_UCS2_H_1[]; +-extern const uint16_t g_FXCMAP_UniKS_UCS2_V_1[]; +-extern const uint16_t g_FXCMAP_UniKS_UTF16_H_0[]; +-extern const uint16_t g_FXCMAP_Korea1CID2Unicode_2[18352]; +-extern const FXCMAP_CMap g_FXCMAP_Korea1_cmaps[]; +-extern const size_t g_FXCMAP_Korea1_cmaps_size; ++namespace fxcmap { ++ ++extern const uint16_t kKSC_EUC_H_0[]; ++extern const uint16_t kKSC_EUC_V_0[]; ++extern const uint16_t kKSCms_UHC_H_1[]; ++extern const uint16_t kKSCms_UHC_V_1[]; ++extern const uint16_t kKSCms_UHC_HW_H_1[]; ++extern const uint16_t kKSCms_UHC_HW_V_1[]; ++extern const uint16_t kKSCpc_EUC_H_0[]; ++extern const uint16_t kUniKS_UCS2_H_1[]; ++extern const uint16_t kUniKS_UCS2_V_1[]; ++extern const uint16_t kUniKS_UTF16_H_0[]; ++extern const uint16_t kKorea1CID2Unicode_2[18352]; ++extern const CMap kKorea1_cmaps[]; ++extern const size_t kKorea1_cmaps_size; ++ ++} // namespace fxcmap + + #endif // CORE_FPDFAPI_CMAPS_KOREA1_CMAPS_KOREA1_H_ +diff --git a/core/fpdfapi/cmaps/fpdf_cmaps.cpp b/core/fpdfapi/cmaps/fpdf_cmaps.cpp +index 5c89faf81..98187a1c5 100644 +--- a/core/fpdfapi/cmaps/fpdf_cmaps.cpp ++++ b/core/fpdfapi/cmaps/fpdf_cmaps.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,6 +8,10 @@ + + #include + ++#include "third_party/base/check.h" ++ ++namespace fxcmap { ++ + namespace { + + struct SingleCmap { +@@ -21,32 +25,23 @@ struct RangeCmap { + uint16_t cid; + }; + +-const FXCMAP_CMap* FindNextCMap(const FXCMAP_CMap* pMap) { ++const CMap* FindNextCMap(const CMap* pMap) { + return pMap->m_UseOffset ? pMap + pMap->m_UseOffset : nullptr; + } + + } // namespace + +-const FXCMAP_CMap* FindEmbeddedCMap(pdfium::span pCMaps, +- ByteStringView bsName) { +- for (size_t i = 0; i < pCMaps.size(); i++) { +- if (bsName == pCMaps[i].m_Name) +- return &pCMaps[i]; +- } +- return nullptr; +-} +- +-uint16_t CIDFromCharCode(const FXCMAP_CMap* pMap, uint32_t charcode) { +- ASSERT(pMap); ++uint16_t CIDFromCharCode(const CMap* pMap, uint32_t charcode) { ++ DCHECK(pMap); + const uint16_t loword = static_cast(charcode); + if (charcode >> 16) { + while (pMap) { + if (pMap->m_pDWordMap) { +- const FXCMAP_DWordCIDMap* begin = pMap->m_pDWordMap; ++ const DWordCIDMap* begin = pMap->m_pDWordMap; + const auto* end = begin + pMap->m_DWordCount; + const auto* found = std::lower_bound( + begin, end, charcode, +- [](const FXCMAP_DWordCIDMap& element, uint32_t charcode) { ++ [](const DWordCIDMap& element, uint32_t charcode) { + uint16_t hiword = static_cast(charcode >> 16); + if (element.m_HiWord != hiword) + return element.m_HiWord < hiword; +@@ -64,7 +59,7 @@ uint16_t CIDFromCharCode(const FXCMAP_CMap* pMap, uint32_t charcode) { + + while (pMap && pMap->m_pWordMap) { + switch (pMap->m_WordMapType) { +- case FXCMAP_CMap::Single: { ++ case CMap::Type::kSingle: { + const auto* begin = + reinterpret_cast(pMap->m_pWordMap); + const auto* end = begin + pMap->m_WordCount; +@@ -76,7 +71,7 @@ uint16_t CIDFromCharCode(const FXCMAP_CMap* pMap, uint32_t charcode) { + return found->cid; + break; + } +- case FXCMAP_CMap::Range: { ++ case CMap::Type::kRange: { + const auto* begin = + reinterpret_cast(pMap->m_pWordMap); + const auto* end = begin + pMap->m_WordCount; +@@ -88,10 +83,6 @@ uint16_t CIDFromCharCode(const FXCMAP_CMap* pMap, uint32_t charcode) { + return found->cid + loword - found->low; + break; + } +- default: { +- NOTREACHED(); +- break; +- } + } + pMap = FindNextCMap(pMap); + } +@@ -99,16 +90,16 @@ uint16_t CIDFromCharCode(const FXCMAP_CMap* pMap, uint32_t charcode) { + return 0; + } + +-uint32_t CharCodeFromCID(const FXCMAP_CMap* pMap, uint16_t cid) { ++uint32_t CharCodeFromCID(const CMap* pMap, uint16_t cid) { + // TODO(dsinclair): This should be checking both pMap->m_WordMap and + // pMap->m_DWordMap. There was a second while() but it was never reached as + // the first always returns. Investigate and determine how this should + // really be working. (https://codereview.chromium.org/2235743003 removed the + // second while loop.) +- ASSERT(pMap); ++ DCHECK(pMap); + while (pMap) { + switch (pMap->m_WordMapType) { +- case FXCMAP_CMap::Single: { ++ case CMap::Type::kSingle: { + const auto* pCur = + reinterpret_cast(pMap->m_pWordMap); + const auto* pEnd = pCur + pMap->m_WordCount; +@@ -119,7 +110,7 @@ uint32_t CharCodeFromCID(const FXCMAP_CMap* pMap, uint16_t cid) { + } + break; + } +- case FXCMAP_CMap::Range: { ++ case CMap::Type::kRange: { + const auto* pCur = reinterpret_cast(pMap->m_pWordMap); + const auto* pEnd = pCur + pMap->m_WordCount; + while (pCur < pEnd) { +@@ -129,12 +120,10 @@ uint32_t CharCodeFromCID(const FXCMAP_CMap* pMap, uint16_t cid) { + } + break; + } +- default: { +- NOTREACHED(); +- break; +- } + } + pMap = FindNextCMap(pMap); + } + return 0; + } ++ ++} // namespace fxcmap +diff --git a/core/fpdfapi/cmaps/fpdf_cmaps.h b/core/fpdfapi/cmaps/fpdf_cmaps.h +index 2c7548ac9..079459a2d 100644 +--- a/core/fpdfapi/cmaps/fpdf_cmaps.h ++++ b/core/fpdfapi/cmaps/fpdf_cmaps.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,31 +9,30 @@ + + #include + +-#include "core/fxcrt/fx_string.h" +-#include "third_party/base/span.h" ++namespace fxcmap { + +-struct FXCMAP_DWordCIDMap { ++struct DWordCIDMap { + uint16_t m_HiWord; + uint16_t m_LoWordLow; + uint16_t m_LoWordHigh; + uint16_t m_CID; + }; + +-struct FXCMAP_CMap { +- enum MapType : uint8_t { Single, Range }; ++struct CMap { ++ enum class Type : bool { kSingle, kRange }; + +- const char* m_Name; // Raw, POD struct. +- const uint16_t* m_pWordMap; // Raw, POD struct. +- const FXCMAP_DWordCIDMap* m_pDWordMap; // Raw, POD struct. ++ const char* m_Name; // Raw, POD struct. ++ const uint16_t* m_pWordMap; // Raw, POD struct. ++ const DWordCIDMap* m_pDWordMap; // Raw, POD struct. + uint16_t m_WordCount; + uint16_t m_DWordCount; +- MapType m_WordMapType; ++ Type m_WordMapType; + int8_t m_UseOffset; + }; + +-const FXCMAP_CMap* FindEmbeddedCMap(pdfium::span pCMaps, +- ByteStringView name); +-uint16_t CIDFromCharCode(const FXCMAP_CMap* pMap, uint32_t charcode); +-uint32_t CharCodeFromCID(const FXCMAP_CMap* pMap, uint16_t cid); ++uint16_t CIDFromCharCode(const CMap* pMap, uint32_t charcode); ++uint32_t CharCodeFromCID(const CMap* pMap, uint16_t cid); ++ ++} // namespace fxcmap + + #endif // CORE_FPDFAPI_CMAPS_FPDF_CMAPS_H_ +diff --git a/core/fpdfapi/edit/Android.bp b/core/fpdfapi/edit/Android.bp +index 1822b06c4..6b70ca8c3 100644 +--- a/core/fpdfapi/edit/Android.bp ++++ b/core/fpdfapi/edit/Android.bp +@@ -13,11 +13,8 @@ cc_library_static { + + visibility: ["//external/pdfium:__subpackages__"], + +- header_libs: [ +- "libpdfium-constants", +- ], +- + static_libs: [ ++ "libpdfium-constants", + "libpdfium-fxcrt", + "libpdfium-font", + "libpdfium-page", +diff --git a/core/fpdfapi/edit/BUILD.gn b/core/fpdfapi/edit/BUILD.gn +index cda86e63d..721dff809 100644 +--- a/core/fpdfapi/edit/BUILD.gn ++++ b/core/fpdfapi/edit/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -18,7 +18,10 @@ source_set("edit") { + "cpdf_stringarchivestream.cpp", + "cpdf_stringarchivestream.h", + ] +- configs += [ "../../../:pdfium_core_config" ] ++ configs += [ ++ "../../../:pdfium_strict_config", ++ "../../../:pdfium_noshorten_config", ++ ] + deps = [ + "../../../constants", + "../../../third_party:skia_shared", +@@ -37,7 +40,9 @@ pdfium_unittest_source_set("unittests") { + "../../fxge", + "../font", + "../page", ++ "../page:unit_test_support", + "../parser", ++ "../parser:unit_test_support", + "../render", + ] + pdfium_root_dir = "../../../" +diff --git a/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp b/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp +index 28165b1ad..af6a16931 100644 +--- a/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp ++++ b/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp +@@ -1,9 +1,11 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h" + ++#include ++ + #include "third_party/skia_shared/SkFloatToDecimal.h" + + std::ostream& WriteFloat(std::ostream& stream, float value) { +@@ -13,18 +15,26 @@ std::ostream& WriteFloat(std::ostream& stream, float value) { + return stream; + } + +-std::ostream& operator<<(std::ostream& ar, const CFX_Matrix& matrix) { +- WriteFloat(ar, matrix.a) << " "; +- WriteFloat(ar, matrix.b) << " "; +- WriteFloat(ar, matrix.c) << " "; +- WriteFloat(ar, matrix.d) << " "; +- WriteFloat(ar, matrix.e) << " "; +- WriteFloat(ar, matrix.f); +- return ar; ++std::ostream& WriteMatrix(std::ostream& stream, const CFX_Matrix& matrix) { ++ WriteFloat(stream, matrix.a) << " "; ++ WriteFloat(stream, matrix.b) << " "; ++ WriteFloat(stream, matrix.c) << " "; ++ WriteFloat(stream, matrix.d) << " "; ++ WriteFloat(stream, matrix.e) << " "; ++ WriteFloat(stream, matrix.f); ++ return stream; ++} ++ ++std::ostream& WritePoint(std::ostream& stream, const CFX_PointF& point) { ++ WriteFloat(stream, point.x) << " "; ++ WriteFloat(stream, point.y); ++ return stream; + } + +-std::ostream& operator<<(std::ostream& ar, const CFX_PointF& point) { +- WriteFloat(ar, point.x) << " "; +- WriteFloat(ar, point.y); +- return ar; ++std::ostream& WriteRect(std::ostream& stream, const CFX_FloatRect& rect) { ++ WriteFloat(stream, rect.left) << " "; ++ WriteFloat(stream, rect.bottom) << " "; ++ WriteFloat(stream, rect.Width()) << " "; ++ WriteFloat(stream, rect.Height()); ++ return stream; + } +diff --git a/core/fpdfapi/edit/cpdf_contentstream_write_utils.h b/core/fpdfapi/edit/cpdf_contentstream_write_utils.h +index 3e14c9ff7..2440d9218 100644 +--- a/core/fpdfapi/edit/cpdf_contentstream_write_utils.h ++++ b/core/fpdfapi/edit/cpdf_contentstream_write_utils.h +@@ -1,16 +1,17 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #ifndef CORE_FPDFAPI_EDIT_CPDF_CONTENTSTREAM_WRITE_UTILS_H_ + #define CORE_FPDFAPI_EDIT_CPDF_CONTENTSTREAM_WRITE_UTILS_H_ + +-#include ++#include + + #include "core/fxcrt/fx_coordinates.h" + + std::ostream& WriteFloat(std::ostream& stream, float value); +-std::ostream& operator<<(std::ostream& ar, const CFX_Matrix& matrix); +-std::ostream& operator<<(std::ostream& ar, const CFX_PointF& point); ++std::ostream& WriteMatrix(std::ostream& stream, const CFX_Matrix& matrix); ++std::ostream& WritePoint(std::ostream& stream, const CFX_PointF& point); ++std::ostream& WriteRect(std::ostream& stream, const CFX_FloatRect& rect); + + #endif // CORE_FPDFAPI_EDIT_CPDF_CONTENTSTREAM_WRITE_UTILS_H_ +diff --git a/core/fpdfapi/edit/cpdf_creator.cpp b/core/fpdfapi/edit/cpdf_creator.cpp +index 65ddc45fa..9a2b88b98 100644 +--- a/core/fpdfapi/edit/cpdf_creator.cpp ++++ b/core/fpdfapi/edit/cpdf_creator.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,11 @@ + + #include "core/fpdfapi/edit/cpdf_creator.h" + ++#include ++ + #include ++#include ++#include + + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_crypto_handler.h" +@@ -18,14 +22,16 @@ + #include "core/fpdfapi/parser/cpdf_parser.h" + #include "core/fpdfapi/parser/cpdf_security_handler.h" + #include "core/fpdfapi/parser/cpdf_string.h" +-#include "core/fpdfapi/parser/cpdf_syntax_parser.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" ++#include "core/fpdfapi/parser/object_tree_traversal_util.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_extension.h" +-#include "core/fxcrt/fx_memory_wrappers.h" + #include "core/fxcrt/fx_random.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxcrt/span_util.h" ++#include "core/fxcrt/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" + + namespace { + +@@ -33,33 +39,27 @@ const size_t kArchiveBufferSize = 32768; + + class CFX_FileBufferArchive final : public IFX_ArchiveStream { + public: +- explicit CFX_FileBufferArchive( +- const RetainPtr& file); ++ explicit CFX_FileBufferArchive(RetainPtr file); + ~CFX_FileBufferArchive() override; + +- bool WriteBlock(const void* pBuf, size_t size) override; +- bool WriteByte(uint8_t byte) override; +- bool WriteDWord(uint32_t i) override; +- bool WriteString(ByteStringView str) override; +- ++ bool WriteBlock(pdfium::span buffer) override; + FX_FILESIZE CurrentOffset() const override { return offset_; } + + private: + bool Flush(); + +- FX_FILESIZE offset_; +- size_t current_length_; +- std::vector> buffer_; +- RetainPtr backing_file_; ++ FX_FILESIZE offset_ = 0; ++ DataVector buffer_; ++ pdfium::span available_; ++ RetainPtr const backing_file_; + }; + + CFX_FileBufferArchive::CFX_FileBufferArchive( +- const RetainPtr& file) +- : offset_(0), +- current_length_(0), +- buffer_(kArchiveBufferSize), +- backing_file_(file) { +- ASSERT(file); ++ RetainPtr file) ++ : buffer_(kArchiveBufferSize), ++ available_(buffer_), ++ backing_file_(std::move(file)) { ++ DCHECK(backing_file_); + } + + CFX_FileBufferArchive::~CFX_FileBufferArchive() { +@@ -67,35 +67,29 @@ CFX_FileBufferArchive::~CFX_FileBufferArchive() { + } + + bool CFX_FileBufferArchive::Flush() { +- size_t nRemaining = current_length_; +- current_length_ = 0; +- if (!backing_file_) +- return false; +- if (!nRemaining) ++ size_t nUsed = buffer_.size() - available_.size(); ++ available_ = pdfium::make_span(buffer_); ++ if (!nUsed) + return true; +- return backing_file_->WriteBlock(buffer_.data(), nRemaining); ++ return backing_file_->WriteBlock(available_.first(nUsed)); + } + +-bool CFX_FileBufferArchive::WriteBlock(const void* pBuf, size_t size) { +- ASSERT(pBuf); +- ASSERT(size > 0); +- +- const uint8_t* buffer = reinterpret_cast(pBuf); +- size_t temp_size = size; +- while (temp_size) { +- size_t buf_size = std::min(kArchiveBufferSize - current_length_, temp_size); +- memcpy(buffer_.data() + current_length_, buffer, buf_size); ++bool CFX_FileBufferArchive::WriteBlock(pdfium::span buffer) { ++ if (buffer.empty()) ++ return true; + +- current_length_ += buf_size; +- if (current_length_ == kArchiveBufferSize && !Flush()) ++ pdfium::span src_span = buffer; ++ while (!src_span.empty()) { ++ size_t copy_size = std::min(available_.size(), src_span.size()); ++ fxcrt::spancpy(available_, src_span.first(copy_size)); ++ src_span = src_span.subspan(copy_size); ++ available_ = available_.subspan(copy_size); ++ if (available_.empty() && !Flush()) + return false; +- +- temp_size -= buf_size; +- buffer += buf_size; + } + + FX_SAFE_FILESIZE safe_offset = offset_; +- safe_offset += size; ++ safe_offset += buffer.size(); + if (!safe_offset.IsValid()) + return false; + +@@ -103,20 +97,6 @@ bool CFX_FileBufferArchive::WriteBlock(const void* pBuf, size_t size) { + return true; + } + +-bool CFX_FileBufferArchive::WriteByte(uint8_t byte) { +- return WriteBlock(&byte, 1); +-} +- +-bool CFX_FileBufferArchive::WriteDWord(uint32_t i) { +- char buf[32]; +- FXSYS_itoa(i, buf, 10); +- return WriteBlock(buf, strlen(buf)); +-} +- +-bool CFX_FileBufferArchive::WriteString(ByteStringView str) { +- return WriteBlock(str.raw_str(), str.GetLength()); +-} +- + ByteString GenerateFileID(uint32_t dwSeed1, uint32_t dwSeed2) { + uint32_t buffer[4]; + void* pContext1 = FX_Random_MT_Start(dwSeed1); +@@ -141,15 +121,15 @@ bool OutputIndex(IFX_ArchiveStream* archive, FX_FILESIZE offset) { + } // namespace + + CPDF_Creator::CPDF_Creator(CPDF_Document* pDoc, +- const RetainPtr& archive) ++ RetainPtr archive) + : m_pDocument(pDoc), + m_pParser(pDoc->GetParser()), + m_pEncryptDict(m_pParser ? m_pParser->GetEncryptDict() : nullptr), + m_pSecurityHandler(m_pParser ? m_pParser->GetSecurityHandler() : nullptr), + m_dwLastObjNum(m_pDocument->GetLastObjNum()), +- m_Archive(pdfium::MakeUnique(archive)) {} ++ m_Archive(std::make_unique(std::move(archive))) {} + +-CPDF_Creator::~CPDF_Creator() {} ++CPDF_Creator::~CPDF_Creator() = default; + + bool CPDF_Creator::WriteIndirectObj(uint32_t objnum, const CPDF_Object* pObj) { + if (!m_Archive->WriteDWord(objnum) || !m_Archive->WriteString(" 0 obj\r\n")) +@@ -157,7 +137,7 @@ bool CPDF_Creator::WriteIndirectObj(uint32_t objnum, const CPDF_Object* pObj) { + + std::unique_ptr encryptor; + if (GetCryptoHandler() && pObj != m_pEncryptDict) +- encryptor = pdfium::MakeUnique(GetCryptoHandler(), objnum); ++ encryptor = std::make_unique(GetCryptoHandler(), objnum); + + if (!pObj->WriteTo(m_Archive.get(), encryptor.get())) + return false; +@@ -172,12 +152,12 @@ bool CPDF_Creator::WriteOldIndirectObject(uint32_t objnum) { + m_ObjectOffsets[objnum] = m_Archive->CurrentOffset(); + + bool bExistInMap = !!m_pDocument->GetIndirectObject(objnum); +- CPDF_Object* pObj = m_pDocument->GetOrParseIndirectObject(objnum); ++ RetainPtr pObj = m_pDocument->GetOrParseIndirectObject(objnum); + if (!pObj) { + m_ObjectOffsets.erase(objnum); + return true; + } +- if (!WriteIndirectObj(pObj->GetObjNum(), pObj)) ++ if (!WriteIndirectObj(pObj->GetObjNum(), pObj.Get())) + return false; + if (!bExistInMap) + m_pDocument->DeleteIndirectObject(objnum); +@@ -185,13 +165,30 @@ bool CPDF_Creator::WriteOldIndirectObject(uint32_t objnum) { + } + + bool CPDF_Creator::WriteOldObjs() { +- uint32_t nLastObjNum = m_pParser->GetLastObjNum(); +- if (!m_pParser->IsValidObjectNumber(nLastObjNum)) ++ const uint32_t nLastObjNum = m_pParser->GetLastObjNum(); ++ if (!m_pParser->IsValidObjectNumber(nLastObjNum)) { + return true; ++ } ++ if (m_CurObjNum > nLastObjNum) { ++ return true; ++ } + ++ const std::set objects_with_refs = ++ GetObjectsWithReferences(m_pDocument); ++ uint32_t last_object_number_written = 0; + for (uint32_t objnum = m_CurObjNum; objnum <= nLastObjNum; ++objnum) { +- if (!WriteOldIndirectObject(objnum)) ++ if (!pdfium::Contains(objects_with_refs, objnum)) { ++ continue; ++ } ++ if (!WriteOldIndirectObject(objnum)) { + return false; ++ } ++ last_object_number_written = objnum; ++ } ++ // If there are no new objects to write, then adjust `m_dwLastObjNum` if ++ // needed to reflect the actual last object number. ++ if (m_NewObjNumArray.empty()) { ++ m_dwLastObjNum = last_object_number_written; + } + return true; + } +@@ -199,12 +196,12 @@ bool CPDF_Creator::WriteOldObjs() { + bool CPDF_Creator::WriteNewObjs() { + for (size_t i = m_CurObjNum; i < m_NewObjNumArray.size(); ++i) { + uint32_t objnum = m_NewObjNumArray[i]; +- CPDF_Object* pObj = m_pDocument->GetIndirectObject(objnum); ++ RetainPtr pObj = m_pDocument->GetIndirectObject(objnum); + if (!pObj) + continue; + + m_ObjectOffsets[objnum] = m_Archive->CurrentOffset(); +- if (!WriteIndirectObj(pObj->GetObjNum(), pObj)) ++ if (!WriteIndirectObj(pObj->GetObjNum(), pObj.Get())) + return false; + } + return true; +@@ -228,13 +225,11 @@ void CPDF_Creator::InitNewObjNumOffsets() { + } + + CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage1() { +- ASSERT(m_iStage > Stage::kInvalid || m_iStage < Stage::kInitWriteObjs20); ++ DCHECK(m_iStage > Stage::kInvalid || m_iStage < Stage::kInitWriteObjs20); + if (m_iStage == Stage::kInit0) { + if (!m_pParser || (m_bSecurityChanged && m_IsOriginal)) + m_IsIncremental = false; + +- const CPDF_Dictionary* pDict = m_pDocument->GetRoot(); +- m_pMetadata.Reset(pDict ? pDict->GetDirectObjectFor("Metadata") : nullptr); + m_iStage = Stage::kWriteHeader10; + } + if (m_iStage == Stage::kWriteHeader10) { +@@ -254,26 +249,14 @@ CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage1() { + } + m_iStage = Stage::kInitWriteObjs20; + } else { +- m_SavedOffset = m_pParser->GetSyntax()->GetDocumentSize(); ++ m_SavedOffset = m_pParser->GetDocumentSize(); + m_iStage = Stage::kWriteIncremental15; + } + } + if (m_iStage == Stage::kWriteIncremental15) { + if (m_IsOriginal && m_SavedOffset > 0) { +- static constexpr FX_FILESIZE kBufferSize = 4096; +- std::vector> buffer(kBufferSize); +- FX_FILESIZE src_size = m_SavedOffset; +- m_pParser->GetSyntax()->SetPos(0); +- while (src_size) { +- const FX_FILESIZE block_size = std::min(kBufferSize, src_size); +- if (!m_pParser->GetSyntax()->ReadBlock(buffer.data(), block_size)) { +- return Stage::kInvalid; +- } +- if (!m_Archive->WriteBlock(buffer.data(), block_size)) +- return Stage::kInvalid; +- +- src_size -= block_size; +- } ++ if (!m_pParser->WriteToArchive(m_Archive.get(), m_SavedOffset)) ++ return Stage::kInvalid; + } + if (m_IsOriginal && m_pParser->GetLastXRefOffset() == 0) { + for (uint32_t num = 0; num <= m_pParser->GetLastObjNum(); ++num) { +@@ -290,7 +273,7 @@ CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage1() { + } + + CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage2() { +- ASSERT(m_iStage >= Stage::kInitWriteObjs20 || ++ DCHECK(m_iStage >= Stage::kInitWriteObjs20 || + m_iStage < Stage::kInitWriteXRefs80); + if (m_iStage == Stage::kInitWriteObjs20) { + if (!m_IsIncremental && m_pParser) { +@@ -333,7 +316,7 @@ CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage2() { + } + + CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage3() { +- ASSERT(m_iStage >= Stage::kInitWriteXRefs80 || ++ DCHECK(m_iStage >= Stage::kInitWriteXRefs80 || + m_iStage < Stage::kWriteTrailerAndFinish90); + + uint32_t dwLastObjNum = m_dwLastObjNum; +@@ -342,7 +325,7 @@ CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage3() { + if (!m_IsIncremental || !m_pParser->IsXRefStream()) { + if (!m_IsIncremental || m_pParser->GetLastXRefOffset() == 0) { + ByteString str; +- str = pdfium::ContainsKey(m_ObjectOffsets, 1) ++ str = pdfium::Contains(m_ObjectOffsets, 1) + ? "xref\r\n" + : "xref\r\n0 1\r\n0000000000 65535 f\r\n"; + if (!m_Archive->WriteString(str.AsStringView())) +@@ -366,14 +349,14 @@ CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage3() { + uint32_t i = m_CurObjNum; + uint32_t j; + while (i <= dwLastObjNum) { +- while (i <= dwLastObjNum && !pdfium::ContainsKey(m_ObjectOffsets, i)) ++ while (i <= dwLastObjNum && !pdfium::Contains(m_ObjectOffsets, i)) + i++; + + if (i > dwLastObjNum) + break; + + j = i; +- while (j <= dwLastObjNum && pdfium::ContainsKey(m_ObjectOffsets, j)) ++ while (j <= dwLastObjNum && pdfium::Contains(m_ObjectOffsets, j)) + j++; + + if (i == 1) +@@ -396,7 +379,7 @@ CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage3() { + } + if (m_iStage == Stage::kWriteXrefsIncremental82) { + ByteString str; +- uint32_t iCount = pdfium::CollectionSize(m_NewObjNumArray); ++ uint32_t iCount = fxcrt::CollectionSize(m_NewObjNumArray); + uint32_t i = m_CurObjNum; + while (i < iCount) { + size_t j = i; +@@ -431,7 +414,7 @@ CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage3() { + } + + CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage4() { +- ASSERT(m_iStage >= Stage::kWriteTrailerAndFinish90); ++ DCHECK(m_iStage >= Stage::kWriteTrailerAndFinish90); + + bool bXRefStream = m_IsIncremental && m_pParser->IsXRefStream(); + if (!bXRefStream) { +@@ -445,11 +428,10 @@ CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage4() { + } + + if (m_pParser) { +- RetainPtr p = m_pParser->GetCombinedTrailer(); +- CPDF_DictionaryLocker locker(p.Get()); ++ CPDF_DictionaryLocker locker(m_pParser->GetCombinedTrailer()); + for (const auto& it : locker) { + const ByteString& key = it.first; +- CPDF_Object* pValue = it.second.Get(); ++ const RetainPtr& pValue = it.second; + if (key == "Encrypt" || key == "Size" || key == "Filter" || + key == "Index" || key == "Length" || key == "Prev" || key == "W" || + key == "XRefStm" || key == "ID" || key == "DecodeParms" || +@@ -497,13 +479,7 @@ CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage4() { + if (m_IsIncremental) { + FX_FILESIZE prev = m_pParser->GetLastXRefOffset(); + if (prev) { +- if (!m_Archive->WriteString("/Prev ")) +- return Stage::kInvalid; +- +- char offset_buf[20]; +- memset(offset_buf, 0, sizeof(offset_buf)); +- FXSYS_i64toa(prev, offset_buf, 10); +- if (!m_Archive->WriteBlock(offset_buf, strlen(offset_buf))) ++ if (!m_Archive->WriteString("/Prev ") || !m_Archive->WriteFilesize(prev)) + return Stage::kInvalid; + } + } +@@ -522,7 +498,7 @@ CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage4() { + if (m_IsIncremental && m_pParser && m_pParser->GetLastXRefOffset() == 0) { + uint32_t i = 0; + for (i = 0; i < m_dwLastObjNum; i++) { +- if (!pdfium::ContainsKey(m_ObjectOffsets, i)) ++ if (!pdfium::Contains(m_ObjectOffsets, i)) + continue; + if (!m_Archive->WriteDWord(i) || !m_Archive->WriteString(" 1 ")) + return Stage::kInvalid; +@@ -540,8 +516,8 @@ CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage4() { + return Stage::kInvalid; + } + } else { +- size_t count = m_NewObjNumArray.size(); +- size_t i = 0; ++ int count = fxcrt::CollectionSize(m_NewObjNumArray); ++ int i = 0; + for (i = 0; i < count; i++) { + if (!m_Archive->WriteDWord(m_NewObjNumArray[i]) || + !m_Archive->WriteString(" 1 ")) { +@@ -562,13 +538,8 @@ CPDF_Creator::Stage CPDF_Creator::WriteDoc_Stage4() { + return Stage::kInvalid; + } + +- if (!m_Archive->WriteString("\r\nstartxref\r\n")) +- return Stage::kInvalid; +- +- char offset_buf[20]; +- memset(offset_buf, 0, sizeof(offset_buf)); +- FXSYS_i64toa(m_XrefStart, offset_buf, 10); +- if (!m_Archive->WriteBlock(offset_buf, strlen(offset_buf)) || ++ if (!m_Archive->WriteString("\r\nstartxref\r\n") || ++ !m_Archive->WriteFilesize(m_XrefStart) || + !m_Archive->WriteString("\r\n%%EOF\r\n")) { + return Stage::kInvalid; + } +@@ -591,37 +562,39 @@ bool CPDF_Creator::Create(uint32_t flags) { + } + + void CPDF_Creator::InitID() { +- ASSERT(!m_pIDArray); ++ DCHECK(!m_pIDArray); + + m_pIDArray = pdfium::MakeRetain(); +- const CPDF_Array* pOldIDArray = m_pParser ? m_pParser->GetIDArray() : nullptr; +- const CPDF_Object* pID1 = pOldIDArray ? pOldIDArray->GetObjectAt(0) : nullptr; ++ RetainPtr pOldIDArray = ++ m_pParser ? m_pParser->GetIDArray() : nullptr; ++ RetainPtr pID1 = ++ pOldIDArray ? pOldIDArray->GetObjectAt(0) : nullptr; + if (pID1) { +- m_pIDArray->Add(pID1->Clone()); ++ m_pIDArray->Append(pID1->Clone()); + } else { + ByteString bsBuffer = + GenerateFileID((uint32_t)(uintptr_t)this, m_dwLastObjNum); +- m_pIDArray->AddNew(bsBuffer, true); ++ m_pIDArray->AppendNew(bsBuffer, true); + } + + if (pOldIDArray) { +- const CPDF_Object* pID2 = pOldIDArray->GetObjectAt(1); ++ RetainPtr pID2 = pOldIDArray->GetObjectAt(1); + if (m_IsIncremental && m_pEncryptDict && pID2) { +- m_pIDArray->Add(pID2->Clone()); ++ m_pIDArray->Append(pID2->Clone()); + return; + } + ByteString bsBuffer = + GenerateFileID((uint32_t)(uintptr_t)this, m_dwLastObjNum); +- m_pIDArray->AddNew(bsBuffer, true); ++ m_pIDArray->AppendNew(bsBuffer, true); + return; + } + +- m_pIDArray->Add(m_pIDArray->GetObjectAt(0)->Clone()); ++ m_pIDArray->Append(m_pIDArray->GetObjectAt(0)->Clone()); + if (m_pEncryptDict) { +- ASSERT(m_pParser); ++ DCHECK(m_pParser); + int revision = m_pEncryptDict->GetIntegerFor("R"); + if ((revision == 2 || revision == 3) && +- m_pEncryptDict->GetStringFor("Filter") == "Standard") { ++ m_pEncryptDict->GetByteStringFor("Filter") == "Standard") { + m_pNewEncryptDict = ToDictionary(m_pEncryptDict->Clone()); + m_pEncryptDict = m_pNewEncryptDict; + m_pSecurityHandler = pdfium::MakeRetain(); +diff --git a/core/fpdfapi/edit/cpdf_creator.h b/core/fpdfapi/edit/cpdf_creator.h +index d85d8cf98..b97dc2961 100644 +--- a/core/fpdfapi/edit/cpdf_creator.h ++++ b/core/fpdfapi/edit/cpdf_creator.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -29,7 +29,7 @@ class CPDF_Parser; + class CPDF_Creator { + public: + CPDF_Creator(CPDF_Document* pDoc, +- const RetainPtr& archive); ++ RetainPtr archive); + ~CPDF_Creator(); + + void RemoveSecurity(); +@@ -73,11 +73,10 @@ class CPDF_Creator { + CPDF_CryptoHandler* GetCryptoHandler(); + + UnownedPtr const m_pDocument; +- UnownedPtr const m_pParser; ++ UnownedPtr const m_pParser; + RetainPtr m_pEncryptDict; + RetainPtr m_pNewEncryptDict; + RetainPtr m_pSecurityHandler; +- RetainPtr m_pMetadata; + uint32_t m_dwLastObjNum; + std::unique_ptr m_Archive; + FX_FILESIZE m_SavedOffset = 0; +diff --git a/core/fpdfapi/edit/cpdf_creator_embeddertest.cpp b/core/fpdfapi/edit/cpdf_creator_embeddertest.cpp +index 9d849c16a..8dbad0c63 100644 +--- a/core/fpdfapi/edit/cpdf_creator_embeddertest.cpp ++++ b/core/fpdfapi/edit/cpdf_creator_embeddertest.cpp +@@ -1,13 +1,11 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include +-#include ++#include ++ + #include +-#include + +-#include "core/fxcrt/fx_system.h" + #include "public/cpp/fpdf_scopers.h" + #include "public/fpdf_annot.h" + #include "public/fpdf_edit.h" +@@ -48,7 +46,7 @@ TEST_F(CPDF_CreatorEmbedderTest, SavedDocsAreEqualAfterParse) { + } + + TEST_F(CPDF_CreatorEmbedderTest, BUG_873) { +- EXPECT_TRUE(OpenDocument("embedded_attachments.pdf")); ++ ASSERT_TRUE(OpenDocument("embedded_attachments.pdf")); + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + + // Cannot match second part of the ID since it is randomly generated. +@@ -69,24 +67,24 @@ TEST_F(CPDF_CreatorEmbedderTest, SaveLinearizedInfo) { + FileAccessForTesting file_acc("linearized.pdf"); + FakeFileAccess fake_acc(&file_acc); + +- avail_ = FPDFAvail_Create(fake_acc.GetFileAvail(), fake_acc.GetFileAccess()); ++ CreateAvail(fake_acc.GetFileAvail(), fake_acc.GetFileAccess()); + while (PDF_DATA_AVAIL != +- FPDFAvail_IsDocAvail(avail_, fake_acc.GetDownloadHints())) { ++ FPDFAvail_IsDocAvail(avail(), fake_acc.GetDownloadHints())) { + fake_acc.SetRequestedDataAvailable(); + } + +- document_ = FPDFAvail_GetDocument(avail_, nullptr); +- ASSERT_TRUE(document_); ++ SetDocumentFromAvail(); ++ ASSERT_TRUE(document()); + + // Load second page, to parse additional crossref sections. + while (PDF_DATA_AVAIL != +- FPDFAvail_IsPageAvail(avail_, 1, fake_acc.GetDownloadHints())) { ++ FPDFAvail_IsPageAvail(avail(), 1, fake_acc.GetDownloadHints())) { + fake_acc.SetRequestedDataAvailable(); + } + // Simulate downloading of whole file. + fake_acc.SetWholeFileAvailable(); + // Save document. +- EXPECT_TRUE(FPDF_SaveAsCopy(document_, this, 0)); ++ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + const std::string saved_doc = GetString(); + + EXPECT_THAT(saved_doc, ::testing::HasSubstr("/Info")); +diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp +index 77d23a201..871702843 100644 +--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp ++++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,9 +9,11 @@ + #include + #include + #include ++#include + #include + #include + ++#include "constants/page_object.h" + #include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h" + #include "core/fpdfapi/edit/cpdf_pagecontentmanager.h" + #include "core/fpdfapi/edit/cpdf_stringarchivestream.h" +@@ -19,6 +21,8 @@ + #include "core/fpdfapi/font/cpdf_type1font.h" + #include "core/fpdfapi/page/cpdf_contentmarks.h" + #include "core/fpdfapi/page/cpdf_docpagedata.h" ++#include "core/fpdfapi/page/cpdf_form.h" ++#include "core/fpdfapi/page/cpdf_formobject.h" + #include "core/fpdfapi/page/cpdf_image.h" + #include "core/fpdfapi/page/cpdf_imageobject.h" + #include "core/fpdfapi/page/cpdf_page.h" +@@ -34,11 +38,19 @@ + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "core/fpdfapi/parser/object_tree_traversal_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/notreached.h" ++#include "third_party/base/numerics/safe_conversions.h" ++#include "third_party/base/span.h" + + namespace { + ++// Key: The resource type. ++// Value: The resource names of a given type. ++using ResourcesMap = std::map>; ++ + bool GetColor(const CPDF_Color* pColor, float* rgb) { + int intRGB[3]; + if (!pColor || !pColor->IsColorSpaceRGB() || +@@ -51,6 +63,68 @@ bool GetColor(const CPDF_Color* pColor, float* rgb) { + return true; + } + ++void RecordPageObjectResourceUsage(const CPDF_PageObject* page_object, ++ ResourcesMap& seen_resources) { ++ const ByteString& resource_name = page_object->GetResourceName(); ++ if (!resource_name.IsEmpty()) { ++ switch (page_object->GetType()) { ++ case CPDF_PageObject::Type::kText: ++ seen_resources["Font"].insert(resource_name); ++ break; ++ case CPDF_PageObject::Type::kImage: ++ case CPDF_PageObject::Type::kForm: ++ seen_resources["XObject"].insert(resource_name); ++ break; ++ case CPDF_PageObject::Type::kPath: ++ break; ++ case CPDF_PageObject::Type::kShading: ++ break; ++ } ++ } ++ const ByteString& graphics_resource_name = ++ page_object->GetGraphicsResourceName(); ++ if (!graphics_resource_name.IsEmpty()) { ++ seen_resources["ExtGState"].insert(graphics_resource_name); ++ } ++} ++ ++void RemoveUnusedResources(RetainPtr resources_dict, ++ const ResourcesMap& resources_in_use) { ++ // TODO(thestig): Remove other unused resource types: ++ // - ColorSpace ++ // - Pattern ++ // - Shading ++ static constexpr const char* kResourceKeys[] = {"ExtGState", "Font", ++ "XObject"}; ++ for (const char* resource_key : kResourceKeys) { ++ RetainPtr resource_dict = ++ resources_dict->GetMutableDictFor(resource_key); ++ if (!resource_dict) { ++ continue; ++ } ++ ++ std::vector keys; ++ { ++ CPDF_DictionaryLocker resource_dict_locker(resource_dict); ++ for (auto& it : resource_dict_locker) { ++ keys.push_back(it.first); ++ } ++ } ++ ++ auto it = resources_in_use.find(resource_key); ++ const std::set* resource_in_use_of_current_type = ++ it != resources_in_use.end() ? &it->second : nullptr; ++ for (const ByteString& key : keys) { ++ if (resource_in_use_of_current_type && ++ pdfium::Contains(*resource_in_use_of_current_type, key)) { ++ continue; ++ } ++ ++ resource_dict->RemoveFor(key.AsStringView()); ++ } ++ } ++} ++ + } // namespace + + CPDF_PageContentGenerator::CPDF_PageContentGenerator( +@@ -62,22 +136,23 @@ CPDF_PageContentGenerator::CPDF_PageContentGenerator( + } + } + +-CPDF_PageContentGenerator::~CPDF_PageContentGenerator() {} ++CPDF_PageContentGenerator::~CPDF_PageContentGenerator() = default; + + void CPDF_PageContentGenerator::GenerateContent() { +- ASSERT(m_pObjHolder->IsPage()); +- +- std::map> stream = ++ DCHECK(m_pObjHolder->IsPage()); ++ std::map new_stream_data = + GenerateModifiedStreams(); ++ // If no streams were regenerated or removed, nothing to do here. ++ if (new_stream_data.empty()) { ++ return; ++ } + +- UpdateContentStreams(&stream); ++ UpdateContentStreams(std::move(new_stream_data)); ++ UpdateResourcesDict(); + } + +-std::map> ++std::map + CPDF_PageContentGenerator::GenerateModifiedStreams() { +- // Make sure default graphics are created. +- GetOrCreateDefaultGraphics(); +- + // Figure out which streams are dirty. + std::set all_dirty_streams; + for (auto& pPageObj : m_pageObjects) { +@@ -89,23 +164,21 @@ CPDF_PageContentGenerator::GenerateModifiedStreams() { + marked_dirty_streams.end()); + + // Start regenerating dirty streams. +- std::map> streams; ++ std::map streams; + std::set empty_streams; + std::unique_ptr empty_content_marks = +- pdfium::MakeUnique(); ++ std::make_unique(); + std::map current_content_marks; + + for (int32_t dirty_stream : all_dirty_streams) { +- std::unique_ptr buf = +- pdfium::MakeUnique(); ++ fxcrt::ostringstream buf; + + // Set the default graphic state values +- *buf << "q\n"; ++ buf << "q\n"; + if (!m_pObjHolder->GetLastCTM().IsIdentity()) +- *buf << m_pObjHolder->GetLastCTM().GetInverse() << " cm\n"; +- +- ProcessDefaultGraphics(buf.get()); ++ WriteMatrix(buf, m_pObjHolder->GetLastCTM().GetInverse()) << " cm\n"; + ++ ProcessDefaultGraphics(&buf); + streams[dirty_stream] = std::move(buf); + empty_streams.insert(dirty_stream); + current_content_marks[dirty_stream] = empty_content_marks.get(); +@@ -118,17 +191,17 @@ CPDF_PageContentGenerator::GenerateModifiedStreams() { + if (it == streams.end()) + continue; + +- std::ostringstream* buf = it->second.get(); ++ fxcrt::ostringstream* buf = &it->second; + empty_streams.erase(stream_index); +- current_content_marks[stream_index] = ProcessContentMarks( +- buf, pPageObj.Get(), current_content_marks[stream_index]); +- ProcessPageObject(buf, pPageObj.Get()); ++ current_content_marks[stream_index] = ++ ProcessContentMarks(buf, pPageObj, current_content_marks[stream_index]); ++ ProcessPageObject(buf, pPageObj); + } + + // Finish dirty streams. + for (int32_t dirty_stream : all_dirty_streams) { +- std::ostringstream* buf = streams[dirty_stream].get(); +- if (pdfium::ContainsKey(empty_streams, dirty_stream)) { ++ fxcrt::ostringstream* buf = &streams[dirty_stream]; ++ if (pdfium::Contains(empty_streams, dirty_stream)) { + // Clear to show that this stream needs to be deleted. + buf->str(""); + } else { +@@ -143,70 +216,91 @@ CPDF_PageContentGenerator::GenerateModifiedStreams() { + } + + void CPDF_PageContentGenerator::UpdateContentStreams( +- std::map>* new_stream_data) { +- // If no streams were regenerated or removed, nothing to do here. +- if (new_stream_data->empty()) +- return; ++ std::map&& new_stream_data) { ++ CHECK(!new_stream_data.empty()); + +- CPDF_PageContentManager page_content_manager(m_pObjHolder.Get()); ++ // Make sure default graphics are created. ++ m_DefaultGraphicsName = GetOrCreateDefaultGraphics(); + +- for (auto& pair : *new_stream_data) { ++ CPDF_PageContentManager page_content_manager(m_pObjHolder, m_pDocument); ++ for (auto& pair : new_stream_data) { + int32_t stream_index = pair.first; +- std::ostringstream* buf = pair.second.get(); ++ fxcrt::ostringstream* buf = &pair.second; + + if (stream_index == CPDF_PageObject::kNoContentStream) { +- int new_stream_index = page_content_manager.AddStream(buf); ++ int new_stream_index = ++ pdfium::base::checked_cast(page_content_manager.AddStream(buf)); + UpdateStreamlessPageObjects(new_stream_index); + continue; + } + +- CPDF_Stream* old_stream = +- page_content_manager.GetStreamByIndex(stream_index); +- ASSERT(old_stream); ++ page_content_manager.UpdateStream(stream_index, buf); ++ } ++} ++ ++void CPDF_PageContentGenerator::UpdateResourcesDict() { ++ RetainPtr resources = m_pObjHolder->GetMutableResources(); ++ if (!resources) { ++ return; ++ } ++ ++ const uint32_t resources_object_number = resources->GetObjNum(); ++ if (resources_object_number) { ++ // If `resources` is not an inline object, then do not modify it directly if ++ // it has multiple references. ++ if (pdfium::Contains(GetObjectsWithMultipleReferences(m_pDocument), ++ resources_object_number)) { ++ resources = pdfium::WrapRetain(resources->Clone()->AsMutableDictionary()); ++ const uint32_t clone_object_number = ++ m_pDocument->AddIndirectObject(resources); ++ m_pObjHolder->SetResources(resources); ++ m_pObjHolder->GetMutableDict()->SetNewFor( ++ pdfium::page_object::kResources, m_pDocument, clone_object_number); ++ } ++ } + +- // If buf is now empty, remove the stream instead of setting the data. +- if (buf->tellp() <= 0) +- page_content_manager.ScheduleRemoveStreamByIndex(stream_index); +- else +- old_stream->SetDataFromStringstreamAndRemoveFilter(buf); ++ ResourcesMap seen_resources; ++ for (auto& page_object : m_pageObjects) { ++ RecordPageObjectResourceUsage(page_object, seen_resources); ++ } ++ if (!m_DefaultGraphicsName.IsEmpty()) { ++ seen_resources["ExtGState"].insert(m_DefaultGraphicsName); + } + +- page_content_manager.ExecuteScheduledRemovals(); ++ RemoveUnusedResources(std::move(resources), seen_resources); + } + + ByteString CPDF_PageContentGenerator::RealizeResource( + const CPDF_Object* pResource, + const ByteString& bsType) const { +- ASSERT(pResource); +- if (!m_pObjHolder->m_pResources) { +- m_pObjHolder->m_pResources.Reset( +- m_pDocument->NewIndirect()); +- m_pObjHolder->GetDict()->SetNewFor( +- "Resources", m_pDocument.Get(), +- m_pObjHolder->m_pResources->GetObjNum()); +- } +- CPDF_Dictionary* pResList = m_pObjHolder->m_pResources->GetDictFor(bsType); +- if (!pResList) +- pResList = m_pObjHolder->m_pResources->SetNewFor(bsType); ++ DCHECK(pResource); ++ if (!m_pObjHolder->GetResources()) { ++ m_pObjHolder->SetResources(m_pDocument->NewIndirect()); ++ m_pObjHolder->GetMutableDict()->SetNewFor( ++ pdfium::page_object::kResources, m_pDocument, ++ m_pObjHolder->GetResources()->GetObjNum()); ++ } + ++ RetainPtr pResList = ++ m_pObjHolder->GetMutableResources()->GetOrCreateDictFor(bsType); + ByteString name; + int idnum = 1; +- while (1) { ++ while (true) { + name = ByteString::Format("FX%c%d", bsType[0], idnum); + if (!pResList->KeyExist(name)) + break; + + idnum++; + } +- pResList->SetNewFor(name, m_pDocument.Get(), ++ pResList->SetNewFor(name, m_pDocument, + pResource->GetObjNum()); + return name; + } + +-bool CPDF_PageContentGenerator::ProcessPageObjects(std::ostringstream* buf) { ++bool CPDF_PageContentGenerator::ProcessPageObjects(fxcrt::ostringstream* buf) { + bool bDirty = false; + std::unique_ptr empty_content_marks = +- pdfium::MakeUnique(); ++ std::make_unique(); + const CPDF_ContentMarks* content_marks = empty_content_marks.get(); + + for (auto& pPageObj : m_pageObjects) { +@@ -214,8 +308,8 @@ bool CPDF_PageContentGenerator::ProcessPageObjects(std::ostringstream* buf) { + continue; + + bDirty = true; +- content_marks = ProcessContentMarks(buf, pPageObj.Get(), content_marks); +- ProcessPageObject(buf, pPageObj.Get()); ++ content_marks = ProcessContentMarks(buf, pPageObj, content_marks); ++ ProcessPageObject(buf, pPageObj); + } + FinishMarks(buf, content_marks); + return bDirty; +@@ -230,12 +324,11 @@ void CPDF_PageContentGenerator::UpdateStreamlessPageObjects( + } + + const CPDF_ContentMarks* CPDF_PageContentGenerator::ProcessContentMarks( +- std::ostringstream* buf, ++ fxcrt::ostringstream* buf, + const CPDF_PageObject* pPageObj, + const CPDF_ContentMarks* pPrev) { +- const CPDF_ContentMarks* pNext = &pPageObj->m_ContentMarks; +- +- size_t first_different = pPrev->FindFirstDifference(pNext); ++ const CPDF_ContentMarks* pNext = pPageObj->GetContentMarks(); ++ const size_t first_different = pPrev->FindFirstDifference(pNext); + + // Close all marks that are in prev but not in next. + // Technically we should iterate backwards to close from the top to the +@@ -282,7 +375,7 @@ const CPDF_ContentMarks* CPDF_PageContentGenerator::ProcessContentMarks( + } + + void CPDF_PageContentGenerator::FinishMarks( +- std::ostringstream* buf, ++ fxcrt::ostringstream* buf, + const CPDF_ContentMarks* pContentMarks) { + // Technically we should iterate backwards to close from the top to the + // bottom, but since the EMC operators do not identify which mark they are +@@ -291,10 +384,12 @@ void CPDF_PageContentGenerator::FinishMarks( + *buf << "EMC\n"; + } + +-void CPDF_PageContentGenerator::ProcessPageObject(std::ostringstream* buf, ++void CPDF_PageContentGenerator::ProcessPageObject(fxcrt::ostringstream* buf, + CPDF_PageObject* pPageObj) { + if (CPDF_ImageObject* pImageObject = pPageObj->AsImage()) + ProcessImage(buf, pImageObject); ++ else if (CPDF_FormObject* pFormObj = pPageObj->AsForm()) ++ ProcessForm(buf, pFormObj); + else if (CPDF_PathObject* pPathObj = pPageObj->AsPath()) + ProcessPath(buf, pPathObj); + else if (CPDF_TextObject* pTextObj = pPageObj->AsText()) +@@ -302,36 +397,59 @@ void CPDF_PageContentGenerator::ProcessPageObject(std::ostringstream* buf, + pPageObj->SetDirty(false); + } + +-void CPDF_PageContentGenerator::ProcessImage(std::ostringstream* buf, ++void CPDF_PageContentGenerator::ProcessImage(fxcrt::ostringstream* buf, + CPDF_ImageObject* pImageObj) { + if ((pImageObj->matrix().a == 0 && pImageObj->matrix().b == 0) || + (pImageObj->matrix().c == 0 && pImageObj->matrix().d == 0)) { + return; + } +- *buf << "q " << pImageObj->matrix() << " cm "; + + RetainPtr pImage = pImageObj->GetImage(); + if (pImage->IsInline()) + return; + +- CPDF_Stream* pStream = pImage->GetStream(); ++ RetainPtr pStream = pImage->GetStream(); + if (!pStream) + return; + ++ *buf << "q "; ++ WriteMatrix(*buf, pImageObj->matrix()) << " cm "; ++ + bool bWasInline = pStream->IsInline(); + if (bWasInline) + pImage->ConvertStreamToIndirectObject(); + + ByteString name = RealizeResource(pStream, "XObject"); ++ pImageObj->SetResourceName(name); ++ + if (bWasInline) { +- auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument.Get()); ++ auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument); + pImageObj->SetImage(pPageData->GetImage(pStream->GetObjNum())); + } + + *buf << "/" << PDF_NameEncode(name) << " Do Q\n"; + } + +-// Processing path with operators from Tables 4.9 and 4.10 of PDF spec 1.7: ++void CPDF_PageContentGenerator::ProcessForm(fxcrt::ostringstream* buf, ++ CPDF_FormObject* pFormObj) { ++ if ((pFormObj->form_matrix().a == 0 && pFormObj->form_matrix().b == 0) || ++ (pFormObj->form_matrix().c == 0 && pFormObj->form_matrix().d == 0)) { ++ return; ++ } ++ ++ RetainPtr pStream = pFormObj->form()->GetStream(); ++ if (!pStream) ++ return; ++ ++ ByteString name = RealizeResource(pStream.Get(), "XObject"); ++ pFormObj->SetResourceName(name); ++ ++ *buf << "q\n"; ++ WriteMatrix(*buf, pFormObj->form_matrix()) << " cm "; ++ *buf << "/" << PDF_NameEncode(name) << " Do Q\n"; ++} ++ ++// Processing path construction with operators from Table 4.9 of PDF spec 1.7: + // "re" appends a rectangle (here, used only if the whole path is a rectangle) + // "m" moves current point to the given coordinates + // "l" creates a line from current point to the new point +@@ -339,49 +457,56 @@ void CPDF_PageContentGenerator::ProcessImage(std::ostringstream* buf, + // points as the Bezier control points + // Note: "l", "c" change the current point + // "h" closes the subpath (appends a line from current to starting point) ++void CPDF_PageContentGenerator::ProcessPathPoints(fxcrt::ostringstream* buf, ++ CPDF_Path* pPath) { ++ pdfium::span points = pPath->GetPoints(); ++ if (pPath->IsRect()) { ++ CFX_PointF diff = points[2].m_Point - points[0].m_Point; ++ WritePoint(*buf, points[0].m_Point) << " "; ++ WritePoint(*buf, diff) << " re"; ++ return; ++ } ++ for (size_t i = 0; i < points.size(); ++i) { ++ if (i > 0) ++ *buf << " "; ++ ++ WritePoint(*buf, points[i].m_Point); ++ ++ CFX_Path::Point::Type point_type = points[i].m_Type; ++ if (point_type == CFX_Path::Point::Type::kMove) { ++ *buf << " m"; ++ } else if (point_type == CFX_Path::Point::Type::kLine) { ++ *buf << " l"; ++ } else if (point_type == CFX_Path::Point::Type::kBezier) { ++ if (i + 2 >= points.size() || ++ !points[i].IsTypeAndOpen(CFX_Path::Point::Type::kBezier) || ++ !points[i + 1].IsTypeAndOpen(CFX_Path::Point::Type::kBezier) || ++ points[i + 2].m_Type != CFX_Path::Point::Type::kBezier) { ++ // If format is not supported, close the path and paint ++ *buf << " h"; ++ break; ++ } ++ *buf << " "; ++ WritePoint(*buf, points[i + 1].m_Point) << " "; ++ WritePoint(*buf, points[i + 2].m_Point) << " c"; ++ i += 2; ++ } ++ if (points[i].m_CloseFigure) ++ *buf << " h"; ++ } ++} ++ ++// Processing path painting with operators from Table 4.10 of PDF spec 1.7: + // Path painting operators: "S", "n", "B", "f", "B*", "f*", depending on + // the filling mode and whether we want stroking the path or not. + // "Q" restores the graphics state imposed by the ProcessGraphics method. +-void CPDF_PageContentGenerator::ProcessPath(std::ostringstream* buf, ++void CPDF_PageContentGenerator::ProcessPath(fxcrt::ostringstream* buf, + CPDF_PathObject* pPathObj) { + ProcessGraphics(buf, pPathObj); + +- *buf << pPathObj->matrix() << " cm "; +- +- const auto& pPoints = pPathObj->path().GetPoints(); +- if (pPathObj->path().IsRect()) { +- CFX_PointF diff = pPoints[2].m_Point - pPoints[0].m_Point; +- *buf << pPoints[0].m_Point << " " << diff << " re"; +- } else { +- for (size_t i = 0; i < pPoints.size(); i++) { +- if (i > 0) +- *buf << " "; ++ WriteMatrix(*buf, pPathObj->matrix()) << " cm "; ++ ProcessPathPoints(buf, &pPathObj->path()); + +- *buf << pPoints[i].m_Point; +- +- FXPT_TYPE pointType = pPoints[i].m_Type; +- if (pointType == FXPT_TYPE::MoveTo) { +- *buf << " m"; +- } else if (pointType == FXPT_TYPE::LineTo) { +- *buf << " l"; +- } else if (pointType == FXPT_TYPE::BezierTo) { +- if (i + 2 >= pPoints.size() || +- !pPoints[i].IsTypeAndOpen(FXPT_TYPE::BezierTo) || +- !pPoints[i + 1].IsTypeAndOpen(FXPT_TYPE::BezierTo) || +- pPoints[i + 2].m_Type != FXPT_TYPE::BezierTo) { +- // If format is not supported, close the path and paint +- *buf << " h"; +- break; +- } +- *buf << " "; +- *buf << pPoints[i + 1].m_Point << " "; +- *buf << pPoints[i + 2].m_Point << " c"; +- i += 2; +- } +- if (pPoints[i].m_CloseFigure) +- *buf << " h"; +- } +- } + if (pPathObj->has_no_filltype()) + *buf << (pPathObj->stroke() ? " S" : " n"); + else if (pPathObj->has_winding_filltype()) +@@ -398,8 +523,10 @@ void CPDF_PageContentGenerator::ProcessPath(std::ostringstream* buf, + // "rg" sets the fill color, "RG" sets the stroke color (using DefaultRGB) + // "w" sets the stroke line width. + // "ca" sets the fill alpha, "CA" sets the stroke alpha. ++// "W" and "W*" modify the clipping path using the nonzero winding rule and ++// even-odd rules, respectively. + // "q" saves the graphics state, so that the settings can later be reversed +-void CPDF_PageContentGenerator::ProcessGraphics(std::ostringstream* buf, ++void CPDF_PageContentGenerator::ProcessGraphics(fxcrt::ostringstream* buf, + CPDF_PageObject* pPageObj) { + *buf << "q "; + float fillColor[3]; +@@ -416,12 +543,35 @@ void CPDF_PageContentGenerator::ProcessGraphics(std::ostringstream* buf, + if (lineWidth != 1.0f) + WriteFloat(*buf, lineWidth) << " w "; + CFX_GraphStateData::LineCap lineCap = pPageObj->m_GraphState.GetLineCap(); +- if (lineCap != CFX_GraphStateData::LineCapButt) ++ if (lineCap != CFX_GraphStateData::LineCap::kButt) + *buf << static_cast(lineCap) << " J "; + CFX_GraphStateData::LineJoin lineJoin = pPageObj->m_GraphState.GetLineJoin(); +- if (lineJoin != CFX_GraphStateData::LineJoinMiter) ++ if (lineJoin != CFX_GraphStateData::LineJoin::kMiter) + *buf << static_cast(lineJoin) << " j "; + ++ const CPDF_ClipPath& clip_path = pPageObj->m_ClipPath; ++ if (clip_path.HasRef()) { ++ for (size_t i = 0; i < clip_path.GetPathCount(); ++i) { ++ CPDF_Path path = clip_path.GetPath(i); ++ ProcessPathPoints(buf, &path); ++ switch (clip_path.GetClipType(i)) { ++ case CFX_FillRenderOptions::FillType::kWinding: ++ *buf << " W "; ++ break; ++ case CFX_FillRenderOptions::FillType::kEvenOdd: ++ *buf << " W* "; ++ break; ++ case CFX_FillRenderOptions::FillType::kNoFill: ++ NOTREACHED(); ++ break; ++ } ++ ++ // Use a no-op path-painting operator to terminate the path without ++ // causing any marks to be placed on the page. ++ *buf << "n "; ++ } ++ } ++ + GraphicsData graphD; + graphD.fillAlpha = pPageObj->m_GeneralState.GetFillAlpha(); + graphD.strokeAlpha = pPageObj->m_GeneralState.GetStrokeAlpha(); +@@ -432,9 +582,10 @@ void CPDF_PageContentGenerator::ProcessGraphics(std::ostringstream* buf, + } + + ByteString name; +- auto it = m_pObjHolder->m_GraphicsMap.find(graphD); +- if (it != m_pObjHolder->m_GraphicsMap.end()) { +- name = it->second; ++ absl::optional maybe_name = ++ m_pObjHolder->GraphicsMapSearch(graphD); ++ if (maybe_name.has_value()) { ++ name = std::move(maybe_name.value()); + } else { + auto gsDict = pdfium::MakeRetain(); + if (graphD.fillAlpha != 1.0f) +@@ -447,20 +598,21 @@ void CPDF_PageContentGenerator::ProcessGraphics(std::ostringstream* buf, + gsDict->SetNewFor("BM", + pPageObj->m_GeneralState.GetBlendMode()); + } +- CPDF_Object* pDict = m_pDocument->AddIndirectObject(gsDict); +- name = RealizeResource(pDict, "ExtGState"); +- m_pObjHolder->m_GraphicsMap[graphD] = name; ++ m_pDocument->AddIndirectObject(gsDict); ++ name = RealizeResource(std::move(gsDict), "ExtGState"); ++ pPageObj->SetGraphicsResourceName(name); ++ m_pObjHolder->GraphicsMapInsert(graphD, name); + } + *buf << "/" << PDF_NameEncode(name) << " gs "; + } + + void CPDF_PageContentGenerator::ProcessDefaultGraphics( +- std::ostringstream* buf) { ++ fxcrt::ostringstream* buf) { + *buf << "0 0 0 RG 0 0 0 rg 1 w " +- << static_cast(CFX_GraphStateData::LineCapButt) << " J " +- << static_cast(CFX_GraphStateData::LineJoinMiter) << " j\n"; +- ByteString name = GetOrCreateDefaultGraphics(); +- *buf << "/" << PDF_NameEncode(name) << " gs "; ++ << static_cast(CFX_GraphStateData::LineCap::kButt) << " J " ++ << static_cast(CFX_GraphStateData::LineJoin::kMiter) << " j\n"; ++ m_DefaultGraphicsName = GetOrCreateDefaultGraphics(); ++ *buf << "/" << PDF_NameEncode(m_DefaultGraphicsName) << " gs "; + } + + ByteString CPDF_PageContentGenerator::GetOrCreateDefaultGraphics() const { +@@ -468,34 +620,35 @@ ByteString CPDF_PageContentGenerator::GetOrCreateDefaultGraphics() const { + defaultGraphics.fillAlpha = 1.0f; + defaultGraphics.strokeAlpha = 1.0f; + defaultGraphics.blendType = BlendMode::kNormal; +- auto it = m_pObjHolder->m_GraphicsMap.find(defaultGraphics); + +- // If default graphics already exists, return it. +- if (it != m_pObjHolder->m_GraphicsMap.end()) +- return it->second; ++ absl::optional maybe_name = ++ m_pObjHolder->GraphicsMapSearch(defaultGraphics); ++ if (maybe_name.has_value()) ++ return maybe_name.value(); + +- // Otherwise, create them. + auto gsDict = pdfium::MakeRetain(); + gsDict->SetNewFor("ca", defaultGraphics.fillAlpha); + gsDict->SetNewFor("CA", defaultGraphics.strokeAlpha); + gsDict->SetNewFor("BM", "Normal"); +- CPDF_Object* pDict = m_pDocument->AddIndirectObject(gsDict); +- ByteString name = RealizeResource(pDict, "ExtGState"); +- m_pObjHolder->m_GraphicsMap[defaultGraphics] = name; ++ m_pDocument->AddIndirectObject(gsDict); ++ ByteString name = RealizeResource(std::move(gsDict), "ExtGState"); ++ m_pObjHolder->GraphicsMapInsert(defaultGraphics, name); + return name; + } + + // This method adds text to the buffer, BT begins the text object, ET ends it. + // Tm sets the text matrix (allows positioning and transforming text). + // Tf sets the font name (from Font in Resources) and font size. ++// Tr sets the text rendering mode. + // Tj sets the actual text, <####...> is used when specifying charcodes. +-void CPDF_PageContentGenerator::ProcessText(std::ostringstream* buf, ++void CPDF_PageContentGenerator::ProcessText(fxcrt::ostringstream* buf, + CPDF_TextObject* pTextObj) { + ProcessGraphics(buf, pTextObj); +- *buf << "BT " << pTextObj->GetTextMatrix() << " Tm "; ++ *buf << "BT "; ++ WriteMatrix(*buf, pTextObj->GetTextMatrix()) << " Tm "; + RetainPtr pFont(pTextObj->GetFont()); + if (!pFont) +- pFont = CPDF_Font::GetStockFont(m_pDocument.Get(), "Helvetica"); ++ pFont = CPDF_Font::GetStockFont(m_pDocument, "Helvetica"); + + FontData data; + const CPDF_FontEncoding* pEncoding = nullptr; +@@ -511,12 +664,13 @@ void CPDF_PageContentGenerator::ProcessText(std::ostringstream* buf, + return; + } + data.baseFont = pFont->GetBaseFontName(); +- auto it = m_pObjHolder->m_FontsMap.find(data); +- ByteString dictName; +- if (it != m_pObjHolder->m_FontsMap.end()) { +- dictName = it->second; ++ ++ ByteString dict_name; ++ absl::optional maybe_name = m_pObjHolder->FontsMapSearch(data); ++ if (maybe_name.has_value()) { ++ dict_name = std::move(maybe_name.value()); + } else { +- CPDF_Object* pIndirectFont = pFont->GetFontDict(); ++ RetainPtr pIndirectFont = pFont->GetFontDict(); + if (pIndirectFont->IsInline()) { + // In this case we assume it must be a standard font + auto pFontDict = pdfium::MakeRetain(); +@@ -527,18 +681,22 @@ void CPDF_PageContentGenerator::ProcessText(std::ostringstream* buf, + pFontDict->SetFor("Encoding", + pEncoding->Realize(m_pDocument->GetByteStringPool())); + } +- pIndirectFont = m_pDocument->AddIndirectObject(pFontDict); ++ m_pDocument->AddIndirectObject(pFontDict); ++ pIndirectFont = std::move(pFontDict); + } +- dictName = RealizeResource(pIndirectFont, "Font"); +- m_pObjHolder->m_FontsMap[data] = dictName; ++ dict_name = RealizeResource(std::move(pIndirectFont), "Font"); ++ m_pObjHolder->FontsMapInsert(data, dict_name); + } +- *buf << "/" << PDF_NameEncode(dictName) << " "; ++ pTextObj->SetResourceName(dict_name); ++ ++ *buf << "/" << PDF_NameEncode(dict_name) << " "; + WriteFloat(*buf, pTextObj->GetFontSize()) << " Tf "; ++ *buf << static_cast(pTextObj->GetTextRenderMode()) << " Tr "; + ByteString text; + for (uint32_t charcode : pTextObj->GetCharCodes()) { + if (charcode != CPDF_Font::kInvalidCharCode) + pFont->AppendChar(&text, charcode); + } +- *buf << PDF_EncodeString(text, true) << " Tj ET"; ++ *buf << PDF_HexEncodeString(text.AsStringView()) << " Tj ET"; + *buf << " Q\n"; + } +diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.h b/core/fpdfapi/edit/cpdf_pagecontentgenerator.h +index 40d19ae06..06bb239c2 100644 +--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.h ++++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,21 +7,23 @@ + #ifndef CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTGENERATOR_H_ + #define CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTGENERATOR_H_ + ++#include ++ + #include +-#include +-#include + #include + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/fx_string_wrappers.h" + #include "core/fxcrt/unowned_ptr.h" + + class CPDF_ContentMarks; + class CPDF_Document; ++class CPDF_FormObject; + class CPDF_ImageObject; + class CPDF_Object; + class CPDF_PageObject; + class CPDF_PageObjectHolder; ++class CPDF_Path; + class CPDF_PathObject; + class CPDF_TextObject; + +@@ -31,43 +33,50 @@ class CPDF_PageContentGenerator { + ~CPDF_PageContentGenerator(); + + void GenerateContent(); +- bool ProcessPageObjects(std::ostringstream* buf); ++ bool ProcessPageObjects(fxcrt::ostringstream* buf); + + private: + friend class CPDF_PageContentGeneratorTest; + +- void ProcessPageObject(std::ostringstream* buf, CPDF_PageObject* pPageObj); +- void ProcessPath(std::ostringstream* buf, CPDF_PathObject* pPathObj); +- void ProcessImage(std::ostringstream* buf, CPDF_ImageObject* pImageObj); +- void ProcessGraphics(std::ostringstream* buf, CPDF_PageObject* pPageObj); +- void ProcessDefaultGraphics(std::ostringstream* buf); +- void ProcessText(std::ostringstream* buf, CPDF_TextObject* pTextObj); ++ void ProcessPageObject(fxcrt::ostringstream* buf, CPDF_PageObject* pPageObj); ++ void ProcessPathPoints(fxcrt::ostringstream* buf, CPDF_Path* pPath); ++ void ProcessPath(fxcrt::ostringstream* buf, CPDF_PathObject* pPathObj); ++ void ProcessForm(fxcrt::ostringstream* buf, CPDF_FormObject* pFormObj); ++ void ProcessImage(fxcrt::ostringstream* buf, CPDF_ImageObject* pImageObj); ++ void ProcessGraphics(fxcrt::ostringstream* buf, CPDF_PageObject* pPageObj); ++ void ProcessDefaultGraphics(fxcrt::ostringstream* buf); ++ void ProcessText(fxcrt::ostringstream* buf, CPDF_TextObject* pTextObj); + ByteString GetOrCreateDefaultGraphics() const; + ByteString RealizeResource(const CPDF_Object* pResource, + const ByteString& bsType) const; +- const CPDF_ContentMarks* ProcessContentMarks(std::ostringstream* buf, ++ const CPDF_ContentMarks* ProcessContentMarks(fxcrt::ostringstream* buf, + const CPDF_PageObject* pPageObj, + const CPDF_ContentMarks* pPrev); +- void FinishMarks(std::ostringstream* buf, ++ void FinishMarks(fxcrt::ostringstream* buf, + const CPDF_ContentMarks* pContentMarks); + + // Returns a map from content stream index to new stream data. Unmodified + // streams are not touched. +- std::map> +- GenerateModifiedStreams(); ++ std::map GenerateModifiedStreams(); + +- // Add buffer as a stream in page's 'Contents' ++ // For each entry in `new_stream_data`, adds the string buffer to the page's ++ // content stream. + void UpdateContentStreams( +- std::map>* new_stream_data); ++ std::map&& new_stream_data); + +- // Set the stream index of all page objects with stream index == ++ // Sets the stream index of all page objects with stream index == + // |CPDF_PageObject::kNoContentStream|. These are new objects that had not + // been parsed from or written to any content stream yet. + void UpdateStreamlessPageObjects(int new_content_stream_index); + ++ // Updates the resource dictionary for `m_pObjHolder` to account for all the ++ // changes. ++ void UpdateResourcesDict(); ++ + UnownedPtr const m_pObjHolder; + UnownedPtr const m_pDocument; + std::vector> m_pageObjects; ++ ByteString m_DefaultGraphicsName; + }; + + #endif // CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTGENERATOR_H_ +diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp +index 62c3df569..8d6aa1f8a 100644 +--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp ++++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp +@@ -1,9 +1,10 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h" + ++#include + #include + #include + +@@ -12,65 +13,66 @@ + #include "core/fpdfapi/page/cpdf_docpagedata.h" + #include "core/fpdfapi/page/cpdf_form.h" + #include "core/fpdfapi/page/cpdf_page.h" +-#include "core/fpdfapi/page/cpdf_pagemodule.h" + #include "core/fpdfapi/page/cpdf_pathobject.h" + #include "core/fpdfapi/page/cpdf_textobject.h" ++#include "core/fpdfapi/page/cpdf_textstate.h" ++#include "core/fpdfapi/page/test_with_page_module.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_parser.h" + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_stream.h" +-#include "core/fpdfapi/render/cpdf_docrenderdata.h" +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxge/render_defines.h" ++#include "core/fpdfapi/parser/cpdf_test_document.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxge/cfx_fillrenderoptions.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + +-class CPDF_PageContentGeneratorTest : public testing::Test { ++class CPDF_PageContentGeneratorTest : public TestWithPageModule { + protected: +- void SetUp() override { CPDF_PageModule::Create(); } +- void TearDown() override { CPDF_PageModule::Destroy(); } +- + void TestProcessPath(CPDF_PageContentGenerator* pGen, +- std::ostringstream* buf, ++ fxcrt::ostringstream* buf, + CPDF_PathObject* pPathObj) { + pGen->ProcessPath(buf, pPathObj); + } + +- CPDF_Dictionary* TestGetResource(CPDF_PageContentGenerator* pGen, +- const ByteString& type, +- const ByteString& name) { +- return pGen->m_pObjHolder->m_pResources->GetDictFor(type)->GetDictFor(name); ++ RetainPtr TestGetResource( ++ CPDF_PageContentGenerator* pGen, ++ const ByteString& type, ++ const ByteString& name) { ++ RetainPtr pResources = ++ pGen->m_pObjHolder->GetResources(); ++ return pResources->GetDictFor(type)->GetDictFor(name); + } + + void TestProcessText(CPDF_PageContentGenerator* pGen, +- std::ostringstream* buf, ++ fxcrt::ostringstream* buf, + CPDF_TextObject* pTextObj) { + pGen->ProcessText(buf, pTextObj); + } + }; + + TEST_F(CPDF_PageContentGeneratorTest, ProcessRect) { +- auto pPathObj = pdfium::MakeUnique(); ++ auto pPathObj = std::make_unique(); + pPathObj->set_stroke(true); +- pPathObj->set_filltype(FXFILL_ALTERNATE); ++ pPathObj->set_filltype(CFX_FillRenderOptions::FillType::kEvenOdd); + pPathObj->path().AppendRect(10, 5, 13, 30); + + auto dummy_page_dict = pdfium::MakeRetain(); +- auto pTestPage = +- pdfium::MakeRetain(nullptr, dummy_page_dict.Get()); ++ auto pTestPage = pdfium::MakeRetain(nullptr, dummy_page_dict); + CPDF_PageContentGenerator generator(pTestPage.Get()); +- std::ostringstream buf; ++ fxcrt::ostringstream buf; + TestProcessPath(&generator, &buf, pPathObj.get()); + EXPECT_EQ("q 1 0 0 1 0 0 cm 10 5 3 25 re B* Q\n", ByteString(buf)); + +- pPathObj = pdfium::MakeUnique(); +- pPathObj->path().AppendPoint(CFX_PointF(0, 0), FXPT_TYPE::MoveTo, false); +- pPathObj->path().AppendPoint(CFX_PointF(5.2f, 0), FXPT_TYPE::LineTo, false); +- pPathObj->path().AppendPoint(CFX_PointF(5.2f, 3.78f), FXPT_TYPE::LineTo, +- false); +- pPathObj->path().AppendPoint(CFX_PointF(0, 3.78f), FXPT_TYPE::LineTo, true); ++ pPathObj = std::make_unique(); ++ pPathObj->path().AppendPoint(CFX_PointF(0, 0), CFX_Path::Point::Type::kMove); ++ pPathObj->path().AppendPoint(CFX_PointF(5.2f, 0), ++ CFX_Path::Point::Type::kLine); ++ pPathObj->path().AppendPoint(CFX_PointF(5.2f, 3.78f), ++ CFX_Path::Point::Type::kLine); ++ pPathObj->path().AppendPointAndClose(CFX_PointF(0, 3.78f), ++ CFX_Path::Point::Type::kLine); + buf.str(""); + TestProcessPath(&generator, &buf, pPathObj.get()); + EXPECT_EQ("q 1 0 0 1 0 0 cm 0 0 5.1999998 3.78 re n Q\n", ByteString(buf)); +@@ -78,10 +80,11 @@ TEST_F(CPDF_PageContentGeneratorTest, ProcessRect) { + + TEST_F(CPDF_PageContentGeneratorTest, BUG_937) { + static const std::vector rgb = {0.000000000000000000001f, 0.7f, 0.35f}; +- RetainPtr pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB); ++ RetainPtr pCS = ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB); + { +- auto pPathObj = pdfium::MakeUnique(); +- pPathObj->set_filltype(FXFILL_WINDING); ++ auto pPathObj = std::make_unique(); ++ pPathObj->set_filltype(CFX_FillRenderOptions::FillType::kWinding); + + // Test code in ProcessPath that generates re operator + pPathObj->path().AppendRect(0.000000000000000000001, +@@ -94,10 +97,9 @@ TEST_F(CPDF_PageContentGeneratorTest, BUG_937) { + 200000000000000.000002)); + + auto dummy_page_dict = pdfium::MakeRetain(); +- auto pTestPage = +- pdfium::MakeRetain(nullptr, dummy_page_dict.Get()); ++ auto pTestPage = pdfium::MakeRetain(nullptr, dummy_page_dict); + CPDF_PageContentGenerator generator(pTestPage.Get()); +- std::ostringstream buf; ++ fxcrt::ostringstream buf; + TestProcessPath(&generator, &buf, pPathObj.get()); + EXPECT_EQ( + "q 0 0.701961 0.34902 rg 0 0.701961 0.34902 RG 200000000000000000000 w" +@@ -108,30 +110,29 @@ TEST_F(CPDF_PageContentGeneratorTest, BUG_937) { + + { + // Test code in ProcessPath that handles bezier operator +- auto pPathObj = pdfium::MakeUnique(); ++ auto pPathObj = std::make_unique(); + pPathObj->m_ColorState.SetFillColor(pCS, rgb); + pPathObj->m_ColorState.SetStrokeColor(pCS, rgb); + pPathObj->m_GraphState.SetLineWidth(2.000000000000000000001); + pPathObj->Transform(CFX_Matrix(1, 0, 0, 1, 432, 500000000000000.000002)); + +- pPathObj->set_filltype(FXFILL_WINDING); ++ pPathObj->set_filltype(CFX_FillRenderOptions::FillType::kWinding); + pPathObj->path().AppendPoint(CFX_PointF(0.000000000000000000001f, 4.67f), +- FXPT_TYPE::MoveTo, false); ++ CFX_Path::Point::Type::kMove); + pPathObj->path().AppendPoint( + CFX_PointF(0.000000000000000000001, 100000000000000.000002), +- FXPT_TYPE::LineTo, false); ++ CFX_Path::Point::Type::kLine); + pPathObj->path().AppendPoint(CFX_PointF(0.0000000000001f, 3.15f), +- FXPT_TYPE::BezierTo, false); +- pPathObj->path().AppendPoint(CFX_PointF(3.57f, 2.98f), FXPT_TYPE::BezierTo, +- false); +- pPathObj->path().AppendPoint( ++ CFX_Path::Point::Type::kBezier); ++ pPathObj->path().AppendPoint(CFX_PointF(3.57f, 2.98f), ++ CFX_Path::Point::Type::kBezier); ++ pPathObj->path().AppendPointAndClose( + CFX_PointF(53.4f, 5000000000000000000.00000000000000004), +- FXPT_TYPE::BezierTo, true); ++ CFX_Path::Point::Type::kBezier); + auto dummy_page_dict = pdfium::MakeRetain(); +- auto pTestPage = +- pdfium::MakeRetain(nullptr, dummy_page_dict.Get()); ++ auto pTestPage = pdfium::MakeRetain(nullptr, dummy_page_dict); + CPDF_PageContentGenerator generator(pTestPage.Get()); +- std::ostringstream buf; ++ fxcrt::ostringstream buf; + + TestProcessPath(&generator, &buf, pPathObj.get()); + EXPECT_EQ( +@@ -144,33 +145,33 @@ TEST_F(CPDF_PageContentGeneratorTest, BUG_937) { + } + + TEST_F(CPDF_PageContentGeneratorTest, ProcessPath) { +- auto pPathObj = pdfium::MakeUnique(); +- pPathObj->set_filltype(FXFILL_WINDING); +- pPathObj->path().AppendPoint(CFX_PointF(3.102f, 4.67f), FXPT_TYPE::MoveTo, +- false); +- pPathObj->path().AppendPoint(CFX_PointF(5.45f, 0.29f), FXPT_TYPE::LineTo, +- false); +- pPathObj->path().AppendPoint(CFX_PointF(4.24f, 3.15f), FXPT_TYPE::BezierTo, +- false); +- pPathObj->path().AppendPoint(CFX_PointF(4.65f, 2.98f), FXPT_TYPE::BezierTo, +- false); +- pPathObj->path().AppendPoint(CFX_PointF(3.456f, 0.24f), FXPT_TYPE::BezierTo, +- false); +- pPathObj->path().AppendPoint(CFX_PointF(10.6f, 11.15f), FXPT_TYPE::LineTo, +- false); +- pPathObj->path().AppendPoint(CFX_PointF(11, 12.5f), FXPT_TYPE::LineTo, false); +- pPathObj->path().AppendPoint(CFX_PointF(11.46f, 12.67f), FXPT_TYPE::BezierTo, +- false); +- pPathObj->path().AppendPoint(CFX_PointF(11.84f, 12.96f), FXPT_TYPE::BezierTo, +- false); +- pPathObj->path().AppendPoint(CFX_PointF(12, 13.64f), FXPT_TYPE::BezierTo, +- true); ++ auto pPathObj = std::make_unique(); ++ pPathObj->set_filltype(CFX_FillRenderOptions::FillType::kWinding); ++ pPathObj->path().AppendPoint(CFX_PointF(3.102f, 4.67f), ++ CFX_Path::Point::Type::kMove); ++ pPathObj->path().AppendPoint(CFX_PointF(5.45f, 0.29f), ++ CFX_Path::Point::Type::kLine); ++ pPathObj->path().AppendPoint(CFX_PointF(4.24f, 3.15f), ++ CFX_Path::Point::Type::kBezier); ++ pPathObj->path().AppendPoint(CFX_PointF(4.65f, 2.98f), ++ CFX_Path::Point::Type::kBezier); ++ pPathObj->path().AppendPoint(CFX_PointF(3.456f, 0.24f), ++ CFX_Path::Point::Type::kBezier); ++ pPathObj->path().AppendPoint(CFX_PointF(10.6f, 11.15f), ++ CFX_Path::Point::Type::kLine); ++ pPathObj->path().AppendPoint(CFX_PointF(11, 12.5f), ++ CFX_Path::Point::Type::kLine); ++ pPathObj->path().AppendPoint(CFX_PointF(11.46f, 12.67f), ++ CFX_Path::Point::Type::kBezier); ++ pPathObj->path().AppendPoint(CFX_PointF(11.84f, 12.96f), ++ CFX_Path::Point::Type::kBezier); ++ pPathObj->path().AppendPointAndClose(CFX_PointF(12, 13.64f), ++ CFX_Path::Point::Type::kBezier); + + auto dummy_page_dict = pdfium::MakeRetain(); +- auto pTestPage = +- pdfium::MakeRetain(nullptr, dummy_page_dict.Get()); ++ auto pTestPage = pdfium::MakeRetain(nullptr, dummy_page_dict); + CPDF_PageContentGenerator generator(pTestPage.Get()); +- std::ostringstream buf; ++ fxcrt::ostringstream buf; + TestProcessPath(&generator, &buf, pPathObj.get()); + EXPECT_EQ( + "q 1 0 0 1 0 0 cm 3.102 4.6700001 m 5.4499998 .28999999 l 4.2399998 " +@@ -181,15 +182,17 @@ TEST_F(CPDF_PageContentGeneratorTest, ProcessPath) { + } + + TEST_F(CPDF_PageContentGeneratorTest, ProcessGraphics) { +- auto pPathObj = pdfium::MakeUnique(); ++ auto pPathObj = std::make_unique(); + pPathObj->set_stroke(true); +- pPathObj->set_filltype(FXFILL_WINDING); +- pPathObj->path().AppendPoint(CFX_PointF(1, 2), FXPT_TYPE::MoveTo, false); +- pPathObj->path().AppendPoint(CFX_PointF(3, 4), FXPT_TYPE::LineTo, false); +- pPathObj->path().AppendPoint(CFX_PointF(5, 6), FXPT_TYPE::LineTo, true); ++ pPathObj->set_filltype(CFX_FillRenderOptions::FillType::kWinding); ++ pPathObj->path().AppendPoint(CFX_PointF(1, 2), CFX_Path::Point::Type::kMove); ++ pPathObj->path().AppendPoint(CFX_PointF(3, 4), CFX_Path::Point::Type::kLine); ++ pPathObj->path().AppendPointAndClose(CFX_PointF(5, 6), ++ CFX_Path::Point::Type::kLine); + + static const std::vector rgb = {0.5f, 0.7f, 0.35f}; +- RetainPtr pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB); ++ RetainPtr pCS = ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB); + pPathObj->m_ColorState.SetFillColor(pCS, rgb); + + static const std::vector rgb2 = {1, 0.9f, 0}; +@@ -197,15 +200,13 @@ TEST_F(CPDF_PageContentGeneratorTest, ProcessGraphics) { + pPathObj->m_GeneralState.SetFillAlpha(0.5f); + pPathObj->m_GeneralState.SetStrokeAlpha(0.8f); + +- auto pDoc = pdfium::MakeUnique( +- pdfium::MakeUnique(), +- pdfium::MakeUnique()); +- ++ auto pDoc = std::make_unique(); + pDoc->CreateNewDoc(); +- CPDF_Dictionary* pPageDict = pDoc->CreateNewPage(0); ++ ++ RetainPtr pPageDict(pDoc->CreateNewPage(0)); + auto pTestPage = pdfium::MakeRetain(pDoc.get(), pPageDict); + CPDF_PageContentGenerator generator(pTestPage.Get()); +- std::ostringstream buf; ++ fxcrt::ostringstream buf; + TestProcessPath(&generator, &buf, pPathObj.get()); + ByteString pathString(buf); + +@@ -215,12 +216,12 @@ TEST_F(CPDF_PageContentGeneratorTest, ProcessGraphics) { + EXPECT_EQ(" gs 1 0 0 1 0 0 cm 1 2 m 3 4 l 5 6 l h B Q\n", + pathString.Last(43)); + ASSERT_GT(pathString.GetLength(), 91U); +- CPDF_Dictionary* externalGS = ++ RetainPtr externalGS = + TestGetResource(&generator, "ExtGState", + pathString.Substr(48, pathString.GetLength() - 91)); + ASSERT_TRUE(externalGS); +- EXPECT_EQ(0.5f, externalGS->GetNumberFor("ca")); +- EXPECT_EQ(0.8f, externalGS->GetNumberFor("CA")); ++ EXPECT_EQ(0.5f, externalGS->GetFloatFor("ca")); ++ EXPECT_EQ(0.8f, externalGS->GetFloatFor("CA")); + + // Same path, now with a stroke. + pPathObj->m_GraphState.SetLineWidth(10.5f); +@@ -240,22 +241,20 @@ TEST_F(CPDF_PageContentGeneratorTest, ProcessGraphics) { + + TEST_F(CPDF_PageContentGeneratorTest, ProcessStandardText) { + // Checking font whose font dictionary is not yet indirect object. +- auto pDoc = pdfium::MakeUnique( +- pdfium::MakeUnique(), +- pdfium::MakeUnique()); +- ++ auto pDoc = std::make_unique(); + pDoc->CreateNewDoc(); +- CPDF_Dictionary* pPageDict = pDoc->CreateNewPage(0); ++ ++ RetainPtr pPageDict(pDoc->CreateNewPage(0)); + auto pTestPage = pdfium::MakeRetain(pDoc.get(), pPageDict); + CPDF_PageContentGenerator generator(pTestPage.Get()); +- auto pTextObj = pdfium::MakeUnique(); +- RetainPtr pFont = +- CPDF_Font::GetStockFont(pDoc.get(), "Times-Roman"); +- pTextObj->m_TextState.SetFont(pFont); ++ auto pTextObj = std::make_unique(); ++ pTextObj->m_TextState.SetFont( ++ CPDF_Font::GetStockFont(pDoc.get(), "Times-Roman")); + pTextObj->m_TextState.SetFontSize(10.0f); + + static const std::vector rgb = {0.5f, 0.7f, 0.35f}; +- RetainPtr pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB); ++ RetainPtr pCS = ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB); + pTextObj->m_ColorState.SetFillColor(pCS, rgb); + + static const std::vector rgb2 = {1, 0.9f, 0}; +@@ -264,7 +263,7 @@ TEST_F(CPDF_PageContentGeneratorTest, ProcessStandardText) { + pTextObj->m_GeneralState.SetStrokeAlpha(0.8f); + pTextObj->Transform(CFX_Matrix(1, 0, 0, 1, 100, 100)); + pTextObj->SetText("Hello World"); +- std::ostringstream buf; ++ fxcrt::ostringstream buf; + TestProcessText(&generator, &buf, pTextObj.get()); + ByteString textString(buf); + auto firstResourceAt = textString.Find('/'); +@@ -284,61 +283,70 @@ TEST_F(CPDF_PageContentGeneratorTest, ProcessStandardText) { + "q 0.501961 0.701961 0.34902 rg 1 0.901961 0 RG /"; + // Color RGB values used are integers divided by 255. + ByteString compareString2 = " gs BT 1 0 0 1 100 100 Tm /"; +- ByteString compareString3 = " 10 Tf <48656C6C6F20576F726C64> Tj ET Q\n"; ++ ByteString compareString3 = " 10 Tf 0 Tr <48656C6C6F20576F726C64> Tj ET Q\n"; + EXPECT_LT(compareString1.GetLength() + compareString2.GetLength() + + compareString3.GetLength(), + textString.GetLength()); + EXPECT_EQ(compareString1, firstString.First(compareString1.GetLength())); + EXPECT_EQ(compareString2, midString.Last(compareString2.GetLength())); + EXPECT_EQ(compareString3, lastString.Last(compareString3.GetLength())); +- CPDF_Dictionary* externalGS = TestGetResource( ++ RetainPtr externalGS = TestGetResource( + &generator, "ExtGState", + midString.First(midString.GetLength() - compareString2.GetLength())); + ASSERT_TRUE(externalGS); +- EXPECT_EQ(0.5f, externalGS->GetNumberFor("ca")); +- EXPECT_EQ(0.8f, externalGS->GetNumberFor("CA")); +- CPDF_Dictionary* fontDict = TestGetResource( ++ EXPECT_EQ(0.5f, externalGS->GetFloatFor("ca")); ++ EXPECT_EQ(0.8f, externalGS->GetFloatFor("CA")); ++ RetainPtr fontDict = TestGetResource( + &generator, "Font", + lastString.First(lastString.GetLength() - compareString3.GetLength())); + ASSERT_TRUE(fontDict); +- EXPECT_EQ("Font", fontDict->GetStringFor("Type")); +- EXPECT_EQ("Type1", fontDict->GetStringFor("Subtype")); +- EXPECT_EQ("Times-Roman", fontDict->GetStringFor("BaseFont")); ++ EXPECT_EQ("Font", fontDict->GetNameFor("Type")); ++ EXPECT_EQ("Type1", fontDict->GetNameFor("Subtype")); ++ EXPECT_EQ("Times-Roman", fontDict->GetNameFor("BaseFont")); + } + + TEST_F(CPDF_PageContentGeneratorTest, ProcessText) { + // Checking font whose font dictionary is already an indirect object. +- auto pDoc = pdfium::MakeUnique( +- pdfium::MakeUnique(), +- pdfium::MakeUnique()); ++ auto pDoc = std::make_unique(); + pDoc->CreateNewDoc(); + +- CPDF_Dictionary* pPageDict = pDoc->CreateNewPage(0); ++ RetainPtr pPageDict(pDoc->CreateNewPage(0)); + auto pTestPage = pdfium::MakeRetain(pDoc.get(), pPageDict); + CPDF_PageContentGenerator generator(pTestPage.Get()); + +- std::ostringstream buf; ++ fxcrt::ostringstream buf; + { + // Set the text object font and text +- auto pTextObj = pdfium::MakeUnique(); +- CPDF_Dictionary* pDict = pDoc->NewIndirect(); ++ auto pTextObj = std::make_unique(); ++ auto pDict = pDoc->NewIndirect(); + pDict->SetNewFor("Type", "Font"); + pDict->SetNewFor("Subtype", "TrueType"); + + RetainPtr pFont = CPDF_Font::GetStockFont(pDoc.get(), "Arial"); + pDict->SetNewFor("BaseFont", pFont->GetBaseFontName()); + +- CPDF_Dictionary* pDesc = pDoc->NewIndirect(); ++ auto pDesc = pDoc->NewIndirect(); + pDesc->SetNewFor("Type", "FontDescriptor"); + pDesc->SetNewFor("FontName", pFont->GetBaseFontName()); + pDict->SetNewFor("FontDescriptor", pDoc.get(), + pDesc->GetObjNum()); + +- RetainPtr pLoadedFont = +- CPDF_DocPageData::FromDocument(pDoc.get())->GetFont(pDict); +- pTextObj->m_TextState.SetFont(pLoadedFont); ++ pTextObj->m_TextState.SetFont( ++ CPDF_DocPageData::FromDocument(pDoc.get())->GetFont(pDict)); + pTextObj->m_TextState.SetFontSize(15.5f); + pTextObj->SetText("I am indirect"); ++ pTextObj->SetTextRenderMode(TextRenderingMode::MODE_FILL_CLIP); ++ ++ // Add a clipping path. ++ auto pPath = std::make_unique(); ++ pPath->AppendPoint(CFX_PointF(0, 0), CFX_Path::Point::Type::kMove); ++ pPath->AppendPoint(CFX_PointF(5, 0), CFX_Path::Point::Type::kLine); ++ pPath->AppendPoint(CFX_PointF(5, 4), CFX_Path::Point::Type::kLine); ++ pPath->AppendPointAndClose(CFX_PointF(0, 4), CFX_Path::Point::Type::kLine); ++ pTextObj->m_ClipPath.Emplace(); ++ pTextObj->m_ClipPath.AppendPath(*pPath, ++ CFX_FillRenderOptions::FillType::kEvenOdd); ++ + TestProcessText(&generator, &buf, pTextObj.get()); + } + +@@ -350,75 +358,68 @@ TEST_F(CPDF_PageContentGeneratorTest, ProcessText) { + ByteString lastString = + textString.Last(textString.GetLength() - firstResourceAt.value()); + // q and Q must be outside the BT .. ET operations +- ByteString compareString1 = "q BT 1 0 0 1 0 0 Tm /"; +- ByteString compareString2 = " 15.5 Tf <4920616D20696E646972656374> Tj ET Q\n"; ++ ByteString compareString1 = "q 0 0 5 4 re W* n BT 1 0 0 1 0 0 Tm /"; ++ ByteString compareString2 = ++ " 15.5 Tf 4 Tr <4920616D20696E646972656374> Tj ET Q\n"; + EXPECT_LT(compareString1.GetLength() + compareString2.GetLength(), + textString.GetLength()); + EXPECT_EQ(compareString1, textString.First(compareString1.GetLength())); + EXPECT_EQ(compareString2, textString.Last(compareString2.GetLength())); +- CPDF_Dictionary* fontDict = TestGetResource( ++ RetainPtr fontDict = TestGetResource( + &generator, "Font", + textString.Substr(compareString1.GetLength(), + textString.GetLength() - compareString1.GetLength() - + compareString2.GetLength())); + ASSERT_TRUE(fontDict); + EXPECT_TRUE(fontDict->GetObjNum()); +- EXPECT_EQ("Font", fontDict->GetStringFor("Type")); +- EXPECT_EQ("TrueType", fontDict->GetStringFor("Subtype")); +- EXPECT_EQ("Helvetica", fontDict->GetStringFor("BaseFont")); +- CPDF_Dictionary* fontDesc = fontDict->GetDictFor("FontDescriptor"); ++ EXPECT_EQ("Font", fontDict->GetNameFor("Type")); ++ EXPECT_EQ("TrueType", fontDict->GetNameFor("Subtype")); ++ EXPECT_EQ("Helvetica", fontDict->GetNameFor("BaseFont")); ++ RetainPtr fontDesc = ++ fontDict->GetDictFor("FontDescriptor"); + ASSERT_TRUE(fontDesc); + EXPECT_TRUE(fontDesc->GetObjNum()); +- EXPECT_EQ("FontDescriptor", fontDesc->GetStringFor("Type")); +- EXPECT_EQ("Helvetica", fontDesc->GetStringFor("FontName")); ++ EXPECT_EQ("FontDescriptor", fontDesc->GetNameFor("Type")); ++ EXPECT_EQ("Helvetica", fontDesc->GetNameFor("FontName")); + } + + TEST_F(CPDF_PageContentGeneratorTest, ProcessEmptyForm) { +- auto pDoc = pdfium::MakeUnique( +- pdfium::MakeUnique(), +- pdfium::MakeUnique()); ++ auto pDoc = std::make_unique(); + pDoc->CreateNewDoc(); +- auto pDict = pdfium::MakeRetain(); +- auto pStream = pdfium::MakeRetain(nullptr, 0, std::move(pDict)); ++ auto pStream = ++ pdfium::MakeRetain(pdfium::MakeRetain()); + + // Create an empty form. +- auto pTestForm = +- pdfium::MakeUnique(pDoc.get(), nullptr, pStream.Get()); ++ auto pTestForm = std::make_unique(pDoc.get(), nullptr, pStream); + pTestForm->ParseContent(); + ASSERT_EQ(CPDF_PageObjectHolder::ParseState::kParsed, + pTestForm->GetParseState()); + + // The generated stream for the empty form should be an empty string. + CPDF_PageContentGenerator generator(pTestForm.get()); +- std::ostringstream buf; ++ fxcrt::ostringstream buf; + generator.ProcessPageObjects(&buf); + EXPECT_EQ("", ByteString(buf)); + } + + TEST_F(CPDF_PageContentGeneratorTest, ProcessFormWithPath) { +- auto pDoc = pdfium::MakeUnique( +- pdfium::MakeUnique(), +- pdfium::MakeUnique()); ++ auto pDoc = std::make_unique(); + pDoc->CreateNewDoc(); +- auto pDict = pdfium::MakeRetain(); +- const char content[] = ++ static constexpr uint8_t kContents[] = + "q 1 0 0 1 0 0 cm 3.102 4.6700001 m 5.4500012 .28999999 " + "l 4.2399998 3.1499999 4.65 2.98 3.456 0.24 c 3.102 4.6700001 l h f Q\n"; +- size_t buf_len = FX_ArraySize(content); +- std::unique_ptr buf(FX_Alloc(uint8_t, buf_len)); +- memcpy(buf.get(), content, buf_len); +- auto pStream = pdfium::MakeRetain(std::move(buf), buf_len, +- std::move(pDict)); ++ auto pStream = pdfium::MakeRetain( ++ DataVector(std::begin(kContents), std::end(kContents)), ++ pdfium::MakeRetain()); + + // Create a form with a non-empty stream. +- auto pTestForm = +- pdfium::MakeUnique(pDoc.get(), nullptr, pStream.Get()); ++ auto pTestForm = std::make_unique(pDoc.get(), nullptr, pStream); + pTestForm->ParseContent(); + ASSERT_EQ(CPDF_PageObjectHolder::ParseState::kParsed, + pTestForm->GetParseState()); + + CPDF_PageContentGenerator generator(pTestForm.get()); +- std::ostringstream process_buf; ++ fxcrt::ostringstream process_buf; + generator.ProcessPageObjects(&process_buf); + EXPECT_STREQ( + "q 1 0 0 1 0 0 cm 3.102 4.6700001 m 5.4500012 .28999999 l 4.2399998 3.14" +diff --git a/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp b/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp +index 35a8acd02..4be22e61f 100644 +--- a/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp ++++ b/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp +@@ -1,11 +1,16 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/edit/cpdf_pagecontentmanager.h" + ++#include ++ + #include + #include ++#include ++#include ++#include + #include + + #include "core/fpdfapi/page/cpdf_pageobject.h" +@@ -15,87 +20,155 @@ + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_stream.h" ++#include "core/fpdfapi/parser/object_tree_traversal_util.h" ++#include "third_party/abseil-cpp/absl/types/variant.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/adapters.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/numerics/safe_conversions.h" + + CPDF_PageContentManager::CPDF_PageContentManager( +- const CPDF_PageObjectHolder* obj_holder) +- : obj_holder_(obj_holder), doc_(obj_holder_->GetDocument()) { +- CPDF_Dictionary* page_dict = obj_holder_->GetDict(); +- CPDF_Object* contents_obj = page_dict->GetObjectFor("Contents"); +- CPDF_Array* contents_array = ToArray(contents_obj); ++ CPDF_PageObjectHolder* page_obj_holder, ++ CPDF_Document* document) ++ : page_obj_holder_(page_obj_holder), ++ document_(document), ++ objects_with_multi_refs_(GetObjectsWithMultipleReferences(document_)) { ++ RetainPtr page_dict = page_obj_holder_->GetMutableDict(); ++ RetainPtr contents_obj = ++ page_dict->GetMutableObjectFor("Contents"); ++ RetainPtr contents_array = ToArray(contents_obj); + if (contents_array) { +- contents_array_.Reset(contents_array); ++ CHECK(contents_array->IsInline()); ++ contents_ = std::move(contents_array); + return; + } + +- CPDF_Reference* contents_reference = ToReference(contents_obj); ++ RetainPtr contents_reference = ToReference(contents_obj); + if (contents_reference) { +- CPDF_Object* indirect_obj = contents_reference->GetDirect(); ++ RetainPtr indirect_obj = ++ contents_reference->GetMutableDirect(); + if (!indirect_obj) + return; + +- contents_array = indirect_obj->AsArray(); +- if (contents_array) +- contents_array_.Reset(contents_array); +- else if (indirect_obj->IsStream()) +- contents_stream_.Reset(indirect_obj->AsStream()); ++ contents_array.Reset(indirect_obj->AsMutableArray()); ++ if (contents_array) { ++ if (pdfium::Contains(objects_with_multi_refs_, ++ contents_array->GetObjNum())) { ++ RetainPtr cloned_contents_array = ++ pdfium::WrapRetain(contents_array->Clone()->AsMutableArray()); ++ page_dict->SetFor("Contents", cloned_contents_array); ++ contents_ = std::move(cloned_contents_array); ++ } else { ++ contents_ = std::move(contents_array); ++ } ++ } else if (indirect_obj->IsStream()) { ++ contents_ = pdfium::WrapRetain(indirect_obj->AsMutableStream()); ++ } + } + } + +-CPDF_PageContentManager::~CPDF_PageContentManager() = default; +- +-CPDF_Stream* CPDF_PageContentManager::GetStreamByIndex(size_t stream_index) { +- if (contents_stream_) +- return stream_index == 0 ? contents_stream_.Get() : nullptr; ++CPDF_PageContentManager::~CPDF_PageContentManager() { ++ ExecuteScheduledRemovals(); ++} + +- if (contents_array_) { +- CPDF_Reference* stream_reference = +- ToReference(contents_array_->GetObjectAt(stream_index)); +- if (!stream_reference) +- return nullptr; ++RetainPtr CPDF_PageContentManager::GetStreamByIndex( ++ size_t stream_index) { ++ RetainPtr contents_stream = GetContentsStream(); ++ if (contents_stream) { ++ return stream_index == 0 ? contents_stream : nullptr; ++ } + +- return stream_reference->GetDirect()->AsStream(); ++ RetainPtr contents_array = GetContentsArray(); ++ if (!contents_array) { ++ return nullptr; + } + +- return nullptr; ++ RetainPtr stream_reference = ++ ToReference(contents_array->GetMutableObjectAt(stream_index)); ++ if (!stream_reference) ++ return nullptr; ++ ++ return ToStream(stream_reference->GetMutableDirect()); + } + +-size_t CPDF_PageContentManager::AddStream(std::ostringstream* buf) { +- CPDF_Stream* new_stream = doc_->NewIndirect(); ++size_t CPDF_PageContentManager::AddStream(fxcrt::ostringstream* buf) { ++ auto new_stream = document_->NewIndirect(); + new_stream->SetDataFromStringstream(buf); + + // If there is one Content stream (not in an array), now there will be two, so + // create an array with the old and the new one. The new one's index is 1. +- if (contents_stream_) { +- CPDF_Array* new_contents_array = doc_->NewIndirect(); +- new_contents_array->AddNew(doc_.Get(), +- contents_stream_->GetObjNum()); +- new_contents_array->AddNew(doc_.Get(), +- new_stream->GetObjNum()); +- +- CPDF_Dictionary* page_dict = obj_holder_->GetDict(); +- page_dict->SetNewFor("Contents", doc_.Get(), ++ RetainPtr contents_stream = GetContentsStream(); ++ if (contents_stream) { ++ auto new_contents_array = document_->NewIndirect(); ++ new_contents_array->AppendNew(document_, ++ contents_stream->GetObjNum()); ++ new_contents_array->AppendNew(document_, ++ new_stream->GetObjNum()); ++ ++ RetainPtr page_dict = page_obj_holder_->GetMutableDict(); ++ page_dict->SetNewFor("Contents", document_, + new_contents_array->GetObjNum()); +- contents_array_.Reset(new_contents_array); +- contents_stream_ = nullptr; ++ contents_ = std::move(new_contents_array); + return 1; + } + + // If there is an array, just add the new stream to it, at the last position. +- if (contents_array_) { +- contents_array_->AddNew(doc_.Get(), +- new_stream->GetObjNum()); +- return contents_array_->size() - 1; ++ RetainPtr contents_array = GetContentsArray(); ++ if (contents_array) { ++ contents_array->AppendNew(document_, ++ new_stream->GetObjNum()); ++ return contents_array->size() - 1; + } + + // There were no Contents, so add the new stream as the single Content stream. + // Its index is 0. +- CPDF_Dictionary* page_dict = obj_holder_->GetDict(); +- page_dict->SetNewFor("Contents", doc_.Get(), ++ RetainPtr page_dict = page_obj_holder_->GetMutableDict(); ++ page_dict->SetNewFor("Contents", document_, + new_stream->GetObjNum()); +- contents_stream_.Reset(new_stream); ++ contents_ = std::move(new_stream); + return 0; + } + ++void CPDF_PageContentManager::UpdateStream(size_t stream_index, ++ fxcrt::ostringstream* buf) { ++ // If `buf` is now empty, remove the stream instead of setting the data. ++ if (buf->tellp() <= 0) { ++ ScheduleRemoveStreamByIndex(stream_index); ++ return; ++ } ++ ++ RetainPtr existing_stream = GetStreamByIndex(stream_index); ++ CHECK(existing_stream); ++ if (!pdfium::Contains(objects_with_multi_refs_, ++ existing_stream->GetObjNum())) { ++ existing_stream->SetDataFromStringstreamAndRemoveFilter(buf); ++ return; ++ } ++ ++ if (GetContentsStream()) { ++ auto new_stream = document_->NewIndirect(); ++ new_stream->SetDataFromStringstream(buf); ++ RetainPtr page_dict = page_obj_holder_->GetMutableDict(); ++ page_dict->SetNewFor("Contents", document_, ++ new_stream->GetObjNum()); ++ } ++ ++ RetainPtr contents_array = GetContentsArray(); ++ if (!contents_array) { ++ return; ++ } ++ ++ RetainPtr stream_reference = ++ ToReference(contents_array->GetMutableObjectAt(stream_index)); ++ if (!stream_reference) { ++ return; ++ } ++ ++ auto new_stream = document_->NewIndirect(); ++ new_stream->SetDataFromStringstream(buf); ++ stream_reference->SetRef(document_, new_stream->GetObjNum()); ++} ++ + void CPDF_PageContentManager::ScheduleRemoveStreamByIndex(size_t stream_index) { + streams_to_remove_.insert(stream_index); + } +@@ -104,49 +177,72 @@ void CPDF_PageContentManager::ExecuteScheduledRemovals() { + // This method assumes there are no dirty streams in the + // CPDF_PageObjectHolder. If there were any, their indexes would need to be + // updated. +- // Since this is only called by CPDF_PageContentGenerator::GenerateContent(), +- // which cleans up the dirty streams first, this should always be true. +- ASSERT(!obj_holder_->HasDirtyStreams()); ++ // Since CPDF_PageContentManager is only instantiated in ++ // CPDF_PageContentGenerator::GenerateContent(), which cleans up the dirty ++ // streams first, this should always be true. ++ DCHECK(!page_obj_holder_->HasDirtyStreams()); ++ ++ if (streams_to_remove_.empty()) { ++ return; ++ } + +- if (contents_stream_) { ++ RetainPtr contents_stream = GetContentsStream(); ++ if (contents_stream) { + // Only stream that can be removed is 0. + if (streams_to_remove_.find(0) != streams_to_remove_.end()) { +- CPDF_Dictionary* page_dict = obj_holder_->GetDict(); ++ RetainPtr page_dict = page_obj_holder_->GetMutableDict(); + page_dict->RemoveFor("Contents"); +- contents_stream_ = nullptr; +- } +- } else if (contents_array_) { +- // Initialize a vector with the old stream indexes. This will be used to +- // build a map from the old to the new indexes. +- std::vector streams_left(contents_array_->size()); +- std::iota(streams_left.begin(), streams_left.end(), 0); +- +- // In reverse order so as to not change the indexes in the middle of the +- // loop, remove the streams. +- for (auto it = streams_to_remove_.rbegin(); it != streams_to_remove_.rend(); +- ++it) { +- size_t stream_index = *it; +- contents_array_->RemoveAt(stream_index); +- streams_left.erase(streams_left.begin() + stream_index); + } ++ return; ++ } + +- // Create a mapping from the old to the new stream indexes, shifted due to +- // the deletion of the |streams_to_remove_|. +- std::map stream_index_mapping; +- for (size_t i = 0; i < streams_left.size(); ++i) +- stream_index_mapping[streams_left[i]] = i; +- +- // Update the page objects' content stream indexes. +- for (const auto& obj : *obj_holder_) { +- int32_t old_stream_index = obj->GetContentStream(); +- size_t new_stream_index = stream_index_mapping[old_stream_index]; +- obj->SetContentStream(new_stream_index); +- } ++ RetainPtr contents_array = GetContentsArray(); ++ if (!contents_array) { ++ return; ++ } ++ ++ // Initialize a vector with the old stream indexes. This will be used to build ++ // a map from the old to the new indexes. ++ std::vector streams_left(contents_array->size()); ++ std::iota(streams_left.begin(), streams_left.end(), 0); + +- // Even if there is a single content stream now, keep the array with a +- // single element. It's valid, a second stream might be added soon, and the +- // complexity of removing it is not worth it. ++ // In reverse order so as to not change the indexes in the middle of the loop, ++ // remove the streams. ++ for (size_t stream_index : pdfium::base::Reversed(streams_to_remove_)) { ++ contents_array->RemoveAt(stream_index); ++ streams_left.erase(streams_left.begin() + stream_index); + } + +- streams_to_remove_.clear(); ++ // Create a mapping from the old to the new stream indexes, shifted due to the ++ // deletion of the |streams_to_remove_|. ++ std::map stream_index_mapping; ++ for (size_t i = 0; i < streams_left.size(); ++i) { ++ stream_index_mapping[streams_left[i]] = i; ++ } ++ ++ // Update the page objects' content stream indexes. ++ for (const auto& obj : *page_obj_holder_) { ++ int32_t old_stream_index = obj->GetContentStream(); ++ int32_t new_stream_index = pdfium::base::checked_cast( ++ stream_index_mapping[old_stream_index]); ++ obj->SetContentStream(new_stream_index); ++ } ++ ++ // Even if there is a single content stream now, keep the array with a single ++ // element. It's valid, a second stream might be added in the near future, and ++ // the complexity of removing it is not worth it. ++} ++ ++RetainPtr CPDF_PageContentManager::GetContentsStream() { ++ if (absl::holds_alternative>(contents_)) { ++ return absl::get>(contents_); ++ } ++ return nullptr; ++} ++ ++RetainPtr CPDF_PageContentManager::GetContentsArray() { ++ if (absl::holds_alternative>(contents_)) { ++ return absl::get>(contents_); ++ } ++ return nullptr; + } +diff --git a/core/fpdfapi/edit/cpdf_pagecontentmanager.h b/core/fpdfapi/edit/cpdf_pagecontentmanager.h +index 2e2b225d2..5785fc3ba 100644 +--- a/core/fpdfapi/edit/cpdf_pagecontentmanager.h ++++ b/core/fpdfapi/edit/cpdf_pagecontentmanager.h +@@ -1,49 +1,60 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #ifndef CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTMANAGER_H_ + #define CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTMANAGER_H_ + ++#include ++ + #include +-#include + ++#include "core/fxcrt/fx_string_wrappers.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "third_party/abseil-cpp/absl/types/variant.h" + + class CPDF_Array; + class CPDF_Document; +-class CPDF_Object; +-class CPDF_Stream; + class CPDF_PageObjectHolder; ++class CPDF_Stream; + + class CPDF_PageContentManager { + public: +- explicit CPDF_PageContentManager(const CPDF_PageObjectHolder* obj_holder); ++ CPDF_PageContentManager(CPDF_PageObjectHolder* page_obj_holder, ++ CPDF_Document* document); + ~CPDF_PageContentManager(); + +- // Gets the Content stream at a given index. If Contents is a single stream +- // rather than an array, it is considered to be at index 0. +- CPDF_Stream* GetStreamByIndex(size_t stream_index); +- + // Adds a new Content stream. Its index in the array will be returned, or 0 + // if Contents is not an array, but only a single stream. +- size_t AddStream(std::ostringstream* buf); ++ size_t AddStream(fxcrt::ostringstream* buf); ++ ++ // Changes the stream at `stream_index` to contain the data in `buf`. If `buf` ++ // is empty, then schedule the removal of the stream instead. ++ void UpdateStream(size_t stream_index, fxcrt::ostringstream* buf); + +- // Schedule the removal of the Content stream at a given index. It will be +- // removed when ExecuteScheduledRemovals() is called. ++ private: ++ // Gets the Content stream at a given index. If Contents is a single stream ++ // rather than an array, it is retrievable at index 0. ++ RetainPtr GetStreamByIndex(size_t stream_index); ++ ++ // Schedules the removal of the Content stream at a given index. It will be ++ // removed upon CPDF_PageContentManager destruction. + void ScheduleRemoveStreamByIndex(size_t stream_index); + +- // Remove all Content streams for which ScheduleRemoveStreamByIndex() was ++ // Removes all Content streams for which ScheduleRemoveStreamByIndex() was + // called. Update the content stream of all page objects with the shifted + // indexes. + void ExecuteScheduledRemovals(); + +- private: +- UnownedPtr const obj_holder_; +- UnownedPtr const doc_; +- RetainPtr contents_array_; +- RetainPtr contents_stream_; ++ RetainPtr GetContentsStream(); ++ RetainPtr GetContentsArray(); ++ ++ UnownedPtr const page_obj_holder_; ++ UnownedPtr const document_; ++ const std::set objects_with_multi_refs_; ++ // When holding a CPDF_Stream, the pointer may be null. ++ absl::variant, RetainPtr> contents_; + std::set streams_to_remove_; + }; + +diff --git a/core/fpdfapi/edit/cpdf_stringarchivestream.cpp b/core/fpdfapi/edit/cpdf_stringarchivestream.cpp +index 4840db84a..4d9f7f2aa 100644 +--- a/core/fpdfapi/edit/cpdf_stringarchivestream.cpp ++++ b/core/fpdfapi/edit/cpdf_stringarchivestream.cpp +@@ -1,35 +1,24 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/edit/cpdf_stringarchivestream.h" + +-CPDF_StringArchiveStream::CPDF_StringArchiveStream(std::ostringstream* stream) +- : stream_(stream) {} ++#include + +-CPDF_StringArchiveStream::~CPDF_StringArchiveStream() {} ++#include "third_party/base/notreached.h" + +-bool CPDF_StringArchiveStream::WriteByte(uint8_t byte) { +- NOTREACHED(); +- return false; +-} ++CPDF_StringArchiveStream::CPDF_StringArchiveStream(fxcrt::ostringstream* stream) ++ : stream_(stream) {} + +-bool CPDF_StringArchiveStream::WriteDWord(uint32_t i) { +- NOTREACHED(); +- return false; +-} ++CPDF_StringArchiveStream::~CPDF_StringArchiveStream() = default; + + FX_FILESIZE CPDF_StringArchiveStream::CurrentOffset() const { + NOTREACHED(); + return false; + } + +-bool CPDF_StringArchiveStream::WriteBlock(const void* pData, size_t size) { +- stream_->write(static_cast(pData), size); +- return true; +-} +- +-bool CPDF_StringArchiveStream::WriteString(ByteStringView str) { +- stream_->write(str.unterminated_c_str(), str.GetLength()); ++bool CPDF_StringArchiveStream::WriteBlock(pdfium::span buffer) { ++ stream_->write(reinterpret_cast(buffer.data()), buffer.size()); + return true; + } +diff --git a/core/fpdfapi/edit/cpdf_stringarchivestream.h b/core/fpdfapi/edit/cpdf_stringarchivestream.h +index 59d168f43..130634390 100644 +--- a/core/fpdfapi/edit/cpdf_stringarchivestream.h ++++ b/core/fpdfapi/edit/cpdf_stringarchivestream.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,21 +6,19 @@ + #define CORE_FPDFAPI_EDIT_CPDF_STRINGARCHIVESTREAM_H_ + + #include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/fx_string_wrappers.h" + + class CPDF_StringArchiveStream final : public IFX_ArchiveStream { + public: +- explicit CPDF_StringArchiveStream(std::ostringstream* stream); ++ explicit CPDF_StringArchiveStream(fxcrt::ostringstream* stream); + ~CPDF_StringArchiveStream() override; + +- // IFX_ArchiveStream +- bool WriteByte(uint8_t byte) override; +- bool WriteDWord(uint32_t i) override; ++ // IFX_ArchiveStream: ++ bool WriteBlock(pdfium::span buffer) override; + FX_FILESIZE CurrentOffset() const override; +- bool WriteBlock(const void* pData, size_t size) override; +- bool WriteString(ByteStringView str) override; + + private: +- std::ostringstream* stream_; ++ fxcrt::ostringstream* stream_; + }; + + #endif // CORE_FPDFAPI_EDIT_CPDF_STRINGARCHIVESTREAM_H_ +diff --git a/core/fpdfapi/font/BUILD.gn b/core/fpdfapi/font/BUILD.gn +index c30269adb..ce8943382 100644 +--- a/core/fpdfapi/font/BUILD.gn ++++ b/core/fpdfapi/font/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -17,8 +17,6 @@ source_set("font") { + "cpdf_cidfont.h", + "cpdf_cmap.cpp", + "cpdf_cmap.h", +- "cpdf_cmapmanager.cpp", +- "cpdf_cmapmanager.h", + "cpdf_cmapparser.cpp", + "cpdf_cmapparser.h", + "cpdf_font.cpp", +@@ -40,17 +38,21 @@ source_set("font") { + "cpdf_type3font.cpp", + "cpdf_type3font.h", + ] +- configs += [ "../../../:pdfium_core_config" ] ++ configs += [ ++ "../../../:pdfium_strict_config", ++ "../../../:pdfium_noshorten_config", ++ ] + deps = [ ++ "../../../constants", + "../../fxcrt", + "../../fxge", + "../cmaps", + "../parser", + ] ++ visibility = [ "../../../*" ] + if (is_mac) { +- libs = [ "CoreFoundation.framework" ] ++ frameworks = [ "CoreFoundation.framework" ] + } +- visibility = [ "../../../*" ] + } + + pdfium_unittest_source_set("unittests") { +@@ -61,8 +63,9 @@ pdfium_unittest_source_set("unittests") { + ] + deps = [ + ":font", +- "../page", ++ "../page:unit_test_support", + "../parser", ++ "../parser:unit_test_support", + "../render", + ] + pdfium_root_dir = "../../../" +diff --git a/core/fpdfapi/font/cfx_cttgsubtable.cpp b/core/fpdfapi/font/cfx_cttgsubtable.cpp +index 49f93dc42..ae5a84126 100644 +--- a/core/fpdfapi/font/cfx_cttgsubtable.cpp ++++ b/core/fpdfapi/font/cfx_cttgsubtable.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,11 +6,14 @@ + + #include "core/fpdfapi/font/cfx_cttgsubtable.h" + ++#include ++ + #include + ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/stl_util.h" + #include "core/fxge/cfx_fontmapper.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" + + namespace { + +@@ -50,61 +53,63 @@ CFX_CTTGSUBTable::CFX_CTTGSUBTable(FT_Bytes gsub) { + CFX_CTTGSUBTable::~CFX_CTTGSUBTable() = default; + + bool CFX_CTTGSUBTable::LoadGSUBTable(FT_Bytes gsub) { +- if ((gsub[0] << 24u | gsub[1] << 16u | gsub[2] << 8u | gsub[3]) != 0x00010000) ++ if (FXSYS_UINT32_GET_MSBFIRST(gsub) != 0x00010000) + return false; + +- return Parse(&gsub[gsub[4] << 8 | gsub[5]], &gsub[gsub[6] << 8 | gsub[7]], +- &gsub[gsub[8] << 8 | gsub[9]]); ++ return Parse(&gsub[FXSYS_UINT16_GET_MSBFIRST(gsub + 4)], ++ &gsub[FXSYS_UINT16_GET_MSBFIRST(gsub + 6)], ++ &gsub[FXSYS_UINT16_GET_MSBFIRST(gsub + 8)]); + } + + uint32_t CFX_CTTGSUBTable::GetVerticalGlyph(uint32_t glyphnum) const { +- uint32_t vglyphnum = 0; + for (uint32_t item : m_featureSet) { +- if (GetVerticalGlyphSub(FeatureList[item], glyphnum, &vglyphnum)) +- break; ++ absl::optional result = ++ GetVerticalGlyphSub(FeatureList[item], glyphnum); ++ if (result.has_value()) ++ return result.value(); + } +- return vglyphnum; ++ return 0; + } + +-bool CFX_CTTGSUBTable::GetVerticalGlyphSub(const TFeatureRecord& feature, +- uint32_t glyphnum, +- uint32_t* vglyphnum) const { ++absl::optional CFX_CTTGSUBTable::GetVerticalGlyphSub( ++ const TFeatureRecord& feature, ++ uint32_t glyphnum) const { + for (int index : feature.LookupListIndices) { +- if (!pdfium::IndexInBounds(LookupList, index)) ++ if (!fxcrt::IndexInBounds(LookupList, index)) + continue; +- if (LookupList[index].LookupType == 1 && +- GetVerticalGlyphSub2(LookupList[index], glyphnum, vglyphnum)) { +- return true; +- } ++ if (LookupList[index].LookupType != 1) ++ continue; ++ absl::optional result = ++ GetVerticalGlyphSub2(LookupList[index], glyphnum); ++ if (result.has_value()) ++ return result.value(); + } +- return false; ++ return absl::nullopt; + } + +-bool CFX_CTTGSUBTable::GetVerticalGlyphSub2(const TLookup& lookup, +- uint32_t glyphnum, +- uint32_t* vglyphnum) const { ++absl::optional CFX_CTTGSUBTable::GetVerticalGlyphSub2( ++ const TLookup& lookup, ++ uint32_t glyphnum) const { + for (const auto& subTable : lookup.SubTables) { + switch (subTable->SubstFormat) { + case 1: { + auto* tbl1 = static_cast(subTable.get()); + if (GetCoverageIndex(tbl1->Coverage.get(), glyphnum) >= 0) { +- *vglyphnum = glyphnum + tbl1->DeltaGlyphID; +- return true; ++ return glyphnum + tbl1->DeltaGlyphID; + } + break; + } + case 2: { + auto* tbl2 = static_cast(subTable.get()); + int index = GetCoverageIndex(tbl2->Coverage.get(), glyphnum); +- if (pdfium::IndexInBounds(tbl2->Substitutes, index)) { +- *vglyphnum = tbl2->Substitutes[index]; +- return true; ++ if (fxcrt::IndexInBounds(tbl2->Substitutes, index)) { ++ return tbl2->Substitutes[index]; + } + break; + } + } + } +- return false; ++ return absl::nullopt; + } + + int CFX_CTTGSUBTable::GetCoverageIndex(TCoverageFormatBase* Coverage, +@@ -145,25 +150,25 @@ uint8_t CFX_CTTGSUBTable::GetUInt8(FT_Bytes& p) const { + } + + int16_t CFX_CTTGSUBTable::GetInt16(FT_Bytes& p) const { +- uint16_t ret = p[0] << 8 | p[1]; ++ uint16_t ret = FXSYS_UINT16_GET_MSBFIRST(p); + p += 2; + return *(int16_t*)&ret; + } + + uint16_t CFX_CTTGSUBTable::GetUInt16(FT_Bytes& p) const { +- uint16_t ret = p[0] << 8 | p[1]; ++ uint16_t ret = FXSYS_UINT16_GET_MSBFIRST(p); + p += 2; + return ret; + } + + int32_t CFX_CTTGSUBTable::GetInt32(FT_Bytes& p) const { +- uint32_t ret = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; ++ uint32_t ret = FXSYS_UINT32_GET_MSBFIRST(p); + p += 4; + return *(int32_t*)&ret; + } + + uint32_t CFX_CTTGSUBTable::GetUInt32(FT_Bytes& p) const { +- uint32_t ret = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; ++ uint32_t ret = FXSYS_UINT32_GET_MSBFIRST(p); + p += 4; + return ret; + } +@@ -200,7 +205,7 @@ void CFX_CTTGSUBTable::ParseLangSys(FT_Bytes raw, TLangSysRecord* rec) { + FT_Bytes sp = raw; + rec->LookupOrder = GetUInt16(sp); + rec->ReqFeatureIndex = GetUInt16(sp); +- rec->FeatureIndices = std::vector(GetUInt16(sp)); ++ rec->FeatureIndices = DataVector(GetUInt16(sp)); + for (auto& element : rec->FeatureIndices) + element = GetUInt16(sp); + } +@@ -217,7 +222,7 @@ void CFX_CTTGSUBTable::ParseFeatureList(FT_Bytes raw) { + void CFX_CTTGSUBTable::ParseFeature(FT_Bytes raw, TFeatureRecord* rec) { + FT_Bytes sp = raw; + rec->FeatureParams = GetUInt16(sp); +- rec->LookupListIndices = std::vector(GetUInt16(sp)); ++ rec->LookupListIndices = DataVector(GetUInt16(sp)); + for (auto& listIndex : rec->LookupListIndices) + listIndex = GetUInt16(sp); + } +@@ -238,127 +243,117 @@ void CFX_CTTGSUBTable::ParseLookup(FT_Bytes raw, TLookup* rec) { + return; + + for (auto& subTable : rec->SubTables) +- ParseSingleSubst(&raw[GetUInt16(sp)], &subTable); ++ subTable = ParseSingleSubst(&raw[GetUInt16(sp)]); + } + + std::unique_ptr + CFX_CTTGSUBTable::ParseCoverage(FT_Bytes raw) { + FT_Bytes sp = raw; + uint16_t format = GetUInt16(sp); +- if (format == 1) { +- auto rec = pdfium::MakeUnique(); +- ParseCoverageFormat1(raw, rec.get()); +- return std::move(rec); +- } +- if (format == 2) { +- auto rec = pdfium::MakeUnique(); +- ParseCoverageFormat2(raw, rec.get()); +- return std::move(rec); +- } ++ if (format == 1) ++ return ParseCoverageFormat1(raw); ++ if (format == 2) ++ return ParseCoverageFormat2(raw); + return nullptr; + } + +-void CFX_CTTGSUBTable::ParseCoverageFormat1(FT_Bytes raw, +- TCoverageFormat1* rec) { ++std::unique_ptr ++CFX_CTTGSUBTable::ParseCoverageFormat1(FT_Bytes raw) { + FT_Bytes sp = raw; + (void)GetUInt16(sp); +- rec->GlyphArray = std::vector(GetUInt16(sp)); ++ auto rec = std::make_unique(GetUInt16(sp)); + for (auto& glyph : rec->GlyphArray) + glyph = GetUInt16(sp); ++ return rec; + } + +-void CFX_CTTGSUBTable::ParseCoverageFormat2(FT_Bytes raw, +- TCoverageFormat2* rec) { ++std::unique_ptr ++CFX_CTTGSUBTable::ParseCoverageFormat2(FT_Bytes raw) { + FT_Bytes sp = raw; + (void)GetUInt16(sp); +- rec->RangeRecords = std::vector(GetUInt16(sp)); ++ auto rec = std::make_unique(GetUInt16(sp)); + for (auto& rangeRec : rec->RangeRecords) { + rangeRec.Start = GetUInt16(sp); + rangeRec.End = GetUInt16(sp); + rangeRec.StartCoverageIndex = GetUInt16(sp); + } ++ return rec; + } + +-void CFX_CTTGSUBTable::ParseSingleSubst(FT_Bytes raw, +- std::unique_ptr* rec) { ++std::unique_ptr ++CFX_CTTGSUBTable::ParseSingleSubst(FT_Bytes raw) { + FT_Bytes sp = raw; +- uint16_t Format = GetUInt16(sp); +- switch (Format) { +- case 1: +- *rec = pdfium::MakeUnique(); +- ParseSingleSubstFormat1(raw, static_cast(rec->get())); +- break; +- case 2: +- *rec = pdfium::MakeUnique(); +- ParseSingleSubstFormat2(raw, static_cast(rec->get())); +- break; +- } ++ uint16_t format = GetUInt16(sp); ++ if (format == 1) ++ return ParseSingleSubstFormat1(raw); ++ if (format == 2) ++ return ParseSingleSubstFormat2(raw); ++ return nullptr; + } + +-void CFX_CTTGSUBTable::ParseSingleSubstFormat1(FT_Bytes raw, TSubTable1* rec) { ++std::unique_ptr ++CFX_CTTGSUBTable::ParseSingleSubstFormat1(FT_Bytes raw) { + FT_Bytes sp = raw; + GetUInt16(sp); + uint16_t offset = GetUInt16(sp); ++ auto rec = std::make_unique(); + rec->Coverage = ParseCoverage(&raw[offset]); + rec->DeltaGlyphID = GetInt16(sp); ++ return rec; + } + +-void CFX_CTTGSUBTable::ParseSingleSubstFormat2(FT_Bytes raw, TSubTable2* rec) { ++std::unique_ptr ++CFX_CTTGSUBTable::ParseSingleSubstFormat2(FT_Bytes raw) { + FT_Bytes sp = raw; + (void)GetUInt16(sp); + uint16_t offset = GetUInt16(sp); ++ auto rec = std::make_unique(); + rec->Coverage = ParseCoverage(&raw[offset]); +- rec->Substitutes = std::vector(GetUInt16(sp)); ++ rec->Substitutes = DataVector(GetUInt16(sp)); + for (auto& substitute : rec->Substitutes) + substitute = GetUInt16(sp); ++ return rec; + } + +-CFX_CTTGSUBTable::TLangSysRecord::TLangSysRecord() +- : LangSysTag(0), LookupOrder(0), ReqFeatureIndex(0) {} ++CFX_CTTGSUBTable::TLangSysRecord::TLangSysRecord() = default; + +-CFX_CTTGSUBTable::TLangSysRecord::~TLangSysRecord() {} ++CFX_CTTGSUBTable::TLangSysRecord::~TLangSysRecord() = default; + +-CFX_CTTGSUBTable::TScriptRecord::TScriptRecord() +- : ScriptTag(0), DefaultLangSys(0) {} ++CFX_CTTGSUBTable::TScriptRecord::TScriptRecord() = default; + +-CFX_CTTGSUBTable::TScriptRecord::~TScriptRecord() {} ++CFX_CTTGSUBTable::TScriptRecord::~TScriptRecord() = default; + +-CFX_CTTGSUBTable::TFeatureRecord::TFeatureRecord() +- : FeatureTag(0), FeatureParams(0) {} ++CFX_CTTGSUBTable::TFeatureRecord::TFeatureRecord() = default; + +-CFX_CTTGSUBTable::TFeatureRecord::~TFeatureRecord() {} ++CFX_CTTGSUBTable::TFeatureRecord::~TFeatureRecord() = default; + +-CFX_CTTGSUBTable::TRangeRecord::TRangeRecord() +- : Start(0), End(0), StartCoverageIndex(0) {} ++CFX_CTTGSUBTable::TRangeRecord::TRangeRecord() = default; + +-CFX_CTTGSUBTable::TCoverageFormat1::TCoverageFormat1() { +- CoverageFormat = 1; +-} ++CFX_CTTGSUBTable::TCoverageFormat1::TCoverageFormat1(size_t initial_size) ++ : TCoverageFormatBase(1), GlyphArray(initial_size) {} + +-CFX_CTTGSUBTable::TCoverageFormat1::~TCoverageFormat1() {} ++CFX_CTTGSUBTable::TCoverageFormat1::~TCoverageFormat1() = default; + +-CFX_CTTGSUBTable::TCoverageFormat2::TCoverageFormat2() { +- CoverageFormat = 2; +-} ++CFX_CTTGSUBTable::TCoverageFormat2::TCoverageFormat2(size_t initial_size) ++ : TCoverageFormatBase(2), RangeRecords(initial_size) {} + +-CFX_CTTGSUBTable::TCoverageFormat2::~TCoverageFormat2() {} ++CFX_CTTGSUBTable::TCoverageFormat2::~TCoverageFormat2() = default; + +-CFX_CTTGSUBTable::TSubTableBase::TSubTableBase() {} ++CFX_CTTGSUBTable::TDevice::TDevice() = default; + +-CFX_CTTGSUBTable::TSubTableBase::~TSubTableBase() {} ++CFX_CTTGSUBTable::TSubTableBase::TSubTableBase(uint16_t format) ++ : SubstFormat(format) {} + +-CFX_CTTGSUBTable::TSubTable1::TSubTable1() { +- SubstFormat = 1; +-} ++CFX_CTTGSUBTable::TSubTableBase::~TSubTableBase() = default; + +-CFX_CTTGSUBTable::TSubTable1::~TSubTable1() {} ++CFX_CTTGSUBTable::TSubTable1::TSubTable1() : TSubTableBase(1) {} + +-CFX_CTTGSUBTable::TSubTable2::TSubTable2() { +- SubstFormat = 2; +-} ++CFX_CTTGSUBTable::TSubTable1::~TSubTable1() = default; ++ ++CFX_CTTGSUBTable::TSubTable2::TSubTable2() : TSubTableBase(2) {} + +-CFX_CTTGSUBTable::TSubTable2::~TSubTable2() {} ++CFX_CTTGSUBTable::TSubTable2::~TSubTable2() = default; + +-CFX_CTTGSUBTable::TLookup::TLookup() : LookupType(0), LookupFlag(0) {} ++CFX_CTTGSUBTable::TLookup::TLookup() = default; + +-CFX_CTTGSUBTable::TLookup::~TLookup() {} ++CFX_CTTGSUBTable::TLookup::~TLookup() = default; +diff --git a/core/fpdfapi/font/cfx_cttgsubtable.h b/core/fpdfapi/font/cfx_cttgsubtable.h +index 54e16b4ae..055533315 100644 +--- a/core/fpdfapi/font/cfx_cttgsubtable.h ++++ b/core/fpdfapi/font/cfx_cttgsubtable.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,7 +13,9 @@ + #include + #include + +-#include "core/fxge/fx_freetype.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxge/freetype/fx_freetype.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CFX_CTTGSUBTable { + public: +@@ -27,18 +29,18 @@ class CFX_CTTGSUBTable { + TLangSysRecord(); + ~TLangSysRecord(); + +- uint32_t LangSysTag; +- uint16_t LookupOrder; +- uint16_t ReqFeatureIndex; +- std::vector FeatureIndices; ++ uint32_t LangSysTag = 0; ++ uint16_t LookupOrder = 0; ++ uint16_t ReqFeatureIndex = 0; ++ DataVector FeatureIndices; + }; + + struct TScriptRecord { + TScriptRecord(); + ~TScriptRecord(); + +- uint32_t ScriptTag; +- uint16_t DefaultLangSys; ++ uint32_t ScriptTag = 0; ++ uint16_t DefaultLangSys = 0; + std::vector LangSysRecords; + }; + +@@ -46,74 +48,76 @@ class CFX_CTTGSUBTable { + TFeatureRecord(); + ~TFeatureRecord(); + +- uint32_t FeatureTag; +- uint16_t FeatureParams; +- std::vector LookupListIndices; ++ uint32_t FeatureTag = 0; ++ uint16_t FeatureParams = 0; ++ DataVector LookupListIndices; + }; + + struct TRangeRecord { + TRangeRecord(); + +- uint16_t Start; +- uint16_t End; +- uint16_t StartCoverageIndex; ++ uint16_t Start = 0; ++ uint16_t End = 0; ++ uint16_t StartCoverageIndex = 0; + }; + + struct TCoverageFormatBase { ++ explicit TCoverageFormatBase(uint16_t format) : CoverageFormat(format) {} + virtual ~TCoverageFormatBase() = default; +- uint16_t CoverageFormat; ++ ++ const uint16_t CoverageFormat; + }; + + struct TCoverageFormat1 final : public TCoverageFormatBase { +- TCoverageFormat1(); ++ explicit TCoverageFormat1(size_t initial_size); + ~TCoverageFormat1() override; + +- std::vector GlyphArray; ++ DataVector GlyphArray; + }; + + struct TCoverageFormat2 final : public TCoverageFormatBase { +- TCoverageFormat2(); ++ explicit TCoverageFormat2(size_t initial_size); + ~TCoverageFormat2() override; + + std::vector RangeRecords; + }; + + struct TDevice { +- TDevice() : StartSize(0), EndSize(0), DeltaFormat(0) {} ++ TDevice(); + +- uint16_t StartSize; +- uint16_t EndSize; +- uint16_t DeltaFormat; ++ uint16_t StartSize = 0; ++ uint16_t EndSize = 0; ++ uint16_t DeltaFormat = 0; + }; + + struct TSubTableBase { +- TSubTableBase(); ++ explicit TSubTableBase(uint16_t format); + virtual ~TSubTableBase(); + ++ const uint16_t SubstFormat; + std::unique_ptr Coverage; +- uint16_t SubstFormat; + }; + + struct TSubTable1 final : public TSubTableBase { + TSubTable1(); + ~TSubTable1() override; + +- int16_t DeltaGlyphID; ++ int16_t DeltaGlyphID = 0; + }; + + struct TSubTable2 final : public TSubTableBase { + TSubTable2(); + ~TSubTable2() override; + +- std::vector Substitutes; ++ DataVector Substitutes; + }; + + struct TLookup { + TLookup(); + ~TLookup(); + +- uint16_t LookupType; +- uint16_t LookupFlag; ++ uint16_t LookupType = 0; ++ uint16_t LookupFlag = 0; + std::vector> SubTables; + }; + +@@ -127,18 +131,16 @@ class CFX_CTTGSUBTable { + void ParseLookupList(FT_Bytes raw); + void ParseLookup(FT_Bytes raw, TLookup* rec); + std::unique_ptr ParseCoverage(FT_Bytes raw); +- void ParseCoverageFormat1(FT_Bytes raw, TCoverageFormat1* rec); +- void ParseCoverageFormat2(FT_Bytes raw, TCoverageFormat2* rec); +- void ParseSingleSubst(FT_Bytes raw, std::unique_ptr* rec); +- void ParseSingleSubstFormat1(FT_Bytes raw, TSubTable1* rec); +- void ParseSingleSubstFormat2(FT_Bytes raw, TSubTable2* rec); +- +- bool GetVerticalGlyphSub(const TFeatureRecord& feature, +- uint32_t glyphnum, +- uint32_t* vglyphnum) const; +- bool GetVerticalGlyphSub2(const TLookup& lookup, +- uint32_t glyphnum, +- uint32_t* vglyphnum) const; ++ std::unique_ptr ParseCoverageFormat1(FT_Bytes raw); ++ std::unique_ptr ParseCoverageFormat2(FT_Bytes raw); ++ std::unique_ptr ParseSingleSubst(FT_Bytes raw); ++ std::unique_ptr ParseSingleSubstFormat1(FT_Bytes raw); ++ std::unique_ptr ParseSingleSubstFormat2(FT_Bytes raw); ++ ++ absl::optional GetVerticalGlyphSub(const TFeatureRecord& feature, ++ uint32_t glyphnum) const; ++ absl::optional GetVerticalGlyphSub2(const TLookup& lookup, ++ uint32_t glyphnum) const; + int GetCoverageIndex(TCoverageFormatBase* Coverage, uint32_t g) const; + + uint8_t GetUInt8(FT_Bytes& p) const; +diff --git a/core/fpdfapi/font/cfx_stockfontarray.cpp b/core/fpdfapi/font/cfx_stockfontarray.cpp +index a8d8597ae..b6793ff3b 100644 +--- a/core/fpdfapi/font/cfx_stockfontarray.cpp ++++ b/core/fpdfapi/font/cfx_stockfontarray.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,19 +6,22 @@ + + #include "core/fpdfapi/font/cfx_stockfontarray.h" + +-#include ++#include + #include + + #include "core/fpdfapi/font/cpdf_font.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" +-#include "core/fxcrt/fx_memory.h" ++#include "third_party/base/notreached.h" + + CFX_StockFontArray::CFX_StockFontArray() = default; + + CFX_StockFontArray::~CFX_StockFontArray() { +- for (size_t i = 0; i < FX_ArraySize(m_StockFonts); ++i) { ++ for (size_t i = 0; i < std::size(m_StockFonts); ++i) { + if (m_StockFonts[i]) { +- RetainPtr destroy(m_StockFonts[i]->GetFontDict()); ++ // Ensure m_StockFonts[i]'s dict is cleared before releasing what ++ // may be the last reference to it. ++ RetainPtr destroy = ++ m_StockFonts[i]->GetMutableFontDict(); + m_StockFonts[i]->ClearFontDict(); + } + } +@@ -26,14 +29,14 @@ CFX_StockFontArray::~CFX_StockFontArray() { + + RetainPtr CFX_StockFontArray::GetFont( + CFX_FontMapper::StandardFont index) const { +- if (index < FX_ArraySize(m_StockFonts)) ++ if (index < std::size(m_StockFonts)) + return m_StockFonts[index]; + NOTREACHED(); + return nullptr; + } + + void CFX_StockFontArray::SetFont(CFX_FontMapper::StandardFont index, +- const RetainPtr& pFont) { +- if (index < FX_ArraySize(m_StockFonts)) +- m_StockFonts[index] = pFont; ++ RetainPtr pFont) { ++ if (index < std::size(m_StockFonts)) ++ m_StockFonts[index] = std::move(pFont); + } +diff --git a/core/fpdfapi/font/cfx_stockfontarray.h b/core/fpdfapi/font/cfx_stockfontarray.h +index 5e5470430..30989d61c 100644 +--- a/core/fpdfapi/font/cfx_stockfontarray.h ++++ b/core/fpdfapi/font/cfx_stockfontarray.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,8 +7,6 @@ + #ifndef CORE_FPDFAPI_FONT_CFX_STOCKFONTARRAY_H_ + #define CORE_FPDFAPI_FONT_CFX_STOCKFONTARRAY_H_ + +-#include +- + #include "core/fxcrt/retain_ptr.h" + #include "core/fxge/cfx_fontmapper.h" + +@@ -20,8 +18,7 @@ class CFX_StockFontArray { + ~CFX_StockFontArray(); + + RetainPtr GetFont(CFX_FontMapper::StandardFont index) const; +- void SetFont(CFX_FontMapper::StandardFont index, +- const RetainPtr& pFont); ++ void SetFont(CFX_FontMapper::StandardFont index, RetainPtr pFont); + + private: + RetainPtr m_StockFonts[14]; +diff --git a/core/fpdfapi/font/cpdf_cid2unicodemap.cpp b/core/fpdfapi/font/cpdf_cid2unicodemap.cpp +index abb23ac59..fa14fe971 100644 +--- a/core/fpdfapi/font/cpdf_cid2unicodemap.cpp ++++ b/core/fpdfapi/font/cpdf_cid2unicodemap.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fpdfapi/font/cpdf_cid2unicodemap.h b/core/fpdfapi/font/cpdf_cid2unicodemap.h +index e556917fd..5c184936e 100644 +--- a/core/fpdfapi/font/cpdf_cid2unicodemap.h ++++ b/core/fpdfapi/font/cpdf_cid2unicodemap.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fpdfapi/font/cpdf_cidfont.cpp b/core/fpdfapi/font/cpdf_cidfont.cpp +index 0c02b9e19..6e0e66510 100644 +--- a/core/fpdfapi/font/cpdf_cidfont.cpp ++++ b/core/fpdfapi/font/cpdf_cidfont.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,6 +8,7 @@ + + #include + #include ++#include + #include + + #include "build/build_config.h" +@@ -20,18 +21,30 @@ + #include "core/fpdfapi/font/cpdf_fontglobals.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" ++#include "core/fxcrt/fx_codepage.h" ++#include "core/fxcrt/fx_memory.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/fx_unicode.h" ++#include "core/fxcrt/stl_util.h" + #include "core/fxge/fx_font.h" +-#include "third_party/base/numerics/safe_math.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/cxx17_backports.h" + #include "third_party/base/span.h" +-#include "third_party/base/stl_util.h" + + namespace { + +-const uint16_t g_CharsetCPs[CIDSET_NUM_SETS] = {0, 936, 950, 932, 949, 1200}; ++constexpr FX_CodePage kCharsetCodePages[CIDSET_NUM_SETS] = { ++ FX_CodePage::kDefANSI, ++ FX_CodePage::kChineseSimplified, ++ FX_CodePage::kChineseTraditional, ++ FX_CodePage::kShiftJIS, ++ FX_CodePage::kHangul, ++ FX_CodePage::kUTF16LE}; + +-const struct CIDTransform { ++struct CIDTransform { + uint16_t cid; + uint8_t a; + uint8_t b; +@@ -39,7 +52,9 @@ const struct CIDTransform { + uint8_t d; + uint8_t e; + uint8_t f; +-} g_Japan1_VertCIDs[] = { ++}; ++ ++constexpr CIDTransform kJapan1VerticalCIDs[] = { + {97, 129, 0, 0, 127, 55, 0}, {7887, 127, 0, 0, 127, 76, 89}, + {7888, 127, 0, 0, 127, 79, 94}, {7889, 0, 129, 127, 0, 17, 127}, + {7890, 0, 129, 127, 0, 17, 127}, {7891, 0, 129, 127, 0, 17, 127}, +@@ -119,14 +134,17 @@ const struct CIDTransform { + {8818, 0, 129, 127, 0, 19, 114}, {8819, 0, 129, 127, 0, 218, 108}, + }; + +-// Boundary values to avoid integer overflow when multiplied by 1000. +-constexpr long kMinCBox = -2147483; +-constexpr long kMaxCBox = 2147483; +- + // Boundary value to avoid integer overflow when adding 1/64th of the value. + constexpr int kMaxRectTop = 2114445437; + +-#if !defined(OS_WIN) ++int FTPosToCBoxInt(FT_Pos pos) { ++ // Boundary values to avoid integer overflow when multiplied by 1000. ++ constexpr FT_Pos kMinCBox = -2147483; ++ constexpr FT_Pos kMaxCBox = 2147483; ++ return static_cast(pdfium::clamp(pos, kMinCBox, kMaxCBox)); ++} ++ ++#if !BUILDFLAG(IS_WIN) + + bool IsValidEmbeddedCharcodeFromUnicodeCharset(CIDSet charset) { + switch (charset) { +@@ -141,13 +159,13 @@ bool IsValidEmbeddedCharcodeFromUnicodeCharset(CIDSet charset) { + } + } + +-wchar_t EmbeddedUnicodeFromCharcode(const FXCMAP_CMap* pEmbedMap, ++wchar_t EmbeddedUnicodeFromCharcode(const fxcmap::CMap* pEmbedMap, + CIDSet charset, + uint32_t charcode) { + if (!IsValidEmbeddedCharcodeFromUnicodeCharset(charset)) + return 0; + +- uint16_t cid = CIDFromCharCode(pEmbedMap, charcode); ++ uint16_t cid = fxcmap::CIDFromCharCode(pEmbedMap, charcode); + if (!cid) + return 0; + +@@ -156,7 +174,7 @@ wchar_t EmbeddedUnicodeFromCharcode(const FXCMAP_CMap* pEmbedMap, + return cid < map.size() ? map[cid] : 0; + } + +-uint32_t EmbeddedCharcodeFromUnicode(const FXCMAP_CMap* pEmbedMap, ++uint32_t EmbeddedCharcodeFromUnicode(const fxcmap::CMap* pEmbedMap, + CIDSet charset, + wchar_t unicode) { + if (!IsValidEmbeddedCharcodeFromUnicodeCharset(charset)) +@@ -166,7 +184,7 @@ uint32_t EmbeddedCharcodeFromUnicode(const FXCMAP_CMap* pEmbedMap, + CPDF_FontGlobals::GetInstance()->GetEmbeddedToUnicode(charset); + for (uint32_t i = 0; i < map.size(); ++i) { + if (map[i] == unicode) { +- uint32_t charCode = CharCodeFromCID(pEmbedMap, i); ++ uint32_t charCode = fxcmap::CharCodeFromCID(pEmbedMap, i); + if (charCode) + return charCode; + } +@@ -174,21 +192,21 @@ uint32_t EmbeddedCharcodeFromUnicode(const FXCMAP_CMap* pEmbedMap, + return 0; + } + +-#endif // !defined(OS_WIN) ++#endif // !BUILDFLAG(IS_WIN) + +-void FT_UseCIDCharmap(FXFT_FaceRec* face, int coding) { ++void FT_UseCIDCharmap(FXFT_FaceRec* face, CIDCoding coding) { + int encoding; + switch (coding) { +- case CIDCODING_GB: ++ case CIDCoding::kGB: + encoding = FT_ENCODING_GB2312; + break; +- case CIDCODING_BIG5: ++ case CIDCoding::kBIG5: + encoding = FT_ENCODING_BIG5; + break; +- case CIDCODING_JIS: ++ case CIDCoding::kJIS: + encoding = FT_ENCODING_SJIS; + break; +- case CIDCODING_KOREA: ++ case CIDCoding::kKOREA: + encoding = FT_ENCODING_JOHAB; + break; + default: +@@ -197,23 +215,76 @@ void FT_UseCIDCharmap(FXFT_FaceRec* face, int coding) { + int err = FXFT_Select_Charmap(face, encoding); + if (err) + err = FXFT_Select_Charmap(face, FT_ENCODING_UNICODE); +- if (err && FXFT_Get_Face_Charmaps(face)) +- FT_Set_Charmap(face, *FXFT_Get_Face_Charmaps(face)); ++ if (err && face->charmaps) ++ FT_Set_Charmap(face, face->charmaps[0]); ++} ++ ++bool IsMetricForCID(const int* pEntry, uint16_t cid) { ++ return pEntry[0] <= cid && pEntry[1] >= cid; + } + +-bool IsMetricForCID(const uint32_t* pEntry, uint16_t CID) { +- return pEntry[0] <= CID && pEntry[1] >= CID; ++void LoadMetricsArray(RetainPtr pArray, ++ std::vector* result, ++ int nElements) { ++ int width_status = 0; ++ int iCurElement = 0; ++ int first_code = 0; ++ int last_code = 0; ++ for (size_t i = 0; i < pArray->size(); i++) { ++ RetainPtr pObj = pArray->GetDirectObjectAt(i); ++ if (!pObj) ++ continue; ++ ++ const CPDF_Array* pObjArray = pObj->AsArray(); ++ if (pObjArray) { ++ if (width_status != 1) ++ return; ++ if (first_code > std::numeric_limits::max() - ++ fxcrt::CollectionSize(*pObjArray)) { ++ width_status = 0; ++ continue; ++ } ++ ++ for (size_t j = 0; j < pObjArray->size(); j += nElements) { ++ result->push_back(first_code); ++ result->push_back(first_code); ++ for (int k = 0; k < nElements; k++) ++ result->push_back(pObjArray->GetIntegerAt(j + k)); ++ first_code++; ++ } ++ width_status = 0; ++ } else { ++ if (width_status == 0) { ++ first_code = pObj->GetInteger(); ++ width_status = 1; ++ } else if (width_status == 1) { ++ last_code = pObj->GetInteger(); ++ width_status = 2; ++ iCurElement = 0; ++ } else { ++ if (!iCurElement) { ++ result->push_back(first_code); ++ result->push_back(last_code); ++ } ++ result->push_back(pObj->GetInteger()); ++ iCurElement++; ++ if (iCurElement == nElements) ++ width_status = 0; ++ } ++ } ++ } + } + + } // namespace + +-CPDF_CIDFont::CPDF_CIDFont(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict) +- : CPDF_Font(pDocument, pFontDict) { +- for (size_t i = 0; i < FX_ArraySize(m_CharBBox); ++i) ++CPDF_CIDFont::CPDF_CIDFont(CPDF_Document* pDocument, ++ RetainPtr pFontDict) ++ : CPDF_Font(pDocument, std::move(pFontDict)) { ++ for (size_t i = 0; i < std::size(m_CharBBox); ++i) + m_CharBBox[i] = FX_RECT(-1, -1, -1, -1); + } + +-CPDF_CIDFont::~CPDF_CIDFont() {} ++CPDF_CIDFont::~CPDF_CIDFont() = default; + + bool CPDF_CIDFont::IsCIDFont() const { + return true; +@@ -241,32 +312,35 @@ WideString CPDF_CIDFont::UnicodeFromCharCode(uint32_t charcode) const { + if (!str.IsEmpty()) + return str; + wchar_t ret = GetUnicodeFromCharCode(charcode); +- return ret ? ret : WideString(); ++ return ret ? WideString(ret) : WideString(); + } + + wchar_t CPDF_CIDFont::GetUnicodeFromCharCode(uint32_t charcode) const { + switch (m_pCMap->GetCoding()) { +- case CIDCODING_UCS2: +- case CIDCODING_UTF16: ++ case CIDCoding::kUCS2: ++ case CIDCoding::kUTF16: + return static_cast(charcode); +- case CIDCODING_CID: ++ case CIDCoding::kCID: + if (!m_pCID2UnicodeMap || !m_pCID2UnicodeMap->IsLoaded()) + return 0; + return m_pCID2UnicodeMap->UnicodeFromCID(static_cast(charcode)); ++ default: ++ break; + } + if (m_pCID2UnicodeMap && m_pCID2UnicodeMap->IsLoaded() && m_pCMap->IsLoaded()) + return m_pCID2UnicodeMap->UnicodeFromCID(CIDFromCharCode(charcode)); + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + wchar_t unicode; + int charsize = 1; + if (charcode > 255) { + charcode = (charcode % 256) * 256 + (charcode / 256); + charsize = 2; + } +- int ret = FXSYS_MultiByteToWideChar(g_CharsetCPs[m_pCMap->GetCoding()], 0, +- reinterpret_cast(&charcode), +- charsize, &unicode, 1); ++ size_t ret = FX_MultiByteToWideChar( ++ kCharsetCodePages[static_cast(m_pCMap->GetCoding())], ++ ByteStringView(reinterpret_cast(&charcode), charsize), ++ pdfium::make_span(&unicode, 1)); + return ret == 1 ? unicode : 0; + #else + if (!m_pCMap->GetEmbedMap()) +@@ -280,36 +354,40 @@ uint32_t CPDF_CIDFont::CharCodeFromUnicode(wchar_t unicode) const { + uint32_t charcode = CPDF_Font::CharCodeFromUnicode(unicode); + if (charcode) + return charcode; ++ + switch (m_pCMap->GetCoding()) { +- case CIDCODING_UNKNOWN: ++ case CIDCoding::kUNKNOWN: + return 0; +- case CIDCODING_UCS2: +- case CIDCODING_UTF16: ++ case CIDCoding::kUCS2: ++ case CIDCoding::kUTF16: + return unicode; +- case CIDCODING_CID: { ++ case CIDCoding::kCID: { + if (!m_pCID2UnicodeMap || !m_pCID2UnicodeMap->IsLoaded()) + return 0; +- uint32_t CID = 0; +- while (CID < 65536) { ++ uint32_t cid = 0; ++ while (cid < 65536) { + wchar_t this_unicode = +- m_pCID2UnicodeMap->UnicodeFromCID(static_cast(CID)); ++ m_pCID2UnicodeMap->UnicodeFromCID(static_cast(cid)); + if (this_unicode == unicode) +- return CID; +- CID++; ++ return cid; ++ cid++; + } + break; + } ++ default: ++ break; + } + + if (unicode < 0x80) + return static_cast(unicode); +- if (m_pCMap->GetCoding() == CIDCODING_CID) ++ if (m_pCMap->GetCoding() == CIDCoding::kCID) + return 0; +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + uint8_t buffer[32]; +- int ret = FXSYS_WideCharToMultiByte( +- g_CharsetCPs[m_pCMap->GetCoding()], 0, &unicode, 1, +- reinterpret_cast(buffer), 4, nullptr, nullptr); ++ size_t ret = FX_WideCharToMultiByte( ++ kCharsetCodePages[static_cast(m_pCMap->GetCoding())], ++ WideStringView(&unicode, 1), ++ pdfium::make_span(reinterpret_cast(buffer), 4)); + if (ret == 1) + return buffer[0]; + if (ret == 2) +@@ -324,97 +402,116 @@ uint32_t CPDF_CIDFont::CharCodeFromUnicode(wchar_t unicode) const { + } + + bool CPDF_CIDFont::Load() { +- if (m_pFontDict->GetStringFor("Subtype") == "TrueType") { ++ if (m_pFontDict->GetByteStringFor("Subtype") == "TrueType") { + LoadGB2312(); + return true; + } + +- const CPDF_Array* pFonts = m_pFontDict->GetArrayFor("DescendantFonts"); ++ RetainPtr pFonts = ++ m_pFontDict->GetArrayFor("DescendantFonts"); + if (!pFonts || pFonts->size() != 1) + return false; + +- const CPDF_Dictionary* pCIDFontDict = pFonts->GetDictAt(0); ++ RetainPtr pCIDFontDict = pFonts->GetDictAt(0); + if (!pCIDFontDict) + return false; + +- m_BaseFontName = pCIDFontDict->GetStringFor("BaseFont"); +- if ((m_BaseFontName.Compare("CourierStd") == 0 || +- m_BaseFontName.Compare("CourierStd-Bold") == 0 || +- m_BaseFontName.Compare("CourierStd-BoldOblique") == 0 || +- m_BaseFontName.Compare("CourierStd-Oblique") == 0) && ++ m_BaseFontName = pCIDFontDict->GetByteStringFor("BaseFont"); ++ if ((m_BaseFontName == "CourierStd" || m_BaseFontName == "CourierStd-Bold" || ++ m_BaseFontName == "CourierStd-BoldOblique" || ++ m_BaseFontName == "CourierStd-Oblique") && + !IsEmbedded()) { + m_bAdobeCourierStd = true; + } + +- CPDF_Object* pEncoding = m_pFontDict->GetDirectObjectFor("Encoding"); ++ RetainPtr pEncoding = ++ m_pFontDict->GetDirectObjectFor("Encoding"); + if (!pEncoding) + return false; + +- ByteString subtype = pCIDFontDict->GetStringFor("Subtype"); +- m_bType1 = (subtype == "CIDFontType0"); ++ ByteString subtype = pCIDFontDict->GetByteStringFor("Subtype"); ++ m_FontType = ++ subtype == "CIDFontType0" ? CIDFontType::kType1 : CIDFontType::kTrueType; + +- CPDF_CMapManager* manager = CPDF_FontGlobals::GetInstance()->GetCMapManager(); +- if (pEncoding->IsName()) { +- ByteString cmap = pEncoding->GetString(); +- m_pCMap = manager->GetPredefinedCMap(cmap); +- } else if (CPDF_Stream* pStream = pEncoding->AsStream()) { +- auto pAcc = pdfium::MakeRetain(pStream); ++ if (!pEncoding->IsName() && !pEncoding->IsStream()) ++ return false; ++ ++ auto* pFontGlobals = CPDF_FontGlobals::GetInstance(); ++ const CPDF_Stream* pEncodingStream = pEncoding->AsStream(); ++ if (pEncodingStream) { ++ auto pAcc = ++ pdfium::MakeRetain(pdfium::WrapRetain(pEncodingStream)); + pAcc->LoadAllDataFiltered(); + pdfium::span span = pAcc->GetSpan(); + m_pCMap = pdfium::MakeRetain(span); + } else { +- return false; ++ DCHECK(pEncoding->IsName()); ++ ByteString cmap = pEncoding->GetString(); ++ m_pCMap = pFontGlobals->GetPredefinedCMap(cmap); + } + +- const CPDF_Dictionary* pFontDesc = pCIDFontDict->GetDictFor("FontDescriptor"); ++ RetainPtr pFontDesc = ++ pCIDFontDict->GetDictFor("FontDescriptor"); + if (pFontDesc) +- LoadFontDescriptor(pFontDesc); ++ LoadFontDescriptor(pFontDesc.Get()); + + m_Charset = m_pCMap->GetCharset(); + if (m_Charset == CIDSET_UNKNOWN) { +- const CPDF_Dictionary* pCIDInfo = pCIDFontDict->GetDictFor("CIDSystemInfo"); ++ RetainPtr pCIDInfo = ++ pCIDFontDict->GetDictFor("CIDSystemInfo"); + if (pCIDInfo) { + m_Charset = CPDF_CMapParser::CharsetFromOrdering( +- pCIDInfo->GetStringFor("Ordering").AsStringView()); ++ pCIDInfo->GetByteStringFor("Ordering").AsStringView()); + } + } + if (m_Charset != CIDSET_UNKNOWN) { +- m_pCID2UnicodeMap = manager->GetCID2UnicodeMap(m_Charset); ++ m_pCID2UnicodeMap = pFontGlobals->GetCID2UnicodeMap(m_Charset); + } + if (m_Font.GetFaceRec()) { +- if (m_bType1) ++ if (m_FontType == CIDFontType::kType1) + FXFT_Select_Charmap(m_Font.GetFaceRec(), FT_ENCODING_UNICODE); + else + FT_UseCIDCharmap(m_Font.GetFaceRec(), m_pCMap->GetCoding()); + } + m_DefaultWidth = pCIDFontDict->GetIntegerFor("DW", 1000); +- const CPDF_Array* pWidthArray = pCIDFontDict->GetArrayFor("W"); ++ RetainPtr pWidthArray = pCIDFontDict->GetArrayFor("W"); + if (pWidthArray) +- LoadMetricsArray(pWidthArray, &m_WidthList, 1); ++ LoadMetricsArray(std::move(pWidthArray), &m_WidthList, 1); ++ + if (!IsEmbedded()) + LoadSubstFont(); + +- const CPDF_Object* pmap = pCIDFontDict->GetDirectObjectFor("CIDToGIDMap"); ++ RetainPtr pmap = ++ pCIDFontDict->GetDirectObjectFor("CIDToGIDMap"); + if (pmap) { +- if (const CPDF_Stream* pStream = pmap->AsStream()) { +- m_pStreamAcc = pdfium::MakeRetain(pStream); ++ RetainPtr pMapStream(pmap->AsStream()); ++ if (pMapStream) { ++ m_pStreamAcc = pdfium::MakeRetain(std::move(pMapStream)); + m_pStreamAcc->LoadAllDataFiltered(); +- } else if (m_pFontFile && pmap->GetString() == "Identity") { ++ } else if (m_pFontFile && pmap->IsName() && ++ pmap->GetString() == "Identity") { + m_bCIDIsGID = true; + } + } + + CheckFontMetrics(); + if (IsVertWriting()) { +- pWidthArray = pCIDFontDict->GetArrayFor("W2"); +- if (pWidthArray) +- LoadMetricsArray(pWidthArray, &m_VertMetrics, 3); +- const CPDF_Array* pDefaultArray = pCIDFontDict->GetArrayFor("DW2"); ++ RetainPtr pWidth2Array = pCIDFontDict->GetArrayFor("W2"); ++ if (pWidth2Array) ++ LoadMetricsArray(std::move(pWidth2Array), &m_VertMetrics, 3); ++ ++ RetainPtr pDefaultArray = ++ pCIDFontDict->GetArrayFor("DW2"); + if (pDefaultArray) { + m_DefaultVY = pDefaultArray->GetIntegerAt(0); + m_DefaultW1 = pDefaultArray->GetIntegerAt(1); + } + } ++ ++ // TODO(thestig): Better identify font types and identify more font types. ++ if (m_FontType == CIDFontType::kTrueType && IsEmbedded()) ++ m_Font.SetFontType(CFX_Font::FontType::kCIDTrueType); ++ + return true; + } + +@@ -436,19 +533,18 @@ FX_RECT CPDF_CIDFont::GetCharBBox(uint32_t charcode) { + if (!err) { + FT_BBox cbox; + FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &cbox); +- cbox.xMin = pdfium::clamp(cbox.xMin, kMinCBox, kMaxCBox); +- cbox.xMax = pdfium::clamp(cbox.xMax, kMinCBox, kMaxCBox); +- cbox.yMin = pdfium::clamp(cbox.yMin, kMinCBox, kMaxCBox); +- cbox.yMax = pdfium::clamp(cbox.yMax, kMinCBox, kMaxCBox); +- int pixel_size_x = face->size->metrics.x_ppem; +- int pixel_size_y = face->size->metrics.y_ppem; ++ const int xMin = FTPosToCBoxInt(cbox.xMin); ++ const int xMax = FTPosToCBoxInt(cbox.xMax); ++ const int yMin = FTPosToCBoxInt(cbox.yMin); ++ const int yMax = FTPosToCBoxInt(cbox.yMax); ++ const int pixel_size_x = face->size->metrics.x_ppem; ++ const int pixel_size_y = face->size->metrics.y_ppem; + if (pixel_size_x == 0 || pixel_size_y == 0) { +- rect = FX_RECT(cbox.xMin, cbox.yMax, cbox.xMax, cbox.yMin); ++ rect = FX_RECT(xMin, yMax, xMax, yMin); + } else { +- rect = FX_RECT(cbox.xMin * 1000 / pixel_size_x, +- cbox.yMax * 1000 / pixel_size_y, +- cbox.xMax * 1000 / pixel_size_x, +- cbox.yMin * 1000 / pixel_size_y); ++ rect = ++ FX_RECT(xMin * 1000 / pixel_size_x, yMax * 1000 / pixel_size_y, ++ xMax * 1000 / pixel_size_x, yMin * 1000 / pixel_size_y); + } + rect.top = std::min(rect.top, + static_cast(FXFT_Get_Face_Ascender(face))); +@@ -476,8 +572,8 @@ FX_RECT CPDF_CIDFont::GetCharBBox(uint32_t charcode) { + } + } + if (!m_pFontFile && m_Charset == CIDSET_JAPAN1) { +- uint16_t CID = CIDFromCharCode(charcode); +- const uint8_t* pTransform = GetCIDTransform(CID); ++ uint16_t cid = CIDFromCharCode(charcode); ++ const uint8_t* pTransform = GetCIDTransform(cid); + if (pTransform && !bVert) { + CFX_Matrix matrix(CIDTransformToFloat(pTransform[0]), + CIDTransformToFloat(pTransform[1]), +@@ -494,59 +590,57 @@ FX_RECT CPDF_CIDFont::GetCharBBox(uint32_t charcode) { + return rect; + } + +-uint32_t CPDF_CIDFont::GetCharWidthF(uint32_t charcode) { ++int CPDF_CIDFont::GetCharWidthF(uint32_t charcode) { + if (charcode < 0x80 && m_bAnsiWidthsFixed) + return (charcode >= 32 && charcode < 127) ? 500 : 0; + + uint16_t cid = CIDFromCharCode(charcode); + size_t size = m_WidthList.size(); +- const uint32_t* pList = m_WidthList.data(); ++ const int* pList = m_WidthList.data(); + for (size_t i = 0; i < size; i += 3) { +- const uint32_t* pEntry = pList + i; ++ const int* pEntry = pList + i; + if (IsMetricForCID(pEntry, cid)) + return pEntry[2]; + } + return m_DefaultWidth; + } + +-short CPDF_CIDFont::GetVertWidth(uint16_t CID) const { ++int16_t CPDF_CIDFont::GetVertWidth(uint16_t cid) const { + size_t vertsize = m_VertMetrics.size() / 5; + if (vertsize) { +- const uint32_t* pTable = m_VertMetrics.data(); ++ const int* pTable = m_VertMetrics.data(); + for (size_t i = 0; i < vertsize; i++) { +- const uint32_t* pEntry = pTable + (i * 5); +- if (IsMetricForCID(pEntry, CID)) +- return static_cast(pEntry[2]); ++ const int* pEntry = pTable + (i * 5); ++ if (IsMetricForCID(pEntry, cid)) ++ return static_cast(pEntry[2]); + } + } + return m_DefaultW1; + } + +-void CPDF_CIDFont::GetVertOrigin(uint16_t CID, short& vx, short& vy) const { ++CFX_Point16 CPDF_CIDFont::GetVertOrigin(uint16_t cid) const { + size_t vertsize = m_VertMetrics.size() / 5; + if (vertsize) { +- const uint32_t* pTable = m_VertMetrics.data(); ++ const int* pTable = m_VertMetrics.data(); + for (size_t i = 0; i < vertsize; i++) { +- const uint32_t* pEntry = pTable + (i * 5); +- if (IsMetricForCID(pEntry, CID)) { +- vx = static_cast(pEntry[3]); +- vy = static_cast(pEntry[4]); +- return; ++ const int* pEntry = pTable + (i * 5); ++ if (IsMetricForCID(pEntry, cid)) { ++ return {static_cast(pEntry[3]), ++ static_cast(pEntry[4])}; + } + } + } +- uint32_t dwWidth = m_DefaultWidth; ++ int width = m_DefaultWidth; + size_t size = m_WidthList.size(); +- const uint32_t* pList = m_WidthList.data(); ++ const int* pList = m_WidthList.data(); + for (size_t i = 0; i < size; i += 3) { +- const uint32_t* pEntry = pList + i; +- if (IsMetricForCID(pEntry, CID)) { +- dwWidth = pEntry[2]; ++ const int* pEntry = pList + i; ++ if (IsMetricForCID(pEntry, cid)) { ++ width = pEntry[2]; + break; + } + } +- vx = static_cast(dwWidth) / 2; +- vy = m_DefaultVY; ++ return {static_cast(width / 2), m_DefaultVY}; + } + + int CPDF_CIDFont::GetGlyphIndex(uint32_t unicode, bool* pVertGlyph) { +@@ -555,7 +649,7 @@ int CPDF_CIDFont::GetGlyphIndex(uint32_t unicode, bool* pVertGlyph) { + + FXFT_FaceRec* face = m_Font.GetFaceRec(); + int index = FT_Get_Char_Index(face, unicode); +- if (unicode == 0x2502) ++ if (unicode == pdfium::unicode::kBoxDrawingsLightVerical) + return index; + + if (!index || !IsVertWriting()) +@@ -564,19 +658,20 @@ int CPDF_CIDFont::GetGlyphIndex(uint32_t unicode, bool* pVertGlyph) { + if (m_pTTGSUBTable) + return GetVerticalGlyph(index, pVertGlyph); + ++ static constexpr uint32_t kGsubTag = ++ CFX_FontMapper::MakeTag('G', 'S', 'U', 'B'); + if (!m_Font.GetSubData()) { + unsigned long length = 0; +- int error = FT_Load_Sfnt_Table(face, FT_MAKE_TAG('G', 'S', 'U', 'B'), 0, +- nullptr, &length); ++ int error = FT_Load_Sfnt_Table(face, kGsubTag, 0, nullptr, &length); + if (!error) +- m_Font.SetSubData(FX_Alloc(uint8_t, length)); ++ m_Font.AllocSubData(length); + } +- int error = FT_Load_Sfnt_Table(face, FT_MAKE_TAG('G', 'S', 'U', 'B'), 0, +- m_Font.GetSubData(), nullptr); ++ int error = ++ FT_Load_Sfnt_Table(face, kGsubTag, 0, m_Font.GetSubData(), nullptr); + if (error || !m_Font.GetSubData()) + return index; + +- m_pTTGSUBTable = pdfium::MakeUnique(m_Font.GetSubData()); ++ m_pTTGSUBTable = std::make_unique(m_Font.GetSubData()); + return GetVerticalGlyph(index, pVertGlyph); + } + +@@ -599,7 +694,7 @@ int CPDF_CIDFont::GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) { + uint16_t cid = CIDFromCharCode(charcode); + wchar_t unicode = 0; + if (m_bCIDIsGID) { +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + if (FontStyleIsSymbolic(m_Flags)) + return cid; + +@@ -628,34 +723,34 @@ int CPDF_CIDFont::GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) { + return charcode ? static_cast(charcode) : -1; + + charcode += 31; +- bool bMSUnicode = FT_UseTTCharmap(face, 3, 1); +- bool bMacRoman = !bMSUnicode && FT_UseTTCharmap(face, 1, 0); +- int iBaseEncoding = PDFFONT_ENCODING_STANDARD; ++ bool bMSUnicode = UseTTCharmapMSUnicode(face); ++ bool bMacRoman = !bMSUnicode && UseTTCharmapMacRoman(face); ++ FontEncoding base_encoding = FontEncoding::kStandard; + if (bMSUnicode) +- iBaseEncoding = PDFFONT_ENCODING_WINANSI; ++ base_encoding = FontEncoding::kWinAnsi; + else if (bMacRoman) +- iBaseEncoding = PDFFONT_ENCODING_MACROMAN; ++ base_encoding = FontEncoding::kMacRoman; + const char* name = +- GetAdobeCharName(iBaseEncoding, std::vector(), charcode); ++ GetAdobeCharName(base_encoding, std::vector(), charcode); + if (!name) + return charcode ? static_cast(charcode) : -1; + + int index = 0; +- uint16_t name_unicode = PDF_UnicodeFromAdobeName(name); ++ uint16_t name_unicode = UnicodeFromAdobeName(name); + if (!name_unicode) + return charcode ? static_cast(charcode) : -1; + +- if (iBaseEncoding == PDFFONT_ENCODING_STANDARD) ++ if (base_encoding == FontEncoding::kStandard) + return FT_Get_Char_Index(face, name_unicode); + +- if (iBaseEncoding == PDFFONT_ENCODING_WINANSI) { ++ if (base_encoding == FontEncoding::kWinAnsi) { + index = FT_Get_Char_Index(face, name_unicode); + } else { +- ASSERT(iBaseEncoding == PDFFONT_ENCODING_MACROMAN); +- uint32_t maccode = +- FT_CharCodeFromUnicode(FT_ENCODING_APPLE_ROMAN, name_unicode); ++ DCHECK_EQ(base_encoding, FontEncoding::kMacRoman); ++ uint32_t maccode = CharCodeFromUnicodeForFreetypeEncoding( ++ FT_ENCODING_APPLE_ROMAN, name_unicode); + index = maccode ? FT_Get_Char_Index(face, maccode) +- : FXFT_Get_Name_Index(face, name); ++ : FT_Get_Name_Index(face, name); + } + if (index == 0 || index == 0xffff) + return charcode ? static_cast(charcode) : -1; +@@ -664,7 +759,7 @@ int CPDF_CIDFont::GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) { + if (m_Charset == CIDSET_JAPAN1) { + if (unicode == '\\') { + unicode = '/'; +-#if !defined(OS_MACOSX) ++#if !BUILDFLAG(IS_APPLE) + } else if (unicode == 0xa5) { + unicode = 0x5c; + #endif +@@ -676,22 +771,22 @@ int CPDF_CIDFont::GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) { + int err = FXFT_Select_Charmap(face, FT_ENCODING_UNICODE); + if (err) { + int i; +- for (i = 0; i < FXFT_Get_Face_CharmapCount(face); i++) { +- uint32_t ret = FT_CharCodeFromUnicode( +- FXFT_Get_Charmap_Encoding(FXFT_Get_Face_Charmaps(face)[i]), ++ for (i = 0; i < face->num_charmaps; i++) { ++ uint32_t ret = CharCodeFromUnicodeForFreetypeEncoding( ++ FXFT_Get_Charmap_Encoding(face->charmaps[i]), + static_cast(charcode)); + if (ret == 0) + continue; +- FT_Set_Charmap(face, FXFT_Get_Face_Charmaps(face)[i]); ++ FT_Set_Charmap(face, face->charmaps[i]); + unicode = static_cast(ret); + break; + } +- if (i == FXFT_Get_Face_CharmapCount(face) && i) { +- FT_Set_Charmap(face, FXFT_Get_Face_Charmaps(face)[0]); ++ if (i == face->num_charmaps && i) { ++ FT_Set_Charmap(face, face->charmaps[0]); + unicode = static_cast(charcode); + } + } +- if (FXFT_Get_Face_Charmap(face)) { ++ if (face->charmap) { + int index = GetGlyphIndex(unicode, pVertGlyph); + return index != 0 ? index : -1; + } +@@ -703,16 +798,16 @@ int CPDF_CIDFont::GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) { + + uint16_t cid = CIDFromCharCode(charcode); + if (!m_pStreamAcc) { +- if (m_bType1) ++ if (m_FontType == CIDFontType::kType1) + return cid; + if (m_pFontFile && m_pCMap->IsDirectCharcodeToCIDTableIsEmpty()) + return cid; +- if (m_pCMap->GetCoding() == CIDCODING_UNKNOWN || +- !FXFT_Get_Face_Charmap(m_Font.GetFaceRec())) { ++ ++ FT_CharMap charmap = m_Font.GetFaceRec()->charmap; ++ if (!charmap || m_pCMap->GetCoding() == CIDCoding::kUNKNOWN) + return cid; +- } +- if (FXFT_Get_Charmap_Encoding(FXFT_Get_Face_Charmap(m_Font.GetFaceRec())) == +- FT_ENCODING_UNICODE) { ++ ++ if (FXFT_Get_Charmap_Encoding(charmap) == FT_ENCODING_UNICODE) { + WideString unicode_str = UnicodeFromCharCode(charcode); + if (unicode_str.IsEmpty()) + return -1; +@@ -725,8 +820,8 @@ int CPDF_CIDFont::GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) { + if (byte_pos + 2 > m_pStreamAcc->GetSize()) + return -1; + +- const uint8_t* pdata = m_pStreamAcc->GetData() + byte_pos; +- return pdata[0] * 256 + pdata[1]; ++ pdfium::span span = m_pStreamAcc->GetSpan().subspan(byte_pos); ++ return span[0] * 256 + span[1]; + } + + uint32_t CPDF_CIDFont::GetNextChar(ByteStringView pString, +@@ -749,66 +844,16 @@ int CPDF_CIDFont::AppendChar(char* str, uint32_t charcode) const { + bool CPDF_CIDFont::IsUnicodeCompatible() const { + if (m_pCID2UnicodeMap && m_pCID2UnicodeMap->IsLoaded() && m_pCMap->IsLoaded()) + return true; +- return m_pCMap->GetCoding() != CIDCODING_UNKNOWN; ++ return m_pCMap->GetCoding() != CIDCoding::kUNKNOWN; + } + + void CPDF_CIDFont::LoadSubstFont() { +- pdfium::base::CheckedNumeric safeStemV(m_StemV); ++ FX_SAFE_INT32 safeStemV(m_StemV); + safeStemV *= 5; +- m_Font.LoadSubst(m_BaseFontName, !m_bType1, m_Flags, +- safeStemV.ValueOrDefault(FXFONT_FW_NORMAL), m_ItalicAngle, +- g_CharsetCPs[m_Charset], IsVertWriting()); +-} +- +-void CPDF_CIDFont::LoadMetricsArray(const CPDF_Array* pArray, +- std::vector* result, +- int nElements) { +- int width_status = 0; +- int iCurElement = 0; +- uint32_t first_code = 0; +- uint32_t last_code = 0; +- for (size_t i = 0; i < pArray->size(); i++) { +- const CPDF_Object* pObj = pArray->GetDirectObjectAt(i); +- if (!pObj) +- continue; +- +- if (const CPDF_Array* pObjArray = pObj->AsArray()) { +- if (width_status != 1) +- return; +- if (first_code > +- std::numeric_limits::max() - pObjArray->size()) { +- width_status = 0; +- continue; +- } +- +- for (size_t j = 0; j < pObjArray->size(); j += nElements) { +- result->push_back(first_code); +- result->push_back(first_code); +- for (int k = 0; k < nElements; k++) +- result->push_back(pObjArray->GetIntegerAt(j + k)); +- first_code++; +- } +- width_status = 0; +- } else { +- if (width_status == 0) { +- first_code = pObj->GetInteger(); +- width_status = 1; +- } else if (width_status == 1) { +- last_code = pObj->GetInteger(); +- width_status = 2; +- iCurElement = 0; +- } else { +- if (!iCurElement) { +- result->push_back(first_code); +- result->push_back(last_code); +- } +- result->push_back(pObj->GetInteger()); +- iCurElement++; +- if (iCurElement == nElements) +- width_status = 0; +- } +- } +- } ++ m_Font.LoadSubst(m_BaseFontName, m_FontType == CIDFontType::kTrueType, ++ m_Flags, safeStemV.ValueOrDefault(FXFONT_FW_NORMAL), ++ m_ItalicAngle, kCharsetCodePages[m_Charset], ++ IsVertWriting()); + } + + // static +@@ -817,15 +862,16 @@ float CPDF_CIDFont::CIDTransformToFloat(uint8_t ch) { + } + + void CPDF_CIDFont::LoadGB2312() { +- m_BaseFontName = m_pFontDict->GetStringFor("BaseFont"); ++ m_BaseFontName = m_pFontDict->GetByteStringFor("BaseFont"); + m_Charset = CIDSET_GB1; + +- CPDF_CMapManager* manager = CPDF_FontGlobals::GetInstance()->GetCMapManager(); +- m_pCMap = manager->GetPredefinedCMap("GBK-EUC-H"); +- m_pCID2UnicodeMap = manager->GetCID2UnicodeMap(m_Charset); +- const CPDF_Dictionary* pFontDesc = m_pFontDict->GetDictFor("FontDescriptor"); ++ auto* pFontGlobals = CPDF_FontGlobals::GetInstance(); ++ m_pCMap = pFontGlobals->GetPredefinedCMap("GBK-EUC-H"); ++ m_pCID2UnicodeMap = pFontGlobals->GetCID2UnicodeMap(m_Charset); ++ RetainPtr pFontDesc = ++ m_pFontDict->GetDictFor("FontDescriptor"); + if (pFontDesc) +- LoadFontDescriptor(pFontDesc); ++ LoadFontDescriptor(pFontDesc.Get()); + + if (!IsEmbedded()) + LoadSubstFont(); +@@ -833,14 +879,14 @@ void CPDF_CIDFont::LoadGB2312() { + m_bAnsiWidthsFixed = true; + } + +-const uint8_t* CPDF_CIDFont::GetCIDTransform(uint16_t CID) const { ++const uint8_t* CPDF_CIDFont::GetCIDTransform(uint16_t cid) const { + if (m_Charset != CIDSET_JAPAN1 || m_pFontFile) + return nullptr; + +- const auto* pEnd = g_Japan1_VertCIDs + FX_ArraySize(g_Japan1_VertCIDs); ++ const auto* pEnd = kJapan1VerticalCIDs + std::size(kJapan1VerticalCIDs); + const auto* pTransform = std::lower_bound( +- g_Japan1_VertCIDs, pEnd, CID, ++ kJapan1VerticalCIDs, pEnd, cid, + [](const CIDTransform& entry, uint16_t cid) { return entry.cid < cid; }); +- return (pTransform < pEnd && CID == pTransform->cid) ? &pTransform->a ++ return (pTransform < pEnd && cid == pTransform->cid) ? &pTransform->a + : nullptr; + } +diff --git a/core/fpdfapi/font/cpdf_cidfont.h b/core/fpdfapi/font/cpdf_cidfont.h +index ce00e1dd3..fe270a21c 100644 +--- a/core/fpdfapi/font/cpdf_cidfont.h ++++ b/core/fpdfapi/font/cpdf_cidfont.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,14 @@ + #ifndef CORE_FPDFAPI_FONT_CPDF_CIDFONT_H_ + #define CORE_FPDFAPI_FONT_CPDF_CIDFONT_H_ + ++#include ++ + #include + #include + + #include "core/fpdfapi/font/cpdf_font.h" ++#include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + +@@ -27,16 +29,13 @@ enum CIDSet : uint8_t { + }; + + class CFX_CTTGSUBTable; +-class CPDF_Array; + class CPDF_CID2UnicodeMap; + class CPDF_CMap; + class CPDF_StreamAcc; + + class CPDF_CIDFont final : public CPDF_Font { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- ++ CONSTRUCT_VIA_MAKE_RETAIN; + ~CPDF_CIDFont() override; + + static float CIDTransformToFloat(uint8_t ch); +@@ -46,7 +45,7 @@ class CPDF_CIDFont final : public CPDF_Font { + const CPDF_CIDFont* AsCIDFont() const override; + CPDF_CIDFont* AsCIDFont() override; + int GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) override; +- uint32_t GetCharWidthF(uint32_t charcode) override; ++ int GetCharWidthF(uint32_t charcode) override; + FX_RECT GetCharBBox(uint32_t charcode) override; + uint32_t GetNextChar(ByteStringView pString, size_t* pOffset) const override; + size_t CountChar(ByteStringView pString) const override; +@@ -58,20 +57,22 @@ class CPDF_CIDFont final : public CPDF_Font { + uint32_t CharCodeFromUnicode(wchar_t Unicode) const override; + + uint16_t CIDFromCharCode(uint32_t charcode) const; +- const uint8_t* GetCIDTransform(uint16_t CID) const; +- short GetVertWidth(uint16_t CID) const; +- void GetVertOrigin(uint16_t CID, short& vx, short& vy) const; ++ const uint8_t* GetCIDTransform(uint16_t cid) const; ++ int16_t GetVertWidth(uint16_t cid) const; ++ CFX_Point16 GetVertOrigin(uint16_t cid) const; + int GetCharSize(uint32_t charcode) const; + + private: +- CPDF_CIDFont(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict); ++ enum class CIDFontType : bool { ++ kType1, // CIDFontType0 ++ kTrueType // CIDFontType2 ++ }; ++ ++ CPDF_CIDFont(CPDF_Document* pDocument, RetainPtr pFontDict); + + void LoadGB2312(); + int GetGlyphIndex(uint32_t unicodeb, bool* pVertGlyph); + int GetVerticalGlyph(int index, bool* pVertGlyph); +- void LoadMetricsArray(const CPDF_Array* pArray, +- std::vector* result, +- int nElements); + void LoadSubstFont(); + wchar_t GetUnicodeFromCharCode(uint32_t charcode) const; + +@@ -79,16 +80,16 @@ class CPDF_CIDFont final : public CPDF_Font { + UnownedPtr m_pCID2UnicodeMap; + RetainPtr m_pStreamAcc; + std::unique_ptr m_pTTGSUBTable; +- bool m_bType1 = false; ++ CIDFontType m_FontType = CIDFontType::kTrueType; + bool m_bCIDIsGID = false; + bool m_bAnsiWidthsFixed = false; + bool m_bAdobeCourierStd = false; + CIDSet m_Charset = CIDSET_UNKNOWN; +- uint16_t m_DefaultWidth = 1000; +- short m_DefaultVY = 880; +- short m_DefaultW1 = -1000; +- std::vector m_WidthList; +- std::vector m_VertMetrics; ++ int16_t m_DefaultWidth = 1000; ++ int16_t m_DefaultVY = 880; ++ int16_t m_DefaultW1 = -1000; ++ std::vector m_WidthList; ++ std::vector m_VertMetrics; + FX_RECT m_CharBBox[256]; + }; + +diff --git a/core/fpdfapi/font/cpdf_cidfont_unittest.cpp b/core/fpdfapi/font/cpdf_cidfont_unittest.cpp +index 225ea3a1b..4bc74261d 100644 +--- a/core/fpdfapi/font/cpdf_cidfont_unittest.cpp ++++ b/core/fpdfapi/font/cpdf_cidfont_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,24 +6,18 @@ + + #include + +-#include "core/fpdfapi/page/cpdf_docpagedata.h" +-#include "core/fpdfapi/page/cpdf_pagemodule.h" ++#include "core/fpdfapi/page/test_with_page_module.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_name.h" +-#include "core/fpdfapi/render/cpdf_docrenderdata.h" ++#include "core/fpdfapi/parser/cpdf_test_document.h" + #include "testing/gtest/include/gtest/gtest.h" + +-class CPDF_CIDFontTest : public testing::Test { +- protected: +- void SetUp() override { CPDF_PageModule::Create(); } +- void TearDown() override { CPDF_PageModule::Destroy(); } +-}; ++using CPDF_CIDFontTest = TestWithPageModule; + + TEST_F(CPDF_CIDFontTest, BUG_920636) { +- CPDF_Document doc(pdfium::MakeUnique(), +- pdfium::MakeUnique()); ++ CPDF_TestDocument doc; + auto font_dict = pdfium::MakeRetain(); + font_dict->SetNewFor("Encoding", "Identity−H"); + +@@ -32,12 +26,12 @@ TEST_F(CPDF_CIDFontTest, BUG_920636) { + { + auto descendant_font = pdfium::MakeRetain(); + descendant_font->SetNewFor("BaseFont", "CourierStd"); +- descendant_fonts->Add(std::move(descendant_font)); ++ descendant_fonts->Append(std::move(descendant_font)); + } + font_dict->SetFor("DescendantFonts", std::move(descendant_fonts)); + } + +- auto font = pdfium::MakeRetain(&doc, font_dict.Get()); ++ auto font = pdfium::MakeRetain(&doc, std::move(font_dict)); + ASSERT_TRUE(font->Load()); + + // It would be nice if we can test more values here. However, the glyph +diff --git a/core/fpdfapi/font/cpdf_cmap.cpp b/core/fpdfapi/font/cpdf_cmap.cpp +index 844bc5f2a..d5fa61b5e 100644 +--- a/core/fpdfapi/font/cpdf_cmap.cpp ++++ b/core/fpdfapi/font/cpdf_cmap.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,6 @@ + + #include "core/fpdfapi/font/cpdf_cmap.h" + +-#include + #include + #include + +@@ -14,6 +13,7 @@ + #include "core/fpdfapi/font/cpdf_cmapparser.h" + #include "core/fpdfapi/font/cpdf_fontglobals.h" + #include "core/fpdfapi/parser/cpdf_simple_parser.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -34,151 +34,176 @@ struct PredefinedCMap { + constexpr PredefinedCMap kPredefinedCMaps[] = { + {"GB-EUC", + CIDSET_GB1, +- CIDCODING_GB, ++ CIDCoding::kGB, + CPDF_CMap::MixedTwoBytes, + 1, + {{0xa1, 0xfe}}}, + {"GBpc-EUC", + CIDSET_GB1, +- CIDCODING_GB, ++ CIDCoding::kGB, + CPDF_CMap::MixedTwoBytes, + 1, + {{0xa1, 0xfc}}}, + {"GBK-EUC", + CIDSET_GB1, +- CIDCODING_GB, ++ CIDCoding::kGB, + CPDF_CMap::MixedTwoBytes, + 1, + {{0x81, 0xfe}}}, + {"GBKp-EUC", + CIDSET_GB1, +- CIDCODING_GB, ++ CIDCoding::kGB, + CPDF_CMap::MixedTwoBytes, + 1, + {{0x81, 0xfe}}}, + {"GBK2K-EUC", + CIDSET_GB1, +- CIDCODING_GB, ++ CIDCoding::kGB, + CPDF_CMap::MixedTwoBytes, + 1, + {{0x81, 0xfe}}}, + {"GBK2K", + CIDSET_GB1, +- CIDCODING_GB, ++ CIDCoding::kGB, + CPDF_CMap::MixedTwoBytes, + 1, + {{0x81, 0xfe}}}, +- {"UniGB-UCS2", CIDSET_GB1, CIDCODING_UCS2, CPDF_CMap::TwoBytes, 0, {}}, +- {"UniGB-UTF16", CIDSET_GB1, CIDCODING_UTF16, CPDF_CMap::TwoBytes, 0, {}}, ++ {"UniGB-UCS2", CIDSET_GB1, CIDCoding::kUCS2, CPDF_CMap::TwoBytes, 0, {}}, ++ {"UniGB-UTF16", CIDSET_GB1, CIDCoding::kUTF16, CPDF_CMap::TwoBytes, 0, {}}, + {"B5pc", + CIDSET_CNS1, +- CIDCODING_BIG5, ++ CIDCoding::kBIG5, + CPDF_CMap::MixedTwoBytes, + 1, + {{0xa1, 0xfc}}}, + {"HKscs-B5", + CIDSET_CNS1, +- CIDCODING_BIG5, ++ CIDCoding::kBIG5, + CPDF_CMap::MixedTwoBytes, + 1, + {{0x88, 0xfe}}}, + {"ETen-B5", + CIDSET_CNS1, +- CIDCODING_BIG5, ++ CIDCoding::kBIG5, + CPDF_CMap::MixedTwoBytes, + 1, + {{0xa1, 0xfe}}}, + {"ETenms-B5", + CIDSET_CNS1, +- CIDCODING_BIG5, ++ CIDCoding::kBIG5, + CPDF_CMap::MixedTwoBytes, + 1, + {{0xa1, 0xfe}}}, +- {"UniCNS-UCS2", CIDSET_CNS1, CIDCODING_UCS2, CPDF_CMap::TwoBytes, 0, {}}, +- {"UniCNS-UTF16", CIDSET_CNS1, CIDCODING_UTF16, CPDF_CMap::TwoBytes, 0, {}}, ++ {"UniCNS-UCS2", CIDSET_CNS1, CIDCoding::kUCS2, CPDF_CMap::TwoBytes, 0, {}}, ++ {"UniCNS-UTF16", ++ CIDSET_CNS1, ++ CIDCoding::kUTF16, ++ CPDF_CMap::TwoBytes, ++ 0, ++ {}}, + {"83pv-RKSJ", + CIDSET_JAPAN1, +- CIDCODING_JIS, ++ CIDCoding::kJIS, + CPDF_CMap::MixedTwoBytes, + 2, + {{0x81, 0x9f}, {0xe0, 0xfc}}}, + {"90ms-RKSJ", + CIDSET_JAPAN1, +- CIDCODING_JIS, ++ CIDCoding::kJIS, + CPDF_CMap::MixedTwoBytes, + 2, + {{0x81, 0x9f}, {0xe0, 0xfc}}}, + {"90msp-RKSJ", + CIDSET_JAPAN1, +- CIDCODING_JIS, ++ CIDCoding::kJIS, + CPDF_CMap::MixedTwoBytes, + 2, + {{0x81, 0x9f}, {0xe0, 0xfc}}}, + {"90pv-RKSJ", + CIDSET_JAPAN1, +- CIDCODING_JIS, ++ CIDCoding::kJIS, + CPDF_CMap::MixedTwoBytes, + 2, + {{0x81, 0x9f}, {0xe0, 0xfc}}}, + {"Add-RKSJ", + CIDSET_JAPAN1, +- CIDCODING_JIS, ++ CIDCoding::kJIS, + CPDF_CMap::MixedTwoBytes, + 2, + {{0x81, 0x9f}, {0xe0, 0xfc}}}, + {"EUC", + CIDSET_JAPAN1, +- CIDCODING_JIS, ++ CIDCoding::kJIS, + CPDF_CMap::MixedTwoBytes, + 2, + {{0x8e, 0x8e}, {0xa1, 0xfe}}}, +- {"H", CIDSET_JAPAN1, CIDCODING_JIS, CPDF_CMap::TwoBytes, 1, {{0x21, 0x7e}}}, +- {"V", CIDSET_JAPAN1, CIDCODING_JIS, CPDF_CMap::TwoBytes, 1, {{0x21, 0x7e}}}, ++ {"H", ++ CIDSET_JAPAN1, ++ CIDCoding::kJIS, ++ CPDF_CMap::TwoBytes, ++ 1, ++ {{0x21, 0x7e}}}, ++ {"V", ++ CIDSET_JAPAN1, ++ CIDCoding::kJIS, ++ CPDF_CMap::TwoBytes, ++ 1, ++ {{0x21, 0x7e}}}, + {"Ext-RKSJ", + CIDSET_JAPAN1, +- CIDCODING_JIS, ++ CIDCoding::kJIS, + CPDF_CMap::MixedTwoBytes, + 2, + {{0x81, 0x9f}, {0xe0, 0xfc}}}, +- {"UniJIS-UCS2", CIDSET_JAPAN1, CIDCODING_UCS2, CPDF_CMap::TwoBytes, 0, {}}, ++ {"UniJIS-UCS2", ++ CIDSET_JAPAN1, ++ CIDCoding::kUCS2, ++ CPDF_CMap::TwoBytes, ++ 0, ++ {}}, + {"UniJIS-UCS2-HW", + CIDSET_JAPAN1, +- CIDCODING_UCS2, ++ CIDCoding::kUCS2, + CPDF_CMap::TwoBytes, + 0, + {}}, + {"UniJIS-UTF16", + CIDSET_JAPAN1, +- CIDCODING_UTF16, ++ CIDCoding::kUTF16, + CPDF_CMap::TwoBytes, + 0, + {}}, + {"KSC-EUC", + CIDSET_KOREA1, +- CIDCODING_KOREA, ++ CIDCoding::kKOREA, + CPDF_CMap::MixedTwoBytes, + 1, + {{0xa1, 0xfe}}}, + {"KSCms-UHC", + CIDSET_KOREA1, +- CIDCODING_KOREA, ++ CIDCoding::kKOREA, + CPDF_CMap::MixedTwoBytes, + 1, + {{0x81, 0xfe}}}, + {"KSCms-UHC-HW", + CIDSET_KOREA1, +- CIDCODING_KOREA, ++ CIDCoding::kKOREA, + CPDF_CMap::MixedTwoBytes, + 1, + {{0x81, 0xfe}}}, + {"KSCpc-EUC", + CIDSET_KOREA1, +- CIDCODING_KOREA, ++ CIDCoding::kKOREA, + CPDF_CMap::MixedTwoBytes, + 1, + {{0xa1, 0xfd}}}, +- {"UniKS-UCS2", CIDSET_KOREA1, CIDCODING_UCS2, CPDF_CMap::TwoBytes, 0, {}}, +- {"UniKS-UTF16", CIDSET_KOREA1, CIDCODING_UTF16, CPDF_CMap::TwoBytes, 0, {}}, ++ {"UniKS-UCS2", CIDSET_KOREA1, CIDCoding::kUCS2, CPDF_CMap::TwoBytes, 0, {}}, ++ {"UniKS-UTF16", ++ CIDSET_KOREA1, ++ CIDCoding::kUTF16, ++ CPDF_CMap::TwoBytes, ++ 0, ++ {}}, + }; + + const PredefinedCMap* GetPredefinedCMap(ByteStringView cmapid) { +@@ -255,12 +280,21 @@ size_t GetFourByteCharSizeImpl( + return 1; + } + ++const fxcmap::CMap* FindEmbeddedCMap(pdfium::span pCMaps, ++ ByteStringView bsName) { ++ for (size_t i = 0; i < pCMaps.size(); i++) { ++ if (bsName == pCMaps[i].m_Name) ++ return &pCMaps[i]; ++ } ++ return nullptr; ++} ++ + } // namespace + + CPDF_CMap::CPDF_CMap(ByteStringView bsPredefinedName) + : m_bVertical(bsPredefinedName.Back() == 'V') { + if (bsPredefinedName == "Identity-H" || bsPredefinedName == "Identity-V") { +- m_Coding = CIDCODING_CID; ++ m_Coding = CIDCoding::kCID; + m_bLoaded = true; + return; + } +@@ -284,10 +318,10 @@ CPDF_CMap::CPDF_CMap(ByteStringView bsPredefinedName) + } + + CPDF_CMap::CPDF_CMap(pdfium::span spEmbeddedData) +- : m_DirectCharcodeToCIDTable(65536) { ++ : m_DirectCharcodeToCIDTable(kDirectMapTableSize) { + CPDF_CMapParser parser(this); + CPDF_SimpleParser syntax(spEmbeddedData); +- while (1) { ++ while (true) { + ByteStringView word = syntax.GetWord(); + if (word.IsEmpty()) + break; +@@ -299,17 +333,18 @@ CPDF_CMap::CPDF_CMap(pdfium::span spEmbeddedData) + CPDF_CMap::~CPDF_CMap() = default; + + uint16_t CPDF_CMap::CIDFromCharCode(uint32_t charcode) const { +- if (m_Coding == CIDCODING_CID) ++ if (m_Coding == CIDCoding::kCID) + return static_cast(charcode); + + if (m_pEmbedMap) +- return ::CIDFromCharCode(m_pEmbedMap.Get(), charcode); ++ return fxcmap::CIDFromCharCode(m_pEmbedMap, charcode); + + if (m_DirectCharcodeToCIDTable.empty()) + return static_cast(charcode); + +- if (charcode < 0x10000) +- return m_DirectCharcodeToCIDTable[charcode]; ++ auto table_span = m_DirectCharcodeToCIDTable.span(); ++ if (charcode < table_span.size()) ++ return table_span[charcode]; + + auto it = std::lower_bound(m_AdditionalCharcodeToCIDMappings.begin(), + m_AdditionalCharcodeToCIDMappings.end(), charcode, +@@ -346,7 +381,7 @@ uint32_t CPDF_CMap::GetNextChar(ByteStringView pString, size_t* pOffset) const { + uint8_t codes[4]; + int char_size = 1; + codes[0] = offset < pBytes.size() ? pBytes[offset++] : 0; +- while (1) { ++ while (true) { + int ret = CheckFourByteCodeRange(codes, char_size, + m_MixedFourByteLeadingRanges); + if (ret == 0) +@@ -361,7 +396,6 @@ uint32_t CPDF_CMap::GetNextChar(ByteStringView pString, size_t* pOffset) const { + return 0; + codes[char_size++] = pBytes[offset++]; + } +- break; + } + } + return 0; +@@ -466,7 +500,7 @@ int CPDF_CMap::AppendChar(char* str, uint32_t charcode) const { + } + + void CPDF_CMap::SetAdditionalMappings(std::vector mappings) { +- ASSERT(m_AdditionalCharcodeToCIDMappings.empty()); ++ DCHECK(m_AdditionalCharcodeToCIDMappings.empty()); + if (m_CodingScheme != MixedFourBytes || mappings.empty()) + return; + +diff --git a/core/fpdfapi/font/cpdf_cmap.h b/core/fpdfapi/font/cpdf_cmap.h +index e2caf8d2e..cc186e82b 100644 +--- a/core/fpdfapi/font/cpdf_cmap.h ++++ b/core/fpdfapi/font/cpdf_cmap.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,27 +7,35 @@ + #ifndef CORE_FPDFAPI_FONT_CPDF_CMAP_H_ + #define CORE_FPDFAPI_FONT_CPDF_CMAP_H_ + ++#include ++ + #include + + #include "core/fpdfapi/font/cpdf_cidfont.h" ++#include "core/fxcrt/fixed_zeroed_data_vector.h" + #include "core/fxcrt/retain_ptr.h" ++#include "core/fxcrt/unowned_ptr.h" + #include "third_party/base/span.h" + +-struct FXCMAP_CMap; +- +-enum CIDCoding : uint8_t { +- CIDCODING_UNKNOWN = 0, +- CIDCODING_GB, +- CIDCODING_BIG5, +- CIDCODING_JIS, +- CIDCODING_KOREA, +- CIDCODING_UCS2, +- CIDCODING_CID, +- CIDCODING_UTF16, ++namespace fxcmap { ++struct CMap; ++} ++ ++enum class CIDCoding : uint8_t { ++ kUNKNOWN = 0, ++ kGB, ++ kBIG5, ++ kJIS, ++ kKOREA, ++ kUCS2, ++ kCID, ++ kUTF16, + }; + + class CPDF_CMap final : public Retainable { + public: ++ static constexpr size_t kDirectMapTableSize = 65536; ++ + enum CodingScheme : uint8_t { + OneByte, + TwoBytes, +@@ -47,8 +55,7 @@ class CPDF_CMap final : public Retainable { + uint16_t m_StartCID; + }; + +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + bool IsLoaded() const { return m_bLoaded; } + bool IsVertWriting() const { return m_bVertical; } +@@ -65,13 +72,13 @@ class CPDF_CMap final : public Retainable { + void SetAdditionalMappings(std::vector mappings); + void SetMixedFourByteLeadingRanges(std::vector ranges); + +- int GetCoding() const { return m_Coding; } +- const FXCMAP_CMap* GetEmbedMap() const { return m_pEmbedMap.Get(); } ++ CIDCoding GetCoding() const { return m_Coding; } ++ const fxcmap::CMap* GetEmbedMap() const { return m_pEmbedMap; } + CIDSet GetCharset() const { return m_Charset; } + void SetCharset(CIDSet set) { m_Charset = set; } + + void SetDirectCharcodeToCIDTable(size_t idx, uint16_t val) { +- m_DirectCharcodeToCIDTable[idx] = val; ++ m_DirectCharcodeToCIDTable.writable_span()[idx] = val; + } + bool IsDirectCharcodeToCIDTableIsEmpty() const { + return m_DirectCharcodeToCIDTable.empty(); +@@ -86,12 +93,12 @@ class CPDF_CMap final : public Retainable { + bool m_bVertical = false; + CIDSet m_Charset = CIDSET_UNKNOWN; + CodingScheme m_CodingScheme = TwoBytes; +- int m_Coding = CIDCODING_UNKNOWN; ++ CIDCoding m_Coding = CIDCoding::kUNKNOWN; + std::vector m_MixedTwoByteLeadingBytes; + std::vector m_MixedFourByteLeadingRanges; +- std::vector m_DirectCharcodeToCIDTable; ++ FixedZeroedDataVector m_DirectCharcodeToCIDTable; + std::vector m_AdditionalCharcodeToCIDMappings; +- UnownedPtr m_pEmbedMap; ++ UnownedPtr m_pEmbedMap; + }; + + #endif // CORE_FPDFAPI_FONT_CPDF_CMAP_H_ +diff --git a/core/fpdfapi/font/cpdf_cmapmanager.cpp b/core/fpdfapi/font/cpdf_cmapmanager.cpp +deleted file mode 100644 +index 726b6489f..000000000 +--- a/core/fpdfapi/font/cpdf_cmapmanager.cpp ++++ /dev/null +@@ -1,48 +0,0 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fpdfapi/font/cpdf_cmapmanager.h" +- +-#include +- +-#include "core/fpdfapi/font/cpdf_cid2unicodemap.h" +-#include "core/fpdfapi/font/cpdf_cmap.h" +-#include "third_party/base/ptr_util.h" +- +-namespace { +- +-RetainPtr LoadPredefinedCMap(ByteStringView name) { +- if (!name.IsEmpty() && name[0] == '/') +- name = name.Last(name.GetLength() - 1); +- return pdfium::MakeRetain(name); +-} +- +-} // namespace +- +-CPDF_CMapManager::CPDF_CMapManager() = default; +- +-CPDF_CMapManager::~CPDF_CMapManager() = default; +- +-RetainPtr CPDF_CMapManager::GetPredefinedCMap( +- const ByteString& name) { +- auto it = m_CMaps.find(name); +- if (it != m_CMaps.end()) +- return it->second; +- +- RetainPtr pCMap = LoadPredefinedCMap(name.AsStringView()); +- if (!name.IsEmpty()) +- m_CMaps[name] = pCMap; +- +- return pCMap; +-} +- +-CPDF_CID2UnicodeMap* CPDF_CMapManager::GetCID2UnicodeMap(CIDSet charset) { +- if (!m_CID2UnicodeMaps[charset]) { +- m_CID2UnicodeMaps[charset] = +- pdfium::MakeUnique(charset); +- } +- return m_CID2UnicodeMaps[charset].get(); +-} +diff --git a/core/fpdfapi/font/cpdf_cmapmanager.h b/core/fpdfapi/font/cpdf_cmapmanager.h +deleted file mode 100644 +index bc8d4e947..000000000 +--- a/core/fpdfapi/font/cpdf_cmapmanager.h ++++ /dev/null +@@ -1,30 +0,0 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FPDFAPI_FONT_CPDF_CMAPMANAGER_H_ +-#define CORE_FPDFAPI_FONT_CPDF_CMAPMANAGER_H_ +- +-#include +-#include +- +-#include "core/fpdfapi/font/cpdf_cidfont.h" +-#include "core/fxcrt/bytestring.h" +-#include "core/fxcrt/retain_ptr.h" +- +-class CPDF_CMapManager { +- public: +- CPDF_CMapManager(); +- ~CPDF_CMapManager(); +- +- RetainPtr GetPredefinedCMap(const ByteString& name); +- CPDF_CID2UnicodeMap* GetCID2UnicodeMap(CIDSet charset); +- +- private: +- std::map> m_CMaps; +- std::unique_ptr m_CID2UnicodeMaps[CIDSET_NUM_SETS]; +-}; +- +-#endif // CORE_FPDFAPI_FONT_CPDF_CMAPMANAGER_H_ +diff --git a/core/fpdfapi/font/cpdf_cmapparser.cpp b/core/fpdfapi/font/cpdf_cmapparser.cpp +index d1fe51cce..87a590781 100644 +--- a/core/fpdfapi/font/cpdf_cmapparser.cpp ++++ b/core/fpdfapi/font/cpdf_cmapparser.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "core/fpdfapi/font/cpdf_cmapparser.h" + +-#include ++#include ++ ++#include + + #include "core/fpdfapi/cmaps/fpdf_cmaps.h" + #include "core/fpdfapi/parser/cpdf_array.h" +@@ -14,8 +16,8 @@ + #include "core/fpdfapi/parser/cpdf_simple_parser.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "core/fxge/fx_freetype.h" +-#include "third_party/base/logging.h" ++#include "core/fxge/freetype/fx_freetype.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -35,7 +37,7 @@ CPDF_CMapParser::~CPDF_CMapParser() { + } + + void CPDF_CMapParser::ParseWord(ByteStringView word) { +- ASSERT(!word.IsEmpty()); ++ DCHECK(!word.IsEmpty()); + + if (word == "begincidchar") { + m_Status = kProcessingCidChar; +@@ -78,7 +80,7 @@ void CPDF_CMapParser::ParseWord(ByteStringView word) { + } + + void CPDF_CMapParser::HandleCid(ByteStringView word) { +- ASSERT(m_Status == kProcessingCidChar || m_Status == kProcessingCidRange); ++ DCHECK(m_Status == kProcessingCidChar || m_Status == kProcessingCidRange); + bool bChar = m_Status == kProcessingCidChar; + + m_CodePoints[m_CodeSeq] = GetCode(word); +@@ -97,7 +99,7 @@ void CPDF_CMapParser::HandleCid(ByteStringView word) { + EndCode = m_CodePoints[1]; + StartCID = static_cast(m_CodePoints[2]); + } +- if (EndCode < 0x10000) { ++ if (EndCode < CPDF_CMap::kDirectMapTableSize) { + for (uint32_t code = StartCode; code <= EndCode; code++) { + m_pCMap->SetDirectCharcodeToCIDTable( + code, static_cast(StartCID + code - StartCode)); +@@ -114,7 +116,7 @@ void CPDF_CMapParser::HandleCodeSpaceRange(ByteStringView word) { + return; + + if (m_CodeSeq % 2) { +- Optional range = ++ absl::optional range = + GetCodeRange(m_LastWord.AsStringView(), word); + if (range.has_value()) + m_PendingRanges.push_back(range.value()); +@@ -146,7 +148,7 @@ uint32_t CPDF_CMapParser::GetCode(ByteStringView word) { + + FX_SAFE_UINT32 num = 0; + if (word[0] == '<') { +- for (size_t i = 1; i < word.GetLength() && std::isxdigit(word[i]); ++i) { ++ for (size_t i = 1; i < word.GetLength() && isxdigit(word[i]); ++i) { + num = num * 16 + FXSYS_HexCharToInt(word[i]); + if (!num.IsValid()) + return 0; +@@ -154,7 +156,7 @@ uint32_t CPDF_CMapParser::GetCode(ByteStringView word) { + return num.ValueOrDie(); + } + +- for (size_t i = 0; i < word.GetLength() && std::isdigit(word[i]); ++i) { ++ for (size_t i = 0; i < word.GetLength() && isdigit(word[i]); ++i) { + num = num * 10 + FXSYS_DecimalCharToInt(static_cast(word[i])); + if (!num.IsValid()) + return 0; +@@ -163,11 +165,11 @@ uint32_t CPDF_CMapParser::GetCode(ByteStringView word) { + } + + // static +-Optional CPDF_CMapParser::GetCodeRange( ++absl::optional CPDF_CMapParser::GetCodeRange( + ByteStringView first, + ByteStringView second) { + if (first.IsEmpty() || first[0] != '<') +- return pdfium::nullopt; ++ return absl::nullopt; + + size_t i; + for (i = 1; i < first.GetLength(); ++i) { +@@ -176,7 +178,7 @@ Optional CPDF_CMapParser::GetCodeRange( + } + size_t char_size = (i - 1) / 2; + if (char_size > 4) +- return pdfium::nullopt; ++ return absl::nullopt; + + CPDF_CMap::CodeRange range; + range.m_CharSize = char_size; +@@ -203,10 +205,10 @@ Optional CPDF_CMapParser::GetCodeRange( + CIDSet CPDF_CMapParser::CharsetFromOrdering(ByteStringView ordering) { + static const char* const kCharsetNames[CIDSET_NUM_SETS] = { + nullptr, "GB1", "CNS1", "Japan1", "Korea1", "UCS"}; +- static_assert(FX_ArraySize(kCharsetNames) == CIDSET_NUM_SETS, ++ static_assert(std::size(kCharsetNames) == CIDSET_NUM_SETS, + "Too many CID sets"); + +- for (size_t charset = 1; charset < FX_ArraySize(kCharsetNames); ++charset) { ++ for (size_t charset = 1; charset < std::size(kCharsetNames); ++charset) { + if (ordering == kCharsetNames[charset]) + return static_cast(charset); + } +diff --git a/core/fpdfapi/font/cpdf_cmapparser.h b/core/fpdfapi/font/cpdf_cmapparser.h +index b2454af28..9219814aa 100644 +--- a/core/fpdfapi/font/cpdf_cmapparser.h ++++ b/core/fpdfapi/font/cpdf_cmapparser.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,7 +13,7 @@ + #include "core/fpdfapi/font/cpdf_cidfont.h" + #include "core/fpdfapi/font/cpdf_cmap.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CPDF_CMapParser { + public: +@@ -43,8 +43,9 @@ class CPDF_CMapParser { + void HandleCodeSpaceRange(ByteStringView word); + + static uint32_t GetCode(ByteStringView word); +- static Optional GetCodeRange(ByteStringView first, +- ByteStringView second); ++ static absl::optional GetCodeRange( ++ ByteStringView first, ++ ByteStringView second); + + Status m_Status = kStart; + int m_CodeSeq = 0; +@@ -53,7 +54,7 @@ class CPDF_CMapParser { + std::vector m_PendingRanges; + std::vector m_AdditionalCharcodeToCIDMappings; + ByteString m_LastWord; +- uint32_t m_CodePoints[4]; ++ uint32_t m_CodePoints[4] = {}; + }; + + #endif // CORE_FPDFAPI_FONT_CPDF_CMAPPARSER_H_ +diff --git a/core/fpdfapi/font/cpdf_cmapparser_unittest.cpp b/core/fpdfapi/font/cpdf_cmapparser_unittest.cpp +index da654f39d..a9cdc71cd 100644 +--- a/core/fpdfapi/font/cpdf_cmapparser_unittest.cpp ++++ b/core/fpdfapi/font/cpdf_cmapparser_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -37,7 +37,7 @@ TEST(cpdf_cmapparser, GetCode) { + } + + TEST(cpdf_cmapparser, GetCodeRange) { +- Optional range; ++ absl::optional range; + + // Must start with a < + range = CPDF_CMapParser::GetCodeRange("", ""); +diff --git a/core/fpdfapi/font/cpdf_font.cpp b/core/fpdfapi/font/cpdf_font.cpp +index 326bc6edf..36413f826 100644 +--- a/core/fpdfapi/font/cpdf_font.cpp ++++ b/core/fpdfapi/font/cpdf_font.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,12 +6,13 @@ + + #include "core/fpdfapi/font/cpdf_font.h" + +-#include ++#include + #include + #include + #include + + #include "build/build_config.h" ++#include "constants/font_encodings.h" + #include "core/fpdfapi/font/cpdf_cidfont.h" + #include "core/fpdfapi/font/cpdf_fontencoding.h" + #include "core/fpdfapi/font/cpdf_fontglobals.h" +@@ -25,13 +26,15 @@ + #include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" +-#include "core/fxcrt/fx_memory.h" ++#include "core/fxcrt/fx_codepage.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/stl_util.h" + #include "core/fxge/cfx_fontmapper.h" ++#include "core/fxge/cfx_substfont.h" ++#include "core/fxge/freetype/fx_freetype.h" + #include "core/fxge/fx_font.h" +-#include "core/fxge/fx_freetype.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/numerics/safe_conversions.h" + + namespace { + +@@ -45,19 +48,15 @@ const uint8_t kChineseFontNames[][kChineseFontNameSize] = { + + } // namespace + +-CPDF_Font::CPDF_Font(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict) ++CPDF_Font::CPDF_Font(CPDF_Document* pDocument, ++ RetainPtr pFontDict) + : m_pDocument(pDocument), +- m_pFontDict(pFontDict), +- m_BaseFontName(pFontDict->GetStringFor("BaseFont")) {} ++ m_pFontDict(std::move(pFontDict)), ++ m_BaseFontName(m_pFontDict->GetByteStringFor("BaseFont")) {} + + CPDF_Font::~CPDF_Font() { +- if (m_pFontFile) { +- auto* pPageData = m_pDocument->GetPageData(); +- if (pPageData) { +- pPageData->MaybePurgeFontFileStreamAcc( +- m_pFontFile->GetStream()->AsStream()); +- } +- } ++ if (m_pFontFile) ++ m_pDocument->MaybePurgeFontFileStreamAcc(std::move(m_pFontFile)); + } + + bool CPDF_Font::IsType1Font() const { +@@ -108,15 +107,11 @@ CPDF_CIDFont* CPDF_Font::AsCIDFont() { + return nullptr; + } + +-bool CPDF_Font::IsUnicodeCompatible() const { +- return false; +-} +- + size_t CPDF_Font::CountChar(ByteStringView pString) const { + return pString.GetLength(); + } + +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + int CPDF_Font::GlyphFromCharCodeExt(uint32_t charcode) { + return GlyphFromCharCode(charcode, nullptr); + } +@@ -194,7 +189,7 @@ void CPDF_Font::LoadFontDescriptor(const CPDF_Dictionary* pFontDesc) { + } + if (m_Descent > 10) + m_Descent = -m_Descent; +- const CPDF_Array* pBBox = pFontDesc->GetArrayFor("FontBBox"); ++ RetainPtr pBBox = pFontDesc->GetArrayFor("FontBBox"); + if (pBBox) { + m_FontBBox.left = pBBox->GetIntegerAt(0); + m_FontBBox.bottom = pBBox->GetIntegerAt(1); +@@ -202,7 +197,7 @@ void CPDF_Font::LoadFontDescriptor(const CPDF_Dictionary* pFontDesc) { + m_FontBBox.top = pBBox->GetIntegerAt(3); + } + +- const CPDF_Stream* pFontFile = pFontDesc->GetStreamFor("FontFile"); ++ RetainPtr pFontFile = pFontDesc->GetStreamFor("FontFile"); + if (!pFontFile) + pFontFile = pFontDesc->GetStreamFor("FontFile2"); + if (!pFontFile) +@@ -210,15 +205,13 @@ void CPDF_Font::LoadFontDescriptor(const CPDF_Dictionary* pFontDesc) { + if (!pFontFile) + return; + +- auto* pData = m_pDocument->GetPageData(); +- m_pFontFile = pData->GetFontFileStreamAcc(pFontFile); ++ const uint64_t key = pFontFile->KeyForCache(); ++ m_pFontFile = m_pDocument->GetFontFileStreamAcc(std::move(pFontFile)); + if (!m_pFontFile) + return; + +- if (!m_Font.LoadEmbedded(m_pFontFile->GetSpan(), IsVertWriting())) { +- pData->MaybePurgeFontFileStreamAcc(m_pFontFile->GetStream()->AsStream()); +- m_pFontFile = nullptr; +- } ++ if (!m_Font.LoadEmbedded(m_pFontFile->GetSpan(), IsVertWriting(), key)) ++ m_pDocument->MaybePurgeFontFileStreamAcc(std::move(m_pFontFile)); + } + + void CPDF_Font::CheckFontMetrics() { +@@ -243,18 +236,10 @@ void CPDF_Font::CheckFontMetrics() { + m_FontBBox = rect; + bFirst = false; + } else { +- if (m_FontBBox.top < rect.top) { +- m_FontBBox.top = rect.top; +- } +- if (m_FontBBox.right < rect.right) { +- m_FontBBox.right = rect.right; +- } +- if (m_FontBBox.left > rect.left) { +- m_FontBBox.left = rect.left; +- } +- if (m_FontBBox.bottom > rect.bottom) { +- m_FontBBox.bottom = rect.bottom; +- } ++ m_FontBBox.left = std::min(m_FontBBox.left, rect.left); ++ m_FontBBox.top = std::max(m_FontBBox.top, rect.top); ++ m_FontBBox.right = std::max(m_FontBBox.right, rect.right); ++ m_FontBBox.bottom = std::min(m_FontBBox.bottom, rect.bottom); + } + } + } +@@ -269,16 +254,16 @@ void CPDF_Font::CheckFontMetrics() { + + void CPDF_Font::LoadUnicodeMap() const { + m_bToUnicodeLoaded = true; +- const CPDF_Stream* pStream = m_pFontDict->GetStreamFor("ToUnicode"); ++ RetainPtr pStream = m_pFontDict->GetStreamFor("ToUnicode"); + if (!pStream) + return; + +- m_pToUnicodeMap = pdfium::MakeUnique(pStream); ++ m_pToUnicodeMap = std::make_unique(std::move(pStream)); + } + +-uint32_t CPDF_Font::GetStringWidth(ByteStringView pString) { ++int CPDF_Font::GetStringWidth(ByteStringView pString) { + size_t offset = 0; +- uint32_t width = 0; ++ int width = 0; + while (offset < pString.GetLength()) + width += GetCharWidthF(GetNextChar(pString, &offset)); + return width; +@@ -288,7 +273,7 @@ uint32_t CPDF_Font::GetStringWidth(ByteStringView pString) { + RetainPtr CPDF_Font::GetStockFont(CPDF_Document* pDoc, + ByteStringView name) { + ByteString fontname(name); +- Optional font_id = ++ absl::optional font_id = + CFX_FontMapper::GetStandardFontName(&fontname); + if (!font_id.has_value()) + return nullptr; +@@ -302,37 +287,39 @@ RetainPtr CPDF_Font::GetStockFont(CPDF_Document* pDoc, + pDict->SetNewFor("Type", "Font"); + pDict->SetNewFor("Subtype", "Type1"); + pDict->SetNewFor("BaseFont", fontname); +- pDict->SetNewFor("Encoding", "WinAnsiEncoding"); +- pFont = CPDF_Font::Create(nullptr, pDict.Get(), nullptr); ++ pDict->SetNewFor("Encoding", ++ pdfium::font_encodings::kWinAnsiEncoding); ++ pFont = CPDF_Font::Create(nullptr, std::move(pDict), nullptr); + pFontGlobals->Set(pDoc, font_id.value(), pFont); + return pFont; + } + + // static + RetainPtr CPDF_Font::Create(CPDF_Document* pDoc, +- CPDF_Dictionary* pFontDict, ++ RetainPtr pFontDict, + FormFactoryIface* pFactory) { +- ByteString type = pFontDict->GetStringFor("Subtype"); ++ ByteString type = pFontDict->GetByteStringFor("Subtype"); + RetainPtr pFont; + if (type == "TrueType") { +- ByteString tag = pFontDict->GetStringFor("BaseFont").First(4); +- for (size_t i = 0; i < FX_ArraySize(kChineseFontNames); ++i) { ++ ByteString tag = pFontDict->GetByteStringFor("BaseFont").First(4); ++ for (size_t i = 0; i < std::size(kChineseFontNames); ++i) { + if (tag == ByteString(kChineseFontNames[i], kChineseFontNameSize)) { +- const CPDF_Dictionary* pFontDesc = ++ RetainPtr pFontDesc = + pFontDict->GetDictFor("FontDescriptor"); + if (!pFontDesc || !pFontDesc->KeyExist("FontFile2")) +- pFont = pdfium::MakeRetain(pDoc, pFontDict); ++ pFont = pdfium::MakeRetain(pDoc, std::move(pFontDict)); + break; + } + } + if (!pFont) +- pFont = pdfium::MakeRetain(pDoc, pFontDict); ++ pFont = pdfium::MakeRetain(pDoc, std::move(pFontDict)); + } else if (type == "Type3") { +- pFont = pdfium::MakeRetain(pDoc, pFontDict, pFactory); ++ pFont = pdfium::MakeRetain(pDoc, std::move(pFontDict), ++ pFactory); + } else if (type == "Type0") { +- pFont = pdfium::MakeRetain(pDoc, pFontDict); ++ pFont = pdfium::MakeRetain(pDoc, std::move(pFontDict)); + } else { +- pFont = pdfium::MakeRetain(pDoc, pFontDict); ++ pFont = pdfium::MakeRetain(pDoc, std::move(pFontDict)); + } + if (!pFont->Load()) + return nullptr; +@@ -356,9 +343,16 @@ bool CPDF_Font::IsStandardFont() const { + return AsType1Font()->IsBase14Font(); + } + ++absl::optional CPDF_Font::GetSubstFontCharset() const { ++ CFX_SubstFont* pFont = m_Font.GetSubstFont(); ++ if (!pFont) ++ return absl::nullopt; ++ return pFont->m_Charset; ++} ++ + // static + const char* CPDF_Font::GetAdobeCharName( +- int iBaseEncoding, ++ FontEncoding base_encoding, + const std::vector& charnames, + uint32_t charcode) { + if (charcode >= 256) +@@ -368,29 +362,30 @@ const char* CPDF_Font::GetAdobeCharName( + return charnames[charcode].c_str(); + + const char* name = nullptr; +- if (iBaseEncoding) +- name = PDF_CharNameFromPredefinedCharSet(iBaseEncoding, charcode); ++ if (base_encoding != FontEncoding::kBuiltin) ++ name = CharNameFromPredefinedCharSet(base_encoding, charcode); + if (!name) + return nullptr; + +- ASSERT(name[0]); ++ DCHECK(name[0]); + return name; + } + + uint32_t CPDF_Font::FallbackFontFromCharcode(uint32_t charcode) { + if (m_FontFallbacks.empty()) { +- m_FontFallbacks.push_back(pdfium::MakeUnique()); +- pdfium::base::CheckedNumeric safeWeight = m_StemV; ++ m_FontFallbacks.push_back(std::make_unique()); ++ FX_SAFE_INT32 safeWeight = m_StemV; + safeWeight *= 5; + m_FontFallbacks[0]->LoadSubst("Arial", IsTrueTypeFont(), m_Flags, + safeWeight.ValueOrDefault(FXFONT_FW_NORMAL), +- m_ItalicAngle, 0, IsVertWriting()); ++ m_ItalicAngle, FX_CodePage::kDefANSI, ++ IsVertWriting()); + } + return 0; + } + + int CPDF_Font::FallbackGlyphFromCharcode(int fallbackFont, uint32_t charcode) { +- if (!pdfium::IndexInBounds(m_FontFallbacks, fallbackFont)) ++ if (!fxcrt::IndexInBounds(m_FontFallbacks, fallbackFont)) + return -1; + + WideString str = UnicodeFromCharCode(charcode); +@@ -410,26 +405,23 @@ CFX_Font* CPDF_Font::GetFontFallback(int position) { + } + + // static +-int CPDF_Font::TT2PDF(int m, FXFT_FaceRec* face) { ++int CPDF_Font::TT2PDF(FT_Pos m, FXFT_FaceRec* face) { + int upm = FXFT_Get_Face_UnitsPerEM(face); + if (upm == 0) +- return m; ++ return pdfium::base::saturated_cast(m); + +- return static_cast( +- pdfium::clamp((m * 1000.0 + upm / 2) / upm, +- static_cast(std::numeric_limits::min()), +- static_cast(std::numeric_limits::max()))); ++ const double dm = (m * 1000.0 + upm / 2) / upm; ++ return pdfium::base::saturated_cast(dm); + } + + // static +-bool CPDF_Font::FT_UseTTCharmap(FXFT_FaceRec* face, +- int platform_id, +- int encoding_id) { +- auto** pCharMap = FXFT_Get_Face_Charmaps(face); +- for (int i = 0; i < FXFT_Get_Face_CharmapCount(face); i++) { +- if (FXFT_Get_Charmap_PlatformID(pCharMap[i]) == platform_id && +- FXFT_Get_Charmap_EncodingID(pCharMap[i]) == encoding_id) { +- FT_Set_Charmap(face, pCharMap[i]); ++bool CPDF_Font::UseTTCharmap(FXFT_FaceRec* face, ++ int platform_id, ++ int encoding_id) { ++ for (int i = 0; i < face->num_charmaps; i++) { ++ if (FXFT_Get_Charmap_PlatformID(face->charmaps[i]) == platform_id && ++ FXFT_Get_Charmap_EncodingID(face->charmaps[i]) == encoding_id) { ++ FT_Set_Charmap(face, face->charmaps[i]); + return true; + } + } +@@ -437,7 +429,7 @@ bool CPDF_Font::FT_UseTTCharmap(FXFT_FaceRec* face, + } + + int CPDF_Font::GetFontWeight() const { +- pdfium::base::CheckedNumeric safeStemV(m_StemV); ++ FX_SAFE_INT32 safeStemV(m_StemV); + if (m_StemV < 140) + safeStemV *= 5; + else +diff --git a/core/fpdfapi/font/cpdf_font.h b/core/fpdfapi/font/cpdf_font.h +index db776fd18..a74cba9ac 100644 +--- a/core/fpdfapi/font/cpdf_font.h ++++ b/core/fpdfapi/font/cpdf_font.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,8 @@ + #ifndef CORE_FPDFAPI_FONT_CPDF_FONT_H_ + #define CORE_FPDFAPI_FONT_CPDF_FONT_H_ + ++#include ++ + #include + #include + #include +@@ -15,53 +17,53 @@ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/observed_ptr.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + #include "core/fxge/cfx_font.h" ++#include "core/fxge/freetype/fx_freetype.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CFX_DIBitmap; +-class CFX_SubstFont; + class CPDF_CIDFont; + class CPDF_Document; +-class CPDF_Object; + class CPDF_TrueTypeFont; + class CPDF_Type1Font; + class CPDF_Type3Char; + class CPDF_Type3Font; + class CPDF_ToUnicodeMap; ++enum class FontEncoding; + + class CPDF_Font : public Retainable, public Observable { + public: + // Callback mechanism for Type3 fonts to get pixels from forms. + class FormIface { + public: +- virtual ~FormIface() {} ++ virtual ~FormIface() = default; + + virtual void ParseContentForType3Char(CPDF_Type3Char* pChar) = 0; + virtual bool HasPageObjects() const = 0; + virtual CFX_FloatRect CalcBoundingBox() const = 0; +- virtual Optional, CFX_Matrix>> ++ virtual absl::optional, CFX_Matrix>> + GetBitmapAndMatrixFromSoleImageOfForm() const = 0; + }; + + // Callback mechanism for Type3 fonts to get new forms from upper layers. + class FormFactoryIface { + public: +- virtual ~FormFactoryIface() {} ++ virtual ~FormFactoryIface() = default; + + virtual std::unique_ptr CreateForm( + CPDF_Document* pDocument, +- CPDF_Dictionary* pPageResources, +- CPDF_Stream* pFormStream) = 0; ++ RetainPtr pPageResources, ++ RetainPtr pFormStream) = 0; + }; + +- static const uint32_t kInvalidCharCode = static_cast(-1); ++ static constexpr uint32_t kInvalidCharCode = static_cast(-1); + + // |pFactory| only required for Type3 fonts. + static RetainPtr Create(CPDF_Document* pDoc, +- CPDF_Dictionary* pFontDict, ++ RetainPtr pFontDict, + FormFactoryIface* pFactory); + static RetainPtr GetStockFont(CPDF_Document* pDoc, + ByteStringView fontname); +@@ -83,12 +85,12 @@ class CPDF_Font : public Retainable, public Observable { + + virtual void WillBeDestroyed(); + virtual bool IsVertWriting() const; +- virtual bool IsUnicodeCompatible() const; ++ virtual bool IsUnicodeCompatible() const = 0; + virtual uint32_t GetNextChar(ByteStringView pString, size_t* pOffset) const; + virtual size_t CountChar(ByteStringView pString) const; + virtual int AppendChar(char* buf, uint32_t charcode) const; + virtual int GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) = 0; +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + virtual int GlyphFromCharCodeExt(uint32_t charcode); + #endif + virtual WideString UnicodeFromCharCode(uint32_t charcode) const; +@@ -96,9 +98,14 @@ class CPDF_Font : public Retainable, public Observable { + virtual bool HasFontWidths() const; + + ByteString GetBaseFontName() const { return m_BaseFontName; } +- CFX_SubstFont* GetSubstFont() const { return m_Font.GetSubstFont(); } ++ absl::optional GetSubstFontCharset() const; + bool IsEmbedded() const { return IsType3Font() || m_pFontFile != nullptr; } +- CPDF_Dictionary* GetFontDict() const { return m_pFontDict.Get(); } ++ RetainPtr GetMutableFontDict() { return m_pFontDict; } ++ RetainPtr GetFontDict() const { return m_pFontDict; } ++ uint32_t GetFontDictObjNum() const { return m_pFontDict->GetObjNum(); } ++ bool FontDictIs(const CPDF_Dictionary* pThat) const { ++ return m_pFontDict == pThat; ++ } + void ClearFontDict() { m_pFontDict = nullptr; } + bool IsStandardFont() const; + bool HasFace() const { return !!m_Font.GetFaceRec(); } +@@ -107,32 +114,48 @@ class CPDF_Font : public Retainable, public Observable { + const FX_RECT& GetFontBBox() const { return m_FontBBox; } + int GetTypeAscent() const { return m_Ascent; } + int GetTypeDescent() const { return m_Descent; } +- uint32_t GetStringWidth(ByteStringView pString); ++ int GetStringWidth(ByteStringView pString); + uint32_t FallbackFontFromCharcode(uint32_t charcode); + int FallbackGlyphFromCharcode(int fallbackFont, uint32_t charcode); + int GetFontFlags() const { return m_Flags; } ++ int GetItalicAngle() const { return m_ItalicAngle; } + int GetFontWeight() const; + +- virtual uint32_t GetCharWidthF(uint32_t charcode) = 0; ++ virtual int GetCharWidthF(uint32_t charcode) = 0; + virtual FX_RECT GetCharBBox(uint32_t charcode) = 0; + + // Can return nullptr for stock Type1 fonts. Always returns non-null for other + // font types. +- CPDF_Document* GetDocument() const { return m_pDocument.Get(); } ++ CPDF_Document* GetDocument() const { return m_pDocument; } + + CFX_Font* GetFont() { return &m_Font; } + const CFX_Font* GetFont() const { return &m_Font; } + + CFX_Font* GetFontFallback(int position); + +- protected: +- CPDF_Font(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict); ++ const ByteString& GetResourceName() const { return m_ResourceName; } ++ void SetResourceName(const ByteString& name) { m_ResourceName = name; } + +- static int TT2PDF(int m, FXFT_FaceRec* face); +- static bool FT_UseTTCharmap(FXFT_FaceRec* face, +- int platform_id, +- int encoding_id); +- static const char* GetAdobeCharName(int iBaseEncoding, ++ protected: ++ CPDF_Font(CPDF_Document* pDocument, RetainPtr pFontDict); ++ ++ static int TT2PDF(FT_Pos m, FXFT_FaceRec* face); ++ ++ // Commonly used wrappers for UseTTCharmap(). ++ static bool UseTTCharmapMSUnicode(FXFT_FaceRec* face) { ++ return UseTTCharmap(face, 3, 1); ++ } ++ static bool UseTTCharmapMSSymbol(FXFT_FaceRec* face) { ++ return UseTTCharmap(face, 3, 0); ++ } ++ static bool UseTTCharmapMacRoman(FXFT_FaceRec* face) { ++ return UseTTCharmap(face, 1, 0); ++ } ++ static bool UseTTCharmap(FXFT_FaceRec* face, ++ int platform_id, ++ int encoding_id); ++ ++ static const char* GetAdobeCharName(FontEncoding base_encoding, + const std::vector& charnames, + uint32_t charcode); + +@@ -143,6 +166,7 @@ class CPDF_Font : public Retainable, public Observable { + void CheckFontMetrics(); + + UnownedPtr const m_pDocument; ++ ByteString m_ResourceName; // The resource name for this font. + CFX_Font m_Font; + std::vector> m_FontFallbacks; + RetainPtr m_pFontFile; +diff --git a/core/fpdfapi/font/cpdf_fontencoding.cpp b/core/fpdfapi/font/cpdf_fontencoding.cpp +index a4e687e3c..ff2baed6b 100644 +--- a/core/fpdfapi/font/cpdf_fontencoding.cpp ++++ b/core/fpdfapi/font/cpdf_fontencoding.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,20 +6,21 @@ + + #include "core/fpdfapi/font/cpdf_fontencoding.h" + +-#include ++#include + ++#include "constants/font_encodings.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" ++#include "core/fxge/freetype/fx_freetype.h" + #include "core/fxge/fx_font.h" +-#include "core/fxge/fx_freetype.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + namespace { + +-const uint16_t MSSymbolEncoding[CPDF_FontEncoding::kEncodingTableSize] = { ++const uint16_t kMSSymbolEncoding[CPDF_FontEncoding::kEncodingTableSize] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +@@ -50,7 +51,7 @@ const uint16_t MSSymbolEncoding[CPDF_FontEncoding::kEncodingTableSize] = { + 0x2320, 0x0000, 0x2321, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000}; + +-const uint16_t StandardEncoding[CPDF_FontEncoding::kEncodingTableSize] = { ++const uint16_t kStandardEncoding[CPDF_FontEncoding::kEncodingTableSize] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +@@ -81,7 +82,7 @@ const uint16_t StandardEncoding[CPDF_FontEncoding::kEncodingTableSize] = { + 0x0000, 0x0000, 0x0131, 0x0000, 0x0000, 0x0142, 0x00f8, 0x0153, 0x00df, + 0x0000, 0x0000, 0x0000, 0x0000}; + +-const uint16_t MacRomanEncoding[CPDF_FontEncoding::kEncodingTableSize] = { ++const uint16_t kMacRomanEncoding[CPDF_FontEncoding::kEncodingTableSize] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +@@ -112,7 +113,7 @@ const uint16_t MacRomanEncoding[CPDF_FontEncoding::kEncodingTableSize] = { + 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc, 0x00af, 0x02d8, 0x02d9, 0x02da, + 0x00b8, 0x02dd, 0x02db, 0x02c7}; + +-const uint16_t AdobeWinAnsiEncoding[CPDF_FontEncoding::kEncodingTableSize] = { ++const uint16_t kAdobeWinAnsiEncoding[CPDF_FontEncoding::kEncodingTableSize] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +@@ -143,7 +144,7 @@ const uint16_t AdobeWinAnsiEncoding[CPDF_FontEncoding::kEncodingTableSize] = { + 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, + 0x00fc, 0x00fd, 0x00fe, 0x00ff}; + +-const uint16_t MacExpertEncoding[CPDF_FontEncoding::kEncodingTableSize] = { ++const uint16_t kMacExpertEncoding[CPDF_FontEncoding::kEncodingTableSize] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +@@ -174,7 +175,7 @@ const uint16_t MacExpertEncoding[CPDF_FontEncoding::kEncodingTableSize] = { + 0xf6f4, 0xf7af, 0xf6ea, 0x207f, 0xf6ef, 0xf6e2, 0xf6e8, 0xf6f7, 0xf6fc, + 0x0000, 0x0000, 0x0000, 0x0000}; + +-const uint16_t AdobeSymbolEncoding[CPDF_FontEncoding::kEncodingTableSize] = { ++const uint16_t kAdobeSymbolEncoding[CPDF_FontEncoding::kEncodingTableSize] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +@@ -206,7 +207,7 @@ const uint16_t AdobeSymbolEncoding[CPDF_FontEncoding::kEncodingTableSize] = { + 0xF8FC, 0xF8FD, 0xF8FE, 0x0000, + }; + +-const uint16_t ZapfEncoding[CPDF_FontEncoding::kEncodingTableSize] = { ++const uint16_t kZapfEncoding[CPDF_FontEncoding::kEncodingTableSize] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +@@ -245,7 +246,7 @@ constexpr size_t kPDFDocEncodingTableFirstChar = 24; + constexpr size_t kPDFDocEncodingNamesTableSize = + CPDF_FontEncoding::kEncodingTableSize - kPDFDocEncodingTableFirstChar; + +-const char* const StandardEncodingNames[kEncodingNamesTableSize] = { ++const char* const kStandardEncodingNames[kEncodingNamesTableSize] = { + "space", + "exclam", + "quotedbl", +@@ -472,7 +473,7 @@ const char* const StandardEncodingNames[kEncodingNamesTableSize] = { + nullptr, + }; + +-const char* const AdobeWinAnsiEncodingNames[kEncodingNamesTableSize] = { ++const char* const kAdobeWinAnsiEncodingNames[kEncodingNamesTableSize] = { + "space", + "exclam", + "quotedbl", +@@ -699,7 +700,7 @@ const char* const AdobeWinAnsiEncodingNames[kEncodingNamesTableSize] = { + "ydieresis", + }; + +-const char* const MacRomanEncodingNames[kEncodingNamesTableSize] = { ++const char* const kMacRomanEncodingNames[kEncodingNamesTableSize] = { + "space", + "exclam", + "quotedbl", +@@ -926,7 +927,7 @@ const char* const MacRomanEncodingNames[kEncodingNamesTableSize] = { + "caron", + }; + +-const char* const MacExpertEncodingNames[kEncodingNamesTableSize] = { ++const char* const kMacExpertEncodingNames[kEncodingNamesTableSize] = { + "space", + "exclamsmall", + "Hungarumlautsmall", +@@ -1153,7 +1154,7 @@ const char* const MacExpertEncodingNames[kEncodingNamesTableSize] = { + nullptr, + }; + +-const char* const PDFDocEncodingNames[kPDFDocEncodingNamesTableSize] = { ++const char* const kPDFDocEncodingNames[kPDFDocEncodingNamesTableSize] = { + "breve", + "caron", + "circumflex", +@@ -1388,7 +1389,7 @@ const char* const PDFDocEncodingNames[kPDFDocEncodingNamesTableSize] = { + "ydieresis", + }; + +-const char* const AdobeSymbolEncodingNames[kEncodingNamesTableSize] = { ++const char* const kAdobeSymbolEncodingNames[kEncodingNamesTableSize] = { + "space", + "exclam", + "universal", +@@ -1615,7 +1616,7 @@ const char* const AdobeSymbolEncodingNames[kEncodingNamesTableSize] = { + nullptr, + }; + +-const char* const ZapfEncodingNames[kEncodingNamesTableSize] = { ++const char* const kZapfEncodingNames[kEncodingNamesTableSize] = { + "space", "a1", "a2", "a202", "a3", "a4", "a5", "a119", + "a118", "a117", "a11", "a12", "a13", "a14", "a15", "a16", + "a105", "a17", "a18", "a19", "a20", "a21", "a22", "a23", +@@ -1648,7 +1649,7 @@ const char* const ZapfEncodingNames[kEncodingNamesTableSize] = { + uint32_t PDF_FindCode(const uint16_t* pCodes, uint16_t unicode) { + for (size_t i = 0; i < CPDF_FontEncoding::kEncodingTableSize; i++) { + if (pCodes[i] == unicode) +- return i; ++ return static_cast(i); + } + return 0; + } +@@ -1656,20 +1657,18 @@ uint32_t PDF_FindCode(const uint16_t* pCodes, uint16_t unicode) { + } // namespace + + int CPDF_FontEncoding::CharCodeFromUnicode(wchar_t unicode) const { +- for (size_t i = 0; i < FX_ArraySize(m_Unicodes); i++) { ++ for (size_t i = 0; i < std::size(m_Unicodes); i++) { + if (m_Unicodes[i] == unicode) +- return i; ++ return static_cast(i); + } + return -1; + } + +-CPDF_FontEncoding::CPDF_FontEncoding(int PredefinedEncoding) { +- const uint16_t* pSrc = PDF_UnicodesForPredefinedCharSet(PredefinedEncoding); ++CPDF_FontEncoding::CPDF_FontEncoding(FontEncoding predefined_encoding) { ++ const uint16_t* pSrc = UnicodesForPredefinedCharSet(predefined_encoding); + if (pSrc) { +- for (size_t i = 0; i < FX_ArraySize(m_Unicodes); i++) ++ for (size_t i = 0; i < std::size(m_Unicodes); i++) + m_Unicodes[i] = pSrc[i]; +- } else { +- memset(m_Unicodes, 0, sizeof(m_Unicodes)); + } + } + +@@ -1679,12 +1678,17 @@ bool CPDF_FontEncoding::IsIdentical(const CPDF_FontEncoding* pAnother) const { + + RetainPtr CPDF_FontEncoding::Realize( + WeakPtr pPool) const { +- int predefined = 0; +- for (int cs = PDFFONT_ENCODING_WINANSI; cs < PDFFONT_ENCODING_ZAPFDINGBATS; +- cs++) { +- const uint16_t* pSrc = PDF_UnicodesForPredefinedCharSet(cs); ++ static constexpr FontEncoding kEncodings[] = { ++ FontEncoding::kWinAnsi, FontEncoding::kMacRoman, ++ FontEncoding::kMacExpert, FontEncoding::kStandard, ++ FontEncoding::kAdobeSymbol, ++ }; ++ ++ absl::optional predefined; ++ for (FontEncoding cs : kEncodings) { ++ const uint16_t* pSrc = UnicodesForPredefinedCharSet(cs); + bool match = true; +- for (size_t i = 0; i < FX_ArraySize(m_Unicodes); i++) { ++ for (size_t i = 0; i < std::size(m_Unicodes); i++) { + if (m_Unicodes[i] != pSrc[i]) { + match = false; + break; +@@ -1695,79 +1699,87 @@ RetainPtr CPDF_FontEncoding::Realize( + break; + } + } +- if (predefined) { ++ if (predefined.has_value()) { + const char* pName; +- if (predefined == PDFFONT_ENCODING_WINANSI) +- pName = "WinAnsiEncoding"; +- else if (predefined == PDFFONT_ENCODING_MACROMAN) +- pName = "MacRomanEncoding"; +- else if (predefined == PDFFONT_ENCODING_MACEXPERT) +- pName = "MacExpertEncoding"; ++ if (predefined.value() == FontEncoding::kWinAnsi) ++ pName = pdfium::font_encodings::kWinAnsiEncoding; ++ else if (predefined.value() == FontEncoding::kMacRoman) ++ pName = pdfium::font_encodings::kMacRomanEncoding; ++ else if (predefined.value() == FontEncoding::kMacExpert) ++ pName = pdfium::font_encodings::kMacExpertEncoding; + else + return nullptr; + + return pdfium::MakeRetain(pPool, pName); + } + const uint16_t* pStandard = +- PDF_UnicodesForPredefinedCharSet(PDFFONT_ENCODING_WINANSI); ++ UnicodesForPredefinedCharSet(FontEncoding::kWinAnsi); + auto pDiff = pdfium::MakeRetain(); +- for (size_t i = 0; i < FX_ArraySize(m_Unicodes); i++) { ++ for (size_t i = 0; i < std::size(m_Unicodes); i++) { + if (pStandard[i] == m_Unicodes[i]) + continue; + +- pDiff->AddNew(static_cast(i)); +- pDiff->AddNew(PDF_AdobeNameFromUnicode(m_Unicodes[i])); ++ pDiff->AppendNew(static_cast(i)); ++ pDiff->AppendNew(AdobeNameFromUnicode(m_Unicodes[i])); + } + + auto pDict = pdfium::MakeRetain(pPool); +- pDict->SetNewFor("BaseEncoding", "WinAnsiEncoding"); ++ pDict->SetNewFor("BaseEncoding", ++ pdfium::font_encodings::kWinAnsiEncoding); + pDict->SetFor("Differences", pDiff); + return pDict; + } + +-uint32_t FT_CharCodeFromUnicode(int encoding, wchar_t unicode) { ++uint32_t CharCodeFromUnicodeForFreetypeEncoding(int encoding, wchar_t unicode) { + switch (encoding) { + case FT_ENCODING_UNICODE: + return unicode; + case FT_ENCODING_ADOBE_STANDARD: +- return PDF_FindCode(StandardEncoding, unicode); ++ return PDF_FindCode(kStandardEncoding, unicode); + case FT_ENCODING_ADOBE_EXPERT: +- return PDF_FindCode(MacExpertEncoding, unicode); ++ return PDF_FindCode(kMacExpertEncoding, unicode); + case FT_ENCODING_ADOBE_LATIN_1: +- return PDF_FindCode(AdobeWinAnsiEncoding, unicode); ++ return PDF_FindCode(kAdobeWinAnsiEncoding, unicode); + case FT_ENCODING_APPLE_ROMAN: +- return PDF_FindCode(MacRomanEncoding, unicode); ++ return PDF_FindCode(kMacRomanEncoding, unicode); + case FT_ENCODING_ADOBE_CUSTOM: +- return PDF_FindCode(PDFDocEncoding, unicode); ++ return PDF_FindCode(kPDFDocEncoding, unicode); + case FT_ENCODING_MS_SYMBOL: +- return PDF_FindCode(MSSymbolEncoding, unicode); ++ return PDF_FindCode(kMSSymbolEncoding, unicode); + } + return 0; + } +-const uint16_t* PDF_UnicodesForPredefinedCharSet(int encoding) { ++ ++wchar_t UnicodeFromAppleRomanCharCode(uint8_t charcode) { ++ return kMacRomanEncoding[charcode]; ++} ++ ++const uint16_t* UnicodesForPredefinedCharSet(FontEncoding encoding) { + switch (encoding) { +- case PDFFONT_ENCODING_WINANSI: +- return AdobeWinAnsiEncoding; +- case PDFFONT_ENCODING_MACROMAN: +- return MacRomanEncoding; +- case PDFFONT_ENCODING_MACEXPERT: +- return MacExpertEncoding; +- case PDFFONT_ENCODING_STANDARD: +- return StandardEncoding; +- case PDFFONT_ENCODING_ADOBE_SYMBOL: +- return AdobeSymbolEncoding; +- case PDFFONT_ENCODING_ZAPFDINGBATS: +- return ZapfEncoding; +- case PDFFONT_ENCODING_PDFDOC: +- return PDFDocEncoding; +- case PDFFONT_ENCODING_MS_SYMBOL: +- return MSSymbolEncoding; ++ case FontEncoding::kBuiltin: ++ return nullptr; ++ case FontEncoding::kWinAnsi: ++ return kAdobeWinAnsiEncoding; ++ case FontEncoding::kMacRoman: ++ return kMacRomanEncoding; ++ case FontEncoding::kMacExpert: ++ return kMacExpertEncoding; ++ case FontEncoding::kStandard: ++ return kStandardEncoding; ++ case FontEncoding::kAdobeSymbol: ++ return kAdobeSymbolEncoding; ++ case FontEncoding::kZapfDingbats: ++ return kZapfEncoding; ++ case FontEncoding::kPdfDoc: ++ return kPDFDocEncoding; ++ case FontEncoding::kMsSymbol: ++ return kMSSymbolEncoding; + } +- return nullptr; + } + +-const char* PDF_CharNameFromPredefinedCharSet(int encoding, uint8_t charcode) { +- if (encoding == PDFFONT_ENCODING_PDFDOC) { ++const char* CharNameFromPredefinedCharSet(FontEncoding encoding, ++ uint8_t charcode) { ++ if (encoding == FontEncoding::kPdfDoc) { + if (charcode < kPDFDocEncodingTableFirstChar) + return nullptr; + +@@ -1779,38 +1791,21 @@ const char* PDF_CharNameFromPredefinedCharSet(int encoding, uint8_t charcode) { + charcode -= kEncodingTableFirstChar; + } + switch (encoding) { +- case PDFFONT_ENCODING_WINANSI: +- return AdobeWinAnsiEncodingNames[charcode]; +- case PDFFONT_ENCODING_MACROMAN: +- return MacRomanEncodingNames[charcode]; +- case PDFFONT_ENCODING_MACEXPERT: +- return MacExpertEncodingNames[charcode]; +- case PDFFONT_ENCODING_STANDARD: +- return StandardEncodingNames[charcode]; +- case PDFFONT_ENCODING_ADOBE_SYMBOL: +- return AdobeSymbolEncodingNames[charcode]; +- case PDFFONT_ENCODING_ZAPFDINGBATS: +- return ZapfEncodingNames[charcode]; +- case PDFFONT_ENCODING_PDFDOC: +- return PDFDocEncodingNames[charcode]; +- } +- return nullptr; +-} +- +-wchar_t FT_UnicodeFromCharCode(int encoding, uint32_t charcode) { +- switch (encoding) { +- case FT_ENCODING_UNICODE: +- return (uint16_t)charcode; +- case FT_ENCODING_ADOBE_STANDARD: +- return StandardEncoding[(uint8_t)charcode]; +- case FT_ENCODING_ADOBE_EXPERT: +- return MacExpertEncoding[(uint8_t)charcode]; +- case FT_ENCODING_ADOBE_LATIN_1: +- return AdobeWinAnsiEncoding[(uint8_t)charcode]; +- case FT_ENCODING_APPLE_ROMAN: +- return MacRomanEncoding[(uint8_t)charcode]; +- case PDFFONT_ENCODING_PDFDOC: +- return PDFDocEncoding[(uint8_t)charcode]; ++ case FontEncoding::kWinAnsi: ++ return kAdobeWinAnsiEncodingNames[charcode]; ++ case FontEncoding::kMacRoman: ++ return kMacRomanEncodingNames[charcode]; ++ case FontEncoding::kMacExpert: ++ return kMacExpertEncodingNames[charcode]; ++ case FontEncoding::kStandard: ++ return kStandardEncodingNames[charcode]; ++ case FontEncoding::kAdobeSymbol: ++ return kAdobeSymbolEncodingNames[charcode]; ++ case FontEncoding::kZapfDingbats: ++ return kZapfEncodingNames[charcode]; ++ case FontEncoding::kPdfDoc: ++ return kPDFDocEncodingNames[charcode]; ++ default: ++ return nullptr; + } +- return 0; + } +diff --git a/core/fpdfapi/font/cpdf_fontencoding.h b/core/fpdfapi/font/cpdf_fontencoding.h +index 1bfb0d5c0..272c8a6d6 100644 +--- a/core/fpdfapi/font/cpdf_fontencoding.h ++++ b/core/fpdfapi/font/cpdf_fontencoding.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,27 +7,29 @@ + #ifndef CORE_FPDFAPI_FONT_CPDF_FONTENCODING_H_ + #define CORE_FPDFAPI_FONT_CPDF_FONTENCODING_H_ + +-#include +- +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/string_pool_template.h" + #include "core/fxcrt/weak_ptr.h" + +-#define PDFFONT_ENCODING_BUILTIN 0 +-#define PDFFONT_ENCODING_WINANSI 1 +-#define PDFFONT_ENCODING_MACROMAN 2 +-#define PDFFONT_ENCODING_MACEXPERT 3 +-#define PDFFONT_ENCODING_STANDARD 4 +-#define PDFFONT_ENCODING_ADOBE_SYMBOL 5 +-#define PDFFONT_ENCODING_ZAPFDINGBATS 6 +-#define PDFFONT_ENCODING_PDFDOC 7 +-#define PDFFONT_ENCODING_MS_SYMBOL 8 ++enum class FontEncoding { ++ kBuiltin = 0, ++ kWinAnsi = 1, ++ kMacRoman = 2, ++ kMacExpert = 3, ++ kStandard = 4, ++ kAdobeSymbol = 5, ++ kZapfDingbats = 6, ++ kPdfDoc = 7, ++ kMsSymbol = 8, ++}; + +-uint32_t FT_CharCodeFromUnicode(int encoding, wchar_t unicode); +-wchar_t FT_UnicodeFromCharCode(int encoding, uint32_t charcode); ++uint32_t CharCodeFromUnicodeForFreetypeEncoding(int encoding, wchar_t unicode); ++wchar_t UnicodeFromAppleRomanCharCode(uint8_t charcode); + +-const uint16_t* PDF_UnicodesForPredefinedCharSet(int encoding); +-const char* PDF_CharNameFromPredefinedCharSet(int encoding, uint8_t charcode); ++const uint16_t* UnicodesForPredefinedCharSet(FontEncoding encoding); ++const char* CharNameFromPredefinedCharSet(FontEncoding encoding, ++ uint8_t charcode); + + class CPDF_Object; + +@@ -35,7 +37,7 @@ class CPDF_FontEncoding { + public: + static constexpr size_t kEncodingTableSize = 256; + +- explicit CPDF_FontEncoding(int PredefinedEncoding); ++ explicit CPDF_FontEncoding(FontEncoding predefined_encoding); + + bool IsIdentical(const CPDF_FontEncoding* pAnother) const; + +@@ -51,7 +53,7 @@ class CPDF_FontEncoding { + RetainPtr Realize(WeakPtr pPool) const; + + private: +- wchar_t m_Unicodes[kEncodingTableSize]; ++ wchar_t m_Unicodes[kEncodingTableSize] = {}; + }; + + #endif // CORE_FPDFAPI_FONT_CPDF_FONTENCODING_H_ +diff --git a/core/fpdfapi/font/cpdf_fontglobals.cpp b/core/fpdfapi/font/cpdf_fontglobals.cpp +index 10b6c5e66..1aa19d612 100644 +--- a/core/fpdfapi/font/cpdf_fontglobals.cpp ++++ b/core/fpdfapi/font/cpdf_fontglobals.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,37 +6,47 @@ + + #include "core/fpdfapi/font/cpdf_fontglobals.h" + ++#include ++ + #include "core/fpdfapi/cmaps/CNS1/cmaps_cns1.h" + #include "core/fpdfapi/cmaps/GB1/cmaps_gb1.h" + #include "core/fpdfapi/cmaps/Japan1/cmaps_japan1.h" + #include "core/fpdfapi/cmaps/Korea1/cmaps_korea1.h" + #include "core/fpdfapi/font/cfx_stockfontarray.h" ++#include "core/fpdfapi/font/cpdf_cid2unicodemap.h" ++#include "core/fpdfapi/font/cpdf_cmap.h" + #include "core/fpdfapi/parser/cpdf_document.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" + + namespace { + + CPDF_FontGlobals* g_FontGlobals = nullptr; + ++RetainPtr LoadPredefinedCMap(ByteStringView name) { ++ if (!name.IsEmpty() && name[0] == '/') ++ name = name.Last(name.GetLength() - 1); ++ return pdfium::MakeRetain(name); ++} ++ + } // namespace + + // static + void CPDF_FontGlobals::Create() { +- ASSERT(!g_FontGlobals); ++ DCHECK(!g_FontGlobals); + g_FontGlobals = new CPDF_FontGlobals(); + } + + // static + void CPDF_FontGlobals::Destroy() { +- ASSERT(g_FontGlobals); ++ DCHECK(g_FontGlobals); + delete g_FontGlobals; + g_FontGlobals = nullptr; + } + + // static + CPDF_FontGlobals* CPDF_FontGlobals::GetInstance() { +- ASSERT(g_FontGlobals); ++ DCHECK(g_FontGlobals); + return g_FontGlobals; + } + +@@ -66,38 +76,63 @@ RetainPtr CPDF_FontGlobals::Find( + + void CPDF_FontGlobals::Set(CPDF_Document* pDoc, + CFX_FontMapper::StandardFont index, +- const RetainPtr& pFont) { +- if (!pdfium::ContainsKey(m_StockMap, pDoc)) +- m_StockMap[pDoc] = pdfium::MakeUnique(); +- m_StockMap[pDoc]->SetFont(index, pFont); ++ RetainPtr pFont) { ++ UnownedPtr pKey(pDoc); ++ if (!pdfium::Contains(m_StockMap, pKey)) ++ m_StockMap[pKey] = std::make_unique(); ++ m_StockMap[pKey]->SetFont(index, std::move(pFont)); + } + + void CPDF_FontGlobals::Clear(CPDF_Document* pDoc) { +- m_StockMap.erase(pDoc); ++ // Avoid constructing smart-pointer key as erase() doesn't invoke ++ // transparent lookup in the same way find() does. ++ auto it = m_StockMap.find(pDoc); ++ if (it != m_StockMap.end()) ++ m_StockMap.erase(it); + } + + void CPDF_FontGlobals::LoadEmbeddedGB1CMaps() { +- SetEmbeddedCharset(CIDSET_GB1, pdfium::make_span(g_FXCMAP_GB1_cmaps, +- g_FXCMAP_GB1_cmaps_size)); +- SetEmbeddedToUnicode(CIDSET_GB1, g_FXCMAP_GB1CID2Unicode_5); ++ SetEmbeddedCharset(CIDSET_GB1, pdfium::make_span(fxcmap::kGB1_cmaps, ++ fxcmap::kGB1_cmaps_size)); ++ SetEmbeddedToUnicode(CIDSET_GB1, fxcmap::kGB1CID2Unicode_5); + } + + void CPDF_FontGlobals::LoadEmbeddedCNS1CMaps() { +- SetEmbeddedCharset(CIDSET_CNS1, pdfium::make_span(g_FXCMAP_CNS1_cmaps, +- g_FXCMAP_CNS1_cmaps_size)); +- SetEmbeddedToUnicode(CIDSET_CNS1, g_FXCMAP_CNS1CID2Unicode_5); ++ SetEmbeddedCharset(CIDSET_CNS1, pdfium::make_span(fxcmap::kCNS1_cmaps, ++ fxcmap::kCNS1_cmaps_size)); ++ SetEmbeddedToUnicode(CIDSET_CNS1, fxcmap::kCNS1CID2Unicode_5); + } + + void CPDF_FontGlobals::LoadEmbeddedJapan1CMaps() { + SetEmbeddedCharset( + CIDSET_JAPAN1, +- pdfium::make_span(g_FXCMAP_Japan1_cmaps, g_FXCMAP_Japan1_cmaps_size)); +- SetEmbeddedToUnicode(CIDSET_JAPAN1, g_FXCMAP_Japan1CID2Unicode_4); ++ pdfium::make_span(fxcmap::kJapan1_cmaps, fxcmap::kJapan1_cmaps_size)); ++ SetEmbeddedToUnicode(CIDSET_JAPAN1, fxcmap::kJapan1CID2Unicode_4); + } + + void CPDF_FontGlobals::LoadEmbeddedKorea1CMaps() { + SetEmbeddedCharset( + CIDSET_KOREA1, +- pdfium::make_span(g_FXCMAP_Korea1_cmaps, g_FXCMAP_Korea1_cmaps_size)); +- SetEmbeddedToUnicode(CIDSET_KOREA1, g_FXCMAP_Korea1CID2Unicode_2); ++ pdfium::make_span(fxcmap::kKorea1_cmaps, fxcmap::kKorea1_cmaps_size)); ++ SetEmbeddedToUnicode(CIDSET_KOREA1, fxcmap::kKorea1CID2Unicode_2); ++} ++ ++RetainPtr CPDF_FontGlobals::GetPredefinedCMap( ++ const ByteString& name) { ++ auto it = m_CMaps.find(name); ++ if (it != m_CMaps.end()) ++ return it->second; ++ ++ RetainPtr pCMap = LoadPredefinedCMap(name.AsStringView()); ++ if (!name.IsEmpty()) ++ m_CMaps[name] = pCMap; ++ ++ return pCMap; ++} ++ ++CPDF_CID2UnicodeMap* CPDF_FontGlobals::GetCID2UnicodeMap(CIDSet charset) { ++ if (!m_CID2UnicodeMaps[charset]) { ++ m_CID2UnicodeMaps[charset] = std::make_unique(charset); ++ } ++ return m_CID2UnicodeMaps[charset].get(); + } +diff --git a/core/fpdfapi/font/cpdf_fontglobals.h b/core/fpdfapi/font/cpdf_fontglobals.h +index c09f29c60..6b2e3bc80 100644 +--- a/core/fpdfapi/font/cpdf_fontglobals.h ++++ b/core/fpdfapi/font/cpdf_fontglobals.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,16 +7,18 @@ + #ifndef CORE_FPDFAPI_FONT_CPDF_FONTGLOBALS_H_ + #define CORE_FPDFAPI_FONT_CPDF_FONTGLOBALS_H_ + ++#include + #include + #include + + #include "core/fpdfapi/cmaps/fpdf_cmaps.h" +-#include "core/fpdfapi/font/cpdf_cmapmanager.h" ++#include "core/fpdfapi/font/cpdf_cidfont.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxge/cfx_fontmapper.h" + #include "third_party/base/span.h" + + class CFX_StockFontArray; ++class CPDF_Font; + + class CPDF_FontGlobals { + public: +@@ -33,22 +35,23 @@ class CPDF_FontGlobals { + CFX_FontMapper::StandardFont index); + void Set(CPDF_Document* pDoc, + CFX_FontMapper::StandardFont index, +- const RetainPtr& pFont); ++ RetainPtr pFont); + +- void SetEmbeddedCharset(size_t idx, pdfium::span map) { ++ void SetEmbeddedCharset(CIDSet idx, pdfium::span map) { + m_EmbeddedCharsets[idx] = map; + } +- pdfium::span GetEmbeddedCharset(size_t idx) const { ++ pdfium::span GetEmbeddedCharset(CIDSet idx) const { + return m_EmbeddedCharsets[idx]; + } +- void SetEmbeddedToUnicode(size_t idx, pdfium::span map) { ++ void SetEmbeddedToUnicode(CIDSet idx, pdfium::span map) { + m_EmbeddedToUnicodes[idx] = map; + } +- pdfium::span GetEmbeddedToUnicode(size_t idx) { ++ pdfium::span GetEmbeddedToUnicode(CIDSet idx) { + return m_EmbeddedToUnicodes[idx]; + } + +- CPDF_CMapManager* GetCMapManager() { return &m_CMapManager; } ++ RetainPtr GetPredefinedCMap(const ByteString& name); ++ CPDF_CID2UnicodeMap* GetCID2UnicodeMap(CIDSet charset); + + private: + CPDF_FontGlobals(); +@@ -59,10 +62,14 @@ class CPDF_FontGlobals { + void LoadEmbeddedJapan1CMaps(); + void LoadEmbeddedKorea1CMaps(); + +- CPDF_CMapManager m_CMapManager; +- pdfium::span m_EmbeddedCharsets[CIDSET_NUM_SETS]; ++ std::map> m_CMaps; ++ std::unique_ptr m_CID2UnicodeMaps[CIDSET_NUM_SETS]; ++ pdfium::span m_EmbeddedCharsets[CIDSET_NUM_SETS]; + pdfium::span m_EmbeddedToUnicodes[CIDSET_NUM_SETS]; +- std::map> m_StockMap; ++ std::map, ++ std::unique_ptr, ++ std::less<>> ++ m_StockMap; + }; + + #endif // CORE_FPDFAPI_FONT_CPDF_FONTGLOBALS_H_ +diff --git a/core/fpdfapi/font/cpdf_simplefont.cpp b/core/fpdfapi/font/cpdf_simplefont.cpp +index c0fd4e0c5..ecdf72a0a 100644 +--- a/core/fpdfapi/font/cpdf_simplefont.cpp ++++ b/core/fpdfapi/font/cpdf_simplefont.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,34 +6,40 @@ + + #include "core/fpdfapi/font/cpdf_simplefont.h" + ++#include ++#include ++#include ++ ++#include "constants/font_encodings.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_name.h" ++#include "core/fxcrt/fx_codepage.h" ++#include "core/fxge/freetype/fx_freetype.h" + #include "core/fxge/fx_font.h" +-#include "core/fxge/fx_freetype.h" + #include "third_party/base/numerics/safe_math.h" + + namespace { + +-void GetPredefinedEncoding(const ByteString& value, int* basemap) { +- if (value == "WinAnsiEncoding") +- *basemap = PDFFONT_ENCODING_WINANSI; +- else if (value == "MacRomanEncoding") +- *basemap = PDFFONT_ENCODING_MACROMAN; +- else if (value == "MacExpertEncoding") +- *basemap = PDFFONT_ENCODING_MACEXPERT; +- else if (value == "PDFDocEncoding") +- *basemap = PDFFONT_ENCODING_PDFDOC; ++void GetPredefinedEncoding(const ByteString& value, FontEncoding* basemap) { ++ if (value == pdfium::font_encodings::kWinAnsiEncoding) ++ *basemap = FontEncoding::kWinAnsi; ++ else if (value == pdfium::font_encodings::kMacRomanEncoding) ++ *basemap = FontEncoding::kMacRoman; ++ else if (value == pdfium::font_encodings::kMacExpertEncoding) ++ *basemap = FontEncoding::kMacExpert; ++ else if (value == pdfium::font_encodings::kPDFDocEncoding) ++ *basemap = FontEncoding::kPdfDoc; + } + + } // namespace + + CPDF_SimpleFont::CPDF_SimpleFont(CPDF_Document* pDocument, +- CPDF_Dictionary* pFontDict) +- : CPDF_Font(pDocument, pFontDict) { ++ RetainPtr pFontDict) ++ : CPDF_Font(pDocument, std::move(pFontDict)) { + memset(m_CharWidth, 0xff, sizeof(m_CharWidth)); + memset(m_GlyphIndex, 0xff, sizeof(m_GlyphIndex)); +- for (size_t i = 0; i < FX_ArraySize(m_CharBBox); ++i) ++ for (size_t i = 0; i < std::size(m_CharBBox); ++i) + m_CharBBox[i] = FX_RECT(-1, -1, -1, -1); + } + +@@ -78,8 +84,8 @@ void CPDF_SimpleFont::LoadCharMetrics(int charcode) { + if (err) + return; + +- int iHoriBearingX = FXFT_Get_Glyph_HoriBearingX(face); +- int iHoriBearingY = FXFT_Get_Glyph_HoriBearingY(face); ++ FT_Pos iHoriBearingX = FXFT_Get_Glyph_HoriBearingX(face); ++ FT_Pos iHoriBearingY = FXFT_Get_Glyph_HoriBearingY(face); + m_CharBBox[charcode] = + FX_RECT(TT2PDF(iHoriBearingX, face), TT2PDF(iHoriBearingY, face), + TT2PDF(iHoriBearingX + FXFT_Get_Glyph_Width(face), face), +@@ -98,31 +104,78 @@ void CPDF_SimpleFont::LoadCharMetrics(int charcode) { + } + } + ++void CPDF_SimpleFont::LoadCharWidths(const CPDF_Dictionary* font_desc) { ++ RetainPtr width_array = m_pFontDict->GetArrayFor("Widths"); ++ m_bUseFontWidth = !width_array; ++ if (!width_array) ++ return; ++ ++ if (font_desc && font_desc->KeyExist("MissingWidth")) { ++ int missing_width = font_desc->GetIntegerFor("MissingWidth"); ++ std::fill(std::begin(m_CharWidth), std::end(m_CharWidth), missing_width); ++ } ++ ++ size_t width_start = m_pFontDict->GetIntegerFor("FirstChar", 0); ++ size_t width_end = m_pFontDict->GetIntegerFor("LastChar", 0); ++ if (width_start > 255) ++ return; ++ ++ if (width_end == 0 || width_end >= width_start + width_array->size()) ++ width_end = width_start + width_array->size() - 1; ++ if (width_end > 255) ++ width_end = 255; ++ for (size_t i = width_start; i <= width_end; i++) ++ m_CharWidth[i] = width_array->GetIntegerAt(i - width_start); ++} ++ ++void CPDF_SimpleFont::LoadDifferences(const CPDF_Dictionary* encoding) { ++ RetainPtr diffs = encoding->GetArrayFor("Differences"); ++ if (!diffs) ++ return; ++ ++ m_CharNames.resize(kInternalTableSize); ++ uint32_t cur_code = 0; ++ for (uint32_t i = 0; i < diffs->size(); i++) { ++ RetainPtr element = diffs->GetDirectObjectAt(i); ++ if (!element) ++ continue; ++ ++ const CPDF_Name* name = element->AsName(); ++ if (name) { ++ if (cur_code < m_CharNames.size()) ++ m_CharNames[cur_code] = name->GetString(); ++ cur_code++; ++ } else { ++ cur_code = element->GetInteger(); ++ } ++ } ++} ++ + void CPDF_SimpleFont::LoadPDFEncoding(bool bEmbedded, bool bTrueType) { +- const CPDF_Object* pEncoding = m_pFontDict->GetDirectObjectFor("Encoding"); ++ RetainPtr pEncoding = ++ m_pFontDict->GetDirectObjectFor("Encoding"); + if (!pEncoding) { + if (m_BaseFontName == "Symbol") { +- m_BaseEncoding = bTrueType ? PDFFONT_ENCODING_MS_SYMBOL +- : PDFFONT_ENCODING_ADOBE_SYMBOL; +- } else if (!bEmbedded && m_BaseEncoding == PDFFONT_ENCODING_BUILTIN) { +- m_BaseEncoding = PDFFONT_ENCODING_WINANSI; ++ m_BaseEncoding = ++ bTrueType ? FontEncoding::kMsSymbol : FontEncoding::kAdobeSymbol; ++ } else if (!bEmbedded && m_BaseEncoding == FontEncoding::kBuiltin) { ++ m_BaseEncoding = FontEncoding::kWinAnsi; + } + return; + } + if (pEncoding->IsName()) { +- if (m_BaseEncoding == PDFFONT_ENCODING_ADOBE_SYMBOL || +- m_BaseEncoding == PDFFONT_ENCODING_ZAPFDINGBATS) { ++ if (m_BaseEncoding == FontEncoding::kAdobeSymbol || ++ m_BaseEncoding == FontEncoding::kZapfDingbats) { + return; + } + if (FontStyleIsSymbolic(m_Flags) && m_BaseFontName == "Symbol") { + if (!bTrueType) +- m_BaseEncoding = PDFFONT_ENCODING_ADOBE_SYMBOL; ++ m_BaseEncoding = FontEncoding::kAdobeSymbol; + return; + } + ByteString bsEncoding = pEncoding->GetString(); +- if (bsEncoding.Compare("MacExpertEncoding") == 0) { +- bsEncoding = "WinAnsiEncoding"; +- } ++ if (bsEncoding == pdfium::font_encodings::kMacExpertEncoding) ++ bsEncoding = pdfium::font_encodings::kWinAnsiEncoding; + GetPredefinedEncoding(bsEncoding, &m_BaseEncoding); + return; + } +@@ -131,39 +184,20 @@ void CPDF_SimpleFont::LoadPDFEncoding(bool bEmbedded, bool bTrueType) { + if (!pDict) + return; + +- if (m_BaseEncoding != PDFFONT_ENCODING_ADOBE_SYMBOL && +- m_BaseEncoding != PDFFONT_ENCODING_ZAPFDINGBATS) { +- ByteString bsEncoding = pDict->GetStringFor("BaseEncoding"); +- if (bTrueType && bsEncoding.Compare("MacExpertEncoding") == 0) +- bsEncoding = "WinAnsiEncoding"; ++ if (m_BaseEncoding != FontEncoding::kAdobeSymbol && ++ m_BaseEncoding != FontEncoding::kZapfDingbats) { ++ ByteString bsEncoding = pDict->GetByteStringFor("BaseEncoding"); ++ if (bTrueType && bsEncoding == pdfium::font_encodings::kMacExpertEncoding) ++ bsEncoding = pdfium::font_encodings::kWinAnsiEncoding; + GetPredefinedEncoding(bsEncoding, &m_BaseEncoding); + } +- if ((!bEmbedded || bTrueType) && m_BaseEncoding == PDFFONT_ENCODING_BUILTIN) +- m_BaseEncoding = PDFFONT_ENCODING_STANDARD; +- +- const CPDF_Array* pDiffs = pDict->GetArrayFor("Differences"); +- if (!pDiffs) +- return; ++ if ((!bEmbedded || bTrueType) && m_BaseEncoding == FontEncoding::kBuiltin) ++ m_BaseEncoding = FontEncoding::kStandard; + +- m_CharNames.resize(256); +- uint32_t cur_code = 0; +- for (uint32_t i = 0; i < pDiffs->size(); i++) { +- const CPDF_Object* pElement = pDiffs->GetDirectObjectAt(i); +- if (!pElement) +- continue; +- +- const CPDF_Name* pName = pElement->AsName(); +- if (pName) { +- if (cur_code < 256) +- m_CharNames[cur_code] = pName->GetString(); +- cur_code++; +- } else { +- cur_code = pElement->GetInteger(); +- } +- } ++ LoadDifferences(pDict); + } + +-uint32_t CPDF_SimpleFont::GetCharWidthF(uint32_t charcode) { ++int CPDF_SimpleFont::GetCharWidthF(uint32_t charcode) { + if (charcode > 0xff) + charcode = 0; + +@@ -187,30 +221,11 @@ FX_RECT CPDF_SimpleFont::GetCharBBox(uint32_t charcode) { + } + + bool CPDF_SimpleFont::LoadCommon() { +- const CPDF_Dictionary* pFontDesc = m_pFontDict->GetDictFor("FontDescriptor"); +- if (pFontDesc) { +- LoadFontDescriptor(pFontDesc); +- } +- const CPDF_Array* pWidthArray = m_pFontDict->GetArrayFor("Widths"); +- m_bUseFontWidth = !pWidthArray; +- if (pWidthArray) { +- if (pFontDesc && pFontDesc->KeyExist("MissingWidth")) { +- int MissingWidth = pFontDesc->GetIntegerFor("MissingWidth"); +- for (int i = 0; i < 256; i++) { +- m_CharWidth[i] = MissingWidth; +- } +- } +- size_t width_start = m_pFontDict->GetIntegerFor("FirstChar", 0); +- size_t width_end = m_pFontDict->GetIntegerFor("LastChar", 0); +- if (width_start <= 255) { +- if (width_end == 0 || width_end >= width_start + pWidthArray->size()) +- width_end = width_start + pWidthArray->size() - 1; +- if (width_end > 255) +- width_end = 255; +- for (size_t i = width_start; i <= width_end; i++) +- m_CharWidth[i] = pWidthArray->GetIntegerAt(i - width_start); +- } +- } ++ RetainPtr pFontDesc = ++ m_pFontDict->GetDictFor("FontDescriptor"); ++ if (pFontDesc) ++ LoadFontDescriptor(pFontDesc.Get()); ++ LoadCharWidths(pFontDesc.Get()); + if (m_pFontFile) { + if (m_BaseFontName.GetLength() > 8 && m_BaseFontName[7] == '+') + m_BaseFontName = m_BaseFontName.Last(m_BaseFontName.GetLength() - 8); +@@ -218,7 +233,7 @@ bool CPDF_SimpleFont::LoadCommon() { + LoadSubstFont(); + } + if (!FontStyleIsSymbolic(m_Flags)) +- m_BaseEncoding = PDFFONT_ENCODING_STANDARD; ++ m_BaseEncoding = FontEncoding::kStandard; + LoadPDFEncoding(!!m_pFontFile, m_Font.IsTTFont()); + LoadGlyphMap(); + m_CharNames.clear(); +@@ -228,7 +243,7 @@ bool CPDF_SimpleFont::LoadCommon() { + if (FontStyleIsAllCaps(m_Flags)) { + static const unsigned char kLowercases[][2] = { + {'a', 'z'}, {0xe0, 0xf6}, {0xf8, 0xfd}}; +- for (size_t range = 0; range < FX_ArraySize(kLowercases); ++range) { ++ for (size_t range = 0; range < std::size(kLowercases); ++range) { + const auto& lower = kLowercases[range]; + for (int i = lower[0]; i <= lower[1]; ++i) { + if (m_GlyphIndex[i] != 0xffff && m_pFontFile) +@@ -249,8 +264,9 @@ bool CPDF_SimpleFont::LoadCommon() { + + void CPDF_SimpleFont::LoadSubstFont() { + if (!m_bUseFontWidth && !FontStyleIsFixedPitch(m_Flags)) { +- int width = 0, i; +- for (i = 0; i < 256; i++) { ++ int width = 0; ++ size_t i; ++ for (i = 0; i < kInternalTableSize; i++) { + if (m_CharWidth[i] == 0 || m_CharWidth[i] == 0xffff) + continue; + +@@ -259,17 +275,17 @@ void CPDF_SimpleFont::LoadSubstFont() { + else if (width != m_CharWidth[i]) + break; + } +- if (i == 256 && width) ++ if (i == kInternalTableSize && width) + m_Flags |= FXFONT_FIXED_PITCH; + } + m_Font.LoadSubst(m_BaseFontName, IsTrueTypeFont(), m_Flags, GetFontWeight(), +- m_ItalicAngle, 0, false); ++ m_ItalicAngle, FX_CodePage::kDefANSI, false); + } + + bool CPDF_SimpleFont::IsUnicodeCompatible() const { +- return m_BaseEncoding != PDFFONT_ENCODING_BUILTIN && +- m_BaseEncoding != PDFFONT_ENCODING_ADOBE_SYMBOL && +- m_BaseEncoding != PDFFONT_ENCODING_ZAPFDINGBATS; ++ return m_BaseEncoding != FontEncoding::kBuiltin && ++ m_BaseEncoding != FontEncoding::kAdobeSymbol && ++ m_BaseEncoding != FontEncoding::kZapfDingbats; + } + + WideString CPDF_SimpleFont::UnicodeFromCharCode(uint32_t charcode) const { +@@ -279,7 +295,7 @@ WideString CPDF_SimpleFont::UnicodeFromCharCode(uint32_t charcode) const { + wchar_t ret = m_Encoding.UnicodeFromCharCode((uint8_t)charcode); + if (ret == 0) + return WideString(); +- return ret; ++ return WideString(ret); + } + + uint32_t CPDF_SimpleFont::CharCodeFromUnicode(wchar_t unicode) const { +diff --git a/core/fpdfapi/font/cpdf_simplefont.h b/core/fpdfapi/font/cpdf_simplefont.h +index 11359c3f5..e0d0400f4 100644 +--- a/core/fpdfapi/font/cpdf_simplefont.h ++++ b/core/fpdfapi/font/cpdf_simplefont.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,19 +7,20 @@ + #ifndef CORE_FPDFAPI_FONT_CPDF_SIMPLEFONT_H_ + #define CORE_FPDFAPI_FONT_CPDF_SIMPLEFONT_H_ + ++#include ++ + #include + + #include "core/fpdfapi/font/cpdf_font.h" + #include "core/fpdfapi/font/cpdf_fontencoding.h" + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" + + class CPDF_SimpleFont : public CPDF_Font { + public: + ~CPDF_SimpleFont() override; + + // CPDF_Font +- uint32_t GetCharWidthF(uint32_t charcode) override; ++ int GetCharWidthF(uint32_t charcode) override; + FX_RECT GetCharBBox(uint32_t charcode) override; + int GlyphFromCharCode(uint32_t charcode, bool* pVertGlyph) override; + bool IsUnicodeCompatible() const override; +@@ -31,22 +32,27 @@ class CPDF_SimpleFont : public CPDF_Font { + bool HasFontWidths() const override; + + protected: +- CPDF_SimpleFont(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict); ++ static constexpr size_t kInternalTableSize = 256; ++ ++ CPDF_SimpleFont(CPDF_Document* pDocument, ++ RetainPtr pFontDict); + + virtual void LoadGlyphMap() = 0; + + bool LoadCommon(); + void LoadSubstFont(); + void LoadCharMetrics(int charcode); ++ void LoadCharWidths(const CPDF_Dictionary* font_desc); ++ void LoadDifferences(const CPDF_Dictionary* encoding); + void LoadPDFEncoding(bool bEmbedded, bool bTrueType); + +- CPDF_FontEncoding m_Encoding{PDFFONT_ENCODING_BUILTIN}; +- int m_BaseEncoding = PDFFONT_ENCODING_BUILTIN; +- bool m_bUseFontWidth; ++ CPDF_FontEncoding m_Encoding{FontEncoding::kBuiltin}; ++ FontEncoding m_BaseEncoding = FontEncoding::kBuiltin; ++ bool m_bUseFontWidth = false; + std::vector m_CharNames; +- uint16_t m_GlyphIndex[256]; +- uint16_t m_CharWidth[256]; +- FX_RECT m_CharBBox[256]; ++ uint16_t m_GlyphIndex[kInternalTableSize]; ++ uint16_t m_CharWidth[kInternalTableSize]; ++ FX_RECT m_CharBBox[kInternalTableSize]; + }; + + #endif // CORE_FPDFAPI_FONT_CPDF_SIMPLEFONT_H_ +diff --git a/core/fpdfapi/font/cpdf_tounicodemap.cpp b/core/fpdfapi/font/cpdf_tounicodemap.cpp +index 1872d5d7b..3c6e73ed3 100644 +--- a/core/fpdfapi/font/cpdf_tounicodemap.cpp ++++ b/core/fpdfapi/font/cpdf_tounicodemap.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,7 @@ + + #include "core/fpdfapi/font/cpdf_tounicodemap.h" + ++#include + #include + + #include "core/fpdfapi/font/cpdf_cid2unicodemap.h" +@@ -14,6 +15,7 @@ + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_safe_types.h" ++#include "third_party/base/containers/contains.h" + #include "third_party/base/numerics/safe_conversions.h" + + namespace { +@@ -37,56 +39,60 @@ WideString StringDataAdd(WideString str) { + + } // namespace + +-CPDF_ToUnicodeMap::CPDF_ToUnicodeMap(const CPDF_Stream* pStream) { +- Load(pStream); ++CPDF_ToUnicodeMap::CPDF_ToUnicodeMap(RetainPtr pStream) { ++ Load(std::move(pStream)); + } + + CPDF_ToUnicodeMap::~CPDF_ToUnicodeMap() = default; + + WideString CPDF_ToUnicodeMap::Lookup(uint32_t charcode) const { +- auto it = m_Map.find(charcode); +- if (it == m_Map.end()) { ++ auto it = m_Multimap.find(charcode); ++ if (it == m_Multimap.end()) { + if (!m_pBaseMap) + return WideString(); +- return m_pBaseMap->UnicodeFromCID(static_cast(charcode)); ++ return WideString( ++ m_pBaseMap->UnicodeFromCID(static_cast(charcode))); + } + +- uint32_t value = it->second; ++ uint32_t value = *it->second.begin(); + wchar_t unicode = static_cast(value & 0xffff); + if (unicode != 0xffff) +- return unicode; ++ return WideString(unicode); + +- WideStringView buf = m_MultiCharBuf.AsStringView(); + size_t index = value >> 16; +- if (!buf.IsValidIndex(index)) +- return WideString(); +- return WideString(buf.Substr(index + 1, buf[index])); ++ return index < m_MultiCharVec.size() ? m_MultiCharVec[index] : WideString(); + } + + uint32_t CPDF_ToUnicodeMap::ReverseLookup(wchar_t unicode) const { +- for (const auto& pair : m_Map) { +- if (pair.second == static_cast(unicode)) ++ for (const auto& pair : m_Multimap) { ++ if (pdfium::Contains(pair.second, static_cast(unicode))) + return pair.first; + } + return 0; + } + ++size_t CPDF_ToUnicodeMap::GetUnicodeCountByCharcodeForTesting( ++ uint32_t charcode) const { ++ auto it = m_Multimap.find(charcode); ++ return it != m_Multimap.end() ? it->second.size() : 0u; ++} ++ + // static +-pdfium::Optional CPDF_ToUnicodeMap::StringToCode(ByteStringView str) { ++absl::optional CPDF_ToUnicodeMap::StringToCode(ByteStringView str) { + size_t len = str.GetLength(); + if (len <= 2 || str[0] != '<' || str[len - 1] != '>') +- return pdfium::nullopt; ++ return absl::nullopt; + + FX_SAFE_UINT32 code = 0; + for (char c : str.Substr(1, len - 2)) { + if (!FXSYS_IsHexDigit(c)) +- return pdfium::nullopt; ++ return absl::nullopt; + + code = code * 16 + FXSYS_HexCharToInt(c); + if (!code.IsValid()) +- return pdfium::nullopt; ++ return absl::nullopt; + } +- return pdfium::Optional(code.ValueOrDie()); ++ return absl::optional(code.ValueOrDie()); + } + + // static +@@ -113,12 +119,12 @@ WideString CPDF_ToUnicodeMap::StringToWideString(ByteStringView str) { + return result; + } + +-void CPDF_ToUnicodeMap::Load(const CPDF_Stream* pStream) { ++void CPDF_ToUnicodeMap::Load(RetainPtr pStream) { + CIDSet cid_set = CIDSET_UNKNOWN; +- auto pAcc = pdfium::MakeRetain(pStream); ++ auto pAcc = pdfium::MakeRetain(std::move(pStream)); + pAcc->LoadAllDataFiltered(); + CPDF_SimpleParser parser(pAcc->GetSpan()); +- while (1) { ++ while (true) { + ByteStringView word = parser.GetWord(); + if (word.IsEmpty()) + break; +@@ -136,19 +142,18 @@ void CPDF_ToUnicodeMap::Load(const CPDF_Stream* pStream) { + else if (word == "/Adobe-GB1-UCS2") + cid_set = CIDSET_GB1; + } +- if (cid_set) { +- auto* manager = CPDF_FontGlobals::GetInstance()->GetCMapManager(); +- m_pBaseMap = manager->GetCID2UnicodeMap(cid_set); ++ if (cid_set != CIDSET_UNKNOWN) { ++ m_pBaseMap = CPDF_FontGlobals::GetInstance()->GetCID2UnicodeMap(cid_set); + } + } + + void CPDF_ToUnicodeMap::HandleBeginBFChar(CPDF_SimpleParser* pParser) { +- while (1) { ++ while (true) { + ByteStringView word = pParser->GetWord(); + if (word.IsEmpty() || word == "endbfchar") + return; + +- pdfium::Optional code = StringToCode(word); ++ absl::optional code = StringToCode(word); + if (!code.has_value()) + return; + +@@ -157,17 +162,17 @@ void CPDF_ToUnicodeMap::HandleBeginBFChar(CPDF_SimpleParser* pParser) { + } + + void CPDF_ToUnicodeMap::HandleBeginBFRange(CPDF_SimpleParser* pParser) { +- while (1) { ++ while (true) { + ByteStringView lowcode_str = pParser->GetWord(); + if (lowcode_str.IsEmpty() || lowcode_str == "endbfrange") + return; + +- pdfium::Optional lowcode_opt = StringToCode(lowcode_str); ++ absl::optional lowcode_opt = StringToCode(lowcode_str); + if (!lowcode_opt.has_value()) + return; + + ByteStringView highcode_str = pParser->GetWord(); +- pdfium::Optional highcode_opt = StringToCode(highcode_str); ++ absl::optional highcode_opt = StringToCode(highcode_str); + if (!highcode_opt.has_value()) + return; + +@@ -176,36 +181,41 @@ void CPDF_ToUnicodeMap::HandleBeginBFRange(CPDF_SimpleParser* pParser) { + + ByteStringView start = pParser->GetWord(); + if (start == "[") { +- for (uint32_t code = lowcode; code <= highcode; code++) +- SetCode(code, StringToWideString(pParser->GetWord())); ++ for (FX_SAFE_UINT32 code = lowcode; ++ code.IsValid() && code.ValueOrDie() <= highcode; code++) { ++ SetCode(code.ValueOrDie(), StringToWideString(pParser->GetWord())); ++ } + pParser->GetWord(); + continue; + } + + WideString destcode = StringToWideString(start); + if (destcode.GetLength() == 1) { +- pdfium::Optional value_or_error = StringToCode(start); ++ absl::optional value_or_error = StringToCode(start); + if (!value_or_error.has_value()) + return; + + uint32_t value = value_or_error.value(); +- for (uint32_t code = lowcode; code <= highcode; code++) +- m_Map[code] = value++; ++ for (FX_SAFE_UINT32 code = lowcode; ++ code.IsValid() && code.ValueOrDie() <= highcode; code++) { ++ InsertIntoMultimap(code.ValueOrDie(), value++); ++ } + } else { +- for (uint32_t code = lowcode; code <= highcode; code++) { ++ for (FX_SAFE_UINT32 code = lowcode; ++ code.IsValid() && code.ValueOrDie() <= highcode; code++) { ++ uint32_t code_value = code.ValueOrDie(); + WideString retcode = +- code == lowcode ? destcode : StringDataAdd(destcode); +- m_Map[code] = GetUnicode(); +- m_MultiCharBuf.AppendChar(retcode.GetLength()); +- m_MultiCharBuf << retcode; ++ code_value == lowcode ? destcode : StringDataAdd(destcode); ++ InsertIntoMultimap(code_value, GetMultiCharIndexIndicator()); ++ m_MultiCharVec.push_back(retcode); + destcode = std::move(retcode); + } + } + } + } + +-uint32_t CPDF_ToUnicodeMap::GetUnicode() const { +- FX_SAFE_UINT32 uni = m_MultiCharBuf.GetLength(); ++uint32_t CPDF_ToUnicodeMap::GetMultiCharIndexIndicator() const { ++ FX_SAFE_UINT32 uni = m_MultiCharVec.size(); + uni = uni * 0x10000 + 0xffff; + return uni.ValueOrDefault(0); + } +@@ -216,10 +226,19 @@ void CPDF_ToUnicodeMap::SetCode(uint32_t srccode, WideString destcode) { + return; + + if (len == 1) { +- m_Map[srccode] = destcode[0]; ++ InsertIntoMultimap(srccode, destcode[0]); + } else { +- m_Map[srccode] = GetUnicode(); +- m_MultiCharBuf.AppendChar(len); +- m_MultiCharBuf << destcode; ++ InsertIntoMultimap(srccode, GetMultiCharIndexIndicator()); ++ m_MultiCharVec.push_back(destcode); + } + } ++ ++void CPDF_ToUnicodeMap::InsertIntoMultimap(uint32_t code, uint32_t destcode) { ++ auto it = m_Multimap.find(code); ++ if (it == m_Multimap.end()) { ++ m_Multimap.emplace(code, std::set{destcode}); ++ return; ++ } ++ ++ it->second.emplace(destcode); ++} +diff --git a/core/fpdfapi/font/cpdf_tounicodemap.h b/core/fpdfapi/font/cpdf_tounicodemap.h +index 9eaf625e1..a074f4518 100644 +--- a/core/fpdfapi/font/cpdf_tounicodemap.h ++++ b/core/fpdfapi/font/cpdf_tounicodemap.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,9 +8,13 @@ + #define CORE_FPDFAPI_FONT_CPDF_TOUNICODEMAP_H_ + + #include ++#include ++#include + +-#include "core/fxcrt/cfx_widetextbuf.h" ++#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CPDF_CID2UnicodeMap; + class CPDF_SimpleParser; +@@ -18,28 +22,34 @@ class CPDF_Stream; + + class CPDF_ToUnicodeMap { + public: +- explicit CPDF_ToUnicodeMap(const CPDF_Stream* pStream); ++ explicit CPDF_ToUnicodeMap(RetainPtr pStream); + ~CPDF_ToUnicodeMap(); + + WideString Lookup(uint32_t charcode) const; + uint32_t ReverseLookup(wchar_t unicode) const; + ++ size_t GetUnicodeCountByCharcodeForTesting(uint32_t charcode) const; ++ + private: + friend class cpdf_tounicodemap_StringToCode_Test; + friend class cpdf_tounicodemap_StringToWideString_Test; + +- static pdfium::Optional StringToCode(ByteStringView str); ++ static absl::optional StringToCode(ByteStringView str); + static WideString StringToWideString(ByteStringView str); + +- void Load(const CPDF_Stream* pStream); ++ void Load(RetainPtr pStream); + void HandleBeginBFChar(CPDF_SimpleParser* pParser); + void HandleBeginBFRange(CPDF_SimpleParser* pParser); +- uint32_t GetUnicode() const; ++ uint32_t GetMultiCharIndexIndicator() const; + void SetCode(uint32_t srccode, WideString destcode); + +- std::map m_Map; ++ // Inserts a new entry which hasn't not been inserted into `m_Multimap` ++ // before. ++ void InsertIntoMultimap(uint32_t code, uint32_t destcode); ++ ++ std::map> m_Multimap; + UnownedPtr m_pBaseMap; +- CFX_WideTextBuf m_MultiCharBuf; ++ std::vector m_MultiCharVec; + }; + + #endif // CORE_FPDFAPI_FONT_CPDF_TOUNICODEMAP_H_ +diff --git a/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp b/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp +index 7dacc5a4e..86ac2b1df 100644 +--- a/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp ++++ b/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp +@@ -1,11 +1,14 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/font/cpdf_tounicodemap.h" + ++#include "core/fpdfapi/parser/cpdf_stream.h" ++#include "core/fxcrt/retain_ptr.h" + #include "testing/gmock/include/gmock/gmock.h" + #include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/base/span.h" + + TEST(cpdf_tounicodemap, StringToCode) { + EXPECT_THAT(CPDF_ToUnicodeMap::StringToCode("<0001>"), testing::Optional(1u)); +@@ -47,3 +50,70 @@ TEST(cpdf_tounicodemap, StringToWideString) { + EXPECT_EQ(res, CPDF_ToUnicodeMap::StringToWideString("")); + EXPECT_EQ(res, CPDF_ToUnicodeMap::StringToWideString("")); + } ++ ++TEST(cpdf_tounicodemap, HandleBeginBFRangeAvoidIntegerOverflow) { ++ // Make sure there won't be infinite loops due to integer overflows in ++ // HandleBeginBFRange(). ++ { ++ static constexpr uint8_t kInput1[] = ++ "beginbfrange[<0041>]endbfrange"; ++ auto stream = pdfium::MakeRetain(); ++ stream->SetData(pdfium::make_span(kInput1)); ++ CPDF_ToUnicodeMap map(stream); ++ EXPECT_STREQ(L"A", map.Lookup(0xffffffff).c_str()); ++ } ++ { ++ static constexpr uint8_t kInput2[] = ++ "beginbfrange<0042>endbfrange"; ++ auto stream = pdfium::MakeRetain(); ++ stream->SetData(pdfium::make_span(kInput2)); ++ CPDF_ToUnicodeMap map(stream); ++ EXPECT_STREQ(L"B", map.Lookup(0xffffffff).c_str()); ++ } ++ { ++ static constexpr uint8_t kInput3[] = ++ "beginbfrange<00410042>endbfrange"; ++ auto stream = pdfium::MakeRetain(); ++ stream->SetData(pdfium::make_span(kInput3)); ++ CPDF_ToUnicodeMap map(stream); ++ EXPECT_STREQ(L"AB", map.Lookup(0xffffffff).c_str()); ++ } ++} ++ ++TEST(cpdf_tounicodemap, InsertIntoMultimap) { ++ { ++ // Both the CIDs and the unicodes are different. ++ static constexpr uint8_t kInput1[] = ++ "beginbfchar<1><0041><2><0042>endbfchar"; ++ auto stream = pdfium::MakeRetain(); ++ stream->SetData(pdfium::make_span(kInput1)); ++ CPDF_ToUnicodeMap map(stream); ++ EXPECT_EQ(1u, map.ReverseLookup(0x0041)); ++ EXPECT_EQ(2u, map.ReverseLookup(0x0042)); ++ EXPECT_EQ(1u, map.GetUnicodeCountByCharcodeForTesting(1u)); ++ EXPECT_EQ(1u, map.GetUnicodeCountByCharcodeForTesting(2u)); ++ } ++ { ++ // The same CID with different unicodes. ++ static constexpr uint8_t kInput2[] = ++ "beginbfrange<0><0><0041><0><0><0042>endbfrange"; ++ auto stream = pdfium::MakeRetain(); ++ stream->SetData(pdfium::make_span(kInput2)); ++ CPDF_ToUnicodeMap map(stream); ++ EXPECT_EQ(0u, map.ReverseLookup(0x0041)); ++ EXPECT_EQ(0u, map.ReverseLookup(0x0042)); ++ EXPECT_EQ(2u, map.GetUnicodeCountByCharcodeForTesting(0u)); ++ } ++ { ++ // Duplicate mappings of CID 0 to unicode "A". There should be only 1 entry ++ // in `m_Multimap`. ++ static constexpr uint8_t kInput3[] = ++ "beginbfrange<0><0>[<0041>]endbfrange\n" ++ "beginbfchar<0><0041>endbfchar"; ++ auto stream = pdfium::MakeRetain(); ++ stream->SetData(pdfium::make_span(kInput3)); ++ CPDF_ToUnicodeMap map(stream); ++ EXPECT_EQ(0u, map.ReverseLookup(0x0041)); ++ EXPECT_EQ(1u, map.GetUnicodeCountByCharcodeForTesting(0u)); ++ } ++} +diff --git a/core/fpdfapi/font/cpdf_truetypefont.cpp b/core/fpdfapi/font/cpdf_truetypefont.cpp +index d107ed0f8..0a59b54eb 100644 +--- a/core/fpdfapi/font/cpdf_truetypefont.cpp ++++ b/core/fpdfapi/font/cpdf_truetypefont.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,18 +6,37 @@ + + #include "core/fpdfapi/font/cpdf_truetypefont.h" + ++#include ++#include ++ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fxge/fx_font.h" ++#include "third_party/base/cxx17_backports.h" + + namespace { + +-const uint8_t kPrefix[4] = {0x00, 0xf0, 0xf1, 0xf2}; ++constexpr uint8_t kPrefix[4] = {0x00, 0xf0, 0xf1, 0xf2}; ++ ++uint16_t GetGlyphIndexForMSSymbol(FXFT_FaceRec* face, uint32_t charcode) { ++ for (uint8_t c : kPrefix) { ++ uint16_t unicode = c * 256 + charcode; ++ uint16_t val = FT_Get_Char_Index(face, unicode); ++ if (val) ++ return val; ++ } ++ return 0; ++} ++ ++bool IsWinAnsiOrMacRomanEncoding(FontEncoding encoding) { ++ return encoding == FontEncoding::kWinAnsi || ++ encoding == FontEncoding::kMacRoman; ++} + + } // namespace + + CPDF_TrueTypeFont::CPDF_TrueTypeFont(CPDF_Document* pDocument, +- CPDF_Dictionary* pFontDict) +- : CPDF_SimpleFont(pDocument, pFontDict) {} ++ RetainPtr pFontDict) ++ : CPDF_SimpleFont(pDocument, std::move(pFontDict)) {} + + CPDF_TrueTypeFont::~CPDF_TrueTypeFont() = default; + +@@ -42,86 +61,37 @@ void CPDF_TrueTypeFont::LoadGlyphMap() { + if (!face) + return; + +- int baseEncoding = m_BaseEncoding; +- if (m_pFontFile && face->num_charmaps > 0 && +- (baseEncoding == PDFFONT_ENCODING_MACROMAN || +- baseEncoding == PDFFONT_ENCODING_WINANSI) && +- FontStyleIsSymbolic(m_Flags)) { +- bool bSupportWin = false; +- bool bSupportMac = false; +- for (int i = 0; i < FXFT_Get_Face_CharmapCount(face); i++) { +- int platform_id = +- FXFT_Get_Charmap_PlatformID(FXFT_Get_Face_Charmaps(face)[i]); +- if (platform_id == 0 || platform_id == 3) { +- bSupportWin = true; +- } else if (platform_id == 0 || platform_id == 1) { +- bSupportMac = true; +- } +- } +- if (baseEncoding == PDFFONT_ENCODING_WINANSI && !bSupportWin) { +- baseEncoding = +- bSupportMac ? PDFFONT_ENCODING_MACROMAN : PDFFONT_ENCODING_BUILTIN; +- } else if (baseEncoding == PDFFONT_ENCODING_MACROMAN && !bSupportMac) { +- baseEncoding = +- bSupportWin ? PDFFONT_ENCODING_WINANSI : PDFFONT_ENCODING_BUILTIN; +- } +- } +- if (((baseEncoding == PDFFONT_ENCODING_MACROMAN || +- baseEncoding == PDFFONT_ENCODING_WINANSI) && +- m_CharNames.empty()) || ++ const FontEncoding base_encoding = DetermineEncoding(); ++ if ((IsWinAnsiOrMacRomanEncoding(base_encoding) && m_CharNames.empty()) || + FontStyleIsNonSymbolic(m_Flags)) { + if (!FXFT_Has_Glyph_Names(face) && + (!face->num_charmaps || !face->charmaps)) { +- int nStartChar = m_pFontDict->GetIntegerFor("FirstChar"); +- if (nStartChar < 0 || nStartChar > 255) +- return; +- +- int charcode = 0; +- for (; charcode < nStartChar; charcode++) +- m_GlyphIndex[charcode] = 0; +- uint16_t nGlyph = charcode - nStartChar + 3; +- for (; charcode < 256; charcode++, nGlyph++) +- m_GlyphIndex[charcode] = nGlyph; ++ SetGlyphIndicesFromFirstChar(); + return; + } +- bool bMSUnicode = FT_UseTTCharmap(face, 3, 1); +- bool bMacRoman = false; +- bool bMSSymbol = false; +- if (!bMSUnicode) { +- if (FontStyleIsNonSymbolic(m_Flags)) { +- bMacRoman = FT_UseTTCharmap(face, 1, 0); +- bMSSymbol = !bMacRoman && FT_UseTTCharmap(face, 3, 0); +- } else { +- bMSSymbol = FT_UseTTCharmap(face, 3, 0); +- bMacRoman = !bMSSymbol && FT_UseTTCharmap(face, 1, 0); +- } +- } ++ ++ const CharmapType charmap_type = DetermineCharmapType(); + bool bToUnicode = m_pFontDict->KeyExist("ToUnicode"); + for (uint32_t charcode = 0; charcode < 256; charcode++) { +- const char* name = GetAdobeCharName(baseEncoding, m_CharNames, charcode); ++ const char* name = GetAdobeCharName(base_encoding, m_CharNames, charcode); + if (!name) { + m_GlyphIndex[charcode] = + m_pFontFile ? FT_Get_Char_Index(face, charcode) : -1; + continue; + } +- m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name)); +- if (bMSSymbol) { +- for (size_t j = 0; j < FX_ArraySize(kPrefix); j++) { +- uint16_t unicode = kPrefix[j] * 256 + charcode; +- m_GlyphIndex[charcode] = FT_Get_Char_Index(face, unicode); +- if (m_GlyphIndex[charcode]) +- break; +- } ++ m_Encoding.SetUnicode(charcode, UnicodeFromAdobeName(name)); ++ if (charmap_type == CharmapType::kMSSymbol) { ++ m_GlyphIndex[charcode] = GetGlyphIndexForMSSymbol(face, charcode); + } else if (m_Encoding.UnicodeFromCharCode(charcode)) { +- if (bMSUnicode) { ++ if (charmap_type == CharmapType::kMSUnicode) { + m_GlyphIndex[charcode] = + FT_Get_Char_Index(face, m_Encoding.UnicodeFromCharCode(charcode)); +- } else if (bMacRoman) { +- uint32_t maccode = +- FT_CharCodeFromUnicode(FT_ENCODING_APPLE_ROMAN, +- m_Encoding.UnicodeFromCharCode(charcode)); ++ } else if (charmap_type == CharmapType::kMacRoman) { ++ uint32_t maccode = CharCodeFromUnicodeForFreetypeEncoding( ++ FT_ENCODING_APPLE_ROMAN, ++ m_Encoding.UnicodeFromCharCode(charcode)); + if (!maccode) { +- m_GlyphIndex[charcode] = FXFT_Get_Name_Index(face, name); ++ m_GlyphIndex[charcode] = FT_Get_Name_Index(face, name); + } else { + m_GlyphIndex[charcode] = FT_Get_Char_Index(face, maccode); + } +@@ -135,7 +105,7 @@ void CPDF_TrueTypeFont::LoadGlyphMap() { + m_GlyphIndex[charcode] = FT_Get_Char_Index(face, 32); + continue; + } +- m_GlyphIndex[charcode] = FXFT_Get_Name_Index(face, name); ++ m_GlyphIndex[charcode] = FT_Get_Name_Index(face, name); + if (m_GlyphIndex[charcode] != 0 || !bToUnicode) + continue; + +@@ -147,70 +117,123 @@ void CPDF_TrueTypeFont::LoadGlyphMap() { + } + return; + } +- if (FT_UseTTCharmap(face, 3, 0)) { +- bool bFound = false; +- for (int charcode = 0; charcode < 256; charcode++) { +- for (size_t j = 0; j < FX_ArraySize(kPrefix); j++) { +- uint16_t unicode = kPrefix[j] * 256 + charcode; +- m_GlyphIndex[charcode] = FT_Get_Char_Index(face, unicode); +- if (m_GlyphIndex[charcode]) { +- bFound = true; +- break; +- } +- } +- } +- if (bFound) { +- if (baseEncoding != PDFFONT_ENCODING_BUILTIN) { ++ if (UseTTCharmapMSSymbol(face)) { ++ for (uint32_t charcode = 0; charcode < 256; charcode++) ++ m_GlyphIndex[charcode] = GetGlyphIndexForMSSymbol(face, charcode); ++ if (HasAnyGlyphIndex()) { ++ if (base_encoding != FontEncoding::kBuiltin) { + for (uint32_t charcode = 0; charcode < 256; charcode++) { + const char* name = +- GetAdobeCharName(baseEncoding, m_CharNames, charcode); ++ GetAdobeCharName(base_encoding, m_CharNames, charcode); + if (name) +- m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name)); ++ m_Encoding.SetUnicode(charcode, UnicodeFromAdobeName(name)); + } +- } else if (FT_UseTTCharmap(face, 1, 0)) { +- for (int charcode = 0; charcode < 256; charcode++) { +- m_Encoding.SetUnicode( +- charcode, +- FT_UnicodeFromCharCode(FT_ENCODING_APPLE_ROMAN, charcode)); ++ } else if (UseTTCharmapMacRoman(face)) { ++ for (uint32_t charcode = 0; charcode < 256; charcode++) { ++ m_Encoding.SetUnicode(charcode, ++ UnicodeFromAppleRomanCharCode(charcode)); + } + } + return; + } + } +- if (FT_UseTTCharmap(face, 1, 0)) { +- bool bFound = false; +- for (int charcode = 0; charcode < 256; charcode++) { ++ if (UseTTCharmapMacRoman(face)) { ++ for (uint32_t charcode = 0; charcode < 256; charcode++) { + m_GlyphIndex[charcode] = FT_Get_Char_Index(face, charcode); +- m_Encoding.SetUnicode( +- charcode, FT_UnicodeFromCharCode(FT_ENCODING_APPLE_ROMAN, charcode)); +- if (m_GlyphIndex[charcode]) { +- bFound = true; +- } ++ m_Encoding.SetUnicode(charcode, UnicodeFromAppleRomanCharCode(charcode)); + } +- if (m_pFontFile || bFound) ++ if (m_pFontFile || HasAnyGlyphIndex()) + return; + } + if (FXFT_Select_Charmap(face, FT_ENCODING_UNICODE) == 0) { +- bool bFound = false; +- const uint16_t* pUnicodes = PDF_UnicodesForPredefinedCharSet(baseEncoding); ++ const uint16_t* pUnicodes = UnicodesForPredefinedCharSet(base_encoding); + for (uint32_t charcode = 0; charcode < 256; charcode++) { + if (m_pFontFile) { + m_Encoding.SetUnicode(charcode, charcode); + } else { +- const char* name = GetAdobeCharName(0, m_CharNames, charcode); ++ const char* name = ++ GetAdobeCharName(FontEncoding::kBuiltin, m_CharNames, charcode); + if (name) +- m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name)); ++ m_Encoding.SetUnicode(charcode, UnicodeFromAdobeName(name)); + else if (pUnicodes) + m_Encoding.SetUnicode(charcode, pUnicodes[charcode]); + } + m_GlyphIndex[charcode] = + FT_Get_Char_Index(face, m_Encoding.UnicodeFromCharCode(charcode)); +- if (m_GlyphIndex[charcode]) +- bFound = true; + } +- if (bFound) ++ if (HasAnyGlyphIndex()) + return; + } + for (int charcode = 0; charcode < 256; charcode++) + m_GlyphIndex[charcode] = charcode; + } ++ ++bool CPDF_TrueTypeFont::HasAnyGlyphIndex() const { ++ for (uint32_t charcode = 0; charcode < kInternalTableSize; charcode++) { ++ if (m_GlyphIndex[charcode]) ++ return true; ++ } ++ return false; ++} ++ ++CPDF_TrueTypeFont::CharmapType CPDF_TrueTypeFont::DetermineCharmapType() const { ++ if (UseTTCharmapMSUnicode(m_Font.GetFaceRec())) ++ return CharmapType::kMSUnicode; ++ ++ if (FontStyleIsNonSymbolic(m_Flags)) { ++ if (UseTTCharmapMacRoman(m_Font.GetFaceRec())) ++ return CharmapType::kMacRoman; ++ if (UseTTCharmapMSSymbol(m_Font.GetFaceRec())) ++ return CharmapType::kMSSymbol; ++ } else { ++ if (UseTTCharmapMSSymbol(m_Font.GetFaceRec())) ++ return CharmapType::kMSSymbol; ++ if (UseTTCharmapMacRoman(m_Font.GetFaceRec())) ++ return CharmapType::kMacRoman; ++ } ++ return CharmapType::kOther; ++} ++ ++FontEncoding CPDF_TrueTypeFont::DetermineEncoding() const { ++ if (!m_pFontFile || !FontStyleIsSymbolic(m_Flags) || ++ !IsWinAnsiOrMacRomanEncoding(m_BaseEncoding)) { ++ return m_BaseEncoding; ++ } ++ ++ // Not null - caller checked. ++ FXFT_FaceRec* face = m_Font.GetFaceRec(); ++ if (face->num_charmaps <= 0) ++ return m_BaseEncoding; ++ ++ bool support_win = false; ++ bool support_mac = false; ++ for (int i = 0; i < face->num_charmaps; i++) { ++ int platform_id = FXFT_Get_Charmap_PlatformID(face->charmaps[i]); ++ if (platform_id == kNamePlatformAppleUnicode || ++ platform_id == kNamePlatformWindows) { ++ support_win = true; ++ } else if (platform_id == kNamePlatformMac) { ++ support_mac = true; ++ } ++ if (support_win && support_mac) ++ break; ++ } ++ ++ if (m_BaseEncoding == FontEncoding::kWinAnsi && !support_win) ++ return support_mac ? FontEncoding::kMacRoman : FontEncoding::kBuiltin; ++ if (m_BaseEncoding == FontEncoding::kMacRoman && !support_mac) ++ return support_win ? FontEncoding::kWinAnsi : FontEncoding::kBuiltin; ++ return m_BaseEncoding; ++} ++ ++void CPDF_TrueTypeFont::SetGlyphIndicesFromFirstChar() { ++ int start_char = m_pFontDict->GetIntegerFor("FirstChar"); ++ if (start_char < 0 || start_char > 255) ++ return; ++ ++ auto* it = std::begin(m_GlyphIndex); ++ std::fill(it, it + start_char, 0); ++ uint16_t glyph = 3; ++ for (int charcode = start_char; charcode < 256; charcode++, glyph++) ++ m_GlyphIndex[charcode] = glyph; ++} +diff --git a/core/fpdfapi/font/cpdf_truetypefont.h b/core/fpdfapi/font/cpdf_truetypefont.h +index caa162519..cc8707385 100644 +--- a/core/fpdfapi/font/cpdf_truetypefont.h ++++ b/core/fpdfapi/font/cpdf_truetypefont.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,13 +8,11 @@ + #define CORE_FPDFAPI_FONT_CPDF_TRUETYPEFONT_H_ + + #include "core/fpdfapi/font/cpdf_simplefont.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/retain_ptr.h" + + class CPDF_TrueTypeFont final : public CPDF_SimpleFont { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- ++ CONSTRUCT_VIA_MAKE_RETAIN; + ~CPDF_TrueTypeFont() override; + + // CPDF_Font: +@@ -23,13 +21,21 @@ class CPDF_TrueTypeFont final : public CPDF_SimpleFont { + CPDF_TrueTypeFont* AsTrueTypeFont() override; + + private: +- CPDF_TrueTypeFont(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict); ++ enum class CharmapType { kMSUnicode, kMSSymbol, kMacRoman, kOther }; ++ ++ CPDF_TrueTypeFont(CPDF_Document* pDocument, ++ RetainPtr pFontDict); + + // CPDF_Font: + bool Load() override; + + // CPDF_SimpleFont: + void LoadGlyphMap() override; ++ ++ bool HasAnyGlyphIndex() const; ++ CharmapType DetermineCharmapType() const; ++ FontEncoding DetermineEncoding() const; ++ void SetGlyphIndicesFromFirstChar(); + }; + + #endif // CORE_FPDFAPI_FONT_CPDF_TRUETYPEFONT_H_ +diff --git a/core/fpdfapi/font/cpdf_type1font.cpp b/core/fpdfapi/font/cpdf_type1font.cpp +index 04dd7c2d6..55510e7d7 100644 +--- a/core/fpdfapi/font/cpdf_type1font.cpp ++++ b/core/fpdfapi/font/cpdf_type1font.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,66 +7,67 @@ + #include "core/fpdfapi/font/cpdf_type1font.h" + + #include ++#include ++#include + + #include "build/build_config.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/span_util.h" + #include "core/fxge/cfx_fontmapper.h" + #include "core/fxge/cfx_gemodule.h" ++#include "core/fxge/freetype/fx_freetype.h" + #include "core/fxge/fx_font.h" +-#include "core/fxge/fx_freetype.h" + +-#if defined(OS_MACOSX) +-#include "core/fxge/apple/apple_int.h" +-#endif ++#if BUILDFLAG(IS_APPLE) ++#include ++#include ++#endif // BUILDFLAG(IS_APPLE) + + namespace { + +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + struct GlyphNameMap { + const char* m_pStrAdobe; // Raw, POD struct. + const char* m_pStrUnicode; // Raw, POD struct. + }; + +-const GlyphNameMap g_GlyphNameSubsts[] = {{"ff", "uniFB00"}, +- {"ffi", "uniFB03"}, +- {"ffl", "uniFB04"}, +- {"fi", "uniFB01"}, +- {"fl", "uniFB02"}}; ++const GlyphNameMap kGlyphNameSubsts[] = {{"ff", "uniFB00"}, ++ {"ffi", "uniFB03"}, ++ {"ffl", "uniFB04"}, ++ {"fi", "uniFB01"}, ++ {"fl", "uniFB02"}}; + + const char* GlyphNameRemap(const char* pStrAdobe) { +- for (const auto& element : g_GlyphNameSubsts) { ++ for (const auto& element : kGlyphNameSubsts) { + if (!FXSYS_stricmp(element.m_pStrAdobe, pStrAdobe)) + return element.m_pStrUnicode; + } + return nullptr; + } + +-#endif // defined(OS_MACOSX) ++#endif // BUILDFLAG(IS_APPLE) + + bool FT_UseType1Charmap(FXFT_FaceRec* face) { +- if (FXFT_Get_Face_CharmapCount(face) == 0) { ++ if (face->num_charmaps == 0) + return false; +- } +- if (FXFT_Get_Face_CharmapCount(face) == 1 && +- FXFT_Get_Charmap_Encoding(FXFT_Get_Face_Charmaps(face)[0]) == +- FT_ENCODING_UNICODE) { ++ ++ bool is_first_charmap_unicode = ++ FXFT_Get_Charmap_Encoding(face->charmaps[0]) == FT_ENCODING_UNICODE; ++ if (face->num_charmaps == 1 && is_first_charmap_unicode) + return false; +- } +- if (FXFT_Get_Charmap_Encoding(FXFT_Get_Face_Charmaps(face)[0]) == +- FT_ENCODING_UNICODE) { +- FT_Set_Charmap(face, FXFT_Get_Face_Charmaps(face)[1]); +- } else { +- FT_Set_Charmap(face, FXFT_Get_Face_Charmaps(face)[0]); +- } ++ ++ int index = is_first_charmap_unicode ? 1 : 0; ++ FT_Set_Charmap(face, face->charmaps[index]); + return true; + } + + } // namespace + + CPDF_Type1Font::CPDF_Type1Font(CPDF_Document* pDocument, +- CPDF_Dictionary* pFontDict) +- : CPDF_SimpleFont(pDocument, pFontDict) { +-#if defined(OS_MACOSX) ++ RetainPtr pFontDict) ++ : CPDF_SimpleFont(pDocument, std::move(pFontDict)) { ++#if BUILDFLAG(IS_APPLE) + memset(m_ExtGID, 0xff, sizeof(m_ExtGID)); + #endif + } +@@ -90,7 +91,8 @@ bool CPDF_Type1Font::Load() { + if (!IsBase14Font()) + return LoadCommon(); + +- const CPDF_Dictionary* pFontDesc = m_pFontDict->GetDictFor("FontDescriptor"); ++ RetainPtr pFontDesc = ++ m_pFontDict->GetDictFor("FontDescriptor"); + if (pFontDesc && pFontDesc->KeyExist("Flags")) { + m_Flags = pFontDesc->GetIntegerFor("Flags"); + } else if (IsSymbolicFont()) { +@@ -99,19 +101,18 @@ bool CPDF_Type1Font::Load() { + m_Flags = FXFONT_NONSYMBOLIC; + } + if (IsFixedFont()) { +- for (int i = 0; i < 256; i++) +- m_CharWidth[i] = 600; ++ std::fill(std::begin(m_CharWidth), std::end(m_CharWidth), 600); + } + if (m_Base14Font == CFX_FontMapper::kSymbol) +- m_BaseEncoding = PDFFONT_ENCODING_ADOBE_SYMBOL; ++ m_BaseEncoding = FontEncoding::kAdobeSymbol; + else if (m_Base14Font == CFX_FontMapper::kDingbats) +- m_BaseEncoding = PDFFONT_ENCODING_ZAPFDINGBATS; ++ m_BaseEncoding = FontEncoding::kZapfDingbats; + else if (FontStyleIsNonSymbolic(m_Flags)) +- m_BaseEncoding = PDFFONT_ENCODING_STANDARD; ++ m_BaseEncoding = FontEncoding::kStandard; + return LoadCommon(); + } + +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + int CPDF_Type1Font::GlyphFromCharCodeExt(uint32_t charcode) { + if (charcode > 0xff) + return -1; +@@ -125,31 +126,29 @@ void CPDF_Type1Font::LoadGlyphMap() { + if (!m_Font.GetFaceRec()) + return; + +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + bool bCoreText = true; +- CQuartz2D& quartz2d = +- static_cast(CFX_GEModule::Get()->GetPlatform()) +- ->m_quartz2d; + if (!m_Font.GetPlatformFont()) { + if (m_Font.GetPsName() == "DFHeiStd-W5") + bCoreText = false; + ++ auto* pPlatform = CFX_GEModule::Get()->GetPlatform(); + pdfium::span span = m_Font.GetFontSpan(); +- m_Font.SetPlatformFont(quartz2d.CreateFont(span.data(), span.size())); ++ m_Font.SetPlatformFont(pPlatform->CreatePlatformFont(span)); + if (!m_Font.GetPlatformFont()) + bCoreText = false; + } + #endif + if (!IsEmbedded() && !IsSymbolicFont() && m_Font.IsTTFont()) { +- if (FT_UseTTCharmap(m_Font.GetFaceRec(), 3, 0)) { ++ if (UseTTCharmapMSSymbol(m_Font.GetFaceRec())) { + bool bGotOne = false; +- for (uint32_t charcode = 0; charcode < 256; charcode++) { ++ for (uint32_t charcode = 0; charcode < kInternalTableSize; charcode++) { + const uint8_t prefix[4] = {0x00, 0xf0, 0xf1, 0xf2}; + for (int j = 0; j < 4; j++) { + uint16_t unicode = prefix[j] * 256 + charcode; + m_GlyphIndex[charcode] = + FT_Get_Char_Index(m_Font.GetFaceRec(), unicode); +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + CalcExtGID(charcode); + #endif + if (m_GlyphIndex[charcode]) { +@@ -159,71 +158,65 @@ void CPDF_Type1Font::LoadGlyphMap() { + } + } + if (bGotOne) { +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + if (!bCoreText) +- memcpy(m_ExtGID, m_GlyphIndex, 256); ++ memcpy(m_ExtGID, m_GlyphIndex, sizeof(m_ExtGID)); + #endif + return; + } + } + FXFT_Select_Charmap(m_Font.GetFaceRec(), FT_ENCODING_UNICODE); +- if (m_BaseEncoding == 0) +- m_BaseEncoding = PDFFONT_ENCODING_STANDARD; ++ if (m_BaseEncoding == FontEncoding::kBuiltin) ++ m_BaseEncoding = FontEncoding::kStandard; + +- for (uint32_t charcode = 0; charcode < 256; charcode++) { ++ for (uint32_t charcode = 0; charcode < kInternalTableSize; charcode++) { + const char* name = + GetAdobeCharName(m_BaseEncoding, m_CharNames, charcode); + if (!name) + continue; + +- m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name)); ++ m_Encoding.SetUnicode(charcode, UnicodeFromAdobeName(name)); + m_GlyphIndex[charcode] = FT_Get_Char_Index( + m_Font.GetFaceRec(), m_Encoding.UnicodeFromCharCode(charcode)); +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + CalcExtGID(charcode); + #endif + if (m_GlyphIndex[charcode] == 0 && strcmp(name, ".notdef") == 0) { + m_Encoding.SetUnicode(charcode, 0x20); + m_GlyphIndex[charcode] = FT_Get_Char_Index(m_Font.GetFaceRec(), 0x20); +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + CalcExtGID(charcode); + #endif + } + } +-#if defined(OS_MACOSX) +- if (!bCoreText) +- memcpy(m_ExtGID, m_GlyphIndex, 256); ++#if BUILDFLAG(IS_APPLE) ++ if (!bCoreText) { ++ fxcrt::spancpy(pdfium::make_span(m_ExtGID), ++ pdfium::make_span(m_GlyphIndex)); ++ } + #endif + return; + } + FT_UseType1Charmap(m_Font.GetFaceRec()); +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + if (bCoreText) { + if (FontStyleIsSymbolic(m_Flags)) { +- for (uint32_t charcode = 0; charcode < 256; charcode++) { ++ for (uint32_t charcode = 0; charcode < kInternalTableSize; charcode++) { + const char* name = + GetAdobeCharName(m_BaseEncoding, m_CharNames, charcode); + if (name) { +- m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name)); +- m_GlyphIndex[charcode] = +- FXFT_Get_Name_Index(m_Font.GetFaceRec(), name); ++ m_Encoding.SetUnicode(charcode, UnicodeFromAdobeName(name)); ++ m_GlyphIndex[charcode] = FT_Get_Name_Index(m_Font.GetFaceRec(), name); + SetExtGID(name, charcode); + } else { + m_GlyphIndex[charcode] = + FT_Get_Char_Index(m_Font.GetFaceRec(), charcode); +- wchar_t unicode = 0; +- if (m_GlyphIndex[charcode]) { +- unicode = +- FT_UnicodeFromCharCode(PDFFONT_ENCODING_STANDARD, charcode); +- } +- char name_glyph[256]; +- memset(name_glyph, 0, sizeof(name_glyph)); ++ char name_glyph[kInternalTableSize] = {}; + FT_Get_Glyph_Name(m_Font.GetFaceRec(), m_GlyphIndex[charcode], +- name_glyph, 256); +- name_glyph[255] = 0; +- if (unicode == 0 && name_glyph[0] != 0) +- unicode = PDF_UnicodeFromAdobeName(name_glyph); +- ++ name_glyph, sizeof(name_glyph)); ++ name_glyph[kInternalTableSize - 1] = 0; ++ const wchar_t unicode = ++ name_glyph[0] != 0 ? UnicodeFromAdobeName(name_glyph) : 0; + m_Encoding.SetUnicode(charcode, unicode); + SetExtGID(name_glyph, charcode); + } +@@ -233,18 +226,18 @@ void CPDF_Type1Font::LoadGlyphMap() { + + bool bUnicode = + FXFT_Select_Charmap(m_Font.GetFaceRec(), FT_ENCODING_UNICODE) == 0; +- for (uint32_t charcode = 0; charcode < 256; charcode++) { ++ for (uint32_t charcode = 0; charcode < kInternalTableSize; charcode++) { + const char* name = + GetAdobeCharName(m_BaseEncoding, m_CharNames, charcode); + if (!name) + continue; + +- m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name)); ++ m_Encoding.SetUnicode(charcode, UnicodeFromAdobeName(name)); + const char* pStrUnicode = GlyphNameRemap(name); +- if (pStrUnicode && FXFT_Get_Name_Index(m_Font.GetFaceRec(), name) == 0) { ++ if (pStrUnicode && FT_Get_Name_Index(m_Font.GetFaceRec(), name) == 0) { + name = pStrUnicode; + } +- m_GlyphIndex[charcode] = FXFT_Get_Name_Index(m_Font.GetFaceRec(), name); ++ m_GlyphIndex[charcode] = FT_Get_Name_Index(m_Font.GetFaceRec(), name); + SetExtGID(name, charcode); + if (m_GlyphIndex[charcode] != 0) + continue; +@@ -263,65 +256,61 @@ void CPDF_Type1Font::LoadGlyphMap() { + } + return; + } +-#endif // defined(OS_MACOSX) ++#endif // BUILDFLAG(IS_APPLE) + if (FontStyleIsSymbolic(m_Flags)) { +- for (int charcode = 0; charcode < 256; charcode++) { +- const char* name = +- GetAdobeCharName(m_BaseEncoding, m_CharNames, charcode); ++ for (size_t charcode = 0; charcode < kInternalTableSize; charcode++) { ++ const char* name = GetAdobeCharName(m_BaseEncoding, m_CharNames, ++ static_cast(charcode)); + if (name) { +- m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name)); +- m_GlyphIndex[charcode] = FXFT_Get_Name_Index(m_Font.GetFaceRec(), name); ++ m_Encoding.SetUnicode(charcode, UnicodeFromAdobeName(name)); ++ m_GlyphIndex[charcode] = FT_Get_Name_Index(m_Font.GetFaceRec(), name); + } else { +- m_GlyphIndex[charcode] = +- FT_Get_Char_Index(m_Font.GetFaceRec(), charcode); ++ m_GlyphIndex[charcode] = FT_Get_Char_Index( ++ m_Font.GetFaceRec(), static_cast(charcode)); + if (m_GlyphIndex[charcode]) { +- wchar_t unicode = +- FT_UnicodeFromCharCode(PDFFONT_ENCODING_STANDARD, charcode); +- if (unicode == 0) { +- char name_glyph[256]; +- memset(name_glyph, 0, sizeof(name_glyph)); +- FT_Get_Glyph_Name(m_Font.GetFaceRec(), m_GlyphIndex[charcode], +- name_glyph, 256); +- name_glyph[255] = 0; +- if (name_glyph[0] != 0) +- unicode = PDF_UnicodeFromAdobeName(name_glyph); +- } ++ char name_glyph[kInternalTableSize] = {}; ++ FT_Get_Glyph_Name(m_Font.GetFaceRec(), m_GlyphIndex[charcode], ++ name_glyph, sizeof(name_glyph)); ++ name_glyph[kInternalTableSize - 1] = 0; ++ const wchar_t unicode = ++ name_glyph[0] != 0 ? UnicodeFromAdobeName(name_glyph) : 0; + m_Encoding.SetUnicode(charcode, unicode); + } + } + } +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + if (!bCoreText) +- memcpy(m_ExtGID, m_GlyphIndex, 256); +- ++ memcpy(m_ExtGID, m_GlyphIndex, sizeof(m_ExtGID)); + #endif + return; + } + + bool bUnicode = + FXFT_Select_Charmap(m_Font.GetFaceRec(), FT_ENCODING_UNICODE) == 0; +- for (int charcode = 0; charcode < 256; charcode++) { +- const char* name = GetAdobeCharName(m_BaseEncoding, m_CharNames, charcode); ++ for (size_t charcode = 0; charcode < kInternalTableSize; charcode++) { ++ const char* name = GetAdobeCharName(m_BaseEncoding, m_CharNames, ++ static_cast(charcode)); + if (!name) + continue; + +- m_Encoding.SetUnicode(charcode, PDF_UnicodeFromAdobeName(name)); +- m_GlyphIndex[charcode] = FXFT_Get_Name_Index(m_Font.GetFaceRec(), name); ++ m_Encoding.SetUnicode(charcode, UnicodeFromAdobeName(name)); ++ m_GlyphIndex[charcode] = FT_Get_Name_Index(m_Font.GetFaceRec(), name); + if (m_GlyphIndex[charcode] != 0) + continue; + + if (strcmp(name, ".notdef") != 0 && strcmp(name, "space") != 0) { +- m_GlyphIndex[charcode] = FT_Get_Char_Index( +- m_Font.GetFaceRec(), +- bUnicode ? m_Encoding.UnicodeFromCharCode(charcode) : charcode); ++ m_GlyphIndex[charcode] = ++ FT_Get_Char_Index(m_Font.GetFaceRec(), ++ bUnicode ? m_Encoding.UnicodeFromCharCode(charcode) ++ : static_cast(charcode)); + } else { + m_Encoding.SetUnicode(charcode, 0x20); + m_GlyphIndex[charcode] = 0xffff; + } + } +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + if (!bCoreText) +- memcpy(m_ExtGID, m_GlyphIndex, 256); ++ memcpy(m_ExtGID, m_GlyphIndex, sizeof(m_ExtGID)); + #endif + } + +@@ -335,7 +324,7 @@ bool CPDF_Type1Font::IsFixedFont() const { + CFX_FontMapper::IsFixedFont(m_Base14Font.value()); + } + +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + void CPDF_Type1Font::SetExtGID(const char* name, uint32_t charcode) { + CFStringRef name_ct = CFStringCreateWithCStringNoCopy( + kCFAllocatorDefault, name, kCFStringEncodingASCII, kCFAllocatorNull); +@@ -346,10 +335,10 @@ void CPDF_Type1Font::SetExtGID(const char* name, uint32_t charcode) { + } + + void CPDF_Type1Font::CalcExtGID(uint32_t charcode) { +- char name_glyph[256]; ++ char name_glyph[kInternalTableSize] = {}; + FT_Get_Glyph_Name(m_Font.GetFaceRec(), m_GlyphIndex[charcode], name_glyph, +- 256); +- name_glyph[255] = 0; ++ sizeof(name_glyph)); ++ name_glyph[kInternalTableSize - 1] = 0; + SetExtGID(name_glyph, charcode); + } +-#endif // defined(OS_MACOSX) ++#endif // BUILDFLAG(IS_APPLE) +diff --git a/core/fpdfapi/font/cpdf_type1font.h b/core/fpdfapi/font/cpdf_type1font.h +index 79dfe31df..55f542f77 100644 +--- a/core/fpdfapi/font/cpdf_type1font.h ++++ b/core/fpdfapi/font/cpdf_type1font.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,30 +7,31 @@ + #ifndef CORE_FPDFAPI_FONT_CPDF_TYPE1FONT_H_ + #define CORE_FPDFAPI_FONT_CPDF_TYPE1FONT_H_ + ++#include ++ + #include "build/build_config.h" + #include "core/fpdfapi/font/cpdf_simplefont.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxge/cfx_fontmapper.h" + + class CPDF_Type1Font final : public CPDF_SimpleFont { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- ++ CONSTRUCT_VIA_MAKE_RETAIN; + ~CPDF_Type1Font() override; + + // CPDF_Font: + bool IsType1Font() const override; + const CPDF_Type1Font* AsType1Font() const override; + CPDF_Type1Font* AsType1Font() override; +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + int GlyphFromCharCodeExt(uint32_t charcode) override; + #endif + + bool IsBase14Font() const { return m_Base14Font.has_value(); } + + private: +- CPDF_Type1Font(CPDF_Document* pDocument, CPDF_Dictionary* pFontDict); ++ CPDF_Type1Font(CPDF_Document* pDocument, ++ RetainPtr pFontDict); + + // CPDF_Font: + bool Load() override; +@@ -41,14 +42,14 @@ class CPDF_Type1Font final : public CPDF_SimpleFont { + bool IsSymbolicFont() const; + bool IsFixedFont() const; + +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + void SetExtGID(const char* name, uint32_t charcode); + void CalcExtGID(uint32_t charcode); + +- uint16_t m_ExtGID[256]; ++ uint16_t m_ExtGID[kInternalTableSize]; + #endif + +- Optional m_Base14Font; ++ absl::optional m_Base14Font; + }; + + #endif // CORE_FPDFAPI_FONT_CPDF_TYPE1FONT_H_ +diff --git a/core/fpdfapi/font/cpdf_type3char.cpp b/core/fpdfapi/font/cpdf_type3char.cpp +index 3ad9bba79..ff9a579ab 100644 +--- a/core/fpdfapi/font/cpdf_type3char.cpp ++++ b/core/fpdfapi/font/cpdf_type3char.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,9 +8,9 @@ + + #include + ++#include "core/fxcrt/fx_system.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "core/fxge/fx_dib.h" +-#include "third_party/base/ptr_util.h" ++#include "core/fxge/dib/fx_dib.h" + + namespace { + +@@ -49,7 +49,7 @@ bool CPDF_Type3Char::LoadBitmapFromSoleImageOfForm() { + } + + void CPDF_Type3Char::InitializeFromStreamData(bool bColored, +- const float* pData) { ++ pdfium::span pData) { + m_bColored = bColored; + m_Width = FXSYS_roundf(TextUnitToGlyphUnit(pData[0])); + m_BBox.left = FXSYS_roundf(TextUnitToGlyphUnit(pData[2])); +@@ -85,7 +85,3 @@ void CPDF_Type3Char::SetForm(std::unique_ptr pForm) { + RetainPtr CPDF_Type3Char::GetBitmap() { + return m_pBitmap; + } +- +-const RetainPtr& CPDF_Type3Char::GetBitmap() const { +- return m_pBitmap; +-} +diff --git a/core/fpdfapi/font/cpdf_type3char.h b/core/fpdfapi/font/cpdf_type3char.h +index ca8345206..afa39ef26 100644 +--- a/core/fpdfapi/font/cpdf_type3char.h ++++ b/core/fpdfapi/font/cpdf_type3char.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,9 +12,9 @@ + + #include "core/fpdfapi/font/cpdf_font.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/span.h" + + class CFX_DIBitmap; + +@@ -27,15 +27,14 @@ class CPDF_Type3Char { + static void TextUnitRectToGlyphUnitRect(CFX_FloatRect* pRect); + + bool LoadBitmapFromSoleImageOfForm(); +- void InitializeFromStreamData(bool bColored, const float* pData); ++ void InitializeFromStreamData(bool bColored, pdfium::span pData); + void Transform(CPDF_Font::FormIface* pForm, const CFX_Matrix& matrix); + void WillBeDestroyed(); + + RetainPtr GetBitmap(); +- const RetainPtr& GetBitmap() const; + + bool colored() const { return m_bColored; } +- uint32_t width() const { return m_Width; } ++ int width() const { return m_Width; } + const CFX_Matrix& matrix() const { return m_ImageMatrix; } + const FX_RECT& bbox() const { return m_BBox; } + +@@ -46,7 +45,7 @@ class CPDF_Type3Char { + std::unique_ptr m_pForm; + RetainPtr m_pBitmap; + bool m_bColored = false; +- uint32_t m_Width = 0; ++ int m_Width = 0; + CFX_Matrix m_ImageMatrix; + FX_RECT m_BBox; + }; +diff --git a/core/fpdfapi/font/cpdf_type3font.cpp b/core/fpdfapi/font/cpdf_type3font.cpp +index eae08ea0c..866b9e1ec 100644 +--- a/core/fpdfapi/font/cpdf_type3font.cpp ++++ b/core/fpdfapi/font/cpdf_type3font.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,8 @@ + #include "core/fpdfapi/font/cpdf_type3font.h" + + #include ++#include ++#include + #include + + #include "core/fpdfapi/font/cpdf_type3char.h" +@@ -15,7 +17,7 @@ + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fxcrt/autorestorer.h" + #include "core/fxcrt/fx_system.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -24,11 +26,11 @@ constexpr int kMaxType3FormLevel = 4; + } // namespace + + CPDF_Type3Font::CPDF_Type3Font(CPDF_Document* pDocument, +- CPDF_Dictionary* pFontDict, ++ RetainPtr pFontDict, + FormFactoryIface* pFormFactory) +- : CPDF_SimpleFont(pDocument, pFontDict), m_pFormFactory(pFormFactory) { +- ASSERT(GetDocument()); +- memset(m_CharWidthL, 0, sizeof(m_CharWidthL)); ++ : CPDF_SimpleFont(pDocument, std::move(pFontDict)), ++ m_pFormFactory(pFormFactory) { ++ DCHECK(GetDocument()); + } + + CPDF_Type3Font::~CPDF_Type3Font() = default; +@@ -55,8 +57,8 @@ void CPDF_Type3Font::WillBeDestroyed() { + } + + bool CPDF_Type3Font::Load() { +- m_pFontResources.Reset(m_pFontDict->GetDictFor("Resources")); +- const CPDF_Array* pMatrix = m_pFontDict->GetArrayFor("FontMatrix"); ++ m_pFontResources = m_pFontDict->GetMutableDictFor("Resources"); ++ RetainPtr pMatrix = m_pFontDict->GetArrayFor("FontMatrix"); + float xscale = 1.0f; + float yscale = 1.0f; + if (pMatrix) { +@@ -65,30 +67,31 @@ bool CPDF_Type3Font::Load() { + yscale = m_FontMatrix.d; + } + +- const CPDF_Array* pBBox = m_pFontDict->GetArrayFor("FontBBox"); ++ RetainPtr pBBox = m_pFontDict->GetArrayFor("FontBBox"); + if (pBBox) { + CFX_FloatRect box( +- pBBox->GetNumberAt(0) * xscale, pBBox->GetNumberAt(1) * yscale, +- pBBox->GetNumberAt(2) * xscale, pBBox->GetNumberAt(3) * yscale); ++ pBBox->GetFloatAt(0) * xscale, pBBox->GetFloatAt(1) * yscale, ++ pBBox->GetFloatAt(2) * xscale, pBBox->GetFloatAt(3) * yscale); + CPDF_Type3Char::TextUnitRectToGlyphUnitRect(&box); + m_FontBBox = box.ToFxRect(); + } + +- static constexpr size_t kCharLimit = FX_ArraySize(m_CharWidthL); ++ static constexpr size_t kCharLimit = std::extent(); + int StartChar = m_pFontDict->GetIntegerFor("FirstChar"); + if (StartChar >= 0 && static_cast(StartChar) < kCharLimit) { +- const CPDF_Array* pWidthArray = m_pFontDict->GetArrayFor("Widths"); ++ RetainPtr pWidthArray = ++ m_pFontDict->GetArrayFor("Widths"); + if (pWidthArray) { + size_t count = std::min(pWidthArray->size(), kCharLimit); + count = std::min(count, kCharLimit - StartChar); + for (size_t i = 0; i < count; i++) { + m_CharWidthL[StartChar + i] = + FXSYS_roundf(CPDF_Type3Char::TextUnitToGlyphUnit( +- pWidthArray->GetNumberAt(i) * xscale)); ++ pWidthArray->GetFloatAt(i) * xscale)); + } + } + } +- m_pCharProcs.Reset(m_pFontDict->GetDictFor("CharProcs")); ++ m_pCharProcs = m_pFontDict->GetMutableDictFor("CharProcs"); + if (m_pFontDict->GetDirectObjectFor("Encoding")) + LoadPDFEncoding(false, false); + return true; +@@ -115,16 +118,16 @@ CPDF_Type3Char* CPDF_Type3Font::LoadChar(uint32_t charcode) { + if (!m_pCharProcs) + return nullptr; + +- CPDF_Stream* pStream = ToStream(m_pCharProcs->GetDirectObjectFor(name)); ++ RetainPtr pStream = ++ ToStream(m_pCharProcs->GetMutableDirectObjectFor(name)); + if (!pStream) + return nullptr; + + std::unique_ptr pForm = m_pFormFactory->CreateForm( +- m_pDocument.Get(), +- m_pFontResources ? m_pFontResources.Get() : m_pPageResources.Get(), ++ m_pDocument, m_pFontResources ? m_pFontResources : m_pPageResources, + pStream); + +- auto pNewChar = pdfium::MakeUnique(); ++ auto pNewChar = std::make_unique(); + + // This can trigger recursion into this method. The content of |m_CacheMap| + // can change as a result. Thus after it returns, check the cache again for +@@ -147,8 +150,8 @@ CPDF_Type3Char* CPDF_Type3Font::LoadChar(uint32_t charcode) { + return pCachedChar; + } + +-uint32_t CPDF_Type3Font::GetCharWidthF(uint32_t charcode) { +- if (charcode >= FX_ArraySize(m_CharWidthL)) ++int CPDF_Type3Font::GetCharWidthF(uint32_t charcode) { ++ if (charcode >= std::size(m_CharWidthL)) + charcode = 0; + + if (m_CharWidthL[charcode]) +diff --git a/core/fpdfapi/font/cpdf_type3font.h b/core/fpdfapi/font/cpdf_type3font.h +index 1ef469bb6..4f5c650e6 100644 +--- a/core/fpdfapi/font/cpdf_type3font.h ++++ b/core/fpdfapi/font/cpdf_type3font.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,24 +7,23 @@ + #ifndef CORE_FPDFAPI_FONT_CPDF_TYPE3FONT_H_ + #define CORE_FPDFAPI_FONT_CPDF_TYPE3FONT_H_ + ++#include ++ + #include + #include + + #include "core/fpdfapi/font/cpdf_simplefont.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + + class CPDF_Dictionary; + class CPDF_Document; +-class CPDF_Stream; + class CPDF_Type3Char; + + class CPDF_Type3Font final : public CPDF_SimpleFont { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- ++ CONSTRUCT_VIA_MAKE_RETAIN; + ~CPDF_Type3Font() override; + + // CPDF_Font: +@@ -32,7 +31,7 @@ class CPDF_Type3Font final : public CPDF_SimpleFont { + const CPDF_Type3Font* AsType3Font() const override; + CPDF_Type3Font* AsType3Font() override; + void WillBeDestroyed() override; +- uint32_t GetCharWidthF(uint32_t charcode) override; ++ int GetCharWidthF(uint32_t charcode) override; + FX_RECT GetCharBBox(uint32_t charcode) override; + + void SetPageResources(CPDF_Dictionary* pResources) { +@@ -45,7 +44,7 @@ class CPDF_Type3Font final : public CPDF_SimpleFont { + + private: + CPDF_Type3Font(CPDF_Document* pDocument, +- CPDF_Dictionary* pFontDict, ++ RetainPtr pFontDict, + FormFactoryIface* pFormFactory); + + // CPDF_Font: +@@ -62,7 +61,7 @@ class CPDF_Type3Font final : public CPDF_SimpleFont { + RetainPtr m_pPageResources; + RetainPtr m_pFontResources; + std::map> m_CacheMap; +- uint32_t m_CharWidthL[256]; ++ int m_CharWidthL[256] = {}; + }; + + #endif // CORE_FPDFAPI_FONT_CPDF_TYPE3FONT_H_ +diff --git a/core/fpdfapi/page/Android.bp b/core/fpdfapi/page/Android.bp +index e44cca217..57f3683b5 100644 +--- a/core/fpdfapi/page/Android.bp ++++ b/core/fpdfapi/page/Android.bp +@@ -13,11 +13,12 @@ cc_library_static { + + visibility: ["//external/pdfium:__subpackages__"], + +- header_libs: [ +- "libpdfium-constants", ++ exclude_srcs: [ ++ "test_with_page_module.cpp", + ], + + static_libs: [ ++ "libpdfium-constants", + "libpdfium-fxcodec", + "libpdfium-fxcrt", + "libpdfium-fxge", +diff --git a/core/fpdfapi/page/BUILD.gn b/core/fpdfapi/page/BUILD.gn +index 2ab0b72e5..51b4adf14 100644 +--- a/core/fpdfapi/page/BUILD.gn ++++ b/core/fpdfapi/page/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -11,6 +11,8 @@ source_set("page") { + "cpdf_allstates.h", + "cpdf_annotcontext.cpp", + "cpdf_annotcontext.h", ++ "cpdf_basedcs.cpp", ++ "cpdf_basedcs.h", + "cpdf_clippath.cpp", + "cpdf_clippath.h", + "cpdf_color.cpp", +@@ -47,14 +49,20 @@ source_set("page") { + "cpdf_iccprofile.h", + "cpdf_image.cpp", + "cpdf_image.h", ++ "cpdf_imageloader.cpp", ++ "cpdf_imageloader.h", + "cpdf_imageobject.cpp", + "cpdf_imageobject.h", ++ "cpdf_indexedcs.cpp", ++ "cpdf_indexedcs.h", + "cpdf_meshstream.cpp", + "cpdf_meshstream.h", + "cpdf_occontext.cpp", + "cpdf_occontext.h", + "cpdf_page.cpp", + "cpdf_page.h", ++ "cpdf_pageimagecache.cpp", ++ "cpdf_pageimagecache.h", + "cpdf_pagemodule.cpp", + "cpdf_pagemodule.h", + "cpdf_pageobject.cpp", +@@ -99,7 +107,7 @@ source_set("page") { + "cpdf_transparency.h", + "ipdf_page.h", + ] +- configs += [ "../../../:pdfium_core_config" ] ++ configs += [ "../../../:pdfium_strict_config" ] + deps = [ + "../../../constants", + "../../fxcodec", +@@ -109,20 +117,40 @@ source_set("page") { + "../parser", + ] + allow_circular_includes_from = [] +- if (pdf_use_skia || pdf_use_skia_paths) { ++ if (pdf_use_skia) { + allow_circular_includes_from += [ "../../fxge" ] + } + visibility = [ "../../../*" ] + } + ++source_set("unit_test_support") { ++ testonly = true ++ sources = [ ++ "test_with_page_module.cpp", ++ "test_with_page_module.h", ++ ] ++ configs += [ "../../../:pdfium_strict_config" ] ++ deps = [ ++ "../page", ++ "//testing/gtest", ++ ] ++} ++ + pdfium_unittest_source_set("unittests") { + sources = [ ++ "cpdf_colorspace_unittest.cpp", + "cpdf_devicecs_unittest.cpp", ++ "cpdf_function_unittest.cpp", ++ "cpdf_pageimagecache_unittest.cpp", + "cpdf_pageobjectholder_unittest.cpp", + "cpdf_psengine_unittest.cpp", + "cpdf_streamcontentparser_unittest.cpp", + "cpdf_streamparser_unittest.cpp", + ] +- deps = [ ":page" ] ++ deps = [ ++ ":page", ++ "../parser", ++ "../render", ++ ] + pdfium_root_dir = "../../../" + } +diff --git a/core/fpdfapi/page/cpdf_allstates.cpp b/core/fpdfapi/page/cpdf_allstates.cpp +index a12ce33dd..a4330db1e 100644 +--- a/core/fpdfapi/page/cpdf_allstates.cpp ++++ b/core/fpdfapi/page/cpdf_allstates.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,9 +16,9 @@ + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "third_party/base/compiler_specific.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/cxx17_backports.h" + + CPDF_AllStates::CPDF_AllStates() = default; + +@@ -26,6 +26,7 @@ CPDF_AllStates::~CPDF_AllStates() = default; + + void CPDF_AllStates::Copy(const CPDF_AllStates& src) { + CopyStates(src); ++ m_GraphicsResourceName = src.m_GraphicsResourceName; + m_TextMatrix = src.m_TextMatrix; + m_ParentMatrix = src.m_ParentMatrix; + m_CTM = src.m_CTM; +@@ -43,17 +44,15 @@ void CPDF_AllStates::SetLineDash(const CPDF_Array* pArray, + m_GraphState.SetLineDash(std::move(dashes), phase, scale); + } + +-void CPDF_AllStates::ProcessExtGS(CPDF_Dictionary* pGS, ++void CPDF_AllStates::ProcessExtGS(const CPDF_Dictionary* pGS, + CPDF_StreamContentParser* pParser) { + CPDF_DictionaryLocker locker(pGS); + for (const auto& it : locker) { +- const ByteString& key_str = it.first; +- CPDF_Object* pElement = it.second.Get(); +- CPDF_Object* pObject = pElement ? pElement->GetDirect() : nullptr; ++ RetainPtr pObject = it.second->GetMutableDirect(); + if (!pObject) + continue; + +- uint32_t key = key_str.GetID(); ++ uint32_t key = it.first.GetID(); + switch (key) { + case FXBSTR_ID('L', 'W', 0, 0): + m_GraphState.SetLineWidth(pObject->GetNumber()); +@@ -70,53 +69,52 @@ void CPDF_AllStates::ProcessExtGS(CPDF_Dictionary* pGS, + m_GraphState.SetMiterLimit(pObject->GetNumber()); + break; + case FXBSTR_ID('D', 0, 0, 0): { +- CPDF_Array* pDash = pObject->AsArray(); ++ const CPDF_Array* pDash = pObject->AsArray(); + if (!pDash) + break; + +- CPDF_Array* pArray = pDash->GetArrayAt(0); ++ RetainPtr pArray = pDash->GetArrayAt(0); + if (!pArray) + break; + +- SetLineDash(pArray, pDash->GetNumberAt(1), 1.0f); ++ SetLineDash(pArray.Get(), pDash->GetFloatAt(1), 1.0f); + break; + } + case FXBSTR_ID('R', 'I', 0, 0): + m_GeneralState.SetRenderIntent(pObject->GetString()); + break; + case FXBSTR_ID('F', 'o', 'n', 't'): { +- CPDF_Array* pFont = pObject->AsArray(); ++ const CPDF_Array* pFont = pObject->AsArray(); + if (!pFont) + break; + +- m_TextState.SetFontSize(pFont->GetNumberAt(1)); +- m_TextState.SetFont(pParser->FindFont(pFont->GetStringAt(0))); ++ m_TextState.SetFontSize(pFont->GetFloatAt(1)); ++ m_TextState.SetFont(pParser->FindFont(pFont->GetByteStringAt(0))); + break; + } + case FXBSTR_ID('T', 'R', 0, 0): + if (pGS->KeyExist("TR2")) { + continue; + } +- FALLTHROUGH; ++ [[fallthrough]]; + case FXBSTR_ID('T', 'R', '2', 0): +- m_GeneralState.SetTR(pObject && !pObject->IsName() ? pObject : nullptr); ++ m_GeneralState.SetTR(!pObject->IsName() ? std::move(pObject) : nullptr); + break; + case FXBSTR_ID('B', 'M', 0, 0): { +- CPDF_Array* pArray = pObject->AsArray(); +- m_GeneralState.SetBlendMode(pArray ? pArray->GetStringAt(0) ++ const CPDF_Array* pArray = pObject->AsArray(); ++ m_GeneralState.SetBlendMode(pArray ? pArray->GetByteStringAt(0) + : pObject->GetString()); + if (m_GeneralState.GetBlendType() > BlendMode::kMultiply) + pParser->GetPageObjectHolder()->SetBackgroundAlphaNeeded(true); + break; + } +- case FXBSTR_ID('S', 'M', 'a', 's'): +- if (ToDictionary(pObject)) { +- m_GeneralState.SetSoftMask(pObject); ++ case FXBSTR_ID('S', 'M', 'a', 's'): { ++ RetainPtr pMaskDict = ToDictionary(pObject); ++ m_GeneralState.SetSoftMask(pMaskDict); ++ if (pMaskDict) + m_GeneralState.SetSMaskMatrix(pParser->GetCurStates()->m_CTM); +- } else { +- m_GeneralState.SetSoftMask(nullptr); +- } + break; ++ } + case FXBSTR_ID('C', 'A', 0, 0): + m_GeneralState.SetStrokeAlpha( + pdfium::clamp(pObject->GetNumber(), 0.0f, 1.0f)); +@@ -140,20 +138,20 @@ void CPDF_AllStates::ProcessExtGS(CPDF_Dictionary* pGS, + if (pGS->KeyExist("BG2")) { + continue; + } +- FALLTHROUGH; ++ [[fallthrough]]; + case FXBSTR_ID('B', 'G', '2', 0): +- m_GeneralState.SetBG(pObject); ++ m_GeneralState.SetBG(std::move(pObject)); + break; + case FXBSTR_ID('U', 'C', 'R', 0): + if (pGS->KeyExist("UCR2")) { + continue; + } +- FALLTHROUGH; ++ [[fallthrough]]; + case FXBSTR_ID('U', 'C', 'R', '2'): +- m_GeneralState.SetUCR(pObject); ++ m_GeneralState.SetUCR(std::move(pObject)); + break; + case FXBSTR_ID('H', 'T', 0, 0): +- m_GeneralState.SetHT(pObject); ++ m_GeneralState.SetHT(std::move(pObject)); + break; + case FXBSTR_ID('F', 'L', 0, 0): + m_GeneralState.SetFlatness(pObject->GetNumber()); +diff --git a/core/fpdfapi/page/cpdf_allstates.h b/core/fpdfapi/page/cpdf_allstates.h +index dc6f17fd8..63eb52722 100644 +--- a/core/fpdfapi/page/cpdf_allstates.h ++++ b/core/fpdfapi/page/cpdf_allstates.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,8 +8,8 @@ + #define CORE_FPDFAPI_PAGE_CPDF_ALLSTATES_H_ + + #include "core/fpdfapi/page/cpdf_graphicstates.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" + + class CPDF_Array; + class CPDF_Dictionary; +@@ -21,9 +21,11 @@ class CPDF_AllStates final : public CPDF_GraphicStates { + ~CPDF_AllStates() override; + + void Copy(const CPDF_AllStates& src); +- void ProcessExtGS(CPDF_Dictionary* pGS, CPDF_StreamContentParser* pParser); ++ void ProcessExtGS(const CPDF_Dictionary* pGS, ++ CPDF_StreamContentParser* pParser); + void SetLineDash(const CPDF_Array* pArray, float phase, float scale); + ++ ByteString m_GraphicsResourceName; + CFX_Matrix m_TextMatrix; + CFX_Matrix m_CTM; + CFX_Matrix m_ParentMatrix; +diff --git a/core/fpdfapi/page/cpdf_annotcontext.cpp b/core/fpdfapi/page/cpdf_annotcontext.cpp +index 1078e4616..7148a09d1 100644 +--- a/core/fpdfapi/page/cpdf_annotcontext.cpp ++++ b/core/fpdfapi/page/cpdf_annotcontext.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,30 +6,34 @@ + + #include "core/fpdfapi/page/cpdf_annotcontext.h" + ++#include ++ + #include "core/fpdfapi/page/cpdf_form.h" + #include "core/fpdfapi/page/cpdf_page.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_stream.h" +-#include "third_party/base/ptr_util.h" +- +-CPDF_AnnotContext::CPDF_AnnotContext(CPDF_Dictionary* pAnnotDict, +- CPDF_Page* pPage) +- : m_pAnnotDict(pAnnotDict), m_pPage(pPage) { +- ASSERT(m_pAnnotDict); +- ASSERT(m_pPage); ++#include "third_party/base/check.h" ++ ++CPDF_AnnotContext::CPDF_AnnotContext(RetainPtr pAnnotDict, ++ IPDF_Page* pPage) ++ : m_pAnnotDict(std::move(pAnnotDict)), m_pPage(pPage) { ++ DCHECK(m_pAnnotDict); ++ DCHECK(m_pPage); ++ DCHECK(m_pPage->AsPDFPage()); + } + + CPDF_AnnotContext::~CPDF_AnnotContext() = default; + +-void CPDF_AnnotContext::SetForm(CPDF_Stream* pStream) { ++void CPDF_AnnotContext::SetForm(RetainPtr pStream) { + if (!pStream) + return; + + // Reset the annotation matrix to be the identity matrix, since the + // appearance stream already takes matrix into account. +- pStream->GetDict()->SetMatrixFor("Matrix", CFX_Matrix()); ++ pStream->GetMutableDict()->SetMatrixFor("Matrix", CFX_Matrix()); + +- m_pAnnotForm = pdfium::MakeUnique( +- m_pPage->GetDocument(), m_pPage->m_pResources.Get(), pStream); ++ m_pAnnotForm = std::make_unique( ++ m_pPage->GetDocument(), m_pPage->AsPDFPage()->GetMutableResources(), ++ pStream); + m_pAnnotForm->ParseContent(); + } +diff --git a/core/fpdfapi/page/cpdf_annotcontext.h b/core/fpdfapi/page/cpdf_annotcontext.h +index ee9f3fc10..dee2ad36f 100644 +--- a/core/fpdfapi/page/cpdf_annotcontext.h ++++ b/core/fpdfapi/page/cpdf_annotcontext.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,28 +14,29 @@ + + class CPDF_Dictionary; + class CPDF_Form; +-class CPDF_Page; + class CPDF_Stream; ++class IPDF_Page; + + class CPDF_AnnotContext { + public: +- CPDF_AnnotContext(CPDF_Dictionary* pAnnotDict, CPDF_Page* pPage); ++ CPDF_AnnotContext(RetainPtr pAnnotDict, IPDF_Page* pPage); + ~CPDF_AnnotContext(); + +- void SetForm(CPDF_Stream* pStream); ++ void SetForm(RetainPtr pStream); + bool HasForm() const { return !!m_pAnnotForm; } + CPDF_Form* GetForm() const { return m_pAnnotForm.get(); } + + // Never nullptr. +- CPDF_Dictionary* GetAnnotDict() const { return m_pAnnotDict.Get(); } ++ RetainPtr GetMutableAnnotDict() { return m_pAnnotDict; } ++ const CPDF_Dictionary* GetAnnotDict() const { return m_pAnnotDict.Get(); } + + // Never nullptr. +- CPDF_Page* GetPage() const { return m_pPage.Get(); } ++ IPDF_Page* GetPage() const { return m_pPage; } + + private: + std::unique_ptr m_pAnnotForm; + RetainPtr const m_pAnnotDict; +- UnownedPtr const m_pPage; ++ UnownedPtr const m_pPage; + }; + + #endif // CORE_FPDFAPI_PAGE_CPDF_ANNOTCONTEXT_H_ +diff --git a/core/fpdfapi/page/cpdf_basedcs.cpp b/core/fpdfapi/page/cpdf_basedcs.cpp +new file mode 100644 +index 000000000..c8988bbe5 +--- /dev/null ++++ b/core/fpdfapi/page/cpdf_basedcs.cpp +@@ -0,0 +1,17 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fpdfapi/page/cpdf_basedcs.h" ++ ++CPDF_BasedCS::CPDF_BasedCS(Family family) : CPDF_ColorSpace(family) {} ++ ++CPDF_BasedCS::~CPDF_BasedCS() = default; ++ ++void CPDF_BasedCS::EnableStdConversion(bool bEnabled) { ++ CPDF_ColorSpace::EnableStdConversion(bEnabled); ++ if (m_pBaseCS) ++ m_pBaseCS->EnableStdConversion(bEnabled); ++} +diff --git a/core/fpdfapi/page/cpdf_basedcs.h b/core/fpdfapi/page/cpdf_basedcs.h +new file mode 100644 +index 000000000..7c72f1e35 +--- /dev/null ++++ b/core/fpdfapi/page/cpdf_basedcs.h +@@ -0,0 +1,29 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FPDFAPI_PAGE_CPDF_BASEDCS_H_ ++#define CORE_FPDFAPI_PAGE_CPDF_BASEDCS_H_ ++ ++#include "core/fpdfapi/page/cpdf_colorspace.h" ++#include "core/fxcrt/retain_ptr.h" ++ ++// Represents a color space that is based on another color space. This includes ++// all the special color spaces in ISO 32000-1:2008, table 62, as well as the ++// ICCBased color space. ++class CPDF_BasedCS : public CPDF_ColorSpace { ++ public: ++ CONSTRUCT_VIA_MAKE_RETAIN; ++ ~CPDF_BasedCS() override; ++ ++ void EnableStdConversion(bool bEnabled) final; ++ ++ protected: ++ explicit CPDF_BasedCS(Family family); ++ ++ RetainPtr m_pBaseCS; // May be fallback CS in some cases. ++}; ++ ++#endif // CORE_FPDFAPI_PAGE_CPDF_BASEDCS_H_ +diff --git a/core/fpdfapi/page/cpdf_clippath.cpp b/core/fpdfapi/page/cpdf_clippath.cpp +index 53b602da0..032aff691 100644 +--- a/core/fpdfapi/page/cpdf_clippath.cpp ++++ b/core/fpdfapi/page/cpdf_clippath.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -26,7 +26,7 @@ CPDF_Path CPDF_ClipPath::GetPath(size_t i) const { + return m_Ref.GetObject()->m_PathAndTypeList[i].first; + } + +-uint8_t CPDF_ClipPath::GetClipType(size_t i) const { ++CFX_FillRenderOptions::FillType CPDF_ClipPath::GetClipType(size_t i) const { + return m_Ref.GetObject()->m_PathAndTypeList[i].second; + } + +@@ -74,9 +74,17 @@ CFX_FloatRect CPDF_ClipPath::GetClipBox() const { + return rect; + } + +-void CPDF_ClipPath::AppendPath(CPDF_Path path, uint8_t type, bool bAutoMerge) { ++void CPDF_ClipPath::AppendPath(CPDF_Path path, ++ CFX_FillRenderOptions::FillType type) { + PathData* pData = m_Ref.GetPrivateCopy(); +- if (!pData->m_PathAndTypeList.empty() && bAutoMerge) { ++ pData->m_PathAndTypeList.emplace_back(path, type); ++} ++ ++void CPDF_ClipPath::AppendPathWithAutoMerge( ++ CPDF_Path path, ++ CFX_FillRenderOptions::FillType type) { ++ PathData* pData = m_Ref.GetPrivateCopy(); ++ if (!pData->m_PathAndTypeList.empty()) { + const CPDF_Path& old_path = pData->m_PathAndTypeList.back().first; + if (old_path.IsRect()) { + CFX_PointF point0 = old_path.GetPoint(0); +@@ -87,7 +95,7 @@ void CPDF_ClipPath::AppendPath(CPDF_Path path, uint8_t type, bool bAutoMerge) { + pData->m_PathAndTypeList.pop_back(); + } + } +- pData->m_PathAndTypeList.push_back(std::make_pair(path, type)); ++ AppendPath(path, type); + } + + void CPDF_ClipPath::AppendTexts( +@@ -107,7 +115,7 @@ void CPDF_ClipPath::CopyClipPath(const CPDF_ClipPath& that) { + return; + + for (size_t i = 0; i < that.GetPathCount(); ++i) +- AppendPath(that.GetPath(i), that.GetClipType(i), /*bAutoMerge=*/false); ++ AppendPath(that.GetPath(i), that.GetClipType(i)); + } + + void CPDF_ClipPath::Transform(const CFX_Matrix& matrix) { +@@ -123,10 +131,9 @@ void CPDF_ClipPath::Transform(const CFX_Matrix& matrix) { + + CPDF_ClipPath::PathData::PathData() = default; + +-CPDF_ClipPath::PathData::PathData(const PathData& that) { +- m_PathAndTypeList = that.m_PathAndTypeList; +- +- m_TextList.resize(that.m_TextList.size()); ++CPDF_ClipPath::PathData::PathData(const PathData& that) ++ : m_PathAndTypeList(that.m_PathAndTypeList), ++ m_TextList(that.m_TextList.size()) { + for (size_t i = 0; i < that.m_TextList.size(); ++i) { + if (that.m_TextList[i]) + m_TextList[i] = that.m_TextList[i]->Clone(); +diff --git a/core/fpdfapi/page/cpdf_clippath.h b/core/fpdfapi/page/cpdf_clippath.h +index aff28399e..e93f9a861 100644 +--- a/core/fpdfapi/page/cpdf_clippath.h ++++ b/core/fpdfapi/page/cpdf_clippath.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,7 +13,9 @@ + + #include "core/fpdfapi/page/cpdf_path.h" + #include "core/fxcrt/fx_coordinates.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/shared_copy_on_write.h" ++#include "core/fxge/cfx_fillrenderoptions.h" + + class CPDF_TextObject; + +@@ -35,11 +37,13 @@ class CPDF_ClipPath { + + size_t GetPathCount() const; + CPDF_Path GetPath(size_t i) const; +- uint8_t GetClipType(size_t i) const; ++ CFX_FillRenderOptions::FillType GetClipType(size_t i) const; + size_t GetTextCount() const; + CPDF_TextObject* GetText(size_t i) const; + CFX_FloatRect GetClipBox() const; +- void AppendPath(CPDF_Path path, uint8_t type, bool bAutoMerge); ++ void AppendPath(CPDF_Path path, CFX_FillRenderOptions::FillType type); ++ void AppendPathWithAutoMerge(CPDF_Path path, ++ CFX_FillRenderOptions::FillType type); + void AppendTexts(std::vector>* pTexts); + void CopyClipPath(const CPDF_ClipPath& that); + void Transform(const CFX_Matrix& matrix); +@@ -47,12 +51,12 @@ class CPDF_ClipPath { + private: + class PathData final : public Retainable { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + RetainPtr Clone() const; + +- using PathAndTypeData = std::pair; ++ using PathAndTypeData = ++ std::pair; + + std::vector m_PathAndTypeList; + std::vector> m_TextList; +diff --git a/core/fpdfapi/page/cpdf_color.cpp b/core/fpdfapi/page/cpdf_color.cpp +index cfa64222e..57e483caf 100644 +--- a/core/fpdfapi/page/cpdf_color.cpp ++++ b/core/fpdfapi/page/cpdf_color.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,11 +6,10 @@ + + #include "core/fpdfapi/page/cpdf_color.h" + +-#include "core/fpdfapi/page/cpdf_docpagedata.h" ++#include ++ + #include "core/fpdfapi/page/cpdf_patterncs.h" +-#include "core/fpdfapi/parser/cpdf_array.h" +-#include "core/fpdfapi/parser/cpdf_document.h" +-#include "core/fxcrt/fx_system.h" ++#include "third_party/base/check.h" + + CPDF_Color::CPDF_Color() = default; + +@@ -25,35 +24,36 @@ bool CPDF_Color::IsPattern() const { + } + + bool CPDF_Color::IsPatternInternal() const { +- return m_pCS->GetFamily() == PDFCS_PATTERN; ++ return m_pCS->GetFamily() == CPDF_ColorSpace::Family::kPattern; + } + +-void CPDF_Color::SetColorSpace(const RetainPtr& pCS) { +- m_pCS = pCS; ++void CPDF_Color::SetColorSpace(RetainPtr colorspace) { ++ m_pCS = std::move(colorspace); + if (IsPatternInternal()) { + m_Buffer.clear(); +- m_pValue = pdfium::MakeUnique(); ++ m_pValue = std::make_unique(); + } else { +- m_Buffer = pCS->CreateBufAndSetDefaultColor(); ++ m_Buffer = m_pCS->CreateBufAndSetDefaultColor(); + m_pValue.reset(); + } + } + +-void CPDF_Color::SetValueForNonPattern(const std::vector& values) { +- ASSERT(!IsPatternInternal()); +- ASSERT(m_pCS->CountComponents() <= values.size()); +- m_Buffer = values; ++void CPDF_Color::SetValueForNonPattern(std::vector values) { ++ DCHECK(!IsPatternInternal()); ++ DCHECK(m_pCS->CountComponents() <= values.size()); ++ m_Buffer = std::move(values); + } + +-void CPDF_Color::SetValueForPattern(const RetainPtr& pPattern, +- const std::vector& values) { ++void CPDF_Color::SetValueForPattern(RetainPtr pattern, ++ pdfium::span values) { + if (values.size() > kMaxPatternColorComps) + return; + +- if (!IsPattern()) +- SetColorSpace(CPDF_ColorSpace::GetStockCS(PDFCS_PATTERN)); +- +- m_pValue->SetPattern(pPattern); ++ if (!IsPattern()) { ++ SetColorSpace( ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kPattern)); ++ } ++ m_pValue->SetPattern(std::move(pattern)); + m_pValue->SetComps(values); + } + +@@ -62,8 +62,8 @@ CPDF_Color& CPDF_Color::operator=(const CPDF_Color& that) { + return *this; + + m_Buffer = that.m_Buffer; +- m_pValue = that.m_pValue ? pdfium::MakeUnique(*that.m_pValue) +- : nullptr; ++ m_pValue = ++ that.m_pValue ? std::make_unique(*that.m_pValue) : nullptr; + m_pCS = that.m_pCS; + return *this; + } +@@ -73,7 +73,8 @@ uint32_t CPDF_Color::CountComponents() const { + } + + bool CPDF_Color::IsColorSpaceRGB() const { +- return m_pCS == CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB); ++ return m_pCS == ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB); + } + + bool CPDF_Color::GetRGB(int* R, int* G, int* B) const { +@@ -88,7 +89,7 @@ bool CPDF_Color::GetRGB(int* R, int* G, int* B) const { + } + } else { + if (!m_Buffer.empty()) +- result = m_pCS->GetRGB(m_Buffer.data(), &r, &g, &b); ++ result = m_pCS->GetRGB(m_Buffer, &r, &g, &b); + } + if (!result) + return false; +@@ -99,7 +100,7 @@ bool CPDF_Color::GetRGB(int* R, int* G, int* B) const { + return true; + } + +-CPDF_Pattern* CPDF_Color::GetPattern() const { +- ASSERT(IsPattern()); ++RetainPtr CPDF_Color::GetPattern() const { ++ DCHECK(IsPattern()); + return m_pValue ? m_pValue->GetPattern() : nullptr; + } +diff --git a/core/fpdfapi/page/cpdf_color.h b/core/fpdfapi/page/cpdf_color.h +index b533d084b..d3e605b82 100644 +--- a/core/fpdfapi/page/cpdf_color.h ++++ b/core/fpdfapi/page/cpdf_color.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,13 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_COLOR_H_ + #define CORE_FPDFAPI_PAGE_CPDF_COLOR_H_ + ++#include ++ + #include + #include + +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" ++#include "third_party/base/span.h" + + class CPDF_ColorSpace; + class CPDF_Pattern; +@@ -28,16 +30,17 @@ class CPDF_Color { + + bool IsNull() const { return m_Buffer.empty() && !m_pValue; } + bool IsPattern() const; +- void SetColorSpace(const RetainPtr& pCS); +- void SetValueForNonPattern(const std::vector& values); +- void SetValueForPattern(const RetainPtr& pPattern, +- const std::vector& values); ++ void SetColorSpace(RetainPtr colorspace); ++ void SetValueForNonPattern(std::vector values); ++ void SetValueForPattern(RetainPtr pattern, ++ pdfium::span values); ++ + uint32_t CountComponents() const; + bool IsColorSpaceRGB() const; + bool GetRGB(int* R, int* G, int* B) const; + + // Should only be called if IsPattern() returns true. +- CPDF_Pattern* GetPattern() const; ++ RetainPtr GetPattern() const; + + protected: + bool IsPatternInternal() const; +diff --git a/core/fpdfapi/page/cpdf_colorspace.cpp b/core/fpdfapi/page/cpdf_colorspace.cpp +index fd4a2adcc..9d94acd63 100644 +--- a/core/fpdfapi/page/cpdf_colorspace.cpp ++++ b/core/fpdfapi/page/cpdf_colorspace.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,9 +6,13 @@ + + #include "core/fpdfapi/page/cpdf_colorspace.h" + ++#include ++#include ++ + #include + #include + #include ++#include + #include + #include + +@@ -16,6 +20,7 @@ + #include "core/fpdfapi/page/cpdf_docpagedata.h" + #include "core/fpdfapi/page/cpdf_function.h" + #include "core/fpdfapi/page/cpdf_iccprofile.h" ++#include "core/fpdfapi/page/cpdf_indexedcs.h" + #include "core/fpdfapi/page/cpdf_pagemodule.h" + #include "core/fpdfapi/page/cpdf_pattern.h" + #include "core/fpdfapi/page/cpdf_patterncs.h" +@@ -25,19 +30,25 @@ + #include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_object.h" + #include "core/fpdfapi/parser/cpdf_stream.h" +-#include "core/fpdfapi/parser/cpdf_stream_acc.h" +-#include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fxcodec/fx_codec.h" +-#include "core/fxcodec/icc/iccmodule.h" +-#include "core/fxcrt/fx_memory.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_2d_size.h" ++#include "core/fxcrt/fx_memory_wrappers.h" + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxcrt/maybe_owned.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxcrt/scoped_set_insertion.h" ++#include "core/fxcrt/span_util.h" ++#include "core/fxcrt/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/cxx17_backports.h" ++#include "third_party/base/notreached.h" + + namespace { + +-const uint8_t g_sRGBSamples1[] = { ++constexpr uint8_t kSRGBSamples1[] = { + 0, 3, 6, 10, 13, 15, 18, 20, 22, 23, 25, 27, 28, 30, 31, + 32, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 49, 50, 51, 52, 53, 53, 54, 55, 56, 56, 57, 58, 58, +@@ -53,7 +64,7 @@ const uint8_t g_sRGBSamples1[] = { + 116, 117, 117, 117, 118, 118, 118, 118, 119, 119, 119, 120, + }; + +-const uint8_t g_sRGBSamples2[] = { ++constexpr uint8_t kSRGBSamples2[] = { + 120, 121, 122, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, + 150, 151, 152, 153, 154, 155, 155, 156, 157, 158, 159, 159, 160, 161, 162, +@@ -79,7 +90,7 @@ void GetDefaultBlackPoint(float* pPoints) { + } + + void GetBlackPoint(const CPDF_Dictionary* pDict, float* pPoints) { +- const CPDF_Array* pParam = pDict->GetArrayFor("BlackPoint"); ++ RetainPtr pParam = pDict->GetArrayFor("BlackPoint"); + if (!pParam || pParam->size() != kBlackWhitePointCount) { + GetDefaultBlackPoint(pPoints); + return; +@@ -87,7 +98,7 @@ void GetBlackPoint(const CPDF_Dictionary* pDict, float* pPoints) { + + // Check to make sure all values are non-negative. + for (size_t i = 0; i < kBlackWhitePointCount; ++i) { +- pPoints[i] = pParam->GetNumberAt(i); ++ pPoints[i] = pParam->GetFloatAt(i); + if (pPoints[i] < 0) { + GetDefaultBlackPoint(pPoints); + return; +@@ -96,29 +107,30 @@ void GetBlackPoint(const CPDF_Dictionary* pDict, float* pPoints) { + } + + bool GetWhitePoint(const CPDF_Dictionary* pDict, float* pPoints) { +- const CPDF_Array* pParam = pDict->GetArrayFor("WhitePoint"); ++ RetainPtr pParam = pDict->GetArrayFor("WhitePoint"); + if (!pParam || pParam->size() != kBlackWhitePointCount) + return false; + + for (size_t i = 0; i < kBlackWhitePointCount; ++i) +- pPoints[i] = pParam->GetNumberAt(i); ++ pPoints[i] = pParam->GetFloatAt(i); + return pPoints[0] > 0.0f && pPoints[1] == 1.0f && pPoints[2] > 0.0f; + } + + class CPDF_CalGray final : public CPDF_ColorSpace { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- ++ CONSTRUCT_VIA_MAKE_RETAIN; + ~CPDF_CalGray() override; + + // CPDF_ColorSpace: +- bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override; ++ bool GetRGB(pdfium::span pBuf, ++ float* R, ++ float* G, ++ float* B) const override; + uint32_t v_Load(CPDF_Document* pDoc, + const CPDF_Array* pArray, + std::set* pVisited) override; +- void TranslateImageLine(uint8_t* pDestBuf, +- const uint8_t* pSrcBuf, ++ void TranslateImageLine(pdfium::span dest_span, ++ pdfium::span src_span, + int pixels, + int image_width, + int image_height, +@@ -127,24 +139,25 @@ class CPDF_CalGray final : public CPDF_ColorSpace { + private: + static constexpr float kDefaultGamma = 1.0f; + +- explicit CPDF_CalGray(CPDF_Document* pDoc); ++ CPDF_CalGray(); + + float m_Gamma = kDefaultGamma; +- float m_WhitePoint[kBlackWhitePointCount]; +- float m_BlackPoint[kBlackWhitePointCount]; ++ float m_WhitePoint[kBlackWhitePointCount] = {1.0f, 1.0f, 1.0f}; ++ float m_BlackPoint[kBlackWhitePointCount] = {0.0f, 0.0f, 0.0f}; + }; + + class CPDF_CalRGB final : public CPDF_ColorSpace { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- ++ CONSTRUCT_VIA_MAKE_RETAIN; + ~CPDF_CalRGB() override; + + // CPDF_ColorSpace: +- bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override; +- void TranslateImageLine(uint8_t* pDestBuf, +- const uint8_t* pSrcBuf, ++ bool GetRGB(pdfium::span pBuf, ++ float* R, ++ float* G, ++ float* B) const override; ++ void TranslateImageLine(pdfium::span dest_span, ++ pdfium::span src_span, + int pixels, + int image_width, + int image_height, +@@ -157,31 +170,32 @@ class CPDF_CalRGB final : public CPDF_ColorSpace { + static constexpr size_t kGammaCount = 3; + static constexpr size_t kMatrixCount = 9; + +- explicit CPDF_CalRGB(CPDF_Document* pDoc); ++ CPDF_CalRGB(); + +- float m_WhitePoint[kBlackWhitePointCount]; +- float m_BlackPoint[kBlackWhitePointCount]; +- float m_Gamma[kGammaCount]; +- float m_Matrix[kMatrixCount]; +- bool m_bGamma = false; +- bool m_bMatrix = false; ++ float m_WhitePoint[kBlackWhitePointCount] = {1.0f, 1.0f, 1.0f}; ++ float m_BlackPoint[kBlackWhitePointCount] = {0.0f, 0.0f, 0.0f}; ++ float m_Gamma[kGammaCount] = {}; ++ float m_Matrix[kMatrixCount] = {}; ++ bool m_bHasGamma = false; ++ bool m_bHasMatrix = false; + }; + + class CPDF_LabCS final : public CPDF_ColorSpace { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- ++ CONSTRUCT_VIA_MAKE_RETAIN; + ~CPDF_LabCS() override; + + // CPDF_ColorSpace: +- bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override; ++ bool GetRGB(pdfium::span pBuf, ++ float* R, ++ float* G, ++ float* B) const override; + void GetDefaultValue(int iComponent, + float* value, + float* min, + float* max) const override; +- void TranslateImageLine(uint8_t* pDestBuf, +- const uint8_t* pSrcBuf, ++ void TranslateImageLine(pdfium::span dest_span, ++ pdfium::span src_span, + int pixels, + int image_width, + int image_height, +@@ -193,25 +207,25 @@ class CPDF_LabCS final : public CPDF_ColorSpace { + private: + static constexpr size_t kRangesCount = 4; + +- explicit CPDF_LabCS(CPDF_Document* pDoc); ++ CPDF_LabCS(); + +- float m_WhitePoint[kBlackWhitePointCount]; +- float m_BlackPoint[kBlackWhitePointCount]; +- float m_Ranges[kRangesCount]; ++ float m_WhitePoint[kBlackWhitePointCount] = {1.0f, 1.0f, 1.0f}; ++ float m_BlackPoint[kBlackWhitePointCount] = {0.0f, 0.0f, 0.0f}; ++ float m_Ranges[kRangesCount] = {}; + }; + +-class CPDF_ICCBasedCS final : public CPDF_ColorSpace { ++class CPDF_ICCBasedCS final : public CPDF_BasedCS { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- ++ CONSTRUCT_VIA_MAKE_RETAIN; + ~CPDF_ICCBasedCS() override; + + // CPDF_ColorSpace: +- bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override; +- void EnableStdConversion(bool bEnabled) override; +- void TranslateImageLine(uint8_t* pDestBuf, +- const uint8_t* pSrcBuf, ++ bool GetRGB(pdfium::span pBuf, ++ float* R, ++ float* G, ++ float* B) const override; ++ void TranslateImageLine(pdfium::span dest_span, ++ pdfium::span src_span, + int pixels, + int image_width, + int image_height, +@@ -222,7 +236,7 @@ class CPDF_ICCBasedCS final : public CPDF_ColorSpace { + std::set* pVisited) override; + + private: +- explicit CPDF_ICCBasedCS(CPDF_Document* pDoc); ++ CPDF_ICCBasedCS(); + + // If no valid ICC profile or using sRGB, try looking for an alternate. + bool FindAlternateProfile(CPDF_Document* pDoc, +@@ -234,85 +248,57 @@ class CPDF_ICCBasedCS final : public CPDF_ColorSpace { + static std::vector GetRanges(const CPDF_Dictionary* pDict, + uint32_t nComponents); + +- RetainPtr m_pAlterCS; + RetainPtr m_pProfile; +- mutable std::vector m_pCache; ++ mutable DataVector m_pCache; + std::vector m_pRanges; + }; + +-class CPDF_IndexedCS final : public CPDF_ColorSpace { +- public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- +- ~CPDF_IndexedCS() override; +- +- // CPDF_ColorSpace: +- bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override; +- void EnableStdConversion(bool bEnabled) override; +- uint32_t v_Load(CPDF_Document* pDoc, +- const CPDF_Array* pArray, +- std::set* pVisited) override; +- +- private: +- explicit CPDF_IndexedCS(CPDF_Document* pDoc); +- +- RetainPtr m_pBaseCS; +- uint32_t m_nBaseComponents = 0; +- int m_MaxIndex = 0; +- ByteString m_Table; +- std::vector m_pCompMinMax; +-}; +- +-class CPDF_SeparationCS final : public CPDF_ColorSpace { ++class CPDF_SeparationCS final : public CPDF_BasedCS { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- ++ CONSTRUCT_VIA_MAKE_RETAIN; + ~CPDF_SeparationCS() override; + + // CPDF_ColorSpace: +- bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override; ++ bool GetRGB(pdfium::span pBuf, ++ float* R, ++ float* G, ++ float* B) const override; + void GetDefaultValue(int iComponent, + float* value, + float* min, + float* max) const override; +- void EnableStdConversion(bool bEnabled) override; + uint32_t v_Load(CPDF_Document* pDoc, + const CPDF_Array* pArray, + std::set* pVisited) override; + + private: +- enum { None, All, Colorant } m_Type; ++ CPDF_SeparationCS(); + +- explicit CPDF_SeparationCS(CPDF_Document* pDoc); +- +- RetainPtr m_pAltCS; ++ bool m_IsNoneType = false; + std::unique_ptr m_pFunc; + }; + +-class CPDF_DeviceNCS final : public CPDF_ColorSpace { ++class CPDF_DeviceNCS final : public CPDF_BasedCS { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- ++ CONSTRUCT_VIA_MAKE_RETAIN; + ~CPDF_DeviceNCS() override; + + // CPDF_ColorSpace: +- bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override; ++ bool GetRGB(pdfium::span pBuf, ++ float* R, ++ float* G, ++ float* B) const override; + void GetDefaultValue(int iComponent, + float* value, + float* min, + float* max) const override; +- void EnableStdConversion(bool bEnabled) override; + uint32_t v_Load(CPDF_Document* pDoc, + const CPDF_Array* pArray, + std::set* pVisited) override; + + private: +- explicit CPDF_DeviceNCS(CPDF_Document* pDoc); ++ CPDF_DeviceNCS(); + +- RetainPtr m_pAltCS; + std::unique_ptr m_pFunc; + }; + +@@ -390,8 +376,8 @@ float RGB_Conversion(float colorComponent) { + colorComponent = pdfium::clamp(colorComponent, 0.0f, 1.0f); + int scale = std::max(static_cast(colorComponent * 1023), 0); + if (scale < 192) +- return g_sRGBSamples1[scale] / 255.0f; +- return g_sRGBSamples2[scale / 4 - 48] / 255.0f; ++ return kSRGBSamples1[scale] / 255.0f; ++ return kSRGBSamples2[scale / 4 - 48] / 255.0f; + } + + void XYZ_to_sRGB(float X, float Y, float Z, float* R, float* G, float* B) { +@@ -440,45 +426,35 @@ void XYZ_to_sRGB_WhitePoint(float X, + + } // namespace + +-PatternValue::PatternValue() { +- std::fill(std::begin(m_Comps), std::end(m_Comps), 0.0f); +-} ++PatternValue::PatternValue() = default; + + PatternValue::PatternValue(const PatternValue& that) = default; + + PatternValue::~PatternValue() = default; + + void PatternValue::SetComps(pdfium::span comps) { +- CHECK(comps.size() <= m_Comps.size()); +- std::copy(std::begin(comps), std::end(comps), std::begin(m_Comps)); ++ fxcrt::spancpy(pdfium::make_span(m_Comps), comps); + } + + // static +-RetainPtr CPDF_ColorSpace::ColorspaceFromName( ++RetainPtr CPDF_ColorSpace::GetStockCSForName( + const ByteString& name) { + if (name == "DeviceRGB" || name == "RGB") +- return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB); ++ return GetStockCS(Family::kDeviceRGB); + if (name == "DeviceGray" || name == "G") +- return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY); ++ return GetStockCS(Family::kDeviceGray); + if (name == "DeviceCMYK" || name == "CMYK") +- return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK); ++ return GetStockCS(Family::kDeviceCMYK); + if (name == "Pattern") +- return CPDF_ColorSpace::GetStockCS(PDFCS_PATTERN); ++ return GetStockCS(Family::kPattern); + return nullptr; + } + + // static +-RetainPtr CPDF_ColorSpace::GetStockCS(int family) { ++RetainPtr CPDF_ColorSpace::GetStockCS(Family family) { + return CPDF_PageModule::GetInstance()->GetStockCS(family); + } + +-// static +-RetainPtr CPDF_ColorSpace::Load(CPDF_Document* pDoc, +- CPDF_Object* pObj) { +- std::set visited; +- return Load(pDoc, pObj, &visited); +-} +- + // static + RetainPtr CPDF_ColorSpace::Load( + CPDF_Document* pDoc, +@@ -487,25 +463,25 @@ RetainPtr CPDF_ColorSpace::Load( + if (!pObj) + return nullptr; + +- if (pdfium::ContainsKey(*pVisited, pObj)) ++ if (pdfium::Contains(*pVisited, pObj)) + return nullptr; + +- pdfium::ScopedSetInsertion insertion(pVisited, pObj); ++ ScopedSetInsertion insertion(pVisited, pObj); + + if (pObj->IsName()) +- return ColorspaceFromName(pObj->GetString()); ++ return GetStockCSForName(pObj->GetString()); + + if (const CPDF_Stream* pStream = pObj->AsStream()) { +- const CPDF_Dictionary* pDict = pStream->GetDict(); ++ RetainPtr pDict = pStream->GetDict(); + if (!pDict) + return nullptr; + +- CPDF_DictionaryLocker locker(pDict); ++ CPDF_DictionaryLocker locker(std::move(pDict)); + for (const auto& it : locker) { +- CPDF_Name* pValue = ToName(it.second.Get()); ++ RetainPtr pValue = ToName(it.second); + if (pValue) { + RetainPtr pRet = +- ColorspaceFromName(pValue->GetString()); ++ GetStockCSForName(pValue->GetString()); + if (pRet) + return pRet; + } +@@ -517,64 +493,64 @@ RetainPtr CPDF_ColorSpace::Load( + if (!pArray || pArray->IsEmpty()) + return nullptr; + +- const CPDF_Object* pFamilyObj = pArray->GetDirectObjectAt(0); ++ RetainPtr pFamilyObj = pArray->GetDirectObjectAt(0); + if (!pFamilyObj) + return nullptr; + + ByteString familyname = pFamilyObj->GetString(); + if (pArray->size() == 1) +- return ColorspaceFromName(familyname); ++ return GetStockCSForName(familyname); + +- RetainPtr pCS; +- switch (familyname.GetID()) { ++ RetainPtr pCS = ++ CPDF_ColorSpace::AllocateColorSpace(familyname.AsStringView()); ++ if (!pCS) ++ return nullptr; ++ ++ pCS->m_pArray.Reset(pArray); ++ pCS->m_nComponents = pCS->v_Load(pDoc, pArray, pVisited); ++ if (pCS->m_nComponents == 0) ++ return nullptr; ++ ++ return pCS; ++} ++ ++// static ++RetainPtr CPDF_ColorSpace::AllocateColorSpace( ++ ByteStringView bsFamilyName) { ++ switch (bsFamilyName.GetID()) { + case FXBSTR_ID('C', 'a', 'l', 'G'): +- pCS = pdfium::MakeRetain(pDoc); +- break; ++ return pdfium::MakeRetain(); + case FXBSTR_ID('C', 'a', 'l', 'R'): +- pCS = pdfium::MakeRetain(pDoc); +- break; ++ return pdfium::MakeRetain(); + case FXBSTR_ID('L', 'a', 'b', 0): +- pCS = pdfium::MakeRetain(pDoc); +- break; ++ return pdfium::MakeRetain(); + case FXBSTR_ID('I', 'C', 'C', 'B'): +- pCS = pdfium::MakeRetain(pDoc); +- break; ++ return pdfium::MakeRetain(); + case FXBSTR_ID('I', 'n', 'd', 'e'): + case FXBSTR_ID('I', 0, 0, 0): +- pCS = pdfium::MakeRetain(pDoc); +- break; ++ return pdfium::MakeRetain(); + case FXBSTR_ID('S', 'e', 'p', 'a'): +- pCS = pdfium::MakeRetain(pDoc); +- break; ++ return pdfium::MakeRetain(); + case FXBSTR_ID('D', 'e', 'v', 'i'): +- pCS = pdfium::MakeRetain(pDoc); +- break; ++ return pdfium::MakeRetain(); + case FXBSTR_ID('P', 'a', 't', 't'): +- pCS = pdfium::MakeRetain(pDoc); +- break; ++ return pdfium::MakeRetain(); + default: + return nullptr; + } +- pCS->m_pArray.Reset(pArray); +- pCS->m_nComponents = pCS->v_Load(pDoc, pArray, pVisited); +- if (pCS->m_nComponents == 0) +- return nullptr; +- +- return pCS; + } + + // static +-uint32_t CPDF_ColorSpace::ComponentsForFamily(int family) { ++uint32_t CPDF_ColorSpace::ComponentsForFamily(Family family) { + switch (family) { +- case PDFCS_DEVICEGRAY: ++ case Family::kDeviceGray: + return 1; +- case PDFCS_DEVICERGB: ++ case Family::kDeviceRGB: + return 3; +- case PDFCS_DEVICECMYK: ++ case Family::kDeviceCMYK: + return 4; + default: +- NOTREACHED(); +- return 4; ++ NOTREACHED_NORETURN(); + } + } + +@@ -584,7 +560,7 @@ bool CPDF_ColorSpace::IsValidIccComponents(int components) { + } + + std::vector CPDF_ColorSpace::CreateBufAndSetDefaultColor() const { +- ASSERT(m_Family != PDFCS_PATTERN); ++ DCHECK(m_Family != Family::kPattern); + + float min; + float max; +@@ -608,21 +584,23 @@ void CPDF_ColorSpace::GetDefaultValue(int iComponent, + *max = 1.0f; + } + +-void CPDF_ColorSpace::TranslateImageLine(uint8_t* dest_buf, +- const uint8_t* src_buf, ++void CPDF_ColorSpace::TranslateImageLine(pdfium::span dest_span, ++ pdfium::span src_span, + int pixels, + int image_width, + int image_height, + bool bTransMask) const { ++ uint8_t* dest_buf = dest_span.data(); ++ const uint8_t* src_buf = src_span.data(); + std::vector src(m_nComponents); + float R; + float G; + float B; +- const int divisor = m_Family != PDFCS_INDEXED ? 255 : 1; ++ const int divisor = m_Family != Family::kIndexed ? 255 : 1; + for (int i = 0; i < pixels; i++) { + for (uint32_t j = 0; j < m_nComponents; j++) + src[j] = static_cast(*src_buf++) / divisor; +- GetRGB(src.data(), &R, &G, &B); ++ GetRGB(src, &R, &G, &B); + *dest_buf++ = static_cast(B * 255); + *dest_buf++ = static_cast(G * 255); + *dest_buf++ = static_cast(R * 255); +@@ -637,135 +615,126 @@ void CPDF_ColorSpace::EnableStdConversion(bool bEnabled) { + } + + bool CPDF_ColorSpace::IsNormal() const { +- return GetFamily() == PDFCS_DEVICEGRAY || GetFamily() == PDFCS_DEVICERGB || +- GetFamily() == PDFCS_DEVICECMYK || GetFamily() == PDFCS_CALGRAY || +- GetFamily() == PDFCS_CALRGB; +-} +- +-CPDF_PatternCS* CPDF_ColorSpace::AsPatternCS() { +- NOTREACHED(); +- return nullptr; ++ return GetFamily() == Family::kDeviceGray || ++ GetFamily() == Family::kDeviceRGB || ++ GetFamily() == Family::kDeviceCMYK || ++ GetFamily() == Family::kCalGray || GetFamily() == Family::kCalRGB; + } + + const CPDF_PatternCS* CPDF_ColorSpace::AsPatternCS() const { +- NOTREACHED(); + return nullptr; + } + +-bool CPDF_ColorSpace::GetPatternRGB(const PatternValue& value, +- float* R, +- float* G, +- float* B) const { +- NOTREACHED(); +- return false; ++const CPDF_IndexedCS* CPDF_ColorSpace::AsIndexedCS() const { ++ return nullptr; + } + +-CPDF_ColorSpace::CPDF_ColorSpace(CPDF_Document* pDoc, int family) +- : m_pDocument(pDoc), m_Family(family) {} ++CPDF_ColorSpace::CPDF_ColorSpace(Family family) : m_Family(family) {} + + CPDF_ColorSpace::~CPDF_ColorSpace() = default; + + void CPDF_ColorSpace::SetComponentsForStockCS(uint32_t nComponents) { +- ASSERT(!m_pDocument); // Stock colorspace is not associated with a document. + m_nComponents = nComponents; + } + +-CPDF_CalGray::CPDF_CalGray(CPDF_Document* pDoc) +- : CPDF_ColorSpace(pDoc, PDFCS_CALGRAY) {} ++CPDF_CalGray::CPDF_CalGray() : CPDF_ColorSpace(Family::kCalGray) {} + + CPDF_CalGray::~CPDF_CalGray() = default; + + uint32_t CPDF_CalGray::v_Load(CPDF_Document* pDoc, + const CPDF_Array* pArray, + std::set* pVisited) { +- const CPDF_Dictionary* pDict = pArray->GetDictAt(1); ++ RetainPtr pDict = pArray->GetDictAt(1); + if (!pDict) + return 0; + +- if (!GetWhitePoint(pDict, m_WhitePoint)) ++ if (!GetWhitePoint(pDict.Get(), m_WhitePoint)) + return 0; + +- GetBlackPoint(pDict, m_BlackPoint); ++ GetBlackPoint(pDict.Get(), m_BlackPoint); + +- m_Gamma = pDict->GetNumberFor("Gamma"); ++ m_Gamma = pDict->GetFloatFor("Gamma"); + if (m_Gamma == 0) + m_Gamma = kDefaultGamma; + return 1; + } + +-bool CPDF_CalGray::GetRGB(const float* pBuf, ++bool CPDF_CalGray::GetRGB(pdfium::span pBuf, + float* R, + float* G, + float* B) const { +- *R = *pBuf; +- *G = *pBuf; +- *B = *pBuf; ++ *R = pBuf[0]; ++ *G = pBuf[0]; ++ *B = pBuf[0]; + return true; + } + +-void CPDF_CalGray::TranslateImageLine(uint8_t* pDestBuf, +- const uint8_t* pSrcBuf, ++void CPDF_CalGray::TranslateImageLine(pdfium::span dest_span, ++ pdfium::span src_span, + int pixels, + int image_width, + int image_height, + bool bTransMask) const { ++ uint8_t* pDestBuf = dest_span.data(); ++ const uint8_t* pSrcBuf = src_span.data(); + for (int i = 0; i < pixels; i++) { +- *pDestBuf++ = pSrcBuf[i]; +- *pDestBuf++ = pSrcBuf[i]; +- *pDestBuf++ = pSrcBuf[i]; ++ // Compiler can not conclude that src/dest don't overlap. ++ const uint8_t pix = pSrcBuf[i]; ++ *pDestBuf++ = pix; ++ *pDestBuf++ = pix; ++ *pDestBuf++ = pix; + } + } + +-CPDF_CalRGB::CPDF_CalRGB(CPDF_Document* pDoc) +- : CPDF_ColorSpace(pDoc, PDFCS_CALRGB) {} ++CPDF_CalRGB::CPDF_CalRGB() : CPDF_ColorSpace(Family::kCalRGB) {} + + CPDF_CalRGB::~CPDF_CalRGB() = default; + + uint32_t CPDF_CalRGB::v_Load(CPDF_Document* pDoc, + const CPDF_Array* pArray, + std::set* pVisited) { +- const CPDF_Dictionary* pDict = pArray->GetDictAt(1); ++ RetainPtr pDict = pArray->GetDictAt(1); + if (!pDict) + return 0; + +- if (!GetWhitePoint(pDict, m_WhitePoint)) ++ if (!GetWhitePoint(pDict.Get(), m_WhitePoint)) + return 0; + +- GetBlackPoint(pDict, m_BlackPoint); ++ GetBlackPoint(pDict.Get(), m_BlackPoint); + +- const CPDF_Array* pParam = pDict->GetArrayFor("Gamma"); +- if (pParam) { +- m_bGamma = true; +- for (size_t i = 0; i < FX_ArraySize(m_Gamma); ++i) +- m_Gamma[i] = pParam->GetNumberAt(i); ++ RetainPtr pGamma = pDict->GetArrayFor("Gamma"); ++ if (pGamma) { ++ m_bHasGamma = true; ++ for (size_t i = 0; i < std::size(m_Gamma); ++i) ++ m_Gamma[i] = pGamma->GetFloatAt(i); + } + +- pParam = pDict->GetArrayFor("Matrix"); +- if (pParam) { +- m_bMatrix = true; +- for (size_t i = 0; i < FX_ArraySize(m_Matrix); ++i) +- m_Matrix[i] = pParam->GetNumberAt(i); ++ RetainPtr pMatrix = pDict->GetArrayFor("Matrix"); ++ if (pMatrix) { ++ m_bHasMatrix = true; ++ for (size_t i = 0; i < std::size(m_Matrix); ++i) ++ m_Matrix[i] = pMatrix->GetFloatAt(i); + } + return 3; + } + +-bool CPDF_CalRGB::GetRGB(const float* pBuf, ++bool CPDF_CalRGB::GetRGB(pdfium::span pBuf, + float* R, + float* G, + float* B) const { + float A_ = pBuf[0]; + float B_ = pBuf[1]; + float C_ = pBuf[2]; +- if (m_bGamma) { +- A_ = FXSYS_pow(A_, m_Gamma[0]); +- B_ = FXSYS_pow(B_, m_Gamma[1]); +- C_ = FXSYS_pow(C_, m_Gamma[2]); ++ if (m_bHasGamma) { ++ A_ = powf(A_, m_Gamma[0]); ++ B_ = powf(B_, m_Gamma[1]); ++ C_ = powf(C_, m_Gamma[2]); + } + + float X; + float Y; + float Z; +- if (m_bMatrix) { ++ if (m_bHasMatrix) { + X = m_Matrix[0] * A_ + m_Matrix[3] * B_ + m_Matrix[6] * C_; + Y = m_Matrix[1] * A_ + m_Matrix[4] * B_ + m_Matrix[7] * C_; + Z = m_Matrix[2] * A_ + m_Matrix[5] * B_ + m_Matrix[8] * C_; +@@ -779,34 +748,37 @@ bool CPDF_CalRGB::GetRGB(const float* pBuf, + return true; + } + +-void CPDF_CalRGB::TranslateImageLine(uint8_t* pDestBuf, +- const uint8_t* pSrcBuf, ++void CPDF_CalRGB::TranslateImageLine(pdfium::span dest_span, ++ pdfium::span src_span, + int pixels, + int image_width, + int image_height, + bool bTransMask) const { +- if (bTransMask) { +- float Cal[3]; +- float R; +- float G; +- float B; +- for (int i = 0; i < pixels; i++) { +- Cal[0] = static_cast(pSrcBuf[2]) / 255; +- Cal[1] = static_cast(pSrcBuf[1]) / 255; +- Cal[2] = static_cast(pSrcBuf[0]) / 255; +- GetRGB(Cal, &R, &G, &B); +- pDestBuf[0] = FXSYS_roundf(B * 255); +- pDestBuf[1] = FXSYS_roundf(G * 255); +- pDestBuf[2] = FXSYS_roundf(R * 255); +- pSrcBuf += 3; +- pDestBuf += 3; +- } ++ uint8_t* pDestBuf = dest_span.data(); ++ const uint8_t* pSrcBuf = src_span.data(); ++ if (!bTransMask) { ++ fxcodec::ReverseRGB(pDestBuf, pSrcBuf, pixels); ++ return; ++ } ++ ++ float Cal[3]; ++ float R; ++ float G; ++ float B; ++ for (int i = 0; i < pixels; i++) { ++ Cal[0] = static_cast(pSrcBuf[2]) / 255; ++ Cal[1] = static_cast(pSrcBuf[1]) / 255; ++ Cal[2] = static_cast(pSrcBuf[0]) / 255; ++ GetRGB(Cal, &R, &G, &B); ++ pDestBuf[0] = FXSYS_roundf(B * 255); ++ pDestBuf[1] = FXSYS_roundf(G * 255); ++ pDestBuf[2] = FXSYS_roundf(R * 255); ++ pSrcBuf += 3; ++ pDestBuf += 3; + } +- fxcodec::ReverseRGB(pDestBuf, pSrcBuf, pixels); + } + +-CPDF_LabCS::CPDF_LabCS(CPDF_Document* pDoc) +- : CPDF_ColorSpace(pDoc, PDFCS_LAB) {} ++CPDF_LabCS::CPDF_LabCS() : CPDF_ColorSpace(Family::kLab) {} + + CPDF_LabCS::~CPDF_LabCS() = default; + +@@ -814,42 +786,50 @@ void CPDF_LabCS::GetDefaultValue(int iComponent, + float* value, + float* min, + float* max) const { +- ASSERT(iComponent < 3); +- if (iComponent == 0) { +- *min = 0.0f; +- *max = 100 * 1.0f; +- *value = 0.0f; +- return; ++ DCHECK_LT(iComponent, 3); ++ ++ if (iComponent > 0) { ++ float range_min = m_Ranges[iComponent * 2 - 2]; ++ float range_max = m_Ranges[iComponent * 2 - 1]; ++ if (range_min <= range_max) { ++ *min = range_min; ++ *max = range_max; ++ *value = pdfium::clamp(0.0f, *min, *max); ++ return; ++ } + } + +- *min = m_Ranges[iComponent * 2 - 2]; +- *max = m_Ranges[iComponent * 2 - 1]; +- *value = pdfium::clamp(0.0f, *min, *max); ++ *min = 0.0f; ++ *max = 100.0f; ++ *value = 0.0f; + } + + uint32_t CPDF_LabCS::v_Load(CPDF_Document* pDoc, + const CPDF_Array* pArray, + std::set* pVisited) { +- const CPDF_Dictionary* pDict = pArray->GetDictAt(1); ++ RetainPtr pDict = pArray->GetDictAt(1); + if (!pDict) + return 0; + +- if (!GetWhitePoint(pDict, m_WhitePoint)) ++ if (!GetWhitePoint(pDict.Get(), m_WhitePoint)) + return 0; + +- GetBlackPoint(pDict, m_BlackPoint); ++ GetBlackPoint(pDict.Get(), m_BlackPoint); + +- const CPDF_Array* pParam = pDict->GetArrayFor("Range"); ++ RetainPtr pParam = pDict->GetArrayFor("Range"); + static constexpr float kDefaultRanges[kRangesCount] = {-100.0f, 100.0f, + -100.0f, 100.0f}; +- static_assert(FX_ArraySize(kDefaultRanges) == FX_ArraySize(m_Ranges), ++ static_assert(std::size(kDefaultRanges) == std::extent(), + "Range size mismatch"); +- for (size_t i = 0; i < FX_ArraySize(kDefaultRanges); ++i) +- m_Ranges[i] = pParam ? pParam->GetNumberAt(i) : kDefaultRanges[i]; ++ for (size_t i = 0; i < std::size(kDefaultRanges); ++i) ++ m_Ranges[i] = pParam ? pParam->GetFloatAt(i) : kDefaultRanges[i]; + return 3; + } + +-bool CPDF_LabCS::GetRGB(const float* pBuf, float* R, float* G, float* B) const { ++bool CPDF_LabCS::GetRGB(pdfium::span pBuf, ++ float* R, ++ float* G, ++ float* B) const { + float Lstar = pBuf[0]; + float astar = pBuf[1]; + float bstar = pBuf[2]; +@@ -878,12 +858,14 @@ bool CPDF_LabCS::GetRGB(const float* pBuf, float* R, float* G, float* B) const { + return true; + } + +-void CPDF_LabCS::TranslateImageLine(uint8_t* pDestBuf, +- const uint8_t* pSrcBuf, ++void CPDF_LabCS::TranslateImageLine(pdfium::span dest_span, ++ pdfium::span src_span, + int pixels, + int image_width, + int image_height, + bool bTransMask) const { ++ uint8_t* pDestBuf = dest_span.data(); ++ const uint8_t* pSrcBuf = src_span.data(); + for (int i = 0; i < pixels; i++) { + float lab[3]; + lab[0] = pSrcBuf[0] * 100 / 255.0f; +@@ -902,22 +884,21 @@ void CPDF_LabCS::TranslateImageLine(uint8_t* pDestBuf, + } + } + +-CPDF_ICCBasedCS::CPDF_ICCBasedCS(CPDF_Document* pDoc) +- : CPDF_ColorSpace(pDoc, PDFCS_ICCBASED) {} ++CPDF_ICCBasedCS::CPDF_ICCBasedCS() : CPDF_BasedCS(Family::kICCBased) {} + + CPDF_ICCBasedCS::~CPDF_ICCBasedCS() = default; + + uint32_t CPDF_ICCBasedCS::v_Load(CPDF_Document* pDoc, + const CPDF_Array* pArray, + std::set* pVisited) { +- const CPDF_Stream* pStream = pArray->GetStreamAt(1); ++ RetainPtr pStream = pArray->GetStreamAt(1); + if (!pStream) + return 0; + + // The PDF 1.7 spec says the number of components must be valid. While some + // PDF viewers tolerate invalid values, Acrobat does not, so be consistent + // with Acrobat and reject bad values. +- const CPDF_Dictionary* pDict = pStream->GetDict(); ++ RetainPtr pDict = pStream->GetDict(); + int32_t nDictComponents = pDict ? pDict->GetIntegerFor("N") : 0; + if (!IsValidIccComponents(nDictComponents)) + return 0; +@@ -938,39 +919,38 @@ uint32_t CPDF_ICCBasedCS::v_Load(CPDF_Document* pDoc, + // SRGB, a profile PDFium recognizes but does not support well, then try the + // alternate profile. + if (!m_pProfile->IsSupported() && +- !FindAlternateProfile(pDoc, pDict, pVisited, nComponents)) { ++ !FindAlternateProfile(pDoc, pDict.Get(), pVisited, nComponents)) { + // If there is no alternate profile, use a stock profile as mentioned in + // the PDF 1.7 spec in table 4.16 in the "Alternate" key description. +- ASSERT(!m_pAlterCS); +- m_pAlterCS = GetStockAlternateProfile(nComponents); ++ DCHECK(!m_pBaseCS); ++ m_pBaseCS = GetStockAlternateProfile(nComponents); + } + +- m_pRanges = GetRanges(pDict, nComponents); ++ m_pRanges = GetRanges(pDict.Get(), nComponents); + return nComponents; + } + +-bool CPDF_ICCBasedCS::GetRGB(const float* pBuf, ++bool CPDF_ICCBasedCS::GetRGB(pdfium::span pBuf, + float* R, + float* G, + float* B) const { +- ASSERT(m_pProfile); ++ DCHECK(m_pProfile); + if (m_pProfile->IsSRGB()) { + *R = pBuf[0]; + *G = pBuf[1]; + *B = pBuf[2]; + return true; + } +- if (m_pProfile->transform()) { ++ if (m_pProfile->IsSupported()) { + float rgb[3]; +- IccModule::Translate(m_pProfile->transform(), CountComponents(), pBuf, rgb); ++ m_pProfile->Translate(pBuf.first(CountComponents()), rgb); + *R = rgb[0]; + *G = rgb[1]; + *B = rgb[2]; + return true; + } +- +- if (m_pAlterCS) +- return m_pAlterCS->GetRGB(pBuf, R, G, B); ++ if (m_pBaseCS) ++ return m_pBaseCS->GetRGB(pBuf, R, G, B); + + *R = 0.0f; + *G = 0.0f; +@@ -978,33 +958,27 @@ bool CPDF_ICCBasedCS::GetRGB(const float* pBuf, + return true; + } + +-void CPDF_ICCBasedCS::EnableStdConversion(bool bEnabled) { +- CPDF_ColorSpace::EnableStdConversion(bEnabled); +- if (m_pAlterCS) +- m_pAlterCS->EnableStdConversion(bEnabled); +-} +- +-void CPDF_ICCBasedCS::TranslateImageLine(uint8_t* pDestBuf, +- const uint8_t* pSrcBuf, ++void CPDF_ICCBasedCS::TranslateImageLine(pdfium::span dest_span, ++ pdfium::span src_span, + int pixels, + int image_width, + int image_height, + bool bTransMask) const { + if (m_pProfile->IsSRGB()) { +- fxcodec::ReverseRGB(pDestBuf, pSrcBuf, pixels); ++ fxcodec::ReverseRGB(dest_span.data(), src_span.data(), pixels); + return; + } +- if (!m_pProfile->transform()) { +- if (m_pAlterCS) { +- m_pAlterCS->TranslateImageLine(pDestBuf, pSrcBuf, pixels, image_width, +- image_height, false); ++ if (!m_pProfile->IsSupported()) { ++ if (m_pBaseCS) { ++ m_pBaseCS->TranslateImageLine(dest_span, src_span, pixels, image_width, ++ image_height, false); + } + return; + } + + // |nMaxColors| will not overflow since |nComponents| is limited in size. + const uint32_t nComponents = CountComponents(); +- ASSERT(IsValidIccComponents(nComponents)); ++ DCHECK(IsValidIccComponents(nComponents)); + int nMaxColors = 1; + for (uint32_t i = 0; i < nComponents; i++) + nMaxColors *= 52; +@@ -1016,15 +990,13 @@ void CPDF_ICCBasedCS::TranslateImageLine(uint8_t* pDestBuf, + if (nPixelCount.IsValid()) + bTranslate = nPixelCount.ValueOrDie() < nMaxColors * 3 / 2; + } +- if (bTranslate) { +- IccModule::TranslateScanline(m_pProfile->transform(), pDestBuf, pSrcBuf, +- pixels); ++ if (bTranslate && m_pProfile->IsSupported()) { ++ m_pProfile->TranslateScanline(dest_span, src_span, pixels); + return; + } +- + if (m_pCache.empty()) { +- m_pCache = pdfium::Vector2D(nMaxColors, 3); +- auto temp_src = pdfium::Vector2D(nMaxColors, nComponents); ++ m_pCache.resize(Fx2DSizeOrDie(nMaxColors, 3)); ++ DataVector temp_src(Fx2DSizeOrDie(nMaxColors, nComponents)); + size_t src_index = 0; + for (int i = 0; i < nMaxColors; i++) { + uint32_t color = i; +@@ -1035,9 +1007,12 @@ void CPDF_ICCBasedCS::TranslateImageLine(uint8_t* pDestBuf, + order /= 52; + } + } +- IccModule::TranslateScanline(m_pProfile->transform(), m_pCache.data(), +- temp_src.data(), nMaxColors); ++ if (m_pProfile->IsSupported()) { ++ m_pProfile->TranslateScanline(m_pCache, temp_src, nMaxColors); ++ } + } ++ uint8_t* pDestBuf = dest_span.data(); ++ const uint8_t* pSrcBuf = src_span.data(); + for (int i = 0; i < pixels; i++) { + int index = 0; + for (uint32_t c = 0; c < nComponents; c++) { +@@ -1054,10 +1029,10 @@ void CPDF_ICCBasedCS::TranslateImageLine(uint8_t* pDestBuf, + bool CPDF_ICCBasedCS::IsNormal() const { + if (m_pProfile->IsSRGB()) + return true; +- if (m_pProfile->transform()) +- return m_pProfile->transform()->IsNormal(); +- if (m_pAlterCS) +- return m_pAlterCS->IsNormal(); ++ if (m_pProfile->IsSupported()) ++ return m_pProfile->IsNormal(); ++ if (m_pBaseCS) ++ return m_pBaseCS->IsNormal(); + return false; + } + +@@ -1066,21 +1041,22 @@ bool CPDF_ICCBasedCS::FindAlternateProfile( + const CPDF_Dictionary* pDict, + std::set* pVisited, + uint32_t nExpectedComponents) { +- const CPDF_Object* pAlterCSObj = pDict->GetDirectObjectFor("Alternate"); ++ RetainPtr pAlterCSObj = ++ pDict->GetDirectObjectFor("Alternate"); + if (!pAlterCSObj) + return false; + +- auto pAlterCS = CPDF_ColorSpace::Load(pDoc, pAlterCSObj, pVisited); ++ auto pAlterCS = CPDF_ColorSpace::Load(pDoc, pAlterCSObj.Get(), pVisited); + if (!pAlterCS) + return false; + +- if (pAlterCS->GetFamily() == PDFCS_PATTERN) ++ if (pAlterCS->GetFamily() == Family::kPattern) + return false; + + if (pAlterCS->CountComponents() != nExpectedComponents) + return false; + +- m_pAlterCS = std::move(pAlterCS); ++ m_pBaseCS = std::move(pAlterCS); + return true; + } + +@@ -1088,122 +1064,32 @@ bool CPDF_ICCBasedCS::FindAlternateProfile( + RetainPtr CPDF_ICCBasedCS::GetStockAlternateProfile( + uint32_t nComponents) { + if (nComponents == 1) +- return GetStockCS(PDFCS_DEVICEGRAY); ++ return GetStockCS(Family::kDeviceGray); + if (nComponents == 3) +- return GetStockCS(PDFCS_DEVICERGB); ++ return GetStockCS(Family::kDeviceRGB); + if (nComponents == 4) +- return GetStockCS(PDFCS_DEVICECMYK); +- NOTREACHED(); +- return nullptr; ++ return GetStockCS(Family::kDeviceCMYK); ++ NOTREACHED_NORETURN(); + } + + // static + std::vector CPDF_ICCBasedCS::GetRanges(const CPDF_Dictionary* pDict, + uint32_t nComponents) { +- ASSERT(IsValidIccComponents(nComponents)); ++ DCHECK(IsValidIccComponents(nComponents)); ++ RetainPtr pRanges = pDict->GetArrayFor("Range"); ++ if (pRanges && pRanges->size() >= nComponents * 2) ++ return ReadArrayElementsToVector(pRanges.Get(), nComponents * 2); + + std::vector ranges; +- const CPDF_Array* pRanges = pDict->GetArrayFor("Range"); +- if (pRanges) { +- ranges = ReadArrayElementsToVector(pRanges, nComponents * 2); +- } else { +- ranges.reserve(nComponents * 2); +- for (uint32_t i = 0; i < nComponents; i++) { +- ranges.push_back(0.0f); +- ranges.push_back(1.0f); +- } ++ ranges.reserve(nComponents * 2); ++ for (uint32_t i = 0; i < nComponents; i++) { ++ ranges.push_back(0.0f); ++ ranges.push_back(1.0f); + } + return ranges; + } + +-CPDF_IndexedCS::CPDF_IndexedCS(CPDF_Document* pDoc) +- : CPDF_ColorSpace(pDoc, PDFCS_INDEXED) {} +- +-CPDF_IndexedCS::~CPDF_IndexedCS() = default; +- +-uint32_t CPDF_IndexedCS::v_Load(CPDF_Document* pDoc, +- const CPDF_Array* pArray, +- std::set* pVisited) { +- if (pArray->size() < 4) +- return 0; +- +- const CPDF_Object* pBaseObj = pArray->GetDirectObjectAt(1); +- if (pBaseObj == m_pArray) +- return 0; +- +- auto* pDocPageData = CPDF_DocPageData::FromDocument(pDoc); +- m_pBaseCS = pDocPageData->GetColorSpaceGuarded(pBaseObj, nullptr, pVisited); +- if (!m_pBaseCS) +- return 0; +- +- // The base color space cannot be a Pattern or Indexed space, according to the +- // PDF 1.7 spec, page 263. +- int family = m_pBaseCS->GetFamily(); +- if (family == PDFCS_INDEXED || family == PDFCS_PATTERN) +- return 0; +- +- m_nBaseComponents = m_pBaseCS->CountComponents(); +- m_pCompMinMax = pdfium::Vector2D(m_nBaseComponents, 2); +- float defvalue; +- for (uint32_t i = 0; i < m_nBaseComponents; i++) { +- m_pBaseCS->GetDefaultValue(i, &defvalue, &m_pCompMinMax[i * 2], +- &m_pCompMinMax[i * 2 + 1]); +- m_pCompMinMax[i * 2 + 1] -= m_pCompMinMax[i * 2]; +- } +- m_MaxIndex = pArray->GetIntegerAt(2); +- +- const CPDF_Object* pTableObj = pArray->GetDirectObjectAt(3); +- if (!pTableObj) +- return 0; +- +- if (const CPDF_String* pString = pTableObj->AsString()) { +- m_Table = pString->GetString(); +- } else if (const CPDF_Stream* pStream = pTableObj->AsStream()) { +- auto pAcc = pdfium::MakeRetain(pStream); +- pAcc->LoadAllDataFiltered(); +- m_Table = ByteStringView(pAcc->GetSpan()); +- } +- return 1; +-} +- +-bool CPDF_IndexedCS::GetRGB(const float* pBuf, +- float* R, +- float* G, +- float* B) const { +- int32_t index = static_cast(*pBuf); +- if (index < 0 || index > m_MaxIndex) +- return false; +- +- if (m_nBaseComponents) { +- FX_SAFE_SIZE_T length = index; +- length += 1; +- length *= m_nBaseComponents; +- if (!length.IsValid() || length.ValueOrDie() > m_Table.GetLength()) { +- *R = 0; +- *G = 0; +- *B = 0; +- return false; +- } +- } +- std::vector comps(m_nBaseComponents); +- const uint8_t* pTable = m_Table.raw_str(); +- for (uint32_t i = 0; i < m_nBaseComponents; ++i) { +- comps[i] = +- m_pCompMinMax[i * 2] + +- m_pCompMinMax[i * 2 + 1] * pTable[index * m_nBaseComponents + i] / 255; +- } +- ASSERT(m_nBaseComponents == m_pBaseCS->CountComponents()); +- return m_pBaseCS->GetRGB(comps.data(), R, G, B); +-} +- +-void CPDF_IndexedCS::EnableStdConversion(bool bEnabled) { +- CPDF_ColorSpace::EnableStdConversion(bEnabled); +- if (m_pBaseCS) +- m_pBaseCS->EnableStdConversion(bEnabled); +-} +- +-CPDF_SeparationCS::CPDF_SeparationCS(CPDF_Document* pDoc) +- : CPDF_ColorSpace(pDoc, PDFCS_SEPARATION) {} ++CPDF_SeparationCS::CPDF_SeparationCS() : CPDF_BasedCS(Family::kSeparation) {} + + CPDF_SeparationCS::~CPDF_SeparationCS() = default; + +@@ -1219,74 +1105,64 @@ void CPDF_SeparationCS::GetDefaultValue(int iComponent, + uint32_t CPDF_SeparationCS::v_Load(CPDF_Document* pDoc, + const CPDF_Array* pArray, + std::set* pVisited) { +- ByteString name = pArray->GetStringAt(1); +- if (name == "None") { +- m_Type = None; ++ m_IsNoneType = pArray->GetByteStringAt(1) == "None"; ++ if (m_IsNoneType) + return 1; +- } + +- m_Type = Colorant; +- const CPDF_Object* pAltCS = pArray->GetDirectObjectAt(2); +- if (pAltCS == m_pArray) ++ RetainPtr pAltArray = pArray->GetDirectObjectAt(2); ++ if (HasSameArray(pAltArray.Get())) + return 0; + +- m_pAltCS = Load(pDoc, pAltCS, pVisited); +- if (!m_pAltCS) ++ m_pBaseCS = Load(pDoc, pAltArray.Get(), pVisited); ++ if (!m_pBaseCS) + return 0; + +- if (m_pAltCS->IsSpecial()) ++ if (m_pBaseCS->IsSpecial()) + return 0; + +- const CPDF_Object* pFuncObj = pArray->GetDirectObjectAt(3); ++ RetainPtr pFuncObj = pArray->GetDirectObjectAt(3); + if (pFuncObj && !pFuncObj->IsName()) { +- auto pFunc = CPDF_Function::Load(pFuncObj); +- if (pFunc && pFunc->CountOutputs() >= m_pAltCS->CountComponents()) ++ auto pFunc = CPDF_Function::Load(std::move(pFuncObj)); ++ if (pFunc && pFunc->CountOutputs() >= m_pBaseCS->CountComponents()) + m_pFunc = std::move(pFunc); + } + return 1; + } + +-bool CPDF_SeparationCS::GetRGB(const float* pBuf, ++bool CPDF_SeparationCS::GetRGB(pdfium::span pBuf, + float* R, + float* G, + float* B) const { +- if (m_Type == None) ++ if (m_IsNoneType) + return false; + + if (!m_pFunc) { +- if (!m_pAltCS) ++ if (!m_pBaseCS) + return false; + +- int nComps = m_pAltCS->CountComponents(); ++ int nComps = m_pBaseCS->CountComponents(); + std::vector results(nComps); + for (int i = 0; i < nComps; i++) +- results[i] = *pBuf; +- return m_pAltCS->GetRGB(results.data(), R, G, B); ++ results[i] = pBuf[0]; ++ return m_pBaseCS->GetRGB(results, R, G, B); + } + + // Using at least 16 elements due to the call m_pAltCS->GetRGB() below. + std::vector results(std::max(m_pFunc->CountOutputs(), 16u)); +- int nresults = 0; +- if (!m_pFunc->Call(pBuf, 1, results.data(), &nresults) || nresults == 0) ++ uint32_t nresults = m_pFunc->Call(pBuf.first(1), results).value_or(0); ++ if (nresults == 0) + return false; + +- if (m_pAltCS) +- return m_pAltCS->GetRGB(results.data(), R, G, B); ++ if (m_pBaseCS) ++ return m_pBaseCS->GetRGB(results, R, G, B); + +- R = 0; +- G = 0; +- B = 0; ++ *R = 0.0f; ++ *G = 0.0f; ++ *B = 0.0f; + return false; + } + +-void CPDF_SeparationCS::EnableStdConversion(bool bEnabled) { +- CPDF_ColorSpace::EnableStdConversion(bEnabled); +- if (m_pAltCS) +- m_pAltCS->EnableStdConversion(bEnabled); +-} +- +-CPDF_DeviceNCS::CPDF_DeviceNCS(CPDF_Document* pDoc) +- : CPDF_ColorSpace(pDoc, PDFCS_DEVICEN) {} ++CPDF_DeviceNCS::CPDF_DeviceNCS() : CPDF_BasedCS(Family::kDeviceN) {} + + CPDF_DeviceNCS::~CPDF_DeviceNCS() = default; + +@@ -1302,29 +1178,29 @@ void CPDF_DeviceNCS::GetDefaultValue(int iComponent, + uint32_t CPDF_DeviceNCS::v_Load(CPDF_Document* pDoc, + const CPDF_Array* pArray, + std::set* pVisited) { +- const CPDF_Array* pObj = ToArray(pArray->GetDirectObjectAt(1)); ++ RetainPtr pObj = ToArray(pArray->GetDirectObjectAt(1)); + if (!pObj) + return 0; + +- const CPDF_Object* pAltCS = pArray->GetDirectObjectAt(2); +- if (!pAltCS || pAltCS == m_pArray) ++ RetainPtr pAltCS = pArray->GetDirectObjectAt(2); ++ if (!pAltCS || HasSameArray(pAltCS.Get())) + return 0; + +- m_pAltCS = Load(pDoc, pAltCS, pVisited); ++ m_pBaseCS = Load(pDoc, pAltCS.Get(), pVisited); + m_pFunc = CPDF_Function::Load(pArray->GetDirectObjectAt(3)); +- if (!m_pAltCS || !m_pFunc) ++ if (!m_pBaseCS || !m_pFunc) + return 0; + +- if (m_pAltCS->IsSpecial()) ++ if (m_pBaseCS->IsSpecial()) + return 0; + +- if (m_pFunc->CountOutputs() < m_pAltCS->CountComponents()) ++ if (m_pFunc->CountOutputs() < m_pBaseCS->CountComponents()) + return 0; + +- return pObj->size(); ++ return fxcrt::CollectionSize(*pObj); + } + +-bool CPDF_DeviceNCS::GetRGB(const float* pBuf, ++bool CPDF_DeviceNCS::GetRGB(pdfium::span pBuf, + float* R, + float* G, + float* B) const { +@@ -1333,18 +1209,12 @@ bool CPDF_DeviceNCS::GetRGB(const float* pBuf, + + // Using at least 16 elements due to the call m_pAltCS->GetRGB() below. + std::vector results(std::max(m_pFunc->CountOutputs(), 16u)); +- int nresults = 0; +- if (!m_pFunc->Call(pBuf, CountComponents(), results.data(), &nresults) || +- nresults == 0) { +- return false; +- } ++ uint32_t nresults = ++ m_pFunc->Call(pBuf.first(CountComponents()), pdfium::make_span(results)) ++ .value_or(0); + +- return m_pAltCS->GetRGB(results.data(), R, G, B); +-} ++ if (nresults == 0) ++ return false; + +-void CPDF_DeviceNCS::EnableStdConversion(bool bEnabled) { +- CPDF_ColorSpace::EnableStdConversion(bEnabled); +- if (m_pAltCS) { +- m_pAltCS->EnableStdConversion(bEnabled); +- } ++ return m_pBaseCS->GetRGB(results, R, G, B); + } +diff --git a/core/fpdfapi/page/cpdf_colorspace.h b/core/fpdfapi/page/cpdf_colorspace.h +index 75928d252..23253c854 100644 +--- a/core/fpdfapi/page/cpdf_colorspace.h ++++ b/core/fpdfapi/page/cpdf_colorspace.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,34 +7,25 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_COLORSPACE_H_ + #define CORE_FPDFAPI_PAGE_CPDF_COLORSPACE_H_ + ++#include ++#include ++ + #include +-#include + #include ++#include + #include + + #include "core/fpdfapi/page/cpdf_pattern.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fpdfapi/parser/cpdf_array.h" ++#include "core/fpdfapi/parser/cpdf_object.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/observed_ptr.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + #include "third_party/base/span.h" + +-#define PDFCS_DEVICEGRAY 1 +-#define PDFCS_DEVICERGB 2 +-#define PDFCS_DEVICECMYK 3 +-#define PDFCS_CALGRAY 4 +-#define PDFCS_CALRGB 5 +-#define PDFCS_LAB 6 +-#define PDFCS_ICCBASED 7 +-#define PDFCS_SEPARATION 8 +-#define PDFCS_DEVICEN 9 +-#define PDFCS_INDEXED 10 +-#define PDFCS_PATTERN 11 +- +-class CPDF_Array; + class CPDF_Document; +-class CPDF_Object; ++class CPDF_IndexedCS; + class CPDF_PatternCS; + + constexpr size_t kMaxPatternColorComps = 16; +@@ -51,43 +42,60 @@ class PatternValue { + return {m_Comps.data(), m_Comps.size()}; + } + +- CPDF_Pattern* GetPattern() const { return m_pRetainedPattern.Get(); } +- void SetPattern(const RetainPtr& pPattern) { +- m_pRetainedPattern = pPattern; ++ RetainPtr GetPattern() const { return m_pRetainedPattern; } ++ void SetPattern(RetainPtr pPattern) { ++ m_pRetainedPattern = std::move(pPattern); + } + + private: + RetainPtr m_pRetainedPattern; +- std::array m_Comps; ++ std::array m_Comps{}; + }; + + class CPDF_ColorSpace : public Retainable, public Observable { + public: +- static RetainPtr GetStockCS(int Family); +- static RetainPtr ColorspaceFromName(const ByteString& name); +- static RetainPtr Load(CPDF_Document* pDoc, +- CPDF_Object* pObj); ++ enum class Family { ++ kUnknown = 0, ++ kDeviceGray = 1, ++ kDeviceRGB = 2, ++ kDeviceCMYK = 3, ++ kCalGray = 4, ++ kCalRGB = 5, ++ kLab = 6, ++ kICCBased = 7, ++ kSeparation = 8, ++ kDeviceN = 9, ++ kIndexed = 10, ++ kPattern = 11, ++ }; ++ ++ static RetainPtr GetStockCS(Family family); ++ static RetainPtr GetStockCSForName(const ByteString& name); + static RetainPtr Load( + CPDF_Document* pDoc, + const CPDF_Object* pObj, + std::set* pVisited); +- static uint32_t ComponentsForFamily(int family); +- static bool IsValidIccComponents(int components); + +- const CPDF_Array* GetArray() const { return m_pArray.Get(); } +- CPDF_Document* GetDocument() const { return m_pDocument.Get(); } ++ static RetainPtr AllocateColorSpaceForID( ++ CPDF_Document* pDocument, ++ uint32_t family_id); ++ ++ static uint32_t ComponentsForFamily(Family family); ++ static bool IsValidIccComponents(int components); + + // Should only be called if this colorspace is not a pattern. + std::vector CreateBufAndSetDefaultColor() const; + + uint32_t CountComponents() const; +- int GetFamily() const { return m_Family; } ++ Family GetFamily() const { return m_Family; } + bool IsSpecial() const { +- return GetFamily() == PDFCS_SEPARATION || GetFamily() == PDFCS_DEVICEN || +- GetFamily() == PDFCS_INDEXED || GetFamily() == PDFCS_PATTERN; ++ return GetFamily() == Family::kSeparation || ++ GetFamily() == Family::kDeviceN || GetFamily() == Family::kIndexed || ++ GetFamily() == Family::kPattern; + } + +- virtual bool GetRGB(const float* pBuf, ++ // Use CPDF_Pattern::GetPatternRGB() instead of GetRGB() for patterns. ++ virtual bool GetRGB(pdfium::span pBuf, + float* R, + float* G, + float* B) const = 0; +@@ -96,8 +104,9 @@ class CPDF_ColorSpace : public Retainable, public Observable { + float* value, + float* min, + float* max) const; +- virtual void TranslateImageLine(uint8_t* dest_buf, +- const uint8_t* src_buf, ++ ++ virtual void TranslateImageLine(pdfium::span dest_span, ++ pdfium::span src_span, + int pixels, + int image_width, + int image_height, +@@ -105,18 +114,14 @@ class CPDF_ColorSpace : public Retainable, public Observable { + virtual void EnableStdConversion(bool bEnabled); + virtual bool IsNormal() const; + +- // Returns |this| as a CPDF_PatternCS* if |this| is a pattern. +- virtual CPDF_PatternCS* AsPatternCS(); ++ // Returns `this` as a CPDF_PatternCS* if `this` is a pattern. + virtual const CPDF_PatternCS* AsPatternCS() const; + +- // Use instead of GetRGB() for patterns. +- virtual bool GetPatternRGB(const PatternValue& value, +- float* R, +- float* G, +- float* B) const; ++ // Returns `this` as a CPDF_IndexedCS* if `this` is indexed. ++ virtual const CPDF_IndexedCS* AsIndexedCS() const; + + protected: +- CPDF_ColorSpace(CPDF_Document* pDoc, int family); ++ explicit CPDF_ColorSpace(Family family); + ~CPDF_ColorSpace() override; + + // Returns the number of components, or 0 on failure. +@@ -128,13 +133,20 @@ class CPDF_ColorSpace : public Retainable, public Observable { + // components count. + void SetComponentsForStockCS(uint32_t nComponents); + +- UnownedPtr const m_pDocument; +- RetainPtr m_pArray; +- const int m_Family; +- uint32_t m_dwStdConversion = 0; ++ bool IsStdConversionEnabled() const { return m_dwStdConversion != 0; } ++ bool HasSameArray(const CPDF_Object* pObj) const { return m_pArray == pObj; } + + private: ++ friend class CPDF_CalGray_TranslateImageLine_Test; ++ friend class CPDF_CalRGB_TranslateImageLine_Test; ++ ++ static RetainPtr AllocateColorSpace( ++ ByteStringView bsFamilyName); ++ ++ const Family m_Family; ++ uint32_t m_dwStdConversion = 0; + uint32_t m_nComponents = 0; ++ RetainPtr m_pArray; + }; + + #endif // CORE_FPDFAPI_PAGE_CPDF_COLORSPACE_H_ +diff --git a/core/fpdfapi/page/cpdf_colorspace_unittest.cpp b/core/fpdfapi/page/cpdf_colorspace_unittest.cpp +new file mode 100644 +index 000000000..d0196feb9 +--- /dev/null ++++ b/core/fpdfapi/page/cpdf_colorspace_unittest.cpp +@@ -0,0 +1,52 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fpdfapi/page/cpdf_colorspace.h" ++ ++#include ++#include ++ ++#include "core/fxcrt/retain_ptr.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++TEST(CPDF_CalGray, TranslateImageLine) { ++ const uint8_t kSrc[12] = {255, 0, 0, 0, 255, 0, 0, 0, 255, 128, 128, 128}; ++ const uint8_t kExpect[12] = {255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0}; ++ ++ RetainPtr pCal = CPDF_ColorSpace::AllocateColorSpace("CalG"); ++ ASSERT_TRUE(pCal); ++ ++ uint8_t dst[12]; ++ memset(dst, 0xbd, sizeof(dst)); ++ pCal->TranslateImageLine(dst, kSrc, 4, 4, 1, true); ++ for (size_t i = 0; i < 12; ++i) ++ EXPECT_EQ(dst[i], kExpect[i]) << " at " << i; ++ ++ memset(dst, 0xbd, sizeof(dst)); ++ pCal->TranslateImageLine(dst, kSrc, 4, 4, 1, false); ++ for (size_t i = 0; i < 12; ++i) ++ EXPECT_EQ(dst[i], kExpect[i]) << " at " << i; ++} ++ ++TEST(CPDF_CalRGB, TranslateImageLine) { ++ const uint8_t kSrc[12] = {255, 0, 0, 0, 255, 0, 0, 0, 255, 128, 128, 128}; ++ const uint8_t kExpectMask[12] = {255, 58, 0, 0, 255, 0, ++ 70, 0, 255, 188, 188, 188}; ++ const uint8_t kExpectNomask[12] = {0, 0, 255, 0, 255, 0, ++ 255, 0, 0, 128, 128, 128}; ++ ++ RetainPtr pCal = CPDF_ColorSpace::AllocateColorSpace("CalR"); ++ ASSERT_TRUE(pCal); ++ ++ uint8_t dst[12]; ++ memset(dst, 0xbd, sizeof(dst)); ++ pCal->TranslateImageLine(dst, kSrc, 4, 4, 1, true); ++ for (size_t i = 0; i < 12; ++i) ++ EXPECT_EQ(dst[i], kExpectMask[i]) << " at " << i; ++ ++ memset(dst, 0xbd, sizeof(dst)); ++ pCal->TranslateImageLine(dst, kSrc, 4, 4, 1, false); ++ for (size_t i = 0; i < 12; ++i) ++ EXPECT_EQ(dst[i], kExpectNomask[i]) << " at " << i; ++} +diff --git a/core/fpdfapi/page/cpdf_colorstate.cpp b/core/fpdfapi/page/cpdf_colorstate.cpp +index 50dbb7174..a03fd437a 100644 +--- a/core/fpdfapi/page/cpdf_colorstate.cpp ++++ b/core/fpdfapi/page/cpdf_colorstate.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,17 +6,20 @@ + + #include "core/fpdfapi/page/cpdf_colorstate.h" + ++#include ++ + #include "core/fpdfapi/page/cpdf_colorspace.h" + #include "core/fpdfapi/page/cpdf_pattern.h" + #include "core/fpdfapi/page/cpdf_tilingpattern.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/check.h" + +-CPDF_ColorState::CPDF_ColorState() {} ++CPDF_ColorState::CPDF_ColorState() = default; + + CPDF_ColorState::CPDF_ColorState(const CPDF_ColorState& that) + : m_Ref(that.m_Ref) {} + +-CPDF_ColorState::~CPDF_ColorState() {} ++CPDF_ColorState::~CPDF_ColorState() = default; + + void CPDF_ColorState::Emplace() { + m_Ref.Emplace(); +@@ -70,72 +73,74 @@ bool CPDF_ColorState::HasStrokeColor() const { + return pColor && !pColor->IsNull(); + } + +-void CPDF_ColorState::SetFillColor(const RetainPtr& pCS, +- const std::vector& values) { ++void CPDF_ColorState::SetFillColor(RetainPtr colorspace, ++ std::vector values) { + ColorData* pData = m_Ref.GetPrivateCopy(); +- SetColor(pCS, values, &pData->m_FillColor, &pData->m_FillColorRef); ++ SetColor(std::move(colorspace), std::move(values), &pData->m_FillColor, ++ &pData->m_FillColorRef); + } + +-void CPDF_ColorState::SetStrokeColor(const RetainPtr& pCS, +- const std::vector& values) { ++void CPDF_ColorState::SetStrokeColor(RetainPtr colorspace, ++ std::vector values) { + ColorData* pData = m_Ref.GetPrivateCopy(); +- SetColor(pCS, values, &pData->m_StrokeColor, &pData->m_StrokeColorRef); ++ SetColor(std::move(colorspace), std::move(values), &pData->m_StrokeColor, ++ &pData->m_StrokeColorRef); + } + +-void CPDF_ColorState::SetFillPattern(const RetainPtr& pPattern, +- const std::vector& values) { ++void CPDF_ColorState::SetFillPattern(RetainPtr pattern, ++ pdfium::span values) { + ColorData* pData = m_Ref.GetPrivateCopy(); +- SetPattern(pPattern, values, &pData->m_FillColor, &pData->m_FillColorRef); ++ SetPattern(std::move(pattern), values, &pData->m_FillColor, ++ &pData->m_FillColorRef); + } + +-void CPDF_ColorState::SetStrokePattern(const RetainPtr& pPattern, +- const std::vector& values) { ++void CPDF_ColorState::SetStrokePattern(RetainPtr pattern, ++ pdfium::span values) { + ColorData* pData = m_Ref.GetPrivateCopy(); +- SetPattern(pPattern, values, &pData->m_StrokeColor, &pData->m_StrokeColorRef); ++ SetPattern(std::move(pattern), values, &pData->m_StrokeColor, ++ &pData->m_StrokeColorRef); + } + +-void CPDF_ColorState::SetColor(const RetainPtr& pCS, +- const std::vector& values, ++void CPDF_ColorState::SetColor(RetainPtr colorspace, ++ std::vector values, + CPDF_Color* color, + FX_COLORREF* colorref) { +- ASSERT(color); +- ASSERT(colorref); +- +- if (pCS) +- color->SetColorSpace(pCS); +- else if (color->IsNull()) +- color->SetColorSpace(CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY)); +- ++ DCHECK(color); ++ DCHECK(colorref); ++ ++ if (colorspace) { ++ color->SetColorSpace(std::move(colorspace)); ++ } else if (color->IsNull()) { ++ color->SetColorSpace( ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray)); ++ } + if (color->CountComponents() > values.size()) + return; + + if (!color->IsPattern()) +- color->SetValueForNonPattern(values); ++ color->SetValueForNonPattern(std::move(values)); + int R; + int G; + int B; + *colorref = color->GetRGB(&R, &G, &B) ? FXSYS_BGR(B, G, R) : 0xFFFFFFFF; + } + +-void CPDF_ColorState::SetPattern(const RetainPtr& pPattern, +- const std::vector& values, ++void CPDF_ColorState::SetPattern(RetainPtr pattern, ++ pdfium::span values, + CPDF_Color* color, + FX_COLORREF* colorref) { +- ASSERT(color); +- ASSERT(colorref); +- +- color->SetValueForPattern(pPattern, values); ++ DCHECK(color); ++ DCHECK(colorref); ++ color->SetValueForPattern(pattern, values); + int R; + int G; + int B; +- bool ret = color->GetRGB(&R, &G, &B); +- if (CPDF_TilingPattern* pTilingPattern = pPattern->AsTilingPattern()) { +- if (!ret && pTilingPattern->colored()) { +- *colorref = 0x00BFBFBF; +- return; +- } ++ if (color->GetRGB(&R, &G, &B)) { ++ *colorref = FXSYS_BGR(B, G, R); ++ return; + } +- *colorref = ret ? FXSYS_BGR(B, G, R) : 0xFFFFFFFF; ++ CPDF_TilingPattern* tiling = pattern->AsTilingPattern(); ++ *colorref = tiling && tiling->colored() ? 0x00BFBFBF : 0xFFFFFFFF; + } + + CPDF_ColorState::ColorData::ColorData() = default; +@@ -151,8 +156,10 @@ CPDF_ColorState::ColorData::~ColorData() = default; + void CPDF_ColorState::ColorData::SetDefault() { + m_FillColorRef = 0; + m_StrokeColorRef = 0; +- m_FillColor.SetColorSpace(CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY)); +- m_StrokeColor.SetColorSpace(CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY)); ++ m_FillColor.SetColorSpace( ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray)); ++ m_StrokeColor.SetColorSpace( ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray)); + } + + RetainPtr CPDF_ColorState::ColorData::Clone() +diff --git a/core/fpdfapi/page/cpdf_colorstate.h b/core/fpdfapi/page/cpdf_colorstate.h +index f0f6ebdd0..ec2de6b48 100644 +--- a/core/fpdfapi/page/cpdf_colorstate.h ++++ b/core/fpdfapi/page/cpdf_colorstate.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,12 +10,11 @@ + #include + + #include "core/fpdfapi/page/cpdf_color.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/shared_copy_on_write.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/span.h" + +-class CPDF_Color; + class CPDF_ColorSpace; + class CPDF_Pattern; + +@@ -42,22 +41,21 @@ class CPDF_ColorState { + CPDF_Color* GetMutableStrokeColor(); + bool HasStrokeColor() const; + +- void SetFillColor(const RetainPtr& pCS, +- const std::vector& values); +- void SetStrokeColor(const RetainPtr& pCS, +- const std::vector& values); +- void SetFillPattern(const RetainPtr& pattern, +- const std::vector& values); +- void SetStrokePattern(const RetainPtr& pattern, +- const std::vector& values); ++ void SetFillColor(RetainPtr colorspace, ++ std::vector values); ++ void SetStrokeColor(RetainPtr colorspace, ++ std::vector values); ++ void SetFillPattern(RetainPtr pattern, ++ pdfium::span values); ++ void SetStrokePattern(RetainPtr pattern, ++ pdfium::span values); + + bool HasRef() const { return !!m_Ref; } + + private: + class ColorData final : public Retainable { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + RetainPtr Clone() const; + +@@ -74,12 +72,12 @@ class CPDF_ColorState { + ~ColorData() override; + }; + +- void SetColor(const RetainPtr& pCS, +- const std::vector& values, ++ void SetColor(RetainPtr colorspace, ++ std::vector values, + CPDF_Color* color, + FX_COLORREF* colorref); +- void SetPattern(const RetainPtr& pPattern, +- const std::vector& values, ++ void SetPattern(RetainPtr pattern, ++ pdfium::span values, + CPDF_Color* color, + FX_COLORREF* colorref); + +diff --git a/core/fpdfapi/page/cpdf_contentmarkitem.cpp b/core/fpdfapi/page/cpdf_contentmarkitem.cpp +index a361e5b87..6b5cd6242 100644 +--- a/core/fpdfapi/page/cpdf_contentmarkitem.cpp ++++ b/core/fpdfapi/page/cpdf_contentmarkitem.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,28 +13,23 @@ + CPDF_ContentMarkItem::CPDF_ContentMarkItem(ByteString name) + : m_MarkName(std::move(name)) {} + +-CPDF_ContentMarkItem::~CPDF_ContentMarkItem() {} ++CPDF_ContentMarkItem::~CPDF_ContentMarkItem() = default; + +-const CPDF_Dictionary* CPDF_ContentMarkItem::GetParam() const { ++RetainPtr CPDF_ContentMarkItem::GetParam() const { + switch (m_ParamType) { + case kPropertiesDict: + return m_pPropertiesHolder->GetDictFor(m_PropertyName); + case kDirectDict: +- return m_pDirectDict.Get(); ++ return m_pDirectDict; + case kNone: + default: + return nullptr; + } + } + +-CPDF_Dictionary* CPDF_ContentMarkItem::GetParam() { +- return const_cast( +- static_cast(this)->GetParam()); +-} +- +-bool CPDF_ContentMarkItem::HasMCID() const { +- const CPDF_Dictionary* pDict = GetParam(); +- return pDict && pDict->KeyExist("MCID"); ++RetainPtr CPDF_ContentMarkItem::GetParam() { ++ return pdfium::WrapRetain( ++ const_cast(std::as_const(*this).GetParam().Get())); + } + + void CPDF_ContentMarkItem::SetDirectDict(RetainPtr pDict) { +@@ -43,9 +38,9 @@ void CPDF_ContentMarkItem::SetDirectDict(RetainPtr pDict) { + } + + void CPDF_ContentMarkItem::SetPropertiesHolder( +- CPDF_Dictionary* pHolder, ++ RetainPtr pHolder, + const ByteString& property_name) { + m_ParamType = kPropertiesDict; +- m_pPropertiesHolder.Reset(pHolder); ++ m_pPropertiesHolder = std::move(pHolder); + m_PropertyName = property_name; + } +diff --git a/core/fpdfapi/page/cpdf_contentmarkitem.h b/core/fpdfapi/page/cpdf_contentmarkitem.h +index 15a34e9d6..f418aab81 100644 +--- a/core/fpdfapi/page/cpdf_contentmarkitem.h ++++ b/core/fpdfapi/page/cpdf_contentmarkitem.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,7 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_CONTENTMARKITEM_H_ + #define CORE_FPDFAPI_PAGE_CPDF_CONTENTMARKITEM_H_ + +-#include +- +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/retain_ptr.h" + + class CPDF_Dictionary; +@@ -24,13 +21,12 @@ class CPDF_ContentMarkItem final : public Retainable { + + const ByteString& GetName() const { return m_MarkName; } + ParamType GetParamType() const { return m_ParamType; } +- const CPDF_Dictionary* GetParam() const; +- CPDF_Dictionary* GetParam(); ++ RetainPtr GetParam() const; ++ RetainPtr GetParam(); + const ByteString& GetPropertyName() const { return m_PropertyName; } +- bool HasMCID() const; + + void SetDirectDict(RetainPtr pDict); +- void SetPropertiesHolder(CPDF_Dictionary* pHolder, ++ void SetPropertiesHolder(RetainPtr pHolder, + const ByteString& property_name); + + private: +diff --git a/core/fpdfapi/page/cpdf_contentmarks.cpp b/core/fpdfapi/page/cpdf_contentmarks.cpp +index 0b95ac52b..4cb439e2d 100644 +--- a/core/fpdfapi/page/cpdf_contentmarks.cpp ++++ b/core/fpdfapi/page/cpdf_contentmarks.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,14 +10,14 @@ + #include + + #include "core/fpdfapi/parser/cpdf_dictionary.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check_op.h" + +-CPDF_ContentMarks::CPDF_ContentMarks() {} ++CPDF_ContentMarks::CPDF_ContentMarks() = default; + +-CPDF_ContentMarks::~CPDF_ContentMarks() {} ++CPDF_ContentMarks::~CPDF_ContentMarks() = default; + + std::unique_ptr CPDF_ContentMarks::Clone() { +- auto result = pdfium::MakeUnique(); ++ auto result = std::make_unique(); + if (m_pMarkData) + result->m_pMarkData = pdfium::MakeRetain(*m_pMarkData); + return result; +@@ -32,12 +32,10 @@ bool CPDF_ContentMarks::ContainsItem(const CPDF_ContentMarkItem* pItem) const { + } + + CPDF_ContentMarkItem* CPDF_ContentMarks::GetItem(size_t index) { +- return const_cast( +- static_cast(this)->GetItem(index)); ++ return m_pMarkData->GetItem(index); + } + + const CPDF_ContentMarkItem* CPDF_ContentMarks::GetItem(size_t index) const { +- ASSERT(index < CountItems()); + return m_pMarkData->GetItem(index); + } + +@@ -50,18 +48,20 @@ void CPDF_ContentMarks::AddMark(ByteString name) { + m_pMarkData->AddMark(std::move(name)); + } + +-void CPDF_ContentMarks::AddMarkWithDirectDict(ByteString name, +- CPDF_Dictionary* pDict) { ++void CPDF_ContentMarks::AddMarkWithDirectDict( ++ ByteString name, ++ RetainPtr pDict) { + EnsureMarkDataExists(); +- m_pMarkData->AddMarkWithDirectDict(std::move(name), pDict); ++ m_pMarkData->AddMarkWithDirectDict(std::move(name), std::move(pDict)); + } + + void CPDF_ContentMarks::AddMarkWithPropertiesHolder( + const ByteString& name, +- CPDF_Dictionary* pDict, ++ RetainPtr pDict, + const ByteString& property_name) { + EnsureMarkDataExists(); +- m_pMarkData->AddMarkWithPropertiesHolder(name, pDict, property_name); ++ m_pMarkData->AddMarkWithPropertiesHolder(name, std::move(pDict), ++ property_name); + } + + bool CPDF_ContentMarks::RemoveMark(CPDF_ContentMarkItem* pMarkItem) { +@@ -73,15 +73,6 @@ void CPDF_ContentMarks::EnsureMarkDataExists() { + m_pMarkData = pdfium::MakeRetain(); + } + +-void CPDF_ContentMarks::DeleteLastMark() { +- if (!m_pMarkData) +- return; +- +- m_pMarkData->DeleteLastMark(); +- if (CountItems() == 0) +- m_pMarkData.Reset(); +-} +- + size_t CPDF_ContentMarks::FindFirstDifference( + const CPDF_ContentMarks* other) const { + if (m_pMarkData == other->m_pMarkData) +@@ -96,12 +87,12 @@ size_t CPDF_ContentMarks::FindFirstDifference( + return min_len; + } + +-CPDF_ContentMarks::MarkData::MarkData() {} ++CPDF_ContentMarks::MarkData::MarkData() = default; + + CPDF_ContentMarks::MarkData::MarkData(const MarkData& src) + : m_Marks(src.m_Marks) {} + +-CPDF_ContentMarks::MarkData::~MarkData() {} ++CPDF_ContentMarks::MarkData::~MarkData() = default; + + size_t CPDF_ContentMarks::MarkData::CountItems() const { + return m_Marks.size(); +@@ -109,7 +100,7 @@ size_t CPDF_ContentMarks::MarkData::CountItems() const { + + bool CPDF_ContentMarks::MarkData::ContainsItem( + const CPDF_ContentMarkItem* pItem) const { +- for (const auto pMark : m_Marks) { ++ for (const auto& pMark : m_Marks) { + if (pMark == pItem) + return true; + } +@@ -117,17 +108,19 @@ bool CPDF_ContentMarks::MarkData::ContainsItem( + } + + CPDF_ContentMarkItem* CPDF_ContentMarks::MarkData::GetItem(size_t index) { ++ CHECK_LT(index, m_Marks.size()); + return m_Marks[index].Get(); + } + + const CPDF_ContentMarkItem* CPDF_ContentMarks::MarkData::GetItem( + size_t index) const { ++ CHECK_LT(index, m_Marks.size()); + return m_Marks[index].Get(); + } + + int CPDF_ContentMarks::MarkData::GetMarkedContentID() const { +- for (const auto pMark : m_Marks) { +- const CPDF_Dictionary* pDict = pMark->GetParam(); ++ for (const auto& pMark : m_Marks) { ++ RetainPtr pDict = pMark->GetParam(); + if (pDict && pDict->KeyExist("MCID")) + return pDict->GetIntegerFor("MCID"); + } +@@ -141,7 +134,7 @@ void CPDF_ContentMarks::MarkData::AddMark(ByteString name) { + + void CPDF_ContentMarks::MarkData::AddMarkWithDirectDict( + ByteString name, +- CPDF_Dictionary* pDict) { ++ RetainPtr pDict) { + auto pItem = pdfium::MakeRetain(std::move(name)); + pItem->SetDirectDict(ToDictionary(pDict->Clone())); + m_Marks.push_back(pItem); +@@ -149,11 +142,11 @@ void CPDF_ContentMarks::MarkData::AddMarkWithDirectDict( + + void CPDF_ContentMarks::MarkData::AddMarkWithPropertiesHolder( + const ByteString& name, +- CPDF_Dictionary* pDict, ++ RetainPtr pDict, + const ByteString& property_name) { + auto pItem = pdfium::MakeRetain(name); +- pItem->SetPropertiesHolder(pDict, property_name); +- m_Marks.push_back(pItem); ++ pItem->SetPropertiesHolder(std::move(pDict), property_name); ++ m_Marks.push_back(std::move(pItem)); + } + + bool CPDF_ContentMarks::MarkData::RemoveMark(CPDF_ContentMarkItem* pMarkItem) { +@@ -165,8 +158,3 @@ bool CPDF_ContentMarks::MarkData::RemoveMark(CPDF_ContentMarkItem* pMarkItem) { + } + return false; + } +- +-void CPDF_ContentMarks::MarkData::DeleteLastMark() { +- if (!m_Marks.empty()) +- m_Marks.pop_back(); +-} +diff --git a/core/fpdfapi/page/cpdf_contentmarks.h b/core/fpdfapi/page/cpdf_contentmarks.h +index 7bb25ecb3..95eaeea57 100644 +--- a/core/fpdfapi/page/cpdf_contentmarks.h ++++ b/core/fpdfapi/page/cpdf_contentmarks.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,12 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_CONTENTMARKS_H_ + #define CORE_FPDFAPI_PAGE_CPDF_CONTENTMARKS_H_ + ++#include ++ + #include + #include + + #include "core/fpdfapi/page/cpdf_contentmarkitem.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" + + class CPDF_Dictionary; +@@ -31,12 +32,11 @@ class CPDF_ContentMarks { + const CPDF_ContentMarkItem* GetItem(size_t index) const; + + void AddMark(ByteString name); +- void AddMarkWithDirectDict(ByteString name, CPDF_Dictionary* pDict); ++ void AddMarkWithDirectDict(ByteString name, RetainPtr pDict); + void AddMarkWithPropertiesHolder(const ByteString& name, +- CPDF_Dictionary* pDict, ++ RetainPtr pDict, + const ByteString& property_name); + bool RemoveMark(CPDF_ContentMarkItem* pMarkItem); +- void DeleteLastMark(); + size_t FindFirstDifference(const CPDF_ContentMarks* other) const; + + private: +@@ -53,12 +53,12 @@ class CPDF_ContentMarks { + + int GetMarkedContentID() const; + void AddMark(ByteString name); +- void AddMarkWithDirectDict(ByteString name, CPDF_Dictionary* pDict); ++ void AddMarkWithDirectDict(ByteString name, ++ RetainPtr pDict); + void AddMarkWithPropertiesHolder(const ByteString& name, +- CPDF_Dictionary* pDict, ++ RetainPtr pDict, + const ByteString& property_name); + bool RemoveMark(CPDF_ContentMarkItem* pMarkItem); +- void DeleteLastMark(); + + private: + std::vector> m_Marks; +diff --git a/core/fpdfapi/page/cpdf_contentparser.cpp b/core/fpdfapi/page/cpdf_contentparser.cpp +index 5b3efe1c7..5ef9d80d1 100644 +--- a/core/fpdfapi/page/cpdf_contentparser.cpp ++++ b/core/fpdfapi/page/cpdf_contentparser.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,10 +6,11 @@ + + #include "core/fpdfapi/page/cpdf_contentparser.h" + ++#include ++ + #include "constants/page_object.h" + #include "core/fpdfapi/font/cpdf_type3char.h" + #include "core/fpdfapi/page/cpdf_allstates.h" +-#include "core/fpdfapi/page/cpdf_form.h" + #include "core/fpdfapi/page/cpdf_page.h" + #include "core/fpdfapi/page/cpdf_pageobject.h" + #include "core/fpdfapi/page/cpdf_path.h" +@@ -17,53 +18,61 @@ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" ++#include "core/fxcrt/fixed_try_alloc_zeroed_data_vector.h" + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxcrt/pauseindicator_iface.h" +-#include "core/fxge/render_defines.h" +-#include "third_party/base/ptr_util.h" ++#include "core/fxcrt/span_util.h" ++#include "core/fxcrt/stl_util.h" ++#include "core/fxge/cfx_fillrenderoptions.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" + + CPDF_ContentParser::CPDF_ContentParser(CPDF_Page* pPage) +- : m_CurrentStage(Stage::kGetContent), m_pObjectHolder(pPage) { +- ASSERT(pPage); ++ : m_CurrentStage(Stage::kGetContent), m_pPageObjectHolder(pPage) { ++ DCHECK(pPage); + if (!pPage->GetDocument()) { + m_CurrentStage = Stage::kComplete; + return; + } + +- CPDF_Object* pContent = +- pPage->GetDict()->GetDirectObjectFor(pdfium::page_object::kContents); ++ RetainPtr pContent = ++ pPage->GetMutableDict()->GetMutableDirectObjectFor( ++ pdfium::page_object::kContents); + if (!pContent) { + HandlePageContentFailure(); + return; + } + +- CPDF_Stream* pStream = pContent->AsStream(); ++ const CPDF_Stream* pStream = pContent->AsStream(); + if (pStream) { + HandlePageContentStream(pStream); + return; + } + +- CPDF_Array* pArray = pContent->AsArray(); ++ const CPDF_Array* pArray = pContent->AsArray(); + if (pArray && HandlePageContentArray(pArray)) + return; + + HandlePageContentFailure(); + } + +-CPDF_ContentParser::CPDF_ContentParser(CPDF_Form* pForm, ++CPDF_ContentParser::CPDF_ContentParser(RetainPtr pStream, ++ CPDF_PageObjectHolder* pPageObjectHolder, + const CPDF_AllStates* pGraphicStates, + const CFX_Matrix* pParentMatrix, + CPDF_Type3Char* pType3Char, + std::set* pParsedSet) + : m_CurrentStage(Stage::kParse), +- m_pObjectHolder(pForm), ++ m_pPageObjectHolder(pPageObjectHolder), + m_pType3Char(pType3Char) { +- ASSERT(pForm); +- CFX_Matrix form_matrix = pForm->GetDict()->GetMatrixFor("Matrix"); ++ DCHECK(m_pPageObjectHolder); ++ CFX_Matrix form_matrix = ++ m_pPageObjectHolder->GetDict()->GetMatrixFor("Matrix"); + if (pGraphicStates) + form_matrix.Concat(pGraphicStates->m_CTM); + +- CPDF_Array* pBBox = pForm->GetDict()->GetArrayFor("BBox"); ++ RetainPtr pBBox = ++ m_pPageObjectHolder->GetDict()->GetArrayFor("BBox"); + CFX_FloatRect form_bbox; + CPDF_Path ClipPath; + if (pBBox) { +@@ -79,31 +88,33 @@ CPDF_ContentParser::CPDF_ContentParser(CPDF_Form* pForm, + form_bbox = pParentMatrix->TransformRect(form_bbox); + } + +- CPDF_Dictionary* pResources = pForm->GetDict()->GetDictFor("Resources"); +- m_pParser = pdfium::MakeUnique( +- pForm->GetDocument(), pForm->m_pPageResources.Get(), +- pForm->m_pResources.Get(), pParentMatrix, pForm, pResources, form_bbox, +- pGraphicStates, pParsedSet); ++ RetainPtr pResources = ++ m_pPageObjectHolder->GetMutableDict()->GetMutableDictFor("Resources"); ++ m_pParser = std::make_unique( ++ m_pPageObjectHolder->GetDocument(), ++ m_pPageObjectHolder->GetMutablePageResources(), ++ m_pPageObjectHolder->GetMutableResources(), pParentMatrix, ++ m_pPageObjectHolder, std::move(pResources), form_bbox, pGraphicStates, ++ pParsedSet); + m_pParser->GetCurStates()->m_CTM = form_matrix; + m_pParser->GetCurStates()->m_ParentMatrix = form_matrix; + if (ClipPath.HasRef()) { +- m_pParser->GetCurStates()->m_ClipPath.AppendPath(ClipPath, FXFILL_WINDING, +- true); ++ m_pParser->GetCurStates()->m_ClipPath.AppendPathWithAutoMerge( ++ ClipPath, CFX_FillRenderOptions::FillType::kWinding); + } +- if (pForm->GetTransparency().IsGroup()) { ++ if (m_pPageObjectHolder->GetTransparency().IsGroup()) { + CPDF_GeneralState* pState = &m_pParser->GetCurStates()->m_GeneralState; + pState->SetBlendType(BlendMode::kNormal); + pState->SetStrokeAlpha(1.0f); + pState->SetFillAlpha(1.0f); + pState->SetSoftMask(nullptr); + } +- m_pSingleStream = pdfium::MakeRetain(pForm->GetStream()); ++ m_pSingleStream = pdfium::MakeRetain(std::move(pStream)); + m_pSingleStream->LoadAllDataFiltered(); +- m_pData.Reset(m_pSingleStream->GetData()); +- m_Size = m_pSingleStream->GetSize(); ++ m_Data = m_pSingleStream->GetSpan(); + } + +-CPDF_ContentParser::~CPDF_ContentParser() {} ++CPDF_ContentParser::~CPDF_ContentParser() = default; + + // Returning |true| means that there is more content to be processed and + // Continue() should be called again. Returning |false| means that we've +@@ -127,19 +138,20 @@ bool CPDF_ContentParser::Continue(PauseIndicatorIface* pPause) { + if (m_CurrentStage == Stage::kCheckClip) + m_CurrentStage = CheckClip(); + +- ASSERT(m_CurrentStage == Stage::kComplete); ++ DCHECK_EQ(m_CurrentStage, Stage::kComplete); + return false; + } + + CPDF_ContentParser::Stage CPDF_ContentParser::GetContent() { +- ASSERT(m_CurrentStage == Stage::kGetContent); +- ASSERT(m_pObjectHolder->IsPage()); +- CPDF_Array* pContent = +- m_pObjectHolder->GetDict()->GetArrayFor(pdfium::page_object::kContents); +- CPDF_Stream* pStreamObj = ToStream( ++ DCHECK_EQ(m_CurrentStage, Stage::kGetContent); ++ DCHECK(m_pPageObjectHolder->IsPage()); ++ RetainPtr pContent = ++ m_pPageObjectHolder->GetDict()->GetArrayFor( ++ pdfium::page_object::kContents); ++ RetainPtr pStreamObj = ToStream( + pContent ? pContent->GetDirectObjectAt(m_CurrentOffset) : nullptr); + m_StreamArray[m_CurrentOffset] = +- pdfium::MakeRetain(pStreamObj); ++ pdfium::MakeRetain(std::move(pStreamObj)); + m_StreamArray[m_CurrentOffset]->LoadAllDataFiltered(); + m_CurrentOffset++; + +@@ -151,54 +163,56 @@ CPDF_ContentParser::Stage CPDF_ContentParser::PrepareContent() { + m_CurrentOffset = 0; + + if (m_StreamArray.empty()) { +- m_pData.Reset(m_pSingleStream->GetData()); +- m_Size = m_pSingleStream->GetSize(); ++ m_Data = m_pSingleStream->GetSpan(); + return Stage::kParse; + } + +- FX_SAFE_UINT32 safeSize = 0; ++ FX_SAFE_UINT32 safe_size = 0; + for (const auto& stream : m_StreamArray) { +- m_StreamSegmentOffsets.push_back(safeSize.ValueOrDie()); +- +- safeSize += stream->GetSize(); +- safeSize += 1; +- if (!safeSize.IsValid()) ++ m_StreamSegmentOffsets.push_back(safe_size.ValueOrDie()); ++ safe_size += stream->GetSize(); ++ safe_size += 1; ++ if (!safe_size.IsValid()) + return Stage::kComplete; + } + +- m_Size = safeSize.ValueOrDie(); +- m_pData.Reset( +- std::unique_ptr(FX_Alloc(uint8_t, m_Size))); ++ const size_t buffer_size = safe_size.ValueOrDie(); ++ FixedTryAllocZeroedDataVector buffer(buffer_size); ++ if (buffer.empty()) { ++ m_Data.emplace>(); ++ return Stage::kComplete; ++ } + +- uint32_t pos = 0; ++ size_t pos = 0; ++ auto data_span = buffer.writable_span(); + for (const auto& stream : m_StreamArray) { +- memcpy(m_pData.Get() + pos, stream->GetData(), stream->GetSize()); ++ fxcrt::spancpy(data_span.subspan(pos), stream->GetSpan()); + pos += stream->GetSize(); +- m_pData.Get()[pos++] = ' '; ++ data_span[pos++] = ' '; + } + m_StreamArray.clear(); +- ++ m_Data = std::move(buffer); + return Stage::kParse; + } + + CPDF_ContentParser::Stage CPDF_ContentParser::Parse() { + if (!m_pParser) { +- m_pParsedSet = pdfium::MakeUnique>(); +- m_pParser = pdfium::MakeUnique( +- m_pObjectHolder->GetDocument(), m_pObjectHolder->m_pPageResources.Get(), +- nullptr, nullptr, m_pObjectHolder.Get(), +- m_pObjectHolder->m_pResources.Get(), m_pObjectHolder->GetBBox(), +- nullptr, m_pParsedSet.get()); ++ m_ParsedSet.clear(); ++ m_pParser = std::make_unique( ++ m_pPageObjectHolder->GetDocument(), ++ m_pPageObjectHolder->GetMutablePageResources(), nullptr, nullptr, ++ m_pPageObjectHolder, m_pPageObjectHolder->GetMutableResources(), ++ m_pPageObjectHolder->GetBBox(), nullptr, &m_ParsedSet); + m_pParser->GetCurStates()->m_ColorState.SetDefault(); + } +- if (m_CurrentOffset >= m_Size) ++ if (m_CurrentOffset >= GetData().size()) + return Stage::kCheckClip; + + if (m_StreamSegmentOffsets.empty()) + m_StreamSegmentOffsets.push_back(0); + + static constexpr uint32_t kParseStepLimit = 100; +- m_CurrentOffset += m_pParser->Parse(m_pData.Get(), m_Size, m_CurrentOffset, ++ m_CurrentOffset += m_pParser->Parse(GetData(), m_CurrentOffset, + kParseStepLimit, m_StreamSegmentOffsets); + return Stage::kParse; + } +@@ -209,7 +223,7 @@ CPDF_ContentParser::Stage CPDF_ContentParser::CheckClip() { + m_pParser->GetType3Data()); + } + +- for (auto& pObj : *m_pObjectHolder) { ++ for (auto& pObj : *m_pPageObjectHolder) { + if (!pObj->m_ClipPath.HasRef()) + continue; + if (pObj->m_ClipPath.GetPathCount() != 1) +@@ -230,14 +244,15 @@ CPDF_ContentParser::Stage CPDF_ContentParser::CheckClip() { + return Stage::kComplete; + } + +-void CPDF_ContentParser::HandlePageContentStream(CPDF_Stream* pStream) { +- m_pSingleStream = pdfium::MakeRetain(pStream); ++void CPDF_ContentParser::HandlePageContentStream(const CPDF_Stream* pStream) { ++ m_pSingleStream = ++ pdfium::MakeRetain(pdfium::WrapRetain(pStream)); + m_pSingleStream->LoadAllDataFiltered(); + m_CurrentStage = Stage::kPrepareContent; + } + +-bool CPDF_ContentParser::HandlePageContentArray(CPDF_Array* pArray) { +- m_nStreams = pArray->size(); ++bool CPDF_ContentParser::HandlePageContentArray(const CPDF_Array* pArray) { ++ m_nStreams = fxcrt::CollectionSize(*pArray); + if (m_nStreams == 0) + return false; + +@@ -248,3 +263,9 @@ bool CPDF_ContentParser::HandlePageContentArray(CPDF_Array* pArray) { + void CPDF_ContentParser::HandlePageContentFailure() { + m_CurrentStage = Stage::kComplete; + } ++ ++pdfium::span CPDF_ContentParser::GetData() const { ++ if (is_owned()) ++ return absl::get>(m_Data).span(); ++ return absl::get>(m_Data); ++} +diff --git a/core/fpdfapi/page/cpdf_contentparser.h b/core/fpdfapi/page/cpdf_contentparser.h +index 2f9a44b55..090c6240a 100644 +--- a/core/fpdfapi/page/cpdf_contentparser.h ++++ b/core/fpdfapi/page/cpdf_contentparser.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,18 +7,20 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_CONTENTPARSER_H_ + #define CORE_FPDFAPI_PAGE_CPDF_CONTENTPARSER_H_ + ++#include ++ + #include + #include + #include + + #include "core/fpdfapi/page/cpdf_streamcontentparser.h" +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxcrt/maybe_owned.h" ++#include "core/fxcrt/fixed_try_alloc_zeroed_data_vector.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "third_party/abseil-cpp/absl/types/variant.h" + + class CPDF_AllStates; + class CPDF_Array; +-class CPDF_Form; + class CPDF_Page; + class CPDF_PageObjectHolder; + class CPDF_Stream; +@@ -29,7 +31,8 @@ class PauseIndicatorIface; + class CPDF_ContentParser { + public: + explicit CPDF_ContentParser(CPDF_Page* pPage); +- CPDF_ContentParser(CPDF_Form* pForm, ++ CPDF_ContentParser(RetainPtr pStream, ++ CPDF_PageObjectHolder* pPageObjectHolder, + const CPDF_AllStates* pGraphicStates, + const CFX_Matrix* pParentMatrix, + CPDF_Type3Char* pType3Char, +@@ -56,26 +59,30 @@ class CPDF_ContentParser { + Stage Parse(); + Stage CheckClip(); + +- void HandlePageContentStream(CPDF_Stream* pStream); +- bool HandlePageContentArray(CPDF_Array* pArray); ++ void HandlePageContentStream(const CPDF_Stream* pStream); ++ bool HandlePageContentArray(const CPDF_Array* pArray); + void HandlePageContentFailure(); + ++ bool is_owned() const { ++ return absl::holds_alternative>( ++ m_Data); ++ } ++ pdfium::span GetData() const; ++ + Stage m_CurrentStage; +- UnownedPtr const m_pObjectHolder; ++ UnownedPtr const m_pPageObjectHolder; + UnownedPtr m_pType3Char; // Only used when parsing forms. + RetainPtr m_pSingleStream; + std::vector> m_StreamArray; + std::vector m_StreamSegmentOffsets; +- MaybeOwned m_pData; ++ absl::variant, ++ FixedTryAllocZeroedDataVector> ++ m_Data; + uint32_t m_nStreams = 0; +- uint32_t m_Size = 0; + uint32_t m_CurrentOffset = 0; ++ std::set m_ParsedSet; // Only used when parsing pages. + +- // Only used when parsing pages. +- std::unique_ptr> m_pParsedSet; +- +- // |m_pParser| has a reference to |m_pParsedSet|, so must be below and thus +- // destroyed first. ++ // Must not outlive |m_pParsedSet|. + std::unique_ptr m_pParser; + }; + +diff --git a/core/fpdfapi/page/cpdf_devicecs.cpp b/core/fpdfapi/page/cpdf_devicecs.cpp +index e55751619..5590c5b17 100644 +--- a/core/fpdfapi/page/cpdf_devicecs.cpp ++++ b/core/fpdfapi/page/cpdf_devicecs.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,8 +6,6 @@ + + #include "core/fpdfapi/page/cpdf_devicecs.h" + +-#include +- + #include + + #include "core/fpdfapi/parser/cpdf_array.h" +@@ -17,8 +15,9 @@ + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fxcodec/fx_codec.h" + #include "core/fxge/dib/cfx_cmyk_to_srgb.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/cxx17_backports.h" ++#include "third_party/base/notreached.h" + + namespace { + +@@ -28,9 +27,9 @@ float NormalizeChannel(float fVal) { + + } // namespace + +-CPDF_DeviceCS::CPDF_DeviceCS(int family) : CPDF_ColorSpace(nullptr, family) { +- ASSERT(family == PDFCS_DEVICEGRAY || family == PDFCS_DEVICERGB || +- family == PDFCS_DEVICECMYK); ++CPDF_DeviceCS::CPDF_DeviceCS(Family family) : CPDF_ColorSpace(family) { ++ DCHECK(family == Family::kDeviceGray || family == Family::kDeviceRGB || ++ family == Family::kDeviceCMYK); + SetComponentsForStockCS(ComponentsForFamily(GetFamily())); + } + +@@ -41,27 +40,26 @@ uint32_t CPDF_DeviceCS::v_Load(CPDF_Document* pDoc, + std::set* pVisited) { + // Unlike other classes that inherit from CPDF_ColorSpace, CPDF_DeviceCS is + // never loaded by CPDF_ColorSpace. +- NOTREACHED(); +- return 0; ++ NOTREACHED_NORETURN(); + } + +-bool CPDF_DeviceCS::GetRGB(const float* pBuf, ++bool CPDF_DeviceCS::GetRGB(pdfium::span pBuf, + float* R, + float* G, + float* B) const { +- switch (m_Family) { +- case PDFCS_DEVICEGRAY: +- *R = NormalizeChannel(*pBuf); ++ switch (GetFamily()) { ++ case Family::kDeviceGray: ++ *R = NormalizeChannel(pBuf[0]); + *G = *R; + *B = *R; + return true; +- case PDFCS_DEVICERGB: ++ case Family::kDeviceRGB: + *R = NormalizeChannel(pBuf[0]); + *G = NormalizeChannel(pBuf[1]); + *B = NormalizeChannel(pBuf[2]); + return true; +- case PDFCS_DEVICECMYK: +- if (m_dwStdConversion) { ++ case Family::kDeviceCMYK: ++ if (IsStdConversionEnabled()) { + float k = pBuf[3]; + *R = 1.0f - std::min(1.0f, pBuf[0] + k); + *G = 1.0f - std::min(1.0f, pBuf[1] + k); +@@ -73,57 +71,74 @@ bool CPDF_DeviceCS::GetRGB(const float* pBuf, + } + return true; + default: +- NOTREACHED(); +- return false; ++ NOTREACHED_NORETURN(); + } + } + +-void CPDF_DeviceCS::TranslateImageLine(uint8_t* pDestBuf, +- const uint8_t* pSrcBuf, ++void CPDF_DeviceCS::TranslateImageLine(pdfium::span dest_span, ++ pdfium::span src_span, + int pixels, + int image_width, + int image_height, + bool bTransMask) const { +- switch (m_Family) { +- case PDFCS_DEVICEGRAY: ++ uint8_t* pDestBuf = dest_span.data(); ++ const uint8_t* pSrcBuf = src_span.data(); ++ switch (GetFamily()) { ++ case Family::kDeviceGray: + for (int i = 0; i < pixels; i++) { +- *pDestBuf++ = pSrcBuf[i]; +- *pDestBuf++ = pSrcBuf[i]; +- *pDestBuf++ = pSrcBuf[i]; ++ // Compiler can not conclude that src/dest don't overlap, avoid ++ // duplicate loads. ++ const uint8_t pix = pSrcBuf[i]; ++ *pDestBuf++ = pix; ++ *pDestBuf++ = pix; ++ *pDestBuf++ = pix; + } + break; +- case PDFCS_DEVICERGB: ++ case Family::kDeviceRGB: + fxcodec::ReverseRGB(pDestBuf, pSrcBuf, pixels); + break; +- case PDFCS_DEVICECMYK: ++ case Family::kDeviceCMYK: + if (bTransMask) { + for (int i = 0; i < pixels; i++) { +- int k = 255 - pSrcBuf[3]; +- pDestBuf[0] = ((255 - pSrcBuf[0]) * k) / 255; +- pDestBuf[1] = ((255 - pSrcBuf[1]) * k) / 255; +- pDestBuf[2] = ((255 - pSrcBuf[2]) * k) / 255; ++ // Compiler can't conclude src/dest don't overlap, avoid interleaved ++ // loads and stores. ++ const uint8_t s0 = pSrcBuf[0]; ++ const uint8_t s1 = pSrcBuf[1]; ++ const uint8_t s2 = pSrcBuf[2]; ++ const int k = 255 - pSrcBuf[3]; ++ pDestBuf[0] = ((255 - s0) * k) / 255; ++ pDestBuf[1] = ((255 - s1) * k) / 255; ++ pDestBuf[2] = ((255 - s2) * k) / 255; + pDestBuf += 3; + pSrcBuf += 4; + } + } else { +- for (int i = 0; i < pixels; i++) { +- if (m_dwStdConversion) { +- uint8_t k = pSrcBuf[3]; +- pDestBuf[2] = 255 - std::min(255, pSrcBuf[0] + k); +- pDestBuf[1] = 255 - std::min(255, pSrcBuf[1] + k); +- pDestBuf[0] = 255 - std::min(255, pSrcBuf[2] + k); +- } else { ++ if (IsStdConversionEnabled()) { ++ for (int i = 0; i < pixels; i++) { ++ // Compiler can't conclude src/dest don't overlap, avoid ++ // interleaved loads and stores. ++ const uint8_t s0 = pSrcBuf[0]; ++ const uint8_t s1 = pSrcBuf[1]; ++ const uint8_t s2 = pSrcBuf[2]; ++ const uint8_t k = pSrcBuf[3]; ++ pDestBuf[2] = 255 - std::min(255, s0 + k); ++ pDestBuf[1] = 255 - std::min(255, s1 + k); ++ pDestBuf[0] = 255 - std::min(255, s2 + k); ++ pSrcBuf += 4; ++ pDestBuf += 3; ++ } ++ } else { ++ for (int i = 0; i < pixels; i++) { + std::tie(pDestBuf[2], pDestBuf[1], pDestBuf[0]) = + AdobeCMYK_to_sRGB1(pSrcBuf[0], pSrcBuf[1], pSrcBuf[2], + pSrcBuf[3]); ++ pSrcBuf += 4; ++ pDestBuf += 3; + } +- pSrcBuf += 4; +- pDestBuf += 3; + } + } + break; + default: +- NOTREACHED(); +- break; ++ NOTREACHED_NORETURN(); + } + } +diff --git a/core/fpdfapi/page/cpdf_devicecs.h b/core/fpdfapi/page/cpdf_devicecs.h +index c8e256c67..8e8ca66cd 100644 +--- a/core/fpdfapi/page/cpdf_devicecs.h ++++ b/core/fpdfapi/page/cpdf_devicecs.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,15 +14,16 @@ + + class CPDF_DeviceCS final : public CPDF_ColorSpace { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- ++ CONSTRUCT_VIA_MAKE_RETAIN; + ~CPDF_DeviceCS() override; + + // CPDF_ColorSpace: +- bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override; +- void TranslateImageLine(uint8_t* pDestBuf, +- const uint8_t* pSrcBuf, ++ bool GetRGB(pdfium::span pBuf, ++ float* R, ++ float* G, ++ float* B) const override; ++ void TranslateImageLine(pdfium::span dest_span, ++ pdfium::span src_span, + int pixels, + int image_width, + int image_height, +@@ -32,7 +33,7 @@ class CPDF_DeviceCS final : public CPDF_ColorSpace { + std::set* pVisited) override; + + private: +- explicit CPDF_DeviceCS(int family); ++ explicit CPDF_DeviceCS(Family family); + }; + + #endif // CORE_FPDFAPI_PAGE_CPDF_DEVICECS_H_ +diff --git a/core/fpdfapi/page/cpdf_devicecs_unittest.cpp b/core/fpdfapi/page/cpdf_devicecs_unittest.cpp +index fbf255892..4c3c348cc 100644 +--- a/core/fpdfapi/page/cpdf_devicecs_unittest.cpp ++++ b/core/fpdfapi/page/cpdf_devicecs_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,7 +13,8 @@ TEST(CPDF_DeviceCSTest, GetRGBFromGray) { + float R; + float G; + float B; +- auto device_gray = pdfium::MakeRetain(PDFCS_DEVICEGRAY); ++ auto device_gray = ++ pdfium::MakeRetain(CPDF_ColorSpace::Family::kDeviceGray); + + // Test normal values. For gray, only first value from buf should be used. + float buf[3] = {0.43f, 0.11f, 0.34f}; +@@ -56,7 +57,8 @@ TEST(CPDF_DeviceCSTest, GetRGBFromRGB) { + float R; + float G; + float B; +- auto device_rgb = pdfium::MakeRetain(PDFCS_DEVICERGB); ++ auto device_rgb = ++ pdfium::MakeRetain(CPDF_ColorSpace::Family::kDeviceRGB); + + // Test normal values + float buf[3] = {0.13f, 1.0f, 0.652f}; +@@ -85,7 +87,8 @@ TEST(CPDF_DeviceCSTest, GetRGBFromCMYK) { + float R; + float G; + float B; +- auto device_cmyk = pdfium::MakeRetain(PDFCS_DEVICECMYK); ++ auto device_cmyk = ++ pdfium::MakeRetain(CPDF_ColorSpace::Family::kDeviceCMYK); + + // Test normal values + float buf[4] = {0.6f, 0.5f, 0.3f, 0.9f}; +diff --git a/core/fpdfapi/page/cpdf_dib.cpp b/core/fpdfapi/page/cpdf_dib.cpp +index 6976a0d63..29db5a4fd 100644 +--- a/core/fpdfapi/page/cpdf_dib.cpp ++++ b/core/fpdfapi/page/cpdf_dib.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,8 @@ + + #include "core/fpdfapi/page/cpdf_dib.h" + ++#include ++ + #include + #include + #include +@@ -15,33 +17,41 @@ + #include "core/fpdfapi/page/cpdf_docpagedata.h" + #include "core/fpdfapi/page/cpdf_image.h" + #include "core/fpdfapi/page/cpdf_imageobject.h" ++#include "core/fpdfapi/page/cpdf_indexedcs.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fpdfapi/parser/cpdf_name.h" ++#include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fxcodec/basic/basicmodule.h" +-#include "core/fxcodec/fx_codec.h" +-#include "core/fxcodec/jbig2/jbig2module.h" ++#include "core/fxcodec/jbig2/jbig2_decoder.h" + #include "core/fxcodec/jpeg/jpegmodule.h" + #include "core/fxcodec/jpx/cjpx_decoder.h" +-#include "core/fxcodec/jpx/jpxmodule.h" + #include "core/fxcodec/scanlinedecoder.h" +-#include "core/fxcrt/cfx_fixedbufgrow.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/span_util.h" ++#include "core/fxge/calculate_pitch.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/cxx17_backports.h" ++#include "third_party/base/notreached.h" + + namespace { + +-constexpr int kMaxImageDimension = 0x01FFFF; ++bool IsValidDimension(int value) { ++ constexpr int kMaxImageDimension = 0x01FFFF; ++ return value > 0 && value <= kMaxImageDimension; ++} + + unsigned int GetBits8(const uint8_t* pData, uint64_t bitpos, size_t nbits) { +- ASSERT(nbits == 1 || nbits == 2 || nbits == 4 || nbits == 8 || nbits == 16); +- ASSERT((bitpos & (nbits - 1)) == 0); ++ DCHECK(nbits == 1 || nbits == 2 || nbits == 4 || nbits == 8 || nbits == 16); ++ DCHECK_EQ((bitpos & (nbits - 1)), 0); + unsigned int byte = pData[bitpos / 8]; + if (nbits == 8) + return byte; +@@ -80,7 +90,7 @@ bool AreColorIndicesOutOfBounds(const uint8_t* indices, + } + + int CalculateBitsPerPixel(uint32_t bpc, uint32_t comps) { +- // TODO(thestig): Can |bpp| be 0 here? Add an ASSERT() or handle it? ++ // TODO(thestig): Can |bpp| be 0 here? Add an DCHECK() or handle it? + uint32_t bpp = bpc * comps; + if (bpp == 1) + return 1; +@@ -93,7 +103,7 @@ CJPX_Decoder::ColorSpaceOption ColorSpaceOptionFromColorSpace( + CPDF_ColorSpace* pCS) { + if (!pCS) + return CJPX_Decoder::kNoColorSpace; +- if (pCS->GetFamily() == PDFCS_INDEXED) ++ if (pCS->GetFamily() == CPDF_ColorSpace::Family::kIndexed) + return CJPX_Decoder::kIndexedColorSpace; + return CJPX_Decoder::kNormalColorSpace; + } +@@ -101,179 +111,192 @@ CJPX_Decoder::ColorSpaceOption ColorSpaceOptionFromColorSpace( + enum class JpxDecodeAction { + kFail, + kDoNothing, ++ kUseGray, + kUseRgb, + kUseCmyk, ++ kConvertArgbToRgb, + }; + +-JpxDecodeAction GetJpxDecodeAction(uint32_t jpx_components, +- const CPDF_ColorSpace* pdf_colorspace) { +- if (pdf_colorspace) { +- // Make sure the JPX image and the PDF colorspace agree on the number of +- // components. +- if (jpx_components != pdf_colorspace->CountComponents()) ++// Decides which JpxDecodeAction to use based on the colorspace information from ++// the PDF and the JPX image. Called only when the PDF's image object contains a ++// "/ColorSpace" entry. ++JpxDecodeAction GetJpxDecodeActionFromColorSpaces( ++ const CJPX_Decoder::JpxImageInfo& jpx_info, ++ const CPDF_ColorSpace* pdf_colorspace) { ++ if (pdf_colorspace == ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray)) { ++ if (jpx_info.colorspace != OPJ_CLRSPC_GRAY && ++ jpx_info.colorspace != OPJ_CLRSPC_UNSPECIFIED) { + return JpxDecodeAction::kFail; ++ } ++ return JpxDecodeAction::kUseGray; ++ } + +- if (pdf_colorspace == CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB)) +- return JpxDecodeAction::kUseRgb; ++ if (pdf_colorspace == ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB)) { ++ if (jpx_info.colorspace != OPJ_CLRSPC_SRGB && ++ jpx_info.colorspace != OPJ_CLRSPC_UNSPECIFIED) { ++ return JpxDecodeAction::kFail; ++ } + +- return JpxDecodeAction::kDoNothing; ++ // The channel count of a JPX image can be different from the PDF color ++ // space's component count. ++ if (jpx_info.channels > 3) { ++ return JpxDecodeAction::kConvertArgbToRgb; ++ } ++ return JpxDecodeAction::kUseRgb; + } + +- // Cases where the PDF did not provide a colorspace. +- // Choose how to decode based on the number of components in the JPX image. +- switch (jpx_components) { +- case 3: ++ if (pdf_colorspace == ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceCMYK)) { ++ if (jpx_info.colorspace != OPJ_CLRSPC_CMYK && ++ jpx_info.colorspace != OPJ_CLRSPC_UNSPECIFIED) { ++ return JpxDecodeAction::kFail; ++ } ++ return JpxDecodeAction::kUseCmyk; ++ } ++ ++ return JpxDecodeAction::kDoNothing; ++} ++ ++JpxDecodeAction GetJpxDecodeActionFromImageColorSpace( ++ const CJPX_Decoder::JpxImageInfo& jpx_info) { ++ switch (jpx_info.colorspace) { ++ case OPJ_CLRSPC_SYCC: ++ case OPJ_CLRSPC_EYCC: ++ case OPJ_CLRSPC_UNKNOWN: ++ case OPJ_CLRSPC_UNSPECIFIED: ++ return JpxDecodeAction::kDoNothing; ++ ++ case OPJ_CLRSPC_SRGB: ++ if (jpx_info.channels > 3) { ++ return JpxDecodeAction::kConvertArgbToRgb; ++ } ++ + return JpxDecodeAction::kUseRgb; + +- case 4: ++ case OPJ_CLRSPC_GRAY: ++ return JpxDecodeAction::kUseGray; ++ ++ case OPJ_CLRSPC_CMYK: + return JpxDecodeAction::kUseCmyk; + + default: +- return JpxDecodeAction::kDoNothing; ++ NOTREACHED_NORETURN(); + } + } + +-} // namespace ++JpxDecodeAction GetJpxDecodeAction(const CJPX_Decoder::JpxImageInfo& jpx_info, ++ const CPDF_ColorSpace* pdf_colorspace) { ++ if (pdf_colorspace) { ++ return GetJpxDecodeActionFromColorSpaces(jpx_info, pdf_colorspace); ++ } + +-CPDF_DIB::CPDF_DIB() = default; ++ // When PDF does not provide a color space, check the image color space. ++ return GetJpxDecodeActionFromImageColorSpace(jpx_info); ++} + +-CPDF_DIB::~CPDF_DIB() = default; ++int GetComponentCountFromOpjColorSpace(OPJ_COLOR_SPACE colorspace) { ++ switch (colorspace) { ++ case OPJ_CLRSPC_GRAY: ++ return 1; + +-bool CPDF_DIB::Load(CPDF_Document* pDoc, const CPDF_Stream* pStream) { +- if (!pStream) +- return false; ++ case OPJ_CLRSPC_SRGB: ++ case OPJ_CLRSPC_SYCC: ++ case OPJ_CLRSPC_EYCC: ++ return 3; + +- m_pDocument = pDoc; +- m_pDict.Reset(pStream->GetDict()); +- if (!m_pDict) +- return false; ++ case OPJ_CLRSPC_CMYK: ++ return 4; + +- m_pStream.Reset(pStream); +- m_Width = m_pDict->GetIntegerFor("Width"); +- m_Height = m_pDict->GetIntegerFor("Height"); +- if (m_Width <= 0 || m_Height <= 0 || m_Width > kMaxImageDimension || +- m_Height > kMaxImageDimension) { +- return false; ++ default: ++ return 0; + } +- m_GroupFamily = 0; +- m_bLoadMask = false; +- if (!LoadColorInfo(nullptr, nullptr)) +- return false; ++} + +- if (m_bDoBpcCheck && (m_bpc == 0 || m_nComponents == 0)) +- return false; ++} // namespace + +- FX_SAFE_UINT32 src_size = +- fxcodec::CalculatePitch8(m_bpc, m_nComponents, m_Width) * m_Height; +- if (!src_size.IsValid()) +- return false; ++CPDF_DIB::CPDF_DIB(CPDF_Document* pDoc, RetainPtr pStream) ++ : m_pDocument(pDoc), m_pStream(std::move(pStream)) {} + +- m_pStreamAcc = pdfium::MakeRetain(pStream); +- m_pStreamAcc->LoadAllDataImageAcc(src_size.ValueOrDie()); +- if (m_pStreamAcc->GetSize() == 0 || !m_pStreamAcc->GetData()) +- return false; ++CPDF_DIB::~CPDF_DIB() = default; + +- if (CreateDecoder() == LoadState::kFail) +- return false; ++CPDF_DIB::JpxSMaskInlineData::JpxSMaskInlineData() = default; + +- if (m_bImageMask) +- SetMaskProperties(); +- else +- m_bpp = CalculateBitsPerPixel(m_bpc, m_nComponents); ++CPDF_DIB::JpxSMaskInlineData::~JpxSMaskInlineData() = default; + +- FX_SAFE_UINT32 pitch = fxcodec::CalculatePitch32(m_bpp, m_Width); +- if (!pitch.IsValid()) ++bool CPDF_DIB::Load() { ++ if (!LoadInternal(nullptr, nullptr)) + return false; + +- m_pLineBuf.reset(FX_Alloc(uint8_t, pitch.ValueOrDie())); +- LoadPalette(); +- if (m_bColorKey) { +- m_bpp = 32; +- m_AlphaFlag = 2; +- pitch = fxcodec::CalculatePitch32(m_bpp, m_Width); +- if (!pitch.IsValid()) +- return false; ++ if (CreateDecoder(0) == LoadState::kFail) ++ return false; + +- m_pMaskedLine.reset(FX_Alloc(uint8_t, pitch.ValueOrDie())); +- } +- m_Pitch = pitch.ValueOrDie(); +- return true; ++ return ContinueInternal(); + } + + bool CPDF_DIB::ContinueToLoadMask() { ++ if (m_pColorSpace && m_bStdCS) ++ m_pColorSpace->EnableStdConversion(true); ++ ++ return ContinueInternal(); ++} ++ ++bool CPDF_DIB::ContinueInternal() { + if (m_bImageMask) { + SetMaskProperties(); + } else { + if (!m_bpc || !m_nComponents) + return false; + +- m_bpp = CalculateBitsPerPixel(m_bpc, m_nComponents); ++ m_Format = MakeRGBFormat(CalculateBitsPerPixel(m_bpc, m_nComponents)); + } + +- FX_SAFE_UINT32 pitch = fxcodec::CalculatePitch32(m_bpp, m_Width); +- if (!pitch.IsValid()) ++ absl::optional pitch = ++ fxge::CalculatePitch32(GetBppFromFormat(m_Format), m_Width); ++ if (!pitch.has_value()) + return false; + +- m_pLineBuf.reset(FX_Alloc(uint8_t, pitch.ValueOrDie())); +- if (m_pColorSpace && m_bStdCS) { +- m_pColorSpace->EnableStdConversion(true); +- } ++ m_LineBuf = DataVector(pitch.value()); + LoadPalette(); + if (m_bColorKey) { +- m_bpp = 32; +- m_AlphaFlag = 2; +- pitch = fxcodec::CalculatePitch32(m_bpp, m_Width); +- if (!pitch.IsValid()) ++ m_Format = FXDIB_Format::kArgb; ++ pitch = fxge::CalculatePitch32(GetBppFromFormat(m_Format), m_Width); ++ if (!pitch.has_value()) + return false; +- m_pMaskedLine.reset(FX_Alloc(uint8_t, pitch.ValueOrDie())); ++ m_MaskBuf = DataVector(pitch.value()); + } +- m_Pitch = pitch.ValueOrDie(); ++ m_Pitch = pitch.value(); + return true; + } + + CPDF_DIB::LoadState CPDF_DIB::StartLoadDIBBase( +- CPDF_Document* pDoc, +- const CPDF_Stream* pStream, + bool bHasMask, + const CPDF_Dictionary* pFormResources, +- CPDF_Dictionary* pPageResources, ++ const CPDF_Dictionary* pPageResources, + bool bStdCS, +- uint32_t GroupFamily, +- bool bLoadMask) { +- if (!pStream) +- return LoadState::kFail; +- +- m_pDocument = pDoc; +- m_pDict.Reset(pStream->GetDict()); +- m_pStream.Reset(pStream); ++ CPDF_ColorSpace::Family GroupFamily, ++ bool bLoadMask, ++ const CFX_Size& max_size_required) { + m_bStdCS = bStdCS; + m_bHasMask = bHasMask; +- m_Width = m_pDict->GetIntegerFor("Width"); +- m_Height = m_pDict->GetIntegerFor("Height"); +- if (m_Width <= 0 || m_Height <= 0 || m_Width > kMaxImageDimension || +- m_Height > kMaxImageDimension) { +- return LoadState::kFail; +- } + m_GroupFamily = GroupFamily; + m_bLoadMask = bLoadMask; +- if (!LoadColorInfo(m_pStream->IsInline() ? pFormResources : nullptr, +- pPageResources)) { +- return LoadState::kFail; +- } +- if (m_bDoBpcCheck && (m_bpc == 0 || m_nComponents == 0)) +- return LoadState::kFail; + +- FX_SAFE_UINT32 src_size = +- fxcodec::CalculatePitch8(m_bpc, m_nComponents, m_Width) * m_Height; +- if (!src_size.IsValid()) +- return LoadState::kFail; ++ if (!m_pStream->IsInline()) ++ pFormResources = nullptr; + +- m_pStreamAcc = pdfium::MakeRetain(pStream); +- m_pStreamAcc->LoadAllDataImageAcc(src_size.ValueOrDie()); +- if (m_pStreamAcc->GetSize() == 0 || !m_pStreamAcc->GetData()) ++ if (!LoadInternal(pFormResources, pPageResources)) + return LoadState::kFail; + +- LoadState iCreatedDecoder = CreateDecoder(); ++ uint8_t resolution_levels_to_skip = 0; ++ if (max_size_required.width != 0 && max_size_required.height != 0) { ++ resolution_levels_to_skip = static_cast( ++ std::log2(std::max(1, std::min(m_Width / max_size_required.width, ++ m_Height / max_size_required.height)))); ++ } ++ ++ LoadState iCreatedDecoder = CreateDecoder(resolution_levels_to_skip); + if (iCreatedDecoder == LoadState::kFail) + return LoadState::kFail; + +@@ -286,8 +309,8 @@ CPDF_DIB::LoadState CPDF_DIB::StartLoadDIBBase( + return LoadState::kContinue; + } + +- ASSERT(iCreatedDecoder == LoadState::kSuccess); +- ASSERT(iLoadedMask == LoadState::kSuccess); ++ DCHECK_EQ(iCreatedDecoder, LoadState::kSuccess); ++ DCHECK_EQ(iLoadedMask, LoadState::kSuccess); + if (m_pColorSpace && m_bStdCS) + m_pColorSpace->EnableStdConversion(false); + return LoadState::kSuccess; +@@ -308,47 +331,43 @@ CPDF_DIB::LoadState CPDF_DIB::ContinueLoadDIBBase(PauseIndicatorIface* pPause) { + return LoadState::kFail; + + FXCODEC_STATUS iDecodeStatus; +- Jbig2Module* pJbig2Module = +- fxcodec::ModuleMgr::GetInstance()->GetJbig2Module(); + if (!m_pJbig2Context) { +- m_pJbig2Context = pdfium::MakeUnique(); ++ m_pJbig2Context = std::make_unique(); + if (m_pStreamAcc->GetImageParam()) { +- const CPDF_Stream* pGlobals = ++ RetainPtr pGlobals = + m_pStreamAcc->GetImageParam()->GetStreamFor("JBIG2Globals"); + if (pGlobals) { +- m_pGlobalAcc = pdfium::MakeRetain(pGlobals); ++ m_pGlobalAcc = pdfium::MakeRetain(std::move(pGlobals)); + m_pGlobalAcc->LoadAllDataFiltered(); + } + } +- uint32_t nSrcObjNum = 0; ++ uint64_t nSrcKey = 0; + pdfium::span pSrcSpan; + if (m_pStreamAcc) { + pSrcSpan = m_pStreamAcc->GetSpan(); +- if (m_pStreamAcc->GetStream()) +- nSrcObjNum = m_pStreamAcc->GetStream()->GetObjNum(); ++ nSrcKey = m_pStreamAcc->KeyForCache(); + } +- uint32_t nGlobalObjNum = 0; ++ uint64_t nGlobalKey = 0; + pdfium::span pGlobalSpan; + if (m_pGlobalAcc) { + pGlobalSpan = m_pGlobalAcc->GetSpan(); +- if (m_pGlobalAcc->GetStream()) +- nGlobalObjNum = m_pGlobalAcc->GetStream()->GetObjNum(); ++ nGlobalKey = m_pGlobalAcc->KeyForCache(); + } +- iDecodeStatus = pJbig2Module->StartDecode( +- m_pJbig2Context.get(), m_pDocument->CodecContext(), m_Width, m_Height, +- pSrcSpan, nSrcObjNum, pGlobalSpan, nGlobalObjNum, ++ iDecodeStatus = Jbig2Decoder::StartDecode( ++ m_pJbig2Context.get(), m_pDocument->GetOrCreateCodecContext(), m_Width, ++ m_Height, pSrcSpan, nSrcKey, pGlobalSpan, nGlobalKey, + m_pCachedBitmap->GetBuffer(), m_pCachedBitmap->GetPitch(), pPause); + } else { +- iDecodeStatus = pJbig2Module->ContinueDecode(m_pJbig2Context.get(), pPause); ++ iDecodeStatus = Jbig2Decoder::ContinueDecode(m_pJbig2Context.get(), pPause); + } + +- if (iDecodeStatus < 0) { ++ if (iDecodeStatus == FXCODEC_STATUS::kError) { + m_pJbig2Context.reset(); + m_pCachedBitmap.Reset(); + m_pGlobalAcc.Reset(); + return LoadState::kFail; + } +- if (iDecodeStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE) ++ if (iDecodeStatus == FXCODEC_STATUS::kDecodeToBeContinued) + return LoadState::kContinue; + + LoadState iContinueStatus = LoadState::kSuccess; +@@ -368,58 +387,51 @@ CPDF_DIB::LoadState CPDF_DIB::ContinueLoadDIBBase(PauseIndicatorIface* pPause) { + + bool CPDF_DIB::LoadColorInfo(const CPDF_Dictionary* pFormResources, + const CPDF_Dictionary* pPageResources) { ++ absl::optional decoder_array = GetDecoderArray(m_pDict); ++ if (!decoder_array.has_value()) ++ return false; ++ + m_bpc_orig = m_pDict->GetIntegerFor("BitsPerComponent"); + if (!IsMaybeValidBitsPerComponent(m_bpc_orig)) + return false; + +- if (m_pDict->GetIntegerFor("ImageMask")) +- m_bImageMask = true; ++ m_bImageMask = m_pDict->GetBooleanFor("ImageMask", /*bDefault=*/false); + + if (m_bImageMask || !m_pDict->KeyExist("ColorSpace")) { +- if (!m_bImageMask) { +- const CPDF_Object* pFilter = m_pDict->GetDirectObjectFor("Filter"); +- if (pFilter) { +- ByteString filter; +- if (pFilter->IsName()) { +- filter = pFilter->GetString(); +- } else if (const CPDF_Array* pArray = pFilter->AsArray()) { +- if (!ValidateDecoderPipeline(pArray)) +- return false; +- filter = pArray->GetStringAt(pArray->size() - 1); +- } +- +- if (filter == "JPXDecode") { +- m_bDoBpcCheck = false; +- return true; +- } ++ if (!m_bImageMask && !decoder_array.value().empty()) { ++ const ByteString& filter = decoder_array.value().back().first; ++ if (filter == "JPXDecode") { ++ m_bDoBpcCheck = false; ++ return true; + } + } + m_bImageMask = true; + m_bpc = m_nComponents = 1; +- const CPDF_Array* pDecode = m_pDict->GetArrayFor("Decode"); ++ RetainPtr pDecode = m_pDict->GetArrayFor("Decode"); + m_bDefaultDecode = !pDecode || !pDecode->GetIntegerAt(0); + return true; + } + +- const CPDF_Object* pCSObj = m_pDict->GetDirectObjectFor("ColorSpace"); ++ RetainPtr pCSObj = ++ m_pDict->GetDirectObjectFor("ColorSpace"); + if (!pCSObj) + return false; + +- auto* pDocPageData = CPDF_DocPageData::FromDocument(m_pDocument.Get()); ++ auto* pDocPageData = CPDF_DocPageData::FromDocument(m_pDocument); + if (pFormResources) +- m_pColorSpace = pDocPageData->GetColorSpace(pCSObj, pFormResources); ++ m_pColorSpace = pDocPageData->GetColorSpace(pCSObj.Get(), pFormResources); + if (!m_pColorSpace) +- m_pColorSpace = pDocPageData->GetColorSpace(pCSObj, pPageResources); ++ m_pColorSpace = pDocPageData->GetColorSpace(pCSObj.Get(), pPageResources); + if (!m_pColorSpace) + return false; + + // If the checks above failed to find a colorspace, and the next line to set + // |m_nComponents| does not get reached, then a decoder can try to set +- // |m_nComponents| based on the number of components in the image being ++ // |m_nComponents| based on the number of channels in the image being + // decoded. + m_nComponents = m_pColorSpace->CountComponents(); + m_Family = m_pColorSpace->GetFamily(); +- if (m_Family == PDFCS_ICCBASED && pCSObj->IsName()) { ++ if (m_Family == CPDF_ColorSpace::Family::kICCBased && pCSObj->IsName()) { + ByteString cs = pCSObj->GetString(); + if (cs == "DeviceGray") + m_nComponents = 1; +@@ -428,37 +440,44 @@ bool CPDF_DIB::LoadColorInfo(const CPDF_Dictionary* pFormResources, + else if (cs == "DeviceCMYK") + m_nComponents = 4; + } +- ValidateDictParam(); +- return GetDecodeAndMaskArray(&m_bDefaultDecode, &m_bColorKey); ++ ++ ByteString filter; ++ if (!decoder_array.value().empty()) ++ filter = decoder_array.value().back().first; ++ ++ if (!ValidateDictParam(filter)) ++ return false; ++ ++ return GetDecodeAndMaskArray(); + } + +-bool CPDF_DIB::GetDecodeAndMaskArray(bool* bDefaultDecode, bool* bColorKey) { ++bool CPDF_DIB::GetDecodeAndMaskArray() { + if (!m_pColorSpace) + return false; + + m_CompData.resize(m_nComponents); + int max_data = (1 << m_bpc) - 1; +- const CPDF_Array* pDecode = m_pDict->GetArrayFor("Decode"); ++ RetainPtr pDecode = m_pDict->GetArrayFor("Decode"); + if (pDecode) { + for (uint32_t i = 0; i < m_nComponents; i++) { +- m_CompData[i].m_DecodeMin = pDecode->GetNumberAt(i * 2); +- float max = pDecode->GetNumberAt(i * 2 + 1); ++ m_CompData[i].m_DecodeMin = pDecode->GetFloatAt(i * 2); ++ float max = pDecode->GetFloatAt(i * 2 + 1); + m_CompData[i].m_DecodeStep = (max - m_CompData[i].m_DecodeMin) / max_data; + float def_value; + float def_min; + float def_max; + m_pColorSpace->GetDefaultValue(i, &def_value, &def_min, &def_max); +- if (m_Family == PDFCS_INDEXED) ++ if (m_Family == CPDF_ColorSpace::Family::kIndexed) + def_max = max_data; + if (def_min != m_CompData[i].m_DecodeMin || def_max != max) +- *bDefaultDecode = false; ++ m_bDefaultDecode = false; + } + } else { + for (uint32_t i = 0; i < m_nComponents; i++) { + float def_value; + m_pColorSpace->GetDefaultValue(i, &def_value, &m_CompData[i].m_DecodeMin, + &m_CompData[i].m_DecodeStep); +- if (m_Family == PDFCS_INDEXED) ++ if (m_Family == CPDF_ColorSpace::Family::kIndexed) + m_CompData[i].m_DecodeStep = max_data; + m_CompData[i].m_DecodeStep = + (m_CompData[i].m_DecodeStep - m_CompData[i].m_DecodeMin) / max_data; +@@ -467,7 +486,7 @@ bool CPDF_DIB::GetDecodeAndMaskArray(bool* bDefaultDecode, bool* bColorKey) { + if (m_pDict->KeyExist("SMask")) + return true; + +- const CPDF_Object* pMask = m_pDict->GetDirectObjectFor("Mask"); ++ RetainPtr pMask = m_pDict->GetDirectObjectFor("Mask"); + if (!pMask) + return true; + +@@ -480,12 +499,12 @@ bool CPDF_DIB::GetDecodeAndMaskArray(bool* bDefaultDecode, bool* bColorKey) { + m_CompData[i].m_ColorKeyMax = std::min(max_num, max_data); + } + } +- *bColorKey = true; ++ m_bColorKey = true; + } + return true; + } + +-CPDF_DIB::LoadState CPDF_DIB::CreateDecoder() { ++CPDF_DIB::LoadState CPDF_DIB::CreateDecoder(uint8_t resolution_levels_to_skip) { + ByteString decoder = m_pStreamAcc->GetImageDecoder(); + if (decoder.IsEmpty()) + return LoadState::kSuccess; +@@ -494,14 +513,15 @@ CPDF_DIB::LoadState CPDF_DIB::CreateDecoder() { + return LoadState::kFail; + + if (decoder == "JPXDecode") { +- m_pCachedBitmap = LoadJpxBitmap(); ++ m_pCachedBitmap = LoadJpxBitmap(resolution_levels_to_skip); + return m_pCachedBitmap ? LoadState::kSuccess : LoadState::kFail; + } + + if (decoder == "JBIG2Decode") { + m_pCachedBitmap = pdfium::MakeRetain(); + if (!m_pCachedBitmap->Create( +- m_Width, m_Height, m_bImageMask ? FXDIB_1bppMask : FXDIB_1bppRgb)) { ++ m_Width, m_Height, ++ m_bImageMask ? FXDIB_Format::k1bppMask : FXDIB_Format::k1bppRgb)) { + m_pCachedBitmap.Reset(); + return LoadState::kFail; + } +@@ -510,7 +530,7 @@ CPDF_DIB::LoadState CPDF_DIB::CreateDecoder() { + } + + pdfium::span src_span = m_pStreamAcc->GetSpan(); +- const CPDF_Dictionary* pParams = m_pStreamAcc->GetImageParam(); ++ RetainPtr pParams = m_pStreamAcc->GetImageParam(); + if (decoder == "CCITTFaxDecode") { + m_pDecoder = CreateFaxDecoder(src_span, m_Width, m_Height, pParams); + } else if (decoder == "FlateDecode") { +@@ -526,34 +546,33 @@ CPDF_DIB::LoadState CPDF_DIB::CreateDecoder() { + if (!m_pDecoder) + return LoadState::kFail; + +- FX_SAFE_UINT32 requested_pitch = +- fxcodec::CalculatePitch8(m_bpc, m_nComponents, m_Width); +- if (!requested_pitch.IsValid()) ++ const absl::optional requested_pitch = ++ fxge::CalculatePitch8(m_bpc, m_nComponents, m_Width); ++ if (!requested_pitch.has_value()) + return LoadState::kFail; +- FX_SAFE_UINT32 provided_pitch = fxcodec::CalculatePitch8( ++ const absl::optional provided_pitch = fxge::CalculatePitch8( + m_pDecoder->GetBPC(), m_pDecoder->CountComps(), m_pDecoder->GetWidth()); +- if (!provided_pitch.IsValid()) ++ if (!provided_pitch.has_value()) + return LoadState::kFail; +- if (provided_pitch.ValueOrDie() < requested_pitch.ValueOrDie()) ++ if (provided_pitch.value() < requested_pitch.value()) + return LoadState::kFail; + return LoadState::kSuccess; + } + + bool CPDF_DIB::CreateDCTDecoder(pdfium::span src_span, + const CPDF_Dictionary* pParams) { +- JpegModule* pJpegModule = fxcodec::ModuleMgr::GetInstance()->GetJpegModule(); +- m_pDecoder = pJpegModule->CreateDecoder( ++ m_pDecoder = JpegModule::CreateDecoder( + src_span, m_Width, m_Height, m_nComponents, + !pParams || pParams->GetIntegerFor("ColorTransform", 1)); + if (m_pDecoder) + return true; + +- Optional info_opt = +- pJpegModule->LoadInfo(src_span); ++ absl::optional info_opt = ++ JpegModule::LoadInfo(src_span); + if (!info_opt.has_value()) + return false; + +- const JpegModule::JpegImageInfo& info = info_opt.value(); ++ const JpegModule::ImageInfo& info = info_opt.value(); + m_Width = info.width; + m_Height = info.height; + +@@ -564,8 +583,8 @@ bool CPDF_DIB::CreateDCTDecoder(pdfium::span src_span, + + if (m_nComponents == static_cast(info.num_components)) { + m_bpc = info.bits_per_components; +- m_pDecoder = pJpegModule->CreateDecoder( +- src_span, m_Width, m_Height, m_nComponents, info.color_transform); ++ m_pDecoder = JpegModule::CreateDecoder(src_span, m_Width, m_Height, ++ m_nComponents, info.color_transform); + return true; + } + +@@ -574,20 +593,20 @@ bool CPDF_DIB::CreateDCTDecoder(pdfium::span src_span, + if (m_pColorSpace) { + uint32_t colorspace_comps = m_pColorSpace->CountComponents(); + switch (m_Family) { +- case PDFCS_DEVICEGRAY: +- case PDFCS_DEVICERGB: +- case PDFCS_DEVICECMYK: { ++ case CPDF_ColorSpace::Family::kDeviceGray: ++ case CPDF_ColorSpace::Family::kDeviceRGB: ++ case CPDF_ColorSpace::Family::kDeviceCMYK: { + uint32_t dwMinComps = CPDF_ColorSpace::ComponentsForFamily(m_Family); + if (colorspace_comps < dwMinComps || m_nComponents < dwMinComps) + return false; + break; + } +- case PDFCS_LAB: { ++ case CPDF_ColorSpace::Family::kLab: { + if (m_nComponents != 3 || colorspace_comps < 3) + return false; + break; + } +- case PDFCS_ICCBASED: { ++ case CPDF_ColorSpace::Family::kICCBased: { + if (!CPDF_ColorSpace::IsValidIccComponents(colorspace_comps) || + !CPDF_ColorSpace::IsValidIccComponents(m_nComponents) || + colorspace_comps < m_nComponents) { +@@ -602,25 +621,30 @@ bool CPDF_DIB::CreateDCTDecoder(pdfium::span src_span, + } + } + } else { +- if (m_Family == PDFCS_LAB && m_nComponents != 3) ++ if (m_Family == CPDF_ColorSpace::Family::kLab && m_nComponents != 3) + return false; + } +- if (!GetDecodeAndMaskArray(&m_bDefaultDecode, &m_bColorKey)) ++ if (!GetDecodeAndMaskArray()) + return false; + + m_bpc = info.bits_per_components; +- m_pDecoder = pJpegModule->CreateDecoder(src_span, m_Width, m_Height, +- m_nComponents, info.color_transform); ++ m_pDecoder = JpegModule::CreateDecoder(src_span, m_Width, m_Height, ++ m_nComponents, info.color_transform); + return true; + } + +-RetainPtr CPDF_DIB::LoadJpxBitmap() { +- std::unique_ptr decoder = JpxModule::CreateDecoder( +- m_pStreamAcc->GetSpan(), +- ColorSpaceOptionFromColorSpace(m_pColorSpace.Get())); ++RetainPtr CPDF_DIB::LoadJpxBitmap( ++ uint8_t resolution_levels_to_skip) { ++ std::unique_ptr decoder = ++ CJPX_Decoder::Create(m_pStreamAcc->GetSpan(), ++ ColorSpaceOptionFromColorSpace(m_pColorSpace.Get()), ++ resolution_levels_to_skip); + if (!decoder) + return nullptr; + ++ m_Height >>= resolution_levels_to_skip; ++ m_Width >>= resolution_levels_to_skip; ++ + if (!decoder->StartDecode()) + return nullptr; + +@@ -632,22 +656,35 @@ RetainPtr CPDF_DIB::LoadJpxBitmap() { + + RetainPtr original_colorspace = m_pColorSpace; + bool swap_rgb = false; +- switch (GetJpxDecodeAction(image_info.components, m_pColorSpace.Get())) { ++ bool convert_argb_to_rgb = false; ++ auto action = GetJpxDecodeAction(image_info, m_pColorSpace.Get()); ++ switch (action) { + case JpxDecodeAction::kFail: + return nullptr; + + case JpxDecodeAction::kDoNothing: + break; + ++ case JpxDecodeAction::kUseGray: ++ m_pColorSpace = ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray); ++ break; ++ + case JpxDecodeAction::kUseRgb: +- DCHECK(image_info.components >= 3); ++ DCHECK(image_info.channels >= 3); + swap_rgb = true; + m_pColorSpace = nullptr; + break; + + case JpxDecodeAction::kUseCmyk: +- m_pColorSpace = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK); ++ m_pColorSpace = ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceCMYK); + break; ++ ++ case JpxDecodeAction::kConvertArgbToRgb: ++ swap_rgb = true; ++ convert_argb_to_rgb = true; ++ m_pColorSpace.Reset(); + } + + // If |original_colorspace| exists, then LoadColorInfo() already set +@@ -656,19 +693,25 @@ RetainPtr CPDF_DIB::LoadJpxBitmap() { + DCHECK_NE(0, m_nComponents); + } else { + DCHECK_EQ(0, m_nComponents); +- m_nComponents = image_info.components; ++ m_nComponents = GetComponentCountFromOpjColorSpace(image_info.colorspace); ++ if (m_nComponents == 0) { ++ return nullptr; ++ } + } + + FXDIB_Format format; +- if (image_info.components == 1) { +- format = FXDIB_8bppRgb; +- } else if (image_info.components <= 3) { +- format = FXDIB_Rgb; +- } else if (image_info.components == 4) { +- format = FXDIB_Rgb32; ++ if (action == JpxDecodeAction::kUseGray) { ++ format = FXDIB_Format::k8bppRgb; ++ } else if (action == JpxDecodeAction::kUseRgb && image_info.channels == 3) { ++ format = FXDIB_Format::kRgb; ++ } else if (action == JpxDecodeAction::kConvertArgbToRgb && ++ image_info.channels == 4) { ++ format = FXDIB_Format::kRgb32; ++ } else if (action == JpxDecodeAction::kUseRgb && image_info.channels == 4) { ++ format = FXDIB_Format::kRgb32; + } else { +- image_info.width = (image_info.width * image_info.components + 2) / 3; +- format = FXDIB_Rgb; ++ image_info.width = (image_info.width * image_info.channels + 2) / 3; ++ format = FXDIB_Format::kRgb; + } + + auto result_bitmap = pdfium::MakeRetain(); +@@ -677,44 +720,143 @@ RetainPtr CPDF_DIB::LoadJpxBitmap() { + + result_bitmap->Clear(0xFFFFFFFF); + if (!decoder->Decode(result_bitmap->GetBuffer(), result_bitmap->GetPitch(), +- swap_rgb)) { ++ swap_rgb, m_nComponents)) { + return nullptr; + } + +- if (m_pColorSpace && m_pColorSpace->GetFamily() == PDFCS_INDEXED && +- m_bpc < 8) { ++ if (convert_argb_to_rgb) { ++ DCHECK_EQ(3, m_nComponents); ++ auto rgb_bitmap = pdfium::MakeRetain(); ++ if (!rgb_bitmap->Create(image_info.width, image_info.height, ++ FXDIB_Format::kRgb)) { ++ return nullptr; ++ } ++ if (m_pDict->GetIntegerFor("SMaskInData") == 1) { ++ // TODO(thestig): Acrobat does not support "/SMaskInData 1" combined with ++ // filters. Check for that and fail early. ++ DCHECK(m_JpxInlineData.data.empty()); ++ m_JpxInlineData.width = image_info.width; ++ m_JpxInlineData.height = image_info.height; ++ m_JpxInlineData.data.reserve(image_info.width * image_info.height); ++ for (uint32_t row = 0; row < image_info.height; ++row) { ++ const uint8_t* src = result_bitmap->GetScanline(row).data(); ++ uint8_t* dest = rgb_bitmap->GetWritableScanline(row).data(); ++ for (uint32_t col = 0; col < image_info.width; ++col) { ++ uint8_t a = src[3]; ++ m_JpxInlineData.data.push_back(a); ++ uint8_t na = 255 - a; ++ uint8_t b = (src[0] * a + 255 * na) / 255; ++ uint8_t g = (src[1] * a + 255 * na) / 255; ++ uint8_t r = (src[2] * a + 255 * na) / 255; ++ dest[0] = b; ++ dest[1] = g; ++ dest[2] = r; ++ src += 4; ++ dest += 3; ++ } ++ } ++ } else { ++ // TODO(thestig): Is there existing code that does this already? ++ for (uint32_t row = 0; row < image_info.height; ++row) { ++ const uint8_t* src = result_bitmap->GetScanline(row).data(); ++ uint8_t* dest = rgb_bitmap->GetWritableScanline(row).data(); ++ for (uint32_t col = 0; col < image_info.width; ++col) { ++ memcpy(dest, src, 3); ++ src += 4; ++ dest += 3; ++ } ++ } ++ } ++ result_bitmap = std::move(rgb_bitmap); ++ } else if (m_pColorSpace && ++ m_pColorSpace->GetFamily() == CPDF_ColorSpace::Family::kIndexed && ++ m_bpc < 8) { + int scale = 8 - m_bpc; + for (uint32_t row = 0; row < image_info.height; ++row) { +- uint8_t* scanline = result_bitmap->GetWritableScanline(row); ++ uint8_t* scanline = result_bitmap->GetWritableScanline(row).data(); + for (uint32_t col = 0; col < image_info.width; ++col) { + *scanline = (*scanline) >> scale; + ++scanline; + } + } + } ++ ++ // TODO(crbug.com/pdfium/1747): Handle SMaskInData entries for different ++ // color space types. ++ + m_bpc = 8; + return result_bitmap; + } + ++bool CPDF_DIB::LoadInternal(const CPDF_Dictionary* pFormResources, ++ const CPDF_Dictionary* pPageResources) { ++ if (!m_pStream) ++ return false; ++ ++ m_pDict = m_pStream->GetDict(); ++ if (!m_pDict) ++ return false; ++ ++ m_Width = m_pDict->GetIntegerFor("Width"); ++ m_Height = m_pDict->GetIntegerFor("Height"); ++ if (!IsValidDimension(m_Width) || !IsValidDimension(m_Height)) ++ return false; ++ ++ if (!LoadColorInfo(pFormResources, pPageResources)) ++ return false; ++ ++ if (m_bDoBpcCheck && (m_bpc == 0 || m_nComponents == 0)) ++ return false; ++ ++ const absl::optional maybe_size = ++ fxge::CalculatePitch8(m_bpc, m_nComponents, m_Width); ++ if (!maybe_size.has_value()) ++ return false; ++ ++ FX_SAFE_UINT32 src_size = maybe_size.value(); ++ src_size *= m_Height; ++ if (!src_size.IsValid()) ++ return false; ++ ++ m_pStreamAcc = pdfium::MakeRetain(m_pStream); ++ m_pStreamAcc->LoadAllDataImageAcc(src_size.ValueOrDie()); ++ return !m_pStreamAcc->GetSpan().empty(); ++} ++ + CPDF_DIB::LoadState CPDF_DIB::StartLoadMask() { + m_MatteColor = 0XFFFFFFFF; ++ ++ if (!m_JpxInlineData.data.empty()) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "XObject"); ++ dict->SetNewFor("Subtype", "Image"); ++ dict->SetNewFor("ColorSpace", "DeviceGray"); ++ dict->SetNewFor("Width", m_JpxInlineData.width); ++ dict->SetNewFor("Height", m_JpxInlineData.height); ++ dict->SetNewFor("BitsPerComponent", 8); ++ ++ return StartLoadMaskDIB( ++ pdfium::MakeRetain(m_JpxInlineData.data, std::move(dict))); ++ } ++ + RetainPtr mask(m_pDict->GetStreamFor("SMask")); + if (!mask) { +- mask.Reset(ToStream(m_pDict->GetDirectObjectFor("Mask"))); ++ mask = ToStream(m_pDict->GetDirectObjectFor("Mask")); + return mask ? StartLoadMaskDIB(std::move(mask)) : LoadState::kSuccess; + } + +- const CPDF_Array* pMatte = mask->GetDict()->GetArrayFor("Matte"); +- if (pMatte && m_pColorSpace && m_Family != PDFCS_PATTERN && ++ RetainPtr pMatte = mask->GetDict()->GetArrayFor("Matte"); ++ if (pMatte && m_pColorSpace && ++ m_Family != CPDF_ColorSpace::Family::kPattern && + pMatte->size() == m_nComponents && + m_pColorSpace->CountComponents() <= m_nComponents) { + std::vector colors = +- ReadArrayElementsToVector(pMatte, m_nComponents); ++ ReadArrayElementsToVector(pMatte.Get(), m_nComponents); + + float R; + float G; + float B; +- m_pColorSpace->GetRGB(colors.data(), &R, &G, &B); ++ m_pColorSpace->GetRGB(colors, &R, &G, &B); + m_MatteColor = ArgbEncode(0, FXSYS_roundf(R * 255), FXSYS_roundf(G * 255), + FXSYS_roundf(B * 255)); + } +@@ -748,10 +890,11 @@ bool CPDF_DIB::IsJBigImage() const { + } + + CPDF_DIB::LoadState CPDF_DIB::StartLoadMaskDIB( +- RetainPtr mask) { +- m_pMask = pdfium::MakeRetain(); +- LoadState ret = m_pMask->StartLoadDIBBase( +- m_pDocument.Get(), mask.Get(), false, nullptr, nullptr, true, 0, false); ++ RetainPtr mask_stream) { ++ m_pMask = pdfium::MakeRetain(m_pDocument, std::move(mask_stream)); ++ LoadState ret = m_pMask->StartLoadDIBBase(false, nullptr, nullptr, true, ++ CPDF_ColorSpace::Family::kUnknown, ++ false, {0, 0}); + if (ret == LoadState::kContinue) { + if (m_Status == LoadState::kFail) + m_Status = LoadState::kContinue; +@@ -763,7 +906,7 @@ CPDF_DIB::LoadState CPDF_DIB::StartLoadMaskDIB( + } + + void CPDF_DIB::LoadPalette() { +- if (!m_pColorSpace || m_Family == PDFCS_PATTERN) ++ if (!m_pColorSpace || m_Family == CPDF_ColorSpace::Family::kPattern) + return; + + if (m_bpc == 0) +@@ -778,17 +921,16 @@ void CPDF_DIB::LoadPalette() { + return; + + if (bits == 1) { +- if (m_bDefaultDecode && +- (m_Family == PDFCS_DEVICEGRAY || m_Family == PDFCS_DEVICERGB)) { ++ if (m_bDefaultDecode && (m_Family == CPDF_ColorSpace::Family::kDeviceGray || ++ m_Family == CPDF_ColorSpace::Family::kDeviceRGB)) { + return; + } + if (m_pColorSpace->CountComponents() > 3) { + return; + } + float color_values[3]; +- color_values[0] = m_CompData[0].m_DecodeMin; +- color_values[1] = color_values[0]; +- color_values[2] = color_values[0]; ++ std::fill(std::begin(color_values), std::end(color_values), ++ m_CompData[0].m_DecodeMin); + + float R = 0.0f; + float G = 0.0f; +@@ -797,12 +939,22 @@ void CPDF_DIB::LoadPalette() { + + FX_ARGB argb0 = ArgbEncode(255, FXSYS_roundf(R * 255), + FXSYS_roundf(G * 255), FXSYS_roundf(B * 255)); +- color_values[0] += m_CompData[0].m_DecodeStep; +- color_values[1] += m_CompData[0].m_DecodeStep; +- color_values[2] += m_CompData[0].m_DecodeStep; +- m_pColorSpace->GetRGB(color_values, &R, &G, &B); +- FX_ARGB argb1 = ArgbEncode(255, FXSYS_roundf(R * 255), +- FXSYS_roundf(G * 255), FXSYS_roundf(B * 255)); ++ FX_ARGB argb1; ++ const CPDF_IndexedCS* indexed_cs = m_pColorSpace->AsIndexedCS(); ++ if (indexed_cs && indexed_cs->GetMaxIndex() == 0) { ++ // If an indexed color space's hival value is 0, only 1 color is specified ++ // in the lookup table. Another color should be set to 0xFF000000 by ++ // default to set the range of the color space. ++ argb1 = 0xFF000000; ++ } else { ++ color_values[0] += m_CompData[0].m_DecodeStep; ++ color_values[1] += m_CompData[0].m_DecodeStep; ++ color_values[2] += m_CompData[0].m_DecodeStep; ++ m_pColorSpace->GetRGB(color_values, &R, &G, &B); ++ argb1 = ArgbEncode(255, FXSYS_roundf(R * 255), FXSYS_roundf(G * 255), ++ FXSYS_roundf(B * 255)); ++ } ++ + if (argb0 != 0xFF000000 || argb1 != 0xFFFFFFFF) { + SetPaletteArgb(0, argb0); + SetPaletteArgb(1, argb1); +@@ -810,7 +962,8 @@ void CPDF_DIB::LoadPalette() { + return; + } + if (m_bpc == 8 && m_bDefaultDecode && +- m_pColorSpace == CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY)) { ++ m_pColorSpace == ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray)) { + return; + } + +@@ -828,56 +981,49 @@ void CPDF_DIB::LoadPalette() { + float R = 0; + float G = 0; + float B = 0; +- if (m_nComponents == 1 && m_Family == PDFCS_ICCBASED && ++ if (m_nComponents == 1 && m_Family == CPDF_ColorSpace::Family::kICCBased && + m_pColorSpace->CountComponents() > 1) { + int nComponents = m_pColorSpace->CountComponents(); + std::vector temp_buf(nComponents); + for (int k = 0; k < nComponents; ++k) + temp_buf[k] = color_values[0]; +- m_pColorSpace->GetRGB(temp_buf.data(), &R, &G, &B); ++ m_pColorSpace->GetRGB(temp_buf, &R, &G, &B); + } else { +- m_pColorSpace->GetRGB(color_values.data(), &R, &G, &B); ++ m_pColorSpace->GetRGB(color_values, &R, &G, &B); + } + SetPaletteArgb(i, ArgbEncode(255, FXSYS_roundf(R * 255), + FXSYS_roundf(G * 255), FXSYS_roundf(B * 255))); + } + } + +-void CPDF_DIB::ValidateDictParam() { ++bool CPDF_DIB::ValidateDictParam(const ByteString& filter) { + m_bpc = m_bpc_orig; +- const CPDF_Object* pFilter = m_pDict->GetDirectObjectFor("Filter"); +- if (pFilter) { +- if (pFilter->IsName()) { +- ByteString filter = pFilter->GetString(); +- if (filter == "CCITTFaxDecode" || filter == "JBIG2Decode") { +- m_bpc = 1; +- m_nComponents = 1; +- } else if (filter == "RunLengthDecode") { +- if (m_bpc != 1) { +- m_bpc = 8; +- } +- } else if (filter == "DCTDecode") { +- m_bpc = 8; +- } +- } else if (const CPDF_Array* pArray = pFilter->AsArray()) { +- ByteString filter = pArray->GetStringAt(pArray->size() - 1); +- if (filter == "CCITTFaxDecode" || filter == "JBIG2Decode") { +- m_bpc = 1; +- m_nComponents = 1; +- } else if (filter == "DCTDecode") { +- // Previously, filter == "RunLengthDecode" was checked in the "if" +- // statement as well, but too many documents don't conform to it. +- m_bpc = 8; +- } +- } ++ ++ // Per spec, |m_bpc| should always be 8 for RunLengthDecode, but too many ++ // documents do not conform to it. So skip this check. ++ ++ if (filter == "JPXDecode") { ++ m_bDoBpcCheck = false; ++ return true; ++ } ++ ++ if (filter == "CCITTFaxDecode" || filter == "JBIG2Decode") { ++ m_bpc = 1; ++ m_nComponents = 1; ++ } else if (filter == "DCTDecode") { ++ m_bpc = 8; + } + +- if (!IsAllowedBitsPerComponent(m_bpc)) ++ if (!IsAllowedBitsPerComponent(m_bpc)) { + m_bpc = 0; ++ return false; ++ } ++ return true; + } + +-void CPDF_DIB::TranslateScanline24bpp(uint8_t* dest_scan, +- const uint8_t* src_scan) const { ++void CPDF_DIB::TranslateScanline24bpp( ++ pdfium::span dest_scan, ++ pdfium::span src_scan) const { + if (m_bpc == 0) + return; + +@@ -900,7 +1046,7 @@ void CPDF_DIB::TranslateScanline24bpp(uint8_t* dest_scan, + color_values[color] = m_CompData[color].m_DecodeMin + + m_CompData[color].m_DecodeStep * data; + } else { +- unsigned int data = GetBits8(src_scan, src_bit_pos, m_bpc); ++ unsigned int data = GetBits8(src_scan.data(), src_bit_pos, m_bpc); + color_values[color] = m_CompData[color].m_DecodeMin + + m_CompData[color].m_DecodeStep * data; + src_bit_pos += m_bpc; +@@ -912,8 +1058,8 @@ void CPDF_DIB::TranslateScanline24bpp(uint8_t* dest_scan, + R = (1.0f - color_values[0]) * k; + G = (1.0f - color_values[1]) * k; + B = (1.0f - color_values[2]) * k; +- } else if (m_Family != PDFCS_PATTERN) { +- m_pColorSpace->GetRGB(color_values.data(), &R, &G, &B); ++ } else if (m_Family != CPDF_ColorSpace::Family::kPattern) { ++ m_pColorSpace->GetRGB(color_values, &R, &G, &B); + } + R = pdfium::clamp(R, 0.0f, 1.0f); + G = pdfium::clamp(G, 0.0f, 1.0f); +@@ -926,12 +1072,13 @@ void CPDF_DIB::TranslateScanline24bpp(uint8_t* dest_scan, + } + + bool CPDF_DIB::TranslateScanline24bppDefaultDecode( +- uint8_t* dest_scan, +- const uint8_t* src_scan) const { ++ pdfium::span dest_scan, ++ pdfium::span src_scan) const { + if (!m_bDefaultDecode) + return false; + +- if (m_Family != PDFCS_DEVICERGB && m_Family != PDFCS_CALRGB) { ++ if (m_Family != CPDF_ColorSpace::Family::kDeviceRGB && ++ m_Family != CPDF_ColorSpace::Family::kCalRGB) { + if (m_bpc != 8) + return false; + +@@ -945,21 +1092,22 @@ bool CPDF_DIB::TranslateScanline24bppDefaultDecode( + if (m_nComponents != 3) + return true; + +- const uint8_t* src_pos = src_scan; ++ uint8_t* dest_pos = dest_scan.data(); ++ const uint8_t* src_pos = src_scan.data(); + switch (m_bpc) { + case 8: + for (int column = 0; column < m_Width; column++) { +- *dest_scan++ = src_pos[2]; +- *dest_scan++ = src_pos[1]; +- *dest_scan++ = *src_pos; ++ *dest_pos++ = src_pos[2]; ++ *dest_pos++ = src_pos[1]; ++ *dest_pos++ = *src_pos; + src_pos += 3; + } + break; + case 16: + for (int col = 0; col < m_Width; col++) { +- *dest_scan++ = src_pos[4]; +- *dest_scan++ = src_pos[2]; +- *dest_scan++ = *src_pos; ++ *dest_pos++ = src_pos[4]; ++ *dest_pos++ = src_pos[2]; ++ *dest_pos++ = *src_pos; + src_pos += 6; + } + break; +@@ -968,18 +1116,18 @@ bool CPDF_DIB::TranslateScanline24bppDefaultDecode( + uint64_t src_bit_pos = 0; + size_t dest_byte_pos = 0; + for (int column = 0; column < m_Width; column++) { +- unsigned int R = GetBits8(src_scan, src_bit_pos, m_bpc); ++ unsigned int R = GetBits8(src_scan.data(), src_bit_pos, m_bpc); + src_bit_pos += m_bpc; +- unsigned int G = GetBits8(src_scan, src_bit_pos, m_bpc); ++ unsigned int G = GetBits8(src_scan.data(), src_bit_pos, m_bpc); + src_bit_pos += m_bpc; +- unsigned int B = GetBits8(src_scan, src_bit_pos, m_bpc); ++ unsigned int B = GetBits8(src_scan.data(), src_bit_pos, m_bpc); + src_bit_pos += m_bpc; + R = std::min(R, max_data); + G = std::min(G, max_data); + B = std::min(B, max_data); +- dest_scan[dest_byte_pos] = B * 255 / max_data; +- dest_scan[dest_byte_pos + 1] = G * 255 / max_data; +- dest_scan[dest_byte_pos + 2] = R * 255 / max_data; ++ dest_pos[dest_byte_pos] = B * 255 / max_data; ++ dest_pos[dest_byte_pos + 1] = G * 255 / max_data; ++ dest_pos[dest_byte_pos + 2] = R * 255 / max_data; + dest_byte_pos += 3; + } + break; +@@ -987,424 +1135,175 @@ bool CPDF_DIB::TranslateScanline24bppDefaultDecode( + return true; + } + +-uint8_t* CPDF_DIB::GetBuffer() const { +- return m_pCachedBitmap ? m_pCachedBitmap->GetBuffer() : nullptr; ++pdfium::span CPDF_DIB::GetBuffer() const { ++ return m_pCachedBitmap ? m_pCachedBitmap->GetBuffer() ++ : pdfium::span(); + } + +-const uint8_t* CPDF_DIB::GetScanline(int line) const { ++pdfium::span CPDF_DIB::GetScanline(int line) const { + if (m_bpc == 0) +- return nullptr; ++ return pdfium::span(); + +- FX_SAFE_UINT32 src_pitch = +- fxcodec::CalculatePitch8(m_bpc, m_nComponents, m_Width); +- if (!src_pitch.IsValid()) +- return nullptr; +- uint32_t src_pitch_value = src_pitch.ValueOrDie(); ++ const absl::optional src_pitch = ++ fxge::CalculatePitch8(m_bpc, m_nComponents, m_Width); ++ if (!src_pitch.has_value()) ++ return pdfium::span(); ++ ++ uint32_t src_pitch_value = src_pitch.value(); ++ // This is used as the buffer of `pSrcLine` when the stream is truncated, ++ // and the remaining bytes count is less than `src_pitch_value` ++ DataVector temp_buffer; ++ pdfium::span pSrcLine; + +- const uint8_t* pSrcLine = nullptr; + if (m_pCachedBitmap && src_pitch_value <= m_pCachedBitmap->GetPitch()) { +- if (line >= m_pCachedBitmap->GetHeight()) { ++ if (line >= m_pCachedBitmap->GetHeight()) + line = m_pCachedBitmap->GetHeight() - 1; +- } + pSrcLine = m_pCachedBitmap->GetScanline(line); + } else if (m_pDecoder) { + pSrcLine = m_pDecoder->GetScanline(line); +- } else if (m_pStreamAcc->GetSize() >= (line + 1) * src_pitch_value) { +- pSrcLine = m_pStreamAcc->GetData() + line * src_pitch_value; +- } +- if (!pSrcLine) { +- uint8_t* pLineBuf = m_pMaskedLine ? m_pMaskedLine.get() : m_pLineBuf.get(); +- memset(pLineBuf, 0xFF, m_Pitch); +- return pLineBuf; ++ } else if (m_pStreamAcc->GetSize() > line * src_pitch_value) { ++ pdfium::span remaining_bytes = ++ m_pStreamAcc->GetSpan().subspan(line * src_pitch_value); ++ if (remaining_bytes.size() >= src_pitch_value) { ++ pSrcLine = remaining_bytes.first(src_pitch_value); ++ } else { ++ temp_buffer = DataVector(src_pitch_value); ++ pdfium::span result = temp_buffer; ++ fxcrt::spancpy(result, remaining_bytes); ++ pSrcLine = result; ++ } + } + ++ if (pSrcLine.empty()) { ++ pdfium::span result = !m_MaskBuf.empty() ? m_MaskBuf : m_LineBuf; ++ fxcrt::spanset(result, 0); ++ return result; ++ } + if (m_bpc * m_nComponents == 1) { + if (m_bImageMask && m_bDefaultDecode) { +- for (uint32_t i = 0; i < src_pitch_value; i++) +- m_pLineBuf.get()[i] = ~pSrcLine[i]; +- return m_pLineBuf.get(); ++ for (uint32_t i = 0; i < src_pitch_value; i++) { ++ // TODO(tsepez): Bounds check if cost is acceptable. ++ m_LineBuf[i] = ~pSrcLine.data()[i]; ++ } ++ return pdfium::make_span(m_LineBuf).first(src_pitch_value); + } +- + if (!m_bColorKey) { +- memcpy(m_pLineBuf.get(), pSrcLine, src_pitch_value); +- return m_pLineBuf.get(); ++ pdfium::span result = m_LineBuf; ++ fxcrt::spancpy(result, pSrcLine.first(src_pitch_value)); ++ return result.first(src_pitch_value); + } +- +- uint32_t reset_argb = m_pPalette ? m_pPalette.get()[0] : 0xFF000000; +- uint32_t set_argb = m_pPalette ? m_pPalette.get()[1] : 0xFFFFFFFF; +- if (m_CompData[0].m_ColorKeyMin == 0) +- reset_argb = 0; +- if (m_CompData[0].m_ColorKeyMax == 1) +- set_argb = 0; +- set_argb = FXARGB_TODIB(set_argb); +- reset_argb = FXARGB_TODIB(reset_argb); +- uint32_t* dest_scan = reinterpret_cast(m_pMaskedLine.get()); +- for (int col = 0; col < m_Width; col++) { +- *dest_scan = GetBitValue(pSrcLine, col) ? set_argb : reset_argb; +- dest_scan++; ++ uint32_t reset_argb = Get1BitResetValue(); ++ uint32_t set_argb = Get1BitSetValue(); ++ uint32_t* dest_scan = reinterpret_cast(m_MaskBuf.data()); ++ for (int col = 0; col < m_Width; col++, dest_scan++) { ++ *dest_scan = GetBitValue(pSrcLine.data(), col) ? set_argb : reset_argb; + } +- return m_pMaskedLine.get(); ++ return pdfium::make_span(m_MaskBuf).first(m_Width * sizeof(uint32_t)); + } + if (m_bpc * m_nComponents <= 8) { ++ pdfium::span result = m_LineBuf; + if (m_bpc == 8) { +- memcpy(m_pLineBuf.get(), pSrcLine, src_pitch_value); ++ fxcrt::spancpy(result, pSrcLine.first(src_pitch_value)); ++ result = result.first(src_pitch_value); + } else { + uint64_t src_bit_pos = 0; + for (int col = 0; col < m_Width; col++) { + unsigned int color_index = 0; + for (uint32_t color = 0; color < m_nComponents; color++) { +- unsigned int data = GetBits8(pSrcLine, src_bit_pos, m_bpc); ++ unsigned int data = GetBits8(pSrcLine.data(), src_bit_pos, m_bpc); + color_index |= data << (color * m_bpc); + src_bit_pos += m_bpc; + } +- m_pLineBuf.get()[col] = color_index; ++ m_LineBuf[col] = color_index; + } ++ result = result.first(m_Width); + } + if (!m_bColorKey) +- return m_pLineBuf.get(); +- +- uint8_t* pDestPixel = m_pMaskedLine.get(); +- const uint8_t* pSrcPixel = m_pLineBuf.get(); +- for (int col = 0; col < m_Width; col++) { +- uint8_t index = *pSrcPixel++; +- if (m_pPalette) { +- *pDestPixel++ = FXARGB_B(m_pPalette.get()[index]); +- *pDestPixel++ = FXARGB_G(m_pPalette.get()[index]); +- *pDestPixel++ = FXARGB_R(m_pPalette.get()[index]); +- } else { ++ return result; ++ ++ uint8_t* pDestPixel = m_MaskBuf.data(); ++ const uint8_t* pSrcPixel = m_LineBuf.data(); ++ pdfium::span palette = GetPaletteSpan(); ++ if (HasPalette()) { ++ for (int col = 0; col < m_Width; col++) { ++ uint8_t index = *pSrcPixel++; ++ *pDestPixel++ = FXARGB_B(palette[index]); ++ *pDestPixel++ = FXARGB_G(palette[index]); ++ *pDestPixel++ = FXARGB_R(palette[index]); ++ *pDestPixel++ = ++ IsColorIndexOutOfBounds(index, m_CompData[0]) ? 0xFF : 0; ++ } ++ } else { ++ for (int col = 0; col < m_Width; col++) { ++ uint8_t index = *pSrcPixel++; + *pDestPixel++ = index; + *pDestPixel++ = index; + *pDestPixel++ = index; ++ *pDestPixel++ = ++ IsColorIndexOutOfBounds(index, m_CompData[0]) ? 0xFF : 0; + } +- *pDestPixel = IsColorIndexOutOfBounds(index, m_CompData[0]) ? 0xFF : 0; +- pDestPixel++; + } +- return m_pMaskedLine.get(); ++ return pdfium::make_span(m_MaskBuf).first(4 * m_Width); + } + if (m_bColorKey) { + if (m_nComponents == 3 && m_bpc == 8) { +- uint8_t* alpha_channel = m_pMaskedLine.get() + 3; ++ uint8_t* alpha_channel = m_MaskBuf.data() + 3; + for (int col = 0; col < m_Width; col++) { +- const uint8_t* pPixel = pSrcLine + col * 3; ++ const uint8_t* pPixel = pSrcLine.data() + col * 3; + alpha_channel[col * 4] = + AreColorIndicesOutOfBounds(pPixel, m_CompData.data(), 3) ? 0xFF : 0; + } + } else { +- memset(m_pMaskedLine.get(), 0xFF, m_Pitch); ++ fxcrt::spanset(pdfium::make_span(m_MaskBuf), 0xFF); + } + } + if (m_pColorSpace) { +- TranslateScanline24bpp(m_pLineBuf.get(), pSrcLine); +- pSrcLine = m_pLineBuf.get(); ++ TranslateScanline24bpp(m_LineBuf, pSrcLine); ++ src_pitch_value = 3 * m_Width; ++ pSrcLine = pdfium::make_span(m_LineBuf).first(src_pitch_value); + } + if (!m_bColorKey) + return pSrcLine; + +- const uint8_t* pSrcPixel = pSrcLine; +- uint8_t* pDestPixel = m_pMaskedLine.get(); ++ // TODO(tsepez): Bounds check if cost is acceptable. ++ const uint8_t* pSrcPixel = pSrcLine.data(); ++ uint8_t* pDestPixel = m_MaskBuf.data(); + for (int col = 0; col < m_Width; col++) { + *pDestPixel++ = *pSrcPixel++; + *pDestPixel++ = *pSrcPixel++; + *pDestPixel++ = *pSrcPixel++; + pDestPixel++; + } +- return m_pMaskedLine.get(); ++ return pdfium::make_span(m_MaskBuf).first(4 * m_Width); + } + + bool CPDF_DIB::SkipToScanline(int line, PauseIndicatorIface* pPause) const { + return m_pDecoder && m_pDecoder->SkipToScanline(line, pPause); + } + +-void CPDF_DIB::DownSampleScanline(int line, +- uint8_t* dest_scan, +- int dest_bpp, +- int dest_width, +- bool bFlipX, +- int clip_left, +- int clip_width) const { +- if (line < 0 || !dest_scan || dest_bpp <= 0 || dest_width <= 0 || +- clip_left < 0 || clip_width <= 0) { +- return; +- } +- +- uint32_t src_width = m_Width; +- FX_SAFE_UINT32 pitch = +- fxcodec::CalculatePitch8(m_bpc, m_nComponents, m_Width); +- if (!pitch.IsValid()) +- return; +- +- const uint8_t* pSrcLine = nullptr; +- if (m_pCachedBitmap) { +- pSrcLine = m_pCachedBitmap->GetScanline(line); +- } else if (m_pDecoder) { +- pSrcLine = m_pDecoder->GetScanline(line); +- } else { +- uint32_t src_pitch = pitch.ValueOrDie(); +- pitch *= (line + 1); +- if (!pitch.IsValid()) { +- return; +- } +- +- if (m_pStreamAcc->GetSize() >= pitch.ValueOrDie()) { +- pSrcLine = m_pStreamAcc->GetData() + line * src_pitch; +- } +- } +- int orig_Bpp = m_bpc * m_nComponents / 8; +- int dest_Bpp = dest_bpp / 8; +- if (!pSrcLine) { +- memset(dest_scan, 0xFF, dest_Bpp * clip_width); +- return; +- } +- +- FX_SAFE_INT32 max_src_x = clip_left; +- max_src_x += clip_width - 1; +- max_src_x *= src_width; +- max_src_x /= dest_width; +- if (!max_src_x.IsValid()) +- return; +- +- if (m_bpc * m_nComponents == 1) { +- DownSampleScanline1Bit(orig_Bpp, dest_Bpp, src_width, pSrcLine, dest_scan, +- dest_width, bFlipX, clip_left, clip_width); +- } else if (m_bpc * m_nComponents <= 8) { +- DownSampleScanline8Bit(orig_Bpp, dest_Bpp, src_width, pSrcLine, dest_scan, +- dest_width, bFlipX, clip_left, clip_width); +- } else { +- DownSampleScanline32Bit(orig_Bpp, dest_Bpp, src_width, pSrcLine, dest_scan, +- dest_width, bFlipX, clip_left, clip_width); +- } +-} +- +-void CPDF_DIB::DownSampleScanline1Bit(int orig_Bpp, +- int dest_Bpp, +- uint32_t src_width, +- const uint8_t* pSrcLine, +- uint8_t* dest_scan, +- int dest_width, +- bool bFlipX, +- int clip_left, +- int clip_width) const { +- if (m_bColorKey && !m_bImageMask) { +- uint32_t reset_argb = m_pPalette ? m_pPalette.get()[0] : 0xFF000000; +- uint32_t set_argb = m_pPalette ? m_pPalette.get()[1] : 0xFFFFFFFF; +- if (m_CompData[0].m_ColorKeyMin == 0) +- reset_argb = 0; +- if (m_CompData[0].m_ColorKeyMax == 1) +- set_argb = 0; +- set_argb = FXARGB_TODIB(set_argb); +- reset_argb = FXARGB_TODIB(reset_argb); +- uint32_t* dest_scan_dword = reinterpret_cast(dest_scan); +- for (int i = 0; i < clip_width; i++) { +- uint32_t src_x = (clip_left + i) * src_width / dest_width; +- if (bFlipX) +- src_x = src_width - src_x - 1; +- src_x %= src_width; +- dest_scan_dword[i] = GetBitValue(pSrcLine, src_x) ? set_argb : reset_argb; +- } +- return; +- } +- +- uint32_t set_argb = 0xFFFFFFFF; +- uint32_t reset_argb = 0; +- if (m_bImageMask) { +- if (m_bDefaultDecode) { +- set_argb = 0; +- reset_argb = 0xFFFFFFFF; +- } +- } else if (m_pPalette && dest_Bpp != 1) { +- reset_argb = m_pPalette.get()[0]; +- set_argb = m_pPalette.get()[1]; +- } +- for (int i = 0; i < clip_width; i++) { +- uint32_t src_x = (clip_left + i) * src_width / dest_width; +- if (bFlipX) +- src_x = src_width - src_x - 1; +- src_x %= src_width; +- int dest_pos = i * dest_Bpp; +- uint32_t value_argb = GetBitValue(pSrcLine, src_x) ? set_argb : reset_argb; +- if (dest_Bpp == 1) { +- dest_scan[dest_pos] = static_cast(value_argb); +- } else if (dest_Bpp == 3) { +- dest_scan[dest_pos] = FXARGB_B(value_argb); +- dest_scan[dest_pos + 1] = FXARGB_G(value_argb); +- dest_scan[dest_pos + 2] = FXARGB_R(value_argb); +- } else { +- *reinterpret_cast(dest_scan + dest_pos) = value_argb; +- } +- } +-} +- +-void CPDF_DIB::DownSampleScanline8Bit(int orig_Bpp, +- int dest_Bpp, +- uint32_t src_width, +- const uint8_t* pSrcLine, +- uint8_t* dest_scan, +- int dest_width, +- bool bFlipX, +- int clip_left, +- int clip_width) const { +- if (m_bpc < 8) { +- uint64_t src_bit_pos = 0; +- for (uint32_t col = 0; col < src_width; col++) { +- unsigned int color_index = 0; +- for (uint32_t color = 0; color < m_nComponents; color++) { +- unsigned int data = GetBits8(pSrcLine, src_bit_pos, m_bpc); +- color_index |= data << (color * m_bpc); +- src_bit_pos += m_bpc; +- } +- m_pLineBuf.get()[col] = color_index; +- } +- pSrcLine = m_pLineBuf.get(); +- } +- if (m_bColorKey) { +- for (int i = 0; i < clip_width; i++) { +- uint32_t src_x = (clip_left + i) * src_width / dest_width; +- if (bFlipX) { +- src_x = src_width - src_x - 1; +- } +- src_x %= src_width; +- uint8_t* pDestPixel = dest_scan + i * 4; +- uint8_t index = pSrcLine[src_x]; +- if (m_pPalette) { +- *pDestPixel++ = FXARGB_B(m_pPalette.get()[index]); +- *pDestPixel++ = FXARGB_G(m_pPalette.get()[index]); +- *pDestPixel++ = FXARGB_R(m_pPalette.get()[index]); +- } else { +- *pDestPixel++ = index; +- *pDestPixel++ = index; +- *pDestPixel++ = index; +- } +- *pDestPixel = (index < m_CompData[0].m_ColorKeyMin || +- index > m_CompData[0].m_ColorKeyMax) +- ? 0xFF +- : 0; +- } +- return; +- } +- for (int i = 0; i < clip_width; i++) { +- uint32_t src_x = (clip_left + i) * src_width / dest_width; +- if (bFlipX) +- src_x = src_width - src_x - 1; +- src_x %= src_width; +- uint8_t index = pSrcLine[src_x]; +- if (dest_Bpp == 1) { +- dest_scan[i] = index; +- } else { +- int dest_pos = i * dest_Bpp; +- FX_ARGB argb = m_pPalette.get()[index]; +- dest_scan[dest_pos] = FXARGB_B(argb); +- dest_scan[dest_pos + 1] = FXARGB_G(argb); +- dest_scan[dest_pos + 2] = FXARGB_R(argb); +- } +- } +-} +- +-void CPDF_DIB::DownSampleScanline32Bit(int orig_Bpp, +- int dest_Bpp, +- uint32_t src_width, +- const uint8_t* pSrcLine, +- uint8_t* dest_scan, +- int dest_width, +- bool bFlipX, +- int clip_left, +- int clip_width) const { +- // last_src_x used to store the last seen src_x position which should be +- // in [0, src_width). Set the initial value to be an invalid src_x value. +- uint32_t last_src_x = src_width; +- FX_ARGB last_argb = ArgbEncode(0xFF, 0xFF, 0xFF, 0xFF); +- float unit_To8Bpc = 255.0f / ((1 << m_bpc) - 1); +- for (int i = 0; i < clip_width; i++) { +- int dest_x = clip_left + i; +- uint32_t src_x = (bFlipX ? (dest_width - dest_x - 1) : dest_x) * +- (int64_t)src_width / dest_width; +- src_x %= src_width; +- +- uint8_t* pDestPixel = dest_scan + i * dest_Bpp; +- FX_ARGB argb; +- if (src_x == last_src_x) { +- argb = last_argb; +- } else { +- CFX_FixedBufGrow extracted_components(m_nComponents); +- const uint8_t* pSrcPixel = nullptr; +- if (m_bpc % 8 != 0) { +- // No need to check for 32-bit overflow, as |src_x| is bounded by +- // |src_width| and DownSampleScanline() already checked for overflow +- // with the pitch calculation. +- size_t num_bits = src_x * m_bpc * m_nComponents; +- uint64_t src_bit_pos = num_bits % 8; +- pSrcPixel = pSrcLine + num_bits / 8; +- for (uint32_t j = 0; j < m_nComponents; ++j) { +- extracted_components[j] = static_cast( +- GetBits8(pSrcPixel, src_bit_pos, m_bpc) * unit_To8Bpc); +- src_bit_pos += m_bpc; +- } +- pSrcPixel = extracted_components; +- } else { +- pSrcPixel = pSrcLine + src_x * orig_Bpp; +- if (m_bpc == 16) { +- for (uint32_t j = 0; j < m_nComponents; ++j) +- extracted_components[j] = pSrcPixel[j * 2]; +- pSrcPixel = extracted_components; +- } +- } +- +- if (m_pColorSpace) { +- uint8_t color[4]; +- const bool bTransMask = TransMask(); +- if (!m_bDefaultDecode) { +- for (uint32_t j = 0; j < m_nComponents; ++j) { +- float component_value = static_cast(pSrcPixel[j]); +- int color_value = static_cast( +- (m_CompData[j].m_DecodeMin + +- m_CompData[j].m_DecodeStep * component_value) * +- 255.0f + +- 0.5f); +- extracted_components[j] = pdfium::clamp(color_value, 0, 255); +- } +- } +- const uint8_t* pSrc = +- m_bDefaultDecode ? pSrcPixel : extracted_components; +- m_pColorSpace->TranslateImageLine(color, pSrc, 1, 0, 0, bTransMask); +- argb = ArgbEncode(0xFF, color[2], color[1], color[0]); +- } else { +- argb = ArgbEncode(0xFF, pSrcPixel[2], pSrcPixel[1], pSrcPixel[0]); +- } +- if (m_bColorKey) { +- int alpha = 0xFF; +- if (m_nComponents == 3 && m_bpc == 8) { +- alpha = (pSrcPixel[0] < m_CompData[0].m_ColorKeyMin || +- pSrcPixel[0] > m_CompData[0].m_ColorKeyMax || +- pSrcPixel[1] < m_CompData[1].m_ColorKeyMin || +- pSrcPixel[1] > m_CompData[1].m_ColorKeyMax || +- pSrcPixel[2] < m_CompData[2].m_ColorKeyMin || +- pSrcPixel[2] > m_CompData[2].m_ColorKeyMax) +- ? 0xFF +- : 0; +- } +- argb &= 0xFFFFFF; +- argb |= alpha << 24; +- } +- last_src_x = src_x; +- last_argb = argb; +- } +- if (dest_Bpp == 4) { +- *reinterpret_cast(pDestPixel) = FXARGB_TODIB(argb); +- } else { +- *pDestPixel++ = FXARGB_B(argb); +- *pDestPixel++ = FXARGB_G(argb); +- *pDestPixel = FXARGB_R(argb); +- } +- } ++size_t CPDF_DIB::GetEstimatedImageMemoryBurden() const { ++ return m_pCachedBitmap ? m_pCachedBitmap->GetEstimatedImageMemoryBurden() : 0; + } + + bool CPDF_DIB::TransMask() const { +- return m_bLoadMask && m_GroupFamily == PDFCS_DEVICECMYK && +- m_Family == PDFCS_DEVICECMYK; ++ return m_bLoadMask && m_GroupFamily == CPDF_ColorSpace::Family::kDeviceCMYK && ++ m_Family == CPDF_ColorSpace::Family::kDeviceCMYK; + } + + void CPDF_DIB::SetMaskProperties() { +- m_bpp = 1; + m_bpc = 1; + m_nComponents = 1; +- m_AlphaFlag = 1; ++ m_Format = FXDIB_Format::k1bppMask; ++} ++ ++uint32_t CPDF_DIB::Get1BitSetValue() const { ++ if (m_CompData[0].m_ColorKeyMax == 1) ++ return 0x00000000; ++ return HasPalette() ? GetPaletteSpan()[1] : 0xFFFFFFFF; ++} ++ ++uint32_t CPDF_DIB::Get1BitResetValue() const { ++ if (m_CompData[0].m_ColorKeyMin == 0) ++ return 0x00000000; ++ return HasPalette() ? GetPaletteSpan()[0] : 0xFF000000; + } +diff --git a/core/fpdfapi/page/cpdf_dib.h b/core/fpdfapi/page/cpdf_dib.h +index 6960cea50..a24d192f5 100644 +--- a/core/fpdfapi/page/cpdf_dib.h ++++ b/core/fpdfapi/page/cpdf_dib.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,13 +7,13 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_DIB_H_ + #define CORE_FPDFAPI_PAGE_CPDF_DIB_H_ + ++#include ++ + #include + #include + +-#include "core/fpdfapi/page/cpdf_clippath.h" + #include "core/fpdfapi/page/cpdf_colorspace.h" +-#include "core/fpdfapi/page/cpdf_graphicstates.h" +-#include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + #include "core/fxge/dib/cfx_dibbase.h" +@@ -42,100 +42,79 @@ class CPDF_DIB final : public CFX_DIBBase { + public: + enum class LoadState : uint8_t { kFail, kSuccess, kContinue }; + +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- +- bool Load(CPDF_Document* pDoc, const CPDF_Stream* pStream); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // CFX_DIBBase: ++ pdfium::span GetBuffer() const override; ++ pdfium::span GetScanline(int line) const override; + bool SkipToScanline(int line, PauseIndicatorIface* pPause) const override; +- uint8_t* GetBuffer() const override; +- const uint8_t* GetScanline(int line) const override; +- void DownSampleScanline(int line, +- uint8_t* dest_scan, +- int dest_bpp, +- int dest_width, +- bool bFlipX, +- int clip_left, +- int clip_width) const override; ++ size_t GetEstimatedImageMemoryBurden() const override; + + RetainPtr GetColorSpace() const { return m_pColorSpace; } + uint32_t GetMatteColor() const { return m_MatteColor; } ++ bool IsJBigImage() const; + +- LoadState StartLoadDIBBase(CPDF_Document* pDoc, +- const CPDF_Stream* pStream, +- bool bHasMask, ++ bool Load(); ++ LoadState StartLoadDIBBase(bool bHasMask, + const CPDF_Dictionary* pFormResources, +- CPDF_Dictionary* pPageResources, ++ const CPDF_Dictionary* pPageResources, + bool bStdCS, +- uint32_t GroupFamily, +- bool bLoadMask); ++ CPDF_ColorSpace::Family GroupFamily, ++ bool bLoadMask, ++ const CFX_Size& max_size_required); + LoadState ContinueLoadDIBBase(PauseIndicatorIface* pPause); + RetainPtr DetachMask(); + +- bool IsJBigImage() const; +- + private: +- CPDF_DIB(); ++ CPDF_DIB(CPDF_Document* pDoc, RetainPtr pStream); + ~CPDF_DIB() override; + ++ struct JpxSMaskInlineData { ++ JpxSMaskInlineData(); ++ ~JpxSMaskInlineData(); ++ ++ int width = 0; ++ int height = 0; ++ DataVector data; ++ }; ++ ++ bool LoadInternal(const CPDF_Dictionary* pFormResources, ++ const CPDF_Dictionary* pPageResources); ++ bool ContinueInternal(); + LoadState StartLoadMask(); +- LoadState StartLoadMaskDIB(RetainPtr mask); ++ LoadState StartLoadMaskDIB(RetainPtr mask_stream); + bool ContinueToLoadMask(); + LoadState ContinueLoadMaskDIB(PauseIndicatorIface* pPause); + bool LoadColorInfo(const CPDF_Dictionary* pFormResources, + const CPDF_Dictionary* pPageResources); +- bool GetDecodeAndMaskArray(bool* bDefaultDecode, bool* bColorKey); +- RetainPtr LoadJpxBitmap(); ++ bool GetDecodeAndMaskArray(); ++ RetainPtr LoadJpxBitmap(uint8_t resolution_levels_to_skip); + void LoadPalette(); +- LoadState CreateDecoder(); ++ LoadState CreateDecoder(uint8_t resolution_levels_to_skip); + bool CreateDCTDecoder(pdfium::span src_span, + const CPDF_Dictionary* pParams); +- void TranslateScanline24bpp(uint8_t* dest_scan, +- const uint8_t* src_scan) const; +- bool TranslateScanline24bppDefaultDecode(uint8_t* dest_scan, +- const uint8_t* src_scan) const; +- void ValidateDictParam(); +- void DownSampleScanline1Bit(int orig_Bpp, +- int dest_Bpp, +- uint32_t src_width, +- const uint8_t* pSrcLine, +- uint8_t* dest_scan, +- int dest_width, +- bool bFlipX, +- int clip_left, +- int clip_width) const; +- void DownSampleScanline8Bit(int orig_Bpp, +- int dest_Bpp, +- uint32_t src_width, +- const uint8_t* pSrcLine, +- uint8_t* dest_scan, +- int dest_width, +- bool bFlipX, +- int clip_left, +- int clip_width) const; +- void DownSampleScanline32Bit(int orig_Bpp, +- int dest_Bpp, +- uint32_t src_width, +- const uint8_t* pSrcLine, +- uint8_t* dest_scan, +- int dest_width, +- bool bFlipX, +- int clip_left, +- int clip_width) const; ++ void TranslateScanline24bpp(pdfium::span dest_scan, ++ pdfium::span src_scan) const; ++ bool TranslateScanline24bppDefaultDecode( ++ pdfium::span dest_scan, ++ pdfium::span src_scan) const; ++ bool ValidateDictParam(const ByteString& filter); + bool TransMask() const; + void SetMaskProperties(); + +- UnownedPtr m_pDocument; +- RetainPtr m_pStream; ++ uint32_t Get1BitSetValue() const; ++ uint32_t Get1BitResetValue() const; ++ ++ UnownedPtr const m_pDocument; ++ RetainPtr const m_pStream; + RetainPtr m_pDict; + RetainPtr m_pStreamAcc; + RetainPtr m_pColorSpace; +- uint32_t m_Family = 0; + uint32_t m_bpc = 0; + uint32_t m_bpc_orig = 0; + uint32_t m_nComponents = 0; +- uint32_t m_GroupFamily = 0; ++ CPDF_ColorSpace::Family m_Family = CPDF_ColorSpace::Family::kUnknown; ++ CPDF_ColorSpace::Family m_GroupFamily = CPDF_ColorSpace::Family::kUnknown; + uint32_t m_MatteColor = 0; + LoadState m_Status = LoadState::kFail; + bool m_bLoadMask = false; +@@ -146,13 +125,14 @@ class CPDF_DIB final : public CFX_DIBBase { + bool m_bHasMask = false; + bool m_bStdCS = false; + std::vector m_CompData; +- std::unique_ptr m_pLineBuf; +- std::unique_ptr m_pMaskedLine; ++ mutable DataVector m_LineBuf; ++ mutable DataVector m_MaskBuf; + RetainPtr m_pCachedBitmap; + // Note: Must not create a cycle between CPDF_DIB instances. + RetainPtr m_pMask; + RetainPtr m_pGlobalAcc; + std::unique_ptr m_pDecoder; ++ JpxSMaskInlineData m_JpxInlineData; + + // Must come after |m_pCachedBitmap|. + std::unique_ptr m_pJbig2Context; +diff --git a/core/fpdfapi/page/cpdf_docpagedata.cpp b/core/fpdfapi/page/cpdf_docpagedata.cpp +index 9c8bc92ea..b00a616b5 100644 +--- a/core/fpdfapi/page/cpdf_docpagedata.cpp ++++ b/core/fpdfapi/page/cpdf_docpagedata.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,6 +13,7 @@ + #include + + #include "build/build_config.h" ++#include "constants/font_encodings.h" + #include "core/fpdfapi/font/cpdf_type1font.h" + #include "core/fpdfapi/page/cpdf_form.h" + #include "core/fpdfapi/page/cpdf_iccprofile.h" +@@ -30,14 +31,16 @@ + #include "core/fpdfapi/parser/cpdf_stream_acc.h" + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fxcrt/fx_codepage.h" ++#include "core/fxcrt/fx_memory.h" + #include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/scoped_set_insertion.h" + #include "core/fxge/cfx_font.h" + #include "core/fxge/cfx_fontmapper.h" + #include "core/fxge/cfx_substfont.h" + #include "core/fxge/cfx_unicodeencoding.h" + #include "core/fxge/fx_font.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" + + namespace { + +@@ -49,17 +52,17 @@ void InsertWidthArrayImpl(std::vector widths, CPDF_Array* pWidthArray) { + } + if (i == widths.size()) { + int first = pWidthArray->GetIntegerAt(pWidthArray->size() - 1); +- pWidthArray->AddNew(first + static_cast(widths.size()) - +- 1); +- pWidthArray->AddNew(widths[0]); ++ pWidthArray->AppendNew(first + ++ static_cast(widths.size()) - 1); ++ pWidthArray->AppendNew(widths[0]); + return; + } +- CPDF_Array* pWidthArray1 = pWidthArray->AddNew(); ++ auto pWidthArray1 = pWidthArray->AppendNew(); + for (int w : widths) +- pWidthArray1->AddNew(w); ++ pWidthArray1->AppendNew(w); + } + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + void InsertWidthArray(HDC hDC, int start, int end, CPDF_Array* pWidthArray) { + std::vector widths(end - start + 1); + GetCharWidth(hDC, start, end, widths.data()); +@@ -77,7 +80,7 @@ ByteString GetPSNameFromTT(HDC hDC) { + } + return result; + } +-#endif // defined(OS_WIN) ++#endif // BUILDFLAG(IS_WIN) + + void InsertWidthArray1(CFX_Font* pFont, + CFX_UnicodeEncoding* pEncoding, +@@ -116,7 +119,7 @@ int CalculateFlags(bool bold, + return flags; + } + +-void ProcessNonbCJK(CPDF_Dictionary* pBaseDict, ++void ProcessNonbCJK(RetainPtr pBaseDict, + bool bold, + bool italic, + ByteString basefont, +@@ -174,7 +177,8 @@ void CPDF_DocPageData::ClearStockFont() { + CPDF_PageModule::GetInstance()->ClearStockFont(GetDocument()); + } + +-RetainPtr CPDF_DocPageData::GetFont(CPDF_Dictionary* pFontDict) { ++RetainPtr CPDF_DocPageData::GetFont( ++ RetainPtr pFontDict) { + if (!pFontDict) + return nullptr; + +@@ -187,7 +191,7 @@ RetainPtr CPDF_DocPageData::GetFont(CPDF_Dictionary* pFontDict) { + if (!pFont) + return nullptr; + +- m_FontMap[pFontDict].Reset(pFont.Get()); ++ m_FontMap[std::move(pFontDict)].Reset(pFont.Get()); + return pFont; + } + +@@ -217,7 +221,7 @@ RetainPtr CPDF_DocPageData::GetStandardFont( + return pdfium::WrapRetain(pFont); + } + +- CPDF_Dictionary* pDict = GetDocument()->NewIndirect(); ++ auto pDict = GetDocument()->NewIndirect(); + pDict->SetNewFor("Type", "Font"); + pDict->SetNewFor("Subtype", "Type1"); + pDict->SetNewFor("BaseFont", fontName); +@@ -231,7 +235,7 @@ RetainPtr CPDF_DocPageData::GetStandardFont( + if (!pFont) + return nullptr; + +- m_FontMap[pDict].Reset(pFont.Get()); ++ m_FontMap[std::move(pDict)].Reset(pFont.Get()); + return pFont; + } + +@@ -258,73 +262,76 @@ RetainPtr CPDF_DocPageData::GetColorSpaceInternal( + if (!pCSObj) + return nullptr; + +- if (pdfium::ContainsKey(*pVisitedInternal, pCSObj)) ++ if (pdfium::Contains(*pVisitedInternal, pCSObj)) + return nullptr; + +- pdfium::ScopedSetInsertion insertion(pVisitedInternal, +- pCSObj); ++ ScopedSetInsertion insertion(pVisitedInternal, pCSObj); + + if (pCSObj->IsName()) { + ByteString name = pCSObj->GetString(); +- RetainPtr pCS = CPDF_ColorSpace::ColorspaceFromName(name); ++ RetainPtr pCS = CPDF_ColorSpace::GetStockCSForName(name); + if (!pCS && pResources) { +- const CPDF_Dictionary* pList = pResources->GetDictFor("ColorSpace"); ++ RetainPtr pList = ++ pResources->GetDictFor("ColorSpace"); + if (pList) { +- return GetColorSpaceInternal(pList->GetDirectObjectFor(name), nullptr, +- pVisited, pVisitedInternal); ++ return GetColorSpaceInternal(pList->GetDirectObjectFor(name).Get(), ++ nullptr, pVisited, pVisitedInternal); + } + } + if (!pCS || !pResources) + return pCS; + +- const CPDF_Dictionary* pColorSpaces = pResources->GetDictFor("ColorSpace"); ++ RetainPtr pColorSpaces = ++ pResources->GetDictFor("ColorSpace"); + if (!pColorSpaces) + return pCS; + +- const CPDF_Object* pDefaultCS = nullptr; ++ RetainPtr pDefaultCS; + switch (pCS->GetFamily()) { +- case PDFCS_DEVICERGB: ++ case CPDF_ColorSpace::Family::kDeviceRGB: + pDefaultCS = pColorSpaces->GetDirectObjectFor("DefaultRGB"); + break; +- case PDFCS_DEVICEGRAY: ++ case CPDF_ColorSpace::Family::kDeviceGray: + pDefaultCS = pColorSpaces->GetDirectObjectFor("DefaultGray"); + break; +- case PDFCS_DEVICECMYK: ++ case CPDF_ColorSpace::Family::kDeviceCMYK: + pDefaultCS = pColorSpaces->GetDirectObjectFor("DefaultCMYK"); + break; ++ default: ++ break; + } + if (!pDefaultCS) + return pCS; + +- return GetColorSpaceInternal(pDefaultCS, nullptr, pVisited, ++ return GetColorSpaceInternal(pDefaultCS.Get(), nullptr, pVisited, + pVisitedInternal); + } + +- const CPDF_Array* pArray = pCSObj->AsArray(); ++ RetainPtr pArray(pCSObj->AsArray()); + if (!pArray || pArray->IsEmpty()) + return nullptr; + + if (pArray->size() == 1) { +- return GetColorSpaceInternal(pArray->GetDirectObjectAt(0), pResources, ++ return GetColorSpaceInternal(pArray->GetDirectObjectAt(0).Get(), pResources, + pVisited, pVisitedInternal); + } + +- auto it = m_ColorSpaceMap.find(pCSObj); ++ auto it = m_ColorSpaceMap.find(pArray); + if (it != m_ColorSpaceMap.end() && it->second) + return pdfium::WrapRetain(it->second.Get()); + + RetainPtr pCS = +- CPDF_ColorSpace::Load(GetDocument(), pArray, pVisited); ++ CPDF_ColorSpace::Load(GetDocument(), pArray.Get(), pVisited); + if (!pCS) + return nullptr; + +- m_ColorSpaceMap[pCSObj].Reset(pCS.Get()); ++ m_ColorSpaceMap[std::move(pArray)].Reset(pCS.Get()); + return pCS; + } + +-RetainPtr CPDF_DocPageData::GetPattern(CPDF_Object* pPatternObj, +- bool bShading, +- const CFX_Matrix& matrix) { ++RetainPtr CPDF_DocPageData::GetPattern( ++ RetainPtr pPatternObj, ++ const CFX_Matrix& matrix) { + if (!pPatternObj) + return nullptr; + +@@ -332,33 +339,43 @@ RetainPtr CPDF_DocPageData::GetPattern(CPDF_Object* pPatternObj, + if (it != m_PatternMap.end() && it->second) + return pdfium::WrapRetain(it->second.Get()); + ++ RetainPtr pDict = pPatternObj->GetDict(); ++ if (!pDict) ++ return nullptr; ++ + RetainPtr pPattern; +- if (bShading) { ++ int type = pDict->GetIntegerFor("PatternType"); ++ if (type == CPDF_Pattern::kTiling) { ++ pPattern = pdfium::MakeRetain(GetDocument(), ++ pPatternObj, matrix); ++ } else if (type == CPDF_Pattern::kShading) { + pPattern = pdfium::MakeRetain( +- GetDocument(), pPatternObj, true, matrix); ++ GetDocument(), pPatternObj, false, matrix); + } else { +- CPDF_Dictionary* pDict = pPatternObj->GetDict(); +- if (!pDict) +- return nullptr; +- +- int type = pDict->GetIntegerFor("PatternType"); +- if (type == CPDF_Pattern::kTiling) { +- pPattern = pdfium::MakeRetain(GetDocument(), +- pPatternObj, matrix); +- } else if (type == CPDF_Pattern::kShading) { +- pPattern = pdfium::MakeRetain( +- GetDocument(), pPatternObj, false, matrix); +- } else { +- return nullptr; +- } ++ return nullptr; + } ++ m_PatternMap[pPatternObj].Reset(pPattern.Get()); ++ return pPattern; ++} ++ ++RetainPtr CPDF_DocPageData::GetShading( ++ RetainPtr pPatternObj, ++ const CFX_Matrix& matrix) { ++ if (!pPatternObj) ++ return nullptr; ++ ++ auto it = m_PatternMap.find(pPatternObj); ++ if (it != m_PatternMap.end() && it->second) ++ return pdfium::WrapRetain(it->second->AsShadingPattern()); + ++ auto pPattern = pdfium::MakeRetain( ++ GetDocument(), pPatternObj, true, matrix); + m_PatternMap[pPatternObj].Reset(pPattern.Get()); + return pPattern; + } + + RetainPtr CPDF_DocPageData::GetImage(uint32_t dwStreamObjNum) { +- ASSERT(dwStreamObjNum); ++ DCHECK(dwStreamObjNum); + auto it = m_ImageMap.find(dwStreamObjNum); + if (it != m_ImageMap.end()) + return it->second; +@@ -369,14 +386,14 @@ RetainPtr CPDF_DocPageData::GetImage(uint32_t dwStreamObjNum) { + } + + void CPDF_DocPageData::MaybePurgeImage(uint32_t dwStreamObjNum) { +- ASSERT(dwStreamObjNum); ++ DCHECK(dwStreamObjNum); + auto it = m_ImageMap.find(dwStreamObjNum); + if (it != m_ImageMap.end() && it->second->HasOneRef()) + m_ImageMap.erase(it); + } + + RetainPtr CPDF_DocPageData::GetIccProfile( +- const CPDF_Stream* pProfileStream) { ++ RetainPtr pProfileStream) { + if (!pProfileStream) + return nullptr; + +@@ -390,25 +407,25 @@ RetainPtr CPDF_DocPageData::GetIccProfile( + ByteString bsDigest = pAccessor->ComputeDigest(); + auto hash_it = m_HashProfileMap.find(bsDigest); + if (hash_it != m_HashProfileMap.end()) { +- auto it_copied_stream = m_IccProfileMap.find(hash_it->second.Get()); ++ auto it_copied_stream = m_IccProfileMap.find(hash_it->second); + if (it_copied_stream != m_IccProfileMap.end() && it_copied_stream->second) + return pdfium::WrapRetain(it_copied_stream->second.Get()); + } + auto pProfile = + pdfium::MakeRetain(pProfileStream, pAccessor->GetSpan()); + m_IccProfileMap[pProfileStream].Reset(pProfile.Get()); +- m_HashProfileMap[bsDigest].Reset(pProfileStream); ++ m_HashProfileMap[bsDigest] = std::move(pProfileStream); + return pProfile; + } + + RetainPtr CPDF_DocPageData::GetFontFileStreamAcc( +- const CPDF_Stream* pFontStream) { +- ASSERT(pFontStream); ++ RetainPtr pFontStream) { ++ DCHECK(pFontStream); + auto it = m_FontFileMap.find(pFontStream); + if (it != m_FontFileMap.end()) + return it->second; + +- const CPDF_Dictionary* pFontDict = pFontStream->GetDict(); ++ RetainPtr pFontDict = pFontStream->GetDict(); + int32_t len1 = pFontDict->GetIntegerFor("Length1"); + int32_t len2 = pFontDict->GetIntegerFor("Length2"); + int32_t len3 = pFontDict->GetIntegerFor("Length3"); +@@ -422,38 +439,46 @@ RetainPtr CPDF_DocPageData::GetFontFileStreamAcc( + + auto pFontAcc = pdfium::MakeRetain(pFontStream); + pFontAcc->LoadAllDataFilteredWithEstimatedSize(org_size); +- m_FontFileMap[pFontStream] = pFontAcc; ++ m_FontFileMap[std::move(pFontStream)] = pFontAcc; + return pFontAcc; + } + + void CPDF_DocPageData::MaybePurgeFontFileStreamAcc( +- const CPDF_Stream* pFontStream) { ++ RetainPtr&& pStreamAcc) { ++ if (!pStreamAcc) ++ return; ++ ++ RetainPtr pFontStream = pStreamAcc->GetStream(); + if (!pFontStream) + return; + +- auto it = m_FontFileMap.find(pFontStream); ++ pStreamAcc.Reset(); // Drop moved caller's reference. ++ auto it = m_FontFileMap.find(std::move(pFontStream)); + if (it != m_FontFileMap.end() && it->second->HasOneRef()) + m_FontFileMap.erase(it); + } + + std::unique_ptr CPDF_DocPageData::CreateForm( + CPDF_Document* pDocument, +- CPDF_Dictionary* pPageResources, +- CPDF_Stream* pFormStream) { +- return pdfium::MakeUnique(pDocument, pPageResources, pFormStream); ++ RetainPtr pPageResources, ++ RetainPtr pFormStream) { ++ return std::make_unique(pDocument, std::move(pPageResources), ++ std::move(pFormStream)); + } + + RetainPtr CPDF_DocPageData::AddStandardFont( + const ByteString& fontName, + const CPDF_FontEncoding* pEncoding) { + ByteString mutable_name(fontName); +- if (!CFX_FontMapper::GetStandardFontName(&mutable_name)) ++ absl::optional font_id = ++ CFX_FontMapper::GetStandardFontName(&mutable_name); ++ if (!font_id.has_value()) + return nullptr; + return GetStandardFont(mutable_name, pEncoding); + } + + RetainPtr CPDF_DocPageData::AddFont(std::unique_ptr pFont, +- int charset) { ++ FX_Charset charset) { + if (!pFont) + return nullptr; + +@@ -462,35 +487,37 @@ RetainPtr CPDF_DocPageData::AddFont(std::unique_ptr pFont, + basefont.Replace(" ", ""); + int flags = + CalculateFlags(pFont->IsBold(), pFont->IsItalic(), pFont->IsFixedWidth(), +- false, false, charset == FX_CHARSET_Symbol); ++ false, false, charset == FX_Charset::kSymbol); + +- CPDF_Dictionary* pBaseDict = GetDocument()->NewIndirect(); ++ auto pBaseDict = GetDocument()->NewIndirect(); + pBaseDict->SetNewFor("Type", "Font"); +- auto pEncoding = pdfium::MakeUnique(pFont.get()); +- CPDF_Dictionary* pFontDict = pBaseDict; ++ ++ auto pEncoding = std::make_unique(pFont.get()); ++ RetainPtr pFontDict = pBaseDict; + if (!bCJK) { + auto pWidths = pdfium::MakeRetain(); + for (int charcode = 32; charcode < 128; charcode++) { + int glyph_index = pEncoding->GlyphFromCharCode(charcode); + int char_width = pFont->GetGlyphWidth(glyph_index); +- pWidths->AddNew(char_width); ++ pWidths->AppendNew(char_width); + } +- if (charset == FX_CHARSET_ANSI || charset == FX_CHARSET_Default || +- charset == FX_CHARSET_Symbol) { +- pBaseDict->SetNewFor("Encoding", "WinAnsiEncoding"); ++ if (charset == FX_Charset::kANSI || charset == FX_Charset::kDefault || ++ charset == FX_Charset::kSymbol) { ++ pBaseDict->SetNewFor("Encoding", ++ pdfium::font_encodings::kWinAnsiEncoding); + for (int charcode = 128; charcode <= 255; charcode++) { + int glyph_index = pEncoding->GlyphFromCharCode(charcode); + int char_width = pFont->GetGlyphWidth(glyph_index); +- pWidths->AddNew(char_width); ++ pWidths->AppendNew(char_width); + } + } else { +- size_t i = CalculateEncodingDict(charset, pBaseDict); +- if (i < FX_ArraySize(g_FX_CharsetUnicodes)) { +- const uint16_t* pUnicodes = g_FX_CharsetUnicodes[i].m_pUnicodes; ++ size_t i = CalculateEncodingDict(charset, pBaseDict.Get()); ++ if (i < std::size(kFX_CharsetUnicodes)) { ++ const uint16_t* pUnicodes = kFX_CharsetUnicodes[i].m_pUnicodes; + for (int j = 0; j < 128; j++) { + int glyph_index = pEncoding->GlyphFromCharCode(pUnicodes[j]); + int char_width = pFont->GetGlyphWidth(glyph_index); +- pWidths->AddNew(char_width); ++ pWidths->AppendNew(char_width); + } + } + } +@@ -504,19 +531,18 @@ RetainPtr CPDF_DocPageData::AddFont(std::unique_ptr pFont, + }); + } + int italicangle = pFont->GetSubstFontItalicAngle(); +- FX_RECT bbox; +- pFont->GetBBox(&bbox); ++ FX_RECT bbox = pFont->GetBBox().value_or(FX_RECT()); + auto pBBox = pdfium::MakeRetain(); +- pBBox->AddNew(bbox.left); +- pBBox->AddNew(bbox.bottom); +- pBBox->AddNew(bbox.right); +- pBBox->AddNew(bbox.top); ++ pBBox->AppendNew(bbox.left); ++ pBBox->AppendNew(bbox.bottom); ++ pBBox->AppendNew(bbox.right); ++ pBBox->AppendNew(bbox.top); + int32_t nStemV = 0; + if (pFont->GetSubstFont()) { + nStemV = pFont->GetSubstFont()->m_Weight / 5; + } else { + static const char stem_chars[] = {'i', 'I', '!', '1'}; +- const size_t count = FX_ArraySize(stem_chars); ++ const size_t count = std::size(stem_chars); + uint32_t glyph = pEncoding->GlyphFromCharCode(stem_chars[0]); + nStemV = pFont->GetGlyphWidth(glyph); + for (size_t i = 1; i < count; i++) { +@@ -526,16 +552,16 @@ RetainPtr CPDF_DocPageData::AddFont(std::unique_ptr pFont, + nStemV = width; + } + } +- CPDF_Dictionary* pFontDesc = +- ToDictionary(GetDocument()->AddIndirectObject(CalculateFontDesc( +- GetDocument(), basefont, flags, italicangle, pFont->GetAscent(), +- pFont->GetDescent(), std::move(pBBox), nStemV))); ++ RetainPtr pFontDesc = CalculateFontDesc( ++ GetDocument(), basefont, flags, italicangle, pFont->GetAscent(), ++ pFont->GetDescent(), std::move(pBBox), nStemV); ++ uint32_t new_objnum = GetDocument()->AddIndirectObject(std::move(pFontDesc)); + pFontDict->SetNewFor("FontDescriptor", GetDocument(), +- pFontDesc->GetObjNum()); ++ new_objnum); + return GetFont(pBaseDict); + } + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + RetainPtr CPDF_DocPageData::AddWindowsFont(LOGFONTA* pLogFont) { + pLogFont->lfHeight = -1000; + pLogFont->lfWidth = 0; +@@ -553,13 +579,15 @@ RetainPtr CPDF_DocPageData::AddWindowsFont(LOGFONTA* pLogFont) { + LPBYTE tm_buf = FX_Alloc(BYTE, tm_size); + OUTLINETEXTMETRIC* ptm = reinterpret_cast(tm_buf); + GetOutlineTextMetrics(hDC, tm_size, ptm); +- int flags = CalculateFlags(false, pLogFont->lfItalic != 0, +- (pLogFont->lfPitchAndFamily & 3) == FIXED_PITCH, +- (pLogFont->lfPitchAndFamily & 0xf8) == FF_ROMAN, +- (pLogFont->lfPitchAndFamily & 0xf8) == FF_SCRIPT, +- pLogFont->lfCharSet == FX_CHARSET_Symbol); +- +- const bool bCJK = FX_CharSetIsCJK(pLogFont->lfCharSet); ++ int flags = CalculateFlags( ++ false, pLogFont->lfItalic != 0, ++ (pLogFont->lfPitchAndFamily & 3) == FIXED_PITCH, ++ (pLogFont->lfPitchAndFamily & 0xf8) == FF_ROMAN, ++ (pLogFont->lfPitchAndFamily & 0xf8) == FF_SCRIPT, ++ pLogFont->lfCharSet == static_cast(FX_Charset::kSymbol)); ++ ++ const FX_Charset eCharset = FX_GetCharsetFromInt(pLogFont->lfCharSet); ++ const bool bCJK = FX_CharSetIsCJK(eCharset); + ByteString basefont; + if (bCJK) + basefont = GetPSNameFromTT(hDC); +@@ -575,122 +603,122 @@ RetainPtr CPDF_DocPageData::AddWindowsFont(LOGFONTA* pLogFont) { + ptm->otmrcFontBox.right, ptm->otmrcFontBox.top}; + FX_Free(tm_buf); + basefont.Replace(" ", ""); +- CPDF_Dictionary* pBaseDict = GetDocument()->NewIndirect(); ++ auto pBaseDict = GetDocument()->NewIndirect(); + pBaseDict->SetNewFor("Type", "Font"); +- CPDF_Dictionary* pFontDict = pBaseDict; ++ RetainPtr pFontDict = pBaseDict; + if (!bCJK) { +- if (pLogFont->lfCharSet == FX_CHARSET_ANSI || +- pLogFont->lfCharSet == FX_CHARSET_Default || +- pLogFont->lfCharSet == FX_CHARSET_Symbol) { +- pBaseDict->SetNewFor("Encoding", "WinAnsiEncoding"); ++ if (eCharset == FX_Charset::kANSI || eCharset == FX_Charset::kDefault || ++ eCharset == FX_Charset::kSymbol) { ++ pBaseDict->SetNewFor("Encoding", ++ pdfium::font_encodings::kWinAnsiEncoding); + } else { +- CalculateEncodingDict(pLogFont->lfCharSet, pBaseDict); ++ CalculateEncodingDict(eCharset, pBaseDict.Get()); + } + int char_widths[224]; + GetCharWidth(hDC, 32, 255, char_widths); + auto pWidths = pdfium::MakeRetain(); + for (size_t i = 0; i < 224; i++) +- pWidths->AddNew(char_widths[i]); ++ pWidths->AppendNew(char_widths[i]); + ProcessNonbCJK(pBaseDict, pLogFont->lfWeight > FW_MEDIUM, + pLogFont->lfItalic != 0, basefont, std::move(pWidths)); + } else { + pFontDict = +- ProcessbCJK(pBaseDict, pLogFont->lfCharSet, basefont, ++ ProcessbCJK(pBaseDict, eCharset, basefont, + [&hDC](wchar_t start, wchar_t end, CPDF_Array* widthArr) { + InsertWidthArray(hDC, start, end, widthArr); + }); + } + auto pBBox = pdfium::MakeRetain(); + for (int i = 0; i < 4; i++) +- pBBox->AddNew(bbox[i]); ++ pBBox->AppendNew(bbox[i]); + RetainPtr pFontDesc = + CalculateFontDesc(GetDocument(), basefont, flags, italicangle, ascend, + descend, std::move(pBBox), pLogFont->lfWeight / 5); + pFontDesc->SetNewFor("CapHeight", capheight); +- pFontDict->SetFor("FontDescriptor", +- GetDocument() +- ->AddIndirectObject(std::move(pFontDesc)) +- ->MakeReference(GetDocument())); ++ GetDocument()->AddIndirectObject(pFontDesc); ++ pFontDict->SetFor("FontDescriptor", pFontDesc->MakeReference(GetDocument())); + hFont = SelectObject(hDC, hFont); + DeleteObject(hFont); + DeleteDC(hDC); +- return GetFont(pBaseDict); ++ return GetFont(std::move(pBaseDict)); + } +-#endif // defined(OS_WIN) ++#endif // BUILDFLAG(IS_WIN) + +-size_t CPDF_DocPageData::CalculateEncodingDict(int charset, ++size_t CPDF_DocPageData::CalculateEncodingDict(FX_Charset charset, + CPDF_Dictionary* pBaseDict) { + size_t i; +- for (i = 0; i < FX_ArraySize(g_FX_CharsetUnicodes); ++i) { +- if (g_FX_CharsetUnicodes[i].m_Charset == charset) ++ for (i = 0; i < std::size(kFX_CharsetUnicodes); ++i) { ++ if (kFX_CharsetUnicodes[i].m_Charset == charset) + break; + } +- if (i == FX_ArraySize(g_FX_CharsetUnicodes)) ++ if (i == std::size(kFX_CharsetUnicodes)) + return i; + +- CPDF_Dictionary* pEncodingDict = +- GetDocument()->NewIndirect(); +- pEncodingDict->SetNewFor("BaseEncoding", "WinAnsiEncoding"); ++ auto pEncodingDict = GetDocument()->NewIndirect(); ++ pEncodingDict->SetNewFor("BaseEncoding", ++ pdfium::font_encodings::kWinAnsiEncoding); + +- CPDF_Array* pArray = pEncodingDict->SetNewFor("Differences"); +- pArray->AddNew(128); ++ auto pArray = pEncodingDict->SetNewFor("Differences"); ++ pArray->AppendNew(128); + +- const uint16_t* pUnicodes = g_FX_CharsetUnicodes[i].m_pUnicodes; ++ const uint16_t* pUnicodes = kFX_CharsetUnicodes[i].m_pUnicodes; + for (int j = 0; j < 128; j++) { +- ByteString name = PDF_AdobeNameFromUnicode(pUnicodes[j]); +- pArray->AddNew(name.IsEmpty() ? ".notdef" : name); ++ ByteString name = AdobeNameFromUnicode(pUnicodes[j]); ++ pArray->AppendNew(name.IsEmpty() ? ".notdef" : name); + } + pBaseDict->SetNewFor("Encoding", GetDocument(), + pEncodingDict->GetObjNum()); + return i; + } + +-CPDF_Dictionary* CPDF_DocPageData::ProcessbCJK( +- CPDF_Dictionary* pBaseDict, +- int charset, ++RetainPtr CPDF_DocPageData::ProcessbCJK( ++ RetainPtr pBaseDict, ++ FX_Charset charset, + ByteString basefont, + std::function Insert) { +- CPDF_Dictionary* pFontDict = GetDocument()->NewIndirect(); ++ auto pFontDict = GetDocument()->NewIndirect(); + ByteString cmap; + ByteString ordering; + int supplement = 0; +- CPDF_Array* pWidthArray = pFontDict->SetNewFor("W"); ++ auto pWidthArray = pFontDict->SetNewFor("W"); + switch (charset) { +- case FX_CHARSET_ChineseTraditional: ++ case FX_Charset::kChineseTraditional: + cmap = "ETenms-B5-H"; + ordering = "CNS1"; + supplement = 4; +- pWidthArray->AddNew(1); +- Insert(0x20, 0x7e, pWidthArray); ++ pWidthArray->AppendNew(1); ++ Insert(0x20, 0x7e, pWidthArray.Get()); + break; +- case FX_CHARSET_ChineseSimplified: ++ case FX_Charset::kChineseSimplified: + cmap = "GBK-EUC-H"; + ordering = "GB1"; + supplement = 2; +- pWidthArray->AddNew(7716); +- Insert(0x20, 0x20, pWidthArray); +- pWidthArray->AddNew(814); +- Insert(0x21, 0x7e, pWidthArray); ++ pWidthArray->AppendNew(7716); ++ Insert(0x20, 0x20, pWidthArray.Get()); ++ pWidthArray->AppendNew(814); ++ Insert(0x21, 0x7e, pWidthArray.Get()); + break; +- case FX_CHARSET_Hangul: ++ case FX_Charset::kHangul: + cmap = "KSCms-UHC-H"; + ordering = "Korea1"; + supplement = 2; +- pWidthArray->AddNew(1); +- Insert(0x20, 0x7e, pWidthArray); ++ pWidthArray->AppendNew(1); ++ Insert(0x20, 0x7e, pWidthArray.Get()); + break; +- case FX_CHARSET_ShiftJIS: ++ case FX_Charset::kShiftJIS: + cmap = "90ms-RKSJ-H"; + ordering = "Japan1"; + supplement = 5; +- pWidthArray->AddNew(231); +- Insert(0x20, 0x7d, pWidthArray); +- pWidthArray->AddNew(326); +- Insert(0xa0, 0xa0, pWidthArray); +- pWidthArray->AddNew(327); +- Insert(0xa1, 0xdf, pWidthArray); +- pWidthArray->AddNew(631); +- Insert(0x7e, 0x7e, pWidthArray); ++ pWidthArray->AppendNew(231); ++ Insert(0x20, 0x7d, pWidthArray.Get()); ++ pWidthArray->AppendNew(326); ++ Insert(0xa0, 0xa0, pWidthArray.Get()); ++ pWidthArray->AppendNew(327); ++ Insert(0xa1, 0xdf, pWidthArray.Get()); ++ pWidthArray->AppendNew(631); ++ Insert(0x7e, 0x7e, pWidthArray.Get()); ++ break; ++ default: + break; + } + pBaseDict->SetNewFor("Subtype", "Type0"); +@@ -700,13 +728,12 @@ CPDF_Dictionary* CPDF_DocPageData::ProcessbCJK( + pFontDict->SetNewFor("Subtype", "CIDFontType2"); + pFontDict->SetNewFor("BaseFont", basefont); + +- CPDF_Dictionary* pCIDSysInfo = +- pFontDict->SetNewFor("CIDSystemInfo"); ++ auto pCIDSysInfo = pFontDict->SetNewFor("CIDSystemInfo"); + pCIDSysInfo->SetNewFor("Registry", "Adobe", false); + pCIDSysInfo->SetNewFor("Ordering", ordering, false); + pCIDSysInfo->SetNewFor("Supplement", supplement); + +- CPDF_Array* pArray = pBaseDict->SetNewFor("DescendantFonts"); +- pArray->AddNew(GetDocument(), pFontDict->GetObjNum()); ++ auto pArray = pBaseDict->SetNewFor("DescendantFonts"); ++ pArray->AppendNew(GetDocument(), pFontDict->GetObjNum()); + return pFontDict; + } +diff --git a/core/fpdfapi/page/cpdf_docpagedata.h b/core/fpdfapi/page/cpdf_docpagedata.h +index 854d80db4..59c035415 100644 +--- a/core/fpdfapi/page/cpdf_docpagedata.h ++++ b/core/fpdfapi/page/cpdf_docpagedata.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,11 +14,11 @@ + #include "core/fpdfapi/font/cpdf_font.h" + #include "core/fpdfapi/page/cpdf_colorspace.h" + #include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/fx_codepage_forward.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" + #include "core/fxcrt/observed_ptr.h" + #include "core/fxcrt/retain_ptr.h" +-#include "core/fxcrt/unowned_ptr.h" + + class CFX_Font; + class CPDF_Dictionary; +@@ -30,8 +30,8 @@ class CPDF_Pattern; + class CPDF_Stream; + class CPDF_StreamAcc; + +-class CPDF_DocPageData : public CPDF_Document::PageDataIface, +- public CPDF_Font::FormFactoryIface { ++class CPDF_DocPageData final : public CPDF_Document::PageDataIface, ++ public CPDF_Font::FormFactoryIface { + public: + static CPDF_DocPageData* FromDocument(const CPDF_Document* pDoc); + +@@ -41,24 +41,27 @@ class CPDF_DocPageData : public CPDF_Document::PageDataIface, + // CPDF_Document::PageDataIface: + void ClearStockFont() override; + RetainPtr GetFontFileStreamAcc( +- const CPDF_Stream* pFontStream) override; +- void MaybePurgeFontFileStreamAcc(const CPDF_Stream* pFontStream) override; ++ RetainPtr pFontStream) override; ++ void MaybePurgeFontFileStreamAcc( ++ RetainPtr&& pStreamAcc) override; ++ void MaybePurgeImage(uint32_t dwStreamObjNum) override; + + // CPDF_Font::FormFactoryIFace: + std::unique_ptr CreateForm( + CPDF_Document* pDocument, +- CPDF_Dictionary* pPageResources, +- CPDF_Stream* pFormStream) override; ++ RetainPtr pPageResources, ++ RetainPtr pFormStream) override; + + bool IsForceClear() const { return m_bForceClear; } + +- RetainPtr AddFont(std::unique_ptr pFont, int charset); +- RetainPtr GetFont(CPDF_Dictionary* pFontDict); ++ RetainPtr AddFont(std::unique_ptr pFont, ++ FX_Charset charset); ++ RetainPtr GetFont(RetainPtr pFontDict); + RetainPtr AddStandardFont(const ByteString& fontName, + const CPDF_FontEncoding* pEncoding); + RetainPtr GetStandardFont(const ByteString& fontName, + const CPDF_FontEncoding* pEncoding); +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + RetainPtr AddWindowsFont(LOGFONTA* pLogFont); + #endif + +@@ -74,14 +77,15 @@ class CPDF_DocPageData : public CPDF_Document::PageDataIface, + const CPDF_Dictionary* pResources, + std::set* pVisited); + +- RetainPtr GetPattern(CPDF_Object* pPatternObj, +- bool bShading, ++ RetainPtr GetPattern(RetainPtr pPatternObj, + const CFX_Matrix& matrix); ++ RetainPtr GetShading(RetainPtr pPatternObj, ++ const CFX_Matrix& matrix); + + RetainPtr GetImage(uint32_t dwStreamObjNum); +- void MaybePurgeImage(uint32_t dwStreamObjNum); + +- RetainPtr GetIccProfile(const CPDF_Stream* pProfileStream); ++ RetainPtr GetIccProfile( ++ RetainPtr pProfileStream); + + private: + // Loads a colorspace in a context that might be while loading another +@@ -95,24 +99,27 @@ class CPDF_DocPageData : public CPDF_Document::PageDataIface, + std::set* pVisited, + std::set* pVisitedInternal); + +- size_t CalculateEncodingDict(int charset, CPDF_Dictionary* pBaseDict); +- CPDF_Dictionary* ProcessbCJK( +- CPDF_Dictionary* pBaseDict, +- int charset, ++ size_t CalculateEncodingDict(FX_Charset charset, CPDF_Dictionary* pBaseDict); ++ RetainPtr ProcessbCJK( ++ RetainPtr pBaseDict, ++ FX_Charset charset, + ByteString basefont, + std::function Insert); +- void Clear(bool bForceRelease); + + bool m_bForceClear = false; + + // Specific destruction order may be required between maps. + std::map> m_HashProfileMap; +- std::map> m_ColorSpaceMap; +- std::map> m_FontFileMap; +- std::map> m_IccProfileMap; +- std::map> m_PatternMap; ++ std::map, ObservedPtr> ++ m_ColorSpaceMap; ++ std::map, RetainPtr> ++ m_FontFileMap; ++ std::map, ObservedPtr> ++ m_IccProfileMap; ++ std::map, ObservedPtr> ++ m_PatternMap; + std::map> m_ImageMap; +- std::map> m_FontMap; ++ std::map, ObservedPtr> m_FontMap; + }; + + #endif // CORE_FPDFAPI_PAGE_CPDF_DOCPAGEDATA_H_ +diff --git a/core/fpdfapi/page/cpdf_expintfunc.cpp b/core/fpdfapi/page/cpdf_expintfunc.cpp +index 7194f97fd..a8de8b9a4 100644 +--- a/core/fpdfapi/page/cpdf_expintfunc.cpp ++++ b/core/fpdfapi/page/cpdf_expintfunc.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,41 +6,44 @@ + + #include "core/fpdfapi/page/cpdf_expintfunc.h" + ++#include ++ + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_number.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_2d_size.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxcrt/stl_util.h" + + CPDF_ExpIntFunc::CPDF_ExpIntFunc() +- : CPDF_Function(Type::kType2ExpotentialInterpolation) {} ++ : CPDF_Function(Type::kType2ExponentialInterpolation) {} + + CPDF_ExpIntFunc::~CPDF_ExpIntFunc() = default; + +-bool CPDF_ExpIntFunc::v_Init(const CPDF_Object* pObj, +- std::set* pVisited) { +- const CPDF_Dictionary* pDict = pObj->GetDict(); ++bool CPDF_ExpIntFunc::v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) { ++ RetainPtr pDict = pObj->GetDict(); + if (!pDict) + return false; + +- const CPDF_Number* pExponent = ToNumber(pDict->GetObjectFor("N")); ++ RetainPtr pExponent = pDict->GetNumberFor("N"); + if (!pExponent) + return false; + + m_Exponent = pExponent->GetNumber(); + +- const CPDF_Array* pArray0 = pDict->GetArrayFor("C0"); ++ RetainPtr pArray0 = pDict->GetArrayFor("C0"); + if (pArray0 && m_nOutputs == 0) +- m_nOutputs = pArray0->size(); ++ m_nOutputs = fxcrt::CollectionSize(*pArray0); + if (m_nOutputs == 0) + m_nOutputs = 1; + +- const CPDF_Array* pArray1 = pDict->GetArrayFor("C1"); +- m_BeginValues = pdfium::Vector2D(m_nOutputs, 2); +- m_EndValues = pdfium::Vector2D(m_nOutputs, 2); ++ RetainPtr pArray1 = pDict->GetArrayFor("C1"); ++ m_BeginValues = DataVector(Fx2DSizeOrDie(m_nOutputs, 2)); ++ m_EndValues = DataVector(m_BeginValues.size()); + for (uint32_t i = 0; i < m_nOutputs; i++) { +- m_BeginValues[i] = pArray0 ? pArray0->GetNumberAt(i) : 0.0f; +- m_EndValues[i] = pArray1 ? pArray1->GetNumberAt(i) : 1.0f; ++ m_BeginValues[i] = pArray0 ? pArray0->GetFloatAt(i) : 0.0f; ++ m_EndValues[i] = pArray1 ? pArray1->GetFloatAt(i) : 1.0f; + } + + FX_SAFE_UINT32 nOutputs = m_nOutputs; +@@ -53,12 +56,13 @@ bool CPDF_ExpIntFunc::v_Init(const CPDF_Object* pObj, + return true; + } + +-bool CPDF_ExpIntFunc::v_Call(const float* inputs, float* results) const { ++bool CPDF_ExpIntFunc::v_Call(pdfium::span inputs, ++ pdfium::span results) const { + for (uint32_t i = 0; i < m_nInputs; i++) { + for (uint32_t j = 0; j < m_nOrigOutputs; j++) { + results[i * m_nOrigOutputs + j] = +- m_BeginValues[j] + FXSYS_pow(inputs[i], m_Exponent) * +- (m_EndValues[j] - m_BeginValues[j]); ++ m_BeginValues[j] + ++ powf(inputs[i], m_Exponent) * (m_EndValues[j] - m_BeginValues[j]); + } + } + return true; +diff --git a/core/fpdfapi/page/cpdf_expintfunc.h b/core/fpdfapi/page/cpdf_expintfunc.h +index 95bdab68e..08b12fd7b 100644 +--- a/core/fpdfapi/page/cpdf_expintfunc.h ++++ b/core/fpdfapi/page/cpdf_expintfunc.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,25 +7,36 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_EXPINTFUNC_H_ + #define CORE_FPDFAPI_PAGE_CPDF_EXPINTFUNC_H_ + +-#include +-#include +- + #include "core/fpdfapi/page/cpdf_function.h" ++#include "core/fxcrt/data_vector.h" ++ ++#if defined(_SKIA_SUPPORT_) ++#include "third_party/base/span.h" ++#endif + + class CPDF_ExpIntFunc final : public CPDF_Function { + public: + CPDF_ExpIntFunc(); + ~CPDF_ExpIntFunc() override; + +- // CPDF_Function +- bool v_Init(const CPDF_Object* pObj, +- std::set* pVisited) override; +- bool v_Call(const float* inputs, float* results) const override; ++ // CPDF_Function: ++ bool v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) override; ++ bool v_Call(pdfium::span inputs, ++ pdfium::span results) const override; ++ ++ uint32_t GetOrigOutputs() const { return m_nOrigOutputs; } ++ float GetExponent() const { return m_Exponent; } ++ ++#if defined(_SKIA_SUPPORT_) ++ pdfium::span GetBeginValues() const { return m_BeginValues; } ++ pdfium::span GetEndValues() const { return m_EndValues; } ++#endif + ++ private: + uint32_t m_nOrigOutputs = 0; + float m_Exponent = 0.0f; +- std::vector m_BeginValues; +- std::vector m_EndValues; ++ DataVector m_BeginValues; ++ DataVector m_EndValues; + }; + + #endif // CORE_FPDFAPI_PAGE_CPDF_EXPINTFUNC_H_ +diff --git a/core/fpdfapi/page/cpdf_form.cpp b/core/fpdfapi/page/cpdf_form.cpp +index ab3296ea7..90a865035 100644 +--- a/core/fpdfapi/page/cpdf_form.cpp ++++ b/core/fpdfapi/page/cpdf_form.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,7 @@ + #include "core/fpdfapi/page/cpdf_form.h" + + #include ++#include + + #include "core/fpdfapi/page/cpdf_contentparser.h" + #include "core/fpdfapi/page/cpdf_imageobject.h" +@@ -15,7 +16,7 @@ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check_op.h" + + // static + CPDF_Dictionary* CPDF_Form::ChooseResourcesDict( +@@ -28,22 +29,27 @@ CPDF_Dictionary* CPDF_Form::ChooseResourcesDict( + } + + CPDF_Form::CPDF_Form(CPDF_Document* pDoc, +- CPDF_Dictionary* pPageResources, +- CPDF_Stream* pFormStream) +- : CPDF_Form(pDoc, pPageResources, pFormStream, nullptr) {} ++ RetainPtr pPageResources, ++ RetainPtr pFormStream) ++ : CPDF_Form(pDoc, ++ std::move(pPageResources), ++ std::move(pFormStream), ++ nullptr) {} + + CPDF_Form::CPDF_Form(CPDF_Document* pDoc, +- CPDF_Dictionary* pPageResources, +- CPDF_Stream* pFormStream, ++ RetainPtr pPageResources, ++ RetainPtr pFormStream, + CPDF_Dictionary* pParentResources) +- : CPDF_PageObjectHolder( +- pDoc, +- pFormStream->GetDict(), +- pPageResources, +- ChooseResourcesDict(pFormStream->GetDict()->GetDictFor("Resources"), +- pParentResources, +- pPageResources)), +- m_pFormStream(pFormStream) { ++ : CPDF_PageObjectHolder(pDoc, ++ pFormStream->GetMutableDict(), ++ pPageResources, ++ pdfium::WrapRetain(ChooseResourcesDict( ++ pFormStream->GetMutableDict() ++ ->GetMutableDictFor("Resources") ++ .Get(), ++ pParentResources, ++ pPageResources.Get()))), ++ m_pFormStream(std::move(pFormStream)) { + LoadTransparencyInfo(); + } + +@@ -71,16 +77,11 @@ void CPDF_Form::ParseContentInternal(const CPDF_AllStates* pGraphicStates, + return; + + if (GetParseState() == ParseState::kNotParsed) { +- if (!pParsedSet) { +- if (!m_ParsedSet) +- m_ParsedSet = pdfium::MakeUnique>(); +- pParsedSet = m_ParsedSet.get(); +- } +- StartParse(pdfium::MakeUnique( +- this, pGraphicStates, pParentMatrix, pType3Char, pParsedSet)); ++ StartParse(std::make_unique( ++ GetStream(), this, pGraphicStates, pParentMatrix, pType3Char, ++ pParsedSet ? pParsedSet : &m_ParsedSet)); + } +- +- ASSERT(GetParseState() == ParseState::kParsing); ++ DCHECK_EQ(GetParseState(), ParseState::kParsing); + ContinueParse(nullptr); + } + +@@ -106,18 +107,18 @@ CFX_FloatRect CPDF_Form::CalcBoundingBox() const { + return CFX_FloatRect(left, bottom, right, top); + } + +-const CPDF_Stream* CPDF_Form::GetStream() const { +- return m_pFormStream.Get(); ++RetainPtr CPDF_Form::GetStream() const { ++ return m_pFormStream; + } + +-Optional, CFX_Matrix>> ++absl::optional, CFX_Matrix>> + CPDF_Form::GetBitmapAndMatrixFromSoleImageOfForm() const { + if (GetPageObjectCount() != 1) +- return {}; ++ return absl::nullopt; + + CPDF_ImageObject* pImageObject = (*begin())->AsImage(); + if (!pImageObject) +- return {}; ++ return absl::nullopt; + + return {{pImageObject->GetIndependentBitmap(), pImageObject->matrix()}}; + } +diff --git a/core/fpdfapi/page/cpdf_form.h b/core/fpdfapi/page/cpdf_form.h +index e8b9d1f7c..9052b9d00 100644 +--- a/core/fpdfapi/page/cpdf_form.h ++++ b/core/fpdfapi/page/cpdf_form.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,18 +7,17 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_FORM_H_ + #define CORE_FPDFAPI_PAGE_CPDF_FORM_H_ + +-#include + #include + #include + + #include "core/fpdfapi/font/cpdf_font.h" + #include "core/fpdfapi/page/cpdf_pageobjectholder.h" ++#include "core/fxcrt/retain_ptr.h" + + class CFX_Matrix; + class CPDF_AllStates; + class CPDF_Dictionary; + class CPDF_Document; +-class CPDF_ImageObject; + class CPDF_Stream; + class CPDF_Type3Char; + +@@ -31,11 +30,11 @@ class CPDF_Form final : public CPDF_PageObjectHolder, + CPDF_Dictionary* pPageResources); + + CPDF_Form(CPDF_Document* pDocument, +- CPDF_Dictionary* pPageResources, +- CPDF_Stream* pFormStream); ++ RetainPtr pPageResources, ++ RetainPtr pFormStream); + CPDF_Form(CPDF_Document* pDocument, +- CPDF_Dictionary* pPageResources, +- CPDF_Stream* pFormStream, ++ RetainPtr pPageResources, ++ RetainPtr pFormStream, + CPDF_Dictionary* pParentResources); + ~CPDF_Form() override; + +@@ -43,7 +42,7 @@ class CPDF_Form final : public CPDF_PageObjectHolder, + void ParseContentForType3Char(CPDF_Type3Char* pType3Char) override; + bool HasPageObjects() const override; + CFX_FloatRect CalcBoundingBox() const override; +- Optional, CFX_Matrix>> ++ absl::optional, CFX_Matrix>> + GetBitmapAndMatrixFromSoleImageOfForm() const override; + + void ParseContent(); +@@ -51,7 +50,7 @@ class CPDF_Form final : public CPDF_PageObjectHolder, + const CFX_Matrix* pParentMatrix, + std::set* pParsedSet); + +- const CPDF_Stream* GetStream() const; ++ RetainPtr GetStream() const; + + private: + void ParseContentInternal(const CPDF_AllStates* pGraphicStates, +@@ -59,7 +58,7 @@ class CPDF_Form final : public CPDF_PageObjectHolder, + CPDF_Type3Char* pType3Char, + std::set* pParsedSet); + +- std::unique_ptr> m_ParsedSet; ++ std::set m_ParsedSet; + RetainPtr const m_pFormStream; + }; + +diff --git a/core/fpdfapi/page/cpdf_formobject.cpp b/core/fpdfapi/page/cpdf_formobject.cpp +index a7c2643b6..8b270ca30 100644 +--- a/core/fpdfapi/page/cpdf_formobject.cpp ++++ b/core/fpdfapi/page/cpdf_formobject.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -17,11 +17,12 @@ CPDF_FormObject::CPDF_FormObject(int32_t content_stream, + m_pForm(std::move(pForm)), + m_FormMatrix(matrix) {} + +-CPDF_FormObject::~CPDF_FormObject() {} ++CPDF_FormObject::~CPDF_FormObject() = default; + + void CPDF_FormObject::Transform(const CFX_Matrix& matrix) { + m_FormMatrix.Concat(matrix); + CalcBoundingBox(); ++ SetDirty(true); + } + + bool CPDF_FormObject::IsForm() const { +@@ -37,9 +38,14 @@ const CPDF_FormObject* CPDF_FormObject::AsForm() const { + } + + CPDF_PageObject::Type CPDF_FormObject::GetType() const { +- return FORM; ++ return Type::kForm; + } + + void CPDF_FormObject::CalcBoundingBox() { + SetRect(m_FormMatrix.TransformRect(m_pForm->CalcBoundingBox())); + } ++ ++void CPDF_FormObject::SetFormMatrix(const CFX_Matrix& matrix) { ++ m_FormMatrix = matrix; ++ CalcBoundingBox(); ++} +diff --git a/core/fpdfapi/page/cpdf_formobject.h b/core/fpdfapi/page/cpdf_formobject.h +index c24bfbfd2..a18a774f8 100644 +--- a/core/fpdfapi/page/cpdf_formobject.h ++++ b/core/fpdfapi/page/cpdf_formobject.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -31,6 +31,7 @@ class CPDF_FormObject final : public CPDF_PageObject { + void CalcBoundingBox(); + const CPDF_Form* form() const { return m_pForm.get(); } + const CFX_Matrix& form_matrix() const { return m_FormMatrix; } ++ void SetFormMatrix(const CFX_Matrix& matrix); + + private: + std::unique_ptr const m_pForm; +diff --git a/core/fpdfapi/page/cpdf_function.cpp b/core/fpdfapi/page/cpdf_function.cpp +index 688c48edc..85b9e4175 100644 +--- a/core/fpdfapi/page/cpdf_function.cpp ++++ b/core/fpdfapi/page/cpdf_function.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,7 @@ + + #include "core/fpdfapi/page/cpdf_function.h" + ++#include + #include + + #include "core/fpdfapi/page/cpdf_expintfunc.h" +@@ -17,26 +18,45 @@ + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxcrt/scoped_set_insertion.h" ++#include "core/fxcrt/stl_util.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/cxx17_backports.h" ++ ++namespace { ++ ++CPDF_Function::Type IntegerToFunctionType(int iType) { ++ switch (iType) { ++ case 0: ++ case 2: ++ case 3: ++ case 4: ++ return static_cast(iType); ++ default: ++ return CPDF_Function::Type::kTypeInvalid; ++ } ++} ++ ++} // namespace + + // static + std::unique_ptr CPDF_Function::Load( +- const CPDF_Object* pFuncObj) { +- std::set visited; +- return Load(pFuncObj, &visited); ++ RetainPtr pFuncObj) { ++ VisitedSet visited; ++ return Load(std::move(pFuncObj), &visited); + } + + // static + std::unique_ptr CPDF_Function::Load( +- const CPDF_Object* pFuncObj, +- std::set* pVisited) { ++ RetainPtr pFuncObj, ++ VisitedSet* pVisited) { + if (!pFuncObj) + return nullptr; + +- if (pdfium::ContainsKey(*pVisited, pFuncObj)) ++ if (pdfium::Contains(*pVisited, pFuncObj)) + return nullptr; +- pdfium::ScopedSetInsertion insertion(pVisited, pFuncObj); ++ ++ ScopedSetInsertion insertion(pVisited, pFuncObj); + + int iType = -1; + if (const CPDF_Stream* pStream = pFuncObj->AsStream()) +@@ -47,13 +67,13 @@ std::unique_ptr CPDF_Function::Load( + std::unique_ptr pFunc; + Type type = IntegerToFunctionType(iType); + if (type == Type::kType0Sampled) +- pFunc = pdfium::MakeUnique(); +- else if (type == Type::kType2ExpotentialInterpolation) +- pFunc = pdfium::MakeUnique(); ++ pFunc = std::make_unique(); ++ else if (type == Type::kType2ExponentialInterpolation) ++ pFunc = std::make_unique(); + else if (type == Type::kType3Stitching) +- pFunc = pdfium::MakeUnique(); ++ pFunc = std::make_unique(); + else if (type == Type::kType4PostScript) +- pFunc = pdfium::MakeUnique(); ++ pFunc = std::make_unique(); + + if (!pFunc || !pFunc->Init(pFuncObj, pVisited)) + return nullptr; +@@ -61,42 +81,28 @@ std::unique_ptr CPDF_Function::Load( + return pFunc; + } + +-// static +-CPDF_Function::Type CPDF_Function::IntegerToFunctionType(int iType) { +- switch (iType) { +- case 0: +- case 2: +- case 3: +- case 4: +- return static_cast(iType); +- default: +- return Type::kTypeInvalid; +- } +-} +- + CPDF_Function::CPDF_Function(Type type) : m_Type(type) {} + + CPDF_Function::~CPDF_Function() = default; + +-bool CPDF_Function::Init(const CPDF_Object* pObj, +- std::set* pVisited) { ++bool CPDF_Function::Init(const CPDF_Object* pObj, VisitedSet* pVisited) { + const CPDF_Stream* pStream = pObj->AsStream(); +- const CPDF_Dictionary* pDict = +- pStream ? pStream->GetDict() : pObj->AsDictionary(); ++ RetainPtr pDict = ++ pStream ? pStream->GetDict() : pdfium::WrapRetain(pObj->AsDictionary()); + +- const CPDF_Array* pDomains = pDict->GetArrayFor("Domain"); ++ RetainPtr pDomains = pDict->GetArrayFor("Domain"); + if (!pDomains) + return false; + +- m_nInputs = pDomains->size() / 2; ++ m_nInputs = fxcrt::CollectionSize(*pDomains) / 2; + if (m_nInputs == 0) + return false; + + size_t nInputs = m_nInputs * 2; +- m_Domains = ReadArrayElementsToVector(pDomains, nInputs); ++ m_Domains = ReadArrayElementsToVector(pDomains.Get(), nInputs); + +- const CPDF_Array* pRanges = pDict->GetArrayFor("Range"); +- m_nOutputs = pRanges ? pRanges->size() / 2 : 0; ++ RetainPtr pRanges = pDict->GetArrayFor("Range"); ++ m_nOutputs = pRanges ? fxcrt::CollectionSize(*pRanges) / 2 : 0; + + // Ranges are required for type 0 and type 4 functions. A non-zero + // |m_nOutputs| here implied Ranges meets the requirements. +@@ -107,7 +113,7 @@ bool CPDF_Function::Init(const CPDF_Object* pObj, + + if (m_nOutputs > 0) { + size_t nOutputs = m_nOutputs * 2; +- m_Ranges = ReadArrayElementsToVector(pRanges, nOutputs); ++ m_Ranges = ReadArrayElementsToVector(pRanges.Get(), nOutputs); + } + + uint32_t old_outputs = m_nOutputs; +@@ -122,30 +128,36 @@ bool CPDF_Function::Init(const CPDF_Object* pObj, + return true; + } + +-bool CPDF_Function::Call(const float* inputs, +- uint32_t ninputs, +- float* results, +- int* nresults) const { +- if (m_nInputs != ninputs) +- return false; ++absl::optional CPDF_Function::Call( ++ pdfium::span inputs, ++ pdfium::span results) const { ++ if (m_nInputs != inputs.size()) ++ return absl::nullopt; + +- *nresults = m_nOutputs; + std::vector clamped_inputs(m_nInputs); + for (uint32_t i = 0; i < m_nInputs; i++) { +- clamped_inputs[i] = +- pdfium::clamp(inputs[i], m_Domains[i * 2], m_Domains[i * 2 + 1]); ++ float domain1 = m_Domains[i * 2]; ++ float domain2 = m_Domains[i * 2 + 1]; ++ if (domain1 > domain2) ++ return absl::nullopt; ++ ++ clamped_inputs[i] = pdfium::clamp(inputs[i], domain1, domain2); + } +- if (!v_Call(clamped_inputs.data(), results)) +- return false; ++ if (!v_Call(clamped_inputs, results)) ++ return absl::nullopt; + + if (m_Ranges.empty()) +- return true; ++ return m_nOutputs; + + for (uint32_t i = 0; i < m_nOutputs; i++) { +- results[i] = +- pdfium::clamp(results[i], m_Ranges[i * 2], m_Ranges[i * 2 + 1]); ++ float range1 = m_Ranges[i * 2]; ++ float range2 = m_Ranges[i * 2 + 1]; ++ if (range1 > range2) ++ return absl::nullopt; ++ ++ results[i] = pdfium::clamp(results[i], range1, range2); + } +- return true; ++ return m_nOutputs; + } + + // See PDF Reference 1.7, page 170. +@@ -158,6 +170,7 @@ float CPDF_Function::Interpolate(float x, + return ymin + (divisor ? (x - xmin) * (ymax - ymin) / divisor : 0); + } + ++#if defined(_SKIA_SUPPORT_) + const CPDF_SampledFunc* CPDF_Function::ToSampledFunc() const { + return m_Type == Type::kType0Sampled + ? static_cast(this) +@@ -165,7 +178,7 @@ const CPDF_SampledFunc* CPDF_Function::ToSampledFunc() const { + } + + const CPDF_ExpIntFunc* CPDF_Function::ToExpIntFunc() const { +- return m_Type == Type::kType2ExpotentialInterpolation ++ return m_Type == Type::kType2ExponentialInterpolation + ? static_cast(this) + : nullptr; + } +@@ -175,3 +188,4 @@ const CPDF_StitchFunc* CPDF_Function::ToStitchFunc() const { + ? static_cast(this) + : nullptr; + } ++#endif // defined(_SKIA_SUPPORT_) +diff --git a/core/fpdfapi/page/cpdf_function.h b/core/fpdfapi/page/cpdf_function.h +index 5f4e125fa..faddb4ec7 100644 +--- a/core/fpdfapi/page/cpdf_function.h ++++ b/core/fpdfapi/page/cpdf_function.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,6 +11,10 @@ + #include + #include + ++#include "core/fxcrt/retain_ptr.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/span.h" ++ + class CPDF_ExpIntFunc; + class CPDF_Object; + class CPDF_SampledFunc; +@@ -18,23 +22,22 @@ class CPDF_StitchFunc; + + class CPDF_Function { + public: ++ // Valid values are from ISO 32000-1:2008 spec, table 38. DO NOT CHANGE. + enum class Type { + kTypeInvalid = -1, + kType0Sampled = 0, +- kType2ExpotentialInterpolation = 2, ++ kType2ExponentialInterpolation = 2, + kType3Stitching = 3, + kType4PostScript = 4, + }; + +- static std::unique_ptr Load(const CPDF_Object* pFuncObj); +- static Type IntegerToFunctionType(int iType); ++ static std::unique_ptr Load( ++ RetainPtr pFuncObj); + + virtual ~CPDF_Function(); + +- bool Call(const float* inputs, +- uint32_t ninputs, +- float* results, +- int* nresults) const; ++ absl::optional Call(pdfium::span inputs, ++ pdfium::span results) const; + uint32_t CountInputs() const { return m_nInputs; } + uint32_t CountOutputs() const { return m_nOutputs; } + float GetDomain(int i) const { return m_Domains[i]; } +@@ -45,24 +48,27 @@ class CPDF_Function { + float ymin, + float ymax) const; + ++#if defined(_SKIA_SUPPORT_) + const CPDF_SampledFunc* ToSampledFunc() const; + const CPDF_ExpIntFunc* ToExpIntFunc() const; + const CPDF_StitchFunc* ToStitchFunc() const; ++#endif // defined(_SKIA_SUPPORT_) + + protected: + explicit CPDF_Function(Type type); + ++ using VisitedSet = std::set>; + static std::unique_ptr Load( +- const CPDF_Object* pFuncObj, +- std::set* pVisited); +- bool Init(const CPDF_Object* pObj, std::set* pVisited); +- virtual bool v_Init(const CPDF_Object* pObj, +- std::set* pVisited) = 0; +- virtual bool v_Call(const float* inputs, float* results) const = 0; ++ RetainPtr pFuncObj, ++ VisitedSet* pVisited); ++ bool Init(const CPDF_Object* pObj, VisitedSet* pVisited); ++ virtual bool v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) = 0; ++ virtual bool v_Call(pdfium::span inputs, ++ pdfium::span results) const = 0; + + const Type m_Type; +- uint32_t m_nInputs; +- uint32_t m_nOutputs; ++ uint32_t m_nInputs = 0; ++ uint32_t m_nOutputs = 0; + std::vector m_Domains; + std::vector m_Ranges; + }; +diff --git a/core/fpdfapi/page/cpdf_function_unittest.cpp b/core/fpdfapi/page/cpdf_function_unittest.cpp +new file mode 100644 +index 000000000..af0ca03fa +--- /dev/null ++++ b/core/fpdfapi/page/cpdf_function_unittest.cpp +@@ -0,0 +1,43 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fpdfapi/page/cpdf_function.h" ++ ++#include "core/fpdfapi/parser/cpdf_array.h" ++#include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fpdfapi/parser/cpdf_number.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++TEST(CPDFFunction, BadFunctionType) { ++ auto pDict = pdfium::MakeRetain(); ++ pDict->SetNewFor("FunctionType", -2); ++ EXPECT_FALSE(CPDF_Function::Load(pDict)); ++ ++ pDict->SetNewFor("FunctionType", 5); ++ EXPECT_FALSE(CPDF_Function::Load(pDict)); ++} ++ ++TEST(CPDFFunction, NoDomain) { ++ auto pDict = pdfium::MakeRetain(); ++ pDict->SetNewFor("FunctionType", 0); ++ EXPECT_FALSE(CPDF_Function::Load(pDict)); ++} ++ ++TEST(CPDFFunction, EmptyDomain) { ++ auto pDict = pdfium::MakeRetain(); ++ pDict->SetNewFor("FunctionType", 0); ++ pDict->SetNewFor("Domain"); ++ EXPECT_FALSE(CPDF_Function::Load(pDict)); ++} ++ ++TEST(CPDFFunction, NoRange) { ++ auto pDict = pdfium::MakeRetain(); ++ pDict->SetNewFor("FunctionType", 0); ++ ++ auto pArray = pDict->SetNewFor("Domain"); ++ pArray->AppendNew(0); ++ pArray->AppendNew(10); ++ EXPECT_FALSE(CPDF_Function::Load(pDict)); ++} +diff --git a/core/fpdfapi/page/cpdf_generalstate.cpp b/core/fpdfapi/page/cpdf_generalstate.cpp +index 6463b57be..8ebefa3a3 100644 +--- a/core/fpdfapi/page/cpdf_generalstate.cpp ++++ b/core/fpdfapi/page/cpdf_generalstate.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,8 +6,11 @@ + + #include "core/fpdfapi/page/cpdf_generalstate.h" + ++#include ++ + #include "core/fpdfapi/page/cpdf_transferfunc.h" +-#include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fpdfapi/parser/cpdf_object.h" + + namespace { + +@@ -66,12 +69,12 @@ BlendMode GetBlendTypeInternal(const ByteString& mode) { + + } // namespace + +-CPDF_GeneralState::CPDF_GeneralState() {} ++CPDF_GeneralState::CPDF_GeneralState() = default; + + CPDF_GeneralState::CPDF_GeneralState(const CPDF_GeneralState& that) + : m_Ref(that.m_Ref) {} + +-CPDF_GeneralState::~CPDF_GeneralState() {} ++CPDF_GeneralState::~CPDF_GeneralState() = default; + + void CPDF_GeneralState::SetRenderIntent(const ByteString& ri) { + m_Ref.GetPrivateCopy()->m_RenderIntent = RI_StringToId(ri); +@@ -142,22 +145,27 @@ void CPDF_GeneralState::SetStrokeAlpha(float alpha) { + m_Ref.GetPrivateCopy()->m_StrokeAlpha = alpha; + } + +-CPDF_Object* CPDF_GeneralState::GetSoftMask() const { ++RetainPtr CPDF_GeneralState::GetSoftMask() const { ++ const StateData* pData = m_Ref.GetObject(); ++ return pData ? pData->m_pSoftMask : nullptr; ++} ++ ++RetainPtr CPDF_GeneralState::GetMutableSoftMask() { + const StateData* pData = m_Ref.GetObject(); +- return pData ? pData->m_pSoftMask.Get() : nullptr; ++ return pData ? pData->m_pSoftMask : nullptr; + } + +-void CPDF_GeneralState::SetSoftMask(CPDF_Object* pObject) { +- m_Ref.GetPrivateCopy()->m_pSoftMask.Reset(pObject); ++void CPDF_GeneralState::SetSoftMask(RetainPtr pDict) { ++ m_Ref.GetPrivateCopy()->m_pSoftMask = std::move(pDict); + } + +-const CPDF_Object* CPDF_GeneralState::GetTR() const { ++RetainPtr CPDF_GeneralState::GetTR() const { + const StateData* pData = m_Ref.GetObject(); +- return pData ? pData->m_pTR.Get() : nullptr; ++ return pData ? pData->m_pTR : nullptr; + } + +-void CPDF_GeneralState::SetTR(CPDF_Object* pObject) { +- m_Ref.GetPrivateCopy()->m_pTR.Reset(pObject); ++void CPDF_GeneralState::SetTR(RetainPtr pObject) { ++ m_Ref.GetPrivateCopy()->m_pTR = std::move(pObject); + } + + RetainPtr CPDF_GeneralState::GetTransferFunc() const { +@@ -165,9 +173,8 @@ RetainPtr CPDF_GeneralState::GetTransferFunc() const { + return pData ? pData->m_pTransferFunc : nullptr; + } + +-void CPDF_GeneralState::SetTransferFunc( +- const RetainPtr& pFunc) { +- m_Ref.GetPrivateCopy()->m_pTransferFunc = pFunc; ++void CPDF_GeneralState::SetTransferFunc(RetainPtr pFunc) { ++ m_Ref.GetPrivateCopy()->m_pTransferFunc = std::move(pFunc); + } + + void CPDF_GeneralState::SetBlendMode(const ByteString& mode) { +@@ -211,16 +218,16 @@ void CPDF_GeneralState::SetOPMode(int mode) { + m_Ref.GetPrivateCopy()->m_OPMode = mode; + } + +-void CPDF_GeneralState::SetBG(CPDF_Object* pObject) { +- m_Ref.GetPrivateCopy()->m_pBG.Reset(pObject); ++void CPDF_GeneralState::SetBG(RetainPtr pObject) { ++ m_Ref.GetPrivateCopy()->m_pBG = std::move(pObject); + } + +-void CPDF_GeneralState::SetUCR(CPDF_Object* pObject) { +- m_Ref.GetPrivateCopy()->m_pUCR.Reset(pObject); ++void CPDF_GeneralState::SetUCR(RetainPtr pObject) { ++ m_Ref.GetPrivateCopy()->m_pUCR = std::move(pObject); + } + +-void CPDF_GeneralState::SetHT(CPDF_Object* pObject) { +- m_Ref.GetPrivateCopy()->m_pHT.Reset(pObject); ++void CPDF_GeneralState::SetHT(RetainPtr pObject) { ++ m_Ref.GetPrivateCopy()->m_pHT = std::move(pObject); + } + + void CPDF_GeneralState::SetFlatness(float flatness) { +diff --git a/core/fpdfapi/page/cpdf_generalstate.h b/core/fpdfapi/page/cpdf_generalstate.h +index f374380ba..2fb228531 100644 +--- a/core/fpdfapi/page/cpdf_generalstate.h ++++ b/core/fpdfapi/page/cpdf_generalstate.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,12 +8,13 @@ + #define CORE_FPDFAPI_PAGE_CPDF_GENERALSTATE_H_ + + #include "constants/transparency.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/shared_copy_on_write.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" + ++class CPDF_Dictionary; + class CPDF_Object; + class CPDF_TransferFunc; + +@@ -38,14 +39,15 @@ class CPDF_GeneralState { + float GetStrokeAlpha() const; + void SetStrokeAlpha(float alpha); + +- CPDF_Object* GetSoftMask() const; +- void SetSoftMask(CPDF_Object* pObject); ++ RetainPtr GetSoftMask() const; ++ RetainPtr GetMutableSoftMask(); ++ void SetSoftMask(RetainPtr pDict); + +- const CPDF_Object* GetTR() const; +- void SetTR(CPDF_Object* pObject); ++ RetainPtr GetTR() const; ++ void SetTR(RetainPtr pObject); + + RetainPtr GetTransferFunc() const; +- void SetTransferFunc(const RetainPtr& pFunc); ++ void SetTransferFunc(RetainPtr pFunc); + + void SetBlendMode(const ByteString& mode); + +@@ -61,9 +63,9 @@ class CPDF_GeneralState { + int GetOPMode() const; + void SetOPMode(int mode); + +- void SetBG(CPDF_Object* pObject); +- void SetUCR(CPDF_Object* pObject); +- void SetHT(CPDF_Object* pObject); ++ void SetBG(RetainPtr pObject); ++ void SetUCR(RetainPtr pObject); ++ void SetHT(RetainPtr pObject); + + void SetFlatness(float flatness); + void SetSmoothness(float smoothness); +@@ -80,14 +82,13 @@ class CPDF_GeneralState { + private: + class StateData final : public Retainable { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + RetainPtr Clone() const; + + ByteString m_BlendMode = pdfium::transparency::kNormal; + BlendMode m_BlendType = BlendMode::kNormal; +- RetainPtr m_pSoftMask; ++ RetainPtr m_pSoftMask; + CFX_Matrix m_SMaskMatrix; + float m_StrokeAlpha = 1.0f; + float m_FillAlpha = 1.0f; +diff --git a/core/fpdfapi/page/cpdf_graphicstates.cpp b/core/fpdfapi/page/cpdf_graphicstates.cpp +index 962bc0a89..8f19a2d56 100644 +--- a/core/fpdfapi/page/cpdf_graphicstates.cpp ++++ b/core/fpdfapi/page/cpdf_graphicstates.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,9 +6,9 @@ + + #include "core/fpdfapi/page/cpdf_graphicstates.h" + +-CPDF_GraphicStates::CPDF_GraphicStates() {} ++CPDF_GraphicStates::CPDF_GraphicStates() = default; + +-CPDF_GraphicStates::~CPDF_GraphicStates() {} ++CPDF_GraphicStates::~CPDF_GraphicStates() = default; + + void CPDF_GraphicStates::DefaultStates() { + m_ColorState.Emplace(); +diff --git a/core/fpdfapi/page/cpdf_graphicstates.h b/core/fpdfapi/page/cpdf_graphicstates.h +index b7e7fa205..9f604c7a4 100644 +--- a/core/fpdfapi/page/cpdf_graphicstates.h ++++ b/core/fpdfapi/page/cpdf_graphicstates.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fpdfapi/page/cpdf_iccprofile.cpp b/core/fpdfapi/page/cpdf_iccprofile.cpp +index f8d40e46d..82bbe9b03 100644 +--- a/core/fpdfapi/page/cpdf_iccprofile.cpp ++++ b/core/fpdfapi/page/cpdf_iccprofile.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,8 +6,10 @@ + + #include "core/fpdfapi/page/cpdf_iccprofile.h" + ++#include ++ + #include "core/fpdfapi/parser/cpdf_stream.h" +-#include "core/fxcodec/icc/iccmodule.h" ++#include "core/fxcodec/icc/icc_transform.h" + + namespace { + +@@ -18,17 +20,32 @@ bool DetectSRGB(pdfium::span span) { + + } // namespace + +-CPDF_IccProfile::CPDF_IccProfile(const CPDF_Stream* pStream, ++CPDF_IccProfile::CPDF_IccProfile(RetainPtr pStream, + pdfium::span span) +- : m_bsRGB(DetectSRGB(span)), m_pStream(pStream) { ++ : m_bsRGB(DetectSRGB(span)), m_pStream(std::move(pStream)) { + if (m_bsRGB) { + m_nSrcComponents = 3; + return; + } + +- m_Transform = IccModule::CreateTransformSRGB(span); ++ m_Transform = fxcodec::IccTransform::CreateTransformSRGB(span); + if (m_Transform) + m_nSrcComponents = m_Transform->components(); + } + + CPDF_IccProfile::~CPDF_IccProfile() = default; ++ ++bool CPDF_IccProfile::IsNormal() const { ++ return m_Transform->IsNormal(); ++} ++ ++void CPDF_IccProfile::Translate(pdfium::span pSrcValues, ++ pdfium::span pDestValues) { ++ m_Transform->Translate(pSrcValues, pDestValues); ++} ++ ++void CPDF_IccProfile::TranslateScanline(pdfium::span pDest, ++ pdfium::span pSrc, ++ int pixels) { ++ m_Transform->TranslateScanline(pDest, pSrc, pixels); ++} +diff --git a/core/fpdfapi/page/cpdf_iccprofile.h b/core/fpdfapi/page/cpdf_iccprofile.h +index 070f8843c..0e040aa45 100644 +--- a/core/fpdfapi/page/cpdf_iccprofile.h ++++ b/core/fpdfapi/page/cpdf_iccprofile.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,8 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_ICCPROFILE_H_ + #define CORE_FPDFAPI_PAGE_CPDF_ICCPROFILE_H_ + ++#include ++ + #include + + #include "core/fxcrt/observed_ptr.h" +@@ -16,29 +18,35 @@ + class CPDF_Stream; + + namespace fxcodec { +-class CLcmsCmm; ++class IccTransform; + } // namespace fxcodec + + class CPDF_IccProfile final : public Retainable, public Observable { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + +- const CPDF_Stream* GetStream() const { return m_pStream.Get(); } + bool IsValid() const { return IsSRGB() || IsSupported(); } + bool IsSRGB() const { return m_bsRGB; } + bool IsSupported() const { return !!m_Transform; } +- fxcodec::CLcmsCmm* transform() { return m_Transform.get(); } + uint32_t GetComponents() const { return m_nSrcComponents; } + ++ bool IsNormal() const; ++ void Translate(pdfium::span pSrcValues, ++ pdfium::span pDestValues); ++ void TranslateScanline(pdfium::span pDest, ++ pdfium::span pSrc, ++ int pixels); ++ + private: +- CPDF_IccProfile(const CPDF_Stream* pStream, pdfium::span span); ++ // Keeps stream alive for the duration of the CPDF_IccProfile. ++ CPDF_IccProfile(RetainPtr pStream, ++ pdfium::span span); + ~CPDF_IccProfile() override; + + const bool m_bsRGB; + uint32_t m_nSrcComponents = 0; +- RetainPtr const m_pStream; +- std::unique_ptr m_Transform; ++ RetainPtr const m_pStream; // Used by `m_Transform`. ++ std::unique_ptr m_Transform; + }; + + #endif // CORE_FPDFAPI_PAGE_CPDF_ICCPROFILE_H_ +diff --git a/core/fpdfapi/page/cpdf_image.cpp b/core/fpdfapi/page/cpdf_image.cpp +index ce46538d5..c9be23106 100644 +--- a/core/fpdfapi/page/cpdf_image.cpp ++++ b/core/fpdfapi/page/cpdf_image.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,14 +6,16 @@ + + #include "core/fpdfapi/page/cpdf_image.h" + ++#include ++ + #include + #include + #include +-#include + + #include "constants/stream_dict_common.h" + #include "core/fpdfapi/page/cpdf_dib.h" + #include "core/fpdfapi/page/cpdf_page.h" ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_boolean.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" +@@ -23,14 +25,16 @@ + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_string.h" +-#include "core/fxcodec/fx_codec.h" + #include "core/fxcodec/jpeg/jpegmodule.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_2d_size.h" + #include "core/fxcrt/fx_memory_wrappers.h" + #include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/span_util.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/check.h" + #include "third_party/base/numerics/safe_conversions.h" +-#include "third_party/base/ptr_util.h" + + // static + bool CPDF_Image::IsValidJpegComponent(int32_t comps) { +@@ -43,28 +47,29 @@ bool CPDF_Image::IsValidJpegBitsPerComponent(int32_t bpc) { + } + + CPDF_Image::CPDF_Image(CPDF_Document* pDoc) : m_pDocument(pDoc) { +- ASSERT(m_pDocument); ++ DCHECK(m_pDocument); + } + + CPDF_Image::CPDF_Image(CPDF_Document* pDoc, RetainPtr pStream) + : m_bIsInline(true), m_pDocument(pDoc), m_pStream(std::move(pStream)) { +- ASSERT(m_pDocument); +- FinishInitialization(m_pStream->GetDict()); ++ DCHECK(m_pDocument); ++ FinishInitialization(); + } + + CPDF_Image::CPDF_Image(CPDF_Document* pDoc, uint32_t dwStreamObjNum) + : m_pDocument(pDoc), +- m_pStream(ToStream(pDoc->GetIndirectObject(dwStreamObjNum))) { +- ASSERT(m_pDocument); +- FinishInitialization(m_pStream->GetDict()); ++ m_pStream(ToStream(pDoc->GetMutableIndirectObject(dwStreamObjNum))) { ++ DCHECK(m_pDocument); ++ FinishInitialization(); + } + + CPDF_Image::~CPDF_Image() = default; + +-void CPDF_Image::FinishInitialization(CPDF_Dictionary* pStreamDict) { +- m_pOC.Reset(pStreamDict->GetDictFor("OC")); ++void CPDF_Image::FinishInitialization() { ++ RetainPtr pStreamDict = m_pStream->GetMutableDict(); ++ m_pOC = pStreamDict->GetMutableDictFor("OC"); + m_bIsMask = !pStreamDict->KeyExist("ColorSpace") || +- pStreamDict->GetIntegerFor("ImageMask"); ++ pStreamDict->GetBooleanFor("ImageMask", /*bDefault=*/false); + m_bInterpolate = !!pStreamDict->GetIntegerFor("Interpolate"); + m_Height = pStreamDict->GetIntegerFor("Height"); + m_Width = pStreamDict->GetIntegerFor("Width"); +@@ -77,18 +82,26 @@ void CPDF_Image::ConvertStreamToIndirectObject() { + m_pDocument->AddIndirectObject(m_pStream); + } + +-CPDF_Dictionary* CPDF_Image::GetDict() const { ++RetainPtr CPDF_Image::GetDict() const { + return m_pStream ? m_pStream->GetDict() : nullptr; + } + ++RetainPtr CPDF_Image::GetStream() const { ++ return m_pStream; ++} ++ ++RetainPtr CPDF_Image::GetOC() const { ++ return m_pOC; ++} ++ + RetainPtr CPDF_Image::InitJPEG( + pdfium::span src_span) { +- Optional info_opt = +- fxcodec::ModuleMgr::GetInstance()->GetJpegModule()->LoadInfo(src_span); ++ absl::optional info_opt = ++ JpegModule::LoadInfo(src_span); + if (!info_opt.has_value()) + return nullptr; + +- const JpegModule::JpegImageInfo& info = info_opt.value(); ++ const JpegModule::ImageInfo& info = info_opt.value(); + if (!IsValidJpegComponent(info.num_components) || + !IsValidJpegBitsPerComponent(info.bits_per_components)) { + return nullptr; +@@ -103,17 +116,17 @@ RetainPtr CPDF_Image::InitJPEG( + csname = "DeviceRGB"; + } else if (info.num_components == 4) { + csname = "DeviceCMYK"; +- CPDF_Array* pDecode = pDict->SetNewFor("Decode"); ++ auto pDecode = pDict->SetNewFor("Decode"); + for (int n = 0; n < 4; n++) { +- pDecode->AddNew(1); +- pDecode->AddNew(0); ++ pDecode->AppendNew(1); ++ pDecode->AppendNew(0); + } + } + pDict->SetNewFor("ColorSpace", csname); + pDict->SetNewFor("BitsPerComponent", info.bits_per_components); + pDict->SetNewFor("Filter", "DCTDecode"); + if (!info.color_transform) { +- CPDF_Dictionary* pParms = ++ auto pParms = + pDict->SetNewFor(pdfium::stream::kDecodeParms); + pParms->SetNewFor("ColorTransform", 0); + } +@@ -125,43 +138,43 @@ RetainPtr CPDF_Image::InitJPEG( + return pDict; + } + +-void CPDF_Image::SetJpegImage(const RetainPtr& pFile) { ++void CPDF_Image::SetJpegImage(RetainPtr pFile) { + uint32_t size = pdfium::base::checked_cast(pFile->GetSize()); + if (!size) + return; + + uint32_t dwEstimateSize = std::min(size, 8192U); +- std::vector data(dwEstimateSize); +- if (!pFile->ReadBlockAtOffset(data.data(), 0, dwEstimateSize)) ++ DataVector data(dwEstimateSize); ++ if (!pFile->ReadBlockAtOffset(data, 0)) + return; + + RetainPtr pDict = InitJPEG(data); + if (!pDict && size > dwEstimateSize) { + data.resize(size); +- if (pFile->ReadBlockAtOffset(data.data(), 0, size)) ++ if (pFile->ReadBlockAtOffset(data, 0)) + pDict = InitJPEG(data); + } + if (!pDict) + return; + +- m_pStream->InitStreamFromFile(pFile, std::move(pDict)); ++ m_pStream->InitStreamFromFile(std::move(pFile), std::move(pDict)); + } + +-void CPDF_Image::SetJpegImageInline( +- const RetainPtr& pFile) { ++void CPDF_Image::SetJpegImageInline(RetainPtr pFile) { + uint32_t size = pdfium::base::checked_cast(pFile->GetSize()); + if (!size) + return; + +- std::vector data(size); +- if (!pFile->ReadBlockAtOffset(data.data(), 0, size)) ++ DataVector data(size); ++ if (!pFile->ReadBlockAtOffset(data, 0)) + return; + + RetainPtr pDict = InitJPEG(data); + if (!pDict) + return; + +- m_pStream->InitStream(data, std::move(pDict)); ++ m_pStream = ++ pdfium::MakeRetain(std::move(data), std::move(pDict)); + } + + void CPDF_Image::SetImage(const RetainPtr& pBitmap) { +@@ -184,7 +197,7 @@ void CPDF_Image::SetImage(const RetainPtr& pBitmap) { + int32_t set_r = 0; + int32_t set_g = 0; + int32_t set_b = 0; +- if (!pBitmap->IsAlphaMask()) { ++ if (!pBitmap->IsMaskFormat()) { + std::tie(reset_a, reset_r, reset_g, reset_b) = + ArgbDecode(pBitmap->GetPaletteArgb(0)); + std::tie(set_a, set_r, set_g, set_b) = +@@ -193,15 +206,15 @@ void CPDF_Image::SetImage(const RetainPtr& pBitmap) { + if (set_a == 0 || reset_a == 0) { + pDict->SetNewFor("ImageMask", true); + if (reset_a == 0) { +- CPDF_Array* pArray = pDict->SetNewFor("Decode"); +- pArray->AddNew(1); +- pArray->AddNew(0); ++ auto pArray = pDict->SetNewFor("Decode"); ++ pArray->AppendNew(1); ++ pArray->AppendNew(0); + } + } else { +- CPDF_Array* pCS = pDict->SetNewFor("ColorSpace"); +- pCS->AddNew("Indexed"); +- pCS->AddNew("DeviceRGB"); +- pCS->AddNew(1); ++ auto pCS = pDict->SetNewFor("ColorSpace"); ++ pCS->AppendNew("Indexed"); ++ pCS->AppendNew("DeviceRGB"); ++ pCS->AppendNew(1); + ByteString ct; + { + // Span's lifetime must end before ReleaseBuffer() below. +@@ -214,33 +227,32 @@ void CPDF_Image::SetImage(const RetainPtr& pBitmap) { + pBuf[5] = static_cast(set_b); + } + ct.ReleaseBuffer(6); +- pCS->AddNew(ct, true); ++ pCS->AppendNew(ct, true); + } + pDict->SetNewFor("BitsPerComponent", 1); + dest_pitch = (BitmapWidth + 7) / 8; + } else if (bpp == 8) { +- size_t palette_size = pBitmap->GetPaletteSize(); ++ size_t palette_size = pBitmap->GetRequiredPaletteSize(); + if (palette_size > 0) { +- ASSERT(palette_size <= 256); +- CPDF_Array* pCS = m_pDocument->NewIndirect(); +- pCS->AddNew("Indexed"); +- pCS->AddNew("DeviceRGB"); +- pCS->AddNew(static_cast(palette_size - 1)); +- std::unique_ptr pColorTable( +- FX_Alloc2D(uint8_t, palette_size, 3)); +- uint8_t* ptr = pColorTable.get(); ++ DCHECK(palette_size <= 256); ++ auto pCS = m_pDocument->NewIndirect(); ++ pCS->AppendNew("Indexed"); ++ pCS->AppendNew("DeviceRGB"); ++ pCS->AppendNew(static_cast(palette_size - 1)); ++ DataVector color_table(Fx2DSizeOrDie(palette_size, 3)); ++ auto color_table_span = pdfium::make_span(color_table); + for (size_t i = 0; i < palette_size; i++) { + uint32_t argb = pBitmap->GetPaletteArgb(i); +- ptr[0] = FXARGB_R(argb); +- ptr[1] = FXARGB_G(argb); +- ptr[2] = FXARGB_B(argb); +- ptr += 3; ++ color_table_span[0] = FXARGB_R(argb); ++ color_table_span[1] = FXARGB_G(argb); ++ color_table_span[2] = FXARGB_B(argb); ++ color_table_span = color_table_span.subspan(3); + } + auto pNewDict = m_pDocument->New(); +- CPDF_Stream* pCTS = m_pDocument->NewIndirect( +- std::move(pColorTable), palette_size * 3, std::move(pNewDict)); +- pCS->AddNew(m_pDocument.Get(), pCTS->GetObjNum()); +- pDict->SetNewFor("ColorSpace", m_pDocument.Get(), ++ auto pCTS = m_pDocument->NewIndirect(std::move(color_table), ++ std::move(pNewDict)); ++ pCS->AppendNew(m_pDocument, pCTS->GetObjNum()); ++ pDict->SetNewFor("ColorSpace", m_pDocument, + pCS->GetObjNum()); + } else { + pDict->SetNewFor("ColorSpace", "DeviceGray"); +@@ -255,84 +267,78 @@ void CPDF_Image::SetImage(const RetainPtr& pBitmap) { + } + + RetainPtr pMaskBitmap; +- if (pBitmap->HasAlpha()) ++ if (pBitmap->IsAlphaFormat()) + pMaskBitmap = pBitmap->CloneAlphaMask(); + + if (pMaskBitmap) { +- int32_t maskWidth = pMaskBitmap->GetWidth(); +- int32_t maskHeight = pMaskBitmap->GetHeight(); +- std::unique_ptr mask_buf; +- int32_t mask_size = 0; ++ const int32_t mask_width = pMaskBitmap->GetWidth(); ++ const int32_t mask_height = pMaskBitmap->GetHeight(); ++ DataVector mask_buf; + RetainPtr pMaskDict = +- CreateXObjectImageDict(maskWidth, maskHeight); ++ CreateXObjectImageDict(mask_width, mask_height); + pMaskDict->SetNewFor("ColorSpace", "DeviceGray"); + pMaskDict->SetNewFor("BitsPerComponent", 8); +- if (pMaskBitmap->GetFormat() != FXDIB_1bppMask) { +- mask_buf.reset(FX_Alloc2D(uint8_t, maskHeight, maskWidth)); +- mask_size = maskHeight * maskWidth; // Safe since checked alloc returned. +- for (int32_t a = 0; a < maskHeight; a++) { +- memcpy(mask_buf.get() + a * maskWidth, pMaskBitmap->GetScanline(a), +- maskWidth); ++ if (pMaskBitmap->GetFormat() != FXDIB_Format::k1bppMask) { ++ mask_buf.resize(Fx2DSizeOrDie(mask_width, mask_height)); ++ for (int32_t a = 0; a < mask_height; a++) { ++ fxcrt::spancpy(pdfium::make_span(mask_buf).subspan(a * mask_width), ++ pMaskBitmap->GetScanline(a).first(mask_width)); + } + } +- pMaskDict->SetNewFor("Length", mask_size); +- CPDF_Stream* pNewStream = m_pDocument->NewIndirect( +- std::move(mask_buf), mask_size, std::move(pMaskDict)); +- pDict->SetNewFor("SMask", m_pDocument.Get(), ++ pMaskDict->SetNewFor( ++ "Length", pdfium::base::checked_cast(mask_buf.size())); ++ auto pNewStream = m_pDocument->NewIndirect( ++ std::move(mask_buf), std::move(pMaskDict)); ++ pDict->SetNewFor("SMask", m_pDocument, + pNewStream->GetObjNum()); + } + +- uint8_t* src_buf = pBitmap->GetBuffer(); +- int32_t src_pitch = pBitmap->GetPitch(); +- std::unique_ptr dest_buf( +- FX_Alloc2D(uint8_t, dest_pitch, BitmapHeight)); +- // Safe as checked alloc returned. +- size_t dest_size = dest_pitch * BitmapHeight; +- auto dest_span = pdfium::make_span(dest_buf.get(), dest_size); +- size_t dest_span_offset = 0; ++ DataVector dest_buf(Fx2DSizeOrDie(dest_pitch, BitmapHeight)); ++ pdfium::span dest_span = pdfium::make_span(dest_buf); ++ pdfium::span src_span = pBitmap->GetBuffer(); ++ const int32_t src_pitch = pBitmap->GetPitch(); + if (bCopyWithoutAlpha) { + for (int32_t i = 0; i < BitmapHeight; i++) { +- memcpy(&dest_span[dest_span_offset], src_buf, dest_pitch); +- dest_span_offset += dest_pitch; +- src_buf += src_pitch; ++ fxcrt::spancpy(dest_span, src_span.first(dest_pitch)); ++ dest_span = dest_span.subspan(dest_pitch); ++ src_span = src_span.subspan(src_pitch); + } + } else { +- int32_t src_offset = 0; ++ const size_t src_step = bpp == 24 ? 3 : 4; + for (int32_t row = 0; row < BitmapHeight; row++) { +- size_t dest_span_row_offset = dest_span_offset; +- src_offset = row * src_pitch; ++ uint8_t* dest_ptr = dest_span.data(); ++ const uint8_t* src_ptr = src_span.data(); + for (int32_t column = 0; column < BitmapWidth; column++) { +- float alpha = 1; +- dest_span[dest_span_row_offset] = +- static_cast(src_buf[src_offset + 2] * alpha); +- dest_span[dest_span_row_offset + 1] = +- static_cast(src_buf[src_offset + 1] * alpha); +- dest_span[dest_span_row_offset + 2] = +- static_cast(src_buf[src_offset] * alpha); +- dest_span_row_offset += 3; +- src_offset += bpp == 24 ? 3 : 4; ++ dest_ptr[0] = src_ptr[2]; ++ dest_ptr[1] = src_ptr[1]; ++ dest_ptr[2] = src_ptr[0]; ++ dest_ptr += 3; ++ src_ptr += src_step; + } +- +- dest_span_offset += dest_pitch; ++ dest_span = dest_span.subspan(dest_pitch); ++ src_span = src_span.subspan(src_pitch); + } + } +- if (!m_pStream) +- m_pStream = pdfium::MakeRetain(); + +- m_pStream->InitStream(dest_span, std::move(pDict)); +- m_bIsMask = pBitmap->IsAlphaMask(); ++ m_pStream = ++ pdfium::MakeRetain(std::move(dest_buf), std::move(pDict)); ++ m_bIsMask = pBitmap->IsMaskFormat(); + m_Width = BitmapWidth; + m_Height = BitmapHeight; + } + + void CPDF_Image::ResetCache(CPDF_Page* pPage) { + RetainPtr pHolder(this); +- pPage->GetRenderCache()->ResetBitmapForImage(pHolder); ++ pPage->GetPageImageCache()->ResetBitmapForImage(std::move(pHolder)); ++} ++ ++RetainPtr CPDF_Image::CreateNewDIB() const { ++ return pdfium::MakeRetain(GetDocument(), GetStream()); + } + + RetainPtr CPDF_Image::LoadDIBBase() const { +- auto source = pdfium::MakeRetain(); +- if (!source->Load(m_pDocument.Get(), m_pStream.Get())) ++ RetainPtr source = CreateNewDIB(); ++ if (!source->Load()) + return nullptr; + + if (!source->IsJBigImage()) +@@ -353,14 +359,15 @@ RetainPtr CPDF_Image::DetachMask() { + } + + bool CPDF_Image::StartLoadDIBBase(const CPDF_Dictionary* pFormResource, +- CPDF_Dictionary* pPageResource, ++ const CPDF_Dictionary* pPageResource, + bool bStdCS, +- uint32_t GroupFamily, +- bool bLoadMask) { +- auto source = pdfium::MakeRetain(); +- CPDF_DIB::LoadState ret = source->StartLoadDIBBase( +- m_pDocument.Get(), m_pStream.Get(), true, pFormResource, pPageResource, +- bStdCS, GroupFamily, bLoadMask); ++ CPDF_ColorSpace::Family GroupFamily, ++ bool bLoadMask, ++ const CFX_Size& max_size_required) { ++ RetainPtr source = CreateNewDIB(); ++ CPDF_DIB::LoadState ret = ++ source->StartLoadDIBBase(true, pFormResource, pPageResource, bStdCS, ++ GroupFamily, bLoadMask, max_size_required); + if (ret == CPDF_DIB::LoadState::kFail) { + m_pDIBBase.Reset(); + return false; +diff --git a/core/fpdfapi/page/cpdf_image.h b/core/fpdfapi/page/cpdf_image.h +index 4194d490c..04355a9d7 100644 +--- a/core/fpdfapi/page/cpdf_image.h ++++ b/core/fpdfapi/page/cpdf_image.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,15 +7,16 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_IMAGE_H_ + #define CORE_FPDFAPI_PAGE_CPDF_IMAGE_H_ + +-#include ++#include + +-#include "core/fxcrt/fx_system.h" ++#include "core/fpdfapi/page/cpdf_colorspace.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + #include "third_party/base/span.h" + + class CFX_DIBBase; + class CFX_DIBitmap; ++class CPDF_DIB; + class CPDF_Dictionary; + class CPDF_Document; + class CPDF_Page; +@@ -25,40 +26,41 @@ class IFX_SeekableReadStream; + + class CPDF_Image final : public Retainable { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + static bool IsValidJpegComponent(int32_t comps); + static bool IsValidJpegBitsPerComponent(int32_t bpc); + + void ConvertStreamToIndirectObject(); + +- CPDF_Dictionary* GetDict() const; +- CPDF_Stream* GetStream() const { return m_pStream.Get(); } +- const CPDF_Dictionary* GetOC() const { return m_pOC.Get(); } +- CPDF_Document* GetDocument() const { return m_pDocument.Get(); } ++ RetainPtr GetDict() const; ++ RetainPtr GetStream() const; ++ RetainPtr GetOC() const; ++ CPDF_Document* GetDocument() const { return m_pDocument; } + + int32_t GetPixelHeight() const { return m_Height; } + int32_t GetPixelWidth() const { return m_Width; } +- ++ uint32_t GetMatteColor() const { return m_MatteColor; } + bool IsInline() const { return m_bIsInline; } + bool IsMask() const { return m_bIsMask; } + bool IsInterpol() const { return m_bInterpolate; } + ++ RetainPtr CreateNewDIB() const; + RetainPtr LoadDIBBase() const; + + void SetImage(const RetainPtr& pBitmap); +- void SetJpegImage(const RetainPtr& pFile); +- void SetJpegImageInline(const RetainPtr& pFile); ++ void SetJpegImage(RetainPtr pFile); ++ void SetJpegImageInline(RetainPtr pFile); + + void ResetCache(CPDF_Page* pPage); + + // Returns whether to Continue() or not. + bool StartLoadDIBBase(const CPDF_Dictionary* pFormResource, +- CPDF_Dictionary* pPageResource, ++ const CPDF_Dictionary* pPageResource, + bool bStdCS, +- uint32_t GroupFamily, +- bool bLoadMask); ++ CPDF_ColorSpace::Family GroupFamily, ++ bool bLoadMask, ++ const CFX_Size& max_size_required); + + // Returns whether to Continue() or not. + bool Continue(PauseIndicatorIface* pPause); +@@ -66,27 +68,25 @@ class CPDF_Image final : public Retainable { + RetainPtr DetachBitmap(); + RetainPtr DetachMask(); + +- RetainPtr m_pDIBBase; +- RetainPtr m_pMask; +- uint32_t m_MatteColor = 0; +- + private: + explicit CPDF_Image(CPDF_Document* pDoc); + CPDF_Image(CPDF_Document* pDoc, RetainPtr pStream); + CPDF_Image(CPDF_Document* pDoc, uint32_t dwStreamObjNum); + ~CPDF_Image() override; + +- void FinishInitialization(CPDF_Dictionary* pStreamDict); ++ void FinishInitialization(); + RetainPtr InitJPEG(pdfium::span src_span); +- + RetainPtr CreateXObjectImageDict(int width, int height); + + int32_t m_Height = 0; + int32_t m_Width = 0; ++ uint32_t m_MatteColor = 0; + bool m_bIsInline = false; + bool m_bIsMask = false; + bool m_bInterpolate = false; + UnownedPtr const m_pDocument; ++ RetainPtr m_pDIBBase; ++ RetainPtr m_pMask; + RetainPtr m_pStream; + RetainPtr m_pOC; + }; +diff --git a/core/fpdfapi/page/cpdf_imageloader.cpp b/core/fpdfapi/page/cpdf_imageloader.cpp +new file mode 100644 +index 000000000..78f4f1f53 +--- /dev/null ++++ b/core/fpdfapi/page/cpdf_imageloader.cpp +@@ -0,0 +1,80 @@ ++// Copyright 2016 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fpdfapi/page/cpdf_imageloader.h" ++ ++#include ++ ++#include "core/fpdfapi/page/cpdf_dib.h" ++#include "core/fpdfapi/page/cpdf_image.h" ++#include "core/fpdfapi/page/cpdf_imageobject.h" ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" ++#include "core/fpdfapi/page/cpdf_transferfunc.h" ++#include "core/fxge/dib/cfx_dibitmap.h" ++#include "third_party/base/check.h" ++ ++CPDF_ImageLoader::CPDF_ImageLoader() = default; ++ ++CPDF_ImageLoader::~CPDF_ImageLoader() = default; ++ ++bool CPDF_ImageLoader::Start(const CPDF_ImageObject* pImage, ++ CPDF_PageImageCache* pPageImageCache, ++ const CPDF_Dictionary* pFormResource, ++ const CPDF_Dictionary* pPageResource, ++ bool bStdCS, ++ CPDF_ColorSpace::Family eFamily, ++ bool bLoadMask, ++ const CFX_Size& max_size_required) { ++ m_pCache = pPageImageCache; ++ m_pImageObject = pImage; ++ bool ret; ++ if (m_pCache) { ++ ret = m_pCache->StartGetCachedBitmap(m_pImageObject->GetImage(), ++ pFormResource, pPageResource, bStdCS, ++ eFamily, bLoadMask, max_size_required); ++ } else { ++ ret = m_pImageObject->GetImage()->StartLoadDIBBase( ++ pFormResource, pPageResource, bStdCS, eFamily, bLoadMask, ++ max_size_required); ++ } ++ if (!ret) ++ HandleFailure(); ++ return ret; ++} ++ ++bool CPDF_ImageLoader::Continue(PauseIndicatorIface* pPause) { ++ bool ret = m_pCache ? m_pCache->Continue(pPause) ++ : m_pImageObject->GetImage()->Continue(pPause); ++ if (!ret) ++ HandleFailure(); ++ return ret; ++} ++ ++RetainPtr CPDF_ImageLoader::TranslateImage( ++ RetainPtr pTransferFunc) { ++ DCHECK(pTransferFunc); ++ DCHECK(!pTransferFunc->GetIdentity()); ++ m_pBitmap = pTransferFunc->TranslateImage(std::move(m_pBitmap)); ++ if (m_bCached && m_pMask) ++ m_pMask = m_pMask->Realize(); ++ m_bCached = false; ++ return m_pBitmap; ++} ++ ++void CPDF_ImageLoader::HandleFailure() { ++ if (m_pCache) { ++ m_bCached = true; ++ m_pBitmap = m_pCache->DetachCurBitmap(); ++ m_pMask = m_pCache->DetachCurMask(); ++ m_MatteColor = m_pCache->GetCurMatteColor(); ++ return; ++ } ++ RetainPtr pImage = m_pImageObject->GetImage(); ++ m_bCached = false; ++ m_pBitmap = pImage->DetachBitmap(); ++ m_pMask = pImage->DetachMask(); ++ m_MatteColor = pImage->GetMatteColor(); ++} +diff --git a/core/fpdfapi/render/cpdf_imageloader.h b/core/fpdfapi/page/cpdf_imageloader.h +similarity index 53% +rename from core/fpdfapi/render/cpdf_imageloader.h +rename to core/fpdfapi/page/cpdf_imageloader.h +index 7e12c0103..b0fcfb9a0 100644 +--- a/core/fpdfapi/render/cpdf_imageloader.h ++++ b/core/fpdfapi/page/cpdf_imageloader.h +@@ -1,19 +1,20 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#ifndef CORE_FPDFAPI_RENDER_CPDF_IMAGELOADER_H_ +-#define CORE_FPDFAPI_RENDER_CPDF_IMAGELOADER_H_ ++#ifndef CORE_FPDFAPI_PAGE_CPDF_IMAGELOADER_H_ ++#define CORE_FPDFAPI_PAGE_CPDF_IMAGELOADER_H_ + ++#include "core/fpdfapi/page/cpdf_colorspace.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + + class CFX_DIBBase; ++class CPDF_Dictionary; + class CPDF_ImageObject; +-class CPDF_PageRenderCache; +-class CPDF_RenderStatus; ++class CPDF_PageImageCache; + class CPDF_TransferFunc; + class PauseIndicatorIface; + +@@ -22,16 +23,18 @@ class CPDF_ImageLoader { + CPDF_ImageLoader(); + ~CPDF_ImageLoader(); + +- bool Start(CPDF_ImageObject* pImage, +- CPDF_PageRenderCache* pCache, ++ bool Start(const CPDF_ImageObject* pImage, ++ CPDF_PageImageCache* pPageImageCache, ++ const CPDF_Dictionary* pFormResource, ++ const CPDF_Dictionary* pPageResource, + bool bStdCS, +- uint32_t GroupFamily, ++ CPDF_ColorSpace::Family eFamily, + bool bLoadMask, +- CPDF_RenderStatus* pRenderStatus); +- bool Continue(PauseIndicatorIface* pPause, CPDF_RenderStatus* pRenderStatus); ++ const CFX_Size& max_size_required); ++ bool Continue(PauseIndicatorIface* pPause); + + RetainPtr TranslateImage( +- const RetainPtr& pTransferFunc); ++ RetainPtr pTransferFunc); + + const RetainPtr& GetBitmap() const { return m_pBitmap; } + const RetainPtr& GetMask() const { return m_pMask; } +@@ -44,8 +47,8 @@ class CPDF_ImageLoader { + bool m_bCached = false; + RetainPtr m_pBitmap; + RetainPtr m_pMask; +- UnownedPtr m_pCache; +- UnownedPtr m_pImageObject; ++ UnownedPtr m_pCache; ++ UnownedPtr m_pImageObject; + }; + +-#endif // CORE_FPDFAPI_RENDER_CPDF_IMAGELOADER_H_ ++#endif // CORE_FPDFAPI_PAGE_CPDF_IMAGELOADER_H_ +diff --git a/core/fpdfapi/page/cpdf_imageobject.cpp b/core/fpdfapi/page/cpdf_imageobject.cpp +index 0f6a26ba4..c031502e0 100644 +--- a/core/fpdfapi/page/cpdf_imageobject.cpp ++++ b/core/fpdfapi/page/cpdf_imageobject.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,12 +6,12 @@ + + #include "core/fpdfapi/page/cpdf_imageobject.h" + +-#include ++#include + + #include "core/fpdfapi/page/cpdf_docpagedata.h" + #include "core/fpdfapi/page/cpdf_image.h" +-#include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_stream.h" ++#include "core/fxcrt/fx_coordinates.h" + #include "core/fxge/dib/cfx_dibbase.h" + #include "core/fxge/dib/cfx_dibitmap.h" + +@@ -25,7 +25,7 @@ CPDF_ImageObject::~CPDF_ImageObject() { + } + + CPDF_PageObject::Type CPDF_ImageObject::GetType() const { +- return IMAGE; ++ return Type::kImage; + } + + void CPDF_ImageObject::Transform(const CFX_Matrix& matrix) { +@@ -48,12 +48,13 @@ const CPDF_ImageObject* CPDF_ImageObject::AsImage() const { + + void CPDF_ImageObject::CalcBoundingBox() { + static constexpr CFX_FloatRect kRect(0.0f, 0.0f, 1.0f, 1.0f); ++ SetOriginalRect(kRect); + SetRect(m_Matrix.TransformRect(kRect)); + } + +-void CPDF_ImageObject::SetImage(const RetainPtr& pImage) { ++void CPDF_ImageObject::SetImage(RetainPtr pImage) { + MaybePurgeCache(); +- m_pImage = pImage; ++ m_pImage = std::move(pImage); + } + + RetainPtr CPDF_ImageObject::GetImage() const { +@@ -63,23 +64,29 @@ RetainPtr CPDF_ImageObject::GetImage() const { + RetainPtr CPDF_ImageObject::GetIndependentBitmap() const { + RetainPtr pSource = GetImage()->LoadDIBBase(); + +- // Clone() is non-virtual, and can't be overloaded by CPDF_DIB to +- // return a clone of the subclass as one would typically expect from a +- // such a method. Instead, it only clones the CFX_DIBBase, none of whose +- // members point to objects owned by |this| or the form containing |this|. +- // As a result, the clone may outlive them. +- return pSource ? pSource->Clone(nullptr) : nullptr; ++ // Realize() is non-virtual, and can't be overloaded by CPDF_DIB to ++ // return a full-up CPDF_DIB subclass. Instead, it only works upon the ++ // CFX_DIBBase, which is convenient since none of its members point to ++ // objects owned by |this| or the form containing |this|. As a result, ++ // the new bitmap may outlive them, giving the "independent" property ++ // this method is named after. ++ return pSource ? pSource->Realize() : nullptr; ++} ++ ++void CPDF_ImageObject::SetImageMatrix(const CFX_Matrix& matrix) { ++ m_Matrix = matrix; ++ CalcBoundingBox(); + } + + void CPDF_ImageObject::MaybePurgeCache() { + if (!m_pImage) + return; + +- auto* pPageData = CPDF_DocPageData::FromDocument(m_pImage->GetDocument()); +- if (!pPageData) ++ auto* pDoc = m_pImage->GetDocument(); ++ if (!pDoc) + return; + +- CPDF_Stream* pStream = m_pImage->GetStream(); ++ RetainPtr pStream = m_pImage->GetStream(); + if (!pStream) + return; + +@@ -88,5 +95,5 @@ void CPDF_ImageObject::MaybePurgeCache() { + return; + + m_pImage.Reset(); // Clear my reference before asking the cache. +- pPageData->MaybePurgeImage(objnum); ++ pDoc->MaybePurgeImage(objnum); + } +diff --git a/core/fpdfapi/page/cpdf_imageobject.h b/core/fpdfapi/page/cpdf_imageobject.h +index f221a9816..06e5a6468 100644 +--- a/core/fpdfapi/page/cpdf_imageobject.h ++++ b/core/fpdfapi/page/cpdf_imageobject.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -28,11 +28,11 @@ class CPDF_ImageObject final : public CPDF_PageObject { + const CPDF_ImageObject* AsImage() const override; + + void CalcBoundingBox(); +- void SetImage(const RetainPtr& pImage); ++ void SetImage(RetainPtr pImage); + RetainPtr GetImage() const; + RetainPtr GetIndependentBitmap() const; + +- void set_matrix(const CFX_Matrix& matrix) { m_Matrix = matrix; } ++ void SetImageMatrix(const CFX_Matrix& matrix); + const CFX_Matrix& matrix() const { return m_Matrix; } + + private: +diff --git a/core/fpdfapi/page/cpdf_indexedcs.cpp b/core/fpdfapi/page/cpdf_indexedcs.cpp +new file mode 100644 +index 000000000..85f694fe3 +--- /dev/null ++++ b/core/fpdfapi/page/cpdf_indexedcs.cpp +@@ -0,0 +1,109 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fpdfapi/page/cpdf_indexedcs.h" ++ ++#include ++#include ++ ++#include "core/fpdfapi/page/cpdf_colorspace.h" ++#include "core/fpdfapi/page/cpdf_docpagedata.h" ++#include "core/fpdfapi/parser/cpdf_array.h" ++#include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fpdfapi/parser/cpdf_object.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" ++#include "core/fpdfapi/parser/cpdf_stream_acc.h" ++#include "core/fpdfapi/parser/cpdf_string.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_2d_size.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/span.h" ++ ++CPDF_IndexedCS::CPDF_IndexedCS() : CPDF_BasedCS(Family::kIndexed) {} ++ ++CPDF_IndexedCS::~CPDF_IndexedCS() = default; ++ ++const CPDF_IndexedCS* CPDF_IndexedCS::AsIndexedCS() const { ++ return this; ++} ++ ++uint32_t CPDF_IndexedCS::v_Load(CPDF_Document* pDoc, ++ const CPDF_Array* pArray, ++ std::set* pVisited) { ++ if (pArray->size() < 4) ++ return 0; ++ ++ RetainPtr pBaseObj = pArray->GetDirectObjectAt(1); ++ if (HasSameArray(pBaseObj.Get())) ++ return 0; ++ ++ auto* pDocPageData = CPDF_DocPageData::FromDocument(pDoc); ++ m_pBaseCS = ++ pDocPageData->GetColorSpaceGuarded(pBaseObj.Get(), nullptr, pVisited); ++ if (!m_pBaseCS) ++ return 0; ++ ++ // The base color space cannot be a Pattern or Indexed space, according to the ++ // PDF 1.7 spec, page 263. ++ Family family = m_pBaseCS->GetFamily(); ++ if (family == Family::kIndexed || family == Family::kPattern) ++ return 0; ++ ++ m_nBaseComponents = m_pBaseCS->CountComponents(); ++ DCHECK(m_nBaseComponents); ++ m_pCompMinMax = DataVector(Fx2DSizeOrDie(m_nBaseComponents, 2)); ++ float defvalue; ++ for (uint32_t i = 0; i < m_nBaseComponents; i++) { ++ m_pBaseCS->GetDefaultValue(i, &defvalue, &m_pCompMinMax[i * 2], ++ &m_pCompMinMax[i * 2 + 1]); ++ m_pCompMinMax[i * 2 + 1] -= m_pCompMinMax[i * 2]; ++ } ++ m_MaxIndex = pArray->GetIntegerAt(2); ++ ++ RetainPtr pTableObj = pArray->GetDirectObjectAt(3); ++ if (!pTableObj) ++ return 0; ++ ++ if (const CPDF_String* pString = pTableObj->AsString()) { ++ m_Table = pString->GetString(); ++ } else if (const CPDF_Stream* pStream = pTableObj->AsStream()) { ++ auto pAcc = pdfium::MakeRetain(pdfium::WrapRetain(pStream)); ++ pAcc->LoadAllDataFiltered(); ++ m_Table = ByteStringView(pAcc->GetSpan()); ++ } ++ return 1; ++} ++ ++bool CPDF_IndexedCS::GetRGB(pdfium::span pBuf, ++ float* R, ++ float* G, ++ float* B) const { ++ int32_t index = static_cast(pBuf[0]); ++ if (index < 0 || index > m_MaxIndex) ++ return false; ++ ++ DCHECK(m_nBaseComponents); ++ DCHECK_EQ(m_nBaseComponents, m_pBaseCS->CountComponents()); ++ ++ FX_SAFE_SIZE_T length = index; ++ length += 1; ++ length *= m_nBaseComponents; ++ if (!length.IsValid() || length.ValueOrDie() > m_Table.GetLength()) { ++ *R = 0; ++ *G = 0; ++ *B = 0; ++ return false; ++ } ++ ++ std::vector comps(m_nBaseComponents); ++ const uint8_t* pTable = m_Table.raw_str(); ++ for (uint32_t i = 0; i < m_nBaseComponents; ++i) { ++ comps[i] = ++ m_pCompMinMax[i * 2] + ++ m_pCompMinMax[i * 2 + 1] * pTable[index * m_nBaseComponents + i] / 255; ++ } ++ return m_pBaseCS->GetRGB(comps, R, G, B); ++} +diff --git a/core/fpdfapi/page/cpdf_indexedcs.h b/core/fpdfapi/page/cpdf_indexedcs.h +new file mode 100644 +index 000000000..325501fb9 +--- /dev/null ++++ b/core/fpdfapi/page/cpdf_indexedcs.h +@@ -0,0 +1,43 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FPDFAPI_PAGE_CPDF_INDEXEDCS_H_ ++#define CORE_FPDFAPI_PAGE_CPDF_INDEXEDCS_H_ ++ ++#include ++ ++#include "core/fpdfapi/page/cpdf_basedcs.h" ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/retain_ptr.h" ++ ++class CPDF_Document; ++ ++class CPDF_IndexedCS final : public CPDF_BasedCS { ++ public: ++ CONSTRUCT_VIA_MAKE_RETAIN; ++ ~CPDF_IndexedCS() override; ++ ++ // CPDF_ColorSpace: ++ bool GetRGB(pdfium::span pBuf, ++ float* R, ++ float* G, ++ float* B) const override; ++ const CPDF_IndexedCS* AsIndexedCS() const override; ++ uint32_t v_Load(CPDF_Document* pDoc, ++ const CPDF_Array* pArray, ++ std::set* pVisited) override; ++ ++ int GetMaxIndex() const { return m_MaxIndex; } ++ ++ private: ++ CPDF_IndexedCS(); ++ ++ uint32_t m_nBaseComponents = 0; ++ int m_MaxIndex = 0; ++ ByteString m_Table; ++ DataVector m_pCompMinMax; ++}; ++ ++#endif // CORE_FPDFAPI_PAGE_CPDF_INDEXEDCS_H_ +diff --git a/core/fpdfapi/page/cpdf_meshstream.cpp b/core/fpdfapi/page/cpdf_meshstream.cpp +index 06b23296a..95f560f59 100644 +--- a/core/fpdfapi/page/cpdf_meshstream.cpp ++++ b/core/fpdfapi/page/cpdf_meshstream.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,13 +6,16 @@ + + #include "core/fpdfapi/page/cpdf_meshstream.h" + ++#include ++ + #include "core/fpdfapi/page/cpdf_colorspace.h" + #include "core/fpdfapi/page/cpdf_function.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" +-#include "third_party/base/ptr_util.h" ++#include "core/fxcrt/cfx_bitstream.h" ++#include "third_party/base/check.h" + #include "third_party/base/span.h" + + namespace { +@@ -97,33 +100,21 @@ CPDF_MeshVertex::~CPDF_MeshVertex() = default; + CPDF_MeshStream::CPDF_MeshStream( + ShadingType type, + const std::vector>& funcs, +- const CPDF_Stream* pShadingStream, +- const RetainPtr& pCS) ++ RetainPtr pShadingStream, ++ RetainPtr pCS) + : m_type(type), + m_funcs(funcs), +- m_pShadingStream(pShadingStream), +- m_pCS(pCS), +- m_nCoordBits(0), +- m_nComponentBits(0), +- m_nFlagBits(0), +- m_nComponents(0), +- m_CoordMax(0), +- m_ComponentMax(0), +- m_xmin(0), +- m_xmax(0), +- m_ymin(0), +- m_ymax(0), +- m_pStream(pdfium::MakeRetain(pShadingStream)) { +- memset(&m_ColorMin, 0, sizeof(m_ColorMin)); +- memset(&m_ColorMax, 0, sizeof(m_ColorMax)); +-} ++ m_pShadingStream(std::move(pShadingStream)), ++ m_pCS(std::move(pCS)), ++ m_pStream(pdfium::MakeRetain(m_pShadingStream)) {} + +-CPDF_MeshStream::~CPDF_MeshStream() {} ++CPDF_MeshStream::~CPDF_MeshStream() = default; + + bool CPDF_MeshStream::Load() { + m_pStream->LoadAllDataFiltered(); +- m_BitStream = pdfium::MakeUnique(m_pStream->GetSpan()); +- const CPDF_Dictionary* pDict = m_pShadingStream->GetDict(); ++ m_BitStream = std::make_unique(m_pStream->GetSpan()); ++ ++ RetainPtr pDict = m_pShadingStream->GetDict(); + m_nCoordBits = pDict->GetIntegerFor("BitsPerCoordinate"); + m_nComponentBits = pDict->GetIntegerFor("BitsPerComponent"); + if (ShouldCheckBPC(m_type)) { +@@ -142,17 +133,17 @@ bool CPDF_MeshStream::Load() { + return false; + + m_nComponents = m_funcs.empty() ? nComponents : 1; +- const CPDF_Array* pDecode = pDict->GetArrayFor("Decode"); ++ RetainPtr pDecode = pDict->GetArrayFor("Decode"); + if (!pDecode || pDecode->size() != 4 + m_nComponents * 2) + return false; + +- m_xmin = pDecode->GetNumberAt(0); +- m_xmax = pDecode->GetNumberAt(1); +- m_ymin = pDecode->GetNumberAt(2); +- m_ymax = pDecode->GetNumberAt(3); ++ m_xmin = pDecode->GetFloatAt(0); ++ m_xmax = pDecode->GetFloatAt(1); ++ m_ymin = pDecode->GetFloatAt(2); ++ m_ymax = pDecode->GetFloatAt(3); + for (uint32_t i = 0; i < m_nComponents; ++i) { +- m_ColorMin[i] = pDecode->GetNumberAt(i * 2 + 4); +- m_ColorMax[i] = pDecode->GetNumberAt(i * 2 + 5); ++ m_ColorMin[i] = pDecode->GetFloatAt(i * 2 + 4); ++ m_ColorMax[i] = pDecode->GetFloatAt(i * 2 + 5); + } + + if (ShouldCheckBPC(m_type)) { +@@ -162,6 +153,18 @@ bool CPDF_MeshStream::Load() { + return true; + } + ++void CPDF_MeshStream::SkipBits(uint32_t nbits) { ++ m_BitStream->SkipBits(nbits); ++} ++ ++void CPDF_MeshStream::ByteAlign() { ++ m_BitStream->ByteAlign(); ++} ++ ++bool CPDF_MeshStream::IsEOF() const { ++ return m_BitStream->IsEOF(); ++} ++ + bool CPDF_MeshStream::CanReadFlag() const { + return m_BitStream->BitsRemaining() >= m_nFlagBits; + } +@@ -175,12 +178,12 @@ bool CPDF_MeshStream::CanReadColor() const { + } + + uint32_t CPDF_MeshStream::ReadFlag() { +- ASSERT(ShouldCheckBitsPerFlag(m_type)); ++ DCHECK(ShouldCheckBitsPerFlag(m_type)); + return m_BitStream->GetBits(m_nFlagBits) & 0x03; + } + + CFX_PointF CPDF_MeshStream::ReadCoords() { +- ASSERT(ShouldCheckBPC(m_type)); ++ DCHECK(ShouldCheckBPC(m_type)); + + CFX_PointF pos; + if (m_nCoordBits == 32) { +@@ -198,7 +201,7 @@ CFX_PointF CPDF_MeshStream::ReadCoords() { + } + + std::tuple CPDF_MeshStream::ReadColor() { +- ASSERT(ShouldCheckBPC(m_type)); ++ DCHECK(ShouldCheckBPC(m_type)); + + float color_value[kMaxComponents]; + for (uint32_t i = 0; i < m_nComponents; ++i) { +@@ -215,12 +218,10 @@ std::tuple CPDF_MeshStream::ReadColor() { + return std::tuple(r, g, b); + } + +- float result[kMaxComponents]; +- memset(result, 0, sizeof(result)); +- int nResults; ++ float result[kMaxComponents] = {}; + for (const auto& func : m_funcs) { + if (func && func->CountOutputs() <= kMaxComponents) +- func->Call(color_value, 1, result, &nResults); ++ func->Call(pdfium::make_span(color_value, 1), result); + } + + m_pCS->GetRGB(result, &r, &g, &b); +@@ -253,7 +254,7 @@ std::vector CPDF_MeshStream::ReadVertexRow( + if (m_BitStream->IsEOF() || !CanReadCoords()) + return std::vector(); + +- vertices.push_back(CPDF_MeshVertex()); ++ vertices.emplace_back(); + CPDF_MeshVertex& vertex = vertices.back(); + vertex.position = pObject2Bitmap.Transform(ReadCoords()); + if (!CanReadColor()) +diff --git a/core/fpdfapi/page/cpdf_meshstream.h b/core/fpdfapi/page/cpdf_meshstream.h +index 939b19238..b1dab6e96 100644 +--- a/core/fpdfapi/page/cpdf_meshstream.h ++++ b/core/fpdfapi/page/cpdf_meshstream.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,13 +7,13 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_MESHSTREAM_H_ + #define CORE_FPDFAPI_PAGE_CPDF_MESHSTREAM_H_ + ++#include ++ + #include + #include + #include + + #include "core/fpdfapi/page/cpdf_shadingpattern.h" +-#include "core/fxcrt/cfx_bitstream.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" + + class CPDF_StreamAcc; +@@ -25,11 +25,12 @@ class CPDF_MeshVertex { + ~CPDF_MeshVertex(); + + CFX_PointF position; +- float r; +- float g; +- float b; ++ float r = 0.0f; ++ float g = 0.0f; ++ float b = 0.0f; + }; + ++class CFX_BitStream; + class CFX_Matrix; + class CPDF_ColorSpace; + class CPDF_Function; +@@ -39,12 +40,15 @@ class CPDF_MeshStream { + public: + CPDF_MeshStream(ShadingType type, + const std::vector>& funcs, +- const CPDF_Stream* pShadingStream, +- const RetainPtr& pCS); ++ RetainPtr pShadingStream, ++ RetainPtr pCS); + ~CPDF_MeshStream(); + + bool Load(); ++ void SkipBits(uint32_t nbits); ++ void ByteAlign(); + ++ bool IsEOF() const; + bool CanReadFlag() const; + bool CanReadCoords() const; + bool CanReadColor() const; +@@ -59,31 +63,30 @@ class CPDF_MeshStream { + std::vector ReadVertexRow(const CFX_Matrix& pObject2Bitmap, + int count); + +- CFX_BitStream* BitStream() { return m_BitStream.get(); } + uint32_t ComponentBits() const { return m_nComponentBits; } + uint32_t Components() const { return m_nComponents; } + + private: +- static const uint32_t kMaxComponents = 8; ++ static constexpr uint32_t kMaxComponents = 8; + + const ShadingType m_type; + const std::vector>& m_funcs; + RetainPtr const m_pShadingStream; + RetainPtr const m_pCS; +- uint32_t m_nCoordBits; +- uint32_t m_nComponentBits; +- uint32_t m_nFlagBits; +- uint32_t m_nComponents; +- uint32_t m_CoordMax; +- uint32_t m_ComponentMax; +- float m_xmin; +- float m_xmax; +- float m_ymin; +- float m_ymax; ++ uint32_t m_nCoordBits = 0; ++ uint32_t m_nComponentBits = 0; ++ uint32_t m_nFlagBits = 0; ++ uint32_t m_nComponents = 0; ++ uint32_t m_CoordMax = 0; ++ uint32_t m_ComponentMax = 0; ++ float m_xmin = 0.0f; ++ float m_xmax = 0.0f; ++ float m_ymin = 0.0f; ++ float m_ymax = 0.0f; + RetainPtr m_pStream; + std::unique_ptr m_BitStream; +- float m_ColorMin[kMaxComponents]; +- float m_ColorMax[kMaxComponents]; ++ float m_ColorMin[kMaxComponents] = {}; ++ float m_ColorMax[kMaxComponents] = {}; + }; + + #endif // CORE_FPDFAPI_PAGE_CPDF_MESHSTREAM_H_ +diff --git a/core/fpdfapi/page/cpdf_occontext.cpp b/core/fpdfapi/page/cpdf_occontext.cpp +index 96f8a88e7..7d2b35fa7 100644 +--- a/core/fpdfapi/page/cpdf_occontext.cpp ++++ b/core/fpdfapi/page/cpdf_occontext.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,31 +10,21 @@ + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" ++#include "third_party/base/check.h" + + namespace { + +-int32_t FindGroup(const CPDF_Array* pArray, const CPDF_Dictionary* pGroupDict) { +- if (!pArray || !pGroupDict) +- return -1; +- +- for (size_t i = 0; i < pArray->size(); i++) { +- if (pArray->GetDictAt(i) == pGroupDict) +- return i; +- } +- return -1; +-} +- + bool HasIntent(const CPDF_Dictionary* pDict, + ByteStringView csElement, + ByteStringView csDef) { +- const CPDF_Object* pIntent = pDict->GetDirectObjectFor("Intent"); ++ RetainPtr pIntent = pDict->GetDirectObjectFor("Intent"); + if (!pIntent) + return csElement == csDef; + + ByteString bsIntent; + if (const CPDF_Array* pArray = pIntent->AsArray()) { + for (size_t i = 0; i < pArray->size(); i++) { +- bsIntent = pArray->GetStringAt(i); ++ bsIntent = pArray->GetByteStringAt(i); + if (bsIntent == "All" || bsIntent == csElement) + return true; + } +@@ -44,28 +34,30 @@ bool HasIntent(const CPDF_Dictionary* pDict, + return bsIntent == "All" || bsIntent == csElement; + } + +-CPDF_Dictionary* GetConfig(CPDF_Document* pDoc, +- const CPDF_Dictionary* pOCGDict) { +- ASSERT(pOCGDict); +- CPDF_Dictionary* pOCProperties = pDoc->GetRoot()->GetDictFor("OCProperties"); ++RetainPtr GetConfig(CPDF_Document* pDoc, ++ const CPDF_Dictionary* pOCGDict) { ++ DCHECK(pOCGDict); ++ RetainPtr pOCProperties = ++ pDoc->GetRoot()->GetDictFor("OCProperties"); + if (!pOCProperties) + return nullptr; + +- CPDF_Array* pOCGs = pOCProperties->GetArrayFor("OCGs"); ++ RetainPtr pOCGs = pOCProperties->GetArrayFor("OCGs"); + if (!pOCGs) + return nullptr; + +- if (FindGroup(pOCGs, pOCGDict) < 0) ++ if (!pOCGs->Contains(pOCGDict)) + return nullptr; + +- CPDF_Dictionary* pConfig = pOCProperties->GetDictFor("D"); +- CPDF_Array* pConfigs = pOCProperties->GetArrayFor("Configs"); +- if (!pConfigs) ++ RetainPtr pConfig = pOCProperties->GetDictFor("D"); ++ RetainPtr pConfigArray = ++ pOCProperties->GetArrayFor("Configs"); ++ if (!pConfigArray) + return pConfig; + +- for (size_t i = 0; i < pConfigs->size(); i++) { +- CPDF_Dictionary* pFind = pConfigs->GetDictAt(i); +- if (pFind && HasIntent(pFind, "View", "")) ++ for (size_t i = 0; i < pConfigArray->size(); i++) { ++ RetainPtr pFind = pConfigArray->GetDictAt(i); ++ if (pFind && HasIntent(pFind.Get(), "View", "")) + return pFind; + } + return pConfig; +@@ -74,13 +66,13 @@ CPDF_Dictionary* GetConfig(CPDF_Document* pDoc, + ByteString GetUsageTypeString(CPDF_OCContext::UsageType eType) { + ByteString csState; + switch (eType) { +- case CPDF_OCContext::Design: ++ case CPDF_OCContext::kDesign: + csState = "Design"; + break; +- case CPDF_OCContext::Print: ++ case CPDF_OCContext::kPrint: + csState = "Print"; + break; +- case CPDF_OCContext::Export: ++ case CPDF_OCContext::kExport: + csState = "Export"; + break; + default: +@@ -94,54 +86,52 @@ ByteString GetUsageTypeString(CPDF_OCContext::UsageType eType) { + + CPDF_OCContext::CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType) + : m_pDocument(pDoc), m_eUsageType(eUsageType) { +- ASSERT(pDoc); ++ DCHECK(pDoc); + } + +-CPDF_OCContext::~CPDF_OCContext() {} ++CPDF_OCContext::~CPDF_OCContext() = default; + + bool CPDF_OCContext::LoadOCGStateFromConfig( + const ByteString& csConfig, + const CPDF_Dictionary* pOCGDict) const { +- CPDF_Dictionary* pConfig = GetConfig(m_pDocument.Get(), pOCGDict); ++ RetainPtr pConfig = GetConfig(m_pDocument, pOCGDict); + if (!pConfig) + return true; + +- bool bState = pConfig->GetStringFor("BaseState", "ON") != "OFF"; +- CPDF_Array* pArray = pConfig->GetArrayFor("ON"); +- if (pArray) { +- if (FindGroup(pArray, pOCGDict) >= 0) +- bState = true; +- } ++ bool bState = pConfig->GetByteStringFor("BaseState", "ON") != "OFF"; ++ RetainPtr pArray = pConfig->GetArrayFor("ON"); ++ if (pArray && pArray->Contains(pOCGDict)) ++ bState = true; ++ + pArray = pConfig->GetArrayFor("OFF"); +- if (pArray) { +- if (FindGroup(pArray, pOCGDict) >= 0) +- bState = false; +- } ++ if (pArray && pArray->Contains(pOCGDict)) ++ bState = false; ++ + pArray = pConfig->GetArrayFor("AS"); + if (!pArray) + return bState; + + ByteString csFind = csConfig + "State"; + for (size_t i = 0; i < pArray->size(); i++) { +- CPDF_Dictionary* pUsage = pArray->GetDictAt(i); ++ RetainPtr pUsage = pArray->GetDictAt(i); + if (!pUsage) + continue; + +- if (pUsage->GetStringFor("Event", "View") != csConfig) ++ if (pUsage->GetByteStringFor("Event", "View") != csConfig) + continue; + +- CPDF_Array* pOCGs = pUsage->GetArrayFor("OCGs"); ++ RetainPtr pOCGs = pUsage->GetArrayFor("OCGs"); + if (!pOCGs) + continue; + +- if (FindGroup(pOCGs, pOCGDict) < 0) ++ if (!pOCGs->Contains(pOCGDict)) + continue; + +- CPDF_Dictionary* pState = pUsage->GetDictFor(csConfig); ++ RetainPtr pState = pUsage->GetDictFor(csConfig); + if (!pState) + continue; + +- bState = pState->GetStringFor(csFind) != "OFF"; ++ bState = pState->GetByteStringFor(csFind) != "OFF"; + } + return bState; + } +@@ -151,18 +141,18 @@ bool CPDF_OCContext::LoadOCGState(const CPDF_Dictionary* pOCGDict) const { + return true; + + ByteString csState = GetUsageTypeString(m_eUsageType); +- const CPDF_Dictionary* pUsage = pOCGDict->GetDictFor("Usage"); ++ RetainPtr pUsage = pOCGDict->GetDictFor("Usage"); + if (pUsage) { +- const CPDF_Dictionary* pState = pUsage->GetDictFor(csState); ++ RetainPtr pState = pUsage->GetDictFor(csState); + if (pState) { + ByteString csFind = csState + "State"; + if (pState->KeyExist(csFind)) +- return pState->GetStringFor(csFind) != "OFF"; ++ return pState->GetByteStringFor(csFind) != "OFF"; + } + if (csState != "View") { + pState = pUsage->GetDictFor("View"); + if (pState && pState->KeyExist("ViewState")) +- return pState->GetStringFor("ViewState") != "OFF"; ++ return pState->GetByteStringFor("ViewState") != "OFF"; + } + } + return LoadOCGStateFromConfig(csState, pOCGDict); +@@ -177,16 +167,17 @@ bool CPDF_OCContext::GetOCGVisible(const CPDF_Dictionary* pOCGDict) const { + return it->second; + + bool bState = LoadOCGState(pOCGDict); +- m_OGCStateCache[pOCGDict] = bState; ++ m_OGCStateCache[pdfium::WrapRetain(pOCGDict)] = bState; + return bState; + } + +-bool CPDF_OCContext::CheckObjectVisible(const CPDF_PageObject* pObj) const { +- for (size_t i = 0; i < pObj->m_ContentMarks.CountItems(); ++i) { +- const CPDF_ContentMarkItem* item = pObj->m_ContentMarks.GetItem(i); ++bool CPDF_OCContext::CheckPageObjectVisible(const CPDF_PageObject* pObj) const { ++ const CPDF_ContentMarks* pMarks = pObj->GetContentMarks(); ++ for (size_t i = 0; i < pMarks->CountItems(); ++i) { ++ const CPDF_ContentMarkItem* item = pMarks->GetItem(i); + if (item->GetName() == "OC" && + item->GetParamType() == CPDF_ContentMarkItem::kPropertiesDict && +- !CheckOCGVisible(item->GetParam())) { ++ !CheckOCGDictVisible(item->GetParam().Get())) { + return false; + } + } +@@ -197,9 +188,9 @@ bool CPDF_OCContext::GetOCGVE(const CPDF_Array* pExpression, int nLevel) const { + if (nLevel > 32 || !pExpression) + return false; + +- ByteString csOperator = pExpression->GetStringAt(0); ++ ByteString csOperator = pExpression->GetByteStringAt(0); + if (csOperator == "Not") { +- const CPDF_Object* pOCGObj = pExpression->GetDirectObjectAt(1); ++ RetainPtr pOCGObj = pExpression->GetDirectObjectAt(1); + if (!pOCGObj) + return false; + if (const CPDF_Dictionary* pDict = pOCGObj->AsDictionary()) +@@ -214,7 +205,7 @@ bool CPDF_OCContext::GetOCGVE(const CPDF_Array* pExpression, int nLevel) const { + + bool bValue = false; + for (size_t i = 1; i < pExpression->size(); i++) { +- const CPDF_Object* pOCGObj = pExpression->GetDirectObjectAt(1); ++ RetainPtr pOCGObj = pExpression->GetDirectObjectAt(i); + if (!pOCGObj) + continue; + +@@ -238,12 +229,13 @@ bool CPDF_OCContext::GetOCGVE(const CPDF_Array* pExpression, int nLevel) const { + } + + bool CPDF_OCContext::LoadOCMDState(const CPDF_Dictionary* pOCMDDict) const { +- const CPDF_Array* pVE = pOCMDDict->GetArrayFor("VE"); +- if (pVE) +- return GetOCGVE(pVE, 0); ++ RetainPtr pVE = pOCMDDict->GetArrayFor("VE"); ++ if (pVE) { ++ return GetOCGVE(pVE.Get(), 0); ++ } + +- ByteString csP = pOCMDDict->GetStringFor("P", "AnyOn"); +- const CPDF_Object* pOCGObj = pOCMDDict->GetDirectObjectFor("OCGs"); ++ ByteString csP = pOCMDDict->GetByteStringFor("P", "AnyOn"); ++ RetainPtr pOCGObj = pOCMDDict->GetDirectObjectFor("OCGs"); + if (!pOCGObj) + return true; + +@@ -260,12 +252,12 @@ bool CPDF_OCContext::LoadOCMDState(const CPDF_Dictionary* pOCMDDict) const { + bool bValidEntrySeen = false; + for (size_t i = 0; i < pArray->size(); i++) { + bool bItem = true; +- const CPDF_Dictionary* pItemDict = pArray->GetDictAt(i); ++ RetainPtr pItemDict = pArray->GetDictAt(i); + if (!pItemDict) + continue; + + bValidEntrySeen = true; +- bItem = GetOCGVisible(pItemDict); ++ bItem = GetOCGVisible(pItemDict.Get()); + + if ((csP == "AnyOn" && bItem) || (csP == "AnyOff" && !bItem)) + return true; +@@ -276,11 +268,12 @@ bool CPDF_OCContext::LoadOCMDState(const CPDF_Dictionary* pOCMDDict) const { + return !bValidEntrySeen || bState; + } + +-bool CPDF_OCContext::CheckOCGVisible(const CPDF_Dictionary* pOCGDict) const { ++bool CPDF_OCContext::CheckOCGDictVisible( ++ const CPDF_Dictionary* pOCGDict) const { + if (!pOCGDict) + return true; + +- ByteString csType = pOCGDict->GetStringFor("Type", "OCG"); ++ ByteString csType = pOCGDict->GetByteStringFor("Type", "OCG"); + if (csType == "OCG") + return GetOCGVisible(pOCGDict); + return LoadOCMDState(pOCGDict); +diff --git a/core/fpdfapi/page/cpdf_occontext.h b/core/fpdfapi/page/cpdf_occontext.h +index 0a68639ab..c0012d1ec 100644 +--- a/core/fpdfapi/page/cpdf_occontext.h ++++ b/core/fpdfapi/page/cpdf_occontext.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,12 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_OCCONTEXT_H_ + #define CORE_FPDFAPI_PAGE_CPDF_OCCONTEXT_H_ + ++#include + #include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/retain_ptr.h" ++#include "core/fxcrt/unowned_ptr.h" + + class CPDF_Array; + class CPDF_Dictionary; +@@ -19,13 +21,12 @@ class CPDF_PageObject; + + class CPDF_OCContext final : public Retainable { + public: +- enum UsageType { View = 0, Design, Print, Export }; ++ enum UsageType { kView = 0, kDesign, kPrint, kExport }; + +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + +- bool CheckOCGVisible(const CPDF_Dictionary* pOCGDict) const; +- bool CheckObjectVisible(const CPDF_PageObject* pObj) const; ++ bool CheckOCGDictVisible(const CPDF_Dictionary* pOCGDict) const; ++ bool CheckPageObjectVisible(const CPDF_PageObject* pObj) const; + + private: + CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType); +@@ -40,7 +41,8 @@ class CPDF_OCContext final : public Retainable { + + UnownedPtr const m_pDocument; + const UsageType m_eUsageType; +- mutable std::map m_OGCStateCache; ++ mutable std::map, bool, std::less<>> ++ m_OGCStateCache; + }; + + #endif // CORE_FPDFAPI_PAGE_CPDF_OCCONTEXT_H_ +diff --git a/core/fpdfapi/page/cpdf_page.cpp b/core/fpdfapi/page/cpdf_page.cpp +index 210f266c3..d3493de8a 100644 +--- a/core/fpdfapi/page/cpdf_page.cpp ++++ b/core/fpdfapi/page/cpdf_page.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,24 +11,26 @@ + + #include "constants/page_object.h" + #include "core/fpdfapi/page/cpdf_contentparser.h" ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" + #include "core/fpdfapi/page/cpdf_pageobject.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_object.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/containers/contains.h" + +-CPDF_Page::CPDF_Page(CPDF_Document* pDocument, CPDF_Dictionary* pPageDict) +- : CPDF_PageObjectHolder(pDocument, pPageDict, nullptr, nullptr), ++CPDF_Page::CPDF_Page(CPDF_Document* pDocument, ++ RetainPtr pPageDict) ++ : CPDF_PageObjectHolder(pDocument, std::move(pPageDict), nullptr, nullptr), + m_PageSize(100, 100), + m_pPDFDocument(pDocument) { +- ASSERT(pPageDict); +- + // Cannot initialize |m_pResources| and |m_pPageResources| via the + // CPDF_PageObjectHolder ctor because GetPageAttr() requires + // CPDF_PageObjectHolder to finish initializing first. +- CPDF_Object* pPageAttr = GetPageAttr(pdfium::page_object::kResources); +- m_pResources.Reset(pPageAttr ? pPageAttr->GetDict() : nullptr); ++ RetainPtr pPageAttr = ++ GetMutablePageAttr(pdfium::page_object::kResources); ++ m_pResources = pPageAttr ? pPageAttr->GetMutableDict() : nullptr; + m_pPageResources = m_pResources; + + UpdateDimensions(); +@@ -36,7 +38,7 @@ CPDF_Page::CPDF_Page(CPDF_Document* pDocument, CPDF_Dictionary* pPageDict) + LoadTransparencyInfo(); + } + +-CPDF_Page::~CPDF_Page() {} ++CPDF_Page::~CPDF_Page() = default; + + CPDF_Page* CPDF_Page::AsPDFPage() { + return this; +@@ -47,7 +49,7 @@ CPDFXFA_Page* CPDF_Page::AsXFAPage() { + } + + CPDF_Document* CPDF_Page::GetDocument() const { +- return GetPDFDocument(); ++ return m_pPDFDocument; + } + + float CPDF_Page::GetPageWidth() const { +@@ -67,30 +69,34 @@ void CPDF_Page::ParseContent() { + return; + + if (GetParseState() == ParseState::kNotParsed) +- StartParse(pdfium::MakeUnique(this)); ++ StartParse(std::make_unique(this)); + +- ASSERT(GetParseState() == ParseState::kParsing); ++ DCHECK_EQ(GetParseState(), ParseState::kParsing); + ContinueParse(nullptr); + } + +-CPDF_Object* CPDF_Page::GetPageAttr(const ByteString& name) const { +- CPDF_Dictionary* pPageDict = GetDict(); +- std::set visited; +- while (1) { +- visited.insert(pPageDict); +- if (CPDF_Object* pObj = pPageDict->GetDirectObjectFor(name)) ++RetainPtr CPDF_Page::GetMutablePageAttr(const ByteString& name) { ++ return pdfium::WrapRetain(const_cast(GetPageAttr(name).Get())); ++} ++ ++RetainPtr CPDF_Page::GetPageAttr( ++ const ByteString& name) const { ++ std::set> visited; ++ RetainPtr pPageDict = GetDict(); ++ while (pPageDict && !pdfium::Contains(visited, pPageDict)) { ++ RetainPtr pObj = pPageDict->GetDirectObjectFor(name); ++ if (pObj) + return pObj; + ++ visited.insert(pPageDict); + pPageDict = pPageDict->GetDictFor(pdfium::page_object::kParent); +- if (!pPageDict || pdfium::ContainsKey(visited, pPageDict)) +- break; + } + return nullptr; + } + + CFX_FloatRect CPDF_Page::GetBox(const ByteString& name) const { + CFX_FloatRect box; +- CPDF_Array* pBox = ToArray(GetPageAttr(name)); ++ RetainPtr pBox = ToArray(GetPageAttr(name)); + if (pBox) { + box = pBox->GetRect(); + box.Normalize(); +@@ -98,7 +104,7 @@ CFX_FloatRect CPDF_Page::GetBox(const ByteString& name) const { + return box; + } + +-Optional CPDF_Page::DeviceToPage( ++absl::optional CPDF_Page::DeviceToPage( + const FX_RECT& rect, + int rotate, + const CFX_PointF& device_point) const { +@@ -106,7 +112,7 @@ Optional CPDF_Page::DeviceToPage( + return page2device.GetInverse().Transform(device_point); + } + +-Optional CPDF_Page::PageToDevice( ++absl::optional CPDF_Page::PageToDevice( + const FX_RECT& rect, + int rotate, + const CFX_PointF& page_point) const { +@@ -172,11 +178,43 @@ CFX_Matrix CPDF_Page::GetDisplayMatrix(const FX_RECT& rect, int iRotate) const { + } + + int CPDF_Page::GetPageRotation() const { +- CPDF_Object* pRotate = GetPageAttr(pdfium::page_object::kRotate); ++ RetainPtr pRotate = ++ GetPageAttr(pdfium::page_object::kRotate); + int rotate = pRotate ? (pRotate->GetInteger() / 90) % 4 : 0; + return (rotate < 0) ? (rotate + 4) : rotate; + } + ++RetainPtr CPDF_Page::GetOrCreateAnnotsArray() { ++ return GetMutableDict()->GetOrCreateArrayFor("Annots"); ++} ++ ++RetainPtr CPDF_Page::GetMutableAnnotsArray() { ++ return GetMutableDict()->GetMutableArrayFor("Annots"); ++} ++ ++RetainPtr CPDF_Page::GetAnnotsArray() const { ++ return GetDict()->GetArrayFor("Annots"); ++} ++ ++void CPDF_Page::AddPageImageCache() { ++ m_pPageImageCache = std::make_unique(this); ++} ++ ++void CPDF_Page::SetRenderContext(std::unique_ptr pContext) { ++ DCHECK(!m_pRenderContext); ++ DCHECK(pContext); ++ m_pRenderContext = std::move(pContext); ++} ++ ++void CPDF_Page::ClearRenderContext() { ++ m_pRenderContext.reset(); ++} ++ ++void CPDF_Page::ClearView() { ++ if (m_pView) ++ m_pView->ClearPage(this); ++} ++ + void CPDF_Page::UpdateDimensions() { + CFX_FloatRect mediabox = GetBox(pdfium::page_object::kMediaBox); + if (mediabox.IsEmpty()) +@@ -214,5 +252,6 @@ CPDF_Page::RenderContextClearer::RenderContextClearer(CPDF_Page* pPage) + : m_pPage(pPage) {} + + CPDF_Page::RenderContextClearer::~RenderContextClearer() { +- m_pPage->SetRenderContext(nullptr); ++ if (m_pPage) ++ m_pPage->ClearRenderContext(); + } +diff --git a/core/fpdfapi/page/cpdf_page.h b/core/fpdfapi/page/cpdf_page.h +index 9bdb75d0f..2b659e69f 100644 +--- a/core/fpdfapi/page/cpdf_page.h ++++ b/core/fpdfapi/page/cpdf_page.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,37 +13,35 @@ + #include "core/fpdfapi/page/cpdf_pageobjectholder.h" + #include "core/fpdfapi/page/ipdf_page.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/fx_memory.h" + #include "core/fxcrt/observed_ptr.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + ++class CPDF_Array; + class CPDF_Dictionary; + class CPDF_Document; +-class CPDF_Image; + class CPDF_Object; ++class CPDF_PageImageCache; + + class CPDF_Page final : public IPDF_Page, public CPDF_PageObjectHolder { + public: +- // Caller implements as desired, empty here due to layering. +- class View : public Observable {}; +- +- // Data for the render layer to attach to this page. +- class RenderContextIface { ++ // Caller implements as desired, exists here due to layering. ++ class View : public Observable { + public: +- virtual ~RenderContextIface() {} ++ virtual void ClearPage(CPDF_Page* pPage) = 0; + }; + +- // Cache for the render layer to attach to this page. +- class RenderCacheIface { ++ // Data for the render layer to attach to this page. ++ class RenderContextIface { + public: +- virtual ~RenderCacheIface() {} +- virtual void ResetBitmapForImage(const RetainPtr& pImage) = 0; ++ virtual ~RenderContextIface() = default; + }; + + class RenderContextClearer { + public: ++ FX_STACK_ALLOCATED(); + explicit RenderContextClearer(CPDF_Page* pPage); + ~RenderContextClearer(); + +@@ -51,8 +49,7 @@ class CPDF_Page final : public IPDF_Page, public CPDF_PageObjectHolder { + UnownedPtr const m_pPage; + }; + +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // IPDF_Page: + CPDF_Page* AsPDFPage() override; +@@ -61,11 +58,11 @@ class CPDF_Page final : public IPDF_Page, public CPDF_PageObjectHolder { + float GetPageWidth() const override; + float GetPageHeight() const override; + CFX_Matrix GetDisplayMatrix(const FX_RECT& rect, int iRotate) const override; +- Optional DeviceToPage( ++ absl::optional DeviceToPage( + const FX_RECT& rect, + int rotate, + const CFX_PointF& device_point) const override; +- Optional PageToDevice( ++ absl::optional PageToDevice( + const FX_RECT& rect, + int rotate, + const CFX_PointF& page_point) const override; +@@ -75,35 +72,39 @@ class CPDF_Page final : public IPDF_Page, public CPDF_PageObjectHolder { + + void ParseContent(); + const CFX_SizeF& GetPageSize() const { return m_PageSize; } ++ const CFX_Matrix& GetPageMatrix() const { return m_PageMatrix; } + int GetPageRotation() const; +- RenderCacheIface* GetRenderCache() const { return m_pRenderCache.get(); } +- void SetRenderCache(std::unique_ptr pCache) { +- m_pRenderCache = std::move(pCache); +- } +- +- RenderContextIface* GetRenderContext() const { +- return m_pRenderContext.get(); +- } +- void SetRenderContext(std::unique_ptr pContext) { +- m_pRenderContext = std::move(pContext); +- } +- +- CPDF_Document* GetPDFDocument() const { return m_pPDFDocument.Get(); } +- View* GetView() const { return m_pView.Get(); } ++ ++ RetainPtr GetOrCreateAnnotsArray(); ++ RetainPtr GetMutableAnnotsArray(); ++ RetainPtr GetAnnotsArray() const; ++ ++ void AddPageImageCache(); ++ CPDF_PageImageCache* GetPageImageCache() { return m_pPageImageCache.get(); } ++ RenderContextIface* GetRenderContext() { return m_pRenderContext.get(); } ++ ++ // `pContext` cannot be null. `SetRenderContext()` cannot be called if the ++ // page already has a render context. Use `ClearRenderContext()` to reset the ++ // render context. ++ void SetRenderContext(std::unique_ptr pContext); ++ void ClearRenderContext(); ++ + void SetView(View* pView) { m_pView.Reset(pView); } ++ void ClearView(); + void UpdateDimensions(); + + private: +- CPDF_Page(CPDF_Document* pDocument, CPDF_Dictionary* pPageDict); ++ CPDF_Page(CPDF_Document* pDocument, RetainPtr pPageDict); + ~CPDF_Page() override; + +- CPDF_Object* GetPageAttr(const ByteString& name) const; ++ RetainPtr GetMutablePageAttr(const ByteString& name); ++ RetainPtr GetPageAttr(const ByteString& name) const; + CFX_FloatRect GetBox(const ByteString& name) const; + + CFX_SizeF m_PageSize; + CFX_Matrix m_PageMatrix; +- UnownedPtr m_pPDFDocument; +- std::unique_ptr m_pRenderCache; ++ UnownedPtr const m_pPDFDocument; ++ std::unique_ptr m_pPageImageCache; + std::unique_ptr m_pRenderContext; + ObservedPtr m_pView; + }; +diff --git a/core/fpdfapi/page/cpdf_pageimagecache.cpp b/core/fpdfapi/page/cpdf_pageimagecache.cpp +new file mode 100644 +index 000000000..497cd8be0 +--- /dev/null ++++ b/core/fpdfapi/page/cpdf_pageimagecache.cpp +@@ -0,0 +1,262 @@ ++// Copyright 2016 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" ++ ++#include ++#include ++#include ++ ++#include "core/fpdfapi/page/cpdf_dib.h" ++#include "core/fpdfapi/page/cpdf_image.h" ++#include "core/fpdfapi/page/cpdf_page.h" ++#include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" ++#include "core/fxcrt/stl_util.h" ++#include "core/fxge/dib/cfx_dibitmap.h" ++ ++namespace { ++ ++struct CacheInfo { ++ CacheInfo(uint32_t t, RetainPtr stream) ++ : time(t), pStream(std::move(stream)) {} ++ ++ uint32_t time; ++ RetainPtr pStream; ++ ++ bool operator<(const CacheInfo& other) const { return time < other.time; } ++}; ++ ++} // namespace ++ ++CPDF_PageImageCache::CPDF_PageImageCache(CPDF_Page* pPage) : m_pPage(pPage) {} ++ ++CPDF_PageImageCache::~CPDF_PageImageCache() = default; ++ ++void CPDF_PageImageCache::CacheOptimization(int32_t dwLimitCacheSize) { ++ if (m_nCacheSize <= (uint32_t)dwLimitCacheSize) ++ return; ++ ++ uint32_t nCount = fxcrt::CollectionSize(m_ImageCache); ++ std::vector cache_info; ++ cache_info.reserve(nCount); ++ for (const auto& it : m_ImageCache) { ++ cache_info.emplace_back(it.second->GetTimeCount(), ++ it.second->GetImage()->GetStream()); ++ } ++ std::sort(cache_info.begin(), cache_info.end()); ++ ++ // Check if time value is about to roll over and reset all entries. ++ // The comparison is legal because uint32_t is an unsigned type. ++ uint32_t nTimeCount = m_nTimeCount; ++ if (nTimeCount + 1 < nTimeCount) { ++ for (uint32_t i = 0; i < nCount; i++) ++ m_ImageCache[cache_info[i].pStream]->SetTimeCount(i); ++ m_nTimeCount = nCount; ++ } ++ ++ size_t i = 0; ++ while (i + 15 < nCount) ++ ClearImageCacheEntry(cache_info[i++].pStream); ++ ++ while (i < nCount && m_nCacheSize > (uint32_t)dwLimitCacheSize) ++ ClearImageCacheEntry(cache_info[i++].pStream); ++} ++ ++void CPDF_PageImageCache::ClearImageCacheEntry(const CPDF_Stream* pStream) { ++ auto it = m_ImageCache.find(pStream); ++ if (it == m_ImageCache.end()) ++ return; ++ ++ m_nCacheSize -= it->second->EstimateSize(); ++ ++ // Avoid leaving `m_pCurImageCacheEntry` as a dangling pointer when `it` is ++ // about to be deleted. ++ if (m_pCurImageCacheEntry.Get() == it->second.get()) { ++ DCHECK(!m_pCurImageCacheEntry.IsOwned()); ++ m_pCurImageCacheEntry.Reset(); ++ } ++ m_ImageCache.erase(it); ++} ++ ++bool CPDF_PageImageCache::StartGetCachedBitmap( ++ RetainPtr pImage, ++ const CPDF_Dictionary* pFormResources, ++ const CPDF_Dictionary* pPageResources, ++ bool bStdCS, ++ CPDF_ColorSpace::Family eFamily, ++ bool bLoadMask, ++ const CFX_Size& max_size_required) { ++ // A cross-document image may have come from the embedder. ++ if (m_pPage->GetDocument() != pImage->GetDocument()) ++ return false; ++ ++ RetainPtr pStream = pImage->GetStream(); ++ const auto it = m_ImageCache.find(pStream); ++ m_bCurFindCache = it != m_ImageCache.end(); ++ if (m_bCurFindCache) { ++ m_pCurImageCacheEntry = it->second.get(); ++ } else { ++ m_pCurImageCacheEntry = std::make_unique(std::move(pImage)); ++ } ++ CPDF_DIB::LoadState ret = m_pCurImageCacheEntry->StartGetCachedBitmap( ++ this, pFormResources, pPageResources, bStdCS, eFamily, bLoadMask, ++ max_size_required); ++ if (ret == CPDF_DIB::LoadState::kContinue) ++ return true; ++ ++ m_nTimeCount++; ++ if (!m_bCurFindCache) ++ m_ImageCache[pStream] = m_pCurImageCacheEntry.Release(); ++ ++ if (ret == CPDF_DIB::LoadState::kFail) ++ m_nCacheSize += m_pCurImageCacheEntry->EstimateSize(); ++ ++ return false; ++} ++ ++bool CPDF_PageImageCache::Continue(PauseIndicatorIface* pPause) { ++ bool ret = m_pCurImageCacheEntry->Continue(pPause, this); ++ if (ret) ++ return true; ++ ++ m_nTimeCount++; ++ if (!m_bCurFindCache) { ++ m_ImageCache[m_pCurImageCacheEntry->GetImage()->GetStream()] = ++ m_pCurImageCacheEntry.Release(); ++ } ++ m_nCacheSize += m_pCurImageCacheEntry->EstimateSize(); ++ return false; ++} ++ ++void CPDF_PageImageCache::ResetBitmapForImage(RetainPtr pImage) { ++ RetainPtr pStream = pImage->GetStream(); ++ const auto it = m_ImageCache.find(pStream); ++ if (it == m_ImageCache.end()) ++ return; ++ ++ Entry* pEntry = it->second.get(); ++ m_nCacheSize -= pEntry->EstimateSize(); ++ pEntry->Reset(); ++ m_nCacheSize += pEntry->EstimateSize(); ++} ++ ++uint32_t CPDF_PageImageCache::GetCurMatteColor() const { ++ return m_pCurImageCacheEntry->GetMatteColor(); ++} ++ ++RetainPtr CPDF_PageImageCache::DetachCurBitmap() { ++ return m_pCurImageCacheEntry->DetachBitmap(); ++} ++ ++RetainPtr CPDF_PageImageCache::DetachCurMask() { ++ return m_pCurImageCacheEntry->DetachMask(); ++} ++ ++CPDF_PageImageCache::Entry::Entry(RetainPtr pImage) ++ : m_pImage(std::move(pImage)) {} ++ ++CPDF_PageImageCache::Entry::~Entry() = default; ++ ++void CPDF_PageImageCache::Entry::Reset() { ++ m_pCachedBitmap.Reset(); ++ CalcSize(); ++} ++ ++RetainPtr CPDF_PageImageCache::Entry::DetachBitmap() { ++ return std::move(m_pCurBitmap); ++} ++ ++RetainPtr CPDF_PageImageCache::Entry::DetachMask() { ++ return std::move(m_pCurMask); ++} ++ ++CPDF_DIB::LoadState CPDF_PageImageCache::Entry::StartGetCachedBitmap( ++ CPDF_PageImageCache* pPageImageCache, ++ const CPDF_Dictionary* pFormResources, ++ const CPDF_Dictionary* pPageResources, ++ bool bStdCS, ++ CPDF_ColorSpace::Family eFamily, ++ bool bLoadMask, ++ const CFX_Size& max_size_required) { ++ if (m_pCachedBitmap && IsCacheValid(max_size_required)) { ++ m_pCurBitmap = m_pCachedBitmap; ++ m_pCurMask = m_pCachedMask; ++ return CPDF_DIB::LoadState::kSuccess; ++ } ++ ++ m_pCurBitmap = m_pImage->CreateNewDIB(); ++ CPDF_DIB::LoadState ret = m_pCurBitmap.AsRaw()->StartLoadDIBBase( ++ true, pFormResources, pPageResources, bStdCS, eFamily, bLoadMask, ++ max_size_required); ++ m_bCachedSetMaxSizeRequired = ++ (max_size_required.width != 0 && max_size_required.height != 0); ++ if (ret == CPDF_DIB::LoadState::kContinue) ++ return CPDF_DIB::LoadState::kContinue; ++ ++ if (ret == CPDF_DIB::LoadState::kSuccess) ++ ContinueGetCachedBitmap(pPageImageCache); ++ else ++ m_pCurBitmap.Reset(); ++ return CPDF_DIB::LoadState::kFail; ++} ++ ++bool CPDF_PageImageCache::Entry::Continue( ++ PauseIndicatorIface* pPause, ++ CPDF_PageImageCache* pPageImageCache) { ++ CPDF_DIB::LoadState ret = ++ m_pCurBitmap.AsRaw()->ContinueLoadDIBBase(pPause); ++ if (ret == CPDF_DIB::LoadState::kContinue) ++ return true; ++ ++ if (ret == CPDF_DIB::LoadState::kSuccess) ++ ContinueGetCachedBitmap(pPageImageCache); ++ else ++ m_pCurBitmap.Reset(); ++ return false; ++} ++ ++void CPDF_PageImageCache::Entry::ContinueGetCachedBitmap( ++ CPDF_PageImageCache* pPageImageCache) { ++ m_MatteColor = m_pCurBitmap.AsRaw()->GetMatteColor(); ++ m_pCurMask = m_pCurBitmap.AsRaw()->DetachMask(); ++ m_dwTimeCount = pPageImageCache->GetTimeCount(); ++ if (m_pCurBitmap->GetPitch() * m_pCurBitmap->GetHeight() < kHugeImageSize) { ++ m_pCachedBitmap = m_pCurBitmap->Realize(); ++ m_pCurBitmap.Reset(); ++ } else { ++ m_pCachedBitmap = m_pCurBitmap; ++ } ++ if (m_pCurMask) { ++ m_pCachedMask = m_pCurMask->Realize(); ++ m_pCurMask.Reset(); ++ } ++ m_pCurBitmap = m_pCachedBitmap; ++ m_pCurMask = m_pCachedMask; ++ CalcSize(); ++} ++ ++void CPDF_PageImageCache::Entry::CalcSize() { ++ m_dwCacheSize = 0; ++ if (m_pCachedBitmap) ++ m_dwCacheSize += m_pCachedBitmap->GetEstimatedImageMemoryBurden(); ++ if (m_pCachedMask) ++ m_dwCacheSize += m_pCachedMask->GetEstimatedImageMemoryBurden(); ++} ++ ++bool CPDF_PageImageCache::Entry::IsCacheValid( ++ const CFX_Size& max_size_required) const { ++ if (!m_bCachedSetMaxSizeRequired) { ++ return true; ++ } ++ if (max_size_required.width == 0 && max_size_required.height == 0) { ++ return false; ++ } ++ ++ return (m_pCachedBitmap->GetWidth() >= max_size_required.width) && ++ (m_pCachedBitmap->GetHeight() >= max_size_required.height); ++} +diff --git a/core/fpdfapi/page/cpdf_pageimagecache.h b/core/fpdfapi/page/cpdf_pageimagecache.h +new file mode 100644 +index 000000000..5184ff61a +--- /dev/null ++++ b/core/fpdfapi/page/cpdf_pageimagecache.h +@@ -0,0 +1,107 @@ ++// Copyright 2016 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FPDFAPI_PAGE_CPDF_PAGEIMAGECACHE_H_ ++#define CORE_FPDFAPI_PAGE_CPDF_PAGEIMAGECACHE_H_ ++ ++#include ++ ++#include ++#include ++#include ++ ++#include "core/fpdfapi/page/cpdf_dib.h" ++#include "core/fxcrt/maybe_owned.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxcrt/unowned_ptr.h" ++ ++class CPDF_Dictionary; ++class CPDF_Image; ++class CPDF_Page; ++class CPDF_Stream; ++class PauseIndicatorIface; ++ ++class CPDF_PageImageCache { ++ public: ++ explicit CPDF_PageImageCache(CPDF_Page* pPage); ++ ~CPDF_PageImageCache(); ++ ++ void ResetBitmapForImage(RetainPtr pImage); ++ void CacheOptimization(int32_t dwLimitCacheSize); ++ uint32_t GetTimeCount() const { return m_nTimeCount; } ++ CPDF_Page* GetPage() const { return m_pPage; } ++ ++ bool StartGetCachedBitmap(RetainPtr pImage, ++ const CPDF_Dictionary* pFormResources, ++ const CPDF_Dictionary* pPageResources, ++ bool bStdCS, ++ CPDF_ColorSpace::Family eFamily, ++ bool bLoadMask, ++ const CFX_Size& max_size_required); ++ ++ bool Continue(PauseIndicatorIface* pPause); ++ ++ uint32_t GetCurMatteColor() const; ++ RetainPtr DetachCurBitmap(); ++ RetainPtr DetachCurMask(); ++ ++ private: ++ class Entry { ++ public: ++ explicit Entry(RetainPtr pImage); ++ ~Entry(); ++ ++ void Reset(); ++ uint32_t EstimateSize() const { return m_dwCacheSize; } ++ uint32_t GetMatteColor() const { return m_MatteColor; } ++ uint32_t GetTimeCount() const { return m_dwTimeCount; } ++ void SetTimeCount(uint32_t count) { m_dwTimeCount = count; } ++ CPDF_Image* GetImage() const { return m_pImage.Get(); } ++ ++ CPDF_DIB::LoadState StartGetCachedBitmap( ++ CPDF_PageImageCache* pPageImageCache, ++ const CPDF_Dictionary* pFormResources, ++ const CPDF_Dictionary* pPageResources, ++ bool bStdCS, ++ CPDF_ColorSpace::Family eFamily, ++ bool bLoadMask, ++ const CFX_Size& max_size_required); ++ ++ // Returns whether to Continue() or not. ++ bool Continue(PauseIndicatorIface* pPause, ++ CPDF_PageImageCache* pPageImageCache); ++ ++ RetainPtr DetachBitmap(); ++ RetainPtr DetachMask(); ++ ++ private: ++ void ContinueGetCachedBitmap(CPDF_PageImageCache* pPageImageCache); ++ void CalcSize(); ++ bool IsCacheValid(const CFX_Size& max_size_required) const; ++ ++ uint32_t m_dwTimeCount = 0; ++ uint32_t m_MatteColor = 0; ++ uint32_t m_dwCacheSize = 0; ++ RetainPtr const m_pImage; ++ RetainPtr m_pCurBitmap; ++ RetainPtr m_pCurMask; ++ RetainPtr m_pCachedBitmap; ++ RetainPtr m_pCachedMask; ++ bool m_bCachedSetMaxSizeRequired = false; ++ }; ++ ++ void ClearImageCacheEntry(const CPDF_Stream* pStream); ++ ++ UnownedPtr const m_pPage; ++ std::map, std::unique_ptr, std::less<>> ++ m_ImageCache; ++ MaybeOwned m_pCurImageCacheEntry; ++ uint32_t m_nTimeCount = 0; ++ uint32_t m_nCacheSize = 0; ++ bool m_bCurFindCache = false; ++}; ++ ++#endif // CORE_FPDFAPI_PAGE_CPDF_PAGEIMAGECACHE_H_ +diff --git a/core/fpdfapi/page/cpdf_pageimagecache_unittest.cpp b/core/fpdfapi/page/cpdf_pageimagecache_unittest.cpp +new file mode 100644 +index 000000000..34e436fe2 +--- /dev/null ++++ b/core/fpdfapi/page/cpdf_pageimagecache_unittest.cpp +@@ -0,0 +1,81 @@ ++// Copyright 2023 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" ++ ++#include ++#include ++#include ++ ++#include "core/fpdfapi/page/cpdf_docpagedata.h" ++#include "core/fpdfapi/page/cpdf_image.h" ++#include "core/fpdfapi/page/cpdf_imageobject.h" ++#include "core/fpdfapi/page/cpdf_page.h" ++#include "core/fpdfapi/page/cpdf_pagemodule.h" ++#include "core/fpdfapi/parser/cpdf_parser.h" ++#include "core/fpdfapi/render/cpdf_docrenderdata.h" ++#include "core/fxcrt/fx_stream.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "testing/utils/path_service.h" ++ ++TEST(CPDFPageImageCache, RenderBug1924) { ++ // If you render a page with a JPEG2000 image as a thumbnail (small picture) ++ // first, the image that gets cached has a low resolution. If you afterwards ++ // render it full-size, you should get a larger image - the image cache will ++ // be regenerate. ++ ++ CPDF_PageModule::Create(); ++ { ++ std::string file_path; ++ ASSERT_TRUE(PathService::GetTestFilePath("jpx_lzw.pdf", &file_path)); ++ auto document = ++ std::make_unique(std::make_unique(), ++ std::make_unique()); ++ ASSERT_EQ(document->LoadDoc( ++ IFX_SeekableReadStream::CreateFromFilename(file_path.c_str()), ++ nullptr), ++ CPDF_Parser::SUCCESS); ++ ++ RetainPtr page_dict = ++ document->GetMutablePageDictionary(0); ++ ASSERT_TRUE(page_dict); ++ auto page = ++ pdfium::MakeRetain(document.get(), std::move(page_dict)); ++ page->AddPageImageCache(); ++ page->ParseContent(); ++ ++ CPDF_PageImageCache* page_image_cache = page->GetPageImageCache(); ++ ASSERT_TRUE(page_image_cache); ++ ++ CPDF_PageObject* page_obj = page->GetPageObjectByIndex(0); ++ ASSERT_TRUE(page_obj); ++ CPDF_ImageObject* image = page_obj->AsImage(); ++ ASSERT_TRUE(image); ++ ++ // Render with small scale. ++ bool should_continue = page_image_cache->StartGetCachedBitmap( ++ image->GetImage(), nullptr, page->GetMutablePageResources(), true, ++ CPDF_ColorSpace::Family::kICCBased, false, {50, 50}); ++ while (should_continue) ++ should_continue = page_image_cache->Continue(nullptr); ++ ++ RetainPtr bitmap_small = page_image_cache->DetachCurBitmap(); ++ ++ // And render with large scale. ++ should_continue = page_image_cache->StartGetCachedBitmap( ++ image->GetImage(), nullptr, page->GetMutablePageResources(), true, ++ CPDF_ColorSpace::Family::kICCBased, false, {100, 100}); ++ while (should_continue) ++ should_continue = page_image_cache->Continue(nullptr); ++ ++ RetainPtr bitmap_large = page_image_cache->DetachCurBitmap(); ++ ++ ASSERT_GT(bitmap_large->GetWidth(), bitmap_small->GetWidth()); ++ ASSERT_GT(bitmap_large->GetHeight(), bitmap_small->GetHeight()); ++ ++ ASSERT_TRUE(page->AsPDFPage()); ++ page->AsPDFPage()->ClearView(); ++ } ++ CPDF_PageModule::Destroy(); ++} +diff --git a/core/fpdfapi/page/cpdf_pagemodule.cpp b/core/fpdfapi/page/cpdf_pagemodule.cpp +index 705d313fc..6cda6766b 100644 +--- a/core/fpdfapi/page/cpdf_pagemodule.cpp ++++ b/core/fpdfapi/page/cpdf_pagemodule.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,7 +10,7 @@ + #include "core/fpdfapi/page/cpdf_colorspace.h" + #include "core/fpdfapi/page/cpdf_devicecs.h" + #include "core/fpdfapi/page/cpdf_patterncs.h" +-#include "core/fxcodec/fx_codec.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -20,30 +20,31 @@ CPDF_PageModule* g_PageModule = nullptr; + + // static + void CPDF_PageModule::Create() { +- ASSERT(!g_PageModule); +- fxcodec::ModuleMgr::Create(); ++ DCHECK(!g_PageModule); + g_PageModule = new CPDF_PageModule(); + } + + // static + void CPDF_PageModule::Destroy() { +- ASSERT(g_PageModule); ++ DCHECK(g_PageModule); + delete g_PageModule; + g_PageModule = nullptr; +- fxcodec::ModuleMgr::Destroy(); + } + + // static + CPDF_PageModule* CPDF_PageModule::GetInstance() { +- ASSERT(g_PageModule); ++ DCHECK(g_PageModule); + return g_PageModule; + } + + CPDF_PageModule::CPDF_PageModule() +- : m_StockGrayCS(pdfium::MakeRetain(PDFCS_DEVICEGRAY)), +- m_StockRGBCS(pdfium::MakeRetain(PDFCS_DEVICERGB)), +- m_StockCMYKCS(pdfium::MakeRetain(PDFCS_DEVICECMYK)), +- m_StockPatternCS(pdfium::MakeRetain(nullptr)) { ++ : m_StockGrayCS(pdfium::MakeRetain( ++ CPDF_ColorSpace::Family::kDeviceGray)), ++ m_StockRGBCS(pdfium::MakeRetain( ++ CPDF_ColorSpace::Family::kDeviceRGB)), ++ m_StockCMYKCS(pdfium::MakeRetain( ++ CPDF_ColorSpace::Family::kDeviceCMYK)), ++ m_StockPatternCS(pdfium::MakeRetain()) { + m_StockPatternCS->InitializeStockPattern(); + CPDF_FontGlobals::Create(); + CPDF_FontGlobals::GetInstance()->LoadEmbeddedMaps(); +@@ -53,14 +54,15 @@ CPDF_PageModule::~CPDF_PageModule() { + CPDF_FontGlobals::Destroy(); + } + +-RetainPtr CPDF_PageModule::GetStockCS(int family) { +- if (family == PDFCS_DEVICEGRAY) ++RetainPtr CPDF_PageModule::GetStockCS( ++ CPDF_ColorSpace::Family family) { ++ if (family == CPDF_ColorSpace::Family::kDeviceGray) + return m_StockGrayCS; +- if (family == PDFCS_DEVICERGB) ++ if (family == CPDF_ColorSpace::Family::kDeviceRGB) + return m_StockRGBCS; +- if (family == PDFCS_DEVICECMYK) ++ if (family == CPDF_ColorSpace::Family::kDeviceCMYK) + return m_StockCMYKCS; +- if (family == PDFCS_PATTERN) ++ if (family == CPDF_ColorSpace::Family::kPattern) + return m_StockPatternCS; + return nullptr; + } +diff --git a/core/fpdfapi/page/cpdf_pagemodule.h b/core/fpdfapi/page/cpdf_pagemodule.h +index e7234b9c0..dfd130818 100644 +--- a/core/fpdfapi/page/cpdf_pagemodule.h ++++ b/core/fpdfapi/page/cpdf_pagemodule.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,10 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_PAGEMODULE_H_ + #define CORE_FPDFAPI_PAGE_CPDF_PAGEMODULE_H_ + ++#include "core/fpdfapi/page/cpdf_colorspace.h" + #include "core/fxcrt/retain_ptr.h" + + class CPDF_Document; +-class CPDF_ColorSpace; + class CPDF_DeviceCS; + class CPDF_PatternCS; + +@@ -21,7 +21,7 @@ class CPDF_PageModule { + static void Destroy(); + static CPDF_PageModule* GetInstance(); + +- RetainPtr GetStockCS(int family); ++ RetainPtr GetStockCS(CPDF_ColorSpace::Family family); + void ClearStockFont(CPDF_Document* pDoc); + + private: +diff --git a/core/fpdfapi/page/cpdf_pageobject.cpp b/core/fpdfapi/page/cpdf_pageobject.cpp +index bde1704e8..8636af6ce 100644 +--- a/core/fpdfapi/page/cpdf_pageobject.cpp ++++ b/core/fpdfapi/page/cpdf_pageobject.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,8 @@ + + #include "core/fpdfapi/page/cpdf_pageobject.h" + ++#include "core/fxcrt/fx_coordinates.h" ++ + CPDF_PageObject::CPDF_PageObject(int32_t content_stream) + : m_ContentStream(content_stream) {} + +diff --git a/core/fpdfapi/page/cpdf_pageobject.h b/core/fpdfapi/page/cpdf_pageobject.h +index f84021467..7d9d01583 100644 +--- a/core/fpdfapi/page/cpdf_pageobject.h ++++ b/core/fpdfapi/page/cpdf_pageobject.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,12 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECT_H_ + #define CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECT_H_ + ++#include ++ + #include "core/fpdfapi/page/cpdf_contentmarks.h" + #include "core/fpdfapi/page/cpdf_graphicstates.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" + + class CPDF_FormObject; + class CPDF_ImageObject; +@@ -18,14 +20,18 @@ class CPDF_PathObject; + class CPDF_ShadingObject; + class CPDF_TextObject; + ++// Represents an object within the page, like a form or image. Not to be ++// confused with the PDF spec's page object that lives in a page tree, which is ++// represented by CPDF_Page. + class CPDF_PageObject : public CPDF_GraphicStates { + public: +- enum Type { +- TEXT = 1, +- PATH, +- IMAGE, +- SHADING, +- FORM, ++ // Values must match corresponding values in //public. ++ enum class Type { ++ kText = 1, ++ kPath, ++ kImage, ++ kShading, ++ kForm, + }; + + static constexpr int32_t kNoContentStream = -1; +@@ -58,11 +64,19 @@ class CPDF_PageObject : public CPDF_GraphicStates { + void TransformClipPath(const CFX_Matrix& matrix); + void TransformGeneralState(const CFX_Matrix& matrix); + ++ void SetOriginalRect(const CFX_FloatRect& rect) { m_OriginalRect = rect; } ++ const CFX_FloatRect& GetOriginalRect() const { return m_OriginalRect; } + void SetRect(const CFX_FloatRect& rect) { m_Rect = rect; } + const CFX_FloatRect& GetRect() const { return m_Rect; } + FX_RECT GetBBox() const; + FX_RECT GetTransformedBBox(const CFX_Matrix& matrix) const; + ++ CPDF_ContentMarks* GetContentMarks() { return &m_ContentMarks; } ++ const CPDF_ContentMarks* GetContentMarks() const { return &m_ContentMarks; } ++ void SetContentMarks(const CPDF_ContentMarks& marks) { ++ m_ContentMarks = marks; ++ } ++ + // Get what content stream the object was parsed from in its page. This number + // is the index of the content stream in the "Contents" array, or 0 if there + // is a single content stream. If the object is newly created, +@@ -75,16 +89,29 @@ class CPDF_PageObject : public CPDF_GraphicStates { + m_ContentStream = new_content_stream; + } + +- CPDF_ContentMarks m_ContentMarks; ++ const ByteString& GetResourceName() const { return m_ResourceName; } ++ void SetResourceName(const ByteString& resource_name) { ++ m_ResourceName = resource_name; ++ } ++ ++ const ByteString& GetGraphicsResourceName() const { ++ return m_GraphicsResourceName; ++ } ++ void SetGraphicsResourceName(const ByteString& resource_name) { ++ m_GraphicsResourceName = resource_name; ++ } + + protected: + void CopyData(const CPDF_PageObject* pSrcObject); + +- CFX_FloatRect m_Rect; +- + private: ++ CFX_FloatRect m_Rect; ++ CFX_FloatRect m_OriginalRect; ++ CPDF_ContentMarks m_ContentMarks; + bool m_bDirty = false; + int32_t m_ContentStream; ++ ByteString m_ResourceName; // The resource name for this object. ++ ByteString m_GraphicsResourceName; // Like `m_ResourceName` but for graphics. + }; + + #endif // CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECT_H_ +diff --git a/core/fpdfapi/page/cpdf_pageobjectholder.cpp b/core/fpdfapi/page/cpdf_pageobjectholder.cpp +index d7307ab28..8aa30cae5 100644 +--- a/core/fpdfapi/page/cpdf_pageobjectholder.cpp ++++ b/core/fpdfapi/page/cpdf_pageobjectholder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,7 +16,9 @@ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fxcrt/fx_extension.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxcrt/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" + + bool GraphicsData::operator<(const GraphicsData& other) const { + if (!FXSYS_SafeEQ(fillAlpha, other.fillAlpha)) +@@ -32,15 +34,16 @@ bool FontData::operator<(const FontData& other) const { + return type < other.type; + } + +-CPDF_PageObjectHolder::CPDF_PageObjectHolder(CPDF_Document* pDoc, +- CPDF_Dictionary* pDict, +- CPDF_Dictionary* pPageResources, +- CPDF_Dictionary* pResources) +- : m_pPageResources(pPageResources), +- m_pResources(pResources), +- m_pDict(pDict), ++CPDF_PageObjectHolder::CPDF_PageObjectHolder( ++ CPDF_Document* pDoc, ++ RetainPtr pDict, ++ RetainPtr pPageResources, ++ RetainPtr pResources) ++ : m_pPageResources(std::move(pPageResources)), ++ m_pResources(std::move(pResources)), ++ m_pDict(std::move(pDict)), + m_pDocument(pDoc) { +- ASSERT(m_pDict); ++ DCHECK(m_pDict); + } + + CPDF_PageObjectHolder::~CPDF_PageObjectHolder() = default; +@@ -51,7 +54,7 @@ bool CPDF_PageObjectHolder::IsPage() const { + + void CPDF_PageObjectHolder::StartParse( + std::unique_ptr pParser) { +- ASSERT(m_ParseState == ParseState::kNotParsed); ++ DCHECK_EQ(m_ParseState, ParseState::kNotParsed); + m_pParser = std::move(pParser); + m_ParseState = ParseState::kParsing; + } +@@ -60,7 +63,7 @@ void CPDF_PageObjectHolder::ContinueParse(PauseIndicatorIface* pPause) { + if (m_ParseState == ParseState::kParsed) + return; + +- ASSERT(m_ParseState == ParseState::kParsing); ++ DCHECK_EQ(m_ParseState, ParseState::kParsing); + if (m_pParser->Continue(pPause)) + return; + +@@ -82,12 +85,40 @@ std::set CPDF_PageObjectHolder::TakeDirtyStreams() { + return dirty_streams; + } + ++absl::optional CPDF_PageObjectHolder::GraphicsMapSearch( ++ const GraphicsData& gd) { ++ auto it = m_GraphicsMap.find(gd); ++ if (it == m_GraphicsMap.end()) ++ return absl::nullopt; ++ ++ return it->second; ++} ++ ++void CPDF_PageObjectHolder::GraphicsMapInsert(const GraphicsData& gd, ++ const ByteString& str) { ++ m_GraphicsMap[gd] = str; ++} ++ ++absl::optional CPDF_PageObjectHolder::FontsMapSearch( ++ const FontData& fd) { ++ auto it = m_FontsMap.find(fd); ++ if (it == m_FontsMap.end()) ++ return absl::nullopt; ++ ++ return it->second; ++} ++ ++void CPDF_PageObjectHolder::FontsMapInsert(const FontData& fd, ++ const ByteString& str) { ++ m_FontsMap[fd] = str; ++} ++ + void CPDF_PageObjectHolder::LoadTransparencyInfo() { +- CPDF_Dictionary* pGroup = m_pDict->GetDictFor("Group"); ++ RetainPtr pGroup = m_pDict->GetDictFor("Group"); + if (!pGroup) + return; + +- if (pGroup->GetStringFor(pdfium::transparency::kGroupSubType) != ++ if (pGroup->GetByteStringFor(pdfium::transparency::kGroupSubType) != + pdfium::transparency::kTransparency) { + return; + } +@@ -98,7 +129,7 @@ void CPDF_PageObjectHolder::LoadTransparencyInfo() { + + CPDF_PageObject* CPDF_PageObjectHolder::GetPageObjectByIndex( + size_t index) const { +- return pdfium::IndexInBounds(m_PageObjectList, index) ++ return fxcrt::IndexInBounds(m_PageObjectList, index) + ? m_PageObjectList[index].get() + : nullptr; + } +@@ -108,22 +139,21 @@ void CPDF_PageObjectHolder::AppendPageObject( + m_PageObjectList.push_back(std::move(pPageObj)); + } + +-bool CPDF_PageObjectHolder::RemovePageObject(CPDF_PageObject* pPageObj) { +- pdfium::FakeUniquePtr p(pPageObj); +- +- auto it = +- std::find(std::begin(m_PageObjectList), std::end(m_PageObjectList), p); ++std::unique_ptr CPDF_PageObjectHolder::RemovePageObject( ++ CPDF_PageObject* pPageObj) { ++ auto it = std::find(std::begin(m_PageObjectList), std::end(m_PageObjectList), ++ fxcrt::MakeFakeUniquePtr(pPageObj)); + if (it == std::end(m_PageObjectList)) +- return false; ++ return nullptr; + +- it->release(); ++ std::unique_ptr result = std::move(*it); + m_PageObjectList.erase(it); + + int32_t content_stream = pPageObj->GetContentStream(); + if (content_stream >= 0) + m_DirtyStreams.insert(content_stream); + +- return true; ++ return result; + } + + bool CPDF_PageObjectHolder::ErasePageObjectAtIndex(size_t index) { +diff --git a/core/fpdfapi/page/cpdf_pageobjectholder.h b/core/fpdfapi/page/cpdf_pageobjectholder.h +index b9eff3058..629c02843 100644 +--- a/core/fpdfapi/page/cpdf_pageobjectholder.h ++++ b/core/fpdfapi/page/cpdf_pageobjectholder.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,24 +7,28 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECTHOLDER_H_ + #define CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECTHOLDER_H_ + ++#include ++#include ++ + #include + #include + #include + #include ++#include + #include + + #include "core/fpdfapi/page/cpdf_transparency.h" ++#include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CPDF_ContentParser; +-class CPDF_Dictionary; + class CPDF_Document; + class CPDF_PageObject; +-class CPDF_Stream; + class PauseIndicatorIface; + + // These structs are used to keep track of resources that have already been +@@ -53,9 +57,9 @@ class CPDF_PageObjectHolder { + std::deque>::const_iterator; + + CPDF_PageObjectHolder(CPDF_Document* pDoc, +- CPDF_Dictionary* pDict, +- CPDF_Dictionary* pPageResources, +- CPDF_Dictionary* pResources); ++ RetainPtr pDict, ++ RetainPtr pPageResources, ++ RetainPtr pResources); + virtual ~CPDF_PageObjectHolder(); + + virtual bool IsPage() const; +@@ -64,13 +68,26 @@ class CPDF_PageObjectHolder { + void ContinueParse(PauseIndicatorIface* pPause); + ParseState GetParseState() const { return m_ParseState; } + +- CPDF_Document* GetDocument() const { return m_pDocument.Get(); } +- +- CPDF_Dictionary* GetDict() const { return m_pDict.Get(); } ++ CPDF_Document* GetDocument() const { return m_pDocument; } ++ RetainPtr GetDict() const { return m_pDict; } ++ RetainPtr GetMutableDict() { return m_pDict; } ++ RetainPtr GetResources() const { return m_pResources; } ++ RetainPtr GetMutableResources() { return m_pResources; } ++ void SetResources(RetainPtr pDict) { ++ m_pResources = std::move(pDict); ++ } ++ RetainPtr GetPageResources() const { ++ return m_pPageResources; ++ } ++ RetainPtr GetMutablePageResources() { ++ return m_pPageResources; ++ } + size_t GetPageObjectCount() const { return m_PageObjectList.size(); } + CPDF_PageObject* GetPageObjectByIndex(size_t index) const; + void AppendPageObject(std::unique_ptr pPageObj); +- bool RemovePageObject(CPDF_PageObject* pPageObj); ++ ++ // Remove `pPageObj` if present, and transfer ownership to the caller. ++ std::unique_ptr RemovePageObject(CPDF_PageObject* pPageObj); + bool ErasePageObjectAtIndex(size_t index); + + iterator begin() { return m_PageObjectList.begin(); } +@@ -96,14 +113,19 @@ class CPDF_PageObjectHolder { + bool HasDirtyStreams() const { return !m_DirtyStreams.empty(); } + std::set TakeDirtyStreams(); + +- RetainPtr m_pPageResources; +- RetainPtr m_pResources; +- std::map m_GraphicsMap; +- std::map m_FontsMap; ++ absl::optional GraphicsMapSearch(const GraphicsData& gd); ++ void GraphicsMapInsert(const GraphicsData& gd, const ByteString& str); ++ ++ absl::optional FontsMapSearch(const FontData& fd); ++ void FontsMapInsert(const FontData& fd, const ByteString& str); + + protected: + void LoadTransparencyInfo(); + ++ RetainPtr m_pPageResources; ++ RetainPtr m_pResources; ++ std::map m_GraphicsMap; ++ std::map m_FontsMap; + CFX_FloatRect m_BBox; + CPDF_Transparency m_Transparency; + +diff --git a/core/fpdfapi/page/cpdf_pageobjectholder_unittest.cpp b/core/fpdfapi/page/cpdf_pageobjectholder_unittest.cpp +index 77602562f..c59975216 100644 +--- a/core/fpdfapi/page/cpdf_pageobjectholder_unittest.cpp ++++ b/core/fpdfapi/page/cpdf_pageobjectholder_unittest.cpp +@@ -1,13 +1,22 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/page/cpdf_pageobjectholder.h" + ++#include ++ ++#include + #include ++#include + ++#include "core/fxcrt/fx_extension.h" + #include "testing/gtest/include/gtest/gtest.h" + ++bool SafeCompare(const float& x, const float& y) { ++ return FXSYS_SafeLT(x, y); ++} ++ + // See https://crbug.com/852273 + TEST(CPDFPageObjectHolder, GraphicsDataAsKey) { + const float fMin = std::numeric_limits::min(); +@@ -23,6 +32,14 @@ TEST(CPDFPageObjectHolder, GraphicsDataAsKey) { + } + } + ++ // Validate the documented sort order. ++ std::vector data = {fMax, fInf, fNan, fMin}; ++ std::sort(data.begin(), data.end(), SafeCompare); ++ EXPECT_EQ(data[0], fMin); ++ EXPECT_EQ(data[1], fMax); ++ EXPECT_EQ(data[2], fInf); ++ EXPECT_EQ(isnan(data[3]), isnan(fNan)); ++ + std::map graphics_map; + + // Insert in reverse index permuted order. +diff --git a/core/fpdfapi/page/cpdf_path.cpp b/core/fpdfapi/page/cpdf_path.cpp +index a07bb6a25..cec81752a 100644 +--- a/core/fpdfapi/page/cpdf_path.cpp ++++ b/core/fpdfapi/page/cpdf_path.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,7 +12,7 @@ CPDF_Path::CPDF_Path(const CPDF_Path& that) : m_Ref(that.m_Ref) {} + + CPDF_Path::~CPDF_Path() = default; + +-const std::vector& CPDF_Path::GetPoints() const { ++const std::vector& CPDF_Path::GetPoints() const { + return m_Ref.GetObject()->GetPoints(); + } + +@@ -28,9 +28,10 @@ CFX_FloatRect CPDF_Path::GetBoundingBox() const { + return m_Ref.GetObject()->GetBoundingBox(); + } + +-CFX_FloatRect CPDF_Path::GetBoundingBox(float line_width, +- float miter_limit) const { +- return m_Ref.GetObject()->GetBoundingBox(line_width, miter_limit); ++CFX_FloatRect CPDF_Path::GetBoundingBoxForStrokePath(float line_width, ++ float miter_limit) const { ++ return m_Ref.GetObject()->GetBoundingBoxForStrokePath(line_width, ++ miter_limit); + } + + bool CPDF_Path::IsRect() const { +@@ -41,8 +42,8 @@ void CPDF_Path::Transform(const CFX_Matrix& matrix) { + m_Ref.GetPrivateCopy()->Transform(matrix); + } + +-void CPDF_Path::Append(const CFX_PathData* pData, const CFX_Matrix* pMatrix) { +- m_Ref.GetPrivateCopy()->Append(pData, pMatrix); ++void CPDF_Path::Append(const CFX_Path& path, const CFX_Matrix* pMatrix) { ++ m_Ref.GetPrivateCopy()->Append(path, pMatrix); + } + + void CPDF_Path::AppendFloatRect(const CFX_FloatRect& rect) { +@@ -54,9 +55,15 @@ void CPDF_Path::AppendRect(float left, float bottom, float right, float top) { + } + + void CPDF_Path::AppendPoint(const CFX_PointF& point, +- FXPT_TYPE type, +- bool close) { +- CFX_PathData data; +- data.AppendPoint(point, type, close); +- Append(&data, nullptr); ++ CFX_Path::Point::Type type) { ++ CFX_Path data; ++ data.AppendPoint(point, type); ++ Append(data, nullptr); ++} ++ ++void CPDF_Path::AppendPointAndClose(const CFX_PointF& point, ++ CFX_Path::Point::Type type) { ++ CFX_Path data; ++ data.AppendPointAndClose(point, type); ++ Append(data, nullptr); + } +diff --git a/core/fpdfapi/page/cpdf_path.h b/core/fpdfapi/page/cpdf_path.h +index 48f563438..f901f505f 100644 +--- a/core/fpdfapi/page/cpdf_path.h ++++ b/core/fpdfapi/page/cpdf_path.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,9 +9,8 @@ + + #include + +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/shared_copy_on_write.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + + class CPDF_Path { + public: +@@ -22,26 +21,28 @@ class CPDF_Path { + void Emplace() { m_Ref.Emplace(); } + bool HasRef() const { return !!m_Ref; } + +- const std::vector& GetPoints() const; ++ const std::vector& GetPoints() const; + void ClosePath(); + + CFX_PointF GetPoint(int index) const; + CFX_FloatRect GetBoundingBox() const; +- CFX_FloatRect GetBoundingBox(float line_width, float miter_limit) const; ++ CFX_FloatRect GetBoundingBoxForStrokePath(float line_width, ++ float miter_limit) const; + + bool IsRect() const; + void Transform(const CFX_Matrix& matrix); + +- void Append(const CFX_PathData* pData, const CFX_Matrix* pMatrix); ++ void Append(const CFX_Path& path, const CFX_Matrix* pMatrix); + void AppendFloatRect(const CFX_FloatRect& rect); + void AppendRect(float left, float bottom, float right, float top); +- void AppendPoint(const CFX_PointF& point, FXPT_TYPE type, bool close); ++ void AppendPoint(const CFX_PointF& point, CFX_Path::Point::Type type); ++ void AppendPointAndClose(const CFX_PointF& point, CFX_Path::Point::Type type); + + // TODO(tsepez): Remove when all access thru this class. +- const CFX_PathData* GetObject() const { return m_Ref.GetObject(); } ++ const CFX_Path* GetObject() const { return m_Ref.GetObject(); } + + private: +- SharedCopyOnWrite m_Ref; ++ SharedCopyOnWrite m_Ref; + }; + + #endif // CORE_FPDFAPI_PAGE_CPDF_PATH_H_ +diff --git a/core/fpdfapi/page/cpdf_pathobject.cpp b/core/fpdfapi/page/cpdf_pathobject.cpp +index eafbe77ca..b5e7469bc 100644 +--- a/core/fpdfapi/page/cpdf_pathobject.cpp ++++ b/core/fpdfapi/page/cpdf_pathobject.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ CPDF_PathObject::CPDF_PathObject() : CPDF_PathObject(kNoContentStream) {} + CPDF_PathObject::~CPDF_PathObject() = default; + + CPDF_PageObject::Type CPDF_PathObject::GetType() const { +- return PATH; ++ return Type::kPath; + } + + void CPDF_PathObject::Transform(const CFX_Matrix& matrix) { +@@ -41,7 +41,8 @@ void CPDF_PathObject::CalcBoundingBox() { + CFX_FloatRect rect; + float width = m_GraphState.GetLineWidth(); + if (m_bStroke && width != 0) { +- rect = m_Path.GetBoundingBox(width, m_GraphState.GetMiterLimit()); ++ rect = ++ m_Path.GetBoundingBoxForStrokePath(width, m_GraphState.GetMiterLimit()); + } else { + rect = m_Path.GetBoundingBox(); + } +@@ -51,3 +52,8 @@ void CPDF_PathObject::CalcBoundingBox() { + rect.Inflate(0.5f, 0.5f); + SetRect(rect); + } ++ ++void CPDF_PathObject::SetPathMatrix(const CFX_Matrix& matrix) { ++ m_Matrix = matrix; ++ CalcBoundingBox(); ++} +diff --git a/core/fpdfapi/page/cpdf_pathobject.h b/core/fpdfapi/page/cpdf_pathobject.h +index 889d8a69b..a0b19cb74 100644 +--- a/core/fpdfapi/page/cpdf_pathobject.h ++++ b/core/fpdfapi/page/cpdf_pathobject.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,12 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_PATHOBJECT_H_ + #define CORE_FPDFAPI_PAGE_CPDF_PATHOBJECT_H_ + ++#include ++ + #include "core/fpdfapi/page/cpdf_pageobject.h" + #include "core/fpdfapi/page/cpdf_path.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" +-#include "core/fxge/render_defines.h" ++#include "core/fxge/cfx_fillrenderoptions.h" + + class CPDF_PathObject final : public CPDF_PageObject { + public: +@@ -31,26 +32,41 @@ class CPDF_PathObject final : public CPDF_PageObject { + bool stroke() const { return m_bStroke; } + void set_stroke(bool stroke) { m_bStroke = stroke; } + +- // Layering, avoid caller knowledge of FXFILL_ values. +- bool has_no_filltype() const { return m_FillType == 0; } +- bool has_winding_filltype() const { return m_FillType == FXFILL_WINDING; } +- bool has_alternate_filltype() const { return m_FillType == FXFILL_ALTERNATE; } +- void set_no_filltype() { m_FillType = 0; } +- void set_winding_filltype() { m_FillType = FXFILL_WINDING; } +- void set_alternate_filltype() { m_FillType = FXFILL_ALTERNATE; } ++ // Layering, avoid caller knowledge of CFX_FillRenderOptions::FillType values. ++ bool has_no_filltype() const { ++ return m_FillType == CFX_FillRenderOptions::FillType::kNoFill; ++ } ++ bool has_winding_filltype() const { ++ return m_FillType == CFX_FillRenderOptions::FillType::kWinding; ++ } ++ bool has_alternate_filltype() const { ++ return m_FillType == CFX_FillRenderOptions::FillType::kEvenOdd; ++ } ++ void set_no_filltype() { ++ m_FillType = CFX_FillRenderOptions::FillType::kNoFill; ++ } ++ void set_winding_filltype() { ++ m_FillType = CFX_FillRenderOptions::FillType::kWinding; ++ } ++ void set_alternate_filltype() { ++ m_FillType = CFX_FillRenderOptions::FillType::kEvenOdd; ++ } + +- int filltype() const { return m_FillType; } +- void set_filltype(int filltype) { m_FillType = filltype; } ++ CFX_FillRenderOptions::FillType filltype() const { return m_FillType; } ++ void set_filltype(CFX_FillRenderOptions::FillType fill_type) { ++ m_FillType = fill_type; ++ } + + CPDF_Path& path() { return m_Path; } + const CPDF_Path& path() const { return m_Path; } + + const CFX_Matrix& matrix() const { return m_Matrix; } +- void set_matrix(const CFX_Matrix& matrix) { m_Matrix = matrix; } ++ void SetPathMatrix(const CFX_Matrix& matrix); + + private: + bool m_bStroke = false; +- int m_FillType = 0; ++ CFX_FillRenderOptions::FillType m_FillType = ++ CFX_FillRenderOptions::FillType::kNoFill; + CPDF_Path m_Path; + CFX_Matrix m_Matrix; + }; +diff --git a/core/fpdfapi/page/cpdf_pattern.cpp b/core/fpdfapi/page/cpdf_pattern.cpp +index 36ecc98d5..297c77a99 100644 +--- a/core/fpdfapi/page/cpdf_pattern.cpp ++++ b/core/fpdfapi/page/cpdf_pattern.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,14 +6,19 @@ + + #include "core/fpdfapi/page/cpdf_pattern.h" + ++#include ++ + #include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "third_party/base/check.h" + + CPDF_Pattern::CPDF_Pattern(CPDF_Document* pDoc, +- CPDF_Object* pObj, ++ RetainPtr pObj, + const CFX_Matrix& parentMatrix) +- : m_pDocument(pDoc), m_pPatternObj(pObj), m_ParentMatrix(parentMatrix) { +- ASSERT(m_pDocument); +- ASSERT(m_pPatternObj); ++ : m_pDocument(pDoc), ++ m_pPatternObj(std::move(pObj)), ++ m_ParentMatrix(parentMatrix) { ++ DCHECK(m_pDocument); ++ DCHECK(m_pPatternObj); + } + + CPDF_Pattern::~CPDF_Pattern() = default; +@@ -27,6 +32,6 @@ CPDF_ShadingPattern* CPDF_Pattern::AsShadingPattern() { + } + + void CPDF_Pattern::SetPatternToFormMatrix() { +- const CPDF_Dictionary* pDict = pattern_obj()->GetDict(); ++ RetainPtr pDict = pattern_obj()->GetDict(); + m_Pattern2Form = pDict->GetMatrixFor("Matrix") * m_ParentMatrix; + } +diff --git a/core/fpdfapi/page/cpdf_pattern.h b/core/fpdfapi/page/cpdf_pattern.h +index 7e3eb7b63..cd535b124 100644 +--- a/core/fpdfapi/page/cpdf_pattern.h ++++ b/core/fpdfapi/page/cpdf_pattern.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,14 +7,13 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_PATTERN_H_ + #define CORE_FPDFAPI_PAGE_CPDF_PATTERN_H_ + ++#include "core/fpdfapi/parser/cpdf_object.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/observed_ptr.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + + class CPDF_Document; +-class CPDF_Object; + class CPDF_ShadingPattern; + class CPDF_TilingPattern; + +@@ -28,17 +27,18 @@ class CPDF_Pattern : public Retainable, public Observable { + virtual CPDF_TilingPattern* AsTilingPattern(); + virtual CPDF_ShadingPattern* AsShadingPattern(); + +- // All the getters that return pointers return non-NULL pointers. +- CPDF_Document* document() const { return m_pDocument.Get(); } +- CPDF_Object* pattern_obj() const { return m_pPatternObj.Get(); } + const CFX_Matrix& pattern_to_form() const { return m_Pattern2Form; } +- const CFX_Matrix& parent_matrix() const { return m_ParentMatrix; } + + protected: + CPDF_Pattern(CPDF_Document* pDoc, +- CPDF_Object* pObj, ++ RetainPtr pObj, + const CFX_Matrix& parentMatrix); + ++ // All the getters that return pointers return non-NULL pointers. ++ CPDF_Document* document() const { return m_pDocument; } ++ RetainPtr pattern_obj() const { return m_pPatternObj; } ++ const CFX_Matrix& parent_matrix() const { return m_ParentMatrix; } ++ + void SetPatternToFormMatrix(); + + private: +diff --git a/core/fpdfapi/page/cpdf_patterncs.cpp b/core/fpdfapi/page/cpdf_patterncs.cpp +index 1c5dc6ce6..7abc0b251 100644 +--- a/core/fpdfapi/page/cpdf_patterncs.cpp ++++ b/core/fpdfapi/page/cpdf_patterncs.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,9 +9,9 @@ + #include "core/fpdfapi/page/cpdf_docpagedata.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_document.h" ++#include "third_party/base/notreached.h" + +-CPDF_PatternCS::CPDF_PatternCS(CPDF_Document* pDoc) +- : CPDF_ColorSpace(pDoc, PDFCS_PATTERN) {} ++CPDF_PatternCS::CPDF_PatternCS() : CPDF_BasedCS(Family::kPattern) {} + + CPDF_PatternCS::~CPDF_PatternCS() = default; + +@@ -22,16 +22,17 @@ void CPDF_PatternCS::InitializeStockPattern() { + uint32_t CPDF_PatternCS::v_Load(CPDF_Document* pDoc, + const CPDF_Array* pArray, + std::set* pVisited) { +- const CPDF_Object* pBaseCS = pArray->GetDirectObjectAt(1); +- if (pBaseCS == m_pArray) ++ RetainPtr pBaseCS = pArray->GetDirectObjectAt(1); ++ if (HasSameArray(pBaseCS.Get())) + return 0; + + auto* pDocPageData = CPDF_DocPageData::FromDocument(pDoc); +- m_pBaseCS = pDocPageData->GetColorSpaceGuarded(pBaseCS, nullptr, pVisited); ++ m_pBaseCS = ++ pDocPageData->GetColorSpaceGuarded(pBaseCS.Get(), nullptr, pVisited); + if (!m_pBaseCS) + return 1; + +- if (m_pBaseCS->GetFamily() == PDFCS_PATTERN) ++ if (m_pBaseCS->GetFamily() == Family::kPattern) + return 0; + + if (m_pBaseCS->CountComponents() > kMaxPatternColorComps) +@@ -40,16 +41,11 @@ uint32_t CPDF_PatternCS::v_Load(CPDF_Document* pDoc, + return m_pBaseCS->CountComponents() + 1; + } + +-bool CPDF_PatternCS::GetRGB(const float* pBuf, ++bool CPDF_PatternCS::GetRGB(pdfium::span pBuf, + float* R, + float* G, + float* B) const { +- NOTREACHED(); +- return false; +-} +- +-CPDF_PatternCS* CPDF_PatternCS::AsPatternCS() { +- return this; ++ NOTREACHED_NORETURN(); + } + + const CPDF_PatternCS* CPDF_PatternCS::AsPatternCS() const { +@@ -60,7 +56,7 @@ bool CPDF_PatternCS::GetPatternRGB(const PatternValue& value, + float* R, + float* G, + float* B) const { +- if (m_pBaseCS && m_pBaseCS->GetRGB(value.GetComps().data(), R, G, B)) ++ if (m_pBaseCS && m_pBaseCS->GetRGB(value.GetComps(), R, G, B)) + return true; + + *R = 0.75f; +diff --git a/core/fpdfapi/page/cpdf_patterncs.h b/core/fpdfapi/page/cpdf_patterncs.h +index b6b46f6ae..896a95ccd 100644 +--- a/core/fpdfapi/page/cpdf_patterncs.h ++++ b/core/fpdfapi/page/cpdf_patterncs.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,16 +9,14 @@ + + #include + +-#include "core/fpdfapi/page/cpdf_colorspace.h" ++#include "core/fpdfapi/page/cpdf_basedcs.h" + #include "core/fxcrt/retain_ptr.h" + + class CPDF_Document; + +-class CPDF_PatternCS final : public CPDF_ColorSpace { ++class CPDF_PatternCS final : public CPDF_BasedCS { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- ++ CONSTRUCT_VIA_MAKE_RETAIN; + ~CPDF_PatternCS() override; + + // Called for the stock pattern, since it is not initialized via +@@ -26,21 +24,22 @@ class CPDF_PatternCS final : public CPDF_ColorSpace { + void InitializeStockPattern(); + + // CPDF_ColorSpace: +- bool GetRGB(const float* pBuf, float* R, float* G, float* B) const override; +- bool GetPatternRGB(const PatternValue& value, +- float* R, +- float* G, +- float* B) const override; +- CPDF_PatternCS* AsPatternCS() override; ++ bool GetRGB(pdfium::span pBuf, ++ float* R, ++ float* G, ++ float* B) const override; + const CPDF_PatternCS* AsPatternCS() const override; + uint32_t v_Load(CPDF_Document* pDoc, + const CPDF_Array* pArray, + std::set* pVisited) override; + +- private: +- explicit CPDF_PatternCS(CPDF_Document* pDoc); ++ bool GetPatternRGB(const PatternValue& value, ++ float* R, ++ float* G, ++ float* B) const; + +- RetainPtr m_pBaseCS; ++ private: ++ CPDF_PatternCS(); + }; + + #endif // CORE_FPDFAPI_PAGE_CPDF_PATTERNCS_H_ +diff --git a/core/fpdfapi/page/cpdf_psengine.cpp b/core/fpdfapi/page/cpdf_psengine.cpp +index 98f5c47a9..da7b00dc0 100644 +--- a/core/fpdfapi/page/cpdf_psengine.cpp ++++ b/core/fpdfapi/page/cpdf_psengine.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,21 +6,24 @@ + + #include "core/fpdfapi/page/cpdf_psengine.h" + ++#include ++ + #include +-#include + #include + #include + + #include "core/fpdfapi/parser/cpdf_simple_parser.h" + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxcrt/fx_string.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/notreached.h" + + namespace { + + struct PDF_PSOpName { +- const char* name; ++ // Inline string data reduces size for small strings. ++ const char name[9]; + PDF_PSOP op; + }; + +@@ -72,7 +75,7 @@ constexpr PDF_PSOpName kPsOpNames[] = { + // Round half up is a nearest integer round with half-way numbers always rounded + // up. Example: -5.5 rounds to -5. + float RoundHalfUp(float f) { +- if (std::isnan(f)) ++ if (isnan(f)) + return 0; + if (f > std::numeric_limits::max() - 0.5f) + return std::numeric_limits::max(); +@@ -82,44 +85,47 @@ float RoundHalfUp(float f) { + } // namespace + + CPDF_PSOP::CPDF_PSOP() +- : m_op(PSOP_PROC), m_value(0), m_proc(pdfium::MakeUnique()) {} ++ : m_op(PSOP_PROC), m_value(0), m_proc(std::make_unique()) {} + + CPDF_PSOP::CPDF_PSOP(PDF_PSOP op) : m_op(op), m_value(0) { +- ASSERT(m_op != PSOP_CONST); +- ASSERT(m_op != PSOP_PROC); ++ DCHECK(m_op != PSOP_CONST); ++ DCHECK(m_op != PSOP_PROC); + } + + CPDF_PSOP::CPDF_PSOP(float value) : m_op(PSOP_CONST), m_value(value) {} + +-CPDF_PSOP::~CPDF_PSOP() {} ++CPDF_PSOP::~CPDF_PSOP() = default; ++ ++bool CPDF_PSOP::Parse(CPDF_SimpleParser* parser, int depth) { ++ CHECK_EQ(m_op, PSOP_PROC); ++ return m_proc->Parse(parser, depth); ++} ++ ++void CPDF_PSOP::Execute(CPDF_PSEngine* pEngine) { ++ CHECK_EQ(m_op, PSOP_PROC); ++ m_proc->Execute(pEngine); ++} + + float CPDF_PSOP::GetFloatValue() const { + if (m_op == PSOP_CONST) + return m_value; + +- NOTREACHED(); +- return 0; +-} +- +-CPDF_PSProc* CPDF_PSOP::GetProc() const { +- if (m_op == PSOP_PROC) +- return m_proc.get(); +- NOTREACHED(); +- return nullptr; ++ NOTREACHED_NORETURN(); + } + + bool CPDF_PSEngine::Execute() { + return m_MainProc.Execute(this); + } + +-CPDF_PSProc::CPDF_PSProc() {} +-CPDF_PSProc::~CPDF_PSProc() {} ++CPDF_PSProc::CPDF_PSProc() = default; ++ ++CPDF_PSProc::~CPDF_PSProc() = default; + + bool CPDF_PSProc::Parse(CPDF_SimpleParser* parser, int depth) { + if (depth > kMaxDepth) + return false; + +- while (1) { ++ while (true) { + ByteStringView word = parser->GetWord(); + if (word.IsEmpty()) + return false; +@@ -128,8 +134,8 @@ bool CPDF_PSProc::Parse(CPDF_SimpleParser* parser, int depth) { + return true; + + if (word == "{") { +- m_Operators.push_back(pdfium::MakeUnique()); +- if (!m_Operators.back()->GetProc()->Parse(parser, depth + 1)) ++ m_Operators.push_back(std::make_unique()); ++ if (!m_Operators.back()->Parse(parser, depth + 1)) + return false; + continue; + } +@@ -154,14 +160,14 @@ bool CPDF_PSProc::Execute(CPDF_PSEngine* pEngine) { + return false; + + if (pEngine->PopInt()) +- m_Operators[i - 1]->GetProc()->Execute(pEngine); ++ m_Operators[i - 1]->Execute(pEngine); + } else if (op == PSOP_IFELSE) { + if (i < 2 || m_Operators[i - 1]->GetOp() != PSOP_PROC || + m_Operators[i - 2]->GetOp() != PSOP_PROC) { + return false; + } + size_t offset = pEngine->PopInt() ? 2 : 1; +- m_Operators[i - offset]->GetProc()->Execute(pEngine); ++ m_Operators[i - offset]->Execute(pEngine); + } else { + pEngine->DoOperator(op); + } +@@ -180,9 +186,9 @@ void CPDF_PSProc::AddOperator(ByteStringView word) { + return name.name < word; + }); + if (pFound != std::end(kPsOpNames) && pFound->name == word) +- m_Operators.push_back(pdfium::MakeUnique(pFound->op)); ++ m_Operators.push_back(std::make_unique(pFound->op)); + else +- m_Operators.push_back(pdfium::MakeUnique(StringToFloat(word))); ++ m_Operators.push_back(std::make_unique(StringToFloat(word))); + } + + CPDF_PSEngine::CPDF_PSEngine() = default; +@@ -232,7 +238,7 @@ bool CPDF_PSEngine::DoOperator(PDF_PSOP op) { + case PSOP_DIV: + d2 = Pop(); + d1 = Pop(); +- Push(d1 / d2); ++ Push(d2 ? d1 / d2 : 0); + break; + case PSOP_IDIV: + i2 = PopInt(); +@@ -286,16 +292,16 @@ bool CPDF_PSEngine::DoOperator(PDF_PSOP op) { + break; + case PSOP_SIN: + d1 = Pop(); +- Push(sin(d1 * FX_PI / 180.0f)); ++ Push(sin(d1 * FXSYS_PI / 180.0f)); + break; + case PSOP_COS: + d1 = Pop(); +- Push(cos(d1 * FX_PI / 180.0f)); ++ Push(cos(d1 * FXSYS_PI / 180.0f)); + break; + case PSOP_ATAN: + d2 = Pop(); + d1 = Pop(); +- d1 = atan2(d1, d2) * 180.0 / FX_PI; ++ d1 = atan2(d1, d2) * 180.0 / FXSYS_PI; + if (d1 < 0) { + d1 += 360; + } +@@ -304,7 +310,7 @@ bool CPDF_PSEngine::DoOperator(PDF_PSOP op) { + case PSOP_EXP: + d2 = Pop(); + d1 = Pop(); +- Push(FXSYS_pow(d1, d2)); ++ Push(powf(d1, d2)); + break; + case PSOP_LN: + d1 = Pop(); +diff --git a/core/fpdfapi/page/cpdf_psengine.h b/core/fpdfapi/page/cpdf_psengine.h +index 3db8db737..d7f6f7e51 100644 +--- a/core/fpdfapi/page/cpdf_psengine.h ++++ b/core/fpdfapi/page/cpdf_psengine.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,13 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_PSENGINE_H_ + #define CORE_FPDFAPI_PAGE_CPDF_PSENGINE_H_ + ++#include ++#include ++ + #include + #include + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/bytestring.h" + #include "third_party/base/span.h" + + class CPDF_PSEngine; +@@ -72,8 +74,9 @@ class CPDF_PSOP { + explicit CPDF_PSOP(float value); + ~CPDF_PSOP(); + ++ bool Parse(CPDF_SimpleParser* parser, int depth); ++ void Execute(CPDF_PSEngine* pEngine); + float GetFloatValue() const; +- CPDF_PSProc* GetProc() const; + PDF_PSOP GetOp() const { return m_op; } + + private: +@@ -98,7 +101,7 @@ class CPDF_PSProc { + } + + private: +- static const int kMaxDepth = 128; ++ static constexpr int kMaxDepth = 128; + + void AddOperator(ByteStringView word); + +@@ -124,7 +127,7 @@ class CPDF_PSEngine { + + uint32_t m_StackCount = 0; + CPDF_PSProc m_MainProc; +- float m_Stack[kPSEngineStackSize]; ++ float m_Stack[kPSEngineStackSize] = {}; + }; + + #endif // CORE_FPDFAPI_PAGE_CPDF_PSENGINE_H_ +diff --git a/core/fpdfapi/page/cpdf_psengine_unittest.cpp b/core/fpdfapi/page/cpdf_psengine_unittest.cpp +index a922de188..ae458b3b2 100644 +--- a/core/fpdfapi/page/cpdf_psengine_unittest.cpp ++++ b/core/fpdfapi/page/cpdf_psengine_unittest.cpp +@@ -1,14 +1,24 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include "core/fpdfapi/page/cpdf_psengine.h" ++ ++#include + #include + +-#include "core/fpdfapi/page/cpdf_psengine.h" + #include "testing/gtest/include/gtest/gtest.h" + + namespace { + ++float DoOperator0(CPDF_PSEngine* engine, PDF_PSOP op) { ++ EXPECT_EQ(0u, engine->GetStackSize()); ++ engine->DoOperator(op); ++ float ret = engine->Pop(); ++ EXPECT_EQ(0u, engine->GetStackSize()); ++ return ret; ++} ++ + float DoOperator1(CPDF_PSEngine* engine, float v1, PDF_PSOP op) { + EXPECT_EQ(0u, engine->GetStackSize()); + engine->Push(v1); +@@ -62,7 +72,7 @@ TEST(CPDF_PSProc, AddOperator) { + + CPDF_PSProc proc; + EXPECT_EQ(0U, proc.num_operators()); +- for (size_t i = 0; i < FX_ArraySize(kTestData); ++i) { ++ for (size_t i = 0; i < std::size(kTestData); ++i) { + ByteStringView word(kTestData[i].name); + proc.AddOperatorForTesting(word); + ASSERT_EQ(i + 1, proc.num_operators()); +@@ -93,6 +103,17 @@ TEST(CPDF_PSEngine, Basic) { + EXPECT_FLOAT_EQ(5.0f, DoOperator1(&engine, -5, PSOP_ABS)); + } + ++TEST(CPDF_PSEngine, DivByZero) { ++ CPDF_PSEngine engine; ++ ++ // Integer divide by zero is defined as resulting in 0. ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 100, 0.0, PSOP_IDIV)); ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 100, 0.0, PSOP_MOD)); ++ ++ // floating divide by zero is defined as resulting in 0. ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 100, 0.0, PSOP_DIV)); ++} ++ + TEST(CPDF_PSEngine, Ceiling) { + CPDF_PSEngine engine; + +@@ -184,10 +205,79 @@ TEST(CPDF_PSEngine, Truncate) { + + // Truncate does not behave according to the PostScript Language Reference for + // values beyond the range of integers. This seems to match Acrobat's +- // behavior. See https://crbug.com/1314. +- float max_int = std::numeric_limits::max(); +- EXPECT_FLOAT_EQ(-max_int, +- DoOperator1(&engine, max_int * 2.0f, PSOP_TRUNCATE)); ++ // behavior. See https://crbug.com/pdfium/1314. ++ float max_int = static_cast(std::numeric_limits::max()); + EXPECT_FLOAT_EQ(-max_int, + DoOperator1(&engine, max_int * -1.5f, PSOP_TRUNCATE)); + } ++ ++TEST(CPDF_PSEngine, Comparisons) { ++ CPDF_PSEngine engine; ++ ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_EQ)); ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_EQ)); ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 255.0f, 1.0f, PSOP_EQ)); ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, -1.0f, 0.0f, PSOP_EQ)); ++ ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_NE)); ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_NE)); ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 255.0f, 1.0f, PSOP_NE)); ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, -1.0f, 0.0f, PSOP_NE)); ++ ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_GT)); ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_GT)); ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 255.0f, 1.0f, PSOP_GT)); ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, -1.0f, 0.0f, PSOP_GT)); ++ ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_GE)); ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_GE)); ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 255.0f, 1.0f, PSOP_GE)); ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, -1.0f, 0.0f, PSOP_GE)); ++ ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_LT)); ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_LT)); ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 255.0f, 1.0f, PSOP_LT)); ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, -1.0f, 0.0f, PSOP_LT)); ++ ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_LE)); ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_LE)); ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 255.0f, 1.0f, PSOP_LE)); ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, -1.0f, 0.0f, PSOP_LE)); ++} ++ ++TEST(CPDF_PSEngine, Logic) { ++ CPDF_PSEngine engine; ++ ++ EXPECT_FLOAT_EQ(1.0f, DoOperator0(&engine, PSOP_TRUE)); ++ EXPECT_FLOAT_EQ(0.0f, DoOperator0(&engine, PSOP_FALSE)); ++ ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_AND)); ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_AND)); ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 1.0f, 0.0f, PSOP_AND)); ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 1.0f, 1.0f, PSOP_AND)); ++ ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_OR)); ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_OR)); ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 1.0f, 0.0f, PSOP_OR)); ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 1.0f, 1.0f, PSOP_OR)); ++ ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 0.0f, 0.0f, PSOP_XOR)); ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 0.0f, 1.0f, PSOP_XOR)); ++ EXPECT_FLOAT_EQ(1.0f, DoOperator2(&engine, 1.0f, 0.0f, PSOP_XOR)); ++ EXPECT_FLOAT_EQ(0.0f, DoOperator2(&engine, 1.0f, 1.0f, PSOP_XOR)); ++ ++ EXPECT_FLOAT_EQ(1.0f, DoOperator1(&engine, 0.0f, PSOP_NOT)); ++ EXPECT_FLOAT_EQ(0.0f, DoOperator1(&engine, 1.0f, PSOP_NOT)); ++} ++ ++TEST(CPDF_PSEngine, MathFunctions) { ++ CPDF_PSEngine engine; ++ ++ EXPECT_FLOAT_EQ(1.4142135f, DoOperator1(&engine, 2.0f, PSOP_SQRT)); ++ EXPECT_FLOAT_EQ(0.8660254f, DoOperator1(&engine, 60.0f, PSOP_SIN)); ++ EXPECT_FLOAT_EQ(0.5f, DoOperator1(&engine, 60.0f, PSOP_COS)); ++ EXPECT_FLOAT_EQ(45.0f, DoOperator2(&engine, 1.0f, 1.0f, PSOP_ATAN)); ++ EXPECT_FLOAT_EQ(1000.0f, DoOperator2(&engine, 10.0f, 3.0f, PSOP_EXP)); ++ EXPECT_FLOAT_EQ(3.0f, DoOperator1(&engine, 1000.0f, PSOP_LOG)); ++ EXPECT_FLOAT_EQ(2.302585f, DoOperator1(&engine, 10.0f, PSOP_LN)); ++} +diff --git a/core/fpdfapi/page/cpdf_psfunc.cpp b/core/fpdfapi/page/cpdf_psfunc.cpp +index ffc5053c1..60ec52bc0 100644 +--- a/core/fpdfapi/page/cpdf_psfunc.cpp ++++ b/core/fpdfapi/page/cpdf_psfunc.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,16 +11,17 @@ + + CPDF_PSFunc::CPDF_PSFunc() : CPDF_Function(Type::kType4PostScript) {} + +-CPDF_PSFunc::~CPDF_PSFunc() {} ++CPDF_PSFunc::~CPDF_PSFunc() = default; + +-bool CPDF_PSFunc::v_Init(const CPDF_Object* pObj, +- std::set* pVisited) { +- auto pAcc = pdfium::MakeRetain(pObj->AsStream()); ++bool CPDF_PSFunc::v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) { ++ auto pAcc = ++ pdfium::MakeRetain(pdfium::WrapRetain(pObj->AsStream())); + pAcc->LoadAllDataFiltered(); + return m_PS.Parse(pAcc->GetSpan()); + } + +-bool CPDF_PSFunc::v_Call(const float* inputs, float* results) const { ++bool CPDF_PSFunc::v_Call(pdfium::span inputs, ++ pdfium::span results) const { + m_PS.Reset(); + for (uint32_t i = 0; i < m_nInputs; i++) + m_PS.Push(inputs[i]); +diff --git a/core/fpdfapi/page/cpdf_psfunc.h b/core/fpdfapi/page/cpdf_psfunc.h +index b81c2e7ac..fd7a7d8a0 100644 +--- a/core/fpdfapi/page/cpdf_psfunc.h ++++ b/core/fpdfapi/page/cpdf_psfunc.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,8 +7,6 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_PSFUNC_H_ + #define CORE_FPDFAPI_PAGE_CPDF_PSFUNC_H_ + +-#include +- + #include "core/fpdfapi/page/cpdf_function.h" + #include "core/fpdfapi/page/cpdf_psengine.h" + +@@ -19,10 +17,10 @@ class CPDF_PSFunc final : public CPDF_Function { + CPDF_PSFunc(); + ~CPDF_PSFunc() override; + +- // CPDF_Function +- bool v_Init(const CPDF_Object* pObj, +- std::set* pVisited) override; +- bool v_Call(const float* inputs, float* results) const override; ++ // CPDF_Function: ++ bool v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) override; ++ bool v_Call(pdfium::span inputs, ++ pdfium::span results) const override; + + private: + mutable CPDF_PSEngine m_PS; // Pre-initialized scratch space for v_Call(). +diff --git a/core/fpdfapi/page/cpdf_sampledfunc.cpp b/core/fpdfapi/page/cpdf_sampledfunc.cpp +index c80e16c98..0aa28f4f0 100644 +--- a/core/fpdfapi/page/cpdf_sampledfunc.cpp ++++ b/core/fpdfapi/page/cpdf_sampledfunc.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,14 +6,16 @@ + + #include "core/fpdfapi/page/cpdf_sampledfunc.h" + ++#include ++ + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" + #include "core/fxcrt/cfx_bitstream.h" +-#include "core/fxcrt/cfx_fixedbufgrow.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxcrt/small_buffer.h" ++#include "third_party/base/cxx17_backports.h" + + namespace { + +@@ -38,16 +40,15 @@ bool IsValidBitsPerSample(uint32_t x) { + + CPDF_SampledFunc::CPDF_SampledFunc() : CPDF_Function(Type::kType0Sampled) {} + +-CPDF_SampledFunc::~CPDF_SampledFunc() {} ++CPDF_SampledFunc::~CPDF_SampledFunc() = default; + +-bool CPDF_SampledFunc::v_Init(const CPDF_Object* pObj, +- std::set* pVisited) { +- const CPDF_Stream* pStream = pObj->AsStream(); ++bool CPDF_SampledFunc::v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) { ++ RetainPtr pStream(pObj->AsStream()); + if (!pStream) + return false; + +- const CPDF_Dictionary* pDict = pStream->GetDict(); +- const CPDF_Array* pSize = pDict->GetArrayFor("Size"); ++ RetainPtr pDict = pStream->GetDict(); ++ RetainPtr pSize = pDict->GetArrayFor("Size"); + if (!pSize || pSize->IsEmpty()) + return false; + +@@ -57,7 +58,7 @@ bool CPDF_SampledFunc::v_Init(const CPDF_Object* pObj, + + FX_SAFE_UINT32 nTotalSampleBits = m_nBitsPerSample; + nTotalSampleBits *= m_nOutputs; +- const CPDF_Array* pEncode = pDict->GetArrayFor("Encode"); ++ RetainPtr pEncode = pDict->GetArrayFor("Encode"); + m_EncodeInfo.resize(m_nInputs); + for (uint32_t i = 0; i < m_nInputs; i++) { + int size = pSize->GetIntegerAt(i); +@@ -67,8 +68,8 @@ bool CPDF_SampledFunc::v_Init(const CPDF_Object* pObj, + m_EncodeInfo[i].sizes = size; + nTotalSampleBits *= m_EncodeInfo[i].sizes; + if (pEncode) { +- m_EncodeInfo[i].encode_min = pEncode->GetNumberAt(i * 2); +- m_EncodeInfo[i].encode_max = pEncode->GetNumberAt(i * 2 + 1); ++ m_EncodeInfo[i].encode_min = pEncode->GetFloatAt(i * 2); ++ m_EncodeInfo[i].encode_max = pEncode->GetFloatAt(i * 2 + 1); + } else { + m_EncodeInfo[i].encode_min = 0; + m_EncodeInfo[i].encode_max = +@@ -80,17 +81,17 @@ bool CPDF_SampledFunc::v_Init(const CPDF_Object* pObj, + return false; + + m_SampleMax = 0xffffffff >> (32 - m_nBitsPerSample); +- m_pSampleStream = pdfium::MakeRetain(pStream); ++ m_pSampleStream = pdfium::MakeRetain(std::move(pStream)); + m_pSampleStream->LoadAllDataFiltered(); + if (nTotalSampleBytes.ValueOrDie() > m_pSampleStream->GetSize()) + return false; + +- const CPDF_Array* pDecode = pDict->GetArrayFor("Decode"); ++ RetainPtr pDecode = pDict->GetArrayFor("Decode"); + m_DecodeInfo.resize(m_nOutputs); + for (uint32_t i = 0; i < m_nOutputs; i++) { + if (pDecode) { +- m_DecodeInfo[i].decode_min = pDecode->GetNumberAt(2 * i); +- m_DecodeInfo[i].decode_max = pDecode->GetNumberAt(2 * i + 1); ++ m_DecodeInfo[i].decode_min = pDecode->GetFloatAt(2 * i); ++ m_DecodeInfo[i].decode_max = pDecode->GetFloatAt(2 * i + 1); + } else { + m_DecodeInfo[i].decode_min = m_Ranges[i * 2]; + m_DecodeInfo[i].decode_max = m_Ranges[i * 2 + 1]; +@@ -99,12 +100,13 @@ bool CPDF_SampledFunc::v_Init(const CPDF_Object* pObj, + return true; + } + +-bool CPDF_SampledFunc::v_Call(const float* inputs, float* results) const { ++bool CPDF_SampledFunc::v_Call(pdfium::span inputs, ++ pdfium::span results) const { + int pos = 0; +- CFX_FixedBufGrow encoded_input_buf(m_nInputs); +- float* encoded_input = encoded_input_buf; +- CFX_FixedBufGrow int_buf(m_nInputs * 2); +- uint32_t* index = int_buf; ++ fxcrt::SmallBuffer encoded_input_buf(m_nInputs); ++ fxcrt::SmallBuffer int_buf(m_nInputs * 2); ++ float* encoded_input = encoded_input_buf.data(); ++ uint32_t* index = int_buf.data(); + uint32_t* blocksize = index + m_nInputs; + for (uint32_t i = 0; i < m_nInputs; i++) { + if (i == 0) +@@ -174,7 +176,7 @@ bool CPDF_SampledFunc::v_Call(const float* inputs, float* results) const { + return true; + } + +-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ ++#if defined(_SKIA_SUPPORT_) + RetainPtr CPDF_SampledFunc::GetSampleStream() const { + return m_pSampleStream; + } +diff --git a/core/fpdfapi/page/cpdf_sampledfunc.h b/core/fpdfapi/page/cpdf_sampledfunc.h +index 520b692dd..93c81df5e 100644 +--- a/core/fpdfapi/page/cpdf_sampledfunc.h ++++ b/core/fpdfapi/page/cpdf_sampledfunc.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,6 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_SAMPLEDFUNC_H_ + #define CORE_FPDFAPI_PAGE_CPDF_SAMPLEDFUNC_H_ + +-#include + #include + + #include "core/fpdfapi/page/cpdf_function.h" +@@ -31,25 +30,25 @@ class CPDF_SampledFunc final : public CPDF_Function { + CPDF_SampledFunc(); + ~CPDF_SampledFunc() override; + +- // CPDF_Function +- bool v_Init(const CPDF_Object* pObj, +- std::set* pVisited) override; +- bool v_Call(const float* inputs, float* results) const override; ++ // CPDF_Function: ++ bool v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) override; ++ bool v_Call(pdfium::span inputs, ++ pdfium::span results) const override; + + const std::vector& GetEncodeInfo() const { + return m_EncodeInfo; + } + uint32_t GetBitsPerSample() const { return m_nBitsPerSample; } + +-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ ++#if defined(_SKIA_SUPPORT_) + RetainPtr GetSampleStream() const; + #endif + + private: + std::vector m_EncodeInfo; + std::vector m_DecodeInfo; +- uint32_t m_nBitsPerSample; +- uint32_t m_SampleMax; ++ uint32_t m_nBitsPerSample = 0; ++ uint32_t m_SampleMax = 0; + RetainPtr m_pSampleStream; + }; + +diff --git a/core/fpdfapi/page/cpdf_shadingobject.cpp b/core/fpdfapi/page/cpdf_shadingobject.cpp +index bdaceaa7e..06d43aac5 100644 +--- a/core/fpdfapi/page/cpdf_shadingobject.cpp ++++ b/core/fpdfapi/page/cpdf_shadingobject.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,18 +6,21 @@ + + #include "core/fpdfapi/page/cpdf_shadingobject.h" + ++#include ++ + #include "core/fpdfapi/page/cpdf_shadingpattern.h" +-#include "core/fpdfapi/parser/cpdf_document.h" + + CPDF_ShadingObject::CPDF_ShadingObject(int32_t content_stream, +- CPDF_ShadingPattern* pattern, ++ RetainPtr pattern, + const CFX_Matrix& matrix) +- : CPDF_PageObject(content_stream), m_pShading(pattern), m_Matrix(matrix) {} ++ : CPDF_PageObject(content_stream), ++ m_pShading(std::move(pattern)), ++ m_Matrix(matrix) {} + +-CPDF_ShadingObject::~CPDF_ShadingObject() {} ++CPDF_ShadingObject::~CPDF_ShadingObject() = default; + + CPDF_PageObject::Type CPDF_ShadingObject::GetType() const { +- return SHADING; ++ return Type::kShading; + } + + void CPDF_ShadingObject::Transform(const CFX_Matrix& matrix) { +@@ -25,12 +28,11 @@ void CPDF_ShadingObject::Transform(const CFX_Matrix& matrix) { + m_ClipPath.Transform(matrix); + + m_Matrix.Concat(matrix); +- if (m_ClipPath.HasRef()) { ++ if (m_ClipPath.HasRef()) + CalcBoundingBox(); +- return; +- } +- +- SetRect(matrix.TransformRect(GetRect())); ++ else ++ SetRect(matrix.TransformRect(GetRect())); ++ SetDirty(true); + } + + bool CPDF_ShadingObject::IsShading() const { +diff --git a/core/fpdfapi/page/cpdf_shadingobject.h b/core/fpdfapi/page/cpdf_shadingobject.h +index 072a025dd..78aba1fd0 100644 +--- a/core/fpdfapi/page/cpdf_shadingobject.h ++++ b/core/fpdfapi/page/cpdf_shadingobject.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,7 +16,7 @@ class CPDF_ShadingPattern; + class CPDF_ShadingObject final : public CPDF_PageObject { + public: + CPDF_ShadingObject(int32_t content_stream, +- CPDF_ShadingPattern* pattern, ++ RetainPtr pattern, + const CFX_Matrix& matrix); + ~CPDF_ShadingObject() override; + +diff --git a/core/fpdfapi/page/cpdf_shadingpattern.cpp b/core/fpdfapi/page/cpdf_shadingpattern.cpp +index 69696ac20..9d26ba9a9 100644 +--- a/core/fpdfapi/page/cpdf_shadingpattern.cpp ++++ b/core/fpdfapi/page/cpdf_shadingpattern.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,7 @@ + #include "core/fpdfapi/page/cpdf_shadingpattern.h" + + #include ++#include + + #include "core/fpdfapi/page/cpdf_docpagedata.h" + #include "core/fpdfapi/page/cpdf_function.h" +@@ -16,6 +17,8 @@ + #include "core/fpdfapi/parser/cpdf_object.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fxcrt/fx_safe_types.h" ++#include "third_party/base/check.h" ++#include "third_party/base/notreached.h" + + namespace { + +@@ -28,11 +31,12 @@ ShadingType ToShadingType(int type) { + } // namespace + + CPDF_ShadingPattern::CPDF_ShadingPattern(CPDF_Document* pDoc, +- CPDF_Object* pPatternObj, ++ RetainPtr pPatternObj, + bool bShading, + const CFX_Matrix& parentMatrix) +- : CPDF_Pattern(pDoc, pPatternObj, parentMatrix), m_bShading(bShading) { +- ASSERT(document()); ++ : CPDF_Pattern(pDoc, std::move(pPatternObj), parentMatrix), ++ m_bShading(bShading) { ++ DCHECK(document()); + if (!bShading) + SetPatternToFormMatrix(); + } +@@ -47,40 +51,43 @@ bool CPDF_ShadingPattern::Load() { + if (m_ShadingType != kInvalidShading) + return true; + +- const CPDF_Object* pShadingObj = GetShadingObject(); +- const CPDF_Dictionary* pShadingDict = ++ RetainPtr pShadingObj = GetShadingObject(); ++ RetainPtr pShadingDict = + pShadingObj ? pShadingObj->GetDict() : nullptr; + if (!pShadingDict) + return false; + + m_pFunctions.clear(); +- const CPDF_Object* pFunc = pShadingDict->GetDirectObjectFor("Function"); ++ RetainPtr pFunc = ++ pShadingDict->GetDirectObjectFor("Function"); + if (pFunc) { + if (const CPDF_Array* pArray = pFunc->AsArray()) { + m_pFunctions.resize(std::min(pArray->size(), 4)); +- for (size_t i = 0; i < m_pFunctions.size(); ++i) ++ for (size_t i = 0; i < m_pFunctions.size(); ++i) { + m_pFunctions[i] = CPDF_Function::Load(pArray->GetDirectObjectAt(i)); ++ } + } else { +- m_pFunctions.push_back(CPDF_Function::Load(pFunc)); ++ m_pFunctions.push_back(CPDF_Function::Load(std::move(pFunc))); + } + } +- const CPDF_Object* pCSObj = pShadingDict->GetDirectObjectFor("ColorSpace"); ++ RetainPtr pCSObj = ++ pShadingDict->GetDirectObjectFor("ColorSpace"); + if (!pCSObj) + return false; + + auto* pDocPageData = CPDF_DocPageData::FromDocument(document()); +- m_pCS = pDocPageData->GetColorSpace(pCSObj, nullptr); ++ m_pCS = pDocPageData->GetColorSpace(pCSObj.Get(), nullptr); + + // The color space is required and cannot be a Pattern space, according to the + // PDF 1.7 spec, page 305. +- if (!m_pCS || m_pCS->GetFamily() == PDFCS_PATTERN) ++ if (!m_pCS || m_pCS->GetFamily() == CPDF_ColorSpace::Family::kPattern) + return false; + + m_ShadingType = ToShadingType(pShadingDict->GetIntegerFor("ShadingType")); + return Validate(); + } + +-const CPDF_Object* CPDF_ShadingPattern::GetShadingObject() const { ++RetainPtr CPDF_ShadingPattern::GetShadingObject() const { + return m_bShading ? pattern_obj() + : pattern_obj()->GetDict()->GetDirectObjectFor("Shading"); + } +@@ -98,7 +105,7 @@ bool CPDF_ShadingPattern::Validate() const { + case kFunctionBasedShading: + case kAxialShading: + case kRadialShading: { +- if (m_pCS->GetFamily() == PDFCS_INDEXED) ++ if (m_pCS->GetFamily() == CPDF_ColorSpace::Family::kIndexed) + return false; + break; + } +@@ -106,13 +113,14 @@ bool CPDF_ShadingPattern::Validate() const { + case kLatticeFormGouraudTriangleMeshShading: + case kCoonsPatchMeshShading: + case kTensorProductPatchMeshShading: { +- if (!m_pFunctions.empty() && m_pCS->GetFamily() == PDFCS_INDEXED) ++ if (!m_pFunctions.empty() && ++ m_pCS->GetFamily() == CPDF_ColorSpace::Family::kIndexed) { + return false; ++ } + break; + } + default: { +- NOTREACHED(); +- return false; ++ NOTREACHED_NORETURN(); + } + } + +@@ -139,10 +147,8 @@ bool CPDF_ShadingPattern::Validate() const { + ValidateFunctions(nNumColorSpaceComponents, 1, 1); + } + default: +- break; ++ NOTREACHED_NORETURN(); + } +- NOTREACHED(); +- return false; + } + + bool CPDF_ShadingPattern::ValidateFunctions( +diff --git a/core/fpdfapi/page/cpdf_shadingpattern.h b/core/fpdfapi/page/cpdf_shadingpattern.h +index 392aa2741..1e2672067 100644 +--- a/core/fpdfapi/page/cpdf_shadingpattern.h ++++ b/core/fpdfapi/page/cpdf_shadingpattern.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,14 +7,14 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_SHADINGPATTERN_H_ + #define CORE_FPDFAPI_PAGE_CPDF_SHADINGPATTERN_H_ + ++#include ++ + #include + #include + + #include "core/fpdfapi/page/cpdf_colorspace.h" + #include "core/fpdfapi/page/cpdf_pattern.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" +-#include "core/fxcrt/unowned_ptr.h" + + // Values used in PDFs except for |kInvalidShading| and |kMaxShading|. + // Do not change. +@@ -38,9 +38,7 @@ class CPDF_Object; + + class CPDF_ShadingPattern final : public CPDF_Pattern { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- ++ CONSTRUCT_VIA_MAKE_RETAIN; + ~CPDF_ShadingPattern() override; + + // CPDF_Pattern: +@@ -56,7 +54,7 @@ class CPDF_ShadingPattern final : public CPDF_Pattern { + + ShadingType GetShadingType() const { return m_ShadingType; } + bool IsShadingObject() const { return m_bShading; } +- const CPDF_Object* GetShadingObject() const; ++ RetainPtr GetShadingObject() const; + RetainPtr GetCS() const { return m_pCS; } + const std::vector>& GetFuncs() const { + return m_pFunctions; +@@ -64,7 +62,7 @@ class CPDF_ShadingPattern final : public CPDF_Pattern { + + private: + CPDF_ShadingPattern(CPDF_Document* pDoc, +- CPDF_Object* pPatternObj, ++ RetainPtr pPatternObj, + bool bShading, + const CFX_Matrix& parentMatrix); + CPDF_ShadingPattern(const CPDF_ShadingPattern&) = delete; +diff --git a/core/fpdfapi/page/cpdf_stitchfunc.cpp b/core/fpdfapi/page/cpdf_stitchfunc.cpp +index 1ec48ac9a..f522bc347 100644 +--- a/core/fpdfapi/page/cpdf_stitchfunc.cpp ++++ b/core/fpdfapi/page/cpdf_stitchfunc.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,6 +12,7 @@ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/stl_util.h" + + namespace { + +@@ -21,30 +22,29 @@ constexpr uint32_t kRequiredNumInputs = 1; + + CPDF_StitchFunc::CPDF_StitchFunc() : CPDF_Function(Type::kType3Stitching) {} + +-CPDF_StitchFunc::~CPDF_StitchFunc() {} ++CPDF_StitchFunc::~CPDF_StitchFunc() = default; + +-bool CPDF_StitchFunc::v_Init(const CPDF_Object* pObj, +- std::set* pVisited) { ++bool CPDF_StitchFunc::v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) { + if (m_nInputs != kRequiredNumInputs) + return false; + +- const CPDF_Dictionary* pDict = pObj->GetDict(); ++ RetainPtr pDict = pObj->GetDict(); + if (!pDict) + return false; + +- const CPDF_Array* pFunctionsArray = pDict->GetArrayFor("Functions"); ++ RetainPtr pFunctionsArray = pDict->GetArrayFor("Functions"); + if (!pFunctionsArray) + return false; + +- const CPDF_Array* pBoundsArray = pDict->GetArrayFor("Bounds"); ++ RetainPtr pBoundsArray = pDict->GetArrayFor("Bounds"); + if (!pBoundsArray) + return false; + +- const CPDF_Array* pEncodeArray = pDict->GetArrayFor("Encode"); ++ RetainPtr pEncodeArray = pDict->GetArrayFor("Encode"); + if (!pEncodeArray) + return false; + +- const uint32_t nSubs = pFunctionsArray->size(); ++ const uint32_t nSubs = fxcrt::CollectionSize(*pFunctionsArray); + if (nSubs == 0) + return false; + +@@ -65,13 +65,14 @@ bool CPDF_StitchFunc::v_Init(const CPDF_Object* pObj, + + // Check sub-functions. + { +- Optional nOutputs; ++ absl::optional nOutputs; + for (uint32_t i = 0; i < nSubs; ++i) { +- const CPDF_Object* pSub = pFunctionsArray->GetDirectObjectAt(i); ++ RetainPtr pSub = pFunctionsArray->GetDirectObjectAt(i); + if (pSub == pObj) + return false; + +- std::unique_ptr pFunc(CPDF_Function::Load(pSub, pVisited)); ++ std::unique_ptr pFunc = ++ CPDF_Function::Load(std::move(pSub), pVisited); + if (!pFunc) + return false; + +@@ -84,29 +85,29 @@ bool CPDF_StitchFunc::v_Init(const CPDF_Object* pObj, + if (nFuncOutputs == 0) + return false; + +- if (nOutputs) { +- if (nFuncOutputs != *nOutputs) ++ if (nOutputs.has_value()) { ++ if (nOutputs != nFuncOutputs) + return false; + } else { + nOutputs = nFuncOutputs; + } +- + m_pSubFunctions.push_back(std::move(pFunc)); + } +- m_nOutputs = *nOutputs; ++ m_nOutputs = nOutputs.value(); + } + + m_bounds.reserve(nSubs + 1); + m_bounds.push_back(m_Domains[0]); + for (uint32_t i = 0; i < nSubs - 1; i++) +- m_bounds.push_back(pBoundsArray->GetNumberAt(i)); ++ m_bounds.push_back(pBoundsArray->GetFloatAt(i)); + m_bounds.push_back(m_Domains[1]); + +- m_encode = ReadArrayElementsToVector(pEncodeArray, nSubs * 2); ++ m_encode = ReadArrayElementsToVector(pEncodeArray.Get(), nSubs * 2); + return true; + } + +-bool CPDF_StitchFunc::v_Call(const float* inputs, float* results) const { ++bool CPDF_StitchFunc::v_Call(pdfium::span inputs, ++ pdfium::span results) const { + float input = inputs[0]; + size_t i; + for (i = 0; i < m_pSubFunctions.size() - 1; i++) { +@@ -115,7 +116,7 @@ bool CPDF_StitchFunc::v_Call(const float* inputs, float* results) const { + } + input = Interpolate(input, m_bounds[i], m_bounds[i + 1], m_encode[i * 2], + m_encode[i * 2 + 1]); +- int nresults; +- return m_pSubFunctions[i]->Call(&input, kRequiredNumInputs, results, +- &nresults); ++ return m_pSubFunctions[i] ++ ->Call(pdfium::make_span(&input, 1), results) ++ .has_value(); + } +diff --git a/core/fpdfapi/page/cpdf_stitchfunc.h b/core/fpdfapi/page/cpdf_stitchfunc.h +index 761c9ba4c..e3490e9d9 100644 +--- a/core/fpdfapi/page/cpdf_stitchfunc.h ++++ b/core/fpdfapi/page/cpdf_stitchfunc.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,7 +8,6 @@ + #define CORE_FPDFAPI_PAGE_CPDF_STITCHFUNC_H_ + + #include +-#include + #include + + #include "core/fpdfapi/page/cpdf_function.h" +@@ -18,10 +17,10 @@ class CPDF_StitchFunc final : public CPDF_Function { + CPDF_StitchFunc(); + ~CPDF_StitchFunc() override; + +- // CPDF_Function +- bool v_Init(const CPDF_Object* pObj, +- std::set* pVisited) override; +- bool v_Call(const float* inputs, float* results) const override; ++ // CPDF_Function: ++ bool v_Init(const CPDF_Object* pObj, VisitedSet* pVisited) override; ++ bool v_Call(pdfium::span inputs, ++ pdfium::span results) const override; + + const std::vector>& GetSubFunctions() const { + return m_pSubFunctions; +diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.cpp b/core/fpdfapi/page/cpdf_streamcontentparser.cpp +index a18bead85..0522cf1d8 100644 +--- a/core/fpdfapi/page/cpdf_streamcontentparser.cpp ++++ b/core/fpdfapi/page/cpdf_streamcontentparser.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -34,17 +34,22 @@ + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" ++#include "core/fxcrt/autonuller.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/scoped_set_insertion.h" ++#include "core/fxcrt/stl_util.h" ++#include "core/fxge/cfx_graphstate.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/render_defines.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/no_destructor.h" ++#include "third_party/base/notreached.h" + #include "third_party/base/span.h" +-#include "third_party/base/stl_util.h" + + namespace { + +-const int kMaxFormLevel = 30; ++const int kMaxFormLevel = 40; + + const int kSingleCoordinatePair = 1; + const int kTensorCoordinatePairs = 16; +@@ -60,47 +65,39 @@ const char kPathOperatorCubicBezier3 = 'y'; + const char kPathOperatorClosePath = 'h'; + const char kPathOperatorRectangle[] = "re"; + +-class CPDF_StreamParserAutoClearer { +- public: +- CPDF_StreamParserAutoClearer(UnownedPtr* scoped_variable, +- CPDF_StreamParser* new_parser) +- : scoped_variable_(scoped_variable) { +- *scoped_variable_ = new_parser; +- } +- ~CPDF_StreamParserAutoClearer() { *scoped_variable_ = nullptr; } +- +- private: +- UnownedPtr* scoped_variable_; +-}; +- + CFX_FloatRect GetShadingBBox(CPDF_ShadingPattern* pShading, + const CFX_Matrix& matrix) { + ShadingType type = pShading->GetShadingType(); +- const CPDF_Stream* pStream = ToStream(pShading->GetShadingObject()); ++ RetainPtr pStream = ToStream(pShading->GetShadingObject()); + RetainPtr pCS = pShading->GetCS(); + if (!pStream || !pCS) + return CFX_FloatRect(); + +- CPDF_MeshStream stream(type, pShading->GetFuncs(), pStream, pCS); ++ CPDF_MeshStream stream(type, pShading->GetFuncs(), std::move(pStream), ++ std::move(pCS)); + if (!stream.Load()) + return CFX_FloatRect(); + + CFX_FloatRect rect; +- bool bStarted = false; ++ bool update_rect = false; + bool bGouraud = type == kFreeFormGouraudTriangleMeshShading || + type == kLatticeFormGouraudTriangleMeshShading; + +- int point_count = kSingleCoordinatePair; ++ int point_count; + if (type == kTensorProductPatchMeshShading) + point_count = kTensorCoordinatePairs; + else if (type == kCoonsPatchMeshShading) + point_count = kCoonsCoordinatePairs; ++ else ++ point_count = kSingleCoordinatePair; + +- int color_count = kSingleColorPerPatch; ++ int color_count; + if (type == kCoonsPatchMeshShading || type == kTensorProductPatchMeshShading) + color_count = kQuadColorsPerPatch; ++ else ++ color_count = kSingleColorPerPatch; + +- while (!stream.BitStream()->IsEOF()) { ++ while (!stream.IsEOF()) { + uint32_t flag = 0; + if (type != kLatticeFormGouraudTriangleMeshShading) { + if (!stream.CanReadFlag()) +@@ -113,15 +110,16 @@ CFX_FloatRect GetShadingBBox(CPDF_ShadingPattern* pShading, + color_count -= 2; + } + +- for (int i = 0; i < point_count; i++) { ++ for (int i = 0; i < point_count; ++i) { + if (!stream.CanReadCoords()) + break; ++ + CFX_PointF origin = stream.ReadCoords(); +- if (bStarted) { ++ if (update_rect) { + rect.UpdateRect(origin); + } else { +- rect.InitRect(origin); +- bStarted = true; ++ rect = CFX_FloatRect(origin); ++ update_rect = true; + } + } + FX_SAFE_UINT32 nBits = stream.Components(); +@@ -130,9 +128,9 @@ CFX_FloatRect GetShadingBBox(CPDF_ShadingPattern* pShading, + if (!nBits.IsValid()) + break; + +- stream.BitStream()->SkipBits(nBits.ValueOrDie()); ++ stream.SkipBits(nBits.ValueOrDie()); + if (bGouraud) +- stream.BitStream()->ByteAlign(); ++ stream.ByteAlign(); + } + return matrix.TransformRect(rect); + } +@@ -163,16 +161,16 @@ struct AbbrReplacementOp { + ByteStringView replacement; + }; + +-ByteStringView FindFullName(const AbbrPair* table, +- size_t count, ++ByteStringView FindFullName(pdfium::span table, + ByteStringView abbr) { +- auto* it = std::find_if(table, table + count, [abbr](const AbbrPair& pair) { +- return pair.abbr == abbr; +- }); +- return it != table + count ? ByteStringView(it->full_name) : ByteStringView(); ++ for (const auto& pair : table) { ++ if (pair.abbr == abbr) ++ return ByteStringView(pair.full_name); ++ } ++ return ByteStringView(); + } + +-void ReplaceAbbr(CPDF_Object* pObj); ++void ReplaceAbbr(RetainPtr pObj); + + void ReplaceAbbrInDictionary(CPDF_Dictionary* pDict) { + std::vector replacements; +@@ -180,9 +178,8 @@ void ReplaceAbbrInDictionary(CPDF_Dictionary* pDict) { + CPDF_DictionaryLocker locker(pDict); + for (const auto& it : locker) { + ByteString key = it.first; +- CPDF_Object* value = it.second.Get(); +- ByteStringView fullname = FindFullName( +- kInlineKeyAbbr, FX_ArraySize(kInlineKeyAbbr), key.AsStringView()); ++ ByteStringView fullname = ++ FindFullName(kInlineKeyAbbr, key.AsStringView()); + if (!fullname.IsEmpty()) { + AbbrReplacementOp op; + op.is_replace_key = true; +@@ -191,12 +188,10 @@ void ReplaceAbbrInDictionary(CPDF_Dictionary* pDict) { + replacements.push_back(op); + key = fullname; + } +- ++ RetainPtr value = it.second; + if (value->IsName()) { + ByteString name = value->GetString(); +- fullname = +- FindFullName(kInlineValueAbbr, FX_ArraySize(kInlineValueAbbr), +- name.AsStringView()); ++ fullname = FindFullName(kInlineValueAbbr, name.AsStringView()); + if (!fullname.IsEmpty()) { + AbbrReplacementOp op; + op.is_replace_key = false; +@@ -205,7 +200,7 @@ void ReplaceAbbrInDictionary(CPDF_Dictionary* pDict) { + replacements.push_back(op); + } + } else { +- ReplaceAbbr(value); ++ ReplaceAbbr(std::move(value)); + } + } + } +@@ -219,28 +214,27 @@ void ReplaceAbbrInDictionary(CPDF_Dictionary* pDict) { + + void ReplaceAbbrInArray(CPDF_Array* pArray) { + for (size_t i = 0; i < pArray->size(); ++i) { +- CPDF_Object* pElement = pArray->GetObjectAt(i); ++ RetainPtr pElement = pArray->GetMutableObjectAt(i); + if (pElement->IsName()) { + ByteString name = pElement->GetString(); + ByteStringView fullname = +- FindFullName(kInlineValueAbbr, FX_ArraySize(kInlineValueAbbr), +- name.AsStringView()); ++ FindFullName(kInlineValueAbbr, name.AsStringView()); + if (!fullname.IsEmpty()) + pArray->SetNewAt(i, ByteString(fullname)); + } else { +- ReplaceAbbr(pElement); ++ ReplaceAbbr(std::move(pElement)); + } + } + } + +-void ReplaceAbbr(CPDF_Object* pObj) { +- CPDF_Dictionary* pDict = pObj->AsDictionary(); ++void ReplaceAbbr(RetainPtr pObj) { ++ CPDF_Dictionary* pDict = pObj->AsMutableDictionary(); + if (pDict) { + ReplaceAbbrInDictionary(pDict); + return; + } + +- CPDF_Array* pArray = pObj->AsArray(); ++ CPDF_Array* pArray = pObj->AsMutableArray(); + if (pArray) + ReplaceAbbrInArray(pArray); + } +@@ -249,24 +243,24 @@ void ReplaceAbbr(CPDF_Object* pObj) { + + CPDF_StreamContentParser::CPDF_StreamContentParser( + CPDF_Document* pDocument, +- CPDF_Dictionary* pPageResources, +- CPDF_Dictionary* pParentResources, ++ RetainPtr pPageResources, ++ RetainPtr pParentResources, + const CFX_Matrix* pmtContentToUser, + CPDF_PageObjectHolder* pObjHolder, +- CPDF_Dictionary* pResources, ++ RetainPtr pResources, + const CFX_FloatRect& rcBBox, + const CPDF_AllStates* pStates, + std::set* pParsedSet) + : m_pDocument(pDocument), + m_pPageResources(pPageResources), + m_pParentResources(pParentResources), +- m_pResources(CPDF_Form::ChooseResourcesDict(pResources, +- pParentResources, +- pPageResources)), ++ m_pResources(CPDF_Form::ChooseResourcesDict(pResources.Get(), ++ pParentResources.Get(), ++ pPageResources.Get())), + m_pObjectHolder(pObjHolder), + m_ParsedSet(pParsedSet), + m_BBox(rcBBox), +- m_pCurStates(pdfium::MakeUnique()) { ++ m_pCurStates(std::make_unique()) { + if (pmtContentToUser) + m_mtContentToUser = *pmtContentToUser; + if (pStates) { +@@ -279,7 +273,7 @@ CPDF_StreamContentParser::CPDF_StreamContentParser( + } + + // Add the sentinel. +- m_ContentMarksStack.push(pdfium::MakeUnique()); ++ m_ContentMarksStack.push(std::make_unique()); + } + + CPDF_StreamContentParser::~CPDF_StreamContentParser() { +@@ -292,7 +286,7 @@ int CPDF_StreamContentParser::GetNextParamPos() { + if (m_ParamStartPos == kParamBufSize) { + m_ParamStartPos = 0; + } +- if (m_ParamBuf[m_ParamStartPos].m_Type == ContentParam::OBJECT) ++ if (m_ParamBuf[m_ParamStartPos].m_Type == ContentParam::Type::kObject) + m_ParamBuf[m_ParamStartPos].m_pObject.Reset(); + + return m_ParamStartPos; +@@ -307,27 +301,26 @@ int CPDF_StreamContentParser::GetNextParamPos() { + + void CPDF_StreamContentParser::AddNameParam(ByteStringView bsName) { + ContentParam& param = m_ParamBuf[GetNextParamPos()]; +- param.m_Type = ContentParam::NAME; +- param.m_Name = +- bsName.Contains('#') ? PDF_NameDecode(bsName) : ByteString(bsName); ++ param.m_Type = ContentParam::Type::kName; ++ param.m_Name = PDF_NameDecode(bsName); + } + + void CPDF_StreamContentParser::AddNumberParam(ByteStringView str) { + ContentParam& param = m_ParamBuf[GetNextParamPos()]; +- param.m_Type = ContentParam::NUMBER; ++ param.m_Type = ContentParam::Type::kNumber; + param.m_Number = FX_Number(str); + } + + void CPDF_StreamContentParser::AddObjectParam(RetainPtr pObj) { + ContentParam& param = m_ParamBuf[GetNextParamPos()]; +- param.m_Type = ContentParam::OBJECT; ++ param.m_Type = ContentParam::Type::kObject; + param.m_pObject = std::move(pObj); + } + + void CPDF_StreamContentParser::ClearAllParams() { + uint32_t index = m_ParamStartPos; + for (uint32_t i = 0; i < m_ParamCount; i++) { +- if (m_ParamBuf[index].m_Type == ContentParam::OBJECT) ++ if (m_ParamBuf[index].m_Type == ContentParam::Type::kObject) + m_ParamBuf[index].m_pObject.Reset(); + index++; + if (index == kParamBufSize) +@@ -337,7 +330,7 @@ void CPDF_StreamContentParser::ClearAllParams() { + m_ParamCount = 0; + } + +-CPDF_Object* CPDF_StreamContentParser::GetObject(uint32_t index) { ++RetainPtr CPDF_StreamContentParser::GetObject(uint32_t index) { + if (index >= m_ParamCount) { + return nullptr; + } +@@ -346,24 +339,23 @@ CPDF_Object* CPDF_StreamContentParser::GetObject(uint32_t index) { + real_index -= kParamBufSize; + } + ContentParam& param = m_ParamBuf[real_index]; +- if (param.m_Type == ContentParam::NUMBER) { +- param.m_Type = ContentParam::OBJECT; ++ if (param.m_Type == ContentParam::Type::kNumber) { ++ param.m_Type = ContentParam::Type::kObject; + param.m_pObject = + param.m_Number.IsInteger() + ? pdfium::MakeRetain(param.m_Number.GetSigned()) + : pdfium::MakeRetain(param.m_Number.GetFloat()); +- return param.m_pObject.Get(); ++ return param.m_pObject; + } +- if (param.m_Type == ContentParam::NAME) { +- param.m_Type = ContentParam::OBJECT; ++ if (param.m_Type == ContentParam::Type::kName) { ++ param.m_Type = ContentParam::Type::kObject; + param.m_pObject = m_pDocument->New(param.m_Name); +- return param.m_pObject.Get(); ++ return param.m_pObject; + } +- if (param.m_Type == ContentParam::OBJECT) +- return param.m_pObject.Get(); ++ if (param.m_Type == ContentParam::Type::kObject) ++ return param.m_pObject; + +- NOTREACHED(); +- return nullptr; ++ NOTREACHED_NORETURN(); + } + + ByteString CPDF_StreamContentParser::GetString(uint32_t index) const { +@@ -375,10 +367,10 @@ ByteString CPDF_StreamContentParser::GetString(uint32_t index) const { + real_index -= kParamBufSize; + + const ContentParam& param = m_ParamBuf[real_index]; +- if (param.m_Type == ContentParam::NAME) ++ if (param.m_Type == ContentParam::Type::kName) + return param.m_Name; + +- if (param.m_Type == 0 && param.m_pObject) ++ if (param.m_Type == ContentParam::Type::kObject && param.m_pObject) + return param.m_pObject->GetString(); + + return ByteString(); +@@ -393,10 +385,10 @@ float CPDF_StreamContentParser::GetNumber(uint32_t index) const { + real_index -= kParamBufSize; + + const ContentParam& param = m_ParamBuf[real_index]; +- if (param.m_Type == ContentParam::NUMBER) ++ if (param.m_Type == ContentParam::Type::kNumber) + return param.m_Number.GetFloat(); + +- if (param.m_Type == 0 && param.m_pObject) ++ if (param.m_Type == ContentParam::Type::kObject && param.m_pObject) + return param.m_pObject->GetNumber(); + + return 0; +@@ -409,13 +401,22 @@ std::vector CPDF_StreamContentParser::GetNumbers(size_t count) const { + return values; + } + ++CFX_PointF CPDF_StreamContentParser::GetPoint(uint32_t index) const { ++ return CFX_PointF(GetNumber(index + 1), GetNumber(index)); ++} ++ ++CFX_Matrix CPDF_StreamContentParser::GetMatrix() const { ++ return CFX_Matrix(GetNumber(5), GetNumber(4), GetNumber(3), GetNumber(2), ++ GetNumber(1), GetNumber(0)); ++} ++ + void CPDF_StreamContentParser::SetGraphicStates(CPDF_PageObject* pObj, + bool bColor, + bool bText, + bool bGraph) { + pObj->m_GeneralState = m_pCurStates->m_GeneralState; + pObj->m_ClipPath = m_pCurStates->m_ClipPath; +- pObj->m_ContentMarks = *m_ContentMarksStack.top(); ++ pObj->SetContentMarks(*m_ContentMarksStack.top()); + if (bColor) { + pObj->m_ColorState = m_pCurStates->m_ColorState; + } +@@ -425,6 +426,7 @@ void CPDF_StreamContentParser::SetGraphicStates(CPDF_PageObject* pObj, + if (bText) { + pObj->m_TextState = m_pCurStates->m_TextState; + } ++ pObj->SetGraphicsResourceName(m_pCurStates->m_GraphicsResourceName); + } + + // static +@@ -549,33 +551,34 @@ CPDF_StreamContentParser::InitializeOpCodes() { + } + + void CPDF_StreamContentParser::OnOperator(ByteStringView op) { +- static const OpCodes s_OpCodes = InitializeOpCodes(); ++ static const pdfium::base::NoDestructor s_OpCodes( ++ InitializeOpCodes()); + +- auto it = s_OpCodes.find(op.GetID()); +- if (it != s_OpCodes.end()) ++ auto it = s_OpCodes->find(op.GetID()); ++ if (it != s_OpCodes->end()) + (this->*it->second)(); + } + + void CPDF_StreamContentParser::Handle_CloseFillStrokePath() { + Handle_ClosePath(); +- AddPathObject(FXFILL_WINDING, true); ++ AddPathObject(CFX_FillRenderOptions::FillType::kWinding, RenderType::kStroke); + } + + void CPDF_StreamContentParser::Handle_FillStrokePath() { +- AddPathObject(FXFILL_WINDING, true); ++ AddPathObject(CFX_FillRenderOptions::FillType::kWinding, RenderType::kStroke); + } + + void CPDF_StreamContentParser::Handle_CloseEOFillStrokePath() { +- AddPathPoint(m_PathStartX, m_PathStartY, FXPT_TYPE::LineTo, true); +- AddPathObject(FXFILL_ALTERNATE, true); ++ AddPathPointAndClose(m_PathStart, CFX_Path::Point::Type::kLine); ++ AddPathObject(CFX_FillRenderOptions::FillType::kEvenOdd, RenderType::kStroke); + } + + void CPDF_StreamContentParser::Handle_EOFillStrokePath() { +- AddPathObject(FXFILL_ALTERNATE, true); ++ AddPathObject(CFX_FillRenderOptions::FillType::kEvenOdd, RenderType::kStroke); + } + + void CPDF_StreamContentParser::Handle_BeginMarkedContent_Dictionary() { +- CPDF_Object* pProperty = GetObject(0); ++ RetainPtr pProperty = GetObject(0); + if (!pProperty) + return; + +@@ -585,12 +588,13 @@ void CPDF_StreamContentParser::Handle_BeginMarkedContent_Dictionary() { + + if (pProperty->IsName()) { + ByteString property_name = pProperty->GetString(); +- CPDF_Dictionary* pHolder = FindResourceHolder("Properties"); ++ RetainPtr pHolder = FindResourceHolder("Properties"); + if (!pHolder || !pHolder->GetDictFor(property_name)) + return; +- new_marks->AddMarkWithPropertiesHolder(tag, pHolder, property_name); ++ new_marks->AddMarkWithPropertiesHolder(tag, std::move(pHolder), ++ property_name); + } else if (pProperty->IsDictionary()) { +- new_marks->AddMarkWithDirectDict(tag, pProperty->AsDictionary()); ++ new_marks->AddMarkWithDirectDict(tag, ToDictionary(pProperty)); + } else { + return; + } +@@ -600,31 +604,28 @@ void CPDF_StreamContentParser::Handle_BeginMarkedContent_Dictionary() { + void CPDF_StreamContentParser::Handle_BeginImage() { + FX_FILESIZE savePos = m_pSyntax->GetPos(); + auto pDict = m_pDocument->New(); +- while (1) { +- CPDF_StreamParser::SyntaxType type = m_pSyntax->ParseNextElement(); +- if (type == CPDF_StreamParser::Keyword) { ++ while (true) { ++ CPDF_StreamParser::ElementType type = m_pSyntax->ParseNextElement(); ++ if (type == CPDF_StreamParser::ElementType::kKeyword) { + if (m_pSyntax->GetWord() != "ID") { + m_pSyntax->SetPos(savePos); + return; + } + } +- if (type != CPDF_StreamParser::Name) { ++ if (type != CPDF_StreamParser::ElementType::kName) { + break; + } + auto word = m_pSyntax->GetWord(); + ByteString key(word.Last(word.GetLength() - 1)); + auto pObj = m_pSyntax->ReadNextObject(false, false, 0); +- if (!key.IsEmpty()) { +- if (pObj && !pObj->IsInline()) { +- pDict->SetNewFor(key, m_pDocument.Get(), +- pObj->GetObjNum()); +- } else { +- pDict->SetFor(key, std::move(pObj)); +- } ++ if (pObj && !pObj->IsInline()) { ++ pDict->SetNewFor(key, m_pDocument, pObj->GetObjNum()); ++ } else { ++ pDict->SetFor(key, std::move(pObj)); + } + } +- ReplaceAbbr(pDict.Get()); +- CPDF_Object* pCSObj = nullptr; ++ ReplaceAbbr(pDict); ++ RetainPtr pCSObj; + if (pDict->KeyExist("ColorSpace")) { + pCSObj = pDict->GetDirectObjectFor("ColorSpace"); + if (pCSObj->IsName()) { +@@ -638,20 +639,20 @@ void CPDF_StreamContentParser::Handle_BeginImage() { + } + pDict->SetNewFor("Subtype", "Image"); + RetainPtr pStream = +- m_pSyntax->ReadInlineStream(m_pDocument.Get(), std::move(pDict), pCSObj); +- while (1) { +- CPDF_StreamParser::SyntaxType type = m_pSyntax->ParseNextElement(); +- if (type == CPDF_StreamParser::EndOfData) { ++ m_pSyntax->ReadInlineStream(m_pDocument, std::move(pDict), pCSObj.Get()); ++ while (true) { ++ CPDF_StreamParser::ElementType type = m_pSyntax->ParseNextElement(); ++ if (type == CPDF_StreamParser::ElementType::kEndOfData) + break; +- } +- if (type != CPDF_StreamParser::Keyword) { ++ ++ if (type != CPDF_StreamParser::ElementType::kKeyword) + continue; +- } +- if (m_pSyntax->GetWord() == "EI") { ++ ++ if (m_pSyntax->GetWord() == "EI") + break; +- } + } +- CPDF_ImageObject* pObj = AddImage(std::move(pStream)); ++ CPDF_ImageObject* pObj = ++ AddImageFromStream(std::move(pStream), /*resource_name=*/""); + // Record the bounding box of this image, so rendering code can draw it + // properly. + if (pObj && pObj->GetImage()->IsMask()) +@@ -673,15 +674,13 @@ void CPDF_StreamContentParser::Handle_BeginText() { + } + + void CPDF_StreamContentParser::Handle_CurveTo_123() { +- AddPathPoint(GetNumber(5), GetNumber(4), FXPT_TYPE::BezierTo, false); +- AddPathPoint(GetNumber(3), GetNumber(2), FXPT_TYPE::BezierTo, false); +- AddPathPoint(GetNumber(1), GetNumber(0), FXPT_TYPE::BezierTo, false); ++ AddPathPoint(GetPoint(4), CFX_Path::Point::Type::kBezier); ++ AddPathPoint(GetPoint(2), CFX_Path::Point::Type::kBezier); ++ AddPathPoint(GetPoint(0), CFX_Path::Point::Type::kBezier); + } + + void CPDF_StreamContentParser::Handle_ConcatMatrix() { +- CFX_Matrix new_matrix(GetNumber(5), GetNumber(4), GetNumber(3), GetNumber(2), +- GetNumber(1), GetNumber(0)); +- m_pCurStates->m_CTM = new_matrix * m_pCurStates->m_CTM; ++ m_pCurStates->m_CTM = GetMatrix() * m_pCurStates->m_CTM; + OnChangeTextMatrix(); + } + +@@ -690,7 +689,8 @@ void CPDF_StreamContentParser::Handle_SetColorSpace_Fill() { + if (!pCS) + return; + +- m_pCurStates->m_ColorState.GetMutableFillColor()->SetColorSpace(pCS); ++ m_pCurStates->m_ColorState.GetMutableFillColor()->SetColorSpace( ++ std::move(pCS)); + } + + void CPDF_StreamContentParser::Handle_SetColorSpace_Stroke() { +@@ -698,15 +698,16 @@ void CPDF_StreamContentParser::Handle_SetColorSpace_Stroke() { + if (!pCS) + return; + +- m_pCurStates->m_ColorState.GetMutableStrokeColor()->SetColorSpace(pCS); ++ m_pCurStates->m_ColorState.GetMutableStrokeColor()->SetColorSpace( ++ std::move(pCS)); + } + + void CPDF_StreamContentParser::Handle_SetDash() { +- CPDF_Array* pArray = ToArray(GetObject(1)); ++ RetainPtr pArray = ToArray(GetObject(1)); + if (!pArray) + return; + +- m_pCurStates->SetLineDash(pArray, GetNumber(0), 1.0f); ++ m_pCurStates->SetLineDash(pArray.Get(), GetNumber(0), 1.0f); + } + + void CPDF_StreamContentParser::Handle_SetCharWidth() { +@@ -726,7 +727,7 @@ void CPDF_StreamContentParser::Handle_ExecuteXObject() { + ByteString name = GetString(0); + if (name == m_LastImageName && m_pLastImage && m_pLastImage->GetStream() && + m_pLastImage->GetStream()->GetObjNum()) { +- CPDF_ImageObject* pObj = AddImage(m_pLastImage); ++ CPDF_ImageObject* pObj = AddLastImage(); + // Record the bounding box of this image, so rendering code can draw it + // properly. + if (pObj && pObj->GetImage()->IsMask()) +@@ -734,25 +735,24 @@ void CPDF_StreamContentParser::Handle_ExecuteXObject() { + return; + } + +- CPDF_Stream* pXObject = ToStream(FindResourceObj("XObject", name)); +- if (!pXObject) { +- m_bResourceMissing = true; ++ RetainPtr pXObject(ToStream(FindResourceObj("XObject", name))); ++ if (!pXObject) + return; +- } + + ByteString type; + if (pXObject->GetDict()) +- type = pXObject->GetDict()->GetStringFor("Subtype"); ++ type = pXObject->GetDict()->GetByteStringFor("Subtype"); + + if (type == "Form") { +- AddForm(pXObject); ++ AddForm(std::move(pXObject), name); + return; + } + + if (type == "Image") { +- CPDF_ImageObject* pObj = pXObject->IsInline() +- ? AddImage(ToStream(pXObject->Clone())) +- : AddImage(pXObject->GetObjNum()); ++ CPDF_ImageObject* pObj = ++ pXObject->IsInline() ++ ? AddImageFromStream(ToStream(pXObject->Clone()), name) ++ : AddImageFromStreamObjNum(pXObject->GetObjNum(), name); + + m_LastImageName = std::move(name); + if (pObj) { +@@ -763,20 +763,22 @@ void CPDF_StreamContentParser::Handle_ExecuteXObject() { + } + } + +-void CPDF_StreamContentParser::AddForm(CPDF_Stream* pStream) { ++void CPDF_StreamContentParser::AddForm(RetainPtr pStream, ++ const ByteString& name) { + CPDF_AllStates status; + status.m_GeneralState = m_pCurStates->m_GeneralState; + status.m_GraphState = m_pCurStates->m_GraphState; + status.m_ColorState = m_pCurStates->m_ColorState; + status.m_TextState = m_pCurStates->m_TextState; +- auto form = pdfium::MakeUnique( +- m_pDocument.Get(), m_pPageResources.Get(), pStream, m_pResources.Get()); +- form->ParseContent(&status, nullptr, m_ParsedSet.Get()); ++ auto form = std::make_unique( ++ m_pDocument, m_pPageResources, std::move(pStream), m_pResources.Get()); ++ form->ParseContent(&status, nullptr, m_ParsedSet); + + CFX_Matrix matrix = m_pCurStates->m_CTM * m_mtContentToUser; +- +- auto pFormObj = pdfium::MakeUnique(GetCurrentStreamIndex(), +- std::move(form), matrix); ++ auto pFormObj = std::make_unique(GetCurrentStreamIndex(), ++ std::move(form), matrix); ++ pFormObj->SetResourceName(name); ++ pFormObj->SetGraphicsResourceName(m_pCurStates->m_GraphicsResourceName); + if (!m_pObjectHolder->BackgroundAlphaNeeded() && + pFormObj->form()->BackgroundAlphaNeeded()) { + m_pObjectHolder->SetBackgroundAlphaNeeded(true); +@@ -786,37 +788,38 @@ void CPDF_StreamContentParser::AddForm(CPDF_Stream* pStream) { + m_pObjectHolder->AppendPageObject(std::move(pFormObj)); + } + +-CPDF_ImageObject* CPDF_StreamContentParser::AddImage( +- RetainPtr pStream) { ++CPDF_ImageObject* CPDF_StreamContentParser::AddImageFromStream( ++ RetainPtr pStream, ++ const ByteString& name) { + if (!pStream) + return nullptr; + +- auto pImageObj = +- pdfium::MakeUnique(GetCurrentStreamIndex()); ++ auto pImageObj = std::make_unique(GetCurrentStreamIndex()); ++ pImageObj->SetResourceName(name); + pImageObj->SetImage( +- pdfium::MakeRetain(m_pDocument.Get(), std::move(pStream))); ++ pdfium::MakeRetain(m_pDocument, std::move(pStream))); + + return AddImageObject(std::move(pImageObj)); + } + +-CPDF_ImageObject* CPDF_StreamContentParser::AddImage(uint32_t streamObjNum) { +- auto pImageObj = +- pdfium::MakeUnique(GetCurrentStreamIndex()); +- pImageObj->SetImage(CPDF_DocPageData::FromDocument(m_pDocument.Get()) +- ->GetImage(streamObjNum)); ++CPDF_ImageObject* CPDF_StreamContentParser::AddImageFromStreamObjNum( ++ uint32_t stream_obj_num, ++ const ByteString& name) { ++ auto pImageObj = std::make_unique(GetCurrentStreamIndex()); ++ pImageObj->SetResourceName(name); ++ pImageObj->SetImage( ++ CPDF_DocPageData::FromDocument(m_pDocument)->GetImage(stream_obj_num)); + + return AddImageObject(std::move(pImageObj)); + } + +-CPDF_ImageObject* CPDF_StreamContentParser::AddImage( +- const RetainPtr& pImage) { +- if (!pImage) +- return nullptr; ++CPDF_ImageObject* CPDF_StreamContentParser::AddLastImage() { ++ DCHECK(m_pLastImage); + +- auto pImageObj = +- pdfium::MakeUnique(GetCurrentStreamIndex()); +- pImageObj->SetImage(CPDF_DocPageData::FromDocument(m_pDocument.Get()) +- ->GetImage(pImage->GetStream()->GetObjNum())); ++ auto pImageObj = std::make_unique(GetCurrentStreamIndex()); ++ pImageObj->SetResourceName(m_LastImageName); ++ pImageObj->SetImage(CPDF_DocPageData::FromDocument(m_pDocument) ++ ->GetImage(m_pLastImage->GetStream()->GetObjNum())); + + return AddImageObject(std::move(pImageObj)); + } +@@ -827,8 +830,7 @@ CPDF_ImageObject* CPDF_StreamContentParser::AddImageObject( + false); + + CFX_Matrix ImageMatrix = m_pCurStates->m_CTM * m_mtContentToUser; +- pImageObj->set_matrix(ImageMatrix); +- pImageObj->CalcBoundingBox(); ++ pImageObj->SetImageMatrix(ImageMatrix); + + CPDF_ImageObject* pRet = pImageObj.get(); + m_pObjectHolder->AppendPageObject(std::move(pImageObj)); +@@ -836,12 +838,12 @@ CPDF_ImageObject* CPDF_StreamContentParser::AddImageObject( + } + + std::vector CPDF_StreamContentParser::GetColors() const { +- ASSERT(m_ParamCount > 0); ++ DCHECK(m_ParamCount > 0); + return GetNumbers(m_ParamCount); + } + + std::vector CPDF_StreamContentParser::GetNamedColors() const { +- ASSERT(m_ParamCount > 0); ++ DCHECK(m_ParamCount > 0); + const uint32_t nvalues = m_ParamCount - 1; + std::vector values(nvalues); + for (size_t i = 0; i < nvalues; ++i) +@@ -871,47 +873,49 @@ void CPDF_StreamContentParser::Handle_EndText() { + } + + void CPDF_StreamContentParser::Handle_FillPath() { +- AddPathObject(FXFILL_WINDING, false); ++ AddPathObject(CFX_FillRenderOptions::FillType::kWinding, RenderType::kFill); + } + + void CPDF_StreamContentParser::Handle_FillPathOld() { +- AddPathObject(FXFILL_WINDING, false); ++ AddPathObject(CFX_FillRenderOptions::FillType::kWinding, RenderType::kFill); + } + + void CPDF_StreamContentParser::Handle_EOFillPath() { +- AddPathObject(FXFILL_ALTERNATE, false); ++ AddPathObject(CFX_FillRenderOptions::FillType::kEvenOdd, RenderType::kFill); + } + + void CPDF_StreamContentParser::Handle_SetGray_Fill() { +- RetainPtr pCS = +- CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY); +- m_pCurStates->m_ColorState.SetFillColor(pCS, GetNumbers(1)); ++ m_pCurStates->m_ColorState.SetFillColor( ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray), ++ GetNumbers(1)); + } + + void CPDF_StreamContentParser::Handle_SetGray_Stroke() { +- RetainPtr pCS = +- CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY); +- m_pCurStates->m_ColorState.SetStrokeColor(pCS, GetNumbers(1)); ++ m_pCurStates->m_ColorState.SetStrokeColor( ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceGray), ++ GetNumbers(1)); + } + + void CPDF_StreamContentParser::Handle_SetExtendGraphState() { + ByteString name = GetString(0); +- CPDF_Dictionary* pGS = ToDictionary(FindResourceObj("ExtGState", name)); +- if (!pGS) { +- m_bResourceMissing = true; ++ RetainPtr pGS = ++ ToDictionary(FindResourceObj("ExtGState", name)); ++ if (!pGS) + return; +- } +- m_pCurStates->ProcessExtGS(pGS, this); ++ ++ m_pCurStates->m_GraphicsResourceName = name; ++ m_pCurStates->ProcessExtGS(pGS.Get(), this); + } + + void CPDF_StreamContentParser::Handle_ClosePath() { + if (m_PathPoints.empty()) + return; + +- if (m_PathStartX != m_PathCurrentX || m_PathStartY != m_PathCurrentY) +- AddPathPoint(m_PathStartX, m_PathStartY, FXPT_TYPE::LineTo, true); +- else if (m_PathPoints.back().m_Type != FXPT_TYPE::MoveTo) ++ if (m_PathStart.x != m_PathCurrent.x || m_PathStart.y != m_PathCurrent.y) { ++ AddPathPointAndClose(m_PathStart, CFX_Path::Point::Type::kLine); ++ } else { + m_PathPoints.back().m_CloseFigure = true; ++ } + } + + void CPDF_StreamContentParser::Handle_SetFlat() { +@@ -934,32 +938,32 @@ void CPDF_StreamContentParser::Handle_SetCMYKColor_Fill() { + if (m_ParamCount != 4) + return; + +- RetainPtr pCS = +- CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK); +- m_pCurStates->m_ColorState.SetFillColor(pCS, GetNumbers(4)); ++ m_pCurStates->m_ColorState.SetFillColor( ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceCMYK), ++ GetNumbers(4)); + } + + void CPDF_StreamContentParser::Handle_SetCMYKColor_Stroke() { + if (m_ParamCount != 4) + return; + +- RetainPtr pCS = +- CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK); +- m_pCurStates->m_ColorState.SetStrokeColor(pCS, GetNumbers(4)); ++ m_pCurStates->m_ColorState.SetStrokeColor( ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceCMYK), ++ GetNumbers(4)); + } + + void CPDF_StreamContentParser::Handle_LineTo() { + if (m_ParamCount != 2) + return; + +- AddPathPoint(GetNumber(1), GetNumber(0), FXPT_TYPE::LineTo, false); ++ AddPathPoint(GetPoint(0), CFX_Path::Point::Type::kLine); + } + + void CPDF_StreamContentParser::Handle_MoveTo() { + if (m_ParamCount != 2) + return; + +- AddPathPoint(GetNumber(1), GetNumber(0), FXPT_TYPE::MoveTo, false); ++ AddPathPoint(GetPoint(0), CFX_Path::Point::Type::kMove); + ParsePathObject(); + } + +@@ -970,11 +974,11 @@ void CPDF_StreamContentParser::Handle_SetMiterLimit() { + void CPDF_StreamContentParser::Handle_MarkPlace() {} + + void CPDF_StreamContentParser::Handle_EndPath() { +- AddPathObject(0, false); ++ AddPathObject(CFX_FillRenderOptions::FillType::kNoFill, RenderType::kFill); + } + + void CPDF_StreamContentParser::Handle_SaveGraphState() { +- auto pStates = pdfium::MakeUnique(); ++ auto pStates = std::make_unique(); + pStates->Copy(*m_pCurStates); + m_StateStack.push_back(std::move(pStates)); + } +@@ -988,44 +992,48 @@ void CPDF_StreamContentParser::Handle_RestoreGraphState() { + } + + void CPDF_StreamContentParser::Handle_Rectangle() { +- float x = GetNumber(3), y = GetNumber(2); +- float w = GetNumber(1), h = GetNumber(0); ++ float x = GetNumber(3); ++ float y = GetNumber(2); ++ float w = GetNumber(1); ++ float h = GetNumber(0); + AddPathRect(x, y, w, h); + } + + void CPDF_StreamContentParser::AddPathRect(float x, float y, float w, float h) { +- AddPathPoint(x, y, FXPT_TYPE::MoveTo, false); +- AddPathPoint(x + w, y, FXPT_TYPE::LineTo, false); +- AddPathPoint(x + w, y + h, FXPT_TYPE::LineTo, false); +- AddPathPoint(x, y + h, FXPT_TYPE::LineTo, false); +- AddPathPoint(x, y, FXPT_TYPE::LineTo, true); ++ AddPathPoint({x, y}, CFX_Path::Point::Type::kMove); ++ AddPathPoint({x + w, y}, CFX_Path::Point::Type::kLine); ++ AddPathPoint({x + w, y + h}, CFX_Path::Point::Type::kLine); ++ AddPathPoint({x, y + h}, CFX_Path::Point::Type::kLine); ++ AddPathPointAndClose({x, y}, CFX_Path::Point::Type::kLine); + } + + void CPDF_StreamContentParser::Handle_SetRGBColor_Fill() { + if (m_ParamCount != 3) + return; + +- RetainPtr pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB); +- m_pCurStates->m_ColorState.SetFillColor(pCS, GetNumbers(3)); ++ m_pCurStates->m_ColorState.SetFillColor( ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB), ++ GetNumbers(3)); + } + + void CPDF_StreamContentParser::Handle_SetRGBColor_Stroke() { + if (m_ParamCount != 3) + return; + +- RetainPtr pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB); +- m_pCurStates->m_ColorState.SetStrokeColor(pCS, GetNumbers(3)); ++ m_pCurStates->m_ColorState.SetStrokeColor( ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB), ++ GetNumbers(3)); + } + + void CPDF_StreamContentParser::Handle_SetRenderIntent() {} + + void CPDF_StreamContentParser::Handle_CloseStrokePath() { + Handle_ClosePath(); +- AddPathObject(0, true); ++ AddPathObject(CFX_FillRenderOptions::FillType::kNoFill, RenderType::kStroke); + } + + void CPDF_StreamContentParser::Handle_StrokePath() { +- AddPathObject(0, true); ++ AddPathObject(CFX_FillRenderOptions::FillType::kNoFill, RenderType::kStroke); + } + + void CPDF_StreamContentParser::Handle_SetColor_Fill() { +@@ -1039,7 +1047,7 @@ void CPDF_StreamContentParser::Handle_SetColor_Stroke() { + } + + void CPDF_StreamContentParser::Handle_SetColorPS_Fill() { +- CPDF_Object* pLastParam = GetObject(0); ++ RetainPtr pLastParam = GetObject(0); + if (!pLastParam) + return; + +@@ -1050,13 +1058,16 @@ void CPDF_StreamContentParser::Handle_SetColorPS_Fill() { + + // A valid |pLastParam| implies |m_ParamCount| > 0, so GetNamedColors() call + // below is safe. +- RetainPtr pPattern = FindPattern(GetString(0), false); +- if (pPattern) +- m_pCurStates->m_ColorState.SetFillPattern(pPattern, GetNamedColors()); ++ RetainPtr pPattern = FindPattern(GetString(0)); ++ if (!pPattern) ++ return; ++ ++ std::vector values = GetNamedColors(); ++ m_pCurStates->m_ColorState.SetFillPattern(std::move(pPattern), values); + } + + void CPDF_StreamContentParser::Handle_SetColorPS_Stroke() { +- CPDF_Object* pLastParam = GetObject(0); ++ RetainPtr pLastParam = GetObject(0); + if (!pLastParam) + return; + +@@ -1067,17 +1078,16 @@ void CPDF_StreamContentParser::Handle_SetColorPS_Stroke() { + + // A valid |pLastParam| implies |m_ParamCount| > 0, so GetNamedColors() call + // below is safe. +- RetainPtr pPattern = FindPattern(GetString(0), false); +- if (pPattern) +- m_pCurStates->m_ColorState.SetStrokePattern(pPattern, GetNamedColors()); +-} +- +-void CPDF_StreamContentParser::Handle_ShadeFill() { +- RetainPtr pPattern = FindPattern(GetString(0), true); ++ RetainPtr pPattern = FindPattern(GetString(0)); + if (!pPattern) + return; + +- CPDF_ShadingPattern* pShading = pPattern->AsShadingPattern(); ++ std::vector values = GetNamedColors(); ++ m_pCurStates->m_ColorState.SetStrokePattern(std::move(pPattern), values); ++} ++ ++void CPDF_StreamContentParser::Handle_ShadeFill() { ++ RetainPtr pShading = FindShading(GetString(0)); + if (!pShading) + return; + +@@ -1085,13 +1095,13 @@ void CPDF_StreamContentParser::Handle_ShadeFill() { + return; + + CFX_Matrix matrix = m_pCurStates->m_CTM * m_mtContentToUser; +- auto pObj = pdfium::MakeUnique(GetCurrentStreamIndex(), +- pShading, matrix); ++ auto pObj = std::make_unique(GetCurrentStreamIndex(), ++ pShading, matrix); + SetGraphicStates(pObj.get(), false, false, false); + CFX_FloatRect bbox = + pObj->m_ClipPath.HasRef() ? pObj->m_ClipPath.GetClipBox() : m_BBox; + if (pShading->IsMeshShading()) +- bbox.Intersect(GetShadingBBox(pShading, pObj->matrix())); ++ bbox.Intersect(GetShadingBBox(pShading.Get(), pObj->matrix())); + pObj->SetRect(bbox); + m_pObjectHolder->AppendPageObject(std::move(pObj)); + } +@@ -1101,7 +1111,7 @@ void CPDF_StreamContentParser::Handle_SetCharSpace() { + } + + void CPDF_StreamContentParser::Handle_MoveTextPoint() { +- m_pCurStates->m_TextLinePos += CFX_PointF(GetNumber(1), GetNumber(0)); ++ m_pCurStates->m_TextLinePos += GetPoint(0); + m_pCurStates->m_TextPos = m_pCurStates->m_TextLinePos; + } + +@@ -1111,52 +1121,51 @@ void CPDF_StreamContentParser::Handle_MoveTextPoint_SetLeading() { + } + + void CPDF_StreamContentParser::Handle_SetFont() { +- float fs = GetNumber(0); +- if (fs == 0) { +- constexpr float kDefaultFontSize = 0.0f; +- fs = kDefaultFontSize; +- } +- +- m_pCurStates->m_TextState.SetFontSize(fs); ++ m_pCurStates->m_TextState.SetFontSize(GetNumber(0)); + RetainPtr pFont = FindFont(GetString(1)); + if (pFont) +- m_pCurStates->m_TextState.SetFont(pFont); ++ m_pCurStates->m_TextState.SetFont(std::move(pFont)); + } + +-CPDF_Dictionary* CPDF_StreamContentParser::FindResourceHolder( ++RetainPtr CPDF_StreamContentParser::FindResourceHolder( + const ByteString& type) { + if (!m_pResources) + return nullptr; + +- CPDF_Dictionary* pDict = m_pResources->GetDictFor(type); ++ RetainPtr pDict = m_pResources->GetMutableDictFor(type); + if (pDict) + return pDict; + + if (m_pResources == m_pPageResources || !m_pPageResources) + return nullptr; + +- return m_pPageResources->GetDictFor(type); ++ return m_pPageResources->GetMutableDictFor(type); + } + +-CPDF_Object* CPDF_StreamContentParser::FindResourceObj(const ByteString& type, +- const ByteString& name) { +- CPDF_Dictionary* pHolder = FindResourceHolder(type); +- return pHolder ? pHolder->GetDirectObjectFor(name) : nullptr; ++RetainPtr CPDF_StreamContentParser::FindResourceObj( ++ const ByteString& type, ++ const ByteString& name) { ++ RetainPtr pHolder = FindResourceHolder(type); ++ return pHolder ? pHolder->GetMutableDirectObjectFor(name) : nullptr; + } + + RetainPtr CPDF_StreamContentParser::FindFont( + const ByteString& name) { +- CPDF_Dictionary* pFontDict = ToDictionary(FindResourceObj("Font", name)); ++ RetainPtr pFontDict( ++ ToDictionary(FindResourceObj("Font", name))); + if (!pFontDict) { +- m_bResourceMissing = true; +- return CPDF_Font::GetStockFont(m_pDocument.Get(), +- CFX_Font::kDefaultAnsiFontName); ++ return CPDF_Font::GetStockFont(m_pDocument, CFX_Font::kDefaultAnsiFontName); + } +- RetainPtr pFont = +- CPDF_DocPageData::FromDocument(m_pDocument.Get())->GetFont(pFontDict); +- if (pFont && pFont->IsType3Font()) { +- pFont->AsType3Font()->SetPageResources(m_pResources.Get()); +- pFont->AsType3Font()->CheckType3FontMetrics(); ++ RetainPtr pFont = CPDF_DocPageData::FromDocument(m_pDocument) ++ ->GetFont(std::move(pFontDict)); ++ if (pFont) { ++ // Save `name` for later retrieval by the CPDF_TextObject that uses the ++ // font. ++ pFont->SetResourceName(name); ++ if (pFont->IsType3Font()) { ++ pFont->AsType3Font()->SetPageResources(m_pResources.Get()); ++ pFont->AsType3Font()->CheckType3FontMetrics(); ++ } + } + return pFont; + } +@@ -1164,44 +1173,49 @@ RetainPtr CPDF_StreamContentParser::FindFont( + RetainPtr CPDF_StreamContentParser::FindColorSpace( + const ByteString& name) { + if (name == "Pattern") +- return CPDF_ColorSpace::GetStockCS(PDFCS_PATTERN); ++ return CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kPattern); + + if (name == "DeviceGray" || name == "DeviceCMYK" || name == "DeviceRGB") { + ByteString defname = "Default"; + defname += name.Last(name.GetLength() - 7); +- const CPDF_Object* pDefObj = FindResourceObj("ColorSpace", defname); ++ RetainPtr pDefObj = ++ FindResourceObj("ColorSpace", defname); + if (!pDefObj) { +- if (name == "DeviceGray") +- return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY); +- ++ if (name == "DeviceGray") { ++ return CPDF_ColorSpace::GetStockCS( ++ CPDF_ColorSpace::Family::kDeviceGray); ++ } + if (name == "DeviceRGB") +- return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB); ++ return CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB); + +- return CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK); ++ return CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceCMYK); + } +- return CPDF_DocPageData::FromDocument(m_pDocument.Get()) +- ->GetColorSpace(pDefObj, nullptr); ++ return CPDF_DocPageData::FromDocument(m_pDocument) ++ ->GetColorSpace(pDefObj.Get(), nullptr); + } +- const CPDF_Object* pCSObj = FindResourceObj("ColorSpace", name); +- if (!pCSObj) { +- m_bResourceMissing = true; ++ RetainPtr pCSObj = FindResourceObj("ColorSpace", name); ++ if (!pCSObj) + return nullptr; +- } +- return CPDF_DocPageData::FromDocument(m_pDocument.Get()) +- ->GetColorSpace(pCSObj, nullptr); ++ return CPDF_DocPageData::FromDocument(m_pDocument) ++ ->GetColorSpace(pCSObj.Get(), nullptr); + } + + RetainPtr CPDF_StreamContentParser::FindPattern( +- const ByteString& name, +- bool bShading) { +- CPDF_Object* pPattern = +- FindResourceObj(bShading ? "Shading" : "Pattern", name); +- if (!pPattern || (!pPattern->IsDictionary() && !pPattern->IsStream())) { +- m_bResourceMissing = true; ++ const ByteString& name) { ++ RetainPtr pPattern = FindResourceObj("Pattern", name); ++ if (!pPattern || (!pPattern->IsDictionary() && !pPattern->IsStream())) + return nullptr; +- } +- return CPDF_DocPageData::FromDocument(m_pDocument.Get()) +- ->GetPattern(pPattern, bShading, m_pCurStates->m_ParentMatrix); ++ return CPDF_DocPageData::FromDocument(m_pDocument) ++ ->GetPattern(std::move(pPattern), m_pCurStates->m_ParentMatrix); ++} ++ ++RetainPtr CPDF_StreamContentParser::FindShading( ++ const ByteString& name) { ++ RetainPtr pPattern = FindResourceObj("Shading", name); ++ if (!pPattern || (!pPattern->IsDictionary() && !pPattern->IsStream())) ++ return nullptr; ++ return CPDF_DocPageData::FromDocument(m_pDocument) ++ ->GetShading(std::move(pPattern), m_pCurStates->m_ParentMatrix); + } + + void CPDF_StreamContentParser::AddTextObject(const ByteString* pStrs, +@@ -1225,11 +1239,11 @@ void CPDF_StreamContentParser::AddTextObject(const ByteString* pStrs, + pFont->IsType3Font() ? TextRenderingMode::MODE_FILL + : m_pCurStates->m_TextState.GetTextMode(); + { +- auto pText = pdfium::MakeUnique(GetCurrentStreamIndex()); +- m_pLastTextObject = pText.get(); +- SetGraphicStates(m_pLastTextObject.Get(), true, true, true); ++ auto pText = std::make_unique(GetCurrentStreamIndex()); ++ pText->SetResourceName(pFont->GetResourceName()); ++ SetGraphicStates(pText.get(), true, true, true); + if (TextRenderingModeIsStrokeMode(text_mode)) { +- float* pCTM = pText->m_TextState.GetMutableCTM(); ++ pdfium::span pCTM = pText->m_TextState.GetMutableCTM(); + pCTM[0] = m_pCurStates->m_CTM.a; + pCTM[1] = m_pCurStates->m_CTM.c; + pCTM[2] = m_pCurStates->m_CTM.b; +@@ -1244,10 +1258,8 @@ void CPDF_StreamContentParser::AddTextObject(const ByteString* pStrs, + + m_pCurStates->m_TextPos += + pText->CalcPositionData(m_pCurStates->m_TextHorzScale); +- if (TextRenderingModeIsClipMode(text_mode)) { +- m_ClipTextList.push_back( +- std::unique_ptr(pText->Clone())); +- } ++ if (TextRenderingModeIsClipMode(text_mode)) ++ m_ClipTextList.push_back(pText->Clone()); + m_pObjectHolder->AppendPageObject(std::move(pText)); + } + if (!kernings.empty() && kernings[nSegs - 1] != 0) { +@@ -1280,20 +1292,20 @@ void CPDF_StreamContentParser::Handle_ShowText() { + } + + void CPDF_StreamContentParser::Handle_ShowText_Positioning() { +- CPDF_Array* pArray = ToArray(GetObject(0)); ++ RetainPtr pArray = ToArray(GetObject(0)); + if (!pArray) + return; + + size_t n = pArray->size(); + size_t nsegs = 0; + for (size_t i = 0; i < n; i++) { +- const CPDF_Object* pDirectObject = pArray->GetDirectObjectAt(i); ++ RetainPtr pDirectObject = pArray->GetDirectObjectAt(i); + if (pDirectObject && pDirectObject->IsString()) + nsegs++; + } + if (nsegs == 0) { + for (size_t i = 0; i < n; i++) { +- float fKerning = pArray->GetNumberAt(i); ++ float fKerning = pArray->GetFloatAt(i); + if (fKerning != 0) + m_pCurStates->m_TextPos.x -= GetHorizontalTextSize(fKerning); + } +@@ -1304,7 +1316,7 @@ void CPDF_StreamContentParser::Handle_ShowText_Positioning() { + size_t iSegment = 0; + float fInitKerning = 0; + for (size_t i = 0; i < n; i++) { +- CPDF_Object* pObj = pArray->GetDirectObjectAt(i); ++ RetainPtr pObj = pArray->GetDirectObjectAt(i); + if (!pObj) + continue; + +@@ -1330,9 +1342,7 @@ void CPDF_StreamContentParser::Handle_SetTextLeading() { + } + + void CPDF_StreamContentParser::Handle_SetTextMatrix() { +- m_pCurStates->m_TextMatrix = +- CFX_Matrix(GetNumber(5), GetNumber(4), GetNumber(3), GetNumber(2), +- GetNumber(1), GetNumber(0)); ++ m_pCurStates->m_TextMatrix = GetMatrix(); + OnChangeTextMatrix(); + m_pCurStates->m_TextPos = CFX_PointF(); + m_pCurStates->m_TextLinePos = CFX_PointF(); +@@ -1344,7 +1354,8 @@ void CPDF_StreamContentParser::OnChangeTextMatrix() { + text_matrix.Concat(m_pCurStates->m_TextMatrix); + text_matrix.Concat(m_pCurStates->m_CTM); + text_matrix.Concat(m_mtContentToUser); +- float* pTextMatrix = m_pCurStates->m_TextState.GetMutableMatrix(); ++ pdfium::span pTextMatrix = ++ m_pCurStates->m_TextState.GetMutableMatrix(); + pTextMatrix[0] = text_matrix.a; + pTextMatrix[1] = text_matrix.c; + pTextMatrix[2] = text_matrix.b; +@@ -1379,9 +1390,9 @@ void CPDF_StreamContentParser::Handle_MoveToNextLine() { + } + + void CPDF_StreamContentParser::Handle_CurveTo_23() { +- AddPathPoint(m_PathCurrentX, m_PathCurrentY, FXPT_TYPE::BezierTo, false); +- AddPathPoint(GetNumber(3), GetNumber(2), FXPT_TYPE::BezierTo, false); +- AddPathPoint(GetNumber(1), GetNumber(0), FXPT_TYPE::BezierTo, false); ++ AddPathPoint(m_PathCurrent, CFX_Path::Point::Type::kBezier); ++ AddPathPoint(GetPoint(2), CFX_Path::Point::Type::kBezier); ++ AddPathPoint(GetPoint(0), CFX_Path::Point::Type::kBezier); + } + + void CPDF_StreamContentParser::Handle_SetLineWidth() { +@@ -1389,17 +1400,17 @@ void CPDF_StreamContentParser::Handle_SetLineWidth() { + } + + void CPDF_StreamContentParser::Handle_Clip() { +- m_PathClipType = FXFILL_WINDING; ++ m_PathClipType = CFX_FillRenderOptions::FillType::kWinding; + } + + void CPDF_StreamContentParser::Handle_EOClip() { +- m_PathClipType = FXFILL_ALTERNATE; ++ m_PathClipType = CFX_FillRenderOptions::FillType::kEvenOdd; + } + + void CPDF_StreamContentParser::Handle_CurveTo_13() { +- AddPathPoint(GetNumber(3), GetNumber(2), FXPT_TYPE::BezierTo, false); +- AddPathPoint(GetNumber(1), GetNumber(0), FXPT_TYPE::BezierTo, false); +- AddPathPoint(GetNumber(1), GetNumber(0), FXPT_TYPE::BezierTo, false); ++ AddPathPoint(GetPoint(2), CFX_Path::Point::Type::kBezier); ++ AddPathPoint(GetPoint(0), CFX_Path::Point::Type::kBezier); ++ AddPathPoint(GetPoint(0), CFX_Path::Point::Type::kBezier); + } + + void CPDF_StreamContentParser::Handle_NextLineShowText() { +@@ -1415,129 +1426,152 @@ void CPDF_StreamContentParser::Handle_NextLineShowText_Space() { + + void CPDF_StreamContentParser::Handle_Invalid() {} + +-void CPDF_StreamContentParser::AddPathPoint(float x, +- float y, +- FXPT_TYPE type, +- bool close) { ++void CPDF_StreamContentParser::AddPathPoint(const CFX_PointF& point, ++ CFX_Path::Point::Type type) { + // If the path point is the same move as the previous one and neither of them + // closes the path, then just skip it. +- if (!close && type == FXPT_TYPE::MoveTo && !m_PathPoints.empty() && ++ if (type == CFX_Path::Point::Type::kMove && !m_PathPoints.empty() && + !m_PathPoints.back().m_CloseFigure && +- m_PathPoints.back().m_Type == type && m_PathCurrentX == x && +- m_PathCurrentY == y) { ++ m_PathPoints.back().m_Type == type && m_PathCurrent == point) { + return; + } + +- m_PathCurrentX = x; +- m_PathCurrentY = y; +- if (type == FXPT_TYPE::MoveTo && !close) { +- m_PathStartX = x; +- m_PathStartY = y; ++ m_PathCurrent = point; ++ if (type == CFX_Path::Point::Type::kMove) { ++ m_PathStart = point; + if (!m_PathPoints.empty() && +- m_PathPoints.back().IsTypeAndOpen(FXPT_TYPE::MoveTo)) { +- m_PathPoints.back().m_Point = CFX_PointF(x, y); ++ m_PathPoints.back().IsTypeAndOpen(CFX_Path::Point::Type::kMove)) { ++ m_PathPoints.back().m_Point = point; + return; + } + } else if (m_PathPoints.empty()) { + return; + } +- m_PathPoints.push_back(FX_PATHPOINT(CFX_PointF(x, y), type, close)); ++ m_PathPoints.emplace_back(point, type, /*close=*/false); + } + +-void CPDF_StreamContentParser::AddPathObject(int FillType, bool bStroke) { +- std::vector PathPoints; +- PathPoints.swap(m_PathPoints); +- uint8_t PathClipType = m_PathClipType; +- m_PathClipType = 0; ++void CPDF_StreamContentParser::AddPathPointAndClose( ++ const CFX_PointF& point, ++ CFX_Path::Point::Type type) { ++ m_PathCurrent = point; ++ if (m_PathPoints.empty()) ++ return; ++ ++ m_PathPoints.emplace_back(point, type, /*close=*/true); ++} + +- if (PathPoints.empty()) ++void CPDF_StreamContentParser::AddPathObject( ++ CFX_FillRenderOptions::FillType fill_type, ++ RenderType render_type) { ++ std::vector path_points; ++ path_points.swap(m_PathPoints); ++ CFX_FillRenderOptions::FillType path_clip_type = m_PathClipType; ++ m_PathClipType = CFX_FillRenderOptions::FillType::kNoFill; ++ ++ if (path_points.empty()) + return; + +- if (PathPoints.size() == 1) { +- if (PathClipType) { ++ if (path_points.size() == 1) { ++ if (path_clip_type != CFX_FillRenderOptions::FillType::kNoFill) { + CPDF_Path path; + path.AppendRect(0, 0, 0, 0); +- m_pCurStates->m_ClipPath.AppendPath(path, FXFILL_WINDING, true); ++ m_pCurStates->m_ClipPath.AppendPathWithAutoMerge( ++ path, CFX_FillRenderOptions::FillType::kWinding); ++ return; + } +- return; ++ ++ CFX_Path::Point& point = path_points.front(); ++ if (point.m_Type != CFX_Path::Point::Type::kMove || !point.m_CloseFigure || ++ m_pCurStates->m_GraphState.GetLineCap() != ++ CFX_GraphStateData::LineCap::kRound) { ++ return; ++ } ++ ++ // For round line cap only: When a path moves to a point and immediately ++ // gets closed, we can treat it as drawing a path from this point to itself ++ // and closing the path. This should not apply to butt line cap or ++ // projecting square line cap since they should not be rendered. ++ point.m_CloseFigure = false; ++ path_points.emplace_back(point.m_Point, CFX_Path::Point::Type::kLine, ++ /*close=*/true); + } + +- if (PathPoints.back().IsTypeAndOpen(FXPT_TYPE::MoveTo)) +- PathPoints.pop_back(); ++ if (path_points.back().IsTypeAndOpen(CFX_Path::Point::Type::kMove)) ++ path_points.pop_back(); + +- CPDF_Path Path; +- for (const auto& point : PathPoints) +- Path.AppendPoint(point.m_Point, point.m_Type, point.m_CloseFigure); ++ CPDF_Path path; ++ for (const auto& point : path_points) { ++ if (point.m_CloseFigure) ++ path.AppendPointAndClose(point.m_Point, point.m_Type); ++ else ++ path.AppendPoint(point.m_Point, point.m_Type); ++ } + + CFX_Matrix matrix = m_pCurStates->m_CTM * m_mtContentToUser; +- if (bStroke || FillType) { +- auto pPathObj = +- pdfium::MakeUnique(GetCurrentStreamIndex()); ++ bool bStroke = render_type == RenderType::kStroke; ++ if (bStroke || fill_type != CFX_FillRenderOptions::FillType::kNoFill) { ++ auto pPathObj = std::make_unique(GetCurrentStreamIndex()); + pPathObj->set_stroke(bStroke); +- pPathObj->set_filltype(FillType); +- pPathObj->path() = Path; +- pPathObj->set_matrix(matrix); ++ pPathObj->set_filltype(fill_type); ++ pPathObj->path() = path; + SetGraphicStates(pPathObj.get(), true, false, true); +- pPathObj->CalcBoundingBox(); ++ pPathObj->SetPathMatrix(matrix); + m_pObjectHolder->AppendPageObject(std::move(pPathObj)); + } +- if (PathClipType) { ++ if (path_clip_type != CFX_FillRenderOptions::FillType::kNoFill) { + if (!matrix.IsIdentity()) +- Path.Transform(matrix); +- m_pCurStates->m_ClipPath.AppendPath(Path, PathClipType, true); ++ path.Transform(matrix); ++ m_pCurStates->m_ClipPath.AppendPathWithAutoMerge(path, path_clip_type); + } + } + + uint32_t CPDF_StreamContentParser::Parse( +- const uint8_t* pData, +- uint32_t dwSize, ++ pdfium::span pData, + uint32_t start_offset, + uint32_t max_cost, + const std::vector& stream_start_offsets) { +- ASSERT(start_offset < dwSize); +- +- // Parsing will be done from |pDataStart|, for at most |size_left| bytes. +- const uint8_t* pDataStart = pData + start_offset; +- uint32_t size_left = dwSize - start_offset; ++ DCHECK(start_offset < pData.size()); + ++ // Parsing will be done from within |pDataStart|. ++ pdfium::span pDataStart = pData.subspan(start_offset); + m_StartParseOffset = start_offset; +- + if (m_ParsedSet->size() > kMaxFormLevel || +- pdfium::ContainsKey(*m_ParsedSet, pDataStart)) { +- return size_left; ++ pdfium::Contains(*m_ParsedSet, pDataStart.data())) { ++ return fxcrt::CollectionSize(pDataStart); + } + + m_StreamStartOffsets = stream_start_offsets; + +- pdfium::ScopedSetInsertion scopedInsert(m_ParsedSet.Get(), +- pDataStart); ++ ScopedSetInsertion scopedInsert(m_ParsedSet, ++ pDataStart.data()); + + uint32_t init_obj_count = m_pObjectHolder->GetPageObjectCount(); +- CPDF_StreamParser syntax(pdfium::make_span(pDataStart, size_left), +- m_pDocument->GetByteStringPool()); +- CPDF_StreamParserAutoClearer auto_clearer(&m_pSyntax, &syntax); +- while (1) { ++ AutoNuller> auto_clearer(&m_pSyntax); ++ m_pSyntax = std::make_unique( ++ pDataStart, m_pDocument->GetByteStringPool()); ++ ++ while (true) { + uint32_t cost = m_pObjectHolder->GetPageObjectCount() - init_obj_count; + if (max_cost && cost >= max_cost) { + break; + } +- switch (syntax.ParseNextElement()) { +- case CPDF_StreamParser::EndOfData: ++ switch (m_pSyntax->ParseNextElement()) { ++ case CPDF_StreamParser::ElementType::kEndOfData: + return m_pSyntax->GetPos(); +- case CPDF_StreamParser::Keyword: +- OnOperator(syntax.GetWord()); ++ case CPDF_StreamParser::ElementType::kKeyword: ++ OnOperator(m_pSyntax->GetWord()); + ClearAllParams(); + break; +- case CPDF_StreamParser::Number: +- AddNumberParam(syntax.GetWord()); ++ case CPDF_StreamParser::ElementType::kNumber: ++ AddNumberParam(m_pSyntax->GetWord()); + break; +- case CPDF_StreamParser::Name: { +- auto word = syntax.GetWord(); ++ case CPDF_StreamParser::ElementType::kName: { ++ auto word = m_pSyntax->GetWord(); + AddNameParam(word.Last(word.GetLength() - 1)); + break; + } + default: +- AddObjectParam(syntax.GetObject()); ++ AddObjectParam(m_pSyntax->GetObject()); + } + } + return m_pSyntax->GetPos(); +@@ -1547,42 +1581,51 @@ void CPDF_StreamContentParser::ParsePathObject() { + float params[6] = {}; + int nParams = 0; + int last_pos = m_pSyntax->GetPos(); +- while (1) { +- CPDF_StreamParser::SyntaxType type = m_pSyntax->ParseNextElement(); ++ while (true) { ++ CPDF_StreamParser::ElementType type = m_pSyntax->ParseNextElement(); + bool bProcessed = true; + switch (type) { +- case CPDF_StreamParser::EndOfData: ++ case CPDF_StreamParser::ElementType::kEndOfData: + return; +- case CPDF_StreamParser::Keyword: { ++ case CPDF_StreamParser::ElementType::kKeyword: { + ByteStringView strc = m_pSyntax->GetWord(); + int len = strc.GetLength(); + if (len == 1) { + switch (strc[0]) { + case kPathOperatorSubpath: +- AddPathPoint(params[0], params[1], FXPT_TYPE::MoveTo, false); ++ AddPathPoint({params[0], params[1]}, ++ CFX_Path::Point::Type::kMove); + nParams = 0; + break; + case kPathOperatorLine: +- AddPathPoint(params[0], params[1], FXPT_TYPE::LineTo, false); ++ AddPathPoint({params[0], params[1]}, ++ CFX_Path::Point::Type::kLine); + nParams = 0; + break; + case kPathOperatorCubicBezier1: +- AddPathPoint(params[0], params[1], FXPT_TYPE::BezierTo, false); +- AddPathPoint(params[2], params[3], FXPT_TYPE::BezierTo, false); +- AddPathPoint(params[4], params[5], FXPT_TYPE::BezierTo, false); ++ AddPathPoint({params[0], params[1]}, ++ CFX_Path::Point::Type::kBezier); ++ AddPathPoint({params[2], params[3]}, ++ CFX_Path::Point::Type::kBezier); ++ AddPathPoint({params[4], params[5]}, ++ CFX_Path::Point::Type::kBezier); + nParams = 0; + break; + case kPathOperatorCubicBezier2: +- AddPathPoint(m_PathCurrentX, m_PathCurrentY, FXPT_TYPE::BezierTo, +- false); +- AddPathPoint(params[0], params[1], FXPT_TYPE::BezierTo, false); +- AddPathPoint(params[2], params[3], FXPT_TYPE::BezierTo, false); ++ AddPathPoint(m_PathCurrent, CFX_Path::Point::Type::kBezier); ++ AddPathPoint({params[0], params[1]}, ++ CFX_Path::Point::Type::kBezier); ++ AddPathPoint({params[2], params[3]}, ++ CFX_Path::Point::Type::kBezier); + nParams = 0; + break; + case kPathOperatorCubicBezier3: +- AddPathPoint(params[0], params[1], FXPT_TYPE::BezierTo, false); +- AddPathPoint(params[2], params[3], FXPT_TYPE::BezierTo, false); +- AddPathPoint(params[2], params[3], FXPT_TYPE::BezierTo, false); ++ AddPathPoint({params[0], params[1]}, ++ CFX_Path::Point::Type::kBezier); ++ AddPathPoint({params[2], params[3]}, ++ CFX_Path::Point::Type::kBezier); ++ AddPathPoint({params[2], params[3]}, ++ CFX_Path::Point::Type::kBezier); + nParams = 0; + break; + case kPathOperatorClosePath: +@@ -1609,7 +1652,7 @@ void CPDF_StreamContentParser::ParsePathObject() { + } + break; + } +- case CPDF_StreamParser::Number: { ++ case CPDF_StreamParser::ElementType::kNumber: { + if (nParams == 6) + break; + +@@ -1630,15 +1673,15 @@ void CPDF_StreamContentParser::ParsePathObject() { + // static + ByteStringView CPDF_StreamContentParser::FindKeyAbbreviationForTesting( + ByteStringView abbr) { +- return FindFullName(kInlineKeyAbbr, FX_ArraySize(kInlineKeyAbbr), abbr); ++ return FindFullName(kInlineKeyAbbr, abbr); + } + + // static + ByteStringView CPDF_StreamContentParser::FindValueAbbreviationForTesting( + ByteStringView abbr) { +- return FindFullName(kInlineValueAbbr, FX_ArraySize(kInlineValueAbbr), abbr); ++ return FindFullName(kInlineValueAbbr, abbr); + } + +-CPDF_StreamContentParser::ContentParam::ContentParam() {} ++CPDF_StreamContentParser::ContentParam::ContentParam() = default; + +-CPDF_StreamContentParser::ContentParam::~ContentParam() {} ++CPDF_StreamContentParser::ContentParam::~ContentParam() = default; +diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.h b/core/fpdfapi/page/cpdf_streamcontentparser.h +index a27032009..276dc7292 100644 +--- a/core/fpdfapi/page/cpdf_streamcontentparser.h ++++ b/core/fpdfapi/page/cpdf_streamcontentparser.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,10 +14,14 @@ + #include + + #include "core/fpdfapi/page/cpdf_contentmarks.h" ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/fx_number.h" +-#include "core/fxcrt/fx_string.h" + #include "core/fxcrt/retain_ptr.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxcrt/unowned_ptr.h" ++#include "core/fxge/cfx_fillrenderoptions.h" ++#include "core/fxge/cfx_path.h" ++#include "third_party/base/span.h" + + class CPDF_AllStates; + class CPDF_ColorSpace; +@@ -30,6 +34,7 @@ class CPDF_Object; + class CPDF_PageObject; + class CPDF_PageObjectHolder; + class CPDF_Pattern; ++class CPDF_ShadingPattern; + class CPDF_Stream; + class CPDF_StreamParser; + class CPDF_TextObject; +@@ -37,46 +42,45 @@ class CPDF_TextObject; + class CPDF_StreamContentParser { + public: + CPDF_StreamContentParser(CPDF_Document* pDoc, +- CPDF_Dictionary* pPageResources, +- CPDF_Dictionary* pParentResources, ++ RetainPtr pPageResources, ++ RetainPtr pParentResources, + const CFX_Matrix* pmtContentToUser, + CPDF_PageObjectHolder* pObjHolder, +- CPDF_Dictionary* pResources, ++ RetainPtr pResources, + const CFX_FloatRect& rcBBox, + const CPDF_AllStates* pStates, + std::set* pParsedSet); + ~CPDF_StreamContentParser(); + +- uint32_t Parse(const uint8_t* pData, +- uint32_t dwSize, ++ uint32_t Parse(pdfium::span pData, + uint32_t start_offset, + uint32_t max_cost, + const std::vector& stream_start_offsets); +- CPDF_PageObjectHolder* GetPageObjectHolder() const { +- return m_pObjectHolder.Get(); +- } ++ CPDF_PageObjectHolder* GetPageObjectHolder() const { return m_pObjectHolder; } + CPDF_AllStates* GetCurStates() const { return m_pCurStates.get(); } + bool IsColored() const { return m_bColored; } +- const float* GetType3Data() const { return m_Type3Data; } ++ pdfium::span GetType3Data() const { return m_Type3Data; } + RetainPtr FindFont(const ByteString& name); + + static ByteStringView FindKeyAbbreviationForTesting(ByteStringView abbr); + static ByteStringView FindValueAbbreviationForTesting(ByteStringView abbr); + + private: ++ enum class RenderType : bool { kFill = false, kStroke = true }; ++ + struct ContentParam { +- enum Type { OBJECT = 0, NUMBER, NAME }; ++ enum class Type : uint8_t { kObject = 0, kNumber, kName }; + + ContentParam(); + ~ContentParam(); + +- Type m_Type; ++ Type m_Type = Type::kObject; + FX_Number m_Number; + ByteString m_Name; + RetainPtr m_pObject; + }; + +- static const int kParamBufSize = 16; ++ static constexpr int kParamBufSize = 16; + + using OpCodes = std::map; + static OpCodes InitializeOpCodes(); +@@ -86,7 +90,7 @@ class CPDF_StreamContentParser { + void AddObjectParam(RetainPtr pObj); + int GetNextParamPos(); + void ClearAllParams(); +- CPDF_Object* GetObject(uint32_t index); ++ RetainPtr GetObject(uint32_t index); + ByteString GetString(uint32_t index) const; + float GetNumber(uint32_t index) const; + // Calls GetNumber() |count| times and returns the values in reverse order. +@@ -95,6 +99,10 @@ class CPDF_StreamContentParser { + int GetInteger(uint32_t index) const { + return static_cast(GetNumber(index)); + } ++ // Makes a point from {GetNumber(index + 1), GetNumber(index)}. ++ CFX_PointF GetPoint(uint32_t index) const; ++ // Makes a matrix from {GetNumber(5), ..., GetNumber(0)}. ++ CFX_Matrix GetMatrix() const; + void OnOperator(ByteStringView op); + void AddTextObject(const ByteString* pStrs, + float fInitKerning, +@@ -105,22 +113,29 @@ class CPDF_StreamContentParser { + + void OnChangeTextMatrix(); + void ParsePathObject(); +- void AddPathPoint(float x, float y, FXPT_TYPE type, bool close); ++ void AddPathPoint(const CFX_PointF& point, CFX_Path::Point::Type type); ++ void AddPathPointAndClose(const CFX_PointF& point, ++ CFX_Path::Point::Type type); + void AddPathRect(float x, float y, float w, float h); +- void AddPathObject(int FillType, bool bStroke); +- CPDF_ImageObject* AddImage(RetainPtr pStream); +- CPDF_ImageObject* AddImage(uint32_t streamObjNum); +- CPDF_ImageObject* AddImage(const RetainPtr& pImage); +- +- void AddForm(CPDF_Stream* pStream); ++ void AddPathObject(CFX_FillRenderOptions::FillType fill_type, ++ RenderType render_type); ++ CPDF_ImageObject* AddImageFromStream(RetainPtr pStream, ++ const ByteString& name); ++ CPDF_ImageObject* AddImageFromStreamObjNum(uint32_t stream_obj_num, ++ const ByteString& name); ++ CPDF_ImageObject* AddLastImage(); ++ ++ void AddForm(RetainPtr pStream, const ByteString& name); + void SetGraphicStates(CPDF_PageObject* pObj, + bool bColor, + bool bText, + bool bGraph); + RetainPtr FindColorSpace(const ByteString& name); +- RetainPtr FindPattern(const ByteString& name, bool bShading); +- CPDF_Dictionary* FindResourceHolder(const ByteString& type); +- CPDF_Object* FindResourceObj(const ByteString& type, const ByteString& name); ++ RetainPtr FindPattern(const ByteString& name); ++ RetainPtr FindShading(const ByteString& name); ++ RetainPtr FindResourceHolder(const ByteString& type); ++ RetainPtr FindResourceObj(const ByteString& type, ++ const ByteString& name); + + // Takes ownership of |pImageObj|, returns unowned pointer to it. + CPDF_ImageObject* AddImageObject(std::unique_ptr pImageObj); +@@ -212,21 +227,18 @@ class CPDF_StreamContentParser { + const CFX_FloatRect m_BBox; + uint32_t m_ParamStartPos = 0; + uint32_t m_ParamCount = 0; +- UnownedPtr m_pSyntax; ++ std::unique_ptr m_pSyntax; + std::unique_ptr m_pCurStates; + std::stack> m_ContentMarksStack; + std::vector> m_ClipTextList; +- UnownedPtr m_pLastTextObject; +- std::vector m_PathPoints; +- float m_PathStartX = 0.0f; +- float m_PathStartY = 0.0f; +- float m_PathCurrentX = 0.0f; +- float m_PathCurrentY = 0.0f; +- uint8_t m_PathClipType = 0; ++ std::vector m_PathPoints; ++ CFX_PointF m_PathStart; ++ CFX_PointF m_PathCurrent; ++ CFX_FillRenderOptions::FillType m_PathClipType = ++ CFX_FillRenderOptions::FillType::kNoFill; + ByteString m_LastImageName; + RetainPtr m_pLastImage; + bool m_bColored = false; +- bool m_bResourceMissing = false; + std::vector> m_StateStack; + float m_Type3Data[6] = {0.0f}; + ContentParam m_ParamBuf[kParamBufSize]; +diff --git a/core/fpdfapi/page/cpdf_streamcontentparser_unittest.cpp b/core/fpdfapi/page/cpdf_streamcontentparser_unittest.cpp +index 0f4fc1e87..8198daff9 100644 +--- a/core/fpdfapi/page/cpdf_streamcontentparser_unittest.cpp ++++ b/core/fpdfapi/page/cpdf_streamcontentparser_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fpdfapi/page/cpdf_streamparser.cpp b/core/fpdfapi/page/cpdf_streamparser.cpp +index 25172a616..dd0a76d5b 100644 +--- a/core/fpdfapi/page/cpdf_streamparser.cpp ++++ b/core/fpdfapi/page/cpdf_streamparser.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,11 +6,10 @@ + + #include "core/fpdfapi/page/cpdf_streamparser.h" + +-#include ++#include + + #include + #include +-#include + #include + + #include "constants/stream_dict_common.h" +@@ -18,7 +17,6 @@ + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_boolean.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" +-#include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_null.h" + #include "core/fpdfapi/parser/cpdf_number.h" +@@ -26,13 +24,15 @@ + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" +-#include "core/fxcodec/fx_codec.h" + #include "core/fxcodec/jpeg/jpegmodule.h" + #include "core/fxcodec/scanlinedecoder.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_memory_wrappers.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/ptr_util.h" ++#include "core/fxcrt/span_util.h" ++#include "core/fxge/calculate_pitch.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -54,13 +54,18 @@ uint32_t DecodeAllScanlines(std::unique_ptr pDecoder) { + if (width <= 0 || height <= 0) + return FX_INVALID_OFFSET; + +- FX_SAFE_UINT32 size = fxcodec::CalculatePitch8(bpc, ncomps, width); ++ absl::optional maybe_size = ++ fxge::CalculatePitch8(bpc, ncomps, width); ++ if (!maybe_size.has_value()) ++ return FX_INVALID_OFFSET; ++ ++ FX_SAFE_UINT32 size = maybe_size.value(); + size *= height; + if (size.ValueOrDefault(0) == 0) + return FX_INVALID_OFFSET; + + for (int row = 0; row < height; ++row) { +- if (!pDecoder->GetScanline(row)) ++ if (pDecoder->GetScanline(row).empty()) + break; + } + return pDecoder->GetSrcOffset(); +@@ -70,37 +75,36 @@ uint32_t DecodeInlineStream(pdfium::span src_span, + int width, + int height, + const ByteString& decoder, +- const CPDF_Dictionary* pParam, ++ RetainPtr pParam, + uint32_t orig_size) { + // |decoder| should not be an abbreviation. +- ASSERT(decoder != "A85"); +- ASSERT(decoder != "AHx"); +- ASSERT(decoder != "CCF"); +- ASSERT(decoder != "DCT"); +- ASSERT(decoder != "Fl"); +- ASSERT(decoder != "LZW"); +- ASSERT(decoder != "RL"); ++ DCHECK(decoder != "A85"); ++ DCHECK(decoder != "AHx"); ++ DCHECK(decoder != "CCF"); ++ DCHECK(decoder != "DCT"); ++ DCHECK(decoder != "Fl"); ++ DCHECK(decoder != "LZW"); ++ DCHECK(decoder != "RL"); + + std::unique_ptr ignored_result; + uint32_t ignored_size; + if (decoder == "FlateDecode") { +- return FlateOrLZWDecode(false, src_span, pParam, orig_size, &ignored_result, +- &ignored_size); ++ return FlateOrLZWDecode(false, src_span, pParam.Get(), orig_size, ++ &ignored_result, &ignored_size); + } + if (decoder == "LZWDecode") { +- return FlateOrLZWDecode(true, src_span, pParam, 0, &ignored_result, ++ return FlateOrLZWDecode(true, src_span, pParam.Get(), 0, &ignored_result, + &ignored_size); + } + if (decoder == "DCTDecode") { +- std::unique_ptr pDecoder = +- fxcodec::ModuleMgr::GetInstance()->GetJpegModule()->CreateDecoder( +- src_span, width, height, 0, +- !pParam || pParam->GetIntegerFor("ColorTransform", 1)); ++ std::unique_ptr pDecoder = JpegModule::CreateDecoder( ++ src_span, width, height, 0, ++ !pParam || pParam->GetIntegerFor("ColorTransform", 1)); + return DecodeAllScanlines(std::move(pDecoder)); + } + if (decoder == "CCITTFaxDecode") { + std::unique_ptr pDecoder = +- CreateFaxDecoder(src_span, width, height, pParam); ++ CreateFaxDecoder(src_span, width, height, pParam.Get()); + return DecodeAllScanlines(std::move(pDecoder)); + } + +@@ -123,7 +127,7 @@ CPDF_StreamParser::CPDF_StreamParser(pdfium::span span, + const WeakPtr& pPool) + : m_pPool(pPool), m_pBuf(span) {} + +-CPDF_StreamParser::~CPDF_StreamParser() {} ++CPDF_StreamParser::~CPDF_StreamParser() = default; + + RetainPtr CPDF_StreamParser::ReadInlineStream( + CPDF_Document* pDoc, +@@ -136,13 +140,13 @@ RetainPtr CPDF_StreamParser::ReadInlineStream( + return nullptr; + + ByteString decoder; +- const CPDF_Dictionary* pParam = nullptr; +- CPDF_Object* pFilter = pDict->GetDirectObjectFor("Filter"); ++ RetainPtr pParam; ++ RetainPtr pFilter = pDict->GetDirectObjectFor("Filter"); + if (pFilter) { + const CPDF_Array* pArray = pFilter->AsArray(); + if (pArray) { +- decoder = pArray->GetStringAt(0); +- const CPDF_Array* pParams = ++ decoder = pArray->GetByteStringAt(0); ++ RetainPtr pParams = + pDict->GetArrayFor(pdfium::stream::kDecodeParms); + if (pParams) + pParam = pParams->GetDictAt(0); +@@ -161,36 +165,40 @@ RetainPtr CPDF_StreamParser::ReadInlineStream( + nComponents = pCS ? pCS->CountComponents() : 3; + bpc = pDict->GetIntegerFor("BitsPerComponent"); + } +- FX_SAFE_UINT32 size = fxcodec::CalculatePitch8(bpc, nComponents, width); ++ absl::optional maybe_size = ++ fxge::CalculatePitch8(bpc, nComponents, width); ++ if (!maybe_size.has_value()) ++ return nullptr; ++ ++ FX_SAFE_UINT32 size = maybe_size.value(); + size *= height; + if (!size.IsValid()) + return nullptr; + + uint32_t dwOrigSize = size.ValueOrDie(); +- std::unique_ptr pData; ++ DataVector data; + uint32_t dwStreamSize; + if (decoder.IsEmpty()) { + dwOrigSize = std::min(dwOrigSize, m_pBuf.size() - m_Pos); +- pData.reset(FX_Alloc(uint8_t, dwOrigSize)); +- auto copy_span = m_pBuf.subspan(m_Pos, dwOrigSize); +- memcpy(pData.get(), copy_span.data(), copy_span.size()); ++ auto src_span = m_pBuf.subspan(m_Pos, dwOrigSize); ++ data = DataVector(src_span.begin(), src_span.end()); + dwStreamSize = dwOrigSize; + m_Pos += dwOrigSize; + } else { + dwStreamSize = DecodeInlineStream(m_pBuf.subspan(m_Pos), width, height, +- decoder, pParam, dwOrigSize); ++ decoder, std::move(pParam), dwOrigSize); + if (!pdfium::base::IsValueInRangeForNumericType(dwStreamSize)) + return nullptr; + + uint32_t dwSavePos = m_Pos; + m_Pos += dwStreamSize; +- while (1) { ++ while (true) { + uint32_t dwPrevPos = m_Pos; +- CPDF_StreamParser::SyntaxType type = ParseNextElement(); +- if (type == CPDF_StreamParser::EndOfData) ++ ElementType type = ParseNextElement(); ++ if (type == ElementType::kEndOfData) + break; + +- if (type != CPDF_StreamParser::Keyword) { ++ if (type != ElementType::kKeyword) { + dwStreamSize += m_Pos - dwPrevPos; + continue; + } +@@ -201,27 +209,25 @@ RetainPtr CPDF_StreamParser::ReadInlineStream( + dwStreamSize += m_Pos - dwPrevPos; + } + m_Pos = dwSavePos; +- pData.reset(FX_Alloc(uint8_t, dwStreamSize)); +- auto copy_span = m_pBuf.subspan(m_Pos, dwStreamSize); +- memcpy(pData.get(), copy_span.data(), copy_span.size()); ++ auto src_span = m_pBuf.subspan(m_Pos, dwStreamSize); ++ data = DataVector(src_span.begin(), src_span.end()); + m_Pos += dwStreamSize; + } + pDict->SetNewFor("Length", static_cast(dwStreamSize)); +- return pdfium::MakeRetain(std::move(pData), dwStreamSize, +- std::move(pDict)); ++ return pdfium::MakeRetain(std::move(data), std::move(pDict)); + } + +-CPDF_StreamParser::SyntaxType CPDF_StreamParser::ParseNextElement() { ++CPDF_StreamParser::ElementType CPDF_StreamParser::ParseNextElement() { + m_pLastObj.Reset(); + m_WordSize = 0; + if (!PositionIsInBounds()) +- return EndOfData; ++ return ElementType::kEndOfData; + + uint8_t ch = m_pBuf[m_Pos++]; +- while (1) { ++ while (true) { + while (PDFCharIsWhitespace(ch)) { + if (!PositionIsInBounds()) +- return EndOfData; ++ return ElementType::kEndOfData; + + ch = m_pBuf[m_Pos++]; + } +@@ -229,9 +235,9 @@ CPDF_StreamParser::SyntaxType CPDF_StreamParser::ParseNextElement() { + if (ch != '%') + break; + +- while (1) { ++ while (true) { + if (!PositionIsInBounds()) +- return EndOfData; ++ return ElementType::kEndOfData; + + ch = m_pBuf[m_Pos++]; + if (PDFCharIsLineEnding(ch)) +@@ -242,11 +248,11 @@ CPDF_StreamParser::SyntaxType CPDF_StreamParser::ParseNextElement() { + if (PDFCharIsDelimiter(ch) && ch != '/') { + m_Pos--; + m_pLastObj = ReadNextObject(false, false, 0); +- return Others; ++ return ElementType::kOther; + } + + bool bIsNumber = true; +- while (1) { ++ while (true) { + if (m_WordSize < kMaxWordLength) + m_WordBuffer[m_WordSize++] = ch; + +@@ -266,27 +272,27 @@ CPDF_StreamParser::SyntaxType CPDF_StreamParser::ParseNextElement() { + + m_WordBuffer[m_WordSize] = 0; + if (bIsNumber) +- return Number; ++ return ElementType::kNumber; + + if (m_WordBuffer[0] == '/') +- return Name; ++ return ElementType::kName; + + if (m_WordSize == 4) { +- if (WordBufferMatches(kTrue)) { ++ if (GetWord() == kTrue) { + m_pLastObj = pdfium::MakeRetain(true); +- return Others; ++ return ElementType::kOther; + } +- if (WordBufferMatches(kNull)) { ++ if (GetWord() == kNull) { + m_pLastObj = pdfium::MakeRetain(); +- return Others; ++ return ElementType::kOther; + } + } else if (m_WordSize == 5) { +- if (WordBufferMatches(kFalse)) { ++ if (GetWord() == kFalse) { + m_pLastObj = pdfium::MakeRetain(false); +- return Others; ++ return ElementType::kOther; + } + } +- return Keyword; ++ return ElementType::kKeyword; + } + + RetainPtr CPDF_StreamParser::ReadNextObject( +@@ -301,14 +307,12 @@ RetainPtr CPDF_StreamParser::ReadNextObject( + + if (bIsNumber) { + m_WordBuffer[m_WordSize] = 0; +- return pdfium::MakeRetain( +- ByteStringView(m_WordBuffer, m_WordSize)); ++ return pdfium::MakeRetain(GetWord()); + } + + int first_char = m_WordBuffer[0]; + if (first_char == '/') { +- ByteString name = +- PDF_NameDecode(ByteStringView(m_WordBuffer + 1, m_WordSize - 1)); ++ ByteString name = PDF_NameDecode(GetWord().Substr(1)); + return pdfium::MakeRetain(m_pPool, name); + } + +@@ -322,7 +326,7 @@ RetainPtr CPDF_StreamParser::ReadNextObject( + return pdfium::MakeRetain(m_pPool, ReadHexString(), true); + + auto pDict = pdfium::MakeRetain(m_pPool); +- while (1) { ++ while (true) { + GetNextWord(bIsNumber); + if (m_WordSize == 2 && m_WordBuffer[0] == '>') + break; +@@ -330,15 +334,13 @@ RetainPtr CPDF_StreamParser::ReadNextObject( + if (!m_WordSize || m_WordBuffer[0] != '/') + return nullptr; + +- ByteString key = +- PDF_NameDecode(ByteStringView(m_WordBuffer + 1, m_WordSize - 1)); ++ ByteString key = PDF_NameDecode(GetWord().Substr(1)); + RetainPtr pObj = + ReadNextObject(true, bInArray, dwRecursionLevel + 1); + if (!pObj) + return nullptr; + +- if (!key.IsEmpty()) +- pDict->SetFor(key, std::move(pObj)); ++ pDict->SetFor(key, std::move(pObj)); + } + return pDict; + } +@@ -348,11 +350,11 @@ RetainPtr CPDF_StreamParser::ReadNextObject( + return nullptr; + + auto pArray = pdfium::MakeRetain(); +- while (1) { ++ while (true) { + RetainPtr pObj = + ReadNextObject(bAllowNestedArray, true, dwRecursionLevel + 1); + if (pObj) { +- pArray->Add(std::move(pObj)); ++ pArray->Append(std::move(pObj)); + continue; + } + if (!m_WordSize || m_WordBuffer[0] == ']') +@@ -361,11 +363,11 @@ RetainPtr CPDF_StreamParser::ReadNextObject( + return pArray; + } + +- if (WordBufferMatches(kFalse)) ++ if (GetWord() == kFalse) + return pdfium::MakeRetain(false); +- if (WordBufferMatches(kTrue)) ++ if (GetWord() == kTrue) + return pdfium::MakeRetain(true); +- if (WordBufferMatches(kNull)) ++ if (GetWord() == kNull) + return pdfium::MakeRetain(); + return nullptr; + } +@@ -378,7 +380,7 @@ void CPDF_StreamParser::GetNextWord(bool& bIsNumber) { + return; + + uint8_t ch = m_pBuf[m_Pos++]; +- while (1) { ++ while (true) { + while (PDFCharIsWhitespace(ch)) { + if (!PositionIsInBounds()) { + return; +@@ -389,7 +391,7 @@ void CPDF_StreamParser::GetNextWord(bool& bIsNumber) { + if (ch != '%') + break; + +- while (1) { ++ while (true) { + if (!PositionIsInBounds()) + return; + ch = m_pBuf[m_Pos++]; +@@ -402,7 +404,7 @@ void CPDF_StreamParser::GetNextWord(bool& bIsNumber) { + bIsNumber = false; + m_WordBuffer[m_WordSize++] = ch; + if (ch == '/') { +- while (1) { ++ while (true) { + if (!PositionIsInBounds()) + return; + ch = m_pBuf[m_Pos++]; +@@ -433,7 +435,7 @@ void CPDF_StreamParser::GetNextWord(bool& bIsNumber) { + return; + } + +- while (1) { ++ while (true) { + if (m_WordSize < kMaxWordLength) + m_WordBuffer[m_WordSize++] = ch; + if (!PDFCharIsNumeric(ch)) +@@ -453,32 +455,27 @@ ByteString CPDF_StreamParser::ReadString() { + if (!PositionIsInBounds()) + return ByteString(); + +- uint8_t ch = m_pBuf[m_Pos++]; +- std::ostringstream buf; ++ ByteString buf; + int parlevel = 0; + int status = 0; + int iEscCode = 0; +- while (1) { ++ uint8_t ch = m_pBuf[m_Pos++]; ++ while (true) { + switch (status) { + case 0: + if (ch == ')') { + if (parlevel == 0) { +- if (buf.tellp() <= 0) +- return ByteString(); +- +- return ByteString( +- buf.str().c_str(), +- std::min(static_cast(buf.tellp()), kMaxStringLength)); ++ return buf.First(std::min(buf.GetLength(), kMaxStringLength)); + } + parlevel--; +- buf << ')'; ++ buf += ')'; + } else if (ch == '(') { + parlevel++; +- buf << '('; ++ buf += '('; + } else if (ch == '\\') { + status = 1; + } else { +- buf << static_cast(ch); ++ buf += static_cast(ch); + } + break; + case 1: +@@ -494,17 +491,17 @@ ByteString CPDF_StreamParser::ReadString() { + if (ch == '\n') { + // Do nothing. + } else if (ch == 'n') { +- buf << '\n'; ++ buf += '\n'; + } else if (ch == 'r') { +- buf << '\r'; ++ buf += '\r'; + } else if (ch == 't') { +- buf << '\t'; ++ buf += '\t'; + } else if (ch == 'b') { +- buf << '\b'; ++ buf += '\b'; + } else if (ch == 'f') { +- buf << '\f'; ++ buf += '\f'; + } else { +- buf << static_cast(ch); ++ buf += static_cast(ch); + } + status = 0; + break; +@@ -514,7 +511,7 @@ ByteString CPDF_StreamParser::ReadString() { + iEscCode * 8 + FXSYS_DecimalCharToInt(static_cast(ch)); + status = 3; + } else { +- buf << static_cast(iEscCode); ++ buf += static_cast(iEscCode); + status = 0; + continue; + } +@@ -523,10 +520,10 @@ ByteString CPDF_StreamParser::ReadString() { + if (FXSYS_IsOctalDigit(ch)) { + iEscCode = + iEscCode * 8 + FXSYS_DecimalCharToInt(static_cast(ch)); +- buf << static_cast(iEscCode); ++ buf += static_cast(iEscCode); + status = 0; + } else { +- buf << static_cast(iEscCode); ++ buf += static_cast(iEscCode); + status = 0; + continue; + } +@@ -538,26 +535,17 @@ ByteString CPDF_StreamParser::ReadString() { + break; + } + if (!PositionIsInBounds()) +- break; ++ return buf.First(std::min(buf.GetLength(), kMaxStringLength)); + + ch = m_pBuf[m_Pos++]; + } +- if (PositionIsInBounds()) +- ++m_Pos; +- +- if (buf.tellp() <= 0) +- return ByteString(); +- +- return ByteString( +- buf.str().c_str(), +- std::min(static_cast(buf.tellp()), kMaxStringLength)); + } + + ByteString CPDF_StreamParser::ReadHexString() { + if (!PositionIsInBounds()) + return ByteString(); + +- std::ostringstream buf; ++ ByteString buf; + bool bFirst = true; + int code = 0; + while (PositionIsInBounds()) { +@@ -565,7 +553,7 @@ ByteString CPDF_StreamParser::ReadHexString() { + if (ch == '>') + break; + +- if (!std::isxdigit(ch)) ++ if (!isxdigit(ch)) + continue; + + int val = FXSYS_HexCharToInt(ch); +@@ -573,26 +561,16 @@ ByteString CPDF_StreamParser::ReadHexString() { + code = val * 16; + } else { + code += val; +- buf << static_cast(code); ++ buf += static_cast(code); + } + bFirst = !bFirst; + } + if (!bFirst) +- buf << static_cast(code); ++ buf += static_cast(code); + +- if (buf.tellp() <= 0) +- return ByteString(); +- +- return ByteString( +- buf.str().c_str(), +- std::min(static_cast(buf.tellp()), kMaxStringLength)); ++ return buf.First(std::min(buf.GetLength(), kMaxStringLength)); + } + + bool CPDF_StreamParser::PositionIsInBounds() const { + return m_Pos < m_pBuf.size(); + } +- +-bool CPDF_StreamParser::WordBufferMatches(const char* pWord) const { +- const size_t iLength = strlen(pWord); +- return m_WordSize == iLength && memcmp(m_WordBuffer, pWord, iLength) == 0; +-} +diff --git a/core/fpdfapi/page/cpdf_streamparser.h b/core/fpdfapi/page/cpdf_streamparser.h +index 6051b8cb6..1a0f1159d 100644 +--- a/core/fpdfapi/page/cpdf_streamparser.h ++++ b/core/fpdfapi/page/cpdf_streamparser.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,28 +7,26 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_STREAMPARSER_H_ + #define CORE_FPDFAPI_PAGE_CPDF_STREAMPARSER_H_ + +-#include +-#include +- +-#include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/string_pool_template.h" + #include "core/fxcrt/weak_ptr.h" + #include "third_party/base/span.h" + + class CPDF_Dictionary; ++class CPDF_Document; + class CPDF_Object; + class CPDF_Stream; + + class CPDF_StreamParser { + public: +- enum SyntaxType { EndOfData, Number, Keyword, Name, Others }; ++ enum ElementType { kEndOfData, kNumber, kKeyword, kName, kOther }; + + explicit CPDF_StreamParser(pdfium::span span); + CPDF_StreamParser(pdfium::span span, + const WeakPtr& pPool); + ~CPDF_StreamParser(); + +- SyntaxType ParseNextElement(); ++ ElementType ParseNextElement(); + ByteStringView GetWord() const { + return ByteStringView(m_WordBuffer, m_WordSize); + } +@@ -44,20 +42,19 @@ class CPDF_StreamParser { + + private: + friend class cpdf_streamparser_ReadHexString_Test; +- static const uint32_t kMaxWordLength = 255; ++ static constexpr uint32_t kMaxWordLength = 255; + + void GetNextWord(bool& bIsNumber); + ByteString ReadString(); + ByteString ReadHexString(); + bool PositionIsInBounds() const; +- bool WordBufferMatches(const char* pWord) const; + + uint32_t m_Pos = 0; // Current byte position within |m_pBuf|. + uint32_t m_WordSize = 0; // Current byte position within |m_WordBuffer|. + WeakPtr m_pPool; + RetainPtr m_pLastObj; + pdfium::span m_pBuf; +- uint8_t m_WordBuffer[kMaxWordLength + 1]; // Include space for NUL. ++ uint8_t m_WordBuffer[kMaxWordLength + 1] = {}; // Include space for NUL. + }; + + #endif // CORE_FPDFAPI_PAGE_CPDF_STREAMPARSER_H_ +diff --git a/core/fpdfapi/page/cpdf_streamparser_unittest.cpp b/core/fpdfapi/page/cpdf_streamparser_unittest.cpp +index 64bb57c78..c9af44b20 100644 +--- a/core/fpdfapi/page/cpdf_streamparser_unittest.cpp ++++ b/core/fpdfapi/page/cpdf_streamparser_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fpdfapi/page/cpdf_textobject.cpp b/core/fpdfapi/page/cpdf_textobject.cpp +index 077e5ed7b..a063541c7 100644 +--- a/core/fpdfapi/page/cpdf_textobject.cpp ++++ b/core/fpdfapi/page/cpdf_textobject.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,55 +7,62 @@ + #include "core/fpdfapi/page/cpdf_textobject.h" + + #include +-#include + + #include "core/fpdfapi/font/cpdf_cidfont.h" + #include "core/fpdfapi/font/cpdf_font.h" +-#include "third_party/base/ptr_util.h" ++#include "core/fxcrt/fx_coordinates.h" ++#include "third_party/base/check.h" ++#include "third_party/base/span.h" + + #define ISLATINWORD(u) (u != 0x20 && u <= 0x28FF) + +-CPDF_TextObjectItem::CPDF_TextObjectItem() : m_CharCode(0) {} ++namespace { + +-CPDF_TextObjectItem::~CPDF_TextObjectItem() = default; ++bool IsVertWritingCIDFont(const CPDF_CIDFont* font) { ++ return font && font->IsVertWriting(); ++} ++ ++} // namespace ++ ++CPDF_TextObject::Item::Item() = default; ++ ++CPDF_TextObject::Item::Item(const Item& that) = default; ++ ++CPDF_TextObject::Item::~Item() = default; + + CPDF_TextObject::CPDF_TextObject(int32_t content_stream) + : CPDF_PageObject(content_stream) {} + + CPDF_TextObject::CPDF_TextObject() : CPDF_TextObject(kNoContentStream) {} + +-CPDF_TextObject::~CPDF_TextObject() { +- // Move m_CharCodes to a local variable so it will be captured in crash dumps, +- // to help with investigating crbug.com/782215. +- auto char_codes_copy = std::move(m_CharCodes); +-} ++CPDF_TextObject::~CPDF_TextObject() = default; + + size_t CPDF_TextObject::CountItems() const { + return m_CharCodes.size(); + } + +-void CPDF_TextObject::GetItemInfo(size_t index, +- CPDF_TextObjectItem* pInfo) const { +- ASSERT(index < m_CharCodes.size()); +- pInfo->m_CharCode = m_CharCodes[index]; +- pInfo->m_Origin = CFX_PointF(index > 0 ? m_CharPos[index - 1] : 0, 0); +- if (pInfo->m_CharCode == CPDF_Font::kInvalidCharCode) +- return; ++CPDF_TextObject::Item CPDF_TextObject::GetItemInfo(size_t index) const { ++ DCHECK(index < m_CharCodes.size()); + +- RetainPtr pFont = GetFont(); +- if (!pFont->IsCIDFont() || !pFont->AsCIDFont()->IsVertWriting()) +- return; ++ Item info; ++ info.m_CharCode = m_CharCodes[index]; ++ info.m_Origin = CFX_PointF(index > 0 ? m_CharPos[index - 1] : 0, 0); ++ if (info.m_CharCode == CPDF_Font::kInvalidCharCode) ++ return info; + +- uint16_t CID = pFont->AsCIDFont()->CIDFromCharCode(pInfo->m_CharCode); +- pInfo->m_Origin = CFX_PointF(0, pInfo->m_Origin.x); ++ RetainPtr pFont = GetFont(); ++ const CPDF_CIDFont* pCIDFont = pFont->AsCIDFont(); ++ if (!IsVertWritingCIDFont(pCIDFont)) ++ return info; + +- short vx; +- short vy; +- pFont->AsCIDFont()->GetVertOrigin(CID, vx, vy); ++ uint16_t cid = pCIDFont->CIDFromCharCode(info.m_CharCode); ++ info.m_Origin = CFX_PointF(0, info.m_Origin.x); + ++ CFX_Point16 vertical_origin = pCIDFont->GetVertOrigin(cid); + float fontsize = GetFontSize(); +- pInfo->m_Origin.x -= fontsize * vx / 1000; +- pInfo->m_Origin.y -= fontsize * vy / 1000; ++ info.m_Origin.x -= fontsize * vertical_origin.x / 1000; ++ info.m_Origin.y -= fontsize * vertical_origin.y / 1000; ++ return info; + } + + size_t CPDF_TextObject::CountChars() const { +@@ -67,38 +74,28 @@ size_t CPDF_TextObject::CountChars() const { + return count; + } + +-void CPDF_TextObject::GetCharInfo(size_t index, +- uint32_t* charcode, +- float* kerning) const { ++uint32_t CPDF_TextObject::GetCharCode(size_t index) const { + size_t count = 0; +- for (size_t i = 0; i < m_CharCodes.size(); ++i) { +- if (m_CharCodes[i] == CPDF_Font::kInvalidCharCode) ++ for (uint32_t code : m_CharCodes) { ++ if (code == CPDF_Font::kInvalidCharCode) + continue; + if (count++ != index) + continue; +- *charcode = m_CharCodes[i]; +- if (i == m_CharCodes.size() - 1 || +- m_CharCodes[i + 1] != CPDF_Font::kInvalidCharCode) { +- *kerning = 0; +- } else { +- *kerning = m_CharPos[i]; +- } +- return; ++ return code; + } ++ return CPDF_Font::kInvalidCharCode; + } + +-void CPDF_TextObject::GetCharInfo(size_t index, +- CPDF_TextObjectItem* pInfo) const { ++CPDF_TextObject::Item CPDF_TextObject::GetCharInfo(size_t index) const { + size_t count = 0; + for (size_t i = 0; i < m_CharCodes.size(); ++i) { + uint32_t charcode = m_CharCodes[i]; + if (charcode == CPDF_Font::kInvalidCharCode) + continue; +- if (count++ != index) +- continue; +- GetItemInfo(i, pInfo); +- break; ++ if (count++ == index) ++ return GetItemInfo(i); + } ++ return Item(); + } + + int CPDF_TextObject::CountWords() const { +@@ -106,9 +103,7 @@ int CPDF_TextObject::CountWords() const { + bool bInLatinWord = false; + int nWords = 0; + for (size_t i = 0, sz = CountChars(); i < sz; ++i) { +- uint32_t charcode = CPDF_Font::kInvalidCharCode; +- float unused_kerning; +- GetCharInfo(i, &charcode, &unused_kerning); ++ uint32_t charcode = GetCharCode(i); + + WideString swUnicode = pFont->UnicodeFromCharCode(charcode); + uint16_t unicode = 0; +@@ -133,9 +128,7 @@ WideString CPDF_TextObject::GetWordString(int nWordIndex) const { + int nWords = 0; + bool bInLatinWord = false; + for (size_t i = 0, sz = CountChars(); i < sz; ++i) { +- uint32_t charcode = CPDF_Font::kInvalidCharCode; +- float unused_kerning; +- GetCharInfo(i, &charcode, &unused_kerning); ++ uint32_t charcode = GetCharCode(i); + + WideString swUnicode = pFont->UnicodeFromCharCode(charcode); + uint16_t unicode = 0; +@@ -155,7 +148,7 @@ WideString CPDF_TextObject::GetWordString(int nWordIndex) const { + } + + std::unique_ptr CPDF_TextObject::Clone() const { +- auto obj = pdfium::MakeUnique(); ++ auto obj = std::make_unique(); + obj->CopyData(this); + obj->m_CharCodes = m_CharCodes; + obj->m_CharPos = m_CharPos; +@@ -164,19 +157,11 @@ std::unique_ptr CPDF_TextObject::Clone() const { + } + + CPDF_PageObject::Type CPDF_TextObject::GetType() const { +- return TEXT; ++ return Type::kText; + } + + void CPDF_TextObject::Transform(const CFX_Matrix& matrix) { +- CFX_Matrix text_matrix = GetTextMatrix() * matrix; +- +- float* pTextMatrix = m_TextState.GetMutableMatrix(); +- pTextMatrix[0] = text_matrix.a; +- pTextMatrix[1] = text_matrix.c; +- pTextMatrix[2] = text_matrix.b; +- pTextMatrix[3] = text_matrix.d; +- m_Pos = CFX_PointF(text_matrix.e, text_matrix.f); +- CalcPositionData(0); ++ SetTextMatrix(GetTextMatrix() * matrix); + SetDirty(true); + } + +@@ -193,21 +178,33 @@ const CPDF_TextObject* CPDF_TextObject::AsText() const { + } + + CFX_Matrix CPDF_TextObject::GetTextMatrix() const { +- const float* pTextMatrix = m_TextState.GetMatrix(); ++ pdfium::span pTextMatrix = m_TextState.GetMatrix(); + return CFX_Matrix(pTextMatrix[0], pTextMatrix[2], pTextMatrix[1], + pTextMatrix[3], m_Pos.x, m_Pos.y); + } + ++void CPDF_TextObject::SetTextMatrix(const CFX_Matrix& matrix) { ++ pdfium::span pTextMatrix = m_TextState.GetMutableMatrix(); ++ pTextMatrix[0] = matrix.a; ++ pTextMatrix[1] = matrix.c; ++ pTextMatrix[2] = matrix.b; ++ pTextMatrix[3] = matrix.d; ++ m_Pos = CFX_PointF(matrix.e, matrix.f); ++ CalcPositionDataInternal(GetFont()); ++} ++ + void CPDF_TextObject::SetSegments(const ByteString* pStrs, + const std::vector& kernings, + size_t nSegs) { ++ CHECK(nSegs); + m_CharCodes.clear(); + m_CharPos.clear(); + RetainPtr pFont = GetFont(); +- int nChars = 0; ++ size_t nChars = nSegs - 1; + for (size_t i = 0; i < nSegs; ++i) + nChars += pFont->CountChar(pStrs[i].AsStringView()); +- nChars += nSegs - 1; ++ ++ CHECK(nChars); + m_CharCodes.resize(nChars); + m_CharPos.resize(nChars - 1); + size_t index = 0; +@@ -215,7 +212,7 @@ void CPDF_TextObject::SetSegments(const ByteString* pStrs, + ByteStringView segment = pStrs[i].AsStringView(); + size_t offset = 0; + while (offset < segment.GetLength()) { +- ASSERT(index < m_CharCodes.size()); ++ DCHECK(index < m_CharCodes.size()); + m_CharCodes[index++] = pFont->GetNextChar(segment, &offset); + } + if (i != nSegs - 1) { +@@ -227,22 +224,19 @@ void CPDF_TextObject::SetSegments(const ByteString* pStrs, + + void CPDF_TextObject::SetText(const ByteString& str) { + SetSegments(&str, std::vector(), 1); +- RecalcPositionData(); ++ CalcPositionDataInternal(GetFont()); + SetDirty(true); + } + + float CPDF_TextObject::GetCharWidth(uint32_t charcode) const { +- float fontsize = GetFontSize() / 1000; ++ const float fontsize = GetFontSize() / 1000; + RetainPtr pFont = GetFont(); +- bool bVertWriting = false; +- CPDF_CIDFont* pCIDFont = pFont->AsCIDFont(); +- if (pCIDFont) +- bVertWriting = pCIDFont->IsVertWriting(); +- if (!bVertWriting) ++ const CPDF_CIDFont* pCIDFont = pFont->AsCIDFont(); ++ if (!IsVertWritingCIDFont(pCIDFont)) + return pFont->GetCharWidthF(charcode) * fontsize; + +- uint16_t CID = pCIDFont->CIDFromCharCode(charcode); +- return pCIDFont->GetVertWidth(CID) * fontsize; ++ uint16_t cid = pCIDFont->CIDFromCharCode(charcode); ++ return pCIDFont->GetVertWidth(cid) * fontsize; + } + + RetainPtr CPDF_TextObject::GetFont() const { +@@ -257,21 +251,32 @@ TextRenderingMode CPDF_TextObject::GetTextRenderMode() const { + return m_TextState.GetTextMode(); + } + ++void CPDF_TextObject::SetTextRenderMode(TextRenderingMode mode) { ++ m_TextState.SetTextMode(mode); ++ SetDirty(true); ++} ++ + CFX_PointF CPDF_TextObject::CalcPositionData(float horz_scale) { +- float curpos = 0; +- float min_x = 10000 * 1.0f; +- float max_x = -10000 * 1.0f; +- float min_y = 10000 * 1.0f; +- float max_y = -10000 * 1.0f; + RetainPtr pFont = GetFont(); +- bool bVertWriting = false; +- CPDF_CIDFont* pCIDFont = pFont->AsCIDFont(); +- if (pCIDFont) +- bVertWriting = pCIDFont->IsVertWriting(); ++ const float curpos = CalcPositionDataInternal(pFont); ++ if (IsVertWritingCIDFont(pFont->AsCIDFont())) ++ return {0, curpos}; ++ return {curpos * horz_scale, 0}; ++} ++ ++float CPDF_TextObject::CalcPositionDataInternal( ++ const RetainPtr& pFont) { ++ float curpos = 0; ++ float min_x = 10000.0f; ++ float max_x = -10000.0f; ++ float min_y = 10000.0f; ++ float max_y = -10000.0f; ++ const CPDF_CIDFont* pCIDFont = pFont->AsCIDFont(); ++ const bool bVertWriting = IsVertWritingCIDFont(pCIDFont); ++ const float fontsize = GetFontSize(); + +- float fontsize = GetFontSize(); + for (size_t i = 0; i < m_CharCodes.size(); ++i) { +- uint32_t charcode = m_CharCodes[i]; ++ const uint32_t charcode = m_CharCodes[i]; + if (i > 0) { + if (charcode == CPDF_Font::kInvalidCharCode) { + curpos -= (m_CharPos[i - 1] * fontsize) / 1000; +@@ -282,34 +287,29 @@ CFX_PointF CPDF_TextObject::CalcPositionData(float horz_scale) { + + FX_RECT char_rect = pFont->GetCharBBox(charcode); + float charwidth; +- if (!bVertWriting) { ++ if (bVertWriting) { ++ uint16_t cid = pCIDFont->CIDFromCharCode(charcode); ++ CFX_Point16 vertical_origin = pCIDFont->GetVertOrigin(cid); ++ char_rect.Offset(-vertical_origin.x, -vertical_origin.y); ++ min_x = std::min( ++ min_x, static_cast(std::min(char_rect.left, char_rect.right))); ++ max_x = std::max( ++ max_x, static_cast(std::max(char_rect.left, char_rect.right))); ++ const float char_top = curpos + char_rect.top * fontsize / 1000; ++ const float char_bottom = curpos + char_rect.bottom * fontsize / 1000; ++ min_y = std::min(min_y, std::min(char_top, char_bottom)); ++ max_y = std::max(max_y, std::max(char_top, char_bottom)); ++ charwidth = pCIDFont->GetVertWidth(cid) * fontsize / 1000; ++ } else { + min_y = std::min( + min_y, static_cast(std::min(char_rect.top, char_rect.bottom))); + max_y = std::max( + max_y, static_cast(std::max(char_rect.top, char_rect.bottom))); +- float char_left = curpos + char_rect.left * fontsize / 1000; +- float char_right = curpos + char_rect.right * fontsize / 1000; ++ const float char_left = curpos + char_rect.left * fontsize / 1000; ++ const float char_right = curpos + char_rect.right * fontsize / 1000; + min_x = std::min(min_x, std::min(char_left, char_right)); + max_x = std::max(max_x, std::max(char_left, char_right)); + charwidth = pFont->GetCharWidthF(charcode) * fontsize / 1000; +- } else { +- uint16_t CID = pCIDFont->CIDFromCharCode(charcode); +- short vx; +- short vy; +- pCIDFont->GetVertOrigin(CID, vx, vy); +- char_rect.left -= vx; +- char_rect.right -= vx; +- char_rect.top -= vy; +- char_rect.bottom -= vy; +- min_x = std::min( +- min_x, static_cast(std::min(char_rect.left, char_rect.right))); +- max_x = std::max( +- max_x, static_cast(std::max(char_rect.left, char_rect.right))); +- float char_top = curpos + char_rect.top * fontsize / 1000; +- float char_bottom = curpos + char_rect.bottom * fontsize / 1000; +- min_y = std::min(min_y, std::min(char_top, char_bottom)); +- max_y = std::max(max_y, std::max(char_top, char_bottom)); +- charwidth = pCIDFont->GetVertWidth(CID) * fontsize / 1000; + } + curpos += charwidth; + if (charcode == ' ' && (!pCIDFont || pCIDFont->GetCharSize(' ') == 1)) +@@ -318,31 +318,23 @@ CFX_PointF CPDF_TextObject::CalcPositionData(float horz_scale) { + curpos += m_TextState.GetCharSpace(); + } + +- CFX_PointF ret; + if (bVertWriting) { +- ret.y = curpos; + min_x = min_x * fontsize / 1000; + max_x = max_x * fontsize / 1000; + } else { +- ret.x = curpos * horz_scale; + min_y = min_y * fontsize / 1000; + max_y = max_y * fontsize / 1000; + } +- SetRect( +- GetTextMatrix().TransformRect(CFX_FloatRect(min_x, min_y, max_x, max_y))); +- +- if (!TextRenderingModeIsStrokeMode(m_TextState.GetTextMode())) +- return ret; +- +- float half_width = m_GraphState.GetLineWidth() / 2; +- m_Rect.left -= half_width; +- m_Rect.right += half_width; +- m_Rect.top += half_width; +- m_Rect.bottom -= half_width; + +- return ret; +-} ++ SetOriginalRect(CFX_FloatRect(min_x, min_y, max_x, max_y)); ++ CFX_FloatRect rect = GetTextMatrix().TransformRect(GetOriginalRect()); ++ if (TextRenderingModeIsStrokeMode(m_TextState.GetTextMode())) { ++ // TODO(crbug.com/pdfium/1840): Does the original rect need a similar ++ // adjustment? ++ const float half_width = m_GraphState.GetLineWidth() / 2; ++ rect.Inflate(half_width, half_width); ++ } ++ SetRect(rect); + +-void CPDF_TextObject::RecalcPositionData() { +- CalcPositionData(1); ++ return curpos; + } +diff --git a/core/fpdfapi/page/cpdf_textobject.h b/core/fpdfapi/page/cpdf_textobject.h +index ad9918ecd..4a936bcc5 100644 +--- a/core/fpdfapi/page/cpdf_textobject.h ++++ b/core/fpdfapi/page/cpdf_textobject.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,30 +7,33 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_TEXTOBJECT_H_ + #define CORE_FPDFAPI_PAGE_CPDF_TEXTOBJECT_H_ + ++#include ++#include ++ + #include + #include + + #include "core/fpdfapi/page/cpdf_pageobject.h" ++#include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" + +-class CPDF_TextObjectItem { ++class CPDF_TextObject final : public CPDF_PageObject { + public: +- CPDF_TextObjectItem(); +- ~CPDF_TextObjectItem(); ++ struct Item { ++ Item(); ++ Item(const Item& that); ++ ~Item(); + +- uint32_t m_CharCode; +- CFX_PointF m_Origin; +-}; ++ uint32_t m_CharCode = 0; ++ CFX_PointF m_Origin; ++ }; + +-class CPDF_TextObject final : public CPDF_PageObject { +- public: + explicit CPDF_TextObject(int32_t content_stream); + CPDF_TextObject(); + ~CPDF_TextObject() override; + +- // CPDF_PageObject ++ // CPDF_PageObject: + Type GetType() const override; + void Transform(const CFX_Matrix& matrix) override; + bool IsText() const override; +@@ -40,11 +43,11 @@ class CPDF_TextObject final : public CPDF_PageObject { + std::unique_ptr Clone() const; + + size_t CountItems() const; +- void GetItemInfo(size_t index, CPDF_TextObjectItem* pInfo) const; ++ Item GetItemInfo(size_t index) const; + + size_t CountChars() const; +- void GetCharInfo(size_t index, uint32_t* charcode, float* kerning) const; +- void GetCharInfo(size_t index, CPDF_TextObjectItem* pInfo) const; ++ uint32_t GetCharCode(size_t index) const; ++ Item GetCharInfo(size_t index) const; + float GetCharWidth(uint32_t charcode) const; + int CountWords() const; + WideString GetWordString(int nWordIndex) const; +@@ -56,21 +59,25 @@ class CPDF_TextObject final : public CPDF_PageObject { + float GetFontSize() const; + + TextRenderingMode GetTextRenderMode() const; ++ void SetTextRenderMode(TextRenderingMode mode); + + void SetText(const ByteString& str); + void SetPosition(const CFX_PointF& pos) { m_Pos = pos; } + +- void RecalcPositionData(); +- + const std::vector& GetCharCodes() const { return m_CharCodes; } + const std::vector& GetCharPositions() const { return m_CharPos; } + ++ // Caller is expected to call SetDirty(true) when done changing the object. ++ void SetTextMatrix(const CFX_Matrix& matrix); ++ + void SetSegments(const ByteString* pStrs, + const std::vector& kernings, + size_t nSegs); + CFX_PointF CalcPositionData(float horz_scale); + + private: ++ float CalcPositionDataInternal(const RetainPtr& pFont); ++ + CFX_PointF m_Pos; + std::vector m_CharCodes; + std::vector m_CharPos; +diff --git a/core/fpdfapi/page/cpdf_textstate.cpp b/core/fpdfapi/page/cpdf_textstate.cpp +index b8019d53c..6e48b8734 100644 +--- a/core/fpdfapi/page/cpdf_textstate.cpp ++++ b/core/fpdfapi/page/cpdf_textstate.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,9 +6,12 @@ + + #include "core/fpdfapi/page/cpdf_textstate.h" + ++#include ++ ++#include ++ + #include "core/fpdfapi/font/cpdf_font.h" + #include "core/fpdfapi/page/cpdf_docpagedata.h" +-#include "core/fpdfapi/parser/cpdf_document.h" + + CPDF_TextState::CPDF_TextState() = default; + +@@ -22,8 +25,8 @@ RetainPtr CPDF_TextState::GetFont() const { + return m_Ref.GetObject()->m_pFont; + } + +-void CPDF_TextState::SetFont(const RetainPtr& pFont) { +- m_Ref.GetPrivateCopy()->SetFont(pFont); ++void CPDF_TextState::SetFont(RetainPtr pFont) { ++ m_Ref.GetPrivateCopy()->SetFont(std::move(pFont)); + } + + float CPDF_TextState::GetFontSize() const { +@@ -34,11 +37,11 @@ void CPDF_TextState::SetFontSize(float size) { + m_Ref.GetPrivateCopy()->m_FontSize = size; + } + +-const float* CPDF_TextState::GetMatrix() const { ++pdfium::span CPDF_TextState::GetMatrix() const { + return m_Ref.GetObject()->m_Matrix; + } + +-float* CPDF_TextState::GetMutableMatrix() { ++pdfium::span CPDF_TextState::GetMutableMatrix() { + return m_Ref.GetPrivateCopy()->m_Matrix; + } + +@@ -70,26 +73,15 @@ void CPDF_TextState::SetTextMode(TextRenderingMode mode) { + m_Ref.GetPrivateCopy()->m_TextMode = mode; + } + +-const float* CPDF_TextState::GetCTM() const { ++pdfium::span CPDF_TextState::GetCTM() const { + return m_Ref.GetObject()->m_CTM; + } + +-float* CPDF_TextState::GetMutableCTM() { ++pdfium::span CPDF_TextState::GetMutableCTM() { + return m_Ref.GetPrivateCopy()->m_CTM; + } + +-CPDF_TextState::TextData::TextData() +- : m_pFont(nullptr), +- m_pDocument(nullptr), +- m_FontSize(1.0f), +- m_CharSpace(0), +- m_WordSpace(0), +- m_TextMode(TextRenderingMode::MODE_FILL) { +- m_Matrix[0] = m_Matrix[3] = 1.0f; +- m_Matrix[1] = m_Matrix[2] = 0; +- m_CTM[0] = m_CTM[3] = 1.0f; +- m_CTM[1] = m_CTM[2] = 0; +-} ++CPDF_TextState::TextData::TextData() = default; + + CPDF_TextState::TextData::TextData(const TextData& that) + : m_pFont(that.m_pFont), +@@ -105,8 +97,8 @@ CPDF_TextState::TextData::TextData(const TextData& that) + m_CTM[i] = that.m_CTM[i]; + + if (m_pDocument && m_pFont) { +- auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument.Get()); +- m_pFont = pPageData->GetFont(m_pFont->GetFontDict()); ++ auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument); ++ m_pFont = pPageData->GetFont(m_pFont->GetMutableFontDict()); + } + } + +@@ -116,9 +108,9 @@ RetainPtr CPDF_TextState::TextData::Clone() const { + return pdfium::MakeRetain(*this); + } + +-void CPDF_TextState::TextData::SetFont(const RetainPtr& pFont) { ++void CPDF_TextState::TextData::SetFont(RetainPtr pFont) { + m_pDocument = pFont ? pFont->GetDocument() : nullptr; +- m_pFont = pFont; ++ m_pFont = std::move(pFont); + } + + float CPDF_TextState::TextData::GetFontSizeH() const { +diff --git a/core/fpdfapi/page/cpdf_textstate.h b/core/fpdfapi/page/cpdf_textstate.h +index 5cc0c1d88..932d5a7a4 100644 +--- a/core/fpdfapi/page/cpdf_textstate.h ++++ b/core/fpdfapi/page/cpdf_textstate.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,6 +10,7 @@ + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/shared_copy_on_write.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "third_party/base/span.h" + + class CPDF_Document; + class CPDF_Font; +@@ -36,13 +37,13 @@ class CPDF_TextState { + void Emplace(); + + RetainPtr GetFont() const; +- void SetFont(const RetainPtr& pFont); ++ void SetFont(RetainPtr pFont); + + float GetFontSize() const; + void SetFontSize(float size); + +- const float* GetMatrix() const; +- float* GetMutableMatrix(); ++ pdfium::span GetMatrix() const; ++ pdfium::span GetMutableMatrix(); + + float GetCharSpace() const; + void SetCharSpace(float sp); +@@ -55,29 +56,28 @@ class CPDF_TextState { + TextRenderingMode GetTextMode() const; + void SetTextMode(TextRenderingMode mode); + +- const float* GetCTM() const; +- float* GetMutableCTM(); ++ pdfium::span GetCTM() const; ++ pdfium::span GetMutableCTM(); + + private: + class TextData final : public Retainable { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + RetainPtr Clone() const; + +- void SetFont(const RetainPtr& pFont); ++ void SetFont(RetainPtr pFont); + float GetFontSizeV() const; + float GetFontSizeH() const; + + RetainPtr m_pFont; +- UnownedPtr m_pDocument; +- float m_FontSize; +- float m_CharSpace; +- float m_WordSpace; +- TextRenderingMode m_TextMode; +- float m_Matrix[4]; +- float m_CTM[4]; ++ UnownedPtr m_pDocument; ++ float m_FontSize = 1.0f; ++ float m_CharSpace = 0.0f; ++ float m_WordSpace = 0.0f; ++ TextRenderingMode m_TextMode = TextRenderingMode::MODE_FILL; ++ float m_Matrix[4] = {1.0f, 0.0f, 0.0f, 1.0f}; ++ float m_CTM[4] = {1.0f, 0.0f, 0.0f, 1.0f}; + + private: + TextData(); +diff --git a/core/fpdfapi/page/cpdf_tilingpattern.cpp b/core/fpdfapi/page/cpdf_tilingpattern.cpp +index 46a59db1b..d7b2fd3b7 100644 +--- a/core/fpdfapi/page/cpdf_tilingpattern.cpp ++++ b/core/fpdfapi/page/cpdf_tilingpattern.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,19 +6,23 @@ + + #include "core/fpdfapi/page/cpdf_tilingpattern.h" + ++#include ++ ++#include ++ + #include "core/fpdfapi/page/cpdf_allstates.h" + #include "core/fpdfapi/page/cpdf_form.h" + #include "core/fpdfapi/page/cpdf_pageobject.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_object.h" + #include "core/fpdfapi/parser/cpdf_stream.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + CPDF_TilingPattern::CPDF_TilingPattern(CPDF_Document* pDoc, +- CPDF_Object* pPatternObj, ++ RetainPtr pPatternObj, + const CFX_Matrix& parentMatrix) +- : CPDF_Pattern(pDoc, pPatternObj, parentMatrix) { +- ASSERT(document()); ++ : CPDF_Pattern(pDoc, std::move(pPatternObj), parentMatrix) { ++ DCHECK(document()); + m_bColored = pattern_obj()->GetDict()->GetIntegerFor("PaintType") == 1; + SetPatternToFormMatrix(); + } +@@ -30,17 +34,18 @@ CPDF_TilingPattern* CPDF_TilingPattern::AsTilingPattern() { + } + + std::unique_ptr CPDF_TilingPattern::Load(CPDF_PageObject* pPageObj) { +- const CPDF_Dictionary* pDict = pattern_obj()->GetDict(); ++ RetainPtr pDict = pattern_obj()->GetDict(); + m_bColored = pDict->GetIntegerFor("PaintType") == 1; +- m_XStep = static_cast(fabs(pDict->GetNumberFor("XStep"))); +- m_YStep = static_cast(fabs(pDict->GetNumberFor("YStep"))); ++ m_XStep = fabsf(pDict->GetFloatFor("XStep")); ++ m_YStep = fabsf(pDict->GetFloatFor("YStep")); + +- CPDF_Stream* pStream = pattern_obj()->AsStream(); ++ RetainPtr pStream = ToStream(pattern_obj()); + if (!pStream) + return nullptr; + + const CFX_Matrix& matrix = parent_matrix(); +- auto form = pdfium::MakeUnique(document(), nullptr, pStream); ++ auto form = ++ std::make_unique(document(), nullptr, std::move(pStream)); + + CPDF_AllStates allStates; + allStates.m_ColorState.Emplace(); +diff --git a/core/fpdfapi/page/cpdf_tilingpattern.h b/core/fpdfapi/page/cpdf_tilingpattern.h +index 134da8e9d..519e457d2 100644 +--- a/core/fpdfapi/page/cpdf_tilingpattern.h ++++ b/core/fpdfapi/page/cpdf_tilingpattern.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,7 +11,7 @@ + + #include "core/fpdfapi/page/cpdf_pattern.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/retain_ptr.h" + + class CPDF_Document; + class CPDF_Form; +@@ -20,9 +20,7 @@ class CPDF_PageObject; + + class CPDF_TilingPattern final : public CPDF_Pattern { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- ++ CONSTRUCT_VIA_MAKE_RETAIN; + ~CPDF_TilingPattern() override; + + // CPDF_Pattern: +@@ -37,15 +35,15 @@ class CPDF_TilingPattern final : public CPDF_Pattern { + + private: + CPDF_TilingPattern(CPDF_Document* pDoc, +- CPDF_Object* pPatternObj, ++ RetainPtr pPatternObj, + const CFX_Matrix& parentMatrix); + CPDF_TilingPattern(const CPDF_TilingPattern&) = delete; + CPDF_TilingPattern& operator=(const CPDF_TilingPattern&) = delete; + + bool m_bColored; + CFX_FloatRect m_BBox; +- float m_XStep; +- float m_YStep; ++ float m_XStep = 0.0f; ++ float m_YStep = 0.0f; + }; + + #endif // CORE_FPDFAPI_PAGE_CPDF_TILINGPATTERN_H_ +diff --git a/core/fpdfapi/page/cpdf_transferfunc.cpp b/core/fpdfapi/page/cpdf_transferfunc.cpp +index 9e3092b09..f7aa5e520 100644 +--- a/core/fpdfapi/page/cpdf_transferfunc.cpp ++++ b/core/fpdfapi/page/cpdf_transferfunc.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,39 +6,41 @@ + + #include "core/fpdfapi/page/cpdf_transferfunc.h" + ++#include ++ + #include + + #include "core/fpdfapi/page/cpdf_transferfuncdib.h" + #include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fxcrt/fixed_uninit_data_vector.h" + #include "core/fxge/dib/cfx_dibbase.h" ++#include "third_party/base/check_op.h" + +-CPDF_TransferFunc::CPDF_TransferFunc(CPDF_Document* pDoc, +- bool bIdentify, +- std::vector samples_r, +- std::vector samples_g, +- std::vector samples_b) +- : m_pPDFDoc(pDoc), +- m_bIdentity(bIdentify), ++CPDF_TransferFunc::CPDF_TransferFunc(bool bIdentify, ++ FixedUninitDataVector samples_r, ++ FixedUninitDataVector samples_g, ++ FixedUninitDataVector samples_b) ++ : m_bIdentity(bIdentify), + m_SamplesR(std::move(samples_r)), + m_SamplesG(std::move(samples_g)), + m_SamplesB(std::move(samples_b)) { +- ASSERT(m_SamplesR.size() == kChannelSampleSize); +- ASSERT(m_SamplesG.size() == kChannelSampleSize); +- ASSERT(m_SamplesB.size() == kChannelSampleSize); ++ DCHECK_EQ(m_SamplesR.size(), kChannelSampleSize); ++ DCHECK_EQ(m_SamplesG.size(), kChannelSampleSize); ++ DCHECK_EQ(m_SamplesB.size(), kChannelSampleSize); + } + + CPDF_TransferFunc::~CPDF_TransferFunc() = default; + + FX_COLORREF CPDF_TransferFunc::TranslateColor(FX_COLORREF colorref) const { +- return FXSYS_BGR(m_SamplesB[FXSYS_GetBValue(colorref)], +- m_SamplesG[FXSYS_GetGValue(colorref)], +- m_SamplesR[FXSYS_GetRValue(colorref)]); ++ return FXSYS_BGR(m_SamplesB.span()[FXSYS_GetBValue(colorref)], ++ m_SamplesG.span()[FXSYS_GetGValue(colorref)], ++ m_SamplesR.span()[FXSYS_GetRValue(colorref)]); + } + + RetainPtr CPDF_TransferFunc::TranslateImage( +- const RetainPtr& pSrc) { +- RetainPtr pHolder(this); +- return pdfium::MakeRetain(pSrc, pHolder); ++ RetainPtr pSrc) { ++ return pdfium::MakeRetain(std::move(pSrc), ++ pdfium::WrapRetain(this)); + } + + pdfium::span CPDF_TransferFunc::GetSamplesR() const { +diff --git a/core/fpdfapi/page/cpdf_transferfunc.h b/core/fpdfapi/page/cpdf_transferfunc.h +index b2d997e47..d16893021 100644 +--- a/core/fpdfapi/page/cpdf_transferfunc.h ++++ b/core/fpdfapi/page/cpdf_transferfunc.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,28 +7,24 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNC_H_ + #define CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNC_H_ + +-#include ++#include + ++#include "core/fxcrt/fixed_uninit_data_vector.h" + #include "core/fxcrt/observed_ptr.h" + #include "core/fxcrt/retain_ptr.h" +-#include "core/fxcrt/unowned_ptr.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" + #include "third_party/base/span.h" + +-class CPDF_Document; + class CFX_DIBBase; + + class CPDF_TransferFunc final : public Retainable, public Observable { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + static constexpr size_t kChannelSampleSize = 256; + + FX_COLORREF TranslateColor(FX_COLORREF colorref) const; +- RetainPtr TranslateImage(const RetainPtr& pSrc); +- +- const CPDF_Document* GetDocument() const { return m_pPDFDoc.Get(); } ++ RetainPtr TranslateImage(RetainPtr pSrc); + + // Spans are |kChannelSampleSize| in size. + pdfium::span GetSamplesR() const; +@@ -38,18 +34,16 @@ class CPDF_TransferFunc final : public Retainable, public Observable { + bool GetIdentity() const { return m_bIdentity; } + + private: +- CPDF_TransferFunc(CPDF_Document* pDoc, +- bool bIdentify, +- std::vector samples_r, +- std::vector samples_g, +- std::vector samples_b); ++ CPDF_TransferFunc(bool bIdentify, ++ FixedUninitDataVector samples_r, ++ FixedUninitDataVector samples_g, ++ FixedUninitDataVector samples_b); + ~CPDF_TransferFunc() override; + +- UnownedPtr const m_pPDFDoc; + const bool m_bIdentity; +- const std::vector m_SamplesR; +- const std::vector m_SamplesG; +- const std::vector m_SamplesB; ++ const FixedUninitDataVector m_SamplesR; ++ const FixedUninitDataVector m_SamplesG; ++ const FixedUninitDataVector m_SamplesB; + }; + + #endif // CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNC_H_ +diff --git a/core/fpdfapi/page/cpdf_transferfuncdib.cpp b/core/fpdfapi/page/cpdf_transferfuncdib.cpp +index f98561669..24e400760 100644 +--- a/core/fpdfapi/page/cpdf_transferfuncdib.cpp ++++ b/core/fpdfapi/page/cpdf_transferfuncdib.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,50 +6,48 @@ + + #include "core/fpdfapi/page/cpdf_transferfuncdib.h" + +-#include ++#include + + #include "build/build_config.h" + #include "core/fpdfapi/page/cpdf_transferfunc.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" +-#include "third_party/base/compiler_specific.h" ++#include "core/fxge/calculate_pitch.h" ++#include "third_party/base/check.h" + + CPDF_TransferFuncDIB::CPDF_TransferFuncDIB( +- const RetainPtr& pSrc, +- const RetainPtr& pTransferFunc) +- : m_pSrc(pSrc), +- m_pTransferFunc(pTransferFunc), +- m_RampR(pTransferFunc->GetSamplesR()), +- m_RampG(pTransferFunc->GetSamplesG()), +- m_RampB(pTransferFunc->GetSamplesB()) { +- m_Width = pSrc->GetWidth(); +- m_Height = pSrc->GetHeight(); +- FXDIB_Format format = GetDestFormat(); +- m_bpp = GetBppFromFormat(format); +- m_AlphaFlag = GetAlphaFlagFromFormat(format); +- m_Pitch = (m_Width * m_bpp + 31) / 32 * 4; +- m_pPalette.reset(); ++ RetainPtr pSrc, ++ RetainPtr pTransferFunc) ++ : m_pSrc(std::move(pSrc)), ++ m_pTransferFunc(std::move(pTransferFunc)), ++ m_RampR(m_pTransferFunc->GetSamplesR()), ++ m_RampG(m_pTransferFunc->GetSamplesG()), ++ m_RampB(m_pTransferFunc->GetSamplesB()) { ++ m_Width = m_pSrc->GetWidth(); ++ m_Height = m_pSrc->GetHeight(); ++ m_Format = GetDestFormat(); ++ m_Pitch = fxge::CalculatePitch32OrDie(GetBppFromFormat(m_Format), m_Width); + m_Scanline.resize(m_Pitch); ++ DCHECK(m_palette.empty()); + } + + CPDF_TransferFuncDIB::~CPDF_TransferFuncDIB() = default; + + FXDIB_Format CPDF_TransferFuncDIB::GetDestFormat() const { +- if (m_pSrc->IsAlphaMask()) +- return FXDIB_8bppMask; ++ if (m_pSrc->IsMaskFormat()) ++ return FXDIB_Format::k8bppMask; + +-#if defined(OS_MACOSX) +- return m_pSrc->HasAlpha() ? FXDIB_Argb : FXDIB_Rgb32; +-#else +- return m_pSrc->HasAlpha() ? FXDIB_Argb : FXDIB_Rgb; +-#endif ++ if (m_pSrc->IsAlphaFormat()) ++ return FXDIB_Format::kArgb; ++ ++ return CFX_DIBBase::kPlatformRGBFormat; + } + + void CPDF_TransferFuncDIB::TranslateScanline( +- const uint8_t* src_buf, +- std::vector* dest_buf) const { ++ pdfium::span src_span) const { ++ const uint8_t* src_buf = src_span.data(); + bool bSkip = false; + switch (m_pSrc->GetFormat()) { +- case FXDIB_1bppRgb: { ++ case FXDIB_Format::k1bppRgb: { + int r0 = m_RampR[0]; + int g0 = m_RampG[0]; + int b0 = m_RampB[0]; +@@ -59,84 +57,84 @@ void CPDF_TransferFuncDIB::TranslateScanline( + int index = 0; + for (int i = 0; i < m_Width; i++) { + if (src_buf[i / 8] & (1 << (7 - i % 8))) { +- (*dest_buf)[index++] = b1; +- (*dest_buf)[index++] = g1; +- (*dest_buf)[index++] = r1; ++ m_Scanline[index++] = b1; ++ m_Scanline[index++] = g1; ++ m_Scanline[index++] = r1; + } else { +- (*dest_buf)[index++] = b0; +- (*dest_buf)[index++] = g0; +- (*dest_buf)[index++] = r0; ++ m_Scanline[index++] = b0; ++ m_Scanline[index++] = g0; ++ m_Scanline[index++] = r0; + } +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + index++; + #endif + } + break; + } +- case FXDIB_1bppMask: { ++ case FXDIB_Format::k1bppMask: { + int m0 = m_RampR[0]; + int m1 = m_RampR[255]; + int index = 0; + for (int i = 0; i < m_Width; i++) { + if (src_buf[i / 8] & (1 << (7 - i % 8))) +- (*dest_buf)[index++] = m1; ++ m_Scanline[index++] = m1; + else +- (*dest_buf)[index++] = m0; ++ m_Scanline[index++] = m0; + } + break; + } +- case FXDIB_8bppRgb: { +- FX_ARGB* pPal = m_pSrc->GetPalette(); ++ case FXDIB_Format::k8bppRgb: { ++ pdfium::span src_palette = m_pSrc->GetPaletteSpan(); + int index = 0; + for (int i = 0; i < m_Width; i++) { +- if (pPal) { +- FX_ARGB src_argb = pPal[*src_buf]; +- (*dest_buf)[index++] = m_RampB[FXARGB_R(src_argb)]; +- (*dest_buf)[index++] = m_RampG[FXARGB_G(src_argb)]; +- (*dest_buf)[index++] = m_RampR[FXARGB_B(src_argb)]; ++ if (m_pSrc->HasPalette()) { ++ FX_ARGB src_argb = src_palette[*src_buf]; ++ m_Scanline[index++] = m_RampB[FXARGB_R(src_argb)]; ++ m_Scanline[index++] = m_RampG[FXARGB_G(src_argb)]; ++ m_Scanline[index++] = m_RampR[FXARGB_B(src_argb)]; + } else { + uint32_t src_byte = *src_buf; +- (*dest_buf)[index++] = m_RampB[src_byte]; +- (*dest_buf)[index++] = m_RampG[src_byte]; +- (*dest_buf)[index++] = m_RampR[src_byte]; ++ m_Scanline[index++] = m_RampB[src_byte]; ++ m_Scanline[index++] = m_RampG[src_byte]; ++ m_Scanline[index++] = m_RampR[src_byte]; + } + src_buf++; +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + index++; + #endif + } + break; + } +- case FXDIB_8bppMask: { ++ case FXDIB_Format::k8bppMask: { + int index = 0; + for (int i = 0; i < m_Width; i++) +- (*dest_buf)[index++] = m_RampR[*(src_buf++)]; ++ m_Scanline[index++] = m_RampR[*(src_buf++)]; + break; + } +- case FXDIB_Rgb: { ++ case FXDIB_Format::kRgb: { + int index = 0; + for (int i = 0; i < m_Width; i++) { +- (*dest_buf)[index++] = m_RampB[*(src_buf++)]; +- (*dest_buf)[index++] = m_RampG[*(src_buf++)]; +- (*dest_buf)[index++] = m_RampR[*(src_buf++)]; +-#if defined(OS_MACOSX) ++ m_Scanline[index++] = m_RampB[*(src_buf++)]; ++ m_Scanline[index++] = m_RampG[*(src_buf++)]; ++ m_Scanline[index++] = m_RampR[*(src_buf++)]; ++#if BUILDFLAG(IS_APPLE) + index++; + #endif + } + break; + } +- case FXDIB_Rgb32: ++ case FXDIB_Format::kRgb32: + bSkip = true; +- FALLTHROUGH; +- case FXDIB_Argb: { ++ [[fallthrough]]; ++ case FXDIB_Format::kArgb: { + int index = 0; + for (int i = 0; i < m_Width; i++) { +- (*dest_buf)[index++] = m_RampB[*(src_buf++)]; +- (*dest_buf)[index++] = m_RampG[*(src_buf++)]; +- (*dest_buf)[index++] = m_RampR[*(src_buf++)]; ++ m_Scanline[index++] = m_RampB[*(src_buf++)]; ++ m_Scanline[index++] = m_RampG[*(src_buf++)]; ++ m_Scanline[index++] = m_RampR[*(src_buf++)]; + if (!bSkip) { +- (*dest_buf)[index++] = *src_buf; +-#if defined(OS_MACOSX) ++ m_Scanline[index++] = *src_buf; ++#if BUILDFLAG(IS_APPLE) + } else { + index++; + #endif +@@ -150,56 +148,7 @@ void CPDF_TransferFuncDIB::TranslateScanline( + } + } + +-void CPDF_TransferFuncDIB::TranslateDownSamples(uint8_t* dest_buf, +- const uint8_t* src_buf, +- int pixels, +- int Bpp) const { +- if (Bpp == 8) { +- for (int i = 0; i < pixels; i++) +- *dest_buf++ = m_RampR[*(src_buf++)]; +- } else if (Bpp == 24) { +- for (int i = 0; i < pixels; i++) { +- *dest_buf++ = m_RampB[*(src_buf++)]; +- *dest_buf++ = m_RampG[*(src_buf++)]; +- *dest_buf++ = m_RampR[*(src_buf++)]; +- } +- } else { +-#if defined(OS_MACOSX) +- if (!m_pSrc->HasAlpha()) { +- for (int i = 0; i < pixels; i++) { +- *dest_buf++ = m_RampB[*(src_buf++)]; +- *dest_buf++ = m_RampG[*(src_buf++)]; +- *dest_buf++ = m_RampR[*(src_buf++)]; +- dest_buf++; +- src_buf++; +- } +- } else { +-#endif +- for (int i = 0; i < pixels; i++) { +- *dest_buf++ = m_RampB[*(src_buf++)]; +- *dest_buf++ = m_RampG[*(src_buf++)]; +- *dest_buf++ = m_RampR[*(src_buf++)]; +- *dest_buf++ = *(src_buf++); +- } +-#if defined(OS_MACOSX) +- } +-#endif +- } +-} +- +-const uint8_t* CPDF_TransferFuncDIB::GetScanline(int line) const { +- TranslateScanline(m_pSrc->GetScanline(line), &m_Scanline); +- return m_Scanline.data(); +-} +- +-void CPDF_TransferFuncDIB::DownSampleScanline(int line, +- uint8_t* dest_scan, +- int dest_bpp, +- int dest_width, +- bool bFlipX, +- int clip_left, +- int clip_width) const { +- m_pSrc->DownSampleScanline(line, dest_scan, dest_bpp, dest_width, bFlipX, +- clip_left, clip_width); +- TranslateDownSamples(dest_scan, dest_scan, clip_width, dest_bpp); ++pdfium::span CPDF_TransferFuncDIB::GetScanline(int line) const { ++ TranslateScanline(m_pSrc->GetScanline(line)); ++ return m_Scanline; + } +diff --git a/core/fpdfapi/page/cpdf_transferfuncdib.h b/core/fpdfapi/page/cpdf_transferfuncdib.h +index c30718a93..a03411623 100644 +--- a/core/fpdfapi/page/cpdf_transferfuncdib.h ++++ b/core/fpdfapi/page/cpdf_transferfuncdib.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,8 +7,9 @@ + #ifndef CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNCDIB_H_ + #define CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNCDIB_H_ + +-#include ++#include + ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxge/dib/cfx_dibbase.h" + #include "third_party/base/span.h" +@@ -17,39 +18,25 @@ class CPDF_TransferFunc; + + class CPDF_TransferFuncDIB final : public CFX_DIBBase { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + +- void TranslateScanline(const uint8_t* src_buf, +- std::vector* dest_buf) const; +- void TranslateDownSamples(uint8_t* dest_buf, +- const uint8_t* src_buf, +- int pixels, +- int Bpp) const; ++ // CFX_DIBBase: ++ pdfium::span GetScanline(int line) const override; + + private: +- CPDF_TransferFuncDIB(const RetainPtr& pSrc, +- const RetainPtr& pTransferFunc); ++ CPDF_TransferFuncDIB(RetainPtr pSrc, ++ RetainPtr pTransferFunc); + ~CPDF_TransferFuncDIB() override; + +- // CFX_DIBBase: +- const uint8_t* GetScanline(int line) const override; +- void DownSampleScanline(int line, +- uint8_t* dest_scan, +- int dest_bpp, +- int dest_width, +- bool bFlipX, +- int clip_left, +- int clip_width) const override; +- ++ void TranslateScanline(pdfium::span src_span) const; + FXDIB_Format GetDestFormat() const; + + RetainPtr const m_pSrc; +- mutable std::vector m_Scanline; + RetainPtr const m_pTransferFunc; + const pdfium::span m_RampR; + const pdfium::span m_RampG; + const pdfium::span m_RampB; ++ mutable DataVector m_Scanline; + }; + + #endif // CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNCDIB_H_ +diff --git a/core/fpdfapi/page/cpdf_transparency.cpp b/core/fpdfapi/page/cpdf_transparency.cpp +index f9be5417a..811160dd4 100644 +--- a/core/fpdfapi/page/cpdf_transparency.cpp ++++ b/core/fpdfapi/page/cpdf_transparency.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,3 +7,6 @@ + CPDF_Transparency::CPDF_Transparency() = default; + + CPDF_Transparency::CPDF_Transparency(const CPDF_Transparency& other) = default; ++ ++CPDF_Transparency& CPDF_Transparency::operator=( ++ const CPDF_Transparency& other) = default; +diff --git a/core/fpdfapi/page/cpdf_transparency.h b/core/fpdfapi/page/cpdf_transparency.h +index 6d4972d2e..a2e0a251f 100644 +--- a/core/fpdfapi/page/cpdf_transparency.h ++++ b/core/fpdfapi/page/cpdf_transparency.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,6 +10,7 @@ class CPDF_Transparency { + CPDF_Transparency(); + + CPDF_Transparency(const CPDF_Transparency& other); ++ CPDF_Transparency& operator=(const CPDF_Transparency& other); + + bool IsGroup() const { return m_bGroup; } + bool IsIsolated() const { return m_bIsolated; } +diff --git a/core/fpdfapi/page/ipdf_page.h b/core/fpdfapi/page/ipdf_page.h +index 9b28559a9..a71106bcd 100644 +--- a/core/fpdfapi/page/ipdf_page.h ++++ b/core/fpdfapi/page/ipdf_page.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,7 +9,7 @@ + + #include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/retain_ptr.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CPDF_Document; + class CPDF_Page; +@@ -35,12 +35,12 @@ class IPDF_Page : public Retainable { + virtual CFX_Matrix GetDisplayMatrix(const FX_RECT& rect, + int iRotate) const = 0; + +- virtual Optional DeviceToPage( ++ virtual absl::optional DeviceToPage( + const FX_RECT& rect, + int rotate, + const CFX_PointF& device_point) const = 0; + +- virtual Optional PageToDevice( ++ virtual absl::optional PageToDevice( + const FX_RECT& rect, + int rotate, + const CFX_PointF& page_point) const = 0; +diff --git a/core/fpdfapi/page/test_with_page_module.cpp b/core/fpdfapi/page/test_with_page_module.cpp +new file mode 100644 +index 000000000..0515ad910 +--- /dev/null ++++ b/core/fpdfapi/page/test_with_page_module.cpp +@@ -0,0 +1,15 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fpdfapi/page/test_with_page_module.h" ++ ++#include "core/fpdfapi/page/cpdf_pagemodule.h" ++ ++void TestWithPageModule::SetUp() { ++ CPDF_PageModule::Create(); ++} ++ ++void TestWithPageModule::TearDown() { ++ CPDF_PageModule::Destroy(); ++} +diff --git a/core/fpdfapi/page/test_with_page_module.h b/core/fpdfapi/page/test_with_page_module.h +new file mode 100644 +index 000000000..0b39b84bf +--- /dev/null ++++ b/core/fpdfapi/page/test_with_page_module.h +@@ -0,0 +1,16 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FPDFAPI_PAGE_TEST_WITH_PAGE_MODULE_H_ ++#define CORE_FPDFAPI_PAGE_TEST_WITH_PAGE_MODULE_H_ ++ ++#include "testing/gtest/include/gtest/gtest.h" ++ ++class TestWithPageModule : public testing::Test { ++ public: ++ void SetUp() override; ++ void TearDown() override; ++}; ++ ++#endif // CORE_FPDFAPI_PAGE_TEST_WITH_PAGE_MODULE_H_ +diff --git a/core/fpdfapi/parser/Android.bp b/core/fpdfapi/parser/Android.bp +index cacb42e03..0ad980706 100644 +--- a/core/fpdfapi/parser/Android.bp ++++ b/core/fpdfapi/parser/Android.bp +@@ -13,11 +13,8 @@ cc_library_static { + + visibility: ["//external/pdfium:__subpackages__"], + +- header_libs: [ +- "libpdfium-constants", +- ], +- + static_libs: [ ++ "libpdfium-constants", + "libpdfium-fdrm", + "libpdfium-fxcodec", + "libpdfium-fxcrt", +diff --git a/core/fpdfapi/parser/BUILD.gn b/core/fpdfapi/parser/BUILD.gn +index ad8a783ab..e3152d86a 100644 +--- a/core/fpdfapi/parser/BUILD.gn ++++ b/core/fpdfapi/parser/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -73,14 +73,16 @@ source_set("parser") { + "fpdf_parser_decode.h", + "fpdf_parser_utility.cpp", + "fpdf_parser_utility.h", ++ "object_tree_traversal_util.cpp", ++ "object_tree_traversal_util.h", + ] +- configs += [ "../../../:pdfium_core_config" ] ++ configs += [ "../../../:pdfium_strict_config" ] + deps = [ + "../../../constants", + "../../fdrm", + "../../fxcodec", +- "../../fxcrt", + ] ++ public_deps = [ "../../fxcrt" ] + allow_circular_includes_from = [] + visibility = [ "../../../*" ] + +@@ -90,20 +92,36 @@ source_set("parser") { + "cpdf_seekablemultistream.h", + ] + } +- if (pdf_use_skia || pdf_use_skia_paths) { ++ if (pdf_use_skia) { + deps += [ "../../fxge" ] + allow_circular_includes_from += [ "../../fxge" ] + } + } + ++source_set("unit_test_support") { ++ testonly = true ++ sources = [ ++ "cpdf_test_document.cpp", ++ "cpdf_test_document.h", ++ ] ++ configs += [ "../../../:pdfium_strict_config" ] ++ deps = [ ++ ":parser", ++ "../page", ++ "../render", ++ ] ++} ++ + pdfium_unittest_source_set("unittests") { + sources = [ + "cpdf_array_unittest.cpp", + "cpdf_cross_ref_avail_unittest.cpp", ++ "cpdf_dictionary_unittest.cpp", + "cpdf_document_unittest.cpp", + "cpdf_hint_tables_unittest.cpp", + "cpdf_indirect_object_holder_unittest.cpp", + "cpdf_object_avail_unittest.cpp", ++ "cpdf_object_stream_unittest.cpp", + "cpdf_object_unittest.cpp", + "cpdf_object_walker_unittest.cpp", + "cpdf_page_object_avail_unittest.cpp", +@@ -117,8 +135,10 @@ pdfium_unittest_source_set("unittests") { + ] + deps = [ + ":parser", ++ ":unit_test_support", + "../../../constants", + "../page", ++ "../page:unit_test_support", + "../render", + ] + pdfium_root_dir = "../../../" +@@ -126,10 +146,6 @@ pdfium_unittest_source_set("unittests") { + if (pdf_enable_xfa) { + sources += [ "cpdf_seekablemultistream_unittest.cpp" ] + } +- if (is_clang) { +- # Suppress no override warning for overridden functions. +- cflags = [ "-Wno-inconsistent-missing-override" ] +- } + } + + pdfium_embeddertest_source_set("embeddertests") { +@@ -137,7 +153,11 @@ pdfium_embeddertest_source_set("embeddertests") { + "cpdf_parser_embeddertest.cpp", + "cpdf_security_handler_embeddertest.cpp", + "fpdf_parser_decode_embeddertest.cpp", ++ "object_tree_traversal_util_embeddertest.cpp", ++ ] ++ deps = [ ++ ":parser", ++ "../../fxge", + ] +- deps = [ ":parser" ] + pdfium_root_dir = "../../../" + } +diff --git a/core/fpdfapi/parser/cfdf_document.cpp b/core/fpdfapi/parser/cfdf_document.cpp +index 054f999ea..5e9b6d3dd 100644 +--- a/core/fpdfapi/parser/cfdf_document.cpp ++++ b/core/fpdfapi/parser/cfdf_document.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,8 +13,8 @@ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_syntax_parser.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" +-#include "core/fxcrt/cfx_readonlymemorystream.h" +-#include "third_party/base/ptr_util.h" ++#include "core/fxcrt/cfx_read_only_span_stream.h" ++#include "core/fxcrt/fx_string_wrappers.h" + #include "third_party/base/span.h" + + CFDF_Document::CFDF_Document() = default; +@@ -22,36 +22,35 @@ CFDF_Document::CFDF_Document() = default; + CFDF_Document::~CFDF_Document() = default; + + std::unique_ptr CFDF_Document::CreateNewDoc() { +- auto pDoc = pdfium::MakeUnique(); +- pDoc->m_pRootDict.Reset(pDoc->NewIndirect()); ++ auto pDoc = std::make_unique(); ++ pDoc->m_pRootDict = pDoc->NewIndirect(); + pDoc->m_pRootDict->SetNewFor("FDF"); + return pDoc; + } + + std::unique_ptr CFDF_Document::ParseMemory( + pdfium::span span) { +- auto pDoc = pdfium::MakeUnique(); +- pDoc->ParseStream(pdfium::MakeRetain(span)); ++ auto pDoc = std::make_unique(); ++ pDoc->ParseStream(pdfium::MakeRetain(span)); + return pDoc->m_pRootDict ? std::move(pDoc) : nullptr; + } + + void CFDF_Document::ParseStream(RetainPtr pFile) { + m_pFile = std::move(pFile); + CPDF_SyntaxParser parser(m_pFile); +- while (1) { +- bool bNumber; +- ByteString word = parser.GetNextWord(&bNumber); +- if (bNumber) { +- uint32_t objnum = FXSYS_atoui(word.c_str()); ++ while (true) { ++ CPDF_SyntaxParser::WordResult word_result = parser.GetNextWord(); ++ if (word_result.is_number) { ++ uint32_t objnum = FXSYS_atoui(word_result.word.c_str()); + if (!objnum) + break; + +- word = parser.GetNextWord(&bNumber); +- if (!bNumber) ++ word_result = parser.GetNextWord(); ++ if (!word_result.is_number) + break; + +- word = parser.GetNextWord(nullptr); +- if (word != "obj") ++ word_result = parser.GetNextWord(); ++ if (word_result.word != "obj") + break; + + RetainPtr pObj = parser.GetObjectBody(this); +@@ -59,17 +58,17 @@ void CFDF_Document::ParseStream(RetainPtr pFile) { + break; + + ReplaceIndirectObjectIfHigherGeneration(objnum, std::move(pObj)); +- word = parser.GetNextWord(nullptr); +- if (word != "endobj") ++ word_result = parser.GetNextWord(); ++ if (word_result.word != "endobj") + break; + } else { +- if (word != "trailer") ++ if (word_result.word != "trailer") + break; + + RetainPtr pMainDict = + ToDictionary(parser.GetObjectBody(this)); + if (pMainDict) +- m_pRootDict.Reset(pMainDict->GetDictFor("Root")); ++ m_pRootDict = pMainDict->GetMutableDictFor("Root"); + + break; + } +@@ -80,7 +79,7 @@ ByteString CFDF_Document::WriteToString() const { + if (!m_pRootDict) + return ByteString(); + +- std::ostringstream buf; ++ fxcrt::ostringstream buf; + buf << "%FDF-1.2\r\n"; + for (const auto& pair : *this) + buf << pair.first << " 0 obj\r\n" +diff --git a/core/fpdfapi/parser/cfdf_document.h b/core/fpdfapi/parser/cfdf_document.h +index cad94d002..bd0e72f52 100644 +--- a/core/fpdfapi/parser/cfdf_document.h ++++ b/core/fpdfapi/parser/cfdf_document.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -26,7 +26,8 @@ class CFDF_Document final : public CPDF_IndirectObjectHolder { + ~CFDF_Document() override; + + ByteString WriteToString() const; +- CPDF_Dictionary* GetRoot() const { return m_pRootDict.Get(); } ++ const CPDF_Dictionary* GetRoot() const { return m_pRootDict.Get(); } ++ RetainPtr GetMutableRoot() const { return m_pRootDict; } + + private: + void ParseStream(RetainPtr pFile); +diff --git a/core/fpdfapi/parser/cpdf_array.cpp b/core/fpdfapi/parser/cpdf_array.cpp +index 0d66cb057..55b82e743 100644 +--- a/core/fpdfapi/parser/cpdf_array.cpp ++++ b/core/fpdfapi/parser/cpdf_array.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,15 +10,16 @@ + #include + + #include "core/fpdfapi/parser/cpdf_boolean.h" ++#include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fxcrt/fx_stream.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/notreached.h" + + CPDF_Array::CPDF_Array() = default; + +@@ -28,7 +29,7 @@ CPDF_Array::~CPDF_Array() { + // Break cycles for cyclic references. + m_ObjNum = kInvalidObjNum; + for (auto& it : m_Objects) { +- if (it && it->GetObjNum() == kInvalidObjNum) ++ if (it->GetObjNum() == kInvalidObjNum) + it.Leak(); + } + } +@@ -37,15 +38,7 @@ CPDF_Object::Type CPDF_Array::GetType() const { + return kArray; + } + +-bool CPDF_Array::IsArray() const { +- return true; +-} +- +-CPDF_Array* CPDF_Array::AsArray() { +- return this; +-} +- +-const CPDF_Array* CPDF_Array::AsArray() const { ++CPDF_Array* CPDF_Array::AsMutableArray() { + return this; + } + +@@ -59,7 +52,7 @@ RetainPtr CPDF_Array::CloneNonCyclic( + pVisited->insert(this); + auto pCopy = pdfium::MakeRetain(); + for (const auto& pValue : m_Objects) { +- if (!pdfium::ContainsKey(*pVisited, pValue.Get())) { ++ if (!pdfium::Contains(*pVisited, pValue.Get())) { + std::set visited(*pVisited); + if (auto obj = pValue->CloneNonCyclic(bDirect, &visited)) + pCopy->m_Objects.push_back(std::move(obj)); +@@ -73,10 +66,10 @@ CFX_FloatRect CPDF_Array::GetRect() const { + if (m_Objects.size() != 4) + return rect; + +- rect.left = GetNumberAt(0); +- rect.bottom = GetNumberAt(1); +- rect.right = GetNumberAt(2); +- rect.top = GetNumberAt(3); ++ rect.left = GetFloatAt(0); ++ rect.bottom = GetFloatAt(1); ++ rect.right = GetFloatAt(2); ++ rect.top = GetFloatAt(3); + return rect; + } + +@@ -84,35 +77,48 @@ CFX_Matrix CPDF_Array::GetMatrix() const { + if (m_Objects.size() != 6) + return CFX_Matrix(); + +- return CFX_Matrix(GetNumberAt(0), GetNumberAt(1), GetNumberAt(2), +- GetNumberAt(3), GetNumberAt(4), GetNumberAt(5)); ++ return CFX_Matrix(GetFloatAt(0), GetFloatAt(1), GetFloatAt(2), GetFloatAt(3), ++ GetFloatAt(4), GetFloatAt(5)); + } + +-CPDF_Object* CPDF_Array::GetObjectAt(size_t index) { +- if (index >= m_Objects.size()) +- return nullptr; +- return m_Objects[index].Get(); ++absl::optional CPDF_Array::Find(const CPDF_Object* pThat) const { ++ for (size_t i = 0; i < size(); ++i) { ++ if (GetDirectObjectAt(i) == pThat) ++ return i; ++ } ++ return absl::nullopt; + } + +-const CPDF_Object* CPDF_Array::GetObjectAt(size_t index) const { +- if (index >= m_Objects.size()) +- return nullptr; +- return m_Objects[index].Get(); ++bool CPDF_Array::Contains(const CPDF_Object* pThat) const { ++ return Find(pThat).has_value(); + } + +-CPDF_Object* CPDF_Array::GetDirectObjectAt(size_t index) { +- if (index >= m_Objects.size()) +- return nullptr; +- return m_Objects[index]->GetDirect(); ++CPDF_Object* CPDF_Array::GetMutableObjectAtInternal(size_t index) { ++ return index < m_Objects.size() ? m_Objects[index].Get() : nullptr; + } + +-const CPDF_Object* CPDF_Array::GetDirectObjectAt(size_t index) const { +- if (index >= m_Objects.size()) +- return nullptr; +- return m_Objects[index]->GetDirect(); ++const CPDF_Object* CPDF_Array::GetObjectAtInternal(size_t index) const { ++ return const_cast(this)->GetMutableObjectAtInternal(index); ++} ++ ++RetainPtr CPDF_Array::GetMutableObjectAt(size_t index) { ++ return pdfium::WrapRetain(GetMutableObjectAtInternal(index)); ++} ++ ++RetainPtr CPDF_Array::GetObjectAt(size_t index) const { ++ return pdfium::WrapRetain(GetObjectAtInternal(index)); ++} ++ ++RetainPtr CPDF_Array::GetDirectObjectAt(size_t index) const { ++ return const_cast(this)->GetMutableDirectObjectAt(index); + } + +-ByteString CPDF_Array::GetStringAt(size_t index) const { ++RetainPtr CPDF_Array::GetMutableDirectObjectAt(size_t index) { ++ RetainPtr pObj = GetMutableObjectAt(index); ++ return pObj ? pObj->GetMutableDirect() : nullptr; ++} ++ ++ByteString CPDF_Array::GetByteStringAt(size_t index) const { + if (index >= m_Objects.size()) + return ByteString(); + return m_Objects[index]->GetString(); +@@ -137,48 +143,51 @@ int CPDF_Array::GetIntegerAt(size_t index) const { + return m_Objects[index]->GetInteger(); + } + +-float CPDF_Array::GetNumberAt(size_t index) const { ++float CPDF_Array::GetFloatAt(size_t index) const { + if (index >= m_Objects.size()) + return 0; + return m_Objects[index]->GetNumber(); + } + +-CPDF_Dictionary* CPDF_Array::GetDictAt(size_t index) { +- CPDF_Object* p = GetDirectObjectAt(index); ++RetainPtr CPDF_Array::GetMutableDictAt(size_t index) { ++ RetainPtr p = GetMutableDirectObjectAt(index); + if (!p) + return nullptr; +- if (CPDF_Dictionary* pDict = p->AsDictionary()) +- return pDict; +- if (CPDF_Stream* pStream = p->AsStream()) +- return pStream->GetDict(); ++ CPDF_Dictionary* pDict = p->AsMutableDictionary(); ++ if (pDict) ++ return pdfium::WrapRetain(pDict); ++ CPDF_Stream* pStream = p->AsMutableStream(); ++ if (pStream) ++ return pStream->GetMutableDict(); + return nullptr; + } + +-const CPDF_Dictionary* CPDF_Array::GetDictAt(size_t index) const { +- const CPDF_Object* p = GetDirectObjectAt(index); +- if (!p) +- return nullptr; +- if (const CPDF_Dictionary* pDict = p->AsDictionary()) +- return pDict; +- if (const CPDF_Stream* pStream = p->AsStream()) +- return pStream->GetDict(); +- return nullptr; ++RetainPtr CPDF_Array::GetDictAt(size_t index) const { ++ return const_cast(this)->GetMutableDictAt(index); ++} ++ ++RetainPtr CPDF_Array::GetMutableStreamAt(size_t index) { ++ return ToStream(GetMutableDirectObjectAt(index)); ++} ++ ++RetainPtr CPDF_Array::GetStreamAt(size_t index) const { ++ return const_cast(this)->GetMutableStreamAt(index); + } + +-CPDF_Stream* CPDF_Array::GetStreamAt(size_t index) { +- return ToStream(GetDirectObjectAt(index)); ++RetainPtr CPDF_Array::GetMutableArrayAt(size_t index) { ++ return ToArray(GetMutableDirectObjectAt(index)); + } + +-const CPDF_Stream* CPDF_Array::GetStreamAt(size_t index) const { +- return ToStream(GetDirectObjectAt(index)); ++RetainPtr CPDF_Array::GetArrayAt(size_t index) const { ++ return const_cast(this)->GetMutableArrayAt(index); + } + +-CPDF_Array* CPDF_Array::GetArrayAt(size_t index) { +- return ToArray(GetDirectObjectAt(index)); ++RetainPtr CPDF_Array::GetNumberAt(size_t index) const { ++ return ToNumber(GetObjectAt(index)); + } + +-const CPDF_Array* CPDF_Array::GetArrayAt(size_t index) const { +- return ToArray(GetDirectObjectAt(index)); ++RetainPtr CPDF_Array::GetStringAt(size_t index) const { ++ return ToString(GetObjectAt(index)); + } + + void CPDF_Array::Clear() { +@@ -201,40 +210,52 @@ void CPDF_Array::ConvertToIndirectObjectAt(size_t index, + if (!m_Objects[index] || m_Objects[index]->IsReference()) + return; + +- CPDF_Object* pNew = pHolder->AddIndirectObject(std::move(m_Objects[index])); +- m_Objects[index] = pNew->MakeReference(pHolder); ++ pHolder->AddIndirectObject(m_Objects[index]); ++ m_Objects[index] = m_Objects[index]->MakeReference(pHolder); ++} ++ ++void CPDF_Array::SetAt(size_t index, RetainPtr pObj) { ++ (void)SetAtInternal(index, std::move(pObj)); ++} ++ ++void CPDF_Array::InsertAt(size_t index, RetainPtr pObj) { ++ (void)InsertAtInternal(index, std::move(pObj)); ++} ++ ++void CPDF_Array::Append(RetainPtr pObj) { ++ (void)AppendInternal(std::move(pObj)); + } + +-CPDF_Object* CPDF_Array::SetAt(size_t index, RetainPtr pObj) { ++CPDF_Object* CPDF_Array::SetAtInternal(size_t index, ++ RetainPtr pObj) { + CHECK(!IsLocked()); +- ASSERT(!pObj || pObj->IsInline()); +- if (index >= m_Objects.size()) { +- NOTREACHED(); ++ CHECK(pObj); ++ CHECK(pObj->IsInline()); ++ if (index >= m_Objects.size()) + return nullptr; +- } ++ + CPDF_Object* pRet = pObj.Get(); + m_Objects[index] = std::move(pObj); + return pRet; + } + +-CPDF_Object* CPDF_Array::InsertAt(size_t index, RetainPtr pObj) { ++CPDF_Object* CPDF_Array::InsertAtInternal(size_t index, ++ RetainPtr pObj) { + CHECK(!IsLocked()); +- CHECK(!pObj || pObj->IsInline()); ++ CHECK(pObj); ++ CHECK(pObj->IsInline()); ++ if (index > m_Objects.size()) ++ return nullptr; ++ + CPDF_Object* pRet = pObj.Get(); +- if (index >= m_Objects.size()) { +- // Allocate space first. +- m_Objects.resize(index + 1); +- m_Objects[index] = std::move(pObj); +- } else { +- // Directly insert. +- m_Objects.insert(m_Objects.begin() + index, std::move(pObj)); +- } ++ m_Objects.insert(m_Objects.begin() + index, std::move(pObj)); + return pRet; + } + +-CPDF_Object* CPDF_Array::Add(RetainPtr pObj) { ++CPDF_Object* CPDF_Array::AppendInternal(RetainPtr pObj) { + CHECK(!IsLocked()); +- CHECK(!pObj || pObj->IsInline()); ++ CHECK(pObj); ++ CHECK(pObj->IsInline()); + CPDF_Object* pRet = pObj.Get(); + m_Objects.push_back(std::move(pObj)); + return pRet; +@@ -257,6 +278,16 @@ CPDF_ArrayLocker::CPDF_ArrayLocker(const CPDF_Array* pArray) + m_pArray->m_LockCount++; + } + ++CPDF_ArrayLocker::CPDF_ArrayLocker(RetainPtr pArray) ++ : m_pArray(std::move(pArray)) { ++ m_pArray->m_LockCount++; ++} ++ ++CPDF_ArrayLocker::CPDF_ArrayLocker(RetainPtr pArray) ++ : m_pArray(std::move(pArray)) { ++ m_pArray->m_LockCount++; ++} ++ + CPDF_ArrayLocker::~CPDF_ArrayLocker() { + m_pArray->m_LockCount--; + } +diff --git a/core/fpdfapi/parser/cpdf_array.h b/core/fpdfapi/parser/cpdf_array.h +index 2a12d9950..ea9a81527 100644 +--- a/core/fpdfapi/parser/cpdf_array.h ++++ b/core/fpdfapi/parser/cpdf_array.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,8 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_ARRAY_H_ + #define CORE_FPDFAPI_PARSER_CPDF_ARRAY_H_ + +-#include ++#include ++ + #include + #include + #include +@@ -17,94 +18,118 @@ + #include "core/fpdfapi/parser/cpdf_object.h" + #include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/retain_ptr.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/check.h" + ++// Arrays never contain nullptrs for objects within bounds, but some of the ++// methods will tolerate out-of-bounds indices and return nullptr for those ++// cases. + class CPDF_Array final : public CPDF_Object { + public: + using const_iterator = std::vector>::const_iterator; + +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // CPDF_Object: + Type GetType() const override; + RetainPtr Clone() const override; +- bool IsArray() const override; +- CPDF_Array* AsArray() override; +- const CPDF_Array* AsArray() const override; ++ CPDF_Array* AsMutableArray() override; + bool WriteTo(IFX_ArchiveStream* archive, + const CPDF_Encryptor* encryptor) const override; + + bool IsEmpty() const { return m_Objects.empty(); } + size_t size() const { return m_Objects.size(); } +- CPDF_Object* GetObjectAt(size_t index); +- const CPDF_Object* GetObjectAt(size_t index) const; +- CPDF_Object* GetDirectObjectAt(size_t index); +- const CPDF_Object* GetDirectObjectAt(size_t index) const; +- ByteString GetStringAt(size_t index) const; ++ ++ // The Get*ObjectAt() methods tolerate out-of-bounds indices and return ++ // nullptr in those cases. Otherwise, for in-bound indices, the result ++ // is never nullptr. ++ RetainPtr GetMutableObjectAt(size_t index); ++ RetainPtr GetObjectAt(size_t index) const; ++ ++ // The Get*DirectObjectAt() methods tolerate out-of-bounds indices and ++ // return nullptr in those cases. Furthermore, for reference objects that ++ // do not correspond to a valid indirect object, nullptr is returned. ++ RetainPtr GetMutableDirectObjectAt(size_t index); ++ RetainPtr GetDirectObjectAt(size_t index) const; ++ ++ // The Get*At() methods tolerate out-of-bounds indices and return nullptr ++ // in those cases. Furthermore, these safely coerce to the sub-class, ++ // returning nullptr if the object at the location is of a different type. ++ ByteString GetByteStringAt(size_t index) const; + WideString GetUnicodeTextAt(size_t index) const; + bool GetBooleanAt(size_t index, bool bDefault) const; + int GetIntegerAt(size_t index) const; +- float GetNumberAt(size_t index) const; +- CPDF_Dictionary* GetDictAt(size_t index); +- const CPDF_Dictionary* GetDictAt(size_t index) const; +- CPDF_Stream* GetStreamAt(size_t index); +- const CPDF_Stream* GetStreamAt(size_t index) const; +- CPDF_Array* GetArrayAt(size_t index); +- const CPDF_Array* GetArrayAt(size_t index) const; +- CFX_Matrix GetMatrix() const; ++ float GetFloatAt(size_t index) const; ++ RetainPtr GetMutableDictAt(size_t index); ++ RetainPtr GetDictAt(size_t index) const; ++ RetainPtr GetMutableStreamAt(size_t index); ++ RetainPtr GetStreamAt(size_t index) const; ++ RetainPtr GetMutableArrayAt(size_t index); ++ RetainPtr GetArrayAt(size_t index) const; ++ RetainPtr GetNumberAt(size_t index) const; ++ RetainPtr GetStringAt(size_t index) const; ++ + CFX_FloatRect GetRect() const; ++ CFX_Matrix GetMatrix() const; ++ ++ absl::optional Find(const CPDF_Object* pThat) const; ++ bool Contains(const CPDF_Object* pThat) const; + +- // Creates object owned by the array, returns unowned pointer to it. ++ // Creates object owned by the array, and returns a retained pointer to it. + // We have special cases for objects that can intern strings from + // a ByteStringPool. Prefer using these templates over direct calls +- // to Add()/SetAt()/InsertAt() since by creating a new object with no ++ // to Append()/SetAt()/InsertAt() since by creating a new object with no + // previous references, they ensure cycles can not be introduced. + template +- typename std::enable_if::value, T*>::type AddNew( +- Args&&... args) { +- return static_cast( +- Add(pdfium::MakeRetain(std::forward(args)...))); ++ typename std::enable_if::value, RetainPtr>::type ++ AppendNew(Args&&... args) { ++ return pdfium::WrapRetain(static_cast( ++ AppendInternal(pdfium::MakeRetain(std::forward(args)...)))); + } + template +- typename std::enable_if::value, T*>::type AddNew( +- Args&&... args) { +- return static_cast( +- Add(pdfium::MakeRetain(m_pPool, std::forward(args)...))); ++ typename std::enable_if::value, RetainPtr>::type ++ AppendNew(Args&&... args) { ++ return pdfium::WrapRetain(static_cast(AppendInternal( ++ pdfium::MakeRetain(m_pPool, std::forward(args)...)))); + } + template +- typename std::enable_if::value, T*>::type SetNewAt( +- size_t index, +- Args&&... args) { +- return static_cast( +- SetAt(index, pdfium::MakeRetain(std::forward(args)...))); ++ typename std::enable_if::value, RetainPtr>::type ++ SetNewAt(size_t index, Args&&... args) { ++ return pdfium::WrapRetain(static_cast(SetAtInternal( ++ index, pdfium::MakeRetain(std::forward(args)...)))); + } + template +- typename std::enable_if::value, T*>::type SetNewAt( +- size_t index, +- Args&&... args) { +- return static_cast(SetAt( +- index, pdfium::MakeRetain(m_pPool, std::forward(args)...))); ++ typename std::enable_if::value, RetainPtr>::type ++ SetNewAt(size_t index, Args&&... args) { ++ return pdfium::WrapRetain(static_cast(SetAtInternal( ++ index, pdfium::MakeRetain(m_pPool, std::forward(args)...)))); + } + template +- typename std::enable_if::value, T*>::type InsertNewAt( +- size_t index, +- Args&&... args) { +- return static_cast( +- InsertAt(index, pdfium::MakeRetain(std::forward(args)...))); ++ typename std::enable_if::value, RetainPtr>::type ++ InsertNewAt(size_t index, Args&&... args) { ++ return pdfium::WrapRetain(static_cast(InsertAtInternal( ++ index, pdfium::MakeRetain(std::forward(args)...)))); + } + template +- typename std::enable_if::value, T*>::type InsertNewAt( +- size_t index, +- Args&&... args) { +- return static_cast(InsertAt( +- index, pdfium::MakeRetain(m_pPool, std::forward(args)...))); ++ typename std::enable_if::value, RetainPtr>::type ++ InsertNewAt(size_t index, Args&&... args) { ++ return pdfium::WrapRetain(static_cast(InsertAtInternal( ++ index, pdfium::MakeRetain(m_pPool, std::forward(args)...)))); + } + +- // Takes ownership of |pObj|, returns unowned pointer to it. +- CPDF_Object* Add(RetainPtr pObj); +- CPDF_Object* SetAt(size_t index, RetainPtr pObj); +- CPDF_Object* InsertAt(size_t index, RetainPtr pObj); ++ // Adds non-null `pObj` to the end of the array, growing as appropriate. ++ void Append(RetainPtr pObj); ++ ++ // Overwrites the object at `index` with non-null `pObj`, if it is ++ // in bounds. Otherwise, `index` is out of bounds, and `pObj` is ++ // not stored. ++ void SetAt(size_t index, RetainPtr pObj); ++ ++ // Inserts non-null `pObj` at `index` and shifts by one position all of the ++ // objects beyond it like std::vector::insert(), if `index` is less than or ++ // equal to the current array size. Otherwise, `index` is out of bounds, ++ // and `pObj` is not stored. ++ void InsertAt(size_t index, RetainPtr pObj); + + void Clear(); + void RemoveAt(size_t index); +@@ -119,6 +144,13 @@ class CPDF_Array final : public CPDF_Object { + explicit CPDF_Array(const WeakPtr& pPool); + ~CPDF_Array() override; + ++ // No guarantees about result lifetime, use with caution. ++ const CPDF_Object* GetObjectAtInternal(size_t index) const; ++ CPDF_Object* GetMutableObjectAtInternal(size_t index); ++ CPDF_Object* AppendInternal(RetainPtr pObj); ++ CPDF_Object* SetAtInternal(size_t index, RetainPtr pObj); ++ CPDF_Object* InsertAtInternal(size_t index, RetainPtr pObj); ++ + RetainPtr CloneNonCyclic( + bool bDirect, + std::set* pVisited) const override; +@@ -130,9 +162,12 @@ class CPDF_Array final : public CPDF_Object { + + class CPDF_ArrayLocker { + public: ++ FX_STACK_ALLOCATED(); + using const_iterator = CPDF_Array::const_iterator; + + explicit CPDF_ArrayLocker(const CPDF_Array* pArray); ++ explicit CPDF_ArrayLocker(RetainPtr pArray); ++ explicit CPDF_ArrayLocker(RetainPtr pArray); + ~CPDF_ArrayLocker(); + + const_iterator begin() const { +@@ -149,7 +184,7 @@ class CPDF_ArrayLocker { + }; + + inline CPDF_Array* ToArray(CPDF_Object* obj) { +- return obj ? obj->AsArray() : nullptr; ++ return obj ? obj->AsMutableArray() : nullptr; + } + + inline const CPDF_Array* ToArray(const CPDF_Object* obj) { +@@ -160,4 +195,8 @@ inline RetainPtr ToArray(RetainPtr obj) { + return RetainPtr(ToArray(obj.Get())); + } + ++inline RetainPtr ToArray(RetainPtr obj) { ++ return RetainPtr(ToArray(obj.Get())); ++} ++ + #endif // CORE_FPDFAPI_PARSER_CPDF_ARRAY_H_ +diff --git a/core/fpdfapi/parser/cpdf_array_unittest.cpp b/core/fpdfapi/parser/cpdf_array_unittest.cpp +index 457961ff7..9b3e841fa 100644 +--- a/core/fpdfapi/parser/cpdf_array_unittest.cpp ++++ b/core/fpdfapi/parser/cpdf_array_unittest.cpp +@@ -1,24 +1,25 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/parser/cpdf_array.h" + ++#include + #include + #include + + #include "core/fpdfapi/parser/cpdf_boolean.h" ++#include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + +-TEST(cpdf_array, GetBooleanAt) { ++TEST(ArrayTest, GetBooleanAt) { + auto arr = pdfium::MakeRetain(); +- arr->AddNew(true); +- arr->AddNew(false); +- arr->AddNew(100); +- arr->AddNew(0); ++ arr->AppendNew(true); ++ arr->AppendNew(false); ++ arr->AppendNew(100); ++ arr->AppendNew(0); + + ASSERT_EQ(4u, arr->size()); + EXPECT_TRUE(arr->GetBooleanAt(0, true)); +@@ -33,92 +34,88 @@ TEST(cpdf_array, GetBooleanAt) { + EXPECT_FALSE(arr->GetBooleanAt(99, false)); + } + +-TEST(cpdf_array, RemoveAt) { ++TEST(ArrayTest, RemoveAt) { + { + const int elems[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + auto arr = pdfium::MakeRetain(); +- for (size_t i = 0; i < FX_ArraySize(elems); ++i) +- arr->AddNew(elems[i]); ++ for (size_t i = 0; i < std::size(elems); ++i) ++ arr->AppendNew(elems[i]); + for (size_t i = 0; i < 3; ++i) + arr->RemoveAt(3); + const int expected[] = {1, 2, 3, 7, 8, 9, 10}; +- ASSERT_EQ(FX_ArraySize(expected), arr->size()); +- for (size_t i = 0; i < FX_ArraySize(expected); ++i) ++ ASSERT_EQ(std::size(expected), arr->size()); ++ for (size_t i = 0; i < std::size(expected); ++i) + EXPECT_EQ(expected[i], arr->GetIntegerAt(i)); + arr->RemoveAt(4); + arr->RemoveAt(4); + const int expected2[] = {1, 2, 3, 7, 10}; +- ASSERT_EQ(FX_ArraySize(expected2), arr->size()); +- for (size_t i = 0; i < FX_ArraySize(expected2); ++i) ++ ASSERT_EQ(std::size(expected2), arr->size()); ++ for (size_t i = 0; i < std::size(expected2); ++i) + EXPECT_EQ(expected2[i], arr->GetIntegerAt(i)); + } + { + // When the range is out of bound, RemoveAt() has no effect. + const int elems[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + auto arr = pdfium::MakeRetain(); +- for (size_t i = 0; i < FX_ArraySize(elems); ++i) +- arr->AddNew(elems[i]); ++ for (size_t i = 0; i < std::size(elems); ++i) ++ arr->AppendNew(elems[i]); + arr->RemoveAt(11); +- EXPECT_EQ(FX_ArraySize(elems), arr->size()); ++ EXPECT_EQ(std::size(elems), arr->size()); + } + } + +-TEST(cpdf_array, Clear) { ++TEST(ArrayTest, Clear) { + const int elems[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + auto arr = pdfium::MakeRetain(); + EXPECT_EQ(0U, arr->size()); +- for (size_t i = 0; i < FX_ArraySize(elems); ++i) +- arr->AddNew(elems[i]); +- EXPECT_EQ(FX_ArraySize(elems), arr->size()); ++ for (size_t i = 0; i < std::size(elems); ++i) ++ arr->AppendNew(elems[i]); ++ EXPECT_EQ(std::size(elems), arr->size()); + arr->Clear(); + EXPECT_EQ(0U, arr->size()); + } + +-TEST(cpdf_array, InsertAt) { +- { +- const int elems[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; +- auto arr = pdfium::MakeRetain(); +- for (size_t i = 0; i < FX_ArraySize(elems); ++i) +- arr->InsertNewAt(i, elems[i]); +- ASSERT_EQ(FX_ArraySize(elems), arr->size()); +- for (size_t i = 0; i < FX_ArraySize(elems); ++i) +- EXPECT_EQ(elems[i], arr->GetIntegerAt(i)); +- arr->InsertNewAt(3, 33); +- arr->InsertNewAt(6, 55); +- arr->InsertNewAt(12, 12); +- const int expected[] = {1, 2, 3, 33, 4, 5, 55, 6, 7, 8, 9, 10, 12}; +- ASSERT_EQ(FX_ArraySize(expected), arr->size()); +- for (size_t i = 0; i < FX_ArraySize(expected); ++i) +- EXPECT_EQ(expected[i], arr->GetIntegerAt(i)); +- } +- { +- // When the position to insert is beyond the upper bound, +- // an element is inserted at that position while other unfilled +- // positions have nullptr. +- const int elems[] = {1, 2}; +- auto arr = pdfium::MakeRetain(); +- for (size_t i = 0; i < FX_ArraySize(elems); ++i) +- arr->InsertNewAt(i, elems[i]); +- arr->InsertNewAt(10, 10); +- ASSERT_EQ(11u, arr->size()); +- for (size_t i = 0; i < FX_ArraySize(elems); ++i) +- EXPECT_EQ(elems[i], arr->GetIntegerAt(i)); +- for (size_t i = FX_ArraySize(elems); i < 10; ++i) +- EXPECT_EQ(nullptr, arr->GetObjectAt(i)); +- EXPECT_EQ(10, arr->GetIntegerAt(10)); +- } ++TEST(ArrayTest, SetAtBeyond) { ++ auto arr = pdfium::MakeRetain(); ++ EXPECT_FALSE(arr->SetNewAt(0, 0)); ++ EXPECT_TRUE(arr->InsertNewAt(0, 0)); ++ EXPECT_FALSE(arr->SetNewAt(1, 0)); ++} ++ ++TEST(ArrayTest, InsertAt) { ++ const int elems[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; ++ auto arr = pdfium::MakeRetain(); ++ for (size_t i = 0; i < std::size(elems); ++i) ++ arr->InsertNewAt(i, elems[i]); ++ ASSERT_EQ(std::size(elems), arr->size()); ++ for (size_t i = 0; i < std::size(elems); ++i) ++ EXPECT_EQ(elems[i], arr->GetIntegerAt(i)); ++ arr->InsertNewAt(3, 33); ++ arr->InsertNewAt(6, 55); ++ arr->InsertNewAt(12, 12); ++ const int expected[] = {1, 2, 3, 33, 4, 5, 55, 6, 7, 8, 9, 10, 12}; ++ ASSERT_EQ(std::size(expected), arr->size()); ++ for (size_t i = 0; i < std::size(expected); ++i) ++ EXPECT_EQ(expected[i], arr->GetIntegerAt(i)); + } + +-TEST(cpdf_array, Clone) { ++TEST(ArrayTest, InsertAtBeyond) { ++ auto arr = pdfium::MakeRetain(); ++ EXPECT_FALSE(arr->InsertNewAt(1, 0)); ++ EXPECT_TRUE(arr->InsertNewAt(0, 0)); ++ EXPECT_FALSE(arr->InsertNewAt(2, 0)); ++} ++ ++TEST(ArrayTest, Clone) { + { + // Basic case. + const int elems[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + auto arr = pdfium::MakeRetain(); +- for (size_t i = 0; i < FX_ArraySize(elems); ++i) ++ for (size_t i = 0; i < std::size(elems); ++i) + arr->InsertNewAt(i, elems[i]); + RetainPtr arr2 = ToArray(arr->Clone()); + ASSERT_EQ(arr->size(), arr2->size()); +- for (size_t i = 0; i < FX_ArraySize(elems); ++i) { ++ for (size_t i = 0; i < std::size(elems); ++i) { + // Clone() always create new objects. + EXPECT_NE(arr->GetObjectAt(i), arr2->GetObjectAt(i)); + EXPECT_EQ(arr->GetIntegerAt(i), arr2->GetIntegerAt(i)); +@@ -132,7 +129,7 @@ TEST(cpdf_array, Clone) { + {1, 2, 3, 4, 5}, {10, 9, 8, 7, 6}, {11, 12, 13, 14, 15}}; + auto arr = pdfium::MakeRetain(); + // Indirect references to indirect objects. +- auto obj_holder = pdfium::MakeUnique(); ++ auto obj_holder = std::make_unique(); + for (size_t i = 0; i < kNumOfRows; ++i) { + auto arr_elem = pdfium::MakeRetain(); + for (size_t j = 0; j < kNumOfRowElems; ++j) { +@@ -154,15 +151,15 @@ TEST(cpdf_array, Clone) { + RetainPtr arr2 = ToArray(arr->CloneDirectObject()); + ASSERT_EQ(arr->size(), arr2->size()); + for (size_t i = 0; i < kNumOfRows; ++i) { +- CPDF_Array* arr_elem = arr->GetObjectAt(i)->AsArray(); +- CPDF_Array* arr1_elem = arr1->GetObjectAt(i)->AsArray(); +- CPDF_Array* arr2_elem = arr2->GetObjectAt(i)->AsArray(); ++ const CPDF_Array* arr_elem = arr->GetObjectAt(i)->AsArray(); ++ const CPDF_Array* arr1_elem = arr1->GetObjectAt(i)->AsArray(); ++ const CPDF_Array* arr2_elem = arr2->GetObjectAt(i)->AsArray(); + EXPECT_NE(arr_elem, arr1_elem); + EXPECT_NE(arr_elem, arr2_elem); + for (size_t j = 0; j < kNumOfRowElems; ++j) { +- auto* elem_obj = arr_elem->GetObjectAt(j); +- auto* elem_obj1 = arr1_elem->GetObjectAt(j); +- auto* elem_obj2 = arr2_elem->GetObjectAt(j); ++ auto elem_obj = arr_elem->GetObjectAt(j); ++ auto elem_obj1 = arr1_elem->GetObjectAt(j); ++ auto elem_obj2 = arr2_elem->GetObjectAt(j); + // Results from not deferencing reference objects. + EXPECT_NE(elem_obj, elem_obj1); + EXPECT_TRUE(elem_obj1->IsReference()); +@@ -181,7 +178,7 @@ TEST(cpdf_array, Clone) { + for (size_t i = 0; i < kNumOfRows; ++i) { + for (size_t j = 0; j < kNumOfRowElems; ++j) { + // Results from not deferencing reference objects. +- auto* elem_obj1 = arr1->GetObjectAt(i)->AsArray()->GetObjectAt(j); ++ auto elem_obj1 = arr1->GetObjectAt(i)->AsArray()->GetObjectAt(j); + EXPECT_TRUE(elem_obj1->IsReference()); + EXPECT_EQ(elems[i][j], elem_obj1->GetInteger()); + // Results from deferencing reference objects. +@@ -192,16 +189,51 @@ TEST(cpdf_array, Clone) { + } + } + +-TEST(cpdf_array, Iterator) { ++TEST(ArrayTest, Find) { ++ auto arr = pdfium::MakeRetain(); ++ auto dict0 = pdfium::MakeRetain(); ++ auto dict1 = pdfium::MakeRetain(); ++ auto dict2 = pdfium::MakeRetain(); ++ arr->Append(dict0); ++ arr->Append(dict1); ++ ++ absl::optional maybe_found = arr->Find(nullptr); ++ EXPECT_FALSE(maybe_found.has_value()); ++ ++ maybe_found = arr->Find(dict0.Get()); ++ ASSERT_TRUE(maybe_found.has_value()); ++ EXPECT_EQ(0u, maybe_found.value()); ++ ++ maybe_found = arr->Find(dict1.Get()); ++ ASSERT_TRUE(maybe_found.has_value()); ++ EXPECT_EQ(1u, maybe_found.value()); ++ ++ maybe_found = arr->Find(dict2.Get()); ++ EXPECT_FALSE(maybe_found.has_value()); ++} ++ ++TEST(ArrayTest, Contains) { ++ auto arr = pdfium::MakeRetain(); ++ auto dict0 = pdfium::MakeRetain(); ++ auto dict1 = pdfium::MakeRetain(); ++ auto dict2 = pdfium::MakeRetain(); ++ arr->Append(dict0); ++ arr->Append(dict1); ++ EXPECT_TRUE(arr->Contains(dict0.Get())); ++ EXPECT_TRUE(arr->Contains(dict1.Get())); ++ EXPECT_FALSE(arr->Contains(dict2.Get())); ++} ++ ++TEST(ArrayTest, Iterator) { + const int elems[] = {-23, -11, 3, 455, 2345877, + 0, 7895330, -12564334, 10000, -100000}; + auto arr = pdfium::MakeRetain(); +- for (size_t i = 0; i < FX_ArraySize(elems); ++i) ++ for (size_t i = 0; i < std::size(elems); ++i) + arr->InsertNewAt(i, elems[i]); +- size_t index = 0; + +- CPDF_ArrayLocker locker(arr.Get()); ++ size_t index = 0; ++ CPDF_ArrayLocker locker(arr); + for (const auto& it : locker) + EXPECT_EQ(elems[index++], it->AsNumber()->GetInteger()); +- EXPECT_EQ(FX_ArraySize(elems), index); ++ EXPECT_EQ(std::size(elems), index); + } +diff --git a/core/fpdfapi/parser/cpdf_boolean.cpp b/core/fpdfapi/parser/cpdf_boolean.cpp +index b5e12eb86..340495995 100644 +--- a/core/fpdfapi/parser/cpdf_boolean.cpp ++++ b/core/fpdfapi/parser/cpdf_boolean.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,6 @@ + #include "core/fpdfapi/parser/cpdf_boolean.h" + + #include "core/fxcrt/fx_stream.h" +-#include "third_party/base/ptr_util.h" + + CPDF_Boolean::CPDF_Boolean() = default; + +@@ -35,15 +34,7 @@ void CPDF_Boolean::SetString(const ByteString& str) { + m_bValue = (str == "true"); + } + +-bool CPDF_Boolean::IsBoolean() const { +- return true; +-} +- +-CPDF_Boolean* CPDF_Boolean::AsBoolean() { +- return this; +-} +- +-const CPDF_Boolean* CPDF_Boolean::AsBoolean() const { ++CPDF_Boolean* CPDF_Boolean::AsMutableBoolean() { + return this; + } + +diff --git a/core/fpdfapi/parser/cpdf_boolean.h b/core/fpdfapi/parser/cpdf_boolean.h +index 8ef47ad5d..9cec390e3 100644 +--- a/core/fpdfapi/parser/cpdf_boolean.h ++++ b/core/fpdfapi/parser/cpdf_boolean.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,16 +7,13 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_BOOLEAN_H_ + #define CORE_FPDFAPI_PARSER_CPDF_BOOLEAN_H_ + +-#include +- + #include "core/fpdfapi/parser/cpdf_object.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/retain_ptr.h" + + class CPDF_Boolean final : public CPDF_Object { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // CPDF_Object: + Type GetType() const override; +@@ -24,9 +21,7 @@ class CPDF_Boolean final : public CPDF_Object { + ByteString GetString() const override; + int GetInteger() const override; + void SetString(const ByteString& str) override; +- bool IsBoolean() const override; +- CPDF_Boolean* AsBoolean() override; +- const CPDF_Boolean* AsBoolean() const override; ++ CPDF_Boolean* AsMutableBoolean() override; + bool WriteTo(IFX_ArchiveStream* archive, + const CPDF_Encryptor* encryptor) const override; + +@@ -39,7 +34,7 @@ class CPDF_Boolean final : public CPDF_Object { + }; + + inline CPDF_Boolean* ToBoolean(CPDF_Object* obj) { +- return obj ? obj->AsBoolean() : nullptr; ++ return obj ? obj->AsMutableBoolean() : nullptr; + } + + inline const CPDF_Boolean* ToBoolean(const CPDF_Object* obj) { +diff --git a/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp b/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp +index a6de007ba..7f0b7fed5 100644 +--- a/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp ++++ b/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp +@@ -1,19 +1,18 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/parser/cpdf_cross_ref_avail.h" + +-#include +-#include +-#include +- + #include "core/fpdfapi/parser/cpdf_dictionary.h" +-#include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_read_validator.h" + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_syntax_parser.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/notreached.h" ++#include "third_party/base/numerics/safe_conversions.h" + + namespace { + +@@ -30,20 +29,20 @@ constexpr char kEncryptKey[] = "Encrypt"; + CPDF_CrossRefAvail::CPDF_CrossRefAvail(CPDF_SyntaxParser* parser, + FX_FILESIZE last_crossref_offset) + : parser_(parser), last_crossref_offset_(last_crossref_offset) { +- ASSERT(parser_); ++ DCHECK(parser_); + AddCrossRefForCheck(last_crossref_offset); + } + +-CPDF_CrossRefAvail::~CPDF_CrossRefAvail() {} ++CPDF_CrossRefAvail::~CPDF_CrossRefAvail() = default; + + CPDF_DataAvail::DocAvailStatus CPDF_CrossRefAvail::CheckAvail() { +- if (current_status_ == CPDF_DataAvail::DataAvailable) +- return CPDF_DataAvail::DataAvailable; ++ if (status_ == CPDF_DataAvail::kDataAvailable) ++ return CPDF_DataAvail::kDataAvailable; + +- const CPDF_ReadValidator::Session read_session(GetValidator()); ++ CPDF_ReadValidator::ScopedSession read_session(GetValidator()); + while (true) { + bool check_result = false; +- switch (current_state_) { ++ switch (state_) { + case State::kCrossRefCheck: + check_result = CheckCrossRef(); + break; +@@ -56,7 +55,7 @@ CPDF_DataAvail::DocAvailStatus CPDF_CrossRefAvail::CheckAvail() { + case State::kDone: + break; + default: { +- current_status_ = CPDF_DataAvail::DataError; ++ status_ = CPDF_DataAvail::kDataError; + NOTREACHED(); + break; + } +@@ -64,14 +63,14 @@ CPDF_DataAvail::DocAvailStatus CPDF_CrossRefAvail::CheckAvail() { + if (!check_result) + break; + +- ASSERT(!GetValidator()->has_read_problems()); ++ DCHECK(!GetValidator()->has_read_problems()); + } +- return current_status_; ++ return status_; + } + + bool CPDF_CrossRefAvail::CheckReadProblems() { + if (GetValidator()->read_error()) { +- current_status_ = CPDF_DataAvail::DataError; ++ status_ = CPDF_DataAvail::kDataError; + return true; + } + return GetValidator()->has_unavailable_data(); +@@ -80,13 +79,13 @@ bool CPDF_CrossRefAvail::CheckReadProblems() { + bool CPDF_CrossRefAvail::CheckCrossRef() { + if (cross_refs_for_check_.empty()) { + // All cross refs were checked. +- current_state_ = State::kDone; +- current_status_ = CPDF_DataAvail::DataAvailable; ++ state_ = State::kDone; ++ status_ = CPDF_DataAvail::kDataAvailable; + return true; + } + parser_->SetPos(cross_refs_for_check_.front()); + +- const ByteString first_word = parser_->PeekNextWord(nullptr); ++ const ByteString first_word = parser_->PeekNextWord(); + if (CheckReadProblems()) + return false; + +@@ -105,36 +104,36 @@ bool CPDF_CrossRefAvail::CheckCrossRefV4() { + return false; + + if (keyword != kCrossRefKeyword) { +- current_status_ = CPDF_DataAvail::DataError; ++ status_ = CPDF_DataAvail::kDataError; + return false; + } + +- current_state_ = State::kCrossRefV4ItemCheck; +- current_offset_ = parser_->GetPos(); ++ state_ = State::kCrossRefV4ItemCheck; ++ offset_ = parser_->GetPos(); + return true; + } + + bool CPDF_CrossRefAvail::CheckCrossRefV4Item() { +- parser_->SetPos(current_offset_); ++ parser_->SetPos(offset_); + const ByteString keyword = parser_->GetKeyword(); + if (CheckReadProblems()) + return false; + + if (keyword.IsEmpty()) { +- current_status_ = CPDF_DataAvail::DataError; ++ status_ = CPDF_DataAvail::kDataError; + return false; + } + + if (keyword == kTrailerKeyword) +- current_state_ = State::kCrossRefV4TrailerCheck; ++ state_ = State::kCrossRefV4TrailerCheck; + + // Go to next item. +- current_offset_ = parser_->GetPos(); ++ offset_ = parser_->GetPos(); + return true; + } + + bool CPDF_CrossRefAvail::CheckCrossRefV4Trailer() { +- parser_->SetPos(current_offset_); ++ parser_->SetPos(offset_); + + RetainPtr trailer = + ToDictionary(parser_->GetObjectBody(nullptr)); +@@ -142,30 +141,29 @@ bool CPDF_CrossRefAvail::CheckCrossRefV4Trailer() { + return false; + + if (!trailer) { +- current_status_ = CPDF_DataAvail::DataError; ++ status_ = CPDF_DataAvail::kDataError; + return false; + } + + if (ToReference(trailer->GetObjectFor(kEncryptKey))) { +- current_status_ = CPDF_DataAvail::DataError; ++ status_ = CPDF_DataAvail::kDataError; + return false; + } + +- const int32_t xrefpos = +- GetDirectInteger(trailer.Get(), kPrevCrossRefFieldKey); +- if (xrefpos && ++ const int32_t xrefpos = trailer->GetDirectIntegerFor(kPrevCrossRefFieldKey); ++ if (xrefpos > 0 && + pdfium::base::IsValueInRangeForNumericType(xrefpos)) + AddCrossRefForCheck(static_cast(xrefpos)); + + const int32_t stream_xref_offset = +- GetDirectInteger(trailer.Get(), kPrevCrossRefStreamOffsetFieldKey); +- if (stream_xref_offset && ++ trailer->GetDirectIntegerFor(kPrevCrossRefStreamOffsetFieldKey); ++ if (stream_xref_offset > 0 && + pdfium::base::IsValueInRangeForNumericType( + stream_xref_offset)) + AddCrossRefForCheck(static_cast(stream_xref_offset)); + + // Goto check next crossref +- current_state_ = State::kCrossRefCheck; ++ state_ = State::kCrossRefCheck; + return true; + } + +@@ -175,32 +173,32 @@ bool CPDF_CrossRefAvail::CheckCrossRefStream() { + if (CheckReadProblems()) + return false; + +- const CPDF_Dictionary* trailer = ++ RetainPtr trailer = + cross_ref && cross_ref->IsStream() ? cross_ref->GetDict() : nullptr; + if (!trailer) { +- current_status_ = CPDF_DataAvail::DataError; ++ status_ = CPDF_DataAvail::kDataError; + return false; + } + + if (ToReference(trailer->GetObjectFor(kEncryptKey))) { +- current_status_ = CPDF_DataAvail::DataError; ++ status_ = CPDF_DataAvail::kDataError; + return false; + } + +- const CPDF_Name* type_name = ToName(trailer->GetObjectFor(kTypeFieldKey)); +- if (type_name && type_name->GetString() == kXRefKeyword) { ++ if (trailer->GetNameFor(kTypeFieldKey) == kXRefKeyword) { + const int32_t xrefpos = trailer->GetIntegerFor(kPrevCrossRefFieldKey); +- if (xrefpos && +- pdfium::base::IsValueInRangeForNumericType(xrefpos)) ++ if (xrefpos > 0 && ++ pdfium::base::IsValueInRangeForNumericType(xrefpos)) { + AddCrossRefForCheck(static_cast(xrefpos)); ++ } + } + // Goto check next crossref +- current_state_ = State::kCrossRefCheck; ++ state_ = State::kCrossRefCheck; + return true; + } + + void CPDF_CrossRefAvail::AddCrossRefForCheck(FX_FILESIZE crossref_offset) { +- if (registered_crossrefs_.count(crossref_offset)) ++ if (pdfium::Contains(registered_crossrefs_, crossref_offset)) + return; + + cross_refs_for_check_.push(crossref_offset); +diff --git a/core/fpdfapi/parser/cpdf_cross_ref_avail.h b/core/fpdfapi/parser/cpdf_cross_ref_avail.h +index e55041017..276fab1f8 100644 +--- a/core/fpdfapi/parser/cpdf_cross_ref_avail.h ++++ b/core/fpdfapi/parser/cpdf_cross_ref_avail.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,6 +9,7 @@ + #include + + #include "core/fpdfapi/parser/cpdf_data_avail.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + + class CPDF_SyntaxParser; +@@ -42,12 +43,11 @@ class CPDF_CrossRefAvail { + + RetainPtr GetValidator(); + +- UnownedPtr parser_; +- const FX_FILESIZE last_crossref_offset_ = 0; +- CPDF_DataAvail::DocAvailStatus current_status_ = +- CPDF_DataAvail::DataNotAvailable; +- State current_state_ = State::kCrossRefCheck; +- FX_FILESIZE current_offset_ = 0; ++ UnownedPtr const parser_; ++ const FX_FILESIZE last_crossref_offset_; ++ CPDF_DataAvail::DocAvailStatus status_ = CPDF_DataAvail::kDataNotAvailable; ++ State state_ = State::kCrossRefCheck; ++ FX_FILESIZE offset_ = 0; + std::queue cross_refs_for_check_; + std::set registered_crossrefs_; + }; +diff --git a/core/fpdfapi/parser/cpdf_cross_ref_avail_unittest.cpp b/core/fpdfapi/parser/cpdf_cross_ref_avail_unittest.cpp +index 6cdc3f181..57f3bd4c4 100644 +--- a/core/fpdfapi/parser/cpdf_cross_ref_avail_unittest.cpp ++++ b/core/fpdfapi/parser/cpdf_cross_ref_avail_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,21 +8,20 @@ + #include + + #include "core/fpdfapi/parser/cpdf_syntax_parser.h" +-#include "core/fxcrt/cfx_readonlymemorystream.h" ++#include "core/fxcrt/cfx_read_only_span_stream.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + + namespace { + + std::unique_ptr MakeParserForBuffer( + pdfium::span buffer) { +- return pdfium::MakeUnique( +- pdfium::MakeRetain(buffer)); ++ return std::make_unique( ++ pdfium::MakeRetain(buffer)); + } + + } // namespace + +-TEST(CPDF_CrossRefAvailTest, CheckCrossRefV4) { ++TEST(CrossRefAvailTest, CheckCrossRefV4) { + const unsigned char xref_table[] = + "xref \n" + "0 6 \n" +@@ -39,13 +38,13 @@ TEST(CPDF_CrossRefAvailTest, CheckCrossRefV4) { + const FX_FILESIZE last_crossref_offset = 0; + + auto parser = MakeParserForBuffer(xref_table); +- auto cross_ref_avail = pdfium::MakeUnique( +- parser.get(), last_crossref_offset); ++ auto cross_ref_avail = ++ std::make_unique(parser.get(), last_crossref_offset); + +- EXPECT_EQ(CPDF_DataAvail::DataAvailable, cross_ref_avail->CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, cross_ref_avail->CheckAvail()); + } + +-TEST(CPDF_CrossRefAvailTest, CheckCrossRefStream) { ++TEST(CrossRefAvailTest, CheckCrossRefStream) { + const unsigned char xref_stream[] = + "16 0 obj\n" + "<>" +@@ -56,13 +55,13 @@ TEST(CPDF_CrossRefAvailTest, CheckCrossRefStream) { + const FX_FILESIZE last_crossref_offset = 0; + + auto parser = MakeParserForBuffer(xref_stream); +- auto cross_ref_avail = pdfium::MakeUnique( +- parser.get(), last_crossref_offset); ++ auto cross_ref_avail = ++ std::make_unique(parser.get(), last_crossref_offset); + +- EXPECT_EQ(CPDF_DataAvail::DataAvailable, cross_ref_avail->CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, cross_ref_avail->CheckAvail()); + } + +-TEST(CPDF_CrossRefAvailTest, IncorrectStartOffset) { ++TEST(CrossRefAvailTest, IncorrectStartOffset) { + const unsigned char xref_stream[] = + "16 0 obj\n" + "<>" +@@ -74,13 +73,13 @@ TEST(CPDF_CrossRefAvailTest, IncorrectStartOffset) { + const FX_FILESIZE last_crossref_offset = 70000; + + auto parser = MakeParserForBuffer(xref_stream); +- auto cross_ref_avail = pdfium::MakeUnique( +- parser.get(), last_crossref_offset); ++ auto cross_ref_avail = ++ std::make_unique(parser.get(), last_crossref_offset); + +- EXPECT_EQ(CPDF_DataAvail::DataError, cross_ref_avail->CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataError, cross_ref_avail->CheckAvail()); + } + +-TEST(CPDF_CrossRefAvailTest, IncorrectPrevOffset) { ++TEST(CrossRefAvailTest, IncorrectPrevOffset) { + const unsigned char xref_stream[] = + "16 0 obj\n" + "<>" +@@ -91,12 +90,12 @@ TEST(CPDF_CrossRefAvailTest, IncorrectPrevOffset) { + const FX_FILESIZE last_crossref_offset = 0; + + auto parser = MakeParserForBuffer(xref_stream); +- auto cross_ref_avail = pdfium::MakeUnique( +- parser.get(), last_crossref_offset); +- EXPECT_EQ(CPDF_DataAvail::DataError, cross_ref_avail->CheckAvail()); ++ auto cross_ref_avail = ++ std::make_unique(parser.get(), last_crossref_offset); ++ EXPECT_EQ(CPDF_DataAvail::kDataError, cross_ref_avail->CheckAvail()); + } + +-TEST(CPDF_CrossRefAvailTest, IncorrectPrevStreamOffset) { ++TEST(CrossRefAvailTest, IncorrectPrevStreamOffset) { + const unsigned char xref_table[] = + "xref \n" + "0 6 \n" +@@ -113,24 +112,24 @@ TEST(CPDF_CrossRefAvailTest, IncorrectPrevStreamOffset) { + const FX_FILESIZE last_crossref_offset = 0; + + auto parser = MakeParserForBuffer(xref_table); +- auto cross_ref_avail = pdfium::MakeUnique( +- parser.get(), last_crossref_offset); +- EXPECT_EQ(CPDF_DataAvail::DataError, cross_ref_avail->CheckAvail()); ++ auto cross_ref_avail = ++ std::make_unique(parser.get(), last_crossref_offset); ++ EXPECT_EQ(CPDF_DataAvail::kDataError, cross_ref_avail->CheckAvail()); + } + +-TEST(CPDF_CrossRefAvailTest, IncorrectData) { ++TEST(CrossRefAvailTest, IncorrectData) { + const unsigned char incorrect_data[] = + "fiajaoilf w9ifaoihwoiafhja wfijaofijoiaw fhj oiawhfoiah " + "wfoihoiwfghouiafghwoigahfi"; + const FX_FILESIZE last_crossref_offset = 0; + + auto parser = MakeParserForBuffer(incorrect_data); +- auto cross_ref_avail = pdfium::MakeUnique( +- parser.get(), last_crossref_offset); +- EXPECT_EQ(CPDF_DataAvail::DataError, cross_ref_avail->CheckAvail()); ++ auto cross_ref_avail = ++ std::make_unique(parser.get(), last_crossref_offset); ++ EXPECT_EQ(CPDF_DataAvail::kDataError, cross_ref_avail->CheckAvail()); + } + +-TEST(CPDF_CrossRefAvailTest, ThreeCrossRefV4) { ++TEST(CrossRefAvailTest, ThreeCrossRefV4) { + char int_buffer[100]; + std::string table = "pdf blah blah blah\n"; + size_t cur_offset = table.size(); +@@ -173,12 +172,12 @@ TEST(CPDF_CrossRefAvailTest, ThreeCrossRefV4) { + const FX_FILESIZE last_crossref_offset = static_cast(cur_offset); + + auto parser = MakeParserForBuffer(pdfium::as_bytes(pdfium::make_span(table))); +- auto cross_ref_avail = pdfium::MakeUnique( +- parser.get(), last_crossref_offset); +- EXPECT_EQ(CPDF_DataAvail::DataAvailable, cross_ref_avail->CheckAvail()); ++ auto cross_ref_avail = ++ std::make_unique(parser.get(), last_crossref_offset); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, cross_ref_avail->CheckAvail()); + } + +-TEST(CPDF_CrossRefAvailTest, ThreeCrossRefV5) { ++TEST(CrossRefAvailTest, ThreeCrossRefV5) { + char int_buffer[100]; + std::string table = "pdf blah blah blah\n"; + size_t cur_offset = table.size(); +@@ -217,12 +216,12 @@ TEST(CPDF_CrossRefAvailTest, ThreeCrossRefV5) { + const FX_FILESIZE last_crossref_offset = static_cast(cur_offset); + + auto parser = MakeParserForBuffer(pdfium::as_bytes(pdfium::make_span(table))); +- auto cross_ref_avail = pdfium::MakeUnique( +- parser.get(), last_crossref_offset); +- EXPECT_EQ(CPDF_DataAvail::DataAvailable, cross_ref_avail->CheckAvail()); ++ auto cross_ref_avail = ++ std::make_unique(parser.get(), last_crossref_offset); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, cross_ref_avail->CheckAvail()); + } + +-TEST(CPDF_CrossRefAvailTest, Mixed) { ++TEST(CrossRefAvailTest, Mixed) { + char int_buffer[100]; + std::string table = "pdf blah blah blah\n"; + +@@ -266,12 +265,12 @@ TEST(CPDF_CrossRefAvailTest, Mixed) { + const FX_FILESIZE last_crossref_offset = last_v4_table_offset; + + auto parser = MakeParserForBuffer(pdfium::as_bytes(pdfium::make_span(table))); +- auto cross_ref_avail = pdfium::MakeUnique( +- parser.get(), last_crossref_offset); +- EXPECT_EQ(CPDF_DataAvail::DataAvailable, cross_ref_avail->CheckAvail()); ++ auto cross_ref_avail = ++ std::make_unique(parser.get(), last_crossref_offset); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, cross_ref_avail->CheckAvail()); + } + +-TEST(CPDF_CrossRefAvailTest, CrossRefV5IsNotStream) { ++TEST(CrossRefAvailTest, CrossRefV5IsNotStream) { + const unsigned char invalid_xref_stream[] = + "16 0 obj\n" + "[/array /object]\n" +@@ -280,12 +279,12 @@ TEST(CPDF_CrossRefAvailTest, CrossRefV5IsNotStream) { + const FX_FILESIZE last_crossref_offset = 0; + + auto parser = MakeParserForBuffer(invalid_xref_stream); +- auto cross_ref_avail = pdfium::MakeUnique( +- parser.get(), last_crossref_offset); +- EXPECT_EQ(CPDF_DataAvail::DataError, cross_ref_avail->CheckAvail()); ++ auto cross_ref_avail = ++ std::make_unique(parser.get(), last_crossref_offset); ++ EXPECT_EQ(CPDF_DataAvail::kDataError, cross_ref_avail->CheckAvail()); + } + +-TEST(CPDF_CrossRefAvailTest, CrossRefV4WithEncryptRef) { ++TEST(CrossRefAvailTest, CrossRefV4WithEncryptRef) { + const unsigned char xref_table[] = + "xref \n" + "0 6 \n" +@@ -303,12 +302,12 @@ TEST(CPDF_CrossRefAvailTest, CrossRefV4WithEncryptRef) { + const FX_FILESIZE last_crossref_offset = 0; + + auto parser = MakeParserForBuffer(xref_table); +- auto cross_ref_avail = pdfium::MakeUnique( +- parser.get(), last_crossref_offset); +- EXPECT_EQ(CPDF_DataAvail::DataError, cross_ref_avail->CheckAvail()); ++ auto cross_ref_avail = ++ std::make_unique(parser.get(), last_crossref_offset); ++ EXPECT_EQ(CPDF_DataAvail::kDataError, cross_ref_avail->CheckAvail()); + } + +-TEST(CPDF_CrossRefAvailTest, CrossRefStreamWithEncryptRef) { ++TEST(CrossRefAvailTest, CrossRefStreamWithEncryptRef) { + const unsigned char xref_stream[] = + "16 0 obj\n" + "<>" +@@ -319,7 +318,7 @@ TEST(CPDF_CrossRefAvailTest, CrossRefStreamWithEncryptRef) { + const FX_FILESIZE last_crossref_offset = 0; + + auto parser = MakeParserForBuffer(xref_stream); +- auto cross_ref_avail = pdfium::MakeUnique( +- parser.get(), last_crossref_offset); +- EXPECT_EQ(CPDF_DataAvail::DataError, cross_ref_avail->CheckAvail()); ++ auto cross_ref_avail = ++ std::make_unique(parser.get(), last_crossref_offset); ++ EXPECT_EQ(CPDF_DataAvail::kDataError, cross_ref_avail->CheckAvail()); + } +diff --git a/core/fpdfapi/parser/cpdf_cross_ref_table.cpp b/core/fpdfapi/parser/cpdf_cross_ref_table.cpp +index bd25b6ca1..0bb5bb69c 100644 +--- a/core/fpdfapi/parser/cpdf_cross_ref_table.cpp ++++ b/core/fpdfapi/parser/cpdf_cross_ref_table.cpp +@@ -1,15 +1,15 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/parser/cpdf_cross_ref_table.h" + + #include +-#include + + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_parser.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/notreached.h" + + // static + std::unique_ptr CPDF_CrossRefTable::MergeUp( +@@ -27,13 +27,16 @@ std::unique_ptr CPDF_CrossRefTable::MergeUp( + + CPDF_CrossRefTable::CPDF_CrossRefTable() = default; + +-CPDF_CrossRefTable::CPDF_CrossRefTable(RetainPtr trailer) +- : trailer_(std::move(trailer)) {} ++CPDF_CrossRefTable::CPDF_CrossRefTable(RetainPtr trailer, ++ uint32_t trailer_object_number) ++ : trailer_(std::move(trailer)), ++ trailer_object_number_(trailer_object_number) {} + + CPDF_CrossRefTable::~CPDF_CrossRefTable() = default; + + void CPDF_CrossRefTable::AddCompressed(uint32_t obj_num, +- uint32_t archive_obj_num) { ++ uint32_t archive_obj_num, ++ uint32_t archive_obj_index) { + if (obj_num >= CPDF_Parser::kMaxObjectNumber || + archive_obj_num >= CPDF_Parser::kMaxObjectNumber) { + NOTREACHED(); +@@ -48,7 +51,8 @@ void CPDF_CrossRefTable::AddCompressed(uint32_t obj_num, + return; + + info.type = ObjectType::kCompressed; +- info.archive_obj_num = archive_obj_num; ++ info.archive.obj_num = archive_obj_num; ++ info.archive.obj_index = archive_obj_index; + info.gennum = 0; + + objects_info_[archive_obj_num].type = ObjectType::kObjStream; +@@ -88,8 +92,10 @@ void CPDF_CrossRefTable::SetFree(uint32_t obj_num) { + info.pos = 0; + } + +-void CPDF_CrossRefTable::SetTrailer(RetainPtr trailer) { ++void CPDF_CrossRefTable::SetTrailer(RetainPtr trailer, ++ uint32_t trailer_object_number) { + trailer_ = std::move(trailer); ++ trailer_object_number_ = trailer_object_number; + } + + const CPDF_CrossRefTable::ObjectInfo* CPDF_CrossRefTable::GetObjectInfo( +@@ -112,7 +118,7 @@ void CPDF_CrossRefTable::ShrinkObjectMap(uint32_t objnum) { + + objects_info_.erase(objects_info_.lower_bound(objnum), objects_info_.end()); + +- if (!pdfium::ContainsKey(objects_info_, objnum - 1)) ++ if (!pdfium::Contains(objects_info_, objnum - 1)) + objects_info_[objnum - 1].pos = 0; + } + +@@ -154,5 +160,5 @@ void CPDF_CrossRefTable::UpdateTrailer(RetainPtr new_trailer) { + new_trailer->SetFor("Prev", trailer_->RemoveFor("Prev")); + + for (const auto& key : new_trailer->GetKeys()) +- trailer_->SetFor(key, new_trailer->RemoveFor(key)); ++ trailer_->SetFor(key, new_trailer->RemoveFor(key.AsStringView())); + } +diff --git a/core/fpdfapi/parser/cpdf_cross_ref_table.h b/core/fpdfapi/parser/cpdf_cross_ref_table.h +index 66a51dea9..246e12974 100644 +--- a/core/fpdfapi/parser/cpdf_cross_ref_table.h ++++ b/core/fpdfapi/parser/cpdf_cross_ref_table.h +@@ -1,14 +1,16 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #ifndef CORE_FPDFAPI_PARSER_CPDF_CROSS_REF_TABLE_H_ + #define CORE_FPDFAPI_PARSER_CPDF_CROSS_REF_TABLE_H_ + ++#include ++ + #include + #include + +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/fx_types.h" + #include "core/fxcrt/retain_ptr.h" + + class CPDF_Dictionary; +@@ -25,16 +27,20 @@ class CPDF_CrossRefTable { + }; + + struct ObjectInfo { +- ObjectInfo() : pos(0), type(ObjectType::kFree), gennum(0) {} +- // if type is ObjectType::kCompressed the archive_obj_num should be used. +- // if type is ObjectType::kNotCompressed the pos should be used. +- // In other cases its are unused. ++ ObjectInfo() = default; ++ ++ // If `type` is `ObjectType::kCompressed`, `archive` should be used. ++ // If `type` is `ObjectType::kNotCompressed`, `pos` should be used. ++ // In other cases, it is unused. + union { +- FX_FILESIZE pos; +- uint32_t archive_obj_num; ++ FX_FILESIZE pos = 0; ++ struct { ++ uint32_t obj_num; ++ uint32_t obj_index; ++ } archive; + }; +- ObjectType type; +- uint16_t gennum; ++ ObjectType type = ObjectType::kFree; ++ uint16_t gennum = 0; + }; + + // Merge cross reference tables. Apply top on current. +@@ -43,14 +49,19 @@ class CPDF_CrossRefTable { + std::unique_ptr top); + + CPDF_CrossRefTable(); +- explicit CPDF_CrossRefTable(RetainPtr trailer); ++ CPDF_CrossRefTable(RetainPtr trailer, ++ uint32_t trailer_object_number); + ~CPDF_CrossRefTable(); + +- void AddCompressed(uint32_t obj_num, uint32_t archive_obj_num); ++ void AddCompressed(uint32_t obj_num, ++ uint32_t archive_obj_num, ++ uint32_t archive_obj_index); + void AddNormal(uint32_t obj_num, uint16_t gen_num, FX_FILESIZE pos); + void SetFree(uint32_t obj_num); + +- void SetTrailer(RetainPtr trailer); ++ void SetTrailer(RetainPtr trailer, ++ uint32_t trailer_object_number); ++ uint32_t trailer_object_number() const { return trailer_object_number_; } + const CPDF_Dictionary* trailer() const { return trailer_.Get(); } + CPDF_Dictionary* GetMutableTrailerForTesting() { return trailer_.Get(); } + +@@ -69,6 +80,10 @@ class CPDF_CrossRefTable { + void UpdateTrailer(RetainPtr new_trailer); + + RetainPtr trailer_; ++ // `trailer_` can be the dictionary part of a XRef stream object. Since it is ++ // inline, it has no object number. Store the stream's object number, or 0 if ++ // there is none. ++ uint32_t trailer_object_number_ = 0; + std::map objects_info_; + }; + +diff --git a/core/fpdfapi/parser/cpdf_crypto_handler.cpp b/core/fpdfapi/parser/cpdf_crypto_handler.cpp +index a19cf0a1f..5404207db 100644 +--- a/core/fpdfapi/parser/cpdf_crypto_handler.cpp ++++ b/core/fpdfapi/parser/cpdf_crypto_handler.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -23,6 +23,8 @@ + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" + #include "core/fpdfapi/parser/cpdf_string.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" + + namespace { + +@@ -36,64 +38,55 @@ bool CPDF_CryptoHandler::IsSignatureDictionary( + const CPDF_Dictionary* dictionary) { + if (!dictionary) + return false; +- const CPDF_Object* type_obj = dictionary->GetDirectObjectFor(kTypeKey); ++ RetainPtr type_obj = ++ dictionary->GetDirectObjectFor(kTypeKey); + if (!type_obj) + type_obj = dictionary->GetDirectObjectFor(pdfium::form_fields::kFT); + return type_obj && type_obj->GetString() == pdfium::form_fields::kSig; + } + +-void CPDF_CryptoHandler::CryptBlock(bool bEncrypt, +- uint32_t objnum, +- uint32_t gennum, +- pdfium::span source, +- uint8_t* dest_buf, +- uint32_t& dest_size) { +- if (m_Cipher == FXCIPHER_NONE) { ++void CPDF_CryptoHandler::EncryptContent(uint32_t objnum, ++ uint32_t gennum, ++ pdfium::span source, ++ uint8_t* dest_buf, ++ size_t& dest_size) const { ++ if (m_Cipher == Cipher::kNone) { + memcpy(dest_buf, source.data(), source.size()); + return; + } + uint8_t realkey[16]; + size_t realkeylen = sizeof(realkey); +- if (m_Cipher != FXCIPHER_AES || m_KeyLen != 32) { ++ if (m_Cipher != Cipher::kAES || m_KeyLen != 32) { + uint8_t key1[32]; + PopulateKey(objnum, gennum, key1); + +- if (m_Cipher == FXCIPHER_AES) ++ if (m_Cipher == Cipher::kAES) + memcpy(key1 + m_KeyLen + 5, "sAlT", 4); +- size_t len = m_Cipher == FXCIPHER_AES ? m_KeyLen + 9 : m_KeyLen + 5; ++ size_t len = m_Cipher == Cipher::kAES ? m_KeyLen + 9 : m_KeyLen + 5; + CRYPT_MD5Generate({key1, len}, realkey); + realkeylen = std::min(m_KeyLen + 5, sizeof(realkey)); + } +- if (m_Cipher == FXCIPHER_AES) { ++ if (m_Cipher == Cipher::kAES) { + CRYPT_AESSetKey(m_pAESContext.get(), +- m_KeyLen == 32 ? m_EncryptKey : realkey, m_KeyLen, +- bEncrypt); +- if (bEncrypt) { +- uint8_t iv[16]; +- for (int i = 0; i < 16; i++) { +- iv[i] = (uint8_t)rand(); +- } +- CRYPT_AESSetIV(m_pAESContext.get(), iv); +- memcpy(dest_buf, iv, 16); +- int nblocks = source.size() / 16; +- CRYPT_AESEncrypt(m_pAESContext.get(), dest_buf + 16, source.data(), +- nblocks * 16); +- uint8_t padding[16]; +- memcpy(padding, source.data() + nblocks * 16, source.size() % 16); +- memset(padding + source.size() % 16, 16 - source.size() % 16, +- 16 - source.size() % 16); +- CRYPT_AESEncrypt(m_pAESContext.get(), dest_buf + nblocks * 16 + 16, +- padding, 16); +- dest_size = 32 + nblocks * 16; +- } else { +- CRYPT_AESSetIV(m_pAESContext.get(), source.data()); +- CRYPT_AESDecrypt(m_pAESContext.get(), dest_buf, source.data() + 16, +- source.size() - 16); +- dest_size = source.size() - 16; +- dest_size -= dest_buf[dest_size - 1]; ++ m_KeyLen == 32 ? m_EncryptKey : realkey, m_KeyLen); ++ uint8_t iv[16]; ++ for (int i = 0; i < 16; i++) { ++ iv[i] = (uint8_t)rand(); + } ++ CRYPT_AESSetIV(m_pAESContext.get(), iv); ++ memcpy(dest_buf, iv, 16); ++ int nblocks = source.size() / 16; ++ CRYPT_AESEncrypt(m_pAESContext.get(), dest_buf + 16, source.data(), ++ nblocks * 16); ++ uint8_t padding[16]; ++ memcpy(padding, source.data() + nblocks * 16, source.size() % 16); ++ memset(padding + source.size() % 16, 16 - source.size() % 16, ++ 16 - source.size() % 16); ++ CRYPT_AESEncrypt(m_pAESContext.get(), dest_buf + nblocks * 16 + 16, padding, ++ 16); ++ dest_size = 32 + nblocks * 16; + } else { +- ASSERT(dest_size == source.size()); ++ DCHECK_EQ(dest_size, source.size()); + if (dest_buf != source.data()) + memcpy(dest_buf, source.data(), source.size()); + CRYPT_ArcFourCryptBlock({dest_buf, dest_size}, {realkey, realkeylen}); +@@ -107,47 +100,33 @@ struct AESCryptContext { + uint8_t m_Block[16]; + }; + +-void* CPDF_CryptoHandler::CryptStart(uint32_t objnum, +- uint32_t gennum, +- bool bEncrypt) { +- if (m_Cipher == FXCIPHER_NONE) { ++void* CPDF_CryptoHandler::DecryptStart(uint32_t objnum, uint32_t gennum) { ++ if (m_Cipher == Cipher::kNone) + return this; +- } +- if (m_Cipher == FXCIPHER_AES && m_KeyLen == 32) { ++ ++ if (m_Cipher == Cipher::kAES && m_KeyLen == 32) { + AESCryptContext* pContext = FX_Alloc(AESCryptContext, 1); + pContext->m_bIV = true; + pContext->m_BlockOffset = 0; +- CRYPT_AESSetKey(&pContext->m_Context, m_EncryptKey, 32, bEncrypt); +- if (bEncrypt) { +- for (int i = 0; i < 16; i++) { +- pContext->m_Block[i] = (uint8_t)rand(); +- } +- CRYPT_AESSetIV(&pContext->m_Context, pContext->m_Block); +- } ++ CRYPT_AESSetKey(&pContext->m_Context, m_EncryptKey, 32); + return pContext; + } + uint8_t key1[48]; + PopulateKey(objnum, gennum, key1); + +- if (m_Cipher == FXCIPHER_AES) ++ if (m_Cipher == Cipher::kAES) + memcpy(key1 + m_KeyLen + 5, "sAlT", 4); + + uint8_t realkey[16]; +- size_t len = m_Cipher == FXCIPHER_AES ? m_KeyLen + 9 : m_KeyLen + 5; ++ size_t len = m_Cipher == Cipher::kAES ? m_KeyLen + 9 : m_KeyLen + 5; + CRYPT_MD5Generate({key1, len}, realkey); + size_t realkeylen = std::min(m_KeyLen + 5, sizeof(realkey)); + +- if (m_Cipher == FXCIPHER_AES) { ++ if (m_Cipher == Cipher::kAES) { + AESCryptContext* pContext = FX_Alloc(AESCryptContext, 1); + pContext->m_bIV = true; + pContext->m_BlockOffset = 0; +- CRYPT_AESSetKey(&pContext->m_Context, realkey, 16, bEncrypt); +- if (bEncrypt) { +- for (int i = 0; i < 16; i++) { +- pContext->m_Block[i] = (uint8_t)rand(); +- } +- CRYPT_AESSetIV(&pContext->m_Context, pContext->m_Block); +- } ++ CRYPT_AESSetKey(&pContext->m_Context, realkey, 16); + return pContext; + } + CRYPT_rc4_context* pContext = FX_Alloc(CRYPT_rc4_context, 1); +@@ -155,32 +134,28 @@ void* CPDF_CryptoHandler::CryptStart(uint32_t objnum, + return pContext; + } + +-bool CPDF_CryptoHandler::CryptStream(void* context, +- pdfium::span source, +- CFX_BinaryBuf& dest_buf, +- bool bEncrypt) { ++bool CPDF_CryptoHandler::DecryptStream(void* context, ++ pdfium::span source, ++ BinaryBuffer& dest_buf) { + if (!context) + return false; + +- if (m_Cipher == FXCIPHER_NONE) { +- dest_buf.AppendBlock(source.data(), source.size()); ++ if (m_Cipher == Cipher::kNone) { ++ dest_buf.AppendSpan(source); + return true; + } +- if (m_Cipher == FXCIPHER_RC4) { +- int old_size = dest_buf.GetSize(); +- dest_buf.AppendBlock(source.data(), source.size()); +- CRYPT_ArcFourCrypt(static_cast(context), +- dest_buf.GetSpan().subspan(old_size, source.size())); ++ if (m_Cipher == Cipher::kRC4) { ++ size_t old_size = dest_buf.GetSize(); ++ dest_buf.AppendSpan(source); ++ CRYPT_ArcFourCrypt( ++ static_cast(context), ++ dest_buf.GetMutableSpan().subspan(old_size, source.size())); + return true; + } + AESCryptContext* pContext = static_cast(context); +- if (pContext->m_bIV && bEncrypt) { +- dest_buf.AppendBlock(pContext->m_Block, 16); +- pContext->m_bIV = false; +- } + uint32_t src_off = 0; + uint32_t src_left = source.size(); +- while (1) { ++ while (true) { + uint32_t copy_size = 16 - pContext->m_BlockOffset; + if (copy_size > src_left) { + copy_size = src_left; +@@ -191,20 +166,15 @@ bool CPDF_CryptoHandler::CryptStream(void* context, + src_left -= copy_size; + pContext->m_BlockOffset += copy_size; + if (pContext->m_BlockOffset == 16) { +- if (!bEncrypt && pContext->m_bIV) { ++ if (pContext->m_bIV) { + CRYPT_AESSetIV(&pContext->m_Context, pContext->m_Block); + pContext->m_bIV = false; + pContext->m_BlockOffset = 0; + } else if (src_off < source.size()) { + uint8_t block_buf[16]; +- if (bEncrypt) { +- CRYPT_AESEncrypt(&pContext->m_Context, block_buf, pContext->m_Block, +- 16); +- } else { +- CRYPT_AESDecrypt(&pContext->m_Context, block_buf, pContext->m_Block, +- 16); +- } +- dest_buf.AppendBlock(block_buf, 16); ++ CRYPT_AESDecrypt(&pContext->m_Context, block_buf, pContext->m_Block, ++ 16); ++ dest_buf.AppendSpan(block_buf); + pContext->m_BlockOffset = 0; + } + } +@@ -214,37 +184,25 @@ bool CPDF_CryptoHandler::CryptStream(void* context, + } + return true; + } +-bool CPDF_CryptoHandler::CryptFinish(void* context, +- CFX_BinaryBuf& dest_buf, +- bool bEncrypt) { +- if (!context) { ++ ++bool CPDF_CryptoHandler::DecryptFinish(void* context, BinaryBuffer& dest_buf) { ++ if (!context) + return false; +- } +- if (m_Cipher == FXCIPHER_NONE) { ++ ++ if (m_Cipher == Cipher::kNone) + return true; +- } +- if (m_Cipher == FXCIPHER_RC4) { ++ ++ if (m_Cipher == Cipher::kRC4) { + FX_Free(context); + return true; + } + auto* pContext = static_cast(context); +- if (bEncrypt) { +- uint8_t block_buf[16]; +- if (pContext->m_BlockOffset == 16) { +- CRYPT_AESEncrypt(&pContext->m_Context, block_buf, pContext->m_Block, 16); +- dest_buf.AppendBlock(block_buf, 16); +- pContext->m_BlockOffset = 0; +- } +- memset(pContext->m_Block + pContext->m_BlockOffset, +- (uint8_t)(16 - pContext->m_BlockOffset), +- 16 - pContext->m_BlockOffset); +- CRYPT_AESEncrypt(&pContext->m_Context, block_buf, pContext->m_Block, 16); +- dest_buf.AppendBlock(block_buf, 16); +- } else if (pContext->m_BlockOffset == 16) { ++ if (pContext->m_BlockOffset == 16) { + uint8_t block_buf[16]; + CRYPT_AESDecrypt(&pContext->m_Context, block_buf, pContext->m_Block, 16); +- if (block_buf[15] <= 16) { +- dest_buf.AppendBlock(block_buf, 16 - block_buf[15]); ++ if (block_buf[15] < 16) { ++ dest_buf.AppendSpan( ++ pdfium::make_span(block_buf).first(16 - block_buf[15])); + } + } + FX_Free(pContext); +@@ -254,22 +212,19 @@ bool CPDF_CryptoHandler::CryptFinish(void* context, + ByteString CPDF_CryptoHandler::Decrypt(uint32_t objnum, + uint32_t gennum, + const ByteString& str) { +- CFX_BinaryBuf dest_buf; ++ BinaryBuffer dest_buf; + void* context = DecryptStart(objnum, gennum); + DecryptStream(context, str.raw_span(), dest_buf); + DecryptFinish(context, dest_buf); +- return ByteString(dest_buf.GetBuffer(), dest_buf.GetSize()); ++ return ByteString(dest_buf.GetSpan()); + } + +-void* CPDF_CryptoHandler::DecryptStart(uint32_t objnum, uint32_t gennum) { +- return CryptStart(objnum, gennum, false); +-} +-uint32_t CPDF_CryptoHandler::DecryptGetSize(uint32_t src_size) { +- return m_Cipher == FXCIPHER_AES ? src_size - 16 : src_size; ++size_t CPDF_CryptoHandler::DecryptGetSize(size_t src_size) { ++ return m_Cipher == Cipher::kAES ? src_size - 16 : src_size; + } + + bool CPDF_CryptoHandler::IsCipherAES() const { +- return m_Cipher == FXCIPHER_AES; ++ return m_Cipher == Cipher::kAES; + } + + bool CPDF_CryptoHandler::DecryptObjectTree(RetainPtr object) { +@@ -278,19 +233,18 @@ bool CPDF_CryptoHandler::DecryptObjectTree(RetainPtr object) { + + struct MayBeSignature { + const CPDF_Dictionary* parent; +- CPDF_Object* contents; ++ RetainPtr contents; + }; + + std::stack may_be_sign_dictionaries; + const uint32_t obj_num = object->GetObjNum(); + const uint32_t gen_num = object->GetGenNum(); + +- CPDF_Object* object_to_decrypt = object.Get(); ++ RetainPtr object_to_decrypt = object; + while (object_to_decrypt) { +- CPDF_NonConstObjectWalker walker(object_to_decrypt); +- object_to_decrypt = nullptr; +- while (CPDF_Object* child = walker.GetNext()) { +- const CPDF_Dictionary* parent_dict = ++ CPDF_NonConstObjectWalker walker(std::move(object_to_decrypt)); ++ while (RetainPtr child = walker.GetNext()) { ++ RetainPtr parent_dict = + walker.GetParent() ? walker.GetParent()->GetDict() : nullptr; + if (walker.dictionary_key() == kContentsKey && + (parent_dict->KeyExist(kTypeKey) || +@@ -301,21 +255,22 @@ bool CPDF_CryptoHandler::DecryptObjectTree(RetainPtr object) { + // Temporary skip it, to prevent signature corruption. + // It will be decrypted on next interations, if this is not contents of + // signature dictionary. +- may_be_sign_dictionaries.push(MayBeSignature({parent_dict, child})); ++ may_be_sign_dictionaries.push({parent_dict.Get(), std::move(child)}); + walker.SkipWalkIntoCurrentObject(); + continue; + } + // Strings decryption. + if (child->IsString()) { + // TODO(art-snake): Move decryption into the CPDF_String class. +- CPDF_String* str = child->AsString(); ++ CPDF_String* str = child->AsMutableString(); + str->SetString(Decrypt(obj_num, gen_num, str->GetString())); + } + // Stream decryption. + if (child->IsStream()) { + // TODO(art-snake): Move decryption into the CPDF_Stream class. +- CPDF_Stream* stream = child->AsStream(); +- auto stream_access = pdfium::MakeRetain(stream); ++ CPDF_Stream* stream = child->AsMutableStream(); ++ auto stream_access = ++ pdfium::MakeRetain(pdfium::WrapRetain(stream)); + stream_access->LoadAllDataRaw(); + + if (IsCipherAES() && stream_access->GetSize() < 16) { +@@ -323,7 +278,7 @@ bool CPDF_CryptoHandler::DecryptObjectTree(RetainPtr object) { + continue; + } + +- CFX_BinaryBuf decrypted_buf; ++ BinaryBuffer decrypted_buf; + decrypted_buf.EstimateSize(DecryptGetSize(stream_access->GetSize())); + + void* context = DecryptStart(obj_num, gen_num); +@@ -331,8 +286,7 @@ bool CPDF_CryptoHandler::DecryptObjectTree(RetainPtr object) { + DecryptStream(context, stream_access->GetSpan(), decrypted_buf); + decrypt_result &= DecryptFinish(context, decrypted_buf); + if (decrypt_result) { +- const uint32_t decrypted_size = decrypted_buf.GetSize(); +- stream->TakeData(decrypted_buf.DetachBuffer(), decrypted_size); ++ stream->TakeData(decrypted_buf.DetachBuffer()); + } else { + // Decryption failed, set the stream to empty + stream->SetData({}); +@@ -353,43 +307,24 @@ bool CPDF_CryptoHandler::DecryptObjectTree(RetainPtr object) { + return true; + } + +-bool CPDF_CryptoHandler::DecryptStream(void* context, +- pdfium::span source, +- CFX_BinaryBuf& dest_buf) { +- return CryptStream(context, source, dest_buf, false); +-} +- +-bool CPDF_CryptoHandler::DecryptFinish(void* context, CFX_BinaryBuf& dest_buf) { +- return CryptFinish(context, dest_buf, false); +-} +- + size_t CPDF_CryptoHandler::EncryptGetSize( + pdfium::span source) const { +- return m_Cipher == FXCIPHER_AES ? source.size() + 32 : source.size(); +-} +- +-bool CPDF_CryptoHandler::EncryptContent(uint32_t objnum, +- uint32_t gennum, +- pdfium::span source, +- uint8_t* dest_buf, +- uint32_t& dest_size) { +- CryptBlock(true, objnum, gennum, source, dest_buf, dest_size); +- return true; ++ return m_Cipher == Cipher::kAES ? source.size() + 32 : source.size(); + } + +-CPDF_CryptoHandler::CPDF_CryptoHandler(int cipher, ++CPDF_CryptoHandler::CPDF_CryptoHandler(Cipher cipher, + const uint8_t* key, + size_t keylen) + : m_KeyLen(std::min(keylen, 32)), m_Cipher(cipher) { +- ASSERT(cipher != FXCIPHER_AES || keylen == 16 || keylen == 24 || ++ DCHECK(cipher != Cipher::kAES || keylen == 16 || keylen == 24 || + keylen == 32); +- ASSERT(cipher != FXCIPHER_AES2 || keylen == 32); +- ASSERT(cipher != FXCIPHER_RC4 || (keylen >= 5 && keylen <= 16)); ++ DCHECK(cipher != Cipher::kAES2 || keylen == 32); ++ DCHECK(cipher != Cipher::kRC4 || (keylen >= 5 && keylen <= 16)); + +- if (m_Cipher != FXCIPHER_NONE) ++ if (m_Cipher != Cipher::kNone) + memcpy(m_EncryptKey, key, m_KeyLen); + +- if (m_Cipher == FXCIPHER_AES) ++ if (m_Cipher == Cipher::kAES) + m_pAESContext.reset(FX_Alloc(CRYPT_aes_context, 1)); + } + +@@ -397,7 +332,7 @@ CPDF_CryptoHandler::~CPDF_CryptoHandler() = default; + + void CPDF_CryptoHandler::PopulateKey(uint32_t objnum, + uint32_t gennum, +- uint8_t* key) { ++ uint8_t* key) const { + memcpy(key, m_EncryptKey, m_KeyLen); + key[m_KeyLen + 0] = (uint8_t)objnum; + key[m_KeyLen + 1] = (uint8_t)(objnum >> 8); +diff --git a/core/fpdfapi/parser/cpdf_crypto_handler.h b/core/fpdfapi/parser/cpdf_crypto_handler.h +index edfba970f..b8f938c23 100644 +--- a/core/fpdfapi/parser/cpdf_crypto_handler.h ++++ b/core/fpdfapi/parser/cpdf_crypto_handler.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,63 +7,59 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_CRYPTO_HANDLER_H_ + #define CORE_FPDFAPI_PARSER_CPDF_CRYPTO_HANDLER_H_ + ++#include ++#include ++ + #include + + #include "core/fdrm/fx_crypt.h" +-#include "core/fxcrt/cfx_binarybuf.h" ++#include "core/fxcrt/binary_buffer.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/retain_ptr.h" + #include "third_party/base/span.h" + + class CPDF_Dictionary; + class CPDF_Object; +-class CPDF_SecurityHandler; + + class CPDF_CryptoHandler { + public: +- CPDF_CryptoHandler(int cipher, const uint8_t* key, size_t keylen); +- ~CPDF_CryptoHandler(); ++ enum class Cipher { ++ kNone = 0, ++ kRC4 = 1, ++ kAES = 2, ++ kAES2 = 3, ++ }; + + static bool IsSignatureDictionary(const CPDF_Dictionary* dictionary); + ++ CPDF_CryptoHandler(Cipher cipher, const uint8_t* key, size_t keylen); ++ ~CPDF_CryptoHandler(); ++ + bool DecryptObjectTree(RetainPtr object); + size_t EncryptGetSize(pdfium::span source) const; +- bool EncryptContent(uint32_t objnum, ++ void EncryptContent(uint32_t objnum, + uint32_t gennum, + pdfium::span source, + uint8_t* dest_buf, +- uint32_t& dest_size); ++ size_t& dest_size) const; + + bool IsCipherAES() const; + + private: +- uint32_t DecryptGetSize(uint32_t src_size); ++ size_t DecryptGetSize(size_t src_size); + void* DecryptStart(uint32_t objnum, uint32_t gennum); + ByteString Decrypt(uint32_t objnum, uint32_t gennum, const ByteString& str); + bool DecryptStream(void* context, + pdfium::span source, +- CFX_BinaryBuf& dest_buf); +- bool DecryptFinish(void* context, CFX_BinaryBuf& dest_buf); +- +- void PopulateKey(uint32_t objnum, uint32_t gennum, uint8_t* key); +- void CryptBlock(bool bEncrypt, +- uint32_t objnum, +- uint32_t gennum, +- pdfium::span source, +- uint8_t* dest_buf, +- uint32_t& dest_size); +- void* CryptStart(uint32_t objnum, uint32_t gennum, bool bEncrypt); +- bool CryptStream(void* context, +- pdfium::span source, +- CFX_BinaryBuf& dest_buf, +- bool bEncrypt); +- bool CryptFinish(void* context, CFX_BinaryBuf& dest_buf, bool bEncrypt); ++ BinaryBuffer& dest_buf); ++ bool DecryptFinish(void* context, BinaryBuffer& dest_buf); ++ void PopulateKey(uint32_t objnum, uint32_t gennum, uint8_t* key) const; + + const size_t m_KeyLen; +- const int m_Cipher; ++ const Cipher m_Cipher; + std::unique_ptr m_pAESContext; +- uint8_t m_EncryptKey[32]; ++ uint8_t m_EncryptKey[32] = {}; + }; + + #endif // CORE_FPDFAPI_PARSER_CPDF_CRYPTO_HANDLER_H_ +diff --git a/core/fpdfapi/parser/cpdf_data_avail.cpp b/core/fpdfapi/parser/cpdf_data_avail.cpp +index 457ae61c4..7db348a8d 100644 +--- a/core/fpdfapi/parser/cpdf_data_avail.cpp ++++ b/core/fpdfapi/parser/cpdf_data_avail.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -24,32 +24,31 @@ + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_syntax_parser.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" ++#include "core/fxcrt/autorestorer.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/compiler_specific.h" ++#include "core/fxcrt/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/notreached.h" + #include "third_party/base/numerics/safe_conversions.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" + + namespace { + +-// static +-CPDF_Object* GetResourceObject(CPDF_Dictionary* pDict) { ++RetainPtr GetResourceObject(RetainPtr pDict) { + constexpr size_t kMaxHierarchyDepth = 64; + size_t depth = 0; + +- CPDF_Dictionary* dictionary_to_check = pDict; +- while (dictionary_to_check) { +- CPDF_Object* result = dictionary_to_check->GetObjectFor("Resources"); ++ while (pDict) { ++ RetainPtr result = pDict->GetMutableObjectFor("Resources"); + if (result) + return result; +- CPDF_Object* parent = dictionary_to_check->GetObjectFor("Parent"); +- dictionary_to_check = parent ? parent->GetDict() : nullptr; +- + if (++depth > kMaxHierarchyDepth) { + // We have cycle in parents hierarchy. + return nullptr; + } ++ RetainPtr parent = pDict->GetMutableObjectFor("Parent"); ++ pDict = parent ? parent->GetMutableDict() : nullptr; + } + return nullptr; + } +@@ -59,7 +58,7 @@ class HintsScope { + HintsScope(RetainPtr validator, + CPDF_DataAvail::DownloadHints* hints) + : validator_(std::move(validator)) { +- ASSERT(validator_); ++ DCHECK(validator_); + validator_->SetDownloadHints(hints); + } + +@@ -71,18 +70,15 @@ class HintsScope { + + } // namespace + +-CPDF_DataAvail::FileAvail::~FileAvail() {} ++CPDF_DataAvail::FileAvail::~FileAvail() = default; + +-CPDF_DataAvail::DownloadHints::~DownloadHints() {} ++CPDF_DataAvail::DownloadHints::~DownloadHints() = default; + +-CPDF_DataAvail::CPDF_DataAvail( +- FileAvail* pFileAvail, +- const RetainPtr& pFileRead, +- bool bSupportHintTable) +- : m_pFileRead( +- pdfium::MakeRetain(pFileRead, pFileAvail)), +- m_dwFileLen(m_pFileRead->GetSize()), +- m_bSupportHintTable(bSupportHintTable) {} ++CPDF_DataAvail::CPDF_DataAvail(FileAvail* pFileAvail, ++ RetainPtr pFileRead) ++ : m_pFileRead(pdfium::MakeRetain(std::move(pFileRead), ++ pFileAvail)), ++ m_dwFileLen(m_pFileRead->GetSize()) {} + + CPDF_DataAvail::~CPDF_DataAvail() { + m_pHintTables.reset(); +@@ -101,47 +97,49 @@ void CPDF_DataAvail::OnObservableDestroyed() { + CPDF_DataAvail::DocAvailStatus CPDF_DataAvail::IsDocAvail( + DownloadHints* pHints) { + if (!m_dwFileLen) +- return DataError; ++ return kDataError; + ++ DCHECK(m_SeenPageObjList.empty()); ++ AutoRestorer> seen_objects_restorer(&m_SeenPageObjList); + const HintsScope hints_scope(GetValidator(), pHints); + while (!m_bDocAvail) { + if (!CheckDocStatus()) +- return DataNotAvailable; ++ return kDataNotAvailable; + } + +- return DataAvailable; ++ return kDataAvailable; + } + + bool CPDF_DataAvail::CheckDocStatus() { +- switch (m_docStatus) { +- case PDF_DATAAVAIL_HEADER: ++ switch (m_internalStatus) { ++ case InternalStatus::kHeader: + return CheckHeader(); +- case PDF_DATAAVAIL_FIRSTPAGE: ++ case InternalStatus::kFirstPage: + return CheckFirstPage(); +- case PDF_DATAAVAIL_HINTTABLE: ++ case InternalStatus::kHintTable: + return CheckHintTables(); +- case PDF_DATAAVAIL_LOADALLCROSSREF: ++ case InternalStatus::kLoadAllCrossRef: + return CheckAndLoadAllXref(); +- case PDF_DATAAVAIL_LOADALLFILE: ++ case InternalStatus::kLoadAllFile: + return LoadAllFile(); +- case PDF_DATAAVAIL_ROOT: ++ case InternalStatus::kRoot: + return CheckRoot(); +- case PDF_DATAAVAIL_INFO: ++ case InternalStatus::kInfo: + return CheckInfo(); +- case PDF_DATAAVAIL_PAGETREE: ++ case InternalStatus::kPageTree: + if (m_bTotalLoadPageTree) + return CheckPages(); + return LoadDocPages(); +- case PDF_DATAAVAIL_PAGE: ++ case InternalStatus::kPage: + if (m_bTotalLoadPageTree) + return CheckPage(); +- m_docStatus = PDF_DATAAVAIL_PAGE_LATERLOAD; ++ m_internalStatus = InternalStatus::kPageLaterLoad; + return true; +- case PDF_DATAAVAIL_ERROR: ++ case InternalStatus::kError: + return LoadAllFile(); +- case PDF_DATAAVAIL_PAGE_LATERLOAD: +- m_docStatus = PDF_DATAAVAIL_PAGE; +- FALLTHROUGH; ++ case InternalStatus::kPageLaterLoad: ++ m_internalStatus = InternalStatus::kPage; ++ [[fallthrough]]; + default: + m_bDocAvail = true; + return true; +@@ -149,12 +147,12 @@ bool CPDF_DataAvail::CheckDocStatus() { + } + + bool CPDF_DataAvail::CheckPageStatus() { +- switch (m_docStatus) { +- case PDF_DATAAVAIL_PAGETREE: ++ switch (m_internalStatus) { ++ case InternalStatus::kPageTree: + return CheckPages(); +- case PDF_DATAAVAIL_PAGE: ++ case InternalStatus::kPage: + return CheckPage(); +- case PDF_DATAAVAIL_ERROR: ++ case InternalStatus::kError: + return LoadAllFile(); + default: + m_bPagesTreeLoad = true; +@@ -165,7 +163,7 @@ bool CPDF_DataAvail::CheckPageStatus() { + + bool CPDF_DataAvail::LoadAllFile() { + if (GetValidator()->CheckWholeFileAndRequestIfUnavailable()) { +- m_docStatus = PDF_DATAAVAIL_DONE; ++ m_internalStatus = InternalStatus::kDone; + return true; + } + return false; +@@ -173,27 +171,27 @@ bool CPDF_DataAvail::LoadAllFile() { + + bool CPDF_DataAvail::CheckAndLoadAllXref() { + if (!m_pCrossRefAvail) { +- const CPDF_ReadValidator::Session read_session(GetValidator()); ++ CPDF_ReadValidator::ScopedSession read_session(GetValidator()); + const FX_FILESIZE last_xref_offset = m_parser.ParseStartXRef(); + if (GetValidator()->has_read_problems()) + return false; + + if (last_xref_offset <= 0) { +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return false; + } + +- m_pCrossRefAvail = pdfium::MakeUnique(GetSyntaxParser(), +- last_xref_offset); ++ m_pCrossRefAvail = std::make_unique(GetSyntaxParser(), ++ last_xref_offset); + } + + switch (m_pCrossRefAvail->CheckAvail()) { +- case DocAvailStatus::DataAvailable: ++ case kDataAvailable: + break; +- case DocAvailStatus::DataNotAvailable: ++ case kDataNotAvailable: + return false; +- case DocAvailStatus::DataError: +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ case kDataError: ++ m_internalStatus = InternalStatus::kError; + return false; + default: + NOTREACHED(); +@@ -202,33 +200,29 @@ bool CPDF_DataAvail::CheckAndLoadAllXref() { + + if (!m_parser.LoadAllCrossRefV4(m_pCrossRefAvail->last_crossref_offset()) && + !m_parser.LoadAllCrossRefV5(m_pCrossRefAvail->last_crossref_offset())) { +- m_docStatus = PDF_DATAAVAIL_LOADALLFILE; ++ m_internalStatus = InternalStatus::kLoadAllFile; + return false; + } + +- m_docStatus = PDF_DATAAVAIL_ROOT; ++ m_internalStatus = InternalStatus::kRoot; + return true; + } + + RetainPtr CPDF_DataAvail::GetObject(uint32_t objnum, + bool* pExistInFile) { +- CPDF_Parser* pParser = nullptr; +- +- if (pExistInFile) +- *pExistInFile = true; +- +- pParser = m_pDocument ? m_pDocument->GetParser() : &m_parser; ++ *pExistInFile = false; ++ CPDF_Parser* pParser = m_pDocument ? m_pDocument->GetParser() : &m_parser; ++ if (!pParser) ++ return nullptr; + +- RetainPtr pRet; +- if (pParser) { +- const CPDF_ReadValidator::Session read_session(GetValidator()); +- pRet = pParser->ParseIndirectObject(objnum); +- if (GetValidator()->has_read_problems()) +- return nullptr; +- } ++ CPDF_ReadValidator::ScopedSession read_session(GetValidator()); ++ RetainPtr pRet = pParser->ParseIndirectObject(objnum); ++ if (!pRet) ++ return nullptr; + +- if (!pRet && pExistInFile) +- *pExistInFile = false; ++ *pExistInFile = true; ++ if (GetValidator()->has_read_problems()) ++ return nullptr; + + return pRet; + } +@@ -236,54 +230,64 @@ RetainPtr CPDF_DataAvail::GetObject(uint32_t objnum, + bool CPDF_DataAvail::CheckInfo() { + const uint32_t dwInfoObjNum = m_parser.GetInfoObjNum(); + if (dwInfoObjNum == CPDF_Object::kInvalidObjNum) { +- m_docStatus = PDF_DATAAVAIL_PAGETREE; ++ m_internalStatus = InternalStatus::kPageTree; + return true; + } + +- const CPDF_ReadValidator::Session read_session(GetValidator()); ++ CPDF_ReadValidator::ScopedSession read_session(GetValidator()); + m_parser.ParseIndirectObject(dwInfoObjNum); + if (GetValidator()->has_read_problems()) + return false; + +- m_docStatus = PDF_DATAAVAIL_PAGETREE; ++ m_internalStatus = InternalStatus::kPageTree; + return true; + } + + bool CPDF_DataAvail::CheckRoot() { + const uint32_t dwRootObjNum = m_parser.GetRootObjNum(); + if (dwRootObjNum == CPDF_Object::kInvalidObjNum) { +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return true; + } + +- const CPDF_ReadValidator::Session read_session(GetValidator()); ++ CPDF_ReadValidator::ScopedSession read_session(GetValidator()); + m_pRoot = ToDictionary(m_parser.ParseIndirectObject(dwRootObjNum)); + if (GetValidator()->has_read_problems()) + return false; + +- const CPDF_Reference* pRef = +- ToReference(m_pRoot ? m_pRoot->GetObjectFor("Pages") : nullptr); ++ if (!m_pRoot) { ++ m_internalStatus = InternalStatus::kError; ++ return false; ++ } ++ ++ RetainPtr pRef = ++ ToReference(m_pRoot->GetObjectFor("Pages")); + if (!pRef) { +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return false; + } + + m_PagesObjNum = pRef->GetRefObjNum(); +- m_docStatus = PDF_DATAAVAIL_INFO; ++ m_internalStatus = InternalStatus::kInfo; + return true; + } + + bool CPDF_DataAvail::PreparePageItem() { + const CPDF_Dictionary* pRoot = m_pDocument->GetRoot(); +- const CPDF_Reference* pRef = +- ToReference(pRoot ? pRoot->GetObjectFor("Pages") : nullptr); ++ if (!pRoot) { ++ m_internalStatus = InternalStatus::kError; ++ return false; ++ } ++ ++ RetainPtr pRef = ++ ToReference(pRoot->GetObjectFor("Pages")); + if (!pRef) { +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return false; + } + + m_PagesObjNum = pRef->GetRefObjNum(); +- m_docStatus = PDF_DATAAVAIL_PAGETREE; ++ m_internalStatus = InternalStatus::kPageTree; + return true; + } + +@@ -305,21 +309,23 @@ bool CPDF_DataAvail::CheckPage() { + UnavailObjList.push_back(dwPageObjNum); + continue; + } +- CPDF_Array* pArray = ToArray(pObj.Get()); +- if (pArray) { +- CPDF_ArrayLocker locker(pArray); +- for (const auto& pArrayObj : locker) { +- if (CPDF_Reference* pRef = ToReference(pArrayObj.Get())) +- UnavailObjList.push_back(pRef->GetRefObjNum()); +- } +- } +- if (!pObj->IsDictionary()) +- continue; + +- ByteString type = pObj->GetDict()->GetStringFor("Type"); +- if (type == "Pages") { +- m_PagesArray.push_back(std::move(pObj)); +- continue; ++ switch (pObj->GetType()) { ++ case CPDF_Object::kArray: { ++ CPDF_ArrayLocker locker(pObj->AsArray()); ++ for (const auto& pArrayObj : locker) { ++ const CPDF_Reference* pRef = ToReference(pArrayObj.Get()); ++ if (pRef) ++ UnavailObjList.push_back(pRef->GetRefObjNum()); ++ } ++ break; ++ } ++ case CPDF_Object::kDictionary: ++ if (pObj->GetDict()->GetNameFor("Type") == "Pages") ++ m_PagesArray.push_back(std::move(pObj)); ++ break; ++ default: ++ break; + } + } + m_PageObjList.clear(); +@@ -332,39 +338,50 @@ bool CPDF_DataAvail::CheckPage() { + RetainPtr pPages = std::move(m_PagesArray[i]); + if (pPages && !GetPageKids(pPages.Get())) { + m_PagesArray.clear(); +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return false; + } + } + m_PagesArray.clear(); + if (m_PageObjList.empty()) +- m_docStatus = PDF_DATAAVAIL_DONE; ++ m_internalStatus = InternalStatus::kDone; + + return true; + } + + bool CPDF_DataAvail::GetPageKids(CPDF_Object* pPages) { +- CPDF_Dictionary* pDict = pPages->GetDict(); +- CPDF_Object* pKids = pDict ? pDict->GetObjectFor("Kids") : nullptr; ++ RetainPtr pDict = pPages->GetDict(); ++ if (!pDict) ++ return true; ++ ++ RetainPtr pKids = pDict->GetObjectFor("Kids"); + if (!pKids) + return true; + ++ std::vector object_numbers; + switch (pKids->GetType()) { + case CPDF_Object::kReference: +- m_PageObjList.push_back(pKids->AsReference()->GetRefObjNum()); ++ object_numbers.push_back(pKids->AsReference()->GetRefObjNum()); + break; + case CPDF_Object::kArray: { +- CPDF_Array* pKidsArray = pKids->AsArray(); +- for (size_t i = 0; i < pKidsArray->size(); ++i) { +- if (CPDF_Reference* pRef = ToReference(pKidsArray->GetObjectAt(i))) +- m_PageObjList.push_back(pRef->GetRefObjNum()); ++ CPDF_ArrayLocker locker(pKids->AsArray()); ++ for (const auto& pArrayObj : locker) { ++ const CPDF_Reference* pRef = ToReference(pArrayObj.Get()); ++ if (pRef) ++ object_numbers.push_back(pRef->GetRefObjNum()); + } + break; + } + default: +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return false; + } ++ ++ for (uint32_t num : object_numbers) { ++ bool inserted = m_SeenPageObjList.insert(num).second; ++ if (inserted) ++ m_PageObjList.push_back(num); ++ } + return true; + } + +@@ -372,37 +389,37 @@ bool CPDF_DataAvail::CheckPages() { + bool bExists = false; + RetainPtr pPages = GetObject(m_PagesObjNum, &bExists); + if (!bExists) { +- m_docStatus = PDF_DATAAVAIL_LOADALLFILE; ++ m_internalStatus = InternalStatus::kLoadAllFile; + return true; + } + + if (!pPages) { +- if (m_docStatus == PDF_DATAAVAIL_ERROR) { +- m_docStatus = PDF_DATAAVAIL_LOADALLFILE; ++ if (m_internalStatus == InternalStatus::kError) { ++ m_internalStatus = InternalStatus::kLoadAllFile; + return true; + } + return false; + } + + if (!GetPageKids(pPages.Get())) { +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return false; + } + +- m_docStatus = PDF_DATAAVAIL_PAGE; ++ m_internalStatus = InternalStatus::kPage; + return true; + } + + bool CPDF_DataAvail::CheckHeader() { + switch (CheckHeaderAndLinearized()) { +- case DocAvailStatus::DataAvailable: +- m_docStatus = m_pLinearized ? PDF_DATAAVAIL_FIRSTPAGE +- : PDF_DATAAVAIL_LOADALLCROSSREF; ++ case kDataAvailable: ++ m_internalStatus = m_pLinearized ? InternalStatus::kFirstPage ++ : InternalStatus::kLoadAllCrossRef; + return true; +- case DocAvailStatus::DataNotAvailable: ++ case kDataNotAvailable: + return false; +- case DocAvailStatus::DataError: +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ case kDataError: ++ m_internalStatus = InternalStatus::kError; + return true; + default: + NOTREACHED(); +@@ -414,7 +431,7 @@ bool CPDF_DataAvail::CheckFirstPage() { + if (!m_pLinearized->GetFirstPageEndOffset() || + !m_pLinearized->GetFileSize() || + !m_pLinearized->GetMainXRefTableFirstEntryOffset()) { +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return false; + } + +@@ -429,24 +446,23 @@ bool CPDF_DataAvail::CheckFirstPage() { + data_size)) + return false; + +- m_docStatus = +- m_bSupportHintTable ? PDF_DATAAVAIL_HINTTABLE : PDF_DATAAVAIL_DONE; ++ m_internalStatus = InternalStatus::kHintTable; + return true; + } + + bool CPDF_DataAvail::CheckHintTables() { +- const CPDF_ReadValidator::Session read_session(GetValidator()); ++ CPDF_ReadValidator::ScopedSession read_session(GetValidator()); + m_pHintTables = + CPDF_HintTables::Parse(GetSyntaxParser(), m_pLinearized.get()); + + if (GetValidator()->read_error()) { +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return true; + } + if (GetValidator()->has_unavailable_data()) + return false; + +- m_docStatus = PDF_DATAAVAIL_DONE; ++ m_internalStatus = InternalStatus::kDone; + return true; + } + +@@ -466,59 +482,59 @@ RetainPtr CPDF_DataAvail::ParseIndirectObjectAt( + + CPDF_DataAvail::DocLinearizationStatus CPDF_DataAvail::IsLinearizedPDF() { + switch (CheckHeaderAndLinearized()) { +- case DocAvailStatus::DataAvailable: +- return m_pLinearized ? DocLinearizationStatus::Linearized +- : DocLinearizationStatus::NotLinearized; +- case DocAvailStatus::DataNotAvailable: +- return DocLinearizationStatus::LinearizationUnknown; +- case DocAvailStatus::DataError: +- return DocLinearizationStatus::NotLinearized; ++ case kDataAvailable: ++ return m_pLinearized ? kLinearized : kNotLinearized; ++ case kDataNotAvailable: ++ return kLinearizationUnknown; ++ case kDataError: ++ return kNotLinearized; + default: + NOTREACHED(); +- return DocLinearizationStatus::LinearizationUnknown; ++ return kLinearizationUnknown; + } + } + + CPDF_DataAvail::DocAvailStatus CPDF_DataAvail::CheckHeaderAndLinearized() { + if (m_bHeaderAvail) +- return DocAvailStatus::DataAvailable; ++ return kDataAvailable; + +- const CPDF_ReadValidator::Session read_session(GetValidator()); +- const Optional header_offset = GetHeaderOffset(GetValidator()); ++ CPDF_ReadValidator::ScopedSession read_session(GetValidator()); ++ const absl::optional header_offset = ++ GetHeaderOffset(GetValidator()); + if (GetValidator()->has_read_problems()) +- return DocAvailStatus::DataNotAvailable; ++ return kDataNotAvailable; + +- if (!header_offset) +- return DocAvailStatus::DataError; ++ if (!header_offset.has_value()) ++ return kDataError; + +- m_parser.m_pSyntax = +- pdfium::MakeUnique(GetValidator(), *header_offset); ++ m_parser.m_pSyntax = std::make_unique( ++ GetValidator(), header_offset.value()); + m_pLinearized = m_parser.ParseLinearizedHeader(); + if (GetValidator()->has_read_problems()) +- return DocAvailStatus::DataNotAvailable; ++ return kDataNotAvailable; + + m_bHeaderAvail = true; +- return DocAvailStatus::DataAvailable; ++ return kDataAvailable; + } + + bool CPDF_DataAvail::CheckPage(uint32_t dwPage) { + while (true) { +- switch (m_docStatus) { +- case PDF_DATAAVAIL_PAGETREE: ++ switch (m_internalStatus) { ++ case InternalStatus::kPageTree: + if (!LoadDocPages()) + return false; + break; +- case PDF_DATAAVAIL_PAGE: ++ case InternalStatus::kPage: + if (!LoadDocPage(dwPage)) + return false; + break; +- case PDF_DATAAVAIL_ERROR: ++ case InternalStatus::kError: + return LoadAllFile(); + default: + m_bPagesTreeLoad = true; + m_bPagesLoad = true; + m_bCurPageDictLoadOK = true; +- m_docStatus = PDF_DATAAVAIL_PAGE; ++ m_internalStatus = InternalStatus::kPage; + return true; + } + } +@@ -529,26 +545,26 @@ bool CPDF_DataAvail::CheckArrayPageNode(uint32_t dwPageNo, + bool bExists = false; + RetainPtr pPages = GetObject(dwPageNo, &bExists); + if (!bExists) { +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return false; + } + + if (!pPages) + return false; + +- CPDF_Array* pArray = pPages->AsArray(); ++ const CPDF_Array* pArray = pPages->AsArray(); + if (!pArray) { +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return false; + } + +- pPageNode->m_type = PDF_PAGENODE_PAGES; ++ pPageNode->m_type = PageNode::Type::kPages; + for (size_t i = 0; i < pArray->size(); ++i) { +- CPDF_Reference* pKid = ToReference(pArray->GetObjectAt(i)); ++ RetainPtr pKid = ToReference(pArray->GetObjectAt(i)); + if (!pKid) + continue; + +- auto pNode = pdfium::MakeUnique(); ++ auto pNode = std::make_unique(); + pNode->m_dwPageNo = pKid->GetRefObjNum(); + pPageNode->m_ChildNodes.push_back(std::move(pNode)); + } +@@ -560,7 +576,7 @@ bool CPDF_DataAvail::CheckUnknownPageNode(uint32_t dwPageNo, + bool bExists = false; + RetainPtr pPage = GetObject(dwPageNo, &bExists); + if (!bExists) { +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return false; + } + +@@ -569,51 +585,52 @@ bool CPDF_DataAvail::CheckUnknownPageNode(uint32_t dwPageNo, + + if (pPage->IsArray()) { + pPageNode->m_dwPageNo = dwPageNo; +- pPageNode->m_type = PDF_PAGENODE_ARRAY; ++ pPageNode->m_type = PageNode::Type::kArray; + return true; + } + + if (!pPage->IsDictionary()) { +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return false; + } + + pPageNode->m_dwPageNo = dwPageNo; +- CPDF_Dictionary* pDict = pPage->GetDict(); +- const ByteString type = pDict->GetStringFor("Type"); ++ RetainPtr pDict = pPage->GetMutableDict(); ++ const ByteString type = pDict->GetNameFor("Type"); + if (type == "Page") { +- pPageNode->m_type = PDF_PAGENODE_PAGE; ++ pPageNode->m_type = PageNode::Type::kPage; + return true; + } + + if (type != "Pages") { +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return false; + } + +- pPageNode->m_type = PDF_PAGENODE_PAGES; +- CPDF_Object* pKids = pDict->GetObjectFor("Kids"); ++ pPageNode->m_type = PageNode::Type::kPages; ++ RetainPtr pKids = pDict->GetMutableObjectFor("Kids"); + if (!pKids) { +- m_docStatus = PDF_DATAAVAIL_PAGE; ++ m_internalStatus = InternalStatus::kPage; + return true; + } + + switch (pKids->GetType()) { + case CPDF_Object::kReference: { +- CPDF_Reference* pKid = pKids->AsReference(); +- auto pNode = pdfium::MakeUnique(); ++ const CPDF_Reference* pKid = pKids->AsReference(); ++ auto pNode = std::make_unique(); + pNode->m_dwPageNo = pKid->GetRefObjNum(); + pPageNode->m_ChildNodes.push_back(std::move(pNode)); + break; + } + case CPDF_Object::kArray: { +- CPDF_Array* pKidsArray = pKids->AsArray(); ++ const CPDF_Array* pKidsArray = pKids->AsArray(); + for (size_t i = 0; i < pKidsArray->size(); ++i) { +- CPDF_Reference* pKid = ToReference(pKidsArray->GetObjectAt(i)); ++ RetainPtr pKid = ++ ToReference(pKidsArray->GetObjectAt(i)); + if (!pKid) + continue; + +- auto pNode = pdfium::MakeUnique(); ++ auto pNode = std::make_unique(); + pNode->m_dwPageNo = pKid->GetRefObjNum(); + pPageNode->m_ChildNodes.push_back(std::move(pNode)); + } +@@ -632,9 +649,9 @@ bool CPDF_DataAvail::CheckPageNode(const CPDF_DataAvail::PageNode& pageNode, + if (level >= kMaxPageRecursionDepth) + return false; + +- int32_t iSize = pdfium::CollectionSize(pageNode.m_ChildNodes); ++ int32_t iSize = fxcrt::CollectionSize(pageNode.m_ChildNodes); + if (iSize <= 0 || iPage >= iSize) { +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return false; + } + for (int32_t i = 0; i < iSize; ++i) { +@@ -642,33 +659,33 @@ bool CPDF_DataAvail::CheckPageNode(const CPDF_DataAvail::PageNode& pageNode, + if (!pNode) + continue; + +- if (pNode->m_type == PDF_PAGENODE_UNKNOWN) { ++ if (pNode->m_type == PageNode::Type::kUnknown) { + // Updates the type for the unknown page node. + if (!CheckUnknownPageNode(pNode->m_dwPageNo, pNode)) + return false; + } +- if (pNode->m_type == PDF_PAGENODE_ARRAY) { ++ if (pNode->m_type == PageNode::Type::kArray) { + // Updates a more specific type for the array page node. + if (!CheckArrayPageNode(pNode->m_dwPageNo, pNode)) + return false; + } + switch (pNode->m_type) { +- case PDF_PAGENODE_PAGE: ++ case PageNode::Type::kPage: + iCount++; + if (iPage == iCount && m_pDocument) + m_pDocument->SetPageObjNum(iPage, pNode->m_dwPageNo); + break; +- case PDF_PAGENODE_PAGES: ++ case PageNode::Type::kPages: + if (!CheckPageNode(*pNode, iPage, iCount, level + 1)) + return false; + break; +- case PDF_PAGENODE_UNKNOWN: +- case PDF_PAGENODE_ARRAY: ++ case PageNode::Type::kUnknown: ++ case PageNode::Type::kArray: + // Already converted above, error if we get here. + return false; + } + if (iPage == iCount) { +- m_docStatus = PDF_DATAAVAIL_DONE; ++ m_internalStatus = InternalStatus::kDone; + return true; + } + } +@@ -676,15 +693,15 @@ bool CPDF_DataAvail::CheckPageNode(const CPDF_DataAvail::PageNode& pageNode, + } + + bool CPDF_DataAvail::LoadDocPage(uint32_t dwPage) { +- FX_SAFE_INT32 safePage = pdfium::base::checked_cast(dwPage); +- int32_t iPage = safePage.ValueOrDie(); ++ int iPage = pdfium::base::checked_cast(dwPage); + if (m_pDocument->GetPageCount() <= iPage || + m_pDocument->IsPageLoaded(iPage)) { +- m_docStatus = PDF_DATAAVAIL_DONE; ++ m_internalStatus = InternalStatus::kDone; + return true; + } +- if (m_PageNode.m_type == PDF_PAGENODE_PAGE) { +- m_docStatus = iPage == 0 ? PDF_DATAAVAIL_DONE : PDF_DATAAVAIL_ERROR; ++ if (m_PageNode.m_type == PageNode::Type::kPage) { ++ m_internalStatus = ++ iPage == 0 ? InternalStatus::kDone : InternalStatus::kError; + return true; + } + int32_t iCount = -1; +@@ -695,15 +712,15 @@ bool CPDF_DataAvail::CheckPageCount() { + bool bExists = false; + RetainPtr pPages = GetObject(m_PagesObjNum, &bExists); + if (!bExists) { +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return false; + } + if (!pPages) + return false; + +- CPDF_Dictionary* pPagesDict = pPages->GetDict(); ++ RetainPtr pPagesDict = pPages->GetDict(); + if (!pPagesDict) { +- m_docStatus = PDF_DATAAVAIL_ERROR; ++ m_internalStatus = InternalStatus::kError; + return false; + } + if (!pPagesDict->KeyExist("Kids")) +@@ -717,7 +734,7 @@ bool CPDF_DataAvail::LoadDocPages() { + return false; + + if (CheckPageCount()) { +- m_docStatus = PDF_DATAAVAIL_PAGE; ++ m_internalStatus = InternalStatus::kPage; + return true; + } + +@@ -740,11 +757,11 @@ bool CPDF_DataAvail::LoadPages() { + + CPDF_DataAvail::DocAvailStatus CPDF_DataAvail::CheckLinearizedData() { + if (m_bLinearedDataOK) +- return DataAvailable; +- ASSERT(m_pLinearized); ++ return kDataAvailable; ++ DCHECK(m_pLinearized); + if (!m_pLinearized->GetMainXRefTableFirstEntryOffset() || !m_pDocument || + !m_pDocument->GetParser() || !m_pDocument->GetParser()->GetTrailer()) { +- return DataError; ++ return kDataError; + } + + if (!m_bMainXRefLoadTried) { +@@ -752,68 +769,66 @@ CPDF_DataAvail::DocAvailStatus CPDF_DataAvail::CheckLinearizedData() { + m_pDocument->GetParser()->GetTrailer()->GetIntegerFor("Prev"); + const FX_FILESIZE main_xref_offset = prev.ValueOrDefault(-1); + if (main_xref_offset < 0) +- return DataError; ++ return kDataError; + + if (main_xref_offset == 0) +- return DataAvailable; ++ return kDataAvailable; + + FX_SAFE_SIZE_T data_size = m_dwFileLen; + data_size -= main_xref_offset; + if (!data_size.IsValid()) +- return DataError; ++ return kDataError; + + if (!GetValidator()->CheckDataRangeAndRequestIfUnavailable( + main_xref_offset, data_size.ValueOrDie())) +- return DataNotAvailable; ++ return kDataNotAvailable; + + CPDF_Parser::Error eRet = + m_pDocument->GetParser()->LoadLinearizedMainXRefTable(); + m_bMainXRefLoadTried = true; + if (eRet != CPDF_Parser::SUCCESS) +- return DataError; ++ return kDataError; + + if (!PreparePageItem()) +- return DataNotAvailable; ++ return kDataNotAvailable; + + m_bMainXRefLoadedOK = true; + m_bLinearedDataOK = true; + } + +- return m_bLinearedDataOK ? DataAvailable : DataNotAvailable; ++ return m_bLinearedDataOK ? kDataAvailable : kDataNotAvailable; + } + + CPDF_DataAvail::DocAvailStatus CPDF_DataAvail::IsPageAvail( + uint32_t dwPage, + DownloadHints* pHints) { + if (!m_pDocument) +- return DataError; ++ return kDataError; + +- const FX_SAFE_INT32 safePage = pdfium::base::checked_cast(dwPage); +- if (!safePage.IsValid()) +- return DataError; +- +- if (safePage.ValueOrDie() >= m_pDocument->GetPageCount()) { ++ const int iPage = pdfium::base::checked_cast(dwPage); ++ if (iPage >= m_pDocument->GetPageCount()) { + // This is XFA page. +- return DataAvailable; ++ return kDataAvailable; + } + + if (IsFirstCheck(dwPage)) { + m_bCurPageDictLoadOK = false; + } + +- if (pdfium::ContainsKey(m_pagesLoadState, dwPage)) +- return DataAvailable; ++ if (pdfium::Contains(m_pagesLoadState, dwPage)) ++ return kDataAvailable; + + const HintsScope hints_scope(GetValidator(), pHints); + if (m_pLinearized) { + if (dwPage == m_pLinearized->GetFirstPageNo()) { +- auto* pPageDict = m_pDocument->GetPageDictionary(safePage.ValueOrDie()); ++ RetainPtr pPageDict = ++ m_pDocument->GetPageDictionary(iPage); + if (!pPageDict) +- return DataError; ++ return kDataError; + +- auto page_num_obj = std::make_pair( +- dwPage, pdfium::MakeUnique( +- GetValidator(), m_pDocument.Get(), pPageDict)); ++ auto page_num_obj = ++ std::make_pair(dwPage, std::make_unique( ++ GetValidator(), m_pDocument, pPageDict)); + + CPDF_PageObjectAvail* page_obj_avail = + m_PagesObjAvail.insert(std::move(page_num_obj)).first->second.get(); +@@ -822,83 +837,84 @@ CPDF_DataAvail::DocAvailStatus CPDF_DataAvail::IsPageAvail( + } + + DocAvailStatus nResult = CheckLinearizedData(); +- if (nResult != DataAvailable) ++ if (nResult != kDataAvailable) + return nResult; + + if (m_pHintTables) { + nResult = m_pHintTables->CheckPage(dwPage); +- if (nResult != DataAvailable) ++ if (nResult != kDataAvailable) + return nResult; + if (GetPageDictionary(dwPage)) { + m_pagesLoadState.insert(dwPage); +- return DataAvailable; ++ return kDataAvailable; + } + } + + if (!m_bMainXRefLoadedOK) { + if (!LoadAllFile()) +- return DataNotAvailable; ++ return kDataNotAvailable; + m_pDocument->GetParser()->RebuildCrossRef(); + ResetFirstCheck(dwPage); +- return DataAvailable; ++ return kDataAvailable; + } + if (m_bTotalLoadPageTree) { + if (!LoadPages()) +- return DataNotAvailable; ++ return kDataNotAvailable; + } else { + if (!m_bCurPageDictLoadOK && !CheckPage(dwPage)) +- return DataNotAvailable; ++ return kDataNotAvailable; + } + } else { + if (!m_bTotalLoadPageTree && !m_bCurPageDictLoadOK && !CheckPage(dwPage)) { +- return DataNotAvailable; ++ return kDataNotAvailable; + } + } + +- if (CheckAcroForm() == DocFormStatus::FormNotAvailable) +- return DataNotAvailable; ++ if (CheckAcroForm() == kFormNotAvailable) ++ return kDataNotAvailable; + +- auto* pPageDict = m_pDocument->GetPageDictionary(safePage.ValueOrDie()); ++ RetainPtr pPageDict = ++ m_pDocument->GetMutablePageDictionary(iPage); + if (!pPageDict) +- return DataError; ++ return kDataError; + + { +- auto page_num_obj = std::make_pair( +- dwPage, pdfium::MakeUnique( +- GetValidator(), m_pDocument.Get(), pPageDict)); ++ auto page_num_obj = ++ std::make_pair(dwPage, std::make_unique( ++ GetValidator(), m_pDocument, pPageDict)); + CPDF_PageObjectAvail* page_obj_avail = + m_PagesObjAvail.insert(std::move(page_num_obj)).first->second.get(); + const DocAvailStatus status = page_obj_avail->CheckAvail(); +- if (status != DocAvailStatus::DataAvailable) ++ if (status != kDataAvailable) + return status; + } + +- const DocAvailStatus resources_status = CheckResources(pPageDict); +- if (resources_status != DocAvailStatus::DataAvailable) ++ const DocAvailStatus resources_status = CheckResources(std::move(pPageDict)); ++ if (resources_status != kDataAvailable) + return resources_status; + + m_bCurPageDictLoadOK = false; + ResetFirstCheck(dwPage); + m_pagesLoadState.insert(dwPage); +- return DataAvailable; ++ return kDataAvailable; + } + + CPDF_DataAvail::DocAvailStatus CPDF_DataAvail::CheckResources( +- CPDF_Dictionary* page) { +- ASSERT(page); +- const CPDF_ReadValidator::Session read_session(GetValidator()); +- CPDF_Object* resources = GetResourceObject(page); ++ RetainPtr page) { ++ DCHECK(page); ++ CPDF_ReadValidator::ScopedSession read_session(GetValidator()); ++ RetainPtr resources = GetResourceObject(std::move(page)); + if (GetValidator()->has_read_problems()) +- return DocAvailStatus::DataNotAvailable; ++ return kDataNotAvailable; + + if (!resources) +- return DocAvailStatus::DataAvailable; ++ return kDataAvailable; + + CPDF_PageObjectAvail* resource_avail = + m_PagesResourcesAvail +- .insert(std::make_pair( +- resources, pdfium::MakeUnique( +- GetValidator(), m_pDocument.Get(), resources))) ++ .insert(std::make_pair(resources, ++ std::make_unique( ++ GetValidator(), m_pDocument, resources))) + .first->second.get(); + return resource_avail->CheckAvail(); + } +@@ -918,10 +934,11 @@ int CPDF_DataAvail::GetPageCount() const { + return m_pDocument ? m_pDocument->GetPageCount() : 0; + } + +-CPDF_Dictionary* CPDF_DataAvail::GetPageDictionary(int index) const { ++RetainPtr CPDF_DataAvail::GetPageDictionary( ++ int index) const { + if (!m_pDocument || index < 0 || index >= GetPageCount()) + return nullptr; +- CPDF_Dictionary* page = m_pDocument->GetPageDictionary(index); ++ RetainPtr page = m_pDocument->GetPageDictionary(index); + if (page) + return page; + if (!m_pLinearized || !m_pHintTables) +@@ -941,8 +958,7 @@ CPDF_Dictionary* CPDF_DataAvail::GetPageDictionary(int index) const { + // Page object already can be parsed in document. + if (!m_pDocument->GetIndirectObject(dwObjNum)) { + m_pDocument->ReplaceIndirectObjectIfHigherGeneration( +- dwObjNum, +- ParseIndirectObjectAt(szPageStartPos, dwObjNum, m_pDocument.Get())); ++ dwObjNum, ParseIndirectObjectAt(szPageStartPos, dwObjNum, m_pDocument)); + } + if (!ValidatePage(index)) + return nullptr; +@@ -957,64 +973,67 @@ CPDF_DataAvail::DocFormStatus CPDF_DataAvail::IsFormAvail( + + CPDF_DataAvail::DocFormStatus CPDF_DataAvail::CheckAcroForm() { + if (!m_pDocument) +- return FormAvailable; ++ return kFormAvailable; + + if (m_pLinearized) { + DocAvailStatus nDocStatus = CheckLinearizedData(); +- if (nDocStatus == DataError) +- return FormError; +- if (nDocStatus == DataNotAvailable) +- return FormNotAvailable; ++ if (nDocStatus == kDataError) ++ return kFormError; ++ if (nDocStatus == kDataNotAvailable) ++ return kFormNotAvailable; + } + + if (!m_pFormAvail) { +- CPDF_Dictionary* pRoot = m_pDocument->GetRoot(); ++ const CPDF_Dictionary* pRoot = m_pDocument->GetRoot(); + if (!pRoot) +- return FormAvailable; ++ return kFormAvailable; + +- CPDF_Object* pAcroForm = pRoot->GetObjectFor("AcroForm"); ++ RetainPtr pAcroForm = pRoot->GetObjectFor("AcroForm"); + if (!pAcroForm) +- return FormNotExist; ++ return kFormNotExist; + +- m_pFormAvail = pdfium::MakeUnique( +- GetValidator(), m_pDocument.Get(), pAcroForm); ++ m_pFormAvail = std::make_unique( ++ GetValidator(), m_pDocument, std::move(pAcroForm)); + } + switch (m_pFormAvail->CheckAvail()) { +- case DocAvailStatus::DataError: +- return DocFormStatus::FormError; +- case DocAvailStatus::DataNotAvailable: +- return DocFormStatus::FormNotAvailable; +- case DocAvailStatus::DataAvailable: +- return DocFormStatus::FormAvailable; ++ case kDataError: ++ return kFormError; ++ case kDataNotAvailable: ++ return kFormNotAvailable; ++ case kDataAvailable: ++ return kFormAvailable; + default: + NOTREACHED(); + } +- return DocFormStatus::FormError; ++ return kFormError; + } + + bool CPDF_DataAvail::ValidatePage(uint32_t dwPage) const { +- FX_SAFE_INT32 safePage = pdfium::base::checked_cast(dwPage); +- auto* pPageDict = m_pDocument->GetPageDictionary(safePage.ValueOrDie()); ++ int iPage = pdfium::base::checked_cast(dwPage); ++ RetainPtr pPageDict = ++ m_pDocument->GetPageDictionary(iPage); + if (!pPageDict) + return false; +- CPDF_PageObjectAvail obj_avail(GetValidator(), m_pDocument.Get(), pPageDict); +- return obj_avail.CheckAvail() == DocAvailStatus::DataAvailable; ++ ++ CPDF_PageObjectAvail obj_avail(GetValidator(), m_pDocument, ++ std::move(pPageDict)); ++ return obj_avail.CheckAvail() == kDataAvailable; + } + + std::pair> + CPDF_DataAvail::ParseDocument( + std::unique_ptr pRenderData, + std::unique_ptr pPageData, +- const char* password) { ++ const ByteString& password) { + if (m_pDocument) { + // We already returned parsed document. + return std::make_pair(CPDF_Parser::HANDLER_ERROR, nullptr); + } +- auto document = pdfium::MakeUnique(std::move(pRenderData), +- std::move(pPageData)); ++ auto document = std::make_unique(std::move(pRenderData), ++ std::move(pPageData)); + document->AddObserver(this); + +- CPDF_ReadValidator::Session read_session(GetValidator()); ++ CPDF_ReadValidator::ScopedSession read_session(GetValidator()); + CPDF_Parser::Error error = + document->LoadLinearizedDoc(GetValidator(), password); + +@@ -1031,6 +1050,6 @@ CPDF_DataAvail::ParseDocument( + return std::make_pair(CPDF_Parser::SUCCESS, std::move(document)); + } + +-CPDF_DataAvail::PageNode::PageNode() : m_type(PDF_PAGENODE_UNKNOWN) {} ++CPDF_DataAvail::PageNode::PageNode() = default; + +-CPDF_DataAvail::PageNode::~PageNode() {} ++CPDF_DataAvail::PageNode::~PageNode() = default; +diff --git a/core/fpdfapi/parser/cpdf_data_avail.h b/core/fpdfapi/parser/cpdf_data_avail.h +index 6bcea0715..a3f215e2b 100644 +--- a/core/fpdfapi/parser/cpdf_data_avail.h ++++ b/core/fpdfapi/parser/cpdf_data_avail.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,7 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_DATA_AVAIL_H_ + #define CORE_FPDFAPI_PARSER_CPDF_DATA_AVAIL_H_ + ++#include + #include + #include + #include +@@ -15,6 +16,7 @@ + + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_parser.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + + class CPDF_CrossRefAvail; +@@ -26,57 +28,34 @@ class CPDF_PageObjectAvail; + class CPDF_ReadValidator; + class CPDF_SyntaxParser; + +-enum PDF_DATAAVAIL_STATUS { +- PDF_DATAAVAIL_HEADER = 0, +- PDF_DATAAVAIL_FIRSTPAGE, +- PDF_DATAAVAIL_HINTTABLE, +- PDF_DATAAVAIL_LOADALLCROSSREF, +- PDF_DATAAVAIL_ROOT, +- PDF_DATAAVAIL_INFO, +- PDF_DATAAVAIL_PAGETREE, +- PDF_DATAAVAIL_PAGE, +- PDF_DATAAVAIL_PAGE_LATERLOAD, +- PDF_DATAAVAIL_RESOURCES, +- PDF_DATAAVAIL_DONE, +- PDF_DATAAVAIL_ERROR, +- PDF_DATAAVAIL_LOADALLFILE, +-}; +- +-enum PDF_PAGENODE_TYPE { +- PDF_PAGENODE_UNKNOWN = 0, +- PDF_PAGENODE_PAGE, +- PDF_PAGENODE_PAGES, +- PDF_PAGENODE_ARRAY, +-}; +- + class CPDF_DataAvail final : public Observable::ObserverIface { + public: + // Must match PDF_DATA_* definitions in public/fpdf_dataavail.h, but cannot + // #include that header. fpdfsdk/fpdf_dataavail.cpp has static_asserts + // to make sure the two sets of values match. + enum DocAvailStatus { +- DataError = -1, // PDF_DATA_ERROR +- DataNotAvailable = 0, // PDF_DATA_NOTAVAIL +- DataAvailable = 1, // PDF_DATA_AVAIL ++ kDataError = -1, // PDF_DATA_ERROR ++ kDataNotAvailable = 0, // PDF_DATA_NOTAVAIL ++ kDataAvailable = 1, // PDF_DATA_AVAIL + }; + + // Must match PDF_*LINEAR* definitions in public/fpdf_dataavail.h, but cannot + // #include that header. fpdfsdk/fpdf_dataavail.cpp has static_asserts + // to make sure the two sets of values match. + enum DocLinearizationStatus { +- LinearizationUnknown = -1, // PDF_LINEARIZATION_UNKNOWN +- NotLinearized = 0, // PDF_NOT_LINEARIZED +- Linearized = 1, // PDF_LINEARIZED ++ kLinearizationUnknown = -1, // PDF_LINEARIZATION_UNKNOWN ++ kNotLinearized = 0, // PDF_NOT_LINEARIZED ++ kLinearized = 1, // PDF_LINEARIZED + }; + + // Must match PDF_FORM_* definitions in public/fpdf_dataavail.h, but cannot + // #include that header. fpdfsdk/fpdf_dataavail.cpp has static_asserts + // to make sure the two sets of values match. + enum DocFormStatus { +- FormError = -1, // PDF_FORM_ERROR +- FormNotAvailable = 0, // PDF_FORM_NOTAVAIL +- FormAvailable = 1, // PDF_FORM_AVAIL +- FormNotExist = 2, // PDF_FORM_NOTEXIST ++ kFormError = -1, // PDF_FORM_ERROR ++ kFormNotAvailable = 0, // PDF_FORM_NOTAVAIL ++ kFormAvailable = 1, // PDF_FORM_AVAIL ++ kFormNotExist = 2, // PDF_FORM_NOTEXIST + }; + + class FileAvail { +@@ -92,11 +71,10 @@ class CPDF_DataAvail final : public Observable::ObserverIface { + }; + + CPDF_DataAvail(FileAvail* pFileAvail, +- const RetainPtr& pFileRead, +- bool bSupportHintTable); ++ RetainPtr pFileRead); + ~CPDF_DataAvail() override; + +- // CPDF_Document::Observer: ++ // Observable::ObserverIface: + void OnObservableDestroyed() override; + + DocAvailStatus IsDocAvail(DownloadHints* pHints); +@@ -104,28 +82,48 @@ class CPDF_DataAvail final : public Observable::ObserverIface { + DocFormStatus IsFormAvail(DownloadHints* pHints); + DocLinearizationStatus IsLinearizedPDF(); + int GetPageCount() const; +- CPDF_Dictionary* GetPageDictionary(int index) const; ++ RetainPtr GetPageDictionary(int index) const; + RetainPtr GetValidator() const; + + std::pair> ParseDocument( + std::unique_ptr pRenderData, + std::unique_ptr pPageData, +- const char* password); ++ const ByteString& password); + +- const CPDF_HintTables* GetHintTables() const { return m_pHintTables.get(); } ++ const CPDF_HintTables* GetHintTablesForTest() const { ++ return m_pHintTables.get(); ++ } + + private: ++ enum class InternalStatus : uint8_t { ++ kHeader = 0, ++ kFirstPage, ++ kHintTable, ++ kLoadAllCrossRef, ++ kRoot, ++ kInfo, ++ kPageTree, ++ kPage, ++ kPageLaterLoad, ++ kResources, ++ kDone, ++ kError, ++ kLoadAllFile, ++ }; ++ + class PageNode { + public: ++ enum class Type { kUnknown = 0, kPage, kPages, kArray }; ++ + PageNode(); + ~PageNode(); + +- PDF_PAGENODE_TYPE m_type; +- uint32_t m_dwPageNo; ++ Type m_type = Type::kUnknown; ++ uint32_t m_dwPageNo = 0; + std::vector> m_ChildNodes; + }; + +- static const int kMaxPageRecursionDepth = 1024; ++ static constexpr int kMaxPageRecursionDepth = 1024; + + bool CheckDocStatus(); + bool CheckHeader(); +@@ -135,7 +133,7 @@ class CPDF_DataAvail final : public Observable::ObserverIface { + bool CheckInfo(); + bool CheckPages(); + bool CheckPage(); +- DocAvailStatus CheckResources(CPDF_Dictionary* page); ++ DocAvailStatus CheckResources(RetainPtr page); + DocFormStatus CheckAcroForm(); + bool CheckPageStatus(); + +@@ -172,11 +170,12 @@ class CPDF_DataAvail final : public Observable::ObserverIface { + RetainPtr m_pRoot; + std::unique_ptr m_pLinearized; + bool m_bDocAvail = false; ++ InternalStatus m_internalStatus = InternalStatus::kHeader; + std::unique_ptr m_pCrossRefAvail; +- PDF_DATAAVAIL_STATUS m_docStatus = PDF_DATAAVAIL_HEADER; + const FX_FILESIZE m_dwFileLen; + UnownedPtr m_pDocument; + std::vector m_PageObjList; ++ std::set m_SeenPageObjList; + uint32_t m_PagesObjNum = 0; + bool m_bLinearedDataOK = false; + bool m_bMainXRefLoadTried = false; +@@ -187,15 +186,16 @@ class CPDF_DataAvail final : public Observable::ObserverIface { + std::vector> m_PagesArray; + bool m_bTotalLoadPageTree = false; + bool m_bCurPageDictLoadOK = false; ++ bool m_bHeaderAvail = false; + PageNode m_PageNode; + std::set m_pageMapCheckState; + std::set m_pagesLoadState; + std::unique_ptr m_pHintTables; +- const bool m_bSupportHintTable; + std::map> m_PagesObjAvail; +- std::map> ++ std::map, ++ std::unique_ptr, ++ std::less<>> + m_PagesResourcesAvail; +- bool m_bHeaderAvail = false; + }; + + #endif // CORE_FPDFAPI_PARSER_CPDF_DATA_AVAIL_H_ +diff --git a/core/fpdfapi/parser/cpdf_dictionary.cpp b/core/fpdfapi/parser/cpdf_dictionary.cpp +index 6c4c6e658..bf6f59318 100644 +--- a/core/fpdfapi/parser/cpdf_dictionary.cpp ++++ b/core/fpdfapi/parser/cpdf_dictionary.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -19,9 +19,8 @@ + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fxcrt/fx_stream.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" + + CPDF_Dictionary::CPDF_Dictionary() + : CPDF_Dictionary(WeakPtr()) {} +@@ -34,7 +33,7 @@ CPDF_Dictionary::~CPDF_Dictionary() { + // and break cyclic references. + m_ObjNum = kInvalidObjNum; + for (auto& it : m_Map) { +- if (it.second && it.second->GetObjNum() == kInvalidObjNum) ++ if (it.second->GetObjNum() == kInvalidObjNum) + it.second.Leak(); + } + } +@@ -43,23 +42,7 @@ CPDF_Object::Type CPDF_Dictionary::GetType() const { + return kDictionary; + } + +-CPDF_Dictionary* CPDF_Dictionary::GetDict() { +- return this; +-} +- +-const CPDF_Dictionary* CPDF_Dictionary::GetDict() const { +- return this; +-} +- +-bool CPDF_Dictionary::IsDictionary() const { +- return true; +-} +- +-CPDF_Dictionary* CPDF_Dictionary::AsDictionary() { +- return this; +-} +- +-const CPDF_Dictionary* CPDF_Dictionary::AsDictionary() const { ++CPDF_Dictionary* CPDF_Dictionary::AsMutableDictionary() { + return this; + } + +@@ -74,126 +57,203 @@ RetainPtr CPDF_Dictionary::CloneNonCyclic( + auto pCopy = pdfium::MakeRetain(m_pPool); + CPDF_DictionaryLocker locker(this); + for (const auto& it : locker) { +- if (!pdfium::ContainsKey(*pVisited, it.second.Get())) { ++ if (!pdfium::Contains(*pVisited, it.second.Get())) { + std::set visited(*pVisited); +- if (auto obj = it.second->CloneNonCyclic(bDirect, &visited)) ++ auto obj = it.second->CloneNonCyclic(bDirect, &visited); ++ if (obj) + pCopy->m_Map.insert(std::make_pair(it.first, std::move(obj))); + } + } + return pCopy; + } + +-const CPDF_Object* CPDF_Dictionary::GetObjectFor(const ByteString& key) const { ++const CPDF_Object* CPDF_Dictionary::GetObjectForInternal( ++ const ByteString& key) const { + auto it = m_Map.find(key); + return it != m_Map.end() ? it->second.Get() : nullptr; + } + +-CPDF_Object* CPDF_Dictionary::GetObjectFor(const ByteString& key) { +- return const_cast( +- static_cast(this)->GetObjectFor(key)); ++RetainPtr CPDF_Dictionary::GetObjectFor( ++ const ByteString& key) const { ++ return pdfium::WrapRetain(GetObjectForInternal(key)); ++} ++ ++RetainPtr CPDF_Dictionary::GetMutableObjectFor( ++ const ByteString& key) { ++ return pdfium::WrapRetain( ++ const_cast(GetObjectForInternal(key))); ++} ++ ++const CPDF_Object* CPDF_Dictionary::GetDirectObjectForInternal( ++ const ByteString& key) const { ++ const CPDF_Object* p = GetObjectForInternal(key); ++ return p ? p->GetDirectInternal() : nullptr; + } + +-const CPDF_Object* CPDF_Dictionary::GetDirectObjectFor( ++RetainPtr CPDF_Dictionary::GetDirectObjectFor( + const ByteString& key) const { +- const CPDF_Object* p = GetObjectFor(key); +- return p ? p->GetDirect() : nullptr; ++ return pdfium::WrapRetain(GetDirectObjectForInternal(key)); + } + +-CPDF_Object* CPDF_Dictionary::GetDirectObjectFor(const ByteString& key) { +- return const_cast( +- static_cast(this)->GetDirectObjectFor(key)); ++RetainPtr CPDF_Dictionary::GetMutableDirectObjectFor( ++ const ByteString& key) { ++ return pdfium::WrapRetain( ++ const_cast(GetDirectObjectForInternal(key))); + } + +-ByteString CPDF_Dictionary::GetStringFor(const ByteString& key) const { +- const CPDF_Object* p = GetObjectFor(key); ++ByteString CPDF_Dictionary::GetByteStringFor(const ByteString& key) const { ++ const CPDF_Object* p = GetObjectForInternal(key); + return p ? p->GetString() : ByteString(); + } + ++ByteString CPDF_Dictionary::GetByteStringFor(const ByteString& key, ++ const ByteString& def) const { ++ const CPDF_Object* p = GetObjectForInternal(key); ++ return p ? p->GetString() : ByteString(def); ++} ++ + WideString CPDF_Dictionary::GetUnicodeTextFor(const ByteString& key) const { +- const CPDF_Object* p = GetObjectFor(key); ++ const CPDF_Object* p = GetObjectForInternal(key); + if (const CPDF_Reference* pRef = ToReference(p)) +- p = pRef->GetDirect(); ++ p = pRef->GetDirectInternal(); + return p ? p->GetUnicodeText() : WideString(); + } + +-ByteString CPDF_Dictionary::GetStringFor(const ByteString& key, +- const ByteString& def) const { +- const CPDF_Object* p = GetObjectFor(key); +- return p ? p->GetString() : ByteString(def); ++ByteString CPDF_Dictionary::GetNameFor(const ByteString& key) const { ++ const CPDF_Name* p = ToName(GetObjectForInternal(key)); ++ return p ? p->GetString() : ByteString(); ++} ++ ++bool CPDF_Dictionary::GetBooleanFor(const ByteString& key, ++ bool bDefault) const { ++ const CPDF_Object* p = GetObjectForInternal(key); ++ return ToBoolean(p) ? p->GetInteger() != 0 : bDefault; + } + + int CPDF_Dictionary::GetIntegerFor(const ByteString& key) const { +- const CPDF_Object* p = GetObjectFor(key); ++ const CPDF_Object* p = GetObjectForInternal(key); + return p ? p->GetInteger() : 0; + } + + int CPDF_Dictionary::GetIntegerFor(const ByteString& key, int def) const { +- const CPDF_Object* p = GetObjectFor(key); ++ const CPDF_Object* p = GetObjectForInternal(key); + return p ? p->GetInteger() : def; + } + +-float CPDF_Dictionary::GetNumberFor(const ByteString& key) const { +- const CPDF_Object* p = GetObjectFor(key); ++int CPDF_Dictionary::GetDirectIntegerFor(const ByteString& key) const { ++ const CPDF_Number* p = ToNumber(GetObjectForInternal(key)); ++ return p ? p->GetInteger() : 0; ++} ++ ++float CPDF_Dictionary::GetFloatFor(const ByteString& key) const { ++ const CPDF_Object* p = GetObjectForInternal(key); + return p ? p->GetNumber() : 0; + } + +-bool CPDF_Dictionary::GetBooleanFor(const ByteString& key, +- bool bDefault) const { +- const CPDF_Object* p = GetObjectFor(key); +- return ToBoolean(p) ? p->GetInteger() != 0 : bDefault; ++const CPDF_Dictionary* CPDF_Dictionary::GetDictInternal() const { ++ return this; + } + +-const CPDF_Dictionary* CPDF_Dictionary::GetDictFor( ++const CPDF_Dictionary* CPDF_Dictionary::GetDictForInternal( + const ByteString& key) const { +- const CPDF_Object* p = GetDirectObjectFor(key); +- if (!p) +- return nullptr; +- if (const CPDF_Dictionary* pDict = p->AsDictionary()) +- return pDict; +- if (const CPDF_Stream* pStream = p->AsStream()) +- return pStream->GetDict(); +- return nullptr; ++ const CPDF_Object* p = GetDirectObjectForInternal(key); ++ return p ? p->GetDictInternal() : nullptr; + } + +-CPDF_Dictionary* CPDF_Dictionary::GetDictFor(const ByteString& key) { +- return const_cast( +- static_cast(this)->GetDictFor(key)); ++RetainPtr CPDF_Dictionary::GetDictFor( ++ const ByteString& key) const { ++ return pdfium::WrapRetain(GetDictForInternal(key)); ++} ++ ++RetainPtr CPDF_Dictionary::GetMutableDictFor( ++ const ByteString& key) { ++ return pdfium::WrapRetain( ++ const_cast(GetDictForInternal(key))); ++} ++ ++RetainPtr CPDF_Dictionary::GetOrCreateDictFor( ++ const ByteString& key) { ++ RetainPtr result = GetMutableDictFor(key); ++ if (result) ++ return result; ++ return SetNewFor(key); ++} ++ ++const CPDF_Array* CPDF_Dictionary::GetArrayForInternal( ++ const ByteString& key) const { ++ return ToArray(GetDirectObjectForInternal(key)); ++} ++ ++RetainPtr CPDF_Dictionary::GetArrayFor( ++ const ByteString& key) const { ++ return pdfium::WrapRetain(GetArrayForInternal(key)); + } + +-const CPDF_Array* CPDF_Dictionary::GetArrayFor(const ByteString& key) const { +- return ToArray(GetDirectObjectFor(key)); ++RetainPtr CPDF_Dictionary::GetMutableArrayFor( ++ const ByteString& key) { ++ return pdfium::WrapRetain(const_cast(GetArrayForInternal(key))); + } + +-CPDF_Array* CPDF_Dictionary::GetArrayFor(const ByteString& key) { +- return ToArray(GetDirectObjectFor(key)); ++RetainPtr CPDF_Dictionary::GetOrCreateArrayFor( ++ const ByteString& key) { ++ RetainPtr result = GetMutableArrayFor(key); ++ if (result) ++ return result; ++ return SetNewFor(key); + } + +-const CPDF_Stream* CPDF_Dictionary::GetStreamFor(const ByteString& key) const { +- return ToStream(GetDirectObjectFor(key)); ++const CPDF_Stream* CPDF_Dictionary::GetStreamForInternal( ++ const ByteString& key) const { ++ return ToStream(GetDirectObjectForInternal(key)); + } + +-CPDF_Stream* CPDF_Dictionary::GetStreamFor(const ByteString& key) { +- return ToStream(GetDirectObjectFor(key)); ++RetainPtr CPDF_Dictionary::GetStreamFor( ++ const ByteString& key) const { ++ return pdfium::WrapRetain(GetStreamForInternal(key)); ++} ++ ++RetainPtr CPDF_Dictionary::GetMutableStreamFor( ++ const ByteString& key) { ++ return pdfium::WrapRetain( ++ const_cast(GetStreamForInternal(key))); ++} ++ ++const CPDF_Number* CPDF_Dictionary::GetNumberForInternal( ++ const ByteString& key) const { ++ return ToNumber(GetObjectForInternal(key)); ++} ++ ++RetainPtr CPDF_Dictionary::GetNumberFor( ++ const ByteString& key) const { ++ return pdfium::WrapRetain(GetNumberForInternal(key)); ++} ++ ++const CPDF_String* CPDF_Dictionary::GetStringForInternal( ++ const ByteString& key) const { ++ return ToString(GetObjectForInternal(key)); ++} ++ ++RetainPtr CPDF_Dictionary::GetStringFor( ++ const ByteString& key) const { ++ return pdfium::WrapRetain(GetStringForInternal(key)); + } + + CFX_FloatRect CPDF_Dictionary::GetRectFor(const ByteString& key) const { +- CFX_FloatRect rect; +- const CPDF_Array* pArray = GetArrayFor(key); ++ const CPDF_Array* pArray = GetArrayForInternal(key); + if (pArray) +- rect = pArray->GetRect(); +- return rect; ++ return pArray->GetRect(); ++ return CFX_FloatRect(); + } + + CFX_Matrix CPDF_Dictionary::GetMatrixFor(const ByteString& key) const { +- CFX_Matrix matrix; +- const CPDF_Array* pArray = GetArrayFor(key); ++ const CPDF_Array* pArray = GetArrayForInternal(key); + if (pArray) +- matrix = pArray->GetMatrix(); +- return matrix; ++ return pArray->GetMatrix(); ++ return CFX_Matrix(); + } + + bool CPDF_Dictionary::KeyExist(const ByteString& key) const { +- return pdfium::ContainsKey(m_Map, key); ++ return pdfium::Contains(m_Map, key); + } + + std::vector CPDF_Dictionary::GetKeys() const { +@@ -204,14 +264,19 @@ std::vector CPDF_Dictionary::GetKeys() const { + return result; + } + +-CPDF_Object* CPDF_Dictionary::SetFor(const ByteString& key, +- RetainPtr pObj) { ++void CPDF_Dictionary::SetFor(const ByteString& key, ++ RetainPtr pObj) { ++ (void)SetForInternal(key, std::move(pObj)); ++} ++ ++CPDF_Object* CPDF_Dictionary::SetForInternal(const ByteString& key, ++ RetainPtr pObj) { + CHECK(!IsLocked()); + if (!pObj) { + m_Map.erase(key); + return nullptr; + } +- ASSERT(pObj->IsInline()); ++ DCHECK(pObj->IsInline()); + CPDF_Object* pRet = pObj.Get(); + m_Map[MaybeIntern(key)] = std::move(pObj); + return pRet; +@@ -225,11 +290,11 @@ void CPDF_Dictionary::ConvertToIndirectObjectFor( + if (it == m_Map.end() || it->second->IsReference()) + return; + +- CPDF_Object* pObj = pHolder->AddIndirectObject(std::move(it->second)); +- it->second = pObj->MakeReference(pHolder); ++ pHolder->AddIndirectObject(it->second); ++ it->second = it->second->MakeReference(pHolder); + } + +-RetainPtr CPDF_Dictionary::RemoveFor(const ByteString& key) { ++RetainPtr CPDF_Dictionary::RemoveFor(ByteStringView key) { + CHECK(!IsLocked()); + RetainPtr result; + auto it = m_Map.find(key); +@@ -257,22 +322,22 @@ void CPDF_Dictionary::ReplaceKey(const ByteString& oldkey, + + void CPDF_Dictionary::SetRectFor(const ByteString& key, + const CFX_FloatRect& rect) { +- CPDF_Array* pArray = SetNewFor(key); +- pArray->AddNew(rect.left); +- pArray->AddNew(rect.bottom); +- pArray->AddNew(rect.right); +- pArray->AddNew(rect.top); ++ auto pArray = SetNewFor(key); ++ pArray->AppendNew(rect.left); ++ pArray->AppendNew(rect.bottom); ++ pArray->AppendNew(rect.right); ++ pArray->AppendNew(rect.top); + } + + void CPDF_Dictionary::SetMatrixFor(const ByteString& key, + const CFX_Matrix& matrix) { +- CPDF_Array* pArray = SetNewFor(key); +- pArray->AddNew(matrix.a); +- pArray->AddNew(matrix.b); +- pArray->AddNew(matrix.c); +- pArray->AddNew(matrix.d); +- pArray->AddNew(matrix.e); +- pArray->AddNew(matrix.f); ++ auto pArray = SetNewFor(key); ++ pArray->AppendNew(matrix.a); ++ pArray->AppendNew(matrix.b); ++ pArray->AppendNew(matrix.c); ++ pArray->AppendNew(matrix.d); ++ pArray->AppendNew(matrix.e); ++ pArray->AppendNew(matrix.f); + } + + ByteString CPDF_Dictionary::MaybeIntern(const ByteString& str) { +@@ -289,7 +354,7 @@ bool CPDF_Dictionary::WriteTo(IFX_ArchiveStream* archive, + CPDF_DictionaryLocker locker(this); + for (const auto& it : locker) { + const ByteString& key = it.first; +- CPDF_Object* pValue = it.second.Get(); ++ const RetainPtr& pValue = it.second; + if (!archive->WriteString("/") || + !archive->WriteString(PDF_NameEncode(key).AsStringView())) { + return false; +@@ -308,6 +373,18 @@ CPDF_DictionaryLocker::CPDF_DictionaryLocker(const CPDF_Dictionary* pDictionary) + m_pDictionary->m_LockCount++; + } + ++CPDF_DictionaryLocker::CPDF_DictionaryLocker( ++ RetainPtr pDictionary) ++ : m_pDictionary(std::move(pDictionary)) { ++ m_pDictionary->m_LockCount++; ++} ++ ++CPDF_DictionaryLocker::CPDF_DictionaryLocker( ++ RetainPtr pDictionary) ++ : m_pDictionary(std::move(pDictionary)) { ++ m_pDictionary->m_LockCount++; ++} ++ + CPDF_DictionaryLocker::~CPDF_DictionaryLocker() { + m_pDictionary->m_LockCount--; + } +diff --git a/core/fpdfapi/parser/cpdf_dictionary.h b/core/fpdfapi/parser/cpdf_dictionary.h +index ac1b226f6..34be276e8 100644 +--- a/core/fpdfapi/parser/cpdf_dictionary.h ++++ b/core/fpdfapi/parser/cpdf_dictionary.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,8 +7,8 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_DICTIONARY_H_ + #define CORE_FPDFAPI_PARSER_CPDF_DICTIONARY_H_ + ++#include + #include +-#include + #include + #include + #include +@@ -19,92 +19,100 @@ + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/string_pool_template.h" + #include "core/fxcrt/weak_ptr.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + class CPDF_IndirectObjectHolder; + ++// Dictionaries never contain nullptr for valid keys, but some of the methods ++// will return nullptr to indicate non-existent keys. + class CPDF_Dictionary final : public CPDF_Object { + public: +- using const_iterator = +- std::map>::const_iterator; ++ using DictMap = std::map, std::less<>>; ++ using const_iterator = DictMap::const_iterator; + +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // CPDF_Object: + Type GetType() const override; + RetainPtr Clone() const override; +- CPDF_Dictionary* GetDict() override; +- const CPDF_Dictionary* GetDict() const override; +- bool IsDictionary() const override; +- CPDF_Dictionary* AsDictionary() override; +- const CPDF_Dictionary* AsDictionary() const override; ++ CPDF_Dictionary* AsMutableDictionary() override; + bool WriteTo(IFX_ArchiveStream* archive, + const CPDF_Encryptor* encryptor) const override; + + bool IsLocked() const { return !!m_LockCount; } + + size_t size() const { return m_Map.size(); } +- const CPDF_Object* GetObjectFor(const ByteString& key) const; +- CPDF_Object* GetObjectFor(const ByteString& key); +- const CPDF_Object* GetDirectObjectFor(const ByteString& key) const; +- CPDF_Object* GetDirectObjectFor(const ByteString& key); +- ByteString GetStringFor(const ByteString& key) const; +- ByteString GetStringFor(const ByteString& key, +- const ByteString& default_str) const; ++ RetainPtr GetObjectFor(const ByteString& key) const; ++ RetainPtr GetMutableObjectFor(const ByteString& key); ++ ++ RetainPtr GetDirectObjectFor(const ByteString& key) const; ++ RetainPtr GetMutableDirectObjectFor(const ByteString& key); ++ ++ // These will return the string representation of the object specified by ++ // |key|, for any object type that has a string representation. ++ ByteString GetByteStringFor(const ByteString& key) const; ++ ByteString GetByteStringFor(const ByteString& key, ++ const ByteString& default_str) const; + WideString GetUnicodeTextFor(const ByteString& key) const; ++ ++ // This will only return the string representation of a name object specified ++ // by |key|. Useful when the PDF spec requires the value to be an object of ++ // type name. i.e. /Foo and not (Foo). ++ ByteString GetNameFor(const ByteString& key) const; ++ ++ bool GetBooleanFor(const ByteString& key, bool bDefault) const; + int GetIntegerFor(const ByteString& key) const; + int GetIntegerFor(const ByteString& key, int default_int) const; +- bool GetBooleanFor(const ByteString& key, bool bDefault) const; +- float GetNumberFor(const ByteString& key) const; +- const CPDF_Dictionary* GetDictFor(const ByteString& key) const; +- CPDF_Dictionary* GetDictFor(const ByteString& key); +- const CPDF_Stream* GetStreamFor(const ByteString& key) const; +- CPDF_Stream* GetStreamFor(const ByteString& key); +- const CPDF_Array* GetArrayFor(const ByteString& key) const; +- CPDF_Array* GetArrayFor(const ByteString& key); ++ int GetDirectIntegerFor(const ByteString& key) const; ++ float GetFloatFor(const ByteString& key) const; ++ RetainPtr GetDictFor(const ByteString& key) const; ++ RetainPtr GetMutableDictFor(const ByteString& key); ++ RetainPtr GetOrCreateDictFor(const ByteString& key); ++ RetainPtr GetArrayFor(const ByteString& key) const; ++ RetainPtr GetMutableArrayFor(const ByteString& key); ++ RetainPtr GetOrCreateArrayFor(const ByteString& key); ++ RetainPtr GetStreamFor(const ByteString& key) const; ++ RetainPtr GetMutableStreamFor(const ByteString& key); ++ RetainPtr GetNumberFor(const ByteString& key) const; ++ RetainPtr GetStringFor(const ByteString& key) const; + CFX_FloatRect GetRectFor(const ByteString& key) const; + CFX_Matrix GetMatrixFor(const ByteString& key) const; +- float GetFloatFor(const ByteString& key) const { return GetNumberFor(key); } + + bool KeyExist(const ByteString& key) const; + std::vector GetKeys() const; + + // Creates a new object owned by the dictionary and returns an unowned +- // pointer to it. Prefer using these templates over calls to SetFor(), +- // since by creating a new object with no previous references, they ensure +- // cycles can not be introduced. ++ // pointer to it. Invalidates iterators for the element with the key |key|. ++ // Prefer using these templates over calls to SetFor(), since by creating ++ // a new object with no previous references, they ensure cycles can not be ++ // introduced. + template +- typename std::enable_if::value, T*>::type SetNewFor( +- const ByteString& key, +- Args&&... args) { +- CHECK(!IsLocked()); +- return static_cast( +- SetFor(key, pdfium::MakeRetain(std::forward(args)...))); ++ typename std::enable_if::value, RetainPtr>::type ++ SetNewFor(const ByteString& key, Args&&... args) { ++ return pdfium::WrapRetain(static_cast(SetForInternal( ++ key, pdfium::MakeRetain(std::forward(args)...)))); + } + template +- typename std::enable_if::value, T*>::type SetNewFor( +- const ByteString& key, +- Args&&... args) { +- CHECK(!IsLocked()); +- return static_cast(SetFor( +- key, pdfium::MakeRetain(m_pPool, std::forward(args)...))); ++ typename std::enable_if::value, RetainPtr>::type ++ SetNewFor(const ByteString& key, Args&&... args) { ++ return pdfium::WrapRetain(static_cast(SetForInternal( ++ key, pdfium::MakeRetain(m_pPool, std::forward(args)...)))); + } + ++ // If |pObj| is null, then |key| is erased from the map. Otherwise, takes ++ // ownership of |pObj| and stores in in the map. Invalidates iterators for ++ // the element with the key |key|. ++ void SetFor(const ByteString& key, RetainPtr pObj); ++ + // Convenience functions to convert native objects to array form. + void SetRectFor(const ByteString& key, const CFX_FloatRect& rect); + void SetMatrixFor(const ByteString& key, const CFX_Matrix& matrix); + +- // Set* functions invalidate iterators for the element with the key |key|. +- // Takes ownership of |pObj|, returns an unowned pointer to it. +- CPDF_Object* SetFor(const ByteString& key, RetainPtr pObj); +- + void ConvertToIndirectObjectFor(const ByteString& key, + CPDF_IndirectObjectHolder* pHolder); + + // Invalidates iterators for the element with the key |key|. +- RetainPtr RemoveFor(const ByteString& key); ++ RetainPtr RemoveFor(ByteStringView key); + + // Invalidates iterators for the element with the key |oldkey|. + void ReplaceKey(const ByteString& oldkey, const ByteString& newkey); +@@ -118,21 +126,36 @@ class CPDF_Dictionary final : public CPDF_Object { + explicit CPDF_Dictionary(const WeakPtr& pPool); + ~CPDF_Dictionary() override; + ++ // No guarantees about result lifetime, use with caution. ++ const CPDF_Object* GetObjectForInternal(const ByteString& key) const; ++ const CPDF_Object* GetDirectObjectForInternal(const ByteString& key) const; ++ const CPDF_Array* GetArrayForInternal(const ByteString& key) const; ++ const CPDF_Dictionary* GetDictForInternal(const ByteString& key) const; ++ const CPDF_Number* GetNumberForInternal(const ByteString& key) const; ++ const CPDF_Stream* GetStreamForInternal(const ByteString& key) const; ++ const CPDF_String* GetStringForInternal(const ByteString& key) const; ++ CPDF_Object* SetForInternal(const ByteString& key, ++ RetainPtr pObj); ++ + ByteString MaybeIntern(const ByteString& str); ++ const CPDF_Dictionary* GetDictInternal() const override; + RetainPtr CloneNonCyclic( + bool bDirect, + std::set* visited) const override; + + mutable uint32_t m_LockCount = 0; + WeakPtr m_pPool; +- std::map> m_Map; ++ DictMap m_Map; + }; + + class CPDF_DictionaryLocker { + public: ++ FX_STACK_ALLOCATED(); + using const_iterator = CPDF_Dictionary::const_iterator; + + explicit CPDF_DictionaryLocker(const CPDF_Dictionary* pDictionary); ++ explicit CPDF_DictionaryLocker(RetainPtr pDictionary); ++ explicit CPDF_DictionaryLocker(RetainPtr pDictionary); + ~CPDF_DictionaryLocker(); + + const_iterator begin() const { +@@ -149,7 +172,7 @@ class CPDF_DictionaryLocker { + }; + + inline CPDF_Dictionary* ToDictionary(CPDF_Object* obj) { +- return obj ? obj->AsDictionary() : nullptr; ++ return obj ? obj->AsMutableDictionary() : nullptr; + } + + inline const CPDF_Dictionary* ToDictionary(const CPDF_Object* obj) { +@@ -160,4 +183,9 @@ inline RetainPtr ToDictionary(RetainPtr obj) { + return RetainPtr(ToDictionary(obj.Get())); + } + ++inline RetainPtr ToDictionary( ++ RetainPtr obj) { ++ return RetainPtr(ToDictionary(obj.Get())); ++} ++ + #endif // CORE_FPDFAPI_PARSER_CPDF_DICTIONARY_H_ +diff --git a/core/fpdfapi/parser/cpdf_dictionary_unittest.cpp b/core/fpdfapi/parser/cpdf_dictionary_unittest.cpp +new file mode 100644 +index 000000000..84acc3af3 +--- /dev/null ++++ b/core/fpdfapi/parser/cpdf_dictionary_unittest.cpp +@@ -0,0 +1,44 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fpdfapi/parser/cpdf_dictionary.h" ++ ++#include ++ ++#include "core/fpdfapi/parser/cpdf_array.h" ++#include "core/fpdfapi/parser/cpdf_number.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++TEST(DictionaryTest, Iterators) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("the-dictionary"); ++ dict->SetNewFor("the-array"); ++ dict->SetNewFor("the-stream"); ++ dict->SetNewFor("the-number", 42); ++ ++ CPDF_DictionaryLocker locked_dict(dict); ++ auto it = locked_dict.begin(); ++ EXPECT_NE(it, locked_dict.end()); ++ EXPECT_EQ(it->first, ByteString("the-array")); ++ EXPECT_TRUE(it->second->IsArray()); ++ ++ ++it; ++ EXPECT_NE(it, locked_dict.end()); ++ EXPECT_EQ(it->first, ByteString("the-dictionary")); ++ EXPECT_TRUE(it->second->IsDictionary()); ++ ++ ++it; ++ EXPECT_NE(it, locked_dict.end()); ++ EXPECT_EQ(it->first, ByteString("the-number")); ++ EXPECT_TRUE(it->second->IsNumber()); ++ ++ ++it; ++ EXPECT_NE(it, locked_dict.end()); ++ EXPECT_EQ(it->first, ByteString("the-stream")); ++ EXPECT_TRUE(it->second->IsStream()); ++ ++ ++it; ++ EXPECT_EQ(it, locked_dict.end()); ++} +diff --git a/core/fpdfapi/parser/cpdf_document.cpp b/core/fpdfapi/parser/cpdf_document.cpp +index d26d5c4aa..ee9dfcdc9 100644 +--- a/core/fpdfapi/parser/cpdf_document.cpp ++++ b/core/fpdfapi/parser/cpdf_document.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,11 +6,8 @@ + + #include "core/fpdfapi/parser/cpdf_document.h" + +-#include + #include +-#include + +-#include "build/build_config.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_linearized_header.h" +@@ -21,43 +18,109 @@ + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" +-#include "core/fpdfapi/parser/cpdf_string.h" ++#include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fxcodec/jbig2/JBig2_DocumentContext.h" + #include "core/fxcrt/fx_codepage.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxcrt/scoped_set_insertion.h" ++#include "core/fxcrt/stl_util.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" + + namespace { + + const int kMaxPageLevel = 1024; + +-int CountPages(CPDF_Dictionary* pPages, +- std::set* visited_pages) { ++// Returns a value in the range [0, `CPDF_Document::kPageMaxNum`), or nullopt on ++// error. ++absl::optional CountPages( ++ RetainPtr pPages, ++ std::set>* visited_pages) { + int count = pPages->GetIntegerFor("Count"); + if (count > 0 && count < CPDF_Document::kPageMaxNum) + return count; +- CPDF_Array* pKidList = pPages->GetArrayFor("Kids"); ++ RetainPtr pKidList = pPages->GetMutableArrayFor("Kids"); + if (!pKidList) + return 0; + count = 0; + for (size_t i = 0; i < pKidList->size(); i++) { +- CPDF_Dictionary* pKid = pKidList->GetDictAt(i); +- if (!pKid || pdfium::ContainsKey(*visited_pages, pKid)) ++ RetainPtr pKid = pKidList->GetMutableDictAt(i); ++ if (!pKid || pdfium::Contains(*visited_pages, pKid)) + continue; + if (pKid->KeyExist("Kids")) { + // Use |visited_pages| to help detect circular references of pages. +- pdfium::ScopedSetInsertion local_add(visited_pages, +- pKid); +- count += CountPages(pKid, visited_pages); ++ ScopedSetInsertion> local_add(visited_pages, ++ pKid); ++ absl::optional local_count = ++ CountPages(std::move(pKid), visited_pages); ++ if (!local_count.has_value()) { ++ return absl::nullopt; // Propagate error. ++ } ++ count += local_count.value(); + } else { + // This page is a leaf node. + count++; + } ++ if (count >= CPDF_Document::kPageMaxNum) { ++ return absl::nullopt; // Error: too many pages. ++ } + } + pPages->SetNewFor("Count", count); + return count; + } + ++int FindPageIndex(const CPDF_Dictionary* pNode, ++ uint32_t* skip_count, ++ uint32_t objnum, ++ int* index, ++ int level) { ++ if (!pNode->KeyExist("Kids")) { ++ if (objnum == pNode->GetObjNum()) ++ return *index; ++ ++ if (*skip_count != 0) ++ (*skip_count)--; ++ ++ (*index)++; ++ return -1; ++ } ++ ++ RetainPtr pKidList = pNode->GetArrayFor("Kids"); ++ if (!pKidList) ++ return -1; ++ ++ if (level >= kMaxPageLevel) ++ return -1; ++ ++ size_t count = pNode->GetIntegerFor("Count"); ++ if (count <= *skip_count) { ++ (*skip_count) -= count; ++ (*index) += count; ++ return -1; ++ } ++ ++ if (count && count == pKidList->size()) { ++ for (size_t i = 0; i < count; i++) { ++ RetainPtr pKid = ++ ToReference(pKidList->GetObjectAt(i)); ++ if (pKid && pKid->GetRefObjNum() == objnum) ++ return static_cast(*index + i); ++ } ++ } ++ ++ for (size_t i = 0; i < pKidList->size(); i++) { ++ RetainPtr pKid = pKidList->GetDictAt(i); ++ if (!pKid || pKid == pNode) ++ continue; ++ ++ int found_index = ++ FindPageIndex(pKid.Get(), skip_count, objnum, index, level + 1); ++ if (found_index >= 0) ++ return found_index; ++ } ++ return -1; ++} ++ + } // namespace + + CPDF_Document::CPDF_Document(std::unique_ptr pRenderData, +@@ -69,7 +132,19 @@ CPDF_Document::CPDF_Document(std::unique_ptr pRenderData, + m_pDocPage->SetDocument(this); + } + +-CPDF_Document::~CPDF_Document() = default; ++CPDF_Document::~CPDF_Document() { ++ // Be absolutely certain that |m_pExtension| is null before destroying ++ // the extension, to avoid re-entering it while being destroyed. clang ++ // seems to already do this for us, but the C++ standards seem to ++ // indicate the opposite. ++ m_pExtension.reset(); ++} ++ ++// static ++bool CPDF_Document::IsValidPageObject(const CPDF_Object* obj) { ++ // See ISO 32000-1:2008 spec, table 30. ++ return ValidateDictType(ToDictionary(obj), "Page"); ++} + + RetainPtr CPDF_Document::ParseIndirectObject(uint32_t objnum) { + return m_pParser ? m_pParser->ParseIndirectObject(objnum) : nullptr; +@@ -78,30 +153,33 @@ RetainPtr CPDF_Document::ParseIndirectObject(uint32_t objnum) { + bool CPDF_Document::TryInit() { + SetLastObjNum(m_pParser->GetLastObjNum()); + +- CPDF_Object* pRootObj = GetOrParseIndirectObject(m_pParser->GetRootObjNum()); ++ RetainPtr pRootObj = ++ GetOrParseIndirectObject(m_pParser->GetRootObjNum()); + if (pRootObj) +- m_pRootDict.Reset(pRootObj->GetDict()); ++ m_pRootDict = pRootObj->GetMutableDict(); + + LoadPages(); + return GetRoot() && GetPageCount() > 0; + } + + CPDF_Parser::Error CPDF_Document::LoadDoc( +- const RetainPtr& pFileAccess, +- const char* password) { ++ RetainPtr pFileAccess, ++ const ByteString& password) { + if (!m_pParser) +- SetParser(pdfium::MakeUnique(this)); ++ SetParser(std::make_unique(this)); + +- return HandleLoadResult(m_pParser->StartParse(pFileAccess, password)); ++ return HandleLoadResult( ++ m_pParser->StartParse(std::move(pFileAccess), password)); + } + + CPDF_Parser::Error CPDF_Document::LoadLinearizedDoc( +- const RetainPtr& validator, +- const char* password) { ++ RetainPtr validator, ++ const ByteString& password) { + if (!m_pParser) +- SetParser(pdfium::MakeUnique(this)); ++ SetParser(std::make_unique(this)); + +- return HandleLoadResult(m_pParser->StartLinearizedParse(validator, password)); ++ return HandleLoadResult( ++ m_pParser->StartLinearizedParse(std::move(validator), password)); + } + + void CPDF_Document::LoadPages() { +@@ -112,20 +190,27 @@ void CPDF_Document::LoadPages() { + return; + } + +- m_PageList.resize(linearized_header->GetPageCount()); +- ASSERT(linearized_header->GetFirstPageNo() < m_PageList.size()); +- m_PageList[linearized_header->GetFirstPageNo()] = +- linearized_header->GetFirstPageObjNum(); ++ uint32_t objnum = linearized_header->GetFirstPageObjNum(); ++ if (!IsValidPageObject(GetOrParseIndirectObject(objnum).Get())) { ++ m_PageList.resize(RetrievePageCount()); ++ return; ++ } ++ ++ uint32_t first_page_num = linearized_header->GetFirstPageNo(); ++ uint32_t page_count = linearized_header->GetPageCount(); ++ DCHECK(first_page_num < page_count); ++ m_PageList.resize(page_count); ++ m_PageList[first_page_num] = objnum; + } + +-CPDF_Dictionary* CPDF_Document::TraversePDFPages(int iPage, +- int* nPagesToGo, +- size_t level) { ++RetainPtr CPDF_Document::TraversePDFPages(int iPage, ++ int* nPagesToGo, ++ size_t level) { + if (*nPagesToGo < 0 || m_bReachedMaxPageLevel) + return nullptr; + +- CPDF_Dictionary* pPages = m_pTreeTraversal[level].first; +- CPDF_Array* pKidList = pPages->GetArrayFor("Kids"); ++ RetainPtr pPages = m_pTreeTraversal[level].first; ++ RetainPtr pKidList = pPages->GetMutableArrayFor("Kids"); + if (!pKidList) { + m_pTreeTraversal.pop_back(); + if (*nPagesToGo != 1) +@@ -138,12 +223,12 @@ CPDF_Dictionary* CPDF_Document::TraversePDFPages(int iPage, + m_bReachedMaxPageLevel = true; + return nullptr; + } +- CPDF_Dictionary* page = nullptr; ++ RetainPtr page; + for (size_t i = m_pTreeTraversal[level].second; i < pKidList->size(); i++) { + if (*nPagesToGo == 0) + break; + pKidList->ConvertToIndirectObjectAt(i, this); +- CPDF_Dictionary* pKid = pKidList->GetDictAt(i); ++ RetainPtr pKid = pKidList->GetMutableDictAt(i); + if (!pKid) { + (*nPagesToGo)--; + m_pTreeTraversal[level].second++; +@@ -158,22 +243,23 @@ CPDF_Dictionary* CPDF_Document::TraversePDFPages(int iPage, + (*nPagesToGo)--; + m_pTreeTraversal[level].second++; + if (*nPagesToGo == 0) { +- page = pKid; ++ page = std::move(pKid); + break; + } + } else { + // If the vector has size level+1, the child is not in yet + if (m_pTreeTraversal.size() == level + 1) +- m_pTreeTraversal.push_back(std::make_pair(pKid, 0)); ++ m_pTreeTraversal.emplace_back(std::move(pKid), 0); + // Now m_pTreeTraversal[level+1] should exist and be equal to pKid. +- CPDF_Dictionary* pageKid = TraversePDFPages(iPage, nPagesToGo, level + 1); ++ RetainPtr pPageKid = ++ TraversePDFPages(iPage, nPagesToGo, level + 1); + // Check if child was completely processed, i.e. it popped itself out + if (m_pTreeTraversal.size() == level + 1) + m_pTreeTraversal[level].second++; + // If child did not finish, no pages to go, or max level reached, end + if (m_pTreeTraversal.size() != level + 1 || *nPagesToGo == 0 || + m_bReachedMaxPageLevel) { +- page = pageKid; ++ page = std::move(pPageKid); + break; + } + } +@@ -190,7 +276,7 @@ void CPDF_Document::ResetTraversal() { + } + + void CPDF_Document::SetParser(std::unique_ptr pParser) { +- ASSERT(!m_pParser); ++ DCHECK(!m_pParser); + m_pParser = std::move(pParser); + } + +@@ -200,104 +286,75 @@ CPDF_Parser::Error CPDF_Document::HandleLoadResult(CPDF_Parser::Error error) { + return error; + } + +-const CPDF_Dictionary* CPDF_Document::GetPagesDict() const { ++RetainPtr CPDF_Document::GetPagesDict() const { + const CPDF_Dictionary* pRoot = GetRoot(); + return pRoot ? pRoot->GetDictFor("Pages") : nullptr; + } + +-CPDF_Dictionary* CPDF_Document::GetPagesDict() { +- return const_cast( +- static_cast(this)->GetPagesDict()); ++RetainPtr CPDF_Document::GetMutablePagesDict() { ++ return pdfium::WrapRetain( ++ const_cast(this->GetPagesDict().Get())); + } + + bool CPDF_Document::IsPageLoaded(int iPage) const { + return !!m_PageList[iPage]; + } + +-CPDF_Dictionary* CPDF_Document::GetPageDictionary(int iPage) { +- if (!pdfium::IndexInBounds(m_PageList, iPage)) ++RetainPtr CPDF_Document::GetPageDictionary(int iPage) { ++ if (!fxcrt::IndexInBounds(m_PageList, iPage)) + return nullptr; + + const uint32_t objnum = m_PageList[iPage]; + if (objnum) { +- CPDF_Dictionary* result = ToDictionary(GetOrParseIndirectObject(objnum)); ++ RetainPtr result = ++ ToDictionary(GetOrParseIndirectObject(objnum)); + if (result) + return result; + } + +- CPDF_Dictionary* pPages = GetPagesDict(); ++ RetainPtr pPages = GetMutablePagesDict(); + if (!pPages) + return nullptr; + + if (m_pTreeTraversal.empty()) { + ResetTraversal(); +- m_pTreeTraversal.push_back(std::make_pair(pPages, 0)); ++ m_pTreeTraversal.emplace_back(std::move(pPages), 0); + } + int nPagesToGo = iPage - m_iNextPageToTraverse + 1; +- CPDF_Dictionary* pPage = TraversePDFPages(iPage, &nPagesToGo, 0); ++ RetainPtr pPage = TraversePDFPages(iPage, &nPagesToGo, 0); + m_iNextPageToTraverse = iPage + 1; + return pPage; + } + ++RetainPtr CPDF_Document::GetMutablePageDictionary(int iPage) { ++ return pdfium::WrapRetain( ++ const_cast(GetPageDictionary(iPage).Get())); ++} ++ + void CPDF_Document::SetPageObjNum(int iPage, uint32_t objNum) { + m_PageList[iPage] = objNum; + } + +-int CPDF_Document::FindPageIndex(const CPDF_Dictionary* pNode, +- uint32_t* skip_count, +- uint32_t objnum, +- int* index, +- int level) const { +- if (!pNode->KeyExist("Kids")) { +- if (objnum == pNode->GetObjNum()) +- return *index; +- +- if (*skip_count) +- (*skip_count)--; +- +- (*index)++; +- return -1; +- } +- +- const CPDF_Array* pKidList = pNode->GetArrayFor("Kids"); +- if (!pKidList) +- return -1; +- +- if (level >= kMaxPageLevel) +- return -1; +- +- size_t count = pNode->GetIntegerFor("Count"); +- if (count <= *skip_count) { +- (*skip_count) -= count; +- (*index) += count; +- return -1; +- } +- +- if (count && count == pKidList->size()) { +- for (size_t i = 0; i < count; i++) { +- const CPDF_Reference* pKid = ToReference(pKidList->GetObjectAt(i)); +- if (pKid && pKid->GetRefObjNum() == objnum) +- return static_cast(*index + i); +- } +- } ++JBig2_DocumentContext* CPDF_Document::GetOrCreateCodecContext() { ++ if (!m_pCodecContext) ++ m_pCodecContext = std::make_unique(); ++ return m_pCodecContext.get(); ++} + +- for (size_t i = 0; i < pKidList->size(); i++) { +- const CPDF_Dictionary* pKid = pKidList->GetDictAt(i); +- if (!pKid || pKid == pNode) +- continue; ++RetainPtr CPDF_Document::CreateModifiedAPStream() { ++ auto stream = NewIndirect(); ++ m_ModifiedAPStreamIDs.insert(stream->GetObjNum()); ++ return stream; ++} + +- int found_index = FindPageIndex(pKid, skip_count, objnum, index, level + 1); +- if (found_index >= 0) +- return found_index; +- } +- return -1; ++bool CPDF_Document::IsModifiedAPStream(const CPDF_Stream* stream) const { ++ return stream && pdfium::Contains(m_ModifiedAPStreamIDs, stream->GetObjNum()); + } + + int CPDF_Document::GetPageIndex(uint32_t objnum) { +- uint32_t nPages = m_PageList.size(); + uint32_t skip_count = 0; + bool bSkipped = false; +- for (uint32_t i = 0; i < nPages; i++) { ++ for (uint32_t i = 0; i < m_PageList.size(); ++i) { + if (m_PageList[i] == objnum) + return i; + +@@ -306,7 +363,7 @@ int CPDF_Document::GetPageIndex(uint32_t objnum) { + bSkipped = true; + } + } +- const CPDF_Dictionary* pPages = GetPagesDict(); ++ RetainPtr pPages = GetPagesDict(); + if (!pPages) + return -1; + +@@ -314,28 +371,29 @@ int CPDF_Document::GetPageIndex(uint32_t objnum) { + int found_index = FindPageIndex(pPages, &skip_count, objnum, &start_index, 0); + + // Corrupt page tree may yield out-of-range results. +- if (!pdfium::IndexInBounds(m_PageList, found_index)) ++ if (!fxcrt::IndexInBounds(m_PageList, found_index)) + return -1; + +- m_PageList[found_index] = objnum; ++ // Only update |m_PageList| when |objnum| points to a /Page object. ++ if (IsValidPageObject(GetOrParseIndirectObject(objnum).Get())) ++ m_PageList[found_index] = objnum; + return found_index; + } + + int CPDF_Document::GetPageCount() const { +- return pdfium::CollectionSize(m_PageList); ++ return fxcrt::CollectionSize(m_PageList); + } + + int CPDF_Document::RetrievePageCount() { +- CPDF_Dictionary* pPages = GetPagesDict(); ++ RetainPtr pPages = GetMutablePagesDict(); + if (!pPages) + return 0; + + if (!pPages->KeyExist("Kids")) + return 1; + +- std::set visited_pages; +- visited_pages.insert(pPages); +- return CountPages(pPages, &visited_pages); ++ std::set> visited_pages = {pPages}; ++ return CountPages(std::move(pPages), &visited_pages).value_or(0); + } + + uint32_t CPDF_Document::GetUserPermissions() const { +@@ -345,22 +403,38 @@ uint32_t CPDF_Document::GetUserPermissions() const { + return m_pExtension ? m_pExtension->GetUserPermissions() : 0; + } + ++RetainPtr CPDF_Document::GetFontFileStreamAcc( ++ RetainPtr pFontStream) { ++ return m_pDocPage->GetFontFileStreamAcc(std::move(pFontStream)); ++} ++ ++void CPDF_Document::MaybePurgeFontFileStreamAcc( ++ RetainPtr&& pStreamAcc) { ++ if (m_pDocPage) ++ m_pDocPage->MaybePurgeFontFileStreamAcc(std::move(pStreamAcc)); ++} ++ ++void CPDF_Document::MaybePurgeImage(uint32_t objnum) { ++ if (m_pDocPage) ++ m_pDocPage->MaybePurgeImage(objnum); ++} ++ + void CPDF_Document::CreateNewDoc() { +- ASSERT(!m_pRootDict); +- ASSERT(!m_pInfoDict); +- m_pRootDict.Reset(NewIndirect()); ++ DCHECK(!m_pRootDict); ++ DCHECK(!m_pInfoDict); ++ m_pRootDict = NewIndirect(); + m_pRootDict->SetNewFor("Type", "Catalog"); + +- CPDF_Dictionary* pPages = NewIndirect(); ++ auto pPages = NewIndirect(); + pPages->SetNewFor("Type", "Pages"); + pPages->SetNewFor("Count", 0); + pPages->SetNewFor("Kids"); + m_pRootDict->SetNewFor("Pages", this, pPages->GetObjNum()); +- m_pInfoDict.Reset(NewIndirect()); ++ m_pInfoDict = NewIndirect(); + } + +-CPDF_Dictionary* CPDF_Document::CreateNewPage(int iPage) { +- CPDF_Dictionary* pDict = NewIndirect(); ++RetainPtr CPDF_Document::CreateNewPage(int iPage) { ++ auto pDict = NewIndirect(); + pDict->SetNewFor("Type", "Page"); + uint32_t dwObjNum = pDict->GetObjNum(); + if (!InsertNewPage(iPage, pDict)) { +@@ -370,18 +444,19 @@ CPDF_Dictionary* CPDF_Document::CreateNewPage(int iPage) { + return pDict; + } + +-bool CPDF_Document::InsertDeletePDFPage(CPDF_Dictionary* pPages, +- int nPagesToGo, +- CPDF_Dictionary* pPageDict, +- bool bInsert, +- std::set* pVisited) { +- CPDF_Array* pKidList = pPages->GetArrayFor("Kids"); ++bool CPDF_Document::InsertDeletePDFPage( ++ RetainPtr pPages, ++ int nPagesToGo, ++ RetainPtr pPageDict, ++ bool bInsert, ++ std::set>* pVisited) { ++ RetainPtr pKidList = pPages->GetMutableArrayFor("Kids"); + if (!pKidList) + return false; + + for (size_t i = 0; i < pKidList->size(); i++) { +- CPDF_Dictionary* pKid = pKidList->GetDictAt(i); +- if (pKid->GetStringFor("Type") == "Page") { ++ RetainPtr pKid = pKidList->GetMutableDictAt(i); ++ if (pKid->GetNameFor("Type") == "Page") { + if (nPagesToGo != 0) { + nPagesToGo--; + continue; +@@ -403,13 +478,14 @@ bool CPDF_Document::InsertDeletePDFPage(CPDF_Dictionary* pPages, + nPagesToGo -= nPages; + continue; + } +- if (pdfium::ContainsKey(*pVisited, pKid)) ++ if (pdfium::Contains(*pVisited, pKid)) + return false; + +- pdfium::ScopedSetInsertion insertion(pVisited, pKid); +- if (!InsertDeletePDFPage(pKid, nPagesToGo, pPageDict, bInsert, pVisited)) ++ ScopedSetInsertion> insertion(pVisited, pKid); ++ if (!InsertDeletePDFPage(std::move(pKid), nPagesToGo, pPageDict, bInsert, ++ pVisited)) { + return false; +- ++ } + pPages->SetNewFor( + "Count", pPages->GetIntegerFor("Count") + (bInsert ? 1 : -1)); + break; +@@ -417,9 +493,13 @@ bool CPDF_Document::InsertDeletePDFPage(CPDF_Dictionary* pPages, + return true; + } + +-bool CPDF_Document::InsertNewPage(int iPage, CPDF_Dictionary* pPageDict) { +- CPDF_Dictionary* pRoot = GetRoot(); +- CPDF_Dictionary* pPages = pRoot ? pRoot->GetDictFor("Pages") : nullptr; ++bool CPDF_Document::InsertNewPage(int iPage, ++ RetainPtr pPageDict) { ++ RetainPtr pRoot = GetMutableRoot(); ++ if (!pRoot) ++ return false; ++ ++ RetainPtr pPages = pRoot->GetMutableDictFor("Pages"); + if (!pPages) + return false; + +@@ -428,37 +508,42 @@ bool CPDF_Document::InsertNewPage(int iPage, CPDF_Dictionary* pPageDict) { + return false; + + if (iPage == nPages) { +- CPDF_Array* pPagesList = pPages->GetArrayFor("Kids"); +- if (!pPagesList) +- pPagesList = pPages->SetNewFor("Kids"); +- pPagesList->AddNew(this, pPageDict->GetObjNum()); ++ RetainPtr pPagesList = pPages->GetOrCreateArrayFor("Kids"); ++ pPagesList->AppendNew(this, pPageDict->GetObjNum()); + pPages->SetNewFor("Count", nPages + 1); + pPageDict->SetNewFor("Parent", this, pPages->GetObjNum()); + ResetTraversal(); + } else { +- std::set stack = {pPages}; +- if (!InsertDeletePDFPage(pPages, iPage, pPageDict, true, &stack)) ++ std::set> stack = {pPages}; ++ if (!InsertDeletePDFPage(std::move(pPages), iPage, pPageDict, true, &stack)) + return false; + } + m_PageList.insert(m_PageList.begin() + iPage, pPageDict->GetObjNum()); + return true; + } + +-CPDF_Dictionary* CPDF_Document::GetInfo() { ++RetainPtr CPDF_Document::GetInfo() { + if (m_pInfoDict) +- return m_pInfoDict.Get(); ++ return m_pInfoDict; ++ ++ if (!m_pParser) ++ return nullptr; + +- if (!m_pParser || !m_pParser->GetInfoObjNum()) ++ uint32_t info_obj_num = m_pParser->GetInfoObjNum(); ++ if (info_obj_num == 0) + return nullptr; + +- auto ref = +- pdfium::MakeRetain(this, m_pParser->GetInfoObjNum()); +- m_pInfoDict.Reset(ToDictionary(ref->GetDirect())); +- return m_pInfoDict.Get(); ++ auto ref = pdfium::MakeRetain(this, info_obj_num); ++ m_pInfoDict = ToDictionary(ref->GetMutableDirect()); ++ return m_pInfoDict; ++} ++ ++RetainPtr CPDF_Document::GetFileIdentifier() const { ++ return m_pParser ? m_pParser->GetIDArray() : nullptr; + } + + void CPDF_Document::DeletePage(int iPage) { +- CPDF_Dictionary* pPages = GetPagesDict(); ++ RetainPtr pPages = GetMutablePagesDict(); + if (!pPages) + return; + +@@ -466,13 +551,21 @@ void CPDF_Document::DeletePage(int iPage) { + if (iPage < 0 || iPage >= nPages) + return; + +- std::set stack = {pPages}; +- if (!InsertDeletePDFPage(pPages, iPage, nullptr, false, &stack)) ++ std::set> stack = {pPages}; ++ if (!InsertDeletePDFPage(std::move(pPages), iPage, nullptr, false, &stack)) + return; + + m_PageList.erase(m_PageList.begin() + iPage); + } + ++void CPDF_Document::SetRootForTesting(RetainPtr root) { ++ m_pRootDict = std::move(root); ++} ++ ++void CPDF_Document::ResizePageListForTesting(size_t size) { ++ m_PageList.resize(size); ++} ++ + CPDF_Document::StockFontClearer::StockFontClearer( + CPDF_Document::PageDataIface* pPageData) + : m_pPageData(pPageData) {} +diff --git a/core/fpdfapi/parser/cpdf_document.h b/core/fpdfapi/parser/cpdf_document.h +index d0d172930..9ceff30b1 100644 +--- a/core/fpdfapi/parser/cpdf_document.h ++++ b/core/fpdfapi/parser/cpdf_document.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,32 +7,23 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_DOCUMENT_H_ + #define CORE_FPDFAPI_PARSER_CPDF_DOCUMENT_H_ + +-#include + #include + #include + #include + #include + +-#include "build/build_config.h" +-#include "core/fpdfapi/parser/cpdf_object.h" ++#include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_parser.h" ++#include "core/fxcrt/fx_memory.h" + #include "core/fxcrt/observed_ptr.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + +-class CFX_Matrix; +-class CPDF_LinearizedHeader; +-class CPDF_Object; + class CPDF_ReadValidator; + class CPDF_StreamAcc; + class IFX_SeekableReadStream; + class JBig2_DocumentContext; + +-#define FPDFPERM_MODIFY 0x0008 +-#define FPDFPERM_ANNOT_FORM 0x0020 +-#define FPDFPERM_FILL_FORM 0x0100 +-#define FPDFPERM_EXTRACT_ACCESS 0x0200 +- + class CPDF_Document : public Observable, + public CPDF_Parser::ParsedObjectsHolder { + public: +@@ -40,7 +31,6 @@ class CPDF_Document : public Observable, + class Extension { + public: + virtual ~Extension() = default; +- virtual CPDF_Document* GetPDFDoc() const = 0; + virtual int GetPageCount() const = 0; + virtual void DeletePage(int page_index) = 0; + virtual uint32_t GetUserPermissions() const = 0; +@@ -62,12 +52,15 @@ class CPDF_Document : public Observable, + + virtual void ClearStockFont() = 0; + virtual RetainPtr GetFontFileStreamAcc( +- const CPDF_Stream* pFontStream) = 0; ++ RetainPtr pFontStream) = 0; + virtual void MaybePurgeFontFileStreamAcc( +- const CPDF_Stream* pFontStream) = 0; ++ RetainPtr&& pStreamAcc) = 0; ++ virtual void MaybePurgeImage(uint32_t objnum) = 0; + + void SetDocument(CPDF_Document* pDoc) { m_pDoc = pDoc; } +- CPDF_Document* GetDocument() const { return m_pDoc.Get(); } ++ ++ protected: ++ CPDF_Document* GetDocument() const { return m_pDoc; } + + private: + UnownedPtr m_pDoc; +@@ -79,13 +72,17 @@ class CPDF_Document : public Observable, + virtual ~RenderDataIface(); + + void SetDocument(CPDF_Document* pDoc) { m_pDoc = pDoc; } +- CPDF_Document* GetDocument() const { return m_pDoc.Get(); } ++ ++ protected: ++ CPDF_Document* GetDocument() const { return m_pDoc; } + + private: + UnownedPtr m_pDoc; + }; + +- static const int kPageMaxNum = 0xFFFFF; ++ static constexpr int kPageMaxNum = 0xFFFFF; ++ ++ static bool IsValidPageObject(const CPDF_Object* obj); + + CPDF_Document(std::unique_ptr pRenderData, + std::unique_ptr pPageData); +@@ -97,53 +94,73 @@ class CPDF_Document : public Observable, + } + + CPDF_Parser* GetParser() const { return m_pParser.get(); } +- CPDF_Dictionary* GetRoot() const { return m_pRootDict.Get(); } +- CPDF_Dictionary* GetInfo(); ++ const CPDF_Dictionary* GetRoot() const { return m_pRootDict.Get(); } ++ RetainPtr GetMutableRoot() { return m_pRootDict; } ++ RetainPtr GetInfo(); ++ RetainPtr GetFileIdentifier() const; + + void DeletePage(int iPage); + int GetPageCount() const; + bool IsPageLoaded(int iPage) const; +- CPDF_Dictionary* GetPageDictionary(int iPage); ++ RetainPtr GetPageDictionary(int iPage); ++ RetainPtr GetMutablePageDictionary(int iPage); + int GetPageIndex(uint32_t objnum); + uint32_t GetUserPermissions() const; + ++ // PageDataIface wrappers, try to avoid explicit getter calls. ++ RetainPtr GetFontFileStreamAcc( ++ RetainPtr pFontStream); ++ void MaybePurgeFontFileStreamAcc(RetainPtr&& pStreamAcc); ++ void MaybePurgeImage(uint32_t objnum); ++ + // Returns a valid pointer, unless it is called during destruction. + PageDataIface* GetPageData() const { return m_pDocPage.get(); } + RenderDataIface* GetRenderData() const { return m_pDocRender.get(); } + + void SetPageObjNum(int iPage, uint32_t objNum); + +- std::unique_ptr* CodecContext() { +- return &m_pCodecContext; +- } ++ JBig2_DocumentContext* GetOrCreateCodecContext(); + LinkListIface* GetLinksContext() const { return m_pLinksContext.get(); } + void SetLinksContext(std::unique_ptr pContext) { + m_pLinksContext = std::move(pContext); + } + +- // CPDF_Parser::ParsedObjectsHolder overrides: ++ // Behaves like NewIndirect(), but keeps track of the new stream. ++ RetainPtr CreateModifiedAPStream(); ++ ++ // Returns whether CreateModifiedAPStream() created `stream`. ++ bool IsModifiedAPStream(const CPDF_Stream* stream) const; ++ ++ // CPDF_Parser::ParsedObjectsHolder: + bool TryInit() override; ++ RetainPtr ParseIndirectObject(uint32_t objnum) override; + +- CPDF_Parser::Error LoadDoc( +- const RetainPtr& pFileAccess, +- const char* password); +- CPDF_Parser::Error LoadLinearizedDoc( +- const RetainPtr& validator, +- const char* password); ++ CPDF_Parser::Error LoadDoc(RetainPtr pFileAccess, ++ const ByteString& password); ++ CPDF_Parser::Error LoadLinearizedDoc(RetainPtr validator, ++ const ByteString& password); + bool has_valid_cross_reference_table() const { + return m_bHasValidCrossReferenceTable; + } + + void LoadPages(); + void CreateNewDoc(); +- CPDF_Dictionary* CreateNewPage(int iPage); ++ RetainPtr CreateNewPage(int iPage); + + void IncrementParsedPageCount() { ++m_ParsedPageCount; } + uint32_t GetParsedPageCountForTesting() { return m_ParsedPageCount; } + + protected: ++ void SetParser(std::unique_ptr pParser); ++ ++ void SetRootForTesting(RetainPtr root); ++ void ResizePageListForTesting(size_t size); ++ ++ private: + class StockFontClearer { + public: ++ FX_STACK_ALLOCATED(); ++ + explicit StockFontClearer(CPDF_Document::PageDataIface* pPageData); + ~StockFontClearer(); + +@@ -153,24 +170,23 @@ class CPDF_Document : public Observable, + + // Retrieve page count information by getting count value from the tree nodes + int RetrievePageCount(); ++ + // When this method is called, m_pTreeTraversal[level] exists. +- CPDF_Dictionary* TraversePDFPages(int iPage, int* nPagesToGo, size_t level); +- int FindPageIndex(const CPDF_Dictionary* pNode, +- uint32_t* skip_count, +- uint32_t objnum, +- int* index, +- int level) const; +- RetainPtr ParseIndirectObject(uint32_t objnum) override; +- const CPDF_Dictionary* GetPagesDict() const; +- CPDF_Dictionary* GetPagesDict(); +- bool InsertDeletePDFPage(CPDF_Dictionary* pPages, ++ RetainPtr TraversePDFPages(int iPage, ++ int* nPagesToGo, ++ size_t level); ++ ++ RetainPtr GetPagesDict() const; ++ RetainPtr GetMutablePagesDict(); ++ ++ bool InsertDeletePDFPage(RetainPtr pPages, + int nPagesToGo, +- CPDF_Dictionary* pPageDict, ++ RetainPtr pPageDict, + bool bInsert, +- std::set* pVisited); +- bool InsertNewPage(int iPage, CPDF_Dictionary* pPageDict); ++ std::set>* pVisited); ++ ++ bool InsertNewPage(int iPage, RetainPtr pPageDict); + void ResetTraversal(); +- void SetParser(std::unique_ptr pParser); + CPDF_Parser::Error HandleLoadResult(CPDF_Parser::Error error); + + std::unique_ptr m_pParser; +@@ -181,7 +197,7 @@ class CPDF_Document : public Observable, + // vector corresponds to the level being described. The pair contains a + // pointer to the dictionary being processed at the level, and an index of the + // of the child being processed within the dictionary's /Kids array. +- std::vector> m_pTreeTraversal; ++ std::vector, size_t>> m_pTreeTraversal; + + // True if the CPDF_Parser succeeded without having to rebuild the cross + // reference table. +@@ -196,6 +212,7 @@ class CPDF_Document : public Observable, + std::unique_ptr m_pDocPage; // Must be after |m_pDocRender|. + std::unique_ptr m_pCodecContext; + std::unique_ptr m_pLinksContext; ++ std::set m_ModifiedAPStreamIDs; + std::vector m_PageList; // Page number to page's dict objnum. + + // Must be second to last. +diff --git a/core/fpdfapi/parser/cpdf_document_unittest.cpp b/core/fpdfapi/parser/cpdf_document_unittest.cpp +index 1c52e5149..bc2aaae6a 100644 +--- a/core/fpdfapi/parser/cpdf_document_unittest.cpp ++++ b/core/fpdfapi/parser/cpdf_document_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,8 +7,7 @@ + #include + #include + +-#include "core/fpdfapi/page/cpdf_docpagedata.h" +-#include "core/fpdfapi/page/cpdf_pagemodule.h" ++#include "core/fpdfapi/page/test_with_page_module.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_boolean.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" +@@ -18,115 +17,114 @@ + #include "core/fpdfapi/parser/cpdf_parser.h" + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_string.h" +-#include "core/fpdfapi/render/cpdf_docrenderdata.h" ++#include "core/fpdfapi/parser/cpdf_test_document.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + namespace { + + const int kNumTestPages = 7; + +-CPDF_Dictionary* CreatePageTreeNode(RetainPtr kids, +- CPDF_Document* pDoc, +- int count) { +- CPDF_Array* pUnowned = pDoc->AddIndirectObject(std::move(kids))->AsArray(); +- CPDF_Dictionary* pageNode = pDoc->NewIndirect(); +- pageNode->SetNewFor("Type", "Pages", false); +- pageNode->SetNewFor("Kids", pDoc, pUnowned->GetObjNum()); ++RetainPtr CreatePageTreeNode(RetainPtr kids, ++ CPDF_Document* pDoc, ++ int count) { ++ uint32_t new_objnum = pDoc->AddIndirectObject(kids); ++ auto pageNode = pDoc->NewIndirect(); ++ pageNode->SetNewFor("Type", "Pages"); ++ pageNode->SetNewFor("Kids", pDoc, new_objnum); + pageNode->SetNewFor("Count", count); +- for (size_t i = 0; i < pUnowned->size(); i++) { +- pUnowned->GetDictAt(i)->SetNewFor("Parent", pDoc, +- pageNode->GetObjNum()); ++ for (size_t i = 0; i < kids->size(); i++) { ++ kids->GetMutableDictAt(i)->SetNewFor("Parent", pDoc, ++ pageNode->GetObjNum()); + } + return pageNode; + } + + RetainPtr CreateNumberedPage(size_t number) { + auto page = pdfium::MakeRetain(); +- page->SetNewFor("Type", "Page", false); ++ page->SetNewFor("Type", "Page"); + page->SetNewFor("PageNumbering", static_cast(number)); + return page; + } + +-class CPDF_TestDocumentForPages final : public CPDF_Document { ++class CPDF_TestDocumentForPages final : public CPDF_TestDocument { + public: +- CPDF_TestDocumentForPages() +- : CPDF_Document(pdfium::MakeUnique(), +- pdfium::MakeUnique()) { ++ CPDF_TestDocumentForPages() { + // Set up test + auto zeroToTwo = pdfium::MakeRetain(); +- zeroToTwo->AddNew( +- this, AddIndirectObject(CreateNumberedPage(0))->GetObjNum()); +- zeroToTwo->AddNew( +- this, AddIndirectObject(CreateNumberedPage(1))->GetObjNum()); +- zeroToTwo->AddNew( +- this, AddIndirectObject(CreateNumberedPage(2))->GetObjNum()); +- CPDF_Dictionary* branch1 = ++ zeroToTwo->AppendNew( ++ this, AddIndirectObject(CreateNumberedPage(0))); ++ zeroToTwo->AppendNew( ++ this, AddIndirectObject(CreateNumberedPage(1))); ++ zeroToTwo->AppendNew( ++ this, AddIndirectObject(CreateNumberedPage(2))); ++ RetainPtr branch1 = + CreatePageTreeNode(std::move(zeroToTwo), this, 3); + + auto zeroToThree = pdfium::MakeRetain(); +- zeroToThree->AddNew(this, branch1->GetObjNum()); +- zeroToThree->AddNew( +- this, AddIndirectObject(CreateNumberedPage(3))->GetObjNum()); +- CPDF_Dictionary* branch2 = ++ zeroToThree->AppendNew(this, branch1->GetObjNum()); ++ zeroToThree->AppendNew( ++ this, AddIndirectObject(CreateNumberedPage(3))); ++ RetainPtr branch2 = + CreatePageTreeNode(std::move(zeroToThree), this, 4); + + auto fourFive = pdfium::MakeRetain(); +- fourFive->AddNew( +- this, AddIndirectObject(CreateNumberedPage(4))->GetObjNum()); +- fourFive->AddNew( +- this, AddIndirectObject(CreateNumberedPage(5))->GetObjNum()); +- CPDF_Dictionary* branch3 = CreatePageTreeNode(std::move(fourFive), this, 2); ++ fourFive->AppendNew( ++ this, AddIndirectObject(CreateNumberedPage(4))); ++ fourFive->AppendNew( ++ this, AddIndirectObject(CreateNumberedPage(5))); ++ RetainPtr branch3 = ++ CreatePageTreeNode(std::move(fourFive), this, 2); + + auto justSix = pdfium::MakeRetain(); +- justSix->AddNew( +- this, AddIndirectObject(CreateNumberedPage(6))->GetObjNum()); +- CPDF_Dictionary* branch4 = CreatePageTreeNode(std::move(justSix), this, 1); ++ justSix->AppendNew( ++ this, AddIndirectObject(CreateNumberedPage(6))); ++ RetainPtr branch4 = ++ CreatePageTreeNode(std::move(justSix), this, 1); + + auto allPages = pdfium::MakeRetain(); +- allPages->AddNew(this, branch2->GetObjNum()); +- allPages->AddNew(this, branch3->GetObjNum()); +- allPages->AddNew(this, branch4->GetObjNum()); +- CPDF_Dictionary* pagesDict = ++ allPages->AppendNew(this, branch2->GetObjNum()); ++ allPages->AppendNew(this, branch3->GetObjNum()); ++ allPages->AppendNew(this, branch4->GetObjNum()); ++ RetainPtr pagesDict = + CreatePageTreeNode(std::move(allPages), this, kNumTestPages); + +- m_pRootDict.Reset(NewIndirect()); +- m_pRootDict->SetNewFor("Pages", this, +- pagesDict->GetObjNum()); +- m_PageList.resize(kNumTestPages); ++ SetRootForTesting(NewIndirect()); ++ GetMutableRoot()->SetNewFor("Pages", this, ++ pagesDict->GetObjNum()); ++ ResizePageListForTesting(kNumTestPages); + } + + void SetTreeSize(int size) { +- m_pRootDict->SetNewFor("Count", size); +- m_PageList.resize(size); ++ GetMutableRoot()->SetNewFor("Count", size); ++ ResizePageListForTesting(size); + } + }; + +-class CPDF_TestDocumentWithPageWithoutPageNum final : public CPDF_Document { ++class CPDF_TestDocumentWithPageWithoutPageNum final : public CPDF_TestDocument { + public: +- CPDF_TestDocumentWithPageWithoutPageNum() +- : CPDF_Document(pdfium::MakeUnique(), +- pdfium::MakeUnique()) { ++ CPDF_TestDocumentWithPageWithoutPageNum() { + // Set up test + auto allPages = pdfium::MakeRetain(); +- allPages->AddNew( +- this, AddIndirectObject(CreateNumberedPage(0))->GetObjNum()); +- allPages->AddNew( +- this, AddIndirectObject(CreateNumberedPage(1))->GetObjNum()); ++ allPages->AppendNew( ++ this, AddIndirectObject(CreateNumberedPage(0))); ++ allPages->AppendNew( ++ this, AddIndirectObject(CreateNumberedPage(1))); + // Page without pageNum. +- inlined_page_ = allPages->Add(CreateNumberedPage(2)); +- CPDF_Dictionary* pagesDict = ++ inlined_page_ = CreateNumberedPage(2); ++ allPages->Append(inlined_page_); ++ RetainPtr pagesDict = + CreatePageTreeNode(std::move(allPages), this, 3); +- m_pRootDict.Reset(NewIndirect()); +- m_pRootDict->SetNewFor("Pages", this, +- pagesDict->GetObjNum()); +- m_PageList.resize(3); ++ SetRootForTesting(NewIndirect()); ++ GetMutableRoot()->SetNewFor("Pages", this, ++ pagesDict->GetObjNum()); ++ ResizePageListForTesting(3); + } + +- const CPDF_Object* inlined_page() const { return inlined_page_; } ++ const CPDF_Object* inlined_page() const { return inlined_page_.Get(); } + + private: +- const CPDF_Object* inlined_page_; ++ RetainPtr inlined_page_; + }; + + class TestLinearized final : public CPDF_LinearizedHeader { +@@ -135,80 +133,75 @@ class TestLinearized final : public CPDF_LinearizedHeader { + : CPDF_LinearizedHeader(dict, 0) {} + }; + +-class CPDF_TestDocPagesWithoutKids final : public CPDF_Document { ++class CPDF_TestDocPagesWithoutKids final : public CPDF_TestDocument { + public: +- CPDF_TestDocPagesWithoutKids() +- : CPDF_Document(pdfium::MakeUnique(), +- pdfium::MakeUnique()) { +- CPDF_Dictionary* pagesDict = NewIndirect(); ++ CPDF_TestDocPagesWithoutKids() { ++ auto pagesDict = NewIndirect(); + pagesDict->SetNewFor("Type", "Pages"); + pagesDict->SetNewFor("Count", 3); +- m_PageList.resize(10); +- m_pRootDict.Reset(NewIndirect()); +- m_pRootDict->SetNewFor("Pages", this, +- pagesDict->GetObjNum()); ++ ResizePageListForTesting(10); ++ SetRootForTesting(NewIndirect()); ++ GetMutableRoot()->SetNewFor("Pages", this, ++ pagesDict->GetObjNum()); + } + }; + +-class CPDF_TestDocumentAllowSetParser final : public CPDF_Document { ++class CPDF_TestDocumentAllowSetParser final : public CPDF_TestDocument { + public: +- CPDF_TestDocumentAllowSetParser() +- : CPDF_Document(pdfium::MakeUnique(), +- pdfium::MakeUnique()) {} ++ CPDF_TestDocumentAllowSetParser() = default; + + using CPDF_Document::SetParser; + }; + + } // namespace + +-class cpdf_document_test : public testing::Test { +- public: +- void SetUp() override { CPDF_PageModule::Create(); } +- void TearDown() override { CPDF_PageModule::Destroy(); } +-}; ++using DocumentTest = TestWithPageModule; + +-TEST_F(cpdf_document_test, GetPages) { ++TEST_F(DocumentTest, GetPages) { + std::unique_ptr document = +- pdfium::MakeUnique(); ++ std::make_unique(); + for (int i = 0; i < kNumTestPages; i++) { +- CPDF_Dictionary* page = document->GetPageDictionary(i); ++ RetainPtr page = document->GetPageDictionary(i); + ASSERT_TRUE(page); + ASSERT_TRUE(page->KeyExist("PageNumbering")); + EXPECT_EQ(i, page->GetIntegerFor("PageNumbering")); + } +- CPDF_Dictionary* page = document->GetPageDictionary(kNumTestPages); ++ RetainPtr page = ++ document->GetPageDictionary(kNumTestPages); + EXPECT_FALSE(page); + } + +-TEST_F(cpdf_document_test, GetPageWithoutObjNumTwice) { +- auto document = pdfium::MakeUnique(); +- CPDF_Dictionary* page = document->GetPageDictionary(2); ++TEST_F(DocumentTest, GetPageWithoutObjNumTwice) { ++ auto document = std::make_unique(); ++ RetainPtr page = document->GetPageDictionary(2); + ASSERT_TRUE(page); + ASSERT_EQ(document->inlined_page(), page); + +- CPDF_Dictionary* second_call_page = document->GetPageDictionary(2); ++ RetainPtr second_call_page = ++ document->GetPageDictionary(2); + EXPECT_TRUE(second_call_page); + EXPECT_EQ(page, second_call_page); + } + +-TEST_F(cpdf_document_test, GetPagesReverseOrder) { ++TEST_F(DocumentTest, GetPagesReverseOrder) { + std::unique_ptr document = +- pdfium::MakeUnique(); ++ std::make_unique(); + for (int i = 6; i >= 0; i--) { +- CPDF_Dictionary* page = document->GetPageDictionary(i); ++ RetainPtr page = document->GetPageDictionary(i); + ASSERT_TRUE(page); + ASSERT_TRUE(page->KeyExist("PageNumbering")); + EXPECT_EQ(i, page->GetIntegerFor("PageNumbering")); + } +- CPDF_Dictionary* page = document->GetPageDictionary(kNumTestPages); ++ RetainPtr page = ++ document->GetPageDictionary(kNumTestPages); + EXPECT_FALSE(page); + } + +-TEST_F(cpdf_document_test, GetPagesInDisorder) { ++TEST_F(DocumentTest, GetPagesInDisorder) { + std::unique_ptr document = +- pdfium::MakeUnique(); ++ std::make_unique(); + +- CPDF_Dictionary* page = document->GetPageDictionary(1); ++ RetainPtr page = document->GetPageDictionary(1); + ASSERT_TRUE(page); + ASSERT_TRUE(page->KeyExist("PageNumbering")); + EXPECT_EQ(1, page->GetIntegerFor("PageNumbering")); +@@ -227,36 +220,72 @@ TEST_F(cpdf_document_test, GetPagesInDisorder) { + EXPECT_EQ(6, page->GetIntegerFor("PageNumbering")); + } + +-TEST_F(cpdf_document_test, UseCachedPageObjNumIfHaveNotPagesDict) { +- // ObjNum can be added in CPDF_DataAvail::IsPageAvail, and PagesDict +- // can be not exists in this case. +- // (case, when hint table is used to page check in CPDF_DataAvail). +- auto dict = pdfium::MakeRetain(); +- dict->SetNewFor("Linearized", true); +- const int page_count = 100; +- dict->SetNewFor("N", page_count); +- auto linearized = pdfium::MakeUnique(dict.Get()); +- auto parser = pdfium::MakeUnique(); +- parser->SetLinearizedHeader(std::move(linearized)); ++TEST_F(DocumentTest, IsValidPageObject) { ++ CPDF_TestDocumentForPages document; ++ ++ auto dict_type_name_page = pdfium::MakeRetain(); ++ dict_type_name_page->SetNewFor("Type", "Page"); ++ document.AddIndirectObject(dict_type_name_page); ++ EXPECT_TRUE(CPDF_Document::IsValidPageObject(dict_type_name_page.Get())); ++ ++ auto dict_type_string_page = pdfium::MakeRetain(); ++ dict_type_string_page->SetNewFor("Type", "Page", false); ++ document.AddIndirectObject(dict_type_string_page); ++ EXPECT_FALSE(CPDF_Document::IsValidPageObject(dict_type_string_page.Get())); ++ ++ auto dict_type_name_font = pdfium::MakeRetain(); ++ dict_type_name_font->SetNewFor("Type", "Font"); ++ document.AddIndirectObject(dict_type_name_font); ++ EXPECT_FALSE(CPDF_Document::IsValidPageObject(dict_type_name_font.Get())); ++ ++ auto obj_no_type = document.NewIndirect(); ++ EXPECT_FALSE(CPDF_Document::IsValidPageObject(obj_no_type.Get())); ++} ++ ++TEST_F(DocumentTest, UseCachedPageObjNumIfHaveNotPagesDict) { ++ // ObjNum can be added in CPDF_DataAvail::IsPageAvail(), and PagesDict may not ++ // exist in this case, e.g. when hint table is used to page check in ++ // CPDF_DataAvail. ++ constexpr int kPageCount = 100; ++ constexpr int kTestPageNum = 33; ++ ++ auto linearization_dict = pdfium::MakeRetain(); + CPDF_TestDocumentAllowSetParser document; +- document.SetParser(std::move(parser)); ++ ++ { ++ auto first_page = CreateNumberedPage(0); ++ ASSERT_TRUE(first_page); ++ ++ int first_page_obj_num = document.AddIndirectObject(first_page); ++ ASSERT_NE(kTestPageNum, first_page_obj_num); ++ ++ linearization_dict->SetNewFor("Linearized", true); ++ linearization_dict->SetNewFor("N", kPageCount); ++ linearization_dict->SetNewFor("O", first_page_obj_num); ++ ++ auto parser = std::make_unique(); ++ parser->SetLinearizedHeaderForTesting( ++ std::make_unique(linearization_dict.Get())); ++ document.SetParser(std::move(parser)); ++ } ++ + document.LoadPages(); +- ASSERT_EQ(page_count, document.GetPageCount()); +- CPDF_Object* page_stub = document.NewIndirect(); ++ ++ ASSERT_EQ(kPageCount, document.GetPageCount()); ++ auto page_stub = document.NewIndirect(); + const uint32_t obj_num = page_stub->GetObjNum(); +- const int test_page_num = 33; + +- EXPECT_FALSE(document.IsPageLoaded(test_page_num)); +- EXPECT_EQ(nullptr, document.GetPageDictionary(test_page_num)); ++ EXPECT_FALSE(document.IsPageLoaded(kTestPageNum)); ++ EXPECT_FALSE(document.GetPageDictionary(kTestPageNum)); + +- document.SetPageObjNum(test_page_num, obj_num); +- EXPECT_TRUE(document.IsPageLoaded(test_page_num)); +- EXPECT_EQ(page_stub, document.GetPageDictionary(test_page_num)); ++ document.SetPageObjNum(kTestPageNum, obj_num); ++ EXPECT_TRUE(document.IsPageLoaded(kTestPageNum)); ++ EXPECT_EQ(page_stub, document.GetPageDictionary(kTestPageNum)); + } + +-TEST_F(cpdf_document_test, CountGreaterThanPageTree) { ++TEST_F(DocumentTest, CountGreaterThanPageTree) { + std::unique_ptr document = +- pdfium::MakeUnique(); ++ std::make_unique(); + document->SetTreeSize(kNumTestPages + 3); + for (int i = 0; i < kNumTestPages; i++) + EXPECT_TRUE(document->GetPageDictionary(i)); +@@ -265,9 +294,9 @@ TEST_F(cpdf_document_test, CountGreaterThanPageTree) { + EXPECT_TRUE(document->GetPageDictionary(kNumTestPages - 1)); + } + +-TEST_F(cpdf_document_test, PagesWithoutKids) { ++TEST_F(DocumentTest, PagesWithoutKids) { + // Set up a document with Pages dict without kids, and Count = 3 +- auto pDoc = pdfium::MakeUnique(); ++ auto pDoc = std::make_unique(); + EXPECT_TRUE(pDoc->GetPageDictionary(0)); + // Test GetPage does not fetch pages out of range + for (int i = 1; i < 5; i++) +diff --git a/core/fpdfapi/parser/cpdf_encryptor.cpp b/core/fpdfapi/parser/cpdf_encryptor.cpp +index 706d668f6..e1804fc7b 100644 +--- a/core/fpdfapi/parser/cpdf_encryptor.cpp ++++ b/core/fpdfapi/parser/cpdf_encryptor.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,20 +6,24 @@ + + #include "core/fpdfapi/parser/cpdf_encryptor.h" + ++#include ++ + #include "core/fpdfapi/parser/cpdf_crypto_handler.h" ++#include "core/fxcrt/data_vector.h" ++#include "third_party/base/check.h" + +-CPDF_Encryptor::CPDF_Encryptor(CPDF_CryptoHandler* pHandler, int objnum) ++CPDF_Encryptor::CPDF_Encryptor(const CPDF_CryptoHandler* pHandler, int objnum) + : m_pHandler(pHandler), m_ObjNum(objnum) { +- ASSERT(m_pHandler); ++ DCHECK(m_pHandler); + } + +-std::vector CPDF_Encryptor::Encrypt( ++DataVector CPDF_Encryptor::Encrypt( + pdfium::span src_data) const { + if (src_data.empty()) +- return std::vector(); ++ return DataVector(); + +- std::vector result; +- uint32_t buf_size = m_pHandler->EncryptGetSize(src_data); ++ DataVector result; ++ size_t buf_size = m_pHandler->EncryptGetSize(src_data); + result.resize(buf_size); + m_pHandler->EncryptContent(m_ObjNum, 0, src_data, result.data(), + buf_size); // Updates |buf_size| with actual. +@@ -27,4 +31,4 @@ std::vector CPDF_Encryptor::Encrypt( + return result; + } + +-CPDF_Encryptor::~CPDF_Encryptor() {} ++CPDF_Encryptor::~CPDF_Encryptor() = default; +diff --git a/core/fpdfapi/parser/cpdf_encryptor.h b/core/fpdfapi/parser/cpdf_encryptor.h +index cea737afd..37f076322 100644 +--- a/core/fpdfapi/parser/cpdf_encryptor.h ++++ b/core/fpdfapi/parser/cpdf_encryptor.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,8 +9,7 @@ + + #include + +-#include +- ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/unowned_ptr.h" + #include "third_party/base/span.h" + +@@ -18,13 +17,13 @@ class CPDF_CryptoHandler; + + class CPDF_Encryptor { + public: +- CPDF_Encryptor(CPDF_CryptoHandler* pHandler, int objnum); ++ CPDF_Encryptor(const CPDF_CryptoHandler* pHandler, int objnum); + ~CPDF_Encryptor(); + +- std::vector Encrypt(pdfium::span src_data) const; ++ DataVector Encrypt(pdfium::span src_data) const; + + private: +- UnownedPtr const m_pHandler; ++ UnownedPtr const m_pHandler; + const int m_ObjNum; + }; + +diff --git a/core/fpdfapi/parser/cpdf_flateencoder.cpp b/core/fpdfapi/parser/cpdf_flateencoder.cpp +index b68582282..64acd72cb 100644 +--- a/core/fpdfapi/parser/cpdf_flateencoder.cpp ++++ b/core/fpdfapi/parser/cpdf_flateencoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,9 +6,6 @@ + + #include "core/fpdfapi/parser/cpdf_flateencoder.h" + +-#include +-#include +- + #include "constants/stream_dict_common.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_name.h" +@@ -16,10 +13,12 @@ + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" ++#include "third_party/base/check.h" ++#include "third_party/base/numerics/safe_conversions.h" + +-CPDF_FlateEncoder::CPDF_FlateEncoder(const CPDF_Stream* pStream, ++CPDF_FlateEncoder::CPDF_FlateEncoder(RetainPtr pStream, + bool bFlateEncode) +- : m_pAcc(pdfium::MakeRetain(pStream)), m_dwSize(0) { ++ : m_pAcc(pdfium::MakeRetain(pStream)) { + m_pAcc->LoadAllDataRaw(); + + bool bHasFilter = pStream->HasFilter(); +@@ -27,56 +26,59 @@ CPDF_FlateEncoder::CPDF_FlateEncoder(const CPDF_Stream* pStream, + auto pDestAcc = pdfium::MakeRetain(pStream); + pDestAcc->LoadAllDataFiltered(); + +- m_dwSize = pDestAcc->GetSize(); +- m_pData = pDestAcc->DetachData(); ++ m_Data = m_pAcc->GetSpan(); + m_pClonedDict = ToDictionary(pStream->GetDict()->Clone()); + m_pClonedDict->RemoveFor("Filter"); +- ASSERT(!m_pDict); ++ DCHECK(!m_pDict); + return; + } + if (bHasFilter || !bFlateEncode) { +- m_pData = m_pAcc->GetData(); +- m_dwSize = m_pAcc->GetSize(); +- m_pDict.Reset(pStream->GetDict()); +- ASSERT(!m_pClonedDict); ++ m_Data = m_pAcc->GetSpan(); ++ m_pDict = pStream->GetDict(); ++ DCHECK(!m_pClonedDict); + return; + } + +- // TODO(thestig): Move to Init() and check return value. +- std::unique_ptr buffer; +- ::FlateEncode(m_pAcc->GetSpan(), &buffer, &m_dwSize); +- +- m_pData = std::move(buffer); ++ // TODO(thestig): Move to Init() and check for empty return value? ++ m_Data = ::FlateEncode(m_pAcc->GetSpan()); + m_pClonedDict = ToDictionary(pStream->GetDict()->Clone()); +- m_pClonedDict->SetNewFor("Length", static_cast(m_dwSize)); ++ m_pClonedDict->SetNewFor( ++ "Length", pdfium::base::checked_cast(GetSpan().size())); + m_pClonedDict->SetNewFor("Filter", "FlateDecode"); + m_pClonedDict->RemoveFor(pdfium::stream::kDecodeParms); +- ASSERT(!m_pDict); ++ DCHECK(!m_pDict); + } + +-CPDF_FlateEncoder::~CPDF_FlateEncoder() {} ++CPDF_FlateEncoder::~CPDF_FlateEncoder() = default; + +-void CPDF_FlateEncoder::CloneDict() { +- if (m_pClonedDict) { +- ASSERT(!m_pDict); ++void CPDF_FlateEncoder::UpdateLength(size_t size) { ++ if (static_cast(GetDict()->GetIntegerFor("Length")) == size) + return; +- } + +- m_pClonedDict = ToDictionary(m_pDict->Clone()); +- ASSERT(m_pClonedDict); +- m_pDict.Reset(); ++ if (!m_pClonedDict) { ++ m_pClonedDict = ToDictionary(m_pDict->Clone()); ++ m_pDict.Reset(); ++ } ++ DCHECK(m_pClonedDict); ++ DCHECK(!m_pDict); ++ m_pClonedDict->SetNewFor("Length", static_cast(size)); + } + +-CPDF_Dictionary* CPDF_FlateEncoder::GetClonedDict() { +- ASSERT(!m_pDict); +- return m_pClonedDict.Get(); ++bool CPDF_FlateEncoder::WriteDictTo(IFX_ArchiveStream* archive, ++ const CPDF_Encryptor* encryptor) const { ++ return GetDict()->WriteTo(archive, encryptor); + } + + const CPDF_Dictionary* CPDF_FlateEncoder::GetDict() const { + if (m_pClonedDict) { +- ASSERT(!m_pDict); ++ DCHECK(!m_pDict); + return m_pClonedDict.Get(); + } +- + return m_pDict.Get(); + } ++ ++pdfium::span CPDF_FlateEncoder::GetSpan() const { ++ if (is_owned()) ++ return absl::get>(m_Data); ++ return absl::get>(m_Data); ++} +diff --git a/core/fpdfapi/parser/cpdf_flateencoder.h b/core/fpdfapi/parser/cpdf_flateencoder.h +index df0db8232..2d26d9b88 100644 +--- a/core/fpdfapi/parser/cpdf_flateencoder.h ++++ b/core/fpdfapi/parser/cpdf_flateencoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,37 +7,42 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_FLATEENCODER_H_ + #define CORE_FPDFAPI_PARSER_CPDF_FLATEENCODER_H_ + +-#include ++#include + +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxcrt/maybe_owned.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/retain_ptr.h" ++#include "third_party/abseil-cpp/absl/types/variant.h" + #include "third_party/base/span.h" + + class CPDF_Dictionary; ++class CPDF_Encryptor; + class CPDF_Stream; + class CPDF_StreamAcc; ++class IFX_ArchiveStream; + + class CPDF_FlateEncoder { + public: +- CPDF_FlateEncoder(const CPDF_Stream* pStream, bool bFlateEncode); ++ CPDF_FlateEncoder(RetainPtr pStream, bool bFlateEncode); + ~CPDF_FlateEncoder(); + +- void CloneDict(); +- CPDF_Dictionary* GetClonedDict(); ++ void UpdateLength(size_t size); ++ bool WriteDictTo(IFX_ArchiveStream* archive, ++ const CPDF_Encryptor* encryptor) const; + +- // Returns |m_pClonedDict| if it is valid. Otherwise returns |m_pDict|. +- const CPDF_Dictionary* GetDict() const; ++ pdfium::span GetSpan() const; + +- pdfium::span GetSpan() const { +- return pdfium::make_span(m_pData.Get(), m_dwSize); ++ private: ++ bool is_owned() const { ++ return absl::holds_alternative>(m_Data); + } + +- private: +- RetainPtr m_pAcc; ++ // Returns |m_pClonedDict| if it is valid. Otherwise returns |m_pDict|. ++ const CPDF_Dictionary* GetDict() const; ++ ++ // Must outlive `m_Data`. ++ RetainPtr const m_pAcc; + +- uint32_t m_dwSize; +- MaybeOwned m_pData; ++ absl::variant, DataVector> m_Data; + + // Only one of these two pointers is valid at any time. + RetainPtr m_pDict; +diff --git a/core/fpdfapi/parser/cpdf_hint_tables.cpp b/core/fpdfapi/parser/cpdf_hint_tables.cpp +index e05125441..ba3173d1d 100644 +--- a/core/fpdfapi/parser/cpdf_hint_tables.cpp ++++ b/core/fpdfapi/parser/cpdf_hint_tables.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,14 +13,15 @@ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_linearized_header.h" ++#include "core/fpdfapi/parser/cpdf_parser.h" + #include "core/fpdfapi/parser/cpdf_read_validator.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" + #include "core/fpdfapi/parser/cpdf_syntax_parser.h" + #include "core/fxcrt/cfx_bitstream.h" + #include "core/fxcrt/fx_safe_types.h" ++#include "third_party/base/check.h" + #include "third_party/base/numerics/safe_conversions.h" +-#include "third_party/base/ptr_util.h" + #include "third_party/base/span.h" + + namespace { +@@ -45,8 +46,8 @@ CPDF_HintTables::PageInfo::~PageInfo() = default; + // static + std::unique_ptr CPDF_HintTables::Parse( + CPDF_SyntaxParser* parser, +- CPDF_LinearizedHeader* pLinearized) { +- ASSERT(parser); ++ const CPDF_LinearizedHeader* pLinearized) { ++ DCHECK(parser); + if (!pLinearized || pLinearized->GetPageCount() <= 1 || + !pLinearized->HasHintTable()) { + return nullptr; +@@ -67,7 +68,7 @@ std::unique_ptr CPDF_HintTables::Parse( + if (!hints_stream) + return nullptr; + +- auto pHintTables = pdfium::MakeUnique( ++ auto pHintTables = std::make_unique( + parser->GetValidator().Get(), pLinearized); + if (!pHintTables->LoadHintStream(hints_stream.Get())) + return nullptr; +@@ -76,15 +77,12 @@ std::unique_ptr CPDF_HintTables::Parse( + } + + CPDF_HintTables::CPDF_HintTables(CPDF_ReadValidator* pValidator, +- CPDF_LinearizedHeader* pLinearized) +- : m_pValidator(pValidator), +- m_pLinearized(pLinearized), +- m_nFirstPageSharedObjs(0), +- m_szFirstPageObjOffset(0) { +- ASSERT(m_pLinearized); ++ const CPDF_LinearizedHeader* pLinearized) ++ : m_pValidator(pValidator), m_pLinearized(pLinearized) { ++ DCHECK(m_pLinearized); + } + +-CPDF_HintTables::~CPDF_HintTables() {} ++CPDF_HintTables::~CPDF_HintTables() = default; + + bool CPDF_HintTables::ReadPageHintTable(CFX_BitStream* hStream) { + const uint32_t nPages = m_pLinearized->GetPageCount(); +@@ -104,7 +102,7 @@ bool CPDF_HintTables::ReadPageHintTable(CFX_BitStream* hStream) { + + // Item 1: The least number of objects in a page. + const uint32_t dwObjLeastNum = hStream->GetBits(32); +- if (!dwObjLeastNum) ++ if (!dwObjLeastNum || dwObjLeastNum >= CPDF_Parser::kMaxObjectNumber) + return false; + + // Item 2: The location of the first page's page object. +@@ -167,7 +165,7 @@ bool CPDF_HintTables::ReadPageHintTable(CFX_BitStream* hStream) { + m_PageInfos[nFirstPageNum].set_start_obj_num( + m_pLinearized->GetFirstPageObjNum()); + // The object number of remaining pages starts from 1. +- uint32_t dwStartObjNum = 1; ++ FX_SAFE_UINT32 dwStartObjNum = 1; + for (uint32_t i = 0; i < nPages; ++i) { + FX_SAFE_UINT32 safeDeltaObj = hStream->GetBits(dwDeltaObjectsBits); + safeDeltaObj += dwObjLeastNum; +@@ -176,8 +174,12 @@ bool CPDF_HintTables::ReadPageHintTable(CFX_BitStream* hStream) { + m_PageInfos[i].set_objects_count(safeDeltaObj.ValueOrDie()); + if (i == nFirstPageNum) + continue; +- m_PageInfos[i].set_start_obj_num(dwStartObjNum); ++ m_PageInfos[i].set_start_obj_num(dwStartObjNum.ValueOrDie()); + dwStartObjNum += m_PageInfos[i].objects_count(); ++ if (!dwStartObjNum.IsValid() || ++ dwStartObjNum.ValueOrDie() >= CPDF_Parser::kMaxObjectNumber) { ++ return false; ++ } + } + hStream->ByteAlign(); + +@@ -194,7 +196,7 @@ bool CPDF_HintTables::ReadPageHintTable(CFX_BitStream* hStream) { + m_PageInfos[i].set_page_length(safePageLen.ValueOrDie()); + } + +- ASSERT(m_szFirstPageObjOffset); ++ DCHECK(m_szFirstPageObjOffset); + m_PageInfos[nFirstPageNum].set_page_offset(m_szFirstPageObjOffset); + FX_FILESIZE prev_page_end = m_pLinearized->GetFirstPageEndOffset(); + for (uint32_t i = 0; i < nPages; ++i) { +@@ -407,18 +409,18 @@ bool CPDF_HintTables::GetPagePos(uint32_t index, + + CPDF_DataAvail::DocAvailStatus CPDF_HintTables::CheckPage(uint32_t index) { + if (index == m_pLinearized->GetFirstPageNo()) +- return CPDF_DataAvail::DataAvailable; ++ return CPDF_DataAvail::kDataAvailable; + + if (index >= m_pLinearized->GetPageCount()) +- return CPDF_DataAvail::DataError; ++ return CPDF_DataAvail::kDataError; + + const uint32_t dwLength = m_PageInfos[index].page_length(); + if (!dwLength) +- return CPDF_DataAvail::DataError; ++ return CPDF_DataAvail::kDataError; + + if (!m_pValidator->CheckDataRangeAndRequestIfUnavailable( + m_PageInfos[index].page_offset(), dwLength)) { +- return CPDF_DataAvail::DataNotAvailable; ++ return CPDF_DataAvail::kDataNotAvailable; + } + + // Download data of shared objects in the page. +@@ -429,22 +431,25 @@ CPDF_DataAvail::DocAvailStatus CPDF_HintTables::CheckPage(uint32_t index) { + m_SharedObjGroupInfos[dwIndex]; + + if (!shared_group_info.m_szOffset || !shared_group_info.m_dwLength) +- return CPDF_DataAvail::DataError; ++ return CPDF_DataAvail::kDataError; + + if (!m_pValidator->CheckDataRangeAndRequestIfUnavailable( + shared_group_info.m_szOffset, shared_group_info.m_dwLength)) { +- return CPDF_DataAvail::DataNotAvailable; ++ return CPDF_DataAvail::kDataNotAvailable; + } + } +- return CPDF_DataAvail::DataAvailable; ++ return CPDF_DataAvail::kDataAvailable; + } + + bool CPDF_HintTables::LoadHintStream(CPDF_Stream* pHintStream) { + if (!pHintStream || !m_pLinearized->HasHintTable()) + return false; + +- CPDF_Dictionary* pDict = pHintStream->GetDict(); +- CPDF_Object* pOffset = pDict ? pDict->GetObjectFor("S") : nullptr; ++ RetainPtr pDict = pHintStream->GetDict(); ++ if (!pDict) ++ return false; ++ ++ RetainPtr pOffset = pDict->GetObjectFor("S"); + if (!pOffset || !pOffset->IsNumber()) + return false; + +@@ -452,7 +457,8 @@ bool CPDF_HintTables::LoadHintStream(CPDF_Stream* pHintStream) { + if (shared_hint_table_offset <= 0) + return false; + +- auto pAcc = pdfium::MakeRetain(pHintStream); ++ auto pAcc = ++ pdfium::MakeRetain(pdfium::WrapRetain(pHintStream)); + pAcc->LoadAllDataFiltered(); + + uint32_t size = pAcc->GetSize(); +@@ -484,7 +490,7 @@ FX_FILESIZE CPDF_HintTables::HintsOffsetToFileOffset( + // itself were not present. That is, a position greater than the hint stream + // offset shall have the hint stream length added to it to determine the + // actual offset relative to the beginning of the file. +- // See specification PDF 32000-1:2008 Annex F.4 (Hint tables). ++ // See ISO 32000-1:2008 spec, annex F.4 (Hint tables). + // Note: The PDF spec does not mention this, but positions equal to the hint + // stream offset also need to have the hint stream length added to it. e.g. + // There exists linearized PDFs generated by Adobe software that have this +diff --git a/core/fpdfapi/parser/cpdf_hint_tables.h b/core/fpdfapi/parser/cpdf_hint_tables.h +index e3f280f23..072d6e6e6 100644 +--- a/core/fpdfapi/parser/cpdf_hint_tables.h ++++ b/core/fpdfapi/parser/cpdf_hint_tables.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -70,10 +70,10 @@ class CPDF_HintTables { + + static std::unique_ptr Parse( + CPDF_SyntaxParser* parser, +- CPDF_LinearizedHeader* pLinearized); ++ const CPDF_LinearizedHeader* pLinearized); + + CPDF_HintTables(CPDF_ReadValidator* pValidator, +- CPDF_LinearizedHeader* pLinearized); ++ const CPDF_LinearizedHeader* pLinearized); + virtual ~CPDF_HintTables(); + + bool GetPagePos(uint32_t index, +@@ -99,17 +99,12 @@ class CPDF_HintTables { + private: + FX_FILESIZE HintsOffsetToFileOffset(uint32_t hints_offset) const; + +- // Owned by |m_pDataAvail|. +- UnownedPtr m_pValidator; +- +- // Owned by |m_pDataAvail|. +- UnownedPtr const m_pLinearized; +- +- uint32_t m_nFirstPageSharedObjs; +- FX_FILESIZE m_szFirstPageObjOffset; +- ++ uint32_t m_nFirstPageSharedObjs = 0; ++ FX_FILESIZE m_szFirstPageObjOffset = 0; + std::vector m_PageInfos; + std::vector m_SharedObjGroupInfos; ++ UnownedPtr m_pValidator; ++ UnownedPtr const m_pLinearized; + }; + + #endif // CORE_FPDFAPI_PARSER_CPDF_HINT_TABLES_H_ +diff --git a/core/fpdfapi/parser/cpdf_hint_tables_unittest.cpp b/core/fpdfapi/parser/cpdf_hint_tables_unittest.cpp +index 27592ba23..c61afa1f2 100644 +--- a/core/fpdfapi/parser/cpdf_hint_tables_unittest.cpp ++++ b/core/fpdfapi/parser/cpdf_hint_tables_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,7 +8,7 @@ + #include + #include + +-#include "core/fpdfapi/page/cpdf_pagemodule.h" ++#include "core/fpdfapi/page/test_with_page_module.h" + #include "core/fpdfapi/parser/cpdf_data_avail.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_linearized_header.h" +@@ -16,12 +16,13 @@ + #include "core/fpdfapi/parser/cpdf_read_validator.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_syntax_parser.h" +-#include "core/fxcrt/cfx_readonlymemorystream.h" ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/cfx_read_only_string_stream.h" + #include "core/fxcrt/fx_stream.h" + #include "testing/gmock/include/gmock/gmock.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/utils/path_service.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -29,15 +30,15 @@ RetainPtr MakeValidatorFromFile( + const std::string& file_name) { + std::string file_path; + PathService::GetTestFilePath(file_name, &file_path); +- ASSERT(!file_path.empty()); ++ DCHECK(!file_path.empty()); + return pdfium::MakeRetain( + IFX_SeekableReadStream::CreateFromFilename(file_path.c_str()), nullptr); + } + + std::unique_ptr MakeDataAvailFromFile( + const std::string& file_name) { +- return pdfium::MakeUnique( +- nullptr, MakeValidatorFromFile(file_name), true); ++ return std::make_unique(nullptr, ++ MakeValidatorFromFile(file_name)); + } + + class TestLinearizedHeader final : public CPDF_LinearizedHeader { +@@ -47,36 +48,28 @@ class TestLinearizedHeader final : public CPDF_LinearizedHeader { + : CPDF_LinearizedHeader(pDict, szLastXRefOffset) {} + + static std::unique_ptr MakeHeader( +- const std::string& inline_data) { +- CPDF_SyntaxParser parser(pdfium::MakeRetain( +- pdfium::as_bytes(pdfium::make_span(inline_data)))); ++ ByteString inline_data) { ++ CPDF_SyntaxParser parser( ++ pdfium::MakeRetain(std::move(inline_data))); + RetainPtr dict = + ToDictionary(parser.GetObjectBody(nullptr)); +- ASSERT(dict); +- return pdfium::MakeUnique(dict.Get(), 0); ++ DCHECK(dict); ++ return std::make_unique(dict.Get(), 0); + } + }; + + } // namespace + +-class CPDF_HintTablesTest : public testing::Test { +- public: +- CPDF_HintTablesTest() { +- // Needs for encoding Hint table stream. +- CPDF_PageModule::Create(); +- } +- +- ~CPDF_HintTablesTest() override { CPDF_PageModule::Destroy(); } +-}; ++// Needs page module for encoding Hint table stream. ++using HintTablesTest = TestWithPageModule; + +-TEST_F(CPDF_HintTablesTest, Load) { ++TEST_F(HintTablesTest, Load) { + auto data_avail = MakeDataAvailFromFile("feature_linearized_loading.pdf"); +- ASSERT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, +- data_avail->IsDocAvail(nullptr)); ++ ASSERT_EQ(CPDF_DataAvail::kDataAvailable, data_avail->IsDocAvail(nullptr)); + +- ASSERT_TRUE(data_avail->GetHintTables()); ++ ASSERT_TRUE(data_avail->GetHintTablesForTest()); + +- const CPDF_HintTables* hint_tables = data_avail->GetHintTables(); ++ const CPDF_HintTables* hint_tables = data_avail->GetHintTablesForTest(); + FX_FILESIZE page_start = 0; + FX_FILESIZE page_length = 0; + uint32_t page_obj_num = 0; +@@ -97,12 +90,11 @@ TEST_F(CPDF_HintTablesTest, Load) { + hint_tables->GetPagePos(2, &page_start, &page_length, &page_obj_num)); + } + +-TEST_F(CPDF_HintTablesTest, PageAndGroupInfos) { ++TEST_F(HintTablesTest, PageAndGroupInfos) { + auto data_avail = MakeDataAvailFromFile("feature_linearized_loading.pdf"); +- ASSERT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, +- data_avail->IsDocAvail(nullptr)); ++ ASSERT_EQ(CPDF_DataAvail::kDataAvailable, data_avail->IsDocAvail(nullptr)); + +- const CPDF_HintTables* hint_tables = data_avail->GetHintTables(); ++ const CPDF_HintTables* hint_tables = data_avail->GetHintTablesForTest(); + ASSERT_TRUE(hint_tables); + ASSERT_EQ(2u, hint_tables->PageInfos().size()); + +@@ -159,7 +151,7 @@ TEST_F(CPDF_HintTablesTest, PageAndGroupInfos) { + EXPECT_EQ(1u, hint_tables->SharedGroupInfos()[5].m_dwObjectsCount); + } + +-TEST_F(CPDF_HintTablesTest, FirstPageOffset) { ++TEST_F(HintTablesTest, FirstPageOffset) { + // Test that valid hint table is loaded, and have correct offset of first page + // object. + const auto linearized_header = TestLinearizedHeader::MakeHeader( +@@ -172,8 +164,8 @@ TEST_F(CPDF_HintTablesTest, FirstPageOffset) { + CPDF_SyntaxParser parser(validator, 0); + RetainPtr stream = ToStream(parser.GetObjectBody(nullptr)); + ASSERT_TRUE(stream); +- auto hint_tables = pdfium::MakeUnique( +- validator.Get(), linearized_header.get()); ++ auto hint_tables = std::make_unique(validator.Get(), ++ linearized_header.get()); + // Check that hint table will load. + ASSERT_TRUE(hint_tables->LoadHintStream(stream.Get())); + // Check that hint table have correct first page offset. +diff --git a/core/fpdfapi/parser/cpdf_indirect_object_holder.cpp b/core/fpdfapi/parser/cpdf_indirect_object_holder.cpp +index b2e1b5441..d4ff4013d 100644 +--- a/core/fpdfapi/parser/cpdf_indirect_object_holder.cpp ++++ b/core/fpdfapi/parser/cpdf_indirect_object_holder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,46 +7,64 @@ + #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h" + + #include ++#include + #include + + #include "core/fpdfapi/parser/cpdf_object.h" + #include "core/fpdfapi/parser/cpdf_parser.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + namespace { + +-CPDF_Object* FilterInvalidObjNum(CPDF_Object* obj) { ++const CPDF_Object* FilterInvalidObjNum(const CPDF_Object* obj) { + return obj && obj->GetObjNum() != CPDF_Object::kInvalidObjNum ? obj : nullptr; + } + + } // namespace + + CPDF_IndirectObjectHolder::CPDF_IndirectObjectHolder() +- : m_LastObjNum(0), +- m_pByteStringPool(pdfium::MakeUnique()) {} ++ : m_pByteStringPool(std::make_unique()) {} + + CPDF_IndirectObjectHolder::~CPDF_IndirectObjectHolder() { + m_pByteStringPool.DeleteObject(); // Make weak. + } + +-CPDF_Object* CPDF_IndirectObjectHolder::GetIndirectObject( ++RetainPtr CPDF_IndirectObjectHolder::GetIndirectObject( ++ uint32_t objnum) const { ++ return pdfium::WrapRetain(GetIndirectObjectInternal(objnum)); ++} ++ ++RetainPtr CPDF_IndirectObjectHolder::GetMutableIndirectObject( ++ uint32_t objnum) { ++ return pdfium::WrapRetain( ++ const_cast(GetIndirectObjectInternal(objnum))); ++} ++ ++const CPDF_Object* CPDF_IndirectObjectHolder::GetIndirectObjectInternal( + uint32_t objnum) const { + auto it = m_IndirectObjs.find(objnum); +- return (it != m_IndirectObjs.end()) ? FilterInvalidObjNum(it->second.Get()) +- : nullptr; ++ if (it == m_IndirectObjs.end()) ++ return nullptr; ++ ++ return FilterInvalidObjNum(it->second.Get()); + } + +-CPDF_Object* CPDF_IndirectObjectHolder::GetOrParseIndirectObject( ++RetainPtr CPDF_IndirectObjectHolder::GetOrParseIndirectObject( ++ uint32_t objnum) { ++ return pdfium::WrapRetain(GetOrParseIndirectObjectInternal(objnum)); ++} ++ ++CPDF_Object* CPDF_IndirectObjectHolder::GetOrParseIndirectObjectInternal( + uint32_t objnum) { + if (objnum == 0 || objnum == CPDF_Object::kInvalidObjNum) + return nullptr; + + // Add item anyway to prevent recursively parsing of same object. + auto insert_result = m_IndirectObjs.insert(std::make_pair(objnum, nullptr)); +- if (!insert_result.second) +- return FilterInvalidObjNum(insert_result.first->second.Get()); +- ++ if (!insert_result.second) { ++ return const_cast( ++ FilterInvalidObjNum(insert_result.first->second.Get())); ++ } + RetainPtr pNewObj = ParseIndirectObject(objnum); + if (!pNewObj) { + m_IndirectObjs.erase(insert_result.first); +@@ -55,8 +73,10 @@ CPDF_Object* CPDF_IndirectObjectHolder::GetOrParseIndirectObject( + + pNewObj->SetObjNum(objnum); + m_LastObjNum = std::max(m_LastObjNum, objnum); ++ ++ CPDF_Object* result = pNewObj.Get(); + insert_result.first->second = std::move(pNewObj); +- return insert_result.first->second.Get(); ++ return result; + } + + RetainPtr CPDF_IndirectObjectHolder::ParseIndirectObject( +@@ -64,20 +84,18 @@ RetainPtr CPDF_IndirectObjectHolder::ParseIndirectObject( + return nullptr; + } + +-CPDF_Object* CPDF_IndirectObjectHolder::AddIndirectObject( ++uint32_t CPDF_IndirectObjectHolder::AddIndirectObject( + RetainPtr pObj) { + CHECK(!pObj->GetObjNum()); + pObj->SetObjNum(++m_LastObjNum); +- +- auto& obj_holder = m_IndirectObjs[m_LastObjNum]; +- obj_holder = std::move(pObj); +- return obj_holder.Get(); ++ m_IndirectObjs[m_LastObjNum] = std::move(pObj); ++ return m_LastObjNum; + } + + bool CPDF_IndirectObjectHolder::ReplaceIndirectObjectIfHigherGeneration( + uint32_t objnum, + RetainPtr pObj) { +- ASSERT(objnum); ++ DCHECK(objnum); + if (!pObj || objnum == CPDF_Object::kInvalidObjNum) + return false; + +diff --git a/core/fpdfapi/parser/cpdf_indirect_object_holder.h b/core/fpdfapi/parser/cpdf_indirect_object_holder.h +index 1887cc886..cdf0bc852 100644 +--- a/core/fpdfapi/parser/cpdf_indirect_object_holder.h ++++ b/core/fpdfapi/parser/cpdf_indirect_object_holder.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,17 +7,16 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_INDIRECT_OBJECT_HOLDER_H_ + #define CORE_FPDFAPI_PARSER_CPDF_INDIRECT_OBJECT_HOLDER_H_ + ++#include ++ + #include +-#include + #include + #include +-#include + + #include "core/fpdfapi/parser/cpdf_object.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/string_pool_template.h" + #include "core/fxcrt/weak_ptr.h" +-#include "third_party/base/ptr_util.h" + + class CPDF_IndirectObjectHolder { + public: +@@ -27,39 +26,39 @@ class CPDF_IndirectObjectHolder { + CPDF_IndirectObjectHolder(); + virtual ~CPDF_IndirectObjectHolder(); + +- CPDF_Object* GetIndirectObject(uint32_t objnum) const; +- virtual CPDF_Object* GetOrParseIndirectObject(uint32_t objnum); ++ RetainPtr GetOrParseIndirectObject(uint32_t objnum); ++ RetainPtr GetIndirectObject(uint32_t objnum) const; ++ RetainPtr GetMutableIndirectObject(uint32_t objnum); + void DeleteIndirectObject(uint32_t objnum); + +- // Creates and adds a new object owned by the indirect object holder, +- // and returns an unowned pointer to it. We have a special case to +- // handle objects that can intern strings from our ByteStringPool. ++ // Creates and adds a new object retained by the indirect object holder, ++ // and returns a retained pointer to it. + template +- typename std::enable_if::value, T*>::type NewIndirect( +- Args&&... args) { +- return static_cast( +- AddIndirectObject(pdfium::MakeRetain(std::forward(args)...))); +- } +- template +- typename std::enable_if::value, T*>::type NewIndirect( +- Args&&... args) { +- return static_cast(AddIndirectObject( +- pdfium::MakeRetain(m_pByteStringPool, std::forward(args)...))); ++ RetainPtr NewIndirect(Args&&... args) { ++ auto obj = New(std::forward(args)...); ++ AddIndirectObject(obj); ++ return obj; + } + +- // Creates and adds a new object not owned by the indirect object holder, +- // but which can intern strings from it. ++ // Creates and adds a new object not retained by the indirect object holder, ++ // but which can intern strings from it. We have a special cast to handle ++ // objects that can intern strings from our ByteStringPool. + template + typename std::enable_if::value, RetainPtr>::type New( + Args&&... args) { + return pdfium::MakeRetain(m_pByteStringPool, + std::forward(args)...); + } ++ template ++ typename std::enable_if::value, RetainPtr>::type New( ++ Args&&... args) { ++ return pdfium::MakeRetain(std::forward(args)...); ++ } + +- // Takes ownership of |pObj|, returns unowned pointer to it. +- CPDF_Object* AddIndirectObject(RetainPtr pObj); ++ // Always Retains |pObj|, returns its new object number. ++ uint32_t AddIndirectObject(RetainPtr pObj); + +- // Always takes ownership of |pObj|, return true if higher generation number. ++ // If higher generation number, retains |pObj| and returns true. + bool ReplaceIndirectObjectIfHigherGeneration(uint32_t objnum, + RetainPtr pObj); + +@@ -77,7 +76,12 @@ class CPDF_IndirectObjectHolder { + virtual RetainPtr ParseIndirectObject(uint32_t objnum); + + private: +- uint32_t m_LastObjNum; ++ friend class CPDF_Reference; ++ ++ const CPDF_Object* GetIndirectObjectInternal(uint32_t objnum) const; ++ CPDF_Object* GetOrParseIndirectObjectInternal(uint32_t objnum); ++ ++ uint32_t m_LastObjNum = 0; + std::map> m_IndirectObjs; + WeakPtr m_pByteStringPool; + }; +diff --git a/core/fpdfapi/parser/cpdf_indirect_object_holder_unittest.cpp b/core/fpdfapi/parser/cpdf_indirect_object_holder_unittest.cpp +index 5494855a2..94e9b38f9 100644 +--- a/core/fpdfapi/parser/cpdf_indirect_object_holder_unittest.cpp ++++ b/core/fpdfapi/parser/cpdf_indirect_object_holder_unittest.cpp +@@ -1,37 +1,36 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h" + +-#include +-#include +- ++#include "core/fpdfapi/parser/cpdf_array.h" ++#include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_null.h" + #include "testing/gmock/include/gmock/gmock.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + namespace { + + class MockIndirectObjectHolder final : public CPDF_IndirectObjectHolder { + public: +- MockIndirectObjectHolder() {} +- ~MockIndirectObjectHolder() override {} ++ MockIndirectObjectHolder() = default; ++ ~MockIndirectObjectHolder() override = default; + + MOCK_METHOD1(ParseIndirectObject, RetainPtr(uint32_t objnum)); + }; + + } // namespace + +-TEST(CPDF_IndirectObjectHolderTest, RecursiveParseOfSameObject) { ++TEST(IndirectObjectHolderTest, RecursiveParseOfSameObject) { + MockIndirectObjectHolder mock_holder; + // ParseIndirectObject should not be called again on recursively same object + // parse request. + EXPECT_CALL(mock_holder, ParseIndirectObject(::testing::_)) + .WillOnce(::testing::WithArg<0>(::testing::Invoke( + [&mock_holder](uint32_t objnum) -> RetainPtr { +- const CPDF_Object* same_parse = ++ RetainPtr same_parse = + mock_holder.GetOrParseIndirectObject(objnum); + CHECK(!same_parse); + return pdfium::MakeRetain(); +@@ -40,7 +39,7 @@ TEST(CPDF_IndirectObjectHolderTest, RecursiveParseOfSameObject) { + EXPECT_TRUE(mock_holder.GetOrParseIndirectObject(1000)); + } + +-TEST(CPDF_IndirectObjectHolderTest, GetObjectMethods) { ++TEST(IndirectObjectHolderTest, GetObjectMethods) { + static constexpr uint32_t kObjNum = 1000; + MockIndirectObjectHolder mock_holder; + +@@ -63,7 +62,7 @@ TEST(CPDF_IndirectObjectHolderTest, GetObjectMethods) { + EXPECT_EQ(kObjNum, mock_holder.GetIndirectObject(kObjNum)->GetObjNum()); + } + +-TEST(CPDF_IndirectObjectHolderTest, ParseInvalidObjNum) { ++TEST(IndirectObjectHolderTest, ParseInvalidObjNum) { + MockIndirectObjectHolder mock_holder; + + EXPECT_CALL(mock_holder, ParseIndirectObject(::testing::_)).Times(0); +@@ -71,10 +70,23 @@ TEST(CPDF_IndirectObjectHolderTest, ParseInvalidObjNum) { + mock_holder.GetOrParseIndirectObject(CPDF_Object::kInvalidObjNum)); + } + +-TEST(CPDF_IndirectObjectHolderTest, ReplaceObjectWithInvalidObjNum) { ++TEST(IndirectObjectHolderTest, ReplaceObjectWithInvalidObjNum) { + MockIndirectObjectHolder mock_holder; + + EXPECT_CALL(mock_holder, ParseIndirectObject(::testing::_)).Times(0); + EXPECT_FALSE(mock_holder.ReplaceIndirectObjectIfHigherGeneration( + CPDF_Object::kInvalidObjNum, pdfium::MakeRetain())); + } ++ ++TEST(IndirectObjectHolderTest, TemplateNewMethods) { ++ MockIndirectObjectHolder mock_holder; ++ ++ auto pDict = mock_holder.NewIndirect(); ++ auto pArray = mock_holder.NewIndirect(); ++ mock_holder.DeleteIndirectObject(pDict->GetObjNum()); ++ mock_holder.DeleteIndirectObject(pArray->GetObjNum()); ++ ++ // No longer UAF since NewIndirect<> returns retained objects. ++ EXPECT_TRUE(pDict->IsDictionary()); ++ EXPECT_TRUE(pArray->IsArray()); ++} +diff --git a/core/fpdfapi/parser/cpdf_linearized_header.cpp b/core/fpdfapi/parser/cpdf_linearized_header.cpp +index c7dc54ac5..f4de7d7a2 100644 +--- a/core/fpdfapi/parser/cpdf_linearized_header.cpp ++++ b/core/fpdfapi/parser/cpdf_linearized_header.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,8 +13,10 @@ + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_number.h" ++#include "core/fpdfapi/parser/cpdf_parser.h" + #include "core/fpdfapi/parser/cpdf_syntax_parser.h" + #include "core/fxcrt/fx_safe_types.h" ++#include "third_party/base/check.h" + #include "third_party/base/ptr_util.h" + + namespace { +@@ -24,12 +26,12 @@ constexpr size_t kMaxInt = static_cast(std::numeric_limits::max()); + + template + bool IsValidNumericDictionaryValue(const CPDF_Dictionary* pDict, +- const char* key, ++ const ByteString& key, + T min_value, + bool must_exist = true) { + if (!pDict->KeyExist(key)) + return !must_exist; +- const CPDF_Number* pNum = ToNumber(pDict->GetObjectFor(key)); ++ RetainPtr pNum = pDict->GetNumberFor(key); + if (!pNum || !pNum->IsInteger()) + return false; + const int raw_value = pNum->GetInteger(); +@@ -40,12 +42,13 @@ bool IsValidNumericDictionaryValue(const CPDF_Dictionary* pDict, + + bool IsLinearizedHeaderValid(const CPDF_LinearizedHeader* header, + FX_FILESIZE document_size) { +- ASSERT(header); ++ DCHECK(header); + return header->GetFileSize() == document_size && + header->GetFirstPageNo() < kMaxInt && + header->GetFirstPageNo() < header->GetPageCount() && + header->GetMainXRefTableFirstEntryOffset() < document_size && + header->GetFirstPageEndOffset() < document_size && ++ header->GetFirstPageObjNum() < CPDF_Parser::kMaxObjectNumber && + header->GetLastXRefOffset() < document_size && + header->GetHintStart() < document_size; + } +@@ -71,7 +74,7 @@ std::unique_ptr CPDF_LinearizedHeader::Parse( + } + // Move parser to the start of the xref table for the documents first page. + // (skpping endobj keyword) +- if (parser->GetNextWord(nullptr) != "endobj") ++ if (parser->GetNextWord().word != "endobj") + return nullptr; + + auto result = pdfium::WrapUnique( +@@ -92,7 +95,7 @@ CPDF_LinearizedHeader::CPDF_LinearizedHeader(const CPDF_Dictionary* pDict, + m_szFirstPageEndOffset(pDict->GetIntegerFor("E")), + m_FirstPageObjNum(pDict->GetIntegerFor("O")), + m_szLastXRefOffset(szLastXRefOffset) { +- const CPDF_Array* pHintStreamRange = pDict->GetArrayFor("H"); ++ RetainPtr pHintStreamRange = pDict->GetArrayFor("H"); + const size_t nHintStreamSize = + pHintStreamRange ? pHintStreamRange->size() : 0; + if (nHintStreamSize == 2 || nHintStreamSize == 4) { +@@ -103,7 +106,7 @@ CPDF_LinearizedHeader::CPDF_LinearizedHeader(const CPDF_Dictionary* pDict, + } + } + +-CPDF_LinearizedHeader::~CPDF_LinearizedHeader() {} ++CPDF_LinearizedHeader::~CPDF_LinearizedHeader() = default; + + bool CPDF_LinearizedHeader::HasHintTable() const { + return GetPageCount() > 1 && GetHintStart() > 0 && GetHintLength() > 0; +diff --git a/core/fpdfapi/parser/cpdf_linearized_header.h b/core/fpdfapi/parser/cpdf_linearized_header.h +index 44e042285..10e2453bd 100644 +--- a/core/fpdfapi/parser/cpdf_linearized_header.h ++++ b/core/fpdfapi/parser/cpdf_linearized_header.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,13 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_LINEARIZED_HEADER_H_ + #define CORE_FPDFAPI_PARSER_CPDF_LINEARIZED_HEADER_H_ + ++#include ++ + #include + +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/fx_types.h" + + class CPDF_Dictionary; +-class CPDF_Object; + class CPDF_SyntaxParser; + + class CPDF_LinearizedHeader { +@@ -31,7 +32,7 @@ class CPDF_LinearizedHeader { + uint32_t GetPageCount() const { return m_PageCount; } + // Will only return values > 0. + FX_FILESIZE GetFirstPageEndOffset() const { return m_szFirstPageEndOffset; } +- // Will only return values > 0. ++ // Will only return values in the range [1, `CPDF_Parser::kMaxObjectNumber`). + uint32_t GetFirstPageObjNum() const { return m_FirstPageObjNum; } + // Will only return values > 0. + FX_FILESIZE GetLastXRefOffset() const { return m_szLastXRefOffset; } +diff --git a/core/fpdfapi/parser/cpdf_name.cpp b/core/fpdfapi/parser/cpdf_name.cpp +index 9f3f49fbd..6236ef456 100644 +--- a/core/fpdfapi/parser/cpdf_name.cpp ++++ b/core/fpdfapi/parser/cpdf_name.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,7 +9,6 @@ + #include "core/fpdfapi/parser/fpdf_parser_decode.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fxcrt/fx_stream.h" +-#include "third_party/base/ptr_util.h" + + CPDF_Name::CPDF_Name(WeakPtr pPool, const ByteString& str) + : m_Name(str) { +@@ -17,7 +16,7 @@ CPDF_Name::CPDF_Name(WeakPtr pPool, const ByteString& str) + m_Name = pPool->Intern(m_Name); + } + +-CPDF_Name::~CPDF_Name() {} ++CPDF_Name::~CPDF_Name() = default; + + CPDF_Object::Type CPDF_Name::GetType() const { + return kName; +@@ -35,15 +34,7 @@ void CPDF_Name::SetString(const ByteString& str) { + m_Name = str; + } + +-bool CPDF_Name::IsName() const { +- return true; +-} +- +-CPDF_Name* CPDF_Name::AsName() { +- return this; +-} +- +-const CPDF_Name* CPDF_Name::AsName() const { ++CPDF_Name* CPDF_Name::AsMutableName() { + return this; + } + +@@ -53,6 +44,9 @@ WideString CPDF_Name::GetUnicodeText() const { + + bool CPDF_Name::WriteTo(IFX_ArchiveStream* archive, + const CPDF_Encryptor* encryptor) const { +- return archive->WriteString("/") && +- archive->WriteString(PDF_NameEncode(GetString()).AsStringView()); ++ if (!archive->WriteString("/")) ++ return false; ++ ++ const ByteString name = PDF_NameEncode(GetString()); ++ return name.IsEmpty() || archive->WriteString(name.AsStringView()); + } +diff --git a/core/fpdfapi/parser/cpdf_name.h b/core/fpdfapi/parser/cpdf_name.h +index cfd90bb8e..c3c023f1c 100644 +--- a/core/fpdfapi/parser/cpdf_name.h ++++ b/core/fpdfapi/parser/cpdf_name.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,16 +7,14 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_NAME_H_ + #define CORE_FPDFAPI_PARSER_CPDF_NAME_H_ + +-#include +- + #include "core/fpdfapi/parser/cpdf_object.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/string_pool_template.h" + #include "core/fxcrt/weak_ptr.h" + + class CPDF_Name final : public CPDF_Object { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // CPDF_Object: + Type GetType() const override; +@@ -24,9 +22,7 @@ class CPDF_Name final : public CPDF_Object { + ByteString GetString() const override; + WideString GetUnicodeText() const override; + void SetString(const ByteString& str) override; +- bool IsName() const override; +- CPDF_Name* AsName() override; +- const CPDF_Name* AsName() const override; ++ CPDF_Name* AsMutableName() override; + bool WriteTo(IFX_ArchiveStream* archive, + const CPDF_Encryptor* encryptor) const override; + +@@ -38,11 +34,19 @@ class CPDF_Name final : public CPDF_Object { + }; + + inline CPDF_Name* ToName(CPDF_Object* obj) { +- return obj ? obj->AsName() : nullptr; ++ return obj ? obj->AsMutableName() : nullptr; + } + + inline const CPDF_Name* ToName(const CPDF_Object* obj) { + return obj ? obj->AsName() : nullptr; + } + ++inline RetainPtr ToName(RetainPtr obj) { ++ return RetainPtr(ToName(obj.Get())); ++} ++ ++inline RetainPtr ToName(RetainPtr obj) { ++ return RetainPtr(ToName(obj.Get())); ++} ++ + #endif // CORE_FPDFAPI_PARSER_CPDF_NAME_H_ +diff --git a/core/fpdfapi/parser/cpdf_null.cpp b/core/fpdfapi/parser/cpdf_null.cpp +index 71299c1fd..44a6c4588 100644 +--- a/core/fpdfapi/parser/cpdf_null.cpp ++++ b/core/fpdfapi/parser/cpdf_null.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,9 +7,8 @@ + #include "core/fpdfapi/parser/cpdf_null.h" + + #include "core/fxcrt/fx_stream.h" +-#include "third_party/base/ptr_util.h" + +-CPDF_Null::CPDF_Null() {} ++CPDF_Null::CPDF_Null() = default; + + CPDF_Object::Type CPDF_Null::GetType() const { + return kNullobj; +@@ -19,11 +18,11 @@ RetainPtr CPDF_Null::Clone() const { + return pdfium::MakeRetain(); + } + ++CPDF_Null* CPDF_Null::AsMutableNull() { ++ return this; ++} ++ + bool CPDF_Null::WriteTo(IFX_ArchiveStream* archive, + const CPDF_Encryptor* encryptor) const { + return archive->WriteString(" null"); + } +- +-bool CPDF_Null::IsNull() const { +- return true; +-} +diff --git a/core/fpdfapi/parser/cpdf_null.h b/core/fpdfapi/parser/cpdf_null.h +index 767583b0c..3d5a237a0 100644 +--- a/core/fpdfapi/parser/cpdf_null.h ++++ b/core/fpdfapi/parser/cpdf_null.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,21 +7,19 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_NULL_H_ + #define CORE_FPDFAPI_PARSER_CPDF_NULL_H_ + +-#include +- + #include "core/fpdfapi/parser/cpdf_object.h" ++#include "core/fxcrt/retain_ptr.h" + + class CPDF_Null final : public CPDF_Object { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // CPDF_Object. + Type GetType() const override; + RetainPtr Clone() const override; ++ CPDF_Null* AsMutableNull() override; + bool WriteTo(IFX_ArchiveStream* archive, + const CPDF_Encryptor* encryptor) const override; +- bool IsNull() const override; + + private: + CPDF_Null(); +diff --git a/core/fpdfapi/parser/cpdf_number.cpp b/core/fpdfapi/parser/cpdf_number.cpp +index 24abf2001..6b9d78d75 100644 +--- a/core/fpdfapi/parser/cpdf_number.cpp ++++ b/core/fpdfapi/parser/cpdf_number.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,9 +7,8 @@ + #include "core/fpdfapi/parser/cpdf_number.h" + + #include "core/fxcrt/fx_stream.h" +-#include "third_party/base/ptr_util.h" + +-CPDF_Number::CPDF_Number() {} ++CPDF_Number::CPDF_Number() = default; + + CPDF_Number::CPDF_Number(int value) : m_Number(value) {} + +@@ -17,7 +16,7 @@ CPDF_Number::CPDF_Number(float value) : m_Number(value) {} + + CPDF_Number::CPDF_Number(ByteStringView str) : m_Number(str) {} + +-CPDF_Number::~CPDF_Number() {} ++CPDF_Number::~CPDF_Number() = default; + + CPDF_Object::Type CPDF_Number::GetType() const { + return kNumber; +@@ -37,15 +36,7 @@ int CPDF_Number::GetInteger() const { + return m_Number.GetSigned(); + } + +-bool CPDF_Number::IsNumber() const { +- return true; +-} +- +-CPDF_Number* CPDF_Number::AsNumber() { +- return this; +-} +- +-const CPDF_Number* CPDF_Number::AsNumber() const { ++CPDF_Number* CPDF_Number::AsMutableNumber() { + return this; + } + +diff --git a/core/fpdfapi/parser/cpdf_number.h b/core/fpdfapi/parser/cpdf_number.h +index dc7534076..93b0f9d8c 100644 +--- a/core/fpdfapi/parser/cpdf_number.h ++++ b/core/fpdfapi/parser/cpdf_number.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,17 +7,14 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_NUMBER_H_ + #define CORE_FPDFAPI_PARSER_CPDF_NUMBER_H_ + +-#include +- + #include "core/fpdfapi/parser/cpdf_object.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/fx_number.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/retain_ptr.h" + + class CPDF_Number final : public CPDF_Object { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // CPDF_Object: + Type GetType() const override; +@@ -26,9 +23,7 @@ class CPDF_Number final : public CPDF_Object { + float GetNumber() const override; + int GetInteger() const override; + void SetString(const ByteString& str) override; +- bool IsNumber() const override; +- CPDF_Number* AsNumber() override; +- const CPDF_Number* AsNumber() const override; ++ CPDF_Number* AsMutableNumber() override; + bool WriteTo(IFX_ArchiveStream* archive, + const CPDF_Encryptor* encryptor) const override; + +@@ -45,11 +40,19 @@ class CPDF_Number final : public CPDF_Object { + }; + + inline CPDF_Number* ToNumber(CPDF_Object* obj) { +- return obj ? obj->AsNumber() : nullptr; ++ return obj ? obj->AsMutableNumber() : nullptr; + } + + inline const CPDF_Number* ToNumber(const CPDF_Object* obj) { + return obj ? obj->AsNumber() : nullptr; + } + ++inline RetainPtr ToNumber(RetainPtr obj) { ++ return RetainPtr(ToNumber(obj.Get())); ++} ++ ++inline RetainPtr ToNumber(RetainPtr obj) { ++ return RetainPtr(ToNumber(obj.Get())); ++} ++ + #endif // CORE_FPDFAPI_PARSER_CPDF_NUMBER_H_ +diff --git a/core/fpdfapi/parser/cpdf_object.cpp b/core/fpdfapi/parser/cpdf_object.cpp +index b61ee7efb..3916392b4 100644 +--- a/core/fpdfapi/parser/cpdf_object.cpp ++++ b/core/fpdfapi/parser/cpdf_object.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,16 +14,33 @@ + #include "core/fpdfapi/parser/cpdf_parser.h" + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fxcrt/fx_string.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/notreached.h" + +-CPDF_Object::~CPDF_Object() {} ++CPDF_Object::~CPDF_Object() = default; + +-CPDF_Object* CPDF_Object::GetDirect() { +- return this; ++static_assert(sizeof(uint64_t) >= sizeof(CPDF_Object*), ++ "Need a bigger type for cache keys"); ++ ++static_assert(CPDF_Parser::kMaxObjectNumber < static_cast(1) << 31, ++ "Need a smaller kMaxObjNumber for cache keys"); ++ ++uint64_t CPDF_Object::KeyForCache() const { ++ if (IsInline()) ++ return (static_cast(1) << 63) | reinterpret_cast(this); ++ ++ return (static_cast(m_ObjNum) << 32) | ++ static_cast(m_GenNum); ++} ++ ++RetainPtr CPDF_Object::GetMutableDirect() { ++ return pdfium::WrapRetain(const_cast(GetDirectInternal())); ++} ++ ++RetainPtr CPDF_Object::GetDirect() const { ++ return pdfium::WrapRetain(GetDirectInternal()); + } + +-const CPDF_Object* CPDF_Object::GetDirect() const { ++const CPDF_Object* CPDF_Object::GetDirectInternal() const { + return this; + } + +@@ -58,11 +75,15 @@ int CPDF_Object::GetInteger() const { + return 0; + } + +-CPDF_Dictionary* CPDF_Object::GetDict() { +- return nullptr; ++RetainPtr CPDF_Object::GetDict() const { ++ return pdfium::WrapRetain(GetDictInternal()); ++} ++ ++RetainPtr CPDF_Object::GetMutableDict() { ++ return pdfium::WrapRetain(const_cast(GetDictInternal())); + } + +-const CPDF_Dictionary* CPDF_Object::GetDict() const { ++const CPDF_Dictionary* CPDF_Object::GetDictInternal() const { + return nullptr; + } + +@@ -70,107 +91,79 @@ void CPDF_Object::SetString(const ByteString& str) { + NOTREACHED(); + } + +-bool CPDF_Object::IsArray() const { +- return false; +-} +- +-bool CPDF_Object::IsBoolean() const { +- return false; +-} +- +-bool CPDF_Object::IsDictionary() const { +- return false; +-} +- +-bool CPDF_Object::IsName() const { +- return false; +-} +- +-bool CPDF_Object::IsNumber() const { +- return false; +-} +- +-bool CPDF_Object::IsReference() const { +- return false; +-} +- +-bool CPDF_Object::IsStream() const { +- return false; +-} +- +-bool CPDF_Object::IsString() const { +- return false; +-} +- +-bool CPDF_Object::IsNull() const { +- return false; +-} +- +-CPDF_Array* CPDF_Object::AsArray() { ++CPDF_Array* CPDF_Object::AsMutableArray() { + return nullptr; + } + + const CPDF_Array* CPDF_Object::AsArray() const { +- return nullptr; ++ return const_cast(this)->AsMutableArray(); + } + +-CPDF_Boolean* CPDF_Object::AsBoolean() { ++CPDF_Boolean* CPDF_Object::AsMutableBoolean() { + return nullptr; + } + + const CPDF_Boolean* CPDF_Object::AsBoolean() const { +- return nullptr; ++ return const_cast(this)->AsMutableBoolean(); + } + +-CPDF_Dictionary* CPDF_Object::AsDictionary() { ++CPDF_Dictionary* CPDF_Object::AsMutableDictionary() { + return nullptr; + } + + const CPDF_Dictionary* CPDF_Object::AsDictionary() const { +- return nullptr; ++ return const_cast(this)->AsMutableDictionary(); + } + +-CPDF_Name* CPDF_Object::AsName() { ++CPDF_Name* CPDF_Object::AsMutableName() { + return nullptr; + } + + const CPDF_Name* CPDF_Object::AsName() const { ++ return const_cast(this)->AsMutableName(); ++} ++ ++CPDF_Null* CPDF_Object::AsMutableNull() { + return nullptr; + } + +-CPDF_Number* CPDF_Object::AsNumber() { ++const CPDF_Null* CPDF_Object::AsNull() const { ++ return const_cast(this)->AsMutableNull(); ++} ++ ++CPDF_Number* CPDF_Object::AsMutableNumber() { + return nullptr; + } + + const CPDF_Number* CPDF_Object::AsNumber() const { +- return nullptr; ++ return const_cast(this)->AsMutableNumber(); + } + +-CPDF_Reference* CPDF_Object::AsReference() { ++CPDF_Reference* CPDF_Object::AsMutableReference() { + return nullptr; + } + + const CPDF_Reference* CPDF_Object::AsReference() const { +- return nullptr; ++ return const_cast(this)->AsMutableReference(); + } + +-CPDF_Stream* CPDF_Object::AsStream() { ++CPDF_Stream* CPDF_Object::AsMutableStream() { + return nullptr; + } + + const CPDF_Stream* CPDF_Object::AsStream() const { +- return nullptr; ++ return const_cast(this)->AsMutableStream(); + } + +-CPDF_String* CPDF_Object::AsString() { ++CPDF_String* CPDF_Object::AsMutableString() { + return nullptr; + } + + const CPDF_String* CPDF_Object::AsString() const { +- return nullptr; ++ return const_cast(this)->AsMutableString(); + } + +-RetainPtr CPDF_Object::MakeReference( ++RetainPtr CPDF_Object::MakeReference( + CPDF_IndirectObjectHolder* holder) const { + if (IsInline()) { + NOTREACHED(); +diff --git a/core/fpdfapi/parser/cpdf_object.h b/core/fpdfapi/parser/cpdf_object.h +index 77810caa9..4793b6160 100644 +--- a/core/fpdfapi/parser/cpdf_object.h ++++ b/core/fpdfapi/parser/cpdf_object.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,13 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_OBJECT_H_ + #define CORE_FPDFAPI_PARSER_CPDF_OBJECT_H_ + +-#include ++#include ++ + #include + #include + + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/retain_ptr.h" + + class CPDF_Array; + class CPDF_Boolean; +@@ -27,9 +28,28 @@ class CPDF_Stream; + class CPDF_String; + class IFX_ArchiveStream; + ++// ISO 32000-1:2008 defines PDF objects. When CPDF_Parser parses a PDF object, ++// it represents the PDF object using CPDF_Objects. Take this PDF object for ++// example: ++// ++// 4 0 obj << ++// /Type /Pages ++// /Count 1 ++// /Kids [9 0 R] ++// >> ++// ++// Multiple CPDF_Objects instances are necessary to represent this PDF object: ++// 1) A CPDF_Dictionary with object number 4 that contains 3 elements. ++// 2) A CPDF_Name for /Pages. ++// 3) A CPDF_Number for the count of 1. ++// 4) A CPDF_Array for [9 0 R], which contains 1 element. ++// 5) A CPDF_Reference that references object 9 0. ++// ++// CPDF_Object (1) has an object number of 4. All the other CPDF_Objects are ++// inline objects. CPDF_Object represent that by using an object number of 0. + class CPDF_Object : public Retainable { + public: +- static const uint32_t kInvalidObjNum = static_cast(-1); ++ static constexpr uint32_t kInvalidObjNum = static_cast(-1); + enum Type { + kBoolean = 1, + kNumber, +@@ -42,57 +62,38 @@ class CPDF_Object : public Retainable { + kReference + }; + +- virtual Type GetType() const = 0; + uint32_t GetObjNum() const { return m_ObjNum; } + void SetObjNum(uint32_t objnum) { m_ObjNum = objnum; } + uint32_t GetGenNum() const { return m_GenNum; } + void SetGenNum(uint32_t gennum) { m_GenNum = gennum; } + bool IsInline() const { return m_ObjNum == 0; } ++ uint64_t KeyForCache() const; ++ ++ virtual Type GetType() const = 0; + + // Create a deep copy of the object. + virtual RetainPtr Clone() const = 0; + + // Create a deep copy of the object except any reference object be + // copied to the object it points to directly. +- virtual RetainPtr CloneDirectObject() const; ++ RetainPtr CloneDirectObject() const; + +- virtual CPDF_Object* GetDirect(); +- virtual const CPDF_Object* GetDirect() const; + virtual ByteString GetString() const; + virtual WideString GetUnicodeText() const; + virtual float GetNumber() const; + virtual int GetInteger() const; +- virtual CPDF_Dictionary* GetDict(); +- virtual const CPDF_Dictionary* GetDict() const; + + virtual void SetString(const ByteString& str); + +- virtual bool IsArray() const; +- virtual bool IsBoolean() const; +- virtual bool IsDictionary() const; +- virtual bool IsName() const; +- virtual bool IsNumber() const; +- virtual bool IsReference() const; +- virtual bool IsStream() const; +- virtual bool IsString() const; +- virtual bool IsNull() const; +- +- virtual CPDF_Array* AsArray(); +- virtual const CPDF_Array* AsArray() const; +- virtual CPDF_Boolean* AsBoolean(); +- virtual const CPDF_Boolean* AsBoolean() const; +- virtual CPDF_Dictionary* AsDictionary(); +- virtual const CPDF_Dictionary* AsDictionary() const; +- virtual CPDF_Name* AsName(); +- virtual const CPDF_Name* AsName() const; +- virtual CPDF_Number* AsNumber(); +- virtual const CPDF_Number* AsNumber() const; +- virtual CPDF_Reference* AsReference(); +- virtual const CPDF_Reference* AsReference() const; +- virtual CPDF_Stream* AsStream(); +- virtual const CPDF_Stream* AsStream() const; +- virtual CPDF_String* AsString(); +- virtual const CPDF_String* AsString() const; ++ virtual CPDF_Array* AsMutableArray(); ++ virtual CPDF_Boolean* AsMutableBoolean(); ++ virtual CPDF_Dictionary* AsMutableDictionary(); ++ virtual CPDF_Name* AsMutableName(); ++ virtual CPDF_Null* AsMutableNull(); ++ virtual CPDF_Number* AsMutableNumber(); ++ virtual CPDF_Reference* AsMutableReference(); ++ virtual CPDF_Stream* AsMutableStream(); ++ virtual CPDF_String* AsMutableString(); + + virtual bool WriteTo(IFX_ArchiveStream* archive, + const CPDF_Encryptor* encryptor) const = 0; +@@ -109,14 +110,46 @@ class CPDF_Object : public Retainable { + + // Return a reference to itself. + // The object must be direct (!IsInlined). +- virtual RetainPtr MakeReference( ++ virtual RetainPtr MakeReference( + CPDF_IndirectObjectHolder* holder) const; + ++ RetainPtr GetDirect() const; // Wraps virtual method. ++ RetainPtr GetMutableDirect(); // Wraps virtual method. ++ RetainPtr GetDict() const; // Wraps virtual method. ++ RetainPtr GetMutableDict(); // Wraps virtual method. ++ ++ // Const methods wrapping non-const virtual As*() methods. ++ const CPDF_Array* AsArray() const; ++ const CPDF_Boolean* AsBoolean() const; ++ const CPDF_Dictionary* AsDictionary() const; ++ const CPDF_Name* AsName() const; ++ const CPDF_Null* AsNull() const; ++ const CPDF_Number* AsNumber() const; ++ const CPDF_Reference* AsReference() const; ++ const CPDF_Stream* AsStream() const; ++ const CPDF_String* AsString() const; ++ ++ // Type-testing methods merely wrap As*() methods. ++ bool IsArray() const { return !!AsArray(); } ++ bool IsBoolean() const { return !!AsBoolean(); } ++ bool IsDictionary() const { return !!AsDictionary(); } ++ bool IsName() const { return !!AsName(); } ++ bool IsNull() const { return !!AsNull(); } ++ bool IsNumber() const { return !!AsNumber(); } ++ bool IsReference() const { return !!AsReference(); } ++ bool IsStream() const { return !!AsStream(); } ++ bool IsString() const { return !!AsString(); } ++ + protected: ++ friend class CPDF_Dictionary; ++ friend class CPDF_Reference; ++ + CPDF_Object() = default; + CPDF_Object(const CPDF_Object& src) = delete; + ~CPDF_Object() override; + ++ virtual const CPDF_Object* GetDirectInternal() const; ++ virtual const CPDF_Dictionary* GetDictInternal() const; + RetainPtr CloneObjectNonCyclic(bool bDirect) const; + + uint32_t m_ObjNum = 0; +@@ -125,10 +158,10 @@ class CPDF_Object : public Retainable { + + template + struct CanInternStrings { +- static const bool value = std::is_same::value || +- std::is_same::value || +- std::is_same::value || +- std::is_same::value; ++ static constexpr bool value = std::is_same::value || ++ std::is_same::value || ++ std::is_same::value || ++ std::is_same::value; + }; + + #endif // CORE_FPDFAPI_PARSER_CPDF_OBJECT_H_ +diff --git a/core/fpdfapi/parser/cpdf_object_avail.cpp b/core/fpdfapi/parser/cpdf_object_avail.cpp +index 1dc5125a2..8bb347323 100644 +--- a/core/fpdfapi/parser/cpdf_object_avail.cpp ++++ b/core/fpdfapi/parser/cpdf_object_avail.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,42 +11,43 @@ + #include "core/fpdfapi/parser/cpdf_object_walker.h" + #include "core/fpdfapi/parser/cpdf_read_validator.h" + #include "core/fpdfapi/parser/cpdf_reference.h" +-#include "third_party/base/ptr_util.h" +- +-CPDF_ObjectAvail::CPDF_ObjectAvail( +- const RetainPtr& validator, +- CPDF_IndirectObjectHolder* holder, +- CPDF_Object* root) +- : validator_(validator), holder_(holder), root_(root) { +- ASSERT(validator_); +- ASSERT(holder); +- ASSERT(root_); ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" ++ ++CPDF_ObjectAvail::CPDF_ObjectAvail(RetainPtr validator, ++ CPDF_IndirectObjectHolder* holder, ++ RetainPtr root) ++ : validator_(std::move(validator)), ++ holder_(holder), ++ root_(std::move(root)) { ++ DCHECK(validator_); ++ DCHECK(holder); ++ DCHECK(root_); + if (!root_->IsInline()) + parsed_objnums_.insert(root_->GetObjNum()); + } + +-CPDF_ObjectAvail::CPDF_ObjectAvail( +- const RetainPtr& validator, +- CPDF_IndirectObjectHolder* holder, +- uint32_t obj_num) +- : validator_(validator), ++CPDF_ObjectAvail::CPDF_ObjectAvail(RetainPtr validator, ++ CPDF_IndirectObjectHolder* holder, ++ uint32_t obj_num) ++ : validator_(std::move(validator)), + holder_(holder), + root_(pdfium::MakeRetain(holder, obj_num)) { +- ASSERT(validator_); +- ASSERT(holder); ++ DCHECK(validator_); ++ DCHECK(holder); + } + +-CPDF_ObjectAvail::~CPDF_ObjectAvail() {} ++CPDF_ObjectAvail::~CPDF_ObjectAvail() = default; + + CPDF_DataAvail::DocAvailStatus CPDF_ObjectAvail::CheckAvail() { + if (!LoadRootObject()) +- return CPDF_DataAvail::DocAvailStatus::DataNotAvailable; ++ return CPDF_DataAvail::kDataNotAvailable; + + if (CheckObjects()) { + CleanMemory(); +- return CPDF_DataAvail::DocAvailStatus::DataAvailable; ++ return CPDF_DataAvail::kDataAvailable; + } +- return CPDF_DataAvail::DocAvailStatus::DataNotAvailable; ++ return CPDF_DataAvail::kDataNotAvailable; + } + + bool CPDF_ObjectAvail::LoadRootObject() { +@@ -60,16 +61,17 @@ bool CPDF_ObjectAvail::LoadRootObject() { + return true; + } + +- const CPDF_ReadValidator::Session parse_session(validator_); +- CPDF_Object* direct = holder_->GetOrParseIndirectObject(ref_obj_num); ++ CPDF_ReadValidator::ScopedSession parse_session(validator_); ++ RetainPtr direct = ++ holder_->GetOrParseIndirectObject(ref_obj_num); + if (validator_->has_read_problems()) + return false; + + parsed_objnums_.insert(ref_obj_num); +- root_.Reset(direct); ++ root_ = std::move(direct); + } + std::stack non_parsed_objects_in_root; +- if (AppendObjectSubRefs(root_.Get(), &non_parsed_objects_in_root)) { ++ if (AppendObjectSubRefs(root_, &non_parsed_objects_in_root)) { + non_parsed_objects_ = std::move(non_parsed_objects_in_root); + return true; + } +@@ -90,13 +92,14 @@ bool CPDF_ObjectAvail::CheckObjects() { + if (!checked_objects.insert(obj_num).second) + continue; + +- const CPDF_ReadValidator::Session parse_session(validator_); +- const CPDF_Object* direct = holder_->GetOrParseIndirectObject(obj_num); ++ CPDF_ReadValidator::ScopedSession parse_session(validator_); ++ RetainPtr direct = ++ holder_->GetOrParseIndirectObject(obj_num); + if (direct == root_) + continue; + + if (validator_->has_read_problems() || +- !AppendObjectSubRefs(direct, &objects_to_check)) { ++ !AppendObjectSubRefs(std::move(direct), &objects_to_check)) { + non_parsed_objects_.push(obj_num); + continue; + } +@@ -105,21 +108,21 @@ bool CPDF_ObjectAvail::CheckObjects() { + return non_parsed_objects_.empty(); + } + +-bool CPDF_ObjectAvail::AppendObjectSubRefs(const CPDF_Object* object, ++bool CPDF_ObjectAvail::AppendObjectSubRefs(RetainPtr object, + std::stack* refs) const { +- ASSERT(refs); ++ DCHECK(refs); + if (!object) + return true; + +- CPDF_ObjectWalker walker(object); +- while (const CPDF_Object* obj = walker.GetNext()) { +- const CPDF_ReadValidator::Session parse_session(validator_); ++ CPDF_ObjectWalker walker(std::move(object)); ++ while (RetainPtr obj = walker.GetNext()) { ++ CPDF_ReadValidator::ScopedSession parse_session(validator_); + + // Skip if this object if it's an inlined root, the parent object or + // explicitily excluded. + const bool skip = (walker.GetParent() && obj == root_) || + walker.dictionary_key() == "Parent" || +- (obj != root_ && ExcludeObject(obj)); ++ (obj != root_ && ExcludeObject(obj.Get())); + + // We need to parse the object before we can do the exclusion check. + // This is because the exclusion check may check against a referenced +@@ -148,5 +151,5 @@ bool CPDF_ObjectAvail::ExcludeObject(const CPDF_Object* object) const { + } + + bool CPDF_ObjectAvail::HasObjectParsed(uint32_t obj_num) const { +- return parsed_objnums_.count(obj_num) > 0; ++ return pdfium::Contains(parsed_objnums_, obj_num); + } +diff --git a/core/fpdfapi/parser/cpdf_object_avail.h b/core/fpdfapi/parser/cpdf_object_avail.h +index 901fb17cd..df7360d45 100644 +--- a/core/fpdfapi/parser/cpdf_object_avail.h ++++ b/core/fpdfapi/parser/cpdf_object_avail.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,17 +13,16 @@ + #include "core/fxcrt/unowned_ptr.h" + + class CPDF_Object; +-class CPDF_Reference; + class CPDF_IndirectObjectHolder; + class CPDF_ReadValidator; + + // Helper for check availability of object tree. + class CPDF_ObjectAvail { + public: +- CPDF_ObjectAvail(const RetainPtr& validator, ++ CPDF_ObjectAvail(RetainPtr validator, + CPDF_IndirectObjectHolder* holder, +- CPDF_Object* root); +- CPDF_ObjectAvail(const RetainPtr& validator, ++ RetainPtr root); ++ CPDF_ObjectAvail(RetainPtr validator, + CPDF_IndirectObjectHolder* holder, + uint32_t obj_num); + virtual ~CPDF_ObjectAvail(); +@@ -36,14 +35,14 @@ class CPDF_ObjectAvail { + private: + bool LoadRootObject(); + bool CheckObjects(); +- bool AppendObjectSubRefs(const CPDF_Object* object, ++ bool AppendObjectSubRefs(RetainPtr object, + std::stack* refs) const; + void CleanMemory(); + bool HasObjectParsed(uint32_t obj_num) const; + +- RetainPtr validator_; +- UnownedPtr holder_; +- RetainPtr root_; ++ RetainPtr const validator_; ++ UnownedPtr const holder_; ++ RetainPtr root_; + std::set parsed_objnums_; + std::stack non_parsed_objects_; + }; +diff --git a/core/fpdfapi/parser/cpdf_object_avail_unittest.cpp b/core/fpdfapi/parser/cpdf_object_avail_unittest.cpp +index f27ae63cd..97eb3d6e2 100644 +--- a/core/fpdfapi/parser/cpdf_object_avail_unittest.cpp ++++ b/core/fpdfapi/parser/cpdf_object_avail_unittest.cpp +@@ -1,11 +1,10 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/parser/cpdf_object_avail.h" + + #include +-#include + #include + + #include "core/fpdfapi/parser/cpdf_array.h" +@@ -17,22 +16,22 @@ + #include "core/fxcrt/fx_stream.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/invalid_seekable_read_stream.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/notreached.h" + + namespace { + + class TestReadValidator final : public CPDF_ReadValidator { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + +- void SimulateReadError() { ReadBlockAtOffset(nullptr, 0, 1); } ++ void SimulateReadError() { ReadBlockAtOffset({}, 0); } + + private: + TestReadValidator() + : CPDF_ReadValidator(pdfium::MakeRetain(100), + nullptr) {} +- ~TestReadValidator() override {} ++ ~TestReadValidator() override = default; + }; + + class TestHolder final : public CPDF_IndirectObjectHolder { +@@ -42,10 +41,10 @@ class TestHolder final : public CPDF_IndirectObjectHolder { + Available, + }; + TestHolder() : validator_(pdfium::MakeRetain()) {} +- ~TestHolder() override {} ++ ~TestHolder() override = default; + + // CPDF_IndirectObjectHolder overrides: +- CPDF_Object* GetOrParseIndirectObject(uint32_t objnum) override { ++ RetainPtr ParseIndirectObject(uint32_t objnum) override { + auto it = objects_data_.find(objnum); + if (it == objects_data_.end()) + return nullptr; +@@ -55,7 +54,7 @@ class TestHolder final : public CPDF_IndirectObjectHolder { + validator_->SimulateReadError(); + return nullptr; + } +- return obj_data.object.Get(); ++ return obj_data.object; + } + + RetainPtr GetValidator() { return validator_; } +@@ -66,13 +65,13 @@ class TestHolder final : public CPDF_IndirectObjectHolder { + ObjectData object_data; + object_data.object = std::move(object); + object_data.state = state; +- ASSERT(objects_data_.find(objnum) == objects_data_.end()); ++ DCHECK(objects_data_.find(objnum) == objects_data_.end()); + objects_data_[objnum] = std::move(object_data); + } + + void SetObjectState(uint32_t objnum, ObjectState state) { + auto it = objects_data_.find(objnum); +- ASSERT(it != objects_data_.end()); ++ DCHECK(it != objects_data_.end()); + ObjectData& obj_data = it->second; + obj_data.state = state; + } +@@ -96,7 +95,7 @@ class TestHolder final : public CPDF_IndirectObjectHolder { + class CPDF_ObjectAvailFailOnExclude final : public CPDF_ObjectAvail { + public: + using CPDF_ObjectAvail::CPDF_ObjectAvail; +- ~CPDF_ObjectAvailFailOnExclude() override {} ++ ~CPDF_ObjectAvailFailOnExclude() override = default; + bool ExcludeObject(const CPDF_Object* object) const override { + NOTREACHED(); + return false; +@@ -106,7 +105,7 @@ class CPDF_ObjectAvailFailOnExclude final : public CPDF_ObjectAvail { + class CPDF_ObjectAvailExcludeArray final : public CPDF_ObjectAvail { + public: + using CPDF_ObjectAvail::CPDF_ObjectAvail; +- ~CPDF_ObjectAvailExcludeArray() override {} ++ ~CPDF_ObjectAvailExcludeArray() override = default; + bool ExcludeObject(const CPDF_Object* object) const override { + return object->IsArray(); + } +@@ -115,49 +114,46 @@ class CPDF_ObjectAvailExcludeArray final : public CPDF_ObjectAvail { + class CPDF_ObjectAvailExcludeTypeKey final : public CPDF_ObjectAvail { + public: + using CPDF_ObjectAvail::CPDF_ObjectAvail; +- ~CPDF_ObjectAvailExcludeTypeKey() override {} ++ ~CPDF_ObjectAvailExcludeTypeKey() override = default; + bool ExcludeObject(const CPDF_Object* object) const override { + // The value of "Type" may be reference, and if it is not available, we can + // incorrect filter objects. + // In this case CPDF_ObjectAvail should wait availability of this item and + // call ExcludeObject again. + return object->IsDictionary() && +- object->GetDict()->GetStringFor("Type") == "Exclude me"; ++ object->GetDict()->GetByteStringFor("Type") == "Exclude me"; + } + }; + + } // namespace + +-TEST(CPDF_ObjectAvailTest, OneObject) { ++TEST(ObjectAvailTest, OneObject) { + TestHolder holder; + holder.AddObject(1, pdfium::MakeRetain(nullptr, "string", false), + TestHolder::ObjectState::Unavailable); + CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1); +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable, +- avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail()); + holder.SetObjectState(1, TestHolder::ObjectState::Available); +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail()); + } + +-TEST(CPDF_ObjectAvailTest, OneReferencedObject) { ++TEST(ObjectAvailTest, OneReferencedObject) { + TestHolder holder; + holder.AddObject(1, pdfium::MakeRetain(&holder, 2), + TestHolder::ObjectState::Unavailable); + holder.AddObject(2, pdfium::MakeRetain(nullptr, "string", false), + TestHolder::ObjectState::Unavailable); + CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1); +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable, +- avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail()); + + holder.SetObjectState(1, TestHolder::ObjectState::Available); +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable, +- avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail()); + + holder.SetObjectState(2, TestHolder::ObjectState::Available); +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail()); + } + +-TEST(CPDF_ObjectAvailTest, CycledReferences) { ++TEST(ObjectAvailTest, CycledReferences) { + TestHolder holder; + holder.AddObject(1, pdfium::MakeRetain(&holder, 2), + TestHolder::ObjectState::Unavailable); +@@ -167,48 +163,44 @@ TEST(CPDF_ObjectAvailTest, CycledReferences) { + TestHolder::ObjectState::Unavailable); + + CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1); +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable, +- avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail()); + + holder.SetObjectState(1, TestHolder::ObjectState::Available); +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable, +- avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail()); + + holder.SetObjectState(2, TestHolder::ObjectState::Available); +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable, +- avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail()); + + holder.SetObjectState(3, TestHolder::ObjectState::Available); +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail()); + } + +-TEST(CPDF_ObjectAvailTest, DoNotCheckParent) { ++TEST(ObjectAvailTest, DoNotCheckParent) { + TestHolder holder; + holder.AddObject(1, pdfium::MakeRetain(), + TestHolder::ObjectState::Unavailable); + holder.AddObject(2, pdfium::MakeRetain(), + TestHolder::ObjectState::Unavailable); + +- holder.GetTestObject(2)->GetDict()->SetNewFor("Parent", +- &holder, 1); ++ holder.GetTestObject(2)->GetMutableDict()->SetNewFor( ++ "Parent", &holder, 1); + + CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 2); +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable, +- avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail()); + + holder.SetObjectState(2, TestHolder::ObjectState::Available); + // Object should be available in case when "Parent" object is unavailable. +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail()); + } + +-TEST(CPDF_ObjectAvailTest, Generic) { ++TEST(ObjectAvailTest, Generic) { + TestHolder holder; + const uint32_t kDepth = 100; + for (uint32_t i = 1; i < kDepth; ++i) { + holder.AddObject(i, pdfium::MakeRetain(), + TestHolder::ObjectState::Unavailable); + // Add ref to next dictionary. +- holder.GetTestObject(i)->GetDict()->SetNewFor( ++ holder.GetTestObject(i)->GetMutableDict()->SetNewFor( + "Child", &holder, i + 1); + } + // Add final object +@@ -217,40 +209,40 @@ TEST(CPDF_ObjectAvailTest, Generic) { + + CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1); + for (uint32_t i = 1; i <= kDepth; ++i) { +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable, +- avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail()); + holder.SetObjectState(i, TestHolder::ObjectState::Available); + } +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail()); + } + +-TEST(CPDF_ObjectAvailTest, NotExcludeRoot) { ++TEST(ObjectAvailTest, NotExcludeRoot) { + TestHolder holder; + holder.AddObject(1, pdfium::MakeRetain(), + TestHolder::ObjectState::Available); + CPDF_ObjectAvailFailOnExclude avail(holder.GetValidator(), &holder, 1); +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail()); + } + +-TEST(CPDF_ObjectAvailTest, NotExcludeReferedRoot) { ++TEST(ObjectAvailTest, NotExcludeReferedRoot) { + TestHolder holder; + holder.AddObject(1, pdfium::MakeRetain(&holder, 2), + TestHolder::ObjectState::Available); + holder.AddObject(2, pdfium::MakeRetain(), + TestHolder::ObjectState::Available); + CPDF_ObjectAvailFailOnExclude avail(holder.GetValidator(), &holder, 1); +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail()); + } + +-TEST(CPDF_ObjectAvailTest, Exclude) { ++TEST(ObjectAvailTest, Exclude) { + TestHolder holder; + holder.AddObject(1, pdfium::MakeRetain(), + TestHolder::ObjectState::Available); +- holder.GetTestObject(1)->GetDict()->SetNewFor("ArrayRef", +- &holder, 2); ++ holder.GetTestObject(1)->GetMutableDict()->SetNewFor( ++ "ArrayRef", &holder, 2); + holder.AddObject(2, pdfium::MakeRetain(), + TestHolder::ObjectState::Available); +- holder.GetTestObject(2)->AsArray()->AddNew(&holder, 2); ++ holder.GetTestObject(2)->AsMutableArray()->AppendNew(&holder, ++ 2); + + // Add string, which is refered by array item. It is should not be checked. + holder.AddObject( +@@ -258,28 +250,28 @@ TEST(CPDF_ObjectAvailTest, Exclude) { + pdfium::MakeRetain(nullptr, "Not available string", false), + TestHolder::ObjectState::Unavailable); + CPDF_ObjectAvailExcludeArray avail(holder.GetValidator(), &holder, 1); +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail()); + } + +-TEST(CPDF_ObjectAvailTest, ReadErrorOnExclude) { ++TEST(ObjectAvailTest, ReadErrorOnExclude) { + TestHolder holder; + holder.AddObject(1, pdfium::MakeRetain(), + TestHolder::ObjectState::Available); +- holder.GetTestObject(1)->GetDict()->SetNewFor("DictRef", +- &holder, 2); ++ holder.GetTestObject(1)->GetMutableDict()->SetNewFor( ++ "DictRef", &holder, 2); + holder.AddObject(2, pdfium::MakeRetain(), + TestHolder::ObjectState::Available); + +- holder.GetTestObject(2)->GetDict()->SetNewFor("Type", &holder, +- 3); ++ holder.GetTestObject(2)->GetMutableDict()->SetNewFor( ++ "Type", &holder, 3); + // The value of "Type" key is not available at start + holder.AddObject( + 3, pdfium::MakeRetain(nullptr, "Exclude me", false), + TestHolder::ObjectState::Unavailable); + +- holder.GetTestObject(2)->GetDict()->SetNewFor("OtherData", +- &holder, 4); +- // Add string, which is refered by dictionary item. It is should not be ++ holder.GetTestObject(2)->GetMutableDict()->SetNewFor( ++ "OtherData", &holder, 4); ++ // Add string, which is referred by dictionary item. It is should not be + // checked, because the dictionary with it, should be skipped. + holder.AddObject( + 4, +@@ -288,30 +280,29 @@ TEST(CPDF_ObjectAvailTest, ReadErrorOnExclude) { + + CPDF_ObjectAvailExcludeTypeKey avail(holder.GetValidator(), &holder, 1); + +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable, +- avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail()); + + // Make "Type" value object available. + holder.SetObjectState(3, TestHolder::ObjectState::Available); + + // Now object should be available, although the object '4' is not available, + // because it is in skipped dictionary. +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail()); + } + +-TEST(CPDF_ObjectAvailTest, IgnoreNotExistsObject) { ++TEST(ObjectAvailTest, IgnoreNotExistsObject) { + TestHolder holder; + holder.AddObject(1, pdfium::MakeRetain(), + TestHolder::ObjectState::Available); +- holder.GetTestObject(1)->GetDict()->SetNewFor( ++ holder.GetTestObject(1)->GetMutableDict()->SetNewFor( + "NotExistsObjRef", &holder, 2); + CPDF_ObjectAvail avail(holder.GetValidator(), &holder, 1); + // Now object should be available, although the object '2' is not exists. But + // all exists in file related data are checked. +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail()); + } + +-TEST(CPDF_ObjectAvailTest, CheckTwice) { ++TEST(ObjectAvailTest, CheckTwice) { + TestHolder holder; + holder.AddObject(1, pdfium::MakeRetain(nullptr, "string", false), + TestHolder::ObjectState::Unavailable); +@@ -323,27 +314,24 @@ TEST(CPDF_ObjectAvailTest, CheckTwice) { + EXPECT_EQ(avail.CheckAvail(), avail.CheckAvail()); + } + +-TEST(CPDF_ObjectAvailTest, SelfReferedInlinedObject) { ++TEST(ObjectAvailTest, SelfReferedInlinedObject) { + TestHolder holder; + holder.AddObject(1, pdfium::MakeRetain(), + TestHolder::ObjectState::Available); + +- holder.GetTestObject(1)->GetDict()->SetNewFor("Data", &holder, +- 2); +- auto* root = +- holder.GetTestObject(1)->GetDict()->SetNewFor("Dict"); ++ holder.GetTestObject(1)->GetMutableDict()->SetNewFor( ++ "Data", &holder, 2); ++ auto root = ++ holder.GetTestObject(1)->GetMutableDict()->SetNewFor( ++ "Dict"); + + root->SetNewFor("Self", &holder, 1); +- + holder.AddObject(2, pdfium::MakeRetain(nullptr, "Data", false), + TestHolder::ObjectState::Unavailable); + + CPDF_ObjectAvail avail(holder.GetValidator(), &holder, root); +- +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable, +- avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataNotAvailable, avail.CheckAvail()); + + holder.SetObjectState(2, TestHolder::ObjectState::Available); +- +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail()); + } +diff --git a/core/fpdfapi/parser/cpdf_object_stream.cpp b/core/fpdfapi/parser/cpdf_object_stream.cpp +index a515dbb29..1c5a7b792 100644 +--- a/core/fpdfapi/parser/cpdf_object_stream.cpp ++++ b/core/fpdfapi/parser/cpdf_object_stream.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,26 +13,26 @@ + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" + #include "core/fpdfapi/parser/cpdf_syntax_parser.h" +-#include "core/fxcrt/cfx_readonlymemorystream.h" ++#include "core/fpdfapi/parser/fpdf_parser_utility.h" ++#include "core/fxcrt/cfx_read_only_span_stream.h" + #include "core/fxcrt/fx_safe_types.h" ++#include "third_party/base/check.h" + #include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" + +-// static +-bool CPDF_ObjectStream::IsObjectsStreamObject(const CPDF_Object* object) { ++namespace { ++ ++bool IsObjectStream(const CPDF_Object* object) { + const CPDF_Stream* stream = ToStream(object); + if (!stream) + return false; + +- const CPDF_Dictionary* stream_dict = stream->GetDict(); +- if (!stream_dict) +- return false; +- +- if (stream_dict->GetStringFor("Type") != "ObjStm") ++ // See ISO 32000-1:2008 spec, table 16. ++ RetainPtr stream_dict = stream->GetDict(); ++ if (!ValidateDictType(stream_dict.Get(), "ObjStm")) + return false; + +- const CPDF_Number* number_of_objects = +- ToNumber(stream_dict->GetObjectFor("N")); ++ RetainPtr number_of_objects = ++ stream_dict->GetNumberFor("N"); + if (!number_of_objects || !number_of_objects->IsInteger() || + number_of_objects->GetInteger() < 0 || + number_of_objects->GetInteger() >= +@@ -40,8 +40,8 @@ bool CPDF_ObjectStream::IsObjectsStreamObject(const CPDF_Object* object) { + return false; + } + +- const CPDF_Number* first_object_offset = +- ToNumber(stream_dict->GetObjectFor("First")); ++ RetainPtr first_object_offset = ++ stream_dict->GetNumberFor("First"); + if (!first_object_offset || !first_object_offset->IsInteger() || + first_object_offset->GetInteger() < 0) { + return false; +@@ -50,56 +50,49 @@ bool CPDF_ObjectStream::IsObjectsStreamObject(const CPDF_Object* object) { + return true; + } + ++} // namespace ++ + // static + std::unique_ptr CPDF_ObjectStream::Create( +- const CPDF_Stream* stream) { +- if (!IsObjectsStreamObject(stream)) ++ RetainPtr stream) { ++ if (!IsObjectStream(stream.Get())) + return nullptr; +- // The ctor of CPDF_ObjectStream is protected. Use WrapUnique instead +- // MakeUnique. +- return pdfium::WrapUnique(new CPDF_ObjectStream(stream)); ++ ++ // Protected constructor. ++ return pdfium::WrapUnique(new CPDF_ObjectStream(std::move(stream))); + } + +-CPDF_ObjectStream::CPDF_ObjectStream(const CPDF_Stream* obj_stream) +- : obj_num_(obj_stream->GetObjNum()), ++CPDF_ObjectStream::CPDF_ObjectStream(RetainPtr obj_stream) ++ : stream_acc_(pdfium::MakeRetain(obj_stream)), + first_object_offset_(obj_stream->GetDict()->GetIntegerFor("First")) { +- ASSERT(IsObjectsStreamObject(obj_stream)); +- if (const auto* extends_ref = +- ToReference(obj_stream->GetDict()->GetObjectFor("Extends"))) { +- extends_obj_num_ = extends_ref->GetRefObjNum(); +- } +- Init(obj_stream); ++ DCHECK(IsObjectStream(obj_stream.Get())); ++ Init(obj_stream.Get()); + } + + CPDF_ObjectStream::~CPDF_ObjectStream() = default; + +-bool CPDF_ObjectStream::HasObject(uint32_t obj_number) const { +- return pdfium::ContainsKey(objects_offsets_, obj_number); +-} +- + RetainPtr CPDF_ObjectStream::ParseObject( + CPDF_IndirectObjectHolder* pObjList, +- uint32_t obj_number) const { +- const auto it = objects_offsets_.find(obj_number); +- if (it == objects_offsets_.end()) ++ uint32_t obj_number, ++ uint32_t archive_obj_index) const { ++ if (archive_obj_index >= object_info_.size()) + return nullptr; + +- RetainPtr result = ParseObjectAtOffset(pObjList, it->second); +- if (!result) ++ const auto& info = object_info_[archive_obj_index]; ++ if (info.obj_num != obj_number) + return nullptr; + +- result->SetObjNum(obj_number); ++ RetainPtr result = ++ ParseObjectAtOffset(pObjList, info.obj_offset); ++ if (result) ++ result->SetObjNum(obj_number); + return result; + } + + void CPDF_ObjectStream::Init(const CPDF_Stream* stream) { +- { +- auto stream_acc = pdfium::MakeRetain(stream); +- stream_acc->LoadAllDataFiltered(); +- const uint32_t data_size = stream_acc->GetSize(); +- data_stream_ = pdfium::MakeRetain( +- stream_acc->DetachData(), data_size); +- } ++ stream_acc_->LoadAllDataFiltered(); ++ data_stream_ = ++ pdfium::MakeRetain(stream_acc_->GetSpan()); + + CPDF_SyntaxParser syntax(data_stream_); + const int object_count = stream->GetDict()->GetIntegerFor("N"); +@@ -112,7 +105,7 @@ void CPDF_ObjectStream::Init(const CPDF_Stream* stream) { + if (!obj_num) + continue; + +- objects_offsets_[obj_num] = obj_offset; ++ object_info_.emplace_back(obj_num, obj_offset); + } + } + +diff --git a/core/fpdfapi/parser/cpdf_object_stream.h b/core/fpdfapi/parser/cpdf_object_stream.h +index 2fa163401..cfa073711 100644 +--- a/core/fpdfapi/parser/cpdf_object_stream.h ++++ b/core/fpdfapi/parser/cpdf_object_stream.h +@@ -1,54 +1,60 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #ifndef CORE_FPDFAPI_PARSER_CPDF_OBJECT_STREAM_H_ + #define CORE_FPDFAPI_PARSER_CPDF_OBJECT_STREAM_H_ + +-#include + #include ++#include + + #include "core/fpdfapi/parser/cpdf_object.h" + #include "core/fxcrt/retain_ptr.h" + + class CPDF_IndirectObjectHolder; + class CPDF_Stream; ++class CPDF_StreamAcc; + class IFX_SeekableReadStream; + + // Implementation of logic of PDF "Object Streams". +-// See "PDF 32000-1:2008" Spec. section 7.5.7. ++// See ISO 32000-1:2008 spec, section 7.5.7. + class CPDF_ObjectStream { + public: +- static bool IsObjectsStreamObject(const CPDF_Object* object); ++ struct ObjectInfo { ++ ObjectInfo(uint32_t obj_num, uint32_t obj_offset) ++ : obj_num(obj_num), obj_offset(obj_offset) {} + +- static std::unique_ptr Create(const CPDF_Stream* stream); ++ bool operator==(const ObjectInfo& that) const { ++ return obj_num == that.obj_num && obj_offset == that.obj_offset; ++ } + +- ~CPDF_ObjectStream(); ++ uint32_t obj_num; ++ uint32_t obj_offset; ++ }; ++ ++ static std::unique_ptr Create( ++ RetainPtr stream); + +- uint32_t obj_num() const { return obj_num_; } +- uint32_t extends_obj_num() const { return extends_obj_num_; } ++ ~CPDF_ObjectStream(); + +- bool HasObject(uint32_t obj_number) const; + RetainPtr ParseObject(CPDF_IndirectObjectHolder* pObjList, +- uint32_t obj_number) const; +- const std::map& objects_offsets() const { +- return objects_offsets_; +- } ++ uint32_t obj_number, ++ uint32_t archive_obj_index) const; ++ const std::vector& object_info() const { return object_info_; } + +- protected: +- explicit CPDF_ObjectStream(const CPDF_Stream* stream); ++ private: ++ explicit CPDF_ObjectStream(RetainPtr stream); + + void Init(const CPDF_Stream* stream); + RetainPtr ParseObjectAtOffset( + CPDF_IndirectObjectHolder* pObjList, + uint32_t object_offset) const; + +- uint32_t obj_num_ = CPDF_Object::kInvalidObjNum; +- uint32_t extends_obj_num_ = CPDF_Object::kInvalidObjNum; +- ++ // Must outlive `data_stream_`. ++ RetainPtr const stream_acc_; + RetainPtr data_stream_; + int first_object_offset_ = 0; +- std::map objects_offsets_; ++ std::vector object_info_; + }; + + #endif // CORE_FPDFAPI_PARSER_CPDF_OBJECT_STREAM_H_ +diff --git a/core/fpdfapi/parser/cpdf_object_stream_unittest.cpp b/core/fpdfapi/parser/cpdf_object_stream_unittest.cpp +new file mode 100644 +index 000000000..43306f41d +--- /dev/null ++++ b/core/fpdfapi/parser/cpdf_object_stream_unittest.cpp +@@ -0,0 +1,509 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fpdfapi/parser/cpdf_object_stream.h" ++ ++#include ++#include ++ ++#include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fpdfapi/parser/cpdf_indirect_object_holder.h" ++#include "core/fpdfapi/parser/cpdf_name.h" ++#include "core/fpdfapi/parser/cpdf_number.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" ++#include "core/fpdfapi/parser/cpdf_string.h" ++#include "core/fxcrt/data_vector.h" ++#include "testing/gmock/include/gmock/gmock.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++using testing::ElementsAre; ++ ++namespace { ++ ++constexpr char kNormalStreamContent[] = ++ "10 0 11 14 12 21<>[1 2 3]4"; ++constexpr int kNormalStreamContentOffset = 16; ++static_assert(kNormalStreamContent[kNormalStreamContentOffset] == '<', ++ "Wrong offset"); ++static_assert(kNormalStreamContent[kNormalStreamContentOffset + 1] == '<', ++ "Wrong offset"); ++ ++} // namespace ++ ++TEST(ObjectStreamTest, StreamDictNormal) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", 3); ++ dict->SetNewFor("First", kNormalStreamContentOffset); ++ ++ ByteStringView contents_view(kNormalStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); ++ ASSERT_TRUE(obj_stream); ++ ++ EXPECT_THAT(obj_stream->object_info(), ++ ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), ++ CPDF_ObjectStream::ObjectInfo(11, 14), ++ CPDF_ObjectStream::ObjectInfo(12, 21))); ++ ++ // Check expected indices. ++ CPDF_IndirectObjectHolder holder; ++ RetainPtr obj10 = obj_stream->ParseObject(&holder, 10, 0); ++ ASSERT_TRUE(obj10); ++ EXPECT_EQ(10u, obj10->GetObjNum()); ++ EXPECT_EQ(0u, obj10->GetGenNum()); ++ EXPECT_TRUE(obj10->IsDictionary()); ++ ++ RetainPtr obj11 = obj_stream->ParseObject(&holder, 11, 1); ++ ASSERT_TRUE(obj11); ++ EXPECT_EQ(11u, obj11->GetObjNum()); ++ EXPECT_EQ(0u, obj11->GetGenNum()); ++ EXPECT_TRUE(obj11->IsArray()); ++ ++ RetainPtr obj12 = obj_stream->ParseObject(&holder, 12, 2); ++ ASSERT_TRUE(obj12); ++ EXPECT_EQ(12u, obj12->GetObjNum()); ++ EXPECT_EQ(0u, obj12->GetGenNum()); ++ EXPECT_TRUE(obj12->IsNumber()); ++ ++ // Check bad indices. ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 1)); ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 2)); ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 3)); ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 0)); ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 2)); ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 3)); ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 12, 0)); ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 12, 1)); ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 12, 3)); ++} ++ ++TEST(ObjectStreamTest, StreamNoDict) { ++ ByteStringView contents_view(kNormalStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), ++ /*pDict=*/nullptr); ++ EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); ++} ++ ++TEST(ObjectStreamTest, StreamDictNoType) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("N", 3); ++ dict->SetNewFor("First", 5); ++ ++ ByteStringView contents_view(kNormalStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); ++} ++ ++TEST(ObjectStreamTest, StreamDictWrongType) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm", /*bHex=*/false); ++ dict->SetNewFor("N", 3); ++ dict->SetNewFor("First", 5); ++ ++ ByteStringView contents_view(kNormalStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); ++} ++ ++TEST(ObjectStreamTest, StreamDictWrongTypeValue) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStmmmm"); ++ dict->SetNewFor("N", 3); ++ dict->SetNewFor("First", 5); ++ ++ ByteStringView contents_view(kNormalStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); ++} ++ ++TEST(ObjectStreamTest, StreamDictNoCount) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("First", 5); ++ ++ ByteStringView contents_view(kNormalStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); ++} ++ ++TEST(ObjectStreamTest, StreamDictFloatCount) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", 2.2f); ++ dict->SetNewFor("First", 5); ++ ++ ByteStringView contents_view(kNormalStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); ++} ++ ++TEST(ObjectStreamTest, StreamDictNegativeCount) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", -1); ++ dict->SetNewFor("First", 5); ++ ++ ByteStringView contents_view(kNormalStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); ++} ++ ++TEST(ObjectStreamTest, StreamDictCountTooBig) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", 999999999); ++ dict->SetNewFor("First", 5); ++ ++ ByteStringView contents_view(kNormalStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); ++} ++ ++TEST(ObjectStreamTest, StreamDictNoOffset) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", 3); ++ ++ ByteStringView contents_view(kNormalStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); ++} ++ ++TEST(ObjectStreamTest, StreamDictFloatOffset) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", 3); ++ dict->SetNewFor("First", 5.5f); ++ ++ ByteStringView contents_view(kNormalStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); ++} ++ ++TEST(ObjectStreamTest, StreamDictNegativeOffset) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", 3); ++ dict->SetNewFor("First", -5); ++ ++ ByteStringView contents_view(kNormalStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); ++} ++ ++TEST(ObjectStreamTest, StreamDictOffsetTooBig) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", 3); ++ constexpr int kTooBigOffset = std::size(kNormalStreamContent); ++ dict->SetNewFor("First", kTooBigOffset); ++ ++ ByteStringView contents_view(kNormalStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); ++ ASSERT_TRUE(obj_stream); ++ ++ EXPECT_THAT(obj_stream->object_info(), ++ ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), ++ CPDF_ObjectStream::ObjectInfo(11, 14), ++ CPDF_ObjectStream::ObjectInfo(12, 21))); ++ ++ CPDF_IndirectObjectHolder holder; ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 0)); ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 1)); ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 12, 2)); ++} ++ ++TEST(ObjectStreamTest, StreamDictTooFewCount) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", 2); ++ dict->SetNewFor("First", kNormalStreamContentOffset); ++ ++ ByteStringView contents_view(kNormalStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); ++ ASSERT_TRUE(obj_stream); ++ ++ EXPECT_THAT(obj_stream->object_info(), ++ ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), ++ CPDF_ObjectStream::ObjectInfo(11, 14))); ++ ++ CPDF_IndirectObjectHolder holder; ++ RetainPtr obj10 = obj_stream->ParseObject(&holder, 10, 0); ++ ASSERT_TRUE(obj10); ++ EXPECT_EQ(10u, obj10->GetObjNum()); ++ EXPECT_EQ(0u, obj10->GetGenNum()); ++ EXPECT_TRUE(obj10->IsDictionary()); ++ ++ RetainPtr obj11 = obj_stream->ParseObject(&holder, 11, 1); ++ ASSERT_TRUE(obj11); ++ EXPECT_EQ(11u, obj11->GetObjNum()); ++ EXPECT_EQ(0u, obj11->GetGenNum()); ++ EXPECT_TRUE(obj11->IsArray()); ++ ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 12, 2)); ++} ++ ++TEST(ObjectStreamTest, StreamDictTooManyObject) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", 9); ++ dict->SetNewFor("First", kNormalStreamContentOffset); ++ ++ ByteStringView contents_view(kNormalStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); ++ ASSERT_TRUE(obj_stream); ++ ++ // TODO(thestig): Can this avoid finding object 2? ++ EXPECT_THAT(obj_stream->object_info(), ++ ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), ++ CPDF_ObjectStream::ObjectInfo(11, 14), ++ CPDF_ObjectStream::ObjectInfo(12, 21), ++ CPDF_ObjectStream::ObjectInfo(2, 3))); ++ ++ CPDF_IndirectObjectHolder holder; ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 2, 0)); ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 2, 1)); ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 2, 2)); ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 2, 3)); ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 2, 4)); ++} ++ ++TEST(ObjectStreamTest, StreamDictGarbageObjNum) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", 3); ++ dict->SetNewFor("First", 19); ++ ++ const char kStreamContent[] = "10 0 hi 14 12 21<>[1 2 3]4"; ++ ByteStringView contents_view(kStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); ++ ASSERT_TRUE(obj_stream); ++ ++ EXPECT_THAT(obj_stream->object_info(), ++ ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), ++ CPDF_ObjectStream::ObjectInfo(12, 21))); ++} ++ ++TEST(ObjectStreamTest, StreamDictGarbageObjectOffset) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", 3); ++ dict->SetNewFor("First", 16); ++ ++ const char kStreamContent[] = "10 0 11 hi 12 21<>[1 2 3]4"; ++ ByteStringView contents_view(kStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); ++ ASSERT_TRUE(obj_stream); ++ ++ // TODO(thestig): Should object 11 be rejected? ++ EXPECT_THAT(obj_stream->object_info(), ++ ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), ++ CPDF_ObjectStream::ObjectInfo(11, 0), ++ CPDF_ObjectStream::ObjectInfo(12, 21))); ++ ++ CPDF_IndirectObjectHolder holder; ++ RetainPtr obj10 = obj_stream->ParseObject(&holder, 10, 0); ++ ASSERT_TRUE(obj10); ++ EXPECT_EQ(10u, obj10->GetObjNum()); ++ EXPECT_EQ(0u, obj10->GetGenNum()); ++ EXPECT_TRUE(obj10->IsDictionary()); ++ ++ RetainPtr obj11 = obj_stream->ParseObject(&holder, 11, 1); ++ ASSERT_TRUE(obj11); ++ EXPECT_EQ(11u, obj11->GetObjNum()); ++ EXPECT_EQ(0u, obj11->GetGenNum()); ++ EXPECT_TRUE(obj11->IsDictionary()); ++} ++ ++TEST(ObjectStreamTest, StreamDictNegativeObjectOffset) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", 3); ++ dict->SetNewFor("First", 16); ++ ++ const char kStreamContent[] = "10 0 11 -1 12 21<>[1 2 3]4"; ++ ByteStringView contents_view(kStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); ++ ASSERT_TRUE(obj_stream); ++ ++ // TODO(thestig): Should object 11 be rejected? ++ EXPECT_THAT(obj_stream->object_info(), ++ ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), ++ CPDF_ObjectStream::ObjectInfo(11, 4294967295), ++ CPDF_ObjectStream::ObjectInfo(12, 21))); ++ ++ CPDF_IndirectObjectHolder holder; ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 1)); ++} ++ ++TEST(ObjectStreamTest, StreamDictObjectOffsetTooBig) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", 3); ++ dict->SetNewFor("First", 17); ++ ++ const char kStreamContent[] = "10 0 11 999 12 21<>[1 2 3]4"; ++ ByteStringView contents_view(kStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); ++ ASSERT_TRUE(obj_stream); ++ ++ // TODO(thestig): Should object 11 be rejected? ++ EXPECT_THAT(obj_stream->object_info(), ++ ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), ++ CPDF_ObjectStream::ObjectInfo(11, 999), ++ CPDF_ObjectStream::ObjectInfo(12, 21))); ++ ++ CPDF_IndirectObjectHolder holder; ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 1)); ++} ++ ++TEST(ObjectStreamTest, StreamDictDuplicateObjNum) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", 3); ++ dict->SetNewFor("First", 16); ++ ++ const char kStreamContent[] = "10 0 10 14 12 21<>[1 2 3]4"; ++ ByteStringView contents_view(kStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); ++ ASSERT_TRUE(obj_stream); ++ ++ EXPECT_THAT(obj_stream->object_info(), ++ ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), ++ CPDF_ObjectStream::ObjectInfo(10, 14), ++ CPDF_ObjectStream::ObjectInfo(12, 21))); ++ ++ CPDF_IndirectObjectHolder holder; ++ RetainPtr obj10 = obj_stream->ParseObject(&holder, 10, 0); ++ ASSERT_TRUE(obj10); ++ EXPECT_EQ(10u, obj10->GetObjNum()); ++ EXPECT_EQ(0u, obj10->GetGenNum()); ++ EXPECT_TRUE(obj10->IsDictionary()); ++ ++ obj10 = obj_stream->ParseObject(&holder, 10, 1); ++ ASSERT_TRUE(obj10); ++ EXPECT_EQ(10u, obj10->GetObjNum()); ++ EXPECT_EQ(0u, obj10->GetGenNum()); ++ EXPECT_TRUE(obj10->IsArray()); ++ ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 2)); ++ EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 3)); ++ ++ RetainPtr obj12 = obj_stream->ParseObject(&holder, 12, 2); ++ ASSERT_TRUE(obj12); ++ EXPECT_EQ(12u, obj12->GetObjNum()); ++ EXPECT_EQ(0u, obj12->GetGenNum()); ++ EXPECT_TRUE(obj12->IsNumber()); ++} ++ ++TEST(ObjectStreamTest, StreamDictUnorderedObjectNumbers) { ++ // ISO 32000-1:2008 spec. section 7.5.7, note 6 says there is no restriction ++ // on object number ordering. ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", 3); ++ dict->SetNewFor("First", 16); ++ ++ const char kStreamContent[] = "11 0 12 14 10 21<>[1 2 3]4"; ++ ByteStringView contents_view(kStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); ++ ASSERT_TRUE(obj_stream); ++ ++ EXPECT_THAT(obj_stream->object_info(), ++ ElementsAre(CPDF_ObjectStream::ObjectInfo(11, 0), ++ CPDF_ObjectStream::ObjectInfo(12, 14), ++ CPDF_ObjectStream::ObjectInfo(10, 21))); ++ ++ CPDF_IndirectObjectHolder holder; ++ RetainPtr obj10 = obj_stream->ParseObject(&holder, 10, 2); ++ ASSERT_TRUE(obj10); ++ EXPECT_EQ(10u, obj10->GetObjNum()); ++ EXPECT_EQ(0u, obj10->GetGenNum()); ++ EXPECT_TRUE(obj10->IsNumber()); ++ ++ RetainPtr obj11 = obj_stream->ParseObject(&holder, 11, 0); ++ ASSERT_TRUE(obj11); ++ EXPECT_EQ(11u, obj11->GetObjNum()); ++ EXPECT_EQ(0u, obj11->GetGenNum()); ++ EXPECT_TRUE(obj11->IsDictionary()); ++ ++ RetainPtr obj12 = obj_stream->ParseObject(&holder, 12, 1); ++ ASSERT_TRUE(obj12); ++ EXPECT_EQ(12u, obj12->GetObjNum()); ++ EXPECT_EQ(0u, obj12->GetGenNum()); ++ EXPECT_TRUE(obj12->IsArray()); ++} ++ ++TEST(ObjectStreamTest, StreamDictUnorderedObjectOffsets) { ++ // ISO 32000-1:2008 spec. section 7.5.7, says offsets shall be in increasing ++ // order. ++ // TODO(thestig): Should CPDF_ObjectStream check for this and reject this ++ // object stream? ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "ObjStm"); ++ dict->SetNewFor("N", 3); ++ dict->SetNewFor("First", 16); ++ ++ const char kStreamContent[] = "10 21 11 0 12 14<>[1 2 3]4"; ++ ByteStringView contents_view(kStreamContent); ++ auto stream = pdfium::MakeRetain( ++ DataVector(contents_view.begin(), contents_view.end()), dict); ++ auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); ++ ASSERT_TRUE(obj_stream); ++ ++ EXPECT_THAT(obj_stream->object_info(), ++ ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 21), ++ CPDF_ObjectStream::ObjectInfo(11, 0), ++ CPDF_ObjectStream::ObjectInfo(12, 14))); ++ ++ CPDF_IndirectObjectHolder holder; ++ RetainPtr obj10 = obj_stream->ParseObject(&holder, 10, 0); ++ ASSERT_TRUE(obj10); ++ EXPECT_EQ(10u, obj10->GetObjNum()); ++ EXPECT_EQ(0u, obj10->GetGenNum()); ++ EXPECT_TRUE(obj10->IsNumber()); ++ ++ RetainPtr obj11 = obj_stream->ParseObject(&holder, 11, 1); ++ ASSERT_TRUE(obj11); ++ EXPECT_EQ(11u, obj11->GetObjNum()); ++ EXPECT_EQ(0u, obj11->GetGenNum()); ++ EXPECT_TRUE(obj11->IsDictionary()); ++ ++ RetainPtr obj12 = obj_stream->ParseObject(&holder, 12, 2); ++ ASSERT_TRUE(obj12); ++ EXPECT_EQ(12u, obj12->GetObjNum()); ++ EXPECT_EQ(0u, obj12->GetGenNum()); ++ EXPECT_TRUE(obj12->IsArray()); ++} +diff --git a/core/fpdfapi/parser/cpdf_object_unittest.cpp b/core/fpdfapi/parser/cpdf_object_unittest.cpp +index ca02618f5..a57b85e0a 100644 +--- a/core/fpdfapi/parser/cpdf_object_unittest.cpp ++++ b/core/fpdfapi/parser/cpdf_object_unittest.cpp +@@ -1,7 +1,11 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include "core/fpdfapi/parser/cpdf_object.h" ++ ++#include ++ + #include + #include + #include +@@ -19,9 +23,9 @@ + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" + #include "core/fpdfapi/parser/cpdf_string.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_memory_wrappers.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + + namespace { + +@@ -34,9 +38,9 @@ void TestArrayAccessors(const CPDF_Array* arr, + CPDF_Array* arr_val, + CPDF_Dictionary* dict_val, + CPDF_Stream* stream_val) { +- EXPECT_STREQ(str_val, arr->GetStringAt(index).c_str()); ++ EXPECT_STREQ(str_val, arr->GetByteStringAt(index).c_str()); + EXPECT_EQ(int_val, arr->GetIntegerAt(index)); +- EXPECT_EQ(float_val, arr->GetNumberAt(index)); ++ EXPECT_EQ(float_val, arr->GetFloatAt(index)); + EXPECT_EQ(arr_val, arr->GetArrayAt(index)); + EXPECT_EQ(dict_val, arr->GetDictAt(index)); + EXPECT_EQ(stream_val, arr->GetStreamAt(index)); +@@ -69,16 +73,14 @@ class PDFObjectsTest : public testing::Test { + m_DictObj->SetNewFor("bool", false); + m_DictObj->SetNewFor("num", 0.23f); + // Stream object. +- const char content[] = "abcdefghijklmnopqrstuvwxyz"; +- size_t buf_len = FX_ArraySize(content); +- std::unique_ptr buf(FX_Alloc(uint8_t, buf_len)); +- memcpy(buf.get(), content, buf_len); ++ static constexpr char kContents[] = "abcdefghijklmnopqrstuvwxyz"; + auto pNewDict = pdfium::MakeRetain(); + m_StreamDictObj = pNewDict; + m_StreamDictObj->SetNewFor("key1", L" test dict"); + m_StreamDictObj->SetNewFor("key2", -1); +- auto stream_obj = pdfium::MakeRetain(std::move(buf), buf_len, +- std::move(pNewDict)); ++ auto stream_obj = pdfium::MakeRetain( ++ DataVector(std::begin(kContents), std::end(kContents)), ++ std::move(pNewDict)); + // Null Object. + auto null_obj = pdfium::MakeRetain(); + // All direct objects. +@@ -92,21 +94,22 @@ class PDFObjectsTest : public testing::Test { + CPDF_Object::kNumber, CPDF_Object::kString, CPDF_Object::kString, + CPDF_Object::kName, CPDF_Object::kArray, CPDF_Object::kDictionary, + CPDF_Object::kStream, CPDF_Object::kNullobj}; +- for (size_t i = 0; i < FX_ArraySize(objs); ++i) ++ for (size_t i = 0; i < std::size(objs); ++i) + m_DirectObjs.emplace_back(objs[i]); + + // Indirect references to indirect objects. +- m_ObjHolder = pdfium::MakeUnique(); +- m_IndirectObjs = {m_ObjHolder->AddIndirectObject(boolean_true_obj->Clone()), +- m_ObjHolder->AddIndirectObject(number_int_obj->Clone()), +- m_ObjHolder->AddIndirectObject(str_spec_obj->Clone()), +- m_ObjHolder->AddIndirectObject(name_obj->Clone()), +- m_ObjHolder->AddIndirectObject(m_ArrayObj->Clone()), +- m_ObjHolder->AddIndirectObject(m_DictObj->Clone()), +- m_ObjHolder->AddIndirectObject(stream_obj->Clone())}; +- for (CPDF_Object* pObj : m_IndirectObjs) { +- m_RefObjs.emplace_back(pdfium::MakeRetain( +- m_ObjHolder.get(), pObj->GetObjNum())); ++ m_ObjHolder = std::make_unique(); ++ m_IndirectObjNums = { ++ m_ObjHolder->AddIndirectObject(boolean_true_obj->Clone()), ++ m_ObjHolder->AddIndirectObject(number_int_obj->Clone()), ++ m_ObjHolder->AddIndirectObject(str_spec_obj->Clone()), ++ m_ObjHolder->AddIndirectObject(name_obj->Clone()), ++ m_ObjHolder->AddIndirectObject(m_ArrayObj->Clone()), ++ m_ObjHolder->AddIndirectObject(m_DictObj->Clone()), ++ m_ObjHolder->AddIndirectObject(stream_obj->Clone())}; ++ for (uint32_t objnum : m_IndirectObjNums) { ++ m_RefObjs.emplace_back( ++ pdfium::MakeRetain(m_ObjHolder.get(), objnum)); + } + } + +@@ -130,8 +133,10 @@ class PDFObjectsTest : public testing::Test { + if (array1->size() != array2->size()) + return false; + for (size_t i = 0; i < array1->size(); ++i) { +- if (!Equal(array1->GetObjectAt(i), array2->GetObjectAt(i))) ++ if (!Equal(array1->GetObjectAt(i).Get(), ++ array2->GetObjectAt(i).Get())) { + return false; ++ } + } + return true; + } +@@ -142,7 +147,7 @@ class PDFObjectsTest : public testing::Test { + return false; + CPDF_DictionaryLocker locker1(dict1); + for (const auto& item : locker1) { +- if (!Equal(item.second.Get(), dict2->GetObjectFor(item.first))) ++ if (!Equal(item.second.Get(), dict2->GetObjectFor(item.first).Get())) + return false; + } + return true; +@@ -150,25 +155,28 @@ class PDFObjectsTest : public testing::Test { + case CPDF_Object::kNullobj: + return true; + case CPDF_Object::kStream: { +- const CPDF_Stream* stream1 = obj1->AsStream(); +- const CPDF_Stream* stream2 = obj2->AsStream(); ++ RetainPtr stream1(obj1->AsStream()); ++ RetainPtr stream2(obj2->AsStream()); + if (!stream1->GetDict() && !stream2->GetDict()) + return true; + // Compare dictionaries. +- if (!Equal(stream1->GetDict(), stream2->GetDict())) ++ if (!Equal(stream1->GetDict().Get(), stream2->GetDict().Get())) + return false; + +- auto streamAcc1 = pdfium::MakeRetain(stream1); ++ auto streamAcc1 = ++ pdfium::MakeRetain(std::move(stream1)); + streamAcc1->LoadAllDataRaw(); +- auto streamAcc2 = pdfium::MakeRetain(stream2); ++ auto streamAcc2 = ++ pdfium::MakeRetain(std::move(stream2)); + streamAcc2->LoadAllDataRaw(); ++ pdfium::span span1 = streamAcc1->GetSpan(); ++ pdfium::span span2 = streamAcc2->GetSpan(); + + // Compare sizes. +- if (streamAcc1->GetSize() != streamAcc2->GetSize()) ++ if (span1.size() != span2.size()) + return false; + +- return memcmp(streamAcc1->GetData(), streamAcc2->GetData(), +- streamAcc2->GetSize()) == 0; ++ return memcmp(span1.data(), span2.data(), span2.size()) == 0; + } + case CPDF_Object::kReference: + return obj1->AsReference()->GetRefObjNum() == +@@ -187,7 +195,7 @@ class PDFObjectsTest : public testing::Test { + RetainPtr m_DictObj; + RetainPtr m_StreamDictObj; + RetainPtr m_ArrayObj; +- std::vector m_IndirectObjs; ++ std::vector m_IndirectObjNums; + }; + + TEST_F(PDFObjectsTest, GetString) { +@@ -265,7 +273,24 @@ TEST_F(PDFObjectsTest, GetDict) { + m_DictObj.Get(), + m_StreamDictObj.Get()}; + for (size_t i = 0; i < m_RefObjs.size(); ++i) +- EXPECT_TRUE(Equal(indirect_obj_results[i], m_RefObjs[i]->GetDict())); ++ EXPECT_TRUE(Equal(indirect_obj_results[i], m_RefObjs[i]->GetDict().Get())); ++} ++ ++TEST_F(PDFObjectsTest, GetNameFor) { ++ m_DictObj->SetNewFor("string", "ium", false); ++ m_DictObj->SetNewFor("name", "Pdf"); ++ ++ EXPECT_STREQ("", m_DictObj->GetNameFor("invalid").c_str()); ++ EXPECT_STREQ("", m_DictObj->GetNameFor("bool").c_str()); ++ EXPECT_STREQ("", m_DictObj->GetNameFor("num").c_str()); ++ EXPECT_STREQ("", m_DictObj->GetNameFor("string").c_str()); ++ EXPECT_STREQ("Pdf", m_DictObj->GetNameFor("name").c_str()); ++ ++ EXPECT_STREQ("", m_DictObj->GetByteStringFor("invalid").c_str()); ++ EXPECT_STREQ("false", m_DictObj->GetByteStringFor("bool").c_str()); ++ EXPECT_STREQ("0.23", m_DictObj->GetByteStringFor("num").c_str()); ++ EXPECT_STREQ("ium", m_DictObj->GetByteStringFor("string").c_str()); ++ EXPECT_STREQ("Pdf", m_DictObj->GetByteStringFor("name").c_str()); + } + + TEST_F(PDFObjectsTest, GetArray) { +@@ -278,7 +303,7 @@ TEST_F(PDFObjectsTest, GetArray) { + + // Check indirect references. + for (const auto& it : m_RefObjs) +- EXPECT_EQ(nullptr, it->AsArray()); ++ EXPECT_FALSE(it->AsArray()); + } + + TEST_F(PDFObjectsTest, Clone) { +@@ -312,7 +337,7 @@ TEST_F(PDFObjectsTest, GetDirect) { + + // Check indirect references. + for (size_t i = 0; i < m_RefObjs.size(); ++i) +- EXPECT_EQ(m_IndirectObjs[i], m_RefObjs[i]->GetDirect()); ++ EXPECT_EQ(m_IndirectObjNums[i], m_RefObjs[i]->GetDirect()->GetObjNum()); + } + + TEST_F(PDFObjectsTest, SetString) { +@@ -321,7 +346,7 @@ TEST_F(PDFObjectsTest, SetString) { + "changed", "", "NewName"}; + const char* const expected[] = {"true", "false", "3.125", "97", + "changed", "", "NewName"}; +- for (size_t i = 0; i < FX_ArraySize(set_values); ++i) { ++ for (size_t i = 0; i < std::size(set_values); ++i) { + m_DirectObjs[i]->SetString(set_values[i]); + EXPECT_STREQ(expected[i], m_DirectObjs[i]->GetString().c_str()); + } +@@ -335,7 +360,7 @@ TEST_F(PDFObjectsTest, IsTypeAndAsType) { + EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsArray()); + } else { + EXPECT_FALSE(m_DirectObjs[i]->IsArray()); +- EXPECT_EQ(nullptr, m_DirectObjs[i]->AsArray()); ++ EXPECT_FALSE(m_DirectObjs[i]->AsArray()); + } + + if (m_DirectObjTypes[i] == CPDF_Object::kBoolean) { +@@ -343,7 +368,7 @@ TEST_F(PDFObjectsTest, IsTypeAndAsType) { + EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsBoolean()); + } else { + EXPECT_FALSE(m_DirectObjs[i]->IsBoolean()); +- EXPECT_EQ(nullptr, m_DirectObjs[i]->AsBoolean()); ++ EXPECT_FALSE(m_DirectObjs[i]->AsBoolean()); + } + + if (m_DirectObjTypes[i] == CPDF_Object::kName) { +@@ -351,7 +376,7 @@ TEST_F(PDFObjectsTest, IsTypeAndAsType) { + EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsName()); + } else { + EXPECT_FALSE(m_DirectObjs[i]->IsName()); +- EXPECT_EQ(nullptr, m_DirectObjs[i]->AsName()); ++ EXPECT_FALSE(m_DirectObjs[i]->AsName()); + } + + if (m_DirectObjTypes[i] == CPDF_Object::kNumber) { +@@ -359,7 +384,7 @@ TEST_F(PDFObjectsTest, IsTypeAndAsType) { + EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsNumber()); + } else { + EXPECT_FALSE(m_DirectObjs[i]->IsNumber()); +- EXPECT_EQ(nullptr, m_DirectObjs[i]->AsNumber()); ++ EXPECT_FALSE(m_DirectObjs[i]->AsNumber()); + } + + if (m_DirectObjTypes[i] == CPDF_Object::kString) { +@@ -367,7 +392,7 @@ TEST_F(PDFObjectsTest, IsTypeAndAsType) { + EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsString()); + } else { + EXPECT_FALSE(m_DirectObjs[i]->IsString()); +- EXPECT_EQ(nullptr, m_DirectObjs[i]->AsString()); ++ EXPECT_FALSE(m_DirectObjs[i]->AsString()); + } + + if (m_DirectObjTypes[i] == CPDF_Object::kDictionary) { +@@ -375,7 +400,7 @@ TEST_F(PDFObjectsTest, IsTypeAndAsType) { + EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsDictionary()); + } else { + EXPECT_FALSE(m_DirectObjs[i]->IsDictionary()); +- EXPECT_EQ(nullptr, m_DirectObjs[i]->AsDictionary()); ++ EXPECT_FALSE(m_DirectObjs[i]->AsDictionary()); + } + + if (m_DirectObjTypes[i] == CPDF_Object::kStream) { +@@ -383,11 +408,11 @@ TEST_F(PDFObjectsTest, IsTypeAndAsType) { + EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsStream()); + } else { + EXPECT_FALSE(m_DirectObjs[i]->IsStream()); +- EXPECT_EQ(nullptr, m_DirectObjs[i]->AsStream()); ++ EXPECT_FALSE(m_DirectObjs[i]->AsStream()); + } + + EXPECT_FALSE(m_DirectObjs[i]->IsReference()); +- EXPECT_EQ(nullptr, m_DirectObjs[i]->AsReference()); ++ EXPECT_FALSE(m_DirectObjs[i]->AsReference()); + } + // Check indirect references. + for (size_t i = 0; i < m_RefObjs.size(); ++i) { +@@ -408,17 +433,33 @@ TEST_F(PDFObjectsTest, MakeReferenceGeneric) { + ToReference(ref_obj.Get())->GetRefObjNum()); + } + ++TEST_F(PDFObjectsTest, KeyForCache) { ++ std::set key_set; ++ ++ // Check all direct objects inserted without collision. ++ for (const auto& direct : m_DirectObjs) { ++ EXPECT_TRUE(key_set.insert(direct->KeyForCache()).second); ++ } ++ // Check indirect objects inserted without collision. ++ for (const auto& pair : *m_ObjHolder) { ++ EXPECT_TRUE(key_set.insert(pair.second->KeyForCache()).second); ++ } ++ ++ // Check all expected objects counted. ++ EXPECT_EQ(18u, key_set.size()); ++} ++ + TEST(PDFArrayTest, GetMatrix) { + float elems[][6] = {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + {1, 2, 3, 4, 5, 6}, + {2.3f, 4.05f, 3, -2, -3, 0.0f}, + {0.05f, 0.1f, 0.56f, 0.67f, 1.34f, 99.9f}}; +- for (size_t i = 0; i < FX_ArraySize(elems); ++i) { ++ for (size_t i = 0; i < std::size(elems); ++i) { + auto arr = pdfium::MakeRetain(); + CFX_Matrix matrix(elems[i][0], elems[i][1], elems[i][2], elems[i][3], + elems[i][4], elems[i][5]); + for (size_t j = 0; j < 6; ++j) +- arr->AddNew(elems[i][j]); ++ arr->AppendNew(elems[i][j]); + CFX_Matrix arr_matrix = arr->GetMatrix(); + EXPECT_EQ(matrix.a, arr_matrix.a); + EXPECT_EQ(matrix.b, arr_matrix.b); +@@ -434,11 +475,11 @@ TEST(PDFArrayTest, GetRect) { + {1, 2, 5, 6}, + {2.3f, 4.05f, -3, 0.0f}, + {0.05f, 0.1f, 1.34f, 99.9f}}; +- for (size_t i = 0; i < FX_ArraySize(elems); ++i) { ++ for (size_t i = 0; i < std::size(elems); ++i) { + auto arr = pdfium::MakeRetain(); +- CFX_FloatRect rect(elems[i]); ++ CFX_FloatRect rect(elems[i][0], elems[i][1], elems[i][2], elems[i][3]); + for (size_t j = 0; j < 4; ++j) +- arr->AddNew(elems[i][j]); ++ arr->AppendNew(elems[i][j]); + CFX_FloatRect arr_rect = arr->GetRect(); + EXPECT_EQ(rect.left, arr_rect.left); + EXPECT_EQ(rect.right, arr_rect.right); +@@ -452,9 +493,9 @@ TEST(PDFArrayTest, GetTypeAt) { + // Boolean array. + const bool vals[] = {true, false, false, true, true}; + auto arr = pdfium::MakeRetain(); +- for (size_t i = 0; i < FX_ArraySize(vals); ++i) ++ for (size_t i = 0; i < std::size(vals); ++i) + arr->InsertNewAt(i, vals[i]); +- for (size_t i = 0; i < FX_ArraySize(vals); ++i) { ++ for (size_t i = 0; i < std::size(vals); ++i) { + TestArrayAccessors(arr.Get(), i, // Array and index. + vals[i] ? "true" : "false", // String value. + nullptr, // Const string value. +@@ -469,9 +510,9 @@ TEST(PDFArrayTest, GetTypeAt) { + // Integer array. + const int vals[] = {10, 0, -345, 2089345456, -1000000000, 567, 93658767}; + auto arr = pdfium::MakeRetain(); +- for (size_t i = 0; i < FX_ArraySize(vals); ++i) ++ for (size_t i = 0; i < std::size(vals); ++i) + arr->InsertNewAt(i, vals[i]); +- for (size_t i = 0; i < FX_ArraySize(vals); ++i) { ++ for (size_t i = 0; i < std::size(vals); ++i) { + char buf[33]; + TestArrayAccessors(arr.Get(), i, // Array and index. + FXSYS_itoa(vals[i], buf, 10), // String value. +@@ -490,9 +531,9 @@ TEST(PDFArrayTest, GetTypeAt) { + const char* const expected_str[] = { + "0", "0", "10", "10", "0.0345", "897.34", "-2.5", "-1", "-345", "0"}; + auto arr = pdfium::MakeRetain(); +- for (size_t i = 0; i < FX_ArraySize(vals); ++i) ++ for (size_t i = 0; i < std::size(vals); ++i) + arr->InsertNewAt(i, vals[i]); +- for (size_t i = 0; i < FX_ArraySize(vals); ++i) { ++ for (size_t i = 0; i < std::size(vals); ++i) { + TestArrayAccessors(arr.Get(), i, // Array and index. + expected_str[i], // String value. + nullptr, // Const string value. +@@ -509,11 +550,11 @@ TEST(PDFArrayTest, GetTypeAt) { + ".", "EYREW", "It is a joke :)"}; + auto string_array = pdfium::MakeRetain(); + auto name_array = pdfium::MakeRetain(); +- for (size_t i = 0; i < FX_ArraySize(vals); ++i) { ++ for (size_t i = 0; i < std::size(vals); ++i) { + string_array->InsertNewAt(i, vals[i], false); + name_array->InsertNewAt(i, vals[i]); + } +- for (size_t i = 0; i < FX_ArraySize(vals); ++i) { ++ for (size_t i = 0; i < std::size(vals); ++i) { + TestArrayAccessors(string_array.Get(), i, // Array and index. + vals[i], // String value. + vals[i], // Const string value. +@@ -550,32 +591,32 @@ TEST(PDFArrayTest, GetTypeAt) { + } + { + // Array of array. +- CPDF_Array* vals[3]; ++ RetainPtr vals[3]; + auto arr = pdfium::MakeRetain(); + for (size_t i = 0; i < 3; ++i) { +- vals[i] = arr->AddNew(); ++ vals[i] = arr->AppendNew(); + for (size_t j = 0; j < 3; ++j) { + int value = j + 100; +- vals[i]->InsertNewAt(i, value); ++ vals[i]->InsertNewAt(j, value); + } + } + for (size_t i = 0; i < 3; ++i) { +- TestArrayAccessors(arr.Get(), i, // Array and index. +- "", // String value. +- nullptr, // Const string value. +- 0, // Integer value. +- 0, // Float value. +- vals[i], // Array value. +- nullptr, // Dictionary value. +- nullptr); // Stream value. ++ TestArrayAccessors(arr.Get(), i, // Array and index. ++ "", // String value. ++ nullptr, // Const string value. ++ 0, // Integer value. ++ 0, // Float value. ++ vals[i].Get(), // Array value. ++ nullptr, // Dictionary value. ++ nullptr); // Stream value. + } + } + { + // Dictionary array. +- CPDF_Dictionary* vals[3]; ++ RetainPtr vals[3]; + auto arr = pdfium::MakeRetain(); + for (size_t i = 0; i < 3; ++i) { +- vals[i] = arr->AddNew(); ++ vals[i] = arr->AppendNew(); + for (size_t j = 0; j < 3; ++j) { + std::string key("key"); + char buf[33]; +@@ -585,20 +626,20 @@ TEST(PDFArrayTest, GetTypeAt) { + } + } + for (size_t i = 0; i < 3; ++i) { +- TestArrayAccessors(arr.Get(), i, // Array and index. +- "", // String value. +- nullptr, // Const string value. +- 0, // Integer value. +- 0, // Float value. +- nullptr, // Array value. +- vals[i], // Dictionary value. +- nullptr); // Stream value. ++ TestArrayAccessors(arr.Get(), i, // Array and index. ++ "", // String value. ++ nullptr, // Const string value. ++ 0, // Integer value. ++ 0, // Float value. ++ nullptr, // Array value. ++ vals[i].Get(), // Dictionary value. ++ nullptr); // Stream value. + } + } + { + // Stream array. + RetainPtr vals[3]; +- CPDF_Stream* stream_vals[3]; ++ RetainPtr stream_vals[3]; + auto arr = pdfium::MakeRetain(); + for (size_t i = 0; i < 3; ++i) { + vals[i] = pdfium::MakeRetain(); +@@ -609,23 +650,20 @@ TEST(PDFArrayTest, GetTypeAt) { + int value = j + 200; + vals[i]->SetNewFor(key.c_str(), value); + } +- uint8_t content[] = "content: this is a stream"; +- size_t data_size = FX_ArraySize(content); +- std::unique_ptr data( +- FX_Alloc(uint8_t, data_size)); +- memcpy(data.get(), content, data_size); +- stream_vals[i] = +- arr->AddNew(std::move(data), data_size, vals[i]); ++ static constexpr uint8_t kContents[] = "content: this is a stream"; ++ stream_vals[i] = arr->AppendNew( ++ DataVector(std::begin(kContents), std::end(kContents)), ++ vals[i]); + } + for (size_t i = 0; i < 3; ++i) { +- TestArrayAccessors(arr.Get(), i, // Array and index. +- "", // String value. +- nullptr, // Const string value. +- 0, // Integer value. +- 0, // Float value. +- nullptr, // Array value. +- vals[i].Get(), // Dictionary value. +- stream_vals[i]); // Stream value. ++ TestArrayAccessors(arr.Get(), i, // Array and index. ++ "", // String value. ++ nullptr, // Const string value. ++ 0, // Integer value. ++ 0, // Float value. ++ nullptr, // Array value. ++ vals[i].Get(), // Dictionary value. ++ stream_vals[i].Get()); // Stream value. + } + } + { +@@ -643,25 +681,23 @@ TEST(PDFArrayTest, GetTypeAt) { + arr->InsertNewAt(9, "test"); + arr->InsertNewAt(10); + +- CPDF_Array* arr_val = arr->InsertNewAt(11); +- arr_val->AddNew(1); +- arr_val->AddNew(2); ++ auto arr_val = arr->InsertNewAt(11); ++ arr_val->AppendNew(1); ++ arr_val->AppendNew(2); + +- CPDF_Dictionary* dict_val = arr->InsertNewAt(12); ++ auto dict_val = arr->InsertNewAt(12); + dict_val->SetNewFor("key1", "Linda", false); + dict_val->SetNewFor("key2", "Zoe", false); + + auto stream_dict = pdfium::MakeRetain(); + stream_dict->SetNewFor("key1", "John", false); + stream_dict->SetNewFor("key2", "King", false); +- uint8_t data[] = "A stream for test"; ++ static constexpr uint8_t kData[] = "A stream for test"; + // The data buffer will be owned by stream object, so it needs to be + // dynamically allocated. +- size_t buf_size = sizeof(data); +- std::unique_ptr buf(FX_Alloc(uint8_t, buf_size)); +- memcpy(buf.get(), data, buf_size); + CPDF_Stream* stream_val = arr->InsertNewAt( +- 13, std::move(buf), buf_size, stream_dict); ++ 13, DataVector(std::begin(kData), std::end(kData)), ++ stream_dict); + const char* const expected_str[] = { + "true", "false", "0", "-1234", "2345", "0.05", "", + "It is a test!", "NAME", "test", "", "", "", ""}; +@@ -670,22 +706,22 @@ TEST(PDFArrayTest, GetTypeAt) { + const float expected_float[] = {0, 0, 0, -1234, 2345, 0.05f, 0, + 0, 0, 0, 0, 0, 0, 0}; + for (size_t i = 0; i < arr->size(); ++i) { +- EXPECT_STREQ(expected_str[i], arr->GetStringAt(i).c_str()); ++ EXPECT_STREQ(expected_str[i], arr->GetByteStringAt(i).c_str()); + EXPECT_EQ(expected_int[i], arr->GetIntegerAt(i)); +- EXPECT_EQ(expected_float[i], arr->GetNumberAt(i)); ++ EXPECT_EQ(expected_float[i], arr->GetFloatAt(i)); + if (i == 11) + EXPECT_EQ(arr_val, arr->GetArrayAt(i)); + else +- EXPECT_EQ(nullptr, arr->GetArrayAt(i)); ++ EXPECT_FALSE(arr->GetArrayAt(i)); + if (i == 13) { + EXPECT_EQ(stream_dict, arr->GetDictAt(i)); + EXPECT_EQ(stream_val, arr->GetStreamAt(i)); + } else { +- EXPECT_EQ(nullptr, arr->GetStreamAt(i)); ++ EXPECT_FALSE(arr->GetStreamAt(i)); + if (i == 12) + EXPECT_EQ(dict_val, arr->GetDictAt(i)); + else +- EXPECT_EQ(nullptr, arr->GetDictAt(i)); ++ EXPECT_FALSE(arr->GetDictAt(i)); + } + } + } +@@ -695,9 +731,9 @@ TEST(PDFArrayTest, AddNumber) { + float vals[] = {1.0f, -1.0f, 0, 0.456734f, + 12345.54321f, 0.5f, 1000, 0.000045f}; + auto arr = pdfium::MakeRetain(); +- for (size_t i = 0; i < FX_ArraySize(vals); ++i) +- arr->AddNew(vals[i]); +- for (size_t i = 0; i < FX_ArraySize(vals); ++i) { ++ for (size_t i = 0; i < std::size(vals); ++i) ++ arr->AppendNew(vals[i]); ++ for (size_t i = 0; i < std::size(vals); ++i) { + EXPECT_EQ(CPDF_Object::kNumber, arr->GetObjectAt(i)->GetType()); + EXPECT_EQ(vals[i], arr->GetObjectAt(i)->GetNumber()); + } +@@ -706,9 +742,9 @@ TEST(PDFArrayTest, AddNumber) { + TEST(PDFArrayTest, AddInteger) { + int vals[] = {0, 1, 934435456, 876, 10000, -1, -24354656, -100}; + auto arr = pdfium::MakeRetain(); +- for (size_t i = 0; i < FX_ArraySize(vals); ++i) +- arr->AddNew(vals[i]); +- for (size_t i = 0; i < FX_ArraySize(vals); ++i) { ++ for (size_t i = 0; i < std::size(vals); ++i) ++ arr->AppendNew(vals[i]); ++ for (size_t i = 0; i < std::size(vals); ++i) { + EXPECT_EQ(CPDF_Object::kNumber, arr->GetObjectAt(i)->GetType()); + EXPECT_EQ(vals[i], arr->GetObjectAt(i)->GetNumber()); + } +@@ -721,10 +757,10 @@ TEST(PDFArrayTest, AddStringAndName) { + auto string_array = pdfium::MakeRetain(); + auto name_array = pdfium::MakeRetain(); + for (const char* val : kVals) { +- string_array->AddNew(val, false); +- name_array->AddNew(val); ++ string_array->AppendNew(val, false); ++ name_array->AppendNew(val); + } +- for (size_t i = 0; i < FX_ArraySize(kVals); ++i) { ++ for (size_t i = 0; i < std::size(kVals); ++i) { + EXPECT_EQ(CPDF_Object::kString, string_array->GetObjectAt(i)->GetType()); + EXPECT_STREQ(kVals[i], string_array->GetObjectAt(i)->GetString().c_str()); + EXPECT_EQ(CPDF_Object::kName, name_array->GetObjectAt(i)->GetType()); +@@ -733,7 +769,7 @@ TEST(PDFArrayTest, AddStringAndName) { + } + + TEST(PDFArrayTest, AddReferenceAndGetObjectAt) { +- auto holder = pdfium::MakeUnique(); ++ auto holder = std::make_unique(); + auto boolean_obj = pdfium::MakeRetain(true); + auto int_obj = pdfium::MakeRetain(-1234); + auto float_obj = pdfium::MakeRetain(2345.089f); +@@ -747,14 +783,15 @@ TEST(PDFArrayTest, AddReferenceAndGetObjectAt) { + auto arr = pdfium::MakeRetain(); + auto arr1 = pdfium::MakeRetain(); + // Create two arrays of references by different AddReference() APIs. +- for (size_t i = 0; i < FX_ArraySize(indirect_objs); ++i) { ++ for (size_t i = 0; i < std::size(indirect_objs); ++i) { + holder->ReplaceIndirectObjectIfHigherGeneration(obj_nums[i], + indirect_objs[i]); +- arr->AddNew(holder.get(), obj_nums[i]); +- arr1->AddNew(holder.get(), indirect_objs[i]->GetObjNum()); ++ arr->AppendNew(holder.get(), obj_nums[i]); ++ arr1->AppendNew(holder.get(), ++ indirect_objs[i]->GetObjNum()); + } + // Check indirect objects. +- for (size_t i = 0; i < FX_ArraySize(obj_nums); ++i) ++ for (size_t i = 0; i < std::size(obj_nums); ++i) + EXPECT_EQ(indirect_objs[i], holder->GetOrParseIndirectObject(obj_nums[i])); + // Check arrays. + EXPECT_EQ(arr->size(), arr1->size()); +@@ -771,9 +808,9 @@ TEST(PDFArrayTest, AddReferenceAndGetObjectAt) { + TEST(PDFArrayTest, CloneDirectObject) { + CPDF_IndirectObjectHolder objects_holder; + auto array = pdfium::MakeRetain(); +- array->AddNew(&objects_holder, 1234); ++ array->AppendNew(&objects_holder, 1234); + ASSERT_EQ(1U, array->size()); +- CPDF_Object* obj = array->GetObjectAt(0); ++ RetainPtr obj = array->GetObjectAt(0); + ASSERT_TRUE(obj); + EXPECT_TRUE(obj->IsReference()); + +@@ -783,17 +820,17 @@ TEST(PDFArrayTest, CloneDirectObject) { + + RetainPtr cloned_array = ToArray(std::move(cloned_array_object)); + ASSERT_EQ(0U, cloned_array->size()); +- CPDF_Object* cloned_obj = cloned_array->GetObjectAt(0); ++ RetainPtr cloned_obj = cloned_array->GetObjectAt(0); + EXPECT_FALSE(cloned_obj); + } + + TEST(PDFArrayTest, ConvertIndirect) { + CPDF_IndirectObjectHolder objects_holder; + auto array = pdfium::MakeRetain(); +- CPDF_Object* pObj = array->AddNew(42); ++ auto pObj = array->AppendNew(42); + array->ConvertToIndirectObjectAt(0, &objects_holder); +- CPDF_Object* pRef = array->GetObjectAt(0); +- CPDF_Object* pNum = array->GetDirectObjectAt(0); ++ RetainPtr pRef = array->GetObjectAt(0); ++ RetainPtr pNum = array->GetDirectObjectAt(0); + EXPECT_TRUE(pRef->IsReference()); + EXPECT_TRUE(pNum->IsNumber()); + EXPECT_NE(pObj, pRef); +@@ -802,18 +839,18 @@ TEST(PDFArrayTest, ConvertIndirect) { + } + + TEST(PDFStreamTest, SetData) { +- std::vector data(100); +- auto stream = pdfium::MakeRetain(); +- stream->InitStream(data, pdfium::MakeRetain()); ++ DataVector data(100); ++ auto stream = pdfium::MakeRetain( ++ data, pdfium::MakeRetain()); + EXPECT_EQ(static_cast(data.size()), + stream->GetDict()->GetIntegerFor(pdfium::stream::kLength)); + +- stream->GetDict()->SetNewFor(pdfium::stream::kFilter, +- L"SomeFilter"); +- stream->GetDict()->SetNewFor(pdfium::stream::kDecodeParms, +- L"SomeParams"); ++ stream->GetMutableDict()->SetNewFor(pdfium::stream::kFilter, ++ L"SomeFilter"); ++ stream->GetMutableDict()->SetNewFor(pdfium::stream::kDecodeParms, ++ L"SomeParams"); + +- std::vector new_data(data.size() * 2); ++ DataVector new_data(data.size() * 2); + stream->SetData(new_data); + + // The "Length" field should be updated for new data size. +@@ -828,18 +865,18 @@ TEST(PDFStreamTest, SetData) { + } + + TEST(PDFStreamTest, SetDataAndRemoveFilter) { +- std::vector data(100); +- auto stream = pdfium::MakeRetain(); +- stream->InitStream(data, pdfium::MakeRetain()); ++ DataVector data(100); ++ auto stream = pdfium::MakeRetain( ++ data, pdfium::MakeRetain()); + EXPECT_EQ(static_cast(data.size()), + stream->GetDict()->GetIntegerFor(pdfium::stream::kLength)); + +- stream->GetDict()->SetNewFor(pdfium::stream::kFilter, +- L"SomeFilter"); +- stream->GetDict()->SetNewFor(pdfium::stream::kDecodeParms, +- L"SomeParams"); ++ stream->GetMutableDict()->SetNewFor(pdfium::stream::kFilter, ++ L"SomeFilter"); ++ stream->GetMutableDict()->SetNewFor(pdfium::stream::kDecodeParms, ++ L"SomeParams"); + +- std::vector new_data(data.size() * 2); ++ DataVector new_data(data.size() * 2); + stream->SetDataAndRemoveFilter(new_data); + // The "Length" field should be updated for new data size. + EXPECT_EQ(static_cast(new_data.size()), +@@ -854,20 +891,16 @@ TEST(PDFStreamTest, LengthInDictionaryOnCreate) { + static constexpr uint32_t kBufSize = 100; + // The length field should be created on stream create. + { +- std::unique_ptr data; +- data.reset(FX_Alloc(uint8_t, kBufSize)); + auto stream = pdfium::MakeRetain( +- std::move(data), kBufSize, pdfium::MakeRetain()); ++ DataVector(kBufSize), pdfium::MakeRetain()); + EXPECT_EQ(static_cast(kBufSize), + stream->GetDict()->GetIntegerFor(pdfium::stream::kLength)); + } + // The length field should be corrected on stream create. + { +- std::unique_ptr data; +- data.reset(FX_Alloc(uint8_t, kBufSize)); + auto dict = pdfium::MakeRetain(); + dict->SetNewFor(pdfium::stream::kLength, 30000); +- auto stream = pdfium::MakeRetain(std::move(data), kBufSize, ++ auto stream = pdfium::MakeRetain(DataVector(kBufSize), + std::move(dict)); + EXPECT_EQ(static_cast(kBufSize), + stream->GetDict()->GetIntegerFor(pdfium::stream::kLength)); +@@ -879,7 +912,7 @@ TEST(PDFDictionaryTest, CloneDirectObject) { + auto dict = pdfium::MakeRetain(); + dict->SetNewFor("foo", &objects_holder, 1234); + ASSERT_EQ(1U, dict->size()); +- CPDF_Object* obj = dict->GetObjectFor("foo"); ++ RetainPtr obj = dict->GetObjectFor("foo"); + ASSERT_TRUE(obj); + EXPECT_TRUE(obj->IsReference()); + +@@ -890,7 +923,7 @@ TEST(PDFDictionaryTest, CloneDirectObject) { + RetainPtr cloned_dict = + ToDictionary(std::move(cloned_dict_object)); + ASSERT_EQ(0U, cloned_dict->size()); +- CPDF_Object* cloned_obj = cloned_dict->GetObjectFor("foo"); ++ RetainPtr cloned_obj = cloned_dict->GetObjectFor("foo"); + EXPECT_FALSE(cloned_obj); + } + +@@ -898,44 +931,43 @@ TEST(PDFObjectTest, CloneCheckLoop) { + { + // Create a dictionary/array pair with a reference loop. + auto arr_obj = pdfium::MakeRetain(); +- CPDF_Dictionary* dict_obj = arr_obj->InsertNewAt(0); ++ auto dict_obj = arr_obj->InsertNewAt(0); + dict_obj->SetFor("arr", arr_obj); + // Clone this object to see whether stack overflow will be triggered. + RetainPtr cloned_array = ToArray(arr_obj->Clone()); + // Cloned object should be the same as the original. + ASSERT_TRUE(cloned_array); + EXPECT_EQ(1u, cloned_array->size()); +- CPDF_Object* cloned_dict = cloned_array->GetObjectAt(0); ++ RetainPtr cloned_dict = cloned_array->GetObjectAt(0); + ASSERT_TRUE(cloned_dict); + ASSERT_TRUE(cloned_dict->IsDictionary()); + // Recursively referenced object is not cloned. +- EXPECT_EQ(nullptr, cloned_dict->AsDictionary()->GetObjectFor("arr")); ++ EXPECT_FALSE(cloned_dict->AsDictionary()->GetObjectFor("arr")); + dict_obj->RemoveFor("arr"); // Break deliberate cycle for cleanup. + } + { + // Create a dictionary/stream pair with a reference loop. + auto dict_obj = pdfium::MakeRetain(); +- CPDF_Stream* stream_obj = +- dict_obj->SetNewFor("stream", nullptr, 0, dict_obj); ++ auto stream_obj = dict_obj->SetNewFor("stream", dict_obj); + // Clone this object to see whether stack overflow will be triggered. + RetainPtr cloned_stream = ToStream(stream_obj->Clone()); + // Cloned object should be the same as the original. + ASSERT_TRUE(cloned_stream); +- CPDF_Object* cloned_dict = cloned_stream->GetDict(); ++ RetainPtr cloned_dict = cloned_stream->GetDict(); + ASSERT_TRUE(cloned_dict); + ASSERT_TRUE(cloned_dict->IsDictionary()); + // Recursively referenced object is not cloned. +- EXPECT_EQ(nullptr, cloned_dict->AsDictionary()->GetObjectFor("stream")); ++ EXPECT_FALSE(cloned_dict->AsDictionary()->GetObjectFor("stream")); + dict_obj->RemoveFor("stream"); // Break deliberate cycle for cleanup. + } + { + CPDF_IndirectObjectHolder objects_holder; + // Create an object with a reference loop. +- CPDF_Dictionary* dict_obj = objects_holder.NewIndirect(); +- RetainPtr arr_obj = pdfium::MakeRetain(); ++ auto dict_obj = objects_holder.NewIndirect(); ++ auto arr_obj = pdfium::MakeRetain(); + arr_obj->InsertNewAt(0, &objects_holder, + dict_obj->GetObjNum()); +- CPDF_Object* elem0 = arr_obj->GetObjectAt(0); ++ RetainPtr elem0 = arr_obj->GetObjectAt(0); + dict_obj->SetFor("arr", std::move(arr_obj)); + EXPECT_EQ(1u, dict_obj->GetObjNum()); + ASSERT_TRUE(elem0); +@@ -948,12 +980,12 @@ TEST(PDFObjectTest, CloneCheckLoop) { + ToDictionary(dict_obj->CloneDirectObject()); + // Cloned object should be the same as the original. + ASSERT_TRUE(cloned_dict); +- CPDF_Object* cloned_arr = cloned_dict->GetObjectFor("arr"); ++ RetainPtr cloned_arr = cloned_dict->GetObjectFor("arr"); + ASSERT_TRUE(cloned_arr); + ASSERT_TRUE(cloned_arr->IsArray()); + EXPECT_EQ(0U, cloned_arr->AsArray()->size()); + // Recursively referenced object is not cloned. +- EXPECT_EQ(nullptr, cloned_arr->AsArray()->GetObjectAt(0)); ++ EXPECT_FALSE(cloned_arr->AsArray()->GetObjectAt(0)); + dict_obj->RemoveFor("arr"); // Break deliberate cycle for cleanup. + } + } +@@ -961,10 +993,10 @@ TEST(PDFObjectTest, CloneCheckLoop) { + TEST(PDFDictionaryTest, ConvertIndirect) { + CPDF_IndirectObjectHolder objects_holder; + auto dict = pdfium::MakeRetain(); +- CPDF_Object* pObj = dict->SetNewFor("clams", 42); ++ auto pObj = dict->SetNewFor("clams", 42); + dict->ConvertToIndirectObjectFor("clams", &objects_holder); +- CPDF_Object* pRef = dict->GetObjectFor("clams"); +- CPDF_Object* pNum = dict->GetDirectObjectFor("clams"); ++ RetainPtr pRef = dict->GetObjectFor("clams"); ++ RetainPtr pNum = dict->GetDirectObjectFor("clams"); + EXPECT_TRUE(pRef->IsReference()); + EXPECT_TRUE(pNum->IsNumber()); + EXPECT_NE(pObj, pRef); +@@ -974,7 +1006,7 @@ TEST(PDFDictionaryTest, ConvertIndirect) { + + TEST(PDFDictionaryTest, ExtractObjectOnRemove) { + auto dict = pdfium::MakeRetain(); +- CPDF_Object* pObj = dict->SetNewFor("child", 42); ++ auto pObj = dict->SetNewFor("child", 42); + auto extracted_object = dict->RemoveFor("child"); + EXPECT_EQ(pObj, extracted_object.Get()); + +@@ -983,7 +1015,7 @@ TEST(PDFDictionaryTest, ExtractObjectOnRemove) { + } + + TEST(PDFRefernceTest, MakeReferenceToReference) { +- auto obj_holder = pdfium::MakeUnique(); ++ auto obj_holder = std::make_unique(); + auto original_ref = pdfium::MakeRetain(obj_holder.get(), 42); + original_ref->SetObjNum(1952); + ASSERT_FALSE(original_ref->IsInline()); +diff --git a/core/fpdfapi/parser/cpdf_object_walker.cpp b/core/fpdfapi/parser/cpdf_object_walker.cpp +index efd4a5ccd..71acdfbc4 100644 +--- a/core/fpdfapi/parser/cpdf_object_walker.cpp ++++ b/core/fpdfapi/parser/cpdf_object_walker.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,21 +9,22 @@ + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_stream.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + namespace { + + class StreamIterator final : public CPDF_ObjectWalker::SubobjectIterator { + public: +- explicit StreamIterator(const CPDF_Stream* stream) +- : SubobjectIterator(stream) {} +- ~StreamIterator() override {} ++ explicit StreamIterator(RetainPtr stream) ++ : SubobjectIterator(std::move(stream)) {} ++ ++ ~StreamIterator() override = default; + + bool IsFinished() const override { return IsStarted() && is_finished_; } + +- const CPDF_Object* IncrementImpl() override { +- ASSERT(IsStarted()); +- ASSERT(!IsFinished()); ++ RetainPtr IncrementImpl() override { ++ DCHECK(IsStarted()); ++ DCHECK(!IsFinished()); + is_finished_ = true; + return object()->GetDict(); + } +@@ -36,25 +37,26 @@ class StreamIterator final : public CPDF_ObjectWalker::SubobjectIterator { + + class DictionaryIterator final : public CPDF_ObjectWalker::SubobjectIterator { + public: +- explicit DictionaryIterator(const CPDF_Dictionary* dictionary) ++ explicit DictionaryIterator(RetainPtr dictionary) + : SubobjectIterator(dictionary), locker_(dictionary) {} +- ~DictionaryIterator() override {} ++ ++ ~DictionaryIterator() override = default; + + bool IsFinished() const override { + return IsStarted() && dict_iterator_ == locker_.end(); + } + +- const CPDF_Object* IncrementImpl() override { +- ASSERT(IsStarted()); +- ASSERT(!IsFinished()); +- const CPDF_Object* result = dict_iterator_->second.Get(); ++ RetainPtr IncrementImpl() override { ++ DCHECK(IsStarted()); ++ DCHECK(!IsFinished()); ++ RetainPtr result = dict_iterator_->second; + dict_key_ = dict_iterator_->first; + ++dict_iterator_; + return result; + } + + void Start() override { +- ASSERT(!IsStarted()); ++ DCHECK(!IsStarted()); + dict_iterator_ = locker_.begin(); + } + +@@ -68,19 +70,19 @@ class DictionaryIterator final : public CPDF_ObjectWalker::SubobjectIterator { + + class ArrayIterator final : public CPDF_ObjectWalker::SubobjectIterator { + public: +- explicit ArrayIterator(const CPDF_Array* array) ++ explicit ArrayIterator(RetainPtr array) + : SubobjectIterator(array), locker_(array) {} + +- ~ArrayIterator() override {} ++ ~ArrayIterator() override = default; + + bool IsFinished() const override { + return IsStarted() && arr_iterator_ == locker_.end(); + } + +- const CPDF_Object* IncrementImpl() override { +- ASSERT(IsStarted()); +- ASSERT(!IsFinished()); +- const CPDF_Object* result = arr_iterator_->Get(); ++ RetainPtr IncrementImpl() override { ++ DCHECK(IsStarted()); ++ DCHECK(!IsFinished()); ++ RetainPtr result = *arr_iterator_; + ++arr_iterator_; + return result; + } +@@ -94,15 +96,15 @@ class ArrayIterator final : public CPDF_ObjectWalker::SubobjectIterator { + + } // namespace + +-CPDF_ObjectWalker::SubobjectIterator::~SubobjectIterator() {} ++CPDF_ObjectWalker::SubobjectIterator::~SubobjectIterator() = default; + +-const CPDF_Object* CPDF_ObjectWalker::SubobjectIterator::Increment() { ++RetainPtr CPDF_ObjectWalker::SubobjectIterator::Increment() { + if (!IsStarted()) { + Start(); + is_started_ = true; + } + while (!IsFinished()) { +- const CPDF_Object* result = IncrementImpl(); ++ RetainPtr result = IncrementImpl(); + if (result) + return result; + } +@@ -110,46 +112,44 @@ const CPDF_Object* CPDF_ObjectWalker::SubobjectIterator::Increment() { + } + + CPDF_ObjectWalker::SubobjectIterator::SubobjectIterator( +- const CPDF_Object* object) +- : object_(object) { +- ASSERT(object_); ++ RetainPtr object) ++ : object_(std::move(object)) { ++ DCHECK(object_); + } + + // static + std::unique_ptr +-CPDF_ObjectWalker::MakeIterator(const CPDF_Object* object) { ++CPDF_ObjectWalker::MakeIterator(RetainPtr object) { + if (object->IsStream()) +- return pdfium::MakeUnique(object->AsStream()); ++ return std::make_unique(ToStream(object)); + if (object->IsDictionary()) +- return pdfium::MakeUnique(object->AsDictionary()); ++ return std::make_unique(ToDictionary(object)); + if (object->IsArray()) +- return pdfium::MakeUnique(object->AsArray()); ++ return std::make_unique(ToArray(object)); + return nullptr; + } + +-CPDF_ObjectWalker::CPDF_ObjectWalker(const CPDF_Object* root) +- : next_object_(root) {} ++CPDF_ObjectWalker::CPDF_ObjectWalker(RetainPtr root) ++ : next_object_(std::move(root)) {} + + CPDF_ObjectWalker::~CPDF_ObjectWalker() = default; + +-const CPDF_Object* CPDF_ObjectWalker::GetNext() { ++RetainPtr CPDF_ObjectWalker::GetNext() { + while (!stack_.empty() || next_object_) { + if (next_object_) { +- auto new_iterator = MakeIterator(next_object_.Get()); ++ auto new_iterator = MakeIterator(next_object_); + if (new_iterator) { + // Schedule walk within composite objects. + stack_.push(std::move(new_iterator)); + } +- auto* result = next_object_.Get(); +- next_object_ = nullptr; +- return result; ++ return std::move(next_object_); // next_object_ is NULL after move. + } + + SubobjectIterator* it = stack_.top().get(); + if (it->IsFinished()) { + stack_.pop(); + } else { +- next_object_.Reset(it->Increment()); ++ next_object_ = it->Increment(); + parent_object_.Reset(it->object()); + dict_key_ = parent_object_->IsDictionary() + ? static_cast(it)->dict_key() +@@ -167,3 +167,12 @@ void CPDF_ObjectWalker::SkipWalkIntoCurrentObject() { + return; + stack_.pop(); + } ++ ++CPDF_NonConstObjectWalker::CPDF_NonConstObjectWalker( ++ RetainPtr root) ++ : CPDF_ObjectWalker(std::move(root)) {} ++ ++RetainPtr CPDF_NonConstObjectWalker::GetNext() { ++ return pdfium::WrapRetain( ++ const_cast(CPDF_ObjectWalker::GetNext().Get())); ++} +diff --git a/core/fpdfapi/parser/cpdf_object_walker.h b/core/fpdfapi/parser/cpdf_object_walker.h +index d019286f5..c779da144 100644 +--- a/core/fpdfapi/parser/cpdf_object_walker.h ++++ b/core/fpdfapi/parser/cpdf_object_walker.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,7 +8,7 @@ + #include + #include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/retain_ptr.h" + + class CPDF_Object; +@@ -22,13 +22,13 @@ class CPDF_ObjectWalker { + virtual ~SubobjectIterator(); + virtual bool IsFinished() const = 0; + bool IsStarted() const { return is_started_; } +- const CPDF_Object* Increment(); ++ RetainPtr Increment(); + const CPDF_Object* object() const { return object_.Get(); } + + protected: +- explicit SubobjectIterator(const CPDF_Object* object); ++ explicit SubobjectIterator(RetainPtr object); + +- virtual const CPDF_Object* IncrementImpl() = 0; ++ virtual RetainPtr IncrementImpl() = 0; + virtual void Start() = 0; + + private: +@@ -36,10 +36,10 @@ class CPDF_ObjectWalker { + bool is_started_ = false; + }; + +- explicit CPDF_ObjectWalker(const CPDF_Object* root); ++ explicit CPDF_ObjectWalker(RetainPtr root); + ~CPDF_ObjectWalker(); + +- const CPDF_Object* GetNext(); ++ RetainPtr GetNext(); + void SkipWalkIntoCurrentObject(); + + size_t current_depth() const { return current_depth_; } +@@ -48,7 +48,7 @@ class CPDF_ObjectWalker { + + private: + static std::unique_ptr MakeIterator( +- const CPDF_Object* object); ++ RetainPtr object); + + RetainPtr next_object_; + RetainPtr parent_object_; +@@ -59,12 +59,8 @@ class CPDF_ObjectWalker { + + class CPDF_NonConstObjectWalker final : public CPDF_ObjectWalker { + public: +- explicit CPDF_NonConstObjectWalker(CPDF_Object* root) +- : CPDF_ObjectWalker(root) {} +- +- CPDF_Object* GetNext() { +- return const_cast(CPDF_ObjectWalker::GetNext()); +- } ++ explicit CPDF_NonConstObjectWalker(RetainPtr root); ++ RetainPtr GetNext(); + }; + + #endif // CORE_FPDFAPI_PARSER_CPDF_OBJECT_WALKER_H_ +diff --git a/core/fpdfapi/parser/cpdf_object_walker_unittest.cpp b/core/fpdfapi/parser/cpdf_object_walker_unittest.cpp +index 4dde72f66..ab0c2c3fb 100644 +--- a/core/fpdfapi/parser/cpdf_object_walker_unittest.cpp ++++ b/core/fpdfapi/parser/cpdf_object_walker_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -18,14 +18,13 @@ + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_string.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + + namespace { + +-std::string Walk(CPDF_Object* object) { ++std::string Walk(RetainPtr object) { + std::ostringstream result; +- CPDF_ObjectWalker walker(object); +- while (const CPDF_Object* obj = walker.GetNext()) { ++ CPDF_ObjectWalker walker(std::move(object)); ++ while (RetainPtr obj = walker.GetNext()) { + if (obj->IsDictionary()) + result << " Dict"; + else if (obj->IsArray()) +@@ -54,64 +53,63 @@ std::string Walk(CPDF_Object* object) { + + } // namespace + +-TEST(CPDF_ObjectWalkerTest, Simple) { +- EXPECT_EQ(Walk(pdfium::MakeRetain().Get()), "Null"); +- EXPECT_EQ(Walk(pdfium::MakeRetain().Get()), "Dict"); +- EXPECT_EQ(Walk(pdfium::MakeRetain().Get()), "Arr"); +- EXPECT_EQ(Walk(pdfium::MakeRetain().Get()), "Str"); +- EXPECT_EQ(Walk(pdfium::MakeRetain().Get()), "Bool"); +- EXPECT_EQ(Walk(pdfium::MakeRetain().Get()), "Stream"); +- EXPECT_EQ(Walk(pdfium::MakeRetain(nullptr, 0).Get()), "Ref"); ++TEST(ObjectWalkerTest, Simple) { ++ EXPECT_EQ(Walk(pdfium::MakeRetain()), "Null"); ++ EXPECT_EQ(Walk(pdfium::MakeRetain()), "Dict"); ++ EXPECT_EQ(Walk(pdfium::MakeRetain()), "Arr"); ++ EXPECT_EQ(Walk(pdfium::MakeRetain()), "Str"); ++ EXPECT_EQ(Walk(pdfium::MakeRetain()), "Bool"); ++ EXPECT_EQ(Walk(pdfium::MakeRetain()), "Stream"); ++ EXPECT_EQ(Walk(pdfium::MakeRetain(nullptr, 0)), "Ref"); + } + +-TEST(CPDF_ObjectWalkerTest, CombinedObject) { ++TEST(ObjectWalkerTest, CombinedObject) { + auto dict = pdfium::MakeRetain(); + dict->SetFor("1", pdfium::MakeRetain()); + dict->SetFor("2", pdfium::MakeRetain()); + auto array = pdfium::MakeRetain(); +- array->Add(pdfium::MakeRetain(nullptr, 0)); +- array->Add(pdfium::MakeRetain()); +- array->Add(pdfium::MakeRetain( +- nullptr, 0, pdfium::MakeRetain())); ++ array->Append(pdfium::MakeRetain(nullptr, 0)); ++ array->Append(pdfium::MakeRetain()); ++ array->Append( ++ pdfium::MakeRetain(pdfium::MakeRetain())); + dict->SetFor("3", std::move(array)); + // The last number for stream length. +- EXPECT_EQ(Walk(dict.Get()), "Dict Str Bool Arr Ref Null Stream Dict Num"); ++ EXPECT_EQ(Walk(dict), "Dict Str Bool Arr Ref Null Stream Dict Num"); + } + +-TEST(CPDF_ObjectWalkerTest, GetParent) { ++TEST(ObjectWalkerTest, GetParent) { + auto level_4 = pdfium::MakeRetain(0); + auto level_3 = pdfium::MakeRetain(); + level_3->SetFor("Length", std::move(level_4)); +- auto level_2 = +- pdfium::MakeRetain(nullptr, 0, std::move(level_3)); ++ auto level_2 = pdfium::MakeRetain(std::move(level_3)); + auto level_1 = pdfium::MakeRetain(); +- level_1->Add(std::move(level_2)); ++ level_1->Append(std::move(level_2)); + auto level_0 = pdfium::MakeRetain(); + level_0->SetFor("Array", std::move(level_1)); + + // We have <>) ]>> + // In this case each step will increase depth. + // And on each step the prev object should be parent for current. +- const CPDF_Object* cur_parent = nullptr; +- CPDF_ObjectWalker walker(level_0.Get()); +- while (const CPDF_Object* obj = walker.GetNext()) { ++ RetainPtr cur_parent; ++ CPDF_ObjectWalker walker(level_0); ++ while (RetainPtr obj = walker.GetNext()) { + EXPECT_EQ(cur_parent, walker.GetParent()); +- cur_parent = obj; ++ cur_parent = std::move(obj); + } + } + +-TEST(CPDF_ObjectWalkerTest, SkipWalkIntoCurrentObject) { ++TEST(ObjectWalkerTest, SkipWalkIntoCurrentObject) { + auto root_array = pdfium::MakeRetain(); + // Add 2 null objects into |root_array|. [ null1, null2 ] +- root_array->AddNew(); +- root_array->AddNew(); ++ root_array->AppendNew(); ++ root_array->AppendNew(); + // |root_array| will contain 4 null objects after this. + // [ null1, null2, [ null3, null4 ] ] +- root_array->Add(root_array->Clone()); ++ root_array->Append(root_array->Clone()); + + int non_array_objects = 0; +- CPDF_ObjectWalker walker(root_array.Get()); +- while (const CPDF_Object* obj = walker.GetNext()) { ++ CPDF_ObjectWalker walker(root_array); ++ while (RetainPtr obj = walker.GetNext()) { + if (obj != root_array && obj->IsArray()) { + // skip other array except root. + walker.SkipWalkIntoCurrentObject(); +@@ -123,7 +121,7 @@ TEST(CPDF_ObjectWalkerTest, SkipWalkIntoCurrentObject) { + EXPECT_EQ(2, non_array_objects); + } + +-TEST(CPDF_ObjectWalkerTest, DictionaryKey) { ++TEST(ObjectWalkerTest, DictionaryKey) { + auto dict = pdfium::MakeRetain(); + dict->SetFor("1", pdfium::MakeRetain()); + dict->SetFor("2", pdfium::MakeRetain()); +@@ -131,8 +129,8 @@ TEST(CPDF_ObjectWalkerTest, DictionaryKey) { + dict->SetFor("4", pdfium::MakeRetain()); + dict->SetFor("5", pdfium::MakeRetain()); + +- CPDF_ObjectWalker walker(dict.Get()); +- while (const CPDF_Object* obj = walker.GetNext()) { ++ CPDF_ObjectWalker walker(dict); ++ while (RetainPtr obj = walker.GetNext()) { + if (dict == obj) { + // Ignore root dictinary object + continue; +diff --git a/core/fpdfapi/parser/cpdf_page_object_avail.cpp b/core/fpdfapi/parser/cpdf_page_object_avail.cpp +index 6673885f6..af46e70cd 100644 +--- a/core/fpdfapi/parser/cpdf_page_object_avail.cpp ++++ b/core/fpdfapi/parser/cpdf_page_object_avail.cpp +@@ -1,17 +1,18 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/parser/cpdf_page_object_avail.h" + + #include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fpdfapi/parser/fpdf_parser_utility.h" + +-CPDF_PageObjectAvail::~CPDF_PageObjectAvail() {} ++CPDF_PageObjectAvail::~CPDF_PageObjectAvail() = default; + + bool CPDF_PageObjectAvail::ExcludeObject(const CPDF_Object* object) const { + if (CPDF_ObjectAvail::ExcludeObject(object)) + return true; + +- return object->IsDictionary() && +- object->GetDict()->GetStringFor("Type") == "Page"; ++ // See ISO 32000-1:2008 spec, table 30. ++ return ValidateDictType(ToDictionary(object), "Page"); + } +diff --git a/core/fpdfapi/parser/cpdf_page_object_avail.h b/core/fpdfapi/parser/cpdf_page_object_avail.h +index bb9e80a66..01a2c6a1d 100644 +--- a/core/fpdfapi/parser/cpdf_page_object_avail.h ++++ b/core/fpdfapi/parser/cpdf_page_object_avail.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fpdfapi/parser/cpdf_page_object_avail_unittest.cpp b/core/fpdfapi/parser/cpdf_page_object_avail_unittest.cpp +index 6a7611881..2486fed95 100644 +--- a/core/fpdfapi/parser/cpdf_page_object_avail_unittest.cpp ++++ b/core/fpdfapi/parser/cpdf_page_object_avail_unittest.cpp +@@ -1,38 +1,37 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/parser/cpdf_page_object_avail.h" + + #include +-#include + #include + + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h" ++#include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_read_validator.h" + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fxcrt/fx_stream.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/invalid_seekable_read_stream.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + namespace { + + class TestReadValidator final : public CPDF_ReadValidator { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + +- void SimulateReadError() { ReadBlockAtOffset(nullptr, 0, 1); } ++ void SimulateReadError() { ReadBlockAtOffset({}, 0); } + + private: + TestReadValidator() + : CPDF_ReadValidator(pdfium::MakeRetain(100), + nullptr) {} +- ~TestReadValidator() override {} ++ ~TestReadValidator() override = default; + }; + + class TestHolder final : public CPDF_IndirectObjectHolder { +@@ -42,10 +41,10 @@ class TestHolder final : public CPDF_IndirectObjectHolder { + Available, + }; + TestHolder() : validator_(pdfium::MakeRetain()) {} +- ~TestHolder() override {} ++ ~TestHolder() override = default; + + // CPDF_IndirectObjectHolder overrides: +- CPDF_Object* GetOrParseIndirectObject(uint32_t objnum) override { ++ RetainPtr ParseIndirectObject(uint32_t objnum) override { + auto it = objects_data_.find(objnum); + if (it == objects_data_.end()) + return nullptr; +@@ -55,7 +54,7 @@ class TestHolder final : public CPDF_IndirectObjectHolder { + validator_->SimulateReadError(); + return nullptr; + } +- return obj_data.object.Get(); ++ return obj_data.object; + } + + RetainPtr GetValidator() { return validator_; } +@@ -66,13 +65,13 @@ class TestHolder final : public CPDF_IndirectObjectHolder { + ObjectData object_data; + object_data.object = std::move(object); + object_data.state = state; +- ASSERT(objects_data_.find(objnum) == objects_data_.end()); ++ DCHECK(objects_data_.find(objnum) == objects_data_.end()); + objects_data_[objnum] = std::move(object_data); + } + + void SetObjectState(uint32_t objnum, ObjectState state) { + auto it = objects_data_.find(objnum); +- ASSERT(it != objects_data_.end()); ++ DCHECK(it != objects_data_.end()); + ObjectData& obj_data = it->second; + obj_data.state = state; + } +@@ -95,22 +94,23 @@ class TestHolder final : public CPDF_IndirectObjectHolder { + + } // namespace + +-TEST(CPDF_PageObjectAvailTest, ExcludePages) { ++TEST(PageObjectAvailTest, ExcludePages) { + TestHolder holder; + holder.AddObject(1, pdfium::MakeRetain(), + TestHolder::ObjectState::Available); +- holder.GetTestObject(1)->GetDict()->SetNewFor("Kids", &holder, +- 2); ++ holder.GetTestObject(1)->GetMutableDict()->SetNewFor( ++ "Kids", &holder, 2); + holder.AddObject(2, pdfium::MakeRetain(), + TestHolder::ObjectState::Available); +- holder.GetTestObject(2)->AsArray()->AddNew(&holder, 3); ++ holder.GetTestObject(2)->AsMutableArray()->AppendNew(&holder, ++ 3); + + holder.AddObject(3, pdfium::MakeRetain(), + TestHolder::ObjectState::Available); +- holder.GetTestObject(3)->GetDict()->SetFor( +- "Type", pdfium::MakeRetain(nullptr, "Page", false)); +- holder.GetTestObject(3)->GetDict()->SetNewFor("OtherPageData", +- &holder, 4); ++ holder.GetTestObject(3)->GetMutableDict()->SetFor( ++ "Type", pdfium::MakeRetain(nullptr, "Page")); ++ holder.GetTestObject(3)->GetMutableDict()->SetNewFor( ++ "OtherPageData", &holder, 4); + // Add unavailable object related to other page. + holder.AddObject( + 4, pdfium::MakeRetain(nullptr, "Other page data", false), +@@ -119,5 +119,5 @@ TEST(CPDF_PageObjectAvailTest, ExcludePages) { + CPDF_PageObjectAvail avail(holder.GetValidator(), &holder, 1); + // Now object should be available, although the object '4' is not available, + // because it is in skipped other page. +- EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail()); ++ EXPECT_EQ(CPDF_DataAvail::kDataAvailable, avail.CheckAvail()); + } +diff --git a/core/fpdfapi/parser/cpdf_parser.cpp b/core/fpdfapi/parser/cpdf_parser.cpp +index 3e2819bbb..302aec1a1 100644 +--- a/core/fpdfapi/parser/cpdf_parser.cpp ++++ b/core/fpdfapi/parser/cpdf_parser.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,9 @@ + + #include "core/fpdfapi/parser/cpdf_parser.h" + ++#include ++#include ++ + #include + #include + #include +@@ -25,10 +28,15 @@ + #include "core/fpdfapi/parser/cpdf_syntax_parser.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fxcrt/autorestorer.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxcrt/scoped_set_insertion.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/notreached.h" ++#include "third_party/base/span.h" + + namespace { + +@@ -39,13 +47,99 @@ const int32_t kMaxXRefSize = 1048576; + // "%PDF-1.7\n" + constexpr FX_FILESIZE kPDFHeaderSize = 9; + +-uint32_t GetVarInt(const uint8_t* p, int32_t n) { ++// The required number of fields in a /W array in a cross-reference stream ++// dictionary. ++constexpr size_t kMinFieldCount = 3; ++ ++// V4 trailers are inline. ++constexpr uint32_t kNoV4TrailerObjectNumber = 0; ++ ++struct CrossRefV5IndexEntry { ++ uint32_t start_obj_num; ++ uint32_t obj_count; ++}; ++ ++CPDF_Parser::ObjectType GetObjectTypeFromCrossRefStreamType( ++ uint32_t cross_ref_stream_type) { ++ switch (cross_ref_stream_type) { ++ case 0: ++ return CPDF_Parser::ObjectType::kFree; ++ case 1: ++ return CPDF_Parser::ObjectType::kNotCompressed; ++ case 2: ++ return CPDF_Parser::ObjectType::kCompressed; ++ default: ++ return CPDF_Parser::ObjectType::kNull; ++ } ++} ++ ++// Use the Get*XRefStreamEntry() functions below, instead of calling this ++// directly. ++uint32_t GetVarInt(pdfium::span input) { + uint32_t result = 0; +- for (int32_t i = 0; i < n; ++i) +- result = result * 256 + p[i]; ++ for (uint8_t c : input) ++ result = result * 256 + c; + return result; + } + ++// The following 3 functions retrieve variable length entries from ++// cross-reference streams, as described in ISO 32000-1:2008 table 18. There are ++// only 3 fields for any given entry. ++uint32_t GetFirstXRefStreamEntry(pdfium::span entry_span, ++ pdfium::span field_widths) { ++ return GetVarInt(entry_span.first(field_widths[0])); ++} ++ ++uint32_t GetSecondXRefStreamEntry(pdfium::span entry_span, ++ pdfium::span field_widths) { ++ return GetVarInt(entry_span.subspan(field_widths[0], field_widths[1])); ++} ++ ++uint32_t GetThirdXRefStreamEntry(pdfium::span entry_span, ++ pdfium::span field_widths) { ++ return GetVarInt( ++ entry_span.subspan(field_widths[0] + field_widths[1], field_widths[2])); ++} ++ ++std::vector GetCrossRefV5Indices(const CPDF_Array* array, ++ uint32_t size) { ++ std::vector indices; ++ if (array) { ++ for (size_t i = 0; i < array->size() / 2; i++) { ++ RetainPtr pStartNumObj = array->GetNumberAt(i * 2); ++ if (!pStartNumObj) ++ continue; ++ ++ RetainPtr pCountObj = array->GetNumberAt(i * 2 + 1); ++ if (!pCountObj) ++ continue; ++ ++ int nStartNum = pStartNumObj->GetInteger(); ++ int nCount = pCountObj->GetInteger(); ++ if (nStartNum < 0 || nCount <= 0) ++ continue; ++ ++ indices.push_back( ++ {static_cast(nStartNum), static_cast(nCount)}); ++ } ++ } ++ ++ if (indices.empty()) ++ indices.push_back({0, size}); ++ return indices; ++} ++ ++std::vector GetFieldWidths(const CPDF_Array* array) { ++ std::vector results; ++ if (!array) ++ return results; ++ ++ CPDF_ArrayLocker locker(array); ++ for (const auto& obj : locker) ++ results.push_back(obj->GetInteger()); ++ return results; ++} ++ + class ObjectsHolderStub final : public CPDF_Parser::ParsedObjectsHolder { + public: + ObjectsHolderStub() = default; +@@ -57,18 +151,16 @@ class ObjectsHolderStub final : public CPDF_Parser::ParsedObjectsHolder { + + CPDF_Parser::CPDF_Parser(ParsedObjectsHolder* holder) + : m_pObjectsHolder(holder), +- m_CrossRefTable(pdfium::MakeUnique()) { ++ m_CrossRefTable(std::make_unique()) { + if (!holder) { +- m_pOwnedObjectsHolder = pdfium::MakeUnique(); ++ m_pOwnedObjectsHolder = std::make_unique(); + m_pObjectsHolder = m_pOwnedObjectsHolder.get(); + } + } + + CPDF_Parser::CPDF_Parser() : CPDF_Parser(nullptr) {} + +-CPDF_Parser::~CPDF_Parser() { +- ReleaseEncryptHandler(); +-} ++CPDF_Parser::~CPDF_Parser() = default; + + uint32_t CPDF_Parser::GetLastObjNum() const { + return m_CrossRefTable->objects_info().empty() +@@ -86,7 +178,7 @@ FX_FILESIZE CPDF_Parser::GetObjectPositionOrZero(uint32_t objnum) const { + } + + CPDF_Parser::ObjectType CPDF_Parser::GetObjectType(uint32_t objnum) const { +- ASSERT(IsValidObjectNumber(objnum)); ++ DCHECK(IsValidObjectNumber(objnum)); + const auto* info = m_CrossRefTable->GetObjectInfo(objnum); + return info ? info->type : ObjectType::kFree; + } +@@ -112,15 +204,15 @@ void CPDF_Parser::ShrinkObjectMap(uint32_t size) { + m_CrossRefTable->ShrinkObjectMap(size); + } + +-bool CPDF_Parser::InitSyntaxParser( +- const RetainPtr& validator) { +- const Optional header_offset = GetHeaderOffset(validator); +- if (!header_offset) ++bool CPDF_Parser::InitSyntaxParser(RetainPtr validator) { ++ const absl::optional header_offset = GetHeaderOffset(validator); ++ if (!header_offset.has_value()) + return false; +- if (validator->GetSize() < *header_offset + kPDFHeaderSize) ++ if (validator->GetSize() < header_offset.value() + kPDFHeaderSize) + return false; + +- m_pSyntax = pdfium::MakeUnique(validator, *header_offset); ++ m_pSyntax = std::make_unique(std::move(validator), ++ header_offset.value()); + return ParseFileVersion(); + } + +@@ -130,30 +222,30 @@ bool CPDF_Parser::ParseFileVersion() { + if (!m_pSyntax->GetCharAt(5, ch)) + return false; + +- if (std::isdigit(ch)) ++ if (isdigit(ch)) + m_FileVersion = FXSYS_DecimalCharToInt(static_cast(ch)) * 10; + + if (!m_pSyntax->GetCharAt(7, ch)) + return false; + +- if (std::isdigit(ch)) ++ if (isdigit(ch)) + m_FileVersion += FXSYS_DecimalCharToInt(static_cast(ch)); + return true; + } + + CPDF_Parser::Error CPDF_Parser::StartParse( +- const RetainPtr& pFileAccess, +- const char* password) { +- if (!InitSyntaxParser( +- pdfium::MakeRetain(pFileAccess, nullptr))) ++ RetainPtr pFileAccess, ++ const ByteString& password) { ++ if (!InitSyntaxParser(pdfium::MakeRetain( ++ std::move(pFileAccess), nullptr))) + return FORMAT_ERROR; + SetPassword(password); + return StartParseInternal(); + } + + CPDF_Parser::Error CPDF_Parser::StartParseInternal() { +- ASSERT(!m_bHasParsed); +- ASSERT(!m_bXRefTableRebuilt); ++ DCHECK(!m_bHasParsed); ++ DCHECK(!m_bXRefTableRebuilt); + m_bHasParsed = true; + m_bXRefStream = false; + +@@ -203,7 +295,7 @@ CPDF_Parser::Error CPDF_Parser::StartParseInternal() { + return eRet; + } + if (m_pSecurityHandler && !m_pSecurityHandler->IsMetadataEncrypted()) { +- CPDF_Reference* pMetadata = ++ RetainPtr pMetadata = + ToReference(GetRoot()->GetObjectFor("Metadata")); + if (pMetadata) + m_MetadataObjnum = pMetadata->GetRefObjNum(); +@@ -221,12 +313,12 @@ FX_FILESIZE CPDF_Parser::ParseStartXRef() { + m_pSyntax->GetKeyword(); + + // Read XRef offset. +- bool bNumber; +- const ByteString xref_offset_str = m_pSyntax->GetNextWord(&bNumber); +- if (!bNumber || xref_offset_str.IsEmpty()) ++ const CPDF_SyntaxParser::WordResult xref_offset_result = ++ m_pSyntax->GetNextWord(); ++ if (!xref_offset_result.is_number || xref_offset_result.word.IsEmpty()) + return 0; + +- const FX_SAFE_FILESIZE result = FXSYS_atoi64(xref_offset_str.c_str()); ++ const FX_SAFE_FILESIZE result = FXSYS_atoi64(xref_offset_result.word.c_str()); + if (!result.IsValid() || result.ValueOrDie() >= m_pSyntax->GetDocumentSize()) + return 0; + +@@ -238,11 +330,11 @@ CPDF_Parser::Error CPDF_Parser::SetEncryptHandler() { + if (!GetTrailer()) + return FORMAT_ERROR; + +- const CPDF_Dictionary* pEncryptDict = GetEncryptDict(); ++ RetainPtr pEncryptDict = GetEncryptDict(); + if (!pEncryptDict) + return SUCCESS; + +- if (pEncryptDict->GetStringFor("Filter") != "Standard") ++ if (pEncryptDict->GetNameFor("Filter") != "Standard") + return HANDLER_ERROR; + + auto pSecurityHandler = pdfium::MakeRetain(); +@@ -263,16 +355,15 @@ void CPDF_Parser::ReleaseEncryptHandler() { + // in the cross reference table are all off by one. + bool CPDF_Parser::VerifyCrossRefV4() { + for (const auto& it : m_CrossRefTable->objects_info()) { +- if (it.second.pos == 0) ++ if (it.second.pos <= 0) + continue; + // Find the first non-zero position. + FX_FILESIZE SavedPos = m_pSyntax->GetPos(); + m_pSyntax->SetPos(it.second.pos); +- bool is_num = false; +- ByteString num_str = m_pSyntax->GetNextWord(&is_num); ++ CPDF_SyntaxParser::WordResult word_result = m_pSyntax->GetNextWord(); + m_pSyntax->SetPos(SavedPos); +- if (!is_num || num_str.IsEmpty() || +- FXSYS_atoui(num_str.c_str()) != it.first) { ++ if (!word_result.is_number || word_result.word.IsEmpty() || ++ FXSYS_atoui(word_result.word.c_str()) != it.first) { + // If the object number read doesn't match the one stored, + // something is wrong with the cross reference table. + return false; +@@ -290,50 +381,50 @@ bool CPDF_Parser::LoadAllCrossRefV4(FX_FILESIZE xref_offset) { + if (!trailer) + return false; + +- m_CrossRefTable->SetTrailer(std::move(trailer)); +- int32_t xrefsize = GetDirectInteger(GetTrailer(), "Size"); ++ m_CrossRefTable->SetTrailer(std::move(trailer), kNoV4TrailerObjectNumber); ++ const int32_t xrefsize = GetTrailer()->GetDirectIntegerFor("Size"); + if (xrefsize > 0 && xrefsize <= kMaxXRefSize) + ShrinkObjectMap(xrefsize); + +- std::vector xref_stream_list{ +- GetDirectInteger(GetTrailer(), "XRefStm")}; ++ FX_FILESIZE xref_stm = GetTrailer()->GetDirectIntegerFor("XRefStm"); ++ std::vector xref_stream_list{xref_stm}; + std::vector xref_list{xref_offset}; + std::set seen_xref_offset{xref_offset}; + + // When the trailer doesn't have Prev entry or Prev entry value is not + // numerical, GetDirectInteger() returns 0. Loading will end. +- xref_offset = GetDirectInteger(GetTrailer(), "Prev"); +- while (xref_offset) { ++ xref_offset = GetTrailer()->GetDirectIntegerFor("Prev"); ++ while (xref_offset > 0) { + // Check for circular references. +- if (pdfium::ContainsKey(seen_xref_offset, xref_offset)) ++ if (pdfium::Contains(seen_xref_offset, xref_offset)) + return false; + + seen_xref_offset.insert(xref_offset); ++ xref_list.insert(xref_list.begin(), xref_offset); + + // SLOW ... +- xref_list.insert(xref_list.begin(), xref_offset); + LoadCrossRefV4(xref_offset, true); + + RetainPtr pDict(LoadTrailerV4()); + if (!pDict) + return false; + +- xref_offset = GetDirectInteger(pDict.Get(), "Prev"); ++ xref_offset = pDict->GetDirectIntegerFor("Prev"); ++ xref_stm = pDict->GetIntegerFor("XRefStm"); ++ xref_stream_list.insert(xref_stream_list.begin(), xref_stm); + + // SLOW ... +- xref_stream_list.insert(xref_stream_list.begin(), +- pDict->GetIntegerFor("XRefStm")); +- + m_CrossRefTable = CPDF_CrossRefTable::MergeUp( +- pdfium::MakeUnique(std::move(pDict)), ++ std::make_unique(std::move(pDict), ++ kNoV4TrailerObjectNumber), + std::move(m_CrossRefTable)); + } + + for (size_t i = 0; i < xref_list.size(); ++i) { +- if (!LoadCrossRefV4(xref_list[i], false)) ++ if (xref_list[i] > 0 && !LoadCrossRefV4(xref_list[i], false)) + return false; + +- if (xref_stream_list[i] && !LoadCrossRefV5(&xref_stream_list[i], false)) ++ if (xref_stream_list[i] > 0 && !LoadCrossRefV5(&xref_stream_list[i], false)) + return false; + + if (i == 0 && !VerifyCrossRefV4()) +@@ -351,59 +442,60 @@ bool CPDF_Parser::LoadLinearizedAllCrossRefV4(FX_FILESIZE main_xref_offset) { + return false; + + // GetTrailer() currently returns the first-page trailer. +- if (GetDirectInteger(GetTrailer(), "Size") == 0) ++ if (GetTrailer()->GetDirectIntegerFor("Size") == 0) + return false; + + // Read /XRefStm from the first-page trailer. No need to read /Prev for the + // first-page trailer, as the caller already did that and passed it in as + // |main_xref_offset|. +- std::vector xref_stream_list{ +- GetDirectInteger(GetTrailer(), "XRefStm")}; ++ FX_FILESIZE xref_stm = GetTrailer()->GetDirectIntegerFor("XRefStm"); ++ std::vector xref_stream_list{xref_stm}; + std::vector xref_list{main_xref_offset}; + std::set seen_xref_offset{main_xref_offset}; + + // Merge the trailers. + m_CrossRefTable = CPDF_CrossRefTable::MergeUp( +- pdfium::MakeUnique(std::move(main_trailer)), ++ std::make_unique(std::move(main_trailer), ++ kNoV4TrailerObjectNumber), + std::move(m_CrossRefTable)); + + // Now GetTrailer() returns the merged trailer, where /Prev is from the + // main-trailer. +- FX_FILESIZE xref_offset = GetDirectInteger(GetTrailer(), "Prev"); +- while (xref_offset) { ++ FX_FILESIZE xref_offset = GetTrailer()->GetDirectIntegerFor("Prev"); ++ while (xref_offset > 0) { + // Check for circular references. +- if (pdfium::ContainsKey(seen_xref_offset, xref_offset)) ++ if (pdfium::Contains(seen_xref_offset, xref_offset)) + return false; + + seen_xref_offset.insert(xref_offset); ++ xref_list.insert(xref_list.begin(), xref_offset); + + // SLOW ... +- xref_list.insert(xref_list.begin(), xref_offset); + LoadCrossRefV4(xref_offset, true); + + RetainPtr pDict(LoadTrailerV4()); + if (!pDict) + return false; + +- xref_offset = GetDirectInteger(pDict.Get(), "Prev"); ++ xref_offset = pDict->GetDirectIntegerFor("Prev"); ++ xref_stm = pDict->GetIntegerFor("XRefStm"); ++ xref_stream_list.insert(xref_stream_list.begin(), xref_stm); + + // SLOW ... +- xref_stream_list.insert(xref_stream_list.begin(), +- pDict->GetIntegerFor("XRefStm")); +- + m_CrossRefTable = CPDF_CrossRefTable::MergeUp( +- pdfium::MakeUnique(std::move(pDict)), ++ std::make_unique(std::move(pDict), ++ kNoV4TrailerObjectNumber), + std::move(m_CrossRefTable)); + } + +- if (xref_stream_list[0] && !LoadCrossRefV5(&xref_stream_list[0], false)) ++ if (xref_stream_list[0] > 0 && !LoadCrossRefV5(&xref_stream_list[0], false)) + return false; + + for (size_t i = 1; i < xref_list.size(); ++i) { +- if (!LoadCrossRefV4(xref_list[i], false)) ++ if (xref_list[i] > 0 && !LoadCrossRefV4(xref_list[i], false)) + return false; + +- if (xref_stream_list[i] && !LoadCrossRefV5(&xref_stream_list[i], false)) ++ if (xref_stream_list[i] > 0 && !LoadCrossRefV5(&xref_stream_list[i], false)) + return false; + } + return true; +@@ -419,11 +511,11 @@ bool CPDF_Parser::ParseAndAppendCrossRefSubsectionData( + // Each entry shall be exactly 20 byte. + // A sample entry looks like: + // "0000000000 00007 f\r\n" +- static constexpr int32_t kEntryConstSize = 20; ++ static constexpr int32_t kEntrySize = 20; + + if (!out_objects) { + FX_SAFE_FILESIZE pos = count; +- pos *= kEntryConstSize; ++ pos *= kEntrySize; + pos += m_pSyntax->GetPos(); + if (!pos.IsValid()) + return false; +@@ -439,33 +531,32 @@ bool CPDF_Parser::ParseAndAppendCrossRefSubsectionData( + if (new_size.ValueOrDie() > kMaxXRefSize) + return false; + +- const size_t max_entries_in_file = +- m_pSyntax->GetDocumentSize() / kEntryConstSize; ++ const size_t max_entries_in_file = m_pSyntax->GetDocumentSize() / kEntrySize; + if (new_size.ValueOrDie() > max_entries_in_file) + return false; + + out_objects->resize(new_size.ValueOrDie()); + +- std::vector buf(1024 * kEntryConstSize + 1); ++ DataVector buf(1024 * kEntrySize + 1); + buf.back() = '\0'; + +- uint32_t nBytesToRead = count; +- while (nBytesToRead > 0) { +- const uint32_t block_size = std::min(nBytesToRead, 1024u); +- if (!m_pSyntax->ReadBlock(reinterpret_cast(buf.data()), +- block_size * kEntryConstSize)) { ++ uint32_t entries_to_read = count; ++ while (entries_to_read > 0) { ++ const uint32_t entries_in_block = std::min(entries_to_read, 1024u); ++ const uint32_t bytes_to_read = entries_in_block * kEntrySize; ++ auto block_span = pdfium::make_span(buf).first(bytes_to_read); ++ if (!m_pSyntax->ReadBlock(pdfium::as_writable_bytes(block_span))) + return false; +- } + +- for (uint32_t i = 0; i < block_size; i++) { +- uint32_t iObjectIndex = count - nBytesToRead + i; ++ for (uint32_t i = 0; i < entries_in_block; i++) { ++ uint32_t iObjectIndex = count - entries_to_read + i; + CrossRefObjData& obj_data = + (*out_objects)[start_obj_index + iObjectIndex]; + const uint32_t objnum = start_objnum + iObjectIndex; + obj_data.obj_num = objnum; + ObjectInfo& info = obj_data.info; + +- char* pEntry = &buf[i * kEntryConstSize]; ++ const char* pEntry = &buf[i * kEntrySize]; + if (pEntry[17] == 'f') { + info.pos = 0; + info.type = ObjectType::kFree; +@@ -476,7 +567,7 @@ bool CPDF_Parser::ParseAndAppendCrossRefSubsectionData( + + if (offset.ValueOrDie() == 0) { + for (int32_t c = 0; c < 10; c++) { +- if (!std::isdigit(pEntry[c])) ++ if (!isdigit(pEntry[c])) + return false; + } + } +@@ -490,7 +581,7 @@ bool CPDF_Parser::ParseAndAppendCrossRefSubsectionData( + info.type = ObjectType::kNotCompressed; + } + } +- nBytesToRead -= block_size; ++ entries_to_read -= entries_in_block; + } + return true; + } +@@ -502,14 +593,14 @@ bool CPDF_Parser::ParseCrossRefV4(std::vector* out_objects) { + if (m_pSyntax->GetKeyword() != "xref") + return false; + std::vector result_objects; +- while (1) { ++ while (true) { + FX_FILESIZE saved_pos = m_pSyntax->GetPos(); +- bool bIsNumber; +- ByteString word = m_pSyntax->GetNextWord(&bIsNumber); ++ CPDF_SyntaxParser::WordResult word_result = m_pSyntax->GetNextWord(); ++ const ByteString& word = word_result.word; + if (word.IsEmpty()) + return false; + +- if (!bIsNumber) { ++ if (!word_result.is_number) { + m_pSyntax->SetPos(saved_pos); + break; + } +@@ -554,7 +645,8 @@ void CPDF_Parser::MergeCrossRefObjectsData( + m_CrossRefTable->AddNormal(obj.obj_num, obj.info.gennum, obj.info.pos); + break; + case ObjectType::kCompressed: +- m_CrossRefTable->AddCompressed(obj.obj_num, obj.info.archive_obj_num); ++ m_CrossRefTable->AddCompressed(obj.obj_num, obj.info.archive.obj_num, ++ obj.info.archive.obj_index); + break; + default: + NOTREACHED(); +@@ -567,13 +659,13 @@ bool CPDF_Parser::LoadAllCrossRefV5(FX_FILESIZE xref_offset) { + return false; + + std::set seen_xref_offset; +- while (xref_offset) { ++ while (xref_offset > 0) { + seen_xref_offset.insert(xref_offset); + if (!LoadCrossRefV5(&xref_offset, false)) + return false; + + // Check for circular references. +- if (pdfium::ContainsKey(seen_xref_offset, xref_offset)) ++ if (pdfium::Contains(seen_xref_offset, xref_offset)) + return false; + } + m_ObjectStreamMap.clear(); +@@ -582,17 +674,17 @@ bool CPDF_Parser::LoadAllCrossRefV5(FX_FILESIZE xref_offset) { + } + + bool CPDF_Parser::RebuildCrossRef() { +- auto cross_ref_table = pdfium::MakeUnique(); ++ auto cross_ref_table = std::make_unique(); + + const uint32_t kBufferSize = 4096; + m_pSyntax->SetReadBufferSize(kBufferSize); + m_pSyntax->SetPos(0); + +- bool bIsNumber; + std::vector> numbers; +- for (ByteString word = m_pSyntax->GetNextWord(&bIsNumber); !word.IsEmpty(); +- word = m_pSyntax->GetNextWord(&bIsNumber)) { +- if (bIsNumber) { ++ for (CPDF_SyntaxParser::WordResult result = m_pSyntax->GetNextWord(); ++ !result.word.IsEmpty(); result = m_pSyntax->GetNextWord()) { ++ const ByteString& word = result.word; ++ if (result.is_number) { + numbers.emplace_back(FXSYS_atoui(word.c_str()), + m_pSyntax->GetPos() - word.GetLength()); + if (numbers.size() > 2u) +@@ -607,11 +699,17 @@ bool CPDF_Parser::RebuildCrossRef() { + } else if (word == "trailer") { + RetainPtr pTrailer = m_pSyntax->GetObjectBody(nullptr); + if (pTrailer) { ++ CPDF_Stream* stream_trailer = pTrailer->AsMutableStream(); ++ // Grab the object number from `pTrailer` before potentially calling ++ // std::move(pTrailer) below. ++ const uint32_t trailer_object_number = pTrailer->GetObjNum(); ++ RetainPtr trailer_dict = ++ stream_trailer ? stream_trailer->GetMutableDict() ++ : ToDictionary(std::move(pTrailer)); + cross_ref_table = CPDF_CrossRefTable::MergeUp( + std::move(cross_ref_table), +- pdfium::MakeUnique(ToDictionary( +- pTrailer->IsStream() ? pTrailer->AsStream()->GetDict()->Clone() +- : std::move(pTrailer)))); ++ std::make_unique(std::move(trailer_dict), ++ trailer_object_number)); + } + } else if (word == "obj" && numbers.size() == 2u) { + const FX_FILESIZE obj_pos = numbers[0].second; +@@ -623,20 +721,24 @@ bool CPDF_Parser::RebuildCrossRef() { + ToStream(m_pSyntax->GetIndirectObject( + nullptr, CPDF_SyntaxParser::ParseType::kStrict)); + +- if (pStream && pStream->GetDict()->GetStringFor("Type") == "XRef") { ++ if (pStream && pStream->GetDict()->GetNameFor("Type") == "XRef") { + cross_ref_table = CPDF_CrossRefTable::MergeUp( + std::move(cross_ref_table), +- pdfium::MakeUnique( +- ToDictionary(pStream->GetDict()->Clone()))); ++ std::make_unique( ++ ToDictionary(pStream->GetDict()->Clone()), ++ pStream->GetObjNum())); + } + + if (obj_num < kMaxObjectNumber) { + cross_ref_table->AddNormal(obj_num, gen_num, obj_pos); +- if (const auto object_stream = +- CPDF_ObjectStream::Create(pStream.Get())) { +- for (const auto& it : object_stream->objects_offsets()) { +- if (it.first < kMaxObjectNumber) +- cross_ref_table->AddCompressed(it.first, obj_num); ++ const auto object_stream = ++ CPDF_ObjectStream::Create(std::move(pStream)); ++ if (object_stream) { ++ const auto& object_info = object_stream->object_info(); ++ for (size_t i = 0; i < object_info.size(); ++i) { ++ const auto& info = object_info[i]; ++ if (info.obj_num < kMaxObjectNumber) ++ cross_ref_table->AddCompressed(info.obj_num, obj_num, i); + } + } + } +@@ -657,161 +759,153 @@ bool CPDF_Parser::LoadCrossRefV5(FX_FILESIZE* pos, bool bMainXRef) { + if (!pObject || !pObject->GetObjNum()) + return false; + +- CPDF_Stream* pStream = pObject->AsStream(); ++ RetainPtr pStream(pObject->AsStream()); + if (!pStream) + return false; + +- CPDF_Dictionary* pDict = pStream->GetDict(); +- *pos = pDict->GetIntegerFor("Prev"); ++ RetainPtr pDict = pStream->GetDict(); ++ int32_t prev = pDict->GetIntegerFor("Prev"); ++ if (prev < 0) ++ return false; ++ + int32_t size = pDict->GetIntegerFor("Size"); + if (size < 0) + return false; + ++ *pos = prev; ++ + RetainPtr pNewTrailer = ToDictionary(pDict->Clone()); + if (bMainXRef) { +- m_CrossRefTable = +- pdfium::MakeUnique(std::move(pNewTrailer)); ++ m_CrossRefTable = std::make_unique( ++ std::move(pNewTrailer), pStream->GetObjNum()); + m_CrossRefTable->ShrinkObjectMap(size); + } else { + m_CrossRefTable = CPDF_CrossRefTable::MergeUp( +- pdfium::MakeUnique(std::move(pNewTrailer)), ++ std::make_unique(std::move(pNewTrailer), ++ pStream->GetObjNum()), + std::move(m_CrossRefTable)); + } + +- std::vector> arrIndex; +- CPDF_Array* pArray = pDict->GetArrayFor("Index"); +- if (pArray) { +- for (size_t i = 0; i < pArray->size() / 2; i++) { +- CPDF_Object* pStartNumObj = pArray->GetObjectAt(i * 2); +- CPDF_Object* pCountObj = pArray->GetObjectAt(i * 2 + 1); +- +- if (ToNumber(pStartNumObj) && ToNumber(pCountObj)) { +- int nStartNum = pStartNumObj->GetInteger(); +- int nCount = pCountObj->GetInteger(); +- if (nStartNum >= 0 && nCount > 0) +- arrIndex.push_back(std::make_pair(nStartNum, nCount)); +- } +- } +- } ++ std::vector indices = ++ GetCrossRefV5Indices(pDict->GetArrayFor("Index").Get(), size); + +- if (arrIndex.empty()) +- arrIndex.push_back(std::make_pair(0, size)); +- +- pArray = pDict->GetArrayFor("W"); +- if (!pArray) ++ std::vector field_widths = ++ GetFieldWidths(pDict->GetArrayFor("W").Get()); ++ if (field_widths.size() < kMinFieldCount) + return false; + +- std::vector WidthArray; +- FX_SAFE_UINT32 dwAccWidth = 0; +- for (size_t i = 0; i < pArray->size(); ++i) { +- WidthArray.push_back(pArray->GetIntegerAt(i)); +- dwAccWidth += WidthArray[i]; +- } +- +- if (!dwAccWidth.IsValid() || WidthArray.size() < 3) ++ FX_SAFE_UINT32 dwAccWidth; ++ for (uint32_t width : field_widths) ++ dwAccWidth += width; ++ if (!dwAccWidth.IsValid()) + return false; + +- uint32_t totalWidth = dwAccWidth.ValueOrDie(); +- auto pAcc = pdfium::MakeRetain(pStream); ++ uint32_t total_width = dwAccWidth.ValueOrDie(); ++ auto pAcc = pdfium::MakeRetain(std::move(pStream)); + pAcc->LoadAllDataFiltered(); + +- const uint8_t* pData = pAcc->GetData(); +- uint32_t dwTotalSize = pAcc->GetSize(); ++ pdfium::span data_span = pAcc->GetSpan(); + uint32_t segindex = 0; +- for (const auto& index : arrIndex) { +- const int32_t startnum = index.first; +- if (startnum < 0) ++ for (const auto& index : indices) { ++ FX_SAFE_UINT32 seg_end = segindex; ++ seg_end += index.obj_count; ++ seg_end *= total_width; ++ if (!seg_end.IsValid() || seg_end.ValueOrDie() > data_span.size()) + continue; + +- uint32_t count = pdfium::base::checked_cast(index.second); +- FX_SAFE_UINT32 dwCaculatedSize = segindex; +- dwCaculatedSize += count; +- dwCaculatedSize *= totalWidth; +- if (!dwCaculatedSize.IsValid() || +- dwCaculatedSize.ValueOrDie() > dwTotalSize) { +- continue; +- } +- +- const uint8_t* segstart = pData + segindex * totalWidth; +- FX_SAFE_UINT32 dwMaxObjNum = startnum; +- dwMaxObjNum += count; ++ pdfium::span seg_span = data_span.subspan( ++ segindex * total_width, index.obj_count * total_width); ++ FX_SAFE_UINT32 dwMaxObjNum = index.start_obj_num; ++ dwMaxObjNum += index.obj_count; + uint32_t dwV5Size = + m_CrossRefTable->objects_info().empty() ? 0 : GetLastObjNum() + 1; + if (!dwMaxObjNum.IsValid() || dwMaxObjNum.ValueOrDie() > dwV5Size) + continue; + +- for (uint32_t i = 0; i < count; i++) { +- ObjectType type = ObjectType::kNotCompressed; +- const uint8_t* entrystart = segstart + i * totalWidth; +- if (WidthArray[0]) { +- const uint32_t cross_ref_stream_obj_type = +- GetVarInt(entrystart, WidthArray[0]); +- type = GetObjectTypeFromCrossRefStreamType(cross_ref_stream_obj_type); +- if (type == ObjectType::kNull) +- continue; +- } ++ for (uint32_t i = 0; i < index.obj_count; ++i) { ++ const uint32_t obj_num = index.start_obj_num + i; ++ if (obj_num >= CPDF_Parser::kMaxObjectNumber) ++ break; + +- const uint32_t objnum = startnum + i; +- if (objnum >= CPDF_Parser::kMaxObjectNumber) +- continue; ++ ProcessCrossRefV5Entry(seg_span.subspan(i * total_width, total_width), ++ field_widths, obj_num); ++ } + +- const ObjectType existing_type = GetObjectType(objnum); +- if (existing_type == ObjectType::kNull) { +- uint32_t offset = GetVarInt(entrystart + WidthArray[0], WidthArray[1]); +- if (pdfium::base::IsValueInRangeForNumericType(offset)) +- m_CrossRefTable->AddNormal(objnum, 0, offset); +- continue; +- } ++ segindex += index.obj_count; ++ } ++ return true; ++} + +- if (existing_type != ObjectType::kFree) +- continue; ++void CPDF_Parser::ProcessCrossRefV5Entry( ++ pdfium::span entry_span, ++ pdfium::span field_widths, ++ uint32_t obj_num) { ++ DCHECK_GE(field_widths.size(), kMinFieldCount); ++ ObjectType type = ObjectType::kNotCompressed; ++ if (field_widths[0]) { ++ const uint32_t cross_ref_stream_obj_type = ++ GetFirstXRefStreamEntry(entry_span, field_widths); ++ type = GetObjectTypeFromCrossRefStreamType(cross_ref_stream_obj_type); ++ if (type == ObjectType::kNull) ++ return; ++ } + +- if (type == ObjectType::kFree) { +- m_CrossRefTable->SetFree(objnum); +- continue; +- } ++ const ObjectType existing_type = GetObjectType(obj_num); ++ if (existing_type == ObjectType::kNull) { ++ const uint32_t offset = GetSecondXRefStreamEntry(entry_span, field_widths); ++ if (pdfium::base::IsValueInRangeForNumericType(offset)) ++ m_CrossRefTable->AddNormal(obj_num, 0, offset); ++ return; ++ } + +- const uint32_t entry_value = +- GetVarInt(entrystart + WidthArray[0], WidthArray[1]); +- if (type == ObjectType::kNotCompressed) { +- const uint32_t offset = entry_value; +- if (pdfium::base::IsValueInRangeForNumericType(offset)) +- m_CrossRefTable->AddNormal(objnum, 0, offset); +- continue; +- } ++ if (existing_type != ObjectType::kFree) ++ return; + +- ASSERT(type == ObjectType::kCompressed); +- const uint32_t archive_obj_num = entry_value; +- if (!IsValidObjectNumber(archive_obj_num)) +- return false; ++ if (type == ObjectType::kFree) { ++ m_CrossRefTable->SetFree(obj_num); ++ return; ++ } + +- m_CrossRefTable->AddCompressed(objnum, archive_obj_num); +- } +- segindex += count; ++ if (type == ObjectType::kNotCompressed) { ++ const uint32_t offset = GetSecondXRefStreamEntry(entry_span, field_widths); ++ if (pdfium::base::IsValueInRangeForNumericType(offset)) ++ m_CrossRefTable->AddNormal(obj_num, 0, offset); ++ return; + } +- return true; ++ ++ DCHECK_EQ(type, ObjectType::kCompressed); ++ const uint32_t archive_obj_num = ++ GetSecondXRefStreamEntry(entry_span, field_widths); ++ if (!IsValidObjectNumber(archive_obj_num)) { ++ return; ++ } ++ ++ const uint32_t archive_obj_index = ++ GetThirdXRefStreamEntry(entry_span, field_widths); ++ m_CrossRefTable->AddCompressed(obj_num, archive_obj_num, archive_obj_index); + } + +-const CPDF_Array* CPDF_Parser::GetIDArray() const { ++RetainPtr CPDF_Parser::GetIDArray() const { + return GetTrailer() ? GetTrailer()->GetArrayFor("ID") : nullptr; + } + +-CPDF_Dictionary* CPDF_Parser::GetRoot() const { +- CPDF_Object* obj = ++RetainPtr CPDF_Parser::GetRoot() const { ++ RetainPtr obj = + m_pObjectsHolder->GetOrParseIndirectObject(GetRootObjNum()); + return obj ? obj->GetDict() : nullptr; + } + +-const CPDF_Dictionary* CPDF_Parser::GetEncryptDict() const { ++RetainPtr CPDF_Parser::GetEncryptDict() const { + if (!GetTrailer()) + return nullptr; + +- const CPDF_Object* pEncryptObj = GetTrailer()->GetObjectFor("Encrypt"); ++ RetainPtr pEncryptObj = ++ GetTrailer()->GetObjectFor("Encrypt"); + if (!pEncryptObj) + return nullptr; + + if (pEncryptObj->IsDictionary()) +- return ToDictionary(pEncryptObj); ++ return pdfium::WrapRetain(pEncryptObj->AsDictionary()); + + if (pEncryptObj->IsReference()) { + return ToDictionary(m_pObjectsHolder->GetOrParseIndirectObject( +@@ -832,6 +926,10 @@ CPDF_Dictionary* CPDF_Parser::GetMutableTrailerForTesting() { + return m_CrossRefTable->GetMutableTrailerForTesting(); + } + ++uint32_t CPDF_Parser::GetTrailerObjectNumber() const { ++ return m_CrossRefTable->trailer_object_number(); ++} ++ + RetainPtr CPDF_Parser::GetCombinedTrailer() const { + return m_CrossRefTable->trailer() + ? ToDictionary(m_CrossRefTable->trailer()->Clone()) +@@ -839,7 +937,7 @@ RetainPtr CPDF_Parser::GetCombinedTrailer() const { + } + + uint32_t CPDF_Parser::GetInfoObjNum() const { +- const CPDF_Reference* pRef = ++ RetainPtr pRef = + ToReference(m_CrossRefTable->trailer() + ? m_CrossRefTable->trailer()->GetObjectFor("Info") + : nullptr); +@@ -847,7 +945,7 @@ uint32_t CPDF_Parser::GetInfoObjNum() const { + } + + uint32_t CPDF_Parser::GetRootObjNum() const { +- const CPDF_Reference* pRef = ++ RetainPtr pRef = + ToReference(m_CrossRefTable->trailer() + ? m_CrossRefTable->trailer()->GetObjectFor("Root") + : nullptr); +@@ -859,10 +957,10 @@ RetainPtr CPDF_Parser::ParseIndirectObject(uint32_t objnum) { + return nullptr; + + // Prevent circular parsing the same object. +- if (pdfium::ContainsKey(m_ParsingObjNums, objnum)) ++ if (pdfium::Contains(m_ParsingObjNums, objnum)) + return nullptr; + +- pdfium::ScopedSetInsertion local_insert(&m_ParsingObjNums, objnum); ++ ScopedSetInsertion local_insert(&m_ParsingObjNums, objnum); + if (GetObjectType(objnum) == ObjectType::kNotCompressed) { + FX_FILESIZE pos = GetObjectPositionOrZero(objnum); + if (pos <= 0) +@@ -872,22 +970,20 @@ RetainPtr CPDF_Parser::ParseIndirectObject(uint32_t objnum) { + if (GetObjectType(objnum) != ObjectType::kCompressed) + return nullptr; + +- const CPDF_ObjectStream* pObjStream = +- GetObjectStream(m_CrossRefTable->GetObjectInfo(objnum)->archive_obj_num); ++ const ObjectInfo& info = *m_CrossRefTable->GetObjectInfo(objnum); ++ const CPDF_ObjectStream* pObjStream = GetObjectStream(info.archive.obj_num); + if (!pObjStream) + return nullptr; + +- return pObjStream->ParseObject(m_pObjectsHolder.Get(), objnum); ++ return pObjStream->ParseObject(m_pObjectsHolder, objnum, ++ info.archive.obj_index); + } + + const CPDF_ObjectStream* CPDF_Parser::GetObjectStream(uint32_t object_number) { + // Prevent circular parsing the same object. +- if (pdfium::ContainsKey(m_ParsingObjNums, object_number)) ++ if (pdfium::Contains(m_ParsingObjNums, object_number)) + return nullptr; + +- pdfium::ScopedSetInsertion local_insert(&m_ParsingObjNums, +- object_number); +- + auto it = m_ObjectStreamMap.find(object_number); + if (it != m_ObjectStreamMap.end()) + return it->second.get(); +@@ -900,13 +996,16 @@ const CPDF_ObjectStream* CPDF_Parser::GetObjectStream(uint32_t object_number) { + if (object_pos <= 0) + return nullptr; + ++ // Keep track of `object_number` before doing more parsing. ++ ScopedSetInsertion local_insert(&m_ParsingObjNums, object_number); ++ + RetainPtr object = + ParseIndirectObjectAt(object_pos, object_number); + if (!object) + return nullptr; + + std::unique_ptr objs_stream = +- CPDF_ObjectStream::Create(ToStream(object.Get())); ++ CPDF_ObjectStream::Create(ToStream(object)); + const CPDF_ObjectStream* result = objs_stream.get(); + m_ObjectStreamMap[object_number] = std::move(objs_stream); + +@@ -919,7 +1018,7 @@ RetainPtr CPDF_Parser::ParseIndirectObjectAt(FX_FILESIZE pos, + m_pSyntax->SetPos(pos); + + auto result = m_pSyntax->GetIndirectObject( +- m_pObjectsHolder.Get(), CPDF_SyntaxParser::ParseType::kLoose); ++ m_pObjectsHolder, CPDF_SyntaxParser::ParseType::kLoose); + m_pSyntax->SetPos(saved_pos); + if (result && objnum && result->GetObjNum() != objnum) + return nullptr; +@@ -934,11 +1033,15 @@ RetainPtr CPDF_Parser::ParseIndirectObjectAt(FX_FILESIZE pos, + return result; + } + ++FX_FILESIZE CPDF_Parser::GetDocumentSize() const { ++ return m_pSyntax->GetDocumentSize(); ++} ++ + uint32_t CPDF_Parser::GetFirstPageNo() const { + return m_pLinearized ? m_pLinearized->GetFirstPageNo() : 0; + } + +-void CPDF_Parser::SetLinearizedHeader( ++void CPDF_Parser::SetLinearizedHeaderForTesting( + std::unique_ptr pLinearized) { + m_pLinearized = std::move(pLinearized); + } +@@ -947,7 +1050,7 @@ RetainPtr CPDF_Parser::LoadTrailerV4() { + if (m_pSyntax->GetKeyword() != "trailer") + return nullptr; + +- return ToDictionary(m_pSyntax->GetObjectBody(m_pObjectsHolder.Get())); ++ return ToDictionary(m_pSyntax->GetObjectBody(m_pObjectsHolder)); + } + + uint32_t CPDF_Parser::GetPermissions() const { +@@ -959,15 +1062,15 @@ std::unique_ptr CPDF_Parser::ParseLinearizedHeader() { + } + + CPDF_Parser::Error CPDF_Parser::StartLinearizedParse( +- const RetainPtr& validator, +- const char* password) { +- ASSERT(!m_bHasParsed); +- ASSERT(!m_bXRefTableRebuilt); ++ RetainPtr validator, ++ const ByteString& password) { ++ DCHECK(!m_bHasParsed); ++ DCHECK(!m_bXRefTableRebuilt); + SetPassword(password); + m_bXRefStream = false; + m_LastXRefOffset = 0; + +- if (!InitSyntaxParser(validator)) ++ if (!InitSyntaxParser(std::move(validator))) + return FORMAT_ERROR; + + m_pLinearized = ParseLinearizedHeader(); +@@ -991,10 +1094,16 @@ CPDF_Parser::Error CPDF_Parser::StartLinearizedParse( + if (!trailer) + return SUCCESS; + +- m_CrossRefTable->SetTrailer(std::move(trailer)); +- int32_t xrefsize = GetDirectInteger(GetTrailer(), "Size"); +- if (xrefsize > 0) +- ShrinkObjectMap(xrefsize); ++ m_CrossRefTable->SetTrailer(std::move(trailer), kNoV4TrailerObjectNumber); ++ const int32_t xrefsize = GetTrailer()->GetDirectIntegerFor("Size"); ++ if (xrefsize > 0) { ++ // Check if `xrefsize` is correct. If it is incorrect, give up and rebuild ++ // the xref table. ++ const uint32_t expected_last_obj_num = xrefsize - 1; ++ if (GetLastObjNum() != expected_last_obj_num && !RebuildCrossRef()) { ++ return FORMAT_ERROR; ++ } ++ } + } + + Error eRet = SetEncryptHandler(); +@@ -1029,8 +1138,9 @@ CPDF_Parser::Error CPDF_Parser::StartLinearizedParse( + } + + if (m_pSecurityHandler && m_pSecurityHandler->IsMetadataEncrypted()) { +- if (CPDF_Reference* pMetadata = +- ToReference(GetRoot()->GetObjectFor("Metadata"))) ++ RetainPtr pMetadata = ++ ToReference(GetRoot()->GetObjectFor("Metadata")); ++ if (pMetadata) + m_MetadataObjnum = pMetadata->GetRefObjNum(); + } + return SUCCESS; +@@ -1048,7 +1158,7 @@ bool CPDF_Parser::LoadLinearizedAllCrossRefV5(FX_FILESIZE main_xref_offset) { + return false; + + // Check for circular references. +- if (pdfium::ContainsKey(seen_xref_offset, xref_offset)) ++ if (pdfium::Contains(seen_xref_offset, xref_offset)) + return false; + } + m_ObjectStreamMap.clear(); +@@ -1078,16 +1188,69 @@ CPDF_Parser::Error CPDF_Parser::LoadLinearizedMainXRefTable() { + return SUCCESS; + } + +-CPDF_Parser::ObjectType CPDF_Parser::GetObjectTypeFromCrossRefStreamType( +- uint32_t cross_ref_stream_type) const { +- switch (cross_ref_stream_type) { +- case 0: +- return CPDF_Parser::ObjectType::kFree; +- case 1: +- return CPDF_Parser::ObjectType::kNotCompressed; +- case 2: +- return CPDF_Parser::ObjectType::kCompressed; +- default: +- return CPDF_Parser::ObjectType::kNull; ++void CPDF_Parser::SetSyntaxParserForTesting( ++ std::unique_ptr parser) { ++ m_pSyntax = std::move(parser); ++} ++ ++std::vector CPDF_Parser::GetTrailerEnds() { ++ std::vector trailer_ends; ++ m_pSyntax->SetTrailerEnds(&trailer_ends); ++ ++ // Traverse the document. ++ m_pSyntax->SetPos(0); ++ while (true) { ++ CPDF_SyntaxParser::WordResult word_result = m_pSyntax->GetNextWord(); ++ if (word_result.is_number) { ++ // The object number was read. Read the generation number. ++ word_result = m_pSyntax->GetNextWord(); ++ if (!word_result.is_number) ++ break; ++ ++ word_result = m_pSyntax->GetNextWord(); ++ if (word_result.word != "obj") ++ break; ++ ++ m_pSyntax->GetObjectBody(nullptr); ++ ++ word_result = m_pSyntax->GetNextWord(); ++ if (word_result.word != "endobj") ++ break; ++ } else if (word_result.word == "trailer") { ++ m_pSyntax->GetObjectBody(nullptr); ++ } else if (word_result.word == "startxref") { ++ m_pSyntax->GetNextWord(); ++ } else if (word_result.word == "xref") { ++ while (true) { ++ word_result = m_pSyntax->GetNextWord(); ++ if (word_result.word.IsEmpty() || word_result.word == "startxref") ++ break; ++ } ++ m_pSyntax->GetNextWord(); ++ } else { ++ break; ++ } + } ++ ++ // Stop recording trailer ends. ++ m_pSyntax->SetTrailerEnds(nullptr); ++ return trailer_ends; ++} ++ ++bool CPDF_Parser::WriteToArchive(IFX_ArchiveStream* archive, ++ FX_FILESIZE src_size) { ++ static constexpr FX_FILESIZE kBufferSize = 4096; ++ DataVector buffer(kBufferSize); ++ m_pSyntax->SetPos(0); ++ while (src_size) { ++ const uint32_t block_size = ++ static_cast(std::min(kBufferSize, src_size)); ++ auto block_span = pdfium::make_span(buffer).first(block_size); ++ if (!m_pSyntax->ReadBlock(block_span)) ++ return false; ++ if (!archive->WriteBlock(pdfium::make_span(buffer).first(block_size))) ++ return false; ++ src_size -= block_size; ++ } ++ return true; + } +diff --git a/core/fpdfapi/parser/cpdf_parser.h b/core/fpdfapi/parser/cpdf_parser.h +index d44244a17..21dd8aaa1 100644 +--- a/core/fpdfapi/parser/cpdf_parser.h ++++ b/core/fpdfapi/parser/cpdf_parser.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,9 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_PARSER_H_ + #define CORE_FPDFAPI_PARSER_CPDF_PARSER_H_ + ++#include ++#include ++ + #include + #include + #include +@@ -15,13 +18,12 @@ + + #include "core/fpdfapi/parser/cpdf_cross_ref_table.h" + #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/fx_types.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + + class CPDF_Array; +-class CPDF_CryptoHandler; + class CPDF_Dictionary; + class CPDF_LinearizedHeader; + class CPDF_Object; +@@ -29,10 +31,14 @@ class CPDF_ObjectStream; + class CPDF_ReadValidator; + class CPDF_SecurityHandler; + class CPDF_SyntaxParser; ++class IFX_ArchiveStream; + class IFX_SeekableReadStream; + + class CPDF_Parser { + public: ++ using ObjectType = CPDF_CrossRefTable::ObjectType; ++ using ObjectInfo = CPDF_CrossRefTable::ObjectInfo; ++ + class ParsedObjectsHolder : public CPDF_IndirectObjectHolder { + public: + virtual bool TryInit() = 0; +@@ -53,18 +59,18 @@ class CPDF_Parser { + // are non-consecutive. + static constexpr uint32_t kMaxObjectNumber = 4 * 1024 * 1024; + +- static const size_t kInvalidPos = std::numeric_limits::max(); ++ static constexpr size_t kInvalidPos = std::numeric_limits::max(); + + explicit CPDF_Parser(ParsedObjectsHolder* holder); + CPDF_Parser(); + ~CPDF_Parser(); + +- Error StartParse(const RetainPtr& pFile, +- const char* password); +- Error StartLinearizedParse(const RetainPtr& validator, +- const char* password); ++ Error StartParse(RetainPtr pFile, ++ const ByteString& password); ++ Error StartLinearizedParse(RetainPtr validator, ++ const ByteString& password); + +- void SetPassword(const char* password) { m_Password = password; } ++ void SetPassword(const ByteString& password) { m_Password = password; } + ByteString GetPassword() const { return m_Password; } + + // Take the GetPassword() value and encode it, if necessary, based on the +@@ -73,6 +79,7 @@ class CPDF_Parser { + + const CPDF_Dictionary* GetTrailer() const; + CPDF_Dictionary* GetMutableTrailerForTesting(); ++ uint32_t GetTrailerObjectNumber() const; + + // Returns a new trailer which combines the last read trailer with the /Root + // and /Info from previous ones. +@@ -83,10 +90,9 @@ class CPDF_Parser { + uint32_t GetPermissions() const; + uint32_t GetRootObjNum() const; + uint32_t GetInfoObjNum() const; +- const CPDF_Array* GetIDArray() const; +- CPDF_Dictionary* GetRoot() const; +- +- const CPDF_Dictionary* GetEncryptDict() const; ++ RetainPtr GetIDArray() const; ++ RetainPtr GetRoot() const; ++ RetainPtr GetEncryptDict() const; + + RetainPtr ParseIndirectObject(uint32_t objnum); + +@@ -105,6 +111,7 @@ class CPDF_Parser { + RetainPtr ParseIndirectObjectAt(FX_FILESIZE pos, + uint32_t objnum); + ++ FX_FILESIZE GetDocumentSize() const; + uint32_t GetFirstPageNo() const; + const CPDF_LinearizedHeader* GetLinearizedHeader() const { + return m_pLinearized.get(); +@@ -116,24 +123,22 @@ class CPDF_Parser { + + bool xref_table_rebuilt() const { return m_bXRefTableRebuilt; } + +- CPDF_SyntaxParser* GetSyntax() const { return m_pSyntax.get(); } ++ std::vector GetTrailerEnds(); ++ bool WriteToArchive(IFX_ArchiveStream* archive, FX_FILESIZE src_size); + +- void SetLinearizedHeader(std::unique_ptr pLinearized); ++ void SetLinearizedHeaderForTesting( ++ std::unique_ptr pLinearized); + + protected: +- using ObjectType = CPDF_CrossRefTable::ObjectType; +- using ObjectInfo = CPDF_CrossRefTable::ObjectInfo; +- + bool LoadCrossRefV4(FX_FILESIZE pos, bool bSkip); + bool RebuildCrossRef(); ++ Error StartParseInternal(); ++ FX_FILESIZE ParseStartXRef(); ++ std::unique_ptr ParseLinearizedHeader(); + +- std::unique_ptr m_pSyntax; ++ void SetSyntaxParserForTesting(std::unique_ptr parser); + + private: +- friend class cpdf_parser_BadStartXrefShouldNotBuildCrossRefTable_Test; +- friend class cpdf_parser_ParseStartXRefWithHeaderOffset_Test; +- friend class cpdf_parser_ParseStartXRef_Test; +- friend class cpdf_parser_ParseLinearizedWithHeaderOffset_Test; + friend class CPDF_DataAvail; + + struct CrossRefObjData { +@@ -141,11 +146,12 @@ class CPDF_Parser { + ObjectInfo info; + }; + +- Error StartParseInternal(); +- FX_FILESIZE ParseStartXRef(); + bool LoadAllCrossRefV4(FX_FILESIZE xref_offset); + bool LoadAllCrossRefV5(FX_FILESIZE xref_offset); + bool LoadCrossRefV5(FX_FILESIZE* pos, bool bMainXRef); ++ void ProcessCrossRefV5Entry(pdfium::span entry_span, ++ pdfium::span field_widths, ++ uint32_t obj_num); + RetainPtr LoadTrailerV4(); + Error SetEncryptHandler(); + void ReleaseEncryptHandler(); +@@ -153,7 +159,6 @@ class CPDF_Parser { + bool LoadLinearizedAllCrossRefV5(FX_FILESIZE main_xref_offset); + Error LoadLinearizedMainXRefTable(); + const CPDF_ObjectStream* GetObjectStream(uint32_t object_number); +- std::unique_ptr ParseLinearizedHeader(); + void ShrinkObjectMap(uint32_t size); + // A simple check whether the cross reference table matches with + // the objects. +@@ -168,13 +173,12 @@ class CPDF_Parser { + bool ParseCrossRefV4(std::vector* out_objects); + void MergeCrossRefObjectsData(const std::vector& objects); + +- bool InitSyntaxParser(const RetainPtr& validator); ++ bool InitSyntaxParser(RetainPtr validator); + bool ParseFileVersion(); + + ObjectType GetObjectType(uint32_t objnum) const; +- ObjectType GetObjectTypeFromCrossRefStreamType( +- uint32_t cross_ref_stream_type) const; + ++ std::unique_ptr m_pSyntax; + std::unique_ptr m_pOwnedObjectsHolder; + UnownedPtr m_pObjectsHolder; + +@@ -182,11 +186,11 @@ class CPDF_Parser { + bool m_bXRefStream = false; + bool m_bXRefTableRebuilt = false; + int m_FileVersion = 0; ++ uint32_t m_MetadataObjnum = 0; + // m_CrossRefTable must be destroyed after m_pSecurityHandler due to the + // ownership of the ID array data. + std::unique_ptr m_CrossRefTable; +- FX_FILESIZE m_LastXRefOffset; +- RetainPtr m_pSecurityHandler; ++ FX_FILESIZE m_LastXRefOffset = 0; + ByteString m_Password; + std::unique_ptr m_pLinearized; + +@@ -196,7 +200,7 @@ class CPDF_Parser { + // All indirect object numbers that are being parsed. + std::set m_ParsingObjNums; + +- uint32_t m_MetadataObjnum = 0; ++ RetainPtr m_pSecurityHandler; + }; + + #endif // CORE_FPDFAPI_PARSER_CPDF_PARSER_H_ +diff --git a/core/fpdfapi/parser/cpdf_parser_embeddertest.cpp b/core/fpdfapi/parser/cpdf_parser_embeddertest.cpp +index 929453405..17ac00f99 100644 +--- a/core/fpdfapi/parser/cpdf_parser_embeddertest.cpp ++++ b/core/fpdfapi/parser/cpdf_parser_embeddertest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,45 +8,45 @@ + + class CPDFParserEmbedderTest : public EmbedderTest {}; + +-TEST_F(CPDFParserEmbedderTest, LoadError_454695) { ++TEST_F(CPDFParserEmbedderTest, LoadErrorBug454695) { + // Test a dictionary with hex string instead of correct content. + // Verify that the defective pdf shouldn't be opened correctly. + EXPECT_FALSE(OpenDocument("bug_454695.pdf")); + } + +-TEST_F(CPDFParserEmbedderTest, Bug_481363) { ++TEST_F(CPDFParserEmbedderTest, Bug481363) { + // Test colorspace object with malformed dictionary. +- EXPECT_TRUE(OpenDocument("bug_481363.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_481363.pdf")); + FPDF_PAGE page = LoadPage(0); +- EXPECT_NE(nullptr, page); ++ EXPECT_TRUE(page); + UnloadPage(page); + } + +-TEST_F(CPDFParserEmbedderTest, Bug_544880) { ++TEST_F(CPDFParserEmbedderTest, Bug544880) { + // Test self referencing /Pages object. +- EXPECT_TRUE(OpenDocument("bug_544880.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_544880.pdf")); + // Shouldn't crash. We don't check the return value here because we get the + // the count from the "/Count 1" in the testcase (at the time of writing) + // rather than the actual count (0). + (void)GetPageCount(); + } + +-TEST_F(CPDFParserEmbedderTest, Bug_325a) { ++TEST_F(CPDFParserEmbedderTest, Bug325a) { + EXPECT_FALSE(OpenDocument("bug_325_a.pdf")); + } + +-TEST_F(CPDFParserEmbedderTest, Bug_325b) { ++TEST_F(CPDFParserEmbedderTest, Bug325b) { + EXPECT_FALSE(OpenDocument("bug_325_b.pdf")); + } + +-TEST_F(CPDFParserEmbedderTest, Bug_602650) { ++TEST_F(CPDFParserEmbedderTest, Bug602650) { + // Test the case that cross reference entries, which are well formed, + // but do not match with the objects. +- EXPECT_TRUE(OpenDocument("bug_602650.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_602650.pdf")); + FPDF_PAGE page = LoadPage(0); +- EXPECT_NE(nullptr, page); ++ EXPECT_TRUE(page); + FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page); +- EXPECT_NE(nullptr, text_page); ++ EXPECT_TRUE(text_page); + // The page should not be blank. + EXPECT_LT(0, FPDFText_CountChars(text_page)); + +@@ -54,29 +54,34 @@ TEST_F(CPDFParserEmbedderTest, Bug_602650) { + UnloadPage(page); + } + +-TEST_F(CPDFParserEmbedderTest, Bug_757705) { +- EXPECT_TRUE(OpenDocument("bug_757705.pdf")); ++TEST_F(CPDFParserEmbedderTest, Bug757705) { ++ ASSERT_TRUE(OpenDocument("bug_757705.pdf")); + } + + TEST_F(CPDFParserEmbedderTest, LoadMainCrossRefTable) { +- EXPECT_TRUE(OpenDocumentLinearized("feature_linearized_loading.pdf")); ++ ASSERT_TRUE(OpenDocumentLinearized("feature_linearized_loading.pdf")); + // To check that main cross ref table is loaded correctly,will be enough to + // check that the second page was correctly loaded. Because it is contains + // crossrefs for second page. + EXPECT_EQ(2, GetPageCount()); + FPDF_PAGE page = LoadPage(1); +- EXPECT_NE(nullptr, page); ++ EXPECT_TRUE(page); + FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page); +- EXPECT_NE(nullptr, text_page); ++ EXPECT_TRUE(text_page); + // The page should not be blank. + EXPECT_LT(0, FPDFText_CountChars(text_page)); + FPDFText_ClosePage(text_page); + UnloadPage(page); + } + +-TEST_F(CPDFParserEmbedderTest, Bug_828049) { +- EXPECT_TRUE(OpenDocument("bug_828049.pdf")); ++TEST_F(CPDFParserEmbedderTest, Bug828049) { ++ ASSERT_TRUE(OpenDocument("bug_828049.pdf")); + FPDF_PAGE page = LoadPage(0); +- EXPECT_NE(nullptr, page); ++ EXPECT_TRUE(page); + UnloadPage(page); + } ++ ++// crbug.com/1191313 ++TEST_F(CPDFParserEmbedderTest, InvalidDictionaryKeys) { ++ ASSERT_TRUE(OpenDocument("bad_dict_keys.pdf")); ++} +diff --git a/core/fpdfapi/parser/cpdf_parser_unittest.cpp b/core/fpdfapi/parser/cpdf_parser_unittest.cpp +index 06328f01a..efde90eb8 100644 +--- a/core/fpdfapi/parser/cpdf_parser_unittest.cpp ++++ b/core/fpdfapi/parser/cpdf_parser_unittest.cpp +@@ -1,23 +1,28 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include "core/fpdfapi/parser/cpdf_parser.h" ++ + #include + #include + #include ++#include + #include + ++#include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_linearized_header.h" + #include "core/fpdfapi/parser/cpdf_object.h" +-#include "core/fpdfapi/parser/cpdf_parser.h" + #include "core/fpdfapi/parser/cpdf_syntax_parser.h" +-#include "core/fxcrt/cfx_readonlymemorystream.h" ++#include "core/fxcrt/cfx_read_only_span_stream.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_stream.h" + #include "core/fxcrt/retain_ptr.h" ++#include "testing/gmock/include/gmock/gmock.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/utils/path_service.h" +-#include "third_party/base/ptr_util.h" ++ ++using testing::Return; + + namespace { + +@@ -27,13 +32,23 @@ CPDF_CrossRefTable::ObjectInfo GetObjInfo(const CPDF_Parser& parser, + return info ? *info : CPDF_CrossRefTable::ObjectInfo(); + } + ++class TestObjectsHolder final : public CPDF_Parser::ParsedObjectsHolder { ++ public: ++ TestObjectsHolder() = default; ++ ~TestObjectsHolder() override = default; ++ ++ // CPDF_Parser::ParsedObjectsHolder: ++ bool TryInit() override { return true; } ++ MOCK_METHOD1(ParseIndirectObject, RetainPtr(uint32_t objnum)); ++}; ++ + } // namespace + + // A wrapper class to help test member functions of CPDF_Parser. + class CPDF_TestParser final : public CPDF_Parser { + public: +- CPDF_TestParser() {} +- ~CPDF_TestParser() {} ++ CPDF_TestParser() : CPDF_Parser(&object_holder_) {} ++ ~CPDF_TestParser() = default; + + // Setup reading from a file and initial states. + bool InitTestFromFile(const char* path) { +@@ -43,15 +58,16 @@ class CPDF_TestParser final : public CPDF_Parser { + return false; + + // For the test file, the header is set at the beginning. +- m_pSyntax = pdfium::MakeUnique(pFileAccess); ++ SetSyntaxParserForTesting( ++ std::make_unique(std::move(pFileAccess))); + return true; + } + + // Setup reading from a buffer and initial states. + bool InitTestFromBufferWithOffset(pdfium::span buffer, + FX_FILESIZE header_offset) { +- m_pSyntax = CPDF_SyntaxParser::CreateForTesting( +- pdfium::MakeRetain(buffer), header_offset); ++ SetSyntaxParserForTesting(CPDF_SyntaxParser::CreateForTesting( ++ pdfium::MakeRetain(buffer), header_offset)); + return true; + } + +@@ -59,17 +75,20 @@ class CPDF_TestParser final : public CPDF_Parser { + return InitTestFromBufferWithOffset(buffer, 0 /*header_offset*/); + } + ++ // Expose protected CPDF_Parser methods for testing. ++ using CPDF_Parser::LoadCrossRefV4; ++ using CPDF_Parser::ParseLinearizedHeader; ++ using CPDF_Parser::ParseStartXRef; ++ using CPDF_Parser::RebuildCrossRef; ++ using CPDF_Parser::StartParseInternal; ++ ++ TestObjectsHolder& object_holder() { return object_holder_; } ++ + private: +- // Add test cases here as private friend so that protected members in +- // CPDF_Parser can be accessed by test cases. +- // Need to access RebuildCrossRef. +- FRIEND_TEST(cpdf_parser, RebuildCrossRefCorrectly); +- FRIEND_TEST(cpdf_parser, RebuildCrossRefFailed); +- // Need to access LoadCrossRefV4. +- FRIEND_TEST(cpdf_parser, LoadCrossRefV4); ++ TestObjectsHolder object_holder_; + }; + +-TEST(cpdf_parser, RebuildCrossRefCorrectly) { ++TEST(ParserTest, RebuildCrossRefCorrectly) { + CPDF_TestParser parser; + std::string test_file; + ASSERT_TRUE(PathService::GetTestFilePath("parser_rebuildxref_correct.pdf", +@@ -79,13 +98,17 @@ TEST(cpdf_parser, RebuildCrossRefCorrectly) { + ASSERT_TRUE(parser.RebuildCrossRef()); + const FX_FILESIZE offsets[] = {0, 15, 61, 154, 296, 374, 450}; + const uint16_t versions[] = {0, 0, 2, 4, 6, 8, 0}; +- for (size_t i = 0; i < FX_ArraySize(offsets); ++i) ++ for (size_t i = 0; i < std::size(offsets); ++i) + EXPECT_EQ(offsets[i], GetObjInfo(parser, i).pos); +- for (size_t i = 0; i < FX_ArraySize(versions); ++i) ++ for (size_t i = 0; i < std::size(versions); ++i) + EXPECT_EQ(versions[i], GetObjInfo(parser, i).gennum); ++ ++ const CPDF_CrossRefTable* cross_ref_table = parser.GetCrossRefTable(); ++ ASSERT_TRUE(cross_ref_table); ++ EXPECT_EQ(0u, cross_ref_table->trailer_object_number()); + } + +-TEST(cpdf_parser, RebuildCrossRefFailed) { ++TEST(ParserTest, RebuildCrossRefFailed) { + CPDF_TestParser parser; + std::string test_file; + ASSERT_TRUE(PathService::GetTestFilePath( +@@ -95,7 +118,7 @@ TEST(cpdf_parser, RebuildCrossRefFailed) { + ASSERT_FALSE(parser.RebuildCrossRef()); + } + +-TEST(cpdf_parser, LoadCrossRefV4) { ++TEST(ParserTest, LoadCrossRefV4) { + { + static const unsigned char kXrefTable[] = + "xref \n" +@@ -119,9 +142,9 @@ TEST(cpdf_parser, LoadCrossRefV4) { + CPDF_TestParser::ObjectType::kFree, + CPDF_TestParser::ObjectType::kNotCompressed, + CPDF_TestParser::ObjectType::kNotCompressed}; +- static_assert(FX_ArraySize(kOffsets) == FX_ArraySize(kTypes), ++ static_assert(std::size(kOffsets) == std::size(kTypes), + "kOffsets / kTypes size mismatch"); +- for (size_t i = 0; i < FX_ArraySize(kOffsets); ++i) { ++ for (size_t i = 0; i < std::size(kOffsets); ++i) { + EXPECT_EQ(kOffsets[i], GetObjInfo(parser, i).pos); + EXPECT_EQ(kTypes[i], GetObjInfo(parser, i).type); + } +@@ -159,9 +182,9 @@ TEST(cpdf_parser, LoadCrossRefV4) { + CPDF_TestParser::ObjectType::kFree, + CPDF_TestParser::ObjectType::kFree, + CPDF_TestParser::ObjectType::kNotCompressed}; +- static_assert(FX_ArraySize(kOffsets) == FX_ArraySize(kTypes), ++ static_assert(std::size(kOffsets) == std::size(kTypes), + "kOffsets / kTypes size mismatch"); +- for (size_t i = 0; i < FX_ArraySize(kOffsets); ++i) { ++ for (size_t i = 0; i < std::size(kOffsets); ++i) { + EXPECT_EQ(kOffsets[i], GetObjInfo(parser, i).pos); + EXPECT_EQ(kTypes[i], GetObjInfo(parser, i).type); + } +@@ -199,9 +222,9 @@ TEST(cpdf_parser, LoadCrossRefV4) { + CPDF_TestParser::ObjectType::kFree, + CPDF_TestParser::ObjectType::kFree, + CPDF_TestParser::ObjectType::kNotCompressed}; +- static_assert(FX_ArraySize(kOffsets) == FX_ArraySize(kTypes), ++ static_assert(std::size(kOffsets) == std::size(kTypes), + "kOffsets / kTypes size mismatch"); +- for (size_t i = 0; i < FX_ArraySize(kOffsets); ++i) { ++ for (size_t i = 0; i < std::size(kOffsets); ++i) { + EXPECT_EQ(kOffsets[i], GetObjInfo(parser, i).pos); + EXPECT_EQ(kTypes[i], GetObjInfo(parser, i).type); + } +@@ -231,9 +254,9 @@ TEST(cpdf_parser, LoadCrossRefV4) { + CPDF_TestParser::ObjectType::kFree, + CPDF_TestParser::ObjectType::kNotCompressed, + CPDF_TestParser::ObjectType::kNotCompressed}; +- static_assert(FX_ArraySize(kOffsets) == FX_ArraySize(kTypes), ++ static_assert(std::size(kOffsets) == std::size(kTypes), + "kOffsets / kTypes size mismatch"); +- for (size_t i = 0; i < FX_ArraySize(kOffsets); ++i) { ++ for (size_t i = 0; i < std::size(kOffsets); ++i) { + EXPECT_EQ(kOffsets[i], GetObjInfo(parser, i).pos); + EXPECT_EQ(kTypes[i], GetObjInfo(parser, i).type); + } +@@ -264,7 +287,7 @@ TEST(cpdf_parser, LoadCrossRefV4) { + } + } + +-TEST(cpdf_parser, ParseStartXRef) { ++TEST(ParserTest, ParseStartXRef) { + CPDF_TestParser parser; + std::string test_file; + ASSERT_TRUE( +@@ -278,7 +301,7 @@ TEST(cpdf_parser, ParseStartXRef) { + EXPECT_EQ(75u, cross_ref_v5_obj->GetObjNum()); + } + +-TEST(cpdf_parser, ParseStartXRefWithHeaderOffset) { ++TEST(ParserTest, ParseStartXRefWithHeaderOffset) { + static constexpr FX_FILESIZE kTestHeaderOffset = 765; + std::string test_file; + ASSERT_TRUE( +@@ -288,8 +311,8 @@ TEST(cpdf_parser, ParseStartXRefWithHeaderOffset) { + ASSERT_TRUE(pFileAccess); + + std::vector data(pFileAccess->GetSize() + kTestHeaderOffset); +- ASSERT_TRUE(pFileAccess->ReadBlockAtOffset(&data.front() + kTestHeaderOffset, +- 0, pFileAccess->GetSize())); ++ ASSERT_TRUE(pFileAccess->ReadBlockAtOffset( ++ pdfium::make_span(data).subspan(kTestHeaderOffset), 0)); + CPDF_TestParser parser; + parser.InitTestFromBufferWithOffset(data, kTestHeaderOffset); + +@@ -300,7 +323,7 @@ TEST(cpdf_parser, ParseStartXRefWithHeaderOffset) { + EXPECT_EQ(75u, cross_ref_v5_obj->GetObjNum()); + } + +-TEST(cpdf_parser, ParseLinearizedWithHeaderOffset) { ++TEST(ParserTest, ParseLinearizedWithHeaderOffset) { + static constexpr FX_FILESIZE kTestHeaderOffset = 765; + std::string test_file; + ASSERT_TRUE(PathService::GetTestFilePath("linearized.pdf", &test_file)); +@@ -309,15 +332,19 @@ TEST(cpdf_parser, ParseLinearizedWithHeaderOffset) { + ASSERT_TRUE(pFileAccess); + + std::vector data(pFileAccess->GetSize() + kTestHeaderOffset); +- ASSERT_TRUE(pFileAccess->ReadBlockAtOffset(&data.front() + kTestHeaderOffset, +- 0, pFileAccess->GetSize())); ++ ASSERT_TRUE(pFileAccess->ReadBlockAtOffset( ++ pdfium::make_span(data).subspan(kTestHeaderOffset), 0)); ++ + CPDF_TestParser parser; + parser.InitTestFromBufferWithOffset(data, kTestHeaderOffset); +- + EXPECT_TRUE(parser.ParseLinearizedHeader()); ++ ++ const CPDF_CrossRefTable* cross_ref_table = parser.GetCrossRefTable(); ++ ASSERT_TRUE(cross_ref_table); ++ EXPECT_EQ(0u, cross_ref_table->trailer_object_number()); + } + +-TEST(cpdf_parser, BadStartXrefShouldNotBuildCrossRefTable) { ++TEST(ParserTest, BadStartXrefShouldNotBuildCrossRefTable) { + const unsigned char kData[] = + "%PDF1-7 0 obj <>\n" + "stream\n" +@@ -333,3 +360,103 @@ TEST(cpdf_parser, BadStartXrefShouldNotBuildCrossRefTable) { + ASSERT_TRUE(parser.GetCrossRefTable()); + EXPECT_EQ(0u, parser.GetCrossRefTable()->objects_info().size()); + } ++ ++TEST(ParserTest, XrefObjectIndicesTooBig) { ++ CPDF_TestParser parser; ++ ++ // Satisfy CPDF_Parser's checks, so the test data below can concentrate on the ++ // /XRef stream and avoid also providing other valid dictionaries. ++ auto dummy_root = pdfium::MakeRetain(); ++ EXPECT_CALL(parser.object_holder(), ParseIndirectObject) ++ .WillRepeatedly(Return(dummy_root)); ++ ++ // Since /Index starts at 4194303, the object number will go past ++ // `kMaxObjectNumber`. ++ static_assert(CPDF_Parser::kMaxObjectNumber == 4194304, ++ "Unexpected kMaxObjectNumber"); ++ const unsigned char kData[] = ++ "%PDF1-7\n%\xa0\xf2\xa4\xf4\n" ++ "7 0 obj <<\n" ++ " /Filter /ASCIIHexDecode\n" ++ " /Index [4194303 3]\n" ++ " /Root 1 0 R\n" ++ " /Size 4194306\n" ++ " /W [1 1 1]\n" ++ ">>\n" ++ "stream\n" ++ "01 00 00\n" ++ "01 0F 00\n" ++ "01 12 00\n" ++ "endstream\n" ++ "endobj\n" ++ "startxref\n" ++ "14\n" ++ "%%EOF\n"; ++ ASSERT_TRUE(parser.InitTestFromBuffer(kData)); ++ EXPECT_EQ(CPDF_Parser::SUCCESS, parser.StartParseInternal()); ++ ASSERT_TRUE(parser.GetCrossRefTable()); ++ const auto& objects_info = parser.GetCrossRefTable()->objects_info(); ++ EXPECT_EQ(2u, objects_info.size()); ++ ++ // This should be the only object from table. Subsequent objects have object ++ // numbers that are too big. ++ auto first_object_it = objects_info.find(4194303); ++ ASSERT_NE(first_object_it, objects_info.end()); ++ EXPECT_EQ(CPDF_Parser::ObjectType::kNormal, first_object_it->second.type); ++ EXPECT_EQ(0, first_object_it->second.pos); ++ ++ // TODO(thestig): Should the xref table contain object 4194305? ++ // Consider reworking CPDF_Parser's object representation to avoid having to ++ // store this placeholder object. ++ auto placeholder_object_it = objects_info.find(4194305); ++ ASSERT_NE(placeholder_object_it, objects_info.end()); ++ EXPECT_EQ(CPDF_Parser::ObjectType::kFree, placeholder_object_it->second.type); ++} ++ ++TEST(ParserTest, XrefHasInvalidArchiveObjectNumber) { ++ CPDF_TestParser parser; ++ ++ // Satisfy CPDF_Parser's checks, so the test data below can concentrate on the ++ // /XRef stream and avoid also providing other valid dictionaries. ++ auto dummy_root = pdfium::MakeRetain(); ++ EXPECT_CALL(parser.object_holder(), ParseIndirectObject) ++ .WillRepeatedly(Return(dummy_root)); ++ ++ // 0xFF in the first object in the xref object stream is invalid. ++ const unsigned char kData[] = ++ "%PDF1-7\n%\xa0\xf2\xa4\xf4\n" ++ "7 0 obj <<\n" ++ " /Filter /ASCIIHexDecode\n" ++ " /Root 1 0 R\n" ++ " /Size 3\n" ++ " /W [1 1 1]\n" ++ ">>\n" ++ "stream\n" ++ "02 FF 00\n" ++ "01 0F 00\n" ++ "01 12 00\n" ++ "endstream\n" ++ "endobj\n" ++ "startxref\n" ++ "14\n" ++ "%%EOF\n"; ++ ASSERT_TRUE(parser.InitTestFromBuffer(kData)); ++ EXPECT_EQ(CPDF_Parser::SUCCESS, parser.StartParseInternal()); ++ ++ const CPDF_CrossRefTable* cross_ref_table = parser.GetCrossRefTable(); ++ ASSERT_TRUE(cross_ref_table); ++ EXPECT_EQ(7u, cross_ref_table->trailer_object_number()); ++ const auto& objects_info = cross_ref_table->objects_info(); ++ EXPECT_EQ(2u, objects_info.size()); ++ ++ // Skip over the first object, and continue parsing the remaining objects. ++ auto second_object_it = objects_info.find(1); ++ ASSERT_NE(second_object_it, objects_info.end()); ++ EXPECT_EQ(CPDF_Parser::ObjectType::kNormal, second_object_it->second.type); ++ EXPECT_EQ(15, second_object_it->second.pos); ++ ++ auto third_object_it = objects_info.find(2); ++ ASSERT_NE(third_object_it, objects_info.end()); ++ EXPECT_EQ(CPDF_Parser::ObjectType::kNormal, third_object_it->second.type); ++ EXPECT_EQ(18, third_object_it->second.pos); ++} +diff --git a/core/fpdfapi/parser/cpdf_read_validator.cpp b/core/fpdfapi/parser/cpdf_read_validator.cpp +index 80e65175a..b6fd872ea 100644 +--- a/core/fpdfapi/parser/cpdf_read_validator.cpp ++++ b/core/fpdfapi/parser/cpdf_read_validator.cpp +@@ -1,14 +1,15 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/parser/cpdf_read_validator.h" + + #include ++#include + + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/logging.h" ++#include "third_party/base/notreached.h" + + namespace { + +@@ -26,55 +27,55 @@ FX_FILESIZE AlignUp(FX_FILESIZE offset) { + + } // namespace + +-CPDF_ReadValidator::Session::Session( +- const RetainPtr& validator) +- : validator_(validator.BackPointer()) { +- ASSERT(validator_); +- saved_read_error_ = validator_->read_error_; +- saved_has_unavailable_data_ = validator_->has_unavailable_data_; ++CPDF_ReadValidator::ScopedSession::ScopedSession( ++ RetainPtr validator) ++ : validator_(std::move(validator)), ++ saved_read_error_(validator_->read_error_), ++ saved_has_unavailable_data_(validator_->has_unavailable_data_) { + validator_->ResetErrors(); + } + +-CPDF_ReadValidator::Session::~Session() { ++CPDF_ReadValidator::ScopedSession::~ScopedSession() { + validator_->read_error_ |= saved_read_error_; + validator_->has_unavailable_data_ |= saved_has_unavailable_data_; + } + + CPDF_ReadValidator::CPDF_ReadValidator( +- const RetainPtr& file_read, ++ RetainPtr file_read, + CPDF_DataAvail::FileAvail* file_avail) +- : file_read_(file_read), ++ : file_read_(std::move(file_read)), + file_avail_(file_avail), +- read_error_(false), +- has_unavailable_data_(false), +- whole_file_already_available_(false), +- file_size_(file_read->GetSize()) {} ++ file_size_(file_read_->GetSize()) {} + +-CPDF_ReadValidator::~CPDF_ReadValidator() {} ++CPDF_ReadValidator::~CPDF_ReadValidator() = default; + + void CPDF_ReadValidator::ResetErrors() { + read_error_ = false; + has_unavailable_data_ = false; + } + +-bool CPDF_ReadValidator::ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) { ++bool CPDF_ReadValidator::ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) { ++ if (offset < 0) { ++ NOTREACHED(); ++ return false; ++ } ++ + FX_SAFE_FILESIZE end_offset = offset; +- end_offset += size; ++ end_offset += buffer.size(); + if (!end_offset.IsValid() || end_offset.ValueOrDie() > file_size_) + return false; + +- if (!IsDataRangeAvailable(offset, size)) { +- ScheduleDownload(offset, size); ++ if (!IsDataRangeAvailable(offset, buffer.size())) { ++ ScheduleDownload(offset, buffer.size()); + return false; + } + +- if (file_read_->ReadBlockAtOffset(buffer, offset, size)) ++ if (file_read_->ReadBlockAtOffset(buffer, offset)) + return true; + + read_error_ = true; +- ScheduleDownload(offset, size); ++ ScheduleDownload(offset, buffer.size()); + return false; + } + +@@ -116,8 +117,7 @@ bool CPDF_ReadValidator::IsWholeFileAvailable() { + const FX_SAFE_SIZE_T safe_size = file_size_; + whole_file_already_available_ = + whole_file_already_available_ || +- (safe_size.IsValid() ? IsDataRangeAvailable(0, safe_size.ValueOrDie()) +- : false); ++ (safe_size.IsValid() && IsDataRangeAvailable(0, safe_size.ValueOrDie())); + + return whole_file_already_available_; + } +diff --git a/core/fpdfapi/parser/cpdf_read_validator.h b/core/fpdfapi/parser/cpdf_read_validator.h +index 6adde02d6..c8665416e 100644 +--- a/core/fpdfapi/parser/cpdf_read_validator.h ++++ b/core/fpdfapi/parser/cpdf_read_validator.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,23 +6,29 @@ + #define CORE_FPDFAPI_PARSER_CPDF_READ_VALIDATOR_H_ + + #include "core/fpdfapi/parser/cpdf_data_avail.h" ++#include "core/fxcrt/fx_memory.h" + #include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxcrt/unowned_ptr.h" + + class CPDF_ReadValidator : public IFX_SeekableReadStream { + public: +- class Session { ++ class ScopedSession { + public: +- explicit Session(const RetainPtr& validator); +- ~Session(); ++ FX_STACK_ALLOCATED(); ++ ++ explicit ScopedSession(RetainPtr validator); ++ ScopedSession(const ScopedSession& that) = delete; ++ ScopedSession& operator=(const ScopedSession& that) = delete; ++ ~ScopedSession(); + + private: +- UnownedPtr validator_; +- bool saved_read_error_; +- bool saved_has_unavailable_data_; ++ RetainPtr const validator_; ++ const bool saved_read_error_; ++ const bool saved_has_unavailable_data_; + }; + +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + void SetDownloadHints(CPDF_DataAvail::DownloadHints* hints) { + hints_ = hints; +@@ -34,20 +40,17 @@ class CPDF_ReadValidator : public IFX_SeekableReadStream { + } + + void ResetErrors(); +- + bool IsWholeFileAvailable(); +- + bool CheckDataRangeAndRequestIfUnavailable(FX_FILESIZE offset, size_t size); + bool CheckWholeFileAndRequestIfUnavailable(); + + // IFX_SeekableReadStream overrides: +- bool ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) override; ++ bool ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) override; + FX_FILESIZE GetSize() override; + + protected: +- CPDF_ReadValidator(const RetainPtr& file_read, ++ CPDF_ReadValidator(RetainPtr file_read, + CPDF_DataAvail::FileAvail* file_avail); + ~CPDF_ReadValidator() override; + +@@ -55,14 +58,12 @@ class CPDF_ReadValidator : public IFX_SeekableReadStream { + void ScheduleDownload(FX_FILESIZE offset, size_t size); + bool IsDataRangeAvailable(FX_FILESIZE offset, size_t size) const; + +- RetainPtr file_read_; +- UnownedPtr file_avail_; +- ++ RetainPtr const file_read_; ++ UnownedPtr const file_avail_; + UnownedPtr hints_; +- +- bool read_error_; +- bool has_unavailable_data_; +- bool whole_file_already_available_; ++ bool read_error_ = false; ++ bool has_unavailable_data_ = false; ++ bool whole_file_already_available_ = false; + const FX_FILESIZE file_size_; + }; + +diff --git a/core/fpdfapi/parser/cpdf_read_validator_unittest.cpp b/core/fpdfapi/parser/cpdf_read_validator_unittest.cpp +index 38b4bf949..bb3006aaa 100644 +--- a/core/fpdfapi/parser/cpdf_read_validator_unittest.cpp ++++ b/core/fpdfapi/parser/cpdf_read_validator_unittest.cpp +@@ -1,14 +1,17 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/parser/cpdf_read_validator.h" + ++#include ++ + #include + #include +-#include + +-#include "core/fxcrt/cfx_readonlymemorystream.h" ++#include "core/fxcrt/cfx_read_only_span_stream.h" ++#include "core/fxcrt/cfx_read_only_vector_stream.h" ++#include "core/fxcrt/data_vector.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/invalid_seekable_read_stream.h" + +@@ -23,7 +26,7 @@ std::pair MakeRange(uint32_t start, uint32_t end) { + class MockFileAvail final : public CPDF_DataAvail::FileAvail { + public: + MockFileAvail() : available_range_(0, 0) {} +- ~MockFileAvail() override {} ++ ~MockFileAvail() override = default; + + bool IsDataAvail(FX_FILESIZE offset, size_t size) override { + return available_range_.first <= offset && +@@ -45,7 +48,7 @@ class MockFileAvail final : public CPDF_DataAvail::FileAvail { + class MockDownloadHints final : public CPDF_DataAvail::DownloadHints { + public: + MockDownloadHints() : last_requested_range_(0, 0) {} +- ~MockDownloadHints() override {} ++ ~MockDownloadHints() override = default; + + void AddSegment(FX_FILESIZE offset, size_t size) override { + last_requested_range_.first = offset; +@@ -64,42 +67,39 @@ class MockDownloadHints final : public CPDF_DataAvail::DownloadHints { + + } // namespace + +-TEST(CPDF_ReadValidatorTest, UnavailableData) { +- std::vector test_data(kTestDataSize); +- auto file = pdfium::MakeRetain(test_data); ++TEST(ReadValidatorTest, UnavailableData) { ++ DataVector test_data(kTestDataSize); ++ auto file = ++ pdfium::MakeRetain(std::move(test_data)); + MockFileAvail file_avail; +- auto validator = pdfium::MakeRetain(file, &file_avail); +- +- std::vector read_buffer(100); +- EXPECT_FALSE(validator->ReadBlockAtOffset(read_buffer.data(), 5000, +- read_buffer.size())); ++ auto validator = ++ pdfium::MakeRetain(std::move(file), &file_avail); + ++ DataVector read_buffer(100); ++ EXPECT_FALSE(validator->ReadBlockAtOffset(read_buffer, 5000)); + EXPECT_FALSE(validator->read_error()); + EXPECT_TRUE(validator->has_unavailable_data()); + + validator->ResetErrors(); +- + file_avail.SetAvailableRange(5000, 5000 + read_buffer.size()); +- +- EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer.data(), 5000, +- read_buffer.size())); ++ EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer, 5000)); + EXPECT_FALSE(validator->read_error()); + EXPECT_FALSE(validator->has_unavailable_data()); + } + +-TEST(CPDF_ReadValidatorTest, UnavailableDataWithHints) { +- std::vector test_data(kTestDataSize); +- auto file = pdfium::MakeRetain(test_data); ++TEST(ReadValidatorTest, UnavailableDataWithHints) { ++ DataVector test_data(kTestDataSize); ++ auto file = ++ pdfium::MakeRetain(std::move(test_data)); + MockFileAvail file_avail; +- auto validator = pdfium::MakeRetain(file, &file_avail); ++ auto validator = ++ pdfium::MakeRetain(std::move(file), &file_avail); + + MockDownloadHints hints; + validator->SetDownloadHints(&hints); + +- std::vector read_buffer(100); +- +- EXPECT_FALSE(validator->ReadBlockAtOffset(read_buffer.data(), 5000, +- read_buffer.size())); ++ DataVector read_buffer(100); ++ EXPECT_FALSE(validator->ReadBlockAtOffset(read_buffer, 5000)); + EXPECT_FALSE(validator->read_error()); + EXPECT_TRUE(validator->has_unavailable_data()); + +@@ -110,8 +110,7 @@ TEST(CPDF_ReadValidatorTest, UnavailableDataWithHints) { + hints.Reset(); + + validator->ResetErrors(); +- EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer.data(), 5000, +- read_buffer.size())); ++ EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer, 5000)); + // No new request on already available data. + EXPECT_EQ(MakeRange(0, 0), hints.GetLastRequstedRange()); + EXPECT_FALSE(validator->read_error()); +@@ -120,8 +119,7 @@ TEST(CPDF_ReadValidatorTest, UnavailableDataWithHints) { + validator->ResetErrors(); + // Try read unavailable data at file end. + EXPECT_FALSE(validator->ReadBlockAtOffset( +- read_buffer.data(), validator->GetSize() - read_buffer.size(), +- read_buffer.size())); ++ read_buffer, validator->GetSize() - read_buffer.size())); + // Should not enlarge request at file end. + EXPECT_EQ(validator->GetSize(), hints.GetLastRequstedRange().second); + EXPECT_FALSE(validator->read_error()); +@@ -130,63 +128,66 @@ TEST(CPDF_ReadValidatorTest, UnavailableDataWithHints) { + validator->SetDownloadHints(nullptr); + } + +-TEST(CPDF_ReadValidatorTest, ReadError) { ++TEST(ReadValidatorTest, ReadError) { + auto file = pdfium::MakeRetain(kTestDataSize); +- auto validator = pdfium::MakeRetain(file, nullptr); ++ auto validator = ++ pdfium::MakeRetain(std::move(file), nullptr); + + static const uint32_t kBufferSize = 3 * 1000; +- std::vector buffer(kBufferSize); ++ DataVector buffer(kBufferSize); + +- EXPECT_FALSE(validator->ReadBlockAtOffset(buffer.data(), 5000, 100)); ++ EXPECT_FALSE( ++ validator->ReadBlockAtOffset(pdfium::make_span(buffer).first(100), 5000)); + EXPECT_TRUE(validator->read_error()); + EXPECT_TRUE(validator->has_unavailable_data()); + } + +-TEST(CPDF_ReadValidatorTest, IntOverflow) { +- std::vector test_data(kTestDataSize); +- auto file = pdfium::MakeRetain(test_data); ++TEST(ReadValidatorTest, IntOverflow) { ++ DataVector test_data(kTestDataSize); ++ auto file = ++ pdfium::MakeRetain(std::move(test_data)); + MockFileAvail file_avail; +- auto validator = pdfium::MakeRetain(file, &file_avail); ++ auto validator = ++ pdfium::MakeRetain(std::move(file), &file_avail); + +- std::vector read_buffer(100); ++ DataVector read_buffer(100); + + // If we have int overflow, this is equal reading after file end. This is not + // read_error, and in this case we have not unavailable data. It is just error + // of input params. + EXPECT_FALSE(validator->ReadBlockAtOffset( +- read_buffer.data(), std::numeric_limits::max() - 1, +- read_buffer.size())); ++ read_buffer, std::numeric_limits::max() - 1)); + EXPECT_FALSE(validator->read_error()); + EXPECT_FALSE(validator->has_unavailable_data()); + } + +-TEST(CPDF_ReadValidatorTest, Session) { +- std::vector test_data(kTestDataSize); ++TEST(ReadValidatorTest, Session) { ++ DataVector test_data(kTestDataSize); + + auto file = pdfium::MakeRetain(kTestDataSize); + MockFileAvail file_avail; + MockDownloadHints hints; +- auto validator = pdfium::MakeRetain(file, &file_avail); ++ auto validator = ++ pdfium::MakeRetain(std::move(file), &file_avail); + validator->SetDownloadHints(&hints); + +- const CPDF_ReadValidator::Session read_session(validator); ++ CPDF_ReadValidator::ScopedSession read_session(validator); + ASSERT_FALSE(validator->has_read_problems()); + + // Data is unavailable +- validator->ReadBlockAtOffset(test_data.data(), 0, 100); +- ++ validator->ReadBlockAtOffset(pdfium::make_span(test_data).first(100), 0); + EXPECT_TRUE(validator->has_read_problems()); + EXPECT_TRUE(validator->has_unavailable_data()); + EXPECT_FALSE(validator->read_error()); + + { +- const CPDF_ReadValidator::Session read_subsession(validator); ++ CPDF_ReadValidator::ScopedSession read_subsession(validator); + // The read problems should be hidden. + EXPECT_FALSE(validator->has_read_problems()); + + file_avail.SetAvailableRange(0, 100); + // Read fail. +- validator->ReadBlockAtOffset(test_data.data(), 0, 100); ++ validator->ReadBlockAtOffset(pdfium::make_span(test_data).first(100), 0); + EXPECT_TRUE(validator->has_read_problems()); + EXPECT_TRUE(validator->has_unavailable_data()); + EXPECT_TRUE(validator->read_error()); +@@ -198,33 +199,33 @@ TEST(CPDF_ReadValidatorTest, Session) { + EXPECT_TRUE(validator->read_error()); + } + +-TEST(CPDF_ReadValidatorTest, SessionReset) { +- std::vector test_data(kTestDataSize); ++TEST(ReadValidatorTest, SessionReset) { ++ DataVector test_data(kTestDataSize); + + auto file = pdfium::MakeRetain(kTestDataSize); + MockFileAvail file_avail; + MockDownloadHints hints; +- auto validator = pdfium::MakeRetain(file, &file_avail); ++ auto validator = ++ pdfium::MakeRetain(std::move(file), &file_avail); + validator->SetDownloadHints(&hints); + +- const CPDF_ReadValidator::Session read_session(validator); ++ CPDF_ReadValidator::ScopedSession read_session(validator); + ASSERT_FALSE(validator->has_read_problems()); + + // Data is unavailable +- validator->ReadBlockAtOffset(test_data.data(), 0, 100); +- ++ validator->ReadBlockAtOffset(pdfium::make_span(test_data).first(100), 0); + EXPECT_TRUE(validator->has_read_problems()); + EXPECT_TRUE(validator->has_unavailable_data()); + EXPECT_FALSE(validator->read_error()); + + { +- const CPDF_ReadValidator::Session read_subsession(validator); ++ CPDF_ReadValidator::ScopedSession read_subsession(validator); + // The read problems should be hidden. + EXPECT_FALSE(validator->has_read_problems()); + + file_avail.SetAvailableRange(0, 100); + // Read fail. +- validator->ReadBlockAtOffset(test_data.data(), 0, 100); ++ validator->ReadBlockAtOffset(pdfium::make_span(test_data).first(100), 0); + EXPECT_TRUE(validator->has_read_problems()); + EXPECT_TRUE(validator->has_unavailable_data()); + EXPECT_TRUE(validator->read_error()); +@@ -240,11 +241,13 @@ TEST(CPDF_ReadValidatorTest, SessionReset) { + EXPECT_FALSE(validator->read_error()); + } + +-TEST(CPDF_ReadValidatorTest, CheckDataRangeAndRequestIfUnavailable) { +- std::vector test_data(kTestDataSize); +- auto file = pdfium::MakeRetain(test_data); ++TEST(ReadValidatorTest, CheckDataRangeAndRequestIfUnavailable) { ++ DataVector test_data(kTestDataSize); ++ auto file = ++ pdfium::MakeRetain(std::move(test_data)); + MockFileAvail file_avail; +- auto validator = pdfium::MakeRetain(file, &file_avail); ++ auto validator = ++ pdfium::MakeRetain(std::move(file), &file_avail); + + MockDownloadHints hints; + validator->SetDownloadHints(&hints); +@@ -266,9 +269,8 @@ TEST(CPDF_ReadValidatorTest, CheckDataRangeAndRequestIfUnavailable) { + EXPECT_FALSE(validator->read_error()); + EXPECT_FALSE(validator->has_unavailable_data()); + +- std::vector read_buffer(100); +- EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer.data(), 5000, +- read_buffer.size())); ++ DataVector read_buffer(100); ++ EXPECT_TRUE(validator->ReadBlockAtOffset(read_buffer, 5000)); + // No new request on already available data. + EXPECT_EQ(MakeRange(0, 0), hints.GetLastRequstedRange()); + EXPECT_FALSE(validator->read_error()); +diff --git a/core/fpdfapi/parser/cpdf_reference.cpp b/core/fpdfapi/parser/cpdf_reference.cpp +index d50db340f..8baa6b684 100644 +--- a/core/fpdfapi/parser/cpdf_reference.cpp ++++ b/core/fpdfapi/parser/cpdf_reference.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,54 +6,42 @@ + + #include "core/fpdfapi/parser/cpdf_reference.h" + ++#include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h" + #include "core/fxcrt/fx_stream.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/containers/contains.h" + + CPDF_Reference::CPDF_Reference(CPDF_IndirectObjectHolder* pDoc, uint32_t objnum) + : m_pObjList(pDoc), m_RefObjNum(objnum) {} + +-CPDF_Reference::~CPDF_Reference() {} ++CPDF_Reference::~CPDF_Reference() = default; + + CPDF_Object::Type CPDF_Reference::GetType() const { + return kReference; + } + + ByteString CPDF_Reference::GetString() const { +- const CPDF_Object* obj = SafeGetDirect(); ++ const CPDF_Object* obj = FastGetDirect(); + return obj ? obj->GetString() : ByteString(); + } + + float CPDF_Reference::GetNumber() const { +- const CPDF_Object* obj = SafeGetDirect(); ++ const CPDF_Object* obj = FastGetDirect(); + return obj ? obj->GetNumber() : 0; + } + + int CPDF_Reference::GetInteger() const { +- const CPDF_Object* obj = SafeGetDirect(); ++ const CPDF_Object* obj = FastGetDirect(); + return obj ? obj->GetInteger() : 0; + } + +-CPDF_Dictionary* CPDF_Reference::GetDict() { +- CPDF_Object* obj = SafeGetDirect(); +- return obj ? obj->GetDict() : nullptr; ++const CPDF_Dictionary* CPDF_Reference::GetDictInternal() const { ++ const CPDF_Object* obj = FastGetDirect(); ++ return obj ? obj->GetDictInternal() : nullptr; + } + +-const CPDF_Dictionary* CPDF_Reference::GetDict() const { +- const CPDF_Object* obj = SafeGetDirect(); +- return obj ? obj->GetDict() : nullptr; +-} +- +-bool CPDF_Reference::IsReference() const { +- return true; +-} +- +-CPDF_Reference* CPDF_Reference::AsReference() { +- return this; +-} +- +-const CPDF_Reference* CPDF_Reference::AsReference() const { ++CPDF_Reference* CPDF_Reference::AsMutableReference() { + return this; + } + +@@ -65,22 +53,20 @@ RetainPtr CPDF_Reference::CloneNonCyclic( + bool bDirect, + std::set* pVisited) const { + pVisited->insert(this); +- if (bDirect) { +- auto* pDirect = GetDirect(); +- return pDirect && !pdfium::ContainsKey(*pVisited, pDirect) +- ? pDirect->CloneNonCyclic(true, pVisited) +- : nullptr; ++ if (!bDirect) { ++ return pdfium::MakeRetain(m_pObjList, m_RefObjNum); + } +- return pdfium::MakeRetain(m_pObjList.Get(), m_RefObjNum); ++ RetainPtr pDirect = GetDirect(); ++ return pDirect && !pdfium::Contains(*pVisited, pDirect.Get()) ++ ? pDirect->CloneNonCyclic(true, pVisited) ++ : nullptr; + } + +-CPDF_Object* CPDF_Reference::SafeGetDirect() { +- CPDF_Object* obj = GetDirect(); +- return (obj && !obj->IsReference()) ? obj : nullptr; +-} +- +-const CPDF_Object* CPDF_Reference::SafeGetDirect() const { +- const CPDF_Object* obj = GetDirect(); ++const CPDF_Object* CPDF_Reference::FastGetDirect() const { ++ if (!m_pObjList) ++ return nullptr; ++ const CPDF_Object* obj = ++ m_pObjList->GetOrParseIndirectObjectInternal(m_RefObjNum); + return (obj && !obj->IsReference()) ? obj : nullptr; + } + +@@ -89,13 +75,8 @@ void CPDF_Reference::SetRef(CPDF_IndirectObjectHolder* pDoc, uint32_t objnum) { + m_RefObjNum = objnum; + } + +-CPDF_Object* CPDF_Reference::GetDirect() { +- return m_pObjList ? m_pObjList->GetOrParseIndirectObject(m_RefObjNum) +- : nullptr; +-} +- +-const CPDF_Object* CPDF_Reference::GetDirect() const { +- return m_pObjList ? m_pObjList->GetOrParseIndirectObject(m_RefObjNum) ++const CPDF_Object* CPDF_Reference::GetDirectInternal() const { ++ return m_pObjList ? m_pObjList->GetOrParseIndirectObjectInternal(m_RefObjNum) + : nullptr; + } + +@@ -105,9 +86,9 @@ bool CPDF_Reference::WriteTo(IFX_ArchiveStream* archive, + archive->WriteString(" 0 R "); + } + +-RetainPtr CPDF_Reference::MakeReference( ++RetainPtr CPDF_Reference::MakeReference( + CPDF_IndirectObjectHolder* holder) const { +- ASSERT(holder == m_pObjList); ++ DCHECK_EQ(holder, m_pObjList); + // Do not allow reference to reference, just create other reference for same + // object. + return pdfium::MakeRetain(holder, GetRefObjNum()); +diff --git a/core/fpdfapi/parser/cpdf_reference.h b/core/fpdfapi/parser/cpdf_reference.h +index 1ec728250..241b398df 100644 +--- a/core/fpdfapi/parser/cpdf_reference.h ++++ b/core/fpdfapi/parser/cpdf_reference.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,61 +7,67 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_REFERENCE_H_ + #define CORE_FPDFAPI_PARSER_CPDF_REFERENCE_H_ + +-#include + #include + + #include "core/fpdfapi/parser/cpdf_object.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + + class CPDF_IndirectObjectHolder; + + class CPDF_Reference final : public CPDF_Object { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // CPDF_Object: + Type GetType() const override; + RetainPtr Clone() const override; +- CPDF_Object* GetDirect() override; +- const CPDF_Object* GetDirect() const override; + ByteString GetString() const override; + float GetNumber() const override; + int GetInteger() const override; +- CPDF_Dictionary* GetDict() override; +- const CPDF_Dictionary* GetDict() const override; +- bool IsReference() const override; +- CPDF_Reference* AsReference() override; +- const CPDF_Reference* AsReference() const override; ++ CPDF_Reference* AsMutableReference() override; + bool WriteTo(IFX_ArchiveStream* archive, + const CPDF_Encryptor* encryptor) const override; +- RetainPtr MakeReference( ++ RetainPtr MakeReference( + CPDF_IndirectObjectHolder* holder) const override; + +- CPDF_IndirectObjectHolder* GetObjList() const { return m_pObjList.Get(); } + uint32_t GetRefObjNum() const { return m_RefObjNum; } ++ bool HasIndirectObjectHolder() const { return !!m_pObjList; } + void SetRef(CPDF_IndirectObjectHolder* pDoc, uint32_t objnum); + + private: ++ friend class CPDF_Dictionary; ++ + CPDF_Reference(CPDF_IndirectObjectHolder* pDoc, uint32_t objnum); + ~CPDF_Reference() override; + ++ const CPDF_Object* GetDirectInternal() const override; ++ const CPDF_Dictionary* GetDictInternal() const override; + RetainPtr CloneNonCyclic( + bool bDirect, + std::set* pVisited) const override; +- CPDF_Object* SafeGetDirect(); +- const CPDF_Object* SafeGetDirect() const; ++ ++ const CPDF_Object* FastGetDirect() const; + + UnownedPtr m_pObjList; + uint32_t m_RefObjNum; + }; + + inline CPDF_Reference* ToReference(CPDF_Object* obj) { +- return obj ? obj->AsReference() : nullptr; ++ return obj ? obj->AsMutableReference() : nullptr; + } + + inline const CPDF_Reference* ToReference(const CPDF_Object* obj) { + return obj ? obj->AsReference() : nullptr; + } + ++inline RetainPtr ToReference(RetainPtr obj) { ++ return RetainPtr(ToReference(obj.Get())); ++} ++ ++inline RetainPtr ToReference( ++ RetainPtr obj) { ++ return RetainPtr(ToReference(obj.Get())); ++} ++ + #endif // CORE_FPDFAPI_PARSER_CPDF_REFERENCE_H_ +diff --git a/core/fpdfapi/parser/cpdf_security_handler.cpp b/core/fpdfapi/parser/cpdf_security_handler.cpp +index 126fc6b8a..ff17c59e8 100644 +--- a/core/fpdfapi/parser/cpdf_security_handler.cpp ++++ b/core/fpdfapi/parser/cpdf_security_handler.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,11 +6,11 @@ + + #include "core/fpdfapi/parser/cpdf_security_handler.h" + ++#include + #include + + #include + #include +-#include + + #include "core/fdrm/fx_crypt.h" + #include "core/fpdfapi/parser/cpdf_array.h" +@@ -18,8 +18,11 @@ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_object.h" + #include "core/fpdfapi/parser/cpdf_string.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_random.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/notreached.h" + + namespace { + +@@ -47,7 +50,7 @@ void CalcEncryptKey(const CPDF_Dictionary* pEncrypt, + GetPassCode(password, passcode); + CRYPT_md5_context md5 = CRYPT_MD5Start(); + CRYPT_MD5Update(&md5, passcode); +- ByteString okey = pEncrypt->GetStringFor("O"); ++ ByteString okey = pEncrypt->GetByteStringFor("O"); + CRYPT_MD5Update(&md5, okey.raw_span()); + uint32_t perm = pEncrypt->GetIntegerFor("P"); + CRYPT_MD5Update(&md5, pdfium::as_bytes(pdfium::make_span(&perm, 1))); +@@ -70,20 +73,18 @@ void CalcEncryptKey(const CPDF_Dictionary* pEncrypt, + memcpy(key, digest, copy_len); + } + +-bool IsValidKeyLengthForCipher(int cipher, size_t keylen) { ++bool IsValidKeyLengthForCipher(CPDF_CryptoHandler::Cipher cipher, ++ size_t keylen) { + switch (cipher) { +- case FXCIPHER_AES: ++ case CPDF_CryptoHandler::Cipher::kAES: + return keylen == 16 || keylen == 24 || keylen == 32; +- case FXCIPHER_AES2: ++ case CPDF_CryptoHandler::Cipher::kAES2: + return keylen == 32; +- case FXCIPHER_RC4: ++ case CPDF_CryptoHandler::Cipher::kRC4: + return keylen >= 5 && keylen <= 16; +- case FXCIPHER_NONE: ++ case CPDF_CryptoHandler::Cipher::kNone: + return true; +- default: +- NOTREACHED(); + } +- return false; + } + + #define FX_GET_32WORD(n, b, i) \ +@@ -118,13 +119,13 @@ void Revision6_Hash(const ByteString& password, + uint8_t digest[32]; + CRYPT_SHA256Finish(&sha, digest); + +- std::vector buf; ++ DataVector buf; + uint8_t* input = digest; + uint8_t* key = input; + uint8_t* iv = input + 16; + uint8_t* E = nullptr; + int iBufLen = 0; +- std::vector interDigest; ++ DataVector interDigest; + int i = 0; + int iBlockSize = 32; + CRYPT_aes_context aes = {}; +@@ -136,7 +137,7 @@ void Revision6_Hash(const ByteString& password, + iBufLen = iRoundSize * 64; + buf.resize(iBufLen); + E = buf.data(); +- std::vector content; ++ DataVector content; + for (int j = 0; j < 64; ++j) { + content.insert(std::end(content), password.raw_str(), + password.raw_str() + password.GetLength()); +@@ -145,7 +146,7 @@ void Revision6_Hash(const ByteString& password, + content.insert(std::end(content), vector, vector + 48); + } + } +- CRYPT_AESSetKey(&aes, key, 16, true); ++ CRYPT_AESSetKey(&aes, key, 16); + CRYPT_AESSetIV(&aes, iv); + CRYPT_AESEncrypt(&aes, E, content.data(), iBufLen); + int iHash = 0; +@@ -188,15 +189,15 @@ CPDF_SecurityHandler::CPDF_SecurityHandler() = default; + CPDF_SecurityHandler::~CPDF_SecurityHandler() = default; + + bool CPDF_SecurityHandler::OnInit(const CPDF_Dictionary* pEncryptDict, +- const CPDF_Array* pIdArray, ++ RetainPtr pIdArray, + const ByteString& password) { + if (pIdArray) +- m_FileId = pIdArray->GetStringAt(0); ++ m_FileId = pIdArray->GetByteStringAt(0); + else + m_FileId.clear(); + if (!LoadDict(pEncryptDict)) + return false; +- if (m_Cipher == FXCIPHER_NONE) ++ if (m_Cipher == CPDF_CryptoHandler::Cipher::kNone) + return true; + if (!CheckSecurity(password)) + return false; +@@ -215,7 +216,8 @@ bool CPDF_SecurityHandler::CheckSecurity(const ByteString& password) { + + uint32_t CPDF_SecurityHandler::GetPermissions() const { + uint32_t dwPermission = m_bOwnerUnlocked ? 0xFFFFFFFF : m_Permissions; +- if (m_pEncryptDict && m_pEncryptDict->GetStringFor("Filter") == "Standard") { ++ if (m_pEncryptDict && ++ m_pEncryptDict->GetByteStringFor("Filter") == "Standard") { + // See PDF Reference 1.7, page 123, table 3.20. + dwPermission &= 0xFFFFFFFC; + dwPermission |= 0xFFFFF0C0; +@@ -225,21 +227,23 @@ uint32_t CPDF_SecurityHandler::GetPermissions() const { + + static bool LoadCryptInfo(const CPDF_Dictionary* pEncryptDict, + const ByteString& name, +- int* cipher, ++ CPDF_CryptoHandler::Cipher* cipher, + size_t* keylen_out) { + int Version = pEncryptDict->GetIntegerFor("V"); +- *cipher = FXCIPHER_RC4; ++ *cipher = CPDF_CryptoHandler::Cipher::kRC4; + *keylen_out = 0; + int keylen = 0; + if (Version >= 4) { +- const CPDF_Dictionary* pCryptFilters = pEncryptDict->GetDictFor("CF"); ++ RetainPtr pCryptFilters = ++ pEncryptDict->GetDictFor("CF"); + if (!pCryptFilters) + return false; + + if (name == "Identity") { +- *cipher = FXCIPHER_NONE; ++ *cipher = CPDF_CryptoHandler::Cipher::kNone; + } else { +- const CPDF_Dictionary* pDefFilter = pCryptFilters->GetDictFor(name); ++ RetainPtr pDefFilter = ++ pCryptFilters->GetDictFor(name); + if (!pDefFilter) + return false; + +@@ -259,9 +263,9 @@ static bool LoadCryptInfo(const CPDF_Dictionary* pEncryptDict, + nKeyBits *= 8; + } + keylen = nKeyBits / 8; +- ByteString cipher_name = pDefFilter->GetStringFor("CFM"); ++ ByteString cipher_name = pDefFilter->GetByteStringFor("CFM"); + if (cipher_name == "AESV2" || cipher_name == "AESV3") +- *cipher = FXCIPHER_AES; ++ *cipher = CPDF_CryptoHandler::Cipher::kAES; + } + } else { + keylen = Version > 1 ? pEncryptDict->GetIntegerFor("Length", 40) / 8 : 5; +@@ -284,8 +288,8 @@ bool CPDF_SecurityHandler::LoadDict(const CPDF_Dictionary* pEncryptDict) { + if (m_Version < 4) + return LoadCryptInfo(pEncryptDict, ByteString(), &m_Cipher, &m_KeyLen); + +- ByteString stmf_name = pEncryptDict->GetStringFor("StmF"); +- ByteString strf_name = pEncryptDict->GetStringFor("StrF"); ++ ByteString stmf_name = pEncryptDict->GetByteStringFor("StmF"); ++ ByteString strf_name = pEncryptDict->GetByteStringFor("StrF"); + if (stmf_name != strf_name) + return false; + +@@ -293,7 +297,7 @@ bool CPDF_SecurityHandler::LoadDict(const CPDF_Dictionary* pEncryptDict) { + } + + bool CPDF_SecurityHandler::LoadDict(const CPDF_Dictionary* pEncryptDict, +- int* cipher, ++ CPDF_CryptoHandler::Cipher* cipher, + size_t* key_len) { + m_pEncryptDict.Reset(pEncryptDict); + m_Version = pEncryptDict->GetIntegerFor("V"); +@@ -303,8 +307,8 @@ bool CPDF_SecurityHandler::LoadDict(const CPDF_Dictionary* pEncryptDict, + ByteString strf_name; + ByteString stmf_name; + if (m_Version >= 4) { +- stmf_name = pEncryptDict->GetStringFor("StmF"); +- strf_name = pEncryptDict->GetStringFor("StrF"); ++ stmf_name = pEncryptDict->GetByteStringFor("StmF"); ++ strf_name = pEncryptDict->GetByteStringFor("StrF"); + if (stmf_name != strf_name) + return false; + } +@@ -318,14 +322,14 @@ bool CPDF_SecurityHandler::LoadDict(const CPDF_Dictionary* pEncryptDict, + + bool CPDF_SecurityHandler::AES256_CheckPassword(const ByteString& password, + bool bOwner) { +- ASSERT(m_pEncryptDict); +- ASSERT(m_Revision >= 5); ++ DCHECK(m_pEncryptDict); ++ DCHECK(m_Revision >= 5); + +- ByteString okey = m_pEncryptDict->GetStringFor("O"); ++ ByteString okey = m_pEncryptDict->GetByteStringFor("O"); + if (okey.GetLength() < 48) + return false; + +- ByteString ukey = m_pEncryptDict->GetStringFor("U"); ++ ByteString ukey = m_pEncryptDict->GetByteStringFor("U"); + if (ukey.GetLength() < 48) + return false; + +@@ -357,18 +361,18 @@ bool CPDF_SecurityHandler::AES256_CheckPassword(const ByteString& password, + CRYPT_SHA256Update(&sha, ukey.raw_str(), 48); + CRYPT_SHA256Finish(&sha, digest); + } +- ByteString ekey = m_pEncryptDict->GetStringFor(bOwner ? "OE" : "UE"); ++ ByteString ekey = m_pEncryptDict->GetByteStringFor(bOwner ? "OE" : "UE"); + if (ekey.GetLength() < 32) + return false; + + CRYPT_aes_context aes = {}; +- CRYPT_AESSetKey(&aes, digest, sizeof(digest), false); ++ CRYPT_AESSetKey(&aes, digest, sizeof(digest)); + uint8_t iv[16] = {}; + CRYPT_AESSetIV(&aes, iv); + CRYPT_AESDecrypt(&aes, m_EncryptKey, ekey.raw_str(), 32); +- CRYPT_AESSetKey(&aes, m_EncryptKey, sizeof(m_EncryptKey), false); ++ CRYPT_AESSetKey(&aes, m_EncryptKey, sizeof(m_EncryptKey)); + CRYPT_AESSetIV(&aes, iv); +- ByteString perms = m_pEncryptDict->GetStringFor("Perms"); ++ ByteString perms = m_pEncryptDict->GetByteStringFor("Perms"); + if (perms.IsEmpty()) + return false; + +@@ -381,7 +385,7 @@ bool CPDF_SecurityHandler::AES256_CheckPassword(const ByteString& password, + if (buf[9] != 'a' || buf[10] != 'd' || buf[11] != 'b') + return false; + +- if (FXDWORD_GET_LSBFIRST(buf) != m_Permissions) ++ if (FXSYS_UINT32_GET_LSBFIRST(buf) != m_Permissions) + return false; + + // Relax this check as there appear to be some non-conforming documents +@@ -437,7 +441,7 @@ bool CPDF_SecurityHandler::CheckUserPassword(const ByteString& password, + CalcEncryptKey(m_pEncryptDict.Get(), password, m_EncryptKey, m_KeyLen, + bIgnoreEncryptMeta, m_FileId); + ByteString ukey = +- m_pEncryptDict ? m_pEncryptDict->GetStringFor("U") : ByteString(); ++ m_pEncryptDict ? m_pEncryptDict->GetByteStringFor("U") : ByteString(); + if (ukey.GetLength() < 16) { + return false; + } +@@ -470,7 +474,7 @@ bool CPDF_SecurityHandler::CheckUserPassword(const ByteString& password, + ByteString CPDF_SecurityHandler::GetUserPassword( + const ByteString& owner_password) const { + constexpr size_t kRequiredOkeyLength = 32; +- ByteString okey = m_pEncryptDict->GetStringFor("O"); ++ ByteString okey = m_pEncryptDict->GetByteStringFor("O"); + size_t okeylen = std::min(okey.GetLength(), kRequiredOkeyLength); + if (okeylen < kRequiredOkeyLength) + return ByteString(); +@@ -539,9 +543,9 @@ void CPDF_SecurityHandler::OnCreateInternal(CPDF_Dictionary* pEncryptDict, + const ByteString& user_password, + const ByteString& owner_password, + bool bDefault) { +- ASSERT(pEncryptDict); ++ DCHECK(pEncryptDict); + +- int cipher = FXCIPHER_NONE; ++ CPDF_CryptoHandler::Cipher cipher = CPDF_CryptoHandler::Cipher::kNone; + size_t key_len = 0; + if (!LoadDict(pEncryptDict, &cipher, &key_len)) { + return; +@@ -552,7 +556,7 @@ void CPDF_SecurityHandler::OnCreateInternal(CPDF_Dictionary* pEncryptDict, + + if (m_Revision >= 5) { + uint32_t random[4]; +- FX_Random_GenerateMT(random, FX_ArraySize(random)); ++ FX_Random_GenerateMT(random, std::size(random)); + CRYPT_sha2_context sha; + CRYPT_SHA256Start(&sha); + CRYPT_SHA256Update(&sha, reinterpret_cast(random), +@@ -590,7 +594,7 @@ void CPDF_SecurityHandler::OnCreateInternal(CPDF_Dictionary* pEncryptDict, + + ByteString file_id; + if (pIdArray) +- file_id = pIdArray->GetStringAt(0); ++ file_id = pIdArray->GetByteStringAt(0); + + CalcEncryptKey(m_pEncryptDict.Get(), user_password, m_EncryptKey, key_len, + false, file_id); +@@ -646,7 +650,7 @@ void CPDF_SecurityHandler::AES256_SetPassword(CPDF_Dictionary* pEncryptDict, + uint8_t digest[20]; + CRYPT_SHA1Finish(&sha, digest); + +- ByteString ukey = pEncryptDict->GetStringFor("U"); ++ ByteString ukey = pEncryptDict->GetByteStringFor("U"); + CRYPT_sha2_context sha2; + uint8_t digest1[48]; + if (m_Revision >= 6) { +@@ -677,7 +681,7 @@ void CPDF_SecurityHandler::AES256_SetPassword(CPDF_Dictionary* pEncryptDict, + CRYPT_SHA256Finish(&sha2, digest1); + } + CRYPT_aes_context aes = {}; +- CRYPT_AESSetKey(&aes, digest1, 32, true); ++ CRYPT_AESSetKey(&aes, digest1, 32); + uint8_t iv[16] = {}; + CRYPT_AESSetIV(&aes, iv); + CRYPT_AESEncrypt(&aes, digest1, m_EncryptKey, sizeof(m_EncryptKey)); +@@ -706,7 +710,7 @@ void CPDF_SecurityHandler::AES256_SetPerms(CPDF_Dictionary* pEncryptDict) { + FX_Random_GenerateMT(buf_random, 1); + + CRYPT_aes_context aes = {}; +- CRYPT_AESSetKey(&aes, m_EncryptKey, sizeof(m_EncryptKey), true); ++ CRYPT_AESSetKey(&aes, m_EncryptKey, sizeof(m_EncryptKey)); + + uint8_t iv[16] = {}; + CRYPT_AESSetIV(&aes, iv); +@@ -718,5 +722,5 @@ void CPDF_SecurityHandler::AES256_SetPerms(CPDF_Dictionary* pEncryptDict) { + + void CPDF_SecurityHandler::InitCryptoHandler() { + m_pCryptoHandler = +- pdfium::MakeUnique(m_Cipher, m_EncryptKey, m_KeyLen); ++ std::make_unique(m_Cipher, m_EncryptKey, m_KeyLen); + } +diff --git a/core/fpdfapi/parser/cpdf_security_handler.h b/core/fpdfapi/parser/cpdf_security_handler.h +index 05eb689b1..300dd9bed 100644 +--- a/core/fpdfapi/parser/cpdf_security_handler.h ++++ b/core/fpdfapi/parser/cpdf_security_handler.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,29 +7,24 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_SECURITY_HANDLER_H_ + #define CORE_FPDFAPI_PARSER_CPDF_SECURITY_HANDLER_H_ + ++#include ++#include ++ + #include + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fpdfapi/parser/cpdf_crypto_handler.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/retain_ptr.h" + +-#define FXCIPHER_NONE 0 +-#define FXCIPHER_RC4 1 +-#define FXCIPHER_AES 2 +-#define FXCIPHER_AES2 3 +- + class CPDF_Array; +-class CPDF_CryptoHandler; + class CPDF_Dictionary; +-class CPDF_Parser; + +-class CPDF_SecurityHandler : public Retainable { ++class CPDF_SecurityHandler final : public Retainable { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + bool OnInit(const CPDF_Dictionary* pEncryptDict, +- const CPDF_Array* pIdArray, ++ RetainPtr pIdArray, + const ByteString& password); + void OnCreate(CPDF_Dictionary* pEncryptDict, + const CPDF_Array* pIdArray, +@@ -63,7 +58,7 @@ class CPDF_SecurityHandler : public Retainable { + + bool LoadDict(const CPDF_Dictionary* pEncryptDict); + bool LoadDict(const CPDF_Dictionary* pEncryptDict, +- int* cipher, ++ CPDF_CryptoHandler::Cipher* cipher, + size_t* key_len); + + ByteString GetUserPassword(const ByteString& owner_password) const; +@@ -89,13 +84,13 @@ class CPDF_SecurityHandler : public Retainable { + int m_Version = 0; + int m_Revision = 0; + uint32_t m_Permissions = 0; +- int m_Cipher = FXCIPHER_NONE; + size_t m_KeyLen = 0; ++ CPDF_CryptoHandler::Cipher m_Cipher = CPDF_CryptoHandler::Cipher::kNone; + PasswordEncodingConversion m_PasswordEncodingConversion = kUnknown; + ByteString m_FileId; + RetainPtr m_pEncryptDict; + std::unique_ptr m_pCryptoHandler; +- uint8_t m_EncryptKey[32]; ++ uint8_t m_EncryptKey[32] = {}; + }; + + #endif // CORE_FPDFAPI_PARSER_CPDF_SECURITY_HANDLER_H_ +diff --git a/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp b/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp +index ee2a62151..f2c5ac1ea 100644 +--- a/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp ++++ b/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp +@@ -1,8 +1,7 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include + #include + + #include "build/build_config.h" +@@ -10,11 +9,13 @@ + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_parser.h" + #include "core/fxcrt/fx_system.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" + #include "public/cpp/fpdf_scopers.h" + #include "public/fpdf_edit.h" + #include "public/fpdf_save.h" + #include "public/fpdfview.h" + #include "testing/embedder_test.h" ++#include "testing/embedder_test_constants.h" + #include "testing/gtest/include/gtest/gtest.h" + + namespace { +@@ -92,31 +93,16 @@ class CPDFSecurityHandlerEmbedderTest : public EmbedderTest { + void VerifyHelloWorldPage(FPDF_PAGE page) { + ASSERT_TRUE(page); + +-#if defined(OS_WIN) +- const char kExpectedHash[] = "795b7ce1626931aa06af0fa23b7d80bb"; +-#elif defined(OS_MACOSX) +- const char kExpectedHash[] = "b90475ca64d1348c3bf5e2b77ad9187a"; +-#else +- const char kExpectedHash[] = "2baa4c0e1758deba1b9c908e1fbd04ed"; +-#endif +- + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 200, kExpectedHash); ++ CompareBitmap(page_bitmap.get(), 200, 200, pdfium::HelloWorldChecksum()); + } + + void VerifyModifiedHelloWorldPage(FPDF_PAGE page) { + ASSERT_TRUE(page); + +-#if defined(OS_WIN) +- const char kExpectedHash[] = "93db13099042bafefb3c22a165bad684"; +-#elif defined(OS_MACOSX) +- const char kExpectedHash[] = "f8fbd14a048b9e2ea8e5f059f22a910e"; +-#else +- const char kExpectedHash[] = "93dcc09055f87a2792c8e3065af99a1b"; +-#endif +- + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 200, kExpectedHash); ++ CompareBitmap(page_bitmap.get(), 200, 200, ++ pdfium::HelloWorldRemovedChecksum()); + } + }; + +@@ -148,20 +134,16 @@ TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPassword) { + EXPECT_EQ(0xFFFFFFFC, FPDF_GetDocPermissions(document())); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_PasswordAfterGenerateSave DISABLED_PasswordAfterGenerateSave ++TEST_F(CPDFSecurityHandlerEmbedderTest, PasswordAfterGenerateSave) { ++ const char* checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "df9fe67555b7ceb59c99036e8d2c1c76"; ++#if BUILDFLAG(IS_APPLE) ++ return "2a308e8cc20a6221112c387d122075a8"; + #else +-#define MAYBE_PasswordAfterGenerateSave PasswordAfterGenerateSave +-#endif +-TEST_F(CPDFSecurityHandlerEmbedderTest, MAYBE_PasswordAfterGenerateSave) { +-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_ +- const char md5[] = "7048dca58e2ed8f93339008b91e4eb4e"; +-#elif defined(OS_MACOSX) +- const char md5[] = "6951b6c9891dfe0332a5b1983e484400"; +-#else +- const char md5[] = "041c2fb541c8907cc22ce101b686c79e"; +-#endif // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_ ++ return "9fe7eef8e51d15a604001854be6ed1ee"; ++#endif // BUILDFLAG(IS_APPLE) ++ }(); + { + ASSERT_TRUE(OpenDocumentWithOptions("encrypted.pdf", "5678", + LinearizeOption::kMustLinearize, +@@ -174,7 +156,7 @@ TEST_F(CPDFSecurityHandlerEmbedderTest, MAYBE_PasswordAfterGenerateSave) { + EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0)); + FPDFPage_InsertObject(page, red_rect); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), 612, 792, md5); ++ CompareBitmap(bitmap.get(), 612, 792, checksum); + EXPECT_TRUE(FPDFPage_GenerateContent(page)); + SetWholeFileAvailable(); + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); +@@ -196,8 +178,9 @@ TEST_F(CPDFSecurityHandlerEmbedderTest, MAYBE_PasswordAfterGenerateSave) { + for (const auto& test : tests) { + ASSERT_TRUE(OpenSavedDocumentWithPassword(test.password)); + FPDF_PAGE page = LoadSavedPage(0); +- VerifySavedRendering(page, 612, 792, md5); +- EXPECT_EQ(test.permissions, FPDF_GetDocPermissions(saved_document_)); ++ ASSERT_TRUE(page); ++ VerifySavedRendering(page, 612, 792, checksum); ++ EXPECT_EQ(test.permissions, FPDF_GetDocPermissions(saved_document())); + + CloseSavedPage(page); + CloseSavedDocument(); +@@ -679,3 +662,7 @@ TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion6Latin1) { + VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelLatin1); + VerifySavedModifiedHelloWorldDocumentWithPassword(kHotelUTF8); + } ++ ++TEST_F(CPDFSecurityHandlerEmbedderTest, Bug1124998) { ++ OpenAndVerifyHelloWorldDocumentWithPassword("bug_1124998.pdf", "test"); ++} +diff --git a/core/fpdfapi/parser/cpdf_seekablemultistream.cpp b/core/fpdfapi/parser/cpdf_seekablemultistream.cpp +index d2a04174b..bad6d97c3 100644 +--- a/core/fpdfapi/parser/cpdf_seekablemultistream.cpp ++++ b/core/fpdfapi/parser/cpdf_seekablemultistream.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,32 +7,35 @@ + #include "core/fpdfapi/parser/cpdf_seekablemultistream.h" + + #include ++#include + ++#include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/span_util.h" ++#include "core/fxcrt/stl_util.h" ++#include "third_party/base/notreached.h" + + CPDF_SeekableMultiStream::CPDF_SeekableMultiStream( +- const std::vector& streams) { +- for (const CPDF_Stream* pStream : streams) { +- m_Data.push_back(pdfium::MakeRetain(pStream)); ++ std::vector> streams) { ++ for (auto& pStream : streams) { ++ m_Data.push_back(pdfium::MakeRetain(std::move(pStream))); + m_Data.back()->LoadAllDataFiltered(); + } + } + +-CPDF_SeekableMultiStream::~CPDF_SeekableMultiStream() {} ++CPDF_SeekableMultiStream::~CPDF_SeekableMultiStream() = default; + + FX_FILESIZE CPDF_SeekableMultiStream::GetSize() { +- uint32_t dwSize = 0; ++ FX_SAFE_FILESIZE dwSize = 0; + for (const auto& acc : m_Data) + dwSize += acc->GetSize(); +- return dwSize; ++ return dwSize.ValueOrDie(); + } + +-bool CPDF_SeekableMultiStream::ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) { +- int32_t iCount = pdfium::CollectionSize(m_Data); ++bool CPDF_SeekableMultiStream::ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) { ++ int32_t iCount = fxcrt::CollectionSize(m_Data); + int32_t index = 0; + while (index < iCount) { + const auto& acc = m_Data[index]; +@@ -44,22 +47,20 @@ bool CPDF_SeekableMultiStream::ReadBlockAtOffset(void* buffer, + index++; + } + while (index < iCount) { +- const auto& acc = m_Data[index]; +- uint32_t dwSize = acc->GetSize(); +- size_t dwRead = std::min(size, static_cast(dwSize - offset)); +- memcpy(buffer, acc->GetSpan().subspan(offset, dwRead).data(), dwRead); +- size -= dwRead; +- if (size == 0) ++ auto acc_span = m_Data[index]->GetSpan(); ++ size_t dwRead = std::min(buffer.size(), acc_span.size() - offset); ++ fxcrt::spancpy(buffer, acc_span.subspan(offset, dwRead)); ++ buffer = buffer.subspan(dwRead); ++ if (buffer.empty()) + return true; + +- buffer = static_cast(buffer) + dwRead; + offset = 0; + index++; + } + return false; + } + +-size_t CPDF_SeekableMultiStream::ReadBlock(void* buffer, size_t size) { ++size_t CPDF_SeekableMultiStream::ReadBlock(pdfium::span buffer) { + NOTREACHED(); + return 0; + } +@@ -77,9 +78,9 @@ bool CPDF_SeekableMultiStream::Flush() { + return false; + } + +-bool CPDF_SeekableMultiStream::WriteBlockAtOffset(const void* pData, +- FX_FILESIZE offset, +- size_t size) { ++bool CPDF_SeekableMultiStream::WriteBlockAtOffset( ++ pdfium::span buffer, ++ FX_FILESIZE offset) { + NOTREACHED(); + return false; + } +diff --git a/core/fpdfapi/parser/cpdf_seekablemultistream.h b/core/fpdfapi/parser/cpdf_seekablemultistream.h +index 30a8479c7..fc659a64d 100644 +--- a/core/fpdfapi/parser/cpdf_seekablemultistream.h ++++ b/core/fpdfapi/parser/cpdf_seekablemultistream.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -18,21 +18,19 @@ class CPDF_StreamAcc; + class CPDF_SeekableMultiStream final : public IFX_SeekableStream { + public: + explicit CPDF_SeekableMultiStream( +- const std::vector& streams); ++ std::vector> streams); + ~CPDF_SeekableMultiStream() override; + +- // IFX_SeekableReadStream ++ // IFX_SeekableReadStream: + FX_FILESIZE GetPosition() override; + FX_FILESIZE GetSize() override; +- bool ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) override; +- size_t ReadBlock(void* buffer, size_t size) override; + bool IsEOF() override; ++ size_t ReadBlock(pdfium::span buffer) override; ++ bool ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) override; ++ bool WriteBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) override; + bool Flush() override; +- bool WriteBlockAtOffset(const void* pData, +- FX_FILESIZE offset, +- size_t size) override; + + private: + std::vector> m_Data; +diff --git a/core/fpdfapi/parser/cpdf_seekablemultistream_unittest.cpp b/core/fpdfapi/parser/cpdf_seekablemultistream_unittest.cpp +index 4f806fa05..5e63db066 100644 +--- a/core/fpdfapi/parser/cpdf_seekablemultistream_unittest.cpp ++++ b/core/fpdfapi/parser/cpdf_seekablemultistream_unittest.cpp +@@ -1,85 +1,91 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/parser/cpdf_seekablemultistream.h" + +-#include ++#include + #include + + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_stream.h" ++#include "core/fxcrt/data_vector.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + + TEST(CPDFSeekableMultiStreamTest, NoStreams) { +- std::vector streams; +- auto fileread = pdfium::MakeRetain(streams); ++ std::vector> streams; ++ auto fileread = ++ pdfium::MakeRetain(std::move(streams)); + + uint8_t output_buffer[16]; + memset(output_buffer, 0xbd, sizeof(output_buffer)); +- EXPECT_FALSE(fileread->ReadBlockAtOffset(output_buffer, 0, 0)); ++ EXPECT_FALSE(fileread->ReadBlockAtOffset({output_buffer, 0}, 0)); + EXPECT_EQ(0xbd, output_buffer[0]); + } + + TEST(CXFAFileReadTest, EmptyStreams) { +- std::vector streams; +- auto stream1 = pdfium::MakeRetain(); +- streams.push_back(stream1.Get()); +- auto fileread = pdfium::MakeRetain(streams); ++ std::vector> streams; ++ streams.push_back(pdfium::MakeRetain()); ++ auto fileread = ++ pdfium::MakeRetain(std::move(streams)); + + uint8_t output_buffer[16]; + memset(output_buffer, 0xbd, sizeof(output_buffer)); +- EXPECT_FALSE(fileread->ReadBlockAtOffset(output_buffer, 0, 0)); ++ EXPECT_FALSE(fileread->ReadBlockAtOffset({output_buffer, 0}, 0)); + EXPECT_EQ(0xbd, output_buffer[0]); + } + + TEST(CXFAFileReadTest, NormalStreams) { +- std::vector streams; +- auto stream1 = pdfium::MakeRetain(); +- auto stream2 = pdfium::MakeRetain(); +- auto stream3 = pdfium::MakeRetain(); +- + // 16 chars total. +- stream1->InitStream(ByteStringView("one t").raw_span(), +- pdfium::MakeRetain()); +- stream2->InitStream(ByteStringView("wo ").raw_span(), +- pdfium::MakeRetain()); +- stream3->InitStream(ByteStringView("three!!!").raw_span(), +- pdfium::MakeRetain()); +- +- streams.push_back(stream1.Get()); +- streams.push_back(stream2.Get()); +- streams.push_back(stream3.Get()); +- auto fileread = pdfium::MakeRetain(streams); ++ static const char kOne[] = "one t"; ++ static const char kTwo[] = "wo "; ++ static const char kThree[] = "three!!!"; ++ ++ ByteStringView one_view(kOne); ++ ByteStringView two_view(kTwo); ++ ByteStringView three_view(kThree); ++ auto stream1 = pdfium::MakeRetain( ++ DataVector(one_view.begin(), one_view.end()), ++ pdfium::MakeRetain()); ++ auto stream2 = pdfium::MakeRetain( ++ DataVector(two_view.begin(), two_view.end()), ++ pdfium::MakeRetain()); ++ auto stream3 = pdfium::MakeRetain( ++ DataVector(three_view.begin(), three_view.end()), ++ pdfium::MakeRetain()); ++ ++ std::vector> streams; ++ streams.push_back(std::move(stream1)); ++ streams.push_back(std::move(stream2)); ++ streams.push_back(std::move(stream3)); ++ auto fileread = ++ pdfium::MakeRetain(std::move(streams)); + + uint8_t output_buffer[16]; + memset(output_buffer, 0xbd, sizeof(output_buffer)); +- EXPECT_TRUE(fileread->ReadBlockAtOffset(output_buffer, 0, 0)); ++ EXPECT_TRUE(fileread->ReadBlockAtOffset({output_buffer, 0}, 0)); + EXPECT_EQ(0xbd, output_buffer[0]); + + memset(output_buffer, 0xbd, sizeof(output_buffer)); +- EXPECT_TRUE(fileread->ReadBlockAtOffset(output_buffer, 1, 0)); ++ EXPECT_TRUE(fileread->ReadBlockAtOffset({output_buffer, 0}, 1)); + EXPECT_EQ(0xbd, output_buffer[0]); + + memset(output_buffer, 0xbd, sizeof(output_buffer)); +- EXPECT_TRUE(fileread->ReadBlockAtOffset(output_buffer, 0, 1)); ++ EXPECT_TRUE(fileread->ReadBlockAtOffset({output_buffer, 1}, 0)); + EXPECT_EQ(0, memcmp(output_buffer, "o", 1)); + EXPECT_EQ(0xbd, output_buffer[1]); + + memset(output_buffer, 0xbd, sizeof(output_buffer)); +- EXPECT_TRUE( +- fileread->ReadBlockAtOffset(output_buffer, 0, sizeof(output_buffer))); ++ EXPECT_TRUE(fileread->ReadBlockAtOffset(output_buffer, 0)); + EXPECT_EQ(0, memcmp(output_buffer, "one two three!!!", 16)); + + memset(output_buffer, 0xbd, sizeof(output_buffer)); +- EXPECT_TRUE(fileread->ReadBlockAtOffset(output_buffer, 2, 10)); ++ EXPECT_TRUE(fileread->ReadBlockAtOffset({output_buffer, 10}, 2)); + EXPECT_EQ(0, memcmp(output_buffer, "e two thre", 10)); + EXPECT_EQ(0xbd, output_buffer[11]); + + memset(output_buffer, 0xbd, sizeof(output_buffer)); +- EXPECT_FALSE( +- fileread->ReadBlockAtOffset(output_buffer, 1, sizeof(output_buffer))); ++ EXPECT_FALSE(fileread->ReadBlockAtOffset(output_buffer, 1)); + EXPECT_EQ(0, memcmp(output_buffer, "ne two three!!!", 15)); + EXPECT_EQ(0xbd, output_buffer[15]); + } +diff --git a/core/fpdfapi/parser/cpdf_simple_parser.cpp b/core/fpdfapi/parser/cpdf_simple_parser.cpp +index ff6e2cf94..ce0990426 100644 +--- a/core/fpdfapi/parser/cpdf_simple_parser.cpp ++++ b/core/fpdfapi/parser/cpdf_simple_parser.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -17,7 +17,7 @@ ByteStringView CPDF_SimpleParser::GetWord() { + uint8_t ch; + + // Skip whitespace and comment lines. +- while (1) { ++ while (true) { + if (data_.size() <= cur_pos_) + return ByteStringView(); + +@@ -31,7 +31,7 @@ ByteStringView CPDF_SimpleParser::GetWord() { + if (ch != '%') + break; + +- while (1) { ++ while (true) { + if (data_.size() <= cur_pos_) + return ByteStringView(); + +@@ -46,7 +46,7 @@ ByteStringView CPDF_SimpleParser::GetWord() { + if (PDFCharIsDelimiter(ch)) { + // Find names + if (ch == '/') { +- while (1) { ++ while (true) { + if (data_.size() <= cur_pos_) + break; + +diff --git a/core/fpdfapi/parser/cpdf_simple_parser.h b/core/fpdfapi/parser/cpdf_simple_parser.h +index f9461b644..e4bf0c088 100644 +--- a/core/fpdfapi/parser/cpdf_simple_parser.h ++++ b/core/fpdfapi/parser/cpdf_simple_parser.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,8 +7,9 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_SIMPLE_PARSER_H_ + #define CORE_FPDFAPI_PARSER_CPDF_SIMPLE_PARSER_H_ + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include ++ ++#include "core/fxcrt/bytestring.h" + #include "third_party/base/span.h" + + class CPDF_SimpleParser { +diff --git a/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp b/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp +index 5834d7739..965d5778e 100644 +--- a/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp ++++ b/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp +@@ -1,10 +1,10 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/parser/cpdf_simple_parser.h" + +-#include ++#include + + #include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "testing/gtest/include/gtest/gtest.h" +@@ -50,7 +50,7 @@ TEST(SimpleParserTest, GetWord) { + STR_IN_OUT_CASE(" $^&&*\t\0sdff ", "$^&&*"), + STR_IN_OUT_CASE("\n\r+3.5656 -11.0", "+3.5656"), + }; +- for (size_t i = 0; i < FX_ArraySize(test_data); ++i) { ++ for (size_t i = 0; i < std::size(test_data); ++i) { + const pdfium::StrFuncTestData& data = test_data[i]; + CPDF_SimpleParser parser(pdfium::make_span(data.input, data.input_size)); + ByteStringView word = parser.GetWord(); +diff --git a/core/fpdfapi/parser/cpdf_stream.cpp b/core/fpdfapi/parser/cpdf_stream.cpp +index 4b5ef5c74..fa08ffa36 100644 +--- a/core/fpdfapi/parser/cpdf_stream.cpp ++++ b/core/fpdfapi/parser/cpdf_stream.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,8 +6,10 @@ + + #include "core/fpdfapi/parser/cpdf_stream.h" + ++#include ++ ++#include + #include +-#include + + #include "constants/stream_dict_common.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" +@@ -16,74 +18,64 @@ + #include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" ++#include "core/fpdfapi/parser/fpdf_parser_utility.h" ++#include "core/fxcrt/cfx_memorystream.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/span_util.h" ++#include "third_party/base/containers/contains.h" + #include "third_party/base/numerics/safe_conversions.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" + + namespace { + + bool IsMetaDataStreamDictionary(const CPDF_Dictionary* dict) { +- return dict && dict->GetStringFor("Type") == "Metadata" && +- dict->GetStringFor("Subtype") == "XML"; ++ // See ISO 32000-1:2008 spec, table 315. ++ return ValidateDictType(dict, "Metadata") && ++ dict->GetNameFor("Subtype") == "XML"; + } + + } // namespace + +-CPDF_Stream::CPDF_Stream() {} ++CPDF_Stream::CPDF_Stream() = default; ++ ++CPDF_Stream::CPDF_Stream(RetainPtr pDict) ++ : CPDF_Stream(DataVector(), std::move(pDict)) {} + +-CPDF_Stream::CPDF_Stream(std::unique_ptr pData, +- uint32_t size, ++CPDF_Stream::CPDF_Stream(DataVector pData, + RetainPtr pDict) +- : m_pDict(std::move(pDict)) { +- TakeData(std::move(pData), size); ++ : data_(std::move(pData)), dict_(std::move(pDict)) { ++ SetLengthInDict(pdfium::base::checked_cast( ++ absl::get>(data_).size())); + } + + CPDF_Stream::~CPDF_Stream() { + m_ObjNum = kInvalidObjNum; +- if (m_pDict && m_pDict->GetObjNum() == kInvalidObjNum) +- m_pDict.Leak(); // lowercase release, release ownership. ++ if (dict_ && dict_->GetObjNum() == kInvalidObjNum) ++ dict_.Leak(); // lowercase release, release ownership. + } + + CPDF_Object::Type CPDF_Stream::GetType() const { + return kStream; + } + +-CPDF_Dictionary* CPDF_Stream::GetDict() { +- return m_pDict.Get(); +-} +- +-const CPDF_Dictionary* CPDF_Stream::GetDict() const { +- return m_pDict.Get(); +-} +- +-bool CPDF_Stream::IsStream() const { +- return true; ++const CPDF_Dictionary* CPDF_Stream::GetDictInternal() const { ++ return dict_.Get(); + } + +-CPDF_Stream* CPDF_Stream::AsStream() { ++CPDF_Stream* CPDF_Stream::AsMutableStream() { + return this; + } + +-const CPDF_Stream* CPDF_Stream::AsStream() const { +- return this; ++void CPDF_Stream::InitStreamWithEmptyData(RetainPtr pDict) { ++ dict_ = std::move(pDict); ++ TakeData({}); + } + +-void CPDF_Stream::InitStream(pdfium::span pData, +- RetainPtr pDict) { +- m_pDict = std::move(pDict); +- SetData(pData); +-} +- +-void CPDF_Stream::InitStreamFromFile( +- const RetainPtr& pFile, +- RetainPtr pDict) { +- m_bMemoryBased = false; +- m_pDataBuf.reset(); +- m_pFile = pFile; +- m_dwSize = pdfium::base::checked_cast(pFile->GetSize()); +- m_pDict = std::move(pDict); +- m_pDict->SetNewFor("Length", static_cast(m_dwSize)); ++void CPDF_Stream::InitStreamFromFile(RetainPtr pFile, ++ RetainPtr pDict) { ++ data_ = pFile; ++ dict_ = std::move(pDict); ++ SetLengthInDict(pdfium::base::checked_cast(pFile->GetSize())); + } + + RetainPtr CPDF_Stream::Clone() const { +@@ -94,29 +86,27 @@ RetainPtr CPDF_Stream::CloneNonCyclic( + bool bDirect, + std::set* pVisited) const { + pVisited->insert(this); +- auto pAcc = pdfium::MakeRetain(this); ++ auto pAcc = pdfium::MakeRetain(pdfium::WrapRetain(this)); + pAcc->LoadAllDataRaw(); + +- uint32_t streamSize = pAcc->GetSize(); +- const CPDF_Dictionary* pDict = GetDict(); ++ RetainPtr pDict = GetDict(); + RetainPtr pNewDict; +- if (pDict && !pdfium::ContainsKey(*pVisited, pDict)) { +- pNewDict = +- ToDictionary(static_cast(pDict)->CloneNonCyclic( +- bDirect, pVisited)); ++ if (pDict && !pdfium::Contains(*pVisited, pDict.Get())) { ++ pNewDict = ToDictionary(static_cast(pDict.Get()) ++ ->CloneNonCyclic(bDirect, pVisited)); + } +- return pdfium::MakeRetain(pAcc->DetachData(), streamSize, ++ return pdfium::MakeRetain(pAcc->DetachData(), + std::move(pNewDict)); + } + + void CPDF_Stream::SetDataAndRemoveFilter(pdfium::span pData) { + SetData(pData); +- m_pDict->RemoveFor("Filter"); +- m_pDict->RemoveFor(pdfium::stream::kDecodeParms); ++ dict_->RemoveFor("Filter"); ++ dict_->RemoveFor(pdfium::stream::kDecodeParms); + } + + void CPDF_Stream::SetDataFromStringstreamAndRemoveFilter( +- std::ostringstream* stream) { ++ fxcrt::ostringstream* stream) { + if (stream->tellp() <= 0) { + SetDataAndRemoveFilter({}); + return; +@@ -128,26 +118,17 @@ void CPDF_Stream::SetDataFromStringstreamAndRemoveFilter( + } + + void CPDF_Stream::SetData(pdfium::span pData) { +- std::unique_ptr data_copy; +- if (!pData.empty()) { +- data_copy.reset(FX_Alloc(uint8_t, pData.size())); +- memcpy(data_copy.get(), pData.data(), pData.size()); +- } +- TakeData(std::move(data_copy), pData.size()); ++ DataVector data_copy(pData.begin(), pData.end()); ++ TakeData(std::move(data_copy)); + } + +-void CPDF_Stream::TakeData(std::unique_ptr pData, +- uint32_t size) { +- m_bMemoryBased = true; +- m_pFile = nullptr; +- m_pDataBuf = std::move(pData); +- m_dwSize = size; +- if (!m_pDict) +- m_pDict = pdfium::MakeRetain(); +- m_pDict->SetNewFor("Length", static_cast(size)); ++void CPDF_Stream::TakeData(DataVector data) { ++ const size_t size = data.size(); ++ data_ = std::move(data); ++ SetLengthInDict(pdfium::base::checked_cast(size)); + } + +-void CPDF_Stream::SetDataFromStringstream(std::ostringstream* stream) { ++void CPDF_Stream::SetDataFromStringstream(fxcrt::ostringstream* stream) { + if (stream->tellp() <= 0) { + SetData({}); + return; +@@ -156,59 +137,72 @@ void CPDF_Stream::SetDataFromStringstream(std::ostringstream* stream) { + static_cast(stream->tellp())}); + } + +-bool CPDF_Stream::ReadRawData(FX_FILESIZE offset, +- uint8_t* buf, +- uint32_t size) const { +- if (!m_bMemoryBased && m_pFile) +- return m_pFile->ReadBlockAtOffset(buf, offset, size); ++DataVector CPDF_Stream::ReadAllRawData() const { ++ CHECK(IsFileBased()); + +- if (m_pDataBuf) +- memcpy(buf, m_pDataBuf.get() + offset, size); ++ DataVector result(GetRawSize()); ++ DCHECK(!result.empty()); + +- return true; ++ auto underlying_stream = absl::get>(data_); ++ if (!underlying_stream->ReadBlockAtOffset(result, 0)) ++ return DataVector(); ++ ++ return result; + } + + bool CPDF_Stream::HasFilter() const { +- return m_pDict && m_pDict->KeyExist("Filter"); ++ return dict_ && dict_->KeyExist("Filter"); + } + + WideString CPDF_Stream::GetUnicodeText() const { +- auto pAcc = pdfium::MakeRetain(this); ++ auto pAcc = pdfium::MakeRetain(pdfium::WrapRetain(this)); + pAcc->LoadAllDataFiltered(); + return PDF_DecodeText(pAcc->GetSpan()); + } + + bool CPDF_Stream::WriteTo(IFX_ArchiveStream* archive, + const CPDF_Encryptor* encryptor) const { +- const bool is_metadata = IsMetaDataStreamDictionary(GetDict()); +- CPDF_FlateEncoder encoder(this, !is_metadata); ++ const bool is_metadata = IsMetaDataStreamDictionary(GetDict().Get()); ++ CPDF_FlateEncoder encoder(pdfium::WrapRetain(this), !is_metadata); + +- std::vector encrypted_data; ++ DataVector encrypted_data; + pdfium::span data = encoder.GetSpan(); +- + if (encryptor && !is_metadata) { + encrypted_data = encryptor->Encrypt(data); + data = encrypted_data; + } + +- size_t size = data.size(); +- if (static_cast(encoder.GetDict()->GetIntegerFor("Length")) != size) { +- encoder.CloneDict(); +- encoder.GetClonedDict()->SetNewFor("Length", +- static_cast(size)); +- } +- +- if (!encoder.GetDict()->WriteTo(archive, encryptor)) ++ encoder.UpdateLength(data.size()); ++ if (!encoder.WriteDictTo(archive, encryptor)) + return false; + + if (!archive->WriteString("stream\r\n")) + return false; + +- if (size && !archive->WriteBlock(data.data(), size)) ++ if (!archive->WriteBlock(data)) + return false; + +- if (!archive->WriteString("\r\nendstream")) +- return false; ++ return archive->WriteString("\r\nendstream"); ++} ++ ++size_t CPDF_Stream::GetRawSize() const { ++ if (IsFileBased()) { ++ return pdfium::base::checked_cast( ++ absl::get>(data_)->GetSize()); ++ } ++ if (IsMemoryBased()) ++ return absl::get>(data_).size(); ++ DCHECK(IsUninitialized()); ++ return 0; ++} ++ ++pdfium::span CPDF_Stream::GetInMemoryRawData() const { ++ DCHECK(IsMemoryBased()); ++ return absl::get>(data_); ++} + +- return true; ++void CPDF_Stream::SetLengthInDict(int length) { ++ if (!dict_) ++ dict_ = pdfium::MakeRetain(); ++ dict_->SetNewFor("Length", length); + } +diff --git a/core/fpdfapi/parser/cpdf_stream.h b/core/fpdfapi/parser/cpdf_stream.h +index d29f65568..f1bb78bf3 100644 +--- a/core/fpdfapi/parser/cpdf_stream.h ++++ b/core/fpdfapi/parser/cpdf_stream.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,80 +7,96 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_STREAM_H_ + #define CORE_FPDFAPI_PARSER_CPDF_STREAM_H_ + ++#include ++ + #include + #include +-#include + + #include "core/fpdfapi/parser/cpdf_object.h" +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_string_wrappers.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "third_party/abseil-cpp/absl/types/variant.h" ++ ++class IFX_SeekableReadStream; + + class CPDF_Stream final : public CPDF_Object { + public: +- static const int kFileBufSize = 512; ++ static constexpr int kFileBufSize = 512; + +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // CPDF_Object: + Type GetType() const override; + RetainPtr Clone() const override; +- CPDF_Dictionary* GetDict() override; +- const CPDF_Dictionary* GetDict() const override; + WideString GetUnicodeText() const override; +- bool IsStream() const override; +- CPDF_Stream* AsStream() override; +- const CPDF_Stream* AsStream() const override; ++ CPDF_Stream* AsMutableStream() override; + bool WriteTo(IFX_ArchiveStream* archive, + const CPDF_Encryptor* encryptor) const override; + +- uint32_t GetRawSize() const { return m_dwSize; } +- // Will be null in case when stream is not memory based. +- // Use CPDF_StreamAcc to data access in all cases. +- uint8_t* GetInMemoryRawData() const { return m_pDataBuf.get(); } ++ size_t GetRawSize() const; ++ // Can only be called when stream is memory-based. ++ // This is meant to be used by CPDF_StreamAcc only. ++ // Other callers should use CPDF_StreamAcc to access data in all cases. ++ pdfium::span GetInMemoryRawData() const; + +- // Copies span into internally-owned buffer. ++ // Copies span or stream into internally-owned buffer. + void SetData(pdfium::span pData); ++ void SetDataFromStringstream(fxcrt::ostringstream* stream); + +- void TakeData(std::unique_ptr pData, uint32_t size); +- +- void SetDataFromStringstream(std::ostringstream* stream); ++ void TakeData(DataVector data); + + // Set data and remove "Filter" and "DecodeParms" fields from stream +- // dictionary. ++ // dictionary. Copies span or stream into internally-owned buffer. + void SetDataAndRemoveFilter(pdfium::span pData); +- void SetDataFromStringstreamAndRemoveFilter(std::ostringstream* stream); ++ void SetDataFromStringstreamAndRemoveFilter(fxcrt::ostringstream* stream); + +- void InitStream(pdfium::span pData, +- RetainPtr pDict); +- void InitStreamFromFile(const RetainPtr& pFile, ++ void InitStreamWithEmptyData(RetainPtr pDict); ++ void InitStreamFromFile(RetainPtr pFile, + RetainPtr pDict); + +- bool ReadRawData(FX_FILESIZE offset, uint8_t* pBuf, uint32_t buf_size) const; +- +- bool IsMemoryBased() const { return m_bMemoryBased; } ++ // Can only be called when a stream is not memory-based. ++ DataVector ReadAllRawData() const; ++ ++ bool IsUninitialized() const { ++ return absl::holds_alternative(data_); ++ } ++ bool IsFileBased() const { ++ return absl::holds_alternative>(data_); ++ } ++ bool IsMemoryBased() const { ++ return absl::holds_alternative>(data_); ++ } + bool HasFilter() const; + + private: ++ friend class CPDF_Dictionary; ++ ++ // Uninitialized. + CPDF_Stream(); +- CPDF_Stream(std::unique_ptr pData, +- uint32_t size, +- RetainPtr pDict); ++ ++ // Initializes with empty data. ++ explicit CPDF_Stream(RetainPtr pDict); ++ ++ CPDF_Stream(DataVector pData, RetainPtr pDict); + ~CPDF_Stream() override; + ++ const CPDF_Dictionary* GetDictInternal() const override; + RetainPtr CloneNonCyclic( + bool bDirect, + std::set* pVisited) const override; + +- bool m_bMemoryBased = true; +- uint32_t m_dwSize = 0; +- RetainPtr m_pDict; +- std::unique_ptr m_pDataBuf; +- RetainPtr m_pFile; ++ void SetLengthInDict(int length); ++ ++ absl::variant, ++ DataVector> ++ data_; ++ RetainPtr dict_; + }; + + inline CPDF_Stream* ToStream(CPDF_Object* obj) { +- return obj ? obj->AsStream() : nullptr; ++ return obj ? obj->AsMutableStream() : nullptr; + } + + inline const CPDF_Stream* ToStream(const CPDF_Object* obj) { +@@ -91,4 +107,8 @@ inline RetainPtr ToStream(RetainPtr obj) { + return RetainPtr(ToStream(obj.Get())); + } + ++inline RetainPtr ToStream(RetainPtr obj) { ++ return RetainPtr(ToStream(obj.Get())); ++} ++ + #endif // CORE_FPDFAPI_PARSER_CPDF_STREAM_H_ +diff --git a/core/fpdfapi/parser/cpdf_stream_acc.cpp b/core/fpdfapi/parser/cpdf_stream_acc.cpp +index cd9975125..4f42c6d6d 100644 +--- a/core/fpdfapi/parser/cpdf_stream_acc.cpp ++++ b/core/fpdfapi/parser/cpdf_stream_acc.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,15 +7,16 @@ + #include "core/fpdfapi/parser/cpdf_stream_acc.h" + + #include +-#include + + #include "core/fdrm/fx_crypt.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" ++#include "core/fxcrt/data_vector.h" ++#include "third_party/base/check_op.h" + +-CPDF_StreamAcc::CPDF_StreamAcc(const CPDF_Stream* pStream) +- : m_pStream(pStream) {} ++CPDF_StreamAcc::CPDF_StreamAcc(RetainPtr pStream) ++ : m_pStream(std::move(pStream)) {} + + CPDF_StreamAcc::~CPDF_StreamAcc() = default; + +@@ -23,8 +24,8 @@ void CPDF_StreamAcc::LoadAllData(bool bRawAccess, + uint32_t estimated_size, + bool bImageAcc) { + if (bRawAccess) { +- ASSERT(!estimated_size); +- ASSERT(!bImageAcc); ++ DCHECK(!estimated_size); ++ DCHECK(!bImageAcc); + } + + if (!m_pStream) +@@ -54,117 +55,117 @@ void CPDF_StreamAcc::LoadAllDataRaw() { + LoadAllData(true, 0, false); + } + +-const CPDF_Dictionary* CPDF_StreamAcc::GetDict() const { +- return m_pStream ? m_pStream->GetDict() : nullptr; ++RetainPtr CPDF_StreamAcc::GetStream() const { ++ return m_pStream; + } + +-uint8_t* CPDF_StreamAcc::GetData() const { +- if (m_pData.IsOwned()) +- return m_pData.Get(); +- return m_pStream ? m_pStream->GetInMemoryRawData() : nullptr; ++int CPDF_StreamAcc::GetLength1ForTest() const { ++ return m_pStream->GetDict()->GetIntegerFor("Length1"); + } + +-uint32_t CPDF_StreamAcc::GetSize() const { +- if (m_pData.IsOwned()) +- return m_dwSize; +- return (m_pStream && m_pStream->IsMemoryBased()) ? m_pStream->GetRawSize() +- : 0; ++RetainPtr CPDF_StreamAcc::GetImageParam() const { ++ return m_pImageParam; + } + +-pdfium::span CPDF_StreamAcc::GetSpan() { +- return {GetData(), GetSize()}; ++uint32_t CPDF_StreamAcc::GetSize() const { ++ return GetSpan().size(); + } + + pdfium::span CPDF_StreamAcc::GetSpan() const { +- return {GetData(), GetSize()}; ++ if (is_owned()) ++ return absl::get>(m_Data); ++ if (m_pStream && m_pStream->IsMemoryBased()) ++ return m_pStream->GetInMemoryRawData(); ++ return {}; ++} ++ ++uint64_t CPDF_StreamAcc::KeyForCache() const { ++ return m_pStream ? m_pStream->KeyForCache() : 0; + } + + ByteString CPDF_StreamAcc::ComputeDigest() const { + uint8_t digest[20]; +- CRYPT_SHA1Generate(GetData(), GetSize(), digest); ++ pdfium::span span = GetSpan(); ++ CRYPT_SHA1Generate(span.data(), span.size(), digest); + return ByteString(digest, 20); + } + +-std::unique_ptr CPDF_StreamAcc::DetachData() { +- if (m_pData.IsOwned()) { +- std::unique_ptr p = m_pData.ReleaseAndClear(); +- m_dwSize = 0; +- return p; +- } +- std::unique_ptr p(FX_Alloc(uint8_t, m_dwSize)); +- memcpy(p.get(), m_pData.Get(), m_dwSize); +- return p; ++DataVector CPDF_StreamAcc::DetachData() { ++ if (is_owned()) ++ return std::move(absl::get>(m_Data)); ++ ++ auto span = absl::get>(m_Data); ++ return DataVector(span.begin(), span.end()); + } + + void CPDF_StreamAcc::ProcessRawData() { ++ if (m_pStream->IsUninitialized()) ++ return; ++ + uint32_t dwSrcSize = m_pStream->GetRawSize(); + if (dwSrcSize == 0) + return; + + if (m_pStream->IsMemoryBased()) { +- m_pData = m_pStream->GetInMemoryRawData(); +- m_dwSize = dwSrcSize; ++ m_Data = m_pStream->GetInMemoryRawData(); + return; + } + +- std::unique_ptr pData = ReadRawStream(); +- if (!pData) ++ DataVector data = ReadRawStream(); ++ if (data.empty()) + return; + +- m_pData = std::move(pData); +- m_dwSize = dwSrcSize; ++ m_Data = std::move(data); + } + + void CPDF_StreamAcc::ProcessFilteredData(uint32_t estimated_size, + bool bImageAcc) { ++ if (m_pStream->IsUninitialized()) ++ return; ++ + uint32_t dwSrcSize = m_pStream->GetRawSize(); + if (dwSrcSize == 0) + return; + +- MaybeOwned pSrcData; ++ absl::variant, DataVector> src_data; ++ pdfium::span src_span; + if (m_pStream->IsMemoryBased()) { +- pSrcData = m_pStream->GetInMemoryRawData(); ++ src_span = m_pStream->GetInMemoryRawData(); ++ src_data = src_span; + } else { +- std::unique_ptr pTempSrcData = ReadRawStream(); +- if (!pTempSrcData) ++ DataVector temp_src_data = ReadRawStream(); ++ if (temp_src_data.empty()) + return; + +- pSrcData = std::move(pTempSrcData); ++ src_span = pdfium::make_span(temp_src_data); ++ src_data = std::move(temp_src_data); + } + + std::unique_ptr pDecodedData; + uint32_t dwDecodedSize = 0; + +- Optional>> +- decoder_array = GetDecoderArray(m_pStream->GetDict()); +- if (!decoder_array.has_value() || +- !PDF_DataDecode({pSrcData.Get(), dwSrcSize}, estimated_size, bImageAcc, ++ absl::optional decoder_array = ++ GetDecoderArray(m_pStream->GetDict()); ++ if (!decoder_array.has_value() || decoder_array.value().empty() || ++ !PDF_DataDecode(src_span, estimated_size, bImageAcc, + decoder_array.value(), &pDecodedData, &dwDecodedSize, + &m_ImageDecoder, &m_pImageParam)) { +- m_pData = std::move(pSrcData); +- m_dwSize = dwSrcSize; ++ m_Data = std::move(src_data); + return; + } + + if (pDecodedData) { +- ASSERT(pDecodedData.get() != pSrcData.Get()); +- m_pData = std::move(pDecodedData); +- m_dwSize = dwDecodedSize; ++ DCHECK_NE(pDecodedData.get(), src_span.data()); ++ // TODO(crbug.com/pdfium/1872): Avoid copying. ++ m_Data = DataVector(pDecodedData.get(), ++ pDecodedData.get() + dwDecodedSize); + } else { +- m_pData = std::move(pSrcData); +- m_dwSize = dwSrcSize; ++ m_Data = std::move(src_data); + } + } + +-std::unique_ptr CPDF_StreamAcc::ReadRawStream() const { +- ASSERT(m_pStream); +- ASSERT(!m_pStream->IsMemoryBased()); +- +- uint32_t dwSrcSize = m_pStream->GetRawSize(); +- ASSERT(dwSrcSize); +- std::unique_ptr pSrcData( +- FX_Alloc(uint8_t, dwSrcSize)); +- if (!m_pStream->ReadRawData(0, pSrcData.get(), dwSrcSize)) +- return nullptr; +- return pSrcData; ++DataVector CPDF_StreamAcc::ReadRawStream() const { ++ DCHECK(m_pStream); ++ DCHECK(m_pStream->IsFileBased()); ++ return m_pStream->ReadAllRawData(); + } +diff --git a/core/fpdfapi/parser/cpdf_stream_acc.h b/core/fpdfapi/parser/cpdf_stream_acc.h +index 046fe5d7e..1f0e726d0 100644 +--- a/core/fpdfapi/parser/cpdf_stream_acc.h ++++ b/core/fpdfapi/parser/cpdf_stream_acc.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,13 +7,14 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_STREAM_ACC_H_ + #define CORE_FPDFAPI_PARSER_CPDF_STREAM_ACC_H_ + ++#include ++ + #include + +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" +-#include "core/fxcrt/maybe_owned.h" ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/retain_ptr.h" ++#include "third_party/abseil-cpp/absl/types/variant.h" + #include "third_party/base/span.h" + + class CPDF_Dictionary; +@@ -21,8 +22,7 @@ class CPDF_Stream; + + class CPDF_StreamAcc final : public Retainable { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + CPDF_StreamAcc(const CPDF_StreamAcc&) = delete; + CPDF_StreamAcc& operator=(const CPDF_StreamAcc&) = delete; +@@ -32,34 +32,38 @@ class CPDF_StreamAcc final : public Retainable { + void LoadAllDataImageAcc(uint32_t estimated_size); + void LoadAllDataRaw(); + +- const CPDF_Stream* GetStream() const { return m_pStream.Get(); } +- const CPDF_Dictionary* GetDict() const; ++ RetainPtr GetStream() const; ++ RetainPtr GetImageParam() const; + +- uint8_t* GetData() const; + uint32_t GetSize() const; +- pdfium::span GetSpan(); + pdfium::span GetSpan() const; ++ uint64_t KeyForCache() const; + ByteString ComputeDigest() const; + ByteString GetImageDecoder() const { return m_ImageDecoder; } +- const CPDF_Dictionary* GetImageParam() const { return m_pImageParam.Get(); } +- std::unique_ptr DetachData(); ++ DataVector DetachData(); ++ ++ int GetLength1ForTest() const; + + private: +- explicit CPDF_StreamAcc(const CPDF_Stream* pStream); ++ explicit CPDF_StreamAcc(RetainPtr pStream); + ~CPDF_StreamAcc() override; + + void LoadAllData(bool bRawAccess, uint32_t estimated_size, bool bImageAcc); + void ProcessRawData(); + void ProcessFilteredData(uint32_t estimated_size, bool bImageAcc); + +- // Reads the raw data from |m_pStream|, or return nullptr on failure. +- std::unique_ptr ReadRawStream() const; ++ // Returns the raw data from `m_pStream`, or no data on failure. ++ DataVector ReadRawStream() const; ++ ++ bool is_owned() const { ++ return absl::holds_alternative>(m_Data); ++ } + +- MaybeOwned m_pData; +- uint32_t m_dwSize = 0; + ByteString m_ImageDecoder; + RetainPtr m_pImageParam; ++ // Needs to outlive `m_Data` when the data is not owned. + RetainPtr const m_pStream; ++ absl::variant, DataVector> m_Data; + }; + + #endif // CORE_FPDFAPI_PARSER_CPDF_STREAM_ACC_H_ +diff --git a/core/fpdfapi/parser/cpdf_stream_acc_unittest.cpp b/core/fpdfapi/parser/cpdf_stream_acc_unittest.cpp +index 72ba93da9..c22e655e7 100644 +--- a/core/fpdfapi/parser/cpdf_stream_acc_unittest.cpp ++++ b/core/fpdfapi/parser/cpdf_stream_acc_unittest.cpp +@@ -1,22 +1,35 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/parser/cpdf_stream_acc.h" + ++#include ++ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fxcrt/fx_stream.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/invalid_seekable_read_stream.h" + +-TEST(CPDF_StreamAccTest, ReadRawDataFailed) { ++TEST(StreamAccTest, ReadRawDataFailed) { + auto stream = pdfium::MakeRetain(); + stream->InitStreamFromFile( + pdfium::MakeRetain(1024), + pdfium::MakeRetain()); +- auto stream_acc = pdfium::MakeRetain(stream.Get()); ++ auto stream_acc = pdfium::MakeRetain(std::move(stream)); ++ stream_acc->LoadAllDataRaw(); ++ EXPECT_TRUE(stream_acc->GetSpan().empty()); ++} ++ ++// Regression test for crbug.com/1361849. Should not trigger ++// ProbeForLowSeverityLifetimeIssue() failure. ++TEST(StreamAccTest, DataStreamLifeTime) { ++ constexpr uint8_t kData[] = {'a', 'b', 'c'}; ++ auto stream = pdfium::MakeRetain(); ++ stream->SetData(kData); ++ auto stream_acc = pdfium::MakeRetain(stream); + stream_acc->LoadAllDataRaw(); +- EXPECT_EQ(0u, stream_acc->GetSize()); +- EXPECT_FALSE(stream_acc->GetData()); ++ stream.Reset(); ++ EXPECT_EQ(pdfium::make_span(kData), stream_acc->GetSpan()); + } +diff --git a/core/fpdfapi/parser/cpdf_string.cpp b/core/fpdfapi/parser/cpdf_string.cpp +index 7058169ee..7716926c9 100644 +--- a/core/fpdfapi/parser/cpdf_string.cpp ++++ b/core/fpdfapi/parser/cpdf_string.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,13 +6,14 @@ + + #include "core/fpdfapi/parser/cpdf_string.h" + ++#include ++ + #include +-#include + + #include "core/fpdfapi/parser/cpdf_encryptor.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_stream.h" +-#include "third_party/base/ptr_util.h" + + CPDF_String::CPDF_String() = default; + +@@ -24,7 +25,7 @@ CPDF_String::CPDF_String(WeakPtr pPool, + m_String = pPool->Intern(m_String); + } + +-CPDF_String::CPDF_String(WeakPtr pPool, const WideString& str) ++CPDF_String::CPDF_String(WeakPtr pPool, WideStringView str) + : m_String(PDF_EncodeText(str)) { + if (pPool) + m_String = pPool->Intern(m_String); +@@ -51,15 +52,7 @@ void CPDF_String::SetString(const ByteString& str) { + m_String = str; + } + +-bool CPDF_String::IsString() const { +- return true; +-} +- +-CPDF_String* CPDF_String::AsString() { +- return this; +-} +- +-const CPDF_String* CPDF_String::AsString() const { ++CPDF_String* CPDF_String::AsMutableString() { + return this; + } + +@@ -69,13 +62,19 @@ WideString CPDF_String::GetUnicodeText() const { + + bool CPDF_String::WriteTo(IFX_ArchiveStream* archive, + const CPDF_Encryptor* encryptor) const { +- std::vector encrypted_data; ++ DataVector encrypted_data; + pdfium::span data = m_String.raw_span(); + if (encryptor) { + encrypted_data = encryptor->Encrypt(data); + data = encrypted_data; + } +- const ByteString content = +- PDF_EncodeString(ByteString(data.data(), data.size()), IsHex()); ++ ByteStringView raw(data.data(), data.size()); ++ ByteString content = ++ m_bHex ? PDF_HexEncodeString(raw) : PDF_EncodeString(raw); + return archive->WriteString(content.AsStringView()); + } ++ ++ByteString CPDF_String::EncodeString() const { ++ return m_bHex ? PDF_HexEncodeString(m_String.AsStringView()) ++ : PDF_EncodeString(m_String.AsStringView()); ++} +diff --git a/core/fpdfapi/parser/cpdf_string.h b/core/fpdfapi/parser/cpdf_string.h +index 8efc71b12..5ae627764 100644 +--- a/core/fpdfapi/parser/cpdf_string.h ++++ b/core/fpdfapi/parser/cpdf_string.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,18 +7,15 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_STRING_H_ + #define CORE_FPDFAPI_PARSER_CPDF_STRING_H_ + +-#include +- + #include "core/fpdfapi/parser/cpdf_object.h" + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/string_pool_template.h" + #include "core/fxcrt/weak_ptr.h" + + class CPDF_String final : public CPDF_Object { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // CPDF_Object: + Type GetType() const override; +@@ -26,18 +23,17 @@ class CPDF_String final : public CPDF_Object { + ByteString GetString() const override; + WideString GetUnicodeText() const override; + void SetString(const ByteString& str) override; +- bool IsString() const override; +- CPDF_String* AsString() override; +- const CPDF_String* AsString() const override; ++ CPDF_String* AsMutableString() override; + bool WriteTo(IFX_ArchiveStream* archive, + const CPDF_Encryptor* encryptor) const override; + + bool IsHex() const { return m_bHex; } ++ ByteString EncodeString() const; + + private: + CPDF_String(); + CPDF_String(WeakPtr pPool, const ByteString& str, bool bHex); +- CPDF_String(WeakPtr pPool, const WideString& str); ++ CPDF_String(WeakPtr pPool, WideStringView str); + ~CPDF_String() override; + + ByteString m_String; +@@ -45,11 +41,15 @@ class CPDF_String final : public CPDF_Object { + }; + + inline CPDF_String* ToString(CPDF_Object* obj) { +- return obj ? obj->AsString() : nullptr; ++ return obj ? obj->AsMutableString() : nullptr; + } + + inline const CPDF_String* ToString(const CPDF_Object* obj) { + return obj ? obj->AsString() : nullptr; + } + ++inline RetainPtr ToString(RetainPtr obj) { ++ return RetainPtr(ToString(obj.Get())); ++} ++ + #endif // CORE_FPDFAPI_PARSER_CPDF_STRING_H_ +diff --git a/core/fpdfapi/parser/cpdf_syntax_parser.cpp b/core/fpdfapi/parser/cpdf_syntax_parser.cpp +index 0a55c27c5..0a85e8ece 100644 +--- a/core/fpdfapi/parser/cpdf_syntax_parser.cpp ++++ b/core/fpdfapi/parser/cpdf_syntax_parser.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,10 +6,10 @@ + + #include "core/fpdfapi/parser/cpdf_syntax_parser.h" + ++#include ++ + #include +-#include + #include +-#include + + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_boolean.h" +@@ -24,39 +24,46 @@ + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fxcrt/autorestorer.h" +-#include "core/fxcrt/cfx_binarybuf.h" ++#include "core/fxcrt/cfx_read_only_vector_stream.h" ++#include "core/fxcrt/fixed_uninit_data_vector.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_safe_types.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" + #include "third_party/base/numerics/safe_math.h" +-#include "third_party/base/ptr_util.h" + + namespace { + +-enum class ReadStatus { Normal, Backslash, Octal, FinishOctal, CarriageReturn }; ++enum class ReadStatus { ++ kNormal, ++ kBackslash, ++ kOctal, ++ kFinishOctal, ++ kCarriageReturn ++}; + + class ReadableSubStream final : public IFX_SeekableReadStream { + public: +- ReadableSubStream(const RetainPtr& pFileRead, ++ ReadableSubStream(RetainPtr pFileRead, + FX_FILESIZE part_offset, + FX_FILESIZE part_size) +- : m_pFileRead(pFileRead), ++ : m_pFileRead(std::move(pFileRead)), + m_PartOffset(part_offset), + m_PartSize(part_size) {} + + ~ReadableSubStream() override = default; + + // IFX_SeekableReadStream overrides: +- bool ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) override { ++ bool ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) override { + FX_SAFE_FILESIZE safe_end = offset; +- safe_end += size; ++ safe_end += buffer.size(); + // Check that requested range is valid, to prevent calling of ReadBlock + // of original m_pFileRead with incorrect params. + if (!safe_end.IsValid() || safe_end.ValueOrDie() > m_PartSize) + return false; + +- return m_pFileRead->ReadBlockAtOffset(buffer, m_PartOffset + offset, size); ++ return m_pFileRead->ReadBlockAtOffset(buffer, m_PartOffset + offset); + } + + FX_FILESIZE GetSize() override { return m_PartSize; } +@@ -74,26 +81,26 @@ int CPDF_SyntaxParser::s_CurrentRecursionDepth = 0; + + // static + std::unique_ptr CPDF_SyntaxParser::CreateForTesting( +- const RetainPtr& pFileAccess, ++ RetainPtr pFileAccess, + FX_FILESIZE HeaderOffset) { +- return pdfium::MakeUnique( +- pdfium::MakeRetain(pFileAccess, nullptr), ++ return std::make_unique( ++ pdfium::MakeRetain(std::move(pFileAccess), nullptr), + HeaderOffset); + } + + CPDF_SyntaxParser::CPDF_SyntaxParser( +- const RetainPtr& pFileAccess) ++ RetainPtr pFileAccess) + : CPDF_SyntaxParser( +- pdfium::MakeRetain(pFileAccess, nullptr), ++ pdfium::MakeRetain(std::move(pFileAccess), ++ nullptr), + 0) {} + +-CPDF_SyntaxParser::CPDF_SyntaxParser( +- const RetainPtr& validator, +- FX_FILESIZE HeaderOffset) +- : m_pFileAccess(validator), ++CPDF_SyntaxParser::CPDF_SyntaxParser(RetainPtr validator, ++ FX_FILESIZE HeaderOffset) ++ : m_pFileAccess(std::move(validator)), + m_HeaderOffset(HeaderOffset), + m_FileLen(m_pFileAccess->GetSize()) { +- ASSERT(m_HeaderOffset <= m_FileLen); ++ DCHECK(m_HeaderOffset <= m_FileLen); + } + + CPDF_SyntaxParser::~CPDF_SyntaxParser() = default; +@@ -114,8 +121,7 @@ bool CPDF_SyntaxParser::ReadBlockAt(FX_FILESIZE read_pos) { + read_size = m_FileLen - read_pos; + + m_pFileBuf.resize(read_size); +- if (!m_pFileAccess->ReadBlockAtOffset(m_pFileBuf.data(), read_pos, +- read_size)) { ++ if (!m_pFileAccess->ReadBlockAtOffset(m_pFileBuf, read_pos)) { + m_pFileBuf.clear(); + return false; + } +@@ -157,36 +163,34 @@ bool CPDF_SyntaxParser::GetCharAtBackward(FX_FILESIZE pos, uint8_t* ch) { + return true; + } + +-bool CPDF_SyntaxParser::ReadBlock(uint8_t* pBuf, uint32_t size) { +- if (!m_pFileAccess->ReadBlockAtOffset(pBuf, m_Pos + m_HeaderOffset, size)) ++bool CPDF_SyntaxParser::ReadBlock(pdfium::span buffer) { ++ if (!m_pFileAccess->ReadBlockAtOffset(buffer, m_Pos + m_HeaderOffset)) + return false; +- m_Pos += size; ++ m_Pos += buffer.size(); + return true; + } + +-void CPDF_SyntaxParser::GetNextWordInternal(bool* bIsNumber) { ++CPDF_SyntaxParser::WordType CPDF_SyntaxParser::GetNextWordInternal() { + m_WordSize = 0; +- if (bIsNumber) +- *bIsNumber = true; ++ WordType word_type = WordType::kNumber; + + ToNextWord(); + uint8_t ch; + if (!GetNextChar(ch)) +- return; ++ return word_type; + + if (PDFCharIsDelimiter(ch)) { +- if (bIsNumber) +- *bIsNumber = false; ++ word_type = WordType::kWord; + + m_WordBuffer[m_WordSize++] = ch; + if (ch == '/') { +- while (1) { ++ while (true) { + if (!GetNextChar(ch)) +- return; ++ return word_type; + + if (!PDFCharIsOther(ch) && !PDFCharIsNumeric(ch)) { + m_Pos--; +- return; ++ return word_type; + } + + if (m_WordSize < sizeof(m_WordBuffer) - 1) +@@ -194,7 +198,7 @@ void CPDF_SyntaxParser::GetNextWordInternal(bool* bIsNumber) { + } + } else if (ch == '<') { + if (!GetNextChar(ch)) +- return; ++ return word_type; + + if (ch == '<') + m_WordBuffer[m_WordSize++] = ch; +@@ -202,33 +206,32 @@ void CPDF_SyntaxParser::GetNextWordInternal(bool* bIsNumber) { + m_Pos--; + } else if (ch == '>') { + if (!GetNextChar(ch)) +- return; ++ return word_type; + + if (ch == '>') + m_WordBuffer[m_WordSize++] = ch; + else + m_Pos--; + } +- return; ++ return word_type; + } + +- while (1) { ++ while (true) { + if (m_WordSize < sizeof(m_WordBuffer) - 1) + m_WordBuffer[m_WordSize++] = ch; + +- if (!PDFCharIsNumeric(ch)) { +- if (bIsNumber) +- *bIsNumber = false; +- } ++ if (!PDFCharIsNumeric(ch)) ++ word_type = WordType::kWord; + + if (!GetNextChar(ch)) +- return; ++ return word_type; + + if (PDFCharIsDelimiter(ch) || PDFCharIsWhitespace(ch)) { + m_Pos--; + break; + } + } ++ return word_type; + } + + ByteString CPDF_SyntaxParser::ReadString() { +@@ -236,13 +239,13 @@ ByteString CPDF_SyntaxParser::ReadString() { + if (!GetNextChar(ch)) + return ByteString(); + +- std::ostringstream buf; ++ ByteString buf; + int32_t parlevel = 0; +- ReadStatus status = ReadStatus::Normal; ++ ReadStatus status = ReadStatus::kNormal; + int32_t iEscCode = 0; +- while (1) { ++ while (true) { + switch (status) { +- case ReadStatus::Normal: ++ case ReadStatus::kNormal: + if (ch == ')') { + if (parlevel == 0) + return ByteString(buf); +@@ -251,60 +254,59 @@ ByteString CPDF_SyntaxParser::ReadString() { + parlevel++; + } + if (ch == '\\') +- status = ReadStatus::Backslash; ++ status = ReadStatus::kBackslash; + else +- buf << static_cast(ch); ++ buf += static_cast(ch); + break; +- case ReadStatus::Backslash: ++ case ReadStatus::kBackslash: + if (FXSYS_IsOctalDigit(ch)) { + iEscCode = FXSYS_DecimalCharToInt(static_cast(ch)); +- status = ReadStatus::Octal; ++ status = ReadStatus::kOctal; + break; + } +- + if (ch == '\r') { +- status = ReadStatus::CarriageReturn; ++ status = ReadStatus::kCarriageReturn; + break; + } + if (ch == 'n') { +- buf << '\n'; ++ buf += '\n'; + } else if (ch == 'r') { +- buf << '\r'; ++ buf += '\r'; + } else if (ch == 't') { +- buf << '\t'; ++ buf += '\t'; + } else if (ch == 'b') { +- buf << '\b'; ++ buf += '\b'; + } else if (ch == 'f') { +- buf << '\f'; ++ buf += '\f'; + } else if (ch != '\n') { +- buf << static_cast(ch); ++ buf += static_cast(ch); + } +- status = ReadStatus::Normal; ++ status = ReadStatus::kNormal; + break; +- case ReadStatus::Octal: ++ case ReadStatus::kOctal: + if (FXSYS_IsOctalDigit(ch)) { + iEscCode = + iEscCode * 8 + FXSYS_DecimalCharToInt(static_cast(ch)); +- status = ReadStatus::FinishOctal; ++ status = ReadStatus::kFinishOctal; + } else { +- buf << static_cast(iEscCode); +- status = ReadStatus::Normal; ++ buf += static_cast(iEscCode); ++ status = ReadStatus::kNormal; + continue; + } + break; +- case ReadStatus::FinishOctal: +- status = ReadStatus::Normal; ++ case ReadStatus::kFinishOctal: ++ status = ReadStatus::kNormal; + if (FXSYS_IsOctalDigit(ch)) { + iEscCode = + iEscCode * 8 + FXSYS_DecimalCharToInt(static_cast(ch)); +- buf << static_cast(iEscCode); ++ buf += static_cast(iEscCode); + } else { +- buf << static_cast(iEscCode); ++ buf += static_cast(iEscCode); + continue; + } + break; +- case ReadStatus::CarriageReturn: +- status = ReadStatus::Normal; ++ case ReadStatus::kCarriageReturn: ++ status = ReadStatus::kNormal; + if (ch != '\n') + continue; + break; +@@ -315,7 +317,7 @@ ByteString CPDF_SyntaxParser::ReadString() { + } + + GetNextChar(ch); +- return ByteString(buf); ++ return buf; + } + + ByteString CPDF_SyntaxParser::ReadHexString() { +@@ -323,20 +325,20 @@ ByteString CPDF_SyntaxParser::ReadHexString() { + if (!GetNextChar(ch)) + return ByteString(); + +- std::ostringstream buf; ++ ByteString buf; + bool bFirst = true; + uint8_t code = 0; +- while (1) { ++ while (true) { + if (ch == '>') + break; + +- if (std::isxdigit(ch)) { ++ if (isxdigit(ch)) { + int val = FXSYS_HexCharToInt(ch); + if (bFirst) { + code = val * 16; + } else { + code += val; +- buf << static_cast(code); ++ buf += static_cast(code); + } + bFirst = !bFirst; + } +@@ -345,9 +347,9 @@ ByteString CPDF_SyntaxParser::ReadHexString() { + break; + } + if (!bFirst) +- buf << static_cast(code); ++ buf += static_cast(code); + +- return ByteString(buf); ++ return buf; + } + + void CPDF_SyntaxParser::ToNextLine() { +@@ -366,11 +368,16 @@ void CPDF_SyntaxParser::ToNextLine() { + } + + void CPDF_SyntaxParser::ToNextWord() { ++ if (m_TrailerEnds) { ++ RecordingToNextWord(); ++ return; ++ } ++ + uint8_t ch; + if (!GetNextChar(ch)) + return; + +- while (1) { ++ while (true) { + while (PDFCharIsWhitespace(ch)) { + if (!GetNextChar(ch)) + return; +@@ -379,7 +386,7 @@ void CPDF_SyntaxParser::ToNextWord() { + if (ch != '%') + break; + +- while (1) { ++ while (true) { + if (!GetNextChar(ch)) + return; + if (PDFCharIsLineEnding(ch)) +@@ -389,31 +396,97 @@ void CPDF_SyntaxParser::ToNextWord() { + m_Pos--; + } + +-ByteString CPDF_SyntaxParser::GetNextWord(bool* bIsNumber) { +- const CPDF_ReadValidator::Session read_session(GetValidator()); +- GetNextWordInternal(bIsNumber); +- ByteString ret; ++// A state machine which goes % -> E -> O -> F -> line ending. ++enum class EofState { ++ kInitial = 0, ++ kNonPercent, ++ kPercent, ++ kE, ++ kO, ++ kF, ++ kInvalid, ++}; ++ ++void CPDF_SyntaxParser::RecordingToNextWord() { ++ DCHECK(m_TrailerEnds); ++ ++ EofState eof_state = EofState::kInitial; ++ // Find the first character which is neither whitespace, nor part of a ++ // comment. ++ while (true) { ++ uint8_t ch; ++ if (!GetNextChar(ch)) ++ return; ++ switch (eof_state) { ++ case EofState::kInitial: ++ if (!PDFCharIsWhitespace(ch)) ++ eof_state = ch == '%' ? EofState::kPercent : EofState::kNonPercent; ++ break; ++ case EofState::kNonPercent: ++ break; ++ case EofState::kPercent: ++ if (ch == 'E') ++ eof_state = EofState::kE; ++ else if (ch != '%') ++ eof_state = EofState::kInvalid; ++ break; ++ case EofState::kE: ++ eof_state = ch == 'O' ? EofState::kO : EofState::kInvalid; ++ break; ++ case EofState::kO: ++ eof_state = ch == 'F' ? EofState::kF : EofState::kInvalid; ++ break; ++ case EofState::kF: ++ if (ch == '\r') { ++ // See if \r has to be combined with a \n that follows it ++ // immediately. ++ if (GetNextChar(ch) && ch != '\n') { ++ ch = '\r'; ++ m_Pos--; ++ } ++ } ++ // If we now have a \r, that's not followed by a \n, so both are OK. ++ if (ch == '\r' || ch == '\n') ++ m_TrailerEnds->push_back(m_Pos); ++ eof_state = EofState::kInvalid; ++ break; ++ case EofState::kInvalid: ++ break; ++ } ++ if (PDFCharIsLineEnding(ch)) ++ eof_state = EofState::kInitial; ++ if (eof_state == EofState::kNonPercent) ++ break; ++ } ++ m_Pos--; ++} ++ ++CPDF_SyntaxParser::WordResult CPDF_SyntaxParser::GetNextWord() { ++ CPDF_ReadValidator::ScopedSession read_session(GetValidator()); ++ WordType word_type = GetNextWordInternal(); ++ ByteString word; + if (!GetValidator()->has_read_problems()) +- ret = ByteString(m_WordBuffer, m_WordSize); +- return ret; ++ word = ByteString(m_WordBuffer, m_WordSize); ++ return {word, word_type == WordType::kNumber}; + } + +-ByteString CPDF_SyntaxParser::PeekNextWord(bool* bIsNumber) { ++ByteString CPDF_SyntaxParser::PeekNextWord() { + AutoRestorer save_pos(&m_Pos); +- return GetNextWord(bIsNumber); ++ return GetNextWord().word; + } + + ByteString CPDF_SyntaxParser::GetKeyword() { +- return GetNextWord(nullptr); ++ return GetNextWord().word; + } + + void CPDF_SyntaxParser::SetPos(FX_FILESIZE pos) { ++ DCHECK_GE(pos, 0); + m_Pos = std::min(pos, m_FileLen); + } + + RetainPtr CPDF_SyntaxParser::GetObjectBody( + CPDF_IndirectObjectHolder* pObjList) { +- const CPDF_ReadValidator::Session read_session(GetValidator()); ++ CPDF_ReadValidator::ScopedSession read_session(GetValidator()); + auto result = GetObjectBodyInternal(pObjList, ParseType::kLoose); + if (GetValidator()->has_read_problems()) + return nullptr; +@@ -428,19 +501,19 @@ RetainPtr CPDF_SyntaxParser::GetObjectBodyInternal( + return nullptr; + + FX_FILESIZE SavedObjPos = m_Pos; +- bool bIsNumber; +- ByteString word = GetNextWord(&bIsNumber); ++ WordResult word_result = GetNextWord(); ++ const ByteString& word = word_result.word; + if (word.IsEmpty()) + return nullptr; + +- if (bIsNumber) { ++ if (word_result.is_number) { + AutoRestorer pos_restorer(&m_Pos); +- ByteString nextword = GetNextWord(&bIsNumber); +- if (!bIsNumber) ++ WordResult nextword = GetNextWord(); ++ if (!nextword.is_number) + return pdfium::MakeRetain(word.AsStringView()); + +- ByteString nextword2 = GetNextWord(nullptr); +- if (nextword2 != "R") ++ WordResult nextword2 = GetNextWord(); ++ if (nextword2.word != "R") + return pdfium::MakeRetain(word.AsStringView()); + + pos_restorer.AbandonRestoration(); +@@ -469,7 +542,7 @@ RetainPtr CPDF_SyntaxParser::GetObjectBodyInternal( + auto pArray = pdfium::MakeRetain(); + while (RetainPtr pObj = + GetObjectBodyInternal(pObjList, ParseType::kLoose)) { +- pArray->Add(std::move(pObj)); ++ pArray->Append(std::move(pObj)); + } + return (parse_type == ParseType::kLoose || m_WordBuffer[0] == ']') + ? std::move(pArray) +@@ -483,8 +556,9 @@ RetainPtr CPDF_SyntaxParser::GetObjectBodyInternal( + if (word == "<<") { + RetainPtr pDict = + pdfium::MakeRetain(m_pPool); +- while (1) { +- ByteString inner_word = GetNextWord(nullptr); ++ while (true) { ++ WordResult inner_word_result = GetNextWord(); ++ const ByteString& inner_word = inner_word_result.word; + if (inner_word.IsEmpty()) + return nullptr; + +@@ -513,14 +587,14 @@ RetainPtr CPDF_SyntaxParser::GetObjectBodyInternal( + return nullptr; + } + +- if (!key.IsEmpty()) { +- ByteString keyNoSlash(key.raw_str() + 1, key.GetLength() - 1); +- pDict->SetFor(keyNoSlash, std::move(pObj)); ++ // `key` has to be "/X" at the minimum. ++ if (key.GetLength() > 1) { ++ pDict->SetFor(key.Substr(1), std::move(pObj)); + } + } + + AutoRestorer pos_restorer(&m_Pos); +- if (GetNextWord(nullptr) != "stream") ++ if (GetNextWord().word != "stream") + return pDict; + pos_restorer.AbandonRestoration(); + return ReadStream(std::move(pDict)); +@@ -534,22 +608,23 @@ RetainPtr CPDF_SyntaxParser::GetObjectBodyInternal( + RetainPtr CPDF_SyntaxParser::GetIndirectObject( + CPDF_IndirectObjectHolder* pObjList, + ParseType parse_type) { +- const CPDF_ReadValidator::Session read_session(GetValidator()); ++ CPDF_ReadValidator::ScopedSession read_session(GetValidator()); + const FX_FILESIZE saved_pos = GetPos(); +- bool is_number = false; +- ByteString word = GetNextWord(&is_number); +- if (!is_number || word.IsEmpty()) { ++ ++ WordResult objnum_word_result = GetNextWord(); ++ if (!objnum_word_result.is_number || objnum_word_result.word.IsEmpty()) { + SetPos(saved_pos); + return nullptr; + } +- const uint32_t parser_objnum = FXSYS_atoui(word.c_str()); ++ const uint32_t parser_objnum = FXSYS_atoui(objnum_word_result.word.c_str()); + +- word = GetNextWord(&is_number); +- if (!is_number || word.IsEmpty()) { ++ WordResult gennum_word_result = GetNextWord(); ++ const ByteString& gennum_word = gennum_word_result.word; ++ if (!gennum_word_result.is_number || gennum_word.IsEmpty()) { + SetPos(saved_pos); + return nullptr; + } +- const uint32_t parser_gennum = FXSYS_atoui(word.c_str()); ++ const uint32_t parser_gennum = FXSYS_atoui(gennum_word.c_str()); + + if (GetKeyword() != "obj") { + SetPos(saved_pos); +@@ -633,7 +708,8 @@ FX_FILESIZE CPDF_SyntaxParser::FindStreamEndPos() { + + RetainPtr CPDF_SyntaxParser::ReadStream( + RetainPtr pDict) { +- const CPDF_Number* pLenObj = ToNumber(pDict->GetDirectObjectFor("Length")); ++ RetainPtr pLenObj = ++ ToNumber(pDict->GetDirectObjectFor("Length")); + FX_FILESIZE len = pLenObj ? pLenObj->GetInteger() : -1; + + // Locate the start of stream. +@@ -647,7 +723,7 @@ RetainPtr CPDF_SyntaxParser::ReadStream( + len = -1; + } + +- RetainPtr data; ++ RetainPtr substream; + if (len > 0) { + // Check data availability first to allow the Validator to request data + // smoothly, without jumps. +@@ -656,7 +732,7 @@ RetainPtr CPDF_SyntaxParser::ReadStream( + return nullptr; + } + +- data = pdfium::MakeRetain( ++ substream = pdfium::MakeRetain( + GetValidator(), m_HeaderOffset + GetPos(), len); + SetPos(GetPos() + len); + } +@@ -667,10 +743,10 @@ RetainPtr CPDF_SyntaxParser::ReadStream( + // Note, we allow zero length streams as we need to pass them through when we + // are importing pages into a new document. + if (len >= 0) { +- const CPDF_ReadValidator::Session read_session(GetValidator()); ++ CPDF_ReadValidator::ScopedSession read_session(GetValidator()); + m_Pos += ReadEOLMarkers(GetPos()); + memset(m_WordBuffer, 0, kEndStreamStr.GetLength() + 1); +- GetNextWordInternal(nullptr); ++ GetNextWordInternal(); + if (GetValidator()->has_read_problems()) + return nullptr; + +@@ -679,7 +755,7 @@ RetainPtr CPDF_SyntaxParser::ReadStream( + // specified length, it signals the end of stream. + if (memcmp(m_WordBuffer, kEndStreamStr.raw_str(), + kEndStreamStr.GetLength()) != 0) { +- data.Reset(); ++ substream.Reset(); + len = -1; + SetPos(streamStartPos); + } +@@ -693,7 +769,7 @@ RetainPtr CPDF_SyntaxParser::ReadStream( + return nullptr; + + len = streamEndPos - streamStartPos; +- ASSERT(len >= 0); ++ DCHECK_GE(len, 0); + if (len > 0) { + SetPos(streamStartPos); + // Check data availability first to allow the Validator to request data +@@ -703,22 +779,41 @@ RetainPtr CPDF_SyntaxParser::ReadStream( + return nullptr; + } + +- data = pdfium::MakeRetain( ++ substream = pdfium::MakeRetain( + GetValidator(), m_HeaderOffset + GetPos(), len); + SetPos(GetPos() + len); + } + } + +- auto pStream = pdfium::MakeRetain(); +- if (data) { +- pStream->InitStreamFromFile(data, std::move(pDict)); ++ RetainPtr pStream; ++ if (substream) { ++ // It is unclear from CPDF_SyntaxParser's perspective what object ++ // `substream` is ultimately holding references to. To avoid unexpectedly ++ // changing object lifetimes by handing `substream` to `pStream`, make a ++ // copy of the data here. ++ FixedUninitDataVector data(substream->GetSize()); ++ bool did_read = substream->ReadBlockAtOffset(data.writable_span(), 0); ++ CHECK(did_read); ++ auto data_as_stream = ++ pdfium::MakeRetain(std::move(data)); ++ ++ pStream = pdfium::MakeRetain(); ++ pStream->InitStreamFromFile(std::move(data_as_stream), std::move(pDict)); + } else { + DCHECK(!len); +- pStream->InitStream({}, std::move(pDict)); // Empty stream ++ pStream = pdfium::MakeRetain(std::move(pDict)); + } + const FX_FILESIZE end_stream_offset = GetPos(); + memset(m_WordBuffer, 0, kEndObjStr.GetLength() + 1); +- GetNextWordInternal(nullptr); ++ GetNextWordInternal(); ++ ++ // Allow whitespace after endstream and before a newline. ++ unsigned char ch = 0; ++ while (GetNextChar(ch)) { ++ if (!PDFCharIsWhitespace(ch) || PDFCharIsLineEnding(ch)) ++ break; ++ } ++ SetPos(GetPos() - 1); + + int numMarkers = ReadEOLMarkers(GetPos()); + if (m_WordSize == static_cast(kEndObjStr.GetLength()) && +@@ -730,15 +825,17 @@ RetainPtr CPDF_SyntaxParser::ReadStream( + } + + uint32_t CPDF_SyntaxParser::GetDirectNum() { +- bool bIsNumber; +- GetNextWordInternal(&bIsNumber); +- if (!bIsNumber) ++ if (GetNextWordInternal() != WordType::kNumber) + return 0; + + m_WordBuffer[m_WordSize] = 0; + return FXSYS_atoui(reinterpret_cast(m_WordBuffer)); + } + ++RetainPtr CPDF_SyntaxParser::GetValidator() const { ++ return m_pFileAccess; ++} ++ + bool CPDF_SyntaxParser::IsWholeWord(FX_FILESIZE startpos, + FX_FILESIZE limit, + ByteStringView tag, +@@ -750,8 +847,8 @@ bool CPDF_SyntaxParser::IsWholeWord(FX_FILESIZE startpos, + !PDFCharIsWhitespace(tag[taglen - 1]); + + uint8_t ch; +- if (bCheckRight && startpos + (int32_t)taglen <= limit && +- GetCharAt(startpos + (int32_t)taglen, ch)) { ++ if (bCheckRight && startpos + static_cast(taglen) <= limit && ++ GetCharAt(startpos + static_cast(taglen), ch)) { + if (PDFCharIsNumeric(ch) || PDFCharIsOther(ch) || + (checkKeyword && PDFCharIsDelimiter(ch))) { + return false; +@@ -775,7 +872,7 @@ bool CPDF_SyntaxParser::BackwardsSearchToWord(ByteStringView word, + + FX_FILESIZE pos = m_Pos; + int32_t offset = taglen - 1; +- while (1) { ++ while (true) { + if (limit && pos <= m_Pos - limit) + return false; + +@@ -804,10 +901,10 @@ bool CPDF_SyntaxParser::BackwardsSearchToWord(ByteStringView word, + FX_FILESIZE CPDF_SyntaxParser::FindTag(ByteStringView tag) { + const FX_FILESIZE startpos = GetPos(); + const int32_t taglen = tag.GetLength(); +- ASSERT(taglen > 0); ++ DCHECK_GT(taglen, 0); + + int32_t match = 0; +- while (1) { ++ while (true) { + uint8_t ch; + if (!GetNextChar(ch)) + return -1; +@@ -820,7 +917,6 @@ FX_FILESIZE CPDF_SyntaxParser::FindTag(ByteStringView tag) { + match = ch == tag[0] ? 1 : 0; + } + } +- return -1; + } + + bool CPDF_SyntaxParser::IsPositionRead(FX_FILESIZE pos) const { +diff --git a/core/fpdfapi/parser/cpdf_syntax_parser.h b/core/fpdfapi/parser/cpdf_syntax_parser.h +index d36aedf1d..ed79af2f7 100644 +--- a/core/fpdfapi/parser/cpdf_syntax_parser.h ++++ b/core/fpdfapi/parser/cpdf_syntax_parser.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,14 +7,20 @@ + #ifndef CORE_FPDFAPI_PARSER_CPDF_SYNTAX_PARSER_H_ + #define CORE_FPDFAPI_PARSER_CPDF_SYNTAX_PARSER_H_ + ++#include ++ + #include + #include + + #include "core/fpdfapi/parser/cpdf_stream.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_types.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/string_pool_template.h" ++#include "core/fxcrt/unowned_ptr.h" + #include "core/fxcrt/weak_ptr.h" ++#include "third_party/base/span.h" + +-class CPDF_CryptoHandler; + class CPDF_Dictionary; + class CPDF_IndirectObjectHolder; + class CPDF_Object; +@@ -24,15 +30,19 @@ class IFX_SeekableReadStream; + + class CPDF_SyntaxParser { + public: +- enum class ParseType { kStrict, kLoose }; ++ enum class ParseType : bool { kStrict, kLoose }; ++ ++ struct WordResult { ++ ByteString word; ++ bool is_number; ++ }; + + static std::unique_ptr CreateForTesting( +- const RetainPtr& pFileAccess, ++ RetainPtr pFileAccess, + FX_FILESIZE HeaderOffset); + +- explicit CPDF_SyntaxParser( +- const RetainPtr& pFileAccess); +- CPDF_SyntaxParser(const RetainPtr& pValidator, ++ explicit CPDF_SyntaxParser(RetainPtr pFileAccess); ++ CPDF_SyntaxParser(RetainPtr pValidator, + FX_FILESIZE HeaderOffset); + ~CPDF_SyntaxParser(); + +@@ -44,23 +54,21 @@ class CPDF_SyntaxParser { + void SetPos(FX_FILESIZE pos); + + RetainPtr GetObjectBody(CPDF_IndirectObjectHolder* pObjList); +- + RetainPtr GetIndirectObject(CPDF_IndirectObjectHolder* pObjList, + ParseType parse_type); + + ByteString GetKeyword(); + void ToNextLine(); + void ToNextWord(); ++ void RecordingToNextWord(); + bool BackwardsSearchToWord(ByteStringView word, FX_FILESIZE limit); + FX_FILESIZE FindTag(ByteStringView tag); +- bool ReadBlock(uint8_t* pBuf, uint32_t size); ++ bool ReadBlock(pdfium::span buffer); + bool GetCharAt(FX_FILESIZE pos, uint8_t& ch); +- ByteString GetNextWord(bool* bIsNumber); +- ByteString PeekNextWord(bool* bIsNumber); ++ WordResult GetNextWord(); ++ ByteString PeekNextWord(); + +- const RetainPtr& GetValidator() const { +- return m_pFileAccess; +- } ++ RetainPtr GetValidator() const; + uint32_t GetDirectNum(); + bool GetNextChar(uint8_t& ch); + +@@ -75,16 +83,22 @@ class CPDF_SyntaxParser { + ByteString ReadString(); + ByteString ReadHexString(); + ++ void SetTrailerEnds(std::vector* trailer_ends) { ++ m_TrailerEnds = trailer_ends; ++ } ++ + private: ++ enum class WordType : bool { kWord, kNumber }; ++ + friend class CPDF_DataAvail; + friend class cpdf_syntax_parser_ReadHexString_Test; + +- static const int kParserMaxRecursionDepth = 64; ++ static constexpr int kParserMaxRecursionDepth = 64; + static int s_CurrentRecursionDepth; + + bool ReadBlockAt(FX_FILESIZE read_pos); + bool GetCharAtBackward(FX_FILESIZE pos, uint8_t* ch); +- void GetNextWordInternal(bool* bIsNumber); ++ WordType GetNextWordInternal(); + bool IsWholeWord(FX_FILESIZE startpos, + FX_FILESIZE limit, + ByteStringView tag, +@@ -109,11 +123,14 @@ class CPDF_SyntaxParser { + const FX_FILESIZE m_FileLen; + FX_FILESIZE m_Pos = 0; + WeakPtr m_pPool; +- std::vector m_pFileBuf; ++ DataVector m_pFileBuf; + FX_FILESIZE m_BufOffset = 0; + uint32_t m_WordSize = 0; +- uint8_t m_WordBuffer[257]; ++ uint8_t m_WordBuffer[257] = {}; + uint32_t m_ReadBufferSize = CPDF_Stream::kFileBufSize; ++ ++ // The syntax parser records traversed trailer end byte offsets here. ++ UnownedPtr> m_TrailerEnds; + }; + + #endif // CORE_FPDFAPI_PARSER_CPDF_SYNTAX_PARSER_H_ +diff --git a/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp b/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp +index 53f0a17f8..94ab97748 100644 +--- a/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp ++++ b/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp +@@ -1,24 +1,23 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include +-#include + + #include "core/fpdfapi/parser/cpdf_object.h" + #include "core/fpdfapi/parser/cpdf_parser.h" + #include "core/fpdfapi/parser/cpdf_syntax_parser.h" +-#include "core/fxcrt/cfx_readonlymemorystream.h" ++#include "core/fxcrt/cfx_read_only_span_stream.h" + #include "core/fxcrt/fx_extension.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/utils/path_service.h" + +-TEST(cpdf_syntax_parser, ReadHexString) { ++TEST(SyntaxParserTest, ReadHexString) { + { + // Empty string. + static const uint8_t data[] = ""; +- CPDF_SyntaxParser parser(pdfium::MakeRetain( +- pdfium::make_span(data, 0))); ++ CPDF_SyntaxParser parser( ++ pdfium::MakeRetain(pdfium::make_span(data, 0))); + EXPECT_EQ("", parser.ReadHexString()); + EXPECT_EQ(0, parser.GetPos()); + } +@@ -26,8 +25,8 @@ TEST(cpdf_syntax_parser, ReadHexString) { + { + // Blank string. + static const uint8_t data[] = " "; +- CPDF_SyntaxParser parser(pdfium::MakeRetain( +- pdfium::make_span(data, 2))); ++ CPDF_SyntaxParser parser( ++ pdfium::MakeRetain(pdfium::make_span(data, 2))); + EXPECT_EQ("", parser.ReadHexString()); + EXPECT_EQ(2, parser.GetPos()); + } +@@ -35,8 +34,8 @@ TEST(cpdf_syntax_parser, ReadHexString) { + { + // Skips unknown characters. + static const uint8_t data[] = "z12b"; +- CPDF_SyntaxParser parser(pdfium::MakeRetain( +- pdfium::make_span(data, 4))); ++ CPDF_SyntaxParser parser( ++ pdfium::MakeRetain(pdfium::make_span(data, 4))); + EXPECT_EQ("\x12\xb0", parser.ReadHexString()); + EXPECT_EQ(4, parser.GetPos()); + } +@@ -44,7 +43,7 @@ TEST(cpdf_syntax_parser, ReadHexString) { + { + // Skips unknown characters. + static const uint8_t data[] = "*<&*#$^&@1"; +- CPDF_SyntaxParser parser(pdfium::MakeRetain( ++ CPDF_SyntaxParser parser(pdfium::MakeRetain( + pdfium::make_span(data, 10))); + EXPECT_EQ("\x10", parser.ReadHexString()); + EXPECT_EQ(10, parser.GetPos()); +@@ -53,8 +52,8 @@ TEST(cpdf_syntax_parser, ReadHexString) { + { + // Skips unknown characters. + static const uint8_t data[] = "\x80zab"; +- CPDF_SyntaxParser parser(pdfium::MakeRetain( +- pdfium::make_span(data, 4))); ++ CPDF_SyntaxParser parser( ++ pdfium::MakeRetain(pdfium::make_span(data, 4))); + EXPECT_EQ("\xab", parser.ReadHexString()); + EXPECT_EQ(4, parser.GetPos()); + } +@@ -62,8 +61,8 @@ TEST(cpdf_syntax_parser, ReadHexString) { + { + // Skips unknown characters. + static const uint8_t data[] = "\xffzab"; +- CPDF_SyntaxParser parser(pdfium::MakeRetain( +- pdfium::make_span(data, 4))); ++ CPDF_SyntaxParser parser( ++ pdfium::MakeRetain(pdfium::make_span(data, 4))); + EXPECT_EQ("\xab", parser.ReadHexString()); + EXPECT_EQ(4, parser.GetPos()); + } +@@ -71,8 +70,8 @@ TEST(cpdf_syntax_parser, ReadHexString) { + { + // Regular conversion. + static const uint8_t data[] = "1A2b>abcd"; +- CPDF_SyntaxParser parser(pdfium::MakeRetain( +- pdfium::make_span(data, 9))); ++ CPDF_SyntaxParser parser( ++ pdfium::MakeRetain(pdfium::make_span(data, 9))); + EXPECT_EQ("\x1a\x2b", parser.ReadHexString()); + EXPECT_EQ(5, parser.GetPos()); + } +@@ -80,17 +79,14 @@ TEST(cpdf_syntax_parser, ReadHexString) { + { + // Position out of bounds. + static const uint8_t data[] = "12ab>"; +- CPDF_SyntaxParser parser(pdfium::MakeRetain( +- pdfium::make_span(data, 5))); ++ CPDF_SyntaxParser parser( ++ pdfium::MakeRetain(pdfium::make_span(data, 5))); + parser.SetPos(5); + EXPECT_EQ("", parser.ReadHexString()); + + parser.SetPos(6); + EXPECT_EQ("", parser.ReadHexString()); + +- parser.SetPos(-1); +- EXPECT_EQ("", parser.ReadHexString()); +- + parser.SetPos(std::numeric_limits::max()); + EXPECT_EQ("", parser.ReadHexString()); + +@@ -102,8 +98,8 @@ TEST(cpdf_syntax_parser, ReadHexString) { + { + // Missing ending >. + static const uint8_t data[] = "1A2b"; +- CPDF_SyntaxParser parser(pdfium::MakeRetain( +- pdfium::make_span(data, 4))); ++ CPDF_SyntaxParser parser( ++ pdfium::MakeRetain(pdfium::make_span(data, 4))); + EXPECT_EQ("\x1a\x2b", parser.ReadHexString()); + EXPECT_EQ(4, parser.GetPos()); + } +@@ -111,8 +107,8 @@ TEST(cpdf_syntax_parser, ReadHexString) { + { + // Missing ending >. + static const uint8_t data[] = "12abz"; +- CPDF_SyntaxParser parser(pdfium::MakeRetain( +- pdfium::make_span(data, 5))); ++ CPDF_SyntaxParser parser( ++ pdfium::MakeRetain(pdfium::make_span(data, 5))); + EXPECT_EQ("\x12\xab", parser.ReadHexString()); + EXPECT_EQ(5, parser.GetPos()); + } +@@ -120,8 +116,8 @@ TEST(cpdf_syntax_parser, ReadHexString) { + { + // Uneven number of bytes. + static const uint8_t data[] = "1A2>asdf"; +- CPDF_SyntaxParser parser(pdfium::MakeRetain( +- pdfium::make_span(data, 8))); ++ CPDF_SyntaxParser parser( ++ pdfium::MakeRetain(pdfium::make_span(data, 8))); + EXPECT_EQ("\x1a\x20", parser.ReadHexString()); + EXPECT_EQ(4, parser.GetPos()); + } +@@ -129,8 +125,8 @@ TEST(cpdf_syntax_parser, ReadHexString) { + { + // Uneven number of bytes. + static const uint8_t data[] = "1A2zasdf"; +- CPDF_SyntaxParser parser(pdfium::MakeRetain( +- pdfium::make_span(data, 8))); ++ CPDF_SyntaxParser parser( ++ pdfium::MakeRetain(pdfium::make_span(data, 8))); + EXPECT_EQ("\x1a\x2a\xdf", parser.ReadHexString()); + EXPECT_EQ(8, parser.GetPos()); + } +@@ -138,25 +134,25 @@ TEST(cpdf_syntax_parser, ReadHexString) { + { + // Just ending character. + static const uint8_t data[] = ">"; +- CPDF_SyntaxParser parser(pdfium::MakeRetain( +- pdfium::make_span(data, 1))); ++ CPDF_SyntaxParser parser( ++ pdfium::MakeRetain(pdfium::make_span(data, 1))); + EXPECT_EQ("", parser.ReadHexString()); + EXPECT_EQ(1, parser.GetPos()); + } + } + +-TEST(cpdf_syntax_parser, GetInvalidReference) { ++TEST(SyntaxParserTest, GetInvalidReference) { + // Data with a reference with number CPDF_Object::kInvalidObjNum + static const uint8_t data[] = "4294967295 0 R"; +- CPDF_SyntaxParser parser(pdfium::MakeRetain( +- pdfium::make_span(data, 14))); ++ CPDF_SyntaxParser parser( ++ pdfium::MakeRetain(pdfium::make_span(data, 14))); + RetainPtr ref = parser.GetObjectBody(nullptr); + EXPECT_FALSE(ref); + } + +-TEST(cpdf_syntax_parser, PeekNextWord) { ++TEST(SyntaxParserTest, PeekNextWord) { + static const uint8_t data[] = " WORD "; +- CPDF_SyntaxParser parser(pdfium::MakeRetain(data)); +- EXPECT_EQ("WORD", parser.PeekNextWord(nullptr)); +- EXPECT_EQ("WORD", parser.GetNextWord(nullptr)); ++ CPDF_SyntaxParser parser(pdfium::MakeRetain(data)); ++ EXPECT_EQ("WORD", parser.PeekNextWord()); ++ EXPECT_EQ("WORD", parser.GetNextWord().word); + } +diff --git a/core/fpdfapi/parser/cpdf_test_document.cpp b/core/fpdfapi/parser/cpdf_test_document.cpp +new file mode 100644 +index 000000000..d13103f90 +--- /dev/null ++++ b/core/fpdfapi/parser/cpdf_test_document.cpp +@@ -0,0 +1,19 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fpdfapi/parser/cpdf_test_document.h" ++ ++#include ++#include ++ ++#include "core/fpdfapi/page/cpdf_docpagedata.h" ++#include "core/fpdfapi/render/cpdf_docrenderdata.h" ++ ++CPDF_TestDocument::CPDF_TestDocument() ++ : CPDF_Document(std::make_unique(), ++ std::make_unique()) {} ++ ++void CPDF_TestDocument::SetRoot(RetainPtr root) { ++ SetRootForTesting(std::move(root)); ++} +diff --git a/core/fpdfapi/parser/cpdf_test_document.h b/core/fpdfapi/parser/cpdf_test_document.h +new file mode 100644 +index 000000000..f50dc8ed3 +--- /dev/null ++++ b/core/fpdfapi/parser/cpdf_test_document.h +@@ -0,0 +1,20 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FPDFAPI_PARSER_CPDF_TEST_DOCUMENT_H_ ++#define CORE_FPDFAPI_PARSER_CPDF_TEST_DOCUMENT_H_ ++ ++#include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fxcrt/retain_ptr.h" ++ ++class CPDF_Dictionary; ++ ++class CPDF_TestDocument : public CPDF_Document { ++ public: ++ CPDF_TestDocument(); ++ ++ void SetRoot(RetainPtr root); ++}; ++ ++#endif // CORE_FPDFAPI_PARSER_CPDF_TEST_DOCUMENT_H_ +diff --git a/core/fpdfapi/parser/fpdf_parser_decode.cpp b/core/fpdfapi/parser/fpdf_parser_decode.cpp +index 707406167..6f29b7a83 100644 +--- a/core/fpdfapi/parser/fpdf_parser_decode.cpp ++++ b/core/fpdfapi/parser/fpdf_parser_decode.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,12 +6,11 @@ + + #include "core/fpdfapi/parser/fpdf_parser_decode.h" + ++#include + #include + + #include +-#include + #include +-#include + + #include "constants/stream_dict_common.h" + #include "core/fpdfapi/parser/cpdf_array.h" +@@ -19,12 +18,12 @@ + #include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fxcodec/fax/faxmodule.h" + #include "core/fxcodec/flate/flatemodule.h" +-#include "core/fxcodec/fx_codec.h" + #include "core/fxcodec/scanlinedecoder.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/numerics/safe_math.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxcrt/span_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" + + namespace { + +@@ -42,7 +41,7 @@ bool CheckFlateDecodeParams(int Colors, int BitsPerComponent, int Columns) { + if (Colors < 0 || BitsPerComponent < 0 || Columns < 0) + return false; + +- pdfium::base::CheckedNumeric check = Columns; ++ FX_SAFE_INT32 check = Columns; + check *= Colors; + check *= BitsPerComponent; + if (!check.IsValid()) +@@ -57,7 +56,7 @@ uint8_t GetA85Result(uint32_t res, size_t i) { + + } // namespace + +-const uint16_t PDFDocEncoding[256] = { ++const uint16_t kPDFDocEncoding[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, + 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, + 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x02d8, 0x02c7, 0x02c6, +@@ -94,8 +93,10 @@ bool ValidateDecoderPipeline(const CPDF_Array* pDecoders) { + return true; + + for (size_t i = 0; i < count; ++i) { +- if (!pDecoders->GetObjectAt(i)->IsName()) ++ RetainPtr object = pDecoders->GetDirectObjectAt(i); ++ if (!object || !object->IsName()) { + return false; ++ } + } + + if (count == 1) +@@ -106,7 +107,7 @@ bool ValidateDecoderPipeline(const CPDF_Array* pDecoders) { + "FlateDecode", "Fl", "LZWDecode", "LZW", "ASCII85Decode", "A85", + "ASCIIHexDecode", "AHx", "RunLengthDecode", "RL"}; + for (size_t i = 0; i < count - 1; ++i) { +- if (!pdfium::ContainsValue(kValidDecoders, pDecoders->GetStringAt(i))) ++ if (!pdfium::Contains(kValidDecoders, pDecoders->GetByteStringAt(i))) + return false; + } + return true; +@@ -219,7 +220,7 @@ uint32_t HexDecode(pdfium::span src_span, + ++i; + break; + } +- if (!std::isxdigit(ch)) ++ if (!isxdigit(ch)) + continue; + + int digit = FXSYS_HexCharToInt(ch); +@@ -273,18 +274,17 @@ uint32_t RunLengthDecode(pdfium::span src_span, + if (buf_left < copy_len) { + uint32_t delta = copy_len - buf_left; + copy_len = buf_left; +- memset(&dest_span[dest_count + copy_len], '\0', delta); ++ fxcrt::spanclr(dest_span.subspan(dest_count + copy_len, delta)); + } + auto copy_span = src_span.subspan(i + 1, copy_len); +- memcpy(&dest_span[dest_count], copy_span.data(), copy_span.size()); ++ fxcrt::spancpy(dest_span.subspan(dest_count), copy_span); + dest_count += src_span[i] + 1; + i += src_span[i] + 2; + } else { +- int fill = 0; +- if (i < src_span.size() - 1) +- fill = src_span[i + 1]; +- memset(&dest_span[dest_count], fill, 257 - src_span[i]); +- dest_count += 257 - src_span[i]; ++ const uint8_t fill = i < src_span.size() - 1 ? src_span[i + 1] : 0; ++ const size_t fill_size = 257 - src_span[i]; ++ fxcrt::spanset(dest_span.subspan(dest_count, fill_size), fill); ++ dest_count += fill_size; + i += 2; + } + } +@@ -365,43 +365,46 @@ uint32_t FlateOrLZWDecode(bool bLZW, + estimated_size, dest_buf, dest_size); + } + +-Optional>> +-GetDecoderArray(const CPDF_Dictionary* pDict) { +- const CPDF_Object* pDecoder = pDict->GetDirectObjectFor("Filter"); +- if (!pDecoder || (!pDecoder->IsArray() && !pDecoder->IsName())) +- return {}; ++absl::optional GetDecoderArray( ++ RetainPtr pDict) { ++ RetainPtr pFilter = pDict->GetDirectObjectFor("Filter"); ++ if (!pFilter) ++ return DecoderArray(); ++ ++ if (!pFilter->IsArray() && !pFilter->IsName()) ++ return absl::nullopt; + +- const CPDF_Object* pParams = ++ RetainPtr pParams = + pDict->GetDirectObjectFor(pdfium::stream::kDecodeParms); + +- std::vector> decoder_array; +- if (const CPDF_Array* pDecoders = pDecoder->AsArray()) { ++ DecoderArray decoder_array; ++ if (const CPDF_Array* pDecoders = pFilter->AsArray()) { + if (!ValidateDecoderPipeline(pDecoders)) +- return {}; ++ return absl::nullopt; + +- const CPDF_Array* pParamsArray = ToArray(pParams); ++ RetainPtr pParamsArray = ToArray(pParams); + for (size_t i = 0; i < pDecoders->size(); ++i) { +- decoder_array.push_back( +- {pDecoders->GetStringAt(i), +- pParamsArray ? pParamsArray->GetDictAt(i) : nullptr}); ++ decoder_array.emplace_back( ++ pDecoders->GetByteStringAt(i), ++ pParamsArray ? pParamsArray->GetDictAt(i) : nullptr); + } + } else { +- decoder_array.push_back( +- {pDecoder->GetString(), pParams ? pParams->GetDict() : nullptr}); ++ DCHECK(pFilter->IsName()); ++ decoder_array.emplace_back(pFilter->GetString(), ++ pParams ? pParams->GetDict() : nullptr); + } + + return decoder_array; + } + +-bool PDF_DataDecode( +- pdfium::span src_span, +- uint32_t last_estimated_size, +- bool bImageAcc, +- const std::vector>& decoder_array, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size, +- ByteString* ImageEncoding, +- RetainPtr* pImageParams) { ++bool PDF_DataDecode(pdfium::span src_span, ++ uint32_t last_estimated_size, ++ bool bImageAcc, ++ const DecoderArray& decoder_array, ++ std::unique_ptr* dest_buf, ++ uint32_t* dest_size, ++ ByteString* ImageEncoding, ++ RetainPtr* pImageParams) { + std::unique_ptr result; + // May be changed to point to |result| in the for-loop below. So put it below + // |result| and let it get destroyed first. +@@ -410,7 +413,8 @@ bool PDF_DataDecode( + for (size_t i = 0; i < nSize; ++i) { + int estimated_size = i == nSize - 1 ? last_estimated_size : 0; + ByteString decoder = decoder_array[i].first; +- const CPDF_Dictionary* pParam = ToDictionary(decoder_array[i].second); ++ RetainPtr pParam = ++ ToDictionary(decoder_array[i].second); + std::unique_ptr new_buf; + uint32_t new_size = 0xFFFFFFFF; + uint32_t offset = FX_INVALID_OFFSET; +@@ -421,7 +425,7 @@ bool PDF_DataDecode( + *ImageEncoding = "FlateDecode"; + *dest_buf = std::move(result); + *dest_size = last_span.size(); +- pImageParams->Reset(pParam); ++ *pImageParams = std::move(pParam); + return true; + } + offset = FlateOrLZWDecode(false, last_span, pParam, estimated_size, +@@ -438,7 +442,7 @@ bool PDF_DataDecode( + *ImageEncoding = "RunLengthDecode"; + *dest_buf = std::move(result); + *dest_size = last_span.size(); +- pImageParams->Reset(pParam); ++ *pImageParams = std::move(pParam); + return true; + } + offset = RunLengthDecode(last_span, &new_buf, &new_size); +@@ -449,7 +453,7 @@ bool PDF_DataDecode( + else if (decoder == "CCF") + decoder = "CCITTFaxDecode"; + *ImageEncoding = std::move(decoder); +- pImageParams->Reset(pParam); ++ *pImageParams = std::move(pParam); + *dest_buf = std::move(result); + *dest_size = last_span.size(); + return true; +@@ -506,14 +510,14 @@ WideString PDF_DecodeText(pdfium::span span) { + } else { + pdfium::span dest_buf = result.GetBuffer(span.size()); + for (size_t i = 0; i < span.size(); ++i) +- dest_buf[i] = PDFDocEncoding[span[i]]; ++ dest_buf[i] = kPDFDocEncoding[span[i]]; + dest_pos = span.size(); + } + result.ReleaseBuffer(dest_pos); + return result; + } + +-ByteString PDF_EncodeText(const WideString& str) { ++ByteString PDF_EncodeText(WideStringView str) { + size_t i = 0; + size_t len = str.GetLength(); + ByteString result; +@@ -522,7 +526,7 @@ ByteString PDF_EncodeText(const WideString& str) { + for (i = 0; i < len; ++i) { + int code; + for (code = 0; code < 256; ++code) { +- if (PDFDocEncoding[code] == str[i]) ++ if (kPDFDocEncoding[code] == str[i]) + break; + } + if (code == 256) +@@ -556,44 +560,44 @@ ByteString PDF_EncodeText(const WideString& str) { + return result; + } + +-ByteString PDF_EncodeString(const ByteString& src, bool bHex) { +- std::ostringstream result; +- int srclen = src.GetLength(); +- if (bHex) { +- result << '<'; +- for (int i = 0; i < srclen; ++i) { +- char buf[2]; +- FXSYS_IntToTwoHexChars(src[i], buf); +- result << buf[0]; +- result << buf[1]; +- } +- result << '>'; +- return ByteString(result); +- } +- result << '('; +- for (int i = 0; i < srclen; ++i) { ++ByteString PDF_EncodeString(ByteStringView src) { ++ ByteString result; ++ result.Reserve(src.GetLength() + 2); ++ result += '('; ++ for (size_t i = 0; i < src.GetLength(); ++i) { + uint8_t ch = src[i]; + if (ch == 0x0a) { +- result << "\\n"; ++ result += "\\n"; + continue; + } + if (ch == 0x0d) { +- result << "\\r"; ++ result += "\\r"; + continue; + } + if (ch == ')' || ch == '\\' || ch == '(') +- result << '\\'; +- result << static_cast(ch); ++ result += '\\'; ++ result += static_cast(ch); + } +- result << ')'; +- return ByteString(result); ++ result += ')'; ++ return result; ++} ++ ++ByteString PDF_HexEncodeString(ByteStringView src) { ++ ByteString result; ++ result.Reserve(2 * src.GetLength() + 2); ++ result += '<'; ++ for (size_t i = 0; i < src.GetLength(); ++i) { ++ char buf[2]; ++ FXSYS_IntToTwoHexChars(src[i], buf); ++ result += buf[0]; ++ result += buf[1]; ++ } ++ result += '>'; ++ return result; + } + +-bool FlateEncode(pdfium::span src_span, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size) { +- return FlateModule::Encode(src_span.data(), src_span.size(), dest_buf, +- dest_size); ++DataVector FlateEncode(pdfium::span src_span) { ++ return FlateModule::Encode(src_span); + } + + uint32_t FlateDecode(pdfium::span src_span, +diff --git a/core/fpdfapi/parser/fpdf_parser_decode.h b/core/fpdfapi/parser/fpdf_parser_decode.h +index ebb64e6da..a437455d1 100644 +--- a/core/fpdfapi/parser/fpdf_parser_decode.h ++++ b/core/fpdfapi/parser/fpdf_parser_decode.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,14 +7,17 @@ + #ifndef CORE_FPDFAPI_PARSER_FPDF_PARSER_DECODE_H_ + #define CORE_FPDFAPI_PARSER_FPDF_PARSER_DECODE_H_ + ++#include ++ + #include + #include + #include + ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_memory_wrappers.h" + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/unowned_ptr.h" +-#include "third_party/base/optional.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + #include "third_party/base/span.h" + + class CPDF_Array; +@@ -26,13 +29,14 @@ class ScanlineDecoder; + } + + // Indexed by 8-bit char code, contains unicode code points. +-extern const uint16_t PDFDocEncoding[256]; ++extern const uint16_t kPDFDocEncoding[256]; + + bool ValidateDecoderPipeline(const CPDF_Array* pDecoders); + +-ByteString PDF_EncodeString(const ByteString& src, bool bHex); ++ByteString PDF_EncodeString(ByteStringView src); ++ByteString PDF_HexEncodeString(ByteStringView src); + WideString PDF_DecodeText(pdfium::span span); +-ByteString PDF_EncodeText(const WideString& str); ++ByteString PDF_EncodeText(WideStringView str); + + std::unique_ptr CreateFaxDecoder( + pdfium::span src_span, +@@ -48,9 +52,7 @@ std::unique_ptr CreateFlateDecoder( + int bpc, + const CPDF_Dictionary* pParams); + +-bool FlateEncode(pdfium::span src_span, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size); ++DataVector FlateEncode(pdfium::span src_span); + + uint32_t FlateDecode(pdfium::span src_span, + std::unique_ptr* dest_buf, +@@ -75,17 +77,23 @@ uint32_t FlateOrLZWDecode(bool bLZW, + std::unique_ptr* dest_buf, + uint32_t* dest_size); + +-Optional>> +-GetDecoderArray(const CPDF_Dictionary* pDict); +- +-bool PDF_DataDecode( +- pdfium::span src_span, +- uint32_t estimated_size, +- bool bImageAcc, +- const std::vector>& decoder_array, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size, +- ByteString* ImageEncoding, +- RetainPtr* pImageParams); ++// Returns absl::nullopt if the filter in |pDict| is the wrong type or an ++// invalid decoder pipeline. ++// Returns an empty vector if there is no filter, or if the filter is an empty ++// array. ++// Otherwise, returns a vector of decoders. ++using DecoderArray = ++ std::vector>>; ++absl::optional GetDecoderArray( ++ RetainPtr pDict); ++ ++bool PDF_DataDecode(pdfium::span src_span, ++ uint32_t estimated_size, ++ bool bImageAcc, ++ const DecoderArray& decoder_array, ++ std::unique_ptr* dest_buf, ++ uint32_t* dest_size, ++ ByteString* ImageEncoding, ++ RetainPtr* pImageParams); + + #endif // CORE_FPDFAPI_PARSER_FPDF_PARSER_DECODE_H_ +diff --git a/core/fpdfapi/parser/fpdf_parser_decode_embeddertest.cpp b/core/fpdfapi/parser/fpdf_parser_decode_embeddertest.cpp +index 32a34dba5..e5b8b0531 100644 +--- a/core/fpdfapi/parser/fpdf_parser_decode_embeddertest.cpp ++++ b/core/fpdfapi/parser/fpdf_parser_decode_embeddertest.cpp +@@ -1,136 +1,48 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include +-#include +-#include +- + #include "build/build_config.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" +-#include "core/fxcrt/fx_memory_wrappers.h" + #include "public/cpp/fpdf_scopers.h" + #include "testing/embedder_test.h" ++#include "testing/embedder_test_constants.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/test_support.h" + + using FPDFParserDecodeEmbedderTest = EmbedderTest; ++using pdfium::kBlankPage612By792Checksum; + +-// NOTE: python's zlib.compress() and zlib.decompress() may be useful for +-// external validation of the FlateEncode/FlateDecode test cases. +- +-TEST_F(FPDFParserDecodeEmbedderTest, FlateEncode) { +- static const pdfium::StrFuncTestData flate_encode_cases[] = { +- STR_IN_OUT_CASE("", "\x78\x9c\x03\x00\x00\x00\x00\x01"), +- STR_IN_OUT_CASE(" ", "\x78\x9c\x53\x00\x00\x00\x21\x00\x21"), +- STR_IN_OUT_CASE("123", "\x78\x9c\x33\x34\x32\x06\x00\01\x2d\x00\x97"), +- STR_IN_OUT_CASE("\x00\xff", "\x78\x9c\x63\xf8\x0f\x00\x01\x01\x01\x00"), +- STR_IN_OUT_CASE( +- "1 0 0 -1 29 763 cm\n0 0 555 735 re\nW n\nq\n0 0 555 734.394 re\n" +- "W n\nq\n0.8009 0 0 0.8009 0 0 cm\n1 1 1 RG 1 1 1 rg\n/G0 gs\n" +- "0 0 693 917 re\nf\nQ\nQ\n", +- "\x78\x9c\x33\x54\x30\x00\x42\x5d\x43\x05\x23\x4b\x05\x73\x33\x63" +- "\x85\xe4\x5c\x2e\x90\x80\xa9\xa9\xa9\x82\xb9\xb1\xa9\x42\x51\x2a" +- "\x57\xb8\x42\x1e\x57\x21\x92\xa0\x89\x9e\xb1\xa5\x09\x92\x84\x9e" +- "\x85\x81\x81\x25\xd8\x14\x24\x26\xd0\x18\x43\x05\x10\x0c\x72\x57" +- "\x80\x30\x8a\xd2\xb9\xf4\xdd\x0d\x14\xd2\x8b\xc1\x46\x99\x59\x1a" +- "\x2b\x58\x1a\x9a\x83\x8c\x49\xe3\x0a\x04\x42\x00\x37\x4c\x1b\x42"), +- }; +- +- for (size_t i = 0; i < FX_ArraySize(flate_encode_cases); ++i) { +- const pdfium::StrFuncTestData& data = flate_encode_cases[i]; +- std::unique_ptr buf; +- uint32_t buf_size; +- EXPECT_TRUE(FlateEncode({data.input, data.input_size}, &buf, &buf_size)); +- ASSERT_TRUE(buf); +- EXPECT_EQ(data.expected_size, buf_size) << " for case " << i; +- if (data.expected_size != buf_size) +- continue; +- EXPECT_EQ(0, memcmp(data.expected, buf.get(), data.expected_size)) +- << " for case " << i; +- } +-} +- +-TEST_F(FPDFParserDecodeEmbedderTest, FlateDecode) { +- static const pdfium::DecodeTestData flate_decode_cases[] = { +- STR_IN_OUT_CASE("", "", 0), +- STR_IN_OUT_CASE("preposterous nonsense", "", 2), +- STR_IN_OUT_CASE("\x78\x9c\x03\x00\x00\x00\x00\x01", "", 8), +- STR_IN_OUT_CASE("\x78\x9c\x53\x00\x00\x00\x21\x00\x21", " ", 9), +- STR_IN_OUT_CASE("\x78\x9c\x33\x34\x32\x06\x00\01\x2d\x00\x97", "123", 11), +- STR_IN_OUT_CASE("\x78\x9c\x63\xf8\x0f\x00\x01\x01\x01\x00", "\x00\xff", +- 10), +- STR_IN_OUT_CASE( +- "\x78\x9c\x33\x54\x30\x00\x42\x5d\x43\x05\x23\x4b\x05\x73\x33\x63" +- "\x85\xe4\x5c\x2e\x90\x80\xa9\xa9\xa9\x82\xb9\xb1\xa9\x42\x51\x2a" +- "\x57\xb8\x42\x1e\x57\x21\x92\xa0\x89\x9e\xb1\xa5\x09\x92\x84\x9e" +- "\x85\x81\x81\x25\xd8\x14\x24\x26\xd0\x18\x43\x05\x10\x0c\x72\x57" +- "\x80\x30\x8a\xd2\xb9\xf4\xdd\x0d\x14\xd2\x8b\xc1\x46\x99\x59\x1a" +- "\x2b\x58\x1a\x9a\x83\x8c\x49\xe3\x0a\x04\x42\x00\x37\x4c\x1b\x42", +- "1 0 0 -1 29 763 cm\n0 0 555 735 re\nW n\nq\n0 0 555 734.394 re\n" +- "W n\nq\n0.8009 0 0 0.8009 0 0 cm\n1 1 1 RG 1 1 1 rg\n/G0 gs\n" +- "0 0 693 917 re\nf\nQ\nQ\n", +- 96), +- }; +- +- for (size_t i = 0; i < FX_ArraySize(flate_decode_cases); ++i) { +- const pdfium::DecodeTestData& data = flate_decode_cases[i]; +- std::unique_ptr buf; +- uint32_t buf_size; +- EXPECT_EQ(data.processed_size, +- FlateDecode({data.input, data.input_size}, &buf, &buf_size)) +- << " for case " << i; +- ASSERT_TRUE(buf); +- EXPECT_EQ(data.expected_size, buf_size) << " for case " << i; +- if (data.expected_size != buf_size) +- continue; +- EXPECT_EQ(0, memcmp(data.expected, buf.get(), data.expected_size)) +- << " for case " << i; +- } +-} +- +-TEST_F(FPDFParserDecodeEmbedderTest, Bug_552046) { ++TEST_F(FPDFParserDecodeEmbedderTest, Bug552046) { + // Tests specifying multiple image filters for a stream. Should not cause a + // crash when rendered. +- EXPECT_TRUE(OpenDocument("bug_552046.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_552046.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3"); ++ CompareBitmap(bitmap.get(), 612, 792, kBlankPage612By792Checksum); + UnloadPage(page); + } + +-TEST_F(FPDFParserDecodeEmbedderTest, Bug_555784) { ++TEST_F(FPDFParserDecodeEmbedderTest, Bug555784) { + // Tests bad input to the run length decoder that caused a heap overflow. + // Should not cause a crash when rendered. +- EXPECT_TRUE(OpenDocument("bug_555784.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_555784.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3"); ++ CompareBitmap(bitmap.get(), 612, 792, kBlankPage612By792Checksum); + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_Bug_455199 DISABLED_Bug_455199 +-#else +-#define MAYBE_Bug_455199 Bug_455199 +-#endif +-TEST_F(FPDFParserDecodeEmbedderTest, MAYBE_Bug_455199) { ++TEST_F(FPDFParserDecodeEmbedderTest, Bug455199) { + // Tests object numbers with a value > 01000000. + // Should open successfully. +- EXPECT_TRUE(OpenDocument("bug_455199.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_455199.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +-#if defined(OS_MACOSX) +- const char kExpectedMd5sum[] = "b90475ca64d1348c3bf5e2b77ad9187a"; +-#elif defined(OS_WIN) +- const char kExpectedMd5sum[] = "795b7ce1626931aa06af0fa23b7d80bb"; +-#else +- const char kExpectedMd5sum[] = "2baa4c0e1758deba1b9c908e1fbd04ed"; +-#endif +- CompareBitmap(bitmap.get(), 200, 200, kExpectedMd5sum); ++ ++ CompareBitmap(bitmap.get(), 200, 200, pdfium::HelloWorldChecksum()); + UnloadPage(page); + } +diff --git a/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp b/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp +index 311226f85..2c2074b66 100644 +--- a/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp ++++ b/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp +@@ -1,17 +1,22 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/parser/fpdf_parser_decode.h" + ++#include ++ + #include "core/fpdfapi/parser/cpdf_array.h" ++#include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fpdfapi/parser/cpdf_indirect_object_holder.h" + #include "core/fpdfapi/parser/cpdf_name.h" ++#include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fxcrt/fx_memory_wrappers.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/test_support.h" + +-TEST(fpdf_parser_decode, ValidateDecoderPipeline) { ++TEST(ParserDecodeTest, ValidateDecoderPipeline) { + { + // Empty decoder list is always valid. + auto decoders = pdfium::MakeRetain(); +@@ -20,99 +25,221 @@ TEST(fpdf_parser_decode, ValidateDecoderPipeline) { + { + // 1 decoder is almost always valid. + auto decoders = pdfium::MakeRetain(); +- decoders->AddNew("FlateEncode"); ++ decoders->AppendNew("FlateEncode"); + EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get())); + } + { + // 1 decoder is almost always valid, even with an unknown decoder. + auto decoders = pdfium::MakeRetain(); +- decoders->AddNew("FooBar"); ++ decoders->AppendNew("FooBar"); + EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get())); + } + { + // Valid 2 decoder pipeline. + auto decoders = pdfium::MakeRetain(); +- decoders->AddNew("AHx"); +- decoders->AddNew("LZWDecode"); ++ decoders->AppendNew("AHx"); ++ decoders->AppendNew("LZWDecode"); + EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get())); + } + { + // Valid 2 decoder pipeline. + auto decoders = pdfium::MakeRetain(); +- decoders->AddNew("ASCII85Decode"); +- decoders->AddNew("ASCII85Decode"); ++ decoders->AppendNew("ASCII85Decode"); ++ decoders->AppendNew("ASCII85Decode"); + EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get())); + } + { + // Valid 5 decoder pipeline. + auto decoders = pdfium::MakeRetain(); +- decoders->AddNew("ASCII85Decode"); +- decoders->AddNew("A85"); +- decoders->AddNew("RunLengthDecode"); +- decoders->AddNew("FlateDecode"); +- decoders->AddNew("RL"); ++ decoders->AppendNew("ASCII85Decode"); ++ decoders->AppendNew("A85"); ++ decoders->AppendNew("RunLengthDecode"); ++ decoders->AppendNew("FlateDecode"); ++ decoders->AppendNew("RL"); + EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get())); + } + { + // Valid 5 decoder pipeline, with an image decoder at the end. + auto decoders = pdfium::MakeRetain(); +- decoders->AddNew("RunLengthDecode"); +- decoders->AddNew("ASCII85Decode"); +- decoders->AddNew("FlateDecode"); +- decoders->AddNew("LZW"); +- decoders->AddNew("DCTDecode"); ++ decoders->AppendNew("RunLengthDecode"); ++ decoders->AppendNew("ASCII85Decode"); ++ decoders->AppendNew("FlateDecode"); ++ decoders->AppendNew("LZW"); ++ decoders->AppendNew("DCTDecode"); + EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get())); + } + { + // Invalid 1 decoder pipeline due to wrong type. + auto decoders = pdfium::MakeRetain(); +- decoders->AddNew("FlateEncode", false); ++ decoders->AppendNew("FlateEncode", false); + EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get())); + } + { + // Invalid 2 decoder pipeline, with 2 image decoders. + auto decoders = pdfium::MakeRetain(); +- decoders->AddNew("DCTDecode"); +- decoders->AddNew("CCITTFaxDecode"); ++ decoders->AppendNew("DCTDecode"); ++ decoders->AppendNew("CCITTFaxDecode"); + EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get())); + } + { + // Invalid 2 decoder pipeline, with 1 image decoder at the start. + auto decoders = pdfium::MakeRetain(); +- decoders->AddNew("DCTDecode"); +- decoders->AddNew("FlateDecode"); ++ decoders->AppendNew("DCTDecode"); ++ decoders->AppendNew("FlateDecode"); + EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get())); + } + { + // Invalid 2 decoder pipeline due to wrong type. + auto decoders = pdfium::MakeRetain(); +- decoders->AddNew("AHx", false); +- decoders->AddNew("LZWDecode"); ++ decoders->AppendNew("AHx", false); ++ decoders->AppendNew("LZWDecode"); + EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get())); + } + { + // Invalid 5 decoder pipeline. + auto decoders = pdfium::MakeRetain(); +- decoders->AddNew("FlateDecode"); +- decoders->AddNew("FlateDecode"); +- decoders->AddNew("DCTDecode"); +- decoders->AddNew("FlateDecode"); +- decoders->AddNew("FlateDecode"); ++ decoders->AppendNew("FlateDecode"); ++ decoders->AppendNew("FlateDecode"); ++ decoders->AppendNew("DCTDecode"); ++ decoders->AppendNew("FlateDecode"); ++ decoders->AppendNew("FlateDecode"); + EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get())); + } + { + // Invalid 5 decoder pipeline due to wrong type. + auto decoders = pdfium::MakeRetain(); +- decoders->AddNew("ASCII85Decode"); +- decoders->AddNew("A85"); +- decoders->AddNew("RunLengthDecode"); +- decoders->AddNew("FlateDecode"); +- decoders->AddNew("RL", false); ++ decoders->AppendNew("ASCII85Decode"); ++ decoders->AppendNew("A85"); ++ decoders->AppendNew("RunLengthDecode"); ++ decoders->AppendNew("FlateDecode"); ++ decoders->AppendNew("RL", false); + EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get())); + } + } + +-TEST(fpdf_parser_decode, A85Decode) { ++TEST(ParserDecodeTest, ValidateDecoderPipelineWithIndirectObjects) { ++ { ++ // Valid 2 decoder pipeline with indirect objects. ++ CPDF_IndirectObjectHolder objects_holder; ++ auto decoder = pdfium::MakeRetain(nullptr, "FlateDecode"); ++ uint32_t decoder_number = ++ objects_holder.AddIndirectObject(std::move(decoder)); ++ ++ auto decoders = pdfium::MakeRetain(); ++ decoders->AppendNew(&objects_holder, decoder_number); ++ decoders->AppendNew("LZW"); ++ EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get())); ++ } ++ { ++ // Valid 5 decoder pipeline with indirect objects, with an image decoder at ++ // the end. ++ CPDF_IndirectObjectHolder objects_holder; ++ auto decoder = pdfium::MakeRetain(nullptr, "LZW"); ++ uint32_t decoder_number = ++ objects_holder.AddIndirectObject(std::move(decoder)); ++ ++ auto decoders = pdfium::MakeRetain(); ++ decoders->AppendNew("RunLengthDecode"); ++ decoders->AppendNew("ASCII85Decode"); ++ decoders->AppendNew("FlateDecode"); ++ decoders->AppendNew(&objects_holder, decoder_number); ++ decoders->AppendNew("DCTDecode"); ++ EXPECT_TRUE(ValidateDecoderPipeline(decoders.Get())); ++ } ++ { ++ // Invalid 2 decoder pipeline due to wrong type indirect object. ++ CPDF_IndirectObjectHolder objects_holder; ++ auto decoder = ++ pdfium::MakeRetain(nullptr, "FlateDecode", false); ++ uint32_t decoder_number = ++ objects_holder.AddIndirectObject(std::move(decoder)); ++ ++ auto decoders = pdfium::MakeRetain(); ++ decoders->AppendNew(&objects_holder, decoder_number); ++ decoders->AppendNew("LZW"); ++ EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get())); ++ } ++ { ++ // Invalid 2 decoder pipeline due to invalid indirect object. ++ CPDF_IndirectObjectHolder objects_holder; ++ auto decoder = pdfium::MakeRetain(nullptr, "DCTDecode"); ++ uint32_t decoder_number = ++ objects_holder.AddIndirectObject(std::move(decoder)); ++ ++ auto decoders = pdfium::MakeRetain(); ++ decoders->AppendNew(&objects_holder, decoder_number); ++ decoders->AppendNew("LZW"); ++ EXPECT_FALSE(ValidateDecoderPipeline(decoders.Get())); ++ } ++} ++ ++// TODO(thestig): Test decoder params. ++TEST(ParserDecodeTest, GetDecoderArray) { ++ { ++ // Treat no filter as an empty filter array. ++ auto dict = pdfium::MakeRetain(); ++ absl::optional decoder_array = GetDecoderArray(dict); ++ ASSERT_TRUE(decoder_array.has_value()); ++ EXPECT_TRUE(decoder_array.value().empty()); ++ } ++ { ++ // Wrong filter type. ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Filter", "RL", false); ++ absl::optional decoder_array = GetDecoderArray(dict); ++ EXPECT_FALSE(decoder_array.has_value()); ++ } ++ { ++ // Filter name. ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Filter", "RL"); ++ absl::optional decoder_array = GetDecoderArray(dict); ++ ASSERT_TRUE(decoder_array.has_value()); ++ ASSERT_EQ(1u, decoder_array.value().size()); ++ EXPECT_EQ("RL", decoder_array.value()[0].first); ++ } ++ { ++ // Empty filter array. ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Filter"); ++ absl::optional decoder_array = GetDecoderArray(dict); ++ ASSERT_TRUE(decoder_array.has_value()); ++ EXPECT_TRUE(decoder_array.value().empty()); ++ } ++ { ++ // Valid 1 element filter array. ++ auto dict = pdfium::MakeRetain(); ++ auto filter_array = dict->SetNewFor("Filter"); ++ filter_array->AppendNew("FooBar"); ++ absl::optional decoder_array = GetDecoderArray(dict); ++ ASSERT_TRUE(decoder_array.has_value()); ++ ASSERT_EQ(1u, decoder_array.value().size()); ++ EXPECT_EQ("FooBar", decoder_array.value()[0].first); ++ } ++ { ++ // Valid 2 element filter array. ++ auto dict = pdfium::MakeRetain(); ++ auto filter_array = dict->SetNewFor("Filter"); ++ filter_array->AppendNew("AHx"); ++ filter_array->AppendNew("LZWDecode"); ++ absl::optional decoder_array = GetDecoderArray(dict); ++ ASSERT_TRUE(decoder_array.has_value()); ++ ASSERT_EQ(2u, decoder_array.value().size()); ++ EXPECT_EQ("AHx", decoder_array.value()[0].first); ++ EXPECT_EQ("LZWDecode", decoder_array.value()[1].first); ++ } ++ { ++ // Invalid 2 element filter array. ++ auto dict = pdfium::MakeRetain(); ++ auto invalid_filter_array = dict->SetNewFor("Filter"); ++ invalid_filter_array->AppendNew("DCTDecode"); ++ invalid_filter_array->AppendNew("CCITTFaxDecode"); ++ absl::optional decoder_array = GetDecoderArray(dict); ++ EXPECT_FALSE(decoder_array.has_value()); ++ } ++} ++ ++TEST(ParserDecodeTest, A85Decode) { + const pdfium::DecodeTestData kTestData[] = { + // Empty src string. + STR_IN_OUT_CASE("", "", 0), +@@ -147,7 +274,76 @@ TEST(fpdf_parser_decode, A85Decode) { + } + } + +-TEST(fpdf_parser_decode, HexDecode) { ++// NOTE: python's zlib.compress() and zlib.decompress() may be useful for ++// external validation of the FlateDncode/FlateEecode test cases. ++TEST(FPDFParserDecodeEmbedderTest, FlateDecode) { ++ static const pdfium::DecodeTestData flate_decode_cases[] = { ++ STR_IN_OUT_CASE("", "", 0), ++ STR_IN_OUT_CASE("preposterous nonsense", "", 2), ++ STR_IN_OUT_CASE("\x78\x9c\x03\x00\x00\x00\x00\x01", "", 8), ++ STR_IN_OUT_CASE("\x78\x9c\x53\x00\x00\x00\x21\x00\x21", " ", 9), ++ STR_IN_OUT_CASE("\x78\x9c\x33\x34\x32\x06\x00\01\x2d\x00\x97", "123", 11), ++ STR_IN_OUT_CASE("\x78\x9c\x63\xf8\x0f\x00\x01\x01\x01\x00", "\x00\xff", ++ 10), ++ STR_IN_OUT_CASE( ++ "\x78\x9c\x33\x54\x30\x00\x42\x5d\x43\x05\x23\x4b\x05\x73\x33\x63" ++ "\x85\xe4\x5c\x2e\x90\x80\xa9\xa9\xa9\x82\xb9\xb1\xa9\x42\x51\x2a" ++ "\x57\xb8\x42\x1e\x57\x21\x92\xa0\x89\x9e\xb1\xa5\x09\x92\x84\x9e" ++ "\x85\x81\x81\x25\xd8\x14\x24\x26\xd0\x18\x43\x05\x10\x0c\x72\x57" ++ "\x80\x30\x8a\xd2\xb9\xf4\xdd\x0d\x14\xd2\x8b\xc1\x46\x99\x59\x1a" ++ "\x2b\x58\x1a\x9a\x83\x8c\x49\xe3\x0a\x04\x42\x00\x37\x4c\x1b\x42", ++ "1 0 0 -1 29 763 cm\n0 0 555 735 re\nW n\nq\n0 0 555 734.394 re\n" ++ "W n\nq\n0.8009 0 0 0.8009 0 0 cm\n1 1 1 RG 1 1 1 rg\n/G0 gs\n" ++ "0 0 693 917 re\nf\nQ\nQ\n", ++ 96), ++ }; ++ ++ for (size_t i = 0; i < std::size(flate_decode_cases); ++i) { ++ const pdfium::DecodeTestData& data = flate_decode_cases[i]; ++ std::unique_ptr buf; ++ uint32_t buf_size; ++ EXPECT_EQ(data.processed_size, ++ FlateDecode({data.input, data.input_size}, &buf, &buf_size)) ++ << " for case " << i; ++ ASSERT_TRUE(buf); ++ EXPECT_EQ(data.expected_size, buf_size) << " for case " << i; ++ if (data.expected_size != buf_size) ++ continue; ++ EXPECT_EQ(0, memcmp(data.expected, buf.get(), data.expected_size)) ++ << " for case " << i; ++ } ++} ++ ++TEST(ParserDecodeTest, FlateEncode) { ++ static const pdfium::StrFuncTestData flate_encode_cases[] = { ++ STR_IN_OUT_CASE("", "\x78\x9c\x03\x00\x00\x00\x00\x01"), ++ STR_IN_OUT_CASE(" ", "\x78\x9c\x53\x00\x00\x00\x21\x00\x21"), ++ STR_IN_OUT_CASE("123", "\x78\x9c\x33\x34\x32\x06\x00\01\x2d\x00\x97"), ++ STR_IN_OUT_CASE("\x00\xff", "\x78\x9c\x63\xf8\x0f\x00\x01\x01\x01\x00"), ++ STR_IN_OUT_CASE( ++ "1 0 0 -1 29 763 cm\n0 0 555 735 re\nW n\nq\n0 0 555 734.394 re\n" ++ "W n\nq\n0.8009 0 0 0.8009 0 0 cm\n1 1 1 RG 1 1 1 rg\n/G0 gs\n" ++ "0 0 693 917 re\nf\nQ\nQ\n", ++ "\x78\x9c\x33\x54\x30\x00\x42\x5d\x43\x05\x23\x4b\x05\x73\x33\x63" ++ "\x85\xe4\x5c\x2e\x90\x80\xa9\xa9\xa9\x82\xb9\xb1\xa9\x42\x51\x2a" ++ "\x57\xb8\x42\x1e\x57\x21\x92\xa0\x89\x9e\xb1\xa5\x09\x92\x84\x9e" ++ "\x85\x81\x81\x25\xd8\x14\x24\x26\xd0\x18\x43\x05\x10\x0c\x72\x57" ++ "\x80\x30\x8a\xd2\xb9\xf4\xdd\x0d\x14\xd2\x8b\xc1\x46\x99\x59\x1a" ++ "\x2b\x58\x1a\x9a\x83\x8c\x49\xe3\x0a\x04\x42\x00\x37\x4c\x1b\x42"), ++ }; ++ ++ for (size_t i = 0; i < std::size(flate_encode_cases); ++i) { ++ const pdfium::StrFuncTestData& data = flate_encode_cases[i]; ++ DataVector result = FlateEncode({data.input, data.input_size}); ++ EXPECT_EQ(data.expected_size, result.size()) << " for case " << i; ++ if (data.expected_size != result.size()) ++ continue; ++ EXPECT_EQ(0, memcmp(data.expected, result.data(), data.expected_size)) ++ << " for case " << i; ++ } ++} ++ ++TEST(ParserDecodeTest, HexDecode) { + const pdfium::DecodeTestData kTestData[] = { + // Empty src string. + STR_IN_OUT_CASE("", "", 0), +@@ -182,7 +378,7 @@ TEST(fpdf_parser_decode, HexDecode) { + } + } + +-TEST(fpdf_parser_decode, DecodeText) { ++TEST(ParserDecodeTest, DecodeText) { + const struct DecodeTestData { + const char* input; + size_t input_length; +@@ -231,7 +427,7 @@ TEST(fpdf_parser_decode, DecodeText) { + } + } + +-TEST(fpdf_parser_decode, EncodeText) { ++TEST(ParserDecodeTest, EncodeText) { + const struct EncodeTestData { + const wchar_t* input; + const char* expected_output; +diff --git a/core/fpdfapi/parser/fpdf_parser_utility.cpp b/core/fpdfapi/parser/fpdf_parser_utility.cpp +index af58e8dcb..950753e3d 100644 +--- a/core/fpdfapi/parser/fpdf_parser_utility.cpp ++++ b/core/fpdfapi/parser/fpdf_parser_utility.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,10 +6,12 @@ + + #include "core/fpdfapi/parser/fpdf_parser_utility.h" + ++#include ++#include ++ + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_boolean.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" +-#include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_stream.h" +@@ -18,7 +20,8 @@ + #include "core/fpdfapi/parser/fpdf_parser_decode.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_stream.h" +-#include "third_party/base/logging.h" ++#include "third_party/base/check.h" ++#include "third_party/base/notreached.h" + + // Indexed by 8-bit character code, contains either: + // 'W' - for whitespace: NUL, TAB, CR, LF, FF, SPACE, 0x80, 0xff +@@ -72,29 +75,21 @@ const char PDF_CharType[256] = { + 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', + 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'W'}; + +-Optional GetHeaderOffset( ++absl::optional GetHeaderOffset( + const RetainPtr& pFile) { + static constexpr size_t kBufSize = 4; + uint8_t buf[kBufSize]; + for (FX_FILESIZE offset = 0; offset <= 1024; ++offset) { +- if (!pFile->ReadBlockAtOffset(buf, offset, kBufSize)) +- return {}; ++ if (!pFile->ReadBlockAtOffset(buf, offset)) ++ return absl::nullopt; + + if (memcmp(buf, "%PDF", 4) == 0) + return offset; + } +- return {}; +-} +- +-int32_t GetDirectInteger(const CPDF_Dictionary* pDict, const ByteString& key) { +- const CPDF_Number* pObj = ToNumber(pDict->GetObjectFor(key)); +- return pObj ? pObj->GetInteger() : 0; ++ return absl::nullopt; + } + + ByteString PDF_NameDecode(ByteStringView orig) { +- if (!orig.Contains('#')) +- return ByteString(orig); +- + size_t src_size = orig.GetLength(); + size_t out_index = 0; + ByteString result; +@@ -155,30 +150,29 @@ ByteString PDF_NameEncode(const ByteString& orig) { + + std::vector ReadArrayElementsToVector(const CPDF_Array* pArray, + size_t nCount) { +- ASSERT(pArray); +- ASSERT(pArray->size() >= nCount); ++ DCHECK(pArray); ++ DCHECK(pArray->size() >= nCount); + std::vector ret(nCount); + for (size_t i = 0; i < nCount; ++i) +- ret[i] = pArray->GetNumberAt(i); ++ ret[i] = pArray->GetFloatAt(i); + return ret; + } + +-bool ValidateDictType(const CPDF_Dictionary* dict, const ByteString& type) { +- ASSERT(dict); +- ASSERT(!type.IsEmpty()); +- const CPDF_Name* name_obj = ToName(dict->GetObjectFor("Type")); +- return name_obj && name_obj->GetString() == type; ++bool ValidateDictType(const CPDF_Dictionary* dict, ByteStringView type) { ++ DCHECK(!type.IsEmpty()); ++ return dict && dict->GetNameFor("Type") == type; + } + + bool ValidateDictAllResourcesOfType(const CPDF_Dictionary* dict, +- const ByteString& type) { ++ ByteStringView type) { + if (!dict) + return false; + + CPDF_DictionaryLocker locker(dict); + for (const auto& it : locker) { +- const CPDF_Dictionary* entry = ToDictionary(it.second.Get()->GetDirect()); +- if (!entry || !ValidateDictType(entry, type)) ++ RetainPtr entry = ++ ToDictionary(it.second->GetDirect()); ++ if (!ValidateDictType(entry.Get(), type)) + return false; + } + return true; +@@ -188,6 +182,12 @@ bool ValidateFontResourceDict(const CPDF_Dictionary* dict) { + return ValidateDictAllResourcesOfType(dict, "Font"); + } + ++bool ValidateDictOptionalType(const CPDF_Dictionary* dict, ++ ByteStringView type) { ++ DCHECK(!type.IsEmpty()); ++ return dict && (!dict->KeyExist("Type") || dict->GetNameFor("Type") == type); ++} ++ + std::ostream& operator<<(std::ostream& buf, const CPDF_Object* pObj) { + if (!pObj) { + buf << " null"; +@@ -202,7 +202,7 @@ std::ostream& operator<<(std::ostream& buf, const CPDF_Object* pObj) { + buf << " " << pObj->GetString(); + break; + case CPDF_Object::kString: +- buf << PDF_EncodeString(pObj->GetString(), pObj->AsString()->IsHex()); ++ buf << pObj->AsString()->EncodeString(); + break; + case CPDF_Object::kName: { + ByteString str = pObj->GetString(); +@@ -217,11 +217,11 @@ std::ostream& operator<<(std::ostream& buf, const CPDF_Object* pObj) { + const CPDF_Array* p = pObj->AsArray(); + buf << "["; + for (size_t i = 0; i < p->size(); i++) { +- const CPDF_Object* pElement = p->GetObjectAt(i); +- if (pElement && !pElement->IsInline()) { ++ RetainPtr pElement = p->GetObjectAt(i); ++ if (!pElement->IsInline()) { + buf << " " << pElement->GetObjNum() << " 0 R"; + } else { +- buf << pElement; ++ buf << pElement.Get(); + } + } + buf << "]"; +@@ -232,9 +232,9 @@ std::ostream& operator<<(std::ostream& buf, const CPDF_Object* pObj) { + buf << "<<"; + for (const auto& it : locker) { + const ByteString& key = it.first; +- CPDF_Object* pValue = it.second.Get(); ++ const RetainPtr& pValue = it.second; + buf << "/" << PDF_NameEncode(key); +- if (pValue && !pValue->IsInline()) { ++ if (!pValue->IsInline()) { + buf << " " << pValue->GetObjNum() << " 0 R "; + } else { + buf << pValue; +@@ -244,12 +244,12 @@ std::ostream& operator<<(std::ostream& buf, const CPDF_Object* pObj) { + break; + } + case CPDF_Object::kStream: { +- const CPDF_Stream* p = pObj->AsStream(); +- buf << p->GetDict() << "stream\r\n"; +- auto pAcc = pdfium::MakeRetain(p); ++ RetainPtr p(pObj->AsStream()); ++ buf << p->GetDict().Get() << "stream\r\n"; ++ auto pAcc = pdfium::MakeRetain(std::move(p)); + pAcc->LoadAllDataRaw(); +- buf.write(reinterpret_cast(pAcc->GetData()), +- pAcc->GetSize()); ++ pdfium::span span = pAcc->GetSpan(); ++ buf.write(reinterpret_cast(span.data()), span.size()); + buf << "\r\nendstream"; + break; + } +diff --git a/core/fpdfapi/parser/fpdf_parser_utility.h b/core/fpdfapi/parser/fpdf_parser_utility.h +index 75f4076af..259495e15 100644 +--- a/core/fpdfapi/parser/fpdf_parser_utility.h ++++ b/core/fpdfapi/parser/fpdf_parser_utility.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,12 @@ + #ifndef CORE_FPDFAPI_PARSER_FPDF_PARSER_UTILITY_H_ + #define CORE_FPDFAPI_PARSER_FPDF_PARSER_UTILITY_H_ + +-#include ++#include + #include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/retain_ptr.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CPDF_Array; + class CPDF_Dictionary; +@@ -42,11 +42,9 @@ inline bool PDFCharIsLineEnding(uint8_t c) { + // On success, return a positive offset value to the PDF header. If the header + // cannot be found, or if there is an error reading from |pFile|, then return + // nullopt. +-Optional GetHeaderOffset( ++absl::optional GetHeaderOffset( + const RetainPtr& pFile); + +-int32_t GetDirectInteger(const CPDF_Dictionary* pDict, const ByteString& key); +- + ByteString PDF_NameDecode(ByteStringView orig); + ByteString PDF_NameEncode(const ByteString& orig); + +@@ -55,17 +53,21 @@ ByteString PDF_NameEncode(const ByteString& orig); + std::vector ReadArrayElementsToVector(const CPDF_Array* pArray, + size_t nCount); + +-// Returns true if |dict| has a /Type name entry that matches |type|. +-bool ValidateDictType(const CPDF_Dictionary* dict, const ByteString& type); ++// Returns true if |dict| is non-null and has a /Type name entry that matches ++// |type|. ++bool ValidateDictType(const CPDF_Dictionary* dict, ByteStringView type); + + // Returns true if |dict| is non-null and all entries in |dict| are dictionaries + // of |type|. + bool ValidateDictAllResourcesOfType(const CPDF_Dictionary* dict, +- const ByteString& type); ++ ByteStringView type); + + // Shorthand for ValidateDictAllResourcesOfType(dict, "Font"). + bool ValidateFontResourceDict(const CPDF_Dictionary* dict); + ++// Like ValidateDictType(), but /Type can also not exist. ++bool ValidateDictOptionalType(const CPDF_Dictionary* dict, ByteStringView type); ++ + std::ostream& operator<<(std::ostream& buf, const CPDF_Object* pObj); + + #endif // CORE_FPDFAPI_PARSER_FPDF_PARSER_UTILITY_H_ +diff --git a/core/fpdfapi/parser/fpdf_parser_utility_unittest.cpp b/core/fpdfapi/parser/fpdf_parser_utility_unittest.cpp +index 098c3bd39..898ab6275 100644 +--- a/core/fpdfapi/parser/fpdf_parser_utility_unittest.cpp ++++ b/core/fpdfapi/parser/fpdf_parser_utility_unittest.cpp +@@ -1,20 +1,21 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/parser/fpdf_parser_utility.h" + +-#include "core/fpdfapi/page/cpdf_docpagedata.h" ++#include ++ + #include "core/fpdfapi/page/cpdf_pagemodule.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_string.h" +-#include "core/fpdfapi/render/cpdf_docrenderdata.h" ++#include "core/fpdfapi/parser/cpdf_test_document.h" + #include "testing/gtest/include/gtest/gtest.h" + +-TEST(fpdf_parser_utility, PDF_NameDecode) { ++TEST(ParserUtilityTest, NameDecode) { + EXPECT_EQ("", PDF_NameDecode("")); + EXPECT_EQ("A", PDF_NameDecode("A")); + EXPECT_EQ("#", PDF_NameDecode("#")); +@@ -23,7 +24,7 @@ TEST(fpdf_parser_utility, PDF_NameDecode) { + EXPECT_EQ("A1", PDF_NameDecode("#411")); + } + +-TEST(fpdf_parser_utility, PDF_NameEncode) { ++TEST(ParserUtilityTest, NameEncode) { + EXPECT_EQ("", PDF_NameEncode("")); + EXPECT_EQ("A", PDF_NameEncode("A")); + EXPECT_EQ("#23", PDF_NameEncode("#")); +@@ -33,7 +34,7 @@ TEST(fpdf_parser_utility, PDF_NameEncode) { + EXPECT_EQ("f#C2#A5", PDF_NameEncode("f\xc2\xa5")); + } + +-TEST(fpdf_parser_utility, ValidateDictType) { ++TEST(ParserUtilityTest, ValidateDictType) { + auto dict = pdfium::MakeRetain(); + + // No type. +@@ -51,7 +52,7 @@ TEST(fpdf_parser_utility, ValidateDictType) { + EXPECT_FALSE(ValidateDictType(dict.Get(), "bar")); + } + +-TEST(fpdf_parser_utility, ValidateDictAllResourcesOfType) { ++TEST(ParserUtilityTest, ValidateDictAllResourcesOfType) { + CPDF_PageModule::Create(); + + { +@@ -67,7 +68,7 @@ TEST(fpdf_parser_utility, ValidateDictAllResourcesOfType) { + EXPECT_FALSE(ValidateDictAllResourcesOfType(nullptr, "bar")); + + // Add two correct dictionary entries and one string entry. +- CPDF_Dictionary* new_dict = dict->SetNewFor("f1"); ++ auto new_dict = dict->SetNewFor("f1"); + new_dict->SetNewFor("Type", "foo"); + new_dict = dict->SetNewFor("f2"); + new_dict->SetNewFor("Type", "foo"); +@@ -89,14 +90,11 @@ TEST(fpdf_parser_utility, ValidateDictAllResourcesOfType) { + + { + // Indirect dictionary. +- auto doc = pdfium::MakeUnique( +- pdfium::MakeUnique(), +- pdfium::MakeUnique()); +- ++ auto doc = std::make_unique(); + auto dict = doc->New(); + + // Add a correct dictionary entry. +- CPDF_Dictionary* new_dict = doc->NewIndirect(); ++ auto new_dict = doc->NewIndirect(); + new_dict->SetNewFor("Type", "foo"); + dict->SetNewFor("f1", doc.get(), new_dict->GetObjNum()); + +@@ -106,3 +104,21 @@ TEST(fpdf_parser_utility, ValidateDictAllResourcesOfType) { + + CPDF_PageModule::Destroy(); + } ++ ++TEST(ParserUtilityTest, ValidateDictOptionalType) { ++ auto dict = pdfium::MakeRetain(); ++ ++ // No type is ok. ++ EXPECT_TRUE(ValidateDictOptionalType(dict.Get(), "foo")); ++ EXPECT_TRUE(ValidateDictOptionalType(dict.Get(), "bar")); ++ ++ // Add the wrong object type. ++ dict->SetNewFor("Type", L"foo"); ++ EXPECT_FALSE(ValidateDictOptionalType(dict.Get(), "foo")); ++ EXPECT_FALSE(ValidateDictOptionalType(dict.Get(), "bar")); ++ ++ // Add the correct object type. ++ dict->SetNewFor("Type", "foo"); ++ EXPECT_TRUE(ValidateDictOptionalType(dict.Get(), "foo")); ++ EXPECT_FALSE(ValidateDictOptionalType(dict.Get(), "bar")); ++} +diff --git a/core/fpdfapi/parser/object_tree_traversal_util.cpp b/core/fpdfapi/parser/object_tree_traversal_util.cpp +new file mode 100644 +index 000000000..e1a0d52d0 +--- /dev/null ++++ b/core/fpdfapi/parser/object_tree_traversal_util.cpp +@@ -0,0 +1,221 @@ ++// Copyright 2023 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fpdfapi/parser/object_tree_traversal_util.h" ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "core/fpdfapi/parser/cpdf_array.h" ++#include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fpdfapi/parser/cpdf_reference.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" ++ ++namespace { ++ ++class ObjectTreeTraverser { ++ public: ++ explicit ObjectTreeTraverser(const CPDF_Document* document) ++ : document_(document) { ++ const CPDF_Parser* parser = document_->GetParser(); ++ const CPDF_Dictionary* trailer = parser ? parser->GetTrailer() : nullptr; ++ const CPDF_Dictionary* root = trailer ? trailer : document_->GetRoot(); ++ const uint32_t root_object_number = ++ trailer ? parser->GetTrailerObjectNumber() : root->GetObjNum(); ++ // If `root` is a trailer, then it may not have an object number, as many ++ // trailers are inlined. ++ if (root_object_number) { ++ referenced_objects_[root_object_number] = 1; ++ object_number_map_[root] = root_object_number; ++ } ++ ++ object_queue_.push(pdfium::WrapRetain(root)); ++ seen_objects_.insert(root); ++ } ++ ~ObjectTreeTraverser() = default; ++ ++ void Traverse() { CalculateReferenceCounts(GetReferenceEntries()); } ++ ++ const std::map& referenced_objects() { ++ return referenced_objects_; ++ } ++ ++ private: ++ struct ReferenceEntry { ++ uint32_t ref_object_number; ++ uint32_t referenced_object_number; ++ }; ++ ++ std::vector GetReferenceEntries() { ++ std::vector reference_entries; ++ while (!object_queue_.empty()) { ++ RetainPtr current_object = object_queue_.front(); ++ object_queue_.pop(); ++ ++ switch (current_object->GetType()) { ++ case CPDF_Object::kArray: { ++ CPDF_ArrayLocker locker(current_object->AsArray()); ++ for (const auto& it : locker) { ++ PushNewObject(current_object, it); ++ } ++ break; ++ } ++ case CPDF_Object::kDictionary: { ++ CPDF_DictionaryLocker locker(current_object->AsDictionary()); ++ for (const auto& it : locker) { ++ PushNewObject(current_object, it.second); ++ } ++ break; ++ } ++ case CPDF_Object::kReference: { ++ const CPDF_Reference* ref_object = current_object->AsReference(); ++ const uint32_t ref_object_number = GetObjectNumber(ref_object); ++ const uint32_t referenced_object_number = ref_object->GetRefObjNum(); ++ CHECK(referenced_object_number); ++ ++ RetainPtr referenced_object; ++ if (ref_object->HasIndirectObjectHolder()) { ++ // Calling GetIndirectObject() does not work for normal references. ++ referenced_object = ref_object->GetDirect(); ++ } else { ++ // Calling GetDirect() does not work for references from trailers. ++ referenced_object = ++ document_->GetIndirectObject(referenced_object_number); ++ } ++ // Unlike the other object types, CPDF_Reference can point at nullptr. ++ if (referenced_object) { ++ reference_entries.push_back( ++ {ref_object_number, referenced_object_number}); ++ PushNewObject(ref_object, referenced_object); ++ } ++ break; ++ } ++ case CPDF_Object::kStream: { ++ RetainPtr dict = ++ current_object->AsStream()->GetDict(); ++ CHECK(dict->IsInline()); // i.e. No object number. ++ CPDF_DictionaryLocker locker(dict); ++ for (const auto& it : locker) { ++ PushNewObject(current_object, it.second); ++ } ++ break; ++ } ++ default: { ++ break; ++ } ++ } ++ } ++ return reference_entries; ++ } ++ ++ void CalculateReferenceCounts( ++ const std::vector& reference_entries) { ++ // Tracks PDF objects that referenced other PDF objects, identified by their ++ // object numbers. Never 0. ++ std::set seen_ref_objects; ++ ++ for (const ReferenceEntry& entry : reference_entries) { ++ // Make sure this is not a self-reference. ++ if (entry.referenced_object_number == entry.ref_object_number) { ++ continue; ++ } ++ ++ // Make sure this is not a circular reference. ++ if (pdfium::Contains(seen_ref_objects, entry.ref_object_number) && ++ pdfium::Contains(seen_ref_objects, entry.referenced_object_number)) { ++ continue; ++ } ++ ++ ++referenced_objects_[entry.referenced_object_number]; ++ if (entry.ref_object_number) { ++ seen_ref_objects.insert(entry.ref_object_number); ++ } ++ } ++ } ++ ++ void PushNewObject(const CPDF_Object* parent_object, ++ RetainPtr child_object) { ++ CHECK(parent_object); ++ CHECK(child_object); ++ const bool inserted = seen_objects_.insert(child_object).second; ++ if (!inserted) { ++ return; ++ } ++ const uint32_t child_object_number = child_object->GetObjNum(); ++ if (child_object_number) { ++ object_number_map_[child_object] = child_object_number; ++ } else { ++ // This search can fail for inlined trailers. ++ auto it = object_number_map_.find(parent_object); ++ if (it != object_number_map_.end()) { ++ object_number_map_[child_object] = it->second; ++ } ++ } ++ object_queue_.push(std::move(child_object)); ++ } ++ ++ // Returns 0 if not found. ++ uint32_t GetObjectNumber(const CPDF_Object* object) const { ++ auto it = object_number_map_.find(object); ++ return it != object_number_map_.end() ? it->second : 0; ++ } ++ ++ const CPDF_Document* const document_; ++ ++ // Queue of objects to traverse. ++ // - Pointers in the queue are non-null. ++ // - The same pointer never enters the queue twice. ++ std::queue> object_queue_; ++ ++ // Map of objects to "top-level" object numbers. For inline objects, this is ++ // the ancestor object with an object number. The keys are non-null and the ++ // values are never 0. ++ // This is used to prevent self-references, as a single PDF object, with ++ // inlined objects, is represented by multiple CPDF_Objects. ++ std::map object_number_map_; ++ ++ // Tracks traversed objects to prevent duplicates from getting into ++ // `object_queue_` and `object_number_map_`. ++ std::set seen_objects_; ++ ++ // Tracks which PDF objects are referenced. ++ // Key: object number ++ // Value: number of times referenced ++ std::map referenced_objects_; ++}; ++ ++} // namespace ++ ++std::set GetObjectsWithReferences(const CPDF_Document* document) { ++ ObjectTreeTraverser traverser(document); ++ traverser.Traverse(); ++ ++ std::set results; ++ for (const auto& it : traverser.referenced_objects()) { ++ results.insert(it.first); ++ } ++ return results; ++} ++ ++std::set GetObjectsWithMultipleReferences( ++ const CPDF_Document* document) { ++ ObjectTreeTraverser traverser(document); ++ traverser.Traverse(); ++ ++ std::set results; ++ for (const auto& it : traverser.referenced_objects()) { ++ if (it.second > 1) { ++ results.insert(it.first); ++ } ++ } ++ return results; ++} +diff --git a/core/fpdfapi/parser/object_tree_traversal_util.h b/core/fpdfapi/parser/object_tree_traversal_util.h +new file mode 100644 +index 000000000..e9db96dce +--- /dev/null ++++ b/core/fpdfapi/parser/object_tree_traversal_util.h +@@ -0,0 +1,46 @@ ++// Copyright 2023 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FPDFAPI_PARSER_OBJECT_TREE_TRAVERSAL_UTIL_H_ ++#define CORE_FPDFAPI_PARSER_OBJECT_TREE_TRAVERSAL_UTIL_H_ ++ ++#include ++ ++#include ++ ++class CPDF_Document; ++ ++// Traverses `document` starting with its trailer, if it has one, or starting at ++// the catalog, which always exists. The trailer should have a reference to the ++// catalog. The traversal avoids cycles. ++// Returns all the PDF objects (not CPDF_Objects) the traversal reached as a set ++// of object numbers. ++std::set GetObjectsWithReferences(const CPDF_Document* document); ++ ++// Same as GetObjectsWithReferences(), but only returns the objects with ++// multiple references. References that would create a cycle are ignored. ++// ++// In this example, where (A) is the root node: ++// ++// A -> B ++// A -> C ++// B -> D ++// C -> D ++// ++// GetObjectsWithMultipleReferences() returns {D}, since both (B) and (C) ++// references to (D), and there are no cycles. ++// ++// In this example, where (A) is the root node: ++// ++// A -> B ++// B -> C ++// C -> B ++// ++// GetObjectsWithMultipleReferences() returns {}, even though both (A) and (C) ++// references (B). Since (B) -> (C) -> (B) creates a cycle, the (C) -> (B) ++// reference does not count. ++std::set GetObjectsWithMultipleReferences( ++ const CPDF_Document* document); ++ ++#endif // CORE_FPDFAPI_PARSER_OBJECT_TREE_TRAVERSAL_UTIL_H_ +diff --git a/core/fpdfapi/parser/object_tree_traversal_util_embeddertest.cpp b/core/fpdfapi/parser/object_tree_traversal_util_embeddertest.cpp +new file mode 100644 +index 000000000..c90c77e41 +--- /dev/null ++++ b/core/fpdfapi/parser/object_tree_traversal_util_embeddertest.cpp +@@ -0,0 +1,96 @@ ++// Copyright 2023 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fpdfapi/parser/object_tree_traversal_util.h" ++ ++#include ++ ++#include ++ ++#include "core/fpdfapi/parser/cpdf_document.h" ++#include "testing/embedder_test.h" ++#include "testing/gmock/include/gmock/gmock.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++using testing::UnorderedElementsAreArray; ++using ObjectTreeTraversalUtilEmbedderTest = EmbedderTest; ++ ++namespace { ++ ++CPDF_Document* GetCPDFDocument(FPDF_DOCUMENT document) { ++ // This is cheating slightly to avoid a layering violation, since this file ++ // cannot include fpdfsdk/cpdfsdk_helpers.h to get access to ++ // CPDFDocumentFromFPDFDocument(). ++ return reinterpret_cast((document)); ++} ++ ++} // namespace ++ ++TEST_F(ObjectTreeTraversalUtilEmbedderTest, GetObjectsWithReferencesBasic) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ CPDF_Document* doc = GetCPDFDocument(document()); ++ std::set referenced_objects = GetObjectsWithReferences(doc); ++ EXPECT_THAT(referenced_objects, ++ UnorderedElementsAreArray({1, 2, 3, 4, 5, 6})); ++} ++ ++TEST_F(ObjectTreeTraversalUtilEmbedderTest, GetObjectsWithReferencesNewDoc) { ++ ScopedFPDFDocument new_doc(FPDF_CreateNewDocument()); ++ CPDF_Document* doc = GetCPDFDocument(new_doc.get()); ++ std::set referenced_objects = GetObjectsWithReferences(doc); ++ // Empty documents have a catalog and an empty pages object. ++ EXPECT_THAT(referenced_objects, UnorderedElementsAreArray({1, 2})); ++} ++ ++TEST_F(ObjectTreeTraversalUtilEmbedderTest, ++ GetObjectsWithReferencesCircularRefs) { ++ ASSERT_TRUE(OpenDocument("circular_viewer_ref.pdf")); ++ CPDF_Document* doc = GetCPDFDocument(document()); ++ std::set referenced_objects = GetObjectsWithReferences(doc); ++ // The trailer points at a catalog, and the catalog only references itself. ++ EXPECT_THAT(referenced_objects, UnorderedElementsAreArray({1})); ++} ++ ++TEST_F(ObjectTreeTraversalUtilEmbedderTest, ++ GetObjectsWithReferencesCrossRefStream) { ++ ASSERT_TRUE(OpenDocument("bug_1399.pdf")); ++ CPDF_Document* doc = GetCPDFDocument(document()); ++ std::set referenced_objects = GetObjectsWithReferences(doc); ++ // The trailer is the dictionary inside /XRef object 16 0. Note that it ++ // references object 3 0, but the rest of the document does not. ++ EXPECT_THAT(referenced_objects, ++ UnorderedElementsAreArray({1, 2, 3, 4, 5, 12, 13, 14, 16})); ++} ++ ++TEST_F(ObjectTreeTraversalUtilEmbedderTest, ++ GetObjectsWithMultipleReferencesBasic) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ CPDF_Document* doc = GetCPDFDocument(document()); ++ std::set referenced_objects = GetObjectsWithMultipleReferences(doc); ++ EXPECT_TRUE(referenced_objects.empty()); ++} ++ ++TEST_F(ObjectTreeTraversalUtilEmbedderTest, ++ GetObjectsWithMultipleReferencesNewDoc) { ++ ScopedFPDFDocument new_doc(FPDF_CreateNewDocument()); ++ CPDF_Document* doc = GetCPDFDocument(new_doc.get()); ++ std::set referenced_objects = GetObjectsWithMultipleReferences(doc); ++ EXPECT_TRUE(referenced_objects.empty()); ++} ++ ++TEST_F(ObjectTreeTraversalUtilEmbedderTest, ++ GetObjectsWithMultipleReferencesCircularRefs) { ++ ASSERT_TRUE(OpenDocument("circular_viewer_ref.pdf")); ++ CPDF_Document* doc = GetCPDFDocument(document()); ++ std::set referenced_objects = GetObjectsWithMultipleReferences(doc); ++ EXPECT_TRUE(referenced_objects.empty()); ++} ++ ++TEST_F(ObjectTreeTraversalUtilEmbedderTest, ++ GetObjectsWithMultipleReferencesSharedObjects) { ++ ASSERT_TRUE(OpenDocument("hello_world_2_pages.pdf")); ++ CPDF_Document* doc = GetCPDFDocument(document()); ++ std::set referenced_objects = GetObjectsWithMultipleReferences(doc); ++ EXPECT_THAT(referenced_objects, UnorderedElementsAreArray({5, 6, 7})); ++} +diff --git a/core/fpdfapi/render/Android.bp b/core/fpdfapi/render/Android.bp +index 79b0db617..cd2b82d7b 100644 +--- a/core/fpdfapi/render/Android.bp ++++ b/core/fpdfapi/render/Android.bp +@@ -13,11 +13,8 @@ cc_library_static { + + visibility: ["//external/pdfium:__subpackages__"], + +- header_libs: [ +- "libpdfium-constants", +- ], +- + static_libs: [ ++ "libpdfium-constants", + "libpdfium-fxcodec", + "libpdfium-fxcrt", + "libpdfium-fxge", +diff --git a/core/fpdfapi/render/BUILD.gn b/core/fpdfapi/render/BUILD.gn +index ddce77acc..bb76ef806 100644 +--- a/core/fpdfapi/render/BUILD.gn ++++ b/core/fpdfapi/render/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -7,20 +7,14 @@ import("../../../testing/test.gni") + + source_set("render") { + sources = [ +- "cpdf_charposlist.cpp", +- "cpdf_charposlist.h", ++ "charposlist.cpp", ++ "charposlist.h", + "cpdf_devicebuffer.cpp", + "cpdf_devicebuffer.h", + "cpdf_docrenderdata.cpp", + "cpdf_docrenderdata.h", +- "cpdf_imagecacheentry.cpp", +- "cpdf_imagecacheentry.h", +- "cpdf_imageloader.cpp", +- "cpdf_imageloader.h", + "cpdf_imagerenderer.cpp", + "cpdf_imagerenderer.h", +- "cpdf_pagerendercache.cpp", +- "cpdf_pagerendercache.h", + "cpdf_pagerendercontext.cpp", + "cpdf_pagerendercontext.h", + "cpdf_progressiverenderer.cpp", +@@ -33,6 +27,8 @@ source_set("render") { + "cpdf_rendershading.h", + "cpdf_renderstatus.cpp", + "cpdf_renderstatus.h", ++ "cpdf_rendertiling.cpp", ++ "cpdf_rendertiling.h", + "cpdf_scaledrenderbuffer.cpp", + "cpdf_scaledrenderbuffer.h", + "cpdf_textrenderer.cpp", +@@ -42,7 +38,10 @@ source_set("render") { + "cpdf_type3glyphmap.cpp", + "cpdf_type3glyphmap.h", + ] +- configs += [ "../../../:pdfium_core_config" ] ++ configs += [ ++ "../../../:pdfium_strict_config", ++ "../../../:pdfium_noshorten_config", ++ ] + deps = [ + "../../../constants", + "../../fxcodec", +@@ -52,13 +51,13 @@ source_set("render") { + "../page", + "../parser", + ] ++ visibility = [ "../../../*" ] + if (is_win) { + sources += [ + "cpdf_windowsrenderdevice.cpp", + "cpdf_windowsrenderdevice.h", + ] + } +- visibility = [ "../../../*" ] + } + + pdfium_unittest_source_set("unittests") { +@@ -76,5 +75,6 @@ pdfium_embeddertest_source_set("embeddertests") { + "fpdf_progressive_render_embeddertest.cpp", + "fpdf_render_pattern_embeddertest.cpp", + ] ++ deps = [ "../../fxge" ] + pdfium_root_dir = "../../../" + } +diff --git a/core/fpdfapi/render/charposlist.cpp b/core/fpdfapi/render/charposlist.cpp +new file mode 100644 +index 000000000..aba0ada75 +--- /dev/null ++++ b/core/fpdfapi/render/charposlist.cpp +@@ -0,0 +1,196 @@ ++// Copyright 2016 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fpdfapi/render/charposlist.h" ++ ++#include "build/build_config.h" ++#include "core/fpdfapi/font/cpdf_cidfont.h" ++#include "core/fpdfapi/font/cpdf_font.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" ++#include "core/fxge/cfx_fontmapper.h" ++#include "core/fxge/cfx_substfont.h" ++#include "core/fxge/text_char_pos.h" ++ ++namespace { ++ ++bool ShouldUseExistingFont(const CPDF_Font* font, ++ uint32_t glyph_id, ++ bool has_to_unicode) { ++ // Check for invalid glyph ID. ++ if (glyph_id == static_cast(-1)) ++ return false; ++ ++ if (!font->IsTrueTypeFont()) ++ return true; ++ ++ // For TrueType fonts, a glyph ID of 0 may be invalid. ++ // ++ // When a "ToUnicode" entry exists in the font dictionary, it indicates ++ // a "ToUnicode" mapping file is used to convert from CIDs (which ++ // begins at decimal 0) to Unicode code. (See ToUnicode Mapping File ++ // Tutorial - Adobe ++ // https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/5411.ToUnicode.pdf ++ // and ++ // https://www.freetype.org/freetype2/docs/tutorial/step1.html#section-6) ++ return glyph_id != 0 || has_to_unicode; ++} ++ ++// The following is not a perfect solution and can be further improved. ++// For example, if `subst_font` is "Book" and the `base_font_name` is "Bookman", ++// this function will return "true" even though the actual font "Bookman" ++// is not loaded. ++// An exact string match is not possible here, because `subst_font_name` ++// will be the same value for different postscript names. ++// For example: "Times New Roman" as `subst_font_name` for all of these ++// `base_font_name` values: "TimesNewRoman,Bold", "TimesNewRomanPS-Bold", ++// "TimesNewRomanBold" and "TimesNewRoman-Bold". ++bool IsActualFontLoaded(const CFX_SubstFont* subst_font, ++ const ByteString& base_font_name) { ++ // Skip if we loaded the actual font. ++ // example: TimesNewRoman,Bold -> Times New Roman ++ ByteString subst_font_name = subst_font->m_Family; ++ subst_font_name.Remove(' '); ++ subst_font_name.MakeLower(); ++ ++ absl::optional find = ++ base_font_name.Find(subst_font_name.AsStringView()); ++ return find.has_value() && find.value() == 0; ++} ++ ++bool ApplyGlyphSpacingHeuristic(const CPDF_Font* font, ++ const CFX_Font* current_font, ++ bool is_vertical_writing) { ++ if (is_vertical_writing || font->IsEmbedded() || !font->HasFontWidths()) { ++ return false; ++ } ++ ++ // Skip if we loaded a standard alternate font. ++ // example: Helvetica -> Arial ++ ByteString base_font_name = font->GetBaseFontName(); ++ base_font_name.MakeLower(); ++ ++ auto standard_font_name = ++ CFX_FontMapper::GetStandardFontName(&base_font_name); ++ if (standard_font_name.has_value()) { ++ return false; ++ } ++ ++ CFX_SubstFont* subst_font = current_font->GetSubstFont(); ++ if (subst_font->IsBuiltInGenericFont()) { ++ return false; ++ } ++ ++ return !IsActualFontLoaded(subst_font, base_font_name); ++} ++ ++} // namespace ++ ++std::vector GetCharPosList(pdfium::span char_codes, ++ pdfium::span char_pos, ++ CPDF_Font* font, ++ float font_size) { ++ std::vector results; ++ results.reserve(char_codes.size()); ++ ++ CPDF_CIDFont* cid_font = font->AsCIDFont(); ++ bool is_vertical_writing = cid_font && cid_font->IsVertWriting(); ++ bool has_to_unicode = !!font->GetFontDict()->GetStreamFor("ToUnicode"); ++ for (size_t i = 0; i < char_codes.size(); ++i) { ++ uint32_t char_code = char_codes[i]; ++ if (char_code == static_cast(-1)) ++ continue; ++ ++ bool is_vertical_glyph = false; ++ results.emplace_back(); ++ TextCharPos& text_char_pos = results.back(); ++ if (cid_font) ++ text_char_pos.m_bFontStyle = true; ++ WideString unicode = font->UnicodeFromCharCode(char_code); ++ text_char_pos.m_Unicode = !unicode.IsEmpty() ? unicode[0] : char_code; ++ text_char_pos.m_GlyphIndex = ++ font->GlyphFromCharCode(char_code, &is_vertical_glyph); ++ uint32_t glyph_id = text_char_pos.m_GlyphIndex; ++#if BUILDFLAG(IS_APPLE) ++ text_char_pos.m_ExtGID = font->GlyphFromCharCodeExt(char_code); ++ glyph_id = text_char_pos.m_ExtGID != static_cast(-1) ++ ? text_char_pos.m_ExtGID ++ : text_char_pos.m_GlyphIndex; ++#endif ++ CFX_Font* current_font; ++ if (ShouldUseExistingFont(font, glyph_id, has_to_unicode)) { ++ current_font = font->GetFont(); ++ text_char_pos.m_FallbackFontPosition = -1; ++ } else { ++ int32_t fallback_position = font->FallbackFontFromCharcode(char_code); ++ current_font = font->GetFontFallback(fallback_position); ++ text_char_pos.m_FallbackFontPosition = fallback_position; ++ text_char_pos.m_GlyphIndex = ++ font->FallbackGlyphFromCharcode(fallback_position, char_code); ++#if BUILDFLAG(IS_APPLE) ++ text_char_pos.m_ExtGID = text_char_pos.m_GlyphIndex; ++#endif ++ } ++ ++ if (!font->IsEmbedded() && !font->IsCIDFont()) ++ text_char_pos.m_FontCharWidth = font->GetCharWidthF(char_code); ++ else ++ text_char_pos.m_FontCharWidth = 0; ++ ++ text_char_pos.m_Origin = CFX_PointF(i > 0 ? char_pos[i - 1] : 0, 0); ++ text_char_pos.m_bGlyphAdjust = false; ++ ++ float scaling_factor = 1.0f; ++ if (ApplyGlyphSpacingHeuristic(font, current_font, is_vertical_writing)) { ++ int pdf_glyph_width = font->GetCharWidthF(char_code); ++ int font_glyph_width = ++ current_font->GetGlyphWidth(text_char_pos.m_GlyphIndex); ++ if (font_glyph_width && pdf_glyph_width > font_glyph_width + 1) { ++ // Move the initial x position by half of the excess (transformed to ++ // text space coordinates). ++ text_char_pos.m_Origin.x += ++ (pdf_glyph_width - font_glyph_width) * font_size / 2000.0f; ++ } else if (pdf_glyph_width && font_glyph_width && ++ pdf_glyph_width < font_glyph_width) { ++ scaling_factor = static_cast(pdf_glyph_width) / font_glyph_width; ++ text_char_pos.m_AdjustMatrix[0] = scaling_factor; ++ text_char_pos.m_AdjustMatrix[1] = 0.0f; ++ text_char_pos.m_AdjustMatrix[2] = 0.0f; ++ text_char_pos.m_AdjustMatrix[3] = 1.0f; ++ text_char_pos.m_bGlyphAdjust = true; ++ } ++ } ++ if (!cid_font) ++ continue; ++ ++ uint16_t cid = cid_font->CIDFromCharCode(char_code); ++ if (is_vertical_writing) { ++ text_char_pos.m_Origin = CFX_PointF(0, text_char_pos.m_Origin.x); ++ ++ CFX_Point16 vertical_origin = cid_font->GetVertOrigin(cid); ++ text_char_pos.m_Origin.x -= font_size * vertical_origin.x / 1000; ++ text_char_pos.m_Origin.y -= font_size * vertical_origin.y / 1000; ++ } ++ ++ const uint8_t* cid_transform = cid_font->GetCIDTransform(cid); ++ if (cid_transform && !is_vertical_glyph) { ++ text_char_pos.m_AdjustMatrix[0] = ++ cid_font->CIDTransformToFloat(cid_transform[0]) * scaling_factor; ++ text_char_pos.m_AdjustMatrix[1] = ++ cid_font->CIDTransformToFloat(cid_transform[1]) * scaling_factor; ++ text_char_pos.m_AdjustMatrix[2] = ++ cid_font->CIDTransformToFloat(cid_transform[2]); ++ text_char_pos.m_AdjustMatrix[3] = ++ cid_font->CIDTransformToFloat(cid_transform[3]); ++ text_char_pos.m_Origin.x += ++ cid_font->CIDTransformToFloat(cid_transform[4]) * font_size; ++ text_char_pos.m_Origin.y += ++ cid_font->CIDTransformToFloat(cid_transform[5]) * font_size; ++ text_char_pos.m_bGlyphAdjust = true; ++ } ++ } ++ ++ return results; ++} +diff --git a/core/fpdfapi/render/charposlist.h b/core/fpdfapi/render/charposlist.h +new file mode 100644 +index 000000000..af0ea6d4a +--- /dev/null ++++ b/core/fpdfapi/render/charposlist.h +@@ -0,0 +1,24 @@ ++// Copyright 2016 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FPDFAPI_RENDER_CHARPOSLIST_H_ ++#define CORE_FPDFAPI_RENDER_CHARPOSLIST_H_ ++ ++#include ++ ++#include ++ ++#include "third_party/base/span.h" ++ ++class CPDF_Font; ++class TextCharPos; ++ ++std::vector GetCharPosList(pdfium::span char_codes, ++ pdfium::span char_pos, ++ CPDF_Font* font, ++ float font_size); ++ ++#endif // CORE_FPDFAPI_RENDER_CHARPOSLIST_H_ +diff --git a/core/fpdfapi/render/cpdf_charposlist.cpp b/core/fpdfapi/render/cpdf_charposlist.cpp +deleted file mode 100644 +index b8d0cf2f7..000000000 +--- a/core/fpdfapi/render/cpdf_charposlist.cpp ++++ /dev/null +@@ -1,140 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fpdfapi/render/cpdf_charposlist.h" +- +-#include "build/build_config.h" +-#include "core/fpdfapi/font/cpdf_cidfont.h" +-#include "core/fpdfapi/font/cpdf_font.h" +-#include "core/fxge/cfx_substfont.h" +-#include "core/fxge/text_char_pos.h" +- +-CPDF_CharPosList::CPDF_CharPosList(const std::vector& charCodes, +- const std::vector& charPos, +- CPDF_Font* pFont, +- float font_size) { +- m_CharPos.reserve(charCodes.size()); +- CPDF_CIDFont* pCIDFont = pFont->AsCIDFont(); +- bool bVertWriting = pCIDFont && pCIDFont->IsVertWriting(); +- bool bToUnicode = !!pFont->GetFontDict()->GetStreamFor("ToUnicode"); +- for (size_t i = 0; i < charCodes.size(); ++i) { +- uint32_t CharCode = charCodes[i]; +- if (CharCode == static_cast(-1)) +- continue; +- +- bool bVert = false; +- m_CharPos.emplace_back(); +- TextCharPos& charpos = m_CharPos.back(); +- if (pCIDFont) +- charpos.m_bFontStyle = true; +- WideString unicode = pFont->UnicodeFromCharCode(CharCode); +- charpos.m_Unicode = !unicode.IsEmpty() ? unicode[0] : CharCode; +- charpos.m_GlyphIndex = pFont->GlyphFromCharCode(CharCode, &bVert); +- uint32_t GlyphID = charpos.m_GlyphIndex; +-#if defined(OS_MACOSX) +- charpos.m_ExtGID = pFont->GlyphFromCharCodeExt(CharCode); +- GlyphID = charpos.m_ExtGID != static_cast(-1) +- ? charpos.m_ExtGID +- : charpos.m_GlyphIndex; +-#endif +- bool bIsInvalidGlyph = GlyphID == static_cast(-1); +- bool bIsTrueTypeZeroGlyph = GlyphID == 0 && pFont->IsTrueTypeFont(); +- bool bUseFallbackFont = false; +- if (bIsInvalidGlyph || bIsTrueTypeZeroGlyph) { +- charpos.m_FallbackFontPosition = +- pFont->FallbackFontFromCharcode(CharCode); +- charpos.m_GlyphIndex = pFont->FallbackGlyphFromCharcode( +- charpos.m_FallbackFontPosition, CharCode); +- if (bIsTrueTypeZeroGlyph && +- charpos.m_GlyphIndex == static_cast(-1)) { +- // For a TrueType font character, when finding the glyph from the +- // fallback font fails, switch back to using the original font. +- +- // When keyword "ToUnicode" exists in the PDF file, it indicates +- // a "ToUnicode" mapping file is used to convert from CIDs (which +- // begins at decimal 0) to Unicode code. (See ToUnicode Mapping File +- // Tutorial - Adobe +- // https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/5411.ToUnicode.pdf +- // and +- // https://www.freetype.org/freetype2/docs/tutorial/step1.html#section-6) +- if (bToUnicode) +- charpos.m_GlyphIndex = 0; +- } else { +- bUseFallbackFont = true; +- } +- } +- CFX_Font* pCurrentFont; +- if (bUseFallbackFont) { +- pCurrentFont = pFont->GetFontFallback(charpos.m_FallbackFontPosition); +-#if defined(OS_MACOSX) +- charpos.m_ExtGID = charpos.m_GlyphIndex; +-#endif +- } else { +- pCurrentFont = pFont->GetFont(); +- charpos.m_FallbackFontPosition = -1; +- } +- +- if (!pFont->IsEmbedded() && !pFont->IsCIDFont()) +- charpos.m_FontCharWidth = pFont->GetCharWidthF(CharCode); +- else +- charpos.m_FontCharWidth = 0; +- +- charpos.m_Origin = CFX_PointF(i > 0 ? charPos[i - 1] : 0, 0); +- charpos.m_bGlyphAdjust = false; +- +- float scalingFactor = 1.0f; +- if (!pFont->IsEmbedded() && pFont->HasFontWidths() && !bVertWriting && +- !pCurrentFont->GetSubstFont()->m_bFlagMM) { +- uint32_t pdfGlyphWidth = pFont->GetCharWidthF(CharCode); +- uint32_t ftGlyphWidth = +- pCurrentFont ? pCurrentFont->GetGlyphWidth(charpos.m_GlyphIndex) : 0; +- if (ftGlyphWidth && pdfGlyphWidth > ftGlyphWidth + 1) { +- // Move the initial x position by half of the excess (transformed to +- // text space coordinates). +- charpos.m_Origin.x += +- (pdfGlyphWidth - ftGlyphWidth) * font_size / 2000.0f; +- } else if (pdfGlyphWidth && ftGlyphWidth && +- pdfGlyphWidth < ftGlyphWidth) { +- scalingFactor = static_cast(pdfGlyphWidth) / ftGlyphWidth; +- charpos.m_AdjustMatrix[0] = scalingFactor; +- charpos.m_AdjustMatrix[1] = 0.0f; +- charpos.m_AdjustMatrix[2] = 0.0f; +- charpos.m_AdjustMatrix[3] = 1.0f; +- charpos.m_bGlyphAdjust = true; +- } +- } +- if (!pCIDFont) +- continue; +- +- uint16_t CID = pCIDFont->CIDFromCharCode(CharCode); +- if (bVertWriting) { +- charpos.m_Origin = CFX_PointF(0, charpos.m_Origin.x); +- +- short vx; +- short vy; +- pCIDFont->GetVertOrigin(CID, vx, vy); +- charpos.m_Origin.x -= font_size * vx / 1000; +- charpos.m_Origin.y -= font_size * vy / 1000; +- } +- +- const uint8_t* pTransform = pCIDFont->GetCIDTransform(CID); +- if (pTransform && !bVert) { +- charpos.m_AdjustMatrix[0] = +- pCIDFont->CIDTransformToFloat(pTransform[0]) * scalingFactor; +- charpos.m_AdjustMatrix[1] = +- pCIDFont->CIDTransformToFloat(pTransform[1]) * scalingFactor; +- charpos.m_AdjustMatrix[2] = pCIDFont->CIDTransformToFloat(pTransform[2]); +- charpos.m_AdjustMatrix[3] = pCIDFont->CIDTransformToFloat(pTransform[3]); +- charpos.m_Origin.x += +- pCIDFont->CIDTransformToFloat(pTransform[4]) * font_size; +- charpos.m_Origin.y += +- pCIDFont->CIDTransformToFloat(pTransform[5]) * font_size; +- charpos.m_bGlyphAdjust = true; +- } +- } +-} +- +-CPDF_CharPosList::~CPDF_CharPosList() = default; +diff --git a/core/fpdfapi/render/cpdf_charposlist.h b/core/fpdfapi/render/cpdf_charposlist.h +deleted file mode 100644 +index b06fcfc99..000000000 +--- a/core/fpdfapi/render/cpdf_charposlist.h ++++ /dev/null +@@ -1,31 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FPDFAPI_RENDER_CPDF_CHARPOSLIST_H_ +-#define CORE_FPDFAPI_RENDER_CPDF_CHARPOSLIST_H_ +- +-#include +- +-#include "core/fxcrt/fx_system.h" +- +-class CPDF_Font; +-class TextCharPos; +- +-class CPDF_CharPosList { +- public: +- CPDF_CharPosList(const std::vector& charCodes, +- const std::vector& charPos, +- CPDF_Font* pFont, +- float font_size); +- ~CPDF_CharPosList(); +- +- const std::vector& Get() const { return m_CharPos; } +- +- private: +- std::vector m_CharPos; +-}; +- +-#endif // CORE_FPDFAPI_RENDER_CPDF_CHARPOSLIST_H_ +diff --git a/core/fpdfapi/render/cpdf_devicebuffer.cpp b/core/fpdfapi/render/cpdf_devicebuffer.cpp +index 3426f0109..b7722f184 100644 +--- a/core/fpdfapi/render/cpdf_devicebuffer.cpp ++++ b/core/fpdfapi/render/cpdf_devicebuffer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,16 +8,17 @@ + + #include "build/build_config.h" + #include "core/fpdfapi/page/cpdf_pageobject.h" ++#include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/render/cpdf_rendercontext.h" + #include "core/fpdfapi/render/cpdf_renderoptions.h" + #include "core/fxge/cfx_defaultrenderdevice.h" + #include "core/fxge/cfx_renderdevice.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" + + namespace { + +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + constexpr bool kScaleDeviceBuffer = false; + #else + constexpr bool kScaleDeviceBuffer = true; +@@ -67,7 +68,7 @@ bool CPDF_DeviceBuffer::Initialize() { + FX_RECT bitmap_rect = + m_Matrix.TransformRect(CFX_FloatRect(m_Rect)).GetOuterRect(); + return m_pBitmap->Create(bitmap_rect.Width(), bitmap_rect.Height(), +- FXDIB_Argb); ++ FXDIB_Format::kArgb); + } + + void CPDF_DeviceBuffer::OutputToDevice() { +@@ -83,7 +84,7 @@ void CPDF_DeviceBuffer::OutputToDevice() { + auto pBuffer = pdfium::MakeRetain(); + m_pDevice->CreateCompatibleBitmap(pBuffer, m_pBitmap->GetWidth(), + m_pBitmap->GetHeight()); +- m_pContext->GetBackground(pBuffer, m_pObject.Get(), nullptr, m_Matrix); ++ m_pContext->GetBackground(pBuffer, m_pObject, nullptr, m_Matrix); + pBuffer->CompositeBitmap(0, 0, pBuffer->GetWidth(), pBuffer->GetHeight(), + m_pBitmap, 0, 0, BlendMode::kNormal, nullptr, false); + m_pDevice->StretchDIBits(pBuffer, m_Rect.left, m_Rect.top, m_Rect.Width(), +diff --git a/core/fpdfapi/render/cpdf_devicebuffer.h b/core/fpdfapi/render/cpdf_devicebuffer.h +index 1f16dbe87..108446596 100644 +--- a/core/fpdfapi/render/cpdf_devicebuffer.h ++++ b/core/fpdfapi/render/cpdf_devicebuffer.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fpdfapi/render/cpdf_docrenderdata.cpp b/core/fpdfapi/render/cpdf_docrenderdata.cpp +index ba702b7e7..e63d54d31 100644 +--- a/core/fpdfapi/render/cpdf_docrenderdata.cpp ++++ b/core/fpdfapi/render/cpdf_docrenderdata.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,10 +6,12 @@ + + #include "core/fpdfapi/render/cpdf_docrenderdata.h" + ++#include ++ ++#include + #include + #include + #include +-#include + + #include "core/fpdfapi/font/cpdf_type3font.h" + #include "core/fpdfapi/page/cpdf_dib.h" +@@ -18,6 +20,11 @@ + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/render/cpdf_type3cache.h" ++#include "core/fxcrt/fixed_uninit_data_vector.h" ++ ++#if BUILDFLAG(IS_WIN) ++#include "core/fxge/win32/cfx_psfonttracker.h" ++#endif + + namespace { + +@@ -47,7 +54,7 @@ RetainPtr CPDF_DocRenderData::GetCachedType3( + } + + RetainPtr CPDF_DocRenderData::GetTransferFunc( +- const CPDF_Object* pObj) { ++ RetainPtr pObj) { + if (!pObj) + return nullptr; + +@@ -60,8 +67,16 @@ RetainPtr CPDF_DocRenderData::GetTransferFunc( + return pFunc; + } + ++#if BUILDFLAG(IS_WIN) ++CFX_PSFontTracker* CPDF_DocRenderData::GetPSFontTracker() { ++ if (!m_PSFontTracker) ++ m_PSFontTracker = std::make_unique(); ++ return m_PSFontTracker.get(); ++} ++#endif ++ + RetainPtr CPDF_DocRenderData::CreateTransferFunc( +- const CPDF_Object* pObj) const { ++ RetainPtr pObj) const { + std::unique_ptr pFuncs[3]; + const CPDF_Array* pArray = pObj->AsArray(); + if (pArray) { +@@ -79,42 +94,48 @@ RetainPtr CPDF_DocRenderData::CreateTransferFunc( + return nullptr; + } + +- int noutput; + float output[kMaxOutputs]; +- memset(output, 0, sizeof(output)); ++ std::fill(std::begin(output), std::end(output), 0.0f); + + bool bIdentity = true; +- std::vector samples_r(CPDF_TransferFunc::kChannelSampleSize); +- std::vector samples_g(CPDF_TransferFunc::kChannelSampleSize); +- std::vector samples_b(CPDF_TransferFunc::kChannelSampleSize); +- std::array, 3> samples = {samples_r, samples_g, +- samples_b}; +- for (size_t v = 0; v < CPDF_TransferFunc::kChannelSampleSize; ++v) { +- float input = static_cast(v) / 255.0f; +- if (pArray) { ++ FixedUninitDataVector samples_r( ++ CPDF_TransferFunc::kChannelSampleSize); ++ FixedUninitDataVector samples_g( ++ CPDF_TransferFunc::kChannelSampleSize); ++ FixedUninitDataVector samples_b( ++ CPDF_TransferFunc::kChannelSampleSize); ++ std::array, 3> samples = {samples_r.writable_span(), ++ samples_g.writable_span(), ++ samples_b.writable_span()}; ++ if (pArray) { ++ for (size_t v = 0; v < CPDF_TransferFunc::kChannelSampleSize; ++v) { ++ float input = static_cast(v) / 255.0f; + for (int i = 0; i < 3; ++i) { + if (pFuncs[i]->CountOutputs() > kMaxOutputs) { + samples[i][v] = v; + continue; + } +- pFuncs[i]->Call(&input, 1, output, &noutput); ++ pFuncs[i]->Call(pdfium::make_span(&input, 1), output); + size_t o = FXSYS_roundf(output[0] * 255); + if (o != v) + bIdentity = false; + samples[i][v] = o; + } +- continue; + } +- if (pFuncs[0]->CountOutputs() <= kMaxOutputs) +- pFuncs[0]->Call(&input, 1, output, &noutput); +- size_t o = FXSYS_roundf(output[0] * 255); +- if (o != v) +- bIdentity = false; +- for (auto& channel : samples) +- channel[v] = o; ++ } else { ++ for (size_t v = 0; v < CPDF_TransferFunc::kChannelSampleSize; ++v) { ++ float input = static_cast(v) / 255.0f; ++ if (pFuncs[0]->CountOutputs() <= kMaxOutputs) ++ pFuncs[0]->Call(pdfium::make_span(&input, 1), output); ++ size_t o = FXSYS_roundf(output[0] * 255); ++ if (o != v) ++ bIdentity = false; ++ for (auto& channel : samples) ++ channel[v] = o; ++ } + } + +- return pdfium::MakeRetain( +- GetDocument(), bIdentity, std::move(samples_r), std::move(samples_g), +- std::move(samples_b)); ++ return pdfium::MakeRetain(bIdentity, std::move(samples_r), ++ std::move(samples_g), ++ std::move(samples_b)); + } +diff --git a/core/fpdfapi/render/cpdf_docrenderdata.h b/core/fpdfapi/render/cpdf_docrenderdata.h +index 32b90a38c..12089316f 100644 +--- a/core/fpdfapi/render/cpdf_docrenderdata.h ++++ b/core/fpdfapi/render/cpdf_docrenderdata.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,18 +7,28 @@ + #ifndef CORE_FPDFAPI_RENDER_CPDF_DOCRENDERDATA_H_ + #define CORE_FPDFAPI_RENDER_CPDF_DOCRENDERDATA_H_ + ++#include + #include + ++#include "build/build_config.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fxcrt/observed_ptr.h" + #include "core/fxcrt/retain_ptr.h" + ++#if BUILDFLAG(IS_WIN) ++#include ++#endif ++ + class CPDF_Font; + class CPDF_Object; + class CPDF_TransferFunc; + class CPDF_Type3Cache; + class CPDF_Type3Font; + ++#if BUILDFLAG(IS_WIN) ++class CFX_PSFontTracker; ++#endif ++ + class CPDF_DocRenderData : public CPDF_Document::RenderDataIface { + public: + static CPDF_DocRenderData* FromDocument(const CPDF_Document* pDoc); +@@ -30,17 +40,29 @@ class CPDF_DocRenderData : public CPDF_Document::RenderDataIface { + CPDF_DocRenderData& operator=(const CPDF_DocRenderData&) = delete; + + RetainPtr GetCachedType3(CPDF_Type3Font* pFont); +- RetainPtr GetTransferFunc(const CPDF_Object* pObj); ++ RetainPtr GetTransferFunc( ++ RetainPtr pObj); ++ ++#if BUILDFLAG(IS_WIN) ++ CFX_PSFontTracker* GetPSFontTracker(); ++#endif + + protected: + // protected for use by test subclasses. + RetainPtr CreateTransferFunc( +- const CPDF_Object* pObj) const; ++ RetainPtr pObj) const; + + private: ++ // TODO(tsepez): investigate this map outliving its font keys. + std::map> m_Type3FaceMap; +- std::map> ++ std::map, ++ ObservedPtr, ++ std::less<>> + m_TransferFuncMap; ++ ++#if BUILDFLAG(IS_WIN) ++ std::unique_ptr m_PSFontTracker; ++#endif + }; + + #endif // CORE_FPDFAPI_RENDER_CPDF_DOCRENDERDATA_H_ +diff --git a/core/fpdfapi/render/cpdf_docrenderdata_unittest.cpp b/core/fpdfapi/render/cpdf_docrenderdata_unittest.cpp +index 2fb25b6c9..df7bb1cc4 100644 +--- a/core/fpdfapi/render/cpdf_docrenderdata_unittest.cpp ++++ b/core/fpdfapi/render/cpdf_docrenderdata_unittest.cpp +@@ -1,9 +1,10 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfapi/render/cpdf_docrenderdata.h" + ++#include + #include + #include + +@@ -12,9 +13,8 @@ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fpdfapi/parser/cpdf_stream.h" +-#include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/data_vector.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + + namespace { + +@@ -73,26 +73,23 @@ constexpr uint8_t kExpectedType4FunctionSamples[] = { + RetainPtr CreateType0FunctionStream() { + auto func_dict = pdfium::MakeRetain(); + func_dict->SetNewFor("FunctionType", 0); ++ func_dict->SetNewFor("BitsPerSample", 8); + +- CPDF_Array* domain_array = func_dict->SetNewFor("Domain"); +- domain_array->AddNew(0); +- domain_array->AddNew(1); +- +- CPDF_Array* range_array = func_dict->SetNewFor("Range"); +- range_array->AddNew(0); +- range_array->AddNew(0.5f); ++ auto domain_array = func_dict->SetNewFor("Domain"); ++ domain_array->AppendNew(0); ++ domain_array->AppendNew(1); + +- CPDF_Array* size_array = func_dict->SetNewFor("Size"); +- size_array->AddNew(4); ++ auto range_array = func_dict->SetNewFor("Range"); ++ range_array->AppendNew(0); ++ range_array->AppendNew(0.5f); + +- func_dict->SetNewFor("BitsPerSample", 8); ++ auto size_array = func_dict->SetNewFor("Size"); ++ size_array->AppendNew(4); + +- static const char content[] = "1234"; +- size_t len = FX_ArraySize(content); +- std::unique_ptr buf(FX_Alloc(uint8_t, len)); +- memcpy(buf.get(), content, len); +- return pdfium::MakeRetain(std::move(buf), len, +- std::move(func_dict)); ++ static constexpr uint8_t kContents[] = "1234"; ++ return pdfium::MakeRetain( ++ DataVector(std::begin(kContents), std::end(kContents)), ++ std::move(func_dict)); + } + + RetainPtr CreateType2FunctionDict() { +@@ -100,19 +97,19 @@ RetainPtr CreateType2FunctionDict() { + func_dict->SetNewFor("FunctionType", 2); + func_dict->SetNewFor("N", 1); + +- CPDF_Array* domain_array = func_dict->SetNewFor("Domain"); +- domain_array->AddNew(0); +- domain_array->AddNew(1); ++ auto domain_array = func_dict->SetNewFor("Domain"); ++ domain_array->AppendNew(0); ++ domain_array->AppendNew(1); + +- CPDF_Array* c0_array = func_dict->SetNewFor("C0"); +- c0_array->AddNew(0.1f); +- c0_array->AddNew(0.2f); +- c0_array->AddNew(0.8f); ++ auto c0_array = func_dict->SetNewFor("C0"); ++ c0_array->AppendNew(0.1f); ++ c0_array->AppendNew(0.2f); ++ c0_array->AppendNew(0.8f); + +- CPDF_Array* c1_array = func_dict->SetNewFor("C1"); +- c1_array->AddNew(0.05f); +- c1_array->AddNew(0.01f); +- c1_array->AddNew(0.4f); ++ auto c1_array = func_dict->SetNewFor("C1"); ++ c1_array->AppendNew(0.05f); ++ c1_array->AppendNew(0.01f); ++ c1_array->AppendNew(0.4f); + + return func_dict; + } +@@ -121,49 +118,45 @@ RetainPtr CreateType4FunctionStream() { + auto func_dict = pdfium::MakeRetain(); + func_dict->SetNewFor("FunctionType", 4); + +- CPDF_Array* domain_array = func_dict->SetNewFor("Domain"); +- domain_array->AddNew(0); +- domain_array->AddNew(1); ++ auto domain_array = func_dict->SetNewFor("Domain"); ++ domain_array->AppendNew(0); ++ domain_array->AppendNew(1); + +- CPDF_Array* range_array = func_dict->SetNewFor("Range"); +- range_array->AddNew(-1); +- range_array->AddNew(1); ++ auto range_array = func_dict->SetNewFor("Range"); ++ range_array->AppendNew(-1); ++ range_array->AppendNew(1); + +- static const char content[] = "{ 360 mul sin 2 div }"; +- size_t len = FX_ArraySize(content); +- std::unique_ptr buf(FX_Alloc(uint8_t, len)); +- memcpy(buf.get(), content, len); +- return pdfium::MakeRetain(std::move(buf), len, +- std::move(func_dict)); ++ static constexpr uint8_t kContents[] = "{ 360 mul sin 2 div }"; ++ return pdfium::MakeRetain( ++ DataVector(std::begin(kContents), std::end(kContents)), ++ std::move(func_dict)); + } + + RetainPtr CreateBadType4FunctionStream() { + auto func_dict = pdfium::MakeRetain(); + func_dict->SetNewFor("FunctionType", 4); + +- CPDF_Array* domain_array = func_dict->SetNewFor("Domain"); +- domain_array->AddNew(0); +- domain_array->AddNew(1); ++ auto domain_array = func_dict->SetNewFor("Domain"); ++ domain_array->AppendNew(0); ++ domain_array->AppendNew(1); + +- CPDF_Array* range_array = func_dict->SetNewFor("Range"); +- range_array->AddNew(-1); +- range_array->AddNew(1); ++ auto range_array = func_dict->SetNewFor("Range"); ++ range_array->AppendNew(-1); ++ range_array->AppendNew(1); + +- static const char content[] = "garbage"; +- size_t len = FX_ArraySize(content); +- std::unique_ptr buf(FX_Alloc(uint8_t, len)); +- memcpy(buf.get(), content, len); +- return pdfium::MakeRetain(std::move(buf), len, +- std::move(func_dict)); ++ static constexpr uint8_t kContents[] = "garbage"; ++ return pdfium::MakeRetain( ++ DataVector(std::begin(kContents), std::end(kContents)), ++ std::move(func_dict)); + } + + class TestDocRenderData : public CPDF_DocRenderData { + public: +- TestDocRenderData() : CPDF_DocRenderData() {} ++ TestDocRenderData() = default; + + RetainPtr CreateTransferFuncForTesting( +- const CPDF_Object* pObj) const { +- return CreateTransferFunc(pObj); ++ RetainPtr pObj) const { ++ return CreateTransferFunc(std::move(pObj)); + } + }; + +@@ -171,18 +164,18 @@ TEST(CPDF_DocRenderDataTest, TransferFunctionOne) { + RetainPtr func_dict = CreateType2FunctionDict(); + + TestDocRenderData render_data; +- auto func = render_data.CreateTransferFuncForTesting(func_dict.Get()); ++ auto func = render_data.CreateTransferFuncForTesting(func_dict); + ASSERT_TRUE(func); + EXPECT_FALSE(func->GetIdentity()); + + auto r_samples = func->GetSamplesR(); + auto g_samples = func->GetSamplesG(); + auto b_samples = func->GetSamplesB(); +- ASSERT_EQ(FX_ArraySize(kExpectedType2FunctionSamples), r_samples.size()); +- ASSERT_EQ(FX_ArraySize(kExpectedType2FunctionSamples), g_samples.size()); +- ASSERT_EQ(FX_ArraySize(kExpectedType2FunctionSamples), b_samples.size()); ++ ASSERT_EQ(std::size(kExpectedType2FunctionSamples), r_samples.size()); ++ ASSERT_EQ(std::size(kExpectedType2FunctionSamples), g_samples.size()); ++ ASSERT_EQ(std::size(kExpectedType2FunctionSamples), b_samples.size()); + +- for (size_t i = 0; i < FX_ArraySize(kExpectedType2FunctionSamples); ++i) { ++ for (size_t i = 0; i < std::size(kExpectedType2FunctionSamples); ++i) { + EXPECT_EQ(kExpectedType2FunctionSamples[i], r_samples[i]); + EXPECT_EQ(kExpectedType2FunctionSamples[i], g_samples[i]); + EXPECT_EQ(kExpectedType2FunctionSamples[i], b_samples[i]); +@@ -202,23 +195,23 @@ TEST(CPDF_DocRenderDataTest, TransferFunctionOne) { + + TEST(CPDF_DocRenderDataTest, TransferFunctionArray) { + auto func_array = pdfium::MakeRetain(); +- func_array->Add(CreateType0FunctionStream()); +- func_array->Add(CreateType2FunctionDict()); +- func_array->Add(CreateType4FunctionStream()); ++ func_array->Append(CreateType0FunctionStream()); ++ func_array->Append(CreateType2FunctionDict()); ++ func_array->Append(CreateType4FunctionStream()); + + TestDocRenderData render_data; +- auto func = render_data.CreateTransferFuncForTesting(func_array.Get()); ++ auto func = render_data.CreateTransferFuncForTesting(func_array); + ASSERT_TRUE(func); + EXPECT_FALSE(func->GetIdentity()); + + auto r_samples = func->GetSamplesR(); + auto g_samples = func->GetSamplesG(); + auto b_samples = func->GetSamplesB(); +- ASSERT_EQ(FX_ArraySize(kExpectedType0FunctionSamples), r_samples.size()); +- ASSERT_EQ(FX_ArraySize(kExpectedType2FunctionSamples), g_samples.size()); +- ASSERT_EQ(FX_ArraySize(kExpectedType4FunctionSamples), b_samples.size()); ++ ASSERT_EQ(std::size(kExpectedType0FunctionSamples), r_samples.size()); ++ ASSERT_EQ(std::size(kExpectedType2FunctionSamples), g_samples.size()); ++ ASSERT_EQ(std::size(kExpectedType4FunctionSamples), b_samples.size()); + +- for (size_t i = 0; i < FX_ArraySize(kExpectedType2FunctionSamples); ++i) { ++ for (size_t i = 0; i < std::size(kExpectedType2FunctionSamples); ++i) { + EXPECT_EQ(kExpectedType0FunctionSamples[i], r_samples[i]); + EXPECT_EQ(kExpectedType2FunctionSamples[i], g_samples[i]); + EXPECT_EQ(kExpectedType4FunctionSamples[i], b_samples[i]); +@@ -241,7 +234,7 @@ TEST(CPDF_DocRenderDataTest, BadTransferFunctions) { + auto func_stream = CreateBadType4FunctionStream(); + + TestDocRenderData render_data; +- auto func = render_data.CreateTransferFuncForTesting(func_stream.Get()); ++ auto func = render_data.CreateTransferFuncForTesting(func_stream); + EXPECT_FALSE(func); + } + +@@ -249,18 +242,18 @@ TEST(CPDF_DocRenderDataTest, BadTransferFunctions) { + auto func_array = pdfium::MakeRetain(); + + TestDocRenderData render_data; +- auto func = render_data.CreateTransferFuncForTesting(func_array.Get()); ++ auto func = render_data.CreateTransferFuncForTesting(func_array); + EXPECT_FALSE(func); + } + + { + auto func_array = pdfium::MakeRetain(); +- func_array->Add(CreateType0FunctionStream()); +- func_array->Add(CreateType2FunctionDict()); +- func_array->Add(CreateBadType4FunctionStream()); ++ func_array->Append(CreateType0FunctionStream()); ++ func_array->Append(CreateType2FunctionDict()); ++ func_array->Append(CreateBadType4FunctionStream()); + + TestDocRenderData render_data; +- auto func = render_data.CreateTransferFuncForTesting(func_array.Get()); ++ auto func = render_data.CreateTransferFuncForTesting(func_array); + EXPECT_FALSE(func); + } + } +diff --git a/core/fpdfapi/render/cpdf_imagecacheentry.cpp b/core/fpdfapi/render/cpdf_imagecacheentry.cpp +deleted file mode 100644 +index 3805b2d36..000000000 +--- a/core/fpdfapi/render/cpdf_imagecacheentry.cpp ++++ /dev/null +@@ -1,124 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fpdfapi/render/cpdf_imagecacheentry.h" +- +-#include +-#include +- +-#include "core/fpdfapi/page/cpdf_dib.h" +-#include "core/fpdfapi/page/cpdf_image.h" +-#include "core/fpdfapi/page/cpdf_page.h" +-#include "core/fpdfapi/parser/cpdf_dictionary.h" +-#include "core/fpdfapi/parser/cpdf_document.h" +-#include "core/fpdfapi/parser/cpdf_stream.h" +-#include "core/fpdfapi/render/cpdf_pagerendercache.h" +-#include "core/fpdfapi/render/cpdf_rendercontext.h" +-#include "core/fpdfapi/render/cpdf_renderstatus.h" +-#include "core/fxge/dib/cfx_dibitmap.h" +- +-namespace { +- +-uint32_t GetEstimatedImageSize(const RetainPtr& pDIB) { +- if (!pDIB || !pDIB->GetBuffer()) +- return 0; +- +- int height = pDIB->GetHeight(); +- ASSERT(pdfium::base::IsValueInRangeForNumericType(height)); +- return static_cast(height) * pDIB->GetPitch() + +- pDIB->GetPaletteSize() * 4; +-} +- +-} // namespace +- +-CPDF_ImageCacheEntry::CPDF_ImageCacheEntry(CPDF_Document* pDoc, +- const RetainPtr& pImage) +- : m_pDocument(pDoc), m_pImage(pImage) {} +- +-CPDF_ImageCacheEntry::~CPDF_ImageCacheEntry() = default; +- +-void CPDF_ImageCacheEntry::Reset() { +- m_pCachedBitmap.Reset(); +- CalcSize(); +-} +- +-RetainPtr CPDF_ImageCacheEntry::DetachBitmap() { +- return std::move(m_pCurBitmap); +-} +- +-RetainPtr CPDF_ImageCacheEntry::DetachMask() { +- return std::move(m_pCurMask); +-} +- +-CPDF_DIB::LoadState CPDF_ImageCacheEntry::StartGetCachedBitmap( +- const CPDF_Dictionary* pFormResources, +- CPDF_Dictionary* pPageResources, +- bool bStdCS, +- uint32_t GroupFamily, +- bool bLoadMask, +- CPDF_RenderStatus* pRenderStatus) { +- ASSERT(pRenderStatus); +- +- if (m_pCachedBitmap) { +- m_pCurBitmap = m_pCachedBitmap; +- m_pCurMask = m_pCachedMask; +- return CPDF_DIB::LoadState::kSuccess; +- } +- +- m_pCurBitmap = pdfium::MakeRetain(); +- CPDF_DIB::LoadState ret = m_pCurBitmap.As()->StartLoadDIBBase( +- m_pDocument.Get(), m_pImage->GetStream(), true, pFormResources, +- pPageResources, bStdCS, GroupFamily, bLoadMask); +- if (ret == CPDF_DIB::LoadState::kContinue) +- return CPDF_DIB::LoadState::kContinue; +- +- if (ret == CPDF_DIB::LoadState::kSuccess) +- ContinueGetCachedBitmap(pRenderStatus); +- else +- m_pCurBitmap.Reset(); +- return CPDF_DIB::LoadState::kFail; +-} +- +-bool CPDF_ImageCacheEntry::Continue(PauseIndicatorIface* pPause, +- CPDF_RenderStatus* pRenderStatus) { +- CPDF_DIB::LoadState ret = +- m_pCurBitmap.As()->ContinueLoadDIBBase(pPause); +- if (ret == CPDF_DIB::LoadState::kContinue) +- return true; +- +- if (ret == CPDF_DIB::LoadState::kSuccess) +- ContinueGetCachedBitmap(pRenderStatus); +- else +- m_pCurBitmap.Reset(); +- return false; +-} +- +-void CPDF_ImageCacheEntry::ContinueGetCachedBitmap( +- CPDF_RenderStatus* pRenderStatus) { +- m_MatteColor = m_pCurBitmap.As()->GetMatteColor(); +- m_pCurMask = m_pCurBitmap.As()->DetachMask(); +- CPDF_RenderContext* pContext = pRenderStatus->GetContext(); +- CPDF_PageRenderCache* pPageRenderCache = pContext->GetPageCache(); +- m_dwTimeCount = pPageRenderCache->GetTimeCount(); +- if (m_pCurBitmap->GetPitch() * m_pCurBitmap->GetHeight() < kHugeImageSize) { +- m_pCachedBitmap = m_pCurBitmap->Clone(nullptr); +- m_pCurBitmap.Reset(); +- } else { +- m_pCachedBitmap = m_pCurBitmap; +- } +- if (m_pCurMask) { +- m_pCachedMask = m_pCurMask->Clone(nullptr); +- m_pCurMask.Reset(); +- } +- m_pCurBitmap = m_pCachedBitmap; +- m_pCurMask = m_pCachedMask; +- CalcSize(); +-} +- +-void CPDF_ImageCacheEntry::CalcSize() { +- m_dwCacheSize = GetEstimatedImageSize(m_pCachedBitmap) + +- GetEstimatedImageSize(m_pCachedMask); +-} +diff --git a/core/fpdfapi/render/cpdf_imagecacheentry.h b/core/fpdfapi/render/cpdf_imagecacheentry.h +deleted file mode 100644 +index c3f373e60..000000000 +--- a/core/fpdfapi/render/cpdf_imagecacheentry.h ++++ /dev/null +@@ -1,62 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FPDFAPI_RENDER_CPDF_IMAGECACHEENTRY_H_ +-#define CORE_FPDFAPI_RENDER_CPDF_IMAGECACHEENTRY_H_ +- +-#include "core/fpdfapi/page/cpdf_dib.h" +-#include "core/fxcrt/fx_system.h" +-#include "core/fxcrt/retain_ptr.h" +-#include "core/fxcrt/unowned_ptr.h" +- +-class CPDF_Dictionary; +-class CPDF_Document; +-class CPDF_Image; +-class CPDF_RenderStatus; +-class PauseIndicatorIface; +- +-class CPDF_ImageCacheEntry { +- public: +- CPDF_ImageCacheEntry(CPDF_Document* pDoc, +- const RetainPtr& pImage); +- ~CPDF_ImageCacheEntry(); +- +- void Reset(); +- uint32_t EstimateSize() const { return m_dwCacheSize; } +- uint32_t GetTimeCount() const { return m_dwTimeCount; } +- CPDF_Image* GetImage() const { return m_pImage.Get(); } +- +- CPDF_DIB::LoadState StartGetCachedBitmap( +- const CPDF_Dictionary* pFormResources, +- CPDF_Dictionary* pPageResources, +- bool bStdCS, +- uint32_t GroupFamily, +- bool bLoadMask, +- CPDF_RenderStatus* pRenderStatus); +- +- // Returns whether to Continue() or not. +- bool Continue(PauseIndicatorIface* pPause, CPDF_RenderStatus* pRenderStatus); +- +- RetainPtr DetachBitmap(); +- RetainPtr DetachMask(); +- +- int m_dwTimeCount = 0; +- uint32_t m_MatteColor = 0; +- +- private: +- void ContinueGetCachedBitmap(CPDF_RenderStatus* pRenderStatus); +- void CalcSize(); +- +- UnownedPtr const m_pDocument; +- RetainPtr const m_pImage; +- RetainPtr m_pCurBitmap; +- RetainPtr m_pCurMask; +- RetainPtr m_pCachedBitmap; +- RetainPtr m_pCachedMask; +- uint32_t m_dwCacheSize = 0; +-}; +- +-#endif // CORE_FPDFAPI_RENDER_CPDF_IMAGECACHEENTRY_H_ +diff --git a/core/fpdfapi/render/cpdf_imageloader.cpp b/core/fpdfapi/render/cpdf_imageloader.cpp +deleted file mode 100644 +index fac4799c1..000000000 +--- a/core/fpdfapi/render/cpdf_imageloader.cpp ++++ /dev/null +@@ -1,79 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fpdfapi/render/cpdf_imageloader.h" +- +-#include "core/fpdfapi/page/cpdf_dib.h" +-#include "core/fpdfapi/page/cpdf_image.h" +-#include "core/fpdfapi/page/cpdf_imageobject.h" +-#include "core/fpdfapi/page/cpdf_transferfunc.h" +-#include "core/fpdfapi/render/cpdf_imagecacheentry.h" +-#include "core/fpdfapi/render/cpdf_pagerendercache.h" +-#include "core/fpdfapi/render/cpdf_renderstatus.h" +-#include "core/fxge/dib/cfx_dibitmap.h" +- +-CPDF_ImageLoader::CPDF_ImageLoader() = default; +- +-CPDF_ImageLoader::~CPDF_ImageLoader() = default; +- +-bool CPDF_ImageLoader::Start(CPDF_ImageObject* pImage, +- CPDF_PageRenderCache* pCache, +- bool bStdCS, +- uint32_t GroupFamily, +- bool bLoadMask, +- CPDF_RenderStatus* pRenderStatus) { +- m_pCache = pCache; +- m_pImageObject = pImage; +- bool ret; +- if (pCache) { +- ret = pCache->StartGetCachedBitmap(m_pImageObject->GetImage(), bStdCS, +- GroupFamily, bLoadMask, pRenderStatus); +- } else { +- ret = m_pImageObject->GetImage()->StartLoadDIBBase( +- pRenderStatus->GetFormResource(), pRenderStatus->GetPageResource(), +- bStdCS, GroupFamily, bLoadMask); +- } +- if (!ret) +- HandleFailure(); +- return ret; +-} +- +-bool CPDF_ImageLoader::Continue(PauseIndicatorIface* pPause, +- CPDF_RenderStatus* pRenderStatus) { +- bool ret = m_pCache ? m_pCache->Continue(pPause, pRenderStatus) +- : m_pImageObject->GetImage()->Continue(pPause); +- if (!ret) +- HandleFailure(); +- return ret; +-} +- +-RetainPtr CPDF_ImageLoader::TranslateImage( +- const RetainPtr& pTransferFunc) { +- ASSERT(pTransferFunc); +- ASSERT(!pTransferFunc->GetIdentity()); +- +- m_pBitmap = pTransferFunc->TranslateImage(m_pBitmap); +- if (m_bCached && m_pMask) +- m_pMask = m_pMask->Clone(nullptr); +- m_bCached = false; +- return m_pBitmap; +-} +- +-void CPDF_ImageLoader::HandleFailure() { +- if (m_pCache) { +- CPDF_ImageCacheEntry* entry = m_pCache->GetCurImageCacheEntry(); +- m_bCached = true; +- m_pBitmap = entry->DetachBitmap(); +- m_pMask = entry->DetachMask(); +- m_MatteColor = entry->m_MatteColor; +- return; +- } +- RetainPtr pImage = m_pImageObject->GetImage(); +- m_bCached = false; +- m_pBitmap = pImage->DetachBitmap(); +- m_pMask = pImage->DetachMask(); +- m_MatteColor = pImage->m_MatteColor; +-} +diff --git a/core/fpdfapi/render/cpdf_imagerenderer.cpp b/core/fpdfapi/render/cpdf_imagerenderer.cpp +index 2d3ec4c4b..c5f3f1d46 100644 +--- a/core/fpdfapi/render/cpdf_imagerenderer.cpp ++++ b/core/fpdfapi/render/cpdf_imagerenderer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,38 +6,43 @@ + + #include "core/fpdfapi/render/cpdf_imagerenderer.h" + ++#include ++ + #include + #include ++#include + + #include "core/fpdfapi/page/cpdf_dib.h" + #include "core/fpdfapi/page/cpdf_docpagedata.h" + #include "core/fpdfapi/page/cpdf_image.h" ++#include "core/fpdfapi/page/cpdf_imageloader.h" + #include "core/fpdfapi/page/cpdf_imageobject.h" + #include "core/fpdfapi/page/cpdf_occontext.h" + #include "core/fpdfapi/page/cpdf_page.h" ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" + #include "core/fpdfapi/page/cpdf_pageobject.h" + #include "core/fpdfapi/page/cpdf_shadingpattern.h" + #include "core/fpdfapi/page/cpdf_tilingpattern.h" + #include "core/fpdfapi/page/cpdf_transferfunc.h" +-#include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_stream.h" +-#include "core/fpdfapi/render/cpdf_pagerendercache.h" ++#include "core/fpdfapi/parser/fpdf_parser_decode.h" + #include "core/fpdfapi/render/cpdf_rendercontext.h" + #include "core/fpdfapi/render/cpdf_renderstatus.h" + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxcrt/maybe_owned.h" + #include "core/fxge/cfx_defaultrenderdevice.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_fillrenderoptions.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/dib/cfx_dibbase.h" + #include "core/fxge/dib/cfx_dibitmap.h" + #include "core/fxge/dib/cfx_imagestretcher.h" + #include "core/fxge/dib/cfx_imagetransformer.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/cxx17_backports.h" + +-#ifdef _SKIA_SUPPORT_ ++#if defined(_SKIA_SUPPORT_) + #include "core/fxge/skia/fx_skia_device.h" + #endif + +@@ -52,9 +57,34 @@ bool IsImageValueTooBig(int val) { + return safe_val.ValueOrDefault(kLimit) >= kLimit; + } + ++void ClearBitmap(CFX_DefaultRenderDevice& bitmap_device, uint32_t color) { ++#if defined(_SKIA_SUPPORT_) ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ bitmap_device.Clear(color); ++ return; ++ } ++#endif ++ bitmap_device.GetBitmap()->Clear(color); ++} ++ ++RetainPtr PreMultiplyBitmapIfAlpha( ++ RetainPtr base_bitmap) { ++#if defined(_SKIA_SUPPORT_) ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ RetainPtr premultiplied = base_bitmap->Realize(); ++ if (base_bitmap->IsAlphaFormat()) ++ premultiplied->PreMultiply(); ++ return premultiplied; ++ } ++#endif // defined(_SKIA_SUPPORT_) ++ return base_bitmap; ++} ++ + } // namespace + +-CPDF_ImageRenderer::CPDF_ImageRenderer() = default; ++CPDF_ImageRenderer::CPDF_ImageRenderer(CPDF_RenderStatus* pStatus) ++ : m_pRenderStatus(pStatus), ++ m_pLoader(std::make_unique()) {} + + CPDF_ImageRenderer::~CPDF_ImageRenderer() = default; + +@@ -62,47 +92,51 @@ bool CPDF_ImageRenderer::StartLoadDIBBase() { + if (!GetUnitRect().has_value()) + return false; + +- if (m_Loader.Start(m_pImageObject.Get(), +- m_pRenderStatus->GetContext()->GetPageCache(), m_bStdCS, +- m_pRenderStatus->GetGroupFamily(), +- m_pRenderStatus->GetLoadMask(), m_pRenderStatus.Get())) { +- m_Mode = Mode::kDefault; +- return true; ++ if (!m_pLoader->Start( ++ m_pImageObject, m_pRenderStatus->GetContext()->GetPageCache(), ++ m_pRenderStatus->GetFormResource(), ++ m_pRenderStatus->GetPageResource(), m_bStdCS, ++ m_pRenderStatus->GetGroupFamily(), m_pRenderStatus->GetLoadMask(), ++ {m_pRenderStatus->GetRenderDevice()->GetWidth(), ++ m_pRenderStatus->GetRenderDevice()->GetHeight()})) { ++ return false; + } +- return false; ++ m_Mode = Mode::kDefault; ++ return true; + } + + bool CPDF_ImageRenderer::StartRenderDIBBase() { +- if (!m_Loader.GetBitmap()) ++ if (!m_pLoader->GetBitmap()) + return false; + + CPDF_GeneralState& state = m_pImageObject->m_GeneralState; + m_BitmapAlpha = FXSYS_roundf(255 * state.GetFillAlpha()); +- m_pDIBBase = m_Loader.GetBitmap(); ++ m_pDIBBase = m_pLoader->GetBitmap(); + if (GetRenderOptions().ColorModeIs(CPDF_RenderOptions::kAlpha) && +- !m_Loader.GetMask()) { ++ !m_pLoader->GetMask()) { + return StartBitmapAlpha(); + } +- if (state.GetTR()) { ++ RetainPtr pTR = state.GetTR(); ++ if (pTR) { + if (!state.GetTransferFunc()) +- state.SetTransferFunc(m_pRenderStatus->GetTransferFunc(state.GetTR())); ++ state.SetTransferFunc(m_pRenderStatus->GetTransferFunc(std::move(pTR))); + + if (state.GetTransferFunc() && !state.GetTransferFunc()->GetIdentity()) +- m_pDIBBase = m_Loader.TranslateImage(state.GetTransferFunc()); ++ m_pDIBBase = m_pLoader->TranslateImage(state.GetTransferFunc()); + } + m_FillArgb = 0; + m_bPatternColor = false; + m_pPattern = nullptr; +- if (m_pDIBBase->IsAlphaMask()) { ++ if (m_pDIBBase->IsMaskFormat()) { + const CPDF_Color* pColor = m_pImageObject->m_ColorState.GetFillColor(); + if (pColor && pColor->IsPattern()) { +- m_pPattern.Reset(pColor->GetPattern()); ++ m_pPattern = pColor->GetPattern(); + if (m_pPattern) + m_bPatternColor = true; + } +- m_FillArgb = m_pRenderStatus->GetFillArgb(m_pImageObject.Get()); ++ m_FillArgb = m_pRenderStatus->GetFillArgb(m_pImageObject); + } else if (GetRenderOptions().ColorModeIs(CPDF_RenderOptions::kGray)) { +- RetainPtr pClone = m_pDIBBase->Clone(nullptr); ++ RetainPtr pClone = m_pDIBBase->Realize(); + if (!pClone) + return false; + +@@ -123,7 +157,7 @@ bool CPDF_ImageRenderer::StartRenderDIBBase() { + else if (m_pImageObject->GetImage()->IsInterpol()) + m_ResampleOptions.bInterpolateBilinear = true; + +- if (m_Loader.GetMask()) ++ if (m_pLoader->GetMask()) + return DrawMaskedImage(); + + if (m_bPatternColor) +@@ -142,62 +176,58 @@ bool CPDF_ImageRenderer::StartRenderDIBBase() { + } else { + pDocument = m_pImageObject->GetImage()->GetDocument(); + } +- CPDF_Dictionary* pPageResources = +- pPage ? pPage->m_pPageResources.Get() : nullptr; +- CPDF_Object* pCSObj = +- m_pImageObject->GetImage()->GetStream()->GetDict()->GetDirectObjectFor( +- "ColorSpace"); ++ RetainPtr pPageResources = ++ pPage ? pPage->GetPageResources() : nullptr; ++ RetainPtr pStreamDict = ++ m_pImageObject->GetImage()->GetStream()->GetDict(); ++ RetainPtr pCSObj = ++ pStreamDict->GetDirectObjectFor("ColorSpace"); + auto* pData = CPDF_DocPageData::FromDocument(pDocument); + RetainPtr pColorSpace = +- pData->GetColorSpace(pCSObj, pPageResources); ++ pData->GetColorSpace(pCSObj.Get(), pPageResources); + if (pColorSpace) { +- int format = pColorSpace->GetFamily(); +- if (format == PDFCS_DEVICECMYK || format == PDFCS_SEPARATION || +- format == PDFCS_DEVICEN) { ++ CPDF_ColorSpace::Family format = pColorSpace->GetFamily(); ++ if (format == CPDF_ColorSpace::Family::kDeviceCMYK || ++ format == CPDF_ColorSpace::Family::kSeparation || ++ format == CPDF_ColorSpace::Family::kDeviceN) { + m_BlendType = BlendMode::kDarken; + } + } + return StartDIBBase(); + } + +-bool CPDF_ImageRenderer::Start(CPDF_RenderStatus* pStatus, +- CPDF_ImageObject* pImageObject, ++bool CPDF_ImageRenderer::Start(CPDF_ImageObject* pImageObject, + const CFX_Matrix& mtObj2Device, + bool bStdCS, + BlendMode blendType) { +- ASSERT(pImageObject); +- m_pRenderStatus = pStatus; ++ DCHECK(pImageObject); + m_bStdCS = bStdCS; + m_pImageObject = pImageObject; + m_BlendType = blendType; + m_mtObj2Device = mtObj2Device; +- const CPDF_Dictionary* pOC = m_pImageObject->GetImage()->GetOC(); +- if (pOC && GetRenderOptions().GetOCContext() && +- !GetRenderOptions().GetOCContext()->CheckOCGVisible(pOC)) { ++ RetainPtr pOC = m_pImageObject->GetImage()->GetOC(); ++ if (pOC && !GetRenderOptions().CheckOCGDictVisible(pOC)) + return false; +- } ++ + m_ImageMatrix = m_pImageObject->matrix() * mtObj2Device; + if (StartLoadDIBBase()) + return true; ++ + return StartRenderDIBBase(); + } + +-bool CPDF_ImageRenderer::Start(CPDF_RenderStatus* pStatus, +- const RetainPtr& pDIBBase, ++bool CPDF_ImageRenderer::Start(RetainPtr pDIBBase, + FX_ARGB bitmap_argb, +- int bitmap_alpha, + const CFX_Matrix& mtImage2Device, + const FXDIB_ResampleOptions& options, +- bool bStdCS, +- BlendMode blendType) { +- m_pRenderStatus = pStatus; +- m_pDIBBase = pDIBBase; ++ bool bStdCS) { ++ m_pDIBBase = std::move(pDIBBase); + m_FillArgb = bitmap_argb; +- m_BitmapAlpha = bitmap_alpha; ++ m_BitmapAlpha = 255; + m_ImageMatrix = mtImage2Device; + m_ResampleOptions = options; + m_bStdCS = bStdCS; +- m_BlendType = blendType; ++ m_BlendType = BlendMode::kNormal; + return StartDIBBase(); + } + +@@ -222,7 +252,7 @@ CFX_Matrix CPDF_ImageRenderer::GetDrawMatrix(const FX_RECT& rect) const { + void CPDF_ImageRenderer::CalculateDrawImage( + CFX_DefaultRenderDevice* pBitmapDevice1, + CFX_DefaultRenderDevice* pBitmapDevice2, +- const RetainPtr& pDIBBase, ++ RetainPtr pDIBBase, + const CFX_Matrix& mtNewMatrix, + const FX_RECT& rect) const { + CPDF_RenderStatus bitmap_render(m_pRenderStatus->GetContext(), +@@ -231,19 +261,21 @@ void CPDF_ImageRenderer::CalculateDrawImage( + bitmap_render.SetStdCS(true); + bitmap_render.Initialize(nullptr, nullptr); + +- CPDF_ImageRenderer image_render; +- if (image_render.Start(&bitmap_render, pDIBBase, 0xffffffff, 255, mtNewMatrix, +- m_ResampleOptions, true, BlendMode::kNormal)) { ++ CPDF_ImageRenderer image_render(&bitmap_render); ++ if (image_render.Start(std::move(pDIBBase), 0xffffffff, mtNewMatrix, ++ m_ResampleOptions, true)) { + image_render.Continue(nullptr); + } +- if (m_Loader.MatteColor() == 0xffffffff) ++ if (m_pLoader->MatteColor() == 0xffffffff) + return; +- int matte_r = FXARGB_R(m_Loader.MatteColor()); +- int matte_g = FXARGB_G(m_Loader.MatteColor()); +- int matte_b = FXARGB_B(m_Loader.MatteColor()); ++ int matte_r = FXARGB_R(m_pLoader->MatteColor()); ++ int matte_g = FXARGB_G(m_pLoader->MatteColor()); ++ int matte_b = FXARGB_B(m_pLoader->MatteColor()); + for (int row = 0; row < rect.Height(); row++) { +- uint8_t* dest_scan = pBitmapDevice1->GetBitmap()->GetWritableScanline(row); +- const uint8_t* mask_scan = pBitmapDevice2->GetBitmap()->GetScanline(row); ++ uint8_t* dest_scan = ++ pBitmapDevice1->GetBitmap()->GetWritableScanline(row).data(); ++ const uint8_t* mask_scan = ++ pBitmapDevice2->GetBitmap()->GetScanline(row).data(); + for (int col = 0; col < rect.Width(); col++) { + int alpha = *mask_scan++; + if (!alpha) { +@@ -277,10 +309,10 @@ bool CPDF_ImageRenderer::DrawPatternImage() { + + CFX_Matrix new_matrix = GetDrawMatrix(rect); + CFX_DefaultRenderDevice bitmap_device1; +- if (!bitmap_device1.Create(rect.Width(), rect.Height(), FXDIB_Rgb32, nullptr)) ++ if (!bitmap_device1.Create(rect.Width(), rect.Height(), FXDIB_Format::kArgb, ++ nullptr)) { + return true; +- +- bitmap_device1.GetBitmap()->Clear(0xffffff); ++ } + + CPDF_RenderStatus bitmap_render(m_pRenderStatus->GetContext(), + &bitmap_device1); +@@ -293,23 +325,22 @@ bool CPDF_ImageRenderer::DrawPatternImage() { + patternDevice.Translate(static_cast(-rect.left), + static_cast(-rect.top)); + if (CPDF_TilingPattern* pTilingPattern = m_pPattern->AsTilingPattern()) { +- bitmap_render.DrawTilingPattern(pTilingPattern, m_pImageObject.Get(), ++ bitmap_render.DrawTilingPattern(pTilingPattern, m_pImageObject, + patternDevice, false); + } else if (CPDF_ShadingPattern* pShadingPattern = + m_pPattern->AsShadingPattern()) { +- bitmap_render.DrawShadingPattern(pShadingPattern, m_pImageObject.Get(), ++ bitmap_render.DrawShadingPattern(pShadingPattern, m_pImageObject, + patternDevice, false); + } + + CFX_DefaultRenderDevice bitmap_device2; +- if (!bitmap_device2.Create(rect.Width(), rect.Height(), FXDIB_8bppRgb, +- nullptr)) { ++ if (!bitmap_device2.Create(rect.Width(), rect.Height(), ++ FXDIB_Format::k8bppRgb, nullptr)) { + return true; + } +- bitmap_device2.GetBitmap()->Clear(0); + CalculateDrawImage(&bitmap_device1, &bitmap_device2, m_pDIBBase, new_matrix, + rect); +- bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_8bppMask); ++ bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_Format::k8bppMask); + bitmap_device1.GetBitmap()->MultiplyAlpha(bitmap_device2.GetBitmap()); + bitmap_device1.GetBitmap()->MultiplyAlpha(255); + m_pRenderStatus->GetRenderDevice()->SetDIBitsWithBlend( +@@ -329,48 +360,41 @@ bool CPDF_ImageRenderer::DrawMaskedImage() { + + CFX_Matrix new_matrix = GetDrawMatrix(rect); + CFX_DefaultRenderDevice bitmap_device1; +- if (!bitmap_device1.Create(rect.Width(), rect.Height(), FXDIB_Rgb32, nullptr)) ++ if (!bitmap_device1.Create(rect.Width(), rect.Height(), FXDIB_Format::kRgb32, ++ nullptr)) { + return true; +- +-#if defined _SKIA_SUPPORT_ +- bitmap_device1.Clear(0xffffff); +-#else +- bitmap_device1.GetBitmap()->Clear(0xffffff); +-#endif ++ } ++ ClearBitmap(bitmap_device1, 0xffffffff); + CPDF_RenderStatus bitmap_render(m_pRenderStatus->GetContext(), + &bitmap_device1); + bitmap_render.SetDropObjects(m_pRenderStatus->GetDropObjects()); + bitmap_render.SetStdCS(true); + bitmap_render.Initialize(nullptr, nullptr); +- CPDF_ImageRenderer image_render; +- if (image_render.Start(&bitmap_render, m_pDIBBase, 0, 255, new_matrix, +- m_ResampleOptions, true, BlendMode::kNormal)) { ++ CPDF_ImageRenderer image_render(&bitmap_render); ++ if (image_render.Start(m_pDIBBase, 0, new_matrix, m_ResampleOptions, true)) { + image_render.Continue(nullptr); + } + CFX_DefaultRenderDevice bitmap_device2; +- if (!bitmap_device2.Create(rect.Width(), rect.Height(), FXDIB_8bppRgb, +- nullptr)) ++ if (!bitmap_device2.Create(rect.Width(), rect.Height(), ++ FXDIB_Format::k8bppRgb, nullptr)) { + return true; +- +-#if defined _SKIA_SUPPORT_ +- bitmap_device2.Clear(0); +-#else +- bitmap_device2.GetBitmap()->Clear(0); +-#endif +- CalculateDrawImage(&bitmap_device1, &bitmap_device2, m_Loader.GetMask(), ++ } ++ CalculateDrawImage(&bitmap_device1, &bitmap_device2, m_pLoader->GetMask(), + new_matrix, rect); +-#ifdef _SKIA_SUPPORT_ +- m_pRenderStatus->GetRenderDevice()->SetBitsWithMask( +- bitmap_device1.GetBitmap(), bitmap_device2.GetBitmap(), rect.left, +- rect.top, m_BitmapAlpha, m_BlendType); +-#else +- bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_8bppMask); ++#if defined(_SKIA_SUPPORT_) ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ m_pRenderStatus->GetRenderDevice()->SetBitsWithMask( ++ bitmap_device1.GetBitmap(), bitmap_device2.GetBitmap(), rect.left, ++ rect.top, m_BitmapAlpha, m_BlendType); ++ return false; ++ } ++#endif ++ bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_Format::k8bppMask); + bitmap_device1.GetBitmap()->MultiplyAlpha(bitmap_device2.GetBitmap()); + if (m_BitmapAlpha < 255) + bitmap_device1.GetBitmap()->MultiplyAlpha(m_BitmapAlpha); + m_pRenderStatus->GetRenderDevice()->SetDIBitsWithBlend( + bitmap_device1.GetBitmap(), rect.left, rect.top, m_BlendType); +-#endif // _SKIA_SUPPORT_ + return false; + } + +@@ -388,30 +412,16 @@ bool CPDF_ImageRenderer::StartDIBBase() { + m_ResampleOptions.bInterpolateBilinear = true; + } + } +-#ifdef _SKIA_SUPPORT_ +- RetainPtr premultiplied = m_pDIBBase->Clone(nullptr); +- if (m_pDIBBase->HasAlpha()) +- CFX_SkiaDeviceDriver::PreMultiply(premultiplied); ++ RetainPtr bitmap = PreMultiplyBitmapIfAlpha(m_pDIBBase); + if (m_pRenderStatus->GetRenderDevice()->StartDIBitsWithBlend( +- premultiplied, m_BitmapAlpha, m_FillArgb, m_ImageMatrix, +- m_ResampleOptions, &m_DeviceHandle, m_BlendType)) { ++ bitmap, m_BitmapAlpha, m_FillArgb, m_ImageMatrix, m_ResampleOptions, ++ &m_DeviceHandle, m_BlendType)) { + if (m_DeviceHandle) { + m_Mode = Mode::kBlend; + return true; + } + return false; + } +-#else +- if (m_pRenderStatus->GetRenderDevice()->StartDIBitsWithBlend( +- m_pDIBBase, m_BitmapAlpha, m_FillArgb, m_ImageMatrix, +- m_ResampleOptions, &m_DeviceHandle, m_BlendType)) { +- if (m_DeviceHandle) { +- m_Mode = Mode::kBlend; +- return true; +- } +- return false; +- } +-#endif + + if ((fabs(m_ImageMatrix.b) >= 0.5f || m_ImageMatrix.a == 0) || + (fabs(m_ImageMatrix.c) >= 0.5f || m_ImageMatrix.d == 0)) { +@@ -420,19 +430,19 @@ bool CPDF_ImageRenderer::StartDIBBase() { + return false; + } + +- Optional image_rect = GetUnitRect(); ++ absl::optional image_rect = GetUnitRect(); + if (!image_rect.has_value()) + return false; + + FX_RECT clip_box = m_pRenderStatus->GetRenderDevice()->GetClipBox(); + clip_box.Intersect(image_rect.value()); + m_Mode = Mode::kTransform; +- m_pTransformer = pdfium::MakeUnique( ++ m_pTransformer = std::make_unique( + m_pDIBBase, m_ImageMatrix, m_ResampleOptions, &clip_box); + return true; + } + +- Optional image_rect = GetUnitRect(); ++ absl::optional image_rect = GetUnitRect(); + if (!image_rect.has_value()) + return false; + +@@ -452,7 +462,7 @@ bool CPDF_ImageRenderer::StartDIBBase() { + return false; + } + } +- if (m_pDIBBase->IsAlphaMask()) { ++ if (m_pDIBBase->IsMaskFormat()) { + if (m_BitmapAlpha != 255) + m_FillArgb = FXARGB_MUL_ALPHA(m_FillArgb, m_BitmapAlpha); + if (m_pRenderStatus->GetRenderDevice()->StretchBitMaskWithFlags( +@@ -484,17 +494,18 @@ bool CPDF_ImageRenderer::StartDIBBase() { + + bool CPDF_ImageRenderer::StartBitmapAlpha() { + if (m_pDIBBase->IsOpaqueImage()) { +- CFX_PathData path; ++ CFX_Path path; + path.AppendRect(0, 0, 1, 1); + path.Transform(m_ImageMatrix); + uint32_t fill_color = + ArgbEncode(0xff, m_BitmapAlpha, m_BitmapAlpha, m_BitmapAlpha); +- m_pRenderStatus->GetRenderDevice()->DrawPath(&path, nullptr, nullptr, +- fill_color, 0, FXFILL_WINDING); ++ m_pRenderStatus->GetRenderDevice()->DrawPath( ++ path, nullptr, nullptr, fill_color, 0, ++ CFX_FillRenderOptions::WindingOptions()); + return false; + } + RetainPtr pAlphaMask; +- if (m_pDIBBase->IsAlphaMask()) ++ if (m_pDIBBase->IsMaskFormat()) + pAlphaMask = m_pDIBBase; + else + pAlphaMask = m_pDIBBase->CloneAlphaMask(); +@@ -513,7 +524,7 @@ bool CPDF_ImageRenderer::StartBitmapAlpha() { + return false; + } + +- Optional image_rect = GetUnitRect(); ++ absl::optional image_rect = GetUnitRect(); + if (!image_rect.has_value()) + return false; + +@@ -543,12 +554,10 @@ bool CPDF_ImageRenderer::Continue(PauseIndicatorIface* pPause) { + case Mode::kTransform: + return ContinueTransform(pPause); + } +- NOTREACHED(); +- return false; + } + + bool CPDF_ImageRenderer::ContinueDefault(PauseIndicatorIface* pPause) { +- if (m_Loader.Continue(pPause, m_pRenderStatus.Get())) ++ if (m_pLoader->Continue(pPause)) + return true; + + if (!StartRenderDIBBase()) +@@ -573,7 +582,7 @@ bool CPDF_ImageRenderer::ContinueTransform(PauseIndicatorIface* pPause) { + if (!pBitmap) + return false; + +- if (pBitmap->IsAlphaMask()) { ++ if (pBitmap->IsMaskFormat()) { + if (m_BitmapAlpha != 255) + m_FillArgb = FXARGB_MUL_ALPHA(m_FillArgb, m_BitmapAlpha); + m_Result = m_pRenderStatus->GetRenderDevice()->SetBitMask( +@@ -590,37 +599,24 @@ bool CPDF_ImageRenderer::ContinueTransform(PauseIndicatorIface* pPause) { + } + + void CPDF_ImageRenderer::HandleFilters() { +- CPDF_Object* pFilters = +- m_pImageObject->GetImage()->GetStream()->GetDict()->GetDirectObjectFor( +- "Filter"); +- if (!pFilters) +- return; +- +- if (pFilters->IsName()) { +- ByteString bsDecodeType = pFilters->GetString(); +- if (bsDecodeType == "DCTDecode" || bsDecodeType == "JPXDecode") +- m_ResampleOptions.bLossy = true; +- return; +- } +- +- CPDF_Array* pArray = pFilters->AsArray(); +- if (!pArray) ++ absl::optional decoder_array = ++ GetDecoderArray(m_pImageObject->GetImage()->GetStream()->GetDict()); ++ if (!decoder_array.has_value()) + return; + +- for (size_t i = 0; i < pArray->size(); i++) { +- ByteString bsDecodeType = pArray->GetStringAt(i); +- if (bsDecodeType == "DCTDecode" || bsDecodeType == "JPXDecode") { ++ for (const auto& decoder : decoder_array.value()) { ++ if (decoder.first == "DCTDecode" || decoder.first == "JPXDecode") { + m_ResampleOptions.bLossy = true; +- break; ++ return; + } + } + } + +-Optional CPDF_ImageRenderer::GetUnitRect() const { ++absl::optional CPDF_ImageRenderer::GetUnitRect() const { + CFX_FloatRect image_rect_f = m_ImageMatrix.GetUnitRect(); + FX_RECT image_rect = image_rect_f.GetOuterRect(); + if (!image_rect.Valid()) +- return {}; ++ return absl::nullopt; + return image_rect; + } + +@@ -629,7 +625,7 @@ bool CPDF_ImageRenderer::GetDimensionsFromUnitRect(const FX_RECT& rect, + int* top, + int* width, + int* height) const { +- ASSERT(rect.Valid()); ++ DCHECK(rect.Valid()); + + int dest_width = rect.Width(); + int dest_height = rect.Height(); +diff --git a/core/fpdfapi/render/cpdf_imagerenderer.h b/core/fpdfapi/render/cpdf_imagerenderer.h +index d7c7bf0ad..530ac957f 100644 +--- a/core/fpdfapi/render/cpdf_imagerenderer.h ++++ b/core/fpdfapi/render/cpdf_imagerenderer.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,42 +9,37 @@ + + #include + +-#include "core/fpdfapi/render/cpdf_imageloader.h" + #include "core/fxcrt/fx_coordinates.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + #include "core/fxge/dib/cfx_imagerenderer.h" +-#include "core/fxge/fx_dib.h" +-#include "third_party/base/optional.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + +-class CFX_DIBitmap; + class CFX_DIBBase; + class CFX_DefaultRenderDevice; + class CFX_ImageTransformer; ++class CPDF_ImageLoader; + class CPDF_ImageObject; +-class CPDF_PageObject; + class CPDF_Pattern; + class CPDF_RenderOptions; + class CPDF_RenderStatus; + + class CPDF_ImageRenderer { + public: +- CPDF_ImageRenderer(); ++ explicit CPDF_ImageRenderer(CPDF_RenderStatus* pStatus); + ~CPDF_ImageRenderer(); + +- bool Start(CPDF_RenderStatus* pStatus, +- CPDF_ImageObject* pImageObject, ++ bool Start(CPDF_ImageObject* pImageObject, + const CFX_Matrix& mtObj2Device, + bool bStdCS, + BlendMode blendType); + +- bool Start(CPDF_RenderStatus* pStatus, +- const RetainPtr& pDIBBase, ++ bool Start(RetainPtr pDIBBase, + FX_ARGB bitmap_argb, +- int bitmap_alpha, + const CFX_Matrix& mtImage2Device, + const FXDIB_ResampleOptions& options, +- bool bStdCS, +- BlendMode blendType); ++ bool bStdCS); + + bool Continue(PauseIndicatorIface* pPause); + bool GetResult() const { return m_Result; } +@@ -71,25 +66,25 @@ class CPDF_ImageRenderer { + CFX_Matrix GetDrawMatrix(const FX_RECT& rect) const; + void CalculateDrawImage(CFX_DefaultRenderDevice* pBitmapDevice1, + CFX_DefaultRenderDevice* pBitmapDevice2, +- const RetainPtr& pDIBBase, ++ RetainPtr pDIBBase, + const CFX_Matrix& mtNewMatrix, + const FX_RECT& rect) const; + const CPDF_RenderOptions& GetRenderOptions() const; + void HandleFilters(); +- Optional GetUnitRect() const; ++ absl::optional GetUnitRect() const; + bool GetDimensionsFromUnitRect(const FX_RECT& rect, + int* left, + int* top, + int* width, + int* height) const; + +- UnownedPtr m_pRenderStatus; ++ UnownedPtr const m_pRenderStatus; + UnownedPtr m_pImageObject; + RetainPtr m_pPattern; + RetainPtr m_pDIBBase; + CFX_Matrix m_mtObj2Device; + CFX_Matrix m_ImageMatrix; +- CPDF_ImageLoader m_Loader; ++ std::unique_ptr const m_pLoader; + std::unique_ptr m_pTransformer; + std::unique_ptr m_DeviceHandle; + Mode m_Mode = Mode::kNone; +diff --git a/core/fpdfapi/render/cpdf_pagerendercache.cpp b/core/fpdfapi/render/cpdf_pagerendercache.cpp +deleted file mode 100644 +index ca4f8ee8d..000000000 +--- a/core/fpdfapi/render/cpdf_pagerendercache.cpp ++++ /dev/null +@@ -1,133 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fpdfapi/render/cpdf_pagerendercache.h" +- +-#include +-#include +- +-#include "core/fpdfapi/page/cpdf_image.h" +-#include "core/fpdfapi/page/cpdf_page.h" +-#include "core/fpdfapi/render/cpdf_imagecacheentry.h" +-#include "core/fpdfapi/render/cpdf_renderstatus.h" +-#include "core/fxge/dib/cfx_dibitmap.h" +-#include "third_party/base/ptr_util.h" +- +-namespace { +- +-struct CacheInfo { +- CacheInfo(uint32_t t, CPDF_Stream* stream) : time(t), pStream(stream) {} +- +- uint32_t time; +- CPDF_Stream* pStream; +- +- bool operator<(const CacheInfo& other) const { return time < other.time; } +-}; +- +-} // namespace +- +-CPDF_PageRenderCache::CPDF_PageRenderCache(CPDF_Page* pPage) : m_pPage(pPage) {} +- +-CPDF_PageRenderCache::~CPDF_PageRenderCache() = default; +- +-void CPDF_PageRenderCache::CacheOptimization(int32_t dwLimitCacheSize) { +- if (m_nCacheSize <= (uint32_t)dwLimitCacheSize) +- return; +- +- size_t nCount = m_ImageCache.size(); +- std::vector cache_info; +- cache_info.reserve(nCount); +- for (const auto& it : m_ImageCache) { +- cache_info.emplace_back(it.second->GetTimeCount(), +- it.second->GetImage()->GetStream()); +- } +- std::sort(cache_info.begin(), cache_info.end()); +- +- // Check if time value is about to roll over and reset all entries. +- // The comparision is legal because uint32_t is an unsigned type. +- uint32_t nTimeCount = m_nTimeCount; +- if (nTimeCount + 1 < nTimeCount) { +- for (size_t i = 0; i < nCount; i++) +- m_ImageCache[cache_info[i].pStream]->m_dwTimeCount = i; +- m_nTimeCount = nCount; +- } +- +- size_t i = 0; +- while (i + 15 < nCount) +- ClearImageCacheEntry(cache_info[i++].pStream); +- +- while (i < nCount && m_nCacheSize > (uint32_t)dwLimitCacheSize) +- ClearImageCacheEntry(cache_info[i++].pStream); +-} +- +-void CPDF_PageRenderCache::ClearImageCacheEntry(CPDF_Stream* pStream) { +- auto it = m_ImageCache.find(pStream); +- if (it == m_ImageCache.end()) +- return; +- +- m_nCacheSize -= it->second->EstimateSize(); +- m_ImageCache.erase(it); +-} +- +-bool CPDF_PageRenderCache::StartGetCachedBitmap( +- const RetainPtr& pImage, +- bool bStdCS, +- uint32_t GroupFamily, +- bool bLoadMask, +- CPDF_RenderStatus* pRenderStatus) { +- CPDF_Stream* pStream = pImage->GetStream(); +- const auto it = m_ImageCache.find(pStream); +- m_bCurFindCache = it != m_ImageCache.end(); +- if (m_bCurFindCache) { +- m_pCurImageCacheEntry = it->second.get(); +- } else { +- m_pCurImageCacheEntry = pdfium::MakeUnique( +- m_pPage->GetDocument(), pImage); +- } +- CPDF_DIB::LoadState ret = m_pCurImageCacheEntry->StartGetCachedBitmap( +- pRenderStatus->GetFormResource(), m_pPage->m_pPageResources.Get(), bStdCS, +- GroupFamily, bLoadMask, pRenderStatus); +- if (ret == CPDF_DIB::LoadState::kContinue) +- return true; +- +- m_nTimeCount++; +- if (!m_bCurFindCache) +- m_ImageCache[pStream] = m_pCurImageCacheEntry.Release(); +- +- if (ret == CPDF_DIB::LoadState::kFail) +- m_nCacheSize += m_pCurImageCacheEntry->EstimateSize(); +- +- return false; +-} +- +-bool CPDF_PageRenderCache::Continue(PauseIndicatorIface* pPause, +- CPDF_RenderStatus* pRenderStatus) { +- bool ret = m_pCurImageCacheEntry->Continue(pPause, pRenderStatus); +- if (ret) +- return true; +- +- m_nTimeCount++; +- if (!m_bCurFindCache) { +- m_ImageCache[m_pCurImageCacheEntry->GetImage()->GetStream()] = +- m_pCurImageCacheEntry.Release(); +- } +- m_nCacheSize += m_pCurImageCacheEntry->EstimateSize(); +- return false; +-} +- +-void CPDF_PageRenderCache::ResetBitmapForImage( +- const RetainPtr& pImage) { +- CPDF_ImageCacheEntry* pEntry; +- CPDF_Stream* pStream = pImage->GetStream(); +- const auto it = m_ImageCache.find(pStream); +- if (it == m_ImageCache.end()) +- return; +- +- pEntry = it->second.get(); +- m_nCacheSize -= pEntry->EstimateSize(); +- pEntry->Reset(); +- m_nCacheSize += pEntry->EstimateSize(); +-} +diff --git a/core/fpdfapi/render/cpdf_pagerendercache.h b/core/fpdfapi/render/cpdf_pagerendercache.h +deleted file mode 100644 +index 1fb2a6117..000000000 +--- a/core/fpdfapi/render/cpdf_pagerendercache.h ++++ /dev/null +@@ -1,60 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FPDFAPI_RENDER_CPDF_PAGERENDERCACHE_H_ +-#define CORE_FPDFAPI_RENDER_CPDF_PAGERENDERCACHE_H_ +- +-#include +-#include +- +-#include "core/fpdfapi/page/cpdf_page.h" +-#include "core/fxcrt/fx_system.h" +-#include "core/fxcrt/maybe_owned.h" +-#include "core/fxcrt/retain_ptr.h" +-#include "core/fxcrt/unowned_ptr.h" +- +-class CPDF_Image; +-class CPDF_ImageCacheEntry; +-class CPDF_Page; +-class CPDF_RenderStatus; +-class CPDF_Stream; +-class PauseIndicatorIface; +- +-class CPDF_PageRenderCache : public CPDF_Page::RenderCacheIface { +- public: +- explicit CPDF_PageRenderCache(CPDF_Page* pPage); +- ~CPDF_PageRenderCache() override; +- +- // CPDF_Page::RenderCacheIface: +- void ResetBitmapForImage(const RetainPtr& pImage) override; +- +- void CacheOptimization(int32_t dwLimitCacheSize); +- uint32_t GetTimeCount() const { return m_nTimeCount; } +- CPDF_Page* GetPage() const { return m_pPage.Get(); } +- CPDF_ImageCacheEntry* GetCurImageCacheEntry() const { +- return m_pCurImageCacheEntry.Get(); +- } +- +- bool StartGetCachedBitmap(const RetainPtr& pImage, +- bool bStdCS, +- uint32_t GroupFamily, +- bool bLoadMask, +- CPDF_RenderStatus* pRenderStatus); +- +- bool Continue(PauseIndicatorIface* pPause, CPDF_RenderStatus* pRenderStatus); +- +- private: +- void ClearImageCacheEntry(CPDF_Stream* pStream); +- +- UnownedPtr const m_pPage; +- std::map> m_ImageCache; +- MaybeOwned m_pCurImageCacheEntry; +- uint32_t m_nTimeCount = 0; +- uint32_t m_nCacheSize = 0; +- bool m_bCurFindCache = false; +-}; +- +-#endif // CORE_FPDFAPI_RENDER_CPDF_PAGERENDERCACHE_H_ +diff --git a/core/fpdfapi/render/cpdf_pagerendercontext.cpp b/core/fpdfapi/render/cpdf_pagerendercontext.cpp +index 8dd66c7fa..9899041f4 100644 +--- a/core/fpdfapi/render/cpdf_pagerendercontext.cpp ++++ b/core/fpdfapi/render/cpdf_pagerendercontext.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,6 +12,6 @@ + #include "core/fpdfapi/render/cpdf_renderoptions.h" + #include "core/fxge/cfx_renderdevice.h" + +-CPDF_PageRenderContext::CPDF_PageRenderContext() {} ++CPDF_PageRenderContext::CPDF_PageRenderContext() = default; + +-CPDF_PageRenderContext::~CPDF_PageRenderContext() {} ++CPDF_PageRenderContext::~CPDF_PageRenderContext() = default; +diff --git a/core/fpdfapi/render/cpdf_pagerendercontext.h b/core/fpdfapi/render/cpdf_pagerendercontext.h +index 8a5a5eb19..f6366c4b6 100644 +--- a/core/fpdfapi/render/cpdf_pagerendercontext.h ++++ b/core/fpdfapi/render/cpdf_pagerendercontext.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,7 +22,7 @@ class CPDF_PageRenderContext final : public CPDF_Page::RenderContextIface { + // Context merely manages the lifetime for callers. + class AnnotListIface { + public: +- virtual ~AnnotListIface() {} ++ virtual ~AnnotListIface() = default; + }; + + CPDF_PageRenderContext(); +diff --git a/core/fpdfapi/render/cpdf_progressiverenderer.cpp b/core/fpdfapi/render/cpdf_progressiverenderer.cpp +index 6e5fc364e..c29077acc 100644 +--- a/core/fpdfapi/render/cpdf_progressiverenderer.cpp ++++ b/core/fpdfapi/render/cpdf_progressiverenderer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,14 +8,13 @@ + + #include "core/fpdfapi/page/cpdf_image.h" + #include "core/fpdfapi/page/cpdf_imageobject.h" ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" + #include "core/fpdfapi/page/cpdf_pageobject.h" + #include "core/fpdfapi/page/cpdf_pageobjectholder.h" +-#include "core/fpdfapi/render/cpdf_pagerendercache.h" + #include "core/fpdfapi/render/cpdf_renderoptions.h" + #include "core/fpdfapi/render/cpdf_renderstatus.h" + #include "core/fxcrt/pauseindicator_iface.h" + #include "core/fxge/cfx_renderdevice.h" +-#include "third_party/base/ptr_util.h" + + CPDF_ProgressiveRenderer::CPDF_ProgressiveRenderer( + CPDF_RenderContext* pContext, +@@ -47,26 +46,26 @@ void CPDF_ProgressiveRenderer::Continue(PauseIndicatorIface* pPause) { + return; + } + m_pCurrentLayer = m_pContext->GetLayer(m_LayerIndex); +- m_LastObjectRendered = m_pCurrentLayer->m_pObjectHolder->end(); +- m_pRenderStatus = pdfium::MakeUnique(m_pContext.Get(), +- m_pDevice.Get()); ++ m_LastObjectRendered = m_pCurrentLayer->GetObjectHolder()->end(); ++ m_pRenderStatus = ++ std::make_unique(m_pContext, m_pDevice); + if (m_pOptions) + m_pRenderStatus->SetOptions(*m_pOptions); + m_pRenderStatus->SetTransparency( +- m_pCurrentLayer->m_pObjectHolder->GetTransparency()); ++ m_pCurrentLayer->GetObjectHolder()->GetTransparency()); + m_pRenderStatus->Initialize(nullptr, nullptr); + m_pDevice->SaveState(); +- m_ClipRect = m_pCurrentLayer->m_Matrix.GetInverse().TransformRect( ++ m_ClipRect = m_pCurrentLayer->GetMatrix().GetInverse().TransformRect( + CFX_FloatRect(m_pDevice->GetClipBox())); + } + CPDF_PageObjectHolder::const_iterator iter; + CPDF_PageObjectHolder::const_iterator iterEnd = +- m_pCurrentLayer->m_pObjectHolder->end(); ++ m_pCurrentLayer->GetObjectHolder()->end(); + if (m_LastObjectRendered != iterEnd) { + iter = m_LastObjectRendered; + ++iter; + } else { +- iter = m_pCurrentLayer->m_pObjectHolder->begin(); ++ iter = m_pCurrentLayer->GetObjectHolder()->begin(); + } + int nObjsToGo = kStepLimit; + bool is_mask = false; +@@ -81,13 +80,13 @@ void CPDF_ProgressiveRenderer::Continue(PauseIndicatorIface* pPause) { + if (m_pDevice->GetDeviceType() == DeviceType::kPrinter) { + m_LastObjectRendered = iter; + m_pRenderStatus->ProcessClipPath(pCurObj->m_ClipPath, +- m_pCurrentLayer->m_Matrix); ++ m_pCurrentLayer->GetMatrix()); + return; + } + is_mask = true; + } + if (m_pRenderStatus->ContinueSingleObject( +- pCurObj, m_pCurrentLayer->m_Matrix, pPause)) { ++ pCurObj, m_pCurrentLayer->GetMatrix(), pPause)) { + return; + } + if (pCurObj->IsImage() && m_pRenderStatus->GetRenderOptions() +@@ -111,7 +110,7 @@ void CPDF_ProgressiveRenderer::Continue(PauseIndicatorIface* pPause) { + if (is_mask && iter != iterEnd) + return; + } +- if (m_pCurrentLayer->m_pObjectHolder->GetParseState() == ++ if (m_pCurrentLayer->GetObjectHolder()->GetParseState() == + CPDF_PageObjectHolder::ParseState::kParsed) { + m_pRenderStatus.reset(); + m_pDevice->RestoreState(false); +@@ -122,8 +121,8 @@ void CPDF_ProgressiveRenderer::Continue(PauseIndicatorIface* pPause) { + } else if (is_mask) { + return; + } else { +- m_pCurrentLayer->m_pObjectHolder->ContinueParse(pPause); +- if (m_pCurrentLayer->m_pObjectHolder->GetParseState() != ++ m_pCurrentLayer->GetObjectHolder()->ContinueParse(pPause); ++ if (m_pCurrentLayer->GetObjectHolder()->GetParseState() != + CPDF_PageObjectHolder::ParseState::kParsed) { + return; + } +diff --git a/core/fpdfapi/render/cpdf_progressiverenderer.h b/core/fpdfapi/render/cpdf_progressiverenderer.h +index fd399f96c..67170fc80 100644 +--- a/core/fpdfapi/render/cpdf_progressiverenderer.h ++++ b/core/fpdfapi/render/cpdf_progressiverenderer.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,14 @@ + #ifndef CORE_FPDFAPI_RENDER_CPDF_PROGRESSIVERENDERER_H_ + #define CORE_FPDFAPI_RENDER_CPDF_PROGRESSIVERENDERER_H_ + ++#include ++ + #include + + #include "core/fpdfapi/page/cpdf_pageobjectholder.h" + #include "core/fpdfapi/render/cpdf_rendercontext.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/unowned_ptr.h" + + class CPDF_RenderOptions; + class CPDF_RenderStatus; +@@ -42,12 +44,12 @@ class CPDF_ProgressiveRenderer { + + private: + // Maximum page objects to render before checking for pause. +- static const int kStepLimit = 100; ++ static constexpr int kStepLimit = 100; + + Status m_Status = kReady; + UnownedPtr const m_pContext; + UnownedPtr const m_pDevice; +- const CPDF_RenderOptions* const m_pOptions; ++ UnownedPtr const m_pOptions; + std::unique_ptr m_pRenderStatus; + CFX_FloatRect m_ClipRect; + uint32_t m_LayerIndex = 0; +diff --git a/core/fpdfapi/render/cpdf_rendercontext.cpp b/core/fpdfapi/render/cpdf_rendercontext.cpp +index 1280cee13..41168407e 100644 +--- a/core/fpdfapi/render/cpdf_rendercontext.cpp ++++ b/core/fpdfapi/render/cpdf_rendercontext.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,11 +6,13 @@ + + #include "core/fpdfapi/render/cpdf_rendercontext.h" + ++#include ++ ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" + #include "core/fpdfapi/page/cpdf_pageobject.h" + #include "core/fpdfapi/page/cpdf_pageobjectholder.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" +-#include "core/fpdfapi/render/cpdf_pagerendercache.h" + #include "core/fpdfapi/render/cpdf_progressiverenderer.h" + #include "core/fpdfapi/render/cpdf_renderoptions.h" + #include "core/fpdfapi/render/cpdf_renderstatus.h" +@@ -18,41 +20,32 @@ + #include "core/fxge/cfx_defaultrenderdevice.h" + #include "core/fxge/cfx_renderdevice.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" + +-CPDF_RenderContext::CPDF_RenderContext(CPDF_Document* pDoc, +- CPDF_Dictionary* pPageResources, +- CPDF_PageRenderCache* pPageCache) ++CPDF_RenderContext::CPDF_RenderContext( ++ CPDF_Document* pDoc, ++ RetainPtr pPageResources, ++ CPDF_PageImageCache* pPageCache) + : m_pDocument(pDoc), +- m_pPageResources(pPageResources), ++ m_pPageResources(std::move(pPageResources)), + m_pPageCache(pPageCache) {} + + CPDF_RenderContext::~CPDF_RenderContext() = default; + +-void CPDF_RenderContext::GetBackground(const RetainPtr& pBuffer, ++void CPDF_RenderContext::GetBackground(RetainPtr pBuffer, + const CPDF_PageObject* pObj, + const CPDF_RenderOptions* pOptions, + const CFX_Matrix& mtFinal) { + CFX_DefaultRenderDevice device; +- device.Attach(pBuffer, false, nullptr, false); +- ++ device.Attach(std::move(pBuffer)); + device.FillRect(FX_RECT(0, 0, device.GetWidth(), device.GetHeight()), + 0xffffffff); + Render(&device, pObj, pOptions, &mtFinal); + } + + void CPDF_RenderContext::AppendLayer(CPDF_PageObjectHolder* pObjectHolder, +- const CFX_Matrix* pObject2Device) { +- m_Layers.emplace_back(); +- m_Layers.back().m_pObjectHolder = pObjectHolder; +- if (pObject2Device) +- m_Layers.back().m_Matrix = *pObject2Device; +-} +- +-void CPDF_RenderContext::Render(CFX_RenderDevice* pDevice, +- const CPDF_RenderOptions* pOptions, +- const CFX_Matrix* pLastMatrix) { +- Render(pDevice, nullptr, pOptions, pLastMatrix); ++ const CFX_Matrix& mtObject2Device) { ++ m_Layers.emplace_back(pObjectHolder, mtObject2Device); + } + + void CPDF_RenderContext::Render(CFX_RenderDevice* pDevice, +@@ -65,14 +58,14 @@ void CPDF_RenderContext::Render(CFX_RenderDevice* pDevice, + if (pOptions) + status.SetOptions(*pOptions); + status.SetStopObject(pStopObj); +- status.SetTransparency(layer.m_pObjectHolder->GetTransparency()); +- CFX_Matrix final_matrix = layer.m_Matrix; ++ status.SetTransparency(layer.GetObjectHolder()->GetTransparency()); ++ CFX_Matrix final_matrix = layer.GetMatrix(); + if (pLastMatrix) { + final_matrix *= *pLastMatrix; + status.SetDeviceMatrix(*pLastMatrix); + } + status.Initialize(nullptr, nullptr); +- status.RenderObjectList(layer.m_pObjectHolder.Get(), final_matrix); ++ status.RenderObjectList(layer.GetObjectHolder(), final_matrix); + if (status.GetRenderOptions().GetOptions().bLimitedImageCache) { + m_pPageCache->CacheOptimization( + status.GetRenderOptions().GetCacheSizeLimit()); +@@ -82,7 +75,9 @@ void CPDF_RenderContext::Render(CFX_RenderDevice* pDevice, + } + } + +-CPDF_RenderContext::Layer::Layer() = default; ++CPDF_RenderContext::Layer::Layer(CPDF_PageObjectHolder* pHolder, ++ const CFX_Matrix& matrix) ++ : m_pObjectHolder(pHolder), m_Matrix(matrix) {} + + CPDF_RenderContext::Layer::Layer(const Layer& that) = default; + +diff --git a/core/fpdfapi/render/cpdf_rendercontext.h b/core/fpdfapi/render/cpdf_rendercontext.h +index d9efbc446..383e79818 100644 +--- a/core/fpdfapi/render/cpdf_rendercontext.h ++++ b/core/fpdfapi/render/cpdf_rendercontext.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,46 +13,46 @@ + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + ++class CFX_DIBitmap; ++class CFX_Matrix; ++class CFX_RenderDevice; + class CPDF_Dictionary; + class CPDF_Document; ++class CPDF_PageImageCache; + class CPDF_PageObject; + class CPDF_PageObjectHolder; +-class CPDF_PageRenderCache; + class CPDF_RenderOptions; +-class CFX_DIBitmap; +-class CFX_Matrix; +-class CFX_RenderDevice; + + class CPDF_RenderContext { + public: + class Layer { + public: +- Layer(); ++ Layer(CPDF_PageObjectHolder* pHolder, const CFX_Matrix& matrix); + Layer(const Layer& that); + ~Layer(); + +- UnownedPtr m_pObjectHolder; +- CFX_Matrix m_Matrix; ++ CPDF_PageObjectHolder* GetObjectHolder() { return m_pObjectHolder; } ++ const CFX_Matrix& GetMatrix() const { return m_Matrix; } ++ ++ private: ++ UnownedPtr const m_pObjectHolder; ++ const CFX_Matrix m_Matrix; + }; + + CPDF_RenderContext(CPDF_Document* pDoc, +- CPDF_Dictionary* pPageResources, +- CPDF_PageRenderCache* pPageCache); ++ RetainPtr pPageResources, ++ CPDF_PageImageCache* pPageCache); + ~CPDF_RenderContext(); + + void AppendLayer(CPDF_PageObjectHolder* pObjectHolder, +- const CFX_Matrix* pObject2Device); +- +- void Render(CFX_RenderDevice* pDevice, +- const CPDF_RenderOptions* pOptions, +- const CFX_Matrix* pLastMatrix); ++ const CFX_Matrix& mtObject2Device); + + void Render(CFX_RenderDevice* pDevice, + const CPDF_PageObject* pStopObj, + const CPDF_RenderOptions* pOptions, + const CFX_Matrix* pLastMatrix); + +- void GetBackground(const RetainPtr& pBuffer, ++ void GetBackground(RetainPtr pBuffer, + const CPDF_PageObject* pObj, + const CPDF_RenderOptions* pOptions, + const CFX_Matrix& mtFinal); +@@ -60,14 +60,19 @@ class CPDF_RenderContext { + size_t CountLayers() const { return m_Layers.size(); } + Layer* GetLayer(uint32_t index) { return &m_Layers[index]; } + +- CPDF_Document* GetDocument() const { return m_pDocument.Get(); } +- CPDF_Dictionary* GetPageResources() const { return m_pPageResources.Get(); } +- CPDF_PageRenderCache* GetPageCache() const { return m_pPageCache.Get(); } ++ CPDF_Document* GetDocument() const { return m_pDocument; } ++ const CPDF_Dictionary* GetPageResources() const { ++ return m_pPageResources.Get(); ++ } ++ RetainPtr GetMutablePageResources() { ++ return m_pPageResources; ++ } ++ CPDF_PageImageCache* GetPageCache() const { return m_pPageCache; } + +- protected: ++ private: + UnownedPtr const m_pDocument; + RetainPtr const m_pPageResources; +- UnownedPtr const m_pPageCache; ++ UnownedPtr const m_pPageCache; + std::vector m_Layers; + }; + +diff --git a/core/fpdfapi/render/cpdf_renderoptions.cpp b/core/fpdfapi/render/cpdf_renderoptions.cpp +index 11efe9e9a..68bb472e0 100644 +--- a/core/fpdfapi/render/cpdf_renderoptions.cpp ++++ b/core/fpdfapi/render/cpdf_renderoptions.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -17,6 +17,9 @@ CPDF_RenderOptions::Options::Options() = default; + CPDF_RenderOptions::Options::Options(const CPDF_RenderOptions::Options& rhs) = + default; + ++CPDF_RenderOptions::Options& CPDF_RenderOptions::Options::operator=( ++ const CPDF_RenderOptions::Options& rhs) = default; ++ + CPDF_RenderOptions::CPDF_RenderOptions() { + // TODO(thestig): Make constexpr to initialize |m_Options| once C++14 is + // available. +@@ -42,6 +45,34 @@ FX_ARGB CPDF_RenderOptions::TranslateColor(FX_ARGB argb) const { + return ArgbEncode(a, gray, gray, gray); + } + ++FX_ARGB CPDF_RenderOptions::TranslateObjectColor( ++ FX_ARGB argb, ++ CPDF_PageObject::Type object_type, ++ RenderType render_type) const { ++ if (!ColorModeIs(kForcedColor)) ++ return TranslateColor(argb); ++ ++ switch (object_type) { ++ case CPDF_PageObject::Type::kPath: ++ return render_type == RenderType::kFill ? m_ColorScheme.path_fill_color ++ : m_ColorScheme.path_stroke_color; ++ case CPDF_PageObject::Type::kText: ++ return render_type == RenderType::kFill ? m_ColorScheme.text_fill_color ++ : m_ColorScheme.text_stroke_color; ++ default: ++ return argb; ++ } ++} ++ + uint32_t CPDF_RenderOptions::GetCacheSizeLimit() const { + return kCacheSizeLimitBytes; + } ++ ++bool CPDF_RenderOptions::CheckOCGDictVisible(const CPDF_Dictionary* pOC) const { ++ return !m_pOCContext || m_pOCContext->CheckOCGDictVisible(pOC); ++} ++ ++bool CPDF_RenderOptions::CheckPageObjectVisible( ++ const CPDF_PageObject* pPageObj) const { ++ return !m_pOCContext || m_pOCContext->CheckPageObjectVisible(pPageObj); ++} +diff --git a/core/fpdfapi/render/cpdf_renderoptions.h b/core/fpdfapi/render/cpdf_renderoptions.h +index 84f7e4c4a..12eb91ad8 100644 +--- a/core/fpdfapi/render/cpdf_renderoptions.h ++++ b/core/fpdfapi/render/cpdf_renderoptions.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,35 +7,43 @@ + #ifndef CORE_FPDFAPI_RENDER_CPDF_RENDEROPTIONS_H_ + #define CORE_FPDFAPI_RENDER_CPDF_RENDEROPTIONS_H_ + ++#include ++ + #include "core/fpdfapi/page/cpdf_occontext.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fpdfapi/page/cpdf_pageobject.h" + #include "core/fxcrt/retain_ptr.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" ++ ++class CPDF_Dictionary; + + class CPDF_RenderOptions { + public: +- enum Type : uint8_t { kNormal = 0, kGray, kAlpha }; ++ enum Type : uint8_t { kNormal = 0, kGray, kAlpha, kForcedColor }; ++ ++ enum RenderType : uint8_t { kFill = 0, kStroke }; + + struct Options { + Options(); + Options(const Options& rhs); ++ Options& operator=(const Options& rhs); + + bool bClearType = false; +- bool bPrintGraphicText = false; +- bool bPrintPreview = false; +- bool bBGRStripe = false; + bool bNoNativeText = false; + bool bForceHalftone = false; + bool bRectAA = false; +- bool bFillFullcover = false; +- bool bPrintImageText = false; +- bool bOverprint = false; +- bool bThinLine = false; + bool bBreakForMasks = false; + bool bNoTextSmooth = false; + bool bNoPathSmooth = false; + bool bNoImageSmooth = false; + bool bLimitedImageCache = false; ++ bool bConvertFillToStroke = false; ++ }; ++ ++ struct ColorScheme { ++ FX_ARGB path_fill_color; ++ FX_ARGB path_stroke_color; ++ FX_ARGB text_fill_color; ++ FX_ARGB text_stroke_color; + }; + + CPDF_RenderOptions(); +@@ -43,6 +51,13 @@ class CPDF_RenderOptions { + ~CPDF_RenderOptions(); + + FX_ARGB TranslateColor(FX_ARGB argb) const; ++ FX_ARGB TranslateObjectColor(FX_ARGB argb, ++ CPDF_PageObject::Type object_type, ++ RenderType render_type) const; ++ ++ void SetColorScheme(const ColorScheme& color_scheme) { ++ m_ColorScheme = color_scheme; ++ } + + void SetColorMode(Type mode) { m_ColorMode = mode; } + bool ColorModeIs(Type mode) const { return m_ColorMode == mode; } +@@ -51,6 +66,8 @@ class CPDF_RenderOptions { + Options& GetOptions() { return m_Options; } + + uint32_t GetCacheSizeLimit() const; ++ bool CheckOCGDictVisible(const CPDF_Dictionary* pOC) const; ++ bool CheckPageObjectVisible(const CPDF_PageObject* pPageObj) const; + + void SetDrawAnnots(bool draw) { m_bDrawAnnots = draw; } + bool GetDrawAnnots() const { return m_bDrawAnnots; } +@@ -58,12 +75,12 @@ class CPDF_RenderOptions { + void SetOCContext(RetainPtr context) { + m_pOCContext = context; + } +- const CPDF_OCContext* GetOCContext() const { return m_pOCContext.Get(); } + + private: + Type m_ColorMode = kNormal; + bool m_bDrawAnnots = false; + Options m_Options; ++ ColorScheme m_ColorScheme = {}; + RetainPtr m_pOCContext; + }; + +diff --git a/core/fpdfapi/render/cpdf_rendershading.cpp b/core/fpdfapi/render/cpdf_rendershading.cpp +index 53fb79a66..ce7543b6e 100644 +--- a/core/fpdfapi/render/cpdf_rendershading.cpp ++++ b/core/fpdfapi/render/cpdf_rendershading.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,9 +6,10 @@ + + #include "core/fpdfapi/render/cpdf_rendershading.h" + ++#include ++ + #include + #include +-#include + #include + #include + #include +@@ -25,9 +26,16 @@ + #include "core/fpdfapi/render/cpdf_renderoptions.h" + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/span_util.h" + #include "core/fxge/cfx_defaultrenderdevice.h" ++#include "core/fxge/cfx_fillrenderoptions.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/cxx17_backports.h" ++#include "third_party/base/span.h" + + namespace { + +@@ -57,28 +65,28 @@ std::array GetShadingSteps( + const RetainPtr& pCS, + int alpha, + size_t results_count) { +- ASSERT(results_count >= CountOutputsFromFunctions(funcs)); +- ASSERT(results_count >= pCS->CountComponents()); ++ DCHECK(results_count >= CountOutputsFromFunctions(funcs)); ++ DCHECK(results_count >= pCS->CountComponents()); + std::array shading_steps; + std::vector result_array(results_count); + float diff = t_max - t_min; + for (int i = 0; i < kShadingSteps; ++i) { + float input = diff * i / kShadingSteps + t_min; +- int offset = 0; ++ pdfium::span result_span = pdfium::make_span(result_array); + for (const auto& func : funcs) { +- if (func) { +- int nresults = 0; +- if (func->Call(&input, 1, &result_array[offset], &nresults)) +- offset += nresults; +- } ++ if (!func) ++ continue; ++ absl::optional nresults = ++ func->Call(pdfium::make_span(&input, 1), result_span); ++ if (nresults.has_value()) ++ result_span = result_span.subspan(nresults.value()); + } + float R = 0.0f; + float G = 0.0f; + float B = 0.0f; +- pCS->GetRGB(result_array.data(), &R, &G, &B); +- shading_steps[i] = +- FXARGB_TODIB(ArgbEncode(alpha, FXSYS_roundf(R * 255), +- FXSYS_roundf(G * 255), FXSYS_roundf(B * 255))); ++ pCS->GetRGB(result_array, &R, &G, &B); ++ shading_steps[i] = ArgbEncode(alpha, FXSYS_roundf(R * 255), ++ FXSYS_roundf(G * 255), FXSYS_roundf(B * 255)); + } + return shading_steps; + } +@@ -89,26 +97,26 @@ void DrawAxialShading(const RetainPtr& pBitmap, + const std::vector>& funcs, + const RetainPtr& pCS, + int alpha) { +- ASSERT(pBitmap->GetFormat() == FXDIB_Argb); ++ DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb); + + const uint32_t total_results = GetValidatedOutputsCount(funcs, pCS); + if (total_results == 0) + return; + +- const CPDF_Array* pCoords = pDict->GetArrayFor("Coords"); ++ RetainPtr pCoords = pDict->GetArrayFor("Coords"); + if (!pCoords) + return; + +- float start_x = pCoords->GetNumberAt(0); +- float start_y = pCoords->GetNumberAt(1); +- float end_x = pCoords->GetNumberAt(2); +- float end_y = pCoords->GetNumberAt(3); ++ float start_x = pCoords->GetFloatAt(0); ++ float start_y = pCoords->GetFloatAt(1); ++ float end_x = pCoords->GetFloatAt(2); ++ float end_y = pCoords->GetFloatAt(3); + float t_min = 0; + float t_max = 1.0f; +- const CPDF_Array* pArray = pDict->GetArrayFor("Domain"); ++ RetainPtr pArray = pDict->GetArrayFor("Domain"); + if (pArray) { +- t_min = pArray->GetNumberAt(0); +- t_max = pArray->GetNumberAt(1); ++ t_min = pArray->GetFloatAt(0); ++ t_max = pArray->GetFloatAt(1); + } + pArray = pDict->GetArrayFor("Extend"); + const bool bStartExtend = pArray && pArray->GetBooleanAt(0, false); +@@ -123,18 +131,17 @@ void DrawAxialShading(const RetainPtr& pBitmap, + std::array shading_steps = + GetShadingSteps(t_min, t_max, funcs, pCS, alpha, total_results); + +- int pitch = pBitmap->GetPitch(); + CFX_Matrix matrix = mtObject2Bitmap.GetInverse(); + for (int row = 0; row < height; row++) { + uint32_t* dib_buf = +- reinterpret_cast(pBitmap->GetBuffer() + row * pitch); ++ reinterpret_cast(pBitmap->GetWritableScanline(row).data()); + for (int column = 0; column < width; column++) { + CFX_PointF pos = matrix.Transform( + CFX_PointF(static_cast(column), static_cast(row))); + float scale = + (((pos.x - start_x) * x_span) + ((pos.y - start_y) * y_span)) / + axis_len_square; +- int index = (int32_t)(scale * (kShadingSteps - 1)); ++ int index = static_cast(scale * (kShadingSteps - 1)); + if (index < 0) { + if (!bStartExtend) + continue; +@@ -157,28 +164,28 @@ void DrawRadialShading(const RetainPtr& pBitmap, + const std::vector>& funcs, + const RetainPtr& pCS, + int alpha) { +- ASSERT(pBitmap->GetFormat() == FXDIB_Argb); ++ DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb); + + const uint32_t total_results = GetValidatedOutputsCount(funcs, pCS); + if (total_results == 0) + return; + +- const CPDF_Array* pCoords = pDict->GetArrayFor("Coords"); ++ RetainPtr pCoords = pDict->GetArrayFor("Coords"); + if (!pCoords) + return; + +- float start_x = pCoords->GetNumberAt(0); +- float start_y = pCoords->GetNumberAt(1); +- float start_r = pCoords->GetNumberAt(2); +- float end_x = pCoords->GetNumberAt(3); +- float end_y = pCoords->GetNumberAt(4); +- float end_r = pCoords->GetNumberAt(5); ++ float start_x = pCoords->GetFloatAt(0); ++ float start_y = pCoords->GetFloatAt(1); ++ float start_r = pCoords->GetFloatAt(2); ++ float end_x = pCoords->GetFloatAt(3); ++ float end_y = pCoords->GetFloatAt(4); ++ float end_r = pCoords->GetFloatAt(5); + float t_min = 0; + float t_max = 1.0f; +- const CPDF_Array* pArray = pDict->GetArrayFor("Domain"); ++ RetainPtr pArray = pDict->GetArrayFor("Domain"); + if (pArray) { +- t_min = pArray->GetNumberAt(0); +- t_max = pArray->GetNumberAt(1); ++ t_min = pArray->GetFloatAt(0); ++ t_max = pArray->GetFloatAt(1); + } + pArray = pDict->GetArrayFor("Extend"); + const bool bStartExtend = pArray && pArray->GetBooleanAt(0, false); +@@ -191,19 +198,16 @@ void DrawRadialShading(const RetainPtr& pBitmap, + const float dy = end_y - start_y; + const float dr = end_r - start_r; + const float a = dx * dx + dy * dy - dr * dr; +- const bool a_is_float_zero = IsFloatZero(a); ++ const bool a_is_float_zero = FXSYS_IsFloatZero(a); + + int width = pBitmap->GetWidth(); + int height = pBitmap->GetHeight(); +- int pitch = pBitmap->GetPitch(); +- +- bool bDecreasing = +- (dr < 0 && static_cast(sqrt(dx * dx + dy * dy)) < -dr); ++ bool bDecreasing = dr < 0 && static_cast(FXSYS_sqrt2(dx, dy)) < -dr; + + CFX_Matrix matrix = mtObject2Bitmap.GetInverse(); + for (int row = 0; row < height; row++) { + uint32_t* dib_buf = +- reinterpret_cast(pBitmap->GetBuffer() + row * pitch); ++ reinterpret_cast(pBitmap->GetWritableScanline(row).data()); + for (int column = 0; column < width; column++) { + CFX_PointF pos = matrix.Transform( + CFX_PointF(static_cast(column), static_cast(row))); +@@ -212,7 +216,7 @@ void DrawRadialShading(const RetainPtr& pBitmap, + float b = -2 * (pos_dx * dx + pos_dy * dy + start_r * dr); + float c = pos_dx * pos_dx + pos_dy * pos_dy - start_r * start_r; + float s; +- if (IsFloatZero(b)) { ++ if (FXSYS_IsFloatZero(b)) { + s = sqrt(-c / a); + } else if (a_is_float_zero) { + s = -c / b; +@@ -256,57 +260,57 @@ void DrawFuncShading(const RetainPtr& pBitmap, + const std::vector>& funcs, + const RetainPtr& pCS, + int alpha) { +- ASSERT(pBitmap->GetFormat() == FXDIB_Argb); ++ DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb); + + const uint32_t total_results = GetValidatedOutputsCount(funcs, pCS); + if (total_results == 0) + return; + +- const CPDF_Array* pDomain = pDict->GetArrayFor("Domain"); ++ RetainPtr pDomain = pDict->GetArrayFor("Domain"); + float xmin = 0.0f; + float ymin = 0.0f; + float xmax = 1.0f; + float ymax = 1.0f; + if (pDomain) { +- xmin = pDomain->GetNumberAt(0); +- xmax = pDomain->GetNumberAt(1); +- ymin = pDomain->GetNumberAt(2); +- ymax = pDomain->GetNumberAt(3); ++ xmin = pDomain->GetFloatAt(0); ++ xmax = pDomain->GetFloatAt(1); ++ ymin = pDomain->GetFloatAt(2); ++ ymax = pDomain->GetFloatAt(3); + } + CFX_Matrix mtDomain2Target = pDict->GetMatrixFor("Matrix"); + CFX_Matrix matrix = + mtObject2Bitmap.GetInverse() * mtDomain2Target.GetInverse(); + int width = pBitmap->GetWidth(); + int height = pBitmap->GetHeight(); +- int pitch = pBitmap->GetPitch(); + +- ASSERT(total_results >= CountOutputsFromFunctions(funcs)); +- ASSERT(total_results >= pCS->CountComponents()); ++ DCHECK(total_results >= CountOutputsFromFunctions(funcs)); ++ DCHECK(total_results >= pCS->CountComponents()); + std::vector result_array(total_results); + for (int row = 0; row < height; ++row) { +- uint32_t* dib_buf = (uint32_t*)(pBitmap->GetBuffer() + row * pitch); ++ uint32_t* dib_buf = ++ reinterpret_cast(pBitmap->GetWritableScanline(row).data()); + for (int column = 0; column < width; column++) { + CFX_PointF pos = matrix.Transform( + CFX_PointF(static_cast(column), static_cast(row))); + if (pos.x < xmin || pos.x > xmax || pos.y < ymin || pos.y > ymax) + continue; + +- float input[] = {pos.x, pos.y}; +- int offset = 0; ++ float input[2] = {pos.x, pos.y}; ++ pdfium::span result_span = pdfium::make_span(result_array); + for (const auto& func : funcs) { +- if (func) { +- int nresults; +- if (func->Call(input, 2, &result_array[offset], &nresults)) +- offset += nresults; +- } ++ if (!func) ++ continue; ++ absl::optional nresults = func->Call(input, result_span); ++ if (nresults.has_value()) ++ result_span = result_span.subspan(nresults.value()); + } +- + float R = 0.0f; + float G = 0.0f; + float B = 0.0f; +- pCS->GetRGB(result_array.data(), &R, &G, &B); +- dib_buf[column] = FXARGB_TODIB(ArgbEncode( +- alpha, (int32_t)(R * 255), (int32_t)(G * 255), (int32_t)(B * 255))); ++ pCS->GetRGB(result_array, &R, &G, &B); ++ dib_buf[column] = ArgbEncode(alpha, static_cast(R * 255), ++ static_cast(G * 255), ++ static_cast(B * 255)); + } + } + } +@@ -340,9 +344,8 @@ void DrawGouraud(const RetainPtr& pBitmap, + if (min_y == max_y) + return; + +- int min_yi = std::max(static_cast(floor(min_y)), 0); +- int max_yi = static_cast(ceil(max_y)); +- ++ int min_yi = std::max(static_cast(floorf(min_y)), 0); ++ int max_yi = static_cast(ceilf(max_y)); + if (max_yi >= pBitmap->GetHeight()) + max_yi = pBitmap->GetHeight() - 1; + +@@ -371,40 +374,42 @@ void DrawGouraud(const RetainPtr& pBitmap, + if (nIntersects != 2) + continue; + +- int min_x, max_x, start_index, end_index; ++ int min_x; ++ int max_x; ++ int start_index; ++ int end_index; + if (inter_x[0] < inter_x[1]) { +- min_x = (int)floor(inter_x[0]); +- max_x = (int)ceil(inter_x[1]); ++ min_x = static_cast(floorf(inter_x[0])); ++ max_x = static_cast(ceilf(inter_x[1])); + start_index = 0; + end_index = 1; + } else { +- min_x = (int)floor(inter_x[1]); +- max_x = (int)ceil(inter_x[0]); ++ min_x = static_cast(floorf(inter_x[1])); ++ max_x = static_cast(ceilf(inter_x[0])); + start_index = 1; + end_index = 0; + } + +- int start_x = std::max(min_x, 0); +- int end_x = max_x; +- if (end_x > pBitmap->GetWidth()) +- end_x = pBitmap->GetWidth(); +- +- uint8_t* dib_buf = +- pBitmap->GetBuffer() + y * pBitmap->GetPitch() + start_x * 4; ++ int start_x = pdfium::clamp(min_x, 0, pBitmap->GetWidth()); ++ int end_x = pdfium::clamp(max_x, 0, pBitmap->GetWidth()); + float r_unit = (r[end_index] - r[start_index]) / (max_x - min_x); + float g_unit = (g[end_index] - g[start_index]) / (max_x - min_x); + float b_unit = (b[end_index] - b[start_index]) / (max_x - min_x); +- float R = r[start_index] + (start_x - min_x) * r_unit; +- float G = g[start_index] + (start_x - min_x) * g_unit; +- float B = b[start_index] + (start_x - min_x) * b_unit; ++ float r_result = r[start_index] + (start_x - min_x) * r_unit; ++ float g_result = g[start_index] + (start_x - min_x) * g_unit; ++ float b_result = b[start_index] + (start_x - min_x) * b_unit; ++ pdfium::span dib_span = ++ pBitmap->GetWritableScanline(y).subspan(start_x * 4); ++ + for (int x = start_x; x < end_x; x++) { +- R += r_unit; +- G += g_unit; +- B += b_unit; +- FXARGB_SETDIB(dib_buf, +- ArgbEncode(alpha, (int32_t)(R * 255), (int32_t)(G * 255), +- (int32_t)(B * 255))); +- dib_buf += 4; ++ uint8_t* dib_buf = dib_span.data(); ++ r_result += r_unit; ++ g_result += g_unit; ++ b_result += b_unit; ++ FXARGB_SETDIB(dib_buf, ArgbEncode(alpha, static_cast(r_result * 255), ++ static_cast(g_result * 255), ++ static_cast(b_result * 255))); ++ dib_span = dib_span.subspan(4); + } + } + } +@@ -412,21 +417,19 @@ void DrawGouraud(const RetainPtr& pBitmap, + void DrawFreeGouraudShading( + const RetainPtr& pBitmap, + const CFX_Matrix& mtObject2Bitmap, +- const CPDF_Stream* pShadingStream, ++ RetainPtr pShadingStream, + const std::vector>& funcs, +- const RetainPtr& pCS, ++ RetainPtr pCS, + int alpha) { +- ASSERT(pBitmap->GetFormat() == FXDIB_Argb); ++ DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb); + + CPDF_MeshStream stream(kFreeFormGouraudTriangleMeshShading, funcs, +- pShadingStream, pCS); ++ std::move(pShadingStream), std::move(pCS)); + if (!stream.Load()) + return; + + CPDF_MeshVertex triangle[3]; +- memset(triangle, 0, sizeof(triangle)); +- +- while (!stream.BitStream()->IsEOF()) { ++ while (!stream.IsEOF()) { + CPDF_MeshVertex vertex; + uint32_t flag; + if (!stream.ReadVertex(mtObject2Bitmap, &vertex, &flag)) +@@ -434,9 +437,9 @@ void DrawFreeGouraudShading( + + if (flag == 0) { + triangle[0] = vertex; +- for (int j = 1; j < 3; j++) { +- uint32_t tflag; +- if (!stream.ReadVertex(mtObject2Bitmap, &triangle[j], &tflag)) ++ for (int i = 1; i < 3; ++i) { ++ uint32_t dummy_flag; ++ if (!stream.ReadVertex(mtObject2Bitmap, &triangle[i], &dummy_flag)) + return; + } + } else { +@@ -453,18 +456,18 @@ void DrawFreeGouraudShading( + void DrawLatticeGouraudShading( + const RetainPtr& pBitmap, + const CFX_Matrix& mtObject2Bitmap, +- const CPDF_Stream* pShadingStream, ++ RetainPtr pShadingStream, + const std::vector>& funcs, +- const RetainPtr& pCS, ++ RetainPtr pCS, + int alpha) { +- ASSERT(pBitmap->GetFormat() == FXDIB_Argb); ++ DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb); + + int row_verts = pShadingStream->GetDict()->GetIntegerFor("VerticesPerRow"); + if (row_verts < 2) + return; + + CPDF_MeshStream stream(kLatticeFormGouraudTriangleMeshShading, funcs, +- pShadingStream, pCS); ++ std::move(pShadingStream), std::move(pCS)); + if (!stream.Load()) + return; + +@@ -474,7 +477,7 @@ void DrawLatticeGouraudShading( + return; + + int last_index = 0; +- while (1) { ++ while (true) { + vertices[1 - last_index] = stream.ReadVertexRow(mtObject2Bitmap, row_verts); + if (vertices[1 - last_index].empty()) + return; +@@ -492,123 +495,127 @@ void DrawLatticeGouraudShading( + } + } + +-struct Coon_BezierCoeff { +- float a, b, c, d; +- void FromPoints(float p0, float p1, float p2, float p3) { ++struct CoonBezierCoeff { ++ void InitFromPoints(float p0, float p1, float p2, float p3) { + a = -p0 + 3 * p1 - 3 * p2 + p3; + b = 3 * p0 - 6 * p1 + 3 * p2; + c = -3 * p0 + 3 * p1; + d = p0; + } +- Coon_BezierCoeff first_half() { +- Coon_BezierCoeff result; ++ ++ void InitFromBezierInterpolation(const CoonBezierCoeff& C1, ++ const CoonBezierCoeff& C2, ++ const CoonBezierCoeff& D1, ++ const CoonBezierCoeff& D2) { ++ a = (D1.a + D2.a) / 2; ++ b = (D1.b + D2.b) / 2; ++ c = (D1.c + D2.c) / 2 - (C1.a / 8 + C1.b / 4 + C1.c / 2) + ++ (C2.a / 8 + C2.b / 4) + (-C1.d + D2.d) / 2 - (C2.a + C2.b) / 2; ++ d = C1.a / 8 + C1.b / 4 + C1.c / 2 + C1.d; ++ } ++ ++ CoonBezierCoeff first_half() const { ++ CoonBezierCoeff result; + result.a = a / 8; + result.b = b / 4; + result.c = c / 2; + result.d = d; + return result; + } +- Coon_BezierCoeff second_half() { +- Coon_BezierCoeff result; ++ ++ CoonBezierCoeff second_half() const { ++ CoonBezierCoeff result; + result.a = a / 8; + result.b = 3 * a / 8 + b / 4; + result.c = 3 * a / 8 + b / 2 + c / 2; + result.d = a / 8 + b / 4 + c / 2 + d; + return result; + } +- void GetPoints(float p[4]) { ++ ++ void GetPoints(float p[4]) const { + p[0] = d; + p[1] = c / 3 + p[0]; + p[2] = b / 3 - p[0] + 2 * p[1]; + p[3] = a + p[0] - 3 * p[1] + 3 * p[2]; + } +- void GetPointsReverse(float p[4]) { +- p[3] = d; +- p[2] = c / 3 + p[3]; +- p[1] = b / 3 - p[3] + 2 * p[2]; +- p[0] = a + p[3] - 3 * p[2] + 3 * p[1]; +- } +- void BezierInterpol(Coon_BezierCoeff& C1, +- Coon_BezierCoeff& C2, +- Coon_BezierCoeff& D1, +- Coon_BezierCoeff& D2) { +- a = (D1.a + D2.a) / 2; +- b = (D1.b + D2.b) / 2; +- c = (D1.c + D2.c) / 2 - (C1.a / 8 + C1.b / 4 + C1.c / 2) + +- (C2.a / 8 + C2.b / 4) + (-C1.d + D2.d) / 2 - (C2.a + C2.b) / 2; +- d = C1.a / 8 + C1.b / 4 + C1.c / 2 + C1.d; +- } +- float Distance() { ++ ++ float Distance() const { + float dis = a + b + c; + return dis < 0 ? -dis : dis; + } ++ ++ float a; ++ float b; ++ float c; ++ float d; + }; + +-struct Coon_Bezier { +- Coon_BezierCoeff x, y; +- void FromPoints(float x0, +- float y0, +- float x1, +- float y1, +- float x2, +- float y2, +- float x3, +- float y3) { +- x.FromPoints(x0, x1, x2, x3); +- y.FromPoints(y0, y1, y2, y3); +- } +- +- Coon_Bezier first_half() { +- Coon_Bezier result; ++struct CoonBezier { ++ void InitFromPoints(float x0, ++ float y0, ++ float x1, ++ float y1, ++ float x2, ++ float y2, ++ float x3, ++ float y3) { ++ x.InitFromPoints(x0, x1, x2, x3); ++ y.InitFromPoints(y0, y1, y2, y3); ++ } ++ ++ void InitFromBezierInterpolation(const CoonBezier& C1, ++ const CoonBezier& C2, ++ const CoonBezier& D1, ++ const CoonBezier& D2) { ++ x.InitFromBezierInterpolation(C1.x, C2.x, D1.x, D2.x); ++ y.InitFromBezierInterpolation(C1.y, C2.y, D1.y, D2.y); ++ } ++ ++ CoonBezier first_half() const { ++ CoonBezier result; + result.x = x.first_half(); + result.y = y.first_half(); + return result; + } + +- Coon_Bezier second_half() { +- Coon_Bezier result; ++ CoonBezier second_half() const { ++ CoonBezier result; + result.x = x.second_half(); + result.y = y.second_half(); + return result; + } + +- void BezierInterpol(Coon_Bezier& C1, +- Coon_Bezier& C2, +- Coon_Bezier& D1, +- Coon_Bezier& D2) { +- x.BezierInterpol(C1.x, C2.x, D1.x, D2.x); +- y.BezierInterpol(C1.y, C2.y, D1.y, D2.y); +- } +- +- void GetPoints(std::vector& pPoints, size_t start_idx) { +- float p[4]; +- int i; +- x.GetPoints(p); +- for (i = 0; i < 4; i++) +- pPoints[start_idx + i].m_Point.x = p[i]; +- +- y.GetPoints(p); +- for (i = 0; i < 4; i++) +- pPoints[start_idx + i].m_Point.y = p[i]; ++ void GetPoints(pdfium::span path_points) const { ++ constexpr size_t kPointsCount = 4; ++ float points_x[kPointsCount]; ++ float points_y[kPointsCount]; ++ x.GetPoints(points_x); ++ y.GetPoints(points_y); ++ for (size_t i = 0; i < kPointsCount; ++i) ++ path_points[i].m_Point = {points_x[i], points_y[i]}; ++ } ++ ++ void GetPointsReverse(pdfium::span path_points) const { ++ constexpr size_t kPointsCount = 4; ++ float points_x[kPointsCount]; ++ float points_y[kPointsCount]; ++ x.GetPoints(points_x); ++ y.GetPoints(points_y); ++ for (size_t i = 0; i < kPointsCount; ++i) { ++ size_t reverse_index = kPointsCount - i - 1; ++ path_points[i].m_Point = {points_x[reverse_index], ++ points_y[reverse_index]}; ++ } + } + +- void GetPointsReverse(std::vector& pPoints, size_t start_idx) { +- float p[4]; +- int i; +- x.GetPointsReverse(p); +- for (i = 0; i < 4; i++) +- pPoints[i + start_idx].m_Point.x = p[i]; ++ float Distance() const { return x.Distance() + y.Distance(); } + +- y.GetPointsReverse(p); +- for (i = 0; i < 4; i++) +- pPoints[i + start_idx].m_Point.y = p[i]; +- } +- +- float Distance() { return x.Distance() + y.Distance(); } ++ CoonBezierCoeff x; ++ CoonBezierCoeff y; + }; + + int Interpolate(int p1, int p2, int delta1, int delta2, bool* overflow) { +- pdfium::base::CheckedNumeric p = p2; ++ FX_SAFE_INT32 p = p2; + p -= p1; + p *= delta1; + p /= delta2; +@@ -632,15 +639,11 @@ int BiInterpolImpl(int c0, + return Interpolate(x1, x2, y, y_scale, overflow); + } + +-struct Coon_Color { +- Coon_Color() { memset(comp, 0, sizeof(int) * 3); } ++struct CoonColor { ++ CoonColor() = default; + + // Returns true if successful, false if overflow detected. +- bool BiInterpol(Coon_Color colors[4], +- int x, +- int y, +- int x_scale, +- int y_scale) { ++ bool BiInterpol(CoonColor colors[4], int x, int y, int x_scale, int y_scale) { + bool overflow = false; + for (int i = 0; i < 3; i++) { + comp[i] = BiInterpolImpl(colors[0].comp[i], colors[1].comp[i], +@@ -650,27 +653,28 @@ struct Coon_Color { + return !overflow; + } + +- int Distance(Coon_Color& o) { ++ int Distance(const CoonColor& o) const { + return std::max({abs(comp[0] - o.comp[0]), abs(comp[1] - o.comp[1]), + abs(comp[2] - o.comp[2])}); + } + +- int comp[3]; ++ int comp[3] = {}; + }; + +-#define COONCOLOR_THRESHOLD 4 +-struct CPDF_PatchDrawer { ++struct PatchDrawer { ++ static constexpr int kCoonColorThreshold = 4; ++ + void Draw(int x_scale, + int y_scale, + int left, + int bottom, +- Coon_Bezier C1, +- Coon_Bezier C2, +- Coon_Bezier D1, +- Coon_Bezier D2) { ++ CoonBezier C1, ++ CoonBezier C2, ++ CoonBezier D1, ++ CoonBezier D2) { + bool bSmall = C1.Distance() < 2 && C2.Distance() < 2 && D1.Distance() < 2 && + D2.Distance() < 2; +- Coon_Color div_colors[4]; ++ CoonColor div_colors[4]; + int d_bottom = 0; + int d_left = 0; + int d_top = 0; +@@ -699,35 +703,37 @@ struct CPDF_PatchDrawer { + } + + if (bSmall || +- (d_bottom < COONCOLOR_THRESHOLD && d_left < COONCOLOR_THRESHOLD && +- d_top < COONCOLOR_THRESHOLD && d_right < COONCOLOR_THRESHOLD)) { +- std::vector& pPoints = path.GetPoints(); +- C1.GetPoints(pPoints, 0); +- D2.GetPoints(pPoints, 3); +- C2.GetPointsReverse(pPoints, 6); +- D1.GetPointsReverse(pPoints, 9); +- int fillFlags = FXFILL_WINDING | FXFILL_FULLCOVER; ++ (d_bottom < kCoonColorThreshold && d_left < kCoonColorThreshold && ++ d_top < kCoonColorThreshold && d_right < kCoonColorThreshold)) { ++ pdfium::span points = path.GetPoints(); ++ C1.GetPoints(points.subspan(0, 4)); ++ D2.GetPoints(points.subspan(3, 4)); ++ C2.GetPointsReverse(points.subspan(6, 4)); ++ D1.GetPointsReverse(points.subspan(9, 4)); ++ CFX_FillRenderOptions fill_options( ++ CFX_FillRenderOptions::WindingOptions()); ++ fill_options.full_cover = true; + if (bNoPathSmooth) +- fillFlags |= FXFILL_NOPATHSMOOTH; ++ fill_options.aliased_path = true; + pDevice->DrawPath( +- &path, nullptr, nullptr, ++ path, nullptr, nullptr, + ArgbEncode(alpha, div_colors[0].comp[0], div_colors[0].comp[1], + div_colors[0].comp[2]), +- 0, fillFlags); ++ 0, fill_options); + } else { +- if (d_bottom < COONCOLOR_THRESHOLD && d_top < COONCOLOR_THRESHOLD) { +- Coon_Bezier m1; +- m1.BezierInterpol(D1, D2, C1, C2); ++ if (d_bottom < kCoonColorThreshold && d_top < kCoonColorThreshold) { ++ CoonBezier m1; ++ m1.InitFromBezierInterpolation(D1, D2, C1, C2); + y_scale *= 2; + bottom *= 2; + Draw(x_scale, y_scale, left, bottom, C1, m1, D1.first_half(), + D2.first_half()); + Draw(x_scale, y_scale, left, bottom + 1, m1, C2, D1.second_half(), + D2.second_half()); +- } else if (d_left < COONCOLOR_THRESHOLD && +- d_right < COONCOLOR_THRESHOLD) { +- Coon_Bezier m2; +- m2.BezierInterpol(C1, C2, D1, D2); ++ } else if (d_left < kCoonColorThreshold && ++ d_right < kCoonColorThreshold) { ++ CoonBezier m2; ++ m2.InitFromBezierInterpolation(C1, C2, D1, D2); + x_scale *= 2; + left *= 2; + Draw(x_scale, y_scale, left, bottom, C1.first_half(), C2.first_half(), +@@ -735,13 +741,14 @@ struct CPDF_PatchDrawer { + Draw(x_scale, y_scale, left + 1, bottom, C1.second_half(), + C2.second_half(), m2, D2); + } else { +- Coon_Bezier m1, m2; +- m1.BezierInterpol(D1, D2, C1, C2); +- m2.BezierInterpol(C1, C2, D1, D2); +- Coon_Bezier m1f = m1.first_half(); +- Coon_Bezier m1s = m1.second_half(); +- Coon_Bezier m2f = m2.first_half(); +- Coon_Bezier m2s = m2.second_half(); ++ CoonBezier m1; ++ CoonBezier m2; ++ m1.InitFromBezierInterpolation(D1, D2, C1, C2); ++ m2.InitFromBezierInterpolation(C1, C2, D1, D2); ++ CoonBezier m1f = m1.first_half(); ++ CoonBezier m1s = m1.second_half(); ++ CoonBezier m2f = m2.first_half(); ++ CoonBezier m2s = m2.second_half(); + x_scale *= 2; + y_scale *= 2; + left *= 2; +@@ -759,49 +766,54 @@ struct CPDF_PatchDrawer { + } + + int max_delta; +- CFX_PathData path; ++ CFX_Path path; + CFX_RenderDevice* pDevice; + int bNoPathSmooth; + int alpha; +- Coon_Color patch_colors[4]; ++ CoonColor patch_colors[4]; + }; + + void DrawCoonPatchMeshes( + ShadingType type, + const RetainPtr& pBitmap, + const CFX_Matrix& mtObject2Bitmap, +- const CPDF_Stream* pShadingStream, ++ RetainPtr pShadingStream, + const std::vector>& funcs, +- const RetainPtr& pCS, ++ RetainPtr pCS, + bool bNoPathSmooth, + int alpha) { +- ASSERT(pBitmap->GetFormat() == FXDIB_Argb); +- ASSERT(type == kCoonsPatchMeshShading || ++ DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb); ++ DCHECK(type == kCoonsPatchMeshShading || + type == kTensorProductPatchMeshShading); + + CFX_DefaultRenderDevice device; +- device.Attach(pBitmap, false, nullptr, false); +- CPDF_MeshStream stream(type, funcs, pShadingStream, pCS); ++ device.Attach(pBitmap); ++ ++ CPDF_MeshStream stream(type, funcs, std::move(pShadingStream), ++ std::move(pCS)); + if (!stream.Load()) + return; + +- CPDF_PatchDrawer patch; ++ PatchDrawer patch; + patch.alpha = alpha; + patch.pDevice = &device; + patch.bNoPathSmooth = bNoPathSmooth; + + for (int i = 0; i < 13; i++) { +- patch.path.AppendPoint( +- CFX_PointF(), i == 0 ? FXPT_TYPE::MoveTo : FXPT_TYPE::BezierTo, false); ++ patch.path.AppendPoint(CFX_PointF(), i == 0 ++ ? CFX_Path::Point::Type::kMove ++ : CFX_Path::Point::Type::kBezier); + } + + CFX_PointF coords[16]; + int point_count = type == kTensorProductPatchMeshShading ? 16 : 12; +- while (!stream.BitStream()->IsEOF()) { ++ while (!stream.IsEOF()) { + if (!stream.CanReadFlag()) + break; + uint32_t flag = stream.ReadFlag(); +- int iStartPoint = 0, iStartColor = 0, i = 0; ++ int iStartPoint = 0; ++ int iStartColor = 0; ++ int i = 0; + if (flag) { + iStartPoint = 4; + iStartColor = 2; +@@ -809,11 +821,12 @@ void DrawCoonPatchMeshes( + for (i = 0; i < 4; i++) { + tempCoords[i] = coords[(flag * 3 + i) % 12]; + } +- memcpy(coords, tempCoords, sizeof(tempCoords)); +- Coon_Color tempColors[2]; +- tempColors[0] = patch.patch_colors[flag]; +- tempColors[1] = patch.patch_colors[(flag + 1) % 4]; +- memcpy(patch.patch_colors, tempColors, sizeof(Coon_Color) * 2); ++ fxcrt::spancpy(pdfium::make_span(coords), pdfium::make_span(tempCoords)); ++ CoonColor tempColors[2] = { ++ tempColors[0] = patch.patch_colors[flag], ++ tempColors[1] = patch.patch_colors[(flag + 1) % 4]}; ++ fxcrt::spancpy(pdfium::make_span(patch.patch_colors), ++ pdfium::make_span(tempColors)); + } + for (i = iStartPoint; i < point_count; i++) { + if (!stream.CanReadCoords()) +@@ -830,24 +843,28 @@ void DrawCoonPatchMeshes( + float b; + std::tie(r, g, b) = stream.ReadColor(); + +- patch.patch_colors[i].comp[0] = (int32_t)(r * 255); +- patch.patch_colors[i].comp[1] = (int32_t)(g * 255); +- patch.patch_colors[i].comp[2] = (int32_t)(b * 255); ++ patch.patch_colors[i].comp[0] = static_cast(r * 255); ++ patch.patch_colors[i].comp[1] = static_cast(g * 255); ++ patch.patch_colors[i].comp[2] = static_cast(b * 255); + } +- CFX_FloatRect bbox = CFX_FloatRect::GetBBox(coords, point_count); ++ CFX_FloatRect bbox = ++ CFX_FloatRect::GetBBox(pdfium::make_span(coords).first(point_count)); + if (bbox.right <= 0 || bbox.left >= (float)pBitmap->GetWidth() || + bbox.top <= 0 || bbox.bottom >= (float)pBitmap->GetHeight()) { + continue; + } +- Coon_Bezier C1, C2, D1, D2; +- C1.FromPoints(coords[0].x, coords[0].y, coords[11].x, coords[11].y, +- coords[10].x, coords[10].y, coords[9].x, coords[9].y); +- C2.FromPoints(coords[3].x, coords[3].y, coords[4].x, coords[4].y, +- coords[5].x, coords[5].y, coords[6].x, coords[6].y); +- D1.FromPoints(coords[0].x, coords[0].y, coords[1].x, coords[1].y, +- coords[2].x, coords[2].y, coords[3].x, coords[3].y); +- D2.FromPoints(coords[9].x, coords[9].y, coords[8].x, coords[8].y, +- coords[7].x, coords[7].y, coords[6].x, coords[6].y); ++ CoonBezier C1; ++ CoonBezier C2; ++ CoonBezier D1; ++ CoonBezier D2; ++ C1.InitFromPoints(coords[0].x, coords[0].y, coords[11].x, coords[11].y, ++ coords[10].x, coords[10].y, coords[9].x, coords[9].y); ++ C2.InitFromPoints(coords[3].x, coords[3].y, coords[4].x, coords[4].y, ++ coords[5].x, coords[5].y, coords[6].x, coords[6].y); ++ D1.InitFromPoints(coords[0].x, coords[0].y, coords[1].x, coords[1].y, ++ coords[2].x, coords[2].y, coords[3].x, coords[3].y); ++ D2.InitFromPoints(coords[9].x, coords[9].y, coords[8].x, coords[8].y, ++ coords[7].x, coords[7].y, coords[6].x, coords[6].y); + patch.Draw(1, 1, 0, 0, C1, C2, D1, D2); + } + } +@@ -863,25 +880,26 @@ void CPDF_RenderShading::Draw(CFX_RenderDevice* pDevice, + const FX_RECT& clip_rect, + int alpha, + const CPDF_RenderOptions& options) { +- const auto& funcs = pPattern->GetFuncs(); +- const CPDF_Dictionary* pDict = pPattern->GetShadingObject()->GetDict(); + RetainPtr pColorSpace = pPattern->GetCS(); + if (!pColorSpace) + return; + + FX_ARGB background = 0; ++ RetainPtr pDict = ++ pPattern->GetShadingObject()->GetDict(); + if (!pPattern->IsShadingObject() && pDict->KeyExist("Background")) { +- const CPDF_Array* pBackColor = pDict->GetArrayFor("Background"); ++ RetainPtr pBackColor = pDict->GetArrayFor("Background"); + if (pBackColor && pBackColor->size() >= pColorSpace->CountComponents()) { +- std::vector comps = +- ReadArrayElementsToVector(pBackColor, pColorSpace->CountComponents()); ++ std::vector comps = ReadArrayElementsToVector( ++ pBackColor.Get(), pColorSpace->CountComponents()); + + float R = 0.0f; + float G = 0.0f; + float B = 0.0f; +- pColorSpace->GetRGB(comps.data(), &R, &G, &B); +- background = ArgbEncode(255, (int32_t)(R * 255), (int32_t)(G * 255), +- (int32_t)(B * 255)); ++ pColorSpace->GetRGB(comps, &R, &G, &B); ++ background = ArgbEncode(255, static_cast(R * 255), ++ static_cast(G * 255), ++ static_cast(B * 255)); + } + } + FX_RECT clip_rect_bbox = clip_rect; +@@ -891,64 +909,80 @@ void CPDF_RenderShading::Draw(CFX_RenderDevice* pDevice, + } + bool bAlphaMode = options.ColorModeIs(CPDF_RenderOptions::kAlpha); + if (pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SHADING && +- pDevice->GetDeviceDriver()->DrawShading( +- pPattern, &mtMatrix, clip_rect_bbox, alpha, bAlphaMode)) { ++ pDevice->DrawShading(pPattern, &mtMatrix, clip_rect_bbox, alpha, ++ bAlphaMode)) { + return; + } + CPDF_DeviceBuffer buffer(pContext, pDevice, clip_rect_bbox, pCurObj, 150); + if (!buffer.Initialize()) + return; + +- CFX_Matrix FinalMatrix = mtMatrix * buffer.GetMatrix(); + RetainPtr pBitmap = buffer.GetBitmap(); +- if (!pBitmap->GetBuffer()) ++ if (pBitmap->GetBuffer().empty()) + return; + +- pBitmap->Clear(background); ++ if (background != 0) { ++ pBitmap->Clear(background); ++ } ++ const CFX_Matrix final_matrix = mtMatrix * buffer.GetMatrix(); ++ const auto& funcs = pPattern->GetFuncs(); + switch (pPattern->GetShadingType()) { + case kInvalidShading: + case kMaxShading: + return; + case kFunctionBasedShading: +- DrawFuncShading(pBitmap, FinalMatrix, pDict, funcs, pColorSpace, alpha); ++ DrawFuncShading(pBitmap, final_matrix, pDict.Get(), funcs, pColorSpace, ++ alpha); + break; + case kAxialShading: +- DrawAxialShading(pBitmap, FinalMatrix, pDict, funcs, pColorSpace, alpha); ++ DrawAxialShading(pBitmap, final_matrix, pDict.Get(), funcs, pColorSpace, ++ alpha); + break; + case kRadialShading: +- DrawRadialShading(pBitmap, FinalMatrix, pDict, funcs, pColorSpace, alpha); ++ DrawRadialShading(pBitmap, final_matrix, pDict.Get(), funcs, pColorSpace, ++ alpha); + break; + case kFreeFormGouraudTriangleMeshShading: { + // The shading object can be a stream or a dictionary. We do not handle + // the case of dictionary at the moment. +- if (const CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject())) { +- DrawFreeGouraudShading(pBitmap, FinalMatrix, pStream, funcs, ++ RetainPtr pStream = ++ ToStream(pPattern->GetShadingObject()); ++ if (pStream) { ++ DrawFreeGouraudShading(pBitmap, final_matrix, std::move(pStream), funcs, + pColorSpace, alpha); + } +- } break; ++ break; ++ } + case kLatticeFormGouraudTriangleMeshShading: { + // The shading object can be a stream or a dictionary. We do not handle + // the case of dictionary at the moment. +- if (const CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject())) { +- DrawLatticeGouraudShading(pBitmap, FinalMatrix, pStream, funcs, +- pColorSpace, alpha); ++ RetainPtr pStream = ++ ToStream(pPattern->GetShadingObject()); ++ if (pStream) { ++ DrawLatticeGouraudShading(pBitmap, final_matrix, std::move(pStream), ++ funcs, pColorSpace, alpha); + } +- } break; ++ break; ++ } + case kCoonsPatchMeshShading: + case kTensorProductPatchMeshShading: { + // The shading object can be a stream or a dictionary. We do not handle + // the case of dictionary at the moment. +- if (const CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject())) { +- DrawCoonPatchMeshes(pPattern->GetShadingType(), pBitmap, FinalMatrix, +- pStream, funcs, pColorSpace, ++ RetainPtr pStream = ++ ToStream(pPattern->GetShadingObject()); ++ if (pStream) { ++ DrawCoonPatchMeshes(pPattern->GetShadingType(), pBitmap, final_matrix, ++ std::move(pStream), funcs, pColorSpace, + options.GetOptions().bNoPathSmooth, alpha); + } +- } break; ++ break; ++ } + } + if (bAlphaMode) +- pBitmap->LoadChannelFromAlpha(FXDIB_Red, pBitmap); ++ pBitmap->SetRedFromBitmap(pBitmap); + + if (options.ColorModeIs(CPDF_RenderOptions::kGray)) + pBitmap->ConvertColorScale(0, 0xffffff); ++ + buffer.OutputToDevice(); + } +diff --git a/core/fpdfapi/render/cpdf_rendershading.h b/core/fpdfapi/render/cpdf_rendershading.h +index 8c0d8a4b9..a8702c914 100644 +--- a/core/fpdfapi/render/cpdf_rendershading.h ++++ b/core/fpdfapi/render/cpdf_rendershading.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fpdfapi/render/cpdf_renderstatus.cpp b/core/fpdfapi/render/cpdf_renderstatus.cpp +index 79d31b95d..fe58de8d1 100644 +--- a/core/fpdfapi/render/cpdf_renderstatus.cpp ++++ b/core/fpdfapi/render/cpdf_renderstatus.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,9 +6,9 @@ + + #include "core/fpdfapi/render/cpdf_renderstatus.h" + ++#include ++ + #include +-#include +-#include + #include + #include + #include +@@ -29,6 +29,7 @@ + #include "core/fpdfapi/page/cpdf_imageobject.h" + #include "core/fpdfapi/page/cpdf_occontext.h" + #include "core/fpdfapi/page/cpdf_page.h" ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" + #include "core/fpdfapi/page/cpdf_pageobject.h" + #include "core/fpdfapi/page/cpdf_pathobject.h" + #include "core/fpdfapi/page/cpdf_shadingobject.h" +@@ -40,34 +41,38 @@ + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" +-#include "core/fpdfapi/render/cpdf_charposlist.h" ++#include "core/fpdfapi/render/charposlist.h" + #include "core/fpdfapi/render/cpdf_docrenderdata.h" + #include "core/fpdfapi/render/cpdf_imagerenderer.h" +-#include "core/fpdfapi/render/cpdf_pagerendercache.h" + #include "core/fpdfapi/render/cpdf_rendercontext.h" + #include "core/fpdfapi/render/cpdf_renderoptions.h" + #include "core/fpdfapi/render/cpdf_rendershading.h" ++#include "core/fpdfapi/render/cpdf_rendertiling.h" + #include "core/fpdfapi/render/cpdf_scaledrenderbuffer.h" + #include "core/fpdfapi/render/cpdf_textrenderer.h" + #include "core/fpdfapi/render/cpdf_type3cache.h" + #include "core/fxcrt/autorestorer.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_2d_size.h" + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/span_util.h" ++#include "core/fxcrt/unowned_ptr.h" + #include "core/fxge/cfx_defaultrenderdevice.h" ++#include "core/fxge/cfx_fillrenderoptions.h" + #include "core/fxge/cfx_glyphbitmap.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/dib/cfx_dibitmap.h" + #include "core/fxge/fx_font.h" + #include "core/fxge/renderdevicedriver_iface.h" + #include "core/fxge/text_char_pos.h" + #include "core/fxge/text_glyph_pos.h" +-#include "third_party/base/compiler_specific.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/numerics/safe_math.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/notreached.h" ++#include "third_party/base/span.h" + +-#ifdef _SKIA_SUPPORT_ ++#if defined(_SKIA_SUPPORT_) + #include "core/fxge/skia/fx_skia_device.h" + #endif + +@@ -76,46 +81,55 @@ namespace { + constexpr int kRenderMaxRecursionDepth = 64; + int g_CurrentRecursionDepth = 0; + +-RetainPtr DrawPatternBitmap( +- CPDF_Document* pDoc, +- CPDF_PageRenderCache* pCache, +- CPDF_TilingPattern* pPattern, +- CPDF_Form* pPatternForm, +- const CFX_Matrix& mtObject2Device, +- int width, +- int height, +- const CPDF_RenderOptions::Options& draw_options) { +- auto pBitmap = pdfium::MakeRetain(); +- if (!pBitmap->Create(width, height, +- pPattern->colored() ? FXDIB_Argb : FXDIB_8bppMask)) { +- return nullptr; ++CFX_FillRenderOptions GetFillOptionsForDrawPathWithBlend( ++ const CPDF_RenderOptions::Options& options, ++ const CPDF_PathObject* path_obj, ++ CFX_FillRenderOptions::FillType fill_type, ++ bool is_stroke, ++ bool is_type3_char) { ++ CFX_FillRenderOptions fill_options(fill_type); ++ if (fill_type != CFX_FillRenderOptions::FillType::kNoFill && options.bRectAA) ++ fill_options.rect_aa = true; ++ if (options.bNoPathSmooth) ++ fill_options.aliased_path = true; ++ if (path_obj->m_GeneralState.GetStrokeAdjust()) ++ fill_options.adjust_stroke = true; ++ if (is_stroke) ++ fill_options.stroke = true; ++ if (is_type3_char) ++ fill_options.text_mode = true; ++ ++ return fill_options; ++} ++ ++CFX_FillRenderOptions GetFillOptionsForDrawTextPath( ++ const CPDF_RenderOptions::Options& options, ++ const CPDF_TextObject* text_obj, ++ bool is_stroke, ++ bool is_fill) { ++ CFX_FillRenderOptions fill_options; ++ if (is_stroke && is_fill) { ++ fill_options.stroke = true; ++ fill_options.stroke_text_mode = true; + } +- CFX_DefaultRenderDevice bitmap_device; +- bitmap_device.Attach(pBitmap, false, nullptr, false); +- pBitmap->Clear(0); +- CFX_FloatRect cell_bbox = +- pPattern->pattern_to_form().TransformRect(pPattern->bbox()); +- cell_bbox = mtObject2Device.TransformRect(cell_bbox); +- CFX_FloatRect bitmap_rect(0.0f, 0.0f, width, height); +- CFX_Matrix mtAdjust; +- mtAdjust.MatchRect(bitmap_rect, cell_bbox); +- +- CFX_Matrix mtPattern2Bitmap = mtObject2Device * mtAdjust; +- CPDF_RenderOptions options; +- if (!pPattern->colored()) +- options.SetColorMode(CPDF_RenderOptions::kAlpha); +- +- options.GetOptions() = draw_options; +- options.GetOptions().bForceHalftone = true; +- +- CPDF_RenderContext context(pDoc, nullptr, pCache); +- context.AppendLayer(pPatternForm, &mtPattern2Bitmap); +- context.Render(&bitmap_device, &options, nullptr); +-#if defined _SKIA_SUPPORT_PATHS_ +- bitmap_device.Flush(true); +- pBitmap->UnPreMultiply(); ++ if (text_obj->m_GeneralState.GetStrokeAdjust()) ++ fill_options.adjust_stroke = true; ++ if (options.bNoTextSmooth) ++ fill_options.aliased_path = true; ++ ++ return fill_options; ++} ++ ++FXDIB_Format GetFormatForLuminosity(bool is_luminosity) { ++ if (!is_luminosity) ++ return FXDIB_Format::k8bppMask; ++#if BUILDFLAG(IS_APPLE) ++ return FXDIB_Format::kRgb32; ++#else ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return FXDIB_Format::kRgb32; ++ return FXDIB_Format::kRgb; + #endif +- return pBitmap; + } + + bool IsAvailableMatrix(const CFX_Matrix& matrix) { +@@ -146,29 +160,13 @@ bool Type3CharMissingStrokeColor(const CPDF_Type3Char* pChar, + return pChar && (!pChar->colored() || MissingStrokeColor(pColorState)); + } + +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-class ScopedSkiaDeviceFlush { +- public: +- explicit ScopedSkiaDeviceFlush(CFX_RenderDevice* pDevice) +- : m_pDevice(pDevice) {} +- +- ScopedSkiaDeviceFlush(const ScopedSkiaDeviceFlush&) = delete; +- ScopedSkiaDeviceFlush& operator=(const ScopedSkiaDeviceFlush&) = delete; +- +- ~ScopedSkiaDeviceFlush() { m_pDevice->Flush(/*release=*/false); } +- +- private: +- CFX_RenderDevice* const m_pDevice; +-}; +-#endif +- + } // namespace + + CPDF_RenderStatus::CPDF_RenderStatus(CPDF_RenderContext* pContext, + CFX_RenderDevice* pDevice) + : m_pContext(pContext), m_pDevice(pDevice) {} + +-CPDF_RenderStatus::~CPDF_RenderStatus() {} ++CPDF_RenderStatus::~CPDF_RenderStatus() = default; + + void CPDF_RenderStatus::Initialize(const CPDF_RenderStatus* pParentStatus, + const CPDF_GraphicStates* pInitialStates) { +@@ -198,9 +196,6 @@ void CPDF_RenderStatus::Initialize(const CPDF_RenderStatus* pParentStatus, + void CPDF_RenderStatus::RenderObjectList( + const CPDF_PageObjectHolder* pObjectHolder, + const CFX_Matrix& mtObj2Device) { +-#if defined _SKIA_SUPPORT_ +- DebugVerifyDeviceIsPreMultiplied(); +-#endif + CFX_FloatRect clip_rect = mtObj2Device.GetInverse().TransformRect( + CFX_FloatRect(m_pDevice->GetClipBox())); + for (const auto& pCurObj : *pObjectHolder) { +@@ -221,23 +216,16 @@ void CPDF_RenderStatus::RenderObjectList( + if (m_bStopped) + return; + } +-#if defined _SKIA_SUPPORT_ +- DebugVerifyDeviceIsPreMultiplied(); +-#endif + } + + void CPDF_RenderStatus::RenderSingleObject(CPDF_PageObject* pObj, + const CFX_Matrix& mtObj2Device) { +-#if defined _SKIA_SUPPORT_ +- DebugVerifyDeviceIsPreMultiplied(); +-#endif + AutoRestorer restorer(&g_CurrentRecursionDepth); + if (++g_CurrentRecursionDepth > kRenderMaxRecursionDepth) { + return; + } + m_pCurObj = pObj; +- if (m_Options.GetOCContext() && +- !m_Options.GetOCContext()->CheckObjectVisible(pObj)) { ++ if (!m_Options.CheckPageObjectVisible(pObj)) { + return; + } + ProcessClipPath(pObj->m_ClipPath, mtObj2Device); +@@ -245,9 +233,6 @@ void CPDF_RenderStatus::RenderSingleObject(CPDF_PageObject* pObj, + return; + } + ProcessObjectNoClip(pObj, mtObj2Device); +-#if defined _SKIA_SUPPORT_ +- DebugVerifyDeviceIsPreMultiplied(); +-#endif + } + + bool CPDF_RenderStatus::ContinueSingleObject(CPDF_PageObject* pObj, +@@ -264,10 +249,8 @@ bool CPDF_RenderStatus::ContinueSingleObject(CPDF_PageObject* pObj, + } + + m_pCurObj = pObj; +- if (m_Options.GetOCContext() && +- !m_Options.GetOCContext()->CheckObjectVisible(pObj)) { ++ if (!m_Options.CheckPageObjectVisible(pObj)) + return false; +- } + + ProcessClipPath(pObj->m_ClipPath, mtObj2Device); + if (ProcessTransparency(pObj, mtObj2Device)) +@@ -278,8 +261,8 @@ bool CPDF_RenderStatus::ContinueSingleObject(CPDF_PageObject* pObj, + return false; + } + +- m_pImageRenderer = pdfium::MakeUnique(); +- if (!m_pImageRenderer->Start(this, pObj->AsImage(), mtObj2Device, false, ++ m_pImageRenderer = std::make_unique(this); ++ if (!m_pImageRenderer->Start(pObj->AsImage(), mtObj2Device, false, + BlendMode::kNormal)) { + if (!m_pImageRenderer->GetResult()) + DrawObjWithBackground(pObj, mtObj2Device); +@@ -299,42 +282,36 @@ FX_RECT CPDF_RenderStatus::GetObjectClippedRect( + + void CPDF_RenderStatus::ProcessObjectNoClip(CPDF_PageObject* pObj, + const CFX_Matrix& mtObj2Device) { +-#if defined _SKIA_SUPPORT_ +- DebugVerifyDeviceIsPreMultiplied(); +-#endif + bool bRet = false; + switch (pObj->GetType()) { +- case CPDF_PageObject::TEXT: ++ case CPDF_PageObject::Type::kText: + bRet = ProcessText(pObj->AsText(), mtObj2Device, nullptr); + break; +- case CPDF_PageObject::PATH: ++ case CPDF_PageObject::Type::kPath: + bRet = ProcessPath(pObj->AsPath(), mtObj2Device); + break; +- case CPDF_PageObject::IMAGE: ++ case CPDF_PageObject::Type::kImage: + bRet = ProcessImage(pObj->AsImage(), mtObj2Device); + break; +- case CPDF_PageObject::SHADING: ++ case CPDF_PageObject::Type::kShading: + ProcessShading(pObj->AsShading(), mtObj2Device); + return; +- case CPDF_PageObject::FORM: ++ case CPDF_PageObject::Type::kForm: + bRet = ProcessForm(pObj->AsForm(), mtObj2Device); + break; + } + if (!bRet) + DrawObjWithBackground(pObj, mtObj2Device); +-#if defined _SKIA_SUPPORT_ +- DebugVerifyDeviceIsPreMultiplied(); +-#endif + } + + bool CPDF_RenderStatus::DrawObjWithBlend(CPDF_PageObject* pObj, + const CFX_Matrix& mtObj2Device) { + switch (pObj->GetType()) { +- case CPDF_PageObject::PATH: ++ case CPDF_PageObject::Type::kPath: + return ProcessPath(pObj->AsPath(), mtObj2Device); +- case CPDF_PageObject::IMAGE: ++ case CPDF_PageObject::Type::kImage: + return ProcessImage(pObj->AsImage(), mtObj2Device); +- case CPDF_PageObject::FORM: ++ case CPDF_PageObject::Type::kForm: + return ProcessForm(pObj->AsForm(), mtObj2Device); + default: + return false; +@@ -347,26 +324,22 @@ void CPDF_RenderStatus::DrawObjWithBackground(CPDF_PageObject* pObj, + if (rect.IsEmpty()) + return; + +- int res = 300; +- if (pObj->IsImage() && m_pDevice->GetDeviceType() == DeviceType::kPrinter) +- res = 0; +- ++ int res = (pObj->IsImage() && m_bPrint) ? 0 : 300; + CPDF_ScaledRenderBuffer buffer; +- if (!buffer.Initialize(m_pContext.Get(), m_pDevice, rect, pObj, &m_Options, +- res)) { ++ if (!buffer.Initialize(m_pContext, m_pDevice, rect, pObj, &m_Options, res)) { + return; + } ++ RetainPtr pFormResource; + CFX_Matrix matrix = mtObj2Device * buffer.GetMatrix(); +- const CPDF_Dictionary* pFormResource = nullptr; + const CPDF_FormObject* pFormObj = pObj->AsForm(); + if (pFormObj) + pFormResource = pFormObj->form()->GetDict()->GetDictFor("Resources"); +- CPDF_RenderStatus status(m_pContext.Get(), buffer.GetDevice()); ++ CPDF_RenderStatus status(m_pContext, buffer.GetDevice()); + status.SetOptions(m_Options); + status.SetDeviceMatrix(buffer.GetMatrix()); + status.SetTransparency(m_Transparency); + status.SetDropObjects(m_bDropObjects); +- status.SetFormResource(pFormResource); ++ status.SetFormResource(std::move(pFormResource)); + status.Initialize(nullptr, nullptr); + status.RenderSingleObject(pObj, matrix); + buffer.OutputToDevice(); +@@ -374,23 +347,20 @@ void CPDF_RenderStatus::DrawObjWithBackground(CPDF_PageObject* pObj, + + bool CPDF_RenderStatus::ProcessForm(const CPDF_FormObject* pFormObj, + const CFX_Matrix& mtObj2Device) { +-#if defined _SKIA_SUPPORT_ +- DebugVerifyDeviceIsPreMultiplied(); +-#endif +- const CPDF_Dictionary* pOC = pFormObj->form()->GetDict()->GetDictFor("OC"); +- if (pOC && m_Options.GetOCContext() && +- !m_Options.GetOCContext()->CheckOCGVisible(pOC)) { ++ RetainPtr pOC = ++ pFormObj->form()->GetDict()->GetDictFor("OC"); ++ if (pOC && !m_Options.CheckOCGDictVisible(pOC.Get())) + return true; +- } ++ + CFX_Matrix matrix = pFormObj->form_matrix() * mtObj2Device; +- const CPDF_Dictionary* pResources = ++ RetainPtr pResources = + pFormObj->form()->GetDict()->GetDictFor("Resources"); +- CPDF_RenderStatus status(m_pContext.Get(), m_pDevice); ++ CPDF_RenderStatus status(m_pContext, m_pDevice); + status.SetOptions(m_Options); +- status.SetStopObject(m_pStopObj.Get()); ++ status.SetStopObject(m_pStopObj); + status.SetTransparency(m_Transparency); + status.SetDropObjects(m_bDropObjects); +- status.SetFormResource(pResources); ++ status.SetFormResource(std::move(pResources)); + status.Initialize(this, pFormObj); + status.m_curBlend = m_curBlend; + { +@@ -398,63 +368,59 @@ bool CPDF_RenderStatus::ProcessForm(const CPDF_FormObject* pFormObj, + status.RenderObjectList(pFormObj->form(), matrix); + m_bStopped = status.m_bStopped; + } +-#if defined _SKIA_SUPPORT_ +- DebugVerifyDeviceIsPreMultiplied(); +-#endif + return true; + } + +-bool CPDF_RenderStatus::ProcessPath(CPDF_PathObject* pPathObj, ++bool CPDF_RenderStatus::ProcessPath(CPDF_PathObject* path_obj, + const CFX_Matrix& mtObj2Device) { +- int FillType = pPathObj->filltype(); +- bool bStroke = pPathObj->stroke(); +- ProcessPathPattern(pPathObj, mtObj2Device, &FillType, &bStroke); +- if (FillType == 0 && !bStroke) ++ CFX_FillRenderOptions::FillType fill_type = path_obj->filltype(); ++ bool stroke = path_obj->stroke(); ++ ProcessPathPattern(path_obj, mtObj2Device, &fill_type, &stroke); ++ if (fill_type == CFX_FillRenderOptions::FillType::kNoFill && !stroke) + return true; + +- uint32_t fill_argb = FillType ? GetFillArgb(pPathObj) : 0; +- uint32_t stroke_argb = bStroke ? GetStrokeArgb(pPathObj) : 0; +- CFX_Matrix path_matrix = pPathObj->matrix() * mtObj2Device; ++ // If the option to convert fill paths to stroke is enabled for forced color, ++ // set |fill_type| to FillType::kNoFill and |stroke| to true. ++ CPDF_RenderOptions::Options& options = m_Options.GetOptions(); ++ if (m_Options.ColorModeIs(CPDF_RenderOptions::Type::kForcedColor) && ++ options.bConvertFillToStroke && ++ fill_type != CFX_FillRenderOptions::FillType::kNoFill) { ++ stroke = true; ++ fill_type = CFX_FillRenderOptions::FillType::kNoFill; ++ } ++ ++ uint32_t fill_argb = fill_type != CFX_FillRenderOptions::FillType::kNoFill ++ ? GetFillArgb(path_obj) ++ : 0; ++ uint32_t stroke_argb = stroke ? GetStrokeArgb(path_obj) : 0; ++ CFX_Matrix path_matrix = path_obj->matrix() * mtObj2Device; + if (!IsAvailableMatrix(path_matrix)) + return true; + +- if (FillType && m_Options.GetOptions().bRectAA) +- FillType |= FXFILL_RECT_AA; +- if (m_Options.GetOptions().bFillFullcover) +- FillType |= FXFILL_FULLCOVER; +- if (m_Options.GetOptions().bNoPathSmooth) +- FillType |= FXFILL_NOPATHSMOOTH; +- if (bStroke) +- FillType |= FX_FILL_STROKE; +- +- const CPDF_PageObject* pPageObj = +- static_cast(pPathObj); +- if (pPageObj->m_GeneralState.GetStrokeAdjust()) +- FillType |= FX_STROKE_ADJUST; +- if (m_pType3Char) +- FillType |= FX_FILL_TEXT_MODE; +- +- CFX_GraphState graphState = pPathObj->m_GraphState; +- if (m_Options.GetOptions().bThinLine) +- graphState.SetLineWidth(0); + return m_pDevice->DrawPathWithBlend( +- pPathObj->path().GetObject(), &path_matrix, graphState.GetObject(), +- fill_argb, stroke_argb, FillType, m_curBlend); ++ *path_obj->path().GetObject(), &path_matrix, ++ path_obj->m_GraphState.GetObject(), fill_argb, stroke_argb, ++ GetFillOptionsForDrawPathWithBlend(options, path_obj, fill_type, stroke, ++ m_pType3Char), ++ m_curBlend); + } + + RetainPtr CPDF_RenderStatus::GetTransferFunc( +- const CPDF_Object* pObj) const { +- ASSERT(pObj); ++ RetainPtr pObj) const { ++ DCHECK(pObj); + auto* pDocCache = CPDF_DocRenderData::FromDocument(m_pContext->GetDocument()); +- return pDocCache ? pDocCache->GetTransferFunc(pObj) : nullptr; ++ return pDocCache ? pDocCache->GetTransferFunc(std::move(pObj)) : nullptr; + } + +-FX_ARGB CPDF_RenderStatus::GetFillArgbInternal(CPDF_PageObject* pObj, +- bool bType3) const { +- const CPDF_ColorState* pColorState = &pObj->m_ColorState; +- if (!bType3 && Type3CharMissingFillColor(m_pType3Char.Get(), pColorState)) ++FX_ARGB CPDF_RenderStatus::GetFillArgb(CPDF_PageObject* pObj) const { ++ if (Type3CharMissingFillColor(m_pType3Char, &pObj->m_ColorState)) + return m_T3FillColor; + ++ return GetFillArgbForType3(pObj); ++} ++ ++FX_ARGB CPDF_RenderStatus::GetFillArgbForType3(CPDF_PageObject* pObj) const { ++ const CPDF_ColorState* pColorState = &pObj->m_ColorState; + if (MissingFillColor(pColorState)) + pColorState = &m_InitialStates.m_ColorState; + +@@ -464,22 +430,24 @@ FX_ARGB CPDF_RenderStatus::GetFillArgbInternal(CPDF_PageObject* pObj, + + int32_t alpha = + static_cast((pObj->m_GeneralState.GetFillAlpha() * 255)); +- if (pObj->m_GeneralState.GetTR()) { ++ RetainPtr pTR = pObj->m_GeneralState.GetTR(); ++ if (pTR) { + if (!pObj->m_GeneralState.GetTransferFunc()) { +- pObj->m_GeneralState.SetTransferFunc( +- GetTransferFunc(pObj->m_GeneralState.GetTR())); ++ pObj->m_GeneralState.SetTransferFunc(GetTransferFunc(std::move(pTR))); + } + if (pObj->m_GeneralState.GetTransferFunc()) { + colorref = + pObj->m_GeneralState.GetTransferFunc()->TranslateColor(colorref); + } + } +- return m_Options.TranslateColor(AlphaAndColorRefToArgb(alpha, colorref)); ++ return m_Options.TranslateObjectColor(AlphaAndColorRefToArgb(alpha, colorref), ++ pObj->GetType(), ++ CPDF_RenderOptions::RenderType::kFill); + } + + FX_ARGB CPDF_RenderStatus::GetStrokeArgb(CPDF_PageObject* pObj) const { + const CPDF_ColorState* pColorState = &pObj->m_ColorState; +- if (Type3CharMissingStrokeColor(m_pType3Char.Get(), pColorState)) ++ if (Type3CharMissingStrokeColor(m_pType3Char, pColorState)) + return m_T3FillColor; + + if (MissingStrokeColor(pColorState)) +@@ -491,17 +459,19 @@ FX_ARGB CPDF_RenderStatus::GetStrokeArgb(CPDF_PageObject* pObj) const { + + int32_t alpha = static_cast(pObj->m_GeneralState.GetStrokeAlpha() * + 255); // not rounded. +- if (pObj->m_GeneralState.GetTR()) { ++ RetainPtr pTR = pObj->m_GeneralState.GetTR(); ++ if (pTR) { + if (!pObj->m_GeneralState.GetTransferFunc()) { +- pObj->m_GeneralState.SetTransferFunc( +- GetTransferFunc(pObj->m_GeneralState.GetTR())); ++ pObj->m_GeneralState.SetTransferFunc(GetTransferFunc(std::move(pTR))); + } + if (pObj->m_GeneralState.GetTransferFunc()) { + colorref = + pObj->m_GeneralState.GetTransferFunc()->TranslateColor(colorref); + } + } +- return m_Options.TranslateColor(AlphaAndColorRefToArgb(alpha, colorref)); ++ return m_Options.TranslateObjectColor( ++ AlphaAndColorRefToArgb(alpha, colorref), pObj->GetType(), ++ CPDF_RenderOptions::RenderType::kStroke); + } + + void CPDF_RenderStatus::ProcessClipPath(const CPDF_ClipPath& ClipPath, +@@ -519,34 +489,36 @@ void CPDF_RenderStatus::ProcessClipPath(const CPDF_ClipPath& ClipPath, + m_LastClipPath = ClipPath; + m_pDevice->RestoreState(true); + for (size_t i = 0; i < ClipPath.GetPathCount(); ++i) { +- const CFX_PathData* pPathData = ClipPath.GetPath(i).GetObject(); +- if (!pPathData) ++ const CFX_Path* pPath = ClipPath.GetPath(i).GetObject(); ++ if (!pPath) + continue; + +- if (pPathData->GetPoints().empty()) { +- CFX_PathData EmptyPath; +- EmptyPath.AppendRect(-1, -1, 0, 0); +- m_pDevice->SetClip_PathFill(&EmptyPath, nullptr, FXFILL_WINDING); ++ if (pPath->GetPoints().empty()) { ++ CFX_Path empty_path; ++ empty_path.AppendRect(-1, -1, 0, 0); ++ m_pDevice->SetClip_PathFill(empty_path, nullptr, ++ CFX_FillRenderOptions::WindingOptions()); + } else { +- m_pDevice->SetClip_PathFill(pPathData, &mtObj2Device, +- ClipPath.GetClipType(i)); ++ m_pDevice->SetClip_PathFill( ++ *pPath, &mtObj2Device, ++ CFX_FillRenderOptions(ClipPath.GetClipType(i))); + } + } + + if (ClipPath.GetTextCount() == 0) + return; + +- if (m_pDevice->GetDeviceType() == DeviceType::kDisplay && ++ if (!m_bPrint && + !(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP)) { + return; + } + +- std::unique_ptr pTextClippingPath; ++ std::unique_ptr pTextClippingPath; + for (size_t i = 0; i < ClipPath.GetTextCount(); ++i) { + CPDF_TextObject* pText = ClipPath.GetText(i); + if (pText) { + if (!pTextClippingPath) +- pTextClippingPath = pdfium::MakeUnique(); ++ pTextClippingPath = std::make_unique(); + ProcessText(pText, mtObj2Device, pTextClippingPath.get()); + continue; + } +@@ -554,60 +526,55 @@ void CPDF_RenderStatus::ProcessClipPath(const CPDF_ClipPath& ClipPath, + if (!pTextClippingPath) + continue; + +- int fill_mode = FXFILL_WINDING; ++ CFX_FillRenderOptions fill_options(CFX_FillRenderOptions::WindingOptions()); + if (m_Options.GetOptions().bNoTextSmooth) +- fill_mode |= FXFILL_NOPATHSMOOTH; +- m_pDevice->SetClip_PathFill(pTextClippingPath.get(), nullptr, fill_mode); ++ fill_options.aliased_path = true; ++ m_pDevice->SetClip_PathFill(*pTextClippingPath, nullptr, fill_options); + pTextClippingPath.reset(); + } + } + +-bool CPDF_RenderStatus::ClipPattern(const CPDF_PageObject* pPageObj, ++bool CPDF_RenderStatus::ClipPattern(const CPDF_PageObject* page_obj, + const CFX_Matrix& mtObj2Device, +- bool bStroke) { +- if (pPageObj->IsPath()) +- return SelectClipPath(pPageObj->AsPath(), mtObj2Device, bStroke); +- if (pPageObj->IsImage()) { +- m_pDevice->SetClip_Rect(pPageObj->GetTransformedBBox(mtObj2Device)); ++ bool stroke) { ++ if (page_obj->IsPath()) ++ return SelectClipPath(page_obj->AsPath(), mtObj2Device, stroke); ++ if (page_obj->IsImage()) { ++ m_pDevice->SetClip_Rect(page_obj->GetTransformedBBox(mtObj2Device)); + return true; + } + return false; + } + +-bool CPDF_RenderStatus::SelectClipPath(const CPDF_PathObject* pPathObj, ++bool CPDF_RenderStatus::SelectClipPath(const CPDF_PathObject* path_obj, + const CFX_Matrix& mtObj2Device, +- bool bStroke) { +- CFX_Matrix path_matrix = pPathObj->matrix() * mtObj2Device; +- if (bStroke) { +- CFX_GraphState graphState = pPathObj->m_GraphState; +- if (m_Options.GetOptions().bThinLine) +- graphState.SetLineWidth(0); +- return m_pDevice->SetClip_PathStroke(pPathObj->path().GetObject(), +- &path_matrix, graphState.GetObject()); ++ bool stroke) { ++ CFX_Matrix path_matrix = path_obj->matrix() * mtObj2Device; ++ if (stroke) { ++ return m_pDevice->SetClip_PathStroke(*path_obj->path().GetObject(), ++ &path_matrix, ++ path_obj->m_GraphState.GetObject()); + } +- int fill_mode = pPathObj->filltype(); ++ CFX_FillRenderOptions fill_options(path_obj->filltype()); + if (m_Options.GetOptions().bNoPathSmooth) { +- fill_mode |= FXFILL_NOPATHSMOOTH; ++ fill_options.aliased_path = true; + } +- return m_pDevice->SetClip_PathFill(pPathObj->path().GetObject(), &path_matrix, +- fill_mode); ++ return m_pDevice->SetClip_PathFill(*path_obj->path().GetObject(), ++ &path_matrix, fill_options); + } + + bool CPDF_RenderStatus::ProcessTransparency(CPDF_PageObject* pPageObj, + const CFX_Matrix& mtObj2Device) { +-#if defined _SKIA_SUPPORT_ +- DebugVerifyDeviceIsPreMultiplied(); +-#endif +- BlendMode blend_type = pPageObj->m_GeneralState.GetBlendType(); +- CPDF_Dictionary* pSMaskDict = +- ToDictionary(pPageObj->m_GeneralState.GetSoftMask()); ++ const BlendMode blend_type = pPageObj->m_GeneralState.GetBlendType(); ++ RetainPtr pSMaskDict = ++ pPageObj->m_GeneralState.GetMutableSoftMask(); + if (pSMaskDict) { + if (pPageObj->IsImage() && + pPageObj->AsImage()->GetImage()->GetDict()->KeyExist("SMask")) { + pSMaskDict = nullptr; + } + } +- const CPDF_Dictionary* pFormResource = nullptr; ++ RetainPtr pFormResource; + float group_alpha = 1.0f; + CPDF_Transparency transparency = m_Transparency; + bool bGroupTransparent = false; +@@ -620,36 +587,8 @@ bool CPDF_RenderStatus::ProcessTransparency(CPDF_PageObject* pPageObj, + } + bool bTextClip = + (pPageObj->m_ClipPath.HasRef() && +- pPageObj->m_ClipPath.GetTextCount() > 0 && +- m_pDevice->GetDeviceType() == DeviceType::kDisplay && ++ pPageObj->m_ClipPath.GetTextCount() > 0 && !m_bPrint && + !(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP)); +- if (m_Options.GetOptions().bOverprint && pPageObj->IsImage() && +- pPageObj->m_GeneralState.GetFillOP() && +- pPageObj->m_GeneralState.GetStrokeOP()) { +- CPDF_Document* pDocument = nullptr; +- CPDF_Page* pPage = nullptr; +- if (m_pContext->GetPageCache()) { +- pPage = m_pContext->GetPageCache()->GetPage(); +- pDocument = pPage->GetDocument(); +- } else { +- pDocument = pPageObj->AsImage()->GetImage()->GetDocument(); +- } +- const CPDF_Dictionary* pPageResources = +- pPage ? pPage->m_pPageResources.Get() : nullptr; +- auto* pImageStream = pPageObj->AsImage()->GetImage()->GetStream(); +- const CPDF_Object* pCSObj = +- pImageStream->GetDict()->GetDirectObjectFor("ColorSpace"); +- RetainPtr pColorSpace = +- CPDF_DocPageData::FromDocument(pDocument)->GetColorSpace( +- pCSObj, pPageResources); +- if (pColorSpace) { +- int format = pColorSpace->GetFamily(); +- if (format == PDFCS_DEVICECMYK || format == PDFCS_SEPARATION || +- format == PDFCS_DEVICEN) { +- blend_type = BlendMode::kDarken; +- } +- } +- } + if (!pSMaskDict && group_alpha == 1.0f && blend_type == BlendMode::kNormal && + !bTextClip && !bGroupTransparent) { + return false; +@@ -685,24 +624,20 @@ bool CPDF_RenderStatus::ProcessTransparency(CPDF_PageObject* pPageObj, + return true; + m_pDevice->GetDIBits(backdrop, rect.left, rect.top); + } +- if (!bitmap_device.Create(width, height, FXDIB_Argb, backdrop)) ++ if (!bitmap_device.Create(width, height, FXDIB_Format::kArgb, backdrop)) + return true; + +- RetainPtr bitmap = bitmap_device.GetBitmap(); +- bitmap->Clear(0); +- + CFX_Matrix new_matrix = mtObj2Device; + new_matrix.Translate(-rect.left, -rect.top); + + RetainPtr pTextMask; + if (bTextClip) { + pTextMask = pdfium::MakeRetain(); +- if (!pTextMask->Create(width, height, FXDIB_8bppMask)) ++ if (!pTextMask->Create(width, height, FXDIB_Format::k8bppMask)) + return true; + +- pTextMask->Clear(0); + CFX_DefaultRenderDevice text_device; +- text_device.Attach(pTextMask, false, nullptr, false); ++ text_device.Attach(pTextMask); + for (size_t i = 0; i < pPageObj->m_ClipPath.GetTextCount(); ++i) { + CPDF_TextObject* textobj = pPageObj->m_ClipPath.GetText(i); + if (!textobj) +@@ -714,125 +649,116 @@ bool CPDF_RenderStatus::ProcessTransparency(CPDF_PageObject* pPageObj, + textobj->m_TextState.GetFont().Get(), + textobj->m_TextState.GetFontSize(), textobj->GetTextMatrix(), + &new_matrix, textobj->m_GraphState.GetObject(), 0xffffffff, 0, +- nullptr, 0); ++ nullptr, CFX_FillRenderOptions()); + } + } +- CPDF_RenderStatus bitmap_render(m_pContext.Get(), &bitmap_device); ++ CPDF_RenderStatus bitmap_render(m_pContext, &bitmap_device); + bitmap_render.SetOptions(m_Options); +- bitmap_render.SetStopObject(m_pStopObj.Get()); ++ bitmap_render.SetStopObject(m_pStopObj); + bitmap_render.SetStdCS(true); + bitmap_render.SetDropObjects(m_bDropObjects); +- bitmap_render.SetFormResource(pFormResource); ++ bitmap_render.SetFormResource(std::move(pFormResource)); + bitmap_render.Initialize(nullptr, nullptr); + bitmap_render.ProcessObjectNoClip(pPageObj, new_matrix); +-#if defined _SKIA_SUPPORT_PATHS_ +- bitmap_device.Flush(true); +- bitmap->UnPreMultiply(); +-#endif ++#if defined(_SKIA_SUPPORT_) ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ // Safe because `CFX_SkiaDeviceDriver` always uses pre-multiplied alpha. ++ // TODO(crbug.com/pdfium/2011): Remove the need for this. ++ bitmap_device.GetBitmap()->ForcePreMultiply(); ++ } ++#endif // _SKIA_SUPPORT + m_bStopped = bitmap_render.m_bStopped; + if (pSMaskDict) { + CFX_Matrix smask_matrix = + *pPageObj->m_GeneralState.GetSMaskMatrix() * mtObj2Device; + RetainPtr pSMaskSource = +- LoadSMask(pSMaskDict, &rect, &smask_matrix); ++ LoadSMask(pSMaskDict.Get(), &rect, smask_matrix); + if (pSMaskSource) +- bitmap->MultiplyAlpha(pSMaskSource); ++ bitmap_device.MultiplyAlpha(pSMaskSource); + } + if (pTextMask) { +- bitmap->MultiplyAlpha(pTextMask); ++ bitmap_device.MultiplyAlpha(pTextMask); + pTextMask.Reset(); + } +- int32_t blitAlpha = 255; + if (group_alpha != 1.0f && transparency.IsGroup()) { +- blitAlpha = (int32_t)(group_alpha * 255); +-#ifndef _SKIA_SUPPORT_ +- bitmap->MultiplyAlpha(blitAlpha); +- blitAlpha = 255; +-#endif ++ bitmap_device.MultiplyAlpha(group_alpha); + } + transparency = m_Transparency; + if (pPageObj->IsForm()) { + transparency.SetGroup(); + } +- CompositeDIBitmap(bitmap, rect.left, rect.top, 0, blitAlpha, blend_type, +- transparency); +-#if defined _SKIA_SUPPORT_ +- DebugVerifyDeviceIsPreMultiplied(); +-#endif ++ CompositeDIBitmap(bitmap_device.GetBitmap(), rect.left, rect.top, 0, 255, ++ blend_type, transparency); + return true; + } + +-RetainPtr CPDF_RenderStatus::GetBackdrop( +- const CPDF_PageObject* pObj, +- const FX_RECT& rect, +- bool bBackAlphaRequired, +- int* left, +- int* top) { ++FX_RECT CPDF_RenderStatus::GetClippedBBox(const FX_RECT& rect) const { + FX_RECT bbox = rect; + bbox.Intersect(m_pDevice->GetClipBox()); +- *left = bbox.left; +- *top = bbox.top; ++ return bbox; ++} ++ ++RetainPtr CPDF_RenderStatus::GetBackdrop( ++ const CPDF_PageObject* pObj, ++ const FX_RECT& bbox, ++ bool bBackAlphaRequired) { + int width = bbox.Width(); + int height = bbox.Height(); + auto pBackdrop = pdfium::MakeRetain(); + if (bBackAlphaRequired && !m_bDropObjects) +- pBackdrop->Create(width, height, FXDIB_Argb); ++ pBackdrop->Create(width, height, FXDIB_Format::kArgb); + else + m_pDevice->CreateCompatibleBitmap(pBackdrop, width, height); + +- if (!pBackdrop->GetBuffer()) ++ if (pBackdrop->GetBuffer().empty()) + return nullptr; + + bool bNeedDraw; +- if (pBackdrop->HasAlpha()) ++ if (pBackdrop->IsAlphaFormat()) + bNeedDraw = !(m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT); + else + bNeedDraw = !(m_pDevice->GetRenderCaps() & FXRC_GET_BITS); + + if (!bNeedDraw) { +- m_pDevice->GetDIBits(pBackdrop, *left, *top); ++ m_pDevice->GetDIBits(pBackdrop, bbox.left, bbox.top); + return pBackdrop; + } + CFX_Matrix FinalMatrix = m_DeviceMatrix; +- FinalMatrix.Translate(-*left, -*top); +- pBackdrop->Clear(pBackdrop->HasAlpha() ? 0 : 0xffffffff); ++ FinalMatrix.Translate(-bbox.left, -bbox.top); ++ if (!pBackdrop->IsAlphaFormat()) { ++ pBackdrop->Clear(0xffffffff); ++ } + + CFX_DefaultRenderDevice device; +- device.Attach(pBackdrop, false, nullptr, false); ++ device.Attach(pBackdrop); + m_pContext->Render(&device, pObj, &m_Options, &FinalMatrix); + return pBackdrop; + } + + std::unique_ptr CPDF_RenderStatus::CloneObjStates( + const CPDF_GraphicStates* pSrcStates, +- bool bStroke) { ++ bool stroke) { + if (!pSrcStates) + return nullptr; + +- auto pStates = pdfium::MakeUnique(); ++ auto pStates = std::make_unique(); + pStates->CopyStates(*pSrcStates); +- const CPDF_Color* pObjColor = bStroke ++ const CPDF_Color* pObjColor = stroke + ? pSrcStates->m_ColorState.GetStrokeColor() + : pSrcStates->m_ColorState.GetFillColor(); + if (!pObjColor->IsNull()) { + pStates->m_ColorState.SetFillColorRef( +- bStroke ? pSrcStates->m_ColorState.GetStrokeColorRef() +- : pSrcStates->m_ColorState.GetFillColorRef()); ++ stroke ? pSrcStates->m_ColorState.GetStrokeColorRef() ++ : pSrcStates->m_ColorState.GetFillColorRef()); + pStates->m_ColorState.SetStrokeColorRef( + pStates->m_ColorState.GetFillColorRef()); + } + return pStates; + } + +-#if defined _SKIA_SUPPORT_ +-void CPDF_RenderStatus::DebugVerifyDeviceIsPreMultiplied() const { +- m_pDevice->DebugVerifyBitmapIsPreMultiplied(); +-} +-#endif +- + bool CPDF_RenderStatus::ProcessText(CPDF_TextObject* textobj, + const CFX_Matrix& mtObj2Device, +- CFX_PathData* pClippingPath) { ++ CFX_Path* clipping_path) { + if (textobj->GetCharCodes().empty()) + return true; + +@@ -844,29 +770,29 @@ bool CPDF_RenderStatus::ProcessText(CPDF_TextObject* textobj, + if (pFont->IsType3Font()) + return ProcessType3Text(textobj, mtObj2Device); + +- bool bFill = false; +- bool bStroke = false; +- bool bClip = false; +- if (pClippingPath) { +- bClip = true; ++ bool is_fill = false; ++ bool is_stroke = false; ++ bool is_clip = false; ++ if (clipping_path) { ++ is_clip = true; + } else { + switch (text_render_mode) { + case TextRenderingMode::MODE_FILL: + case TextRenderingMode::MODE_FILL_CLIP: +- bFill = true; ++ is_fill = true; + break; + case TextRenderingMode::MODE_STROKE: + case TextRenderingMode::MODE_STROKE_CLIP: + if (pFont->HasFace()) +- bStroke = true; ++ is_stroke = true; + else +- bFill = true; ++ is_fill = true; + break; + case TextRenderingMode::MODE_FILL_STROKE: + case TextRenderingMode::MODE_FILL_STROKE_CLIP: +- bFill = true; ++ is_fill = true; + if (pFont->HasFace()) +- bStroke = true; ++ is_stroke = true; + break; + case TextRenderingMode::MODE_INVISIBLE: + // Already handled above, but the compiler is not smart enough to +@@ -883,14 +809,14 @@ bool CPDF_RenderStatus::ProcessText(CPDF_TextObject* textobj, + FX_ARGB stroke_argb = 0; + FX_ARGB fill_argb = 0; + bool bPattern = false; +- if (bStroke) { ++ if (is_stroke) { + if (textobj->m_ColorState.GetStrokeColor()->IsPattern()) { + bPattern = true; + } else { + stroke_argb = GetStrokeArgb(textobj); + } + } +- if (bFill) { ++ if (is_fill) { + if (textobj->m_ColorState.GetFillColor()->IsPattern()) { + bPattern = true; + } else { +@@ -904,14 +830,14 @@ bool CPDF_RenderStatus::ProcessText(CPDF_TextObject* textobj, + float font_size = textobj->m_TextState.GetFontSize(); + if (bPattern) { + DrawTextPathWithPattern(textobj, mtObj2Device, pFont.Get(), font_size, +- &text_matrix, bFill, bStroke); ++ text_matrix, is_fill, is_stroke); + return true; + } +- if (bClip || bStroke) { ++ if (is_clip || is_stroke) { + const CFX_Matrix* pDeviceMatrix = &mtObj2Device; + CFX_Matrix device_matrix; +- if (bStroke) { +- const float* pCTM = textobj->m_TextState.GetCTM(); ++ if (is_stroke) { ++ pdfium::span pCTM = textobj->m_TextState.GetCTM(); + if (pCTM[0] != 1.0f || pCTM[3] != 1.0f) { + CFX_Matrix ctm(pCTM[0], pCTM[1], pCTM[2], pCTM[3], 0, 0); + text_matrix *= ctm.GetInverse(); +@@ -919,20 +845,13 @@ bool CPDF_RenderStatus::ProcessText(CPDF_TextObject* textobj, + pDeviceMatrix = &device_matrix; + } + } +- int flag = 0; +- if (bStroke && bFill) { +- flag |= FX_FILL_STROKE; +- flag |= FX_STROKE_TEXT_MODE; +- } +- if (textobj->m_GeneralState.GetStrokeAdjust()) +- flag |= FX_STROKE_ADJUST; +- if (m_Options.GetOptions().bNoTextSmooth) +- flag |= FXFILL_NOPATHSMOOTH; + return CPDF_TextRenderer::DrawTextPath( + m_pDevice, textobj->GetCharCodes(), textobj->GetCharPositions(), + pFont.Get(), font_size, text_matrix, pDeviceMatrix, + textobj->m_GraphState.GetObject(), fill_argb, stroke_argb, +- pClippingPath, flag); ++ clipping_path, ++ GetFillOptionsForDrawTextPath(m_Options.GetOptions(), textobj, ++ is_stroke, is_fill)); + } + text_matrix.Concat(mtObj2Device); + return CPDF_TextRenderer::DrawNormalText( +@@ -944,13 +863,12 @@ bool CPDF_RenderStatus::ProcessText(CPDF_TextObject* textobj, + bool CPDF_RenderStatus::ProcessType3Text(CPDF_TextObject* textobj, + const CFX_Matrix& mtObj2Device) { + CPDF_Type3Font* pType3Font = textobj->m_TextState.GetFont()->AsType3Font(); +- if (pdfium::ContainsValue(m_Type3FontCache, pType3Font)) ++ if (pdfium::Contains(m_Type3FontCache, pType3Font)) + return true; + +- DeviceType device_type = m_pDevice->GetDeviceType(); + FX_ARGB fill_argb = GetFillArgbForType3(textobj); + int fill_alpha = FXARGB_A(fill_argb); +- if (device_type != DeviceType::kDisplay && fill_alpha < 255) ++ if (m_bPrint && fill_alpha < 255) + return false; + + CFX_Matrix text_matrix = textobj->GetTextMatrix(); +@@ -961,7 +879,7 @@ bool CPDF_RenderStatus::ProcessType3Text(CPDF_TextObject* textobj, + // Must come before |glyphs|, because |glyphs| points into |refTypeCache|. + std::set> refTypeCache; + std::vector glyphs; +- if (device_type == DeviceType::kDisplay) ++ if (!m_bPrint) + glyphs.resize(textobj->GetCharCodes().size()); + + for (size_t iChar = 0; iChar < textobj->GetCharCodes().size(); ++iChar) { +@@ -984,7 +902,7 @@ bool CPDF_RenderStatus::ProcessType3Text(CPDF_TextObject* textobj, + if (!glyph.m_pGlyph) + continue; + +- Optional point = glyph.GetOrigin({0, 0}); ++ absl::optional point = glyph.GetOrigin({0, 0}); + if (!point.has_value()) + continue; + +@@ -1001,20 +919,20 @@ bool CPDF_RenderStatus::ProcessType3Text(CPDF_TextObject* textobj, + options.GetOptions().bRectAA = true; + + const auto* pForm = static_cast(pType3Char->form()); +- const CPDF_Dictionary* pFormResource = ++ RetainPtr pFormResource = + pForm->GetDict()->GetDictFor("Resources"); + + if (fill_alpha == 255) { +- CPDF_RenderStatus status(m_pContext.Get(), m_pDevice); ++ CPDF_RenderStatus status(m_pContext, m_pDevice); + status.SetOptions(options); + status.SetTransparency(pForm->GetTransparency()); + status.SetType3Char(pType3Char); + status.SetFillColor(fill_argb); + status.SetDropObjects(m_bDropObjects); +- status.SetFormResource(pFormResource); ++ status.SetFormResource(std::move(pFormResource)); + status.Initialize(this, pStates.get()); + status.m_Type3FontCache = m_Type3FontCache; +- status.m_Type3FontCache.push_back(pType3Font); ++ status.m_Type3FontCache.emplace_back(pType3Font); + + CFX_RenderDevice::StateRestorer restorer(m_pDevice); + status.RenderObjectList(pForm, matrix); +@@ -1025,32 +943,40 @@ bool CPDF_RenderStatus::ProcessType3Text(CPDF_TextObject* textobj, + continue; + + CFX_DefaultRenderDevice bitmap_device; +- if (!bitmap_device.Create(rect.Width(), rect.Height(), FXDIB_Argb, +- nullptr)) { ++ if (!bitmap_device.Create(rect.Width(), rect.Height(), ++ FXDIB_Format::kArgb, nullptr)) { + return true; + } +- bitmap_device.GetBitmap()->Clear(0); +- CPDF_RenderStatus status(m_pContext.Get(), &bitmap_device); ++ CPDF_RenderStatus status(m_pContext, &bitmap_device); + status.SetOptions(options); + status.SetTransparency(pForm->GetTransparency()); + status.SetType3Char(pType3Char); + status.SetFillColor(fill_argb); + status.SetDropObjects(m_bDropObjects); +- status.SetFormResource(pFormResource); ++ status.SetFormResource(std::move(pFormResource)); + status.Initialize(this, pStates.get()); + status.m_Type3FontCache = m_Type3FontCache; +- status.m_Type3FontCache.push_back(pType3Font); ++ status.m_Type3FontCache.emplace_back(pType3Font); + matrix.Translate(-rect.left, -rect.top); + status.RenderObjectList(pForm, matrix); + m_pDevice->SetDIBits(bitmap_device.GetBitmap(), rect.left, rect.top); + } + } else if (pType3Char->GetBitmap()) { +- if (device_type == DeviceType::kDisplay) { ++ if (m_bPrint) { ++ CFX_Matrix image_matrix = pType3Char->matrix() * matrix; ++ CPDF_ImageRenderer renderer(this); ++ if (renderer.Start(pType3Char->GetBitmap(), fill_argb, image_matrix, ++ FXDIB_ResampleOptions(), false)) { ++ renderer.Continue(nullptr); ++ } ++ if (!renderer.GetResult()) ++ return false; ++ } else { + CPDF_Document* pDoc = pType3Font->GetDocument(); + RetainPtr pCache = + CPDF_DocRenderData::FromDocument(pDoc)->GetCachedType3(pType3Font); + +- const CFX_GlyphBitmap* pBitmap = pCache->LoadGlyph(charcode, &matrix); ++ const CFX_GlyphBitmap* pBitmap = pCache->LoadGlyph(charcode, matrix); + if (!pBitmap) + continue; + +@@ -1074,16 +1000,6 @@ bool CPDF_RenderStatus::ProcessType3Text(CPDF_TextObject* textobj, + glyphs[iChar].m_pGlyph = pBitmap; + glyphs[iChar].m_Origin = origin; + } +- } else { +- CFX_Matrix image_matrix = pType3Char->matrix() * matrix; +- CPDF_ImageRenderer renderer; +- if (renderer.Start(this, pType3Char->GetBitmap(), fill_argb, 255, +- image_matrix, FXDIB_ResampleOptions(), false, +- BlendMode::kNormal)) { +- renderer.Continue(nullptr); +- } +- if (!renderer.GetResult()) +- return false; + } + } + } +@@ -1093,15 +1009,14 @@ bool CPDF_RenderStatus::ProcessType3Text(CPDF_TextObject* textobj, + + FX_RECT rect = GetGlyphsBBox(glyphs, 0); + auto pBitmap = pdfium::MakeRetain(); +- if (!pBitmap->Create(rect.Width(), rect.Height(), FXDIB_8bppMask)) ++ if (!pBitmap->Create(rect.Width(), rect.Height(), FXDIB_Format::k8bppMask)) + return true; + +- pBitmap->Clear(0); + for (const TextGlyphPos& glyph : glyphs) { +- if (!glyph.m_pGlyph) ++ if (!glyph.m_pGlyph || !glyph.m_pGlyph->GetBitmap()->IsMaskFormat()) + continue; + +- Optional point = glyph.GetOrigin({rect.left, rect.top}); ++ absl::optional point = glyph.GetOrigin({rect.left, rect.top}); + if (!point.has_value()) + continue; + +@@ -1118,15 +1033,15 @@ void CPDF_RenderStatus::DrawTextPathWithPattern(const CPDF_TextObject* textobj, + const CFX_Matrix& mtObj2Device, + CPDF_Font* pFont, + float font_size, +- const CFX_Matrix* pTextMatrix, +- bool bFill, +- bool bStroke) { +- if (!bStroke) { ++ const CFX_Matrix& mtTextMatrix, ++ bool fill, ++ bool stroke) { ++ if (!stroke) { + std::vector> pCopy; +- pCopy.push_back(std::unique_ptr(textobj->Clone())); ++ pCopy.push_back(textobj->Clone()); + + CPDF_PathObject path; +- path.set_filltype(FXFILL_WINDING); ++ path.set_filltype(CFX_FillRenderOptions::FillType::kWinding); + path.m_ClipPath.CopyClipPath(m_LastClipPath); + path.m_ClipPath.AppendTexts(&pCopy); + path.m_ColorState = textobj->m_ColorState; +@@ -1138,13 +1053,14 @@ void CPDF_RenderStatus::DrawTextPathWithPattern(const CPDF_TextObject* textobj, + RenderSingleObject(&path, mtObj2Device); + return; + } +- const CPDF_CharPosList CharPosList( ++ ++ std::vector char_pos_list = GetCharPosList( + textobj->GetCharCodes(), textobj->GetCharPositions(), pFont, font_size); +- for (const TextCharPos& charpos : CharPosList.Get()) { ++ for (const TextCharPos& charpos : char_pos_list) { + auto* font = charpos.m_FallbackFontPosition == -1 + ? pFont->GetFont() + : pFont->GetFontFallback(charpos.m_FallbackFontPosition); +- const CFX_PathData* pPath = ++ const CFX_Path* pPath = + font->LoadGlyphPath(charpos.m_GlyphIndex, charpos.m_FontCharWidth); + if (!pPath) + continue; +@@ -1153,19 +1069,14 @@ void CPDF_RenderStatus::DrawTextPathWithPattern(const CPDF_TextObject* textobj, + path.m_GraphState = textobj->m_GraphState; + path.m_ColorState = textobj->m_ColorState; + +- CFX_Matrix matrix; +- if (charpos.m_bGlyphAdjust) { +- matrix = CFX_Matrix(charpos.m_AdjustMatrix[0], charpos.m_AdjustMatrix[1], +- charpos.m_AdjustMatrix[2], charpos.m_AdjustMatrix[3], +- 0, 0); +- } +- matrix.Concat(CFX_Matrix(font_size, 0, 0, font_size, charpos.m_Origin.x, +- charpos.m_Origin.y)); +- path.set_stroke(bStroke); +- path.set_filltype(bFill ? FXFILL_WINDING : 0); +- path.path().Append(pPath, &matrix); +- path.set_matrix(*pTextMatrix); +- path.CalcBoundingBox(); ++ CFX_Matrix matrix = charpos.GetEffectiveMatrix(CFX_Matrix( ++ font_size, 0, 0, font_size, charpos.m_Origin.x, charpos.m_Origin.y)); ++ matrix.Concat(mtTextMatrix); ++ path.set_stroke(stroke); ++ path.set_filltype(fill ? CFX_FillRenderOptions::FillType::kWinding ++ : CFX_FillRenderOptions::FillType::kNoFill); ++ path.path().Append(*pPath, &matrix); ++ path.SetPathMatrix(CFX_Matrix()); + ProcessPath(&path, mtObj2Device); + } + } +@@ -1173,12 +1084,12 @@ void CPDF_RenderStatus::DrawTextPathWithPattern(const CPDF_TextObject* textobj, + void CPDF_RenderStatus::DrawShadingPattern(CPDF_ShadingPattern* pattern, + const CPDF_PageObject* pPageObj, + const CFX_Matrix& mtObj2Device, +- bool bStroke) { ++ bool stroke) { + if (!pattern->Load()) + return; + + CFX_RenderDevice::StateRestorer restorer(m_pDevice); +- if (!ClipPattern(pPageObj, mtObj2Device, bStroke)) ++ if (!ClipPattern(pPageObj, mtObj2Device, stroke)) + return; + + FX_RECT rect = GetObjectClippedRect(pPageObj, mtObj2Device); +@@ -1187,263 +1098,93 @@ void CPDF_RenderStatus::DrawShadingPattern(CPDF_ShadingPattern* pattern, + + CFX_Matrix matrix = pattern->pattern_to_form() * mtObj2Device; + int alpha = +- FXSYS_roundf(255 * (bStroke ? pPageObj->m_GeneralState.GetStrokeAlpha() +- : pPageObj->m_GeneralState.GetFillAlpha())); +- CPDF_RenderShading::Draw(m_pDevice, m_pContext.Get(), m_pCurObj.Get(), +- pattern, matrix, rect, alpha, m_Options); ++ FXSYS_roundf(255 * (stroke ? pPageObj->m_GeneralState.GetStrokeAlpha() ++ : pPageObj->m_GeneralState.GetFillAlpha())); ++ CPDF_RenderShading::Draw(m_pDevice, m_pContext, m_pCurObj, pattern, matrix, ++ rect, alpha, m_Options); + } + + void CPDF_RenderStatus::ProcessShading(const CPDF_ShadingObject* pShadingObj, + const CFX_Matrix& mtObj2Device) { +- FX_RECT rect = pShadingObj->GetTransformedBBox(mtObj2Device); +- FX_RECT clip_box = m_pDevice->GetClipBox(); +- rect.Intersect(clip_box); ++ FX_RECT rect = GetObjectClippedRect(pShadingObj, mtObj2Device); + if (rect.IsEmpty()) + return; + + CFX_Matrix matrix = pShadingObj->matrix() * mtObj2Device; + CPDF_RenderShading::Draw( +- m_pDevice, m_pContext.Get(), m_pCurObj.Get(), pShadingObj->pattern(), +- matrix, rect, ++ m_pDevice, m_pContext, m_pCurObj, pShadingObj->pattern(), matrix, rect, + FXSYS_roundf(255 * pShadingObj->m_GeneralState.GetFillAlpha()), + m_Options); + } + +-void CPDF_RenderStatus::DrawTilingPattern(CPDF_TilingPattern* pPattern, ++void CPDF_RenderStatus::DrawTilingPattern(CPDF_TilingPattern* pattern, + CPDF_PageObject* pPageObj, + const CFX_Matrix& mtObj2Device, +- bool bStroke) { +- const std::unique_ptr pPatternForm = pPattern->Load(pPageObj); ++ bool stroke) { ++ const std::unique_ptr pPatternForm = pattern->Load(pPageObj); + if (!pPatternForm) + return; + + CFX_RenderDevice::StateRestorer restorer(m_pDevice); +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +- ScopedSkiaDeviceFlush scoped_skia_device_flush(m_pDevice); +-#endif +- if (!ClipPattern(pPageObj, mtObj2Device, bStroke)) ++ if (!ClipPattern(pPageObj, mtObj2Device, stroke)) + return; + + FX_RECT clip_box = m_pDevice->GetClipBox(); + if (clip_box.IsEmpty()) + return; + +- const CFX_Matrix mtPattern2Device = +- pPattern->pattern_to_form() * mtObj2Device; +- +- CFX_FloatRect cell_bbox = mtPattern2Device.TransformRect(pPattern->bbox()); +- +- float ceil_height = std::ceil(cell_bbox.Height()); +- float ceil_width = std::ceil(cell_bbox.Width()); +- +- // Validate the float will fit into the int when the conversion is done. +- if (!pdfium::base::IsValueInRangeForNumericType(ceil_height) || +- !pdfium::base::IsValueInRangeForNumericType(ceil_width)) { +- return; +- } +- +- int width = static_cast(ceil_width); +- int height = static_cast(ceil_height); +- if (width <= 0) +- width = 1; +- if (height <= 0) +- height = 1; +- +- CFX_FloatRect clip_box_p = +- mtPattern2Device.GetInverse().TransformRect(CFX_FloatRect(clip_box)); +- int min_col = static_cast( +- ceil((clip_box_p.left - pPattern->bbox().right) / pPattern->x_step())); +- int max_col = static_cast( +- floor((clip_box_p.right - pPattern->bbox().left) / pPattern->x_step())); +- int min_row = static_cast( +- ceil((clip_box_p.bottom - pPattern->bbox().top) / pPattern->y_step())); +- int max_row = static_cast( +- floor((clip_box_p.top - pPattern->bbox().bottom) / pPattern->y_step())); +- +- // Make sure we can fit the needed width * height into an int. +- if (height > std::numeric_limits::max() / width) +- return; +- +- if (width > clip_box.Width() || height > clip_box.Height() || +- width * height > clip_box.Width() * clip_box.Height()) { +- std::unique_ptr pStates; +- if (!pPattern->colored()) +- pStates = CloneObjStates(pPageObj, bStroke); +- +- const CPDF_Dictionary* pFormDict = pPatternForm->GetDict(); +- const CPDF_Dictionary* pFormResource = +- pFormDict ? pFormDict->GetDictFor("Resources") : nullptr; +- for (int col = min_col; col <= max_col; col++) { +- for (int row = min_row; row <= max_row; row++) { +- CFX_PointF original = mtPattern2Device.Transform( +- CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step())); +- CFX_Matrix matrix = mtObj2Device; +- matrix.Translate(original.x - mtPattern2Device.e, +- original.y - mtPattern2Device.f); +- CFX_RenderDevice::StateRestorer restorer2(m_pDevice); +- CPDF_RenderStatus status(m_pContext.Get(), m_pDevice); +- status.SetOptions(m_Options); +- status.SetTransparency(pPatternForm->GetTransparency()); +- status.SetFormResource(pFormResource); +- status.SetDropObjects(m_bDropObjects); +- status.Initialize(this, pStates.get()); +- status.RenderObjectList(pPatternForm.get(), matrix); +- } +- } +- return; +- } +- +- bool bAligned = +- pPattern->bbox().left == 0 && pPattern->bbox().bottom == 0 && +- pPattern->bbox().right == pPattern->x_step() && +- pPattern->bbox().top == pPattern->y_step() && +- (mtPattern2Device.IsScaled() || mtPattern2Device.Is90Rotated()); +- if (bAligned) { +- int orig_x = FXSYS_roundf(mtPattern2Device.e); +- int orig_y = FXSYS_roundf(mtPattern2Device.f); +- min_col = (clip_box.left - orig_x) / width; +- if (clip_box.left < orig_x) +- min_col--; +- +- max_col = (clip_box.right - orig_x) / width; +- if (clip_box.right <= orig_x) +- max_col--; +- +- min_row = (clip_box.top - orig_y) / height; +- if (clip_box.top < orig_y) +- min_row--; +- +- max_row = (clip_box.bottom - orig_y) / height; +- if (clip_box.bottom <= orig_y) +- max_row--; +- } +- float left_offset = cell_bbox.left - mtPattern2Device.e; +- float top_offset = cell_bbox.bottom - mtPattern2Device.f; +- RetainPtr pPatternBitmap; +- if (width * height < 16) { +- RetainPtr pEnlargedBitmap = DrawPatternBitmap( +- m_pContext->GetDocument(), m_pContext->GetPageCache(), pPattern, +- pPatternForm.get(), mtObj2Device, 8, 8, m_Options.GetOptions()); +- pPatternBitmap = pEnlargedBitmap->StretchTo( +- width, height, FXDIB_ResampleOptions(), nullptr); +- } else { +- pPatternBitmap = +- DrawPatternBitmap(m_pContext->GetDocument(), m_pContext->GetPageCache(), +- pPattern, pPatternForm.get(), mtObj2Device, width, +- height, m_Options.GetOptions()); +- } +- if (!pPatternBitmap) +- return; +- +- if (m_Options.ColorModeIs(CPDF_RenderOptions::kGray)) +- pPatternBitmap->ConvertColorScale(0, 0xffffff); +- +- FX_ARGB fill_argb = GetFillArgb(pPageObj); +- int clip_width = clip_box.right - clip_box.left; +- int clip_height = clip_box.bottom - clip_box.top; +- auto pScreen = pdfium::MakeRetain(); +- if (!pScreen->Create(clip_width, clip_height, FXDIB_Argb)) ++ RetainPtr pScreen = ++ CPDF_RenderTiling::Draw(this, pPageObj, pattern, pPatternForm.get(), ++ mtObj2Device, clip_box, stroke); ++ if (!pScreen) + return; + +- pScreen->Clear(0); +- const uint8_t* const src_buf = pPatternBitmap->GetBuffer(); +- for (int col = min_col; col <= max_col; col++) { +- for (int row = min_row; row <= max_row; row++) { +- int start_x; +- int start_y; +- if (bAligned) { +- start_x = +- FXSYS_roundf(mtPattern2Device.e) + col * width - clip_box.left; +- start_y = +- FXSYS_roundf(mtPattern2Device.f) + row * height - clip_box.top; +- } else { +- CFX_PointF original = mtPattern2Device.Transform( +- CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step())); +- +- pdfium::base::CheckedNumeric safeStartX = +- FXSYS_roundf(original.x + left_offset); +- pdfium::base::CheckedNumeric safeStartY = +- FXSYS_roundf(original.y + top_offset); +- +- safeStartX -= clip_box.left; +- safeStartY -= clip_box.top; +- if (!safeStartX.IsValid() || !safeStartY.IsValid()) +- return; +- +- start_x = safeStartX.ValueOrDie(); +- start_y = safeStartY.ValueOrDie(); +- } +- if (width == 1 && height == 1) { +- if (start_x < 0 || start_x >= clip_box.Width() || start_y < 0 || +- start_y >= clip_box.Height()) { +- continue; +- } +- uint32_t* dest_buf = reinterpret_cast( +- pScreen->GetBuffer() + pScreen->GetPitch() * start_y + start_x * 4); +- if (pPattern->colored()) { +- const auto* src_buf32 = reinterpret_cast(src_buf); +- *dest_buf = *src_buf32; +- } else { +- *dest_buf = (*src_buf << 24) | (fill_argb & 0xffffff); +- } +- } else { +- if (pPattern->colored()) { +- pScreen->CompositeBitmap(start_x, start_y, width, height, +- pPatternBitmap, 0, 0, BlendMode::kNormal, +- nullptr, false); +- } else { +- pScreen->CompositeMask(start_x, start_y, width, height, +- pPatternBitmap, fill_argb, 0, 0, +- BlendMode::kNormal, nullptr, false); +- } +- } +- } +- } + CompositeDIBitmap(pScreen, clip_box.left, clip_box.top, 0, 255, + BlendMode::kNormal, CPDF_Transparency()); + } + +-void CPDF_RenderStatus::DrawPathWithPattern(CPDF_PathObject* pPathObj, ++void CPDF_RenderStatus::DrawPathWithPattern(CPDF_PathObject* path_obj, + const CFX_Matrix& mtObj2Device, + const CPDF_Color* pColor, +- bool bStroke) { +- CPDF_Pattern* pattern = pColor->GetPattern(); ++ bool stroke) { ++ RetainPtr pattern = pColor->GetPattern(); + if (!pattern) + return; + + if (CPDF_TilingPattern* pTilingPattern = pattern->AsTilingPattern()) +- DrawTilingPattern(pTilingPattern, pPathObj, mtObj2Device, bStroke); ++ DrawTilingPattern(pTilingPattern, path_obj, mtObj2Device, stroke); + else if (CPDF_ShadingPattern* pShadingPattern = pattern->AsShadingPattern()) +- DrawShadingPattern(pShadingPattern, pPathObj, mtObj2Device, bStroke); ++ DrawShadingPattern(pShadingPattern, path_obj, mtObj2Device, stroke); + } + +-void CPDF_RenderStatus::ProcessPathPattern(CPDF_PathObject* pPathObj, +- const CFX_Matrix& mtObj2Device, +- int* filltype, +- bool* bStroke) { +- ASSERT(filltype); +- ASSERT(bStroke); ++void CPDF_RenderStatus::ProcessPathPattern( ++ CPDF_PathObject* path_obj, ++ const CFX_Matrix& mtObj2Device, ++ CFX_FillRenderOptions::FillType* fill_type, ++ bool* stroke) { ++ DCHECK(fill_type); ++ DCHECK(stroke); + +- if (*filltype) { +- const CPDF_Color& FillColor = *pPathObj->m_ColorState.GetFillColor(); ++ if (*fill_type != CFX_FillRenderOptions::FillType::kNoFill) { ++ const CPDF_Color& FillColor = *path_obj->m_ColorState.GetFillColor(); + if (FillColor.IsPattern()) { +- DrawPathWithPattern(pPathObj, mtObj2Device, &FillColor, false); +- *filltype = 0; ++ DrawPathWithPattern(path_obj, mtObj2Device, &FillColor, false); ++ *fill_type = CFX_FillRenderOptions::FillType::kNoFill; + } + } +- if (*bStroke) { +- const CPDF_Color& StrokeColor = *pPathObj->m_ColorState.GetStrokeColor(); ++ if (*stroke) { ++ const CPDF_Color& StrokeColor = *path_obj->m_ColorState.GetStrokeColor(); + if (StrokeColor.IsPattern()) { +- DrawPathWithPattern(pPathObj, mtObj2Device, &StrokeColor, true); +- *bStroke = false; ++ DrawPathWithPattern(path_obj, mtObj2Device, &StrokeColor, true); ++ *stroke = false; + } + } + } + + bool CPDF_RenderStatus::ProcessImage(CPDF_ImageObject* pImageObj, + const CFX_Matrix& mtObj2Device) { +- CPDF_ImageRenderer render; +- if (render.Start(this, pImageObj, mtObj2Device, m_bStdCS, m_curBlend)) ++ CPDF_ImageRenderer render(this); ++ if (render.Start(pImageObj, mtObj2Device, m_bStdCS, m_curBlend)) + render.Continue(nullptr); + return render.GetResult(); + } +@@ -1460,21 +1201,21 @@ void CPDF_RenderStatus::CompositeDIBitmap( + return; + + if (blend_mode == BlendMode::kNormal) { +- if (!pDIBitmap->IsAlphaMask()) { ++ if (!pDIBitmap->IsMaskFormat()) { + if (bitmap_alpha < 255) { +-#ifdef _SKIA_SUPPORT_ +- std::unique_ptr dummy; +- CFX_Matrix m = CFX_RenderDevice::GetFlipMatrix( +- pDIBitmap->GetWidth(), pDIBitmap->GetHeight(), left, top); +- m_pDevice->StartDIBits(pDIBitmap, bitmap_alpha, 0, m, +- FXDIB_ResampleOptions(), &dummy); +- return; +-#else ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ std::unique_ptr dummy; ++ CFX_Matrix m = CFX_RenderDevice::GetFlipMatrix( ++ pDIBitmap->GetWidth(), pDIBitmap->GetHeight(), left, top); ++ m_pDevice->StartDIBits(pDIBitmap, bitmap_alpha, 0, m, ++ FXDIB_ResampleOptions(), &dummy); ++ return; ++ } + pDIBitmap->MultiplyAlpha(bitmap_alpha); +-#endif + } +-#ifdef _SKIA_SUPPORT_ +- CFX_SkiaDeviceDriver::PreMultiply(pDIBitmap); ++#if defined(_SKIA_SUPPORT_) ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ pDIBitmap->PreMultiply(); + #endif + if (m_pDevice->SetDIBits(pDIBitmap, left, top)) { + return; +@@ -1499,7 +1240,7 @@ void CPDF_RenderStatus::CompositeDIBitmap( + (m_pDevice->GetRenderCaps() & FXRC_GET_BITS) && !bBackAlphaRequired); + if (bGetBackGround) { + if (bIsolated || !transparency.IsGroup()) { +- if (!pDIBitmap->IsAlphaMask()) ++ if (!pDIBitmap->IsMaskFormat()) + m_pDevice->SetDIBitsWithBlend(pDIBitmap, left, top, blend_mode); + return; + } +@@ -1509,7 +1250,7 @@ void CPDF_RenderStatus::CompositeDIBitmap( + rect.Intersect(m_pDevice->GetClipBox()); + RetainPtr pClone; + if (m_pDevice->GetBackDrop() && m_pDevice->GetBitmap()) { +- pClone = m_pDevice->GetBackDrop()->Clone(&rect); ++ pClone = m_pDevice->GetBackDrop()->ClipTo(rect); + if (!pClone) + return; + +@@ -1519,7 +1260,7 @@ void CPDF_RenderStatus::CompositeDIBitmap( + BlendMode::kNormal, nullptr, false); + left = std::min(left, 0); + top = std::min(top, 0); +- if (pDIBitmap->IsAlphaMask()) { ++ if (pDIBitmap->IsMaskFormat()) { + pClone->CompositeMask(0, 0, pClone->GetWidth(), pClone->GetHeight(), + pDIBitmap, mask_argb, left, top, blend_mode, + nullptr, false); +@@ -1534,124 +1275,116 @@ void CPDF_RenderStatus::CompositeDIBitmap( + if (m_pDevice->GetBackDrop()) { + m_pDevice->SetDIBits(pClone, rect.left, rect.top); + } else { +- if (!pDIBitmap->IsAlphaMask()) { ++ if (!pDIBitmap->IsMaskFormat()) { + m_pDevice->SetDIBitsWithBlend(pDIBitmap, rect.left, rect.top, + blend_mode); + } + } + return; + } +- int back_left; +- int back_top; +- FX_RECT rect(left, top, left + pDIBitmap->GetWidth(), +- top + pDIBitmap->GetHeight()); ++ FX_RECT bbox = GetClippedBBox(FX_RECT(left, top, left + pDIBitmap->GetWidth(), ++ top + pDIBitmap->GetHeight())); + RetainPtr pBackdrop = GetBackdrop( +- m_pCurObj.Get(), rect, blend_mode != BlendMode::kNormal && bIsolated, +- &back_left, &back_top); ++ m_pCurObj, bbox, blend_mode != BlendMode::kNormal && bIsolated); + if (!pBackdrop) + return; + +- if (pDIBitmap->IsAlphaMask()) { +- pBackdrop->CompositeMask(left - back_left, top - back_top, ++ if (pDIBitmap->IsMaskFormat()) { ++ pBackdrop->CompositeMask(left - bbox.left, top - bbox.top, + pDIBitmap->GetWidth(), pDIBitmap->GetHeight(), + pDIBitmap, mask_argb, 0, 0, blend_mode, nullptr, + false); + } else { +- pBackdrop->CompositeBitmap(left - back_left, top - back_top, ++ pBackdrop->CompositeBitmap(left - bbox.left, top - bbox.top, + pDIBitmap->GetWidth(), pDIBitmap->GetHeight(), + pDIBitmap, 0, 0, blend_mode, nullptr, false); + } + + auto pBackdrop1 = pdfium::MakeRetain(); + pBackdrop1->Create(pBackdrop->GetWidth(), pBackdrop->GetHeight(), +- FXDIB_Rgb32); ++ FXDIB_Format::kRgb32); + pBackdrop1->Clear((uint32_t)-1); + pBackdrop1->CompositeBitmap(0, 0, pBackdrop->GetWidth(), + pBackdrop->GetHeight(), pBackdrop, 0, 0, + BlendMode::kNormal, nullptr, false); + pBackdrop = std::move(pBackdrop1); +- m_pDevice->SetDIBits(pBackdrop, back_left, back_top); ++ m_pDevice->SetDIBits(pBackdrop, bbox.left, bbox.top); + } + + RetainPtr CPDF_RenderStatus::LoadSMask( + CPDF_Dictionary* pSMaskDict, + FX_RECT* pClipRect, +- const CFX_Matrix* pMatrix) { ++ const CFX_Matrix& mtMatrix) { + if (!pSMaskDict) + return nullptr; + +- CPDF_Stream* pGroup = pSMaskDict->GetStreamFor(pdfium::transparency::kG); ++ RetainPtr pGroup = ++ pSMaskDict->GetMutableStreamFor(pdfium::transparency::kG); + if (!pGroup) + return nullptr; + + std::unique_ptr pFunc; +- const CPDF_Object* pFuncObj = ++ RetainPtr pFuncObj = + pSMaskDict->GetDirectObjectFor(pdfium::transparency::kTR); + if (pFuncObj && (pFuncObj->IsDictionary() || pFuncObj->IsStream())) +- pFunc = CPDF_Function::Load(pFuncObj); ++ pFunc = CPDF_Function::Load(std::move(pFuncObj)); + +- CFX_Matrix matrix = *pMatrix; ++ CFX_Matrix matrix = mtMatrix; + matrix.Translate(-pClipRect->left, -pClipRect->top); + +- CPDF_Form form(m_pContext->GetDocument(), m_pContext->GetPageResources(), +- pGroup); ++ CPDF_Form form(m_pContext->GetDocument(), ++ m_pContext->GetMutablePageResources(), pGroup); + form.ParseContent(); + + CFX_DefaultRenderDevice bitmap_device; + bool bLuminosity = +- pSMaskDict->GetStringFor(pdfium::transparency::kSoftMaskSubType) != ++ pSMaskDict->GetByteStringFor(pdfium::transparency::kSoftMaskSubType) != + pdfium::transparency::kAlpha; + int width = pClipRect->right - pClipRect->left; + int height = pClipRect->bottom - pClipRect->top; +- FXDIB_Format format; +-#if defined(OS_MACOSX) || defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ +- format = bLuminosity ? FXDIB_Rgb32 : FXDIB_8bppMask; +-#else +- format = bLuminosity ? FXDIB_Rgb : FXDIB_8bppMask; +-#endif ++ FXDIB_Format format = GetFormatForLuminosity(bLuminosity); + if (!bitmap_device.Create(width, height, format, nullptr)) + return nullptr; + + RetainPtr bitmap = bitmap_device.GetBitmap(); +- int nCSFamily = 0; ++ CPDF_ColorSpace::Family nCSFamily = CPDF_ColorSpace::Family::kUnknown; + if (bLuminosity) { + FX_ARGB back_color = +- GetBackColor(pSMaskDict, pGroup->GetDict(), &nCSFamily); ++ GetBackColor(pSMaskDict, pGroup->GetDict().Get(), &nCSFamily); + bitmap->Clear(back_color); + } else { + bitmap->Clear(0); + } + +- const CPDF_Dictionary* pFormResource = ++ RetainPtr pFormResource = + form.GetDict()->GetDictFor("Resources"); + CPDF_RenderOptions options; + options.SetColorMode(bLuminosity ? CPDF_RenderOptions::kNormal + : CPDF_RenderOptions::kAlpha); +- CPDF_RenderStatus status(m_pContext.Get(), &bitmap_device); ++ CPDF_RenderStatus status(m_pContext, &bitmap_device); + status.SetOptions(options); + status.SetGroupFamily(nCSFamily); + status.SetLoadMask(bLuminosity); + status.SetStdCS(true); +- status.SetFormResource(pFormResource); ++ status.SetFormResource(std::move(pFormResource)); + status.SetDropObjects(m_bDropObjects); + status.Initialize(nullptr, nullptr); + status.RenderObjectList(&form, matrix); + + auto pMask = pdfium::MakeRetain(); +- if (!pMask->Create(width, height, FXDIB_8bppMask)) ++ if (!pMask->Create(width, height, FXDIB_Format::k8bppMask)) + return nullptr; + +- uint8_t* dest_buf = pMask->GetBuffer(); ++ pdfium::span dest_buf = pMask->GetBuffer(); ++ pdfium::span src_buf = bitmap->GetBuffer(); + int dest_pitch = pMask->GetPitch(); +- uint8_t* src_buf = bitmap->GetBuffer(); + int src_pitch = bitmap->GetPitch(); +- std::vector transfers(256); ++ DataVector transfers(256); + if (pFunc) { + std::vector results(pFunc->CountOutputs()); + for (size_t i = 0; i < transfers.size(); ++i) { + float input = i / 255.0f; +- int nresult; +- pFunc->Call(&input, 1, results.data(), &nresult); ++ pFunc->Call(pdfium::make_span(&input, 1), results); + transfers[i] = FXSYS_roundf(results[0] * 255); + } + } else { +@@ -1659,10 +1392,12 @@ RetainPtr CPDF_RenderStatus::LoadSMask( + std::iota(transfers.begin(), transfers.end(), 0); + } + if (bLuminosity) { +- int Bpp = bitmap->GetBPP() / 8; ++ const int Bpp = bitmap->GetBPP() / 8; + for (int row = 0; row < height; row++) { +- uint8_t* dest_pos = dest_buf + row * dest_pitch; +- uint8_t* src_pos = src_buf + row * src_pitch; ++ const size_t dest_offset = Fx2DSizeOrDie(row, dest_pitch); ++ const size_t src_offset = Fx2DSizeOrDie(row, src_pitch); ++ uint8_t* dest_pos = dest_buf.subspan(dest_offset).data(); ++ const uint8_t* src_pos = src_buf.subspan(src_offset).data(); + for (int col = 0; col < width; col++) { + *dest_pos++ = transfers[FXRGB2GRAY(src_pos[2], src_pos[1], *src_pos)]; + src_pos += Bpp; +@@ -1674,33 +1409,34 @@ RetainPtr CPDF_RenderStatus::LoadSMask( + dest_buf[i] = transfers[src_buf[i]]; + } + } else { +- memcpy(dest_buf, src_buf, dest_pitch * height); ++ fxcrt::spancpy(dest_buf, src_buf.first(dest_pitch * height)); + } + return pMask; + } + + FX_ARGB CPDF_RenderStatus::GetBackColor(const CPDF_Dictionary* pSMaskDict, + const CPDF_Dictionary* pGroupDict, +- int* pCSFamily) { ++ CPDF_ColorSpace::Family* pCSFamily) { + static constexpr FX_ARGB kDefaultColor = ArgbEncode(255, 0, 0, 0); +- const CPDF_Array* pBC = pSMaskDict->GetArrayFor(pdfium::transparency::kBC); ++ RetainPtr pBC = ++ pSMaskDict->GetArrayFor(pdfium::transparency::kBC); + if (!pBC) + return kDefaultColor; + +- const CPDF_Object* pCSObj = nullptr; +- const CPDF_Dictionary* pGroup = ++ RetainPtr pCSObj; ++ RetainPtr pGroup = + pGroupDict ? pGroupDict->GetDictFor("Group") : nullptr; + if (pGroup) + pCSObj = pGroup->GetDirectObjectFor(pdfium::transparency::kCS); + RetainPtr pCS = + CPDF_DocPageData::FromDocument(m_pContext->GetDocument()) +- ->GetColorSpace(pCSObj, nullptr); ++ ->GetColorSpace(pCSObj.Get(), nullptr); + if (!pCS) + return kDefaultColor; + +- int family = pCS->GetFamily(); +- if (family == PDFCS_LAB || pCS->IsSpecial() || +- (family == PDFCS_ICCBASED && !pCS->IsNormal())) { ++ CPDF_ColorSpace::Family family = pCS->GetFamily(); ++ if (family == CPDF_ColorSpace::Family::kLab || pCS->IsSpecial() || ++ (family == CPDF_ColorSpace::Family::kICCBased && !pCS->IsNormal())) { + return kDefaultColor; + } + +@@ -1709,13 +1445,13 @@ FX_ARGB CPDF_RenderStatus::GetBackColor(const CPDF_Dictionary* pSMaskDict, + + uint32_t comps = std::max(8u, pCS->CountComponents()); + size_t count = std::min(8, pBC->size()); +- std::vector floats = ReadArrayElementsToVector(pBC, count); ++ std::vector floats = ReadArrayElementsToVector(pBC.Get(), count); + floats.resize(comps); + + float R; + float G; + float B; +- pCS->GetRGB(floats.data(), &R, &G, &B); ++ pCS->GetRGB(floats, &R, &G, &B); + return ArgbEncode(255, static_cast(R * 255), static_cast(G * 255), + static_cast(B * 255)); + } +diff --git a/core/fpdfapi/render/cpdf_renderstatus.h b/core/fpdfapi/render/cpdf_renderstatus.h +index 76eb71c3a..76596f044 100644 +--- a/core/fpdfapi/render/cpdf_renderstatus.h ++++ b/core/fpdfapi/render/cpdf_renderstatus.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,24 +8,25 @@ + #define CORE_FPDFAPI_RENDER_CPDF_RENDERSTATUS_H_ + + #include ++#include + #include + + #include "core/fpdfapi/page/cpdf_clippath.h" ++#include "core/fpdfapi/page/cpdf_colorspace.h" + #include "core/fpdfapi/page/cpdf_graphicstates.h" + #include "core/fpdfapi/page/cpdf_transparency.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/render/cpdf_renderoptions.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" + + class CFX_DIBitmap; +-class CFX_PathData; ++class CFX_Path; + class CFX_RenderDevice; + class CPDF_Color; + class CPDF_Font; + class CPDF_FormObject; +-class CPDF_ImageCacheEntry; + class CPDF_ImageObject; + class CPDF_ImageRenderer; + class CPDF_Object; +@@ -37,7 +38,6 @@ class CPDF_ShadingObject; + class CPDF_ShadingPattern; + class CPDF_TilingPattern; + class CPDF_TransferFunc; +-class CPDF_Type3Cache; + class CPDF_Type3Char; + class CPDF_Type3Font; + class PauseIndicatorIface; +@@ -51,15 +51,17 @@ class CPDF_RenderStatus { + void SetOptions(const CPDF_RenderOptions& options) { m_Options = options; } + void SetDeviceMatrix(const CFX_Matrix& matrix) { m_DeviceMatrix = matrix; } + void SetStopObject(const CPDF_PageObject* pStopObj) { m_pStopObj = pStopObj; } +- void SetFormResource(const CPDF_Dictionary* pRes) { +- m_pFormResource.Reset(pRes); ++ void SetFormResource(RetainPtr pRes) { ++ m_pFormResource = std::move(pRes); + } + void SetType3Char(CPDF_Type3Char* pType3Char) { m_pType3Char = pType3Char; } + void SetFillColor(FX_ARGB color) { m_T3FillColor = color; } + void SetDropObjects(bool bDropObjects) { m_bDropObjects = bDropObjects; } + void SetLoadMask(bool bLoadMask) { m_bLoadMask = bLoadMask; } + void SetStdCS(bool bStdCS) { m_bStdCS = bStdCS; } +- void SetGroupFamily(uint32_t family) { m_GroupFamily = family; } ++ void SetGroupFamily(CPDF_ColorSpace::Family family) { ++ m_GroupFamily = family; ++ } + void SetTransparency(const CPDF_Transparency& transparency) { + m_Transparency = transparency; + } +@@ -77,41 +79,39 @@ class CPDF_RenderStatus { + void ProcessClipPath(const CPDF_ClipPath& ClipPath, + const CFX_Matrix& mtObj2Device); + +- uint32_t GetGroupFamily() const { return m_GroupFamily; } ++ CPDF_ColorSpace::Family GetGroupFamily() const { return m_GroupFamily; } + bool GetLoadMask() const { return m_bLoadMask; } + bool GetDropObjects() const { return m_bDropObjects; } + bool IsPrint() const { return m_bPrint; } + bool IsStopped() const { return m_bStopped; } +- CPDF_RenderContext* GetContext() const { return m_pContext.Get(); } ++ CPDF_RenderContext* GetContext() const { return m_pContext; } + const CPDF_Dictionary* GetFormResource() const { + return m_pFormResource.Get(); + } +- CPDF_Dictionary* GetPageResource() const { return m_pPageResource.Get(); } ++ const CPDF_Dictionary* GetPageResource() const { ++ return m_pPageResource.Get(); ++ } + CFX_RenderDevice* GetRenderDevice() const { return m_pDevice; } + const CPDF_RenderOptions& GetRenderOptions() const { return m_Options; } + +-#if defined _SKIA_SUPPORT_ ++#if defined(_SKIA_SUPPORT_) + void DebugVerifyDeviceIsPreMultiplied() const; + #endif + + RetainPtr GetTransferFunc( +- const CPDF_Object* pObject) const; ++ RetainPtr pObject) const; + +- FX_ARGB GetFillArgb(CPDF_PageObject* pObj) const { +- return GetFillArgbInternal(pObj, false); +- } +- FX_ARGB GetFillArgbForType3(CPDF_PageObject* pObj) const { +- return GetFillArgbInternal(pObj, true); +- } ++ FX_ARGB GetFillArgb(CPDF_PageObject* pObj) const; ++ FX_ARGB GetFillArgbForType3(CPDF_PageObject* pObj) const; + +- void DrawTilingPattern(CPDF_TilingPattern* pPattern, ++ void DrawTilingPattern(CPDF_TilingPattern* pattern, + CPDF_PageObject* pPageObj, + const CFX_Matrix& mtObj2Device, +- bool bStroke); +- void DrawShadingPattern(CPDF_ShadingPattern* pPattern, ++ bool stroke); ++ void DrawShadingPattern(CPDF_ShadingPattern* pattern, + const CPDF_PageObject* pPageObj, + const CFX_Matrix& mtObj2Device, +- bool bStroke); ++ bool stroke); + void CompositeDIBitmap(const RetainPtr& pDIBitmap, + int left, + int top, +@@ -120,12 +120,11 @@ class CPDF_RenderStatus { + BlendMode blend_mode, + const CPDF_Transparency& transparency); + +- private: + static std::unique_ptr CloneObjStates( + const CPDF_GraphicStates* pSrcStates, +- bool bStroke); ++ bool stroke); + +- FX_ARGB GetFillArgbInternal(CPDF_PageObject* pObj, bool bType3) const; ++ private: + bool ProcessTransparency(CPDF_PageObject* PageObj, + const CFX_Matrix& mtObj2Device); + void ProcessObjectNoClip(CPDF_PageObject* pObj, +@@ -133,21 +132,21 @@ class CPDF_RenderStatus { + void DrawObjWithBackground(CPDF_PageObject* pObj, + const CFX_Matrix& mtObj2Device); + bool DrawObjWithBlend(CPDF_PageObject* pObj, const CFX_Matrix& mtObj2Device); +- bool ProcessPath(CPDF_PathObject* pPathObj, const CFX_Matrix& mtObj2Device); +- void ProcessPathPattern(CPDF_PathObject* pPathObj, ++ bool ProcessPath(CPDF_PathObject* path_obj, const CFX_Matrix& mtObj2Device); ++ void ProcessPathPattern(CPDF_PathObject* path_obj, + const CFX_Matrix& mtObj2Device, +- int* filltype, +- bool* bStroke); +- void DrawPathWithPattern(CPDF_PathObject* pPathObj, ++ CFX_FillRenderOptions::FillType* fill_type, ++ bool* stroke); ++ void DrawPathWithPattern(CPDF_PathObject* path_obj, + const CFX_Matrix& mtObj2Device, + const CPDF_Color* pColor, +- bool bStroke); +- bool ClipPattern(const CPDF_PageObject* pPageObj, ++ bool stroke); ++ bool ClipPattern(const CPDF_PageObject* page_obj, + const CFX_Matrix& mtObj2Device, +- bool bStroke); +- bool SelectClipPath(const CPDF_PathObject* pPathObj, ++ bool stroke); ++ bool SelectClipPath(const CPDF_PathObject* path_obj, + const CFX_Matrix& mtObj2Device, +- bool bStroke); ++ bool stroke); + bool ProcessImage(CPDF_ImageObject* pImageObj, + const CFX_Matrix& mtObj2Device); + void ProcessShading(const CPDF_ShadingObject* pShadingObj, +@@ -156,52 +155,51 @@ class CPDF_RenderStatus { + const CFX_Matrix& mtObj2Device); + bool ProcessText(CPDF_TextObject* textobj, + const CFX_Matrix& mtObj2Device, +- CFX_PathData* pClippingPath); ++ CFX_Path* clipping_path); + void DrawTextPathWithPattern(const CPDF_TextObject* textobj, + const CFX_Matrix& mtObj2Device, + CPDF_Font* pFont, + float font_size, +- const CFX_Matrix* pTextMatrix, +- bool bFill, +- bool bStroke); ++ const CFX_Matrix& mtTextMatrix, ++ bool fill, ++ bool stroke); + bool ProcessForm(const CPDF_FormObject* pFormObj, + const CFX_Matrix& mtObj2Device); ++ FX_RECT GetClippedBBox(const FX_RECT& rect) const; + RetainPtr GetBackdrop(const CPDF_PageObject* pObj, +- const FX_RECT& rect, +- bool bBackAlphaRequired, +- int* left, +- int* top); ++ const FX_RECT& bbox, ++ bool bBackAlphaRequired); + RetainPtr LoadSMask(CPDF_Dictionary* pSMaskDict, + FX_RECT* pClipRect, +- const CFX_Matrix* pMatrix); ++ const CFX_Matrix& mtMatrix); + // Optionally write the colorspace family value into |pCSFamily|. + FX_ARGB GetBackColor(const CPDF_Dictionary* pSMaskDict, + const CPDF_Dictionary* pGroupDict, +- int* pCSFamily); ++ CPDF_ColorSpace::Family* pCSFamily); + FX_ARGB GetStrokeArgb(CPDF_PageObject* pObj) const; + FX_RECT GetObjectClippedRect(const CPDF_PageObject* pObj, + const CFX_Matrix& mtObj2Device) const; + + CPDF_RenderOptions m_Options; + RetainPtr m_pFormResource; +- RetainPtr m_pPageResource; +- std::vector m_Type3FontCache; ++ RetainPtr m_pPageResource; ++ std::vector> m_Type3FontCache; + UnownedPtr const m_pContext; +- bool m_bStopped = false; +- CFX_RenderDevice* const m_pDevice; ++ UnownedPtr const m_pDevice; + CFX_Matrix m_DeviceMatrix; + CPDF_ClipPath m_LastClipPath; + UnownedPtr m_pCurObj; + UnownedPtr m_pStopObj; + CPDF_GraphicStates m_InitialStates; + std::unique_ptr m_pImageRenderer; ++ UnownedPtr m_pType3Char; + CPDF_Transparency m_Transparency; ++ bool m_bStopped = false; + bool m_bPrint = false; + bool m_bDropObjects = false; + bool m_bStdCS = false; + bool m_bLoadMask = false; +- uint32_t m_GroupFamily = 0; +- UnownedPtr m_pType3Char; ++ CPDF_ColorSpace::Family m_GroupFamily = CPDF_ColorSpace::Family::kUnknown; + FX_ARGB m_T3FillColor = 0; + BlendMode m_curBlend = BlendMode::kNormal; + }; +diff --git a/core/fpdfapi/render/cpdf_rendertiling.cpp b/core/fpdfapi/render/cpdf_rendertiling.cpp +new file mode 100644 +index 000000000..fcde2b2ea +--- /dev/null ++++ b/core/fpdfapi/render/cpdf_rendertiling.cpp +@@ -0,0 +1,250 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fpdfapi/render/cpdf_rendertiling.h" ++ ++#include ++#include ++ ++#include "core/fpdfapi/page/cpdf_form.h" ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" ++#include "core/fpdfapi/page/cpdf_tilingpattern.h" ++#include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fpdfapi/render/cpdf_rendercontext.h" ++#include "core/fpdfapi/render/cpdf_renderoptions.h" ++#include "core/fpdfapi/render/cpdf_renderstatus.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" ++ ++namespace { ++ ++RetainPtr DrawPatternBitmap( ++ CPDF_Document* pDoc, ++ CPDF_PageImageCache* pCache, ++ CPDF_TilingPattern* pPattern, ++ CPDF_Form* pPatternForm, ++ const CFX_Matrix& mtObject2Device, ++ int width, ++ int height, ++ const CPDF_RenderOptions::Options& draw_options) { ++ auto pBitmap = pdfium::MakeRetain(); ++ if (!pBitmap->Create(width, height, ++ pPattern->colored() ? FXDIB_Format::kArgb ++ : FXDIB_Format::k8bppMask)) { ++ return nullptr; ++ } ++ CFX_DefaultRenderDevice bitmap_device; ++ bitmap_device.AttachWithBackdropAndGroupKnockout( ++ pBitmap, /*pBackdropBitmap=*/nullptr, /*bGroupKnockout=*/true); ++ CFX_FloatRect cell_bbox = ++ pPattern->pattern_to_form().TransformRect(pPattern->bbox()); ++ cell_bbox = mtObject2Device.TransformRect(cell_bbox); ++ CFX_FloatRect bitmap_rect(0.0f, 0.0f, width, height); ++ CFX_Matrix mtAdjust; ++ mtAdjust.MatchRect(bitmap_rect, cell_bbox); ++ ++ CFX_Matrix mtPattern2Bitmap = mtObject2Device * mtAdjust; ++ CPDF_RenderOptions options; ++ if (!pPattern->colored()) ++ options.SetColorMode(CPDF_RenderOptions::kAlpha); ++ ++ options.GetOptions() = draw_options; ++ options.GetOptions().bForceHalftone = true; ++ ++ CPDF_RenderContext context(pDoc, nullptr, pCache); ++ context.AppendLayer(pPatternForm, mtPattern2Bitmap); ++ context.Render(&bitmap_device, nullptr, &options, nullptr); ++ ++#if defined(_SKIA_SUPPORT_) ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ pBitmap->UnPreMultiply(); ++#endif // defined(_SKIA_SUPPORT_) ++ return pBitmap; ++} ++ ++} // namespace ++ ++// static ++RetainPtr CPDF_RenderTiling::Draw( ++ CPDF_RenderStatus* pRenderStatus, ++ CPDF_PageObject* pPageObj, ++ CPDF_TilingPattern* pPattern, ++ CPDF_Form* pPatternForm, ++ const CFX_Matrix& mtObj2Device, ++ const FX_RECT& clip_box, ++ bool bStroke) { ++ const CFX_Matrix mtPattern2Device = ++ pPattern->pattern_to_form() * mtObj2Device; ++ ++ CFX_FloatRect cell_bbox = mtPattern2Device.TransformRect(pPattern->bbox()); ++ ++ float ceil_height = std::ceil(cell_bbox.Height()); ++ float ceil_width = std::ceil(cell_bbox.Width()); ++ ++ // Validate the float will fit into the int when the conversion is done. ++ if (!pdfium::base::IsValueInRangeForNumericType(ceil_height) || ++ !pdfium::base::IsValueInRangeForNumericType(ceil_width)) { ++ return nullptr; ++ } ++ ++ int width = static_cast(ceil_width); ++ int height = static_cast(ceil_height); ++ if (width <= 0) ++ width = 1; ++ if (height <= 0) ++ height = 1; ++ ++ CFX_FloatRect clip_box_p = ++ mtPattern2Device.GetInverse().TransformRect(CFX_FloatRect(clip_box)); ++ int min_col = static_cast( ++ ceil((clip_box_p.left - pPattern->bbox().right) / pPattern->x_step())); ++ int max_col = static_cast( ++ floor((clip_box_p.right - pPattern->bbox().left) / pPattern->x_step())); ++ int min_row = static_cast( ++ ceil((clip_box_p.bottom - pPattern->bbox().top) / pPattern->y_step())); ++ int max_row = static_cast( ++ floor((clip_box_p.top - pPattern->bbox().bottom) / pPattern->y_step())); ++ ++ // Make sure we can fit the needed width * height into an int. ++ if (height > std::numeric_limits::max() / width) ++ return nullptr; ++ ++ CFX_RenderDevice* pDevice = pRenderStatus->GetRenderDevice(); ++ CPDF_RenderContext* pContext = pRenderStatus->GetContext(); ++ const CPDF_RenderOptions& options = pRenderStatus->GetRenderOptions(); ++ if (width > clip_box.Width() || height > clip_box.Height() || ++ width * height > clip_box.Width() * clip_box.Height()) { ++ std::unique_ptr pStates; ++ if (!pPattern->colored()) ++ pStates = CPDF_RenderStatus::CloneObjStates(pPageObj, bStroke); ++ ++ RetainPtr pFormResource = ++ pPatternForm->GetDict()->GetDictFor("Resources"); ++ for (int col = min_col; col <= max_col; col++) { ++ for (int row = min_row; row <= max_row; row++) { ++ CFX_PointF original = mtPattern2Device.Transform( ++ CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step())); ++ CFX_Matrix matrix = mtObj2Device; ++ matrix.Translate(original.x - mtPattern2Device.e, ++ original.y - mtPattern2Device.f); ++ CFX_RenderDevice::StateRestorer restorer2(pDevice); ++ CPDF_RenderStatus status(pContext, pDevice); ++ status.SetOptions(options); ++ status.SetTransparency(pPatternForm->GetTransparency()); ++ status.SetFormResource(pFormResource); ++ status.SetDropObjects(pRenderStatus->GetDropObjects()); ++ status.Initialize(pRenderStatus, pStates.get()); ++ status.RenderObjectList(pPatternForm, matrix); ++ } ++ } ++ return nullptr; ++ } ++ ++ bool bAligned = ++ pPattern->bbox().left == 0 && pPattern->bbox().bottom == 0 && ++ pPattern->bbox().right == pPattern->x_step() && ++ pPattern->bbox().top == pPattern->y_step() && ++ (mtPattern2Device.IsScaled() || mtPattern2Device.Is90Rotated()); ++ if (bAligned) { ++ int orig_x = FXSYS_roundf(mtPattern2Device.e); ++ int orig_y = FXSYS_roundf(mtPattern2Device.f); ++ min_col = (clip_box.left - orig_x) / width; ++ if (clip_box.left < orig_x) ++ min_col--; ++ ++ max_col = (clip_box.right - orig_x) / width; ++ if (clip_box.right <= orig_x) ++ max_col--; ++ ++ min_row = (clip_box.top - orig_y) / height; ++ if (clip_box.top < orig_y) ++ min_row--; ++ ++ max_row = (clip_box.bottom - orig_y) / height; ++ if (clip_box.bottom <= orig_y) ++ max_row--; ++ } ++ float left_offset = cell_bbox.left - mtPattern2Device.e; ++ float top_offset = cell_bbox.bottom - mtPattern2Device.f; ++ RetainPtr pPatternBitmap; ++ if (width * height < 16) { ++ RetainPtr pEnlargedBitmap = DrawPatternBitmap( ++ pContext->GetDocument(), pContext->GetPageCache(), pPattern, ++ pPatternForm, mtObj2Device, 8, 8, options.GetOptions()); ++ pPatternBitmap = pEnlargedBitmap->StretchTo( ++ width, height, FXDIB_ResampleOptions(), nullptr); ++ } else { ++ pPatternBitmap = DrawPatternBitmap( ++ pContext->GetDocument(), pContext->GetPageCache(), pPattern, ++ pPatternForm, mtObj2Device, width, height, options.GetOptions()); ++ } ++ if (!pPatternBitmap) ++ return nullptr; ++ ++ if (options.ColorModeIs(CPDF_RenderOptions::kGray)) ++ pPatternBitmap->ConvertColorScale(0, 0xffffff); ++ ++ FX_ARGB fill_argb = pRenderStatus->GetFillArgb(pPageObj); ++ int clip_width = clip_box.right - clip_box.left; ++ int clip_height = clip_box.bottom - clip_box.top; ++ auto pScreen = pdfium::MakeRetain(); ++ if (!pScreen->Create(clip_width, clip_height, FXDIB_Format::kArgb)) ++ return nullptr; ++ ++ pdfium::span src_buf = pPatternBitmap->GetBuffer(); ++ for (int col = min_col; col <= max_col; col++) { ++ for (int row = min_row; row <= max_row; row++) { ++ int start_x; ++ int start_y; ++ if (bAligned) { ++ start_x = ++ FXSYS_roundf(mtPattern2Device.e) + col * width - clip_box.left; ++ start_y = ++ FXSYS_roundf(mtPattern2Device.f) + row * height - clip_box.top; ++ } else { ++ CFX_PointF original = mtPattern2Device.Transform( ++ CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step())); ++ ++ FX_SAFE_INT32 safeStartX = FXSYS_roundf(original.x + left_offset); ++ FX_SAFE_INT32 safeStartY = FXSYS_roundf(original.y + top_offset); ++ ++ safeStartX -= clip_box.left; ++ safeStartY -= clip_box.top; ++ if (!safeStartX.IsValid() || !safeStartY.IsValid()) ++ return nullptr; ++ ++ start_x = safeStartX.ValueOrDie(); ++ start_y = safeStartY.ValueOrDie(); ++ } ++ if (width == 1 && height == 1) { ++ if (start_x < 0 || start_x >= clip_box.Width() || start_y < 0 || ++ start_y >= clip_box.Height()) { ++ continue; ++ } ++ uint32_t* dest_buf = reinterpret_cast( ++ pScreen->GetWritableScanline(start_y).subspan(start_x * 4).data()); ++ if (pPattern->colored()) { ++ const auto* src_buf32 = ++ reinterpret_cast(src_buf.data()); ++ *dest_buf = *src_buf32; ++ } else { ++ *dest_buf = (*(src_buf.data()) << 24) | (fill_argb & 0xffffff); ++ } ++ } else { ++ if (pPattern->colored()) { ++ pScreen->CompositeBitmap(start_x, start_y, width, height, ++ pPatternBitmap, 0, 0, BlendMode::kNormal, ++ nullptr, false); ++ } else { ++ pScreen->CompositeMask(start_x, start_y, width, height, ++ pPatternBitmap, fill_argb, 0, 0, ++ BlendMode::kNormal, nullptr, false); ++ } ++ } ++ } ++ } ++ return pScreen; ++} +diff --git a/core/fpdfapi/render/cpdf_rendertiling.h b/core/fpdfapi/render/cpdf_rendertiling.h +new file mode 100644 +index 000000000..b687f7490 +--- /dev/null ++++ b/core/fpdfapi/render/cpdf_rendertiling.h +@@ -0,0 +1,35 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FPDFAPI_RENDER_CPDF_RENDERTILING_H_ ++#define CORE_FPDFAPI_RENDER_CPDF_RENDERTILING_H_ ++ ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxge/dib/cfx_dibitmap.h" ++ ++class CFX_Matrix; ++class CPDF_Form; ++class CPDF_PageObject; ++class CPDF_RenderStatus; ++class CPDF_TilingPattern; ++struct FX_RECT; ++ ++class CPDF_RenderTiling { ++ public: ++ static RetainPtr Draw(CPDF_RenderStatus* pRenderStatus, ++ CPDF_PageObject* pPageObj, ++ CPDF_TilingPattern* pPattern, ++ CPDF_Form* pPatternForm, ++ const CFX_Matrix& mtObj2Device, ++ const FX_RECT& clip_box, ++ bool bStroke); ++ ++ CPDF_RenderTiling() = delete; ++ CPDF_RenderTiling(const CPDF_RenderTiling&) = delete; ++ CPDF_RenderTiling& operator=(const CPDF_RenderTiling&) = delete; ++}; ++ ++#endif // CORE_FPDFAPI_RENDER_CPDF_RENDERTILING_H_ +diff --git a/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp b/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp +index 8536b3b1e..26e8b7ff4 100644 +--- a/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp ++++ b/core/fpdfapi/render/cpdf_scaledrenderbuffer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,11 +6,11 @@ + + #include "core/fpdfapi/render/cpdf_scaledrenderbuffer.h" + ++#include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/render/cpdf_devicebuffer.h" + #include "core/fpdfapi/render/cpdf_rendercontext.h" + #include "core/fxge/cfx_defaultrenderdevice.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "third_party/base/ptr_util.h" + + namespace { + +@@ -18,9 +18,9 @@ constexpr size_t kImageSizeLimitBytes = 30 * 1024 * 1024; + + } // namespace + +-CPDF_ScaledRenderBuffer::CPDF_ScaledRenderBuffer() {} ++CPDF_ScaledRenderBuffer::CPDF_ScaledRenderBuffer() = default; + +-CPDF_ScaledRenderBuffer::~CPDF_ScaledRenderBuffer() {} ++CPDF_ScaledRenderBuffer::~CPDF_ScaledRenderBuffer() = default; + + bool CPDF_ScaledRenderBuffer::Initialize(CPDF_RenderContext* pContext, + CFX_RenderDevice* pDevice, +@@ -32,41 +32,39 @@ bool CPDF_ScaledRenderBuffer::Initialize(CPDF_RenderContext* pContext, + if (m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_GET_BITS) + return true; + +- m_pContext = pContext; + m_Rect = rect; +- m_pObject = pObj; + m_Matrix = CPDF_DeviceBuffer::CalculateMatrix(pDevice, rect, max_dpi, + /*scale=*/true); +- m_pBitmapDevice = pdfium::MakeUnique(); ++ m_pBitmapDevice = std::make_unique(); + bool bIsAlpha = + !!(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_ALPHA_OUTPUT); +- FXDIB_Format dibFormat = bIsAlpha ? FXDIB_Argb : FXDIB_Rgb; +- while (1) { ++ FXDIB_Format dibFormat = bIsAlpha ? FXDIB_Format::kArgb : FXDIB_Format::kRgb; ++ while (true) { + FX_RECT bitmap_rect = + m_Matrix.TransformRect(CFX_FloatRect(rect)).GetOuterRect(); + int32_t width = bitmap_rect.Width(); + int32_t height = bitmap_rect.Height(); + // Set to 0 to make CalculatePitchAndSize() calculate it. +- uint32_t pitch = 0; +- uint32_t size; +- if (!CFX_DIBitmap::CalculatePitchAndSize(width, height, dibFormat, &pitch, +- &size)) { ++ constexpr uint32_t kNoPitch = 0; ++ absl::optional pitch_size = ++ CFX_DIBitmap::CalculatePitchAndSize(width, height, dibFormat, kNoPitch); ++ if (!pitch_size.has_value()) + return false; +- } + +- if (size <= kImageSizeLimitBytes && ++ if (pitch_size.value().size <= kImageSizeLimitBytes && + m_pBitmapDevice->Create(width, height, dibFormat, nullptr)) { + break; + } + m_Matrix.Scale(0.5f, 0.5f); + } +- m_pContext->GetBackground(m_pBitmapDevice->GetBitmap(), m_pObject.Get(), +- pOptions, m_Matrix); ++ pContext->GetBackground(m_pBitmapDevice->GetBitmap(), pObj, pOptions, ++ m_Matrix); + return true; + } + + CFX_RenderDevice* CPDF_ScaledRenderBuffer::GetDevice() const { +- return m_pBitmapDevice ? m_pBitmapDevice.get() : m_pDevice.Get(); ++ return m_pBitmapDevice ? static_cast(m_pBitmapDevice.get()) ++ : m_pDevice.get(); + } + + void CPDF_ScaledRenderBuffer::OutputToDevice() { +diff --git a/core/fpdfapi/render/cpdf_scaledrenderbuffer.h b/core/fpdfapi/render/cpdf_scaledrenderbuffer.h +index 0e7ac074e..3fb9c0b2f 100644 +--- a/core/fpdfapi/render/cpdf_scaledrenderbuffer.h ++++ b/core/fpdfapi/render/cpdf_scaledrenderbuffer.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -36,10 +36,8 @@ class CPDF_ScaledRenderBuffer { + + private: + UnownedPtr m_pDevice; +- UnownedPtr m_pContext; +- FX_RECT m_Rect; +- UnownedPtr m_pObject; + std::unique_ptr m_pBitmapDevice; ++ FX_RECT m_Rect; + CFX_Matrix m_Matrix; + }; + +diff --git a/core/fpdfapi/render/cpdf_textrenderer.cpp b/core/fpdfapi/render/cpdf_textrenderer.cpp +index fe1d25821..e1a2acba2 100644 +--- a/core/fpdfapi/render/cpdf_textrenderer.cpp ++++ b/core/fpdfapi/render/cpdf_textrenderer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,13 +7,15 @@ + #include "core/fpdfapi/render/cpdf_textrenderer.h" + + #include ++#include + + #include "core/fpdfapi/font/cpdf_font.h" +-#include "core/fpdfapi/render/cpdf_charposlist.h" ++#include "core/fpdfapi/render/charposlist.h" + #include "core/fpdfapi/render/cpdf_renderoptions.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/cfx_renderdevice.h" ++#include "core/fxge/cfx_textrenderoptions.h" + #include "core/fxge/fx_font.h" + #include "core/fxge/text_char_pos.h" + +@@ -23,23 +25,43 @@ CFX_Font* GetFont(CPDF_Font* pFont, int32_t position) { + return position == -1 ? pFont->GetFont() : pFont->GetFontFallback(position); + } + ++CFX_TextRenderOptions GetTextRenderOptionsHelper( ++ const CPDF_Font* pFont, ++ const CPDF_RenderOptions& options) { ++ CFX_TextRenderOptions text_options; ++ ++ if (pFont->IsCIDFont()) ++ text_options.font_is_cid = true; ++ ++ if (options.GetOptions().bNoTextSmooth) ++ text_options.aliasing_type = CFX_TextRenderOptions::kAliasing; ++ else if (options.GetOptions().bClearType) ++ text_options.aliasing_type = CFX_TextRenderOptions::kLcd; ++ ++ if (options.GetOptions().bNoNativeText) ++ text_options.native_text = false; ++ ++ return text_options; ++} ++ + } // namespace + + // static +-bool CPDF_TextRenderer::DrawTextPath(CFX_RenderDevice* pDevice, +- const std::vector& charCodes, +- const std::vector& charPos, +- CPDF_Font* pFont, +- float font_size, +- const CFX_Matrix& mtText2User, +- const CFX_Matrix* pUser2Device, +- const CFX_GraphStateData* pGraphState, +- FX_ARGB fill_argb, +- FX_ARGB stroke_argb, +- CFX_PathData* pClippingPath, +- int nFlag) { +- const CPDF_CharPosList CharPosList(charCodes, charPos, pFont, font_size); +- const std::vector& pos = CharPosList.Get(); ++bool CPDF_TextRenderer::DrawTextPath( ++ CFX_RenderDevice* pDevice, ++ pdfium::span char_codes, ++ pdfium::span char_pos, ++ CPDF_Font* pFont, ++ float font_size, ++ const CFX_Matrix& mtText2User, ++ const CFX_Matrix* pUser2Device, ++ const CFX_GraphStateData* pGraphState, ++ FX_ARGB fill_argb, ++ FX_ARGB stroke_argb, ++ CFX_Path* pClippingPath, ++ const CFX_FillRenderOptions& fill_options) { ++ std::vector pos = ++ GetCharPosList(char_codes, char_pos, pFont, font_size); + if (pos.empty()) + return true; + +@@ -52,19 +74,20 @@ bool CPDF_TextRenderer::DrawTextPath(CFX_RenderDevice* pDevice, + continue; + + CFX_Font* font = GetFont(pFont, fontPosition); +- if (!pDevice->DrawTextPath(i - startIndex, &pos[startIndex], font, +- font_size, mtText2User, pUser2Device, +- pGraphState, fill_argb, stroke_argb, +- pClippingPath, nFlag)) { ++ if (!pDevice->DrawTextPath( ++ pdfium::make_span(pos).subspan(startIndex, i - startIndex), font, ++ font_size, mtText2User, pUser2Device, pGraphState, fill_argb, ++ stroke_argb, pClippingPath, fill_options)) { + bDraw = false; + } + fontPosition = curFontPosition; + startIndex = i; + } + CFX_Font* font = GetFont(pFont, fontPosition); +- if (!pDevice->DrawTextPath(pos.size() - startIndex, &pos[startIndex], font, ++ if (!pDevice->DrawTextPath(pdfium::make_span(pos).subspan(startIndex), font, + font_size, mtText2User, pUser2Device, pGraphState, +- fill_argb, stroke_argb, pClippingPath, nFlag)) { ++ fill_argb, stroke_argb, pClippingPath, ++ fill_options)) { + bDraw = false; + } + return bDraw; +@@ -83,8 +106,8 @@ void CPDF_TextRenderer::DrawTextString(CFX_RenderDevice* pDevice, + if (pFont->IsType3Font()) + return; + +- int nChars = pFont->CountChar(str.AsStringView()); +- if (nChars <= 0) ++ size_t nChars = pFont->CountChar(str.AsStringView()); ++ if (nChars == 0) + return; + + size_t offset = 0; +@@ -93,7 +116,7 @@ void CPDF_TextRenderer::DrawTextString(CFX_RenderDevice* pDevice, + codes.resize(nChars); + positions.resize(nChars - 1); + float cur_pos = 0; +- for (int i = 0; i < nChars; i++) { ++ for (size_t i = 0; i < nChars; i++) { + codes[i] = pFont->GetNextChar(str.AsStringView(), &offset); + if (i) + positions[i - 1] = cur_pos; +@@ -108,36 +131,20 @@ void CPDF_TextRenderer::DrawTextString(CFX_RenderDevice* pDevice, + + // static + bool CPDF_TextRenderer::DrawNormalText(CFX_RenderDevice* pDevice, +- const std::vector& charCodes, +- const std::vector& charPos, ++ pdfium::span char_codes, ++ pdfium::span char_pos, + CPDF_Font* pFont, + float font_size, + const CFX_Matrix& mtText2Device, + FX_ARGB fill_argb, + const CPDF_RenderOptions& options) { +- const CPDF_CharPosList CharPosList(charCodes, charPos, pFont, font_size); +- const std::vector& pos = CharPosList.Get(); ++ std::vector pos = ++ GetCharPosList(char_codes, char_pos, pFont, font_size); + if (pos.empty()) + return true; + +- int fxge_flags = 0; +- if (options.GetOptions().bClearType) { +- fxge_flags |= FXTEXT_CLEARTYPE; +- if (options.GetOptions().bBGRStripe) +- fxge_flags |= FXTEXT_BGR_STRIPE; +- } +- if (options.GetOptions().bNoTextSmooth) +- fxge_flags |= FXTEXT_NOSMOOTH; +- if (options.GetOptions().bPrintGraphicText) +- fxge_flags |= FXTEXT_PRINTGRAPHICTEXT; +- if (options.GetOptions().bNoNativeText) +- fxge_flags |= FXTEXT_NO_NATIVETEXT; +- if (options.GetOptions().bPrintImageText) +- fxge_flags |= FXTEXT_PRINTIMAGETEXT; +- +- if (pFont->IsCIDFont()) +- fxge_flags |= FXFONT_CIDFONT; +- ++ CFX_TextRenderOptions text_options = ++ GetTextRenderOptionsHelper(pFont, options); + bool bDraw = true; + int32_t fontPosition = pos[0].m_FallbackFontPosition; + size_t startIndex = 0; +@@ -147,18 +154,18 @@ bool CPDF_TextRenderer::DrawNormalText(CFX_RenderDevice* pDevice, + continue; + + CFX_Font* font = GetFont(pFont, fontPosition); +- if (!pDevice->DrawNormalText(i - startIndex, &pos[startIndex], font, +- font_size, mtText2Device, fill_argb, +- fxge_flags)) { ++ if (!pDevice->DrawNormalText( ++ pdfium::make_span(pos).subspan(startIndex, i - startIndex), font, ++ font_size, mtText2Device, fill_argb, text_options)) { + bDraw = false; + } + fontPosition = curFontPosition; + startIndex = i; + } + CFX_Font* font = GetFont(pFont, fontPosition); +- if (!pDevice->DrawNormalText(pos.size() - startIndex, &pos[startIndex], font, ++ if (!pDevice->DrawNormalText(pdfium::make_span(pos).subspan(startIndex), font, + font_size, mtText2Device, fill_argb, +- fxge_flags)) { ++ text_options)) { + bDraw = false; + } + return bDraw; +diff --git a/core/fpdfapi/render/cpdf_textrenderer.h b/core/fpdfapi/render/cpdf_textrenderer.h +index 91ab4ccc3..b5529a6ea 100644 +--- a/core/fpdfapi/render/cpdf_textrenderer.h ++++ b/core/fpdfapi/render/cpdf_textrenderer.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,18 +7,19 @@ + #ifndef CORE_FPDFAPI_RENDER_CPDF_TEXTRENDERER_H_ + #define CORE_FPDFAPI_RENDER_CPDF_TEXTRENDERER_H_ + +-#include ++#include + ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/span.h" + + class CFX_RenderDevice; + class CFX_GraphStateData; +-class CFX_PathData; ++class CFX_Path; + class CPDF_RenderOptions; + class CPDF_Font; ++struct CFX_FillRenderOptions; + + class CPDF_TextRenderer { + public: +@@ -33,8 +34,8 @@ class CPDF_TextRenderer { + const CPDF_RenderOptions& options); + + static bool DrawTextPath(CFX_RenderDevice* pDevice, +- const std::vector& charCodes, +- const std::vector& charPos, ++ pdfium::span char_codes, ++ pdfium::span char_pos, + CPDF_Font* pFont, + float font_size, + const CFX_Matrix& mtText2User, +@@ -42,12 +43,12 @@ class CPDF_TextRenderer { + const CFX_GraphStateData* pGraphState, + FX_ARGB fill_argb, + FX_ARGB stroke_argb, +- CFX_PathData* pClippingPath, +- int nFlag); ++ CFX_Path* pClippingPath, ++ const CFX_FillRenderOptions& fill_options); + + static bool DrawNormalText(CFX_RenderDevice* pDevice, +- const std::vector& charCodes, +- const std::vector& charPos, ++ pdfium::span char_codes, ++ pdfium::span char_pos, + CPDF_Font* pFont, + float font_size, + const CFX_Matrix& mtText2Device, +diff --git a/core/fpdfapi/render/cpdf_type3cache.cpp b/core/fpdfapi/render/cpdf_type3cache.cpp +index 41e630b0f..f00945641 100644 +--- a/core/fpdfapi/render/cpdf_type3cache.cpp ++++ b/core/fpdfapi/render/cpdf_type3cache.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,43 +6,23 @@ + + #include "core/fpdfapi/render/cpdf_type3cache.h" + +-#include ++#include ++ + #include + #include + + #include "core/fpdfapi/font/cpdf_type3char.h" + #include "core/fpdfapi/font/cpdf_type3font.h" +-#include "core/fpdfapi/page/cpdf_docpagedata.h" +-#include "core/fpdfapi/render/cpdf_docrenderdata.h" + #include "core/fpdfapi/render/cpdf_type3glyphmap.h" + #include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxge/cfx_glyphbitmap.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "core/fxge/fx_dib.h" +-#include "third_party/base/ptr_util.h" ++#include "core/fxge/dib/fx_dib.h" + + namespace { + +-struct CPDF_UniqueKeyGen { +- void Generate(int count, ...); +- +- int m_KeyLen; +- char m_Key[128]; +-}; +- +-void CPDF_UniqueKeyGen::Generate(int count, ...) { +- va_list argList; +- va_start(argList, count); +- for (int i = 0; i < count; i++) { +- int p = va_arg(argList, int); +- (reinterpret_cast(m_Key))[i] = p; +- } +- va_end(argList); +- m_KeyLen = count * sizeof(uint32_t); +-} +- +-bool IsScanLine1bpp(uint8_t* pBuf, int width) { ++bool IsScanLine1bpp(const uint8_t* pBuf, int width) { + int size = width / 8; + for (int i = 0; i < size; i++) { + if (pBuf[i]) +@@ -51,7 +31,7 @@ bool IsScanLine1bpp(uint8_t* pBuf, int width) { + return (width % 8) && (pBuf[width / 8] & (0xff << (8 - width % 8))); + } + +-bool IsScanLine8bpp(uint8_t* pBuf, int width) { ++bool IsScanLine8bpp(const uint8_t* pBuf, int width) { + for (int i = 0; i < width; i++) { + if (pBuf[i] > 0x40) + return true; +@@ -59,26 +39,34 @@ bool IsScanLine8bpp(uint8_t* pBuf, int width) { + return false; + } + +-int DetectFirstLastScan(const RetainPtr& pBitmap, bool bFirst) { +- int height = pBitmap->GetHeight(); +- int pitch = pBitmap->GetPitch(); +- int width = pBitmap->GetWidth(); +- int bpp = pBitmap->GetBPP(); ++bool IsScanLineBpp(int bpp, const uint8_t* pBuf, int width) { ++ if (bpp == 1) ++ return IsScanLine1bpp(pBuf, width); + if (bpp > 8) + width *= bpp / 8; +- uint8_t* pBuf = pBitmap->GetBuffer(); +- int line = bFirst ? 0 : height - 1; +- int line_step = bFirst ? 1 : -1; +- int line_end = bFirst ? height : -1; +- while (line != line_end) { +- if (bpp == 1) { +- if (IsScanLine1bpp(pBuf + line * pitch, width)) +- return line; +- } else { +- if (IsScanLine8bpp(pBuf + line * pitch, width)) +- return line; +- } +- line += line_step; ++ return IsScanLine8bpp(pBuf, width); ++} ++ ++int DetectFirstScan(const RetainPtr& pBitmap) { ++ const int height = pBitmap->GetHeight(); ++ const int width = pBitmap->GetWidth(); ++ const int bpp = pBitmap->GetBPP(); ++ for (int line = 0; line < height; ++line) { ++ const uint8_t* pBuf = pBitmap->GetScanline(line).data(); ++ if (IsScanLineBpp(bpp, pBuf, width)) ++ return line; ++ } ++ return -1; ++} ++ ++int DetectLastScan(const RetainPtr& pBitmap) { ++ const int height = pBitmap->GetHeight(); ++ const int bpp = pBitmap->GetBPP(); ++ const int width = pBitmap->GetWidth(); ++ for (int line = height - 1; line >= 0; --line) { ++ const uint8_t* pBuf = pBitmap->GetScanline(line).data(); ++ if (IsScanLineBpp(bpp, pBuf, width)) ++ return line; + } + return -1; + } +@@ -90,18 +78,19 @@ CPDF_Type3Cache::CPDF_Type3Cache(CPDF_Type3Font* pFont) : m_pFont(pFont) {} + CPDF_Type3Cache::~CPDF_Type3Cache() = default; + + const CFX_GlyphBitmap* CPDF_Type3Cache::LoadGlyph(uint32_t charcode, +- const CFX_Matrix* pMatrix) { +- CPDF_UniqueKeyGen keygen; +- keygen.Generate( +- 4, FXSYS_roundf(pMatrix->a * 10000), FXSYS_roundf(pMatrix->b * 10000), +- FXSYS_roundf(pMatrix->c * 10000), FXSYS_roundf(pMatrix->d * 10000)); +- ByteString FaceGlyphsKey(keygen.m_Key, keygen.m_KeyLen); ++ const CFX_Matrix& mtMatrix) { ++ SizeKey keygen = { ++ FXSYS_roundf(mtMatrix.a * 10000), ++ FXSYS_roundf(mtMatrix.b * 10000), ++ FXSYS_roundf(mtMatrix.c * 10000), ++ FXSYS_roundf(mtMatrix.d * 10000), ++ }; + CPDF_Type3GlyphMap* pSizeCache; +- auto it = m_SizeMap.find(FaceGlyphsKey); ++ auto it = m_SizeMap.find(keygen); + if (it == m_SizeMap.end()) { +- auto pNew = pdfium::MakeUnique(); ++ auto pNew = std::make_unique(); + pSizeCache = pNew.get(); +- m_SizeMap[FaceGlyphsKey] = std::move(pNew); ++ m_SizeMap[keygen] = std::move(pNew); + } else { + pSizeCache = it->second.get(); + } +@@ -110,7 +99,7 @@ const CFX_GlyphBitmap* CPDF_Type3Cache::LoadGlyph(uint32_t charcode, + return pExisting; + + std::unique_ptr pNewBitmap = +- RenderGlyph(pSizeCache, charcode, pMatrix); ++ RenderGlyph(pSizeCache, charcode, mtMatrix); + CFX_GlyphBitmap* pGlyphBitmap = pNewBitmap.get(); + pSizeCache->SetBitmap(charcode, std::move(pNewBitmap)); + return pGlyphBitmap; +@@ -119,22 +108,25 @@ const CFX_GlyphBitmap* CPDF_Type3Cache::LoadGlyph(uint32_t charcode, + std::unique_ptr CPDF_Type3Cache::RenderGlyph( + CPDF_Type3GlyphMap* pSize, + uint32_t charcode, +- const CFX_Matrix* pMatrix) { +- const CPDF_Type3Char* pChar = m_pFont->LoadChar(charcode); +- if (!pChar || !pChar->GetBitmap()) ++ const CFX_Matrix& mtMatrix) { ++ CPDF_Type3Char* pChar = m_pFont->LoadChar(charcode); ++ if (!pChar) + return nullptr; + +- CFX_Matrix text_matrix(pMatrix->a, pMatrix->b, pMatrix->c, pMatrix->d, 0, 0); ++ RetainPtr pBitmap = pChar->GetBitmap(); ++ if (!pBitmap) ++ return nullptr; ++ ++ CFX_Matrix text_matrix(mtMatrix.a, mtMatrix.b, mtMatrix.c, mtMatrix.d, 0, 0); + CFX_Matrix image_matrix = pChar->matrix() * text_matrix; + +- RetainPtr pBitmap = pChar->GetBitmap(); + RetainPtr pResBitmap; + int left = 0; + int top = 0; + if (fabs(image_matrix.b) < fabs(image_matrix.a) / 100 && + fabs(image_matrix.c) < fabs(image_matrix.d) / 100) { +- int top_line = DetectFirstLastScan(pBitmap, true); +- int bottom_line = DetectFirstLastScan(pBitmap, false); ++ int top_line = DetectFirstScan(pBitmap); ++ int bottom_line = DetectLastScan(pBitmap); + if (top_line == 0 && bottom_line == pBitmap->GetHeight() - 1) { + float top_y = image_matrix.d + image_matrix.f; + float bottom_y = image_matrix.f; +@@ -162,7 +154,7 @@ std::unique_ptr CPDF_Type3Cache::RenderGlyph( + if (!pResBitmap) + return nullptr; + +- auto pGlyph = pdfium::MakeUnique(left, -top); ++ auto pGlyph = std::make_unique(left, -top); + pGlyph->GetBitmap()->TakeOver(std::move(pResBitmap)); + return pGlyph; + } +diff --git a/core/fpdfapi/render/cpdf_type3cache.h b/core/fpdfapi/render/cpdf_type3cache.h +index 4371a015d..e154a72c0 100644 +--- a/core/fpdfapi/render/cpdf_type3cache.h ++++ b/core/fpdfapi/render/cpdf_type3cache.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,13 @@ + #ifndef CORE_FPDFAPI_RENDER_CPDF_TYPE3CACHE_H_ + #define CORE_FPDFAPI_RENDER_CPDF_TYPE3CACHE_H_ + ++#include ++ + #include + #include ++#include + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/observed_ptr.h" + #include "core/fxcrt/retain_ptr.h" + +@@ -22,22 +24,23 @@ class CPDF_Type3GlyphMap; + + class CPDF_Type3Cache final : public Retainable, public Observable { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + const CFX_GlyphBitmap* LoadGlyph(uint32_t charcode, +- const CFX_Matrix* pMatrix); ++ const CFX_Matrix& mtMatrix); + + private: ++ using SizeKey = std::tuple; ++ + explicit CPDF_Type3Cache(CPDF_Type3Font* pFont); + ~CPDF_Type3Cache() override; + + std::unique_ptr RenderGlyph(CPDF_Type3GlyphMap* pSize, + uint32_t charcode, +- const CFX_Matrix* pMatrix); ++ const CFX_Matrix& mtMatrix); + + RetainPtr const m_pFont; +- std::map> m_SizeMap; ++ std::map> m_SizeMap; + }; + + #endif // CORE_FPDFAPI_RENDER_CPDF_TYPE3CACHE_H_ +diff --git a/core/fpdfapi/render/cpdf_type3glyphmap.cpp b/core/fpdfapi/render/cpdf_type3glyphmap.cpp +index b144991ad..10b2b4abd 100644 +--- a/core/fpdfapi/render/cpdf_type3glyphmap.cpp ++++ b/core/fpdfapi/render/cpdf_type3glyphmap.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,10 +6,12 @@ + + #include "core/fpdfapi/render/cpdf_type3glyphmap.h" + ++#include ++ + #include +-#include + #include + ++#include "core/fxcrt/fx_system.h" + #include "core/fxge/cfx_glyphbitmap.h" + #include "core/fxge/fx_font.h" + +@@ -39,7 +41,7 @@ int AdjustBlueHelper(float pos, std::vector* blues) { + + CPDF_Type3GlyphMap::CPDF_Type3GlyphMap() {} + +-CPDF_Type3GlyphMap::~CPDF_Type3GlyphMap() {} ++CPDF_Type3GlyphMap::~CPDF_Type3GlyphMap() = default; + + std::pair CPDF_Type3GlyphMap::AdjustBlue(float top, float bottom) { + return std::make_pair(AdjustBlueHelper(top, &m_TopBlue), +diff --git a/core/fpdfapi/render/cpdf_type3glyphmap.h b/core/fpdfapi/render/cpdf_type3glyphmap.h +index fced0ee29..4965732ff 100644 +--- a/core/fpdfapi/render/cpdf_type3glyphmap.h ++++ b/core/fpdfapi/render/cpdf_type3glyphmap.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,13 +7,13 @@ + #ifndef CORE_FPDFAPI_RENDER_CPDF_TYPE3GLYPHMAP_H_ + #define CORE_FPDFAPI_RENDER_CPDF_TYPE3GLYPHMAP_H_ + ++#include ++ + #include + #include + #include + #include + +-#include "core/fxcrt/fx_system.h" +- + class CFX_GlyphBitmap; + + class CPDF_Type3GlyphMap { +diff --git a/core/fpdfapi/render/cpdf_windowsrenderdevice.cpp b/core/fpdfapi/render/cpdf_windowsrenderdevice.cpp +index 8e1f212c5..78410ddc6 100644 +--- a/core/fpdfapi/render/cpdf_windowsrenderdevice.cpp ++++ b/core/fpdfapi/render/cpdf_windowsrenderdevice.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,8 @@ + + #include "core/fpdfapi/render/cpdf_windowsrenderdevice.h" + ++#include ++ + #include "core/fxcodec/basic/basicmodule.h" + #include "core/fxcodec/fax/faxmodule.h" + #include "core/fxcodec/flate/flatemodule.h" +@@ -20,7 +22,9 @@ constexpr EncoderIface kEncoderIface = { + + } // namespace + +-CPDF_WindowsRenderDevice::CPDF_WindowsRenderDevice(HDC hDC) +- : CFX_WindowsRenderDevice(hDC, &kEncoderIface) {} ++CPDF_WindowsRenderDevice::CPDF_WindowsRenderDevice( ++ HDC hDC, ++ CFX_PSFontTracker* ps_font_tracker) ++ : CFX_WindowsRenderDevice(hDC, ps_font_tracker, &kEncoderIface) {} + + CPDF_WindowsRenderDevice::~CPDF_WindowsRenderDevice() = default; +diff --git a/core/fpdfapi/render/cpdf_windowsrenderdevice.h b/core/fpdfapi/render/cpdf_windowsrenderdevice.h +index 51d150eb0..c83c588fd 100644 +--- a/core/fpdfapi/render/cpdf_windowsrenderdevice.h ++++ b/core/fpdfapi/render/cpdf_windowsrenderdevice.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,9 +9,11 @@ + + #include "core/fxge/cfx_windowsrenderdevice.h" + ++class CFX_PSFontTracker; ++ + class CPDF_WindowsRenderDevice final : public CFX_WindowsRenderDevice { + public: +- explicit CPDF_WindowsRenderDevice(HDC hDC); ++ CPDF_WindowsRenderDevice(HDC hDC, CFX_PSFontTracker* ps_font_tracker); + ~CPDF_WindowsRenderDevice() override; + }; + +diff --git a/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp b/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp +index 271a251bd..a51e16e0c 100644 +--- a/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp ++++ b/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp +@@ -1,28 +1,81 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include ++ + #include + + #include "build/build_config.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" ++#include "core/fxge/dib/fx_dib.h" + #include "public/fpdf_progressive.h" + #include "testing/embedder_test.h" ++#include "testing/embedder_test_constants.h" + #include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/base/check.h" ++ ++namespace { ++ ++constexpr FX_ARGB kBlack = 0xFF000000; ++constexpr FX_ARGB kBlue = 0xFF0000FF; ++constexpr FX_ARGB kGreen = 0xFF00FF00; ++constexpr FX_ARGB kRed = 0xFFFF0000; ++constexpr FX_ARGB kWhite = 0xFFFFFFFF; ++ ++const char* AnnotationStampWithApBaseContentChecksum() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "243f3d6267d9db09198fed9f8c4957fd"; ++#endif ++ return "e31414933c9ff3950773981e5bf61678"; ++} ++ ++} // namespace + + class FPDFProgressiveRenderEmbedderTest : public EmbedderTest { + public: ++ class FakePause : public IFSDK_PAUSE { ++ public: ++ explicit FakePause(bool should_pause) : should_pause_(should_pause) { ++ IFSDK_PAUSE::version = 1; ++ IFSDK_PAUSE::user = nullptr; ++ IFSDK_PAUSE::NeedToPauseNow = Pause_NeedToPauseNow; ++ } ++ ~FakePause() = default; ++ static FPDF_BOOL Pause_NeedToPauseNow(IFSDK_PAUSE* param) { ++ return static_cast(param)->should_pause_; ++ } ++ ++ private: ++ const bool should_pause_; ++ }; ++ + // StartRenderPageWithFlags() with no flags. + // The call returns true if the rendering is complete. + bool StartRenderPage(FPDF_PAGE page, IFSDK_PAUSE* pause); + +- // Start rendering of |page| into a bitmap with the ability to pause the ++ // Start rendering of |page| into a bitmap with the ability to |pause| the + // rendering with the specified rendering |flags|. + // The call returns true if the rendering is complete. + // + // See public/fpdfview.h for a list of page rendering flags. + bool StartRenderPageWithFlags(FPDF_PAGE page, IFSDK_PAUSE* pause, int flags); + ++ // Start rendering of |page| into a bitmap with the ability to pause the ++ // rendering with the specified rendering |flags| and the specified ++ // |color_scheme|. This also takes in the |background_color| for the bitmap. ++ // The call returns true if the rendering is complete. ++ // ++ // See public/fpdfview.h for the list of page rendering flags and ++ // the list of colors in the scheme. ++ bool StartRenderPageWithColorSchemeAndBackground( ++ FPDF_PAGE page, ++ IFSDK_PAUSE* pause, ++ int flags, ++ const FPDF_COLORSCHEME* color_scheme, ++ uint32_t background_color); ++ + // Continue rendering of |page| into the bitmap created in + // StartRenderPageWithFlags(). + // The call returns true if the rendering is complete. +@@ -40,6 +93,33 @@ class FPDFProgressiveRenderEmbedderTest : public EmbedderTest { + ScopedFPDFBitmap FinishRenderPageWithForms(FPDF_PAGE page, + FPDF_FORMHANDLE handle); + ++ // Convert the |page| into a bitmap with a |background_color|, using the ++ // color scheme render API with the specific |flags| and |color_scheme|. ++ // The form handle associated with |page| should be passed in via |handle|. ++ // If |handle| is nullptr, then forms on the page will not be rendered. ++ // This returns the bitmap generated by the progressive render calls. ++ // ++ // See public/fpdfview.h for a list of page rendering flags and ++ // the color scheme that can be applied for rendering. ++ ScopedFPDFBitmap RenderPageWithForcedColorScheme( ++ FPDF_PAGE page, ++ FPDF_FORMHANDLE handle, ++ int flags, ++ const FPDF_COLORSCHEME* color_scheme, ++ FX_ARGB background_color); ++ ++ protected: ++ // Utility method to render the |page_num| of the currently loaded Pdf ++ // using RenderPageWithForcedColorScheme() passing in the render options ++ // and expected values for bitmap verification. ++ void VerifyRenderingWithColorScheme(int page_num, ++ int flags, ++ const FPDF_COLORSCHEME* color_scheme, ++ FX_ARGB background_color, ++ int bitmap_width, ++ int bitmap_height, ++ const char* md5); ++ + private: + // Keeps the bitmap used for progressive rendering alive until + // FPDF_RenderPage_Close() is called after which the bitmap is returned +@@ -72,9 +152,31 @@ bool FPDFProgressiveRenderEmbedderTest::StartRenderPageWithFlags( + return rv != FPDF_RENDER_TOBECONTINUED; + } + ++bool FPDFProgressiveRenderEmbedderTest:: ++ StartRenderPageWithColorSchemeAndBackground( ++ FPDF_PAGE page, ++ IFSDK_PAUSE* pause, ++ int flags, ++ const FPDF_COLORSCHEME* color_scheme, ++ uint32_t background_color) { ++ int width = static_cast(FPDF_GetPageWidth(page)); ++ int height = static_cast(FPDF_GetPageHeight(page)); ++ progressive_render_flags_ = flags; ++ int alpha = FPDFPage_HasTransparency(page) ? 1 : 0; ++ progressive_render_bitmap_ = ++ ScopedFPDFBitmap(FPDFBitmap_Create(width, height, alpha)); ++ DCHECK(progressive_render_bitmap_); ++ FPDFBitmap_FillRect(progressive_render_bitmap_.get(), 0, 0, width, height, ++ background_color); ++ int rv = FPDF_RenderPageBitmapWithColorScheme_Start( ++ progressive_render_bitmap_.get(), page, 0, 0, width, height, 0, ++ progressive_render_flags_, color_scheme, pause); ++ return rv != FPDF_RENDER_TOBECONTINUED; ++} ++ + bool FPDFProgressiveRenderEmbedderTest::ContinueRenderPage(FPDF_PAGE page, + IFSDK_PAUSE* pause) { +- ASSERT(progressive_render_bitmap_); ++ DCHECK(progressive_render_bitmap_); + + int rv = FPDF_RenderPage_Continue(page, pause); + return rv != FPDF_RENDER_TOBECONTINUED; +@@ -88,7 +190,7 @@ ScopedFPDFBitmap FPDFProgressiveRenderEmbedderTest::FinishRenderPage( + ScopedFPDFBitmap FPDFProgressiveRenderEmbedderTest::FinishRenderPageWithForms( + FPDF_PAGE page, + FPDF_FORMHANDLE handle) { +- ASSERT(progressive_render_bitmap_); ++ DCHECK(progressive_render_bitmap_); + + int width = static_cast(FPDF_GetPageWidth(page)); + int height = static_cast(FPDF_GetPageHeight(page)); +@@ -98,68 +200,43 @@ ScopedFPDFBitmap FPDFProgressiveRenderEmbedderTest::FinishRenderPageWithForms( + return std::move(progressive_render_bitmap_); + } + +-class FakePause : public IFSDK_PAUSE { +- public: +- explicit FakePause(bool should_pause) : should_pause_(should_pause) { +- IFSDK_PAUSE::version = 1; +- IFSDK_PAUSE::user = nullptr; +- IFSDK_PAUSE::NeedToPauseNow = Pause_NeedToPauseNow; +- } +- ~FakePause() = default; ++ScopedFPDFBitmap ++FPDFProgressiveRenderEmbedderTest::RenderPageWithForcedColorScheme( ++ FPDF_PAGE page, ++ FPDF_FORMHANDLE handle, ++ int flags, ++ const FPDF_COLORSCHEME* color_scheme, ++ FX_ARGB background_color) { ++ FakePause pause(true); ++ bool render_done = StartRenderPageWithColorSchemeAndBackground( ++ page, &pause, flags, color_scheme, background_color) == ++ FPDF_RENDER_TOBECONTINUED; ++ EXPECT_FALSE(render_done); + +- static FPDF_BOOL Pause_NeedToPauseNow(IFSDK_PAUSE* param) { +- return static_cast(param)->should_pause_; ++ while (!render_done) { ++ render_done = ContinueRenderPage(page, &pause); + } ++ return FinishRenderPageWithForms(page, form_handle()); ++} + +- private: +- const bool should_pause_ = false; +-}; +- +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_RenderWithoutPause DISABLED_RenderWithoutPause +-#else +-#define MAYBE_RenderWithoutPause RenderWithoutPause +-#endif +-TEST_F(FPDFProgressiveRenderEmbedderTest, MAYBE_RenderWithoutPause) { +-#if defined(OS_WIN) +- static constexpr char kMd5BaseContent[] = "649d6792ea50faf98c013c2d81710595"; +-#elif defined(OS_MACOSX) +- static constexpr char kMd5BaseContent[] = "5f933aac2a74434be1b4d0bdb5334f0b"; +-#else +- static constexpr char kMd5BaseContent[] = "a24edc7740f1d6f76899652dcf825dea"; +-#endif +- ++TEST_F(FPDFProgressiveRenderEmbedderTest, RenderWithoutPause) { + // Test rendering of page content using progressive render APIs + // without pausing the rendering. +- EXPECT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf")); ++ ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + FakePause pause(false); + EXPECT_TRUE(StartRenderPage(page, &pause)); + ScopedFPDFBitmap bitmap = FinishRenderPage(page); +- CompareBitmap(bitmap.get(), 595, 842, kMd5BaseContent); ++ CompareBitmap(bitmap.get(), 595, 842, ++ AnnotationStampWithApBaseContentChecksum()); + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_RenderWithPause DISABLED_RenderWithPause +-#else +-#define MAYBE_RenderWithPause RenderWithPause +-#endif +-TEST_F(FPDFProgressiveRenderEmbedderTest, MAYBE_RenderWithPause) { +-#if defined(OS_WIN) +- static constexpr char kMd5BaseContent[] = "649d6792ea50faf98c013c2d81710595"; +-#elif defined(OS_MACOSX) +- static constexpr char kMd5BaseContent[] = "5f933aac2a74434be1b4d0bdb5334f0b"; +-#else +- static constexpr char kMd5BaseContent[] = "a24edc7740f1d6f76899652dcf825dea"; +-#endif +- ++TEST_F(FPDFProgressiveRenderEmbedderTest, RenderWithPause) { + // Test rendering of page content using progressive render APIs + // with pause in rendering. +- EXPECT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf")); ++ ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + FakePause pause(true); +@@ -170,31 +247,15 @@ TEST_F(FPDFProgressiveRenderEmbedderTest, MAYBE_RenderWithPause) { + render_done = ContinueRenderPage(page, &pause); + } + ScopedFPDFBitmap bitmap = FinishRenderPage(page); +- CompareBitmap(bitmap.get(), 595, 842, kMd5BaseContent); ++ CompareBitmap(bitmap.get(), 595, 842, ++ AnnotationStampWithApBaseContentChecksum()); + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_RenderAnnotWithPause DISABLED_RenderAnnotWithPause +-#else +-#define MAYBE_RenderAnnotWithPause RenderAnnotWithPause +-#endif +-TEST_F(FPDFProgressiveRenderEmbedderTest, MAYBE_RenderAnnotWithPause) { +-#if defined(OS_WIN) +- static constexpr char kMd5ContentWithAnnot[] = +- "6aa001a77ec05d0f1b0d1d22e28744d4"; +-#elif defined(OS_MACOSX) +- static constexpr char kMd5ContentWithAnnot[] = +- "c35408717759562d1f8bf33d317483d2"; +-#else +- static constexpr char kMd5ContentWithAnnot[] = +- "b42cef463483e668eaf4055a65e4f1f5"; +-#endif +- ++TEST_F(FPDFProgressiveRenderEmbedderTest, RenderAnnotWithPause) { + // Test rendering of the page with annotations using progressive render APIs + // with pause in rendering. +- EXPECT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf")); ++ ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + FakePause pause(true); +@@ -205,31 +266,15 @@ TEST_F(FPDFProgressiveRenderEmbedderTest, MAYBE_RenderAnnotWithPause) { + render_done = ContinueRenderPage(page, &pause); + } + ScopedFPDFBitmap bitmap = FinishRenderPage(page); +- CompareBitmap(bitmap.get(), 595, 842, kMd5ContentWithAnnot); ++ CompareBitmap(bitmap.get(), 595, 842, ++ pdfium::AnnotationStampWithApChecksum()); + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_RenderFormsWithPause DISABLED_RenderFormsWithPause +-#else +-#define MAYBE_RenderFormsWithPause RenderFormsWithPause +-#endif +-TEST_F(FPDFProgressiveRenderEmbedderTest, MAYBE_RenderFormsWithPause) { +-#if defined(OS_WIN) +- static constexpr char kMd5ContentWithForms[] = +- "d3204faa62b607f0bd3893c9c22cabcb"; +-#elif defined(OS_MACOSX) +- static constexpr char kMd5ContentWithForms[] = +- "5f11dbe575fe197a37c3fb422559f8ff"; +-#else +- static constexpr char kMd5ContentWithForms[] = +- "b890950d4b9bc163b1a96797f3004b53"; +-#endif +- ++TEST_F(FPDFProgressiveRenderEmbedderTest, RenderFormsWithPause) { + // Test rendering of the page with forms using progressive render APIs + // with pause in rendering. +- EXPECT_TRUE(OpenDocument("text_form.pdf")); ++ ASSERT_TRUE(OpenDocument("text_form.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + FakePause pause(true); +@@ -239,7 +284,184 @@ TEST_F(FPDFProgressiveRenderEmbedderTest, MAYBE_RenderFormsWithPause) { + while (!render_done) { + render_done = ContinueRenderPage(page, &pause); + } +- ScopedFPDFBitmap bitmap = FinishRenderPageWithForms(page, form_handle_); +- CompareBitmap(bitmap.get(), 300, 300, kMd5ContentWithForms); ++ ScopedFPDFBitmap bitmap = FinishRenderPageWithForms(page, form_handle()); ++ CompareBitmap(bitmap.get(), 300, 300, pdfium::TextFormChecksum()); ++ UnloadPage(page); ++} ++ ++void FPDFProgressiveRenderEmbedderTest::VerifyRenderingWithColorScheme( ++ int page_num, ++ int flags, ++ const FPDF_COLORSCHEME* color_scheme, ++ FX_ARGB background_color, ++ int bitmap_width, ++ int bitmap_height, ++ const char* md5) { ++ ASSERT_TRUE(document()); ++ ++ FPDF_PAGE page = LoadPage(page_num); ++ ASSERT_TRUE(page); ++ ++ ScopedFPDFBitmap bitmap = RenderPageWithForcedColorScheme( ++ page, form_handle(), flags, color_scheme, background_color); ++ ASSERT_TRUE(bitmap); ++ CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, md5); + UnloadPage(page); + } ++ ++TEST_F(FPDFProgressiveRenderEmbedderTest, RenderTextWithColorScheme) { ++ // Test rendering of text with forced color scheme on. ++ const char* content_with_text_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "5ece6059efdc2ecb2894fa3cf329dc94"; ++#if BUILDFLAG(IS_APPLE) ++ return "ee4ec12f54ce8d117a73bd9b85a8954d"; ++#else ++ return "704db63ed2bf77254ecaa8035b85f21a"; ++#endif // BUILDFLAG(IS_APPLE) ++ }(); ++ ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ ++ FPDF_COLORSCHEME color_scheme{kBlack, kWhite, kWhite, kWhite}; ++ VerifyRenderingWithColorScheme(/*page_num=*/0, /*flags=*/0, &color_scheme, ++ kBlack, 200, 200, content_with_text_checksum); ++} ++ ++TEST_F(FPDFProgressiveRenderEmbedderTest, RenderPathWithColorScheme) { ++ // Test rendering of paths with forced color scheme on. ++ const char* rectangles_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "4b0f850a94698d07b6cd2814d1b4ccb7"; ++ return "249f59b0d066c4f6bd89782a80822219"; ++ }(); ++ ++ ASSERT_TRUE(OpenDocument("rectangles.pdf")); ++ ++ FPDF_COLORSCHEME color_scheme{kWhite, kRed, kBlue, kBlue}; ++ VerifyRenderingWithColorScheme(/*page_num=*/0, /*flags=*/0, &color_scheme, ++ kBlack, 200, 300, rectangles_checksum); ++} ++ ++TEST_F(FPDFProgressiveRenderEmbedderTest, ++ RenderPathWithColorSchemeAndConvertFillToStroke) { ++ // Test rendering of paths with forced color scheme on and conversion from ++ // fill to stroke enabled. The fill paths should be rendered as stroke. ++ const char* rectangles_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "c1cbbd2ce6921f608a3c55140592419b"; ++ return "0ebcc11e617635eca1fa9ce475383a80"; ++ }(); ++ ++ ASSERT_TRUE(OpenDocument("rectangles.pdf")); ++ ++ FPDF_COLORSCHEME color_scheme{kWhite, kRed, kBlue, kBlue}; ++ VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_CONVERT_FILL_TO_STROKE, ++ &color_scheme, kBlack, 200, 300, ++ rectangles_checksum); ++} ++ ++TEST_F(FPDFProgressiveRenderEmbedderTest, RenderHighlightWithColorScheme) { ++ // Test rendering of highlight with forced color scheme on. ++ // ++ // Note: The fill color rendered for highlight is different from the normal ++ // path since highlights have Multiply blend mode, while the other path has ++ // Normal blend mode. ++ const char* content_with_highlight_fill_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "9b6273fdbc9db780c49f7540756209f8"; ++#if BUILDFLAG(IS_APPLE) ++ return "a820afec9b99d3d3f2e9e9382bbad7c1"; ++#else ++ return "a08a0639f89446f66f3689ee8e08b9fe"; ++#endif // BUILDFLAG(IS_APPLE) ++ }(); ++ ++ ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf")); ++ ++ FPDF_COLORSCHEME color_scheme{kRed, kGreen, kWhite, kWhite}; ++ VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, &color_scheme, ++ kBlue, 612, 792, ++ content_with_highlight_fill_checksum); ++} ++ ++TEST_F(FPDFProgressiveRenderEmbedderTest, ++ RenderHighlightWithColorSchemeAndConvertFillToStroke) { ++ // Test rendering of highlight with forced color and converting fill to ++ // stroke. The highlight should be rendered as a stroke of the rect. ++ // ++ // Note: The stroke color rendered for highlight is different from the normal ++ // path since highlights have Multiply blend mode, while the other path has ++ // Normal blend mode. ++ ++ const char* md5_content_with_highlight = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "772246195d18f75d40a22bee913c098f"; ++#if BUILDFLAG(IS_APPLE) ++ return "8837bea0b3520164b1784e513c882a2d"; ++#else ++ return "3dd8c02f5c06bac85e0d2c8bf37d1dc4"; ++#endif // BUILDFLAG(IS_APPLE) ++ }(); ++ ++ ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf")); ++ ++ FPDF_COLORSCHEME color_scheme{kRed, kGreen, kWhite, kWhite}; ++ VerifyRenderingWithColorScheme( ++ /*page_num=*/0, FPDF_ANNOT | FPDF_CONVERT_FILL_TO_STROKE, &color_scheme, ++ kBlue, 612, 792, md5_content_with_highlight); ++} ++ ++TEST_F(FPDFProgressiveRenderEmbedderTest, RenderInkWithColorScheme) { ++ // Test rendering of multiple ink with forced color scheme on. ++ const char* content_with_ink_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "ebc57721e4c8da34156e09b9b2e62fb0"; ++ return "797bce7dc6c50ee86b095405df9fe5aa"; ++ }(); ++ ++ ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf")); ++ ++ FPDF_COLORSCHEME color_scheme{kBlack, kGreen, kRed, kRed}; ++ VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, &color_scheme, ++ kBlack, 612, 792, content_with_ink_checksum); ++} ++ ++TEST_F(FPDFProgressiveRenderEmbedderTest, RenderStampWithColorScheme) { ++ // Test rendering of static annotation with forced color scheme on. ++ const char* content_with_stamp_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "a791fdb4f595bb6c4187cc2aeed5e9e8"; ++#if BUILDFLAG(IS_APPLE) ++ return "8170c539e95f22f14eb8f266a5f1bbed"; ++#else ++ return "d1fd087e59d4dcebf47b56570bdb8c22"; ++#endif ++ }(); ++ ++ ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf")); ++ ++ FPDF_COLORSCHEME color_scheme{kBlue, kGreen, kRed, kRed}; ++ VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, &color_scheme, ++ kWhite, 595, 842, content_with_stamp_checksum); ++} ++ ++TEST_F(FPDFProgressiveRenderEmbedderTest, RenderFormWithColorScheme) { ++ // Test rendering of form does not change with forced color scheme on. ++ const char* content_with_form_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "9f75d98afc6d6313bd87e6562ea6df15"; ++ return "080f7a4381606659301440e1b14dca35"; ++ }(); ++ ++ ASSERT_TRUE(OpenDocument("annotiter.pdf")); ++ ++ FPDF_COLORSCHEME color_scheme{kGreen, kGreen, kRed, kRed}; ++ VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, &color_scheme, ++ kWhite, 612, 792, content_with_form_checksum); ++ ++ // Verify that the MD5 hash matches when rendered without |color_scheme|. ++ VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, ++ /*color_scheme=*/nullptr, kWhite, 612, 792, ++ content_with_form_checksum); ++} +diff --git a/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp b/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp +index 6de721c18..f3b6cae2b 100644 +--- a/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp ++++ b/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp +@@ -1,21 +1,20 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include +- + #include "public/cpp/fpdf_scopers.h" + #include "testing/embedder_test.h" ++#include "testing/embedder_test_constants.h" + #include "testing/gtest/include/gtest/gtest.h" + + class FPDFRenderPatternEmbedderTest : public EmbedderTest {}; + + TEST_F(FPDFRenderPatternEmbedderTest, LoadError_547706) { + // Test shading where object is a dictionary instead of a stream. +- EXPECT_TRUE(OpenDocument("bug_547706.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_547706.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3"); ++ CompareBitmap(bitmap.get(), 612, 792, pdfium::kBlankPage612By792Checksum); + UnloadPage(page); + } +diff --git a/core/fpdfdoc/Android.bp b/core/fpdfdoc/Android.bp +index 6931dc5a3..00321a4bc 100644 +--- a/core/fpdfdoc/Android.bp ++++ b/core/fpdfdoc/Android.bp +@@ -13,11 +13,8 @@ cc_library_static { + + visibility: ["//external/pdfium:__subpackages__"], + +- header_libs: [ +- "libpdfium-constants", +- ], +- + static_libs: [ ++ "libpdfium-constants", + "libpdfium-font", + "libpdfium-page", + "libpdfium-parser", +diff --git a/core/fpdfdoc/BUILD.gn b/core/fpdfdoc/BUILD.gn +index f25ace9ac..2e7a150f6 100644 +--- a/core/fpdfdoc/BUILD.gn ++++ b/core/fpdfdoc/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -7,10 +7,6 @@ import("../../testing/test.gni") + + source_set("fpdfdoc") { + sources = [ +- "cba_fontmap.cpp", +- "cba_fontmap.h", +- "cline.cpp", +- "cline.h", + "cpdf_aaction.cpp", + "cpdf_aaction.h", + "cpdf_action.cpp", +@@ -21,6 +17,8 @@ source_set("fpdfdoc") { + "cpdf_annotlist.h", + "cpdf_apsettings.cpp", + "cpdf_apsettings.h", ++ "cpdf_bafontmap.cpp", ++ "cpdf_bafontmap.h", + "cpdf_bookmark.cpp", + "cpdf_bookmark.h", + "cpdf_bookmarktree.cpp", +@@ -37,6 +35,8 @@ source_set("fpdfdoc") { + "cpdf_formcontrol.h", + "cpdf_formfield.cpp", + "cpdf_formfield.h", ++ "cpdf_generateap.cpp", ++ "cpdf_generateap.h", + "cpdf_icon.cpp", + "cpdf_icon.h", + "cpdf_iconfit.cpp", +@@ -59,29 +59,28 @@ source_set("fpdfdoc") { + "cpdf_structelement.h", + "cpdf_structtree.cpp", + "cpdf_structtree.h", +- "cpdf_variabletext.cpp", +- "cpdf_variabletext.h", + "cpdf_viewerpreferences.cpp", + "cpdf_viewerpreferences.h", + "cpvt_floatrect.h", + "cpvt_fontmap.cpp", + "cpvt_fontmap.h", +- "cpvt_generateap.cpp", +- "cpvt_generateap.h", + "cpvt_line.h", + "cpvt_lineinfo.h", ++ "cpvt_section.cpp", ++ "cpvt_section.h", ++ "cpvt_variabletext.cpp", ++ "cpvt_variabletext.h", + "cpvt_word.h", + "cpvt_wordinfo.cpp", + "cpvt_wordinfo.h", + "cpvt_wordplace.h", + "cpvt_wordrange.h", +- "csection.cpp", +- "csection.h", +- "ctypeset.cpp", +- "ctypeset.h", + "ipvt_fontmap.h", + ] +- configs += [ "../../:pdfium_core_config" ] ++ configs += [ ++ "../../:pdfium_strict_config", ++ "../../:pdfium_noshorten_config", ++ ] + deps = [ + "../../constants", + "../fpdfapi/font", +@@ -96,7 +95,9 @@ source_set("fpdfdoc") { + + pdfium_unittest_source_set("unittests") { + sources = [ ++ "cpdf_action_unittest.cpp", + "cpdf_annot_unittest.cpp", ++ "cpdf_bafontmap_unittest.cpp", + "cpdf_defaultappearance_unittest.cpp", + "cpdf_dest_unittest.cpp", + "cpdf_filespec_unittest.cpp", +@@ -106,7 +107,13 @@ pdfium_unittest_source_set("unittests") { + ] + deps = [ + ":fpdfdoc", ++ "../../constants", ++ "../fpdfapi/font", ++ "../fpdfapi/page", ++ "../fpdfapi/page:unit_test_support", + "../fpdfapi/parser", ++ "../fpdfapi/parser:unit_test_support", ++ "../fpdfapi/render", + ] + pdfium_root_dir = "../../" + } +diff --git a/core/fpdfdoc/cba_fontmap.cpp b/core/fpdfdoc/cba_fontmap.cpp +deleted file mode 100644 +index c71c724b4..000000000 +--- a/core/fpdfdoc/cba_fontmap.cpp ++++ /dev/null +@@ -1,493 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fpdfdoc/cba_fontmap.h" +- +-#include +-#include +- +-#include "constants/annotation_common.h" +-#include "core/fpdfapi/font/cpdf_font.h" +-#include "core/fpdfapi/font/cpdf_fontencoding.h" +-#include "core/fpdfapi/page/cpdf_docpagedata.h" +-#include "core/fpdfapi/page/cpdf_page.h" +-#include "core/fpdfapi/parser/cpdf_dictionary.h" +-#include "core/fpdfapi/parser/cpdf_document.h" +-#include "core/fpdfapi/parser/cpdf_parser.h" +-#include "core/fpdfapi/parser/cpdf_reference.h" +-#include "core/fpdfapi/parser/cpdf_stream.h" +-#include "core/fpdfapi/parser/fpdf_parser_utility.h" +-#include "core/fpdfdoc/cpdf_defaultappearance.h" +-#include "core/fpdfdoc/cpdf_formfield.h" +-#include "core/fpdfdoc/ipvt_fontmap.h" +-#include "core/fxcrt/fx_codepage.h" +-#include "core/fxge/cfx_fontmapper.h" +-#include "core/fxge/cfx_fontmgr.h" +-#include "core/fxge/cfx_gemodule.h" +-#include "core/fxge/cfx_substfont.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" +- +-namespace { +- +-bool FindNativeTrueTypeFont(ByteString sFontFaceName) { +- CFX_FontMgr* pFontMgr = CFX_GEModule::Get()->GetFontMgr(); +- if (!pFontMgr) +- return false; +- +- CFX_FontMapper* pFontMapper = pFontMgr->GetBuiltinMapper(); +- pFontMapper->LoadInstalledFonts(); +- +- for (const auto& font : pFontMapper->m_InstalledTTFonts) { +- if (font.Compare(sFontFaceName.AsStringView())) +- return true; +- } +- for (const auto& fontPair : pFontMapper->m_LocalizedTTFonts) { +- if (fontPair.first.Compare(sFontFaceName.AsStringView())) +- return true; +- } +- return false; +-} +- +-RetainPtr AddNativeTrueTypeFontToPDF(CPDF_Document* pDoc, +- ByteString sFontFaceName, +- uint8_t nCharset) { +- if (!pDoc) +- return nullptr; +- +- auto pFXFont = pdfium::MakeUnique(); +- pFXFont->LoadSubst(sFontFaceName, true, 0, 0, 0, +- FX_GetCodePageFromCharset(nCharset), false); +- +- auto* pDocPageData = CPDF_DocPageData::FromDocument(pDoc); +- return pDocPageData->AddFont(std::move(pFXFont), nCharset); +-} +- +-} // namespace +- +-CBA_FontMap::Data::Data() = default; +- +-CBA_FontMap::Data::~Data() = default; +- +-CBA_FontMap::CBA_FontMap(CPDF_Document* pDocument, CPDF_Dictionary* pAnnotDict) +- : m_pDocument(pDocument), m_pAnnotDict(pAnnotDict) { +- Initialize(); +-} +- +-CBA_FontMap::~CBA_FontMap() { +- Clear(); +-} +- +-RetainPtr CBA_FontMap::GetPDFFont(int32_t nFontIndex) { +- if (pdfium::IndexInBounds(m_Data, nFontIndex)) +- return m_Data[nFontIndex]->pFont; +- return nullptr; +-} +- +-ByteString CBA_FontMap::GetPDFFontAlias(int32_t nFontIndex) { +- if (pdfium::IndexInBounds(m_Data, nFontIndex)) +- return m_Data[nFontIndex]->sFontName; +- return ByteString(); +-} +- +-int32_t CBA_FontMap::GetWordFontIndex(uint16_t word, +- int32_t nCharset, +- int32_t nFontIndex) { +- if (nFontIndex > 0) { +- if (KnowWord(nFontIndex, word)) +- return nFontIndex; +- } else { +- if (!m_Data.empty()) { +- const Data* pData = m_Data.front().get(); +- if (nCharset == FX_CHARSET_Default || +- pData->nCharset == FX_CHARSET_Symbol || nCharset == pData->nCharset) { +- if (KnowWord(0, word)) +- return 0; +- } +- } +- } +- +- int32_t nNewFontIndex = +- GetFontIndex(GetCachedNativeFontName(nCharset), nCharset, true); +- if (nNewFontIndex >= 0) { +- if (KnowWord(nNewFontIndex, word)) +- return nNewFontIndex; +- } +- nNewFontIndex = GetFontIndex(CFX_Font::kUniversalDefaultFontName, +- FX_CHARSET_Default, false); +- if (nNewFontIndex >= 0) { +- if (KnowWord(nNewFontIndex, word)) +- return nNewFontIndex; +- } +- return -1; +-} +- +-int32_t CBA_FontMap::CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) { +- if (!pdfium::IndexInBounds(m_Data, nFontIndex)) +- return -1; +- +- Data* pData = m_Data[nFontIndex].get(); +- if (!pData->pFont) +- return -1; +- +- if (pData->pFont->IsUnicodeCompatible()) +- return pData->pFont->CharCodeFromUnicode(word); +- +- return word < 0xFF ? word : -1; +-} +- +-int32_t CBA_FontMap::CharSetFromUnicode(uint16_t word, int32_t nOldCharset) { +- // to avoid CJK Font to show ASCII +- if (word < 0x7F) +- return FX_CHARSET_ANSI; +- +- // follow the old charset +- if (nOldCharset != FX_CHARSET_Default) +- return nOldCharset; +- +- return CFX_Font::GetCharSetFromUnicode(word); +-} +- +-int32_t CBA_FontMap::GetNativeCharset() { +- return FX_GetCharsetFromCodePage(FXSYS_GetACP()); +-} +- +-void CBA_FontMap::Reset() { +- Clear(); +- m_pDefaultFont = nullptr; +- m_sDefaultFontName.clear(); +-} +- +-void CBA_FontMap::SetAPType(const ByteString& sAPType) { +- m_sAPType = sAPType; +- +- Reset(); +- Initialize(); +-} +- +-void CBA_FontMap::Initialize() { +- int32_t nCharset = FX_CHARSET_Default; +- +- if (!m_pDefaultFont) { +- m_pDefaultFont = GetAnnotDefaultFont(&m_sDefaultFontName); +- if (m_pDefaultFont) { +- if (const CFX_SubstFont* pSubstFont = m_pDefaultFont->GetSubstFont()) { +- nCharset = pSubstFont->m_Charset; +- } else { +- if (m_sDefaultFontName == "Wingdings" || +- m_sDefaultFontName == "Wingdings2" || +- m_sDefaultFontName == "Wingdings3" || +- m_sDefaultFontName == "Webdings") +- nCharset = FX_CHARSET_Symbol; +- else +- nCharset = FX_CHARSET_ANSI; +- } +- AddFontData(m_pDefaultFont, m_sDefaultFontName, nCharset); +- AddFontToAnnotDict(m_pDefaultFont, m_sDefaultFontName); +- } +- } +- +- if (nCharset != FX_CHARSET_ANSI) +- GetFontIndex(CFX_Font::kDefaultAnsiFontName, FX_CHARSET_ANSI, false); +-} +- +-RetainPtr CBA_FontMap::FindFontSameCharset(ByteString* sFontAlias, +- int32_t nCharset) { +- if (m_pAnnotDict->GetStringFor(pdfium::annotation::kSubtype) != "Widget") +- return nullptr; +- +- const CPDF_Dictionary* pRootDict = m_pDocument->GetRoot(); +- if (!pRootDict) +- return nullptr; +- +- const CPDF_Dictionary* pAcroFormDict = pRootDict->GetDictFor("AcroForm"); +- if (!pAcroFormDict) +- return nullptr; +- +- const CPDF_Dictionary* pDRDict = pAcroFormDict->GetDictFor("DR"); +- if (!pDRDict) +- return nullptr; +- +- return FindResFontSameCharset(pDRDict, sFontAlias, nCharset); +-} +- +-RetainPtr CBA_FontMap::FindResFontSameCharset( +- const CPDF_Dictionary* pResDict, +- ByteString* sFontAlias, +- int32_t nCharset) { +- if (!pResDict) +- return nullptr; +- +- const CPDF_Dictionary* pFonts = pResDict->GetDictFor("Font"); +- if (!pFonts) +- return nullptr; +- +- RetainPtr pFind; +- CPDF_DictionaryLocker locker(pFonts); +- for (const auto& it : locker) { +- const ByteString& csKey = it.first; +- if (!it.second) +- continue; +- +- CPDF_Dictionary* pElement = ToDictionary(it.second->GetDirect()); +- if (!pElement || pElement->GetStringFor("Type") != "Font") +- continue; +- +- auto* pData = CPDF_DocPageData::FromDocument(m_pDocument.Get()); +- RetainPtr pFont = pData->GetFont(pElement); +- if (!pFont) +- continue; +- +- const CFX_SubstFont* pSubst = pFont->GetSubstFont(); +- if (!pSubst) +- continue; +- +- if (pSubst->m_Charset == nCharset) { +- *sFontAlias = csKey; +- pFind = std::move(pFont); +- } +- } +- return pFind; +-} +- +-RetainPtr CBA_FontMap::GetAnnotDefaultFont(ByteString* sAlias) { +- CPDF_Dictionary* pAcroFormDict = nullptr; +- const bool bWidget = +- (m_pAnnotDict->GetStringFor(pdfium::annotation::kSubtype) == "Widget"); +- if (bWidget) { +- CPDF_Dictionary* pRootDict = m_pDocument->GetRoot(); +- if (pRootDict) +- pAcroFormDict = pRootDict->GetDictFor("AcroForm"); +- } +- +- ByteString sDA; +- const CPDF_Object* pObj = +- CPDF_FormField::GetFieldAttr(m_pAnnotDict.Get(), "DA"); +- if (pObj) +- sDA = pObj->GetString(); +- +- if (bWidget) { +- if (sDA.IsEmpty()) { +- pObj = CPDF_FormField::GetFieldAttr(pAcroFormDict, "DA"); +- sDA = pObj ? pObj->GetString() : ByteString(); +- } +- } +- if (sDA.IsEmpty()) +- return nullptr; +- +- CPDF_DefaultAppearance appearance(sDA); +- float font_size; +- Optional font = appearance.GetFont(&font_size); +- *sAlias = font.value_or(ByteString()); +- +- CPDF_Dictionary* pFontDict = nullptr; +- if (CPDF_Dictionary* pAPDict = +- m_pAnnotDict->GetDictFor(pdfium::annotation::kAP)) { +- if (CPDF_Dictionary* pNormalDict = pAPDict->GetDictFor("N")) { +- if (CPDF_Dictionary* pNormalResDict = +- pNormalDict->GetDictFor("Resources")) { +- if (CPDF_Dictionary* pResFontDict = pNormalResDict->GetDictFor("Font")) +- pFontDict = pResFontDict->GetDictFor(*sAlias); +- } +- } +- } +- if (bWidget && !pFontDict && pAcroFormDict) { +- if (CPDF_Dictionary* pDRDict = pAcroFormDict->GetDictFor("DR")) { +- if (CPDF_Dictionary* pDRFontDict = pDRDict->GetDictFor("Font")) +- pFontDict = pDRFontDict->GetDictFor(*sAlias); +- } +- } +- if (!pFontDict) +- return nullptr; +- +- return CPDF_DocPageData::FromDocument(m_pDocument.Get())->GetFont(pFontDict); +-} +- +-void CBA_FontMap::AddFontToAnnotDict(const RetainPtr& pFont, +- const ByteString& sAlias) { +- if (!pFont) +- return; +- +- CPDF_Dictionary* pAPDict = m_pAnnotDict->GetDictFor(pdfium::annotation::kAP); +- if (!pAPDict) +- pAPDict = m_pAnnotDict->SetNewFor(pdfium::annotation::kAP); +- +- // to avoid checkbox and radiobutton +- if (ToDictionary(pAPDict->GetObjectFor(m_sAPType))) +- return; +- +- CPDF_Stream* pStream = pAPDict->GetStreamFor(m_sAPType); +- if (!pStream) { +- pStream = m_pDocument->NewIndirect(); +- pAPDict->SetNewFor(m_sAPType, m_pDocument.Get(), +- pStream->GetObjNum()); +- } +- +- CPDF_Dictionary* pStreamDict = pStream->GetDict(); +- if (!pStreamDict) { +- auto pOwnedDict = m_pDocument->New(); +- pStreamDict = pOwnedDict.Get(); +- pStream->InitStream({}, std::move(pOwnedDict)); +- } +- +- CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources"); +- if (!pStreamResList) +- pStreamResList = pStreamDict->SetNewFor("Resources"); +- CPDF_Dictionary* pStreamResFontList = pStreamResList->GetDictFor("Font"); +- if (!pStreamResFontList) { +- pStreamResFontList = m_pDocument->NewIndirect(); +- pStreamResList->SetNewFor("Font", m_pDocument.Get(), +- pStreamResFontList->GetObjNum()); +- } +- if (!pStreamResFontList->KeyExist(sAlias)) { +- CPDF_Dictionary* pFontDict = pFont->GetFontDict(); +- RetainPtr pObject = +- pFontDict->IsInline() ? pFontDict->Clone() +- : pFontDict->MakeReference(m_pDocument.Get()); +- pStreamResFontList->SetFor(sAlias, std::move(pObject)); +- } +-} +- +-bool CBA_FontMap::KnowWord(int32_t nFontIndex, uint16_t word) { +- return pdfium::IndexInBounds(m_Data, nFontIndex) && +- CharCodeFromUnicode(nFontIndex, word) >= 0; +-} +- +-void CBA_FontMap::Clear() { +- m_Data.clear(); +- m_NativeFont.clear(); +-} +- +-int32_t CBA_FontMap::GetFontIndex(const ByteString& sFontName, +- int32_t nCharset, +- bool bFind) { +- int32_t nFontIndex = FindFont(EncodeFontAlias(sFontName, nCharset), nCharset); +- if (nFontIndex >= 0) +- return nFontIndex; +- +- ByteString sAlias; +- RetainPtr pFont = +- bFind ? FindFontSameCharset(&sAlias, nCharset) : nullptr; +- if (!pFont) { +- ByteString sTemp = sFontName; +- pFont = AddFontToDocument(sTemp, nCharset); +- sAlias = EncodeFontAlias(sTemp, nCharset); +- } +- AddFontToAnnotDict(pFont, sAlias); +- return AddFontData(pFont, sAlias, nCharset); +-} +- +-int32_t CBA_FontMap::AddFontData(const RetainPtr& pFont, +- const ByteString& sFontAlias, +- int32_t nCharset) { +- auto pNewData = pdfium::MakeUnique(); +- pNewData->pFont = pFont; +- pNewData->sFontName = sFontAlias; +- pNewData->nCharset = nCharset; +- m_Data.push_back(std::move(pNewData)); +- return pdfium::CollectionSize(m_Data) - 1; +-} +- +-ByteString CBA_FontMap::EncodeFontAlias(const ByteString& sFontName, +- int32_t nCharset) { +- return EncodeFontAlias(sFontName) + ByteString::Format("_%02X", nCharset); +-} +- +-ByteString CBA_FontMap::EncodeFontAlias(const ByteString& sFontName) { +- ByteString sRet = sFontName; +- sRet.Remove(' '); +- return sRet; +-} +- +-int32_t CBA_FontMap::FindFont(const ByteString& sFontName, int32_t nCharset) { +- int32_t i = 0; +- for (const auto& pData : m_Data) { +- if ((nCharset == FX_CHARSET_Default || nCharset == pData->nCharset) && +- (sFontName.IsEmpty() || pData->sFontName == sFontName)) { +- return i; +- } +- ++i; +- } +- return -1; +-} +- +-ByteString CBA_FontMap::GetNativeFontName(int32_t nCharset) { +- if (nCharset == FX_CHARSET_Default) +- nCharset = GetNativeCharset(); +- +- ByteString sFontName = CFX_Font::GetDefaultFontNameByCharset(nCharset); +- if (!FindNativeTrueTypeFont(sFontName)) +- return ByteString(); +- +- return sFontName; +-} +- +-ByteString CBA_FontMap::GetCachedNativeFontName(int32_t nCharset) { +- for (const auto& pData : m_NativeFont) { +- if (pData && pData->nCharset == nCharset) +- return pData->sFontName; +- } +- +- ByteString sNew = GetNativeFontName(nCharset); +- if (sNew.IsEmpty()) +- return ByteString(); +- +- auto pNewData = pdfium::MakeUnique(); +- pNewData->nCharset = nCharset; +- pNewData->sFontName = sNew; +- m_NativeFont.push_back(std::move(pNewData)); +- return sNew; +-} +- +-RetainPtr CBA_FontMap::AddFontToDocument(ByteString sFontName, +- uint8_t nCharset) { +- if (IsStandardFont(sFontName)) +- return AddStandardFont(sFontName); +- +- return AddSystemFont(sFontName, nCharset); +-} +- +-bool CBA_FontMap::IsStandardFont(const ByteString& sFontName) { +- static const char* const kStandardFontNames[] = {"Courier", +- "Courier-Bold", +- "Courier-BoldOblique", +- "Courier-Oblique", +- "Helvetica", +- "Helvetica-Bold", +- "Helvetica-BoldOblique", +- "Helvetica-Oblique", +- "Times-Roman", +- "Times-Bold", +- "Times-Italic", +- "Times-BoldItalic", +- "Symbol", +- "ZapfDingbats"}; +- for (const char* name : kStandardFontNames) { +- if (sFontName == name) +- return true; +- } +- return false; +-} +- +-RetainPtr CBA_FontMap::AddStandardFont(ByteString sFontName) { +- auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument.Get()); +- if (sFontName == "ZapfDingbats") +- return pPageData->AddStandardFont(sFontName, nullptr); +- +- static const CPDF_FontEncoding fe(PDFFONT_ENCODING_WINANSI); +- return pPageData->AddStandardFont(sFontName, &fe); +-} +- +-RetainPtr CBA_FontMap::AddSystemFont(ByteString sFontName, +- uint8_t nCharset) { +- if (sFontName.IsEmpty()) +- sFontName = GetNativeFontName(nCharset); +- +- if (nCharset == FX_CHARSET_Default) +- nCharset = GetNativeCharset(); +- +- return AddNativeTrueTypeFontToPDF(m_pDocument.Get(), sFontName, nCharset); +-} +diff --git a/core/fpdfdoc/cline.cpp b/core/fpdfdoc/cline.cpp +deleted file mode 100644 +index 7940dc81b..000000000 +--- a/core/fpdfdoc/cline.cpp ++++ /dev/null +@@ -1,38 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fpdfdoc/cline.h" +- +-CLine::CLine(const CPVT_LineInfo& lineinfo) : m_LineInfo(lineinfo) {} +- +-CLine::~CLine() = default; +- +-CPVT_WordPlace CLine::GetBeginWordPlace() const { +- return CPVT_WordPlace(LinePlace.nSecIndex, LinePlace.nLineIndex, -1); +-} +- +-CPVT_WordPlace CLine::GetEndWordPlace() const { +- return CPVT_WordPlace(LinePlace.nSecIndex, LinePlace.nLineIndex, +- m_LineInfo.nEndWordIndex); +-} +- +-CPVT_WordPlace CLine::GetPrevWordPlace(const CPVT_WordPlace& place) const { +- if (place.nWordIndex > m_LineInfo.nEndWordIndex) { +- return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, +- m_LineInfo.nEndWordIndex); +- } +- return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, +- place.nWordIndex - 1); +-} +- +-CPVT_WordPlace CLine::GetNextWordPlace(const CPVT_WordPlace& place) const { +- if (place.nWordIndex < m_LineInfo.nBeginWordIndex) { +- return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, +- m_LineInfo.nBeginWordIndex); +- } +- return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, +- place.nWordIndex + 1); +-} +diff --git a/core/fpdfdoc/cline.h b/core/fpdfdoc/cline.h +deleted file mode 100644 +index b7e32d46a..000000000 +--- a/core/fpdfdoc/cline.h ++++ /dev/null +@@ -1,26 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FPDFDOC_CLINE_H_ +-#define CORE_FPDFDOC_CLINE_H_ +- +-#include "core/fpdfdoc/cpvt_lineinfo.h" +-#include "core/fpdfdoc/cpvt_wordplace.h" +- +-class CLine { +- public: +- explicit CLine(const CPVT_LineInfo& lineinfo); +- ~CLine(); +- +- CPVT_WordPlace GetBeginWordPlace() const; +- CPVT_WordPlace GetEndWordPlace() const; +- CPVT_WordPlace GetPrevWordPlace(const CPVT_WordPlace& place) const; +- CPVT_WordPlace GetNextWordPlace(const CPVT_WordPlace& place) const; +- CPVT_WordPlace LinePlace; +- CPVT_LineInfo m_LineInfo; +-}; +- +-#endif // CORE_FPDFDOC_CLINE_H_ +diff --git a/core/fpdfdoc/cpdf_aaction.cpp b/core/fpdfdoc/cpdf_aaction.cpp +index 8284913fe..4b62a7fea 100644 +--- a/core/fpdfdoc/cpdf_aaction.cpp ++++ b/core/fpdfdoc/cpdf_aaction.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,9 @@ + + #include "core/fpdfdoc/cpdf_aaction.h" + ++#include ++#include ++ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + + namespace { +@@ -36,12 +39,13 @@ constexpr const char* kAATypes[] = { + + // |kAATypes| should have one less element than enum AActionType due to + // |kDocumentOpen|, which is an artificial type. +-static_assert(FX_ArraySize(kAATypes) == CPDF_AAction::kNumberOfActions - 1, ++static_assert(std::size(kAATypes) == CPDF_AAction::kNumberOfActions - 1, + "kAATypes count mismatch"); + + } // namespace + +-CPDF_AAction::CPDF_AAction(const CPDF_Dictionary* pDict) : m_pDict(pDict) {} ++CPDF_AAction::CPDF_AAction(RetainPtr pDict) ++ : m_pDict(std::move(pDict)) {} + + CPDF_AAction::CPDF_AAction(const CPDF_AAction& that) = default; + +@@ -56,10 +60,11 @@ CPDF_Action CPDF_AAction::GetAction(AActionType eType) const { + } + + // static +-bool CPDF_AAction::IsUserClick(AActionType eType) { +- switch (eType) { ++bool CPDF_AAction::IsUserInput(AActionType type) { ++ switch (type) { + case kButtonUp: + case kButtonDown: ++ case kKeyStroke: + return true; + default: + return false; +diff --git a/core/fpdfdoc/cpdf_aaction.h b/core/fpdfdoc/cpdf_aaction.h +index c3c65e8dd..9b4cbcbee 100644 +--- a/core/fpdfdoc/cpdf_aaction.h ++++ b/core/fpdfdoc/cpdf_aaction.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,10 @@ + #ifndef CORE_FPDFDOC_CPDF_AACTION_H_ + #define CORE_FPDFDOC_CPDF_AACTION_H_ + ++#include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfdoc/cpdf_action.h" + #include "core/fxcrt/retain_ptr.h" + +-class CPDF_Dictionary; +- + class CPDF_AAction { + public: + enum AActionType { +@@ -40,15 +39,15 @@ class CPDF_AAction { + kNumberOfActions // Must be last. + }; + +- explicit CPDF_AAction(const CPDF_Dictionary* pDict); ++ explicit CPDF_AAction(RetainPtr pDict); + CPDF_AAction(const CPDF_AAction& that); + ~CPDF_AAction(); + + bool ActionExist(AActionType eType) const; + CPDF_Action GetAction(AActionType eType) const; +- const CPDF_Dictionary* GetDict() const { return m_pDict.Get(); } ++ bool HasDict() const { return !!m_pDict; } + +- static bool IsUserClick(AActionType eType); ++ static bool IsUserInput(AActionType type); + + private: + RetainPtr const m_pDict; +diff --git a/core/fpdfdoc/cpdf_action.cpp b/core/fpdfdoc/cpdf_action.cpp +index 5133c1b55..1370c7f1e 100644 +--- a/core/fpdfdoc/cpdf_action.cpp ++++ b/core/fpdfdoc/cpdf_action.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,105 +6,94 @@ + + #include "core/fpdfdoc/cpdf_action.h" + ++#include ++#include ++ + #include "constants/stream_dict_common.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_name.h" ++#include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fpdfdoc/cpdf_filespec.h" +-#include "core/fpdfdoc/cpdf_nametree.h" + + namespace { + +-const char* const g_sATypes[] = { +- "Unknown", "GoTo", "GoToR", "GoToE", "Launch", +- "Thread", "URI", "Sound", "Movie", "Hide", +- "Named", "SubmitForm", "ResetForm", "ImportData", "JavaScript", +- "SetOCGState", "Rendition", "Trans", "GoTo3DView", nullptr}; ++const char* const kActionTypeStrings[] = { ++ "GoTo", "GoToR", "GoToE", "Launch", "Thread", ++ "URI", "Sound", "Movie", "Hide", "Named", ++ "SubmitForm", "ResetForm", "ImportData", "JavaScript", "SetOCGState", ++ "Rendition", "Trans", "GoTo3DView"}; + + } // namespace + +-CPDF_Action::CPDF_Action(const CPDF_Dictionary* pDict) : m_pDict(pDict) {} ++CPDF_Action::CPDF_Action(RetainPtr pDict) ++ : m_pDict(std::move(pDict)) {} + + CPDF_Action::CPDF_Action(const CPDF_Action& that) = default; + + CPDF_Action::~CPDF_Action() = default; + +-CPDF_Action::ActionType CPDF_Action::GetType() const { +- if (!m_pDict) +- return Unknown; +- +- // Validate |m_pDict|. Type is optional, but must be valid if present. +- const CPDF_Object* pType = m_pDict->GetObjectFor("Type"); +- if (pType) { +- const CPDF_Name* pName = pType->AsName(); +- if (!pName || pName->GetString() != "Action") +- return Unknown; +- } ++CPDF_Action::Type CPDF_Action::GetType() const { ++ // See ISO 32000-1:2008 spec, table 193. ++ if (!ValidateDictOptionalType(m_pDict.Get(), "Action")) ++ return Type::kUnknown; + +- ByteString csType = m_pDict->GetStringFor("S"); ++ ByteString csType = m_pDict->GetNameFor("S"); + if (csType.IsEmpty()) +- return Unknown; +- +- for (int i = 0; g_sATypes[i]; ++i) { +- if (csType == g_sATypes[i]) +- return static_cast(i); ++ return Type::kUnknown; ++ ++ static_assert( ++ std::size(kActionTypeStrings) == static_cast(Type::kLast), ++ "Type mismatch"); ++ for (size_t i = 0; i < std::size(kActionTypeStrings); ++i) { ++ if (csType == kActionTypeStrings[i]) ++ return static_cast(i + 1); + } +- return Unknown; ++ return Type::kUnknown; + } + + CPDF_Dest CPDF_Action::GetDest(CPDF_Document* pDoc) const { +- ActionType type = GetType(); +- if (type != GoTo && type != GoToR) +- return CPDF_Dest(); +- +- const CPDF_Object* pDest = m_pDict->GetDirectObjectFor("D"); +- if (!pDest) +- return CPDF_Dest(); +- if (pDest->IsString() || pDest->IsName()) { +- CPDF_NameTree name_tree(pDoc, "Dests"); +- return CPDF_Dest(name_tree.LookupNamedDest(pDoc, pDest->GetUnicodeText())); ++ Type type = GetType(); ++ if (type != Type::kGoTo && type != Type::kGoToR && type != Type::kGoToE) { ++ return CPDF_Dest(nullptr); + } +- if (const CPDF_Array* pArray = pDest->AsArray()) +- return CPDF_Dest(pArray); +- +- return CPDF_Dest(); ++ return CPDF_Dest::Create(pDoc, m_pDict->GetDirectObjectFor("D")); + } + + WideString CPDF_Action::GetFilePath() const { +- ActionType type = GetType(); +- if (type != GoToR && type != Launch && type != SubmitForm && +- type != ImportData) { ++ Type type = GetType(); ++ if (type != Type::kGoToR && type != Type::kGoToE && type != Type::kLaunch && ++ type != Type::kSubmitForm && type != Type::kImportData) { + return WideString(); + } + +- const CPDF_Object* pFile = m_pDict->GetDirectObjectFor(pdfium::stream::kF); ++ RetainPtr pFile = ++ m_pDict->GetDirectObjectFor(pdfium::stream::kF); + if (pFile) +- return CPDF_FileSpec(pFile).GetFileName(); ++ return CPDF_FileSpec(std::move(pFile)).GetFileName(); + +- if (type != Launch) ++ if (type != Type::kLaunch) + return WideString(); + +- const CPDF_Dictionary* pWinDict = m_pDict->GetDictFor("Win"); ++ RetainPtr pWinDict = m_pDict->GetDictFor("Win"); + if (!pWinDict) + return WideString(); + + return WideString::FromDefANSI( +- pWinDict->GetStringFor(pdfium::stream::kF).AsStringView()); ++ pWinDict->GetByteStringFor(pdfium::stream::kF).AsStringView()); + } + + ByteString CPDF_Action::GetURI(const CPDF_Document* pDoc) const { +- ActionType type = GetType(); +- if (type != URI) ++ if (GetType() != Type::kURI) + return ByteString(); + +- ByteString csURI = m_pDict->GetStringFor("URI"); +- const CPDF_Dictionary* pRoot = pDoc->GetRoot(); +- const CPDF_Dictionary* pURI = pRoot->GetDictFor("URI"); ++ ByteString csURI = m_pDict->GetByteStringFor("URI"); ++ RetainPtr pURI = pDoc->GetRoot()->GetDictFor("URI"); + if (pURI) { + auto result = csURI.Find(":"); + if (!result.has_value() || result.value() == 0) { +- auto* pBase = pURI->GetDirectObjectFor("Base"); ++ RetainPtr pBase = pURI->GetDirectObjectFor("Base"); + if (pBase && (pBase->IsString() || pBase->IsStream())) + csURI = pBase->GetString() + csURI; + } +@@ -117,46 +106,55 @@ bool CPDF_Action::GetHideStatus() const { + } + + ByteString CPDF_Action::GetNamedAction() const { +- return m_pDict->GetStringFor("N"); ++ return m_pDict->GetByteStringFor("N"); + } + + uint32_t CPDF_Action::GetFlags() const { + return m_pDict->GetIntegerFor("Flags"); + } + +-std::vector CPDF_Action::GetAllFields() const { +- std::vector result; ++bool CPDF_Action::HasFields() const { ++ return m_pDict->KeyExist("Fields"); ++} ++ ++std::vector> CPDF_Action::GetAllFields() const { ++ std::vector> result; + if (!m_pDict) + return result; + +- ByteString csType = m_pDict->GetStringFor("S"); +- const CPDF_Object* pFields = csType == "Hide" +- ? m_pDict->GetDirectObjectFor("T") +- : m_pDict->GetArrayFor("Fields"); ++ ByteString csType = m_pDict->GetByteStringFor("S"); ++ RetainPtr pFields = csType == "Hide" ++ ? m_pDict->GetDirectObjectFor("T") ++ : m_pDict->GetArrayFor("Fields"); + if (!pFields) + return result; + + if (pFields->IsDictionary() || pFields->IsString()) { +- result.push_back(pFields); +- } else if (const CPDF_Array* pArray = pFields->AsArray()) { +- for (size_t i = 0; i < pArray->size(); ++i) { +- const CPDF_Object* pObj = pArray->GetDirectObjectAt(i); +- if (pObj) +- result.push_back(pObj); +- } ++ result.push_back(std::move(pFields)); ++ return result; ++ } ++ ++ const CPDF_Array* pArray = pFields->AsArray(); ++ if (!pArray) ++ return result; ++ ++ for (size_t i = 0; i < pArray->size(); ++i) { ++ RetainPtr pObj = pArray->GetDirectObjectAt(i); ++ if (pObj) ++ result.push_back(std::move(pObj)); + } + return result; + } + +-Optional CPDF_Action::MaybeGetJavaScript() const { +- const CPDF_Object* pObject = GetJavaScriptObject(); ++absl::optional CPDF_Action::MaybeGetJavaScript() const { ++ RetainPtr pObject = GetJavaScriptObject(); + if (!pObject) +- return pdfium::nullopt; ++ return absl::nullopt; + return pObject->GetUnicodeText(); + } + + WideString CPDF_Action::GetJavaScript() const { +- const CPDF_Object* pObject = GetJavaScriptObject(); ++ RetainPtr pObject = GetJavaScriptObject(); + return pObject ? pObject->GetUnicodeText() : WideString(); + } + +@@ -164,7 +162,7 @@ size_t CPDF_Action::GetSubActionsCount() const { + if (!m_pDict || !m_pDict->KeyExist("Next")) + return 0; + +- const CPDF_Object* pNext = m_pDict->GetDirectObjectFor("Next"); ++ RetainPtr pNext = m_pDict->GetDirectObjectFor("Next"); + if (!pNext) + return 0; + if (pNext->IsDictionary()) +@@ -177,20 +175,24 @@ CPDF_Action CPDF_Action::GetSubAction(size_t iIndex) const { + if (!m_pDict || !m_pDict->KeyExist("Next")) + return CPDF_Action(nullptr); + +- const CPDF_Object* pNext = m_pDict->GetDirectObjectFor("Next"); +- if (const CPDF_Array* pArray = ToArray(pNext)) ++ RetainPtr pNext = m_pDict->GetDirectObjectFor("Next"); ++ if (!pNext) ++ return CPDF_Action(nullptr); ++ ++ if (const CPDF_Array* pArray = pNext->AsArray()) + return CPDF_Action(pArray->GetDictAt(iIndex)); +- if (const CPDF_Dictionary* pDict = ToDictionary(pNext)) { ++ ++ if (const CPDF_Dictionary* pDict = pNext->AsDictionary()) { + if (iIndex == 0) +- return CPDF_Action(pDict); ++ return CPDF_Action(pdfium::WrapRetain(pDict)); + } + return CPDF_Action(nullptr); + } + +-const CPDF_Object* CPDF_Action::GetJavaScriptObject() const { ++RetainPtr CPDF_Action::GetJavaScriptObject() const { + if (!m_pDict) + return nullptr; + +- const CPDF_Object* pJS = m_pDict->GetDirectObjectFor("JS"); ++ RetainPtr pJS = m_pDict->GetDirectObjectFor("JS"); + return (pJS && (pJS->IsString() || pJS->IsStream())) ? pJS : nullptr; + } +diff --git a/core/fpdfdoc/cpdf_action.h b/core/fpdfdoc/cpdf_action.h +index c7cc2d8e8..cb9e930a9 100644 +--- a/core/fpdfdoc/cpdf_action.h ++++ b/core/fpdfdoc/cpdf_action.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,7 +12,7 @@ + #include "core/fpdfdoc/cpdf_dest.h" + #include "core/fxcrt/fx_string.h" + #include "core/fxcrt/retain_ptr.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CPDF_Dictionary; + class CPDF_Document; +@@ -20,46 +20,49 @@ class CPDF_Object; + + class CPDF_Action { + public: +- enum ActionType { +- Unknown = 0, +- GoTo, +- GoToR, +- GoToE, +- Launch, +- Thread, +- URI, +- Sound, +- Movie, +- Hide, +- Named, +- SubmitForm, +- ResetForm, +- ImportData, +- JavaScript, +- SetOCGState, +- Rendition, +- Trans, +- GoTo3DView ++ enum class Type { ++ kUnknown = 0, ++ kGoTo, ++ kGoToR, ++ kGoToE, ++ kLaunch, ++ kThread, ++ kURI, ++ kSound, ++ kMovie, ++ kHide, ++ kNamed, ++ kSubmitForm, ++ kResetForm, ++ kImportData, ++ kJavaScript, ++ kSetOCGState, ++ kRendition, ++ kTrans, ++ kGoTo3DView, ++ kLast = kGoTo3DView + }; + +- explicit CPDF_Action(const CPDF_Dictionary* pDict); ++ explicit CPDF_Action(RetainPtr pDict); + CPDF_Action(const CPDF_Action& that); + ~CPDF_Action(); + ++ bool HasDict() const { return !!m_pDict; } + const CPDF_Dictionary* GetDict() const { return m_pDict.Get(); } + +- ActionType GetType() const; ++ Type GetType() const; + CPDF_Dest GetDest(CPDF_Document* pDoc) const; + WideString GetFilePath() const; + ByteString GetURI(const CPDF_Document* pDoc) const; + bool GetHideStatus() const; + ByteString GetNamedAction() const; + uint32_t GetFlags() const; ++ bool HasFields() const; + +- std::vector GetAllFields() const; ++ std::vector> GetAllFields() const; + + // Differentiates between empty JS entry and no JS entry. +- Optional MaybeGetJavaScript() const; ++ absl::optional MaybeGetJavaScript() const; + + // Returns empty string for empty JS entry and no JS entry. + WideString GetJavaScript() const; +@@ -68,7 +71,7 @@ class CPDF_Action { + CPDF_Action GetSubAction(size_t iIndex) const; + + private: +- const CPDF_Object* GetJavaScriptObject() const; ++ RetainPtr GetJavaScriptObject() const; + + RetainPtr const m_pDict; + }; +diff --git a/core/fpdfdoc/cpdf_action_unittest.cpp b/core/fpdfdoc/cpdf_action_unittest.cpp +new file mode 100644 +index 000000000..a787711d7 +--- /dev/null ++++ b/core/fpdfdoc/cpdf_action_unittest.cpp +@@ -0,0 +1,127 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fpdfdoc/cpdf_action.h" ++ ++#include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fpdfapi/parser/cpdf_name.h" ++#include "core/fpdfapi/parser/cpdf_string.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++namespace { ++ ++RetainPtr CreateActionDictWithType( ++ const ByteString& action_type) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "Action"); ++ dict->SetNewFor("S", action_type); ++ return dict; ++} ++ ++RetainPtr CreateActionDictWithoutType( ++ const ByteString& action_type) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("S", action_type); ++ return dict; ++} ++ ++RetainPtr CreateActionDictWithInvalidType( ++ const ByteString& action_type) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "Lights"); ++ dict->SetNewFor("S", action_type); ++ return dict; ++} ++ ++RetainPtr CreateInvalidActionDictWithType( ++ const ByteString& action_type) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("Type", "Action"); ++ dict->SetNewFor("S", action_type, /*is_hex=*/false); ++ return dict; ++} ++ ++RetainPtr CreateInvalidActionDictWithoutType( ++ const ByteString& action_type) { ++ auto dict = pdfium::MakeRetain(); ++ dict->SetNewFor("S", action_type, /*is_hex=*/false); ++ return dict; ++} ++ ++} // namespace ++ ++TEST(CPDFActionTest, GetType) { ++ static constexpr struct { ++ const char* action_type; ++ CPDF_Action::Type expected_type; ++ } kValidTestCases[] = { ++ {"GoTo", CPDF_Action::Type::kGoTo}, ++ {"GoToR", CPDF_Action::Type::kGoToR}, ++ {"GoToE", CPDF_Action::Type::kGoToE}, ++ {"Launch", CPDF_Action::Type::kLaunch}, ++ {"Thread", CPDF_Action::Type::kThread}, ++ {"URI", CPDF_Action::Type::kURI}, ++ {"Sound", CPDF_Action::Type::kSound}, ++ {"Movie", CPDF_Action::Type::kMovie}, ++ {"Hide", CPDF_Action::Type::kHide}, ++ {"Named", CPDF_Action::Type::kNamed}, ++ {"SubmitForm", CPDF_Action::Type::kSubmitForm}, ++ {"ResetForm", CPDF_Action::Type::kResetForm}, ++ {"ImportData", CPDF_Action::Type::kImportData}, ++ {"JavaScript", CPDF_Action::Type::kJavaScript}, ++ {"SetOCGState", CPDF_Action::Type::kSetOCGState}, ++ {"Rendition", CPDF_Action::Type::kRendition}, ++ {"Trans", CPDF_Action::Type::kTrans}, ++ {"GoTo3DView", CPDF_Action::Type::kGoTo3DView}, ++ }; ++ ++ // Test correctly constructed actions. ++ for (const auto& test_case : kValidTestCases) { ++ { ++ // Type is present. ++ CPDF_Action action(CreateActionDictWithType(test_case.action_type)); ++ EXPECT_EQ(test_case.expected_type, action.GetType()); ++ } ++ { ++ // Type is optional, so omitting it is ok. ++ CPDF_Action action(CreateActionDictWithoutType(test_case.action_type)); ++ EXPECT_EQ(test_case.expected_type, action.GetType()); ++ } ++ } ++ ++ // Test incorrectly constructed actions. ++ for (const auto& test_case : kValidTestCases) { ++ { ++ // Type is optional, but must be valid if present. ++ CPDF_Action action( ++ CreateActionDictWithInvalidType(test_case.action_type)); ++ EXPECT_EQ(CPDF_Action::Type::kUnknown, action.GetType()); ++ } ++ { ++ // The action type (/S) must be a name. ++ CPDF_Action action( ++ CreateInvalidActionDictWithType(test_case.action_type)); ++ EXPECT_EQ(CPDF_Action::Type::kUnknown, action.GetType()); ++ } ++ { ++ // The action type (/S) must be a name. ++ CPDF_Action action( ++ CreateInvalidActionDictWithoutType(test_case.action_type)); ++ EXPECT_EQ(CPDF_Action::Type::kUnknown, action.GetType()); ++ } ++ } ++ ++ static constexpr const char* kInvalidTestCases[] = { ++ "Camera", ++ "Javascript", ++ "Unknown", ++ }; ++ ++ // Test invalid actions. ++ for (const char* test_case : kInvalidTestCases) { ++ CPDF_Action action(CreateActionDictWithType(test_case)); ++ EXPECT_EQ(CPDF_Action::Type::kUnknown, action.GetType()); ++ } ++} +diff --git a/core/fpdfdoc/cpdf_annot.cpp b/core/fpdfdoc/cpdf_annot.cpp +index f4740747e..e0ecd882d 100644 +--- a/core/fpdfdoc/cpdf_annot.cpp ++++ b/core/fpdfdoc/cpdf_annot.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,19 +13,21 @@ + #include "constants/annotation_flags.h" + #include "core/fpdfapi/page/cpdf_form.h" + #include "core/fpdfapi/page/cpdf_page.h" ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_boolean.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" +-#include "core/fpdfapi/render/cpdf_pagerendercache.h" + #include "core/fpdfapi/render/cpdf_rendercontext.h" + #include "core/fpdfapi/render/cpdf_renderoptions.h" +-#include "core/fpdfdoc/cpvt_generateap.h" ++#include "core/fpdfdoc/cpdf_generateap.h" ++#include "core/fxge/cfx_fillrenderoptions.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/cfx_renderdevice.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -38,10 +40,10 @@ bool IsTextMarkupAnnotation(CPDF_Annot::Subtype type) { + type == CPDF_Annot::Subtype::UNDERLINE; + } + +-CPDF_Form* AnnotGetMatrix(const CPDF_Page* pPage, ++CPDF_Form* AnnotGetMatrix(CPDF_Page* pPage, + CPDF_Annot* pAnnot, + CPDF_Annot::AppearanceMode mode, +- const CFX_Matrix* pUser2Device, ++ const CFX_Matrix& mtUser2Device, + CFX_Matrix* matrix) { + CPDF_Form* pForm = pAnnot->GetAPForm(pPage, mode); + if (!pForm) +@@ -51,77 +53,88 @@ CPDF_Form* AnnotGetMatrix(const CPDF_Page* pPage, + CFX_FloatRect form_bbox = + form_matrix.TransformRect(pForm->GetDict()->GetRectFor("BBox")); + matrix->MatchRect(pAnnot->GetRect(), form_bbox); +- matrix->Concat(*pUser2Device); ++ ++ // Compensate for page rotation. ++ if ((pAnnot->GetFlags() & pdfium::annotation_flags::kNoRotate) && ++ pPage->GetPageRotation() != 0) { ++ // Rotate annotation rect around top-left angle (according to the ++ // specification). ++ const float offset_x = pAnnot->GetRect().Left(); ++ const float offset_y = pAnnot->GetRect().Top(); ++ matrix->Concat({1, 0, 0, 1, -offset_x, -offset_y}); ++ // GetPageRotation returns value in fractions of pi/2. ++ const float angle = FXSYS_PI / 2 * pPage->GetPageRotation(); ++ matrix->Rotate(angle); ++ matrix->Concat({1, 0, 0, 1, offset_x, offset_y}); ++ } ++ ++ matrix->Concat(mtUser2Device); + return pForm; + } + +-CPDF_Stream* GetAnnotAPInternal(CPDF_Dictionary* pAnnotDict, +- CPDF_Annot::AppearanceMode eMode, +- bool bFallbackToNormal) { +- CPDF_Dictionary* pAP = pAnnotDict->GetDictFor(pdfium::annotation::kAP); ++RetainPtr GetAnnotAPInternal(CPDF_Dictionary* pAnnotDict, ++ CPDF_Annot::AppearanceMode eMode, ++ bool bFallbackToNormal) { ++ RetainPtr pAP = ++ pAnnotDict->GetMutableDictFor(pdfium::annotation::kAP); + if (!pAP) + return nullptr; + + const char* ap_entry = "N"; +- if (eMode == CPDF_Annot::Down) ++ if (eMode == CPDF_Annot::AppearanceMode::kDown) + ap_entry = "D"; +- else if (eMode == CPDF_Annot::Rollover) ++ else if (eMode == CPDF_Annot::AppearanceMode::kRollover) + ap_entry = "R"; + if (bFallbackToNormal && !pAP->KeyExist(ap_entry)) + ap_entry = "N"; + +- CPDF_Object* psub = pAP->GetDirectObjectFor(ap_entry); ++ RetainPtr psub = pAP->GetMutableDirectObjectFor(ap_entry); + if (!psub) + return nullptr; +- if (CPDF_Stream* pStream = psub->AsStream()) ++ ++ RetainPtr pStream(psub->AsMutableStream()); ++ if (pStream) + return pStream; + +- CPDF_Dictionary* pDict = psub->AsDictionary(); ++ CPDF_Dictionary* pDict = psub->AsMutableDictionary(); + if (!pDict) + return nullptr; + +- ByteString as = pAnnotDict->GetStringFor(pdfium::annotation::kAS); ++ ByteString as = pAnnotDict->GetByteStringFor(pdfium::annotation::kAS); + if (as.IsEmpty()) { +- ByteString value = pAnnotDict->GetStringFor("V"); ++ ByteString value = pAnnotDict->GetByteStringFor("V"); + if (value.IsEmpty()) { +- const CPDF_Dictionary* pParentDict = pAnnotDict->GetDictFor("Parent"); +- value = pParentDict ? pParentDict->GetStringFor("V") : ByteString(); ++ RetainPtr pParentDict = ++ pAnnotDict->GetDictFor("Parent"); ++ value = pParentDict ? pParentDict->GetByteStringFor("V") : ByteString(); + } + as = (!value.IsEmpty() && pDict->KeyExist(value)) ? value : "Off"; + } +- return pDict->GetStreamFor(as); ++ return pDict->GetMutableStreamFor(as); + } + + } // namespace + + CPDF_Annot::CPDF_Annot(RetainPtr pDict, + CPDF_Document* pDocument) +- : m_pAnnotDict(std::move(pDict)), m_pDocument(pDocument) { +- Init(); +-} +- +-CPDF_Annot::CPDF_Annot(CPDF_Dictionary* pDict, CPDF_Document* pDocument) +- : m_pAnnotDict(pDict), m_pDocument(pDocument) { +- Init(); ++ : m_pAnnotDict(std::move(pDict)), ++ m_pDocument(pDocument), ++ m_nSubtype(StringToAnnotSubtype( ++ m_pAnnotDict->GetByteStringFor(pdfium::annotation::kSubtype))), ++ m_bIsTextMarkupAnnotation(IsTextMarkupAnnotation(m_nSubtype)), ++ m_bHasGeneratedAP( ++ m_pAnnotDict->GetBooleanFor(kPDFiumKey_HasGeneratedAP, false)) { ++ GenerateAPIfNeeded(); + } + + CPDF_Annot::~CPDF_Annot() { + ClearCachedAP(); + } + +-void CPDF_Annot::Init() { +- m_nSubtype = StringToAnnotSubtype( +- m_pAnnotDict->GetStringFor(pdfium::annotation::kSubtype)); +- m_bIsTextMarkupAnnotation = IsTextMarkupAnnotation(m_nSubtype); +- m_bHasGeneratedAP = +- m_pAnnotDict->GetBooleanFor(kPDFiumKey_HasGeneratedAP, false); +- GenerateAPIfNeeded(); +-} +- + void CPDF_Annot::GenerateAPIfNeeded() { + if (!ShouldGenerateAP()) + return; +- if (!CPVT_GenerateAP::GenerateAnnotAP(m_pDocument.Get(), m_pAnnotDict.Get(), ++ if (!CPDF_GenerateAP::GenerateAnnotAP(m_pDocument, m_pAnnotDict.Get(), + m_nSubtype)) { + return; + } +@@ -133,7 +146,7 @@ void CPDF_Annot::GenerateAPIfNeeded() { + bool CPDF_Annot::ShouldGenerateAP() const { + // If AP dictionary exists and defines an appearance for normal mode, we use + // the appearance defined in the existing AP dictionary. +- const CPDF_Dictionary* pAP = ++ RetainPtr pAP = + m_pAnnotDict->GetDictFor(pdfium::annotation::kAP); + if (pAP && pAP->GetDictFor("N")) + return false; +@@ -177,20 +190,20 @@ bool CPDF_Annot::IsHidden() const { + return !!(GetFlags() & pdfium::annotation_flags::kHidden); + } + +-CPDF_Stream* GetAnnotAP(CPDF_Dictionary* pAnnotDict, +- CPDF_Annot::AppearanceMode eMode) { +- ASSERT(pAnnotDict); ++RetainPtr GetAnnotAP(CPDF_Dictionary* pAnnotDict, ++ CPDF_Annot::AppearanceMode eMode) { ++ DCHECK(pAnnotDict); + return GetAnnotAPInternal(pAnnotDict, eMode, true); + } + +-CPDF_Stream* GetAnnotAPNoFallback(CPDF_Dictionary* pAnnotDict, +- CPDF_Annot::AppearanceMode eMode) { +- ASSERT(pAnnotDict); ++RetainPtr GetAnnotAPNoFallback(CPDF_Dictionary* pAnnotDict, ++ CPDF_Annot::AppearanceMode eMode) { ++ DCHECK(pAnnotDict); + return GetAnnotAPInternal(pAnnotDict, eMode, false); + } + +-CPDF_Form* CPDF_Annot::GetAPForm(const CPDF_Page* pPage, AppearanceMode mode) { +- CPDF_Stream* pStream = GetAnnotAP(m_pAnnotDict.Get(), mode); ++CPDF_Form* CPDF_Annot::GetAPForm(CPDF_Page* pPage, AppearanceMode mode) { ++ RetainPtr pStream = GetAnnotAP(m_pAnnotDict.Get(), mode); + if (!pStream) + return nullptr; + +@@ -198,8 +211,8 @@ CPDF_Form* CPDF_Annot::GetAPForm(const CPDF_Page* pPage, AppearanceMode mode) { + if (it != m_APMap.end()) + return it->second.get(); + +- auto pNewForm = pdfium::MakeUnique( +- m_pDocument.Get(), pPage->m_pResources.Get(), pStream); ++ auto pNewForm = std::make_unique( ++ m_pDocument, pPage->GetMutableResources(), pStream); + pNewForm->ParseContent(); + + CPDF_Form* pResult = pNewForm.get(); +@@ -207,11 +220,22 @@ CPDF_Form* CPDF_Annot::GetAPForm(const CPDF_Page* pPage, AppearanceMode mode) { + return pResult; + } + ++void CPDF_Annot::SetPopupAnnotOpenState(bool bOpenState) { ++ if (m_pPopupAnnot) ++ m_pPopupAnnot->SetOpenState(bOpenState); ++} ++ ++absl::optional CPDF_Annot::GetPopupAnnotRect() const { ++ if (!m_pPopupAnnot) ++ return absl::nullopt; ++ return m_pPopupAnnot->GetRect(); ++} ++ + // static + CFX_FloatRect CPDF_Annot::RectFromQuadPointsArray(const CPDF_Array* pArray, + size_t nIndex) { +- ASSERT(pArray); +- ASSERT(nIndex < pArray->size() / 8); ++ DCHECK(pArray); ++ DCHECK(nIndex < pArray->size() / 8); + + // QuadPoints are defined with 4 pairs of numbers + // ([ pair0, pair1, pair2, pair3 ]), where +@@ -225,22 +249,22 @@ CFX_FloatRect CPDF_Annot::RectFromQuadPointsArray(const CPDF_Array* pArray, + // pair1 = top_right. + + return CFX_FloatRect( +- pArray->GetNumberAt(4 + nIndex * 8), pArray->GetNumberAt(5 + nIndex * 8), +- pArray->GetNumberAt(2 + nIndex * 8), pArray->GetNumberAt(3 + nIndex * 8)); ++ pArray->GetFloatAt(4 + nIndex * 8), pArray->GetFloatAt(5 + nIndex * 8), ++ pArray->GetFloatAt(2 + nIndex * 8), pArray->GetFloatAt(3 + nIndex * 8)); + } + + // static + CFX_FloatRect CPDF_Annot::BoundingRectFromQuadPoints( + const CPDF_Dictionary* pAnnotDict) { + CFX_FloatRect ret; +- const CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints"); +- size_t nQuadPointCount = pArray ? QuadPointCount(pArray) : 0; ++ RetainPtr pArray = pAnnotDict->GetArrayFor("QuadPoints"); ++ size_t nQuadPointCount = pArray ? QuadPointCount(pArray.Get()) : 0; + if (nQuadPointCount == 0) + return ret; + +- ret = RectFromQuadPointsArray(pArray, 0); ++ ret = RectFromQuadPointsArray(pArray.Get(), 0); + for (size_t i = 1; i < nQuadPointCount; ++i) { +- CFX_FloatRect rect = RectFromQuadPointsArray(pArray, i); ++ CFX_FloatRect rect = RectFromQuadPointsArray(pArray.Get(), i); + ret.Union(rect); + } + return ret; +@@ -249,11 +273,11 @@ CFX_FloatRect CPDF_Annot::BoundingRectFromQuadPoints( + // static + CFX_FloatRect CPDF_Annot::RectFromQuadPoints(const CPDF_Dictionary* pAnnotDict, + size_t nIndex) { +- const CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints"); +- size_t nQuadPointCount = pArray ? QuadPointCount(pArray) : 0; ++ RetainPtr pArray = pAnnotDict->GetArrayFor("QuadPoints"); ++ size_t nQuadPointCount = pArray ? QuadPointCount(pArray.Get()) : 0; + if (nIndex >= nQuadPointCount) + return CFX_FloatRect(); +- return RectFromQuadPointsArray(pArray, nIndex); ++ return RectFromQuadPointsArray(pArray.Get(), nIndex); + } + + // static +@@ -313,6 +337,8 @@ CPDF_Annot::Subtype CPDF_Annot::StringToAnnotSubtype( + return CPDF_Annot::Subtype::RICHMEDIA; + if (sSubtype == "XFAWidget") + return CPDF_Annot::Subtype::XFAWIDGET; ++ if (sSubtype == "Redact") ++ return CPDF_Annot::Subtype::REDACT; + return CPDF_Annot::Subtype::UNKNOWN; + } + +@@ -372,6 +398,8 @@ ByteString CPDF_Annot::AnnotSubtypeToString(CPDF_Annot::Subtype nSubtype) { + return "RichMedia"; + if (nSubtype == CPDF_Annot::Subtype::XFAWIDGET) + return "XFAWidget"; ++ if (nSubtype == CPDF_Annot::Subtype::REDACT) ++ return "Redact"; + return ByteString(); + } + +@@ -383,57 +411,55 @@ size_t CPDF_Annot::QuadPointCount(const CPDF_Array* pArray) { + bool CPDF_Annot::DrawAppearance(CPDF_Page* pPage, + CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device, +- AppearanceMode mode, +- const CPDF_RenderOptions* pOptions) { ++ AppearanceMode mode) { + if (!ShouldDrawAnnotation()) + return false; + + // It might happen that by the time this annotation instance was created, +- // it was flagged as "hidden" (e.g. /F 2), and hence CPVT_GenerateAP decided ++ // it was flagged as "hidden" (e.g. /F 2), and hence CPDF_GenerateAP decided + // to not "generate" its AP. + // If for a reason the object is no longer hidden, but still does not have + // its "AP" generated, generate it now. + GenerateAPIfNeeded(); + + CFX_Matrix matrix; +- CPDF_Form* pForm = AnnotGetMatrix(pPage, this, mode, &mtUser2Device, &matrix); ++ CPDF_Form* pForm = AnnotGetMatrix(pPage, this, mode, mtUser2Device, &matrix); + if (!pForm) + return false; + +- CPDF_RenderContext context( +- pPage->GetDocument(), pPage->m_pPageResources.Get(), +- static_cast(pPage->GetRenderCache())); +- context.AppendLayer(pForm, &matrix); +- context.Render(pDevice, pOptions, nullptr); ++ CPDF_RenderContext context(pPage->GetDocument(), ++ pPage->GetMutablePageResources(), ++ pPage->GetPageImageCache()); ++ context.AppendLayer(pForm, matrix); ++ context.Render(pDevice, nullptr, nullptr, nullptr); + return true; + } + +-bool CPDF_Annot::DrawInContext(const CPDF_Page* pPage, ++bool CPDF_Annot::DrawInContext(CPDF_Page* pPage, + CPDF_RenderContext* pContext, +- const CFX_Matrix* pUser2Device, ++ const CFX_Matrix& mtUser2Device, + AppearanceMode mode) { + if (!ShouldDrawAnnotation()) + return false; + + // It might happen that by the time this annotation instance was created, +- // it was flagged as "hidden" (e.g. /F 2), and hence CPVT_GenerateAP decided ++ // it was flagged as "hidden" (e.g. /F 2), and hence CPDF_GenerateAP decided + // to not "generate" its AP. + // If for a reason the object is no longer hidden, but still does not have + // its "AP" generated, generate it now. + GenerateAPIfNeeded(); + + CFX_Matrix matrix; +- CPDF_Form* pForm = AnnotGetMatrix(pPage, this, mode, pUser2Device, &matrix); ++ CPDF_Form* pForm = AnnotGetMatrix(pPage, this, mode, mtUser2Device, &matrix); + if (!pForm) + return false; + +- pContext->AppendLayer(pForm, &matrix); ++ pContext->AppendLayer(pForm, matrix); + return true; + } + + void CPDF_Annot::DrawBorder(CFX_RenderDevice* pDevice, +- const CFX_Matrix* pUser2Device, +- const CPDF_RenderOptions* pOptions) { ++ const CFX_Matrix* pUser2Device) { + if (GetSubtype() == CPDF_Annot::Subtype::POPUP) + return; + +@@ -441,24 +467,23 @@ void CPDF_Annot::DrawBorder(CFX_RenderDevice* pDevice, + if (annot_flags & pdfium::annotation_flags::kHidden) + return; + +- bool bPrinting = pDevice->GetDeviceType() == DeviceType::kPrinter || +- (pOptions && pOptions->GetOptions().bPrintPreview); ++ bool bPrinting = pDevice->GetDeviceType() == DeviceType::kPrinter; + if (bPrinting && (annot_flags & pdfium::annotation_flags::kPrint) == 0) { + return; + } + if (!bPrinting && (annot_flags & pdfium::annotation_flags::kNoView)) { + return; + } +- CPDF_Dictionary* pBS = m_pAnnotDict->GetDictFor("BS"); ++ RetainPtr pBS = m_pAnnotDict->GetDictFor("BS"); + char style_char; + float width; +- CPDF_Array* pDashArray = nullptr; ++ RetainPtr pDashArray; + if (!pBS) { +- CPDF_Array* pBorderArray = ++ RetainPtr pBorderArray = + m_pAnnotDict->GetArrayFor(pdfium::annotation::kBorder); + style_char = 'S'; + if (pBorderArray) { +- width = pBorderArray->GetNumberAt(2); ++ width = pBorderArray->GetFloatAt(2); + if (pBorderArray->size() == 4) { + pDashArray = pBorderArray->GetArrayAt(3); + if (!pDashArray) { +@@ -467,7 +492,7 @@ void CPDF_Annot::DrawBorder(CFX_RenderDevice* pDevice, + size_t nLen = pDashArray->size(); + size_t i = 0; + for (; i < nLen; ++i) { +- CPDF_Object* pObj = pDashArray->GetDirectObjectAt(i); ++ RetainPtr pObj = pDashArray->GetDirectObjectAt(i); + if (pObj && pObj->GetInteger()) { + break; + } +@@ -481,28 +506,35 @@ void CPDF_Annot::DrawBorder(CFX_RenderDevice* pDevice, + width = 1; + } + } else { +- ByteString style = pBS->GetStringFor("S"); ++ ByteString style = pBS->GetByteStringFor("S"); + pDashArray = pBS->GetArrayFor("D"); +- style_char = style[1]; +- width = pBS->GetNumberFor("W"); ++ style_char = style[0]; ++ width = pBS->GetFloatFor("W"); + } + if (width <= 0) { + return; + } +- CPDF_Array* pColor = m_pAnnotDict->GetArrayFor(pdfium::annotation::kC); ++ RetainPtr pColor = ++ m_pAnnotDict->GetArrayFor(pdfium::annotation::kC); + uint32_t argb = 0xff000000; + if (pColor) { +- int R = (int32_t)(pColor->GetNumberAt(0) * 255); +- int G = (int32_t)(pColor->GetNumberAt(1) * 255); +- int B = (int32_t)(pColor->GetNumberAt(2) * 255); ++ int R = static_cast(pColor->GetFloatAt(0) * 255); ++ int G = static_cast(pColor->GetFloatAt(1) * 255); ++ int B = static_cast(pColor->GetFloatAt(2) * 255); + argb = ArgbEncode(0xff, R, G, B); + } + CFX_GraphStateData graph_state; + graph_state.m_LineWidth = width; ++ if (style_char == 'U') { ++ // TODO(https://crbug.com/237527): Handle the "Underline" border style ++ // instead of drawing the rectangle border. ++ return; ++ } ++ + if (style_char == 'D') { + if (pDashArray) { + graph_state.m_DashArray = +- ReadArrayElementsToVector(pDashArray, pDashArray->size()); ++ ReadArrayElementsToVector(pDashArray.Get(), pDashArray->size()); + if (graph_state.m_DashArray.size() % 2) + graph_state.m_DashArray.push_back(graph_state.m_DashArray.back()); + } else { +@@ -512,12 +544,9 @@ void CPDF_Annot::DrawBorder(CFX_RenderDevice* pDevice, + + CFX_FloatRect rect = GetRect(); + rect.Deflate(width / 2, width / 2); +- CFX_PathData path; +- path.AppendFloatRect(rect); + +- int fill_type = 0; +- if (pOptions && pOptions->GetOptions().bNoPathSmooth) +- fill_type |= FXFILL_NOPATHSMOOTH; +- +- pDevice->DrawPath(&path, pUser2Device, &graph_state, argb, argb, fill_type); ++ CFX_Path path; ++ path.AppendFloatRect(rect); ++ pDevice->DrawPath(path, pUser2Device, &graph_state, argb, argb, ++ CFX_FillRenderOptions()); + } +diff --git a/core/fpdfdoc/cpdf_annot.h b/core/fpdfdoc/cpdf_annot.h +index 64ab69db8..4f8b2f802 100644 +--- a/core/fpdfdoc/cpdf_annot.h ++++ b/core/fpdfdoc/cpdf_annot.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,13 +7,19 @@ + #ifndef CORE_FPDFDOC_CPDF_ANNOT_H_ + #define CORE_FPDFDOC_CPDF_ANNOT_H_ + ++#include ++#include ++ + #include + #include + ++#include "core/fpdfapi/parser/cpdf_stream.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/maybe_owned.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxcrt/unowned_ptr.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CFX_RenderDevice; + class CPDF_Array; +@@ -22,13 +28,11 @@ class CPDF_Document; + class CPDF_Form; + class CPDF_Page; + class CPDF_RenderContext; +-class CPDF_RenderOptions; +-class CPDF_Stream; + + class CPDF_Annot { + public: +- enum AppearanceMode { Normal, Rollover, Down }; +- enum class Subtype { ++ enum class AppearanceMode { kNormal, kRollover, kDown }; ++ enum class Subtype : uint8_t { + UNKNOWN = 0, + TEXT, + LINK, +@@ -56,11 +60,12 @@ class CPDF_Annot { + WATERMARK, + THREED, + RICHMEDIA, +- XFAWIDGET ++ XFAWIDGET, ++ REDACT + }; + +- static CPDF_Annot::Subtype StringToAnnotSubtype(const ByteString& sSubtype); +- static ByteString AnnotSubtypeToString(CPDF_Annot::Subtype nSubtype); ++ static Subtype StringToAnnotSubtype(const ByteString& sSubtype); ++ static ByteString AnnotSubtypeToString(Subtype nSubtype); + static CFX_FloatRect RectFromQuadPointsArray(const CPDF_Array* pArray, + size_t nIndex); + static CFX_FloatRect BoundingRectFromQuadPoints( +@@ -69,41 +74,35 @@ class CPDF_Annot { + size_t nIndex); + static size_t QuadPointCount(const CPDF_Array* pArray); + +- // The second constructor does not take ownership of the dictionary. + CPDF_Annot(RetainPtr pDict, CPDF_Document* pDocument); +- CPDF_Annot(CPDF_Dictionary* pDict, CPDF_Document* pDocument); + ~CPDF_Annot(); + +- CPDF_Annot::Subtype GetSubtype() const; ++ Subtype GetSubtype() const; + uint32_t GetFlags() const; + CFX_FloatRect GetRect() const; +- CPDF_Document* GetDocument() const { return m_pDocument.Get(); } + const CPDF_Dictionary* GetAnnotDict() const { return m_pAnnotDict.Get(); } +- CPDF_Dictionary* GetAnnotDict() { return m_pAnnotDict.Get(); } ++ RetainPtr GetMutableAnnotDict() { return m_pAnnotDict; } + + bool IsHidden() const; + + bool DrawAppearance(CPDF_Page* pPage, + CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device, +- AppearanceMode mode, +- const CPDF_RenderOptions* pOptions); +- bool DrawInContext(const CPDF_Page* pPage, ++ AppearanceMode mode); ++ bool DrawInContext(CPDF_Page* pPage, + CPDF_RenderContext* pContext, +- const CFX_Matrix* pUser2Device, ++ const CFX_Matrix& mtUser2Device, + AppearanceMode mode); + + void ClearCachedAP(); +- void DrawBorder(CFX_RenderDevice* pDevice, +- const CFX_Matrix* pUser2Device, +- const CPDF_RenderOptions* pOptions); +- CPDF_Form* GetAPForm(const CPDF_Page* pPage, AppearanceMode mode); ++ void DrawBorder(CFX_RenderDevice* pDevice, const CFX_Matrix* pUser2Device); ++ CPDF_Form* GetAPForm(CPDF_Page* pPage, AppearanceMode mode); + void SetOpenState(bool bOpenState) { m_bOpenState = bOpenState; } +- CPDF_Annot* GetPopupAnnot() const { return m_pPopupAnnot.Get(); } ++ void SetPopupAnnotOpenState(bool bOpenState); ++ absl::optional GetPopupAnnotRect() const; + void SetPopupAnnot(CPDF_Annot* pAnnot) { m_pPopupAnnot = pAnnot; } + + private: +- void Init(); + void GenerateAPIfNeeded(); + bool ShouldGenerateAP() const; + bool ShouldDrawAnnotation() const; +@@ -112,25 +111,25 @@ class CPDF_Annot { + + RetainPtr const m_pAnnotDict; + UnownedPtr const m_pDocument; +- CPDF_Annot::Subtype m_nSubtype; +- std::map> m_APMap; ++ std::map, std::unique_ptr> m_APMap; + // If non-null, then this is not a popup annotation. + UnownedPtr m_pPopupAnnot; ++ const Subtype m_nSubtype; ++ const bool m_bIsTextMarkupAnnotation; + // |m_bOpenState| is only set for popup annotations. + bool m_bOpenState = false; + bool m_bHasGeneratedAP; +- bool m_bIsTextMarkupAnnotation; + }; + + // Get the AP in an annotation dict for a given appearance mode. + // If |eMode| is not Normal and there is not AP for that mode, falls back to + // the Normal AP. +-CPDF_Stream* GetAnnotAP(CPDF_Dictionary* pAnnotDict, +- CPDF_Annot::AppearanceMode eMode); ++RetainPtr GetAnnotAP(CPDF_Dictionary* pAnnotDict, ++ CPDF_Annot::AppearanceMode eMode); + + // Get the AP in an annotation dict for a given appearance mode. + // No fallbacks to Normal like in GetAnnotAP. +-CPDF_Stream* GetAnnotAPNoFallback(CPDF_Dictionary* pAnnotDict, +- CPDF_Annot::AppearanceMode eMode); ++RetainPtr GetAnnotAPNoFallback(CPDF_Dictionary* pAnnotDict, ++ CPDF_Annot::AppearanceMode eMode); + + #endif // CORE_FPDFDOC_CPDF_ANNOT_H_ +diff --git a/core/fpdfdoc/cpdf_annot_unittest.cpp b/core/fpdfdoc/cpdf_annot_unittest.cpp +index a7625e9db..a98871997 100644 +--- a/core/fpdfdoc/cpdf_annot_unittest.cpp ++++ b/core/fpdfdoc/cpdf_annot_unittest.cpp +@@ -1,10 +1,9 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfdoc/cpdf_annot.h" + +-#include + #include + + #include "core/fpdfapi/parser/cpdf_array.h" +@@ -18,7 +17,7 @@ RetainPtr CreateQuadPointArrayFromVector( + const std::vector& points) { + auto array = pdfium::MakeRetain(); + for (float point : points) +- array->AddNew(point); ++ array->AppendNew(point); + return array; + } + +@@ -124,14 +123,14 @@ TEST(CPDFAnnotTest, QuadPointCount) { + EXPECT_EQ(0u, CPDF_Annot::QuadPointCount(array.Get())); + + for (int i = 0; i < 7; ++i) { +- array->AddNew(0); ++ array->AppendNew(0); + EXPECT_EQ(0u, CPDF_Annot::QuadPointCount(array.Get())); + } + for (int i = 0; i < 8; ++i) { +- array->AddNew(0); ++ array->AppendNew(0); + EXPECT_EQ(1u, CPDF_Annot::QuadPointCount(array.Get())); + } + for (int i = 0; i < 50; ++i) +- array->AddNew(0); ++ array->AppendNew(0); + EXPECT_EQ(8u, CPDF_Annot::QuadPointCount(array.Get())); + } +diff --git a/core/fpdfdoc/cpdf_annotlist.cpp b/core/fpdfdoc/cpdf_annotlist.cpp +index 37208e3cc..f565a5a70 100644 +--- a/core/fpdfdoc/cpdf_annotlist.cpp ++++ b/core/fpdfdoc/cpdf_annotlist.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -26,10 +26,9 @@ + #include "core/fpdfapi/render/cpdf_renderoptions.h" + #include "core/fpdfdoc/cpdf_annot.h" + #include "core/fpdfdoc/cpdf_formfield.h" ++#include "core/fpdfdoc/cpdf_generateap.h" + #include "core/fpdfdoc/cpdf_interactiveform.h" +-#include "core/fpdfdoc/cpvt_generateap.h" + #include "core/fxge/cfx_renderdevice.h" +-#include "third_party/base/ptr_util.h" + + namespace { + +@@ -49,6 +48,7 @@ bool PopupAppearsForAnnotType(CPDF_Annot::Subtype subtype) { + case CPDF_Annot::Subtype::CARET: + case CPDF_Annot::Subtype::INK: + case CPDF_Annot::Subtype::FILEATTACHMENT: ++ case CPDF_Annot::Subtype::REDACT: + return true; + case CPDF_Annot::Subtype::UNKNOWN: + case CPDF_Annot::Subtype::LINK: +@@ -91,7 +91,7 @@ std::unique_ptr CreatePopupAnnot(CPDF_Document* pDocument, + pAnnotDict->SetNewFor(pdfium::annotation::kSubtype, "Popup"); + pAnnotDict->SetNewFor( + pdfium::form_fields::kT, +- pParentDict->GetStringFor(pdfium::form_fields::kT), false); ++ pParentDict->GetByteStringFor(pdfium::form_fields::kT), false); + pAnnotDict->SetNewFor(pdfium::annotation::kContents, + sContents.ToUTF8(), false); + +@@ -117,37 +117,37 @@ std::unique_ptr CreatePopupAnnot(CPDF_Document* pDocument, + pAnnotDict->SetNewFor(pdfium::annotation::kF, 0); + + auto pPopupAnnot = +- pdfium::MakeUnique(std::move(pAnnotDict), pDocument); ++ std::make_unique(std::move(pAnnotDict), pDocument); + pAnnot->SetPopupAnnot(pPopupAnnot.get()); + return pPopupAnnot; + } + + void GenerateAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { + if (!pAnnotDict || +- pAnnotDict->GetStringFor(pdfium::annotation::kSubtype) != "Widget") { ++ pAnnotDict->GetByteStringFor(pdfium::annotation::kSubtype) != "Widget") { + return; + } + +- CPDF_Object* pFieldTypeObj = +- CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kFT); ++ RetainPtr pFieldTypeObj = ++ CPDF_FormField::GetFieldAttrForDict(pAnnotDict, pdfium::form_fields::kFT); + if (!pFieldTypeObj) + return; + + ByteString field_type = pFieldTypeObj->GetString(); + if (field_type == pdfium::form_fields::kTx) { +- CPVT_GenerateAP::GenerateFormAP(pDoc, pAnnotDict, +- CPVT_GenerateAP::kTextField); ++ CPDF_GenerateAP::GenerateFormAP(pDoc, pAnnotDict, ++ CPDF_GenerateAP::kTextField); + return; + } + +- CPDF_Object* pFieldFlagsObj = +- CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kFf); ++ RetainPtr pFieldFlagsObj = ++ CPDF_FormField::GetFieldAttrForDict(pAnnotDict, pdfium::form_fields::kFf); + uint32_t flags = pFieldFlagsObj ? pFieldFlagsObj->GetInteger() : 0; + if (field_type == pdfium::form_fields::kCh) { + auto type = (flags & pdfium::form_flags::kChoiceCombo) +- ? CPVT_GenerateAP::kComboBox +- : CPVT_GenerateAP::kListBox; +- CPVT_GenerateAP::GenerateFormAP(pDoc, pAnnotDict, type); ++ ? CPDF_GenerateAP::kComboBox ++ : CPDF_GenerateAP::kListBox; ++ CPDF_GenerateAP::GenerateFormAP(pDoc, pAnnotDict, type); + return; + } + +@@ -158,53 +158,53 @@ void GenerateAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { + if (pAnnotDict->KeyExist(pdfium::annotation::kAS)) + return; + +- CPDF_Dictionary* pParentDict = ++ RetainPtr pParentDict = + pAnnotDict->GetDictFor(pdfium::form_fields::kParent); + if (!pParentDict || !pParentDict->KeyExist(pdfium::annotation::kAS)) + return; + + pAnnotDict->SetNewFor( + pdfium::annotation::kAS, +- pParentDict->GetStringFor(pdfium::annotation::kAS), false); ++ pParentDict->GetByteStringFor(pdfium::annotation::kAS), false); + } + + } // namespace + + CPDF_AnnotList::CPDF_AnnotList(CPDF_Page* pPage) + : m_pDocument(pPage->GetDocument()) { +- CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots"); ++ RetainPtr pAnnots = pPage->GetMutableAnnotsArray(); + if (!pAnnots) + return; + + const CPDF_Dictionary* pRoot = m_pDocument->GetRoot(); +- const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm"); ++ RetainPtr pAcroForm = pRoot->GetDictFor("AcroForm"); + bool bRegenerateAP = + pAcroForm && pAcroForm->GetBooleanFor("NeedAppearances", false); + for (size_t i = 0; i < pAnnots->size(); ++i) { +- CPDF_Dictionary* pDict = ToDictionary(pAnnots->GetDirectObjectAt(i)); ++ RetainPtr pDict = ++ ToDictionary(pAnnots->GetMutableDirectObjectAt(i)); + if (!pDict) + continue; + const ByteString subtype = +- pDict->GetStringFor(pdfium::annotation::kSubtype); ++ pDict->GetByteStringFor(pdfium::annotation::kSubtype); + if (subtype == "Popup") { + // Skip creating Popup annotations in the PDF document since PDFium + // provides its own Popup annotations. + continue; + } +- pAnnots->ConvertToIndirectObjectAt(i, m_pDocument.Get()); +- m_AnnotList.push_back( +- pdfium::MakeUnique(pDict, m_pDocument.Get())); ++ pAnnots->ConvertToIndirectObjectAt(i, m_pDocument); ++ m_AnnotList.push_back(std::make_unique(pDict, m_pDocument)); + if (bRegenerateAP && subtype == "Widget" && + CPDF_InteractiveForm::IsUpdateAPEnabled() && + !pDict->GetDictFor(pdfium::annotation::kAP)) { +- GenerateAP(m_pDocument.Get(), pDict); ++ GenerateAP(m_pDocument, pDict.Get()); + } + } + + m_nAnnotCount = m_AnnotList.size(); + for (size_t i = 0; i < m_nAnnotCount; ++i) { + std::unique_ptr pPopupAnnot = +- CreatePopupAnnot(m_pDocument.Get(), pPage, m_AnnotList[i].get()); ++ CreatePopupAnnot(m_pDocument, pPage, m_AnnotList[i].get()); + if (pPopupAnnot) + m_AnnotList.push_back(std::move(pPopupAnnot)); + } +@@ -221,14 +221,20 @@ CPDF_AnnotList::~CPDF_AnnotList() { + m_AnnotList.clear(); + } + ++bool CPDF_AnnotList::Contains(const CPDF_Annot* pAnnot) const { ++ auto it = std::find_if(m_AnnotList.begin(), m_AnnotList.end(), ++ [pAnnot](const std::unique_ptr& annot) { ++ return annot.get() == pAnnot; ++ }); ++ return it != m_AnnotList.end(); ++} ++ + void CPDF_AnnotList::DisplayPass(CPDF_Page* pPage, + CFX_RenderDevice* pDevice, + CPDF_RenderContext* pContext, + bool bPrinting, +- const CFX_Matrix* pMatrix, +- bool bWidgetPass, +- CPDF_RenderOptions* pOptions, +- FX_RECT* clip_rect) { ++ const CFX_Matrix& mtMatrix, ++ bool bWidgetPass) { + for (const auto& pAnnot : m_AnnotList) { + bool bWidget = pAnnot->GetSubtype() == CPDF_Annot::Subtype::WIDGET; + if ((bWidgetPass && !bWidget) || (!bWidgetPass && bWidget)) +@@ -244,29 +250,12 @@ void CPDF_AnnotList::DisplayPass(CPDF_Page* pPage, + if (!bPrinting && (annot_flags & pdfium::annotation_flags::kNoView)) + continue; + +- if (pOptions) { +- const CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict(); +- const CPDF_OCContext* pOCContext = pOptions->GetOCContext(); +- if (pAnnotDict && pOCContext && +- !pOCContext->CheckOCGVisible( +- pAnnotDict->GetDictFor(pdfium::annotation::kOC))) { +- continue; +- } +- } +- +- CFX_Matrix matrix = *pMatrix; +- if (clip_rect) { +- FX_RECT annot_rect = +- matrix.TransformRect(pAnnot->GetRect()).GetOuterRect(); +- annot_rect.Intersect(*clip_rect); +- if (annot_rect.IsEmpty()) +- continue; +- } + if (pContext) { +- pAnnot->DrawInContext(pPage, pContext, &matrix, CPDF_Annot::Normal); +- } else if (!pAnnot->DrawAppearance(pPage, pDevice, matrix, +- CPDF_Annot::Normal, pOptions)) { +- pAnnot->DrawBorder(pDevice, &matrix, pOptions); ++ pAnnot->DrawInContext(pPage, pContext, mtMatrix, ++ CPDF_Annot::AppearanceMode::kNormal); ++ } else if (!pAnnot->DrawAppearance(pPage, pDevice, mtMatrix, ++ CPDF_Annot::AppearanceMode::kNormal)) { ++ pAnnot->DrawBorder(pDevice, &mtMatrix); + } + } + } +@@ -275,29 +264,9 @@ void CPDF_AnnotList::DisplayAnnots(CPDF_Page* pPage, + CFX_RenderDevice* pDevice, + CPDF_RenderContext* pContext, + bool bPrinting, +- const CFX_Matrix* pUser2Device, +- uint32_t dwAnnotFlags, +- CPDF_RenderOptions* pOptions, +- FX_RECT* pClipRect) { +- if (dwAnnotFlags & pdfium::annotation_flags::kInvisible) { +- DisplayPass(pPage, pDevice, pContext, bPrinting, pUser2Device, false, +- pOptions, pClipRect); +- } +- if (dwAnnotFlags & pdfium::annotation_flags::kHidden) { +- DisplayPass(pPage, pDevice, pContext, bPrinting, pUser2Device, true, +- pOptions, pClipRect); +- } +-} +- +-void CPDF_AnnotList::DisplayAnnots(CPDF_Page* pPage, +- CPDF_RenderContext* pContext, +- bool bPrinting, +- const CFX_Matrix* pMatrix, +- bool bShowWidget, +- CPDF_RenderOptions* pOptions) { +- uint32_t dwAnnotFlags = bShowWidget ? pdfium::annotation_flags::kInvisible | +- pdfium::annotation_flags::kHidden +- : pdfium::annotation_flags::kInvisible; +- DisplayAnnots(pPage, nullptr, pContext, bPrinting, pMatrix, dwAnnotFlags, +- pOptions, nullptr); ++ const CFX_Matrix& mtUser2Device, ++ bool bShowWidget) { ++ DisplayPass(pPage, pDevice, pContext, bPrinting, mtUser2Device, false); ++ if (bShowWidget) ++ DisplayPass(pPage, pDevice, pContext, bPrinting, mtUser2Device, true); + } +diff --git a/core/fpdfdoc/cpdf_annotlist.h b/core/fpdfdoc/cpdf_annotlist.h +index 85075cf5a..ff98c8ab3 100644 +--- a/core/fpdfdoc/cpdf_annotlist.h ++++ b/core/fpdfdoc/cpdf_annotlist.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,14 @@ + #ifndef CORE_FPDFDOC_CPDF_ANNOTLIST_H_ + #define CORE_FPDFDOC_CPDF_ANNOTLIST_H_ + ++#include ++#include ++ + #include + #include + + #include "core/fpdfapi/render/cpdf_pagerendercontext.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/unowned_ptr.h" + + class CFX_RenderDevice; +@@ -20,44 +22,30 @@ class CPDF_Annot; + class CPDF_Document; + class CPDF_Page; + class CPDF_RenderContext; +-class CPDF_RenderOptions; + +-class CPDF_AnnotList : public CPDF_PageRenderContext::AnnotListIface { ++class CPDF_AnnotList final : public CPDF_PageRenderContext::AnnotListIface { + public: + explicit CPDF_AnnotList(CPDF_Page* pPage); + ~CPDF_AnnotList() override; + +- void DisplayAnnots(CPDF_Page* pPage, +- CPDF_RenderContext* pContext, +- bool bPrinting, +- const CFX_Matrix* pMatrix, +- bool bShowWidget, +- CPDF_RenderOptions* pOptions); +- + void DisplayAnnots(CPDF_Page* pPage, + CFX_RenderDevice* pDevice, + CPDF_RenderContext* pContext, + bool bPrinting, +- const CFX_Matrix* pUser2Device, +- uint32_t dwAnnotFlags, +- CPDF_RenderOptions* pOptions, +- FX_RECT* pClipRect); ++ const CFX_Matrix& mtUser2Device, ++ bool bShowWidget); + + size_t Count() const { return m_AnnotList.size(); } + CPDF_Annot* GetAt(size_t index) const { return m_AnnotList[index].get(); } +- const std::vector>& All() const { +- return m_AnnotList; +- } ++ bool Contains(const CPDF_Annot* pAnnot) const; + + private: + void DisplayPass(CPDF_Page* pPage, + CFX_RenderDevice* pDevice, + CPDF_RenderContext* pContext, + bool bPrinting, +- const CFX_Matrix* pMatrix, +- bool bWidget, +- CPDF_RenderOptions* pOptions, +- FX_RECT* clip_rect); ++ const CFX_Matrix& mtMatrix, ++ bool bWidget); + + UnownedPtr const m_pDocument; + +diff --git a/core/fpdfdoc/cpdf_apsettings.cpp b/core/fpdfdoc/cpdf_apsettings.cpp +index 341764ffa..e7cd3059d 100644 +--- a/core/fpdfdoc/cpdf_apsettings.cpp ++++ b/core/fpdfdoc/cpdf_apsettings.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,17 +7,19 @@ + #include "core/fpdfdoc/cpdf_apsettings.h" + + #include ++#include + + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfdoc/cpdf_formcontrol.h" +-#include "core/fxge/cfx_color.h" + +-CPDF_ApSettings::CPDF_ApSettings(CPDF_Dictionary* pDict) : m_pDict(pDict) {} ++CPDF_ApSettings::CPDF_ApSettings(RetainPtr pDict) ++ : m_pDict(std::move(pDict)) {} + + CPDF_ApSettings::CPDF_ApSettings(const CPDF_ApSettings& that) = default; + +-CPDF_ApSettings::~CPDF_ApSettings() {} ++CPDF_ApSettings::~CPDF_ApSettings() = default; + + bool CPDF_ApSettings::HasMKEntry(const ByteString& csEntry) const { + return m_pDict && m_pDict->KeyExist(csEntry); +@@ -27,91 +29,80 @@ int CPDF_ApSettings::GetRotation() const { + return m_pDict ? m_pDict->GetIntegerFor("R") : 0; + } + +-FX_ARGB CPDF_ApSettings::GetColor(int& iColorType, +- const ByteString& csEntry) const { +- iColorType = CFX_Color::kTransparent; ++CFX_Color::TypeAndARGB CPDF_ApSettings::GetColorARGB( ++ const ByteString& csEntry) const { + if (!m_pDict) +- return 0; ++ return {CFX_Color::Type::kTransparent, 0}; + +- CPDF_Array* pEntry = m_pDict->GetArrayFor(csEntry); ++ RetainPtr pEntry = m_pDict->GetArrayFor(csEntry); + if (!pEntry) +- return 0; ++ return {CFX_Color::Type::kTransparent, 0}; + +- FX_ARGB color = 0; +- size_t dwCount = pEntry->size(); ++ const size_t dwCount = pEntry->size(); + if (dwCount == 1) { +- iColorType = CFX_Color::kGray; +- float g = pEntry->GetNumberAt(0) * 255; +- return ArgbEncode(255, (int)g, (int)g, (int)g); ++ const float g = pEntry->GetFloatAt(0) * 255; ++ return {CFX_Color::Type::kGray, ArgbEncode(255, (int)g, (int)g, (int)g)}; + } + if (dwCount == 3) { +- iColorType = CFX_Color::kRGB; +- float r = pEntry->GetNumberAt(0) * 255; +- float g = pEntry->GetNumberAt(1) * 255; +- float b = pEntry->GetNumberAt(2) * 255; +- return ArgbEncode(255, (int)r, (int)g, (int)b); ++ float r = pEntry->GetFloatAt(0) * 255; ++ float g = pEntry->GetFloatAt(1) * 255; ++ float b = pEntry->GetFloatAt(2) * 255; ++ return {CFX_Color::Type::kRGB, ArgbEncode(255, (int)r, (int)g, (int)b)}; + } + if (dwCount == 4) { +- iColorType = CFX_Color::kCMYK; +- float c = pEntry->GetNumberAt(0); +- float m = pEntry->GetNumberAt(1); +- float y = pEntry->GetNumberAt(2); +- float k = pEntry->GetNumberAt(3); +- float r = 1.0f - std::min(1.0f, c + k); +- float g = 1.0f - std::min(1.0f, m + k); +- float b = 1.0f - std::min(1.0f, y + k); +- return ArgbEncode(255, (int)(r * 255), (int)(g * 255), (int)(b * 255)); ++ float c = pEntry->GetFloatAt(0); ++ float m = pEntry->GetFloatAt(1); ++ float y = pEntry->GetFloatAt(2); ++ float k = pEntry->GetFloatAt(3); ++ float r = (1.0f - std::min(1.0f, c + k)) * 255; ++ float g = (1.0f - std::min(1.0f, m + k)) * 255; ++ float b = (1.0f - std::min(1.0f, y + k)) * 255; ++ return {CFX_Color::Type::kCMYK, ArgbEncode(255, (int)r, (int)g, (int)b)}; + } +- return color; ++ return {CFX_Color::Type::kTransparent, 0}; + } + +-float CPDF_ApSettings::GetOriginalColor(int index, +- const ByteString& csEntry) const { ++float CPDF_ApSettings::GetOriginalColorComponent( ++ int index, ++ const ByteString& csEntry) const { + if (!m_pDict) + return 0; + +- CPDF_Array* pEntry = m_pDict->GetArrayFor(csEntry); +- return pEntry ? pEntry->GetNumberAt(index) : 0; ++ RetainPtr pEntry = m_pDict->GetArrayFor(csEntry); ++ return pEntry ? pEntry->GetFloatAt(index) : 0; + } + +-void CPDF_ApSettings::GetOriginalColor(int& iColorType, +- float fc[4], +- const ByteString& csEntry) const { +- iColorType = CFX_Color::kTransparent; +- for (int i = 0; i < 4; i++) +- fc[i] = 0; +- ++CFX_Color CPDF_ApSettings::GetOriginalColor(const ByteString& csEntry) const { + if (!m_pDict) +- return; ++ return CFX_Color(); + +- CPDF_Array* pEntry = m_pDict->GetArrayFor(csEntry); ++ RetainPtr pEntry = m_pDict->GetArrayFor(csEntry); + if (!pEntry) +- return; ++ return CFX_Color(); + + size_t dwCount = pEntry->size(); + if (dwCount == 1) { +- iColorType = CFX_Color::kGray; +- fc[0] = pEntry->GetNumberAt(0); +- } else if (dwCount == 3) { +- iColorType = CFX_Color::kRGB; +- fc[0] = pEntry->GetNumberAt(0); +- fc[1] = pEntry->GetNumberAt(1); +- fc[2] = pEntry->GetNumberAt(2); +- } else if (dwCount == 4) { +- iColorType = CFX_Color::kCMYK; +- fc[0] = pEntry->GetNumberAt(0); +- fc[1] = pEntry->GetNumberAt(1); +- fc[2] = pEntry->GetNumberAt(2); +- fc[3] = pEntry->GetNumberAt(3); ++ return CFX_Color(CFX_Color::Type::kGray, pEntry->GetFloatAt(0)); ++ } ++ if (dwCount == 3) { ++ return CFX_Color(CFX_Color::Type::kRGB, pEntry->GetFloatAt(0), ++ pEntry->GetFloatAt(1), pEntry->GetFloatAt(2)); ++ } ++ if (dwCount == 4) { ++ return CFX_Color(CFX_Color::Type::kCMYK, pEntry->GetFloatAt(0), ++ pEntry->GetFloatAt(1), pEntry->GetFloatAt(2), ++ pEntry->GetFloatAt(3)); + } ++ return CFX_Color(); + } + + WideString CPDF_ApSettings::GetCaption(const ByteString& csEntry) const { + return m_pDict ? m_pDict->GetUnicodeTextFor(csEntry) : WideString(); + } + +-CPDF_Stream* CPDF_ApSettings::GetIcon(const ByteString& csEntry) const { +- return m_pDict ? m_pDict->GetStreamFor(csEntry) : nullptr; ++RetainPtr CPDF_ApSettings::GetIcon( ++ const ByteString& csEntry) const { ++ return m_pDict ? m_pDict->GetMutableStreamFor(csEntry) : nullptr; + } + + CPDF_IconFit CPDF_ApSettings::GetIconFit() const { +diff --git a/core/fpdfdoc/cpdf_apsettings.h b/core/fpdfdoc/cpdf_apsettings.h +index 2a163364b..31e8e6043 100644 +--- a/core/fpdfdoc/cpdf_apsettings.h ++++ b/core/fpdfdoc/cpdf_apsettings.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,12 +9,11 @@ + + #include "core/fpdfdoc/cpdf_iconfit.h" + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/cfx_color.h" ++#include "core/fxge/dib/fx_dib.h" + + class CPDF_Dictionary; +-class CPDF_FormControl; + class CPDF_Stream; + + // Corresponds to PDF spec section 12.5.6.19 (Widget annotation TP dictionary). +@@ -28,56 +27,25 @@ class CPDF_Stream; + + class CPDF_ApSettings { + public: +- explicit CPDF_ApSettings(CPDF_Dictionary* pDict); ++ explicit CPDF_ApSettings(RetainPtr pDict); + CPDF_ApSettings(const CPDF_ApSettings& that); + ~CPDF_ApSettings(); + + bool HasMKEntry(const ByteString& csEntry) const; + int GetRotation() const; + +- FX_ARGB GetBorderColor(int& iColorType) const { +- return GetColor(iColorType, "BC"); +- } +- +- float GetOriginalBorderColor(int index) const { +- return GetOriginalColor(index, "BC"); +- } +- +- void GetOriginalBorderColor(int& iColorType, float fc[4]) const { +- GetOriginalColor(iColorType, fc, "BC"); +- } +- +- FX_ARGB GetBackgroundColor(int& iColorType) const { +- return GetColor(iColorType, "BG"); +- } +- +- float GetOriginalBackgroundColor(int index) const { +- return GetOriginalColor(index, "BG"); +- } +- +- void GetOriginalBackgroundColor(int& iColorType, float fc[4]) const { +- GetOriginalColor(iColorType, fc, "BG"); +- } +- +- WideString GetNormalCaption() const { return GetCaption("CA"); } +- WideString GetRolloverCaption() const { return GetCaption("RC"); } +- WideString GetDownCaption() const { return GetCaption("AC"); } +- CPDF_Stream* GetNormalIcon() const { return GetIcon("I"); } +- CPDF_Stream* GetRolloverIcon() const { return GetIcon("RI"); } +- CPDF_Stream* GetDownIcon() const { return GetIcon("IX"); } + CPDF_IconFit GetIconFit() const; + + // Returns one of the TEXTPOS_* values above. + int GetTextPosition() const; + +- FX_ARGB GetColor(int& iColorType, const ByteString& csEntry) const; +- float GetOriginalColor(int index, const ByteString& csEntry) const; +- void GetOriginalColor(int& iColorType, +- float fc[4], +- const ByteString& csEntry) const; ++ CFX_Color::TypeAndARGB GetColorARGB(const ByteString& csEntry) const; ++ ++ float GetOriginalColorComponent(int index, const ByteString& csEntry) const; ++ CFX_Color GetOriginalColor(const ByteString& csEntry) const; + + WideString GetCaption(const ByteString& csEntry) const; +- CPDF_Stream* GetIcon(const ByteString& csEntry) const; ++ RetainPtr GetIcon(const ByteString& csEntry) const; + + private: + RetainPtr const m_pDict; +diff --git a/core/fpdfdoc/cpdf_bafontmap.cpp b/core/fpdfdoc/cpdf_bafontmap.cpp +new file mode 100644 +index 000000000..c8617bbed +--- /dev/null ++++ b/core/fpdfdoc/cpdf_bafontmap.cpp +@@ -0,0 +1,432 @@ ++// Copyright 2014 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fpdfdoc/cpdf_bafontmap.h" ++ ++#include ++#include ++ ++#include "constants/annotation_common.h" ++#include "core/fpdfapi/font/cpdf_font.h" ++#include "core/fpdfapi/font/cpdf_fontencoding.h" ++#include "core/fpdfapi/page/cpdf_docpagedata.h" ++#include "core/fpdfapi/page/cpdf_page.h" ++#include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fpdfapi/parser/cpdf_parser.h" ++#include "core/fpdfapi/parser/cpdf_reference.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" ++#include "core/fpdfapi/parser/fpdf_parser_utility.h" ++#include "core/fpdfdoc/cpdf_defaultappearance.h" ++#include "core/fpdfdoc/cpdf_formfield.h" ++#include "core/fpdfdoc/ipvt_fontmap.h" ++#include "core/fxcrt/fx_codepage.h" ++#include "core/fxcrt/stl_util.h" ++#include "core/fxge/cfx_fontmapper.h" ++#include "core/fxge/cfx_fontmgr.h" ++#include "core/fxge/cfx_gemodule.h" ++ ++namespace { ++ ++bool FindNativeTrueTypeFont(ByteStringView sFontFaceName) { ++ CFX_FontMgr* pFontMgr = CFX_GEModule::Get()->GetFontMgr(); ++ CFX_FontMapper* pFontMapper = pFontMgr->GetBuiltinMapper(); ++ pFontMapper->LoadInstalledFonts(); ++ return pFontMapper->HasInstalledFont(sFontFaceName) || ++ pFontMapper->HasLocalizedFont(sFontFaceName); ++} ++ ++RetainPtr AddNativeTrueTypeFontToPDF(CPDF_Document* pDoc, ++ ByteString sFontFaceName, ++ FX_Charset nCharset) { ++ if (!pDoc) ++ return nullptr; ++ ++ auto pFXFont = std::make_unique(); ++ pFXFont->LoadSubst(sFontFaceName, true, 0, 0, 0, ++ FX_GetCodePageFromCharset(nCharset), false); ++ ++ auto* pDocPageData = CPDF_DocPageData::FromDocument(pDoc); ++ return pDocPageData->AddFont(std::move(pFXFont), nCharset); ++} ++ ++ByteString EncodeFontAlias(ByteString sFontName, FX_Charset nCharset) { ++ sFontName.Remove(' '); ++ sFontName += ByteString::Format("_%02X", nCharset); ++ return sFontName; ++} ++ ++} // namespace ++ ++CPDF_BAFontMap::Data::Data() = default; ++ ++CPDF_BAFontMap::Data::~Data() = default; ++ ++CPDF_BAFontMap::CPDF_BAFontMap(CPDF_Document* pDocument, ++ RetainPtr pAnnotDict, ++ const ByteString& sAPType) ++ : m_pDocument(pDocument), ++ m_pAnnotDict(std::move(pAnnotDict)), ++ m_sAPType(sAPType) { ++ FX_Charset nCharset = FX_Charset::kDefault; ++ m_pDefaultFont = GetAnnotDefaultFont(&m_sDefaultFontName); ++ if (m_pDefaultFont) { ++ auto maybe_charset = m_pDefaultFont->GetSubstFontCharset(); ++ if (maybe_charset.has_value()) { ++ nCharset = maybe_charset.value(); ++ } else if (m_sDefaultFontName == "Wingdings" || ++ m_sDefaultFontName == "Wingdings2" || ++ m_sDefaultFontName == "Wingdings3" || ++ m_sDefaultFontName == "Webdings") { ++ nCharset = FX_Charset::kSymbol; ++ } else { ++ nCharset = FX_Charset::kANSI; ++ } ++ AddFontData(m_pDefaultFont, m_sDefaultFontName, nCharset); ++ AddFontToAnnotDict(m_pDefaultFont, m_sDefaultFontName); ++ } ++ ++ if (nCharset != FX_Charset::kANSI) ++ GetFontIndex(CFX_Font::kDefaultAnsiFontName, FX_Charset::kANSI, false); ++} ++ ++CPDF_BAFontMap::~CPDF_BAFontMap() = default; ++ ++RetainPtr CPDF_BAFontMap::GetPDFFont(int32_t nFontIndex) { ++ if (fxcrt::IndexInBounds(m_Data, nFontIndex)) ++ return m_Data[nFontIndex]->pFont; ++ return nullptr; ++} ++ ++ByteString CPDF_BAFontMap::GetPDFFontAlias(int32_t nFontIndex) { ++ if (fxcrt::IndexInBounds(m_Data, nFontIndex)) ++ return m_Data[nFontIndex]->sFontName; ++ return ByteString(); ++} ++ ++int32_t CPDF_BAFontMap::GetWordFontIndex(uint16_t word, ++ FX_Charset nCharset, ++ int32_t nFontIndex) { ++ if (nFontIndex > 0) { ++ if (KnowWord(nFontIndex, word)) ++ return nFontIndex; ++ } else { ++ if (!m_Data.empty()) { ++ const Data* pData = m_Data.front().get(); ++ if (nCharset == FX_Charset::kDefault || ++ pData->nCharset == FX_Charset::kSymbol || ++ nCharset == pData->nCharset) { ++ if (KnowWord(0, word)) ++ return 0; ++ } ++ } ++ } ++ ++ int32_t nNewFontIndex = ++ GetFontIndex(GetCachedNativeFontName(nCharset), nCharset, true); ++ if (nNewFontIndex >= 0) { ++ if (KnowWord(nNewFontIndex, word)) ++ return nNewFontIndex; ++ } ++ nNewFontIndex = GetFontIndex(CFX_Font::kUniversalDefaultFontName, ++ FX_Charset::kDefault, false); ++ if (nNewFontIndex >= 0) { ++ if (KnowWord(nNewFontIndex, word)) ++ return nNewFontIndex; ++ } ++ return -1; ++} ++ ++int32_t CPDF_BAFontMap::CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) { ++ if (!fxcrt::IndexInBounds(m_Data, nFontIndex)) ++ return -1; ++ ++ Data* pData = m_Data[nFontIndex].get(); ++ if (!pData->pFont) ++ return -1; ++ ++ if (pData->pFont->IsUnicodeCompatible()) ++ return pData->pFont->CharCodeFromUnicode(word); ++ ++ return word < 0xFF ? word : -1; ++} ++ ++FX_Charset CPDF_BAFontMap::CharSetFromUnicode(uint16_t word, ++ FX_Charset nOldCharset) { ++ // to avoid CJK Font to show ASCII ++ if (word < 0x7F) ++ return FX_Charset::kANSI; ++ ++ // follow the old charset ++ if (nOldCharset != FX_Charset::kDefault) ++ return nOldCharset; ++ ++ return CFX_Font::GetCharSetFromUnicode(word); ++} ++ ++FX_Charset CPDF_BAFontMap::GetNativeCharset() { ++ return FX_GetCharsetFromCodePage(FX_GetACP()); ++} ++ ++RetainPtr CPDF_BAFontMap::FindFontSameCharset(ByteString* sFontAlias, ++ FX_Charset nCharset) { ++ if (m_pAnnotDict->GetNameFor(pdfium::annotation::kSubtype) != "Widget") ++ return nullptr; ++ ++ const CPDF_Dictionary* pRootDict = m_pDocument->GetRoot(); ++ if (!pRootDict) ++ return nullptr; ++ ++ RetainPtr pAcroFormDict = ++ pRootDict->GetDictFor("AcroForm"); ++ if (!pAcroFormDict) ++ return nullptr; ++ ++ RetainPtr pDRDict = pAcroFormDict->GetDictFor("DR"); ++ if (!pDRDict) ++ return nullptr; ++ ++ return FindResFontSameCharset(pDRDict.Get(), sFontAlias, nCharset); ++} ++ ++RetainPtr CPDF_BAFontMap::FindResFontSameCharset( ++ const CPDF_Dictionary* pResDict, ++ ByteString* sFontAlias, ++ FX_Charset nCharset) { ++ if (!pResDict) ++ return nullptr; ++ ++ RetainPtr pFonts = pResDict->GetDictFor("Font"); ++ if (!pFonts) ++ return nullptr; ++ ++ RetainPtr pFind; ++ CPDF_DictionaryLocker locker(pFonts); ++ for (const auto& it : locker) { ++ const ByteString& csKey = it.first; ++ RetainPtr pElement = ++ ToDictionary(it.second->GetMutableDirect()); ++ if (!ValidateDictType(pElement.Get(), "Font")) ++ continue; ++ ++ auto* pData = CPDF_DocPageData::FromDocument(m_pDocument); ++ RetainPtr pFont = pData->GetFont(std::move(pElement)); ++ if (!pFont) ++ continue; ++ ++ auto maybe_charset = pFont->GetSubstFontCharset(); ++ if (maybe_charset.has_value() && maybe_charset.value() == nCharset) { ++ *sFontAlias = csKey; ++ pFind = std::move(pFont); ++ } ++ } ++ return pFind; ++} ++ ++RetainPtr CPDF_BAFontMap::GetAnnotDefaultFont(ByteString* sAlias) { ++ RetainPtr pAcroFormDict; ++ const bool bWidget = ++ (m_pAnnotDict->GetNameFor(pdfium::annotation::kSubtype) == "Widget"); ++ if (bWidget) { ++ RetainPtr pRootDict = m_pDocument->GetMutableRoot(); ++ if (pRootDict) ++ pAcroFormDict = pRootDict->GetMutableDictFor("AcroForm"); ++ } ++ ++ ByteString sDA; ++ RetainPtr pObj = ++ CPDF_FormField::GetFieldAttrForDict(m_pAnnotDict.Get(), "DA"); ++ if (pObj) ++ sDA = pObj->GetString(); ++ ++ if (bWidget) { ++ if (sDA.IsEmpty()) { ++ pObj = CPDF_FormField::GetFieldAttrForDict(pAcroFormDict.Get(), "DA"); ++ sDA = pObj ? pObj->GetString() : ByteString(); ++ } ++ } ++ if (sDA.IsEmpty()) ++ return nullptr; ++ ++ CPDF_DefaultAppearance appearance(sDA); ++ float font_size; ++ absl::optional font = appearance.GetFont(&font_size); ++ *sAlias = font.value_or(ByteString()); ++ ++ RetainPtr pFontDict; ++ if (RetainPtr pAPDict = ++ m_pAnnotDict->GetMutableDictFor(pdfium::annotation::kAP)) { ++ if (RetainPtr pNormalDict = ++ pAPDict->GetMutableDictFor("N")) { ++ if (RetainPtr pNormalResDict = ++ pNormalDict->GetMutableDictFor("Resources")) { ++ if (RetainPtr pResFontDict = ++ pNormalResDict->GetMutableDictFor("Font")) { ++ pFontDict = pResFontDict->GetMutableDictFor(*sAlias); ++ } ++ } ++ } ++ } ++ if (bWidget && !pFontDict && pAcroFormDict) { ++ if (RetainPtr pDRDict = ++ pAcroFormDict->GetMutableDictFor("DR")) { ++ if (RetainPtr pDRFontDict = ++ pDRDict->GetMutableDictFor("Font")) { ++ pFontDict = pDRFontDict->GetMutableDictFor(*sAlias); ++ } ++ } ++ } ++ if (!pFontDict) ++ return nullptr; ++ ++ return CPDF_DocPageData::FromDocument(m_pDocument)->GetFont(pFontDict); ++} ++ ++void CPDF_BAFontMap::AddFontToAnnotDict(const RetainPtr& pFont, ++ const ByteString& sAlias) { ++ if (!pFont) ++ return; ++ ++ RetainPtr pAPDict = ++ m_pAnnotDict->GetOrCreateDictFor(pdfium::annotation::kAP); ++ ++ // to avoid checkbox and radiobutton ++ if (ToDictionary(pAPDict->GetObjectFor(m_sAPType))) ++ return; ++ ++ RetainPtr pStream = pAPDict->GetMutableStreamFor(m_sAPType); ++ if (!pStream) { ++ pStream = m_pDocument->NewIndirect(); ++ pAPDict->SetNewFor(m_sAPType, m_pDocument, ++ pStream->GetObjNum()); ++ } ++ ++ RetainPtr pStreamDict = pStream->GetMutableDict(); ++ if (!pStreamDict) { ++ pStreamDict = m_pDocument->New(); ++ pStream->InitStreamWithEmptyData(pStreamDict); ++ } ++ ++ RetainPtr pStreamResList = ++ pStreamDict->GetOrCreateDictFor("Resources"); ++ RetainPtr pStreamResFontList = ++ pStreamResList->GetMutableDictFor("Font"); ++ if (!pStreamResFontList) { ++ pStreamResFontList = m_pDocument->NewIndirect(); ++ pStreamResList->SetNewFor("Font", m_pDocument, ++ pStreamResFontList->GetObjNum()); ++ } ++ if (!pStreamResFontList->KeyExist(sAlias)) { ++ RetainPtr pFontDict = pFont->GetFontDict(); ++ RetainPtr pObject = ++ pFontDict->IsInline() ? pFontDict->Clone() ++ : pFontDict->MakeReference(m_pDocument); ++ pStreamResFontList->SetFor(sAlias, std::move(pObject)); ++ } ++} ++ ++bool CPDF_BAFontMap::KnowWord(int32_t nFontIndex, uint16_t word) { ++ return fxcrt::IndexInBounds(m_Data, nFontIndex) && ++ CharCodeFromUnicode(nFontIndex, word) >= 0; ++} ++ ++int32_t CPDF_BAFontMap::GetFontIndex(const ByteString& sFontName, ++ FX_Charset nCharset, ++ bool bFind) { ++ int32_t nFontIndex = FindFont(EncodeFontAlias(sFontName, nCharset), nCharset); ++ if (nFontIndex >= 0) ++ return nFontIndex; ++ ++ ByteString sAlias; ++ RetainPtr pFont = ++ bFind ? FindFontSameCharset(&sAlias, nCharset) : nullptr; ++ if (!pFont) { ++ pFont = AddFontToDocument(sFontName, nCharset); ++ sAlias = EncodeFontAlias(sFontName, nCharset); ++ } ++ AddFontToAnnotDict(pFont, sAlias); ++ return AddFontData(pFont, sAlias, nCharset); ++} ++ ++int32_t CPDF_BAFontMap::AddFontData(const RetainPtr& pFont, ++ const ByteString& sFontAlias, ++ FX_Charset nCharset) { ++ auto pNewData = std::make_unique(); ++ pNewData->pFont = pFont; ++ pNewData->sFontName = sFontAlias; ++ pNewData->nCharset = nCharset; ++ m_Data.push_back(std::move(pNewData)); ++ return fxcrt::CollectionSize(m_Data) - 1; ++} ++ ++int32_t CPDF_BAFontMap::FindFont(const ByteString& sFontName, ++ FX_Charset nCharset) { ++ int32_t i = 0; ++ for (const auto& pData : m_Data) { ++ if ((nCharset == FX_Charset::kDefault || nCharset == pData->nCharset) && ++ (sFontName.IsEmpty() || pData->sFontName == sFontName)) { ++ return i; ++ } ++ ++i; ++ } ++ return -1; ++} ++ ++ByteString CPDF_BAFontMap::GetNativeFontName(FX_Charset nCharset) { ++ if (nCharset == FX_Charset::kDefault) ++ nCharset = GetNativeCharset(); ++ ++ ByteString sFontName = CFX_Font::GetDefaultFontNameByCharset(nCharset); ++ if (!FindNativeTrueTypeFont(sFontName.AsStringView())) ++ return ByteString(); ++ ++ return sFontName; ++} ++ ++ByteString CPDF_BAFontMap::GetCachedNativeFontName(FX_Charset nCharset) { ++ for (const auto& pData : m_NativeFont) { ++ if (pData && pData->nCharset == nCharset) ++ return pData->sFontName; ++ } ++ ++ ByteString sNew = GetNativeFontName(nCharset); ++ if (sNew.IsEmpty()) ++ return ByteString(); ++ ++ auto pNewData = std::make_unique(); ++ pNewData->nCharset = nCharset; ++ pNewData->sFontName = sNew; ++ m_NativeFont.push_back(std::move(pNewData)); ++ return sNew; ++} ++ ++RetainPtr CPDF_BAFontMap::AddFontToDocument(ByteString sFontName, ++ FX_Charset nCharset) { ++ if (CFX_FontMapper::IsStandardFontName(sFontName)) ++ return AddStandardFont(sFontName); ++ ++ return AddSystemFont(sFontName, nCharset); ++} ++ ++RetainPtr CPDF_BAFontMap::AddStandardFont(ByteString sFontName) { ++ auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument); ++ if (sFontName == "ZapfDingbats") ++ return pPageData->AddStandardFont(sFontName, nullptr); ++ ++ static const CPDF_FontEncoding fe(FontEncoding::kWinAnsi); ++ return pPageData->AddStandardFont(sFontName, &fe); ++} ++ ++RetainPtr CPDF_BAFontMap::AddSystemFont(ByteString sFontName, ++ FX_Charset nCharset) { ++ if (sFontName.IsEmpty()) ++ sFontName = GetNativeFontName(nCharset); ++ ++ if (nCharset == FX_Charset::kDefault) ++ nCharset = GetNativeCharset(); ++ ++ return AddNativeTrueTypeFontToPDF(m_pDocument, sFontName, nCharset); ++} +diff --git a/core/fpdfdoc/cba_fontmap.h b/core/fpdfdoc/cpdf_bafontmap.h +similarity index 58% +rename from core/fpdfdoc/cba_fontmap.h +rename to core/fpdfdoc/cpdf_bafontmap.h +index 713006463..5cd4bcd5a 100644 +--- a/core/fpdfdoc/cba_fontmap.h ++++ b/core/fpdfdoc/cpdf_bafontmap.h +@@ -1,11 +1,11 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#ifndef CORE_FPDFDOC_CBA_FONTMAP_H_ +-#define CORE_FPDFDOC_CBA_FONTMAP_H_ ++#ifndef CORE_FPDFDOC_CPDF_BAFONTMAP_H_ ++#define CORE_FPDFDOC_CPDF_BAFONTMAP_H_ + + #include + #include +@@ -18,71 +18,64 @@ + class CPDF_Dictionary; + class CPDF_Document; + +-class CBA_FontMap final : public IPVT_FontMap { ++class CPDF_BAFontMap final : public IPVT_FontMap { + public: +- static int32_t GetNativeCharset(); ++ static FX_Charset GetNativeCharset(); + +- CBA_FontMap(CPDF_Document* pDocument, CPDF_Dictionary* pAnnotDict); +- ~CBA_FontMap() override; ++ CPDF_BAFontMap(CPDF_Document* pDocument, ++ RetainPtr pAnnotDict, ++ const ByteString& sAPType); ++ ~CPDF_BAFontMap() override; + +- // IPVT_FontMap ++ // IPVT_FontMap: + RetainPtr GetPDFFont(int32_t nFontIndex) override; + ByteString GetPDFFontAlias(int32_t nFontIndex) override; + int32_t GetWordFontIndex(uint16_t word, +- int32_t nCharset, ++ FX_Charset nCharset, + int32_t nFontIndex) override; + int32_t CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) override; +- int32_t CharSetFromUnicode(uint16_t word, int32_t nOldCharset) override; +- +- void Reset(); +- void SetAPType(const ByteString& sAPType); ++ FX_Charset CharSetFromUnicode(uint16_t word, FX_Charset nOldCharset) override; + + private: + struct Data { + Data(); + ~Data(); + ++ FX_Charset nCharset = FX_Charset::kANSI; + RetainPtr pFont; +- int32_t nCharset; + ByteString sFontName; + }; + + struct Native { +- int32_t nCharset; ++ FX_Charset nCharset; + ByteString sFontName; + }; + +- void Initialize(); + RetainPtr FindFontSameCharset(ByteString* sFontAlias, +- int32_t nCharset); ++ FX_Charset nCharset); + RetainPtr FindResFontSameCharset(const CPDF_Dictionary* pResDict, + ByteString* sFontAlias, +- int32_t nCharset); ++ FX_Charset nCharset); + RetainPtr GetAnnotDefaultFont(ByteString* sAlias); + void AddFontToAnnotDict(const RetainPtr& pFont, + const ByteString& sAlias); + + bool KnowWord(int32_t nFontIndex, uint16_t word); + +- void Clear(); + int32_t GetFontIndex(const ByteString& sFontName, +- int32_t nCharset, ++ FX_Charset nCharset, + bool bFind); + int32_t AddFontData(const RetainPtr& pFont, + const ByteString& sFontAlias, +- int32_t nCharset); +- +- ByteString EncodeFontAlias(const ByteString& sFontName, int32_t nCharset); +- ByteString EncodeFontAlias(const ByteString& sFontName); ++ FX_Charset nCharset); + +- int32_t FindFont(const ByteString& sFontName, int32_t nCharset); +- ByteString GetNativeFontName(int32_t nCharset); +- ByteString GetCachedNativeFontName(int32_t nCharset); +- bool IsStandardFont(const ByteString& sFontName); ++ int32_t FindFont(const ByteString& sFontName, FX_Charset nCharset); ++ ByteString GetNativeFontName(FX_Charset nCharset); ++ ByteString GetCachedNativeFontName(FX_Charset nCharset); + RetainPtr AddFontToDocument(ByteString sFontName, +- uint8_t nCharset); ++ FX_Charset nCharset); + RetainPtr AddStandardFont(ByteString sFontName); +- RetainPtr AddSystemFont(ByteString sFontName, uint8_t nCharset); ++ RetainPtr AddSystemFont(ByteString sFontName, FX_Charset nCharset); + + std::vector> m_Data; + std::vector> m_NativeFont; +@@ -90,7 +83,7 @@ class CBA_FontMap final : public IPVT_FontMap { + RetainPtr const m_pAnnotDict; + RetainPtr m_pDefaultFont; + ByteString m_sDefaultFontName; +- ByteString m_sAPType = "N"; ++ const ByteString m_sAPType; + }; + +-#endif // CORE_FPDFDOC_CBA_FONTMAP_H_ ++#endif // CORE_FPDFDOC_CPDF_BAFONTMAP_H_ +diff --git a/core/fpdfdoc/cpdf_bafontmap_unittest.cpp b/core/fpdfdoc/cpdf_bafontmap_unittest.cpp +new file mode 100644 +index 000000000..25e7da574 +--- /dev/null ++++ b/core/fpdfdoc/cpdf_bafontmap_unittest.cpp +@@ -0,0 +1,63 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fpdfdoc/cpdf_bafontmap.h" ++ ++#include ++ ++#include "build/build_config.h" ++#include "constants/annotation_common.h" ++#include "core/fpdfapi/font/cpdf_font.h" ++#include "core/fpdfapi/page/test_with_page_module.h" ++#include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fpdfapi/parser/cpdf_name.h" ++#include "core/fpdfapi/parser/cpdf_string.h" ++#include "core/fpdfapi/parser/cpdf_test_document.h" ++ ++using BAFontMapTest = TestWithPageModule; ++ ++TEST_F(BAFontMapTest, DefaultFont) { ++ // Without any font resources, CPDF_BAFontMap generates a default font. ++ CPDF_TestDocument doc; ++ ++ auto annot_dict = pdfium::MakeRetain(); ++ annot_dict->SetNewFor(pdfium::annotation::kSubtype, "Widget"); ++ annot_dict->SetNewFor("DA", "0 0 0 rg /F1 12 Tf", ++ /*bHex=*/false); ++ ++ CPDF_BAFontMap font_map(&doc, std::move(annot_dict), "N"); ++#if !BUILDFLAG(IS_WIN) ++ // TODO(thestig): Figure out why this does not work on Windows. ++ EXPECT_EQ(font_map.GetPDFFontAlias(0), "Helvetica_00"); ++#endif ++ RetainPtr font = font_map.GetPDFFont(0); ++ ASSERT_TRUE(font); ++ EXPECT_TRUE(font->IsType1Font()); ++ EXPECT_EQ(font->GetBaseFontName(), "Helvetica"); ++} ++ ++TEST_F(BAFontMapTest, Bug853238) { ++ CPDF_TestDocument doc; ++ auto root_dict = pdfium::MakeRetain(); ++ auto acroform_dict = root_dict->SetNewFor("AcroForm"); ++ auto annot_dr_dict = acroform_dict->SetNewFor("DR"); ++ auto annot_font_dict = annot_dr_dict->SetNewFor("Font"); ++ auto annot_font_f1_dict = annot_font_dict->SetNewFor("F1"); ++ annot_font_f1_dict->SetNewFor("Type", "Font"); ++ annot_font_f1_dict->SetNewFor("Subtype", "Type1"); ++ annot_font_f1_dict->SetNewFor("BaseFont", "Times-Roman"); ++ doc.SetRoot(root_dict); ++ ++ auto annot_dict = pdfium::MakeRetain(); ++ annot_dict->SetNewFor(pdfium::annotation::kSubtype, "Widget"); ++ annot_dict->SetNewFor("DA", "0 0 0 rg /F1 12 Tf", ++ /*bHex=*/false); ++ ++ CPDF_BAFontMap font_map(&doc, std::move(annot_dict), "N"); ++ EXPECT_EQ(font_map.GetPDFFontAlias(0), "F1"); ++ RetainPtr font = font_map.GetPDFFont(0); ++ ASSERT_TRUE(font); ++ EXPECT_TRUE(font->IsType1Font()); ++ EXPECT_EQ(font->GetBaseFontName(), "Times-Roman"); ++} +diff --git a/core/fpdfdoc/cpdf_bookmark.cpp b/core/fpdfdoc/cpdf_bookmark.cpp +index b08fba586..f4176b9e4 100644 +--- a/core/fpdfdoc/cpdf_bookmark.cpp ++++ b/core/fpdfdoc/cpdf_bookmark.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,21 +6,20 @@ + + #include "core/fpdfdoc/cpdf_bookmark.h" + +-#include +-#include ++#include + + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_string.h" +-#include "core/fpdfdoc/cpdf_nametree.h" +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxge/dib/fx_dib.h" + + CPDF_Bookmark::CPDF_Bookmark() = default; + + CPDF_Bookmark::CPDF_Bookmark(const CPDF_Bookmark& that) = default; + +-CPDF_Bookmark::CPDF_Bookmark(const CPDF_Dictionary* pDict) : m_pDict(pDict) {} ++CPDF_Bookmark::CPDF_Bookmark(RetainPtr pDict) ++ : m_pDict(std::move(pDict)) {} + + CPDF_Bookmark::~CPDF_Bookmark() = default; + +@@ -28,17 +27,18 @@ WideString CPDF_Bookmark::GetTitle() const { + if (!m_pDict) + return WideString(); + +- const CPDF_String* pString = ToString(m_pDict->GetDirectObjectFor("Title")); ++ RetainPtr pString = ++ ToString(m_pDict->GetDirectObjectFor("Title")); + if (!pString) + return WideString(); + + WideString title = pString->GetUnicodeText(); +- int len = title.GetLength(); ++ size_t len = title.GetLength(); + if (!len) + return WideString(); + +- std::vector> buf(len); +- for (int i = 0; i < len; i++) { ++ DataVector buf(len); ++ for (size_t i = 0; i < len; i++) { + wchar_t w = title[i]; + buf[i] = w > 0x20 ? w : 0x20; + } +@@ -47,21 +47,14 @@ WideString CPDF_Bookmark::GetTitle() const { + + CPDF_Dest CPDF_Bookmark::GetDest(CPDF_Document* pDocument) const { + if (!m_pDict) +- return CPDF_Dest(); +- +- const CPDF_Object* pDest = m_pDict->GetDirectObjectFor("Dest"); +- if (!pDest) +- return CPDF_Dest(); +- if (pDest->IsString() || pDest->IsName()) { +- CPDF_NameTree name_tree(pDocument, "Dests"); +- return CPDF_Dest( +- name_tree.LookupNamedDest(pDocument, pDest->GetUnicodeText())); +- } +- if (const CPDF_Array* pArray = pDest->AsArray()) +- return CPDF_Dest(pArray); +- return CPDF_Dest(); ++ return CPDF_Dest(nullptr); ++ return CPDF_Dest::Create(pDocument, m_pDict->GetDirectObjectFor("Dest")); + } + + CPDF_Action CPDF_Bookmark::GetAction() const { + return CPDF_Action(m_pDict ? m_pDict->GetDictFor("A") : nullptr); + } ++ ++int CPDF_Bookmark::GetCount() const { ++ return m_pDict->GetIntegerFor("Count"); ++} +diff --git a/core/fpdfdoc/cpdf_bookmark.h b/core/fpdfdoc/cpdf_bookmark.h +index b185c0374..11a9d18c2 100644 +--- a/core/fpdfdoc/cpdf_bookmark.h ++++ b/core/fpdfdoc/cpdf_bookmark.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,8 +9,8 @@ + + #include "core/fpdfdoc/cpdf_action.h" + #include "core/fpdfdoc/cpdf_dest.h" +-#include "core/fxcrt/fx_string.h" + #include "core/fxcrt/retain_ptr.h" ++#include "core/fxcrt/widestring.h" + + class CPDF_Dictionary; + class CPDF_Document; +@@ -19,7 +19,7 @@ class CPDF_Bookmark { + public: + CPDF_Bookmark(); + CPDF_Bookmark(const CPDF_Bookmark& that); +- explicit CPDF_Bookmark(const CPDF_Dictionary* pDict); ++ explicit CPDF_Bookmark(RetainPtr pDict); + ~CPDF_Bookmark(); + + const CPDF_Dictionary* GetDict() const { return m_pDict.Get(); } +@@ -27,6 +27,7 @@ class CPDF_Bookmark { + WideString GetTitle() const; + CPDF_Dest GetDest(CPDF_Document* pDocument) const; + CPDF_Action GetAction() const; ++ int GetCount() const; + + private: + RetainPtr m_pDict; +diff --git a/core/fpdfdoc/cpdf_bookmarktree.cpp b/core/fpdfdoc/cpdf_bookmarktree.cpp +index 5c4fffef6..1f1aea98e 100644 +--- a/core/fpdfdoc/cpdf_bookmarktree.cpp ++++ b/core/fpdfdoc/cpdf_bookmarktree.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,32 +6,37 @@ + + #include "core/fpdfdoc/cpdf_bookmarktree.h" + ++#include ++ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + +-CPDF_BookmarkTree::CPDF_BookmarkTree(CPDF_Document* pDoc) : m_pDocument(pDoc) {} ++CPDF_BookmarkTree::CPDF_BookmarkTree(const CPDF_Document* doc) ++ : document_(doc) {} + + CPDF_BookmarkTree::~CPDF_BookmarkTree() = default; + +-CPDF_Bookmark CPDF_BookmarkTree::GetFirstChild(CPDF_Bookmark* parent) const { +- const CPDF_Dictionary* pParentDict = parent->GetDict(); +- if (pParentDict) +- return CPDF_Bookmark(pParentDict->GetDictFor("First")); ++CPDF_Bookmark CPDF_BookmarkTree::GetFirstChild( ++ const CPDF_Bookmark& parent) const { ++ const CPDF_Dictionary* parent_dict = parent.GetDict(); ++ if (parent_dict) ++ return CPDF_Bookmark(parent_dict->GetDictFor("First")); + +- CPDF_Dictionary* pRoot = m_pDocument->GetRoot(); +- if (!pRoot) ++ const CPDF_Dictionary* root = document_->GetRoot(); ++ if (!root) + return CPDF_Bookmark(); + +- CPDF_Dictionary* pOutlines = pRoot->GetDictFor("Outlines"); +- return pOutlines ? CPDF_Bookmark(pOutlines->GetDictFor("First")) +- : CPDF_Bookmark(); ++ RetainPtr outlines = root->GetDictFor("Outlines"); ++ return outlines ? CPDF_Bookmark(outlines->GetDictFor("First")) ++ : CPDF_Bookmark(); + } + +-CPDF_Bookmark CPDF_BookmarkTree::GetNextSibling(CPDF_Bookmark* bookmark) const { +- const CPDF_Dictionary* pDict = bookmark->GetDict(); +- if (!pDict) ++CPDF_Bookmark CPDF_BookmarkTree::GetNextSibling( ++ const CPDF_Bookmark& bookmark) const { ++ const CPDF_Dictionary* dict = bookmark.GetDict(); ++ if (!dict) + return CPDF_Bookmark(); + +- const CPDF_Dictionary* pNext = pDict->GetDictFor("Next"); +- return pNext == pDict ? CPDF_Bookmark() : CPDF_Bookmark(pNext); ++ RetainPtr next = dict->GetDictFor("Next"); ++ return next != dict ? CPDF_Bookmark(std::move(next)) : CPDF_Bookmark(); + } +diff --git a/core/fpdfdoc/cpdf_bookmarktree.h b/core/fpdfdoc/cpdf_bookmarktree.h +index b374bfa5b..144879bdf 100644 +--- a/core/fpdfdoc/cpdf_bookmarktree.h ++++ b/core/fpdfdoc/cpdf_bookmarktree.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,15 +14,14 @@ class CPDF_Document; + + class CPDF_BookmarkTree { + public: +- explicit CPDF_BookmarkTree(CPDF_Document* pDoc); ++ explicit CPDF_BookmarkTree(const CPDF_Document* doc); + ~CPDF_BookmarkTree(); + +- CPDF_Bookmark GetFirstChild(CPDF_Bookmark* parent) const; +- CPDF_Bookmark GetNextSibling(CPDF_Bookmark* bookmark) const; +- CPDF_Document* GetDocument() const { return m_pDocument.Get(); } ++ CPDF_Bookmark GetFirstChild(const CPDF_Bookmark& parent) const; ++ CPDF_Bookmark GetNextSibling(const CPDF_Bookmark& bookmark) const; + + private: +- UnownedPtr const m_pDocument; ++ UnownedPtr const document_; + }; + + #endif // CORE_FPDFDOC_CPDF_BOOKMARKTREE_H_ +diff --git a/core/fpdfdoc/cpdf_color_utils.cpp b/core/fpdfdoc/cpdf_color_utils.cpp +index 198b1ca6a..b4e1d8683 100644 +--- a/core/fpdfdoc/cpdf_color_utils.cpp ++++ b/core/fpdfdoc/cpdf_color_utils.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,6 +9,7 @@ + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfdoc/cpdf_defaultappearance.h" + #include "core/fxcrt/bytestring.h" ++#include "third_party/base/notreached.h" + + namespace fpdfdoc { + +@@ -16,16 +17,16 @@ CFX_Color CFXColorFromArray(const CPDF_Array& array) { + CFX_Color rt; + switch (array.size()) { + case 1: +- rt = CFX_Color(CFX_Color::kGray, array.GetNumberAt(0)); ++ rt = CFX_Color(CFX_Color::Type::kGray, array.GetFloatAt(0)); + break; + case 3: +- rt = CFX_Color(CFX_Color::kRGB, array.GetNumberAt(0), +- array.GetNumberAt(1), array.GetNumberAt(2)); ++ rt = CFX_Color(CFX_Color::Type::kRGB, array.GetFloatAt(0), ++ array.GetFloatAt(1), array.GetFloatAt(2)); + break; + case 4: +- rt = CFX_Color(CFX_Color::kCMYK, array.GetNumberAt(0), +- array.GetNumberAt(1), array.GetNumberAt(2), +- array.GetNumberAt(3)); ++ rt = CFX_Color(CFX_Color::Type::kCMYK, array.GetFloatAt(0), ++ array.GetFloatAt(1), array.GetFloatAt(2), ++ array.GetFloatAt(3)); + break; + } + return rt; +@@ -33,21 +34,7 @@ CFX_Color CFXColorFromArray(const CPDF_Array& array) { + + CFX_Color CFXColorFromString(const ByteString& str) { + CPDF_DefaultAppearance appearance(str); +- float values[4]; +- Optional color_type = appearance.GetColor(values); +- if (!color_type || *color_type == CFX_Color::kTransparent) +- return CFX_Color(CFX_Color::kTransparent); +- if (*color_type == CFX_Color::kGray) +- return CFX_Color(CFX_Color::kGray, values[0]); +- if (*color_type == CFX_Color::kRGB) +- return CFX_Color(CFX_Color::kRGB, values[0], values[1], values[2]); +- if (*color_type == CFX_Color::kCMYK) { +- return CFX_Color(CFX_Color::kCMYK, values[0], values[1], values[2], +- values[3]); +- } +- +- NOTREACHED(); +- return CFX_Color(CFX_Color::kTransparent); ++ return appearance.GetColor().value_or(CFX_Color()); + } + + } // namespace fpdfdoc +diff --git a/core/fpdfdoc/cpdf_color_utils.h b/core/fpdfdoc/cpdf_color_utils.h +index 993a7d730..05998cbc9 100644 +--- a/core/fpdfdoc/cpdf_color_utils.h ++++ b/core/fpdfdoc/cpdf_color_utils.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fpdfdoc/cpdf_defaultappearance.cpp b/core/fpdfdoc/cpdf_defaultappearance.cpp +index e6ef4f77e..1eda1d928 100644 +--- a/core/fpdfdoc/cpdf_defaultappearance.cpp ++++ b/core/fpdfdoc/cpdf_defaultappearance.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,7 +11,9 @@ + + #include "core/fpdfapi/parser/cpdf_simple_parser.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" ++#include "core/fxcrt/fx_string.h" + #include "core/fxge/cfx_color.h" ++#include "third_party/base/notreached.h" + + namespace { + +@@ -27,7 +29,7 @@ bool FindTagParamFromStart(CPDF_SimpleParser* parser, + int buf_count = 0; + + parser->SetCurPos(0); +- while (1) { ++ while (true) { + pBuf[buf_index++] = parser->GetCurPos(); + if (buf_index == nParams) + buf_index = 0; +@@ -48,15 +50,25 @@ bool FindTagParamFromStart(CPDF_SimpleParser* parser, + return true; + } + } +- return false; + } + + } // namespace + +-Optional CPDF_DefaultAppearance::GetFont(float* fFontSize) { ++CPDF_DefaultAppearance::CPDF_DefaultAppearance() = default; ++ ++CPDF_DefaultAppearance::CPDF_DefaultAppearance(const ByteString& csDA) ++ : m_csDA(csDA) {} ++ ++CPDF_DefaultAppearance::CPDF_DefaultAppearance( ++ const CPDF_DefaultAppearance& cDA) = default; ++ ++CPDF_DefaultAppearance::~CPDF_DefaultAppearance() = default; ++ ++absl::optional CPDF_DefaultAppearance::GetFont( ++ float* fFontSize) const { + *fFontSize = 0.0f; + if (m_csDA.IsEmpty()) +- return {}; ++ return absl::nullopt; + + ByteString csFontNameTag; + CPDF_SimpleParser syntax(m_csDA.AsStringView().raw_span()); +@@ -65,67 +77,69 @@ Optional CPDF_DefaultAppearance::GetFont(float* fFontSize) { + csFontNameTag.Delete(0, 1); + *fFontSize = StringToFloat(syntax.GetWord()); + } +- return {PDF_NameDecode(csFontNameTag.AsStringView())}; ++ return PDF_NameDecode(csFontNameTag.AsStringView()); + } + +-Optional CPDF_DefaultAppearance::GetColor(float fc[4]) { +- for (int c = 0; c < 4; c++) +- fc[c] = 0; +- ++absl::optional CPDF_DefaultAppearance::GetColor() const { + if (m_csDA.IsEmpty()) +- return {}; ++ return absl::nullopt; + ++ float fc[4]; + CPDF_SimpleParser syntax(m_csDA.AsStringView().raw_span()); + if (FindTagParamFromStart(&syntax, "g", 1)) { + fc[0] = StringToFloat(syntax.GetWord()); +- return {CFX_Color::kGray}; ++ return CFX_Color(CFX_Color::Type::kGray, fc[0]); + } + if (FindTagParamFromStart(&syntax, "rg", 3)) { + fc[0] = StringToFloat(syntax.GetWord()); + fc[1] = StringToFloat(syntax.GetWord()); + fc[2] = StringToFloat(syntax.GetWord()); +- return {CFX_Color::kRGB}; ++ return CFX_Color(CFX_Color::Type::kRGB, fc[0], fc[1], fc[2]); + } + if (FindTagParamFromStart(&syntax, "k", 4)) { + fc[0] = StringToFloat(syntax.GetWord()); + fc[1] = StringToFloat(syntax.GetWord()); + fc[2] = StringToFloat(syntax.GetWord()); + fc[3] = StringToFloat(syntax.GetWord()); +- return {CFX_Color::kCMYK}; ++ return CFX_Color(CFX_Color::Type::kCMYK, fc[0], fc[1], fc[2], fc[3]); + } +- +- return {}; ++ return absl::nullopt; + } + +-std::pair, FX_ARGB> +-CPDF_DefaultAppearance::GetColor() { +- float values[4]; +- Optional type = GetColor(values); +- if (!type) +- return {type, 0}; +- +- if (*type == CFX_Color::kGray) { +- int g = static_cast(values[0] * 255 + 0.5f); +- return {type, ArgbEncode(255, g, g, g)}; ++absl::optional CPDF_DefaultAppearance::GetColorARGB() ++ const { ++ absl::optional maybe_color = GetColor(); ++ if (!maybe_color.has_value()) ++ return absl::nullopt; ++ ++ const CFX_Color& color = maybe_color.value(); ++ if (color.nColorType == CFX_Color::Type::kGray) { ++ int g = static_cast(color.fColor1 * 255 + 0.5f); ++ return CFX_Color::TypeAndARGB(CFX_Color::Type::kGray, ++ ArgbEncode(255, g, g, g)); + } +- if (*type == CFX_Color::kRGB) { +- int r = static_cast(values[0] * 255 + 0.5f); +- int g = static_cast(values[1] * 255 + 0.5f); +- int b = static_cast(values[2] * 255 + 0.5f); +- return {type, ArgbEncode(255, r, g, b)}; ++ if (color.nColorType == CFX_Color::Type::kRGB) { ++ int r = static_cast(color.fColor1 * 255 + 0.5f); ++ int g = static_cast(color.fColor2 * 255 + 0.5f); ++ int b = static_cast(color.fColor3 * 255 + 0.5f); ++ return CFX_Color::TypeAndARGB(CFX_Color::Type::kRGB, ++ ArgbEncode(255, r, g, b)); + } +- if (*type == CFX_Color::kCMYK) { +- float r = 1.0f - std::min(1.0f, values[0] + values[3]); +- float g = 1.0f - std::min(1.0f, values[1] + values[3]); +- float b = 1.0f - std::min(1.0f, values[2] + values[3]); +- return {type, ArgbEncode(255, static_cast(r * 255 + 0.5f), +- static_cast(g * 255 + 0.5f), +- static_cast(b * 255 + 0.5f))}; ++ if (color.nColorType == CFX_Color::Type::kCMYK) { ++ float r = 1.0f - std::min(1.0f, color.fColor1 + color.fColor4); ++ float g = 1.0f - std::min(1.0f, color.fColor2 + color.fColor4); ++ float b = 1.0f - std::min(1.0f, color.fColor3 + color.fColor4); ++ return CFX_Color::TypeAndARGB( ++ CFX_Color::Type::kCMYK, ++ ArgbEncode(255, static_cast(r * 255 + 0.5f), ++ static_cast(g * 255 + 0.5f), ++ static_cast(b * 255 + 0.5f))); + } + NOTREACHED(); +- return {{}, 0}; ++ return absl::nullopt; + } + ++// static + bool CPDF_DefaultAppearance::FindTagParamFromStartForTesting( + CPDF_SimpleParser* parser, + ByteStringView token, +diff --git a/core/fpdfdoc/cpdf_defaultappearance.h b/core/fpdfdoc/cpdf_defaultappearance.h +index 97762f86a..42389ec21 100644 +--- a/core/fpdfdoc/cpdf_defaultappearance.h ++++ b/core/fpdfdoc/cpdf_defaultappearance.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,32 +7,30 @@ + #ifndef CORE_FPDFDOC_CPDF_DEFAULTAPPEARANCE_H_ + #define CORE_FPDFDOC_CPDF_DEFAULTAPPEARANCE_H_ + +-#include +- +-#include "core/fpdfapi/parser/cpdf_simple_parser.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxge/cfx_color.h" +-#include "core/fxge/fx_dib.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++ ++class CPDF_SimpleParser; + + class CPDF_DefaultAppearance { + public: +- CPDF_DefaultAppearance() {} +- explicit CPDF_DefaultAppearance(const ByteString& csDA) : m_csDA(csDA) {} +- CPDF_DefaultAppearance(const CPDF_DefaultAppearance& cDA) +- : m_csDA(cDA.m_csDA) {} ++ CPDF_DefaultAppearance(); ++ explicit CPDF_DefaultAppearance(const ByteString& csDA); ++ CPDF_DefaultAppearance(const CPDF_DefaultAppearance& cDA); ++ ~CPDF_DefaultAppearance(); + +- Optional GetFont(float* fFontSize); ++ absl::optional GetFont(float* fFontSize) const; + +- Optional GetColor(float fc[4]); +- std::pair, FX_ARGB> GetColor(); ++ absl::optional GetColor() const; ++ absl::optional GetColorARGB() const; + +- bool FindTagParamFromStartForTesting(CPDF_SimpleParser* parser, +- ByteStringView token, +- int nParams); ++ static bool FindTagParamFromStartForTesting(CPDF_SimpleParser* parser, ++ ByteStringView token, ++ int nParams); + + private: +- ByteString m_csDA; ++ const ByteString m_csDA; + }; + + #endif // CORE_FPDFDOC_CPDF_DEFAULTAPPEARANCE_H_ +diff --git a/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp b/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp +index 6f6c52505..86bac2753 100644 +--- a/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp ++++ b/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp +@@ -1,9 +1,12 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfdoc/cpdf_defaultappearance.h" + ++#include ++ ++#include "core/fpdfapi/parser/cpdf_simple_parser.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/test_support.h" + #include "third_party/base/span.h" +@@ -35,13 +38,12 @@ TEST(CPDFDefaultAppearanceTest, FindTagParamFromStart) { + STR_IN_TEST_CASE("1 2 3 4 5 6 7 8 cm", "cm", 6, true, 3), + }; + +- CPDF_DefaultAppearance da; +- for (size_t i = 0; i < FX_ArraySize(test_data); ++i) { ++ for (size_t i = 0; i < std::size(test_data); ++i) { + CPDF_SimpleParser parser( + pdfium::make_span(test_data[i].input, test_data[i].input_size)); + EXPECT_EQ(test_data[i].result, +- da.FindTagParamFromStartForTesting(&parser, test_data[i].token, +- test_data[i].num_params)) ++ CPDF_DefaultAppearance::FindTagParamFromStartForTesting( ++ &parser, test_data[i].token, test_data[i].num_params)) + << " for case " << i; + EXPECT_EQ(test_data[i].result_pos, parser.GetCurPos()) << " for case " << i; + } +diff --git a/core/fpdfdoc/cpdf_dest.cpp b/core/fpdfdoc/cpdf_dest.cpp +index 1a07acfc0..b7682e1e4 100644 +--- a/core/fpdfdoc/cpdf_dest.cpp ++++ b/core/fpdfdoc/cpdf_dest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,42 +7,54 @@ + #include "core/fpdfdoc/cpdf_dest.h" + + #include ++#include ++#include + + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_number.h" ++#include "core/fpdfdoc/cpdf_nametree.h" + + namespace { + + // These arrays are indexed by the PDFDEST_VIEW_* constants. + + // Last element is a sentinel. +-const char* const g_sZoomModes[] = {"Unknown", "XYZ", "Fit", "FitH", +- "FitV", "FitR", "FitB", "FitBH", +- "FitBV", nullptr}; ++const char* const kZoomModes[] = {"Unknown", "XYZ", "Fit", "FitH", "FitV", ++ "FitR", "FitB", "FitBH", "FitBV", nullptr}; + +-const uint8_t g_sZoomModeMaxParamCount[] = {0, 3, 0, 1, 1, 4, 0, 1, 1, 0}; ++constexpr uint8_t kZoomModeMaxParamCount[] = {0, 3, 0, 1, 1, 4, 0, 1, 1, 0}; + +-static_assert(FX_ArraySize(g_sZoomModes) == +- FX_ArraySize(g_sZoomModeMaxParamCount), ++static_assert(std::size(kZoomModes) == std::size(kZoomModeMaxParamCount), + "Zoom mode count Mismatch"); + + } // namespace + +-CPDF_Dest::CPDF_Dest() {} +- +-CPDF_Dest::CPDF_Dest(const CPDF_Array* pArray) : m_pArray(pArray) {} ++CPDF_Dest::CPDF_Dest(RetainPtr pArray) ++ : m_pArray(std::move(pArray)) {} + + CPDF_Dest::CPDF_Dest(const CPDF_Dest& that) = default; + +-CPDF_Dest::~CPDF_Dest() {} ++CPDF_Dest::~CPDF_Dest() = default; ++ ++// static ++CPDF_Dest CPDF_Dest::Create(CPDF_Document* pDoc, ++ RetainPtr pDest) { ++ if (!pDest) ++ return CPDF_Dest(nullptr); ++ ++ if (pDest->IsString() || pDest->IsName()) ++ return CPDF_Dest(CPDF_NameTree::LookupNamedDest(pDoc, pDest->GetString())); ++ ++ return CPDF_Dest(ToArray(pDest)); ++} + + int CPDF_Dest::GetDestPageIndex(CPDF_Document* pDoc) const { + if (!m_pArray) + return -1; + +- const CPDF_Object* pPage = m_pArray->GetDirectObjectAt(0); ++ RetainPtr pPage = m_pArray->GetDirectObjectAt(0); + if (!pPage) + return -1; + +@@ -55,17 +67,28 @@ int CPDF_Dest::GetDestPageIndex(CPDF_Document* pDoc) const { + return pDoc->GetPageIndex(pPage->GetObjNum()); + } + ++std::vector CPDF_Dest::GetScrollPositionArray() const { ++ std::vector result; ++ if (m_pArray) { ++ // Skip over index 0 which contains destination page details, and index 1 ++ // which contains a parameter that describes the rest of the array. ++ for (size_t i = 2; i < m_pArray->size(); i++) ++ result.push_back(m_pArray->GetFloatAt(i)); ++ } ++ return result; ++} ++ + int CPDF_Dest::GetZoomMode() const { + if (!m_pArray) + return 0; + +- const CPDF_Object* pArray = m_pArray->GetDirectObjectAt(1); ++ RetainPtr pArray = m_pArray->GetDirectObjectAt(1); + if (!pArray) + return 0; + + ByteString mode = pArray->GetString(); +- for (int i = 1; g_sZoomModes[i]; ++i) { +- if (mode == g_sZoomModes[i]) ++ for (int i = 1; kZoomModes[i]; ++i) { ++ if (mode == kZoomModes[i]) + return i; + } + +@@ -88,13 +111,14 @@ bool CPDF_Dest::GetXYZ(bool* pHasX, + if (m_pArray->size() < 5) + return false; + +- const CPDF_Name* xyz = ToName(m_pArray->GetDirectObjectAt(1)); ++ RetainPtr xyz = ToName(m_pArray->GetDirectObjectAt(1)); + if (!xyz || xyz->GetString() != "XYZ") + return false; + +- const CPDF_Number* numX = ToNumber(m_pArray->GetDirectObjectAt(2)); +- const CPDF_Number* numY = ToNumber(m_pArray->GetDirectObjectAt(3)); +- const CPDF_Number* numZoom = ToNumber(m_pArray->GetDirectObjectAt(4)); ++ RetainPtr numX = ToNumber(m_pArray->GetDirectObjectAt(2)); ++ RetainPtr numY = ToNumber(m_pArray->GetDirectObjectAt(3)); ++ RetainPtr numZoom = ++ ToNumber(m_pArray->GetDirectObjectAt(4)); + + // If the value is a CPDF_Null then ToNumber will return nullptr. + *pHasX = !!numX; +@@ -118,15 +142,15 @@ bool CPDF_Dest::GetXYZ(bool* pHasX, + return true; + } + +-unsigned long CPDF_Dest::GetNumParams() const { ++size_t CPDF_Dest::GetNumParams() const { + if (!m_pArray || m_pArray->size() < 2) + return 0; + +- unsigned long maxParamsForFitType = g_sZoomModeMaxParamCount[GetZoomMode()]; +- unsigned long numParamsInArray = m_pArray->size() - 2; ++ size_t maxParamsForFitType = kZoomModeMaxParamCount[GetZoomMode()]; ++ size_t numParamsInArray = m_pArray->size() - 2; + return std::min(maxParamsForFitType, numParamsInArray); + } + +-float CPDF_Dest::GetParam(int index) const { +- return m_pArray ? m_pArray->GetNumberAt(2 + index) : 0; ++float CPDF_Dest::GetParam(size_t index) const { ++ return m_pArray ? m_pArray->GetFloatAt(2 + index) : 0; + } +diff --git a/core/fpdfdoc/cpdf_dest.h b/core/fpdfdoc/cpdf_dest.h +index c215634fe..c32c7da5f 100644 +--- a/core/fpdfdoc/cpdf_dest.h ++++ b/core/fpdfdoc/cpdf_dest.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,29 +7,34 @@ + #ifndef CORE_FPDFDOC_CPDF_DEST_H_ + #define CORE_FPDFDOC_CPDF_DEST_H_ + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include ++ ++#include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fxcrt/retain_ptr.h" + + class CPDF_Document; +-class CPDF_Array; ++class CPDF_Object; + + class CPDF_Dest { + public: +- CPDF_Dest(); +- explicit CPDF_Dest(const CPDF_Array* pArray); ++ explicit CPDF_Dest(RetainPtr pArray); + CPDF_Dest(const CPDF_Dest& that); + ~CPDF_Dest(); + ++ // Use when |pDest| is an object of an unknown type. Can pass in nullptr. ++ static CPDF_Dest Create(CPDF_Document* pDoc, ++ RetainPtr pDest); ++ + const CPDF_Array* GetArray() const { return m_pArray.Get(); } ++ + int GetDestPageIndex(CPDF_Document* pDoc) const; ++ std::vector GetScrollPositionArray() const; + + // Returns the zoom mode, as one of the PDFDEST_VIEW_* values in fpdf_doc.h. + int GetZoomMode() const; + +- unsigned long GetNumParams() const; +- float GetParam(int index) const; +- ++ size_t GetNumParams() const; ++ float GetParam(size_t index) const; + bool GetXYZ(bool* pHasX, + bool* pHasY, + bool* pHasZoom, +diff --git a/core/fpdfdoc/cpdf_dest_unittest.cpp b/core/fpdfdoc/cpdf_dest_unittest.cpp +index 07e46b334..ae2d9796f 100644 +--- a/core/fpdfdoc/cpdf_dest_unittest.cpp ++++ b/core/fpdfdoc/cpdf_dest_unittest.cpp +@@ -1,15 +1,16 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfdoc/cpdf_dest.h" + ++#include ++ + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_null.h" + #include "core/fpdfapi/parser/cpdf_number.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + + TEST(cpdf_dest, GetXYZ) { + bool hasX; +@@ -21,23 +22,23 @@ TEST(cpdf_dest, GetXYZ) { + + // |array| must outlive |dest|. + auto array = pdfium::MakeRetain(); +- array->AddNew(0); // Page Index. +- array->AddNew("XYZ"); +- array->AddNew(4); // X ++ array->AppendNew(0); // Page Index. ++ array->AppendNew("XYZ"); ++ array->AppendNew(4); // X + { +- auto dest = pdfium::MakeUnique(); +- EXPECT_FALSE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom)); ++ CPDF_Dest dest(nullptr); ++ EXPECT_FALSE(dest.GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom)); + } + { + // Not enough entries. +- auto dest = pdfium::MakeUnique(array.Get()); +- EXPECT_FALSE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom)); ++ CPDF_Dest dest(array); ++ EXPECT_FALSE(dest.GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom)); + } +- array->AddNew(5); // Y +- array->AddNew(6); // Zoom. ++ array->AppendNew(5); // Y ++ array->AppendNew(6); // Zoom. + { +- auto dest = pdfium::MakeUnique(array.Get()); +- EXPECT_TRUE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom)); ++ CPDF_Dest dest(array); ++ EXPECT_TRUE(dest.GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom)); + EXPECT_TRUE(hasX); + EXPECT_TRUE(hasY); + EXPECT_TRUE(hasZoom); +@@ -48,8 +49,8 @@ TEST(cpdf_dest, GetXYZ) { + // Set zoom to 0. + array->SetNewAt(4, 0); + { +- auto dest = pdfium::MakeUnique(array.Get()); +- EXPECT_TRUE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom)); ++ CPDF_Dest dest(array); ++ EXPECT_TRUE(dest.GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom)); + EXPECT_FALSE(hasZoom); + } + // Set values to null. +@@ -57,8 +58,8 @@ TEST(cpdf_dest, GetXYZ) { + array->SetNewAt(3); + array->SetNewAt(4); + { +- auto dest = pdfium::MakeUnique(array.Get()); +- EXPECT_TRUE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom)); ++ CPDF_Dest dest(array); ++ EXPECT_TRUE(dest.GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom)); + EXPECT_FALSE(hasX); + EXPECT_FALSE(hasY); + EXPECT_FALSE(hasZoom); +diff --git a/core/fpdfdoc/cpdf_filespec.cpp b/core/fpdfdoc/cpdf_filespec.cpp +index 506e17674..2e982b2f9 100644 +--- a/core/fpdfdoc/cpdf_filespec.cpp ++++ b/core/fpdfdoc/cpdf_filespec.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,8 @@ + + #include "core/fpdfdoc/cpdf_filespec.h" + +-#include ++#include ++#include + + #include "build/build_config.h" + #include "constants/stream_dict_common.h" +@@ -17,15 +18,17 @@ + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" + #include "core/fxcrt/fx_system.h" ++#include "third_party/base/check.h" ++#include "third_party/base/notreached.h" + + namespace { + +-#if defined(OS_MACOSX) || defined(OS_WIN) ++#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) + WideString ChangeSlashToPlatform(const wchar_t* str) { + WideString result; + while (*str) { + if (*str == '/') { +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + result += L':'; + #else + result += L'\\'; +@@ -50,30 +53,26 @@ WideString ChangeSlashToPDF(const wchar_t* str) { + } + return result; + } +-#endif // defined(OS_MACOSX) || defined(OS_WIN) ++#endif // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) + + } // namespace + +-CPDF_FileSpec::CPDF_FileSpec(const CPDF_Object* pObj) : m_pObj(pObj) { +- ASSERT(m_pObj); ++CPDF_FileSpec::CPDF_FileSpec(RetainPtr pObj) ++ : m_pObj(std::move(pObj)) { ++ DCHECK(m_pObj); + } + +-CPDF_FileSpec::CPDF_FileSpec(CPDF_Object* pObj) +- : m_pObj(pObj), m_pWritableObj(pObj) { +- ASSERT(m_pObj); +-} +- +-CPDF_FileSpec::~CPDF_FileSpec() {} ++CPDF_FileSpec::~CPDF_FileSpec() = default; + + WideString CPDF_FileSpec::DecodeFileName(const WideString& filepath) { + if (filepath.GetLength() <= 1) + return WideString(); + +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + if (filepath.First(sizeof("/Mac") - 1) == WideStringView(L"/Mac")) + return ChangeSlashToPlatform(filepath.c_str() + 1); + return ChangeSlashToPlatform(filepath.c_str()); +-#elif defined(OS_WIN) ++#elif BUILDFLAG(IS_WIN) + + if (filepath[0] != L'/') + return ChangeSlashToPlatform(filepath.c_str()); +@@ -98,21 +97,23 @@ WideString CPDF_FileSpec::DecodeFileName(const WideString& filepath) { + WideString CPDF_FileSpec::GetFileName() const { + WideString csFileName; + if (const CPDF_Dictionary* pDict = m_pObj->AsDictionary()) { +- const CPDF_String* pUF = ToString(pDict->GetDirectObjectFor("UF")); ++ RetainPtr pUF = ++ ToString(pDict->GetDirectObjectFor("UF")); + if (pUF) + csFileName = pUF->GetUnicodeText(); + if (csFileName.IsEmpty()) { +- const CPDF_String* pK = ++ RetainPtr pK = + ToString(pDict->GetDirectObjectFor(pdfium::stream::kF)); + if (pK) + csFileName = WideString::FromDefANSI(pK->GetString().AsStringView()); + } +- if (pDict->GetStringFor("FS") == "URL") ++ if (pDict->GetByteStringFor("FS") == "URL") + return csFileName; + + if (csFileName.IsEmpty()) { + for (const auto* key : {"DOS", "Mac", "Unix"}) { +- const CPDF_String* pValue = ToString(pDict->GetDirectObjectFor(key)); ++ RetainPtr pValue = ++ ToString(pDict->GetDirectObjectFor(key)); + if (pValue) { + csFileName = + WideString::FromDefANSI(pValue->GetString().AsStringView()); +@@ -126,24 +127,24 @@ WideString CPDF_FileSpec::GetFileName() const { + return DecodeFileName(csFileName); + } + +-const CPDF_Stream* CPDF_FileSpec::GetFileStream() const { ++RetainPtr CPDF_FileSpec::GetFileStream() const { + const CPDF_Dictionary* pDict = m_pObj->AsDictionary(); + if (!pDict) + return nullptr; + + // Get the embedded files dictionary. +- const CPDF_Dictionary* pFiles = pDict->GetDictFor("EF"); ++ RetainPtr pFiles = pDict->GetDictFor("EF"); + if (!pFiles) + return nullptr; + + // List of keys to check for the file specification string. + // Follows the same precedence order as GetFileName(). + static constexpr const char* kKeys[] = {"UF", "F", "DOS", "Mac", "Unix"}; +- size_t end = pDict->GetStringFor("FS") == "URL" ? 2 : FX_ArraySize(kKeys); ++ size_t end = pDict->GetByteStringFor("FS") == "URL" ? 2 : std::size(kKeys); + for (size_t i = 0; i < end; ++i) { + ByteString key = kKeys[i]; + if (!pDict->GetUnicodeTextFor(key).IsEmpty()) { +- const CPDF_Stream* pStream = pFiles->GetStreamFor(key); ++ RetainPtr pStream = pFiles->GetStreamFor(key); + if (pStream) + return pStream; + } +@@ -151,30 +152,25 @@ const CPDF_Stream* CPDF_FileSpec::GetFileStream() const { + return nullptr; + } + +-CPDF_Stream* CPDF_FileSpec::GetFileStream() { +- return const_cast( +- static_cast(this)->GetFileStream()); +-} +- +-const CPDF_Dictionary* CPDF_FileSpec::GetParamsDict() const { +- const CPDF_Stream* pStream = GetFileStream(); ++RetainPtr CPDF_FileSpec::GetParamsDict() const { ++ RetainPtr pStream = GetFileStream(); + if (!pStream) + return nullptr; + +- const CPDF_Dictionary* pDict = pStream->GetDict(); ++ RetainPtr pDict = pStream->GetDict(); + return pDict ? pDict->GetDictFor("Params") : nullptr; + } + +-CPDF_Dictionary* CPDF_FileSpec::GetParamsDict() { +- return const_cast( +- static_cast(this)->GetParamsDict()); ++RetainPtr CPDF_FileSpec::GetMutableParamsDict() { ++ return pdfium::WrapRetain( ++ const_cast(GetParamsDict().Get())); + } + + WideString CPDF_FileSpec::EncodeFileName(const WideString& filepath) { + if (filepath.GetLength() <= 1) + return WideString(); + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + if (filepath[1] == L':') { + WideString result(L'/'); + result += filepath[0]; +@@ -190,7 +186,7 @@ WideString CPDF_FileSpec::EncodeFileName(const WideString& filepath) { + if (filepath[0] == L'\\') + return L'/' + ChangeSlashToPDF(filepath.c_str()); + return ChangeSlashToPDF(filepath.c_str()); +-#elif defined(OS_MACOSX) ++#elif BUILDFLAG(IS_APPLE) + if (filepath.First(sizeof("Mac") - 1).EqualsASCII("Mac")) + return L'/' + ChangeSlashToPDF(filepath.c_str()); + return ChangeSlashToPDF(filepath.c_str()); +@@ -198,18 +194,3 @@ WideString CPDF_FileSpec::EncodeFileName(const WideString& filepath) { + return WideString(filepath); + #endif + } +- +-void CPDF_FileSpec::SetFileName(const WideString& wsFileName) { +- if (!m_pWritableObj) { +- NOTREACHED(); +- return; +- } +- +- WideString wsStr = EncodeFileName(wsFileName); +- if (m_pObj->IsString()) { +- m_pWritableObj->SetString(wsStr.ToDefANSI()); +- } else if (CPDF_Dictionary* pDict = m_pWritableObj->AsDictionary()) { +- pDict->SetNewFor(pdfium::stream::kF, wsStr.ToDefANSI(), false); +- pDict->SetNewFor("UF", wsStr); +- } +-} +diff --git a/core/fpdfdoc/cpdf_filespec.h b/core/fpdfdoc/cpdf_filespec.h +index 820b948d3..d94381179 100644 +--- a/core/fpdfdoc/cpdf_filespec.h ++++ b/core/fpdfdoc/cpdf_filespec.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,10 @@ + #ifndef CORE_FPDFDOC_CPDF_FILESPEC_H_ + #define CORE_FPDFDOC_CPDF_FILESPEC_H_ + +-#include "core/fxcrt/fx_string.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/string_pool_template.h" + #include "core/fxcrt/weak_ptr.h" ++#include "core/fxcrt/widestring.h" + + class CPDF_Dictionary; + class CPDF_Object; +@@ -18,8 +18,7 @@ class CPDF_Stream; + + class CPDF_FileSpec { + public: +- explicit CPDF_FileSpec(const CPDF_Object* pObj); +- explicit CPDF_FileSpec(CPDF_Object* pObj); ++ explicit CPDF_FileSpec(RetainPtr pObj); + ~CPDF_FileSpec(); + + // Convert a platform dependent file name into pdf format. +@@ -28,20 +27,13 @@ class CPDF_FileSpec { + // Convert a pdf file name into platform dependent format. + static WideString DecodeFileName(const WideString& filepath); + +- const CPDF_Object* GetObj() const { return m_pObj.Get(); } +- CPDF_Object* GetObj() { return m_pWritableObj.Get(); } + WideString GetFileName() const; +- const CPDF_Stream* GetFileStream() const; +- CPDF_Stream* GetFileStream(); +- const CPDF_Dictionary* GetParamsDict() const; +- CPDF_Dictionary* GetParamsDict(); +- +- // Set this file spec to refer to a file name (not a url). +- void SetFileName(const WideString& wsFileName); ++ RetainPtr GetFileStream() const; ++ RetainPtr GetParamsDict() const; ++ RetainPtr GetMutableParamsDict(); + + private: + RetainPtr const m_pObj; +- RetainPtr const m_pWritableObj; + }; + + #endif // CORE_FPDFDOC_CPDF_FILESPEC_H_ +diff --git a/core/fpdfdoc/cpdf_filespec_unittest.cpp b/core/fpdfdoc/cpdf_filespec_unittest.cpp +index a075c5ebc..7c517a968 100644 +--- a/core/fpdfdoc/cpdf_filespec_unittest.cpp ++++ b/core/fpdfdoc/cpdf_filespec_unittest.cpp +@@ -1,7 +1,9 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include "core/fpdfdoc/cpdf_filespec.h" ++ + #include + #include + #include +@@ -12,11 +14,9 @@ + #include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_string.h" +-#include "core/fpdfdoc/cpdf_filespec.h" +-#include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/data_vector.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/test_support.h" +-#include "third_party/base/ptr_util.h" + + TEST(cpdf_filespec, EncodeDecodeFileName) { + static const std::vector test_data = { +@@ -24,7 +24,7 @@ TEST(cpdf_filespec, EncodeDecodeFileName) { + {L"", L""}, + // only file name. + {L"test.pdf", L"test.pdf"}, +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + // With drive identifier. + {L"r:\\pdfdocs\\spec.pdf", L"/r/pdfdocs/spec.pdf"}, + // Relative path. +@@ -35,7 +35,7 @@ TEST(cpdf_filespec, EncodeDecodeFileName) { + {L"\\\\pdfdocs\\spec.pdf", L"/pdfdocs/spec.pdf"}, + // Network resource name. It is not supported yet. + // {L"pclib/eng:\\pdfdocs\\spec.pdf", L"/pclib/eng/pdfdocs/spec.pdf"}, +-#elif defined(OS_MACOSX) ++#elif BUILDFLAG(IS_APPLE) + // Absolute path with colon separator. + {L"Mac HD:PDFDocs:spec.pdf", L"/Mac HD/PDFDocs/spec.pdf"}, + // Relative path with colon separator. +@@ -62,10 +62,10 @@ TEST(cpdf_filespec, GetFileName) { + { + // String object. + static const pdfium::NullTermWstrFuncTestData test_data = { +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + L"/C/docs/test.pdf", + L"C:\\docs\\test.pdf" +-#elif defined(OS_MACOSX) ++#elif BUILDFLAG(IS_APPLE) + L"/Mac HD/docs/test.pdf", + L"Mac HD:docs:test.pdf" + #else +@@ -74,19 +74,19 @@ TEST(cpdf_filespec, GetFileName) { + #endif + }; + auto str_obj = pdfium::MakeRetain(nullptr, test_data.input); +- CPDF_FileSpec file_spec(str_obj.Get()); ++ CPDF_FileSpec file_spec(str_obj); + EXPECT_STREQ(test_data.expected, file_spec.GetFileName().c_str()); + } + { + // Dictionary object. + static const pdfium::NullTermWstrFuncTestData test_data[] = { +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + {L"/C/docs/test.pdf", L"C:\\docs\\test.pdf"}, + {L"/D/docs/test.pdf", L"D:\\docs\\test.pdf"}, + {L"/E/docs/test.pdf", L"E:\\docs\\test.pdf"}, + {L"/F/docs/test.pdf", L"F:\\docs\\test.pdf"}, + {L"/G/docs/test.pdf", L"G:\\docs\\test.pdf"}, +-#elif defined(OS_MACOSX) ++#elif BUILDFLAG(IS_APPLE) + {L"/Mac HD/docs1/test.pdf", L"Mac HD:docs1:test.pdf"}, + {L"/Mac HD/docs2/test.pdf", L"Mac HD:docs2:test.pdf"}, + {L"/Mac HD/docs3/test.pdf", L"Mac HD:docs3:test.pdf"}, +@@ -102,12 +102,11 @@ TEST(cpdf_filespec, GetFileName) { + }; + // Keyword fields in reverse order of precedence to retrieve the file name. + const char* const keywords[] = {"Unix", "Mac", "DOS", "F", "UF"}; +- static_assert(FX_ArraySize(test_data) == FX_ArraySize(keywords), +- "size mismatch"); ++ static_assert(std::size(test_data) == std::size(keywords), "size mismatch"); + auto dict_obj = pdfium::MakeRetain(); +- CPDF_FileSpec file_spec(dict_obj.Get()); ++ CPDF_FileSpec file_spec(dict_obj); + EXPECT_TRUE(file_spec.GetFileName().IsEmpty()); +- for (size_t i = 0; i < FX_ArraySize(keywords); ++i) { ++ for (size_t i = 0; i < std::size(keywords); ++i) { + dict_obj->SetNewFor(keywords[i], test_data[i].input); + EXPECT_STREQ(test_data[i].expected, file_spec.GetFileName().c_str()); + } +@@ -120,13 +119,13 @@ TEST(cpdf_filespec, GetFileName) { + { + // Invalid object. + auto name_obj = pdfium::MakeRetain(nullptr, "test.pdf"); +- CPDF_FileSpec file_spec(name_obj.Get()); ++ CPDF_FileSpec file_spec(name_obj); + EXPECT_TRUE(file_spec.GetFileName().IsEmpty()); + } + { + // Invalid CPDF_Name objects in dictionary. See https://crbug.com/959183 + auto dict_obj = pdfium::MakeRetain(); +- CPDF_FileSpec file_spec(dict_obj.Get()); ++ CPDF_FileSpec file_spec(dict_obj); + for (const char* key : {"Unix", "Mac", "DOS", "F", "UF"}) { + dict_obj->SetNewFor(key, "http://evil.org"); + EXPECT_TRUE(file_spec.GetFileName().IsEmpty()); +@@ -136,84 +135,49 @@ TEST(cpdf_filespec, GetFileName) { + } + } + +-TEST(cpdf_filespec, SetFileName) { +- static const pdfium::NullTermWstrFuncTestData test_data = { +-#if defined(OS_WIN) +- L"C:\\docs\\test.pdf", +- L"/C/docs/test.pdf" +-#elif defined(OS_MACOSX) +- L"Mac HD:docs:test.pdf", +- L"/Mac HD/docs/test.pdf" +-#else +- L"/docs/test.pdf", +- L"/docs/test.pdf" +-#endif +- }; +- // String object. +- auto str_obj = pdfium::MakeRetain(nullptr, L"babababa"); +- CPDF_FileSpec file_spec1(str_obj.Get()); +- file_spec1.SetFileName(test_data.input); +- // Check internal object value. +- EXPECT_STREQ(test_data.expected, str_obj->GetUnicodeText().c_str()); +- // Check we can get the file name back. +- EXPECT_STREQ(test_data.input, file_spec1.GetFileName().c_str()); +- +- // Dictionary object. +- auto dict_obj = pdfium::MakeRetain(); +- CPDF_FileSpec file_spec2(dict_obj.Get()); +- file_spec2.SetFileName(test_data.input); +- // Check internal object value. +- EXPECT_STREQ(test_data.expected, dict_obj->GetUnicodeTextFor("F").c_str()); +- EXPECT_STREQ(test_data.expected, dict_obj->GetUnicodeTextFor("UF").c_str()); +- // Check we can get the file name back. +- EXPECT_STREQ(test_data.input, file_spec2.GetFileName().c_str()); +-} +- + TEST(cpdf_filespec, GetFileStream) { + { + // Invalid object. + auto name_obj = pdfium::MakeRetain(nullptr, "test.pdf"); +- CPDF_FileSpec file_spec(name_obj.Get()); ++ CPDF_FileSpec file_spec(name_obj); + EXPECT_FALSE(file_spec.GetFileStream()); + } + { + // Dictionary object missing its embedded files dictionary. + auto dict_obj = pdfium::MakeRetain(); +- CPDF_FileSpec file_spec(dict_obj.Get()); ++ CPDF_FileSpec file_spec(dict_obj); + EXPECT_FALSE(file_spec.GetFileStream()); + } + { + // Dictionary object with an empty embedded files dictionary. + auto dict_obj = pdfium::MakeRetain(); + dict_obj->SetNewFor("EF"); +- CPDF_FileSpec file_spec(dict_obj.Get()); ++ CPDF_FileSpec file_spec(dict_obj); + EXPECT_FALSE(file_spec.GetFileStream()); + } + { + // Dictionary object with a non-empty embedded files dictionary. + auto dict_obj = pdfium::MakeRetain(); + dict_obj->SetNewFor("EF"); +- CPDF_FileSpec file_spec(dict_obj.Get()); ++ CPDF_FileSpec file_spec(dict_obj); + + const wchar_t file_name[] = L"test.pdf"; + const char* const keys[] = {"Unix", "Mac", "DOS", "F", "UF"}; + const char* const streams[] = {"test1", "test2", "test3", "test4", "test5"}; +- static_assert(FX_ArraySize(keys) == FX_ArraySize(streams), "size mismatch"); +- CPDF_Dictionary* file_dict = +- file_spec.GetObj()->AsDictionary()->GetDictFor("EF"); ++ static_assert(std::size(keys) == std::size(streams), "size mismatch"); ++ RetainPtr file_dict = dict_obj->GetMutableDictFor("EF"); + + // Keys in reverse order of precedence to retrieve the file content stream. +- for (size_t i = 0; i < FX_ArraySize(keys); ++i) { ++ for (size_t i = 0; i < std::size(keys); ++i) { + // Set the file name. + dict_obj->SetNewFor(keys[i], file_name); + + // Set the file stream. + auto pDict = pdfium::MakeRetain(); + size_t buf_len = strlen(streams[i]) + 1; +- std::unique_ptr buf(FX_Alloc(uint8_t, buf_len)); +- memcpy(buf.get(), streams[i], buf_len); +- file_dict->SetNewFor(keys[i], std::move(buf), buf_len, +- std::move(pDict)); ++ file_dict->SetNewFor( ++ keys[i], DataVector(streams[i], streams[i] + buf_len), ++ std::move(pDict)); + + // Check that the file content stream is as expected. + EXPECT_STREQ( +@@ -232,7 +196,7 @@ TEST(cpdf_filespec, GetParamsDict) { + { + // Invalid object. + auto name_obj = pdfium::MakeRetain(nullptr, "test.pdf"); +- CPDF_FileSpec file_spec(name_obj.Get()); ++ CPDF_FileSpec file_spec(name_obj); + EXPECT_FALSE(file_spec.GetParamsDict()); + } + { +@@ -240,26 +204,26 @@ TEST(cpdf_filespec, GetParamsDict) { + auto dict_obj = pdfium::MakeRetain(); + dict_obj->SetNewFor("EF"); + dict_obj->SetNewFor("UF", L"test.pdf"); +- CPDF_FileSpec file_spec(dict_obj.Get()); ++ CPDF_FileSpec file_spec(dict_obj); + EXPECT_FALSE(file_spec.GetParamsDict()); + + // Add a file stream to the embedded files dictionary. +- CPDF_Dictionary* file_dict = +- file_spec.GetObj()->AsDictionary()->GetDictFor("EF"); ++ RetainPtr file_dict = dict_obj->GetMutableDictFor("EF"); + auto pDict = pdfium::MakeRetain(); +- std::unique_ptr buf(FX_Alloc(uint8_t, 6)); +- memcpy(buf.get(), "hello", 6); +- file_dict->SetNewFor("UF", std::move(buf), 6, +- std::move(pDict)); ++ static constexpr char kHello[] = "hello"; ++ file_dict->SetNewFor( ++ "UF", DataVector(std::begin(kHello), std::end(kHello)), ++ std::move(pDict)); + + // Add a params dictionary to the file stream. +- CPDF_Stream* stream = file_dict->GetStreamFor("UF"); +- CPDF_Dictionary* stream_dict = stream->GetDict(); ++ RetainPtr stream = file_dict->GetMutableStreamFor("UF"); ++ RetainPtr stream_dict = stream->GetMutableDict(); + stream_dict->SetNewFor("Params"); + EXPECT_TRUE(file_spec.GetParamsDict()); + + // Add a parameter to the params dictionary. +- CPDF_Dictionary* params_dict = stream_dict->GetDictFor("Params"); ++ RetainPtr params_dict = ++ stream_dict->GetMutableDictFor("Params"); + params_dict->SetNewFor("Size", 6); + EXPECT_EQ(6, file_spec.GetParamsDict()->GetIntegerFor("Size")); + } +diff --git a/core/fpdfdoc/cpdf_formcontrol.cpp b/core/fpdfdoc/cpdf_formcontrol.cpp +index b98afba2b..31bbdf374 100644 +--- a/core/fpdfdoc/cpdf_formcontrol.cpp ++++ b/core/fpdfdoc/cpdf_formcontrol.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,32 +6,45 @@ + + #include "core/fpdfdoc/cpdf_formcontrol.h" + +-#include ++#include ++#include + ++#include "constants/form_fields.h" + #include "core/fpdfapi/font/cpdf_font.h" + #include "core/fpdfapi/page/cpdf_docpagedata.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" +-#include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fpdfdoc/cpdf_interactiveform.h" ++#include "third_party/base/check.h" + + namespace { + +-const char* const g_sHighlightingMode[] = { +- // Must match order of HighlightingMode enum. +- "N", "I", "O", "P", "T"}; ++constexpr char kHighlightModes[] = {'N', 'I', 'O', 'P', 'T'}; ++ ++// Order of |kHighlightModes| must match order of HighlightingMode enum. ++static_assert(kHighlightModes[CPDF_FormControl::kNone] == 'N', ++ "HighlightingMode mismatch"); ++static_assert(kHighlightModes[CPDF_FormControl::kInvert] == 'I', ++ "HighlightingMode mismatch"); ++static_assert(kHighlightModes[CPDF_FormControl::kOutline] == 'O', ++ "HighlightingMode mismatch"); ++static_assert(kHighlightModes[CPDF_FormControl::kPush] == 'P', ++ "HighlightingMode mismatch"); ++static_assert(kHighlightModes[CPDF_FormControl::kToggle] == 'T', ++ "HighlightingMode mismatch"); + + } // namespace + + CPDF_FormControl::CPDF_FormControl(CPDF_FormField* pField, +- CPDF_Dictionary* pWidgetDict) +- : m_pField(pField), +- m_pWidgetDict(pWidgetDict), +- m_pForm(m_pField->GetForm()) {} ++ RetainPtr pWidgetDict, ++ CPDF_InteractiveForm* pForm) ++ : m_pField(pField), m_pWidgetDict(std::move(pWidgetDict)), m_pForm(pForm) { ++ DCHECK(m_pWidgetDict); ++} + + CPDF_FormControl::~CPDF_FormControl() = default; + +@@ -40,16 +53,15 @@ CFX_FloatRect CPDF_FormControl::GetRect() const { + } + + ByteString CPDF_FormControl::GetOnStateName() const { +- ASSERT(GetType() == CPDF_FormField::kCheckBox || ++ DCHECK(GetType() == CPDF_FormField::kCheckBox || + GetType() == CPDF_FormField::kRadioButton); +- ByteString csOn; +- CPDF_Dictionary* pAP = m_pWidgetDict->GetDictFor("AP"); ++ RetainPtr pAP = m_pWidgetDict->GetDictFor("AP"); + if (!pAP) +- return csOn; ++ return ByteString(); + +- CPDF_Dictionary* pN = pAP->GetDictFor("N"); ++ RetainPtr pN = pAP->GetDictFor("N"); + if (!pN) +- return csOn; ++ return ByteString(); + + CPDF_DictionaryLocker locker(pN); + for (const auto& it : locker) { +@@ -60,41 +72,40 @@ ByteString CPDF_FormControl::GetOnStateName() const { + } + + ByteString CPDF_FormControl::GetCheckedAPState() const { +- ASSERT(GetType() == CPDF_FormField::kCheckBox || ++ DCHECK(GetType() == CPDF_FormField::kCheckBox || + GetType() == CPDF_FormField::kRadioButton); + ByteString csOn = GetOnStateName(); +- if (ToArray(CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "Opt"))) +- csOn = ByteString::Format("%d", m_pField->GetControlIndex(this)); ++ if (ToArray(m_pField->GetFieldAttr("Opt"))) ++ csOn = ByteString::FormatInteger(m_pField->GetControlIndex(this)); + if (csOn.IsEmpty()) + csOn = "Yes"; + return csOn; + } + + WideString CPDF_FormControl::GetExportValue() const { +- ASSERT(GetType() == CPDF_FormField::kCheckBox || ++ DCHECK(GetType() == CPDF_FormField::kCheckBox || + GetType() == CPDF_FormField::kRadioButton); + ByteString csOn = GetOnStateName(); +- CPDF_Array* pArray = +- ToArray(CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "Opt")); ++ RetainPtr pArray = ToArray(m_pField->GetFieldAttr("Opt")); + if (pArray) +- csOn = pArray->GetStringAt(m_pField->GetControlIndex(this)); ++ csOn = pArray->GetByteStringAt(m_pField->GetControlIndex(this)); + if (csOn.IsEmpty()) + csOn = "Yes"; + return PDF_DecodeText(csOn.raw_span()); + } + + bool CPDF_FormControl::IsChecked() const { +- ASSERT(GetType() == CPDF_FormField::kCheckBox || ++ DCHECK(GetType() == CPDF_FormField::kCheckBox || + GetType() == CPDF_FormField::kRadioButton); + ByteString csOn = GetOnStateName(); +- ByteString csAS = m_pWidgetDict->GetStringFor("AS"); ++ ByteString csAS = m_pWidgetDict->GetByteStringFor("AS"); + return csAS == csOn; + } + + bool CPDF_FormControl::IsDefaultChecked() const { +- ASSERT(GetType() == CPDF_FormField::kCheckBox || ++ DCHECK(GetType() == CPDF_FormField::kCheckBox || + GetType() == CPDF_FormField::kRadioButton); +- CPDF_Object* pDV = CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "DV"); ++ RetainPtr pDV = m_pField->GetFieldAttr("DV"); + if (!pDV) + return false; + +@@ -104,9 +115,9 @@ bool CPDF_FormControl::IsDefaultChecked() const { + } + + void CPDF_FormControl::CheckControl(bool bChecked) { +- ASSERT(GetType() == CPDF_FormField::kCheckBox || ++ DCHECK(GetType() == CPDF_FormField::kCheckBox || + GetType() == CPDF_FormField::kRadioButton); +- ByteString csOldAS = m_pWidgetDict->GetStringFor("AS", "Off"); ++ ByteString csOldAS = m_pWidgetDict->GetByteStringFor("AS", "Off"); + ByteString csAS = "Off"; + if (bChecked) + csAS = GetOnStateName(); +@@ -117,20 +128,17 @@ void CPDF_FormControl::CheckControl(bool bChecked) { + + CPDF_FormControl::HighlightingMode CPDF_FormControl::GetHighlightingMode() + const { +- if (!m_pWidgetDict) +- return Invert; +- +- ByteString csH = m_pWidgetDict->GetStringFor("H", "I"); +- for (size_t i = 0; i < FX_ArraySize(g_sHighlightingMode); ++i) { +- if (csH == g_sHighlightingMode[i]) ++ ByteString csH = m_pWidgetDict->GetByteStringFor("H", "I"); ++ for (size_t i = 0; i < std::size(kHighlightModes); ++i) { ++ // TODO(tsepez): disambiguate string ctors. ++ if (csH == ByteStringView(kHighlightModes[i])) + return static_cast(i); + } +- return Invert; ++ return kInvert; + } + + CPDF_ApSettings CPDF_FormControl::GetMK() const { +- return CPDF_ApSettings(m_pWidgetDict ? m_pWidgetDict->GetDictFor("MK") +- : nullptr); ++ return CPDF_ApSettings(m_pWidgetDict->GetMutableDictFor("MK")); + } + + bool CPDF_FormControl::HasMKEntry(const ByteString& csEntry) const { +@@ -141,25 +149,25 @@ int CPDF_FormControl::GetRotation() const { + return GetMK().GetRotation(); + } + +-FX_ARGB CPDF_FormControl::GetColor(int& iColorType, const ByteString& csEntry) { +- return GetMK().GetColor(iColorType, csEntry); ++CFX_Color::TypeAndARGB CPDF_FormControl::GetColorARGB( ++ const ByteString& csEntry) { ++ return GetMK().GetColorARGB(csEntry); + } + +-float CPDF_FormControl::GetOriginalColor(int index, const ByteString& csEntry) { +- return GetMK().GetOriginalColor(index, csEntry); ++float CPDF_FormControl::GetOriginalColorComponent(int index, ++ const ByteString& csEntry) { ++ return GetMK().GetOriginalColorComponent(index, csEntry); + } + +-void CPDF_FormControl::GetOriginalColor(int& iColorType, +- float fc[4], +- const ByteString& csEntry) { +- GetMK().GetOriginalColor(iColorType, fc, csEntry); ++CFX_Color CPDF_FormControl::GetOriginalColor(const ByteString& csEntry) { ++ return GetMK().GetOriginalColor(csEntry); + } + + WideString CPDF_FormControl::GetCaption(const ByteString& csEntry) const { + return GetMK().GetCaption(csEntry); + } + +-CPDF_Stream* CPDF_FormControl::GetIcon(const ByteString& csEntry) { ++RetainPtr CPDF_FormControl::GetIcon(const ByteString& csEntry) { + return GetMK().GetIcon(csEntry); + } + +@@ -171,43 +179,23 @@ int CPDF_FormControl::GetTextPosition() const { + return GetMK().GetTextPosition(); + } + +-CPDF_Action CPDF_FormControl::GetAction() const { +- if (!m_pWidgetDict) +- return CPDF_Action(nullptr); +- +- if (m_pWidgetDict->KeyExist("A")) +- return CPDF_Action(m_pWidgetDict->GetDictFor("A")); +- +- CPDF_Object* pObj = CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "A"); +- return CPDF_Action(pObj ? pObj->GetDict() : nullptr); +-} +- +-CPDF_AAction CPDF_FormControl::GetAdditionalAction() const { +- if (!m_pWidgetDict) +- return CPDF_AAction(nullptr); +- +- if (m_pWidgetDict->KeyExist("AA")) +- return CPDF_AAction(m_pWidgetDict->GetDictFor("AA")); +- return m_pField->GetAdditionalAction(); +-} +- + CPDF_DefaultAppearance CPDF_FormControl::GetDefaultAppearance() const { +- if (!m_pWidgetDict) +- return CPDF_DefaultAppearance(); +- +- if (m_pWidgetDict->KeyExist("DA")) +- return CPDF_DefaultAppearance(m_pWidgetDict->GetStringFor("DA")); ++ if (m_pWidgetDict->KeyExist(pdfium::form_fields::kDA)) { ++ return CPDF_DefaultAppearance( ++ m_pWidgetDict->GetByteStringFor(pdfium::form_fields::kDA)); ++ } ++ RetainPtr pObj = ++ m_pField->GetFieldAttr(pdfium::form_fields::kDA); ++ if (pObj) ++ return CPDF_DefaultAppearance(pObj->GetString()); + +- CPDF_Object* pObj = CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "DA"); +- if (!pObj) +- return m_pForm->GetDefaultAppearance(); +- return CPDF_DefaultAppearance(pObj->GetString()); ++ return m_pForm->GetDefaultAppearance(); + } + +-Optional CPDF_FormControl::GetDefaultControlFontName() const { ++absl::optional CPDF_FormControl::GetDefaultControlFontName() const { + RetainPtr pFont = GetDefaultControlFont(); + if (!pFont) +- return {}; ++ return absl::nullopt; + + return WideString::FromDefANSI(pFont->GetBaseFontName().AsStringView()); + } +@@ -215,53 +203,55 @@ Optional CPDF_FormControl::GetDefaultControlFontName() const { + RetainPtr CPDF_FormControl::GetDefaultControlFont() const { + float fFontSize; + CPDF_DefaultAppearance cDA = GetDefaultAppearance(); +- Optional csFontNameTag = cDA.GetFont(&fFontSize); +- if (!csFontNameTag || csFontNameTag->IsEmpty()) ++ absl::optional csFontNameTag = cDA.GetFont(&fFontSize); ++ if (!csFontNameTag.has_value() || csFontNameTag->IsEmpty()) + return nullptr; + +- CPDF_Object* pObj = CPDF_FormField::GetFieldAttr(m_pWidgetDict.Get(), "DR"); +- if (CPDF_Dictionary* pDict = ToDictionary(pObj)) { +- CPDF_Dictionary* pFonts = pDict->GetDictFor("Font"); +- if (ValidateFontResourceDict(pFonts)) { +- CPDF_Dictionary* pElement = pFonts->GetDictFor(*csFontNameTag); ++ RetainPtr pDRDict = ToDictionary( ++ CPDF_FormField::GetMutableFieldAttrForDict(m_pWidgetDict.Get(), "DR")); ++ if (pDRDict) { ++ RetainPtr pFonts = pDRDict->GetMutableDictFor("Font"); ++ if (ValidateFontResourceDict(pFonts.Get())) { ++ RetainPtr pElement = ++ pFonts->GetMutableDictFor(csFontNameTag.value()); + if (pElement) { +- auto* pData = CPDF_DocPageData::FromDocument(m_pForm->GetDocument()); +- RetainPtr pFont = pData->GetFont(pElement); ++ RetainPtr pFont = ++ m_pForm->GetFontForElement(std::move(pElement)); + if (pFont) + return pFont; + } + } + } +- RetainPtr pFormFont = m_pForm->GetFormFont(*csFontNameTag); ++ RetainPtr pFormFont = m_pForm->GetFormFont(csFontNameTag.value()); + if (pFormFont) + return pFormFont; + +- CPDF_Dictionary* pPageDict = m_pWidgetDict->GetDictFor("P"); +- CPDF_Dictionary* pDict = +- ToDictionary(CPDF_FormField::GetFieldAttr(pPageDict, "Resources")); ++ RetainPtr pPageDict = m_pWidgetDict->GetMutableDictFor("P"); ++ RetainPtr pDict = ToDictionary( ++ CPDF_FormField::GetMutableFieldAttrForDict(pPageDict.Get(), "Resources")); + if (!pDict) + return nullptr; + +- CPDF_Dictionary* pFonts = pDict->GetDictFor("Font"); +- if (!ValidateFontResourceDict(pFonts)) ++ RetainPtr pFonts = pDict->GetMutableDictFor("Font"); ++ if (!ValidateFontResourceDict(pFonts.Get())) + return nullptr; + +- CPDF_Dictionary* pElement = pFonts->GetDictFor(*csFontNameTag); ++ RetainPtr pElement = ++ pFonts->GetMutableDictFor(csFontNameTag.value()); + if (!pElement) + return nullptr; + +- auto* pDocPageData = CPDF_DocPageData::FromDocument(m_pForm->GetDocument()); +- return pDocPageData->GetFont(pElement); ++ return m_pForm->GetFontForElement(std::move(pElement)); + } + + int CPDF_FormControl::GetControlAlignment() const { +- if (!m_pWidgetDict) +- return 0; +- if (m_pWidgetDict->KeyExist("Q")) +- return m_pWidgetDict->GetIntegerFor("Q", 0); ++ if (m_pWidgetDict->KeyExist(pdfium::form_fields::kQ)) ++ return m_pWidgetDict->GetIntegerFor(pdfium::form_fields::kQ, 0); + +- CPDF_Object* pObj = CPDF_FormField::GetFieldAttr(m_pField->GetDict(), "Q"); ++ RetainPtr pObj = ++ m_pField->GetFieldAttr(pdfium::form_fields::kQ); + if (pObj) + return pObj->GetInteger(); ++ + return m_pForm->GetFormAlignment(); + } +diff --git a/core/fpdfdoc/cpdf_formcontrol.h b/core/fpdfdoc/cpdf_formcontrol.h +index d8256344f..59eec215d 100644 +--- a/core/fpdfdoc/cpdf_formcontrol.h ++++ b/core/fpdfdoc/cpdf_formcontrol.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,7 @@ + #ifndef CORE_FPDFDOC_CPDF_FORMCONTROL_H_ + #define CORE_FPDFDOC_CPDF_FORMCONTROL_H_ + ++#include "constants/appearance.h" + #include "core/fpdfdoc/cpdf_aaction.h" + #include "core/fpdfdoc/cpdf_action.h" + #include "core/fpdfdoc/cpdf_annot.h" +@@ -18,31 +19,32 @@ + #include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/fx_string.h" + #include "core/fxcrt/retain_ptr.h" +-#include "core/fxge/fx_dib.h" +-#include "third_party/base/optional.h" ++#include "core/fxcrt/unowned_ptr.h" ++#include "core/fxge/cfx_color.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CFX_RenderDevice; + class CPDF_Dictionary; + class CPDF_Font; + class CPDF_FormField; + class CPDF_InteractiveForm; +-class CPDF_OCContext; +-class CPDF_RenderOptions; + class CPDF_Stream; + + class CPDF_FormControl { + public: +- enum HighlightingMode { None = 0, Invert, Outline, Push, Toggle }; ++ enum HighlightingMode { kNone = 0, kInvert, kOutline, kPush, kToggle }; + +- CPDF_FormControl(CPDF_FormField* pField, CPDF_Dictionary* pWidgetDict); ++ CPDF_FormControl(CPDF_FormField* pField, ++ RetainPtr pWidgetDict, ++ CPDF_InteractiveForm* pForm); + ~CPDF_FormControl(); + + CPDF_FormField::Type GetType() const { return m_pField->GetType(); } +- const CPDF_InteractiveForm* GetInteractiveForm() const { +- return m_pForm.Get(); ++ CPDF_FormField* GetField() const { return m_pField; } ++ RetainPtr GetWidgetDict() const { ++ return m_pWidgetDict; + } +- CPDF_FormField* GetField() const { return m_pField.Get(); } +- CPDF_Dictionary* GetWidget() const { return m_pWidgetDict.Get(); } + CFX_FloatRect GetRect() const; + + ByteString GetCheckedAPState() const; +@@ -55,43 +57,42 @@ class CPDF_FormControl { + bool HasMKEntry(const ByteString& csEntry) const; + int GetRotation() const; + +- FX_ARGB GetBorderColor(int& iColorType) { return GetColor(iColorType, "BC"); } ++ CFX_Color::TypeAndARGB GetColorARGB(const ByteString& csEntry); ++ float GetOriginalColorComponent(int index, const ByteString& csEntry); + +- float GetOriginalBorderColor(int index) { +- return GetOriginalColor(index, "BC"); ++ CFX_Color GetOriginalBorderColor() { ++ return GetOriginalColor(pdfium::appearance::kBC); + } + +- void GetOriginalBorderColor(int& iColorType, float fc[4]) { +- GetOriginalColor(iColorType, fc, "BC"); ++ CFX_Color GetOriginalBackgroundColor() { ++ return GetOriginalColor(pdfium::appearance::kBG); + } + +- FX_ARGB GetBackgroundColor(int& iColorType) { +- return GetColor(iColorType, "BG"); ++ WideString GetNormalCaption() const { ++ return GetCaption(pdfium::appearance::kCA); + } +- +- float GetOriginalBackgroundColor(int index) { +- return GetOriginalColor(index, "BG"); ++ WideString GetRolloverCaption() const { ++ return GetCaption(pdfium::appearance::kRC); + } +- +- void GetOriginalBackgroundColor(int& iColorType, float fc[4]) { +- GetOriginalColor(iColorType, fc, "BG"); ++ WideString GetDownCaption() const { ++ return GetCaption(pdfium::appearance::kAC); + } + +- WideString GetNormalCaption() const { return GetCaption("CA"); } +- WideString GetRolloverCaption() const { return GetCaption("RC"); } +- WideString GetDownCaption() const { return GetCaption("AC"); } +- +- CPDF_Stream* GetNormalIcon() { return GetIcon("I"); } +- CPDF_Stream* GetRolloverIcon() { return GetIcon("RI"); } +- CPDF_Stream* GetDownIcon() { return GetIcon("IX"); } ++ RetainPtr GetNormalIcon() { ++ return GetIcon(pdfium::appearance::kI); ++ } ++ RetainPtr GetRolloverIcon() { ++ return GetIcon(pdfium::appearance::kRI); ++ } ++ RetainPtr GetDownIcon() { ++ return GetIcon(pdfium::appearance::kIX); ++ } + CPDF_IconFit GetIconFit() const; + + int GetTextPosition() const; +- CPDF_Action GetAction() const; +- CPDF_AAction GetAdditionalAction() const; + CPDF_DefaultAppearance GetDefaultAppearance() const; + +- Optional GetDefaultControlFontName() const; ++ absl::optional GetDefaultControlFontName() const; + int GetControlAlignment() const; + + ByteString GetOnStateName() const; +@@ -99,14 +100,10 @@ class CPDF_FormControl { + + private: + RetainPtr GetDefaultControlFont() const; +- FX_ARGB GetColor(int& iColorType, const ByteString& csEntry); +- float GetOriginalColor(int index, const ByteString& csEntry); +- void GetOriginalColor(int& iColorType, +- float fc[4], +- const ByteString& csEntry); ++ CFX_Color GetOriginalColor(const ByteString& csEntry); + + WideString GetCaption(const ByteString& csEntry) const; +- CPDF_Stream* GetIcon(const ByteString& csEntry); ++ RetainPtr GetIcon(const ByteString& csEntry); + CPDF_ApSettings GetMK() const; + + UnownedPtr const m_pField; +diff --git a/core/fpdfdoc/cpdf_formfield.cpp b/core/fpdfdoc/cpdf_formfield.cpp +index 1eae005e3..705827d76 100644 +--- a/core/fpdfdoc/cpdf_formfield.cpp ++++ b/core/fpdfdoc/cpdf_formfield.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fpdfdoc/cpdf_formfield.h" + +-#include ++#include + #include + #include + +@@ -17,66 +17,71 @@ + #include "core/fpdfapi/parser/cfdf_document.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" +-#include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_number.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fpdfdoc/cpdf_defaultappearance.h" + #include "core/fpdfdoc/cpdf_formcontrol.h" ++#include "core/fpdfdoc/cpdf_generateap.h" + #include "core/fpdfdoc/cpdf_interactiveform.h" +-#include "core/fpdfdoc/cpvt_generateap.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxcrt/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" + + namespace { + +-const CPDF_Object* GetFieldAttrRecursive(const CPDF_Dictionary* pFieldDict, +- const ByteString& name, +- int nLevel) { ++RetainPtr GetFieldAttrRecursive( ++ const CPDF_Dictionary* pFieldDict, ++ const ByteString& name, ++ int nLevel) { + static constexpr int kGetFieldMaxRecursion = 32; + if (!pFieldDict || nLevel > kGetFieldMaxRecursion) + return nullptr; + +- const CPDF_Object* pAttr = pFieldDict->GetDirectObjectFor(name); ++ RetainPtr pAttr = pFieldDict->GetDirectObjectFor(name); + if (pAttr) + return pAttr; + + return GetFieldAttrRecursive( +- pFieldDict->GetDictFor(pdfium::form_fields::kParent), name, nLevel + 1); ++ pFieldDict->GetDictFor(pdfium::form_fields::kParent).Get(), name, ++ nLevel + 1); + } + + } // namespace + + // static +-Optional CPDF_FormField::IntToFormFieldType(int value) { ++absl::optional CPDF_FormField::IntToFormFieldType(int value) { + if (value >= static_cast(FormFieldType::kUnknown) && + value < static_cast(kFormFieldTypeCount)) { +- return {static_cast(value)}; ++ return static_cast(value); + } +- return {}; ++ return absl::nullopt; + } + + // static +-const CPDF_Object* CPDF_FormField::GetFieldAttr( ++RetainPtr CPDF_FormField::GetFieldAttrForDict( + const CPDF_Dictionary* pFieldDict, + const ByteString& name) { + return GetFieldAttrRecursive(pFieldDict, name, 0); + } + + // static +-CPDF_Object* CPDF_FormField::GetFieldAttr(CPDF_Dictionary* pFieldDict, +- const ByteString& name) { +- return const_cast(GetFieldAttrRecursive( +- static_cast(pFieldDict), name, 0)); ++RetainPtr CPDF_FormField::GetMutableFieldAttrForDict( ++ CPDF_Dictionary* pFieldDict, ++ const ByteString& name) { ++ return pdfium::WrapRetain(const_cast( ++ GetFieldAttrRecursive(pFieldDict, name, 0).Get())); + } + + // static +-WideString CPDF_FormField::GetFullNameForDict(CPDF_Dictionary* pFieldDict) { ++WideString CPDF_FormField::GetFullNameForDict( ++ const CPDF_Dictionary* pFieldDict) { + WideString full_name; +- std::set visited; +- CPDF_Dictionary* pLevel = pFieldDict; ++ std::set visited; ++ const CPDF_Dictionary* pLevel = pFieldDict; + while (pLevel) { + visited.insert(pLevel); + WideString short_name = pLevel->GetUnicodeTextFor(pdfium::form_fields::kT); +@@ -86,24 +91,24 @@ WideString CPDF_FormField::GetFullNameForDict(CPDF_Dictionary* pFieldDict) { + else + full_name = short_name + L'.' + full_name; + } +- pLevel = pLevel->GetDictFor(pdfium::form_fields::kParent); +- if (pdfium::ContainsKey(visited, pLevel)) ++ pLevel = pLevel->GetDictFor(pdfium::form_fields::kParent).Get(); ++ if (pdfium::Contains(visited, pLevel)) + break; + } + return full_name; + } + + CPDF_FormField::CPDF_FormField(CPDF_InteractiveForm* pForm, +- CPDF_Dictionary* pDict) +- : m_pForm(pForm), m_pDict(pDict) { ++ RetainPtr pDict) ++ : m_pForm(pForm), m_pDict(std::move(pDict)) { + InitFieldFlags(); + } + + CPDF_FormField::~CPDF_FormField() = default; + + void CPDF_FormField::InitFieldFlags() { +- const CPDF_Object* ft_attr = +- GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kFT); ++ RetainPtr ft_attr = ++ GetFieldAttrInternal(pdfium::form_fields::kFT); + ByteString type_name = ft_attr ? ft_attr->GetString() : ByteString(); + uint32_t flags = GetFieldFlags(); + m_bRequired = flags & pdfium::form_flags::kRequired; +@@ -126,7 +131,6 @@ void CPDF_FormField::InitFieldFlags() { + m_Type = kRichText; + else + m_Type = kText; +- LoadDA(); + } else if (type_name == pdfium::form_fields::kCh) { + if (flags & pdfium::form_flags::kChoiceCombo) { + m_Type = kComboBox; +@@ -134,7 +138,7 @@ void CPDF_FormField::InitFieldFlags() { + m_Type = kListBox; + m_bIsMultiSelectListBox = flags & pdfium::form_flags::kChoiceMultiSelect; + } +- LoadDA(); ++ m_bUseSelectedIndices = UseSelectedIndicesObject(); + } else if (type_name == pdfium::form_fields::kSig) { + m_Type = kSign; + } +@@ -144,7 +148,16 @@ WideString CPDF_FormField::GetFullName() const { + return GetFullNameForDict(m_pDict.Get()); + } + +-bool CPDF_FormField::ResetField(NotificationOption notify) { ++RetainPtr CPDF_FormField::GetFieldAttr( ++ const ByteString& name) const { ++ return GetFieldAttrInternal(name); ++} ++ ++RetainPtr CPDF_FormField::GetFieldDict() const { ++ return pdfium::WrapRetain(GetFieldDictInternal()); ++} ++ ++bool CPDF_FormField::ResetField() { + switch (m_Type) { + case kCheckBox: + case kRadioButton: { +@@ -155,8 +168,7 @@ bool CPDF_FormField::ResetField(NotificationOption notify) { + CheckControl(i, GetControl(i)->IsDefaultChecked(), + NotificationOption::kDoNotNotify); + } +- if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify()) +- m_pForm->GetFormNotify()->AfterCheckedStatusChange(this); ++ m_pForm->NotifyAfterCheckedStatusChange(this); + break; + } + case kComboBox: +@@ -166,55 +178,57 @@ bool CPDF_FormField::ResetField(NotificationOption notify) { + int iIndex = GetDefaultSelectedItem(); + if (iIndex >= 0) + csValue = GetOptionLabel(iIndex); +- if (notify == NotificationOption::kNotify && +- !NotifyListOrComboBoxBeforeChange(csValue)) { ++ if (!NotifyListOrComboBoxBeforeChange(csValue)) { + return false; + } +- SetItemSelection(iIndex, true, NotificationOption::kDoNotNotify); +- if (notify == NotificationOption::kNotify) +- NotifyListOrComboBoxAfterChange(); ++ SetItemSelection(iIndex, NotificationOption::kDoNotNotify); ++ NotifyListOrComboBoxAfterChange(); + break; + } + case kText: + case kRichText: + case kFile: + default: { +- const CPDF_Object* pDV = GetDefaultValueObject(); + WideString csDValue; +- if (pDV) +- csDValue = pDV->GetUnicodeText(); +- + WideString csValue; + { +- // Limit the scope of |pV| because it may get invalidated below. +- const CPDF_Object* pV = GetValueObject(); ++ // Limit scope of |pDV| and |pV| because they may get invalidated ++ // during notification below. ++ RetainPtr pDV = GetDefaultValueObject(); ++ if (pDV) ++ csDValue = pDV->GetUnicodeText(); ++ ++ RetainPtr pV = GetValueObject(); + if (pV) + csValue = pV->GetUnicodeText(); + } + +- bool bHasRV = !!GetFieldAttr(m_pDict.Get(), "RV"); ++ bool bHasRV = !!GetFieldAttrInternal(pdfium::form_fields::kRV); + if (!bHasRV && (csDValue == csValue)) + return false; + +- if (notify == NotificationOption::kNotify && +- !NotifyBeforeValueChange(csDValue)) { ++ if (!m_pForm->NotifyBeforeValueChange(this, csDValue)) + return false; +- } +- if (pDV) { +- RetainPtr pClone = pDV->Clone(); +- if (!pClone) +- return false; +- +- m_pDict->SetFor(pdfium::form_fields::kV, std::move(pClone)); +- if (bHasRV) { +- m_pDict->SetFor("RV", pDV->Clone()); ++ ++ { ++ // Limit scope of |pDV| because it may get invalidated during ++ // notification below. ++ RetainPtr pDV = GetDefaultValueObject(); ++ if (pDV) { ++ RetainPtr pClone = pDV->Clone(); ++ if (!pClone) ++ return false; ++ ++ m_pDict->SetFor(pdfium::form_fields::kV, std::move(pClone)); ++ if (bHasRV) { ++ m_pDict->SetFor(pdfium::form_fields::kRV, pDV->Clone()); ++ } ++ } else { ++ m_pDict->RemoveFor(pdfium::form_fields::kV); ++ m_pDict->RemoveFor(pdfium::form_fields::kRV); + } +- } else { +- m_pDict->RemoveFor(pdfium::form_fields::kV); +- m_pDict->RemoveFor("RV"); + } +- if (notify == NotificationOption::kNotify) +- NotifyAfterValueChange(); ++ m_pForm->NotifyAfterValueChange(this); + break; + } + } +@@ -222,11 +236,11 @@ bool CPDF_FormField::ResetField(NotificationOption notify) { + } + + int CPDF_FormField::CountControls() const { +- return pdfium::CollectionSize(GetControls()); ++ return fxcrt::CollectionSize(GetControls()); + } + + CPDF_FormControl* CPDF_FormField::GetControl(int index) const { +- return GetControls()[index].Get(); ++ return GetControls()[index]; + } + + int CPDF_FormField::GetControlIndex(const CPDF_FormControl* pControl) const { +@@ -235,7 +249,10 @@ int CPDF_FormField::GetControlIndex(const CPDF_FormControl* pControl) const { + + const auto& controls = GetControls(); + auto it = std::find(controls.begin(), controls.end(), pControl); +- return it != controls.end() ? it - controls.begin() : -1; ++ if (it == controls.end()) ++ return -1; ++ ++ return pdfium::base::checked_cast(it - controls.begin()); + } + + FormFieldType CPDF_FormField::GetFieldType() const { +@@ -262,42 +279,39 @@ FormFieldType CPDF_FormField::GetFieldType() const { + } + + CPDF_AAction CPDF_FormField::GetAdditionalAction() const { +- CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kAA); ++ RetainPtr pObj = ++ GetFieldAttrInternal(pdfium::form_fields::kAA); + return CPDF_AAction(pObj ? pObj->GetDict() : nullptr); + } + + WideString CPDF_FormField::GetAlternateName() const { +- const CPDF_Object* pObj = +- GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kTU); ++ RetainPtr pObj = ++ GetFieldAttrInternal(pdfium::form_fields::kTU); + return pObj ? pObj->GetUnicodeText() : WideString(); + } + + WideString CPDF_FormField::GetMappingName() const { +- const CPDF_Object* pObj = +- GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kTM); ++ RetainPtr pObj = ++ GetFieldAttrInternal(pdfium::form_fields::kTM); + return pObj ? pObj->GetUnicodeText() : WideString(); + } + + uint32_t CPDF_FormField::GetFieldFlags() const { +- const CPDF_Object* pObj = +- GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kFf); ++ RetainPtr pObj = ++ GetFieldAttrInternal(pdfium::form_fields::kFf); + return pObj ? pObj->GetInteger() : 0; + } + +-ByteString CPDF_FormField::GetDefaultStyle() const { +- const CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), "DS"); +- return pObj ? pObj->GetString() : ByteString(); +-} +- +-void CPDF_FormField::SetOpt(RetainPtr pOpt) { +- m_pDict->SetFor("Opt", std::move(pOpt)); ++void CPDF_FormField::SetFieldFlags(uint32_t dwFlags) { ++ m_pDict->SetNewFor(pdfium::form_fields::kFf, ++ static_cast(dwFlags)); + } + + WideString CPDF_FormField::GetValue(bool bDefault) const { + if (GetType() == kCheckBox || GetType() == kRadioButton) + return GetCheckValue(bDefault); + +- const CPDF_Object* pValue = ++ RetainPtr pValue = + bDefault ? GetDefaultValueObject() : GetValueObject(); + if (!pValue) { + if (!bDefault && m_Type != kText) +@@ -310,11 +324,13 @@ WideString CPDF_FormField::GetValue(bool bDefault) const { + case CPDF_Object::kString: + case CPDF_Object::kStream: + return pValue->GetUnicodeText(); +- case CPDF_Object::kArray: +- pValue = pValue->AsArray()->GetDirectObjectAt(0); +- if (pValue) +- return pValue->GetUnicodeText(); ++ case CPDF_Object::kArray: { ++ RetainPtr pNewValue = ++ pValue->AsArray()->GetDirectObjectAt(0); ++ if (pNewValue) ++ return pNewValue->GetUnicodeText(); + break; ++ } + default: + break; + } +@@ -344,26 +360,27 @@ bool CPDF_FormField::SetValue(const WideString& value, + case kComboBox: { + WideString csValue = value; + if (notify == NotificationOption::kNotify && +- !NotifyBeforeValueChange(csValue)) { ++ !m_pForm->NotifyBeforeValueChange(this, csValue)) { + return false; + } + ByteString key(bDefault ? pdfium::form_fields::kDV + : pdfium::form_fields::kV); +- m_pDict->SetNewFor(key, csValue); ++ m_pDict->SetNewFor(key, csValue.AsStringView()); + int iIndex = FindOption(csValue); + if (iIndex < 0) { + if (m_Type == kRichText && !bDefault) { +- m_pDict->SetFor("RV", m_pDict->GetObjectFor(key)->Clone()); ++ m_pDict->SetFor(pdfium::form_fields::kRV, ++ m_pDict->GetObjectFor(key)->Clone()); + } + m_pDict->RemoveFor("I"); + } else { + if (!bDefault) { + ClearSelection(NotificationOption::kDoNotNotify); +- SetItemSelection(iIndex, true, NotificationOption::kDoNotNotify); ++ SetItemSelection(iIndex, NotificationOption::kDoNotNotify); + } + } + if (notify == NotificationOption::kNotify) +- NotifyAfterValueChange(); ++ m_pForm->NotifyAfterValueChange(this); + break; + } + case kListBox: { +@@ -375,15 +392,15 @@ bool CPDF_FormField::SetValue(const WideString& value, + return false; + + if (notify == NotificationOption::kNotify && +- !NotifyBeforeSelectionChange(value)) { ++ !m_pForm->NotifyBeforeSelectionChange(this, value)) { + return false; + } + if (!bDefault) { + ClearSelection(NotificationOption::kDoNotNotify); +- SetItemSelection(iIndex, true, NotificationOption::kDoNotNotify); ++ SetItemSelection(iIndex, NotificationOption::kDoNotNotify); + } + if (notify == NotificationOption::kNotify) +- NotifyAfterSelectionChange(); ++ m_pForm->NotifyAfterSelectionChange(this); + break; + } + default: +@@ -398,14 +415,15 @@ bool CPDF_FormField::SetValue(const WideString& value, + } + + int CPDF_FormField::GetMaxLen() const { +- if (const CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), "MaxLen")) ++ RetainPtr pObj = GetFieldAttrInternal("MaxLen"); ++ if (pObj) + return pObj->GetInteger(); + + for (auto& pControl : GetControls()) { + if (!pControl) + continue; + +- const CPDF_Dictionary* pWidgetDict = pControl->GetWidget(); ++ RetainPtr pWidgetDict = pControl->GetWidgetDict(); + if (pWidgetDict->KeyExist("MaxLen")) + return pWidgetDict->GetIntegerFor("MaxLen"); + } +@@ -420,7 +438,7 @@ int CPDF_FormField::CountSelectedItems() const { + if (pValue->IsString() || pValue->IsNumber()) + return pValue->GetString().IsEmpty() ? 0 : 1; + const CPDF_Array* pArray = pValue->AsArray(); +- return pArray ? pArray->size() : 0; ++ return pArray ? fxcrt::CollectionSize(*pArray) : 0; + } + + int CPDF_FormField::GetSelectedIndex(int index) const { +@@ -441,7 +459,8 @@ int CPDF_FormField::GetSelectedIndex(int index) const { + if (!pArray || index < 0) + return -1; + +- const CPDF_Object* elementValue = pArray->GetDirectObjectAt(index); ++ RetainPtr elementValue = ++ pArray->GetDirectObjectAt(index); + sel_value = elementValue ? elementValue->GetUnicodeText() : WideString(); + } + if (index < CountSelectedOptions()) { +@@ -458,7 +477,7 @@ int CPDF_FormField::GetSelectedIndex(int index) const { + } + + bool CPDF_FormField::ClearSelection(NotificationOption notify) { +- if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify()) { ++ if (notify == NotificationOption::kNotify) { + WideString csValue; + int iIndex = GetSelectedIndex(0); + if (iIndex >= 0) +@@ -474,43 +493,17 @@ bool CPDF_FormField::ClearSelection(NotificationOption notify) { + } + + bool CPDF_FormField::IsItemSelected(int index) const { +- ASSERT(GetType() == kComboBox || GetType() == kListBox); ++ DCHECK(GetType() == kComboBox || GetType() == kListBox); + if (index < 0 || index >= CountOptions()) + return false; +- if (IsOptionSelected(index)) +- return true; +- +- WideString opt_value = GetOptionValue(index); +- const CPDF_Object* pValue = GetValueOrSelectedIndicesObject(); +- if (!pValue) +- return false; +- +- if (pValue->IsString()) +- return pValue->GetUnicodeText() == opt_value; + +- if (pValue->IsNumber()) { +- if (pValue->GetString().IsEmpty()) +- return false; +- return (pValue->GetInteger() == index); +- } +- +- const CPDF_Array* pArray = pValue->AsArray(); +- if (!pArray) +- return false; +- +- for (int i = 0; i < CountSelectedOptions(); ++i) { +- if (GetSelectedOptionIndex(i) == index) { +- const CPDF_Object* pDirectObj = pArray->GetDirectObjectAt(i); +- return pDirectObj && pDirectObj->GetUnicodeText() == opt_value; +- } +- } +- return false; ++ // First consider the /I entry if it is valid, then fall back to the /V entry. ++ return m_bUseSelectedIndices ? IsSelectedIndex(index) ++ : IsSelectedOption(GetOptionValue(index)); + } + +-bool CPDF_FormField::SetItemSelection(int index, +- bool bSelected, +- NotificationOption notify) { +- ASSERT(GetType() == kComboBox || GetType() == kListBox); ++bool CPDF_FormField::SetItemSelection(int index, NotificationOption notify) { ++ DCHECK(GetType() == kComboBox || GetType() == kListBox); + if (index < 0 || index >= CountOptions()) + return false; + +@@ -520,10 +513,12 @@ bool CPDF_FormField::SetItemSelection(int index, + return false; + } + +- if (bSelected) +- SetItemSelectionSelected(index, opt_value); +- else +- SetItemSelectionUnselected(index, opt_value); ++ SetItemSelectionSelected(index, opt_value); ++ ++ // UseSelectedIndicesObject() has a non-trivial linearithmic run-time, so run ++ // only if necessary. ++ if (!m_bUseSelectedIndices) ++ m_bUseSelectedIndices = UseSelectedIndicesObject(); + + if (notify == NotificationOption::kNotify) + NotifyListOrComboBoxAfterChange(); +@@ -533,69 +528,30 @@ bool CPDF_FormField::SetItemSelection(int index, + void CPDF_FormField::SetItemSelectionSelected(int index, + const WideString& opt_value) { + if (GetType() != kListBox) { +- m_pDict->SetNewFor(pdfium::form_fields::kV, opt_value); +- CPDF_Array* pI = m_pDict->SetNewFor("I"); +- pI->AddNew(index); ++ m_pDict->SetNewFor(pdfium::form_fields::kV, ++ opt_value.AsStringView()); ++ auto pI = m_pDict->SetNewFor("I"); ++ pI->AppendNew(index); + return; + } + +- SelectOption(index, true, NotificationOption::kDoNotNotify); ++ SelectOption(index); + if (!m_bIsMultiSelectListBox) { +- m_pDict->SetNewFor(pdfium::form_fields::kV, opt_value); ++ m_pDict->SetNewFor(pdfium::form_fields::kV, ++ opt_value.AsStringView()); + return; + } + +- CPDF_Array* pArray = m_pDict->SetNewFor(pdfium::form_fields::kV); ++ auto pArray = m_pDict->SetNewFor(pdfium::form_fields::kV); + for (int i = 0; i < CountOptions(); i++) { + if (i == index || IsItemSelected(i)) +- pArray->AddNew(GetOptionValue(i)); ++ pArray->AppendNew(GetOptionValue(i).AsStringView()); + } + } + +-void CPDF_FormField::SetItemSelectionUnselected(int index, +- const WideString& opt_value) { +- const CPDF_Object* pValue = GetValueObject(); +- if (!pValue) +- return; +- +- if (GetType() != kListBox) { +- m_pDict->RemoveFor(pdfium::form_fields::kV); +- m_pDict->RemoveFor("I"); +- return; +- } +- +- SelectOption(index, false, NotificationOption::kDoNotNotify); +- if (pValue->IsString()) { +- if (pValue->GetUnicodeText() == opt_value) { +- m_pDict->RemoveFor(pdfium::form_fields::kV); +- } +- return; +- } +- +- if (!pValue->IsArray()) +- return; +- +- auto pArray = pdfium::MakeRetain(); +- for (int i = 0; i < CountOptions(); i++) { +- if (i != index && IsItemSelected(i)) +- pArray->AddNew(GetOptionValue(i)); +- } +- if (pArray->size() > 0) { +- m_pDict->SetFor(pdfium::form_fields::kV, pArray); +- } +-} +- +-bool CPDF_FormField::IsItemDefaultSelected(int index) const { +- ASSERT(GetType() == kComboBox || GetType() == kListBox); +- if (index < 0 || index >= CountOptions()) +- return false; +- int iDVIndex = GetDefaultSelectedItem(); +- return iDVIndex >= 0 && iDVIndex == index; +-} +- + int CPDF_FormField::GetDefaultSelectedItem() const { +- ASSERT(GetType() == kComboBox || GetType() == kListBox); +- const CPDF_Object* pValue = GetDefaultValueObject(); ++ DCHECK(GetType() == kComboBox || GetType() == kListBox); ++ RetainPtr pValue = GetDefaultValueObject(); + if (!pValue) + return -1; + WideString csDV = pValue->GetUnicodeText(); +@@ -609,22 +565,27 @@ int CPDF_FormField::GetDefaultSelectedItem() const { + } + + int CPDF_FormField::CountOptions() const { +- const CPDF_Array* pArray = ToArray(GetFieldAttr(m_pDict.Get(), "Opt")); +- return pArray ? pArray->size() : 0; ++ RetainPtr pArray = ToArray(GetFieldAttrInternal("Opt")); ++ return pArray ? fxcrt::CollectionSize(*pArray) : 0; + } + + WideString CPDF_FormField::GetOptionText(int index, int sub_index) const { +- const CPDF_Array* pArray = ToArray(GetFieldAttr(m_pDict.Get(), "Opt")); ++ RetainPtr pArray = ToArray(GetFieldAttrInternal("Opt")); + if (!pArray) + return WideString(); + +- const CPDF_Object* pOption = pArray->GetDirectObjectAt(index); ++ RetainPtr pOption = pArray->GetDirectObjectAt(index); + if (!pOption) + return WideString(); +- if (const CPDF_Array* pOptionArray = pOption->AsArray()) ++ ++ const CPDF_Array* pOptionArray = pOption->AsArray(); ++ if (pOptionArray) + pOption = pOptionArray->GetDirectObjectAt(sub_index); + +- const CPDF_String* pString = ToString(pOption); ++ if (!pOption) ++ return WideString(); ++ ++ const CPDF_String* pString = pOption->AsString(); + return pString ? pString->GetUnicodeText() : WideString(); + } + +@@ -647,7 +608,7 @@ int CPDF_FormField::FindOption(const WideString& csOptValue) const { + bool CPDF_FormField::CheckControl(int iControlIndex, + bool bChecked, + NotificationOption notify) { +- ASSERT(GetType() == kCheckBox || GetType() == kRadioButton); ++ DCHECK(GetType() == kCheckBox || GetType() == kRadioButton); + CPDF_FormControl* pControl = GetControl(iControlIndex); + if (!pControl) + return false; +@@ -676,9 +637,9 @@ bool CPDF_FormField::CheckControl(int iControlIndex, + } + } + +- const CPDF_Object* pOpt = GetFieldAttr(m_pDict.Get(), "Opt"); ++ RetainPtr pOpt = GetFieldAttrInternal("Opt"); + if (!ToArray(pOpt)) { +- ByteString csBExport = PDF_EncodeText(csWExport); ++ ByteString csBExport = PDF_EncodeText(csWExport.AsStringView()); + if (bChecked) { + m_pDict->SetNewFor(pdfium::form_fields::kV, csBExport); + } else { +@@ -691,15 +652,15 @@ bool CPDF_FormField::CheckControl(int iControlIndex, + } + } else if (bChecked) { + m_pDict->SetNewFor(pdfium::form_fields::kV, +- ByteString::Format("%d", iControlIndex)); ++ ByteString::FormatInteger(iControlIndex)); + } +- if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify()) +- m_pForm->GetFormNotify()->AfterCheckedStatusChange(this); ++ if (notify == NotificationOption::kNotify) ++ m_pForm->NotifyAfterCheckedStatusChange(this); + return true; + } + + WideString CPDF_FormField::GetCheckValue(bool bDefault) const { +- ASSERT(GetType() == kCheckBox || GetType() == kRadioButton); ++ DCHECK(GetType() == kCheckBox || GetType() == kRadioButton); + WideString csExport = L"Off"; + int iCount = CountControls(); + for (int i = 0; i < iCount; i++) { +@@ -717,7 +678,7 @@ WideString CPDF_FormField::GetCheckValue(bool bDefault) const { + bool CPDF_FormField::SetCheckValue(const WideString& value, + bool bDefault, + NotificationOption notify) { +- ASSERT(GetType() == kCheckBox || GetType() == kRadioButton); ++ DCHECK(GetType() == kCheckBox || GetType() == kRadioButton); + int iCount = CountControls(); + for (int i = 0; i < iCount; i++) { + CPDF_FormControl* pControl = GetControl(i); +@@ -730,163 +691,170 @@ bool CPDF_FormField::SetCheckValue(const WideString& value, + if (val) + break; + } +- if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify()) +- m_pForm->GetFormNotify()->AfterCheckedStatusChange(this); ++ if (notify == NotificationOption::kNotify) ++ m_pForm->NotifyAfterCheckedStatusChange(this); + return true; + } + + int CPDF_FormField::GetTopVisibleIndex() const { +- const CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), "TI"); ++ RetainPtr pObj = GetFieldAttrInternal("TI"); + return pObj ? pObj->GetInteger() : 0; + } + + int CPDF_FormField::CountSelectedOptions() const { +- const CPDF_Array* pArray = ToArray(GetSelectedIndicesObject()); +- return pArray ? pArray->size() : 0; ++ RetainPtr pArray = ToArray(GetSelectedIndicesObject()); ++ return pArray ? fxcrt::CollectionSize(*pArray) : 0; + } + + int CPDF_FormField::GetSelectedOptionIndex(int index) const { +- const CPDF_Array* pArray = ToArray(GetSelectedIndicesObject()); ++ if (index < 0) ++ return 0; ++ ++ RetainPtr pArray = ToArray(GetSelectedIndicesObject()); + if (!pArray) + return -1; + +- int iCount = pArray->size(); +- if (iCount < 0 || index >= iCount) +- return -1; +- return pArray->GetIntegerAt(index); ++ return index < fxcrt::CollectionSize(*pArray) ++ ? pArray->GetIntegerAt(index) ++ : -1; + } + +-bool CPDF_FormField::IsOptionSelected(int iOptIndex) const { +- const CPDF_Array* pArray = ToArray(GetSelectedIndicesObject()); +- if (!pArray) ++bool CPDF_FormField::IsSelectedOption(const WideString& wsOptValue) const { ++ RetainPtr pValueObject = GetValueObject(); ++ if (!pValueObject) + return false; + +- CPDF_ArrayLocker locker(pArray); +- for (const auto& pObj : locker) { +- if (pObj->GetInteger() == iOptIndex) +- return true; ++ const CPDF_Array* pValueArray = pValueObject->AsArray(); ++ if (pValueArray) { ++ CPDF_ArrayLocker locker(pValueArray); ++ for (const auto& pObj : locker) { ++ if (pObj->IsString() && pObj->GetUnicodeText() == wsOptValue) ++ return true; ++ } + } +- return false; ++ ++ return pValueObject->IsString() && ++ pValueObject->GetUnicodeText() == wsOptValue; + } + +-bool CPDF_FormField::SelectOption(int iOptIndex, +- bool bSelected, +- NotificationOption notify) { +- CPDF_Array* pArray = m_pDict->GetArrayFor("I"); +- if (!pArray) { +- if (!bSelected) +- return true; ++bool CPDF_FormField::IsSelectedIndex(int iOptIndex) const { ++ RetainPtr pSelectedIndicesObject = ++ GetSelectedIndicesObject(); ++ if (!pSelectedIndicesObject) ++ return false; + +- pArray = m_pDict->SetNewFor("I"); ++ const CPDF_Array* pSelectedIndicesArray = pSelectedIndicesObject->AsArray(); ++ if (pSelectedIndicesArray) { ++ CPDF_ArrayLocker locker(pSelectedIndicesArray); ++ for (const auto& pObj : locker) { ++ if (pObj->IsNumber() && pObj->GetInteger() == iOptIndex) ++ return true; ++ } + } + +- bool bReturn = false; ++ return pSelectedIndicesObject->IsNumber() && ++ pSelectedIndicesObject->GetInteger() == iOptIndex; ++} ++ ++void CPDF_FormField::SelectOption(int iOptIndex) { ++ RetainPtr pArray = m_pDict->GetOrCreateArrayFor("I"); + for (size_t i = 0; i < pArray->size(); i++) { + int iFind = pArray->GetIntegerAt(i); +- if (iFind == iOptIndex) { +- if (bSelected) +- return true; +- +- if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify()) { +- WideString csValue = GetOptionLabel(iOptIndex); +- if (!NotifyListOrComboBoxBeforeChange(csValue)) +- return false; +- } +- pArray->RemoveAt(i); +- bReturn = true; +- break; +- } ++ if (iFind == iOptIndex) ++ return; + + if (iFind > iOptIndex) { +- if (!bSelected) +- continue; +- +- if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify()) { +- WideString csValue = GetOptionLabel(iOptIndex); +- if (!NotifyListOrComboBoxBeforeChange(csValue)) +- return false; +- } + pArray->InsertNewAt(i, iOptIndex); +- bReturn = true; +- break; ++ return; + } + } +- if (!bReturn) { +- if (bSelected) +- pArray->AddNew(iOptIndex); +- if (pArray->IsEmpty()) +- m_pDict->RemoveFor("I"); +- } +- if (notify == NotificationOption::kNotify) +- NotifyListOrComboBoxAfterChange(); +- +- return true; ++ pArray->AppendNew(iOptIndex); + } + +-void CPDF_FormField::LoadDA() { +- CPDF_Dictionary* pFormDict = m_pForm->GetFormDict(); +- if (!pFormDict) +- return; +- +- ByteString DA; +- if (const CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), "DA")) +- DA = pObj->GetString(); ++bool CPDF_FormField::UseSelectedIndicesObject() const { ++ DCHECK(GetType() == kComboBox || GetType() == kListBox); + +- if (DA.IsEmpty()) +- DA = pFormDict->GetStringFor("DA"); ++ RetainPtr pSelectedIndicesObject = ++ GetSelectedIndicesObject(); ++ if (!pSelectedIndicesObject) ++ return false; + +- if (DA.IsEmpty()) +- return; ++ // If there's not value object, then just use the indices object. ++ RetainPtr pValueObject = GetValueObject(); ++ if (!pValueObject) ++ return true; + +- CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR"); +- if (!pDR) +- return; ++ // Verify that the selected indices object is either an array or a number and ++ // count the number of indices. ++ size_t selected_indices_size; ++ const CPDF_Array* pSelectedIndicesArray = pSelectedIndicesObject->AsArray(); ++ if (pSelectedIndicesArray) ++ selected_indices_size = pSelectedIndicesArray->size(); ++ else if (pSelectedIndicesObject->IsNumber()) ++ selected_indices_size = 1; ++ else ++ return false; + +- CPDF_Dictionary* pFont = pDR->GetDictFor("Font"); +- if (!ValidateFontResourceDict(pFont)) +- return; ++ // Verify that the number of values is equal to |selected_indices_size|. Then, ++ // count the number of occurrences of each of the distinct values in the ++ // values object. ++ std::map values; ++ const CPDF_Array* pValueArray = pValueObject->AsArray(); ++ if (pValueArray) { ++ if (pValueArray->size() != selected_indices_size) ++ return false; ++ CPDF_ArrayLocker locker(pValueArray); ++ for (const auto& pObj : locker) { ++ if (pObj->IsString()) ++ values[pObj->GetUnicodeText()]++; ++ } ++ } else if (pValueObject->IsString()) { ++ if (selected_indices_size != 1) ++ return false; ++ values[pValueObject->GetUnicodeText()]++; ++ } + +- CPDF_DefaultAppearance appearance(DA); +- Optional font_name = appearance.GetFont(&m_FontSize); +- if (!font_name) +- return; ++ // Validate each index in the selected indices object. Then, verify that items ++ // identified by selected indices entry do not differ from those in the values ++ // entry of the field dictionary. ++ const int num_options = CountOptions(); ++ if (pSelectedIndicesArray) { ++ CPDF_ArrayLocker locker(pSelectedIndicesArray); ++ for (const auto& pObj : locker) { ++ if (!pObj->IsNumber()) ++ return false; + +- CPDF_Dictionary* pFontDict = pFont->GetDictFor(*font_name); +- if (!pFontDict) +- return; ++ int index = pObj->GetInteger(); ++ if (index < 0 || index >= num_options) ++ return false; + +- auto* pData = CPDF_DocPageData::FromDocument(m_pForm->GetDocument()); +- m_pFont = pData->GetFont(pFontDict); +-} ++ WideString wsOptValue = GetOptionValue(index); ++ auto it = values.find(wsOptValue); ++ if (it == values.end()) ++ return false; + +-bool CPDF_FormField::NotifyBeforeSelectionChange(const WideString& value) { +- auto* pNotify = m_pForm->GetFormNotify(); +- return !pNotify || pNotify->BeforeSelectionChange(this, value); +-} ++ it->second--; ++ if (it->second == 0) ++ values.erase(it); ++ } + +-void CPDF_FormField::NotifyAfterSelectionChange() { +- auto* pNotify = m_pForm->GetFormNotify(); +- if (pNotify) +- pNotify->AfterSelectionChange(this); +-} ++ return values.empty(); ++ } + +-bool CPDF_FormField::NotifyBeforeValueChange(const WideString& value) { +- auto* pNotify = m_pForm->GetFormNotify(); +- return !pNotify || pNotify->BeforeValueChange(this, value); +-} ++ DCHECK(pSelectedIndicesObject->IsNumber()); ++ int index = pSelectedIndicesObject->GetInteger(); ++ if (index < 0 || index >= num_options) ++ return false; + +-void CPDF_FormField::NotifyAfterValueChange() { +- auto* pNotify = m_pForm->GetFormNotify(); +- if (pNotify) +- pNotify->AfterValueChange(this); ++ return pdfium::Contains(values, GetOptionValue(index)); + } + + bool CPDF_FormField::NotifyListOrComboBoxBeforeChange(const WideString& value) { + switch (GetType()) { + case kListBox: +- return NotifyBeforeSelectionChange(value); ++ return m_pForm->NotifyBeforeSelectionChange(this, value); + case kComboBox: +- return NotifyBeforeValueChange(value); ++ return m_pForm->NotifyBeforeValueChange(this, value); + default: + return true; + } +@@ -895,32 +863,42 @@ bool CPDF_FormField::NotifyListOrComboBoxBeforeChange(const WideString& value) { + void CPDF_FormField::NotifyListOrComboBoxAfterChange() { + switch (GetType()) { + case kListBox: +- NotifyAfterSelectionChange(); ++ m_pForm->NotifyAfterSelectionChange(this); + break; + case kComboBox: +- NotifyAfterValueChange(); ++ m_pForm->NotifyAfterValueChange(this); + break; + default: + break; + } + } + +-const CPDF_Object* CPDF_FormField::GetDefaultValueObject() const { +- return GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kDV); ++RetainPtr CPDF_FormField::GetFieldAttrInternal( ++ const ByteString& name) const { ++ return GetFieldAttrRecursive(m_pDict.Get(), name, 0); ++} ++ ++const CPDF_Dictionary* CPDF_FormField::GetFieldDictInternal() const { ++ return m_pDict.Get(); + } + +-const CPDF_Object* CPDF_FormField::GetValueObject() const { +- return GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kV); ++RetainPtr CPDF_FormField::GetDefaultValueObject() const { ++ return GetFieldAttrInternal(pdfium::form_fields::kDV); + } + +-const CPDF_Object* CPDF_FormField::GetSelectedIndicesObject() const { +- ASSERT(GetType() == kComboBox || GetType() == kListBox); +- return GetFieldAttr(m_pDict.Get(), "I"); ++RetainPtr CPDF_FormField::GetValueObject() const { ++ return GetFieldAttrInternal(pdfium::form_fields::kV); + } + +-const CPDF_Object* CPDF_FormField::GetValueOrSelectedIndicesObject() const { +- ASSERT(GetType() == kComboBox || GetType() == kListBox); +- const CPDF_Object* pValue = GetValueObject(); ++RetainPtr CPDF_FormField::GetSelectedIndicesObject() const { ++ DCHECK(GetType() == kComboBox || GetType() == kListBox); ++ return GetFieldAttrInternal("I"); ++} ++ ++RetainPtr CPDF_FormField::GetValueOrSelectedIndicesObject() ++ const { ++ DCHECK(GetType() == kComboBox || GetType() == kListBox); ++ RetainPtr pValue = GetValueObject(); + return pValue ? pValue : GetSelectedIndicesObject(); + } + +diff --git a/core/fpdfdoc/cpdf_formfield.h b/core/fpdfdoc/cpdf_formfield.h +index aa51ddc0b..e6e4e9b21 100644 +--- a/core/fpdfdoc/cpdf_formfield.h ++++ b/core/fpdfdoc/cpdf_formfield.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,24 +7,24 @@ + #ifndef CORE_FPDFDOC_CPDF_FORMFIELD_H_ + #define CORE_FPDFDOC_CPDF_FORMFIELD_H_ + +-#include ++#include ++#include ++ + #include + #include + + #include "core/fpdfdoc/cpdf_aaction.h" + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + + class CPDF_Dictionary; +-class CPDF_Font; + class CPDF_FormControl; + class CPDF_InteractiveForm; + class CPDF_Object; + class CPDF_String; + +-enum class NotificationOption { kDoNotNotify = 0, kNotify }; ++enum class NotificationOption : bool { kDoNotNotify = false, kNotify = true }; + + enum class FormFieldType : uint8_t { + kUnknown = 0, +@@ -69,23 +69,24 @@ class CPDF_FormField { + kSign + }; + +- CPDF_FormField(CPDF_InteractiveForm* pForm, CPDF_Dictionary* pDict); ++ CPDF_FormField(CPDF_InteractiveForm* pForm, RetainPtr pDict); + ~CPDF_FormField(); + +- static Optional IntToFormFieldType(int value); +- +- static const CPDF_Object* GetFieldAttr(const CPDF_Dictionary* pFieldDict, +- const ByteString& name); +- static CPDF_Object* GetFieldAttr(CPDF_Dictionary* pFieldDict, +- const ByteString& name); +- +- static WideString GetFullNameForDict(CPDF_Dictionary* pFieldDict); ++ static absl::optional IntToFormFieldType(int value); ++ static WideString GetFullNameForDict(const CPDF_Dictionary* pFieldDict); ++ static RetainPtr GetFieldAttrForDict( ++ const CPDF_Dictionary* pFieldDict, ++ const ByteString& name); ++ static RetainPtr GetMutableFieldAttrForDict( ++ CPDF_Dictionary* pFieldDict, ++ const ByteString& name); + + WideString GetFullName() const; + Type GetType() const { return m_Type; } + +- CPDF_Dictionary* GetFieldDict() const { return m_pDict.Get(); } +- bool ResetField(NotificationOption notify); ++ RetainPtr GetFieldAttr(const ByteString& name) const; ++ RetainPtr GetFieldDict() const; ++ bool ResetField(); + + int CountControls() const; + CPDF_FormControl* GetControl(int index) const; +@@ -98,7 +99,7 @@ class CPDF_FormField { + WideString GetMappingName() const; + + uint32_t GetFieldFlags() const; +- ByteString GetDefaultStyle() const; ++ void SetFieldFlags(uint32_t dwFlags); + + bool IsRequired() const { return m_bRequired; } + bool IsNoExport() const { return m_bNoExport; } +@@ -113,9 +114,7 @@ class CPDF_FormField { + + bool ClearSelection(NotificationOption notify); + bool IsItemSelected(int index) const; +- bool SetItemSelection(int index, bool bSelected, NotificationOption notify); +- +- bool IsItemDefaultSelected(int index) const; ++ bool SetItemSelection(int index, NotificationOption notify); + + int GetDefaultSelectedItem() const; + int CountOptions() const; +@@ -130,19 +129,16 @@ class CPDF_FormField { + int GetTopVisibleIndex() const; + int CountSelectedOptions() const; + int GetSelectedOptionIndex(int index) const; +- bool IsOptionSelected(int iOptIndex) const; +- bool SelectOption(int iOptIndex, bool bSelected, NotificationOption notify); +- +- float GetFontSize() const { return m_FontSize; } +- CPDF_Font* GetFont() const { return m_pFont.Get(); } ++ bool IsSelectedOption(const WideString& wsOptValue) const; ++ bool IsSelectedIndex(int iOptIndex) const; ++ void SelectOption(int iOptIndex); + +- CPDF_Dictionary* GetDict() const { return m_pDict.Get(); } +- CPDF_InteractiveForm* GetForm() const { return m_pForm.Get(); } ++ // Verifies if there is a valid selected indicies (/I) object and whether its ++ // entries are consistent with the value (/V) object. ++ bool UseSelectedIndicesObject() const; + + WideString GetCheckValue(bool bDefault) const; + +- void SetOpt(RetainPtr pOpt); +- + private: + WideString GetValue(bool bDefault) const; + bool SetValue(const WideString& value, +@@ -156,23 +152,21 @@ class CPDF_FormField { + bool bDefault, + NotificationOption notify); + void SetItemSelectionSelected(int index, const WideString& opt_value); +- void SetItemSelectionUnselected(int index, const WideString& opt_value); +- bool NotifyBeforeSelectionChange(const WideString& value); +- void NotifyAfterSelectionChange(); +- bool NotifyBeforeValueChange(const WideString& value); +- void NotifyAfterValueChange(); + bool NotifyListOrComboBoxBeforeChange(const WideString& value); + void NotifyListOrComboBoxAfterChange(); + +- const CPDF_Object* GetDefaultValueObject() const; +- const CPDF_Object* GetValueObject() const; ++ RetainPtr GetFieldAttrInternal( ++ const ByteString& name) const; ++ const CPDF_Dictionary* GetFieldDictInternal() const; ++ RetainPtr GetDefaultValueObject() const; ++ RetainPtr GetValueObject() const; + + // For choice fields. +- const CPDF_Object* GetSelectedIndicesObject() const; ++ RetainPtr GetSelectedIndicesObject() const; + + // For choice fields. + // Value object takes precedence over selected indices object. +- const CPDF_Object* GetValueOrSelectedIndicesObject() const; ++ RetainPtr GetValueOrSelectedIndicesObject() const; + + const std::vector>& GetControls() const; + +@@ -181,10 +175,9 @@ class CPDF_FormField { + bool m_bNoExport = false; + bool m_bIsMultiSelectListBox = false; + bool m_bIsUnison = false; +- float m_FontSize = 0; ++ bool m_bUseSelectedIndices = false; + UnownedPtr const m_pForm; + RetainPtr const m_pDict; +- RetainPtr m_pFont; + }; + + #endif // CORE_FPDFDOC_CPDF_FORMFIELD_H_ +diff --git a/core/fpdfdoc/cpdf_formfield_unittest.cpp b/core/fpdfdoc/cpdf_formfield_unittest.cpp +index ca7589152..41b99f5b8 100644 +--- a/core/fpdfdoc/cpdf_formfield_unittest.cpp ++++ b/core/fpdfdoc/cpdf_formfield_unittest.cpp +@@ -1,49 +1,335 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fpdfdoc/cpdf_formfield.h" + ++#include ++ ++#include "constants/form_fields.h" ++#include "constants/form_flags.h" ++#include "core/fpdfapi/page/cpdf_pagemodule.h" ++#include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h" + #include "core/fpdfapi/parser/cpdf_name.h" ++#include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fpdfapi/parser/cpdf_reference.h" ++#include "core/fpdfapi/parser/cpdf_string.h" ++#include "core/fpdfapi/parser/cpdf_test_document.h" ++#include "core/fpdfdoc/cpdf_interactiveform.h" ++#include "core/fxcrt/fx_memory.h" + #include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/base/containers/contains.h" ++ ++namespace { ++ ++// Create and destroys the page module that is necessary when instantiating a ++// CPDF_Document. ++class ScopedCPDF_PageModule { ++ public: ++ FX_STACK_ALLOCATED(); ++ ++ ScopedCPDF_PageModule() { CPDF_PageModule::Create(); } ++ ~ScopedCPDF_PageModule() { CPDF_PageModule::Destroy(); } ++}; ++ ++void TestMultiselectFieldDict(RetainPtr opt_array, ++ RetainPtr values, ++ RetainPtr selected_indices, ++ bool expected_use_indices, ++ const std::vector& expected_indices, ++ const std::vector& excluded_indices) { ++ auto form_dict = pdfium::MakeRetain(); ++ form_dict->SetNewFor("Type", "Annot"); ++ form_dict->SetNewFor("Subtype", "Widget"); ++ form_dict->SetNewFor(pdfium::form_fields::kFT, ++ pdfium::form_fields::kCh); ++ constexpr int kMuliSelectFlag = pdfium::form_flags::kChoiceMultiSelect; ++ form_dict->SetNewFor(pdfium::form_fields::kFf, kMuliSelectFlag); ++ form_dict->SetFor("Opt", opt_array); ++ form_dict->SetFor(pdfium::form_fields::kV, values); ++ form_dict->SetFor("I", selected_indices); ++ ++ CPDF_TestDocument doc; ++ CPDF_InteractiveForm form(&doc); ++ CPDF_FormField form_field(&form, std::move(form_dict)); ++ EXPECT_EQ(expected_use_indices, form_field.UseSelectedIndicesObject()); ++ for (int i = 0; i < form_field.CountOptions(); i++) { ++ const bool expected_selected = pdfium::Contains(expected_indices, i); ++ EXPECT_EQ(expected_selected, form_field.IsItemSelected(i)); ++ } ++ for (int i : excluded_indices) { ++ EXPECT_FALSE(form_field.IsItemSelected(i)); ++ } ++} ++ ++} // namespace + +-TEST(cpdf_formfield, GetFullNameForDict) { ++TEST(CPDF_FormFieldTest, GetFullNameForDict) { + WideString name = CPDF_FormField::GetFullNameForDict(nullptr); + EXPECT_TRUE(name.IsEmpty()); + + CPDF_IndirectObjectHolder obj_holder; +- CPDF_Dictionary* root = obj_holder.NewIndirect(); ++ auto root = obj_holder.NewIndirect(); + root->SetNewFor("T", "foo"); +- name = CPDF_FormField::GetFullNameForDict(root); ++ name = CPDF_FormField::GetFullNameForDict(root.Get()); + EXPECT_STREQ("foo", name.ToUTF8().c_str()); + +- CPDF_Dictionary* dict1 = obj_holder.NewIndirect(); ++ auto dict1 = obj_holder.NewIndirect(); + root->SetNewFor("Parent", &obj_holder, dict1->GetObjNum()); + dict1->SetNewFor("T", "bar"); +- name = CPDF_FormField::GetFullNameForDict(root); ++ name = CPDF_FormField::GetFullNameForDict(root.Get()); + EXPECT_STREQ("bar.foo", name.ToUTF8().c_str()); + +- CPDF_Dictionary* dict2 = dict1->SetNewFor("Parent"); +- name = CPDF_FormField::GetFullNameForDict(root); ++ auto dict2 = dict1->SetNewFor("Parent"); ++ name = CPDF_FormField::GetFullNameForDict(root.Get()); + EXPECT_STREQ("bar.foo", name.ToUTF8().c_str()); + +- CPDF_Dictionary* dict3 = obj_holder.NewIndirect(); ++ auto dict3 = obj_holder.NewIndirect(); + dict2->SetNewFor("Parent", &obj_holder, dict3->GetObjNum()); + + dict3->SetNewFor("T", "qux"); +- name = CPDF_FormField::GetFullNameForDict(root); ++ name = CPDF_FormField::GetFullNameForDict(root.Get()); + EXPECT_STREQ("qux.bar.foo", name.ToUTF8().c_str()); + + dict3->SetNewFor("Parent", &obj_holder, root->GetObjNum()); +- name = CPDF_FormField::GetFullNameForDict(root); ++ name = CPDF_FormField::GetFullNameForDict(root.Get()); + EXPECT_STREQ("qux.bar.foo", name.ToUTF8().c_str()); +- name = CPDF_FormField::GetFullNameForDict(dict1); ++ name = CPDF_FormField::GetFullNameForDict(dict1.Get()); + EXPECT_STREQ("foo.qux.bar", name.ToUTF8().c_str()); +- name = CPDF_FormField::GetFullNameForDict(dict2); ++ name = CPDF_FormField::GetFullNameForDict(dict2.Get()); + EXPECT_STREQ("bar.foo.qux", name.ToUTF8().c_str()); +- name = CPDF_FormField::GetFullNameForDict(dict3); ++ name = CPDF_FormField::GetFullNameForDict(dict3.Get()); + EXPECT_STREQ("bar.foo.qux", name.ToUTF8().c_str()); + } ++ ++TEST(CPDF_FormFieldTest, IsItemSelected) { ++ ScopedCPDF_PageModule page_module; ++ ++ auto opt_array = pdfium::MakeRetain(); ++ opt_array->AppendNew(L"Alpha"); ++ opt_array->AppendNew(L"Beta"); ++ opt_array->AppendNew(L"Gamma"); ++ opt_array->AppendNew(L"Delta"); ++ opt_array->AppendNew(L"Epsilon"); ++ ++ { ++ // No Values (/V) or Selected Indices (/I) objects. ++ std::vector expected_indices; ++ std::vector excluded_indices{-1, 5}; ++ TestMultiselectFieldDict(opt_array, /*values=*/nullptr, ++ /*selected_indices=*/nullptr, ++ /*expected_use_indices=*/false, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Values (/V) object is just a string. ++ auto values = pdfium::MakeRetain(/*pPool=*/nullptr, L"Gamma"); ++ std::vector expected_indices{2}; ++ std::vector excluded_indices{-1, 5}; ++ TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr, ++ /*expected_use_indices=*/false, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Values (/V) object is just an invalid string. ++ auto values = pdfium::MakeRetain(/*pPool=*/nullptr, L"Omega"); ++ std::vector expected_indices; ++ std::vector excluded_indices{-1, 5}; ++ TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr, ++ /*expected_use_indices=*/false, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Values (/V) object is an array with one object. ++ auto values = pdfium::MakeRetain(); ++ values->AppendNew(L"Beta"); ++ std::vector expected_indices{1}; ++ std::vector excluded_indices{-1, 5}; ++ TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr, ++ /*expected_use_indices=*/false, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Values (/V) object is an array with one invalid object. ++ auto values = pdfium::MakeRetain(); ++ values->AppendNew(L"Omega"); ++ std::vector expected_indices; ++ std::vector excluded_indices{-1, 5}; ++ TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr, ++ /*expected_use_indices=*/false, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Values (/V) object is an array with multiple objects. ++ auto values = pdfium::MakeRetain(); ++ values->AppendNew(L"Beta"); ++ values->AppendNew(L"Epsilon"); ++ std::vector expected_indices{1, 4}; ++ std::vector excluded_indices{-1, 5}; ++ TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr, ++ /*expected_use_indices=*/false, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Values (/V) object is an array with multiple objects with one invalid. ++ auto values = pdfium::MakeRetain(); ++ values->AppendNew(L"Beta"); ++ values->AppendNew(L"Epsilon"); ++ values->AppendNew(L"Omega"); ++ std::vector expected_indices{1, 4}; ++ std::vector excluded_indices{-1, 5}; ++ TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr, ++ /*expected_use_indices=*/false, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Selected indices (/I) object is just a number. ++ auto selected_indices = pdfium::MakeRetain(3); ++ std::vector expected_indices{3}; ++ std::vector excluded_indices{-1, 5}; ++ TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices, ++ /*expected_use_indices=*/true, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Selected indices (/I) object is just an invalid number. ++ auto selected_indices = pdfium::MakeRetain(26); ++ std::vector expected_indices; ++ std::vector excluded_indices{-1, 5, 26}; ++ TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices, ++ /*expected_use_indices=*/true, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Selected indices (/I) object is an array with one object. ++ auto selected_indices = pdfium::MakeRetain(); ++ selected_indices->AppendNew(0); ++ std::vector expected_indices{0}; ++ std::vector excluded_indices{-1, 5}; ++ TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices, ++ /*expected_use_indices=*/true, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Selected indices (/I) object is an array with multiple objects. ++ auto selected_indices = pdfium::MakeRetain(); ++ selected_indices->AppendNew(0); ++ selected_indices->AppendNew(2); ++ selected_indices->AppendNew(3); ++ std::vector expected_indices{0, 2, 3}; ++ std::vector excluded_indices{-1, 5}; ++ TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices, ++ /*expected_use_indices=*/true, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Selected indices (/I) object is an array with multiple objects and some ++ // are invalid. ++ auto selected_indices = pdfium::MakeRetain(); ++ selected_indices->AppendNew(0); ++ selected_indices->AppendNew(2); ++ selected_indices->AppendNew(3); ++ selected_indices->AppendNew(-5); ++ selected_indices->AppendNew(12); ++ selected_indices->AppendNew(42); ++ std::vector expected_indices{0, 2, 3}; ++ std::vector excluded_indices{-5, -1, 5, 12, 42}; ++ TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices, ++ /*expected_use_indices=*/true, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Values (/V) or Selected Indices (/I) objects conflict with different ++ // lengths. ++ auto values = pdfium::MakeRetain(); ++ values->AppendNew(L"Beta"); ++ values->AppendNew(L"Epsilon"); ++ auto selected_indices = pdfium::MakeRetain(); ++ selected_indices->AppendNew(0); ++ selected_indices->AppendNew(2); ++ selected_indices->AppendNew(3); ++ std::vector expected_indices{1, 4}; ++ std::vector excluded_indices{-1, 5}; ++ TestMultiselectFieldDict(opt_array, values, selected_indices, ++ /*expected_use_indices=*/false, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Values (/V) or Selected Indices (/I) objects conflict with same lengths. ++ auto values = pdfium::MakeRetain(); ++ values->AppendNew(L"Alpha"); ++ values->AppendNew(L"Epsilon"); ++ auto selected_indices = pdfium::MakeRetain(); ++ selected_indices->AppendNew(2); ++ selected_indices->AppendNew(3); ++ std::vector expected_indices{0, 4}; ++ std::vector excluded_indices{-1, 5}; ++ TestMultiselectFieldDict(opt_array, values, selected_indices, ++ /*expected_use_indices=*/false, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Values (/V) or Selected Indices (/I) objects conflict with values being ++ // invalid. ++ auto values = pdfium::MakeRetain(); ++ values->AppendNew(L"Beta"); ++ values->AppendNew(L"Epsilon"); ++ values->AppendNew(L"Omega"); ++ auto selected_indices = pdfium::MakeRetain(); ++ selected_indices->AppendNew(1); ++ selected_indices->AppendNew(4); ++ std::vector expected_indices{1, 4}; ++ std::vector excluded_indices{-1, 5}; ++ TestMultiselectFieldDict(opt_array, values, selected_indices, ++ /*expected_use_indices=*/false, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Values (/V) or Selected Indices (/I) objects conflict with selected ++ // indices being invalid. ++ auto values = pdfium::MakeRetain(); ++ values->AppendNew(L"Beta"); ++ values->AppendNew(L"Epsilon"); ++ auto selected_indices = pdfium::MakeRetain(); ++ selected_indices->AppendNew(1); ++ selected_indices->AppendNew(4); ++ selected_indices->AppendNew(26); ++ std::vector expected_indices{1, 4}; ++ std::vector excluded_indices{-1, 5, 26}; ++ TestMultiselectFieldDict(opt_array, values, selected_indices, ++ /*expected_use_indices=*/false, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Values (/V) or Selected Indices (/I) objects conflict with both being ++ // invalid. ++ auto values = pdfium::MakeRetain(); ++ values->AppendNew(L"Beta"); ++ values->AppendNew(L"Epsilon"); ++ values->AppendNew(L"Omega"); ++ auto selected_indices = pdfium::MakeRetain(); ++ selected_indices->AppendNew(0); ++ selected_indices->AppendNew(2); ++ selected_indices->AppendNew(3); ++ selected_indices->AppendNew(26); ++ std::vector expected_indices{1, 4}; ++ std::vector excluded_indices{-1, 5, 26}; ++ TestMultiselectFieldDict(opt_array, values, selected_indices, ++ /*expected_use_indices=*/false, expected_indices, ++ excluded_indices); ++ } ++ { ++ // Values (/V) or Selected Indices (/I) objects conflict with each not being ++ // an array. ++ auto values = pdfium::MakeRetain(/*pPool=*/nullptr, L"Gamma"); ++ auto selected_indices = pdfium::MakeRetain(4); ++ std::vector expected_indices{2}; ++ std::vector excluded_indices{-1, 5}; ++ TestMultiselectFieldDict(opt_array, values, selected_indices, ++ /*expected_use_indices=*/false, expected_indices, ++ excluded_indices); ++ } ++} +diff --git a/core/fpdfdoc/cpvt_generateap.cpp b/core/fpdfdoc/cpdf_generateap.cpp +similarity index 72% +rename from core/fpdfdoc/cpvt_generateap.cpp +rename to core/fpdfdoc/cpdf_generateap.cpp +index 2b90365e2..8c6f25ec8 100644 +--- a/core/fpdfdoc/cpvt_generateap.cpp ++++ b/core/fpdfdoc/cpdf_generateap.cpp +@@ -1,17 +1,18 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#include "core/fpdfdoc/cpvt_generateap.h" ++#include "core/fpdfdoc/cpdf_generateap.h" + + #include +-#include + #include + #include + + #include "constants/annotation_common.h" ++#include "constants/appearance.h" ++#include "constants/font_encodings.h" + #include "constants/form_fields.h" + #include "core/fpdfapi/font/cpdf_font.h" + #include "core/fpdfapi/page/cpdf_docpagedata.h" +@@ -30,11 +31,11 @@ + #include "core/fpdfdoc/cpdf_color_utils.h" + #include "core/fpdfdoc/cpdf_defaultappearance.h" + #include "core/fpdfdoc/cpdf_formfield.h" +-#include "core/fpdfdoc/cpdf_variabletext.h" + #include "core/fpdfdoc/cpvt_fontmap.h" ++#include "core/fpdfdoc/cpvt_variabletext.h" + #include "core/fpdfdoc/cpvt_word.h" ++#include "core/fxcrt/fx_string_wrappers.h" + #include "core/fxge/cfx_renderdevice.h" +-#include "third_party/base/ptr_util.h" + + namespace { + +@@ -47,7 +48,7 @@ struct CPVT_Dash { + int32_t nPhase; + }; + +-enum class PaintOperation { STROKE, FILL }; ++enum class PaintOperation { kStroke, kFill }; + + ByteString GetPDFWordString(IPVT_FontMap* pFontMap, + int32_t nFontIndex, +@@ -63,8 +64,8 @@ ByteString GetPDFWordString(IPVT_FontMap* pFontMap, + if (!pPDFFont) + return ByteString(); + +- if (pPDFFont->GetBaseFontName().Compare("Symbol") == 0 || +- pPDFFont->GetBaseFontName().Compare("ZapfDingbats") == 0) { ++ if (pPDFFont->GetBaseFontName() == "Symbol" || ++ pPDFFont->GetBaseFontName() == "ZapfDingbats") { + return ByteString::Format("%c", Word); + } + +@@ -76,16 +77,16 @@ ByteString GetPDFWordString(IPVT_FontMap* pFontMap, + return sWord; + } + +-ByteString GetWordRenderString(const ByteString& strWords) { +- if (strWords.GetLength() > 0) +- return PDF_EncodeString(strWords, false) + " Tj\n"; +- return ByteString(); ++ByteString GetWordRenderString(ByteStringView strWords) { ++ if (strWords.IsEmpty()) ++ return ByteString(); ++ return PDF_EncodeString(strWords) + " Tj\n"; + } + + ByteString GetFontSetString(IPVT_FontMap* pFontMap, + int32_t nFontIndex, + float fFontSize) { +- std::ostringstream sRet; ++ fxcrt::ostringstream sRet; + if (pFontMap) { + ByteString sFontAlias = pFontMap->GetPDFFontAlias(nFontIndex); + if (sFontAlias.GetLength() > 0 && fFontSize > 0) +@@ -95,28 +96,27 @@ ByteString GetFontSetString(IPVT_FontMap* pFontMap, + } + + ByteString GenerateEditAP(IPVT_FontMap* pFontMap, +- CPDF_VariableText::Iterator* pIterator, ++ CPVT_VariableText::Iterator* pIterator, + const CFX_PointF& ptOffset, + bool bContinuous, + uint16_t SubWord) { +- std::ostringstream sEditStream; +- std::ostringstream sLineStream; +- std::ostringstream sWords; ++ fxcrt::ostringstream sEditStream; ++ fxcrt::ostringstream sLineStream; + CFX_PointF ptOld; + CFX_PointF ptNew; + int32_t nCurFontIndex = -1; + CPVT_WordPlace oldplace; +- ++ ByteString sWords; + pIterator->SetAt(0); + while (pIterator->NextWord()) { + CPVT_WordPlace place = pIterator->GetWordPlace(); + if (bContinuous) { + if (place.LineCmp(oldplace) != 0) { +- if (sWords.tellp() > 0) { +- sLineStream << GetWordRenderString(ByteString(sWords)); ++ if (!sWords.IsEmpty()) { ++ sLineStream << GetWordRenderString(sWords.AsStringView()); + sEditStream << sLineStream.str(); + sLineStream.str(""); +- sWords.str(""); ++ sWords.clear(); + } + CPVT_Word word; + if (pIterator->GetWord(word)) { +@@ -137,15 +137,15 @@ ByteString GenerateEditAP(IPVT_FontMap* pFontMap, + CPVT_Word word; + if (pIterator->GetWord(word)) { + if (word.nFontIndex != nCurFontIndex) { +- if (sWords.tellp() > 0) { +- sLineStream << GetWordRenderString(ByteString(sWords)); +- sWords.str(""); ++ if (!sWords.IsEmpty()) { ++ sLineStream << GetWordRenderString(sWords.AsStringView()); ++ sWords.clear(); + } + sLineStream << GetFontSetString(pFontMap, word.nFontIndex, + word.fFontSize); + nCurFontIndex = word.nFontIndex; + } +- sWords << GetPDFWordString(pFontMap, nCurFontIndex, word.Word, SubWord); ++ sWords += GetPDFWordString(pFontMap, nCurFontIndex, word.Word, SubWord); + } + oldplace = place; + } else { +@@ -164,39 +164,39 @@ ByteString GenerateEditAP(IPVT_FontMap* pFontMap, + nCurFontIndex = word.nFontIndex; + } + sEditStream << GetWordRenderString( +- GetPDFWordString(pFontMap, nCurFontIndex, word.Word, SubWord)); ++ GetPDFWordString(pFontMap, nCurFontIndex, word.Word, SubWord) ++ .AsStringView()); + } + } + } +- if (sWords.tellp() > 0) { +- sLineStream << GetWordRenderString(ByteString(sWords)); ++ if (!sWords.IsEmpty()) { ++ sLineStream << GetWordRenderString(sWords.AsStringView()); + sEditStream << sLineStream.str(); +- sWords.str(""); + } + return ByteString(sEditStream); + } + + ByteString GenerateColorAP(const CFX_Color& color, PaintOperation nOperation) { +- std::ostringstream sColorStream; ++ fxcrt::ostringstream sColorStream; + switch (color.nColorType) { +- case CFX_Color::kRGB: ++ case CFX_Color::Type::kRGB: + sColorStream << color.fColor1 << " " << color.fColor2 << " " + << color.fColor3 << " " +- << (nOperation == PaintOperation::STROKE ? "RG" : "rg") ++ << (nOperation == PaintOperation::kStroke ? "RG" : "rg") + << "\n"; + break; +- case CFX_Color::kGray: ++ case CFX_Color::Type::kGray: + sColorStream << color.fColor1 << " " +- << (nOperation == PaintOperation::STROKE ? "G" : "g") ++ << (nOperation == PaintOperation::kStroke ? "G" : "g") + << "\n"; + break; +- case CFX_Color::kCMYK: ++ case CFX_Color::Type::kCMYK: + sColorStream << color.fColor1 << " " << color.fColor2 << " " + << color.fColor3 << " " << color.fColor4 << " " +- << (nOperation == PaintOperation::STROKE ? "K" : "k") ++ << (nOperation == PaintOperation::kStroke ? "K" : "k") + << "\n"; + break; +- case CFX_Color::kTransparent: ++ case CFX_Color::Type::kTransparent: + break; + } + return ByteString(sColorStream); +@@ -209,7 +209,7 @@ ByteString GenerateBorderAP(const CFX_FloatRect& rect, + const CFX_Color& crRightBottom, + BorderStyle nStyle, + const CPVT_Dash& dash) { +- std::ostringstream sAppStream; ++ fxcrt::ostringstream sAppStream; + ByteString sColor; + float fLeft = rect.left; + float fRight = rect.right; +@@ -219,8 +219,8 @@ ByteString GenerateBorderAP(const CFX_FloatRect& rect, + float fHalfWidth = fWidth / 2.0f; + switch (nStyle) { + default: +- case BorderStyle::SOLID: +- sColor = GenerateColorAP(color, PaintOperation::FILL); ++ case BorderStyle::kSolid: ++ sColor = GenerateColorAP(color, PaintOperation::kFill); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " " +@@ -231,8 +231,8 @@ ByteString GenerateBorderAP(const CFX_FloatRect& rect, + sAppStream << "f*\n"; + } + break; +- case BorderStyle::DASH: +- sColor = GenerateColorAP(color, PaintOperation::STROKE); ++ case BorderStyle::kDash: ++ sColor = GenerateColorAP(color, PaintOperation::kStroke); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fWidth << " w" +@@ -250,9 +250,9 @@ ByteString GenerateBorderAP(const CFX_FloatRect& rect, + << " l S\n"; + } + break; +- case BorderStyle::BEVELED: +- case BorderStyle::INSET: +- sColor = GenerateColorAP(crLeftTop, PaintOperation::FILL); ++ case BorderStyle::kBeveled: ++ case BorderStyle::kInset: ++ sColor = GenerateColorAP(crLeftTop, PaintOperation::kFill); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth +@@ -268,7 +268,7 @@ ByteString GenerateBorderAP(const CFX_FloatRect& rect, + sAppStream << fLeft + fHalfWidth * 2 << " " + << fBottom + fHalfWidth * 2 << " l f\n"; + } +- sColor = GenerateColorAP(crRightBottom, PaintOperation::FILL); ++ sColor = GenerateColorAP(crRightBottom, PaintOperation::kFill); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth +@@ -284,7 +284,7 @@ ByteString GenerateBorderAP(const CFX_FloatRect& rect, + sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2 + << " l f\n"; + } +- sColor = GenerateColorAP(color, PaintOperation::FILL); ++ sColor = GenerateColorAP(color, PaintOperation::kFill); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " " +@@ -294,8 +294,8 @@ ByteString GenerateBorderAP(const CFX_FloatRect& rect, + << fTop - fBottom - fHalfWidth * 2 << " re f*\n"; + } + break; +- case BorderStyle::UNDERLINE: +- sColor = GenerateColorAP(color, PaintOperation::STROKE); ++ case BorderStyle::kUnderline: ++ sColor = GenerateColorAP(color, PaintOperation::kStroke); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fWidth << " w\n"; +@@ -308,7 +308,7 @@ ByteString GenerateBorderAP(const CFX_FloatRect& rect, + return ByteString(sAppStream); + } + +-ByteString GetColorStringWithDefault(CPDF_Array* pColor, ++ByteString GetColorStringWithDefault(const CPDF_Array* pColor, + const CFX_Color& crDefaultColor, + PaintOperation nOperation) { + if (pColor) { +@@ -319,48 +319,43 @@ ByteString GetColorStringWithDefault(CPDF_Array* pColor, + return GenerateColorAP(crDefaultColor, nOperation); + } + +-float GetBorderWidth(const CPDF_Dictionary& pAnnotDict) { +- if (const CPDF_Dictionary* pBorderStyleDict = pAnnotDict.GetDictFor("BS")) { +- if (pBorderStyleDict->KeyExist("W")) +- return pBorderStyleDict->GetNumberFor("W"); +- } ++float GetBorderWidth(const CPDF_Dictionary* pDict) { ++ RetainPtr pBorderStyleDict = pDict->GetDictFor("BS"); ++ if (pBorderStyleDict && pBorderStyleDict->KeyExist("W")) ++ return pBorderStyleDict->GetFloatFor("W"); + +- if (const CPDF_Array* pBorderArray = +- pAnnotDict.GetArrayFor(pdfium::annotation::kBorder)) { +- if (pBorderArray->size() > 2) +- return pBorderArray->GetNumberAt(2); +- } ++ auto pBorderArray = pDict->GetArrayFor(pdfium::annotation::kBorder); ++ if (pBorderArray && pBorderArray->size() > 2) ++ return pBorderArray->GetFloatAt(2); + + return 1; + } + +-const CPDF_Array* GetDashArray(const CPDF_Dictionary& pAnnotDict) { +- if (const CPDF_Dictionary* pBorderStyleDict = pAnnotDict.GetDictFor("BS")) { +- if (pBorderStyleDict->GetStringFor("S") == "D") +- return pBorderStyleDict->GetArrayFor("D"); +- } ++RetainPtr GetDashArray(const CPDF_Dictionary* pDict) { ++ RetainPtr pBorderStyleDict = pDict->GetDictFor("BS"); ++ if (pBorderStyleDict && pBorderStyleDict->GetByteStringFor("S") == "D") ++ return pBorderStyleDict->GetArrayFor("D"); + +- if (const CPDF_Array* pBorderArray = +- pAnnotDict.GetArrayFor(pdfium::annotation::kBorder)) { +- if (pBorderArray->size() == 4) +- return pBorderArray->GetArrayAt(3); +- } ++ RetainPtr pBorderArray = ++ pDict->GetArrayFor(pdfium::annotation::kBorder); ++ if (pBorderArray && pBorderArray->size() == 4) ++ return pBorderArray->GetArrayAt(3); + + return nullptr; + } + +-ByteString GetDashPatternString(const CPDF_Dictionary& pAnnotDict) { +- const CPDF_Array* pDashArray = GetDashArray(pAnnotDict); ++ByteString GetDashPatternString(const CPDF_Dictionary* pDict) { ++ RetainPtr pDashArray = GetDashArray(pDict); + if (!pDashArray || pDashArray->IsEmpty()) + return ByteString(); + + // Support maximum of ten elements in the dash array. + size_t pDashArrayCount = std::min(pDashArray->size(), 10); +- std::ostringstream sDashStream; ++ fxcrt::ostringstream sDashStream; + + sDashStream << "["; + for (size_t i = 0; i < pDashArrayCount; ++i) +- sDashStream << pDashArray->GetNumberAt(i) << " "; ++ sDashStream << pDashArray->GetFloatAt(i) << " "; + sDashStream << "] 0 d\n"; + + return ByteString(sDashStream); +@@ -368,24 +363,23 @@ ByteString GetDashPatternString(const CPDF_Dictionary& pAnnotDict) { + + ByteString GetPopupContentsString(CPDF_Document* pDoc, + const CPDF_Dictionary& pAnnotDict, +- const RetainPtr& pDefFont, ++ RetainPtr pDefFont, + const ByteString& sFontName) { + WideString swValue(pAnnotDict.GetUnicodeTextFor(pdfium::form_fields::kT)); + swValue += L'\n'; + swValue += pAnnotDict.GetUnicodeTextFor(pdfium::annotation::kContents); +- CPVT_FontMap map(pDoc, nullptr, pDefFont, sFontName); + +- CPDF_VariableText::Provider prd(&map); +- CPDF_VariableText vt; +- vt.SetProvider(&prd); ++ CPVT_FontMap map(pDoc, nullptr, std::move(pDefFont), sFontName); ++ CPVT_VariableText::Provider prd(&map); ++ CPVT_VariableText vt(&prd); + vt.SetPlateRect(pAnnotDict.GetRectFor(pdfium::annotation::kRect)); + vt.SetFontSize(12); + vt.SetAutoReturn(true); + vt.SetMultiLine(true); +- + vt.Initialize(); + vt.SetText(swValue); + vt.RearrangeAll(); ++ + CFX_PointF ptOffset(3.0f, -3.0f); + ByteString sContent = + GenerateEditAP(&map, vt.GetIterator(), ptOffset, false, 0); +@@ -393,23 +387,22 @@ ByteString GetPopupContentsString(CPDF_Document* pDoc, + if (sContent.IsEmpty()) + return ByteString(); + +- std::ostringstream sAppStream; +- sAppStream << "BT\n" +- << GenerateColorAP(CFX_Color(CFX_Color::kRGB, 0, 0, 0), +- PaintOperation::FILL) +- << sContent << "ET\n" +- << "Q\n"; +- return ByteString(sAppStream); ++ ByteString sColorAP = GenerateColorAP( ++ CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kFill); ++ ++ return ByteString{"BT\n", sColorAP.AsStringView(), sContent.AsStringView(), ++ "ET\n", "Q\n"}; + } + + RetainPtr GenerateResourceFontDict( + CPDF_Document* pDoc, + const ByteString& sFontDictName) { +- CPDF_Dictionary* pFontDict = pDoc->NewIndirect(); ++ auto pFontDict = pDoc->NewIndirect(); + pFontDict->SetNewFor("Type", "Font"); + pFontDict->SetNewFor("Subtype", "Type1"); + pFontDict->SetNewFor("BaseFont", CFX_Font::kDefaultAnsiFontName); +- pFontDict->SetNewFor("Encoding", "WinAnsiEncoding"); ++ pFontDict->SetNewFor("Encoding", ++ pdfium::font_encodings::kWinAnsiEncoding); + + auto pResourceFontDict = pDoc->New(); + pResourceFontDict->SetNewFor(sFontDictName, pDoc, +@@ -424,11 +417,11 @@ ByteString GetPaintOperatorString(bool bIsStrokeRect, bool bIsFillRect) { + } + + ByteString GenerateTextSymbolAP(const CFX_FloatRect& rect) { +- std::ostringstream sAppStream; +- sAppStream << GenerateColorAP(CFX_Color(CFX_Color::kRGB, 1, 1, 0), +- PaintOperation::FILL); +- sAppStream << GenerateColorAP(CFX_Color(CFX_Color::kRGB, 0, 0, 0), +- PaintOperation::STROKE); ++ fxcrt::ostringstream sAppStream; ++ sAppStream << GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 1, 1, 0), ++ PaintOperation::kFill); ++ sAppStream << GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), ++ PaintOperation::kStroke); + + const float fBorderWidth = 1; + sAppStream << fBorderWidth << " w\n"; +@@ -481,8 +474,7 @@ RetainPtr GenerateExtGStateDict( + pdfium::MakeRetain(pAnnotDict.GetByteStringPool()); + pGSDict->SetNewFor("Type", "ExtGState"); + +- float fOpacity = +- pAnnotDict.KeyExist("CA") ? pAnnotDict.GetNumberFor("CA") : 1; ++ float fOpacity = pAnnotDict.KeyExist("CA") ? pAnnotDict.GetFloatFor("CA") : 1; + pGSDict->SetNewFor("CA", fOpacity); + pGSDict->SetNewFor("ca", fOpacity); + pGSDict->SetNewFor("AIS", false); +@@ -508,19 +500,17 @@ RetainPtr GenerateResourceDict( + + void GenerateAndSetAPDict(CPDF_Document* pDoc, + CPDF_Dictionary* pAnnotDict, +- std::ostringstream* psAppStream, ++ fxcrt::ostringstream* psAppStream, + RetainPtr pResourceDict, + bool bIsTextMarkupAnnotation) { +- CPDF_Stream* pNormalStream = pDoc->NewIndirect(); ++ auto pNormalStream = pDoc->NewIndirect(); + pNormalStream->SetDataFromStringstream(psAppStream); + +- CPDF_Dictionary* pAPDict = pAnnotDict->GetDictFor(pdfium::annotation::kAP); +- if (!pAPDict) +- pAPDict = pAnnotDict->SetNewFor(pdfium::annotation::kAP); +- ++ RetainPtr pAPDict = ++ pAnnotDict->GetOrCreateDictFor(pdfium::annotation::kAP); + pAPDict->SetNewFor("N", pDoc, pNormalStream->GetObjNum()); + +- CPDF_Dictionary* pStreamDict = pNormalStream->GetDict(); ++ RetainPtr pStreamDict = pNormalStream->GetMutableDict(); + pStreamDict->SetNewFor("FormType", 1); + pStreamDict->SetNewFor("Type", "XObject"); + pStreamDict->SetNewFor("Subtype", "Form"); +@@ -534,33 +524,34 @@ void GenerateAndSetAPDict(CPDF_Document* pDoc, + } + + bool GenerateCircleAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { +- std::ostringstream sAppStream; ++ fxcrt::ostringstream sAppStream; + ByteString sExtGSDictName = "GS"; + sAppStream << "/" << sExtGSDictName << " gs "; + +- CPDF_Array* pInteriorColor = pAnnotDict->GetArrayFor("IC"); ++ RetainPtr pInteriorColor = pAnnotDict->GetArrayFor("IC"); + sAppStream << GetColorStringWithDefault( +- pInteriorColor, CFX_Color(CFX_Color::kTransparent), PaintOperation::FILL); ++ pInteriorColor.Get(), CFX_Color(CFX_Color::Type::kTransparent), ++ PaintOperation::kFill); + + sAppStream << GetColorStringWithDefault( +- pAnnotDict->GetArrayFor(pdfium::annotation::kC), +- CFX_Color(CFX_Color::kRGB, 0, 0, 0), PaintOperation::STROKE); ++ pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(), ++ CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke); + +- float fBorderWidth = GetBorderWidth(*pAnnotDict); ++ float fBorderWidth = GetBorderWidth(pAnnotDict); + bool bIsStrokeRect = fBorderWidth > 0; + + if (bIsStrokeRect) { + sAppStream << fBorderWidth << " w "; +- sAppStream << GetDashPatternString(*pAnnotDict); ++ sAppStream << GetDashPatternString(pAnnotDict); + } + + CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect); + rect.Normalize(); + + if (bIsStrokeRect) { +- // Deflating rect because stroking a path entails painting all points whose +- // perpendicular distance from the path in user space is less than or equal +- // to half the line width. ++ // Deflating rect because stroking a path entails painting all points ++ // whose perpendicular distance from the path in user space is less than ++ // or equal to half the line width. + rect.Deflate(fBorderWidth / 2, fBorderWidth / 2); + } + +@@ -606,17 +597,17 @@ bool GenerateCircleAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { + } + + bool GenerateHighlightAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { +- std::ostringstream sAppStream; ++ fxcrt::ostringstream sAppStream; + ByteString sExtGSDictName = "GS"; + sAppStream << "/" << sExtGSDictName << " gs "; + + sAppStream << GetColorStringWithDefault( +- pAnnotDict->GetArrayFor(pdfium::annotation::kC), +- CFX_Color(CFX_Color::kRGB, 1, 1, 0), PaintOperation::FILL); ++ pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(), ++ CFX_Color(CFX_Color::Type::kRGB, 1, 1, 0), PaintOperation::kFill); + +- CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints"); ++ RetainPtr pArray = pAnnotDict->GetArrayFor("QuadPoints"); + if (pArray) { +- size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray); ++ size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray.Get()); + for (size_t i = 0; i < nQuadPointCount; ++i) { + CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i); + rect.Normalize(); +@@ -638,26 +629,24 @@ bool GenerateHighlightAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { + } + + bool GenerateInkAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { +- float fBorderWidth = GetBorderWidth(*pAnnotDict); +- bool bIsStroke = fBorderWidth > 0; +- +- if (!bIsStroke) ++ RetainPtr pInkList = pAnnotDict->GetArrayFor("InkList"); ++ if (!pInkList || pInkList->IsEmpty()) + return false; + +- CPDF_Array* pInkList = pAnnotDict->GetArrayFor("InkList"); +- if (!pInkList || pInkList->IsEmpty()) ++ float fBorderWidth = GetBorderWidth(pAnnotDict); ++ const bool bIsStroke = fBorderWidth > 0; ++ if (!bIsStroke) + return false; + +- std::ostringstream sAppStream; + ByteString sExtGSDictName = "GS"; ++ fxcrt::ostringstream sAppStream; + sAppStream << "/" << sExtGSDictName << " gs "; +- + sAppStream << GetColorStringWithDefault( +- pAnnotDict->GetArrayFor(pdfium::annotation::kC), +- CFX_Color(CFX_Color::kRGB, 0, 0, 0), PaintOperation::STROKE); ++ pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(), ++ CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke); + + sAppStream << fBorderWidth << " w "; +- sAppStream << GetDashPatternString(*pAnnotDict); ++ sAppStream << GetDashPatternString(pAnnotDict); + + // Set inflated rect as a new rect because paths near the border with large + // width should not be clipped to the original rect. +@@ -666,16 +655,16 @@ bool GenerateInkAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { + pAnnotDict->SetRectFor(pdfium::annotation::kRect, rect); + + for (size_t i = 0; i < pInkList->size(); i++) { +- CPDF_Array* pInkCoordList = pInkList->GetArrayAt(i); ++ RetainPtr pInkCoordList = pInkList->GetArrayAt(i); + if (!pInkCoordList || pInkCoordList->size() < 2) + continue; + +- sAppStream << pInkCoordList->GetNumberAt(0) << " " +- << pInkCoordList->GetNumberAt(1) << " m "; ++ sAppStream << pInkCoordList->GetFloatAt(0) << " " ++ << pInkCoordList->GetFloatAt(1) << " m "; + + for (size_t j = 0; j < pInkCoordList->size() - 1; j += 2) { +- sAppStream << pInkCoordList->GetNumberAt(j) << " " +- << pInkCoordList->GetNumberAt(j + 1) << " l "; ++ sAppStream << pInkCoordList->GetFloatAt(j) << " " ++ << pInkCoordList->GetFloatAt(j + 1) << " l "; + } + + sAppStream << "S\n"; +@@ -691,7 +680,7 @@ bool GenerateInkAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { + } + + bool GenerateTextAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { +- std::ostringstream sAppStream; ++ fxcrt::ostringstream sAppStream; + ByteString sExtGSDictName = "GS"; + sAppStream << "/" << sExtGSDictName << " gs "; + +@@ -713,19 +702,19 @@ bool GenerateTextAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { + } + + bool GenerateUnderlineAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { +- std::ostringstream sAppStream; ++ fxcrt::ostringstream sAppStream; + ByteString sExtGSDictName = "GS"; + sAppStream << "/" << sExtGSDictName << " gs "; + + sAppStream << GetColorStringWithDefault( +- pAnnotDict->GetArrayFor(pdfium::annotation::kC), +- CFX_Color(CFX_Color::kRGB, 0, 0, 0), PaintOperation::STROKE); ++ pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(), ++ CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke); + +- CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints"); ++ RetainPtr pArray = pAnnotDict->GetArrayFor("QuadPoints"); + if (pArray) { + static constexpr float kLineWidth = 1.0f; + sAppStream << kLineWidth << " w "; +- size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray); ++ size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray.Get()); + for (size_t i = 0; i < nQuadPointCount; ++i) { + CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i); + rect.Normalize(); +@@ -744,14 +733,14 @@ bool GenerateUnderlineAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { + } + + bool GeneratePopupAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { +- std::ostringstream sAppStream; ++ fxcrt::ostringstream sAppStream; + ByteString sExtGSDictName = "GS"; + sAppStream << "/" << sExtGSDictName << " gs\n"; + +- sAppStream << GenerateColorAP(CFX_Color(CFX_Color::kRGB, 1, 1, 0), +- PaintOperation::FILL); +- sAppStream << GenerateColorAP(CFX_Color(CFX_Color::kRGB, 0, 0, 0), +- PaintOperation::STROKE); ++ sAppStream << GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 1, 1, 0), ++ PaintOperation::kFill); ++ sAppStream << GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), ++ PaintOperation::kStroke); + + const float fBorderWidth = 1; + sAppStream << fBorderWidth << " w\n"; +@@ -768,7 +757,7 @@ bool GeneratePopupAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { + GenerateResourceFontDict(pDoc, sFontName); + + auto* pData = CPDF_DocPageData::FromDocument(pDoc); +- RetainPtr pDefFont = pData->GetFont(pResourceFontDict.Get()); ++ RetainPtr pDefFont = pData->GetFont(pResourceFontDict); + if (!pDefFont) + return false; + +@@ -777,45 +766,45 @@ bool GeneratePopupAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { + RetainPtr pResourceDict = GenerateResourceDict( + pDoc, std::move(pExtGStateDict), std::move(pResourceFontDict)); + +- sAppStream << GetPopupContentsString(pDoc, *pAnnotDict, pDefFont, sFontName); ++ sAppStream << GetPopupContentsString(pDoc, *pAnnotDict, std::move(pDefFont), ++ sFontName); + GenerateAndSetAPDict(pDoc, pAnnotDict, &sAppStream, std::move(pResourceDict), + false /*IsTextMarkupAnnotation*/); + return true; + } + + bool GenerateSquareAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { +- std::ostringstream sAppStream; +- ByteString sExtGSDictName = "GS"; ++ const ByteString sExtGSDictName = "GS"; ++ fxcrt::ostringstream sAppStream; + sAppStream << "/" << sExtGSDictName << " gs "; + +- CPDF_Array* pInteriorColor = pAnnotDict->GetArrayFor("IC"); ++ RetainPtr pInteriorColor = pAnnotDict->GetArrayFor("IC"); + sAppStream << GetColorStringWithDefault( +- pInteriorColor, CFX_Color(CFX_Color::kTransparent), PaintOperation::FILL); ++ pInteriorColor.Get(), CFX_Color(CFX_Color::Type::kTransparent), ++ PaintOperation::kFill); + + sAppStream << GetColorStringWithDefault( +- pAnnotDict->GetArrayFor(pdfium::annotation::kC), +- CFX_Color(CFX_Color::kRGB, 0, 0, 0), PaintOperation::STROKE); +- +- float fBorderWidth = GetBorderWidth(*pAnnotDict); +- bool bIsStrokeRect = fBorderWidth > 0; ++ pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(), ++ CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke); + ++ float fBorderWidth = GetBorderWidth(pAnnotDict); ++ const bool bIsStrokeRect = fBorderWidth > 0; + if (bIsStrokeRect) { + sAppStream << fBorderWidth << " w "; +- sAppStream << GetDashPatternString(*pAnnotDict); ++ sAppStream << GetDashPatternString(pAnnotDict); + } + + CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect); + rect.Normalize(); + + if (bIsStrokeRect) { +- // Deflating rect because stroking a path entails painting all points whose +- // perpendicular distance from the path in user space is less than or equal +- // to half the line width. ++ // Deflating rect because stroking a path entails painting all points ++ // whose perpendicular distance from the path in user space is less than ++ // or equal to half the line width. + rect.Deflate(fBorderWidth / 2, fBorderWidth / 2); + } + +- bool bIsFillRect = pInteriorColor && (pInteriorColor->size() > 0); +- ++ const bool bIsFillRect = pInteriorColor && (pInteriorColor->size() > 0); + sAppStream << rect.left << " " << rect.bottom << " " << rect.Width() << " " + << rect.Height() << " re " + << GetPaintOperatorString(bIsStrokeRect, bIsFillRect) << "\n"; +@@ -830,20 +819,20 @@ bool GenerateSquareAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { + } + + bool GenerateSquigglyAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { +- std::ostringstream sAppStream; ++ fxcrt::ostringstream sAppStream; + ByteString sExtGSDictName = "GS"; + sAppStream << "/" << sExtGSDictName << " gs "; + + sAppStream << GetColorStringWithDefault( +- pAnnotDict->GetArrayFor(pdfium::annotation::kC), +- CFX_Color(CFX_Color::kRGB, 0, 0, 0), PaintOperation::STROKE); ++ pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(), ++ CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke); + +- CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints"); ++ RetainPtr pArray = pAnnotDict->GetArrayFor("QuadPoints"); + if (pArray) { + static constexpr float kLineWidth = 1.0f; + static constexpr float kDelta = 2.0f; + sAppStream << kLineWidth << " w "; +- size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray); ++ size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray.Get()); + for (size_t i = 0; i < nQuadPointCount; ++i) { + CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i); + rect.Normalize(); +@@ -880,18 +869,18 @@ bool GenerateSquigglyAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { + } + + bool GenerateStrikeOutAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { +- std::ostringstream sAppStream; ++ fxcrt::ostringstream sAppStream; + ByteString sExtGSDictName = "GS"; + sAppStream << "/" << sExtGSDictName << " gs "; + + sAppStream << GetColorStringWithDefault( +- pAnnotDict->GetArrayFor(pdfium::annotation::kC), +- CFX_Color(CFX_Color::kRGB, 0, 0, 0), PaintOperation::STROKE); ++ pAnnotDict->GetArrayFor(pdfium::annotation::kC).Get(), ++ CFX_Color(CFX_Color::Type::kRGB, 0, 0, 0), PaintOperation::kStroke); + +- CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints"); ++ RetainPtr pArray = pAnnotDict->GetArrayFor("QuadPoints"); + if (pArray) { + static constexpr float kLineWidth = 1.0f; +- size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray); ++ size_t nQuadPointCount = CPDF_Annot::QuadPointCount(pArray.Get()); + for (size_t i = 0; i < nQuadPointCount; ++i) { + CFX_FloatRect rect = CPDF_Annot::RectFromQuadPoints(pAnnotDict, i); + rect.Normalize(); +@@ -914,49 +903,55 @@ bool GenerateStrikeOutAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict) { + } // namespace + + // static +-void CPVT_GenerateAP::GenerateFormAP(CPDF_Document* pDoc, ++void CPDF_GenerateAP::GenerateFormAP(CPDF_Document* pDoc, + CPDF_Dictionary* pAnnotDict, + FormType type) { +- CPDF_Dictionary* pRootDict = pDoc->GetRoot(); ++ RetainPtr pRootDict = pDoc->GetMutableRoot(); + if (!pRootDict) + return; + +- CPDF_Dictionary* pFormDict = pRootDict->GetDictFor("AcroForm"); ++ RetainPtr pFormDict = ++ pRootDict->GetMutableDictFor("AcroForm"); + if (!pFormDict) + return; + + ByteString DA; +- if (CPDF_Object* pDAObj = CPDF_FormField::GetFieldAttr(pAnnotDict, "DA")) ++ RetainPtr pDAObj = ++ CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "DA"); ++ if (pDAObj) + DA = pDAObj->GetString(); + if (DA.IsEmpty()) +- DA = pFormDict->GetStringFor("DA"); ++ DA = pFormDict->GetByteStringFor("DA"); + if (DA.IsEmpty()) + return; + + CPDF_DefaultAppearance appearance(DA); + + float fFontSize = 0; +- Optional font = appearance.GetFont(&fFontSize); +- if (!font) ++ absl::optional font = appearance.GetFont(&fFontSize); ++ if (!font.has_value()) + return; + +- ByteString font_name = *font; ++ ByteString font_name = font.value(); ++ + CFX_Color crText = fpdfdoc::CFXColorFromString(DA); +- CPDF_Dictionary* pDRDict = pFormDict->GetDictFor("DR"); ++ RetainPtr pDRDict = pFormDict->GetMutableDictFor("DR"); + if (!pDRDict) + return; + +- CPDF_Dictionary* pDRFontDict = pDRDict->GetDictFor("Font"); +- if (!ValidateFontResourceDict(pDRFontDict)) ++ RetainPtr pDRFontDict = pDRDict->GetMutableDictFor("Font"); ++ if (!ValidateFontResourceDict(pDRFontDict.Get())) + return; + +- CPDF_Dictionary* pFontDict = pDRFontDict->GetDictFor(font_name); ++ RetainPtr pFontDict = ++ pDRFontDict->GetMutableDictFor(font_name); + if (!pFontDict) { + pFontDict = pDoc->NewIndirect(); + pFontDict->SetNewFor("Type", "Font"); + pFontDict->SetNewFor("Subtype", "Type1"); + pFontDict->SetNewFor("BaseFont", CFX_Font::kDefaultAnsiFontName); +- pFontDict->SetNewFor("Encoding", "WinAnsiEncoding"); ++ pFontDict->SetNewFor("Encoding", ++ pdfium::font_encodings::kWinAnsiEncoding); + pDRFontDict->SetNewFor(font_name, pDoc, + pFontDict->GetObjNum()); + } +@@ -966,8 +961,9 @@ void CPVT_GenerateAP::GenerateFormAP(CPDF_Document* pDoc, + return; + + CFX_FloatRect rcAnnot = pAnnotDict->GetRectFor(pdfium::annotation::kRect); +- CPDF_Dictionary* pMKDict = pAnnotDict->GetDictFor("MK"); +- int32_t nRotate = pMKDict ? pMKDict->GetIntegerFor("R") : 0; ++ RetainPtr pMKDict = pAnnotDict->GetDictFor("MK"); ++ int32_t nRotate = ++ pMKDict ? pMKDict->GetIntegerFor(pdfium::appearance::kR) : 0; + + CFX_FloatRect rcBBox; + CFX_Matrix matrix; +@@ -994,41 +990,41 @@ void CPVT_GenerateAP::GenerateFormAP(CPDF_Document* pDoc, + break; + } + +- BorderStyle nBorderStyle = BorderStyle::SOLID; ++ BorderStyle nBorderStyle = BorderStyle::kSolid; + float fBorderWidth = 1; + CPVT_Dash dsBorder(3, 0, 0); + CFX_Color crLeftTop; + CFX_Color crRightBottom; +- if (CPDF_Dictionary* pBSDict = pAnnotDict->GetDictFor("BS")) { ++ if (RetainPtr pBSDict = pAnnotDict->GetDictFor("BS")) { + if (pBSDict->KeyExist("W")) +- fBorderWidth = pBSDict->GetNumberFor("W"); ++ fBorderWidth = pBSDict->GetFloatFor("W"); + +- if (CPDF_Array* pArray = pBSDict->GetArrayFor("D")) { ++ if (RetainPtr pArray = pBSDict->GetArrayFor("D")) { + dsBorder = CPVT_Dash(pArray->GetIntegerAt(0), pArray->GetIntegerAt(1), + pArray->GetIntegerAt(2)); + } +- if (pBSDict->GetStringFor("S").GetLength()) { +- switch (pBSDict->GetStringFor("S")[0]) { ++ if (pBSDict->GetByteStringFor("S").GetLength()) { ++ switch (pBSDict->GetByteStringFor("S")[0]) { + case 'S': +- nBorderStyle = BorderStyle::SOLID; ++ nBorderStyle = BorderStyle::kSolid; + break; + case 'D': +- nBorderStyle = BorderStyle::DASH; ++ nBorderStyle = BorderStyle::kDash; + break; + case 'B': +- nBorderStyle = BorderStyle::BEVELED; ++ nBorderStyle = BorderStyle::kBeveled; + fBorderWidth *= 2; +- crLeftTop = CFX_Color(CFX_Color::kGray, 1); +- crRightBottom = CFX_Color(CFX_Color::kGray, 0.5); ++ crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1); ++ crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.5); + break; + case 'I': +- nBorderStyle = BorderStyle::INSET; ++ nBorderStyle = BorderStyle::kInset; + fBorderWidth *= 2; +- crLeftTop = CFX_Color(CFX_Color::kGray, 0.5); +- crRightBottom = CFX_Color(CFX_Color::kGray, 0.75); ++ crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5); ++ crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75); + break; + case 'U': +- nBorderStyle = BorderStyle::UNDERLINE; ++ nBorderStyle = BorderStyle::kUnderline; + break; + } + } +@@ -1036,13 +1032,16 @@ void CPVT_GenerateAP::GenerateFormAP(CPDF_Document* pDoc, + CFX_Color crBorder; + CFX_Color crBG; + if (pMKDict) { +- if (CPDF_Array* pArray = pMKDict->GetArrayFor("BC")) ++ RetainPtr pArray = ++ pMKDict->GetArrayFor(pdfium::appearance::kBC); ++ if (pArray) + crBorder = fpdfdoc::CFXColorFromArray(*pArray); +- if (CPDF_Array* pArray = pMKDict->GetArrayFor("BG")) ++ pArray = pMKDict->GetArrayFor(pdfium::appearance::kBG); ++ if (pArray) + crBG = fpdfdoc::CFXColorFromArray(*pArray); + } +- std::ostringstream sAppStream; +- ByteString sBG = GenerateColorAP(crBG, PaintOperation::FILL); ++ fxcrt::ostringstream sAppStream; ++ ByteString sBG = GenerateColorAP(crBG, PaintOperation::kFill); + if (sBG.GetLength() > 0) { + sAppStream << "q\n" + << sBG << rcBBox.left << " " << rcBBox.bottom << " " +@@ -1060,22 +1059,22 @@ void CPVT_GenerateAP::GenerateFormAP(CPDF_Document* pDoc, + rcBBox.right - fBorderWidth, rcBBox.top - fBorderWidth); + rcBody.Normalize(); + +- CPDF_Dictionary* pAPDict = pAnnotDict->GetDictFor(pdfium::annotation::kAP); +- if (!pAPDict) +- pAPDict = pAnnotDict->SetNewFor(pdfium::annotation::kAP); +- +- CPDF_Stream* pNormalStream = pAPDict->GetStreamFor("N"); ++ RetainPtr pAPDict = ++ pAnnotDict->GetOrCreateDictFor(pdfium::annotation::kAP); ++ RetainPtr pNormalStream = pAPDict->GetMutableStreamFor("N"); + if (!pNormalStream) { + pNormalStream = pDoc->NewIndirect(); + pAPDict->SetNewFor("N", pDoc, pNormalStream->GetObjNum()); + } +- CPDF_Dictionary* pStreamDict = pNormalStream->GetDict(); ++ RetainPtr pStreamDict = pNormalStream->GetMutableDict(); + if (pStreamDict) { +- CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources"); ++ RetainPtr pStreamResList = ++ pStreamDict->GetMutableDictFor("Resources"); + if (pStreamResList) { +- CPDF_Dictionary* pStreamResFontList = pStreamResList->GetDictFor("Font"); ++ RetainPtr pStreamResFontList = ++ pStreamResList->GetMutableDictFor("Font"); + if (pStreamResFontList) { +- if (!ValidateFontResourceDict(pStreamResFontList)) ++ if (!ValidateFontResourceDict(pStreamResFontList.Get())) + return; + } else { + pStreamResFontList = pStreamResList->SetNewFor("Font"); +@@ -1090,28 +1089,29 @@ void CPVT_GenerateAP::GenerateFormAP(CPDF_Document* pDoc, + pStreamDict->SetMatrixFor("Matrix", matrix); + pStreamDict->SetRectFor("BBox", rcBBox); + } ++ CPVT_FontMap map( ++ pDoc, pStreamDict ? pStreamDict->GetMutableDictFor("Resources") : nullptr, ++ std::move(pDefFont), font_name); ++ CPVT_VariableText::Provider prd(&map); ++ + switch (type) { +- case CPVT_GenerateAP::kTextField: { +- const CPDF_Object* pV = +- CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kV); ++ case CPDF_GenerateAP::kTextField: { ++ RetainPtr pV = CPDF_FormField::GetFieldAttrForDict( ++ pAnnotDict, pdfium::form_fields::kV); + WideString swValue = pV ? pV->GetUnicodeText() : WideString(); +- const CPDF_Object* pQ = CPDF_FormField::GetFieldAttr(pAnnotDict, "Q"); ++ RetainPtr pQ = ++ CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "Q"); + int32_t nAlign = pQ ? pQ->GetInteger() : 0; +- const CPDF_Object* pFf = +- CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kFf); ++ RetainPtr pFf = CPDF_FormField::GetFieldAttrForDict( ++ pAnnotDict, pdfium::form_fields::kFf); + uint32_t dwFlags = pFf ? pFf->GetInteger() : 0; +- const CPDF_Object* pMaxLen = +- CPDF_FormField::GetFieldAttr(pAnnotDict, "MaxLen"); ++ RetainPtr pMaxLen = ++ CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "MaxLen"); + uint32_t dwMaxLen = pMaxLen ? pMaxLen->GetInteger() : 0; +- CPVT_FontMap map( +- pDoc, pStreamDict ? pStreamDict->GetDictFor("Resources") : nullptr, +- pDefFont, font_name); +- CPDF_VariableText::Provider prd(&map); +- CPDF_VariableText vt; +- vt.SetProvider(&prd); ++ CPVT_VariableText vt(&prd); + vt.SetPlateRect(rcBody); + vt.SetAlignment(nAlign); +- if (IsFloatZero(fFontSize)) ++ if (FXSYS_IsFloatZero(fFontSize)) + vt.SetAutoFontSize(true); + else + vt.SetFontSize(fFontSize); +@@ -1153,22 +1153,17 @@ void CPVT_GenerateAP::GenerateFormAP(CPDF_Document* pDoc, + << " re\nW\nn\n"; + } + sAppStream << "BT\n" +- << GenerateColorAP(crText, PaintOperation::FILL) << sBody ++ << GenerateColorAP(crText, PaintOperation::kFill) << sBody + << "ET\n" + << "Q\nEMC\n"; + } + break; + } +- case CPVT_GenerateAP::kComboBox: { +- const CPDF_Object* pV = +- CPDF_FormField::GetFieldAttr(pAnnotDict, pdfium::form_fields::kV); ++ case CPDF_GenerateAP::kComboBox: { ++ RetainPtr pV = CPDF_FormField::GetFieldAttrForDict( ++ pAnnotDict, pdfium::form_fields::kV); + WideString swValue = pV ? pV->GetUnicodeText() : WideString(); +- CPVT_FontMap map( +- pDoc, pStreamDict ? pStreamDict->GetDictFor("Resources") : nullptr, +- pDefFont, font_name); +- CPDF_VariableText::Provider prd(&map); +- CPDF_VariableText vt; +- vt.SetProvider(&prd); ++ CPVT_VariableText vt(&prd); + CFX_FloatRect rcButton = rcBody; + rcButton.left = rcButton.right - 13; + rcButton.Normalize(); +@@ -1176,7 +1171,7 @@ void CPVT_GenerateAP::GenerateFormAP(CPDF_Document* pDoc, + rcEdit.right = rcButton.left; + rcEdit.Normalize(); + vt.SetPlateRect(rcEdit); +- if (IsFloatZero(fFontSize)) ++ if (FXSYS_IsFloatZero(fFontSize)) + vt.SetAutoFontSize(true); + else + vt.SetFontSize(fFontSize); +@@ -1195,30 +1190,31 @@ void CPVT_GenerateAP::GenerateFormAP(CPDF_Document* pDoc, + sAppStream << rcEdit.left << " " << rcEdit.bottom << " " + << rcEdit.Width() << " " << rcEdit.Height() << " re\nW\nn\n"; + sAppStream << "BT\n" +- << GenerateColorAP(crText, PaintOperation::FILL) << sEdit ++ << GenerateColorAP(crText, PaintOperation::kFill) << sEdit + << "ET\n" + << "Q\nEMC\n"; + } + ByteString sButton = +- GenerateColorAP(CFX_Color(CFX_Color::kRGB, 220.0f / 255.0f, ++ GenerateColorAP(CFX_Color(CFX_Color::Type::kRGB, 220.0f / 255.0f, + 220.0f / 255.0f, 220.0f / 255.0f), +- PaintOperation::FILL); ++ PaintOperation::kFill); + if (sButton.GetLength() > 0 && !rcButton.IsEmpty()) { + sAppStream << "q\n" << sButton; + sAppStream << rcButton.left << " " << rcButton.bottom << " " + << rcButton.Width() << " " << rcButton.Height() << " re f\n"; + sAppStream << "Q\n"; +- ByteString sButtonBorder = GenerateBorderAP( +- rcButton, 2, CFX_Color(CFX_Color::kGray, 0), +- CFX_Color(CFX_Color::kGray, 1), CFX_Color(CFX_Color::kGray, 0.5), +- BorderStyle::BEVELED, CPVT_Dash(3, 0, 0)); ++ ByteString sButtonBorder = ++ GenerateBorderAP(rcButton, 2, CFX_Color(CFX_Color::Type::kGray, 0), ++ CFX_Color(CFX_Color::Type::kGray, 1), ++ CFX_Color(CFX_Color::Type::kGray, 0.5), ++ BorderStyle::kBeveled, CPVT_Dash(3, 0, 0)); + if (sButtonBorder.GetLength() > 0) + sAppStream << "q\n" << sButtonBorder << "Q\n"; + + CFX_PointF ptCenter = CFX_PointF((rcButton.left + rcButton.right) / 2, + (rcButton.top + rcButton.bottom) / 2); +- if (IsFloatBigger(rcButton.Width(), 6) && +- IsFloatBigger(rcButton.Height(), 6)) { ++ if (FXSYS_IsFloatBigger(rcButton.Width(), 6) && ++ FXSYS_IsFloatBigger(rcButton.Height(), 6)) { + sAppStream << "q\n" + << " 0 g\n"; + sAppStream << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " m\n"; +@@ -1230,30 +1226,28 @@ void CPVT_GenerateAP::GenerateFormAP(CPDF_Document* pDoc, + } + break; + } +- case CPVT_GenerateAP::kListBox: { +- CPVT_FontMap map( +- pDoc, pStreamDict ? pStreamDict->GetDictFor("Resources") : nullptr, +- pDefFont, font_name); +- CPDF_VariableText::Provider prd(&map); +- CPDF_Array* pOpts = +- ToArray(CPDF_FormField::GetFieldAttr(pAnnotDict, "Opt")); +- CPDF_Array* pSels = +- ToArray(CPDF_FormField::GetFieldAttr(pAnnotDict, "I")); +- CPDF_Object* pTi = CPDF_FormField::GetFieldAttr(pAnnotDict, "TI"); ++ case CPDF_GenerateAP::kListBox: { ++ RetainPtr pOpts = ++ ToArray(CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "Opt")); ++ RetainPtr pSels = ++ ToArray(CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "I")); ++ RetainPtr pTi = ++ CPDF_FormField::GetFieldAttrForDict(pAnnotDict, "TI"); + int32_t nTop = pTi ? pTi->GetInteger() : 0; +- std::ostringstream sBody; ++ fxcrt::ostringstream sBody; + if (pOpts) { + float fy = rcBody.top; + for (size_t i = nTop, sz = pOpts->size(); i < sz; i++) { +- if (IsFloatSmaller(fy, rcBody.bottom)) ++ if (FXSYS_IsFloatSmaller(fy, rcBody.bottom)) + break; + +- if (CPDF_Object* pOpt = pOpts->GetDirectObjectAt(i)) { ++ if (RetainPtr pOpt = pOpts->GetDirectObjectAt(i)) { + WideString swItem; + if (pOpt->IsString()) { + swItem = pOpt->GetUnicodeText(); +- } else if (CPDF_Array* pArray = pOpt->AsArray()) { +- CPDF_Object* pDirectObj = pArray->GetDirectObjectAt(1); ++ } else if (const CPDF_Array* pArray = pOpt->AsArray()) { ++ RetainPtr pDirectObj = ++ pArray->GetDirectObjectAt(1); + if (pDirectObj) + swItem = pDirectObj->GetUnicodeText(); + } +@@ -1267,36 +1261,35 @@ void CPVT_GenerateAP::GenerateFormAP(CPDF_Document* pDoc, + } + } + } +- CPDF_VariableText vt; +- vt.SetProvider(&prd); ++ CPVT_VariableText vt(&prd); + vt.SetPlateRect( + CFX_FloatRect(rcBody.left, 0.0f, rcBody.right, 0.0f)); +- vt.SetFontSize(IsFloatZero(fFontSize) ? 12.0f : fFontSize); +- ++ vt.SetFontSize(FXSYS_IsFloatZero(fFontSize) ? 12.0f : fFontSize); + vt.Initialize(); + vt.SetText(swItem); + vt.RearrangeAll(); ++ + float fItemHeight = vt.GetContentRect().Height(); + if (bSelected) { + CFX_FloatRect rcItem = CFX_FloatRect( + rcBody.left, fy - fItemHeight, rcBody.right, fy); + sBody << "q\n" + << GenerateColorAP( +- CFX_Color(CFX_Color::kRGB, 0, 51.0f / 255.0f, ++ CFX_Color(CFX_Color::Type::kRGB, 0, 51.0f / 255.0f, + 113.0f / 255.0f), +- PaintOperation::FILL) ++ PaintOperation::kFill) + << rcItem.left << " " << rcItem.bottom << " " + << rcItem.Width() << " " << rcItem.Height() << " re f\n" + << "Q\n"; + sBody << "BT\n" +- << GenerateColorAP(CFX_Color(CFX_Color::kGray, 1), +- PaintOperation::FILL) ++ << GenerateColorAP(CFX_Color(CFX_Color::Type::kGray, 1), ++ PaintOperation::kFill) + << GenerateEditAP(&map, vt.GetIterator(), + CFX_PointF(0.0f, fy), true, 0) + << "ET\n"; + } else { + sBody << "BT\n" +- << GenerateColorAP(crText, PaintOperation::FILL) ++ << GenerateColorAP(crText, PaintOperation::kFill) + << GenerateEditAP(&map, vt.GetIterator(), + CFX_PointF(0.0f, fy), true, 0) + << "ET\n"; +@@ -1319,21 +1312,23 @@ void CPVT_GenerateAP::GenerateFormAP(CPDF_Document* pDoc, + return; + + pNormalStream->SetDataFromStringstreamAndRemoveFilter(&sAppStream); +- pStreamDict = pNormalStream->GetDict(); ++ pStreamDict = pNormalStream->GetMutableDict(); + if (!pStreamDict) + return; + + pStreamDict->SetMatrixFor("Matrix", matrix); + pStreamDict->SetRectFor("BBox", rcBBox); +- CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources"); ++ RetainPtr pStreamResList = ++ pStreamDict->GetMutableDictFor("Resources"); + if (!pStreamResList) { + pStreamDict->SetFor("Resources", pFormDict->GetDictFor("DR")->Clone()); + return; + } + +- CPDF_Dictionary* pStreamResFontList = pStreamResList->GetDictFor("Font"); ++ RetainPtr pStreamResFontList = ++ pStreamResList->GetMutableDictFor("Font"); + if (pStreamResFontList) { +- if (!ValidateFontResourceDict(pStreamResFontList)) ++ if (!ValidateFontResourceDict(pStreamResFontList.Get())) + return; + } else { + pStreamResFontList = pStreamResList->SetNewFor("Font"); +@@ -1346,19 +1341,19 @@ void CPVT_GenerateAP::GenerateFormAP(CPDF_Document* pDoc, + } + + // static +-void CPVT_GenerateAP::GenerateEmptyAP(CPDF_Document* pDoc, ++void CPDF_GenerateAP::GenerateEmptyAP(CPDF_Document* pDoc, + CPDF_Dictionary* pAnnotDict) { + auto pExtGStateDict = GenerateExtGStateDict(*pAnnotDict, "GS", "Normal"); + auto pResourceDict = + GenerateResourceDict(pDoc, std::move(pExtGStateDict), nullptr); + +- std::ostringstream sStream; ++ fxcrt::ostringstream sStream; + GenerateAndSetAPDict(pDoc, pAnnotDict, &sStream, std::move(pResourceDict), + false); + } + + // static +-bool CPVT_GenerateAP::GenerateAnnotAP(CPDF_Document* pDoc, ++bool CPDF_GenerateAP::GenerateAnnotAP(CPDF_Document* pDoc, + CPDF_Dictionary* pAnnotDict, + CPDF_Annot::Subtype subtype) { + switch (subtype) { +diff --git a/core/fpdfdoc/cpvt_generateap.h b/core/fpdfdoc/cpdf_generateap.h +similarity index 66% +rename from core/fpdfdoc/cpvt_generateap.h +rename to core/fpdfdoc/cpdf_generateap.h +index 78eebba77..520ce4f89 100644 +--- a/core/fpdfdoc/cpvt_generateap.h ++++ b/core/fpdfdoc/cpdf_generateap.h +@@ -1,19 +1,18 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#ifndef CORE_FPDFDOC_CPVT_GENERATEAP_H_ +-#define CORE_FPDFDOC_CPVT_GENERATEAP_H_ ++#ifndef CORE_FPDFDOC_CPDF_GENERATEAP_H_ ++#define CORE_FPDFDOC_CPDF_GENERATEAP_H_ + + #include "core/fpdfdoc/cpdf_annot.h" +-#include "core/fxcrt/fx_system.h" + + class CPDF_Dictionary; + class CPDF_Document; + +-class CPVT_GenerateAP { ++class CPDF_GenerateAP { + public: + enum FormType { kTextField, kComboBox, kListBox }; + +@@ -27,9 +26,9 @@ class CPVT_GenerateAP { + CPDF_Dictionary* pAnnotDict, + CPDF_Annot::Subtype subtype); + +- CPVT_GenerateAP() = delete; +- CPVT_GenerateAP(const CPVT_GenerateAP&) = delete; +- CPVT_GenerateAP& operator=(const CPVT_GenerateAP&) = delete; ++ CPDF_GenerateAP() = delete; ++ CPDF_GenerateAP(const CPDF_GenerateAP&) = delete; ++ CPDF_GenerateAP& operator=(const CPDF_GenerateAP&) = delete; + }; + +-#endif // CORE_FPDFDOC_CPVT_GENERATEAP_H_ ++#endif // CORE_FPDFDOC_CPDF_GENERATEAP_H_ +diff --git a/core/fpdfdoc/cpdf_icon.cpp b/core/fpdfdoc/cpdf_icon.cpp +index a450e1e82..8c4909a3e 100644 +--- a/core/fpdfdoc/cpdf_icon.cpp ++++ b/core/fpdfdoc/cpdf_icon.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,15 +6,18 @@ + + #include "core/fpdfdoc/cpdf_icon.h" + ++#include ++ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + +-CPDF_Icon::CPDF_Icon(CPDF_Stream* pStream) : m_pStream(pStream) {} ++CPDF_Icon::CPDF_Icon(RetainPtr pStream) ++ : m_pStream(std::move(pStream)) {} + + CPDF_Icon::~CPDF_Icon() = default; + + CFX_SizeF CPDF_Icon::GetImageSize() const { +- CPDF_Dictionary* pDict = m_pStream->GetDict(); ++ RetainPtr pDict = m_pStream->GetDict(); + if (!pDict) + return CFX_SizeF(); + +@@ -23,7 +26,7 @@ CFX_SizeF CPDF_Icon::GetImageSize() const { + } + + CFX_Matrix CPDF_Icon::GetImageMatrix() const { +- CPDF_Dictionary* pDict = m_pStream->GetDict(); ++ RetainPtr pDict = m_pStream->GetDict(); + if (!pDict) + return CFX_Matrix(); + +@@ -31,9 +34,9 @@ CFX_Matrix CPDF_Icon::GetImageMatrix() const { + } + + ByteString CPDF_Icon::GetImageAlias() const { +- CPDF_Dictionary* pDict = m_pStream->GetDict(); ++ RetainPtr pDict = m_pStream->GetDict(); + if (!pDict) + return ByteString(); + +- return pDict->GetStringFor("Name"); ++ return pDict->GetByteStringFor("Name"); + } +diff --git a/core/fpdfdoc/cpdf_icon.h b/core/fpdfdoc/cpdf_icon.h +index 519fcdad5..c1d2d27d5 100644 +--- a/core/fpdfdoc/cpdf_icon.h ++++ b/core/fpdfdoc/cpdf_icon.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,15 +7,15 @@ + #ifndef CORE_FPDFDOC_CPDF_ICON_H_ + #define CORE_FPDFDOC_CPDF_ICON_H_ + ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" + #include "core/fxcrt/retain_ptr.h" + + class CPDF_Stream; + + class CPDF_Icon final { + public: +- CPDF_Icon(CPDF_Stream* pStream); ++ explicit CPDF_Icon(RetainPtr pStream); + ~CPDF_Icon(); + + CFX_SizeF GetImageSize() const; +@@ -23,7 +23,7 @@ class CPDF_Icon final { + ByteString GetImageAlias() const; + + private: +- RetainPtr const m_pStream; ++ RetainPtr const m_pStream; + }; + + #endif // CORE_FPDFDOC_CPDF_ICON_H_ +diff --git a/core/fpdfdoc/cpdf_iconfit.cpp b/core/fpdfdoc/cpdf_iconfit.cpp +index 55702a3ea..5eb95e2ff 100644 +--- a/core/fpdfdoc/cpdf_iconfit.cpp ++++ b/core/fpdfdoc/cpdf_iconfit.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,9 @@ + + #include "core/fpdfdoc/cpdf_iconfit.h" + ++#include ++#include ++ + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fxcrt/fx_string.h" +@@ -16,7 +19,8 @@ constexpr float kDefaultPosition = 0.5f; + + } // namespace + +-CPDF_IconFit::CPDF_IconFit(const CPDF_Dictionary* pDict) : m_pDict(pDict) {} ++CPDF_IconFit::CPDF_IconFit(RetainPtr pDict) ++ : m_pDict(std::move(pDict)) {} + + CPDF_IconFit::CPDF_IconFit(const CPDF_IconFit& that) = default; + +@@ -24,20 +28,20 @@ CPDF_IconFit::~CPDF_IconFit() = default; + + CPDF_IconFit::ScaleMethod CPDF_IconFit::GetScaleMethod() const { + if (!m_pDict) +- return Always; ++ return ScaleMethod::kAlways; + +- ByteString csSW = m_pDict->GetStringFor("SW", "A"); ++ ByteString csSW = m_pDict->GetByteStringFor("SW", "A"); + if (csSW == "B") +- return Bigger; ++ return ScaleMethod::kBigger; + if (csSW == "S") +- return Smaller; ++ return ScaleMethod::kSmaller; + if (csSW == "N") +- return Never; +- return Always; ++ return ScaleMethod::kNever; ++ return ScaleMethod::kAlways; + } + + bool CPDF_IconFit::IsProportionalScale() const { +- return !m_pDict || m_pDict->GetStringFor("S", "P") != "A"; ++ return !m_pDict || m_pDict->GetByteStringFor("S", "P") != "A"; + } + + CFX_PointF CPDF_IconFit::GetIconBottomLeftPosition() const { +@@ -46,15 +50,15 @@ CFX_PointF CPDF_IconFit::GetIconBottomLeftPosition() const { + if (!m_pDict) + return {fLeft, fBottom}; + +- const CPDF_Array* pA = m_pDict->GetArrayFor("A"); ++ RetainPtr pA = m_pDict->GetArrayFor("A"); + if (!pA) + return {fLeft, fBottom}; + + size_t dwCount = pA->size(); + if (dwCount > 0) +- fLeft = pA->GetNumberAt(0); ++ fLeft = pA->GetFloatAt(0); + if (dwCount > 1) +- fBottom = pA->GetNumberAt(1); ++ fBottom = pA->GetFloatAt(1); + return {fLeft, fBottom}; + } + +@@ -66,11 +70,58 @@ CFX_PointF CPDF_IconFit::GetIconPosition() const { + if (!m_pDict) + return CFX_PointF(); + +- const CPDF_Array* pA = m_pDict->GetArrayFor("A"); ++ RetainPtr pA = m_pDict->GetArrayFor("A"); + if (!pA) + return CFX_PointF(); + + size_t dwCount = pA->size(); +- return {dwCount > 0 ? pA->GetNumberAt(0) : 0.0f, +- dwCount > 1 ? pA->GetNumberAt(1) : 0.0f}; ++ return {dwCount > 0 ? pA->GetFloatAt(0) : 0.0f, ++ dwCount > 1 ? pA->GetFloatAt(1) : 0.0f}; ++} ++ ++CFX_VectorF CPDF_IconFit::GetScale(const CFX_SizeF& image_size, ++ const CFX_FloatRect& rcPlate) const { ++ float fHScale = 1.0f; ++ float fVScale = 1.0f; ++ const float fPlateWidth = rcPlate.Width(); ++ const float fPlateHeight = rcPlate.Height(); ++ const float fImageWidth = image_size.width; ++ const float fImageHeight = image_size.height; ++ switch (GetScaleMethod()) { ++ case CPDF_IconFit::ScaleMethod::kAlways: ++ fHScale = fPlateWidth / std::max(fImageWidth, 1.0f); ++ fVScale = fPlateHeight / std::max(fImageHeight, 1.0f); ++ break; ++ case CPDF_IconFit::ScaleMethod::kBigger: ++ if (fPlateWidth < fImageWidth) ++ fHScale = fPlateWidth / std::max(fImageWidth, 1.0f); ++ if (fPlateHeight < fImageHeight) ++ fVScale = fPlateHeight / std::max(fImageHeight, 1.0f); ++ break; ++ case CPDF_IconFit::ScaleMethod::kSmaller: ++ if (fPlateWidth > fImageWidth) ++ fHScale = fPlateWidth / std::max(fImageWidth, 1.0f); ++ if (fPlateHeight > fImageHeight) ++ fVScale = fPlateHeight / std::max(fImageHeight, 1.0f); ++ break; ++ case CPDF_IconFit::ScaleMethod::kNever: ++ break; ++ } ++ ++ if (IsProportionalScale()) { ++ float min_scale = std::min(fHScale, fVScale); ++ fHScale = min_scale; ++ fVScale = min_scale; ++ } ++ return {fHScale, fVScale}; ++} ++ ++CFX_VectorF CPDF_IconFit::GetImageOffset(const CFX_SizeF& image_size, ++ const CFX_VectorF& scale, ++ const CFX_FloatRect& rcPlate) const { ++ const CFX_PointF icon_position = GetIconPosition(); ++ const float fImageFactWidth = image_size.width * scale.x; ++ const float fImageFactHeight = image_size.height * scale.y; ++ return {(rcPlate.Width() - fImageFactWidth) * icon_position.x, ++ (rcPlate.Height() - fImageFactHeight) * icon_position.y}; + } +diff --git a/core/fpdfdoc/cpdf_iconfit.h b/core/fpdfdoc/cpdf_iconfit.h +index 86a491856..3e55e1e50 100644 +--- a/core/fpdfdoc/cpdf_iconfit.h ++++ b/core/fpdfdoc/cpdf_iconfit.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,16 +8,15 @@ + #define CORE_FPDFDOC_CPDF_ICONFIT_H_ + + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" + + class CPDF_Dictionary; + + class CPDF_IconFit { + public: +- enum ScaleMethod { Always = 0, Bigger, Smaller, Never }; ++ enum class ScaleMethod { kAlways = 0, kBigger, kSmaller, kNever }; + +- explicit CPDF_IconFit(const CPDF_Dictionary* pDict); ++ explicit CPDF_IconFit(RetainPtr pDict); + CPDF_IconFit(const CPDF_IconFit& that); + ~CPDF_IconFit(); + +@@ -25,9 +24,15 @@ class CPDF_IconFit { + bool IsProportionalScale() const; + bool GetFittingBounds() const; + CFX_PointF GetIconBottomLeftPosition() const; +- CFX_PointF GetIconPosition() const; ++ CFX_VectorF GetScale(const CFX_SizeF& image_size, ++ const CFX_FloatRect& rcPlate) const; ++ CFX_VectorF GetImageOffset(const CFX_SizeF& image_size, ++ const CFX_VectorF& scale, ++ const CFX_FloatRect& rcPlate) const; + + private: ++ CFX_PointF GetIconPosition() const; ++ + RetainPtr const m_pDict; + }; + +diff --git a/core/fpdfdoc/cpdf_interactiveform.cpp b/core/fpdfdoc/cpdf_interactiveform.cpp +index a4a10257f..de7fd71f3 100644 +--- a/core/fpdfdoc/cpdf_interactiveform.cpp ++++ b/core/fpdfdoc/cpdf_interactiveform.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,6 +11,7 @@ + + #include "build/build_config.h" + #include "constants/form_fields.h" ++#include "constants/form_flags.h" + #include "constants/stream_dict_common.h" + #include "core/fpdfapi/font/cpdf_font.h" + #include "core/fpdfapi/font/cpdf_fontencoding.h" +@@ -22,24 +23,96 @@ + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_reference.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fpdfdoc/cpdf_filespec.h" + #include "core/fpdfdoc/cpdf_formcontrol.h" + #include "core/fxcrt/fx_codepage.h" +-#include "core/fxge/cfx_substfont.h" ++#include "core/fxcrt/stl_util.h" + #include "core/fxge/fx_font.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/numerics/safe_conversions.h" + + namespace { + + const int nMaxRecursion = 32; + +-void AddFont(CPDF_Dictionary*& pFormDict, +- CPDF_Document* pDocument, +- const RetainPtr& pFont, +- ByteString* csNameTag); ++#if BUILDFLAG(IS_WIN) ++struct PDF_FONTDATA { ++ bool bFind; ++ LOGFONTA lf; ++}; ++ ++int CALLBACK EnumFontFamExProc(ENUMLOGFONTEXA* lpelfe, ++ NEWTEXTMETRICEX* lpntme, ++ DWORD FontType, ++ LPARAM lParam) { ++ if (FontType != 0x004 || strchr(lpelfe->elfLogFont.lfFaceName, '@')) ++ return 1; ++ ++ PDF_FONTDATA* pData = (PDF_FONTDATA*)lParam; ++ memcpy(&pData->lf, &lpelfe->elfLogFont, sizeof(LOGFONTA)); ++ pData->bFind = true; ++ return 0; ++} ++ ++bool RetrieveSpecificFont(FX_Charset charSet, ++ LPCSTR pcsFontName, ++ LOGFONTA& lf) { ++ memset(&lf, 0, sizeof(LOGFONTA)); ++ lf.lfCharSet = static_cast(charSet); ++ lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; ++ if (pcsFontName) { ++ // TODO(dsinclair): Should this be strncpy? ++ // NOLINTNEXTLINE(runtime/printf) ++ strcpy(lf.lfFaceName, pcsFontName); ++ } ++ ++ PDF_FONTDATA fd; ++ memset(&fd, 0, sizeof(PDF_FONTDATA)); ++ HDC hDC = ::GetDC(nullptr); ++ EnumFontFamiliesExA(hDC, &lf, (FONTENUMPROCA)EnumFontFamExProc, (LPARAM)&fd, ++ 0); ++ ::ReleaseDC(nullptr, hDC); ++ if (fd.bFind) ++ memcpy(&lf, &fd.lf, sizeof(LOGFONTA)); ++ ++ return fd.bFind; ++} ++#endif // BUILDFLAG(IS_WIN) ++ ++ByteString GetNativeFontName(FX_Charset charSet, void* pLogFont) { ++ ByteString csFontName; ++#if BUILDFLAG(IS_WIN) ++ LOGFONTA lf = {}; ++ if (charSet == FX_Charset::kANSI) { ++ csFontName = CFX_Font::kDefaultAnsiFontName; ++ return csFontName; ++ } ++ bool bRet = false; ++ const ByteString default_font_name = ++ CFX_Font::GetDefaultFontNameByCharset(charSet); ++ if (!default_font_name.IsEmpty()) ++ bRet = RetrieveSpecificFont(charSet, default_font_name.c_str(), lf); ++ if (!bRet) { ++ bRet = ++ RetrieveSpecificFont(charSet, CFX_Font::kUniversalDefaultFontName, lf); ++ } ++ if (!bRet) ++ bRet = RetrieveSpecificFont(charSet, "Microsoft Sans Serif", lf); ++ if (!bRet) ++ bRet = RetrieveSpecificFont(charSet, nullptr, lf); ++ if (bRet) { ++ if (pLogFont) ++ memcpy(pLogFont, &lf, sizeof(LOGFONTA)); ++ csFontName = lf.lfFaceName; ++ } ++#endif ++ return csFontName; ++} + + ByteString GenerateNewFontResourceName(const CPDF_Dictionary* pResDict, + const ByteString& csPrefix) { +@@ -58,8 +131,8 @@ ByteString GenerateNewFontResourceName(const CPDF_Dictionary* pResDict, + m++; + } + +- const CPDF_Dictionary* pDict = pResDict->GetDictFor("Font"); +- ASSERT(pDict); ++ RetainPtr pDict = pResDict->GetDictFor("Font"); ++ DCHECK(pDict); + + int num = 0; + ByteString bsNum; +@@ -67,146 +140,57 @@ ByteString GenerateNewFontResourceName(const CPDF_Dictionary* pResDict, + ByteString csKey = csTmp + bsNum; + if (!pDict->KeyExist(csKey)) + return csKey; ++ + if (m < szCount) + csTmp += csStr[m++]; + else +- bsNum = ByteString::Format("%d", num++); +- ++ bsNum = ByteString::FormatInteger(num++); + m++; + } +- return csTmp; + } + +-void InitDict(CPDF_Dictionary*& pFormDict, CPDF_Document* pDocument) { +- if (!pDocument) +- return; +- +- if (!pFormDict) { +- pFormDict = pDocument->NewIndirect(); +- pDocument->GetRoot()->SetNewFor("AcroForm", pDocument, +- pFormDict->GetObjNum()); +- } +- +- ByteString csDA; +- if (!pFormDict->KeyExist("DR")) { +- ByteString csBaseName; +- uint8_t charSet = CPDF_InteractiveForm::GetNativeCharSet(); +- RetainPtr pFont = CPDF_InteractiveForm::AddStandardFont( +- pDocument, CFX_Font::kDefaultAnsiFontName); +- if (pFont) +- AddFont(pFormDict, pDocument, pFont, &csBaseName); +- +- if (charSet != FX_CHARSET_ANSI) { +- ByteString csFontName = +- CPDF_InteractiveForm::GetNativeFontName(charSet, nullptr); +- if (!pFont || csFontName != CFX_Font::kDefaultAnsiFontName) { +- pFont = CPDF_InteractiveForm::AddNativeFont(pDocument); +- if (pFont) { +- csBaseName.clear(); +- AddFont(pFormDict, pDocument, pFont, &csBaseName); +- } +- } +- } +- if (pFont) +- csDA = "/" + PDF_NameEncode(csBaseName) + " 0 Tf"; +- } +- if (!csDA.IsEmpty()) +- csDA += " "; +- +- csDA += "0 g"; +- if (!pFormDict->KeyExist("DA")) +- pFormDict->SetNewFor("DA", csDA, false); ++RetainPtr AddStandardFont(CPDF_Document* pDocument) { ++ auto* pPageData = CPDF_DocPageData::FromDocument(pDocument); ++ static const CPDF_FontEncoding encoding(FontEncoding::kWinAnsi); ++ return pPageData->AddStandardFont(CFX_Font::kDefaultAnsiFontName, &encoding); + } + +-RetainPtr GetFont(CPDF_Dictionary* pFormDict, +- CPDF_Document* pDocument, +- const ByteString& csNameTag) { +- ByteString csAlias = PDF_NameDecode(csNameTag.AsStringView()); +- if (!pFormDict || csAlias.IsEmpty()) +- return nullptr; +- +- CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR"); +- if (!pDR) +- return nullptr; +- +- CPDF_Dictionary* pFonts = pDR->GetDictFor("Font"); +- if (!ValidateFontResourceDict(pFonts)) +- return nullptr; +- +- CPDF_Dictionary* pElement = pFonts->GetDictFor(csAlias); +- if (!pElement || pElement->GetStringFor("Type") != "Font") +- return nullptr; ++RetainPtr AddNativeFont(FX_Charset charSet, ++ CPDF_Document* pDocument) { ++ DCHECK(pDocument); + +- return CPDF_DocPageData::FromDocument(pDocument)->GetFont(pElement); +-} +- +-RetainPtr GetNativeFont(CPDF_Dictionary* pFormDict, +- CPDF_Document* pDocument, +- uint8_t charSet, +- ByteString* csNameTag) { +- if (!pFormDict) +- return nullptr; +- +- CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR"); +- if (!pDR) +- return nullptr; +- +- CPDF_Dictionary* pFonts = pDR->GetDictFor("Font"); +- if (!ValidateFontResourceDict(pFonts)) +- return nullptr; +- +- CPDF_DictionaryLocker locker(pFonts); +- for (const auto& it : locker) { +- const ByteString& csKey = it.first; +- if (!it.second) +- continue; +- +- CPDF_Dictionary* pElement = ToDictionary(it.second->GetDirect()); +- if (!pElement || pElement->GetStringFor("Type") != "Font") +- continue; +- +- auto* pData = CPDF_DocPageData::FromDocument(pDocument); +- RetainPtr pFind = pData->GetFont(pElement); +- if (!pFind) +- continue; +- +- CFX_SubstFont* pSubst = pFind->GetSubstFont(); +- if (!pSubst) +- continue; +- +- if (pSubst->m_Charset == static_cast(charSet)) { +- *csNameTag = csKey; +- return pFind; +- } ++#if BUILDFLAG(IS_WIN) ++ LOGFONTA lf; ++ ByteString csFontName = GetNativeFontName(charSet, &lf); ++ if (!csFontName.IsEmpty()) { ++ if (csFontName == CFX_Font::kDefaultAnsiFontName) ++ return AddStandardFont(pDocument); ++ return CPDF_DocPageData::FromDocument(pDocument)->AddWindowsFont(&lf); + } ++#endif + return nullptr; + } + +-bool FindFont(CPDF_Dictionary* pFormDict, ++bool FindFont(const CPDF_Dictionary* pFormDict, + const CPDF_Font* pFont, + ByteString* csNameTag) { +- if (!pFormDict || !pFont) +- return false; +- +- CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR"); ++ RetainPtr pDR = pFormDict->GetDictFor("DR"); + if (!pDR) + return false; + +- CPDF_Dictionary* pFonts = pDR->GetDictFor("Font"); +- if (!ValidateFontResourceDict(pFonts)) ++ RetainPtr pFonts = pDR->GetDictFor("Font"); ++ // TODO(tsepez): this eventually locks the dict, pass locker instead. ++ if (!ValidateFontResourceDict(pFonts.Get())) + return false; + +- CPDF_DictionaryLocker locker(pFonts); ++ CPDF_DictionaryLocker locker(std::move(pFonts)); + for (const auto& it : locker) { + const ByteString& csKey = it.first; +- if (!it.second) +- continue; +- CPDF_Dictionary* pElement = ToDictionary(it.second->GetDirect()); +- if (!pElement) ++ RetainPtr pElement = ++ ToDictionary(it.second->GetDirect()); ++ if (!ValidateDictType(pElement.Get(), "Font")) + continue; +- if (pElement->GetStringFor("Type") != "Font") +- continue; +- if (pFont->GetFontDict() == pElement) { ++ if (pFont->FontDictIs(pElement)) { + *csNameTag = csKey; + return true; + } +@@ -214,36 +198,33 @@ bool FindFont(CPDF_Dictionary* pFormDict, + return false; + } + +-bool FindFont(CPDF_Dictionary* pFormDict, +- CPDF_Document* pDocument, +- ByteString csFontName, +- RetainPtr& pFont, +- ByteString* csNameTag) { +- if (!pFormDict) ++bool FindFontFromDoc(const CPDF_Dictionary* pFormDict, ++ CPDF_Document* pDocument, ++ ByteString csFontName, ++ RetainPtr& pFont, ++ ByteString* csNameTag) { ++ if (csFontName.IsEmpty()) + return false; + +- CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR"); ++ RetainPtr pDR = pFormDict->GetDictFor("DR"); + if (!pDR) + return false; + +- CPDF_Dictionary* pFonts = pDR->GetDictFor("Font"); +- if (!ValidateFontResourceDict(pFonts)) ++ RetainPtr pFonts = pDR->GetDictFor("Font"); ++ if (!ValidateFontResourceDict(pFonts.Get())) + return false; + +- if (csFontName.GetLength() > 0) +- csFontName.Remove(' '); +- ++ csFontName.Remove(' '); + CPDF_DictionaryLocker locker(pFonts); + for (const auto& it : locker) { + const ByteString& csKey = it.first; +- if (!it.second) +- continue; +- +- CPDF_Dictionary* pElement = ToDictionary(it.second->GetDirect()); +- if (!pElement || pElement->GetStringFor("Type") != "Font") ++ RetainPtr pElement = ++ ToDictionary(it.second->GetMutableDirect()); ++ if (!ValidateDictType(pElement.Get(), "Font")) + continue; + +- pFont = CPDF_DocPageData::FromDocument(pDocument)->GetFont(pElement); ++ auto* pData = CPDF_DocPageData::FromDocument(pDocument); ++ pFont = pData->GetFont(std::move(pElement)); + if (!pFont) + continue; + +@@ -257,140 +238,120 @@ bool FindFont(CPDF_Dictionary* pFormDict, + return false; + } + +-void AddFont(CPDF_Dictionary*& pFormDict, ++void AddFont(CPDF_Dictionary* pFormDict, + CPDF_Document* pDocument, + const RetainPtr& pFont, + ByteString* csNameTag) { +- if (!pFont) +- return; +- if (!pFormDict) +- InitDict(pFormDict, pDocument); ++ DCHECK(pFormDict); ++ DCHECK(pFont); + + ByteString csTag; + if (FindFont(pFormDict, pFont.Get(), &csTag)) { + *csNameTag = std::move(csTag); + return; + } +- if (!pFormDict) +- InitDict(pFormDict, pDocument); +- +- CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR"); +- if (!pDR) +- pDR = pFormDict->SetNewFor("DR"); + +- CPDF_Dictionary* pFonts = pDR->GetDictFor("Font"); +- if (!pFonts) +- pFonts = pDR->SetNewFor("Font"); ++ RetainPtr pDR = pFormDict->GetOrCreateDictFor("DR"); ++ RetainPtr pFonts = pDR->GetOrCreateDictFor("Font"); + + if (csNameTag->IsEmpty()) + *csNameTag = pFont->GetBaseFontName(); + + csNameTag->Remove(' '); +- *csNameTag = GenerateNewFontResourceName(pDR, *csNameTag); ++ *csNameTag = GenerateNewFontResourceName(pDR.Get(), *csNameTag); + pFonts->SetNewFor(*csNameTag, pDocument, +- pFont->GetFontDict()->GetObjNum()); ++ pFont->GetFontDictObjNum()); + } + +-RetainPtr AddNativeFont(CPDF_Dictionary*& pFormDict, +- CPDF_Document* pDocument, +- uint8_t charSet, +- ByteString* csNameTag) { +- if (!pFormDict) +- InitDict(pFormDict, pDocument); ++FX_Charset GetNativeCharSet() { ++ return FX_GetCharsetFromCodePage(FX_GetACP()); ++} + +- ByteString csTemp; +- RetainPtr pFont = +- GetNativeFont(pFormDict, pDocument, charSet, &csTemp); ++RetainPtr InitDict(CPDF_Document* pDocument) { ++ auto pFormDict = pDocument->NewIndirect(); ++ pDocument->GetMutableRoot()->SetNewFor( ++ "AcroForm", pDocument, pFormDict->GetObjNum()); ++ ++ ByteString csBaseName; ++ FX_Charset charSet = GetNativeCharSet(); ++ RetainPtr pFont = AddStandardFont(pDocument); + if (pFont) { +- *csNameTag = std::move(csTemp); +- return pFont; ++ AddFont(pFormDict.Get(), pDocument, pFont, &csBaseName); + } +- ByteString csFontName = +- CPDF_InteractiveForm::GetNativeFontName(charSet, nullptr); +- if (!csFontName.IsEmpty() && +- FindFont(pFormDict, pDocument, csFontName, pFont, csNameTag)) { +- return pFont; ++ if (charSet != FX_Charset::kANSI) { ++ ByteString csFontName = GetNativeFontName(charSet, nullptr); ++ if (!pFont || csFontName != CFX_Font::kDefaultAnsiFontName) { ++ pFont = AddNativeFont(charSet, pDocument); ++ if (pFont) { ++ csBaseName.clear(); ++ AddFont(pFormDict.Get(), pDocument, pFont, &csBaseName); ++ } ++ } + } +- pFont = CPDF_InteractiveForm::AddNativeFont(charSet, pDocument); +- if (!pFont) ++ ByteString csDA; ++ if (pFont) ++ csDA = "/" + PDF_NameEncode(csBaseName) + " 0 Tf "; ++ csDA += "0 g"; ++ pFormDict->SetNewFor("DA", csDA, /*bHex=*/false); ++ return pFormDict; ++} ++ ++RetainPtr GetNativeFont(const CPDF_Dictionary* pFormDict, ++ CPDF_Document* pDocument, ++ FX_Charset charSet, ++ ByteString* csNameTag) { ++ RetainPtr pDR = pFormDict->GetDictFor("DR"); ++ if (!pDR) + return nullptr; + +- AddFont(pFormDict, pDocument, pFont, csNameTag); +- return pFont; ++ RetainPtr pFonts = pDR->GetDictFor("Font"); ++ if (!ValidateFontResourceDict(pFonts.Get())) ++ return nullptr; ++ ++ CPDF_DictionaryLocker locker(pFonts); ++ for (const auto& it : locker) { ++ const ByteString& csKey = it.first; ++ RetainPtr pElement = ++ ToDictionary(it.second->GetMutableDirect()); ++ if (!ValidateDictType(pElement.Get(), "Font")) ++ continue; ++ ++ auto* pData = CPDF_DocPageData::FromDocument(pDocument); ++ RetainPtr pFind = pData->GetFont(std::move(pElement)); ++ if (!pFind) ++ continue; ++ ++ auto maybe_charset = pFind->GetSubstFontCharset(); ++ if (maybe_charset.has_value() && maybe_charset.value() == charSet) { ++ *csNameTag = csKey; ++ return pFind; ++ } ++ } ++ return nullptr; + } + + class CFieldNameExtractor { + public: + explicit CFieldNameExtractor(const WideString& full_name) +- : m_FullName(full_name) { +- m_pCur = m_FullName.c_str(); +- m_pEnd = m_pCur + m_FullName.GetLength(); +- } ++ : m_FullName(full_name) {} + +- void GetNext(const wchar_t*& pSubName, size_t& size) { +- pSubName = m_pCur; +- while (m_pCur < m_pEnd && m_pCur[0] != L'.') +- m_pCur++; ++ WideStringView GetNext() { ++ size_t start_pos = m_iCur; ++ while (m_iCur < m_FullName.GetLength() && m_FullName[m_iCur] != L'.') ++ ++m_iCur; + +- size = static_cast(m_pCur - pSubName); +- if (m_pCur < m_pEnd && m_pCur[0] == L'.') +- m_pCur++; ++ size_t length = m_iCur - start_pos; ++ if (m_iCur < m_FullName.GetLength() && m_FullName[m_iCur] == L'.') ++ ++m_iCur; ++ ++ return m_FullName.AsStringView().Substr(start_pos, length); + } + + protected: +- WideString m_FullName; +- const wchar_t* m_pCur; +- const wchar_t* m_pEnd; ++ const WideString m_FullName; ++ size_t m_iCur = 0; + }; + +-#if defined(OS_WIN) +-struct PDF_FONTDATA { +- bool bFind; +- LOGFONTA lf; +-}; +- +-static int CALLBACK EnumFontFamExProc(ENUMLOGFONTEXA* lpelfe, +- NEWTEXTMETRICEX* lpntme, +- DWORD FontType, +- LPARAM lParam) { +- if (FontType != 0x004 || strchr(lpelfe->elfLogFont.lfFaceName, '@')) +- return 1; +- +- PDF_FONTDATA* pData = (PDF_FONTDATA*)lParam; +- memcpy(&pData->lf, &lpelfe->elfLogFont, sizeof(LOGFONTA)); +- pData->bFind = true; +- return 0; +-} +- +-bool RetrieveSpecificFont(LOGFONTA& lf) { +- PDF_FONTDATA fd; +- memset(&fd, 0, sizeof(PDF_FONTDATA)); +- HDC hDC = ::GetDC(nullptr); +- EnumFontFamiliesExA(hDC, &lf, (FONTENUMPROCA)EnumFontFamExProc, (LPARAM)&fd, +- 0); +- ::ReleaseDC(nullptr, hDC); +- if (fd.bFind) +- memcpy(&lf, &fd.lf, sizeof(LOGFONTA)); +- +- return fd.bFind; +-} +- +-bool RetrieveSpecificFont(uint8_t charSet, +- uint8_t pitchAndFamily, +- LPCSTR pcsFontName, +- LOGFONTA& lf) { +- memset(&lf, 0, sizeof(LOGFONTA)); +- lf.lfCharSet = charSet; +- lf.lfPitchAndFamily = pitchAndFamily; +- if (pcsFontName) { +- // TODO(dsinclair): Should this be strncpy? +- // NOLINTNEXTLINE(runtime/printf) +- strcpy(lf.lfFaceName, pcsFontName); +- } +- return RetrieveSpecificFont(lf); +-} +-#endif // defined(OS_WIN) +- + } // namespace + + class CFieldTree { +@@ -465,15 +426,16 @@ class CFieldTree { + std::unique_ptr pField); + CPDF_FormField* GetField(const WideString& full_name); + ++ Node* GetRoot() { return m_pRoot.get(); } + Node* FindNode(const WideString& full_name); + Node* AddChild(Node* pParent, const WideString& short_name); ++ Node* Lookup(Node* pParent, WideStringView short_name); + +- Node* Lookup(Node* pParent, const WideString& short_name); +- +- Node m_Root; ++ private: ++ std::unique_ptr m_pRoot; + }; + +-CFieldTree::CFieldTree() = default; ++CFieldTree::CFieldTree() : m_pRoot(std::make_unique()) {} + + CFieldTree::~CFieldTree() = default; + +@@ -486,14 +448,13 @@ CFieldTree::Node* CFieldTree::AddChild(Node* pParent, + if (level > nMaxRecursion) + return nullptr; + +- auto pNew = pdfium::MakeUnique(short_name, pParent->GetLevel() + 1); ++ auto pNew = std::make_unique(short_name, pParent->GetLevel() + 1); + Node* pChild = pNew.get(); + pParent->AddChildNode(std::move(pNew)); + return pChild; + } + +-CFieldTree::Node* CFieldTree::Lookup(Node* pParent, +- const WideString& short_name) { ++CFieldTree::Node* CFieldTree::Lookup(Node* pParent, WideStringView short_name) { + if (!pParent) + return nullptr; + +@@ -510,24 +471,22 @@ bool CFieldTree::SetField(const WideString& full_name, + if (full_name.IsEmpty()) + return false; + +- CFieldNameExtractor name_extractor(full_name); +- const wchar_t* pName; +- size_t nLength; +- name_extractor.GetNext(pName, nLength); +- Node* pNode = &m_Root; ++ Node* pNode = GetRoot(); + Node* pLast = nullptr; +- while (nLength > 0) { ++ CFieldNameExtractor name_extractor(full_name); ++ while (true) { ++ WideStringView name_view = name_extractor.GetNext(); ++ if (name_view.IsEmpty()) ++ break; + pLast = pNode; +- WideString name = WideString(pName, nLength); +- pNode = Lookup(pLast, name); +- if (!pNode) +- pNode = AddChild(pLast, name); ++ pNode = Lookup(pLast, name_view); ++ if (pNode) ++ continue; ++ pNode = AddChild(pLast, WideString(name_view)); + if (!pNode) + return false; +- +- name_extractor.GetNext(pName, nLength); + } +- if (pNode == &m_Root) ++ if (pNode == GetRoot()) + return false; + + pNode->SetField(std::move(pField)); +@@ -538,17 +497,15 @@ CPDF_FormField* CFieldTree::GetField(const WideString& full_name) { + if (full_name.IsEmpty()) + return nullptr; + +- CFieldNameExtractor name_extractor(full_name); +- const wchar_t* pName; +- size_t nLength; +- name_extractor.GetNext(pName, nLength); +- Node* pNode = &m_Root; ++ Node* pNode = GetRoot(); + Node* pLast = nullptr; +- while (nLength > 0 && pNode) { ++ CFieldNameExtractor name_extractor(full_name); ++ while (pNode) { ++ WideStringView name_view = name_extractor.GetNext(); ++ if (name_view.IsEmpty()) ++ break; + pLast = pNode; +- WideString name = WideString(pName, nLength); +- pNode = Lookup(pLast, name); +- name_extractor.GetNext(pName, nLength); ++ pNode = Lookup(pLast, name_view); + } + return pNode ? pNode->GetField() : nullptr; + } +@@ -557,159 +514,103 @@ CFieldTree::Node* CFieldTree::FindNode(const WideString& full_name) { + if (full_name.IsEmpty()) + return nullptr; + +- CFieldNameExtractor name_extractor(full_name); +- const wchar_t* pName; +- size_t nLength; +- name_extractor.GetNext(pName, nLength); +- Node* pNode = &m_Root; ++ Node* pNode = GetRoot(); + Node* pLast = nullptr; +- while (nLength > 0 && pNode) { ++ CFieldNameExtractor name_extractor(full_name); ++ while (pNode) { ++ WideStringView name_view = name_extractor.GetNext(); ++ if (name_view.IsEmpty()) ++ break; + pLast = pNode; +- WideString name = WideString(pName, nLength); +- pNode = Lookup(pLast, name); +- name_extractor.GetNext(pName, nLength); ++ pNode = Lookup(pLast, name_view); + } + return pNode; + } + +-RetainPtr AddNativeInteractiveFormFont(CPDF_Dictionary*& pFormDict, +- CPDF_Document* pDocument, +- ByteString* csNameTag) { +- uint8_t charSet = CPDF_InteractiveForm::GetNativeCharSet(); +- return AddNativeFont(pFormDict, pDocument, charSet, csNameTag); +-} +- +-// static +-uint8_t CPDF_InteractiveForm::GetNativeCharSet() { +- return FX_GetCharsetFromCodePage(FXSYS_GetACP()); +-} +- + CPDF_InteractiveForm::CPDF_InteractiveForm(CPDF_Document* pDocument) +- : m_pDocument(pDocument), m_pFieldTree(pdfium::MakeUnique()) { +- CPDF_Dictionary* pRoot = m_pDocument->GetRoot(); ++ : m_pDocument(pDocument), m_pFieldTree(std::make_unique()) { ++ RetainPtr pRoot = m_pDocument->GetMutableRoot(); + if (!pRoot) + return; + +- m_pFormDict.Reset(pRoot->GetDictFor("AcroForm")); ++ m_pFormDict = pRoot->GetMutableDictFor("AcroForm"); + if (!m_pFormDict) + return; + +- CPDF_Array* pFields = m_pFormDict->GetArrayFor("Fields"); ++ RetainPtr pFields = m_pFormDict->GetMutableArrayFor("Fields"); + if (!pFields) + return; + + for (size_t i = 0; i < pFields->size(); ++i) +- LoadField(pFields->GetDictAt(i), 0); ++ LoadField(pFields->GetMutableDictAt(i), 0); + } + + CPDF_InteractiveForm::~CPDF_InteractiveForm() = default; + + bool CPDF_InteractiveForm::s_bUpdateAP = true; + ++// static + bool CPDF_InteractiveForm::IsUpdateAPEnabled() { + return s_bUpdateAP; + } + ++// static + void CPDF_InteractiveForm::SetUpdateAP(bool bUpdateAP) { + s_bUpdateAP = bUpdateAP; + } + +-RetainPtr CPDF_InteractiveForm::AddStandardFont( ++// static ++RetainPtr CPDF_InteractiveForm::AddNativeInteractiveFormFont( + CPDF_Document* pDocument, +- ByteString csFontName) { +- if (!pDocument || csFontName.IsEmpty()) +- return nullptr; +- +- auto* pPageData = CPDF_DocPageData::FromDocument(pDocument); +- if (csFontName == "ZapfDingbats") +- return pPageData->AddStandardFont(csFontName, nullptr); +- +- static const CPDF_FontEncoding encoding(PDFFONT_ENCODING_WINANSI); +- return pPageData->AddStandardFont(csFontName, &encoding); +-} ++ ByteString* csNameTag) { ++ DCHECK(pDocument); ++ DCHECK(csNameTag); + +-ByteString CPDF_InteractiveForm::GetNativeFontName(uint8_t charSet, +- void* pLogFont) { +- ByteString csFontName; +-#if defined(OS_WIN) +- LOGFONTA lf = {}; +- if (charSet == FX_CHARSET_ANSI) { +- csFontName = CFX_Font::kDefaultAnsiFontName; +- return csFontName; +- } +- bool bRet = false; +- const ByteString default_font_name = +- CFX_Font::GetDefaultFontNameByCharset(charSet); +- if (!default_font_name.IsEmpty()) { +- bRet = RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE, +- default_font_name.c_str(), lf); +- } +- if (!bRet) { +- bRet = RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE, +- CFX_Font::kUniversalDefaultFontName, lf); +- } +- if (!bRet) { +- bRet = RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE, +- "Microsoft Sans Serif", lf); +- } +- if (!bRet) { +- bRet = +- RetrieveSpecificFont(charSet, DEFAULT_PITCH | FF_DONTCARE, nullptr, lf); +- } +- if (bRet) { +- if (pLogFont) +- memcpy(pLogFont, &lf, sizeof(LOGFONTA)); ++ RetainPtr pFormDict = ++ pDocument->GetMutableRoot()->GetMutableDictFor("AcroForm"); ++ if (!pFormDict) ++ pFormDict = InitDict(pDocument); + +- csFontName = lf.lfFaceName; +- return csFontName; ++ FX_Charset charSet = GetNativeCharSet(); ++ ByteString csTemp; ++ RetainPtr pFont = ++ GetNativeFont(pFormDict.Get(), pDocument, charSet, &csTemp); ++ if (pFont) { ++ *csNameTag = std::move(csTemp); ++ return pFont; + } +-#endif +- return csFontName; +-} ++ ByteString csFontName = GetNativeFontName(charSet, nullptr); ++ if (FindFontFromDoc(pFormDict.Get(), pDocument, csFontName, pFont, csNameTag)) ++ return pFont; + +-RetainPtr CPDF_InteractiveForm::AddNativeFont( +- uint8_t charSet, +- CPDF_Document* pDocument) { +- if (!pDocument) ++ pFont = AddNativeFont(charSet, pDocument); ++ if (!pFont) + return nullptr; + +-#if defined(OS_WIN) +- LOGFONTA lf; +- ByteString csFontName = GetNativeFontName(charSet, &lf); +- if (!csFontName.IsEmpty()) { +- if (csFontName == CFX_Font::kDefaultAnsiFontName) +- return AddStandardFont(pDocument, csFontName); +- return CPDF_DocPageData::FromDocument(pDocument)->AddWindowsFont(&lf); +- } +-#endif +- return nullptr; +-} +- +-RetainPtr CPDF_InteractiveForm::AddNativeFont( +- CPDF_Document* pDocument) { +- return pDocument ? AddNativeFont(GetNativeCharSet(), pDocument) : nullptr; ++ AddFont(pFormDict.Get(), pDocument, pFont, csNameTag); ++ return pFont; + } + + size_t CPDF_InteractiveForm::CountFields(const WideString& csFieldName) const { + if (csFieldName.IsEmpty()) +- return m_pFieldTree->m_Root.CountFields(); ++ return m_pFieldTree->GetRoot()->CountFields(); + + CFieldTree::Node* pNode = m_pFieldTree->FindNode(csFieldName); + return pNode ? pNode->CountFields() : 0; + } + + CPDF_FormField* CPDF_InteractiveForm::GetField( +- uint32_t index, ++ size_t index, + const WideString& csFieldName) const { + if (csFieldName.IsEmpty()) +- return m_pFieldTree->m_Root.GetFieldAtIndex(index); ++ return m_pFieldTree->GetRoot()->GetFieldAtIndex(index); + + CFieldTree::Node* pNode = m_pFieldTree->FindNode(csFieldName); + return pNode ? pNode->GetFieldAtIndex(index) : nullptr; + } + + CPDF_FormField* CPDF_InteractiveForm::GetFieldByDict( +- CPDF_Dictionary* pFieldDict) const { ++ const CPDF_Dictionary* pFieldDict) const { + if (!pFieldDict) + return nullptr; + +@@ -717,26 +618,26 @@ CPDF_FormField* CPDF_InteractiveForm::GetFieldByDict( + return m_pFieldTree->GetField(csWName); + } + +-CPDF_FormControl* CPDF_InteractiveForm::GetControlAtPoint( +- CPDF_Page* pPage, ++const CPDF_FormControl* CPDF_InteractiveForm::GetControlAtPoint( ++ const CPDF_Page* pPage, + const CFX_PointF& point, +- + int* z_order) const { +- CPDF_Array* pAnnotList = pPage->GetDict()->GetArrayFor("Annots"); ++ RetainPtr pAnnotList = pPage->GetAnnotsArray(); + if (!pAnnotList) + return nullptr; + + for (size_t i = pAnnotList->size(); i > 0; --i) { + size_t annot_index = i - 1; +- const CPDF_Dictionary* pAnnot = pAnnotList->GetDictAt(annot_index); ++ RetainPtr pAnnot = ++ pAnnotList->GetDictAt(annot_index); + if (!pAnnot) + continue; + +- const auto it = m_ControlMap.find(pAnnot); ++ const auto it = m_ControlMap.find(pAnnot.Get()); + if (it == m_ControlMap.end()) + continue; + +- CPDF_FormControl* pControl = it->second.get(); ++ const CPDF_FormControl* pControl = it->second.get(); + if (!pControl->GetRect().Contains(point)) + continue; + +@@ -761,156 +662,165 @@ int CPDF_InteractiveForm::CountFieldsInCalculationOrder() { + if (!m_pFormDict) + return 0; + +- CPDF_Array* pArray = m_pFormDict->GetArrayFor("CO"); +- return pArray ? pArray->size() : 0; ++ RetainPtr pArray = m_pFormDict->GetArrayFor("CO"); ++ return pArray ? fxcrt::CollectionSize(*pArray) : 0; + } + + CPDF_FormField* CPDF_InteractiveForm::GetFieldInCalculationOrder(int index) { + if (!m_pFormDict || index < 0) + return nullptr; + +- CPDF_Array* pArray = m_pFormDict->GetArrayFor("CO"); ++ RetainPtr pArray = m_pFormDict->GetArrayFor("CO"); + if (!pArray) + return nullptr; + +- CPDF_Dictionary* pElement = ToDictionary(pArray->GetDirectObjectAt(index)); +- return pElement ? GetFieldByDict(pElement) : nullptr; ++ RetainPtr pElement = ++ ToDictionary(pArray->GetDirectObjectAt(index)); ++ return pElement ? GetFieldByDict(pElement.Get()) : nullptr; + } + + int CPDF_InteractiveForm::FindFieldInCalculationOrder( + const CPDF_FormField* pField) { +- if (!m_pFormDict || !pField) ++ if (!m_pFormDict) + return -1; + +- CPDF_Array* pArray = m_pFormDict->GetArrayFor("CO"); ++ RetainPtr pArray = m_pFormDict->GetArrayFor("CO"); + if (!pArray) + return -1; + +- for (size_t i = 0; i < pArray->size(); i++) { +- CPDF_Object* pElement = pArray->GetDirectObjectAt(i); +- if (pElement == pField->GetDict()) +- return i; +- } +- return -1; ++ absl::optional maybe_found = pArray->Find(pField->GetFieldDict()); ++ if (!maybe_found.has_value()) ++ return -1; ++ ++ return pdfium::base::checked_cast(maybe_found.value()); + } + + RetainPtr CPDF_InteractiveForm::GetFormFont( + ByteString csNameTag) const { +- return GetFont(m_pFormDict.Get(), m_pDocument.Get(), csNameTag); ++ ByteString csAlias = PDF_NameDecode(csNameTag.AsStringView()); ++ if (!m_pFormDict || csAlias.IsEmpty()) ++ return nullptr; ++ ++ RetainPtr pDR = m_pFormDict->GetMutableDictFor("DR"); ++ if (!pDR) ++ return nullptr; ++ ++ RetainPtr pFonts = pDR->GetMutableDictFor("Font"); ++ if (!ValidateFontResourceDict(pFonts.Get())) ++ return nullptr; ++ ++ RetainPtr pElement = pFonts->GetMutableDictFor(csAlias); ++ if (!ValidateDictType(pElement.Get(), "Font")) ++ return nullptr; ++ ++ return GetFontForElement(std::move(pElement)); ++} ++ ++RetainPtr CPDF_InteractiveForm::GetFontForElement( ++ RetainPtr pElement) const { ++ auto* pData = CPDF_DocPageData::FromDocument(m_pDocument); ++ return pData->GetFont(std::move(pElement)); + } + + CPDF_DefaultAppearance CPDF_InteractiveForm::GetDefaultAppearance() const { + if (!m_pFormDict) + return CPDF_DefaultAppearance(); +- return CPDF_DefaultAppearance(m_pFormDict->GetStringFor("DA")); ++ return CPDF_DefaultAppearance(m_pFormDict->GetByteStringFor("DA")); + } + + int CPDF_InteractiveForm::GetFormAlignment() const { + return m_pFormDict ? m_pFormDict->GetIntegerFor("Q", 0) : 0; + } + +-void CPDF_InteractiveForm::ResetForm(const std::vector& fields, +- bool bIncludeOrExclude, +- NotificationOption notify) { +- size_t nCount = m_pFieldTree->m_Root.CountFields(); ++void CPDF_InteractiveForm::ResetForm(pdfium::span fields, ++ bool bIncludeOrExclude) { ++ CFieldTree::Node* pRoot = m_pFieldTree->GetRoot(); ++ const size_t nCount = pRoot->CountFields(); + for (size_t i = 0; i < nCount; ++i) { +- CPDF_FormField* pField = m_pFieldTree->m_Root.GetFieldAtIndex(i); ++ CPDF_FormField* pField = pRoot->GetFieldAtIndex(i); + if (!pField) + continue; + +- if (bIncludeOrExclude == pdfium::ContainsValue(fields, pField)) +- pField->ResetField(notify); ++ if (bIncludeOrExclude == pdfium::Contains(fields, pField)) ++ pField->ResetField(); + } +- if (notify == NotificationOption::kNotify && m_pFormNotify) ++ if (m_pFormNotify) + m_pFormNotify->AfterFormReset(this); + } + +-void CPDF_InteractiveForm::ResetForm(NotificationOption notify) { +- size_t nCount = m_pFieldTree->m_Root.CountFields(); +- for (size_t i = 0; i < nCount; ++i) { +- CPDF_FormField* pField = m_pFieldTree->m_Root.GetFieldAtIndex(i); +- if (!pField) +- continue; +- +- pField->ResetField(notify); +- } +- if (notify == NotificationOption::kNotify && m_pFormNotify) +- m_pFormNotify->AfterFormReset(this); ++void CPDF_InteractiveForm::ResetForm() { ++ ResetForm(/*fields=*/{}, /*bIncludeOrExclude=*/false); + } + + const std::vector>& + CPDF_InteractiveForm::GetControlsForField(const CPDF_FormField* pField) { +- return m_ControlLists[pField]; ++ return m_ControlLists[pdfium::WrapUnowned(pField)]; + } + +-void CPDF_InteractiveForm::LoadField(CPDF_Dictionary* pFieldDict, int nLevel) { ++void CPDF_InteractiveForm::LoadField(RetainPtr pFieldDict, ++ int nLevel) { + if (nLevel > nMaxRecursion) + return; + if (!pFieldDict) + return; + + uint32_t dwParentObjNum = pFieldDict->GetObjNum(); +- CPDF_Array* pKids = pFieldDict->GetArrayFor(pdfium::form_fields::kKids); ++ RetainPtr pKids = ++ pFieldDict->GetMutableArrayFor(pdfium::form_fields::kKids); + if (!pKids) { +- AddTerminalField(pFieldDict); ++ AddTerminalField(std::move(pFieldDict)); + return; + } + +- CPDF_Dictionary* pFirstKid = pKids->GetDictAt(0); ++ RetainPtr pFirstKid = pKids->GetDictAt(0); + if (!pFirstKid) + return; + +- if (pFirstKid->KeyExist(pdfium::form_fields::kT) || +- pFirstKid->KeyExist(pdfium::form_fields::kKids)) { +- for (size_t i = 0; i < pKids->size(); i++) { +- CPDF_Dictionary* pChildDict = pKids->GetDictAt(i); +- if (pChildDict) { +- if (pChildDict->GetObjNum() != dwParentObjNum) +- LoadField(pChildDict, nLevel + 1); +- } +- } +- } else { +- AddTerminalField(pFieldDict); ++ if (!pFirstKid->KeyExist(pdfium::form_fields::kT) && ++ !pFirstKid->KeyExist(pdfium::form_fields::kKids)) { ++ AddTerminalField(std::move(pFieldDict)); ++ return; ++ } ++ for (size_t i = 0; i < pKids->size(); i++) { ++ RetainPtr pChildDict = pKids->GetMutableDictAt(i); ++ if (pChildDict && pChildDict->GetObjNum() != dwParentObjNum) ++ LoadField(std::move(pChildDict), nLevel + 1); + } +-} +- +-bool CPDF_InteractiveForm::HasXFAForm() const { +- return m_pFormDict && m_pFormDict->GetArrayFor("XFA"); + } + + void CPDF_InteractiveForm::FixPageFields(CPDF_Page* pPage) { +- CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots"); ++ RetainPtr pAnnots = pPage->GetMutableAnnotsArray(); + if (!pAnnots) + return; + + for (size_t i = 0; i < pAnnots->size(); i++) { +- CPDF_Dictionary* pAnnot = pAnnots->GetDictAt(i); +- if (pAnnot && pAnnot->GetStringFor("Subtype") == "Widget") +- LoadField(pAnnot, 0); ++ RetainPtr pAnnot = pAnnots->GetMutableDictAt(i); ++ if (pAnnot && pAnnot->GetNameFor("Subtype") == "Widget") ++ LoadField(std::move(pAnnot), 0); + } + } + +-void CPDF_InteractiveForm::AddTerminalField(CPDF_Dictionary* pFieldDict) { ++void CPDF_InteractiveForm::AddTerminalField( ++ RetainPtr pFieldDict) { + if (!pFieldDict->KeyExist(pdfium::form_fields::kFT)) { + // Key "FT" is required for terminal fields, it is also inheritable. +- CPDF_Dictionary* pParentDict = ++ RetainPtr pParentDict = + pFieldDict->GetDictFor(pdfium::form_fields::kParent); + if (!pParentDict || !pParentDict->KeyExist(pdfium::form_fields::kFT)) + return; + } + +- CPDF_Dictionary* pDict = pFieldDict; +- WideString csWName = CPDF_FormField::GetFullNameForDict(pFieldDict); ++ WideString csWName = CPDF_FormField::GetFullNameForDict(pFieldDict.Get()); + if (csWName.IsEmpty()) + return; + + CPDF_FormField* pField = nullptr; + pField = m_pFieldTree->GetField(csWName); + if (!pField) { +- CPDF_Dictionary* pParent = pFieldDict; ++ RetainPtr pParent(pFieldDict); + if (!pFieldDict->KeyExist(pdfium::form_fields::kT) && +- pFieldDict->GetStringFor("Subtype") == "Widget") { +- pParent = pFieldDict->GetDictFor(pdfium::form_fields::kParent); ++ pFieldDict->GetNameFor("Subtype") == "Widget") { ++ pParent = pFieldDict->GetMutableDictFor(pdfium::form_fields::kParent); + if (!pParent) + pParent = pFieldDict; + } +@@ -918,71 +828,71 @@ void CPDF_InteractiveForm::AddTerminalField(CPDF_Dictionary* pFieldDict) { + if (pParent && pParent != pFieldDict && + !pParent->KeyExist(pdfium::form_fields::kFT)) { + if (pFieldDict->KeyExist(pdfium::form_fields::kFT)) { +- CPDF_Object* pFTValue = ++ RetainPtr pFTValue = + pFieldDict->GetDirectObjectFor(pdfium::form_fields::kFT); + if (pFTValue) + pParent->SetFor(pdfium::form_fields::kFT, pFTValue->Clone()); + } + + if (pFieldDict->KeyExist(pdfium::form_fields::kFf)) { +- CPDF_Object* pFfValue = ++ RetainPtr pFfValue = + pFieldDict->GetDirectObjectFor(pdfium::form_fields::kFf); + if (pFfValue) + pParent->SetFor(pdfium::form_fields::kFf, pFfValue->Clone()); + } + } + +- auto newField = pdfium::MakeUnique(this, pParent); ++ auto newField = std::make_unique(this, std::move(pParent)); + pField = newField.get(); +- CPDF_Object* pTObj = pDict->GetObjectFor(pdfium::form_fields::kT); ++ RetainPtr pTObj = ++ pFieldDict->GetObjectFor(pdfium::form_fields::kT); + if (ToReference(pTObj)) { + RetainPtr pClone = pTObj->CloneDirectObject(); + if (pClone) +- pDict->SetFor(pdfium::form_fields::kT, std::move(pClone)); ++ pFieldDict->SetFor(pdfium::form_fields::kT, std::move(pClone)); + else +- pDict->SetNewFor(pdfium::form_fields::kT, ByteString()); ++ pFieldDict->SetNewFor(pdfium::form_fields::kT, ByteString()); + } + if (!m_pFieldTree->SetField(csWName, std::move(newField))) + return; + } + +- CPDF_Array* pKids = pFieldDict->GetArrayFor(pdfium::form_fields::kKids); +- if (pKids) { +- for (size_t i = 0; i < pKids->size(); i++) { +- CPDF_Dictionary* pKid = pKids->GetDictAt(i); +- if (!pKid) +- continue; +- if (pKid->GetStringFor("Subtype") != "Widget") +- continue; +- +- AddControl(pField, pKid); +- } +- } else { +- if (pFieldDict->GetStringFor("Subtype") == "Widget") +- AddControl(pField, pFieldDict); ++ RetainPtr pKids = ++ pFieldDict->GetMutableArrayFor(pdfium::form_fields::kKids); ++ if (!pKids) { ++ if (pFieldDict->GetNameFor("Subtype") == "Widget") ++ AddControl(pField, std::move(pFieldDict)); ++ return; ++ } ++ for (size_t i = 0; i < pKids->size(); i++) { ++ RetainPtr pKid = pKids->GetMutableDictAt(i); ++ if (pKid && pKid->GetNameFor("Subtype") == "Widget") ++ AddControl(pField, std::move(pKid)); + } + } + + CPDF_FormControl* CPDF_InteractiveForm::AddControl( + CPDF_FormField* pField, +- CPDF_Dictionary* pWidgetDict) { +- const auto it = m_ControlMap.find(pWidgetDict); ++ RetainPtr pWidgetDict) { ++ DCHECK(pWidgetDict); ++ const auto it = m_ControlMap.find(pWidgetDict.Get()); + if (it != m_ControlMap.end()) + return it->second.get(); + +- auto pNew = pdfium::MakeUnique(pField, pWidgetDict); ++ auto pNew = std::make_unique(pField, pWidgetDict, this); + CPDF_FormControl* pControl = pNew.get(); + m_ControlMap[pWidgetDict] = std::move(pNew); +- m_ControlLists[pField].emplace_back(pControl); ++ m_ControlLists[pdfium::WrapUnowned(pField)].emplace_back(pControl); + return pControl; + } + + bool CPDF_InteractiveForm::CheckRequiredFields( + const std::vector* fields, + bool bIncludeOrExclude) const { +- size_t nCount = m_pFieldTree->m_Root.CountFields(); ++ CFieldTree::Node* pRoot = m_pFieldTree->GetRoot(); ++ const size_t nCount = pRoot->CountFields(); + for (size_t i = 0; i < nCount; ++i) { +- CPDF_FormField* pField = m_pFieldTree->m_Root.GetFieldAtIndex(i); ++ CPDF_FormField* pField = pRoot->GetFieldAtIndex(i); + if (!pField) + continue; + +@@ -997,11 +907,11 @@ bool CPDF_InteractiveForm::CheckRequiredFields( + + bool bFind = true; + if (fields) +- bFind = pdfium::ContainsValue(*fields, pField); ++ bFind = pdfium::Contains(*fields, pField); + if (bIncludeOrExclude == bFind) { +- const CPDF_Dictionary* pFieldDict = pField->GetDict(); ++ RetainPtr pFieldDict = pField->GetFieldDict(); + if (pField->IsRequired() && +- pFieldDict->GetStringFor(pdfium::form_fields::kV).IsEmpty()) { ++ pFieldDict->GetByteStringFor(pdfium::form_fields::kV).IsEmpty()) { + return false; + } + } +@@ -1010,69 +920,67 @@ bool CPDF_InteractiveForm::CheckRequiredFields( + } + + std::unique_ptr CPDF_InteractiveForm::ExportToFDF( +- const WideString& pdf_path, +- bool bSimpleFileSpec) const { ++ const WideString& pdf_path) const { + std::vector fields; +- size_t nCount = m_pFieldTree->m_Root.CountFields(); ++ CFieldTree::Node* pRoot = m_pFieldTree->GetRoot(); ++ const size_t nCount = pRoot->CountFields(); + for (size_t i = 0; i < nCount; ++i) +- fields.push_back(m_pFieldTree->m_Root.GetFieldAtIndex(i)); +- return ExportToFDF(pdf_path, fields, true, bSimpleFileSpec); ++ fields.push_back(pRoot->GetFieldAtIndex(i)); ++ return ExportToFDF(pdf_path, fields, true); + } + + std::unique_ptr CPDF_InteractiveForm::ExportToFDF( + const WideString& pdf_path, + const std::vector& fields, +- bool bIncludeOrExclude, +- bool bSimpleFileSpec) const { ++ bool bIncludeOrExclude) const { + std::unique_ptr pDoc = CFDF_Document::CreateNewDoc(); + if (!pDoc) + return nullptr; + +- CPDF_Dictionary* pMainDict = pDoc->GetRoot()->GetDictFor("FDF"); ++ RetainPtr pMainDict = ++ pDoc->GetMutableRoot()->GetMutableDictFor("FDF"); + if (!pdf_path.IsEmpty()) { +- if (bSimpleFileSpec) { +- WideString wsFilePath = CPDF_FileSpec::EncodeFileName(pdf_path); +- pMainDict->SetNewFor(pdfium::stream::kF, +- wsFilePath.ToDefANSI(), false); +- pMainDict->SetNewFor("UF", wsFilePath); +- } else { +- auto pNewDict = pDoc->New(); +- pNewDict->SetNewFor("Type", "Filespec"); +- CPDF_FileSpec filespec(pNewDict.Get()); +- filespec.SetFileName(pdf_path); +- pMainDict->SetFor("F", pNewDict); +- } ++ auto pNewDict = pDoc->New(); ++ pNewDict->SetNewFor("Type", "Filespec"); ++ WideString wsStr = CPDF_FileSpec::EncodeFileName(pdf_path); ++ pNewDict->SetNewFor(pdfium::stream::kF, wsStr.ToDefANSI(), ++ false); ++ pNewDict->SetNewFor("UF", wsStr.AsStringView()); ++ pMainDict->SetFor("F", pNewDict); + } + +- CPDF_Array* pFields = pMainDict->SetNewFor("Fields"); +- size_t nCount = m_pFieldTree->m_Root.CountFields(); ++ auto pFields = pMainDict->SetNewFor("Fields"); ++ CFieldTree::Node* pRoot = m_pFieldTree->GetRoot(); ++ const size_t nCount = pRoot->CountFields(); + for (size_t i = 0; i < nCount; ++i) { +- CPDF_FormField* pField = m_pFieldTree->m_Root.GetFieldAtIndex(i); ++ CPDF_FormField* pField = pRoot->GetFieldAtIndex(i); + if (!pField || pField->GetType() == CPDF_FormField::kPushButton) + continue; + + uint32_t dwFlags = pField->GetFieldFlags(); +- if (dwFlags & 0x04) ++ if (dwFlags & pdfium::form_flags::kNoExport) + continue; + +- if (bIncludeOrExclude != pdfium::ContainsValue(fields, pField)) ++ if (bIncludeOrExclude != pdfium::Contains(fields, pField)) + continue; + +- if ((dwFlags & 0x02) != 0 && +- pField->GetDict()->GetStringFor(pdfium::form_fields::kV).IsEmpty()) { ++ if ((dwFlags & pdfium::form_flags::kRequired) != 0 && ++ pField->GetFieldDict() ++ ->GetByteStringFor(pdfium::form_fields::kV) ++ .IsEmpty()) { + continue; + } + + WideString fullname = + CPDF_FormField::GetFullNameForDict(pField->GetFieldDict()); + auto pFieldDict = pDoc->New(); +- pFieldDict->SetNewFor(pdfium::form_fields::kT, fullname); ++ pFieldDict->SetNewFor(pdfium::form_fields::kT, ++ fullname.AsStringView()); + if (pField->GetType() == CPDF_FormField::kCheckBox || + pField->GetType() == CPDF_FormField::kRadioButton) { + WideString csExport = pField->GetCheckValue(false); +- ByteString csBExport = PDF_EncodeText(csExport); +- CPDF_Object* pOpt = +- CPDF_FormField::GetFieldAttr(pField->GetDict(), "Opt"); ++ ByteString csBExport = PDF_EncodeText(csExport.AsStringView()); ++ RetainPtr pOpt = pField->GetFieldAttr("Opt"); + if (pOpt) { + pFieldDict->SetNewFor(pdfium::form_fields::kV, csBExport, + false); +@@ -1080,12 +988,12 @@ std::unique_ptr CPDF_InteractiveForm::ExportToFDF( + pFieldDict->SetNewFor(pdfium::form_fields::kV, csBExport); + } + } else { +- CPDF_Object* pV = CPDF_FormField::GetFieldAttr(pField->GetDict(), +- pdfium::form_fields::kV); ++ RetainPtr pV = ++ pField->GetFieldAttr(pdfium::form_fields::kV); + if (pV) + pFieldDict->SetFor(pdfium::form_fields::kV, pV->CloneDirectObject()); + } +- pFields->Add(pFieldDict); ++ pFields->Append(pFieldDict); + } + return pDoc; + } +@@ -1093,3 +1001,31 @@ std::unique_ptr CPDF_InteractiveForm::ExportToFDF( + void CPDF_InteractiveForm::SetNotifierIface(NotifierIface* pNotify) { + m_pFormNotify = pNotify; + } ++ ++bool CPDF_InteractiveForm::NotifyBeforeValueChange(CPDF_FormField* pField, ++ const WideString& csValue) { ++ return !m_pFormNotify || m_pFormNotify->BeforeValueChange(pField, csValue); ++} ++ ++void CPDF_InteractiveForm::NotifyAfterValueChange(CPDF_FormField* pField) { ++ if (m_pFormNotify) ++ m_pFormNotify->AfterValueChange(pField); ++} ++ ++bool CPDF_InteractiveForm::NotifyBeforeSelectionChange( ++ CPDF_FormField* pField, ++ const WideString& csValue) { ++ return !m_pFormNotify || ++ m_pFormNotify->BeforeSelectionChange(pField, csValue); ++} ++ ++void CPDF_InteractiveForm::NotifyAfterSelectionChange(CPDF_FormField* pField) { ++ if (m_pFormNotify) ++ m_pFormNotify->AfterSelectionChange(pField); ++} ++ ++void CPDF_InteractiveForm::NotifyAfterCheckedStatusChange( ++ CPDF_FormField* pField) { ++ if (m_pFormNotify) ++ m_pFormNotify->AfterCheckedStatusChange(pField); ++} +diff --git a/core/fpdfdoc/cpdf_interactiveform.h b/core/fpdfdoc/cpdf_interactiveform.h +index 7d1f6c282..3eb3c30e3 100644 +--- a/core/fpdfdoc/cpdf_interactiveform.h ++++ b/core/fpdfdoc/cpdf_interactiveform.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,31 +7,31 @@ + #ifndef CORE_FPDFDOC_CPDF_INTERACTIVEFORM_H_ + #define CORE_FPDFDOC_CPDF_INTERACTIVEFORM_H_ + ++#include ++#include ++ ++#include + #include + #include + #include + ++#include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" + #include "core/fpdfdoc/cpdf_defaultappearance.h" + #include "core/fpdfdoc/cpdf_formfield.h" ++#include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "third_party/base/span.h" + + class CFieldTree; + class CFDF_Document; + class CPDF_Document; +-class CPDF_Dictionary; + class CPDF_Font; + class CPDF_FormControl; +-class CPDF_Object; + class CPDF_Page; + +-RetainPtr AddNativeInteractiveFormFont(CPDF_Dictionary*& pFormDict, +- CPDF_Document* pDocument, +- ByteString* csNameTag); +- + class CPDF_InteractiveForm { + public: + class NotifierIface { +@@ -51,23 +51,19 @@ class CPDF_InteractiveForm { + explicit CPDF_InteractiveForm(CPDF_Document* pDocument); + ~CPDF_InteractiveForm(); + +- static void SetUpdateAP(bool bUpdateAP); + static bool IsUpdateAPEnabled(); +- static uint8_t GetNativeCharSet(); +- static ByteString GetNativeFontName(uint8_t iCharSet, void* pLogFont); +- static RetainPtr AddStandardFont(CPDF_Document* pDocument, +- ByteString csFontName); +- static RetainPtr AddNativeFont(uint8_t iCharSet, +- CPDF_Document* pDocument); +- static RetainPtr AddNativeFont(CPDF_Document* pDocument); ++ static void SetUpdateAP(bool bUpdateAP); ++ static RetainPtr AddNativeInteractiveFormFont( ++ CPDF_Document* pDocument, ++ ByteString* csNameTag); + + size_t CountFields(const WideString& csFieldName) const; +- CPDF_FormField* GetField(uint32_t index, const WideString& csFieldName) const; +- CPDF_FormField* GetFieldByDict(CPDF_Dictionary* pFieldDict) const; ++ CPDF_FormField* GetField(size_t index, const WideString& csFieldName) const; ++ CPDF_FormField* GetFieldByDict(const CPDF_Dictionary* pFieldDict) const; + +- CPDF_FormControl* GetControlAtPoint(CPDF_Page* pPage, +- const CFX_PointF& point, +- int* z_order) const; ++ const CPDF_FormControl* GetControlAtPoint(const CPDF_Page* pPage, ++ const CFX_PointF& point, ++ int* z_order) const; + CPDF_FormControl* GetControlByDict(const CPDF_Dictionary* pWidgetDict) const; + + bool NeedConstructAP() const; +@@ -76,44 +72,42 @@ class CPDF_InteractiveForm { + int FindFieldInCalculationOrder(const CPDF_FormField* pField); + + RetainPtr GetFormFont(ByteString csNameTag) const; ++ RetainPtr GetFontForElement( ++ RetainPtr pElement) const; + CPDF_DefaultAppearance GetDefaultAppearance() const; + int GetFormAlignment() const; +- + bool CheckRequiredFields(const std::vector* fields, + bool bIncludeOrExclude) const; + +- std::unique_ptr ExportToFDF(const WideString& pdf_path, +- bool bSimpleFileSpec) const; +- ++ std::unique_ptr ExportToFDF(const WideString& pdf_path) const; + std::unique_ptr ExportToFDF( + const WideString& pdf_path, + const std::vector& fields, +- bool bIncludeOrExclude, +- bool bSimpleFileSpec) const; +- +- void ResetForm(NotificationOption notify); ++ bool bIncludeOrExclude) const; + +- // TODO(tsepez): Use a span. +- void ResetForm(const std::vector& fields, +- bool bIncludeOrExclude, +- NotificationOption notify); ++ void ResetForm(); ++ void ResetForm(pdfium::span fields, bool bIncludeOrExclude); + + void SetNotifierIface(NotifierIface* pNotify); +- bool HasXFAForm() const; + void FixPageFields(CPDF_Page* pPage); + +- NotifierIface* GetFormNotify() const { return m_pFormNotify.Get(); } +- CPDF_Document* GetDocument() const { return m_pDocument.Get(); } +- CPDF_Dictionary* GetFormDict() const { return m_pFormDict.Get(); } ++ // Wrap callbacks thru NotifierIface. ++ bool NotifyBeforeValueChange(CPDF_FormField* pField, ++ const WideString& csValue); ++ void NotifyAfterValueChange(CPDF_FormField* pField); ++ bool NotifyBeforeSelectionChange(CPDF_FormField* pField, ++ const WideString& csValue); ++ void NotifyAfterSelectionChange(CPDF_FormField* pField); ++ void NotifyAfterCheckedStatusChange(CPDF_FormField* pField); + + const std::vector>& GetControlsForField( + const CPDF_FormField* pField); + + private: +- void LoadField(CPDF_Dictionary* pFieldDict, int nLevel); +- void AddTerminalField(CPDF_Dictionary* pFieldDict); ++ void LoadField(RetainPtr pFieldDict, int nLevel); ++ void AddTerminalField(RetainPtr pFieldDict); + CPDF_FormControl* AddControl(CPDF_FormField* pField, +- CPDF_Dictionary* pWidgetDict); ++ RetainPtr pWidgetDict); + + static bool s_bUpdateAP; + +@@ -121,10 +115,14 @@ class CPDF_InteractiveForm { + UnownedPtr const m_pDocument; + RetainPtr m_pFormDict; + std::unique_ptr m_pFieldTree; +- std::map> ++ std::map, ++ std::unique_ptr, ++ std::less<>> + m_ControlMap; + // Points into |m_ControlMap|. +- std::map>> ++ std::map, ++ std::vector>, ++ std::less<>> + m_ControlLists; + UnownedPtr m_pFormNotify; + }; +diff --git a/core/fpdfdoc/cpdf_link.cpp b/core/fpdfdoc/cpdf_link.cpp +index d5f24b1c4..d68585eed 100644 +--- a/core/fpdfdoc/cpdf_link.cpp ++++ b/core/fpdfdoc/cpdf_link.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,13 +6,14 @@ + + #include "core/fpdfdoc/cpdf_link.h" + +-#include "core/fpdfapi/parser/cpdf_array.h" ++#include ++ + #include "core/fpdfapi/parser/cpdf_dictionary.h" +-#include "core/fpdfdoc/cpdf_nametree.h" + + CPDF_Link::CPDF_Link() = default; + +-CPDF_Link::CPDF_Link(CPDF_Dictionary* pDict) : m_pDict(pDict) {} ++CPDF_Link::CPDF_Link(RetainPtr pDict) ++ : m_pDict(std::move(pDict)) {} + + CPDF_Link::CPDF_Link(const CPDF_Link& that) = default; + +@@ -23,17 +24,7 @@ CFX_FloatRect CPDF_Link::GetRect() { + } + + CPDF_Dest CPDF_Link::GetDest(CPDF_Document* pDoc) { +- CPDF_Object* pDest = m_pDict->GetDirectObjectFor("Dest"); +- if (!pDest) +- return CPDF_Dest(); +- +- if (pDest->IsString() || pDest->IsName()) { +- CPDF_NameTree name_tree(pDoc, "Dests"); +- return CPDF_Dest(name_tree.LookupNamedDest(pDoc, pDest->GetUnicodeText())); +- } +- if (CPDF_Array* pArray = pDest->AsArray()) +- return CPDF_Dest(pArray); +- return CPDF_Dest(); ++ return CPDF_Dest::Create(pDoc, m_pDict->GetDirectObjectFor("Dest")); + } + + CPDF_Action CPDF_Link::GetAction() { +diff --git a/core/fpdfdoc/cpdf_link.h b/core/fpdfdoc/cpdf_link.h +index aaa6e8ebe..2432fd6d7 100644 +--- a/core/fpdfdoc/cpdf_link.h ++++ b/core/fpdfdoc/cpdf_link.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,21 +7,20 @@ + #ifndef CORE_FPDFDOC_CPDF_LINK_H_ + #define CORE_FPDFDOC_CPDF_LINK_H_ + ++#include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfdoc/cpdf_action.h" + #include "core/fpdfdoc/cpdf_dest.h" + #include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/retain_ptr.h" + +-class CPDF_Dictionary; +- + class CPDF_Link { + public: + CPDF_Link(); +- explicit CPDF_Link(CPDF_Dictionary* pDict); ++ explicit CPDF_Link(RetainPtr pDict); + CPDF_Link(const CPDF_Link& that); + ~CPDF_Link(); + +- CPDF_Dictionary* GetDict() const { return m_pDict.Get(); } ++ RetainPtr GetMutableDict() const { return m_pDict; } + CFX_FloatRect GetRect(); + CPDF_Dest GetDest(CPDF_Document* pDoc); + CPDF_Action GetAction(); +diff --git a/core/fpdfdoc/cpdf_linklist.cpp b/core/fpdfdoc/cpdf_linklist.cpp +index 746bbab30..cd4e202db 100644 +--- a/core/fpdfdoc/cpdf_linklist.cpp ++++ b/core/fpdfdoc/cpdf_linklist.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,64 +6,63 @@ + + #include "core/fpdfdoc/cpdf_linklist.h" + ++#include ++ + #include "core/fpdfapi/page/cpdf_page.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "third_party/base/numerics/safe_conversions.h" + + CPDF_LinkList::CPDF_LinkList() = default; + + CPDF_LinkList::~CPDF_LinkList() = default; + +-const std::vector* CPDF_LinkList::GetPageLinks( +- CPDF_Page* pPage) { +- uint32_t objnum = pPage->GetDict()->GetObjNum(); +- if (objnum == 0) +- return nullptr; +- +- auto it = m_PageMap.find(objnum); +- if (it != m_PageMap.end()) +- return &it->second; +- +- // std::map::operator[] forces the creation of a map entry. +- std::vector& page_link_list = m_PageMap[objnum]; +- LoadPageLinks(pPage, &page_link_list); +- return &page_link_list; +-} +- + CPDF_Link CPDF_LinkList::GetLinkAtPoint(CPDF_Page* pPage, + const CFX_PointF& point, + int* z_order) { +- const std::vector* pPageLinkList = GetPageLinks(pPage); ++ const std::vector>* pPageLinkList = ++ GetPageLinks(pPage); + if (!pPageLinkList) + return CPDF_Link(); + + for (size_t i = pPageLinkList->size(); i > 0; --i) { + size_t annot_index = i - 1; +- CPDF_Dictionary* pAnnot = (*pPageLinkList)[annot_index]; ++ RetainPtr pAnnot = (*pPageLinkList)[annot_index]; + if (!pAnnot) + continue; + +- CPDF_Link link(pAnnot); ++ CPDF_Link link(std::move(pAnnot)); + if (!link.GetRect().Contains(point)) + continue; + + if (z_order) +- *z_order = annot_index; ++ *z_order = pdfium::base::checked_cast(annot_index); + return link; + } + return CPDF_Link(); + } + +-void CPDF_LinkList::LoadPageLinks(CPDF_Page* pPage, +- std::vector* pList) { +- CPDF_Array* pAnnotList = pPage->GetDict()->GetArrayFor("Annots"); ++const std::vector>* CPDF_LinkList::GetPageLinks( ++ CPDF_Page* pPage) { ++ uint32_t objnum = pPage->GetDict()->GetObjNum(); ++ if (objnum == 0) ++ return nullptr; ++ ++ auto it = m_PageMap.find(objnum); ++ if (it != m_PageMap.end()) ++ return &it->second; ++ ++ // std::map::operator[] forces the creation of a map entry. ++ auto* page_link_list = &m_PageMap[objnum]; ++ RetainPtr pAnnotList = pPage->GetMutableAnnotsArray(); + if (!pAnnotList) +- return; ++ return page_link_list; + + for (size_t i = 0; i < pAnnotList->size(); ++i) { +- CPDF_Dictionary* pAnnot = pAnnotList->GetDictAt(i); +- bool add_link = (pAnnot && pAnnot->GetStringFor("Subtype") == "Link"); ++ RetainPtr pAnnot = pAnnotList->GetMutableDictAt(i); ++ bool add_link = (pAnnot && pAnnot->GetByteStringFor("Subtype") == "Link"); + // Add non-links as nullptrs to preserve z-order. +- pList->push_back(add_link ? pAnnot : nullptr); ++ page_link_list->emplace_back(add_link ? pAnnot : nullptr); + } ++ return page_link_list; + } +diff --git a/core/fpdfdoc/cpdf_linklist.h b/core/fpdfdoc/cpdf_linklist.h +index 442f66c0a..d2647181b 100644 +--- a/core/fpdfdoc/cpdf_linklist.h ++++ b/core/fpdfdoc/cpdf_linklist.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,17 +7,19 @@ + #ifndef CORE_FPDFDOC_CPDF_LINKLIST_H_ + #define CORE_FPDFDOC_CPDF_LINKLIST_H_ + ++#include ++ + #include + #include + + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfdoc/cpdf_link.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/retain_ptr.h" + + class CPDF_Page; + class CPDF_Dictionary; + +-class CPDF_LinkList : public CPDF_Document::LinkListIface { ++class CPDF_LinkList final : public CPDF_Document::LinkListIface { + public: + CPDF_LinkList(); + ~CPDF_LinkList() override; +@@ -27,10 +29,9 @@ class CPDF_LinkList : public CPDF_Document::LinkListIface { + int* z_order); + + private: +- const std::vector* GetPageLinks(CPDF_Page* pPage); +- void LoadPageLinks(CPDF_Page* pPage, std::vector* pList); ++ const std::vector>* GetPageLinks(CPDF_Page* pPage); + +- std::map> m_PageMap; ++ std::map>> m_PageMap; + }; + + #endif // CORE_FPDFDOC_CPDF_LINKLIST_H_ +diff --git a/core/fpdfdoc/cpdf_metadata.cpp b/core/fpdfdoc/cpdf_metadata.cpp +index da8dcb996..228a0c137 100644 +--- a/core/fpdfdoc/cpdf_metadata.cpp ++++ b/core/fpdfdoc/cpdf_metadata.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,14 +7,16 @@ + #include "core/fpdfdoc/cpdf_metadata.h" + + #include ++#include + + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" +-#include "core/fxcrt/cfx_readonlymemorystream.h" ++#include "core/fxcrt/cfx_read_only_span_stream.h" + #include "core/fxcrt/fx_codepage.h" + #include "core/fxcrt/xml/cfx_xmldocument.h" + #include "core/fxcrt/xml/cfx_xmlelement.h" + #include "core/fxcrt/xml/cfx_xmlparser.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -60,17 +62,18 @@ void CheckForSharedFormInternal(CFX_XMLElement* element, + + } // namespace + +-CPDF_Metadata::CPDF_Metadata(const CPDF_Stream* pStream) : stream_(pStream) { +- ASSERT(pStream); ++CPDF_Metadata::CPDF_Metadata(RetainPtr pStream) ++ : stream_(std::move(pStream)) { ++ DCHECK(stream_); + } + + CPDF_Metadata::~CPDF_Metadata() = default; + + std::vector CPDF_Metadata::CheckForSharedForm() const { +- auto pAcc = pdfium::MakeRetain(stream_.Get()); ++ auto pAcc = pdfium::MakeRetain(stream_); + pAcc->LoadAllDataFiltered(); + +- auto stream = pdfium::MakeRetain(pAcc->GetSpan()); ++ auto stream = pdfium::MakeRetain(pAcc->GetSpan()); + CFX_XMLParser parser(stream); + std::unique_ptr doc = parser.Parse(); + if (!doc) +diff --git a/core/fpdfdoc/cpdf_metadata.h b/core/fpdfdoc/cpdf_metadata.h +index 554492a27..6b63e22ed 100644 +--- a/core/fpdfdoc/cpdf_metadata.h ++++ b/core/fpdfdoc/cpdf_metadata.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -34,7 +34,7 @@ enum class UnsupportedFeature : uint8_t { + + class CPDF_Metadata { + public: +- explicit CPDF_Metadata(const CPDF_Stream* pStream); ++ explicit CPDF_Metadata(RetainPtr pStream); + ~CPDF_Metadata(); + + std::vector CheckForSharedForm() const; +diff --git a/core/fpdfdoc/cpdf_metadata_unittest.cpp b/core/fpdfdoc/cpdf_metadata_unittest.cpp +index 41f4b5db1..cfcb8f35b 100644 +--- a/core/fpdfdoc/cpdf_metadata_unittest.cpp ++++ b/core/fpdfdoc/cpdf_metadata_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -17,7 +17,7 @@ TEST(CPDF_MetadataTest, CheckSharedFormEmailAtTopLevel) { + + auto stream = pdfium::MakeRetain(); + stream->SetData(ByteStringView(data).raw_span()); +- CPDF_Metadata metadata(stream.Get()); ++ CPDF_Metadata metadata(stream); + + auto results = metadata.CheckForSharedForm(); + ASSERT_EQ(1U, results.size()); +@@ -34,7 +34,7 @@ TEST(CPDF_MetadataTest, CheckSharedFormAcrobatAtTopLevel) { + + auto stream = pdfium::MakeRetain(); + stream->SetData(ByteStringView(data).raw_span()); +- CPDF_Metadata metadata(stream.Get()); ++ CPDF_Metadata metadata(stream); + + auto results = metadata.CheckForSharedForm(); + ASSERT_EQ(1U, results.size()); +@@ -51,7 +51,7 @@ TEST(CPDF_MetadataTest, CheckSharedFormFilesystemAtTopLevel) { + + auto stream = pdfium::MakeRetain(); + stream->SetData(ByteStringView(data).raw_span()); +- CPDF_Metadata metadata(stream.Get()); ++ CPDF_Metadata metadata(stream); + + auto results = metadata.CheckForSharedForm(); + ASSERT_EQ(1U, results.size()); +@@ -68,7 +68,7 @@ TEST(CPDF_MetadataTest, CheckSharedFormWithoutWorkflow) { + + auto stream = pdfium::MakeRetain(); + stream->SetData(ByteStringView(data).raw_span()); +- CPDF_Metadata metadata(stream.Get()); ++ CPDF_Metadata metadata(stream); + + auto results = metadata.CheckForSharedForm(); + EXPECT_EQ(0U, results.size()); +@@ -86,7 +86,7 @@ TEST(CPDF_MetadataTest, CheckSharedFormAsChild) { + + auto stream = pdfium::MakeRetain(); + stream->SetData(ByteStringView(data).raw_span()); +- CPDF_Metadata metadata(stream.Get()); ++ CPDF_Metadata metadata(stream); + + auto results = metadata.CheckForSharedForm(); + ASSERT_EQ(1U, results.size()); +@@ -100,7 +100,7 @@ TEST(CPDF_MetadataTest, CheckSharedFormAsNoAdhoc) { + + auto stream = pdfium::MakeRetain(); + stream->SetData(ByteStringView(data).raw_span()); +- CPDF_Metadata metadata(stream.Get()); ++ CPDF_Metadata metadata(stream); + + auto results = metadata.CheckForSharedForm(); + EXPECT_EQ(0U, results.size()); +@@ -116,7 +116,7 @@ TEST(CPDF_MetadataTest, CheckSharedFormWrongNamespace) { + + auto stream = pdfium::MakeRetain(); + stream->SetData(ByteStringView(data).raw_span()); +- CPDF_Metadata metadata(stream.Get()); ++ CPDF_Metadata metadata(stream); + + auto results = metadata.CheckForSharedForm(); + EXPECT_EQ(0U, results.size()); +@@ -146,7 +146,7 @@ TEST(CPDF_MetadataTest, CheckSharedFormMultipleErrors) { + + auto stream = pdfium::MakeRetain(); + stream->SetData(ByteStringView(data).raw_span()); +- CPDF_Metadata metadata(stream.Get()); ++ CPDF_Metadata metadata(stream); + + auto results = metadata.CheckForSharedForm(); + ASSERT_EQ(3U, results.size()); +diff --git a/core/fpdfdoc/cpdf_nametree.cpp b/core/fpdfdoc/cpdf_nametree.cpp +index b232540c1..3b60fab6f 100644 +--- a/core/fpdfdoc/cpdf_nametree.cpp ++++ b/core/fpdfdoc/cpdf_nametree.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,65 +6,83 @@ + + #include "core/fpdfdoc/cpdf_nametree.h" + ++#include + #include + #include + + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" ++#include "core/fxcrt/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/ptr_util.h" + + namespace { + + constexpr int kNameTreeMaxRecursion = 32; + +-std::pair GetNodeLimitsMaybeSwap(CPDF_Array* pLimits) { +- ASSERT(pLimits); ++std::pair GetNodeLimitsAndSanitize( ++ CPDF_Array* pLimits) { ++ DCHECK(pLimits); + WideString csLeft = pLimits->GetUnicodeTextAt(0); + WideString csRight = pLimits->GetUnicodeTextAt(1); + // If the lower limit is greater than the upper limit, swap them. + if (csLeft.Compare(csRight) > 0) { +- pLimits->SetNewAt(0, csRight); +- pLimits->SetNewAt(1, csLeft); ++ pLimits->SetNewAt(0, csRight.AsStringView()); ++ pLimits->SetNewAt(1, csLeft.AsStringView()); + csLeft = pLimits->GetUnicodeTextAt(0); + csRight = pLimits->GetUnicodeTextAt(1); + } ++ while (pLimits->size() > 2) ++ pLimits->RemoveAt(pLimits->size() - 1); + return {csLeft, csRight}; + } + + // Get the limit arrays that leaf array |pFind| is under in the tree with root + // |pNode|. |pLimits| will hold all the limit arrays from the leaf up to before + // the root. Return true if successful. +-bool GetNodeAncestorsLimits(CPDF_Dictionary* pNode, +- const CPDF_Array* pFind, +- int nLevel, +- std::vector* pLimits) { ++bool GetNodeAncestorsLimitsInternal(const RetainPtr& pNode, ++ const CPDF_Array* pFind, ++ int nLevel, ++ std::vector* pLimits) { + if (nLevel > kNameTreeMaxRecursion) + return false; + + if (pNode->GetArrayFor("Names") == pFind) { +- pLimits->push_back(pNode->GetArrayFor("Limits")); ++ pLimits->push_back(pNode->GetMutableArrayFor("Limits").Get()); + return true; + } + +- CPDF_Array* pKids = pNode->GetArrayFor("Kids"); ++ RetainPtr pKids = pNode->GetMutableArrayFor("Kids"); + if (!pKids) + return false; + + for (size_t i = 0; i < pKids->size(); ++i) { +- CPDF_Dictionary* pKid = pKids->GetDictAt(i); ++ RetainPtr pKid = pKids->GetMutableDictAt(i); + if (!pKid) + continue; + +- if (GetNodeAncestorsLimits(pKid, pFind, nLevel + 1, pLimits)) { +- pLimits->push_back(pNode->GetArrayFor("Limits")); ++ if (GetNodeAncestorsLimitsInternal(pKid, pFind, nLevel + 1, pLimits)) { ++ pLimits->push_back(pNode->GetMutableArrayFor("Limits").Get()); + return true; + } + } + return false; + } + ++// Wrapper for GetNodeAncestorsLimitsInternal() so callers do not need to know ++// about the details. ++std::vector GetNodeAncestorsLimits( ++ const RetainPtr& pNode, ++ const CPDF_Array* pFind) { ++ std::vector results; ++ GetNodeAncestorsLimitsInternal(pNode, pFind, 0, &results); ++ return results; ++} ++ + // Upon the deletion of |csName| from leaf array |pFind|, update the ancestors + // of |pFind|. Specifically, the limits of |pFind|'s ancestors will be updated + // if needed, and any ancestors that are now empty will be removed. +@@ -75,13 +93,13 @@ bool UpdateNodesAndLimitsUponDeletion(CPDF_Dictionary* pNode, + if (nLevel > kNameTreeMaxRecursion) + return false; + +- CPDF_Array* pLimits = pNode->GetArrayFor("Limits"); ++ RetainPtr pLimits = pNode->GetMutableArrayFor("Limits"); + WideString csLeft; + WideString csRight; + if (pLimits) +- std::tie(csLeft, csRight) = GetNodeLimitsMaybeSwap(pLimits); ++ std::tie(csLeft, csRight) = GetNodeLimitsAndSanitize(pLimits.Get()); + +- CPDF_Array* pNames = pNode->GetArrayFor("Names"); ++ RetainPtr pNames = pNode->GetArrayFor("Names"); + if (pNames) { + if (pNames != pFind) + return false; +@@ -101,23 +119,24 @@ bool UpdateNodesAndLimitsUponDeletion(CPDF_Dictionary* pNode, + if (wsName.Compare(csNewRight) > 0) + csNewRight = wsName; + } +- pLimits->SetNewAt(0, csNewLeft); +- pLimits->SetNewAt(1, csNewRight); ++ pLimits->SetNewAt(0, csNewLeft.AsStringView()); ++ pLimits->SetNewAt(1, csNewRight.AsStringView()); + return true; + } + +- CPDF_Array* pKids = pNode->GetArrayFor("Kids"); ++ RetainPtr pKids = pNode->GetMutableArrayFor("Kids"); + if (!pKids) + return false; + + // Loop through the kids to find the leaf array |pFind|. + for (size_t i = 0; i < pKids->size(); ++i) { +- CPDF_Dictionary* pKid = pKids->GetDictAt(i); ++ RetainPtr pKid = pKids->GetMutableDictAt(i); + if (!pKid) + continue; +- if (!UpdateNodesAndLimitsUponDeletion(pKid, pFind, csName, nLevel + 1)) ++ if (!UpdateNodesAndLimitsUponDeletion(pKid.Get(), pFind, csName, ++ nLevel + 1)) { + continue; +- ++ } + // Remove this child node if it's empty. + if ((pKid->KeyExist("Names") && pKid->GetArrayFor("Names")->IsEmpty()) || + (pKid->KeyExist("Kids") && pKid->GetArrayFor("Kids")->IsEmpty())) { +@@ -133,16 +152,40 @@ bool UpdateNodesAndLimitsUponDeletion(CPDF_Dictionary* pNode, + WideString csNewLeft = csRight; + WideString csNewRight = csLeft; + for (size_t j = 0; j < pKids->size(); ++j) { +- CPDF_Array* pKidLimits = pKids->GetDictAt(j)->GetArrayFor("Limits"); +- ASSERT(pKidLimits); ++ RetainPtr pKidLimits = ++ pKids->GetDictAt(j)->GetArrayFor("Limits"); ++ DCHECK(pKidLimits); + if (pKidLimits->GetUnicodeTextAt(0).Compare(csNewLeft) < 0) + csNewLeft = pKidLimits->GetUnicodeTextAt(0); + if (pKidLimits->GetUnicodeTextAt(1).Compare(csNewRight) > 0) + csNewRight = pKidLimits->GetUnicodeTextAt(1); + } +- pLimits->SetNewAt(0, csNewLeft); +- pLimits->SetNewAt(1, csNewRight); ++ pLimits->SetNewAt(0, csNewLeft.AsStringView()); ++ pLimits->SetNewAt(1, csNewRight.AsStringView()); ++ return true; ++ } ++ return false; ++} ++ ++bool IsTraversedObject(const CPDF_Object* obj, ++ std::set* seen_obj_nums) { ++ uint32_t obj_num = obj->GetObjNum(); ++ if (!obj_num) ++ return false; ++ ++ bool inserted = seen_obj_nums->insert(obj_num).second; ++ return !inserted; ++} ++ ++bool IsArrayWithTraversedObject(const CPDF_Array* array, ++ std::set* seen_obj_nums) { ++ if (IsTraversedObject(array, seen_obj_nums)) + return true; ++ ++ CPDF_ArrayLocker locker(array); ++ for (const auto& item : locker) { ++ if (IsTraversedObject(item.Get(), seen_obj_nums)) ++ return true; + } + return false; + } +@@ -153,21 +196,28 @@ bool UpdateNodesAndLimitsUponDeletion(CPDF_Dictionary* pNode, + // will be the index of |csName| in |ppFind|. If |csName| is not found, |ppFind| + // will be the leaf array that |csName| should be added to, and |pFindIndex| + // will be the index that it should be added at. +-CPDF_Object* SearchNameNodeByName(CPDF_Dictionary* pNode, +- const WideString& csName, +- int nLevel, +- size_t* nIndex, +- CPDF_Array** ppFind, +- int* pFindIndex) { ++RetainPtr SearchNameNodeByNameInternal( ++ const RetainPtr& pNode, ++ const WideString& csName, ++ int nLevel, ++ size_t* nIndex, ++ RetainPtr* ppFind, ++ int* pFindIndex, ++ std::set* seen_obj_nums) { + if (nLevel > kNameTreeMaxRecursion) + return nullptr; + +- CPDF_Array* pLimits = pNode->GetArrayFor("Limits"); +- CPDF_Array* pNames = pNode->GetArrayFor("Names"); ++ RetainPtr pLimits = pNode->GetMutableArrayFor("Limits"); ++ RetainPtr pNames = pNode->GetMutableArrayFor("Names"); ++ if (pNames && IsArrayWithTraversedObject(pNames.Get(), seen_obj_nums)) ++ pNames.Reset(); ++ if (pLimits && IsArrayWithTraversedObject(pLimits.Get(), seen_obj_nums)) ++ pLimits.Reset(); ++ + if (pLimits) { + WideString csLeft; + WideString csRight; +- std::tie(csLeft, csRight) = GetNodeLimitsMaybeSwap(pLimits); ++ std::tie(csLeft, csRight) = GetNodeLimitsAndSanitize(pLimits.Get()); + // Skip this node if the name to look for is smaller than its lower limit. + if (csName.Compare(csLeft) < 0) + return nullptr; +@@ -178,8 +228,7 @@ CPDF_Object* SearchNameNodeByName(CPDF_Dictionary* pNode, + if (ppFind) + *ppFind = pNames; + if (pFindIndex) +- *pFindIndex = pNames->size() / 2 - 1; +- ++ *pFindIndex = fxcrt::CollectionSize(*pNames) / 2 - 1; + return nullptr; + } + } +@@ -195,7 +244,7 @@ CPDF_Object* SearchNameNodeByName(CPDF_Dictionary* pNode, + if (ppFind) + *ppFind = pNames; + if (pFindIndex) +- *pFindIndex = i; ++ *pFindIndex = pdfium::base::checked_cast(i); + if (iCompare < 0) + continue; + +@@ -207,127 +256,248 @@ CPDF_Object* SearchNameNodeByName(CPDF_Dictionary* pNode, + } + + // Search through the node's children. +- CPDF_Array* pKids = pNode->GetArrayFor("Kids"); +- if (!pKids) ++ RetainPtr pKids = pNode->GetMutableArrayFor("Kids"); ++ if (!pKids || IsTraversedObject(pKids.Get(), seen_obj_nums)) + return nullptr; + + for (size_t i = 0; i < pKids->size(); i++) { +- CPDF_Dictionary* pKid = pKids->GetDictAt(i); +- if (!pKid) ++ RetainPtr pKid = pKids->GetMutableDictAt(i); ++ if (!pKid || IsTraversedObject(pKid.Get(), seen_obj_nums)) + continue; + +- CPDF_Object* pFound = SearchNameNodeByName(pKid, csName, nLevel + 1, nIndex, +- ppFind, pFindIndex); ++ RetainPtr pFound = SearchNameNodeByNameInternal( ++ pKid, csName, nLevel + 1, nIndex, ppFind, pFindIndex, seen_obj_nums); + if (pFound) + return pFound; + } + return nullptr; + } + +-// Get the key-value pair at |nIndex| in the tree with root |pNode|. If +-// successful, return the value object; |csName| will be the key, |ppFind| +-// will be the leaf array that this pair is in, and |pFindIndex| will be the +-// index of the pair in |pFind|. +-CPDF_Object* SearchNameNodeByIndex(CPDF_Dictionary* pNode, +- size_t nIndex, +- int nLevel, +- size_t* nCurIndex, +- WideString* csName, +- CPDF_Array** ppFind, +- int* pFindIndex) { ++// Wrapper for SearchNameNodeByNameInternal() so callers do not need to know ++// about the details. ++RetainPtr SearchNameNodeByName( ++ const RetainPtr& pNode, ++ const WideString& csName, ++ RetainPtr* ppFind, ++ int* pFindIndex) { ++ size_t nIndex = 0; ++ std::set seen_obj_nums; ++ return SearchNameNodeByNameInternal(pNode, csName, 0, &nIndex, ppFind, ++ pFindIndex, &seen_obj_nums); ++} ++ ++struct IndexSearchResult { ++ // For the n-th object in a tree, the key and value. ++ WideString key; ++ RetainPtr value; ++ // The leaf node that holds `key` and `value`. ++ RetainPtr container; ++ // The index for `key` in `container`. Must be even. ++ size_t index; ++}; ++ ++// Find the `nTargetPairIndex` node in the tree with root `pNode`. `nLevel` ++// tracks the recursion level and `nCurPairIndex` tracks the progress towards ++// `nTargetPairIndex`. ++absl::optional SearchNameNodeByIndexInternal( ++ CPDF_Dictionary* pNode, ++ size_t nTargetPairIndex, ++ int nLevel, ++ size_t* nCurPairIndex) { + if (nLevel > kNameTreeMaxRecursion) +- return nullptr; ++ return absl::nullopt; + +- CPDF_Array* pNames = pNode->GetArrayFor("Names"); ++ RetainPtr pNames = pNode->GetMutableArrayFor("Names"); + if (pNames) { + size_t nCount = pNames->size() / 2; +- if (nIndex >= *nCurIndex + nCount) { +- *nCurIndex += nCount; +- return nullptr; ++ if (nTargetPairIndex >= *nCurPairIndex + nCount) { ++ *nCurPairIndex += nCount; ++ return absl::nullopt; + } +- if (ppFind) +- *ppFind = pNames; +- if (pFindIndex) +- *pFindIndex = nIndex - *nCurIndex; + +- *csName = pNames->GetUnicodeTextAt((nIndex - *nCurIndex) * 2); +- return pNames->GetDirectObjectAt((nIndex - *nCurIndex) * 2 + 1); ++ size_t index = 2 * (nTargetPairIndex - *nCurPairIndex); ++ RetainPtr value = pNames->GetMutableDirectObjectAt(index + 1); ++ if (!value) ++ return absl::nullopt; ++ ++ IndexSearchResult result; ++ result.key = pNames->GetUnicodeTextAt(index); ++ result.value = std::move(value); ++ result.container = std::move(pNames); ++ result.index = index; ++ return result; + } + +- CPDF_Array* pKids = pNode->GetArrayFor("Kids"); ++ RetainPtr pKids = pNode->GetMutableArrayFor("Kids"); + if (!pKids) +- return nullptr; ++ return absl::nullopt; + + for (size_t i = 0; i < pKids->size(); i++) { +- CPDF_Dictionary* pKid = pKids->GetDictAt(i); ++ RetainPtr pKid = pKids->GetMutableDictAt(i); + if (!pKid) + continue; +- CPDF_Object* pFound = SearchNameNodeByIndex( +- pKid, nIndex, nLevel + 1, nCurIndex, csName, ppFind, pFindIndex); +- if (pFound) +- return pFound; ++ absl::optional result = SearchNameNodeByIndexInternal( ++ pKid.Get(), nTargetPairIndex, nLevel + 1, nCurPairIndex); ++ if (result.has_value()) ++ return result; + } +- return nullptr; ++ return absl::nullopt; ++} ++ ++// Wrapper for SearchNameNodeByIndexInternal() so callers do not need to know ++// about the details. ++absl::optional SearchNameNodeByIndex( ++ CPDF_Dictionary* pNode, ++ size_t nTargetPairIndex) { ++ size_t nCurPairIndex = 0; ++ return SearchNameNodeByIndexInternal(pNode, nTargetPairIndex, 0, ++ &nCurPairIndex); + } + + // Get the total number of key-value pairs in the tree with root |pNode|. +-size_t CountNamesInternal(CPDF_Dictionary* pNode, int nLevel) { ++size_t CountNamesInternal(const CPDF_Dictionary* pNode, ++ int nLevel, ++ std::set& seen) { + if (nLevel > kNameTreeMaxRecursion) + return 0; + +- CPDF_Array* pNames = pNode->GetArrayFor("Names"); ++ const bool inserted = seen.insert(pNode).second; ++ if (!inserted) ++ return 0; ++ ++ RetainPtr pNames = pNode->GetArrayFor("Names"); + if (pNames) + return pNames->size() / 2; + +- CPDF_Array* pKids = pNode->GetArrayFor("Kids"); ++ RetainPtr pKids = pNode->GetArrayFor("Kids"); + if (!pKids) + return 0; + + size_t nCount = 0; + for (size_t i = 0; i < pKids->size(); i++) { +- CPDF_Dictionary* pKid = pKids->GetDictAt(i); ++ RetainPtr pKid = pKids->GetDictAt(i); + if (!pKid) + continue; + +- nCount += CountNamesInternal(pKid, nLevel + 1); ++ nCount += CountNamesInternal(pKid.Get(), nLevel + 1, seen); + } + return nCount; + } + ++RetainPtr GetNamedDestFromObject( ++ RetainPtr obj) { ++ RetainPtr array = ToArray(obj); ++ if (array) ++ return array; ++ RetainPtr dict = ToDictionary(obj); ++ if (dict) ++ return dict->GetArrayFor("D"); ++ return nullptr; ++} ++ ++RetainPtr LookupOldStyleNamedDest(CPDF_Document* pDoc, ++ const ByteString& name) { ++ RetainPtr pDests = ++ pDoc->GetRoot()->GetDictFor("Dests"); ++ if (!pDests) ++ return nullptr; ++ return GetNamedDestFromObject(pDests->GetDirectObjectFor(name)); ++} ++ + } // namespace + +-CPDF_NameTree::CPDF_NameTree(CPDF_Dictionary* pRoot) : m_pRoot(pRoot) {} ++CPDF_NameTree::CPDF_NameTree(RetainPtr pRoot) ++ : m_pRoot(std::move(pRoot)) { ++ DCHECK(m_pRoot); ++} ++ ++CPDF_NameTree::~CPDF_NameTree() = default; + +-CPDF_NameTree::CPDF_NameTree(CPDF_Document* pDoc, const ByteString& category) { +- CPDF_Dictionary* pRoot = pDoc->GetRoot(); ++// static ++std::unique_ptr CPDF_NameTree::Create( ++ CPDF_Document* pDoc, ++ const ByteString& category) { ++ RetainPtr pRoot = pDoc->GetMutableRoot(); + if (!pRoot) +- return; ++ return nullptr; + +- CPDF_Dictionary* pNames = pRoot->GetDictFor("Names"); ++ RetainPtr pNames = pRoot->GetMutableDictFor("Names"); + if (!pNames) +- return; ++ return nullptr; ++ ++ RetainPtr pCategory = pNames->GetMutableDictFor(category); ++ if (!pCategory) ++ return nullptr; ++ ++ return pdfium::WrapUnique( ++ new CPDF_NameTree(std::move(pCategory))); // Private ctor. ++} ++ ++// static ++std::unique_ptr CPDF_NameTree::CreateWithRootNameArray( ++ CPDF_Document* pDoc, ++ const ByteString& category) { ++ RetainPtr pRoot = pDoc->GetMutableRoot(); ++ if (!pRoot) ++ return nullptr; + +- m_pRoot.Reset(pNames->GetDictFor(category)); ++ // Retrieve the document's Names dictionary; create it if missing. ++ RetainPtr pNames = pRoot->GetMutableDictFor("Names"); ++ if (!pNames) { ++ pNames = pDoc->NewIndirect(); ++ pRoot->SetNewFor("Names", pDoc, pNames->GetObjNum()); ++ } ++ ++ // Create the |category| dictionary if missing. ++ RetainPtr pCategory = pNames->GetMutableDictFor(category); ++ if (!pCategory) { ++ pCategory = pDoc->NewIndirect(); ++ pCategory->SetNewFor("Names"); ++ pNames->SetNewFor(category, pDoc, pCategory->GetObjNum()); ++ } ++ ++ return pdfium::WrapUnique(new CPDF_NameTree(pCategory)); // Private ctor. ++} ++ ++// static ++std::unique_ptr CPDF_NameTree::CreateForTesting( ++ CPDF_Dictionary* pRoot) { ++ return pdfium::WrapUnique( ++ new CPDF_NameTree(pdfium::WrapRetain(pRoot))); // Private ctor. + } + +-CPDF_NameTree::~CPDF_NameTree() {} ++// static ++RetainPtr CPDF_NameTree::LookupNamedDest( ++ CPDF_Document* pDoc, ++ const ByteString& name) { ++ RetainPtr dest_array; ++ std::unique_ptr name_tree = Create(pDoc, "Dests"); ++ if (name_tree) ++ dest_array = name_tree->LookupNewStyleNamedDest(name); ++ if (!dest_array) ++ dest_array = LookupOldStyleNamedDest(pDoc, name); ++ return dest_array; ++} + + size_t CPDF_NameTree::GetCount() const { +- return m_pRoot ? CountNamesInternal(m_pRoot.Get(), 0) : 0; ++ std::set seen; ++ return CountNamesInternal(m_pRoot.Get(), 0, seen); + } + + bool CPDF_NameTree::AddValueAndName(RetainPtr pObj, + const WideString& name) { +- if (!m_pRoot) +- return false; +- +- size_t nIndex = 0; +- CPDF_Array* pFind = nullptr; ++ RetainPtr pFind; + int nFindIndex = -1; +- // Fail if the tree already contains this name or if the tree is too deep. +- if (SearchNameNodeByName(m_pRoot.Get(), name, 0, &nIndex, &pFind, +- &nFindIndex)) { +- return false; ++ // Handle the corner case where the root node is empty. i.e. No kids and no ++ // names. In which case, just insert into it and skip all the searches. ++ RetainPtr pNames = m_pRoot->GetMutableArrayFor("Names"); ++ if (pNames && pNames->IsEmpty() && !m_pRoot->GetArrayFor("Kids")) ++ pFind = pNames; ++ ++ if (!pFind) { ++ // Fail if the tree already contains this name or if the tree is too deep. ++ if (SearchNameNodeByName(m_pRoot, name, &pFind, &nFindIndex)) ++ return false; + } + + // If the returned |pFind| is a nullptr, then |name| is smaller than all +@@ -335,96 +505,80 @@ bool CPDF_NameTree::AddValueAndName(RetainPtr pObj, + // |name| into. We instead will find the leftmost leaf array in which to place + // |name| and |pObj|. + if (!pFind) { +- size_t nCurIndex = 0; +- WideString csName; +- SearchNameNodeByIndex(m_pRoot.Get(), 0, 0, &nCurIndex, &csName, &pFind, +- nullptr); ++ absl::optional result = ++ SearchNameNodeByIndex(m_pRoot.Get(), 0); ++ if (!result.has_value()) { ++ // Give up if that fails too. ++ return false; ++ } ++ ++ pFind = result.value().container; ++ DCHECK(pFind); + } +- // Give up if that fails too. +- if (!pFind) +- return false; + + // Insert the name and the object into the leaf array found. Note that the + // insertion position is right after the key-value pair returned by |index|. + size_t nNameIndex = (nFindIndex + 1) * 2; + size_t nValueIndex = nNameIndex + 1; +- pFind->InsertNewAt(nNameIndex, name); ++ pFind->InsertNewAt(nNameIndex, name.AsStringView()); + pFind->InsertAt(nValueIndex, std::move(pObj)); + + // Expand the limits that the newly added name is under, if the name falls + // outside of the limits of its leaf array or any arrays above it. +- std::vector pLimits; +- GetNodeAncestorsLimits(m_pRoot.Get(), pFind, 0, &pLimits); +- for (auto* pLimit : pLimits) { +- if (!pLimit) ++ std::vector all_limits = ++ GetNodeAncestorsLimits(m_pRoot, pFind.Get()); ++ for (auto* pLimits : all_limits) { ++ if (!pLimits) + continue; + +- if (name.Compare(pLimit->GetUnicodeTextAt(0)) < 0) +- pLimit->SetNewAt(0, name); ++ if (name.Compare(pLimits->GetUnicodeTextAt(0)) < 0) ++ pLimits->SetNewAt(0, name.AsStringView()); + +- if (name.Compare(pLimit->GetUnicodeTextAt(1)) > 0) +- pLimit->SetNewAt(1, name); ++ if (name.Compare(pLimits->GetUnicodeTextAt(1)) > 0) ++ pLimits->SetNewAt(1, name.AsStringView()); + } + return true; + } + +-bool CPDF_NameTree::DeleteValueAndName(int nIndex) { +- if (!m_pRoot) +- return false; +- +- size_t nCurIndex = 0; +- WideString csName; +- CPDF_Array* pFind = nullptr; +- int nFindIndex = -1; +- // Fail if the tree does not contain |nIndex|. +- if (!SearchNameNodeByIndex(m_pRoot.Get(), nIndex, 0, &nCurIndex, &csName, +- &pFind, &nFindIndex)) { ++bool CPDF_NameTree::DeleteValueAndName(size_t nIndex) { ++ absl::optional result = ++ SearchNameNodeByIndex(m_pRoot.Get(), nIndex); ++ if (!result) { ++ // Fail if the tree does not contain |nIndex|. + return false; + } + + // Remove the name and the object from the leaf array |pFind|. +- pFind->RemoveAt(nFindIndex * 2); +- pFind->RemoveAt(nFindIndex * 2); ++ RetainPtr pFind = result.value().container; ++ pFind->RemoveAt(result.value().index + 1); ++ pFind->RemoveAt(result.value().index); + + // Delete empty nodes and update the limits of |pFind|'s ancestors as needed. +- UpdateNodesAndLimitsUponDeletion(m_pRoot.Get(), pFind, csName, 0); ++ UpdateNodesAndLimitsUponDeletion(m_pRoot.Get(), pFind.Get(), ++ result.value().key, 0); + return true; + } + +-CPDF_Object* CPDF_NameTree::LookupValueAndName(int nIndex, +- WideString* csName) const { +- csName->clear(); +- if (!m_pRoot) ++RetainPtr CPDF_NameTree::LookupValueAndName( ++ size_t nIndex, ++ WideString* csName) const { ++ absl::optional result = ++ SearchNameNodeByIndex(m_pRoot.Get(), nIndex); ++ if (!result) { ++ csName->clear(); + return nullptr; ++ } + +- size_t nCurIndex = 0; +- return SearchNameNodeByIndex(m_pRoot.Get(), nIndex, 0, &nCurIndex, csName, +- nullptr, nullptr); ++ *csName = std::move(result.value().key); ++ return result.value().value; + } + +-CPDF_Object* CPDF_NameTree::LookupValue(const WideString& csName) const { +- if (!m_pRoot) +- return nullptr; +- +- size_t nIndex = 0; +- return SearchNameNodeByName(m_pRoot.Get(), csName, 0, &nIndex, nullptr, +- nullptr); ++RetainPtr CPDF_NameTree::LookupValue( ++ const WideString& csName) const { ++ return SearchNameNodeByName(m_pRoot, csName, nullptr, nullptr); + } + +-CPDF_Array* CPDF_NameTree::LookupNamedDest(CPDF_Document* pDoc, +- const WideString& sName) { +- CPDF_Object* pValue = LookupValue(sName); +- if (!pValue) { +- CPDF_Dictionary* pDests = pDoc->GetRoot()->GetDictFor("Dests"); +- if (!pDests) +- return nullptr; +- pValue = pDests->GetDirectObjectFor(PDF_EncodeText(sName)); +- } +- if (!pValue) +- return nullptr; +- if (CPDF_Array* pArray = pValue->AsArray()) +- return pArray; +- if (CPDF_Dictionary* pDict = pValue->AsDictionary()) +- return pDict->GetArrayFor("D"); +- return nullptr; ++RetainPtr CPDF_NameTree::LookupNewStyleNamedDest( ++ const ByteString& sName) { ++ return GetNamedDestFromObject(LookupValue(PDF_DecodeText(sName.raw_span()))); + } +diff --git a/core/fpdfdoc/cpdf_nametree.h b/core/fpdfdoc/cpdf_nametree.h +index b018ae766..b40b2329c 100644 +--- a/core/fpdfdoc/cpdf_nametree.h ++++ b/core/fpdfdoc/cpdf_nametree.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,8 @@ + #ifndef CORE_FPDFDOC_CPDF_NAMETREE_H_ + #define CORE_FPDFDOC_CPDF_NAMETREE_H_ + ++#include ++ + #include + + #include "core/fxcrt/fx_string.h" +@@ -19,22 +21,42 @@ class CPDF_Object; + + class CPDF_NameTree { + public: +- explicit CPDF_NameTree(CPDF_Dictionary* pRoot); +- CPDF_NameTree(CPDF_Document* pDoc, const ByteString& category); ++ CPDF_NameTree(const CPDF_NameTree&) = delete; ++ CPDF_NameTree& operator=(const CPDF_NameTree&) = delete; + ~CPDF_NameTree(); + ++ static std::unique_ptr Create(CPDF_Document* pDoc, ++ const ByteString& category); ++ ++ // If necessary, create missing Names dictionary in |pDoc|, and/or missing ++ // Names array in the dictionary that corresponds to |category|, if necessary. ++ // Returns nullptr on failure. ++ static std::unique_ptr CreateWithRootNameArray( ++ CPDF_Document* pDoc, ++ const ByteString& category); ++ ++ static std::unique_ptr CreateForTesting( ++ CPDF_Dictionary* pRoot); ++ ++ static RetainPtr LookupNamedDest(CPDF_Document* doc, ++ const ByteString& name); ++ + bool AddValueAndName(RetainPtr pObj, const WideString& name); +- bool DeleteValueAndName(int nIndex); ++ bool DeleteValueAndName(size_t nIndex); + +- CPDF_Object* LookupValueAndName(int nIndex, WideString* csName) const; +- CPDF_Object* LookupValue(const WideString& csName) const; +- CPDF_Array* LookupNamedDest(CPDF_Document* pDoc, const WideString& sName); ++ RetainPtr LookupValueAndName(size_t nIndex, ++ WideString* csName) const; ++ RetainPtr LookupValue(const WideString& csName) const; + + size_t GetCount() const; +- CPDF_Dictionary* GetRootForTest() const { return m_pRoot.Get(); } ++ CPDF_Dictionary* GetRootForTesting() const { return m_pRoot.Get(); } + + private: +- RetainPtr m_pRoot; ++ explicit CPDF_NameTree(RetainPtr pRoot); ++ ++ RetainPtr LookupNewStyleNamedDest(const ByteString& name); ++ ++ RetainPtr const m_pRoot; + }; + + #endif // CORE_FPDFDOC_CPDF_NAMETREE_H_ +diff --git a/core/fpdfdoc/cpdf_nametree_unittest.cpp b/core/fpdfdoc/cpdf_nametree_unittest.cpp +index 13b6b5cb1..a70359c58 100644 +--- a/core/fpdfdoc/cpdf_nametree_unittest.cpp ++++ b/core/fpdfdoc/cpdf_nametree_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,70 +7,96 @@ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fpdfapi/parser/cpdf_string.h" ++#include "core/fxcrt/retain_ptr.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + + namespace { + +-void AddNameKeyValue(CPDF_Array* pNames, const char* key, const int value) { +- pNames->AddNew(key, false); +- pNames->AddNew(value); ++void AddNameKeyValue(CPDF_Array* names, const char* key, int value) { ++ names->AppendNew(key, false); ++ names->AppendNew(value); + } + +-void CheckNameKeyValue(CPDF_Array* pNames, +- const int index, ++void CheckNameKeyValue(const CPDF_Array* names, ++ int pair_index, + const char* key, +- const int value) { +- EXPECT_STREQ(key, pNames->GetStringAt(index * 2).c_str()); +- EXPECT_EQ(value, pNames->GetIntegerAt(index * 2 + 1)); ++ int value) { ++ ASSERT_TRUE(names); ++ EXPECT_STREQ(key, names->GetByteStringAt(pair_index * 2).c_str()); ++ EXPECT_EQ(value, names->GetIntegerAt(pair_index * 2 + 1)); + } + +-void AddLimitsArray(CPDF_Dictionary* pNode, ++void AddLimitsArray(CPDF_Dictionary* node, + const char* least, + const char* greatest) { +- CPDF_Array* pLimits = pNode->SetNewFor("Limits"); +- pLimits->AddNew(least, false); +- pLimits->AddNew(greatest, false); ++ auto limits = node->SetNewFor("Limits"); ++ limits->AppendNew(least, false); ++ limits->AppendNew(greatest, false); + } + +-void CheckLimitsArray(CPDF_Dictionary* pNode, ++void CheckLimitsArray(const CPDF_Dictionary* node, + const char* least, + const char* greatest) { +- CPDF_Array* pLimits = pNode->GetArrayFor("Limits"); +- ASSERT_TRUE(pLimits); +- EXPECT_STREQ(least, pLimits->GetStringAt(0).c_str()); +- EXPECT_STREQ(greatest, pLimits->GetStringAt(1).c_str()); ++ ASSERT_TRUE(node); ++ RetainPtr limits = node->GetArrayFor("Limits"); ++ ASSERT_TRUE(limits); ++ EXPECT_EQ(2u, limits->size()); ++ RetainPtr left = limits->GetStringAt(0); ++ ASSERT_TRUE(left); ++ RetainPtr right = limits->GetStringAt(1); ++ ASSERT_TRUE(right); ++ EXPECT_STREQ(least, left->GetString().c_str()); ++ EXPECT_STREQ(greatest, right->GetString().c_str()); + } + ++// Set up a name tree with 3 levels and 5 nodes, per diagram: ++// ++// [root] ++// | ++// | ++// | ++// [pKid1] ++// | ++// +------------+ ++// | | ++// [pGrandKid2] [pGrandKid3] ++// | {9.txt: 999} ++// | ++// +-----------------+ ++// | | ++// [pGreatGrandKid4] [pGreatGrandKid5] ++// {1.txt: 111} {3.txt: 333} ++// {2.txt: 222} {5.txt: 555} ++// + void FillNameTreeDict(CPDF_Dictionary* pRootDict) { +- CPDF_Array* pKids = pRootDict->SetNewFor("Kids"); +- CPDF_Dictionary* pKid1 = pKids->AddNew(); ++ auto pRootKids = pRootDict->SetNewFor("Kids"); ++ auto pKid1 = pRootKids->AppendNew(); + + // Make the lower and upper limit out of order on purpose. +- AddLimitsArray(pKid1, "9.txt", "1.txt"); +- pKids = pKid1->SetNewFor("Kids"); +- CPDF_Dictionary* pKid2 = pKids->AddNew(); +- CPDF_Dictionary* pKid3 = pKids->AddNew(); ++ AddLimitsArray(pKid1.Get(), "9.txt", "1.txt"); ++ auto pKids1Kids = pKid1->SetNewFor("Kids"); ++ auto pGrandKid2 = pKids1Kids->AppendNew(); ++ auto pGrandKid3 = pKids1Kids->AppendNew(); + +- AddLimitsArray(pKid2, "1.txt", "5.txt"); +- pKids = pKid2->SetNewFor("Kids"); +- CPDF_Dictionary* pKid4 = pKids->AddNew(); +- CPDF_Dictionary* pKid5 = pKids->AddNew(); ++ AddLimitsArray(pGrandKid2.Get(), "1.txt", "5.txt"); ++ auto pGrandKid2Kids = pGrandKid2->SetNewFor("Kids"); ++ auto pGreatGrandKid4 = pGrandKid2Kids->AppendNew(); ++ auto pGreatGrandKid5 = pGrandKid2Kids->AppendNew(); + +- AddLimitsArray(pKid3, "9.txt", "9.txt"); +- CPDF_Array* pNames = pKid3->SetNewFor("Names"); +- AddNameKeyValue(pNames, "9.txt", 999); ++ AddLimitsArray(pGrandKid3.Get(), "9.txt", "9.txt"); ++ auto pNames = pGrandKid3->SetNewFor("Names"); ++ AddNameKeyValue(pNames.Get(), "9.txt", 999); + + // Make the lower and upper limit out of order on purpose. +- AddLimitsArray(pKid4, "2.txt", "1.txt"); +- pNames = pKid4->SetNewFor("Names"); +- AddNameKeyValue(pNames, "1.txt", 111); +- AddNameKeyValue(pNames, "2.txt", 222); +- +- AddLimitsArray(pKid5, "3.txt", "5.txt"); +- pNames = pKid5->SetNewFor("Names"); +- AddNameKeyValue(pNames, "3.txt", 333); +- AddNameKeyValue(pNames, "5.txt", 555); ++ AddLimitsArray(pGreatGrandKid4.Get(), "2.txt", "1.txt"); ++ pNames = pGreatGrandKid4->SetNewFor("Names"); ++ AddNameKeyValue(pNames.Get(), "1.txt", 111); ++ AddNameKeyValue(pNames.Get(), "2.txt", 222); ++ ++ AddLimitsArray(pGreatGrandKid5.Get(), "3.txt", "5.txt"); ++ pNames = pGreatGrandKid5->SetNewFor("Names"); ++ AddNameKeyValue(pNames.Get(), "3.txt", 333); ++ AddNameKeyValue(pNames.Get(), "5.txt", 555); + } + + } // namespace +@@ -78,221 +104,283 @@ void FillNameTreeDict(CPDF_Dictionary* pRootDict) { + TEST(cpdf_nametree, GetUnicodeNameWithBOM) { + // Set up the root dictionary with a Names array. + auto pRootDict = pdfium::MakeRetain(); +- CPDF_Array* pNames = pRootDict->SetNewFor("Names"); ++ auto pNames = pRootDict->SetNewFor("Names"); + + // Add the key "1" (with BOM) and value 100 into the array. +- std::ostringstream buf; + constexpr char kData[] = "\xFE\xFF\x00\x31"; +- for (size_t i = 0; i < sizeof(kData); ++i) +- buf.put(kData[i]); +- pNames->AddNew(ByteString(buf), true); +- pNames->AddNew(100); ++ pNames->AppendNew(ByteString(kData, sizeof(kData) - 1), true); ++ pNames->AppendNew(100); + + // Check that the key is as expected. +- CPDF_NameTree nameTree(pRootDict.Get()); +- WideString storedName; +- nameTree.LookupValueAndName(0, &storedName); +- EXPECT_STREQ(L"1", storedName.c_str()); ++ std::unique_ptr name_tree = ++ CPDF_NameTree::CreateForTesting(pRootDict.Get()); ++ WideString stored_name; ++ name_tree->LookupValueAndName(0, &stored_name); ++ EXPECT_STREQ(L"1", stored_name.c_str()); + + // Check that the correct value object can be obtained by looking up "1". +- WideString matchName = L"1"; +- CPDF_Object* pObj = nameTree.LookupValue(matchName); +- ASSERT_TRUE(pObj->IsNumber()); +- EXPECT_EQ(100, pObj->AsNumber()->GetInteger()); ++ RetainPtr pNumber = ToNumber(name_tree->LookupValue(L"1")); ++ ASSERT_TRUE(pNumber); ++ EXPECT_EQ(100, pNumber->GetInteger()); ++} ++ ++TEST(cpdf_nametree, GetFromTreeWithLimitsArrayWith4Items) { ++ // After creating a name tree, mutate a /Limits array so it has excess ++ // elements. ++ auto pRootDict = pdfium::MakeRetain(); ++ FillNameTreeDict(pRootDict.Get()); ++ RetainPtr pKid1 = ++ pRootDict->GetMutableArrayFor("Kids")->GetMutableDictAt(0); ++ RetainPtr pGrandKid3 = ++ pKid1->GetMutableArrayFor("Kids")->GetMutableDictAt(1); ++ RetainPtr pLimits = pGrandKid3->GetMutableArrayFor("Limits"); ++ ASSERT_EQ(2u, pLimits->size()); ++ pLimits->AppendNew(5); ++ pLimits->AppendNew(6); ++ ASSERT_EQ(4u, pLimits->size()); ++ std::unique_ptr name_tree = ++ CPDF_NameTree::CreateForTesting(pRootDict.Get()); ++ ++ RetainPtr pNumber = ++ ToNumber(name_tree->LookupValue(L"9.txt")); ++ ASSERT_TRUE(pNumber); ++ EXPECT_EQ(999, pNumber->GetInteger()); ++ CheckLimitsArray(pKid1.Get(), "1.txt", "9.txt"); ++ CheckLimitsArray(pGrandKid3.Get(), "9.txt", "9.txt"); + } + + TEST(cpdf_nametree, AddIntoNames) { + // Set up a name tree with a single Names array. + auto pRootDict = pdfium::MakeRetain(); +- CPDF_Array* pNames = pRootDict->SetNewFor("Names"); +- AddNameKeyValue(pNames, "2.txt", 222); +- AddNameKeyValue(pNames, "7.txt", 777); ++ auto pNames = pRootDict->SetNewFor("Names"); ++ AddNameKeyValue(pNames.Get(), "2.txt", 222); ++ AddNameKeyValue(pNames.Get(), "7.txt", 777); ++ ++ std::unique_ptr name_tree = ++ CPDF_NameTree::CreateForTesting(pRootDict.Get()); ++ ++ // Insert a name that already exists in the names array. ++ EXPECT_FALSE(name_tree->AddValueAndName(pdfium::MakeRetain(111), ++ L"2.txt")); ++ ++ // Insert in the beginning of the names array. ++ EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain(111), ++ L"1.txt")); ++ ++ // Insert in the middle of the names array. ++ EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain(555), ++ L"5.txt")); ++ ++ // Insert at the end of the names array. ++ EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain(999), ++ L"9.txt")); ++ ++ // Check that the names array has the expected key-value pairs. ++ CheckNameKeyValue(pNames.Get(), 0, "1.txt", 111); ++ CheckNameKeyValue(pNames.Get(), 1, "2.txt", 222); ++ CheckNameKeyValue(pNames.Get(), 2, "5.txt", 555); ++ CheckNameKeyValue(pNames.Get(), 3, "7.txt", 777); ++ CheckNameKeyValue(pNames.Get(), 4, "9.txt", 999); ++} ++ ++TEST(cpdf_nametree, AddIntoEmptyNames) { ++ // Set up a name tree with an empty Names array. ++ auto pRootDict = pdfium::MakeRetain(); ++ auto pNames = pRootDict->SetNewFor("Names"); ++ ++ std::unique_ptr name_tree = ++ CPDF_NameTree::CreateForTesting(pRootDict.Get()); + +- CPDF_NameTree nameTree(pRootDict.Get()); +- pNames = nameTree.GetRootForTest()->GetArrayFor("Names"); ++ // Insert a name should work. ++ EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain(111), ++ L"2.txt")); + + // Insert a name that already exists in the names array. +- EXPECT_FALSE( +- nameTree.AddValueAndName(pdfium::MakeRetain(111), L"2.txt")); ++ EXPECT_FALSE(name_tree->AddValueAndName(pdfium::MakeRetain(111), ++ L"2.txt")); + + // Insert in the beginning of the names array. +- EXPECT_TRUE( +- nameTree.AddValueAndName(pdfium::MakeRetain(111), L"1.txt")); ++ EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain(111), ++ L"1.txt")); + + // Insert in the middle of the names array. +- EXPECT_TRUE( +- nameTree.AddValueAndName(pdfium::MakeRetain(555), L"5.txt")); ++ EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain(555), ++ L"5.txt")); + + // Insert at the end of the names array. +- EXPECT_TRUE( +- nameTree.AddValueAndName(pdfium::MakeRetain(999), L"9.txt")); ++ EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain(999), ++ L"9.txt")); + + // Check that the names array has the expected key-value pairs. +- CheckNameKeyValue(pNames, 0, "1.txt", 111); +- CheckNameKeyValue(pNames, 1, "2.txt", 222); +- CheckNameKeyValue(pNames, 2, "5.txt", 555); +- CheckNameKeyValue(pNames, 3, "7.txt", 777); +- CheckNameKeyValue(pNames, 4, "9.txt", 999); ++ CheckNameKeyValue(pNames.Get(), 0, "1.txt", 111); ++ CheckNameKeyValue(pNames.Get(), 1, "2.txt", 111); ++ CheckNameKeyValue(pNames.Get(), 2, "5.txt", 555); ++ CheckNameKeyValue(pNames.Get(), 3, "9.txt", 999); + } + + TEST(cpdf_nametree, AddIntoKids) { +- // Set up a name tree with five nodes of three levels. + auto pRootDict = pdfium::MakeRetain(); + FillNameTreeDict(pRootDict.Get()); +- CPDF_NameTree nameTree(pRootDict.Get()); ++ std::unique_ptr name_tree = ++ CPDF_NameTree::CreateForTesting(pRootDict.Get()); + + // Check that adding an existing name would fail. +- EXPECT_FALSE( +- nameTree.AddValueAndName(pdfium::MakeRetain(444), L"9.txt")); ++ EXPECT_FALSE(name_tree->AddValueAndName(pdfium::MakeRetain(444), ++ L"9.txt")); + + // Add a name within the limits of a leaf node. +- EXPECT_TRUE( +- nameTree.AddValueAndName(pdfium::MakeRetain(444), L"4.txt")); +- ASSERT_TRUE(nameTree.LookupValue(L"4.txt")); +- EXPECT_EQ(444, nameTree.LookupValue(L"4.txt")->GetInteger()); ++ EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain(444), ++ L"4.txt")); ++ ASSERT_TRUE(name_tree->LookupValue(L"4.txt")); ++ EXPECT_EQ(444, name_tree->LookupValue(L"4.txt")->GetInteger()); + + // Add a name that requires changing the limits of two bottom levels. +- EXPECT_TRUE( +- nameTree.AddValueAndName(pdfium::MakeRetain(666), L"6.txt")); +- ASSERT_TRUE(nameTree.LookupValue(L"6.txt")); +- EXPECT_EQ(666, nameTree.LookupValue(L"6.txt")->GetInteger()); ++ EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain(666), ++ L"6.txt")); ++ ASSERT_TRUE(name_tree->LookupValue(L"6.txt")); ++ EXPECT_EQ(666, name_tree->LookupValue(L"6.txt")->GetInteger()); + + // Add a name that requires changing the limits of two top levels. +- EXPECT_TRUE( +- nameTree.AddValueAndName(pdfium::MakeRetain(99), L"99.txt")); +- ASSERT_TRUE(nameTree.LookupValue(L"99.txt")); +- EXPECT_EQ(99, nameTree.LookupValue(L"99.txt")->GetInteger()); ++ EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain(99), ++ L"99.txt")); ++ ASSERT_TRUE(name_tree->LookupValue(L"99.txt")); ++ EXPECT_EQ(99, name_tree->LookupValue(L"99.txt")->GetInteger()); + + // Add a name that requires changing the lower limit of all levels. +- EXPECT_TRUE( +- nameTree.AddValueAndName(pdfium::MakeRetain(-5), L"0.txt")); +- ASSERT_TRUE(nameTree.LookupValue(L"0.txt")); +- EXPECT_EQ(-5, nameTree.LookupValue(L"0.txt")->GetInteger()); ++ EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain(-5), ++ L"0.txt")); ++ ASSERT_TRUE(name_tree->LookupValue(L"0.txt")); ++ EXPECT_EQ(-5, name_tree->LookupValue(L"0.txt")->GetInteger()); + + // Check that the node on the first level has the expected limits. +- CPDF_Dictionary* pKid1 = +- nameTree.GetRootForTest()->GetArrayFor("Kids")->GetDictAt(0); ++ RetainPtr pKid1 = ++ name_tree->GetRootForTesting()->GetArrayFor("Kids")->GetDictAt(0); + ASSERT_TRUE(pKid1); +- CheckLimitsArray(pKid1, "0.txt", "99.txt"); ++ CheckLimitsArray(pKid1.Get(), "0.txt", "99.txt"); + + // Check that the nodes on the second level has the expected limits and names. +- CPDF_Dictionary* pKid2 = pKid1->GetArrayFor("Kids")->GetDictAt(0); +- ASSERT_TRUE(pKid2); +- CheckLimitsArray(pKid2, "0.txt", "6.txt"); +- +- CPDF_Dictionary* pKid3 = pKid1->GetArrayFor("Kids")->GetDictAt(1); +- ASSERT_TRUE(pKid3); +- CheckLimitsArray(pKid3, "9.txt", "99.txt"); +- CPDF_Array* pNames = pKid3->GetArrayFor("Names"); +- ASSERT_TRUE(pNames); +- CheckNameKeyValue(pNames, 0, "9.txt", 999); +- CheckNameKeyValue(pNames, 1, "99.txt", 99); ++ RetainPtr pGrandKid2 = ++ pKid1->GetArrayFor("Kids")->GetDictAt(0); ++ ASSERT_TRUE(pGrandKid2); ++ CheckLimitsArray(pGrandKid2.Get(), "0.txt", "6.txt"); ++ ++ RetainPtr pGrandKid3 = ++ pKid1->GetArrayFor("Kids")->GetDictAt(1); ++ ASSERT_TRUE(pGrandKid3); ++ CheckLimitsArray(pGrandKid3.Get(), "9.txt", "99.txt"); ++ RetainPtr pNames = pGrandKid3->GetArrayFor("Names"); ++ CheckNameKeyValue(pNames.Get(), 0, "9.txt", 999); ++ CheckNameKeyValue(pNames.Get(), 1, "99.txt", 99); + + // Check that the nodes on the third level has the expected limits and names. +- CPDF_Dictionary* pKid4 = pKid2->GetArrayFor("Kids")->GetDictAt(0); +- ASSERT_TRUE(pKid4); +- CheckLimitsArray(pKid4, "0.txt", "2.txt"); +- pNames = pKid4->GetArrayFor("Names"); +- ASSERT_TRUE(pNames); +- CheckNameKeyValue(pNames, 0, "0.txt", -5); +- CheckNameKeyValue(pNames, 1, "1.txt", 111); +- CheckNameKeyValue(pNames, 2, "2.txt", 222); +- +- CPDF_Dictionary* pKid5 = pKid2->GetArrayFor("Kids")->GetDictAt(1); +- ASSERT_TRUE(pKid5); +- CheckLimitsArray(pKid5, "3.txt", "6.txt"); +- pNames = pKid5->GetArrayFor("Names"); +- ASSERT_TRUE(pNames); +- CheckNameKeyValue(pNames, 0, "3.txt", 333); +- CheckNameKeyValue(pNames, 1, "4.txt", 444); +- CheckNameKeyValue(pNames, 2, "5.txt", 555); +- CheckNameKeyValue(pNames, 3, "6.txt", 666); ++ RetainPtr pGreatGrandKid4 = ++ pGrandKid2->GetArrayFor("Kids")->GetDictAt(0); ++ ASSERT_TRUE(pGreatGrandKid4); ++ CheckLimitsArray(pGreatGrandKid4.Get(), "0.txt", "2.txt"); ++ pNames = pGreatGrandKid4->GetArrayFor("Names"); ++ CheckNameKeyValue(pNames.Get(), 0, "0.txt", -5); ++ CheckNameKeyValue(pNames.Get(), 1, "1.txt", 111); ++ CheckNameKeyValue(pNames.Get(), 2, "2.txt", 222); ++ ++ RetainPtr pGreatGrandKid5 = ++ pGrandKid2->GetArrayFor("Kids")->GetDictAt(1); ++ ASSERT_TRUE(pGreatGrandKid5); ++ CheckLimitsArray(pGreatGrandKid5.Get(), "3.txt", "6.txt"); ++ pNames = pGreatGrandKid5->GetArrayFor("Names"); ++ CheckNameKeyValue(pNames.Get(), 0, "3.txt", 333); ++ CheckNameKeyValue(pNames.Get(), 1, "4.txt", 444); ++ CheckNameKeyValue(pNames.Get(), 2, "5.txt", 555); ++ CheckNameKeyValue(pNames.Get(), 3, "6.txt", 666); + } + + TEST(cpdf_nametree, DeleteFromKids) { +- // Set up a name tree with five nodes of three levels. + auto pRootDict = pdfium::MakeRetain(); + FillNameTreeDict(pRootDict.Get()); +- CPDF_NameTree nameTree(pRootDict.Get()); ++ std::unique_ptr name_tree = ++ CPDF_NameTree::CreateForTesting(pRootDict.Get()); + + // Retrieve the kid dictionaries. +- CPDF_Dictionary* pKid1 = +- nameTree.GetRootForTest()->GetArrayFor("Kids")->GetDictAt(0); ++ RetainPtr pKid1 = ++ name_tree->GetRootForTesting()->GetArrayFor("Kids")->GetDictAt(0); + ASSERT_TRUE(pKid1); +- CPDF_Dictionary* pKid2 = pKid1->GetArrayFor("Kids")->GetDictAt(0); +- ASSERT_TRUE(pKid2); +- CPDF_Dictionary* pKid3 = pKid1->GetArrayFor("Kids")->GetDictAt(1); +- ASSERT_TRUE(pKid3); +- CPDF_Dictionary* pKid4 = pKid2->GetArrayFor("Kids")->GetDictAt(0); +- ASSERT_TRUE(pKid4); +- CPDF_Dictionary* pKid5 = pKid2->GetArrayFor("Kids")->GetDictAt(1); +- ASSERT_TRUE(pKid5); ++ RetainPtr pGrandKid2 = ++ pKid1->GetArrayFor("Kids")->GetDictAt(0); ++ ASSERT_TRUE(pGrandKid2); ++ RetainPtr pGrandKid3 = ++ pKid1->GetArrayFor("Kids")->GetDictAt(1); ++ ASSERT_TRUE(pGrandKid3); ++ RetainPtr pGreatGrandKid4 = ++ pGrandKid2->GetArrayFor("Kids")->GetDictAt(0); ++ ASSERT_TRUE(pGreatGrandKid4); ++ RetainPtr pGreatGrandKid5 = ++ pGrandKid2->GetArrayFor("Kids")->GetDictAt(1); ++ ASSERT_TRUE(pGreatGrandKid5); + + // Check that deleting an out-of-bound index would fail. +- EXPECT_FALSE(nameTree.DeleteValueAndName(5)); ++ EXPECT_FALSE(name_tree->DeleteValueAndName(5)); + + // Delete the name "9.txt", and check that its node gets deleted and its + // parent node's limits get updated. + WideString csName; +- ASSERT_TRUE(nameTree.LookupValue(L"9.txt")); +- EXPECT_EQ(999, nameTree.LookupValue(L"9.txt")->GetInteger()); +- EXPECT_TRUE(nameTree.LookupValueAndName(4, &csName)); ++ ASSERT_TRUE(name_tree->LookupValue(L"9.txt")); ++ EXPECT_EQ(999, name_tree->LookupValue(L"9.txt")->GetInteger()); ++ EXPECT_TRUE(name_tree->LookupValueAndName(4, &csName)); + EXPECT_STREQ(L"9.txt", csName.c_str()); + EXPECT_EQ(2u, pKid1->GetArrayFor("Kids")->size()); +- EXPECT_TRUE(nameTree.DeleteValueAndName(4)); ++ EXPECT_TRUE(name_tree->DeleteValueAndName(4)); + EXPECT_EQ(1u, pKid1->GetArrayFor("Kids")->size()); +- CheckLimitsArray(pKid1, "1.txt", "5.txt"); ++ CheckLimitsArray(pKid1.Get(), "1.txt", "5.txt"); + + // Delete the name "2.txt", and check that its node does not get deleted, its + // node's limits get updated, and no other limits get updated. +- ASSERT_TRUE(nameTree.LookupValue(L"2.txt")); +- EXPECT_EQ(222, nameTree.LookupValue(L"2.txt")->GetInteger()); +- EXPECT_TRUE(nameTree.LookupValueAndName(1, &csName)); ++ ASSERT_TRUE(name_tree->LookupValue(L"2.txt")); ++ EXPECT_EQ(222, name_tree->LookupValue(L"2.txt")->GetInteger()); ++ EXPECT_TRUE(name_tree->LookupValueAndName(1, &csName)); + EXPECT_STREQ(L"2.txt", csName.c_str()); +- EXPECT_EQ(4u, pKid4->GetArrayFor("Names")->size()); +- EXPECT_TRUE(nameTree.DeleteValueAndName(1)); +- EXPECT_EQ(2u, pKid4->GetArrayFor("Names")->size()); +- CheckLimitsArray(pKid4, "1.txt", "1.txt"); +- CheckLimitsArray(pKid2, "1.txt", "5.txt"); +- CheckLimitsArray(pKid1, "1.txt", "5.txt"); ++ EXPECT_EQ(4u, pGreatGrandKid4->GetArrayFor("Names")->size()); ++ EXPECT_TRUE(name_tree->DeleteValueAndName(1)); ++ EXPECT_EQ(2u, pGreatGrandKid4->GetArrayFor("Names")->size()); ++ CheckLimitsArray(pGreatGrandKid4.Get(), "1.txt", "1.txt"); ++ CheckLimitsArray(pGrandKid2.Get(), "1.txt", "5.txt"); ++ CheckLimitsArray(pKid1.Get(), "1.txt", "5.txt"); + + // Delete the name "1.txt", and check that its node gets deleted, and its + // parent's and gradparent's limits get updated. +- ASSERT_TRUE(nameTree.LookupValue(L"1.txt")); +- EXPECT_EQ(111, nameTree.LookupValue(L"1.txt")->GetInteger()); +- EXPECT_TRUE(nameTree.LookupValueAndName(0, &csName)); ++ ASSERT_TRUE(name_tree->LookupValue(L"1.txt")); ++ EXPECT_EQ(111, name_tree->LookupValue(L"1.txt")->GetInteger()); ++ EXPECT_TRUE(name_tree->LookupValueAndName(0, &csName)); + EXPECT_STREQ(L"1.txt", csName.c_str()); +- EXPECT_EQ(2u, pKid2->GetArrayFor("Kids")->size()); +- EXPECT_TRUE(nameTree.DeleteValueAndName(0)); +- EXPECT_EQ(1u, pKid2->GetArrayFor("Kids")->size()); +- CheckLimitsArray(pKid2, "3.txt", "5.txt"); +- CheckLimitsArray(pKid1, "3.txt", "5.txt"); ++ EXPECT_EQ(2u, pGrandKid2->GetArrayFor("Kids")->size()); ++ EXPECT_TRUE(name_tree->DeleteValueAndName(0)); ++ EXPECT_EQ(1u, pGrandKid2->GetArrayFor("Kids")->size()); ++ CheckLimitsArray(pGrandKid2.Get(), "3.txt", "5.txt"); ++ CheckLimitsArray(pKid1.Get(), "3.txt", "5.txt"); + + // Delete the name "3.txt", and check that its node does not get deleted, and + // its node's, its parent's, and its grandparent's limits get updated. +- ASSERT_TRUE(nameTree.LookupValue(L"3.txt")); +- EXPECT_EQ(333, nameTree.LookupValue(L"3.txt")->GetInteger()); +- EXPECT_TRUE(nameTree.LookupValueAndName(0, &csName)); ++ ASSERT_TRUE(name_tree->LookupValue(L"3.txt")); ++ EXPECT_EQ(333, name_tree->LookupValue(L"3.txt")->GetInteger()); ++ EXPECT_TRUE(name_tree->LookupValueAndName(0, &csName)); + EXPECT_STREQ(L"3.txt", csName.c_str()); +- EXPECT_EQ(4u, pKid5->GetArrayFor("Names")->size()); +- EXPECT_TRUE(nameTree.DeleteValueAndName(0)); +- EXPECT_EQ(2u, pKid5->GetArrayFor("Names")->size()); +- CheckLimitsArray(pKid5, "5.txt", "5.txt"); +- CheckLimitsArray(pKid2, "5.txt", "5.txt"); +- CheckLimitsArray(pKid1, "5.txt", "5.txt"); ++ EXPECT_EQ(4u, pGreatGrandKid5->GetArrayFor("Names")->size()); ++ EXPECT_TRUE(name_tree->DeleteValueAndName(0)); ++ EXPECT_EQ(2u, pGreatGrandKid5->GetArrayFor("Names")->size()); ++ CheckLimitsArray(pGreatGrandKid5.Get(), "5.txt", "5.txt"); ++ CheckLimitsArray(pGrandKid2.Get(), "5.txt", "5.txt"); ++ CheckLimitsArray(pKid1.Get(), "5.txt", "5.txt"); + + // Delete the name "5.txt", and check that all nodes in the tree get deleted + // since they are now all empty. +- ASSERT_TRUE(nameTree.LookupValue(L"5.txt")); +- EXPECT_EQ(555, nameTree.LookupValue(L"5.txt")->GetInteger()); +- EXPECT_TRUE(nameTree.LookupValueAndName(0, &csName)); ++ ASSERT_TRUE(name_tree->LookupValue(L"5.txt")); ++ EXPECT_EQ(555, name_tree->LookupValue(L"5.txt")->GetInteger()); ++ EXPECT_TRUE(name_tree->LookupValueAndName(0, &csName)); + EXPECT_STREQ(L"5.txt", csName.c_str()); +- EXPECT_EQ(1u, nameTree.GetRootForTest()->GetArrayFor("Kids")->size()); +- EXPECT_TRUE(nameTree.DeleteValueAndName(0)); +- EXPECT_EQ(0u, nameTree.GetRootForTest()->GetArrayFor("Kids")->size()); ++ EXPECT_EQ(1u, name_tree->GetRootForTesting()->GetArrayFor("Kids")->size()); ++ EXPECT_TRUE(name_tree->DeleteValueAndName(0)); ++ EXPECT_EQ(0u, name_tree->GetRootForTesting()->GetArrayFor("Kids")->size()); + + // Check that the tree is now empty. +- EXPECT_EQ(0u, nameTree.GetCount()); +- EXPECT_FALSE(nameTree.LookupValueAndName(0, &csName)); +- EXPECT_FALSE(nameTree.DeleteValueAndName(0)); ++ EXPECT_EQ(0u, name_tree->GetCount()); ++ EXPECT_FALSE(name_tree->LookupValueAndName(0, &csName)); ++ EXPECT_FALSE(name_tree->DeleteValueAndName(0)); + } +diff --git a/core/fpdfdoc/cpdf_numbertree.cpp b/core/fpdfdoc/cpdf_numbertree.cpp +index 46257c872..f381634bc 100644 +--- a/core/fpdfdoc/cpdf_numbertree.cpp ++++ b/core/fpdfdoc/cpdf_numbertree.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,18 +6,21 @@ + + #include "core/fpdfdoc/cpdf_numbertree.h" + ++#include ++ + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + + namespace { + +-const CPDF_Object* SearchNumberNode(const CPDF_Dictionary* pNode, int num) { +- const CPDF_Array* pLimits = pNode->GetArrayFor("Limits"); ++RetainPtr SearchNumberNode(const CPDF_Dictionary* pNode, ++ int num) { ++ RetainPtr pLimits = pNode->GetArrayFor("Limits"); + if (pLimits && + (num < pLimits->GetIntegerAt(0) || num > pLimits->GetIntegerAt(1))) { + return nullptr; + } +- const CPDF_Array* pNumbers = pNode->GetArrayFor("Nums"); ++ RetainPtr pNumbers = pNode->GetArrayFor("Nums"); + if (pNumbers) { + for (size_t i = 0; i < pNumbers->size() / 2; i++) { + int index = pNumbers->GetIntegerAt(i * 2); +@@ -29,16 +32,16 @@ const CPDF_Object* SearchNumberNode(const CPDF_Dictionary* pNode, int num) { + return nullptr; + } + +- const CPDF_Array* pKids = pNode->GetArrayFor("Kids"); ++ RetainPtr pKids = pNode->GetArrayFor("Kids"); + if (!pKids) + return nullptr; + + for (size_t i = 0; i < pKids->size(); i++) { +- const CPDF_Dictionary* pKid = pKids->GetDictAt(i); ++ RetainPtr pKid = pKids->GetDictAt(i); + if (!pKid) + continue; + +- const CPDF_Object* pFound = SearchNumberNode(pKid, num); ++ RetainPtr pFound = SearchNumberNode(pKid.Get(), num); + if (pFound) + return pFound; + } +@@ -47,11 +50,11 @@ const CPDF_Object* SearchNumberNode(const CPDF_Dictionary* pNode, int num) { + + } // namespace + +-CPDF_NumberTree::CPDF_NumberTree(const CPDF_Dictionary* pRoot) +- : m_pRoot(pRoot) {} ++CPDF_NumberTree::CPDF_NumberTree(RetainPtr pRoot) ++ : m_pRoot(std::move(pRoot)) {} + +-CPDF_NumberTree::~CPDF_NumberTree() {} ++CPDF_NumberTree::~CPDF_NumberTree() = default; + +-const CPDF_Object* CPDF_NumberTree::LookupValue(int num) const { ++RetainPtr CPDF_NumberTree::LookupValue(int num) const { + return SearchNumberNode(m_pRoot.Get(), num); + } +diff --git a/core/fpdfdoc/cpdf_numbertree.h b/core/fpdfdoc/cpdf_numbertree.h +index 2a3fa6e3b..1cd1ef894 100644 +--- a/core/fpdfdoc/cpdf_numbertree.h ++++ b/core/fpdfdoc/cpdf_numbertree.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,10 +14,10 @@ class CPDF_Object; + + class CPDF_NumberTree { + public: +- explicit CPDF_NumberTree(const CPDF_Dictionary* pRoot); ++ explicit CPDF_NumberTree(RetainPtr pRoot); + ~CPDF_NumberTree(); + +- const CPDF_Object* LookupValue(int num) const; ++ RetainPtr LookupValue(int num) const; + + protected: + RetainPtr const m_pRoot; +diff --git a/core/fpdfdoc/cpdf_pagelabel.cpp b/core/fpdfdoc/cpdf_pagelabel.cpp +index 8985def43..26119364d 100644 +--- a/core/fpdfdoc/cpdf_pagelabel.cpp ++++ b/core/fpdfdoc/cpdf_pagelabel.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,8 @@ + + #include "core/fpdfdoc/cpdf_pagelabel.h" + ++#include ++ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" +@@ -15,8 +17,9 @@ namespace { + + WideString MakeRoman(int num) { + const int kArabic[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; +- const WideString kRoman[] = {L"m", L"cm", L"d", L"cd", L"c", L"xc", L"l", +- L"xl", L"x", L"ix", L"v", L"iv", L"i"}; ++ const WideStringView kRoman[] = {L"m", L"cm", L"d", L"cd", L"c", ++ L"xc", L"l", L"xl", L"x", L"ix", ++ L"v", L"iv", L"i"}; + const int kMaxNum = 1000000; + + num %= kMaxNum; +@@ -53,7 +56,7 @@ WideString GetLabelNumPortion(int num, const ByteString& bsStyle) { + if (bsStyle.IsEmpty()) + return WideString(); + if (bsStyle == "D") +- return WideString::Format(L"%d", num); ++ return WideString::FormatInteger(num); + if (bsStyle == "R") { + WideString wsNumPortion = MakeRoman(num); + wsNumPortion.MakeUpper(); +@@ -76,25 +79,25 @@ WideString GetLabelNumPortion(int num, const ByteString& bsStyle) { + CPDF_PageLabel::CPDF_PageLabel(CPDF_Document* pDocument) + : m_pDocument(pDocument) {} + +-CPDF_PageLabel::~CPDF_PageLabel() {} ++CPDF_PageLabel::~CPDF_PageLabel() = default; + +-Optional CPDF_PageLabel::GetLabel(int nPage) const { ++absl::optional CPDF_PageLabel::GetLabel(int nPage) const { + if (!m_pDocument) +- return {}; ++ return absl::nullopt; + + if (nPage < 0 || nPage >= m_pDocument->GetPageCount()) +- return {}; ++ return absl::nullopt; + + const CPDF_Dictionary* pPDFRoot = m_pDocument->GetRoot(); + if (!pPDFRoot) +- return {}; ++ return absl::nullopt; + +- const CPDF_Dictionary* pLabels = pPDFRoot->GetDictFor("PageLabels"); ++ RetainPtr pLabels = pPDFRoot->GetDictFor("PageLabels"); + if (!pLabels) +- return {}; ++ return absl::nullopt; + +- CPDF_NumberTree numberTree(pLabels); +- const CPDF_Object* pValue = nullptr; ++ CPDF_NumberTree numberTree(std::move(pLabels)); ++ RetainPtr pValue; + int n = nPage; + while (n >= 0) { + pValue = numberTree.LookupValue(n); +@@ -103,20 +106,19 @@ Optional CPDF_PageLabel::GetLabel(int nPage) const { + n--; + } + +- WideString label; + if (pValue) { + pValue = pValue->GetDirect(); + if (const CPDF_Dictionary* pLabel = pValue->AsDictionary()) { ++ WideString label; + if (pLabel->KeyExist("P")) + label += pLabel->GetUnicodeTextFor("P"); + +- ByteString bsNumberingStyle = pLabel->GetStringFor("S", ByteString()); ++ ByteString bsNumberingStyle = pLabel->GetByteStringFor("S", ByteString()); + int nLabelNum = nPage - n + pLabel->GetIntegerFor("St", 1); + WideString wsNumPortion = GetLabelNumPortion(nLabelNum, bsNumberingStyle); + label += wsNumPortion; +- return {label}; ++ return label; + } + } +- label = WideString::Format(L"%d", nPage + 1); +- return {label}; ++ return WideString::FormatInteger(nPage + 1); + } +diff --git a/core/fpdfdoc/cpdf_pagelabel.h b/core/fpdfdoc/cpdf_pagelabel.h +index 473485dc4..05ec2371d 100644 +--- a/core/fpdfdoc/cpdf_pagelabel.h ++++ b/core/fpdfdoc/cpdf_pagelabel.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,8 +7,9 @@ + #ifndef CORE_FPDFDOC_CPDF_PAGELABEL_H_ + #define CORE_FPDFDOC_CPDF_PAGELABEL_H_ + +-#include "core/fxcrt/fx_string.h" +-#include "third_party/base/optional.h" ++#include "core/fxcrt/unowned_ptr.h" ++#include "core/fxcrt/widestring.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CPDF_Document; + +@@ -17,7 +18,7 @@ class CPDF_PageLabel { + explicit CPDF_PageLabel(CPDF_Document* pDocument); + ~CPDF_PageLabel(); + +- Optional GetLabel(int nPage) const; ++ absl::optional GetLabel(int nPage) const; + + private: + UnownedPtr const m_pDocument; +diff --git a/core/fpdfdoc/cpdf_structelement.cpp b/core/fpdfdoc/cpdf_structelement.cpp +index 40477b460..b28774dbd 100644 +--- a/core/fpdfdoc/cpdf_structelement.cpp ++++ b/core/fpdfdoc/cpdf_structelement.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,46 +16,66 @@ + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfdoc/cpdf_structtree.h" ++#include "third_party/base/check.h" + +-namespace { ++CPDF_StructElement::Kid::Kid() = default; + +-ByteString GetStructElementType(CPDF_StructTree* pTree, +- const CPDF_Dictionary* pDict) { +- ByteString type = pDict->GetStringFor("S"); +- if (pTree->GetRoleMap()) { +- ByteString mapped = pTree->GetRoleMap()->GetStringFor(type); +- if (!mapped.IsEmpty()) +- type = std::move(mapped); ++CPDF_StructElement::Kid::Kid(const Kid& that) = default; ++ ++CPDF_StructElement::Kid::~Kid() = default; ++ ++CPDF_StructElement::CPDF_StructElement(const CPDF_StructTree* pTree, ++ RetainPtr pDict) ++ : m_pTree(pTree), ++ m_pDict(std::move(pDict)), ++ m_Type(m_pTree->GetRoleMapNameFor(m_pDict->GetNameFor("S"))) { ++ LoadKids(m_pDict); ++} ++ ++CPDF_StructElement::~CPDF_StructElement() { ++ for (auto& kid : m_Kids) { ++ if (kid.m_Type == Kid::kElement && kid.m_pElement) { ++ kid.m_pElement->SetParent(nullptr); ++ } + } +- return type; + } + +-} // namespace ++ByteString CPDF_StructElement::GetObjType() const { ++ return m_pDict->GetByteStringFor("Type"); ++} + +-CPDF_StructKid::CPDF_StructKid() = default; ++WideString CPDF_StructElement::GetAltText() const { ++ return m_pDict->GetUnicodeTextFor("Alt"); ++} + +-CPDF_StructKid::CPDF_StructKid(const CPDF_StructKid& that) = default; ++WideString CPDF_StructElement::GetActualText() const { ++ return m_pDict->GetUnicodeTextFor("ActualText"); ++} + +-CPDF_StructKid::~CPDF_StructKid() = default; ++WideString CPDF_StructElement::GetTitle() const { ++ return m_pDict->GetUnicodeTextFor("T"); ++} + +-CPDF_StructElement::CPDF_StructElement(CPDF_StructTree* pTree, +- CPDF_StructElement* pParent, +- const CPDF_Dictionary* pDict) +- : m_pTree(pTree), +- m_pParent(pParent), +- m_pDict(pDict), +- m_Type(GetStructElementType(m_pTree.Get(), m_pDict.Get())) { +- LoadKids(m_pDict.Get()); ++absl::optional CPDF_StructElement::GetID() const { ++ RetainPtr obj = m_pDict->GetObjectFor("ID"); ++ if (!obj || !obj->IsString()) ++ return absl::nullopt; ++ return obj->GetUnicodeText(); + } + +-CPDF_StructElement::~CPDF_StructElement() = default; ++absl::optional CPDF_StructElement::GetLang() const { ++ RetainPtr obj = m_pDict->GetObjectFor("Lang"); ++ if (!obj || !obj->IsString()) ++ return absl::nullopt; ++ return obj->GetUnicodeText(); ++} + +-WideString CPDF_StructElement::GetAltText() const { +- return GetDict()->GetUnicodeTextFor("Alt"); ++RetainPtr CPDF_StructElement::GetA() const { ++ return m_pDict->GetObjectFor("A"); + } + +-WideString CPDF_StructElement::GetTitle() const { +- return GetDict()->GetUnicodeTextFor("T"); ++RetainPtr CPDF_StructElement::GetK() const { ++ return m_pDict->GetObjectFor("K"); + } + + size_t CPDF_StructElement::CountKids() const { +@@ -63,47 +83,54 @@ size_t CPDF_StructElement::CountKids() const { + } + + CPDF_StructElement* CPDF_StructElement::GetKidIfElement(size_t index) const { +- return m_Kids[index].m_Type == CPDF_StructKid::kElement +- ? m_Kids[index].m_pElement.Get() +- : nullptr; ++ return m_Kids[index].m_Type == Kid::kElement ? m_Kids[index].m_pElement.Get() ++ : nullptr; + } + +-void CPDF_StructElement::LoadKids(const CPDF_Dictionary* pDict) { +- const CPDF_Object* pObj = pDict->GetObjectFor("Pg"); +- uint32_t PageObjNum = 0; +- if (const CPDF_Reference* pRef = ToReference(pObj)) +- PageObjNum = pRef->GetRefObjNum(); ++bool CPDF_StructElement::UpdateKidIfElement(const CPDF_Dictionary* pDict, ++ CPDF_StructElement* pElement) { ++ bool bSave = false; ++ for (auto& kid : m_Kids) { ++ if (kid.m_Type == Kid::kElement && kid.m_pDict == pDict) { ++ kid.m_pElement.Reset(pElement); ++ bSave = true; ++ } ++ } ++ return bSave; ++} + +- const CPDF_Object* pKids = pDict->GetDirectObjectFor("K"); ++void CPDF_StructElement::LoadKids(RetainPtr pDict) { ++ RetainPtr pObj = pDict->GetObjectFor("Pg"); ++ const CPDF_Reference* pRef = ToReference(pObj.Get()); ++ const uint32_t PageObjNum = pRef ? pRef->GetRefObjNum() : 0; ++ RetainPtr pKids = pDict->GetDirectObjectFor("K"); + if (!pKids) + return; + +- m_Kids.clear(); ++ DCHECK(m_Kids.empty()); + if (const CPDF_Array* pArray = pKids->AsArray()) { + m_Kids.resize(pArray->size()); +- for (uint32_t i = 0; i < pArray->size(); i++) { +- const CPDF_Object* pKid = pArray->GetDirectObjectAt(i); +- LoadKid(PageObjNum, pKid, &m_Kids[i]); ++ for (size_t i = 0; i < pArray->size(); ++i) { ++ LoadKid(PageObjNum, pArray->GetDirectObjectAt(i), &m_Kids[i]); + } + return; + } + + m_Kids.resize(1); +- LoadKid(PageObjNum, pKids, &m_Kids[0]); ++ LoadKid(PageObjNum, std::move(pKids), &m_Kids[0]); + } + + void CPDF_StructElement::LoadKid(uint32_t PageObjNum, +- const CPDF_Object* pKidObj, +- CPDF_StructKid* pKid) { +- pKid->m_Type = CPDF_StructKid::kInvalid; ++ RetainPtr pKidObj, ++ Kid* pKid) { + if (!pKidObj) + return; + + if (pKidObj->IsNumber()) { +- if (m_pTree->GetPage()->GetObjNum() != PageObjNum) ++ if (m_pTree->GetPageObjNum() != PageObjNum) + return; + +- pKid->m_Type = CPDF_StructKid::kPageContent; ++ pKid->m_Type = Kid::kPageContent; + pKid->m_ContentId = pKidObj->GetInteger(); + pKid->m_PageObjNum = PageObjNum; + return; +@@ -112,18 +139,21 @@ void CPDF_StructElement::LoadKid(uint32_t PageObjNum, + const CPDF_Dictionary* pKidDict = pKidObj->AsDictionary(); + if (!pKidDict) + return; +- if (const CPDF_Reference* pRef = ToReference(pKidDict->GetObjectFor("Pg"))) +- PageObjNum = pRef->GetRefObjNum(); + +- ByteString type = pKidDict->GetStringFor("Type"); ++ if (RetainPtr pRef = ++ ToReference(pKidDict->GetObjectFor("Pg"))) { ++ PageObjNum = pRef->GetRefObjNum(); ++ } ++ ByteString type = pKidDict->GetNameFor("Type"); + if ((type == "MCR" || type == "OBJR") && +- m_pTree->GetPage()->GetObjNum() != PageObjNum) { ++ m_pTree->GetPageObjNum() != PageObjNum) { + return; + } + + if (type == "MCR") { +- pKid->m_Type = CPDF_StructKid::kStreamContent; +- const CPDF_Reference* pRef = ToReference(pKidDict->GetObjectFor("Stm")); ++ pKid->m_Type = Kid::kStreamContent; ++ RetainPtr pRef = ++ ToReference(pKidDict->GetObjectFor("Stm")); + pKid->m_RefObjNum = pRef ? pRef->GetRefObjNum() : 0; + pKid->m_PageObjNum = PageObjNum; + pKid->m_ContentId = pKidDict->GetIntegerFor("MCID"); +@@ -131,14 +161,14 @@ void CPDF_StructElement::LoadKid(uint32_t PageObjNum, + } + + if (type == "OBJR") { +- pKid->m_Type = CPDF_StructKid::kObject; +- const CPDF_Reference* pObj = ToReference(pKidDict->GetObjectFor("Obj")); ++ pKid->m_Type = Kid::kObject; ++ RetainPtr pObj = ++ ToReference(pKidDict->GetObjectFor("Obj")); + pKid->m_RefObjNum = pObj ? pObj->GetRefObjNum() : 0; + pKid->m_PageObjNum = PageObjNum; + return; + } + +- pKid->m_Type = CPDF_StructKid::kElement; ++ pKid->m_Type = Kid::kElement; + pKid->m_pDict.Reset(pKidDict); +- pKid->m_pElement = nullptr; + } +diff --git a/core/fpdfdoc/cpdf_structelement.h b/core/fpdfdoc/cpdf_structelement.h +index 54042a954..6dd8f8f00 100644 +--- a/core/fpdfdoc/cpdf_structelement.h ++++ b/core/fpdfdoc/cpdf_structelement.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,60 +12,66 @@ + #include "core/fxcrt/fx_string.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CPDF_Dictionary; + class CPDF_Object; +-class CPDF_StructElement; + class CPDF_StructTree; + +-class CPDF_StructKid { +- public: +- enum Type { kInvalid, kElement, kPageContent, kStreamContent, kObject }; +- +- CPDF_StructKid(); +- CPDF_StructKid(const CPDF_StructKid& that); +- ~CPDF_StructKid(); +- +- Type m_Type = kInvalid; +- uint32_t m_PageObjNum = 0; // For {PageContent, StreamContent, Object} types. +- uint32_t m_RefObjNum = 0; // For {StreamContent, Object} types. +- uint32_t m_ContentId = 0; // For {PageContent, StreamContent} types. +- RetainPtr m_pElement; // For Element. +- RetainPtr m_pDict; // For Element. +-}; +- + class CPDF_StructElement final : public Retainable { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + ByteString GetType() const { return m_Type; } ++ ByteString GetObjType() const; + WideString GetAltText() const; ++ WideString GetActualText() const; + WideString GetTitle() const; +- +- // Never returns nullptr. +- const CPDF_Dictionary* GetDict() const { return m_pDict.Get(); } ++ absl::optional GetID() const; ++ absl::optional GetLang() const; ++ RetainPtr GetA() const; ++ RetainPtr GetK() const; + + size_t CountKids() const; + CPDF_StructElement* GetKidIfElement(size_t index) const; +- std::vector* GetKids() { return &m_Kids; } ++ bool UpdateKidIfElement(const CPDF_Dictionary* pDict, ++ CPDF_StructElement* pElement); ++ ++ CPDF_StructElement* GetParent() const { return m_pParentElement; } ++ void SetParent(CPDF_StructElement* pParentElement) { ++ m_pParentElement = pParentElement; ++ } + + private: +- CPDF_StructElement(CPDF_StructTree* pTree, +- CPDF_StructElement* pParent, +- const CPDF_Dictionary* pDict); ++ struct Kid { ++ enum Type { kInvalid, kElement, kPageContent, kStreamContent, kObject }; ++ ++ Kid(); ++ Kid(const Kid& that); ++ ~Kid(); ++ ++ Type m_Type = kInvalid; ++ uint32_t m_PageObjNum = 0; // For {PageContent, StreamContent, Object}. ++ uint32_t m_RefObjNum = 0; // For {StreamContent, Object} types. ++ uint32_t m_ContentId = 0; // For {PageContent, StreamContent} types. ++ RetainPtr m_pElement; // For Element type. ++ RetainPtr m_pDict; // For Element type. ++ }; ++ ++ CPDF_StructElement(const CPDF_StructTree* pTree, ++ RetainPtr pDict); + ~CPDF_StructElement() override; + +- void LoadKids(const CPDF_Dictionary* pDict); ++ void LoadKids(RetainPtr pDict); + void LoadKid(uint32_t PageObjNum, +- const CPDF_Object* pKidObj, +- CPDF_StructKid* pKid); ++ RetainPtr pKidObj, ++ Kid* pKid); + +- UnownedPtr const m_pTree; +- UnownedPtr const m_pParent; ++ UnownedPtr const m_pTree; + RetainPtr const m_pDict; ++ UnownedPtr m_pParentElement; + const ByteString m_Type; +- std::vector m_Kids; ++ std::vector m_Kids; + }; + + #endif // CORE_FPDFDOC_CPDF_STRUCTELEMENT_H_ +diff --git a/core/fpdfdoc/cpdf_structtree.cpp b/core/fpdfdoc/cpdf_structtree.cpp +index 8fd43e2f6..67ac9485f 100644 +--- a/core/fpdfdoc/cpdf_structtree.cpp ++++ b/core/fpdfdoc/cpdf_structtree.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,8 @@ + + #include "core/fpdfdoc/cpdf_structtree.h" + ++#include ++ + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" +@@ -13,13 +15,13 @@ + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfdoc/cpdf_numbertree.h" + #include "core/fpdfdoc/cpdf_structelement.h" +-#include "third_party/base/ptr_util.h" ++#include "core/fxcrt/stl_util.h" + + namespace { + + bool IsTagged(const CPDF_Document* pDoc) { +- const CPDF_Dictionary* pCatalog = pDoc->GetRoot(); +- const CPDF_Dictionary* pMarkInfo = pCatalog->GetDictFor("MarkInfo"); ++ RetainPtr pMarkInfo = ++ pDoc->GetRoot()->GetDictFor("MarkInfo"); + return pMarkInfo && pMarkInfo->GetIntegerFor("Marked"); + } + +@@ -28,12 +30,12 @@ bool IsTagged(const CPDF_Document* pDoc) { + // static + std::unique_ptr CPDF_StructTree::LoadPage( + const CPDF_Document* pDoc, +- const CPDF_Dictionary* pPageDict) { ++ RetainPtr pPageDict) { + if (!IsTagged(pDoc)) + return nullptr; + +- auto pTree = pdfium::MakeUnique(pDoc); +- pTree->LoadPageTree(pPageDict); ++ auto pTree = std::make_unique(pDoc); ++ pTree->LoadPageTree(std::move(pPageDict)); + return pTree; + } + +@@ -43,12 +45,21 @@ CPDF_StructTree::CPDF_StructTree(const CPDF_Document* pDoc) + + CPDF_StructTree::~CPDF_StructTree() = default; + +-void CPDF_StructTree::LoadPageTree(const CPDF_Dictionary* pPageDict) { +- m_pPage.Reset(pPageDict); ++ByteString CPDF_StructTree::GetRoleMapNameFor(const ByteString& type) const { ++ if (m_pRoleMap) { ++ ByteString mapped = m_pRoleMap->GetNameFor(type); ++ if (!mapped.IsEmpty()) ++ return mapped; ++ } ++ return type; ++} ++ ++void CPDF_StructTree::LoadPageTree(RetainPtr pPageDict) { ++ m_pPage = std::move(pPageDict); + if (!m_pTreeRoot) + return; + +- const CPDF_Object* pKids = m_pTreeRoot->GetDirectObjectFor("K"); ++ RetainPtr pKids = m_pTreeRoot->GetDirectObjectFor("K"); + if (!pKids) + return; + +@@ -56,34 +67,38 @@ void CPDF_StructTree::LoadPageTree(const CPDF_Dictionary* pPageDict) { + if (pKids->IsDictionary()) + dwKids = 1; + else if (const CPDF_Array* pArray = pKids->AsArray()) +- dwKids = pArray->size(); ++ dwKids = fxcrt::CollectionSize(*pArray); + else + return; + + m_Kids.clear(); + m_Kids.resize(dwKids); +- const CPDF_Dictionary* pParentTree = m_pTreeRoot->GetDictFor("ParentTree"); ++ ++ RetainPtr pParentTree = ++ m_pTreeRoot->GetDictFor("ParentTree"); + if (!pParentTree) + return; + +- CPDF_NumberTree parent_tree(pParentTree); +- int parents_id = pPageDict->GetIntegerFor("StructParents", -1); ++ CPDF_NumberTree parent_tree(std::move(pParentTree)); ++ int parents_id = m_pPage->GetIntegerFor("StructParents", -1); + if (parents_id < 0) + return; + +- const CPDF_Array* pParentArray = ToArray(parent_tree.LookupValue(parents_id)); ++ RetainPtr pParentArray = ++ ToArray(parent_tree.LookupValue(parents_id)); + if (!pParentArray) + return; + + StructElementMap element_map; + for (size_t i = 0; i < pParentArray->size(); i++) { +- if (const CPDF_Dictionary* pParent = pParentArray->GetDictAt(i)) +- AddPageNode(pParent, &element_map, 0); ++ RetainPtr pParent = pParentArray->GetDictAt(i); ++ if (pParent) ++ AddPageNode(std::move(pParent), &element_map, 0); + } + } + + RetainPtr CPDF_StructTree::AddPageNode( +- const CPDF_Dictionary* pDict, ++ RetainPtr pDict, + StructElementMap* map, + int nLevel) { + static constexpr int kStructTreeMaxRecursion = 32; +@@ -94,33 +109,33 @@ RetainPtr CPDF_StructTree::AddPageNode( + if (it != map->end()) + return it->second; + +- auto pElement = pdfium::MakeRetain(this, nullptr, pDict); +- (*map)[pDict] = pElement; +- const CPDF_Dictionary* pParent = pDict->GetDictFor("P"); +- if (!pParent || pParent->GetStringFor("Type") == "StructTreeRoot") { ++ RetainPtr key(pDict); ++ auto pElement = pdfium::MakeRetain(this, pDict); ++ (*map)[key] = pElement; ++ RetainPtr pParent = pDict->GetDictFor("P"); ++ if (!pParent || pParent->GetNameFor("Type") == "StructTreeRoot") { + if (!AddTopLevelNode(pDict, pElement)) +- map->erase(pDict); ++ map->erase(key); + return pElement; + } + + RetainPtr pParentElement = +- AddPageNode(pParent, map, nLevel + 1); +- bool bSave = false; +- for (CPDF_StructKid& kid : *pParentElement->GetKids()) { +- if (kid.m_Type == CPDF_StructKid::kElement && kid.m_pDict == pDict) { +- kid.m_pElement = pElement; +- bSave = true; +- } +- } +- if (!bSave) +- map->erase(pDict); ++ AddPageNode(std::move(pParent), map, nLevel + 1); ++ if (!pParentElement) ++ return pElement; ++ ++ if (!pParentElement->UpdateKidIfElement(pDict, pElement.Get())) ++ map->erase(key); ++ ++ pElement->SetParent(pParentElement.Get()); ++ + return pElement; + } + + bool CPDF_StructTree::AddTopLevelNode( + const CPDF_Dictionary* pDict, + const RetainPtr& pElement) { +- const CPDF_Object* pObj = m_pTreeRoot->GetDirectObjectFor("K"); ++ RetainPtr pObj = m_pTreeRoot->GetDirectObjectFor("K"); + if (!pObj) + return false; + +@@ -136,7 +151,8 @@ bool CPDF_StructTree::AddTopLevelNode( + + bool bSave = false; + for (size_t i = 0; i < pTopKids->size(); i++) { +- const CPDF_Reference* pKidRef = ToReference(pTopKids->GetObjectAt(i)); ++ RetainPtr pKidRef = ++ ToReference(pTopKids->GetObjectAt(i)); + if (pKidRef && pKidRef->GetRefObjNum() == pDict->GetObjNum()) { + m_Kids[i] = pElement; + bSave = true; +diff --git a/core/fpdfdoc/cpdf_structtree.h b/core/fpdfdoc/cpdf_structtree.h +index b0eafba96..8cca8b214 100644 +--- a/core/fpdfdoc/cpdf_structtree.h ++++ b/core/fpdfdoc/cpdf_structtree.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,13 +7,15 @@ + #ifndef CORE_FPDFDOC_CPDF_STRUCTTREE_H_ + #define CORE_FPDFDOC_CPDF_STRUCTTREE_H_ + ++#include + #include + #include + #include + ++#include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/retain_ptr.h" + +-class CPDF_Dictionary; + class CPDF_Document; + class CPDF_StructElement; + +@@ -21,25 +23,26 @@ class CPDF_StructTree { + public: + static std::unique_ptr LoadPage( + const CPDF_Document* pDoc, +- const CPDF_Dictionary* pPageDict); ++ RetainPtr pPageDict); + + explicit CPDF_StructTree(const CPDF_Document* pDoc); + ~CPDF_StructTree(); + + size_t CountTopElements() const { return m_Kids.size(); } + CPDF_StructElement* GetTopElement(size_t i) const { return m_Kids[i].Get(); } +- const CPDF_Dictionary* GetRoleMap() const { return m_pRoleMap.Get(); } +- const CPDF_Dictionary* GetPage() const { return m_pPage.Get(); } +- const CPDF_Dictionary* GetTreeRoot() const { return m_pTreeRoot.Get(); } ++ uint32_t GetPageObjNum() const { return m_pPage->GetObjNum(); } ++ ByteString GetRoleMapNameFor(const ByteString& type) const; + + private: +- using StructElementMap = +- std::map>; +- +- void LoadPageTree(const CPDF_Dictionary* pPageDict); +- RetainPtr AddPageNode(const CPDF_Dictionary* pDict, +- StructElementMap* map, +- int nLevel); ++ using StructElementMap = std::map, ++ RetainPtr, ++ std::less<>>; ++ ++ void LoadPageTree(RetainPtr pPageDict); ++ RetainPtr AddPageNode( ++ RetainPtr pDict, ++ StructElementMap* map, ++ int nLevel); + bool AddTopLevelNode(const CPDF_Dictionary* pDict, + const RetainPtr& pElement); + +diff --git a/core/fpdfdoc/cpdf_variabletext.cpp b/core/fpdfdoc/cpdf_variabletext.cpp +deleted file mode 100644 +index d7d2265e7..000000000 +--- a/core/fpdfdoc/cpdf_variabletext.cpp ++++ /dev/null +@@ -1,937 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fpdfdoc/cpdf_variabletext.h" +- +-#include +-#include +- +-#include "core/fpdfapi/font/cpdf_font.h" +-#include "core/fpdfdoc/cline.h" +-#include "core/fpdfdoc/cpvt_word.h" +-#include "core/fpdfdoc/cpvt_wordinfo.h" +-#include "core/fpdfdoc/csection.h" +-#include "core/fpdfdoc/ipvt_fontmap.h" +-#include "core/fxcrt/fx_codepage.h" +-#include "third_party/base/compiler_specific.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" +- +-namespace { +- +-const float kFontScale = 0.001f; +-const uint8_t kReturnLength = 1; +- +-const uint8_t gFontSizeSteps[] = {4, 6, 8, 9, 10, 12, 14, 18, 20, +- 25, 30, 35, 40, 45, 50, 55, 60, 70, +- 80, 90, 100, 110, 120, 130, 144}; +- +-} // namespace +- +-CPDF_VariableText::Provider::Provider(IPVT_FontMap* pFontMap) +- : m_pFontMap(pFontMap) { +- ASSERT(m_pFontMap); +-} +- +-CPDF_VariableText::Provider::~Provider() {} +- +-uint32_t CPDF_VariableText::Provider::GetCharWidth(int32_t nFontIndex, +- uint16_t word) { +- RetainPtr pPDFFont = m_pFontMap->GetPDFFont(nFontIndex); +- if (!pPDFFont) +- return 0; +- +- uint32_t charcode = pPDFFont->CharCodeFromUnicode(word); +- if (charcode == CPDF_Font::kInvalidCharCode) +- return 0; +- +- return pPDFFont->GetCharWidthF(charcode); +-} +- +-int32_t CPDF_VariableText::Provider::GetTypeAscent(int32_t nFontIndex) { +- RetainPtr pPDFFont = m_pFontMap->GetPDFFont(nFontIndex); +- return pPDFFont ? pPDFFont->GetTypeAscent() : 0; +-} +- +-int32_t CPDF_VariableText::Provider::GetTypeDescent(int32_t nFontIndex) { +- RetainPtr pPDFFont = m_pFontMap->GetPDFFont(nFontIndex); +- return pPDFFont ? pPDFFont->GetTypeDescent() : 0; +-} +- +-int32_t CPDF_VariableText::Provider::GetWordFontIndex(uint16_t word, +- int32_t charset, +- int32_t nFontIndex) { +- if (RetainPtr pDefFont = m_pFontMap->GetPDFFont(0)) { +- if (pDefFont->CharCodeFromUnicode(word) != CPDF_Font::kInvalidCharCode) +- return 0; +- } +- if (RetainPtr pSysFont = m_pFontMap->GetPDFFont(1)) { +- if (pSysFont->CharCodeFromUnicode(word) != CPDF_Font::kInvalidCharCode) +- return 1; +- } +- return -1; +-} +- +-bool CPDF_VariableText::Provider::IsLatinWord(uint16_t word) { +- return (word >= 0x61 && word <= 0x7A) || (word >= 0x41 && word <= 0x5A) || +- word == 0x2D || word == 0x27; +-} +- +-int32_t CPDF_VariableText::Provider::GetDefaultFontIndex() { +- return 0; +-} +- +-CPDF_VariableText::Iterator::Iterator(CPDF_VariableText* pVT) +- : m_CurPos(-1, -1, -1), m_pVT(pVT) {} +- +-CPDF_VariableText::Iterator::~Iterator() {} +- +-void CPDF_VariableText::Iterator::SetAt(int32_t nWordIndex) { +- m_CurPos = m_pVT->WordIndexToWordPlace(nWordIndex); +-} +- +-void CPDF_VariableText::Iterator::SetAt(const CPVT_WordPlace& place) { +- ASSERT(m_pVT); +- m_CurPos = place; +-} +- +-bool CPDF_VariableText::Iterator::NextWord() { +- if (m_CurPos == m_pVT->GetEndWordPlace()) +- return false; +- +- m_CurPos = m_pVT->GetNextWordPlace(m_CurPos); +- return true; +-} +- +-bool CPDF_VariableText::Iterator::PrevWord() { +- if (m_CurPos == m_pVT->GetBeginWordPlace()) +- return false; +- +- m_CurPos = m_pVT->GetPrevWordPlace(m_CurPos); +- return true; +-} +- +-bool CPDF_VariableText::Iterator::NextLine() { +- if (!pdfium::IndexInBounds(m_pVT->m_SectionArray, m_CurPos.nSecIndex)) +- return false; +- +- CSection* pSection = m_pVT->m_SectionArray[m_CurPos.nSecIndex].get(); +- if (m_CurPos.nLineIndex < +- pdfium::CollectionSize(pSection->m_LineArray) - 1) { +- m_CurPos = CPVT_WordPlace(m_CurPos.nSecIndex, m_CurPos.nLineIndex + 1, -1); +- return true; +- } +- if (m_CurPos.nSecIndex < +- pdfium::CollectionSize(m_pVT->m_SectionArray) - 1) { +- m_CurPos = CPVT_WordPlace(m_CurPos.nSecIndex + 1, 0, -1); +- return true; +- } +- return false; +-} +- +-bool CPDF_VariableText::Iterator::GetWord(CPVT_Word& word) const { +- word.WordPlace = m_CurPos; +- if (!pdfium::IndexInBounds(m_pVT->m_SectionArray, m_CurPos.nSecIndex)) +- return false; +- +- CSection* pSection = m_pVT->m_SectionArray[m_CurPos.nSecIndex].get(); +- if (!pdfium::IndexInBounds(pSection->m_LineArray, m_CurPos.nLineIndex) || +- !pdfium::IndexInBounds(pSection->m_WordArray, m_CurPos.nWordIndex)) { +- return false; +- } +- +- CPVT_WordInfo* pWord = pSection->m_WordArray[m_CurPos.nWordIndex].get(); +- word.Word = pWord->Word; +- word.nCharset = pWord->nCharset; +- word.fWidth = m_pVT->GetWordWidth(*pWord); +- word.ptWord = +- m_pVT->InToOut(CFX_PointF(pWord->fWordX + pSection->m_Rect.left, +- pWord->fWordY + pSection->m_Rect.top)); +- word.fAscent = m_pVT->GetWordAscent(*pWord); +- word.fDescent = m_pVT->GetWordDescent(*pWord); +- word.nFontIndex = m_pVT->GetWordFontIndex(*pWord); +- word.fFontSize = m_pVT->GetWordFontSize(); +- return true; +-} +- +-bool CPDF_VariableText::Iterator::GetLine(CPVT_Line& line) const { +- ASSERT(m_pVT); +- line.lineplace = CPVT_WordPlace(m_CurPos.nSecIndex, m_CurPos.nLineIndex, -1); +- if (!pdfium::IndexInBounds(m_pVT->m_SectionArray, m_CurPos.nSecIndex)) +- return false; +- +- CSection* pSection = m_pVT->m_SectionArray[m_CurPos.nSecIndex].get(); +- if (!pdfium::IndexInBounds(pSection->m_LineArray, m_CurPos.nLineIndex)) +- return false; +- +- CLine* pLine = pSection->m_LineArray[m_CurPos.nLineIndex].get(); +- line.ptLine = m_pVT->InToOut( +- CFX_PointF(pLine->m_LineInfo.fLineX + pSection->m_Rect.left, +- pLine->m_LineInfo.fLineY + pSection->m_Rect.top)); +- line.fLineWidth = pLine->m_LineInfo.fLineWidth; +- line.fLineAscent = pLine->m_LineInfo.fLineAscent; +- line.fLineDescent = pLine->m_LineInfo.fLineDescent; +- line.lineEnd = pLine->GetEndWordPlace(); +- return true; +-} +- +-CPDF_VariableText::CPDF_VariableText() = default; +- +-CPDF_VariableText::~CPDF_VariableText() = default; +- +-void CPDF_VariableText::Initialize() { +- if (m_bInitialized) +- return; +- +- CPVT_WordPlace place; +- place.nSecIndex = 0; +- AddSection(place); +- +- CPVT_LineInfo lineinfo; +- lineinfo.fLineAscent = GetFontAscent(GetDefaultFontIndex(), GetFontSize()); +- lineinfo.fLineDescent = GetFontDescent(GetDefaultFontIndex(), GetFontSize()); +- AddLine(place, lineinfo); +- +- if (!m_SectionArray.empty()) +- m_SectionArray.front()->ResetLinePlace(); +- +- m_bInitialized = true; +-} +- +-CPVT_WordPlace CPDF_VariableText::InsertWord(const CPVT_WordPlace& place, +- uint16_t word, +- int32_t charset) { +- int32_t nTotalWords = GetTotalWords(); +- if (m_nLimitChar > 0 && nTotalWords >= m_nLimitChar) +- return place; +- if (m_nCharArray > 0 && nTotalWords >= m_nCharArray) +- return place; +- +- CPVT_WordPlace newplace = place; +- newplace.nWordIndex++; +- int32_t nFontIndex = +- GetSubWord() > 0 ? GetDefaultFontIndex() +- : GetWordFontIndex(word, charset, GetDefaultFontIndex()); +- return AddWord(newplace, CPVT_WordInfo(word, charset, nFontIndex)); +-} +- +-CPVT_WordPlace CPDF_VariableText::InsertSection(const CPVT_WordPlace& place) { +- int32_t nTotalWords = GetTotalWords(); +- if (m_nLimitChar > 0 && nTotalWords >= m_nLimitChar) +- return place; +- if (m_nCharArray > 0 && nTotalWords >= m_nCharArray) +- return place; +- if (!m_bMultiLine) +- return place; +- +- CPVT_WordPlace wordplace = place; +- UpdateWordPlace(wordplace); +- if (!pdfium::IndexInBounds(m_SectionArray, wordplace.nSecIndex)) +- return place; +- +- CSection* pSection = m_SectionArray[wordplace.nSecIndex].get(); +- CPVT_WordPlace NewPlace(wordplace.nSecIndex + 1, 0, -1); +- AddSection(NewPlace); +- CPVT_WordPlace result = NewPlace; +- if (pdfium::IndexInBounds(m_SectionArray, NewPlace.nSecIndex)) { +- CSection* pNewSection = m_SectionArray[NewPlace.nSecIndex].get(); +- for (int32_t w = wordplace.nWordIndex + 1; +- w < pdfium::CollectionSize(pSection->m_WordArray); ++w) { +- NewPlace.nWordIndex++; +- pNewSection->AddWord(NewPlace, *pSection->m_WordArray[w]); +- } +- } +- ClearSectionRightWords(wordplace); +- return result; +-} +- +-CPVT_WordPlace CPDF_VariableText::DeleteWords( +- const CPVT_WordRange& PlaceRange) { +- bool bLastSecPos = +- pdfium::IndexInBounds(m_SectionArray, PlaceRange.EndPos.nSecIndex) && +- PlaceRange.EndPos == +- m_SectionArray[PlaceRange.EndPos.nSecIndex]->GetEndWordPlace(); +- +- ClearWords(PlaceRange); +- if (PlaceRange.BeginPos.nSecIndex != PlaceRange.EndPos.nSecIndex) { +- ClearEmptySections(PlaceRange); +- if (!bLastSecPos) +- LinkLatterSection(PlaceRange.BeginPos); +- } +- return PlaceRange.BeginPos; +-} +- +-CPVT_WordPlace CPDF_VariableText::DeleteWord(const CPVT_WordPlace& place) { +- return ClearRightWord(AdjustLineHeader(place, true)); +-} +- +-CPVT_WordPlace CPDF_VariableText::BackSpaceWord(const CPVT_WordPlace& place) { +- return ClearLeftWord(AdjustLineHeader(place, true)); +-} +- +-void CPDF_VariableText::SetText(const WideString& swText) { +- DeleteWords(CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace())); +- CPVT_WordPlace wp(0, 0, -1); +- if (!m_SectionArray.empty()) +- m_SectionArray.front()->m_Rect = CPVT_FloatRect(); +- +- int32_t nCharCount = 0; +- for (int32_t i = 0, sz = swText.GetLength(); i < sz; i++) { +- if (m_nLimitChar > 0 && nCharCount >= m_nLimitChar) +- break; +- if (m_nCharArray > 0 && nCharCount >= m_nCharArray) +- break; +- +- uint16_t word = swText[i]; +- switch (word) { +- case 0x0D: +- if (m_bMultiLine) { +- if (i + 1 < sz && swText[i + 1] == 0x0A) +- i++; +- wp.AdvanceSection(); +- AddSection(wp); +- } +- break; +- case 0x0A: +- if (m_bMultiLine) { +- if (i + 1 < sz && swText[i + 1] == 0x0D) +- i++; +- wp.AdvanceSection(); +- AddSection(wp); +- } +- break; +- case 0x09: +- word = 0x20; +- FALLTHROUGH; +- default: +- wp = InsertWord(wp, word, FX_CHARSET_Default); +- break; +- } +- nCharCount++; +- } +-} +- +-void CPDF_VariableText::UpdateWordPlace(CPVT_WordPlace& place) const { +- if (place.nSecIndex < 0) +- place = GetBeginWordPlace(); +- if (place.nSecIndex >= pdfium::CollectionSize(m_SectionArray)) +- place = GetEndWordPlace(); +- +- place = AdjustLineHeader(place, true); +- if (pdfium::IndexInBounds(m_SectionArray, place.nSecIndex)) +- m_SectionArray[place.nSecIndex]->UpdateWordPlace(place); +-} +- +-int32_t CPDF_VariableText::WordPlaceToWordIndex( +- const CPVT_WordPlace& place) const { +- CPVT_WordPlace newplace = place; +- UpdateWordPlace(newplace); +- int32_t nIndex = 0; +- int32_t i = 0; +- int32_t sz = 0; +- for (i = 0, sz = pdfium::CollectionSize(m_SectionArray); +- i < sz && i < newplace.nSecIndex; i++) { +- CSection* pSection = m_SectionArray[i].get(); +- nIndex += pdfium::CollectionSize(pSection->m_WordArray); +- if (i != sz - 1) +- nIndex += kReturnLength; +- } +- if (pdfium::IndexInBounds(m_SectionArray, i)) +- nIndex += newplace.nWordIndex + kReturnLength; +- return nIndex; +-} +- +-CPVT_WordPlace CPDF_VariableText::WordIndexToWordPlace(int32_t index) const { +- CPVT_WordPlace place = GetBeginWordPlace(); +- int32_t nOldIndex = 0; +- int32_t nIndex = 0; +- bool bFound = false; +- for (int32_t i = 0, sz = pdfium::CollectionSize(m_SectionArray); +- i < sz; i++) { +- CSection* pSection = m_SectionArray[i].get(); +- nIndex += pdfium::CollectionSize(pSection->m_WordArray); +- if (nIndex == index) { +- place = pSection->GetEndWordPlace(); +- bFound = true; +- break; +- } +- if (nIndex > index) { +- place.nSecIndex = i; +- place.nWordIndex = index - nOldIndex - 1; +- pSection->UpdateWordPlace(place); +- bFound = true; +- break; +- } +- if (i != sz - 1) +- nIndex += kReturnLength; +- nOldIndex = nIndex; +- } +- if (!bFound) +- place = GetEndWordPlace(); +- return place; +-} +- +-CPVT_WordPlace CPDF_VariableText::GetBeginWordPlace() const { +- return m_bInitialized ? CPVT_WordPlace(0, 0, -1) : CPVT_WordPlace(); +-} +- +-CPVT_WordPlace CPDF_VariableText::GetEndWordPlace() const { +- if (m_SectionArray.empty()) +- return CPVT_WordPlace(); +- return m_SectionArray.back()->GetEndWordPlace(); +-} +- +-CPVT_WordPlace CPDF_VariableText::GetPrevWordPlace( +- const CPVT_WordPlace& place) const { +- if (place.nSecIndex < 0) +- return GetBeginWordPlace(); +- if (place.nSecIndex >= pdfium::CollectionSize(m_SectionArray)) +- return GetEndWordPlace(); +- +- CSection* pSection = m_SectionArray[place.nSecIndex].get(); +- if (place > pSection->GetBeginWordPlace()) +- return pSection->GetPrevWordPlace(place); +- if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex - 1)) +- return GetBeginWordPlace(); +- return m_SectionArray[place.nSecIndex - 1]->GetEndWordPlace(); +-} +- +-CPVT_WordPlace CPDF_VariableText::GetNextWordPlace( +- const CPVT_WordPlace& place) const { +- if (place.nSecIndex < 0) +- return GetBeginWordPlace(); +- if (place.nSecIndex >= pdfium::CollectionSize(m_SectionArray)) +- return GetEndWordPlace(); +- +- CSection* pSection = m_SectionArray[place.nSecIndex].get(); +- if (place < pSection->GetEndWordPlace()) +- return pSection->GetNextWordPlace(place); +- if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex + 1)) +- return GetEndWordPlace(); +- return m_SectionArray[place.nSecIndex + 1]->GetBeginWordPlace(); +-} +- +-CPVT_WordPlace CPDF_VariableText::SearchWordPlace( +- const CFX_PointF& point) const { +- CFX_PointF pt = OutToIn(point); +- CPVT_WordPlace place = GetBeginWordPlace(); +- int32_t nLeft = 0; +- int32_t nRight = pdfium::CollectionSize(m_SectionArray) - 1; +- int32_t nMid = pdfium::CollectionSize(m_SectionArray) / 2; +- bool bUp = true; +- bool bDown = true; +- while (nLeft <= nRight) { +- if (!pdfium::IndexInBounds(m_SectionArray, nMid)) +- break; +- CSection* pSection = m_SectionArray[nMid].get(); +- if (IsFloatBigger(pt.y, pSection->m_Rect.top)) +- bUp = false; +- if (IsFloatBigger(pSection->m_Rect.bottom, pt.y)) +- bDown = false; +- if (IsFloatSmaller(pt.y, pSection->m_Rect.top)) { +- nRight = nMid - 1; +- nMid = (nLeft + nRight) / 2; +- continue; +- } +- if (IsFloatBigger(pt.y, pSection->m_Rect.bottom)) { +- nLeft = nMid + 1; +- nMid = (nLeft + nRight) / 2; +- continue; +- } +- place = pSection->SearchWordPlace( +- CFX_PointF(pt.x - pSection->m_Rect.left, pt.y - pSection->m_Rect.top)); +- place.nSecIndex = nMid; +- return place; +- } +- if (bUp) +- place = GetBeginWordPlace(); +- if (bDown) +- place = GetEndWordPlace(); +- return place; +-} +- +-CPVT_WordPlace CPDF_VariableText::GetUpWordPlace( +- const CPVT_WordPlace& place, +- const CFX_PointF& point) const { +- if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex)) +- return place; +- +- CSection* pSection = m_SectionArray[place.nSecIndex].get(); +- CPVT_WordPlace temp = place; +- CFX_PointF pt = OutToIn(point); +- if (temp.nLineIndex-- > 0) { +- return pSection->SearchWordPlace(pt.x - pSection->m_Rect.left, temp); +- } +- if (temp.nSecIndex-- > 0) { +- if (pdfium::IndexInBounds(m_SectionArray, temp.nSecIndex)) { +- CSection* pLastSection = m_SectionArray[temp.nSecIndex].get(); +- temp.nLineIndex = +- pdfium::CollectionSize(pLastSection->m_LineArray) - 1; +- return pLastSection->SearchWordPlace(pt.x - pLastSection->m_Rect.left, +- temp); +- } +- } +- return place; +-} +- +-CPVT_WordPlace CPDF_VariableText::GetDownWordPlace( +- const CPVT_WordPlace& place, +- const CFX_PointF& point) const { +- if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex)) +- return place; +- +- CSection* pSection = m_SectionArray[place.nSecIndex].get(); +- CPVT_WordPlace temp = place; +- CFX_PointF pt = OutToIn(point); +- if (temp.nLineIndex++ < +- pdfium::CollectionSize(pSection->m_LineArray) - 1) { +- return pSection->SearchWordPlace(pt.x - pSection->m_Rect.left, temp); +- } +- temp.AdvanceSection(); +- if (!pdfium::IndexInBounds(m_SectionArray, temp.nSecIndex)) +- return place; +- +- return m_SectionArray[temp.nSecIndex]->SearchWordPlace( +- pt.x - pSection->m_Rect.left, temp); +-} +- +-CPVT_WordPlace CPDF_VariableText::GetLineBeginPlace( +- const CPVT_WordPlace& place) const { +- return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, -1); +-} +- +-CPVT_WordPlace CPDF_VariableText::GetLineEndPlace( +- const CPVT_WordPlace& place) const { +- if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex)) +- return place; +- +- CSection* pSection = m_SectionArray[place.nSecIndex].get(); +- if (!pdfium::IndexInBounds(pSection->m_LineArray, place.nLineIndex)) +- return place; +- +- return pSection->m_LineArray[place.nLineIndex]->GetEndWordPlace(); +-} +- +-CPVT_WordPlace CPDF_VariableText::GetSectionBeginPlace( +- const CPVT_WordPlace& place) const { +- return CPVT_WordPlace(place.nSecIndex, 0, -1); +-} +- +-CPVT_WordPlace CPDF_VariableText::GetSectionEndPlace( +- const CPVT_WordPlace& place) const { +- if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex)) +- return place; +- +- return m_SectionArray[place.nSecIndex]->GetEndWordPlace(); +-} +- +-int32_t CPDF_VariableText::GetTotalWords() const { +- int32_t nTotal = 0; +- for (const auto& pSection : m_SectionArray) { +- nTotal += +- pdfium::CollectionSize(pSection->m_WordArray) + kReturnLength; +- } +- return nTotal - kReturnLength; +-} +- +-CPVT_WordPlace CPDF_VariableText::AddSection(const CPVT_WordPlace& place) { +- if (IsValid() && !m_bMultiLine) +- return place; +- +- int32_t nSecIndex = pdfium::clamp( +- place.nSecIndex, 0, pdfium::CollectionSize(m_SectionArray)); +- +- auto pSection = pdfium::MakeUnique(this); +- pSection->m_Rect = CPVT_FloatRect(); +- pSection->SecPlace.nSecIndex = nSecIndex; +- m_SectionArray.insert(m_SectionArray.begin() + nSecIndex, +- std::move(pSection)); +- return place; +-} +- +-CPVT_WordPlace CPDF_VariableText::AddLine(const CPVT_WordPlace& place, +- const CPVT_LineInfo& lineinfo) { +- if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex)) +- return place; +- +- return m_SectionArray[place.nSecIndex]->AddLine(lineinfo); +-} +- +-CPVT_WordPlace CPDF_VariableText::AddWord(const CPVT_WordPlace& place, +- const CPVT_WordInfo& wordinfo) { +- if (m_SectionArray.empty()) +- return place; +- +- CPVT_WordPlace newplace = place; +- newplace.nSecIndex = +- pdfium::clamp(newplace.nSecIndex, 0, +- pdfium::CollectionSize(m_SectionArray) - 1); +- return m_SectionArray[newplace.nSecIndex]->AddWord(newplace, wordinfo); +-} +- +-void CPDF_VariableText::SetPlateRect(const CFX_FloatRect& rect) { +- m_rcPlate = rect; +-} +- +-void CPDF_VariableText::SetContentRect(const CPVT_FloatRect& rect) { +- m_rcContent = rect; +-} +- +-CFX_FloatRect CPDF_VariableText::GetContentRect() const { +- return InToOut(CPVT_FloatRect(m_rcContent)); +-} +- +-const CFX_FloatRect& CPDF_VariableText::GetPlateRect() const { +- return m_rcPlate; +-} +- +-float CPDF_VariableText::GetWordFontSize() { +- return GetFontSize(); +-} +- +-int32_t CPDF_VariableText::GetWordFontIndex(const CPVT_WordInfo& WordInfo) { +- return WordInfo.nFontIndex; +-} +- +-float CPDF_VariableText::GetWordWidth(int32_t nFontIndex, +- uint16_t Word, +- uint16_t SubWord, +- float fCharSpace, +- float fFontSize, +- float fWordTail) { +- return GetCharWidth(nFontIndex, Word, SubWord) * fFontSize * kFontScale + +- fCharSpace + fWordTail; +-} +- +-float CPDF_VariableText::GetWordWidth(const CPVT_WordInfo& WordInfo) { +- return GetWordWidth(GetWordFontIndex(WordInfo), WordInfo.Word, GetSubWord(), +- GetCharSpace(), GetWordFontSize(), WordInfo.fWordTail); +-} +- +-float CPDF_VariableText::GetLineAscent() { +- return GetFontAscent(GetDefaultFontIndex(), GetFontSize()); +-} +- +-float CPDF_VariableText::GetLineDescent() { +- return GetFontDescent(GetDefaultFontIndex(), GetFontSize()); +-} +- +-float CPDF_VariableText::GetFontAscent(int32_t nFontIndex, float fFontSize) { +- return (float)GetTypeAscent(nFontIndex) * fFontSize * kFontScale; +-} +- +-float CPDF_VariableText::GetFontDescent(int32_t nFontIndex, float fFontSize) { +- return (float)GetTypeDescent(nFontIndex) * fFontSize * kFontScale; +-} +- +-float CPDF_VariableText::GetWordAscent(const CPVT_WordInfo& WordInfo, +- float fFontSize) { +- return GetFontAscent(GetWordFontIndex(WordInfo), fFontSize); +-} +- +-float CPDF_VariableText::GetWordDescent(const CPVT_WordInfo& WordInfo, +- float fFontSize) { +- return GetFontDescent(GetWordFontIndex(WordInfo), fFontSize); +-} +- +-float CPDF_VariableText::GetWordAscent(const CPVT_WordInfo& WordInfo) { +- return GetFontAscent(GetWordFontIndex(WordInfo), GetWordFontSize()); +-} +- +-float CPDF_VariableText::GetWordDescent(const CPVT_WordInfo& WordInfo) { +- return GetFontDescent(GetWordFontIndex(WordInfo), GetWordFontSize()); +-} +- +-float CPDF_VariableText::GetLineLeading() { +- return m_fLineLeading; +-} +- +-float CPDF_VariableText::GetLineIndent() { +- return 0.0f; +-} +- +-int32_t CPDF_VariableText::GetAlignment() { +- return m_nAlignment; +-} +- +-void CPDF_VariableText::ClearSectionRightWords(const CPVT_WordPlace& place) { +- CPVT_WordPlace wordplace = AdjustLineHeader(place, true); +- if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex)) +- return; +- +- CSection* pSection = m_SectionArray[place.nSecIndex].get(); +- if (!pdfium::IndexInBounds(pSection->m_WordArray, wordplace.nWordIndex + 1)) +- return; +- +- pSection->m_WordArray.erase( +- pSection->m_WordArray.begin() + wordplace.nWordIndex + 1, +- pSection->m_WordArray.end()); +-} +- +-CPVT_WordPlace CPDF_VariableText::AdjustLineHeader(const CPVT_WordPlace& place, +- bool bPrevOrNext) const { +- if (place.nWordIndex < 0 && place.nLineIndex > 0) +- return bPrevOrNext ? GetPrevWordPlace(place) : GetNextWordPlace(place); +- return place; +-} +- +-bool CPDF_VariableText::ClearEmptySection(const CPVT_WordPlace& place) { +- if (place.nSecIndex == 0 && m_SectionArray.size() == 1) +- return false; +- +- if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex)) +- return false; +- +- if (!m_SectionArray[place.nSecIndex]->m_WordArray.empty()) +- return false; +- +- m_SectionArray.erase(m_SectionArray.begin() + place.nSecIndex); +- return true; +-} +- +-void CPDF_VariableText::ClearEmptySections(const CPVT_WordRange& PlaceRange) { +- CPVT_WordPlace wordplace; +- for (int32_t s = PlaceRange.EndPos.nSecIndex; +- s > PlaceRange.BeginPos.nSecIndex; s--) { +- wordplace.nSecIndex = s; +- ClearEmptySection(wordplace); +- } +-} +- +-void CPDF_VariableText::LinkLatterSection(const CPVT_WordPlace& place) { +- CPVT_WordPlace oldplace = AdjustLineHeader(place, true); +- if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex + 1)) +- return; +- +- CSection* pNextSection = m_SectionArray[place.nSecIndex + 1].get(); +- if (pdfium::IndexInBounds(m_SectionArray, oldplace.nSecIndex)) { +- CSection* pSection = m_SectionArray[oldplace.nSecIndex].get(); +- for (auto& pWord : pNextSection->m_WordArray) { +- oldplace.nWordIndex++; +- pSection->AddWord(oldplace, *pWord); +- } +- } +- m_SectionArray.erase(m_SectionArray.begin() + place.nSecIndex + 1); +-} +- +-void CPDF_VariableText::ClearWords(const CPVT_WordRange& PlaceRange) { +- CPVT_WordRange NewRange; +- NewRange.BeginPos = AdjustLineHeader(PlaceRange.BeginPos, true); +- NewRange.EndPos = AdjustLineHeader(PlaceRange.EndPos, true); +- for (int32_t s = NewRange.EndPos.nSecIndex; s >= NewRange.BeginPos.nSecIndex; +- s--) { +- if (pdfium::IndexInBounds(m_SectionArray, s)) +- m_SectionArray[s]->ClearWords(NewRange); +- } +-} +- +-CPVT_WordPlace CPDF_VariableText::ClearLeftWord(const CPVT_WordPlace& place) { +- if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex)) +- return place; +- +- CSection* pSection = m_SectionArray[place.nSecIndex].get(); +- CPVT_WordPlace leftplace = GetPrevWordPlace(place); +- if (leftplace == place) +- return place; +- +- if (leftplace.nSecIndex != place.nSecIndex) { +- if (pSection->m_WordArray.empty()) +- ClearEmptySection(place); +- else +- LinkLatterSection(leftplace); +- } else { +- pSection->ClearWord(place); +- } +- return leftplace; +-} +- +-CPVT_WordPlace CPDF_VariableText::ClearRightWord(const CPVT_WordPlace& place) { +- if (!pdfium::IndexInBounds(m_SectionArray, place.nSecIndex)) +- return place; +- +- CSection* pSection = m_SectionArray[place.nSecIndex].get(); +- CPVT_WordPlace rightplace = AdjustLineHeader(GetNextWordPlace(place), false); +- if (rightplace == place) +- return place; +- +- if (rightplace.nSecIndex != place.nSecIndex) +- LinkLatterSection(place); +- else +- pSection->ClearWord(rightplace); +- return place; +-} +- +-void CPDF_VariableText::RearrangeAll() { +- Rearrange(CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace())); +-} +- +-void CPDF_VariableText::RearrangePart(const CPVT_WordRange& PlaceRange) { +- Rearrange(PlaceRange); +-} +- +-CPVT_FloatRect CPDF_VariableText::Rearrange(const CPVT_WordRange& PlaceRange) { +- CPVT_FloatRect rcRet; +- if (IsValid()) { +- if (m_bAutoFontSize) { +- SetFontSize(GetAutoFontSize()); +- rcRet = RearrangeSections( +- CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace())); +- } else { +- rcRet = RearrangeSections(PlaceRange); +- } +- } +- SetContentRect(rcRet); +- return rcRet; +-} +- +-float CPDF_VariableText::GetAutoFontSize() { +- int32_t nTotal = sizeof(gFontSizeSteps) / sizeof(uint8_t); +- if (IsMultiLine()) +- nTotal /= 4; +- if (nTotal <= 0) +- return 0; +- if (GetPlateWidth() <= 0) +- return 0; +- +- int32_t nLeft = 0; +- int32_t nRight = nTotal - 1; +- int32_t nMid = nTotal / 2; +- while (nLeft <= nRight) { +- if (IsBigger(gFontSizeSteps[nMid])) +- nRight = nMid - 1; +- else +- nLeft = nMid + 1; +- nMid = (nLeft + nRight) / 2; +- } +- return (float)gFontSizeSteps[nMid]; +-} +- +-bool CPDF_VariableText::IsBigger(float fFontSize) const { +- CFX_SizeF szTotal; +- for (const auto& pSection : m_SectionArray) { +- CFX_SizeF size = pSection->GetSectionSize(fFontSize); +- szTotal.width = std::max(size.width, szTotal.width); +- szTotal.height += size.height; +- if (IsFloatBigger(szTotal.width, GetPlateWidth()) || +- IsFloatBigger(szTotal.height, GetPlateHeight())) { +- return true; +- } +- } +- return false; +-} +- +-CPVT_FloatRect CPDF_VariableText::RearrangeSections( +- const CPVT_WordRange& PlaceRange) { +- CPVT_WordPlace place; +- float fPosY = 0; +- float fOldHeight; +- int32_t nSSecIndex = PlaceRange.BeginPos.nSecIndex; +- int32_t nESecIndex = PlaceRange.EndPos.nSecIndex; +- CPVT_FloatRect rcRet; +- for (int32_t s = 0, sz = pdfium::CollectionSize(m_SectionArray); +- s < sz; s++) { +- place.nSecIndex = s; +- CSection* pSection = m_SectionArray[s].get(); +- pSection->SecPlace = place; +- CPVT_FloatRect rcSec = pSection->m_Rect; +- if (s >= nSSecIndex) { +- if (s <= nESecIndex) { +- rcSec = pSection->Rearrange(); +- rcSec.top += fPosY; +- rcSec.bottom += fPosY; +- } else { +- fOldHeight = pSection->m_Rect.bottom - pSection->m_Rect.top; +- rcSec.top = fPosY; +- rcSec.bottom = fPosY + fOldHeight; +- } +- pSection->m_Rect = rcSec; +- pSection->ResetLinePlace(); +- } +- if (s == 0) { +- rcRet = rcSec; +- } else { +- rcRet.left = std::min(rcSec.left, rcRet.left); +- rcRet.top = std::min(rcSec.top, rcRet.top); +- rcRet.right = std::max(rcSec.right, rcRet.right); +- rcRet.bottom = std::max(rcSec.bottom, rcRet.bottom); +- } +- fPosY += rcSec.Height(); +- } +- return rcRet; +-} +- +-uint32_t CPDF_VariableText::GetCharWidth(int32_t nFontIndex, +- uint16_t Word, +- uint16_t SubWord) { +- if (!m_pVTProvider) +- return 0; +- uint16_t word = SubWord ? SubWord : Word; +- return m_pVTProvider->GetCharWidth(nFontIndex, word); +-} +- +-int32_t CPDF_VariableText::GetTypeAscent(int32_t nFontIndex) { +- return m_pVTProvider ? m_pVTProvider->GetTypeAscent(nFontIndex) : 0; +-} +- +-int32_t CPDF_VariableText::GetTypeDescent(int32_t nFontIndex) { +- return m_pVTProvider ? m_pVTProvider->GetTypeDescent(nFontIndex) : 0; +-} +- +-int32_t CPDF_VariableText::GetWordFontIndex(uint16_t word, +- int32_t charset, +- int32_t nFontIndex) { +- return m_pVTProvider +- ? m_pVTProvider->GetWordFontIndex(word, charset, nFontIndex) +- : -1; +-} +- +-int32_t CPDF_VariableText::GetDefaultFontIndex() { +- return m_pVTProvider ? m_pVTProvider->GetDefaultFontIndex() : -1; +-} +- +-bool CPDF_VariableText::IsLatinWord(uint16_t word) { +- return m_pVTProvider && m_pVTProvider->IsLatinWord(word); +-} +- +-CPDF_VariableText::Iterator* CPDF_VariableText::GetIterator() { +- if (!m_pVTIterator) +- m_pVTIterator = pdfium::MakeUnique(this); +- return m_pVTIterator.get(); +-} +- +-void CPDF_VariableText::SetProvider(CPDF_VariableText::Provider* pProvider) { +- m_pVTProvider = pProvider; +-} +- +-CFX_PointF CPDF_VariableText::GetBTPoint() const { +- return CFX_PointF(m_rcPlate.left, m_rcPlate.top); +-} +- +-CFX_PointF CPDF_VariableText::GetETPoint() const { +- return CFX_PointF(m_rcPlate.right, m_rcPlate.bottom); +-} +- +-CFX_PointF CPDF_VariableText::InToOut(const CFX_PointF& point) const { +- return CFX_PointF(point.x + GetBTPoint().x, GetBTPoint().y - point.y); +-} +- +-CFX_PointF CPDF_VariableText::OutToIn(const CFX_PointF& point) const { +- return CFX_PointF(point.x - GetBTPoint().x, GetBTPoint().y - point.y); +-} +- +-CFX_FloatRect CPDF_VariableText::InToOut(const CPVT_FloatRect& rect) const { +- CFX_PointF ptLeftTop = InToOut(CFX_PointF(rect.left, rect.top)); +- CFX_PointF ptRightBottom = InToOut(CFX_PointF(rect.right, rect.bottom)); +- return CFX_FloatRect(ptLeftTop.x, ptRightBottom.y, ptRightBottom.x, +- ptLeftTop.y); +-} +- +-CPVT_FloatRect CPDF_VariableText::OutToIn(const CFX_FloatRect& rect) const { +- CFX_PointF ptLeftTop = OutToIn(CFX_PointF(rect.left, rect.top)); +- CFX_PointF ptRightBottom = OutToIn(CFX_PointF(rect.right, rect.bottom)); +- return CPVT_FloatRect(ptLeftTop.x, ptLeftTop.y, ptRightBottom.x, +- ptRightBottom.y); +-} +diff --git a/core/fpdfdoc/cpdf_viewerpreferences.cpp b/core/fpdfdoc/cpdf_viewerpreferences.cpp +index be07fdc20..e3f15f0b8 100644 +--- a/core/fpdfdoc/cpdf_viewerpreferences.cpp ++++ b/core/fpdfdoc/cpdf_viewerpreferences.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,7 @@ + + #include "core/fpdfdoc/cpdf_viewerpreferences.h" + ++#include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_name.h" +@@ -16,44 +17,45 @@ CPDF_ViewerPreferences::CPDF_ViewerPreferences(const CPDF_Document* pDoc) + CPDF_ViewerPreferences::~CPDF_ViewerPreferences() = default; + + bool CPDF_ViewerPreferences::IsDirectionR2L() const { +- const CPDF_Dictionary* pDict = GetViewerPreferences(); +- return pDict && pDict->GetStringFor("Direction") == "R2L"; ++ RetainPtr pDict = GetViewerPreferences(); ++ return pDict && pDict->GetByteStringFor("Direction") == "R2L"; + } + + bool CPDF_ViewerPreferences::PrintScaling() const { +- const CPDF_Dictionary* pDict = GetViewerPreferences(); +- return !pDict || pDict->GetStringFor("PrintScaling") != "None"; ++ RetainPtr pDict = GetViewerPreferences(); ++ return !pDict || pDict->GetByteStringFor("PrintScaling") != "None"; + } + + int32_t CPDF_ViewerPreferences::NumCopies() const { +- const CPDF_Dictionary* pDict = GetViewerPreferences(); ++ RetainPtr pDict = GetViewerPreferences(); + return pDict ? pDict->GetIntegerFor("NumCopies") : 1; + } + +-CPDF_Array* CPDF_ViewerPreferences::PrintPageRange() const { +- CPDF_Dictionary* pDict = GetViewerPreferences(); ++RetainPtr CPDF_ViewerPreferences::PrintPageRange() const { ++ RetainPtr pDict = GetViewerPreferences(); + return pDict ? pDict->GetArrayFor("PrintPageRange") : nullptr; + } + + ByteString CPDF_ViewerPreferences::Duplex() const { +- const CPDF_Dictionary* pDict = GetViewerPreferences(); +- return pDict ? pDict->GetStringFor("Duplex") : ByteString("None"); ++ RetainPtr pDict = GetViewerPreferences(); ++ return pDict ? pDict->GetByteStringFor("Duplex") : ByteString("None"); + } + +-Optional CPDF_ViewerPreferences::GenericName( ++absl::optional CPDF_ViewerPreferences::GenericName( + const ByteString& bsKey) const { +- const CPDF_Dictionary* pDict = GetViewerPreferences(); ++ RetainPtr pDict = GetViewerPreferences(); + if (!pDict) +- return {}; ++ return absl::nullopt; + +- const CPDF_Name* pName = ToName(pDict->GetObjectFor(bsKey)); ++ RetainPtr pName = ToName(pDict->GetObjectFor(bsKey)); + if (!pName) +- return {}; ++ return absl::nullopt; + + return pName->GetString(); + } + +-CPDF_Dictionary* CPDF_ViewerPreferences::GetViewerPreferences() const { +- CPDF_Dictionary* pDict = m_pDoc->GetRoot(); ++RetainPtr CPDF_ViewerPreferences::GetViewerPreferences() ++ const { ++ const CPDF_Dictionary* pDict = m_pDoc->GetRoot(); + return pDict ? pDict->GetDictFor("ViewerPreferences") : nullptr; + } +diff --git a/core/fpdfdoc/cpdf_viewerpreferences.h b/core/fpdfdoc/cpdf_viewerpreferences.h +index ff2b1c8a4..3e170485b 100644 +--- a/core/fpdfdoc/cpdf_viewerpreferences.h ++++ b/core/fpdfdoc/cpdf_viewerpreferences.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,12 @@ + #ifndef CORE_FPDFDOC_CPDF_VIEWERPREFERENCES_H_ + #define CORE_FPDFDOC_CPDF_VIEWERPREFERENCES_H_ + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include ++ ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CPDF_Array; + class CPDF_Dictionary; +@@ -24,14 +26,14 @@ class CPDF_ViewerPreferences { + bool IsDirectionR2L() const; + bool PrintScaling() const; + int32_t NumCopies() const; +- CPDF_Array* PrintPageRange() const; ++ RetainPtr PrintPageRange() const; + ByteString Duplex() const; + + // Gets the entry for |bsKey|. +- Optional GenericName(const ByteString& bsKey) const; ++ absl::optional GenericName(const ByteString& bsKey) const; + + private: +- CPDF_Dictionary* GetViewerPreferences() const; ++ RetainPtr GetViewerPreferences() const; + + UnownedPtr const m_pDoc; + }; +diff --git a/core/fpdfdoc/cpvt_floatrect.h b/core/fpdfdoc/cpvt_floatrect.h +index d302ed060..a9add92e3 100644 +--- a/core/fpdfdoc/cpvt_floatrect.h ++++ b/core/fpdfdoc/cpvt_floatrect.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fpdfdoc/cpvt_fontmap.cpp b/core/fpdfdoc/cpvt_fontmap.cpp +index b21bd0d4e..d26655f13 100644 +--- a/core/fpdfdoc/cpvt_fontmap.cpp ++++ b/core/fpdfdoc/cpvt_fontmap.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,8 @@ + + #include "core/fpdfdoc/cpvt_fontmap.h" + ++#include ++ + #include "core/fpdfapi/font/cpdf_font.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" +@@ -13,40 +15,37 @@ + #include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fpdfdoc/cpdf_interactiveform.h" + #include "core/fxcrt/fx_codepage.h" +-#include "third_party/base/logging.h" ++#include "third_party/base/check.h" ++#include "third_party/base/notreached.h" + + CPVT_FontMap::CPVT_FontMap(CPDF_Document* pDoc, +- CPDF_Dictionary* pResDict, +- const RetainPtr& pDefFont, ++ RetainPtr pResDict, ++ RetainPtr pDefFont, + const ByteString& sDefFontAlias) + : m_pDocument(pDoc), +- m_pResDict(pResDict), +- m_pDefFont(pDefFont), ++ m_pResDict(std::move(pResDict)), ++ m_pDefFont(std::move(pDefFont)), + m_sDefFontAlias(sDefFontAlias) {} + +-CPVT_FontMap::~CPVT_FontMap() {} ++CPVT_FontMap::~CPVT_FontMap() = default; + +-// static +-RetainPtr CPVT_FontMap::GetAnnotSysPDFFont( +- CPDF_Document* pDoc, +- CPDF_Dictionary* pResDict, +- ByteString* sSysFontAlias) { +- if (!pDoc || !pResDict) +- return nullptr; ++void CPVT_FontMap::SetupAnnotSysPDFFont() { ++ if (!m_pDocument || !m_pResDict) ++ return; + +- CPDF_Dictionary* pFormDict = pDoc->GetRoot()->GetDictFor("AcroForm"); + RetainPtr pPDFFont = +- AddNativeInteractiveFormFont(pFormDict, pDoc, sSysFontAlias); ++ CPDF_InteractiveForm::AddNativeInteractiveFormFont(m_pDocument, ++ &m_sSysFontAlias); + if (!pPDFFont) +- return nullptr; ++ return; + +- CPDF_Dictionary* pFontList = pResDict->GetDictFor("Font"); +- if (ValidateFontResourceDict(pFontList) && +- !pFontList->KeyExist(*sSysFontAlias)) { +- pFontList->SetNewFor(*sSysFontAlias, pDoc, +- pPDFFont->GetFontDict()->GetObjNum()); ++ RetainPtr pFontList = m_pResDict->GetMutableDictFor("Font"); ++ if (ValidateFontResourceDict(pFontList.Get()) && ++ !pFontList->KeyExist(m_sSysFontAlias)) { ++ pFontList->SetNewFor(m_sSysFontAlias, m_pDocument, ++ pPDFFont->GetFontDictObjNum()); + } +- return pPDFFont; ++ m_pSysFont = std::move(pPDFFont); + } + + RetainPtr CPVT_FontMap::GetPDFFont(int32_t nFontIndex) { +@@ -54,10 +53,8 @@ RetainPtr CPVT_FontMap::GetPDFFont(int32_t nFontIndex) { + case 0: + return m_pDefFont; + case 1: +- if (!m_pSysFont) { +- m_pSysFont = GetAnnotSysPDFFont(m_pDocument.Get(), m_pResDict.Get(), +- &m_sSysFontAlias); +- } ++ if (!m_pSysFont) ++ SetupAnnotSysPDFFont(); + return m_pSysFont; + default: + return nullptr; +@@ -69,10 +66,8 @@ ByteString CPVT_FontMap::GetPDFFontAlias(int32_t nFontIndex) { + case 0: + return m_sDefFontAlias; + case 1: +- if (!m_pSysFont) { +- m_pSysFont = GetAnnotSysPDFFont(m_pDocument.Get(), m_pResDict.Get(), +- &m_sSysFontAlias); +- } ++ if (!m_pSysFont) ++ SetupAnnotSysPDFFont(); + return m_sSysFontAlias; + default: + return ByteString(); +@@ -80,7 +75,7 @@ ByteString CPVT_FontMap::GetPDFFontAlias(int32_t nFontIndex) { + } + + int32_t CPVT_FontMap::GetWordFontIndex(uint16_t word, +- int32_t charset, ++ FX_Charset charset, + int32_t nFontIndex) { + NOTREACHED(); + return 0; +@@ -91,7 +86,8 @@ int32_t CPVT_FontMap::CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) { + return 0; + } + +-int32_t CPVT_FontMap::CharSetFromUnicode(uint16_t word, int32_t nOldCharset) { ++FX_Charset CPVT_FontMap::CharSetFromUnicode(uint16_t word, ++ FX_Charset nOldCharset) { + NOTREACHED(); +- return FX_CHARSET_ANSI; ++ return FX_Charset::kANSI; + } +diff --git a/core/fpdfdoc/cpvt_fontmap.h b/core/fpdfdoc/cpvt_fontmap.h +index a85afcfb8..e3add6fce 100644 +--- a/core/fpdfdoc/cpvt_fontmap.h ++++ b/core/fpdfdoc/cpvt_fontmap.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,7 +10,7 @@ + #include + + #include "core/fpdfdoc/ipvt_fontmap.h" +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + +@@ -21,8 +21,8 @@ class CPDF_Font; + class CPVT_FontMap final : public IPVT_FontMap { + public: + CPVT_FontMap(CPDF_Document* pDoc, +- CPDF_Dictionary* pResDict, +- const RetainPtr& pDefFont, ++ RetainPtr pResDict, ++ RetainPtr pDefFont, + const ByteString& sDefFontAlias); + ~CPVT_FontMap() override; + +@@ -30,16 +30,14 @@ class CPVT_FontMap final : public IPVT_FontMap { + RetainPtr GetPDFFont(int32_t nFontIndex) override; + ByteString GetPDFFontAlias(int32_t nFontIndex) override; + int32_t GetWordFontIndex(uint16_t word, +- int32_t charset, ++ FX_Charset charset, + int32_t nFontIndex) override; + int32_t CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) override; +- int32_t CharSetFromUnicode(uint16_t word, int32_t nOldCharset) override; +- +- static RetainPtr GetAnnotSysPDFFont(CPDF_Document* pDoc, +- CPDF_Dictionary* pResDict, +- ByteString* sSysFontAlias); ++ FX_Charset CharSetFromUnicode(uint16_t word, FX_Charset nOldCharset) override; + + private: ++ void SetupAnnotSysPDFFont(); ++ + UnownedPtr const m_pDocument; + RetainPtr const m_pResDict; + RetainPtr const m_pDefFont; +diff --git a/core/fpdfdoc/cpvt_line.h b/core/fpdfdoc/cpvt_line.h +index 087034d3d..f50ffd51d 100644 +--- a/core/fpdfdoc/cpvt_line.h ++++ b/core/fpdfdoc/cpvt_line.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,7 +9,6 @@ + + #include "core/fpdfdoc/cpvt_wordplace.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" + + class CPVT_Line { + public: +diff --git a/core/fpdfdoc/cpvt_lineinfo.h b/core/fpdfdoc/cpvt_lineinfo.h +index 96a323438..ac1f41d37 100644 +--- a/core/fpdfdoc/cpvt_lineinfo.h ++++ b/core/fpdfdoc/cpvt_lineinfo.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,30 +7,17 @@ + #ifndef CORE_FPDFDOC_CPVT_LINEINFO_H_ + #define CORE_FPDFDOC_CPVT_LINEINFO_H_ + +-#include "core/fxcrt/fx_system.h" ++#include + +-class CPVT_LineInfo { +- public: +- CPVT_LineInfo(); +- +- int32_t nTotalWord; +- int32_t nBeginWordIndex; +- int32_t nEndWordIndex; +- float fLineX; +- float fLineY; +- float fLineWidth; +- float fLineAscent; +- float fLineDescent; ++struct CPVT_LineInfo { ++ int32_t nTotalWord = 0; ++ int32_t nBeginWordIndex = -1; ++ int32_t nEndWordIndex = -1; ++ float fLineX = 0.0f; ++ float fLineY = 0.0f; ++ float fLineWidth = 0.0f; ++ float fLineAscent = 0.0f; ++ float fLineDescent = 0.0f; + }; + +-inline CPVT_LineInfo::CPVT_LineInfo() +- : nTotalWord(0), +- nBeginWordIndex(-1), +- nEndWordIndex(-1), +- fLineX(0.0f), +- fLineY(0.0f), +- fLineWidth(0.0f), +- fLineAscent(0.0f), +- fLineDescent(0.0f) {} +- + #endif // CORE_FPDFDOC_CPVT_LINEINFO_H_ +diff --git a/core/fpdfdoc/ctypeset.cpp b/core/fpdfdoc/cpvt_section.cpp +similarity index 50% +rename from core/fpdfdoc/ctypeset.cpp +rename to core/fpdfdoc/cpvt_section.cpp +index 8c61f9ed8..9276d0cae 100644 +--- a/core/fpdfdoc/ctypeset.cpp ++++ b/core/fpdfdoc/cpvt_section.cpp +@@ -1,22 +1,22 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#include "core/fpdfdoc/ctypeset.h" ++#include "core/fpdfdoc/cpvt_section.h" + + #include + +-#include "core/fpdfdoc/cline.h" +-#include "core/fpdfdoc/cpdf_variabletext.h" ++#include "core/fpdfdoc/cpvt_variabletext.h" + #include "core/fpdfdoc/cpvt_wordinfo.h" +-#include "core/fpdfdoc/csection.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxcrt/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/cxx17_backports.h" + + namespace { + +-const uint8_t special_chars[128] = { ++constexpr uint8_t kSpecialChars[128] = { + 0x00, 0x0C, 0x08, 0x0C, 0x08, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, +@@ -32,7 +32,7 @@ const uint8_t special_chars[128] = { + + bool IsLatin(uint16_t word) { + if (word <= 0x007F) +- return !!(special_chars[word] & 0x01); ++ return !!(kSpecialChars[word] & 0x01); + + return ((word >= 0x00C0 && word <= 0x00FF) || + (word >= 0x0100 && word <= 0x024F) || +@@ -70,7 +70,7 @@ bool IsCJK(uint32_t word) { + + bool IsPunctuation(uint32_t word) { + if (word <= 0x007F) +- return !!(special_chars[word] & 0x08); ++ return !!(kSpecialChars[word] & 0x08); + + if (word >= 0x0080 && word <= 0x00FF) { + return (word == 0x0082 || word == 0x0084 || word == 0x0085 || +@@ -116,12 +116,12 @@ bool IsPunctuation(uint32_t word) { + } + + bool IsConnectiveSymbol(uint32_t word) { +- return word <= 0x007F && (special_chars[word] & 0x20); ++ return word <= 0x007F && (kSpecialChars[word] & 0x20); + } + + bool IsOpenStylePunctuation(uint32_t word) { + if (word <= 0x007F) +- return !!(special_chars[word] & 0x04); ++ return !!(kSpecialChars[word] & 0x04); + + return (word == 0x300A || word == 0x300C || word == 0x300E || + word == 0x3010 || word == 0x3014 || word == 0x3016 || +@@ -173,15 +173,270 @@ bool NeedDivision(uint16_t prevWord, uint16_t curWord) { + + } // namespace + +-CTypeset::CTypeset(CSection* pSection) +- : m_pVT(pSection->m_pVT), m_pSection(pSection) {} ++CPVT_Section::Line::Line(const CPVT_LineInfo& lineinfo) ++ : m_LineInfo(lineinfo) {} + +-CTypeset::~CTypeset() = default; ++CPVT_Section::Line::~Line() = default; + +-CPVT_FloatRect CTypeset::CharArray() { +- m_rcRet = CPVT_FloatRect(); +- if (m_pSection->m_LineArray.empty()) +- return m_rcRet; ++CPVT_WordPlace CPVT_Section::Line::GetBeginWordPlace() const { ++ return CPVT_WordPlace(m_LinePlace.nSecIndex, m_LinePlace.nLineIndex, -1); ++} ++ ++CPVT_WordPlace CPVT_Section::Line::GetEndWordPlace() const { ++ return CPVT_WordPlace(m_LinePlace.nSecIndex, m_LinePlace.nLineIndex, ++ m_LineInfo.nEndWordIndex); ++} ++ ++CPVT_WordPlace CPVT_Section::Line::GetPrevWordPlace( ++ const CPVT_WordPlace& place) const { ++ if (place.nWordIndex > m_LineInfo.nEndWordIndex) { ++ return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, ++ m_LineInfo.nEndWordIndex); ++ } ++ return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, ++ place.nWordIndex - 1); ++} ++ ++CPVT_WordPlace CPVT_Section::Line::GetNextWordPlace( ++ const CPVT_WordPlace& place) const { ++ if (place.nWordIndex < m_LineInfo.nBeginWordIndex) { ++ return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, ++ m_LineInfo.nBeginWordIndex); ++ } ++ return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, ++ place.nWordIndex + 1); ++} ++ ++CPVT_Section::CPVT_Section(CPVT_VariableText* pVT) : m_pVT(pVT) { ++ DCHECK(m_pVT); ++} ++ ++CPVT_Section::~CPVT_Section() = default; ++ ++void CPVT_Section::ResetLinePlace() { ++ int32_t i = 0; ++ for (auto& pLine : m_LineArray) { ++ pLine->m_LinePlace = CPVT_WordPlace(m_SecPlace.nSecIndex, i, -1); ++ ++i; ++ } ++} ++ ++CPVT_WordPlace CPVT_Section::AddWord(const CPVT_WordPlace& place, ++ const CPVT_WordInfo& wordinfo) { ++ int32_t nWordIndex = pdfium::clamp( ++ place.nWordIndex, 0, fxcrt::CollectionSize(m_WordArray)); ++ m_WordArray.insert(m_WordArray.begin() + nWordIndex, ++ std::make_unique(wordinfo)); ++ return place; ++} ++ ++CPVT_WordPlace CPVT_Section::AddLine(const CPVT_LineInfo& lineinfo) { ++ m_LineArray.push_back(std::make_unique(lineinfo)); ++ return CPVT_WordPlace(m_SecPlace.nSecIndex, ++ fxcrt::CollectionSize(m_LineArray) - 1, -1); ++} ++ ++CPVT_FloatRect CPVT_Section::Rearrange() { ++ if (m_pVT->GetCharArray() > 0) ++ return RearrangeCharArray(); ++ return RearrangeTypeset(); ++} ++ ++CFX_SizeF CPVT_Section::GetSectionSize(float fFontSize) { ++ CPVT_FloatRect result = SplitLines(/*bTypeset=*/false, fFontSize); ++ return CFX_SizeF(result.Width(), result.Height()); ++} ++ ++CPVT_WordPlace CPVT_Section::GetBeginWordPlace() const { ++ if (m_LineArray.empty()) ++ return m_SecPlace; ++ return m_LineArray.front()->GetBeginWordPlace(); ++} ++ ++CPVT_WordPlace CPVT_Section::GetEndWordPlace() const { ++ if (m_LineArray.empty()) ++ return m_SecPlace; ++ return m_LineArray.back()->GetEndWordPlace(); ++} ++ ++CPVT_WordPlace CPVT_Section::GetPrevWordPlace( ++ const CPVT_WordPlace& place) const { ++ if (place.nLineIndex < 0) ++ return GetBeginWordPlace(); ++ ++ if (place.nLineIndex >= fxcrt::CollectionSize(m_LineArray)) ++ return GetEndWordPlace(); ++ ++ Line* pLine = m_LineArray[place.nLineIndex].get(); ++ if (place.nWordIndex == pLine->m_LineInfo.nBeginWordIndex) ++ return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, -1); ++ ++ if (place.nWordIndex >= pLine->m_LineInfo.nBeginWordIndex) ++ return pLine->GetPrevWordPlace(place); ++ ++ if (!fxcrt::IndexInBounds(m_LineArray, place.nLineIndex - 1)) ++ return place; ++ ++ return m_LineArray[place.nLineIndex - 1]->GetEndWordPlace(); ++} ++ ++CPVT_WordPlace CPVT_Section::GetNextWordPlace( ++ const CPVT_WordPlace& place) const { ++ if (place.nLineIndex < 0) ++ return GetBeginWordPlace(); ++ ++ if (place.nLineIndex >= fxcrt::CollectionSize(m_LineArray)) ++ return GetEndWordPlace(); ++ ++ Line* pLine = m_LineArray[place.nLineIndex].get(); ++ if (place.nWordIndex < pLine->m_LineInfo.nEndWordIndex) ++ return pLine->GetNextWordPlace(place); ++ ++ if (!fxcrt::IndexInBounds(m_LineArray, place.nLineIndex + 1)) ++ return place; ++ ++ return m_LineArray[place.nLineIndex + 1]->GetBeginWordPlace(); ++} ++ ++void CPVT_Section::UpdateWordPlace(CPVT_WordPlace& place) const { ++ int32_t nLeft = 0; ++ int32_t nRight = fxcrt::CollectionSize(m_LineArray) - 1; ++ int32_t nMid = (nLeft + nRight) / 2; ++ while (nLeft <= nRight) { ++ Line* pLine = m_LineArray[nMid].get(); ++ if (place.nWordIndex < pLine->m_LineInfo.nBeginWordIndex) { ++ nRight = nMid - 1; ++ nMid = (nLeft + nRight) / 2; ++ } else if (place.nWordIndex > pLine->m_LineInfo.nEndWordIndex) { ++ nLeft = nMid + 1; ++ nMid = (nLeft + nRight) / 2; ++ } else { ++ place.nLineIndex = nMid; ++ return; ++ } ++ } ++} ++ ++CPVT_WordPlace CPVT_Section::SearchWordPlace(const CFX_PointF& point) const { ++ CPVT_WordPlace place = GetBeginWordPlace(); ++ bool bUp = true; ++ bool bDown = true; ++ int32_t nLeft = 0; ++ int32_t nRight = fxcrt::CollectionSize(m_LineArray) - 1; ++ int32_t nMid = fxcrt::CollectionSize(m_LineArray) / 2; ++ while (nLeft <= nRight) { ++ Line* pLine = m_LineArray[nMid].get(); ++ float fTop = pLine->m_LineInfo.fLineY - pLine->m_LineInfo.fLineAscent - ++ m_pVT->GetLineLeading(); ++ float fBottom = pLine->m_LineInfo.fLineY - pLine->m_LineInfo.fLineDescent; ++ if (FXSYS_IsFloatBigger(point.y, fTop)) ++ bUp = false; ++ if (FXSYS_IsFloatSmaller(point.y, fBottom)) ++ bDown = false; ++ if (FXSYS_IsFloatSmaller(point.y, fTop)) { ++ nRight = nMid - 1; ++ nMid = (nLeft + nRight) / 2; ++ continue; ++ } ++ if (FXSYS_IsFloatBigger(point.y, fBottom)) { ++ nLeft = nMid + 1; ++ nMid = (nLeft + nRight) / 2; ++ continue; ++ } ++ place = SearchWordPlace( ++ point.x, ++ CPVT_WordRange(pLine->GetNextWordPlace(pLine->GetBeginWordPlace()), ++ pLine->GetEndWordPlace())); ++ place.nLineIndex = nMid; ++ return place; ++ } ++ if (bUp) ++ place = GetBeginWordPlace(); ++ if (bDown) ++ place = GetEndWordPlace(); ++ return place; ++} ++ ++CPVT_WordPlace CPVT_Section::SearchWordPlace( ++ float fx, ++ const CPVT_WordPlace& lineplace) const { ++ if (!fxcrt::IndexInBounds(m_LineArray, lineplace.nLineIndex)) ++ return GetBeginWordPlace(); ++ ++ Line* pLine = m_LineArray[lineplace.nLineIndex].get(); ++ return SearchWordPlace( ++ fx - m_Rect.left, ++ CPVT_WordRange(pLine->GetNextWordPlace(pLine->GetBeginWordPlace()), ++ pLine->GetEndWordPlace())); ++} ++ ++CPVT_WordPlace CPVT_Section::SearchWordPlace( ++ float fx, ++ const CPVT_WordRange& range) const { ++ CPVT_WordPlace wordplace = range.BeginPos; ++ wordplace.nWordIndex = -1; ++ ++ int32_t nLeft = range.BeginPos.nWordIndex; ++ int32_t nRight = range.EndPos.nWordIndex + 1; ++ int32_t nMid = (nLeft + nRight) / 2; ++ while (nLeft < nRight) { ++ if (nMid == nLeft) ++ break; ++ if (nMid == nRight) { ++ nMid--; ++ break; ++ } ++ if (!fxcrt::IndexInBounds(m_WordArray, nMid)) ++ break; ++ CPVT_WordInfo* pWord = m_WordArray[nMid].get(); ++ if (fx > pWord->fWordX + m_pVT->GetWordWidth(*pWord) * 0.5f) { ++ nLeft = nMid; ++ nMid = (nLeft + nRight) / 2; ++ continue; ++ } ++ nRight = nMid; ++ nMid = (nLeft + nRight) / 2; ++ } ++ if (fxcrt::IndexInBounds(m_WordArray, nMid)) { ++ CPVT_WordInfo* pWord = m_WordArray[nMid].get(); ++ if (fx > pWord->fWordX + m_pVT->GetWordWidth(*pWord) * 0.5f) ++ wordplace.nWordIndex = nMid; ++ } ++ return wordplace; ++} ++ ++int32_t CPVT_Section::GetLineArraySize() const { ++ return fxcrt::CollectionSize(m_LineArray); ++} ++ ++const CPVT_Section::Line* CPVT_Section::GetLineFromArray(int32_t index) const { ++ if (!fxcrt::IndexInBounds(m_LineArray, index)) ++ return nullptr; ++ ++ return m_LineArray[index].get(); ++} ++ ++int32_t CPVT_Section::GetWordArraySize() const { ++ return fxcrt::CollectionSize(m_WordArray); ++} ++ ++const CPVT_WordInfo* CPVT_Section::GetWordFromArray(int32_t index) const { ++ if (!fxcrt::IndexInBounds(m_WordArray, index)) ++ return nullptr; ++ ++ return m_WordArray[index].get(); ++} ++ ++void CPVT_Section::EraseWordsFrom(int32_t index) { ++ if (!fxcrt::IndexInBounds(m_WordArray, index)) ++ return; ++ ++ m_WordArray.erase(m_WordArray.begin() + index, m_WordArray.end()); ++} ++ ++CPVT_FloatRect CPVT_Section::RearrangeCharArray() const { ++ if (m_LineArray.empty()) ++ return CPVT_FloatRect(); + + float fNodeWidth = m_pVT->GetPlateWidth() / + (m_pVT->GetCharArray() <= 0 ? 1 : m_pVT->GetCharArray()); +@@ -192,54 +447,49 @@ CPVT_FloatRect CTypeset::CharArray() { + float x = 0.0f; + float y = m_pVT->GetLineLeading() + fLineAscent; + int32_t nStart = 0; +- CLine* pLine = m_pSection->m_LineArray.front().get(); ++ CPVT_Section::Line* pLine = m_LineArray.front().get(); + switch (m_pVT->GetAlignment()) { + case 0: +- pLine->m_LineInfo.fLineX = fNodeWidth * VARIABLETEXT_HALF; ++ pLine->m_LineInfo.fLineX = fNodeWidth * 0.5f; + break; + case 1: + nStart = (m_pVT->GetCharArray() - +- pdfium::CollectionSize(m_pSection->m_WordArray)) / ++ fxcrt::CollectionSize(m_WordArray)) / + 2; +- pLine->m_LineInfo.fLineX = +- fNodeWidth * nStart - fNodeWidth * VARIABLETEXT_HALF; ++ pLine->m_LineInfo.fLineX = fNodeWidth * nStart - fNodeWidth * 0.5f; + break; + case 2: +- nStart = m_pVT->GetCharArray() - +- pdfium::CollectionSize(m_pSection->m_WordArray); +- pLine->m_LineInfo.fLineX = +- fNodeWidth * nStart - fNodeWidth * VARIABLETEXT_HALF; ++ nStart = ++ m_pVT->GetCharArray() - fxcrt::CollectionSize(m_WordArray); ++ pLine->m_LineInfo.fLineX = fNodeWidth * nStart - fNodeWidth * 0.5f; + break; + } +- for (int32_t w = 0, +- sz = pdfium::CollectionSize(m_pSection->m_WordArray); +- w < sz; w++) { ++ for (int32_t w = 0, sz = fxcrt::CollectionSize(m_WordArray); w < sz; ++ w++) { + if (w >= m_pVT->GetCharArray()) + break; + + float fNextWidth = 0; +- if (pdfium::IndexInBounds(m_pSection->m_WordArray, w + 1)) { +- CPVT_WordInfo* pNextWord = m_pSection->m_WordArray[w + 1].get(); ++ if (fxcrt::IndexInBounds(m_WordArray, w + 1)) { ++ CPVT_WordInfo* pNextWord = m_WordArray[w + 1].get(); + pNextWord->fWordTail = 0; + fNextWidth = m_pVT->GetWordWidth(*pNextWord); + } +- CPVT_WordInfo* pWord = m_pSection->m_WordArray[w].get(); ++ CPVT_WordInfo* pWord = m_WordArray[w].get(); + pWord->fWordTail = 0; + float fWordWidth = m_pVT->GetWordWidth(*pWord); + float fWordAscent = m_pVT->GetWordAscent(*pWord); + float fWordDescent = m_pVT->GetWordDescent(*pWord); +- x = (float)(fNodeWidth * (w + nStart + 0.5) - +- fWordWidth * VARIABLETEXT_HALF); ++ x = (float)(fNodeWidth * (w + nStart + 0.5) - fWordWidth * 0.5f); + pWord->fWordX = x; + pWord->fWordY = y; + if (w == 0) { + pLine->m_LineInfo.fLineX = x; + } +- if (w != pdfium::CollectionSize(m_pSection->m_WordArray) - 1) { +- pWord->fWordTail = +- (fNodeWidth - (fWordWidth + fNextWidth) * VARIABLETEXT_HALF > 0 +- ? fNodeWidth - (fWordWidth + fNextWidth) * VARIABLETEXT_HALF +- : 0); ++ if (w != fxcrt::CollectionSize(m_WordArray) - 1) { ++ pWord->fWordTail = (fNodeWidth - (fWordWidth + fNextWidth) * 0.5f > 0 ++ ? fNodeWidth - (fWordWidth + fNextWidth) * 0.5f ++ : 0); + } else { + pWord->fWordTail = 0; + } +@@ -249,59 +499,42 @@ CPVT_FloatRect CTypeset::CharArray() { + } + pLine->m_LineInfo.nBeginWordIndex = 0; + pLine->m_LineInfo.nEndWordIndex = +- pdfium::CollectionSize(m_pSection->m_WordArray) - 1; ++ fxcrt::CollectionSize(m_WordArray) - 1; + pLine->m_LineInfo.fLineY = y; + pLine->m_LineInfo.fLineWidth = x - pLine->m_LineInfo.fLineX; + pLine->m_LineInfo.fLineAscent = fLineAscent; + pLine->m_LineInfo.fLineDescent = fLineDescent; +- m_rcRet = CPVT_FloatRect(0, 0, x, y - fLineDescent); +- return m_rcRet; +-} +- +-CFX_SizeF CTypeset::GetEditSize(float fFontSize) { +- ASSERT(m_pSection); +- ASSERT(m_pVT); +- SplitLines(false, fFontSize); +- return CFX_SizeF(m_rcRet.Width(), m_rcRet.Height()); ++ return CPVT_FloatRect(0, 0, x, y - fLineDescent); + } + +-CPVT_FloatRect CTypeset::Typeset() { +- ASSERT(m_pVT); +- m_pSection->m_LineArray.clear(); +- SplitLines(true, 0.0f); +- OutputLines(); +- return m_rcRet; ++CPVT_FloatRect CPVT_Section::RearrangeTypeset() { ++ m_LineArray.clear(); ++ return OutputLines(SplitLines(/*bTypeset=*/true, /*fFontSize=*/0.0f)); + } + +-void CTypeset::SplitLines(bool bTypeset, float fFontSize) { +- ASSERT(m_pVT); +- ASSERT(m_pSection); +- ++CPVT_FloatRect CPVT_Section::SplitLines(bool bTypeset, float fFontSize) { + CPVT_LineInfo line; +- if (m_pSection->m_WordArray.empty()) { ++ if (m_WordArray.empty()) { + float fLineAscent; + float fLineDescent; + if (bTypeset) { + fLineAscent = m_pVT->GetLineAscent(); + fLineDescent = m_pVT->GetLineDescent(); +- } else { +- fLineAscent = +- m_pVT->GetFontAscent(m_pVT->GetDefaultFontIndex(), fFontSize); +- fLineDescent = +- m_pVT->GetFontDescent(m_pVT->GetDefaultFontIndex(), fFontSize); +- } +- if (bTypeset) { + line.nBeginWordIndex = -1; + line.nEndWordIndex = -1; + line.nTotalWord = 0; + line.fLineWidth = 0; + line.fLineAscent = fLineAscent; + line.fLineDescent = fLineDescent; +- m_pSection->AddLine(line); ++ AddLine(line); ++ } else { ++ fLineAscent = ++ m_pVT->GetFontAscent(m_pVT->GetDefaultFontIndex(), fFontSize); ++ fLineDescent = ++ m_pVT->GetFontDescent(m_pVT->GetDefaultFontIndex(), fFontSize); + } + float fMaxY = m_pVT->GetLineLeading() + fLineAscent - fLineDescent; +- m_rcRet = CPVT_FloatRect(0, 0, 0, fMaxY); +- return; ++ return CPVT_FloatRect(0, 0, 0, fMaxY); + } + + int32_t nLineHead = 0; +@@ -321,15 +554,14 @@ void CTypeset::SplitLines(bool bTypeset, float fFontSize) { + float fWordWidth = 0; + float fTypesetWidth = + std::max(m_pVT->GetPlateWidth() - m_pVT->GetLineIndent(), 0.0f); +- int32_t nTotalWords = +- pdfium::CollectionSize(m_pSection->m_WordArray); ++ int32_t nTotalWords = fxcrt::CollectionSize(m_WordArray); + bool bOpened = false; + int32_t i = 0; + while (i < nTotalWords) { +- CPVT_WordInfo* pWord = m_pSection->m_WordArray[i].get(); ++ CPVT_WordInfo* pWord = m_WordArray[i].get(); + CPVT_WordInfo* pOldWord = pWord; + if (i > 0) { +- pOldWord = m_pSection->m_WordArray[i - 1].get(); ++ pOldWord = m_WordArray[i - 1].get(); + } + if (pWord) { + if (bTypeset) { +@@ -341,9 +573,9 @@ void CTypeset::SplitLines(bool bTypeset, float fFontSize) { + std::max(fLineAscent, m_pVT->GetWordAscent(*pWord, fFontSize)); + fLineDescent = + std::min(fLineDescent, m_pVT->GetWordDescent(*pWord, fFontSize)); +- fWordWidth = m_pVT->GetWordWidth( +- pWord->nFontIndex, pWord->Word, m_pVT->GetSubWord(), +- m_pVT->GetCharSpace(), fFontSize, pWord->fWordTail); ++ fWordWidth = m_pVT->GetWordWidth(pWord->nFontIndex, pWord->Word, ++ m_pVT->GetSubWord(), fFontSize, ++ pWord->fWordTail); + } + if (!bOpened) { + if (IsOpenStylePunctuation(pWord->Word)) { +@@ -391,7 +623,7 @@ void CTypeset::SplitLines(bool bTypeset, float fFontSize) { + line.fLineWidth = fLineWidth; + line.fLineAscent = fLineAscent; + line.fLineDescent = fLineDescent; +- m_pSection->AddLine(line); ++ AddLine(line); + } + fMaxY += (fLineAscent + m_pVT->GetLineLeading()); + fMaxY -= fLineDescent; +@@ -417,18 +649,16 @@ void CTypeset::SplitLines(bool bTypeset, float fFontSize) { + line.fLineWidth = fLineWidth; + line.fLineAscent = fLineAscent; + line.fLineDescent = fLineDescent; +- m_pSection->AddLine(line); ++ AddLine(line); + } + fMaxY += (fLineAscent + m_pVT->GetLineLeading()); + fMaxY -= fLineDescent; + fMaxX = std::max(fLineWidth, fMaxX); + } +- m_rcRet = CPVT_FloatRect(0, 0, fMaxX, fMaxY); ++ return CPVT_FloatRect(0, 0, fMaxX, fMaxY); + } + +-void CTypeset::OutputLines() { +- ASSERT(m_pVT); +- ASSERT(m_pSection); ++CPVT_FloatRect CPVT_Section::OutputLines(const CPVT_FloatRect& rect) const { + float fMinX; + float fLineIndent = m_pVT->GetLineIndent(); + float fTypesetWidth = std::max(m_pVT->GetPlateWidth() - fLineIndent, 0.0f); +@@ -438,30 +668,28 @@ void CTypeset::OutputLines() { + fMinX = 0.0f; + break; + case 1: +- fMinX = (fTypesetWidth - m_rcRet.Width()) * VARIABLETEXT_HALF; ++ fMinX = (fTypesetWidth - rect.Width()) * 0.5f; + break; + case 2: +- fMinX = fTypesetWidth - m_rcRet.Width(); ++ fMinX = fTypesetWidth - rect.Width(); + break; + } +- float fMaxX = fMinX + m_rcRet.Width(); ++ float fMaxX = fMinX + rect.Width(); + float fMinY = 0.0f; +- float fMaxY = m_rcRet.Height(); +- int32_t nTotalLines = +- pdfium::CollectionSize(m_pSection->m_LineArray); ++ float fMaxY = rect.Height(); ++ int32_t nTotalLines = fxcrt::CollectionSize(m_LineArray); + if (nTotalLines > 0) { + float fPosX = 0.0f; + float fPosY = 0.0f; + for (int32_t l = 0; l < nTotalLines; l++) { +- CLine* pLine = m_pSection->m_LineArray[l].get(); ++ CPVT_Section::Line* pLine = m_LineArray[l].get(); + switch (m_pVT->GetAlignment()) { + default: + case 0: + fPosX = 0; + break; + case 1: +- fPosX = (fTypesetWidth - pLine->m_LineInfo.fLineWidth) * +- VARIABLETEXT_HALF; ++ fPosX = (fTypesetWidth - pLine->m_LineInfo.fLineWidth) * 0.5f; + break; + case 2: + fPosX = fTypesetWidth - pLine->m_LineInfo.fLineWidth; +@@ -474,8 +702,8 @@ void CTypeset::OutputLines() { + pLine->m_LineInfo.fLineY = fPosY - fMinY; + for (int32_t w = pLine->m_LineInfo.nBeginWordIndex; + w <= pLine->m_LineInfo.nEndWordIndex; w++) { +- if (pdfium::IndexInBounds(m_pSection->m_WordArray, w)) { +- CPVT_WordInfo* pWord = m_pSection->m_WordArray[w].get(); ++ if (fxcrt::IndexInBounds(m_WordArray, w)) { ++ CPVT_WordInfo* pWord = m_WordArray[w].get(); + pWord->fWordX = fPosX - fMinX; + pWord->fWordY = fPosY - fMinY; + +@@ -485,5 +713,49 @@ void CTypeset::OutputLines() { + fPosY -= pLine->m_LineInfo.fLineDescent; + } + } +- m_rcRet = CPVT_FloatRect(fMinX, fMinY, fMaxX, fMaxY); ++ return CPVT_FloatRect(fMinX, fMinY, fMaxX, fMaxY); ++} ++ ++void CPVT_Section::ClearLeftWords(int32_t nWordIndex) { ++ for (int32_t i = nWordIndex; i >= 0; i--) { ++ if (fxcrt::IndexInBounds(m_WordArray, i)) ++ m_WordArray.erase(m_WordArray.begin() + i); ++ } ++} ++ ++void CPVT_Section::ClearRightWords(int32_t nWordIndex) { ++ int32_t sz = fxcrt::CollectionSize(m_WordArray); ++ for (int32_t i = sz - 1; i > nWordIndex; i--) { ++ if (fxcrt::IndexInBounds(m_WordArray, i)) ++ m_WordArray.erase(m_WordArray.begin() + i); ++ } ++} ++ ++void CPVT_Section::ClearMidWords(int32_t nBeginIndex, int32_t nEndIndex) { ++ for (int32_t i = nEndIndex; i > nBeginIndex; i--) { ++ if (fxcrt::IndexInBounds(m_WordArray, i)) ++ m_WordArray.erase(m_WordArray.begin() + i); ++ } ++} ++ ++void CPVT_Section::ClearWords(const CPVT_WordRange& PlaceRange) { ++ CPVT_WordPlace SecBeginPos = GetBeginWordPlace(); ++ CPVT_WordPlace SecEndPos = GetEndWordPlace(); ++ if (PlaceRange.BeginPos >= SecBeginPos) { ++ if (PlaceRange.EndPos <= SecEndPos) { ++ ClearMidWords(PlaceRange.BeginPos.nWordIndex, ++ PlaceRange.EndPos.nWordIndex); ++ } else { ++ ClearRightWords(PlaceRange.BeginPos.nWordIndex); ++ } ++ } else if (PlaceRange.EndPos <= SecEndPos) { ++ ClearLeftWords(PlaceRange.EndPos.nWordIndex); ++ } else { ++ m_WordArray.clear(); ++ } ++} ++ ++void CPVT_Section::ClearWord(const CPVT_WordPlace& place) { ++ if (fxcrt::IndexInBounds(m_WordArray, place.nWordIndex)) ++ m_WordArray.erase(m_WordArray.begin() + place.nWordIndex); + } +diff --git a/core/fpdfdoc/cpvt_section.h b/core/fpdfdoc/cpvt_section.h +new file mode 100644 +index 000000000..0f95acbe1 +--- /dev/null ++++ b/core/fpdfdoc/cpvt_section.h +@@ -0,0 +1,90 @@ ++// Copyright 2016 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FPDFDOC_CPVT_SECTION_H_ ++#define CORE_FPDFDOC_CPVT_SECTION_H_ ++ ++#include ++ ++#include ++#include ++ ++#include "core/fpdfdoc/cpvt_floatrect.h" ++#include "core/fpdfdoc/cpvt_lineinfo.h" ++#include "core/fpdfdoc/cpvt_wordinfo.h" ++#include "core/fpdfdoc/cpvt_wordrange.h" ++#include "core/fxcrt/fx_coordinates.h" ++#include "core/fxcrt/unowned_ptr.h" ++ ++class CPVT_VariableText; ++struct CPVT_LineInfo; ++struct CPVT_WordPlace; ++ ++class CPVT_Section final { ++ public: ++ class Line { ++ public: ++ explicit Line(const CPVT_LineInfo& lineinfo); ++ ~Line(); ++ ++ CPVT_WordPlace GetBeginWordPlace() const; ++ CPVT_WordPlace GetEndWordPlace() const; ++ CPVT_WordPlace GetPrevWordPlace(const CPVT_WordPlace& place) const; ++ CPVT_WordPlace GetNextWordPlace(const CPVT_WordPlace& place) const; ++ CPVT_WordPlace m_LinePlace; ++ CPVT_LineInfo m_LineInfo; ++ }; ++ ++ explicit CPVT_Section(CPVT_VariableText* pVT); ++ ~CPVT_Section(); ++ ++ void ResetLinePlace(); ++ CPVT_WordPlace AddWord(const CPVT_WordPlace& place, ++ const CPVT_WordInfo& wordinfo); ++ CPVT_WordPlace AddLine(const CPVT_LineInfo& lineinfo); ++ void ClearWords(const CPVT_WordRange& PlaceRange); ++ void ClearWord(const CPVT_WordPlace& place); ++ CPVT_FloatRect Rearrange(); ++ CFX_SizeF GetSectionSize(float fFontSize); ++ CPVT_WordPlace GetBeginWordPlace() const; ++ CPVT_WordPlace GetEndWordPlace() const; ++ CPVT_WordPlace GetPrevWordPlace(const CPVT_WordPlace& place) const; ++ CPVT_WordPlace GetNextWordPlace(const CPVT_WordPlace& place) const; ++ void UpdateWordPlace(CPVT_WordPlace& place) const; ++ CPVT_WordPlace SearchWordPlace(const CFX_PointF& point) const; ++ CPVT_WordPlace SearchWordPlace(float fx, ++ const CPVT_WordPlace& lineplace) const; ++ CPVT_WordPlace SearchWordPlace(float fx, const CPVT_WordRange& range) const; ++ ++ void SetPlace(const CPVT_WordPlace& place) { m_SecPlace = place; } ++ void SetPlaceIndex(int32_t index) { m_SecPlace.nSecIndex = index; } ++ const CPVT_FloatRect& GetRect() const { return m_Rect; } ++ void SetRect(const CPVT_FloatRect& rect) { m_Rect = rect; } ++ ++ int32_t GetLineArraySize() const; ++ const Line* GetLineFromArray(int32_t index) const; ++ int32_t GetWordArraySize() const; ++ const CPVT_WordInfo* GetWordFromArray(int32_t index) const; ++ void EraseWordsFrom(int32_t index); ++ ++ private: ++ CPVT_FloatRect RearrangeCharArray() const; ++ CPVT_FloatRect RearrangeTypeset(); ++ CPVT_FloatRect SplitLines(bool bTypeset, float fFontSize); ++ CPVT_FloatRect OutputLines(const CPVT_FloatRect& rect) const; ++ ++ void ClearLeftWords(int32_t nWordIndex); ++ void ClearRightWords(int32_t nWordIndex); ++ void ClearMidWords(int32_t nBeginIndex, int32_t nEndIndex); ++ ++ CPVT_WordPlace m_SecPlace; ++ CPVT_FloatRect m_Rect; ++ std::vector> m_LineArray; ++ std::vector> m_WordArray; ++ UnownedPtr const m_pVT; ++}; ++ ++#endif // CORE_FPDFDOC_CPVT_SECTION_H_ +diff --git a/core/fpdfdoc/cpvt_variabletext.cpp b/core/fpdfdoc/cpvt_variabletext.cpp +new file mode 100644 +index 000000000..a13af1cfe +--- /dev/null ++++ b/core/fpdfdoc/cpvt_variabletext.cpp +@@ -0,0 +1,894 @@ ++// Copyright 2016 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fpdfdoc/cpvt_variabletext.h" ++ ++#include ++#include ++ ++#include "core/fpdfapi/font/cpdf_font.h" ++#include "core/fpdfdoc/cpvt_section.h" ++#include "core/fpdfdoc/cpvt_word.h" ++#include "core/fpdfdoc/cpvt_wordinfo.h" ++#include "core/fpdfdoc/ipvt_fontmap.h" ++#include "core/fxcrt/fx_codepage.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/cxx17_backports.h" ++ ++namespace { ++ ++constexpr float kFontScale = 0.001f; ++constexpr uint8_t kReturnLength = 1; ++ ++constexpr uint8_t kFontSizeSteps[] = {4, 6, 8, 9, 10, 12, 14, 18, 20, ++ 25, 30, 35, 40, 45, 50, 55, 60, 70, ++ 80, 90, 100, 110, 120, 130, 144}; ++ ++} // namespace ++ ++CPVT_VariableText::Provider::Provider(IPVT_FontMap* pFontMap) ++ : m_pFontMap(pFontMap) { ++ DCHECK(m_pFontMap); ++} ++ ++CPVT_VariableText::Provider::~Provider() = default; ++ ++int CPVT_VariableText::Provider::GetCharWidth(int32_t nFontIndex, ++ uint16_t word) { ++ RetainPtr pPDFFont = m_pFontMap->GetPDFFont(nFontIndex); ++ if (!pPDFFont) ++ return 0; ++ ++ uint32_t charcode = pPDFFont->CharCodeFromUnicode(word); ++ if (charcode == CPDF_Font::kInvalidCharCode) ++ return 0; ++ ++ return pPDFFont->GetCharWidthF(charcode); ++} ++ ++int32_t CPVT_VariableText::Provider::GetTypeAscent(int32_t nFontIndex) { ++ RetainPtr pPDFFont = m_pFontMap->GetPDFFont(nFontIndex); ++ return pPDFFont ? pPDFFont->GetTypeAscent() : 0; ++} ++ ++int32_t CPVT_VariableText::Provider::GetTypeDescent(int32_t nFontIndex) { ++ RetainPtr pPDFFont = m_pFontMap->GetPDFFont(nFontIndex); ++ return pPDFFont ? pPDFFont->GetTypeDescent() : 0; ++} ++ ++int32_t CPVT_VariableText::Provider::GetWordFontIndex(uint16_t word, ++ FX_Charset charset, ++ int32_t nFontIndex) { ++ if (RetainPtr pDefFont = m_pFontMap->GetPDFFont(0)) { ++ if (pDefFont->CharCodeFromUnicode(word) != CPDF_Font::kInvalidCharCode) ++ return 0; ++ } ++ if (RetainPtr pSysFont = m_pFontMap->GetPDFFont(1)) { ++ if (pSysFont->CharCodeFromUnicode(word) != CPDF_Font::kInvalidCharCode) ++ return 1; ++ } ++ return -1; ++} ++ ++int32_t CPVT_VariableText::Provider::GetDefaultFontIndex() { ++ return 0; ++} ++ ++CPVT_VariableText::Iterator::Iterator(CPVT_VariableText* pVT) : m_pVT(pVT) { ++ DCHECK(m_pVT); ++} ++ ++CPVT_VariableText::Iterator::~Iterator() = default; ++ ++void CPVT_VariableText::Iterator::SetAt(int32_t nWordIndex) { ++ m_CurPos = m_pVT->WordIndexToWordPlace(nWordIndex); ++} ++ ++void CPVT_VariableText::Iterator::SetAt(const CPVT_WordPlace& place) { ++ m_CurPos = place; ++} ++ ++bool CPVT_VariableText::Iterator::NextWord() { ++ if (m_CurPos == m_pVT->GetEndWordPlace()) ++ return false; ++ ++ m_CurPos = m_pVT->GetNextWordPlace(m_CurPos); ++ return true; ++} ++ ++bool CPVT_VariableText::Iterator::NextLine() { ++ if (!fxcrt::IndexInBounds(m_pVT->m_SectionArray, m_CurPos.nSecIndex)) ++ return false; ++ ++ CPVT_Section* pSection = m_pVT->m_SectionArray[m_CurPos.nSecIndex].get(); ++ if (m_CurPos.nLineIndex < pSection->GetLineArraySize() - 1) { ++ m_CurPos = CPVT_WordPlace(m_CurPos.nSecIndex, m_CurPos.nLineIndex + 1, -1); ++ return true; ++ } ++ if (m_CurPos.nSecIndex < ++ fxcrt::CollectionSize(m_pVT->m_SectionArray) - 1) { ++ m_CurPos = CPVT_WordPlace(m_CurPos.nSecIndex + 1, 0, -1); ++ return true; ++ } ++ return false; ++} ++ ++bool CPVT_VariableText::Iterator::GetWord(CPVT_Word& word) const { ++ word.WordPlace = m_CurPos; ++ if (!fxcrt::IndexInBounds(m_pVT->m_SectionArray, m_CurPos.nSecIndex)) ++ return false; ++ ++ CPVT_Section* pSection = m_pVT->m_SectionArray[m_CurPos.nSecIndex].get(); ++ if (!pSection->GetLineFromArray(m_CurPos.nLineIndex)) ++ return false; ++ ++ const CPVT_WordInfo* pInfo = pSection->GetWordFromArray(m_CurPos.nWordIndex); ++ if (!pInfo) ++ return false; ++ ++ word.Word = pInfo->Word; ++ word.nCharset = pInfo->nCharset; ++ word.fWidth = m_pVT->GetWordWidth(*pInfo); ++ word.ptWord = ++ m_pVT->InToOut(CFX_PointF(pInfo->fWordX + pSection->GetRect().left, ++ pInfo->fWordY + pSection->GetRect().top)); ++ word.fAscent = m_pVT->GetWordAscent(*pInfo); ++ word.fDescent = m_pVT->GetWordDescent(*pInfo); ++ word.nFontIndex = pInfo->nFontIndex; ++ word.fFontSize = m_pVT->GetWordFontSize(); ++ return true; ++} ++ ++bool CPVT_VariableText::Iterator::GetLine(CPVT_Line& line) const { ++ DCHECK(m_pVT); ++ line.lineplace = CPVT_WordPlace(m_CurPos.nSecIndex, m_CurPos.nLineIndex, -1); ++ if (!fxcrt::IndexInBounds(m_pVT->m_SectionArray, m_CurPos.nSecIndex)) ++ return false; ++ ++ CPVT_Section* pSection = m_pVT->m_SectionArray[m_CurPos.nSecIndex].get(); ++ const CPVT_Section::Line* pLine = ++ pSection->GetLineFromArray(m_CurPos.nLineIndex); ++ if (!pLine) ++ return false; ++ ++ line.ptLine = m_pVT->InToOut( ++ CFX_PointF(pLine->m_LineInfo.fLineX + pSection->GetRect().left, ++ pLine->m_LineInfo.fLineY + pSection->GetRect().top)); ++ line.fLineWidth = pLine->m_LineInfo.fLineWidth; ++ line.fLineAscent = pLine->m_LineInfo.fLineAscent; ++ line.fLineDescent = pLine->m_LineInfo.fLineDescent; ++ line.lineEnd = pLine->GetEndWordPlace(); ++ return true; ++} ++ ++CPVT_VariableText::CPVT_VariableText(Provider* pProvider) ++ : m_pVTProvider(pProvider) {} ++ ++CPVT_VariableText::~CPVT_VariableText() = default; ++ ++void CPVT_VariableText::Initialize() { ++ if (m_bInitialized) ++ return; ++ ++ CPVT_WordPlace place; ++ place.nSecIndex = 0; ++ AddSection(place); ++ ++ CPVT_LineInfo lineinfo; ++ lineinfo.fLineAscent = GetFontAscent(GetDefaultFontIndex(), GetFontSize()); ++ lineinfo.fLineDescent = GetFontDescent(GetDefaultFontIndex(), GetFontSize()); ++ AddLine(place, lineinfo); ++ ++ if (!m_SectionArray.empty()) ++ m_SectionArray.front()->ResetLinePlace(); ++ ++ m_bInitialized = true; ++} ++ ++CPVT_WordPlace CPVT_VariableText::InsertWord(const CPVT_WordPlace& place, ++ uint16_t word, ++ FX_Charset charset) { ++ int32_t nTotalWords = GetTotalWords(); ++ if (m_nLimitChar > 0 && nTotalWords >= m_nLimitChar) ++ return place; ++ if (m_nCharArray > 0 && nTotalWords >= m_nCharArray) ++ return place; ++ ++ CPVT_WordPlace newplace = place; ++ newplace.nWordIndex++; ++ int32_t nFontIndex = ++ GetSubWord() > 0 ? GetDefaultFontIndex() ++ : GetWordFontIndex(word, charset, GetDefaultFontIndex()); ++ return AddWord(newplace, CPVT_WordInfo(word, charset, nFontIndex)); ++} ++ ++CPVT_WordPlace CPVT_VariableText::InsertSection(const CPVT_WordPlace& place) { ++ int32_t nTotalWords = GetTotalWords(); ++ if (m_nLimitChar > 0 && nTotalWords >= m_nLimitChar) ++ return place; ++ if (m_nCharArray > 0 && nTotalWords >= m_nCharArray) ++ return place; ++ if (!m_bMultiLine) ++ return place; ++ ++ CPVT_WordPlace wordplace = place; ++ UpdateWordPlace(wordplace); ++ if (!fxcrt::IndexInBounds(m_SectionArray, wordplace.nSecIndex)) ++ return place; ++ ++ CPVT_Section* pSection = m_SectionArray[wordplace.nSecIndex].get(); ++ CPVT_WordPlace NewPlace(wordplace.nSecIndex + 1, 0, -1); ++ AddSection(NewPlace); ++ CPVT_WordPlace result = NewPlace; ++ if (fxcrt::IndexInBounds(m_SectionArray, NewPlace.nSecIndex)) { ++ CPVT_Section* pNewSection = m_SectionArray[NewPlace.nSecIndex].get(); ++ for (int32_t w = wordplace.nWordIndex + 1; w < pSection->GetWordArraySize(); ++ ++w) { ++ NewPlace.nWordIndex++; ++ pNewSection->AddWord(NewPlace, *pSection->GetWordFromArray(w)); ++ } ++ } ++ ClearSectionRightWords(wordplace); ++ return result; ++} ++ ++CPVT_WordPlace CPVT_VariableText::DeleteWords( ++ const CPVT_WordRange& PlaceRange) { ++ bool bLastSecPos = ++ fxcrt::IndexInBounds(m_SectionArray, PlaceRange.EndPos.nSecIndex) && ++ PlaceRange.EndPos == ++ m_SectionArray[PlaceRange.EndPos.nSecIndex]->GetEndWordPlace(); ++ ++ ClearWords(PlaceRange); ++ if (PlaceRange.BeginPos.nSecIndex != PlaceRange.EndPos.nSecIndex) { ++ ClearEmptySections(PlaceRange); ++ if (!bLastSecPos) ++ LinkLatterSection(PlaceRange.BeginPos); ++ } ++ return PlaceRange.BeginPos; ++} ++ ++CPVT_WordPlace CPVT_VariableText::DeleteWord(const CPVT_WordPlace& place) { ++ return ClearRightWord(PrevLineHeaderPlace(place)); ++} ++ ++CPVT_WordPlace CPVT_VariableText::BackSpaceWord(const CPVT_WordPlace& place) { ++ return ClearLeftWord(PrevLineHeaderPlace(place)); ++} ++ ++void CPVT_VariableText::SetText(const WideString& swText) { ++ DeleteWords(CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace())); ++ CPVT_WordPlace wp(0, 0, -1); ++ if (!m_SectionArray.empty()) ++ m_SectionArray.front()->SetRect(CPVT_FloatRect()); ++ ++ FX_SAFE_INT32 nCharCount = 0; ++ for (size_t i = 0, sz = swText.GetLength(); i < sz; i++) { ++ if (m_nLimitChar > 0 && nCharCount.ValueOrDie() >= m_nLimitChar) ++ break; ++ if (m_nCharArray > 0 && nCharCount.ValueOrDie() >= m_nCharArray) ++ break; ++ ++ uint16_t word = swText[i]; ++ switch (word) { ++ case 0x0D: ++ if (m_bMultiLine) { ++ if (i + 1 < sz && swText[i + 1] == 0x0A) ++ i++; ++ wp.AdvanceSection(); ++ AddSection(wp); ++ } ++ break; ++ case 0x0A: ++ if (m_bMultiLine) { ++ if (i + 1 < sz && swText[i + 1] == 0x0D) ++ i++; ++ wp.AdvanceSection(); ++ AddSection(wp); ++ } ++ break; ++ case 0x09: ++ word = 0x20; ++ [[fallthrough]]; ++ default: ++ wp = InsertWord(wp, word, FX_Charset::kDefault); ++ break; ++ } ++ nCharCount++; ++ } ++} ++ ++void CPVT_VariableText::UpdateWordPlace(CPVT_WordPlace& place) const { ++ if (place.nSecIndex < 0) ++ place = GetBeginWordPlace(); ++ if (static_cast(place.nSecIndex) >= m_SectionArray.size()) ++ place = GetEndWordPlace(); ++ ++ place = PrevLineHeaderPlace(place); ++ if (fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex)) ++ m_SectionArray[place.nSecIndex]->UpdateWordPlace(place); ++} ++ ++int32_t CPVT_VariableText::WordPlaceToWordIndex( ++ const CPVT_WordPlace& place) const { ++ CPVT_WordPlace newplace = place; ++ UpdateWordPlace(newplace); ++ int32_t nIndex = 0; ++ int32_t i = 0; ++ int32_t sz = 0; ++ for (i = 0, sz = fxcrt::CollectionSize(m_SectionArray); ++ i < sz && i < newplace.nSecIndex; i++) { ++ CPVT_Section* pSection = m_SectionArray[i].get(); ++ nIndex += pSection->GetWordArraySize(); ++ if (i != sz - 1) ++ nIndex += kReturnLength; ++ } ++ if (fxcrt::IndexInBounds(m_SectionArray, i)) ++ nIndex += newplace.nWordIndex + kReturnLength; ++ return nIndex; ++} ++ ++CPVT_WordPlace CPVT_VariableText::WordIndexToWordPlace(int32_t index) const { ++ CPVT_WordPlace place = GetBeginWordPlace(); ++ int32_t nOldIndex = 0; ++ int32_t nIndex = 0; ++ bool bFound = false; ++ for (size_t i = 0; i < m_SectionArray.size(); ++i) { ++ CPVT_Section* pSection = m_SectionArray[i].get(); ++ nIndex += pSection->GetWordArraySize(); ++ if (nIndex == index) { ++ place = pSection->GetEndWordPlace(); ++ bFound = true; ++ break; ++ } ++ if (nIndex > index) { ++ place.nSecIndex = pdfium::base::checked_cast(i); ++ place.nWordIndex = index - nOldIndex - 1; ++ pSection->UpdateWordPlace(place); ++ bFound = true; ++ break; ++ } ++ if (i != m_SectionArray.size() - 1) ++ nIndex += kReturnLength; ++ nOldIndex = nIndex; ++ } ++ if (!bFound) ++ place = GetEndWordPlace(); ++ return place; ++} ++ ++CPVT_WordPlace CPVT_VariableText::GetBeginWordPlace() const { ++ return m_bInitialized ? CPVT_WordPlace(0, 0, -1) : CPVT_WordPlace(); ++} ++ ++CPVT_WordPlace CPVT_VariableText::GetEndWordPlace() const { ++ if (m_SectionArray.empty()) ++ return CPVT_WordPlace(); ++ return m_SectionArray.back()->GetEndWordPlace(); ++} ++ ++CPVT_WordPlace CPVT_VariableText::GetPrevWordPlace( ++ const CPVT_WordPlace& place) const { ++ if (place.nSecIndex < 0) ++ return GetBeginWordPlace(); ++ if (static_cast(place.nSecIndex) >= m_SectionArray.size()) ++ return GetEndWordPlace(); ++ ++ CPVT_Section* pSection = m_SectionArray[place.nSecIndex].get(); ++ if (place > pSection->GetBeginWordPlace()) ++ return pSection->GetPrevWordPlace(place); ++ if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex - 1)) ++ return GetBeginWordPlace(); ++ return m_SectionArray[place.nSecIndex - 1]->GetEndWordPlace(); ++} ++ ++CPVT_WordPlace CPVT_VariableText::GetNextWordPlace( ++ const CPVT_WordPlace& place) const { ++ if (place.nSecIndex < 0) ++ return GetBeginWordPlace(); ++ if (static_cast(place.nSecIndex) >= m_SectionArray.size()) ++ return GetEndWordPlace(); ++ ++ CPVT_Section* pSection = m_SectionArray[place.nSecIndex].get(); ++ if (place < pSection->GetEndWordPlace()) ++ return pSection->GetNextWordPlace(place); ++ if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex + 1)) ++ return GetEndWordPlace(); ++ return m_SectionArray[place.nSecIndex + 1]->GetBeginWordPlace(); ++} ++ ++CPVT_WordPlace CPVT_VariableText::SearchWordPlace( ++ const CFX_PointF& point) const { ++ CFX_PointF pt = OutToIn(point); ++ CPVT_WordPlace place = GetBeginWordPlace(); ++ int32_t nLeft = 0; ++ int32_t nRight = fxcrt::CollectionSize(m_SectionArray) - 1; ++ int32_t nMid = fxcrt::CollectionSize(m_SectionArray) / 2; ++ bool bUp = true; ++ bool bDown = true; ++ while (nLeft <= nRight) { ++ if (!fxcrt::IndexInBounds(m_SectionArray, nMid)) ++ break; ++ CPVT_Section* pSection = m_SectionArray[nMid].get(); ++ if (FXSYS_IsFloatBigger(pt.y, pSection->GetRect().top)) ++ bUp = false; ++ if (FXSYS_IsFloatBigger(pSection->GetRect().bottom, pt.y)) ++ bDown = false; ++ if (FXSYS_IsFloatSmaller(pt.y, pSection->GetRect().top)) { ++ nRight = nMid - 1; ++ nMid = (nLeft + nRight) / 2; ++ continue; ++ } ++ if (FXSYS_IsFloatBigger(pt.y, pSection->GetRect().bottom)) { ++ nLeft = nMid + 1; ++ nMid = (nLeft + nRight) / 2; ++ continue; ++ } ++ place = pSection->SearchWordPlace(CFX_PointF( ++ pt.x - pSection->GetRect().left, pt.y - pSection->GetRect().top)); ++ place.nSecIndex = nMid; ++ return place; ++ } ++ if (bUp) ++ place = GetBeginWordPlace(); ++ if (bDown) ++ place = GetEndWordPlace(); ++ return place; ++} ++ ++CPVT_WordPlace CPVT_VariableText::GetUpWordPlace( ++ const CPVT_WordPlace& place, ++ const CFX_PointF& point) const { ++ if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex)) ++ return place; ++ ++ CPVT_Section* pSection = m_SectionArray[place.nSecIndex].get(); ++ CPVT_WordPlace temp = place; ++ CFX_PointF pt = OutToIn(point); ++ if (temp.nLineIndex-- > 0) { ++ return pSection->SearchWordPlace(pt.x - pSection->GetRect().left, temp); ++ } ++ if (temp.nSecIndex-- > 0) { ++ if (fxcrt::IndexInBounds(m_SectionArray, temp.nSecIndex)) { ++ CPVT_Section* pLastSection = m_SectionArray[temp.nSecIndex].get(); ++ temp.nLineIndex = pLastSection->GetLineArraySize() - 1; ++ return pLastSection->SearchWordPlace(pt.x - pLastSection->GetRect().left, ++ temp); ++ } ++ } ++ return place; ++} ++ ++CPVT_WordPlace CPVT_VariableText::GetDownWordPlace( ++ const CPVT_WordPlace& place, ++ const CFX_PointF& point) const { ++ if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex)) ++ return place; ++ ++ CPVT_Section* pSection = m_SectionArray[place.nSecIndex].get(); ++ CPVT_WordPlace temp = place; ++ CFX_PointF pt = OutToIn(point); ++ if (temp.nLineIndex++ < pSection->GetLineArraySize() - 1) { ++ return pSection->SearchWordPlace(pt.x - pSection->GetRect().left, temp); ++ } ++ temp.AdvanceSection(); ++ if (!fxcrt::IndexInBounds(m_SectionArray, temp.nSecIndex)) ++ return place; ++ ++ return m_SectionArray[temp.nSecIndex]->SearchWordPlace( ++ pt.x - pSection->GetRect().left, temp); ++} ++ ++CPVT_WordPlace CPVT_VariableText::GetLineBeginPlace( ++ const CPVT_WordPlace& place) const { ++ return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, -1); ++} ++ ++CPVT_WordPlace CPVT_VariableText::GetLineEndPlace( ++ const CPVT_WordPlace& place) const { ++ if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex)) ++ return place; ++ ++ CPVT_Section* pSection = m_SectionArray[place.nSecIndex].get(); ++ const CPVT_Section::Line* pLine = ++ pSection->GetLineFromArray(place.nLineIndex); ++ if (!pLine) ++ return place; ++ ++ return pLine->GetEndWordPlace(); ++} ++ ++CPVT_WordPlace CPVT_VariableText::GetSectionBeginPlace( ++ const CPVT_WordPlace& place) const { ++ return CPVT_WordPlace(place.nSecIndex, 0, -1); ++} ++ ++CPVT_WordPlace CPVT_VariableText::GetSectionEndPlace( ++ const CPVT_WordPlace& place) const { ++ if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex)) ++ return place; ++ ++ return m_SectionArray[place.nSecIndex]->GetEndWordPlace(); ++} ++ ++int32_t CPVT_VariableText::GetTotalWords() const { ++ int32_t nTotal = 0; ++ for (const auto& pSection : m_SectionArray) { ++ nTotal += pSection->GetWordArraySize() + kReturnLength; ++ } ++ return nTotal - kReturnLength; ++} ++ ++CPVT_WordPlace CPVT_VariableText::AddSection(const CPVT_WordPlace& place) { ++ if (IsValid() && !m_bMultiLine) ++ return place; ++ ++ int32_t nSecIndex = pdfium::clamp( ++ place.nSecIndex, 0, fxcrt::CollectionSize(m_SectionArray)); ++ ++ auto pSection = std::make_unique(this); ++ pSection->SetRect(CPVT_FloatRect()); ++ pSection->SetPlaceIndex(nSecIndex); ++ m_SectionArray.insert(m_SectionArray.begin() + nSecIndex, ++ std::move(pSection)); ++ return place; ++} ++ ++CPVT_WordPlace CPVT_VariableText::AddLine(const CPVT_WordPlace& place, ++ const CPVT_LineInfo& lineinfo) { ++ if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex)) ++ return place; ++ ++ return m_SectionArray[place.nSecIndex]->AddLine(lineinfo); ++} ++ ++CPVT_WordPlace CPVT_VariableText::AddWord(const CPVT_WordPlace& place, ++ const CPVT_WordInfo& wordinfo) { ++ if (m_SectionArray.empty()) ++ return place; ++ ++ CPVT_WordPlace newplace = place; ++ newplace.nSecIndex = ++ pdfium::clamp(newplace.nSecIndex, 0, ++ fxcrt::CollectionSize(m_SectionArray) - 1); ++ return m_SectionArray[newplace.nSecIndex]->AddWord(newplace, wordinfo); ++} ++ ++void CPVT_VariableText::SetPlateRect(const CFX_FloatRect& rect) { ++ m_rcPlate = rect; ++} ++ ++CFX_FloatRect CPVT_VariableText::GetContentRect() const { ++ return InToOut(m_rcContent); ++} ++ ++const CFX_FloatRect& CPVT_VariableText::GetPlateRect() const { ++ return m_rcPlate; ++} ++ ++float CPVT_VariableText::GetWordFontSize() const { ++ return GetFontSize(); ++} ++ ++float CPVT_VariableText::GetWordWidth(int32_t nFontIndex, ++ uint16_t Word, ++ uint16_t SubWord, ++ float fFontSize, ++ float fWordTail) const { ++ return GetCharWidth(nFontIndex, Word, SubWord) * fFontSize * kFontScale + ++ fWordTail; ++} ++ ++float CPVT_VariableText::GetWordWidth(const CPVT_WordInfo& WordInfo) const { ++ return GetWordWidth(WordInfo.nFontIndex, WordInfo.Word, GetSubWord(), ++ GetWordFontSize(), WordInfo.fWordTail); ++} ++ ++float CPVT_VariableText::GetLineAscent() { ++ return GetFontAscent(GetDefaultFontIndex(), GetFontSize()); ++} ++ ++float CPVT_VariableText::GetLineDescent() { ++ return GetFontDescent(GetDefaultFontIndex(), GetFontSize()); ++} ++ ++float CPVT_VariableText::GetFontAscent(int32_t nFontIndex, ++ float fFontSize) const { ++ float ascent = m_pVTProvider ? m_pVTProvider->GetTypeAscent(nFontIndex) : 0; ++ return ascent * fFontSize * kFontScale; ++} ++ ++float CPVT_VariableText::GetFontDescent(int32_t nFontIndex, ++ float fFontSize) const { ++ float descent = m_pVTProvider ? m_pVTProvider->GetTypeDescent(nFontIndex) : 0; ++ return descent * fFontSize * kFontScale; ++} ++ ++float CPVT_VariableText::GetWordAscent(const CPVT_WordInfo& WordInfo, ++ float fFontSize) const { ++ return GetFontAscent(WordInfo.nFontIndex, fFontSize); ++} ++ ++float CPVT_VariableText::GetWordDescent(const CPVT_WordInfo& WordInfo, ++ float fFontSize) const { ++ return GetFontDescent(WordInfo.nFontIndex, fFontSize); ++} ++ ++float CPVT_VariableText::GetWordAscent(const CPVT_WordInfo& WordInfo) const { ++ return GetFontAscent(WordInfo.nFontIndex, GetWordFontSize()); ++} ++ ++float CPVT_VariableText::GetWordDescent(const CPVT_WordInfo& WordInfo) const { ++ return GetFontDescent(WordInfo.nFontIndex, GetWordFontSize()); ++} ++ ++float CPVT_VariableText::GetLineLeading() { ++ return m_fLineLeading; ++} ++ ++float CPVT_VariableText::GetLineIndent() { ++ return 0.0f; ++} ++ ++void CPVT_VariableText::ClearSectionRightWords(const CPVT_WordPlace& place) { ++ CPVT_WordPlace wordplace = PrevLineHeaderPlace(place); ++ if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex)) ++ return; ++ ++ CPVT_Section* pSection = m_SectionArray[place.nSecIndex].get(); ++ pSection->EraseWordsFrom(wordplace.nWordIndex + 1); ++} ++ ++CPVT_WordPlace CPVT_VariableText::PrevLineHeaderPlace( ++ const CPVT_WordPlace& place) const { ++ if (place.nWordIndex < 0 && place.nLineIndex > 0) ++ return GetPrevWordPlace(place); ++ return place; ++} ++ ++CPVT_WordPlace CPVT_VariableText::NextLineHeaderPlace( ++ const CPVT_WordPlace& place) const { ++ if (place.nWordIndex < 0 && place.nLineIndex > 0) ++ return GetNextWordPlace(place); ++ return place; ++} ++ ++bool CPVT_VariableText::ClearEmptySection(const CPVT_WordPlace& place) { ++ if (place.nSecIndex == 0 && m_SectionArray.size() == 1) ++ return false; ++ ++ if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex)) ++ return false; ++ ++ if (m_SectionArray[place.nSecIndex]->GetWordArraySize() != 0) ++ return false; ++ ++ m_SectionArray.erase(m_SectionArray.begin() + place.nSecIndex); ++ return true; ++} ++ ++void CPVT_VariableText::ClearEmptySections(const CPVT_WordRange& PlaceRange) { ++ CPVT_WordPlace wordplace; ++ for (int32_t s = PlaceRange.EndPos.nSecIndex; ++ s > PlaceRange.BeginPos.nSecIndex; s--) { ++ wordplace.nSecIndex = s; ++ ClearEmptySection(wordplace); ++ } ++} ++ ++void CPVT_VariableText::LinkLatterSection(const CPVT_WordPlace& place) { ++ CPVT_WordPlace oldplace = PrevLineHeaderPlace(place); ++ if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex + 1)) ++ return; ++ ++ CPVT_Section* pNextSection = m_SectionArray[place.nSecIndex + 1].get(); ++ if (fxcrt::IndexInBounds(m_SectionArray, oldplace.nSecIndex)) { ++ CPVT_Section* pSection = m_SectionArray[oldplace.nSecIndex].get(); ++ for (int32_t i = 0; i < pNextSection->GetWordArraySize(); ++i) { ++ oldplace.nWordIndex++; ++ pSection->AddWord(oldplace, *pNextSection->GetWordFromArray(i)); ++ } ++ } ++ m_SectionArray.erase(m_SectionArray.begin() + place.nSecIndex + 1); ++} ++ ++void CPVT_VariableText::ClearWords(const CPVT_WordRange& PlaceRange) { ++ CPVT_WordRange NewRange; ++ NewRange.BeginPos = PrevLineHeaderPlace(PlaceRange.BeginPos); ++ NewRange.EndPos = PrevLineHeaderPlace(PlaceRange.EndPos); ++ for (int32_t s = NewRange.EndPos.nSecIndex; s >= NewRange.BeginPos.nSecIndex; ++ s--) { ++ if (fxcrt::IndexInBounds(m_SectionArray, s)) ++ m_SectionArray[s]->ClearWords(NewRange); ++ } ++} ++ ++CPVT_WordPlace CPVT_VariableText::ClearLeftWord(const CPVT_WordPlace& place) { ++ if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex)) ++ return place; ++ ++ CPVT_Section* pSection = m_SectionArray[place.nSecIndex].get(); ++ CPVT_WordPlace leftplace = GetPrevWordPlace(place); ++ if (leftplace == place) ++ return place; ++ ++ if (leftplace.nSecIndex != place.nSecIndex) { ++ if (pSection->GetWordArraySize() == 0) ++ ClearEmptySection(place); ++ else ++ LinkLatterSection(leftplace); ++ } else { ++ pSection->ClearWord(place); ++ } ++ return leftplace; ++} ++ ++CPVT_WordPlace CPVT_VariableText::ClearRightWord(const CPVT_WordPlace& place) { ++ if (!fxcrt::IndexInBounds(m_SectionArray, place.nSecIndex)) ++ return place; ++ ++ CPVT_Section* pSection = m_SectionArray[place.nSecIndex].get(); ++ CPVT_WordPlace rightplace = NextLineHeaderPlace(GetNextWordPlace(place)); ++ if (rightplace == place) ++ return place; ++ ++ if (rightplace.nSecIndex != place.nSecIndex) ++ LinkLatterSection(place); ++ else ++ pSection->ClearWord(rightplace); ++ return place; ++} ++ ++void CPVT_VariableText::RearrangeAll() { ++ Rearrange(CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace())); ++} ++ ++void CPVT_VariableText::RearrangePart(const CPVT_WordRange& PlaceRange) { ++ Rearrange(PlaceRange); ++} ++ ++void CPVT_VariableText::Rearrange(const CPVT_WordRange& PlaceRange) { ++ CPVT_FloatRect rcRet; ++ if (IsValid()) { ++ if (m_bAutoFontSize) { ++ SetFontSize(GetAutoFontSize()); ++ rcRet = RearrangeSections( ++ CPVT_WordRange(GetBeginWordPlace(), GetEndWordPlace())); ++ } else { ++ rcRet = RearrangeSections(PlaceRange); ++ } ++ } ++ m_rcContent = rcRet; ++} ++ ++float CPVT_VariableText::GetAutoFontSize() { ++ int32_t nTotal = sizeof(kFontSizeSteps) / sizeof(uint8_t); ++ if (IsMultiLine()) ++ nTotal /= 4; ++ if (nTotal <= 0) ++ return 0; ++ if (GetPlateWidth() <= 0) ++ return 0; ++ ++ int32_t nLeft = 0; ++ int32_t nRight = nTotal - 1; ++ int32_t nMid = nTotal / 2; ++ while (nLeft <= nRight) { ++ if (IsBigger(kFontSizeSteps[nMid])) ++ nRight = nMid - 1; ++ else ++ nLeft = nMid + 1; ++ nMid = (nLeft + nRight) / 2; ++ } ++ return (float)kFontSizeSteps[nMid]; ++} ++ ++bool CPVT_VariableText::IsBigger(float fFontSize) const { ++ CFX_SizeF szTotal; ++ for (const auto& pSection : m_SectionArray) { ++ CFX_SizeF size = pSection->GetSectionSize(fFontSize); ++ szTotal.width = std::max(size.width, szTotal.width); ++ szTotal.height += size.height; ++ if (FXSYS_IsFloatBigger(szTotal.width, GetPlateWidth()) || ++ FXSYS_IsFloatBigger(szTotal.height, GetPlateHeight())) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++CPVT_FloatRect CPVT_VariableText::RearrangeSections( ++ const CPVT_WordRange& PlaceRange) { ++ float fPosY = 0; ++ CPVT_FloatRect rcRet; ++ for (int32_t s = 0, sz = fxcrt::CollectionSize(m_SectionArray); ++ s < sz; s++) { ++ CPVT_WordPlace place; ++ place.nSecIndex = s; ++ CPVT_Section* pSection = m_SectionArray[s].get(); ++ pSection->SetPlace(place); ++ CPVT_FloatRect rcSec = pSection->GetRect(); ++ if (s >= PlaceRange.BeginPos.nSecIndex) { ++ if (s <= PlaceRange.EndPos.nSecIndex) { ++ rcSec = pSection->Rearrange(); ++ rcSec.top += fPosY; ++ rcSec.bottom += fPosY; ++ } else { ++ float fOldHeight = pSection->GetRect().bottom - pSection->GetRect().top; ++ rcSec.top = fPosY; ++ rcSec.bottom = fPosY + fOldHeight; ++ } ++ pSection->SetRect(rcSec); ++ pSection->ResetLinePlace(); ++ } ++ if (s == 0) { ++ rcRet = rcSec; ++ } else { ++ rcRet.left = std::min(rcSec.left, rcRet.left); ++ rcRet.top = std::min(rcSec.top, rcRet.top); ++ rcRet.right = std::max(rcSec.right, rcRet.right); ++ rcRet.bottom = std::max(rcSec.bottom, rcRet.bottom); ++ } ++ fPosY += rcSec.Height(); ++ } ++ return rcRet; ++} ++ ++int CPVT_VariableText::GetCharWidth(int32_t nFontIndex, ++ uint16_t Word, ++ uint16_t SubWord) const { ++ if (!m_pVTProvider) ++ return 0; ++ uint16_t word = SubWord ? SubWord : Word; ++ return m_pVTProvider->GetCharWidth(nFontIndex, word); ++} ++ ++int32_t CPVT_VariableText::GetWordFontIndex(uint16_t word, ++ FX_Charset charset, ++ int32_t nFontIndex) { ++ return m_pVTProvider ++ ? m_pVTProvider->GetWordFontIndex(word, charset, nFontIndex) ++ : -1; ++} ++ ++int32_t CPVT_VariableText::GetDefaultFontIndex() { ++ return m_pVTProvider ? m_pVTProvider->GetDefaultFontIndex() : -1; ++} ++ ++CPVT_VariableText::Iterator* CPVT_VariableText::GetIterator() { ++ if (!m_pVTIterator) ++ m_pVTIterator = std::make_unique(this); ++ return m_pVTIterator.get(); ++} ++ ++void CPVT_VariableText::SetProvider(Provider* pProvider) { ++ m_pVTProvider = pProvider; ++} ++ ++CFX_PointF CPVT_VariableText::GetBTPoint() const { ++ return CFX_PointF(m_rcPlate.left, m_rcPlate.top); ++} ++ ++CFX_PointF CPVT_VariableText::GetETPoint() const { ++ return CFX_PointF(m_rcPlate.right, m_rcPlate.bottom); ++} ++ ++CFX_PointF CPVT_VariableText::InToOut(const CFX_PointF& point) const { ++ return CFX_PointF(point.x + GetBTPoint().x, GetBTPoint().y - point.y); ++} ++ ++CFX_PointF CPVT_VariableText::OutToIn(const CFX_PointF& point) const { ++ return CFX_PointF(point.x - GetBTPoint().x, GetBTPoint().y - point.y); ++} ++ ++CFX_FloatRect CPVT_VariableText::InToOut(const CPVT_FloatRect& rect) const { ++ CFX_PointF ptLeftTop = InToOut(CFX_PointF(rect.left, rect.top)); ++ CFX_PointF ptRightBottom = InToOut(CFX_PointF(rect.right, rect.bottom)); ++ return CFX_FloatRect(ptLeftTop.x, ptRightBottom.y, ptRightBottom.x, ++ ptLeftTop.y); ++} +diff --git a/core/fpdfdoc/cpdf_variabletext.h b/core/fpdfdoc/cpvt_variabletext.h +similarity index 73% +rename from core/fpdfdoc/cpdf_variabletext.h +rename to core/fpdfdoc/cpvt_variabletext.h +index aa4701551..a378dbbfd 100644 +--- a/core/fpdfdoc/cpdf_variabletext.h ++++ b/core/fpdfdoc/cpvt_variabletext.h +@@ -1,11 +1,13 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#ifndef CORE_FPDFDOC_CPDF_VARIABLETEXT_H_ +-#define CORE_FPDFDOC_CPDF_VARIABLETEXT_H_ ++#ifndef CORE_FPDFDOC_CPVT_VARIABLETEXT_H_ ++#define CORE_FPDFDOC_CPVT_VARIABLETEXT_H_ ++ ++#include + + #include + #include +@@ -15,27 +17,24 @@ + #include "core/fpdfdoc/cpvt_lineinfo.h" + #include "core/fpdfdoc/cpvt_wordplace.h" + #include "core/fpdfdoc/cpvt_wordrange.h" ++#include "core/fxcrt/fx_codepage_forward.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "core/fxcrt/widestring.h" + ++class CPVT_Section; + class CPVT_Word; +-class CSection; + class IPVT_FontMap; + struct CPVT_WordInfo; + +-#define VARIABLETEXT_HALF 0.5f +- +-class CPDF_VariableText { ++class CPVT_VariableText { + public: + class Iterator { + public: +- explicit Iterator(CPDF_VariableText* pVT); ++ explicit Iterator(CPVT_VariableText* pVT); + ~Iterator(); + + bool NextWord(); +- bool PrevWord(); + bool NextLine(); + bool GetWord(CPVT_Word& word) const; + bool GetLine(CPVT_Line& line) const; +@@ -45,7 +44,7 @@ class CPDF_VariableText { + + private: + CPVT_WordPlace m_CurPos; +- UnownedPtr const m_pVT; ++ UnownedPtr const m_pVT; + }; + + class Provider { +@@ -53,26 +52,26 @@ class CPDF_VariableText { + explicit Provider(IPVT_FontMap* pFontMap); + virtual ~Provider(); + +- virtual uint32_t GetCharWidth(int32_t nFontIndex, uint16_t word); ++ virtual int GetCharWidth(int32_t nFontIndex, uint16_t word); + virtual int32_t GetTypeAscent(int32_t nFontIndex); + virtual int32_t GetTypeDescent(int32_t nFontIndex); + virtual int32_t GetWordFontIndex(uint16_t word, +- int32_t charset, ++ FX_Charset charset, + int32_t nFontIndex); +- virtual bool IsLatinWord(uint16_t word); + virtual int32_t GetDefaultFontIndex(); + ++ IPVT_FontMap* GetFontMap() { return m_pFontMap; } ++ + private: + UnownedPtr const m_pFontMap; + }; + +- CPDF_VariableText(); +- ~CPDF_VariableText(); ++ explicit CPVT_VariableText(Provider* Provider); ++ ~CPVT_VariableText(); + +- void SetProvider(CPDF_VariableText::Provider* pProvider); +- CPDF_VariableText::Iterator* GetIterator(); ++ void SetProvider(Provider* pProvider); ++ CPVT_VariableText::Iterator* GetIterator(); + +- void SetContentRect(const CPVT_FloatRect& rect); + CFX_FloatRect GetContentRect() const; + void SetPlateRect(const CFX_FloatRect& rect); + const CFX_FloatRect& GetPlateRect() const; +@@ -80,7 +79,6 @@ class CPDF_VariableText { + void SetAlignment(int32_t nFormat) { m_nAlignment = nFormat; } + void SetPasswordChar(uint16_t wSubWord) { m_wSubWord = wSubWord; } + void SetLimitChar(int32_t nLimitChar) { m_nLimitChar = nLimitChar; } +- void SetCharSpace(float fCharSpace) { m_fCharSpace = fCharSpace; } + void SetMultiLine(bool bMultiLine) { m_bMultiLine = bMultiLine; } + void SetAutoReturn(bool bAuto) { m_bLimitWidth = bAuto; } + void SetFontSize(float fFontSize) { m_fFontSize = fFontSize; } +@@ -95,7 +93,7 @@ class CPDF_VariableText { + void SetText(const WideString& text); + CPVT_WordPlace InsertWord(const CPVT_WordPlace& place, + uint16_t word, +- int32_t charset); ++ FX_Charset charset); + CPVT_WordPlace InsertSection(const CPVT_WordPlace& place); + CPVT_WordPlace DeleteWords(const CPVT_WordRange& PlaceRange); + CPVT_WordPlace DeleteWord(const CPVT_WordPlace& place); +@@ -108,7 +106,6 @@ class CPDF_VariableText { + int32_t GetCharArray() const { return m_nCharArray; } + int32_t GetLimitChar() const { return m_nLimitChar; } + bool IsMultiLine() const { return m_bMultiLine; } +- float GetCharSpace() const { return m_fCharSpace; } + bool IsAutoReturn() const { return m_bLimitWidth; } + + CPVT_WordPlace GetBeginWordPlace() const; +@@ -125,8 +122,8 @@ class CPDF_VariableText { + CPVT_WordPlace GetSectionBeginPlace(const CPVT_WordPlace& place) const; + CPVT_WordPlace GetSectionEndPlace(const CPVT_WordPlace& place) const; + void UpdateWordPlace(CPVT_WordPlace& place) const; +- CPVT_WordPlace AdjustLineHeader(const CPVT_WordPlace& place, +- bool bPrevOrNext) const; ++ CPVT_WordPlace PrevLineHeaderPlace(const CPVT_WordPlace& place) const; ++ CPVT_WordPlace NextLineHeaderPlace(const CPVT_WordPlace& place) const; + int32_t WordPlaceToWordIndex(const CPVT_WordPlace& place) const; + CPVT_WordPlace WordIndexToWordPlace(int32_t index) const; + +@@ -140,42 +137,37 @@ class CPDF_VariableText { + CFX_PointF InToOut(const CFX_PointF& point) const; + CFX_PointF OutToIn(const CFX_PointF& point) const; + CFX_FloatRect InToOut(const CPVT_FloatRect& rect) const; +- CPVT_FloatRect OutToIn(const CFX_FloatRect& rect) const; + +- float GetFontAscent(int32_t nFontIndex, float fFontSize); +- float GetFontDescent(int32_t nFontIndex, float fFontSize); ++ float GetFontAscent(int32_t nFontIndex, float fFontSize) const; ++ float GetFontDescent(int32_t nFontIndex, float fFontSize) const; + int32_t GetDefaultFontIndex(); + float GetLineLeading(); +- int32_t GetAlignment(); +- float GetWordWidth(const CPVT_WordInfo& WordInfo); ++ float GetWordWidth(const CPVT_WordInfo& WordInfo) const; + float GetWordWidth(int32_t nFontIndex, + uint16_t Word, + uint16_t SubWord, +- float fCharSpace, + float fFontSize, +- float fWordTail); +- float GetWordAscent(const CPVT_WordInfo& WordInfo); +- float GetWordDescent(const CPVT_WordInfo& WordInfo); +- float GetWordAscent(const CPVT_WordInfo& WordInfo, float fFontSize); +- float GetWordDescent(const CPVT_WordInfo& WordInfo, float fFontSize); ++ float fWordTail) const; ++ float GetWordAscent(const CPVT_WordInfo& WordInfo) const; ++ float GetWordDescent(const CPVT_WordInfo& WordInfo) const; ++ float GetWordAscent(const CPVT_WordInfo& WordInfo, float fFontSize) const; ++ float GetWordDescent(const CPVT_WordInfo& WordInfo, float fFontSize) const; + float GetLineAscent(); + float GetLineDescent(); + float GetLineIndent(); + + private: +- uint32_t GetCharWidth(int32_t nFontIndex, uint16_t Word, uint16_t SubWord); +- int32_t GetTypeAscent(int32_t nFontIndex); +- int32_t GetTypeDescent(int32_t nFontIndex); +- int32_t GetWordFontIndex(uint16_t word, int32_t charset, int32_t nFontIndex); +- bool IsLatinWord(uint16_t word); ++ int GetCharWidth(int32_t nFontIndex, uint16_t Word, uint16_t SubWord) const; ++ int32_t GetWordFontIndex(uint16_t word, ++ FX_Charset charset, ++ int32_t nFontIndex); + + CPVT_WordPlace AddSection(const CPVT_WordPlace& place); + CPVT_WordPlace AddLine(const CPVT_WordPlace& place, + const CPVT_LineInfo& lineinfo); + CPVT_WordPlace AddWord(const CPVT_WordPlace& place, + const CPVT_WordInfo& wordinfo); +- float GetWordFontSize(); +- int32_t GetWordFontIndex(const CPVT_WordInfo& WordInfo); ++ float GetWordFontSize() const; + + void ClearSectionRightWords(const CPVT_WordPlace& place); + +@@ -186,7 +178,7 @@ class CPDF_VariableText { + CPVT_WordPlace ClearLeftWord(const CPVT_WordPlace& place); + CPVT_WordPlace ClearRightWord(const CPVT_WordPlace& place); + +- CPVT_FloatRect Rearrange(const CPVT_WordRange& PlaceRange); ++ void Rearrange(const CPVT_WordRange& PlaceRange); + float GetAutoFontSize(); + bool IsBigger(float fFontSize) const; + CPVT_FloatRect RearrangeSections(const CPVT_WordRange& PlaceRange); +@@ -200,13 +192,12 @@ class CPDF_VariableText { + int32_t m_nCharArray = 0; + int32_t m_nAlignment = 0; + float m_fLineLeading = 0.0f; +- float m_fCharSpace = 0.0f; + float m_fFontSize = 0.0f; +- std::vector> m_SectionArray; +- UnownedPtr m_pVTProvider; +- std::unique_ptr m_pVTIterator; ++ std::vector> m_SectionArray; ++ UnownedPtr m_pVTProvider; ++ std::unique_ptr m_pVTIterator; + CFX_FloatRect m_rcPlate; + CPVT_FloatRect m_rcContent; + }; + +-#endif // CORE_FPDFDOC_CPDF_VARIABLETEXT_H_ ++#endif // CORE_FPDFDOC_CPVT_VARIABLETEXT_H_ +diff --git a/core/fpdfdoc/cpvt_word.h b/core/fpdfdoc/cpvt_word.h +index ad0ba744c..f5ec84381 100644 +--- a/core/fpdfdoc/cpvt_word.h ++++ b/core/fpdfdoc/cpvt_word.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,15 +7,17 @@ + #ifndef CORE_FPDFDOC_CPVT_WORD_H_ + #define CORE_FPDFDOC_CPVT_WORD_H_ + ++#include ++ + #include "core/fpdfdoc/cpvt_wordplace.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/fx_codepage.h" + + class CPVT_Word { + public: + CPVT_Word(); + + uint16_t Word; +- int32_t nCharset; ++ FX_Charset nCharset; + CPVT_WordPlace WordPlace; + CFX_PointF ptWord; + float fAscent; +@@ -27,7 +29,7 @@ class CPVT_Word { + + inline CPVT_Word::CPVT_Word() + : Word(0), +- nCharset(0), ++ nCharset(FX_Charset::kANSI), + fAscent(0.0f), + fDescent(0.0f), + fWidth(0.0f), +diff --git a/core/fpdfdoc/cpvt_wordinfo.cpp b/core/fpdfdoc/cpvt_wordinfo.cpp +index 1e1556bcc..a301a323d 100644 +--- a/core/fpdfdoc/cpvt_wordinfo.cpp ++++ b/core/fpdfdoc/cpvt_wordinfo.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + // +@@ -10,13 +10,15 @@ + + CPVT_WordInfo::CPVT_WordInfo() + : Word(0), +- nCharset(FX_CHARSET_ANSI), ++ nCharset(FX_Charset::kANSI), + fWordX(0.0f), + fWordY(0.0f), + fWordTail(0.0f), + nFontIndex(-1) {} + +-CPVT_WordInfo::CPVT_WordInfo(uint16_t word, int32_t charset, int32_t fontIndex) ++CPVT_WordInfo::CPVT_WordInfo(uint16_t word, ++ FX_Charset charset, ++ int32_t fontIndex) + : Word(word), + nCharset(charset), + fWordX(0.0f), +@@ -26,7 +28,7 @@ CPVT_WordInfo::CPVT_WordInfo(uint16_t word, int32_t charset, int32_t fontIndex) + + CPVT_WordInfo::CPVT_WordInfo(const CPVT_WordInfo& word) + : Word(0), +- nCharset(FX_CHARSET_ANSI), ++ nCharset(FX_Charset::kANSI), + fWordX(0.0f), + fWordY(0.0f), + fWordTail(0.0f), +@@ -34,7 +36,7 @@ CPVT_WordInfo::CPVT_WordInfo(const CPVT_WordInfo& word) + operator=(word); + } + +-CPVT_WordInfo::~CPVT_WordInfo() {} ++CPVT_WordInfo::~CPVT_WordInfo() = default; + + CPVT_WordInfo& CPVT_WordInfo::operator=(const CPVT_WordInfo& word) { + if (this == &word) +diff --git a/core/fpdfdoc/cpvt_wordinfo.h b/core/fpdfdoc/cpvt_wordinfo.h +index e23ab2ca2..98ef9f2fc 100644 +--- a/core/fpdfdoc/cpvt_wordinfo.h ++++ b/core/fpdfdoc/cpvt_wordinfo.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,18 +7,20 @@ + #ifndef CORE_FPDFDOC_CPVT_WORDINFO_H_ + #define CORE_FPDFDOC_CPVT_WORDINFO_H_ + +-#include "core/fxcrt/fx_system.h" ++#include ++ ++#include "core/fxcrt/fx_codepage_forward.h" + + struct CPVT_WordInfo { + CPVT_WordInfo(); +- CPVT_WordInfo(uint16_t word, int32_t charset, int32_t fontIndex); ++ CPVT_WordInfo(uint16_t word, FX_Charset charset, int32_t fontIndex); + CPVT_WordInfo(const CPVT_WordInfo& word); + ~CPVT_WordInfo(); + + CPVT_WordInfo& operator=(const CPVT_WordInfo& word); + + uint16_t Word; +- int32_t nCharset; ++ FX_Charset nCharset; + float fWordX; + float fWordY; + float fWordTail; +diff --git a/core/fpdfdoc/cpvt_wordplace.h b/core/fpdfdoc/cpvt_wordplace.h +index c0a1a9cd5..2d0f9c825 100644 +--- a/core/fpdfdoc/cpvt_wordplace.h ++++ b/core/fpdfdoc/cpvt_wordplace.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,10 @@ + #ifndef CORE_FPDFDOC_CPVT_WORDPLACE_H_ + #define CORE_FPDFDOC_CPVT_WORDPLACE_H_ + +-#include "core/fxcrt/fx_system.h" ++#include + + struct CPVT_WordPlace { +- CPVT_WordPlace() : nSecIndex(-1), nLineIndex(-1), nWordIndex(-1) {} ++ CPVT_WordPlace() = default; + + CPVT_WordPlace(int32_t other_nSecIndex, + int32_t other_nLineIndex, +@@ -65,9 +65,9 @@ struct CPVT_WordPlace { + return nLineIndex - wp.nLineIndex; + } + +- int32_t nSecIndex; +- int32_t nLineIndex; +- int32_t nWordIndex; ++ int32_t nSecIndex = -1; ++ int32_t nLineIndex = -1; ++ int32_t nWordIndex = -1; + }; + + #endif // CORE_FPDFDOC_CPVT_WORDPLACE_H_ +diff --git a/core/fpdfdoc/cpvt_wordrange.h b/core/fpdfdoc/cpvt_wordrange.h +index fbc691fba..5d84af352 100644 +--- a/core/fpdfdoc/cpvt_wordrange.h ++++ b/core/fpdfdoc/cpvt_wordrange.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,7 +11,6 @@ + #include + + #include "core/fpdfdoc/cpvt_wordplace.h" +-#include "core/fxcrt/fx_system.h" + + struct CPVT_WordRange { + CPVT_WordRange() = default; +diff --git a/core/fpdfdoc/csection.cpp b/core/fpdfdoc/csection.cpp +deleted file mode 100644 +index ad962be55..000000000 +--- a/core/fpdfdoc/csection.cpp ++++ /dev/null +@@ -1,252 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fpdfdoc/csection.h" +- +-#include +- +-#include "core/fpdfdoc/cline.h" +-#include "core/fpdfdoc/cpdf_variabletext.h" +-#include "core/fpdfdoc/cpvt_wordinfo.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" +- +-CSection::CSection(CPDF_VariableText* pVT) : m_pVT(pVT) { +- ASSERT(m_pVT); +-} +- +-CSection::~CSection() = default; +- +-void CSection::ResetLinePlace() { +- int32_t i = 0; +- for (auto& pLine : m_LineArray) { +- pLine->LinePlace = CPVT_WordPlace(SecPlace.nSecIndex, i, -1); +- ++i; +- } +-} +- +-CPVT_WordPlace CSection::AddWord(const CPVT_WordPlace& place, +- const CPVT_WordInfo& wordinfo) { +- int32_t nWordIndex = pdfium::clamp( +- place.nWordIndex, 0, pdfium::CollectionSize(m_WordArray)); +- m_WordArray.insert(m_WordArray.begin() + nWordIndex, +- pdfium::MakeUnique(wordinfo)); +- return place; +-} +- +-CPVT_WordPlace CSection::AddLine(const CPVT_LineInfo& lineinfo) { +- m_LineArray.push_back(pdfium::MakeUnique(lineinfo)); +- return CPVT_WordPlace(SecPlace.nSecIndex, m_LineArray.size() - 1, -1); +-} +- +-CPVT_FloatRect CSection::Rearrange() { +- if (m_pVT->GetCharArray() > 0) +- return CTypeset(this).CharArray(); +- return CTypeset(this).Typeset(); +-} +- +-CFX_SizeF CSection::GetSectionSize(float fFontSize) { +- return CTypeset(this).GetEditSize(fFontSize); +-} +- +-CPVT_WordPlace CSection::GetBeginWordPlace() const { +- if (m_LineArray.empty()) +- return SecPlace; +- return m_LineArray.front()->GetBeginWordPlace(); +-} +- +-CPVT_WordPlace CSection::GetEndWordPlace() const { +- if (m_LineArray.empty()) +- return SecPlace; +- return m_LineArray.back()->GetEndWordPlace(); +-} +- +-CPVT_WordPlace CSection::GetPrevWordPlace(const CPVT_WordPlace& place) const { +- if (place.nLineIndex < 0) +- return GetBeginWordPlace(); +- +- if (place.nLineIndex >= pdfium::CollectionSize(m_LineArray)) +- return GetEndWordPlace(); +- +- CLine* pLine = m_LineArray[place.nLineIndex].get(); +- if (place.nWordIndex == pLine->m_LineInfo.nBeginWordIndex) +- return CPVT_WordPlace(place.nSecIndex, place.nLineIndex, -1); +- +- if (place.nWordIndex >= pLine->m_LineInfo.nBeginWordIndex) +- return pLine->GetPrevWordPlace(place); +- +- if (!pdfium::IndexInBounds(m_LineArray, place.nLineIndex - 1)) +- return place; +- +- return m_LineArray[place.nLineIndex - 1]->GetEndWordPlace(); +-} +- +-CPVT_WordPlace CSection::GetNextWordPlace(const CPVT_WordPlace& place) const { +- if (place.nLineIndex < 0) +- return GetBeginWordPlace(); +- +- if (place.nLineIndex >= pdfium::CollectionSize(m_LineArray)) +- return GetEndWordPlace(); +- +- CLine* pLine = m_LineArray[place.nLineIndex].get(); +- if (place.nWordIndex < pLine->m_LineInfo.nEndWordIndex) +- return pLine->GetNextWordPlace(place); +- +- if (!pdfium::IndexInBounds(m_LineArray, place.nLineIndex + 1)) +- return place; +- +- return m_LineArray[place.nLineIndex + 1]->GetBeginWordPlace(); +-} +- +-void CSection::UpdateWordPlace(CPVT_WordPlace& place) const { +- int32_t nLeft = 0; +- int32_t nRight = pdfium::CollectionSize(m_LineArray) - 1; +- int32_t nMid = (nLeft + nRight) / 2; +- while (nLeft <= nRight) { +- CLine* pLine = m_LineArray[nMid].get(); +- if (place.nWordIndex < pLine->m_LineInfo.nBeginWordIndex) { +- nRight = nMid - 1; +- nMid = (nLeft + nRight) / 2; +- } else if (place.nWordIndex > pLine->m_LineInfo.nEndWordIndex) { +- nLeft = nMid + 1; +- nMid = (nLeft + nRight) / 2; +- } else { +- place.nLineIndex = nMid; +- return; +- } +- } +-} +- +-CPVT_WordPlace CSection::SearchWordPlace(const CFX_PointF& point) const { +- CPVT_WordPlace place = GetBeginWordPlace(); +- bool bUp = true; +- bool bDown = true; +- int32_t nLeft = 0; +- int32_t nRight = pdfium::CollectionSize(m_LineArray) - 1; +- int32_t nMid = pdfium::CollectionSize(m_LineArray) / 2; +- while (nLeft <= nRight) { +- CLine* pLine = m_LineArray[nMid].get(); +- float fTop = pLine->m_LineInfo.fLineY - pLine->m_LineInfo.fLineAscent - +- m_pVT->GetLineLeading(); +- float fBottom = pLine->m_LineInfo.fLineY - pLine->m_LineInfo.fLineDescent; +- if (IsFloatBigger(point.y, fTop)) +- bUp = false; +- if (IsFloatSmaller(point.y, fBottom)) +- bDown = false; +- if (IsFloatSmaller(point.y, fTop)) { +- nRight = nMid - 1; +- nMid = (nLeft + nRight) / 2; +- continue; +- } +- if (IsFloatBigger(point.y, fBottom)) { +- nLeft = nMid + 1; +- nMid = (nLeft + nRight) / 2; +- continue; +- } +- place = SearchWordPlace( +- point.x, +- CPVT_WordRange(pLine->GetNextWordPlace(pLine->GetBeginWordPlace()), +- pLine->GetEndWordPlace())); +- place.nLineIndex = nMid; +- return place; +- } +- if (bUp) +- place = GetBeginWordPlace(); +- if (bDown) +- place = GetEndWordPlace(); +- return place; +-} +- +-CPVT_WordPlace CSection::SearchWordPlace( +- float fx, +- const CPVT_WordPlace& lineplace) const { +- if (!pdfium::IndexInBounds(m_LineArray, lineplace.nLineIndex)) +- return GetBeginWordPlace(); +- +- CLine* pLine = m_LineArray[lineplace.nLineIndex].get(); +- return SearchWordPlace( +- fx - m_Rect.left, +- CPVT_WordRange(pLine->GetNextWordPlace(pLine->GetBeginWordPlace()), +- pLine->GetEndWordPlace())); +-} +- +-CPVT_WordPlace CSection::SearchWordPlace(float fx, +- const CPVT_WordRange& range) const { +- CPVT_WordPlace wordplace = range.BeginPos; +- wordplace.nWordIndex = -1; +- +- int32_t nLeft = range.BeginPos.nWordIndex; +- int32_t nRight = range.EndPos.nWordIndex + 1; +- int32_t nMid = (nLeft + nRight) / 2; +- while (nLeft < nRight) { +- if (nMid == nLeft) +- break; +- if (nMid == nRight) { +- nMid--; +- break; +- } +- if (!pdfium::IndexInBounds(m_WordArray, nMid)) +- break; +- CPVT_WordInfo* pWord = m_WordArray[nMid].get(); +- if (fx > pWord->fWordX + m_pVT->GetWordWidth(*pWord) * VARIABLETEXT_HALF) { +- nLeft = nMid; +- nMid = (nLeft + nRight) / 2; +- continue; +- } +- nRight = nMid; +- nMid = (nLeft + nRight) / 2; +- } +- if (pdfium::IndexInBounds(m_WordArray, nMid)) { +- CPVT_WordInfo* pWord = m_WordArray[nMid].get(); +- if (fx > pWord->fWordX + m_pVT->GetWordWidth(*pWord) * VARIABLETEXT_HALF) +- wordplace.nWordIndex = nMid; +- } +- return wordplace; +-} +- +-void CSection::ClearLeftWords(int32_t nWordIndex) { +- for (int32_t i = nWordIndex; i >= 0; i--) { +- if (pdfium::IndexInBounds(m_WordArray, i)) +- m_WordArray.erase(m_WordArray.begin() + i); +- } +-} +- +-void CSection::ClearRightWords(int32_t nWordIndex) { +- int32_t sz = pdfium::CollectionSize(m_WordArray); +- for (int32_t i = sz - 1; i > nWordIndex; i--) { +- if (pdfium::IndexInBounds(m_WordArray, i)) +- m_WordArray.erase(m_WordArray.begin() + i); +- } +-} +- +-void CSection::ClearMidWords(int32_t nBeginIndex, int32_t nEndIndex) { +- for (int32_t i = nEndIndex; i > nBeginIndex; i--) { +- if (pdfium::IndexInBounds(m_WordArray, i)) +- m_WordArray.erase(m_WordArray.begin() + i); +- } +-} +- +-void CSection::ClearWords(const CPVT_WordRange& PlaceRange) { +- CPVT_WordPlace SecBeginPos = GetBeginWordPlace(); +- CPVT_WordPlace SecEndPos = GetEndWordPlace(); +- if (PlaceRange.BeginPos >= SecBeginPos) { +- if (PlaceRange.EndPos <= SecEndPos) { +- ClearMidWords(PlaceRange.BeginPos.nWordIndex, +- PlaceRange.EndPos.nWordIndex); +- } else { +- ClearRightWords(PlaceRange.BeginPos.nWordIndex); +- } +- } else if (PlaceRange.EndPos <= SecEndPos) { +- ClearLeftWords(PlaceRange.EndPos.nWordIndex); +- } else { +- m_WordArray.clear(); +- } +-} +- +-void CSection::ClearWord(const CPVT_WordPlace& place) { +- if (pdfium::IndexInBounds(m_WordArray, place.nWordIndex)) +- m_WordArray.erase(m_WordArray.begin() + place.nWordIndex); +-} +diff --git a/core/fpdfdoc/csection.h b/core/fpdfdoc/csection.h +deleted file mode 100644 +index 7352c71d4..000000000 +--- a/core/fpdfdoc/csection.h ++++ /dev/null +@@ -1,63 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FPDFDOC_CSECTION_H_ +-#define CORE_FPDFDOC_CSECTION_H_ +- +-#include +-#include +- +-#include "core/fpdfdoc/cline.h" +-#include "core/fpdfdoc/cpvt_wordinfo.h" +-#include "core/fpdfdoc/cpvt_wordrange.h" +-#include "core/fpdfdoc/ctypeset.h" +-#include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" +- +-class CPDF_VariableText; +-class CPVT_LineInfo; +-struct CPVT_WordLine; +-struct CPVT_WordPlace; +- +-class CSection final { +- public: +- explicit CSection(CPDF_VariableText* pVT); +- ~CSection(); +- +- void ResetLinePlace(); +- CPVT_WordPlace AddWord(const CPVT_WordPlace& place, +- const CPVT_WordInfo& wordinfo); +- CPVT_WordPlace AddLine(const CPVT_LineInfo& lineinfo); +- void ClearWords(const CPVT_WordRange& PlaceRange); +- void ClearWord(const CPVT_WordPlace& place); +- CPVT_FloatRect Rearrange(); +- CFX_SizeF GetSectionSize(float fFontSize); +- CPVT_WordPlace GetBeginWordPlace() const; +- CPVT_WordPlace GetEndWordPlace() const; +- CPVT_WordPlace GetPrevWordPlace(const CPVT_WordPlace& place) const; +- CPVT_WordPlace GetNextWordPlace(const CPVT_WordPlace& place) const; +- void UpdateWordPlace(CPVT_WordPlace& place) const; +- CPVT_WordPlace SearchWordPlace(const CFX_PointF& point) const; +- CPVT_WordPlace SearchWordPlace(float fx, +- const CPVT_WordPlace& lineplace) const; +- CPVT_WordPlace SearchWordPlace(float fx, const CPVT_WordRange& range) const; +- +- CPVT_WordPlace SecPlace; +- CPVT_FloatRect m_Rect; +- std::vector> m_LineArray; +- std::vector> m_WordArray; +- +- private: +- friend class CTypeset; +- +- void ClearLeftWords(int32_t nWordIndex); +- void ClearRightWords(int32_t nWordIndex); +- void ClearMidWords(int32_t nBeginIndex, int32_t nEndIndex); +- +- UnownedPtr const m_pVT; +-}; +- +-#endif // CORE_FPDFDOC_CSECTION_H_ +diff --git a/core/fpdfdoc/ctypeset.h b/core/fpdfdoc/ctypeset.h +deleted file mode 100644 +index 3d56a3b09..000000000 +--- a/core/fpdfdoc/ctypeset.h ++++ /dev/null +@@ -1,35 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FPDFDOC_CTYPESET_H_ +-#define CORE_FPDFDOC_CTYPESET_H_ +- +-#include "core/fpdfdoc/cpvt_floatrect.h" +-#include "core/fxcrt/fx_system.h" +-#include "core/fxcrt/unowned_ptr.h" +- +-class CPDF_VariableText; +-class CSection; +- +-class CTypeset final { +- public: +- explicit CTypeset(CSection* pSection); +- ~CTypeset(); +- +- CFX_SizeF GetEditSize(float fFontSize); +- CPVT_FloatRect Typeset(); +- CPVT_FloatRect CharArray(); +- +- private: +- void SplitLines(bool bTypeset, float fFontSize); +- void OutputLines(); +- +- CPVT_FloatRect m_rcRet; +- UnownedPtr const m_pVT; +- CSection* const m_pSection; +-}; +- +-#endif // CORE_FPDFDOC_CTYPESET_H_ +diff --git a/core/fpdfdoc/ipvt_fontmap.h b/core/fpdfdoc/ipvt_fontmap.h +index e818356dd..dd210b083 100644 +--- a/core/fpdfdoc/ipvt_fontmap.h ++++ b/core/fpdfdoc/ipvt_fontmap.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,7 +9,8 @@ + + #include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/fx_codepage_forward.h" + #include "core/fxcrt/retain_ptr.h" + + class CPDF_Font; +@@ -21,10 +22,11 @@ class IPVT_FontMap { + virtual RetainPtr GetPDFFont(int32_t nFontIndex) = 0; + virtual ByteString GetPDFFontAlias(int32_t nFontIndex) = 0; + virtual int32_t GetWordFontIndex(uint16_t word, +- int32_t charset, ++ FX_Charset charset, + int32_t nFontIndex) = 0; + virtual int32_t CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) = 0; +- virtual int32_t CharSetFromUnicode(uint16_t word, int32_t nOldCharset) = 0; ++ virtual FX_Charset CharSetFromUnicode(uint16_t word, ++ FX_Charset nOldCharset) = 0; + }; + + #endif // CORE_FPDFDOC_IPVT_FONTMAP_H_ +diff --git a/core/fpdftext/BUILD.gn b/core/fpdftext/BUILD.gn +index f48a96dee..cea247e6b 100644 +--- a/core/fpdftext/BUILD.gn ++++ b/core/fpdftext/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -16,7 +16,7 @@ source_set("fpdftext") { + "unicodenormalizationdata.cpp", + "unicodenormalizationdata.h", + ] +- configs += [ "../../:pdfium_core_config" ] ++ configs += [ "../../:pdfium_strict_config" ] + deps = [ + "../fpdfapi/font", + "../fpdfapi/page", +diff --git a/core/fpdftext/cpdf_linkextract.cpp b/core/fpdftext/cpdf_linkextract.cpp +index f822f3bcc..66a3dda3a 100644 +--- a/core/fpdftext/cpdf_linkextract.cpp ++++ b/core/fpdftext/cpdf_linkextract.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -112,11 +112,11 @@ CPDF_LinkExtract::~CPDF_LinkExtract() = default; + + void CPDF_LinkExtract::ExtractLinks() { + m_LinkArray.clear(); +- int start = 0; +- int pos = 0; ++ size_t start = 0; ++ size_t pos = 0; + bool bAfterHyphen = false; + bool bLineBreak = false; +- const int nTotalChar = m_pTextPage->CountChars(); ++ const size_t nTotalChar = m_pTextPage->CountChars(); + const WideString page_text = m_pTextPage->GetAllPageText(); + while (pos < nTotalChar) { + const CPDF_TextPage::CharInfo& char_info = m_pTextPage->GetCharInfo(pos); +@@ -130,7 +130,7 @@ void CPDF_LinkExtract::ExtractLinks() { + continue; + } + +- int nCount = pos - start; ++ size_t nCount = pos - start; + if (pos == nTotalChar - 1) { + ++nCount; + } else if (bAfterHyphen && +@@ -163,13 +163,12 @@ void CPDF_LinkExtract::ExtractLinks() { + // Check for potential web URLs and email addresses. + // Ftp address, file system links, data, blob etc. are not checked. + if (nCount > 5) { +- int32_t nStartOffset; +- int32_t nCountOverload; +- if (CheckWebLink(&strBeCheck, &nStartOffset, &nCountOverload)) { +- m_LinkArray.push_back( +- {start + nStartOffset, nCountOverload, strBeCheck}); ++ auto maybe_link = CheckWebLink(strBeCheck); ++ if (maybe_link.has_value()) { ++ maybe_link.value().m_Start += start; ++ m_LinkArray.push_back(maybe_link.value()); + } else if (CheckMailLink(&strBeCheck)) { +- m_LinkArray.push_back({start, nCount, strBeCheck}); ++ m_LinkArray.push_back(Link{{start, nCount}, strBeCheck}); + } + } + } +@@ -177,36 +176,34 @@ void CPDF_LinkExtract::ExtractLinks() { + } + } + +-bool CPDF_LinkExtract::CheckWebLink(WideString* strBeCheck, +- int32_t* nStart, +- int32_t* nCount) { ++absl::optional CPDF_LinkExtract::CheckWebLink( ++ const WideString& strBeCheck) { + static const wchar_t kHttpScheme[] = L"http"; + static const wchar_t kWWWAddrStart[] = L"www."; + + const size_t kHttpSchemeLen = FXSYS_len(kHttpScheme); + const size_t kWWWAddrStartLen = FXSYS_len(kWWWAddrStart); + +- WideString str = *strBeCheck; ++ WideString str = strBeCheck; + str.MakeLower(); + +- size_t len = str.GetLength(); + // First, try to find the scheme. + auto start = str.Find(kHttpScheme); + if (start.has_value()) { + size_t off = start.value() + kHttpSchemeLen; // move after "http". +- if (len > off + 4) { // At least "://" follows. ++ if (str.GetLength() > off + 4) { // At least "://" follows. + if (str[off] == L's') // "https" scheme is accepted. + off++; + if (str[off] == L':' && str[off + 1] == L'/' && str[off + 2] == L'/') { + off += 3; +- size_t end = TrimExternalBracketsFromWebLink(str, start.value(), +- str.GetLength() - 1); +- end = FindWebLinkEnding(str, off, end); ++ const size_t end = ++ FindWebLinkEnding(str, off, ++ TrimExternalBracketsFromWebLink( ++ str, start.value(), str.GetLength() - 1)); + if (end > off) { // Non-empty host name. +- *nStart = start.value(); +- *nCount = end - start.value() + 1; +- *strBeCheck = strBeCheck->Substr(*nStart, *nCount); +- return true; ++ const size_t nStart = start.value(); ++ const size_t nCount = end - nStart + 1; ++ return Link{{nStart, nCount}, strBeCheck.Substr(nStart, nCount)}; + } + } + } +@@ -214,18 +211,23 @@ bool CPDF_LinkExtract::CheckWebLink(WideString* strBeCheck, + + // When there is no scheme, try to find url starting with "www.". + start = str.Find(kWWWAddrStart); +- if (start.has_value() && len > start.value() + kWWWAddrStartLen) { +- size_t end = TrimExternalBracketsFromWebLink(str, start.value(), +- str.GetLength() - 1); +- end = FindWebLinkEnding(str, start.value(), end); +- if (end > start.value() + kWWWAddrStartLen) { +- *nStart = start.value(); +- *nCount = end - start.value() + 1; +- *strBeCheck = L"http://" + strBeCheck->Substr(*nStart, *nCount); +- return true; ++ if (start.has_value()) { ++ size_t off = start.value() + kWWWAddrStartLen; ++ if (str.GetLength() > off) { ++ const size_t end = ++ FindWebLinkEnding(str, start.value(), ++ TrimExternalBracketsFromWebLink( ++ str, start.value(), str.GetLength() - 1)); ++ if (end > off) { ++ const size_t nStart = start.value(); ++ const size_t nCount = end - nStart + 1; ++ return Link{{nStart, nCount}, ++ L"http://" + strBeCheck.Substr(nStart, nCount)}; ++ } + } + } +- return false; ++ ++ return absl::nullopt; + } + + bool CPDF_LinkExtract::CheckMailLink(WideString* str) { +@@ -309,12 +311,9 @@ std::vector CPDF_LinkExtract::GetRects(size_t index) const { + m_LinkArray[index].m_Count); + } + +-bool CPDF_LinkExtract::GetTextRange(size_t index, +- int* start_char_index, +- int* char_count) const { ++absl::optional CPDF_LinkExtract::GetTextRange( ++ size_t index) const { + if (index >= m_LinkArray.size()) +- return false; +- *start_char_index = m_LinkArray[index].m_Start; +- *char_count = m_LinkArray[index].m_Count; +- return true; ++ return absl::nullopt; ++ return m_LinkArray[index]; + } +diff --git a/core/fpdftext/cpdf_linkextract.h b/core/fpdftext/cpdf_linkextract.h +index 56d70a34b..3f0fa61e0 100644 +--- a/core/fpdftext/cpdf_linkextract.h ++++ b/core/fpdftext/cpdf_linkextract.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,16 +7,25 @@ + #ifndef CORE_FPDFTEXT_CPDF_LINKEXTRACT_H_ + #define CORE_FPDFTEXT_CPDF_LINKEXTRACT_H_ + ++#include ++#include ++ + #include + + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/unowned_ptr.h" ++#include "core/fxcrt/widestring.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CPDF_TextPage; + + class CPDF_LinkExtract { + public: ++ struct Range { ++ size_t m_Start; ++ size_t m_Count; ++ }; ++ + explicit CPDF_LinkExtract(const CPDF_TextPage* pTextPage); + ~CPDF_LinkExtract(); + +@@ -24,19 +33,16 @@ class CPDF_LinkExtract { + size_t CountLinks() const { return m_LinkArray.size(); } + WideString GetURL(size_t index) const; + std::vector GetRects(size_t index) const; +- bool GetTextRange(size_t index, int* start_char_index, int* char_count) const; ++ absl::optional GetTextRange(size_t index) const; + + protected: +- bool CheckWebLink(WideString* str, int32_t* nStart, int32_t* nCount); +- bool CheckMailLink(WideString* str); +- +- private: +- struct Link { +- int m_Start; +- int m_Count; ++ struct Link : public Range { + WideString m_strUrl; + }; + ++ absl::optional CheckWebLink(const WideString& str); ++ bool CheckMailLink(WideString* str); ++ + UnownedPtr const m_pTextPage; + std::vector m_LinkArray; + }; +diff --git a/core/fpdftext/cpdf_linkextract_unittest.cpp b/core/fpdftext/cpdf_linkextract_unittest.cpp +index f4bd197dd..a0c69d6aa 100644 +--- a/core/fpdftext/cpdf_linkextract_unittest.cpp ++++ b/core/fpdftext/cpdf_linkextract_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -21,7 +21,7 @@ class CPDF_TestLinkExtract final : public CPDF_LinkExtract { + TEST(CPDF_LinkExtractTest, CheckMailLink) { + CPDF_TestLinkExtract extractor; + // Check cases that fail to extract valid mail link. +- const wchar_t* const invalid_strs[] = { ++ const wchar_t* const kInvalidStrings[] = { + L"", + L"peter.pan", // '@' is required. + L"abc@server", // Domain name needs at least one '.'. +@@ -30,15 +30,14 @@ TEST(CPDF_LinkExtractTest, CheckMailLink) { + L"abc@.xyz.org", // Domain name should not start with '.'. + L"fan@g..com" // Domain name should not have consecutive '.' + }; +- for (size_t i = 0; i < FX_ArraySize(invalid_strs); ++i) { +- const wchar_t* const input = invalid_strs[i]; ++ for (const wchar_t* input : kInvalidStrings) { + WideString text_str(input); + EXPECT_FALSE(extractor.CheckMailLink(&text_str)) << input; + } + + // Check cases that can extract valid mail link. + // An array of {input_string, expected_extracted_email_address}. +- const wchar_t* const valid_strs[][2] = { ++ const wchar_t* const kValidStrings[][2] = { + {L"peter@abc.d", L"peter@abc.d"}, + {L"red.teddy.b@abc.com", L"red.teddy.b@abc.com"}, + {L"abc_@gmail.com", L"abc_@gmail.com"}, // '_' is ok before '@'. +@@ -51,11 +50,11 @@ TEST(CPDF_LinkExtractTest, CheckMailLink) { + {L"fan@g.com..", L"fan@g.com"}, // Trim the ending periods. + {L"CAP.cap@Gmail.Com", L"CAP.cap@Gmail.Com"}, // Keep the original case. + }; +- for (size_t i = 0; i < FX_ArraySize(valid_strs); ++i) { +- const wchar_t* const input = valid_strs[i][0]; ++ for (const auto& it : kValidStrings) { ++ const wchar_t* const input = it[0]; + WideString text_str(input); + WideString expected_str(L"mailto:"); +- expected_str += valid_strs[i][1]; ++ expected_str += it[1]; + EXPECT_TRUE(extractor.CheckMailLink(&text_str)) << input; + EXPECT_STREQ(expected_str.c_str(), text_str.c_str()); + } +@@ -65,8 +64,11 @@ TEST(CPDF_LinkExtractTest, CheckWebLink) { + CPDF_TestLinkExtract extractor; + // Check cases that fail to extract valid web link. + // The last few are legit web addresses that we don't handle now. +- const wchar_t* const invalid_cases[] = { +- L"", L"http", L"www.", L"https-and-www", ++ const wchar_t* const kInvalidCases[] = { ++ L"", // Blank. ++ L"http", // No colon. ++ L"www.", // Missing domain. ++ L"https-and-www", // Dash not colon. + L"http:/abc.com", // Missing slash. + L"http://((()),", // Only invalid chars in host name. + L"ftp://example.com", // Ftp scheme is not supported. +@@ -74,19 +76,11 @@ TEST(CPDF_LinkExtractTest, CheckWebLink) { + L"http//[example.com", // Invalid IPv6 address. + L"http//[00:00:00:00:00:00", // Invalid IPv6 address. + L"http//[]", // Empty IPv6 address. +- // Web addresses that in correct format that we don't handle. +- L"abc.example.com", // URL without scheme. ++ L"abc.example.com", // URL without scheme. + }; +- const int32_t DEFAULT_VALUE = -42; +- for (size_t i = 0; i < FX_ArraySize(invalid_cases); ++i) { +- const wchar_t* const input = invalid_cases[i]; +- WideString text_str(input); +- int32_t start_offset = DEFAULT_VALUE; +- int32_t count = DEFAULT_VALUE; +- EXPECT_FALSE(extractor.CheckWebLink(&text_str, &start_offset, &count)) +- << input; +- EXPECT_EQ(DEFAULT_VALUE, start_offset) << input; +- EXPECT_EQ(DEFAULT_VALUE, count) << input; ++ for (const wchar_t* input : kInvalidCases) { ++ auto maybe_link = extractor.CheckWebLink(input); ++ EXPECT_FALSE(maybe_link.has_value()) << input; + } + + // Check cases that can extract valid web link. +@@ -94,10 +88,10 @@ TEST(CPDF_LinkExtractTest, CheckWebLink) { + struct ValidCase { + const wchar_t* const input_string; + const wchar_t* const url_extracted; +- const int32_t start_offset; +- const int32_t count; ++ const size_t start_offset; ++ const size_t count; + }; +- const ValidCase valid_cases[] = { ++ const ValidCase kValidCases[] = { + {L"http://www.example.com", L"http://www.example.com", 0, + 22}, // standard URL. + {L"http://www.example.com:88", L"http://www.example.com:88", 0, +@@ -173,15 +167,11 @@ TEST(CPDF_LinkExtractTest, CheckWebLink) { + {L"www.测试。net。", L"http://www.测试。net。", 0, 11}, + {L"www.测试.net;", L"http://www.测试.net;", 0, 11}, + }; +- for (size_t i = 0; i < FX_ArraySize(valid_cases); ++i) { +- const wchar_t* const input = valid_cases[i].input_string; +- WideString text_str(input); +- int32_t start_offset = DEFAULT_VALUE; +- int32_t count = DEFAULT_VALUE; +- EXPECT_TRUE(extractor.CheckWebLink(&text_str, &start_offset, &count)) +- << input; +- EXPECT_STREQ(valid_cases[i].url_extracted, text_str.c_str()); +- EXPECT_EQ(valid_cases[i].start_offset, start_offset) << input; +- EXPECT_EQ(valid_cases[i].count, count) << input; ++ for (const auto& it : kValidCases) { ++ auto maybe_link = extractor.CheckWebLink(it.input_string); ++ ASSERT_TRUE(maybe_link.has_value()) << it.input_string; ++ EXPECT_STREQ(it.url_extracted, maybe_link.value().m_strUrl.c_str()); ++ EXPECT_EQ(it.start_offset, maybe_link.value().m_Start) << it.input_string; ++ EXPECT_EQ(it.count, maybe_link.value().m_Count) << it.input_string; + } + } +diff --git a/core/fpdftext/cpdf_textpage.cpp b/core/fpdftext/cpdf_textpage.cpp +index 58a2959b9..697e20267 100644 +--- a/core/fpdftext/cpdf_textpage.cpp ++++ b/core/fpdftext/cpdf_textpage.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,11 +6,14 @@ + + #include "core/fpdftext/cpdf_textpage.h" + ++#include ++#include ++ + #include +-#include + #include + #include + ++#include "core/fpdfapi/font/cpdf_cidfont.h" + #include "core/fpdfapi/font/cpdf_font.h" + #include "core/fpdfapi/page/cpdf_form.h" + #include "core/fpdfapi/page/cpdf_formobject.h" +@@ -20,24 +23,27 @@ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fpdftext/unicodenormalizationdata.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_bidi.h" + #include "core/fxcrt/fx_extension.h" +-#include "core/fxcrt/fx_memory_wrappers.h" + #include "core/fxcrt/fx_unicode.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxcrt/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/cxx17_backports.h" + + namespace { + + constexpr float kDefaultFontSize = 1.0f; + constexpr float kSizeEpsilon = 0.01f; + +-const uint16_t* const g_UnicodeData_Normalization_Maps[] = { +- g_UnicodeData_Normalization_Map2, g_UnicodeData_Normalization_Map3, +- g_UnicodeData_Normalization_Map4}; ++const uint16_t* const kUnicodeDataNormalizationMaps[] = { ++ kUnicodeDataNormalizationMap2, kUnicodeDataNormalizationMap3, ++ kUnicodeDataNormalizationMap4}; + + float NormalizeThreshold(float threshold, int t1, int t2, int t3) { +- ASSERT(t1 < t2); +- ASSERT(t2 < t3); ++ DCHECK(t1 < t2); ++ DCHECK(t2 < t3); + if (threshold < t1) + return threshold / 2.0f; + if (threshold < t2) +@@ -58,8 +64,7 @@ float CalculateBaseSpace(const CPDF_TextObject* pTextObj, + matrix.TransformDistance(pTextObj->m_TextState.GetCharSpace()); + float baseSpace = spacing; + for (size_t i = 0; i < nItems; ++i) { +- CPDF_TextObjectItem item; +- pTextObj->GetItemInfo(i, &item); ++ CPDF_TextObject::Item item = pTextObj->GetItemInfo(i); + if (item.m_CharCode == 0xffffffff) { + float fontsize_h = pTextObj->m_TextState.GetFontSizeH(); + float kerning = -fontsize_h * item.m_Origin.x / 1000; +@@ -73,33 +78,24 @@ float CalculateBaseSpace(const CPDF_TextObject* pTextObj, + return baseSpace; + } + +-size_t Unicode_GetNormalization(wchar_t wch, wchar_t* pDst) { ++DataVector GetUnicodeNormalization(wchar_t wch) { + wch = wch & 0xFFFF; +- wchar_t wFind = g_UnicodeData_Normalization[wch]; +- if (!wFind) { +- if (pDst) +- *pDst = wch; +- return 1; +- } ++ wchar_t wFind = kUnicodeDataNormalization[wch]; ++ if (!wFind) ++ return DataVector(1, wch); ++ + if (wFind >= 0x8000) { +- const uint16_t* pMap = g_UnicodeData_Normalization_Map1 + (wFind - 0x8000); +- if (pDst) +- *pDst = *pMap; +- return 1; ++ return DataVector(1, ++ kUnicodeDataNormalizationMap1[wFind - 0x8000]); + } + + wch = wFind & 0x0FFF; + wFind >>= 12; +- const uint16_t* pMap = g_UnicodeData_Normalization_Maps[wFind - 2] + wch; ++ const uint16_t* pMap = kUnicodeDataNormalizationMaps[wFind - 2] + wch; + if (wFind == 4) + wFind = static_cast(*pMap++); + +- if (pDst) { +- wchar_t n = wFind; +- while (n--) +- *pDst++ = *pMap++; +- } +- return static_cast(wFind); ++ return DataVector(pMap, pMap + wFind); + } + + float MaskPercentFilled(const std::vector& mask, +@@ -143,8 +139,7 @@ bool IsRightToLeft(const CPDF_TextObject& text_obj, const CPDF_Font& font) { + WideString str; + str.Reserve(nItems); + for (size_t i = 0; i < nItems; ++i) { +- CPDF_TextObjectItem item; +- text_obj.GetItemInfo(i, &item); ++ CPDF_TextObject::Item item = text_obj.GetItemInfo(i); + if (item.m_CharCode == 0xffffffff) + continue; + WideString wstrItem = font.UnicodeFromCharCode(item.m_CharCode); +@@ -154,14 +149,15 @@ bool IsRightToLeft(const CPDF_TextObject& text_obj, const CPDF_Font& font) { + if (wChar) + str += wChar; + } +- return CFX_BidiString(str).OverallDirection() == CFX_BidiChar::RIGHT; ++ return CFX_BidiString(str).OverallDirection() == ++ CFX_BidiChar::Direction::kRight; + } + +-uint32_t GetCharWidth(uint32_t charCode, CPDF_Font* pFont) { ++int GetCharWidth(uint32_t charCode, CPDF_Font* pFont) { + if (charCode == CPDF_Font::kInvalidCharCode) + return 0; + +- uint32_t w = pFont->GetCharWidthF(charCode); ++ int w = pFont->GetCharWidthF(charCode); + if (w > 0) + return w; + +@@ -226,13 +222,59 @@ CFX_Matrix GetPageMatrix(const CPDF_Page* pPage) { + return pPage->GetDisplayMatrix(rect, 0); + } + ++float GetFontSize(const CPDF_TextObject* text_object) { ++ bool has_font = text_object && text_object->GetFont(); ++ return has_font ? text_object->GetFontSize() : kDefaultFontSize; ++} ++ ++CFX_FloatRect GetLooseBounds(const CPDF_TextPage::CharInfo& charinfo) { ++ float font_size = GetFontSize(charinfo.m_pTextObj); ++ if (charinfo.m_pTextObj && !FXSYS_IsFloatZero(font_size)) { ++ bool is_vert_writing = charinfo.m_pTextObj->GetFont()->IsVertWriting(); ++ if (is_vert_writing && charinfo.m_pTextObj->GetFont()->IsCIDFont()) { ++ CPDF_CIDFont* pCIDFont = charinfo.m_pTextObj->GetFont()->AsCIDFont(); ++ uint16_t cid = pCIDFont->CIDFromCharCode(charinfo.m_CharCode); ++ ++ CFX_Point16 vertical_origin = pCIDFont->GetVertOrigin(cid); ++ double offsetx = (vertical_origin.x - 500) * font_size / 1000.0; ++ double offsety = vertical_origin.y * font_size / 1000.0; ++ int16_t vert_width = pCIDFont->GetVertWidth(cid); ++ double height = vert_width * font_size / 1000.0; ++ ++ float left = charinfo.m_Origin.x + offsetx; ++ float right = left + font_size; ++ float bottom = charinfo.m_Origin.y + offsety; ++ float top = bottom + height; ++ return CFX_FloatRect(left, bottom, right, top); ++ } ++ ++ int ascent = charinfo.m_pTextObj->GetFont()->GetTypeAscent(); ++ int descent = charinfo.m_pTextObj->GetFont()->GetTypeDescent(); ++ if (ascent != descent) { ++ float width = charinfo.m_Matrix.a * ++ charinfo.m_pTextObj->GetCharWidth(charinfo.m_CharCode); ++ float font_scale = charinfo.m_Matrix.a * font_size / (ascent - descent); ++ ++ float left = charinfo.m_Origin.x; ++ float right = charinfo.m_Origin.x + (is_vert_writing ? -width : width); ++ float bottom = charinfo.m_Origin.y + descent * font_scale; ++ float top = charinfo.m_Origin.y + ascent * font_scale; ++ return CFX_FloatRect(left, bottom, right, top); ++ } ++ } ++ ++ // Fallback to the tight bounds in empty text scenarios, or bad font metrics ++ return charinfo.m_CharBox; ++} ++ + } // namespace + +-PDFTEXT_Obj::PDFTEXT_Obj() {} ++CPDF_TextPage::TransformedTextObject::TransformedTextObject() = default; + +-PDFTEXT_Obj::PDFTEXT_Obj(const PDFTEXT_Obj& that) = default; ++CPDF_TextPage::TransformedTextObject::TransformedTextObject( ++ const TransformedTextObject& that) = default; + +-PDFTEXT_Obj::~PDFTEXT_Obj() {} ++CPDF_TextPage::TransformedTextObject::~TransformedTextObject() = default; + + CPDF_TextPage::CharInfo::CharInfo() = default; + +@@ -253,100 +295,93 @@ void CPDF_TextPage::Init() { + + const int nCount = CountChars(); + if (nCount) +- m_CharIndices.push_back(0); ++ m_CharIndices.push_back({0, 0}); + ++ bool skipped = false; + for (int i = 0; i < nCount; ++i) { + const CharInfo& charinfo = m_CharList[i]; + if (charinfo.m_CharType == CPDF_TextPage::CharType::kGenerated || + (charinfo.m_Unicode != 0 && !IsControlChar(charinfo)) || + (charinfo.m_Unicode == 0 && charinfo.m_CharCode != 0)) { +- if (m_CharIndices.size() % 2) { +- m_CharIndices.push_back(1); +- } else { +- if (m_CharIndices.empty()) +- continue; +- m_CharIndices.back() += 1; +- } ++ m_CharIndices.back().count++; ++ skipped = true; + } else { +- if (m_CharIndices.size() % 2) { +- if (m_CharIndices.empty()) +- continue; +- m_CharIndices.back() = i + 1; ++ if (skipped) { ++ m_CharIndices.push_back({i + 1, 0}); ++ skipped = false; + } else { +- m_CharIndices.push_back(i + 1); ++ m_CharIndices.back().index = i + 1; + } + } + } +- if (m_CharIndices.size() % 2) +- m_CharIndices.pop_back(); + } + + int CPDF_TextPage::CountChars() const { +- return pdfium::CollectionSize(m_CharList); ++ return fxcrt::CollectionSize(m_CharList); + } + + int CPDF_TextPage::CharIndexFromTextIndex(int text_index) const { + int count = 0; +- for (size_t i = 0; i < m_CharIndices.size(); i += 2) { +- count += m_CharIndices[i + 1]; ++ for (const auto& info : m_CharIndices) { ++ count += info.count; + if (count > text_index) +- return text_index - count + m_CharIndices[i + 1] + m_CharIndices[i]; ++ return text_index - count + info.count + info.index; + } + return -1; + } + + int CPDF_TextPage::TextIndexFromCharIndex(int char_index) const { + int count = 0; +- for (size_t i = 0; i < m_CharIndices.size(); i += 2) { +- int text_index = char_index - m_CharIndices[i]; +- if (text_index < m_CharIndices[i + 1]) ++ for (const auto& info : m_CharIndices) { ++ int text_index = char_index - info.index; ++ if (text_index < info.count) + return text_index >= 0 ? text_index + count : -1; + +- count += m_CharIndices[i + 1]; ++ count += info.count; + } + return -1; + } + + std::vector CPDF_TextPage::GetRectArray(int start, +- int nCount) const { ++ int count) const { + std::vector rects; +- if (start < 0 || nCount == 0) ++ if (start < 0 || count == 0) + return rects; + +- const int nCharListSize = CountChars(); +- if (start >= nCharListSize) ++ const int number_of_chars = CountChars(); ++ if (start >= number_of_chars) + return rects; + +- if (nCount < 0 || start + nCount > nCharListSize) +- nCount = nCharListSize - start; +- ASSERT(nCount > 0); ++ if (count < 0 || start + count > number_of_chars) ++ count = number_of_chars - start; ++ DCHECK(count > 0); + +- CPDF_TextObject* pCurObj = nullptr; ++ const CPDF_TextObject* text_object = nullptr; + CFX_FloatRect rect; +- int curPos = start; +- bool bFlagNewRect = true; +- while (nCount--) { +- const CharInfo& info_curchar = m_CharList[curPos++]; +- if (info_curchar.m_CharType == CPDF_TextPage::CharType::kGenerated) ++ int pos = start; ++ bool is_new_rect = true; ++ while (count--) { ++ const CharInfo& charinfo = m_CharList[pos++]; ++ if (charinfo.m_CharType == CPDF_TextPage::CharType::kGenerated) + continue; +- if (info_curchar.m_CharBox.Width() < kSizeEpsilon || +- info_curchar.m_CharBox.Height() < kSizeEpsilon) { ++ if (charinfo.m_CharBox.Width() < kSizeEpsilon || ++ charinfo.m_CharBox.Height() < kSizeEpsilon) { + continue; + } +- if (!pCurObj) +- pCurObj = info_curchar.m_pTextObj.Get(); +- if (pCurObj != info_curchar.m_pTextObj) { ++ if (!text_object) ++ text_object = charinfo.m_pTextObj; ++ if (text_object != charinfo.m_pTextObj) { + rects.push_back(rect); +- pCurObj = info_curchar.m_pTextObj.Get(); +- bFlagNewRect = true; ++ text_object = charinfo.m_pTextObj; ++ is_new_rect = true; + } +- if (bFlagNewRect) { +- bFlagNewRect = false; +- rect = info_curchar.m_CharBox; ++ if (is_new_rect) { ++ is_new_rect = false; ++ rect = charinfo.m_CharBox; + rect.Normalize(); + continue; + } +- rect.Union(info_curchar.m_CharBox); ++ rect.Union(charinfo.m_CharBox); + } + rects.push_back(rect); + return rects; +@@ -441,9 +476,11 @@ const CPDF_TextPage::CharInfo& CPDF_TextPage::GetCharInfo(size_t index) const { + + float CPDF_TextPage::GetCharFontSize(size_t index) const { + CHECK(index < m_CharList.size()); +- const CPDF_TextObject* text_object = m_CharList[index].m_pTextObj.Get(); +- bool has_font = text_object && text_object->GetFont(); +- return has_font ? text_object->GetFontSize() : kDefaultFontSize; ++ return GetFontSize(m_CharList[index].m_pTextObj); ++} ++ ++CFX_FloatRect CPDF_TextPage::GetCharLooseBounds(size_t index) const { ++ return GetLooseBounds(GetCharInfo(index)); + } + + WideString CPDF_TextPage::GetPageText(int start, int count) const { +@@ -494,11 +531,11 @@ int CPDF_TextPage::CountRects(int start, int nCount) { + return -1; + + m_SelRects = GetRectArray(start, nCount); +- return pdfium::CollectionSize(m_SelRects); ++ return fxcrt::CollectionSize(m_SelRects); + } + + bool CPDF_TextPage::GetRect(int rectIndex, CFX_FloatRect* pRect) const { +- if (!pdfium::IndexInBounds(m_SelRects, rectIndex)) ++ if (!fxcrt::IndexInBounds(m_SelRects, rectIndex)) + return false; + + *pRect = m_SelRects[rectIndex]; +@@ -525,13 +562,14 @@ CPDF_TextPage::TextOrientation CPDF_TextPage::FindTextlineFlowOrientation() + if (!pPageObj->IsText()) + continue; + +- int32_t minH = std::max(static_cast(pPageObj->GetRect().left), 0); +- int32_t maxH = +- std::min(static_cast(pPageObj->GetRect().right), nPageWidth); +- int32_t minV = +- std::max(static_cast(pPageObj->GetRect().bottom), 0); +- int32_t maxV = +- std::min(static_cast(pPageObj->GetRect().top), nPageHeight); ++ int32_t minH = static_cast( ++ pdfium::clamp(pPageObj->GetRect().left, 0.0f, nPageWidth)); ++ int32_t maxH = static_cast( ++ pdfium::clamp(pPageObj->GetRect().right, 0.0f, nPageWidth)); ++ int32_t minV = static_cast( ++ pdfium::clamp(pPageObj->GetRect().bottom, 0.0f, nPageHeight)); ++ int32_t maxV = static_cast( ++ pdfium::clamp(pPageObj->GetRect().top, 0.0f, nPageHeight)); + if (minH >= maxH || minV >= maxV) + continue; + +@@ -568,14 +606,14 @@ CPDF_TextPage::TextOrientation CPDF_TextPage::FindTextlineFlowOrientation() + + void CPDF_TextPage::AppendGeneratedCharacter(wchar_t unicode, + const CFX_Matrix& formMatrix) { +- Optional pGenerateChar = GenerateCharInfo(unicode); +- if (!pGenerateChar) ++ absl::optional pGenerateChar = GenerateCharInfo(unicode); ++ if (!pGenerateChar.has_value()) + return; + + m_TextBuf.AppendChar(unicode); + if (!formMatrix.IsIdentity()) + pGenerateChar->m_Matrix = formMatrix; +- m_CharList.push_back(*pGenerateChar); ++ m_CharList.push_back(pGenerateChar.value()); + } + + void CPDF_TextPage::ProcessObject() { +@@ -590,14 +628,14 @@ void CPDF_TextPage::ProcessObject() { + + CFX_Matrix matrix; + if (pObj->IsText()) +- ProcessTextObject(pObj->AsText(), matrix, m_pPage.Get(), it); ++ ProcessTextObject(pObj->AsText(), matrix, m_pPage, it); + else if (pObj->IsForm()) + ProcessFormObject(pObj->AsForm(), matrix); + } +- for (const auto& obj : m_LineObj) ++ for (const auto& obj : mTextObjects) + ProcessTextObject(obj); + +- m_LineObj.clear(); ++ mTextObjects.clear(); + CloseTempLine(); + } + +@@ -625,21 +663,17 @@ void CPDF_TextPage::AddCharInfoByLRDirection(wchar_t wChar, + m_CharList.push_back(info2); + return; + } +- + info2.m_Index = m_TextBuf.GetLength(); +- size_t nCount = 0; ++ DataVector normalized; + if (wChar >= 0xFB00 && wChar <= 0xFB06) +- nCount = Unicode_GetNormalization(wChar, nullptr); +- if (nCount == 0) { ++ normalized = GetUnicodeNormalization(wChar); ++ if (normalized.empty()) { + m_TextBuf.AppendChar(wChar); + m_CharList.push_back(info2); + return; + } +- +- std::unique_ptr pDst(FX_Alloc(wchar_t, nCount)); +- Unicode_GetNormalization(wChar, pDst.get()); +- for (size_t nIndex = 0; nIndex < nCount; ++nIndex) { +- info2.m_Unicode = pDst.get()[nIndex]; ++ for (wchar_t normalized_char : normalized) { ++ info2.m_Unicode = normalized_char; + info2.m_CharType = CPDF_TextPage::CharType::kPiece; + m_TextBuf.AppendChar(info2.m_Unicode); + m_CharList.push_back(info2); +@@ -654,21 +688,17 @@ void CPDF_TextPage::AddCharInfoByRLDirection(wchar_t wChar, + m_CharList.push_back(info2); + return; + } +- + info2.m_Index = m_TextBuf.GetLength(); +- wChar = FX_GetMirrorChar(wChar); +- size_t nCount = Unicode_GetNormalization(wChar, nullptr); +- if (nCount == 0) { ++ wChar = pdfium::unicode::GetMirrorChar(wChar); ++ DataVector normalized = GetUnicodeNormalization(wChar); ++ if (normalized.empty()) { + info2.m_Unicode = wChar; + m_TextBuf.AppendChar(info2.m_Unicode); + m_CharList.push_back(info2); + return; + } +- +- std::unique_ptr pDst(FX_Alloc(wchar_t, nCount)); +- Unicode_GetNormalization(wChar, pDst.get()); +- for (size_t nIndex = 0; nIndex < nCount; ++nIndex) { +- info2.m_Unicode = pDst.get()[nIndex]; ++ for (wchar_t normalized_char : normalized) { ++ info2.m_Unicode = normalized_char; + info2.m_CharType = CPDF_TextPage::CharType::kPiece; + m_TextBuf.AppendChar(info2.m_Unicode); + m_CharList.push_back(info2); +@@ -699,14 +729,16 @@ void CPDF_TextPage::CloseTempLine() { + bidi.SetOverallDirectionRight(); + CFX_BidiChar::Direction eCurrentDirection = bidi.OverallDirection(); + for (const auto& segment : bidi) { +- if (segment.direction == CFX_BidiChar::RIGHT || +- (segment.direction == CFX_BidiChar::NEUTRAL && +- eCurrentDirection == CFX_BidiChar::RIGHT)) { +- eCurrentDirection = CFX_BidiChar::RIGHT; ++ if (segment.direction == CFX_BidiChar::Direction::kRight || ++ (segment.direction == CFX_BidiChar::Direction::kNeutral && ++ eCurrentDirection == CFX_BidiChar::Direction::kRight)) { ++ eCurrentDirection = CFX_BidiChar::Direction::kRight; + for (int m = segment.start + segment.count; m > segment.start; --m) + AddCharInfoByRLDirection(str[m - 1], m_TempCharList[m - 1]); + } else { +- eCurrentDirection = CFX_BidiChar::LEFT; ++ if (segment.direction != CFX_BidiChar::Direction::kLeftWeak) { ++ eCurrentDirection = CFX_BidiChar::Direction::kLeft; ++ } + for (int m = segment.start; m < segment.start + segment.count; ++m) + AddCharInfoByLRDirection(str[m], m_TempCharList[m]); + } +@@ -723,32 +755,31 @@ void CPDF_TextPage::ProcessTextObject( + if (fabs(pTextObj->GetRect().Width()) < kSizeEpsilon) + return; + +- size_t count = m_LineObj.size(); +- PDFTEXT_Obj Obj; +- Obj.m_pTextObj = pTextObj; +- Obj.m_formMatrix = formMatrix; ++ size_t count = mTextObjects.size(); ++ TransformedTextObject new_obj; ++ new_obj.m_pTextObj = pTextObj; ++ new_obj.m_formMatrix = formMatrix; + if (count == 0) { +- m_LineObj.push_back(Obj); ++ mTextObjects.push_back(new_obj); + return; + } + if (IsSameAsPreTextObject(pTextObj, pObjList, ObjPos)) + return; + +- PDFTEXT_Obj prev_Obj = m_LineObj[count - 1]; +- size_t nItem = prev_Obj.m_pTextObj->CountItems(); ++ TransformedTextObject prev_obj = mTextObjects[count - 1]; ++ size_t nItem = prev_obj.m_pTextObj->CountItems(); + if (nItem == 0) + return; + +- CPDF_TextObjectItem item; +- prev_Obj.m_pTextObj->GetItemInfo(nItem - 1, &item); ++ CPDF_TextObject::Item item = prev_obj.m_pTextObj->GetItemInfo(nItem - 1); + float prev_width = +- GetCharWidth(item.m_CharCode, prev_Obj.m_pTextObj->GetFont().Get()) * +- prev_Obj.m_pTextObj->GetFontSize() / 1000; ++ GetCharWidth(item.m_CharCode, prev_obj.m_pTextObj->GetFont().Get()) * ++ prev_obj.m_pTextObj->GetFontSize() / 1000; + + CFX_Matrix prev_matrix = +- prev_Obj.m_pTextObj->GetTextMatrix() * prev_Obj.m_formMatrix; ++ prev_obj.m_pTextObj->GetTextMatrix() * prev_obj.m_formMatrix; + prev_width = prev_matrix.TransformDistance(fabs(prev_width)); +- pTextObj->GetItemInfo(0, &item); ++ item = pTextObj->GetItemInfo(0); + float this_width = GetCharWidth(item.m_CharCode, pTextObj->GetFont().Get()) * + pTextObj->GetFontSize() / 1000; + this_width = fabs(this_width); +@@ -758,46 +789,46 @@ void CPDF_TextPage::ProcessTextObject( + + float threshold = std::max(prev_width, this_width) / 4; + CFX_PointF prev_pos = m_DisplayMatrix.Transform( +- prev_Obj.m_formMatrix.Transform(prev_Obj.m_pTextObj->GetPos())); ++ prev_obj.m_formMatrix.Transform(prev_obj.m_pTextObj->GetPos())); + CFX_PointF this_pos = + m_DisplayMatrix.Transform(formMatrix.Transform(pTextObj->GetPos())); + if (fabs(this_pos.y - prev_pos.y) > threshold * 2) { + for (size_t i = 0; i < count; ++i) +- ProcessTextObject(m_LineObj[i]); +- m_LineObj.clear(); +- m_LineObj.push_back(Obj); ++ ProcessTextObject(mTextObjects[i]); ++ mTextObjects.clear(); ++ mTextObjects.push_back(new_obj); + return; + } + + for (size_t i = count; i > 0; --i) { +- PDFTEXT_Obj prev_text_obj = m_LineObj[i - 1]; ++ TransformedTextObject prev_text_obj = mTextObjects[i - 1]; + CFX_PointF new_prev_pos = + m_DisplayMatrix.Transform(prev_text_obj.m_formMatrix.Transform( + prev_text_obj.m_pTextObj->GetPos())); + if (this_pos.x >= new_prev_pos.x) { +- m_LineObj.insert(m_LineObj.begin() + i, Obj); ++ mTextObjects.insert(mTextObjects.begin() + i, new_obj); + return; + } + } +- m_LineObj.insert(m_LineObj.begin(), Obj); ++ mTextObjects.insert(mTextObjects.begin(), new_obj); + } + + CPDF_TextPage::MarkedContentState CPDF_TextPage::PreMarkedContent( +- PDFTEXT_Obj Obj) { +- CPDF_TextObject* pTextObj = Obj.m_pTextObj.Get(); +- size_t nContentMarks = pTextObj->m_ContentMarks.CountItems(); ++ const CPDF_TextObject* pTextObj) { ++ const CPDF_ContentMarks* pMarks = pTextObj->GetContentMarks(); ++ const size_t nContentMarks = pMarks->CountItems(); + if (nContentMarks == 0) + return MarkedContentState::kPass; + + WideString actText; + bool bExist = false; +- const CPDF_Dictionary* pDict = nullptr; ++ RetainPtr pDict; + for (size_t i = 0; i < nContentMarks; ++i) { +- const CPDF_ContentMarkItem* item = pTextObj->m_ContentMarks.GetItem(i); ++ const CPDF_ContentMarkItem* item = pMarks->GetItem(i); + pDict = item->GetParam(); + if (!pDict) + continue; +- const CPDF_String* temp = ToString(pDict->GetObjectFor("ActualText")); ++ RetainPtr temp = pDict->GetStringFor("ActualText"); + if (temp) { + bExist = true; + actText = temp->GetUnicodeText(); +@@ -807,9 +838,9 @@ CPDF_TextPage::MarkedContentState CPDF_TextPage::PreMarkedContent( + return MarkedContentState::kPass; + + if (m_pPrevTextObj) { +- const CPDF_ContentMarks& marks = m_pPrevTextObj->m_ContentMarks; +- if (marks.CountItems() == nContentMarks && +- marks.GetItem(nContentMarks - 1)->GetParam() == pDict) { ++ const CPDF_ContentMarks* pPrevMarks = m_pPrevTextObj->GetContentMarks(); ++ if (pPrevMarks->CountItems() == nContentMarks && ++ pPrevMarks->GetItem(nContentMarks - 1)->GetParam() == pDict) { + return MarkedContentState::kDone; + } + } +@@ -842,17 +873,14 @@ CPDF_TextPage::MarkedContentState CPDF_TextPage::PreMarkedContent( + return MarkedContentState::kDelay; + } + +-void CPDF_TextPage::ProcessMarkedContent(PDFTEXT_Obj Obj) { +- CPDF_TextObject* pTextObj = Obj.m_pTextObj.Get(); +- +- size_t nContentMarks = pTextObj->m_ContentMarks.CountItems(); +- if (nContentMarks == 0) +- return; +- ++void CPDF_TextPage::ProcessMarkedContent(const TransformedTextObject& obj) { ++ const CPDF_TextObject* pTextObj = obj.m_pTextObj; ++ const CPDF_ContentMarks* pMarks = pTextObj->GetContentMarks(); ++ const size_t nContentMarks = pMarks->CountItems(); + WideString actText; + for (size_t n = 0; n < nContentMarks; ++n) { +- const CPDF_ContentMarkItem* item = pTextObj->m_ContentMarks.GetItem(n); +- const CPDF_Dictionary* pDict = item->GetParam(); ++ const CPDF_ContentMarkItem* item = pMarks->GetItem(n); ++ RetainPtr pDict = item->GetParam(); + if (pDict) + actText = pDict->GetUnicodeTextFor("ActualText"); + } +@@ -860,7 +888,7 @@ void CPDF_TextPage::ProcessMarkedContent(PDFTEXT_Obj Obj) { + return; + + RetainPtr pFont = pTextObj->GetFont(); +- CFX_Matrix matrix = pTextObj->GetTextMatrix() * Obj.m_formMatrix; ++ CFX_Matrix matrix = pTextObj->GetTextMatrix() * obj.m_formMatrix; + + for (size_t k = 0; k < actText.GetLength(); ++k) { + wchar_t wChar = actText[k]; +@@ -892,67 +920,69 @@ void CPDF_TextPage::FindPreviousTextObject() { + m_pPrevTextObj = pPrevCharInfo->m_pTextObj; + } + +-void CPDF_TextPage::SwapTempTextBuf(int32_t iCharListStartAppend, +- int32_t iBufStartAppend) { +- int32_t i = iCharListStartAppend; +- int32_t j = pdfium::CollectionSize(m_TempCharList) - 1; +- for (; i < j; ++i, --j) { +- std::swap(m_TempCharList[i], m_TempCharList[j]); +- std::swap(m_TempCharList[i].m_Index, m_TempCharList[j].m_Index); ++void CPDF_TextPage::SwapTempTextBuf(size_t iCharListStartAppend, ++ size_t iBufStartAppend) { ++ DCHECK(!m_TempCharList.empty()); ++ if (iCharListStartAppend < m_TempCharList.size()) { ++ auto fwd = m_TempCharList.begin() + iCharListStartAppend; ++ auto rev = m_TempCharList.end() - 1; ++ for (; fwd < rev; ++fwd, --rev) { ++ std::swap(*fwd, *rev); ++ std::swap(fwd->m_Index, rev->m_Index); ++ } ++ } ++ pdfium::span temp_span = m_TempTextBuf.GetWideSpan(); ++ DCHECK(!temp_span.empty()); ++ if (iBufStartAppend < temp_span.size()) { ++ std::reverse(temp_span.begin() + iBufStartAppend, temp_span.end()); + } +- wchar_t* pTempBuffer = m_TempTextBuf.GetBuffer(); +- i = iBufStartAppend; +- j = m_TempTextBuf.GetLength() - 1; +- for (; i < j; ++i, --j) +- std::swap(pTempBuffer[i], pTempBuffer[j]); + } + +-void CPDF_TextPage::ProcessTextObject(PDFTEXT_Obj Obj) { +- CPDF_TextObject* pTextObj = Obj.m_pTextObj.Get(); ++void CPDF_TextPage::ProcessTextObject(const TransformedTextObject& obj) { ++ const CPDF_TextObject* pTextObj = obj.m_pTextObj; + if (fabs(pTextObj->GetRect().Width()) < kSizeEpsilon) + return; + +- CFX_Matrix formMatrix = Obj.m_formMatrix; ++ CFX_Matrix form_matrix = obj.m_formMatrix; + RetainPtr pFont = pTextObj->GetFont(); +- CFX_Matrix matrix = pTextObj->GetTextMatrix() * formMatrix; +- MarkedContentState ePreMKC = PreMarkedContent(Obj); ++ CFX_Matrix matrix = pTextObj->GetTextMatrix() * form_matrix; ++ MarkedContentState ePreMKC = PreMarkedContent(obj.m_pTextObj); + if (ePreMKC == MarkedContentState::kDone) { + m_pPrevTextObj = pTextObj; +- m_PrevMatrix = formMatrix; ++ m_PrevMatrix = form_matrix; + return; + } + GenerateCharacter result = GenerateCharacter::kNone; + if (m_pPrevTextObj) { +- result = ProcessInsertObject(pTextObj, formMatrix); ++ result = ProcessInsertObject(pTextObj, form_matrix); + if (result == GenerateCharacter::kLineBreak) +- m_CurlineRect = Obj.m_pTextObj->GetRect(); ++ m_CurlineRect = pTextObj->GetRect(); + else +- m_CurlineRect.Union(Obj.m_pTextObj->GetRect()); ++ m_CurlineRect.Union(obj.m_pTextObj->GetRect()); + + switch (result) { + case GenerateCharacter::kNone: + break; + case GenerateCharacter::kSpace: { +- Optional pGenerateChar = GenerateCharInfo(L' '); +- if (pGenerateChar) { +- if (!formMatrix.IsIdentity()) +- pGenerateChar->m_Matrix = formMatrix; ++ absl::optional pGenerateChar = GenerateCharInfo(L' '); ++ if (pGenerateChar.has_value()) { ++ if (!form_matrix.IsIdentity()) ++ pGenerateChar->m_Matrix = form_matrix; + m_TempTextBuf.AppendChar(L' '); +- m_TempCharList.push_back(*pGenerateChar); ++ m_TempCharList.push_back(pGenerateChar.value()); + } + break; + } + case GenerateCharacter::kLineBreak: + CloseTempLine(); + if (m_TextBuf.GetSize()) { +- AppendGeneratedCharacter(L'\r', formMatrix); +- AppendGeneratedCharacter(L'\n', formMatrix); ++ AppendGeneratedCharacter(L'\r', form_matrix); ++ AppendGeneratedCharacter(L'\n', form_matrix); + } + break; + case GenerateCharacter::kHyphen: + if (pTextObj->CountChars() == 1) { +- CPDF_TextObjectItem item; +- pTextObj->GetCharInfo(0, &item); ++ CPDF_TextObject::Item item = pTextObj->GetCharInfo(0); + WideString wstrItem = + pTextObj->GetFont()->UnicodeFromCharCode(item.m_CharCode); + if (wstrItem.IsEmpty()) +@@ -974,32 +1004,30 @@ void CPDF_TextPage::ProcessTextObject(PDFTEXT_Obj Obj) { + break; + } + } else { +- m_CurlineRect = Obj.m_pTextObj->GetRect(); ++ m_CurlineRect = pTextObj->GetRect(); + } + + if (ePreMKC == MarkedContentState::kDelay) { +- ProcessMarkedContent(Obj); ++ ProcessMarkedContent(obj); + m_pPrevTextObj = pTextObj; +- m_PrevMatrix = formMatrix; ++ m_PrevMatrix = form_matrix; + return; + } + m_pPrevTextObj = pTextObj; +- m_PrevMatrix = formMatrix; ++ m_PrevMatrix = form_matrix; + float baseSpace = CalculateBaseSpace(pTextObj, matrix); + + const bool bR2L = IsRightToLeft(*pTextObj, *pFont); + const bool bIsBidiAndMirrorInverse = + bR2L && (matrix.a * matrix.d - matrix.b * matrix.c) < 0; +- int32_t iBufStartAppend = m_TempTextBuf.GetLength(); +- int32_t iCharListStartAppend = +- pdfium::CollectionSize(m_TempCharList); ++ const size_t iBufStartAppend = m_TempTextBuf.GetLength(); ++ const size_t iCharListStartAppend = m_TempCharList.size(); + + float spacing = 0; + const size_t nItems = pTextObj->CountItems(); + for (size_t i = 0; i < nItems; ++i) { +- CPDF_TextObjectItem item; + CharInfo charinfo; +- pTextObj->GetItemInfo(i, &item); ++ CPDF_TextObject::Item item = pTextObj->GetItemInfo(i); + if (item.m_CharCode == 0xffffffff) { + WideString str = m_TempTextBuf.MakeString(); + if (str.IsEmpty()) +@@ -1039,7 +1067,7 @@ void CPDF_TextPage::ProcessTextObject(PDFTEXT_Obj Obj) { + charinfo.m_Index = m_TextBuf.GetLength(); + m_TempTextBuf.AppendChar(L' '); + charinfo.m_CharCode = CPDF_Font::kInvalidCharCode; +- charinfo.m_Matrix = formMatrix; ++ charinfo.m_Matrix = form_matrix; + charinfo.m_Origin = matrix.Transform(item.m_Origin); + charinfo.m_CharBox = + CFX_FloatRect(charinfo.m_Origin.x, charinfo.m_Origin.y, +@@ -1072,8 +1100,7 @@ void CPDF_TextPage::ProcessTextObject(PDFTEXT_Obj Obj) { + charinfo.m_CharBox.bottom = rect.bottom * fFontSize + item.m_Origin.y; + if (fabsf(charinfo.m_CharBox.top - charinfo.m_CharBox.bottom) < + kSizeEpsilon) { +- charinfo.m_CharBox.top = +- charinfo.m_CharBox.bottom + pTextObj->GetFontSize(); ++ charinfo.m_CharBox.top = charinfo.m_CharBox.bottom + fFontSize; + } + if (fabsf(charinfo.m_CharBox.right - charinfo.m_CharBox.left) < + kSizeEpsilon) { +@@ -1088,14 +1115,14 @@ void CPDF_TextPage::ProcessTextObject(PDFTEXT_Obj Obj) { + m_TempTextBuf.AppendChar(0xfffe); + continue; + } +- int nTotal = wstrItem.GetLength(); ++ size_t nTotal = wstrItem.GetLength(); + bool bDel = false; +- const int count = std::min(pdfium::CollectionSize(m_TempCharList), 7); ++ const int count = std::min(fxcrt::CollectionSize(m_TempCharList), 7); + constexpr float kTextCharRatioGapDelta = 0.07f; + float threshold = charinfo.m_Matrix.TransformXDistance( + kTextCharRatioGapDelta * pTextObj->GetFontSize()); +- for (int n = pdfium::CollectionSize(m_TempCharList); +- n > pdfium::CollectionSize(m_TempCharList) - count; --n) { ++ for (int n = fxcrt::CollectionSize(m_TempCharList); ++ n > fxcrt::CollectionSize(m_TempCharList) - count; --n) { + const CharInfo& charinfo1 = m_TempCharList[n - 1]; + CFX_PointF diff = charinfo1.m_Origin - charinfo.m_Origin; + if (charinfo1.m_CharCode == charinfo.m_CharCode && +@@ -1106,7 +1133,7 @@ void CPDF_TextPage::ProcessTextObject(PDFTEXT_Obj Obj) { + } + } + if (!bDel) { +- for (int nIndex = 0; nIndex < nTotal; ++nIndex) { ++ for (size_t nIndex = 0; nIndex < nTotal; ++nIndex) { + charinfo.m_Unicode = wstrItem[nIndex]; + if (charinfo.m_Unicode) { + charinfo.m_Index = m_TextBuf.GetLength(); +@@ -1134,10 +1161,8 @@ CPDF_TextPage::TextOrientation CPDF_TextPage::GetTextObjectWritingMode( + if (nChars <= 1) + return m_TextlineDir; + +- CPDF_TextObjectItem first, last; +- pTextObj->GetCharInfo(0, &first); +- pTextObj->GetCharInfo(nChars - 1, &last); +- ++ CPDF_TextObject::Item first = pTextObj->GetCharInfo(0); ++ CPDF_TextObject::Item last = pTextObj->GetCharInfo(nChars - 1); + CFX_Matrix textMatrix = pTextObj->GetTextMatrix(); + first.m_Origin = textMatrix.Transform(first.m_Origin); + last.m_Origin = textMatrix.Transform(last.m_Origin); +@@ -1197,24 +1222,20 @@ CPDF_TextPage::GenerateCharacter CPDF_TextPage::ProcessInsertObject( + FindPreviousTextObject(); + TextOrientation WritingMode = GetTextObjectWritingMode(pObj); + if (WritingMode == TextOrientation::kUnknown) +- WritingMode = GetTextObjectWritingMode(m_pPrevTextObj.Get()); ++ WritingMode = GetTextObjectWritingMode(m_pPrevTextObj); + + size_t nItem = m_pPrevTextObj->CountItems(); + if (nItem == 0) + return GenerateCharacter::kNone; + +- CPDF_TextObjectItem PrevItem; +- m_pPrevTextObj->GetItemInfo(nItem - 1, &PrevItem); +- +- CPDF_TextObjectItem item; +- pObj->GetItemInfo(0, &item); +- ++ CPDF_TextObject::Item PrevItem = m_pPrevTextObj->GetItemInfo(nItem - 1); ++ CPDF_TextObject::Item item = pObj->GetItemInfo(0); + const CFX_FloatRect& this_rect = pObj->GetRect(); + const CFX_FloatRect& prev_rect = m_pPrevTextObj->GetRect(); +- + WideString wstrItem = pObj->GetFont()->UnicodeFromCharCode(item.m_CharCode); + if (wstrItem.IsEmpty()) + wstrItem += static_cast(item.m_CharCode); ++ + wchar_t curChar = wstrItem[0]; + if (WritingMode == TextOrientation::kHorizontal) { + if (EndHorizontalLine(this_rect, prev_rect)) { +@@ -1230,11 +1251,11 @@ CPDF_TextPage::GenerateCharacter CPDF_TextPage::ProcessInsertObject( + } + + float last_pos = PrevItem.m_Origin.x; +- uint32_t nLastWidth = ++ int nLastWidth = + GetCharWidth(PrevItem.m_CharCode, m_pPrevTextObj->GetFont().Get()); + float last_width = nLastWidth * m_pPrevTextObj->GetFontSize() / 1000; + last_width = fabs(last_width); +- uint32_t nThisWidth = GetCharWidth(item.m_CharCode, pObj->GetFont().Get()); ++ int nThisWidth = GetCharWidth(item.m_CharCode, pObj->GetFont().Get()); + float this_width = fabs(nThisWidth * pObj->GetFontSize() / 1000); + float threshold = std::max(last_width, this_width) / 4; + +@@ -1255,8 +1276,7 @@ CPDF_TextPage::GenerateCharacter CPDF_TextPage::ProcessInsertObject( + (fabs(pos.y) >= 1 || fabs(pos.y) > fabs(pos.x)))) { + bNewline = true; + if (nItem > 1) { +- CPDF_TextObjectItem tempItem; +- m_pPrevTextObj->GetItemInfo(0, &tempItem); ++ CPDF_TextObject::Item tempItem = m_pPrevTextObj->GetItemInfo(0); + CFX_Matrix m = m_pPrevTextObj->GetTextMatrix(); + if (PrevItem.m_Origin.x > tempItem.m_Origin.x && + m_DisplayMatrix.a > 0.9 && m_DisplayMatrix.b < 0.1 && +@@ -1349,11 +1369,11 @@ bool CPDF_TextPage::IsSameTextObject(CPDF_TextObject* pTextObj1, + if (nPreCount == 0) + return true; + +- CPDF_TextObjectItem itemPer; +- CPDF_TextObjectItem itemCur; ++ CPDF_TextObject::Item itemPer; ++ CPDF_TextObject::Item itemCur; + for (size_t i = 0; i < nPreCount; ++i) { +- pTextObj2->GetItemInfo(i, &itemPer); +- pTextObj1->GetItemInfo(i, &itemCur); ++ itemPer = pTextObj2->GetItemInfo(i); ++ itemCur = pTextObj1->GetItemInfo(i); + if (itemCur.m_CharCode != itemPer.m_CharCode) + return false; + } +@@ -1385,11 +1405,11 @@ bool CPDF_TextPage::IsSameAsPreTextObject( + return false; + } + +-Optional CPDF_TextPage::GenerateCharInfo( ++absl::optional CPDF_TextPage::GenerateCharInfo( + wchar_t unicode) { + const CharInfo* pPrevCharInfo = GetPrevCharInfo(); + if (!pPrevCharInfo) +- return {}; ++ return absl::nullopt; + + CharInfo info; + info.m_Index = m_TextBuf.GetLength(); +diff --git a/core/fpdftext/cpdf_textpage.h b/core/fpdftext/cpdf_textpage.h +index bac718129..325e2a4d3 100644 +--- a/core/fpdftext/cpdf_textpage.h ++++ b/core/fpdftext/cpdf_textpage.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,31 +7,32 @@ + #ifndef CORE_FPDFTEXT_CPDF_TEXTPAGE_H_ + #define CORE_FPDFTEXT_CPDF_TEXTPAGE_H_ + ++#include ++ + #include + #include + #include + + #include "core/fpdfapi/page/cpdf_pageobjectholder.h" +-#include "core/fxcrt/cfx_widetextbuf.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/fx_memory_wrappers.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "third_party/base/optional.h" ++#include "core/fxcrt/widestring.h" ++#include "core/fxcrt/widetext_buffer.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + +-class CPDF_Font; + class CPDF_FormObject; + class CPDF_Page; + class CPDF_TextObject; + +-struct PDFTEXT_Obj { +- PDFTEXT_Obj(); +- PDFTEXT_Obj(const PDFTEXT_Obj& that); +- ~PDFTEXT_Obj(); +- +- UnownedPtr m_pTextObj; +- CFX_Matrix m_formMatrix; ++struct TextPageCharSegment { ++ int index; ++ int count; + }; + ++FX_DATA_PARTITION_EXCEPTION(TextPageCharSegment); ++ + class CPDF_TextPage { + public: + enum class CharType : uint8_t { +@@ -54,7 +55,7 @@ class CPDF_TextPage { + CharType m_CharType = CharType::kNormal; + CFX_PointF m_Origin; + CFX_FloatRect m_CharBox; +- UnownedPtr m_pTextObj; ++ UnownedPtr m_pTextObj; + CFX_Matrix m_Matrix; + }; + +@@ -69,8 +70,9 @@ class CPDF_TextPage { + // These methods CHECK() to make sure |index| is within bounds. + const CharInfo& GetCharInfo(size_t index) const; + float GetCharFontSize(size_t index) const; ++ CFX_FloatRect GetCharLooseBounds(size_t index) const; + +- std::vector GetRectArray(int start, int nCount) const; ++ std::vector GetRectArray(int start, int count) const; + int GetIndexAtPos(const CFX_PointF& point, const CFX_SizeF& tolerance) const; + WideString GetTextByRect(const CFX_FloatRect& rect) const; + WideString GetTextByObject(const CPDF_TextObject* pTextObj) const; +@@ -100,12 +102,21 @@ class CPDF_TextPage { + + enum class MarkedContentState { kPass = 0, kDone, kDelay }; + ++ struct TransformedTextObject { ++ TransformedTextObject(); ++ TransformedTextObject(const TransformedTextObject& that); ++ ~TransformedTextObject(); ++ ++ UnownedPtr m_pTextObj; ++ CFX_Matrix m_formMatrix; ++ }; ++ + void Init(); + bool IsHyphen(wchar_t curChar) const; + void ProcessObject(); + void ProcessFormObject(CPDF_FormObject* pFormObj, + const CFX_Matrix& formMatrix); +- void ProcessTextObject(PDFTEXT_Obj pObj); ++ void ProcessTextObject(const TransformedTextObject& obj); + void ProcessTextObject(CPDF_TextObject* pTextObj, + const CFX_Matrix& formMatrix, + const CPDF_PageObjectHolder* pObjList, +@@ -113,15 +124,15 @@ class CPDF_TextPage { + GenerateCharacter ProcessInsertObject(const CPDF_TextObject* pObj, + const CFX_Matrix& formMatrix); + const CharInfo* GetPrevCharInfo() const; +- Optional GenerateCharInfo(wchar_t unicode); ++ absl::optional GenerateCharInfo(wchar_t unicode); + bool IsSameAsPreTextObject(CPDF_TextObject* pTextObj, + const CPDF_PageObjectHolder* pObjList, + CPDF_PageObjectHolder::const_iterator iter) const; + bool IsSameTextObject(CPDF_TextObject* pTextObj1, + CPDF_TextObject* pTextObj2) const; + void CloseTempLine(); +- MarkedContentState PreMarkedContent(PDFTEXT_Obj pObj); +- void ProcessMarkedContent(PDFTEXT_Obj pObj); ++ MarkedContentState PreMarkedContent(const CPDF_TextObject* pTextObj); ++ void ProcessMarkedContent(const TransformedTextObject& obj); + void FindPreviousTextObject(); + void AddCharInfoByLRDirection(wchar_t wChar, const CharInfo& info); + void AddCharInfoByRLDirection(wchar_t wChar, const CharInfo& info); +@@ -129,22 +140,22 @@ class CPDF_TextPage { + const CPDF_TextObject* pTextObj) const; + TextOrientation FindTextlineFlowOrientation() const; + void AppendGeneratedCharacter(wchar_t unicode, const CFX_Matrix& formMatrix); +- void SwapTempTextBuf(int32_t iCharListStartAppend, int32_t iBufStartAppend); ++ void SwapTempTextBuf(size_t iCharListStartAppend, size_t iBufStartAppend); + WideString GetTextByPredicate( + const std::function& predicate) const; + + UnownedPtr const m_pPage; +- std::vector m_CharIndices; ++ DataVector m_CharIndices; + std::deque m_CharList; + std::deque m_TempCharList; +- CFX_WideTextBuf m_TextBuf; +- CFX_WideTextBuf m_TempTextBuf; +- UnownedPtr m_pPrevTextObj; ++ WideTextBuffer m_TextBuf; ++ WideTextBuffer m_TempTextBuf; ++ UnownedPtr m_pPrevTextObj; + CFX_Matrix m_PrevMatrix; + const bool m_rtl; + const CFX_Matrix m_DisplayMatrix; + std::vector m_SelRects; +- std::vector m_LineObj; ++ std::vector mTextObjects; + TextOrientation m_TextlineDir = TextOrientation::kUnknown; + CFX_FloatRect m_CurlineRect; + }; +diff --git a/core/fpdftext/cpdf_textpagefind.cpp b/core/fpdftext/cpdf_textpagefind.cpp +index 3df499f73..3f4ed4aec 100644 +--- a/core/fpdftext/cpdf_textpagefind.cpp ++++ b/core/fpdftext/cpdf_textpagefind.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,16 +6,18 @@ + + #include "core/fpdftext/cpdf_textpagefind.h" + +-#include +-#include ++#include ++ + #include + + #include "core/fpdftext/cpdf_textpage.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_string.h" + #include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/fx_unicode.h" ++#include "core/fxcrt/stl_util.h" ++#include "third_party/base/check.h" + #include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" + + namespace { + +@@ -89,25 +91,25 @@ WideString GetStringCase(const WideString& wsOriginal, bool bMatchCase) { + return wsLower; + } + +-Optional ExtractSubString(const wchar_t* lpszFullString, +- int iSubString) { +- ASSERT(lpszFullString); ++absl::optional ExtractSubString(const wchar_t* lpszFullString, ++ int iSubString) { ++ DCHECK(lpszFullString); + + while (iSubString--) { +- lpszFullString = std::wcschr(lpszFullString, L' '); ++ lpszFullString = wcschr(lpszFullString, L' '); + if (!lpszFullString) +- return {}; ++ return absl::nullopt; + + lpszFullString++; + while (*lpszFullString == L' ') + lpszFullString++; + } + +- const wchar_t* lpchEnd = std::wcschr(lpszFullString, L' '); ++ const wchar_t* lpchEnd = wcschr(lpszFullString, L' '); + int nLen = lpchEnd ? static_cast(lpchEnd - lpszFullString) + : static_cast(wcslen(lpszFullString)); + if (nLen < 0) +- return {}; ++ return absl::nullopt; + + return WideString(lpszFullString, static_cast(nLen)); + } +@@ -126,9 +128,9 @@ std::vector ExtractFindWhat(const WideString& findwhat) { + } + + int index = 0; +- while (1) { +- Optional word = ExtractSubString(findwhat.c_str(), index); +- if (!word) ++ while (true) { ++ absl::optional word = ExtractSubString(findwhat.c_str(), index); ++ if (!word.has_value()) + break; + + if (word->IsEmpty()) { +@@ -140,9 +142,9 @@ std::vector ExtractFindWhat(const WideString& findwhat) { + size_t pos = 0; + while (pos < word->GetLength()) { + WideString curStr = word->Substr(pos, 1); +- wchar_t curChar = (*word)[pos]; ++ wchar_t curChar = word.value()[pos]; + if (IsIgnoreSpaceCharacter(curChar)) { +- if (pos > 0 && curChar == 0x2019) { ++ if (pos > 0 && curChar == pdfium::unicode::kRightSingleQuotationMark) { + pos++; + continue; + } +@@ -174,7 +176,7 @@ std::unique_ptr CPDF_TextPageFind::Create( + const CPDF_TextPage* pTextPage, + const WideString& findwhat, + const Options& options, +- Optional startPos) { ++ absl::optional startPos) { + std::vector findwhat_array = + ExtractFindWhat(GetStringCase(findwhat, options.bMatchCase)); + auto find = pdfium::WrapUnique( +@@ -187,7 +189,7 @@ CPDF_TextPageFind::CPDF_TextPageFind( + const CPDF_TextPage* pTextPage, + const std::vector& findwhat_array, + const Options& options, +- Optional startPos) ++ absl::optional startPos) + : m_pTextPage(pTextPage), + m_strText(GetStringCase(pTextPage->GetAllPageText(), options.bMatchCase)), + m_csFindWhatArray(findwhat_array), +@@ -216,8 +218,8 @@ bool CPDF_TextPageFind::FindNext() { + if (m_findNextStart.value() > strLen - 1) + return false; + +- int nCount = pdfium::CollectionSize(m_csFindWhatArray); +- Optional nResultPos = 0; ++ int nCount = fxcrt::CollectionSize(m_csFindWhatArray); ++ absl::optional nResultPos = 0; + size_t nStartPos = m_findNextStart.value(); + bool bSpaceStart = false; + for (int iWord = 0; iWord < nCount; iWord++) { +@@ -299,8 +301,7 @@ bool CPDF_TextPageFind::FindPrev() { + if (m_strText.IsEmpty() || !m_findPreStart.has_value()) + return false; + +- CPDF_TextPageFind find_engine(m_pTextPage.Get(), m_csFindWhatArray, m_options, +- 0); ++ CPDF_TextPageFind find_engine(m_pTextPage, m_csFindWhatArray, m_options, 0); + if (!find_engine.FindFirst()) + return false; + +diff --git a/core/fpdftext/cpdf_textpagefind.h b/core/fpdftext/cpdf_textpagefind.h +index c3b295630..ee5c46e5f 100644 +--- a/core/fpdftext/cpdf_textpagefind.h ++++ b/core/fpdftext/cpdf_textpagefind.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,14 +7,15 @@ + #ifndef CORE_FPDFTEXT_CPDF_TEXTPAGEFIND_H_ + #define CORE_FPDFTEXT_CPDF_TEXTPAGEFIND_H_ + ++#include ++ + #include + #include + + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "third_party/base/optional.h" ++#include "core/fxcrt/widestring.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CPDF_TextPage; + +@@ -30,7 +31,7 @@ class CPDF_TextPageFind { + const CPDF_TextPage* pTextPage, + const WideString& findwhat, + const Options& options, +- Optional startPos); ++ absl::optional startPos); + + ~CPDF_TextPageFind(); + +@@ -43,7 +44,7 @@ class CPDF_TextPageFind { + CPDF_TextPageFind(const CPDF_TextPage* pTextPage, + const std::vector& findwhat_array, + const Options& options, +- Optional startPos); ++ absl::optional startPos); + + // Should be called immediately after construction. + bool FindFirst(); +@@ -53,8 +54,8 @@ class CPDF_TextPageFind { + UnownedPtr const m_pTextPage; + const WideString m_strText; + const std::vector m_csFindWhatArray; +- Optional m_findNextStart; +- Optional m_findPreStart; ++ absl::optional m_findNextStart; ++ absl::optional m_findPreStart; + int m_resStart = 0; + int m_resEnd = -1; + const Options m_options; +diff --git a/core/fpdftext/unicodenormalizationdata.cpp b/core/fpdftext/unicodenormalizationdata.cpp +index 7cbc2f73e..2b18afafa 100644 +--- a/core/fpdftext/unicodenormalizationdata.cpp ++++ b/core/fpdftext/unicodenormalizationdata.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,7 @@ + #include "core/fpdftext/unicodenormalizationdata.h" + #include "core/fxcrt/fx_system.h" + +-const uint16_t g_UnicodeData_Normalization[65536] = { ++const uint16_t kUnicodeDataNormalization[65536] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +@@ -7291,7 +7291,7 @@ const uint16_t g_UnicodeData_Normalization[65536] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}; + +-const uint16_t g_UnicodeData_Normalization_Map1[5376] = { ++const uint16_t kUnicodeDataNormalizationMap1[5376] = { + 0x0066, 0x0053, 0x0053, 0x0059, 0x0020, 0x0308, 0x0061, 0x0052, 0x0304, + 0x0032, 0x0033, 0x0301, 0x03BC, 0x0327, 0x0031, 0x006F, 0x0041, 0x0041, + 0x0041, 0x0041, 0x0041, 0x0041, 0x0043, 0x0045, 0x0045, 0x0045, 0x0045, +@@ -7891,7 +7891,7 @@ const uint16_t g_UnicodeData_Normalization_Map1[5376] = { + 0x00AC, 0x00AF, 0x00A6, 0x00A5, 0x20A9, 0x2502, 0x2190, 0x2191, 0x2192, + 0x2193, 0x25A0, 0x25CB}; + +-const uint16_t g_UnicodeData_Normalization_Map2[1724] = { ++const uint16_t kUnicodeDataNormalizationMap2[1724] = { + 0x004F, 0x0045, 0x0054, 0x004D, 0x006F, 0x0065, 0x0049, 0x004A, 0x0069, + 0x006A, 0x004F, 0x0045, 0x006F, 0x0065, 0x0044, 0x017D, 0x0044, 0x017E, + 0x0064, 0x017E, 0x004C, 0x004A, 0x004C, 0x006A, 0x006C, 0x006A, 0x004E, +@@ -8085,7 +8085,7 @@ const uint16_t g_UnicodeData_Normalization_Map2[1724] = { + 0x0644, 0x0622, 0x0644, 0x0623, 0x0644, 0x0623, 0x0644, 0x0625, 0x0644, + 0x0625, 0x0644, 0x0627, 0x0644, 0x0627}; + +-const uint16_t g_UnicodeData_Normalization_Map3[1164] = { ++const uint16_t kUnicodeDataNormalizationMap3[1164] = { + 0x0031, 0x002F, 0x0034, 0x0031, 0x002F, 0x0032, 0x0033, 0x002F, 0x0034, + 0x002E, 0x002E, 0x002E, 0x2032, 0x2032, 0x2032, 0x2035, 0x2035, 0x2035, + 0x0061, 0x002F, 0x0063, 0x0061, 0x002F, 0x0073, 0x0063, 0x002F, 0x006F, +@@ -8217,7 +8217,7 @@ const uint16_t g_UnicodeData_Normalization_Map3[1164] = { + 0x0646, 0x062C, 0x064A, 0x0635, 0x0644, 0x06D2, 0x0642, 0x0644, 0x06D2, + 0x0635, 0x0644, 0x0649}; + +-const uint16_t g_UnicodeData_Normalization_Map4[488] = { ++const uint16_t kUnicodeDataNormalizationMap4[488] = { + 0x0004, 0x2032, 0x2032, 0x2032, 0x2032, 0x0004, 0x0031, 0x002F, 0x0031, + 0x0030, 0x0004, 0x0056, 0x0049, 0x0049, 0x0049, 0x0004, 0x0076, 0x0069, + 0x0069, 0x0069, 0x0004, 0x0028, 0x0031, 0x0030, 0x0029, 0x0004, 0x0028, +diff --git a/core/fpdftext/unicodenormalizationdata.h b/core/fpdftext/unicodenormalizationdata.h +index 39ee552ec..71cdb1efd 100644 +--- a/core/fpdftext/unicodenormalizationdata.h ++++ b/core/fpdftext/unicodenormalizationdata.h +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,10 +9,10 @@ + + #include + +-extern const uint16_t g_UnicodeData_Normalization[]; +-extern const uint16_t g_UnicodeData_Normalization_Map1[]; +-extern const uint16_t g_UnicodeData_Normalization_Map2[]; +-extern const uint16_t g_UnicodeData_Normalization_Map3[]; +-extern const uint16_t g_UnicodeData_Normalization_Map4[]; ++extern const uint16_t kUnicodeDataNormalization[]; ++extern const uint16_t kUnicodeDataNormalizationMap1[]; ++extern const uint16_t kUnicodeDataNormalizationMap2[]; ++extern const uint16_t kUnicodeDataNormalizationMap3[]; ++extern const uint16_t kUnicodeDataNormalizationMap4[]; + + #endif // CORE_FPDFTEXT_UNICODENORMALIZATIONDATA_H_ +diff --git a/core/fxcodec/Android.bp b/core/fxcodec/Android.bp +index 7e5e69b45..62caf7e4a 100644 +--- a/core/fxcodec/Android.bp ++++ b/core/fxcodec/Android.bp +@@ -27,7 +27,8 @@ cc_library_static { + + exclude_srcs: [ + // pdf_enable_xfa +- "progressivedecoder.cpp", ++ "progressive_decoder.cpp", ++ "jpeg/jpeg_progressive_decoder.cpp", + // pdf_enable_xfa_bmp + "bmp/*", + // pdf_enable_xfa_gif +diff --git a/core/fxcodec/BUILD.gn b/core/fxcodec/BUILD.gn +index e0bc3a7c6..28f9915a5 100644 +--- a/core/fxcodec/BUILD.gn ++++ b/core/fxcodec/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -11,7 +11,6 @@ source_set("fxcodec") { + "basic/basicmodule.h", + "cfx_codec_memory.cpp", + "cfx_codec_memory.h", +- "codec_module_iface.h", + "fax/faxmodule.cpp", + "fax/faxmodule.h", + "flate/flatemodule.cpp", +@@ -19,8 +18,8 @@ source_set("fxcodec") { + "fx_codec.cpp", + "fx_codec.h", + "fx_codec_def.h", +- "icc/iccmodule.cpp", +- "icc/iccmodule.h", ++ "icc/icc_transform.cpp", ++ "icc/icc_transform.h", + "jbig2/JBig2_ArithDecoder.cpp", + "jbig2/JBig2_ArithDecoder.h", + "jbig2/JBig2_ArithIntDecoder.cpp", +@@ -57,20 +56,23 @@ source_set("fxcodec") { + "jbig2/JBig2_SymbolDict.h", + "jbig2/JBig2_TrdProc.cpp", + "jbig2/JBig2_TrdProc.h", +- "jbig2/jbig2module.cpp", +- "jbig2/jbig2module.h", ++ "jbig2/jbig2_decoder.cpp", ++ "jbig2/jbig2_decoder.h", ++ "jpeg/jpeg_common.cpp", ++ "jpeg/jpeg_common.h", + "jpeg/jpegmodule.cpp", + "jpeg/jpegmodule.h", + "jpx/cjpx_decoder.cpp", + "jpx/cjpx_decoder.h", + "jpx/jpx_decode_utils.cpp", + "jpx/jpx_decode_utils.h", +- "jpx/jpxmodule.cpp", +- "jpx/jpxmodule.h", + "scanlinedecoder.cpp", + "scanlinedecoder.h", + ] +- configs += [ "../../:pdfium_core_config" ] ++ configs += [ ++ "../../:pdfium_strict_config", ++ "../../:pdfium_noshorten_config", ++ ] + include_dirs = [] + deps = [ + "../../third_party:lcms2", +@@ -83,13 +85,18 @@ source_set("fxcodec") { + + if (pdf_enable_xfa) { + sources += [ +- "progressivedecoder.cpp", +- "progressivedecoder.h", ++ "jpeg/jpeg_progressive_decoder.cpp", ++ "jpeg/jpeg_progressive_decoder.h", ++ "progressive_decoder.cpp", ++ "progressive_decoder.h", ++ "progressive_decoder_iface.h", + ] + if (pdf_enable_xfa_bmp) { + sources += [ +- "bmp/bmpmodule.cpp", +- "bmp/bmpmodule.h", ++ "bmp/bmp_decoder.cpp", ++ "bmp/bmp_decoder.h", ++ "bmp/bmp_progressive_decoder.cpp", ++ "bmp/bmp_progressive_decoder.h", + "bmp/cfx_bmpcontext.cpp", + "bmp/cfx_bmpcontext.h", + "bmp/cfx_bmpdecompressor.cpp", +@@ -103,25 +110,27 @@ source_set("fxcodec") { + "gif/cfx_gif.h", + "gif/cfx_gifcontext.cpp", + "gif/cfx_gifcontext.h", +- "gif/cfx_lzwdecompressor.cpp", +- "gif/cfx_lzwdecompressor.h", +- "gif/gifmodule.cpp", +- "gif/gifmodule.h", ++ "gif/gif_decoder.cpp", ++ "gif/gif_decoder.h", ++ "gif/gif_progressive_decoder.cpp", ++ "gif/gif_progressive_decoder.h", ++ "gif/lzw_decompressor.cpp", ++ "gif/lzw_decompressor.h", + ] + } + if (pdf_enable_xfa_png) { + sources += [ +- "png/pngmodule.cpp", +- "png/pngmodule.h", ++ "png/png_decoder.cpp", ++ "png/png_decoder.h", + ] + deps += [ "../../third_party:png" ] + } + if (pdf_enable_xfa_tiff) { + sources += [ +- "tiff/tiffmodule.cpp", +- "tiff/tiffmodule.h", ++ "tiff/tiff_decoder.cpp", ++ "tiff/tiff_decoder.h", + ] +- deps += [ "../../third_party:fx_tiff" ] ++ deps += [ "../../third_party:tiff" ] + } + } + +@@ -138,17 +147,18 @@ pdfium_unittest_source_set("unittests") { + ] + deps = [ + ":fxcodec", ++ "../../third_party:libopenjpeg2", + "../fpdfapi/parser", + ] + pdfium_root_dir = "../../" + + if (pdf_enable_xfa) { +- sources += [ "progressivedecoder_unittest.cpp" ] ++ sources += [ "progressive_decoder_unittest.cpp" ] + deps += [ "../fxge" ] + if (pdf_enable_xfa_gif) { + sources += [ + "gif/cfx_gifcontext_unittest.cpp", +- "gif/cfx_lzwdecompressor_unittest.cpp", ++ "gif/lzw_decompressor_unittest.cpp", + ] + } + } +diff --git a/core/fxcodec/basic/a85_unittest.cpp b/core/fxcodec/basic/a85_unittest.cpp +index e01d3d208..65dbf9a04 100644 +--- a/core/fxcodec/basic/a85_unittest.cpp ++++ b/core/fxcodec/basic/a85_unittest.cpp +@@ -1,162 +1,130 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include + ++#include + #include + #include + + #include "core/fxcodec/basic/basicmodule.h" +-#include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/data_vector.h" + #include "testing/gtest/include/gtest/gtest.h" + +-TEST(fxcodec, A85TestBadInputs) { +- const uint8_t src_buf[] = {1, 2, 3, 4}; +- std::unique_ptr dest_buf; +- uint32_t dest_size = 0; +- +- // Error codes, not segvs, should callers pass us a nullptr pointer. +- EXPECT_FALSE(BasicModule::A85Encode(src_buf, &dest_buf, nullptr)); +- EXPECT_FALSE(BasicModule::A85Encode(src_buf, nullptr, &dest_size)); +- EXPECT_FALSE(BasicModule::A85Encode({}, &dest_buf, &dest_size)); ++TEST(fxcodec, A85EmptyInput) { ++ EXPECT_TRUE(BasicModule::A85Encode({}).empty()); + } + + // No leftover bytes, just translate 2 sets of symbols. +-TEST(fxcodec, A85TestBasic) { ++TEST(fxcodec, A85Basic) { + // Make sure really big values don't break. + const uint8_t src_buf[] = {1, 2, 3, 4, 255, 255, 255, 255}; +- std::unique_ptr dest_buf; +- uint32_t dest_size = 0; +- EXPECT_TRUE(BasicModule::A85Encode(src_buf, &dest_buf, &dest_size)); ++ DataVector dest_buf = BasicModule::A85Encode(src_buf); + + // Should have 5 chars for each set of 4 and 2 terminators. + const uint8_t expected_out[] = {33, 60, 78, 63, 43, 115, + 56, 87, 45, 33, 126, 62}; +- ASSERT_EQ(FX_ArraySize(expected_out), dest_size); ++ ASSERT_EQ(std::size(expected_out), dest_buf.size()); + + // Check the output +- auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size); +- for (uint32_t i = 0; i < dest_size; i++) +- EXPECT_EQ(expected_out[i], dest_buf_span[i]) << " at " << i; ++ for (uint32_t i = 0; i < dest_buf.size(); i++) ++ EXPECT_EQ(expected_out[i], dest_buf[i]) << " at " << i; + } + + // Leftover bytes. +-TEST(fxcodec, A85TestLeftoverBytes) { +- std::unique_ptr dest_buf; +- uint32_t dest_size = 0; +- ++TEST(fxcodec, A85LeftoverBytes) { + { + // 1 Leftover Byte: + const uint8_t src_buf_1leftover[] = {1, 2, 3, 4, 255}; +- EXPECT_TRUE( +- BasicModule::A85Encode(src_buf_1leftover, &dest_buf, &dest_size)); ++ DataVector dest_buf = BasicModule::A85Encode(src_buf_1leftover); + + // 5 chars for first symbol + 2 + 2 terminators. + uint8_t expected_out_1leftover[] = {33, 60, 78, 63, 43, 114, 114, 126, 62}; +- ASSERT_EQ(FX_ArraySize(expected_out_1leftover), dest_size); ++ ASSERT_EQ(std::size(expected_out_1leftover), dest_buf.size()); + + // Check the output +- auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size); +- for (uint32_t i = 0; i < dest_size; i++) +- EXPECT_EQ(expected_out_1leftover[i], dest_buf_span[i]) << " at " << i; ++ for (uint32_t i = 0; i < dest_buf.size(); i++) ++ EXPECT_EQ(expected_out_1leftover[i], dest_buf[i]) << " at " << i; + } + + { + // 2 Leftover bytes: + const uint8_t src_buf_2leftover[] = {1, 2, 3, 4, 255, 254}; +- dest_buf.reset(); +- dest_size = 0; +- EXPECT_TRUE( +- BasicModule::A85Encode(src_buf_2leftover, &dest_buf, &dest_size)); ++ DataVector dest_buf = BasicModule::A85Encode(src_buf_2leftover); + // 5 chars for first symbol + 3 + 2 terminators. + const uint8_t expected_out_2leftover[] = {33, 60, 78, 63, 43, + 115, 56, 68, 126, 62}; +- ASSERT_EQ(FX_ArraySize(expected_out_2leftover), dest_size); ++ ASSERT_EQ(std::size(expected_out_2leftover), dest_buf.size()); + + // Check the output +- auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size); +- for (uint32_t i = 0; i < dest_size; i++) +- EXPECT_EQ(expected_out_2leftover[i], dest_buf_span[i]) << " at " << i; ++ for (uint32_t i = 0; i < dest_buf.size(); i++) ++ EXPECT_EQ(expected_out_2leftover[i], dest_buf[i]) << " at " << i; + } + + { + // 3 Leftover bytes: + const uint8_t src_buf_3leftover[] = {1, 2, 3, 4, 255, 254, 253}; +- dest_buf.reset(); +- dest_size = 0; +- EXPECT_TRUE( +- BasicModule::A85Encode(src_buf_3leftover, &dest_buf, &dest_size)); ++ DataVector dest_buf = BasicModule::A85Encode(src_buf_3leftover); + // 5 chars for first symbol + 4 + 2 terminators. + const uint8_t expected_out_3leftover[] = {33, 60, 78, 63, 43, 115, + 56, 77, 114, 126, 62}; +- ASSERT_EQ(FX_ArraySize(expected_out_3leftover), dest_size); ++ ASSERT_EQ(std::size(expected_out_3leftover), dest_buf.size()); + + // Check the output +- auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size); +- for (uint32_t i = 0; i < dest_size; i++) +- EXPECT_EQ(expected_out_3leftover[i], dest_buf_span[i]) << " at " << i; ++ for (uint32_t i = 0; i < dest_buf.size(); i++) ++ EXPECT_EQ(expected_out_3leftover[i], dest_buf[i]) << " at " << i; + } + } + + // Test all zeros comes through as "z". +-TEST(fxcodec, A85TestZeros) { +- std::unique_ptr dest_buf; +- uint32_t dest_size = 0; +- ++TEST(fxcodec, A85Zeros) { + { + // Make sure really big values don't break. + const uint8_t src_buf[] = {1, 2, 3, 4, 0, 0, 0, 0}; +- EXPECT_TRUE(BasicModule::A85Encode(src_buf, &dest_buf, &dest_size)); ++ DataVector dest_buf = BasicModule::A85Encode(src_buf); + + // Should have 5 chars for first set of 4 + 1 for z + 2 terminators. + const uint8_t expected_out[] = {33, 60, 78, 63, 43, 122, 126, 62}; +- ASSERT_EQ(FX_ArraySize(expected_out), dest_size); ++ ASSERT_EQ(std::size(expected_out), dest_buf.size()); + + // Check the output +- auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size); +- for (uint32_t i = 0; i < dest_size; i++) +- EXPECT_EQ(expected_out[i], dest_buf_span[i]) << " at " << i; ++ for (uint32_t i = 0; i < dest_buf.size(); i++) ++ EXPECT_EQ(expected_out[i], dest_buf[i]) << " at " << i; + } + + { + // Should also work if it is at the start: + const uint8_t src_buf_2[] = {0, 0, 0, 0, 1, 2, 3, 4}; +- dest_buf.reset(); +- dest_size = 0; +- EXPECT_TRUE(BasicModule::A85Encode(src_buf_2, &dest_buf, &dest_size)); ++ DataVector dest_buf = BasicModule::A85Encode(src_buf_2); + + // Should have 5 chars for set of 4 + 1 for z + 2 terminators. + const uint8_t expected_out_2[] = {122, 33, 60, 78, 63, 43, 126, 62}; +- ASSERT_EQ(FX_ArraySize(expected_out_2), dest_size); ++ ASSERT_EQ(std::size(expected_out_2), dest_buf.size()); + + // Check the output +- auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size); +- for (uint32_t i = 0; i < dest_size; i++) +- EXPECT_EQ(expected_out_2[i], dest_buf_span[i]) << " at " << i; ++ for (uint32_t i = 0; i < dest_buf.size(); i++) ++ EXPECT_EQ(expected_out_2[i], dest_buf[i]) << " at " << i; + } + + { + // Try with 2 leftover zero bytes. Make sure we don't get a "z". + const uint8_t src_buf_3[] = {1, 2, 3, 4, 0, 0}; +- dest_buf.reset(); +- dest_size = 0; +- EXPECT_TRUE(BasicModule::A85Encode(src_buf_3, &dest_buf, &dest_size)); ++ DataVector dest_buf = BasicModule::A85Encode(src_buf_3); + + // Should have 5 chars for set of 4 + 3 for last 2 + 2 terminators. + const uint8_t expected_out_leftover[] = {33, 60, 78, 63, 43, + 33, 33, 33, 126, 62}; +- ASSERT_EQ(FX_ArraySize(expected_out_leftover), dest_size); ++ ASSERT_EQ(std::size(expected_out_leftover), dest_buf.size()); + + // Check the output +- auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size); +- for (uint32_t i = 0; i < dest_size; i++) +- EXPECT_EQ(expected_out_leftover[i], dest_buf_span[i]) << " at " << i; ++ for (uint32_t i = 0; i < dest_buf.size(); i++) ++ EXPECT_EQ(expected_out_leftover[i], dest_buf[i]) << " at " << i; + } + } + + // Make sure we get returns in the expected locations. +-TEST(fxcodec, A85TestLineBreaks) { ++TEST(fxcodec, A85LineBreaks) { + // Make sure really big values don't break. + uint8_t src_buf[131] = {0}; + // 1 full line + most of a line of normal symbols. +@@ -173,21 +141,18 @@ TEST(fxcodec, A85TestLineBreaks) { + src_buf[k + 2] = 3; + src_buf[k + 3] = 4; + } +- std::unique_ptr dest_buf; +- uint32_t dest_size = 0; + + // Should succeed. +- EXPECT_TRUE(BasicModule::A85Encode(src_buf, &dest_buf, &dest_size)); ++ DataVector dest_buf = BasicModule::A85Encode(src_buf); + + // Should have 75 chars in the first row plus 2 char return, + // 76 chars in the second row plus 2 char return, + // and 9 chars in the last row with 2 terminators. +- ASSERT_EQ(166u, dest_size); ++ ASSERT_EQ(166u, dest_buf.size()); + + // Check for the returns. +- auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size); +- EXPECT_EQ(13, dest_buf_span[75]); +- EXPECT_EQ(10, dest_buf_span[76]); +- EXPECT_EQ(13, dest_buf_span[153]); +- EXPECT_EQ(10, dest_buf_span[154]); ++ EXPECT_EQ(13, dest_buf[75]); ++ EXPECT_EQ(10, dest_buf[76]); ++ EXPECT_EQ(13, dest_buf[153]); ++ EXPECT_EQ(10, dest_buf[154]); + } +diff --git a/core/fxcodec/basic/basicmodule.cpp b/core/fxcodec/basic/basicmodule.cpp +index d495454fc..452e46408 100644 +--- a/core/fxcodec/basic/basicmodule.cpp ++++ b/core/fxcodec/basic/basicmodule.cpp +@@ -1,15 +1,22 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcodec/basic/basicmodule.h" + ++#include ++ + #include + #include + + #include "core/fxcodec/scanlinedecoder.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_memory_wrappers.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/ptr_util.h" ++#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/span_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/numerics/safe_conversions.h" + + namespace fxcodec { + +@@ -27,16 +34,16 @@ class RLScanlineDecoder final : public ScanlineDecoder { + int bpc); + + // ScanlineDecoder: +- bool v_Rewind() override; +- uint8_t* v_GetNextLine() override; +- uint32_t GetSrcOffset() override { return m_SrcOffset; } ++ bool Rewind() override; ++ pdfium::span GetNextLine() override; ++ uint32_t GetSrcOffset() override; + + private: + bool CheckDestSize(); + void GetNextOperator(); + void UpdateOperator(uint8_t used_bytes); + +- std::unique_ptr m_pScanline; ++ DataVector m_Scanline; + pdfium::span m_SrcBuf; + size_t m_dwLineBytes = 0; + size_t m_SrcOffset = 0; +@@ -46,7 +53,10 @@ class RLScanlineDecoder final : public ScanlineDecoder { + + RLScanlineDecoder::RLScanlineDecoder() = default; + +-RLScanlineDecoder::~RLScanlineDecoder() = default; ++RLScanlineDecoder::~RLScanlineDecoder() { ++ // Span in superclass can't outlive our buffer. ++ m_pLastScanline = pdfium::span(); ++} + + bool RLScanlineDecoder::CheckDestSize() { + size_t i = 0; +@@ -101,40 +111,43 @@ bool RLScanlineDecoder::Create(pdfium::span src_buf, + m_Pitch = pitch.ValueOrDie(); + // Overflow should already have been checked before this is called. + m_dwLineBytes = (static_cast(width) * nComps * bpc + 7) / 8; +- m_pScanline.reset(FX_Alloc(uint8_t, m_Pitch)); ++ m_Scanline.resize(m_Pitch); + return CheckDestSize(); + } + +-bool RLScanlineDecoder::v_Rewind() { +- memset(m_pScanline.get(), 0, m_Pitch); ++bool RLScanlineDecoder::Rewind() { ++ fxcrt::spanclr(pdfium::make_span(m_Scanline)); + m_SrcOffset = 0; + m_bEOD = false; + m_Operator = 0; + return true; + } + +-uint8_t* RLScanlineDecoder::v_GetNextLine() { ++pdfium::span RLScanlineDecoder::GetNextLine() { + if (m_SrcOffset == 0) { + GetNextOperator(); + } else if (m_bEOD) { +- return nullptr; ++ return pdfium::span(); + } +- memset(m_pScanline.get(), 0, m_Pitch); + uint32_t col_pos = 0; + bool eol = false; ++ auto scan_span = pdfium::make_span(m_Scanline); ++ fxcrt::spanclr(scan_span); + while (m_SrcOffset < m_SrcBuf.size() && !eol) { + if (m_Operator < 128) { + uint32_t copy_len = m_Operator + 1; + if (col_pos + copy_len >= m_dwLineBytes) { +- copy_len = m_dwLineBytes - col_pos; ++ copy_len = ++ pdfium::base::checked_cast(m_dwLineBytes - col_pos); + eol = true; + } + if (copy_len >= m_SrcBuf.size() - m_SrcOffset) { +- copy_len = m_SrcBuf.size() - m_SrcOffset; ++ copy_len = ++ pdfium::base::checked_cast(m_SrcBuf.size() - m_SrcOffset); + m_bEOD = true; + } + auto copy_span = m_SrcBuf.subspan(m_SrcOffset, copy_len); +- memcpy(m_pScanline.get() + col_pos, copy_span.data(), copy_span.size()); ++ fxcrt::spancpy(scan_span.subspan(col_pos), copy_span); + col_pos += copy_len; + UpdateOperator((uint8_t)copy_len); + } else if (m_Operator > 128) { +@@ -144,10 +157,11 @@ uint8_t* RLScanlineDecoder::v_GetNextLine() { + } + uint32_t duplicate_len = 257 - m_Operator; + if (col_pos + duplicate_len >= m_dwLineBytes) { +- duplicate_len = m_dwLineBytes - col_pos; ++ duplicate_len = ++ pdfium::base::checked_cast(m_dwLineBytes - col_pos); + eol = true; + } +- memset(m_pScanline.get() + col_pos, fill, duplicate_len); ++ fxcrt::spanset(scan_span.subspan(col_pos, duplicate_len), fill); + col_pos += duplicate_len; + UpdateOperator((uint8_t)duplicate_len); + } else { +@@ -155,7 +169,11 @@ uint8_t* RLScanlineDecoder::v_GetNextLine() { + break; + } + } +- return m_pScanline.get(); ++ return m_Scanline; ++} ++ ++uint32_t RLScanlineDecoder::GetSrcOffset() { ++ return pdfium::base::checked_cast(m_SrcOffset); + } + + void RLScanlineDecoder::GetNextOperator() { +@@ -171,7 +189,7 @@ void RLScanlineDecoder::UpdateOperator(uint8_t used_bytes) { + return; + } + if (m_Operator < 128) { +- ASSERT((uint32_t)m_Operator + 1 >= used_bytes); ++ DCHECK((uint32_t)m_Operator + 1 >= used_bytes); + if (used_bytes == m_Operator + 1) { + m_SrcOffset += used_bytes; + GetNextOperator(); +@@ -185,7 +203,7 @@ void RLScanlineDecoder::UpdateOperator(uint8_t used_bytes) { + return; + } + uint8_t count = 257 - m_Operator; +- ASSERT((uint32_t)count >= used_bytes); ++ DCHECK((uint32_t)count >= used_bytes); + if (used_bytes == count) { + m_SrcOffset++; + GetNextOperator(); +@@ -204,7 +222,7 @@ std::unique_ptr BasicModule::CreateRunLengthDecoder( + int height, + int nComps, + int bpc) { +- auto pDecoder = pdfium::MakeUnique(); ++ auto pDecoder = std::make_unique(); + if (!pDecoder->Create(src_buf, width, height, nComps, bpc)) + return nullptr; + +@@ -212,24 +230,14 @@ std::unique_ptr BasicModule::CreateRunLengthDecoder( + } + + // static +-bool BasicModule::RunLengthEncode( +- pdfium::span src_span, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size) { +- // Check inputs +- if (src_span.empty() || !dest_buf || !dest_size) +- return false; ++DataVector BasicModule::RunLengthEncode( ++ pdfium::span src_span) { ++ if (src_span.empty()) ++ return {}; + +- // Edge case +- if (src_span.size() == 1) { +- *dest_size = 3; +- dest_buf->reset(FX_Alloc(uint8_t, *dest_size)); +- auto dest_buf_span = pdfium::make_span(dest_buf->get(), *dest_size); +- dest_buf_span[0] = 0; +- dest_buf_span[1] = src_span[0]; +- dest_buf_span[2] = 128; +- return true; +- } ++ // Handle edge case. ++ if (src_span.size() == 1) ++ return {0, src_span[0], 128}; + + // Worst case: 1 nonmatch, 2 match, 1 nonmatch, 2 match, etc. This becomes + // 4 output chars for every 3 input, plus up to 4 more for the 1-2 chars +@@ -239,10 +247,10 @@ bool BasicModule::RunLengthEncode( + estimated_size /= 3; + estimated_size *= 4; + estimated_size += 1; +- dest_buf->reset(FX_Alloc(uint8_t, estimated_size.ValueOrDie())); ++ DataVector result(estimated_size.ValueOrDie()); + +- // Set up pointers. +- uint8_t* out = dest_buf->get(); ++ // Set up span and counts. ++ auto result_span = pdfium::make_span(result); + uint32_t run_start = 0; + uint32_t run_end = 1; + uint8_t x = src_span[run_start]; +@@ -259,56 +267,52 @@ bool BasicModule::RunLengthEncode( + y = src_span[run_end]; + } + if (run_end - run_start > 1) { // Matched run but not at end of input. +- out[0] = 257 - (run_end - run_start); +- out[1] = x; ++ result_span[0] = 257 - (run_end - run_start); ++ result_span[1] = x; + x = y; + run_start = run_end; + run_end++; + if (run_end < src_span.size()) + y = src_span[run_end]; +- out += 2; ++ result_span = result_span.subspan(2); + continue; + } + // Mismatched run + while (x != y && run_end <= run_start + max_len) { +- out[run_end - run_start] = x; ++ result_span[run_end - run_start] = x; + x = y; + run_end++; + if (run_end == src_span.size()) { + if (run_end <= run_start + max_len) { +- out[run_end - run_start] = x; ++ result_span[run_end - run_start] = x; + run_end++; + } + break; + } + y = src_span[run_end]; + } +- out[0] = run_end - run_start - 2; +- out += run_end - run_start; ++ result_span[0] = run_end - run_start - 2; ++ result_span = result_span.subspan(run_end - run_start); + run_start = run_end - 1; + } + if (run_start < src_span.size()) { // 1 leftover character +- out[0] = 0; +- out[1] = x; +- out += 2; ++ result_span[0] = 0; ++ result_span[1] = x; ++ result_span = result_span.subspan(2); + } +- *out = 128; +- *dest_size = out + 1 - dest_buf->get(); +- return true; ++ result_span[0] = 128; ++ size_t new_size = 1 + result.size() - result_span.size(); ++ CHECK_LE(new_size, result.size()); ++ result.resize(new_size); ++ return result; + } + + // static +-bool BasicModule::A85Encode(pdfium::span src_span, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size) { +- // Check inputs. +- if (!dest_buf || !dest_size) +- return false; +- +- if (src_span.empty()) { +- *dest_size = 0; +- return false; +- } ++DataVector BasicModule::A85Encode( ++ pdfium::span src_span) { ++ DataVector result; ++ if (src_span.empty()) ++ return result; + + // Worst case: 5 output for each 4 input (plus up to 4 from leftover), plus + // 2 character new lines each 75 output chars plus 2 termination chars. May +@@ -319,33 +323,32 @@ bool BasicModule::A85Encode(pdfium::span src_span, + estimated_size += 4; + estimated_size += src_span.size() / 30; + estimated_size += 2; +- dest_buf->reset(FX_Alloc(uint8_t, estimated_size.ValueOrDie())); ++ result.resize(estimated_size.ValueOrDie()); + +- // Set up pointers. +- uint8_t* out = dest_buf->get(); ++ // Set up span and counts. ++ auto result_span = pdfium::make_span(result); + uint32_t pos = 0; + uint32_t line_length = 0; + while (src_span.size() >= 4 && pos < src_span.size() - 3) { +- uint32_t val = ((uint32_t)(src_span[pos]) << 24) + +- ((uint32_t)(src_span[pos + 1]) << 16) + +- ((uint32_t)(src_span[pos + 2]) << 8) + +- (uint32_t)(src_span[pos + 3]); ++ auto val_span = src_span.subspan(pos, 4); ++ uint32_t val = FXSYS_UINT32_GET_MSBFIRST(val_span); + pos += 4; + if (val == 0) { // All zero special case +- *out = 'z'; +- out++; ++ result_span[0] = 'z'; ++ result_span = result_span.subspan(1); + line_length++; + } else { // Compute base 85 characters and add 33. + for (int i = 4; i >= 0; i--) { +- out[i] = (uint8_t)(val % 85) + 33; +- val = val / 85; ++ result_span[i] = (val % 85) + 33; ++ val /= 85; + } +- out += 5; ++ result_span = result_span.subspan(5); + line_length += 5; + } + if (line_length >= 75) { // Add a return. +- *out++ = '\r'; +- *out++ = '\n'; ++ result_span[0] = '\r'; ++ result_span[1] = '\n'; ++ result_span = result_span.subspan(2); + line_length = 0; + } + } +@@ -359,18 +362,19 @@ bool BasicModule::A85Encode(pdfium::span src_span, + } + for (int i = 4; i >= 0; i--) { + if (i <= count) +- out[i] = (uint8_t)(val % 85) + 33; +- val = val / 85; ++ result_span[i] = (val % 85) + 33; ++ val /= 85; + } +- out += count + 1; ++ result_span = result_span.subspan(count + 1); + } + + // Terminating characters. +- out[0] = '~'; +- out[1] = '>'; +- out += 2; +- *dest_size = out - dest_buf->get(); +- return true; ++ result_span[0] = '~'; ++ result_span[1] = '>'; ++ size_t new_size = 2 + result.size() - result_span.size(); ++ CHECK_LE(new_size, result.size()); ++ result.resize(new_size); ++ return result; + } + + } // namespace fxcodec +diff --git a/core/fxcodec/basic/basicmodule.h b/core/fxcodec/basic/basicmodule.h +index 5de4da90e..a2573498e 100644 +--- a/core/fxcodec/basic/basicmodule.h ++++ b/core/fxcodec/basic/basicmodule.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,11 @@ + #ifndef CORE_FXCODEC_BASIC_BASICMODULE_H_ + #define CORE_FXCODEC_BASIC_BASICMODULE_H_ + ++#include ++ + #include + +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/data_vector.h" + #include "third_party/base/span.h" + + namespace fxcodec { +@@ -26,13 +27,10 @@ class BasicModule { + int nComps, + int bpc); + +- static bool RunLengthEncode(pdfium::span src_span, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size); ++ static DataVector RunLengthEncode( ++ pdfium::span src_span); + +- static bool A85Encode(pdfium::span src_span, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size); ++ static DataVector A85Encode(pdfium::span src_span); + + BasicModule() = delete; + BasicModule(const BasicModule&) = delete; +diff --git a/core/fxcodec/basic/rle_unittest.cpp b/core/fxcodec/basic/rle_unittest.cpp +index 047b44763..7f81b37f2 100644 +--- a/core/fxcodec/basic/rle_unittest.cpp ++++ b/core/fxcodec/basic/rle_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,47 +9,34 @@ + + #include "core/fpdfapi/parser/fpdf_parser_decode.h" + #include "core/fxcodec/basic/basicmodule.h" +-#include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/data_vector.h" + #include "testing/gtest/include/gtest/gtest.h" + +-TEST(fxcodec, RLETestBadInputs) { +- const uint8_t src_buf[] = {1}; +- std::unique_ptr dest_buf; +- uint32_t dest_size = 0; +- +- // Error codes, not segvs, should callers pass us a nullptr pointer. +- EXPECT_FALSE(BasicModule::RunLengthEncode(src_buf, &dest_buf, nullptr)); +- EXPECT_FALSE(BasicModule::RunLengthEncode(src_buf, nullptr, &dest_size)); +- EXPECT_FALSE(BasicModule::RunLengthEncode({}, &dest_buf, &dest_size)); ++TEST(fxcodec, RLEEmptyInput) { ++ EXPECT_TRUE(BasicModule::RunLengthEncode({}).empty()); + } + + // Check length 1 input works. Check terminating character is applied. +-TEST(fxcodec, RLETestShortInput) { ++TEST(fxcodec, RLEShortInput) { + const uint8_t src_buf[] = {1}; +- std::unique_ptr dest_buf; +- uint32_t dest_size = 0; +- +- EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf, &dest_buf, &dest_size)); +- ASSERT_EQ(3u, dest_size); +- auto dest_buf_span = pdfium::make_span(dest_buf.get(), dest_size); +- EXPECT_EQ(0, dest_buf_span[0]); +- EXPECT_EQ(1, dest_buf_span[1]); +- EXPECT_EQ(128, dest_buf_span[2]); ++ DataVector dest_buf = BasicModule::RunLengthEncode(src_buf); ++ ASSERT_EQ(3u, dest_buf.size()); ++ EXPECT_EQ(0, dest_buf[0]); ++ EXPECT_EQ(1, dest_buf[1]); ++ EXPECT_EQ(128, dest_buf[2]); + } + + // Check a few basic cases (2 matching runs in a row, matching run followed + // by a non-matching run, and non-matching run followed by a matching run). +-TEST(fxcodec, RLETestNormalInputs) { +- std::unique_ptr dest_buf; +- uint32_t dest_size = 0; ++TEST(fxcodec, RLENormalInputs) { + std::unique_ptr decoded_buf; + uint32_t decoded_size = 0; + + { + // Case 1: Match, match + const uint8_t src_buf_1[] = {2, 2, 2, 2, 4, 4, 4, 4, 4, 4}; +- EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_1, &dest_buf, &dest_size)); +- RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size); ++ DataVector dest_buf = BasicModule::RunLengthEncode(src_buf_1); ++ RunLengthDecode(dest_buf, &decoded_buf, &decoded_size); + ASSERT_EQ(sizeof(src_buf_1), decoded_size); + auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size); + for (uint32_t i = 0; i < decoded_size; i++) +@@ -59,12 +46,10 @@ TEST(fxcodec, RLETestNormalInputs) { + { + // Case 2: Match, non-match + const uint8_t src_buf_2[] = {2, 2, 2, 2, 1, 2, 3, 4, 5, 6}; +- dest_buf.reset(); +- dest_size = 0; +- EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_2, &dest_buf, &dest_size)); ++ DataVector dest_buf = BasicModule::RunLengthEncode(src_buf_2); + decoded_buf.reset(); + decoded_size = 0; +- RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size); ++ RunLengthDecode(dest_buf, &decoded_buf, &decoded_size); + ASSERT_EQ(sizeof(src_buf_2), decoded_size); + auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size); + for (uint32_t i = 0; i < decoded_size; i++) +@@ -74,12 +59,10 @@ TEST(fxcodec, RLETestNormalInputs) { + { + // Case 3: Non-match, match + const uint8_t src_buf_3[] = {1, 2, 3, 4, 5, 3, 3, 3, 3, 3}; +- dest_buf.reset(); +- dest_size = 0; +- EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_3, &dest_buf, &dest_size)); ++ DataVector dest_buf = BasicModule::RunLengthEncode(src_buf_3); + decoded_buf.reset(); + decoded_size = 0; +- RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size); ++ RunLengthDecode(dest_buf, &decoded_buf, &decoded_size); + ASSERT_EQ(sizeof(src_buf_3), decoded_size); + auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size); + for (uint32_t i = 0; i < decoded_size; i++) +@@ -89,17 +72,15 @@ TEST(fxcodec, RLETestNormalInputs) { + + // Check that runs longer than 128 are broken up properly, both matched and + // non-matched. +-TEST(fxcodec, RLETestFullLengthInputs) { +- std::unique_ptr dest_buf; +- uint32_t dest_size = 0; ++TEST(fxcodec, RLEFullLengthInputs) { + std::unique_ptr decoded_buf; + uint32_t decoded_size = 0; + + { + // Case 1: Match, match + const uint8_t src_buf_1[260] = {1}; +- EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_1, &dest_buf, &dest_size)); +- RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size); ++ DataVector dest_buf = BasicModule::RunLengthEncode(src_buf_1); ++ RunLengthDecode(dest_buf, &decoded_buf, &decoded_size); + ASSERT_EQ(sizeof(src_buf_1), decoded_size); + auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size); + for (uint32_t i = 0; i < decoded_size; i++) +@@ -111,12 +92,10 @@ TEST(fxcodec, RLETestFullLengthInputs) { + uint8_t src_buf_2[260] = {2}; + for (uint16_t i = 128; i < 260; i++) + src_buf_2[i] = static_cast(i - 125); +- dest_buf.reset(); +- dest_size = 0; +- EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_2, &dest_buf, &dest_size)); ++ DataVector dest_buf = BasicModule::RunLengthEncode(src_buf_2); + decoded_buf.reset(); + decoded_size = 0; +- RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size); ++ RunLengthDecode(dest_buf, &decoded_buf, &decoded_size); + ASSERT_EQ(sizeof(src_buf_2), decoded_size); + auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size); + for (uint32_t i = 0; i < decoded_size; i++) +@@ -128,12 +107,10 @@ TEST(fxcodec, RLETestFullLengthInputs) { + uint8_t src_buf_3[260] = {3}; + for (uint8_t i = 0; i < 128; i++) + src_buf_3[i] = i; +- dest_buf.reset(); +- dest_size = 0; +- EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_3, &dest_buf, &dest_size)); ++ DataVector dest_buf = BasicModule::RunLengthEncode(src_buf_3); + decoded_buf.reset(); + decoded_size = 0; +- RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size); ++ RunLengthDecode(dest_buf, &decoded_buf, &decoded_size); + ASSERT_EQ(sizeof(src_buf_3), decoded_size); + auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size); + for (uint32_t i = 0; i < decoded_size; i++) +@@ -145,12 +122,10 @@ TEST(fxcodec, RLETestFullLengthInputs) { + uint8_t src_buf_4[260]; + for (uint16_t i = 0; i < 260; i++) + src_buf_4[i] = static_cast(i); +- dest_buf.reset(); +- dest_size = 0; +- EXPECT_TRUE(BasicModule::RunLengthEncode(src_buf_4, &dest_buf, &dest_size)); ++ DataVector dest_buf = BasicModule::RunLengthEncode(src_buf_4); + decoded_buf.reset(); + decoded_size = 0; +- RunLengthDecode({dest_buf.get(), dest_size}, &decoded_buf, &decoded_size); ++ RunLengthDecode(dest_buf, &decoded_buf, &decoded_size); + ASSERT_EQ(sizeof(src_buf_4), decoded_size); + auto decoded_buf_span = pdfium::make_span(decoded_buf.get(), decoded_size); + for (uint32_t i = 0; i < decoded_size; i++) +diff --git a/core/fxcodec/bmp/bmp_decoder.cpp b/core/fxcodec/bmp/bmp_decoder.cpp +new file mode 100644 +index 000000000..74fd618b3 +--- /dev/null ++++ b/core/fxcodec/bmp/bmp_decoder.cpp +@@ -0,0 +1,75 @@ ++// Copyright 2014 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxcodec/bmp/bmp_decoder.h" ++ ++#include ++ ++#include "core/fxcodec/bmp/cfx_bmpcontext.h" ++#include "core/fxcodec/cfx_codec_memory.h" ++#include "core/fxcodec/fx_codec.h" ++#include "core/fxcodec/fx_codec_def.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/check.h" ++ ++namespace fxcodec { ++ ++// static ++std::unique_ptr BmpDecoder::StartDecode( ++ Delegate* pDelegate) { ++ return std::make_unique(pDelegate); ++} ++ ++// static ++BmpDecoder::Status BmpDecoder::ReadHeader( ++ ProgressiveDecoderIface::Context* pContext, ++ int32_t* width, ++ int32_t* height, ++ bool* tb_flag, ++ int32_t* components, ++ int32_t* pal_num, ++ const std::vector** palette, ++ CFX_DIBAttribute* pAttribute) { ++ DCHECK(pAttribute); ++ ++ auto* ctx = static_cast(pContext); ++ Status status = ctx->m_Bmp.ReadHeader(); ++ if (status != Status::kSuccess) ++ return status; ++ ++ *width = ctx->m_Bmp.width(); ++ *height = ctx->m_Bmp.height(); ++ *tb_flag = ctx->m_Bmp.img_tb_flag(); ++ *components = ctx->m_Bmp.components(); ++ *pal_num = ctx->m_Bmp.pal_num(); ++ *palette = ctx->m_Bmp.palette(); ++ pAttribute->m_wDPIUnit = CFX_DIBAttribute::kResUnitMeter; ++ pAttribute->m_nXDPI = ctx->m_Bmp.dpi_x(); ++ pAttribute->m_nYDPI = ctx->m_Bmp.dpi_y(); ++ return Status::kSuccess; ++} ++ ++// static ++BmpDecoder::Status BmpDecoder::LoadImage( ++ ProgressiveDecoderIface::Context* pContext) { ++ return static_cast(pContext)->m_Bmp.DecodeImage(); ++} ++ ++// static ++FX_FILESIZE BmpDecoder::GetAvailInput( ++ ProgressiveDecoderIface::Context* pContext) { ++ return static_cast(pContext)->m_Bmp.GetAvailInput(); ++} ++ ++// static ++bool BmpDecoder::Input(ProgressiveDecoderIface::Context* pContext, ++ RetainPtr codec_memory) { ++ auto* ctx = static_cast(pContext); ++ ctx->m_Bmp.SetInputBuffer(std::move(codec_memory)); ++ return true; ++} ++ ++} // namespace fxcodec +diff --git a/core/fxcodec/bmp/bmp_decoder.h b/core/fxcodec/bmp/bmp_decoder.h +new file mode 100644 +index 000000000..46de34d3d +--- /dev/null ++++ b/core/fxcodec/bmp/bmp_decoder.h +@@ -0,0 +1,61 @@ ++// Copyright 2016 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCODEC_BMP_BMP_DECODER_H_ ++#define CORE_FXCODEC_BMP_BMP_DECODER_H_ ++ ++#include ++#include ++ ++#include "core/fxcodec/progressive_decoder_iface.h" ++#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "third_party/base/span.h" ++ ++#ifndef PDF_ENABLE_XFA_BMP ++#error "BMP must be enabled" ++#endif ++ ++namespace fxcodec { ++ ++class CFX_DIBAttribute; ++ ++class BmpDecoder { ++ public: ++ class Delegate { ++ public: ++ virtual bool BmpInputImagePositionBuf(uint32_t rcd_pos) = 0; ++ virtual void BmpReadScanline(uint32_t row_num, ++ pdfium::span row_buf) = 0; ++ }; ++ ++ enum class Status : uint8_t { kFail, kSuccess, kContinue }; ++ ++ static std::unique_ptr StartDecode( ++ Delegate* pDelegate); ++ static Status ReadHeader(ProgressiveDecoderIface::Context* pContext, ++ int32_t* width, ++ int32_t* height, ++ bool* tb_flag, ++ int32_t* components, ++ int32_t* pal_num, ++ const std::vector** palette, ++ CFX_DIBAttribute* pAttribute); ++ static Status LoadImage(ProgressiveDecoderIface::Context* pContext); ++ static FX_FILESIZE GetAvailInput(ProgressiveDecoderIface::Context* pContext); ++ static bool Input(ProgressiveDecoderIface::Context* pContext, ++ RetainPtr codec_memory); ++ ++ BmpDecoder() = delete; ++ BmpDecoder(const BmpDecoder&) = delete; ++ BmpDecoder& operator=(const BmpDecoder&) = delete; ++}; ++ ++} // namespace fxcodec ++ ++using BmpDecoder = fxcodec::BmpDecoder; ++ ++#endif // CORE_FXCODEC_BMP_BMP_DECODER_H_ +diff --git a/core/fxcodec/bmp/bmp_progressive_decoder.cpp b/core/fxcodec/bmp/bmp_progressive_decoder.cpp +new file mode 100644 +index 000000000..4ab9fda5b +--- /dev/null ++++ b/core/fxcodec/bmp/bmp_progressive_decoder.cpp +@@ -0,0 +1,33 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxcodec/bmp/bmp_progressive_decoder.h" ++ ++#include "core/fxcodec/bmp/bmp_decoder.h" ++#include "core/fxcodec/cfx_codec_memory.h" ++ ++namespace fxcodec { ++ ++// static ++BmpProgressiveDecoder* BmpProgressiveDecoder::GetInstance() { ++ static pdfium::base::NoDestructor s; ++ return s.get(); ++} ++ ++BmpProgressiveDecoder::BmpProgressiveDecoder() = default; ++ ++BmpProgressiveDecoder::~BmpProgressiveDecoder() = default; ++ ++FX_FILESIZE BmpProgressiveDecoder::GetAvailInput(Context* context) const { ++ return BmpDecoder::GetAvailInput(context); ++} ++ ++bool BmpProgressiveDecoder::Input(Context* context, ++ RetainPtr codec_memory) { ++ return BmpDecoder::Input(context, codec_memory); ++} ++ ++} // namespace fxcodec +diff --git a/core/fxcodec/bmp/bmp_progressive_decoder.h b/core/fxcodec/bmp/bmp_progressive_decoder.h +new file mode 100644 +index 000000000..f2040c81c +--- /dev/null ++++ b/core/fxcodec/bmp/bmp_progressive_decoder.h +@@ -0,0 +1,39 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCODEC_BMP_BMP_PROGRESSIVE_DECODER_H_ ++#define CORE_FXCODEC_BMP_BMP_PROGRESSIVE_DECODER_H_ ++ ++#include "core/fxcodec/progressive_decoder_iface.h" ++#include "third_party/base/no_destructor.h" ++ ++#ifndef PDF_ENABLE_XFA_BMP ++#error "BMP must be enabled" ++#endif ++ ++namespace fxcodec { ++ ++class BmpProgressiveDecoder final : public ProgressiveDecoderIface { ++ public: ++ static BmpProgressiveDecoder* GetInstance(); ++ ++ // ProgressiveDecoderIface: ++ FX_FILESIZE GetAvailInput(Context* context) const override; ++ bool Input(Context* context, ++ RetainPtr codec_memory) override; ++ ++ private: ++ friend pdfium::base::NoDestructor; ++ ++ BmpProgressiveDecoder(); ++ ~BmpProgressiveDecoder() override; ++}; ++ ++} // namespace fxcodec ++ ++using BmpProgressiveDecoder = fxcodec::BmpProgressiveDecoder; ++ ++#endif // CORE_FXCODEC_BMP_BMP_PROGRESSIVE_DECODER_H_ +diff --git a/core/fxcodec/bmp/bmpmodule.cpp b/core/fxcodec/bmp/bmpmodule.cpp +deleted file mode 100644 +index 78854c43b..000000000 +--- a/core/fxcodec/bmp/bmpmodule.cpp ++++ /dev/null +@@ -1,70 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fxcodec/bmp/bmpmodule.h" +- +-#include +- +-#include "core/fxcodec/bmp/cfx_bmpcontext.h" +-#include "core/fxcodec/cfx_codec_memory.h" +-#include "core/fxcodec/fx_codec.h" +-#include "core/fxge/fx_dib.h" +-#include "third_party/base/ptr_util.h" +- +-namespace fxcodec { +- +-BmpModule::BmpModule() = default; +- +-BmpModule::~BmpModule() = default; +- +-std::unique_ptr BmpModule::Start(Delegate* pDelegate) { +- return pdfium::MakeUnique(this, pDelegate); +-} +- +-BmpModule::Status BmpModule::ReadHeader(Context* pContext, +- int32_t* width, +- int32_t* height, +- bool* tb_flag, +- int32_t* components, +- int32_t* pal_num, +- const std::vector** palette, +- CFX_DIBAttribute* pAttribute) { +- ASSERT(pAttribute); +- +- auto* ctx = static_cast(pContext); +- Status status = ctx->m_Bmp.ReadHeader(); +- if (status != Status::kSuccess) +- return status; +- +- *width = ctx->m_Bmp.width(); +- *height = ctx->m_Bmp.height(); +- *tb_flag = ctx->m_Bmp.img_tb_flag(); +- *components = ctx->m_Bmp.components(); +- *pal_num = ctx->m_Bmp.pal_num(); +- *palette = ctx->m_Bmp.palette(); +- pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_METER; +- pAttribute->m_nXDPI = ctx->m_Bmp.dpi_x(); +- pAttribute->m_nYDPI = ctx->m_Bmp.dpi_y(); +- return Status::kSuccess; +-} +- +-BmpModule::Status BmpModule::LoadImage(Context* pContext) { +- return static_cast(pContext)->m_Bmp.DecodeImage(); +-} +- +-FX_FILESIZE BmpModule::GetAvailInput(Context* pContext) const { +- return static_cast(pContext)->m_Bmp.GetAvailInput(); +-} +- +-bool BmpModule::Input(Context* pContext, +- RetainPtr codec_memory, +- CFX_DIBAttribute*) { +- auto* ctx = static_cast(pContext); +- ctx->m_Bmp.SetInputBuffer(std::move(codec_memory)); +- return true; +-} +- +-} // namespace fxcodec +diff --git a/core/fxcodec/bmp/bmpmodule.h b/core/fxcodec/bmp/bmpmodule.h +deleted file mode 100644 +index 56301284d..000000000 +--- a/core/fxcodec/bmp/bmpmodule.h ++++ /dev/null +@@ -1,56 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXCODEC_BMP_BMPMODULE_H_ +-#define CORE_FXCODEC_BMP_BMPMODULE_H_ +- +-#include +-#include +- +-#include "core/fxcodec/codec_module_iface.h" +-#include "third_party/base/span.h" +- +-namespace fxcodec { +- +-class CFX_DIBAttribute; +- +-class BmpModule final : public ModuleIface { +- public: +- class Delegate { +- public: +- virtual bool BmpInputImagePositionBuf(uint32_t rcd_pos) = 0; +- virtual void BmpReadScanline(uint32_t row_num, +- pdfium::span row_buf) = 0; +- }; +- +- enum class Status : uint8_t { kFail, kSuccess, kContinue }; +- +- BmpModule(); +- ~BmpModule() override; +- +- // ModuleIface: +- FX_FILESIZE GetAvailInput(Context* pContext) const override; +- bool Input(Context* pContext, +- RetainPtr codec_memory, +- CFX_DIBAttribute* pAttribute) override; +- +- std::unique_ptr Start(Delegate* pDelegate); +- Status ReadHeader(Context* pContext, +- int32_t* width, +- int32_t* height, +- bool* tb_flag, +- int32_t* components, +- int32_t* pal_num, +- const std::vector** palette, +- CFX_DIBAttribute* pAttribute); +- Status LoadImage(Context* pContext); +-}; +- +-} // namespace fxcodec +- +-using BmpModule = fxcodec::BmpModule; +- +-#endif // CORE_FXCODEC_BMP_BMPMODULE_H_ +diff --git a/core/fxcodec/bmp/cfx_bmpcontext.cpp b/core/fxcodec/bmp/cfx_bmpcontext.cpp +index a35a58584..3d79901a3 100644 +--- a/core/fxcodec/bmp/cfx_bmpcontext.cpp ++++ b/core/fxcodec/bmp/cfx_bmpcontext.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,9 +8,8 @@ + + namespace fxcodec { + +-CFX_BmpContext::CFX_BmpContext(BmpModule* pModule, +- BmpModule::Delegate* pDelegate) +- : m_Bmp(this), m_pModule(pModule), m_pDelegate(pDelegate) {} ++CFX_BmpContext::CFX_BmpContext(BmpDecoder::Delegate* pDelegate) ++ : m_Bmp(this), m_pDelegate(pDelegate) {} + + CFX_BmpContext::~CFX_BmpContext() = default; + +diff --git a/core/fxcodec/bmp/cfx_bmpcontext.h b/core/fxcodec/bmp/cfx_bmpcontext.h +index e45caf9cd..1f4c5e6a2 100644 +--- a/core/fxcodec/bmp/cfx_bmpcontext.h ++++ b/core/fxcodec/bmp/cfx_bmpcontext.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,21 +7,20 @@ + #ifndef CORE_FXCODEC_BMP_CFX_BMPCONTEXT_H_ + #define CORE_FXCODEC_BMP_CFX_BMPCONTEXT_H_ + +-#include "core/fxcodec/bmp/bmpmodule.h" ++#include "core/fxcodec/bmp/bmp_decoder.h" + #include "core/fxcodec/bmp/cfx_bmpdecompressor.h" + #include "core/fxcodec/bmp/fx_bmp.h" + #include "core/fxcrt/unowned_ptr.h" + + namespace fxcodec { + +-class CFX_BmpContext final : public ModuleIface::Context { ++class CFX_BmpContext final : public ProgressiveDecoderIface::Context { + public: +- CFX_BmpContext(BmpModule* pModule, BmpModule::Delegate* pDelegate); ++ explicit CFX_BmpContext(BmpDecoder::Delegate* pDelegate); + ~CFX_BmpContext() override; + + CFX_BmpDecompressor m_Bmp; +- UnownedPtr const m_pModule; +- UnownedPtr const m_pDelegate; ++ UnownedPtr const m_pDelegate; + }; + + } // namespace fxcodec +diff --git a/core/fxcodec/bmp/cfx_bmpdecompressor.cpp b/core/fxcodec/bmp/cfx_bmpdecompressor.cpp +index 74146479a..b7fdc84ef 100644 +--- a/core/fxcodec/bmp/cfx_bmpdecompressor.cpp ++++ b/core/fxcodec/bmp/cfx_bmpdecompressor.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,17 +6,19 @@ + + #include "core/fxcodec/bmp/cfx_bmpdecompressor.h" + ++#include ++ + #include + #include + #include + + #include "core/fxcodec/bmp/cfx_bmpcontext.h" + #include "core/fxcodec/cfx_codec_memory.h" +-#include "core/fxcodec/fx_codec.h" +-#include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxcrt/fx_system.h" +-#include "third_party/base/logging.h" ++#include "core/fxcrt/span_util.h" ++#include "core/fxge/calculate_pitch.h" + #include "third_party/base/numerics/safe_math.h" + + namespace fxcodec { +@@ -35,7 +37,6 @@ static_assert(sizeof(BmpInfoHeader) == kBmpInfoHeaderSize, + "BmpInfoHeader has wrong size"); + + constexpr uint16_t kBmpSignature = 0x4D42; +-constexpr int32_t kBmpPalOld = 1; + constexpr uint8_t kRleMarker = 0; + constexpr uint8_t kRleEol = 0; + constexpr uint8_t kRleEoi = 1; +@@ -55,7 +56,7 @@ uint8_t HalfRoundUp(uint8_t value) { + + } // namespace + +-CFX_BmpDecompressor::CFX_BmpDecompressor(CFX_BmpContext* context) ++CFX_BmpDecompressor::CFX_BmpDecompressor(const CFX_BmpContext* context) + : context_(context) {} + + CFX_BmpDecompressor::~CFX_BmpDecompressor() = default; +@@ -70,15 +71,15 @@ bool CFX_BmpDecompressor::GetDataPosition(uint32_t rcd_pos) { + return context_->m_pDelegate->BmpInputImagePositionBuf(rcd_pos); + } + +-BmpModule::Status CFX_BmpDecompressor::ReadHeader() { ++BmpDecoder::Status CFX_BmpDecompressor::ReadHeader() { + if (decode_status_ == DecodeStatus::kHeader) { +- BmpModule::Status status = ReadBmpHeader(); +- if (status != BmpModule::Status::kSuccess) ++ BmpDecoder::Status status = ReadBmpHeader(); ++ if (status != BmpDecoder::Status::kSuccess) + return status; + } + + if (decode_status_ != DecodeStatus::kPal) +- return BmpModule::Status::kSuccess; ++ return BmpDecoder::Status::kSuccess; + + if (compress_flag_ == kBmpBitfields) + return ReadBmpBitfields(); +@@ -86,131 +87,131 @@ BmpModule::Status CFX_BmpDecompressor::ReadHeader() { + return ReadBmpPalette(); + } + +-BmpModule::Status CFX_BmpDecompressor::ReadBmpHeader() { ++BmpDecoder::Status CFX_BmpDecompressor::ReadBmpHeader() { + BmpFileHeader bmp_header; +- if (!ReadData(reinterpret_cast(&bmp_header), +- sizeof(BmpFileHeader))) { +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone( ++ pdfium::as_writable_bytes(pdfium::make_span(&bmp_header, 1)))) { ++ return BmpDecoder::Status::kContinue; + } + + bmp_header.bfType = +- FXWORD_GET_LSBFIRST(reinterpret_cast(&bmp_header.bfType)); +- bmp_header.bfOffBits = +- FXDWORD_GET_LSBFIRST(reinterpret_cast(&bmp_header.bfOffBits)); ++ FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast(&bmp_header.bfType)); ++ data_offset_ = FXSYS_UINT32_GET_LSBFIRST( ++ reinterpret_cast(&bmp_header.bfOffBits)); + data_size_ = +- FXDWORD_GET_LSBFIRST(reinterpret_cast(&bmp_header.bfSize)); ++ FXSYS_UINT32_GET_LSBFIRST(reinterpret_cast(&bmp_header.bfSize)); + if (bmp_header.bfType != kBmpSignature) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + + size_t pos = input_buffer_->GetPosition(); +- if (!ReadData(reinterpret_cast(&img_ifh_size_), +- sizeof(img_ifh_size_))) { +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone( ++ pdfium::as_writable_bytes(pdfium::make_span(&img_ifh_size_, 1)))) { ++ return BmpDecoder::Status::kContinue; + } + if (!input_buffer_->Seek(pos)) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + + img_ifh_size_ = +- FXDWORD_GET_LSBFIRST(reinterpret_cast(&img_ifh_size_)); +- pal_type_ = 0; +- BmpModule::Status status = ReadBmpHeaderIfh(); +- if (status != BmpModule::Status::kSuccess) ++ FXSYS_UINT32_GET_LSBFIRST(reinterpret_cast(&img_ifh_size_)); ++ pal_type_ = PalType::kNew; ++ BmpDecoder::Status status = ReadBmpHeaderIfh(); ++ if (status != BmpDecoder::Status::kSuccess) + return status; + + return ReadBmpHeaderDimensions(); + } + +-BmpModule::Status CFX_BmpDecompressor::ReadBmpHeaderIfh() { ++BmpDecoder::Status CFX_BmpDecompressor::ReadBmpHeaderIfh() { + if (img_ifh_size_ == kBmpCoreHeaderSize) { +- pal_type_ = 1; ++ pal_type_ = PalType::kOld; + BmpCoreHeader bmp_core_header; +- if (!ReadData(reinterpret_cast(&bmp_core_header), +- sizeof(BmpCoreHeader))) { +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone(pdfium::as_writable_bytes( ++ pdfium::make_span(&bmp_core_header, 1)))) { ++ return BmpDecoder::Status::kContinue; + } + +- width_ = FXWORD_GET_LSBFIRST( ++ width_ = FXSYS_UINT16_GET_LSBFIRST( + reinterpret_cast(&bmp_core_header.bcWidth)); +- height_ = FXWORD_GET_LSBFIRST( ++ height_ = FXSYS_UINT16_GET_LSBFIRST( + reinterpret_cast(&bmp_core_header.bcHeight)); +- bit_counts_ = FXWORD_GET_LSBFIRST( ++ bit_counts_ = FXSYS_UINT16_GET_LSBFIRST( + reinterpret_cast(&bmp_core_header.bcBitCount)); + compress_flag_ = kBmpRgb; + img_tb_flag_ = false; +- return BmpModule::Status::kSuccess; ++ return BmpDecoder::Status::kSuccess; + } + + if (img_ifh_size_ == kBmpInfoHeaderSize) { + BmpInfoHeader bmp_info_header; +- if (!ReadData(reinterpret_cast(&bmp_info_header), +- sizeof(BmpInfoHeader))) { +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone(pdfium::as_writable_bytes( ++ pdfium::make_span(&bmp_info_header, 1)))) { ++ return BmpDecoder::Status::kContinue; + } + +- width_ = FXDWORD_GET_LSBFIRST( ++ width_ = FXSYS_UINT32_GET_LSBFIRST( + reinterpret_cast(&bmp_info_header.biWidth)); +- int32_t signed_height = FXDWORD_GET_LSBFIRST( ++ int32_t signed_height = FXSYS_UINT32_GET_LSBFIRST( + reinterpret_cast(&bmp_info_header.biHeight)); +- bit_counts_ = FXWORD_GET_LSBFIRST( ++ bit_counts_ = FXSYS_UINT16_GET_LSBFIRST( + reinterpret_cast(&bmp_info_header.biBitCount)); +- compress_flag_ = FXDWORD_GET_LSBFIRST( ++ compress_flag_ = FXSYS_UINT32_GET_LSBFIRST( + reinterpret_cast(&bmp_info_header.biCompression)); +- color_used_ = FXDWORD_GET_LSBFIRST( ++ color_used_ = FXSYS_UINT32_GET_LSBFIRST( + reinterpret_cast(&bmp_info_header.biClrUsed)); +- dpi_x_ = static_cast(FXDWORD_GET_LSBFIRST( ++ dpi_x_ = static_cast(FXSYS_UINT32_GET_LSBFIRST( + reinterpret_cast(&bmp_info_header.biXPelsPerMeter))); +- dpi_y_ = static_cast(FXDWORD_GET_LSBFIRST( ++ dpi_y_ = static_cast(FXSYS_UINT32_GET_LSBFIRST( + reinterpret_cast(&bmp_info_header.biYPelsPerMeter))); + if (!SetHeight(signed_height)) +- return BmpModule::Status::kFail; +- return BmpModule::Status::kSuccess; ++ return BmpDecoder::Status::kFail; ++ return BmpDecoder::Status::kSuccess; + } + + if (img_ifh_size_ <= sizeof(BmpInfoHeader)) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + + FX_SAFE_SIZE_T new_pos = input_buffer_->GetPosition(); + BmpInfoHeader bmp_info_header; +- if (!ReadData(reinterpret_cast(&bmp_info_header), +- sizeof(bmp_info_header))) { +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone( ++ pdfium::as_writable_bytes(pdfium::make_span(&bmp_info_header, 1)))) { ++ return BmpDecoder::Status::kContinue; + } + + new_pos += img_ifh_size_; + if (!new_pos.IsValid()) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + + if (!input_buffer_->Seek(new_pos.ValueOrDie())) +- return BmpModule::Status::kContinue; ++ return BmpDecoder::Status::kContinue; + + uint16_t bi_planes; +- width_ = FXDWORD_GET_LSBFIRST( ++ width_ = FXSYS_UINT32_GET_LSBFIRST( + reinterpret_cast(&bmp_info_header.biWidth)); +- int32_t signed_height = FXDWORD_GET_LSBFIRST( ++ int32_t signed_height = FXSYS_UINT32_GET_LSBFIRST( + reinterpret_cast(&bmp_info_header.biHeight)); +- bit_counts_ = FXWORD_GET_LSBFIRST( ++ bit_counts_ = FXSYS_UINT16_GET_LSBFIRST( + reinterpret_cast(&bmp_info_header.biBitCount)); +- compress_flag_ = FXDWORD_GET_LSBFIRST( ++ compress_flag_ = FXSYS_UINT32_GET_LSBFIRST( + reinterpret_cast(&bmp_info_header.biCompression)); +- color_used_ = FXDWORD_GET_LSBFIRST( ++ color_used_ = FXSYS_UINT32_GET_LSBFIRST( + reinterpret_cast(&bmp_info_header.biClrUsed)); +- bi_planes = FXWORD_GET_LSBFIRST( ++ bi_planes = FXSYS_UINT16_GET_LSBFIRST( + reinterpret_cast(&bmp_info_header.biPlanes)); +- dpi_x_ = FXDWORD_GET_LSBFIRST( ++ dpi_x_ = FXSYS_UINT32_GET_LSBFIRST( + reinterpret_cast(&bmp_info_header.biXPelsPerMeter)); +- dpi_y_ = FXDWORD_GET_LSBFIRST( ++ dpi_y_ = FXSYS_UINT32_GET_LSBFIRST( + reinterpret_cast(&bmp_info_header.biYPelsPerMeter)); + if (!SetHeight(signed_height)) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + if (compress_flag_ != kBmpRgb || bi_planes != 1 || color_used_ != 0) +- return BmpModule::Status::kFail; +- return BmpModule::Status::kSuccess; ++ return BmpDecoder::Status::kFail; ++ return BmpDecoder::Status::kSuccess; + } + +-BmpModule::Status CFX_BmpDecompressor::ReadBmpHeaderDimensions() { ++BmpDecoder::Status CFX_BmpDecompressor::ReadBmpHeaderDimensions() { + if (width_ > kBmpMaxImageDimension || height_ > kBmpMaxImageDimension || + compress_flag_ > kBmpBitfields) { +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + } + + switch (bit_counts_) { +@@ -220,35 +221,35 @@ BmpModule::Status CFX_BmpDecompressor::ReadBmpHeaderDimensions() { + case 16: + case 24: { + if (color_used_ > 1U << bit_counts_) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + break; + } + case 32: + break; + default: +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + } +- FX_SAFE_UINT32 stride = CalculatePitch32(bit_counts_, width_); +- if (!stride.IsValid()) +- return BmpModule::Status::kFail; ++ absl::optional stride = fxge::CalculatePitch32(bit_counts_, width_); ++ if (!stride.has_value()) ++ return BmpDecoder::Status::kFail; + +- src_row_bytes_ = stride.ValueOrDie(); ++ src_row_bytes_ = stride.value(); + switch (bit_counts_) { + case 1: + case 4: + case 8: +- stride = CalculatePitch32(8, width_); +- if (!stride.IsValid()) +- return BmpModule::Status::kFail; +- out_row_bytes_ = stride.ValueOrDie(); ++ stride = fxge::CalculatePitch32(8, width_); ++ if (!stride.has_value()) ++ return BmpDecoder::Status::kFail; ++ out_row_bytes_ = stride.value(); + components_ = 1; + break; + case 16: + case 24: +- stride = CalculatePitch32(24, width_); +- if (!stride.IsValid()) +- return BmpModule::Status::kFail; +- out_row_bytes_ = stride.ValueOrDie(); ++ stride = fxge::CalculatePitch32(24, width_); ++ if (!stride.has_value()) ++ return BmpDecoder::Status::kFail; ++ out_row_bytes_ = stride.value(); + components_ = 3; + break; + case 32: +@@ -259,34 +260,35 @@ BmpModule::Status CFX_BmpDecompressor::ReadBmpHeaderDimensions() { + out_row_buffer_.clear(); + + if (out_row_bytes_ <= 0) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + + out_row_buffer_.resize(out_row_bytes_); + SaveDecodingStatus(DecodeStatus::kPal); +- return BmpModule::Status::kSuccess; ++ return BmpDecoder::Status::kSuccess; + } + +-BmpModule::Status CFX_BmpDecompressor::ReadBmpBitfields() { ++BmpDecoder::Status CFX_BmpDecompressor::ReadBmpBitfields() { + if (bit_counts_ != 16 && bit_counts_ != 32) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + + uint32_t masks[3]; +- if (!ReadData(reinterpret_cast(masks), sizeof(masks))) +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone(pdfium::as_writable_bytes(pdfium::make_span(masks)))) ++ return BmpDecoder::Status::kContinue; + +- mask_red_ = FXDWORD_GET_LSBFIRST(reinterpret_cast(&masks[0])); +- mask_green_ = FXDWORD_GET_LSBFIRST(reinterpret_cast(&masks[1])); +- mask_blue_ = FXDWORD_GET_LSBFIRST(reinterpret_cast(&masks[2])); ++ mask_red_ = FXSYS_UINT32_GET_LSBFIRST(reinterpret_cast(&masks[0])); ++ mask_green_ = ++ FXSYS_UINT32_GET_LSBFIRST(reinterpret_cast(&masks[1])); ++ mask_blue_ = FXSYS_UINT32_GET_LSBFIRST(reinterpret_cast(&masks[2])); + if (mask_red_ & mask_green_ || mask_red_ & mask_blue_ || + mask_green_ & mask_blue_) { +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + } + header_offset_ = std::max(header_offset_, 26 + img_ifh_size_); + SaveDecodingStatus(DecodeStatus::kDataPre); +- return BmpModule::Status::kSuccess; ++ return BmpDecoder::Status::kSuccess; + } + +-BmpModule::Status CFX_BmpDecompressor::ReadBmpPalette() { ++BmpDecoder::Status CFX_BmpDecompressor::ReadBmpPalette() { + if (bit_counts_ == 16) { + mask_red_ = 0x7C00; + mask_green_ = 0x03E0; +@@ -297,15 +299,15 @@ BmpModule::Status CFX_BmpDecompressor::ReadBmpPalette() { + pal_num_ = 1 << bit_counts_; + if (color_used_ != 0) + pal_num_ = color_used_; +- uint32_t src_pal_size = pal_num_ * (pal_type_ ? 3 : 4); +- std::vector> src_pal(src_pal_size); ++ size_t src_pal_size = pal_num_ * PaletteChannelCount(); ++ DataVector src_pal(src_pal_size); + uint8_t* src_pal_data = src_pal.data(); +- if (!ReadData(src_pal_data, src_pal_size)) +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone(src_pal)) ++ return BmpDecoder::Status::kContinue; + + palette_.resize(pal_num_); + int32_t src_pal_index = 0; +- if (pal_type_ == kBmpPalOld) { ++ if (pal_type_ == PalType::kOld) { + while (src_pal_index < pal_num_) { + palette_[src_pal_index++] = BMP_PAL_ENCODE( + 0x00, src_pal_data[2], src_pal_data[1], src_pal_data[0]); +@@ -320,9 +322,9 @@ BmpModule::Status CFX_BmpDecompressor::ReadBmpPalette() { + } + } + header_offset_ = std::max( +- header_offset_, 14 + img_ifh_size_ + pal_num_ * (pal_type_ ? 3 : 4)); ++ header_offset_, 14 + img_ifh_size_ + pal_num_ * PaletteChannelCount()); + SaveDecodingStatus(DecodeStatus::kDataPre); +- return BmpModule::Status::kSuccess; ++ return BmpDecoder::Status::kSuccess; + } + + bool CFX_BmpDecompressor::ValidateFlag() const { +@@ -337,19 +339,23 @@ bool CFX_BmpDecompressor::ValidateFlag() const { + } + } + +-BmpModule::Status CFX_BmpDecompressor::DecodeImage() { ++BmpDecoder::Status CFX_BmpDecompressor::DecodeImage() { + if (decode_status_ == DecodeStatus::kDataPre) { +- input_buffer_->Seek(0); +- if (!GetDataPosition(header_offset_)) { ++ // In order to tolerate certain corrupt BMP files, use the header offset if ++ // the data offset would point into the header. ++ data_offset_ = std::max(header_offset_, data_offset_); ++ ++ input_buffer_->Seek(input_buffer_->GetSize()); ++ if (!GetDataPosition(data_offset_)) { + decode_status_ = DecodeStatus::kTail; +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + } + + row_num_ = 0; + SaveDecodingStatus(DecodeStatus::kData); + } + if (decode_status_ != DecodeStatus::kData || !ValidateFlag()) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + + switch (compress_flag_) { + case kBmpRgb: +@@ -360,7 +366,7 @@ BmpModule::Status CFX_BmpDecompressor::DecodeImage() { + case kBmpRle4: + return DecodeRLE4(); + default: +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + } + } + +@@ -368,26 +374,41 @@ bool CFX_BmpDecompressor::ValidateColorIndex(uint8_t val) const { + return val < pal_num_; + } + +-BmpModule::Status CFX_BmpDecompressor::DecodeRGB() { +- std::vector> dest_buf(src_row_bytes_); ++BmpDecoder::Status CFX_BmpDecompressor::DecodeRGB() { ++ DataVector dest_buf(src_row_bytes_); + while (row_num_ < height_) { + size_t idx = 0; +- if (!ReadData(dest_buf.data(), src_row_bytes_)) +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone(dest_buf)) ++ return BmpDecoder::Status::kContinue; + + SaveDecodingStatus(DecodeStatus::kData); + switch (bit_counts_) { + case 1: { +- for (uint32_t col = 0; col < width_; ++col) +- out_row_buffer_[idx++] = ++ for (uint32_t col = 0; col < width_; ++col) { ++ uint8_t index = + dest_buf[col >> 3] & (0x80 >> (col % 8)) ? 0x01 : 0x00; ++ if (!ValidateColorIndex(index)) ++ return BmpDecoder::Status::kFail; ++ out_row_buffer_[idx++] = index; ++ } + break; + } + case 4: { + for (uint32_t col = 0; col < width_; ++col) { +- out_row_buffer_[idx++] = (col & 0x01) +- ? (dest_buf[col >> 1] & 0x0F) ++ uint8_t index = (col & 0x01) ? (dest_buf[col >> 1] & 0x0F) + : ((dest_buf[col >> 1] & 0xF0) >> 4); ++ if (!ValidateColorIndex(index)) ++ return BmpDecoder::Status::kFail; ++ out_row_buffer_[idx++] = index; ++ } ++ break; ++ } ++ case 8: { ++ for (uint32_t col = 0; col < width_; ++col) { ++ uint8_t index = dest_buf[col]; ++ if (!ValidateColorIndex(index)) ++ return BmpDecoder::Status::kFail; ++ out_row_buffer_[idx++] = index; + } + break; + } +@@ -407,12 +428,12 @@ BmpModule::Status CFX_BmpDecompressor::DecodeRGB() { + green_bits += blue_bits; + red_bits += green_bits; + if (blue_bits > 8 || green_bits < 8 || red_bits < 8) +- return BmpModule::Status::kContinue; ++ return BmpDecoder::Status::kContinue; + blue_bits = 8 - blue_bits; + green_bits -= 8; + red_bits -= 8; + for (uint32_t col = 0; col < width_; ++col) { +- *buf = FXWORD_GET_LSBFIRST(reinterpret_cast(buf)); ++ *buf = FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast(buf)); + out_row_buffer_[idx++] = + static_cast((*buf & mask_blue_) << blue_bits); + out_row_buffer_[idx++] = +@@ -422,47 +443,42 @@ BmpModule::Status CFX_BmpDecompressor::DecodeRGB() { + } + break; + } +- case 8: + case 24: + case 32: +- uint8_t* dest_buf_data = dest_buf.data(); +- std::copy(dest_buf_data, dest_buf_data + src_row_bytes_, +- out_row_buffer_.begin()); ++ // TODO(crbug.com/pdfium/1901): Apply bitfields. ++ fxcrt::spancpy(pdfium::make_span(out_row_buffer_), ++ pdfium::make_span(dest_buf).first(src_row_bytes_)); + idx += src_row_bytes_; + break; + } +- for (uint8_t byte : out_row_buffer_) { +- if (!ValidateColorIndex(byte)) +- return BmpModule::Status::kFail; +- } + ReadNextScanline(); + } + SaveDecodingStatus(DecodeStatus::kTail); +- return BmpModule::Status::kSuccess; ++ return BmpDecoder::Status::kSuccess; + } + +-BmpModule::Status CFX_BmpDecompressor::DecodeRLE8() { ++BmpDecoder::Status CFX_BmpDecompressor::DecodeRLE8() { + uint8_t first_part; + col_num_ = 0; + while (true) { +- if (!ReadData(&first_part, sizeof(first_part))) +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone(pdfium::make_span(&first_part, 1))) ++ return BmpDecoder::Status::kContinue; + + switch (first_part) { + case kRleMarker: { +- if (!ReadData(&first_part, sizeof(first_part))) +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone(pdfium::make_span(&first_part, 1))) ++ return BmpDecoder::Status::kContinue; + + switch (first_part) { + case kRleEol: { + if (row_num_ >= height_) { + SaveDecodingStatus(DecodeStatus::kTail); +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + } + + ReadNextScanline(); + col_num_ = 0; +- std::fill(out_row_buffer_.begin(), out_row_buffer_.end(), 0); ++ fxcrt::spanset(pdfium::make_span(out_row_buffer_), 0); + SaveDecodingStatus(DecodeStatus::kData); + continue; + } +@@ -470,42 +486,42 @@ BmpModule::Status CFX_BmpDecompressor::DecodeRLE8() { + if (row_num_ < height_) + ReadNextScanline(); + SaveDecodingStatus(DecodeStatus::kTail); +- return BmpModule::Status::kSuccess; ++ return BmpDecoder::Status::kSuccess; + } + case kRleDelta: { + uint8_t delta[2]; +- if (!ReadData(delta, sizeof(delta))) +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone(delta)) ++ return BmpDecoder::Status::kContinue; + + col_num_ += delta[0]; + size_t bmp_row_num__next = row_num_ + delta[1]; + if (col_num_ >= out_row_bytes_ || bmp_row_num__next >= height_) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + + while (row_num_ < bmp_row_num__next) { +- std::fill(out_row_buffer_.begin(), out_row_buffer_.end(), 0); ++ fxcrt::spanset(pdfium::make_span(out_row_buffer_), 0); + ReadNextScanline(); + } + break; + } + default: { +- int32_t avail_size = out_row_bytes_ - col_num_; ++ int32_t avail_size = ++ pdfium::base::checked_cast(out_row_bytes_ - col_num_); + if (!avail_size || static_cast(first_part) > avail_size) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + + size_t second_part_size = + first_part & 1 ? first_part + 1 : first_part; +- std::vector> second_part( +- second_part_size); +- uint8_t* second_part_data = second_part.data(); +- if (!ReadData(second_part_data, second_part_size)) +- return BmpModule::Status::kContinue; ++ DataVector second_part(second_part_size); ++ if (!ReadAllOrNone(second_part)) ++ return BmpDecoder::Status::kContinue; ++ ++ fxcrt::spancpy(pdfium::make_span(out_row_buffer_).subspan(col_num_), ++ pdfium::make_span(second_part).first(first_part)); + +- std::copy(second_part_data, second_part_data + first_part, +- out_row_buffer_.begin() + col_num_); + for (size_t i = col_num_; i < col_num_ + first_part; ++i) { + if (!ValidateColorIndex(out_row_buffer_[i])) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + } + col_num_ += first_part; + } +@@ -513,47 +529,49 @@ BmpModule::Status CFX_BmpDecompressor::DecodeRLE8() { + break; + } + default: { +- int32_t avail_size = out_row_bytes_ - col_num_; ++ int32_t avail_size = ++ pdfium::base::checked_cast(out_row_bytes_ - col_num_); + if (!avail_size || static_cast(first_part) > avail_size) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + + uint8_t second_part; +- if (!ReadData(&second_part, sizeof(second_part))) +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone(pdfium::make_span(&second_part, 1))) ++ return BmpDecoder::Status::kContinue; ++ ++ fxcrt::spanset( ++ pdfium::make_span(out_row_buffer_).subspan(col_num_, first_part), ++ second_part); + +- std::fill(out_row_buffer_.begin() + col_num_, +- out_row_buffer_.begin() + col_num_ + first_part, second_part); + if (!ValidateColorIndex(out_row_buffer_[col_num_])) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + col_num_ += first_part; + } + } + } +- return BmpModule::Status::kFail; + } + +-BmpModule::Status CFX_BmpDecompressor::DecodeRLE4() { ++BmpDecoder::Status CFX_BmpDecompressor::DecodeRLE4() { + uint8_t first_part; + col_num_ = 0; + while (true) { +- if (!ReadData(&first_part, sizeof(first_part))) +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone(pdfium::make_span(&first_part, 1))) ++ return BmpDecoder::Status::kContinue; + + switch (first_part) { + case kRleMarker: { +- if (!ReadData(&first_part, sizeof(first_part))) +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone(pdfium::make_span(&first_part, 1))) ++ return BmpDecoder::Status::kContinue; + + switch (first_part) { + case kRleEol: { + if (row_num_ >= height_) { + SaveDecodingStatus(DecodeStatus::kTail); +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + } + + ReadNextScanline(); + col_num_ = 0; +- std::fill(out_row_buffer_.begin(), out_row_buffer_.end(), 0); ++ fxcrt::spanset(pdfium::make_span(out_row_buffer_), 0); + SaveDecodingStatus(DecodeStatus::kData); + continue; + } +@@ -561,47 +579,47 @@ BmpModule::Status CFX_BmpDecompressor::DecodeRLE4() { + if (row_num_ < height_) + ReadNextScanline(); + SaveDecodingStatus(DecodeStatus::kTail); +- return BmpModule::Status::kSuccess; ++ return BmpDecoder::Status::kSuccess; + } + case kRleDelta: { + uint8_t delta[2]; +- if (!ReadData(delta, sizeof(delta))) +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone(delta)) ++ return BmpDecoder::Status::kContinue; + + col_num_ += delta[0]; + size_t bmp_row_num__next = row_num_ + delta[1]; + if (col_num_ >= out_row_bytes_ || bmp_row_num__next >= height_) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + + while (row_num_ < bmp_row_num__next) { +- std::fill(out_row_buffer_.begin(), out_row_buffer_.end(), 0); ++ fxcrt::spanset(pdfium::make_span(out_row_buffer_), 0); + ReadNextScanline(); + } + break; + } + default: { +- int32_t avail_size = out_row_bytes_ - col_num_; ++ int32_t avail_size = ++ pdfium::base::checked_cast(out_row_bytes_ - col_num_); + if (!avail_size) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + uint8_t size = HalfRoundUp(first_part); + if (static_cast(first_part) > avail_size) { + if (size + (col_num_ >> 1) > src_row_bytes_) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + + first_part = avail_size - 1; + } + size_t second_part_size = size & 1 ? size + 1 : size; +- std::vector> second_part( +- second_part_size); ++ DataVector second_part(second_part_size); + uint8_t* second_part_data = second_part.data(); +- if (!ReadData(second_part_data, second_part_size)) +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone(second_part)) ++ return BmpDecoder::Status::kContinue; + + for (uint8_t i = 0; i < first_part; i++) { + uint8_t color = (i & 0x01) ? (*second_part_data++ & 0x0F) + : (*second_part_data & 0xF0) >> 4; + if (!ValidateColorIndex(color)) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + + out_row_buffer_[col_num_++] = color; + } +@@ -610,38 +628,48 @@ BmpModule::Status CFX_BmpDecompressor::DecodeRLE4() { + break; + } + default: { +- int32_t avail_size = out_row_bytes_ - col_num_; ++ int32_t avail_size = ++ pdfium::base::checked_cast(out_row_bytes_ - col_num_); + if (!avail_size) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + + if (static_cast(first_part) > avail_size) { + uint8_t size = HalfRoundUp(first_part); + if (size + (col_num_ >> 1) > src_row_bytes_) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + + first_part = avail_size - 1; + } + uint8_t second_part; +- if (!ReadData(&second_part, sizeof(second_part))) +- return BmpModule::Status::kContinue; ++ if (!ReadAllOrNone(pdfium::make_span(&second_part, 1))) ++ return BmpDecoder::Status::kContinue; + + for (uint8_t i = 0; i < first_part; i++) { + uint8_t second_byte = second_part; + second_byte = + i & 0x01 ? (second_byte & 0x0F) : (second_byte & 0xF0) >> 4; + if (!ValidateColorIndex(second_byte)) +- return BmpModule::Status::kFail; ++ return BmpDecoder::Status::kFail; + + out_row_buffer_[col_num_++] = second_byte; + } + } + } + } +- return BmpModule::Status::kFail; + } + +-bool CFX_BmpDecompressor::ReadData(uint8_t* destination, uint32_t size) { +- return input_buffer_ && input_buffer_->ReadBlock(destination, size) == size; ++bool CFX_BmpDecompressor::ReadAllOrNone(pdfium::span buf) { ++ if (!input_buffer_) ++ return false; ++ ++ size_t original_position = input_buffer_->GetPosition(); ++ size_t read = input_buffer_->ReadBlock(buf); ++ if (read < buf.size()) { ++ input_buffer_->Seek(original_position); ++ return false; ++ } ++ ++ return true; + } + + void CFX_BmpDecompressor::SaveDecodingStatus(DecodeStatus status) { +diff --git a/core/fxcodec/bmp/cfx_bmpdecompressor.h b/core/fxcodec/bmp/cfx_bmpdecompressor.h +index 3f4e2d81e..408e39c69 100644 +--- a/core/fxcodec/bmp/cfx_bmpdecompressor.h ++++ b/core/fxcodec/bmp/cfx_bmpdecompressor.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,16 @@ + #ifndef CORE_FXCODEC_BMP_CFX_BMPDECOMPRESSOR_H_ + #define CORE_FXCODEC_BMP_CFX_BMPDECOMPRESSOR_H_ + ++#include ++ + #include + +-#include "core/fxcodec/bmp/bmpmodule.h" ++#include "core/fxcodec/bmp/bmp_decoder.h" + #include "core/fxcodec/bmp/fx_bmp.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "third_party/base/span.h" + + class CFX_CodecMemory; + +@@ -22,11 +26,11 @@ class CFX_BmpContext; + + class CFX_BmpDecompressor { + public: +- explicit CFX_BmpDecompressor(CFX_BmpContext* context); ++ explicit CFX_BmpDecompressor(const CFX_BmpContext* context); + ~CFX_BmpDecompressor(); + +- BmpModule::Status DecodeImage(); +- BmpModule::Status ReadHeader(); ++ BmpDecoder::Status DecodeImage(); ++ BmpDecoder::Status ReadHeader(); + void SetInputBuffer(RetainPtr codec_memory); + FX_FILESIZE GetAvailInput() const; + +@@ -48,24 +52,27 @@ class CFX_BmpDecompressor { + kTail, + }; + +- BmpModule::Status ReadBmpHeader(); +- BmpModule::Status ReadBmpHeaderIfh(); +- BmpModule::Status ReadBmpHeaderDimensions(); +- BmpModule::Status ReadBmpBitfields(); +- BmpModule::Status ReadBmpPalette(); ++ enum class PalType : bool { kNew, kOld }; ++ ++ BmpDecoder::Status ReadBmpHeader(); ++ BmpDecoder::Status ReadBmpHeaderIfh(); ++ BmpDecoder::Status ReadBmpHeaderDimensions(); ++ BmpDecoder::Status ReadBmpBitfields(); ++ BmpDecoder::Status ReadBmpPalette(); + bool GetDataPosition(uint32_t cur_pos); + void ReadNextScanline(); +- BmpModule::Status DecodeRGB(); +- BmpModule::Status DecodeRLE8(); +- BmpModule::Status DecodeRLE4(); +- bool ReadData(uint8_t* destination, uint32_t size); ++ BmpDecoder::Status DecodeRGB(); ++ BmpDecoder::Status DecodeRLE8(); ++ BmpDecoder::Status DecodeRLE4(); ++ bool ReadAllOrNone(pdfium::span buf); + void SaveDecodingStatus(DecodeStatus status); + bool ValidateColorIndex(uint8_t val) const; + bool ValidateFlag() const; + bool SetHeight(int32_t signed_height); ++ int PaletteChannelCount() const { return pal_type_ == PalType::kNew ? 4 : 3; } + +- UnownedPtr const context_; +- std::vector out_row_buffer_; ++ UnownedPtr const context_; ++ DataVector out_row_buffer_; + std::vector palette_; + uint32_t header_offset_ = 0; + uint32_t width_ = 0; +@@ -78,7 +85,8 @@ class CFX_BmpDecompressor { + uint16_t bit_counts_ = 0; + uint32_t color_used_ = 0; + int32_t pal_num_ = 0; +- int32_t pal_type_ = 0; ++ PalType pal_type_ = PalType::kNew; ++ uint32_t data_offset_ = 0; + uint32_t data_size_ = 0; + uint32_t img_ifh_size_ = 0; + uint32_t row_num_ = 0; +diff --git a/core/fxcodec/bmp/fx_bmp.h b/core/fxcodec/bmp/fx_bmp.h +index 79c61bbed..ae5cda13a 100644 +--- a/core/fxcodec/bmp/fx_bmp.h ++++ b/core/fxcodec/bmp/fx_bmp.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcodec/cfx_codec_memory.cpp b/core/fxcodec/cfx_codec_memory.cpp +index 256e15c49..e3e1ffa9c 100644 +--- a/core/fxcodec/cfx_codec_memory.cpp ++++ b/core/fxcodec/cfx_codec_memory.cpp +@@ -1,9 +1,11 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcodec/cfx_codec_memory.h" + ++#include ++ + #include + + CFX_CodecMemory::CFX_CodecMemory(size_t buffer_size) +@@ -19,12 +21,12 @@ bool CFX_CodecMemory::Seek(size_t pos) { + return true; + } + +-size_t CFX_CodecMemory::ReadBlock(void* buffer, size_t size) { +- if (!buffer || !size || IsEOF()) ++size_t CFX_CodecMemory::ReadBlock(pdfium::span buffer) { ++ if (buffer.empty() || IsEOF()) + return 0; + +- size_t bytes_to_read = std::min(size, size_ - pos_); +- memcpy(buffer, buffer_.get() + pos_, bytes_to_read); ++ size_t bytes_to_read = std::min(buffer.size(), size_ - pos_); ++ memcpy(buffer.data(), buffer_.get() + pos_, bytes_to_read); + pos_ += bytes_to_read; + return bytes_to_read; + } +diff --git a/core/fxcodec/cfx_codec_memory.h b/core/fxcodec/cfx_codec_memory.h +index 57c1ae692..a3044c3b6 100644 +--- a/core/fxcodec/cfx_codec_memory.h ++++ b/core/fxcodec/cfx_codec_memory.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,15 +13,18 @@ + + class CFX_CodecMemory final : public Retainable { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + +- pdfium::span GetSpan() { return {buffer_.get(), size_}; } +- uint8_t* GetBuffer() { return buffer_.get(); } ++ // Returns a span over the unconsumed contents of the buffer. ++ pdfium::span GetUnconsumedSpan() { ++ return GetBufferSpan().subspan(pos_); ++ } ++ ++ pdfium::span GetBufferSpan() { return {buffer_.get(), size_}; } + size_t GetSize() const { return size_; } + size_t GetPosition() const { return pos_; } + bool IsEOF() const { return pos_ >= size_; } +- size_t ReadBlock(void* buffer, size_t size); ++ size_t ReadBlock(pdfium::span buffer); + + // Sets the cursor position to |pos| if possible. + bool Seek(size_t pos); +diff --git a/core/fxcodec/fax/faxmodule.cpp b/core/fxcodec/fax/faxmodule.cpp +index 22ba76408..075c72d08 100644 +--- a/core/fxcodec/fax/faxmodule.cpp ++++ b/core/fxcodec/fax/faxmodule.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,19 +6,30 @@ + + #include "core/fxcodec/fax/faxmodule.h" + ++#include ++ + #include + #include + #include +-#include ++#include + + #include "build/build_config.h" +-#include "core/fxcodec/fx_codec.h" + #include "core/fxcodec/scanlinedecoder.h" +-#include "core/fxcrt/cfx_binarybuf.h" +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxcrt/binary_buffer.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_2d_size.h" ++#include "core/fxcrt/fx_memory.h" ++#include "core/fxge/calculate_pitch.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/cxx17_backports.h" ++#include "third_party/base/numerics/safe_conversions.h" ++#include "third_party/base/span.h" ++ ++#if BUILDFLAG(IS_WIN) ++#include "core/fxcrt/span_util.h" ++#include "core/fxge/dib/cfx_dibbase.h" ++#endif + + namespace fxcodec { + +@@ -44,8 +55,11 @@ constexpr int kFaxMaxImageDimension = 65535; + constexpr int kFaxBpc = 1; + constexpr int kFaxComps = 1; + +-int FindBit(const uint8_t* data_buf, int max_pos, int start_pos, bool bit) { +- ASSERT(start_pos >= 0); ++int FindBit(pdfium::span data_buf, ++ int max_pos, ++ int start_pos, ++ bool bit) { ++ DCHECK(start_pos >= 0); + if (start_pos >= max_pos) + return max_pos; + +@@ -72,7 +86,8 @@ int FindBit(const uint8_t* data_buf, int max_pos, int start_pos, bool bit) { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + const uint8_t* skip_block = bit ? skip_block_0 : skip_block_1; + while (byte_pos < max_byte - kBulkReadSize && +- memcmp(data_buf + byte_pos, skip_block, kBulkReadSize) == 0) { ++ memcmp(data_buf.subspan(byte_pos).data(), skip_block, ++ kBulkReadSize) == 0) { + byte_pos += kBulkReadSize; + } + } +@@ -94,20 +109,20 @@ void FaxG4FindB1B2(pdfium::span ref_buf, + int* b1, + int* b2) { + bool first_bit = a0 < 0 || (ref_buf[a0 / 8] & (1 << (7 - a0 % 8))) != 0; +- *b1 = FindBit(ref_buf.data(), columns, a0 + 1, !first_bit); ++ *b1 = FindBit(ref_buf, columns, a0 + 1, !first_bit); + if (*b1 >= columns) { + *b1 = *b2 = columns; + return; + } + if (first_bit == !a0color) { +- *b1 = FindBit(ref_buf.data(), columns, *b1 + 1, first_bit); ++ *b1 = FindBit(ref_buf, columns, *b1 + 1, first_bit); + first_bit = !first_bit; + } + if (*b1 >= columns) { + *b1 = *b2 = columns; + return; + } +- *b2 = FindBit(ref_buf.data(), columns, *b1 + 1, first_bit); ++ *b2 = FindBit(ref_buf, columns, *b1 + 1, first_bit); + } + + void FaxFillBits(uint8_t* dest_buf, int columns, int startpos, int endpos) { +@@ -138,7 +153,7 @@ inline bool NextBit(const uint8_t* src_buf, int* bitpos) { + return !!(src_buf[pos / 8] & (1 << (7 - pos % 8))); + } + +-const uint8_t FaxBlackRunIns[] = { ++const uint8_t kFaxBlackRunIns[] = { + 0, 2, 0x02, 3, 0, 0x03, + 2, 0, 2, 0x02, 1, 0, + 0x03, 4, 0, 2, 0x02, 6, +@@ -195,7 +210,7 @@ const uint8_t FaxBlackRunIns[] = { + 1088 / 256, 0x76, 1152 % 256, 1152 / 256, 0x77, 1216 % 256, + 1216 / 256, 0xff}; + +-const uint8_t FaxWhiteRunIns[] = { ++const uint8_t kFaxWhiteRunIns[] = { + 0, 0, 0, 6, 0x07, 2, + 0, 0x08, 3, 0, 0x0B, 4, + 0, 0x0C, 5, 0, 0x0E, 6, +@@ -253,13 +268,13 @@ const uint8_t FaxWhiteRunIns[] = { + 0xff, + }; + +-int FaxGetRun(const uint8_t* ins_array, ++int FaxGetRun(pdfium::span ins_array, + const uint8_t* src_buf, + int* bitpos, + int bitsize) { + uint32_t code = 0; + int ins_off = 0; +- while (1) { ++ while (true) { + uint8_t ins = ins_array[ins_off++]; + if (ins == 0xff) + return -1; +@@ -288,7 +303,7 @@ void FaxG4GetRow(const uint8_t* src_buf, + int columns) { + int a0 = -1; + bool a0color = true; +- while (1) { ++ while (true) { + if (*bitpos >= bitsize) + return; + +@@ -312,8 +327,9 @@ void FaxG4GetRow(const uint8_t* src_buf, + v_delta = bit2 ? 1 : -1; + } else if (bit2) { + int run_len1 = 0; +- while (1) { +- int run = FaxGetRun(a0color ? FaxWhiteRunIns : FaxBlackRunIns, ++ while (true) { ++ int run = FaxGetRun(a0color ? pdfium::make_span(kFaxWhiteRunIns) ++ : pdfium::make_span(kFaxBlackRunIns), + src_buf, bitpos, bitsize); + run_len1 += run; + if (run < 64) +@@ -329,8 +345,9 @@ void FaxG4GetRow(const uint8_t* src_buf, + FaxFillBits(dest_buf, columns, a0, a1); + + int run_len2 = 0; +- while (1) { +- int run = FaxGetRun(a0color ? FaxBlackRunIns : FaxWhiteRunIns, ++ while (true) { ++ int run = FaxGetRun(a0color ? pdfium::make_span(kFaxBlackRunIns) ++ : pdfium::make_span(kFaxWhiteRunIns), + src_buf, bitpos, bitsize); + run_len2 += run; + if (run < 64) +@@ -424,14 +441,15 @@ void FaxGet1DLine(const uint8_t* src_buf, + int columns) { + bool color = true; + int startpos = 0; +- while (1) { ++ while (true) { + if (*bitpos >= bitsize) + return; + + int run_len = 0; +- while (1) { +- int run = FaxGetRun(color ? FaxWhiteRunIns : FaxBlackRunIns, src_buf, +- bitpos, bitsize); ++ while (true) { ++ int run = FaxGetRun(color ? pdfium::make_span(kFaxWhiteRunIns) ++ : pdfium::make_span(kFaxBlackRunIns), ++ src_buf, bitpos, bitsize); + if (run < 0) { + while (*bitpos < bitsize) { + if (NextBit(src_buf, bitpos)) +@@ -466,8 +484,8 @@ class FaxDecoder final : public ScanlineDecoder { + ~FaxDecoder() override; + + // ScanlineDecoder: +- bool v_Rewind() override; +- uint8_t* v_GetNextLine() override; ++ bool Rewind() override; ++ pdfium::span GetNextLine() override; + uint32_t GetSrcOffset() override; + + private: +@@ -479,8 +497,8 @@ class FaxDecoder final : public ScanlineDecoder { + const bool m_bEndOfLine; + const bool m_bBlack; + const pdfium::span m_SrcSpan; +- std::vector> m_ScanlineBuf; +- std::vector> m_RefBuf; ++ DataVector m_ScanlineBuf; ++ DataVector m_RefBuf; + }; + + FaxDecoder::FaxDecoder(pdfium::span src_span, +@@ -496,7 +514,7 @@ FaxDecoder::FaxDecoder(pdfium::span src_span, + height, + kFaxComps, + kFaxBpc, +- CalculatePitch32(kFaxBpc, width).ValueOrDie()), ++ fxge::CalculatePitch32OrDie(kFaxBpc, width)), + m_Encoding(K), + m_bByteAlign(EncodedByteAlign), + m_bEndOfLine(EndOfLine), +@@ -505,19 +523,22 @@ FaxDecoder::FaxDecoder(pdfium::span src_span, + m_ScanlineBuf(m_Pitch), + m_RefBuf(m_Pitch) {} + +-FaxDecoder::~FaxDecoder() = default; ++FaxDecoder::~FaxDecoder() { ++ // Span in superclass can't outlive our buffer. ++ m_pLastScanline = pdfium::span(); ++} + +-bool FaxDecoder::v_Rewind() { ++bool FaxDecoder::Rewind() { + memset(m_RefBuf.data(), 0xff, m_RefBuf.size()); + m_bitpos = 0; + return true; + } + +-uint8_t* FaxDecoder::v_GetNextLine() { +- int bitsize = m_SrcSpan.size() * 8; ++pdfium::span FaxDecoder::GetNextLine() { ++ int bitsize = pdfium::base::checked_cast(m_SrcSpan.size() * 8); + FaxSkipEOL(m_SrcSpan.data(), bitsize, &m_bitpos); + if (m_bitpos >= bitsize) +- return nullptr; ++ return pdfium::span(); + + memset(m_ScanlineBuf.data(), 0xff, m_ScanlineBuf.size()); + if (m_Encoding < 0) { +@@ -555,16 +576,17 @@ uint8_t* FaxDecoder::v_GetNextLine() { + } + if (m_bBlack) + InvertBuffer(); +- return m_ScanlineBuf.data(); ++ return m_ScanlineBuf; + } + + uint32_t FaxDecoder::GetSrcOffset() { +- return std::min(static_cast((m_bitpos + 7) / 8), m_SrcSpan.size()); ++ return pdfium::base::checked_cast( ++ std::min((m_bitpos + 7) / 8, m_SrcSpan.size())); + } + + void FaxDecoder::InvertBuffer() { +- ASSERT(m_Pitch == m_ScanlineBuf.size()); +- ASSERT(m_Pitch % 4 == 0); ++ DCHECK_EQ(m_Pitch, m_ScanlineBuf.size()); ++ DCHECK_EQ(m_Pitch % 4, 0); + uint32_t* data = reinterpret_cast(m_ScanlineBuf.data()); + for (size_t i = 0; i < m_ScanlineBuf.size() / 4; ++i) + data[i] = ~data[i]; +@@ -596,9 +618,8 @@ std::unique_ptr FaxModule::CreateDecoder( + return nullptr; + } + +- return pdfium::MakeUnique(src_span, actual_width, actual_height, +- K, EndOfLine, EncodedByteAlign, +- BlackIs1); ++ return std::make_unique(src_span, actual_width, actual_height, K, ++ EndOfLine, EncodedByteAlign, BlackIs1); + } + + // static +@@ -609,9 +630,9 @@ int FaxModule::FaxG4Decode(const uint8_t* src_buf, + int height, + int pitch, + uint8_t* dest_buf) { +- ASSERT(pitch != 0); ++ DCHECK(pitch != 0); + +- std::vector> ref_buf(pitch, 0xff); ++ DataVector ref_buf(pitch, 0xff); + int bitpos = starting_bitpos; + for (int iRow = 0; iRow < height; ++iRow) { + uint8_t* line_buf = dest_buf + iRow * pitch; +@@ -622,7 +643,7 @@ int FaxModule::FaxG4Decode(const uint8_t* src_buf, + return bitpos; + } + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + namespace { + const uint8_t BlackRunTerminator[128] = { + 0x37, 10, 0x02, 3, 0x03, 2, 0x02, 2, 0x03, 3, 0x03, 4, 0x02, 4, +@@ -668,34 +689,37 @@ const uint8_t WhiteRunMarkup[80] = { + + class FaxEncoder { + public: +- FaxEncoder(const uint8_t* src_buf, int width, int height, int pitch); ++ explicit FaxEncoder(RetainPtr src); + ~FaxEncoder(); +- void Encode(std::unique_ptr* dest_buf, +- uint32_t* dest_size); ++ DataVector Encode(); + + private: +- void FaxEncode2DLine(const uint8_t* src_buf); ++ void FaxEncode2DLine(pdfium::span src_span); + void FaxEncodeRun(int run, bool bWhite); + void AddBitStream(int data, int bitlen); + ++ // Must outlive `m_RefLineSpan`. ++ RetainPtr const m_Src; + int m_DestBitpos = 0; + const int m_Cols; + const int m_Rows; + const int m_Pitch; +- const uint8_t* m_pSrcBuf; +- CFX_BinaryBuf m_DestBuf; +- std::vector> m_RefLine; +- std::vector> m_LineBuf; ++ BinaryBuffer m_DestBuf; ++ // Must outlive `m_RefLineSpan`. ++ const DataVector m_InitialRefLine; ++ DataVector m_LineBuf; ++ pdfium::span m_RefLineSpan; + }; + +-FaxEncoder::FaxEncoder(const uint8_t* src_buf, int width, int height, int pitch) +- : m_Cols(width), +- m_Rows(height), +- m_Pitch(pitch), +- m_pSrcBuf(src_buf), +- m_RefLine(pitch, 0xff), +- m_LineBuf( +- pdfium::Vector2D>(8, pitch)) { ++FaxEncoder::FaxEncoder(RetainPtr src) ++ : m_Src(std::move(src)), ++ m_Cols(m_Src->GetWidth()), ++ m_Rows(m_Src->GetHeight()), ++ m_Pitch(m_Src->GetPitch()), ++ m_InitialRefLine(m_Pitch, 0xff), ++ m_LineBuf(Fx2DSizeOrDie(8, m_Pitch)), ++ m_RefLineSpan(m_InitialRefLine) { ++ DCHECK_EQ(1, m_Src->GetBPP()); + m_DestBuf.SetAllocStep(10240); + } + +@@ -725,14 +749,14 @@ void FaxEncoder::FaxEncodeRun(int run, bool bWhite) { + AddBitStream(*p, p[1]); + } + +-void FaxEncoder::FaxEncode2DLine(const uint8_t* src_buf) { ++void FaxEncoder::FaxEncode2DLine(pdfium::span src_span) { + int a0 = -1; + bool a0color = true; + while (1) { +- int a1 = FindBit(src_buf, m_Cols, a0 + 1, !a0color); ++ int a1 = FindBit(src_span, m_Cols, a0 + 1, !a0color); + int b1; + int b2; +- FaxG4FindB1B2(m_RefLine, m_Cols, a0, a0color, &b1, &b2); ++ FaxG4FindB1B2(m_RefLineSpan, m_Cols, a0, a0color, &b1, &b2); + if (b2 < a1) { + m_DestBitpos += 3; + m_LineBuf[m_DestBitpos / 8] |= 1 << (7 - m_DestBitpos % 8); +@@ -764,7 +788,7 @@ void FaxEncoder::FaxEncode2DLine(const uint8_t* src_buf) { + a0 = a1; + a0color = !a0color; + } else { +- int a2 = FindBit(src_buf, m_Cols, a1 + 1, a0color); ++ int a2 = FindBit(src_span, m_Cols, a1 + 1, a0color); + ++m_DestBitpos; + ++m_DestBitpos; + m_LineBuf[m_DestBitpos / 8] |= 1 << (7 - m_DestBitpos % 8); +@@ -780,39 +804,34 @@ void FaxEncoder::FaxEncode2DLine(const uint8_t* src_buf) { + } + } + +-void FaxEncoder::Encode(std::unique_ptr* dest_buf, +- uint32_t* dest_size) { ++DataVector FaxEncoder::Encode() { + m_DestBitpos = 0; + uint8_t last_byte = 0; + for (int i = 0; i < m_Rows; ++i) { +- const uint8_t* scan_line = m_pSrcBuf + i * m_Pitch; +- std::fill(std::begin(m_LineBuf), std::end(m_LineBuf), 0); +- m_LineBuf[0] = last_byte; ++ pdfium::span buf_span = pdfium::make_span(m_LineBuf); ++ fxcrt::spanset(buf_span, 0); ++ buf_span[0] = last_byte; ++ pdfium::span scan_line = m_Src->GetScanline(i); + FaxEncode2DLine(scan_line); +- m_DestBuf.AppendBlock(m_LineBuf.data(), m_DestBitpos / 8); ++ m_DestBuf.AppendSpan(buf_span.first(m_DestBitpos / 8)); + last_byte = m_LineBuf[m_DestBitpos / 8]; + m_DestBitpos %= 8; +- memcpy(m_RefLine.data(), scan_line, m_Pitch); ++ m_RefLineSpan = scan_line; + } + if (m_DestBitpos) +- m_DestBuf.AppendByte(last_byte); +- *dest_size = m_DestBuf.GetSize(); +- *dest_buf = m_DestBuf.DetachBuffer(); ++ m_DestBuf.AppendUint8(last_byte); ++ return m_DestBuf.DetachBuffer(); + } + + } // namespace + + // static +-void FaxModule::FaxEncode(const uint8_t* src_buf, +- int width, +- int height, +- int pitch, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size) { +- FaxEncoder encoder(src_buf, width, height, pitch); +- encoder.Encode(dest_buf, dest_size); ++DataVector FaxModule::FaxEncode(RetainPtr src) { ++ DCHECK_EQ(1, src->GetBPP()); ++ FaxEncoder encoder(std::move(src)); ++ return encoder.Encode(); + } + +-#endif // defined(OS_WIN) ++#endif // BUILDFLAG(IS_WIN) + + } // namespace fxcodec +diff --git a/core/fxcodec/fax/faxmodule.h b/core/fxcodec/fax/faxmodule.h +index 0e1d7b274..5f55cc547 100644 +--- a/core/fxcodec/fax/faxmodule.h ++++ b/core/fxcodec/fax/faxmodule.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,13 +7,20 @@ + #ifndef CORE_FXCODEC_FAX_FAXMODULE_H_ + #define CORE_FXCODEC_FAX_FAXMODULE_H_ + ++#include ++ + #include + + #include "build/build_config.h" +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxcrt/fx_system.h" + #include "third_party/base/span.h" + ++#if BUILDFLAG(IS_WIN) ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/retain_ptr.h" ++#endif ++ ++class CFX_DIBBase; ++ + namespace fxcodec { + + class ScanlineDecoder; +@@ -40,14 +47,10 @@ class FaxModule { + int pitch, + uint8_t* dest_buf); + +-#if defined(OS_WIN) +- static void FaxEncode(const uint8_t* src_buf, +- int width, +- int height, +- int pitch, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size); +-#endif // defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) ++ // `src` must have a BPP value of 1. ++ static DataVector FaxEncode(RetainPtr src); ++#endif // BUILDFLAG(IS_WIN) + + FaxModule() = delete; + FaxModule(const FaxModule&) = delete; +diff --git a/core/fxcodec/flate/flatemodule.cpp b/core/fxcodec/flate/flatemodule.cpp +index 9663df34c..8c397c5bf 100644 +--- a/core/fxcodec/flate/flatemodule.cpp ++++ b/core/fxcodec/flate/flatemodule.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,18 +6,26 @@ + + #include "core/fxcodec/flate/flatemodule.h" + ++#include ++#include ++ + #include + #include + #include + #include + #include + +-#include "core/fxcodec/fx_codec.h" + #include "core/fxcodec/scanlinedecoder.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fixed_zeroed_data_vector.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/span_util.h" ++#include "core/fxge/calculate_pitch.h" ++#include "third_party/base/check.h" ++#include "third_party/base/notreached.h" + #include "third_party/base/numerics/safe_conversions.h" +-#include "third_party/base/ptr_util.h" + #include "third_party/base/span.h" + + #if defined(USE_SYSTEM_ZLIB) +@@ -58,7 +66,7 @@ uint32_t FlateGetPossiblyTruncatedTotalIn(z_stream* context) { + bool FlateCompress(unsigned char* dest_buf, + unsigned long* dest_size, + const unsigned char* src_buf, +- uint32_t src_size) { ++ unsigned long src_size) { + return compress(dest_buf, dest_size, src_buf, src_size) == Z_OK; + } + +@@ -84,7 +92,7 @@ uint32_t FlateOutput(z_stream* context, + int ret = inflate(static_cast(context), Z_SYNC_FLUSH); + + uint32_t post_pos = FlateGetPossiblyTruncatedTotalOut(context); +- ASSERT(post_pos >= pre_pos); ++ DCHECK(post_pos >= pre_pos); + + uint32_t written = post_pos - pre_pos; + if (written < dest_size) +@@ -129,22 +137,26 @@ class CLZWDecoder { + uint32_t dest_buf_size_ = 0; // Actual allocated size. + uint32_t dest_byte_pos_ = 0; // Size used. + uint32_t stack_len_ = 0; +- uint8_t decode_stack_[4000]; ++ FixedZeroedDataVector decode_stack_; + const uint8_t early_change_; + uint8_t code_len_ = 9; + uint32_t current_code_ = 0; +- uint32_t codes_[5021]; ++ FixedZeroedDataVector codes_; + }; + + CLZWDecoder::CLZWDecoder(pdfium::span src_span, + bool early_change) +- : src_span_(src_span), early_change_(early_change ? 1 : 0) {} ++ : src_span_(src_span), ++ decode_stack_(4000), ++ early_change_(early_change ? 1 : 0), ++ codes_(5021) {} + + void CLZWDecoder::AddCode(uint32_t prefix_code, uint8_t append_char) { + if (current_code_ + early_change_ == 4094) + return; + +- codes_[current_code_++] = (prefix_code << 16) | append_char; ++ pdfium::span codes_span = codes_.writable_span(); ++ codes_span[current_code_++] = (prefix_code << 16) | append_char; + if (current_code_ + early_change_ == 512 - 258) + code_len_ = 10; + else if (current_code_ + early_change_ == 1024 - 258) +@@ -154,22 +166,24 @@ void CLZWDecoder::AddCode(uint32_t prefix_code, uint8_t append_char) { + } + + void CLZWDecoder::DecodeString(uint32_t code) { +- while (1) { ++ pdfium::span decode_span = decode_stack_.writable_span(); ++ pdfium::span codes_span = codes_.span(); ++ while (true) { + int index = code - 258; + if (index < 0 || static_cast(index) >= current_code_) + break; + +- uint32_t data = codes_[index]; +- if (stack_len_ >= sizeof(decode_stack_)) ++ uint32_t data = codes_span[index]; ++ if (stack_len_ >= decode_span.size()) + return; + +- decode_stack_[stack_len_++] = static_cast(data); ++ decode_span[stack_len_++] = static_cast(data); + code = data >> 16; + } +- if (stack_len_ >= sizeof(decode_stack_)) ++ if (stack_len_ >= decode_span.size()) + return; + +- decode_stack_[stack_len_++] = static_cast(code); ++ decode_span[stack_len_++] = static_cast(code); + } + + void CLZWDecoder::ExpandDestBuf(uint32_t additional_size) { +@@ -185,6 +199,7 @@ void CLZWDecoder::ExpandDestBuf(uint32_t additional_size) { + } + + bool CLZWDecoder::Decode() { ++ pdfium::span decode_span = decode_stack_.writable_span(); + uint32_t old_code = 0xFFFFFFFF; + uint8_t last_char = 0; + +@@ -192,7 +207,7 @@ bool CLZWDecoder::Decode() { + // this size. + dest_buf_size_ = 512; + dest_buf_.reset(FX_Alloc(uint8_t, dest_buf_size_)); +- while (1) { ++ while (true) { + if (src_bit_pos_ + code_len_ > src_span_.size() * 8) + break; + +@@ -242,11 +257,11 @@ bool CLZWDecoder::Decode() { + if (old_code == 0xFFFFFFFF) + return false; + +- ASSERT(old_code < 256 || old_code >= 258); ++ DCHECK(old_code < 256 || old_code >= 258); + stack_len_ = 0; + if (code - 258 >= current_code_) { +- if (stack_len_ < sizeof(decode_stack_)) +- decode_stack_[stack_len_++] = last_char; ++ if (stack_len_ < decode_stack_.size()) ++ decode_span[stack_len_++] = last_char; + DecodeString(old_code); + } else { + DecodeString(code); +@@ -265,9 +280,9 @@ bool CLZWDecoder::Decode() { + } + + for (uint32_t i = 0; i < stack_len_; i++) +- dest_buf_.get()[dest_byte_pos_ + i] = decode_stack_[stack_len_ - i - 1]; ++ dest_buf_.get()[dest_byte_pos_ + i] = decode_span[stack_len_ - i - 1]; + dest_byte_pos_ += stack_len_; +- last_char = decode_stack_[stack_len_ - 1]; ++ last_char = decode_span[stack_len_ - 1]; + if (old_code >= 258 && old_code - 258 >= current_code_) + break; + +@@ -289,13 +304,16 @@ uint8_t PathPredictor(int a, int b, int c) { + return (uint8_t)c; + } + +-void PNG_PredictLine(uint8_t* pDestData, +- const uint8_t* pSrcData, +- const uint8_t* pLastLine, ++void PNG_PredictLine(pdfium::span dest_span, ++ pdfium::span src_span, ++ pdfium::span last_span, + int bpc, + int nColors, + int nPixels) { +- const uint32_t row_size = CalculatePitch8(bpc, nColors, nPixels).ValueOrDie(); ++ uint8_t* pDestData = dest_span.data(); ++ const uint8_t* pSrcData = src_span.data(); ++ const uint8_t* pLastLine = last_span.data(); ++ const uint32_t row_size = fxge::CalculatePitch8OrDie(bpc, nColors, nPixels); + const uint32_t BytesPerPixel = (bpc * nColors + 7) / 8; + uint8_t tag = pSrcData[0]; + if (tag == 0) { +@@ -526,7 +544,9 @@ void FlateUncompress(pdfium::span src_buf, + FlateInput(context.get(), src_buf); + + const uint32_t kMaxInitialAllocSize = 10000000; +- uint32_t guess_size = orig_size ? orig_size : src_buf.size() * 2; ++ uint32_t guess_size = ++ orig_size ? orig_size ++ : pdfium::base::checked_cast(src_buf.size() * 2); + guess_size = std::min(guess_size, kMaxInitialAllocSize); + + uint32_t buf_size = guess_size; +@@ -538,7 +558,7 @@ void FlateUncompress(pdfium::span src_buf, + std::vector> result_tmp_bufs; + { + std::unique_ptr cur_buf = std::move(guess_buf); +- while (1) { ++ while (true) { + uint32_t ret = FlateOutput(context.get(), cur_buf.get(), buf_size); + uint32_t avail_buf_size = FlateGetAvailOut(context.get()); + if (ret != Z_OK || avail_buf_size != 0) { +@@ -600,14 +620,14 @@ class FlateScanlineDecoder : public ScanlineDecoder { + ~FlateScanlineDecoder() override; + + // ScanlineDecoder: +- bool v_Rewind() override; +- uint8_t* v_GetNextLine() override; ++ bool Rewind() override; ++ pdfium::span GetNextLine() override; + uint32_t GetSrcOffset() override; + + protected: + std::unique_ptr m_pFlate; + const pdfium::span m_SrcBuf; +- std::unique_ptr const m_pScanline; ++ DataVector m_Scanline; + }; + + FlateScanlineDecoder::FlateScanlineDecoder(pdfium::span src_span, +@@ -621,13 +641,16 @@ FlateScanlineDecoder::FlateScanlineDecoder(pdfium::span src_span, + height, + nComps, + bpc, +- CalculatePitch8(bpc, nComps, width).ValueOrDie()), ++ fxge::CalculatePitch8OrDie(bpc, nComps, width)), + m_SrcBuf(src_span), +- m_pScanline(FX_Alloc(uint8_t, m_Pitch)) {} ++ m_Scanline(m_Pitch) {} + +-FlateScanlineDecoder::~FlateScanlineDecoder() = default; ++FlateScanlineDecoder::~FlateScanlineDecoder() { ++ // Span in superclass can't outlive our buffer. ++ m_pLastScanline = pdfium::span(); ++} + +-bool FlateScanlineDecoder::v_Rewind() { ++bool FlateScanlineDecoder::Rewind() { + m_pFlate.reset(FlateInit()); + if (!m_pFlate) + return false; +@@ -636,9 +659,9 @@ bool FlateScanlineDecoder::v_Rewind() { + return true; + } + +-uint8_t* FlateScanlineDecoder::v_GetNextLine() { +- FlateOutput(m_pFlate.get(), m_pScanline.get(), m_Pitch); +- return m_pScanline.get(); ++pdfium::span FlateScanlineDecoder::GetNextLine() { ++ FlateOutput(m_pFlate.get(), m_Scanline.data(), m_Pitch); ++ return m_Scanline; + } + + uint32_t FlateScanlineDecoder::GetSrcOffset() { +@@ -659,8 +682,8 @@ class FlatePredictorScanlineDecoder final : public FlateScanlineDecoder { + ~FlatePredictorScanlineDecoder() override; + + // ScanlineDecoder: +- bool v_Rewind() override; +- uint8_t* v_GetNextLine() override; ++ bool Rewind() override; ++ pdfium::span GetNextLine() override; + + private: + void GetNextLineWithPredictedPitch(); +@@ -672,9 +695,9 @@ class FlatePredictorScanlineDecoder final : public FlateScanlineDecoder { + int m_Columns = 0; + uint32_t m_PredictPitch = 0; + size_t m_LeftOver = 0; +- std::vector> m_LastLine; +- std::vector> m_PredictBuffer; +- std::vector> m_PredictRaw; ++ DataVector m_LastLine; ++ DataVector m_PredictBuffer; ++ DataVector m_PredictRaw; + }; + + FlatePredictorScanlineDecoder::FlatePredictorScanlineDecoder( +@@ -689,7 +712,7 @@ FlatePredictorScanlineDecoder::FlatePredictorScanlineDecoder( + int Columns) + : FlateScanlineDecoder(src_span, width, height, comps, bpc), + m_Predictor(predictor) { +- ASSERT(m_Predictor != PredictorType::kNone); ++ DCHECK(m_Predictor != PredictorType::kNone); + if (BitsPerComponent * Colors * Columns == 0) { + BitsPerComponent = m_bpc; + Colors = m_nComps; +@@ -699,46 +722,48 @@ FlatePredictorScanlineDecoder::FlatePredictorScanlineDecoder( + m_BitsPerComponent = BitsPerComponent; + m_Columns = Columns; + m_PredictPitch = +- CalculatePitch8(m_BitsPerComponent, m_Colors, m_Columns).ValueOrDie(); ++ fxge::CalculatePitch8OrDie(m_BitsPerComponent, m_Colors, m_Columns); + m_LastLine.resize(m_PredictPitch); + m_PredictBuffer.resize(m_PredictPitch); + m_PredictRaw.resize(m_PredictPitch + 1); + } + +-FlatePredictorScanlineDecoder::~FlatePredictorScanlineDecoder() = default; ++FlatePredictorScanlineDecoder::~FlatePredictorScanlineDecoder() { ++ // Span in superclass can't outlive our buffer. ++ m_pLastScanline = pdfium::span(); ++} + +-bool FlatePredictorScanlineDecoder::v_Rewind() { +- if (!FlateScanlineDecoder::v_Rewind()) ++bool FlatePredictorScanlineDecoder::Rewind() { ++ if (!FlateScanlineDecoder::Rewind()) + return false; + + m_LeftOver = 0; + return true; + } + +-uint8_t* FlatePredictorScanlineDecoder::v_GetNextLine() { ++pdfium::span FlatePredictorScanlineDecoder::GetNextLine() { + if (m_Pitch == m_PredictPitch) + GetNextLineWithPredictedPitch(); + else + GetNextLineWithoutPredictedPitch(); +- return m_pScanline.get(); ++ return m_Scanline; + } + + void FlatePredictorScanlineDecoder::GetNextLineWithPredictedPitch() { + switch (m_Predictor) { + case PredictorType::kPng: + FlateOutput(m_pFlate.get(), m_PredictRaw.data(), m_PredictPitch + 1); +- PNG_PredictLine(m_pScanline.get(), m_PredictRaw.data(), m_LastLine.data(), +- m_BitsPerComponent, m_Colors, m_Columns); +- memcpy(m_LastLine.data(), m_pScanline.get(), m_PredictPitch); ++ PNG_PredictLine(m_Scanline, m_PredictRaw, m_LastLine, m_BitsPerComponent, ++ m_Colors, m_Columns); ++ memcpy(m_LastLine.data(), m_Scanline.data(), m_PredictPitch); + break; + case PredictorType::kFlate: +- FlateOutput(m_pFlate.get(), m_pScanline.get(), m_Pitch); +- TIFF_PredictLine(m_pScanline.get(), m_PredictPitch, m_bpc, m_nComps, ++ FlateOutput(m_pFlate.get(), m_Scanline.data(), m_Pitch); ++ TIFF_PredictLine(m_Scanline.data(), m_PredictPitch, m_bpc, m_nComps, + m_OutputWidth); + break; +- default: +- NOTREACHED(); +- break; ++ case PredictorType::kNone: ++ NOTREACHED_NORETURN(); + } + } + +@@ -746,7 +771,7 @@ void FlatePredictorScanlineDecoder::GetNextLineWithoutPredictedPitch() { + size_t bytes_to_go = m_Pitch; + size_t read_leftover = m_LeftOver > bytes_to_go ? bytes_to_go : m_LeftOver; + if (read_leftover) { +- memcpy(m_pScanline.get(), &m_PredictBuffer[m_PredictPitch - m_LeftOver], ++ memcpy(m_Scanline.data(), &m_PredictBuffer[m_PredictPitch - m_LeftOver], + read_leftover); + m_LeftOver -= read_leftover; + bytes_to_go -= read_leftover; +@@ -755,9 +780,8 @@ void FlatePredictorScanlineDecoder::GetNextLineWithoutPredictedPitch() { + switch (m_Predictor) { + case PredictorType::kPng: + FlateOutput(m_pFlate.get(), m_PredictRaw.data(), m_PredictPitch + 1); +- PNG_PredictLine(m_PredictBuffer.data(), m_PredictRaw.data(), +- m_LastLine.data(), m_BitsPerComponent, m_Colors, +- m_Columns); ++ PNG_PredictLine(m_PredictBuffer, m_PredictRaw, m_LastLine, ++ m_BitsPerComponent, m_Colors, m_Columns); + memcpy(m_LastLine.data(), m_PredictBuffer.data(), m_PredictPitch); + break; + case PredictorType::kFlate: +@@ -765,14 +789,13 @@ void FlatePredictorScanlineDecoder::GetNextLineWithoutPredictedPitch() { + TIFF_PredictLine(m_PredictBuffer.data(), m_PredictPitch, + m_BitsPerComponent, m_Colors, m_Columns); + break; +- default: +- NOTREACHED(); +- break; ++ case PredictorType::kNone: ++ NOTREACHED_NORETURN(); + } + size_t read_bytes = + m_PredictPitch > bytes_to_go ? bytes_to_go : m_PredictPitch; +- memcpy(m_pScanline.get() + m_Pitch - bytes_to_go, m_PredictBuffer.data(), +- read_bytes); ++ fxcrt::spancpy(pdfium::make_span(m_Scanline).subspan(m_Pitch - bytes_to_go), ++ pdfium::make_span(m_PredictBuffer).first(read_bytes)); + m_LeftOver += m_PredictPitch - read_bytes; + bytes_to_go -= read_bytes; + } +@@ -793,10 +816,10 @@ std::unique_ptr FlateModule::CreateDecoder( + int Columns) { + PredictorType predictor_type = GetPredictor(predictor); + if (predictor_type == PredictorType::kNone) { +- return pdfium::MakeUnique(src_span, width, height, +- nComps, bpc); ++ return std::make_unique(src_span, width, height, ++ nComps, bpc); + } +- return pdfium::MakeUnique( ++ return std::make_unique( + src_span, width, height, nComps, bpc, predictor_type, Colors, + BitsPerComponent, Columns); + } +@@ -818,7 +841,7 @@ uint32_t FlateModule::FlateOrLZWDecode( + PredictorType predictor_type = GetPredictor(predictor); + + if (bLZW) { +- auto decoder = pdfium::MakeUnique(src_span, bEarlyChange); ++ auto decoder = std::make_unique(src_span, bEarlyChange); + if (!decoder->Decode()) + return FX_INVALID_OFFSET; + +@@ -841,26 +864,24 @@ uint32_t FlateModule::FlateOrLZWDecode( + ret = TIFF_Predictor(Colors, BitsPerComponent, Columns, dest_buf, + dest_size); + break; +- default: +- NOTREACHED(); +- break; + } + return ret ? offset : FX_INVALID_OFFSET; + } + + // static +-bool FlateModule::Encode(const uint8_t* src_buf, +- uint32_t src_size, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size) { +- *dest_size = src_size + src_size / 1000 + 12; +- dest_buf->reset(FX_Alloc(uint8_t, *dest_size)); +- unsigned long temp_size = *dest_size; +- if (!FlateCompress(dest_buf->get(), &temp_size, src_buf, src_size)) +- return false; +- +- *dest_size = (uint32_t)temp_size; +- return true; ++DataVector FlateModule::Encode(pdfium::span src_span) { ++ const unsigned long src_size = ++ pdfium::base::checked_cast(src_span.size()); ++ pdfium::base::CheckedNumeric safe_dest_size = src_size; ++ safe_dest_size += src_size / 1000; ++ safe_dest_size += 12; ++ unsigned long dest_size = safe_dest_size.ValueOrDie(); ++ DataVector dest_buf(dest_size); ++ if (!FlateCompress(dest_buf.data(), &dest_size, src_span.data(), src_size)) ++ return {}; ++ ++ dest_buf.resize(pdfium::base::checked_cast(dest_size)); ++ return dest_buf; + } + + } // namespace fxcodec +diff --git a/core/fxcodec/flate/flatemodule.h b/core/fxcodec/flate/flatemodule.h +index 8ce5aa58f..f82c54264 100644 +--- a/core/fxcodec/flate/flatemodule.h ++++ b/core/fxcodec/flate/flatemodule.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,12 @@ + #ifndef CORE_FXCODEC_FLATE_FLATEMODULE_H_ + #define CORE_FXCODEC_FLATE_FLATEMODULE_H_ + ++#include ++ + #include + ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxcrt/fx_system.h" + #include "third_party/base/span.h" + + namespace fxcodec { +@@ -42,10 +44,7 @@ class FlateModule { + std::unique_ptr* dest_buf, + uint32_t* dest_size); + +- static bool Encode(const uint8_t* src_buf, +- uint32_t src_size, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size); ++ static DataVector Encode(pdfium::span src_span); + + FlateModule() = delete; + FlateModule(const FlateModule&) = delete; +diff --git a/core/fxcodec/fx_codec.cpp b/core/fxcodec/fx_codec.cpp +index 5625534ad..7181d5715 100644 +--- a/core/fxcodec/fx_codec.cpp ++++ b/core/fxcodec/fx_codec.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,110 +6,31 @@ + + #include "core/fxcodec/fx_codec.h" + +-#include +-#include +-#include + #include + +-#include "core/fxcodec/jbig2/jbig2module.h" +-#include "core/fxcodec/jpeg/jpegmodule.h" +-#include "core/fxcrt/fx_extension.h" +-#include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/ptr_util.h" ++#include "core/fxge/dib/fx_dib.h" + + namespace fxcodec { + +-namespace { +- +-ModuleMgr* g_ModuleMgr = nullptr; +- +-} // namespace +- +-// static +-void ModuleMgr::Create() { +- ASSERT(!g_ModuleMgr); +- g_ModuleMgr = new ModuleMgr(); +-} +- +-// static +-void ModuleMgr::Destroy() { +- ASSERT(g_ModuleMgr); +- delete g_ModuleMgr; +- g_ModuleMgr = nullptr; +-} +- +-// static +-ModuleMgr* ModuleMgr::GetInstance() { +- ASSERT(g_ModuleMgr); +- return g_ModuleMgr; +-} +- +-ModuleMgr::ModuleMgr() +- : m_pJpegModule(pdfium::MakeUnique()), +- m_pJbig2Module(pdfium::MakeUnique()) { +-#ifdef PDF_ENABLE_XFA_BMP +- SetBmpModule(pdfium::MakeUnique()); +-#endif +- +-#ifdef PDF_ENABLE_XFA_GIF +- SetGifModule(pdfium::MakeUnique()); +-#endif +- +-#ifdef PDF_ENABLE_XFA_PNG +- SetPngModule(pdfium::MakeUnique()); +-#endif +- +-#ifdef PDF_ENABLE_XFA_TIFF +- SetTiffModule(pdfium::MakeUnique()); +-#endif +-} +- +-ModuleMgr::~ModuleMgr() = default; +- + #ifdef PDF_ENABLE_XFA + CFX_DIBAttribute::CFX_DIBAttribute() = default; + +-CFX_DIBAttribute::~CFX_DIBAttribute() { +- for (const auto& pair : m_Exif) +- FX_Free(pair.second); +-} ++CFX_DIBAttribute::~CFX_DIBAttribute() = default; + #endif // PDF_ENABLE_XFA + + void ReverseRGB(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels) { + if (pDestBuf == pSrcBuf) { + for (int i = 0; i < pixels; i++) { +- uint8_t temp = pDestBuf[2]; +- pDestBuf[2] = pDestBuf[0]; +- pDestBuf[0] = temp; ++ std::swap(pDestBuf[0], pDestBuf[2]); + pDestBuf += 3; + } + } else { + for (int i = 0; i < pixels; i++) { +- *pDestBuf++ = pSrcBuf[2]; +- *pDestBuf++ = pSrcBuf[1]; +- *pDestBuf++ = pSrcBuf[0]; ++ ReverseCopy3Bytes(pDestBuf, pSrcBuf); ++ pDestBuf += 3; + pSrcBuf += 3; + } + } + } + +-FX_SAFE_UINT32 CalculatePitch8(uint32_t bpc, uint32_t components, int width) { +- FX_SAFE_UINT32 pitch = bpc; +- pitch *= components; +- pitch *= width; +- pitch += 7; +- pitch /= 8; +- return pitch; +-} +- +-FX_SAFE_UINT32 CalculatePitch32(int bpp, int width) { +- FX_SAFE_UINT32 pitch = bpp; +- pitch *= width; +- pitch += 31; +- pitch /= 32; // quantized to number of 32-bit words. +- pitch *= 4; // and then back to bytes, (not just /8 in one step). +- return pitch; +-} +- + } // namespace fxcodec +diff --git a/core/fxcodec/fx_codec.h b/core/fxcodec/fx_codec.h +index c7224ff3c..b102b43bd 100644 +--- a/core/fxcodec/fx_codec.h ++++ b/core/fxcodec/fx_codec.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,125 +7,33 @@ + #ifndef CORE_FXCODEC_FX_CODEC_H_ + #define CORE_FXCODEC_FX_CODEC_H_ + +-#include +-#include +-#include +- +-#include "core/fxcodec/fx_codec_def.h" +-#include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_safe_types.h" +-#include "core/fxcrt/fx_string.h" +- +-#ifdef PDF_ENABLE_XFA +-#ifdef PDF_ENABLE_XFA_BMP +-#include "core/fxcodec/bmp/bmpmodule.h" +-#endif // PDF_ENABLE_XFA_BMP +- +-#ifdef PDF_ENABLE_XFA_GIF +-#include "core/fxcodec/gif/gifmodule.h" +-#endif // PDF_ENABLE_XFA_GIF +- +-#ifdef PDF_ENABLE_XFA_PNG +-#include "core/fxcodec/png/pngmodule.h" +-#endif // PDF_ENABLE_XFA_PNG +- +-#ifdef PDF_ENABLE_XFA_TIFF +-#include "core/fxcodec/tiff/tiffmodule.h" +-#endif // PDF_ENABLE_XFA_TIFF +-#endif // PDF_ENABLE_XFA ++#include + + namespace fxcodec { + + #ifdef PDF_ENABLE_XFA + class CFX_DIBAttribute { + public: ++ // Not an enum class yet because we still blindly cast integer results ++ // from third-party libraries to this type. ++ enum ResUnit : uint16_t { ++ kResUnitNone = 0, ++ kResUnitInch, ++ kResUnitCentimeter, ++ kResUnitMeter ++ }; ++ + CFX_DIBAttribute(); + ~CFX_DIBAttribute(); + + int32_t m_nXDPI = -1; + int32_t m_nYDPI = -1; +- uint16_t m_wDPIUnit = 0; +- std::map m_Exif; ++ ResUnit m_wDPIUnit = kResUnitNone; + }; + #endif // PDF_ENABLE_XFA + +-class Jbig2Module; +-class JpegModule; +-class ProgressiveDecoder; +- +-class ModuleMgr { +- public: +- // Per-process singleton managed by callers. +- static void Create(); +- static void Destroy(); +- static ModuleMgr* GetInstance(); +- +- JpegModule* GetJpegModule() const { return m_pJpegModule.get(); } +- Jbig2Module* GetJbig2Module() const { return m_pJbig2Module.get(); } +- +-#ifdef PDF_ENABLE_XFA +- std::unique_ptr CreateProgressiveDecoder(); +- +-#ifdef PDF_ENABLE_XFA_BMP +- BmpModule* GetBmpModule() const { return m_pBmpModule.get(); } +- void SetBmpModule(std::unique_ptr module) { +- m_pBmpModule = std::move(module); +- } +-#endif // PDF_ENABLE_XFA_BMP +- +-#ifdef PDF_ENABLE_XFA_GIF +- GifModule* GetGifModule() const { return m_pGifModule.get(); } +- void SetGifModule(std::unique_ptr module) { +- m_pGifModule = std::move(module); +- } +-#endif // PDF_ENABLE_XFA_GIF +- +-#ifdef PDF_ENABLE_XFA_PNG +- PngModule* GetPngModule() const { return m_pPngModule.get(); } +- void SetPngModule(std::unique_ptr module) { +- m_pPngModule = std::move(module); +- } +-#endif // PDF_ENABLE_XFA_PNG +- +-#ifdef PDF_ENABLE_XFA_TIFF +- TiffModule* GetTiffModule() const { return m_pTiffModule.get(); } +- void SetTiffModule(std::unique_ptr module) { +- m_pTiffModule = std::move(module); +- } +-#endif // PDF_ENABLE_XFA_TIFF +-#endif // PDF_ENABLE_XFA +- +- private: +- ModuleMgr(); +- ~ModuleMgr(); +- +- std::unique_ptr m_pJpegModule; +- std::unique_ptr m_pJbig2Module; +- +-#ifdef PDF_ENABLE_XFA +-#ifdef PDF_ENABLE_XFA_BMP +- std::unique_ptr m_pBmpModule; +-#endif // PDF_ENABLE_XFA_BMP +- +-#ifdef PDF_ENABLE_XFA_GIF +- std::unique_ptr m_pGifModule; +-#endif // PDF_ENABLE_XFA_GIF +- +-#ifdef PDF_ENABLE_XFA_PNG +- std::unique_ptr m_pPngModule; +-#endif // PDF_ENABLE_XFA_PNG +- +-#ifdef PDF_ENABLE_XFA_TIFF +- std::unique_ptr m_pTiffModule; +-#endif // PDF_ENABLE_XFA_TIFF +-#endif // PDF_ENABLE_XFA +-}; +- + void ReverseRGB(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels); + +-FX_SAFE_UINT32 CalculatePitch8(uint32_t bpc, uint32_t components, int width); +-FX_SAFE_UINT32 CalculatePitch32(int bpp, int width); +- + } // namespace fxcodec + + #ifdef PDF_ENABLE_XFA +diff --git a/core/fxcodec/fx_codec_def.h b/core/fxcodec/fx_codec_def.h +index 2d6b5ed93..257007a4f 100644 +--- a/core/fxcodec/fx_codec_def.h ++++ b/core/fxcodec/fx_codec_def.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,20 +7,13 @@ + #ifndef CORE_FXCODEC_FX_CODEC_DEF_H_ + #define CORE_FXCODEC_FX_CODEC_DEF_H_ + +-enum FXCODEC_STATUS { +- FXCODEC_STATUS_ERROR = -1, +- FXCODEC_STATUS_FRAME_READY, +- FXCODEC_STATUS_FRAME_TOBECONTINUE, +- FXCODEC_STATUS_DECODE_READY, +- FXCODEC_STATUS_DECODE_TOBECONTINUE, +- FXCODEC_STATUS_DECODE_FINISH, +-#ifdef PDF_ENABLE_XFA +- FXCODEC_STATUS_ERR_MEMORY, +-#endif // PDF_ENABLE_XFA +- FXCODEC_STATUS_ERR_READ, +- FXCODEC_STATUS_ERR_FLUSH, +- FXCODEC_STATUS_ERR_FORMAT, +- FXCODEC_STATUS_ERR_PARAMS ++enum class FXCODEC_STATUS { ++ kError = -1, ++ kFrameReady, ++ kFrameToBeContinued, ++ kDecodeReady, ++ kDecodeToBeContinued, ++ kDecodeFinished, + }; + + #ifdef PDF_ENABLE_XFA +@@ -41,13 +34,6 @@ enum FXCODEC_IMAGE_TYPE { + #endif // PDF_ENABLE_XFA_TIFF + FXCODEC_IMAGE_MAX + }; +- +-enum FXCODEC_RESUNIT { +- FXCODEC_RESUNIT_NONE = 0, +- FXCODEC_RESUNIT_INCH, +- FXCODEC_RESUNIT_CENTIMETER, +- FXCODEC_RESUNIT_METER +-}; + #endif // PDF_ENABLE_XFA + + #endif // CORE_FXCODEC_FX_CODEC_DEF_H_ +diff --git a/core/fxcodec/gif/cfx_gif.cpp b/core/fxcodec/gif/cfx_gif.cpp +index efe4f4b03..04c673f46 100644 +--- a/core/fxcodec/gif/cfx_gif.cpp ++++ b/core/fxcodec/gif/cfx_gif.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -17,8 +17,8 @@ static_assert(sizeof(CFX_GifHeader) == 6, + "CFX_GifHeader should have a size of 6"); + static_assert(sizeof(CFX_GifLocalScreenDescriptor) == 7, + "CFX_GifLocalScreenDescriptor should have a size of 7"); +-static_assert(sizeof(CFX_CFX_GifImageInfo) == 9, +- "CFX_CFX_GifImageInfo should have a size of 9"); ++static_assert(sizeof(CFX_GifImageInfo) == 9, ++ "CFX_GifImageInfo should have a size of 9"); + static_assert(sizeof(CFX_GifControlExtensionFlags) == 1, + "CFX_GifControlExtensionFlags should have a size of 1"); + static_assert(sizeof(CFX_GifPlainTextExtension) == 13, +diff --git a/core/fxcodec/gif/cfx_gif.h b/core/fxcodec/gif/cfx_gif.h +index c0c9d0000..8556612e3 100644 +--- a/core/fxcodec/gif/cfx_gif.h ++++ b/core/fxcodec/gif/cfx_gif.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,12 @@ + #ifndef CORE_FXCODEC_GIF_CFX_GIF_H_ + #define CORE_FXCODEC_GIF_CFX_GIF_H_ + ++#include ++ + #include + #include + +-#include "core/fxcrt/fx_memory_wrappers.h" +- +-class CFX_GifContext; ++#include "core/fxcrt/data_vector.h" + + extern const char kGifSignature87[]; + extern const char kGifSignature89[]; +@@ -64,7 +64,7 @@ struct CFX_GifLocalScreenDescriptor { + uint8_t pixel_aspect; + }; + +-struct CFX_CFX_GifImageInfo { ++struct CFX_GifImageInfo { + uint16_t left; + uint16_t top; + uint16_t width; +@@ -111,22 +111,15 @@ struct CFX_GifPalette { + }; + #pragma pack() + +-enum class CFX_GifDecodeStatus { +- Error, +- Success, +- Unfinished, +- InsufficientDestSize, // Only used internally by CGifLZWDecoder::Decode() +-}; +- + struct CFX_GifImage { + CFX_GifImage(); + ~CFX_GifImage(); + + std::unique_ptr image_GCE; + std::vector local_palettes; +- std::vector> row_buffer; +- CFX_CFX_GifImageInfo image_info; +- uint8_t local_pallette_exp; ++ DataVector row_buffer; ++ CFX_GifImageInfo image_info; ++ uint8_t local_palette_exp; + uint8_t code_exp; + uint32_t data_pos; + int32_t row_num; +diff --git a/core/fxcodec/gif/cfx_gifcontext.cpp b/core/fxcodec/gif/cfx_gifcontext.cpp +index 267098aad..e7fc432f2 100644 +--- a/core/fxcodec/gif/cfx_gifcontext.cpp ++++ b/core/fxcodec/gif/cfx_gifcontext.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,14 +6,16 @@ + + #include "core/fxcodec/gif/cfx_gifcontext.h" + ++#include ++ + #include ++#include + #include + + #include "core/fxcodec/cfx_codec_memory.h" +-#include "core/fxcodec/gif/cfx_gif.h" +-#include "core/fxcodec/gif/gifmodule.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/stl_util.h" + + namespace fxcodec { + +@@ -23,17 +25,13 @@ constexpr int32_t kGifInterlaceStep[4] = {8, 8, 4, 2}; + + } // namespace + +-CFX_GifContext::CFX_GifContext(GifModule* gif_module, +- GifModule::Delegate* delegate) +- : gif_module_(gif_module), delegate_(delegate) {} ++CFX_GifContext::CFX_GifContext(GifDecoder::Delegate* delegate) ++ : delegate_(delegate) {} + + CFX_GifContext::~CFX_GifContext() = default; + +-void CFX_GifContext::RecordCurrentPosition(uint32_t* cur_pos) { +- delegate_->GifRecordCurrentPosition(*cur_pos); +-} +- +-void CFX_GifContext::ReadScanline(int32_t row_num, uint8_t* row_buf) { ++void CFX_GifContext::ReadScanline(int32_t row_num, ++ pdfium::span row_buf) { + delegate_->GifReadScanline(row_num, row_buf); + } + +@@ -44,33 +42,30 @@ bool CFX_GifContext::GetRecordPosition(uint32_t cur_pos, + int32_t height, + int32_t pal_num, + CFX_GifPalette* pal, +- int32_t delay_time, +- bool user_input, + int32_t trans_index, +- int32_t disposal_method, + bool interlace) { + return delegate_->GifInputRecordPositionBuf( + cur_pos, FX_RECT(left, top, left + width, top + height), pal_num, pal, +- delay_time, user_input, trans_index, disposal_method, interlace); ++ trans_index, interlace); + } + +-CFX_GifDecodeStatus CFX_GifContext::ReadHeader() { +- CFX_GifDecodeStatus status = ReadGifSignature(); +- if (status != CFX_GifDecodeStatus::Success) ++GifDecoder::Status CFX_GifContext::ReadHeader() { ++ GifDecoder::Status status = ReadGifSignature(); ++ if (status != GifDecoder::Status::kSuccess) + return status; + return ReadLogicalScreenDescriptor(); + } + +-CFX_GifDecodeStatus CFX_GifContext::GetFrame() { +- CFX_GifDecodeStatus ret = CFX_GifDecodeStatus::Success; ++GifDecoder::Status CFX_GifContext::GetFrame() { ++ GifDecoder::Status ret = GifDecoder::Status::kSuccess; + while (true) { + switch (decode_status_) { + case GIF_D_STATUS_TAIL: +- return CFX_GifDecodeStatus::Success; ++ return GifDecoder::Status::kSuccess; + case GIF_D_STATUS_SIG: { + uint8_t signature; + if (!ReadAllOrNone(&signature, sizeof(signature))) +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + + switch (signature) { + case GIF_SIG_EXTENSION: +@@ -81,7 +76,7 @@ CFX_GifDecodeStatus CFX_GifContext::GetFrame() { + continue; + case GIF_SIG_TRAILER: + SaveDecodingStatus(GIF_D_STATUS_TAIL); +- return CFX_GifDecodeStatus::Success; ++ return GifDecoder::Status::kSuccess; + default: + if (!input_buffer_->IsEOF()) { + // The Gif File has non_standard Tag! +@@ -89,13 +84,13 @@ CFX_GifDecodeStatus CFX_GifContext::GetFrame() { + continue; + } + // The Gif File Doesn't have Trailer Tag! +- return CFX_GifDecodeStatus::Success; ++ return GifDecoder::Status::kSuccess; + } + } + case GIF_D_STATUS_EXT: { + uint8_t extension; + if (!ReadAllOrNone(&extension, sizeof(extension))) +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + + switch (extension) { + case GIF_BLOCK_CE: +@@ -119,7 +114,7 @@ CFX_GifDecodeStatus CFX_GifContext::GetFrame() { + } + case GIF_D_STATUS_IMG_INFO: { + ret = DecodeImageInfo(); +- if (ret != CFX_GifDecodeStatus::Success) ++ if (ret != GifDecoder::Status::kSuccess) + return ret; + + continue; +@@ -129,13 +124,13 @@ CFX_GifDecodeStatus CFX_GifContext::GetFrame() { + size_t read_marker = input_buffer_->GetPosition(); + + if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size))) +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + + while (img_data_size != GIF_BLOCK_TERMINAL) { + if (!input_buffer_->Seek(input_buffer_->GetPosition() + + img_data_size)) { + input_buffer_->Seek(read_marker); +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + } + + // This saving of the scan state on partial reads is why +@@ -143,7 +138,7 @@ CFX_GifDecodeStatus CFX_GifContext::GetFrame() { + SaveDecodingStatus(GIF_D_STATUS_IMG_DATA); + read_marker = input_buffer_->GetPosition(); + if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size))) +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + } + + SaveDecodingStatus(GIF_D_STATUS_SIG); +@@ -151,26 +146,25 @@ CFX_GifDecodeStatus CFX_GifContext::GetFrame() { + } + default: { + ret = DecodeExtension(); +- if (ret != CFX_GifDecodeStatus::Success) ++ if (ret != GifDecoder::Status::kSuccess) + return ret; + break; + } + } + } +- return CFX_GifDecodeStatus::Success; + } + +-CFX_GifDecodeStatus CFX_GifContext::LoadFrame(int32_t frame_num) { +- if (!pdfium::IndexInBounds(images_, frame_num)) +- return CFX_GifDecodeStatus::Error; ++GifDecoder::Status CFX_GifContext::LoadFrame(size_t frame_num) { ++ if (frame_num >= images_.size()) ++ return GifDecoder::Status::kError; + +- CFX_GifImage* gif_image = images_[static_cast(frame_num)].get(); ++ CFX_GifImage* gif_image = images_[frame_num].get(); + if (gif_image->image_info.height == 0) +- return CFX_GifDecodeStatus::Error; ++ return GifDecoder::Status::kError; + + uint32_t gif_img_row_bytes = gif_image->image_info.width; + if (gif_img_row_bytes == 0) +- return CFX_GifDecodeStatus::Error; ++ return GifDecoder::Status::kError; + + if (decode_status_ == GIF_D_STATUS_TAIL) { + gif_image->row_buffer.resize(gif_img_row_bytes); +@@ -186,33 +180,30 @@ CFX_GifDecodeStatus CFX_GifContext::LoadFrame(int32_t frame_num) { + bool bRes = GetRecordPosition( + gif_image->data_pos, gif_image->image_info.left, + gif_image->image_info.top, gif_image->image_info.width, +- gif_image->image_info.height, loc_pal_num, pLocalPalette, 0, 0, -1, 0, ++ gif_image->image_info.height, loc_pal_num, pLocalPalette, -1, + gif_image->image_info.local_flags.interlace); + if (!bRes) { + gif_image->row_buffer.clear(); +- return CFX_GifDecodeStatus::Error; ++ return GifDecoder::Status::kError; + } + } else { + bool bRes = GetRecordPosition( + gif_image->data_pos, gif_image->image_info.left, + gif_image->image_info.top, gif_image->image_info.width, + gif_image->image_info.height, loc_pal_num, pLocalPalette, +- static_cast(gif_image->image_GCE->delay_time), +- gif_image->image_GCE->gce_flags.user_input, + gif_image->image_GCE->gce_flags.transparency + ? static_cast(gif_image->image_GCE->trans_index) + : -1, +- static_cast(gif_image->image_GCE->gce_flags.disposal_method), + gif_image->image_info.local_flags.interlace); + if (!bRes) { + gif_image->row_buffer.clear(); +- return CFX_GifDecodeStatus::Error; ++ return GifDecoder::Status::kError; + } + } + + if (gif_image->code_exp > GIF_MAX_LZW_EXP) { + gif_image->row_buffer.clear(); +- return CFX_GifDecodeStatus::Error; ++ return GifDecoder::Status::kError; + } + + img_row_offset_ = 0; +@@ -223,114 +214,106 @@ CFX_GifDecodeStatus CFX_GifContext::LoadFrame(int32_t frame_num) { + } + + uint8_t img_data_size; +- std::vector> img_data; ++ DataVector img_data; + size_t read_marker = input_buffer_->GetPosition(); + ++ // TODO(crbug.com/pdfium/1793): This logic can be simplified a lot, but it ++ // probably makes more sense to switch to a different GIF decoder altogether. + if (decode_status_ == GIF_D_STATUS_IMG_DATA) { + if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size))) +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + + if (img_data_size != GIF_BLOCK_TERMINAL) { + img_data.resize(img_data_size); + if (!ReadAllOrNone(img_data.data(), img_data_size)) { + input_buffer_->Seek(read_marker); +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; ++ } ++ ++ if (!lzw_decompressor_) { ++ lzw_decompressor_ = LZWDecompressor::Create(GetPaletteExp(gif_image), ++ gif_image->code_exp); ++ if (!lzw_decompressor_) { ++ DecodingFailureAtTailCleanup(gif_image); ++ return GifDecoder::Status::kError; ++ } + } ++ lzw_decompressor_->SetSource(img_data.data(), img_data_size); + +- if (!lzw_decompressor_.get()) +- lzw_decompressor_ = CFX_LZWDecompressor::Create( +- !gif_image->local_palettes.empty() ? gif_image->local_pallette_exp +- : global_pal_exp_, +- gif_image->code_exp); + SaveDecodingStatus(GIF_D_STATUS_IMG_DATA); + img_row_offset_ += img_row_avail_size_; + img_row_avail_size_ = gif_img_row_bytes - img_row_offset_; +- CFX_GifDecodeStatus ret = +- lzw_decompressor_.get() +- ? lzw_decompressor_->Decode( +- img_data.data(), img_data_size, +- gif_image->row_buffer.data() + img_row_offset_, +- &img_row_avail_size_) +- : CFX_GifDecodeStatus::Error; +- if (ret == CFX_GifDecodeStatus::Error) { ++ LZWDecompressor::Status ret = lzw_decompressor_->Decode( ++ gif_image->row_buffer.data() + img_row_offset_, &img_row_avail_size_); ++ if (ret == LZWDecompressor::Status::kError) { + DecodingFailureAtTailCleanup(gif_image); +- return CFX_GifDecodeStatus::Error; ++ return GifDecoder::Status::kError; + } + +- while (ret != CFX_GifDecodeStatus::Error) { +- if (ret == CFX_GifDecodeStatus::Success) { +- ReadScanline(gif_image->row_num, gif_image->row_buffer.data()); ++ while (ret != LZWDecompressor::Status::kError) { ++ if (ret == LZWDecompressor::Status::kSuccess) { ++ ReadScanline(gif_image->row_num, gif_image->row_buffer); + gif_image->row_buffer.clear(); + SaveDecodingStatus(GIF_D_STATUS_TAIL); +- return CFX_GifDecodeStatus::Success; ++ return GifDecoder::Status::kSuccess; + } + +- if (ret == CFX_GifDecodeStatus::Unfinished) { ++ if (ret == LZWDecompressor::Status::kUnfinished) { + read_marker = input_buffer_->GetPosition(); + if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size))) +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + + if (img_data_size != GIF_BLOCK_TERMINAL) { + img_data.resize(img_data_size); + if (!ReadAllOrNone(img_data.data(), img_data_size)) { + input_buffer_->Seek(read_marker); +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + } + +- if (!lzw_decompressor_.get()) +- lzw_decompressor_ = CFX_LZWDecompressor::Create( +- !gif_image->local_palettes.empty() +- ? gif_image->local_pallette_exp +- : global_pal_exp_, +- gif_image->code_exp); ++ lzw_decompressor_->SetSource(img_data.data(), img_data_size); ++ + SaveDecodingStatus(GIF_D_STATUS_IMG_DATA); + img_row_offset_ += img_row_avail_size_; + img_row_avail_size_ = gif_img_row_bytes - img_row_offset_; +- ret = lzw_decompressor_.get() +- ? lzw_decompressor_->Decode( +- img_data.data(), img_data_size, +- gif_image->row_buffer.data() + img_row_offset_, +- &img_row_avail_size_) +- : CFX_GifDecodeStatus::Error; ++ ret = lzw_decompressor_->Decode( ++ gif_image->row_buffer.data() + img_row_offset_, ++ &img_row_avail_size_); + } + } + +- if (ret == CFX_GifDecodeStatus::InsufficientDestSize) { ++ if (ret == LZWDecompressor::Status::kInsufficientDestSize) { + if (gif_image->image_info.local_flags.interlace) { +- ReadScanline(gif_image->row_num, gif_image->row_buffer.data()); ++ ReadScanline(gif_image->row_num, gif_image->row_buffer); + gif_image->row_num += kGifInterlaceStep[img_pass_num_]; + if (gif_image->row_num >= + static_cast(gif_image->image_info.height)) { + img_pass_num_++; +- if (img_pass_num_ == FX_ArraySize(kGifInterlaceStep)) { ++ if (img_pass_num_ == std::size(kGifInterlaceStep)) { + DecodingFailureAtTailCleanup(gif_image); +- return CFX_GifDecodeStatus::Error; ++ return GifDecoder::Status::kError; + } + gif_image->row_num = kGifInterlaceStep[img_pass_num_] / 2; + } + } else { +- ReadScanline(gif_image->row_num++, gif_image->row_buffer.data()); ++ ReadScanline(gif_image->row_num++, gif_image->row_buffer); + } ++ + img_row_offset_ = 0; + img_row_avail_size_ = gif_img_row_bytes; +- ret = lzw_decompressor_.get() +- ? lzw_decompressor_->Decode( +- img_data.data(), img_data_size, +- gif_image->row_buffer.data() + img_row_offset_, +- &img_row_avail_size_) +- : CFX_GifDecodeStatus::Error; ++ ret = lzw_decompressor_->Decode( ++ gif_image->row_buffer.data() + img_row_offset_, ++ &img_row_avail_size_); + } + +- if (ret == CFX_GifDecodeStatus::InsufficientDestSize || +- ret == CFX_GifDecodeStatus::Error) { ++ if (ret == LZWDecompressor::Status::kError) { + DecodingFailureAtTailCleanup(gif_image); +- return CFX_GifDecodeStatus::Error; ++ return GifDecoder::Status::kError; + } + } + } + SaveDecodingStatus(GIF_D_STATUS_TAIL); + } +- return CFX_GifDecodeStatus::Error; ++ return GifDecoder::Status::kError; + } + + void CFX_GifContext::SetInputBuffer(RetainPtr codec_memory) { +@@ -341,7 +324,8 @@ uint32_t CFX_GifContext::GetAvailInput() const { + if (!input_buffer_) + return 0; + +- return input_buffer_->GetSize() - input_buffer_->GetPosition(); ++ return pdfium::base::checked_cast(input_buffer_->GetSize() - ++ input_buffer_->GetPosition()); + } + + bool CFX_GifContext::ReadAllOrNone(uint8_t* dest, uint32_t size) { +@@ -349,7 +333,7 @@ bool CFX_GifContext::ReadAllOrNone(uint8_t* dest, uint32_t size) { + return false; + + size_t read_marker = input_buffer_->GetPosition(); +- size_t read = input_buffer_->ReadBlock(dest, size); ++ size_t read = input_buffer_->ReadBlock({dest, size}); + if (read < size) { + input_buffer_->Seek(read_marker); + return false; +@@ -358,30 +342,30 @@ bool CFX_GifContext::ReadAllOrNone(uint8_t* dest, uint32_t size) { + return true; + } + +-CFX_GifDecodeStatus CFX_GifContext::ReadGifSignature() { ++GifDecoder::Status CFX_GifContext::ReadGifSignature() { + CFX_GifHeader header; + if (!ReadAllOrNone(reinterpret_cast(&header), 6)) +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + + if (strncmp(header.signature, kGifSignature87, 6) != 0 && + strncmp(header.signature, kGifSignature89, 6) != 0) { +- return CFX_GifDecodeStatus::Error; ++ return GifDecoder::Status::kError; + } + +- return CFX_GifDecodeStatus::Success; ++ return GifDecoder::Status::kSuccess; + } + +-CFX_GifDecodeStatus CFX_GifContext::ReadLogicalScreenDescriptor() { ++GifDecoder::Status CFX_GifContext::ReadLogicalScreenDescriptor() { + CFX_GifLocalScreenDescriptor lsd; + size_t read_marker = input_buffer_->GetPosition(); + + if (!ReadAllOrNone(reinterpret_cast(&lsd), sizeof(lsd))) +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + + if (lsd.global_flags.global_pal) { + uint32_t palette_count = unsigned(2 << lsd.global_flags.pal_bits); + if (lsd.bc_index >= palette_count) +- return CFX_GifDecodeStatus::Error; ++ return GifDecoder::Status::kError; + bc_index_ = lsd.bc_index; + + uint32_t palette_size = palette_count * sizeof(CFX_GifPalette); +@@ -390,62 +374,62 @@ CFX_GifDecodeStatus CFX_GifContext::ReadLogicalScreenDescriptor() { + palette_size)) { + // Roll back the read for the LSD + input_buffer_->Seek(read_marker); +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + } + +- global_pal_exp_ = lsd.global_flags.pal_bits; ++ global_palette_exp_ = lsd.global_flags.pal_bits; + global_sort_flag_ = lsd.global_flags.sort_flag; + global_color_resolution_ = lsd.global_flags.color_resolution; + std::swap(global_palette_, palette); + } + + width_ = static_cast( +- FXWORD_GET_LSBFIRST(reinterpret_cast(&lsd.width))); ++ FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast(&lsd.width))); + height_ = static_cast( +- FXWORD_GET_LSBFIRST(reinterpret_cast(&lsd.height))); ++ FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast(&lsd.height))); + +- return CFX_GifDecodeStatus::Success; ++ return GifDecoder::Status::kSuccess; + } + + void CFX_GifContext::SaveDecodingStatus(int32_t status) { + decode_status_ = status; + } + +-CFX_GifDecodeStatus CFX_GifContext::DecodeExtension() { ++GifDecoder::Status CFX_GifContext::DecodeExtension() { + size_t read_marker = input_buffer_->GetPosition(); + + switch (decode_status_) { + case GIF_D_STATUS_EXT_CE: { + if (!ScanForTerminalMarker()) { + input_buffer_->Seek(read_marker); +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + } + break; + } + case GIF_D_STATUS_EXT_PTE: { + CFX_GifPlainTextExtension gif_pte; + if (!ReadAllOrNone(reinterpret_cast(&gif_pte), sizeof(gif_pte))) +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + + graphic_control_extension_ = nullptr; + if (!ScanForTerminalMarker()) { + input_buffer_->Seek(read_marker); +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + } + break; + } + case GIF_D_STATUS_EXT_GCE: { + CFX_GifGraphicControlExtension gif_gce; + if (!ReadAllOrNone(reinterpret_cast(&gif_gce), sizeof(gif_gce))) +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + + if (!graphic_control_extension_.get()) + graphic_control_extension_ = +- pdfium::MakeUnique(); ++ std::make_unique(); + graphic_control_extension_->block_size = gif_gce.block_size; + graphic_control_extension_->gce_flags = gif_gce.gce_flags; +- graphic_control_extension_->delay_time = +- FXWORD_GET_LSBFIRST(reinterpret_cast(&gif_gce.delay_time)); ++ graphic_control_extension_->delay_time = FXSYS_UINT16_GET_LSBFIRST( ++ reinterpret_cast(&gif_gce.delay_time)); + graphic_control_extension_->trans_index = gif_gce.trans_index; + break; + } +@@ -454,47 +438,47 @@ CFX_GifDecodeStatus CFX_GifContext::DecodeExtension() { + graphic_control_extension_ = nullptr; + if (!ScanForTerminalMarker()) { + input_buffer_->Seek(read_marker); +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + } + } + } + + SaveDecodingStatus(GIF_D_STATUS_SIG); +- return CFX_GifDecodeStatus::Success; ++ return GifDecoder::Status::kSuccess; + } + +-CFX_GifDecodeStatus CFX_GifContext::DecodeImageInfo() { ++GifDecoder::Status CFX_GifContext::DecodeImageInfo() { + if (width_ <= 0 || height_ <= 0) +- return CFX_GifDecodeStatus::Error; ++ return GifDecoder::Status::kError; + + size_t read_marker = input_buffer_->GetPosition(); +- CFX_CFX_GifImageInfo img_info; ++ CFX_GifImageInfo img_info; + if (!ReadAllOrNone(reinterpret_cast(&img_info), sizeof(img_info))) +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + +- auto gif_image = pdfium::MakeUnique(); ++ auto gif_image = std::make_unique(); + gif_image->image_info.left = +- FXWORD_GET_LSBFIRST(reinterpret_cast(&img_info.left)); ++ FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast(&img_info.left)); + gif_image->image_info.top = +- FXWORD_GET_LSBFIRST(reinterpret_cast(&img_info.top)); ++ FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast(&img_info.top)); + gif_image->image_info.width = +- FXWORD_GET_LSBFIRST(reinterpret_cast(&img_info.width)); ++ FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast(&img_info.width)); + gif_image->image_info.height = +- FXWORD_GET_LSBFIRST(reinterpret_cast(&img_info.height)); ++ FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast(&img_info.height)); + gif_image->image_info.local_flags = img_info.local_flags; + if (gif_image->image_info.left + gif_image->image_info.width > width_ || + gif_image->image_info.top + gif_image->image_info.height > height_) +- return CFX_GifDecodeStatus::Error; ++ return GifDecoder::Status::kError; + + CFX_GifLocalFlags* gif_img_info_lf = &img_info.local_flags; + if (gif_img_info_lf->local_pal) { +- gif_image->local_pallette_exp = gif_img_info_lf->pal_bits; ++ gif_image->local_palette_exp = gif_img_info_lf->pal_bits; + uint32_t loc_pal_count = unsigned(2 << gif_img_info_lf->pal_bits); + std::vector loc_pal(loc_pal_count); + if (!ReadAllOrNone(reinterpret_cast(loc_pal.data()), + loc_pal_count * sizeof(CFX_GifPalette))) { + input_buffer_->Seek(read_marker); +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + } + + gif_image->local_palettes = std::move(loc_pal); +@@ -503,22 +487,20 @@ CFX_GifDecodeStatus CFX_GifContext::DecodeImageInfo() { + uint8_t code_size; + if (!ReadAllOrNone(&code_size, sizeof(code_size))) { + input_buffer_->Seek(read_marker); +- return CFX_GifDecodeStatus::Unfinished; ++ return GifDecoder::Status::kUnfinished; + } + + gif_image->code_exp = code_size; +- RecordCurrentPosition(&gif_image->data_pos); +- gif_image->data_pos += input_buffer_->GetPosition(); ++ gif_image->data_pos = delegate_->GifCurrentPosition(); + gif_image->image_GCE = nullptr; + if (graphic_control_extension_.get()) { + if (graphic_control_extension_->gce_flags.transparency) { + // Need to test that the color that is going to be transparent is actually + // in the palette being used. + if (graphic_control_extension_->trans_index >= +- 2 << (gif_image->local_palettes.empty() +- ? global_pal_exp_ +- : gif_image->local_pallette_exp)) +- return CFX_GifDecodeStatus::Error; ++ (2 << GetPaletteExp(gif_image.get()))) { ++ return GifDecoder::Status::kError; ++ } + } + gif_image->image_GCE = std::move(graphic_control_extension_); + graphic_control_extension_ = nullptr; +@@ -526,7 +508,7 @@ CFX_GifDecodeStatus CFX_GifContext::DecodeImageInfo() { + + images_.push_back(std::move(gif_image)); + SaveDecodingStatus(GIF_D_STATUS_IMG_DATA); +- return CFX_GifDecodeStatus::Success; ++ return GifDecoder::Status::kSuccess; + } + + void CFX_GifContext::DecodingFailureAtTailCleanup(CFX_GifImage* gif_image) { +@@ -550,4 +532,9 @@ bool CFX_GifContext::ScanForTerminalMarker() { + return true; + } + ++uint8_t CFX_GifContext::GetPaletteExp(CFX_GifImage* gif_image) const { ++ return !gif_image->local_palettes.empty() ? gif_image->local_palette_exp ++ : global_palette_exp_; ++} ++ + } // namespace fxcodec +diff --git a/core/fxcodec/gif/cfx_gifcontext.h b/core/fxcodec/gif/cfx_gifcontext.h +index d2ccd9d43..5e651f92c 100644 +--- a/core/fxcodec/gif/cfx_gifcontext.h ++++ b/core/fxcodec/gif/cfx_gifcontext.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,22 +11,22 @@ + #include + + #include "core/fxcodec/gif/cfx_gif.h" +-#include "core/fxcodec/gif/cfx_lzwdecompressor.h" +-#include "core/fxcodec/gif/gifmodule.h" +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcodec/gif/gif_decoder.h" ++#include "core/fxcodec/gif/lzw_decompressor.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "third_party/base/span.h" + + class CFX_CodecMemory; + + namespace fxcodec { + +-class CFX_GifContext : public ModuleIface::Context { ++class CFX_GifContext : public ProgressiveDecoderIface::Context { + public: +- CFX_GifContext(GifModule* gif_module, GifModule::Delegate* delegate); ++ explicit CFX_GifContext(GifDecoder::Delegate* delegate); + ~CFX_GifContext() override; + +- void RecordCurrentPosition(uint32_t* cur_pos); +- void ReadScanline(int32_t row_num, uint8_t* row_buf); ++ void ReadScanline(int32_t row_num, pdfium::span row_buf); + bool GetRecordPosition(uint32_t cur_pos, + int32_t left, + int32_t top, +@@ -34,28 +34,24 @@ class CFX_GifContext : public ModuleIface::Context { + int32_t height, + int32_t pal_num, + CFX_GifPalette* pal, +- int32_t delay_time, +- bool user_input, + int32_t trans_index, +- int32_t disposal_method, + bool interlace); +- CFX_GifDecodeStatus ReadHeader(); +- CFX_GifDecodeStatus GetFrame(); +- CFX_GifDecodeStatus LoadFrame(int32_t frame_num); ++ GifDecoder::Status ReadHeader(); ++ GifDecoder::Status GetFrame(); ++ GifDecoder::Status LoadFrame(size_t frame_num); + void SetInputBuffer(RetainPtr codec_memory); + uint32_t GetAvailInput() const; + size_t GetFrameNum() const { return images_.size(); } + +- UnownedPtr const gif_module_; +- UnownedPtr const delegate_; ++ UnownedPtr const delegate_; + std::vector global_palette_; +- uint8_t global_pal_exp_ = 0; ++ uint8_t global_palette_exp_ = 0; + uint32_t img_row_offset_ = 0; + uint32_t img_row_avail_size_ = 0; + int32_t decode_status_ = GIF_D_STATUS_SIG; + std::unique_ptr graphic_control_extension_; + std::vector> images_; +- std::unique_ptr lzw_decompressor_; ++ std::unique_ptr lzw_decompressor_; + int width_ = 0; + int height_ = 0; + uint8_t bc_index_ = 0; +@@ -65,17 +61,18 @@ class CFX_GifContext : public ModuleIface::Context { + + protected: + bool ReadAllOrNone(uint8_t* dest, uint32_t size); +- CFX_GifDecodeStatus ReadGifSignature(); +- CFX_GifDecodeStatus ReadLogicalScreenDescriptor(); ++ GifDecoder::Status ReadGifSignature(); ++ GifDecoder::Status ReadLogicalScreenDescriptor(); + + RetainPtr input_buffer_; + + private: + void SaveDecodingStatus(int32_t status); +- CFX_GifDecodeStatus DecodeExtension(); +- CFX_GifDecodeStatus DecodeImageInfo(); ++ GifDecoder::Status DecodeExtension(); ++ GifDecoder::Status DecodeImageInfo(); + void DecodingFailureAtTailCleanup(CFX_GifImage* gif_image); + bool ScanForTerminalMarker(); ++ uint8_t GetPaletteExp(CFX_GifImage* gif_image) const; + }; + + } // namespace fxcodec +diff --git a/core/fxcodec/gif/cfx_gifcontext_unittest.cpp b/core/fxcodec/gif/cfx_gifcontext_unittest.cpp +index 20d34eaf6..ad731bbef 100644 +--- a/core/fxcodec/gif/cfx_gifcontext_unittest.cpp ++++ b/core/fxcodec/gif/cfx_gifcontext_unittest.cpp +@@ -1,21 +1,24 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcodec/gif/cfx_gifcontext.h" + ++#include ++ + #include + + #include "core/fxcodec/cfx_codec_memory.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/span_util.h" + #include "testing/gtest/include/gtest/gtest.h" + + namespace fxcodec { + + class CFX_GifContextForTest final : public CFX_GifContext { + public: +- CFX_GifContextForTest(GifModule* gif_module, GifModule::Delegate* delegate) +- : CFX_GifContext(gif_module, delegate) {} +- ~CFX_GifContextForTest() override {} ++ CFX_GifContextForTest() : CFX_GifContext(nullptr) {} ++ ~CFX_GifContextForTest() override = default; + + using CFX_GifContext::ReadAllOrNone; + using CFX_GifContext::ReadGifSignature; +@@ -24,14 +27,14 @@ class CFX_GifContextForTest final : public CFX_GifContext { + CFX_CodecMemory* InputBuffer() const { return input_buffer_.Get(); } + void SetTestInputBuffer(pdfium::span input) { + auto pMemory = pdfium::MakeRetain(input.size()); +- memcpy(pMemory->GetBuffer(), input.data(), input.size()); ++ fxcrt::spancpy(pMemory->GetBufferSpan(), input); + SetInputBuffer(std::move(pMemory)); + } + }; + + TEST(CFX_GifContext, SetInputBuffer) { + uint8_t buffer[] = {0x00, 0x01, 0x02}; +- CFX_GifContextForTest context(nullptr, nullptr); ++ CFX_GifContextForTest context; + + context.SetTestInputBuffer({nullptr, 0}); + EXPECT_EQ(0u, context.InputBuffer()->GetSize()); +@@ -47,10 +50,10 @@ TEST(CFX_GifContext, SetInputBuffer) { + } + + TEST(CFX_GifContext, ReadAllOrNone) { +- std::vector dest_buffer; ++ DataVector dest_buffer; + uint8_t src_buffer[] = {0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09}; +- CFX_GifContextForTest context(nullptr, nullptr); ++ CFX_GifContextForTest context; + + context.SetTestInputBuffer({nullptr, 0}); + EXPECT_FALSE(context.ReadAllOrNone(nullptr, 0)); +@@ -85,11 +88,11 @@ TEST(CFX_GifContext, ReadAllOrNone) { + } + + TEST(CFX_GifContext, ReadGifSignature) { +- CFX_GifContextForTest context(nullptr, nullptr); ++ CFX_GifContextForTest context; + { + uint8_t data[1]; + context.SetTestInputBuffer({data, 0}); +- EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadGifSignature()); ++ EXPECT_EQ(GifDecoder::Status::kUnfinished, context.ReadGifSignature()); + EXPECT_EQ(0u, context.InputBuffer()->GetPosition()); + context.SetTestInputBuffer({}); + } +@@ -97,14 +100,14 @@ TEST(CFX_GifContext, ReadGifSignature) { + { + uint8_t data[] = {'G', 'I', 'F'}; + context.SetTestInputBuffer(data); +- EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadGifSignature()); ++ EXPECT_EQ(GifDecoder::Status::kUnfinished, context.ReadGifSignature()); + EXPECT_EQ(0u, context.InputBuffer()->GetPosition()); + context.SetTestInputBuffer({}); + } + { + uint8_t data[] = {'N', 'O', 'T', 'G', 'I', 'F'}; + context.SetTestInputBuffer(data); +- EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature()); ++ EXPECT_EQ(GifDecoder::Status::kError, context.ReadGifSignature()); + EXPECT_EQ(6u, context.InputBuffer()->GetPosition()); + context.SetTestInputBuffer({}); + } +@@ -112,7 +115,7 @@ TEST(CFX_GifContext, ReadGifSignature) { + { + uint8_t data[] = {'G', 'I', 'F', '8', '0', 'a'}; + context.SetTestInputBuffer(data); +- EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature()); ++ EXPECT_EQ(GifDecoder::Status::kError, context.ReadGifSignature()); + EXPECT_EQ(6u, context.InputBuffer()->GetPosition()); + context.SetTestInputBuffer({}); + } +@@ -120,7 +123,7 @@ TEST(CFX_GifContext, ReadGifSignature) { + { + uint8_t data[] = {'G', 'I', 'F', '9', '2', 'a'}; + context.SetTestInputBuffer(data); +- EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature()); ++ EXPECT_EQ(GifDecoder::Status::kError, context.ReadGifSignature()); + EXPECT_EQ(6u, context.InputBuffer()->GetPosition()); + context.SetTestInputBuffer({}); + } +@@ -128,7 +131,7 @@ TEST(CFX_GifContext, ReadGifSignature) { + { + uint8_t data[] = {'G', 'I', 'F', '8', '7', 'a'}; + context.SetTestInputBuffer(data); +- EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadGifSignature()); ++ EXPECT_EQ(GifDecoder::Status::kSuccess, context.ReadGifSignature()); + EXPECT_EQ(6u, context.InputBuffer()->GetPosition()); + context.SetTestInputBuffer({}); + } +@@ -136,18 +139,18 @@ TEST(CFX_GifContext, ReadGifSignature) { + { + uint8_t data[] = {'G', 'I', 'F', '8', '9', 'a'}; + context.SetTestInputBuffer(data); +- EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadGifSignature()); ++ EXPECT_EQ(GifDecoder::Status::kSuccess, context.ReadGifSignature()); + EXPECT_EQ(6u, context.InputBuffer()->GetPosition()); + context.SetTestInputBuffer({}); + } + } + + TEST(CFX_GifContext, ReadLocalScreenDescriptor) { +- CFX_GifContextForTest context(nullptr, nullptr); ++ CFX_GifContextForTest context; + { + uint8_t data[1]; + context.SetTestInputBuffer({data, 0}); +- EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, ++ EXPECT_EQ(GifDecoder::Status::kUnfinished, + context.ReadLogicalScreenDescriptor()); + context.SetTestInputBuffer({}); + } +@@ -157,7 +160,7 @@ TEST(CFX_GifContext, ReadLocalScreenDescriptor) { + memset(&lsd, 0, sizeof(CFX_GifLocalScreenDescriptor)); + context.SetTestInputBuffer(lsd); + +- EXPECT_EQ(CFX_GifDecodeStatus::Success, ++ EXPECT_EQ(GifDecoder::Status::kSuccess, + context.ReadLogicalScreenDescriptor()); + + EXPECT_EQ(sizeof(CFX_GifLocalScreenDescriptor), +@@ -173,7 +176,7 @@ TEST(CFX_GifContext, ReadLocalScreenDescriptor) { + 0x00, 0x01, 0x02}; + context.SetTestInputBuffer(lsd); + +- EXPECT_EQ(CFX_GifDecodeStatus::Success, ++ EXPECT_EQ(GifDecoder::Status::kSuccess, + context.ReadLogicalScreenDescriptor()); + + EXPECT_EQ(sizeof(CFX_GifLocalScreenDescriptor), +@@ -189,7 +192,7 @@ TEST(CFX_GifContext, ReadLocalScreenDescriptor) { + 0x80, 0x01, 0x02}; + context.SetTestInputBuffer(lsd); + +- EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, ++ EXPECT_EQ(GifDecoder::Status::kUnfinished, + context.ReadLogicalScreenDescriptor()); + + EXPECT_EQ(0u, context.InputBuffer()->GetPosition()); +@@ -205,14 +208,14 @@ TEST(CFX_GifContext, ReadLocalScreenDescriptor) { + context.SetTestInputBuffer( + {reinterpret_cast(&data), sizeof(data)}); + +- EXPECT_EQ(CFX_GifDecodeStatus::Success, ++ EXPECT_EQ(GifDecoder::Status::kSuccess, + context.ReadLogicalScreenDescriptor()); + + EXPECT_EQ(sizeof(data), context.InputBuffer()->GetPosition()); + EXPECT_EQ(0x000A, context.width_); + EXPECT_EQ(0x0F00, context.height_); + EXPECT_EQ(1u, context.bc_index_); +- EXPECT_EQ(1u, context.global_pal_exp_); ++ EXPECT_EQ(1u, context.global_palette_exp_); + EXPECT_EQ(1, context.global_sort_flag_); + EXPECT_EQ(2, context.global_color_resolution_); + EXPECT_EQ(0, memcmp(data.palette, context.global_palette_.data(), +@@ -222,7 +225,7 @@ TEST(CFX_GifContext, ReadLocalScreenDescriptor) { + } + + TEST(CFX_GifContext, ReadHeader) { +- CFX_GifContextForTest context(nullptr, nullptr); ++ CFX_GifContextForTest context; + // Bad signature + { + struct { +@@ -233,7 +236,7 @@ TEST(CFX_GifContext, ReadHeader) { + context.SetTestInputBuffer( + {reinterpret_cast(&data), sizeof(data)}); + +- EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadHeader()); ++ EXPECT_EQ(GifDecoder::Status::kError, context.ReadHeader()); + EXPECT_EQ(sizeof(data.signature), context.InputBuffer()->GetPosition()); + context.SetTestInputBuffer({}); + } +@@ -243,7 +246,7 @@ TEST(CFX_GifContext, ReadHeader) { + context.SetTestInputBuffer( + {reinterpret_cast(&signature), sizeof(signature)}); + +- EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadHeader()); ++ EXPECT_EQ(GifDecoder::Status::kUnfinished, context.ReadHeader()); + EXPECT_EQ(sizeof(signature), context.InputBuffer()->GetPosition()); + context.SetTestInputBuffer({}); + } +@@ -257,7 +260,7 @@ TEST(CFX_GifContext, ReadHeader) { + context.SetTestInputBuffer( + {reinterpret_cast(&data), sizeof(data)}); + +- EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadHeader()); ++ EXPECT_EQ(GifDecoder::Status::kSuccess, context.ReadHeader()); + EXPECT_EQ(sizeof(data), context.InputBuffer()->GetPosition()); + EXPECT_EQ(0x000A, context.width_); + EXPECT_EQ(0x0F00, context.height_); +@@ -274,7 +277,7 @@ TEST(CFX_GifContext, ReadHeader) { + context.SetTestInputBuffer( + {reinterpret_cast(&data), sizeof(data)}); + +- EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadHeader()); ++ EXPECT_EQ(GifDecoder::Status::kUnfinished, context.ReadHeader()); + EXPECT_EQ(sizeof(data.signature), context.InputBuffer()->GetPosition()); + context.SetTestInputBuffer({}); + } +@@ -290,12 +293,12 @@ TEST(CFX_GifContext, ReadHeader) { + context.SetTestInputBuffer( + {reinterpret_cast(&data), sizeof(data)}); + +- EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadHeader()); ++ EXPECT_EQ(GifDecoder::Status::kSuccess, context.ReadHeader()); + EXPECT_EQ(sizeof(data), context.InputBuffer()->GetPosition()); + EXPECT_EQ(0x000A, context.width_); + EXPECT_EQ(0x0F00, context.height_); + EXPECT_EQ(1u, context.bc_index_); +- EXPECT_EQ(1u, context.global_pal_exp_); ++ EXPECT_EQ(1u, context.global_palette_exp_); + EXPECT_EQ(1, context.global_sort_flag_); + EXPECT_EQ(2, context.global_color_resolution_); + EXPECT_EQ(0, memcmp(data.palette, context.global_palette_.data(), +diff --git a/core/fxcodec/gif/cfx_lzwdecompressor.h b/core/fxcodec/gif/cfx_lzwdecompressor.h +deleted file mode 100644 +index d3ec588de..000000000 +--- a/core/fxcodec/gif/cfx_lzwdecompressor.h ++++ /dev/null +@@ -1,64 +0,0 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXCODEC_GIF_CFX_LZWDECOMPRESSOR_H_ +-#define CORE_FXCODEC_GIF_CFX_LZWDECOMPRESSOR_H_ +- +-#include +-#include +- +-#include "core/fxcodec/gif/cfx_gif.h" +- +-class CFX_LZWDecompressor { +- public: +- struct CodeEntry { +- uint16_t prefix; +- uint8_t suffix; +- }; +- +- // Returns nullptr on error +- static std::unique_ptr Create(uint8_t color_exp, +- uint8_t code_exp); +- ~CFX_LZWDecompressor(); +- +- CFX_GifDecodeStatus Decode(const uint8_t* src_buf, +- uint32_t src_size, +- uint8_t* dest_buf, +- uint32_t* dest_size); +- +- // Used by unittests, should not be called in production code. +- uint32_t ExtractDataForTest(uint8_t* dest_buf, uint32_t dest_size) { +- return ExtractData(dest_buf, dest_size); +- } +- +- std::vector* DecompressedForTest() { return &decompressed_; } +- size_t* DecompressedNextForTest() { return &decompressed_next_; } +- +- private: +- CFX_LZWDecompressor(uint8_t color_exp, uint8_t code_exp); +- void ClearTable(); +- void AddCode(uint16_t prefix_code, uint8_t append_char); +- bool DecodeString(uint16_t code); +- uint32_t ExtractData(uint8_t* dest_buf, uint32_t dest_size); +- +- uint8_t code_size_; +- uint8_t code_size_cur_; +- uint16_t code_color_end_; +- uint16_t code_clear_; +- uint16_t code_end_; +- uint16_t code_next_; +- uint8_t code_first_; +- std::vector decompressed_; +- size_t decompressed_next_; +- uint16_t code_old_; +- const uint8_t* next_in_; +- uint32_t avail_in_; +- uint8_t bits_left_; +- uint32_t code_store_; +- CodeEntry code_table_[GIF_MAX_LZW_CODE]; +-}; +- +-#endif // CORE_FXCODEC_GIF_CFX_LZWDECOMPRESSOR_H_ +diff --git a/core/fxcodec/gif/gif_decoder.cpp b/core/fxcodec/gif/gif_decoder.cpp +new file mode 100644 +index 000000000..4e7d41718 +--- /dev/null ++++ b/core/fxcodec/gif/gif_decoder.cpp +@@ -0,0 +1,74 @@ ++// Copyright 2014 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxcodec/gif/gif_decoder.h" ++ ++#include "core/fxcodec/cfx_codec_memory.h" ++#include "core/fxcodec/gif/cfx_gifcontext.h" ++#include "core/fxge/dib/fx_dib.h" ++ ++namespace fxcodec { ++ ++// static ++std::unique_ptr GifDecoder::StartDecode( ++ Delegate* pDelegate) { ++ return std::make_unique(pDelegate); ++} ++ ++// static ++GifDecoder::Status GifDecoder::ReadHeader( ++ ProgressiveDecoderIface::Context* pContext, ++ int* width, ++ int* height, ++ int* pal_num, ++ CFX_GifPalette** pal_pp, ++ int* bg_index) { ++ auto* context = static_cast(pContext); ++ Status ret = context->ReadHeader(); ++ if (ret != Status::kSuccess) ++ return ret; ++ ++ *width = context->width_; ++ *height = context->height_; ++ *pal_num = (2 << context->global_palette_exp_); ++ *pal_pp = context->global_palette_.empty() ? nullptr ++ : context->global_palette_.data(); ++ *bg_index = context->bc_index_; ++ return Status::kSuccess; ++} ++ ++// static ++std::pair GifDecoder::LoadFrameInfo( ++ ProgressiveDecoderIface::Context* pContext) { ++ auto* context = static_cast(pContext); ++ Status ret = context->GetFrame(); ++ if (ret != Status::kSuccess) ++ return {ret, 0}; ++ return {Status::kSuccess, context->GetFrameNum()}; ++} ++ ++// static ++GifDecoder::Status GifDecoder::LoadFrame( ++ ProgressiveDecoderIface::Context* pContext, ++ size_t frame_num) { ++ return static_cast(pContext)->LoadFrame(frame_num); ++} ++ ++// static ++FX_FILESIZE GifDecoder::GetAvailInput( ++ ProgressiveDecoderIface::Context* pContext) { ++ return static_cast(pContext)->GetAvailInput(); ++} ++ ++// static ++bool GifDecoder::Input(ProgressiveDecoderIface::Context* pContext, ++ RetainPtr codec_memory) { ++ auto* ctx = static_cast(pContext); ++ ctx->SetInputBuffer(std::move(codec_memory)); ++ return true; ++} ++ ++} // namespace fxcodec +diff --git a/core/fxcodec/gif/gif_decoder.h b/core/fxcodec/gif/gif_decoder.h +new file mode 100644 +index 000000000..cfcf0e961 +--- /dev/null ++++ b/core/fxcodec/gif/gif_decoder.h +@@ -0,0 +1,70 @@ ++// Copyright 2016 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCODEC_GIF_GIF_DECODER_H_ ++#define CORE_FXCODEC_GIF_GIF_DECODER_H_ ++ ++#include ++#include ++ ++#include "core/fxcodec/gif/cfx_gif.h" ++#include "core/fxcodec/progressive_decoder_iface.h" ++#include "core/fxcrt/fx_coordinates.h" ++#include "third_party/base/span.h" ++ ++#ifndef PDF_ENABLE_XFA_GIF ++#error "GIF must be enabled" ++#endif ++ ++namespace fxcodec { ++ ++class GifDecoder { ++ public: ++ enum class Status { ++ kError, ++ kSuccess, ++ kUnfinished, ++ }; ++ ++ class Delegate { ++ public: ++ virtual uint32_t GifCurrentPosition() const = 0; ++ virtual bool GifInputRecordPositionBuf(uint32_t rcd_pos, ++ const FX_RECT& img_rc, ++ int32_t pal_num, ++ CFX_GifPalette* pal_ptr, ++ int32_t trans_index, ++ bool interlace) = 0; ++ virtual void GifReadScanline(int32_t row_num, ++ pdfium::span row_buf) = 0; ++ }; ++ ++ static std::unique_ptr StartDecode( ++ Delegate* pDelegate); ++ static Status ReadHeader(ProgressiveDecoderIface::Context* context, ++ int* width, ++ int* height, ++ int* pal_num, ++ CFX_GifPalette** pal_pp, ++ int* bg_index); ++ static std::pair LoadFrameInfo( ++ ProgressiveDecoderIface::Context* context); ++ static Status LoadFrame(ProgressiveDecoderIface::Context* context, ++ size_t frame_num); ++ static FX_FILESIZE GetAvailInput(ProgressiveDecoderIface::Context* context); ++ static bool Input(ProgressiveDecoderIface::Context* context, ++ RetainPtr codec_memory); ++ ++ GifDecoder() = delete; ++ GifDecoder(const GifDecoder&) = delete; ++ GifDecoder& operator=(const GifDecoder&) = delete; ++}; ++ ++} // namespace fxcodec ++ ++using GifDecoder = fxcodec::GifDecoder; ++ ++#endif // CORE_FXCODEC_GIF_GIF_DECODER_H_ +diff --git a/core/fxcodec/gif/gif_progressive_decoder.cpp b/core/fxcodec/gif/gif_progressive_decoder.cpp +new file mode 100644 +index 000000000..78c6a8cd2 +--- /dev/null ++++ b/core/fxcodec/gif/gif_progressive_decoder.cpp +@@ -0,0 +1,33 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxcodec/gif/gif_progressive_decoder.h" ++ ++#include "core/fxcodec/cfx_codec_memory.h" ++#include "core/fxcodec/gif/gif_decoder.h" ++ ++namespace fxcodec { ++ ++// static ++GifProgressiveDecoder* GifProgressiveDecoder::GetInstance() { ++ static pdfium::base::NoDestructor s; ++ return s.get(); ++} ++ ++GifProgressiveDecoder::GifProgressiveDecoder() = default; ++ ++GifProgressiveDecoder::~GifProgressiveDecoder() = default; ++ ++FX_FILESIZE GifProgressiveDecoder::GetAvailInput(Context* context) const { ++ return GifDecoder::GetAvailInput(context); ++} ++ ++bool GifProgressiveDecoder::Input(Context* context, ++ RetainPtr codec_memory) { ++ return GifDecoder::Input(context, codec_memory); ++} ++ ++} // namespace fxcodec +diff --git a/core/fxcodec/gif/gif_progressive_decoder.h b/core/fxcodec/gif/gif_progressive_decoder.h +new file mode 100644 +index 000000000..6b58c3c16 +--- /dev/null ++++ b/core/fxcodec/gif/gif_progressive_decoder.h +@@ -0,0 +1,39 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCODEC_GIF_GIF_PROGRESSIVE_DECODER_H_ ++#define CORE_FXCODEC_GIF_GIF_PROGRESSIVE_DECODER_H_ ++ ++#include "core/fxcodec/progressive_decoder_iface.h" ++#include "third_party/base/no_destructor.h" ++ ++#ifndef PDF_ENABLE_XFA_GIF ++#error "GIF must be enabled" ++#endif ++ ++namespace fxcodec { ++ ++class GifProgressiveDecoder final : public ProgressiveDecoderIface { ++ public: ++ static GifProgressiveDecoder* GetInstance(); ++ ++ // ProgressiveDecoderIface: ++ FX_FILESIZE GetAvailInput(Context* context) const override; ++ bool Input(Context* context, ++ RetainPtr codec_memory) override; ++ ++ private: ++ friend pdfium::base::NoDestructor; ++ ++ GifProgressiveDecoder(); ++ ~GifProgressiveDecoder() override; ++}; ++ ++} // namespace fxcodec ++ ++using GifProgressiveDecoder = fxcodec::GifProgressiveDecoder; ++ ++#endif // CORE_FXCODEC_GIF_GIF_PROGRESSIVE_DECODER_H_ +diff --git a/core/fxcodec/gif/gifmodule.cpp b/core/fxcodec/gif/gifmodule.cpp +deleted file mode 100644 +index 041aa3643..000000000 +--- a/core/fxcodec/gif/gifmodule.cpp ++++ /dev/null +@@ -1,71 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fxcodec/gif/gifmodule.h" +- +-#include "core/fxcodec/cfx_codec_memory.h" +-#include "core/fxcodec/fx_codec.h" +-#include "core/fxcodec/gif/cfx_gif.h" +-#include "core/fxcodec/gif/cfx_gifcontext.h" +-#include "core/fxge/fx_dib.h" +-#include "third_party/base/ptr_util.h" +- +-namespace fxcodec { +- +-GifModule::GifModule() = default; +- +-GifModule::~GifModule() = default; +- +-std::unique_ptr GifModule::Start(Delegate* pDelegate) { +- return pdfium::MakeUnique(this, pDelegate); +-} +- +-CFX_GifDecodeStatus GifModule::ReadHeader(Context* pContext, +- int* width, +- int* height, +- int* pal_num, +- CFX_GifPalette** pal_pp, +- int* bg_index) { +- auto* context = static_cast(pContext); +- CFX_GifDecodeStatus ret = context->ReadHeader(); +- if (ret != CFX_GifDecodeStatus::Success) +- return ret; +- +- *width = context->width_; +- *height = context->height_; +- *pal_num = (2 << context->global_pal_exp_); +- *pal_pp = context->global_palette_.empty() ? nullptr +- : context->global_palette_.data(); +- *bg_index = context->bc_index_; +- return CFX_GifDecodeStatus::Success; +-} +- +-std::pair GifModule::LoadFrameInfo( +- Context* pContext) { +- auto* context = static_cast(pContext); +- CFX_GifDecodeStatus ret = context->GetFrame(); +- if (ret != CFX_GifDecodeStatus::Success) +- return {ret, 0}; +- return {CFX_GifDecodeStatus::Success, context->GetFrameNum()}; +-} +- +-CFX_GifDecodeStatus GifModule::LoadFrame(Context* pContext, size_t frame_num) { +- return static_cast(pContext)->LoadFrame(frame_num); +-} +- +-FX_FILESIZE GifModule::GetAvailInput(Context* pContext) const { +- return static_cast(pContext)->GetAvailInput(); +-} +- +-bool GifModule::Input(Context* pContext, +- RetainPtr codec_memory, +- CFX_DIBAttribute*) { +- auto* ctx = static_cast(pContext); +- ctx->SetInputBuffer(std::move(codec_memory)); +- return true; +-} +- +-} // namespace fxcodec +diff --git a/core/fxcodec/gif/gifmodule.h b/core/fxcodec/gif/gifmodule.h +deleted file mode 100644 +index 2944e34bd..000000000 +--- a/core/fxcodec/gif/gifmodule.h ++++ /dev/null +@@ -1,62 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXCODEC_GIF_GIFMODULE_H_ +-#define CORE_FXCODEC_GIF_GIFMODULE_H_ +- +-#include +-#include +- +-#include "core/fxcodec/codec_module_iface.h" +-#include "core/fxcodec/gif/cfx_gif.h" +-#include "core/fxcrt/fx_coordinates.h" +- +-namespace fxcodec { +- +-class CFX_DIBAttribute; +- +-class GifModule final : public ModuleIface { +- public: +- class Delegate { +- public: +- virtual void GifRecordCurrentPosition(uint32_t& cur_pos) = 0; +- virtual bool GifInputRecordPositionBuf(uint32_t rcd_pos, +- const FX_RECT& img_rc, +- int32_t pal_num, +- CFX_GifPalette* pal_ptr, +- int32_t delay_time, +- bool user_input, +- int32_t trans_index, +- int32_t disposal_method, +- bool interlace) = 0; +- virtual void GifReadScanline(int32_t row_num, uint8_t* row_buf) = 0; +- }; +- +- GifModule(); +- ~GifModule() override; +- +- // ModuleIface: +- FX_FILESIZE GetAvailInput(Context* context) const override; +- bool Input(Context* context, +- RetainPtr codec_memory, +- CFX_DIBAttribute* pAttribute) override; +- +- std::unique_ptr Start(Delegate* pDelegate); +- CFX_GifDecodeStatus ReadHeader(Context* context, +- int* width, +- int* height, +- int* pal_num, +- CFX_GifPalette** pal_pp, +- int* bg_index); +- std::pair LoadFrameInfo(Context* context); +- CFX_GifDecodeStatus LoadFrame(Context* context, size_t frame_num); +-}; +- +-} // namespace fxcodec +- +-using GifModule = fxcodec::GifModule; +- +-#endif // CORE_FXCODEC_GIF_GIFMODULE_H_ +diff --git a/core/fxcodec/gif/cfx_lzwdecompressor.cpp b/core/fxcodec/gif/lzw_decompressor.cpp +similarity index 67% +rename from core/fxcodec/gif/cfx_lzwdecompressor.cpp +rename to core/fxcodec/gif/lzw_decompressor.cpp +index 12e0d89df..b13932b85 100644 +--- a/core/fxcodec/gif/cfx_lzwdecompressor.cpp ++++ b/core/fxcodec/gif/lzw_decompressor.cpp +@@ -1,66 +1,65 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#include "core/fxcodec/gif/cfx_lzwdecompressor.h" ++#include "core/fxcodec/gif/lzw_decompressor.h" ++ ++#include + + #include +-#include + #include + #include + + #include "core/fxcrt/fx_safe_types.h" + #include "third_party/base/numerics/safe_math.h" ++#include "third_party/base/ptr_util.h" ++ ++namespace fxcodec { + +-std::unique_ptr CFX_LZWDecompressor::Create( +- uint8_t color_exp, +- uint8_t code_exp) { +- // color_exp generates 2^(n + 1) codes, where as the code_exp reserves 2^n. ++std::unique_ptr LZWDecompressor::Create(uint8_t color_exp, ++ uint8_t code_exp) { ++ // |color_exp| generates 2^(n + 1) codes, where as the code_exp reserves 2^n. + // This is a quirk of the GIF spec. + if (code_exp > GIF_MAX_LZW_EXP || code_exp < color_exp + 1) + return nullptr; +- return std::unique_ptr( +- new CFX_LZWDecompressor(color_exp, code_exp)); ++ ++ // Private ctor. ++ return pdfium::WrapUnique(new LZWDecompressor(color_exp, code_exp)); + } + +-CFX_LZWDecompressor::CFX_LZWDecompressor(uint8_t color_exp, uint8_t code_exp) ++LZWDecompressor::LZWDecompressor(uint8_t color_exp, uint8_t code_exp) + : code_size_(code_exp), +- code_size_cur_(0), + code_color_end_(static_cast(1 << (color_exp + 1))), + code_clear_(static_cast(1 << code_exp)), +- code_end_(static_cast((1 << code_exp) + 1)), +- code_next_(0), +- code_first_(0), +- code_old_(0), +- next_in_(nullptr), +- avail_in_(0), +- bits_left_(0), +- code_store_(0) {} +- +-CFX_LZWDecompressor::~CFX_LZWDecompressor() {} +- +-CFX_GifDecodeStatus CFX_LZWDecompressor::Decode(const uint8_t* src_buf, +- uint32_t src_size, +- uint8_t* dest_buf, +- uint32_t* dest_size) { +- if (!src_buf || src_size == 0 || !dest_buf || !dest_size) +- return CFX_GifDecodeStatus::Error; ++ code_end_(static_cast((1 << code_exp) + 1)) { ++ ClearTable(); ++} + +- if (*dest_size == 0) +- return CFX_GifDecodeStatus::InsufficientDestSize; ++LZWDecompressor::~LZWDecompressor() = default; + ++void LZWDecompressor::SetSource(const uint8_t* src_buf, uint32_t src_size) { + next_in_ = src_buf; + avail_in_ = src_size; ++} + +- ClearTable(); ++LZWDecompressor::Status LZWDecompressor::Decode(uint8_t* dest_buf, ++ uint32_t* dest_size) { ++ if (!next_in_ || !dest_buf || !dest_size) ++ return Status::kError; ++ ++ if (avail_in_ == 0) ++ return Status::kUnfinished; ++ ++ if (*dest_size == 0) ++ return Status::kInsufficientDestSize; + + uint32_t i = 0; + if (decompressed_next_ != 0) { + uint32_t extracted_size = ExtractData(dest_buf, *dest_size); + if (decompressed_next_ != 0) +- return CFX_GifDecodeStatus::InsufficientDestSize; ++ return Status::kInsufficientDestSize; + + dest_buf += extracted_size; + i += extracted_size; +@@ -68,17 +67,17 @@ CFX_GifDecodeStatus CFX_LZWDecompressor::Decode(const uint8_t* src_buf, + + while (i <= *dest_size && (avail_in_ > 0 || bits_left_ >= code_size_cur_)) { + if (code_size_cur_ > GIF_MAX_LZW_EXP) +- return CFX_GifDecodeStatus::Error; ++ return Status::kError; + + if (avail_in_ > 0) { + if (bits_left_ > 31) +- return CFX_GifDecodeStatus::Error; ++ return Status::kError; + + FX_SAFE_UINT32 safe_code = *next_in_++; + safe_code <<= bits_left_; + safe_code |= code_store_; + if (!safe_code.IsValid()) +- return CFX_GifDecodeStatus::Error; ++ return Status::kError; + + code_store_ = safe_code.ValueOrDie(); + --avail_in_; +@@ -96,7 +95,7 @@ CFX_GifDecodeStatus CFX_LZWDecompressor::Decode(const uint8_t* src_buf, + } + if (code == code_end_) { + *dest_size = i; +- return CFX_GifDecodeStatus::Success; ++ return Status::kSuccess; + } + + if (code_old_ != static_cast(-1)) { +@@ -104,12 +103,12 @@ CFX_GifDecodeStatus CFX_LZWDecompressor::Decode(const uint8_t* src_buf, + if (code == code_next_) { + AddCode(code_old_, code_first_); + if (!DecodeString(code)) +- return CFX_GifDecodeStatus::Error; ++ return Status::kError; + } else if (code > code_next_) { +- return CFX_GifDecodeStatus::Error; ++ return Status::kError; + } else { + if (!DecodeString(code)) +- return CFX_GifDecodeStatus::Error; ++ return Status::kError; + + uint8_t append_char = decompressed_[decompressed_next_ - 1]; + AddCode(code_old_, append_char); +@@ -117,13 +116,13 @@ CFX_GifDecodeStatus CFX_LZWDecompressor::Decode(const uint8_t* src_buf, + } + } else { + if (!DecodeString(code)) +- return CFX_GifDecodeStatus::Error; ++ return Status::kError; + } + + code_old_ = code; + uint32_t extracted_size = ExtractData(dest_buf, *dest_size - i); + if (decompressed_next_ != 0) +- return CFX_GifDecodeStatus::InsufficientDestSize; ++ return Status::kInsufficientDestSize; + + dest_buf += extracted_size; + i += extracted_size; +@@ -131,13 +130,13 @@ CFX_GifDecodeStatus CFX_LZWDecompressor::Decode(const uint8_t* src_buf, + } + + if (avail_in_ != 0) +- return CFX_GifDecodeStatus::Error; ++ return Status::kError; + + *dest_size = i; +- return CFX_GifDecodeStatus::Unfinished; ++ return Status::kUnfinished; + } + +-void CFX_LZWDecompressor::ClearTable() { ++void LZWDecompressor::ClearTable() { + code_size_cur_ = code_size_ + 1; + code_next_ = code_end_ + 1; + code_old_ = static_cast(-1); +@@ -148,7 +147,7 @@ void CFX_LZWDecompressor::ClearTable() { + decompressed_next_ = 0; + } + +-void CFX_LZWDecompressor::AddCode(uint16_t prefix_code, uint8_t append_char) { ++void LZWDecompressor::AddCode(uint16_t prefix_code, uint8_t append_char) { + if (code_next_ == GIF_MAX_LZW_CODE) + return; + +@@ -160,7 +159,7 @@ void CFX_LZWDecompressor::AddCode(uint16_t prefix_code, uint8_t append_char) { + } + } + +-bool CFX_LZWDecompressor::DecodeString(uint16_t code) { ++bool LZWDecompressor::DecodeString(uint16_t code) { + decompressed_.resize(code_next_ - code_clear_ + 1); + decompressed_next_ = 0; + +@@ -181,8 +180,7 @@ bool CFX_LZWDecompressor::DecodeString(uint16_t code) { + return true; + } + +-uint32_t CFX_LZWDecompressor::ExtractData(uint8_t* dest_buf, +- uint32_t dest_size) { ++uint32_t LZWDecompressor::ExtractData(uint8_t* dest_buf, uint32_t dest_size) { + if (dest_size == 0) + return 0; + +@@ -194,3 +192,5 @@ uint32_t CFX_LZWDecompressor::ExtractData(uint8_t* dest_buf, + decompressed_next_ -= copy_size; + return copy_size; + } ++ ++} // namespace fxcodec +diff --git a/core/fxcodec/gif/lzw_decompressor.h b/core/fxcodec/gif/lzw_decompressor.h +new file mode 100644 +index 000000000..c078fc8c9 +--- /dev/null ++++ b/core/fxcodec/gif/lzw_decompressor.h +@@ -0,0 +1,79 @@ ++// Copyright 2017 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCODEC_GIF_LZW_DECOMPRESSOR_H_ ++#define CORE_FXCODEC_GIF_LZW_DECOMPRESSOR_H_ ++ ++#include ++ ++#include ++ ++#include "core/fxcodec/gif/cfx_gif.h" ++#include "core/fxcrt/data_vector.h" ++ ++namespace fxcodec { ++ ++class LZWDecompressor { ++ public: ++ enum class Status { ++ kError, ++ kSuccess, ++ kUnfinished, ++ kInsufficientDestSize, ++ }; ++ ++ struct CodeEntry { ++ uint16_t prefix; ++ uint8_t suffix; ++ }; ++ ++ // Returns nullptr on error ++ static std::unique_ptr Create(uint8_t color_exp, ++ uint8_t code_exp); ++ ~LZWDecompressor(); ++ ++ void SetSource(const uint8_t* src_buf, uint32_t src_size); ++ Status Decode(uint8_t* dest_buf, uint32_t* dest_size); ++ ++ // Used by unittests, should not be called in production code. ++ uint32_t ExtractDataForTest(uint8_t* dest_buf, uint32_t dest_size) { ++ return ExtractData(dest_buf, dest_size); ++ } ++ ++ DataVector* DecompressedForTest() { return &decompressed_; } ++ size_t* DecompressedNextForTest() { return &decompressed_next_; } ++ ++ private: ++ // Use Create() instead. ++ LZWDecompressor(uint8_t color_exp, uint8_t code_exp); ++ ++ void ClearTable(); ++ void AddCode(uint16_t prefix_code, uint8_t append_char); ++ bool DecodeString(uint16_t code); ++ uint32_t ExtractData(uint8_t* dest_buf, uint32_t dest_size); ++ ++ const uint8_t code_size_; ++ uint8_t code_size_cur_ = 0; ++ const uint16_t code_color_end_; ++ const uint16_t code_clear_; ++ const uint16_t code_end_; ++ uint16_t code_next_ = 0; ++ uint8_t code_first_ = 0; ++ DataVector decompressed_; ++ size_t decompressed_next_ = 0; ++ uint16_t code_old_ = 0; ++ const uint8_t* next_in_ = nullptr; ++ uint32_t avail_in_ = 0; ++ uint8_t bits_left_ = 0; ++ uint32_t code_store_ = 0; ++ CodeEntry code_table_[GIF_MAX_LZW_CODE]; ++}; ++ ++} // namespace fxcodec ++ ++using LZWDecompressor = fxcodec::LZWDecompressor; ++ ++#endif // CORE_FXCODEC_GIF_LZW_DECOMPRESSOR_H_ +diff --git a/core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp b/core/fxcodec/gif/lzw_decompressor_unittest.cpp +similarity index 52% +rename from core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp +rename to core/fxcodec/gif/lzw_decompressor_unittest.cpp +index e31fb783d..191a0e4de 100644 +--- a/core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp ++++ b/core/fxcodec/gif/lzw_decompressor_unittest.cpp +@@ -1,34 +1,42 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include "core/fxcodec/gif/cfx_lzwdecompressor.h" ++#include "core/fxcodec/gif/lzw_decompressor.h" + +-#include "core/fxcrt/fx_memory.h" ++#include ++#include ++ ++#include ++ ++#include "core/fxcrt/data_vector.h" ++#include "testing/gmock/include/gmock/gmock.h" + #include "testing/gtest/include/gtest/gtest.h" + +-TEST(CFX_LZWDecompressor, CreateBadParams) { +- EXPECT_EQ(nullptr, CFX_LZWDecompressor::Create(0x10, 0x02)); +- EXPECT_EQ(nullptr, CFX_LZWDecompressor::Create(0x04, 0x0F)); +- EXPECT_EQ(nullptr, CFX_LZWDecompressor::Create(0x02, 0x02)); ++using ::testing::ElementsAreArray; ++ ++TEST(LZWDecompressor, CreateBadParams) { ++ EXPECT_FALSE(LZWDecompressor::Create(0x10, 0x02)); ++ EXPECT_FALSE(LZWDecompressor::Create(0x04, 0x0F)); ++ EXPECT_FALSE(LZWDecompressor::Create(0x02, 0x02)); + } + +-TEST(CFX_LZWDecompressor, ExtractData) { ++TEST(LZWDecompressor, ExtractData) { + uint8_t palette_exp = 0x1; + uint8_t code_exp = 0x2; +- auto decompressor = CFX_LZWDecompressor::Create(palette_exp, code_exp); ++ auto decompressor = LZWDecompressor::Create(palette_exp, code_exp); + ASSERT_NE(nullptr, decompressor); + + // Check that 0 length extract does nothing + { +- std::vector* decompressed = decompressor->DecompressedForTest(); ++ DataVector* decompressed = decompressor->DecompressedForTest(); + *decompressed = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + *(decompressor->DecompressedNextForTest()) = decompressed->size(); + uint8_t dest_buf[20]; + memset(dest_buf, static_cast(-1), sizeof(dest_buf)); + + EXPECT_EQ(0u, decompressor->ExtractDataForTest(dest_buf, 0)); +- for (size_t i = 0; i < FX_ArraySize(dest_buf); ++i) ++ for (size_t i = 0; i < std::size(dest_buf); ++i) + EXPECT_EQ(static_cast(-1), dest_buf[i]); + + EXPECT_EQ(10u, *(decompressor->DecompressedNextForTest())); +@@ -38,7 +46,7 @@ TEST(CFX_LZWDecompressor, ExtractData) { + + // Check that less than decompressed size only gets the expected number + { +- std::vector* decompressed = decompressor->DecompressedForTest(); ++ DataVector* decompressed = decompressor->DecompressedForTest(); + *decompressed = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + *(decompressor->DecompressedNextForTest()) = decompressed->size(); + uint8_t dest_buf[20]; +@@ -48,7 +56,7 @@ TEST(CFX_LZWDecompressor, ExtractData) { + size_t i = 0; + for (; i < 5; ++i) + EXPECT_EQ(9 - i, dest_buf[i]); +- for (; i < FX_ArraySize(dest_buf); ++i) ++ for (; i < std::size(dest_buf); ++i) + EXPECT_EQ(static_cast(-1), dest_buf[i]); + + EXPECT_EQ(5u, *(decompressor->DecompressedNextForTest())); +@@ -58,84 +66,86 @@ TEST(CFX_LZWDecompressor, ExtractData) { + + // Check that greater than decompressed size depletes the decompressor + { +- std::vector* decompressed = decompressor->DecompressedForTest(); ++ DataVector* decompressed = decompressor->DecompressedForTest(); + *decompressed = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + *(decompressor->DecompressedNextForTest()) = decompressed->size(); + uint8_t dest_buf[20]; + memset(dest_buf, static_cast(-1), sizeof(dest_buf)); + +- EXPECT_EQ(10u, decompressor->ExtractDataForTest(dest_buf, +- FX_ArraySize(dest_buf))); ++ EXPECT_EQ(10u, ++ decompressor->ExtractDataForTest(dest_buf, std::size(dest_buf))); + size_t i = 0; + for (; i < 10; ++i) + EXPECT_EQ(9 - i, dest_buf[i]); +- for (; i < FX_ArraySize(dest_buf); ++i) ++ for (; i < std::size(dest_buf); ++i) + EXPECT_EQ(static_cast(-1), dest_buf[i]); + + EXPECT_EQ(0u, *(decompressor->DecompressedNextForTest())); + } + } + +-TEST(CFX_LZWDecompressor, DecodeBadParams) { ++TEST(LZWDecompressor, DecodeBadParams) { + uint8_t palette_exp = 0x0; + uint8_t code_exp = 0x2; +- auto decompressor = CFX_LZWDecompressor::Create(palette_exp, code_exp); ++ auto decompressor = LZWDecompressor::Create(palette_exp, code_exp); + ASSERT_NE(nullptr, decompressor); + + uint8_t image_data[10]; +- uint32_t image_size = FX_ArraySize(image_data); ++ uint32_t image_size = std::size(image_data); + + uint8_t output_data[10]; +- uint32_t output_size = FX_ArraySize(output_data); +- +- EXPECT_EQ( +- CFX_GifDecodeStatus::Error, +- decompressor->Decode(nullptr, image_size, output_data, &output_size)); +- EXPECT_EQ(CFX_GifDecodeStatus::Error, +- decompressor->Decode(image_data, 0, output_data, &output_size)); +- EXPECT_EQ( +- CFX_GifDecodeStatus::Error, +- decompressor->Decode(image_data, image_size, nullptr, &output_size)); +- EXPECT_EQ(CFX_GifDecodeStatus::Error, +- decompressor->Decode(image_data, image_size, output_data, nullptr)); ++ uint32_t output_size = std::size(output_data); ++ ++ decompressor->SetSource(nullptr, image_size); ++ EXPECT_EQ(LZWDecompressor::Status::kError, ++ decompressor->Decode(output_data, &output_size)); ++ ++ decompressor->SetSource(image_data, 0); ++ EXPECT_EQ(LZWDecompressor::Status::kUnfinished, ++ decompressor->Decode(output_data, &output_size)); ++ ++ decompressor->SetSource(image_data, image_size); ++ EXPECT_EQ(LZWDecompressor::Status::kError, ++ decompressor->Decode(nullptr, &output_size)); ++ EXPECT_EQ(LZWDecompressor::Status::kError, ++ decompressor->Decode(output_data, nullptr)); + + output_size = 0; +- EXPECT_EQ( +- CFX_GifDecodeStatus::InsufficientDestSize, +- decompressor->Decode(image_data, image_size, output_data, &output_size)); ++ EXPECT_EQ(LZWDecompressor::Status::kInsufficientDestSize, ++ decompressor->Decode(output_data, &output_size)); + } + +-TEST(CFX_LZWDecompressor, Decode1x1SingleColour) { ++TEST(LZWDecompressor, Decode1x1SingleColour) { + uint8_t palette_exp = 0x0; + uint8_t code_exp = 0x2; +- auto decompressor = CFX_LZWDecompressor::Create(palette_exp, code_exp); ++ auto decompressor = LZWDecompressor::Create(palette_exp, code_exp); + ASSERT_NE(nullptr, decompressor); + + uint8_t image_data[] = {0x44, 0x01}; +- uint32_t image_size = FX_ArraySize(image_data); ++ uint32_t image_size = std::size(image_data); + + uint8_t expected_data[] = {0x00}; +- uint8_t output_data[FX_ArraySize(expected_data)]; ++ uint8_t output_data[std::size(expected_data)]; + memset(output_data, 0, sizeof(output_data)); +- uint32_t output_size = FX_ArraySize(output_data); ++ uint32_t output_size = std::size(output_data); + +- EXPECT_EQ( +- CFX_GifDecodeStatus::Success, +- decompressor->Decode(image_data, image_size, output_data, &output_size)); ++ decompressor->SetSource(image_data, image_size); ++ EXPECT_EQ(LZWDecompressor::Status::kSuccess, ++ decompressor->Decode(output_data, &output_size)); + +- EXPECT_EQ(FX_ArraySize(output_data), output_size); ++ EXPECT_EQ(std::size(output_data), output_size); + EXPECT_TRUE(0 == memcmp(expected_data, output_data, sizeof(expected_data))); + } + +-TEST(CFX_LZWDecompressor, Decode10x10SingleColour) { ++TEST(LZWDecompressor, Decode10x10SingleColour) { + uint8_t palette_exp = 0x0; + uint8_t code_exp = 0x2; +- auto decompressor = CFX_LZWDecompressor::Create(palette_exp, code_exp); ++ auto decompressor = LZWDecompressor::Create(palette_exp, code_exp); + ASSERT_NE(nullptr, decompressor); + + static constexpr uint8_t kImageData[] = {0x84, 0x8F, 0xA9, 0xCB, + 0xED, 0x0F, 0x63, 0x2B}; +- uint32_t image_size = FX_ArraySize(kImageData); ++ uint32_t image_size = std::size(kImageData); + + static constexpr uint8_t kExpectedData[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +@@ -147,28 +157,28 @@ TEST(CFX_LZWDecompressor, Decode10x10SingleColour) { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; +- uint8_t output_data[FX_ArraySize(kExpectedData)]; ++ uint8_t output_data[std::size(kExpectedData)]; + memset(output_data, 0, sizeof(output_data)); +- uint32_t output_size = FX_ArraySize(output_data); ++ uint32_t output_size = std::size(output_data); + +- EXPECT_EQ( +- CFX_GifDecodeStatus::Success, +- decompressor->Decode(kImageData, image_size, output_data, &output_size)); ++ decompressor->SetSource(kImageData, image_size); ++ EXPECT_EQ(LZWDecompressor::Status::kSuccess, ++ decompressor->Decode(output_data, &output_size)); + +- EXPECT_EQ(FX_ArraySize(output_data), output_size); ++ EXPECT_EQ(std::size(output_data), output_size); + EXPECT_TRUE(0 == memcmp(kExpectedData, output_data, sizeof(kExpectedData))); + } + +-TEST(CFX_LZWDecompressor, Decode10x10MultipleColour) { ++TEST(LZWDecompressor, Decode10x10MultipleColour) { + uint8_t palette_exp = 0x1; + uint8_t code_exp = 0x2; +- auto decompressor = CFX_LZWDecompressor::Create(palette_exp, code_exp); ++ auto decompressor = LZWDecompressor::Create(palette_exp, code_exp); + ASSERT_NE(nullptr, decompressor); + + static constexpr uint8_t kImageData[] = { + 0x8C, 0x2D, 0x99, 0x87, 0x2A, 0x1C, 0xDC, 0x33, 0xA0, 0x02, 0x75, + 0xEC, 0x95, 0xFA, 0xA8, 0xDE, 0x60, 0x8C, 0x04, 0x91, 0x4C, 0x01}; +- uint32_t image_size = FX_ArraySize(kImageData); ++ uint32_t image_size = std::size(kImageData); + + static constexpr uint8_t kExpectedData[] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, +@@ -181,36 +191,61 @@ TEST(CFX_LZWDecompressor, Decode10x10MultipleColour) { + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, + 0x01, 0x01, 0x01, 0x01}; + +- uint8_t output_data[FX_ArraySize(kExpectedData)]; ++ uint8_t output_data[std::size(kExpectedData)]; + memset(output_data, 0, sizeof(output_data)); +- uint32_t output_size = FX_ArraySize(output_data); ++ uint32_t output_size = std::size(output_data); + +- EXPECT_EQ( +- CFX_GifDecodeStatus::Success, +- decompressor->Decode(kImageData, image_size, output_data, &output_size)); ++ decompressor->SetSource(kImageData, image_size); ++ EXPECT_EQ(LZWDecompressor::Status::kSuccess, ++ decompressor->Decode(output_data, &output_size)); + +- EXPECT_EQ(FX_ArraySize(output_data), output_size); ++ EXPECT_EQ(std::size(output_data), output_size); + EXPECT_TRUE(0 == memcmp(kExpectedData, output_data, sizeof(kExpectedData))); + } + +-TEST(CFX_LZWDecompressor, HandleColourCodeOutOfPalette) { ++TEST(LZWDecompressor, MultipleDecodes) { ++ auto decompressor = LZWDecompressor::Create(/*color_exp=*/0, /*code_exp=*/2); ++ ASSERT_NE(nullptr, decompressor); ++ ++ static constexpr uint8_t kImageData[] = {0x84, 0x6f, 0x05}; ++ decompressor->SetSource(kImageData, std::size(kImageData)); ++ ++ static constexpr uint8_t kExpectedScanline[] = {0x00, 0x00, 0x00, 0x00}; ++ uint8_t output_data[std::size(kExpectedScanline)]; ++ ++ memset(output_data, 0xFF, sizeof(output_data)); ++ uint32_t output_size = std::size(output_data); ++ EXPECT_EQ(LZWDecompressor::Status::kInsufficientDestSize, ++ decompressor->Decode(output_data, &output_size)); ++ EXPECT_EQ(std::size(kExpectedScanline), output_size); ++ EXPECT_THAT(output_data, ElementsAreArray(kExpectedScanline)); ++ ++ memset(output_data, 0xFF, sizeof(output_data)); ++ output_size = std::size(output_data); ++ EXPECT_EQ(LZWDecompressor::Status::kSuccess, ++ decompressor->Decode(output_data, &output_size)); ++ EXPECT_EQ(std::size(kExpectedScanline), output_size); ++ EXPECT_THAT(output_data, ElementsAreArray(kExpectedScanline)); ++} ++ ++TEST(LZWDecompressor, HandleColourCodeOutOfPalette) { + uint8_t palette_exp = 0x2; // Image uses 10 colours, so the palette exp + // should be 3, 2^(3+1) = 16 colours. + uint8_t code_exp = 0x4; +- auto decompressor = CFX_LZWDecompressor::Create(palette_exp, code_exp); ++ auto decompressor = LZWDecompressor::Create(palette_exp, code_exp); + ASSERT_NE(nullptr, decompressor); + + static constexpr uint8_t kImageData[] = { + 0x30, 0xC9, 0x49, 0x81, 0xBD, 0x78, 0xE8, 0xCD, 0x89, 0xFF, + 0x60, 0x20, 0x8E, 0xE4, 0x61, 0x9E, 0xA8, 0xA1, 0xAE, 0x2C, + 0xE2, 0xBE, 0xB0, 0x20, 0xCF, 0x74, 0x61, 0xDF, 0x78, 0x04}; +- uint32_t image_size = FX_ArraySize(kImageData); ++ uint32_t image_size = std::size(kImageData); + + uint8_t output_data[100]; // The uncompressed data is for a 10x10 image + memset(output_data, 0, sizeof(output_data)); +- uint32_t output_size = FX_ArraySize(output_data); ++ uint32_t output_size = std::size(output_data); + +- EXPECT_EQ( +- CFX_GifDecodeStatus::Error, +- decompressor->Decode(kImageData, image_size, output_data, &output_size)); ++ decompressor->SetSource(kImageData, image_size); ++ EXPECT_EQ(LZWDecompressor::Status::kError, ++ decompressor->Decode(output_data, &output_size)); + } +diff --git a/core/fxcodec/icc/iccmodule.cpp b/core/fxcodec/icc/icc_transform.cpp +similarity index 59% +rename from core/fxcodec/icc/iccmodule.cpp +rename to core/fxcodec/icc/icc_transform.cpp +index e9a8cd64d..18f8a4179 100644 +--- a/core/fxcodec/icc/iccmodule.cpp ++++ b/core/fxcodec/icc/icc_transform.cpp +@@ -1,17 +1,21 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#include "core/fxcodec/icc/iccmodule.h" ++#include "core/fxcodec/icc/icc_transform.h" ++ ++#include + + #include + #include +-#include + ++#include "core/fxcrt/data_vector.h" ++#include "third_party/base/cxx17_backports.h" ++#include "third_party/base/notreached.h" ++#include "third_party/base/numerics/safe_conversions.h" + #include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" + + namespace fxcodec { + +@@ -36,23 +40,24 @@ bool Check3Components(cmsColorSpaceSignature cs) { + + } // namespace + +-CLcmsCmm::CLcmsCmm(cmsHTRANSFORM hTransform, +- int srcComponents, +- bool bIsLab, +- bool bNormal) ++IccTransform::IccTransform(cmsHTRANSFORM hTransform, ++ int srcComponents, ++ bool bIsLab, ++ bool bNormal) + : m_hTransform(hTransform), + m_nSrcComponents(srcComponents), + m_bLab(bIsLab), + m_bNormal(bNormal) {} + +-CLcmsCmm::~CLcmsCmm() { ++IccTransform::~IccTransform() { + cmsDeleteTransform(m_hTransform); + } + + // static +-std::unique_ptr IccModule::CreateTransformSRGB( ++std::unique_ptr IccTransform::CreateTransformSRGB( + pdfium::span span) { +- ScopedCmsProfile srcProfile(cmsOpenProfileFromMem(span.data(), span.size())); ++ ScopedCmsProfile srcProfile(cmsOpenProfileFromMem( ++ span.data(), pdfium::base::checked_cast(span.size()))); + if (!srcProfile) + return nullptr; + +@@ -61,8 +66,8 @@ std::unique_ptr IccModule::CreateTransformSRGB( + return nullptr; + + cmsColorSpaceSignature srcCS = cmsGetColorSpace(srcProfile.get()); +- + uint32_t nSrcComponents = cmsChannelsOf(srcCS); ++ + // According to PDF spec, number of components must be 1, 3, or 4. + if (nSrcComponents != 1 && nSrcComponents != 3 && nSrcComponents != 4) + return nullptr; +@@ -87,64 +92,56 @@ std::unique_ptr IccModule::CreateTransformSRGB( + return nullptr; + + cmsHTRANSFORM hTransform = nullptr; +- const int intent = 0; + switch (dstCS) { + case cmsSigRgbData: +- hTransform = cmsCreateTransform(srcProfile.get(), srcFormat, +- dstProfile.get(), TYPE_BGR_8, intent, 0); ++ hTransform = ++ cmsCreateTransform(srcProfile.get(), srcFormat, dstProfile.get(), ++ TYPE_BGR_8, INTENT_PERCEPTUAL, /*dwFlags=*/0); + break; + case cmsSigGrayData: + case cmsSigCmykData: + // Check3Components() already filtered these types. +- NOTREACHED(); +- break; ++ NOTREACHED_NORETURN(); + default: + break; + } + if (!hTransform) + return nullptr; + +- return pdfium::MakeUnique(hTransform, nSrcComponents, bLab, +- bNormal); ++ // Private ctor. ++ return pdfium::WrapUnique( ++ new IccTransform(hTransform, nSrcComponents, bLab, bNormal)); + } + +-// static +-void IccModule::Translate(CLcmsCmm* pTransform, +- uint32_t nSrcComponents, +- const float* pSrcValues, +- float* pDestValues) { +- if (!pTransform) +- return; +- ++void IccTransform::Translate(pdfium::span pSrcValues, ++ pdfium::span pDestValues) { + uint8_t output[4]; + // TODO(npm): Currently the CmsDoTransform method is part of LCMS and it will + // apply some member of m_hTransform to the input. We need to go over all the +- // places which set transform to verify that only |nSrcComponents| are used. +- if (pTransform->IsLab()) { +- std::vector inputs(std::max(nSrcComponents, 16u)); +- for (uint32_t i = 0; i < nSrcComponents; ++i) ++ // places which set transform to verify that only `pSrcValues.size()` ++ // components are used. ++ if (m_bLab) { ++ DataVector inputs(std::max(pSrcValues.size(), 16)); ++ for (uint32_t i = 0; i < pSrcValues.size(); ++i) + inputs[i] = pSrcValues[i]; +- cmsDoTransform(pTransform->transform(), inputs.data(), output, 1); ++ cmsDoTransform(m_hTransform, inputs.data(), output, 1); + } else { +- std::vector inputs(std::max(nSrcComponents, 16u)); +- for (uint32_t i = 0; i < nSrcComponents; ++i) { ++ DataVector inputs(std::max(pSrcValues.size(), 16)); ++ for (size_t i = 0; i < pSrcValues.size(); ++i) { + inputs[i] = + pdfium::clamp(static_cast(pSrcValues[i] * 255.0f), 0, 255); + } +- cmsDoTransform(pTransform->transform(), inputs.data(), output, 1); ++ cmsDoTransform(m_hTransform, inputs.data(), output, 1); + } + pDestValues[0] = output[2] / 255.0f; + pDestValues[1] = output[1] / 255.0f; + pDestValues[2] = output[0] / 255.0f; + } + +-// static +-void IccModule::TranslateScanline(CLcmsCmm* pTransform, +- unsigned char* pDest, +- const unsigned char* pSrc, +- int32_t pixels) { +- if (pTransform) +- cmsDoTransform(pTransform->transform(), pSrc, pDest, pixels); ++void IccTransform::TranslateScanline(pdfium::span pDest, ++ pdfium::span pSrc, ++ int32_t pixels) { ++ cmsDoTransform(m_hTransform, pSrc.data(), pDest.data(), pixels); + } + + } // namespace fxcodec +diff --git a/core/fxcodec/icc/icc_transform.h b/core/fxcodec/icc/icc_transform.h +new file mode 100644 +index 000000000..78cf82693 +--- /dev/null ++++ b/core/fxcodec/icc/icc_transform.h +@@ -0,0 +1,55 @@ ++// Copyright 2016 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCODEC_ICC_ICC_TRANSFORM_H_ ++#define CORE_FXCODEC_ICC_ICC_TRANSFORM_H_ ++ ++#include ++ ++#include ++ ++#include "core/fxcodec/fx_codec_def.h" ++#include "third_party/base/span.h" ++ ++#if defined(USE_SYSTEM_LCMS2) ++#include ++#else ++#include "third_party/lcms/include/lcms2.h" ++#endif ++ ++namespace fxcodec { ++ ++class IccTransform { ++ public: ++ static std::unique_ptr CreateTransformSRGB( ++ pdfium::span span); ++ ++ ~IccTransform(); ++ ++ void Translate(pdfium::span pSrcValues, ++ pdfium::span pDestValues); ++ void TranslateScanline(pdfium::span pDest, ++ pdfium::span pSrc, ++ int pixels); ++ ++ int components() const { return m_nSrcComponents; } ++ bool IsNormal() const { return m_bNormal; } ++ ++ private: ++ IccTransform(cmsHTRANSFORM transform, ++ int srcComponents, ++ bool bIsLab, ++ bool bNormal); ++ ++ const cmsHTRANSFORM m_hTransform; ++ const int m_nSrcComponents; ++ const bool m_bLab; ++ const bool m_bNormal; ++}; ++ ++} // namespace fxcodec ++ ++#endif // CORE_FXCODEC_ICC_ICC_TRANSFORM_H_ +diff --git a/core/fxcodec/icc/iccmodule.h b/core/fxcodec/icc/iccmodule.h +deleted file mode 100644 +index a031172b3..000000000 +--- a/core/fxcodec/icc/iccmodule.h ++++ /dev/null +@@ -1,68 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXCODEC_ICC_ICCMODULE_H_ +-#define CORE_FXCODEC_ICC_ICCMODULE_H_ +- +-#include +- +-#include "core/fxcodec/fx_codec_def.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" +-#include "third_party/base/span.h" +- +-#if defined(USE_SYSTEM_LCMS2) +-#include +-#else +-#include "third_party/lcms/include/lcms2.h" +-#endif +- +-namespace fxcodec { +- +-class CLcmsCmm { +- public: +- CLcmsCmm(cmsHTRANSFORM transform, +- int srcComponents, +- bool bIsLab, +- bool bNormal); +- ~CLcmsCmm(); +- +- cmsHTRANSFORM transform() const { return m_hTransform; } +- int components() const { return m_nSrcComponents; } +- bool IsLab() const { return m_bLab; } +- bool IsNormal() const { return m_bNormal; } +- +- private: +- const cmsHTRANSFORM m_hTransform; +- const int m_nSrcComponents; +- const bool m_bLab; +- const bool m_bNormal; +-}; +- +-class IccModule { +- public: +- static std::unique_ptr CreateTransformSRGB( +- pdfium::span span); +- static void Translate(CLcmsCmm* pTransform, +- uint32_t nSrcComponents, +- const float* pSrcValues, +- float* pDestValues); +- static void TranslateScanline(CLcmsCmm* pTransform, +- uint8_t* pDest, +- const uint8_t* pSrc, +- int pixels); +- +- IccModule() = delete; +- IccModule(const IccModule&) = delete; +- IccModule& operator=(const IccModule&) = delete; +-}; +- +-} // namespace fxcodec +- +-using CLcmsCmm = fxcodec::CLcmsCmm; +-using IccModule = fxcodec::IccModule; +- +-#endif // CORE_FXCODEC_ICC_ICCMODULE_H_ +diff --git a/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp b/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp +index 8d3fc5a1a..2555ee2c4 100644 +--- a/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp ++++ b/core/fxcodec/jbig2/JBig2_ArithDecoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,8 +6,10 @@ + + #include "core/fxcodec/jbig2/JBig2_ArithDecoder.h" + ++#include ++ + #include "core/fxcodec/jbig2/JBig2_BitStream.h" +-#include "core/fxcrt/fx_memory.h" ++#include "third_party/base/check_op.h" + + namespace { + +@@ -41,13 +43,13 @@ int JBig2ArithCtx::DecodeNLPS(const JBig2ArithQe& qe) { + if (qe.bSwitch) + m_MPS = !m_MPS; + m_I = qe.NLPS; +- ASSERT(m_I < FX_ArraySize(kQeTable)); ++ DCHECK_LT(m_I, std::size(kQeTable)); + return D; + } + + int JBig2ArithCtx::DecodeNMPS(const JBig2ArithQe& qe) { + m_I = qe.NMPS; +- ASSERT(m_I < FX_ArraySize(kQeTable)); ++ DCHECK_LT(m_I, std::size(kQeTable)); + return MPS(); + } + +@@ -61,11 +63,11 @@ CJBig2_ArithDecoder::CJBig2_ArithDecoder(CJBig2_BitStream* pStream) + m_A = kDefaultAValue; + } + +-CJBig2_ArithDecoder::~CJBig2_ArithDecoder() {} ++CJBig2_ArithDecoder::~CJBig2_ArithDecoder() = default; + + int CJBig2_ArithDecoder::Decode(JBig2ArithCtx* pCX) { +- ASSERT(pCX); +- ASSERT(pCX->I() < FX_ArraySize(kQeTable)); ++ DCHECK(pCX); ++ DCHECK_LT(pCX->I(), std::size(kQeTable)); + + const JBig2ArithCtx::JBig2ArithQe& qe = kQeTable[pCX->I()]; + m_A -= qe.Qe; +diff --git a/core/fxcodec/jbig2/JBig2_ArithDecoder.h b/core/fxcodec/jbig2/JBig2_ArithDecoder.h +index 9731ac645..004d5fc1c 100644 +--- a/core/fxcodec/jbig2/JBig2_ArithDecoder.h ++++ b/core/fxcodec/jbig2/JBig2_ArithDecoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -32,7 +32,7 @@ class JBig2ArithCtx { + unsigned int I() const { return m_I; } + + private: +- bool m_MPS = 0; ++ bool m_MPS = false; + unsigned int m_I = 0; + }; + +diff --git a/core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp b/core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp +index 0d09b31fa..508810a3a 100644 +--- a/core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp ++++ b/core/fxcodec/jbig2/JBig2_ArithIntDecoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,7 +8,7 @@ + + #include + +-#include "core/fxcrt/fx_memory.h" ++#include "core/fxcrt/fx_safe_types.h" + + namespace { + +@@ -16,10 +16,12 @@ int ShiftOr(int val, int bitwise_or_val) { + return (val << 1) | bitwise_or_val; + } + +-const struct ArithIntDecodeData { ++struct ArithIntDecodeData { + int nNeedBits; + int nValue; +-} g_ArithIntDecodeData[] = { ++}; ++ ++constexpr ArithIntDecodeData kArithIntDecodeData[] = { + {2, 0}, {4, 4}, {6, 20}, {8, 84}, {12, 340}, {32, 4436}, + }; + +@@ -27,7 +29,7 @@ size_t RecursiveDecode(CJBig2_ArithDecoder* decoder, + std::vector* context, + int* prev, + size_t depth) { +- static const size_t kDepthEnd = FX_ArraySize(g_ArithIntDecodeData) - 1; ++ static const size_t kDepthEnd = std::size(kArithIntDecodeData) - 1; + if (depth == kDepthEnd) + return kDepthEnd; + +@@ -45,7 +47,7 @@ CJBig2_ArithIntDecoder::CJBig2_ArithIntDecoder() { + m_IAx.resize(512); + } + +-CJBig2_ArithIntDecoder::~CJBig2_ArithIntDecoder() {} ++CJBig2_ArithIntDecoder::~CJBig2_ArithIntDecoder() = default; + + bool CJBig2_ArithIntDecoder::Decode(CJBig2_ArithDecoder* pArithDecoder, + int* nResult) { +@@ -60,15 +62,14 @@ bool CJBig2_ArithIntDecoder::Decode(CJBig2_ArithDecoder* pArithDecoder, + RecursiveDecode(pArithDecoder, &m_IAx, &PREV, 0); + + int nTemp = 0; +- for (int i = 0; i < g_ArithIntDecodeData[nDecodeDataIndex].nNeedBits; ++i) { ++ for (int i = 0; i < kArithIntDecodeData[nDecodeDataIndex].nNeedBits; ++i) { + int D = pArithDecoder->Decode(&m_IAx[PREV]); + PREV = ShiftOr(PREV, D); + if (PREV >= 256) + PREV = (PREV & 511) | 256; + nTemp = ShiftOr(nTemp, D); + } +- pdfium::base::CheckedNumeric safeValue = +- g_ArithIntDecodeData[nDecodeDataIndex].nValue; ++ FX_SAFE_INT32 safeValue = kArithIntDecodeData[nDecodeDataIndex].nValue; + safeValue += nTemp; + + // Value does not fit in int. +@@ -90,7 +91,7 @@ CJBig2_ArithIaidDecoder::CJBig2_ArithIaidDecoder(unsigned char SBSYMCODELENA) + m_IAID.resize(static_cast(1) << SBSYMCODELEN); + } + +-CJBig2_ArithIaidDecoder::~CJBig2_ArithIaidDecoder() {} ++CJBig2_ArithIaidDecoder::~CJBig2_ArithIaidDecoder() = default; + + void CJBig2_ArithIaidDecoder::Decode(CJBig2_ArithDecoder* pArithDecoder, + uint32_t* nResult) { +diff --git a/core/fxcodec/jbig2/JBig2_ArithIntDecoder.h b/core/fxcodec/jbig2/JBig2_ArithIntDecoder.h +index 222176775..7245ffcb0 100644 +--- a/core/fxcodec/jbig2/JBig2_ArithIntDecoder.h ++++ b/core/fxcodec/jbig2/JBig2_ArithIntDecoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,11 @@ + #ifndef CORE_FXCODEC_JBIG2_JBIG2_ARITHINTDECODER_H_ + #define CORE_FXCODEC_JBIG2_JBIG2_ARITHINTDECODER_H_ + ++#include ++ + #include + + #include "core/fxcodec/jbig2/JBig2_ArithDecoder.h" +-#include "core/fxcrt/fx_system.h" + + class CJBig2_ArithIntDecoder { + public: +diff --git a/core/fxcodec/jbig2/JBig2_BitStream.cpp b/core/fxcodec/jbig2/JBig2_BitStream.cpp +index fb2a88803..5f89716e3 100644 +--- a/core/fxcodec/jbig2/JBig2_BitStream.cpp ++++ b/core/fxcodec/jbig2/JBig2_BitStream.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,6 +8,8 @@ + + #include + ++#include "third_party/base/numerics/safe_conversions.h" ++ + namespace { + + pdfium::span ValidatedSpan(pdfium::span sp) { +@@ -19,8 +21,8 @@ pdfium::span ValidatedSpan(pdfium::span sp) { + } // namespace + + CJBig2_BitStream::CJBig2_BitStream(pdfium::span pSrcStream, +- uint32_t dwObjNum) +- : m_Span(ValidatedSpan(pSrcStream)), m_dwObjNum(dwObjNum) {} ++ uint64_t key) ++ : m_Span(ValidatedSpan(pSrcStream)), m_Key(key) {} + + CJBig2_BitStream::~CJBig2_BitStream() = default; + +@@ -143,7 +145,7 @@ uint32_t CJBig2_BitStream::getOffset() const { + } + + void CJBig2_BitStream::setOffset(uint32_t dwOffset) { +- m_dwByteIdx = std::min(dwOffset, m_Span.size()); ++ m_dwByteIdx = std::min(dwOffset, getLength()); + } + + uint32_t CJBig2_BitStream::getBitPos() const { +@@ -159,6 +161,10 @@ const uint8_t* CJBig2_BitStream::getBuf() const { + return m_Span.data(); + } + ++uint32_t CJBig2_BitStream::getLength() const { ++ return pdfium::base::checked_cast(m_Span.size()); ++} ++ + const uint8_t* CJBig2_BitStream::getPointer() const { + return getBuf() + m_dwByteIdx; + } +@@ -168,7 +174,7 @@ void CJBig2_BitStream::offset(uint32_t dwOffset) { + } + + uint32_t CJBig2_BitStream::getByteLeft() const { +- return m_Span.size() - m_dwByteIdx; ++ return getLength() - m_dwByteIdx; + } + + void CJBig2_BitStream::AdvanceBit() { +@@ -181,13 +187,9 @@ void CJBig2_BitStream::AdvanceBit() { + } + + bool CJBig2_BitStream::IsInBounds() const { +- return m_dwByteIdx < m_Span.size(); ++ return m_dwByteIdx < getLength(); + } + + uint32_t CJBig2_BitStream::LengthInBits() const { +- return m_Span.size() << 3; +-} +- +-uint32_t CJBig2_BitStream::getObjNum() const { +- return m_dwObjNum; ++ return getLength() << 3; + } +diff --git a/core/fxcodec/jbig2/JBig2_BitStream.h b/core/fxcodec/jbig2/JBig2_BitStream.h +index 8032090cb..b33f54861 100644 +--- a/core/fxcodec/jbig2/JBig2_BitStream.h ++++ b/core/fxcodec/jbig2/JBig2_BitStream.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,7 +12,7 @@ + + class CJBig2_BitStream { + public: +- CJBig2_BitStream(pdfium::span pSrcStream, uint32_t dwObjNum); ++ CJBig2_BitStream(pdfium::span pSrcStream, uint64_t key); + CJBig2_BitStream(const CJBig2_BitStream&) = delete; + CJBig2_BitStream& operator=(const CJBig2_BitStream&) = delete; + ~CJBig2_BitStream(); +@@ -35,11 +35,11 @@ class CJBig2_BitStream { + uint32_t getBitPos() const; + void setBitPos(uint32_t dwBitPos); + const uint8_t* getBuf() const; +- uint32_t getLength() const { return m_Span.size(); } ++ uint32_t getLength() const; + const uint8_t* getPointer() const; + void offset(uint32_t dwOffset); + uint32_t getByteLeft() const; +- uint32_t getObjNum() const; ++ uint64_t getKey() const { return m_Key; } + bool IsInBounds() const; + + private: +@@ -49,7 +49,7 @@ class CJBig2_BitStream { + const pdfium::span m_Span; + uint32_t m_dwByteIdx = 0; + uint32_t m_dwBitIdx = 0; +- const uint32_t m_dwObjNum; ++ const uint64_t m_Key; + }; + + #endif // CORE_FXCODEC_JBIG2_JBIG2_BITSTREAM_H_ +diff --git a/core/fxcodec/jbig2/JBig2_BitStream_unittest.cpp b/core/fxcodec/jbig2/JBig2_BitStream_unittest.cpp +index 866948937..8e207b0a5 100644 +--- a/core/fxcodec/jbig2/JBig2_BitStream_unittest.cpp ++++ b/core/fxcodec/jbig2/JBig2_BitStream_unittest.cpp +@@ -1,14 +1,10 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcodec/jbig2/JBig2_BitStream.h" + +-#include +-#include +- + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + + TEST(JBig2_BitStream, ReadNBits) { + const uint8_t kData[] = {0xb1}; // 10110001 +diff --git a/core/fxcodec/jbig2/JBig2_Context.cpp b/core/fxcodec/jbig2/JBig2_Context.cpp +index d9c9ee81e..31b6f7204 100644 +--- a/core/fxcodec/jbig2/JBig2_Context.cpp ++++ b/core/fxcodec/jbig2/JBig2_Context.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -25,6 +25,7 @@ + #include "core/fxcrt/fx_memory_wrappers.h" + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxcrt/pauseindicator_iface.h" ++#include "third_party/base/check.h" + #include "third_party/base/ptr_util.h" + + namespace { +@@ -53,29 +54,29 @@ static_assert(kSymbolDictCacheMaxSize > 0, + // static + std::unique_ptr CJBig2_Context::Create( + pdfium::span pGlobalSpan, +- uint32_t dwGlobalObjNum, ++ uint64_t global_key, + pdfium::span pSrcSpan, +- uint32_t dwSrcObjNum, ++ uint64_t src_key, + std::list* pSymbolDictCache) { + auto result = pdfium::WrapUnique( +- new CJBig2_Context(pSrcSpan, dwSrcObjNum, pSymbolDictCache, false)); ++ new CJBig2_Context(pSrcSpan, src_key, pSymbolDictCache, false)); + if (!pGlobalSpan.empty()) { +- result->m_pGlobalContext = pdfium::WrapUnique(new CJBig2_Context( +- pGlobalSpan, dwGlobalObjNum, pSymbolDictCache, true)); ++ result->m_pGlobalContext = pdfium::WrapUnique( ++ new CJBig2_Context(pGlobalSpan, global_key, pSymbolDictCache, true)); + } + return result; + } + + CJBig2_Context::CJBig2_Context(pdfium::span pSrcSpan, +- uint32_t dwObjNum, ++ uint64_t src_key, + std::list* pSymbolDictCache, + bool bIsGlobal) +- : m_pStream(pdfium::MakeUnique(pSrcSpan, dwObjNum)), ++ : m_pStream(std::make_unique(pSrcSpan, src_key)), + m_HuffmanTables(CJBig2_HuffmanTable::kNumHuffmanTables), + m_bIsGlobal(bIsGlobal), + m_pSymbolDictCache(pSymbolDictCache) {} + +-CJBig2_Context::~CJBig2_Context() {} ++CJBig2_Context::~CJBig2_Context() = default; + + JBig2_Result CJBig2_Context::DecodeSequential(PauseIndicatorIface* pPause) { + if (m_pStream->getByteLeft() <= 0) +@@ -84,17 +85,16 @@ JBig2_Result CJBig2_Context::DecodeSequential(PauseIndicatorIface* pPause) { + while (m_pStream->getByteLeft() >= JBIG2_MIN_SEGMENT_SIZE) { + JBig2_Result nRet; + if (!m_pSegment) { +- m_pSegment = pdfium::MakeUnique(); ++ m_pSegment = std::make_unique(); + nRet = ParseSegmentHeader(m_pSegment.get()); + if (nRet != JBig2_Result::kSuccess) { + m_pSegment.reset(); + return nRet; + } +- m_dwOffset = m_pStream->getOffset(); ++ m_nOffset = m_pStream->getOffset(); + } + nRet = ParseSegmentData(m_pSegment.get(), pPause); +- if (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE) { +- m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; ++ if (m_ProcessingStatus == FXCODEC_STATUS::kDecodeToBeContinued) { + m_PauseStep = 2; + return JBig2_Result::kSuccess; + } +@@ -107,18 +107,19 @@ JBig2_Result CJBig2_Context::DecodeSequential(PauseIndicatorIface* pPause) { + return nRet; + } + if (m_pSegment->m_dwData_length != 0xffffffff) { +- m_dwOffset += m_pSegment->m_dwData_length; +- if (!m_dwOffset.IsValid()) ++ FX_SAFE_UINT32 new_offset = m_nOffset; ++ new_offset += m_pSegment->m_dwData_length; ++ if (!new_offset.IsValid()) + return JBig2_Result::kFailure; +- +- m_pStream->setOffset(m_dwOffset.ValueOrDie()); ++ m_nOffset = new_offset.ValueOrDie(); ++ m_pStream->setOffset(m_nOffset); + } else { + m_pStream->offset(4); + } + m_SegmentList.push_back(std::move(m_pSegment)); + if (m_pStream->getByteLeft() > 0 && m_pPage && pPause && + pPause->NeedToPauseNow()) { +- m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; ++ m_ProcessingStatus = FXCODEC_STATUS::kDecodeToBeContinued; + m_PauseStep = 2; + return JBig2_Result::kSuccess; + } +@@ -126,7 +127,7 @@ JBig2_Result CJBig2_Context::DecodeSequential(PauseIndicatorIface* pPause) { + return JBig2_Result::kSuccess; + } + +-bool CJBig2_Context::GetFirstPage(uint8_t* pBuf, ++bool CJBig2_Context::GetFirstPage(pdfium::span pBuf, + int32_t width, + int32_t height, + int32_t stride, +@@ -134,42 +135,42 @@ bool CJBig2_Context::GetFirstPage(uint8_t* pBuf, + if (m_pGlobalContext) { + JBig2_Result nRet = m_pGlobalContext->DecodeSequential(pPause); + if (nRet != JBig2_Result::kSuccess) { +- m_ProcessingStatus = FXCODEC_STATUS_ERROR; ++ m_ProcessingStatus = FXCODEC_STATUS::kError; + return nRet == JBig2_Result::kSuccess; + } + } + m_PauseStep = 0; +- m_pPage = pdfium::MakeUnique(width, height, stride, pBuf); ++ m_pPage = std::make_unique(width, height, stride, pBuf); + m_bBufSpecified = true; + if (pPause && pPause->NeedToPauseNow()) { + m_PauseStep = 1; +- m_ProcessingStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; ++ m_ProcessingStatus = FXCODEC_STATUS::kDecodeToBeContinued; + return true; + } + return Continue(pPause); + } + + bool CJBig2_Context::Continue(PauseIndicatorIface* pPause) { +- m_ProcessingStatus = FXCODEC_STATUS_DECODE_READY; ++ m_ProcessingStatus = FXCODEC_STATUS::kDecodeReady; + JBig2_Result nRet = JBig2_Result::kSuccess; + if (m_PauseStep == 5) { +- m_ProcessingStatus = FXCODEC_STATUS_DECODE_FINISH; ++ m_ProcessingStatus = FXCODEC_STATUS::kDecodeFinished; + return true; + } + + if (m_PauseStep <= 2) + nRet = DecodeSequential(pPause); +- if (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE) ++ if (m_ProcessingStatus == FXCODEC_STATUS::kDecodeToBeContinued) + return nRet == JBig2_Result::kSuccess; + + m_PauseStep = 5; + if (!m_bBufSpecified && nRet == JBig2_Result::kSuccess) { +- m_ProcessingStatus = FXCODEC_STATUS_DECODE_FINISH; ++ m_ProcessingStatus = FXCODEC_STATUS::kDecodeFinished; + return true; + } + m_ProcessingStatus = nRet == JBig2_Result::kSuccess +- ? FXCODEC_STATUS_DECODE_FINISH +- : FXCODEC_STATUS_ERROR; ++ ? FXCODEC_STATUS::kDecodeFinished ++ : FXCODEC_STATUS::kError; + return nRet == JBig2_Result::kSuccess; + } + +@@ -217,7 +218,7 @@ JBig2_Result CJBig2_Context::ParseSegmentHeader(CJBig2_Segment* pSegment) { + } + pSegment->m_nReferred_to_segment_count &= 0x1fffffff; + if (pSegment->m_nReferred_to_segment_count > +- JBIG2_MAX_REFERRED_SEGMENT_COUNT) { ++ kJBig2MaxReferredSegmentCount) { + return JBig2_Result::kFailure; + } + } else { +@@ -269,7 +270,7 @@ JBig2_Result CJBig2_Context::ParseSegmentHeader(CJBig2_Segment* pSegment) { + if (m_pStream->readInteger(&pSegment->m_dwData_length) != 0) + return JBig2_Result::kFailure; + +- pSegment->m_dwObjNum = m_pStream->getObjNum(); ++ pSegment->m_Key = m_pStream->getKey(); + pSegment->m_dwDataOffset = m_pStream->getOffset(); + pSegment->m_State = JBIG2_SEGMENT_DATA_UNPARSED; + return JBig2_Result::kSuccess; +@@ -278,7 +279,7 @@ JBig2_Result CJBig2_Context::ParseSegmentHeader(CJBig2_Segment* pSegment) { + JBig2_Result CJBig2_Context::ParseSegmentData(CJBig2_Segment* pSegment, + PauseIndicatorIface* pPause) { + JBig2_Result ret = ProcessingParseSegmentData(pSegment, pPause); +- while (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE && ++ while (m_ProcessingStatus == FXCODEC_STATUS::kDecodeToBeContinued && + m_pStream->getByteLeft() > 0) { + ret = ProcessingParseSegmentData(pSegment, pPause); + } +@@ -318,18 +319,21 @@ JBig2_Result CJBig2_Context::ProcessingParseSegmentData( + return JBig2_Result::kFailure; + return ParseGenericRefinementRegion(pSegment); + case 48: { +- uint16_t wTemp; +- auto pPageInfo = pdfium::MakeUnique(); ++ uint8_t segment_flags; ++ uint16_t striping_info; ++ auto pPageInfo = std::make_unique(); + if (m_pStream->readInteger(&pPageInfo->m_dwWidth) != 0 || + m_pStream->readInteger(&pPageInfo->m_dwHeight) != 0 || + m_pStream->readInteger(&pPageInfo->m_dwResolutionX) != 0 || + m_pStream->readInteger(&pPageInfo->m_dwResolutionY) != 0 || +- m_pStream->read1Byte(&pPageInfo->m_cFlags) != 0 || +- m_pStream->readShortInteger(&wTemp) != 0) { ++ m_pStream->read1Byte(&segment_flags) != 0 || ++ m_pStream->readShortInteger(&striping_info) != 0) { + return JBig2_Result::kFailure; + } +- pPageInfo->m_bIsStriped = !!(wTemp & 0x8000); +- pPageInfo->m_wMaxStripeSize = wTemp & 0x7fff; ++ ++ pPageInfo->m_bDefaultPixelValue = !!(segment_flags & 4); ++ pPageInfo->m_bIsStriped = !!(striping_info & 0x8000); ++ pPageInfo->m_wMaxStripeSize = striping_info & 0x7fff; + bool bMaxHeight = (pPageInfo->m_dwHeight == 0xffffffff); + if (bMaxHeight && !pPageInfo->m_bIsStriped) + pPageInfo->m_bIsStriped = true; +@@ -337,23 +341,22 @@ JBig2_Result CJBig2_Context::ProcessingParseSegmentData( + if (!m_bBufSpecified) { + uint32_t height = + bMaxHeight ? pPageInfo->m_wMaxStripeSize : pPageInfo->m_dwHeight; +- m_pPage = +- pdfium::MakeUnique(pPageInfo->m_dwWidth, height); ++ m_pPage = std::make_unique(pPageInfo->m_dwWidth, height); + } + + if (!m_pPage->data()) { +- m_ProcessingStatus = FXCODEC_STATUS_ERROR; ++ m_ProcessingStatus = FXCODEC_STATUS::kError; + return JBig2_Result::kFailure; + } + +- m_pPage->Fill((pPageInfo->m_cFlags & 4) ? 1 : 0); ++ m_pPage->Fill(pPageInfo->m_bDefaultPixelValue); + m_PageInfoList.push_back(std::move(pPageInfo)); + m_bInPage = true; +- } break; ++ break; ++ } + case 49: + m_bInPage = false; + return JBig2_Result::kEndReached; +- break; + case 50: + m_pStream->offset(pSegment->m_dwData_length); + break; +@@ -378,19 +381,19 @@ JBig2_Result CJBig2_Context::ParseSymbolDict(CJBig2_Segment* pSegment) { + if (m_pStream->readShortInteger(&wFlags) != 0) + return JBig2_Result::kFailure; + +- auto pSymbolDictDecoder = pdfium::MakeUnique(); ++ auto pSymbolDictDecoder = std::make_unique(); + pSymbolDictDecoder->SDHUFF = wFlags & 0x0001; + pSymbolDictDecoder->SDREFAGG = (wFlags >> 1) & 0x0001; + pSymbolDictDecoder->SDTEMPLATE = (wFlags >> 10) & 0x0003; + pSymbolDictDecoder->SDRTEMPLATE = !!((wFlags >> 12) & 0x0003); +- if (pSymbolDictDecoder->SDHUFF == 0) { ++ if (!pSymbolDictDecoder->SDHUFF) { + const uint32_t dwTemp = (pSymbolDictDecoder->SDTEMPLATE == 0) ? 8 : 2; + for (uint32_t i = 0; i < dwTemp; ++i) { + if (m_pStream->read1Byte((uint8_t*)&pSymbolDictDecoder->SDAT[i]) != 0) + return JBig2_Result::kFailure; + } + } +- if (pSymbolDictDecoder->SDREFAGG == 1 && !pSymbolDictDecoder->SDRTEMPLATE) { ++ if (pSymbolDictDecoder->SDREFAGG && !pSymbolDictDecoder->SDRTEMPLATE) { + for (int32_t i = 0; i < 4; ++i) { + if (m_pStream->read1Byte((uint8_t*)&pSymbolDictDecoder->SDRAT[i]) != 0) + return JBig2_Result::kFailure; +@@ -400,8 +403,8 @@ JBig2_Result CJBig2_Context::ParseSymbolDict(CJBig2_Segment* pSegment) { + m_pStream->readInteger(&pSymbolDictDecoder->SDNUMNEWSYMS) != 0) { + return JBig2_Result::kFailure; + } +- if (pSymbolDictDecoder->SDNUMEXSYMS > JBIG2_MAX_EXPORT_SYSMBOLS || +- pSymbolDictDecoder->SDNUMNEWSYMS > JBIG2_MAX_NEW_SYSMBOLS) { ++ if (pSymbolDictDecoder->SDNUMEXSYMS > kJBig2MaxExportSymbols || ++ pSymbolDictDecoder->SDNUMNEWSYMS > kJBig2MaxNewSymbols) { + return JBig2_Result::kFailure; + } + for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { +@@ -409,28 +412,31 @@ JBig2_Result CJBig2_Context::ParseSymbolDict(CJBig2_Segment* pSegment) { + return JBig2_Result::kFailure; + } + CJBig2_Segment* pLRSeg = nullptr; +- pSymbolDictDecoder->SDNUMINSYMS = 0; ++ FX_SAFE_UINT32 dwNumSyms = 0; + for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { + CJBig2_Segment* pSeg = + FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]); + if (pSeg->m_cFlags.s.type == 0) { +- pSymbolDictDecoder->SDNUMINSYMS += pSeg->m_SymbolDict->NumImages(); ++ dwNumSyms += pSeg->m_SymbolDict->NumImages(); + pLRSeg = pSeg; + } + } ++ pSymbolDictDecoder->SDNUMINSYMS = dwNumSyms.ValueOrDie(); + + std::unique_ptr SDINSYMS; + if (pSymbolDictDecoder->SDNUMINSYMS != 0) { + SDINSYMS.reset(FX_Alloc(CJBig2_Image*, pSymbolDictDecoder->SDNUMINSYMS)); +- uint32_t dwTemp = 0; ++ dwNumSyms = 0; + for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { + CJBig2_Segment* pSeg = + FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]); + if (pSeg->m_cFlags.s.type == 0) { + const CJBig2_SymbolDict& dict = *pSeg->m_SymbolDict; +- for (size_t j = 0; j < dict.NumImages(); ++j) +- SDINSYMS.get()[dwTemp + j] = dict.GetImage(j); +- dwTemp += dict.NumImages(); ++ for (uint32_t j = 0; j < dict.NumImages(); ++j) { ++ uint32_t dwTemp = (dwNumSyms + j).ValueOrDie(); ++ SDINSYMS.get()[dwTemp] = dict.GetImage(j); ++ } ++ dwNumSyms += dict.NumImages(); + } + } + } +@@ -438,7 +444,7 @@ JBig2_Result CJBig2_Context::ParseSymbolDict(CJBig2_Segment* pSegment) { + + uint8_t cSDHUFFDH = (wFlags >> 2) & 0x0003; + uint8_t cSDHUFFDW = (wFlags >> 4) & 0x0003; +- if (pSymbolDictDecoder->SDHUFF == 1) { ++ if (pSymbolDictDecoder->SDHUFF) { + if (cSDHUFFDH == 2 || cSDHUFFDW == 2) + return JBig2_Result::kFailure; + +@@ -475,7 +481,7 @@ JBig2_Result CJBig2_Context::ParseSymbolDict(CJBig2_Segment* pSegment) { + return JBig2_Result::kFailure; + pSymbolDictDecoder->SDHUFFBMSIZE = pSeg->m_HuffmanTable.get(); + } +- if (pSymbolDictDecoder->SDREFAGG == 1) { ++ if (pSymbolDictDecoder->SDREFAGG) { + uint8_t cSDHUFFAGGINST = (wFlags >> 7) & 0x0001; + if (cSDHUFFAGGINST == 0) { + pSymbolDictDecoder->SDHUFFAGGINST = GetHuffmanTable(1); +@@ -489,8 +495,8 @@ JBig2_Result CJBig2_Context::ParseSymbolDict(CJBig2_Segment* pSegment) { + } + } + +- const bool bUseGbContext = (pSymbolDictDecoder->SDHUFF == 0); +- const bool bUseGrContext = (pSymbolDictDecoder->SDREFAGG == 1); ++ const bool bUseGbContext = !pSymbolDictDecoder->SDHUFF; ++ const bool bUseGrContext = pSymbolDictDecoder->SDREFAGG; + const size_t gbContextSize = + GetHuffContextSize(pSymbolDictDecoder->SDTEMPLATE); + const size_t grContextSize = +@@ -515,8 +521,7 @@ JBig2_Result CJBig2_Context::ParseSymbolDict(CJBig2_Segment* pSegment) { + grContext.resize(grContextSize); + } + +- CJBig2_CacheKey key = +- CJBig2_CacheKey(pSegment->m_dwObjNum, pSegment->m_dwDataOffset); ++ CJBig2_CompoundKey key(pSegment->m_Key, pSegment->m_dwDataOffset); + bool cache_hit = false; + pSegment->m_nResultType = JBIG2_SYMBOL_DICT_POINTER; + if (m_bIsGlobal && key.first != 0) { +@@ -535,7 +540,7 @@ JBig2_Result CJBig2_Context::ParseSymbolDict(CJBig2_Segment* pSegment) { + if (!cache_hit) { + if (bUseGbContext) { + auto pArithDecoder = +- pdfium::MakeUnique(m_pStream.get()); ++ std::make_unique(m_pStream.get()); + pSegment->m_SymbolDict = pSymbolDictDecoder->DecodeArith( + pArithDecoder.get(), &gbContext, &grContext); + if (!pSegment->m_SymbolDict) +@@ -580,7 +585,7 @@ JBig2_Result CJBig2_Context::ParseTextRegion(CJBig2_Segment* pSegment) { + if (!CJBig2_Image::IsValidImageSize(ri.width, ri.height)) + return JBig2_Result::kFailure; + +- auto pTRD = pdfium::MakeUnique(); ++ auto pTRD = std::make_unique(); + pTRD->SBW = ri.width; + pTRD->SBH = ri.height; + pTRD->SBHUFF = wFlags & 0x0001; +@@ -597,10 +602,10 @@ JBig2_Result CJBig2_Context::ParseTextRegion(CJBig2_Segment* pSegment) { + } + pTRD->SBRTEMPLATE = !!((wFlags >> 15) & 0x0001); + +- if (pTRD->SBHUFF == 1 && m_pStream->readShortInteger(&wFlags) != 0) { ++ if (pTRD->SBHUFF && m_pStream->readShortInteger(&wFlags) != 0) { + return JBig2_Result::kFailure; + } +- if (pTRD->SBREFINE == 1 && !pTRD->SBRTEMPLATE) { ++ if (pTRD->SBREFINE && !pTRD->SBRTEMPLATE) { + for (int32_t i = 0; i < 4; ++i) { + if (m_pStream->read1Byte((uint8_t*)&pTRD->SBRAT[i]) != 0) + return JBig2_Result::kFailure; +@@ -624,27 +629,30 @@ JBig2_Result CJBig2_Context::ParseTextRegion(CJBig2_Segment* pSegment) { + return JBig2_Result::kFailure; + } + +- pTRD->SBNUMSYMS = 0; ++ FX_SAFE_UINT32 dwNumSyms = 0; + for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { + CJBig2_Segment* pSeg = + FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]); + if (pSeg->m_cFlags.s.type == 0) { +- pTRD->SBNUMSYMS += pSeg->m_SymbolDict->NumImages(); ++ dwNumSyms += pSeg->m_SymbolDict->NumImages(); + } + } ++ pTRD->SBNUMSYMS = dwNumSyms.ValueOrDie(); + + std::unique_ptr SBSYMS; + if (pTRD->SBNUMSYMS > 0) { + SBSYMS.reset(FX_Alloc(CJBig2_Image*, pTRD->SBNUMSYMS)); +- dwTemp = 0; ++ dwNumSyms = 0; + for (int32_t i = 0; i < pSegment->m_nReferred_to_segment_count; ++i) { + CJBig2_Segment* pSeg = + FindSegmentByNumber(pSegment->m_Referred_to_segment_numbers[i]); + if (pSeg->m_cFlags.s.type == 0) { + const CJBig2_SymbolDict& dict = *pSeg->m_SymbolDict; +- for (size_t j = 0; j < dict.NumImages(); ++j) +- SBSYMS.get()[dwTemp + j] = dict.GetImage(j); +- dwTemp += dict.NumImages(); ++ for (uint32_t j = 0; j < dict.NumImages(); ++j) { ++ uint32_t dwIndex = (dwNumSyms + j).ValueOrDie(); ++ SBSYMS.get()[dwIndex] = dict.GetImage(j); ++ } ++ dwNumSyms += dict.NumImages(); + } + } + pTRD->SBSYMS = SBSYMS.get(); +@@ -652,7 +660,7 @@ JBig2_Result CJBig2_Context::ParseTextRegion(CJBig2_Segment* pSegment) { + pTRD->SBSYMS = nullptr; + } + +- if (pTRD->SBHUFF == 1) { ++ if (pTRD->SBHUFF) { + std::vector SBSYMCODES = + DecodeSymbolIDHuffmanTable(pTRD->SBNUMSYMS); + if (SBSYMCODES.empty()) +@@ -668,7 +676,7 @@ JBig2_Result CJBig2_Context::ParseTextRegion(CJBig2_Segment* pSegment) { + pTRD->SBSYMCODELEN = (uint8_t)dwTemp; + } + +- if (pTRD->SBHUFF == 1) { ++ if (pTRD->SBHUFF) { + uint8_t cSBHUFFFS = wFlags & 0x0003; + uint8_t cSBHUFFDS = (wFlags >> 2) & 0x0003; + uint8_t cSBHUFFDT = (wFlags >> 4) & 0x0003; +@@ -774,34 +782,30 @@ JBig2_Result CJBig2_Context::ParseTextRegion(CJBig2_Segment* pSegment) { + } + } + std::unique_ptr grContext; +- if (pTRD->SBREFINE == 1) { ++ if (pTRD->SBREFINE) { + const size_t size = GetRefAggContextSize(pTRD->SBRTEMPLATE); + grContext.reset(FX_Alloc(JBig2ArithCtx, size)); + } +- if (pTRD->SBHUFF == 0) { +- auto pArithDecoder = +- pdfium::MakeUnique(m_pStream.get()); +- pSegment->m_nResultType = JBIG2_IMAGE_POINTER; +- pSegment->m_Image = +- pTRD->DecodeArith(pArithDecoder.get(), grContext.get(), nullptr); ++ pSegment->m_nResultType = JBIG2_IMAGE_POINTER; ++ if (pTRD->SBHUFF) { ++ pSegment->m_Image = pTRD->DecodeHuffman(m_pStream.get(), grContext.get()); + if (!pSegment->m_Image) + return JBig2_Result::kFailure; + m_pStream->alignByte(); +- m_pStream->offset(2); + } else { +- pSegment->m_nResultType = JBIG2_IMAGE_POINTER; +- pSegment->m_Image = pTRD->DecodeHuffman(m_pStream.get(), grContext.get()); ++ auto pArithDecoder = std::make_unique(m_pStream.get()); ++ pSegment->m_Image = ++ pTRD->DecodeArith(pArithDecoder.get(), grContext.get(), nullptr); + if (!pSegment->m_Image) + return JBig2_Result::kFailure; + m_pStream->alignByte(); ++ m_pStream->offset(2); + } + if (pSegment->m_cFlags.s.type != 4) { + if (!m_bBufSpecified) { + const auto& pPageInfo = m_PageInfoList.back(); +- if ((pPageInfo->m_bIsStriped == 1) && +- (ri.y + ri.height > m_pPage->height())) { +- m_pPage->Expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0); +- } ++ if (pPageInfo->m_bIsStriped && ri.y + ri.height > m_pPage->height()) ++ m_pPage->Expand(ri.y + ri.height, pPageInfo->m_bDefaultPixelValue); + } + m_pPage->ComposeFrom(ri.x, ri.y, pSegment->m_Image.get(), + (JBig2ComposeOp)(ri.flags & 0x03)); +@@ -813,25 +817,29 @@ JBig2_Result CJBig2_Context::ParseTextRegion(CJBig2_Segment* pSegment) { + JBig2_Result CJBig2_Context::ParsePatternDict(CJBig2_Segment* pSegment, + PauseIndicatorIface* pPause) { + uint8_t cFlags; +- auto pPDD = pdfium::MakeUnique(); ++ auto pPDD = std::make_unique(); + if (m_pStream->read1Byte(&cFlags) != 0 || + m_pStream->read1Byte(&pPDD->HDPW) != 0 || + m_pStream->read1Byte(&pPDD->HDPH) != 0 || + m_pStream->readInteger(&pPDD->GRAYMAX) != 0) { + return JBig2_Result::kFailure; + } +- if (pPDD->GRAYMAX > JBIG2_MAX_PATTERN_INDEX) ++ if (pPDD->GRAYMAX > kJBig2MaxPatternIndex) + return JBig2_Result::kFailure; + + pPDD->HDMMR = cFlags & 0x01; + pPDD->HDTEMPLATE = (cFlags >> 1) & 0x03; + pSegment->m_nResultType = JBIG2_PATTERN_DICT_POINTER; +- if (pPDD->HDMMR == 0) { ++ if (pPDD->HDMMR) { ++ pSegment->m_PatternDict = pPDD->DecodeMMR(m_pStream.get()); ++ if (!pSegment->m_PatternDict) ++ return JBig2_Result::kFailure; ++ m_pStream->alignByte(); ++ } else { + const size_t size = GetHuffContextSize(pPDD->HDTEMPLATE); + std::unique_ptr gbContext( + FX_Alloc(JBig2ArithCtx, size)); +- auto pArithDecoder = +- pdfium::MakeUnique(m_pStream.get()); ++ auto pArithDecoder = std::make_unique(m_pStream.get()); + pSegment->m_PatternDict = + pPDD->DecodeArith(pArithDecoder.get(), gbContext.get(), pPause); + if (!pSegment->m_PatternDict) +@@ -839,11 +847,6 @@ JBig2_Result CJBig2_Context::ParsePatternDict(CJBig2_Segment* pSegment, + + m_pStream->alignByte(); + m_pStream->offset(2); +- } else { +- pSegment->m_PatternDict = pPDD->DecodeMMR(m_pStream.get()); +- if (!pSegment->m_PatternDict) +- return JBig2_Result::kFailure; +- m_pStream->alignByte(); + } + return JBig2_Result::kSuccess; + } +@@ -852,7 +855,7 @@ JBig2_Result CJBig2_Context::ParseHalftoneRegion(CJBig2_Segment* pSegment, + PauseIndicatorIface* pPause) { + uint8_t cFlags; + JBig2RegionInfo ri; +- auto pHRD = pdfium::MakeUnique(); ++ auto pHRD = std::make_unique(); + if (ParseRegionInfo(&ri) != JBig2_Result::kSuccess || + m_pStream->read1Byte(&cFlags) != 0 || + m_pStream->readInteger(&pHRD->HGW) != 0 || +@@ -894,12 +897,16 @@ JBig2_Result CJBig2_Context::ParseHalftoneRegion(CJBig2_Segment* pSegment, + pHRD->HPW = pPatternDict->HDPATS[0]->width(); + pHRD->HPH = pPatternDict->HDPATS[0]->height(); + pSegment->m_nResultType = JBIG2_IMAGE_POINTER; +- if (pHRD->HMMR == 0) { ++ if (pHRD->HMMR) { ++ pSegment->m_Image = pHRD->DecodeMMR(m_pStream.get()); ++ if (!pSegment->m_Image) ++ return JBig2_Result::kFailure; ++ m_pStream->alignByte(); ++ } else { + const size_t size = GetHuffContextSize(pHRD->HTEMPLATE); + std::unique_ptr gbContext( + FX_Alloc(JBig2ArithCtx, size)); +- auto pArithDecoder = +- pdfium::MakeUnique(m_pStream.get()); ++ auto pArithDecoder = std::make_unique(m_pStream.get()); + pSegment->m_Image = + pHRD->DecodeArith(pArithDecoder.get(), gbContext.get(), pPause); + if (!pSegment->m_Image) +@@ -907,19 +914,12 @@ JBig2_Result CJBig2_Context::ParseHalftoneRegion(CJBig2_Segment* pSegment, + + m_pStream->alignByte(); + m_pStream->offset(2); +- } else { +- pSegment->m_Image = pHRD->DecodeMMR(m_pStream.get()); +- if (!pSegment->m_Image) +- return JBig2_Result::kFailure; +- m_pStream->alignByte(); + } + if (pSegment->m_cFlags.s.type != 20) { + if (!m_bBufSpecified) { + const auto& pPageInfo = m_PageInfoList.back(); +- if (pPageInfo->m_bIsStriped == 1 && +- ri.y + ri.height > m_pPage->height()) { +- m_pPage->Expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0); +- } ++ if (pPageInfo->m_bIsStriped && ri.y + ri.height > m_pPage->height()) ++ m_pPage->Expand(ri.y + ri.height, pPageInfo->m_bDefaultPixelValue); + } + m_pPage->ComposeFrom(ri.x, ri.y, pSegment->m_Image.get(), + (JBig2ComposeOp)(ri.flags & 0x03)); +@@ -931,7 +931,7 @@ JBig2_Result CJBig2_Context::ParseHalftoneRegion(CJBig2_Segment* pSegment, + JBig2_Result CJBig2_Context::ParseGenericRegion(CJBig2_Segment* pSegment, + PauseIndicatorIface* pPause) { + if (!m_pGRD) { +- auto pGRD = pdfium::MakeUnique(); ++ auto pGRD = std::make_unique(); + uint8_t cFlags; + if (ParseRegionInfo(&m_ri) != JBig2_Result::kSuccess || + m_pStream->read1Byte(&cFlags) != 0) { +@@ -944,7 +944,7 @@ JBig2_Result CJBig2_Context::ParseGenericRegion(CJBig2_Segment* pSegment, + pGRD->MMR = cFlags & 0x01; + pGRD->GBTEMPLATE = (cFlags >> 1) & 0x03; + pGRD->TPGDON = (cFlags >> 3) & 0x01; +- if (pGRD->MMR == 0) { ++ if (!pGRD->MMR) { + if (pGRD->GBTEMPLATE == 0) { + for (int32_t i = 0; i < 8; ++i) { + if (m_pStream->read1Byte((uint8_t*)&pGRD->GBAT[i]) != 0) +@@ -957,18 +957,24 @@ JBig2_Result CJBig2_Context::ParseGenericRegion(CJBig2_Segment* pSegment, + } + } + } +- pGRD->USESKIP = 0; ++ pGRD->USESKIP = false; + m_pGRD = std::move(pGRD); + } + pSegment->m_nResultType = JBIG2_IMAGE_POINTER; +- if (m_pGRD->MMR == 0) { ++ if (m_pGRD->MMR) { ++ m_pGRD->StartDecodeMMR(&pSegment->m_Image, m_pStream.get()); ++ if (!pSegment->m_Image) { ++ m_pGRD.reset(); ++ return JBig2_Result::kFailure; ++ } ++ m_pStream->alignByte(); ++ } else { + if (m_gbContext.empty()) + m_gbContext.resize(GetHuffContextSize(m_pGRD->GBTEMPLATE)); + + bool bStart = !m_pArithDecoder; + if (bStart) { +- m_pArithDecoder = +- pdfium::MakeUnique(m_pStream.get()); ++ m_pArithDecoder = std::make_unique(m_pStream.get()); + } + { + // |state.gbContext| can't exist when m_gbContext.clear() called below. +@@ -979,14 +985,14 @@ JBig2_Result CJBig2_Context::ParseGenericRegion(CJBig2_Segment* pSegment, + state.pPause = pPause; + m_ProcessingStatus = bStart ? m_pGRD->StartDecodeArith(&state) + : m_pGRD->ContinueDecode(&state); +- if (m_ProcessingStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE) { ++ if (m_ProcessingStatus == FXCODEC_STATUS::kDecodeToBeContinued) { + if (pSegment->m_cFlags.s.type != 36) { + if (!m_bBufSpecified) { + const auto& pPageInfo = m_PageInfoList.back(); +- if ((pPageInfo->m_bIsStriped == 1) && +- (m_ri.y + m_ri.height > m_pPage->height())) { ++ if (pPageInfo->m_bIsStriped && ++ m_ri.y + m_ri.height > m_pPage->height()) { + m_pPage->Expand(m_ri.y + m_ri.height, +- (pPageInfo->m_cFlags & 4) ? 1 : 0); ++ pPageInfo->m_bDefaultPixelValue); + } + } + const FX_RECT& rect = m_pGRD->GetReplaceRect(); +@@ -1000,28 +1006,18 @@ JBig2_Result CJBig2_Context::ParseGenericRegion(CJBig2_Segment* pSegment, + m_pArithDecoder.reset(); + m_gbContext.clear(); + if (!pSegment->m_Image) { +- m_ProcessingStatus = FXCODEC_STATUS_ERROR; ++ m_ProcessingStatus = FXCODEC_STATUS::kError; + m_pGRD.reset(); + return JBig2_Result::kFailure; + } + m_pStream->alignByte(); + m_pStream->offset(2); +- } else { +- m_pGRD->StartDecodeMMR(&pSegment->m_Image, m_pStream.get()); +- if (!pSegment->m_Image) { +- m_pGRD.reset(); +- return JBig2_Result::kFailure; +- } +- m_pStream->alignByte(); + } + if (pSegment->m_cFlags.s.type != 36) { + if (!m_bBufSpecified) { + JBig2PageInfo* pPageInfo = m_PageInfoList.back().get(); +- if ((pPageInfo->m_bIsStriped == 1) && +- (m_ri.y + m_ri.height > m_pPage->height())) { +- m_pPage->Expand(m_ri.y + m_ri.height, +- (pPageInfo->m_cFlags & 4) ? 1 : 0); +- } ++ if (pPageInfo->m_bIsStriped && m_ri.y + m_ri.height > m_pPage->height()) ++ m_pPage->Expand(m_ri.y + m_ri.height, pPageInfo->m_bDefaultPixelValue); + } + const FX_RECT& rect = m_pGRD->GetReplaceRect(); + m_pPage->ComposeFromWithRect(m_ri.x + rect.left, m_ri.y + rect.top, +@@ -1044,7 +1040,7 @@ JBig2_Result CJBig2_Context::ParseGenericRefinementRegion( + if (!CJBig2_Image::IsValidImageSize(ri.width, ri.height)) + return JBig2_Result::kFailure; + +- auto pGRRD = pdfium::MakeUnique(); ++ auto pGRRD = std::make_unique(); + pGRRD->GRW = ri.width; + pGRRD->GRH = ri.height; + pGRRD->GRTEMPLATE = !!(cFlags & 0x01); +@@ -1080,7 +1076,7 @@ JBig2_Result CJBig2_Context::ParseGenericRefinementRegion( + const size_t size = GetRefAggContextSize(pGRRD->GRTEMPLATE); + std::unique_ptr grContext( + FX_Alloc(JBig2ArithCtx, size)); +- auto pArithDecoder = pdfium::MakeUnique(m_pStream.get()); ++ auto pArithDecoder = std::make_unique(m_pStream.get()); + pSegment->m_nResultType = JBIG2_IMAGE_POINTER; + pSegment->m_Image = pGRRD->Decode(pArithDecoder.get(), grContext.get()); + if (!pSegment->m_Image) +@@ -1091,10 +1087,8 @@ JBig2_Result CJBig2_Context::ParseGenericRefinementRegion( + if (pSegment->m_cFlags.s.type != 40) { + if (!m_bBufSpecified) { + JBig2PageInfo* pPageInfo = m_PageInfoList.back().get(); +- if ((pPageInfo->m_bIsStriped == 1) && +- (ri.y + ri.height > m_pPage->height())) { +- m_pPage->Expand(ri.y + ri.height, (pPageInfo->m_cFlags & 4) ? 1 : 0); +- } ++ if (pPageInfo->m_bIsStriped && ri.y + ri.height > m_pPage->height()) ++ m_pPage->Expand(ri.y + ri.height, pPageInfo->m_bDefaultPixelValue); + } + m_pPage->ComposeFrom(ri.x, ri.y, pSegment->m_Image.get(), + (JBig2ComposeOp)(ri.flags & 0x03)); +@@ -1106,7 +1100,7 @@ JBig2_Result CJBig2_Context::ParseGenericRefinementRegion( + JBig2_Result CJBig2_Context::ParseTable(CJBig2_Segment* pSegment) { + pSegment->m_nResultType = JBIG2_HUFFMAN_TABLE_POINTER; + pSegment->m_HuffmanTable.reset(); +- auto pHuff = pdfium::MakeUnique(m_pStream.get()); ++ auto pHuff = std::make_unique(m_pStream.get()); + if (!pHuff->IsOK()) + return JBig2_Result::kFailure; + +@@ -1200,10 +1194,10 @@ std::vector CJBig2_Context::DecodeSymbolIDHuffmanTable( + } + + const CJBig2_HuffmanTable* CJBig2_Context::GetHuffmanTable(size_t idx) { +- ASSERT(idx > 0); +- ASSERT(idx < CJBig2_HuffmanTable::kNumHuffmanTables); ++ DCHECK(idx > 0); ++ DCHECK(idx < CJBig2_HuffmanTable::kNumHuffmanTables); + if (!m_HuffmanTables[idx].get()) +- m_HuffmanTables[idx] = pdfium::MakeUnique(idx); ++ m_HuffmanTables[idx] = std::make_unique(idx); + return m_HuffmanTables[idx].get(); + } + +@@ -1221,7 +1215,7 @@ bool CJBig2_Context::HuffmanAssignCode(JBig2HuffmanCode* SBSYMCODES, + LENCOUNT[0] = 0; + + for (int i = 1; i <= LENMAX; ++i) { +- pdfium::base::CheckedNumeric shifted = FIRSTCODE[i - 1]; ++ FX_SAFE_INT32 shifted = FIRSTCODE[i - 1]; + shifted += LENCOUNT[i - 1]; + shifted <<= 1; + if (!shifted.IsValid()) +diff --git a/core/fxcodec/jbig2/JBig2_Context.h b/core/fxcodec/jbig2/JBig2_Context.h +index 49669edb8..279db5bac 100644 +--- a/core/fxcodec/jbig2/JBig2_Context.h ++++ b/core/fxcodec/jbig2/JBig2_Context.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,21 +13,16 @@ + #include + + #include "core/fxcodec/fx_codec_def.h" ++#include "core/fxcodec/jbig2/JBig2_DocumentContext.h" + #include "core/fxcodec/jbig2/JBig2_Page.h" + #include "core/fxcodec/jbig2/JBig2_Segment.h" +-#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/unowned_ptr.h" + #include "third_party/base/span.h" + + class CJBig2_ArithDecoder; + class CJBig2_GRDProc; +-class CPDF_StreamAcc; + class PauseIndicatorIface; + +-// Cache is keyed by the ObjNum of a stream and an index within the stream. +-using CJBig2_CacheKey = std::pair; +-using CJBig2_CachePair = +- std::pair>; +- + #define JBIG2_MIN_SEGMENT_SIZE 11 + + enum class JBig2_Result { kSuccess, kFailure, kEndReached }; +@@ -36,16 +31,16 @@ class CJBig2_Context { + public: + static std::unique_ptr Create( + pdfium::span pGlobalSpan, +- uint32_t dwGlobalObjNum, ++ uint64_t global_key, + pdfium::span pSrcSpan, +- uint32_t dwSrcObjNum, ++ uint64_t src_key, + std::list* pSymbolDictCache); + + ~CJBig2_Context(); + + static bool HuffmanAssignCode(JBig2HuffmanCode* SBSYMCODES, uint32_t NTEMP); + +- bool GetFirstPage(uint8_t* pBuf, ++ bool GetFirstPage(pdfium::span pBuf, + int32_t width, + int32_t height, + int32_t stride, +@@ -56,7 +51,7 @@ class CJBig2_Context { + + private: + CJBig2_Context(pdfium::span pSrcSpan, +- uint32_t dwObjNum, ++ uint64_t src_key, + std::list* pSymbolDictCache, + bool bIsGlobal); + +@@ -96,14 +91,14 @@ class CJBig2_Context { + bool m_bInPage = false; + bool m_bBufSpecified = false; + int32_t m_PauseStep = 10; +- FXCODEC_STATUS m_ProcessingStatus = FXCODEC_STATUS_FRAME_READY; ++ FXCODEC_STATUS m_ProcessingStatus = FXCODEC_STATUS::kFrameReady; + std::vector m_gbContext; + std::unique_ptr m_pArithDecoder; + std::unique_ptr m_pGRD; + std::unique_ptr m_pSegment; +- FX_SAFE_UINT32 m_dwOffset = 0; +- JBig2RegionInfo m_ri; +- std::list* const m_pSymbolDictCache; ++ uint32_t m_nOffset = 0; ++ JBig2RegionInfo m_ri = {}; ++ UnownedPtr> const m_pSymbolDictCache; + }; + + #endif // CORE_FXCODEC_JBIG2_JBIG2_CONTEXT_H_ +diff --git a/core/fxcodec/jbig2/JBig2_Define.h b/core/fxcodec/jbig2/JBig2_Define.h +index 17eb007bd..f2796f594 100644 +--- a/core/fxcodec/jbig2/JBig2_Define.h ++++ b/core/fxcodec/jbig2/JBig2_Define.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,8 +9,6 @@ + + #include + +-#define JBIG2_OOB 1 +- + struct JBig2RegionInfo { + int32_t width; + int32_t height; +@@ -24,10 +22,12 @@ struct JBig2HuffmanCode { + int32_t code; + }; + +-#define JBIG2_MAX_REFERRED_SEGMENT_COUNT 64 +-#define JBIG2_MAX_EXPORT_SYSMBOLS 65535 +-#define JBIG2_MAX_NEW_SYSMBOLS 65535 +-#define JBIG2_MAX_PATTERN_INDEX 65535 +-#define JBIG2_MAX_IMAGE_SIZE 65535 ++constexpr int32_t kJBig2OOB = 1; ++ ++constexpr int32_t kJBig2MaxReferredSegmentCount = 64; ++constexpr uint32_t kJBig2MaxExportSymbols = 65535; ++constexpr uint32_t kJBig2MaxNewSymbols = 65535; ++constexpr uint32_t kJBig2MaxPatternIndex = 65535; ++constexpr int32_t kJBig2MaxImageSize = 65535; + + #endif // CORE_FXCODEC_JBIG2_JBIG2_DEFINE_H_ +diff --git a/core/fxcodec/jbig2/JBig2_DocumentContext.cpp b/core/fxcodec/jbig2/JBig2_DocumentContext.cpp +index bca92fe8b..423c1ba3c 100644 +--- a/core/fxcodec/jbig2/JBig2_DocumentContext.cpp ++++ b/core/fxcodec/jbig2/JBig2_DocumentContext.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + // +diff --git a/core/fxcodec/jbig2/JBig2_DocumentContext.h b/core/fxcodec/jbig2/JBig2_DocumentContext.h +index 9bcdd371e..b2a44c5b7 100644 +--- a/core/fxcodec/jbig2/JBig2_DocumentContext.h ++++ b/core/fxcodec/jbig2/JBig2_DocumentContext.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + // +@@ -13,9 +13,10 @@ + + class CJBig2_SymbolDict; + +-using CJBig2_CacheKey = std::pair; ++// Cache is keyed by both the key of a stream and an index within the stream. ++using CJBig2_CompoundKey = std::pair; + using CJBig2_CachePair = +- std::pair>; ++ std::pair>; + + // Holds per-document JBig2 related data. + class JBig2_DocumentContext { +diff --git a/core/fxcodec/jbig2/JBig2_GrdProc.cpp b/core/fxcodec/jbig2/JBig2_GrdProc.cpp +index df6e207a1..a940d0e3e 100644 +--- a/core/fxcodec/jbig2/JBig2_GrdProc.cpp ++++ b/core/fxcodec/jbig2/JBig2_GrdProc.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -15,7 +15,6 @@ + #include "core/fxcodec/jbig2/JBig2_BitStream.h" + #include "core/fxcodec/jbig2/JBig2_Image.h" + #include "core/fxcrt/pauseindicator_iface.h" +-#include "third_party/base/ptr_util.h" + + namespace { + +@@ -63,7 +62,7 @@ std::unique_ptr CJBig2_GRDProc::DecodeArith( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext) { + if (!CJBig2_Image::IsValidImageSize(GBW, GBH)) +- return pdfium::MakeUnique(GBW, GBH); ++ return std::make_unique(GBW, GBH); + + switch (GBTEMPLATE) { + case 0: +@@ -89,7 +88,7 @@ std::unique_ptr CJBig2_GRDProc::DecodeArithOpt3( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + int OPT) { +- auto GBREG = pdfium::MakeUnique(GBW, GBH); ++ auto GBREG = std::make_unique(GBW, GBH); + if (!GBREG->data()) + return nullptr; + +@@ -195,11 +194,11 @@ std::unique_ptr CJBig2_GRDProc::DecodeArithTemplateUnopt( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext, + int UNOPT) { +- auto GBREG = pdfium::MakeUnique(GBW, GBH); ++ auto GBREG = std::make_unique(GBW, GBH); + if (!GBREG->data()) + return nullptr; + +- GBREG->Fill(0); ++ GBREG->Fill(false); + int LTP = 0; + uint8_t MOD2 = UNOPT % 2; + uint8_t DIV2 = UNOPT / 2; +@@ -256,7 +255,7 @@ std::unique_ptr CJBig2_GRDProc::DecodeArithTemplateUnopt( + std::unique_ptr CJBig2_GRDProc::DecodeArithTemplate3Opt3( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext) { +- auto GBREG = pdfium::MakeUnique(GBW, GBH); ++ auto GBREG = std::make_unique(GBW, GBH); + if (!GBREG->data()) + return nullptr; + +@@ -341,11 +340,11 @@ std::unique_ptr CJBig2_GRDProc::DecodeArithTemplate3Opt3( + std::unique_ptr CJBig2_GRDProc::DecodeArithTemplate3Unopt( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* gbContext) { +- auto GBREG = pdfium::MakeUnique(GBW, GBH); ++ auto GBREG = std::make_unique(GBW, GBH); + if (!GBREG->data()) + return nullptr; + +- GBREG->Fill(0); ++ GBREG->Fill(false); + int LTP = 0; + for (uint32_t h = 0; h < GBH; h++) { + if (TPGDON) { +@@ -387,19 +386,19 @@ std::unique_ptr CJBig2_GRDProc::DecodeArithTemplate3Unopt( + FXCODEC_STATUS CJBig2_GRDProc::StartDecodeArith( + ProgressiveArithDecodeState* pState) { + if (!CJBig2_Image::IsValidImageSize(GBW, GBH)) { +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; +- return FXCODEC_STATUS_DECODE_FINISH; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished; ++ return FXCODEC_STATUS::kDecodeFinished; + } +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_READY; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeReady; + std::unique_ptr* pImage = pState->pImage; + if (!*pImage) +- *pImage = pdfium::MakeUnique(GBW, GBH); ++ *pImage = std::make_unique(GBW, GBH); + if (!(*pImage)->data()) { + *pImage = nullptr; +- m_ProssiveStatus = FXCODEC_STATUS_ERROR; +- return FXCODEC_STATUS_ERROR; ++ m_ProgressiveStatus = FXCODEC_STATUS::kError; ++ return FXCODEC_STATUS::kError; + } +- pImage->get()->Fill(0); ++ pImage->get()->Fill(false); + m_DecodeType = 1; + m_LTP = 0; + m_pLine = nullptr; +@@ -437,25 +436,25 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArith( + break; + } + CJBig2_Image* pImage = pState->pImage->get(); +- m_ProssiveStatus = func(*this, pState); ++ m_ProgressiveStatus = func(*this, pState); + m_ReplaceRect.left = 0; + m_ReplaceRect.right = pImage->width(); + m_ReplaceRect.top = iline; + m_ReplaceRect.bottom = m_loopIndex; +- if (m_ProssiveStatus == FXCODEC_STATUS_DECODE_FINISH) ++ if (m_ProgressiveStatus == FXCODEC_STATUS::kDecodeFinished) + m_loopIndex = 0; + +- return m_ProssiveStatus; ++ return m_ProgressiveStatus; + } + + FXCODEC_STATUS CJBig2_GRDProc::StartDecodeMMR( + std::unique_ptr* pImage, + CJBig2_BitStream* pStream) { +- auto image = pdfium::MakeUnique(GBW, GBH); ++ auto image = std::make_unique(GBW, GBH); + if (!image->data()) { + *pImage = nullptr; +- m_ProssiveStatus = FXCODEC_STATUS_ERROR; +- return m_ProssiveStatus; ++ m_ProgressiveStatus = FXCODEC_STATUS::kError; ++ return m_ProgressiveStatus; + } + int bitpos = static_cast(pStream->getBitPos()); + bitpos = +@@ -464,19 +463,19 @@ FXCODEC_STATUS CJBig2_GRDProc::StartDecodeMMR( + pStream->setBitPos(bitpos); + for (uint32_t i = 0; i < image->stride() * GBH; ++i) + image->data()[i] = ~image->data()[i]; +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished; + *pImage = std::move(image); +- return m_ProssiveStatus; ++ return m_ProgressiveStatus; + } + + FXCODEC_STATUS CJBig2_GRDProc::ContinueDecode( + ProgressiveArithDecodeState* pState) { +- if (m_ProssiveStatus != FXCODEC_STATUS_DECODE_TOBECONTINUE) +- return m_ProssiveStatus; ++ if (m_ProgressiveStatus != FXCODEC_STATUS::kDecodeToBeContinued) ++ return m_ProgressiveStatus; + + if (m_DecodeType != 1) { +- m_ProssiveStatus = FXCODEC_STATUS_ERROR; +- return m_ProssiveStatus; ++ m_ProgressiveStatus = FXCODEC_STATUS::kError; ++ return m_ProgressiveStatus; + } + return ProgressiveDecodeArith(pState); + } +@@ -484,8 +483,8 @@ FXCODEC_STATUS CJBig2_GRDProc::ContinueDecode( + FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Opt3( + ProgressiveArithDecodeState* pState) { + CJBig2_Image* pImage = pState->pImage->get(); +- JBig2ArithCtx* gbContext = pState->gbContext.Get(); +- CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get(); ++ JBig2ArithCtx* gbContext = pState->gbContext; ++ CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder; + if (!m_pLine) + m_pLine = pImage->data(); + int32_t nStride = pImage->stride(); +@@ -497,7 +496,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Opt3( + for (; m_loopIndex < height; m_loopIndex++) { + if (TPGDON) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x9b25]); + } +@@ -516,7 +515,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Opt3( + uint8_t cVal = 0; + for (int32_t k = 7; k >= 0; k--) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + cVal |= bVal << k; +@@ -530,7 +529,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Opt3( + uint8_t cVal1 = 0; + for (int32_t k = 0; k < nBitsLeft; k++) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + cVal1 |= bVal << (7 - k); +@@ -550,7 +549,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Opt3( + uint8_t cVal = 0; + for (int32_t k = 7; k >= 0; k--) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + cVal |= bVal << k; +@@ -563,7 +562,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Opt3( + uint8_t cVal1 = 0; + for (int32_t k = 0; k < nBitsLeft; k++) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + cVal1 |= bVal << (7 - k); +@@ -576,23 +575,23 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Opt3( + m_pLine += nStride; + if (pState->pPause && pState->pPause->NeedToPauseNow()) { + m_loopIndex++; +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; +- return FXCODEC_STATUS_DECODE_TOBECONTINUE; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeToBeContinued; ++ return FXCODEC_STATUS::kDecodeToBeContinued; + } + } +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; +- return FXCODEC_STATUS_DECODE_FINISH; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished; ++ return FXCODEC_STATUS::kDecodeFinished; + } + + FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Unopt( + ProgressiveArithDecodeState* pState) { + CJBig2_Image* pImage = pState->pImage->get(); +- JBig2ArithCtx* gbContext = pState->gbContext.Get(); +- CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get(); ++ JBig2ArithCtx* gbContext = pState->gbContext; ++ CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder; + for (; m_loopIndex < GBH; m_loopIndex++) { + if (TPGDON) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x9b25]); + } +@@ -618,7 +617,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Unopt( + CONTEXT |= line1 << 12; + CONTEXT |= pImage->GetPixel(w + GBAT[6], m_loopIndex + GBAT[7]) << 15; + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + } +@@ -634,19 +633,19 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate0Unopt( + } + if (pState->pPause && pState->pPause->NeedToPauseNow()) { + m_loopIndex++; +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; +- return FXCODEC_STATUS_DECODE_TOBECONTINUE; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeToBeContinued; ++ return FXCODEC_STATUS::kDecodeToBeContinued; + } + } +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; +- return FXCODEC_STATUS_DECODE_FINISH; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished; ++ return FXCODEC_STATUS::kDecodeFinished; + } + + FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Opt3( + ProgressiveArithDecodeState* pState) { + CJBig2_Image* pImage = pState->pImage->get(); +- JBig2ArithCtx* gbContext = pState->gbContext.Get(); +- CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get(); ++ JBig2ArithCtx* gbContext = pState->gbContext; ++ CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder; + if (!m_pLine) + m_pLine = pImage->data(); + int32_t nStride = pImage->stride(); +@@ -656,7 +655,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Opt3( + for (; m_loopIndex < GBH; m_loopIndex++) { + if (TPGDON) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x0795]); + } +@@ -675,7 +674,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Opt3( + uint8_t cVal = 0; + for (int32_t k = 7; k >= 0; k--) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + cVal |= bVal << k; +@@ -689,7 +688,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Opt3( + uint8_t cVal1 = 0; + for (int32_t k = 0; k < nBitsLeft; k++) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + cVal1 |= bVal << (7 - k); +@@ -709,7 +708,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Opt3( + uint8_t cVal = 0; + for (int32_t k = 7; k >= 0; k--) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + cVal |= bVal << k; +@@ -722,7 +721,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Opt3( + uint8_t cVal1 = 0; + for (int32_t k = 0; k < nBitsLeft; k++) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + cVal1 |= bVal << (7 - k); +@@ -735,23 +734,23 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Opt3( + m_pLine += nStride; + if (pState->pPause && pState->pPause->NeedToPauseNow()) { + m_loopIndex++; +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; +- return FXCODEC_STATUS_DECODE_TOBECONTINUE; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeToBeContinued; ++ return FXCODEC_STATUS::kDecodeToBeContinued; + } + } +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; +- return FXCODEC_STATUS_DECODE_FINISH; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished; ++ return FXCODEC_STATUS::kDecodeFinished; + } + + FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Unopt( + ProgressiveArithDecodeState* pState) { + CJBig2_Image* pImage = pState->pImage->get(); +- JBig2ArithCtx* gbContext = pState->gbContext.Get(); +- CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get(); ++ JBig2ArithCtx* gbContext = pState->gbContext; ++ CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder; + for (uint32_t h = 0; h < GBH; h++) { + if (TPGDON) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x0795]); + } +@@ -775,7 +774,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Unopt( + CONTEXT |= line2 << 4; + CONTEXT |= line1 << 9; + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + } +@@ -789,19 +788,19 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate1Unopt( + } + if (pState->pPause && pState->pPause->NeedToPauseNow()) { + m_loopIndex++; +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; +- return FXCODEC_STATUS_DECODE_TOBECONTINUE; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeToBeContinued; ++ return FXCODEC_STATUS::kDecodeToBeContinued; + } + } +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; +- return FXCODEC_STATUS_DECODE_FINISH; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished; ++ return FXCODEC_STATUS::kDecodeFinished; + } + + FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Opt3( + ProgressiveArithDecodeState* pState) { + CJBig2_Image* pImage = pState->pImage->get(); +- JBig2ArithCtx* gbContext = pState->gbContext.Get(); +- CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get(); ++ JBig2ArithCtx* gbContext = pState->gbContext; ++ CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder; + if (!m_pLine) + m_pLine = pImage->data(); + int32_t nStride = pImage->stride(); +@@ -811,7 +810,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Opt3( + for (; m_loopIndex < GBH; m_loopIndex++) { + if (TPGDON) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x00e5]); + } +@@ -830,7 +829,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Opt3( + uint8_t cVal = 0; + for (int32_t k = 7; k >= 0; k--) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + cVal |= bVal << k; +@@ -844,7 +843,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Opt3( + uint8_t cVal1 = 0; + for (int32_t k = 0; k < nBitsLeft; k++) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + cVal1 |= bVal << (7 - k); +@@ -864,7 +863,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Opt3( + uint8_t cVal = 0; + for (int32_t k = 7; k >= 0; k--) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + cVal |= bVal << k; +@@ -877,7 +876,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Opt3( + uint8_t cVal1 = 0; + for (int32_t k = 0; k < nBitsLeft; k++) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + cVal1 |= bVal << (7 - k); +@@ -891,23 +890,23 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Opt3( + if (pState->pPause && m_loopIndex % 50 == 0 && + pState->pPause->NeedToPauseNow()) { + m_loopIndex++; +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; +- return FXCODEC_STATUS_DECODE_TOBECONTINUE; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeToBeContinued; ++ return FXCODEC_STATUS::kDecodeToBeContinued; + } + } +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; +- return FXCODEC_STATUS_DECODE_FINISH; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished; ++ return FXCODEC_STATUS::kDecodeFinished; + } + + FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Unopt( + ProgressiveArithDecodeState* pState) { + CJBig2_Image* pImage = pState->pImage->get(); +- JBig2ArithCtx* gbContext = pState->gbContext.Get(); +- CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get(); ++ JBig2ArithCtx* gbContext = pState->gbContext; ++ CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder; + for (; m_loopIndex < GBH; m_loopIndex++) { + if (TPGDON) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x00e5]); + } +@@ -929,7 +928,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Unopt( + CONTEXT |= line2 << 3; + CONTEXT |= line1 << 7; + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + } +@@ -945,19 +944,19 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate2Unopt( + } + if (pState->pPause && pState->pPause->NeedToPauseNow()) { + m_loopIndex++; +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; +- return FXCODEC_STATUS_DECODE_TOBECONTINUE; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeToBeContinued; ++ return FXCODEC_STATUS::kDecodeToBeContinued; + } + } +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; +- return FXCODEC_STATUS_DECODE_FINISH; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished; ++ return FXCODEC_STATUS::kDecodeFinished; + } + + FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Opt3( + ProgressiveArithDecodeState* pState) { + CJBig2_Image* pImage = pState->pImage->get(); +- JBig2ArithCtx* gbContext = pState->gbContext.Get(); +- CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get(); ++ JBig2ArithCtx* gbContext = pState->gbContext; ++ CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder; + if (!m_pLine) + m_pLine = pImage->data(); + int32_t nStride = pImage->stride(); +@@ -966,7 +965,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Opt3( + for (; m_loopIndex < GBH; m_loopIndex++) { + if (TPGDON) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x0195]); + } +@@ -982,7 +981,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Opt3( + uint8_t cVal = 0; + for (int32_t k = 7; k >= 0; k--) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + cVal |= bVal << k; +@@ -995,7 +994,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Opt3( + uint8_t cVal1 = 0; + for (int32_t k = 0; k < nBitsLeft; k++) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + cVal1 |= bVal << (7 - k); +@@ -1009,7 +1008,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Opt3( + uint8_t cVal = 0; + for (int32_t k = 7; k >= 0; k--) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + cVal |= bVal << k; +@@ -1020,7 +1019,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Opt3( + uint8_t cVal1 = 0; + for (int32_t k = 0; k < nBitsLeft; k++) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + int bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + cVal1 |= bVal << (7 - k); +@@ -1032,23 +1031,23 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Opt3( + m_pLine += nStride; + if (pState->pPause && pState->pPause->NeedToPauseNow()) { + m_loopIndex++; +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; +- return FXCODEC_STATUS_DECODE_TOBECONTINUE; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeToBeContinued; ++ return FXCODEC_STATUS::kDecodeToBeContinued; + } + } +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; +- return FXCODEC_STATUS_DECODE_FINISH; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished; ++ return FXCODEC_STATUS::kDecodeFinished; + } + + FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Unopt( + ProgressiveArithDecodeState* pState) { + CJBig2_Image* pImage = pState->pImage->get(); +- JBig2ArithCtx* gbContext = pState->gbContext.Get(); +- CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder.Get(); ++ JBig2ArithCtx* gbContext = pState->gbContext; ++ CJBig2_ArithDecoder* pArithDecoder = pState->pArithDecoder; + for (; m_loopIndex < GBH; m_loopIndex++) { + if (TPGDON) { + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + m_LTP = m_LTP ^ pArithDecoder->Decode(&gbContext[0x0195]); + } +@@ -1067,7 +1066,7 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Unopt( + CONTEXT |= pImage->GetPixel(w + GBAT[0], m_loopIndex + GBAT[1]) << 4; + CONTEXT |= line1 << 5; + if (pArithDecoder->IsComplete()) +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + + bVal = pArithDecoder->Decode(&gbContext[CONTEXT]); + } +@@ -1081,10 +1080,10 @@ FXCODEC_STATUS CJBig2_GRDProc::ProgressiveDecodeArithTemplate3Unopt( + } + if (pState->pPause && pState->pPause->NeedToPauseNow()) { + m_loopIndex++; +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_TOBECONTINUE; +- return FXCODEC_STATUS_DECODE_TOBECONTINUE; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeToBeContinued; ++ return FXCODEC_STATUS::kDecodeToBeContinued; + } + } +- m_ProssiveStatus = FXCODEC_STATUS_DECODE_FINISH; +- return FXCODEC_STATUS_DECODE_FINISH; ++ m_ProgressiveStatus = FXCODEC_STATUS::kDecodeFinished; ++ return FXCODEC_STATUS::kDecodeFinished; + } +diff --git a/core/fxcodec/jbig2/JBig2_GrdProc.h b/core/fxcodec/jbig2/JBig2_GrdProc.h +index af6f2232d..197eb91d9 100644 +--- a/core/fxcodec/jbig2/JBig2_GrdProc.h ++++ b/core/fxcodec/jbig2/JBig2_GrdProc.h +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,12 @@ + #ifndef CORE_FXCODEC_JBIG2_JBIG2_GRDPROC_H_ + #define CORE_FXCODEC_JBIG2_JBIG2_GRDPROC_H_ + ++#include ++ + #include + + #include "core/fxcodec/fx_codec_def.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/unowned_ptr.h" + + class CJBig2_ArithDecoder; +@@ -94,7 +95,7 @@ class CJBig2_GRDProc { + + uint32_t m_loopIndex = 0; + uint8_t* m_pLine = nullptr; +- FXCODEC_STATUS m_ProssiveStatus; ++ FXCODEC_STATUS m_ProgressiveStatus; + uint16_t m_DecodeType = 0; + int m_LTP = 0; + FX_RECT m_ReplaceRect; +diff --git a/core/fxcodec/jbig2/JBig2_GrrdProc.cpp b/core/fxcodec/jbig2/JBig2_GrrdProc.cpp +index e67377622..533fb7997 100644 +--- a/core/fxcodec/jbig2/JBig2_GrrdProc.cpp ++++ b/core/fxcodec/jbig2/JBig2_GrrdProc.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,7 +11,6 @@ + #include "core/fxcodec/jbig2/JBig2_ArithDecoder.h" + #include "core/fxcodec/jbig2/JBig2_BitStream.h" + #include "core/fxcodec/jbig2/JBig2_Image.h" +-#include "third_party/base/ptr_util.h" + + CJBig2_GRRDProc::CJBig2_GRRDProc() = default; + +@@ -21,7 +20,7 @@ std::unique_ptr CJBig2_GRRDProc::Decode( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* grContext) { + if (!CJBig2_Image::IsValidImageSize(GRW, GRH)) +- return pdfium::MakeUnique(GRW, GRH); ++ return std::make_unique(GRW, GRH); + + if (!GRTEMPLATE) { + if ((GRAT[0] == -1) && (GRAT[1] == -1) && (GRAT[2] == -1) && +@@ -41,11 +40,11 @@ std::unique_ptr CJBig2_GRRDProc::Decode( + std::unique_ptr CJBig2_GRRDProc::DecodeTemplate0Unopt( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* grContext) { +- auto GRREG = pdfium::MakeUnique(GRW, GRH); ++ auto GRREG = std::make_unique(GRW, GRH); + if (!GRREG->data()) + return nullptr; + +- GRREG->Fill(0); ++ GRREG->Fill(false); + int LTP = 0; + for (uint32_t h = 0; h < GRH; h++) { + if (TPGRON) { +@@ -149,7 +148,7 @@ std::unique_ptr CJBig2_GRRDProc::DecodeTemplate0Opt( + + int32_t iGRW = static_cast(GRW); + int32_t iGRH = static_cast(GRH); +- auto GRREG = pdfium::MakeUnique(iGRW, iGRH); ++ auto GRREG = std::make_unique(iGRW, iGRH); + if (!GRREG->data()) + return nullptr; + +@@ -282,11 +281,11 @@ std::unique_ptr CJBig2_GRRDProc::DecodeTemplate0Opt( + std::unique_ptr CJBig2_GRRDProc::DecodeTemplate1Unopt( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* grContext) { +- auto GRREG = pdfium::MakeUnique(GRW, GRH); ++ auto GRREG = std::make_unique(GRW, GRH); + if (!GRREG->data()) + return nullptr; + +- GRREG->Fill(0); ++ GRREG->Fill(false); + int LTP = 0; + for (uint32_t h = 0; h < GRH; h++) { + if (TPGRON) { +@@ -396,7 +395,7 @@ std::unique_ptr CJBig2_GRRDProc::DecodeTemplate1Opt( + + int32_t iGRW = static_cast(GRW); + int32_t iGRH = static_cast(GRH); +- auto GRREG = pdfium::MakeUnique(iGRW, iGRH); ++ auto GRREG = std::make_unique(iGRW, iGRH); + if (!GRREG->data()) + return nullptr; + +diff --git a/core/fxcodec/jbig2/JBig2_GrrdProc.h b/core/fxcodec/jbig2/JBig2_GrrdProc.h +index c7e3bf9d1..d1a729b20 100644 +--- a/core/fxcodec/jbig2/JBig2_GrrdProc.h ++++ b/core/fxcodec/jbig2/JBig2_GrrdProc.h +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,9 +7,10 @@ + #ifndef CORE_FXCODEC_JBIG2_JBIG2_GRRDPROC_H_ + #define CORE_FXCODEC_JBIG2_JBIG2_GRRDPROC_H_ + ++#include ++ + #include + +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/unowned_ptr.h" + + class CJBig2_ArithDecoder; +diff --git a/core/fxcodec/jbig2/JBig2_HtrdProc.cpp b/core/fxcodec/jbig2/JBig2_HtrdProc.cpp +index 59fbd8950..1c0f88237 100644 +--- a/core/fxcodec/jbig2/JBig2_HtrdProc.cpp ++++ b/core/fxcodec/jbig2/JBig2_HtrdProc.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,7 +12,6 @@ + #include "core/fxcodec/jbig2/JBig2_BitStream.h" + #include "core/fxcodec/jbig2/JBig2_GrdProc.h" + #include "core/fxcodec/jbig2/JBig2_Image.h" +-#include "third_party/base/ptr_util.h" + + std::unique_ptr CJBig2_HTRDProc::DecodeArith( + CJBig2_ArithDecoder* pArithDecoder, +@@ -20,7 +19,7 @@ std::unique_ptr CJBig2_HTRDProc::DecodeArith( + PauseIndicatorIface* pPause) { + std::unique_ptr HSKIP; + if (HENABLESKIP == 1) { +- HSKIP = pdfium::MakeUnique(HGW, HGH); ++ HSKIP = std::make_unique(HGW, HGH); + for (uint32_t mg = 0; mg < HGH; ++mg) { + for (uint32_t ng = 0; ng < HGW; ++ng) { + int32_t x = (HGX + mg * HRY + ng * HRX) >> 8; +@@ -43,7 +42,7 @@ std::unique_ptr CJBig2_HTRDProc::DecodeArith( + GRD.GBW = HGW; + GRD.GBH = HGH; + GRD.GBTEMPLATE = HTEMPLATE; +- GRD.TPGDON = 0; ++ GRD.TPGDON = false; + GRD.USESKIP = HENABLESKIP; + GRD.SKIP = HSKIP.get(); + if (HTEMPLATE <= 1) +@@ -71,7 +70,7 @@ std::unique_ptr CJBig2_HTRDProc::DecodeArith( + state.pPause = nullptr; + FXCODEC_STATUS status = GRD.StartDecodeArith(&state); + state.pPause = pPause; +- while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE) ++ while (status == FXCODEC_STATUS::kDecodeToBeContinued) + status = GRD.ContinueDecode(&state); + if (!pImage) + return nullptr; +@@ -116,7 +115,7 @@ std::unique_ptr CJBig2_HTRDProc::DecodeMMR( + + std::unique_ptr CJBig2_HTRDProc::DecodeImage( + const std::vector>& GSPLANES) { +- auto HTREG = pdfium::MakeUnique(HBW, HBH); ++ auto HTREG = std::make_unique(HBW, HBH); + if (!HTREG->data()) + return nullptr; + +diff --git a/core/fxcodec/jbig2/JBig2_HtrdProc.h b/core/fxcodec/jbig2/JBig2_HtrdProc.h +index cd90aae88..30e9d9cdd 100644 +--- a/core/fxcodec/jbig2/JBig2_HtrdProc.h ++++ b/core/fxcodec/jbig2/JBig2_HtrdProc.h +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,12 @@ + #ifndef CORE_FXCODEC_JBIG2_JBIG2_HTRDPROC_H_ + #define CORE_FXCODEC_JBIG2_JBIG2_HTRDPROC_H_ + ++#include ++ + #include + #include + + #include "core/fxcodec/jbig2/JBig2_Image.h" +-#include "core/fxcrt/fx_system.h" + + class CJBig2_ArithDecoder; + class CJBig2_BitStream; +diff --git a/core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp b/core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp +index bea2b0904..7852abe86 100644 +--- a/core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp ++++ b/core/fxcodec/jbig2/JBig2_HuffmanDecoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,13 +12,13 @@ + CJBig2_HuffmanDecoder::CJBig2_HuffmanDecoder(CJBig2_BitStream* pStream) + : m_pStream(pStream) {} + +-CJBig2_HuffmanDecoder::~CJBig2_HuffmanDecoder() {} ++CJBig2_HuffmanDecoder::~CJBig2_HuffmanDecoder() = default; + + int CJBig2_HuffmanDecoder::DecodeAValue(const CJBig2_HuffmanTable* pTable, + int* nResult) { + FX_SAFE_INT32 nSafeVal = 0; + int nBits = 0; +- while (1) { ++ while (true) { + uint32_t nTmp; + if (m_pStream->read1Bit(&nTmp) == -1) + break; +@@ -36,7 +36,7 @@ int CJBig2_HuffmanDecoder::DecodeAValue(const CJBig2_HuffmanTable* pTable, + continue; + + if (pTable->IsHTOOB() && i == pTable->Size() - 1) +- return JBIG2_OOB; ++ return kJBig2OOB; + + if (m_pStream->readNBits(pTable->GetRANGELEN()[i], &nTmp) == -1) + return -1; +diff --git a/core/fxcodec/jbig2/JBig2_HuffmanDecoder.h b/core/fxcodec/jbig2/JBig2_HuffmanDecoder.h +index 48f8d0fbb..322c651a2 100644 +--- a/core/fxcodec/jbig2/JBig2_HuffmanDecoder.h ++++ b/core/fxcodec/jbig2/JBig2_HuffmanDecoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp b/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp +index 392b71866..fcbdb3453 100644 +--- a/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp ++++ b/core/fxcodec/jbig2/JBig2_HuffmanTable.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,13 +6,13 @@ + + #include "core/fxcodec/jbig2/JBig2_HuffmanTable.h" + ++#include + #include +-#include + + #include "core/fxcodec/jbig2/JBig2_BitStream.h" + #include "core/fxcodec/jbig2/JBig2_Context.h" +-#include "core/fxcrt/fx_memory.h" +-#include "third_party/base/numerics/safe_math.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -107,36 +107,36 @@ constexpr JBig2TableLine kTableLine15[] = { + + constexpr HuffmanTable kHuffmanTables[16] = { + {false, nullptr, 0}, // Zero dummy to preserve indexing. +- {false, kTableLine1, FX_ArraySize(kTableLine1)}, +- {true, kTableLine2, FX_ArraySize(kTableLine2)}, +- {true, kTableLine3, FX_ArraySize(kTableLine3)}, +- {false, kTableLine4, FX_ArraySize(kTableLine4)}, +- {false, kTableLine5, FX_ArraySize(kTableLine5)}, +- {false, kTableLine6, FX_ArraySize(kTableLine6)}, +- {false, kTableLine7, FX_ArraySize(kTableLine7)}, +- {true, kTableLine8, FX_ArraySize(kTableLine8)}, +- {true, kTableLine9, FX_ArraySize(kTableLine9)}, +- {true, kTableLine10, FX_ArraySize(kTableLine10)}, +- {false, kTableLine11, FX_ArraySize(kTableLine11)}, +- {false, kTableLine12, FX_ArraySize(kTableLine12)}, +- {false, kTableLine13, FX_ArraySize(kTableLine13)}, +- {false, kTableLine14, FX_ArraySize(kTableLine14)}, +- {false, kTableLine15, FX_ArraySize(kTableLine15)}}; ++ {false, kTableLine1, std::size(kTableLine1)}, ++ {true, kTableLine2, std::size(kTableLine2)}, ++ {true, kTableLine3, std::size(kTableLine3)}, ++ {false, kTableLine4, std::size(kTableLine4)}, ++ {false, kTableLine5, std::size(kTableLine5)}, ++ {false, kTableLine6, std::size(kTableLine6)}, ++ {false, kTableLine7, std::size(kTableLine7)}, ++ {true, kTableLine8, std::size(kTableLine8)}, ++ {true, kTableLine9, std::size(kTableLine9)}, ++ {true, kTableLine10, std::size(kTableLine10)}, ++ {false, kTableLine11, std::size(kTableLine11)}, ++ {false, kTableLine12, std::size(kTableLine12)}, ++ {false, kTableLine13, std::size(kTableLine13)}, ++ {false, kTableLine14, std::size(kTableLine14)}, ++ {false, kTableLine15, std::size(kTableLine15)}}; + + static_assert(CJBig2_HuffmanTable::kNumHuffmanTables == +- FX_ArraySize(kHuffmanTables), ++ std::size(kHuffmanTables), + "kNumHuffmanTables must be equal to the size of kHuffmanTables"); + + } // namespace + + CJBig2_HuffmanTable::CJBig2_HuffmanTable(size_t idx) { +- ASSERT(idx > 0); +- ASSERT(idx < kNumHuffmanTables); ++ DCHECK(idx > 0); ++ DCHECK(idx < kNumHuffmanTables); + const HuffmanTable& table = kHuffmanTables[idx]; + HTOOB = table.HTOOB; +- NTEMP = table.size; ++ NTEMP = pdfium::base::checked_cast(table.size); + m_bOK = ParseFromStandardTable(idx); +- ASSERT(m_bOK); ++ DCHECK(m_bOK); + } + + CJBig2_HuffmanTable::CJBig2_HuffmanTable(CJBig2_BitStream* pStream) +@@ -144,7 +144,7 @@ CJBig2_HuffmanTable::CJBig2_HuffmanTable(CJBig2_BitStream* pStream) + m_bOK = ParseFromCodedBuffer(pStream); + } + +-CJBig2_HuffmanTable::~CJBig2_HuffmanTable() {} ++CJBig2_HuffmanTable::~CJBig2_HuffmanTable() = default; + + bool CJBig2_HuffmanTable::ParseFromStandardTable(size_t idx) { + const JBig2TableLine* pTable = kHuffmanTables[idx].lines; +@@ -180,7 +180,7 @@ bool CJBig2_HuffmanTable::ParseFromCodedBuffer(CJBig2_BitStream* pStream) { + return false; + + ExtendBuffers(false); +- pdfium::base::CheckedNumeric cur_low = low; ++ FX_SAFE_INT32 cur_low = low; + do { + if ((pStream->readNBits(HTPS, &CODES[NTEMP].codelen) == -1) || + (pStream->readNBits(HTRS, &RANGELEN[NTEMP]) == -1) || +@@ -234,7 +234,7 @@ void CJBig2_HuffmanTable::ExtendBuffers(bool increment) { + return; + + size += 16; +- ASSERT(NTEMP < size); ++ DCHECK(NTEMP < size); + CODES.resize(size); + RANGELEN.resize(size); + RANGELOW.resize(size); +diff --git a/core/fxcodec/jbig2/JBig2_HuffmanTable.h b/core/fxcodec/jbig2/JBig2_HuffmanTable.h +index 351eb9d89..8e482f94d 100644 +--- a/core/fxcodec/jbig2/JBig2_HuffmanTable.h ++++ b/core/fxcodec/jbig2/JBig2_HuffmanTable.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,12 @@ + #ifndef CORE_FXCODEC_JBIG2_JBIG2_HUFFMANTABLE_H_ + #define CORE_FXCODEC_JBIG2_JBIG2_HUFFMANTABLE_H_ + ++#include ++#include ++ + #include + + #include "core/fxcodec/jbig2/JBig2_Define.h" +-#include "core/fxcrt/fx_system.h" + + class CJBig2_BitStream; + +diff --git a/core/fxcodec/jbig2/JBig2_Image.cpp b/core/fxcodec/jbig2/JBig2_Image.cpp +index 3cfb4f89d..773f6acea 100644 +--- a/core/fxcodec/jbig2/JBig2_Image.cpp ++++ b/core/fxcodec/jbig2/JBig2_Image.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -15,7 +15,7 @@ + #include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/fx_memory.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + #define JBIG2_GETDWORD(buf) \ + ((static_cast((buf)[0]) << 24) | \ +@@ -29,14 +29,19 @@ + (buf)[2] = static_cast((val) >> 8), \ + (buf)[3] = static_cast((val) >> 0)) + +-#define BIT_INDEX_TO_BYTE(x) ((x) >> 3) +-#define BIT_INDEX_TO_ALIGNED_BYTE(x) (((x) >> 5) << 2) +- + namespace { + + const int kMaxImagePixels = INT_MAX - 31; + const int kMaxImageBytes = kMaxImagePixels / 8; + ++int BitIndexToByte(int index) { ++ return index / 8; ++} ++ ++int BitIndexToAlignedByte(int index) { ++ return index / 32 * 4; ++} ++ + } // namespace + + CJBig2_Image::CJBig2_Image(int32_t w, int32_t h) { +@@ -57,7 +62,7 @@ CJBig2_Image::CJBig2_Image(int32_t w, int32_t h) { + CJBig2_Image::CJBig2_Image(int32_t w, + int32_t h, + int32_t stride, +- uint8_t* pBuf) { ++ pdfium::span pBuf) { + if (w < 0 || h < 0) + return; + +@@ -72,7 +77,7 @@ CJBig2_Image::CJBig2_Image(int32_t w, + m_nWidth = w; + m_nHeight = h; + m_nStride = stride; +- m_pData.Reset(pBuf); ++ m_pData.Reset(pBuf.data()); + } + + CJBig2_Image::CJBig2_Image(const CJBig2_Image& other) +@@ -86,12 +91,11 @@ CJBig2_Image::CJBig2_Image(const CJBig2_Image& other) + } + } + +-CJBig2_Image::~CJBig2_Image() {} ++CJBig2_Image::~CJBig2_Image() = default; + + // static + bool CJBig2_Image::IsValidImageSize(int32_t w, int32_t h) { +- return w > 0 && w <= JBIG2_MAX_IMAGE_SIZE && h > 0 && +- h <= JBIG2_MAX_IMAGE_SIZE; ++ return w > 0 && w <= kJBig2MaxImageSize && h > 0 && h <= kJBig2MaxImageSize; + } + + int CJBig2_Image::GetPixel(int32_t x, int32_t y) const { +@@ -105,7 +109,7 @@ int CJBig2_Image::GetPixel(int32_t x, int32_t y) const { + if (!pLine) + return 0; + +- int32_t m = BIT_INDEX_TO_BYTE(x); ++ int32_t m = BitIndexToByte(x); + int32_t n = x & 7; + return ((pLine[m] >> (7 - n)) & 1); + } +@@ -121,7 +125,7 @@ void CJBig2_Image::SetPixel(int32_t x, int32_t y, int v) { + if (!pLine) + return; + +- int32_t m = BIT_INDEX_TO_BYTE(x); ++ int32_t m = BitIndexToByte(x); + int32_t n = 1 << (7 - (x & 7)); + if (v) + pLine[m] |= n; +@@ -187,7 +191,7 @@ std::unique_ptr CJBig2_Image::SubImage(int32_t x, + int32_t y, + int32_t w, + int32_t h) { +- auto pImage = pdfium::MakeUnique(w, h); ++ auto pImage = std::make_unique(w, h); + if (!pImage->data() || !m_pData) + return pImage; + +@@ -208,7 +212,7 @@ void CJBig2_Image::SubImageFast(int32_t x, + int32_t w, + int32_t h, + CJBig2_Image* pImage) { +- int32_t m = BIT_INDEX_TO_BYTE(x); ++ int32_t m = BitIndexToByte(x); + int32_t bytes_to_copy = std::min(pImage->m_nStride, m_nStride - m); + int32_t lines_to_copy = std::min(pImage->m_nHeight, m_nHeight - y); + for (int32_t j = 0; j < lines_to_copy; j++) +@@ -220,7 +224,7 @@ void CJBig2_Image::SubImageSlow(int32_t x, + int32_t w, + int32_t h, + CJBig2_Image* pImage) { +- int32_t m = BIT_INDEX_TO_ALIGNED_BYTE(x); ++ int32_t m = BitIndexToAlignedByte(x); + int32_t n = x & 31; + int32_t bytes_to_copy = std::min(pImage->m_nStride, m_nStride - m); + int32_t lines_to_copy = std::min(pImage->m_nHeight, m_nHeight - y); +@@ -262,7 +266,7 @@ bool CJBig2_Image::ComposeToInternal(CJBig2_Image* pDst, + int32_t y, + JBig2ComposeOp op, + const FX_RECT& rtSrc) { +- ASSERT(m_pData); ++ DCHECK(m_pData); + + // TODO(weili): Check whether the range check is correct. Should x>=1048576? + if (x < -1048576 || x > 1048576 || y < -1048576 || y > 1048576) +@@ -304,11 +308,11 @@ bool CJBig2_Image::ComposeToInternal(CJBig2_Image* pDst, + uint32_t maskL = 0xffffffff >> d1; + uint32_t maskR = 0xffffffff << ((32 - (xd1 & 31)) % 32); + uint32_t maskM = maskL & maskR; +- const uint8_t* lineSrc = GetLineUnsafe(rtSrc.top + ys0) + +- BIT_INDEX_TO_ALIGNED_BYTE(xs0 + rtSrc.left); ++ const uint8_t* lineSrc = ++ GetLineUnsafe(rtSrc.top + ys0) + BitIndexToAlignedByte(xs0 + rtSrc.left); + const uint8_t* lineSrcEnd = data() + m_nHeight * m_nStride; +- int32_t lineLeft = m_nStride - BIT_INDEX_TO_ALIGNED_BYTE(xs0); +- uint8_t* lineDst = pDst->GetLineUnsafe(yd0) + BIT_INDEX_TO_ALIGNED_BYTE(xd0); ++ int32_t lineLeft = m_nStride - BitIndexToAlignedByte(xs0); ++ uint8_t* lineDst = pDst->GetLineUnsafe(yd0) + BitIndexToAlignedByte(xd0); + if ((xd0 & ~31) == ((xd1 - 1) & ~31)) { + if ((xs0 & ~31) == ((xs1 - 1) & ~31)) { + if (s1 > d1) { +@@ -666,5 +670,5 @@ bool CJBig2_Image::ComposeToInternal(CJBig2_Image* pDst, + } + } + } +- return 1; ++ return true; + } +diff --git a/core/fxcodec/jbig2/JBig2_Image.h b/core/fxcodec/jbig2/JBig2_Image.h +index 2be4ad395..4f751f4be 100644 +--- a/core/fxcodec/jbig2/JBig2_Image.h ++++ b/core/fxcodec/jbig2/JBig2_Image.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,6 +12,7 @@ + #include "core/fxcodec/jbig2/JBig2_Define.h" + #include "core/fxcrt/fx_memory_wrappers.h" + #include "core/fxcrt/maybe_owned.h" ++#include "third_party/base/span.h" + + struct FX_RECT; + +@@ -26,7 +27,10 @@ enum JBig2ComposeOp { + class CJBig2_Image { + public: + CJBig2_Image(int32_t w, int32_t h); +- CJBig2_Image(int32_t w, int32_t h, int32_t stride, uint8_t* pBuf); ++ CJBig2_Image(int32_t w, ++ int32_t h, ++ int32_t stride, ++ pdfium::span pBuf); + CJBig2_Image(const CJBig2_Image& other); + ~CJBig2_Image(); + +diff --git a/core/fxcodec/jbig2/JBig2_Image_unittest.cpp b/core/fxcodec/jbig2/JBig2_Image_unittest.cpp +index 13e6eb569..d76ab8a6b 100644 +--- a/core/fxcodec/jbig2/JBig2_Image_unittest.cpp ++++ b/core/fxcodec/jbig2/JBig2_Image_unittest.cpp +@@ -1,14 +1,14 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // TODO(tsepez) this requires a lot more testing. + ++#include "core/fxcodec/jbig2/JBig2_Image.h" ++ + #include + +-#include "core/fxcodec/jbig2/JBig2_Image.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + + namespace { + +@@ -75,15 +75,15 @@ TEST(fxcodec, JBig2ImageCreate) { + EXPECT_EQ(0, img.GetPixel(kWidthPixels, kHeightLines)); + + // Out-of-bounds GetLine() returns null. +- EXPECT_EQ(nullptr, img.GetLine(-1)); +- EXPECT_EQ(nullptr, img.GetLine(kHeightLines)); ++ EXPECT_FALSE(img.GetLine(-1)); ++ EXPECT_FALSE(img.GetLine(kHeightLines)); + } + + TEST(fxcodec, JBig2ImageCreateTooBig) { + CJBig2_Image img(kWidthPixels, kTooLargeHeightLines); + EXPECT_EQ(0, img.width()); + EXPECT_EQ(0, img.height()); +- EXPECT_EQ(nullptr, img.data()); ++ EXPECT_FALSE(img.data()); + } + + TEST(fxcodec, JBig2ImageCreateExternal) { +@@ -102,7 +102,7 @@ TEST(fxcodec, JBig2ImageCreateExternalTooBig) { + CJBig2_Image img(kWidthPixels, kTooLargeHeightLines, kStrideBytes, buf); + EXPECT_EQ(0, img.width()); + EXPECT_EQ(0, img.height()); +- EXPECT_EQ(nullptr, img.data()); ++ EXPECT_FALSE(img.data()); + } + + TEST(fxcodec, JBig2ImageCreateExternalBadStride) { +@@ -110,7 +110,7 @@ TEST(fxcodec, JBig2ImageCreateExternalBadStride) { + CJBig2_Image img(kWidthPixels, kTooLargeHeightLines, kStrideBytes - 1, buf); + EXPECT_EQ(0, img.width()); + EXPECT_EQ(0, img.height()); +- EXPECT_EQ(nullptr, img.data()); ++ EXPECT_FALSE(img.data()); + } + + TEST(fxcodec, JBig2ImageExpand) { +@@ -162,7 +162,7 @@ TEST(fxcodec, JBig2ImageExpandExternalTooBig) { + } + + TEST(fxcodec, JBig2EmptyImage) { +- auto empty = pdfium::MakeUnique(0, 0); ++ auto empty = std::make_unique(0, 0); + + // Empty subimage. + auto sub1 = empty->SubImage(0, 0, 0, 0); +@@ -192,61 +192,55 @@ TEST(fxcodec, JBig2EmptyImage) { + } + + TEST(fxcodec, JBig2SubImage) { ++ // clang-format off + // 1-px wide rectangle in image. +- uint8_t pattern[5][8] = { +- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ++ uint8_t pattern[40] = { ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + // 1-px wide rectangle in image, offset 2 in x. +- uint8_t pattern20[5][8] = { +- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ++ uint8_t pattern20[40] = { ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + // 1-px wide rectangle in image, offset 2 in x and y, padded. +- uint8_t pattern22[5][8] = { +- {0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ++ uint8_t pattern22[40] = { ++ 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + // 1-px wide rectangle in image, offset 16 in x, 1 in y, padded. +- uint8_t pattern161[5][8] = { +- {0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, +- {0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ++ uint8_t pattern161[40] = { ++ 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; ++ // clang-format on + + // Image size a nice clean power of two. +- auto img32 = pdfium::MakeUnique( +- 32, 5, 8, reinterpret_cast(pattern)); ++ auto img32 = std::make_unique(32, 5, 8, pattern); + + // Image size not a nice clean value. +- auto img37 = pdfium::MakeUnique( +- 37, 5, 8, reinterpret_cast(pattern)); ++ auto img37 = std::make_unique(37, 5, 8, pattern); + + // Expected results to check against. +- auto expected20 = pdfium::MakeUnique( +- 30, 5, 8, reinterpret_cast(pattern20)); +- +- auto expected22 = pdfium::MakeUnique( +- 30, 5, 8, reinterpret_cast(pattern22)); +- +- auto expected161 = pdfium::MakeUnique( +- 25, 5, 8, reinterpret_cast(pattern161)); +- +- auto expected_zeros = pdfium::MakeUnique(32, 5); ++ auto expected20 = std::make_unique(30, 5, 8, pattern20); ++ auto expected22 = std::make_unique(30, 5, 8, pattern22); ++ auto expected161 = std::make_unique(25, 5, 8, pattern161); ++ auto expected_zeros = std::make_unique(32, 5); + + // Empty subimage. + auto sub = img32->SubImage(0, 0, 0, 0); +@@ -315,24 +309,23 @@ TEST(fxcodec, JBig2SubImage) { + } + + TEST(fxcodec, JBig2CopyLine) { ++ // clang-format off + // Horizontal line in image. +- uint8_t pattern[3][8] = { +- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ++ uint8_t pattern[24] = { ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + +- uint8_t expected_pattern[3][8] = { +- {0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, +- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ++ uint8_t expected_pattern[24] = { ++ 0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; ++ // clang-format on + +- auto img = pdfium::MakeUnique( +- 37, 3, 8, reinterpret_cast(pattern)); +- +- auto expected = pdfium::MakeUnique( +- 37, 3, 8, reinterpret_cast(expected_pattern)); ++ auto img = std::make_unique(37, 3, 8, pattern); ++ auto expected = std::make_unique(37, 3, 8, expected_pattern); + + // Shuffle. + img->CopyLine(2, 1); +diff --git a/core/fxcodec/jbig2/JBig2_Page.h b/core/fxcodec/jbig2/JBig2_Page.h +index 064b9b354..f41dfba6c 100644 +--- a/core/fxcodec/jbig2/JBig2_Page.h ++++ b/core/fxcodec/jbig2/JBig2_Page.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,14 +7,15 @@ + #ifndef CORE_FXCODEC_JBIG2_JBIG2_PAGE_H_ + #define CORE_FXCODEC_JBIG2_JBIG2_PAGE_H_ + +-#include "core/fxcrt/fx_system.h" ++#include + + struct JBig2PageInfo { + uint32_t m_dwWidth; + uint32_t m_dwHeight; + uint32_t m_dwResolutionX; + uint32_t m_dwResolutionY; +- uint8_t m_cFlags; ++ // Page segment flags, bit 2. ++ bool m_bDefaultPixelValue; + bool m_bIsStriped; + uint16_t m_wMaxStripeSize; + }; +diff --git a/core/fxcodec/jbig2/JBig2_PatternDict.cpp b/core/fxcodec/jbig2/JBig2_PatternDict.cpp +index 4e7959fdc..a125b2e70 100644 +--- a/core/fxcodec/jbig2/JBig2_PatternDict.cpp ++++ b/core/fxcodec/jbig2/JBig2_PatternDict.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,4 +9,4 @@ + CJBig2_PatternDict::CJBig2_PatternDict(uint32_t dict_size) + : NUMPATS(dict_size), HDPATS(dict_size) {} + +-CJBig2_PatternDict::~CJBig2_PatternDict() {} ++CJBig2_PatternDict::~CJBig2_PatternDict() = default; +diff --git a/core/fxcodec/jbig2/JBig2_PatternDict.h b/core/fxcodec/jbig2/JBig2_PatternDict.h +index ca566d08a..3260405df 100644 +--- a/core/fxcodec/jbig2/JBig2_PatternDict.h ++++ b/core/fxcodec/jbig2/JBig2_PatternDict.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcodec/jbig2/JBig2_PddProc.cpp b/core/fxcodec/jbig2/JBig2_PddProc.cpp +index 9d274f9a4..c91f5910a 100644 +--- a/core/fxcodec/jbig2/JBig2_PddProc.cpp ++++ b/core/fxcodec/jbig2/JBig2_PddProc.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,7 +11,6 @@ + #include "core/fxcodec/jbig2/JBig2_GrdProc.h" + #include "core/fxcodec/jbig2/JBig2_Image.h" + #include "core/fxcodec/jbig2/JBig2_PatternDict.h" +-#include "third_party/base/ptr_util.h" + + std::unique_ptr CJBig2_PDDProc::DecodeArith( + CJBig2_ArithDecoder* pArithDecoder, +@@ -22,8 +21,8 @@ std::unique_ptr CJBig2_PDDProc::DecodeArith( + return nullptr; + + pGRD->GBTEMPLATE = HDTEMPLATE; +- pGRD->TPGDON = 0; +- pGRD->USESKIP = 0; ++ pGRD->TPGDON = false; ++ pGRD->USESKIP = false; + pGRD->GBAT[0] = -1 * static_cast(HDPW); + pGRD->GBAT[1] = 0; + if (pGRD->GBTEMPLATE == 0) { +@@ -44,12 +43,12 @@ std::unique_ptr CJBig2_PDDProc::DecodeArith( + + FXCODEC_STATUS status = pGRD->StartDecodeArith(&state); + state.pPause = pPause; +- while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE) ++ while (status == FXCODEC_STATUS::kDecodeToBeContinued) + status = pGRD->ContinueDecode(&state); + if (!BHDC) + return nullptr; + +- auto pDict = pdfium::MakeUnique(GRAYMAX + 1); ++ auto pDict = std::make_unique(GRAYMAX + 1); + for (uint32_t GRAY = 0; GRAY <= GRAYMAX; ++GRAY) + pDict->HDPATS[GRAY] = BHDC->SubImage(HDPW * GRAY, 0, HDPW, HDPH); + return pDict; +@@ -66,7 +65,7 @@ std::unique_ptr CJBig2_PDDProc::DecodeMMR( + if (!BHDC) + return nullptr; + +- auto pDict = pdfium::MakeUnique(GRAYMAX + 1); ++ auto pDict = std::make_unique(GRAYMAX + 1); + for (uint32_t GRAY = 0; GRAY <= GRAYMAX; ++GRAY) + pDict->HDPATS[GRAY] = BHDC->SubImage(HDPW * GRAY, 0, HDPW, HDPH); + return pDict; +@@ -75,10 +74,10 @@ std::unique_ptr CJBig2_PDDProc::DecodeMMR( + std::unique_ptr CJBig2_PDDProc::CreateGRDProc() { + uint32_t width = (GRAYMAX + 1) * HDPW; + uint32_t height = HDPH; +- if (width > JBIG2_MAX_IMAGE_SIZE || height > JBIG2_MAX_IMAGE_SIZE) ++ if (width > kJBig2MaxImageSize || height > kJBig2MaxImageSize) + return nullptr; + +- auto pGRD = pdfium::MakeUnique(); ++ auto pGRD = std::make_unique(); + pGRD->MMR = HDMMR; + pGRD->GBW = width; + pGRD->GBH = height; +diff --git a/core/fxcodec/jbig2/JBig2_PddProc.h b/core/fxcodec/jbig2/JBig2_PddProc.h +index be5fadfb3..b1bd93ead 100644 +--- a/core/fxcodec/jbig2/JBig2_PddProc.h ++++ b/core/fxcodec/jbig2/JBig2_PddProc.h +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,9 +7,9 @@ + #ifndef CORE_FXCODEC_JBIG2_JBIG2_PDDPROC_H_ + #define CORE_FXCODEC_JBIG2_JBIG2_PDDPROC_H_ + +-#include ++#include + +-#include "core/fxcrt/fx_system.h" ++#include + + class CJBig2_ArithDecoder; + class CJBig2_BitStream; +diff --git a/core/fxcodec/jbig2/JBig2_SddProc.cpp b/core/fxcodec/jbig2/JBig2_SddProc.cpp +index 6fd1226ee..801d9d74b 100644 +--- a/core/fxcodec/jbig2/JBig2_SddProc.cpp ++++ b/core/fxcodec/jbig2/JBig2_SddProc.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -21,7 +21,6 @@ + #include "core/fxcodec/jbig2/JBig2_SymbolDict.h" + #include "core/fxcodec/jbig2/JBig2_TrdProc.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/ptr_util.h" + + CJBig2_SDDProc::CJBig2_SDDProc() = default; + +@@ -31,57 +30,40 @@ std::unique_ptr CJBig2_SDDProc::DecodeArith( + CJBig2_ArithDecoder* pArithDecoder, + std::vector* gbContext, + std::vector* grContext) { +- std::vector> SDNEWSYMS; +- uint32_t HCHEIGHT, NSYMSDECODED; +- int32_t HCDH; +- uint32_t SYMWIDTH, TOTWIDTH; +- int32_t DW; +- uint32_t I, J, REFAGGNINST; +- std::vector EXFLAGS; +- uint32_t EXINDEX; +- bool CUREXFLAG; +- uint32_t EXRUNLENGTH; +- uint32_t nTmp; +- uint32_t SBNUMSYMS; +- uint8_t SBSYMCODELEN; +- int32_t RDXI, RDYI; +- uint32_t num_ex_syms; +- // Pointers are not owned +- std::vector SBSYMS; +- std::unique_ptr IAID; +- std::unique_ptr pDict; +- auto IADH = pdfium::MakeUnique(); +- auto IADW = pdfium::MakeUnique(); +- auto IAAI = pdfium::MakeUnique(); +- auto IARDX = pdfium::MakeUnique(); +- auto IARDY = pdfium::MakeUnique(); +- auto IAEX = pdfium::MakeUnique(); +- auto IADT = pdfium::MakeUnique(); +- auto IAFS = pdfium::MakeUnique(); +- auto IADS = pdfium::MakeUnique(); +- auto IAIT = pdfium::MakeUnique(); +- auto IARI = pdfium::MakeUnique(); +- auto IARDW = pdfium::MakeUnique(); +- auto IARDH = pdfium::MakeUnique(); +- nTmp = 0; +- while ((uint32_t)(1 << nTmp) < (SDNUMINSYMS + SDNUMNEWSYMS)) { +- nTmp++; ++ auto IADH = std::make_unique(); ++ auto IADW = std::make_unique(); ++ auto IAAI = std::make_unique(); ++ auto IARDX = std::make_unique(); ++ auto IARDY = std::make_unique(); ++ auto IAEX = std::make_unique(); ++ auto IADT = std::make_unique(); ++ auto IAFS = std::make_unique(); ++ auto IADS = std::make_unique(); ++ auto IAIT = std::make_unique(); ++ auto IARI = std::make_unique(); ++ auto IARDW = std::make_unique(); ++ auto IARDH = std::make_unique(); ++ ++ uint32_t SBSYMCODELENA = 0; ++ while ((uint32_t)(1 << SBSYMCODELENA) < (SDNUMINSYMS + SDNUMNEWSYMS)) { ++ SBSYMCODELENA++; + } +- IAID = pdfium::MakeUnique((uint8_t)nTmp); +- SDNEWSYMS.resize(SDNUMNEWSYMS); ++ auto IAID = std::make_unique((uint8_t)SBSYMCODELENA); + +- HCHEIGHT = 0; +- NSYMSDECODED = 0; ++ std::vector> SDNEWSYMS(SDNUMNEWSYMS); ++ uint32_t HCHEIGHT = 0; ++ uint32_t NSYMSDECODED = 0; + while (NSYMSDECODED < SDNUMNEWSYMS) { + std::unique_ptr BS; ++ int32_t HCDH; + IADH->Decode(pArithDecoder, &HCDH); + HCHEIGHT = HCHEIGHT + HCDH; +- if ((int)HCHEIGHT < 0 || (int)HCHEIGHT > JBIG2_MAX_IMAGE_SIZE) ++ if ((int)HCHEIGHT < 0 || (int)HCHEIGHT > kJBig2MaxImageSize) + return nullptr; + +- SYMWIDTH = 0; +- TOTWIDTH = 0; ++ uint32_t SYMWIDTH = 0; + for (;;) { ++ int32_t DW; + if (!IADW->Decode(pArithDecoder, &DW)) + break; + +@@ -89,24 +71,21 @@ std::unique_ptr CJBig2_SDDProc::DecodeArith( + return nullptr; + + SYMWIDTH = SYMWIDTH + DW; +- if ((int)SYMWIDTH < 0 || (int)SYMWIDTH > JBIG2_MAX_IMAGE_SIZE) ++ if ((int)SYMWIDTH < 0 || (int)SYMWIDTH > kJBig2MaxImageSize) + return nullptr; + + if (HCHEIGHT == 0 || SYMWIDTH == 0) { +- TOTWIDTH = TOTWIDTH + SYMWIDTH; +- SDNEWSYMS[NSYMSDECODED] = nullptr; +- NSYMSDECODED = NSYMSDECODED + 1; ++ ++NSYMSDECODED; + continue; + } +- TOTWIDTH = TOTWIDTH + SYMWIDTH; + if (SDREFAGG == 0) { +- auto pGRD = pdfium::MakeUnique(); +- pGRD->MMR = 0; ++ auto pGRD = std::make_unique(); ++ pGRD->MMR = false; + pGRD->GBW = SYMWIDTH; + pGRD->GBH = HCHEIGHT; + pGRD->GBTEMPLATE = SDTEMPLATE; +- pGRD->TPGDON = 0; +- pGRD->USESKIP = 0; ++ pGRD->TPGDON = false; ++ pGRD->USESKIP = false; + pGRD->GBAT[0] = SDAT[0]; + pGRD->GBAT[1] = SDAT[1]; + pGRD->GBAT[2] = SDAT[2]; +@@ -119,40 +98,41 @@ std::unique_ptr CJBig2_SDDProc::DecodeArith( + if (!BS) + return nullptr; + } else { ++ uint32_t REFAGGNINST; + IAAI->Decode(pArithDecoder, (int*)&REFAGGNINST); + if (REFAGGNINST > 1) { + // Huffman tables must not outlive |pDecoder|. +- auto SBHUFFFS = pdfium::MakeUnique(6); +- auto SBHUFFDS = pdfium::MakeUnique(8); +- auto SBHUFFDT = pdfium::MakeUnique(11); +- auto SBHUFFRDW = pdfium::MakeUnique(15); +- auto SBHUFFRDH = pdfium::MakeUnique(15); +- auto SBHUFFRDX = pdfium::MakeUnique(15); +- auto SBHUFFRDY = pdfium::MakeUnique(15); +- auto SBHUFFRSIZE = pdfium::MakeUnique(1); +- auto pDecoder = pdfium::MakeUnique(); ++ auto SBHUFFFS = std::make_unique(6); ++ auto SBHUFFDS = std::make_unique(8); ++ auto SBHUFFDT = std::make_unique(11); ++ auto SBHUFFRDW = std::make_unique(15); ++ auto SBHUFFRDH = std::make_unique(15); ++ auto SBHUFFRDX = std::make_unique(15); ++ auto SBHUFFRDY = std::make_unique(15); ++ auto SBHUFFRSIZE = std::make_unique(1); ++ auto pDecoder = std::make_unique(); + pDecoder->SBHUFF = SDHUFF; +- pDecoder->SBREFINE = 1; ++ pDecoder->SBREFINE = true; + pDecoder->SBW = SYMWIDTH; + pDecoder->SBH = HCHEIGHT; + pDecoder->SBNUMINSTANCES = REFAGGNINST; + pDecoder->SBSTRIPS = 1; + pDecoder->SBNUMSYMS = SDNUMINSYMS + NSYMSDECODED; +- SBNUMSYMS = pDecoder->SBNUMSYMS; +- nTmp = 0; +- while ((uint32_t)(1 << nTmp) < SBNUMSYMS) { ++ uint32_t nTmp = 0; ++ while ((uint32_t)(1 << nTmp) < pDecoder->SBNUMSYMS) { + nTmp++; + } +- SBSYMCODELEN = (uint8_t)nTmp; ++ uint8_t SBSYMCODELEN = (uint8_t)nTmp; + pDecoder->SBSYMCODELEN = SBSYMCODELEN; +- SBSYMS.resize(SBNUMSYMS); ++ std::vector SBSYMS; // Pointers are not owned ++ SBSYMS.resize(pDecoder->SBNUMSYMS); + std::copy(SDINSYMS, SDINSYMS + SDNUMINSYMS, SBSYMS.begin()); + for (size_t i = 0; i < NSYMSDECODED; ++i) + SBSYMS[i + SDNUMINSYMS] = SDNEWSYMS[i].get(); + pDecoder->SBSYMS = SBSYMS.data(); +- pDecoder->SBDEFPIXEL = 0; ++ pDecoder->SBDEFPIXEL = false; + pDecoder->SBCOMBOP = JBIG2_COMPOSE_OR; +- pDecoder->TRANSPOSED = 0; ++ pDecoder->TRANSPOSED = false; + pDecoder->REFCORNER = JBIG2_CORNER_TOPLEFT; + pDecoder->SBDSOFFSET = 0; + pDecoder->SBHUFFFS = SBHUFFFS.get(); +@@ -183,29 +163,29 @@ std::unique_ptr CJBig2_SDDProc::DecodeArith( + if (!BS) + return nullptr; + } else if (REFAGGNINST == 1) { +- SBNUMSYMS = SDNUMINSYMS + NSYMSDECODED; ++ uint32_t SBNUMSYMS = SDNUMINSYMS + NSYMSDECODED; + uint32_t IDI; + IAID->Decode(pArithDecoder, &IDI); +- IARDX->Decode(pArithDecoder, &RDXI); +- IARDY->Decode(pArithDecoder, &RDYI); + if (IDI >= SBNUMSYMS) + return nullptr; + +- SBSYMS.resize(SBNUMSYMS); +- std::copy(SDINSYMS, SDINSYMS + SDNUMINSYMS, SBSYMS.begin()); +- for (size_t i = 0; i < NSYMSDECODED; ++i) +- SBSYMS[i + SDNUMINSYMS] = SDNEWSYMS[i].get(); +- if (!SBSYMS[IDI]) ++ CJBig2_Image* sbsyms_idi = GetImage(IDI, SDNEWSYMS); ++ if (!sbsyms_idi) + return nullptr; + +- auto pGRRD = pdfium::MakeUnique(); ++ int32_t RDXI; ++ int32_t RDYI; ++ IARDX->Decode(pArithDecoder, &RDXI); ++ IARDY->Decode(pArithDecoder, &RDYI); ++ ++ auto pGRRD = std::make_unique(); + pGRRD->GRW = SYMWIDTH; + pGRRD->GRH = HCHEIGHT; + pGRRD->GRTEMPLATE = SDRTEMPLATE; +- pGRRD->GRREFERENCE = SBSYMS[IDI]; ++ pGRRD->GRREFERENCE = sbsyms_idi; + pGRRD->GRREFERENCEDX = RDXI; + pGRRD->GRREFERENCEDY = RDYI; +- pGRRD->TPGRON = 0; ++ pGRRD->TPGRON = false; + pGRRD->GRAT[0] = SDRAT[0]; + pGRRD->GRAT[1] = SDRAT[1]; + pGRRD->GRAT[2] = SDRAT[2]; +@@ -216,44 +196,46 @@ std::unique_ptr CJBig2_SDDProc::DecodeArith( + } + } + SDNEWSYMS[NSYMSDECODED] = std::move(BS); +- NSYMSDECODED = NSYMSDECODED + 1; ++ ++NSYMSDECODED; + } + } +- EXINDEX = 0; +- CUREXFLAG = 0; ++ ++ std::vector EXFLAGS; + EXFLAGS.resize(SDNUMINSYMS + SDNUMNEWSYMS); +- num_ex_syms = 0; ++ bool CUREXFLAG = false; ++ uint32_t EXINDEX = 0; ++ uint32_t num_ex_syms = 0; + while (EXINDEX < SDNUMINSYMS + SDNUMNEWSYMS) { ++ uint32_t EXRUNLENGTH; + IAEX->Decode(pArithDecoder, (int*)&EXRUNLENGTH); +- if (EXINDEX + EXRUNLENGTH > SDNUMINSYMS + SDNUMNEWSYMS) ++ FX_SAFE_UINT32 new_ex_size = EXINDEX; ++ new_ex_size += EXRUNLENGTH; ++ if (!new_ex_size.IsValid() || ++ new_ex_size.ValueOrDie() > SDNUMINSYMS + SDNUMNEWSYMS) { + return nullptr; +- +- if (EXRUNLENGTH != 0) { +- for (I = EXINDEX; I < EXINDEX + EXRUNLENGTH; I++) { +- if (CUREXFLAG) +- num_ex_syms++; +- EXFLAGS[I] = CUREXFLAG; +- } + } +- EXINDEX = EXINDEX + EXRUNLENGTH; ++ ++ if (CUREXFLAG) ++ num_ex_syms += EXRUNLENGTH; ++ std::fill_n(EXFLAGS.begin() + EXINDEX, EXRUNLENGTH, CUREXFLAG); ++ EXINDEX = new_ex_size.ValueOrDie(); + CUREXFLAG = !CUREXFLAG; + } + if (num_ex_syms > SDNUMEXSYMS) + return nullptr; + +- pDict = pdfium::MakeUnique(); +- J = 0; +- for (I = 0; I < SDNUMINSYMS + SDNUMNEWSYMS; I++) { +- if (!EXFLAGS[I] || J >= SDNUMEXSYMS) ++ std::unique_ptr pDict = ++ std::make_unique(); ++ for (uint32_t i = 0, j = 0; i < SDNUMINSYMS + SDNUMNEWSYMS; ++i) { ++ if (!EXFLAGS[i] || j >= SDNUMEXSYMS) + continue; +- if (I < SDNUMINSYMS) { +- pDict->AddImage(SDINSYMS[I] +- ? pdfium::MakeUnique(*SDINSYMS[I]) +- : nullptr); ++ if (i < SDNUMINSYMS) { ++ pDict->AddImage(SDINSYMS[i] ? std::make_unique(*SDINSYMS[i]) ++ : nullptr); + } else { +- pDict->AddImage(std::move(SDNEWSYMS[I - SDNUMINSYMS])); ++ pDict->AddImage(std::move(SDNEWSYMS[i - SDNUMINSYMS])); + } +- ++J; ++ ++j; + } + return pDict; + } +@@ -262,51 +244,31 @@ std::unique_ptr CJBig2_SDDProc::DecodeHuffman( + CJBig2_BitStream* pStream, + std::vector* gbContext, + std::vector* grContext) { +- std::vector> SDNEWSYMS; ++ auto pHuffmanDecoder = std::make_unique(pStream); ++ ++ std::vector> SDNEWSYMS(SDNUMNEWSYMS); + std::vector SDNEWSYMWIDTHS; +- uint32_t HCHEIGHT, NSYMSDECODED; +- int32_t HCDH; +- uint32_t SYMWIDTH, TOTWIDTH, HCFIRSTSYM; +- int32_t DW; +- uint32_t I, J, REFAGGNINST; +- std::vector EXFLAGS; +- uint32_t EXINDEX; +- bool CUREXFLAG; +- uint32_t EXRUNLENGTH; +- int32_t nVal; +- uint32_t nTmp; +- uint32_t SBNUMSYMS; +- uint8_t SBSYMCODELEN; +- uint32_t IDI; +- int32_t RDXI, RDYI; +- uint32_t BMSIZE; +- uint32_t num_ex_syms; +- // Pointers are not owned +- std::vector SBSYMS; +- auto pHuffmanDecoder = pdfium::MakeUnique(pStream); +- SDNEWSYMS.resize(SDNUMNEWSYMS); + if (SDREFAGG == 0) + SDNEWSYMWIDTHS.resize(SDNUMNEWSYMS); +- auto pDict = pdfium::MakeUnique(); +- std::unique_ptr pTable; +- +- HCHEIGHT = 0; +- NSYMSDECODED = 0; ++ uint32_t HCHEIGHT = 0; ++ uint32_t NSYMSDECODED = 0; + std::unique_ptr BS; + while (NSYMSDECODED < SDNUMNEWSYMS) { +- if (pHuffmanDecoder->DecodeAValue(SDHUFFDH.Get(), &HCDH) != 0) ++ int32_t HCDH; ++ if (pHuffmanDecoder->DecodeAValue(SDHUFFDH, &HCDH) != 0) + return nullptr; + + HCHEIGHT = HCHEIGHT + HCDH; +- if ((int)HCHEIGHT < 0 || (int)HCHEIGHT > JBIG2_MAX_IMAGE_SIZE) ++ if ((int)HCHEIGHT < 0 || (int)HCHEIGHT > kJBig2MaxImageSize) + return nullptr; + +- SYMWIDTH = 0; +- TOTWIDTH = 0; +- HCFIRSTSYM = NSYMSDECODED; ++ uint32_t SYMWIDTH = 0; ++ uint32_t TOTWIDTH = 0; ++ uint32_t HCFIRSTSYM = NSYMSDECODED; + for (;;) { +- nVal = pHuffmanDecoder->DecodeAValue(SDHUFFDW.Get(), &DW); +- if (nVal == JBIG2_OOB) ++ int32_t DW; ++ int32_t nVal = pHuffmanDecoder->DecodeAValue(SDHUFFDW, &DW); ++ if (nVal == kJBig2OOB) + break; + if (nVal != 0) + return nullptr; +@@ -314,57 +276,57 @@ std::unique_ptr CJBig2_SDDProc::DecodeHuffman( + return nullptr; + + SYMWIDTH = SYMWIDTH + DW; +- if ((int)SYMWIDTH < 0 || (int)SYMWIDTH > JBIG2_MAX_IMAGE_SIZE) ++ if ((int)SYMWIDTH < 0 || (int)SYMWIDTH > kJBig2MaxImageSize) + return nullptr; ++ ++ TOTWIDTH += SYMWIDTH; + if (HCHEIGHT == 0 || SYMWIDTH == 0) { +- TOTWIDTH = TOTWIDTH + SYMWIDTH; +- SDNEWSYMS[NSYMSDECODED] = nullptr; +- NSYMSDECODED = NSYMSDECODED + 1; ++ ++NSYMSDECODED; + continue; + } +- TOTWIDTH = TOTWIDTH + SYMWIDTH; + if (SDREFAGG == 1) { +- if (pHuffmanDecoder->DecodeAValue(SDHUFFAGGINST.Get(), +- (int*)&REFAGGNINST) != 0) { ++ uint32_t REFAGGNINST; ++ if (pHuffmanDecoder->DecodeAValue(SDHUFFAGGINST, (int*)&REFAGGNINST) != ++ 0) { + return nullptr; + } + BS = nullptr; + if (REFAGGNINST > 1) { + // Huffman tables must outlive |pDecoder|. +- auto SBHUFFFS = pdfium::MakeUnique(6); +- auto SBHUFFDS = pdfium::MakeUnique(8); +- auto SBHUFFDT = pdfium::MakeUnique(11); +- auto SBHUFFRDW = pdfium::MakeUnique(15); +- auto SBHUFFRDH = pdfium::MakeUnique(15); +- auto SBHUFFRDX = pdfium::MakeUnique(15); +- auto SBHUFFRDY = pdfium::MakeUnique(15); +- auto SBHUFFRSIZE = pdfium::MakeUnique(1); +- auto pDecoder = pdfium::MakeUnique(); ++ auto SBHUFFFS = std::make_unique(6); ++ auto SBHUFFDS = std::make_unique(8); ++ auto SBHUFFDT = std::make_unique(11); ++ auto SBHUFFRDW = std::make_unique(15); ++ auto SBHUFFRDH = std::make_unique(15); ++ auto SBHUFFRDX = std::make_unique(15); ++ auto SBHUFFRDY = std::make_unique(15); ++ auto SBHUFFRSIZE = std::make_unique(1); ++ auto pDecoder = std::make_unique(); + pDecoder->SBHUFF = SDHUFF; +- pDecoder->SBREFINE = 1; ++ pDecoder->SBREFINE = true; + pDecoder->SBW = SYMWIDTH; + pDecoder->SBH = HCHEIGHT; + pDecoder->SBNUMINSTANCES = REFAGGNINST; + pDecoder->SBSTRIPS = 1; + pDecoder->SBNUMSYMS = SDNUMINSYMS + NSYMSDECODED; +- SBNUMSYMS = pDecoder->SBNUMSYMS; +- std::vector SBSYMCODES(SBNUMSYMS); +- nTmp = 1; +- while (static_cast(1 << nTmp) < SBNUMSYMS) ++ std::vector SBSYMCODES(pDecoder->SBNUMSYMS); ++ uint32_t nTmp = 1; ++ while (static_cast(1 << nTmp) < pDecoder->SBNUMSYMS) + ++nTmp; +- for (I = 0; I < SBNUMSYMS; ++I) { +- SBSYMCODES[I].codelen = nTmp; +- SBSYMCODES[I].code = I; ++ for (uint32_t i = 0; i < pDecoder->SBNUMSYMS; ++i) { ++ SBSYMCODES[i].codelen = nTmp; ++ SBSYMCODES[i].code = i; + } + pDecoder->SBSYMCODES = std::move(SBSYMCODES); +- SBSYMS.resize(SBNUMSYMS); ++ std::vector SBSYMS; // Pointers are not owned ++ SBSYMS.resize(pDecoder->SBNUMSYMS); + std::copy(SDINSYMS, SDINSYMS + SDNUMINSYMS, SBSYMS.begin()); + for (size_t i = 0; i < NSYMSDECODED; ++i) + SBSYMS[i + SDNUMINSYMS] = SDNEWSYMS[i].get(); + pDecoder->SBSYMS = SBSYMS.data(); +- pDecoder->SBDEFPIXEL = 0; ++ pDecoder->SBDEFPIXEL = false; + pDecoder->SBCOMBOP = JBIG2_COMPOSE_OR; +- pDecoder->TRANSPOSED = 0; ++ pDecoder->TRANSPOSED = false; + pDecoder->REFCORNER = JBIG2_CORNER_TOPLEFT; + pDecoder->SBDSOFFSET = 0; + pDecoder->SBHUFFFS = SBHUFFFS.get(); +@@ -385,13 +347,14 @@ std::unique_ptr CJBig2_SDDProc::DecodeHuffman( + return nullptr; + + } else if (REFAGGNINST == 1) { +- SBNUMSYMS = SDNUMINSYMS + SDNUMNEWSYMS; +- nTmp = 1; ++ uint32_t SBNUMSYMS = SDNUMINSYMS + SDNUMNEWSYMS; ++ uint32_t nTmp = 1; + while ((uint32_t)(1 << nTmp) < SBNUMSYMS) { + nTmp++; + } +- SBSYMCODELEN = (uint8_t)nTmp; ++ uint8_t SBSYMCODELEN = (uint8_t)nTmp; + uint32_t uVal = 0; ++ uint32_t IDI; + for (;;) { + if (pStream->read1Bit(&nTmp) != 0) + return nullptr; +@@ -404,32 +367,37 @@ std::unique_ptr CJBig2_SDDProc::DecodeHuffman( + if (IDI < SBNUMSYMS) + break; + } +- auto SBHUFFRDX = pdfium::MakeUnique(15); +- auto SBHUFFRSIZE = pdfium::MakeUnique(1); ++ ++ CJBig2_Image* sbsyms_idi = GetImage(IDI, SDNEWSYMS); ++ if (!sbsyms_idi) ++ return nullptr; ++ ++ auto SBHUFFRDX = std::make_unique(15); ++ auto SBHUFFRSIZE = std::make_unique(1); ++ int32_t RDXI; ++ int32_t RDYI; + if ((pHuffmanDecoder->DecodeAValue(SBHUFFRDX.get(), &RDXI) != 0) || + (pHuffmanDecoder->DecodeAValue(SBHUFFRDX.get(), &RDYI) != 0) || + (pHuffmanDecoder->DecodeAValue(SBHUFFRSIZE.get(), &nVal) != 0)) { + return nullptr; + } ++ + pStream->alignByte(); + nTmp = pStream->getOffset(); +- SBSYMS.resize(SBNUMSYMS); +- std::copy(SDINSYMS, SDINSYMS + SDNUMINSYMS, SBSYMS.begin()); +- for (size_t i = 0; i < NSYMSDECODED; ++i) +- SBSYMS[i + SDNUMINSYMS] = SDNEWSYMS[i].get(); +- auto pGRRD = pdfium::MakeUnique(); ++ ++ auto pGRRD = std::make_unique(); + pGRRD->GRW = SYMWIDTH; + pGRRD->GRH = HCHEIGHT; + pGRRD->GRTEMPLATE = SDRTEMPLATE; +- pGRRD->GRREFERENCE = SBSYMS[IDI]; ++ pGRRD->GRREFERENCE = sbsyms_idi; + pGRRD->GRREFERENCEDX = RDXI; + pGRRD->GRREFERENCEDY = RDYI; +- pGRRD->TPGRON = 0; ++ pGRRD->TPGRON = false; + pGRRD->GRAT[0] = SDRAT[0]; + pGRRD->GRAT[1] = SDRAT[1]; + pGRRD->GRAT[2] = SDRAT[2]; + pGRRD->GRAT[3] = SDRAT[3]; +- auto pArithDecoder = pdfium::MakeUnique(pStream); ++ auto pArithDecoder = std::make_unique(pStream); + BS = pGRRD->Decode(pArithDecoder.get(), grContext->data()); + if (!BS) + return nullptr; +@@ -443,89 +411,99 @@ std::unique_ptr CJBig2_SDDProc::DecodeHuffman( + } + if (SDREFAGG == 0) + SDNEWSYMWIDTHS[NSYMSDECODED] = SYMWIDTH; +- NSYMSDECODED = NSYMSDECODED + 1; ++ ++NSYMSDECODED; + } + if (SDREFAGG == 0) { +- if (pHuffmanDecoder->DecodeAValue(SDHUFFBMSIZE.Get(), +- (int32_t*)&BMSIZE) != 0) { ++ uint32_t BMSIZE; ++ if (pHuffmanDecoder->DecodeAValue(SDHUFFBMSIZE, (int32_t*)&BMSIZE) != 0) { + return nullptr; + } + pStream->alignByte(); + std::unique_ptr BHC; + if (BMSIZE == 0) { +- FX_SAFE_UINT32 safe_stride = TOTWIDTH; +- safe_stride += 7; +- safe_stride /= 8; +- FX_SAFE_UINT32 safe_image_size = safe_stride; ++ if (static_cast(TOTWIDTH) > kJBig2MaxImageSize) ++ return nullptr; ++ ++ // OK to not use FX_SAFE_UINT32 to calculate `stride` because ++ // `kJBig2MaxImageSize` is limiting the size. ++ const uint32_t stride = (TOTWIDTH + 7) / 8; ++ FX_SAFE_UINT32 safe_image_size = stride; + safe_image_size *= HCHEIGHT; + if (!safe_image_size.IsValid() || + pStream->getByteLeft() < safe_image_size.ValueOrDie()) { + return nullptr; + } + +- const uint32_t stride = safe_stride.ValueOrDie(); +- BHC = pdfium::MakeUnique(TOTWIDTH, HCHEIGHT); +- for (I = 0; I < HCHEIGHT; I++) { +- memcpy(BHC->data() + I * BHC->stride(), pStream->getPointer(), ++ BHC = std::make_unique(TOTWIDTH, HCHEIGHT); ++ for (uint32_t i = 0; i < HCHEIGHT; ++i) { ++ memcpy(BHC->data() + i * BHC->stride(), pStream->getPointer(), + stride); + pStream->offset(stride); + } + } else { +- auto pGRD = pdfium::MakeUnique(); +- pGRD->MMR = 1; ++ auto pGRD = std::make_unique(); ++ pGRD->MMR = true; + pGRD->GBW = TOTWIDTH; + pGRD->GBH = HCHEIGHT; + pGRD->StartDecodeMMR(&BHC, pStream); + pStream->alignByte(); + } +- nTmp = 0; + if (!BHC) + continue; + +- for (I = HCFIRSTSYM; I < NSYMSDECODED; ++I) { +- SDNEWSYMS[I] = BHC->SubImage(nTmp, 0, SDNEWSYMWIDTHS[I], HCHEIGHT); +- nTmp += SDNEWSYMWIDTHS[I]; ++ uint32_t nTmp = 0; ++ for (uint32_t i = HCFIRSTSYM; i < NSYMSDECODED; ++i) { ++ SDNEWSYMS[i] = BHC->SubImage(nTmp, 0, SDNEWSYMWIDTHS[i], HCHEIGHT); ++ nTmp += SDNEWSYMWIDTHS[i]; + } + } + } +- EXINDEX = 0; +- CUREXFLAG = 0; +- pTable = pdfium::MakeUnique(1); ++ ++ std::unique_ptr pTable = ++ std::make_unique(1); ++ std::vector EXFLAGS; + EXFLAGS.resize(SDNUMINSYMS + SDNUMNEWSYMS); +- num_ex_syms = 0; ++ bool CUREXFLAG = false; ++ uint32_t EXINDEX = 0; ++ uint32_t num_ex_syms = 0; + while (EXINDEX < SDNUMINSYMS + SDNUMNEWSYMS) { ++ uint32_t EXRUNLENGTH; + if (pHuffmanDecoder->DecodeAValue(pTable.get(), (int*)&EXRUNLENGTH) != 0) + return nullptr; + +- if (EXINDEX + EXRUNLENGTH > SDNUMINSYMS + SDNUMNEWSYMS) ++ FX_SAFE_UINT32 new_ex_size = EXINDEX; ++ new_ex_size += EXRUNLENGTH; ++ if (!new_ex_size.IsValid() || ++ new_ex_size.ValueOrDie() > SDNUMINSYMS + SDNUMNEWSYMS) { + return nullptr; +- +- if (EXRUNLENGTH != 0) { +- for (I = EXINDEX; I < EXINDEX + EXRUNLENGTH; ++I) { +- if (CUREXFLAG) +- num_ex_syms++; +- +- EXFLAGS[I] = CUREXFLAG; +- } + } +- EXINDEX = EXINDEX + EXRUNLENGTH; ++ ++ if (CUREXFLAG) ++ num_ex_syms += EXRUNLENGTH; ++ std::fill_n(EXFLAGS.begin() + EXINDEX, EXRUNLENGTH, CUREXFLAG); ++ EXINDEX = new_ex_size.ValueOrDie(); + CUREXFLAG = !CUREXFLAG; + } + if (num_ex_syms > SDNUMEXSYMS) + return nullptr; + +- J = 0; +- for (I = 0; I < SDNUMINSYMS + SDNUMNEWSYMS; ++I) { +- if (!EXFLAGS[I] || J >= SDNUMEXSYMS) ++ auto pDict = std::make_unique(); ++ for (uint32_t i = 0, j = 0; i < SDNUMINSYMS + SDNUMNEWSYMS; ++i) { ++ if (!EXFLAGS[i] || j >= SDNUMEXSYMS) + continue; +- if (I < SDNUMINSYMS) { +- pDict->AddImage(SDINSYMS[I] +- ? pdfium::MakeUnique(*SDINSYMS[I]) +- : nullptr); ++ if (i < SDNUMINSYMS) { ++ pDict->AddImage(SDINSYMS[i] ? std::make_unique(*SDINSYMS[i]) ++ : nullptr); + } else { +- pDict->AddImage(std::move(SDNEWSYMS[I - SDNUMINSYMS])); ++ pDict->AddImage(std::move(SDNEWSYMS[i - SDNUMINSYMS])); + } +- ++J; ++ ++j; + } + return pDict; + } ++ ++CJBig2_Image* CJBig2_SDDProc::GetImage( ++ uint32_t i, ++ pdfium::span> new_syms) const { ++ return i < SDNUMINSYMS ? SDINSYMS[i] : new_syms[i - SDNUMINSYMS].get(); ++} +diff --git a/core/fxcodec/jbig2/JBig2_SddProc.h b/core/fxcodec/jbig2/JBig2_SddProc.h +index abef01086..09478ebb0 100644 +--- a/core/fxcodec/jbig2/JBig2_SddProc.h ++++ b/core/fxcodec/jbig2/JBig2_SddProc.h +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,14 @@ + #ifndef CORE_FXCODEC_JBIG2_JBIG2_SDDPROC_H_ + #define CORE_FXCODEC_JBIG2_JBIG2_SDDPROC_H_ + ++#include ++ + #include + #include + + #include "core/fxcodec/jbig2/JBig2_ArithDecoder.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "third_party/base/span.h" + + class CJBig2_BitStream; + class CJBig2_HuffmanTable; +@@ -48,6 +50,13 @@ class CJBig2_SDDProc { + UnownedPtr SDHUFFAGGINST; + int8_t SDAT[8]; + int8_t SDRAT[4]; ++ ++ private: ++ // Reads from `SDINSYMS` if `i` is in-bounds. Otherwise, reduce `i` by ++ // `SDNUMINSYMS` and read from `new_syms` at the new index. ++ CJBig2_Image* GetImage( ++ uint32_t i, ++ pdfium::span> new_syms) const; + }; + + #endif // CORE_FXCODEC_JBIG2_JBIG2_SDDPROC_H_ +diff --git a/core/fxcodec/jbig2/JBig2_Segment.cpp b/core/fxcodec/jbig2/JBig2_Segment.cpp +index 91fa2d9b5..63981b0b4 100644 +--- a/core/fxcodec/jbig2/JBig2_Segment.cpp ++++ b/core/fxcodec/jbig2/JBig2_Segment.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,17 +6,6 @@ + + #include "core/fxcodec/jbig2/JBig2_Segment.h" + +-CJBig2_Segment::CJBig2_Segment() +- : m_dwNumber(0), +- m_nReferred_to_segment_count(0), +- m_dwPage_association(0), +- m_dwData_length(0), +- m_dwHeader_Length(0), +- m_dwObjNum(0), +- m_dwDataOffset(0), +- m_State(JBIG2_SEGMENT_HEADER_UNPARSED), +- m_nResultType(JBIG2_VOID_POINTER) { +- m_cFlags.c = 0; +-} ++CJBig2_Segment::CJBig2_Segment() = default; + +-CJBig2_Segment::~CJBig2_Segment() {} ++CJBig2_Segment::~CJBig2_Segment() = default; +diff --git a/core/fxcodec/jbig2/JBig2_Segment.h b/core/fxcodec/jbig2/JBig2_Segment.h +index 45e2a3dd0..f588f0bb4 100644 +--- a/core/fxcodec/jbig2/JBig2_Segment.h ++++ b/core/fxcodec/jbig2/JBig2_Segment.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -36,25 +36,24 @@ class CJBig2_Segment { + CJBig2_Segment(); + ~CJBig2_Segment(); + +- uint32_t m_dwNumber; ++ uint32_t m_dwNumber = 0; + union { + struct { + uint8_t type : 6; + uint8_t page_association_size : 1; + uint8_t deferred_non_retain : 1; + } s; +- uint8_t c; ++ uint8_t c = 0; + } m_cFlags; +- int32_t m_nReferred_to_segment_count; ++ int32_t m_nReferred_to_segment_count = 0; + std::vector m_Referred_to_segment_numbers; +- uint32_t m_dwPage_association; +- uint32_t m_dwData_length; +- +- uint32_t m_dwHeader_Length; +- uint32_t m_dwObjNum; +- uint32_t m_dwDataOffset; +- JBig2_SegmentState m_State; +- JBig2_ResultType m_nResultType; ++ uint32_t m_dwPage_association = 0; ++ uint32_t m_dwData_length = 0; ++ uint32_t m_dwHeader_Length = 0; ++ uint32_t m_dwDataOffset = 0; ++ uint64_t m_Key = 0; ++ JBig2_SegmentState m_State = JBIG2_SEGMENT_HEADER_UNPARSED; ++ JBig2_ResultType m_nResultType = JBIG2_VOID_POINTER; + std::unique_ptr m_SymbolDict; + std::unique_ptr m_PatternDict; + std::unique_ptr m_Image; +diff --git a/core/fxcodec/jbig2/JBig2_SymbolDict.cpp b/core/fxcodec/jbig2/JBig2_SymbolDict.cpp +index a6fafb487..431441dac 100644 +--- a/core/fxcodec/jbig2/JBig2_SymbolDict.cpp ++++ b/core/fxcodec/jbig2/JBig2_SymbolDict.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,16 +7,15 @@ + #include "core/fxcodec/jbig2/JBig2_SymbolDict.h" + + #include "core/fxcodec/jbig2/JBig2_Image.h" +-#include "third_party/base/ptr_util.h" + + CJBig2_SymbolDict::CJBig2_SymbolDict() {} + +-CJBig2_SymbolDict::~CJBig2_SymbolDict() {} ++CJBig2_SymbolDict::~CJBig2_SymbolDict() = default; + + std::unique_ptr CJBig2_SymbolDict::DeepCopy() const { +- auto dst = pdfium::MakeUnique(); ++ auto dst = std::make_unique(); + for (const auto& image : m_SDEXSYMS) { +- dst->m_SDEXSYMS.push_back(image ? pdfium::MakeUnique(*image) ++ dst->m_SDEXSYMS.push_back(image ? std::make_unique(*image) + : nullptr); + } + dst->m_gbContext = m_gbContext; +diff --git a/core/fxcodec/jbig2/JBig2_SymbolDict.h b/core/fxcodec/jbig2/JBig2_SymbolDict.h +index 956c83b12..ffdd875c8 100644 +--- a/core/fxcodec/jbig2/JBig2_SymbolDict.h ++++ b/core/fxcodec/jbig2/JBig2_SymbolDict.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcodec/jbig2/JBig2_TrdProc.cpp b/core/fxcodec/jbig2/JBig2_TrdProc.cpp +index 7aa598997..0ea171b1c 100644 +--- a/core/fxcodec/jbig2/JBig2_TrdProc.cpp ++++ b/core/fxcodec/jbig2/JBig2_TrdProc.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,27 +14,26 @@ + #include "core/fxcodec/jbig2/JBig2_HuffmanDecoder.h" + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxcrt/maybe_owned.h" +-#include "third_party/base/optional.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + namespace { + +-Optional CheckTRDDimension(uint32_t dimension, int32_t delta) { ++absl::optional CheckTRDDimension(uint32_t dimension, int32_t delta) { + FX_SAFE_UINT32 result = dimension; + result += delta; + if (!result.IsValid()) +- return {}; +- return {result.ValueOrDie()}; ++ return absl::nullopt; ++ return result.ValueOrDie(); + } + +-Optional CheckTRDReferenceDimension(int32_t dimension, +- uint32_t shift, +- int32_t offset) { ++absl::optional CheckTRDReferenceDimension(int32_t dimension, ++ uint32_t shift, ++ int32_t offset) { + FX_SAFE_INT32 result = offset; + result += dimension >> shift; + if (!result.IsValid()) +- return {}; +- return {result.ValueOrDie()}; ++ return absl::nullopt; ++ return result.ValueOrDie(); + } + + } // namespace +@@ -50,14 +49,14 @@ CJBig2_TRDProc::~CJBig2_TRDProc() = default; + std::unique_ptr CJBig2_TRDProc::DecodeHuffman( + CJBig2_BitStream* pStream, + JBig2ArithCtx* grContext) { +- auto SBREG = pdfium::MakeUnique(SBW, SBH); ++ auto SBREG = std::make_unique(SBW, SBH); + if (!SBREG->data()) + return nullptr; + + SBREG->Fill(SBDEFPIXEL); + int32_t INITIAL_STRIPT; +- auto pHuffmanDecoder = pdfium::MakeUnique(pStream); +- if (pHuffmanDecoder->DecodeAValue(SBHUFFDT.Get(), &INITIAL_STRIPT) != 0) ++ auto pHuffmanDecoder = std::make_unique(pStream); ++ if (pHuffmanDecoder->DecodeAValue(SBHUFFDT, &INITIAL_STRIPT) != 0) + return nullptr; + + FX_SAFE_INT32 STRIPT = INITIAL_STRIPT; +@@ -67,7 +66,7 @@ std::unique_ptr CJBig2_TRDProc::DecodeHuffman( + uint32_t NINSTANCES = 0; + while (NINSTANCES < SBNUMINSTANCES) { + int32_t INITIAL_DT; +- if (pHuffmanDecoder->DecodeAValue(SBHUFFDT.Get(), &INITIAL_DT) != 0) ++ if (pHuffmanDecoder->DecodeAValue(SBHUFFDT, &INITIAL_DT) != 0) + return nullptr; + + FX_SAFE_INT32 DT = INITIAL_DT; +@@ -78,7 +77,7 @@ std::unique_ptr CJBig2_TRDProc::DecodeHuffman( + for (;;) { + if (bFirst) { + int32_t DFS; +- if (pHuffmanDecoder->DecodeAValue(SBHUFFFS.Get(), &DFS) != 0) ++ if (pHuffmanDecoder->DecodeAValue(SBHUFFFS, &DFS) != 0) + return nullptr; + + FIRSTS += DFS; +@@ -86,8 +85,8 @@ std::unique_ptr CJBig2_TRDProc::DecodeHuffman( + bFirst = false; + } else { + int32_t IDS; +- int32_t nVal = pHuffmanDecoder->DecodeAValue(SBHUFFDS.Get(), &IDS); +- if (nVal == JBIG2_OOB) ++ int32_t nVal = pHuffmanDecoder->DecodeAValue(SBHUFFDS, &IDS); ++ if (nVal == kJBig2OOB) + break; + + if (nVal != 0) +@@ -134,7 +133,7 @@ std::unique_ptr CJBig2_TRDProc::DecodeHuffman( + if (IDI < SBNUMSYMS) + break; + } +- bool RI = 0; ++ bool RI = false; + if (SBREFINE != 0 && pStream->read1Bit(&RI) != 0) + return nullptr; + +@@ -147,12 +146,11 @@ std::unique_ptr CJBig2_TRDProc::DecodeHuffman( + int32_t RDXI; + int32_t RDYI; + int32_t HUFFRSIZE; +- if ((pHuffmanDecoder->DecodeAValue(SBHUFFRDW.Get(), &RDWI) != 0) || +- (pHuffmanDecoder->DecodeAValue(SBHUFFRDH.Get(), &RDHI) != 0) || +- (pHuffmanDecoder->DecodeAValue(SBHUFFRDX.Get(), &RDXI) != 0) || +- (pHuffmanDecoder->DecodeAValue(SBHUFFRDY.Get(), &RDYI) != 0) || +- (pHuffmanDecoder->DecodeAValue(SBHUFFRSIZE.Get(), &HUFFRSIZE) != +- 0)) { ++ if ((pHuffmanDecoder->DecodeAValue(SBHUFFRDW, &RDWI) != 0) || ++ (pHuffmanDecoder->DecodeAValue(SBHUFFRDH, &RDHI) != 0) || ++ (pHuffmanDecoder->DecodeAValue(SBHUFFRDX, &RDXI) != 0) || ++ (pHuffmanDecoder->DecodeAValue(SBHUFFRDY, &RDYI) != 0) || ++ (pHuffmanDecoder->DecodeAValue(SBHUFFRSIZE, &HUFFRSIZE) != 0)) { + return nullptr; + } + pStream->alignByte(); +@@ -161,32 +159,32 @@ std::unique_ptr CJBig2_TRDProc::DecodeHuffman( + if (!IBOI) + return nullptr; + +- Optional WOI = CheckTRDDimension(IBOI->width(), RDWI); +- Optional HOI = CheckTRDDimension(IBOI->height(), RDHI); +- if (!WOI || !HOI) ++ absl::optional WOI = CheckTRDDimension(IBOI->width(), RDWI); ++ absl::optional HOI = CheckTRDDimension(IBOI->height(), RDHI); ++ if (!WOI.has_value() || !HOI.has_value()) + return nullptr; + +- Optional GRREFERENCEDX = ++ absl::optional GRREFERENCEDX = + CheckTRDReferenceDimension(RDWI, 2, RDXI); +- Optional GRREFERENCEDY = ++ absl::optional GRREFERENCEDY = + CheckTRDReferenceDimension(RDHI, 2, RDYI); +- if (!GRREFERENCEDX || !GRREFERENCEDY) ++ if (!GRREFERENCEDX.has_value() || !GRREFERENCEDY.has_value()) + return nullptr; + +- auto pGRRD = pdfium::MakeUnique(); ++ auto pGRRD = std::make_unique(); + pGRRD->GRW = WOI.value(); + pGRRD->GRH = HOI.value(); + pGRRD->GRTEMPLATE = SBRTEMPLATE; + pGRRD->GRREFERENCE = IBOI; + pGRRD->GRREFERENCEDX = GRREFERENCEDX.value(); + pGRRD->GRREFERENCEDY = GRREFERENCEDY.value(); +- pGRRD->TPGRON = 0; ++ pGRRD->TPGRON = false; + pGRRD->GRAT[0] = SBRAT[0]; + pGRRD->GRAT[1] = SBRAT[1]; + pGRRD->GRAT[2] = SBRAT[2]; + pGRRD->GRAT[3] = SBRAT[3]; + +- auto pArithDecoder = pdfium::MakeUnique(pStream); ++ auto pArithDecoder = std::make_unique(pStream); + IBI = pGRRD->Decode(pArithDecoder.get(), grContext); + if (!IBI) + return nullptr; +@@ -226,7 +224,7 @@ std::unique_ptr CJBig2_TRDProc::DecodeArith( + CJBig2_ArithDecoder* pArithDecoder, + JBig2ArithCtx* grContext, + JBig2IntDecoderState* pIDS) { +- auto SBREG = pdfium::MakeUnique(SBW, SBH); ++ auto SBREG = std::make_unique(SBW, SBH); + if (!SBREG->data()) + return nullptr; + +@@ -234,7 +232,7 @@ std::unique_ptr CJBig2_TRDProc::DecodeArith( + if (pIDS) + pIADT = pIDS->IADT; + else +- pIADT = pdfium::MakeUnique(); ++ pIADT = std::make_unique(); + int32_t INITIAL_STRIPT; + if (!pIADT->Decode(pArithDecoder, &INITIAL_STRIPT)) + return nullptr; +@@ -259,15 +257,15 @@ std::unique_ptr CJBig2_TRDProc::DecodeArith( + pIARDY = pIDS->IARDY; + pIAID = pIDS->IAID; + } else { +- pIAFS = pdfium::MakeUnique(); +- pIADS = pdfium::MakeUnique(); +- pIAIT = pdfium::MakeUnique(); +- pIARI = pdfium::MakeUnique(); +- pIARDW = pdfium::MakeUnique(); +- pIARDH = pdfium::MakeUnique(); +- pIARDX = pdfium::MakeUnique(); +- pIARDY = pdfium::MakeUnique(); +- pIAID = pdfium::MakeUnique(SBSYMCODELEN); ++ pIAFS = std::make_unique(); ++ pIADS = std::make_unique(); ++ pIAIT = std::make_unique(); ++ pIARI = std::make_unique(); ++ pIARDW = std::make_unique(); ++ pIARDH = std::make_unique(); ++ pIARDX = std::make_unique(); ++ pIARDY = std::make_unique(); ++ pIAID = std::make_unique(SBSYMCODELEN); + } + + SBREG->Fill(SBDEFPIXEL); +@@ -341,26 +339,26 @@ std::unique_ptr CJBig2_TRDProc::DecodeArith( + if (!IBOI) + return nullptr; + +- Optional WOI = CheckTRDDimension(IBOI->width(), RDWI); +- Optional HOI = CheckTRDDimension(IBOI->height(), RDHI); +- if (!WOI || !HOI) ++ absl::optional WOI = CheckTRDDimension(IBOI->width(), RDWI); ++ absl::optional HOI = CheckTRDDimension(IBOI->height(), RDHI); ++ if (!WOI.has_value() || !HOI.has_value()) + return nullptr; + +- Optional GRREFERENCEDX = ++ absl::optional GRREFERENCEDX = + CheckTRDReferenceDimension(RDWI, 1, RDXI); +- Optional GRREFERENCEDY = ++ absl::optional GRREFERENCEDY = + CheckTRDReferenceDimension(RDHI, 1, RDYI); +- if (!GRREFERENCEDX || !GRREFERENCEDY) ++ if (!GRREFERENCEDX.has_value() || !GRREFERENCEDY.has_value()) + return nullptr; + +- auto pGRRD = pdfium::MakeUnique(); ++ auto pGRRD = std::make_unique(); + pGRRD->GRW = WOI.value(); + pGRRD->GRH = HOI.value(); + pGRRD->GRTEMPLATE = SBRTEMPLATE; + pGRRD->GRREFERENCE = IBOI; + pGRRD->GRREFERENCEDX = GRREFERENCEDX.value(); + pGRRD->GRREFERENCEDY = GRREFERENCEDY.value(); +- pGRRD->TPGRON = 0; ++ pGRRD->TPGRON = false; + pGRRD->GRAT[0] = SBRAT[0]; + pGRRD->GRAT[1] = SBRAT[1]; + pGRRD->GRAT[2] = SBRAT[2]; +diff --git a/core/fxcodec/jbig2/JBig2_TrdProc.h b/core/fxcodec/jbig2/JBig2_TrdProc.h +index 8f4e42dd2..7962c7011 100644 +--- a/core/fxcodec/jbig2/JBig2_TrdProc.h ++++ b/core/fxcodec/jbig2/JBig2_TrdProc.h +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,12 @@ + #ifndef CORE_FXCODEC_JBIG2_JBIG2_TRDPROC_H_ + #define CORE_FXCODEC_JBIG2_JBIG2_TRDPROC_H_ + ++#include ++ + #include + #include + + #include "core/fxcodec/jbig2/JBig2_Image.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/unowned_ptr.h" + + class CJBig2_ArithDecoder; +diff --git a/core/fxcodec/jbig2/jbig2module.cpp b/core/fxcodec/jbig2/jbig2_decoder.cpp +similarity index 52% +rename from core/fxcodec/jbig2/jbig2module.cpp +rename to core/fxcodec/jbig2/jbig2_decoder.cpp +index 6f9914362..0b95382ab 100644 +--- a/core/fxcodec/jbig2/jbig2module.cpp ++++ b/core/fxcodec/jbig2/jbig2_decoder.cpp +@@ -1,87 +1,78 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#include "core/fxcodec/jbig2/jbig2module.h" ++#include "core/fxcodec/jbig2/jbig2_decoder.h" ++ ++#include + + #include "core/fxcodec/jbig2/JBig2_Context.h" + #include "core/fxcodec/jbig2/JBig2_DocumentContext.h" +-#include "third_party/base/ptr_util.h" ++#include "core/fxcrt/span_util.h" + + namespace fxcodec { + +-JBig2_DocumentContext* GetJBig2DocumentContext( +- std::unique_ptr* pContextHolder) { +- if (!*pContextHolder) +- *pContextHolder = pdfium::MakeUnique(); +- return pContextHolder->get(); ++namespace { ++ ++FXCODEC_STATUS Decode(Jbig2Context* pJbig2Context, bool decode_success) { ++ FXCODEC_STATUS status = pJbig2Context->m_pContext->GetProcessingStatus(); ++ if (status != FXCODEC_STATUS::kDecodeFinished) ++ return status; ++ ++ pJbig2Context->m_pContext.reset(); ++ if (!decode_success) ++ return FXCODEC_STATUS::kError; ++ ++ int dword_size = pJbig2Context->m_height * pJbig2Context->m_dest_pitch / 4; ++ uint32_t* dword_buf = reinterpret_cast(pJbig2Context->m_dest_buf); ++ for (int i = 0; i < dword_size; i++) ++ dword_buf[i] = ~dword_buf[i]; ++ return FXCODEC_STATUS::kDecodeFinished; + } + ++} // namespace ++ + Jbig2Context::Jbig2Context() = default; + + Jbig2Context::~Jbig2Context() = default; + +-Jbig2Module::Jbig2Module() = default; +- +-Jbig2Module::~Jbig2Module() = default; +- +-FXCODEC_STATUS Jbig2Module::StartDecode( ++// static ++FXCODEC_STATUS Jbig2Decoder::StartDecode( + Jbig2Context* pJbig2Context, +- std::unique_ptr* pContextHolder, ++ JBig2_DocumentContext* pJBig2DocumentContext, + uint32_t width, + uint32_t height, + pdfium::span src_span, +- uint32_t src_objnum, ++ uint64_t src_key, + pdfium::span global_span, +- uint32_t global_objnum, +- uint8_t* dest_buf, ++ uint64_t global_key, ++ pdfium::span dest_buf, + uint32_t dest_pitch, + PauseIndicatorIface* pPause) { +- if (!pJbig2Context) +- return FXCODEC_STATUS_ERR_PARAMS; +- +- JBig2_DocumentContext* pJBig2DocumentContext = +- GetJBig2DocumentContext(pContextHolder); + pJbig2Context->m_width = width; + pJbig2Context->m_height = height; + pJbig2Context->m_pSrcSpan = src_span; +- pJbig2Context->m_nSrcObjNum = src_objnum; ++ pJbig2Context->m_nSrcKey = src_key; + pJbig2Context->m_pGlobalSpan = global_span; +- pJbig2Context->m_nGlobalObjNum = global_objnum; +- pJbig2Context->m_dest_buf = dest_buf; ++ pJbig2Context->m_nGlobalKey = global_key; ++ pJbig2Context->m_dest_buf = dest_buf.data(); + pJbig2Context->m_dest_pitch = dest_pitch; +- memset(dest_buf, 0, height * dest_pitch); ++ fxcrt::spanset(dest_buf.first(height * dest_pitch), 0); + pJbig2Context->m_pContext = +- CJBig2_Context::Create(global_span, global_objnum, src_span, src_objnum, ++ CJBig2_Context::Create(global_span, global_key, src_span, src_key, + pJBig2DocumentContext->GetSymbolDictCache()); + bool succeeded = pJbig2Context->m_pContext->GetFirstPage( + dest_buf, width, height, dest_pitch, pPause); + return Decode(pJbig2Context, succeeded); + } + +-FXCODEC_STATUS Jbig2Module::ContinueDecode(Jbig2Context* pJbig2Context, +- PauseIndicatorIface* pPause) { ++// static ++FXCODEC_STATUS Jbig2Decoder::ContinueDecode(Jbig2Context* pJbig2Context, ++ PauseIndicatorIface* pPause) { + bool succeeded = pJbig2Context->m_pContext->Continue(pPause); + return Decode(pJbig2Context, succeeded); + } + +-FXCODEC_STATUS Jbig2Module::Decode(Jbig2Context* pJbig2Context, +- bool decode_success) { +- FXCODEC_STATUS status = pJbig2Context->m_pContext->GetProcessingStatus(); +- if (status != FXCODEC_STATUS_DECODE_FINISH) +- return status; +- +- pJbig2Context->m_pContext.reset(); +- if (!decode_success) +- return FXCODEC_STATUS_ERROR; +- +- int dword_size = pJbig2Context->m_height * pJbig2Context->m_dest_pitch / 4; +- uint32_t* dword_buf = reinterpret_cast(pJbig2Context->m_dest_buf); +- for (int i = 0; i < dword_size; i++) +- dword_buf[i] = ~dword_buf[i]; +- return FXCODEC_STATUS_DECODE_FINISH; +-} +- + } // namespace fxcodec +diff --git a/core/fxcodec/jbig2/jbig2module.h b/core/fxcodec/jbig2/jbig2_decoder.h +similarity index 57% +rename from core/fxcodec/jbig2/jbig2module.h +rename to core/fxcodec/jbig2/jbig2_decoder.h +index 6f9b0bb30..0a94f5de8 100644 +--- a/core/fxcodec/jbig2/jbig2module.h ++++ b/core/fxcodec/jbig2/jbig2_decoder.h +@@ -1,11 +1,11 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#ifndef CORE_FXCODEC_JBIG2_JBIG2MODULE_H_ +-#define CORE_FXCODEC_JBIG2_JBIG2MODULE_H_ ++#ifndef CORE_FXCODEC_JBIG2_JBIG2_DECODER_H_ ++#define CORE_FXCODEC_JBIG2_JBIG2_DECODER_H_ + + #include + +@@ -13,7 +13,6 @@ + #include "third_party/base/span.h" + + class CJBig2_Context; +-class CJBig2_Image; + class JBig2_DocumentContext; + class PauseIndicatorIface; + +@@ -26,8 +25,8 @@ class Jbig2Context { + + uint32_t m_width = 0; + uint32_t m_height = 0; +- uint32_t m_nGlobalObjNum = 0; +- uint32_t m_nSrcObjNum = 0; ++ uint64_t m_nGlobalKey = 0; ++ uint64_t m_nSrcKey = 0; + pdfium::span m_pGlobalSpan; + pdfium::span m_pSrcSpan; + uint8_t* m_dest_buf = nullptr; +@@ -35,34 +34,32 @@ class Jbig2Context { + std::unique_ptr m_pContext; + }; + +-class Jbig2Module { ++class Jbig2Decoder { + public: +- Jbig2Module(); +- ~Jbig2Module(); +- +- FXCODEC_STATUS StartDecode( ++ static FXCODEC_STATUS StartDecode( + Jbig2Context* pJbig2Context, +- std::unique_ptr* pContextHolder, ++ JBig2_DocumentContext* pJbig2DocumentContext, + uint32_t width, + uint32_t height, + pdfium::span src_span, +- uint32_t src_objnum, ++ uint64_t src_key, + pdfium::span global_span, +- uint32_t global_objnum, +- uint8_t* dest_buf, ++ uint64_t global_key, ++ pdfium::span dest_buf, + uint32_t dest_pitch, + PauseIndicatorIface* pPause); + +- FXCODEC_STATUS ContinueDecode(Jbig2Context* pJbig2Context, +- PauseIndicatorIface* pPause); ++ static FXCODEC_STATUS ContinueDecode(Jbig2Context* pJbig2Context, ++ PauseIndicatorIface* pPause); + +- private: +- FXCODEC_STATUS Decode(Jbig2Context* pJbig2Context, bool decode_success); ++ Jbig2Decoder() = delete; ++ Jbig2Decoder(const Jbig2Decoder&) = delete; ++ Jbig2Decoder& operator=(const Jbig2Decoder&) = delete; + }; + + } // namespace fxcodec + + using Jbig2Context = fxcodec::Jbig2Context; +-using Jbig2Module = fxcodec::Jbig2Module; ++using Jbig2Decoder = fxcodec::Jbig2Decoder; + +-#endif // CORE_FXCODEC_JBIG2_JBIG2MODULE_H_ ++#endif // CORE_FXCODEC_JBIG2_JBIG2_DECODER_H_ +diff --git a/core/fxcodec/jbig2/jbig2_embeddertest.cpp b/core/fxcodec/jbig2/jbig2_embeddertest.cpp +index e2523fcad..1b656fdab 100644 +--- a/core/fxcodec/jbig2/jbig2_embeddertest.cpp ++++ b/core/fxcodec/jbig2/jbig2_embeddertest.cpp +@@ -1,9 +1,7 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include +- + #include "public/cpp/fpdf_scopers.h" + #include "testing/embedder_test.h" + #include "testing/gtest/include/gtest/gtest.h" +@@ -19,10 +17,10 @@ class JBig2EmbedderTest : public EmbedderTest {}; + TEST_F(JBig2EmbedderTest, MAYBE_Bug_631912) { + // Test jbig2 image in PDF file can be loaded successfully. + // Should not crash. +- EXPECT_TRUE(OpenDocument("bug_631912.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_631912.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), 691, 432, "24d75af646f8772c5ee7ced260452ae4"); ++ CompareBitmap(bitmap.get(), 691, 432, "726c2b8c89df0ab40627322d1dddd521"); + UnloadPage(page); + } +diff --git a/core/fxcodec/jpeg/jpeg_common.cpp b/core/fxcodec/jpeg/jpeg_common.cpp +new file mode 100644 +index 000000000..b65bd74ab +--- /dev/null ++++ b/core/fxcodec/jpeg/jpeg_common.cpp +@@ -0,0 +1,27 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxcodec/jpeg/jpeg_common.h" ++ ++extern "C" { ++ ++void src_do_nothing(jpeg_decompress_struct* cinfo) {} ++ ++boolean src_fill_buffer(j_decompress_ptr cinfo) { ++ return FALSE; ++} ++ ++boolean src_resync(j_decompress_ptr cinfo, int desired) { ++ return FALSE; ++} ++ ++void error_do_nothing(j_common_ptr cinfo) {} ++ ++void error_do_nothing_int(j_common_ptr cinfo, int) {} ++ ++void error_do_nothing_char(j_common_ptr cinfo, char*) {} ++ ++} // extern "C" +diff --git a/core/fxcodec/jpeg/jpeg_common.h b/core/fxcodec/jpeg/jpeg_common.h +new file mode 100644 +index 000000000..078ed511f +--- /dev/null ++++ b/core/fxcodec/jpeg/jpeg_common.h +@@ -0,0 +1,45 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCODEC_JPEG_JPEG_COMMON_H_ ++#define CORE_FXCODEC_JPEG_JPEG_COMMON_H_ ++ ++// Common code for interacting with libjpeg shared by other files in ++// core/fxcodec/jpeg/. Not intended to be included in headers. ++ ++#include ++ ++#include "build/build_config.h" ++ ++#if BUILDFLAG(IS_WIN) ++// windows.h must come before the third_party/libjpeg_turbo includes. ++#include ++#endif ++ ++extern "C" { ++ ++#undef FAR ++#if defined(USE_SYSTEM_LIBJPEG) ++#include ++#include ++#elif defined(USE_LIBJPEG_TURBO) ++#include "third_party/libjpeg_turbo/jerror.h" ++#include "third_party/libjpeg_turbo/jpeglib.h" ++#else ++#include "third_party/libjpeg/jerror.h" ++#include "third_party/libjpeg/jpeglib.h" ++#endif ++ ++void src_do_nothing(jpeg_decompress_struct* cinfo); ++boolean src_fill_buffer(j_decompress_ptr cinfo); ++boolean src_resync(j_decompress_ptr cinfo, int desired); ++void error_do_nothing(j_common_ptr cinfo); ++void error_do_nothing_int(j_common_ptr cinfo, int); ++void error_do_nothing_char(j_common_ptr cinfo, char*); ++ ++} // extern "C" ++ ++#endif // CORE_FXCODEC_JPEG_JPEG_COMMON_H_ +diff --git a/core/fxcodec/jpeg/jpeg_progressive_decoder.cpp b/core/fxcodec/jpeg/jpeg_progressive_decoder.cpp +new file mode 100644 +index 000000000..c662f144c +--- /dev/null ++++ b/core/fxcodec/jpeg/jpeg_progressive_decoder.cpp +@@ -0,0 +1,178 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxcodec/jpeg/jpeg_progressive_decoder.h" ++ ++#include ++ ++#include "core/fxcodec/cfx_codec_memory.h" ++#include "core/fxcodec/fx_codec.h" ++#include "core/fxcodec/jpeg/jpeg_common.h" ++#include "core/fxcodec/scanlinedecoder.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxge/dib/cfx_dibbase.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/check.h" ++#include "third_party/base/ptr_util.h" ++ ++class CJpegContext final : public ProgressiveDecoderIface::Context { ++ public: ++ CJpegContext(); ++ ~CJpegContext() override; ++ ++ jmp_buf& GetJumpMark() { return m_JumpMark; } ++ ++ jmp_buf m_JumpMark; ++ jpeg_decompress_struct m_Info = {}; ++ jpeg_error_mgr m_ErrMgr = {}; ++ jpeg_source_mgr m_SrcMgr = {}; ++ unsigned int m_SkipSize = 0; ++}; ++ ++extern "C" { ++ ++static void error_fatal(j_common_ptr cinfo) { ++ auto* pContext = reinterpret_cast(cinfo->client_data); ++ longjmp(pContext->m_JumpMark, -1); ++} ++ ++static void src_skip_data(jpeg_decompress_struct* cinfo, long num) { ++ if (cinfo->src->bytes_in_buffer < static_cast(num)) { ++ auto* pContext = reinterpret_cast(cinfo->client_data); ++ pContext->m_SkipSize = (unsigned int)(num - cinfo->src->bytes_in_buffer); ++ cinfo->src->bytes_in_buffer = 0; ++ } else { ++ cinfo->src->next_input_byte += num; ++ cinfo->src->bytes_in_buffer -= num; ++ } ++} ++ ++} // extern "C" ++ ++static void JpegLoadAttribute(const jpeg_decompress_struct& info, ++ CFX_DIBAttribute* pAttribute) { ++ pAttribute->m_nXDPI = info.X_density; ++ pAttribute->m_nYDPI = info.Y_density; ++ pAttribute->m_wDPIUnit = ++ static_cast(info.density_unit); ++} ++ ++CJpegContext::CJpegContext() { ++ m_Info.client_data = this; ++ m_Info.err = &m_ErrMgr; ++ ++ m_ErrMgr.error_exit = error_fatal; ++ m_ErrMgr.emit_message = error_do_nothing_int; ++ m_ErrMgr.output_message = error_do_nothing; ++ m_ErrMgr.format_message = error_do_nothing_char; ++ m_ErrMgr.reset_error_mgr = error_do_nothing; ++ ++ m_SrcMgr.init_source = src_do_nothing; ++ m_SrcMgr.term_source = src_do_nothing; ++ m_SrcMgr.skip_input_data = src_skip_data; ++ m_SrcMgr.fill_input_buffer = src_fill_buffer; ++ m_SrcMgr.resync_to_restart = src_resync; ++} ++ ++CJpegContext::~CJpegContext() { ++ jpeg_destroy_decompress(&m_Info); ++} ++ ++namespace fxcodec { ++ ++// static ++JpegProgressiveDecoder* JpegProgressiveDecoder::GetInstance() { ++ static pdfium::base::NoDestructor s; ++ return s.get(); ++} ++ ++// static ++std::unique_ptr ++JpegProgressiveDecoder::Start() { ++ // Use ordinary pointer until past the possibility of a longjump. ++ auto* pContext = new CJpegContext(); ++ if (setjmp(pContext->m_JumpMark) == -1) { ++ delete pContext; ++ return nullptr; ++ } ++ ++ jpeg_create_decompress(&pContext->m_Info); ++ pContext->m_Info.src = &pContext->m_SrcMgr; ++ pContext->m_SkipSize = 0; ++ return pdfium::WrapUnique(pContext); ++} ++ ++// static ++jmp_buf& JpegProgressiveDecoder::GetJumpMark(Context* pContext) { ++ return static_cast(pContext)->GetJumpMark(); ++} ++ ++// static ++int JpegProgressiveDecoder::ReadHeader(Context* pContext, ++ int* width, ++ int* height, ++ int* nComps, ++ CFX_DIBAttribute* pAttribute) { ++ DCHECK(pAttribute); ++ ++ auto* ctx = static_cast(pContext); ++ int ret = jpeg_read_header(&ctx->m_Info, TRUE); ++ if (ret == JPEG_SUSPENDED) ++ return 2; ++ if (ret != JPEG_HEADER_OK) ++ return 1; ++ ++ *width = ctx->m_Info.image_width; ++ *height = ctx->m_Info.image_height; ++ *nComps = ctx->m_Info.num_components; ++ JpegLoadAttribute(ctx->m_Info, pAttribute); ++ return 0; ++} ++ ++// static ++bool JpegProgressiveDecoder::StartScanline(Context* pContext, int down_scale) { ++ auto* ctx = static_cast(pContext); ++ ctx->m_Info.scale_denom = static_cast(down_scale); ++ return !!jpeg_start_decompress(&ctx->m_Info); ++} ++ ++// static ++bool JpegProgressiveDecoder::ReadScanline(Context* pContext, ++ unsigned char* dest_buf) { ++ auto* ctx = static_cast(pContext); ++ unsigned int nlines = jpeg_read_scanlines(&ctx->m_Info, &dest_buf, 1); ++ return nlines == 1; ++} ++ ++FX_FILESIZE JpegProgressiveDecoder::GetAvailInput(Context* pContext) const { ++ auto* ctx = static_cast(pContext); ++ return static_cast(ctx->m_SrcMgr.bytes_in_buffer); ++} ++ ++bool JpegProgressiveDecoder::Input(Context* pContext, ++ RetainPtr codec_memory) { ++ pdfium::span src_buf = codec_memory->GetUnconsumedSpan(); ++ auto* ctx = static_cast(pContext); ++ if (ctx->m_SkipSize) { ++ if (ctx->m_SkipSize > src_buf.size()) { ++ ctx->m_SrcMgr.bytes_in_buffer = 0; ++ ctx->m_SkipSize -= src_buf.size(); ++ return true; ++ } ++ src_buf = src_buf.subspan(ctx->m_SkipSize); ++ ctx->m_SkipSize = 0; ++ } ++ ctx->m_SrcMgr.next_input_byte = src_buf.data(); ++ ctx->m_SrcMgr.bytes_in_buffer = src_buf.size(); ++ return true; ++} ++ ++JpegProgressiveDecoder::JpegProgressiveDecoder() = default; ++ ++JpegProgressiveDecoder::~JpegProgressiveDecoder() = default; ++ ++} // namespace fxcodec +diff --git a/core/fxcodec/jpeg/jpeg_progressive_decoder.h b/core/fxcodec/jpeg/jpeg_progressive_decoder.h +new file mode 100644 +index 000000000..6429d01d7 +--- /dev/null ++++ b/core/fxcodec/jpeg/jpeg_progressive_decoder.h +@@ -0,0 +1,54 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCODEC_JPEG_JPEG_PROGRESSIVE_DECODER_H_ ++#define CORE_FXCODEC_JPEG_JPEG_PROGRESSIVE_DECODER_H_ ++ ++#include ++ ++#include ++ ++#include "core/fxcodec/progressive_decoder_iface.h" ++#include "third_party/base/no_destructor.h" ++ ++namespace fxcodec { ++ ++class CFX_DIBAttribute; ++ ++class JpegProgressiveDecoder final : public ProgressiveDecoderIface { ++ public: ++ static JpegProgressiveDecoder* GetInstance(); ++ ++ static std::unique_ptr Start(); ++ ++ static jmp_buf& GetJumpMark(Context* pContext); ++ ++ static int ReadHeader(Context* pContext, ++ int* width, ++ int* height, ++ int* nComps, ++ CFX_DIBAttribute* pAttribute); ++ ++ static bool StartScanline(Context* pContext, int down_scale); ++ static bool ReadScanline(Context* pContext, uint8_t* dest_buf); ++ ++ // ProgressiveDecoderIface: ++ FX_FILESIZE GetAvailInput(Context* pContext) const override; ++ bool Input(Context* pContext, ++ RetainPtr codec_memory) override; ++ ++ private: ++ friend pdfium::base::NoDestructor; ++ ++ JpegProgressiveDecoder(); ++ ~JpegProgressiveDecoder() override; ++}; ++ ++} // namespace fxcodec ++ ++using JpegProgressiveDecoder = fxcodec::JpegProgressiveDecoder; ++ ++#endif // CORE_FXCODEC_JPEG_JPEG_PROGRESSIVE_DECODER_H_ +diff --git a/core/fxcodec/jpeg/jpegmodule.cpp b/core/fxcodec/jpeg/jpegmodule.cpp +index 72925f189..ad80c7e65 100644 +--- a/core/fxcodec/jpeg/jpegmodule.cpp ++++ b/core/fxcodec/jpeg/jpegmodule.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,55 +7,27 @@ + #include "core/fxcodec/jpeg/jpegmodule.h" + + #include ++#include ++#include + + #include + #include + + #include "build/build_config.h" + #include "core/fxcodec/cfx_codec_memory.h" +-#include "core/fxcodec/fx_codec.h" ++#include "core/fxcodec/jpeg/jpeg_common.h" + #include "core/fxcodec/scanlinedecoder.h" +-#include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxge/dib/cfx_dibbase.h" +-#include "core/fxge/fx_dib.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/optional.h" +-#include "third_party/base/ptr_util.h" +- +-extern "C" { +-#undef FAR +-#if defined(USE_SYSTEM_LIBJPEG) +-#include +-#include +-#elif defined(USE_LIBJPEG_TURBO) +-#include "third_party/libjpeg_turbo/jerror.h" +-#include "third_party/libjpeg_turbo/jpeglib.h" +-#else +-#include "third_party/libjpeg/jerror.h" +-#include "third_party/libjpeg/jpeglib.h" +-#endif +-} // extern "C" +- +-class CJpegContext final : public ModuleIface::Context { +- public: +- CJpegContext(); +- ~CJpegContext() override; +- +- jmp_buf& GetJumpMark() { return m_JumpMark; } +- +- jmp_buf m_JumpMark; +- jpeg_decompress_struct m_Info = {}; +- jpeg_error_mgr m_ErrMgr = {}; +- jpeg_source_mgr m_SrcMgr = {}; +- unsigned int m_SkipSize = 0; +- void* (*m_AllocFunc)(unsigned int); +- void (*m_FreeFunc)(void*); +-}; ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" + + static pdfium::span JpegScanSOI( + pdfium::span src_span) { +- ASSERT(!src_span.empty()); ++ DCHECK(!src_span.empty()); + + for (size_t offset = 0; offset < src_span.size() - 1; ++offset) { + if (src_span[offset] == 0xff && src_span[offset + 1] == 0xd8) +@@ -66,8 +38,6 @@ static pdfium::span JpegScanSOI( + + extern "C" { + +-static void src_do_nothing(jpeg_decompress_struct* cinfo) {} +- + static void error_fatal(j_common_ptr cinfo) { + longjmp(*(jmp_buf*)cinfo->client_data, -1); + } +@@ -80,76 +50,25 @@ static void src_skip_data(jpeg_decompress_struct* cinfo, long num) { + cinfo->src->bytes_in_buffer -= num; + } + +-static boolean src_fill_buffer(j_decompress_ptr cinfo) { +- return FALSE; +-} +- +-static boolean src_resync(j_decompress_ptr cinfo, int desired) { +- return FALSE; +-} +- +-static void error_do_nothing(j_common_ptr cinfo) {} +- +-static void error_do_nothing1(j_common_ptr cinfo, int) {} +- +-static void error_do_nothing2(j_common_ptr cinfo, char*) {} +- +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + static void dest_do_nothing(j_compress_ptr cinfo) {} + + static boolean dest_empty(j_compress_ptr cinfo) { + return false; + } +-#endif // defined(OS_WIN) +- +-static void error_fatal1(j_common_ptr cinfo) { +- auto* pContext = reinterpret_cast(cinfo->client_data); +- longjmp(pContext->m_JumpMark, -1); +-} +- +-static void src_skip_data1(jpeg_decompress_struct* cinfo, long num) { +- if (cinfo->src->bytes_in_buffer < static_cast(num)) { +- auto* pContext = reinterpret_cast(cinfo->client_data); +- pContext->m_SkipSize = (unsigned int)(num - cinfo->src->bytes_in_buffer); +- cinfo->src->bytes_in_buffer = 0; +- } else { +- cinfo->src->next_input_byte += num; +- cinfo->src->bytes_in_buffer -= num; +- } +-} +- +-static void* jpeg_alloc_func(unsigned int size) { +- return FX_Alloc(char, size); +-} +- +-static void jpeg_free_func(void* p) { +- FX_Free(p); +-} ++#endif // BUILDFLAG(IS_WIN) + + } // extern "C" + +-#ifdef PDF_ENABLE_XFA +-static void JpegLoadAttribute(const jpeg_decompress_struct& info, +- CFX_DIBAttribute* pAttribute) { +- pAttribute->m_nXDPI = info.X_density; +- pAttribute->m_nYDPI = info.Y_density; +- pAttribute->m_wDPIUnit = info.density_unit; +-} +-#endif // PDF_ENABLE_XFA +- + static bool JpegLoadInfo(pdfium::span src_span, +- int* width, +- int* height, +- int* num_components, +- int* bits_per_components, +- bool* color_transform) { ++ JpegModule::ImageInfo* pInfo) { + src_span = JpegScanSOI(src_span); + jpeg_decompress_struct cinfo; + jpeg_error_mgr jerr; + jerr.error_exit = error_fatal; +- jerr.emit_message = error_do_nothing1; ++ jerr.emit_message = error_do_nothing_int; + jerr.output_message = error_do_nothing; +- jerr.format_message = error_do_nothing2; ++ jerr.format_message = error_do_nothing_char; + jerr.reset_error_mgr = error_do_nothing; + jerr.trace_level = 0; + cinfo.err = &jerr; +@@ -177,38 +96,16 @@ static bool JpegLoadInfo(pdfium::span src_span, + jpeg_destroy_decompress(&cinfo); + return false; + } +- *width = cinfo.image_width; +- *height = cinfo.image_height; +- *num_components = cinfo.num_components; +- *color_transform = ++ pInfo->width = cinfo.image_width; ++ pInfo->height = cinfo.image_height; ++ pInfo->num_components = cinfo.num_components; ++ pInfo->color_transform = + cinfo.jpeg_color_space == JCS_YCbCr || cinfo.jpeg_color_space == JCS_YCCK; +- *bits_per_components = cinfo.data_precision; ++ pInfo->bits_per_components = cinfo.data_precision; + jpeg_destroy_decompress(&cinfo); + return true; + } + +-CJpegContext::CJpegContext() +- : m_AllocFunc(jpeg_alloc_func), m_FreeFunc(jpeg_free_func) { +- m_Info.client_data = this; +- m_Info.err = &m_ErrMgr; +- +- m_ErrMgr.error_exit = error_fatal1; +- m_ErrMgr.emit_message = error_do_nothing1; +- m_ErrMgr.output_message = error_do_nothing; +- m_ErrMgr.format_message = error_do_nothing2; +- m_ErrMgr.reset_error_mgr = error_do_nothing; +- +- m_SrcMgr.init_source = src_do_nothing; +- m_SrcMgr.term_source = src_do_nothing; +- m_SrcMgr.skip_input_data = src_skip_data1; +- m_SrcMgr.fill_input_buffer = src_fill_buffer; +- m_SrcMgr.resync_to_restart = src_resync; +-} +- +-CJpegContext::~CJpegContext() { +- jpeg_destroy_decompress(&m_Info); +-} +- + namespace fxcodec { + + namespace { +@@ -221,28 +118,18 @@ class JpegDecoder final : public ScanlineDecoder { + ~JpegDecoder() override; + + bool Create(pdfium::span src_span, +- int width, +- int height, ++ uint32_t width, ++ uint32_t height, + int nComps, + bool ColorTransform); + + // ScanlineDecoder: +- bool v_Rewind() override; +- uint8_t* v_GetNextLine() override; ++ bool Rewind() override; ++ pdfium::span GetNextLine() override; + uint32_t GetSrcOffset() override; + + bool InitDecode(bool bAcceptKnownBadHeader); + +- jmp_buf m_JmpBuf; +- jpeg_decompress_struct m_Cinfo; +- jpeg_error_mgr m_Jerr; +- jpeg_source_mgr m_Src; +- pdfium::span m_SrcSpan; +- std::unique_ptr m_pScanlineBuf; +- bool m_bInited = false; +- bool m_bStarted = false; +- bool m_bJpegTransform = false; +- + private: + void CalcPitch(); + void InitDecompressSrc(); +@@ -264,8 +151,17 @@ class JpegDecoder final : public ScanlineDecoder { + // For a given invalid height byte offset in + // |kKnownBadHeaderWithInvalidHeightByteOffsetStarts|, the SOFn marker should + // be this many bytes before that. +- static const size_t kSofMarkerByteOffset = 5; ++ static constexpr size_t kSofMarkerByteOffset = 5; + ++ jmp_buf m_JmpBuf; ++ jpeg_decompress_struct m_Cinfo; ++ jpeg_error_mgr m_Jerr; ++ jpeg_source_mgr m_Src; ++ pdfium::span m_SrcSpan; ++ DataVector m_ScanlineBuf; ++ bool m_bInited = false; ++ bool m_bStarted = false; ++ bool m_bJpegTransform = false; + uint32_t m_nDefaultScaleDenom = 1; + }; + +@@ -278,6 +174,9 @@ JpegDecoder::JpegDecoder() { + JpegDecoder::~JpegDecoder() { + if (m_bInited) + jpeg_destroy_decompress(&m_Cinfo); ++ ++ // Span in superclass can't outlive our buffer. ++ m_pLastScanline = pdfium::span(); + } + + bool JpegDecoder::InitDecode(bool bAcceptKnownBadHeader) { +@@ -291,7 +190,7 @@ bool JpegDecoder::InitDecode(bool bAcceptKnownBadHeader) { + m_bInited = true; + + if (setjmp(m_JmpBuf) == -1) { +- Optional known_bad_header_offset; ++ absl::optional known_bad_header_offset; + if (bAcceptKnownBadHeader) { + for (size_t offset : kKnownBadHeaderWithInvalidHeightByteOffsetStarts) { + if (HasKnownBadHeaderWithInvalidHeight(offset)) { +@@ -332,8 +231,8 @@ bool JpegDecoder::InitDecode(bool bAcceptKnownBadHeader) { + } + + bool JpegDecoder::Create(pdfium::span src_span, +- int width, +- int height, ++ uint32_t width, ++ uint32_t height, + int nComps, + bool ColorTransform) { + m_SrcSpan = JpegScanSOI(src_span); +@@ -343,9 +242,9 @@ bool JpegDecoder::Create(pdfium::span src_span, + PatchUpTrailer(); + + m_Jerr.error_exit = error_fatal; +- m_Jerr.emit_message = error_do_nothing1; ++ m_Jerr.emit_message = error_do_nothing_int; + m_Jerr.output_message = error_do_nothing; +- m_Jerr.format_message = error_do_nothing2; ++ m_Jerr.format_message = error_do_nothing_char; + m_Jerr.reset_error_mgr = error_do_nothing; + m_Src.init_source = src_do_nothing; + m_Src.term_source = src_do_nothing; +@@ -361,18 +260,18 @@ bool JpegDecoder::Create(pdfium::span src_span, + if (m_Cinfo.num_components < nComps) + return false; + +- if (static_cast(m_Cinfo.image_width) < width) ++ if (m_Cinfo.image_width < width) + return false; + + CalcPitch(); +- m_pScanlineBuf.reset(FX_Alloc(uint8_t, m_Pitch)); ++ m_ScanlineBuf = DataVector(m_Pitch); + m_nComps = m_Cinfo.num_components; + m_bpc = 8; + m_bStarted = false; + return true; + } + +-bool JpegDecoder::v_Rewind() { ++bool JpegDecoder::Rewind() { + if (m_bStarted) { + jpeg_destroy_decompress(&m_Cinfo); + if (!InitDecode(/*bAcceptKnownBadHeader=*/false)) { +@@ -389,21 +288,21 @@ bool JpegDecoder::v_Rewind() { + jpeg_destroy_decompress(&m_Cinfo); + return false; + } +- if (static_cast(m_Cinfo.output_width) > m_OrigWidth) { +- NOTREACHED(); +- return false; +- } ++ CHECK_LE(static_cast(m_Cinfo.output_width), m_OrigWidth); + m_bStarted = true; + return true; + } + +-uint8_t* JpegDecoder::v_GetNextLine() { ++pdfium::span JpegDecoder::GetNextLine() { + if (setjmp(m_JmpBuf) == -1) +- return nullptr; ++ return pdfium::span(); + +- uint8_t* row_array[] = {m_pScanlineBuf.get()}; ++ uint8_t* row_array[] = {m_ScanlineBuf.data()}; + int nlines = jpeg_read_scanlines(&m_Cinfo, row_array, 1); +- return nlines > 0 ? m_pScanlineBuf.get() : nullptr; ++ if (nlines <= 0) ++ return pdfium::span(); ++ ++ return m_ScanlineBuf; + } + + uint32_t JpegDecoder::GetSrcOffset() { +@@ -459,7 +358,7 @@ bool JpegDecoder::IsSofSegment(size_t marker_offset) const { + + void JpegDecoder::PatchUpKnownBadHeaderWithInvalidHeight( + size_t dimension_offset) { +- ASSERT(m_SrcSpan.size() > dimension_offset + 1u); ++ DCHECK(m_SrcSpan.size() > dimension_offset + 1u); + uint8_t* pData = GetWritableSrcData() + dimension_offset; + pData[0] = (m_OrigHeight >> 8) & 0xff; + pData[1] = m_OrigHeight & 0xff; +@@ -477,117 +376,41 @@ uint8_t* JpegDecoder::GetWritableSrcData() { + + } // namespace + ++// static + std::unique_ptr JpegModule::CreateDecoder( + pdfium::span src_span, +- int width, +- int height, ++ uint32_t width, ++ uint32_t height, + int nComps, + bool ColorTransform) { +- ASSERT(!src_span.empty()); ++ DCHECK(!src_span.empty()); + +- auto pDecoder = pdfium::MakeUnique(); ++ auto pDecoder = std::make_unique(); + if (!pDecoder->Create(src_span, width, height, nComps, ColorTransform)) + return nullptr; + + return std::move(pDecoder); + } + +-Optional JpegModule::LoadInfo( ++// static ++absl::optional JpegModule::LoadInfo( + pdfium::span src_span) { +- JpegImageInfo info; +- if (!JpegLoadInfo(src_span, &info.width, &info.height, &info.num_components, +- &info.bits_per_components, &info.color_transform)) { +- return pdfium::nullopt; +- } +- return info; +-} +- +-std::unique_ptr JpegModule::Start() { +- // Use ordinary pointer until past the possibility of a longjump. +- auto* pContext = new CJpegContext(); +- if (setjmp(pContext->m_JumpMark) == -1) { +- delete pContext; +- return nullptr; +- } +- +- jpeg_create_decompress(&pContext->m_Info); +- pContext->m_Info.src = &pContext->m_SrcMgr; +- pContext->m_SkipSize = 0; +- return pdfium::WrapUnique(pContext); +-} +- +-bool JpegModule::Input(Context* pContext, +- RetainPtr codec_memory, +- CFX_DIBAttribute*) { +- pdfium::span src_buf = codec_memory->GetSpan(); +- auto* ctx = static_cast(pContext); +- if (ctx->m_SkipSize) { +- if (ctx->m_SkipSize > src_buf.size()) { +- ctx->m_SrcMgr.bytes_in_buffer = 0; +- ctx->m_SkipSize -= src_buf.size(); +- return true; +- } +- src_buf = src_buf.subspan(ctx->m_SkipSize); +- ctx->m_SkipSize = 0; +- } +- ctx->m_SrcMgr.next_input_byte = src_buf.data(); +- ctx->m_SrcMgr.bytes_in_buffer = src_buf.size(); +- return true; +-} +- +-#ifdef PDF_ENABLE_XFA +-int JpegModule::ReadHeader(Context* pContext, +- int* width, +- int* height, +- int* nComps, +- CFX_DIBAttribute* pAttribute) { +- ASSERT(pAttribute); +- +- auto* ctx = static_cast(pContext); +- int ret = jpeg_read_header(&ctx->m_Info, TRUE); +- if (ret == JPEG_SUSPENDED) +- return 2; +- if (ret != JPEG_HEADER_OK) +- return 1; ++ ImageInfo info; ++ if (!JpegLoadInfo(src_span, &info)) ++ return absl::nullopt; + +- *width = ctx->m_Info.image_width; +- *height = ctx->m_Info.image_height; +- *nComps = ctx->m_Info.num_components; +- JpegLoadAttribute(ctx->m_Info, pAttribute); +- return 0; +-} +-#endif // PDF_ENABLE_XFA +- +-bool JpegModule::StartScanline(Context* pContext, int down_scale) { +- auto* ctx = static_cast(pContext); +- ctx->m_Info.scale_denom = static_cast(down_scale); +- return !!jpeg_start_decompress(&ctx->m_Info); +-} +- +-bool JpegModule::ReadScanline(Context* pContext, unsigned char* dest_buf) { +- auto* ctx = static_cast(pContext); +- unsigned int nlines = jpeg_read_scanlines(&ctx->m_Info, &dest_buf, 1); +- return nlines == 1; +-} +- +-FX_FILESIZE JpegModule::GetAvailInput(Context* pContext) const { +- auto* ctx = static_cast(pContext); +- return static_cast(ctx->m_SrcMgr.bytes_in_buffer); +-} +- +-jmp_buf& JpegModule::GetJumpMark(Context* pContext) { +- return static_cast(pContext)->GetJumpMark(); ++ return info; + } + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + bool JpegModule::JpegEncode(const RetainPtr& pSource, + uint8_t** dest_buf, + size_t* dest_size) { + jpeg_error_mgr jerr; + jerr.error_exit = error_do_nothing; +- jerr.emit_message = error_do_nothing1; ++ jerr.emit_message = error_do_nothing_int; + jerr.output_message = error_do_nothing; +- jerr.format_message = error_do_nothing2; ++ jerr.format_message = error_do_nothing_char; + jerr.reset_error_mgr = error_do_nothing; + + jpeg_compress_struct cinfo; +@@ -595,7 +418,7 @@ bool JpegModule::JpegEncode(const RetainPtr& pSource, + cinfo.err = &jerr; + jpeg_create_compress(&cinfo); + int Bpp = pSource->GetBPP() / 8; +- uint32_t nComponents = Bpp >= 3 ? (pSource->IsCmykImage() ? 4 : 3) : 1; ++ uint32_t nComponents = Bpp >= 3 ? 3 : 1; + uint32_t pitch = pSource->GetPitch(); + uint32_t width = pdfium::base::checked_cast(pSource->GetWidth()); + uint32_t height = pdfium::base::checked_cast(pSource->GetHeight()); +@@ -642,25 +465,25 @@ bool JpegModule::JpegEncode(const RetainPtr& pSource, + JSAMPROW row_pointer[1]; + JDIMENSION row; + while (cinfo.next_scanline < cinfo.image_height) { +- const uint8_t* src_scan = pSource->GetScanline(cinfo.next_scanline); ++ pdfium::span src_scan = ++ pSource->GetScanline(cinfo.next_scanline); + if (nComponents > 1) { + uint8_t* dest_scan = line_buf; + if (nComponents == 3) { + for (uint32_t i = 0; i < width; i++) { +- dest_scan[0] = src_scan[2]; +- dest_scan[1] = src_scan[1]; +- dest_scan[2] = src_scan[0]; ++ ReverseCopy3Bytes(dest_scan, src_scan.data()); + dest_scan += 3; +- src_scan += Bpp; ++ src_scan = src_scan.subspan(Bpp); + } + } else { + for (uint32_t i = 0; i < pitch; i++) { +- *dest_scan++ = ~*src_scan++; ++ *dest_scan++ = ~src_scan.front(); ++ src_scan = src_scan.subspan(1); + } + } + row_pointer[0] = line_buf; + } else { +- row_pointer[0] = const_cast(src_scan); ++ row_pointer[0] = const_cast(src_scan.data()); + } + row = cinfo.next_scanline; + jpeg_write_scanlines(&cinfo, row_pointer, 1); +@@ -680,6 +503,6 @@ bool JpegModule::JpegEncode(const RetainPtr& pSource, + + return true; + } +-#endif // defined(OS_WIN) ++#endif // BUILDFLAG(IS_WIN) + + } // namespace fxcodec +diff --git a/core/fxcodec/jpeg/jpegmodule.h b/core/fxcodec/jpeg/jpegmodule.h +index 0ecf20472..a4e20ef91 100644 +--- a/core/fxcodec/jpeg/jpegmodule.h ++++ b/core/fxcodec/jpeg/jpegmodule.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,65 +7,53 @@ + #ifndef CORE_FXCODEC_JPEG_JPEGMODULE_H_ + #define CORE_FXCODEC_JPEG_JPEGMODULE_H_ + +-#include ++#include ++ + #include + + #include "build/build_config.h" +-#include "core/fxcodec/codec_module_iface.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + #include "third_party/base/span.h" + ++#if BUILDFLAG(IS_WIN) ++#include "core/fxcrt/retain_ptr.h" ++#endif ++ + class CFX_DIBBase; + + namespace fxcodec { + +-class CFX_DIBAttribute; + class ScanlineDecoder; + +-class JpegModule final : public ModuleIface { ++class JpegModule { + public: +- struct JpegImageInfo { +- int width; +- int height; ++ struct ImageInfo { ++ uint32_t width; ++ uint32_t height; + int num_components; + int bits_per_components; + bool color_transform; + }; + +- std::unique_ptr CreateDecoder( ++ static std::unique_ptr CreateDecoder( + pdfium::span src_span, +- int width, +- int height, ++ uint32_t width, ++ uint32_t height, + int nComps, + bool ColorTransform); + +- // ModuleIface: +- FX_FILESIZE GetAvailInput(Context* pContext) const override; +- bool Input(Context* pContext, +- RetainPtr codec_memory, +- CFX_DIBAttribute* pAttribute) override; +- +- jmp_buf& GetJumpMark(Context* pContext); +- Optional LoadInfo(pdfium::span src_span); +- +- std::unique_ptr Start(); ++ static absl::optional LoadInfo( ++ pdfium::span src_span); + +-#ifdef PDF_ENABLE_XFA +- int ReadHeader(Context* pContext, +- int* width, +- int* height, +- int* nComps, +- CFX_DIBAttribute* pAttribute); +-#endif // PDF_ENABLE_XFA +- +- bool StartScanline(Context* pContext, int down_scale); +- bool ReadScanline(Context* pContext, uint8_t* dest_buf); +- +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + static bool JpegEncode(const RetainPtr& pSource, + uint8_t** dest_buf, + size_t* dest_size); +-#endif // defined(OS_WIN) ++#endif // BUILDFLAG(IS_WIN) ++ ++ JpegModule() = delete; ++ JpegModule(const JpegModule&) = delete; ++ JpegModule& operator=(const JpegModule&) = delete; + }; + + } // namespace fxcodec +diff --git a/core/fxcodec/jpx/DEPS b/core/fxcodec/jpx/DEPS +index d728e0f8a..56cdc4413 100644 +--- a/core/fxcodec/jpx/DEPS ++++ b/core/fxcodec/jpx/DEPS +@@ -1,3 +1,3 @@ + include_rules = [ +- '+third_party/libopenjpeg20', ++ '+third_party/libopenjpeg', + ] +diff --git a/core/fxcodec/jpx/cjpx_decoder.cpp b/core/fxcodec/jpx/cjpx_decoder.cpp +index 95f76ff8a..9391d61ab 100644 +--- a/core/fxcodec/jpx/cjpx_decoder.cpp ++++ b/core/fxcodec/jpx/cjpx_decoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,18 +6,23 @@ + + #include "core/fxcodec/jpx/cjpx_decoder.h" + ++#include ++ + #include + #include + #include ++#include + + #include "core/fxcodec/jpx/jpx_decode_utils.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/optional.h" ++#include "core/fxcrt/span_util.h" ++#include "core/fxge/calculate_pitch.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/cxx17_backports.h" + #include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" + + #if !defined(USE_SYSTEM_LIBOPENJPEG2) +-#include "third_party/libopenjpeg20/opj_malloc.h" ++#include "third_party/libopenjpeg/opj_malloc.h" + #endif + + namespace fxcodec { +@@ -56,19 +61,19 @@ opj_stream_t* fx_opj_stream_create_memory_stream(DecodeData* data) { + return stream; + } + +-Optional alloc_rgb(size_t size) { ++absl::optional alloc_rgb(size_t size) { + OpjImageRgbData data; + data.r.reset(static_cast(opj_image_data_alloc(size))); + if (!data.r) +- return {}; ++ return absl::nullopt; + + data.g.reset(static_cast(opj_image_data_alloc(size))); + if (!data.g) +- return {}; ++ return absl::nullopt; + + data.b.reset(static_cast(opj_image_data_alloc(size))); + if (!data.b) +- return {}; ++ return absl::nullopt; + + return data; + } +@@ -111,7 +116,7 @@ void sycc444_to_rgb(opj_image_t* img) { + if (!y || !cb || !cr) + return; + +- Optional data = alloc_rgb(max_size.ValueOrDie()); ++ absl::optional data = alloc_rgb(max_size.ValueOrDie()); + if (!data.has_value()) + return; + +@@ -176,7 +181,7 @@ void sycc420_to_rgb(opj_image_t* img) { + if (!y || !cb || !cr) + return; + +- Optional data = alloc_rgb(safe_size.ValueOrDie()); ++ absl::optional data = alloc_rgb(safe_size.ValueOrDie()); + if (!data.has_value()) + return; + +@@ -313,7 +318,7 @@ void sycc422_to_rgb(opj_image_t* img) { + if (!y || !cb || !cr) + return; + +- Optional data = alloc_rgb(max_size.ValueOrDie()); ++ absl::optional data = alloc_rgb(max_size.ValueOrDie()); + if (!data.has_value()) + return; + +@@ -384,6 +389,18 @@ void color_sycc_to_rgb(opj_image_t* img) { + + } // namespace + ++// static ++std::unique_ptr CJPX_Decoder::Create( ++ pdfium::span src_span, ++ CJPX_Decoder::ColorSpaceOption option, ++ uint8_t resolution_levels_to_skip) { ++ // Private ctor. ++ auto decoder = pdfium::WrapUnique(new CJPX_Decoder(option)); ++ if (!decoder->Init(src_span, resolution_levels_to_skip)) ++ return nullptr; ++ return decoder; ++} ++ + // static + void CJPX_Decoder::Sycc420ToRgbForTesting(opj_image_t* img) { + sycc420_to_rgb(img); +@@ -394,23 +411,25 @@ CJPX_Decoder::CJPX_Decoder(ColorSpaceOption option) + + CJPX_Decoder::~CJPX_Decoder() { + if (m_Codec) +- opj_destroy_codec(m_Codec.Release()); ++ opj_destroy_codec(m_Codec.ExtractAsDangling()); + if (m_Stream) +- opj_stream_destroy(m_Stream.Release()); ++ opj_stream_destroy(m_Stream.ExtractAsDangling()); + if (m_Image) +- opj_image_destroy(m_Image.Release()); ++ opj_image_destroy(m_Image.ExtractAsDangling()); + } + +-bool CJPX_Decoder::Init(pdfium::span src_data) { +- static const unsigned char szJP2Header[] = { +- 0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a}; +- if (src_data.empty() || src_data.size() < sizeof(szJP2Header)) ++bool CJPX_Decoder::Init(pdfium::span src_data, ++ uint8_t resolution_levels_to_skip) { ++ static constexpr uint8_t kJP2Header[] = {0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, ++ 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a}; ++ if (src_data.size() < sizeof(kJP2Header) || ++ resolution_levels_to_skip > kMaxResolutionsToSkip) { + return false; ++ } + + m_Image = nullptr; + m_SrcData = src_data; +- m_DecodeData = +- pdfium::MakeUnique(src_data.data(), src_data.size()); ++ m_DecodeData = std::make_unique(src_data.data(), src_data.size()); + m_Stream = fx_opj_stream_create_memory_stream(m_DecodeData.get()); + if (!m_Stream) + return false; +@@ -418,7 +437,8 @@ bool CJPX_Decoder::Init(pdfium::span src_data) { + opj_set_default_decoder_parameters(&m_Parameters); + m_Parameters.decod_format = 0; + m_Parameters.cod_format = 3; +- if (memcmp(m_SrcData.data(), szJP2Header, sizeof(szJP2Header)) == 0) { ++ m_Parameters.cp_reduce = resolution_levels_to_skip; ++ if (memcmp(m_SrcData.data(), kJP2Header, sizeof(kJP2Header)) == 0) { + m_Codec = opj_create_decompress(OPJ_CODEC_JP2); + m_Parameters.decod_format = 1; + } else { +@@ -429,15 +449,15 @@ bool CJPX_Decoder::Init(pdfium::span src_data) { + + if (m_ColorSpaceOption == kIndexedColorSpace) + m_Parameters.flags |= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG; +- opj_set_info_handler(m_Codec.Get(), fx_ignore_callback, nullptr); +- opj_set_warning_handler(m_Codec.Get(), fx_ignore_callback, nullptr); +- opj_set_error_handler(m_Codec.Get(), fx_ignore_callback, nullptr); +- if (!opj_setup_decoder(m_Codec.Get(), &m_Parameters)) ++ opj_set_info_handler(m_Codec, fx_ignore_callback, nullptr); ++ opj_set_warning_handler(m_Codec, fx_ignore_callback, nullptr); ++ opj_set_error_handler(m_Codec, fx_ignore_callback, nullptr); ++ if (!opj_setup_decoder(m_Codec, &m_Parameters)) + return false; + + m_Image = nullptr; + opj_image_t* pTempImage = nullptr; +- if (!opj_read_header(m_Stream.Get(), m_Codec.Get(), &pTempImage)) ++ if (!opj_read_header(m_Stream, m_Codec, &pTempImage)) + return false; + + m_Image = pTempImage; +@@ -446,23 +466,23 @@ bool CJPX_Decoder::Init(pdfium::span src_data) { + + bool CJPX_Decoder::StartDecode() { + if (!m_Parameters.nb_tile_to_decode) { +- if (!opj_set_decode_area(m_Codec.Get(), m_Image.Get(), m_Parameters.DA_x0, ++ if (!opj_set_decode_area(m_Codec, m_Image, m_Parameters.DA_x0, + m_Parameters.DA_y0, m_Parameters.DA_x1, + m_Parameters.DA_y1)) { +- opj_image_destroy(m_Image.Release()); ++ opj_image_destroy(m_Image.ExtractAsDangling()); + return false; + } +- if (!(opj_decode(m_Codec.Get(), m_Stream.Get(), m_Image.Get()) && +- opj_end_decompress(m_Codec.Get(), m_Stream.Get()))) { +- opj_image_destroy(m_Image.Release()); ++ if (!(opj_decode(m_Codec, m_Stream, m_Image) && ++ opj_end_decompress(m_Codec, m_Stream))) { ++ opj_image_destroy(m_Image.ExtractAsDangling()); + return false; + } +- } else if (!opj_get_decoded_tile(m_Codec.Get(), m_Stream.Get(), m_Image.Get(), ++ } else if (!opj_get_decoded_tile(m_Codec, m_Stream, m_Image, + m_Parameters.tile_index)) { + return false; + } + +- opj_stream_destroy(m_Stream.Release()); ++ opj_stream_destroy(m_Stream.ExtractAsDangling()); + if (m_Image->color_space != OPJ_CLRSPC_SYCC && m_Image->numcomps == 3 && + m_Image->comps[0].dx == m_Image->comps[0].dy && + m_Image->comps[1].dx != 1) { +@@ -471,7 +491,7 @@ bool CJPX_Decoder::StartDecode() { + m_Image->color_space = OPJ_CLRSPC_GRAY; + } + if (m_Image->color_space == OPJ_CLRSPC_SYCC) +- color_sycc_to_rgb(m_Image.Get()); ++ color_sycc_to_rgb(m_Image); + + if (m_Image->icc_profile_buf) { + // TODO(palmer): Using |opj_free| here resolves the crash described in +@@ -489,24 +509,42 @@ bool CJPX_Decoder::StartDecode() { + } + + CJPX_Decoder::JpxImageInfo CJPX_Decoder::GetInfo() const { +- return {m_Image->x1, m_Image->y1, m_Image->numcomps}; ++ return {m_Image->comps[0].w, m_Image->comps[0].h, m_Image->numcomps, ++ m_Image->color_space}; + } + +-bool CJPX_Decoder::Decode(uint8_t* dest_buf, uint32_t pitch, bool swap_rgb) { +- if (m_Image->comps[0].w != m_Image->x1 || m_Image->comps[0].h != m_Image->y1) +- return false; ++bool CJPX_Decoder::Decode(pdfium::span dest_buf, ++ uint32_t pitch, ++ bool swap_rgb, ++ uint32_t component_count) { ++ CHECK_LE(component_count, m_Image->numcomps); ++ uint32_t channel_count = component_count; ++ if (channel_count == 3 && m_Image->numcomps == 4) { ++ // When decoding for an ARGB image, include the alpha channel in the channel ++ // count. ++ channel_count = 4; ++ } + +- if (pitch<(m_Image->comps[0].w * 8 * m_Image->numcomps + 31)>> 5 << 2) ++ absl::optional calculated_pitch = ++ fxge::CalculatePitch32(8 * channel_count, m_Image->comps[0].w); ++ if (!calculated_pitch.has_value() || pitch < calculated_pitch.value()) { + return false; ++ } + +- if (swap_rgb && m_Image->numcomps < 3) ++ if (swap_rgb && channel_count < 3) { + return false; ++ } + +- memset(dest_buf, 0xff, m_Image->y1 * pitch); ++ // Initialize `channel_bufs` and `adjust_comps` to store information from all ++ // the channels of the JPX image. They will contain more information besides ++ // the color component data if `m_Image->numcomps` > `component_count`. ++ // Currently only the color component data is used for rendering. ++ // TODO(crbug.com/pdfium/1747): Make full use of the component information. ++ fxcrt::spanset(dest_buf.first(m_Image->comps[0].h * pitch), 0xff); + std::vector channel_bufs(m_Image->numcomps); + std::vector adjust_comps(m_Image->numcomps); + for (uint32_t i = 0; i < m_Image->numcomps; i++) { +- channel_bufs[i] = dest_buf + i; ++ channel_bufs[i] = dest_buf.subspan(i).data(); + adjust_comps[i] = m_Image->comps[i].prec - 8; + if (i > 0) { + if (m_Image->comps[i].dx != m_Image->comps[i - 1].dx || +@@ -521,47 +559,43 @@ bool CJPX_Decoder::Decode(uint8_t* dest_buf, uint32_t pitch, bool swap_rgb) { + + uint32_t width = m_Image->comps[0].w; + uint32_t height = m_Image->comps[0].h; +- for (uint32_t channel = 0; channel < m_Image->numcomps; ++channel) { ++ for (uint32_t channel = 0; channel < channel_count; ++channel) { + uint8_t* pChannel = channel_bufs[channel]; +- if (adjust_comps[channel] < 0) { ++ const int adjust = adjust_comps[channel]; ++ const opj_image_comp_t& comps = m_Image->comps[channel]; ++ if (!comps.data) ++ continue; ++ ++ // Perfomance-sensitive code below. Combining these 3 for-loops below will ++ // cause a slowdown. ++ const uint32_t src_offset = comps.sgnd ? 1 << (comps.prec - 1) : 0; ++ if (adjust < 0) { ++ for (uint32_t row = 0; row < height; ++row) { ++ uint8_t* pScanline = pChannel + row * pitch; ++ for (uint32_t col = 0; col < width; ++col) { ++ uint8_t* pPixel = pScanline + col * channel_count; ++ int src = comps.data[row * width + col] + src_offset; ++ *pPixel = static_cast(src << -adjust); ++ } ++ } ++ } else if (adjust == 0) { + for (uint32_t row = 0; row < height; ++row) { + uint8_t* pScanline = pChannel + row * pitch; + for (uint32_t col = 0; col < width; ++col) { +- uint8_t* pPixel = pScanline + col * m_Image->numcomps; +- if (!m_Image->comps[channel].data) +- continue; +- +- int src = m_Image->comps[channel].data[row * width + col]; +- src += m_Image->comps[channel].sgnd +- ? 1 << (m_Image->comps[channel].prec - 1) +- : 0; +- if (adjust_comps[channel] > 0) { +- *pPixel = 0; +- } else { +- *pPixel = static_cast(src << -adjust_comps[channel]); +- } ++ uint8_t* pPixel = pScanline + col * channel_count; ++ int src = comps.data[row * width + col] + src_offset; ++ *pPixel = static_cast(src); + } + } + } else { + for (uint32_t row = 0; row < height; ++row) { + uint8_t* pScanline = pChannel + row * pitch; + for (uint32_t col = 0; col < width; ++col) { +- uint8_t* pPixel = pScanline + col * m_Image->numcomps; +- if (!m_Image->comps[channel].data) +- continue; +- +- int src = m_Image->comps[channel].data[row * width + col]; +- src += m_Image->comps[channel].sgnd +- ? 1 << (m_Image->comps[channel].prec - 1) +- : 0; +- if (adjust_comps[channel] - 1 < 0) { +- *pPixel = static_cast((src >> adjust_comps[channel])); +- } else { +- int tmpPixel = (src >> adjust_comps[channel]) + +- ((src >> (adjust_comps[channel] - 1)) % 2); +- tmpPixel = pdfium::clamp(tmpPixel, 0, 255); +- *pPixel = static_cast(tmpPixel); +- } ++ uint8_t* pPixel = pScanline + col * channel_count; ++ int src = comps.data[row * width + col] + src_offset; ++ int pixel = (src >> adjust) + ((src >> (adjust - 1)) % 2); ++ pixel = pdfium::clamp(pixel, 0, 255); ++ *pPixel = static_cast(pixel); + } + } + } +diff --git a/core/fxcodec/jpx/cjpx_decoder.h b/core/fxcodec/jpx/cjpx_decoder.h +index 4d9883c15..36116b3a9 100644 +--- a/core/fxcodec/jpx/cjpx_decoder.h ++++ b/core/fxcodec/jpx/cjpx_decoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,8 +7,9 @@ + #ifndef CORE_FXCODEC_JPX_CJPX_DECODER_H_ + #define CORE_FXCODEC_JPX_CJPX_DECODER_H_ + ++#include ++ + #include +-#include + + #include "core/fxcrt/unowned_ptr.h" + #include "third_party/base/span.h" +@@ -16,7 +17,7 @@ + #if defined(USE_SYSTEM_LIBOPENJPEG2) + #include + #else +-#include "third_party/libopenjpeg20/openjpeg.h" ++#include "third_party/libopenjpeg/openjpeg.h" + #endif + + namespace fxcodec { +@@ -25,6 +26,10 @@ struct DecodeData; + + class CJPX_Decoder { + public: ++ // Calculated as log2(2^32 / 1), where 2^32 is the largest image dimension and ++ // 1 is the smallest required size. ++ static constexpr uint8_t kMaxResolutionsToSkip = 32; ++ + enum ColorSpaceOption { + kNoColorSpace, + kNormalColorSpace, +@@ -34,29 +39,52 @@ class CJPX_Decoder { + struct JpxImageInfo { + uint32_t width; + uint32_t height; +- uint32_t components; ++ uint32_t channels; ++ COLOR_SPACE colorspace; + }; + ++ static std::unique_ptr Create( ++ pdfium::span src_span, ++ CJPX_Decoder::ColorSpaceOption option, ++ uint8_t resolution_levels_to_skip); ++ + static void Sycc420ToRgbForTesting(opj_image_t* img); + +- explicit CJPX_Decoder(ColorSpaceOption option); + ~CJPX_Decoder(); + +- bool Init(pdfium::span src_data); + JpxImageInfo GetInfo() const; + bool StartDecode(); + +- // |swap_rgb| can only be set for images with 3 or more components. +- bool Decode(uint8_t* dest_buf, uint32_t pitch, bool swap_rgb); ++ // `swap_rgb` can only be set when an image's color space type contains at ++ // least 3 color components. Note that this `component_count` is not ++ // equivalent to `JpxImageInfo::channels`. The JpxImageInfo channels can ++ // contain extra information for rendering the image besides the color ++ // component information. Therefore the `JpxImageInfo::channels` must be no ++ // less than the component count. ++ // ++ // Example: If a JPX image's color space type is OPJ_CLRSPC_SRGB, the ++ // component count for this color space is 3, and the channel count of its ++ // JpxImageInfo can be 4. This is because the extra channel might contain ++ // extra information, such as the transparency level of the image. ++ bool Decode(pdfium::span dest_buf, ++ uint32_t pitch, ++ bool swap_rgb, ++ uint32_t component_count); + + private: ++ // Use Create() to instantiate. ++ explicit CJPX_Decoder(ColorSpaceOption option); ++ ++ bool Init(pdfium::span src_data, ++ uint8_t resolution_levels_to_skip); ++ + const ColorSpaceOption m_ColorSpaceOption; + pdfium::span m_SrcData; + UnownedPtr m_Image; + UnownedPtr m_Codec; + std::unique_ptr m_DecodeData; + UnownedPtr m_Stream; +- opj_dparameters_t m_Parameters; ++ opj_dparameters_t m_Parameters = {}; + }; + + } // namespace fxcodec +diff --git a/core/fxcodec/jpx/jpx_decode_utils.cpp b/core/fxcodec/jpx/jpx_decode_utils.cpp +index d3842d706..52b716f8d 100644 +--- a/core/fxcodec/jpx/jpx_decode_utils.cpp ++++ b/core/fxcodec/jpx/jpx_decode_utils.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,10 +6,12 @@ + + #include "core/fxcodec/jpx/jpx_decode_utils.h" + ++#include + #include + + #include + #include ++#include + + namespace fxcodec { + +diff --git a/core/fxcodec/jpx/jpx_decode_utils.h b/core/fxcodec/jpx/jpx_decode_utils.h +index 4403cb825..fc3d78af5 100644 +--- a/core/fxcodec/jpx/jpx_decode_utils.h ++++ b/core/fxcodec/jpx/jpx_decode_utils.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,7 +12,7 @@ + #if defined(USE_SYSTEM_LIBOPENJPEG2) + #include + #else +-#include "third_party/libopenjpeg20/openjpeg.h" ++#include "third_party/libopenjpeg/openjpeg.h" + #endif + + namespace fxcodec { +diff --git a/core/fxcodec/jpx/jpx_unittest.cpp b/core/fxcodec/jpx/jpx_unittest.cpp +index def506519..b89a91012 100644 +--- a/core/fxcodec/jpx/jpx_unittest.cpp ++++ b/core/fxcodec/jpx/jpx_unittest.cpp +@@ -1,7 +1,8 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include + #include + + #include +@@ -10,7 +11,7 @@ + #include "core/fxcodec/jpx/jpx_decode_utils.h" + #include "core/fxcrt/fx_memory.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/libopenjpeg20/opj_malloc.h" ++#include "third_party/libopenjpeg/opj_malloc.h" + + namespace fxcodec { + +diff --git a/core/fxcodec/jpx/jpxmodule.cpp b/core/fxcodec/jpx/jpxmodule.cpp +deleted file mode 100644 +index 4229dc5d2..000000000 +--- a/core/fxcodec/jpx/jpxmodule.cpp ++++ /dev/null +@@ -1,24 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fxcodec/jpx/jpxmodule.h" +- +-#include "third_party/base/ptr_util.h" +- +-namespace fxcodec { +- +-// static +-std::unique_ptr JpxModule::CreateDecoder( +- pdfium::span src_span, +- CJPX_Decoder::ColorSpaceOption option) { +- auto decoder = pdfium::MakeUnique(option); +- if (!decoder->Init(src_span)) +- return nullptr; +- +- return decoder; +-} +- +-} // namespace fxcodec +diff --git a/core/fxcodec/jpx/jpxmodule.h b/core/fxcodec/jpx/jpxmodule.h +deleted file mode 100644 +index 0f2fb7152..000000000 +--- a/core/fxcodec/jpx/jpxmodule.h ++++ /dev/null +@@ -1,34 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXCODEC_JPX_JPXMODULE_H_ +-#define CORE_FXCODEC_JPX_JPXMODULE_H_ +- +-#include +-#include +- +-#include "core/fxcodec/jpx/cjpx_decoder.h" +-#include "core/fxcrt/fx_system.h" +-#include "third_party/base/span.h" +- +-namespace fxcodec { +- +-class JpxModule { +- public: +- static std::unique_ptr CreateDecoder( +- pdfium::span src_span, +- CJPX_Decoder::ColorSpaceOption option); +- +- JpxModule() = delete; +- JpxModule(const JpxModule&) = delete; +- JpxModule& operator=(const JpxModule&) = delete; +-}; +- +-} // namespace fxcodec +- +-using JpxModule = fxcodec::JpxModule; +- +-#endif // CORE_FXCODEC_JPX_JPXMODULE_H_ +diff --git a/core/fxcodec/png/DEPS b/core/fxcodec/png/DEPS +index cc738553d..9caa4d853 100644 +--- a/core/fxcodec/png/DEPS ++++ b/core/fxcodec/png/DEPS +@@ -1,3 +1,3 @@ + include_rules = [ +- '+third_party/libpng16/png.h', ++ '+third_party/libpng/png.h', + ] +diff --git a/core/fxcodec/png/pngmodule.cpp b/core/fxcodec/png/png_decoder.cpp +similarity index 81% +rename from core/fxcodec/png/pngmodule.cpp +rename to core/fxcodec/png/png_decoder.cpp +index d3a3c0b8d..3cf42215f 100644 +--- a/core/fxcodec/png/pngmodule.cpp ++++ b/core/fxcodec/png/png_decoder.cpp +@@ -1,37 +1,36 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#include "core/fxcodec/png/pngmodule.h" ++#include "core/fxcodec/png/png_decoder.h" + +-#include ++#include ++#include + + #include "core/fxcodec/cfx_codec_memory.h" + #include "core/fxcodec/fx_codec.h" ++#include "core/fxcodec/fx_codec_def.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "core/fxge/fx_dib.h" +-#include "third_party/base/compiler_specific.h" +-#include "third_party/base/ptr_util.h" + + #ifdef USE_SYSTEM_LIBPNG + #include + #else +-#include "third_party/libpng16/png.h" ++#include "third_party/libpng/png.h" + #endif + + #define PNG_ERROR_SIZE 256 + +-class CPngContext final : public ModuleIface::Context { ++class CPngContext final : public ProgressiveDecoderIface::Context { + public: +- explicit CPngContext(PngModule::Delegate* pDelegate); ++ explicit CPngContext(PngDecoder::Delegate* pDelegate); + ~CPngContext() override; + + png_structp m_pPng = nullptr; + png_infop m_pInfo = nullptr; +- UnownedPtr const m_pDelegate; +- char m_szLastError[PNG_ERROR_SIZE]; ++ UnownedPtr const m_pDelegate; ++ char m_szLastError[PNG_ERROR_SIZE] = {}; + }; + + extern "C" { +@@ -59,10 +58,10 @@ void _png_load_bmp_attribute(png_structp png_ptr, + png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type); + switch (unit_type) { + case PNG_RESOLUTION_METER: +- pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_METER; ++ pAttribute->m_wDPIUnit = CFX_DIBAttribute::kResUnitMeter; + break; + default: +- pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_NONE; ++ pAttribute->m_wDPIUnit = CFX_DIBAttribute::kResUnitNone; + } + #endif + #if defined(PNG_iCCP_SUPPORTED) +@@ -130,7 +129,7 @@ void _png_get_header_func(png_structp png_ptr, png_infop info_ptr) { + if (color_type1 != PNG_COLOR_TYPE_PALETTE) { + png_error(pContext->m_pPng, "Not Support Output Palette Now"); + } +- FALLTHROUGH; ++ [[fallthrough]]; + case PNG_COLOR_TYPE_RGB: + case PNG_COLOR_TYPE_RGB_ALPHA: + if (!(color_type1 & PNG_COLOR_MASK_COLOR)) { +@@ -172,10 +171,8 @@ void _png_get_row_func(png_structp png_ptr, + + } // extern "C" + +-CPngContext::CPngContext(PngModule::Delegate* pDelegate) +- : m_pDelegate(pDelegate) { +- memset(m_szLastError, 0, sizeof(m_szLastError)); +-} ++CPngContext::CPngContext(PngDecoder::Delegate* pDelegate) ++ : m_pDelegate(pDelegate) {} + + CPngContext::~CPngContext() { + png_destroy_read_struct(m_pPng ? &m_pPng : nullptr, +@@ -184,12 +181,10 @@ CPngContext::~CPngContext() { + + namespace fxcodec { + +-PngModule::PngModule() = default; +- +-PngModule::~PngModule() = default; +- +-std::unique_ptr PngModule::Start(Delegate* pDelegate) { +- auto p = pdfium::MakeUnique(pDelegate); ++// static ++std::unique_ptr PngDecoder::StartDecode( ++ Delegate* pDelegate) { ++ auto p = std::make_unique(pDelegate); + p->m_pPng = + png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!p->m_pPng) +@@ -209,14 +204,10 @@ std::unique_ptr PngModule::Start(Delegate* pDelegate) { + return p; + } + +-FX_FILESIZE PngModule::GetAvailInput(Context* pContext) const { +- NOTREACHED(); +- return 0; +-} +- +-bool PngModule::Input(Context* pContext, +- RetainPtr codec_memory, +- CFX_DIBAttribute* pAttribute) { ++// static ++bool PngDecoder::ContinueDecode(ProgressiveDecoderIface::Context* pContext, ++ RetainPtr codec_memory, ++ CFX_DIBAttribute* pAttribute) { + auto* ctx = static_cast(pContext); + if (setjmp(png_jmpbuf(ctx->m_pPng))) { + if (pAttribute && +@@ -225,7 +216,7 @@ bool PngModule::Input(Context* pContext, + } + return false; + } +- pdfium::span src_buf = codec_memory->GetSpan(); ++ pdfium::span src_buf = codec_memory->GetUnconsumedSpan(); + png_process_data(ctx->m_pPng, ctx->m_pInfo, src_buf.data(), src_buf.size()); + return true; + } +diff --git a/core/fxcodec/png/pngmodule.h b/core/fxcodec/png/png_decoder.h +similarity index 50% +rename from core/fxcodec/png/pngmodule.h +rename to core/fxcodec/png/png_decoder.h +index ad3fd397f..664ea4426 100644 +--- a/core/fxcodec/png/pngmodule.h ++++ b/core/fxcodec/png/png_decoder.h +@@ -1,19 +1,26 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#ifndef CORE_FXCODEC_PNG_PNGMODULE_H_ +-#define CORE_FXCODEC_PNG_PNGMODULE_H_ ++#ifndef CORE_FXCODEC_PNG_PNG_DECODER_H_ ++#define CORE_FXCODEC_PNG_PNG_DECODER_H_ + + #include + +-#include "core/fxcodec/codec_module_iface.h" ++#include "core/fxcodec/progressive_decoder_iface.h" ++#include "core/fxcrt/retain_ptr.h" ++ ++#ifndef PDF_ENABLE_XFA_PNG ++#error "PNG must be enabled" ++#endif + + namespace fxcodec { + +-class PngModule final : public ModuleIface { ++class CFX_DIBAttribute; ++ ++class PngDecoder { + public: + class Delegate { + public: +@@ -31,20 +38,20 @@ class PngModule final : public ModuleIface { + virtual void PngFillScanlineBufCompleted(int pass, int line) = 0; + }; + +- PngModule(); +- ~PngModule() override; ++ static std::unique_ptr StartDecode( ++ Delegate* pDelegate); + +- // ModuleIface: +- FX_FILESIZE GetAvailInput(Context* pContext) const override; +- bool Input(Context* pContext, +- RetainPtr codec_memory, +- CFX_DIBAttribute* pAttribute) override; ++ static bool ContinueDecode(ProgressiveDecoderIface::Context* pContext, ++ RetainPtr codec_memory, ++ CFX_DIBAttribute* pAttribute); + +- std::unique_ptr Start(Delegate* pDelegate); ++ PngDecoder() = delete; ++ PngDecoder(const PngDecoder&) = delete; ++ PngDecoder& operator=(const PngDecoder&) = delete; + }; + + } // namespace fxcodec + +-using PngModule = fxcodec::PngModule; ++using PngDecoder = fxcodec::PngDecoder; + +-#endif // CORE_FXCODEC_PNG_PNGMODULE_H_ ++#endif // CORE_FXCODEC_PNG_PNG_DECODER_H_ +diff --git a/core/fxcodec/progressivedecoder.cpp b/core/fxcodec/progressive_decoder.cpp +similarity index 55% +rename from core/fxcodec/progressivedecoder.cpp +rename to core/fxcodec/progressive_decoder.cpp +index e948b3908..a65e2bb75 100644 +--- a/core/fxcodec/progressivedecoder.cpp ++++ b/core/fxcodec/progressive_decoder.cpp +@@ -1,10 +1,10 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#include "core/fxcodec/progressivedecoder.h" ++#include "core/fxcodec/progressive_decoder.h" + + #include + #include +@@ -13,15 +13,30 @@ + + #include "build/build_config.h" + #include "core/fxcodec/cfx_codec_memory.h" +-#include "core/fxcodec/fx_codec.h" ++#include "core/fxcodec/jpeg/jpeg_progressive_decoder.h" + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/span_util.h" + #include "core/fxge/dib/cfx_cmyk_to_srgb.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "core/fxge/fx_dib.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/numerics/safe_math.h" +-#include "third_party/base/ptr_util.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/notreached.h" ++#include "third_party/base/numerics/safe_conversions.h" ++ ++#ifdef PDF_ENABLE_XFA_BMP ++#include "core/fxcodec/bmp/bmp_progressive_decoder.h" ++#endif // PDF_ENABLE_XFA_BMP ++ ++#ifdef PDF_ENABLE_XFA_GIF ++#include "core/fxcodec/gif/gif_progressive_decoder.h" ++#endif // PDF_ENABLE_XFA_GIF ++ ++#ifdef PDF_ENABLE_XFA_TIFF ++#include "core/fxcodec/tiff/tiff_decoder.h" ++#endif // PDF_ENABLE_XFA_TIFF + + namespace fxcodec { + +@@ -30,11 +45,11 @@ namespace { + constexpr size_t kBlockSize = 4096; + + #ifdef PDF_ENABLE_XFA_PNG +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + const double kPngGamma = 1.7; + #else + const double kPngGamma = 2.2; +-#endif // defined(OS_MACOSX) ++#endif // BUILDFLAG(IS_APPLE) + #endif // PDF_ENABLE_XFA_PNG + + void RGB2BGR(uint8_t* buffer, int width = 1) { +@@ -52,89 +67,19 @@ void RGB2BGR(uint8_t* buffer, int width = 1) { + + } // namespace + +-ProgressiveDecoder::CFXCODEC_WeightTable::CFXCODEC_WeightTable() = default; +- +-ProgressiveDecoder::CFXCODEC_WeightTable::~CFXCODEC_WeightTable() = default; +- +-void ProgressiveDecoder::CFXCODEC_WeightTable::Calc(int dest_len, int src_len) { +- double scale = static_cast(src_len) / dest_len; +- double base = dest_len < 0 ? src_len : 0.0; +- m_ItemSize = (int)(sizeof(int) * 2 + sizeof(int) * (ceil(fabs(scale)) + 1)); +- m_DestMin = 0; +- m_pWeightTables.resize(dest_len * m_ItemSize + 4); +- if (fabs(scale) < 1.0) { +- for (int dest_pixel = 0; dest_pixel < dest_len; dest_pixel++) { +- PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel); +- double src_pos = dest_pixel * scale + scale / 2 + base; +- pixel_weights.m_SrcStart = (int)floor((float)src_pos - 1.0f / 2); +- pixel_weights.m_SrcEnd = (int)floor((float)src_pos + 1.0f / 2); +- pixel_weights.m_SrcStart = std::max(pixel_weights.m_SrcStart, 0); +- pixel_weights.m_SrcEnd = std::min(pixel_weights.m_SrcEnd, src_len - 1); +- if (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd) { +- pixel_weights.m_Weights[0] = 65536; +- } else { +- pixel_weights.m_Weights[1] = FXSYS_roundf( +- (float)(src_pos - pixel_weights.m_SrcStart - 1.0f / 2) * 65536); +- pixel_weights.m_Weights[0] = 65536 - pixel_weights.m_Weights[1]; +- } +- } +- return; +- } +- for (int dest_pixel = 0; dest_pixel < dest_len; dest_pixel++) { +- PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel); +- double src_start = dest_pixel * scale + base; +- double src_end = src_start + scale; +- int start_i; +- int end_i; +- if (src_start < src_end) { +- start_i = (int)floor((float)src_start); +- end_i = (int)ceil((float)src_end); +- } else { +- start_i = (int)floor((float)src_end); +- end_i = (int)ceil((float)src_start); +- } +- start_i = std::max(start_i, 0); +- end_i = std::min(end_i, src_len - 1); +- if (start_i > end_i) { +- pixel_weights.m_SrcStart = start_i; +- pixel_weights.m_SrcEnd = start_i; +- continue; +- } +- pixel_weights.m_SrcStart = start_i; +- pixel_weights.m_SrcEnd = end_i; +- for (int j = start_i; j <= end_i; j++) { +- double dest_start = ((float)j - base) / scale; +- double dest_end = ((float)(j + 1) - base) / scale; +- if (dest_start > dest_end) { +- double temp = dest_start; +- dest_start = dest_end; +- dest_end = temp; +- } +- double area_start = +- dest_start > (float)(dest_pixel) ? dest_start : (float)(dest_pixel); +- double area_end = dest_end > (float)(dest_pixel + 1) +- ? (float)(dest_pixel + 1) +- : dest_end; +- double weight = area_start >= area_end ? 0.0 : area_end - area_start; +- if (weight == 0 && j == end_i) { +- pixel_weights.m_SrcEnd--; +- break; +- } +- pixel_weights.m_Weights[j - start_i] = +- FXSYS_roundf((float)(weight * 65536)); +- } +- } +-} +- +-ProgressiveDecoder::CFXCODEC_HorzTable::CFXCODEC_HorzTable() = default; ++ProgressiveDecoder::HorzTable::HorzTable() = default; + +-ProgressiveDecoder::CFXCODEC_HorzTable::~CFXCODEC_HorzTable() = default; ++ProgressiveDecoder::HorzTable::~HorzTable() = default; + +-void ProgressiveDecoder::CFXCODEC_HorzTable::Calc(int dest_len, int src_len) { ++void ProgressiveDecoder::HorzTable::CalculateWeights(int dest_len, ++ int src_len) { ++ CHECK_GE(dest_len, 0); ++ m_ItemSize = ++ pdfium::base::checked_cast(PixelWeight::TotalBytesForWeightCount(2)); ++ FX_SAFE_SIZE_T safe_size = m_ItemSize; ++ safe_size *= dest_len; ++ m_pWeightTables.resize(safe_size.ValueOrDie(), 0); + double scale = (double)dest_len / (double)src_len; +- m_ItemSize = sizeof(int) * 4; +- int size = dest_len * m_ItemSize + 4; +- m_pWeightTables.resize(size, 0); + if (scale > 1) { + int pre_dest_col = 0; + for (int src_col = 0; src_col < src_len; src_col++) { +@@ -142,14 +87,14 @@ void ProgressiveDecoder::CFXCODEC_HorzTable::Calc(int dest_len, int src_len) { + int dest_col = FXSYS_roundf((float)dest_col_f); + PixelWeight* pWeight = GetPixelWeight(dest_col); + pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col; +- pWeight->m_Weights[0] = 65536; ++ pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne; + pWeight->m_Weights[1] = 0; + if (src_col == src_len - 1 && dest_col < dest_len - 1) { + for (int dest_col_index = pre_dest_col + 1; dest_col_index < dest_len; + dest_col_index++) { + pWeight = GetPixelWeight(dest_col_index); + pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col; +- pWeight->m_Weights[0] = 65536; ++ pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne; + pWeight->m_Weights[1] = 0; + } + return; +@@ -160,10 +105,10 @@ void ProgressiveDecoder::CFXCODEC_HorzTable::Calc(int dest_len, int src_len) { + pWeight = GetPixelWeight(dest_col_index); + pWeight->m_SrcStart = src_col - 1; + pWeight->m_SrcEnd = src_col; +- pWeight->m_Weights[0] = +- FXSYS_roundf((float)(((float)dest_col - (float)dest_col_index) / +- (float)dest_col_len * 65536)); +- pWeight->m_Weights[1] = 65536 - pWeight->m_Weights[0]; ++ pWeight->m_Weights[0] = CStretchEngine::FixedFromFloat( ++ ((float)dest_col - (float)dest_col_index) / (float)dest_col_len); ++ pWeight->m_Weights[1] = ++ CStretchEngine::kFixedPointOne - pWeight->m_Weights[0]; + } + pre_dest_col = dest_col; + } +@@ -174,26 +119,30 @@ void ProgressiveDecoder::CFXCODEC_HorzTable::Calc(int dest_len, int src_len) { + int src_col = FXSYS_roundf((float)src_col_f); + PixelWeight* pWeight = GetPixelWeight(dest_col); + pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col; +- pWeight->m_Weights[0] = 65536; ++ pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne; + pWeight->m_Weights[1] = 0; + } + } + +-ProgressiveDecoder::CFXCODEC_VertTable::CFXCODEC_VertTable() = default; ++ProgressiveDecoder::VertTable::VertTable() = default; + +-ProgressiveDecoder::CFXCODEC_VertTable::~CFXCODEC_VertTable() = default; ++ProgressiveDecoder::VertTable::~VertTable() = default; + +-void ProgressiveDecoder::CFXCODEC_VertTable::Calc(int dest_len, int src_len) { ++void ProgressiveDecoder::VertTable::CalculateWeights(int dest_len, ++ int src_len) { ++ CHECK_GE(dest_len, 0); ++ m_ItemSize = ++ pdfium::base::checked_cast(PixelWeight::TotalBytesForWeightCount(2)); ++ FX_SAFE_SIZE_T safe_size = m_ItemSize; ++ safe_size *= dest_len; ++ m_pWeightTables.resize(safe_size.ValueOrDie(), 0); + double scale = (double)dest_len / (double)src_len; +- m_ItemSize = sizeof(int) * 4; +- int size = dest_len * m_ItemSize + 4; +- m_pWeightTables.resize(size, 0); + if (scale <= 1) { + for (int dest_row = 0; dest_row < dest_len; dest_row++) { + PixelWeight* pWeight = GetPixelWeight(dest_row); + pWeight->m_SrcStart = dest_row; + pWeight->m_SrcEnd = dest_row; +- pWeight->m_Weights[0] = 65536; ++ pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne; + pWeight->m_Weights[1] = 0; + } + return; +@@ -211,7 +160,7 @@ void ProgressiveDecoder::CFXCODEC_VertTable::Calc(int dest_len, int src_len) { + PixelWeight* pWeight = GetPixelWeight(dest_row); + pWeight->m_SrcStart = start_step; + pWeight->m_SrcEnd = start_step; +- pWeight->m_Weights[0] = 65536; ++ pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne; + pWeight->m_Weights[1] = 0; + } + return; +@@ -221,22 +170,22 @@ void ProgressiveDecoder::CFXCODEC_VertTable::Calc(int dest_len, int src_len) { + PixelWeight* pWeight = GetPixelWeight(start_step); + pWeight->m_SrcStart = start_step; + pWeight->m_SrcEnd = start_step; +- pWeight->m_Weights[0] = 65536; ++ pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne; + pWeight->m_Weights[1] = 0; + } + for (int dest_row = start_step + 1; dest_row < end_step; dest_row++) { + PixelWeight* pWeight = GetPixelWeight(dest_row); + pWeight->m_SrcStart = start_step; + pWeight->m_SrcEnd = end_step; +- pWeight->m_Weights[0] = +- FXSYS_roundf((float)(end_step - dest_row) / (float)length * 65536); +- pWeight->m_Weights[1] = 65536 - pWeight->m_Weights[0]; ++ pWeight->m_Weights[0] = CStretchEngine::FixedFromFloat( ++ (float)(end_step - dest_row) / (float)length); ++ pWeight->m_Weights[1] = ++ CStretchEngine::kFixedPointOne - pWeight->m_Weights[0]; + } + } + } + +-ProgressiveDecoder::ProgressiveDecoder(ModuleMgr* pCodecMgr) +- : m_pCodecMgr(pCodecMgr) {} ++ProgressiveDecoder::ProgressiveDecoder() = default; + + ProgressiveDecoder::~ProgressiveDecoder() = default; + +@@ -275,24 +224,22 @@ bool ProgressiveDecoder::PngReadHeader(int width, + } + FXDIB_Format format = m_pDeviceBitmap->GetFormat(); + switch (format) { +- case FXDIB_1bppMask: +- case FXDIB_1bppRgb: +- NOTREACHED(); +- return false; +- case FXDIB_8bppMask: +- case FXDIB_8bppRgb: ++ case FXDIB_Format::k1bppMask: ++ case FXDIB_Format::k1bppRgb: ++ NOTREACHED_NORETURN(); ++ case FXDIB_Format::k8bppMask: ++ case FXDIB_Format::k8bppRgb: + *color_type = 0; + break; +- case FXDIB_Rgb: ++ case FXDIB_Format::kRgb: + *color_type = 2; + break; +- case FXDIB_Rgb32: +- case FXDIB_Argb: ++ case FXDIB_Format::kRgb32: ++ case FXDIB_Format::kArgb: + *color_type = 6; + break; + default: +- NOTREACHED(); +- return false; ++ NOTREACHED_NORETURN(); + } + *gamma = kPngGamma; + return true; +@@ -300,80 +247,88 @@ bool ProgressiveDecoder::PngReadHeader(int width, + + bool ProgressiveDecoder::PngAskScanlineBuf(int line, uint8_t** pSrcBuf) { + RetainPtr pDIBitmap = m_pDeviceBitmap; +- if (!pDIBitmap) { +- NOTREACHED(); +- return false; +- } +- if (line >= m_clipBox.top && line < m_clipBox.bottom) { +- double scale_y = static_cast(m_sizeY) / m_clipBox.Height(); +- int32_t row = (int32_t)((line - m_clipBox.top) * scale_y) + m_startY; +- const uint8_t* src_scan = pDIBitmap->GetScanline(row); +- uint8_t* dest_scan = m_pDecodeBuf.get(); +- *pSrcBuf = m_pDecodeBuf.get(); +- int32_t src_Bpp = pDIBitmap->GetBPP() >> 3; +- int32_t dest_Bpp = (m_SrcFormat & 0xff) >> 3; +- int32_t src_left = m_startX; +- int32_t dest_left = m_clipBox.left; +- src_scan += src_left * src_Bpp; +- dest_scan += dest_left * dest_Bpp; +- for (int32_t src_col = 0; src_col < m_sizeX; src_col++) { +- PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(src_col); +- if (pPixelWeights->m_SrcStart != pPixelWeights->m_SrcEnd) { +- continue; ++ CHECK(pDIBitmap); ++ if (line < m_clipBox.top || line >= m_clipBox.bottom) ++ return true; ++ ++ double scale_y = static_cast(m_sizeY) / m_clipBox.Height(); ++ int32_t row = ++ static_cast((line - m_clipBox.top) * scale_y) + m_startY; ++ *pSrcBuf = m_DecodeBuf.data(); ++ int32_t src_Bpp = pDIBitmap->GetBPP() >> 3; ++ int32_t dest_Bpp = (m_SrcFormat & 0xff) >> 3; ++ int32_t src_left = m_startX; ++ int32_t dest_left = m_clipBox.left; ++ pdfium::span src_span = ++ pDIBitmap->GetScanline(row).subspan(src_left * src_Bpp); ++ pdfium::span dest_span = ++ pdfium::make_span(m_DecodeBuf).subspan(dest_left * dest_Bpp); ++ const uint8_t* src_scan = src_span.data(); ++ uint8_t* dest_scan = dest_span.data(); ++ switch (pDIBitmap->GetFormat()) { ++ case FXDIB_Format::k1bppMask: ++ case FXDIB_Format::k1bppRgb: ++ for (int32_t src_col = 0; src_col < m_sizeX; src_col++) { ++ PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(src_col); ++ if (pPixelWeights->m_SrcStart != pPixelWeights->m_SrcEnd) ++ continue; ++ NOTREACHED_NORETURN(); + } +- switch (pDIBitmap->GetFormat()) { +- case FXDIB_1bppMask: +- case FXDIB_1bppRgb: +- NOTREACHED(); +- return false; +- case FXDIB_8bppMask: +- case FXDIB_8bppRgb: { +- if (pDIBitmap->GetPalette()) { +- return false; +- } +- uint32_t dest_g = 0; +- dest_g += pPixelWeights->m_Weights[0] * src_scan[src_col]; +- dest_scan[pPixelWeights->m_SrcStart] = (uint8_t)(dest_g >> 16); +- } break; +- case FXDIB_Rgb: +- case FXDIB_Rgb32: { +- uint32_t dest_b = 0; +- uint32_t dest_g = 0; +- uint32_t dest_r = 0; +- const uint8_t* p = src_scan + src_col * src_Bpp; +- dest_b += pPixelWeights->m_Weights[0] * (*p++); +- dest_g += pPixelWeights->m_Weights[0] * (*p++); +- dest_r += pPixelWeights->m_Weights[0] * (*p); +- uint8_t* pDes = &dest_scan[pPixelWeights->m_SrcStart * dest_Bpp]; +- *pDes++ = (uint8_t)((dest_b) >> 16); +- *pDes++ = (uint8_t)((dest_g) >> 16); +- *pDes = (uint8_t)((dest_r) >> 16); +- } break; +- case FXDIB_Argb: { +- uint32_t dest_r = 0; +- uint32_t dest_g = 0; +- uint32_t dest_b = 0; +- const uint8_t* p = src_scan + src_col * src_Bpp; +- dest_b += pPixelWeights->m_Weights[0] * (*p++); +- dest_g += pPixelWeights->m_Weights[0] * (*p++); +- dest_r += pPixelWeights->m_Weights[0] * (*p++); +- uint8_t* pDes = &dest_scan[pPixelWeights->m_SrcStart * dest_Bpp]; +- *pDes++ = (uint8_t)((dest_b) >> 16); +- *pDes++ = (uint8_t)((dest_g) >> 16); +- *pDes++ = (uint8_t)((dest_r) >> 16); +- *pDes = *p; +- } break; +- default: +- return false; ++ return true; ++ case FXDIB_Format::k8bppMask: ++ case FXDIB_Format::k8bppRgb: ++ if (pDIBitmap->HasPalette()) ++ return false; ++ for (int32_t src_col = 0; src_col < m_sizeX; src_col++) { ++ PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(src_col); ++ if (pPixelWeights->m_SrcStart != pPixelWeights->m_SrcEnd) ++ continue; ++ uint32_t dest_g = pPixelWeights->m_Weights[0] * src_scan[src_col]; ++ dest_scan[pPixelWeights->m_SrcStart] = ++ CStretchEngine::PixelFromFixed(dest_g); + } +- } ++ return true; ++ case FXDIB_Format::kRgb: ++ case FXDIB_Format::kRgb32: ++ for (int32_t src_col = 0; src_col < m_sizeX; src_col++) { ++ PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(src_col); ++ if (pPixelWeights->m_SrcStart != pPixelWeights->m_SrcEnd) ++ continue; ++ const uint8_t* p = src_scan + src_col * src_Bpp; ++ uint32_t dest_b = pPixelWeights->m_Weights[0] * (*p++); ++ uint32_t dest_g = pPixelWeights->m_Weights[0] * (*p++); ++ uint32_t dest_r = pPixelWeights->m_Weights[0] * (*p); ++ uint8_t* pDes = &dest_scan[pPixelWeights->m_SrcStart * dest_Bpp]; ++ *pDes++ = CStretchEngine::PixelFromFixed(dest_b); ++ *pDes++ = CStretchEngine::PixelFromFixed(dest_g); ++ *pDes = CStretchEngine::PixelFromFixed(dest_r); ++ } ++ return true; ++ case FXDIB_Format::kArgb: ++ for (int32_t src_col = 0; src_col < m_sizeX; src_col++) { ++ PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(src_col); ++ if (pPixelWeights->m_SrcStart != pPixelWeights->m_SrcEnd) ++ continue; ++ const uint8_t* p = src_scan + src_col * src_Bpp; ++ uint32_t dest_b = pPixelWeights->m_Weights[0] * (*p++); ++ uint32_t dest_g = pPixelWeights->m_Weights[0] * (*p++); ++ uint32_t dest_r = pPixelWeights->m_Weights[0] * (*p++); ++ uint8_t dest_a = *p; ++ uint8_t* pDes = &dest_scan[pPixelWeights->m_SrcStart * dest_Bpp]; ++ *pDes++ = CStretchEngine::PixelFromFixed(dest_b); ++ *pDes++ = CStretchEngine::PixelFromFixed(dest_g); ++ *pDes++ = CStretchEngine::PixelFromFixed(dest_r); ++ *pDes = dest_a; ++ } ++ return true; ++ default: ++ return false; + } +- return true; + } + + void ProgressiveDecoder::PngFillScanlineBufCompleted(int pass, int line) { + RetainPtr pDIBitmap = m_pDeviceBitmap; +- ASSERT(pDIBitmap); ++ DCHECK(pDIBitmap); + int src_top = m_clipBox.top; + int src_bottom = m_clipBox.bottom; + int dest_top = m_startY; +@@ -386,8 +341,7 @@ void ProgressiveDecoder::PngFillScanlineBufCompleted(int pass, int line) { + if (dest_row >= dest_top + dest_height) { + return; + } +- PngOneOneMapResampleHorz(pDIBitmap, dest_row, m_pDecodeBuf.get(), +- m_SrcFormat); ++ PngOneOneMapResampleHorz(pDIBitmap, dest_row, m_DecodeBuf, m_SrcFormat); + if (m_SrcPassNumber == 1 && scale_y > 1.0) { + ResampleVert(pDIBitmap, scale_y, dest_row); + return; +@@ -400,28 +354,25 @@ void ProgressiveDecoder::PngFillScanlineBufCompleted(int pass, int line) { + #endif // PDF_ENABLE_XFA_PNG + + #ifdef PDF_ENABLE_XFA_GIF +-void ProgressiveDecoder::GifRecordCurrentPosition(uint32_t& cur_pos) { +- uint32_t remain_size = +- m_pCodecMgr->GetGifModule()->GetAvailInput(m_pGifContext.get()); +- cur_pos = m_offSet - remain_size; ++uint32_t ProgressiveDecoder::GifCurrentPosition() const { ++ uint32_t remain_size = pdfium::base::checked_cast( ++ GifDecoder::GetAvailInput(m_pGifContext.get())); ++ return m_offSet - remain_size; + } + + bool ProgressiveDecoder::GifInputRecordPositionBuf(uint32_t rcd_pos, + const FX_RECT& img_rc, + int32_t pal_num, + CFX_GifPalette* pal_ptr, +- int32_t delay_time, +- bool user_input, + int32_t trans_index, +- int32_t disposal_method, + bool interlace) { + m_offSet = rcd_pos; +- m_InvalidateGifBuffer = true; + +- FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR; +- if (!GifReadMoreData(m_pCodecMgr->GetGifModule(), error_status)) { ++ FXCODEC_STATUS error_status = FXCODEC_STATUS::kError; ++ m_pCodecMemory->Seek(m_pCodecMemory->GetSize()); ++ if (!GifReadMoreData(&error_status)) + return false; +- } ++ + CFX_GifPalette* pPalette = nullptr; + if (pal_num != 0 && pal_ptr) { + pPalette = pal_ptr; +@@ -431,16 +382,10 @@ bool ProgressiveDecoder::GifInputRecordPositionBuf(uint32_t rcd_pos, + pal_num = m_GifPltNumber; + pPalette = m_pGifPalette; + } +- if (!m_pSrcPalette) +- m_pSrcPalette.reset(FX_Alloc(FX_ARGB, pal_num)); +- else if (pal_num > m_SrcPaletteNumber) +- m_pSrcPalette.reset(FX_Realloc(FX_ARGB, m_pSrcPalette.release(), pal_num)); +- if (!m_pSrcPalette) +- return false; +- ++ m_SrcPalette.resize(pal_num); + m_SrcPaletteNumber = pal_num; + for (int i = 0; i < pal_num; i++) { +- m_pSrcPalette.get()[i] = ++ m_SrcPalette[i] = + ArgbEncode(0xff, pPalette[i].r, pPalette[i].g, pPalette[i].b); + } + m_GifTransIndex = trans_index; +@@ -451,8 +396,8 @@ bool ProgressiveDecoder::GifInputRecordPositionBuf(uint32_t rcd_pos, + if (trans_index >= pal_num) + trans_index = -1; + if (trans_index != -1) { +- m_pSrcPalette.get()[trans_index] &= 0x00ffffff; +- if (pDevice->HasAlpha()) ++ m_SrcPalette[trans_index] &= 0x00ffffff; ++ if (pDevice->IsAlphaFormat()) + pal_index = trans_index; + } + if (pal_index >= pal_num) +@@ -463,10 +408,10 @@ bool ProgressiveDecoder::GifInputRecordPositionBuf(uint32_t rcd_pos, + int sizeX = m_sizeX; + int sizeY = m_sizeY; + int Bpp = pDevice->GetBPP() / 8; +- FX_ARGB argb = m_pSrcPalette.get()[pal_index]; ++ FX_ARGB argb = m_SrcPalette[pal_index]; + for (int row = 0; row < sizeY; row++) { + uint8_t* pScanline = +- pDevice->GetWritableScanline(row + startY) + startX * Bpp; ++ pDevice->GetWritableScanline(row + startY).subspan(startX * Bpp).data(); + switch (m_TransMethod) { + case 3: { + uint8_t gray = +@@ -495,28 +440,31 @@ bool ProgressiveDecoder::GifInputRecordPositionBuf(uint32_t rcd_pos, + return true; + } + +-void ProgressiveDecoder::GifReadScanline(int32_t row_num, uint8_t* row_buf) { ++void ProgressiveDecoder::GifReadScanline(int32_t row_num, ++ pdfium::span row_buf) { + RetainPtr pDIBitmap = m_pDeviceBitmap; +- ASSERT(pDIBitmap); ++ DCHECK(pDIBitmap); + int32_t img_width = m_GifFrameRect.Width(); +- if (!pDIBitmap->HasAlpha()) { +- uint8_t* byte_ptr = row_buf; ++ if (!pDIBitmap->IsAlphaFormat()) { ++ pdfium::span byte_span = row_buf; + for (int i = 0; i < img_width; i++) { +- if (*byte_ptr == m_GifTransIndex) { +- *byte_ptr = m_GifBgIndex; ++ if (byte_span.front() == m_GifTransIndex) { ++ byte_span.front() = m_GifBgIndex; + } +- byte_ptr++; ++ byte_span = byte_span.subspan(1); + } + } + int32_t pal_index = m_GifBgIndex; +- if (m_GifTransIndex != -1 && m_pDeviceBitmap->HasAlpha()) { ++ if (m_GifTransIndex != -1 && m_pDeviceBitmap->IsAlphaFormat()) { + pal_index = m_GifTransIndex; + } +- memset(m_pDecodeBuf.get(), pal_index, m_SrcWidth); ++ const int32_t left = m_GifFrameRect.left; ++ const pdfium::span decode_span = m_DecodeBuf; ++ fxcrt::spanset(decode_span.first(m_SrcWidth), pal_index); ++ fxcrt::spancpy(decode_span.subspan(left), row_buf.first(img_width)); ++ + bool bLastPass = (row_num % 2) == 1; + int32_t line = row_num + m_GifFrameRect.top; +- int32_t left = m_GifFrameRect.left; +- memcpy(m_pDecodeBuf.get() + left, row_buf, img_width); + int src_top = m_clipBox.top; + int src_bottom = m_clipBox.bottom; + int dest_top = m_startY; +@@ -531,7 +479,7 @@ void ProgressiveDecoder::GifReadScanline(int32_t row_num, uint8_t* row_buf) { + if (dest_row >= dest_top + dest_height) + return; + +- ReSampleScanline(pDIBitmap, dest_row, m_pDecodeBuf.get(), m_SrcFormat); ++ ResampleScanline(pDIBitmap, dest_row, decode_span, m_SrcFormat); + if (scale_y > 1.0 && m_SrcPassNumber == 1) { + ResampleVert(pDIBitmap, scale_y, dest_row); + return; +@@ -541,13 +489,15 @@ void ProgressiveDecoder::GifReadScanline(int32_t row_num, uint8_t* row_buf) { + + int dest_bottom = dest_top + m_sizeY; + int dest_Bpp = pDIBitmap->GetBPP() >> 3; +- uint32_t dest_ScanOffet = m_startX * dest_Bpp; ++ uint32_t dest_ScanOffset = m_startX * dest_Bpp; + if (dest_row + (int)scale_y >= dest_bottom - 1) { +- const uint8_t* scan_src = pDIBitmap->GetScanline(dest_row) + dest_ScanOffet; ++ const uint8_t* scan_src = ++ pDIBitmap->GetScanline(dest_row).subspan(dest_ScanOffset).data(); + int cur_row = dest_row; + while (++cur_row < dest_bottom) { +- uint8_t* scan_des = +- pDIBitmap->GetWritableScanline(cur_row) + dest_ScanOffet; ++ uint8_t* scan_des = pDIBitmap->GetWritableScanline(cur_row) ++ .subspan(dest_ScanOffset) ++ .data(); + uint32_t size = m_sizeX * dest_Bpp; + memmove(scan_des, scan_src, size); + } +@@ -560,18 +510,16 @@ void ProgressiveDecoder::GifReadScanline(int32_t row_num, uint8_t* row_buf) { + #ifdef PDF_ENABLE_XFA_BMP + bool ProgressiveDecoder::BmpInputImagePositionBuf(uint32_t rcd_pos) { + m_offSet = rcd_pos; +- FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR; +- return BmpReadMoreData(m_pCodecMgr->GetBmpModule(), m_pBmpContext.get(), +- error_status); ++ FXCODEC_STATUS error_status = FXCODEC_STATUS::kError; ++ return BmpReadMoreData(m_pBmpContext.get(), &error_status); + } + + void ProgressiveDecoder::BmpReadScanline(uint32_t row_num, + pdfium::span row_buf) { + RetainPtr pDIBitmap = m_pDeviceBitmap; +- ASSERT(pDIBitmap); ++ DCHECK(pDIBitmap); + +- pdfium::span src_span = row_buf.first(m_ScanlineSize); +- std::copy(std::begin(src_span), std::end(src_span), m_pDecodeBuf.get()); ++ fxcrt::spancpy(pdfium::make_span(m_DecodeBuf), row_buf.first(m_ScanlineSize)); + + int src_top = m_clipBox.top; + int src_bottom = m_clipBox.bottom; +@@ -589,7 +537,7 @@ void ProgressiveDecoder::BmpReadScanline(uint32_t row_num, + if (dest_row >= dest_top + dest_height) + return; + +- ReSampleScanline(pDIBitmap, dest_row, m_pDecodeBuf.get(), m_SrcFormat); ++ ResampleScanline(pDIBitmap, dest_row, m_DecodeBuf, m_SrcFormat); + if (scale_y <= 1.0) + return; + +@@ -605,154 +553,152 @@ void ProgressiveDecoder::ResampleVertBT( + double scale_y, + int dest_row) { + int dest_Bpp = pDeviceBitmap->GetBPP() >> 3; +- uint32_t dest_ScanOffet = m_startX * dest_Bpp; ++ uint32_t dest_ScanOffset = m_startX * dest_Bpp; + int dest_top = m_startY; + int dest_bottom = m_startY + m_sizeY; +- pdfium::base::CheckedNumeric check_dest_row_1 = dest_row; ++ FX_SAFE_INT32 check_dest_row_1 = dest_row; + check_dest_row_1 += pdfium::base::checked_cast(scale_y); + int dest_row_1 = check_dest_row_1.ValueOrDie(); + if (dest_row_1 >= dest_bottom - 1) { + const uint8_t* scan_src = +- pDeviceBitmap->GetScanline(dest_row) + dest_ScanOffet; ++ pDeviceBitmap->GetScanline(dest_row).subspan(dest_ScanOffset).data(); + while (++dest_row < dest_bottom) { +- uint8_t* scan_des = +- pDeviceBitmap->GetWritableScanline(dest_row) + dest_ScanOffet; ++ uint8_t* scan_des = pDeviceBitmap->GetWritableScanline(dest_row) ++ .subspan(dest_ScanOffset) ++ .data(); + uint32_t size = m_sizeX * dest_Bpp; + memmove(scan_des, scan_src, size); + } + return; + } + for (; dest_row_1 > dest_row; dest_row_1--) { +- uint8_t* scan_des = +- pDeviceBitmap->GetWritableScanline(dest_row_1) + dest_ScanOffet; ++ uint8_t* scan_des = pDeviceBitmap->GetWritableScanline(dest_row_1) ++ .subspan(dest_ScanOffset) ++ .data(); + PixelWeight* pWeight = m_WeightVert.GetPixelWeight(dest_row_1 - dest_top); + const uint8_t* scan_src1 = +- pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top) + +- dest_ScanOffet; ++ pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top) ++ .subspan(dest_ScanOffset) ++ .data(); + const uint8_t* scan_src2 = +- pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top) + +- dest_ScanOffet; +- for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { +- switch (pDeviceBitmap->GetFormat()) { +- case FXDIB_Invalid: +- case FXDIB_1bppMask: +- case FXDIB_1bppRgb: ++ pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top) ++ .subspan(dest_ScanOffset) ++ .data(); ++ switch (pDeviceBitmap->GetFormat()) { ++ case FXDIB_Format::kInvalid: ++ case FXDIB_Format::k1bppMask: ++ case FXDIB_Format::k1bppRgb: ++ return; ++ case FXDIB_Format::k8bppMask: ++ case FXDIB_Format::k8bppRgb: ++ if (pDeviceBitmap->HasPalette()) + return; +- case FXDIB_8bppMask: +- case FXDIB_8bppRgb: { +- if (pDeviceBitmap->GetPalette()) { +- return; +- } +- int dest_g = 0; +- dest_g += pWeight->m_Weights[0] * (*scan_src1++); +- dest_g += pWeight->m_Weights[1] * (*scan_src2++); +- *scan_des++ = (uint8_t)(dest_g >> 16); +- } break; +- case FXDIB_Rgb: +- case FXDIB_Rgb32: { +- uint32_t dest_b = 0; ++ for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { + uint32_t dest_g = 0; +- uint32_t dest_r = 0; +- dest_b += pWeight->m_Weights[0] * (*scan_src1++); + dest_g += pWeight->m_Weights[0] * (*scan_src1++); +- dest_r += pWeight->m_Weights[0] * (*scan_src1++); ++ dest_g += pWeight->m_Weights[1] * (*scan_src2++); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); ++ } ++ break; ++ case FXDIB_Format::kRgb: ++ case FXDIB_Format::kRgb32: ++ for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { ++ uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++); ++ uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++); ++ uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++); + scan_src1 += dest_Bpp - 3; + dest_b += pWeight->m_Weights[1] * (*scan_src2++); + dest_g += pWeight->m_Weights[1] * (*scan_src2++); + dest_r += pWeight->m_Weights[1] * (*scan_src2++); + scan_src2 += dest_Bpp - 3; +- *scan_des++ = (uint8_t)((dest_b) >> 16); +- *scan_des++ = (uint8_t)((dest_g) >> 16); +- *scan_des++ = (uint8_t)((dest_r) >> 16); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_b); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_r); + scan_des += dest_Bpp - 3; +- } break; +- case FXDIB_Argb: { +- uint32_t dest_a = 0; +- uint32_t dest_b = 0; +- uint32_t dest_g = 0; +- uint32_t dest_r = 0; +- dest_b += pWeight->m_Weights[0] * (*scan_src1++); +- dest_g += pWeight->m_Weights[0] * (*scan_src1++); +- dest_r += pWeight->m_Weights[0] * (*scan_src1++); +- dest_a += pWeight->m_Weights[0] * (*scan_src1++); ++ } ++ break; ++ case FXDIB_Format::kArgb: ++ for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { ++ uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++); ++ uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++); ++ uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++); ++ uint32_t dest_a = pWeight->m_Weights[0] * (*scan_src1++); + dest_b += pWeight->m_Weights[1] * (*scan_src2++); + dest_g += pWeight->m_Weights[1] * (*scan_src2++); + dest_r += pWeight->m_Weights[1] * (*scan_src2++); + dest_a += pWeight->m_Weights[1] * (*scan_src2++); +- *scan_des++ = (uint8_t)((dest_b) >> 16); +- *scan_des++ = (uint8_t)((dest_g) >> 16); +- *scan_des++ = (uint8_t)((dest_r) >> 16); +- *scan_des++ = (uint8_t)((dest_a) >> 16); +- } break; +- default: +- return; +- } ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_b); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_r); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_a); ++ } ++ break; ++ default: ++ return; + } + } + } + + bool ProgressiveDecoder::BmpDetectImageTypeInBuffer( + CFX_DIBAttribute* pAttribute) { +- BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule(); +- if (!pBmpModule) { +- m_status = FXCODEC_STATUS_ERR_MEMORY; +- return false; +- } +- +- std::unique_ptr pBmpContext = pBmpModule->Start(this); +- pBmpModule->Input(pBmpContext.get(), m_pCodecMemory, nullptr); ++ std::unique_ptr pBmpContext = ++ BmpDecoder::StartDecode(this); ++ BmpDecoder::Input(pBmpContext.get(), m_pCodecMemory); + + const std::vector* palette; +- BmpModule::Status read_result = pBmpModule->ReadHeader( ++ BmpDecoder::Status read_result = BmpDecoder::ReadHeader( + pBmpContext.get(), &m_SrcWidth, &m_SrcHeight, &m_BmpIsTopBottom, + &m_SrcComponents, &m_SrcPaletteNumber, &palette, pAttribute); +- while (read_result == BmpModule::Status::kContinue) { +- FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT; +- if (!BmpReadMoreData(pBmpModule, pBmpContext.get(), error_status)) { ++ while (read_result == BmpDecoder::Status::kContinue) { ++ FXCODEC_STATUS error_status = FXCODEC_STATUS::kError; ++ if (!BmpReadMoreData(pBmpContext.get(), &error_status)) { + m_status = error_status; + return false; + } +- read_result = pBmpModule->ReadHeader( ++ read_result = BmpDecoder::ReadHeader( + pBmpContext.get(), &m_SrcWidth, &m_SrcHeight, &m_BmpIsTopBottom, + &m_SrcComponents, &m_SrcPaletteNumber, &palette, pAttribute); + } + +- if (read_result != BmpModule::Status::kSuccess) { +- m_status = FXCODEC_STATUS_ERR_FORMAT; ++ if (read_result != BmpDecoder::Status::kSuccess) { ++ m_status = FXCODEC_STATUS::kError; + return false; + } + +- FXDIB_Format format = FXDIB_Invalid; ++ FXDIB_Format format = FXDIB_Format::kInvalid; + switch (m_SrcComponents) { + case 1: + m_SrcFormat = FXCodec_8bppRgb; +- format = FXDIB_8bppRgb; ++ format = FXDIB_Format::k8bppRgb; + break; + case 3: + m_SrcFormat = FXCodec_Rgb; +- format = FXDIB_Rgb; ++ format = FXDIB_Format::kRgb; + break; + case 4: + m_SrcFormat = FXCodec_Rgb32; +- format = FXDIB_Rgb32; ++ format = FXDIB_Format::kRgb32; + break; + default: +- m_status = FXCODEC_STATUS_ERR_FORMAT; ++ m_status = FXCODEC_STATUS::kError; + return false; + } + +- uint32_t pitch = 0; +- uint32_t neededData = 0; +- if (!CFX_DIBitmap::CalculatePitchAndSize(m_SrcWidth, m_SrcHeight, format, +- &pitch, &neededData)) { +- m_status = FXCODEC_STATUS_ERR_FORMAT; ++ // Set to 0 to make CalculatePitchAndSize() calculate it. ++ constexpr uint32_t kNoPitch = 0; ++ absl::optional needed_data = ++ CFX_DIBitmap::CalculatePitchAndSize(m_SrcWidth, m_SrcHeight, format, ++ kNoPitch); ++ if (!needed_data.has_value()) { ++ m_status = FXCODEC_STATUS::kError; + return false; + } + +- uint32_t availableData = m_pFile->GetSize() - m_offSet + +- pBmpModule->GetAvailInput(pBmpContext.get()); +- if (neededData > availableData) { +- m_status = FXCODEC_STATUS_ERR_FORMAT; ++ uint32_t available_data = pdfium::base::checked_cast( ++ m_pFile->GetSize() - m_offSet + ++ BmpDecoder::GetAvailInput(pBmpContext.get())); ++ if (needed_data.value().size > available_data) { ++ m_status = FXCODEC_STATUS::kError; + return false; + } + +@@ -760,163 +706,130 @@ bool ProgressiveDecoder::BmpDetectImageTypeInBuffer( + m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight); + m_pBmpContext = std::move(pBmpContext); + if (m_SrcPaletteNumber) { +- m_pSrcPalette.reset(FX_Alloc(FX_ARGB, m_SrcPaletteNumber)); +- memcpy(m_pSrcPalette.get(), palette->data(), ++ m_SrcPalette.resize(m_SrcPaletteNumber); ++ memcpy(m_SrcPalette.data(), palette->data(), + m_SrcPaletteNumber * sizeof(FX_ARGB)); + } else { +- m_pSrcPalette.reset(); ++ m_SrcPalette.clear(); + } + return true; + } + +-bool ProgressiveDecoder::BmpReadMoreData(BmpModule* pBmpModule, +- ModuleIface::Context* pContext, +- FXCODEC_STATUS& err_status) { +- return ReadMoreData(pBmpModule, pContext, false, err_status); ++bool ProgressiveDecoder::BmpReadMoreData( ++ ProgressiveDecoderIface::Context* pContext, ++ FXCODEC_STATUS* err_status) { ++ return ReadMoreData(BmpProgressiveDecoder::GetInstance(), pContext, ++ err_status); + } + +-FXCODEC_STATUS ProgressiveDecoder::BmpStartDecode( +- const RetainPtr& pDIBitmap) { +- BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule(); +- if (!pBmpModule) { +- m_pDeviceBitmap = nullptr; +- m_pFile = nullptr; +- m_status = FXCODEC_STATUS_ERR_MEMORY; +- return m_status; +- } ++FXCODEC_STATUS ProgressiveDecoder::BmpStartDecode() { + GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat); + m_ScanlineSize = FxAlignToBoundary<4>(m_SrcWidth * m_SrcComponents); +- m_pDecodeBuf.reset(FX_Alloc(uint8_t, m_ScanlineSize)); +- m_WeightHorz.Calc(m_sizeX, m_clipBox.Width()); +- m_WeightVert.Calc(m_sizeY, m_clipBox.Height()); +- m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE; ++ m_DecodeBuf.resize(m_ScanlineSize); ++ FXDIB_ResampleOptions options; ++ options.bInterpolateBilinear = true; ++ m_WeightHorz.CalculateWeights(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0, ++ m_clipBox.Width(), options); ++ m_WeightVert.CalculateWeights(m_sizeY, m_clipBox.Height()); ++ m_status = FXCODEC_STATUS::kDecodeToBeContinued; + return m_status; + } + + FXCODEC_STATUS ProgressiveDecoder::BmpContinueDecode() { +- BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule(); +- if (!pBmpModule) { +- m_status = FXCODEC_STATUS_ERR_MEMORY; +- return m_status; +- } +- +- BmpModule::Status read_res = pBmpModule->LoadImage(m_pBmpContext.get()); +- while (read_res == BmpModule::Status::kContinue) { +- FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH; +- if (!BmpReadMoreData(pBmpModule, m_pBmpContext.get(), error_status)) { ++ BmpDecoder::Status read_res = BmpDecoder::LoadImage(m_pBmpContext.get()); ++ while (read_res == BmpDecoder::Status::kContinue) { ++ FXCODEC_STATUS error_status = FXCODEC_STATUS::kDecodeFinished; ++ if (!BmpReadMoreData(m_pBmpContext.get(), &error_status)) { + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; + m_status = error_status; + return m_status; + } +- read_res = pBmpModule->LoadImage(m_pBmpContext.get()); ++ read_res = BmpDecoder::LoadImage(m_pBmpContext.get()); + } + + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; +- m_status = read_res == BmpModule::Status::kSuccess +- ? FXCODEC_STATUS_DECODE_FINISH +- : FXCODEC_STATUS_ERROR; ++ m_status = read_res == BmpDecoder::Status::kSuccess ++ ? FXCODEC_STATUS::kDecodeFinished ++ : FXCODEC_STATUS::kError; + return m_status; + } + #endif // PDF_ENABLE_XFA_BMP + + #ifdef PDF_ENABLE_XFA_GIF +-bool ProgressiveDecoder::GifReadMoreData(GifModule* pGifModule, +- FXCODEC_STATUS& err_status) { +- if (!ReadMoreData(pGifModule, m_pGifContext.get(), m_InvalidateGifBuffer, +- err_status)) { +- return false; +- } +- m_InvalidateGifBuffer = false; +- return true; ++bool ProgressiveDecoder::GifReadMoreData(FXCODEC_STATUS* err_status) { ++ return ReadMoreData(GifProgressiveDecoder::GetInstance(), m_pGifContext.get(), ++ err_status); + } + + bool ProgressiveDecoder::GifDetectImageTypeInBuffer() { +- GifModule* pGifModule = m_pCodecMgr->GetGifModule(); +- if (!pGifModule) { +- m_status = FXCODEC_STATUS_ERR_MEMORY; +- return false; +- } +- m_pGifContext = pGifModule->Start(this); +- pGifModule->Input(m_pGifContext.get(), m_pCodecMemory, nullptr); ++ m_pGifContext = GifDecoder::StartDecode(this); ++ GifDecoder::Input(m_pGifContext.get(), m_pCodecMemory); + m_SrcComponents = 1; +- CFX_GifDecodeStatus readResult = +- pGifModule->ReadHeader(m_pGifContext.get(), &m_SrcWidth, &m_SrcHeight, ++ GifDecoder::Status readResult = ++ GifDecoder::ReadHeader(m_pGifContext.get(), &m_SrcWidth, &m_SrcHeight, + &m_GifPltNumber, &m_pGifPalette, &m_GifBgIndex); +- while (readResult == CFX_GifDecodeStatus::Unfinished) { +- FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT; +- if (!GifReadMoreData(pGifModule, error_status)) { ++ while (readResult == GifDecoder::Status::kUnfinished) { ++ FXCODEC_STATUS error_status = FXCODEC_STATUS::kError; ++ if (!GifReadMoreData(&error_status)) { + m_pGifContext = nullptr; + m_status = error_status; + return false; + } + readResult = +- pGifModule->ReadHeader(m_pGifContext.get(), &m_SrcWidth, &m_SrcHeight, ++ GifDecoder::ReadHeader(m_pGifContext.get(), &m_SrcWidth, &m_SrcHeight, + &m_GifPltNumber, &m_pGifPalette, &m_GifBgIndex); + } +- if (readResult == CFX_GifDecodeStatus::Success) { ++ if (readResult == GifDecoder::Status::kSuccess) { + m_SrcBPC = 8; + m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight); + return true; + } + m_pGifContext = nullptr; +- m_status = FXCODEC_STATUS_ERR_FORMAT; ++ m_status = FXCODEC_STATUS::kError; + return false; + } + +-FXCODEC_STATUS ProgressiveDecoder::GifStartDecode( +- const RetainPtr& pDIBitmap) { +- GifModule* pGifModule = m_pCodecMgr->GetGifModule(); +- if (!pGifModule) { +- m_pDeviceBitmap = nullptr; +- m_pFile = nullptr; +- m_status = FXCODEC_STATUS_ERR_MEMORY; +- return m_status; +- } ++FXCODEC_STATUS ProgressiveDecoder::GifStartDecode() { + m_SrcFormat = FXCodec_8bppRgb; + GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat); + int scanline_size = FxAlignToBoundary<4>(m_SrcWidth); +- m_pDecodeBuf.reset(FX_Alloc(uint8_t, scanline_size)); +- m_WeightHorz.Calc(m_sizeX, m_clipBox.Width()); +- m_WeightVert.Calc(m_sizeY, m_clipBox.Height()); ++ m_DecodeBuf.resize(scanline_size); ++ FXDIB_ResampleOptions options; ++ options.bInterpolateBilinear = true; ++ m_WeightHorz.CalculateWeights(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0, ++ m_clipBox.Width(), options); ++ m_WeightVert.CalculateWeights(m_sizeY, m_clipBox.Height()); + m_FrameCur = 0; +- m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE; ++ m_status = FXCODEC_STATUS::kDecodeToBeContinued; + return m_status; + } + + FXCODEC_STATUS ProgressiveDecoder::GifContinueDecode() { +- GifModule* pGifModule = m_pCodecMgr->GetGifModule(); +- if (!pGifModule) { +- m_pDeviceBitmap = nullptr; +- m_pFile = nullptr; +- m_status = FXCODEC_STATUS_ERR_MEMORY; +- return m_status; +- } +- +- CFX_GifDecodeStatus readRes = +- pGifModule->LoadFrame(m_pGifContext.get(), m_FrameCur); +- while (readRes == CFX_GifDecodeStatus::Unfinished) { +- FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH; +- if (!GifReadMoreData(pGifModule, error_status)) { ++ GifDecoder::Status readRes = ++ GifDecoder::LoadFrame(m_pGifContext.get(), m_FrameCur); ++ while (readRes == GifDecoder::Status::kUnfinished) { ++ FXCODEC_STATUS error_status = FXCODEC_STATUS::kDecodeFinished; ++ if (!GifReadMoreData(&error_status)) { + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; + m_status = error_status; + return m_status; + } +- readRes = pGifModule->LoadFrame(m_pGifContext.get(), m_FrameCur); ++ readRes = GifDecoder::LoadFrame(m_pGifContext.get(), m_FrameCur); + } + +- if (readRes == CFX_GifDecodeStatus::Success) { ++ if (readRes == GifDecoder::Status::kSuccess) { + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; +- m_status = FXCODEC_STATUS_DECODE_FINISH; ++ m_status = FXCODEC_STATUS::kDecodeFinished; + return m_status; + } + + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; +- m_status = FXCODEC_STATUS_ERROR; ++ m_status = FXCODEC_STATUS::kError; + return m_status; + } + +@@ -925,79 +838,78 @@ void ProgressiveDecoder::GifDoubleLineResampleVert( + double scale_y, + int dest_row) { + int dest_Bpp = pDeviceBitmap->GetBPP() >> 3; +- uint32_t dest_ScanOffet = m_startX * dest_Bpp; ++ uint32_t dest_ScanOffset = m_startX * dest_Bpp; + int dest_top = m_startY; + pdfium::base::CheckedNumeric scale_y2 = scale_y; + scale_y2 *= 2; +- pdfium::base::CheckedNumeric check_dest_row_1 = dest_row; ++ FX_SAFE_INT32 check_dest_row_1 = dest_row; + check_dest_row_1 -= scale_y2.ValueOrDie(); + int dest_row_1 = check_dest_row_1.ValueOrDie(); + dest_row_1 = std::max(dest_row_1, dest_top); + for (; dest_row_1 < dest_row; dest_row_1++) { +- uint8_t* scan_des = +- pDeviceBitmap->GetWritableScanline(dest_row_1) + dest_ScanOffet; ++ uint8_t* scan_des = pDeviceBitmap->GetWritableScanline(dest_row_1) ++ .subspan(dest_ScanOffset) ++ .data(); + PixelWeight* pWeight = m_WeightVert.GetPixelWeight(dest_row_1 - dest_top); + const uint8_t* scan_src1 = +- pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top) + +- dest_ScanOffet; ++ pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top) ++ .subspan(dest_ScanOffset) ++ .data(); + const uint8_t* scan_src2 = +- pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top) + +- dest_ScanOffet; +- for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { +- switch (pDeviceBitmap->GetFormat()) { +- case FXDIB_Invalid: +- case FXDIB_1bppMask: +- case FXDIB_1bppRgb: ++ pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top) ++ .subspan(dest_ScanOffset) ++ .data(); ++ switch (pDeviceBitmap->GetFormat()) { ++ case FXDIB_Format::kInvalid: ++ case FXDIB_Format::k1bppMask: ++ case FXDIB_Format::k1bppRgb: ++ return; ++ case FXDIB_Format::k8bppMask: ++ case FXDIB_Format::k8bppRgb: ++ if (pDeviceBitmap->HasPalette()) + return; +- case FXDIB_8bppMask: +- case FXDIB_8bppRgb: { +- if (pDeviceBitmap->GetPalette()) { +- return; +- } +- int dest_g = 0; +- dest_g += pWeight->m_Weights[0] * (*scan_src1++); +- dest_g += pWeight->m_Weights[1] * (*scan_src2++); +- *scan_des++ = (uint8_t)(dest_g >> 16); +- } break; +- case FXDIB_Rgb: +- case FXDIB_Rgb32: { +- uint32_t dest_b = 0; ++ for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { + uint32_t dest_g = 0; +- uint32_t dest_r = 0; +- dest_b += pWeight->m_Weights[0] * (*scan_src1++); + dest_g += pWeight->m_Weights[0] * (*scan_src1++); +- dest_r += pWeight->m_Weights[0] * (*scan_src1++); ++ dest_g += pWeight->m_Weights[1] * (*scan_src2++); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); ++ } ++ break; ++ case FXDIB_Format::kRgb: ++ case FXDIB_Format::kRgb32: ++ for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { ++ uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++); ++ uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++); ++ uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++); + scan_src1 += dest_Bpp - 3; + dest_b += pWeight->m_Weights[1] * (*scan_src2++); + dest_g += pWeight->m_Weights[1] * (*scan_src2++); + dest_r += pWeight->m_Weights[1] * (*scan_src2++); + scan_src2 += dest_Bpp - 3; +- *scan_des++ = (uint8_t)((dest_b) >> 16); +- *scan_des++ = (uint8_t)((dest_g) >> 16); +- *scan_des++ = (uint8_t)((dest_r) >> 16); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_b); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_r); + scan_des += dest_Bpp - 3; +- } break; +- case FXDIB_Argb: { +- uint32_t dest_a = 0; +- uint32_t dest_b = 0; +- uint32_t dest_g = 0; +- uint32_t dest_r = 0; +- dest_b += pWeight->m_Weights[0] * (*scan_src1++); +- dest_g += pWeight->m_Weights[0] * (*scan_src1++); +- dest_r += pWeight->m_Weights[0] * (*scan_src1++); +- dest_a += pWeight->m_Weights[0] * (*scan_src1++); ++ } ++ break; ++ case FXDIB_Format::kArgb: ++ for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { ++ uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++); ++ uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++); ++ uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++); ++ uint32_t dest_a = pWeight->m_Weights[0] * (*scan_src1++); + dest_b += pWeight->m_Weights[1] * (*scan_src2++); + dest_g += pWeight->m_Weights[1] * (*scan_src2++); + dest_r += pWeight->m_Weights[1] * (*scan_src2++); + dest_a += pWeight->m_Weights[1] * (*scan_src2++); +- *scan_des++ = (uint8_t)((dest_b) >> 16); +- *scan_des++ = (uint8_t)((dest_g) >> 16); +- *scan_des++ = (uint8_t)((dest_r) >> 16); +- *scan_des++ = (uint8_t)((dest_a) >> 16); +- } break; +- default: +- return; +- } ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_b); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_r); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_a); ++ } ++ break; ++ default: ++ return; + } + } + int dest_bottom = dest_top + m_sizeY - 1; +@@ -1008,41 +920,41 @@ void ProgressiveDecoder::GifDoubleLineResampleVert( + } + #endif // PDF_ENABLE_XFA_GIF + +-bool ProgressiveDecoder::JpegReadMoreData(JpegModule* pJpegModule, +- FXCODEC_STATUS& err_status) { +- return ReadMoreData(pJpegModule, m_pJpegContext.get(), false, err_status); ++bool ProgressiveDecoder::JpegReadMoreData(FXCODEC_STATUS* err_status) { ++ return ReadMoreData(JpegProgressiveDecoder::GetInstance(), ++ m_pJpegContext.get(), err_status); + } + + bool ProgressiveDecoder::JpegDetectImageTypeInBuffer( + CFX_DIBAttribute* pAttribute) { +- JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule(); +- m_pJpegContext = pJpegModule->Start(); ++ m_pJpegContext = JpegProgressiveDecoder::Start(); + if (!m_pJpegContext) { +- m_status = FXCODEC_STATUS_ERR_MEMORY; ++ m_status = FXCODEC_STATUS::kError; + return false; + } +- pJpegModule->Input(m_pJpegContext.get(), m_pCodecMemory, nullptr); ++ JpegProgressiveDecoder::GetInstance()->Input(m_pJpegContext.get(), ++ m_pCodecMemory); + + // Setting jump marker before calling ReadHeader, since a longjmp to + // the marker indicates a fatal error. +- if (setjmp(pJpegModule->GetJumpMark(m_pJpegContext.get())) == -1) { ++ if (setjmp(JpegProgressiveDecoder::GetJumpMark(m_pJpegContext.get())) == -1) { + m_pJpegContext.reset(); +- m_status = FXCODEC_STATUS_ERR_FORMAT; ++ m_status = FXCODEC_STATUS::kError; + return false; + } + +- int32_t readResult = +- pJpegModule->ReadHeader(m_pJpegContext.get(), &m_SrcWidth, &m_SrcHeight, +- &m_SrcComponents, pAttribute); ++ int32_t readResult = JpegProgressiveDecoder::ReadHeader( ++ m_pJpegContext.get(), &m_SrcWidth, &m_SrcHeight, &m_SrcComponents, ++ pAttribute); + while (readResult == 2) { +- FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT; +- if (!JpegReadMoreData(pJpegModule, error_status)) { ++ FXCODEC_STATUS error_status = FXCODEC_STATUS::kError; ++ if (!JpegReadMoreData(&error_status)) { + m_status = error_status; + return false; + } +- readResult = +- pJpegModule->ReadHeader(m_pJpegContext.get(), &m_SrcWidth, &m_SrcHeight, +- &m_SrcComponents, pAttribute); ++ readResult = JpegProgressiveDecoder::ReadHeader( ++ m_pJpegContext.get(), &m_SrcWidth, &m_SrcHeight, &m_SrcComponents, ++ pAttribute); + } + if (!readResult) { + m_SrcBPC = 8; +@@ -1050,41 +962,42 @@ bool ProgressiveDecoder::JpegDetectImageTypeInBuffer( + return true; + } + m_pJpegContext.reset(); +- m_status = FXCODEC_STATUS_ERR_FORMAT; ++ m_status = FXCODEC_STATUS::kError; + return false; + } + +-FXCODEC_STATUS ProgressiveDecoder::JpegStartDecode( +- const RetainPtr& pDIBitmap) { +- JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule(); +- int down_scale = 1; +- GetDownScale(down_scale); ++FXCODEC_STATUS ProgressiveDecoder::JpegStartDecode(FXDIB_Format format) { ++ int down_scale = GetDownScale(); + // Setting jump marker before calling StartScanLine, since a longjmp to + // the marker indicates a fatal error. +- if (setjmp(pJpegModule->GetJumpMark(m_pJpegContext.get())) == -1) { ++ if (setjmp(JpegProgressiveDecoder::GetJumpMark(m_pJpegContext.get())) == -1) { + m_pJpegContext.reset(); +- m_status = FXCODEC_STATUS_ERROR; +- return FXCODEC_STATUS_ERROR; ++ m_status = FXCODEC_STATUS::kError; ++ return FXCODEC_STATUS::kError; + } + + bool startStatus = +- pJpegModule->StartScanline(m_pJpegContext.get(), down_scale); ++ JpegProgressiveDecoder::StartScanline(m_pJpegContext.get(), down_scale); + while (!startStatus) { +- FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR; +- if (!JpegReadMoreData(pJpegModule, error_status)) { ++ FXCODEC_STATUS error_status = FXCODEC_STATUS::kError; ++ if (!JpegReadMoreData(&error_status)) { + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; + m_status = error_status; + return m_status; + } + +- startStatus = pJpegModule->StartScanline(m_pJpegContext.get(), down_scale); ++ startStatus = ++ JpegProgressiveDecoder::StartScanline(m_pJpegContext.get(), down_scale); + } + int scanline_size = (m_SrcWidth + down_scale - 1) / down_scale; + scanline_size = FxAlignToBoundary<4>(scanline_size * m_SrcComponents); +- m_pDecodeBuf.reset(FX_Alloc(uint8_t, scanline_size)); +- m_WeightHorz.Calc(m_sizeX, m_clipBox.Width()); +- m_WeightVert.Calc(m_sizeY, m_clipBox.Height()); ++ m_DecodeBuf.resize(scanline_size); ++ FXDIB_ResampleOptions options; ++ options.bInterpolateBilinear = true; ++ m_WeightHorz.CalculateWeights(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0, ++ m_clipBox.Width(), options); ++ m_WeightVert.CalculateWeights(m_sizeY, m_clipBox.Height()); + switch (m_SrcComponents) { + case 1: + m_SrcFormat = FXCodec_8bppGray; +@@ -1096,46 +1009,46 @@ FXCODEC_STATUS ProgressiveDecoder::JpegStartDecode( + m_SrcFormat = FXCodec_Cmyk; + break; + } +- GetTransMethod(pDIBitmap->GetFormat(), m_SrcFormat); +- m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE; ++ GetTransMethod(format, m_SrcFormat); ++ m_status = FXCODEC_STATUS::kDecodeToBeContinued; + return m_status; + } + + FXCODEC_STATUS ProgressiveDecoder::JpegContinueDecode() { +- JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule(); ++ // JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule(); + // Setting jump marker before calling ReadScanLine, since a longjmp to + // the marker indicates a fatal error. +- if (setjmp(pJpegModule->GetJumpMark(m_pJpegContext.get())) == -1) { ++ if (setjmp(JpegProgressiveDecoder::GetJumpMark(m_pJpegContext.get())) == -1) { + m_pJpegContext.reset(); +- m_status = FXCODEC_STATUS_ERROR; +- return FXCODEC_STATUS_ERROR; ++ m_status = FXCODEC_STATUS::kError; ++ return FXCODEC_STATUS::kError; + } + + while (true) { +- bool readRes = +- pJpegModule->ReadScanline(m_pJpegContext.get(), m_pDecodeBuf.get()); ++ bool readRes = JpegProgressiveDecoder::ReadScanline(m_pJpegContext.get(), ++ m_DecodeBuf.data()); + while (!readRes) { +- FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH; +- if (!JpegReadMoreData(pJpegModule, error_status)) { ++ FXCODEC_STATUS error_status = FXCODEC_STATUS::kDecodeFinished; ++ if (!JpegReadMoreData(&error_status)) { + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; + m_status = error_status; + return m_status; + } +- readRes = +- pJpegModule->ReadScanline(m_pJpegContext.get(), m_pDecodeBuf.get()); ++ readRes = JpegProgressiveDecoder::ReadScanline(m_pJpegContext.get(), ++ m_DecodeBuf.data()); + } + if (m_SrcFormat == FXCodec_Rgb) { + int src_Bpp = (m_SrcFormat & 0xff) >> 3; +- RGB2BGR(m_pDecodeBuf.get() + m_clipBox.left * src_Bpp, m_clipBox.Width()); ++ RGB2BGR(m_DecodeBuf.data() + m_clipBox.left * src_Bpp, m_clipBox.Width()); + } + if (m_SrcRow >= m_clipBox.bottom) { + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; +- m_status = FXCODEC_STATUS_DECODE_FINISH; ++ m_status = FXCODEC_STATUS::kDecodeFinished; + return m_status; + } +- Resample(m_pDeviceBitmap, m_SrcRow, m_pDecodeBuf.get(), m_SrcFormat); ++ Resample(m_pDeviceBitmap, m_SrcRow, m_DecodeBuf.data(), m_SrcFormat); + m_SrcRow++; + } + } +@@ -1144,172 +1057,150 @@ FXCODEC_STATUS ProgressiveDecoder::JpegContinueDecode() { + void ProgressiveDecoder::PngOneOneMapResampleHorz( + const RetainPtr& pDeviceBitmap, + int32_t dest_line, +- uint8_t* src_scan, ++ pdfium::span src_span, + FXCodec_Format src_format) { +- uint8_t* dest_scan = pDeviceBitmap->GetWritableScanline(dest_line); + int32_t src_Bpp = (m_SrcFormat & 0xff) >> 3; + int32_t dest_Bpp = pDeviceBitmap->GetBPP() >> 3; + int32_t src_left = m_clipBox.left; + int32_t dest_left = m_startX; +- src_scan += src_left * src_Bpp; +- dest_scan += dest_left * dest_Bpp; +- for (int32_t dest_col = 0; dest_col < m_sizeX; dest_col++) { +- PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(dest_col); +- switch (pDeviceBitmap->GetFormat()) { +- case FXDIB_1bppMask: +- case FXDIB_1bppRgb: +- NOTREACHED(); ++ uint8_t* src_scan = src_span.subspan(src_left * src_Bpp).data(); ++ uint8_t* dest_scan = pDeviceBitmap->GetWritableScanline(dest_line) ++ .subspan(dest_left * dest_Bpp) ++ .data(); ++ switch (pDeviceBitmap->GetFormat()) { ++ case FXDIB_Format::k1bppMask: ++ case FXDIB_Format::k1bppRgb: ++ NOTREACHED_NORETURN(); ++ case FXDIB_Format::k8bppMask: ++ case FXDIB_Format::k8bppRgb: ++ if (pDeviceBitmap->HasPalette()) + return; +- case FXDIB_8bppMask: +- case FXDIB_8bppRgb: { +- if (pDeviceBitmap->GetPalette()) { +- return; +- } +- uint32_t dest_g = 0; +- dest_g += ++ for (int32_t dest_col = 0; dest_col < m_sizeX; dest_col++) { ++ PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(dest_col); ++ uint32_t dest_g = + pPixelWeights->m_Weights[0] * src_scan[pPixelWeights->m_SrcStart]; + dest_g += + pPixelWeights->m_Weights[1] * src_scan[pPixelWeights->m_SrcEnd]; +- *dest_scan++ = (uint8_t)(dest_g >> 16); +- } break; +- case FXDIB_Rgb: +- case FXDIB_Rgb32: { +- uint32_t dest_b = 0; +- uint32_t dest_g = 0; +- uint32_t dest_r = 0; +- const uint8_t* p = src_scan; +- p = src_scan + pPixelWeights->m_SrcStart * src_Bpp; +- dest_b += pPixelWeights->m_Weights[0] * (*p++); +- dest_g += pPixelWeights->m_Weights[0] * (*p++); +- dest_r += pPixelWeights->m_Weights[0] * (*p); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); ++ } ++ break; ++ case FXDIB_Format::kRgb: ++ case FXDIB_Format::kRgb32: ++ for (int32_t dest_col = 0; dest_col < m_sizeX; dest_col++) { ++ PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(dest_col); ++ const uint8_t* p = src_scan + pPixelWeights->m_SrcStart * src_Bpp; ++ uint32_t dest_b = pPixelWeights->m_Weights[0] * (*p++); ++ uint32_t dest_g = pPixelWeights->m_Weights[0] * (*p++); ++ uint32_t dest_r = pPixelWeights->m_Weights[0] * (*p); + p = src_scan + pPixelWeights->m_SrcEnd * src_Bpp; + dest_b += pPixelWeights->m_Weights[1] * (*p++); + dest_g += pPixelWeights->m_Weights[1] * (*p++); + dest_r += pPixelWeights->m_Weights[1] * (*p); +- *dest_scan++ = (uint8_t)((dest_b) >> 16); +- *dest_scan++ = (uint8_t)((dest_g) >> 16); +- *dest_scan++ = (uint8_t)((dest_r) >> 16); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r); + dest_scan += dest_Bpp - 3; +- } break; +- case FXDIB_Argb: { +- uint32_t dest_a = 0; +- uint32_t dest_b = 0; +- uint32_t dest_g = 0; +- uint32_t dest_r = 0; +- const uint8_t* p = src_scan; +- p = src_scan + pPixelWeights->m_SrcStart * src_Bpp; +- dest_b += pPixelWeights->m_Weights[0] * (*p++); +- dest_g += pPixelWeights->m_Weights[0] * (*p++); +- dest_r += pPixelWeights->m_Weights[0] * (*p++); +- dest_a += pPixelWeights->m_Weights[0] * (*p); ++ } ++ break; ++ case FXDIB_Format::kArgb: ++ for (int32_t dest_col = 0; dest_col < m_sizeX; dest_col++) { ++ PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(dest_col); ++ const uint8_t* p = src_scan + pPixelWeights->m_SrcStart * src_Bpp; ++ uint32_t dest_b = pPixelWeights->m_Weights[0] * (*p++); ++ uint32_t dest_g = pPixelWeights->m_Weights[0] * (*p++); ++ uint32_t dest_r = pPixelWeights->m_Weights[0] * (*p++); ++ uint32_t dest_a = pPixelWeights->m_Weights[0] * (*p); + p = src_scan + pPixelWeights->m_SrcEnd * src_Bpp; + dest_b += pPixelWeights->m_Weights[1] * (*p++); + dest_g += pPixelWeights->m_Weights[1] * (*p++); + dest_r += pPixelWeights->m_Weights[1] * (*p++); + dest_a += pPixelWeights->m_Weights[1] * (*p); +- *dest_scan++ = (uint8_t)((dest_b) >> 16); +- *dest_scan++ = (uint8_t)((dest_g) >> 16); +- *dest_scan++ = (uint8_t)((dest_r) >> 16); +- *dest_scan++ = (uint8_t)((dest_a) >> 16); +- } break; +- default: +- return; +- } ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_a); ++ } ++ break; ++ default: ++ return; + } + } + + bool ProgressiveDecoder::PngDetectImageTypeInBuffer( + CFX_DIBAttribute* pAttribute) { +- PngModule* pPngModule = m_pCodecMgr->GetPngModule(); +- if (!pPngModule) { +- m_status = FXCODEC_STATUS_ERR_MEMORY; +- return false; +- } +- m_pPngContext = pPngModule->Start(this); ++ m_pPngContext = PngDecoder::StartDecode(this); + if (!m_pPngContext) { +- m_status = FXCODEC_STATUS_ERR_MEMORY; ++ m_status = FXCODEC_STATUS::kError; + return false; + } +- while (pPngModule->Input(m_pPngContext.get(), m_pCodecMemory, pAttribute)) { ++ while (PngDecoder::ContinueDecode(m_pPngContext.get(), m_pCodecMemory, ++ pAttribute)) { + uint32_t remain_size = static_cast(m_pFile->GetSize()) - m_offSet; + uint32_t input_size = std::min(remain_size, kBlockSize); + if (input_size == 0) { + m_pPngContext.reset(); +- m_status = FXCODEC_STATUS_ERR_FORMAT; ++ m_status = FXCODEC_STATUS::kError; + return false; + } + if (m_pCodecMemory && input_size > m_pCodecMemory->GetSize()) + m_pCodecMemory = pdfium::MakeRetain(input_size); + +- if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBuffer(), m_offSet, +- input_size)) { +- m_status = FXCODEC_STATUS_ERR_READ; ++ if (!m_pFile->ReadBlockAtOffset( ++ m_pCodecMemory->GetBufferSpan().first(input_size), m_offSet)) { ++ m_status = FXCODEC_STATUS::kError; + return false; + } + m_offSet += input_size; + } + m_pPngContext.reset(); + if (m_SrcPassNumber == 0) { +- m_status = FXCODEC_STATUS_ERR_FORMAT; ++ m_status = FXCODEC_STATUS::kError; + return false; + } + return true; + } + +-FXCODEC_STATUS ProgressiveDecoder::PngStartDecode( +- const RetainPtr& pDIBitmap) { +- PngModule* pPngModule = m_pCodecMgr->GetPngModule(); +- if (!pPngModule) { +- m_pDeviceBitmap = nullptr; +- m_pFile = nullptr; +- m_status = FXCODEC_STATUS_ERR_MEMORY; +- return m_status; +- } +- m_pPngContext = pPngModule->Start(this); ++FXCODEC_STATUS ProgressiveDecoder::PngStartDecode() { ++ m_pPngContext = PngDecoder::StartDecode(this); + if (!m_pPngContext) { + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; +- m_status = FXCODEC_STATUS_ERR_MEMORY; ++ m_status = FXCODEC_STATUS::kError; + return m_status; + } + m_offSet = 0; + switch (m_pDeviceBitmap->GetFormat()) { +- case FXDIB_8bppMask: +- case FXDIB_8bppRgb: ++ case FXDIB_Format::k8bppMask: ++ case FXDIB_Format::k8bppRgb: + m_SrcComponents = 1; + m_SrcFormat = FXCodec_8bppGray; + break; +- case FXDIB_Rgb: ++ case FXDIB_Format::kRgb: + m_SrcComponents = 3; + m_SrcFormat = FXCodec_Rgb; + break; +- case FXDIB_Rgb32: +- case FXDIB_Argb: ++ case FXDIB_Format::kRgb32: ++ case FXDIB_Format::kArgb: + m_SrcComponents = 4; + m_SrcFormat = FXCodec_Argb; + break; + default: { + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; +- m_status = FXCODEC_STATUS_ERR_PARAMS; ++ m_status = FXCODEC_STATUS::kError; + return m_status; + } + } + GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat); + int scanline_size = FxAlignToBoundary<4>(m_SrcWidth * m_SrcComponents); +- m_pDecodeBuf.reset(FX_Alloc(uint8_t, scanline_size)); +- m_WeightHorzOO.Calc(m_sizeX, m_clipBox.Width()); +- m_WeightVert.Calc(m_sizeY, m_clipBox.Height()); +- m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE; ++ m_DecodeBuf.resize(scanline_size); ++ m_WeightHorzOO.CalculateWeights(m_sizeX, m_clipBox.Width()); ++ m_WeightVert.CalculateWeights(m_sizeY, m_clipBox.Height()); ++ m_status = FXCODEC_STATUS::kDecodeToBeContinued; + return m_status; + } + + FXCODEC_STATUS ProgressiveDecoder::PngContinueDecode() { +- PngModule* pPngModule = m_pCodecMgr->GetPngModule(); +- if (!pPngModule) { +- m_status = FXCODEC_STATUS_ERR_MEMORY; +- return m_status; +- } + while (true) { + uint32_t remain_size = (uint32_t)m_pFile->GetSize() - m_offSet; + uint32_t input_size = std::min(remain_size, kBlockSize); +@@ -1317,26 +1208,27 @@ FXCODEC_STATUS ProgressiveDecoder::PngContinueDecode() { + m_pPngContext.reset(); + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; +- m_status = FXCODEC_STATUS_DECODE_FINISH; ++ m_status = FXCODEC_STATUS::kDecodeFinished; + return m_status; + } + if (m_pCodecMemory && input_size > m_pCodecMemory->GetSize()) + m_pCodecMemory = pdfium::MakeRetain(input_size); + +- bool bResult = m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBuffer(), +- m_offSet, input_size); ++ bool bResult = m_pFile->ReadBlockAtOffset( ++ m_pCodecMemory->GetBufferSpan().first(input_size), m_offSet); + if (!bResult) { + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; +- m_status = FXCODEC_STATUS_ERR_READ; ++ m_status = FXCODEC_STATUS::kError; + return m_status; + } + m_offSet += input_size; +- bResult = pPngModule->Input(m_pPngContext.get(), m_pCodecMemory, nullptr); ++ bResult = PngDecoder::ContinueDecode(m_pPngContext.get(), m_pCodecMemory, ++ nullptr); + if (!bResult) { + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; +- m_status = FXCODEC_STATUS_ERROR; ++ m_status = FXCODEC_STATUS::kError; + return m_status; + } + } +@@ -1346,36 +1238,26 @@ FXCODEC_STATUS ProgressiveDecoder::PngContinueDecode() { + #ifdef PDF_ENABLE_XFA_TIFF + bool ProgressiveDecoder::TiffDetectImageTypeFromFile( + CFX_DIBAttribute* pAttribute) { +- TiffModule* pTiffModule = m_pCodecMgr->GetTiffModule(); +- if (!pTiffModule) { +- m_status = FXCODEC_STATUS_ERR_FORMAT; +- return false; +- } +- m_pTiffContext = pTiffModule->CreateDecoder(m_pFile); ++ m_pTiffContext = TiffDecoder::CreateDecoder(m_pFile); + if (!m_pTiffContext) { +- m_status = FXCODEC_STATUS_ERR_FORMAT; ++ m_status = FXCODEC_STATUS::kError; + return false; + } + int32_t dummy_bpc; +- bool ret = pTiffModule->LoadFrameInfo(m_pTiffContext.get(), 0, &m_SrcWidth, ++ bool ret = TiffDecoder::LoadFrameInfo(m_pTiffContext.get(), 0, &m_SrcWidth, + &m_SrcHeight, &m_SrcComponents, + &dummy_bpc, pAttribute); + m_SrcComponents = 4; + m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight); + if (!ret) { + m_pTiffContext.reset(); +- m_status = FXCODEC_STATUS_ERR_FORMAT; ++ m_status = FXCODEC_STATUS::kError; + return false; + } + return true; + } + + FXCODEC_STATUS ProgressiveDecoder::TiffContinueDecode() { +- TiffModule* pTiffModule = m_pCodecMgr->GetTiffModule(); +- if (!pTiffModule) { +- m_status = FXCODEC_STATUS_ERR_MEMORY; +- return m_status; +- } + bool ret = false; + if (m_pDeviceBitmap->GetBPP() == 32 && + m_pDeviceBitmap->GetWidth() == m_SrcWidth && m_SrcWidth == m_sizeX && +@@ -1383,77 +1265,77 @@ FXCODEC_STATUS ProgressiveDecoder::TiffContinueDecode() { + m_startX == 0 && m_startY == 0 && m_clipBox.left == 0 && + m_clipBox.top == 0 && m_clipBox.right == m_SrcWidth && + m_clipBox.bottom == m_SrcHeight) { +- ret = pTiffModule->Decode(m_pTiffContext.get(), m_pDeviceBitmap); ++ ret = TiffDecoder::Decode(m_pTiffContext.get(), m_pDeviceBitmap); + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; + if (!ret) { +- m_status = FXCODEC_STATUS_ERROR; ++ m_status = FXCODEC_STATUS::kError; + return m_status; + } +- m_status = FXCODEC_STATUS_DECODE_FINISH; ++ m_status = FXCODEC_STATUS::kDecodeFinished; + return m_status; + } + + auto pDIBitmap = pdfium::MakeRetain(); +- pDIBitmap->Create(m_SrcWidth, m_SrcHeight, FXDIB_Argb); +- if (!pDIBitmap->GetBuffer()) { ++ pDIBitmap->Create(m_SrcWidth, m_SrcHeight, FXDIB_Format::kArgb); ++ if (pDIBitmap->GetBuffer().empty()) { + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; +- m_status = FXCODEC_STATUS_ERR_MEMORY; ++ m_status = FXCODEC_STATUS::kError; + return m_status; + } +- ret = pTiffModule->Decode(m_pTiffContext.get(), pDIBitmap); ++ ret = TiffDecoder::Decode(m_pTiffContext.get(), pDIBitmap); + if (!ret) { + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; +- m_status = FXCODEC_STATUS_ERROR; ++ m_status = FXCODEC_STATUS::kError; + return m_status; + } + RetainPtr pClipBitmap = + (m_clipBox.left == 0 && m_clipBox.top == 0 && + m_clipBox.right == m_SrcWidth && m_clipBox.bottom == m_SrcHeight) + ? pDIBitmap +- : pDIBitmap->Clone(&m_clipBox); ++ : pDIBitmap->ClipTo(m_clipBox); + if (!pClipBitmap) { + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; +- m_status = FXCODEC_STATUS_ERR_MEMORY; ++ m_status = FXCODEC_STATUS::kError; + return m_status; + } + RetainPtr pFormatBitmap; + switch (m_pDeviceBitmap->GetFormat()) { +- case FXDIB_8bppRgb: ++ case FXDIB_Format::k8bppRgb: + pFormatBitmap = pdfium::MakeRetain(); + pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(), +- FXDIB_8bppRgb); ++ FXDIB_Format::k8bppRgb); + break; +- case FXDIB_8bppMask: ++ case FXDIB_Format::k8bppMask: + pFormatBitmap = pdfium::MakeRetain(); + pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(), +- FXDIB_8bppMask); ++ FXDIB_Format::k8bppMask); + break; +- case FXDIB_Rgb: ++ case FXDIB_Format::kRgb: + pFormatBitmap = pdfium::MakeRetain(); + pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(), +- FXDIB_Rgb); ++ FXDIB_Format::kRgb); + break; +- case FXDIB_Rgb32: ++ case FXDIB_Format::kRgb32: + pFormatBitmap = pdfium::MakeRetain(); + pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(), +- FXDIB_Rgb32); ++ FXDIB_Format::kRgb32); + break; +- case FXDIB_Argb: ++ case FXDIB_Format::kArgb: + pFormatBitmap = pClipBitmap; + break; + default: + break; + } + switch (m_pDeviceBitmap->GetFormat()) { +- case FXDIB_8bppRgb: +- case FXDIB_8bppMask: { ++ case FXDIB_Format::k8bppRgb: ++ case FXDIB_Format::k8bppMask: { + for (int32_t row = 0; row < pClipBitmap->GetHeight(); row++) { +- const uint8_t* src_line = pClipBitmap->GetScanline(row); +- uint8_t* dest_line = pFormatBitmap->GetWritableScanline(row); ++ const uint8_t* src_line = pClipBitmap->GetScanline(row).data(); ++ uint8_t* dest_line = pFormatBitmap->GetWritableScanline(row).data(); + for (int32_t col = 0; col < pClipBitmap->GetWidth(); col++) { + uint8_t _a = 255 - src_line[3]; + uint8_t b = (src_line[0] * src_line[3] + 0xFF * _a) / 255; +@@ -1464,12 +1346,13 @@ FXCODEC_STATUS ProgressiveDecoder::TiffContinueDecode() { + } + } + } break; +- case FXDIB_Rgb: +- case FXDIB_Rgb32: { +- int32_t desBpp = (m_pDeviceBitmap->GetFormat() == FXDIB_Rgb) ? 3 : 4; ++ case FXDIB_Format::kRgb: ++ case FXDIB_Format::kRgb32: { ++ int32_t desBpp = ++ (m_pDeviceBitmap->GetFormat() == FXDIB_Format::kRgb) ? 3 : 4; + for (int32_t row = 0; row < pClipBitmap->GetHeight(); row++) { +- const uint8_t* src_line = pClipBitmap->GetScanline(row); +- uint8_t* dest_line = pFormatBitmap->GetWritableScanline(row); ++ const uint8_t* src_line = pClipBitmap->GetScanline(row).data(); ++ uint8_t* dest_line = pFormatBitmap->GetWritableScanline(row).data(); + for (int32_t col = 0; col < pClipBitmap->GetWidth(); col++) { + uint8_t _a = 255 - src_line[3]; + uint8_t b = (src_line[0] * src_line[3] + 0xFF * _a) / 255; +@@ -1489,7 +1372,7 @@ FXCODEC_STATUS ProgressiveDecoder::TiffContinueDecode() { + if (!pFormatBitmap) { + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; +- m_status = FXCODEC_STATUS_ERR_MEMORY; ++ m_status = FXCODEC_STATUS::kError; + return m_status; + } + +@@ -1501,14 +1384,14 @@ FXCODEC_STATUS ProgressiveDecoder::TiffContinueDecode() { + if (!pStrechBitmap) { + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; +- m_status = FXCODEC_STATUS_ERR_MEMORY; ++ m_status = FXCODEC_STATUS::kError; + return m_status; + } + m_pDeviceBitmap->TransferBitmap(m_startX, m_startY, m_sizeX, m_sizeY, + pStrechBitmap, 0, 0); + m_pDeviceBitmap = nullptr; + m_pFile = nullptr; +- m_status = FXCODEC_STATUS_DECODE_FINISH; ++ m_status = FXCODEC_STATUS::kDecodeFinished; + return m_status; + } + #endif // PDF_ENABLE_XFA_TIFF +@@ -1520,12 +1403,13 @@ bool ProgressiveDecoder::DetectImageType(FXCODEC_IMAGE_TYPE imageType, + return TiffDetectImageTypeFromFile(pAttribute); + #endif // PDF_ENABLE_XFA_TIFF + +- size_t size = std::min(m_pFile->GetSize(), kBlockSize); ++ size_t size = pdfium::base::checked_cast( ++ std::min(m_pFile->GetSize(), kBlockSize)); + m_pCodecMemory = pdfium::MakeRetain(size); + m_offSet = 0; +- if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBuffer(), m_offSet, +- size)) { +- m_status = FXCODEC_STATUS_ERR_READ; ++ if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBufferSpan().first(size), ++ m_offSet)) { ++ m_status = FXCODEC_STATUS::kError; + return false; + } + m_offSet += size; +@@ -1548,29 +1432,27 @@ bool ProgressiveDecoder::DetectImageType(FXCODEC_IMAGE_TYPE imageType, + return PngDetectImageTypeInBuffer(pAttribute); + #endif // PDF_ENABLE_XFA_PNG + +- m_status = FXCODEC_STATUS_ERR_FORMAT; ++ m_status = FXCODEC_STATUS::kError; + return false; + } + +-bool ProgressiveDecoder::ReadMoreData(ModuleIface* pModule, +- ModuleIface::Context* pContext, +- bool invalidate_buffer, +- FXCODEC_STATUS& err_status) { ++bool ProgressiveDecoder::ReadMoreData( ++ ProgressiveDecoderIface* pModule, ++ ProgressiveDecoderIface::Context* pContext, ++ FXCODEC_STATUS* err_status) { + // Check for EOF. + if (m_offSet >= static_cast(m_pFile->GetSize())) + return false; + + // Try to get whatever remains. +- uint32_t dwBytesToFetchFromFile = m_pFile->GetSize() - m_offSet; ++ uint32_t dwBytesToFetchFromFile = ++ pdfium::base::checked_cast(m_pFile->GetSize() - m_offSet); + + // Figure out if the codec stopped processing midway through the buffer. +- size_t dwUnconsumed = 0; +- if (!invalidate_buffer) { +- FX_SAFE_SIZE_T avail_input = pModule->GetAvailInput(pContext); +- if (!avail_input.IsValid()) +- return false; +- dwUnconsumed = avail_input.ValueOrDie(); +- } ++ size_t dwUnconsumed; ++ FX_SAFE_SIZE_T avail_input = pModule->GetAvailInput(pContext); ++ if (!avail_input.AssignIfValid(&dwUnconsumed)) ++ return false; + + if (dwUnconsumed == m_pCodecMemory->GetSize()) { + // Codec couldn't make any progress against the bytes in the buffer. +@@ -1580,48 +1462,53 @@ bool ProgressiveDecoder::ReadMoreData(ModuleIface* pModule, + std::min(dwBytesToFetchFromFile, kBlockSize); + size_t dwNewSize = m_pCodecMemory->GetSize() + dwBytesToFetchFromFile; + if (!m_pCodecMemory->TryResize(dwNewSize)) { +- err_status = FXCODEC_STATUS_ERR_MEMORY; ++ *err_status = FXCODEC_STATUS::kError; + return false; + } + } else { +- size_t dwConsumed = m_pCodecMemory->GetSize() - dwUnconsumed; +- m_pCodecMemory->Consume(dwConsumed); +- dwBytesToFetchFromFile = +- std::min(dwBytesToFetchFromFile, dwConsumed); ++ // TODO(crbug.com/pdfium/1904): Simplify the `CFX_CodecMemory` API so we ++ // don't need to do this awkward dance to free up exactly enough buffer ++ // space for the next read. ++ size_t dwConsumable = m_pCodecMemory->GetSize() - dwUnconsumed; ++ dwBytesToFetchFromFile = pdfium::base::checked_cast( ++ std::min(dwBytesToFetchFromFile, dwConsumable)); ++ m_pCodecMemory->Consume(dwBytesToFetchFromFile); ++ m_pCodecMemory->Seek(dwConsumable - dwBytesToFetchFromFile); ++ dwUnconsumed += m_pCodecMemory->GetPosition(); + } + + // Append new data past the bytes not yet processed by the codec. +- if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBuffer() + dwUnconsumed, +- m_offSet, dwBytesToFetchFromFile)) { +- err_status = FXCODEC_STATUS_ERR_READ; ++ if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBufferSpan().subspan( ++ dwUnconsumed, dwBytesToFetchFromFile), ++ m_offSet)) { ++ *err_status = FXCODEC_STATUS::kError; + return false; + } + m_offSet += dwBytesToFetchFromFile; +- return pModule->Input(pContext, m_pCodecMemory, nullptr); ++ return pModule->Input(pContext, m_pCodecMemory); + } + + FXCODEC_STATUS ProgressiveDecoder::LoadImageInfo( +- const RetainPtr& pFile, ++ RetainPtr pFile, + FXCODEC_IMAGE_TYPE imageType, + CFX_DIBAttribute* pAttribute, + bool bSkipImageTypeCheck) { +- ASSERT(pAttribute); ++ DCHECK(pAttribute); + + switch (m_status) { +- case FXCODEC_STATUS_FRAME_READY: +- case FXCODEC_STATUS_FRAME_TOBECONTINUE: +- case FXCODEC_STATUS_DECODE_READY: +- case FXCODEC_STATUS_DECODE_TOBECONTINUE: +- return FXCODEC_STATUS_ERROR; ++ case FXCODEC_STATUS::kFrameReady: ++ case FXCODEC_STATUS::kFrameToBeContinued: ++ case FXCODEC_STATUS::kDecodeReady: ++ case FXCODEC_STATUS::kDecodeToBeContinued: ++ return FXCODEC_STATUS::kError; + default: + break; + } +- if (!pFile) { +- m_status = FXCODEC_STATUS_ERR_PARAMS; +- m_pFile = nullptr; ++ m_pFile = std::move(pFile); ++ if (!m_pFile) { ++ m_status = FXCODEC_STATUS::kError; + return m_status; + } +- m_pFile = pFile; + m_offSet = 0; + m_SrcWidth = m_SrcHeight = 0; + m_SrcComponents = m_SrcBPC = 0; +@@ -1632,7 +1519,7 @@ FXCODEC_STATUS ProgressiveDecoder::LoadImageInfo( + if (imageType != FXCODEC_IMAGE_UNKNOWN && + DetectImageType(imageType, pAttribute)) { + m_imageType = imageType; +- m_status = FXCODEC_STATUS_FRAME_READY; ++ m_status = FXCODEC_STATUS::kFrameReady; + return m_status; + } + // If we got here then the image data does not match the requested decoder. +@@ -1644,17 +1531,17 @@ FXCODEC_STATUS ProgressiveDecoder::LoadImageInfo( + for (int type = FXCODEC_IMAGE_UNKNOWN + 1; type < FXCODEC_IMAGE_MAX; type++) { + if (DetectImageType(static_cast(type), pAttribute)) { + m_imageType = static_cast(type); +- m_status = FXCODEC_STATUS_FRAME_READY; ++ m_status = FXCODEC_STATUS::kFrameReady; + return m_status; + } + } +- m_status = FXCODEC_STATUS_ERR_FORMAT; ++ m_status = FXCODEC_STATUS::kError; + m_pFile = nullptr; + return m_status; + } + + void ProgressiveDecoder::SetClipBox(FX_RECT* clip) { +- if (m_status != FXCODEC_STATUS_FRAME_READY) ++ if (m_status != FXCODEC_STATUS::kFrameReady) + return; + + if (clip->IsEmpty()) { +@@ -1672,35 +1559,34 @@ void ProgressiveDecoder::SetClipBox(FX_RECT* clip) { + m_clipBox = *clip; + } + +-void ProgressiveDecoder::GetDownScale(int& down_scale) { +- down_scale = 1; ++int ProgressiveDecoder::GetDownScale() { ++ int down_scale = 1; + int ratio_w = m_clipBox.Width() / m_sizeX; + int ratio_h = m_clipBox.Height() / m_sizeY; +- int ratio = (ratio_w > ratio_h) ? ratio_h : ratio_w; +- if (ratio >= 8) { ++ int ratio = std::min(ratio_w, ratio_h); ++ if (ratio >= 8) + down_scale = 8; +- } else if (ratio >= 4) { ++ else if (ratio >= 4) + down_scale = 4; +- } else if (ratio >= 2) { ++ else if (ratio >= 2) + down_scale = 2; +- } ++ + m_clipBox.left /= down_scale; + m_clipBox.right /= down_scale; + m_clipBox.top /= down_scale; + m_clipBox.bottom /= down_scale; +- if (m_clipBox.right == m_clipBox.left) { ++ if (m_clipBox.right == m_clipBox.left) + m_clipBox.right = m_clipBox.left + 1; +- } +- if (m_clipBox.bottom == m_clipBox.top) { ++ if (m_clipBox.bottom == m_clipBox.top) + m_clipBox.bottom = m_clipBox.top + 1; +- } ++ return down_scale; + } + + void ProgressiveDecoder::GetTransMethod(FXDIB_Format dest_format, + FXCodec_Format src_format) { + switch (dest_format) { +- case FXDIB_1bppMask: +- case FXDIB_1bppRgb: { ++ case FXDIB_Format::k1bppMask: ++ case FXDIB_Format::k1bppRgb: { + switch (src_format) { + case FXCodec_1bppGray: + m_TransMethod = 0; +@@ -1709,8 +1595,8 @@ void ProgressiveDecoder::GetTransMethod(FXDIB_Format dest_format, + m_TransMethod = -1; + } + } break; +- case FXDIB_8bppMask: +- case FXDIB_8bppRgb: { ++ case FXDIB_Format::k8bppMask: ++ case FXDIB_Format::k8bppRgb: { + switch (src_format) { + case FXCodec_1bppGray: + m_TransMethod = 1; +@@ -1734,7 +1620,7 @@ void ProgressiveDecoder::GetTransMethod(FXDIB_Format dest_format, + m_TransMethod = -1; + } + } break; +- case FXDIB_Rgb: { ++ case FXDIB_Format::kRgb: { + switch (src_format) { + case FXCodec_1bppGray: + m_TransMethod = 6; +@@ -1758,8 +1644,8 @@ void ProgressiveDecoder::GetTransMethod(FXDIB_Format dest_format, + m_TransMethod = -1; + } + } break; +- case FXDIB_Rgb32: +- case FXDIB_Argb: { ++ case FXDIB_Format::kRgb32: ++ case FXDIB_Format::kArgb: { + switch (src_format) { + case FXCodec_1bppGray: + m_TransMethod = 6; +@@ -1769,7 +1655,7 @@ void ProgressiveDecoder::GetTransMethod(FXDIB_Format dest_format, + break; + case FXCodec_1bppRgb: + case FXCodec_8bppRgb: +- if (dest_format == FXDIB_Argb) { ++ if (dest_format == FXDIB_Format::kArgb) { + m_TransMethod = 12; + } else { + m_TransMethod = 8; +@@ -1794,15 +1680,15 @@ void ProgressiveDecoder::GetTransMethod(FXDIB_Format dest_format, + } + } + +-void ProgressiveDecoder::ReSampleScanline( ++void ProgressiveDecoder::ResampleScanline( + const RetainPtr& pDeviceBitmap, + int dest_line, +- uint8_t* src_scan, ++ pdfium::span src_span, + FXCodec_Format src_format) { ++ uint8_t* src_scan = src_span.data(); + int src_left = m_clipBox.left; + int dest_left = m_startX; +- uint8_t* dest_scan = +- pDeviceBitmap->GetBuffer() + dest_line * pDeviceBitmap->GetPitch(); ++ uint8_t* dest_scan = pDeviceBitmap->GetWritableScanline(dest_line).data(); + int src_bytes_per_pixel = (src_format & 0xff) / 8; + int dest_bytes_per_pixel = pDeviceBitmap->GetBPP() / 8; + src_scan += src_left * src_bytes_per_pixel; +@@ -1820,27 +1706,29 @@ void ProgressiveDecoder::ReSampleScanline( + uint32_t dest_g = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { +- int pixel_weight = ++ uint32_t pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + dest_g += pixel_weight * src_scan[j]; + } +- *dest_scan++ = (uint8_t)(dest_g >> 16); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); + } break; + case 3: { +- int dest_r = 0; +- int dest_g = 0; +- int dest_b = 0; ++ uint32_t dest_r = 0; ++ uint32_t dest_g = 0; ++ uint32_t dest_b = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { +- int pixel_weight = ++ uint32_t pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; +- unsigned long argb = m_pSrcPalette.get()[src_scan[j]]; ++ uint32_t argb = m_SrcPalette[src_scan[j]]; + dest_r += pixel_weight * FXARGB_R(argb); + dest_g += pixel_weight * FXARGB_G(argb); + dest_b += pixel_weight * FXARGB_B(argb); + } +- *dest_scan++ = +- (uint8_t)FXRGB2GRAY((dest_r >> 16), (dest_g >> 16), (dest_b >> 16)); ++ *dest_scan++ = static_cast( ++ FXRGB2GRAY(CStretchEngine::PixelFromFixed(dest_r), ++ CStretchEngine::PixelFromFixed(dest_g), ++ CStretchEngine::PixelFromFixed(dest_b))); + } break; + case 4: { + uint32_t dest_b = 0; +@@ -1848,15 +1736,17 @@ void ProgressiveDecoder::ReSampleScanline( + uint32_t dest_r = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { +- int pixel_weight = ++ uint32_t pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel; + dest_b += pixel_weight * (*src_pixel++); + dest_g += pixel_weight * (*src_pixel++); + dest_r += pixel_weight * (*src_pixel); + } +- *dest_scan++ = +- (uint8_t)FXRGB2GRAY((dest_r >> 16), (dest_g >> 16), (dest_b >> 16)); ++ *dest_scan++ = static_cast( ++ FXRGB2GRAY(CStretchEngine::PixelFromFixed(dest_r), ++ CStretchEngine::PixelFromFixed(dest_g), ++ CStretchEngine::PixelFromFixed(dest_b))); + } break; + case 5: { + uint32_t dest_b = 0; +@@ -1864,7 +1754,7 @@ void ProgressiveDecoder::ReSampleScanline( + uint32_t dest_r = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { +- int pixel_weight = ++ uint32_t pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel; + uint8_t src_b = 0; +@@ -1877,8 +1767,10 @@ void ProgressiveDecoder::ReSampleScanline( + dest_g += pixel_weight * src_g; + dest_r += pixel_weight * src_r; + } +- *dest_scan++ = +- (uint8_t)FXRGB2GRAY((dest_r >> 16), (dest_g >> 16), (dest_b >> 16)); ++ *dest_scan++ = static_cast( ++ FXRGB2GRAY(CStretchEngine::PixelFromFixed(dest_r), ++ CStretchEngine::PixelFromFixed(dest_g), ++ CStretchEngine::PixelFromFixed(dest_b))); + } break; + case 6: + return; +@@ -1886,71 +1778,71 @@ void ProgressiveDecoder::ReSampleScanline( + uint32_t dest_g = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { +- int pixel_weight = ++ uint32_t pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + dest_g += pixel_weight * src_scan[j]; + } +- memset(dest_scan, (uint8_t)(dest_g >> 16), 3); ++ memset(dest_scan, CStretchEngine::PixelFromFixed(dest_g), 3); + dest_scan += dest_bytes_per_pixel; + } break; + case 8: { +- int dest_r = 0; +- int dest_g = 0; +- int dest_b = 0; ++ uint32_t dest_r = 0; ++ uint32_t dest_g = 0; ++ uint32_t dest_b = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { +- int pixel_weight = ++ uint32_t pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; +- unsigned long argb = m_pSrcPalette.get()[src_scan[j]]; ++ uint32_t argb = m_SrcPalette[src_scan[j]]; + dest_r += pixel_weight * FXARGB_R(argb); + dest_g += pixel_weight * FXARGB_G(argb); + dest_b += pixel_weight * FXARGB_B(argb); + } +- *dest_scan++ = (uint8_t)((dest_b) >> 16); +- *dest_scan++ = (uint8_t)((dest_g) >> 16); +- *dest_scan++ = (uint8_t)((dest_r) >> 16); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r); + dest_scan += dest_bytes_per_pixel - 3; + } break; + case 12: { + #ifdef PDF_ENABLE_XFA_BMP + if (m_pBmpContext) { +- int dest_r = 0; +- int dest_g = 0; +- int dest_b = 0; ++ uint32_t dest_r = 0; ++ uint32_t dest_g = 0; ++ uint32_t dest_b = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { +- int pixel_weight = ++ uint32_t pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; +- unsigned long argb = m_pSrcPalette.get()[src_scan[j]]; ++ uint32_t argb = m_SrcPalette[src_scan[j]]; + dest_r += pixel_weight * FXARGB_R(argb); + dest_g += pixel_weight * FXARGB_G(argb); + dest_b += pixel_weight * FXARGB_B(argb); + } +- *dest_scan++ = (uint8_t)((dest_b) >> 16); +- *dest_scan++ = (uint8_t)((dest_g) >> 16); +- *dest_scan++ = (uint8_t)((dest_r) >> 16); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r); + *dest_scan++ = 0xFF; + break; + } + #endif // PDF_ENABLE_XFA_BMP +- int dest_a = 0; +- int dest_r = 0; +- int dest_g = 0; +- int dest_b = 0; ++ uint32_t dest_a = 0; ++ uint32_t dest_r = 0; ++ uint32_t dest_g = 0; ++ uint32_t dest_b = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { +- int pixel_weight = ++ uint32_t pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; +- unsigned long argb = m_pSrcPalette.get()[src_scan[j]]; ++ unsigned long argb = m_SrcPalette[src_scan[j]]; + dest_a += pixel_weight * FXARGB_A(argb); + dest_r += pixel_weight * FXARGB_R(argb); + dest_g += pixel_weight * FXARGB_G(argb); + dest_b += pixel_weight * FXARGB_B(argb); + } +- *dest_scan++ = (uint8_t)((dest_b) >> 16); +- *dest_scan++ = (uint8_t)((dest_g) >> 16); +- *dest_scan++ = (uint8_t)((dest_r) >> 16); +- *dest_scan++ = (uint8_t)((dest_a) >> 16); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_a); + } break; + case 9: { + uint32_t dest_b = 0; +@@ -1958,16 +1850,16 @@ void ProgressiveDecoder::ReSampleScanline( + uint32_t dest_r = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { +- int pixel_weight = ++ uint32_t pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel; + dest_b += pixel_weight * (*src_pixel++); + dest_g += pixel_weight * (*src_pixel++); + dest_r += pixel_weight * (*src_pixel); + } +- *dest_scan++ = (uint8_t)((dest_b) >> 16); +- *dest_scan++ = (uint8_t)((dest_g) >> 16); +- *dest_scan++ = (uint8_t)((dest_r) >> 16); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r); + dest_scan += dest_bytes_per_pixel - 3; + } break; + case 10: { +@@ -1976,7 +1868,7 @@ void ProgressiveDecoder::ReSampleScanline( + uint32_t dest_r = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { +- int pixel_weight = ++ uint32_t pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel; + uint8_t src_b = 0; +@@ -1989,9 +1881,9 @@ void ProgressiveDecoder::ReSampleScanline( + dest_g += pixel_weight * src_g; + dest_r += pixel_weight * src_r; + } +- *dest_scan++ = (uint8_t)((dest_b) >> 16); +- *dest_scan++ = (uint8_t)((dest_g) >> 16); +- *dest_scan++ = (uint8_t)((dest_r) >> 16); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r); + dest_scan += dest_bytes_per_pixel - 3; + } break; + case 11: { +@@ -2001,7 +1893,7 @@ void ProgressiveDecoder::ReSampleScanline( + uint32_t dest_b = 0; + for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; + j++) { +- int pixel_weight = ++ uint32_t pixel_weight = + pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; + const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel; + pixel_weight = pixel_weight * src_pixel[3] / 255; +@@ -2010,10 +1902,10 @@ void ProgressiveDecoder::ReSampleScanline( + dest_r += pixel_weight * (*src_pixel); + dest_alpha += pixel_weight; + } +- *dest_scan++ = (uint8_t)((dest_b) >> 16); +- *dest_scan++ = (uint8_t)((dest_g) >> 16); +- *dest_scan++ = (uint8_t)((dest_r) >> 16); +- *dest_scan++ = (uint8_t)((dest_alpha * 255) >> 16); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r); ++ *dest_scan++ = CStretchEngine::PixelFromFixed(dest_alpha * 255); + } break; + default: + return; +@@ -2026,101 +1918,100 @@ void ProgressiveDecoder::ResampleVert( + double scale_y, + int dest_row) { + int dest_Bpp = pDeviceBitmap->GetBPP() >> 3; +- uint32_t dest_ScanOffet = m_startX * dest_Bpp; ++ uint32_t dest_ScanOffset = m_startX * dest_Bpp; + int dest_top = m_startY; +- pdfium::base::CheckedNumeric check_dest_row_1 = dest_row; ++ FX_SAFE_INT32 check_dest_row_1 = dest_row; + check_dest_row_1 -= pdfium::base::checked_cast(scale_y); + int dest_row_1 = check_dest_row_1.ValueOrDie(); + if (dest_row_1 < dest_top) { + int dest_bottom = dest_top + m_sizeY; + if (dest_row + (int)scale_y >= dest_bottom - 1) { +- const uint8_t* scan_src = +- pDeviceBitmap->GetScanline(dest_row) + dest_ScanOffet; ++ pdfium::span scan_src = ++ pDeviceBitmap->GetScanline(dest_row).subspan(dest_ScanOffset, ++ m_sizeX * dest_Bpp); + while (++dest_row < dest_bottom) { +- uint8_t* scan_des = +- pDeviceBitmap->GetWritableScanline(dest_row) + dest_ScanOffet; +- uint32_t size = m_sizeX * dest_Bpp; +- memmove(scan_des, scan_src, size); ++ fxcrt::spanmove(pDeviceBitmap->GetWritableScanline(dest_row).subspan( ++ dest_ScanOffset), ++ scan_src); + } + } + return; + } + for (; dest_row_1 < dest_row; dest_row_1++) { +- uint8_t* scan_des = +- pDeviceBitmap->GetWritableScanline(dest_row_1) + dest_ScanOffet; ++ uint8_t* scan_des = pDeviceBitmap->GetWritableScanline(dest_row_1) ++ .subspan(dest_ScanOffset) ++ .data(); + PixelWeight* pWeight = m_WeightVert.GetPixelWeight(dest_row_1 - dest_top); + const uint8_t* scan_src1 = +- pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top) + +- dest_ScanOffet; ++ pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top) ++ .subspan(dest_ScanOffset) ++ .data(); + const uint8_t* scan_src2 = +- pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top) + +- dest_ScanOffet; +- for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { +- switch (pDeviceBitmap->GetFormat()) { +- case FXDIB_Invalid: +- case FXDIB_1bppMask: +- case FXDIB_1bppRgb: ++ pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top) ++ .subspan(dest_ScanOffset) ++ .data(); ++ switch (pDeviceBitmap->GetFormat()) { ++ case FXDIB_Format::kInvalid: ++ case FXDIB_Format::k1bppMask: ++ case FXDIB_Format::k1bppRgb: ++ return; ++ case FXDIB_Format::k8bppMask: ++ case FXDIB_Format::k8bppRgb: ++ if (pDeviceBitmap->HasPalette()) + return; +- case FXDIB_8bppMask: +- case FXDIB_8bppRgb: { +- if (pDeviceBitmap->GetPalette()) { +- return; +- } +- int dest_g = 0; +- dest_g += pWeight->m_Weights[0] * (*scan_src1++); +- dest_g += pWeight->m_Weights[1] * (*scan_src2++); +- *scan_des++ = (uint8_t)(dest_g >> 16); +- } break; +- case FXDIB_Rgb: +- case FXDIB_Rgb32: { +- uint32_t dest_b = 0; ++ for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { + uint32_t dest_g = 0; +- uint32_t dest_r = 0; +- dest_b += pWeight->m_Weights[0] * (*scan_src1++); + dest_g += pWeight->m_Weights[0] * (*scan_src1++); +- dest_r += pWeight->m_Weights[0] * (*scan_src1++); ++ dest_g += pWeight->m_Weights[1] * (*scan_src2++); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); ++ } ++ break; ++ case FXDIB_Format::kRgb: ++ case FXDIB_Format::kRgb32: ++ for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { ++ uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++); ++ uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++); ++ uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++); + scan_src1 += dest_Bpp - 3; + dest_b += pWeight->m_Weights[1] * (*scan_src2++); + dest_g += pWeight->m_Weights[1] * (*scan_src2++); + dest_r += pWeight->m_Weights[1] * (*scan_src2++); + scan_src2 += dest_Bpp - 3; +- *scan_des++ = (uint8_t)((dest_b) >> 16); +- *scan_des++ = (uint8_t)((dest_g) >> 16); +- *scan_des++ = (uint8_t)((dest_r) >> 16); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_b); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_r); + scan_des += dest_Bpp - 3; +- } break; +- case FXDIB_Argb: { +- uint32_t dest_a = 0; +- uint32_t dest_b = 0; +- uint32_t dest_g = 0; +- uint32_t dest_r = 0; +- dest_b += pWeight->m_Weights[0] * (*scan_src1++); +- dest_g += pWeight->m_Weights[0] * (*scan_src1++); +- dest_r += pWeight->m_Weights[0] * (*scan_src1++); +- dest_a += pWeight->m_Weights[0] * (*scan_src1++); ++ } ++ break; ++ case FXDIB_Format::kArgb: ++ for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { ++ uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++); ++ uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++); ++ uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++); ++ uint32_t dest_a = pWeight->m_Weights[0] * (*scan_src1++); + dest_b += pWeight->m_Weights[1] * (*scan_src2++); + dest_g += pWeight->m_Weights[1] * (*scan_src2++); + dest_r += pWeight->m_Weights[1] * (*scan_src2++); + dest_a += pWeight->m_Weights[1] * (*scan_src2++); +- *scan_des++ = (uint8_t)((dest_b) >> 16); +- *scan_des++ = (uint8_t)((dest_g) >> 16); +- *scan_des++ = (uint8_t)((dest_r) >> 16); +- *scan_des++ = (uint8_t)((dest_a) >> 16); +- } break; +- default: +- return; +- } ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_b); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_r); ++ *scan_des++ = CStretchEngine::PixelFromFixed(dest_a); ++ } ++ break; ++ default: ++ return; + } + } + int dest_bottom = dest_top + m_sizeY; + if (dest_row + (int)scale_y >= dest_bottom - 1) { +- const uint8_t* scan_src = +- pDeviceBitmap->GetScanline(dest_row) + dest_ScanOffet; ++ pdfium::span scan_src = ++ pDeviceBitmap->GetScanline(dest_row).subspan(dest_ScanOffset, ++ m_sizeX * dest_Bpp); + while (++dest_row < dest_bottom) { +- uint8_t* scan_des = +- pDeviceBitmap->GetWritableScanline(dest_row) + dest_ScanOffet; +- uint32_t size = m_sizeX * dest_Bpp; +- memmove(scan_des, scan_src, size); ++ fxcrt::spanmove( ++ pDeviceBitmap->GetWritableScanline(dest_row).subspan(dest_ScanOffset), ++ scan_src); + } + } + } +@@ -2140,16 +2031,16 @@ void ProgressiveDecoder::Resample(const RetainPtr& pDeviceBitmap, + if (dest_row >= dest_top + dest_height) + return; + +- ReSampleScanline(pDeviceBitmap, dest_row, m_pDecodeBuf.get(), src_format); ++ ResampleScanline(pDeviceBitmap, dest_row, m_DecodeBuf, src_format); + if (scale_y > 1.0) + ResampleVert(pDeviceBitmap, scale_y, dest_row); + } + } + + std::pair ProgressiveDecoder::GetFrames() { +- if (!(m_status == FXCODEC_STATUS_FRAME_READY || +- m_status == FXCODEC_STATUS_FRAME_TOBECONTINUE)) { +- return {FXCODEC_STATUS_ERROR, 0}; ++ if (!(m_status == FXCODEC_STATUS::kFrameReady || ++ m_status == FXCODEC_STATUS::kFrameToBeContinued)) { ++ return {FXCODEC_STATUS::kError, 0}; + } + + switch (m_imageType) { +@@ -2164,39 +2055,34 @@ std::pair ProgressiveDecoder::GetFrames() { + case FXCODEC_IMAGE_TIFF: + #endif // PDF_ENABLE_XFA_TIFF + m_FrameNumber = 1; +- m_status = FXCODEC_STATUS_DECODE_READY; ++ m_status = FXCODEC_STATUS::kDecodeReady; + return {m_status, 1}; + #ifdef PDF_ENABLE_XFA_GIF + case FXCODEC_IMAGE_GIF: { +- GifModule* pGifModule = m_pCodecMgr->GetGifModule(); +- if (!pGifModule) { +- m_status = FXCODEC_STATUS_ERR_MEMORY; +- return {m_status, 0}; +- } + while (true) { +- CFX_GifDecodeStatus readResult; ++ GifDecoder::Status readResult; + std::tie(readResult, m_FrameNumber) = +- pGifModule->LoadFrameInfo(m_pGifContext.get()); +- while (readResult == CFX_GifDecodeStatus::Unfinished) { +- FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_READ; +- if (!GifReadMoreData(pGifModule, error_status)) ++ GifDecoder::LoadFrameInfo(m_pGifContext.get()); ++ while (readResult == GifDecoder::Status::kUnfinished) { ++ FXCODEC_STATUS error_status = FXCODEC_STATUS::kError; ++ if (!GifReadMoreData(&error_status)) + return {error_status, 0}; + + std::tie(readResult, m_FrameNumber) = +- pGifModule->LoadFrameInfo(m_pGifContext.get()); ++ GifDecoder::LoadFrameInfo(m_pGifContext.get()); + } +- if (readResult == CFX_GifDecodeStatus::Success) { +- m_status = FXCODEC_STATUS_DECODE_READY; ++ if (readResult == GifDecoder::Status::kSuccess) { ++ m_status = FXCODEC_STATUS::kDecodeReady; + return {m_status, m_FrameNumber}; + } + m_pGifContext = nullptr; +- m_status = FXCODEC_STATUS_ERROR; ++ m_status = FXCODEC_STATUS::kError; + return {m_status, 0}; + } + } + #endif // PDF_ENABLE_XFA_GIF + default: +- return {FXCODEC_STATUS_ERROR, 0}; ++ return {FXCODEC_STATUS::kError, 0}; + } + } + +@@ -2206,17 +2092,17 @@ FXCODEC_STATUS ProgressiveDecoder::StartDecode( + int start_y, + int size_x, + int size_y) { +- if (m_status != FXCODEC_STATUS_DECODE_READY) +- return FXCODEC_STATUS_ERROR; ++ if (m_status != FXCODEC_STATUS::kDecodeReady) ++ return FXCODEC_STATUS::kError; + + if (!pDIBitmap || pDIBitmap->GetBPP() < 8 || m_FrameNumber == 0) +- return FXCODEC_STATUS_ERR_PARAMS; ++ return FXCODEC_STATUS::kError; + + m_pDeviceBitmap = pDIBitmap; + if (m_clipBox.IsEmpty()) +- return FXCODEC_STATUS_ERR_PARAMS; ++ return FXCODEC_STATUS::kError; + if (size_x <= 0 || size_x > 65535 || size_y <= 0 || size_y > 65535) +- return FXCODEC_STATUS_ERR_PARAMS; ++ return FXCODEC_STATUS::kError; + + FX_RECT device_rc = + FX_RECT(start_x, start_y, start_x + size_x, start_y + size_y); +@@ -2225,7 +2111,7 @@ FXCODEC_STATUS ProgressiveDecoder::StartDecode( + device_rc.Intersect( + FX_RECT(0, 0, pDIBitmap->GetWidth(), pDIBitmap->GetHeight())); + if (device_rc.IsEmpty()) +- return FXCODEC_STATUS_ERR_PARAMS; ++ return FXCODEC_STATUS::kError; + + m_startX = device_rc.left; + m_startY = device_rc.top; +@@ -2235,52 +2121,54 @@ FXCODEC_STATUS ProgressiveDecoder::StartDecode( + if (start_x < 0 || out_range_x > 0) { + float scaleX = (float)m_clipBox.Width() / (float)size_x; + if (start_x < 0) { +- m_clipBox.left -= (int32_t)ceil((float)start_x * scaleX); ++ m_clipBox.left -= static_cast(ceil((float)start_x * scaleX)); + } + if (out_range_x > 0) { +- m_clipBox.right -= (int32_t)floor((float)out_range_x * scaleX); ++ m_clipBox.right -= ++ static_cast(floor((float)out_range_x * scaleX)); + } + } + if (start_y < 0 || out_range_y > 0) { + float scaleY = (float)m_clipBox.Height() / (float)size_y; + if (start_y < 0) { +- m_clipBox.top -= (int32_t)ceil((float)start_y * scaleY); ++ m_clipBox.top -= static_cast(ceil((float)start_y * scaleY)); + } + if (out_range_y > 0) { +- m_clipBox.bottom -= (int32_t)floor((float)out_range_y * scaleY); ++ m_clipBox.bottom -= ++ static_cast(floor((float)out_range_y * scaleY)); + } + } + if (m_clipBox.IsEmpty()) { +- return FXCODEC_STATUS_ERR_PARAMS; ++ return FXCODEC_STATUS::kError; + } + switch (m_imageType) { + #ifdef PDF_ENABLE_XFA_BMP + case FXCODEC_IMAGE_BMP: +- return BmpStartDecode(pDIBitmap); ++ return BmpStartDecode(); + #endif // PDF_ENABLE_XFA_BMP + #ifdef PDF_ENABLE_XFA_GIF + case FXCODEC_IMAGE_GIF: +- return GifStartDecode(pDIBitmap); ++ return GifStartDecode(); + #endif // PDF_ENABLE_XFA_GIF + case FXCODEC_IMAGE_JPG: +- return JpegStartDecode(pDIBitmap); ++ return JpegStartDecode(pDIBitmap->GetFormat()); + #ifdef PDF_ENABLE_XFA_PNG + case FXCODEC_IMAGE_PNG: +- return PngStartDecode(pDIBitmap); ++ return PngStartDecode(); + #endif // PDF_ENABLE_XFA_PNG + #ifdef PDF_ENABLE_XFA_TIFF + case FXCODEC_IMAGE_TIFF: +- m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE; ++ m_status = FXCODEC_STATUS::kDecodeToBeContinued; + return m_status; + #endif // PDF_ENABLE_XFA_TIFF + default: +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + } + } + + FXCODEC_STATUS ProgressiveDecoder::ContinueDecode() { +- if (m_status != FXCODEC_STATUS_DECODE_TOBECONTINUE) +- return FXCODEC_STATUS_ERROR; ++ if (m_status != FXCODEC_STATUS::kDecodeToBeContinued) ++ return FXCODEC_STATUS::kError; + + switch (m_imageType) { + case FXCODEC_IMAGE_JPG: +@@ -2302,12 +2190,8 @@ FXCODEC_STATUS ProgressiveDecoder::ContinueDecode() { + return TiffContinueDecode(); + #endif // PDF_ENABLE_XFA_TIFF + default: +- return FXCODEC_STATUS_ERROR; ++ return FXCODEC_STATUS::kError; + } + } + +-std::unique_ptr ModuleMgr::CreateProgressiveDecoder() { +- return pdfium::MakeUnique(this); +-} +- + } // namespace fxcodec +diff --git a/core/fxcodec/progressivedecoder.h b/core/fxcodec/progressive_decoder.h +similarity index 63% +rename from core/fxcodec/progressivedecoder.h +rename to core/fxcodec/progressive_decoder.h +index 1852fe9ee..2f8d2ab6b 100644 +--- a/core/fxcodec/progressivedecoder.h ++++ b/core/fxcodec/progressive_decoder.h +@@ -1,59 +1,57 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#ifndef CORE_FXCODEC_PROGRESSIVEDECODER_H_ +-#define CORE_FXCODEC_PROGRESSIVEDECODER_H_ ++#ifndef CORE_FXCODEC_PROGRESSIVE_DECODER_H_ ++#define CORE_FXCODEC_PROGRESSIVE_DECODER_H_ ++ ++#include ++#include + + #include + #include +-#include + + #include "core/fxcodec/fx_codec_def.h" + #include "core/fxcodec/jpeg/jpegmodule.h" +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcodec/progressive_decoder_iface.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/retain_ptr.h" +-#include "core/fxcrt/unowned_ptr.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/cstretchengine.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/span.h" + + #ifdef PDF_ENABLE_XFA_BMP +-#include "core/fxcodec/bmp/bmpmodule.h" ++#include "core/fxcodec/bmp/bmp_decoder.h" + #endif // PDF_ENABLE_XFA_BMP + + #ifdef PDF_ENABLE_XFA_GIF +-#include "core/fxcodec/gif/gifmodule.h" ++#include "core/fxcodec/gif/gif_decoder.h" + #endif // PDF_ENABLE_XFA_GIF + + #ifdef PDF_ENABLE_XFA_PNG +-#include "core/fxcodec/png/pngmodule.h" ++#include "core/fxcodec/png/png_decoder.h" + #endif // PDF_ENABLE_XFA_PNG + +-#ifdef PDF_ENABLE_XFA_TIFF +-#include "core/fxcodec/tiff/tiffmodule.h" +-#endif // PDF_ENABLE_XFA_TIFF +- + class CFX_DIBitmap; + class IFX_SeekableReadStream; + + namespace fxcodec { + + class CFX_DIBAttribute; +-class ModuleMgr; + + class Dummy {}; // Placeholder to work around C++ syntax issues + +-class ProgressiveDecoder : ++class ProgressiveDecoder final : + #ifdef PDF_ENABLE_XFA_BMP +- public BmpModule::Delegate, ++ public BmpDecoder::Delegate, + #endif // PDF_ENABLE_XFA_BMP + #ifdef PDF_ENABLE_XFA_GIF +- public GifModule::Delegate, ++ public GifDecoder::Delegate, + #endif // PDF_ENABLE_XFA_GIF + #ifdef PDF_ENABLE_XFA_PNG +- public PngModule::Delegate, ++ public PngDecoder::Delegate, + #endif // PDF_ENABLE_XFA_PNG + public Dummy { + public: +@@ -69,10 +67,10 @@ class ProgressiveDecoder : + FXCodec_Cmyk = 0x120 + }; + +- explicit ProgressiveDecoder(ModuleMgr* pCodecMgr); ++ ProgressiveDecoder(); + virtual ~ProgressiveDecoder(); + +- FXCODEC_STATUS LoadImageInfo(const RetainPtr& pFile, ++ FXCODEC_STATUS LoadImageInfo(RetainPtr pFile, + FXCODEC_IMAGE_TYPE imageType, + CFX_DIBAttribute* pAttribute, + bool bSkipImageTypeCheck); +@@ -93,59 +91,8 @@ class ProgressiveDecoder : + + FXCODEC_STATUS ContinueDecode(); + +- struct PixelWeight { +- int m_SrcStart; +- int m_SrcEnd; +- int m_Weights[1]; +- }; +- +- class CFXCODEC_WeightTable { +- public: +- CFXCODEC_WeightTable(); +- ~CFXCODEC_WeightTable(); +- +- void Calc(int dest_len, int src_len); +- PixelWeight* GetPixelWeight(int pixel) { +- return reinterpret_cast(m_pWeightTables.data() + +- (pixel - m_DestMin) * m_ItemSize); +- } +- +- int m_DestMin; +- int m_ItemSize; +- std::vector m_pWeightTables; +- }; +- +- class CFXCODEC_HorzTable { +- public: +- CFXCODEC_HorzTable(); +- ~CFXCODEC_HorzTable(); +- +- void Calc(int dest_len, int src_len); +- PixelWeight* GetPixelWeight(int pixel) { +- return reinterpret_cast(m_pWeightTables.data() + +- pixel * m_ItemSize); +- } +- +- int m_ItemSize; +- std::vector m_pWeightTables; +- }; +- +- class CFXCODEC_VertTable { +- public: +- CFXCODEC_VertTable(); +- ~CFXCODEC_VertTable(); +- +- void Calc(int dest_len, int src_len); +- PixelWeight* GetPixelWeight(int pixel) { +- return reinterpret_cast(m_pWeightTables.data() + +- pixel * m_ItemSize); +- } +- int m_ItemSize; +- std::vector m_pWeightTables; +- }; +- + #ifdef PDF_ENABLE_XFA_PNG +- // PngModule::Delegate ++ // PngDecoder::Delegate + bool PngReadHeader(int width, + int height, + int bpc, +@@ -157,41 +104,72 @@ class ProgressiveDecoder : + #endif // PDF_ENABLE_XFA_PNG + + #ifdef PDF_ENABLE_XFA_GIF +- // GifModule::Delegate +- void GifRecordCurrentPosition(uint32_t& cur_pos) override; ++ // GifDecoder::Delegate ++ uint32_t GifCurrentPosition() const override; + bool GifInputRecordPositionBuf(uint32_t rcd_pos, + const FX_RECT& img_rc, + int32_t pal_num, + CFX_GifPalette* pal_ptr, +- int32_t delay_time, +- bool user_input, + int32_t trans_index, +- int32_t disposal_method, + bool interlace) override; +- void GifReadScanline(int32_t row_num, uint8_t* row_buf) override; ++ void GifReadScanline(int32_t row_num, pdfium::span row_buf) override; + #endif // PDF_ENABLE_XFA_GIF + + #ifdef PDF_ENABLE_XFA_BMP +- // BmpModule::Delegate ++ // BmpDecoder::Delegate + bool BmpInputImagePositionBuf(uint32_t rcd_pos) override; + void BmpReadScanline(uint32_t row_num, + pdfium::span row_buf) override; + #endif // PDF_ENABLE_XFA_BMP + + private: ++ using WeightTable = CStretchEngine::WeightTable; ++ using PixelWeight = CStretchEngine::PixelWeight; ++ ++ class HorzTable { ++ public: ++ HorzTable(); ++ ~HorzTable(); ++ ++ void CalculateWeights(int dest_len, int src_len); ++ PixelWeight* GetPixelWeight(int pixel) { ++ return reinterpret_cast(m_pWeightTables.data() + ++ pixel * m_ItemSize); ++ } ++ ++ private: ++ int m_ItemSize = 0; ++ DataVector m_pWeightTables; ++ }; ++ ++ class VertTable { ++ public: ++ VertTable(); ++ ~VertTable(); ++ ++ void CalculateWeights(int dest_len, int src_len); ++ PixelWeight* GetPixelWeight(int pixel) { ++ return reinterpret_cast(m_pWeightTables.data() + ++ pixel * m_ItemSize); ++ } ++ ++ private: ++ int m_ItemSize = 0; ++ DataVector m_pWeightTables; ++ }; ++ + #ifdef PDF_ENABLE_XFA_BMP +- bool BmpReadMoreData(BmpModule* pBmpModule, +- ModuleIface::Context* pBmpContext, +- FXCODEC_STATUS& err_status); ++ bool BmpReadMoreData(ProgressiveDecoderIface::Context* pBmpContext, ++ FXCODEC_STATUS* err_status); + bool BmpDetectImageTypeInBuffer(CFX_DIBAttribute* pAttribute); +- FXCODEC_STATUS BmpStartDecode(const RetainPtr& pDIBitmap); ++ FXCODEC_STATUS BmpStartDecode(); + FXCODEC_STATUS BmpContinueDecode(); + #endif // PDF_ENABLE_XFA_BMP + + #ifdef PDF_ENABLE_XFA_GIF +- bool GifReadMoreData(GifModule* pGifModule, FXCODEC_STATUS& err_status); ++ bool GifReadMoreData(FXCODEC_STATUS* err_status); + bool GifDetectImageTypeInBuffer(); +- FXCODEC_STATUS GifStartDecode(const RetainPtr& pDIBitmap); ++ FXCODEC_STATUS GifStartDecode(); + FXCODEC_STATUS GifContinueDecode(); + void GifDoubleLineResampleVert(const RetainPtr& pDeviceBitmap, + double scale_y, +@@ -201,10 +179,10 @@ class ProgressiveDecoder : + #ifdef PDF_ENABLE_XFA_PNG + void PngOneOneMapResampleHorz(const RetainPtr& pDeviceBitmap, + int32_t dest_line, +- uint8_t* src_scan, ++ pdfium::span src_span, + FXCodec_Format src_format); + bool PngDetectImageTypeInBuffer(CFX_DIBAttribute* pAttribute); +- FXCODEC_STATUS PngStartDecode(const RetainPtr& pDIBitmap); ++ FXCODEC_STATUS PngStartDecode(); + FXCODEC_STATUS PngContinueDecode(); + #endif // PDF_ENABLE_XFA_PNG + +@@ -213,24 +191,23 @@ class ProgressiveDecoder : + FXCODEC_STATUS TiffContinueDecode(); + #endif // PDF_ENABLE_XFA_TIFF + +- bool JpegReadMoreData(JpegModule* pJpegModule, FXCODEC_STATUS& err_status); ++ bool JpegReadMoreData(FXCODEC_STATUS* err_status); + bool JpegDetectImageTypeInBuffer(CFX_DIBAttribute* pAttribute); +- FXCODEC_STATUS JpegStartDecode(const RetainPtr& pDIBitmap); ++ FXCODEC_STATUS JpegStartDecode(FXDIB_Format format); + FXCODEC_STATUS JpegContinueDecode(); + + bool DetectImageType(FXCODEC_IMAGE_TYPE imageType, + CFX_DIBAttribute* pAttribute); +- bool ReadMoreData(ModuleIface* pModule, +- ModuleIface::Context* pContext, +- bool invalidate_buffer, +- FXCODEC_STATUS& err_status); ++ bool ReadMoreData(ProgressiveDecoderIface* pModule, ++ ProgressiveDecoderIface::Context* pContext, ++ FXCODEC_STATUS* err_status); + +- void GetDownScale(int& down_scale); ++ int GetDownScale(); + void GetTransMethod(FXDIB_Format dest_format, FXCodec_Format src_format); + +- void ReSampleScanline(const RetainPtr& pDeviceBitmap, ++ void ResampleScanline(const RetainPtr& pDeviceBitmap, + int32_t dest_line, +- uint8_t* src_scan, ++ pdfium::span src_span, + FXCodec_Format src_format); + void Resample(const RetainPtr& pDeviceBitmap, + int32_t src_line, +@@ -243,32 +220,31 @@ class ProgressiveDecoder : + double scale_y, + int dest_row); + +- FXCODEC_STATUS m_status = FXCODEC_STATUS_DECODE_FINISH; ++ FXCODEC_STATUS m_status = FXCODEC_STATUS::kDecodeFinished; + FXCODEC_IMAGE_TYPE m_imageType = FXCODEC_IMAGE_UNKNOWN; + RetainPtr m_pFile; + RetainPtr m_pDeviceBitmap; +- UnownedPtr m_pCodecMgr; + RetainPtr m_pCodecMemory; +- std::unique_ptr m_pDecodeBuf; +- std::unique_ptr m_pSrcPalette; +- std::unique_ptr m_pJpegContext; ++ DataVector m_DecodeBuf; ++ DataVector m_SrcPalette; ++ std::unique_ptr m_pJpegContext; + #ifdef PDF_ENABLE_XFA_BMP +- std::unique_ptr m_pBmpContext; ++ std::unique_ptr m_pBmpContext; + #endif // PDF_ENABLE_XFA_BMP + #ifdef PDF_ENABLE_XFA_GIF +- std::unique_ptr m_pGifContext; ++ std::unique_ptr m_pGifContext; + #endif // PDF_ENABLE_XFA_GIF + #ifdef PDF_ENABLE_XFA_PNG +- std::unique_ptr m_pPngContext; ++ std::unique_ptr m_pPngContext; + #endif // PDF_ENABLE_XFA_PNG + #ifdef PDF_ENABLE_XFA_TIFF +- std::unique_ptr m_pTiffContext; ++ std::unique_ptr m_pTiffContext; + #endif // PDF_ENABLE_XFA_TIFF + uint32_t m_offSet = 0; + int m_ScanlineSize = 0; +- CFXCODEC_WeightTable m_WeightHorz; +- CFXCODEC_VertTable m_WeightVert; +- CFXCODEC_HorzTable m_WeightHorzOO; ++ WeightTable m_WeightHorz; ++ VertTable m_WeightVert; ++ HorzTable m_WeightHorzOO; + int m_SrcWidth = 0; + int m_SrcHeight = 0; + int m_SrcComponents = 0; +@@ -291,7 +267,6 @@ class ProgressiveDecoder : + int32_t m_GifPltNumber = 0; + int m_GifTransIndex = -1; + FX_RECT m_GifFrameRect; +- bool m_InvalidateGifBuffer = true; + #endif // PDF_ENABLE_XFA_GIF + #ifdef PDF_ENABLE_XFA_BMP + bool m_BmpIsTopBottom = false; +@@ -302,4 +277,4 @@ class ProgressiveDecoder : + + using ProgressiveDecoder = fxcodec::ProgressiveDecoder; + +-#endif // CORE_FXCODEC_PROGRESSIVEDECODER_H_ ++#endif // CORE_FXCODEC_PROGRESSIVE_DECODER_H_ +diff --git a/core/fxcodec/codec_module_iface.h b/core/fxcodec/progressive_decoder_iface.h +similarity index 50% +rename from core/fxcodec/codec_module_iface.h +rename to core/fxcodec/progressive_decoder_iface.h +index 992d2ad52..a21f23491 100644 +--- a/core/fxcodec/codec_module_iface.h ++++ b/core/fxcodec/progressive_decoder_iface.h +@@ -1,43 +1,42 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#ifndef CORE_FXCODEC_CODEC_MODULE_IFACE_H_ +-#define CORE_FXCODEC_CODEC_MODULE_IFACE_H_ ++#ifndef CORE_FXCODEC_PROGRESSIVE_DECODER_IFACE_H_ ++#define CORE_FXCODEC_PROGRESSIVE_DECODER_IFACE_H_ + +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/fx_types.h" + #include "core/fxcrt/retain_ptr.h" + ++#ifndef PDF_ENABLE_XFA ++#error "XFA Only" ++#endif ++ + class CFX_CodecMemory; + + namespace fxcodec { + +-class CFX_DIBAttribute; +- +-class ModuleIface { ++class ProgressiveDecoderIface { + public: + class Context { + public: + virtual ~Context() = default; + }; + +- virtual ~ModuleIface() = default; ++ virtual ~ProgressiveDecoderIface() = default; + + // Returns the number of unprocessed bytes remaining in the input buffer. + virtual FX_FILESIZE GetAvailInput(Context* pContext) const = 0; + +- // Provides a new input buffer to the codec. Returns true on success, +- // setting details about the image extracted from the buffer into |pAttribute| +- // (if provided and the codec is capable providing that information). ++ // Provides a new input buffer to the codec. Returns true on success. + virtual bool Input(Context* pContext, +- RetainPtr codec_memory, +- CFX_DIBAttribute* pAttribute) = 0; ++ RetainPtr codec_memory) = 0; + }; + + } // namespace fxcodec + +-using fxcodec::ModuleIface; ++using fxcodec::ProgressiveDecoderIface; + +-#endif // CORE_FXCODEC_CODEC_MODULE_IFACE_H_ ++#endif // CORE_FXCODEC_PROGRESSIVE_DECODER_IFACE_H_ +diff --git a/core/fxcodec/progressive_decoder_unittest.cpp b/core/fxcodec/progressive_decoder_unittest.cpp +new file mode 100644 +index 000000000..cdc0ae1c7 +--- /dev/null ++++ b/core/fxcodec/progressive_decoder_unittest.cpp +@@ -0,0 +1,477 @@ ++// Copyright 2019 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcodec/progressive_decoder.h" ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "core/fxcodec/fx_codec.h" ++#include "core/fxcodec/fx_codec_def.h" ++#include "core/fxcrt/cfx_read_only_span_stream.h" ++#include "core/fxcrt/cfx_read_only_vector_stream.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxge/dib/cfx_dibitmap.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "testing/gmock/include/gmock/gmock.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/base/span.h" ++ ++#ifdef PDF_ENABLE_XFA_BMP ++#include "core/fxcodec/bmp/bmp_decoder.h" ++#endif // PDF_ENABLE_XFA_BMP ++ ++#ifdef PDF_ENABLE_XFA_GIF ++#include "core/fxcodec/gif/gif_decoder.h" ++#endif // PDF_ENABLE_XFA_GIF ++ ++namespace fxcodec { ++ ++namespace { ++ ++using ::testing::ElementsAre; ++using ::testing::ElementsAreArray; ++ ++template ++constexpr std::array IotaArray(uint8_t start) { ++ std::array result; ++ std::iota(result.begin(), result.end(), start); ++ return result; ++} ++ ++FXCODEC_STATUS DecodeToBitmap(ProgressiveDecoder& decoder, ++ const fxcrt::RetainPtr& bitmap) { ++ FXCODEC_STATUS status = decoder.StartDecode(bitmap, 0, 0, bitmap->GetWidth(), ++ bitmap->GetHeight()); ++ while (status == FXCODEC_STATUS::kDecodeToBeContinued) ++ status = decoder.ContinueDecode(); ++ return status; ++} ++ ++} // namespace ++ ++#ifdef PDF_ENABLE_XFA_BMP ++TEST(ProgressiveDecoder, Indexed8Bmp) { ++ static constexpr uint8_t kInput[] = { ++ 0x42, 0x4d, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, ++ 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, ++ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, ++ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc0, ++ 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00}; ++ ++ ProgressiveDecoder decoder; ++ ++ auto source = pdfium::MakeRetain(kInput); ++ CFX_DIBAttribute attr; ++ FXCODEC_STATUS status = ++ decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true); ++ ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status); ++ ++ ASSERT_EQ(1, decoder.GetWidth()); ++ ASSERT_EQ(1, decoder.GetHeight()); ++ ++ auto bitmap = pdfium::MakeRetain(); ++ bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kRgb); ++ ++ size_t frames; ++ std::tie(status, frames) = decoder.GetFrames(); ++ ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status); ++ ASSERT_EQ(1u, frames); ++ ++ status = DecodeToBitmap(decoder, bitmap); ++ EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status); ++ EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00)); ++} ++ ++TEST(ProgressiveDecoder, Indexed8BmpWithInvalidIndex) { ++ static constexpr uint8_t kInput[] = { ++ 0x42, 0x4d, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, ++ 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, ++ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, ++ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc0, ++ 0x80, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00}; ++ ++ ProgressiveDecoder decoder; ++ ++ auto source = pdfium::MakeRetain(kInput); ++ CFX_DIBAttribute attr; ++ FXCODEC_STATUS status = ++ decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true); ++ ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status); ++ ++ ASSERT_EQ(1, decoder.GetWidth()); ++ ASSERT_EQ(1, decoder.GetHeight()); ++ ++ auto bitmap = pdfium::MakeRetain(); ++ bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kRgb); ++ ++ size_t frames; ++ std::tie(status, frames) = decoder.GetFrames(); ++ ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status); ++ ASSERT_EQ(1u, frames); ++ ++ status = DecodeToBitmap(decoder, bitmap); ++ EXPECT_EQ(FXCODEC_STATUS::kError, status); ++} ++ ++TEST(ProgressiveDecoder, Direct24Bmp) { ++ static constexpr uint8_t kInput[] = { ++ 0x42, 0x4d, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, ++ 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, ++ 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, ++ 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, 0x40, 0x00}; ++ ++ ProgressiveDecoder decoder; ++ ++ auto source = pdfium::MakeRetain(kInput); ++ CFX_DIBAttribute attr; ++ FXCODEC_STATUS status = ++ decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true); ++ ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status); ++ ++ ASSERT_EQ(1, decoder.GetWidth()); ++ ASSERT_EQ(1, decoder.GetHeight()); ++ ++ auto bitmap = pdfium::MakeRetain(); ++ bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kRgb); ++ ++ size_t frames; ++ std::tie(status, frames) = decoder.GetFrames(); ++ ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status); ++ ASSERT_EQ(1u, frames); ++ ++ status = DecodeToBitmap(decoder, bitmap); ++ EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status); ++ EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00)); ++} ++ ++TEST(ProgressiveDecoder, Direct32Bmp) { ++ static constexpr uint8_t kInput[] = { ++ 0x42, 0x4d, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, ++ 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, ++ 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, ++ 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, 0x40, 0xff}; ++ ++ ProgressiveDecoder decoder; ++ ++ auto source = pdfium::MakeRetain(kInput); ++ CFX_DIBAttribute attr; ++ FXCODEC_STATUS status = ++ decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true); ++ ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status); ++ ++ ASSERT_EQ(1, decoder.GetWidth()); ++ ASSERT_EQ(1, decoder.GetHeight()); ++ ++ auto bitmap = pdfium::MakeRetain(); ++ bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kRgb); ++ ++ size_t frames; ++ std::tie(status, frames) = decoder.GetFrames(); ++ ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status); ++ ASSERT_EQ(1u, frames); ++ ++ status = DecodeToBitmap(decoder, bitmap); ++ EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status); ++ EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00)); ++} ++ ++TEST(ProgressiveDecoder, BmpWithDataOffsetBeforeEndOfHeader) { ++ static constexpr uint8_t kInput[] = { ++ 0x42, 0x4d, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, ++ 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, ++ 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, ++ 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, 0x40, 0x00}; ++ ++ ProgressiveDecoder decoder; ++ ++ auto source = pdfium::MakeRetain(kInput); ++ CFX_DIBAttribute attr; ++ FXCODEC_STATUS status = ++ decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true); ++ ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status); ++ ++ ASSERT_EQ(1, decoder.GetWidth()); ++ ASSERT_EQ(1, decoder.GetHeight()); ++ ++ auto bitmap = pdfium::MakeRetain(); ++ bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kRgb); ++ ++ size_t frames; ++ std::tie(status, frames) = decoder.GetFrames(); ++ ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status); ++ ASSERT_EQ(1u, frames); ++ ++ status = DecodeToBitmap(decoder, bitmap); ++ EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status); ++ EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00)); ++} ++ ++TEST(ProgressiveDecoder, BmpWithDataOffsetAfterEndOfHeader) { ++ static constexpr uint8_t kInput[] = { ++ 0x42, 0x4d, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, ++ 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, ++ 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, ++ 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, 0x40, 0x00}; ++ ++ ProgressiveDecoder decoder; ++ ++ auto source = pdfium::MakeRetain(kInput); ++ CFX_DIBAttribute attr; ++ FXCODEC_STATUS status = ++ decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true); ++ ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status); ++ ++ ASSERT_EQ(1, decoder.GetWidth()); ++ ASSERT_EQ(1, decoder.GetHeight()); ++ ++ auto bitmap = pdfium::MakeRetain(); ++ bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kRgb); ++ ++ size_t frames; ++ std::tie(status, frames) = decoder.GetFrames(); ++ ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status); ++ ASSERT_EQ(1u, frames); ++ ++ status = DecodeToBitmap(decoder, bitmap); ++ EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status); ++ EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00)); ++} ++ ++TEST(ProgressiveDecoder, LargeBmp) { ++ // Construct a 24-bit BMP larger than `kBlockSize` (4096 bytes). ++ static constexpr uint8_t kWidth = 37; ++ static constexpr uint8_t kHeight = 38; ++ static constexpr size_t kScanlineSize = kWidth * 3 + 1; ++ DataVector input = { ++ 0x42, 0x4d, 0xd6, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, ++ 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, kWidth, 0x00, 0x00, 0x00, ++ kHeight, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0xa0, 0x10, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; ++ input.resize(54 + kScanlineSize * kHeight); ++ std::iota(input.begin() + 54, input.end(), 0); ++ ASSERT_EQ(4310u, input.size()); ++ ++ ProgressiveDecoder decoder; ++ ++ auto source = pdfium::MakeRetain(std::move(input)); ++ CFX_DIBAttribute attr; ++ FXCODEC_STATUS status = ++ decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true); ++ ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status); ++ ++ ASSERT_EQ(kWidth, decoder.GetWidth()); ++ ASSERT_EQ(kHeight, decoder.GetHeight()); ++ ++ auto bitmap = pdfium::MakeRetain(); ++ bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kRgb); ++ ++ size_t frames; ++ std::tie(status, frames) = decoder.GetFrames(); ++ ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status); ++ ASSERT_EQ(1u, frames); ++ ++ status = DecodeToBitmap(decoder, bitmap); ++ EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status); ++ ++ for (size_t row = 0; row < kHeight; ++row) { ++ // BMP encodes rows from bottom to top by default. ++ pdfium::span scanline = ++ bitmap->GetScanline(kHeight - row - 1); ++ ++ EXPECT_THAT( ++ scanline.subspan(0, kScanlineSize - 1), ++ ElementsAreArray(IotaArray(row * kScanlineSize))); ++ ++ // Last byte is padding to a 32-bit boundary. ++ EXPECT_EQ(0, scanline[kScanlineSize - 1]); ++ } ++} ++#endif // PDF_ENABLE_XFA_BMP ++ ++#ifdef PDF_ENABLE_XFA_GIF ++TEST(ProgressiveDecoder, Gif87a) { ++ static constexpr uint8_t kInput[] = { ++ 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0x01, ++ 0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x2c, 0x00, 0x00, 0x00, 0x00, ++ 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b}; ++ ++ ProgressiveDecoder decoder; ++ ++ auto source = pdfium::MakeRetain(kInput); ++ CFX_DIBAttribute attr; ++ FXCODEC_STATUS status = ++ decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_GIF, &attr, true); ++ ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status); ++ ++ ASSERT_EQ(1, decoder.GetWidth()); ++ ASSERT_EQ(1, decoder.GetHeight()); ++ ++ auto bitmap = pdfium::MakeRetain(); ++ bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kArgb); ++ ++ size_t frames; ++ std::tie(status, frames) = decoder.GetFrames(); ++ ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status); ++ ASSERT_EQ(1u, frames); ++ ++ status = DecodeToBitmap(decoder, bitmap); ++ EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status); ++ EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0xff)); ++} ++ ++TEST(ProgressiveDecoder, Gif89a) { ++ static constexpr uint8_t kInput[] = { ++ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, ++ 0x01, 0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x21, 0xf9, 0x04, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, ++ 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b}; ++ ++ ProgressiveDecoder decoder; ++ ++ auto source = pdfium::MakeRetain(kInput); ++ CFX_DIBAttribute attr; ++ FXCODEC_STATUS status = ++ decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_GIF, &attr, true); ++ ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status); ++ ++ ASSERT_EQ(1, decoder.GetWidth()); ++ ASSERT_EQ(1, decoder.GetHeight()); ++ ++ auto bitmap = pdfium::MakeRetain(); ++ bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kArgb); ++ ++ size_t frames; ++ std::tie(status, frames) = decoder.GetFrames(); ++ ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status); ++ ASSERT_EQ(1u, frames); ++ ++ status = DecodeToBitmap(decoder, bitmap); ++ EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status); ++ EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0xff)); ++} ++ ++TEST(ProgressiveDecoder, GifInsufficientCodeSize) { ++ // This GIF causes `LZWDecompressor::Create()` to fail because the minimum ++ // code size is too small for the palette. ++ static constexpr uint8_t kInput[] = { ++ 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x01, 0x00, 0x01, 0x00, 0x82, ++ 0x01, 0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, ++ 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, ++ 0x85, 0x86, 0x86, 0x86, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, ++ 0x01, 0x00, 0x00, 0x02, 0x2, 0x44, 0x01, 0x00, 0x3b}; ++ ++ ProgressiveDecoder decoder; ++ ++ auto source = pdfium::MakeRetain(kInput); ++ CFX_DIBAttribute attr; ++ FXCODEC_STATUS status = ++ decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_GIF, &attr, true); ++ ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status); ++ ++ ASSERT_EQ(1, decoder.GetWidth()); ++ ASSERT_EQ(1, decoder.GetHeight()); ++ ++ auto bitmap = pdfium::MakeRetain(); ++ bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kArgb); ++ ++ size_t frames; ++ std::tie(status, frames) = decoder.GetFrames(); ++ ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status); ++ ASSERT_EQ(1u, frames); ++ ++ status = DecodeToBitmap(decoder, bitmap); ++ EXPECT_EQ(FXCODEC_STATUS::kError, status); ++} ++ ++TEST(ProgressiveDecoder, GifDecodeAcrossScanlines) { ++ // This GIF contains an LZW code unit split across 2 scanlines. The decoder ++ // must continue decoding the second scanline using the residual data. ++ static constexpr uint8_t kInput[] = { ++ 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x02, 0x00, 0x80, 0x01, ++ 0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x2c, 0x00, 0x00, 0x00, 0x00, ++ 0x04, 0x00, 0x02, 0x00, 0x00, 0x02, 0x03, 0x84, 0x6f, 0x05, 0x00, 0x3b}; ++ ++ ProgressiveDecoder decoder; ++ ++ auto source = pdfium::MakeRetain(kInput); ++ CFX_DIBAttribute attr; ++ FXCODEC_STATUS status = ++ decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_GIF, &attr, true); ++ ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status); ++ ++ ASSERT_EQ(4, decoder.GetWidth()); ++ ASSERT_EQ(2, decoder.GetHeight()); ++ ++ auto bitmap = pdfium::MakeRetain(); ++ bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kArgb); ++ ++ size_t frames; ++ std::tie(status, frames) = decoder.GetFrames(); ++ ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status); ++ ASSERT_EQ(1u, frames); ++ ++ status = DecodeToBitmap(decoder, bitmap); ++ EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status); ++ EXPECT_THAT(bitmap->GetScanline(0), ++ ElementsAre(0xc0, 0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff, 0xc0, ++ 0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff)); ++ EXPECT_THAT(bitmap->GetScanline(1), ++ ElementsAre(0xc0, 0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff, 0xc0, ++ 0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff)); ++} ++ ++TEST(ProgressiveDecoder, GifDecodeAcrossSubblocks) { ++ // This GIF contains a scanline split across 2 data sub-blocks. The decoder ++ // must continue decoding in the second sub-block. ++ static constexpr uint8_t kInput[] = { ++ 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x02, 0x00, ++ 0x80, 0x01, 0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x2c, ++ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x02, ++ 0x02, 0x84, 0x6f, 0x01, 0x05, 0x00, 0x3b}; ++ ++ ProgressiveDecoder decoder; ++ ++ auto source = pdfium::MakeRetain(kInput); ++ CFX_DIBAttribute attr; ++ FXCODEC_STATUS status = ++ decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_GIF, &attr, true); ++ ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status); ++ ++ ASSERT_EQ(4, decoder.GetWidth()); ++ ASSERT_EQ(2, decoder.GetHeight()); ++ ++ auto bitmap = pdfium::MakeRetain(); ++ bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kArgb); ++ ++ size_t frames; ++ std::tie(status, frames) = decoder.GetFrames(); ++ ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status); ++ ASSERT_EQ(1u, frames); ++ ++ status = DecodeToBitmap(decoder, bitmap); ++ EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status); ++ EXPECT_THAT(bitmap->GetScanline(0), ++ ElementsAre(0xc0, 0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff, 0xc0, ++ 0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff)); ++ EXPECT_THAT(bitmap->GetScanline(1), ++ ElementsAre(0xc0, 0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff, 0xc0, ++ 0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff)); ++} ++#endif // PDF_ENABLE_XFA_GIF ++ ++} // namespace fxcodec +diff --git a/core/fxcodec/progressivedecoder_unittest.cpp b/core/fxcodec/progressivedecoder_unittest.cpp +deleted file mode 100644 +index 5c4e26f21..000000000 +--- a/core/fxcodec/progressivedecoder_unittest.cpp ++++ /dev/null +@@ -1,406 +0,0 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#include "core/fxcodec/progressivedecoder.h" +- +-#include "core/fxcodec/fx_codec.h" +-#include "core/fxcrt/cfx_readonlymemorystream.h" +-#include "core/fxge/dib/cfx_dibitmap.h" +-#include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/span.h" +- +-#ifdef PDF_ENABLE_XFA_GIF +-#include "core/fxcodec/gif/gifmodule.h" +-#endif // PDF_ENABLE_XFA_GIF +- +-namespace fxcodec { +- +-#ifdef PDF_ENABLE_XFA_GIF +-TEST(ProgressiveDecoder, BUG_895009) { +- static constexpr uint8_t kInput[] = { +- 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x62, 0x00, 0x21, 0x1b, 0x27, 0x01, +- 0x00, 0x2c, 0x3b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x71, 0x00, 0xf8, 0x20, +- 0x00, 0x71, 0x00, 0xf8, 0x0b, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, +- 0x00, 0x01, 0x00, 0x01, 0xbe, 0x00, 0x01, 0xbe, 0x00, 0x21, 0xf9, 0xf9, +- 0x21, 0x01, 0x00, 0x21, 0xf9, 0x00, 0x21, 0xf9, 0x00, 0x21, 0x01, 0x20, +- 0x00, 0x21, 0xf9, 0x00, 0x21, 0x21, 0x00, 0x21, 0x00, 0x00, 0x21, 0x01, +- 0x00, 0x21, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, +- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x47, +- 0x49, 0x46, 0x38, 0x39, 0x61, 0x11, 0x00, 0x20, 0x00, 0xf1, 0x03, 0x32, +- 0x34, 0x37, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, +- 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x11, 0x00, +- 0x20, 0x00, 0xf1, 0x03, 0x32, 0x34, 0x37, 0x21, 0x01, 0x00, 0x00, 0x00, +- 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xc3, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x5b, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xf5, +- 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, +- 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, +- 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, +- 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, +- 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, +- 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, +- 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, +- 0x03, 0x03, 0x03, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, +- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, +- 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x47, 0x49, 0x46, 0x37, +- 0x36, 0x61, 0x01, 0x04, 0x20, 0x00, 0xf1, 0x03, 0x31, 0x33, 0x33, 0x37, +- 0x32, 0x30, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, +- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, +- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, +- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x03, 0x00, 0x01, 0x03, +- 0x01, 0x03, 0x3b, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, +- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, +- 0x03, 0x3a, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, +- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xfd, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +- 0x01, 0x01, 0x01, 0xb4, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +- +- ModuleMgr::Create(); +- ModuleMgr::GetInstance()->SetGifModule(pdfium::MakeUnique()); +- { +- std::unique_ptr decoder = +- ModuleMgr::GetInstance()->CreateProgressiveDecoder(); +- +- auto source = pdfium::MakeRetain(kInput); +- CFX_DIBAttribute attr; +- FXCODEC_STATUS status = +- decoder->LoadImageInfo(source, FXCODEC_IMAGE_GIF, &attr, true); +- ASSERT_EQ(FXCODEC_STATUS_FRAME_READY, status); +- +- ASSERT_EQ(98, decoder->GetWidth()); +- ASSERT_EQ(6945, decoder->GetHeight()); +- +- auto bitmap = pdfium::MakeRetain(); +- bitmap->Create(decoder->GetWidth(), decoder->GetHeight(), FXDIB_Argb); +- +- size_t frames; +- std::tie(status, frames) = decoder->GetFrames(); +- ASSERT_EQ(FXCODEC_STATUS_DECODE_READY, status); +- ASSERT_EQ(1u, frames); +- +- status = decoder->StartDecode(bitmap, 0, 0, bitmap->GetWidth(), +- bitmap->GetHeight()); +- while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE) +- status = decoder->ContinueDecode(); +- EXPECT_EQ(FXCODEC_STATUS_DECODE_FINISH, status); +- } +- ModuleMgr::Destroy(); +-} +-#endif // PDF_ENABLE_XFA_GIF +- +-} // namespace fxcodec +diff --git a/core/fxcodec/scanlinedecoder.cpp b/core/fxcodec/scanlinedecoder.cpp +index 2aa41785b..bce71b9ee 100644 +--- a/core/fxcodec/scanlinedecoder.cpp ++++ b/core/fxcodec/scanlinedecoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -29,20 +29,20 @@ ScanlineDecoder::ScanlineDecoder(int nOrigWidth, + + ScanlineDecoder::~ScanlineDecoder() = default; + +-const uint8_t* ScanlineDecoder::GetScanline(int line) { ++pdfium::span ScanlineDecoder::GetScanline(int line) { + if (m_NextLine == line + 1) + return m_pLastScanline; + + if (m_NextLine < 0 || m_NextLine > line) { +- if (!v_Rewind()) +- return nullptr; ++ if (!Rewind()) ++ return pdfium::span(); + m_NextLine = 0; + } + while (m_NextLine < line) { +- ReadNextLine(); ++ GetNextLine(); + m_NextLine++; + } +- m_pLastScanline = ReadNextLine(); ++ m_pLastScanline = GetNextLine(); + m_NextLine++; + return m_pLastScanline; + } +@@ -52,12 +52,12 @@ bool ScanlineDecoder::SkipToScanline(int line, PauseIndicatorIface* pPause) { + return false; + + if (m_NextLine < 0 || m_NextLine > line) { +- v_Rewind(); ++ Rewind(); + m_NextLine = 0; + } +- m_pLastScanline = nullptr; ++ m_pLastScanline = pdfium::span(); + while (m_NextLine < line) { +- m_pLastScanline = ReadNextLine(); ++ m_pLastScanline = GetNextLine(); + m_NextLine++; + if (pPause && pPause->NeedToPauseNow()) { + return true; +@@ -66,8 +66,4 @@ bool ScanlineDecoder::SkipToScanline(int line, PauseIndicatorIface* pPause) { + return false; + } + +-uint8_t* ScanlineDecoder::ReadNextLine() { +- return v_GetNextLine(); +-} +- + } // namespace fxcodec +diff --git a/core/fxcodec/scanlinedecoder.h b/core/fxcodec/scanlinedecoder.h +index e0014cfa0..0a1db37f8 100644 +--- a/core/fxcodec/scanlinedecoder.h ++++ b/core/fxcodec/scanlinedecoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,9 @@ + #ifndef CORE_FXCODEC_SCANLINEDECODER_H_ + #define CORE_FXCODEC_SCANLINEDECODER_H_ + +-#include "core/fxcrt/fx_system.h" ++#include ++ ++#include "third_party/base/span.h" + + class PauseIndicatorIface; + +@@ -25,7 +27,7 @@ class ScanlineDecoder { + uint32_t nPitch); + virtual ~ScanlineDecoder(); + +- const uint8_t* GetScanline(int line); ++ pdfium::span GetScanline(int line); + bool SkipToScanline(int line, PauseIndicatorIface* pPause); + + int GetWidth() const { return m_OutputWidth; } +@@ -36,10 +38,8 @@ class ScanlineDecoder { + virtual uint32_t GetSrcOffset() = 0; + + protected: +- virtual bool v_Rewind() = 0; +- virtual uint8_t* v_GetNextLine() = 0; +- +- uint8_t* ReadNextLine(); ++ virtual bool Rewind() = 0; ++ virtual pdfium::span GetNextLine() = 0; + + int m_OrigWidth; + int m_OrigHeight; +@@ -49,7 +49,7 @@ class ScanlineDecoder { + int m_bpc; + uint32_t m_Pitch; + int m_NextLine = -1; +- uint8_t* m_pLastScanline = nullptr; ++ pdfium::span m_pLastScanline; + }; + + } // namespace fxcodec +diff --git a/core/fxcodec/tiff/tiffmodule.cpp b/core/fxcodec/tiff/tiff_decoder.cpp +similarity index 73% +rename from core/fxcodec/tiff/tiffmodule.cpp +rename to core/fxcodec/tiff/tiff_decoder.cpp +index 179fca665..518dd0b1d 100644 +--- a/core/fxcodec/tiff/tiffmodule.cpp ++++ b/core/fxcodec/tiff/tiff_decoder.cpp +@@ -1,23 +1,26 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#include "core/fxcodec/tiff/tiffmodule.h" ++#include "core/fxcodec/tiff/tiff_decoder.h" + + #include + #include + + #include "core/fxcodec/cfx_codec_memory.h" + #include "core/fxcodec/fx_codec.h" ++#include "core/fxcodec/fx_codec_def.h" + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "core/fxge/fx_dib.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/ptr_util.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/check.h" ++#include "third_party/base/notreached.h" ++#include "third_party/base/numerics/safe_conversions.h" + + extern "C" { + #include "third_party/libtiff/tiffiop.h" +@@ -32,7 +35,7 @@ struct TiffDeleter { + + } // namespace + +-class CTiffContext final : public ModuleIface::Context { ++class CTiffContext final : public ProgressiveDecoderIface::Context { + public: + CTiffContext() = default; + ~CTiffContext() override = default; +@@ -116,19 +119,20 @@ tsize_t tiff_read(thandle_t context, tdata_t buf, tsize_t length) { + return 0; + + FX_FILESIZE offset = pTiffContext->offset(); +- if (!pTiffContext->io_in()->ReadBlockAtOffset(buf, offset, length)) ++ if (!pTiffContext->io_in()->ReadBlockAtOffset( ++ {static_cast(buf), static_cast(length)}, offset)) { + return 0; +- ++ } + pTiffContext->set_offset(increment.ValueOrDie()); +- if (offset + length > pTiffContext->io_in()->GetSize()) +- return pTiffContext->io_in()->GetSize() - offset; +- ++ if (offset + length > pTiffContext->io_in()->GetSize()) { ++ return pdfium::base::checked_cast( ++ pTiffContext->io_in()->GetSize() - offset); ++ } + return length; + } + + tsize_t tiff_write(thandle_t context, tdata_t buf, tsize_t length) { +- NOTREACHED(); +- return 0; ++ NOTREACHED_NORETURN(); + } + + toff_t tiff_seek(thandle_t context, toff_t offset, int whence) { +@@ -142,7 +146,8 @@ toff_t tiff_seek(thandle_t context, toff_t offset, int whence) { + case 0: { + if (file_offset > pTiffContext->io_in()->GetSize()) + return static_cast(-1); +- pTiffContext->set_offset(file_offset); ++ pTiffContext->set_offset( ++ pdfium::base::checked_cast(file_offset)); + return pTiffContext->offset(); + } + case 1: { +@@ -156,7 +161,8 @@ toff_t tiff_seek(thandle_t context, toff_t offset, int whence) { + case 2: { + if (pTiffContext->io_in()->GetSize() < file_offset) + return static_cast(-1); +- pTiffContext->set_offset(pTiffContext->io_in()->GetSize() - file_offset); ++ pTiffContext->set_offset(pdfium::base::checked_cast( ++ pTiffContext->io_in()->GetSize() - file_offset)); + return pTiffContext->offset(); + } + default: +@@ -189,32 +195,6 @@ TIFF* tiff_open(void* context, const char* mode) { + return tif; + } + +-template +-bool Tiff_Exif_GetInfo(TIFF* tif_ctx, ttag_t tag, CFX_DIBAttribute* pAttr) { +- T val = 0; +- TIFFGetField(tif_ctx, tag, &val); +- if (!val) +- return false; +- T* ptr = FX_Alloc(T, 1); +- *ptr = val; +- pAttr->m_Exif[tag] = ptr; +- return true; +-} +- +-void Tiff_Exif_GetStringInfo(TIFF* tif_ctx, +- ttag_t tag, +- CFX_DIBAttribute* pAttr) { +- char* buf = nullptr; +- TIFFGetField(tif_ctx, tag, &buf); +- if (!buf) +- return; +- size_t size = strlen(buf); +- uint8_t* ptr = FX_Alloc(uint8_t, size + 1); +- memcpy(ptr, buf, size); +- ptr[size] = 0; +- pAttr->m_Exif[tag] = ptr; +-} +- + void TiffBGRA2RGBA(uint8_t* pBuf, int32_t pixel, int32_t spp) { + for (int32_t n = 0; n < pixel; n++) { + uint8_t tmp = pBuf[0]; +@@ -239,7 +219,7 @@ bool CTiffContext::LoadFrameInfo(int32_t frame, + int32_t* comps, + int32_t* bpc, + CFX_DIBAttribute* pAttribute) { +- if (!TIFFSetDirectory(m_tif_ctx.get(), (uint16)frame)) ++ if (!TIFFSetDirectory(m_tif_ctx.get(), (uint16_t)frame)) + return false; + + uint32_t tif_width = 0; +@@ -253,28 +233,23 @@ bool CTiffContext::LoadFrameInfo(int32_t frame, + TIFFGetField(m_tif_ctx.get(), TIFFTAG_BITSPERSAMPLE, &tif_bpc); + TIFFGetField(m_tif_ctx.get(), TIFFTAG_ROWSPERSTRIP, &tif_rps); + +- pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_INCH; +- if (TIFFGetField(m_tif_ctx.get(), TIFFTAG_RESOLUTIONUNIT, +- &pAttribute->m_wDPIUnit)) { +- pAttribute->m_wDPIUnit--; ++ uint16_t tif_resunit = 0; ++ if (TIFFGetField(m_tif_ctx.get(), TIFFTAG_RESOLUTIONUNIT, &tif_resunit)) { ++ pAttribute->m_wDPIUnit = ++ static_cast(tif_resunit - 1); ++ } else { ++ pAttribute->m_wDPIUnit = CFX_DIBAttribute::kResUnitInch; + } +- Tiff_Exif_GetInfo(m_tif_ctx.get(), TIFFTAG_ORIENTATION, pAttribute); +- if (Tiff_Exif_GetInfo(m_tif_ctx.get(), TIFFTAG_XRESOLUTION, +- pAttribute)) { +- void* val = pAttribute->m_Exif[TIFFTAG_XRESOLUTION]; +- float fDpi = val ? *reinterpret_cast(val) : 0; +- pAttribute->m_nXDPI = (int32_t)(fDpi + 0.5f); +- } +- if (Tiff_Exif_GetInfo(m_tif_ctx.get(), TIFFTAG_YRESOLUTION, +- pAttribute)) { +- void* val = pAttribute->m_Exif[TIFFTAG_YRESOLUTION]; +- float fDpi = val ? *reinterpret_cast(val) : 0; +- pAttribute->m_nYDPI = (int32_t)(fDpi + 0.5f); +- } +- Tiff_Exif_GetStringInfo(m_tif_ctx.get(), TIFFTAG_IMAGEDESCRIPTION, +- pAttribute); +- Tiff_Exif_GetStringInfo(m_tif_ctx.get(), TIFFTAG_MAKE, pAttribute); +- Tiff_Exif_GetStringInfo(m_tif_ctx.get(), TIFFTAG_MODEL, pAttribute); ++ ++ float tif_xdpi = 0.0f; ++ TIFFGetField(m_tif_ctx.get(), TIFFTAG_XRESOLUTION, &tif_xdpi); ++ if (tif_xdpi) ++ pAttribute->m_nXDPI = static_cast(tif_xdpi + 0.5f); ++ ++ float tif_ydpi = 0.0f; ++ TIFFGetField(m_tif_ctx.get(), TIFFTAG_YRESOLUTION, &tif_ydpi); ++ if (tif_ydpi) ++ pAttribute->m_nYDPI = static_cast(tif_ydpi + 0.5f); + + FX_SAFE_INT32 checked_width = tif_width; + FX_SAFE_INT32 checked_height = tif_height; +@@ -330,7 +305,8 @@ void CTiffContext::SetPalette(const RetainPtr& pDIBitmap, + uint16_t* blue_orig = nullptr; + TIFFGetField(m_tif_ctx.get(), TIFFTAG_COLORMAP, &red_orig, &green_orig, + &blue_orig); +- for (int32_t i = (1L << bps) - 1; i >= 0; i--) { ++ for (int32_t i = pdfium::base::checked_cast((1L << bps) - 1); i >= 0; ++ i--) { + #define CVT(x) ((uint16_t)((x) >> 8)) + red_orig[i] = CVT(red_orig[i]); + green_orig[i] = CVT(green_orig[i]); +@@ -343,7 +319,7 @@ void CTiffContext::SetPalette(const RetainPtr& pDIBitmap, + uint32_t g = green_orig[index] & 0xFF; + uint32_t b = blue_orig[index] & 0xFF; + uint32_t color = (uint32_t)b | ((uint32_t)g << 8) | ((uint32_t)r << 16) | +- (((uint32)0xffL) << 24); ++ (((uint32_t)0xffL) << 24); + pDIBitmap->SetPaletteArgb(index, color); + } + } +@@ -358,18 +334,17 @@ bool CTiffContext::Decode1bppRGB(const RetainPtr& pDIBitmap, + return false; + } + SetPalette(pDIBitmap, bps); +- int32_t size = (int32_t)TIFFScanlineSize(m_tif_ctx.get()); ++ int32_t size = static_cast(TIFFScanlineSize(m_tif_ctx.get())); + uint8_t* buf = (uint8_t*)_TIFFmalloc(size); + if (!buf) { + TIFFError(TIFFFileName(m_tif_ctx.get()), "No space for scanline buffer"); + return false; + } +- uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer(); +- uint32_t pitch = pDIBitmap->GetPitch(); + for (int32_t row = 0; row < height; row++) { ++ uint8_t* bitMapbuffer = pDIBitmap->GetWritableScanline(row).data(); + TIFFReadScanline(m_tif_ctx.get(), buf, row, 0); + for (int32_t j = 0; j < size; j++) { +- bitMapbuffer[row * pitch + j] = buf[j]; ++ bitMapbuffer[j] = buf[j]; + } + } + _TIFFfree(buf); +@@ -386,24 +361,23 @@ bool CTiffContext::Decode8bppRGB(const RetainPtr& pDIBitmap, + return false; + } + SetPalette(pDIBitmap, bps); +- int32_t size = (int32_t)TIFFScanlineSize(m_tif_ctx.get()); ++ int32_t size = static_cast(TIFFScanlineSize(m_tif_ctx.get())); + uint8_t* buf = (uint8_t*)_TIFFmalloc(size); + if (!buf) { + TIFFError(TIFFFileName(m_tif_ctx.get()), "No space for scanline buffer"); + return false; + } +- uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer(); +- uint32_t pitch = pDIBitmap->GetPitch(); + for (int32_t row = 0; row < height; row++) { ++ uint8_t* bitMapbuffer = pDIBitmap->GetWritableScanline(row).data(); + TIFFReadScanline(m_tif_ctx.get(), buf, row, 0); + for (int32_t j = 0; j < size; j++) { + switch (bps) { + case 4: +- bitMapbuffer[row * pitch + 2 * j + 0] = (buf[j] & 0xF0) >> 4; +- bitMapbuffer[row * pitch + 2 * j + 1] = (buf[j] & 0x0F) >> 0; ++ bitMapbuffer[2 * j + 0] = (buf[j] & 0xF0) >> 4; ++ bitMapbuffer[2 * j + 1] = (buf[j] & 0x0F) >> 0; + break; + case 8: +- bitMapbuffer[row * pitch + j] = buf[j]; ++ bitMapbuffer[j] = buf[j]; + break; + } + } +@@ -420,20 +394,19 @@ bool CTiffContext::Decode24bppRGB(const RetainPtr& pDIBitmap, + if (pDIBitmap->GetBPP() != 24 || !IsSupport(pDIBitmap)) + return false; + +- int32_t size = (int32_t)TIFFScanlineSize(m_tif_ctx.get()); ++ int32_t size = static_cast(TIFFScanlineSize(m_tif_ctx.get())); + uint8_t* buf = (uint8_t*)_TIFFmalloc(size); + if (!buf) { + TIFFError(TIFFFileName(m_tif_ctx.get()), "No space for scanline buffer"); + return false; + } +- uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer(); +- uint32_t pitch = pDIBitmap->GetPitch(); + for (int32_t row = 0; row < height; row++) { ++ uint8_t* bitMapbuffer = pDIBitmap->GetWritableScanline(row).data(); + TIFFReadScanline(m_tif_ctx.get(), buf, row, 0); + for (int32_t j = 0; j < size - 2; j += 3) { +- bitMapbuffer[row * pitch + j + 0] = buf[j + 2]; +- bitMapbuffer[row * pitch + j + 1] = buf[j + 1]; +- bitMapbuffer[row * pitch + j + 2] = buf[j + 0]; ++ bitMapbuffer[j + 0] = buf[j + 2]; ++ bitMapbuffer[j + 1] = buf[j + 1]; ++ bitMapbuffer[j + 2] = buf[j + 0]; + } + } + _TIFFfree(buf); +@@ -454,10 +427,10 @@ bool CTiffContext::Decode(const RetainPtr& pDIBitmap) { + uint16_t rotation = ORIENTATION_TOPLEFT; + TIFFGetField(m_tif_ctx.get(), TIFFTAG_ORIENTATION, &rotation); + if (TIFFReadRGBAImageOriented(m_tif_ctx.get(), img_width, img_height, +- (uint32*)pDIBitmap->GetBuffer(), rotation, +- 1)) { ++ (uint32_t*)pDIBitmap->GetBuffer().data(), ++ rotation, 1)) { + for (uint32_t row = 0; row < img_height; row++) { +- uint8_t* row_buf = pDIBitmap->GetWritableScanline(row); ++ uint8_t* row_buf = pDIBitmap->GetWritableScanline(row).data(); + TiffBGRA2RGBA(row_buf, img_width, 4); + } + return true; +@@ -483,42 +456,33 @@ bool CTiffContext::Decode(const RetainPtr& pDIBitmap) { + + namespace fxcodec { + +-std::unique_ptr TiffModule::CreateDecoder( ++// static ++std::unique_ptr TiffDecoder::CreateDecoder( + const RetainPtr& file_ptr) { +- auto pDecoder = pdfium::MakeUnique(); ++ auto pDecoder = std::make_unique(); + if (!pDecoder->InitDecoder(file_ptr)) + return nullptr; + + return pDecoder; + } + +-FX_FILESIZE TiffModule::GetAvailInput(Context* pContext) const { +- NOTREACHED(); +- return 0; +-} +- +-bool TiffModule::Input(Context* pContext, +- RetainPtr codec_memory, +- CFX_DIBAttribute*) { +- NOTREACHED(); +- return false; +-} +- +-bool TiffModule::LoadFrameInfo(Context* pContext, +- int32_t frame, +- int32_t* width, +- int32_t* height, +- int32_t* comps, +- int32_t* bpc, +- CFX_DIBAttribute* pAttribute) { +- ASSERT(pAttribute); ++// static ++bool TiffDecoder::LoadFrameInfo(ProgressiveDecoderIface::Context* pContext, ++ int32_t frame, ++ int32_t* width, ++ int32_t* height, ++ int32_t* comps, ++ int32_t* bpc, ++ CFX_DIBAttribute* pAttribute) { ++ DCHECK(pAttribute); + + auto* ctx = static_cast(pContext); + return ctx->LoadFrameInfo(frame, width, height, comps, bpc, pAttribute); + } + +-bool TiffModule::Decode(Context* pContext, +- const RetainPtr& pDIBitmap) { ++// static ++bool TiffDecoder::Decode(ProgressiveDecoderIface::Context* pContext, ++ const RetainPtr& pDIBitmap) { + auto* ctx = static_cast(pContext); + return ctx->Decode(pDIBitmap); + } +diff --git a/core/fxcodec/tiff/tiff_decoder.h b/core/fxcodec/tiff/tiff_decoder.h +new file mode 100644 +index 000000000..8804095ba +--- /dev/null ++++ b/core/fxcodec/tiff/tiff_decoder.h +@@ -0,0 +1,50 @@ ++// Copyright 2016 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCODEC_TIFF_TIFF_DECODER_H_ ++#define CORE_FXCODEC_TIFF_TIFF_DECODER_H_ ++ ++#include ++ ++#include "core/fxcodec/progressive_decoder_iface.h" ++#include "core/fxcrt/retain_ptr.h" ++ ++#ifndef PDF_ENABLE_XFA_TIFF ++#error "TIFF must be enabled" ++#endif ++ ++class CFX_DIBitmap; ++class IFX_SeekableReadStream; ++ ++namespace fxcodec { ++ ++class CFX_DIBAttribute; ++ ++class TiffDecoder { ++ public: ++ static std::unique_ptr CreateDecoder( ++ const RetainPtr& file_ptr); ++ ++ static bool LoadFrameInfo(ProgressiveDecoderIface::Context* ctx, ++ int32_t frame, ++ int32_t* width, ++ int32_t* height, ++ int32_t* comps, ++ int32_t* bpc, ++ CFX_DIBAttribute* pAttribute); ++ static bool Decode(ProgressiveDecoderIface::Context* ctx, ++ const RetainPtr& pDIBitmap); ++ ++ TiffDecoder() = delete; ++ TiffDecoder(const TiffDecoder&) = delete; ++ TiffDecoder& operator=(const TiffDecoder&) = delete; ++}; ++ ++} // namespace fxcodec ++ ++using TiffDecoder = fxcodec::TiffDecoder; ++ ++#endif // CORE_FXCODEC_TIFF_TIFF_DECODER_H_ +diff --git a/core/fxcodec/tiff/tiffmodule.h b/core/fxcodec/tiff/tiffmodule.h +deleted file mode 100644 +index 770973ba3..000000000 +--- a/core/fxcodec/tiff/tiffmodule.h ++++ /dev/null +@@ -1,46 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXCODEC_TIFF_TIFFMODULE_H_ +-#define CORE_FXCODEC_TIFF_TIFFMODULE_H_ +- +-#include +- +-#include "core/fxcodec/codec_module_iface.h" +- +-class CFX_DIBitmap; +-class IFX_SeekableReadStream; +- +-namespace fxcodec { +- +-class CFX_DIBAttribute; +- +-class TiffModule final : public ModuleIface { +- public: +- std::unique_ptr CreateDecoder( +- const RetainPtr& file_ptr); +- +- // ModuleIface: +- FX_FILESIZE GetAvailInput(Context* pContext) const override; +- bool Input(Context* pContext, +- RetainPtr codec_memory, +- CFX_DIBAttribute* pAttribute) override; +- +- bool LoadFrameInfo(Context* ctx, +- int32_t frame, +- int32_t* width, +- int32_t* height, +- int32_t* comps, +- int32_t* bpc, +- CFX_DIBAttribute* pAttribute); +- bool Decode(Context* ctx, const RetainPtr& pDIBitmap); +-}; +- +-} // namespace fxcodec +- +-using TiffModule = fxcodec::TiffModule; +- +-#endif // CORE_FXCODEC_TIFF_TIFFMODULE_H_ +diff --git a/core/fxcrt/Android.bp b/core/fxcrt/Android.bp +index 5f6cb4105..949a0d950 100644 +--- a/core/fxcrt/Android.bp ++++ b/core/fxcrt/Android.bp +@@ -11,7 +11,7 @@ cc_library_static { + name: "libpdfium-fxcrt", + defaults: ["pdfium-core"], + +- visibility: ["//external/pdfium:__subpackages__"], ++ visibility: ["//external/pdfium:__subpackages__", "//cts/hostsidetests/securitybulletin/securityPatch/CVE-2016-8332"], + + export_shared_lib_headers: [ + "libicu", +@@ -28,8 +28,13 @@ cc_library_static { + exclude_srcs: [ + // is_win + "cfx_fileaccess_windows.cpp", ++ "fx_folder_windows.cpp", + // pdf_enable_xfa + "cfx_memorystream.cpp", ++ // pdf_use_partition_alloc ++ "fx_memory_pa.cpp", ++ // test module ++ "fake_time_test.cpp", + ], + + srcs: [ +diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn +index 1512f97ed..09400e881 100644 +--- a/core/fxcrt/BUILD.gn ++++ b/core/fxcrt/BUILD.gn +@@ -1,25 +1,35 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + + import("../../pdfium.gni") + import("../../testing/test.gni") + ++source_set("unowned_ptr") { ++ sources = [ "unowned_ptr.h" ] ++ deps = [ "../../third_party:pdfium_compiler_specific" ] ++ configs += [ "../../:pdfium_strict_config" ] ++} ++ + source_set("fxcrt") { + sources = [ ++ "autonuller.h", + "autorestorer.h", ++ "binary_buffer.cpp", ++ "binary_buffer.h", + "byteorder.h", + "bytestring.cpp", + "bytestring.h", +- "cfx_binarybuf.cpp", +- "cfx_binarybuf.h", + "cfx_bitstream.cpp", + "cfx_bitstream.h", + "cfx_datetime.cpp", + "cfx_datetime.h", +- "cfx_fixedbufgrow.h", +- "cfx_readonlymemorystream.cpp", +- "cfx_readonlymemorystream.h", ++ "cfx_read_only_span_stream.cpp", ++ "cfx_read_only_span_stream.h", ++ "cfx_read_only_string_stream.cpp", ++ "cfx_read_only_string_stream.h", ++ "cfx_read_only_vector_stream.cpp", ++ "cfx_read_only_vector_stream.h", + "cfx_seekablestreamproxy.cpp", + "cfx_seekablestreamproxy.h", + "cfx_timer.cpp", +@@ -28,17 +38,23 @@ source_set("fxcrt") { + "cfx_utf8decoder.h", + "cfx_utf8encoder.cpp", + "cfx_utf8encoder.h", +- "cfx_widetextbuf.cpp", +- "cfx_widetextbuf.h", ++ "data_vector.h", + "fileaccess_iface.h", ++ "fixed_size_data_vector.h", ++ "fixed_try_alloc_zeroed_data_vector.h", ++ "fixed_uninit_data_vector.h", ++ "fixed_zeroed_data_vector.h", ++ "fx_2d_size.h", + "fx_bidi.cpp", + "fx_bidi.h", + "fx_codepage.cpp", + "fx_codepage.h", ++ "fx_codepage_forward.h", + "fx_coordinates.cpp", + "fx_coordinates.h", + "fx_extension.cpp", + "fx_extension.h", ++ "fx_folder.h", + "fx_memory.cpp", + "fx_memory.h", + "fx_memory_wrappers.h", +@@ -51,26 +67,33 @@ source_set("fxcrt") { + "fx_stream.h", + "fx_string.cpp", + "fx_string.h", ++ "fx_string_wrappers.h", + "fx_system.cpp", + "fx_system.h", ++ "fx_types.h", + "fx_unicode.cpp", + "fx_unicode.h", ++ "mask.h", + "maybe_owned.h", + "observed_ptr.cpp", + "observed_ptr.h", + "pauseindicator_iface.h", + "retain_ptr.h", +- "retained_tree_node.h", ++ "scoped_set_insertion.h", + "shared_copy_on_write.h", ++ "small_buffer.h", ++ "span_util.h", ++ "stl_util.h", ++ "string_data_template.cpp", + "string_data_template.h", + "string_pool_template.h", + "string_view_template.h", +- "timerhandler_iface.h", + "tree_node.h", +- "unowned_ptr.h", + "weak_ptr.h", + "widestring.cpp", + "widestring.h", ++ "widetext_buffer.cpp", ++ "widetext_buffer.h", + "xml/cfx_xmlchardata.cpp", + "xml/cfx_xmlchardata.h", + "xml/cfx_xmldocument.cpp", +@@ -86,7 +109,10 @@ source_set("fxcrt") { + "xml/cfx_xmltext.cpp", + "xml/cfx_xmltext.h", + ] +- configs += [ "../../:pdfium_core_config" ] ++ configs += [ ++ "../../:pdfium_strict_config", ++ "../../:pdfium_noshorten_config", ++ ] + visibility = [ + "../*", + "../../:*", +@@ -97,28 +123,35 @@ source_set("fxcrt") { + "../../testing/fuzzers/*", + "../../third_party:fx_agg", + "../../third_party:fx_lcms2", ++ "../../third_party:fx_libopenjpeg", + "../../third_party:fx_tiff", +- "../../third_party:pdfium_base", + "../../xfa/*", + ] + deps = [ "../../third_party:pdfium_base" ] + public_deps = [ ++ ":unowned_ptr", + "../../:freetype_common", + "../../third_party:pdfium_base", + "//third_party/icu:icuuc", + ] +- allow_circular_includes_from = [ "../../third_party:pdfium_base" ] +- ++ if (pdf_use_partition_alloc) { ++ sources += [ "fx_memory_pa.cpp" ] ++ deps += [ "//base/allocator/partition_allocator:partition_alloc" ] ++ } else { ++ sources += [ "fx_memory_malloc.cpp" ] ++ } + if (is_posix || is_fuchsia) { + sources += [ + "cfx_fileaccess_posix.cpp", + "cfx_fileaccess_posix.h", ++ "fx_folder_posix.cpp", + ] + } + if (is_win) { + sources += [ + "cfx_fileaccess_windows.cpp", + "cfx_fileaccess_windows.h", ++ "fx_folder_windows.cpp", + ] + } + if (pdf_enable_xfa) { +@@ -129,15 +162,33 @@ source_set("fxcrt") { + } + } + ++source_set("unit_test_support") { ++ testonly = true ++ sources = [ ++ "fake_time_test.cpp", ++ "fake_time_test.h", ++ ] ++ configs += [ "../../:pdfium_strict_config" ] ++ deps = [ ++ ":fxcrt", ++ "//testing/gtest", ++ ] ++} ++ + pdfium_unittest_source_set("unittests") { + sources = [ ++ "autonuller_unittest.cpp", + "autorestorer_unittest.cpp", ++ "binary_buffer_unittest.cpp", + "byteorder_unittest.cpp", + "bytestring_unittest.cpp", + "cfx_bitstream_unittest.cpp", ++ "cfx_datetime_unittest.cpp", + "cfx_seekablestreamproxy_unittest.cpp", + "cfx_timer_unittest.cpp", +- "cfx_widetextbuf_unittest.cpp", ++ "fixed_try_alloc_zeroed_data_vector_unittest.cpp", ++ "fixed_uninit_data_vector_unittest.cpp", ++ "fixed_zeroed_data_vector_unittest.cpp", + "fx_bidi_unittest.cpp", + "fx_coordinates_unittest.cpp", + "fx_extension_unittest.cpp", +@@ -145,19 +196,25 @@ pdfium_unittest_source_set("unittests") { + "fx_memory_wrappers_unittest.cpp", + "fx_number_unittest.cpp", + "fx_random_unittest.cpp", ++ "fx_safe_types_unittest.cpp", + "fx_string_unittest.cpp", ++ "fx_string_wrappers_unittest.cpp", + "fx_system_unittest.cpp", ++ "mask_unittest.cpp", + "maybe_owned_unittest.cpp", + "observed_ptr_unittest.cpp", + "pdfium_span_unittest.cpp", + "retain_ptr_unittest.cpp", +- "retained_tree_node_unittest.cpp", ++ "scoped_set_insertion_unittest.cpp", + "shared_copy_on_write_unittest.cpp", ++ "small_buffer_unittest.cpp", ++ "span_util_unittest.cpp", + "string_pool_template_unittest.cpp", + "tree_node_unittest.cpp", + "unowned_ptr_unittest.cpp", + "weak_ptr_unittest.cpp", + "widestring_unittest.cpp", ++ "widetext_buffer_unittest.cpp", + "xml/cfx_xmlchardata_unittest.cpp", + "xml/cfx_xmldocument_unittest.cpp", + "xml/cfx_xmlelement_unittest.cpp", +@@ -166,19 +223,11 @@ pdfium_unittest_source_set("unittests") { + "xml/cfx_xmlparser_unittest.cpp", + "xml/cfx_xmltext_unittest.cpp", + ] +- deps = [] ++ deps = [ ":unit_test_support" ] + pdfium_root_dir = "../../" + + if (pdf_enable_xfa) { +- sources += [ +- "cfx_memorystream_unittest.cpp", +- "css/cfx_cssdeclaration_unittest.cpp", +- "css/cfx_cssstylesheet_unittest.cpp", +- "css/cfx_cssvaluelistparser_unittest.cpp", +- ] +- deps += [ +- "../fpdfapi/parser", +- "css", +- ] ++ sources += [ "cfx_memorystream_unittest.cpp" ] ++ deps += [ "../fpdfapi/parser" ] + } + } +diff --git a/core/fxcrt/DEPS b/core/fxcrt/DEPS +index 2be03524b..5b61cbd44 100644 +--- a/core/fxcrt/DEPS ++++ b/core/fxcrt/DEPS +@@ -1,3 +1,4 @@ + include_rules = [ ++ '+base/allocator', + '+third_party/icu', + ] +diff --git a/core/fxcrt/autonuller.h b/core/fxcrt/autonuller.h +new file mode 100644 +index 000000000..12df63fe7 +--- /dev/null ++++ b/core/fxcrt/autonuller.h +@@ -0,0 +1,32 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXCRT_AUTONULLER_H_ ++#define CORE_FXCRT_AUTONULLER_H_ ++ ++#include "core/fxcrt/fx_memory.h" ++ ++namespace fxcrt { ++ ++template ++class AutoNuller { ++ public: ++ FX_STACK_ALLOCATED(); ++ ++ explicit AutoNuller(T* location) : m_Location(location) {} ++ ~AutoNuller() { ++ if (m_Location) ++ *m_Location = nullptr; ++ } ++ void AbandonNullification() { m_Location = nullptr; } ++ ++ private: ++ T* m_Location; ++}; ++ ++} // namespace fxcrt ++ ++using fxcrt::AutoNuller; ++ ++#endif // CORE_FXCRT_AUTONULLER_H_ +diff --git a/core/fxcrt/autonuller_unittest.cpp b/core/fxcrt/autonuller_unittest.cpp +new file mode 100644 +index 000000000..929fa6064 +--- /dev/null ++++ b/core/fxcrt/autonuller_unittest.cpp +@@ -0,0 +1,53 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/autonuller.h" ++#include "core/fxcrt/unowned_ptr.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++TEST(fxcrt, AutoNuller) { ++ int x = 5; ++ int* ptr; ++ { ++ AutoNuller nuller(&ptr); ++ ptr = &x; ++ EXPECT_EQ(&x, ptr); ++ } ++ EXPECT_FALSE(ptr); ++} ++ ++TEST(fxcrt, AutoNullerAbandon) { ++ int x = 5; ++ int* ptr; ++ { ++ AutoNuller nuller(&ptr); ++ ptr = &x; ++ EXPECT_EQ(&x, ptr); ++ nuller.AbandonNullification(); ++ } ++ EXPECT_EQ(&x, ptr); ++} ++ ++TEST(fxcrt, AutoNullerUnownedPtr) { ++ int x = 5; ++ UnownedPtr ptr; ++ { ++ AutoNuller> nuller(&ptr); ++ ptr = &x; ++ EXPECT_EQ(&x, ptr); ++ } ++ EXPECT_FALSE(ptr); ++} ++ ++TEST(fxcrt, AutoNullerUnownedPtrAbandon) { ++ int x = 5; ++ UnownedPtr ptr; ++ { ++ AutoNuller> nuller(&ptr); ++ ptr = &x; ++ EXPECT_EQ(&x, ptr); ++ nuller.AbandonNullification(); ++ } ++ EXPECT_EQ(&x, ptr); ++} +diff --git a/core/fxcrt/autorestorer.h b/core/fxcrt/autorestorer.h +index cafa07513..02b5f3da0 100644 +--- a/core/fxcrt/autorestorer.h ++++ b/core/fxcrt/autorestorer.h +@@ -1,15 +1,19 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #ifndef CORE_FXCRT_AUTORESTORER_H_ + #define CORE_FXCRT_AUTORESTORER_H_ + ++#include "core/fxcrt/fx_memory.h" ++ + namespace fxcrt { + + template + class AutoRestorer { + public: ++ FX_STACK_ALLOCATED(); ++ + explicit AutoRestorer(T* location) + : m_Location(location), m_OldValue(*location) {} + ~AutoRestorer() { +diff --git a/core/fxcrt/autorestorer_unittest.cpp b/core/fxcrt/autorestorer_unittest.cpp +index 6430fb67f..b7ee576af 100644 +--- a/core/fxcrt/autorestorer_unittest.cpp ++++ b/core/fxcrt/autorestorer_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcrt/binary_buffer.cpp b/core/fxcrt/binary_buffer.cpp +new file mode 100644 +index 000000000..341d4c024 +--- /dev/null ++++ b/core/fxcrt/binary_buffer.cpp +@@ -0,0 +1,122 @@ ++// Copyright 2017 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxcrt/binary_buffer.h" ++ ++#include ++#include ++ ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/span_util.h" ++ ++namespace fxcrt { ++ ++BinaryBuffer::BinaryBuffer() = default; ++ ++BinaryBuffer::BinaryBuffer(BinaryBuffer&& that) noexcept ++ : m_AllocStep(that.m_AllocStep), ++ m_DataSize(that.m_DataSize), ++ m_buffer(std::move(that.m_buffer)) { ++ // Can't just default, need to leave |that| in a valid state, which means ++ // that the size members reflect the (null) moved-from buffer. ++ that.m_AllocStep = 0; ++ that.m_DataSize = 0; ++} ++ ++BinaryBuffer::~BinaryBuffer() = default; ++ ++BinaryBuffer& BinaryBuffer::operator=(BinaryBuffer&& that) noexcept { ++ // Can't just default, need to leave |that| in a valid state, which means ++ // that the size members reflect the (null) moved-from buffer. ++ m_AllocStep = that.m_AllocStep; ++ m_DataSize = that.m_DataSize; ++ m_buffer = std::move(that.m_buffer); ++ that.m_AllocStep = 0; ++ that.m_DataSize = 0; ++ return *this; ++} ++ ++void BinaryBuffer::DeleteBuf(size_t start_index, size_t count) { ++ if (m_buffer.empty() || count > GetSize() || start_index > GetSize() - count) ++ return; ++ ++ auto buffer_span = pdfium::make_span(m_buffer).first(GetSize()); ++ fxcrt::spanmove(buffer_span.subspan(start_index), ++ buffer_span.subspan(start_index + count)); ++ m_DataSize -= count; ++} ++ ++pdfium::span BinaryBuffer::GetMutableSpan() { ++ return {m_buffer.data(), GetSize()}; ++} ++ ++pdfium::span BinaryBuffer::GetSpan() const { ++ return {m_buffer.data(), GetSize()}; ++} ++ ++size_t BinaryBuffer::GetLength() const { ++ return GetSize(); ++} ++ ++void BinaryBuffer::Clear() { ++ m_DataSize = 0; ++} ++ ++DataVector BinaryBuffer::DetachBuffer() { ++ m_buffer.resize(GetSize()); ++ m_DataSize = 0; ++ return std::move(m_buffer); ++} ++ ++void BinaryBuffer::EstimateSize(size_t size) { ++ if (m_buffer.size() < size) ++ ExpandBuf(size - GetSize()); ++} ++ ++void BinaryBuffer::ExpandBuf(size_t add_size) { ++ FX_SAFE_SIZE_T new_size = GetSize(); ++ new_size += add_size; ++ if (m_buffer.size() >= new_size.ValueOrDie()) ++ return; ++ ++ size_t alloc_step = std::max(static_cast(128), ++ m_AllocStep ? m_AllocStep : m_buffer.size() / 4); ++ new_size += alloc_step - 1; // Quantize, don't combine these lines. ++ new_size /= alloc_step; ++ new_size *= alloc_step; ++ m_buffer.resize(new_size.ValueOrDie()); ++} ++ ++void BinaryBuffer::AppendSpan(pdfium::span span) { ++ if (span.empty()) ++ return; ++ ++ ExpandBuf(span.size()); ++ fxcrt::spancpy(pdfium::make_span(m_buffer).subspan(GetSize()), span); ++ m_DataSize += span.size(); ++} ++ ++void BinaryBuffer::AppendString(const ByteString& str) { ++ AppendSpan(str.raw_span()); ++} ++ ++void BinaryBuffer::AppendUint8(uint8_t value) { ++ AppendSpan({&value, 1}); ++} ++ ++void BinaryBuffer::AppendUint16(uint16_t value) { ++ AppendSpan({reinterpret_cast(&value), sizeof(value)}); ++} ++ ++void BinaryBuffer::AppendUint32(uint32_t value) { ++ AppendSpan({reinterpret_cast(&value), sizeof(value)}); ++} ++ ++void BinaryBuffer::AppendDouble(double value) { ++ AppendSpan({reinterpret_cast(&value), sizeof(value)}); ++} ++ ++} // namespace fxcrt +diff --git a/core/fxcrt/binary_buffer.h b/core/fxcrt/binary_buffer.h +new file mode 100644 +index 000000000..9150266ce +--- /dev/null ++++ b/core/fxcrt/binary_buffer.h +@@ -0,0 +1,63 @@ ++// Copyright 2017 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCRT_BINARY_BUFFER_H_ ++#define CORE_FXCRT_BINARY_BUFFER_H_ ++ ++#include ++#include ++ ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/data_vector.h" ++#include "third_party/base/span.h" ++ ++namespace fxcrt { ++ ++class BinaryBuffer { ++ public: ++ BinaryBuffer(); ++ BinaryBuffer(BinaryBuffer&& that) noexcept; ++ BinaryBuffer(const BinaryBuffer& that) = delete; ++ virtual ~BinaryBuffer(); ++ ++ // Moved-from value will be left empty. ++ BinaryBuffer& operator=(BinaryBuffer&& that) noexcept; ++ ++ BinaryBuffer& operator=(const BinaryBuffer& that) = delete; ++ ++ pdfium::span GetMutableSpan(); ++ pdfium::span GetSpan() const; ++ bool IsEmpty() const { return GetLength() == 0; } ++ size_t GetSize() const { return m_DataSize; } // In bytes. ++ virtual size_t GetLength() const; // In subclass-specific units. ++ ++ void Clear(); ++ void SetAllocStep(size_t step) { m_AllocStep = step; } ++ void EstimateSize(size_t size); ++ void AppendSpan(pdfium::span span); ++ void AppendString(const ByteString& str); ++ void AppendUint8(uint8_t value); ++ void AppendUint16(uint16_t value); ++ void AppendUint32(uint32_t value); ++ void AppendDouble(double value); ++ ++ // Releases ownership of `m_pBuffer` and returns it. ++ DataVector DetachBuffer(); ++ ++ protected: ++ void ExpandBuf(size_t size); ++ void DeleteBuf(size_t start_index, size_t count); ++ ++ size_t m_AllocStep = 0; ++ size_t m_DataSize = 0; ++ DataVector m_buffer; ++}; ++ ++} // namespace fxcrt ++ ++using fxcrt::BinaryBuffer; ++ ++#endif // CORE_FXCRT_BINARY_BUFFER_H_ +diff --git a/core/fxcrt/binary_buffer_unittest.cpp b/core/fxcrt/binary_buffer_unittest.cpp +new file mode 100644 +index 000000000..30a7e2411 +--- /dev/null ++++ b/core/fxcrt/binary_buffer_unittest.cpp +@@ -0,0 +1,152 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/binary_buffer.h" ++ ++#include ++#include ++ ++#include "core/fxcrt/bytestring.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++namespace fxcrt { ++ ++TEST(BinaryBuffer, Empty) { ++ BinaryBuffer buffer; ++ EXPECT_TRUE(buffer.IsEmpty()); ++ EXPECT_EQ(0u, buffer.GetSize()); ++ EXPECT_EQ(0u, buffer.GetLength()); ++ EXPECT_TRUE(buffer.GetSpan().empty()); ++} ++ ++TEST(BinaryBuffer, MoveConstruct) { ++ BinaryBuffer buffer; ++ buffer.AppendUint8(65u); ++ ++ BinaryBuffer buffer2(std::move(buffer)); ++ ++ EXPECT_TRUE(buffer.IsEmpty()); ++ EXPECT_EQ(0u, buffer.GetSize()); ++ EXPECT_EQ(0u, buffer.GetLength()); ++ EXPECT_TRUE(buffer.GetSpan().empty()); ++ ++ EXPECT_FALSE(buffer2.IsEmpty()); ++ EXPECT_EQ(1u, buffer2.GetSize()); ++ EXPECT_EQ(1u, buffer2.GetLength()); ++ EXPECT_EQ(65u, buffer2.GetSpan()[0]); ++} ++ ++TEST(BinaryBuffer, MoveAssign) { ++ BinaryBuffer buffer; ++ BinaryBuffer buffer2; ++ buffer.AppendUint8(65u); ++ buffer2 = std::move(buffer); ++ ++ EXPECT_TRUE(buffer.IsEmpty()); ++ EXPECT_EQ(0u, buffer.GetSize()); ++ EXPECT_EQ(0u, buffer.GetLength()); ++ EXPECT_TRUE(buffer.GetSpan().empty()); ++ ++ EXPECT_FALSE(buffer2.IsEmpty()); ++ EXPECT_EQ(1u, buffer2.GetSize()); ++ ASSERT_EQ(1u, buffer2.GetLength()); ++ EXPECT_EQ(65u, buffer2.GetSpan()[0]); ++} ++ ++TEST(BinaryBuffer, Clear) { ++ BinaryBuffer buffer; ++ buffer.AppendUint8(65u); ++ buffer.Clear(); ++ EXPECT_TRUE(buffer.IsEmpty()); ++ EXPECT_EQ(0u, buffer.GetSize()); ++ EXPECT_EQ(0u, buffer.GetLength()); ++ EXPECT_TRUE(buffer.GetSpan().empty()); ++} ++ ++TEST(BinaryBuffer, AppendSpans) { ++ BinaryBuffer buffer; ++ std::vector aaa(3, 65u); ++ std::vector bbb(3, 66u); ++ buffer.AppendSpan(aaa); ++ buffer.AppendSpan(bbb); ++ EXPECT_FALSE(buffer.IsEmpty()); ++ EXPECT_EQ(6u, buffer.GetSize()); ++ EXPECT_EQ(6u, buffer.GetLength()); ++ EXPECT_EQ(65u, buffer.GetSpan()[0]); ++ EXPECT_EQ(65u, buffer.GetSpan()[1]); ++ EXPECT_EQ(65u, buffer.GetSpan()[2]); ++ EXPECT_EQ(66u, buffer.GetSpan()[3]); ++ EXPECT_EQ(66u, buffer.GetSpan()[4]); ++ EXPECT_EQ(66u, buffer.GetSpan()[5]); ++} ++ ++TEST(BinaryBuffer, AppendBlocks) { ++ BinaryBuffer buffer; ++ std::vector aaa(3, 65u); ++ std::vector bbb(3, 66u); ++ buffer.AppendSpan(aaa); ++ buffer.AppendSpan(bbb); ++ EXPECT_EQ(6u, buffer.GetSize()); ++ EXPECT_EQ(6u, buffer.GetLength()); ++ EXPECT_EQ(65u, buffer.GetSpan()[0]); ++ EXPECT_EQ(65u, buffer.GetSpan()[1]); ++ EXPECT_EQ(65u, buffer.GetSpan()[2]); ++ EXPECT_EQ(66u, buffer.GetSpan()[3]); ++ EXPECT_EQ(66u, buffer.GetSpan()[4]); ++ EXPECT_EQ(66u, buffer.GetSpan()[5]); ++} ++ ++TEST(BinaryBuffer, AppendStrings) { ++ BinaryBuffer buffer; ++ buffer.AppendString("AA"); ++ buffer.AppendString("BB"); ++ EXPECT_EQ(4u, buffer.GetSize()); ++ EXPECT_EQ(4u, buffer.GetLength()); ++ EXPECT_EQ(65u, buffer.GetSpan()[0]); ++ EXPECT_EQ(65u, buffer.GetSpan()[1]); ++ EXPECT_EQ(66u, buffer.GetSpan()[2]); ++ EXPECT_EQ(66u, buffer.GetSpan()[3]); ++} ++ ++TEST(BinaryBuffer, AppendBytes) { ++ BinaryBuffer buffer; ++ buffer.AppendUint8(65u); ++ buffer.AppendUint8(66u); ++ EXPECT_EQ(2u, buffer.GetSize()); ++ EXPECT_EQ(2u, buffer.GetLength()); ++ EXPECT_EQ(65u, buffer.GetSpan()[0]); ++ EXPECT_EQ(66u, buffer.GetSpan()[1]); ++} ++ ++// Assumes little endian. ++TEST(BinaryBuffer, AppendUint16) { ++ BinaryBuffer buffer; ++ buffer.AppendUint16(0x4321); ++ EXPECT_EQ(2u, buffer.GetSize()); ++ EXPECT_EQ(2u, buffer.GetLength()); ++ EXPECT_EQ(0x21u, buffer.GetSpan()[0]); ++ EXPECT_EQ(0x43u, buffer.GetSpan()[1]); ++} ++ ++// Assumes little endian. ++TEST(BinaryBuffer, AppendUint32) { ++ BinaryBuffer buffer; ++ buffer.AppendUint32(0x87654321); ++ EXPECT_EQ(4u, buffer.GetSize()); ++ EXPECT_EQ(4u, buffer.GetLength()); ++ EXPECT_EQ(0x21u, buffer.GetSpan()[0]); ++ EXPECT_EQ(0x43u, buffer.GetSpan()[1]); ++ EXPECT_EQ(0x65u, buffer.GetSpan()[2]); ++ EXPECT_EQ(0x87u, buffer.GetSpan()[3]); ++} ++ ++TEST(BinaryBuffer, AppendDouble) { ++ BinaryBuffer buffer; ++ buffer.AppendDouble(1234.5678); ++ EXPECT_EQ(8u, buffer.GetSize()); ++ EXPECT_EQ(8u, buffer.GetLength()); ++ // arch-dependent bit pattern. ++} ++ ++} // namespace fxcrt +diff --git a/core/fxcrt/byteorder.h b/core/fxcrt/byteorder.h +index 283ac4ec0..b6c7a6420 100644 +--- a/core/fxcrt/byteorder.h ++++ b/core/fxcrt/byteorder.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 The PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcrt/byteorder_unittest.cpp b/core/fxcrt/byteorder_unittest.cpp +index 20f7a1255..6688934fd 100644 +--- a/core/fxcrt/byteorder_unittest.cpp ++++ b/core/fxcrt/byteorder_unittest.cpp +@@ -1,28 +1,14 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcrt/byteorder.h" + ++#include "core/fxcrt/fx_system.h" + #include "testing/gtest/include/gtest/gtest.h" + + namespace { + +-// Original code to use as a reference implementation. +- +-#define FXWORD_GET_LSBFIRST(p) \ +- (static_cast((static_cast(p[1]) << 8) | \ +- (static_cast(p[0])))) +-#define FXWORD_GET_MSBFIRST(p) \ +- (static_cast((static_cast(p[0]) << 8) | \ +- (static_cast(p[1])))) +-#define FXDWORD_GET_LSBFIRST(p) \ +- ((static_cast(p[3]) << 24) | (static_cast(p[2]) << 16) | \ +- (static_cast(p[1]) << 8) | (static_cast(p[0]))) +-#define FXDWORD_GET_MSBFIRST(p) \ +- ((static_cast(p[0]) << 24) | (static_cast(p[1]) << 16) | \ +- (static_cast(p[2]) << 8) | (static_cast(p[3]))) +- + constexpr uint32_t kTestValues32[] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0xfe, + 0xff, 0x100, 0x101, 0xffff, 0x10000, 0x123456, +@@ -37,7 +23,11 @@ TEST(ByteOrder, ByteSwapToLE16) { + for (uint32_t v = 0; v < 0x10000; ++v) { + const uint16_t v16 = v; + uint16_t expected = +- FXWORD_GET_LSBFIRST(reinterpret_cast(&v16)); ++ FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast(&v16)); ++ EXPECT_EQ(expected, ByteSwapToLE16(v16)) << v; ++ ++ // Check safety against unexpectedly signed bytes. ++ expected = FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast(&v16)); + EXPECT_EQ(expected, ByteSwapToLE16(v16)) << v; + } + } +@@ -45,7 +35,11 @@ TEST(ByteOrder, ByteSwapToLE16) { + TEST(ByteOrder, ByteSwapToLE32) { + for (uint32_t v : kTestValues32) { + uint32_t expected = +- FXDWORD_GET_LSBFIRST(reinterpret_cast(&v)); ++ FXSYS_UINT32_GET_LSBFIRST(reinterpret_cast(&v)); ++ EXPECT_EQ(expected, ByteSwapToLE32(v)) << v; ++ ++ // Check safety against unexpectedly signed bytes. ++ expected = FXSYS_UINT32_GET_LSBFIRST(reinterpret_cast(&v)); + EXPECT_EQ(expected, ByteSwapToLE32(v)) << v; + } + } +@@ -55,7 +49,11 @@ TEST(ByteOrder, ByteSwapToBE16) { + for (uint32_t v = 0; v < 0x10000; ++v) { + const uint16_t v16 = v; + uint16_t expected = +- FXWORD_GET_MSBFIRST(reinterpret_cast(&v16)); ++ FXSYS_UINT16_GET_MSBFIRST(reinterpret_cast(&v16)); ++ EXPECT_EQ(expected, ByteSwapToBE16(v16)) << v; ++ ++ // Check safety against unexpectedly signed bytes. ++ expected = FXSYS_UINT16_GET_MSBFIRST(reinterpret_cast(&v16)); + EXPECT_EQ(expected, ByteSwapToBE16(v16)) << v; + } + } +@@ -63,7 +61,11 @@ TEST(ByteOrder, ByteSwapToBE16) { + TEST(ByteOrder, ByteSwapToBE32) { + for (uint32_t v : kTestValues32) { + uint32_t expected = +- FXDWORD_GET_MSBFIRST(reinterpret_cast(&v)); ++ FXSYS_UINT32_GET_MSBFIRST(reinterpret_cast(&v)); ++ EXPECT_EQ(expected, ByteSwapToBE32(v)) << v; ++ ++ // Check safety against unexpectedly signed bytes. ++ expected = FXSYS_UINT32_GET_MSBFIRST(reinterpret_cast(&v)); + EXPECT_EQ(expected, ByteSwapToBE32(v)) << v; + } + } +diff --git a/core/fxcrt/bytestring.cpp b/core/fxcrt/bytestring.cpp +index 0778bc260..d3e1cc043 100644 +--- a/core/fxcrt/bytestring.cpp ++++ b/core/fxcrt/bytestring.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,10 +6,11 @@ + + #include "core/fxcrt/bytestring.h" + ++#include + #include + + #include +-#include ++#include + #include + #include + +@@ -17,10 +18,13 @@ + #include "core/fxcrt/fx_codepage.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/string_pool_template.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/cxx17_backports.h" + #include "third_party/base/numerics/safe_math.h" + #include "third_party/base/span.h" +-#include "third_party/base/stl_util.h" + + template class fxcrt::StringDataTemplate; + template class fxcrt::StringViewTemplate; +@@ -32,23 +36,22 @@ namespace { + constexpr char kTrimChars[] = "\x09\x0a\x0b\x0c\x0d\x20"; + + const char* FX_strstr(const char* haystack, +- int haystack_len, ++ size_t haystack_len, + const char* needle, +- int needle_len) { +- if (needle_len > haystack_len || needle_len == 0) { ++ size_t needle_len) { ++ if (needle_len > haystack_len || needle_len == 0) + return nullptr; +- } ++ + const char* end_ptr = haystack + haystack_len - needle_len; + while (haystack <= end_ptr) { +- int i = 0; +- while (1) { +- if (haystack[i] != needle[i]) { ++ size_t i = 0; ++ while (true) { ++ if (haystack[i] != needle[i]) + break; +- } ++ + i++; +- if (i == needle_len) { ++ if (i == needle_len) + return haystack; +- } + } + haystack++; + } +@@ -62,10 +65,6 @@ namespace fxcrt { + static_assert(sizeof(ByteString) <= sizeof(char*), + "Strings must not require more space than pointers"); + +-#define FORCE_ANSI 0x10000 +-#define FORCE_UNICODE 0x20000 +-#define FORCE_INT64 0x40000 +- + // static + ByteString ByteString::FormatInteger(int i) { + char buf[32]; +@@ -182,13 +181,21 @@ ByteString::ByteString(const std::initializer_list& list) { + } + } + +-ByteString::ByteString(const std::ostringstream& outStream) { +- std::string str = outStream.str(); +- if (str.length() > 0) +- m_pData.Reset(StringData::Create(str.c_str(), str.length())); ++ByteString::ByteString(const fxcrt::ostringstream& outStream) { ++ auto str = outStream.str(); ++ if (!str.empty()) ++ m_pData.Reset(StringData::Create(str.c_str(), str.size())); + } + +-ByteString::~ByteString() {} ++ByteString::~ByteString() = default; ++ ++void ByteString::clear() { ++ if (m_pData && m_pData->CanOperateInPlace(0)) { ++ m_pData->m_nDataLength = 0; ++ return; ++ } ++ m_pData.Reset(); ++} + + ByteString& ByteString::operator=(const char* str) { + if (!str || !str[0]) +@@ -215,7 +222,7 @@ ByteString& ByteString::operator=(const ByteString& that) { + return *this; + } + +-ByteString& ByteString::operator=(ByteString&& that) { ++ByteString& ByteString::operator=(ByteString&& that) noexcept { + if (m_pData != that.m_pData) + m_pData = std::move(that.m_pData); + +@@ -381,7 +388,7 @@ void ByteString::ReleaseBuffer(size_t nNewLength) { + return; + } + +- ASSERT(m_pData->m_nRefs == 1); ++ DCHECK_EQ(m_pData->m_nRefs, 1); + m_pData->m_nDataLength = nNewLength; + m_pData->m_String[nNewLength] = 0; + if (m_pData->m_nAllocLength - nNewLength >= 32) { +@@ -469,6 +476,11 @@ intptr_t ByteString::ReferenceCountForTesting() const { + return m_pData ? m_pData->m_nRefs : 0; + } + ++ByteString ByteString::Substr(size_t offset) const { ++ // Unsigned underflow is well-defined and out-of-range is handled by Substr(). ++ return Substr(offset, GetLength() - offset); ++} ++ + ByteString ByteString::Substr(size_t first, size_t count) const { + if (!m_pData) + return ByteString(); +@@ -491,14 +503,11 @@ ByteString ByteString::Substr(size_t first, size_t count) const { + } + + ByteString ByteString::First(size_t count) const { +- if (count == 0 || !IsValidLength(count)) +- return ByteString(); + return Substr(0, count); + } + + ByteString ByteString::Last(size_t count) const { +- if (count == 0 || !IsValidLength(count)) +- return ByteString(); ++ // Unsigned underflow is well-defined and out-of-range is handled by Substr(). + return Substr(GetLength() - count, count); + } + +@@ -514,7 +523,7 @@ void ByteString::AllocCopy(ByteString& dest, + } + + void ByteString::SetAt(size_t index, char c) { +- ASSERT(IsValidIndex(index)); ++ DCHECK(IsValidIndex(index)); + ReallocBeforeWrite(m_pData->m_nDataLength); + m_pData->m_String[index] = c; + } +@@ -533,47 +542,50 @@ size_t ByteString::Insert(size_t index, char ch) { + return new_length; + } + +-Optional ByteString::Find(char ch, size_t start) const { ++absl::optional ByteString::Find(char ch, size_t start) const { + if (!m_pData) +- return pdfium::nullopt; ++ return absl::nullopt; + + if (!IsValidIndex(start)) +- return pdfium::nullopt; ++ return absl::nullopt; + + const char* pStr = static_cast( + memchr(m_pData->m_String + start, ch, m_pData->m_nDataLength - start)); +- return pStr ? Optional(static_cast(pStr - m_pData->m_String)) +- : pdfium::nullopt; ++ return pStr ? absl::optional( ++ static_cast(pStr - m_pData->m_String)) ++ : absl::nullopt; + } + +-Optional ByteString::Find(ByteStringView subStr, size_t start) const { ++absl::optional ByteString::Find(ByteStringView subStr, ++ size_t start) const { + if (!m_pData) +- return pdfium::nullopt; ++ return absl::nullopt; + + if (!IsValidIndex(start)) +- return pdfium::nullopt; ++ return absl::nullopt; + + const char* pStr = + FX_strstr(m_pData->m_String + start, m_pData->m_nDataLength - start, + subStr.unterminated_c_str(), subStr.GetLength()); +- return pStr ? Optional(static_cast(pStr - m_pData->m_String)) +- : pdfium::nullopt; ++ return pStr ? absl::optional( ++ static_cast(pStr - m_pData->m_String)) ++ : absl::nullopt; + } + +-Optional ByteString::ReverseFind(char ch) const { ++absl::optional ByteString::ReverseFind(char ch) const { + if (!m_pData) +- return pdfium::nullopt; ++ return absl::nullopt; + + size_t nLength = m_pData->m_nDataLength; + while (nLength--) { + if (m_pData->m_String[nLength] == ch) + return nLength; + } +- return pdfium::nullopt; ++ return absl::nullopt; + } + + void ByteString::MakeLower() { +- if (!m_pData) ++ if (IsEmpty()) + return; + + ReallocBeforeWrite(m_pData->m_nDataLength); +@@ -581,7 +593,7 @@ void ByteString::MakeLower() { + } + + void ByteString::MakeUpper() { +- if (!m_pData) ++ if (IsEmpty()) + return; + + ReallocBeforeWrite(m_pData->m_nDataLength); +@@ -589,7 +601,7 @@ void ByteString::MakeUpper() { + } + + size_t ByteString::Remove(char chRemove) { +- if (!m_pData || m_pData->m_nDataLength == 0) ++ if (IsEmpty()) + return 0; + + char* pstrSource = m_pData->m_String; +@@ -631,7 +643,7 @@ size_t ByteString::Replace(ByteStringView pOld, ByteStringView pNew) { + size_t nCount = 0; + const char* pStart = m_pData->m_String; + char* pEnd = m_pData->m_String + m_pData->m_nDataLength; +- while (1) { ++ while (true) { + const char* pTarget = FX_strstr(pStart, static_cast(pEnd - pStart), + pOld.unterminated_c_str(), nSourceLen); + if (!pTarget) +@@ -774,26 +786,30 @@ std::ostream& operator<<(std::ostream& os, ByteStringView str) { + + } // namespace fxcrt + +-uint32_t FX_HashCode_GetA(ByteStringView str, bool bIgnoreCase) { ++uint32_t FX_HashCode_GetA(ByteStringView str) { + uint32_t dwHashCode = 0; +- if (bIgnoreCase) { +- for (ByteStringView::UnsignedType c : str) +- dwHashCode = 31 * dwHashCode + tolower(c); +- } else { +- for (ByteStringView::UnsignedType c : str) +- dwHashCode = 31 * dwHashCode + c; +- } ++ for (ByteStringView::UnsignedType c : str) ++ dwHashCode = 31 * dwHashCode + c; + return dwHashCode; + } + +-uint32_t FX_HashCode_GetAsIfW(ByteStringView str, bool bIgnoreCase) { ++uint32_t FX_HashCode_GetLoweredA(ByteStringView str) { + uint32_t dwHashCode = 0; +- if (bIgnoreCase) { +- for (ByteStringView::UnsignedType c : str) +- dwHashCode = 1313 * dwHashCode + FXSYS_towlower(c); +- } else { +- for (ByteStringView::UnsignedType c : str) +- dwHashCode = 1313 * dwHashCode + c; +- } ++ for (ByteStringView::UnsignedType c : str) ++ dwHashCode = 31 * dwHashCode + tolower(c); ++ return dwHashCode; ++} ++ ++uint32_t FX_HashCode_GetAsIfW(ByteStringView str) { ++ uint32_t dwHashCode = 0; ++ for (ByteStringView::UnsignedType c : str) ++ dwHashCode = 1313 * dwHashCode + c; ++ return dwHashCode; ++} ++ ++uint32_t FX_HashCode_GetLoweredAsIfW(ByteStringView str) { ++ uint32_t dwHashCode = 0; ++ for (ByteStringView::UnsignedType c : str) ++ dwHashCode = 1313 * dwHashCode + FXSYS_towlower(c); + return dwHashCode; + } +diff --git a/core/fxcrt/bytestring.h b/core/fxcrt/bytestring.h +index 7c7f2ee37..5f1ed53e4 100644 +--- a/core/fxcrt/bytestring.h ++++ b/core/fxcrt/bytestring.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,18 +7,22 @@ + #ifndef CORE_FXCRT_BYTESTRING_H_ + #define CORE_FXCRT_BYTESTRING_H_ + ++#include ++#include ++#include ++#include ++ + #include ++#include + #include +-#include +-#include + #include + +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/fx_string_wrappers.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/string_data_template.h" + #include "core/fxcrt/string_view_template.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/check.h" + #include "third_party/base/span.h" + + namespace fxcrt { +@@ -31,11 +35,10 @@ class ByteString { + using const_iterator = const CharType*; + using const_reverse_iterator = std::reverse_iterator; + +- static ByteString FormatInteger(int i) WARN_UNUSED_RESULT; +- static ByteString FormatFloat(float f) WARN_UNUSED_RESULT; +- static ByteString Format(const char* pFormat, ...) WARN_UNUSED_RESULT; +- static ByteString FormatV(const char* pFormat, +- va_list argList) WARN_UNUSED_RESULT; ++ [[nodiscard]] static ByteString FormatInteger(int i); ++ [[nodiscard]] static ByteString FormatFloat(float f); ++ [[nodiscard]] static ByteString Format(const char* pFormat, ...); ++ [[nodiscard]] static ByteString FormatV(const char* pFormat, va_list argList); + + ByteString(); + ByteString(const ByteString& other); +@@ -43,10 +46,11 @@ class ByteString { + // Move-construct a ByteString. After construction, |other| is empty. + ByteString(ByteString&& other) noexcept; + ++ // Make a one-character string from a char. ++ explicit ByteString(char ch); ++ + // Deliberately implicit to avoid calling on every string literal. + // NOLINTNEXTLINE(runtime/explicit) +- ByteString(char ch); +- // NOLINTNEXTLINE(runtime/explicit) + ByteString(const char* ptr); + + // No implicit conversions from wide strings. +@@ -59,11 +63,13 @@ class ByteString { + explicit ByteString(ByteStringView bstrc); + ByteString(ByteStringView str1, ByteStringView str2); + ByteString(const std::initializer_list& list); +- explicit ByteString(const std::ostringstream& outStream); ++ explicit ByteString(const fxcrt::ostringstream& outStream); + + ~ByteString(); + +- void clear() { m_pData.Reset(); } ++ // Holds on to buffer if possible for later re-use. Assign ByteString() ++ // to force immediate release if desired. ++ void clear(); + + // Explicit conversion to C-style string. + // Note: Any subsequent modification of |this| will invalidate the result. +@@ -134,7 +140,7 @@ class ByteString { + ByteString& operator=(const ByteString& that); + + // Move-assign a ByteString. After assignment, |that| is empty. +- ByteString& operator=(ByteString&& that); ++ ByteString& operator=(ByteString&& that) noexcept; + + ByteString& operator+=(char ch); + ByteString& operator+=(const char* str); +@@ -163,13 +169,14 @@ class ByteString { + pdfium::span GetBuffer(size_t nMinBufLength); + void ReleaseBuffer(size_t nNewLength); + ++ ByteString Substr(size_t offset) const; + ByteString Substr(size_t first, size_t count) const; + ByteString First(size_t count) const; + ByteString Last(size_t count) const; + +- Optional Find(ByteStringView subStr, size_t start = 0) const; +- Optional Find(char ch, size_t start = 0) const; +- Optional ReverseFind(char ch) const; ++ absl::optional Find(ByteStringView subStr, size_t start = 0) const; ++ absl::optional Find(char ch, size_t start = 0) const; ++ absl::optional ReverseFind(char ch) const; + + bool Contains(ByteStringView lpszSub, size_t start = 0) const { + return Find(lpszSub, start).has_value(); +@@ -232,6 +239,12 @@ inline bool operator!=(ByteStringView lhs, const ByteString& rhs) { + inline bool operator<(const char* lhs, const ByteString& rhs) { + return rhs.Compare(lhs) > 0; + } ++inline bool operator<(const ByteStringView& lhs, const ByteString& rhs) { ++ return rhs.Compare(lhs) > 0; ++} ++inline bool operator<(const ByteStringView& lhs, const char* rhs) { ++ return lhs < ByteStringView(rhs); ++} + + inline ByteString operator+(ByteStringView str1, ByteStringView str2) { + return ByteString(str1, str2); +@@ -246,7 +259,7 @@ inline ByteString operator+(ByteStringView str1, char ch) { + return ByteString(str1, ByteStringView(ch)); + } + inline ByteString operator+(char ch, ByteStringView str2) { +- return ByteString(ch, str2); ++ return ByteString(ByteStringView(ch), str2); + } + inline ByteString operator+(const ByteString& str1, const ByteString& str2) { + return ByteString(str1.AsStringView(), str2.AsStringView()); +@@ -255,7 +268,7 @@ inline ByteString operator+(const ByteString& str1, char ch) { + return ByteString(str1.AsStringView(), ByteStringView(ch)); + } + inline ByteString operator+(char ch, const ByteString& str2) { +- return ByteString(ch, str2.AsStringView()); ++ return ByteString(ByteStringView(ch), str2.AsStringView()); + } + inline ByteString operator+(const ByteString& str1, const char* str2) { + return ByteString(str1.AsStringView(), str2); +@@ -277,15 +290,17 @@ std::ostream& operator<<(std::ostream& os, ByteStringView str); + + using ByteString = fxcrt::ByteString; + +-uint32_t FX_HashCode_GetA(ByteStringView str, bool bIgnoreCase); +-uint32_t FX_HashCode_GetAsIfW(ByteStringView str, bool bIgnoreCase); ++uint32_t FX_HashCode_GetA(ByteStringView str); ++uint32_t FX_HashCode_GetLoweredA(ByteStringView str); ++uint32_t FX_HashCode_GetAsIfW(ByteStringView str); ++uint32_t FX_HashCode_GetLoweredAsIfW(ByteStringView str); + + namespace std { + + template <> + struct hash { +- std::size_t operator()(const ByteString& str) const { +- return FX_HashCode_GetA(str.AsStringView(), false); ++ size_t operator()(const ByteString& str) const { ++ return FX_HashCode_GetA(str.AsStringView()); + } + }; + +diff --git a/core/fxcrt/bytestring_unittest.cpp b/core/fxcrt/bytestring_unittest.cpp +index e2e87f00c..f00bb4631 100644 +--- a/core/fxcrt/bytestring_unittest.cpp ++++ b/core/fxcrt/bytestring_unittest.cpp +@@ -1,17 +1,21 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcrt/bytestring.h" + ++#include ++ + #include ++#include + #include ++#include + #include + + #include "core/fxcrt/fx_string.h" + #include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/base/containers/contains.h" + #include "third_party/base/span.h" +-#include "third_party/base/stl_util.h" + + namespace fxcrt { + +@@ -112,6 +116,26 @@ TEST(ByteString, Assign) { + } + EXPECT_EQ(1, string1.ReferenceCountForTesting()); + } ++ { ++ // From char*. ++ ByteString string1 = "abc"; ++ EXPECT_EQ("abc", string1); ++ string1 = nullptr; ++ EXPECT_TRUE(string1.IsEmpty()); ++ string1 = "def"; ++ EXPECT_EQ("def", string1); ++ string1 = ""; ++ EXPECT_TRUE(string1.IsEmpty()); ++ } ++ { ++ // From ByteStringView. ++ ByteString string1(ByteStringView("abc")); ++ EXPECT_EQ("abc", string1); ++ string1 = ByteStringView(""); ++ EXPECT_TRUE(string1.IsEmpty()); ++ string1 = ByteStringView("def"); ++ EXPECT_EQ("def", string1); ++ } + } + + TEST(ByteString, OperatorLT) { +@@ -207,6 +231,19 @@ TEST(ByteString, OperatorLT) { + EXPECT_FALSE(def < c_abc); + EXPECT_TRUE(abc < v_def); + EXPECT_FALSE(def < v_abc); ++ ++ EXPECT_TRUE(v_empty < a); ++ EXPECT_TRUE(v_empty < c_a); ++ ++ std::set> str_set; ++ bool inserted = str_set.insert(ByteString("hello")).second; ++ ASSERT_TRUE(inserted); ++ EXPECT_TRUE(pdfium::Contains(str_set, ByteString("hello"))); ++ EXPECT_TRUE(pdfium::Contains(str_set, ByteStringView("hello"))); ++ EXPECT_TRUE(pdfium::Contains(str_set, "hello")); ++ EXPECT_FALSE(pdfium::Contains(str_set, ByteString("goodbye"))); ++ EXPECT_FALSE(pdfium::Contains(str_set, ByteStringView("goodbye"))); ++ EXPECT_FALSE(pdfium::Contains(str_set, "goodbye")); + } + + TEST(ByteString, OperatorEQ) { +@@ -506,7 +543,16 @@ TEST(ByteString, RemoveCopies) { + } + + TEST(ByteString, Replace) { ++ ByteString empty; ++ empty.Replace("", "CLAMS"); ++ empty.Replace("xx", "CLAMS"); ++ EXPECT_EQ("", empty); ++ + ByteString fred("FRED"); ++ fred.Replace("", ""); ++ EXPECT_EQ("FRED", fred); ++ fred.Replace("", "CLAMS"); ++ EXPECT_EQ("FRED", fred); + fred.Replace("FR", "BL"); + EXPECT_EQ("BLED", fred); + fred.Replace("D", "DDY"); +@@ -517,10 +563,20 @@ TEST(ByteString, Replace) { + EXPECT_EQ("BY", fred); + fred.Replace("BY", "HI"); + EXPECT_EQ("HI", fred); +- fred.Replace("", "CLAMS"); +- EXPECT_EQ("HI", fred); +- fred.Replace("HI", ""); ++ fred.Replace("I", "IHIHI"); ++ EXPECT_EQ("HIHIHI", fred); ++ fred.Replace("HI", "HO"); ++ EXPECT_EQ("HOHOHO", fred); ++ fred.Replace("HO", ""); + EXPECT_EQ("", fred); ++ ++ ByteString five_xs("xxxxx"); ++ five_xs.Replace("xx", "xxx"); ++ EXPECT_EQ("xxxxxxx", five_xs); ++ ++ ByteString five_ys("yyyyy"); ++ five_ys.Replace("yy", "y"); ++ EXPECT_EQ("yyy", five_ys); + } + + TEST(ByteString, Insert) { +@@ -603,7 +659,20 @@ TEST(ByteString, Delete) { + EXPECT_EQ("", empty); + } + +-TEST(ByteString, Substr) { ++TEST(ByteString, OneArgSubstr) { ++ ByteString fred("FRED"); ++ EXPECT_EQ("FRED", fred.Substr(0)); ++ EXPECT_EQ("RED", fred.Substr(1)); ++ EXPECT_EQ("ED", fred.Substr(2)); ++ EXPECT_EQ("D", fred.Substr(3)); ++ EXPECT_EQ("", fred.Substr(4)); ++ ++ ByteString empty; ++ EXPECT_EQ("", empty.Substr(0)); ++ EXPECT_EQ("", empty.Substr(1)); ++} ++ ++TEST(ByteString, TwoArgSubstr) { + ByteString fred("FRED"); + EXPECT_EQ("", fred.Substr(0, 0)); + EXPECT_EQ("", fred.Substr(3, 0)); +@@ -665,9 +734,8 @@ TEST(ByteString, Find) { + EXPECT_FALSE(empty_string.Find('a').has_value()); + EXPECT_FALSE(empty_string.Find('\0').has_value()); + +- Optional result; + ByteString single_string("a"); +- result = single_string.Find('a'); ++ absl::optional result = single_string.Find('a'); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(0u, result.value()); + EXPECT_FALSE(single_string.Find('b').has_value()); +@@ -714,9 +782,8 @@ TEST(ByteString, ReverseFind) { + EXPECT_FALSE(empty_string.ReverseFind('a').has_value()); + EXPECT_FALSE(empty_string.ReverseFind('\0').has_value()); + +- Optional result; + ByteString single_string("a"); +- result = single_string.ReverseFind('a'); ++ absl::optional result = single_string.ReverseFind('a'); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(0u, result.value()); + EXPECT_FALSE(single_string.ReverseFind('b').has_value()); +@@ -751,6 +818,17 @@ TEST(ByteString, UpperLower) { + EXPECT_EQ("", empty); + empty.MakeUpper(); + EXPECT_EQ("", empty); ++ ++ ByteString empty_with_buffer("x"); ++ empty_with_buffer.Delete(0); ++ ++ ByteString additional_empty_with_buffer_ref = empty_with_buffer; ++ additional_empty_with_buffer_ref.MakeLower(); ++ EXPECT_EQ("", additional_empty_with_buffer_ref); ++ ++ additional_empty_with_buffer_ref = empty_with_buffer; ++ additional_empty_with_buffer_ref.MakeUpper(); ++ EXPECT_EQ("", additional_empty_with_buffer_ref); + } + + TEST(ByteString, Trim) { +@@ -1208,7 +1286,7 @@ TEST(ByteStringView, FromVector) { + cleared_vec.pop_back(); + ByteStringView cleared_string(cleared_vec); + EXPECT_EQ(0u, cleared_string.GetLength()); +- EXPECT_EQ(nullptr, cleared_string.raw_str()); ++ EXPECT_FALSE(cleared_string.raw_str()); + } + + TEST(ByteStringView, GetID) { +@@ -1234,9 +1312,8 @@ TEST(ByteStringView, Find) { + EXPECT_FALSE(empty_string.Find('a').has_value()); + EXPECT_FALSE(empty_string.Find('\0').has_value()); + +- Optional result; + ByteStringView single_string("a"); +- result = single_string.Find('a'); ++ absl::optional result = single_string.Find('a'); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(0u, result.value()); + EXPECT_FALSE(single_string.Find('b').has_value()); +@@ -1260,7 +1337,28 @@ TEST(ByteStringView, Find) { + EXPECT_EQ(2u, result.value()); + } + +-TEST(ByteStringView, Substr) { ++TEST(ByteStringView, OneArgSubstr) { ++ ByteStringView null_string; ++ EXPECT_EQ(null_string, null_string.Substr(0)); ++ EXPECT_EQ(null_string, null_string.Substr(1)); ++ ++ ByteStringView empty_string(""); ++ EXPECT_EQ("", empty_string.Substr(0)); ++ EXPECT_EQ("", empty_string.Substr(1)); ++ ++ ByteStringView single_character("a"); ++ EXPECT_EQ(single_character, single_character.Substr(0)); ++ EXPECT_EQ("", single_character.Substr(1)); ++ ++ ByteStringView longer_string("abcdef"); ++ EXPECT_EQ(longer_string, longer_string.Substr(0)); ++ EXPECT_EQ("", longer_string.Substr(187)); ++ ++ ByteStringView trailing_substring("ef"); ++ EXPECT_EQ(trailing_substring, longer_string.Substr(4)); ++} ++ ++TEST(ByteStringView, TwoArgSubstr) { + ByteStringView null_string; + EXPECT_EQ(null_string, null_string.Substr(0, 1)); + EXPECT_EQ(null_string, null_string.Substr(1, 1)); +@@ -1603,9 +1701,9 @@ TEST(ByteStringView, AnyAllNoneOf) { + EXPECT_TRUE(std::any_of(str.begin(), str.end(), + [](const char& c) { return c == 'a'; })); + +- EXPECT_TRUE(pdfium::ContainsValue(str, 'a')); +- EXPECT_TRUE(pdfium::ContainsValue(str, 'b')); +- EXPECT_FALSE(pdfium::ContainsValue(str, 'z')); ++ EXPECT_TRUE(pdfium::Contains(str, 'a')); ++ EXPECT_TRUE(pdfium::Contains(str, 'b')); ++ EXPECT_FALSE(pdfium::Contains(str, 'z')); + } + + TEST(ByteString, FormatWidth) { +@@ -1630,19 +1728,19 @@ TEST(ByteString, Empty) { + EXPECT_EQ(0u, empty_str.GetLength()); + + const char* cstr = empty_str.c_str(); +- EXPECT_NE(nullptr, cstr); ++ EXPECT_TRUE(cstr); + EXPECT_EQ(0u, strlen(cstr)); + + const uint8_t* rstr = empty_str.raw_str(); +- EXPECT_EQ(nullptr, rstr); ++ EXPECT_FALSE(rstr); + + pdfium::span cspan = empty_str.span(); + EXPECT_TRUE(cspan.empty()); +- EXPECT_EQ(nullptr, cspan.data()); ++ EXPECT_FALSE(cspan.data()); + + pdfium::span rspan = empty_str.raw_span(); + EXPECT_TRUE(rspan.empty()); +- EXPECT_EQ(nullptr, rspan.data()); ++ EXPECT_FALSE(rspan.data()); + } + + TEST(ByteString, InitializerList) { +@@ -1720,9 +1818,9 @@ TEST(ByteString, AnyAllNoneOf) { + EXPECT_TRUE(std::any_of(str.begin(), str.end(), + [](const char& c) { return c == 'a'; })); + +- EXPECT_TRUE(pdfium::ContainsValue(str, 'a')); +- EXPECT_TRUE(pdfium::ContainsValue(str, 'b')); +- EXPECT_FALSE(pdfium::ContainsValue(str, 'z')); ++ EXPECT_TRUE(pdfium::Contains(str, 'a')); ++ EXPECT_TRUE(pdfium::Contains(str, 'b')); ++ EXPECT_FALSE(pdfium::Contains(str, 'z')); + } + + TEST(CFX_BytrString, EqualNoCase) { +@@ -1874,21 +1972,21 @@ TEST(ByteString, FormatInteger) { + } + + TEST(ByteString, FX_HashCode_Ascii) { +- EXPECT_EQ(0u, FX_HashCode_GetA("", false)); +- EXPECT_EQ(65u, FX_HashCode_GetA("A", false)); +- EXPECT_EQ(97u, FX_HashCode_GetA("A", true)); +- EXPECT_EQ(31 * 65u + 66u, FX_HashCode_GetA("AB", false)); +- EXPECT_EQ(31u * 65u + 255u, FX_HashCode_GetA("A\xff", false)); +- EXPECT_EQ(31u * 97u + 255u, FX_HashCode_GetA("A\xff", true)); ++ EXPECT_EQ(0u, FX_HashCode_GetA("")); ++ EXPECT_EQ(65u, FX_HashCode_GetA("A")); ++ EXPECT_EQ(97u, FX_HashCode_GetLoweredA("A")); ++ EXPECT_EQ(31 * 65u + 66u, FX_HashCode_GetA("AB")); ++ EXPECT_EQ(31u * 65u + 255u, FX_HashCode_GetA("A\xff")); ++ EXPECT_EQ(31u * 97u + 255u, FX_HashCode_GetLoweredA("A\xff")); + } + + TEST(ByteString, FX_HashCode_Wide) { +- EXPECT_EQ(0u, FX_HashCode_GetAsIfW("", false)); +- EXPECT_EQ(65u, FX_HashCode_GetAsIfW("A", false)); +- EXPECT_EQ(97u, FX_HashCode_GetAsIfW("A", true)); +- EXPECT_EQ(1313u * 65u + 66u, FX_HashCode_GetAsIfW("AB", false)); +- EXPECT_EQ(1313u * 65u + 255u, FX_HashCode_GetAsIfW("A\xff", false)); +- EXPECT_EQ(1313u * 97u + 255u, FX_HashCode_GetAsIfW("A\xff", true)); ++ EXPECT_EQ(0u, FX_HashCode_GetAsIfW("")); ++ EXPECT_EQ(65u, FX_HashCode_GetAsIfW("A")); ++ EXPECT_EQ(97u, FX_HashCode_GetLoweredAsIfW("A")); ++ EXPECT_EQ(1313u * 65u + 66u, FX_HashCode_GetAsIfW("AB")); ++ EXPECT_EQ(1313u * 65u + 255u, FX_HashCode_GetAsIfW("A\xff")); ++ EXPECT_EQ(1313u * 97u + 255u, FX_HashCode_GetLoweredAsIfW("A\xff")); + } + + } // namespace fxcrt +diff --git a/core/fxcrt/cfx_binarybuf.cpp b/core/fxcrt/cfx_binarybuf.cpp +deleted file mode 100644 +index e24590cf5..000000000 +--- a/core/fxcrt/cfx_binarybuf.cpp ++++ /dev/null +@@ -1,86 +0,0 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fxcrt/cfx_binarybuf.h" +- +-#include +-#include +- +-#include "core/fxcrt/fx_safe_types.h" +- +-CFX_BinaryBuf::CFX_BinaryBuf() = default; +- +-CFX_BinaryBuf::~CFX_BinaryBuf() = default; +- +-void CFX_BinaryBuf::Delete(size_t start_index, size_t count) { +- if (!m_pBuffer || count > m_DataSize || start_index > m_DataSize - count) +- return; +- +- memmove(m_pBuffer.get() + start_index, m_pBuffer.get() + start_index + count, +- m_DataSize - start_index - count); +- m_DataSize -= count; +-} +- +-pdfium::span CFX_BinaryBuf::GetSpan() { +- return {GetBuffer(), GetSize()}; +-} +- +-pdfium::span CFX_BinaryBuf::GetSpan() const { +- return {GetBuffer(), GetSize()}; +-} +- +-size_t CFX_BinaryBuf::GetLength() const { +- return m_DataSize; +-} +- +-void CFX_BinaryBuf::Clear() { +- m_DataSize = 0; +-} +- +-std::unique_ptr CFX_BinaryBuf::DetachBuffer() { +- m_DataSize = 0; +- m_AllocSize = 0; +- return std::move(m_pBuffer); +-} +- +-void CFX_BinaryBuf::EstimateSize(size_t size) { +- if (m_AllocSize < size) +- ExpandBuf(size - m_DataSize); +-} +- +-void CFX_BinaryBuf::ExpandBuf(size_t add_size) { +- FX_SAFE_SIZE_T new_size = m_DataSize; +- new_size += add_size; +- if (m_AllocSize >= new_size.ValueOrDie()) +- return; +- +- size_t alloc_step = std::max(static_cast(128), +- m_AllocStep ? m_AllocStep : m_AllocSize / 4); +- new_size += alloc_step - 1; // Quantize, don't combine these lines. +- new_size /= alloc_step; +- new_size *= alloc_step; +- m_AllocSize = new_size.ValueOrDie(); +- m_pBuffer.reset(m_pBuffer +- ? FX_Realloc(uint8_t, m_pBuffer.release(), m_AllocSize) +- : FX_Alloc(uint8_t, m_AllocSize)); +-} +- +-void CFX_BinaryBuf::AppendSpan(pdfium::span span) { +- return AppendBlock(span.data(), span.size()); +-} +- +-void CFX_BinaryBuf::AppendBlock(const void* pBuf, size_t size) { +- if (size == 0) +- return; +- +- ExpandBuf(size); +- if (pBuf) { +- memcpy(m_pBuffer.get() + m_DataSize, pBuf, size); +- } else { +- memset(m_pBuffer.get() + m_DataSize, 0, size); +- } +- m_DataSize += size; +-} +diff --git a/core/fxcrt/cfx_binarybuf.h b/core/fxcrt/cfx_binarybuf.h +deleted file mode 100644 +index b757c9a6e..000000000 +--- a/core/fxcrt/cfx_binarybuf.h ++++ /dev/null +@@ -1,57 +0,0 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXCRT_CFX_BINARYBUF_H_ +-#define CORE_FXCRT_CFX_BINARYBUF_H_ +- +-#include +- +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" +-#include "third_party/base/span.h" +- +-class CFX_BinaryBuf { +- public: +- CFX_BinaryBuf(); +- virtual ~CFX_BinaryBuf(); +- +- pdfium::span GetSpan(); +- pdfium::span GetSpan() const; +- uint8_t* GetBuffer() const { return m_pBuffer.get(); } +- size_t GetSize() const { return m_DataSize; } +- virtual size_t GetLength() const; +- bool IsEmpty() const { return GetLength() == 0; } +- +- void Clear(); +- void SetAllocStep(size_t step) { m_AllocStep = step; } +- void EstimateSize(size_t size); +- void AppendSpan(pdfium::span span); +- void AppendBlock(const void* pBuf, size_t size); +- void AppendString(const ByteString& str) { +- AppendBlock(str.c_str(), str.GetLength()); +- } +- +- void AppendByte(uint8_t byte) { +- ExpandBuf(1); +- m_pBuffer.get()[m_DataSize++] = byte; +- } +- +- void Delete(size_t start_index, size_t count); +- +- // Releases ownership of |m_pBuffer| and returns it. +- std::unique_ptr DetachBuffer(); +- +- protected: +- void ExpandBuf(size_t size); +- +- size_t m_AllocStep = 0; +- size_t m_AllocSize = 0; +- size_t m_DataSize = 0; +- std::unique_ptr m_pBuffer; +-}; +- +-#endif // CORE_FXCRT_CFX_BINARYBUF_H_ +diff --git a/core/fxcrt/cfx_bitstream.cpp b/core/fxcrt/cfx_bitstream.cpp +index d16fc2f1a..a1719daf6 100644 +--- a/core/fxcrt/cfx_bitstream.cpp ++++ b/core/fxcrt/cfx_bitstream.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,31 +10,31 @@ + + #include "core/fxcrt/fx_memory.h" + #include "core/fxcrt/fx_system.h" ++#include "third_party/base/check_op.h" + + CFX_BitStream::CFX_BitStream(pdfium::span pData) +- : m_BitPos(0), m_BitSize(pData.size() * 8), m_pData(pData.data()) { +- ASSERT(pData.size() <= std::numeric_limits::max() / 8); ++ : m_BitSize(pData.size() * 8), m_pData(pData) { ++ CHECK_LE(m_pData.size(), std::numeric_limits::max() / 8); + } + +-CFX_BitStream::~CFX_BitStream() {} ++CFX_BitStream::~CFX_BitStream() = default; + + void CFX_BitStream::ByteAlign() { + m_BitPos = FxAlignToBoundary<8>(m_BitPos); + } + + uint32_t CFX_BitStream::GetBits(uint32_t nBits) { +- ASSERT(nBits > 0); +- ASSERT(nBits <= 32); ++ DCHECK(nBits > 0); ++ DCHECK(nBits <= 32); + if (nBits > m_BitSize || m_BitPos > m_BitSize - nBits) + return 0; + + const uint32_t bit_pos = m_BitPos % 8; +- uint32_t byte_pos = m_BitPos / 8; +- const uint8_t* data = m_pData.Get(); +- uint8_t current_byte = data[byte_pos]; ++ size_t byte_pos = m_BitPos / 8; ++ uint8_t current_byte = m_pData[byte_pos]; + + if (nBits == 1) { +- int bit = (current_byte & (1 << (7 - bit_pos))) ? 1 : 0; ++ uint32_t bit = (current_byte & (1 << (7 - bit_pos))) ? 1 : 0; + m_BitPos++; + return bit; + } +@@ -54,10 +54,10 @@ uint32_t CFX_BitStream::GetBits(uint32_t nBits) { + } + while (bit_left >= 8) { + bit_left -= 8; +- result |= data[byte_pos++] << bit_left; ++ result |= m_pData[byte_pos++] << bit_left; + } + if (bit_left) +- result |= data[byte_pos] >> (8 - bit_left); ++ result |= m_pData[byte_pos] >> (8 - bit_left); + m_BitPos += nBits; + return result; + } +diff --git a/core/fxcrt/cfx_bitstream.h b/core/fxcrt/cfx_bitstream.h +index baa2a9f54..c60cf0a36 100644 +--- a/core/fxcrt/cfx_bitstream.h ++++ b/core/fxcrt/cfx_bitstream.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,9 +7,9 @@ + #ifndef CORE_FXCRT_CFX_BITSTREAM_H_ + #define CORE_FXCRT_CFX_BITSTREAM_H_ + ++#include + #include + +-#include "core/fxcrt/unowned_ptr.h" + #include "third_party/base/span.h" + + class CFX_BitStream { +@@ -20,20 +20,20 @@ class CFX_BitStream { + void ByteAlign(); + + bool IsEOF() const { return m_BitPos >= m_BitSize; } +- uint32_t GetPos() const { return m_BitPos; } ++ size_t GetPos() const { return m_BitPos; } + uint32_t GetBits(uint32_t nBits); + +- void SkipBits(uint32_t nBits) { m_BitPos += nBits; } ++ void SkipBits(size_t nBits) { m_BitPos += nBits; } + void Rewind() { m_BitPos = 0; } + +- uint32_t BitsRemaining() const { ++ size_t BitsRemaining() const { + return m_BitSize >= m_BitPos ? m_BitSize - m_BitPos : 0; + } + + private: +- uint32_t m_BitPos; +- uint32_t m_BitSize; +- UnownedPtr m_pData; ++ size_t m_BitPos = 0; ++ const size_t m_BitSize; ++ pdfium::span const m_pData; + }; + + #endif // CORE_FXCRT_CFX_BITSTREAM_H_ +diff --git a/core/fxcrt/cfx_bitstream_unittest.cpp b/core/fxcrt/cfx_bitstream_unittest.cpp +index 2ba366369..9debbd03b 100644 +--- a/core/fxcrt/cfx_bitstream_unittest.cpp ++++ b/core/fxcrt/cfx_bitstream_unittest.cpp +@@ -1,8 +1,11 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcrt/cfx_bitstream.h" ++ ++#include ++ + #include "testing/gtest/include/gtest/gtest.h" + + namespace { +@@ -116,6 +119,64 @@ TEST(fxcrt, BitStream) { + EXPECT_TRUE(bitstream.IsEOF()); + EXPECT_EQ(1063U, bitstream.GetPos()); + EXPECT_EQ(0U, bitstream.BitsRemaining()); ++ EXPECT_EQ(0u, bitstream.GetBits(4)); ++ ++ // Align past the end. ++ bitstream.ByteAlign(); ++ EXPECT_TRUE(bitstream.IsEOF()); ++ EXPECT_EQ(1064U, bitstream.GetPos()); ++ EXPECT_EQ(0U, bitstream.BitsRemaining()); ++ EXPECT_EQ(0u, bitstream.GetBits(4)); ++} ++ ++TEST(fxcrt, BitStreamEmpty) { ++ CFX_BitStream bitstream({}); ++ EXPECT_TRUE(bitstream.IsEOF()); ++ EXPECT_EQ(0U, bitstream.GetPos()); ++ EXPECT_EQ(0U, bitstream.BitsRemaining()); ++ ++ // Getting bits returns zero and doesn't advance. ++ EXPECT_EQ(0u, bitstream.GetBits(4)); ++ EXPECT_EQ(0U, bitstream.GetPos()); ++ ++ // Skip past the end. ++ bitstream.SkipBits(63); ++ EXPECT_TRUE(bitstream.IsEOF()); ++ EXPECT_EQ(63U, bitstream.GetPos()); ++ EXPECT_EQ(0U, bitstream.BitsRemaining()); ++ EXPECT_EQ(0u, bitstream.GetBits(4)); ++ ++ // Align past the end. ++ bitstream.ByteAlign(); ++ EXPECT_TRUE(bitstream.IsEOF()); ++ EXPECT_EQ(64U, bitstream.GetPos()); ++ EXPECT_EQ(0U, bitstream.BitsRemaining()); ++ EXPECT_EQ(0u, bitstream.GetBits(4)); ++} ++ ++TEST(fxcrt, BitStreamBig) { ++ // We can't actually allocate enough memory to test the limits of ++ // the bitstream arithmetic, but as long as we don't try to extract ++ // any bits, the calculations should be unaffected. ++ const uint8_t kNotReallyBigEnough[32] = {}; ++ constexpr size_t kAllocationBytes = std::numeric_limits::max() / 8; ++ constexpr size_t kAllocationBits = kAllocationBytes * 8; ++ CFX_BitStream bitstream({kNotReallyBigEnough, kAllocationBytes}); ++ EXPECT_FALSE(bitstream.IsEOF()); ++ EXPECT_EQ(0U, bitstream.GetPos()); ++ EXPECT_EQ(kAllocationBits, bitstream.BitsRemaining()); ++ ++ // Skip some bits. ++ bitstream.SkipBits(kAllocationBits - 1023); ++ EXPECT_FALSE(bitstream.IsEOF()); ++ EXPECT_EQ(kAllocationBits - 1023, bitstream.GetPos()); ++ EXPECT_EQ(1023u, bitstream.BitsRemaining()); ++ ++ // Align to byte. ++ bitstream.ByteAlign(); ++ EXPECT_FALSE(bitstream.IsEOF()); ++ EXPECT_EQ(kAllocationBits - 1016, bitstream.GetPos()); ++ EXPECT_EQ(1016u, bitstream.BitsRemaining()); + } + + TEST(fxcrt, BitStreamSameAsReferenceGetBits32) { +diff --git a/core/fxcrt/cfx_datetime.cpp b/core/fxcrt/cfx_datetime.cpp +index 56c660917..50f1c6a35 100644 +--- a/core/fxcrt/cfx_datetime.cpp ++++ b/core/fxcrt/cfx_datetime.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,51 +7,47 @@ + #include "core/fxcrt/cfx_datetime.h" + + #include "build/build_config.h" ++#include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_system.h" +- +-#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_MACOSX) || \ +- defined(OS_ASMJS) || defined(__wasm__) +-#include +-#include +-#endif ++#include "third_party/base/check.h" ++#include "third_party/base/span.h" + + namespace { + +-const uint8_t g_FXDaysPerMonth[12] = {31, 28, 31, 30, 31, 30, +- 31, 31, 30, 31, 30, 31}; +-const uint8_t g_FXDaysPerLeapMonth[12] = {31, 29, 31, 30, 31, 30, +- 31, 31, 30, 31, 30, 31}; +-const int32_t g_FXDaysBeforeMonth[12] = {0, 31, 59, 90, 120, 151, +- 181, 212, 243, 273, 304, 334}; +-const int32_t g_FXDaysBeforeLeapMonth[12] = {0, 31, 60, 91, 121, 152, +- 182, 213, 244, 274, 305, 335}; +-const int32_t g_FXDaysPerYear = 365; +-const int32_t g_FXDaysPerLeapYear = 366; ++constexpr uint8_t kDaysPerMonth[12] = {31, 28, 31, 30, 31, 30, ++ 31, 31, 30, 31, 30, 31}; ++constexpr uint8_t kDaysPerLeapMonth[12] = {31, 29, 31, 30, 31, 30, ++ 31, 31, 30, 31, 30, 31}; ++constexpr int32_t kDaysBeforeMonth[12] = {0, 31, 59, 90, 120, 151, ++ 181, 212, 243, 273, 304, 334}; ++constexpr int32_t kDaysBeforeLeapMonth[12] = {0, 31, 60, 91, 121, 152, ++ 182, 213, 244, 274, 305, 335}; ++constexpr int32_t kDaysPerYear = 365; ++constexpr int32_t kDaysPerLeapYear = 366; + + int32_t DaysBeforeMonthInYear(int32_t iYear, uint8_t iMonth) { +- ASSERT(iYear != 0); +- ASSERT(iMonth >= 1); +- ASSERT(iMonth <= 12); +- +- const int32_t* p = +- FX_IsLeapYear(iYear) ? g_FXDaysBeforeLeapMonth : g_FXDaysBeforeMonth; ++ DCHECK(iYear != 0); ++ pdfium::span p = FX_IsLeapYear(iYear) ++ ? pdfium::make_span(kDaysBeforeLeapMonth) ++ : pdfium::make_span(kDaysBeforeMonth); ++ // Note: iMonth is one-based. + return p[iMonth - 1]; + } + + int32_t DaysInYear(int32_t iYear) { +- ASSERT(iYear != 0); +- return FX_IsLeapYear(iYear) ? g_FXDaysPerLeapYear : g_FXDaysPerYear; ++ DCHECK(iYear != 0); ++ return FX_IsLeapYear(iYear) ? kDaysPerLeapYear : kDaysPerYear; + } + + int64_t DateToDays(int32_t iYear, + uint8_t iMonth, + uint8_t iDay, + bool bIncludeThisDay) { +- ASSERT(iYear != 0); +- ASSERT(iMonth >= 1); +- ASSERT(iMonth <= 12); +- ASSERT(iDay >= 1); +- ASSERT(iDay <= FX_DaysInMonth(iYear, iMonth)); ++ DCHECK(iYear != 0); ++ DCHECK(iMonth >= 1); ++ DCHECK(iMonth <= 12); ++ DCHECK(iDay >= 1); ++ DCHECK(iDay <= FX_DaysInMonth(iYear, iMonth)); + + int64_t iDays = DaysBeforeMonthInYear(iYear, iMonth); + iDays += iDay; +@@ -68,61 +64,31 @@ int64_t DateToDays(int32_t iYear, + iYear / 400; + } + +-struct FXUT_SYSTEMTIME { +- uint16_t wYear; +- uint16_t wMonth; +- uint16_t wDayOfWeek; +- uint16_t wDay; +- uint16_t wHour; +- uint16_t wMinute; +- uint16_t wSecond; +- uint16_t wMillisecond; +-}; +- + } // namespace + + uint8_t FX_DaysInMonth(int32_t iYear, uint8_t iMonth) { +- ASSERT(iYear != 0); +- ASSERT(iMonth >= 1); +- ASSERT(iMonth <= 12); +- +- const uint8_t* p = +- FX_IsLeapYear(iYear) ? g_FXDaysPerLeapMonth : g_FXDaysPerMonth; ++ DCHECK(iYear != 0); ++ pdfium::span p = FX_IsLeapYear(iYear) ++ ? pdfium::make_span(kDaysPerLeapMonth) ++ : pdfium::make_span(kDaysPerMonth); ++ // Note: iMonth is one-based. + return p[iMonth - 1]; + } + + bool FX_IsLeapYear(int32_t iYear) { +- ASSERT(iYear != 0); ++ DCHECK(iYear != 0); + return ((iYear % 4) == 0 && (iYear % 100) != 0) || (iYear % 400) == 0; + } + + // static + CFX_DateTime CFX_DateTime::Now() { +- FXUT_SYSTEMTIME utLocal; +-#if defined(OS_WIN) +- ::GetLocalTime((LPSYSTEMTIME)&utLocal); +-#else +- timeval curTime; +- gettimeofday(&curTime, nullptr); +- +- struct tm st; +- localtime_r(&curTime.tv_sec, &st); +- utLocal.wYear = st.tm_year + 1900; +- utLocal.wMonth = st.tm_mon + 1; +- utLocal.wDayOfWeek = st.tm_wday; +- utLocal.wDay = st.tm_mday; +- utLocal.wHour = st.tm_hour; +- utLocal.wMinute = st.tm_min; +- utLocal.wSecond = st.tm_sec; +- utLocal.wMillisecond = curTime.tv_usec / 1000; +-#endif // defined(OS_WIN) +- +- return CFX_DateTime(utLocal.wYear, static_cast(utLocal.wMonth), +- static_cast(utLocal.wDay), +- static_cast(utLocal.wHour), +- static_cast(utLocal.wMinute), +- static_cast(utLocal.wSecond), +- static_cast(utLocal.wMillisecond)); ++ time_t t = FXSYS_time(nullptr); ++ struct tm* pTime = FXSYS_localtime(&t); ++ return CFX_DateTime( ++ pTime->tm_year + 1900, static_cast(pTime->tm_mon + 1), ++ static_cast(pTime->tm_mday), ++ static_cast(pTime->tm_hour), static_cast(pTime->tm_min), ++ static_cast(pTime->tm_sec), 0); + } + + int32_t CFX_DateTime::GetDayOfWeek() const { +diff --git a/core/fxcrt/cfx_datetime.h b/core/fxcrt/cfx_datetime.h +index a965df73a..ef274bf11 100644 +--- a/core/fxcrt/cfx_datetime.h ++++ b/core/fxcrt/cfx_datetime.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,23 +7,16 @@ + #ifndef CORE_FXCRT_CFX_DATETIME_H_ + #define CORE_FXCRT_CFX_DATETIME_H_ + +-#include "core/fxcrt/fx_system.h" ++#include + + bool FX_IsLeapYear(int32_t iYear); + uint8_t FX_DaysInMonth(int32_t iYear, uint8_t iMonth); + + class CFX_DateTime { + public: +- static CFX_DateTime Now(); ++ static CFX_DateTime Now(); // Accurate to seconds, subject to test overrides. + +- CFX_DateTime() +- : year_(0), +- month_(0), +- day_(0), +- hour_(0), +- minute_(0), +- second_(0), +- millisecond_(0) {} ++ CFX_DateTime() = default; + CFX_DateTime(int32_t year, + uint8_t month, + uint8_t day, +@@ -82,18 +75,13 @@ class CFX_DateTime { + bool operator==(const CFX_DateTime& other) const; + + private: +- int32_t year_; +- uint8_t month_; +- uint8_t day_; +- uint8_t hour_; +- uint8_t minute_; +- uint8_t second_; +- uint16_t millisecond_; +-}; +- +-struct FX_TIMEZONE { +- int8_t tzHour; +- uint8_t tzMinute; ++ int32_t year_ = 0; ++ uint8_t month_ = 0; ++ uint8_t day_ = 0; ++ uint8_t hour_ = 0; ++ uint8_t minute_ = 0; ++ uint8_t second_ = 0; ++ uint16_t millisecond_ = 0; + }; + + #endif // CORE_FXCRT_CFX_DATETIME_H_ +diff --git a/core/fxcrt/cfx_datetime_unittest.cpp b/core/fxcrt/cfx_datetime_unittest.cpp +new file mode 100644 +index 000000000..5349e2c15 +--- /dev/null ++++ b/core/fxcrt/cfx_datetime_unittest.cpp +@@ -0,0 +1,18 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/cfx_datetime.h" ++ ++#include "core/fxcrt/fake_time_test.h" ++ ++TEST_F(FakeTimeTest, Now) { ++ CFX_DateTime dt = CFX_DateTime::Now(); ++ EXPECT_EQ(2020, dt.GetYear()); ++ EXPECT_EQ(4, dt.GetMonth()); ++ EXPECT_EQ(23, dt.GetDay()); ++ EXPECT_EQ(15, dt.GetHour()); ++ EXPECT_EQ(5, dt.GetMinute()); ++ EXPECT_EQ(21, dt.GetSecond()); ++ EXPECT_EQ(0, dt.GetMillisecond()); ++} +diff --git a/core/fxcrt/cfx_fileaccess_posix.cpp b/core/fxcrt/cfx_fileaccess_posix.cpp +index f08df66a0..fad4b3a0b 100644 +--- a/core/fxcrt/cfx_fileaccess_posix.cpp ++++ b/core/fxcrt/cfx_fileaccess_posix.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,7 +13,7 @@ + #include + + #include "core/fxcrt/fx_stream.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/numerics/safe_conversions.h" + + #ifndef O_BINARY + #define O_BINARY 0 +@@ -23,27 +23,9 @@ + #define O_LARGEFILE 0 + #endif // O_LARGEFILE + +-namespace { +- +-void GetFileMode(uint32_t dwModes, int32_t& nFlags, int32_t& nMasks) { +- nFlags = O_BINARY | O_LARGEFILE; +- if (dwModes & FX_FILEMODE_ReadOnly) { +- nFlags |= O_RDONLY; +- nMasks = 0; +- } else { +- nFlags |= O_RDWR | O_CREAT; +- if (dwModes & FX_FILEMODE_Truncate) { +- nFlags |= O_TRUNC; +- } +- nMasks = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; +- } +-} +- +-} // namespace +- + // static + std::unique_ptr FileAccessIface::Create() { +- return pdfium::MakeUnique(); ++ return std::make_unique(); + } + + CFX_FileAccess_Posix::CFX_FileAccess_Posix() : m_nFD(-1) {} +@@ -52,23 +34,16 @@ CFX_FileAccess_Posix::~CFX_FileAccess_Posix() { + Close(); + } + +-bool CFX_FileAccess_Posix::Open(ByteStringView fileName, uint32_t dwMode) { ++bool CFX_FileAccess_Posix::Open(ByteStringView fileName) { + if (m_nFD > -1) + return false; + +- int32_t nFlags; +- int32_t nMasks; +- GetFileMode(dwMode, nFlags, nMasks); +- + // TODO(tsepez): check usage of c_str() below. +- m_nFD = open(fileName.unterminated_c_str(), nFlags, nMasks); ++ m_nFD = ++ open(fileName.unterminated_c_str(), O_BINARY | O_LARGEFILE | O_RDONLY); + return m_nFD > -1; + } + +-bool CFX_FileAccess_Posix::Open(WideStringView fileName, uint32_t dwMode) { +- return Open(FX_UTF8Encode(fileName).AsStringView(), dwMode); +-} +- + void CFX_FileAccess_Posix::Close() { + if (m_nFD < 0) { + return; +@@ -83,7 +58,7 @@ FX_FILESIZE CFX_FileAccess_Posix::GetSize() const { + struct stat s; + memset(&s, 0, sizeof(s)); + fstat(m_nFD, &s); +- return s.st_size; ++ return pdfium::base::checked_cast(s.st_size); + } + FX_FILESIZE CFX_FileAccess_Posix::GetPosition() const { + if (m_nFD < 0) { +diff --git a/core/fxcrt/cfx_fileaccess_posix.h b/core/fxcrt/cfx_fileaccess_posix.h +index 39fdb2382..3e25f28b4 100644 +--- a/core/fxcrt/cfx_fileaccess_posix.h ++++ b/core/fxcrt/cfx_fileaccess_posix.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,14 @@ + #ifndef CORE_FXCRT_CFX_FILEACCESS_POSIX_H_ + #define CORE_FXCRT_CFX_FILEACCESS_POSIX_H_ + ++#include ++#include ++ + #include "build/build_config.h" + #include "core/fxcrt/fileaccess_iface.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/fx_types.h" + +-#if _FX_PLATFORM_ != _FX_PLATFORM_LINUX_ && !defined(OS_MACOSX) && \ +- !defined(OS_ANDROID) ++#if !BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_FUCHSIA) + #error "Included on the wrong platform" + #endif + +@@ -22,8 +24,7 @@ class CFX_FileAccess_Posix final : public FileAccessIface { + ~CFX_FileAccess_Posix() override; + + // FileAccessIface: +- bool Open(ByteStringView fileName, uint32_t dwMode) override; +- bool Open(WideStringView fileName, uint32_t dwMode) override; ++ bool Open(ByteStringView fileName) override; + void Close() override; + FX_FILESIZE GetSize() const override; + FX_FILESIZE GetPosition() const override; +@@ -38,7 +39,7 @@ class CFX_FileAccess_Posix final : public FileAccessIface { + bool Truncate(FX_FILESIZE szFile) override; + + private: +- int32_t m_nFD; ++ int32_t m_nFD = -1; + }; + + #endif // CORE_FXCRT_CFX_FILEACCESS_POSIX_H_ +diff --git a/core/fxcrt/cfx_fileaccess_windows.cpp b/core/fxcrt/cfx_fileaccess_windows.cpp +index d1e5ff157..bef9f58e5 100644 +--- a/core/fxcrt/cfx_fileaccess_windows.cpp ++++ b/core/fxcrt/cfx_fileaccess_windows.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,60 +10,26 @@ + + #include "core/fxcrt/fx_stream.h" + #include "core/fxcrt/fx_string.h" +-#include "third_party/base/ptr_util.h" +- +-namespace { +- +-void GetFileMode(uint32_t dwMode, +- uint32_t& dwAccess, +- uint32_t& dwShare, +- uint32_t& dwCreation) { +- dwAccess = GENERIC_READ; +- dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE; +- if (!(dwMode & FX_FILEMODE_ReadOnly)) { +- dwAccess |= GENERIC_WRITE; +- dwCreation = (dwMode & FX_FILEMODE_Truncate) ? CREATE_ALWAYS : OPEN_ALWAYS; +- } else { +- dwCreation = OPEN_EXISTING; +- } +-} +- +-} // namespace + + // static + std::unique_ptr FileAccessIface::Create() { +- return pdfium::MakeUnique(); ++ return std::make_unique(); + } + +-CFX_FileAccess_Windows::CFX_FileAccess_Windows() : m_hFile(nullptr) {} ++CFX_FileAccess_Windows::CFX_FileAccess_Windows() = default; + + CFX_FileAccess_Windows::~CFX_FileAccess_Windows() { + Close(); + } + +-bool CFX_FileAccess_Windows::Open(ByteStringView fileName, uint32_t dwMode) { +- if (m_hFile) +- return false; +- +- uint32_t dwAccess, dwShare, dwCreation; +- GetFileMode(dwMode, dwAccess, dwShare, dwCreation); +- m_hFile = ::CreateFileA(fileName.unterminated_c_str(), dwAccess, dwShare, +- nullptr, dwCreation, FILE_ATTRIBUTE_NORMAL, nullptr); +- if (m_hFile == INVALID_HANDLE_VALUE) +- m_hFile = nullptr; +- +- return !!m_hFile; +-} +- +-bool CFX_FileAccess_Windows::Open(WideStringView fileName, uint32_t dwMode) { ++bool CFX_FileAccess_Windows::Open(ByteStringView fileName) { + if (m_hFile) + return false; + +- uint32_t dwAccess, dwShare, dwCreation; +- GetFileMode(dwMode, dwAccess, dwShare, dwCreation); +- m_hFile = +- ::CreateFileW((LPCWSTR)fileName.unterminated_c_str(), dwAccess, dwShare, +- nullptr, dwCreation, FILE_ATTRIBUTE_NORMAL, nullptr); ++ WideString wname = FX_UTF8Decode(fileName); ++ m_hFile = ::CreateFileW(wname.c_str(), GENERIC_READ, ++ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, ++ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (m_hFile == INVALID_HANDLE_VALUE) + m_hFile = nullptr; + +diff --git a/core/fxcrt/cfx_fileaccess_windows.h b/core/fxcrt/cfx_fileaccess_windows.h +index a95466f71..2928d1823 100644 +--- a/core/fxcrt/cfx_fileaccess_windows.h ++++ b/core/fxcrt/cfx_fileaccess_windows.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,14 @@ + #ifndef CORE_FXCRT_CFX_FILEACCESS_WINDOWS_H_ + #define CORE_FXCRT_CFX_FILEACCESS_WINDOWS_H_ + ++#include ++#include ++ + #include "build/build_config.h" + #include "core/fxcrt/fileaccess_iface.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/fx_types.h" + +-#if !defined(OS_WIN) ++#if !BUILDFLAG(IS_WIN) + #error "Included on the wrong platform" + #endif + +@@ -21,8 +24,7 @@ class CFX_FileAccess_Windows final : public FileAccessIface { + ~CFX_FileAccess_Windows() override; + + // FileAccessIface +- bool Open(ByteStringView fileName, uint32_t dwMode) override; +- bool Open(WideStringView fileName, uint32_t dwMode) override; ++ bool Open(ByteStringView fileName) override; + void Close() override; + FX_FILESIZE GetSize() const override; + FX_FILESIZE GetPosition() const override; +@@ -37,7 +39,7 @@ class CFX_FileAccess_Windows final : public FileAccessIface { + bool Truncate(FX_FILESIZE szFile) override; + + private: +- void* m_hFile; ++ void* m_hFile = nullptr; + }; + + #endif // CORE_FXCRT_CFX_FILEACCESS_WINDOWS_H_ +diff --git a/core/fxcrt/cfx_fixedbufgrow.h b/core/fxcrt/cfx_fixedbufgrow.h +deleted file mode 100644 +index daeac1344..000000000 +--- a/core/fxcrt/cfx_fixedbufgrow.h ++++ /dev/null +@@ -1,31 +0,0 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXCRT_CFX_FIXEDBUFGROW_H_ +-#define CORE_FXCRT_CFX_FIXEDBUFGROW_H_ +- +-#include +- +-#include "core/fxcrt/fx_memory_wrappers.h" +- +-template +-class CFX_FixedBufGrow { +- public: +- explicit CFX_FixedBufGrow(size_t data_size) { +- if (data_size > FixedSize) { +- m_pGrowData.reset(FX_Alloc(DataType, data_size)); +- return; +- } +- memset(m_FixedData, 0, sizeof(DataType) * FixedSize); +- } +- operator DataType*() { return m_pGrowData ? m_pGrowData.get() : m_FixedData; } +- +- private: +- std::unique_ptr m_pGrowData; +- DataType m_FixedData[FixedSize]; +-}; +- +-#endif // CORE_FXCRT_CFX_FIXEDBUFGROW_H_ +diff --git a/core/fxcrt/cfx_memorystream.cpp b/core/fxcrt/cfx_memorystream.cpp +index b1f131e06..32bb36060 100644 +--- a/core/fxcrt/cfx_memorystream.cpp ++++ b/core/fxcrt/cfx_memorystream.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,13 +10,9 @@ + #include + + #include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/span_util.h" + +-CFX_MemoryStream::CFX_MemoryStream() : m_nTotalSize(0), m_nCurSize(0) {} +- +-CFX_MemoryStream::CFX_MemoryStream( +- std::unique_ptr pBuffer, +- size_t nSize) +- : m_data(std::move(pBuffer)), m_nTotalSize(nSize), m_nCurSize(nSize) {} ++CFX_MemoryStream::CFX_MemoryStream() = default; + + CFX_MemoryStream::~CFX_MemoryStream() = default; + +@@ -36,48 +32,56 @@ bool CFX_MemoryStream::Flush() { + return true; + } + +-bool CFX_MemoryStream::ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) { +- if (!buffer || offset < 0 || !size) ++pdfium::span CFX_MemoryStream::GetSpan() const { ++ return pdfium::make_span(m_data).first(m_nCurSize); ++} ++ ++bool CFX_MemoryStream::ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) { ++ if (buffer.empty() || offset < 0) + return false; + +- FX_SAFE_SIZE_T newPos = size; +- newPos += offset; +- if (!newPos.IsValid() || newPos.ValueOrDefault(0) == 0 || +- newPos.ValueOrDie() > m_nCurSize) { ++ FX_SAFE_SIZE_T new_pos = buffer.size(); ++ new_pos += offset; ++ if (!new_pos.IsValid() || new_pos.ValueOrDefault(0) == 0 || ++ new_pos.ValueOrDie() > m_nCurSize) { + return false; + } + +- m_nCurPos = newPos.ValueOrDie(); +- memcpy(buffer, &GetBuffer()[offset], size); ++ m_nCurPos = new_pos.ValueOrDie(); ++ // Safe to cast `offset` because it was used to calculate `new_pos` above, and ++ // `new_pos` is valid. ++ fxcrt::spancpy(buffer, ++ GetSpan().subspan(static_cast(offset), buffer.size())); + return true; + } + +-size_t CFX_MemoryStream::ReadBlock(void* buffer, size_t size) { ++size_t CFX_MemoryStream::ReadBlock(pdfium::span buffer) { + if (m_nCurPos >= m_nCurSize) + return 0; + +- size_t nRead = std::min(size, m_nCurSize - m_nCurPos); +- if (!ReadBlockAtOffset(buffer, static_cast(m_nCurPos), nRead)) ++ size_t nRead = std::min(buffer.size(), m_nCurSize - m_nCurPos); ++ if (!ReadBlockAtOffset(buffer.first(nRead), static_cast(m_nCurPos))) + return 0; + + return nRead; + } + +-bool CFX_MemoryStream::WriteBlockAtOffset(const void* buffer, +- FX_FILESIZE offset, +- size_t size) { +- if (!buffer || offset < 0 || !size) ++bool CFX_MemoryStream::WriteBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) { ++ if (offset < 0) + return false; + +- FX_SAFE_SIZE_T safe_new_pos = size; ++ if (buffer.empty()) ++ return true; ++ ++ FX_SAFE_SIZE_T safe_new_pos = buffer.size(); + safe_new_pos += offset; + if (!safe_new_pos.IsValid()) + return false; + + size_t new_pos = safe_new_pos.ValueOrDie(); +- if (new_pos > m_nTotalSize) { ++ if (new_pos > m_data.size()) { + static constexpr size_t kBlockSize = 64 * 1024; + FX_SAFE_SIZE_T new_size = new_pos; + new_size *= 2; +@@ -87,15 +91,14 @@ bool CFX_MemoryStream::WriteBlockAtOffset(const void* buffer, + if (!new_size.IsValid()) + return false; + +- m_nTotalSize = new_size.ValueOrDie(); +- if (m_data) +- m_data.reset(FX_Realloc(uint8_t, m_data.release(), m_nTotalSize)); +- else +- m_data.reset(FX_Alloc(uint8_t, m_nTotalSize)); ++ m_data.resize(new_size.ValueOrDie()); + } + m_nCurPos = new_pos; + +- memcpy(&m_data.get()[offset], buffer, size); ++ // Safe to cast `offset` because it was used to calculate `safe_new_pos` ++ // above, and `safe_new_pos` is valid. ++ fxcrt::spancpy(pdfium::make_span(m_data).subspan(static_cast(offset)), ++ buffer); + m_nCurSize = std::max(m_nCurSize, m_nCurPos); + + return true; +diff --git a/core/fxcrt/cfx_memorystream.h b/core/fxcrt/cfx_memorystream.h +index ce6fd64da..5ecf9951b 100644 +--- a/core/fxcrt/cfx_memorystream.h ++++ b/core/fxcrt/cfx_memorystream.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,41 +7,34 @@ + #ifndef CORE_FXCRT_CFX_MEMORYSTREAM_H_ + #define CORE_FXCRT_CFX_MEMORYSTREAM_H_ + +-#include +- +-#include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_stream.h" + #include "core/fxcrt/retain_ptr.h" ++#include "third_party/base/span.h" + + class CFX_MemoryStream final : public IFX_SeekableStream { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // IFX_SeekableStream + FX_FILESIZE GetSize() override; + FX_FILESIZE GetPosition() override; + bool IsEOF() override; +- bool ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) override; +- size_t ReadBlock(void* buffer, size_t size) override; +- bool WriteBlockAtOffset(const void* buffer, +- FX_FILESIZE offset, +- size_t size) override; ++ size_t ReadBlock(pdfium::span buffer) override; ++ bool ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) override; ++ bool WriteBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) override; + bool Flush() override; + +- const uint8_t* GetBuffer() const { return m_data.get(); } ++ pdfium::span GetSpan() const; + + private: + CFX_MemoryStream(); +- CFX_MemoryStream(std::unique_ptr pBuffer, +- size_t nSize); + ~CFX_MemoryStream() override; + +- std::unique_ptr m_data; +- size_t m_nTotalSize; +- size_t m_nCurSize; ++ DataVector m_data; ++ size_t m_nCurSize = 0; + size_t m_nCurPos = 0; + }; + +diff --git a/core/fxcrt/cfx_memorystream_unittest.cpp b/core/fxcrt/cfx_memorystream_unittest.cpp +index 8339e8e9a..88b0e2569 100644 +--- a/core/fxcrt/cfx_memorystream_unittest.cpp ++++ b/core/fxcrt/cfx_memorystream_unittest.cpp +@@ -1,10 +1,11 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcrt/cfx_memorystream.h" + + #include "core/fxcrt/retain_ptr.h" ++#include "testing/gmock/include/gmock/gmock.h" + #include "testing/gtest/include/gtest/gtest.h" + + namespace { +@@ -14,18 +15,45 @@ const size_t kSomeTextLen = sizeof(kSomeText) - 1; + + } // namespace + +-TEST(CFX_MemoryStreamTest, SparseBlockWrites) { ++TEST(CFXMemoryStreamTest, SparseBlockWrites) { + auto stream = pdfium::MakeRetain(); +- for (FX_FILESIZE offset = 0; offset <= 200000; offset += 100000) +- stream->WriteBlockAtOffset(kSomeText, offset, kSomeTextLen); +- ++ for (FX_FILESIZE offset = 0; offset <= 200000; offset += 100000) { ++ stream->WriteBlockAtOffset( ++ {reinterpret_cast(kSomeText), kSomeTextLen}, offset); ++ } + EXPECT_EQ(200000 + kSomeTextLen, static_cast(stream->GetSize())); + } + +-TEST(CFX_MemoryStreamTest, OverlappingBlockWrites) { ++TEST(CFXMemoryStreamTest, OverlappingBlockWrites) { + auto stream = pdfium::MakeRetain(); +- for (FX_FILESIZE offset = 0; offset <= 100; ++offset) +- stream->WriteBlockAtOffset(kSomeText, offset, kSomeTextLen); +- ++ for (FX_FILESIZE offset = 0; offset <= 100; ++offset) { ++ stream->WriteBlockAtOffset( ++ {reinterpret_cast(kSomeText), kSomeTextLen}, offset); ++ } + EXPECT_EQ(100 + kSomeTextLen, static_cast(stream->GetSize())); + } ++ ++TEST(CFXMemoryStreamTest, ReadWriteBlockAtOffset) { ++ auto stream = pdfium::MakeRetain(); ++ const uint8_t kData1[] = {'a', 'b', 'c'}; ++ ASSERT_TRUE(stream->WriteBlock(kData1)); ++ ASSERT_THAT(stream->GetSpan(), testing::ElementsAre('a', 'b', 'c')); ++ ++ ASSERT_TRUE(stream->WriteBlockAtOffset(kData1, 5)); ++ ASSERT_THAT(stream->GetSpan(), ++ testing::ElementsAre('a', 'b', 'c', '\0', '\0', 'a', 'b', 'c')); ++ ++ uint8_t buffer[4]; ++ ASSERT_TRUE(stream->ReadBlockAtOffset(buffer, 2)); ++ ASSERT_THAT(buffer, testing::ElementsAre('c', '\0', '\0', 'a')); ++} ++ ++TEST(CFXMemoryStreamTest, WriteZeroBytes) { ++ auto stream = pdfium::MakeRetain(); ++ const uint8_t kData1[] = {'a', 'b', 'c'}; ++ ASSERT_TRUE(stream->WriteBlock(kData1)); ++ ASSERT_THAT(stream->GetSpan(), testing::ElementsAre('a', 'b', 'c')); ++ ++ ASSERT_TRUE(stream->WriteBlock({})); ++ ASSERT_THAT(stream->GetSpan(), testing::ElementsAre('a', 'b', 'c')); ++} +diff --git a/core/fxcrt/cfx_read_only_span_stream.cpp b/core/fxcrt/cfx_read_only_span_stream.cpp +new file mode 100644 +index 000000000..ca78fa884 +--- /dev/null ++++ b/core/fxcrt/cfx_read_only_span_stream.cpp +@@ -0,0 +1,36 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxcrt/cfx_read_only_span_stream.h" ++ ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/span_util.h" ++#include "third_party/base/numerics/safe_conversions.h" ++ ++CFX_ReadOnlySpanStream::CFX_ReadOnlySpanStream(pdfium::span span) ++ : span_(span) {} ++ ++CFX_ReadOnlySpanStream::~CFX_ReadOnlySpanStream() = default; ++ ++FX_FILESIZE CFX_ReadOnlySpanStream::GetSize() { ++ return pdfium::base::checked_cast(span_.size()); ++} ++ ++bool CFX_ReadOnlySpanStream::ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) { ++ if (buffer.empty() || offset < 0) ++ return false; ++ ++ FX_SAFE_SIZE_T pos = buffer.size(); ++ pos += offset; ++ if (!pos.IsValid() || pos.ValueOrDie() > span_.size()) ++ return false; ++ ++ fxcrt::spancpy( ++ buffer, ++ span_.subspan(pdfium::base::checked_cast(offset), buffer.size())); ++ return true; ++} +diff --git a/core/fxcrt/cfx_read_only_span_stream.h b/core/fxcrt/cfx_read_only_span_stream.h +new file mode 100644 +index 000000000..b1b30cd6f +--- /dev/null ++++ b/core/fxcrt/cfx_read_only_span_stream.h +@@ -0,0 +1,30 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCRT_CFX_READ_ONLY_SPAN_STREAM_H_ ++#define CORE_FXCRT_CFX_READ_ONLY_SPAN_STREAM_H_ ++ ++#include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "third_party/base/span.h" ++ ++class CFX_ReadOnlySpanStream final : public IFX_SeekableReadStream { ++ public: ++ CONSTRUCT_VIA_MAKE_RETAIN; ++ ++ // IFX_SeekableReadStream: ++ FX_FILESIZE GetSize() override; ++ bool ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) override; ++ ++ private: ++ explicit CFX_ReadOnlySpanStream(pdfium::span span); ++ ~CFX_ReadOnlySpanStream() override; ++ ++ const pdfium::span span_; ++}; ++ ++#endif // CORE_FXCRT_CFX_READ_ONLY_SPAN_STREAM_H_ +diff --git a/core/fxcrt/cfx_read_only_string_stream.cpp b/core/fxcrt/cfx_read_only_string_stream.cpp +new file mode 100644 +index 000000000..62d550546 +--- /dev/null ++++ b/core/fxcrt/cfx_read_only_string_stream.cpp +@@ -0,0 +1,25 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/cfx_read_only_string_stream.h" ++ ++#include ++ ++#include "core/fxcrt/cfx_read_only_span_stream.h" ++#include "third_party/base/span.h" ++ ++CFX_ReadOnlyStringStream::CFX_ReadOnlyStringStream(ByteString data) ++ : data_(std::move(data)), ++ stream_(pdfium::MakeRetain(data_.raw_span())) {} ++ ++CFX_ReadOnlyStringStream::~CFX_ReadOnlyStringStream() = default; ++ ++FX_FILESIZE CFX_ReadOnlyStringStream::GetSize() { ++ return stream_->GetSize(); ++} ++ ++bool CFX_ReadOnlyStringStream::ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) { ++ return stream_->ReadBlockAtOffset(buffer, offset); ++} +diff --git a/core/fxcrt/cfx_read_only_string_stream.h b/core/fxcrt/cfx_read_only_string_stream.h +new file mode 100644 +index 000000000..fe4b26faf +--- /dev/null ++++ b/core/fxcrt/cfx_read_only_string_stream.h +@@ -0,0 +1,31 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXCRT_CFX_READ_ONLY_STRING_STREAM_H_ ++#define CORE_FXCRT_CFX_READ_ONLY_STRING_STREAM_H_ ++ ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/retain_ptr.h" ++ ++class CFX_ReadOnlySpanStream; ++ ++class CFX_ReadOnlyStringStream final : public IFX_SeekableReadStream { ++ public: ++ CONSTRUCT_VIA_MAKE_RETAIN; ++ ++ // IFX_SeekableReadStream: ++ FX_FILESIZE GetSize() override; ++ bool ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) override; ++ ++ private: ++ explicit CFX_ReadOnlyStringStream(ByteString data); ++ ~CFX_ReadOnlyStringStream() override; ++ ++ const ByteString data_; ++ const RetainPtr stream_; ++}; ++ ++#endif // CORE_FXCRT_CFX_READ_ONLY_STRING_STREAM_H_ +diff --git a/core/fxcrt/cfx_read_only_vector_stream.cpp b/core/fxcrt/cfx_read_only_vector_stream.cpp +new file mode 100644 +index 000000000..d6a5bf00e +--- /dev/null ++++ b/core/fxcrt/cfx_read_only_vector_stream.cpp +@@ -0,0 +1,30 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/cfx_read_only_vector_stream.h" ++ ++#include ++ ++#include "core/fxcrt/cfx_read_only_span_stream.h" ++#include "third_party/base/span.h" ++ ++CFX_ReadOnlyVectorStream::CFX_ReadOnlyVectorStream(DataVector data) ++ : data_(std::move(data)), ++ stream_(pdfium::MakeRetain(data_)) {} ++ ++CFX_ReadOnlyVectorStream::CFX_ReadOnlyVectorStream( ++ FixedUninitDataVector data) ++ : fixed_data_(std::move(data)), ++ stream_(pdfium::MakeRetain(fixed_data_)) {} ++ ++CFX_ReadOnlyVectorStream::~CFX_ReadOnlyVectorStream() = default; ++ ++FX_FILESIZE CFX_ReadOnlyVectorStream::GetSize() { ++ return stream_->GetSize(); ++} ++ ++bool CFX_ReadOnlyVectorStream::ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) { ++ return stream_->ReadBlockAtOffset(buffer, offset); ++} +diff --git a/core/fxcrt/cfx_read_only_vector_stream.h b/core/fxcrt/cfx_read_only_vector_stream.h +new file mode 100644 +index 000000000..7e9e1e9bd +--- /dev/null ++++ b/core/fxcrt/cfx_read_only_vector_stream.h +@@ -0,0 +1,37 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXCRT_CFX_READ_ONLY_VECTOR_STREAM_H_ ++#define CORE_FXCRT_CFX_READ_ONLY_VECTOR_STREAM_H_ ++ ++#include ++ ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fixed_uninit_data_vector.h" ++#include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/retain_ptr.h" ++ ++class CFX_ReadOnlySpanStream; ++ ++class CFX_ReadOnlyVectorStream final : public IFX_SeekableReadStream { ++ public: ++ CONSTRUCT_VIA_MAKE_RETAIN; ++ ++ // IFX_SeekableReadStream: ++ FX_FILESIZE GetSize() override; ++ bool ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) override; ++ ++ private: ++ explicit CFX_ReadOnlyVectorStream(DataVector data); ++ explicit CFX_ReadOnlyVectorStream(FixedUninitDataVector data); ++ ~CFX_ReadOnlyVectorStream() override; ++ ++ const DataVector data_; ++ const FixedUninitDataVector fixed_data_; ++ // Spans over either `data_` or `fixed_data_`. ++ const RetainPtr stream_; ++}; ++ ++#endif // CORE_FXCRT_CFX_READ_ONLY_VECTOR_STREAM_H_ +diff --git a/core/fxcrt/cfx_readonlymemorystream.cpp b/core/fxcrt/cfx_readonlymemorystream.cpp +deleted file mode 100644 +index 807788a4b..000000000 +--- a/core/fxcrt/cfx_readonlymemorystream.cpp ++++ /dev/null +@@ -1,42 +0,0 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fxcrt/cfx_readonlymemorystream.h" +- +-#include +- +-#include "core/fxcrt/fx_safe_types.h" +- +-CFX_ReadOnlyMemoryStream::CFX_ReadOnlyMemoryStream( +- std::unique_ptr data, +- size_t size) +- : m_data(std::move(data)), m_span(m_data.get(), size) {} +- +-CFX_ReadOnlyMemoryStream::CFX_ReadOnlyMemoryStream( +- pdfium::span span) +- : m_span(span) {} +- +-CFX_ReadOnlyMemoryStream::~CFX_ReadOnlyMemoryStream() = default; +- +-FX_FILESIZE CFX_ReadOnlyMemoryStream::GetSize() { +- return pdfium::base::checked_cast(m_span.size()); +-} +- +-bool CFX_ReadOnlyMemoryStream::ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) { +- if (!buffer || offset < 0 || size == 0) +- return false; +- +- FX_SAFE_SIZE_T pos = size; +- pos += offset; +- if (!pos.IsValid() || pos.ValueOrDie() > m_span.size()) +- return false; +- +- auto copy_span = m_span.subspan(offset, size); +- memcpy(buffer, copy_span.data(), copy_span.size()); +- return true; +-} +diff --git a/core/fxcrt/cfx_readonlymemorystream.h b/core/fxcrt/cfx_readonlymemorystream.h +deleted file mode 100644 +index ea2b849cb..000000000 +--- a/core/fxcrt/cfx_readonlymemorystream.h ++++ /dev/null +@@ -1,38 +0,0 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXCRT_CFX_READONLYMEMORYSTREAM_H_ +-#define CORE_FXCRT_CFX_READONLYMEMORYSTREAM_H_ +- +-#include +- +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxcrt/fx_stream.h" +-#include "core/fxcrt/retain_ptr.h" +-#include "third_party/base/span.h" +- +-class CFX_ReadOnlyMemoryStream final : public IFX_SeekableReadStream { +- public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- +- // IFX_SeekableReadStream: +- FX_FILESIZE GetSize() override; +- bool ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) override; +- +- private: +- CFX_ReadOnlyMemoryStream(std::unique_ptr data, +- size_t size); +- explicit CFX_ReadOnlyMemoryStream(pdfium::span span); +- ~CFX_ReadOnlyMemoryStream() override; +- +- std::unique_ptr m_data; +- const pdfium::span m_span; +-}; +- +-#endif // CORE_FXCRT_CFX_READONLYMEMORYSTREAM_H_ +diff --git a/core/fxcrt/cfx_seekablestreamproxy.cpp b/core/fxcrt/cfx_seekablestreamproxy.cpp +index 67b304f59..dc311ae92 100644 +--- a/core/fxcrt/cfx_seekablestreamproxy.cpp ++++ b/core/fxcrt/cfx_seekablestreamproxy.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,48 +6,39 @@ + + #include "core/fxcrt/cfx_seekablestreamproxy.h" + +-#if defined(OS_WIN) +-#include +-#endif ++#include + + #include + #include +-#include + #include +-#include + + #include "build/build_config.h" +-#include "core/fxcrt/fx_codepage.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_extension.h" +-#include "core/fxcrt/fx_memory_wrappers.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/cxx17_backports.h" + + namespace { + + // Returns {src bytes consumed, dst chars produced}. + // Invalid sequences are silently not output. +-std::pair UTF8Decode(const char* pSrc, +- size_t srcLen, +- wchar_t* pDst, +- size_t dstLen) { +- ASSERT(pDst); +- ASSERT(dstLen > 0); +- +- if (srcLen < 1) +- return {0, 0}; ++std::pair UTF8Decode(pdfium::span pSrc, ++ pdfium::span pDst) { ++ DCHECK(!pDst.empty()); + + uint32_t dwCode = 0; + int32_t iPending = 0; + size_t iSrcNum = 0; + size_t iDstNum = 0; +- for (size_t iIndex = 0; iIndex < srcLen && iDstNum < dstLen; ++iIndex) { ++ for (size_t iIndex = 0; iIndex < pSrc.size() && iDstNum < pDst.size(); ++ ++iIndex) { + ++iSrcNum; +- uint8_t byte = static_cast(*(pSrc + iIndex)); ++ uint8_t byte = pSrc[iIndex]; + if (byte < 0x80) { + iPending = 0; +- ++iDstNum; +- *pDst++ = byte; ++ pDst[iDstNum++] = byte; + } else if (byte < 0xc0) { + if (iPending < 1) + continue; +@@ -55,10 +46,8 @@ std::pair UTF8Decode(const char* pSrc, + dwCode = dwCode << 6; + dwCode |= (byte & 0x3f); + --iPending; +- if (iPending == 0) { +- ++iDstNum; +- *pDst++ = dwCode; +- } ++ if (iPending == 0) ++ pDst[iDstNum++] = dwCode; + } else if (byte < 0xe0) { + iPending = 1; + dwCode = (byte & 0x1f); +@@ -83,8 +72,8 @@ std::pair UTF8Decode(const char* pSrc, + static_assert(sizeof(wchar_t) > 2, "wchar_t is too small"); + + void UTF16ToWChar(void* pBuffer, size_t iLength) { +- ASSERT(pBuffer); +- ASSERT(iLength > 0); ++ DCHECK(pBuffer); ++ DCHECK_GT(iLength, 0); + + uint16_t* pSrc = static_cast(pBuffer); + wchar_t* pDst = static_cast(pBuffer); +@@ -112,11 +101,8 @@ void SwapByteOrder(uint16_t* pStr, size_t iLength) { + + CFX_SeekableStreamProxy::CFX_SeekableStreamProxy( + const RetainPtr& stream) +- : m_wCodePage(FX_CODEPAGE_DefANSI), +- m_wBOMLength(0), +- m_iPosition(0), +- m_pStream(stream) { +- ASSERT(m_pStream); ++ : m_pStream(stream) { ++ DCHECK(m_pStream); + + Seek(From::Begin, 0); + +@@ -126,18 +112,18 @@ CFX_SeekableStreamProxy::CFX_SeekableStreamProxy( + bom &= BOM_UTF8_MASK; + if (bom == BOM_UTF8) { + m_wBOMLength = 3; +- m_wCodePage = FX_CODEPAGE_UTF8; ++ m_wCodePage = FX_CodePage::kUTF8; + } else { + bom &= BOM_UTF16_MASK; + if (bom == BOM_UTF16_BE) { + m_wBOMLength = 2; +- m_wCodePage = FX_CODEPAGE_UTF16BE; ++ m_wCodePage = FX_CodePage::kUTF16BE; + } else if (bom == BOM_UTF16_LE) { + m_wBOMLength = 2; +- m_wCodePage = FX_CODEPAGE_UTF16LE; ++ m_wCodePage = FX_CodePage::kUTF16LE; + } else { + m_wBOMLength = 0; +- m_wCodePage = FXSYS_GetACP(); ++ m_wCodePage = FX_GetACP(); + } + } + +@@ -174,22 +160,22 @@ void CFX_SeekableStreamProxy::Seek(From eSeek, FX_FILESIZE iOffset) { + pdfium::clamp(m_iPosition, static_cast(0), GetSize()); + } + +-void CFX_SeekableStreamProxy::SetCodePage(uint16_t wCodePage) { ++void CFX_SeekableStreamProxy::SetCodePage(FX_CodePage wCodePage) { + if (m_wBOMLength > 0) + return; + m_wCodePage = wCodePage; + } + + size_t CFX_SeekableStreamProxy::ReadData(uint8_t* pBuffer, size_t iBufferSize) { +- ASSERT(pBuffer); +- ASSERT(iBufferSize > 0); ++ DCHECK(pBuffer); ++ DCHECK(iBufferSize > 0); + + iBufferSize = + std::min(iBufferSize, static_cast(GetSize() - m_iPosition)); + if (iBufferSize <= 0) + return 0; + +- if (!m_pStream->ReadBlockAtOffset(pBuffer, m_iPosition, iBufferSize)) ++ if (!m_pStream->ReadBlockAtOffset({pBuffer, iBufferSize}, m_iPosition)) + return 0; + + FX_SAFE_FILESIZE new_pos = m_iPosition; +@@ -202,38 +188,33 @@ size_t CFX_SeekableStreamProxy::ReadBlock(wchar_t* pStr, size_t size) { + if (!pStr || size == 0) + return 0; + +- if (m_wCodePage == FX_CODEPAGE_UTF16LE || +- m_wCodePage == FX_CODEPAGE_UTF16BE) { ++ if (m_wCodePage == FX_CodePage::kUTF16LE || ++ m_wCodePage == FX_CodePage::kUTF16BE) { + size_t iBytes = size * 2; + size_t iLen = ReadData(reinterpret_cast(pStr), iBytes); + size = iLen / 2; +- if (m_wCodePage == FX_CODEPAGE_UTF16BE) ++ if (m_wCodePage == FX_CodePage::kUTF16BE) + SwapByteOrder(reinterpret_cast(pStr), size); + + #if defined(WCHAR_T_IS_UTF32) + if (size > 0) + UTF16ToWChar(pStr, size); + #endif +- } else { +- FX_FILESIZE pos = GetPosition(); +- size_t iBytes = std::min(size, static_cast(GetSize() - pos)); +- +- if (iBytes > 0) { +- std::vector> buf(iBytes); ++ return size; ++ } + +- size_t iLen = ReadData(buf.data(), iBytes); +- if (m_wCodePage != FX_CODEPAGE_UTF8) +- return 0; ++ FX_FILESIZE pos = GetPosition(); ++ size_t iBytes = std::min(size, static_cast(GetSize() - pos)); ++ if (iBytes == 0) ++ return 0; + +- size_t iSrc = 0; +- std::tie(iSrc, size) = +- UTF8Decode(reinterpret_cast(buf.data()), iLen, +- static_cast(pStr), size); +- Seek(From::Current, iSrc - iLen); +- } else { +- size = 0; +- } +- } ++ DataVector buf(iBytes); ++ size_t iLen = ReadData(buf.data(), iBytes); ++ if (m_wCodePage != FX_CodePage::kUTF8) ++ return 0; + ++ size_t iSrc; ++ std::tie(iSrc, size) = UTF8Decode({buf.data(), iLen}, {pStr, size}); ++ Seek(From::Current, iSrc - iLen); + return size; + } +diff --git a/core/fxcrt/cfx_seekablestreamproxy.h b/core/fxcrt/cfx_seekablestreamproxy.h +index d6ea160af..563134fb7 100644 +--- a/core/fxcrt/cfx_seekablestreamproxy.h ++++ b/core/fxcrt/cfx_seekablestreamproxy.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,14 +7,17 @@ + #ifndef CORE_FXCRT_CFX_SEEKABLESTREAMPROXY_H_ + #define CORE_FXCRT_CFX_SEEKABLESTREAMPROXY_H_ + ++#include ++#include ++ ++#include "core/fxcrt/fx_codepage.h" + #include "core/fxcrt/fx_stream.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/fx_types.h" + #include "core/fxcrt/retain_ptr.h" + + class CFX_SeekableStreamProxy final : public Retainable { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // Unlike IFX_SeekableStreamProxy, buffers and sizes are always in terms + // of the number of wchar_t elementss, not bytes. +@@ -22,8 +25,8 @@ class CFX_SeekableStreamProxy final : public Retainable { + bool IsEOF(); + size_t ReadBlock(wchar_t* pStr, size_t size); + +- uint16_t GetCodePage() const { return m_wCodePage; } +- void SetCodePage(uint16_t wCodePage); ++ FX_CodePage GetCodePage() const { return m_wCodePage; } ++ void SetCodePage(FX_CodePage wCodePage); + + private: + enum class From { +@@ -39,10 +42,10 @@ class CFX_SeekableStreamProxy final : public Retainable { + void Seek(From eSeek, FX_FILESIZE iOffset); + size_t ReadData(uint8_t* pBuffer, size_t iBufferSize); + +- uint16_t m_wCodePage; +- size_t m_wBOMLength; +- FX_FILESIZE m_iPosition; +- RetainPtr m_pStream; ++ FX_CodePage m_wCodePage = FX_CodePage::kDefANSI; ++ size_t m_wBOMLength = 0; ++ FX_FILESIZE m_iPosition = 0; ++ RetainPtr const m_pStream; + }; + + #endif // CORE_FXCRT_CFX_SEEKABLESTREAMPROXY_H_ +diff --git a/core/fxcrt/cfx_seekablestreamproxy_unittest.cpp b/core/fxcrt/cfx_seekablestreamproxy_unittest.cpp +index b263fa821..94ebf6629 100644 +--- a/core/fxcrt/cfx_seekablestreamproxy_unittest.cpp ++++ b/core/fxcrt/cfx_seekablestreamproxy_unittest.cpp +@@ -1,45 +1,43 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcrt/cfx_seekablestreamproxy.h" + +-#include +-#include ++#include + +-#include "core/fxcrt/cfx_readonlymemorystream.h" ++#include "core/fxcrt/cfx_read_only_span_stream.h" + #include "core/fxcrt/retain_ptr.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + #include "third_party/base/span.h" + + TEST(SeekableStreamProxyTest, NullStream) { + auto proxy_stream = pdfium::MakeRetain( +- pdfium::MakeRetain( ++ pdfium::MakeRetain( + pdfium::make_span(nullptr, 0))); + + wchar_t buffer[16]; +- EXPECT_EQ(0u, proxy_stream->ReadBlock(buffer, FX_ArraySize(buffer))); ++ EXPECT_EQ(0u, proxy_stream->ReadBlock(buffer, std::size(buffer))); + } + + TEST(SeekableStreamProxyTest, DefaultStreamBOMNotRecognized) { + const char data[] = "abcd"; + auto proxy_stream = pdfium::MakeRetain( +- pdfium::MakeRetain(pdfium::make_span( ++ pdfium::MakeRetain(pdfium::make_span( + reinterpret_cast(data), sizeof(data) - 1))); + + wchar_t buffer[16]; +- EXPECT_EQ(0u, proxy_stream->ReadBlock(buffer, FX_ArraySize(buffer))); ++ EXPECT_EQ(0u, proxy_stream->ReadBlock(buffer, std::size(buffer))); + } + + TEST(SeekableStreamProxyTest, UTF8Stream) { + const char data[] = "\xEF\xBB\xBF*\xC2\xA2*"; + auto proxy_stream = pdfium::MakeRetain( +- pdfium::MakeRetain(pdfium::make_span( ++ pdfium::MakeRetain(pdfium::make_span( + reinterpret_cast(data), sizeof(data) - 1))); + + wchar_t buffer[16]; +- EXPECT_EQ(3u, proxy_stream->ReadBlock(buffer, FX_ArraySize(buffer))); ++ EXPECT_EQ(3u, proxy_stream->ReadBlock(buffer, std::size(buffer))); + EXPECT_EQ(L'*', buffer[0]); + EXPECT_EQ(L'\u00A2', buffer[1]); + EXPECT_EQ(L'*', buffer[2]); +@@ -48,11 +46,11 @@ TEST(SeekableStreamProxyTest, UTF8Stream) { + TEST(SeekableStreamProxyTest, UTF16LEStream) { + const char data[] = "\xFF\xFE\x41\x00\x42\x01"; + auto proxy_stream = pdfium::MakeRetain( +- pdfium::MakeRetain(pdfium::make_span( ++ pdfium::MakeRetain(pdfium::make_span( + reinterpret_cast(data), sizeof(data) - 1))); + + wchar_t buffer[16]; +- EXPECT_EQ(2u, proxy_stream->ReadBlock(buffer, FX_ArraySize(buffer))); ++ EXPECT_EQ(2u, proxy_stream->ReadBlock(buffer, std::size(buffer))); + EXPECT_EQ(L'A', buffer[0]); + EXPECT_EQ(L'\u0142', buffer[1]); + } +@@ -60,11 +58,11 @@ TEST(SeekableStreamProxyTest, UTF16LEStream) { + TEST(SeekableStreamProxyTest, UTF16BEStream) { + const char data[] = "\xFE\xFF\x00\x41\x01\x42"; + auto proxy_stream = pdfium::MakeRetain( +- pdfium::MakeRetain(pdfium::make_span( ++ pdfium::MakeRetain(pdfium::make_span( + reinterpret_cast(data), sizeof(data) - 1))); + + wchar_t buffer[16]; +- EXPECT_EQ(2u, proxy_stream->ReadBlock(buffer, FX_ArraySize(buffer))); ++ EXPECT_EQ(2u, proxy_stream->ReadBlock(buffer, std::size(buffer))); + EXPECT_EQ(L'A', buffer[0]); + EXPECT_EQ(L'\u0142', buffer[1]); + } +diff --git a/core/fxcrt/cfx_timer.cpp b/core/fxcrt/cfx_timer.cpp +index 6ffb625cf..744b1eb89 100644 +--- a/core/fxcrt/cfx_timer.cpp ++++ b/core/fxcrt/cfx_timer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,6 +8,7 @@ + + #include + ++#include "third_party/base/check.h" + #include "third_party/base/no_destructor.h" + + namespace { +@@ -20,21 +21,23 @@ TimerMap& GetPWLTimerMap() { + + } // namespace + +-CFX_Timer::CFX_Timer(TimerHandlerIface* pTimerHandler, ++CFX_Timer::CFX_Timer(HandlerIface* pHandlerIface, + CallbackIface* pCallbackIface, + int32_t nInterval) +- : m_nTimerID(pTimerHandler->SetTimer(nInterval, TimerProc)), +- m_pTimerHandler(pTimerHandler), +- m_pCallbackIface(pCallbackIface) { +- ASSERT(m_pCallbackIface); +- if (HasValidID()) +- GetPWLTimerMap()[m_nTimerID] = this; ++ : m_pHandlerIface(pHandlerIface), m_pCallbackIface(pCallbackIface) { ++ DCHECK(m_pCallbackIface); ++ if (m_pHandlerIface) { ++ m_nTimerID = m_pHandlerIface->SetTimer(nInterval, TimerProc); ++ if (HasValidID()) ++ GetPWLTimerMap()[m_nTimerID] = this; ++ } + } + + CFX_Timer::~CFX_Timer() { + if (HasValidID()) { +- m_pTimerHandler->KillTimer(m_nTimerID); + GetPWLTimerMap().erase(m_nTimerID); ++ if (m_pHandlerIface) ++ m_pHandlerIface->KillTimer(m_nTimerID); + } + } + +diff --git a/core/fxcrt/cfx_timer.h b/core/fxcrt/cfx_timer.h +index fa97dda19..24ed296c6 100644 +--- a/core/fxcrt/cfx_timer.h ++++ b/core/fxcrt/cfx_timer.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,33 +7,48 @@ + #ifndef CORE_FXCRT_CFX_TIMER_H_ + #define CORE_FXCRT_CFX_TIMER_H_ + +-#include "core/fxcrt/timerhandler_iface.h" +-#include "core/fxcrt/unowned_ptr.h" ++#include + +-class CFX_TimerHandler; ++#include "core/fxcrt/observed_ptr.h" ++#include "core/fxcrt/unowned_ptr.h" + + class CFX_Timer { + public: ++ // HandlerIface is implemented by upper layers that actually perform ++ // the system-dependent actions of scheduling and triggering timers. ++ class HandlerIface : public Observable { ++ public: ++ static constexpr int32_t kInvalidTimerID = 0; ++ using TimerCallback = void (*)(int32_t idEvent); ++ ++ virtual ~HandlerIface() = default; ++ ++ virtual int32_t SetTimer(int32_t uElapse, TimerCallback lpTimerFunc) = 0; ++ virtual void KillTimer(int32_t nTimerID) = 0; ++ }; ++ ++ // CallbackIface is implemented by layers that want to perform a ++ // specific action on timer expiry. + class CallbackIface { + public: + virtual ~CallbackIface() = default; + virtual void OnTimerFired() = 0; + }; + +- CFX_Timer(TimerHandlerIface* pTimerHandler, ++ CFX_Timer(HandlerIface* pHandlerIface, + CallbackIface* pCallbackIface, + int32_t nInterval); + ~CFX_Timer(); + + bool HasValidID() const { +- return m_nTimerID != TimerHandlerIface::kInvalidTimerID; ++ return m_nTimerID != HandlerIface::kInvalidTimerID; + } + + private: + static void TimerProc(int32_t idEvent); + +- const int32_t m_nTimerID; +- UnownedPtr const m_pTimerHandler; ++ int32_t m_nTimerID = HandlerIface::kInvalidTimerID; ++ ObservedPtr m_pHandlerIface; + UnownedPtr const m_pCallbackIface; + }; + +diff --git a/core/fxcrt/cfx_timer_unittest.cpp b/core/fxcrt/cfx_timer_unittest.cpp +index b95de72ec..32220f03b 100644 +--- a/core/fxcrt/cfx_timer_unittest.cpp ++++ b/core/fxcrt/cfx_timer_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,17 +6,15 @@ + + #include + +-#include "core/fxcrt/timerhandler_iface.h" + #include "testing/gmock/include/gmock/gmock.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + + using testing::_; + using testing::DoAll; + using testing::Return; + using testing::SaveArg; + +-class MockTimerScheduler : public TimerHandlerIface { ++class MockTimerScheduler : public CFX_Timer::HandlerIface { + public: + MOCK_METHOD2(SetTimer, int(int32_t uElapse, TimerCallback lpTimerFunc)); + MOCK_METHOD1(KillTimer, void(int32_t nID)); +@@ -28,8 +26,8 @@ class MockTimerCallback : public CFX_Timer::CallbackIface { + }; + + TEST(CFX_Timer, ValidTimers) { +- TimerHandlerIface::TimerCallback fn1 = nullptr; +- TimerHandlerIface::TimerCallback fn2 = nullptr; ++ CFX_Timer::HandlerIface::TimerCallback fn1 = nullptr; ++ CFX_Timer::HandlerIface::TimerCallback fn2 = nullptr; + + MockTimerScheduler scheduler; + EXPECT_CALL(scheduler, SetTimer(100, _)) +@@ -45,8 +43,8 @@ TEST(CFX_Timer, ValidTimers) { + MockTimerCallback cb2; + EXPECT_CALL(cb2, OnTimerFired()).Times(2); + +- auto timer1 = pdfium::MakeUnique(&scheduler, &cb1, 100); +- auto timer2 = pdfium::MakeUnique(&scheduler, &cb2, 200); ++ auto timer1 = std::make_unique(&scheduler, &cb1, 100); ++ auto timer2 = std::make_unique(&scheduler, &cb2, 200); + EXPECT_TRUE(timer1->HasValidID()); + EXPECT_TRUE(timer2->HasValidID()); + +@@ -59,7 +57,7 @@ TEST(CFX_Timer, ValidTimers) { + } + + TEST(CFX_Timer, MisbehavingEmbedder) { +- TimerHandlerIface::TimerCallback fn1 = nullptr; ++ CFX_Timer::HandlerIface::TimerCallback fn1 = nullptr; + + MockTimerScheduler scheduler; + EXPECT_CALL(scheduler, SetTimer(100, _)) +@@ -70,7 +68,7 @@ TEST(CFX_Timer, MisbehavingEmbedder) { + EXPECT_CALL(cb1, OnTimerFired()).Times(0); + + { +- auto timer1 = pdfium::MakeUnique(&scheduler, &cb1, 100); ++ auto timer1 = std::make_unique(&scheduler, &cb1, 100); + EXPECT_TRUE(timer1->HasValidID()); + + // Fire callback with bad arguments. +diff --git a/core/fxcrt/cfx_utf8decoder.cpp b/core/fxcrt/cfx_utf8decoder.cpp +index 68ea30ac2..e834815b0 100644 +--- a/core/fxcrt/cfx_utf8decoder.cpp ++++ b/core/fxcrt/cfx_utf8decoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,18 +6,28 @@ + + #include "core/fxcrt/cfx_utf8decoder.h" + +-CFX_UTF8Decoder::CFX_UTF8Decoder() = default; ++#include ++ ++CFX_UTF8Decoder::CFX_UTF8Decoder(ByteStringView input) { ++ for (char c : input) { ++ ProcessByte(c); ++ } ++} + + CFX_UTF8Decoder::~CFX_UTF8Decoder() = default; + ++WideString CFX_UTF8Decoder::TakeResult() { ++ return std::move(m_Buffer); ++} ++ + void CFX_UTF8Decoder::AppendCodePoint(uint32_t ch) { +- m_Buffer.AppendChar(static_cast(ch)); ++ m_Buffer += static_cast(ch); + } + +-void CFX_UTF8Decoder::Input(uint8_t byte) { ++void CFX_UTF8Decoder::ProcessByte(uint8_t byte) { + if (byte < 0x80) { + m_PendingBytes = 0; +- m_Buffer.AppendChar(byte); ++ AppendCodePoint(byte); + } else if (byte < 0xc0) { + if (m_PendingBytes == 0) { + return; +diff --git a/core/fxcrt/cfx_utf8decoder.h b/core/fxcrt/cfx_utf8decoder.h +index a236aac63..35b56719b 100644 +--- a/core/fxcrt/cfx_utf8decoder.h ++++ b/core/fxcrt/cfx_utf8decoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,22 +7,23 @@ + #ifndef CORE_FXCRT_CFX_UTF8DECODER_H_ + #define CORE_FXCRT_CFX_UTF8DECODER_H_ + +-#include "core/fxcrt/cfx_widetextbuf.h" ++#include "core/fxcrt/string_view_template.h" ++#include "core/fxcrt/widestring.h" + + class CFX_UTF8Decoder { + public: +- CFX_UTF8Decoder(); ++ explicit CFX_UTF8Decoder(ByteStringView input); + ~CFX_UTF8Decoder(); + +- void Input(uint8_t byte); +- void AppendCodePoint(uint32_t ch); +- void ClearStatus() { m_PendingBytes = 0; } +- WideStringView GetResult() const { return m_Buffer.AsStringView(); } ++ WideString TakeResult(); + + private: ++ void ProcessByte(uint8_t byte); ++ void AppendCodePoint(uint32_t ch); ++ + int m_PendingBytes = 0; + uint32_t m_PendingChar = 0; +- CFX_WideTextBuf m_Buffer; ++ WideString m_Buffer; + }; + + #endif // CORE_FXCRT_CFX_UTF8DECODER_H_ +diff --git a/core/fxcrt/cfx_utf8encoder.cpp b/core/fxcrt/cfx_utf8encoder.cpp +index 9ed149f1a..4951126b3 100644 +--- a/core/fxcrt/cfx_utf8encoder.cpp ++++ b/core/fxcrt/cfx_utf8encoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcrt/cfx_utf8encoder.h b/core/fxcrt/cfx_utf8encoder.h +index 20afe4cfb..13815cd12 100644 +--- a/core/fxcrt/cfx_utf8encoder.h ++++ b/core/fxcrt/cfx_utf8encoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,10 @@ + #ifndef CORE_FXCRT_CFX_UTF8ENCODER_H_ + #define CORE_FXCRT_CFX_UTF8ENCODER_H_ + +-#include ++#include + +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/data_vector.h" + + class CFX_UTF8Encoder { + public: +@@ -26,7 +26,7 @@ class CFX_UTF8Encoder { + } + + private: +- std::vector> m_Buffer; ++ DataVector m_Buffer; + }; + + #endif // CORE_FXCRT_CFX_UTF8ENCODER_H_ +diff --git a/core/fxcrt/cfx_widetextbuf.cpp b/core/fxcrt/cfx_widetextbuf.cpp +deleted file mode 100644 +index dea620c19..000000000 +--- a/core/fxcrt/cfx_widetextbuf.cpp ++++ /dev/null +@@ -1,71 +0,0 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fxcrt/cfx_widetextbuf.h" +- +-size_t CFX_WideTextBuf::GetLength() const { +- return m_DataSize / sizeof(wchar_t); +-} +- +-void CFX_WideTextBuf::AppendChar(wchar_t ch) { +- ExpandBuf(sizeof(wchar_t)); +- *reinterpret_cast(m_pBuffer.get() + m_DataSize) = ch; +- m_DataSize += sizeof(wchar_t); +-} +- +-CFX_WideTextBuf& CFX_WideTextBuf::operator<<(ByteStringView ascii) { +- ExpandBuf(ascii.GetLength() * sizeof(wchar_t)); +- for (uint8_t ch : ascii) { +- *reinterpret_cast(m_pBuffer.get() + m_DataSize) = ch; +- m_DataSize += sizeof(wchar_t); +- } +- return *this; +-} +- +-CFX_WideTextBuf& CFX_WideTextBuf::operator<<(WideStringView str) { +- AppendBlock(str.unterminated_c_str(), str.GetLength() * sizeof(wchar_t)); +- return *this; +-} +- +-CFX_WideTextBuf& CFX_WideTextBuf::operator<<(const WideString& str) { +- AppendBlock(str.c_str(), str.GetLength() * sizeof(wchar_t)); +- return *this; +-} +- +-CFX_WideTextBuf& CFX_WideTextBuf::operator<<(int i) { +- char buf[32]; +- FXSYS_itoa(i, buf, 10); +- size_t len = strlen(buf); +- ExpandBuf(len * sizeof(wchar_t)); +- wchar_t* str = reinterpret_cast(m_pBuffer.get() + m_DataSize); +- for (size_t j = 0; j < len; j++) { +- *str++ = buf[j]; +- } +- m_DataSize += len * sizeof(wchar_t); +- return *this; +-} +- +-CFX_WideTextBuf& CFX_WideTextBuf::operator<<(double f) { +- char buf[32]; +- size_t len = FloatToString((float)f, buf); +- ExpandBuf(len * sizeof(wchar_t)); +- wchar_t* str = reinterpret_cast(m_pBuffer.get() + m_DataSize); +- for (size_t i = 0; i < len; i++) { +- *str++ = buf[i]; +- } +- m_DataSize += len * sizeof(wchar_t); +- return *this; +-} +- +-CFX_WideTextBuf& CFX_WideTextBuf::operator<<(const wchar_t* lpsz) { +- AppendBlock(lpsz, wcslen(lpsz) * sizeof(wchar_t)); +- return *this; +-} +- +-CFX_WideTextBuf& CFX_WideTextBuf::operator<<(const CFX_WideTextBuf& buf) { +- AppendBlock(buf.m_pBuffer.get(), buf.m_DataSize); +- return *this; +-} +diff --git a/core/fxcrt/cfx_widetextbuf.h b/core/fxcrt/cfx_widetextbuf.h +deleted file mode 100644 +index 0f8919709..000000000 +--- a/core/fxcrt/cfx_widetextbuf.h ++++ /dev/null +@@ -1,45 +0,0 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXCRT_CFX_WIDETEXTBUF_H_ +-#define CORE_FXCRT_CFX_WIDETEXTBUF_H_ +- +-#include "core/fxcrt/cfx_binarybuf.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" +- +-class CFX_WideTextBuf final : public CFX_BinaryBuf { +- public: +- void AppendChar(wchar_t wch); +- size_t GetLength() const override; +- wchar_t* GetBuffer() const { +- return reinterpret_cast(m_pBuffer.get()); +- } +- +- WideStringView AsStringView() const { +- return WideStringView(reinterpret_cast(m_pBuffer.get()), +- m_DataSize / sizeof(wchar_t)); +- } +- WideString MakeString() const { +- return WideString(reinterpret_cast(m_pBuffer.get()), +- m_DataSize / sizeof(wchar_t)); +- } +- +- void Delete(int start_index, int count) { +- CFX_BinaryBuf::Delete(start_index * sizeof(wchar_t), +- count * sizeof(wchar_t)); +- } +- +- CFX_WideTextBuf& operator<<(int i); +- CFX_WideTextBuf& operator<<(double f); +- CFX_WideTextBuf& operator<<(ByteStringView ascii); +- CFX_WideTextBuf& operator<<(const wchar_t* lpsz); +- CFX_WideTextBuf& operator<<(WideStringView str); +- CFX_WideTextBuf& operator<<(const WideString& str); +- CFX_WideTextBuf& operator<<(const CFX_WideTextBuf& buf); +-}; +- +-#endif // CORE_FXCRT_CFX_WIDETEXTBUF_H_ +diff --git a/core/fxcrt/cfx_widetextbuf_unittest.cpp b/core/fxcrt/cfx_widetextbuf_unittest.cpp +deleted file mode 100644 +index ddca23f0c..000000000 +--- a/core/fxcrt/cfx_widetextbuf_unittest.cpp ++++ /dev/null +@@ -1,45 +0,0 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#include "core/fxcrt/cfx_widetextbuf.h" +- +-#include "testing/gtest/include/gtest/gtest.h" +- +-namespace fxcrt { +- +-TEST(WideTextBuf, EmptyBuf) { +- CFX_WideTextBuf wtb; +- EXPECT_EQ(nullptr, wtb.GetBuffer()); +- EXPECT_TRUE(wtb.AsStringView().IsEmpty()); +- EXPECT_TRUE(wtb.MakeString().IsEmpty()); +-} +- +-TEST(WideTextBuf, OperatorLtLt) { +- CFX_WideTextBuf wtb; +- wtb << 42 << 3.14 << "clams" << L"\u208c\u208e"; +- EXPECT_TRUE(wtb.MakeString() == L"423.14clams\u208c\u208e"); +-} +- +-TEST(WideTextBuf, Deletion) { +- CFX_WideTextBuf wtb; +- wtb << L"ABCDEFG"; +- EXPECT_TRUE(wtb.AsStringView().EqualsASCII("ABCDEFG")); +- +- wtb.Delete(1, 3); +- EXPECT_TRUE(wtb.AsStringView().EqualsASCII("AEFG")); +- +- wtb.Delete(1, 0); +- EXPECT_TRUE(wtb.AsStringView().EqualsASCII("AEFG")); +- +- wtb.Delete(0, 2); +- EXPECT_TRUE(wtb.AsStringView().EqualsASCII("FG")); +- +- wtb.Delete(0, 2); +- EXPECT_TRUE(wtb.AsStringView().EqualsASCII("")); +- +- wtb.Delete(0, 0); +- EXPECT_TRUE(wtb.AsStringView().EqualsASCII("")); +-} +- +-} // namespace fxcrt +diff --git a/core/fxcrt/css/BUILD.gn b/core/fxcrt/css/BUILD.gn +index 66090c1b6..cd138b66a 100644 +--- a/core/fxcrt/css/BUILD.gn ++++ b/core/fxcrt/css/BUILD.gn +@@ -1,8 +1,9 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + + import("../../../pdfium.gni") ++import("../../../testing/test.gni") + + assert(pdf_enable_xfa) + +@@ -21,10 +22,12 @@ source_set("css") { + "cfx_cssdeclaration.h", + "cfx_cssenumvalue.cpp", + "cfx_cssenumvalue.h", +- "cfx_cssexttextbuf.cpp", +- "cfx_cssexttextbuf.h", ++ "cfx_cssinputtextbuf.cpp", ++ "cfx_cssinputtextbuf.h", + "cfx_cssnumbervalue.cpp", + "cfx_cssnumbervalue.h", ++ "cfx_cssoutputtextbuf.cpp", ++ "cfx_cssoutputtextbuf.h", + "cfx_csspropertyholder.cpp", + "cfx_csspropertyholder.h", + "cfx_cssrulecollection.cpp", +@@ -41,8 +44,6 @@ source_set("css") { + "cfx_cssstylesheet.h", + "cfx_csssyntaxparser.cpp", + "cfx_csssyntaxparser.h", +- "cfx_csstextbuf.cpp", +- "cfx_csstextbuf.h", + "cfx_cssvalue.cpp", + "cfx_cssvalue.h", + "cfx_cssvaluelist.cpp", +@@ -50,10 +51,25 @@ source_set("css") { + "cfx_cssvaluelistparser.cpp", + "cfx_cssvaluelistparser.h", + ] ++ configs += [ ++ "../../../:pdfium_strict_config", ++ "../../../:pdfium_noshorten_config", ++ ] + deps = [ + "../", + "../../fxge", + ] +- configs += [ "../../../:pdfium_core_config" ] + visibility = [ "../../../*" ] + } ++ ++pdfium_unittest_source_set("unittests") { ++ sources = [ ++ "cfx_cssdata_unittest.cpp", ++ "cfx_cssdeclaration_unittest.cpp", ++ "cfx_cssstylesheet_unittest.cpp", ++ "cfx_csssyntaxparser_unittest.cpp", ++ "cfx_cssvaluelistparser_unittest.cpp", ++ ] ++ pdfium_root_dir = "../../../" ++ deps = [ ":css" ] ++} +diff --git a/core/fxcrt/css/cfx_css.h b/core/fxcrt/css/cfx_css.h +index 30d9ff80f..1f71851dc 100644 +--- a/core/fxcrt/css/cfx_css.h ++++ b/core/fxcrt/css/cfx_css.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,6 +9,8 @@ + + #include + ++#include ++ + enum CFX_CSSVALUETYPE { + CFX_CSSVALUETYPE_Primitive = 1 << 0, + CFX_CSSVALUETYPE_List = 1 << 1, +@@ -19,112 +21,21 @@ enum CFX_CSSVALUETYPE { + CFX_CSSVALUETYPE_MaybeString = 1 << 7, + CFX_CSSVALUETYPE_MaybeColor = 1 << 8 + }; ++using CFX_CSSValueTypeMask = std::underlying_type::type; + +-enum class CFX_CSSPrimitiveType : uint8_t { +- Unknown = 0, +- Number, +- String, +- RGB, +- Enum, +- Function, +- List, ++#undef CSS_PROP____ ++#define CSS_PROP____(a, b, c, d) a, ++enum class CFX_CSSProperty : uint8_t { ++#include "core/fxcrt/css/properties.inc" + }; ++#undef CSS_PROP____ + +-// Any entries added/removed here, will need to be mirrored in +-// propertyValueTable, in core/fxcrt/css/cfx_cssdata.cpp. ++#undef CSS_PROP_VALUE____ ++#define CSS_PROP_VALUE____(a, b, c) a, + enum class CFX_CSSPropertyValue : uint8_t { +- Bolder = 0, +- None, +- Dot, +- Sub, +- Top, +- Right, +- Normal, +- Auto, +- Text, +- XSmall, +- Thin, +- Small, +- Bottom, +- Underline, +- Double, +- Lighter, +- Oblique, +- Super, +- Center, +- XxLarge, +- Smaller, +- Baseline, +- Thick, +- Justify, +- Middle, +- Medium, +- ListItem, +- XxSmall, +- Bold, +- SmallCaps, +- Inline, +- Overline, +- TextBottom, +- Larger, +- InlineTable, +- InlineBlock, +- Blink, +- Block, +- Italic, +- LineThrough, +- XLarge, +- Large, +- Left, +- TextTop, ++#include "core/fxcrt/css/property_values.inc" + }; +- +-// Any entries added/removed here, will need to be mirrored in +-// propertyTable, in core/fxcrt/css/cfx_cssdata.cpp. +-enum class CFX_CSSProperty : uint8_t { +- BorderLeft = 0, +- Top, +- Margin, +- TextIndent, +- Right, +- PaddingLeft, +- MarginLeft, +- Border, +- BorderTop, +- Bottom, +- PaddingRight, +- BorderBottom, +- FontFamily, +- FontWeight, +- Color, +- LetterSpacing, +- TextAlign, +- BorderRightWidth, +- VerticalAlign, +- PaddingTop, +- FontVariant, +- BorderWidth, +- BorderBottomWidth, +- BorderRight, +- FontSize, +- BorderSpacing, +- FontStyle, +- Font, +- LineHeight, +- MarginRight, +- BorderLeftWidth, +- Display, +- PaddingBottom, +- BorderTopWidth, +- WordSpacing, +- Left, +- TextDecoration, +- Padding, +- MarginBottom, +- MarginTop, +-}; +- +-enum class CFX_CSSSelectorType : uint8_t { Element = 0, Descendant }; ++#undef CSS_PROP_VALUE____ + + enum class CFX_CSSLengthUnit : uint8_t { + Auto, +@@ -173,18 +84,18 @@ enum class CFX_CSSFontVariant : uint8_t { + SmallCaps, + }; + +-enum CFX_CSSTEXTDECORATION { +- CFX_CSSTEXTDECORATION_None = 0, +- CFX_CSSTEXTDECORATION_Underline = 1 << 0, +- CFX_CSSTEXTDECORATION_Overline = 1 << 1, +- CFX_CSSTEXTDECORATION_LineThrough = 1 << 2, +- CFX_CSSTEXTDECORATION_Blink = 1 << 3, +- CFX_CSSTEXTDECORATION_Double = 1 << 4, ++enum class CFX_CSSTEXTDECORATION : uint8_t { ++ kNone = 0, ++ kUnderline = 1 << 0, ++ kOverline = 1 << 1, ++ kLineThrough = 1 << 2, ++ kBlink = 1 << 3, ++ kDouble = 1 << 4, + }; + + class CFX_CSSLength { + public: +- CFX_CSSLength() {} ++ CFX_CSSLength() = default; + + CFX_CSSLength(CFX_CSSLengthUnit eUnit, float fValue) + : m_unit(eUnit), m_fValue(fValue) {} +@@ -212,7 +123,7 @@ class CFX_CSSLength { + + class CFX_CSSRect { + public: +- CFX_CSSRect() {} ++ CFX_CSSRect() = default; + + CFX_CSSRect(CFX_CSSLengthUnit eUnit, float val) + : left(eUnit, val), +diff --git a/core/fxcrt/css/cfx_csscolorvalue.cpp b/core/fxcrt/css/cfx_csscolorvalue.cpp +index 8c5473b32..aba2fbf3f 100644 +--- a/core/fxcrt/css/cfx_csscolorvalue.cpp ++++ b/core/fxcrt/css/cfx_csscolorvalue.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,6 @@ + #include "core/fxcrt/css/cfx_csscolorvalue.h" + + CFX_CSSColorValue::CFX_CSSColorValue(FX_ARGB value) +- : CFX_CSSValue(CFX_CSSPrimitiveType::RGB), value_(value) {} ++ : CFX_CSSValue(PrimitiveType::kRGB), value_(value) {} + +-CFX_CSSColorValue::~CFX_CSSColorValue() {} ++CFX_CSSColorValue::~CFX_CSSColorValue() = default; +diff --git a/core/fxcrt/css/cfx_csscolorvalue.h b/core/fxcrt/css/cfx_csscolorvalue.h +index fdc3fcbe2..3e551fcd8 100644 +--- a/core/fxcrt/css/cfx_csscolorvalue.h ++++ b/core/fxcrt/css/cfx_csscolorvalue.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,7 +8,7 @@ + #define CORE_FXCRT_CSS_CFX_CSSCOLORVALUE_H_ + + #include "core/fxcrt/css/cfx_cssvalue.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" + + class CFX_CSSColorValue final : public CFX_CSSValue { + public: +diff --git a/core/fxcrt/css/cfx_csscomputedstyle.cpp b/core/fxcrt/css/cfx_csscomputedstyle.cpp +index 11ae90118..489eed1e0 100644 +--- a/core/fxcrt/css/cfx_csscomputedstyle.cpp ++++ b/core/fxcrt/css/cfx_csscomputedstyle.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,6 +8,7 @@ + + #include "core/fxcrt/css/cfx_cssstringvalue.h" + #include "core/fxcrt/css/cfx_cssvaluelist.h" ++#include "third_party/base/containers/adapters.h" + + CFX_CSSComputedStyle::CFX_CSSComputedStyle() = default; + +@@ -15,25 +16,24 @@ CFX_CSSComputedStyle::~CFX_CSSComputedStyle() = default; + + bool CFX_CSSComputedStyle::GetCustomStyle(const WideString& wsName, + WideString* pValue) const { +- for (auto iter = m_CustomProperties.rbegin(); +- iter != m_CustomProperties.rend(); ++iter) { +- if (wsName == iter->name()) { +- *pValue = iter->value(); ++ for (const auto& prop : pdfium::base::Reversed(m_CustomProperties)) { ++ if (wsName == prop.name()) { ++ *pValue = prop.value(); + return true; + } + } + return false; + } + +-int32_t CFX_CSSComputedStyle::CountFontFamilies() const { +- return m_InheritedData.m_pFontFamily +- ? m_InheritedData.m_pFontFamily->CountValues() +- : 0; +-} ++absl::optional CFX_CSSComputedStyle::GetLastFontFamily() const { ++ if (!m_InheritedData.m_pFontFamily || ++ m_InheritedData.m_pFontFamily->values().empty()) { ++ return absl::nullopt; ++ } + +-const WideString CFX_CSSComputedStyle::GetFontFamily(int32_t index) const { +- return m_InheritedData.m_pFontFamily->GetValue(index) +- .As() ++ return m_InheritedData.m_pFontFamily->values() ++ .back() ++ .AsRaw() + ->Value(); + } + +@@ -119,14 +119,14 @@ CFX_CSSTextAlign CFX_CSSComputedStyle::GetTextAlign() const { + } + + CFX_CSSVerticalAlign CFX_CSSComputedStyle::GetVerticalAlign() const { +- return m_NonInheritedData.m_eVerticalAlign; ++ return m_NonInheritedData.m_eVerticalAlignType; + } + + float CFX_CSSComputedStyle::GetNumberVerticalAlign() const { + return m_NonInheritedData.m_fVerticalAlign; + } + +-uint32_t CFX_CSSComputedStyle::GetTextDecoration() const { ++Mask CFX_CSSComputedStyle::GetTextDecoration() const { + return m_NonInheritedData.m_dwTextDecoration; + } + +@@ -147,11 +147,12 @@ void CFX_CSSComputedStyle::SetTextAlign(CFX_CSSTextAlign eTextAlign) { + } + + void CFX_CSSComputedStyle::SetNumberVerticalAlign(float fAlign) { +- m_NonInheritedData.m_eVerticalAlign = CFX_CSSVerticalAlign::Number, ++ m_NonInheritedData.m_eVerticalAlignType = CFX_CSSVerticalAlign::Number, + m_NonInheritedData.m_fVerticalAlign = fAlign; + } + +-void CFX_CSSComputedStyle::SetTextDecoration(uint32_t dwTextDecoration) { ++void CFX_CSSComputedStyle::SetTextDecoration( ++ Mask dwTextDecoration) { + m_NonInheritedData.m_dwTextDecoration = dwTextDecoration; + } + +@@ -166,29 +167,8 @@ void CFX_CSSComputedStyle::AddCustomStyle(const CFX_CSSCustomProperty& prop) { + m_CustomProperties.push_back(prop); + } + +-CFX_CSSComputedStyle::InheritedData::InheritedData() +- : m_LetterSpacing(CFX_CSSLengthUnit::Normal, 0), +- m_WordSpacing(CFX_CSSLengthUnit::Normal, 0), +- m_TextIndent(CFX_CSSLengthUnit::Point, 0), +- m_pFontFamily(nullptr), +- m_fFontSize(12.0f), +- m_fLineHeight(14.0f), +- m_dwFontColor(0xFF000000), +- m_wFontWeight(400), +- m_eFontVariant(CFX_CSSFontVariant::Normal), +- m_eFontStyle(CFX_CSSFontStyle::Normal), +- m_eTextAlign(CFX_CSSTextAlign::Left) {} +- +-CFX_CSSComputedStyle::InheritedData::~InheritedData() {} +- +-CFX_CSSComputedStyle::NonInheritedData::NonInheritedData() +- : m_MarginWidth(CFX_CSSLengthUnit::Point, 0), +- m_BorderWidth(CFX_CSSLengthUnit::Point, 0), +- m_PaddingWidth(CFX_CSSLengthUnit::Point, 0), +- m_fVerticalAlign(0.0f), +- m_eDisplay(CFX_CSSDisplay::Inline), +- m_eVerticalAlign(CFX_CSSVerticalAlign::Baseline), +- m_dwTextDecoration(0), +- m_bHasMargin(false), +- m_bHasBorder(false), +- m_bHasPadding(false) {} ++CFX_CSSComputedStyle::InheritedData::InheritedData() = default; ++ ++CFX_CSSComputedStyle::InheritedData::~InheritedData() = default; ++ ++CFX_CSSComputedStyle::NonInheritedData::NonInheritedData() = default; +diff --git a/core/fxcrt/css/cfx_csscomputedstyle.h b/core/fxcrt/css/cfx_csscomputedstyle.h +index d4959eef0..9e685c4b1 100644 +--- a/core/fxcrt/css/cfx_csscomputedstyle.h ++++ b/core/fxcrt/css/cfx_csscomputedstyle.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,8 +11,11 @@ + + #include "core/fxcrt/css/cfx_css.h" + #include "core/fxcrt/css/cfx_csscustomproperty.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxcrt/mask.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxcrt/widestring.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CFX_CSSValueList; + +@@ -23,44 +26,42 @@ class CFX_CSSComputedStyle final : public Retainable { + InheritedData(); + ~InheritedData(); + +- CFX_CSSLength m_LetterSpacing; +- CFX_CSSLength m_WordSpacing; +- CFX_CSSLength m_TextIndent; ++ CFX_CSSLength m_LetterSpacing{CFX_CSSLengthUnit::Normal, 0}; ++ CFX_CSSLength m_WordSpacing{CFX_CSSLengthUnit::Normal, 0}; ++ CFX_CSSLength m_TextIndent{CFX_CSSLengthUnit::Point, 0}; + RetainPtr m_pFontFamily; +- float m_fFontSize; +- float m_fLineHeight; +- FX_ARGB m_dwFontColor; +- uint16_t m_wFontWeight; +- CFX_CSSFontVariant m_eFontVariant; +- CFX_CSSFontStyle m_eFontStyle; +- CFX_CSSTextAlign m_eTextAlign; ++ float m_fFontSize = 12.0f; ++ float m_fLineHeight = 14.0f; ++ FX_ARGB m_dwFontColor = 0xFF000000; ++ uint16_t m_wFontWeight = 400; ++ CFX_CSSFontVariant m_eFontVariant = CFX_CSSFontVariant::Normal; ++ CFX_CSSFontStyle m_eFontStyle = CFX_CSSFontStyle::Normal; ++ CFX_CSSTextAlign m_eTextAlign = CFX_CSSTextAlign::Left; + }; + + class NonInheritedData { + public: + NonInheritedData(); + +- CFX_CSSRect m_MarginWidth; +- CFX_CSSRect m_BorderWidth; +- CFX_CSSRect m_PaddingWidth; ++ CFX_CSSRect m_MarginWidth{CFX_CSSLengthUnit::Point, 0}; ++ CFX_CSSRect m_BorderWidth{CFX_CSSLengthUnit::Point, 0}; ++ CFX_CSSRect m_PaddingWidth{CFX_CSSLengthUnit::Point, 0}; + CFX_CSSLength m_Top; + CFX_CSSLength m_Bottom; + CFX_CSSLength m_Left; + CFX_CSSLength m_Right; +- float m_fVerticalAlign; +- CFX_CSSDisplay m_eDisplay; +- CFX_CSSVerticalAlign m_eVerticalAlign; +- uint8_t m_dwTextDecoration; +- bool m_bHasMargin; +- bool m_bHasBorder; +- bool m_bHasPadding; ++ float m_fVerticalAlign = 0.0f; ++ CFX_CSSDisplay m_eDisplay = CFX_CSSDisplay::Inline; ++ CFX_CSSVerticalAlign m_eVerticalAlignType = CFX_CSSVerticalAlign::Baseline; ++ Mask m_dwTextDecoration; ++ bool m_bHasMargin = false; ++ bool m_bHasBorder = false; ++ bool m_bHasPadding = false; + }; + +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + +- int32_t CountFontFamilies() const; +- const WideString GetFontFamily(int32_t index) const; ++ absl::optional GetLastFontFamily() const; + uint16_t GetFontWeight() const; + CFX_CSSFontVariant GetFontVariant() const; + CFX_CSSFontStyle GetFontStyle() const; +@@ -85,13 +86,13 @@ class CFX_CSSComputedStyle final : public Retainable { + CFX_CSSTextAlign GetTextAlign() const; + CFX_CSSVerticalAlign GetVerticalAlign() const; + float GetNumberVerticalAlign() const; +- uint32_t GetTextDecoration() const; ++ Mask GetTextDecoration() const; + const CFX_CSSLength& GetLetterSpacing() const; + void SetLineHeight(float fLineHeight); + void SetTextIndent(const CFX_CSSLength& textIndent); + void SetTextAlign(CFX_CSSTextAlign eTextAlign); + void SetNumberVerticalAlign(float fAlign); +- void SetTextDecoration(uint32_t dwTextDecoration); ++ void SetTextDecoration(Mask dwTextDecoration); + void SetLetterSpacing(const CFX_CSSLength& letterSpacing); + void AddCustomStyle(const CFX_CSSCustomProperty& prop); + +diff --git a/core/fxcrt/css/cfx_csscustomproperty.cpp b/core/fxcrt/css/cfx_csscustomproperty.cpp +index 353facd5f..f07887b3c 100644 +--- a/core/fxcrt/css/cfx_csscustomproperty.cpp ++++ b/core/fxcrt/css/cfx_csscustomproperty.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,4 +11,4 @@ CFX_CSSCustomProperty::CFX_CSSCustomProperty(const WideString& name, + CFX_CSSCustomProperty::CFX_CSSCustomProperty(const CFX_CSSCustomProperty& prop) + : name_(prop.name_), value_(prop.value_) {} + +-CFX_CSSCustomProperty::~CFX_CSSCustomProperty() {} ++CFX_CSSCustomProperty::~CFX_CSSCustomProperty() = default; +diff --git a/core/fxcrt/css/cfx_csscustomproperty.h b/core/fxcrt/css/cfx_csscustomproperty.h +index 15c3eca12..450c2b1a1 100644 +--- a/core/fxcrt/css/cfx_csscustomproperty.h ++++ b/core/fxcrt/css/cfx_csscustomproperty.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,7 @@ + #ifndef CORE_FXCRT_CSS_CFX_CSSCUSTOMPROPERTY_H_ + #define CORE_FXCRT_CSS_CFX_CSSCUSTOMPROPERTY_H_ + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" + + class CFX_CSSCustomProperty { + public: +diff --git a/core/fxcrt/css/cfx_cssdata.cpp b/core/fxcrt/css/cfx_cssdata.cpp +index cfef22082..15cd8eafb 100644 +--- a/core/fxcrt/css/cfx_cssdata.cpp ++++ b/core/fxcrt/css/cfx_cssdata.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,252 +16,29 @@ + + namespace { + +-#undef PROP +-#define PROP(a, b, c, d) a, c, d +- ++#undef CSS_PROP____ ++#define CSS_PROP____(a, b, c, d) {CFX_CSSProperty::a, c, d}, + const CFX_CSSData::Property propertyTable[] = { +- {PROP(CFX_CSSProperty::BorderLeft, +- "border-left", +- 0x04080036, +- CFX_CSSVALUETYPE_Shorthand)}, +- {PROP(CFX_CSSProperty::Top, +- "top", +- 0x0BEDAF33, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::Margin, +- "margin", +- 0x0CB016BE, +- CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::TextIndent, +- "text-indent", +- 0x169ADB74, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::Right, +- "right", +- 0x193ADE3E, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::PaddingLeft, +- "padding-left", +- 0x228CF02F, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::MarginLeft, +- "margin-left", +- 0x297C5656, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber | +- CFX_CSSVALUETYPE_MaybeEnum)}, +- {PROP(CFX_CSSProperty::Border, +- "border", +- 0x2A23349E, +- CFX_CSSVALUETYPE_Shorthand)}, +- {PROP(CFX_CSSProperty::BorderTop, +- "border-top", +- 0x2B866ADE, +- CFX_CSSVALUETYPE_Shorthand)}, +- {PROP(CFX_CSSProperty::Bottom, +- "bottom", +- 0x399F02B5, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::PaddingRight, +- "padding-right", +- 0x3F616AC2, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::BorderBottom, +- "border-bottom", +- 0x452CE780, +- CFX_CSSVALUETYPE_Shorthand)}, +- {PROP(CFX_CSSProperty::FontFamily, +- "font-family", +- 0x574686E6, +- CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeString)}, +- {PROP(CFX_CSSProperty::FontWeight, +- "font-weight", +- 0x6692F60C, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::Color, +- "color", +- 0x6E67921F, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeColor)}, +- {PROP(CFX_CSSProperty::LetterSpacing, +- "letter-spacing", +- 0x70536102, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::TextAlign, +- "text-align", +- 0x7553F1BD, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum)}, +- {PROP(CFX_CSSProperty::BorderRightWidth, +- "border-right-width", +- 0x8F5A6036, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::VerticalAlign, +- "vertical-align", +- 0x934A87D2, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::PaddingTop, +- "padding-top", +- 0x959D22B7, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::FontVariant, +- "font-variant", +- 0x9C785779, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum)}, +- {PROP(CFX_CSSProperty::BorderWidth, +- "border-width", +- 0xA8DE4FEB, +- CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::BorderBottomWidth, +- "border-bottom-width", +- 0xAE41204D, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::BorderRight, +- "border-right", +- 0xB78E9EA9, +- CFX_CSSVALUETYPE_Shorthand)}, +- {PROP(CFX_CSSProperty::FontSize, +- "font-size", +- 0xB93956DF, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::BorderSpacing, +- "border-spacing", +- 0xC72030F0, +- CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::FontStyle, +- "font-style", +- 0xCB1950F5, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum)}, +- {PROP(CFX_CSSProperty::Font, +- "font", +- 0xCD308B77, +- CFX_CSSVALUETYPE_Shorthand)}, +- {PROP(CFX_CSSProperty::LineHeight, +- "line-height", +- 0xCFCACE2E, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::MarginRight, +- "margin-right", +- 0xD13C58C9, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber | +- CFX_CSSVALUETYPE_MaybeEnum)}, +- {PROP(CFX_CSSProperty::BorderLeftWidth, +- "border-left-width", +- 0xD1E93D83, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::Display, +- "display", +- 0xD4224C36, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum)}, +- {PROP(CFX_CSSProperty::PaddingBottom, +- "padding-bottom", +- 0xE555B3B9, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::BorderTopWidth, +- "border-top-width", +- 0xED2CB62B, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::WordSpacing, +- "word-spacing", +- 0xEDA63BAE, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::Left, +- "left", +- 0xF5AD782B, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | +- CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::TextDecoration, +- "text-decoration", +- 0xF7C634BA, +- CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum)}, +- {PROP(CFX_CSSProperty::Padding, +- "padding", +- 0xF8C373F7, +- CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeNumber)}, +- {PROP(CFX_CSSProperty::MarginBottom, +- "margin-bottom", +- 0xF93485A0, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber | +- CFX_CSSVALUETYPE_MaybeEnum)}, +- {PROP(CFX_CSSProperty::MarginTop, +- "margin-top", +- 0xFE51DCFE, +- CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber | +- CFX_CSSVALUETYPE_MaybeEnum)}, ++#include "core/fxcrt/css/properties.inc" + }; ++#undef CSS_PROP____ + +-#undef PROP +- +-#undef PVAL +-#define PVAL(a, b, c) a, c +- ++#undef CSS_PROP_VALUE____ ++#define CSS_PROP_VALUE____(a, b, c) {CFX_CSSPropertyValue::a, c}, + const CFX_CSSData::PropertyValue propertyValueTable[] = { +- {PVAL(CFX_CSSPropertyValue::Bolder, "bolder", 0x009F1058)}, +- {PVAL(CFX_CSSPropertyValue::None, "none", 0x048B6670)}, +- {PVAL(CFX_CSSPropertyValue::Dot, "dot", 0x0A48CB27)}, +- {PVAL(CFX_CSSPropertyValue::Sub, "sub", 0x0BD37FAA)}, +- {PVAL(CFX_CSSPropertyValue::Top, "top", 0x0BEDAF33)}, +- {PVAL(CFX_CSSPropertyValue::Right, "right", 0x193ADE3E)}, +- {PVAL(CFX_CSSPropertyValue::Normal, "normal", 0x247CF3E9)}, +- {PVAL(CFX_CSSPropertyValue::Auto, "auto", 0x2B35B6D9)}, +- {PVAL(CFX_CSSPropertyValue::Text, "text", 0x2D08AF85)}, +- {PVAL(CFX_CSSPropertyValue::XSmall, "x-small", 0x2D2FCAFE)}, +- {PVAL(CFX_CSSPropertyValue::Thin, "thin", 0x2D574D53)}, +- {PVAL(CFX_CSSPropertyValue::Small, "small", 0x316A3739)}, +- {PVAL(CFX_CSSPropertyValue::Bottom, "bottom", 0x399F02B5)}, +- {PVAL(CFX_CSSPropertyValue::Underline, "underline", 0x3A0273A6)}, +- {PVAL(CFX_CSSPropertyValue::Double, "double", 0x3D98515B)}, +- {PVAL(CFX_CSSPropertyValue::Lighter, "lighter", 0x45BEB7AF)}, +- {PVAL(CFX_CSSPropertyValue::Oblique, "oblique", 0x53EBDDB1)}, +- {PVAL(CFX_CSSPropertyValue::Super, "super", 0x6A4F842F)}, +- {PVAL(CFX_CSSPropertyValue::Center, "center", 0x6C51AFC1)}, +- {PVAL(CFX_CSSPropertyValue::XxLarge, "xx-large", 0x70BB1508)}, +- {PVAL(CFX_CSSPropertyValue::Smaller, "smaller", 0x849769F0)}, +- {PVAL(CFX_CSSPropertyValue::Baseline, "baseline", 0x87436BA3)}, +- {PVAL(CFX_CSSPropertyValue::Thick, "thick", 0x8CC35EB3)}, +- {PVAL(CFX_CSSPropertyValue::Justify, "justify", 0x8D269CAE)}, +- {PVAL(CFX_CSSPropertyValue::Middle, "middle", 0x947FA00F)}, +- {PVAL(CFX_CSSPropertyValue::Medium, "medium", 0xA084A381)}, +- {PVAL(CFX_CSSPropertyValue::ListItem, "list-item", 0xA32382B8)}, +- {PVAL(CFX_CSSPropertyValue::XxSmall, "xx-small", 0xADE1FC76)}, +- {PVAL(CFX_CSSPropertyValue::Bold, "bold", 0xB18313A1)}, +- {PVAL(CFX_CSSPropertyValue::SmallCaps, "small-caps", 0xB299428D)}, +- {PVAL(CFX_CSSPropertyValue::Inline, "inline", 0xC02D649F)}, +- {PVAL(CFX_CSSPropertyValue::Overline, "overline", 0xC0EC9FA4)}, +- {PVAL(CFX_CSSPropertyValue::TextBottom, "text-bottom", 0xC7D08D87)}, +- {PVAL(CFX_CSSPropertyValue::Larger, "larger", 0xCD3C409D)}, +- {PVAL(CFX_CSSPropertyValue::InlineTable, "inline-table", 0xD131F494)}, +- {PVAL(CFX_CSSPropertyValue::InlineBlock, "inline-block", 0xD26A8BD7)}, +- {PVAL(CFX_CSSPropertyValue::Blink, "blink", 0xDC36E390)}, +- {PVAL(CFX_CSSPropertyValue::Block, "block", 0xDCD480AB)}, +- {PVAL(CFX_CSSPropertyValue::Italic, "italic", 0xE31D5396)}, +- {PVAL(CFX_CSSPropertyValue::LineThrough, "line-through", 0xE4C5A276)}, +- {PVAL(CFX_CSSPropertyValue::XLarge, "x-large", 0xF008E390)}, +- {PVAL(CFX_CSSPropertyValue::Large, "large", 0xF4434FCB)}, +- {PVAL(CFX_CSSPropertyValue::Left, "left", 0xF5AD782B)}, +- {PVAL(CFX_CSSPropertyValue::TextTop, "text-top", 0xFCB58D45)}, ++#include "core/fxcrt/css/property_values.inc" + }; +- +-#undef PVAL ++#undef CSS_PROP_VALUE____ + + const CFX_CSSData::LengthUnit lengthUnitTable[] = { +- {L"cm", CFX_CSSNumberType::CentiMeters}, {L"em", CFX_CSSNumberType::EMS}, +- {L"ex", CFX_CSSNumberType::EXS}, {L"in", CFX_CSSNumberType::Inches}, +- {L"mm", CFX_CSSNumberType::MilliMeters}, {L"pc", CFX_CSSNumberType::Picas}, +- {L"pt", CFX_CSSNumberType::Points}, {L"px", CFX_CSSNumberType::Pixels}, ++ {L"cm", CFX_CSSNumberValue::Unit::kCentiMeters}, ++ {L"em", CFX_CSSNumberValue::Unit::kEMS}, ++ {L"ex", CFX_CSSNumberValue::Unit::kEXS}, ++ {L"in", CFX_CSSNumberValue::Unit::kInches}, ++ {L"mm", CFX_CSSNumberValue::Unit::kMilliMeters}, ++ {L"pc", CFX_CSSNumberValue::Unit::kPicas}, ++ {L"pt", CFX_CSSNumberValue::Unit::kPoints}, ++ {L"px", CFX_CSSNumberValue::Unit::kPixels}, + }; + + // 16 colours from CSS 2.0 + alternate spelling of grey/gray. +@@ -281,7 +58,7 @@ const CFX_CSSData::Property* CFX_CSSData::GetPropertyByName( + if (name.IsEmpty()) + return nullptr; + +- uint32_t hash = FX_HashCode_GetW(name, true); ++ uint32_t hash = FX_HashCode_GetLoweredW(name); + auto* result = + std::lower_bound(std::begin(propertyTable), std::end(propertyTable), hash, + [](const CFX_CSSData::Property& iter, +@@ -302,7 +79,7 @@ const CFX_CSSData::PropertyValue* CFX_CSSData::GetPropertyValueByName( + if (wsName.IsEmpty()) + return nullptr; + +- uint32_t hash = FX_HashCode_GetW(wsName, true); ++ uint32_t hash = FX_HashCode_GetLoweredW(wsName); + auto* result = std::lower_bound( + std::begin(propertyValueTable), std::end(propertyValueTable), hash, + [](const PropertyValue& iter, const uint32_t& hash) { +@@ -324,7 +101,7 @@ const CFX_CSSData::LengthUnit* CFX_CSSData::GetLengthUnitByName( + + for (auto* iter = std::begin(lengthUnitTable); + iter != std::end(lengthUnitTable); ++iter) { +- if (lowerName.Compare(iter->value) == 0) ++ if (lowerName == iter->value) + return iter; + } + +@@ -340,7 +117,7 @@ const CFX_CSSData::Color* CFX_CSSData::GetColorByName(WideStringView wsName) { + + for (auto* iter = std::begin(colorTable); iter != std::end(colorTable); + ++iter) { +- if (lowerName.Compare(iter->name) == 0) ++ if (lowerName == iter->name) + return iter; + } + return nullptr; +diff --git a/core/fxcrt/css/cfx_cssdata.h b/core/fxcrt/css/cfx_cssdata.h +index d5b61511d..c129cfeef 100644 +--- a/core/fxcrt/css/cfx_cssdata.h ++++ b/core/fxcrt/css/cfx_cssdata.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,15 +10,15 @@ + #include "core/fxcrt/css/cfx_css.h" + #include "core/fxcrt/css/cfx_cssnumbervalue.h" + #include "core/fxcrt/css/cfx_cssvalue.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxcrt/widestring.h" ++#include "core/fxge/dib/fx_dib.h" + + class CFX_CSSData { + public: + struct Property { + CFX_CSSProperty eName; + uint32_t dwHash; // Hashed as wide string. +- uint32_t dwType; ++ CFX_CSSValueTypeMask dwTypes; + }; + + struct PropertyValue { +@@ -27,12 +27,12 @@ class CFX_CSSData { + }; + + struct LengthUnit { +- const wchar_t* value; +- CFX_CSSNumberType type; ++ const wchar_t* value; // Raw, POD struct. ++ CFX_CSSNumberValue::Unit type; + }; + + struct Color { +- const wchar_t* name; ++ const wchar_t* name; // Raw, POD struct. + FX_ARGB value; + }; + +diff --git a/core/fxcrt/css/cfx_cssdata_unittest.cpp b/core/fxcrt/css/cfx_cssdata_unittest.cpp +new file mode 100644 +index 000000000..9725b436e +--- /dev/null ++++ b/core/fxcrt/css/cfx_cssdata_unittest.cpp +@@ -0,0 +1,34 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/css/cfx_cssdata.h" ++ ++#include "core/fxcrt/bytestring.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++TEST(CSSDataTest, PropertyHashes) { ++ uint32_t max_hash = 0; ++#undef CSS_PROP____ ++#define CSS_PROP____(a, b, c, d) \ ++ { \ ++ EXPECT_EQ(FX_HashCode_GetAsIfW(b), static_cast(c)) << b; \ ++ EXPECT_GT(static_cast(c), max_hash) << b; \ ++ max_hash = c; \ ++ } ++#include "core/fxcrt/css/properties.inc" ++#undef CSS_PROP____ ++} ++ ++TEST(CSSDataTest, PropertyValueHashes) { ++ uint32_t max_hash = 0; ++#undef CSS_PROP_VALUE____ ++#define CSS_PROP_VALUE____(a, b, c) \ ++ { \ ++ EXPECT_EQ(FX_HashCode_GetAsIfW(b), static_cast(c)) << b; \ ++ EXPECT_GT(static_cast(c), max_hash) << b; \ ++ max_hash = c; \ ++ } ++#include "core/fxcrt/css/property_values.inc" ++#undef CSS_PROP_VALUE____ ++} +diff --git a/core/fxcrt/css/cfx_cssdeclaration.cpp b/core/fxcrt/css/cfx_cssdeclaration.cpp +index de97b64bb..e1bdd0e38 100644 +--- a/core/fxcrt/css/cfx_cssdeclaration.cpp ++++ b/core/fxcrt/css/cfx_cssdeclaration.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,8 @@ + + #include "core/fxcrt/css/cfx_cssdeclaration.h" + +-#include ++#include ++ + #include + + #include "core/fxcrt/css/cfx_csscolorvalue.h" +@@ -18,8 +19,10 @@ + #include "core/fxcrt/css/cfx_cssvaluelist.h" + #include "core/fxcrt/css/cfx_cssvaluelistparser.h" + #include "core/fxcrt/fx_extension.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/ptr_util.h" ++#include "core/fxcrt/fx_system.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/notreached.h" + + namespace { + +@@ -28,23 +31,23 @@ uint8_t Hex2Dec(uint8_t hexHigh, uint8_t hexLow) { + } + + bool ParseCSSNumber(const wchar_t* pszValue, +- int32_t iValueLen, ++ size_t nValueLen, + float* pValue, +- CFX_CSSNumberType* pOutUnit) { +- ASSERT(pszValue); +- ASSERT(iValueLen > 0); ++ CFX_CSSNumberValue::Unit* pOutUnit) { ++ DCHECK(pszValue); ++ DCHECK_NE(nValueLen, 0); + +- int32_t iUsedLen = 0; +- *pValue = FXSYS_wcstof(pszValue, iValueLen, &iUsedLen); +- if (iUsedLen <= 0 || !std::isfinite(*pValue)) ++ size_t nUsedLen = 0; ++ *pValue = FXSYS_wcstof(pszValue, nValueLen, &nUsedLen); ++ if (nUsedLen == 0 || !isfinite(*pValue)) + return false; + +- iValueLen -= iUsedLen; +- pszValue += iUsedLen; +- *pOutUnit = CFX_CSSNumberType::Number; +- if (iValueLen >= 1 && *pszValue == '%') { +- *pOutUnit = CFX_CSSNumberType::Percent; +- } else if (iValueLen == 2) { ++ nValueLen -= nUsedLen; ++ pszValue += nUsedLen; ++ *pOutUnit = CFX_CSSNumberValue::Unit::kNumber; ++ if (nValueLen >= 1 && *pszValue == '%') { ++ *pOutUnit = CFX_CSSNumberValue::Unit::kPercent; ++ } else if (nValueLen == 2) { + const CFX_CSSData::LengthUnit* pUnit = + CFX_CSSData::GetLengthUnitByName(WideStringView(pszValue, 2)); + if (pUnit) +@@ -57,34 +60,34 @@ bool ParseCSSNumber(const wchar_t* pszValue, + + // static + bool CFX_CSSDeclaration::ParseCSSString(const wchar_t* pszValue, +- int32_t iValueLen, +- int32_t* iOffset, +- int32_t* iLength) { +- ASSERT(pszValue); +- ASSERT(iValueLen > 0); +- +- *iOffset = 0; +- *iLength = iValueLen; +- if (iValueLen >= 2) { +- wchar_t first = pszValue[0], last = pszValue[iValueLen - 1]; ++ size_t nValueLen, ++ size_t* nOffset, ++ size_t* nLength) { ++ DCHECK(pszValue); ++ DCHECK_NE(nValueLen, 0); ++ ++ *nOffset = 0; ++ *nLength = nValueLen; ++ if (nValueLen >= 2) { ++ wchar_t first = pszValue[0]; ++ wchar_t last = pszValue[nValueLen - 1]; + if ((first == '\"' && last == '\"') || (first == '\'' && last == '\'')) { +- *iOffset = 1; +- *iLength -= 2; ++ *nOffset = 1; ++ *nLength -= 2; + } + } +- return iValueLen > 0; ++ return nValueLen > 0; + } + + // static. + bool CFX_CSSDeclaration::ParseCSSColor(const wchar_t* pszValue, +- int32_t iValueLen, ++ size_t nValueLen, + FX_ARGB* dwColor) { +- ASSERT(pszValue); +- ASSERT(iValueLen > 0); +- ASSERT(dwColor); ++ DCHECK_NE(nValueLen, 0); ++ DCHECK(dwColor); + + if (*pszValue == '#') { +- switch (iValueLen) { ++ switch (nValueLen) { + case 4: { + uint8_t red = Hex2Dec((uint8_t)pszValue[1], (uint8_t)pszValue[1]); + uint8_t green = Hex2Dec((uint8_t)pszValue[2], (uint8_t)pszValue[2]); +@@ -104,24 +107,24 @@ bool CFX_CSSDeclaration::ParseCSSColor(const wchar_t* pszValue, + } + } + +- if (iValueLen >= 10) { +- if (pszValue[iValueLen - 1] != ')' || FXSYS_wcsnicmp(L"rgb(", pszValue, 4)) ++ if (nValueLen >= 10) { ++ if (pszValue[nValueLen - 1] != ')' || FXSYS_wcsnicmp(L"rgb(", pszValue, 4)) + return false; + + uint8_t rgb[3] = {0}; + float fValue; +- CFX_CSSPrimitiveType eType; +- CFX_CSSValueListParser list(pszValue + 4, iValueLen - 5, ','); ++ CFX_CSSValue::PrimitiveType eType; ++ CFX_CSSValueListParser list(pszValue + 4, nValueLen - 5, ','); + for (int32_t i = 0; i < 3; ++i) { +- if (!list.NextValue(&eType, &pszValue, &iValueLen)) ++ if (!list.NextValue(&eType, &pszValue, &nValueLen)) + return false; +- if (eType != CFX_CSSPrimitiveType::Number) ++ if (eType != CFX_CSSValue::PrimitiveType::kNumber) + return false; +- CFX_CSSNumberType eNumType; +- if (!ParseCSSNumber(pszValue, iValueLen, &fValue, &eNumType)) ++ CFX_CSSNumberValue::Unit eNumType; ++ if (!ParseCSSNumber(pszValue, nValueLen, &fValue, &eNumType)) + return false; + +- rgb[i] = eNumType == CFX_CSSNumberType::Percent ++ rgb[i] = eNumType == CFX_CSSNumberValue::Unit::kPercent + ? FXSYS_roundf(fValue * 2.55f) + : FXSYS_roundf(fValue); + } +@@ -130,7 +133,7 @@ bool CFX_CSSDeclaration::ParseCSSColor(const wchar_t* pszValue, + } + + const CFX_CSSData::Color* pColor = +- CFX_CSSData::GetColorByName(WideStringView(pszValue, iValueLen)); ++ CFX_CSSData::GetColorByName(WideStringView(pszValue, nValueLen)); + if (!pColor) + return false; + +@@ -138,9 +141,9 @@ bool CFX_CSSDeclaration::ParseCSSColor(const wchar_t* pszValue, + return true; + } + +-CFX_CSSDeclaration::CFX_CSSDeclaration() {} ++CFX_CSSDeclaration::CFX_CSSDeclaration() = default; + +-CFX_CSSDeclaration::~CFX_CSSDeclaration() {} ++CFX_CSSDeclaration::~CFX_CSSDeclaration() = default; + + RetainPtr CFX_CSSDeclaration::GetProperty( + CFX_CSSProperty eProperty, +@@ -157,7 +160,7 @@ RetainPtr CFX_CSSDeclaration::GetProperty( + void CFX_CSSDeclaration::AddPropertyHolder(CFX_CSSProperty eProperty, + RetainPtr pValue, + bool bImportant) { +- auto pHolder = pdfium::MakeUnique(); ++ auto pHolder = std::make_unique(); + pHolder->bImportant = bImportant; + pHolder->eProperty = eProperty; + pHolder->pValue = pValue; +@@ -166,19 +169,20 @@ void CFX_CSSDeclaration::AddPropertyHolder(CFX_CSSProperty eProperty, + + void CFX_CSSDeclaration::AddProperty(const CFX_CSSData::Property* property, + WideStringView value) { +- ASSERT(!value.IsEmpty()); ++ DCHECK(!value.IsEmpty()); + + const wchar_t* pszValue = value.unterminated_c_str(); +- int32_t iValueLen = value.GetLength(); ++ size_t nValueLen = value.GetLength(); + bool bImportant = false; +- if (iValueLen >= 10 && pszValue[iValueLen - 10] == '!' && +- FXSYS_wcsnicmp(L"important", pszValue + iValueLen - 9, 9) == 0) { +- if ((iValueLen -= 10) == 0) ++ if (nValueLen >= 10 && pszValue[nValueLen - 10] == '!' && ++ FXSYS_wcsnicmp(L"important", pszValue + nValueLen - 9, 9) == 0) { ++ nValueLen -= 10; ++ if (nValueLen == 0) + return; + + bImportant = true; + } +- const uint32_t dwType = property->dwType; ++ const CFX_CSSValueTypeMask dwType = property->dwTypes; + switch (dwType & 0x0F) { + case CFX_CSSVALUETYPE_Primitive: { + static constexpr CFX_CSSVALUETYPE kValueGuessOrder[] = { +@@ -187,24 +191,24 @@ void CFX_CSSDeclaration::AddProperty(const CFX_CSSData::Property* property, + CFX_CSSVALUETYPE_MaybeColor, + CFX_CSSVALUETYPE_MaybeString, + }; +- for (uint32_t guess : kValueGuessOrder) { +- const uint32_t dwMatch = dwType & guess; ++ for (CFX_CSSVALUETYPE guess : kValueGuessOrder) { ++ const CFX_CSSValueTypeMask dwMatch = dwType & guess; + if (dwMatch == 0) + continue; + + RetainPtr pCSSValue; + switch (dwMatch) { + case CFX_CSSVALUETYPE_MaybeNumber: +- pCSSValue = ParseNumber(pszValue, iValueLen); ++ pCSSValue = ParseNumber(pszValue, nValueLen); + break; + case CFX_CSSVALUETYPE_MaybeEnum: +- pCSSValue = ParseEnum(pszValue, iValueLen); ++ pCSSValue = ParseEnum(pszValue, nValueLen); + break; + case CFX_CSSVALUETYPE_MaybeColor: +- pCSSValue = ParseColor(pszValue, iValueLen); ++ pCSSValue = ParseColor(pszValue, nValueLen); + break; + case CFX_CSSVALUETYPE_MaybeString: +- pCSSValue = ParseString(pszValue, iValueLen); ++ pCSSValue = ParseString(pszValue, nValueLen); + break; + default: + break; +@@ -223,10 +227,10 @@ void CFX_CSSDeclaration::AddProperty(const CFX_CSSData::Property* property, + RetainPtr pWidth; + switch (property->eName) { + case CFX_CSSProperty::Font: +- ParseFontProperty(pszValue, iValueLen, bImportant); ++ ParseFontProperty(pszValue, nValueLen, bImportant); + return; + case CFX_CSSProperty::Border: +- if (ParseBorderProperty(pszValue, iValueLen, pWidth)) { ++ if (ParseBorderProperty(pszValue, nValueLen, pWidth)) { + AddPropertyHolder(CFX_CSSProperty::BorderLeftWidth, pWidth, + bImportant); + AddPropertyHolder(CFX_CSSProperty::BorderTopWidth, pWidth, +@@ -239,28 +243,28 @@ void CFX_CSSDeclaration::AddProperty(const CFX_CSSData::Property* property, + } + break; + case CFX_CSSProperty::BorderLeft: +- if (ParseBorderProperty(pszValue, iValueLen, pWidth)) { ++ if (ParseBorderProperty(pszValue, nValueLen, pWidth)) { + AddPropertyHolder(CFX_CSSProperty::BorderLeftWidth, pWidth, + bImportant); + return; + } + break; + case CFX_CSSProperty::BorderTop: +- if (ParseBorderProperty(pszValue, iValueLen, pWidth)) { ++ if (ParseBorderProperty(pszValue, nValueLen, pWidth)) { + AddPropertyHolder(CFX_CSSProperty::BorderTopWidth, pWidth, + bImportant); + return; + } + break; + case CFX_CSSProperty::BorderRight: +- if (ParseBorderProperty(pszValue, iValueLen, pWidth)) { ++ if (ParseBorderProperty(pszValue, nValueLen, pWidth)) { + AddPropertyHolder(CFX_CSSProperty::BorderRightWidth, pWidth, + bImportant); + return; + } + break; + case CFX_CSSProperty::BorderBottom: +- if (ParseBorderProperty(pszValue, iValueLen, pWidth)) { ++ if (ParseBorderProperty(pszValue, nValueLen, pWidth)) { + AddPropertyHolder(CFX_CSSProperty::BorderBottomWidth, pWidth, + bImportant); + return; +@@ -271,7 +275,7 @@ void CFX_CSSDeclaration::AddProperty(const CFX_CSSData::Property* property, + } + } break; + case CFX_CSSVALUETYPE_List: +- ParseValueListProperty(property, pszValue, iValueLen, bImportant); ++ ParseValueListProperty(property, pszValue, nValueLen, bImportant); + return; + default: + NOTREACHED(); +@@ -282,73 +286,73 @@ void CFX_CSSDeclaration::AddProperty(const CFX_CSSData::Property* property, + void CFX_CSSDeclaration::AddProperty(const WideString& prop, + const WideString& value) { + custom_properties_.push_back( +- pdfium::MakeUnique(prop, value)); ++ std::make_unique(prop, value)); + } + + RetainPtr CFX_CSSDeclaration::ParseNumber(const wchar_t* pszValue, +- int32_t iValueLen) { ++ size_t nValueLen) { + float fValue; +- CFX_CSSNumberType eUnit; +- if (!ParseCSSNumber(pszValue, iValueLen, &fValue, &eUnit)) ++ CFX_CSSNumberValue::Unit eUnit; ++ if (!ParseCSSNumber(pszValue, nValueLen, &fValue, &eUnit)) + return nullptr; + return pdfium::MakeRetain(eUnit, fValue); + } + + RetainPtr CFX_CSSDeclaration::ParseEnum(const wchar_t* pszValue, +- int32_t iValueLen) { ++ size_t nValueLen) { + const CFX_CSSData::PropertyValue* pValue = +- CFX_CSSData::GetPropertyValueByName(WideStringView(pszValue, iValueLen)); ++ CFX_CSSData::GetPropertyValueByName(WideStringView(pszValue, nValueLen)); + return pValue ? pdfium::MakeRetain(pValue->eName) : nullptr; + } + + RetainPtr CFX_CSSDeclaration::ParseColor(const wchar_t* pszValue, +- int32_t iValueLen) { ++ size_t nValueLen) { + FX_ARGB dwColor; +- if (!ParseCSSColor(pszValue, iValueLen, &dwColor)) ++ if (!ParseCSSColor(pszValue, nValueLen, &dwColor)) + return nullptr; + return pdfium::MakeRetain(dwColor); + } + + RetainPtr CFX_CSSDeclaration::ParseString(const wchar_t* pszValue, +- int32_t iValueLen) { +- int32_t iOffset; +- if (!ParseCSSString(pszValue, iValueLen, &iOffset, &iValueLen)) ++ size_t nValueLen) { ++ size_t iOffset; ++ if (!ParseCSSString(pszValue, nValueLen, &iOffset, &nValueLen)) + return nullptr; + +- if (iValueLen <= 0) ++ if (nValueLen == 0) + return nullptr; + + return pdfium::MakeRetain( +- WideString(pszValue + iOffset, iValueLen)); ++ WideString(pszValue + iOffset, nValueLen)); + } + + void CFX_CSSDeclaration::ParseValueListProperty( + const CFX_CSSData::Property* pProperty, + const wchar_t* pszValue, +- int32_t iValueLen, ++ size_t nValueLen, + bool bImportant) { + wchar_t separator = + (pProperty->eName == CFX_CSSProperty::FontFamily) ? ',' : ' '; +- CFX_CSSValueListParser parser(pszValue, iValueLen, separator); ++ CFX_CSSValueListParser parser(pszValue, nValueLen, separator); + +- const uint32_t dwType = pProperty->dwType; +- CFX_CSSPrimitiveType eType; ++ const CFX_CSSValueTypeMask dwType = pProperty->dwTypes; ++ CFX_CSSValue::PrimitiveType eType; + std::vector> list; +- while (parser.NextValue(&eType, &pszValue, &iValueLen)) { ++ while (parser.NextValue(&eType, &pszValue, &nValueLen)) { + switch (eType) { +- case CFX_CSSPrimitiveType::Number: ++ case CFX_CSSValue::PrimitiveType::kNumber: + if (dwType & CFX_CSSVALUETYPE_MaybeNumber) { + float fValue; +- CFX_CSSNumberType eNumType; +- if (ParseCSSNumber(pszValue, iValueLen, &fValue, &eNumType)) ++ CFX_CSSNumberValue::Unit eNumType; ++ if (ParseCSSNumber(pszValue, nValueLen, &fValue, &eNumType)) + list.push_back( + pdfium::MakeRetain(eNumType, fValue)); + } + break; +- case CFX_CSSPrimitiveType::String: ++ case CFX_CSSValue::PrimitiveType::kString: + if (dwType & CFX_CSSVALUETYPE_MaybeColor) { + FX_ARGB dwColor; +- if (ParseCSSColor(pszValue, iValueLen, &dwColor)) { ++ if (ParseCSSColor(pszValue, nValueLen, &dwColor)) { + list.push_back(pdfium::MakeRetain(dwColor)); + continue; + } +@@ -356,7 +360,7 @@ void CFX_CSSDeclaration::ParseValueListProperty( + if (dwType & CFX_CSSVALUETYPE_MaybeEnum) { + const CFX_CSSData::PropertyValue* pValue = + CFX_CSSData::GetPropertyValueByName( +- WideStringView(pszValue, iValueLen)); ++ WideStringView(pszValue, nValueLen)); + if (pValue) { + list.push_back(pdfium::MakeRetain(pValue->eName)); + continue; +@@ -364,13 +368,13 @@ void CFX_CSSDeclaration::ParseValueListProperty( + } + if (dwType & CFX_CSSVALUETYPE_MaybeString) { + list.push_back(pdfium::MakeRetain( +- WideString(pszValue, iValueLen))); ++ WideString(pszValue, nValueLen))); + } + break; +- case CFX_CSSPrimitiveType::RGB: ++ case CFX_CSSValue::PrimitiveType::kRGB: + if (dwType & CFX_CSSVALUETYPE_MaybeColor) { + FX_ARGB dwColor; +- if (ParseCSSColor(pszValue, iValueLen, &dwColor)) { ++ if (ParseCSSColor(pszValue, nValueLen, &dwColor)) { + list.push_back(pdfium::MakeRetain(dwColor)); + } + } +@@ -402,8 +406,8 @@ void CFX_CSSDeclaration::ParseValueListProperty( + CFX_CSSProperty::PaddingBottom); + return; + default: { +- auto pList = pdfium::MakeRetain(list); +- AddPropertyHolder(pProperty->eName, pList, bImportant); ++ auto value_list = pdfium::MakeRetain(std::move(list)); ++ AddPropertyHolder(pProperty->eName, value_list, bImportant); + return; + } + } +@@ -448,33 +452,33 @@ void CFX_CSSDeclaration::Add4ValuesProperty( + + bool CFX_CSSDeclaration::ParseBorderProperty( + const wchar_t* pszValue, +- int32_t iValueLen, ++ size_t nValueLen, + RetainPtr& pWidth) const { + pWidth.Reset(nullptr); + +- CFX_CSSValueListParser parser(pszValue, iValueLen, ' '); +- CFX_CSSPrimitiveType eType; +- while (parser.NextValue(&eType, &pszValue, &iValueLen)) { ++ CFX_CSSValue::PrimitiveType eType; ++ CFX_CSSValueListParser parser(pszValue, nValueLen, ' '); ++ while (parser.NextValue(&eType, &pszValue, &nValueLen)) { + switch (eType) { +- case CFX_CSSPrimitiveType::Number: { ++ case CFX_CSSValue::PrimitiveType::kNumber: { + if (pWidth) + continue; + + float fValue; +- CFX_CSSNumberType eNumType; +- if (ParseCSSNumber(pszValue, iValueLen, &fValue, &eNumType)) ++ CFX_CSSNumberValue::Unit eNumType; ++ if (ParseCSSNumber(pszValue, nValueLen, &fValue, &eNumType)) + pWidth = pdfium::MakeRetain(eNumType, fValue); + break; + } +- case CFX_CSSPrimitiveType::String: { ++ case CFX_CSSValue::PrimitiveType::kString: { + const CFX_CSSData::Color* pColorItem = +- CFX_CSSData::GetColorByName(WideStringView(pszValue, iValueLen)); ++ CFX_CSSData::GetColorByName(WideStringView(pszValue, nValueLen)); + if (pColorItem) + continue; + + const CFX_CSSData::PropertyValue* pValue = + CFX_CSSData::GetPropertyValueByName( +- WideStringView(pszValue, iValueLen)); ++ WideStringView(pszValue, nValueLen)); + if (!pValue) + continue; + +@@ -494,30 +498,30 @@ bool CFX_CSSDeclaration::ParseBorderProperty( + break; + } + } +- if (!pWidth) +- pWidth = +- pdfium::MakeRetain(CFX_CSSNumberType::Number, 0.0f); +- ++ if (!pWidth) { ++ pWidth = pdfium::MakeRetain( ++ CFX_CSSNumberValue::Unit::kNumber, 0.0f); ++ } + return true; + } + + void CFX_CSSDeclaration::ParseFontProperty(const wchar_t* pszValue, +- int32_t iValueLen, ++ size_t nValueLen, + bool bImportant) { +- CFX_CSSValueListParser parser(pszValue, iValueLen, '/'); ++ CFX_CSSValueListParser parser(pszValue, nValueLen, '/'); + RetainPtr pStyle; + RetainPtr pVariant; + RetainPtr pWeight; + RetainPtr pFontSize; + RetainPtr pLineHeight; +- std::vector> familyList; +- CFX_CSSPrimitiveType eType; +- while (parser.NextValue(&eType, &pszValue, &iValueLen)) { ++ std::vector> family_list; ++ CFX_CSSValue::PrimitiveType eType; ++ while (parser.NextValue(&eType, &pszValue, &nValueLen)) { + switch (eType) { +- case CFX_CSSPrimitiveType::String: { ++ case CFX_CSSValue::PrimitiveType::kString: { + const CFX_CSSData::PropertyValue* pValue = + CFX_CSSData::GetPropertyValueByName( +- WideStringView(pszValue, iValueLen)); ++ WideStringView(pszValue, nValueLen)); + if (pValue) { + switch (pValue->eName) { + case CFX_CSSPropertyValue::XxSmall: +@@ -565,19 +569,19 @@ void CFX_CSSDeclaration::ParseFontProperty(const wchar_t* pszValue, + } + } + if (pFontSize) { +- familyList.push_back(pdfium::MakeRetain( +- WideString(pszValue, iValueLen))); ++ family_list.push_back(pdfium::MakeRetain( ++ WideString(pszValue, nValueLen))); + } + parser.UseCommaSeparator(); + break; + } +- case CFX_CSSPrimitiveType::Number: { ++ case CFX_CSSValue::PrimitiveType::kNumber: { + float fValue; +- CFX_CSSNumberType eNumType; +- if (!ParseCSSNumber(pszValue, iValueLen, &fValue, &eNumType)) ++ CFX_CSSNumberValue::Unit eNumType; ++ if (!ParseCSSNumber(pszValue, nValueLen, &fValue, &eNumType)) + break; +- if (eType == CFX_CSSPrimitiveType::Number) { +- switch ((int32_t)fValue) { ++ if (eType == CFX_CSSValue::PrimitiveType::kNumber) { ++ switch (static_cast(fValue)) { + case 100: + case 200: + case 300: +@@ -587,9 +591,10 @@ void CFX_CSSDeclaration::ParseFontProperty(const wchar_t* pszValue, + case 700: + case 800: + case 900: +- if (!pWeight) ++ if (!pWeight) { + pWeight = pdfium::MakeRetain( +- CFX_CSSNumberType::Number, fValue); ++ CFX_CSSNumberValue::Unit::kNumber, fValue); ++ } + continue; + } + } +@@ -630,9 +635,10 @@ void CFX_CSSDeclaration::ParseFontProperty(const wchar_t* pszValue, + AddPropertyHolder(CFX_CSSProperty::FontWeight, pWeight, bImportant); + AddPropertyHolder(CFX_CSSProperty::FontSize, pFontSize, bImportant); + AddPropertyHolder(CFX_CSSProperty::LineHeight, pLineHeight, bImportant); +- if (!familyList.empty()) { +- auto pList = pdfium::MakeRetain(familyList); +- AddPropertyHolder(CFX_CSSProperty::FontFamily, pList, bImportant); ++ if (!family_list.empty()) { ++ auto value_list = ++ pdfium::MakeRetain(std::move(family_list)); ++ AddPropertyHolder(CFX_CSSProperty::FontFamily, value_list, bImportant); + } + } + +diff --git a/core/fxcrt/css/cfx_cssdeclaration.h b/core/fxcrt/css/cfx_cssdeclaration.h +index f3d87cc6b..eef58e0e6 100644 +--- a/core/fxcrt/css/cfx_cssdeclaration.h ++++ b/core/fxcrt/css/cfx_cssdeclaration.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,6 +11,7 @@ + #include + + #include "core/fxcrt/css/cfx_cssdata.h" ++#include "core/fxcrt/retain_ptr.h" + + class CFX_CSSPropertyHolder; + class CFX_CSSCustomProperty; +@@ -23,11 +24,11 @@ class CFX_CSSDeclaration { + std::vector>::const_iterator; + + static bool ParseCSSString(const wchar_t* pszValue, +- int32_t iValueLen, +- int32_t* iOffset, +- int32_t* iLength); ++ size_t nValueLen, ++ size_t* nOffset, ++ size_t* nLength); + static bool ParseCSSColor(const wchar_t* pszValue, +- int32_t iValueLen, ++ size_t nValueLen, + FX_ARGB* dwColor); + + CFX_CSSDeclaration(); +@@ -52,19 +53,19 @@ class CFX_CSSDeclaration { + size_t PropertyCountForTesting() const; + + FX_ARGB ParseColorForTest(const wchar_t* pszValue, +- int32_t iValueLen, ++ size_t nValueLen, + FX_ARGB* dwColor) const; + + private: + void ParseFontProperty(const wchar_t* pszValue, +- int32_t iValueLen, ++ size_t nValueLen, + bool bImportant); + bool ParseBorderProperty(const wchar_t* pszValue, +- int32_t iValueLen, ++ size_t nValueLen, + RetainPtr& pWidth) const; + void ParseValueListProperty(const CFX_CSSData::Property* pProperty, + const wchar_t* pszValue, +- int32_t iValueLen, ++ size_t nValueLen, + bool bImportant); + void Add4ValuesProperty(const std::vector>& list, + bool bImportant, +@@ -73,12 +74,11 @@ class CFX_CSSDeclaration { + CFX_CSSProperty eRight, + CFX_CSSProperty eBottom); + RetainPtr ParseNumber(const wchar_t* pszValue, +- int32_t iValueLen); +- RetainPtr ParseEnum(const wchar_t* pszValue, int32_t iValueLen); +- RetainPtr ParseColor(const wchar_t* pszValue, +- int32_t iValueLen); ++ size_t nValueLen); ++ RetainPtr ParseEnum(const wchar_t* pszValue, size_t nValueLen); ++ RetainPtr ParseColor(const wchar_t* pszValue, size_t nValueLen); + RetainPtr ParseString(const wchar_t* pszValue, +- int32_t iValueLen); ++ size_t nValueLen); + void AddPropertyHolder(CFX_CSSProperty eProperty, + RetainPtr pValue, + bool bImportant); +diff --git a/core/fxcrt/css/cfx_cssdeclaration_unittest.cpp b/core/fxcrt/css/cfx_cssdeclaration_unittest.cpp +index f4a032a7a..586806375 100644 +--- a/core/fxcrt/css/cfx_cssdeclaration_unittest.cpp ++++ b/core/fxcrt/css/cfx_cssdeclaration_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcrt/css/cfx_cssenumvalue.cpp b/core/fxcrt/css/cfx_cssenumvalue.cpp +index c9b39beb3..0e7b3cd4b 100644 +--- a/core/fxcrt/css/cfx_cssenumvalue.cpp ++++ b/core/fxcrt/css/cfx_cssenumvalue.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,6 @@ + #include "core/fxcrt/css/cfx_cssenumvalue.h" + + CFX_CSSEnumValue::CFX_CSSEnumValue(CFX_CSSPropertyValue value) +- : CFX_CSSValue(CFX_CSSPrimitiveType::Enum), value_(value) {} ++ : CFX_CSSValue(PrimitiveType::kEnum), value_(value) {} + +-CFX_CSSEnumValue::~CFX_CSSEnumValue() {} ++CFX_CSSEnumValue::~CFX_CSSEnumValue() = default; +diff --git a/core/fxcrt/css/cfx_cssenumvalue.h b/core/fxcrt/css/cfx_cssenumvalue.h +index c397761d5..fe9961c38 100644 +--- a/core/fxcrt/css/cfx_cssenumvalue.h ++++ b/core/fxcrt/css/cfx_cssenumvalue.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcrt/css/cfx_cssexttextbuf.cpp b/core/fxcrt/css/cfx_cssexttextbuf.cpp +deleted file mode 100644 +index 287dc2e95..000000000 +--- a/core/fxcrt/css/cfx_cssexttextbuf.cpp ++++ /dev/null +@@ -1,17 +0,0 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fxcrt/css/cfx_cssexttextbuf.h" +- +-CFX_CSSExtTextBuf::CFX_CSSExtTextBuf() +- : m_pExtBuffer(nullptr), m_iDatLen(0), m_iDatPos(0) {} +- +-CFX_CSSExtTextBuf::~CFX_CSSExtTextBuf() {} +- +-void CFX_CSSExtTextBuf::AttachBuffer(const wchar_t* pBuffer, int32_t iBufLen) { +- m_pExtBuffer = pBuffer; +- m_iDatLen = iBufLen; +-} +diff --git a/core/fxcrt/css/cfx_cssexttextbuf.h b/core/fxcrt/css/cfx_cssexttextbuf.h +deleted file mode 100644 +index 342b91e8a..000000000 +--- a/core/fxcrt/css/cfx_cssexttextbuf.h ++++ /dev/null +@@ -1,34 +0,0 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXCRT_CSS_CFX_CSSEXTTEXTBUF_H_ +-#define CORE_FXCRT_CSS_CFX_CSSEXTTEXTBUF_H_ +- +-#include "core/fxcrt/fx_system.h" +- +-class CFX_CSSExtTextBuf { +- public: +- CFX_CSSExtTextBuf(); +- ~CFX_CSSExtTextBuf(); +- +- void AttachBuffer(const wchar_t* pBuffer, int32_t iBufLen); +- +- bool IsEOF() const { return m_iDatPos >= m_iDatLen; } +- +- wchar_t GetChar() const { return m_pExtBuffer[m_iDatPos]; } +- wchar_t GetNextChar() const { +- return (m_iDatPos + 1 >= m_iDatLen) ? 0 : m_pExtBuffer[m_iDatPos + 1]; +- } +- +- void MoveNext() { m_iDatPos++; } +- +- protected: +- const wchar_t* m_pExtBuffer; +- int32_t m_iDatLen; +- int32_t m_iDatPos; +-}; +- +-#endif // CORE_FXCRT_CSS_CFX_CSSEXTTEXTBUF_H_ +diff --git a/core/fxcrt/css/cfx_cssinputtextbuf.cpp b/core/fxcrt/css/cfx_cssinputtextbuf.cpp +new file mode 100644 +index 000000000..3554f746a +--- /dev/null ++++ b/core/fxcrt/css/cfx_cssinputtextbuf.cpp +@@ -0,0 +1,11 @@ ++// Copyright 2017 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxcrt/css/cfx_cssinputtextbuf.h" ++ ++CFX_CSSInputTextBuf::CFX_CSSInputTextBuf(WideStringView str) : m_Buffer(str) {} ++ ++CFX_CSSInputTextBuf::~CFX_CSSInputTextBuf() = default; +diff --git a/core/fxcrt/css/cfx_cssinputtextbuf.h b/core/fxcrt/css/cfx_cssinputtextbuf.h +new file mode 100644 +index 000000000..e702e1bf6 +--- /dev/null ++++ b/core/fxcrt/css/cfx_cssinputtextbuf.h +@@ -0,0 +1,29 @@ ++// Copyright 2017 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCRT_CSS_CFX_CSSINPUTTEXTBUF_H_ ++#define CORE_FXCRT_CSS_CFX_CSSINPUTTEXTBUF_H_ ++ ++#include "core/fxcrt/widestring.h" ++ ++class CFX_CSSInputTextBuf { ++ public: ++ explicit CFX_CSSInputTextBuf(WideStringView str); ++ ~CFX_CSSInputTextBuf(); ++ ++ bool IsEOF() const { return m_iPos >= m_Buffer.GetLength(); } ++ void MoveNext() { m_iPos++; } ++ wchar_t GetChar() const { return m_Buffer[m_iPos]; } ++ wchar_t GetNextChar() const { ++ return m_iPos + 1 < m_Buffer.GetLength() ? m_Buffer[m_iPos + 1] : 0; ++ } ++ ++ protected: ++ const WideStringView m_Buffer; ++ size_t m_iPos = 0; ++}; ++ ++#endif // CORE_FXCRT_CSS_CFX_CSSINPUTTEXTBUF_H_ +diff --git a/core/fxcrt/css/cfx_cssnumbervalue.cpp b/core/fxcrt/css/cfx_cssnumbervalue.cpp +index d8f72479d..72ae7752e 100644 +--- a/core/fxcrt/css/cfx_cssnumbervalue.cpp ++++ b/core/fxcrt/css/cfx_cssnumbervalue.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,33 +6,35 @@ + + #include "core/fxcrt/css/cfx_cssnumbervalue.h" + +-CFX_CSSNumberValue::CFX_CSSNumberValue(CFX_CSSNumberType type, float value) +- : CFX_CSSValue(CFX_CSSPrimitiveType::Number), type_(type), value_(value) { +- if (type_ == CFX_CSSNumberType::Number && fabs(value_) < 0.001f) ++#include ++ ++CFX_CSSNumberValue::CFX_CSSNumberValue(Unit unit, float value) ++ : CFX_CSSValue(PrimitiveType::kNumber), unit_(unit), value_(value) { ++ if (unit_ == Unit::kNumber && fabs(value_) < 0.001f) + value_ = 0.0f; + } + +-CFX_CSSNumberValue::~CFX_CSSNumberValue() {} ++CFX_CSSNumberValue::~CFX_CSSNumberValue() = default; + + float CFX_CSSNumberValue::Apply(float percentBase) const { +- switch (type_) { +- case CFX_CSSNumberType::Pixels: +- case CFX_CSSNumberType::Number: ++ switch (unit_) { ++ case Unit::kPixels: ++ case Unit::kNumber: + return value_ * 72 / 96; +- case CFX_CSSNumberType::EMS: +- case CFX_CSSNumberType::EXS: ++ case Unit::kEMS: ++ case Unit::kEXS: + return value_ * percentBase; +- case CFX_CSSNumberType::Percent: ++ case Unit::kPercent: + return value_ * percentBase / 100.0f; +- case CFX_CSSNumberType::CentiMeters: ++ case Unit::kCentiMeters: + return value_ * 28.3464f; +- case CFX_CSSNumberType::MilliMeters: ++ case Unit::kMilliMeters: + return value_ * 2.8346f; +- case CFX_CSSNumberType::Inches: ++ case Unit::kInches: + return value_ * 72.0f; +- case CFX_CSSNumberType::Picas: ++ case Unit::kPicas: + return value_ / 12.0f; +- case CFX_CSSNumberType::Points: ++ case Unit::kPoints: + return value_; + } + return value_; +diff --git a/core/fxcrt/css/cfx_cssnumbervalue.h b/core/fxcrt/css/cfx_cssnumbervalue.h +index a977750f0..3b2556be5 100644 +--- a/core/fxcrt/css/cfx_cssnumbervalue.h ++++ b/core/fxcrt/css/cfx_cssnumbervalue.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,33 +8,31 @@ + #define CORE_FXCRT_CSS_CFX_CSSNUMBERVALUE_H_ + + #include "core/fxcrt/css/cfx_cssvalue.h" +-#include "core/fxcrt/fx_system.h" +- +-enum class CFX_CSSNumberType { +- Number, +- Percent, +- EMS, +- EXS, +- Pixels, +- CentiMeters, +- MilliMeters, +- Inches, +- Points, +- Picas, +-}; + + class CFX_CSSNumberValue final : public CFX_CSSValue { + public: +- CFX_CSSNumberValue(CFX_CSSNumberType type, float value); ++ enum class Unit { ++ kNumber, ++ kPercent, ++ kEMS, ++ kEXS, ++ kPixels, ++ kCentiMeters, ++ kMilliMeters, ++ kInches, ++ kPoints, ++ kPicas, ++ }; ++ ++ CFX_CSSNumberValue(Unit unit, float value); + ~CFX_CSSNumberValue() override; + +- float Value() const { return value_; } +- CFX_CSSNumberType Kind() const { return type_; } +- ++ Unit unit() const { return unit_; } ++ float value() const { return value_; } + float Apply(float percentBase) const; + + private: +- CFX_CSSNumberType type_; ++ Unit unit_; + float value_; + }; + +diff --git a/core/fxcrt/css/cfx_cssoutputtextbuf.cpp b/core/fxcrt/css/cfx_cssoutputtextbuf.cpp +new file mode 100644 +index 000000000..a57d41a58 +--- /dev/null ++++ b/core/fxcrt/css/cfx_cssoutputtextbuf.cpp +@@ -0,0 +1,28 @@ ++// Copyright 2017 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxcrt/css/cfx_cssoutputtextbuf.h" ++ ++CFX_CSSOutputTextBuf::CFX_CSSOutputTextBuf() { ++ m_Buffer.reserve(32); ++} ++ ++CFX_CSSOutputTextBuf::~CFX_CSSOutputTextBuf() = default; ++ ++void CFX_CSSOutputTextBuf::AppendCharIfNotLeadingBlank(wchar_t wch) { ++ if (m_Buffer.empty() && wch <= ' ') ++ return; ++ ++ m_Buffer.push_back(wch); ++} ++ ++WideStringView CFX_CSSOutputTextBuf::GetTrailingBlankTrimmedString() const { ++ WideStringView result(m_Buffer); ++ while (!result.IsEmpty() && result.Back() <= ' ') ++ result = result.First(result.GetLength() - 1); ++ ++ return result; ++} +diff --git a/core/fxcrt/css/cfx_cssoutputtextbuf.h b/core/fxcrt/css/cfx_cssoutputtextbuf.h +new file mode 100644 +index 000000000..8cbb59e04 +--- /dev/null ++++ b/core/fxcrt/css/cfx_cssoutputtextbuf.h +@@ -0,0 +1,27 @@ ++// Copyright 2017 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCRT_CSS_CFX_CSSOUTPUTTEXTBUF_H_ ++#define CORE_FXCRT_CSS_CFX_CSSOUTPUTTEXTBUF_H_ ++ ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/widestring.h" ++ ++class CFX_CSSOutputTextBuf { ++ public: ++ CFX_CSSOutputTextBuf(); ++ ~CFX_CSSOutputTextBuf(); ++ ++ void Clear() { m_Buffer.clear(); } ++ bool IsEmpty() const { return m_Buffer.empty(); } ++ void AppendCharIfNotLeadingBlank(wchar_t wch); ++ WideStringView GetTrailingBlankTrimmedString() const; ++ ++ protected: ++ DataVector m_Buffer; ++}; ++ ++#endif // CORE_FXCRT_CSS_CFX_CSSOUTPUTTEXTBUF_H_ +diff --git a/core/fxcrt/css/cfx_csspropertyholder.cpp b/core/fxcrt/css/cfx_csspropertyholder.cpp +index 11e0d4b75..d19a10c76 100644 +--- a/core/fxcrt/css/cfx_csspropertyholder.cpp ++++ b/core/fxcrt/css/cfx_csspropertyholder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,6 @@ + + #include "core/fxcrt/css/cfx_csspropertyholder.h" + +-CFX_CSSPropertyHolder::CFX_CSSPropertyHolder() {} ++CFX_CSSPropertyHolder::CFX_CSSPropertyHolder() = default; + +-CFX_CSSPropertyHolder::~CFX_CSSPropertyHolder() {} ++CFX_CSSPropertyHolder::~CFX_CSSPropertyHolder() = default; +diff --git a/core/fxcrt/css/cfx_csspropertyholder.h b/core/fxcrt/css/cfx_csspropertyholder.h +index a271bdafc..ef5149f78 100644 +--- a/core/fxcrt/css/cfx_csspropertyholder.h ++++ b/core/fxcrt/css/cfx_csspropertyholder.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcrt/css/cfx_cssrulecollection.cpp b/core/fxcrt/css/cfx_cssrulecollection.cpp +index f52582152..4abdba4cc 100644 +--- a/core/fxcrt/css/cfx_cssrulecollection.cpp ++++ b/core/fxcrt/css/cfx_cssrulecollection.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,40 +14,30 @@ + #include "core/fxcrt/css/cfx_cssstylerule.h" + #include "core/fxcrt/css/cfx_cssstylesheet.h" + #include "core/fxcrt/css/cfx_csssyntaxparser.h" +-#include "third_party/base/ptr_util.h" + +-CFX_CSSRuleCollection::CFX_CSSRuleCollection() : m_iSelectors(0) {} ++CFX_CSSRuleCollection::CFX_CSSRuleCollection() = default; + +-CFX_CSSRuleCollection::~CFX_CSSRuleCollection() { +- Clear(); +-} +- +-void CFX_CSSRuleCollection::Clear() { +- m_TagRules.clear(); +- m_iSelectors = 0; +-} ++CFX_CSSRuleCollection::~CFX_CSSRuleCollection() = default; + + const std::vector>* + CFX_CSSRuleCollection::GetTagRuleData(const WideString& tagname) const { +- auto it = m_TagRules.find(FX_HashCode_GetW(tagname.AsStringView(), true)); ++ auto it = m_TagRules.find(FX_HashCode_GetLoweredW(tagname.AsStringView())); + return it != m_TagRules.end() ? &it->second : nullptr; + } + +-void CFX_CSSRuleCollection::AddRulesFrom(const CFX_CSSStyleSheet* sheet) { +- int32_t iRules = sheet->CountRules(); +- for (int32_t j = 0; j < iRules; j++) +- AddRulesFrom(sheet, sheet->GetRule(j)); ++void CFX_CSSRuleCollection::SetRulesFromSheet(const CFX_CSSStyleSheet* sheet) { ++ m_TagRules.clear(); ++ for (size_t i = 0; i < sheet->CountRules(); ++i) ++ AddRule(sheet->GetRule(i)); + } + +-void CFX_CSSRuleCollection::AddRulesFrom(const CFX_CSSStyleSheet* pStyleSheet, +- CFX_CSSStyleRule* pStyleRule) { ++void CFX_CSSRuleCollection::AddRule(CFX_CSSStyleRule* pStyleRule) { + CFX_CSSDeclaration* pDeclaration = pStyleRule->GetDeclaration(); +- int32_t iSelectors = pStyleRule->CountSelectorLists(); +- for (int32_t i = 0; i < iSelectors; ++i) { ++ size_t nSelectors = pStyleRule->CountSelectorLists(); ++ for (size_t i = 0; i < nSelectors; ++i) { + CFX_CSSSelector* pSelector = pStyleRule->GetSelectorList(i); +- m_TagRules[pSelector->GetNameHash()].push_back( +- pdfium::MakeUnique(pSelector, pDeclaration)); +- m_iSelectors++; ++ m_TagRules[pSelector->name_hash()].push_back( ++ std::make_unique(pSelector, pDeclaration)); + } + } + +diff --git a/core/fxcrt/css/cfx_cssrulecollection.h b/core/fxcrt/css/cfx_cssrulecollection.h +index 72ae58c55..b5f09aded 100644 +--- a/core/fxcrt/css/cfx_cssrulecollection.h ++++ b/core/fxcrt/css/cfx_cssrulecollection.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,7 +11,7 @@ + #include + #include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" + + class CFX_CSSDeclaration; + class CFX_CSSSelector; +@@ -31,19 +31,15 @@ class CFX_CSSRuleCollection { + CFX_CSSRuleCollection(); + ~CFX_CSSRuleCollection(); + +- void AddRulesFrom(const CFX_CSSStyleSheet* sheet); +- void Clear(); +- int32_t CountSelectors() const { return m_iSelectors; } ++ void SetRulesFromSheet(const CFX_CSSStyleSheet* sheet); + + const std::vector>* GetTagRuleData( + const WideString& tagname) const; + + private: +- void AddRulesFrom(const CFX_CSSStyleSheet* pStyleSheet, +- CFX_CSSStyleRule* pRule); ++ void AddRule(CFX_CSSStyleRule* pRule); + + std::map>> m_TagRules; +- int32_t m_iSelectors; + }; + + #endif // CORE_FXCRT_CSS_CFX_CSSRULECOLLECTION_H_ +diff --git a/core/fxcrt/css/cfx_cssselector.cpp b/core/fxcrt/css/cfx_cssselector.cpp +index cd90f6290..f10bf5f74 100644 +--- a/core/fxcrt/css/cfx_cssselector.cpp ++++ b/core/fxcrt/css/cfx_cssselector.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,54 +9,34 @@ + #include + + #include "core/fxcrt/fx_extension.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + namespace { + +-int32_t GetCSSNameLen(const wchar_t* psz, const wchar_t* pEnd) { +- const wchar_t* pStart = psz; +- while (psz < pEnd) { +- if (!isascii(*psz) || (!isalnum(*psz) && *psz != '_' && *psz != '-')) { +- break; +- } +- ++psz; ++size_t GetCSSNameLen(WideStringView str) { ++ for (size_t i = 0; i < str.GetLength(); ++i) { ++ wchar_t wch = str[i]; ++ if (!isascii(wch) || (!isalnum(wch) && wch != '_' && wch != '-')) ++ return i; + } +- return psz - pStart; ++ return str.GetLength(); + } + + } // namespace + +-CFX_CSSSelector::CFX_CSSSelector(CFX_CSSSelectorType eType, +- const wchar_t* psz, +- int32_t iLen, +- bool bIgnoreCase) +- : m_eType(eType), +- m_dwHash(FX_HashCode_GetW(WideStringView(psz, iLen), bIgnoreCase)) {} +- +-CFX_CSSSelector::~CFX_CSSSelector() {} +- +-CFX_CSSSelectorType CFX_CSSSelector::GetType() const { +- return m_eType; +-} ++CFX_CSSSelector::CFX_CSSSelector(WideStringView str, ++ std::unique_ptr next) ++ : name_hash_(FX_HashCode_GetLoweredW(str)), next_(std::move(next)) {} + +-uint32_t CFX_CSSSelector::GetNameHash() const { +- return m_dwHash; +-} +- +-CFX_CSSSelector* CFX_CSSSelector::GetNextSelector() const { +- return m_pNext.get(); +-} ++CFX_CSSSelector::~CFX_CSSSelector() = default; + + // static. + std::unique_ptr CFX_CSSSelector::FromString( + WideStringView str) { +- ASSERT(!str.IsEmpty()); ++ DCHECK(!str.IsEmpty()); + +- const wchar_t* psz = str.unterminated_c_str(); +- const wchar_t* pStart = psz; +- const wchar_t* pEnd = psz + str.GetLength(); +- for (; psz < pEnd; ++psz) { +- switch (*psz) { ++ for (wchar_t wch : str) { ++ switch (wch) { + case '>': + case '[': + case '+': +@@ -64,24 +44,26 @@ std::unique_ptr CFX_CSSSelector::FromString( + } + } + +- std::unique_ptr pFirst = nullptr; +- for (psz = pStart; psz < pEnd;) { +- wchar_t wch = *psz; +- if ((isascii(wch) && isalpha(wch)) || wch == '*') { +- int32_t iNameLen = wch == '*' ? 1 : GetCSSNameLen(psz, pEnd); +- auto p = pdfium::MakeUnique(CFX_CSSSelectorType::Element, +- psz, iNameLen, true); +- if (pFirst) { +- pFirst->SetType(CFX_CSSSelectorType::Descendant); +- p->SetNext(std::move(pFirst)); +- } +- pFirst = std::move(p); +- psz += iNameLen; +- } else if (wch == ' ') { +- psz++; +- } else { +- return nullptr; ++ std::unique_ptr head; ++ for (size_t i = 0; i < str.GetLength();) { ++ wchar_t wch = str[i]; ++ if (wch == ' ') { ++ ++i; ++ continue; + } ++ ++ const bool is_star = wch == '*'; ++ const bool is_valid_char = is_star || (isascii(wch) && isalpha(wch)); ++ if (!is_valid_char) ++ return nullptr; ++ ++ if (head) ++ head->set_is_descendant(); ++ size_t len = is_star ? 1 : GetCSSNameLen(str.Last(str.GetLength() - i)); ++ auto new_head = ++ std::make_unique(str.Substr(i, len), std::move(head)); ++ head = std::move(new_head); ++ i += len; + } +- return pFirst; ++ return head; + } +diff --git a/core/fxcrt/css/cfx_cssselector.h b/core/fxcrt/css/cfx_cssselector.h +index 14b61ee67..553c8291d 100644 +--- a/core/fxcrt/css/cfx_cssselector.h ++++ b/core/fxcrt/css/cfx_cssselector.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,35 +8,26 @@ + #define CORE_FXCRT_CSS_CFX_CSSSELECTOR_H_ + + #include +-#include + +-#include "core/fxcrt/css/cfx_css.h" +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" + + class CFX_CSSSelector { + public: + static std::unique_ptr FromString(WideStringView str); + +- CFX_CSSSelector(CFX_CSSSelectorType eType, +- const wchar_t* psz, +- int32_t iLen, +- bool bIgnoreCase); ++ CFX_CSSSelector(WideStringView str, std::unique_ptr next); + ~CFX_CSSSelector(); + +- CFX_CSSSelectorType GetType() const; +- uint32_t GetNameHash() const; +- CFX_CSSSelector* GetNextSelector() const; +- +- void SetNext(std::unique_ptr pNext) { +- m_pNext = std::move(pNext); +- } ++ bool is_descendant() const { return is_descendant_; } ++ uint32_t name_hash() const { return name_hash_; } ++ const CFX_CSSSelector* next_selector() const { return next_.get(); } + + private: +- void SetType(CFX_CSSSelectorType eType) { m_eType = eType; } ++ void set_is_descendant() { is_descendant_ = true; } + +- CFX_CSSSelectorType m_eType; +- uint32_t m_dwHash; +- std::unique_ptr m_pNext; ++ bool is_descendant_ = false; ++ const uint32_t name_hash_; ++ const std::unique_ptr next_; + }; + + #endif // CORE_FXCRT_CSS_CFX_CSSSELECTOR_H_ +diff --git a/core/fxcrt/css/cfx_cssstringvalue.cpp b/core/fxcrt/css/cfx_cssstringvalue.cpp +index 6ff2a33c9..7fbb6b0d3 100644 +--- a/core/fxcrt/css/cfx_cssstringvalue.cpp ++++ b/core/fxcrt/css/cfx_cssstringvalue.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,6 @@ + #include "core/fxcrt/css/cfx_cssstringvalue.h" + + CFX_CSSStringValue::CFX_CSSStringValue(const WideString& value) +- : CFX_CSSValue(CFX_CSSPrimitiveType::String), value_(value) {} ++ : CFX_CSSValue(PrimitiveType::kString), value_(value) {} + +-CFX_CSSStringValue::~CFX_CSSStringValue() {} ++CFX_CSSStringValue::~CFX_CSSStringValue() = default; +diff --git a/core/fxcrt/css/cfx_cssstringvalue.h b/core/fxcrt/css/cfx_cssstringvalue.h +index b59cef097..afc2d3151 100644 +--- a/core/fxcrt/css/cfx_cssstringvalue.h ++++ b/core/fxcrt/css/cfx_cssstringvalue.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcrt/css/cfx_cssstylerule.cpp b/core/fxcrt/css/cfx_cssstylerule.cpp +index 504771ed4..9e8c3ed3b 100644 +--- a/core/fxcrt/css/cfx_cssstylerule.cpp ++++ b/core/fxcrt/css/cfx_cssstylerule.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,15 +6,17 @@ + + #include "core/fxcrt/css/cfx_cssstylerule.h" + +-CFX_CSSStyleRule::CFX_CSSStyleRule() {} ++#include "third_party/base/check.h" + +-CFX_CSSStyleRule::~CFX_CSSStyleRule() {} ++CFX_CSSStyleRule::CFX_CSSStyleRule() = default; ++ ++CFX_CSSStyleRule::~CFX_CSSStyleRule() = default; + + size_t CFX_CSSStyleRule::CountSelectorLists() const { + return m_ppSelector.size(); + } + +-CFX_CSSSelector* CFX_CSSStyleRule::GetSelectorList(int32_t index) const { ++CFX_CSSSelector* CFX_CSSStyleRule::GetSelectorList(size_t index) const { + return m_ppSelector[index].get(); + } + +@@ -24,7 +26,6 @@ CFX_CSSDeclaration* CFX_CSSStyleRule::GetDeclaration() { + + void CFX_CSSStyleRule::SetSelector( + std::vector>* list) { +- ASSERT(m_ppSelector.empty()); +- ++ DCHECK(m_ppSelector.empty()); + m_ppSelector.swap(*list); + } +diff --git a/core/fxcrt/css/cfx_cssstylerule.h b/core/fxcrt/css/cfx_cssstylerule.h +index bba1fc53c..a731d1924 100644 +--- a/core/fxcrt/css/cfx_cssstylerule.h ++++ b/core/fxcrt/css/cfx_cssstylerule.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -19,7 +19,7 @@ class CFX_CSSStyleRule { + ~CFX_CSSStyleRule(); + + size_t CountSelectorLists() const; +- CFX_CSSSelector* GetSelectorList(int32_t index) const; ++ CFX_CSSSelector* GetSelectorList(size_t index) const; + CFX_CSSDeclaration* GetDeclaration(); + + void SetSelector(std::vector>* list); +diff --git a/core/fxcrt/css/cfx_cssstyleselector.cpp b/core/fxcrt/css/cfx_cssstyleselector.cpp +index f6b48bd16..864dae83f 100644 +--- a/core/fxcrt/css/cfx_cssstyleselector.cpp ++++ b/core/fxcrt/css/cfx_cssstyleselector.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -19,20 +19,21 @@ + #include "core/fxcrt/css/cfx_cssstylesheet.h" + #include "core/fxcrt/css/cfx_csssyntaxparser.h" + #include "core/fxcrt/css/cfx_cssvaluelist.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/adapters.h" ++#include "third_party/base/notreached.h" + +-CFX_CSSStyleSelector::CFX_CSSStyleSelector() : m_fDefFontSize(12.0f) {} ++CFX_CSSStyleSelector::CFX_CSSStyleSelector() = default; + +-CFX_CSSStyleSelector::~CFX_CSSStyleSelector() {} ++CFX_CSSStyleSelector::~CFX_CSSStyleSelector() = default; + +-void CFX_CSSStyleSelector::SetDefFontSize(float fFontSize) { +- ASSERT(fFontSize > 0); +- m_fDefFontSize = fFontSize; ++void CFX_CSSStyleSelector::SetDefaultFontSize(float fFontSize) { ++ DCHECK(fFontSize > 0); ++ m_fDefaultFontSize = fFontSize; + } + + RetainPtr CFX_CSSStyleSelector::CreateComputedStyle( +- CFX_CSSComputedStyle* pParentStyle) { ++ const CFX_CSSComputedStyle* pParentStyle) { + auto pStyle = pdfium::MakeRetain(); + if (pParentStyle) + pStyle->m_InheritedData = pParentStyle->m_InheritedData; +@@ -45,14 +46,13 @@ void CFX_CSSStyleSelector::SetUAStyleSheet( + } + + void CFX_CSSStyleSelector::UpdateStyleIndex() { +- m_UARules.Clear(); +- m_UARules.AddRulesFrom(m_UAStyles.get()); ++ m_UARules.SetRulesFromSheet(m_UAStyles.get()); + } + + std::vector CFX_CSSStyleSelector::MatchDeclarations( + const WideString& tagname) { + std::vector matchedDecls; +- if (m_UARules.CountSelectors() == 0 || tagname.IsEmpty()) ++ if (tagname.IsEmpty()) + return matchedDecls; + + auto* rules = m_UARules.GetTagRuleData(tagname); +@@ -71,11 +71,9 @@ bool CFX_CSSStyleSelector::MatchSelector(const WideString& tagname, + // TODO(dsinclair): The code only supports a single level of selector at this + // point. None of the code using selectors required the complexity so lets + // just say we don't support them to simplify the code for now. +- if (!pSel || pSel->GetNextSelector() || +- pSel->GetType() == CFX_CSSSelectorType::Descendant) { ++ if (!pSel || pSel->next_selector() || pSel->is_descendant()) + return false; +- } +- return pSel->GetNameHash() == FX_HashCode_GetW(tagname.AsStringView(), true); ++ return pSel->name_hash() == FX_HashCode_GetLoweredW(tagname.AsStringView()); + } + + void CFX_CSSStyleSelector::ComputeStyle( +@@ -85,7 +83,7 @@ void CFX_CSSStyleSelector::ComputeStyle( + CFX_CSSComputedStyle* pDest) { + std::unique_ptr pDecl; + if (!styleString.IsEmpty() || !alignString.IsEmpty()) { +- pDecl = pdfium::MakeUnique(); ++ pDecl = std::make_unique(); + + if (!styleString.IsEmpty()) + AppendInlineStyle(pDecl.get(), styleString); +@@ -139,22 +137,23 @@ void CFX_CSSStyleSelector::ExtractValues( + + void CFX_CSSStyleSelector::AppendInlineStyle(CFX_CSSDeclaration* pDecl, + const WideString& style) { +- ASSERT(pDecl); +- ASSERT(!style.IsEmpty()); ++ DCHECK(pDecl); ++ DCHECK(!style.IsEmpty()); ++ ++ auto pSyntax = std::make_unique(style.AsStringView()); ++ pSyntax->SetParseOnlyDeclarations(); + +- auto pSyntax = pdfium::MakeUnique( +- style.c_str(), style.GetLength(), 32, true); + int32_t iLen2 = 0; + const CFX_CSSData::Property* property = nullptr; + WideString wsName; +- while (1) { +- CFX_CSSSyntaxStatus eStatus = pSyntax->DoSyntaxParse(); +- if (eStatus == CFX_CSSSyntaxStatus::PropertyName) { ++ while (true) { ++ CFX_CSSSyntaxParser::Status eStatus = pSyntax->DoSyntaxParse(); ++ if (eStatus == CFX_CSSSyntaxParser::Status::kPropertyName) { + WideStringView strValue = pSyntax->GetCurrentString(); + property = CFX_CSSData::GetPropertyByName(strValue); + if (!property) + wsName = WideString(strValue); +- } else if (eStatus == CFX_CSSSyntaxStatus::PropertyValue) { ++ } else if (eStatus == CFX_CSSSyntaxParser::Status::kPropertyValue) { + if (property || iLen2 > 0) { + WideStringView strValue = pSyntax->GetCurrentString(); + if (!strValue.IsEmpty()) { +@@ -173,30 +172,30 @@ void CFX_CSSStyleSelector::AppendInlineStyle(CFX_CSSDeclaration* pDecl, + void CFX_CSSStyleSelector::ApplyProperty(CFX_CSSProperty eProperty, + const RetainPtr& pValue, + CFX_CSSComputedStyle* pComputedStyle) { +- if (pValue->GetType() != CFX_CSSPrimitiveType::List) { +- CFX_CSSPrimitiveType eType = pValue->GetType(); ++ if (pValue->GetType() != CFX_CSSValue::PrimitiveType::kList) { ++ CFX_CSSValue::PrimitiveType eType = pValue->GetType(); + switch (eProperty) { + case CFX_CSSProperty::Display: +- if (eType == CFX_CSSPrimitiveType::Enum) { ++ if (eType == CFX_CSSValue::PrimitiveType::kEnum) { + pComputedStyle->m_NonInheritedData.m_eDisplay = +- ToDisplay(pValue.As()->Value()); ++ ToDisplay(pValue.AsRaw()->Value()); + } + break; + case CFX_CSSProperty::FontSize: { + float& fFontSize = pComputedStyle->m_InheritedData.m_fFontSize; +- if (eType == CFX_CSSPrimitiveType::Number) { +- fFontSize = pValue.As()->Apply(fFontSize); +- } else if (eType == CFX_CSSPrimitiveType::Enum) { ++ if (eType == CFX_CSSValue::PrimitiveType::kNumber) { ++ fFontSize = pValue.AsRaw()->Apply(fFontSize); ++ } else if (eType == CFX_CSSValue::PrimitiveType::kEnum) { + fFontSize = +- ToFontSize(pValue.As()->Value(), fFontSize); ++ ToFontSize(pValue.AsRaw()->Value(), fFontSize); + } + } break; + case CFX_CSSProperty::LineHeight: +- if (eType == CFX_CSSPrimitiveType::Number) { ++ if (eType == CFX_CSSValue::PrimitiveType::kNumber) { + RetainPtr v = pValue.As(); +- if (v->Kind() == CFX_CSSNumberType::Number) { ++ if (v->unit() == CFX_CSSNumberValue::Unit::kNumber) { + pComputedStyle->m_InheritedData.m_fLineHeight = +- v->Value() * pComputedStyle->m_InheritedData.m_fFontSize; ++ v->value() * pComputedStyle->m_InheritedData.m_fFontSize; + } else { + pComputedStyle->m_InheritedData.m_fLineHeight = + v->Apply(pComputedStyle->m_InheritedData.m_fFontSize); +@@ -204,9 +203,9 @@ void CFX_CSSStyleSelector::ApplyProperty(CFX_CSSProperty eProperty, + } + break; + case CFX_CSSProperty::TextAlign: +- if (eType == CFX_CSSPrimitiveType::Enum) { ++ if (eType == CFX_CSSValue::PrimitiveType::kEnum) { + pComputedStyle->m_InheritedData.m_eTextAlign = +- ToTextAlign(pValue.As()->Value()); ++ ToTextAlign(pValue.AsRaw()->Value()); + } + break; + case CFX_CSSProperty::TextIndent: +@@ -215,27 +214,28 @@ void CFX_CSSStyleSelector::ApplyProperty(CFX_CSSProperty eProperty, + pComputedStyle->m_InheritedData.m_fFontSize); + break; + case CFX_CSSProperty::FontWeight: +- if (eType == CFX_CSSPrimitiveType::Enum) { ++ if (eType == CFX_CSSValue::PrimitiveType::kEnum) { + pComputedStyle->m_InheritedData.m_wFontWeight = +- ToFontWeight(pValue.As()->Value()); +- } else if (eType == CFX_CSSPrimitiveType::Number) { +- int32_t iValue = +- (int32_t)pValue.As()->Value() / 100; ++ ToFontWeight(pValue.AsRaw()->Value()); ++ } else if (eType == CFX_CSSValue::PrimitiveType::kNumber) { ++ int32_t iValue = static_cast( ++ pValue.AsRaw()->value()) / ++ 100; + if (iValue >= 1 && iValue <= 9) { + pComputedStyle->m_InheritedData.m_wFontWeight = iValue * 100; + } + } + break; + case CFX_CSSProperty::FontStyle: +- if (eType == CFX_CSSPrimitiveType::Enum) { ++ if (eType == CFX_CSSValue::PrimitiveType::kEnum) { + pComputedStyle->m_InheritedData.m_eFontStyle = +- ToFontStyle(pValue.As()->Value()); ++ ToFontStyle(pValue.AsRaw()->Value()); + } + break; + case CFX_CSSProperty::Color: +- if (eType == CFX_CSSPrimitiveType::RGB) { ++ if (eType == CFX_CSSValue::PrimitiveType::kRGB) { + pComputedStyle->m_InheritedData.m_dwFontColor = +- pValue.As()->Value(); ++ pValue.AsRaw()->Value(); + } + break; + case CFX_CSSProperty::MarginLeft: +@@ -323,30 +323,30 @@ void CFX_CSSStyleSelector::ApplyProperty(CFX_CSSProperty eProperty, + } + break; + case CFX_CSSProperty::VerticalAlign: +- if (eType == CFX_CSSPrimitiveType::Enum) { +- pComputedStyle->m_NonInheritedData.m_eVerticalAlign = +- ToVerticalAlign(pValue.As()->Value()); +- } else if (eType == CFX_CSSPrimitiveType::Number) { +- pComputedStyle->m_NonInheritedData.m_eVerticalAlign = ++ if (eType == CFX_CSSValue::PrimitiveType::kEnum) { ++ pComputedStyle->m_NonInheritedData.m_eVerticalAlignType = ++ ToVerticalAlign(pValue.AsRaw()->Value()); ++ } else if (eType == CFX_CSSValue::PrimitiveType::kNumber) { ++ pComputedStyle->m_NonInheritedData.m_eVerticalAlignType = + CFX_CSSVerticalAlign::Number; + pComputedStyle->m_NonInheritedData.m_fVerticalAlign = +- pValue.As()->Apply( ++ pValue.AsRaw()->Apply( + pComputedStyle->m_InheritedData.m_fFontSize); + } + break; + case CFX_CSSProperty::FontVariant: +- if (eType == CFX_CSSPrimitiveType::Enum) { ++ if (eType == CFX_CSSValue::PrimitiveType::kEnum) { + pComputedStyle->m_InheritedData.m_eFontVariant = +- ToFontVariant(pValue.As()->Value()); ++ ToFontVariant(pValue.AsRaw()->Value()); + } + break; + case CFX_CSSProperty::LetterSpacing: +- if (eType == CFX_CSSPrimitiveType::Enum) { ++ if (eType == CFX_CSSValue::PrimitiveType::kEnum) { + pComputedStyle->m_InheritedData.m_LetterSpacing.Set( + CFX_CSSLengthUnit::Normal); +- } else if (eType == CFX_CSSPrimitiveType::Number) { +- if (pValue.As()->Kind() == +- CFX_CSSNumberType::Percent) { ++ } else if (eType == CFX_CSSValue::PrimitiveType::kNumber) { ++ if (pValue.AsRaw()->unit() == ++ CFX_CSSNumberValue::Unit::kPercent) { + break; + } + +@@ -356,12 +356,12 @@ void CFX_CSSStyleSelector::ApplyProperty(CFX_CSSProperty eProperty, + } + break; + case CFX_CSSProperty::WordSpacing: +- if (eType == CFX_CSSPrimitiveType::Enum) { ++ if (eType == CFX_CSSValue::PrimitiveType::kEnum) { + pComputedStyle->m_InheritedData.m_WordSpacing.Set( + CFX_CSSLengthUnit::Normal); +- } else if (eType == CFX_CSSPrimitiveType::Number) { +- if (pValue.As()->Kind() == +- CFX_CSSNumberType::Percent) { ++ } else if (eType == CFX_CSSValue::PrimitiveType::kNumber) { ++ if (pValue.AsRaw()->unit() == ++ CFX_CSSNumberValue::Unit::kPercent) { + break; + } + SetLengthWithPercent(pComputedStyle->m_InheritedData.m_WordSpacing, +@@ -392,17 +392,16 @@ void CFX_CSSStyleSelector::ApplyProperty(CFX_CSSProperty eProperty, + default: + break; + } +- } else if (pValue->GetType() == CFX_CSSPrimitiveType::List) { +- RetainPtr pList = pValue.As(); +- int32_t iCount = pList->CountValues(); +- if (iCount > 0) { ++ } else if (pValue->GetType() == CFX_CSSValue::PrimitiveType::kList) { ++ RetainPtr value_list = pValue.As(); ++ if (!value_list->values().empty()) { + switch (eProperty) { + case CFX_CSSProperty::FontFamily: +- pComputedStyle->m_InheritedData.m_pFontFamily = pList; ++ pComputedStyle->m_InheritedData.m_pFontFamily = std::move(value_list); + break; + case CFX_CSSProperty::TextDecoration: + pComputedStyle->m_NonInheritedData.m_dwTextDecoration = +- ToTextDecoration(pList); ++ ToTextDecoration(value_list); + break; + default: + break; +@@ -473,22 +472,22 @@ CFX_CSSFontStyle CFX_CSSStyleSelector::ToFontStyle( + + bool CFX_CSSStyleSelector::SetLengthWithPercent( + CFX_CSSLength& width, +- CFX_CSSPrimitiveType eType, ++ CFX_CSSValue::PrimitiveType eType, + const RetainPtr& pValue, + float fFontSize) { +- if (eType == CFX_CSSPrimitiveType::Number) { ++ if (eType == CFX_CSSValue::PrimitiveType::kNumber) { + RetainPtr v = pValue.As(); +- if (v->Kind() == CFX_CSSNumberType::Percent) { ++ if (v->unit() == CFX_CSSNumberValue::Unit::kPercent) { + width.Set(CFX_CSSLengthUnit::Percent, +- pValue.As()->Value() / 100.0f); ++ pValue.AsRaw()->value() / 100.0f); + return width.NonZero(); + } + + float fValue = v->Apply(fFontSize); + width.Set(CFX_CSSLengthUnit::Point, fValue); + return width.NonZero(); +- } else if (eType == CFX_CSSPrimitiveType::Enum) { +- switch (pValue.As()->Value()) { ++ } else if (eType == CFX_CSSValue::PrimitiveType::kEnum) { ++ switch (pValue.AsRaw()->Value()) { + case CFX_CSSPropertyValue::Auto: + width.Set(CFX_CSSLengthUnit::Auto); + return true; +@@ -515,19 +514,19 @@ float CFX_CSSStyleSelector::ToFontSize(CFX_CSSPropertyValue eValue, + float fCurFontSize) { + switch (eValue) { + case CFX_CSSPropertyValue::XxSmall: +- return m_fDefFontSize / 1.2f / 1.2f / 1.2f; ++ return m_fDefaultFontSize / 1.2f / 1.2f / 1.2f; + case CFX_CSSPropertyValue::XSmall: +- return m_fDefFontSize / 1.2f / 1.2f; ++ return m_fDefaultFontSize / 1.2f / 1.2f; + case CFX_CSSPropertyValue::Small: +- return m_fDefFontSize / 1.2f; ++ return m_fDefaultFontSize / 1.2f; + case CFX_CSSPropertyValue::Medium: +- return m_fDefFontSize; ++ return m_fDefaultFontSize; + case CFX_CSSPropertyValue::Large: +- return m_fDefFontSize * 1.2f; ++ return m_fDefaultFontSize * 1.2f; + case CFX_CSSPropertyValue::XLarge: +- return m_fDefFontSize * 1.2f * 1.2f; ++ return m_fDefaultFontSize * 1.2f * 1.2f; + case CFX_CSSPropertyValue::XxLarge: +- return m_fDefFontSize * 1.2f * 1.2f * 1.2f; ++ return m_fDefaultFontSize * 1.2f * 1.2f * 1.2f; + case CFX_CSSPropertyValue::Larger: + return fCurFontSize * 1.2f; + case CFX_CSSPropertyValue::Smaller: +@@ -560,29 +559,29 @@ CFX_CSSVerticalAlign CFX_CSSStyleSelector::ToVerticalAlign( + } + } + +-uint32_t CFX_CSSStyleSelector::ToTextDecoration( ++Mask CFX_CSSStyleSelector::ToTextDecoration( + const RetainPtr& pValue) { +- uint32_t dwDecoration = 0; +- for (int32_t i = pValue->CountValues() - 1; i >= 0; --i) { +- const RetainPtr pVal = pValue->GetValue(i); +- if (pVal->GetType() != CFX_CSSPrimitiveType::Enum) ++ Mask dwDecoration; ++ for (const RetainPtr& val : ++ pdfium::base::Reversed(pValue->values())) { ++ if (val->GetType() != CFX_CSSValue::PrimitiveType::kEnum) + continue; + +- switch (pVal.As()->Value()) { ++ switch (val.AsRaw()->Value()) { + case CFX_CSSPropertyValue::Underline: +- dwDecoration |= CFX_CSSTEXTDECORATION_Underline; ++ dwDecoration |= CFX_CSSTEXTDECORATION::kUnderline; + break; + case CFX_CSSPropertyValue::LineThrough: +- dwDecoration |= CFX_CSSTEXTDECORATION_LineThrough; ++ dwDecoration |= CFX_CSSTEXTDECORATION::kLineThrough; + break; + case CFX_CSSPropertyValue::Overline: +- dwDecoration |= CFX_CSSTEXTDECORATION_Overline; ++ dwDecoration |= CFX_CSSTEXTDECORATION::kOverline; + break; + case CFX_CSSPropertyValue::Blink: +- dwDecoration |= CFX_CSSTEXTDECORATION_Blink; ++ dwDecoration |= CFX_CSSTEXTDECORATION::kBlink; + break; + case CFX_CSSPropertyValue::Double: +- dwDecoration |= CFX_CSSTEXTDECORATION_Double; ++ dwDecoration |= CFX_CSSTEXTDECORATION::kDouble; + break; + default: + break; +diff --git a/core/fxcrt/css/cfx_cssstyleselector.h b/core/fxcrt/css/cfx_cssstyleselector.h +index 13a0f7442..7ff694fb8 100644 +--- a/core/fxcrt/css/cfx_cssstyleselector.h ++++ b/core/fxcrt/css/cfx_cssstyleselector.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,16 @@ + #ifndef CORE_FXCRT_CSS_CFX_CSSSTYLESELECTOR_H_ + #define CORE_FXCRT_CSS_CFX_CSSSTYLESELECTOR_H_ + ++#include ++ + #include + #include + + #include "core/fxcrt/css/cfx_css.h" + #include "core/fxcrt/css/cfx_cssrulecollection.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/css/cfx_cssvalue.h" ++#include "core/fxcrt/mask.h" ++#include "core/fxcrt/retain_ptr.h" + + class CFX_CSSComputedStyle; + class CFX_CSSCustomProperty; +@@ -20,7 +24,6 @@ class CFX_CSSDeclaration; + class CFX_CSSPropertyHolder; + class CFX_CSSSelector; + class CFX_CSSStyleSheet; +-class CFX_CSSValue; + class CFX_CSSValueList; + + class CFX_CSSStyleSelector { +@@ -28,12 +31,12 @@ class CFX_CSSStyleSelector { + CFX_CSSStyleSelector(); + ~CFX_CSSStyleSelector(); + +- void SetDefFontSize(float fFontSize); ++ void SetDefaultFontSize(float fFontSize); + void SetUAStyleSheet(std::unique_ptr pSheet); + void UpdateStyleIndex(); + + RetainPtr CreateComputedStyle( +- CFX_CSSComputedStyle* pParentStyle); ++ const CFX_CSSComputedStyle* pParentStyle); + + // Note, the dest style has to be an out param because the CXFA_TextParser + // adds non-inherited data from the parent style. Attempting to copy +@@ -63,7 +66,7 @@ class CFX_CSSStyleSelector { + std::vector* custom); + + bool SetLengthWithPercent(CFX_CSSLength& width, +- CFX_CSSPrimitiveType eType, ++ CFX_CSSValue::PrimitiveType eType, + const RetainPtr& pValue, + float fFontSize); + float ToFontSize(CFX_CSSPropertyValue eValue, float fCurFontSize); +@@ -72,10 +75,11 @@ class CFX_CSSStyleSelector { + uint16_t ToFontWeight(CFX_CSSPropertyValue eValue); + CFX_CSSFontStyle ToFontStyle(CFX_CSSPropertyValue eValue); + CFX_CSSVerticalAlign ToVerticalAlign(CFX_CSSPropertyValue eValue); +- uint32_t ToTextDecoration(const RetainPtr& pList); ++ Mask ToTextDecoration( ++ const RetainPtr& pList); + CFX_CSSFontVariant ToFontVariant(CFX_CSSPropertyValue eValue); + +- float m_fDefFontSize; ++ float m_fDefaultFontSize = 12.0f; + std::unique_ptr m_UAStyles; + CFX_CSSRuleCollection m_UARules; + }; +diff --git a/core/fxcrt/css/cfx_cssstylesheet.cpp b/core/fxcrt/css/cfx_cssstylesheet.cpp +index c7300fb37..b888311bf 100644 +--- a/core/fxcrt/css/cfx_cssstylesheet.cpp ++++ b/core/fxcrt/css/cfx_cssstylesheet.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,75 +12,57 @@ + #include "core/fxcrt/css/cfx_cssdeclaration.h" + #include "core/fxcrt/css/cfx_cssstylerule.h" + #include "core/fxcrt/fx_codepage.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" + +-CFX_CSSStyleSheet::CFX_CSSStyleSheet() {} ++CFX_CSSStyleSheet::CFX_CSSStyleSheet() = default; + +-CFX_CSSStyleSheet::~CFX_CSSStyleSheet() { +- Reset(); +-} +- +-void CFX_CSSStyleSheet::Reset() { +- m_RuleArray.clear(); +- m_StringCache.clear(); +-} ++CFX_CSSStyleSheet::~CFX_CSSStyleSheet() = default; + +-int32_t CFX_CSSStyleSheet::CountRules() const { +- return pdfium::CollectionSize(m_RuleArray); ++size_t CFX_CSSStyleSheet::CountRules() const { ++ return m_RuleArray.size(); + } + +-CFX_CSSStyleRule* CFX_CSSStyleSheet::GetRule(int32_t index) const { ++CFX_CSSStyleRule* CFX_CSSStyleSheet::GetRule(size_t index) const { + return m_RuleArray[index].get(); + } + +-bool CFX_CSSStyleSheet::LoadBuffer(const wchar_t* pBuffer, int32_t iBufSize) { +- ASSERT(pBuffer); +- ASSERT(iBufSize > 0); +- +- auto pSyntax = pdfium::MakeUnique(pBuffer, iBufSize); +- Reset(); +- CFX_CSSSyntaxStatus eStatus; +- do { +- switch (eStatus = pSyntax->DoSyntaxParse()) { +- case CFX_CSSSyntaxStatus::StyleRule: +- eStatus = LoadStyleRule(pSyntax.get(), &m_RuleArray); +- break; +- default: +- break; +- } +- } while (eStatus >= CFX_CSSSyntaxStatus::None); +- +- m_StringCache.clear(); +- return eStatus != CFX_CSSSyntaxStatus::Error; ++bool CFX_CSSStyleSheet::LoadBuffer(WideStringView buffer) { ++ m_RuleArray.clear(); ++ auto pSyntax = std::make_unique(buffer); ++ while (true) { ++ CFX_CSSSyntaxParser::Status eStatus = pSyntax->DoSyntaxParse(); ++ if (eStatus == CFX_CSSSyntaxParser::Status::kStyleRule) ++ eStatus = LoadStyleRule(pSyntax.get()); ++ if (eStatus == CFX_CSSSyntaxParser::Status::kEOS) ++ return true; ++ if (eStatus == CFX_CSSSyntaxParser::Status::kError) ++ return false; ++ } + } + +-CFX_CSSSyntaxStatus CFX_CSSStyleSheet::LoadStyleRule( +- CFX_CSSSyntaxParser* pSyntax, +- std::vector>* ruleArray) { ++CFX_CSSSyntaxParser::Status CFX_CSSStyleSheet::LoadStyleRule( ++ CFX_CSSSyntaxParser* pSyntax) { + std::vector> selectors; +- + CFX_CSSStyleRule* pStyleRule = nullptr; + int32_t iValueLen = 0; + const CFX_CSSData::Property* property = nullptr; + WideString wsName; +- while (1) { ++ while (true) { + switch (pSyntax->DoSyntaxParse()) { +- case CFX_CSSSyntaxStatus::Selector: { ++ case CFX_CSSSyntaxParser::Status::kSelector: { + WideStringView strValue = pSyntax->GetCurrentString(); + auto pSelector = CFX_CSSSelector::FromString(strValue); + if (pSelector) + selectors.push_back(std::move(pSelector)); + break; + } +- case CFX_CSSSyntaxStatus::PropertyName: { ++ case CFX_CSSSyntaxParser::Status::kPropertyName: { + WideStringView strValue = pSyntax->GetCurrentString(); + property = CFX_CSSData::GetPropertyByName(strValue); + if (!property) + wsName = WideString(strValue); + break; + } +- case CFX_CSSSyntaxStatus::PropertyValue: { ++ case CFX_CSSSyntaxParser::Status::kPropertyValue: { + if (property || iValueLen > 0) { + WideStringView strValue = pSyntax->GetCurrentString(); + auto* decl = pStyleRule->GetDeclaration(); +@@ -94,45 +76,45 @@ CFX_CSSSyntaxStatus CFX_CSSStyleSheet::LoadStyleRule( + } + break; + } +- case CFX_CSSSyntaxStatus::DeclOpen: { ++ case CFX_CSSSyntaxParser::Status::kDeclOpen: { + if (!pStyleRule && !selectors.empty()) { +- auto rule = pdfium::MakeUnique(); ++ auto rule = std::make_unique(); + pStyleRule = rule.get(); + pStyleRule->SetSelector(&selectors); +- ruleArray->push_back(std::move(rule)); ++ m_RuleArray.push_back(std::move(rule)); + } else { + SkipRuleSet(pSyntax); +- return CFX_CSSSyntaxStatus::None; ++ return CFX_CSSSyntaxParser::Status::kNone; + } + break; + } +- case CFX_CSSSyntaxStatus::DeclClose: { ++ case CFX_CSSSyntaxParser::Status::kDeclClose: { + if (pStyleRule && pStyleRule->GetDeclaration()->empty()) { +- ruleArray->pop_back(); ++ m_RuleArray.pop_back(); + pStyleRule = nullptr; + } +- return CFX_CSSSyntaxStatus::None; ++ return CFX_CSSSyntaxParser::Status::kNone; + } +- case CFX_CSSSyntaxStatus::EOS: +- return CFX_CSSSyntaxStatus::EOS; +- case CFX_CSSSyntaxStatus::Error: ++ case CFX_CSSSyntaxParser::Status::kEOS: ++ return CFX_CSSSyntaxParser::Status::kEOS; ++ case CFX_CSSSyntaxParser::Status::kError: + default: +- return CFX_CSSSyntaxStatus::Error; ++ return CFX_CSSSyntaxParser::Status::kError; + } + } + } + + void CFX_CSSStyleSheet::SkipRuleSet(CFX_CSSSyntaxParser* pSyntax) { +- while (1) { ++ while (true) { + switch (pSyntax->DoSyntaxParse()) { +- case CFX_CSSSyntaxStatus::Selector: +- case CFX_CSSSyntaxStatus::DeclOpen: +- case CFX_CSSSyntaxStatus::PropertyName: +- case CFX_CSSSyntaxStatus::PropertyValue: ++ case CFX_CSSSyntaxParser::Status::kSelector: ++ case CFX_CSSSyntaxParser::Status::kDeclOpen: ++ case CFX_CSSSyntaxParser::Status::kPropertyName: ++ case CFX_CSSSyntaxParser::Status::kPropertyValue: + break; +- case CFX_CSSSyntaxStatus::DeclClose: +- case CFX_CSSSyntaxStatus::EOS: +- case CFX_CSSSyntaxStatus::Error: ++ case CFX_CSSSyntaxParser::Status::kDeclClose: ++ case CFX_CSSSyntaxParser::Status::kEOS: ++ case CFX_CSSSyntaxParser::Status::kError: + default: + return; + } +diff --git a/core/fxcrt/css/cfx_cssstylesheet.h b/core/fxcrt/css/cfx_cssstylesheet.h +index 3f3a94a84..392877eb6 100644 +--- a/core/fxcrt/css/cfx_cssstylesheet.h ++++ b/core/fxcrt/css/cfx_cssstylesheet.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,11 @@ + #ifndef CORE_FXCRT_CSS_CFX_CSSSTYLESHEET_H_ + #define CORE_FXCRT_CSS_CFX_CSSSTYLESHEET_H_ + +-#include + #include + #include + + #include "core/fxcrt/css/cfx_csssyntaxparser.h" +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" + + class CFX_CSSStyleRule; + +@@ -21,20 +20,15 @@ class CFX_CSSStyleSheet { + CFX_CSSStyleSheet(); + ~CFX_CSSStyleSheet(); + +- bool LoadBuffer(const wchar_t* pBuffer, int32_t iBufSize); +- +- int32_t CountRules() const; +- CFX_CSSStyleRule* GetRule(int32_t index) const; ++ bool LoadBuffer(WideStringView buffer); ++ size_t CountRules() const; ++ CFX_CSSStyleRule* GetRule(size_t index) const; + + private: +- void Reset(); +- CFX_CSSSyntaxStatus LoadStyleRule( +- CFX_CSSSyntaxParser* pSyntax, +- std::vector>* ruleArray); ++ CFX_CSSSyntaxParser::Status LoadStyleRule(CFX_CSSSyntaxParser* pSyntax); + void SkipRuleSet(CFX_CSSSyntaxParser* pSyntax); + + std::vector> m_RuleArray; +- std::map m_StringCache; + }; + + #endif // CORE_FXCRT_CSS_CFX_CSSSTYLESHEET_H_ +diff --git a/core/fxcrt/css/cfx_cssstylesheet_unittest.cpp b/core/fxcrt/css/cfx_cssstylesheet_unittest.cpp +index baa002b05..627c8e9f5 100644 +--- a/core/fxcrt/css/cfx_cssstylesheet_unittest.cpp ++++ b/core/fxcrt/css/cfx_cssstylesheet_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -15,177 +15,236 @@ + #include "core/fxcrt/css/cfx_cssstylerule.h" + #include "core/fxcrt/css/cfx_cssvaluelist.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" + + class CFX_CSSStyleSheetTest : public testing::Test { + public: + void SetUp() override { +- sheet_ = pdfium::MakeUnique(); ++ sheet_ = std::make_unique(); + decl_ = nullptr; + } + + void TearDown() override { decl_ = nullptr; } + +- void LoadAndVerifyDecl(const wchar_t* buf, +- const std::vector& selectors, +- size_t decl_count) { +- ASSERT(sheet_); ++ void VerifyLoadFails(WideStringView buf) { ++ DCHECK(sheet_); ++ EXPECT_FALSE(sheet_->LoadBuffer(buf)); ++ } + +- EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf))); +- EXPECT_EQ(sheet_->CountRules(), 1); ++ void LoadAndVerifyRuleCount(WideStringView buf, size_t rule_count) { ++ DCHECK(sheet_); ++ EXPECT_TRUE(sheet_->LoadBuffer(buf)); ++ EXPECT_EQ(sheet_->CountRules(), rule_count); ++ } + ++ void LoadAndVerifyDecl(WideStringView buf, ++ const std::vector& selectors, ++ size_t decl_count) { ++ LoadAndVerifyRuleCount(buf, 1); + CFX_CSSStyleRule* style = sheet_->GetRule(0); ++ ASSERT_TRUE(style); + EXPECT_EQ(selectors.size(), style->CountSelectorLists()); + + for (size_t i = 0; i < selectors.size(); i++) { +- uint32_t hash = FX_HashCode_GetW(selectors[i].AsStringView(), true); +- EXPECT_EQ(hash, style->GetSelectorList(i)->GetNameHash()); ++ uint32_t hash = FX_HashCode_GetLoweredW(selectors[i].AsStringView()); ++ EXPECT_EQ(hash, style->GetSelectorList(i)->name_hash()); + } + + decl_ = style->GetDeclaration(); ++ ASSERT_TRUE(decl_); + EXPECT_EQ(decl_->PropertyCountForTesting(), decl_count); + } + +- void VerifyFloat(CFX_CSSProperty prop, float val, CFX_CSSNumberType type) { +- ASSERT(decl_); ++ void VerifyFloat(CFX_CSSProperty prop, ++ float val, ++ CFX_CSSNumberValue::Unit unit) { ++ DCHECK(decl_); + + bool important; + RetainPtr v = decl_->GetProperty(prop, &important); +- EXPECT_EQ(v->GetType(), CFX_CSSPrimitiveType::Number); +- EXPECT_EQ(v.As()->Kind(), type); +- EXPECT_EQ(v.As()->Value(), val); ++ EXPECT_EQ(v->GetType(), CFX_CSSValue::PrimitiveType::kNumber); ++ EXPECT_EQ(v.AsRaw()->unit(), unit); ++ EXPECT_EQ(v.AsRaw()->value(), val); + } + + void VerifyEnum(CFX_CSSProperty prop, CFX_CSSPropertyValue val) { +- ASSERT(decl_); ++ DCHECK(decl_); + + bool important; + RetainPtr v = decl_->GetProperty(prop, &important); +- EXPECT_EQ(v->GetType(), CFX_CSSPrimitiveType::Enum); +- EXPECT_EQ(v.As()->Value(), val); ++ EXPECT_EQ(v->GetType(), CFX_CSSValue::PrimitiveType::kEnum); ++ EXPECT_EQ(v.AsRaw()->Value(), val); + } + + void VerifyList(CFX_CSSProperty prop, +- std::vector values) { +- ASSERT(decl_); ++ std::vector expected_values) { ++ DCHECK(decl_); + + bool important; + RetainPtr list = + decl_->GetProperty(prop, &important).As(); +- EXPECT_EQ(list->CountValues(), pdfium::CollectionSize(values)); ++ ASSERT_TRUE(list); ++ const std::vector>& values = list->values(); ++ ASSERT_EQ(values.size(), expected_values.size()); ++ ++ for (size_t i = 0; i < expected_values.size(); ++i) { ++ const auto& val = values[i]; ++ EXPECT_EQ(val->GetType(), CFX_CSSValue::PrimitiveType::kEnum); ++ EXPECT_EQ(val.AsRaw()->Value(), expected_values[i]); ++ } ++ } + +- for (size_t i = 0; i < values.size(); i++) { +- RetainPtr val = list->GetValue(i); +- EXPECT_EQ(val->GetType(), CFX_CSSPrimitiveType::Enum); +- EXPECT_EQ(val.As()->Value(), values[i]); ++ static bool HasSelector(CFX_CSSStyleRule* style, WideStringView selector) { ++ uint32_t hash = FX_HashCode_GetLoweredW(selector); ++ for (size_t i = 0; i < style->CountSelectorLists(); ++i) { ++ if (style->GetSelectorList(i)->name_hash() == hash) ++ return true; + } ++ return false; + } + + std::unique_ptr sheet_; + CFX_CSSDeclaration* decl_; + }; + ++TEST_F(CFX_CSSStyleSheetTest, ParseEmpty) { ++ LoadAndVerifyRuleCount(L"", 0); ++} ++ ++TEST_F(CFX_CSSStyleSheetTest, ParseBlankEmpty) { ++ LoadAndVerifyRuleCount(L" \n\r\t", 0); ++} ++ ++TEST_F(CFX_CSSStyleSheetTest, ParseStrayClose1) { ++ VerifyLoadFails(L"}"); ++} ++ ++TEST_F(CFX_CSSStyleSheetTest, ParseStrayClose2) { ++ LoadAndVerifyRuleCount(L"foo }", 0); ++} ++ ++TEST_F(CFX_CSSStyleSheetTest, ParseStrayClose3) { ++ VerifyLoadFails(L"foo {a: b}}"); ++} ++ ++TEST_F(CFX_CSSStyleSheetTest, ParseEmptySelector) { ++ VerifyLoadFails(L"{a: b}"); ++} ++ ++TEST_F(CFX_CSSStyleSheetTest, ParseEmptyBody) { ++ LoadAndVerifyRuleCount(L"foo {}", 0); ++} ++ + TEST_F(CFX_CSSStyleSheetTest, ParseMultipleSelectors) { + const wchar_t* buf = +- L"a { border: 10px; }\nb { text-decoration: underline; }"; +- EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf))); +- EXPECT_EQ(2, sheet_->CountRules()); ++ L"a { border: 10px; }\n" ++ L"bcdef { text-decoration: underline; }\n" ++ L"* { padding: 0; }\n"; ++ EXPECT_TRUE(sheet_->LoadBuffer(buf)); ++ ASSERT_EQ(3u, sheet_->CountRules()); + + CFX_CSSStyleRule* style = sheet_->GetRule(0); +- EXPECT_EQ(1UL, style->CountSelectorLists()); +- +- bool found_selector = false; +- uint32_t hash = FX_HashCode_GetW(L"a", true); +- for (size_t i = 0; i < style->CountSelectorLists(); i++) { +- if (style->GetSelectorList(i)->GetNameHash() == hash) { +- found_selector = true; +- break; +- } +- } +- EXPECT_TRUE(found_selector); ++ ASSERT_TRUE(style); ++ EXPECT_EQ(1u, style->CountSelectorLists()); ++ EXPECT_TRUE(HasSelector(style, L"a")); + + decl_ = style->GetDeclaration(); +- EXPECT_EQ(4UL, decl_->PropertyCountForTesting()); +- +- VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 10.0, +- CFX_CSSNumberType::Pixels); +- VerifyFloat(CFX_CSSProperty::BorderRightWidth, 10.0, +- CFX_CSSNumberType::Pixels); +- VerifyFloat(CFX_CSSProperty::BorderTopWidth, 10.0, CFX_CSSNumberType::Pixels); +- VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 10.0, +- CFX_CSSNumberType::Pixels); ++ ASSERT_TRUE(decl_); ++ EXPECT_EQ(4u, decl_->PropertyCountForTesting()); ++ ++ VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 10.0f, ++ CFX_CSSNumberValue::Unit::kPixels); ++ VerifyFloat(CFX_CSSProperty::BorderRightWidth, 10.0f, ++ CFX_CSSNumberValue::Unit::kPixels); ++ VerifyFloat(CFX_CSSProperty::BorderTopWidth, 10.0f, ++ CFX_CSSNumberValue::Unit::kPixels); ++ VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 10.0f, ++ CFX_CSSNumberValue::Unit::kPixels); + + style = sheet_->GetRule(1); +- EXPECT_EQ(1UL, style->CountSelectorLists()); +- +- found_selector = false; +- hash = FX_HashCode_GetW(L"b", true); +- for (size_t i = 0; i < style->CountSelectorLists(); i++) { +- if (style->GetSelectorList(i)->GetNameHash() == hash) { +- found_selector = true; +- break; +- } +- } +- EXPECT_TRUE(found_selector); ++ ASSERT_TRUE(style); ++ EXPECT_EQ(1u, style->CountSelectorLists()); ++ EXPECT_TRUE(HasSelector(style, L"bcdef")); ++ EXPECT_FALSE(HasSelector(style, L"bcde")); + + decl_ = style->GetDeclaration(); +- EXPECT_EQ(1UL, decl_->PropertyCountForTesting()); ++ ASSERT_TRUE(decl_); ++ EXPECT_EQ(1u, decl_->PropertyCountForTesting()); + VerifyList(CFX_CSSProperty::TextDecoration, + {CFX_CSSPropertyValue::Underline}); ++ ++ style = sheet_->GetRule(2); ++ ASSERT_TRUE(style); ++ EXPECT_EQ(1u, style->CountSelectorLists()); ++ EXPECT_TRUE(HasSelector(style, L"*")); ++ ++ decl_ = style->GetDeclaration(); ++ ASSERT_TRUE(decl_); ++ EXPECT_EQ(4u, decl_->PropertyCountForTesting()); ++ VerifyFloat(CFX_CSSProperty::PaddingLeft, 0.0f, ++ CFX_CSSNumberValue::Unit::kNumber); ++ VerifyFloat(CFX_CSSProperty::PaddingRight, 0.0f, ++ CFX_CSSNumberValue::Unit::kNumber); ++ VerifyFloat(CFX_CSSProperty::PaddingTop, 0.0f, ++ CFX_CSSNumberValue::Unit::kNumber); ++ VerifyFloat(CFX_CSSProperty::PaddingBottom, 0.0f, ++ CFX_CSSNumberValue::Unit::kNumber); + } + + TEST_F(CFX_CSSStyleSheetTest, ParseChildSelectors) { + const wchar_t* buf = L"a b c { border: 10px; }"; +- EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf))); +- EXPECT_EQ(1, sheet_->CountRules()); ++ EXPECT_TRUE(sheet_->LoadBuffer(buf)); ++ EXPECT_EQ(1u, sheet_->CountRules()); + + CFX_CSSStyleRule* style = sheet_->GetRule(0); +- EXPECT_EQ(1UL, style->CountSelectorLists()); ++ ASSERT_TRUE(style); ++ EXPECT_EQ(1u, style->CountSelectorLists()); + +- auto* sel = style->GetSelectorList(0); +- EXPECT_TRUE(sel != nullptr); +- EXPECT_EQ(FX_HashCode_GetW(L"c", true), sel->GetNameHash()); ++ const auto* sel = style->GetSelectorList(0); ++ ASSERT_TRUE(sel); ++ EXPECT_EQ(FX_HashCode_GetLoweredW(L"c"), sel->name_hash()); + +- sel = sel->GetNextSelector(); +- EXPECT_TRUE(sel != nullptr); +- EXPECT_EQ(FX_HashCode_GetW(L"b", true), sel->GetNameHash()); ++ sel = sel->next_selector(); ++ ASSERT_TRUE(sel); ++ EXPECT_EQ(FX_HashCode_GetLoweredW(L"b"), sel->name_hash()); + +- sel = sel->GetNextSelector(); +- EXPECT_TRUE(sel != nullptr); +- EXPECT_EQ(FX_HashCode_GetW(L"a", true), sel->GetNameHash()); ++ sel = sel->next_selector(); ++ ASSERT_TRUE(sel); ++ EXPECT_EQ(FX_HashCode_GetLoweredW(L"a"), sel->name_hash()); + +- sel = sel->GetNextSelector(); +- EXPECT_TRUE(sel == nullptr); ++ sel = sel->next_selector(); ++ EXPECT_FALSE(sel); + + decl_ = style->GetDeclaration(); +- EXPECT_EQ(4UL, decl_->PropertyCountForTesting()); +- +- VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 10.0, +- CFX_CSSNumberType::Pixels); +- VerifyFloat(CFX_CSSProperty::BorderRightWidth, 10.0, +- CFX_CSSNumberType::Pixels); +- VerifyFloat(CFX_CSSProperty::BorderTopWidth, 10.0, CFX_CSSNumberType::Pixels); +- VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 10.0, +- CFX_CSSNumberType::Pixels); ++ ASSERT_TRUE(decl_); ++ EXPECT_EQ(4u, decl_->PropertyCountForTesting()); ++ ++ VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 10.0f, ++ CFX_CSSNumberValue::Unit::kPixels); ++ VerifyFloat(CFX_CSSProperty::BorderRightWidth, 10.0f, ++ CFX_CSSNumberValue::Unit::kPixels); ++ VerifyFloat(CFX_CSSProperty::BorderTopWidth, 10.0f, ++ CFX_CSSNumberValue::Unit::kPixels); ++ VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 10.0f, ++ CFX_CSSNumberValue::Unit::kPixels); + } + + TEST_F(CFX_CSSStyleSheetTest, ParseUnhandledSelectors) { + const wchar_t* buf = L"a > b { padding: 0; }"; +- EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf))); +- EXPECT_EQ(0, sheet_->CountRules()); ++ EXPECT_TRUE(sheet_->LoadBuffer(buf)); ++ EXPECT_EQ(0u, sheet_->CountRules()); + + buf = L"a[first] { padding: 0; }"; +- EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf))); +- EXPECT_EQ(0, sheet_->CountRules()); ++ EXPECT_TRUE(sheet_->LoadBuffer(buf)); ++ EXPECT_EQ(0u, sheet_->CountRules()); + + buf = L"a+b { padding: 0; }"; +- EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf))); +- EXPECT_EQ(0, sheet_->CountRules()); ++ EXPECT_TRUE(sheet_->LoadBuffer(buf)); ++ EXPECT_EQ(0u, sheet_->CountRules()); + + buf = L"a ^ b { padding: 0; }"; +- EXPECT_TRUE(sheet_->LoadBuffer(buf, wcslen(buf))); +- EXPECT_EQ(0, sheet_->CountRules()); ++ EXPECT_TRUE(sheet_->LoadBuffer(buf)); ++ EXPECT_EQ(0u, sheet_->CountRules()); + } + + TEST_F(CFX_CSSStyleSheetTest, ParseMultipleSelectorsCombined) { +@@ -194,27 +253,32 @@ TEST_F(CFX_CSSStyleSheetTest, ParseMultipleSelectorsCombined) { + + TEST_F(CFX_CSSStyleSheetTest, ParseBorder) { + LoadAndVerifyDecl(L"a { border: 5px; }", {L"a"}, 4); +- VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 5.0, CFX_CSSNumberType::Pixels); ++ VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 5.0, ++ CFX_CSSNumberValue::Unit::kPixels); + VerifyFloat(CFX_CSSProperty::BorderRightWidth, 5.0, +- CFX_CSSNumberType::Pixels); +- VerifyFloat(CFX_CSSProperty::BorderTopWidth, 5.0, CFX_CSSNumberType::Pixels); ++ CFX_CSSNumberValue::Unit::kPixels); ++ VerifyFloat(CFX_CSSProperty::BorderTopWidth, 5.0, ++ CFX_CSSNumberValue::Unit::kPixels); + VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 5.0, +- CFX_CSSNumberType::Pixels); ++ CFX_CSSNumberValue::Unit::kPixels); + } + + TEST_F(CFX_CSSStyleSheetTest, ParseBorderFull) { + LoadAndVerifyDecl(L"a { border: 5px solid red; }", {L"a"}, 4); +- VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 5.0, CFX_CSSNumberType::Pixels); ++ VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 5.0, ++ CFX_CSSNumberValue::Unit::kPixels); + VerifyFloat(CFX_CSSProperty::BorderRightWidth, 5.0, +- CFX_CSSNumberType::Pixels); +- VerifyFloat(CFX_CSSProperty::BorderTopWidth, 5.0, CFX_CSSNumberType::Pixels); ++ CFX_CSSNumberValue::Unit::kPixels); ++ VerifyFloat(CFX_CSSProperty::BorderTopWidth, 5.0, ++ CFX_CSSNumberValue::Unit::kPixels); + VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 5.0, +- CFX_CSSNumberType::Pixels); ++ CFX_CSSNumberValue::Unit::kPixels); + } + + TEST_F(CFX_CSSStyleSheetTest, ParseBorderLeft) { + LoadAndVerifyDecl(L"a { border-left: 2.5pc; }", {L"a"}, 1); +- VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 2.5, CFX_CSSNumberType::Picas); ++ VerifyFloat(CFX_CSSProperty::BorderLeftWidth, 2.5, ++ CFX_CSSNumberValue::Unit::kPicas); + } + + TEST_F(CFX_CSSStyleSheetTest, ParseBorderLeftThick) { +@@ -224,16 +288,49 @@ TEST_F(CFX_CSSStyleSheetTest, ParseBorderLeftThick) { + + TEST_F(CFX_CSSStyleSheetTest, ParseBorderRight) { + LoadAndVerifyDecl(L"a { border-right: 2.5pc; }", {L"a"}, 1); +- VerifyFloat(CFX_CSSProperty::BorderRightWidth, 2.5, CFX_CSSNumberType::Picas); ++ VerifyFloat(CFX_CSSProperty::BorderRightWidth, 2.5, ++ CFX_CSSNumberValue::Unit::kPicas); + } + + TEST_F(CFX_CSSStyleSheetTest, ParseBorderTop) { + LoadAndVerifyDecl(L"a { border-top: 2.5pc; }", {L"a"}, 1); +- VerifyFloat(CFX_CSSProperty::BorderTopWidth, 2.5, CFX_CSSNumberType::Picas); ++ VerifyFloat(CFX_CSSProperty::BorderTopWidth, 2.5, ++ CFX_CSSNumberValue::Unit::kPicas); + } + + TEST_F(CFX_CSSStyleSheetTest, ParseBorderBottom) { + LoadAndVerifyDecl(L"a { border-bottom: 2.5pc; }", {L"a"}, 1); + VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 2.5, +- CFX_CSSNumberType::Picas); ++ CFX_CSSNumberValue::Unit::kPicas); ++} ++ ++TEST_F(CFX_CSSStyleSheetTest, ParseWithCommentsInSelector) { ++ LoadAndVerifyDecl(L"/**{*/a/**}*/ { border-bottom: 2.5pc; }", {L"a"}, 1); ++ VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 2.5, ++ CFX_CSSNumberValue::Unit::kPicas); ++} ++ ++TEST_F(CFX_CSSStyleSheetTest, ParseWithCommentsInProperty) { ++ LoadAndVerifyDecl(L"a { /*}*/border-bottom: 2.5pc; }", {L"a"}, 1); ++ VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 2.5, ++ CFX_CSSNumberValue::Unit::kPicas); ++} ++ ++TEST_F(CFX_CSSStyleSheetTest, ParseWithCommentsInValue) { ++ LoadAndVerifyDecl(L"a { border-bottom: /*;*/2.5pc;/* color:red;*/ }", {L"a"}, ++ 1); ++ VerifyFloat(CFX_CSSProperty::BorderBottomWidth, 2.5, ++ CFX_CSSNumberValue::Unit::kPicas); ++} ++ ++TEST_F(CFX_CSSStyleSheetTest, ParseWithUnterminatedCommentInSelector) { ++ LoadAndVerifyRuleCount(L"a/* { border-bottom: 2.5pc; }", 0); ++} ++ ++TEST_F(CFX_CSSStyleSheetTest, ParseWithUnterminatedCommentInProperty) { ++ LoadAndVerifyRuleCount(L"a { /*border-bottom: 2.5pc; }", 1); ++} ++ ++TEST_F(CFX_CSSStyleSheetTest, ParseWithUnterminatedCommentInValue) { ++ LoadAndVerifyRuleCount(L"a { border-bottom: /*2.5pc; }", 1); + } +diff --git a/core/fxcrt/css/cfx_csssyntaxparser.cpp b/core/fxcrt/css/cfx_csssyntaxparser.cpp +index 9cc9991d5..9341798d8 100644 +--- a/core/fxcrt/css/cfx_csssyntaxparser.cpp ++++ b/core/fxcrt/css/cfx_csssyntaxparser.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,14 +6,11 @@ + + #include "core/fxcrt/css/cfx_csssyntaxparser.h" + +-#include +- + #include "core/fxcrt/css/cfx_cssdata.h" + #include "core/fxcrt/css/cfx_cssdeclaration.h" + #include "core/fxcrt/fx_codepage.h" + #include "core/fxcrt/fx_extension.h" +-#include "third_party/base/compiler_specific.h" +-#include "third_party/base/logging.h" ++#include "third_party/base/notreached.h" + + namespace { + +@@ -24,215 +21,159 @@ bool IsSelectorStart(wchar_t wch) { + + } // namespace + +-CFX_CSSSyntaxParser::CFX_CSSSyntaxParser(const wchar_t* pBuffer, +- int32_t iBufferSize) +- : CFX_CSSSyntaxParser(pBuffer, iBufferSize, 32, false) {} +- +-CFX_CSSSyntaxParser::CFX_CSSSyntaxParser(const wchar_t* pBuffer, +- int32_t iBufferSize, +- int32_t iTextDatSize, +- bool bOnlyDeclaration) +- : m_iTextDataLen(0), +- m_dwCheck(0xFFFFFFFF), +- m_eStatus(CFX_CSSSyntaxStatus::None) { +- ASSERT(pBuffer); +- ASSERT(iBufferSize > 0); +- ASSERT(iTextDatSize > 0); +- +- m_eMode = bOnlyDeclaration ? CFX_CSSSyntaxMode::PropertyName +- : CFX_CSSSyntaxMode::RuleSet; +- m_TextData.InitWithSize(iTextDatSize); +- m_TextPlane.AttachBuffer(pBuffer, iBufferSize); ++CFX_CSSSyntaxParser::CFX_CSSSyntaxParser(WideStringView str) : m_Input(str) {} ++ ++CFX_CSSSyntaxParser::~CFX_CSSSyntaxParser() = default; ++ ++void CFX_CSSSyntaxParser::SetParseOnlyDeclarations() { ++ m_eMode = Mode::kPropertyName; + } + +-CFX_CSSSyntaxParser::~CFX_CSSSyntaxParser() {} +- +-CFX_CSSSyntaxStatus CFX_CSSSyntaxParser::DoSyntaxParse() { +- while (m_eStatus >= CFX_CSSSyntaxStatus::None) { +- if (m_TextPlane.IsEOF()) { +- if (m_eMode == CFX_CSSSyntaxMode::PropertyValue && +- m_TextData.GetLength() > 0) { +- SaveTextData(); +- m_eStatus = CFX_CSSSyntaxStatus::PropertyValue; +- return m_eStatus; +- } +- m_eStatus = CFX_CSSSyntaxStatus::EOS; +- return m_eStatus; +- } +- wchar_t wch; +- while (!m_TextPlane.IsEOF()) { +- wch = m_TextPlane.GetChar(); +- switch (m_eMode) { +- case CFX_CSSSyntaxMode::RuleSet: +- switch (wch) { +- case '}': +- m_TextPlane.MoveNext(); +- if (RestoreMode()) +- return CFX_CSSSyntaxStatus::DeclClose; +- +- m_eStatus = CFX_CSSSyntaxStatus::Error; +- return m_eStatus; +- case '/': +- if (m_TextPlane.GetNextChar() == '*') { +- m_ModeStack.push(m_eMode); +- SwitchMode(CFX_CSSSyntaxMode::Comment); +- break; +- } +- FALLTHROUGH; +- default: +- if (wch <= ' ') { +- m_TextPlane.MoveNext(); +- } else if (IsSelectorStart(wch)) { +- SwitchMode(CFX_CSSSyntaxMode::Selector); +- return CFX_CSSSyntaxStatus::StyleRule; +- } else { +- m_eStatus = CFX_CSSSyntaxStatus::Error; +- return m_eStatus; +- } +- break; +- } +- break; +- case CFX_CSSSyntaxMode::Selector: +- switch (wch) { +- case ',': +- m_TextPlane.MoveNext(); +- SwitchMode(CFX_CSSSyntaxMode::Selector); +- if (m_iTextDataLen > 0) +- return CFX_CSSSyntaxStatus::Selector; ++CFX_CSSSyntaxParser::Status CFX_CSSSyntaxParser::DoSyntaxParse() { ++ m_Output.Clear(); ++ if (m_bHasError) ++ return Status::kError; ++ ++ while (!m_Input.IsEOF()) { ++ wchar_t wch = m_Input.GetChar(); ++ switch (m_eMode) { ++ case Mode::kRuleSet: ++ switch (wch) { ++ case '}': ++ m_bHasError = true; ++ return Status::kError; ++ case '/': ++ if (m_Input.GetNextChar() == '*') { ++ SaveMode(Mode::kRuleSet); ++ m_eMode = Mode::kComment; + break; +- case '{': +- if (m_TextData.GetLength() > 0) { +- SaveTextData(); +- return CFX_CSSSyntaxStatus::Selector; +- } +- m_TextPlane.MoveNext(); +- m_ModeStack.push(CFX_CSSSyntaxMode::RuleSet); +- SwitchMode(CFX_CSSSyntaxMode::PropertyName); +- return CFX_CSSSyntaxStatus::DeclOpen; +- case '/': +- if (m_TextPlane.GetNextChar() == '*') { +- if (SwitchToComment() > 0) +- return CFX_CSSSyntaxStatus::Selector; +- break; +- } +- FALLTHROUGH; +- default: +- AppendChar(wch); ++ } ++ [[fallthrough]]; ++ default: ++ if (wch <= ' ') { ++ m_Input.MoveNext(); ++ } else if (IsSelectorStart(wch)) { ++ m_eMode = Mode::kSelector; ++ return Status::kStyleRule; ++ } else { ++ m_bHasError = true; ++ return Status::kError; ++ } ++ break; ++ } ++ break; ++ case Mode::kSelector: ++ switch (wch) { ++ case ',': ++ m_Input.MoveNext(); ++ if (!m_Output.IsEmpty()) ++ return Status::kSelector; ++ break; ++ case '{': ++ if (!m_Output.IsEmpty()) ++ return Status::kSelector; ++ m_Input.MoveNext(); ++ SaveMode(Mode::kRuleSet); // Back to validate ruleset again. ++ m_eMode = Mode::kPropertyName; ++ return Status::kDeclOpen; ++ case '/': ++ if (m_Input.GetNextChar() == '*') { ++ SaveMode(Mode::kSelector); ++ m_eMode = Mode::kComment; ++ if (!m_Output.IsEmpty()) ++ return Status::kSelector; + break; +- } +- break; +- case CFX_CSSSyntaxMode::PropertyName: +- switch (wch) { +- case ':': +- m_TextPlane.MoveNext(); +- SwitchMode(CFX_CSSSyntaxMode::PropertyValue); +- return CFX_CSSSyntaxStatus::PropertyName; +- case '}': +- m_TextPlane.MoveNext(); +- if (RestoreMode()) +- return CFX_CSSSyntaxStatus::DeclClose; +- +- m_eStatus = CFX_CSSSyntaxStatus::Error; +- return m_eStatus; +- case '/': +- if (m_TextPlane.GetNextChar() == '*') { +- if (SwitchToComment() > 0) +- return CFX_CSSSyntaxStatus::PropertyName; +- break; +- } +- FALLTHROUGH; +- default: +- AppendChar(wch); ++ } ++ [[fallthrough]]; ++ default: ++ m_Output.AppendCharIfNotLeadingBlank(wch); ++ m_Input.MoveNext(); ++ break; ++ } ++ break; ++ case Mode::kPropertyName: ++ switch (wch) { ++ case ':': ++ m_Input.MoveNext(); ++ m_eMode = Mode::kPropertyValue; ++ return Status::kPropertyName; ++ case '}': ++ m_Input.MoveNext(); ++ if (!RestoreMode()) ++ return Status::kError; ++ ++ return Status::kDeclClose; ++ case '/': ++ if (m_Input.GetNextChar() == '*') { ++ SaveMode(Mode::kPropertyName); ++ m_eMode = Mode::kComment; ++ if (!m_Output.IsEmpty()) ++ return Status::kPropertyName; + break; +- } +- break; +- case CFX_CSSSyntaxMode::PropertyValue: +- switch (wch) { +- case ';': +- m_TextPlane.MoveNext(); +- FALLTHROUGH; +- case '}': +- SwitchMode(CFX_CSSSyntaxMode::PropertyName); +- return CFX_CSSSyntaxStatus::PropertyValue; +- case '/': +- if (m_TextPlane.GetNextChar() == '*') { +- if (SwitchToComment() > 0) +- return CFX_CSSSyntaxStatus::PropertyValue; +- break; +- } +- FALLTHROUGH; +- default: +- AppendChar(wch); ++ } ++ [[fallthrough]]; ++ default: ++ m_Output.AppendCharIfNotLeadingBlank(wch); ++ m_Input.MoveNext(); ++ break; ++ } ++ break; ++ case Mode::kPropertyValue: ++ switch (wch) { ++ case ';': ++ m_Input.MoveNext(); ++ [[fallthrough]]; ++ case '}': ++ m_eMode = Mode::kPropertyName; ++ return Status::kPropertyValue; ++ case '/': ++ if (m_Input.GetNextChar() == '*') { ++ SaveMode(Mode::kPropertyValue); ++ m_eMode = Mode::kComment; ++ if (!m_Output.IsEmpty()) ++ return Status::kPropertyValue; + break; +- } +- break; +- case CFX_CSSSyntaxMode::Comment: +- if (wch == '/' && m_TextData.GetLength() > 0 && +- m_TextData.GetBuffer()[m_TextData.GetLength() - 1] == '*') { +- RestoreMode(); +- } else { +- m_TextData.AppendChar(wch); +- } +- m_TextPlane.MoveNext(); +- break; +- case CFX_CSSSyntaxMode::UnknownRule: +- if (wch == ';') +- SwitchMode(CFX_CSSSyntaxMode::RuleSet); +- m_TextPlane.MoveNext(); +- break; +- default: +- NOTREACHED(); +- break; +- } ++ } ++ [[fallthrough]]; ++ default: ++ m_Output.AppendCharIfNotLeadingBlank(wch); ++ m_Input.MoveNext(); ++ break; ++ } ++ break; ++ case Mode::kComment: ++ if (wch == '*' && m_Input.GetNextChar() == '/') { ++ if (!RestoreMode()) ++ return Status::kError; ++ m_Input.MoveNext(); ++ } ++ m_Input.MoveNext(); ++ break; ++ default: ++ NOTREACHED(); ++ break; + } + } +- return m_eStatus; +-} +- +-bool CFX_CSSSyntaxParser::IsImportEnabled() const { +- if ((m_dwCheck & CFX_CSSSYNTAXCHECK_AllowImport) == 0) +- return false; +- if (m_ModeStack.size() > 1) +- return false; +- return true; +-} +- +-bool CFX_CSSSyntaxParser::AppendChar(wchar_t wch) { +- m_TextPlane.MoveNext(); +- if (m_TextData.GetLength() > 0 || wch > ' ') { +- m_TextData.AppendChar(wch); +- return true; +- } +- return false; +-} ++ if (m_eMode == Mode::kPropertyValue && !m_Output.IsEmpty()) ++ return Status::kPropertyValue; + +-int32_t CFX_CSSSyntaxParser::SaveTextData() { +- m_iTextDataLen = m_TextData.TrimEnd(); +- m_TextData.Clear(); +- return m_iTextDataLen; ++ return Status::kEOS; + } + +-void CFX_CSSSyntaxParser::SwitchMode(CFX_CSSSyntaxMode eMode) { +- m_eMode = eMode; +- SaveTextData(); +-} +- +-int32_t CFX_CSSSyntaxParser::SwitchToComment() { +- int32_t iLength = m_TextData.GetLength(); +- m_ModeStack.push(m_eMode); +- SwitchMode(CFX_CSSSyntaxMode::Comment); +- return iLength; ++void CFX_CSSSyntaxParser::SaveMode(Mode mode) { ++ m_ModeStack.push(mode); + } + + bool CFX_CSSSyntaxParser::RestoreMode() { +- if (m_ModeStack.empty()) ++ if (m_ModeStack.empty()) { ++ m_bHasError = true; + return false; +- +- SwitchMode(m_ModeStack.top()); ++ } ++ m_eMode = m_ModeStack.top(); + m_ModeStack.pop(); + return true; + } + + WideStringView CFX_CSSSyntaxParser::GetCurrentString() const { +- return WideStringView(m_TextData.GetBuffer(), m_iTextDataLen); ++ return m_Output.GetTrailingBlankTrimmedString(); + } +diff --git a/core/fxcrt/css/cfx_csssyntaxparser.h b/core/fxcrt/css/cfx_csssyntaxparser.h +index 778f9a327..1f66f30ee 100644 +--- a/core/fxcrt/css/cfx_csssyntaxparser.h ++++ b/core/fxcrt/css/cfx_csssyntaxparser.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,67 +9,48 @@ + + #include + +-#include "core/fxcrt/css/cfx_cssexttextbuf.h" +-#include "core/fxcrt/css/cfx_csstextbuf.h" +-#include "core/fxcrt/fx_string.h" +- +-#define CFX_CSSSYNTAXCHECK_AllowCharset 1 +-#define CFX_CSSSYNTAXCHECK_AllowImport 2 +- +-enum class CFX_CSSSyntaxMode { +- RuleSet, +- Comment, +- UnknownRule, +- Selector, +- PropertyName, +- PropertyValue, +-}; +- +-enum class CFX_CSSSyntaxStatus : uint8_t { +- Error, +- EOS, +- None, +- StyleRule, +- Selector, +- DeclOpen, +- DeclClose, +- PropertyName, +- PropertyValue, +-}; ++#include "core/fxcrt/css/cfx_cssinputtextbuf.h" ++#include "core/fxcrt/css/cfx_cssoutputtextbuf.h" ++#include "core/fxcrt/widestring.h" + + class CFX_CSSSyntaxParser { + public: +- CFX_CSSSyntaxParser(const wchar_t* pBuffer, int32_t iBufferSize); +- CFX_CSSSyntaxParser(const wchar_t* pBuffer, +- int32_t iBufferSize, +- int32_t iTextDatSize, +- bool bOnlyDeclaration); ++ enum class Status : uint8_t { ++ kError, ++ kEOS, ++ kNone, ++ kStyleRule, ++ kSelector, ++ kDeclOpen, ++ kDeclClose, ++ kPropertyName, ++ kPropertyValue, ++ }; ++ ++ explicit CFX_CSSSyntaxParser(WideStringView str); + ~CFX_CSSSyntaxParser(); + +- CFX_CSSSyntaxStatus DoSyntaxParse(); ++ void SetParseOnlyDeclarations(); ++ Status DoSyntaxParse(); + WideStringView GetCurrentString() const; + +- protected: +- void SwitchMode(CFX_CSSSyntaxMode eMode); +- int32_t SwitchToComment(); ++ private: ++ enum class Mode : uint8_t { ++ kRuleSet, ++ kComment, ++ kSelector, ++ kPropertyName, ++ kPropertyValue, ++ }; + ++ void SaveMode(Mode eMode); + bool RestoreMode(); +- bool AppendChar(wchar_t wch); +- int32_t SaveTextData(); +- bool IsCharsetEnabled() const { +- return (m_dwCheck & CFX_CSSSYNTAXCHECK_AllowCharset) != 0; +- } +- void DisableCharset() { m_dwCheck = CFX_CSSSYNTAXCHECK_AllowImport; } +- bool IsImportEnabled() const; +- void DisableImport() { m_dwCheck = 0; } + +- CFX_CSSTextBuf m_TextData; +- CFX_CSSExtTextBuf m_TextPlane; +- int32_t m_iTextDataLen; +- uint32_t m_dwCheck; +- CFX_CSSSyntaxMode m_eMode; +- CFX_CSSSyntaxStatus m_eStatus; +- std::stack m_ModeStack; ++ bool m_bHasError = false; ++ Mode m_eMode = Mode::kRuleSet; ++ CFX_CSSOutputTextBuf m_Output; ++ CFX_CSSInputTextBuf m_Input; ++ std::stack m_ModeStack; + }; + + #endif // CORE_FXCRT_CSS_CFX_CSSSYNTAXPARSER_H_ +diff --git a/core/fxcrt/css/cfx_csssyntaxparser_unittest.cpp b/core/fxcrt/css/cfx_csssyntaxparser_unittest.cpp +new file mode 100644 +index 000000000..74157f20c +--- /dev/null ++++ b/core/fxcrt/css/cfx_csssyntaxparser_unittest.cpp +@@ -0,0 +1,227 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/css/cfx_csssyntaxparser.h" ++ ++#include "testing/gtest/include/gtest/gtest.h" ++ ++// These tests cover the "declaration only" mode of the CSS Syntax Parser ++// (i.e. inline style="" attribute). The cfx_cssstylesheet_unitttest.cpp ++// file covers the full-up selector { ... } parsing. ++ ++TEST(CSSSyntaxParserTest, ParseEmpty) { ++ const wchar_t* input = L""; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseBlank) { ++ const wchar_t* input = L" \n\r\t"; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseMissingColon) { ++ const wchar_t* input = L"foo "; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseMissingValue) { ++ const wchar_t* input = L"foo: "; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"foo", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseSingleProp1) { ++ const wchar_t* input = L"foo:bar"; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"foo", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue, ++ parser.DoSyntaxParse()); ++ EXPECT_EQ(L"bar", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseSingleProp2) { ++ const wchar_t* input = L"foo:bar;"; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"foo", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue, ++ parser.DoSyntaxParse()); ++ EXPECT_EQ(L"bar", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseMissingColonMultiple) { ++ const wchar_t* input = L"foo:bar; baz"; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"foo", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue, ++ parser.DoSyntaxParse()); ++ EXPECT_EQ(L"bar", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseMissingPropertyMultiple) { ++ const wchar_t* input = L"foo:bar; baz: "; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"foo", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue, ++ parser.DoSyntaxParse()); ++ EXPECT_EQ(L"bar", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"baz", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseMultipleProp1) { ++ const wchar_t* input = L"foo : bar; baz : bam"; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"foo", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue, ++ parser.DoSyntaxParse()); ++ EXPECT_EQ(L"bar", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"baz", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue, ++ parser.DoSyntaxParse()); ++ EXPECT_EQ(L"bam", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseMultipleProp2) { ++ const wchar_t* input = L"foo:bar;baz:bam;"; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"foo", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue, ++ parser.DoSyntaxParse()); ++ EXPECT_EQ(L"bar", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"baz", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue, ++ parser.DoSyntaxParse()); ++ EXPECT_EQ(L"bam", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseOpenBrace1) { ++ const wchar_t* input = L"{a:3}"; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ ++ // TODO(tsepez): these should fail on stray punctuation. ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"{a", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue, ++ parser.DoSyntaxParse()); ++ EXPECT_EQ(L"3", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseOpenBrace2) { ++ const wchar_t* input = L"foo {a:3}"; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ ++ // TODO(tsepez): these should fail on stray punctuation. ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"foo {a", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue, ++ parser.DoSyntaxParse()); ++ EXPECT_EQ(L"3", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseOpenBrace3) { ++ const wchar_t* input = L"foo: bar {a:3}"; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ ++ // TODO(tsepez): these should fail on stray punctuation. ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"foo", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue, ++ parser.DoSyntaxParse()); ++ EXPECT_EQ(L"bar {a:3", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseOpenBrace4) { ++ const wchar_t* input = L"foo: bar; {a:3}"; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"foo", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue, ++ parser.DoSyntaxParse()); ++ EXPECT_EQ(L"bar", parser.GetCurrentString()); ++ ++ // TODO(tsepez): these should fail on stray punctuation. ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"{a", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue, ++ parser.DoSyntaxParse()); ++ EXPECT_EQ(L"3", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseCloseBrace1) { ++ const wchar_t* input = L"} foo:bar"; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseCloseBrace2) { ++ const wchar_t* input = L"foo}:bar"; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseCloseBrace3) { ++ const wchar_t* input = L"foo:bar}"; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"foo", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue, ++ parser.DoSyntaxParse()); ++ EXPECT_EQ(L"bar", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse()); ++} ++ ++TEST(CSSSyntaxParserTest, ParseCloseBrace4) { ++ const wchar_t* input = L"foo:bar;}"; ++ CFX_CSSSyntaxParser parser(input); ++ parser.SetParseOnlyDeclarations(); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse()); ++ EXPECT_EQ(L"foo", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue, ++ parser.DoSyntaxParse()); ++ EXPECT_EQ(L"bar", parser.GetCurrentString()); ++ EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse()); ++} +diff --git a/core/fxcrt/css/cfx_csstextbuf.cpp b/core/fxcrt/css/cfx_csstextbuf.cpp +deleted file mode 100644 +index f2f3b9441..000000000 +--- a/core/fxcrt/css/cfx_csstextbuf.cpp ++++ /dev/null +@@ -1,49 +0,0 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fxcrt/css/cfx_csstextbuf.h" +- +-#include "core/fxcrt/fx_memory.h" +- +-CFX_CSSTextBuf::CFX_CSSTextBuf() +- : m_pBuffer(nullptr), m_iBufLen(0), m_iDatLen(0) {} +- +-CFX_CSSTextBuf::~CFX_CSSTextBuf() { +- FX_Free(m_pBuffer); +- m_pBuffer = nullptr; +- m_iDatLen = m_iBufLen; +-} +- +-void CFX_CSSTextBuf::InitWithSize(int32_t iAllocSize) { +- ExpandBuf(iAllocSize); +-} +- +-void CFX_CSSTextBuf::AppendChar(wchar_t wch) { +- if (m_iDatLen >= m_iBufLen) +- ExpandBuf(m_iBufLen * 2); +- +- m_pBuffer[m_iDatLen++] = wch; +-} +- +-int32_t CFX_CSSTextBuf::TrimEnd() { +- while (m_iDatLen > 0 && m_pBuffer[m_iDatLen - 1] <= ' ') +- --m_iDatLen; +- AppendChar(0); +- return --m_iDatLen; +-} +- +-void CFX_CSSTextBuf::ExpandBuf(int32_t iDesiredSize) { +- ASSERT(iDesiredSize > 0); +- if (m_pBuffer && m_iBufLen == iDesiredSize) +- return; +- +- if (m_pBuffer) +- m_pBuffer = FX_Realloc(wchar_t, m_pBuffer, iDesiredSize); +- else +- m_pBuffer = FX_Alloc(wchar_t, iDesiredSize); +- +- m_iBufLen = iDesiredSize; +-} +diff --git a/core/fxcrt/css/cfx_csstextbuf.h b/core/fxcrt/css/cfx_csstextbuf.h +deleted file mode 100644 +index e1b9c64e1..000000000 +--- a/core/fxcrt/css/cfx_csstextbuf.h ++++ /dev/null +@@ -1,35 +0,0 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXCRT_CSS_CFX_CSSTEXTBUF_H_ +-#define CORE_FXCRT_CSS_CFX_CSSTEXTBUF_H_ +- +-#include "core/fxcrt/fx_system.h" +- +-class CFX_CSSTextBuf { +- public: +- CFX_CSSTextBuf(); +- ~CFX_CSSTextBuf(); +- +- void InitWithSize(int32_t iAllocSize); +- void AppendChar(wchar_t wch); +- +- void Clear() { m_iDatLen = 0; } +- +- int32_t TrimEnd(); +- +- int32_t GetLength() const { return m_iDatLen; } +- const wchar_t* GetBuffer() const { return m_pBuffer; } +- +- protected: +- void ExpandBuf(int32_t iDesiredSize); +- +- wchar_t* m_pBuffer; +- int32_t m_iBufLen; +- int32_t m_iDatLen; +-}; +- +-#endif // CORE_FXCRT_CSS_CFX_CSSTEXTBUF_H_ +diff --git a/core/fxcrt/css/cfx_cssvalue.cpp b/core/fxcrt/css/cfx_cssvalue.cpp +index a55fc742b..9e2243709 100644 +--- a/core/fxcrt/css/cfx_cssvalue.cpp ++++ b/core/fxcrt/css/cfx_cssvalue.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,4 +6,6 @@ + + #include "core/fxcrt/css/cfx_cssvalue.h" + +-CFX_CSSValue::CFX_CSSValue(CFX_CSSPrimitiveType type) : m_value(type) {} ++CFX_CSSValue::CFX_CSSValue(PrimitiveType type) : m_value(type) {} ++ ++CFX_CSSValue::~CFX_CSSValue() = default; +diff --git a/core/fxcrt/css/cfx_cssvalue.h b/core/fxcrt/css/cfx_cssvalue.h +index 30aace2b6..ccb8bbe24 100644 +--- a/core/fxcrt/css/cfx_cssvalue.h ++++ b/core/fxcrt/css/cfx_cssvalue.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,13 +12,24 @@ + + class CFX_CSSValue : public Retainable { + public: +- CFX_CSSPrimitiveType GetType() const { return m_value; } ++ enum class PrimitiveType : uint8_t { ++ kUnknown = 0, ++ kNumber, ++ kString, ++ kRGB, ++ kEnum, ++ kFunction, ++ kList, ++ }; ++ ++ PrimitiveType GetType() const { return m_value; } + + protected: +- explicit CFX_CSSValue(CFX_CSSPrimitiveType type); ++ explicit CFX_CSSValue(PrimitiveType type); ++ ~CFX_CSSValue() override; + + private: +- CFX_CSSPrimitiveType m_value; ++ const PrimitiveType m_value; + }; + + #endif // CORE_FXCRT_CSS_CFX_CSSVALUE_H_ +diff --git a/core/fxcrt/css/cfx_cssvaluelist.cpp b/core/fxcrt/css/cfx_cssvaluelist.cpp +index b96f1658a..c1f031128 100644 +--- a/core/fxcrt/css/cfx_cssvaluelist.cpp ++++ b/core/fxcrt/css/cfx_cssvaluelist.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,15 +10,7 @@ + + #include "core/fxcrt/css/cfx_css.h" + +-CFX_CSSValueList::CFX_CSSValueList(std::vector>& list) +- : CFX_CSSValue(CFX_CSSPrimitiveType::List), m_ppList(std::move(list)) {} ++CFX_CSSValueList::CFX_CSSValueList(std::vector> list) ++ : CFX_CSSValue(PrimitiveType::kList), list_(std::move(list)) {} + +-CFX_CSSValueList::~CFX_CSSValueList() {} +- +-int32_t CFX_CSSValueList::CountValues() const { +- return m_ppList.size(); +-} +- +-RetainPtr CFX_CSSValueList::GetValue(int32_t index) const { +- return m_ppList[index]; +-} ++CFX_CSSValueList::~CFX_CSSValueList() = default; +diff --git a/core/fxcrt/css/cfx_cssvaluelist.h b/core/fxcrt/css/cfx_cssvaluelist.h +index 4b87f4c38..c51790a56 100644 +--- a/core/fxcrt/css/cfx_cssvaluelist.h ++++ b/core/fxcrt/css/cfx_cssvaluelist.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,17 +10,17 @@ + #include + + #include "core/fxcrt/css/cfx_cssvalue.h" ++#include "core/fxcrt/retain_ptr.h" + + class CFX_CSSValueList final : public CFX_CSSValue { + public: +- explicit CFX_CSSValueList(std::vector>& list); ++ explicit CFX_CSSValueList(std::vector> list); + ~CFX_CSSValueList() override; + +- int32_t CountValues() const; +- RetainPtr GetValue(int32_t index) const; ++ const std::vector>& values() const { return list_; } + + private: +- std::vector> m_ppList; ++ std::vector> list_; + }; + + #endif // CORE_FXCRT_CSS_CFX_CSSVALUELIST_H_ +diff --git a/core/fxcrt/css/cfx_cssvaluelistparser.cpp b/core/fxcrt/css/cfx_cssvaluelistparser.cpp +index 447ba9f75..3d2613c82 100644 +--- a/core/fxcrt/css/cfx_cssvaluelistparser.cpp ++++ b/core/fxcrt/css/cfx_cssvaluelistparser.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,79 +7,81 @@ + #include "core/fxcrt/css/cfx_cssvaluelistparser.h" + + #include "core/fxcrt/fx_extension.h" ++#include "core/fxcrt/fx_system.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" + + CFX_CSSValueListParser::CFX_CSSValueListParser(const wchar_t* psz, +- int32_t iLen, ++ size_t nLen, + wchar_t separator) +- : m_Separator(separator), m_pCur(psz), m_pEnd(psz + iLen) { +- ASSERT(psz); +- ASSERT(iLen > 0); ++ : m_Separator(separator), m_pCur(psz), m_pEnd(psz + nLen) { ++ DCHECK(psz); ++ DCHECK_NE(nLen, 0); + } + +-bool CFX_CSSValueListParser::NextValue(CFX_CSSPrimitiveType* eType, ++bool CFX_CSSValueListParser::NextValue(CFX_CSSValue::PrimitiveType* eType, + const wchar_t** pStart, +- int32_t* iLength) { ++ size_t* nLength) { + while (m_pCur < m_pEnd && (*m_pCur <= ' ' || *m_pCur == m_Separator)) + ++m_pCur; + + if (m_pCur >= m_pEnd) + return false; + +- *eType = CFX_CSSPrimitiveType::Unknown; ++ *eType = CFX_CSSValue::PrimitiveType::kUnknown; + *pStart = m_pCur; +- *iLength = 0; ++ *nLength = 0; + wchar_t wch = *m_pCur; + if (wch == '#') { +- *iLength = SkipTo(' ', false, false); +- if (*iLength == 4 || *iLength == 7) +- *eType = CFX_CSSPrimitiveType::RGB; ++ *nLength = SkipToChar(' '); ++ if (*nLength == 4 || *nLength == 7) ++ *eType = CFX_CSSValue::PrimitiveType::kRGB; + } else if (FXSYS_IsDecimalDigit(wch) || wch == '.' || wch == '-' || + wch == '+') { + while (m_pCur < m_pEnd && (*m_pCur > ' ' && *m_pCur != m_Separator)) + ++m_pCur; + +- *iLength = m_pCur - *pStart; +- *eType = CFX_CSSPrimitiveType::Number; ++ *nLength = m_pCur - *pStart; ++ *eType = CFX_CSSValue::PrimitiveType::kNumber; + } else if (wch == '\"' || wch == '\'') { + ++(*pStart); + m_pCur++; +- *iLength = SkipTo(wch, false, false); ++ *nLength = SkipToChar(wch); + m_pCur++; +- *eType = CFX_CSSPrimitiveType::String; ++ *eType = CFX_CSSValue::PrimitiveType::kString; + } else if (m_pEnd - m_pCur > 5 && m_pCur[3] == '(') { + if (FXSYS_wcsnicmp(L"rgb", m_pCur, 3) == 0) { +- *iLength = SkipTo(')', false, false) + 1; ++ *nLength = SkipToChar(')') + 1; + m_pCur++; +- *eType = CFX_CSSPrimitiveType::RGB; ++ *eType = CFX_CSSValue::PrimitiveType::kRGB; + } + } else { +- *iLength = SkipTo(m_Separator, true, true); +- *eType = CFX_CSSPrimitiveType::String; ++ *nLength = SkipToCharMatchingParens(m_Separator); ++ *eType = CFX_CSSValue::PrimitiveType::kString; + } +- return m_pCur <= m_pEnd && *iLength > 0; ++ return m_pCur <= m_pEnd && *nLength > 0; + } + +-int32_t CFX_CSSValueListParser::SkipTo(wchar_t wch, +- bool breakOnSpace, +- bool matchBrackets) { ++size_t CFX_CSSValueListParser::SkipToChar(wchar_t wch) { + const wchar_t* pStart = m_pCur; +- int32_t bracketCount = 0; + while (m_pCur < m_pEnd && *m_pCur != wch) { +- if (breakOnSpace && *m_pCur <= ' ') +- break; +- if (!matchBrackets) { +- m_pCur++; +- continue; +- } ++ m_pCur++; ++ } ++ return m_pCur - pStart; ++} + ++size_t CFX_CSSValueListParser::SkipToCharMatchingParens(wchar_t wch) { ++ const wchar_t* pStart = m_pCur; ++ int64_t bracketCount = 0; ++ while (m_pCur < m_pEnd && *m_pCur != wch) { ++ if (*m_pCur <= ' ') ++ break; + if (*m_pCur == '(') + bracketCount++; + else if (*m_pCur == ')') + bracketCount--; +- + m_pCur++; + } +- + while (bracketCount > 0 && m_pCur < m_pEnd) { + if (*m_pCur == ')') + bracketCount--; +diff --git a/core/fxcrt/css/cfx_cssvaluelistparser.h b/core/fxcrt/css/cfx_cssvaluelistparser.h +index 6872ee278..050d0027e 100644 +--- a/core/fxcrt/css/cfx_cssvaluelistparser.h ++++ b/core/fxcrt/css/cfx_cssvaluelistparser.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,20 +7,22 @@ + #ifndef CORE_FXCRT_CSS_CFX_CSSVALUELISTPARSER_H_ + #define CORE_FXCRT_CSS_CFX_CSSVALUELISTPARSER_H_ + +-#include "core/fxcrt/css/cfx_css.h" +-#include "core/fxcrt/fx_system.h" ++#include ++ ++#include "core/fxcrt/css/cfx_cssvalue.h" + + class CFX_CSSValueListParser { + public: +- CFX_CSSValueListParser(const wchar_t* psz, int32_t iLen, wchar_t separator); ++ CFX_CSSValueListParser(const wchar_t* psz, size_t nLen, wchar_t separator); + +- bool NextValue(CFX_CSSPrimitiveType* eType, ++ bool NextValue(CFX_CSSValue::PrimitiveType* eType, + const wchar_t** pStart, +- int32_t* iLength); ++ size_t* nLength); + void UseCommaSeparator() { m_Separator = ','; } + + private: +- int32_t SkipTo(wchar_t wch, bool breakOnSpace, bool matchBrackets); ++ size_t SkipToChar(wchar_t wch); ++ size_t SkipToCharMatchingParens(wchar_t wch); + + wchar_t m_Separator; + const wchar_t* m_pCur; +diff --git a/core/fxcrt/css/cfx_cssvaluelistparser_unittest.cpp b/core/fxcrt/css/cfx_cssvaluelistparser_unittest.cpp +index bb065f8ff..93d0e4afc 100644 +--- a/core/fxcrt/css/cfx_cssvaluelistparser_unittest.cpp ++++ b/core/fxcrt/css/cfx_cssvaluelistparser_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,136 +6,136 @@ + + #include "core/fxcrt/css/cfx_cssvaluelistparser.h" + ++#include ++ + #include "core/fxcrt/widestring.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + + TEST(CFX_CSSValueListParserTest, rgb_short) { +- CFX_CSSPrimitiveType type; ++ CFX_CSSValue::PrimitiveType type; + const wchar_t* start; +- int32_t len; ++ size_t len; + +- auto parser = pdfium::MakeUnique(L"#abc", 4, L' '); ++ auto parser = std::make_unique(L"#abc", 4, L' '); + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::RGB, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kRGB, type); + EXPECT_EQ(L"#abc", WideString(start, len)); + EXPECT_FALSE(parser->NextValue(&type, &start, &len)); + +- parser = pdfium::MakeUnique(L"#abcdef", 7, L' '); ++ parser = std::make_unique(L"#abcdef", 7, L' '); + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::RGB, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kRGB, type); + EXPECT_EQ(L"#abcdef", WideString(start, len)); + EXPECT_FALSE(parser->NextValue(&type, &start, &len)); + + parser = +- pdfium::MakeUnique(L"rgb(1, 255, 4)", 14, L' '); ++ std::make_unique(L"rgb(1, 255, 4)", 14, L' '); + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::RGB, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kRGB, type); + EXPECT_EQ(L"rgb(1, 255, 4)", WideString(start, len)); + +- parser = pdfium::MakeUnique(L"#abcdefghij", 11, L' '); ++ parser = std::make_unique(L"#abcdefghij", 11, L' '); + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::Unknown, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kUnknown, type); + EXPECT_EQ(L"#abcdefghij", WideString(start, len)); + EXPECT_FALSE(parser->NextValue(&type, &start, &len)); + } + + TEST(CFX_CSSValueListParserTest, number_parsing) { +- CFX_CSSPrimitiveType type; ++ CFX_CSSValue::PrimitiveType type; + const wchar_t* start; +- int32_t len; ++ size_t len; + +- auto parser = pdfium::MakeUnique(L"1234", 4, L' '); ++ auto parser = std::make_unique(L"1234", 4, L' '); + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::Number, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type); + EXPECT_EQ(L"1234", WideString(start, len)); + +- parser = pdfium::MakeUnique(L"-1234", 5, L' '); ++ parser = std::make_unique(L"-1234", 5, L' '); + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::Number, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type); + EXPECT_EQ(L"-1234", WideString(start, len)); + +- parser = pdfium::MakeUnique(L"+1234", 5, L' '); ++ parser = std::make_unique(L"+1234", 5, L' '); + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::Number, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type); + EXPECT_EQ(L"+1234", WideString(start, len)); + +- parser = pdfium::MakeUnique(L".1234", 5, L' '); ++ parser = std::make_unique(L".1234", 5, L' '); + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::Number, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type); + EXPECT_EQ(L".1234", WideString(start, len)); + +- parser = pdfium::MakeUnique(L"4321.1234", 9, L' '); ++ parser = std::make_unique(L"4321.1234", 9, L' '); + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::Number, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type); + EXPECT_EQ(L"4321.1234", WideString(start, len)); + + // TODO(dsinclair): These should probably fail but currently don't. +- parser = pdfium::MakeUnique(L"4321.12.34", 10, L' '); ++ parser = std::make_unique(L"4321.12.34", 10, L' '); + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::Number, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type); + EXPECT_EQ(L"4321.12.34", WideString(start, len)); + +- parser = pdfium::MakeUnique(L"43a1.12.34", 10, L' '); ++ parser = std::make_unique(L"43a1.12.34", 10, L' '); + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::Number, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type); + EXPECT_EQ(L"43a1.12.34", WideString(start, len)); + } + + TEST(CFX_CSSValueListParserTest, string_parsing) { +- CFX_CSSPrimitiveType type; ++ CFX_CSSValue::PrimitiveType type; + const wchar_t* start; +- int32_t len; ++ size_t len; + +- auto parser = +- pdfium::MakeUnique(L"'string'", 8, L' '); ++ auto parser = std::make_unique(L"'string'", 8, L' '); + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::String, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kString, type); + EXPECT_EQ(L"string", WideString(start, len)); + +- parser = pdfium::MakeUnique(L"\"another string\"", 16, +- L' '); ++ parser = ++ std::make_unique(L"\"another string\"", 16, L' '); + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::String, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kString, type); + EXPECT_EQ(L"another string", WideString(start, len)); + +- parser = pdfium::MakeUnique(L"standalone", 10, L' '); ++ parser = std::make_unique(L"standalone", 10, L' '); + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::String, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kString, type); + EXPECT_EQ(L"standalone", WideString(start, len)); + } + + TEST(CFX_CSSValueListParserTest, multiparsing) { +- CFX_CSSPrimitiveType type; ++ CFX_CSSValue::PrimitiveType type; + const wchar_t* start; +- int32_t len; ++ size_t len; + +- auto parser = pdfium::MakeUnique(L"1, 2, 3", 7, L','); ++ auto parser = std::make_unique(L"1, 2, 3", 7, L','); + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::Number, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type); + EXPECT_EQ(L"1", WideString(start, len)); + + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::Number, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type); + EXPECT_EQ(L"2", WideString(start, len)); + + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::Number, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type); + EXPECT_EQ(L"3", WideString(start, len)); + + EXPECT_FALSE(parser->NextValue(&type, &start, &len)); + +- parser = pdfium::MakeUnique(L"'str', rgb(1, 2, 3), 4", +- 22, L','); ++ parser = std::make_unique(L"'str', rgb(1, 2, 3), 4", ++ 22, L','); + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::String, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kString, type); + EXPECT_EQ(L"str", WideString(start, len)); + + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::RGB, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kRGB, type); + EXPECT_EQ(L"rgb(1, 2, 3)", WideString(start, len)); + + EXPECT_TRUE(parser->NextValue(&type, &start, &len)); +- EXPECT_EQ(CFX_CSSPrimitiveType::Number, type); ++ EXPECT_EQ(CFX_CSSValue::PrimitiveType::kNumber, type); + EXPECT_EQ(L"4", WideString(start, len)); + } +diff --git a/core/fxcrt/css/properties.inc b/core/fxcrt/css/properties.inc +new file mode 100644 +index 000000000..3f18a0643 +--- /dev/null ++++ b/core/fxcrt/css/properties.inc +@@ -0,0 +1,188 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++CSS_PROP____(BorderLeft, ++ "border-left", ++ 0x04080036, ++ CFX_CSSVALUETYPE_Shorthand) ++CSS_PROP____(Top, ++ "top", ++ 0x0BEDAF33, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(Margin, ++ "margin", ++ 0x0CB016BE, ++ CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(TextIndent, ++ "text-indent", ++ 0x169ADB74, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(Right, ++ "right", ++ 0x193ADE3E, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(PaddingLeft, ++ "padding-left", ++ 0x228CF02F, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(MarginLeft, ++ "margin-left", ++ 0x297C5656, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber | ++ CFX_CSSVALUETYPE_MaybeEnum) ++CSS_PROP____(Border, ++ "border", ++ 0x2A23349E, ++ CFX_CSSVALUETYPE_Shorthand) ++CSS_PROP____(BorderTop, ++ "border-top", ++ 0x2B866ADE, ++ CFX_CSSVALUETYPE_Shorthand) ++CSS_PROP____(Bottom, ++ "bottom", ++ 0x399F02B5, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(PaddingRight, ++ "padding-right", ++ 0x3F616AC2, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(BorderBottom, ++ "border-bottom", ++ 0x452CE780, ++ CFX_CSSVALUETYPE_Shorthand) ++CSS_PROP____(FontFamily, ++ "font-family", ++ 0x574686E6, ++ CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeString) ++CSS_PROP____(FontWeight, ++ "font-weight", ++ 0x6692F60C, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(Color, ++ "color", ++ 0x6E67921F, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeColor) ++CSS_PROP____(LetterSpacing, ++ "letter-spacing", ++ 0x70536102, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(TextAlign, ++ "text-align", ++ 0x7553F1BD, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum) ++CSS_PROP____(BorderRightWidth, ++ "border-right-width", ++ 0x8F5A6036, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(VerticalAlign, ++ "vertical-align", ++ 0x934A87D2, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(PaddingTop, ++ "padding-top", ++ 0x959D22B7, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(FontVariant, ++ "font-variant", ++ 0x9C785779, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum) ++CSS_PROP____(BorderWidth, ++ "border-width", ++ 0xA8DE4FEB, ++ CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(BorderBottomWidth, ++ "border-bottom-width", ++ 0xAE41204D, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(BorderRight, ++ "border-right", ++ 0xB78E9EA9, ++ CFX_CSSVALUETYPE_Shorthand) ++CSS_PROP____(FontSize, ++ "font-size", ++ 0xB93956DF, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(BorderSpacing, ++ "border-spacing", ++ 0xC72030F0, ++ CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(FontStyle, ++ "font-style", ++ 0xCB1950F5, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum) ++CSS_PROP____(Font, ++ "font", ++ 0xCD308B77, ++ CFX_CSSVALUETYPE_Shorthand) ++CSS_PROP____(LineHeight, ++ "line-height", ++ 0xCFCACE2E, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(MarginRight, ++ "margin-right", ++ 0xD13C58C9, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber | ++ CFX_CSSVALUETYPE_MaybeEnum) ++CSS_PROP____(BorderLeftWidth, ++ "border-left-width", ++ 0xD1E93D83, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(Display, ++ "display", ++ 0xD4224C36, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum) ++CSS_PROP____(PaddingBottom, ++ "padding-bottom", ++ 0xE555B3B9, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(BorderTopWidth, ++ "border-top-width", ++ 0xED2CB62B, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(WordSpacing, ++ "word-spacing", ++ 0xEDA63BAE, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(Left, ++ "left", ++ 0xF5AD782B, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeEnum | ++ CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(TextDecoration, ++ "text-decoration", ++ 0xF7C634BA, ++ CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeEnum) ++CSS_PROP____(Padding, ++ "padding", ++ 0xF8C373F7, ++ CFX_CSSVALUETYPE_List | CFX_CSSVALUETYPE_MaybeNumber) ++CSS_PROP____(MarginBottom, ++ "margin-bottom", ++ 0xF93485A0, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber | ++ CFX_CSSVALUETYPE_MaybeEnum) ++CSS_PROP____(MarginTop, ++ "margin-top", ++ 0xFE51DCFE, ++ CFX_CSSVALUETYPE_Primitive | CFX_CSSVALUETYPE_MaybeNumber | ++ CFX_CSSVALUETYPE_MaybeEnum) ++ +diff --git a/core/fxcrt/css/property_values.inc b/core/fxcrt/css/property_values.inc +new file mode 100644 +index 000000000..c93bb3ab8 +--- /dev/null ++++ b/core/fxcrt/css/property_values.inc +@@ -0,0 +1,50 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++CSS_PROP_VALUE____(Bolder, "bolder", 0x009F1058) ++CSS_PROP_VALUE____(None, "none", 0x048B6670) ++CSS_PROP_VALUE____(Dot, "dot", 0x0A48CB27) ++CSS_PROP_VALUE____(Sub, "sub", 0x0BD37FAA) ++CSS_PROP_VALUE____(Top, "top", 0x0BEDAF33) ++CSS_PROP_VALUE____(Right, "right", 0x193ADE3E) ++CSS_PROP_VALUE____(Normal, "normal", 0x247CF3E9) ++CSS_PROP_VALUE____(Auto, "auto", 0x2B35B6D9) ++CSS_PROP_VALUE____(Text, "text", 0x2D08AF85) ++CSS_PROP_VALUE____(XSmall, "x-small", 0x2D2FCAFE) ++CSS_PROP_VALUE____(Thin, "thin", 0x2D574D53) ++CSS_PROP_VALUE____(Small, "small", 0x316A3739) ++CSS_PROP_VALUE____(Bottom, "bottom", 0x399F02B5) ++CSS_PROP_VALUE____(Underline, "underline", 0x3A0273A6) ++CSS_PROP_VALUE____(Double, "double", 0x3D98515B) ++CSS_PROP_VALUE____(Lighter, "lighter", 0x45BEB7AF) ++CSS_PROP_VALUE____(Oblique, "oblique", 0x53EBDDB1) ++CSS_PROP_VALUE____(Super, "super", 0x6A4F842F) ++CSS_PROP_VALUE____(Center, "center", 0x6C51AFC1) ++CSS_PROP_VALUE____(XxLarge, "xx-large", 0x70BB1508) ++CSS_PROP_VALUE____(Smaller, "smaller", 0x849769F0) ++CSS_PROP_VALUE____(Baseline, "baseline", 0x87436BA3) ++CSS_PROP_VALUE____(Thick, "thick", 0x8CC35EB3) ++CSS_PROP_VALUE____(Justify, "justify", 0x8D269CAE) ++CSS_PROP_VALUE____(Middle, "middle", 0x947FA00F) ++CSS_PROP_VALUE____(Medium, "medium", 0xA084A381) ++CSS_PROP_VALUE____(ListItem, "list-item", 0xA32382B8) ++CSS_PROP_VALUE____(XxSmall, "xx-small", 0xADE1FC76) ++CSS_PROP_VALUE____(Bold, "bold", 0xB18313A1) ++CSS_PROP_VALUE____(SmallCaps, "small-caps", 0xB299428D) ++CSS_PROP_VALUE____(Inline, "inline", 0xC02D649F) ++CSS_PROP_VALUE____(Overline, "overline", 0xC0EC9FA4) ++CSS_PROP_VALUE____(TextBottom, "text-bottom", 0xC7D08D87) ++CSS_PROP_VALUE____(Larger, "larger", 0xCD3C409D) ++CSS_PROP_VALUE____(InlineTable, "inline-table", 0xD131F494) ++CSS_PROP_VALUE____(InlineBlock, "inline-block", 0xD26A8BD7) ++CSS_PROP_VALUE____(Blink, "blink", 0xDC36E390) ++CSS_PROP_VALUE____(Block, "block", 0xDCD480AB) ++CSS_PROP_VALUE____(Italic, "italic", 0xE31D5396) ++CSS_PROP_VALUE____(LineThrough, "line-through", 0xE4C5A276) ++CSS_PROP_VALUE____(XLarge, "x-large", 0xF008E390) ++CSS_PROP_VALUE____(Large, "large", 0xF4434FCB) ++CSS_PROP_VALUE____(Left, "left", 0xF5AD782B) ++CSS_PROP_VALUE____(TextTop, "text-top", 0xFCB58D45) +diff --git a/core/fxcrt/data_vector.h b/core/fxcrt/data_vector.h +new file mode 100644 +index 000000000..258c5a9f0 +--- /dev/null ++++ b/core/fxcrt/data_vector.h +@@ -0,0 +1,21 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXCRT_DATA_VECTOR_H_ ++#define CORE_FXCRT_DATA_VECTOR_H_ ++ ++#include ++ ++#include "core/fxcrt/fx_memory_wrappers.h" ++ ++namespace fxcrt { ++ ++template ++using DataVector = std::vector>; ++ ++} // namespace fxcrt ++ ++using fxcrt::DataVector; ++ ++#endif // CORE_FXCRT_DATA_VECTOR_H_ +diff --git a/core/fxcrt/fake_time_test.cpp b/core/fxcrt/fake_time_test.cpp +new file mode 100644 +index 000000000..a3968ff09 +--- /dev/null ++++ b/core/fxcrt/fake_time_test.cpp +@@ -0,0 +1,18 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/fake_time_test.h" ++ ++#include "core/fxcrt/fx_extension.h" ++ ++void FakeTimeTest::SetUp() { ++ // Arbitrary, picked descending digits, 2020-04-23 15:05:21. ++ FXSYS_SetTimeFunction([]() -> time_t { return 1587654321; }); ++ FXSYS_SetLocaltimeFunction([](const time_t* t) { return gmtime(t); }); ++} ++ ++void FakeTimeTest::TearDown() { ++ FXSYS_SetTimeFunction(nullptr); ++ FXSYS_SetLocaltimeFunction(nullptr); ++} +diff --git a/core/fxcrt/fake_time_test.h b/core/fxcrt/fake_time_test.h +new file mode 100644 +index 000000000..9e514d12d +--- /dev/null ++++ b/core/fxcrt/fake_time_test.h +@@ -0,0 +1,16 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXCRT_FAKE_TIME_TEST_H_ ++#define CORE_FXCRT_FAKE_TIME_TEST_H_ ++ ++#include "testing/gtest/include/gtest/gtest.h" ++ ++class FakeTimeTest : public ::testing::Test { ++ public: ++ void SetUp() override; ++ void TearDown() override; ++}; ++ ++#endif // CORE_FXCRT_FAKE_TIME_TEST_H_ +diff --git a/core/fxcrt/fileaccess_iface.h b/core/fxcrt/fileaccess_iface.h +index c744b5404..29aae635b 100644 +--- a/core/fxcrt/fileaccess_iface.h ++++ b/core/fxcrt/fileaccess_iface.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,7 +9,6 @@ + + #include + +-#include "core/fxcrt/fx_safe_types.h" + #include "core/fxcrt/fx_string.h" + + class FileAccessIface { +@@ -17,8 +16,8 @@ class FileAccessIface { + static std::unique_ptr Create(); + virtual ~FileAccessIface() = default; + +- virtual bool Open(ByteStringView fileName, uint32_t dwMode) = 0; +- virtual bool Open(WideStringView fileName, uint32_t dwMode) = 0; ++ // Opens in read-only mode. `fileName` is UTF-8 on all platforms. ++ virtual bool Open(ByteStringView fileName) = 0; + virtual void Close() = 0; + virtual FX_FILESIZE GetSize() const = 0; + virtual FX_FILESIZE GetPosition() const = 0; +diff --git a/core/fxcrt/fixed_size_data_vector.h b/core/fxcrt/fixed_size_data_vector.h +new file mode 100644 +index 000000000..c0adc8ce6 +--- /dev/null ++++ b/core/fxcrt/fixed_size_data_vector.h +@@ -0,0 +1,100 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXCRT_FIXED_SIZE_DATA_VECTOR_H_ ++#define CORE_FXCRT_FIXED_SIZE_DATA_VECTOR_H_ ++ ++#include ++ ++#include ++#include ++ ++#include "core/fxcrt/fx_memory_wrappers.h" ++#include "third_party/base/span.h" ++ ++namespace fxcrt { ++ ++enum class DataVectorAllocOption { ++ kInitialized, ++ kUninitialized, ++ kTryInitialized, ++}; ++ ++// A simple data container that has a fixed size. ++// Unlike std::vector, it cannot be implicitly copied and its data is only ++// accessible using spans. ++// It can either initialize its data with zeros, or leave its data ++// uninitialized. ++template ++class FixedSizeDataVector { ++ public: ++ FixedSizeDataVector() : FixedSizeDataVector(0) {} ++ explicit FixedSizeDataVector(size_t size) ++ : data_(MaybeInit(size, OPTION)), size_(CalculateSize(size, OPTION)) {} ++ FixedSizeDataVector(const FixedSizeDataVector&) = delete; ++ FixedSizeDataVector& operator=(const FixedSizeDataVector&) = delete; ++ template ++ FixedSizeDataVector(FixedSizeDataVector&& that) noexcept { ++ data_ = std::move(that.data_); ++ size_ = that.size_; ++ that.size_ = 0; ++ } ++ template ++ FixedSizeDataVector& operator=( ++ FixedSizeDataVector&& that) noexcept { ++ data_ = std::move(that.data_); ++ size_ = that.size_; ++ that.size_ = 0; ++ return *this; ++ } ++ ~FixedSizeDataVector() = default; ++ ++ operator pdfium::span() const { return span(); } ++ ++ pdfium::span writable_span() { ++ return pdfium::make_span(data_.get(), size_); ++ } ++ ++ pdfium::span span() const { ++ return pdfium::make_span(data_.get(), size_); ++ } ++ ++ size_t size() const { return size_; } ++ bool empty() const { return size_ == 0; } ++ ++ private: ++ friend class FixedSizeDataVector; ++ friend class FixedSizeDataVector; ++ friend class FixedSizeDataVector; ++ ++ static T* MaybeInit(size_t size, DataVectorAllocOption alloc_option) { ++ if (size == 0) ++ return nullptr; ++ switch (alloc_option) { ++ case DataVectorAllocOption::kInitialized: ++ return FX_Alloc(T, size); ++ case DataVectorAllocOption::kUninitialized: ++ return FX_AllocUninit(T, size); ++ case DataVectorAllocOption::kTryInitialized: ++ return FX_TryAlloc(T, size); ++ } ++ } ++ ++ size_t CalculateSize(size_t size, DataVectorAllocOption alloc_option) const { ++ switch (alloc_option) { ++ case DataVectorAllocOption::kInitialized: ++ case DataVectorAllocOption::kUninitialized: ++ return size; ++ case DataVectorAllocOption::kTryInitialized: ++ return data_ ? size : 0; ++ } ++ } ++ ++ std::unique_ptr data_; ++ size_t size_; ++}; ++ ++} // namespace fxcrt ++ ++#endif // CORE_FXCRT_FIXED_SIZE_DATA_VECTOR_H_ +diff --git a/core/fxcrt/fixed_try_alloc_zeroed_data_vector.h b/core/fxcrt/fixed_try_alloc_zeroed_data_vector.h +new file mode 100644 +index 000000000..e7f1bf89d +--- /dev/null ++++ b/core/fxcrt/fixed_try_alloc_zeroed_data_vector.h +@@ -0,0 +1,17 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXCRT_FIXED_TRY_ALLOC_ZEROED_DATA_VECTOR_H_ ++#define CORE_FXCRT_FIXED_TRY_ALLOC_ZEROED_DATA_VECTOR_H_ ++ ++#include "core/fxcrt/fixed_size_data_vector.h" ++ ++// WARNING: Since FX_TryAlloc() can fail, one must always check if a ++// FixedTryAllocZeroedDataVector is empty after creating one. ++template ++using FixedTryAllocZeroedDataVector = ++ fxcrt::FixedSizeDataVector; ++ ++#endif // CORE_FXCRT_FIXED_TRY_ALLOC_ZEROED_DATA_VECTOR_H_ +diff --git a/core/fxcrt/fixed_try_alloc_zeroed_data_vector_unittest.cpp b/core/fxcrt/fixed_try_alloc_zeroed_data_vector_unittest.cpp +new file mode 100644 +index 000000000..e80ba2717 +--- /dev/null ++++ b/core/fxcrt/fixed_try_alloc_zeroed_data_vector_unittest.cpp +@@ -0,0 +1,108 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/fixed_try_alloc_zeroed_data_vector.h" ++ ++#include ++#include ++ ++#include "core/fxcrt/fixed_uninit_data_vector.h" ++#include "core/fxcrt/fixed_zeroed_data_vector.h" ++#include "core/fxcrt/span_util.h" ++#include "testing/gmock/include/gmock/gmock.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/base/span.h" ++ ++TEST(FixedTryAllocZeroedDataVector, NoData) { ++ FixedTryAllocZeroedDataVector vec; ++ EXPECT_EQ(0u, vec.size()); ++ EXPECT_TRUE(vec.empty()); ++ EXPECT_TRUE(vec.span().empty()); ++ EXPECT_TRUE(vec.writable_span().empty()); ++} ++ ++TEST(FixedTryAllocZeroedDataVector, WithData) { ++ FixedTryAllocZeroedDataVector vec(4); ++ EXPECT_FALSE(vec.empty()); ++ EXPECT_EQ(4u, vec.size()); ++ EXPECT_EQ(4u, vec.span().size()); ++ EXPECT_EQ(4u, vec.writable_span().size()); ++ EXPECT_THAT(vec.span(), testing::ElementsAre(0, 0, 0, 0)); ++ ++ constexpr int kData[] = {1, 2, 3, 4}; ++ fxcrt::spancpy(vec.writable_span(), pdfium::make_span(kData)); ++ EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4)); ++} ++ ++TEST(FixedTryAllocZeroedDataVector, AllocFailure) { ++ constexpr size_t kCloseToMaxByteAlloc = ++ std::numeric_limits::max() - 100; ++ FixedTryAllocZeroedDataVector vec(kCloseToMaxByteAlloc); ++ EXPECT_TRUE(vec.empty()); ++ EXPECT_EQ(0u, vec.size()); ++ EXPECT_EQ(0u, vec.span().size()); ++ EXPECT_EQ(0u, vec.writable_span().size()); ++} ++ ++TEST(FixedTryAllocZeroedDataVector, Move) { ++ FixedTryAllocZeroedDataVector vec(4); ++ constexpr int kData[] = {1, 2, 3, 4}; ++ ASSERT_EQ(4u, vec.writable_span().size()); ++ fxcrt::spancpy(vec.writable_span(), pdfium::make_span(kData)); ++ const int* const original_data_ptr = vec.span().data(); ++ ++ FixedTryAllocZeroedDataVector vec2(std::move(vec)); ++ EXPECT_FALSE(vec2.empty()); ++ EXPECT_EQ(4u, vec2.size()); ++ EXPECT_EQ(4u, vec2.span().size()); ++ EXPECT_EQ(4u, vec2.writable_span().size()); ++ EXPECT_THAT(vec2.span(), testing::ElementsAre(1, 2, 3, 4)); ++ EXPECT_EQ(vec2.span().data(), original_data_ptr); ++ ++ EXPECT_EQ(0u, vec.size()); ++ EXPECT_TRUE(vec.empty()); ++ EXPECT_TRUE(vec.span().empty()); ++ EXPECT_TRUE(vec.writable_span().empty()); ++ ++ vec = std::move(vec2); ++ EXPECT_FALSE(vec.empty()); ++ EXPECT_EQ(4u, vec.size()); ++ EXPECT_EQ(4u, vec.span().size()); ++ EXPECT_EQ(4u, vec.writable_span().size()); ++ EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4)); ++ EXPECT_EQ(vec.span().data(), original_data_ptr); ++ ++ EXPECT_EQ(0u, vec2.size()); ++ EXPECT_TRUE(vec2.empty()); ++ EXPECT_TRUE(vec2.span().empty()); ++ EXPECT_TRUE(vec2.writable_span().empty()); ++} ++ ++TEST(FixedTryAllocZeroedDataVector, AssignFromFixedZeroedDataVector) { ++ FixedTryAllocZeroedDataVector vec; ++ ++ FixedZeroedDataVector vec2(4); ++ constexpr int kData[] = {1, 2, 3, 4}; ++ ASSERT_EQ(4u, vec2.writable_span().size()); ++ fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData)); ++ ++ vec = std::move(vec2); ++ EXPECT_TRUE(vec2.empty()); ++ EXPECT_EQ(4u, vec.span().size()); ++ EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4)); ++} ++ ++TEST(FixedTryAllocZeroedDataVector, AssignFromFixedUninitDataVector) { ++ FixedTryAllocZeroedDataVector vec; ++ ++ FixedUninitDataVector vec2(4); ++ constexpr int kData[] = {1, 2, 3, 4}; ++ ASSERT_EQ(4u, vec2.writable_span().size()); ++ fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData)); ++ ++ vec = std::move(vec2); ++ EXPECT_TRUE(vec2.empty()); ++ EXPECT_EQ(4u, vec.span().size()); ++ EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4)); ++} +diff --git a/core/fxcrt/fixed_uninit_data_vector.h b/core/fxcrt/fixed_uninit_data_vector.h +new file mode 100644 +index 000000000..a208f5ecc +--- /dev/null ++++ b/core/fxcrt/fixed_uninit_data_vector.h +@@ -0,0 +1,14 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXCRT_FIXED_UNINIT_DATA_VECTOR_H_ ++#define CORE_FXCRT_FIXED_UNINIT_DATA_VECTOR_H_ ++ ++#include "core/fxcrt/fixed_size_data_vector.h" ++ ++template ++using FixedUninitDataVector = ++ fxcrt::FixedSizeDataVector; ++ ++#endif // CORE_FXCRT_FIXED_UNINIT_DATA_VECTOR_H_ +diff --git a/core/fxcrt/fixed_uninit_data_vector_unittest.cpp b/core/fxcrt/fixed_uninit_data_vector_unittest.cpp +new file mode 100644 +index 000000000..d7a63da68 +--- /dev/null ++++ b/core/fxcrt/fixed_uninit_data_vector_unittest.cpp +@@ -0,0 +1,96 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/fixed_uninit_data_vector.h" ++ ++#include ++ ++#include "core/fxcrt/fixed_try_alloc_zeroed_data_vector.h" ++#include "core/fxcrt/fixed_zeroed_data_vector.h" ++#include "core/fxcrt/span_util.h" ++#include "testing/gmock/include/gmock/gmock.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/base/span.h" ++ ++TEST(FixedUninitDataVector, NoData) { ++ FixedUninitDataVector vec; ++ EXPECT_EQ(0u, vec.size()); ++ EXPECT_TRUE(vec.empty()); ++ EXPECT_TRUE(vec.span().empty()); ++ EXPECT_TRUE(vec.writable_span().empty()); ++} ++ ++TEST(FixedUninitDataVector, WithData) { ++ FixedUninitDataVector vec(4); ++ EXPECT_FALSE(vec.empty()); ++ EXPECT_EQ(4u, vec.size()); ++ EXPECT_EQ(4u, vec.span().size()); ++ EXPECT_EQ(4u, vec.writable_span().size()); ++ ++ constexpr int kData[] = {1, 2, 3, 4}; ++ fxcrt::spancpy(vec.writable_span(), pdfium::make_span(kData)); ++ EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4)); ++} ++ ++TEST(FixedUninitDataVector, Move) { ++ FixedUninitDataVector vec(4); ++ constexpr int kData[] = {1, 2, 3, 4}; ++ ASSERT_EQ(4u, vec.writable_span().size()); ++ fxcrt::spancpy(vec.writable_span(), pdfium::make_span(kData)); ++ const int* const original_data_ptr = vec.span().data(); ++ ++ FixedUninitDataVector vec2(std::move(vec)); ++ EXPECT_FALSE(vec2.empty()); ++ EXPECT_EQ(4u, vec2.size()); ++ EXPECT_EQ(4u, vec2.span().size()); ++ EXPECT_EQ(4u, vec2.writable_span().size()); ++ EXPECT_THAT(vec2.span(), testing::ElementsAre(1, 2, 3, 4)); ++ EXPECT_EQ(vec2.span().data(), original_data_ptr); ++ ++ EXPECT_EQ(0u, vec.size()); ++ EXPECT_TRUE(vec.empty()); ++ EXPECT_TRUE(vec.span().empty()); ++ EXPECT_TRUE(vec.writable_span().empty()); ++ ++ vec = std::move(vec2); ++ EXPECT_FALSE(vec.empty()); ++ EXPECT_EQ(4u, vec.size()); ++ EXPECT_EQ(4u, vec.span().size()); ++ EXPECT_EQ(4u, vec.writable_span().size()); ++ EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4)); ++ EXPECT_EQ(vec.span().data(), original_data_ptr); ++ ++ EXPECT_EQ(0u, vec2.size()); ++ EXPECT_TRUE(vec2.empty()); ++ EXPECT_TRUE(vec2.span().empty()); ++ EXPECT_TRUE(vec2.writable_span().empty()); ++} ++ ++TEST(FixedUninitDataVector, AssignFromFixedZeroedDataVector) { ++ FixedUninitDataVector vec; ++ ++ FixedZeroedDataVector vec2(4); ++ constexpr int kData[] = {1, 2, 3, 4}; ++ ASSERT_EQ(4u, vec2.writable_span().size()); ++ fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData)); ++ ++ vec = std::move(vec2); ++ EXPECT_TRUE(vec2.empty()); ++ EXPECT_EQ(4u, vec.span().size()); ++ EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4)); ++} ++ ++TEST(FixedUninitDataVector, AssignFromFixedTryAllocZeroedDataVector) { ++ FixedUninitDataVector vec; ++ ++ FixedTryAllocZeroedDataVector vec2(4); ++ constexpr int kData[] = {1, 2, 3, 4}; ++ ASSERT_EQ(4u, vec2.writable_span().size()); ++ fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData)); ++ ++ vec = std::move(vec2); ++ EXPECT_TRUE(vec2.empty()); ++ EXPECT_EQ(4u, vec.span().size()); ++ EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4)); ++} +diff --git a/core/fxcrt/fixed_zeroed_data_vector.h b/core/fxcrt/fixed_zeroed_data_vector.h +new file mode 100644 +index 000000000..c8b91a699 +--- /dev/null ++++ b/core/fxcrt/fixed_zeroed_data_vector.h +@@ -0,0 +1,14 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXCRT_FIXED_ZEROED_DATA_VECTOR_H_ ++#define CORE_FXCRT_FIXED_ZEROED_DATA_VECTOR_H_ ++ ++#include "core/fxcrt/fixed_size_data_vector.h" ++ ++template ++using FixedZeroedDataVector = ++ fxcrt::FixedSizeDataVector; ++ ++#endif // CORE_FXCRT_FIXED_ZEROED_DATA_VECTOR_H_ +diff --git a/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp b/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp +new file mode 100644 +index 000000000..ac024bb31 +--- /dev/null ++++ b/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp +@@ -0,0 +1,97 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/fixed_zeroed_data_vector.h" ++ ++#include ++ ++#include "core/fxcrt/fixed_try_alloc_zeroed_data_vector.h" ++#include "core/fxcrt/fixed_uninit_data_vector.h" ++#include "core/fxcrt/span_util.h" ++#include "testing/gmock/include/gmock/gmock.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/base/span.h" ++ ++TEST(FixedZeroedDataVector, NoData) { ++ FixedZeroedDataVector vec; ++ EXPECT_EQ(0u, vec.size()); ++ EXPECT_TRUE(vec.empty()); ++ EXPECT_TRUE(vec.span().empty()); ++ EXPECT_TRUE(vec.writable_span().empty()); ++} ++ ++TEST(FixedZeroedDataVector, WithData) { ++ FixedZeroedDataVector vec(4); ++ EXPECT_FALSE(vec.empty()); ++ EXPECT_EQ(4u, vec.size()); ++ EXPECT_EQ(4u, vec.span().size()); ++ EXPECT_EQ(4u, vec.writable_span().size()); ++ EXPECT_THAT(vec.span(), testing::ElementsAre(0, 0, 0, 0)); ++ ++ constexpr int kData[] = {1, 2, 3, 4}; ++ fxcrt::spancpy(vec.writable_span(), pdfium::make_span(kData)); ++ EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4)); ++} ++ ++TEST(FixedZeroedDataVector, Move) { ++ FixedZeroedDataVector vec(4); ++ constexpr int kData[] = {1, 2, 3, 4}; ++ ASSERT_EQ(4u, vec.writable_span().size()); ++ fxcrt::spancpy(vec.writable_span(), pdfium::make_span(kData)); ++ const int* const original_data_ptr = vec.span().data(); ++ ++ FixedZeroedDataVector vec2(std::move(vec)); ++ EXPECT_FALSE(vec2.empty()); ++ EXPECT_EQ(4u, vec2.size()); ++ EXPECT_EQ(4u, vec2.span().size()); ++ EXPECT_EQ(4u, vec2.writable_span().size()); ++ EXPECT_THAT(vec2.span(), testing::ElementsAre(1, 2, 3, 4)); ++ EXPECT_EQ(vec2.span().data(), original_data_ptr); ++ ++ EXPECT_EQ(0u, vec.size()); ++ EXPECT_TRUE(vec.empty()); ++ EXPECT_TRUE(vec.span().empty()); ++ EXPECT_TRUE(vec.writable_span().empty()); ++ ++ vec = std::move(vec2); ++ EXPECT_FALSE(vec.empty()); ++ EXPECT_EQ(4u, vec.size()); ++ EXPECT_EQ(4u, vec.span().size()); ++ EXPECT_EQ(4u, vec.writable_span().size()); ++ EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4)); ++ EXPECT_EQ(vec.span().data(), original_data_ptr); ++ ++ EXPECT_EQ(0u, vec2.size()); ++ EXPECT_TRUE(vec2.empty()); ++ EXPECT_TRUE(vec2.span().empty()); ++ EXPECT_TRUE(vec2.writable_span().empty()); ++} ++ ++TEST(FixedZeroedDataVector, AssignFromFixedUninitDataVector) { ++ FixedZeroedDataVector vec; ++ ++ FixedUninitDataVector vec2(4); ++ constexpr int kData[] = {1, 2, 3, 4}; ++ ASSERT_EQ(4u, vec2.writable_span().size()); ++ fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData)); ++ ++ vec = std::move(vec2); ++ EXPECT_TRUE(vec2.empty()); ++ EXPECT_EQ(4u, vec.span().size()); ++ EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4)); ++} ++ ++TEST(FixedZeroedDataVector, AssignFromFixedTryAllocZeroedDataVector) { ++ FixedZeroedDataVector vec; ++ ++ FixedTryAllocZeroedDataVector vec2(4); ++ constexpr int kData[] = {1, 2, 3, 4}; ++ ASSERT_EQ(4u, vec2.writable_span().size()); ++ fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData)); ++ ++ vec = std::move(vec2); ++ EXPECT_TRUE(vec2.empty()); ++ EXPECT_EQ(4u, vec.span().size()); ++ EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4)); ++} +diff --git a/core/fxcrt/fx_2d_size.h b/core/fxcrt/fx_2d_size.h +new file mode 100644 +index 000000000..9b7f5d115 +--- /dev/null ++++ b/core/fxcrt/fx_2d_size.h +@@ -0,0 +1,17 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXCRT_FX_2D_SIZE_H_ ++#define CORE_FXCRT_FX_2D_SIZE_H_ ++ ++#include "core/fxcrt/fx_safe_types.h" ++ ++template ++size_t Fx2DSizeOrDie(const T& w, const U& h) { ++ FX_SAFE_SIZE_T safe_size = w; ++ safe_size *= h; ++ return safe_size.ValueOrDie(); ++} ++ ++#endif // CORE_FXCRT_FX_2D_SIZE_H_ +diff --git a/core/fxcrt/fx_bidi.cpp b/core/fxcrt/fx_bidi.cpp +index c5fb5c6f3..756aa12cd 100644 +--- a/core/fxcrt/fx_bidi.cpp ++++ b/core/fxcrt/fx_bidi.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,25 +9,33 @@ + #include + + #include "core/fxcrt/fx_unicode.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check_op.h" + + CFX_BidiChar::CFX_BidiChar() +- : m_CurrentSegment({0, 0, NEUTRAL}), m_LastSegment({0, 0, NEUTRAL}) {} ++ : m_CurrentSegment({0, 0, Direction::kNeutral}), ++ m_LastSegment({0, 0, Direction::kNeutral}) {} + + bool CFX_BidiChar::AppendChar(wchar_t wch) { + Direction direction; +- switch (FX_GetBidiClass(wch)) { ++ switch (pdfium::unicode::GetBidiClass(wch)) { + case FX_BIDICLASS::kL: ++ direction = Direction::kLeft; ++ break; + case FX_BIDICLASS::kAN: + case FX_BIDICLASS::kEN: +- direction = LEFT; ++ case FX_BIDICLASS::kNSM: ++ case FX_BIDICLASS::kCS: ++ case FX_BIDICLASS::kES: ++ case FX_BIDICLASS::kET: ++ case FX_BIDICLASS::kBN: ++ direction = Direction::kLeftWeak; + break; + case FX_BIDICLASS::kR: + case FX_BIDICLASS::kAL: +- direction = RIGHT; ++ direction = Direction::kRight; + break; + default: +- direction = NEUTRAL; ++ direction = Direction::kNeutral; + break; + } + +@@ -40,7 +48,7 @@ bool CFX_BidiChar::AppendChar(wchar_t wch) { + } + + bool CFX_BidiChar::EndChar() { +- StartNewSegment(NEUTRAL); ++ StartNewSegment(Direction::kNeutral); + return m_LastSegment.count > 0; + } + +@@ -60,30 +68,31 @@ CFX_BidiString::CFX_BidiString(const WideString& str) : m_Str(str) { + if (bidi.EndChar()) + m_Order.push_back(bidi.GetSegmentInfo()); + +- size_t nR2L = std::count_if(m_Order.begin(), m_Order.end(), +- [](const CFX_BidiChar::Segment& seg) { +- return seg.direction == CFX_BidiChar::RIGHT; +- }); ++ size_t nR2L = std::count_if( ++ m_Order.begin(), m_Order.end(), [](const CFX_BidiChar::Segment& seg) { ++ return seg.direction == CFX_BidiChar::Direction::kRight; ++ }); + +- size_t nL2R = std::count_if(m_Order.begin(), m_Order.end(), +- [](const CFX_BidiChar::Segment& seg) { +- return seg.direction == CFX_BidiChar::LEFT; +- }); ++ size_t nL2R = std::count_if( ++ m_Order.begin(), m_Order.end(), [](const CFX_BidiChar::Segment& seg) { ++ return seg.direction == CFX_BidiChar::Direction::kLeft; ++ }); + + if (nR2L > 0 && nR2L >= nL2R) + SetOverallDirectionRight(); + } + +-CFX_BidiString::~CFX_BidiString() {} ++CFX_BidiString::~CFX_BidiString() = default; + + CFX_BidiChar::Direction CFX_BidiString::OverallDirection() const { +- ASSERT(m_eOverallDirection != CFX_BidiChar::NEUTRAL); ++ DCHECK_NE(m_eOverallDirection, CFX_BidiChar::Direction::kNeutral); ++ DCHECK_NE(m_eOverallDirection, CFX_BidiChar::Direction::kLeftWeak); + return m_eOverallDirection; + } + + void CFX_BidiString::SetOverallDirectionRight() { +- if (m_eOverallDirection != CFX_BidiChar::RIGHT) { ++ if (m_eOverallDirection != CFX_BidiChar::Direction::kRight) { + std::reverse(m_Order.begin(), m_Order.end()); +- m_eOverallDirection = CFX_BidiChar::RIGHT; ++ m_eOverallDirection = CFX_BidiChar::Direction::kRight; + } + } +diff --git a/core/fxcrt/fx_bidi.h b/core/fxcrt/fx_bidi.h +index ade9a2a8f..19301c467 100644 +--- a/core/fxcrt/fx_bidi.h ++++ b/core/fxcrt/fx_bidi.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,15 +7,16 @@ + #ifndef CORE_FXCRT_FX_BIDI_H_ + #define CORE_FXCRT_FX_BIDI_H_ + ++#include ++ + #include + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/widestring.h" + + // Processes characters and group them into segments based on text direction. + class CFX_BidiChar { + public: +- enum Direction { NEUTRAL, LEFT, RIGHT }; ++ enum class Direction { kNeutral, kLeft, kRight, kLeftWeak }; + struct Segment { + int32_t start; // Start position. + int32_t count; // Character count. +@@ -24,7 +25,7 @@ class CFX_BidiChar { + + CFX_BidiChar(); + +- // Append a character and classify it as left, right, or neutral. ++ // Append a character and classify it as left, left-weak, right, or neutral. + // Returns true if the character has a different direction than the + // existing direction to indicate there is a segment to process. + bool AppendChar(wchar_t wch); +@@ -64,7 +65,7 @@ class CFX_BidiString { + private: + const WideString& m_Str; + std::vector m_Order; +- CFX_BidiChar::Direction m_eOverallDirection = CFX_BidiChar::LEFT; ++ CFX_BidiChar::Direction m_eOverallDirection = CFX_BidiChar::Direction::kLeft; + }; + + #endif // CORE_FXCRT_FX_BIDI_H_ +diff --git a/core/fxcrt/fx_bidi_unittest.cpp b/core/fxcrt/fx_bidi_unittest.cpp +index ad598a674..1b90e321a 100644 +--- a/core/fxcrt/fx_bidi_unittest.cpp ++++ b/core/fxcrt/fx_bidi_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,9 +7,10 @@ + + namespace { + +-const wchar_t kNeutralChar = 32; +-const wchar_t kLeftChar = 65; +-const wchar_t kRightChar = 1424; ++const wchar_t kNeutralChar = 32; // ' ' ++const wchar_t kLeftChar = 65; // 'A' ++const wchar_t kRightChar = 1488; // '×' ++const wchar_t kLeftWeakChar = 46; // '.' + + } // namespace + +@@ -18,7 +19,7 @@ TEST(fxcrt, BidiCharEmpty) { + CFX_BidiChar::Segment info; + + info = bidi.GetSegmentInfo(); +- EXPECT_EQ(CFX_BidiChar::NEUTRAL, info.direction); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, info.direction); + EXPECT_EQ(0, info.start); + EXPECT_EQ(0, info.count); + EXPECT_FALSE(bidi.EndChar()); +@@ -37,13 +38,13 @@ TEST(fxcrt, BidiCharLeft) { + EXPECT_FALSE(bidi.AppendChar(kLeftChar)); + + info = bidi.GetSegmentInfo(); +- EXPECT_EQ(CFX_BidiChar::NEUTRAL, info.direction); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, info.direction); + EXPECT_EQ(0, info.start); + EXPECT_EQ(0, info.count); + + EXPECT_TRUE(bidi.EndChar()); + info = bidi.GetSegmentInfo(); +- EXPECT_EQ(CFX_BidiChar::LEFT, info.direction); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, info.direction); + EXPECT_EQ(0, info.start); + EXPECT_EQ(3, info.count); + +@@ -71,13 +72,47 @@ TEST(fxcrt, BidiCharLeftNeutralRight) { + EXPECT_FALSE(bidi.AppendChar(kNeutralChar)); + EXPECT_TRUE(bidi.AppendChar(kRightChar)); + info = bidi.GetSegmentInfo(); +- EXPECT_EQ(CFX_BidiChar::NEUTRAL, info.direction); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, info.direction); + EXPECT_EQ(3, info.start); + EXPECT_EQ(4, info.count); + + EXPECT_TRUE(bidi.EndChar()); + info = bidi.GetSegmentInfo(); +- EXPECT_EQ(CFX_BidiChar::RIGHT, info.direction); ++ EXPECT_EQ(CFX_BidiChar::Direction::kRight, info.direction); ++ EXPECT_EQ(7, info.start); ++ EXPECT_EQ(1, info.count); ++ ++ EXPECT_FALSE(bidi.EndChar()); ++} ++ ++TEST(fxcrt, BidiCharLeftLeftWeakRight) { ++ CFX_BidiChar bidi; ++ CFX_BidiChar::Segment info; ++ ++ EXPECT_TRUE(bidi.AppendChar(kLeftChar)); ++ info = bidi.GetSegmentInfo(); ++ EXPECT_EQ(0, info.start); ++ EXPECT_EQ(0, info.count); ++ ++ EXPECT_FALSE(bidi.AppendChar(kLeftChar)); ++ EXPECT_FALSE(bidi.AppendChar(kLeftChar)); ++ EXPECT_TRUE(bidi.AppendChar(kLeftWeakChar)); ++ info = bidi.GetSegmentInfo(); ++ EXPECT_EQ(0, info.start); ++ EXPECT_EQ(3, info.count); ++ ++ EXPECT_FALSE(bidi.AppendChar(kLeftWeakChar)); ++ EXPECT_FALSE(bidi.AppendChar(kLeftWeakChar)); ++ EXPECT_FALSE(bidi.AppendChar(kLeftWeakChar)); ++ EXPECT_TRUE(bidi.AppendChar(kRightChar)); ++ info = bidi.GetSegmentInfo(); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeftWeak, info.direction); ++ EXPECT_EQ(3, info.start); ++ EXPECT_EQ(4, info.count); ++ ++ EXPECT_TRUE(bidi.EndChar()); ++ info = bidi.GetSegmentInfo(); ++ EXPECT_EQ(CFX_BidiChar::Direction::kRight, info.direction); + EXPECT_EQ(7, info.start); + EXPECT_EQ(1, info.count); + +@@ -105,13 +140,13 @@ TEST(fxcrt, BidiCharLeftRightLeft) { + EXPECT_FALSE(bidi.AppendChar(kRightChar)); + EXPECT_TRUE(bidi.AppendChar(kLeftChar)); + info = bidi.GetSegmentInfo(); +- EXPECT_EQ(CFX_BidiChar::RIGHT, info.direction); ++ EXPECT_EQ(CFX_BidiChar::Direction::kRight, info.direction); + EXPECT_EQ(3, info.start); + EXPECT_EQ(4, info.count); + + EXPECT_TRUE(bidi.EndChar()); + info = bidi.GetSegmentInfo(); +- EXPECT_EQ(CFX_BidiChar::LEFT, info.direction); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, info.direction); + EXPECT_EQ(7, info.start); + EXPECT_EQ(1, info.count); + +@@ -120,7 +155,7 @@ TEST(fxcrt, BidiCharLeftRightLeft) { + + TEST(fxcrt, BidiStringEmpty) { + CFX_BidiString bidi(L""); +- EXPECT_EQ(CFX_BidiChar::LEFT, bidi.OverallDirection()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection()); + EXPECT_TRUE(bidi.begin() == bidi.end()); + } + +@@ -128,28 +163,28 @@ TEST(fxcrt, BidiStringAllNeutral) { + { + const wchar_t str[] = {kNeutralChar, 0}; + CFX_BidiString bidi(str); +- EXPECT_EQ(CFX_BidiChar::LEFT, bidi.OverallDirection()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection()); + + auto it = bidi.begin(); +- ASSERT_FALSE(it == bidi.end()); ++ ASSERT_NE(it, bidi.end()); + EXPECT_EQ(0, it->start); + EXPECT_EQ(1, it->count); +- EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction); + ++it; +- EXPECT_TRUE(it == bidi.end()); ++ EXPECT_EQ(it, bidi.end()); + } + { + const wchar_t str[] = {kNeutralChar, kNeutralChar, kNeutralChar, 0}; + CFX_BidiString bidi(str); +- EXPECT_EQ(CFX_BidiChar::LEFT, bidi.OverallDirection()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection()); + + auto it = bidi.begin(); +- ASSERT_FALSE(it == bidi.end()); ++ ASSERT_NE(it, bidi.end()); + EXPECT_EQ(0, it->start); + EXPECT_EQ(3, it->count); +- EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction); + ++it; +- EXPECT_TRUE(it == bidi.end()); ++ EXPECT_EQ(it, bidi.end()); + } + } + +@@ -157,44 +192,87 @@ TEST(fxcrt, BidiStringAllLeft) { + { + const wchar_t str[] = {kLeftChar, 0}; + CFX_BidiString bidi(str); +- EXPECT_EQ(CFX_BidiChar::LEFT, bidi.OverallDirection()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection()); + + auto it = bidi.begin(); +- ASSERT_FALSE(it == bidi.end()); ++ ASSERT_NE(it, bidi.end()); + EXPECT_EQ(0, it->start); + EXPECT_EQ(0, it->count); +- EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; + EXPECT_EQ(0, it->start); + EXPECT_EQ(1, it->count); +- EXPECT_EQ(CFX_BidiChar::LEFT, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; +- EXPECT_TRUE(it == bidi.end()); ++ EXPECT_EQ(it, bidi.end()); + } + { + const wchar_t str[] = {kLeftChar, kLeftChar, kLeftChar, 0}; + CFX_BidiString bidi(str); +- EXPECT_EQ(CFX_BidiChar::LEFT, bidi.OverallDirection()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection()); + + auto it = bidi.begin(); +- ASSERT_FALSE(it == bidi.end()); ++ ASSERT_NE(it, bidi.end()); + EXPECT_EQ(0, it->start); + EXPECT_EQ(0, it->count); +- EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; + EXPECT_EQ(0, it->start); + EXPECT_EQ(3, it->count); +- EXPECT_EQ(CFX_BidiChar::LEFT, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; +- EXPECT_TRUE(it == bidi.end()); ++ EXPECT_EQ(it, bidi.end()); ++ } ++} ++ ++TEST(fxcrt, BidiStringAllLeftWeak) { ++ { ++ const wchar_t str[] = {kLeftWeakChar, 0}; ++ CFX_BidiString bidi(str); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection()); ++ ++ auto it = bidi.begin(); ++ ASSERT_NE(it, bidi.end()); ++ EXPECT_EQ(0, it->start); ++ EXPECT_EQ(0, it->count); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction); ++ ++ ++it; ++ ASSERT_NE(it, bidi.end()); ++ EXPECT_EQ(0, it->start); ++ EXPECT_EQ(1, it->count); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeftWeak, it->direction); ++ ++ ++it; ++ EXPECT_EQ(it, bidi.end()); ++ } ++ { ++ const wchar_t str[] = {kLeftWeakChar, kLeftWeakChar, kLeftWeakChar, 0}; ++ CFX_BidiString bidi(str); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection()); ++ ++ auto it = bidi.begin(); ++ ASSERT_NE(it, bidi.end()); ++ EXPECT_EQ(0, it->start); ++ EXPECT_EQ(0, it->count); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction); ++ ++ ++it; ++ ASSERT_NE(it, bidi.end()); ++ EXPECT_EQ(0, it->start); ++ EXPECT_EQ(3, it->count); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeftWeak, it->direction); ++ ++ ++it; ++ EXPECT_EQ(it, bidi.end()); + } + } + +@@ -202,164 +280,209 @@ TEST(fxcrt, BidiStringAllRight) { + { + const wchar_t str[] = {kRightChar, 0}; + CFX_BidiString bidi(str); +- EXPECT_EQ(CFX_BidiChar::RIGHT, bidi.OverallDirection()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kRight, bidi.OverallDirection()); + + auto it = bidi.begin(); + EXPECT_EQ(0, it->start); + EXPECT_EQ(1, it->count); +- EXPECT_EQ(CFX_BidiChar::RIGHT, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kRight, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; +- ASSERT_FALSE(it == bidi.end()); ++ ASSERT_NE(it, bidi.end()); + EXPECT_EQ(0, it->start); + EXPECT_EQ(0, it->count); +- EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; +- EXPECT_TRUE(it == bidi.end()); ++ EXPECT_EQ(it, bidi.end()); + } + { + const wchar_t str[] = {kRightChar, kRightChar, kRightChar, 0}; + CFX_BidiString bidi(str); +- EXPECT_EQ(CFX_BidiChar::RIGHT, bidi.OverallDirection()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kRight, bidi.OverallDirection()); + + auto it = bidi.begin(); + EXPECT_EQ(0, it->start); + EXPECT_EQ(3, it->count); +- EXPECT_EQ(CFX_BidiChar::RIGHT, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kRight, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; +- ASSERT_FALSE(it == bidi.end()); ++ ASSERT_NE(it, bidi.end()); + EXPECT_EQ(0, it->start); + EXPECT_EQ(0, it->count); +- EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; +- EXPECT_TRUE(it == bidi.end()); ++ EXPECT_EQ(it, bidi.end()); + } + } + + TEST(fxcrt, BidiStringLeftNeutralLeftRight) { + const wchar_t str[] = {kLeftChar, kNeutralChar, kLeftChar, kRightChar, 0}; + CFX_BidiString bidi(str); +- EXPECT_EQ(CFX_BidiChar::LEFT, bidi.OverallDirection()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection()); + + auto it = bidi.begin(); +- ASSERT_FALSE(it == bidi.end()); ++ ASSERT_NE(it, bidi.end()); + EXPECT_EQ(0, it->start); + EXPECT_EQ(0, it->count); +- EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; + EXPECT_EQ(0, it->start); + EXPECT_EQ(1, it->count); +- EXPECT_EQ(CFX_BidiChar::LEFT, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; + EXPECT_EQ(1, it->start); + EXPECT_EQ(1, it->count); +- EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; + EXPECT_EQ(2, it->start); + EXPECT_EQ(1, it->count); +- EXPECT_EQ(CFX_BidiChar::LEFT, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; + EXPECT_EQ(3, it->start); + EXPECT_EQ(1, it->count); +- EXPECT_EQ(CFX_BidiChar::RIGHT, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kRight, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; +- EXPECT_TRUE(it == bidi.end()); ++ EXPECT_EQ(it, bidi.end()); + } + + TEST(fxcrt, BidiStringRightNeutralLeftRight) { + const wchar_t str[] = {kRightChar, kNeutralChar, kLeftChar, kRightChar, 0}; + CFX_BidiString bidi(str); +- EXPECT_EQ(CFX_BidiChar::RIGHT, bidi.OverallDirection()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kRight, bidi.OverallDirection()); + + auto it = bidi.begin(); + EXPECT_EQ(3, it->start); + EXPECT_EQ(1, it->count); +- EXPECT_EQ(CFX_BidiChar::RIGHT, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kRight, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; + EXPECT_EQ(2, it->start); + EXPECT_EQ(1, it->count); +- EXPECT_EQ(CFX_BidiChar::LEFT, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; + EXPECT_EQ(1, it->start); + EXPECT_EQ(1, it->count); +- EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; + EXPECT_EQ(0, it->start); + EXPECT_EQ(1, it->count); +- EXPECT_EQ(CFX_BidiChar::RIGHT, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kRight, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; +- ASSERT_FALSE(it == bidi.end()); ++ ASSERT_NE(it, bidi.end()); + EXPECT_EQ(0, it->start); + EXPECT_EQ(0, it->count); +- EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction); ++ ASSERT_NE(it, bidi.end()); + + ++it; +- EXPECT_TRUE(it == bidi.end()); ++ EXPECT_EQ(it, bidi.end()); ++} ++ ++TEST(fxcrt, BidiStringRightLeftWeakLeftRight) { ++ const wchar_t str[] = {kRightChar, kLeftWeakChar, kLeftChar, kRightChar, 0}; ++ CFX_BidiString bidi(str); ++ EXPECT_EQ(CFX_BidiChar::Direction::kRight, bidi.OverallDirection()); ++ ++ auto it = bidi.begin(); ++ ASSERT_NE(it, bidi.end()); ++ EXPECT_EQ(3, it->start); ++ EXPECT_EQ(1, it->count); ++ EXPECT_EQ(CFX_BidiChar::Direction::kRight, it->direction); ++ ++ ++it; ++ ASSERT_NE(it, bidi.end()); ++ EXPECT_EQ(2, it->start); ++ EXPECT_EQ(1, it->count); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, it->direction); ++ ++ ++it; ++ ASSERT_NE(it, bidi.end()); ++ EXPECT_EQ(1, it->start); ++ EXPECT_EQ(1, it->count); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeftWeak, it->direction); ++ ++ ++it; ++ ASSERT_NE(it, bidi.end()); ++ EXPECT_EQ(0, it->start); ++ EXPECT_EQ(1, it->count); ++ EXPECT_EQ(CFX_BidiChar::Direction::kRight, it->direction); ++ ++ ++it; ++ ASSERT_NE(it, bidi.end()); ++ EXPECT_EQ(0, it->start); ++ EXPECT_EQ(0, it->count); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction); ++ ++ ++it; ++ EXPECT_EQ(it, bidi.end()); + } + + TEST(fxcrt, BidiStringReverse) { +- const wchar_t str[] = {kLeftChar, kNeutralChar, kRightChar, kLeftChar, 0}; ++ const wchar_t str[] = {kLeftChar, kNeutralChar, kRightChar, ++ kLeftWeakChar, kLeftChar, 0}; + CFX_BidiString bidi(str); +- EXPECT_EQ(CFX_BidiChar::LEFT, bidi.OverallDirection()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, bidi.OverallDirection()); + bidi.SetOverallDirectionRight(); + + auto it = bidi.begin(); +- ASSERT_FALSE(it == bidi.end()); ++ ASSERT_NE(it, bidi.end()); ++ EXPECT_EQ(4, it->start); ++ EXPECT_EQ(1, it->count); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, it->direction); ++ ++ ++it; ++ ASSERT_NE(it, bidi.end()); + EXPECT_EQ(3, it->start); + EXPECT_EQ(1, it->count); +- EXPECT_EQ(CFX_BidiChar::LEFT, it->direction); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeftWeak, it->direction); + + ++it; +- ASSERT_FALSE(it == bidi.end()); ++ ASSERT_NE(it, bidi.end()); + EXPECT_EQ(2, it->start); + EXPECT_EQ(1, it->count); +- EXPECT_EQ(CFX_BidiChar::RIGHT, it->direction); +- ASSERT_FALSE(it == bidi.end()); ++ EXPECT_EQ(CFX_BidiChar::Direction::kRight, it->direction); + + ++it; +- ASSERT_FALSE(it == bidi.end()); ++ ASSERT_NE(it, bidi.end()); + EXPECT_EQ(1, it->start); + EXPECT_EQ(1, it->count); +- EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction); + + ++it; +- ASSERT_FALSE(it == bidi.end()); ++ ASSERT_NE(it, bidi.end()); + EXPECT_EQ(0, it->start); + EXPECT_EQ(1, it->count); +- EXPECT_EQ(CFX_BidiChar::LEFT, it->direction); ++ EXPECT_EQ(CFX_BidiChar::Direction::kLeft, it->direction); + + ++it; +- ASSERT_FALSE(it == bidi.end()); ++ ASSERT_NE(it, bidi.end()); + EXPECT_EQ(0, it->start); + EXPECT_EQ(0, it->count); +- EXPECT_EQ(CFX_BidiChar::NEUTRAL, it->direction); ++ EXPECT_EQ(CFX_BidiChar::Direction::kNeutral, it->direction); + + ++it; +- EXPECT_TRUE(it == bidi.end()); ++ EXPECT_EQ(it, bidi.end()); + } +diff --git a/core/fxcrt/fx_codepage.cpp b/core/fxcrt/fx_codepage.cpp +index c85291fe4..20a5bfaf9 100644 +--- a/core/fxcrt/fx_codepage.cpp ++++ b/core/fxcrt/fx_codepage.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,9 +10,16 @@ + #include + #include + ++#include "build/build_config.h" ++#include "third_party/base/numerics/safe_math.h" ++ ++#if BUILDFLAG(IS_WIN) ++#include ++#endif ++ + namespace { + +-const uint16_t g_FX_MSDOSThaiUnicodes[128] = { ++const uint16_t kFX_MSDOSThaiUnicodes[128] = { + 0x20AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x2026, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2018, + 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x0000, 0x0000, +@@ -30,7 +37,7 @@ const uint16_t g_FX_MSDOSThaiUnicodes[128] = { + 0x0000, 0x0000, + }; + +-const uint16_t g_FX_MSWinEasternEuropeanUnicodes[128] = { ++const uint16_t kFX_MSWinEasternEuropeanUnicodes[128] = { + 0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000, + 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179, 0x0000, 0x2018, + 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0161, +@@ -48,7 +55,7 @@ const uint16_t g_FX_MSWinEasternEuropeanUnicodes[128] = { + 0x0163, 0x02D9, + }; + +-const uint16_t g_FX_MSWinCyrillicUnicodes[128] = { ++const uint16_t kFX_MSWinCyrillicUnicodes[128] = { + 0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, 0x20AC, + 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F, 0x0452, 0x2018, + 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0459, +@@ -66,7 +73,7 @@ const uint16_t g_FX_MSWinCyrillicUnicodes[128] = { + 0x044E, 0x044F, + }; + +-const uint16_t g_FX_MSWinGreekUnicodes[128] = { ++const uint16_t kFX_MSWinGreekUnicodes[128] = { + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000, + 0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2018, + 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0000, +@@ -84,7 +91,7 @@ const uint16_t g_FX_MSWinGreekUnicodes[128] = { + 0x03CE, 0x0000, + }; + +-const uint16_t g_FX_MSWinTurkishUnicodes[128] = { ++const uint16_t kFX_MSWinTurkishUnicodes[128] = { + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, + 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000, 0x0000, 0x2018, + 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, +@@ -102,7 +109,7 @@ const uint16_t g_FX_MSWinTurkishUnicodes[128] = { + 0x015F, 0x00FF, + }; + +-const uint16_t g_FX_MSWinHebrewUnicodes[128] = { ++const uint16_t kFX_MSWinHebrewUnicodes[128] = { + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, + 0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2018, + 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0000, +@@ -120,7 +127,7 @@ const uint16_t g_FX_MSWinHebrewUnicodes[128] = { + 0x200F, 0x0000, + }; + +-const uint16_t g_FX_MSWinArabicUnicodes[128] = { ++const uint16_t kFX_MSWinArabicUnicodes[128] = { + 0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, + 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688, 0x06AF, 0x2018, + 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x06A9, 0x2122, 0x0691, +@@ -138,7 +145,7 @@ const uint16_t g_FX_MSWinArabicUnicodes[128] = { + 0x200F, 0x06D2, + }; + +-const uint16_t g_FX_MSWinBalticUnicodes[128] = { ++const uint16_t kFX_MSWinBalticUnicodes[128] = { + 0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000, + 0x2030, 0x0000, 0x2039, 0x0000, 0x00A8, 0x02C7, 0x00B8, 0x0000, 0x2018, + 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0000, +@@ -157,81 +164,170 @@ const uint16_t g_FX_MSWinBalticUnicodes[128] = { + }; + + struct FX_CHARSET_MAP { +- uint16_t charset; +- uint16_t codepage; ++ FX_Charset charset; ++ FX_CodePage codepage; + }; + +-const FX_CHARSET_MAP g_FXCharset2CodePageTable[] = { +- {FX_CHARSET_ANSI, FX_CODEPAGE_MSWin_WesternEuropean}, +- {FX_CHARSET_Default, FX_CODEPAGE_DefANSI}, +- {FX_CHARSET_Symbol, FX_CODEPAGE_Symbol}, +- {FX_CHARSET_MAC_Roman, FX_CODEPAGE_MAC_Roman}, +- {FX_CHARSET_MAC_ShiftJIS, FX_CODEPAGE_MAC_ShiftJIS}, +- {FX_CHARSET_MAC_Korean, FX_CODEPAGE_MAC_Korean}, +- {FX_CHARSET_MAC_ChineseSimplified, FX_CODEPAGE_MAC_ChineseSimplified}, +- {FX_CHARSET_MAC_ChineseTraditional, FX_CODEPAGE_MAC_ChineseTraditional}, +- {FX_CHARSET_MAC_Hebrew, FX_CODEPAGE_MAC_Hebrew}, +- {FX_CHARSET_MAC_Arabic, FX_CODEPAGE_MAC_Arabic}, +- {FX_CHARSET_MAC_Greek, FX_CODEPAGE_MAC_Greek}, +- {FX_CHARSET_MAC_Turkish, FX_CODEPAGE_MAC_Turkish}, +- {FX_CHARSET_MAC_Thai, FX_CODEPAGE_MAC_Thai}, +- {FX_CHARSET_MAC_EasternEuropean, FX_CODEPAGE_MAC_EasternEuropean}, +- {FX_CHARSET_MAC_Cyrillic, FX_CODEPAGE_MAC_Cyrillic}, +- {FX_CHARSET_ShiftJIS, FX_CODEPAGE_ShiftJIS}, +- {FX_CHARSET_Hangul, FX_CODEPAGE_Hangul}, +- {FX_CHARSET_Johab, FX_CODEPAGE_Johab}, +- {FX_CHARSET_ChineseSimplified, FX_CODEPAGE_ChineseSimplified}, +- {FX_CHARSET_ChineseTraditional, FX_CODEPAGE_ChineseTraditional}, +- {FX_CHARSET_MSWin_Greek, FX_CODEPAGE_MSWin_Greek}, +- {FX_CHARSET_MSWin_Turkish, FX_CODEPAGE_MSWin_Turkish}, +- {FX_CHARSET_MSWin_Vietnamese, FX_CODEPAGE_MSWin_Vietnamese}, +- {FX_CHARSET_MSWin_Hebrew, FX_CODEPAGE_MSWin_Hebrew}, +- {FX_CHARSET_MSWin_Arabic, FX_CODEPAGE_MSWin_Arabic}, +- {FX_CHARSET_MSWin_Baltic, FX_CODEPAGE_MSWin_Baltic}, +- {FX_CHARSET_MSWin_Cyrillic, FX_CODEPAGE_MSWin_Cyrillic}, +- {FX_CHARSET_Thai, FX_CODEPAGE_MSDOS_Thai}, +- {FX_CHARSET_MSWin_EasternEuropean, FX_CODEPAGE_MSWin_EasternEuropean}, +- {FX_CHARSET_US, FX_CODEPAGE_MSDOS_US}, +- {FX_CHARSET_OEM, FX_CODEPAGE_MSDOS_WesternEuropean}, ++const FX_CHARSET_MAP kFXCharset2CodePageTable[] = { ++ {FX_Charset::kANSI, FX_CodePage::kMSWin_WesternEuropean}, ++ {FX_Charset::kDefault, FX_CodePage::kDefANSI}, ++ {FX_Charset::kSymbol, FX_CodePage::kSymbol}, ++ {FX_Charset::kMAC_Roman, FX_CodePage::kMAC_Roman}, ++ {FX_Charset::kMAC_ShiftJIS, FX_CodePage::kMAC_ShiftJIS}, ++ {FX_Charset::kMAC_Korean, FX_CodePage::kMAC_Korean}, ++ {FX_Charset::kMAC_ChineseSimplified, FX_CodePage::kMAC_ChineseSimplified}, ++ {FX_Charset::kMAC_ChineseTraditional, FX_CodePage::kMAC_ChineseTraditional}, ++ {FX_Charset::kMAC_Hebrew, FX_CodePage::kMAC_Hebrew}, ++ {FX_Charset::kMAC_Arabic, FX_CodePage::kMAC_Arabic}, ++ {FX_Charset::kMAC_Greek, FX_CodePage::kMAC_Greek}, ++ {FX_Charset::kMAC_Turkish, FX_CodePage::kMAC_Turkish}, ++ {FX_Charset::kMAC_Thai, FX_CodePage::kMAC_Thai}, ++ {FX_Charset::kMAC_EasternEuropean, FX_CodePage::kMAC_EasternEuropean}, ++ {FX_Charset::kMAC_Cyrillic, FX_CodePage::kMAC_Cyrillic}, ++ {FX_Charset::kShiftJIS, FX_CodePage::kShiftJIS}, ++ {FX_Charset::kHangul, FX_CodePage::kHangul}, ++ {FX_Charset::kJohab, FX_CodePage::kJohab}, ++ {FX_Charset::kChineseSimplified, FX_CodePage::kChineseSimplified}, ++ {FX_Charset::kChineseTraditional, FX_CodePage::kChineseTraditional}, ++ {FX_Charset::kMSWin_Greek, FX_CodePage::kMSWin_Greek}, ++ {FX_Charset::kMSWin_Turkish, FX_CodePage::kMSWin_Turkish}, ++ {FX_Charset::kMSWin_Vietnamese, FX_CodePage::kMSWin_Vietnamese}, ++ {FX_Charset::kMSWin_Hebrew, FX_CodePage::kMSWin_Hebrew}, ++ {FX_Charset::kMSWin_Arabic, FX_CodePage::kMSWin_Arabic}, ++ {FX_Charset::kMSWin_Baltic, FX_CodePage::kMSWin_Baltic}, ++ {FX_Charset::kMSWin_Cyrillic, FX_CodePage::kMSWin_Cyrillic}, ++ {FX_Charset::kThai, FX_CodePage::kMSDOS_Thai}, ++ {FX_Charset::kMSWin_EasternEuropean, FX_CodePage::kMSWin_EasternEuropean}, ++ {FX_Charset::kUS, FX_CodePage::kMSDOS_US}, ++ {FX_Charset::kOEM, FX_CodePage::kMSDOS_WesternEuropean}, + }; + + } // namespace + +-const FX_CharsetUnicodes g_FX_CharsetUnicodes[8] = { +- {FX_CHARSET_Thai, g_FX_MSDOSThaiUnicodes}, +- {FX_CHARSET_MSWin_EasternEuropean, g_FX_MSWinEasternEuropeanUnicodes}, +- {FX_CHARSET_MSWin_Cyrillic, g_FX_MSWinCyrillicUnicodes}, +- {FX_CHARSET_MSWin_Greek, g_FX_MSWinGreekUnicodes}, +- {FX_CHARSET_MSWin_Turkish, g_FX_MSWinTurkishUnicodes}, +- {FX_CHARSET_MSWin_Hebrew, g_FX_MSWinHebrewUnicodes}, +- {FX_CHARSET_MSWin_Arabic, g_FX_MSWinArabicUnicodes}, +- {FX_CHARSET_MSWin_Baltic, g_FX_MSWinBalticUnicodes}, ++const FX_CharsetUnicodes kFX_CharsetUnicodes[8] = { ++ {FX_Charset::kThai, kFX_MSDOSThaiUnicodes}, ++ {FX_Charset::kMSWin_EasternEuropean, kFX_MSWinEasternEuropeanUnicodes}, ++ {FX_Charset::kMSWin_Cyrillic, kFX_MSWinCyrillicUnicodes}, ++ {FX_Charset::kMSWin_Greek, kFX_MSWinGreekUnicodes}, ++ {FX_Charset::kMSWin_Turkish, kFX_MSWinTurkishUnicodes}, ++ {FX_Charset::kMSWin_Hebrew, kFX_MSWinHebrewUnicodes}, ++ {FX_Charset::kMSWin_Arabic, kFX_MSWinArabicUnicodes}, ++ {FX_Charset::kMSWin_Baltic, kFX_MSWinBalticUnicodes}, + }; + +-uint16_t FX_GetCodePageFromCharset(uint8_t charset) { +- auto* result = +- std::lower_bound(std::begin(g_FXCharset2CodePageTable), +- std::end(g_FXCharset2CodePageTable), charset, +- [](const FX_CHARSET_MAP& iter, const uint16_t& charset) { +- return iter.charset < charset; +- }); +- if (result != std::end(g_FXCharset2CodePageTable) && ++FX_CodePage FX_GetACP() { ++#if BUILDFLAG(IS_WIN) ++ return static_cast(GetACP()); ++#else ++ return FX_CodePage::kDefANSI; ++#endif ++} ++ ++FX_CodePage FX_GetCodePageFromCharset(FX_Charset charset) { ++ auto* result = std::lower_bound( ++ std::begin(kFXCharset2CodePageTable), std::end(kFXCharset2CodePageTable), ++ charset, [](const FX_CHARSET_MAP& iter, const FX_Charset& charset) { ++ return iter.charset < charset; ++ }); ++ if (result != std::end(kFXCharset2CodePageTable) && + result->charset == charset) { + return result->codepage; + } +- return 0xFFFF; ++ return FX_CodePage::kFailure; + } + +-uint8_t FX_GetCharsetFromCodePage(uint16_t codepage) { +- for (const auto& it : g_FXCharset2CodePageTable) { ++FX_Charset FX_GetCharsetFromCodePage(FX_CodePage codepage) { ++ for (const auto& it : kFXCharset2CodePageTable) { + if (it.codepage == codepage) + return it.charset; + } +- return FX_CHARSET_ANSI; ++ return FX_Charset::kANSI; ++} ++ ++FX_Charset FX_GetCharsetFromInt(int value) { ++ switch (value) { ++ case static_cast(FX_Charset::kANSI): ++ case static_cast(FX_Charset::kDefault): ++ case static_cast(FX_Charset::kSymbol): ++ case static_cast(FX_Charset::kMAC_Roman): ++ case static_cast(FX_Charset::kMAC_ShiftJIS): ++ case static_cast(FX_Charset::kMAC_Korean): ++ case static_cast(FX_Charset::kMAC_ChineseSimplified): ++ case static_cast(FX_Charset::kMAC_ChineseTraditional): ++ case static_cast(FX_Charset::kMAC_Hebrew): ++ case static_cast(FX_Charset::kMAC_Arabic): ++ case static_cast(FX_Charset::kMAC_Greek): ++ case static_cast(FX_Charset::kMAC_Turkish): ++ case static_cast(FX_Charset::kMAC_Thai): ++ case static_cast(FX_Charset::kMAC_EasternEuropean): ++ case static_cast(FX_Charset::kMAC_Cyrillic): ++ case static_cast(FX_Charset::kShiftJIS): ++ case static_cast(FX_Charset::kHangul): ++ case static_cast(FX_Charset::kJohab): ++ case static_cast(FX_Charset::kChineseSimplified): ++ case static_cast(FX_Charset::kChineseTraditional): ++ case static_cast(FX_Charset::kMSWin_Greek): ++ case static_cast(FX_Charset::kMSWin_Turkish): ++ case static_cast(FX_Charset::kMSWin_Vietnamese): ++ case static_cast(FX_Charset::kMSWin_Hebrew): ++ case static_cast(FX_Charset::kMSWin_Arabic): ++ case static_cast(FX_Charset::kMSWin_Baltic): ++ case static_cast(FX_Charset::kMSWin_Cyrillic): ++ case static_cast(FX_Charset::kThai): ++ case static_cast(FX_Charset::kMSWin_EasternEuropean): ++ case static_cast(FX_Charset::kUS): ++ case static_cast(FX_Charset::kOEM): ++ return static_cast(value); ++ default: ++ return FX_Charset::kANSI; ++ } + } + +-bool FX_CharSetIsCJK(uint8_t uCharset) { +- return (uCharset == FX_CHARSET_ChineseSimplified) || +- (uCharset == FX_CHARSET_ChineseTraditional) || +- (uCharset == FX_CHARSET_Hangul) || (uCharset == FX_CHARSET_ShiftJIS); ++bool FX_CharSetIsCJK(FX_Charset uCharset) { ++ return (uCharset == FX_Charset::kChineseSimplified) || ++ (uCharset == FX_Charset::kChineseTraditional) || ++ (uCharset == FX_Charset::kHangul) || ++ (uCharset == FX_Charset::kShiftJIS); ++} ++ ++size_t FX_WideCharToMultiByte(FX_CodePage codepage, ++ WideStringView wstr, ++ pdfium::span buf) { ++#if BUILDFLAG(IS_WIN) ++ int input_len = pdfium::base::checked_cast(wstr.GetLength()); ++ int output_len = pdfium::base::checked_cast(buf.size()); ++ return WideCharToMultiByte(static_cast(codepage), 0, ++ wstr.unterminated_c_str(), input_len, buf.data(), ++ output_len, nullptr, nullptr); ++#else ++ size_t len = 0; ++ for (size_t i = 0; i < wstr.GetLength(); i++) { ++ if (wstr[i] < 0x100) { ++ if (len < buf.size()) ++ buf[len] = static_cast(wstr[i]); ++ len++; ++ } ++ } ++ return len; ++#endif ++} ++ ++size_t FX_MultiByteToWideChar(FX_CodePage codepage, ++ ByteStringView bstr, ++ pdfium::span buf) { ++#if BUILDFLAG(IS_WIN) ++ const int input_len = pdfium::base::checked_cast(bstr.GetLength()); ++ const int output_len = pdfium::base::checked_cast(buf.size()); ++ return MultiByteToWideChar(static_cast(codepage), 0, ++ bstr.unterminated_c_str(), input_len, buf.data(), ++ output_len); ++#else ++ size_t wlen = 0; ++ for (size_t i = 0; i < bstr.GetLength(); i++) { ++ if (wlen < buf.size()) ++ buf[wlen] = reinterpret_cast(bstr[i]); ++ wlen++; ++ } ++ return wlen; ++#endif + } +diff --git a/core/fxcrt/fx_codepage.h b/core/fxcrt/fx_codepage.h +index 4a6c6d86b..94e764eac 100644 +--- a/core/fxcrt/fx_codepage.h ++++ b/core/fxcrt/fx_codepage.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,97 +9,115 @@ + + #include + +-#define FX_CODEPAGE_DefANSI 0 +-#define FX_CODEPAGE_Symbol 42 +-#define FX_CODEPAGE_MSDOS_US 437 +-#define FX_CODEPAGE_Arabic_ASMO708 708 +-#define FX_CODEPAGE_MSDOS_Greek1 737 +-#define FX_CODEPAGE_MSDOS_Baltic 775 +-#define FX_CODEPAGE_MSDOS_WesternEuropean 850 +-#define FX_CODEPAGE_MSDOS_EasternEuropean 852 +-#define FX_CODEPAGE_MSDOS_Cyrillic 855 +-#define FX_CODEPAGE_MSDOS_Turkish 857 +-#define FX_CODEPAGE_MSDOS_Portuguese 860 +-#define FX_CODEPAGE_MSDOS_Icelandic 861 +-#define FX_CODEPAGE_MSDOS_Hebrew 862 +-#define FX_CODEPAGE_MSDOS_FrenchCanadian 863 +-#define FX_CODEPAGE_MSDOS_Arabic 864 +-#define FX_CODEPAGE_MSDOS_Norwegian 865 +-#define FX_CODEPAGE_MSDOS_Russian 866 +-#define FX_CODEPAGE_MSDOS_Greek2 869 +-#define FX_CODEPAGE_MSDOS_Thai 874 +-#define FX_CODEPAGE_ShiftJIS 932 +-#define FX_CODEPAGE_ChineseSimplified 936 +-#define FX_CODEPAGE_Hangul 949 +-#define FX_CODEPAGE_ChineseTraditional 950 +-#define FX_CODEPAGE_UTF16LE 1200 +-#define FX_CODEPAGE_UTF16BE 1201 +-#define FX_CODEPAGE_MSWin_EasternEuropean 1250 +-#define FX_CODEPAGE_MSWin_Cyrillic 1251 +-#define FX_CODEPAGE_MSWin_WesternEuropean 1252 +-#define FX_CODEPAGE_MSWin_Greek 1253 +-#define FX_CODEPAGE_MSWin_Turkish 1254 +-#define FX_CODEPAGE_MSWin_Hebrew 1255 +-#define FX_CODEPAGE_MSWin_Arabic 1256 +-#define FX_CODEPAGE_MSWin_Baltic 1257 +-#define FX_CODEPAGE_MSWin_Vietnamese 1258 +-#define FX_CODEPAGE_Johab 1361 +-#define FX_CODEPAGE_MAC_Roman 10000 +-#define FX_CODEPAGE_MAC_ShiftJIS 10001 +-#define FX_CODEPAGE_MAC_ChineseTraditional 10002 +-#define FX_CODEPAGE_MAC_Korean 10003 +-#define FX_CODEPAGE_MAC_Arabic 10004 +-#define FX_CODEPAGE_MAC_Hebrew 10005 +-#define FX_CODEPAGE_MAC_Greek 10006 +-#define FX_CODEPAGE_MAC_Cyrillic 10007 +-#define FX_CODEPAGE_MAC_ChineseSimplified 10008 +-#define FX_CODEPAGE_MAC_Thai 10021 +-#define FX_CODEPAGE_MAC_EasternEuropean 10029 +-#define FX_CODEPAGE_MAC_Turkish 10081 +-#define FX_CODEPAGE_UTF8 65001 ++// Prove consistency with incomplete forward definitions. ++#include "core/fxcrt/fx_codepage_forward.h" ++#include "core/fxcrt/fx_string.h" ++#include "third_party/base/span.h" + +-#define FX_CHARSET_ANSI 0 +-#define FX_CHARSET_Default 1 +-#define FX_CHARSET_Symbol 2 +-#define FX_CHARSET_MAC_Roman 77 +-#define FX_CHARSET_MAC_ShiftJIS 78 +-#define FX_CHARSET_MAC_Korean 79 +-#define FX_CHARSET_MAC_ChineseSimplified 80 +-#define FX_CHARSET_MAC_ChineseTraditional 81 +-#define FX_CHARSET_MAC_Hebrew 83 +-#define FX_CHARSET_MAC_Arabic 84 +-#define FX_CHARSET_MAC_Greek 85 +-#define FX_CHARSET_MAC_Turkish 86 +-#define FX_CHARSET_MAC_Thai 87 +-#define FX_CHARSET_MAC_EasternEuropean 88 +-#define FX_CHARSET_MAC_Cyrillic 89 +-#define FX_CHARSET_ShiftJIS 128 +-#define FX_CHARSET_Hangul 129 +-#define FX_CHARSET_Johab 130 +-#define FX_CHARSET_ChineseSimplified 134 +-#define FX_CHARSET_ChineseTraditional 136 +-#define FX_CHARSET_MSWin_Greek 161 +-#define FX_CHARSET_MSWin_Turkish 162 +-#define FX_CHARSET_MSWin_Vietnamese 163 +-#define FX_CHARSET_MSWin_Hebrew 177 +-#define FX_CHARSET_MSWin_Arabic 178 +-#define FX_CHARSET_MSWin_Baltic 186 +-#define FX_CHARSET_MSWin_Cyrillic 204 +-#define FX_CHARSET_Thai 222 +-#define FX_CHARSET_MSWin_EasternEuropean 238 +-#define FX_CHARSET_US 254 +-#define FX_CHARSET_OEM 255 ++enum class FX_CodePage : uint16_t { ++ kDefANSI = 0, ++ kSymbol = 42, ++ kMSDOS_US = 437, ++ kArabic_ASMO708 = 708, ++ kMSDOS_Greek1 = 737, ++ kMSDOS_Baltic = 775, ++ kMSDOS_WesternEuropean = 850, ++ kMSDOS_EasternEuropean = 852, ++ kMSDOS_Cyrillic = 855, ++ kMSDOS_Turkish = 857, ++ kMSDOS_Portuguese = 860, ++ kMSDOS_Icelandic = 861, ++ kMSDOS_Hebrew = 862, ++ kMSDOS_FrenchCanadian = 863, ++ kMSDOS_Arabic = 864, ++ kMSDOS_Norwegian = 865, ++ kMSDOS_Russian = 866, ++ kMSDOS_Greek2 = 869, ++ kMSDOS_Thai = 874, ++ kShiftJIS = 932, ++ kChineseSimplified = 936, ++ kHangul = 949, ++ kChineseTraditional = 950, ++ kUTF16LE = 1200, ++ kUTF16BE = 1201, ++ kMSWin_EasternEuropean = 1250, ++ kMSWin_Cyrillic = 1251, ++ kMSWin_WesternEuropean = 1252, ++ kMSWin_Greek = 1253, ++ kMSWin_Turkish = 1254, ++ kMSWin_Hebrew = 1255, ++ kMSWin_Arabic = 1256, ++ kMSWin_Baltic = 1257, ++ kMSWin_Vietnamese = 1258, ++ kJohab = 1361, ++ kMAC_Roman = 10000, ++ kMAC_ShiftJIS = 10001, ++ kMAC_ChineseTraditional = 10002, ++ kMAC_Korean = 10003, ++ kMAC_Arabic = 10004, ++ kMAC_Hebrew = 10005, ++ kMAC_Greek = 10006, ++ kMAC_Cyrillic = 10007, ++ kMAC_ChineseSimplified = 10008, ++ kMAC_Thai = 10021, ++ kMAC_EasternEuropean = 10029, ++ kMAC_Turkish = 10081, ++ kUTF8 = 65001, ++ kFailure = 65535, ++}; ++ ++enum class FX_Charset : uint8_t { ++ kANSI = 0, ++ kDefault = 1, ++ kSymbol = 2, ++ kMAC_Roman = 77, ++ kMAC_ShiftJIS = 78, ++ kMAC_Korean = 79, ++ kMAC_ChineseSimplified = 80, ++ kMAC_ChineseTraditional = 81, ++ kMAC_Hebrew = 83, ++ kMAC_Arabic = 84, ++ kMAC_Greek = 85, ++ kMAC_Turkish = 86, ++ kMAC_Thai = 87, ++ kMAC_EasternEuropean = 88, ++ kMAC_Cyrillic = 89, ++ kShiftJIS = 128, ++ kHangul = 129, ++ kJohab = 130, ++ kChineseSimplified = 134, ++ kChineseTraditional = 136, ++ kMSWin_Greek = 161, ++ kMSWin_Turkish = 162, ++ kMSWin_Vietnamese = 163, ++ kMSWin_Hebrew = 177, ++ kMSWin_Arabic = 178, ++ kMSWin_Baltic = 186, ++ kMSWin_Cyrillic = 204, ++ kThai = 222, ++ kMSWin_EasternEuropean = 238, ++ kUS = 254, ++ kOEM = 255, ++}; + + // Hi-bytes to unicode codepoint mapping for various code pages. + struct FX_CharsetUnicodes { +- uint8_t m_Charset; +- const uint16_t* m_pUnicodes; ++ FX_Charset m_Charset; ++ const uint16_t* m_pUnicodes; // Raw, POD struct. + }; + +-extern const FX_CharsetUnicodes g_FX_CharsetUnicodes[8]; ++extern const FX_CharsetUnicodes kFX_CharsetUnicodes[8]; + +-uint16_t FX_GetCodePageFromCharset(uint8_t charset); +-uint8_t FX_GetCharsetFromCodePage(uint16_t codepage); +-bool FX_CharSetIsCJK(uint8_t uCharset); ++FX_CodePage FX_GetACP(); ++FX_CodePage FX_GetCodePageFromCharset(FX_Charset charset); ++FX_Charset FX_GetCharsetFromCodePage(FX_CodePage codepage); ++FX_Charset FX_GetCharsetFromInt(int value); ++bool FX_CharSetIsCJK(FX_Charset uCharset); ++size_t FX_WideCharToMultiByte(FX_CodePage codepage, ++ WideStringView wstr, ++ pdfium::span buf); ++size_t FX_MultiByteToWideChar(FX_CodePage codepage, ++ ByteStringView bstr, ++ pdfium::span buf); + + #endif // CORE_FXCRT_FX_CODEPAGE_H_ +diff --git a/core/fxcrt/fx_codepage_forward.h b/core/fxcrt/fx_codepage_forward.h +new file mode 100644 +index 000000000..c67eaf2ae +--- /dev/null ++++ b/core/fxcrt/fx_codepage_forward.h +@@ -0,0 +1,17 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCRT_FX_CODEPAGE_FORWARD_H_ ++#define CORE_FXCRT_FX_CODEPAGE_FORWARD_H_ ++ ++#include ++ ++// Incomplete definitions of large enumerated type for headers that don't ++// need the specifics. ++enum class FX_Charset : uint8_t; ++enum class FX_CodePage : uint16_t; ++ ++#endif // CORE_FXCRT_FX_CODEPAGE_FORWARD_H_ +diff --git a/core/fxcrt/fx_coordinates.cpp b/core/fxcrt/fx_coordinates.cpp +index f76df5f35..2cf87d6cf 100644 +--- a/core/fxcrt/fx_coordinates.cpp ++++ b/core/fxcrt/fx_coordinates.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,12 +6,20 @@ + + #include "core/fxcrt/fx_coordinates.h" + ++#include ++ ++#include ++#include + #include + + #include "build/build_config.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/numerics/safe_conversions.h" ++#include "core/fxcrt/fx_system.h" ++ ++#ifndef NDEBUG ++#include ++#endif + + namespace { + +@@ -33,7 +41,7 @@ void MatchFloatRange(float f1, float f2, int* i1, int* i2) { + } + } + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + static_assert(sizeof(FX_RECT) == sizeof(RECT), "FX_RECT vs. RECT mismatch"); + static_assert(offsetof(FX_RECT, left) == offsetof(RECT, left), + "FX_RECT vs. RECT mismatch"); +@@ -55,6 +63,29 @@ static_assert(sizeof(FX_RECT::bottom) == sizeof(RECT::bottom), + + } // namespace + ++template <> ++float CFX_VTemplate::Length() const { ++ return FXSYS_sqrt2(x, y); ++} ++ ++template <> ++void CFX_VTemplate::Normalize() { ++ float fLen = Length(); ++ if (fLen < 0.0001f) ++ return; ++ ++ x /= fLen; ++ y /= fLen; ++} ++ ++bool FX_RECT::Valid() const { ++ FX_SAFE_INT32 w = right; ++ FX_SAFE_INT32 h = bottom; ++ w -= left; ++ h -= top; ++ return w.IsValid() && h.IsValid(); ++} ++ + void FX_RECT::Normalize() { + if (left > right) + std::swap(left, right); +@@ -75,24 +106,50 @@ void FX_RECT::Intersect(const FX_RECT& src) { + } + } + ++FX_RECT FX_RECT::SwappedClipBox(int width, ++ int height, ++ bool bFlipX, ++ bool bFlipY) const { ++ FX_RECT rect; ++ if (bFlipY) { ++ rect.left = height - top; ++ rect.right = height - bottom; ++ } else { ++ rect.left = top; ++ rect.right = bottom; ++ } ++ if (bFlipX) { ++ rect.top = width - left; ++ rect.bottom = width - right; ++ } else { ++ rect.top = left; ++ rect.bottom = right; ++ } ++ rect.Normalize(); ++ return rect; ++} ++ + // Y-axis runs the opposite way in FX_RECT. + CFX_FloatRect::CFX_FloatRect(const FX_RECT& rect) + : left(rect.left), bottom(rect.top), right(rect.right), top(rect.bottom) {} + ++CFX_FloatRect::CFX_FloatRect(const CFX_PointF& point) ++ : left(point.x), bottom(point.y), right(point.x), top(point.y) {} ++ + // static +-CFX_FloatRect CFX_FloatRect::GetBBox(const CFX_PointF* pPoints, int nPoints) { +- if (nPoints == 0) ++CFX_FloatRect CFX_FloatRect::GetBBox(pdfium::span pPoints) { ++ if (pPoints.empty()) + return CFX_FloatRect(); + +- float min_x = pPoints->x; +- float max_x = pPoints->x; +- float min_y = pPoints->y; +- float max_y = pPoints->y; +- for (int i = 1; i < nPoints; i++) { +- min_x = std::min(min_x, pPoints[i].x); +- max_x = std::max(max_x, pPoints[i].x); +- min_y = std::min(min_y, pPoints[i].y); +- max_y = std::max(max_y, pPoints[i].y); ++ float min_x = pPoints.front().x; ++ float max_x = pPoints.front().x; ++ float min_y = pPoints.front().y; ++ float max_y = pPoints.front().y; ++ for (const auto& point : pPoints.subspan(1)) { ++ min_x = std::min(min_x, point.x); ++ max_x = std::max(max_x, point.x); ++ min_y = std::min(min_y, point.y); ++ max_y = std::max(max_y, point.y); + } + return CFX_FloatRect(min_x, min_y, max_x, max_y); + } +@@ -269,6 +326,45 @@ FX_RECT CFX_FloatRect::ToRoundedFxRect() const { + FXSYS_roundf(bottom)); + } + ++void CFX_RectF::Union(float x, float y) { ++ float r = right(); ++ float b = bottom(); ++ ++ left = std::min(left, x); ++ top = std::min(top, y); ++ r = std::max(r, x); ++ b = std::max(b, y); ++ ++ width = r - left; ++ height = b - top; ++} ++ ++void CFX_RectF::Union(const CFX_RectF& rt) { ++ float r = right(); ++ float b = bottom(); ++ ++ left = std::min(left, rt.left); ++ top = std::min(top, rt.top); ++ r = std::max(r, rt.right()); ++ b = std::max(b, rt.bottom()); ++ ++ width = r - left; ++ height = b - top; ++} ++ ++void CFX_RectF::Intersect(const CFX_RectF& rt) { ++ float r = right(); ++ float b = bottom(); ++ ++ left = std::max(left, rt.left); ++ top = std::max(top, rt.top); ++ r = std::min(r, rt.right()); ++ b = std::min(b, rt.bottom()); ++ ++ width = r - left; ++ height = b - top; ++} ++ + FX_RECT CFX_RectF::GetOuterRect() const { + return FX_RECT(static_cast(floor(left)), + static_cast(floor(top)), +@@ -357,7 +453,7 @@ float CFX_Matrix::GetXUnit() const { + return (a > 0 ? a : -a); + if (a == 0) + return (b > 0 ? b : -b); +- return sqrt(a * a + b * b); ++ return FXSYS_sqrt2(a, b); + } + + float CFX_Matrix::GetYUnit() const { +@@ -365,7 +461,7 @@ float CFX_Matrix::GetYUnit() const { + return (d > 0 ? d : -d); + if (d == 0) + return (c > 0 ? c : -c); +- return sqrt(c * c + d * d); ++ return FXSYS_sqrt2(c, d); + } + + CFX_FloatRect CFX_Matrix::GetUnitRect() const { +@@ -375,7 +471,7 @@ CFX_FloatRect CFX_Matrix::GetUnitRect() const { + float CFX_Matrix::TransformXDistance(float dx) const { + float fx = a * dx; + float fy = b * dx; +- return sqrt(fx * fx + fy * fy); ++ return FXSYS_sqrt2(fx, fy); + } + + float CFX_Matrix::TransformDistance(float distance) const { +@@ -405,7 +501,7 @@ CFX_FloatRect CFX_Matrix::TransformRect(const CFX_FloatRect& rect) const { + float new_left = points[0].x; + float new_top = points[0].y; + float new_bottom = points[0].y; +- for (size_t i = 1; i < FX_ArraySize(points); i++) { ++ for (size_t i = 1; i < std::size(points); i++) { + new_right = std::max(new_right, points[i].x); + new_left = std::min(new_left, points[i].x); + new_top = std::max(new_top, points[i].y); +diff --git a/core/fxcrt/fx_coordinates.h b/core/fxcrt/fx_coordinates.h +index e8d1a8a61..abc87ffff 100644 +--- a/core/fxcrt/fx_coordinates.h ++++ b/core/fxcrt/fx_coordinates.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,15 +7,14 @@ + #ifndef CORE_FXCRT_FX_COORDINATES_H_ + #define CORE_FXCRT_FX_COORDINATES_H_ + +-#include +- +-#include "core/fxcrt/fx_system.h" +-#include "third_party/base/numerics/safe_math.h" ++#include + + #ifndef NDEBUG +-#include ++#include + #endif + ++#include "third_party/base/span.h" ++ + template + class CFX_PTemplate { + public: +@@ -56,6 +55,7 @@ class CFX_PTemplate { + BaseType x; + BaseType y; + }; ++using CFX_Point16 = CFX_PTemplate; + using CFX_Point = CFX_PTemplate; + using CFX_PointF = CFX_PTemplate; + +@@ -148,29 +148,8 @@ class CFX_VTemplate final : public CFX_PTemplate { + const CFX_PTemplate& point2) + : CFX_PTemplate(point2.x - point1.x, point2.y - point1.y) {} + +- float Length() const { return sqrt(x * x + y * y); } +- void Normalize() { +- float fLen = Length(); +- if (fLen < 0.0001f) +- return; +- +- x /= fLen; +- y /= fLen; +- } +- void Translate(BaseType dx, BaseType dy) { +- x += dx; +- y += dy; +- } +- void Scale(BaseType sx, BaseType sy) { +- x *= sx; +- y *= sy; +- } +- void Rotate(float fRadian) { +- float cosValue = cos(fRadian); +- float sinValue = sin(fRadian); +- x = x * cosValue - y * sinValue; +- y = x * sinValue + y * cosValue; +- } ++ float Length() const; ++ void Normalize(); + }; + using CFX_Vector = CFX_VTemplate; + using CFX_VectorF = CFX_VTemplate; +@@ -188,17 +167,12 @@ struct FX_RECT { + int Height() const { return bottom - top; } + bool IsEmpty() const { return right <= left || bottom <= top; } + +- bool Valid() const { +- pdfium::base::CheckedNumeric w = right; +- pdfium::base::CheckedNumeric h = bottom; +- w -= left; +- h -= top; +- return w.IsValid() && h.IsValid(); +- } ++ bool Valid() const; + + void Normalize(); + void Intersect(const FX_RECT& src); + void Intersect(int l, int t, int r, int b) { Intersect(FX_RECT(l, t, r, b)); } ++ FX_RECT SwappedClipBox(int width, int height, bool bFlipX, bool bFlipY) const; + + void Offset(int dx, int dy) { + left += dx; +@@ -229,12 +203,10 @@ class CFX_FloatRect { + constexpr CFX_FloatRect(float l, float b, float r, float t) + : left(l), bottom(b), right(r), top(t) {} + +- explicit CFX_FloatRect(const float* pArray) +- : CFX_FloatRect(pArray[0], pArray[1], pArray[2], pArray[3]) {} +- + explicit CFX_FloatRect(const FX_RECT& rect); ++ explicit CFX_FloatRect(const CFX_PointF& point); + +- static CFX_FloatRect GetBBox(const CFX_PointF* pPoints, int nPoints); ++ static CFX_FloatRect GetBBox(pdfium::span pPoints); + + void Normalize(); + +@@ -261,12 +233,6 @@ class CFX_FloatRect { + + CFX_FloatRect GetCenterSquare() const; + +- void InitRect(const CFX_PointF& point) { +- left = point.x; +- right = point.x; +- bottom = point.y; +- top = point.y; +- } + void UpdateRect(const CFX_PointF& point); + + float Width() const { return right - left; } +@@ -308,6 +274,11 @@ class CFX_FloatRect { + // Rounds LBRT values. + FX_RECT ToRoundedFxRect() const; + ++ bool operator==(const CFX_FloatRect& other) const { ++ return left == other.left && right == other.right && top == other.top && ++ bottom == other.bottom; ++ } ++ + float left = 0.0f; + float bottom = 0.0f; + float right = 0.0f; +@@ -345,6 +316,8 @@ class CFX_RectF { + // NOLINTNEXTLINE(runtime/explicit) + CFX_RectF(const CFX_RectF& other) = default; + ++ CFX_RectF& operator=(const CFX_RectF& other) = default; ++ + CFX_RectF& operator+=(const PointType& p) { + left += p.x; + top += p.y; +@@ -436,43 +409,10 @@ class CFX_RectF { + PointType Center() const { + return PointType(left + width / 2, top + height / 2); + } +- void Union(float x, float y) { +- float r = right(); +- float b = bottom(); +- +- left = std::min(left, x); +- top = std::min(top, y); +- r = std::max(r, x); +- b = std::max(b, y); +- +- width = r - left; +- height = b - top; +- } ++ void Union(float x, float y); + void Union(const PointType& p) { Union(p.x, p.y); } +- void Union(const CFX_RectF& rt) { +- float r = right(); +- float b = bottom(); +- +- left = std::min(left, rt.left); +- top = std::min(top, rt.top); +- r = std::max(r, rt.right()); +- b = std::max(b, rt.bottom()); +- +- width = r - left; +- height = b - top; +- } +- void Intersect(const CFX_RectF& rt) { +- float r = right(); +- float b = bottom(); +- +- left = std::max(left, rt.left); +- top = std::max(top, rt.top); +- r = std::min(r, rt.right()); +- b = std::min(b, rt.bottom()); +- +- width = r - left; +- height = b - top; +- } ++ void Union(const CFX_RectF& rt); ++ void Intersect(const CFX_RectF& rt); + bool IntersectWith(const CFX_RectF& rt) const { + CFX_RectF rect = rt; + rect.Intersect(*this); +diff --git a/core/fxcrt/fx_coordinates_unittest.cpp b/core/fxcrt/fx_coordinates_unittest.cpp +index 7539ab7ff..29c802816 100644 +--- a/core/fxcrt/fx_coordinates_unittest.cpp ++++ b/core/fxcrt/fx_coordinates_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,7 @@ + #include + #include + ++#include "core/fxcrt/fx_system.h" + #include "testing/gtest/include/gtest/gtest.h" + + namespace { +@@ -29,7 +30,7 @@ TEST(CFX_FloatRect, FromFXRect) { + } + + TEST(CFX_FloatRect, GetBBox) { +- CFX_FloatRect rect = CFX_FloatRect::GetBBox(nullptr, 0); ++ CFX_FloatRect rect = CFX_FloatRect::GetBBox({nullptr, 0}); + EXPECT_FLOAT_EQ(0.0f, rect.left); + EXPECT_FLOAT_EQ(0.0f, rect.bottom); + EXPECT_FLOAT_EQ(0.0f, rect.right); +@@ -37,12 +38,12 @@ TEST(CFX_FloatRect, GetBBox) { + + std::vector data; + data.emplace_back(0.0f, 0.0f); +- rect = CFX_FloatRect::GetBBox(data.data(), 0); ++ rect = CFX_FloatRect::GetBBox(pdfium::make_span(data).first(0)); + EXPECT_FLOAT_EQ(0.0f, rect.left); + EXPECT_FLOAT_EQ(0.0f, rect.bottom); + EXPECT_FLOAT_EQ(0.0f, rect.right); + EXPECT_FLOAT_EQ(0.0f, rect.top); +- rect = CFX_FloatRect::GetBBox(data.data(), data.size()); ++ rect = CFX_FloatRect::GetBBox(data); + EXPECT_FLOAT_EQ(0.0f, rect.left); + EXPECT_FLOAT_EQ(0.0f, rect.bottom); + EXPECT_FLOAT_EQ(0.0f, rect.right); +@@ -50,34 +51,34 @@ TEST(CFX_FloatRect, GetBBox) { + + data.emplace_back(2.5f, 6.2f); + data.emplace_back(1.5f, 6.2f); +- rect = CFX_FloatRect::GetBBox(data.data(), 2); ++ rect = CFX_FloatRect::GetBBox(pdfium::make_span(data).first(2)); + EXPECT_FLOAT_EQ(0.0f, rect.left); + EXPECT_FLOAT_EQ(0.0f, rect.bottom); + EXPECT_FLOAT_EQ(2.5f, rect.right); + EXPECT_FLOAT_EQ(6.2f, rect.top); + +- rect = CFX_FloatRect::GetBBox(data.data(), data.size()); ++ rect = CFX_FloatRect::GetBBox(data); + EXPECT_FLOAT_EQ(0.0f, rect.left); + EXPECT_FLOAT_EQ(0.0f, rect.bottom); + EXPECT_FLOAT_EQ(2.5f, rect.right); + EXPECT_FLOAT_EQ(6.2f, rect.top); + + data.emplace_back(2.5f, 6.3f); +- rect = CFX_FloatRect::GetBBox(data.data(), data.size()); ++ rect = CFX_FloatRect::GetBBox(data); + EXPECT_FLOAT_EQ(0.0f, rect.left); + EXPECT_FLOAT_EQ(0.0f, rect.bottom); + EXPECT_FLOAT_EQ(2.5f, rect.right); + EXPECT_FLOAT_EQ(6.3f, rect.top); + + data.emplace_back(-3.0f, 6.3f); +- rect = CFX_FloatRect::GetBBox(data.data(), data.size()); ++ rect = CFX_FloatRect::GetBBox(data); + EXPECT_FLOAT_EQ(-3.0f, rect.left); + EXPECT_FLOAT_EQ(0.0f, rect.bottom); + EXPECT_FLOAT_EQ(2.5f, rect.right); + EXPECT_FLOAT_EQ(6.3f, rect.top); + + data.emplace_back(4.0f, -8.0f); +- rect = CFX_FloatRect::GetBBox(data.data(), data.size()); ++ rect = CFX_FloatRect::GetBBox(data); + EXPECT_FLOAT_EQ(-3.0f, rect.left); + EXPECT_FLOAT_EQ(-8.0f, rect.bottom); + EXPECT_FLOAT_EQ(4.0f, rect.right); +@@ -457,11 +458,11 @@ TEST(CFX_Matrix, GetInverseCR714187) { + #define EXPECT_NEAR_FIVE_PLACES(a, b) EXPECT_NEAR((a), (b), 1e-5) + + TEST(CFX_Matrix, ComposeTransformations) { +- // sin(FX_PI/2) and cos(FX_PI/2) have a tiny error and are not exactly 1.0f +- // and 0.0f. The rotation matrix is thus not perfect. ++ // sin(FXSYS_PI/2) and cos(FXSYS_PI/2) have a tiny error and are not ++ // exactly 1.0f and 0.0f. The rotation matrix is thus not perfect. + + CFX_Matrix rotate_90; +- rotate_90.Rotate(FX_PI / 2); ++ rotate_90.Rotate(FXSYS_PI / 2); + EXPECT_NEAR_FIVE_PLACES(0.0f, rotate_90.a); + EXPECT_NEAR_FIVE_PLACES(1.0f, rotate_90.b); + EXPECT_NEAR_FIVE_PLACES(-1.0f, rotate_90.c); +@@ -585,7 +586,7 @@ TEST(CFX_Matrix, ComposeTransformations) { + + TEST(CFX_Matrix, TransformRectForRectF) { + CFX_Matrix rotate_90; +- rotate_90.Rotate(FX_PI / 2); ++ rotate_90.Rotate(FXSYS_PI / 2); + + CFX_Matrix scale_5_13; + scale_5_13.Scale(5, 13); +@@ -606,7 +607,7 @@ TEST(CFX_Matrix, TransformRectForRectF) { + + TEST(CFX_Matrix, TransformRectForFloatRect) { + CFX_Matrix rotate_90; +- rotate_90.Rotate(FX_PI / 2); ++ rotate_90.Rotate(FXSYS_PI / 2); + + CFX_Matrix scale_5_13; + scale_5_13.Scale(5, 13); +diff --git a/core/fxcrt/fx_extension.cpp b/core/fxcrt/fx_extension.cpp +index 8d76151c7..c6822594d 100644 +--- a/core/fxcrt/fx_extension.cpp ++++ b/core/fxcrt/fx_extension.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,10 @@ + #include "core/fxcrt/fx_extension.h" + + #include +-#include + #include + +-#include "third_party/base/compiler_specific.h" ++#include "core/fxcrt/fx_system.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -27,39 +27,36 @@ struct tm* (*g_localtime_func)(const time_t*) = DefaultLocaltimeFunction; + + } // namespace + +-float FXSYS_wcstof(const wchar_t* pwsStr, int32_t iLength, int32_t* pUsedLen) { +- ASSERT(pwsStr); +- +- if (iLength < 0) +- iLength = static_cast(wcslen(pwsStr)); +- if (iLength == 0) ++float FXSYS_wcstof(const wchar_t* pwsStr, size_t nLength, size_t* pUsedLen) { ++ DCHECK(pwsStr); ++ if (nLength == 0) + return 0.0f; + +- int32_t iUsedLen = 0; ++ size_t nUsedLen = 0; + bool bNegtive = false; +- switch (pwsStr[iUsedLen]) { ++ switch (pwsStr[nUsedLen]) { + case '-': + bNegtive = true; +- FALLTHROUGH; ++ [[fallthrough]]; + case '+': +- iUsedLen++; ++ nUsedLen++; + break; + } + + float fValue = 0.0f; +- while (iUsedLen < iLength) { +- wchar_t wch = pwsStr[iUsedLen]; ++ while (nUsedLen < nLength) { ++ wchar_t wch = pwsStr[nUsedLen]; + if (!FXSYS_IsDecimalDigit(wch)) + break; + + fValue = fValue * 10.0f + (wch - L'0'); +- iUsedLen++; ++ nUsedLen++; + } + +- if (iUsedLen < iLength && pwsStr[iUsedLen] == L'.') { ++ if (nUsedLen < nLength && pwsStr[nUsedLen] == L'.') { + float fPrecise = 0.1f; +- while (++iUsedLen < iLength) { +- wchar_t wch = pwsStr[iUsedLen]; ++ while (++nUsedLen < nLength) { ++ wchar_t wch = pwsStr[nUsedLen]; + if (!FXSYS_IsDecimalDigit(wch)) + break; + +@@ -68,20 +65,20 @@ float FXSYS_wcstof(const wchar_t* pwsStr, int32_t iLength, int32_t* pUsedLen) { + } + } + +- if (iUsedLen < iLength && +- (pwsStr[iUsedLen] == 'e' || pwsStr[iUsedLen] == 'E')) { +- ++iUsedLen; ++ if (nUsedLen < nLength && ++ (pwsStr[nUsedLen] == 'e' || pwsStr[nUsedLen] == 'E')) { ++ ++nUsedLen; + + bool negative_exponent = false; +- if (iUsedLen < iLength && +- (pwsStr[iUsedLen] == '-' || pwsStr[iUsedLen] == '+')) { +- negative_exponent = pwsStr[iUsedLen] == '-'; +- ++iUsedLen; ++ if (nUsedLen < nLength && ++ (pwsStr[nUsedLen] == '-' || pwsStr[nUsedLen] == '+')) { ++ negative_exponent = pwsStr[nUsedLen] == '-'; ++ ++nUsedLen; + } + + int32_t exp_value = 0; +- while (iUsedLen < iLength) { +- wchar_t wch = pwsStr[iUsedLen]; ++ while (nUsedLen < nLength) { ++ wchar_t wch = pwsStr[nUsedLen]; + if (!FXSYS_IsDecimalDigit(wch)) + break; + +@@ -96,7 +93,7 @@ float FXSYS_wcstof(const wchar_t* pwsStr, int32_t iLength, int32_t* pUsedLen) { + return 0.0f; + } + +- ++iUsedLen; ++ ++nUsedLen; + } + + for (size_t i = exp_value; i > 0; --i) { +@@ -110,15 +107,15 @@ float FXSYS_wcstof(const wchar_t* pwsStr, int32_t iLength, int32_t* pUsedLen) { + } + + if (pUsedLen) +- *pUsedLen = iUsedLen; ++ *pUsedLen = nUsedLen; + + return bNegtive ? -fValue : fValue; + } + + wchar_t* FXSYS_wcsncpy(wchar_t* dstStr, const wchar_t* srcStr, size_t count) { +- ASSERT(dstStr); +- ASSERT(srcStr); +- ASSERT(count > 0); ++ DCHECK(dstStr); ++ DCHECK(srcStr); ++ DCHECK(count > 0); + + for (size_t i = 0; i < count; ++i) + if ((dstStr[i] = srcStr[i]) == L'\0') +@@ -127,9 +124,9 @@ wchar_t* FXSYS_wcsncpy(wchar_t* dstStr, const wchar_t* srcStr, size_t count) { + } + + int32_t FXSYS_wcsnicmp(const wchar_t* s1, const wchar_t* s2, size_t count) { +- ASSERT(s1); +- ASSERT(s2); +- ASSERT(count > 0); ++ DCHECK(s1); ++ DCHECK(s2); ++ DCHECK(count > 0); + + wchar_t wch1 = 0, wch2 = 0; + while (count-- > 0) { +@@ -153,7 +150,7 @@ void FXSYS_IntToFourHexChars(uint16_t n, char* buf) { + } + + size_t FXSYS_ToUTF16BE(uint32_t unicode, char* buf) { +- ASSERT(unicode <= 0xD7FF || (unicode > 0xDFFF && unicode <= 0x10FFFF)); ++ DCHECK(unicode <= 0xD7FF || (unicode > 0xDFFF && unicode <= 0x10FFFF)); + if (unicode <= 0xFFFF) { + FXSYS_IntToFourHexChars(unicode, buf); + return 4; +diff --git a/core/fxcrt/fx_extension.h b/core/fxcrt/fx_extension.h +index 28b23cf50..ae9447983 100644 +--- a/core/fxcrt/fx_extension.h ++++ b/core/fxcrt/fx_extension.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,14 +7,12 @@ + #ifndef CORE_FXCRT_FX_EXTENSION_H_ + #define CORE_FXCRT_FX_EXTENSION_H_ + ++#include ++#include + #include ++#include + +-#include +-#include +-#include +-#include +- +-#include "core/fxcrt/fx_string.h" ++#include "build/build_config.h" + + #if defined(USE_SYSTEM_ICUUC) + #include +@@ -24,11 +22,9 @@ + + #define FX_INVALID_OFFSET static_cast(-1) + +-#ifdef PDF_ENABLE_XFA + #define FX_IsOdd(a) ((a)&1) +-#endif // PDF_ENABLE_XFA + +-float FXSYS_wcstof(const wchar_t* pwsStr, int32_t iLength, int32_t* pUsedLen); ++float FXSYS_wcstof(const wchar_t* pwsStr, size_t nLength, size_t* pUsedLen); + wchar_t* FXSYS_wcsncpy(wchar_t* dstStr, const wchar_t* srcStr, size_t count); + int32_t FXSYS_wcsnicmp(const wchar_t* s1, const wchar_t* s2, size_t count); + +@@ -48,8 +44,16 @@ inline int32_t FXSYS_towupper(wchar_t c) { + return u_toupper(c); + } + ++inline bool FXSYS_IsLowerASCII(int32_t c) { ++ return c >= 'a' && c <= 'z'; ++} ++ ++inline bool FXSYS_IsUpperASCII(int32_t c) { ++ return c >= 'A' && c <= 'Z'; ++} ++ + inline char FXSYS_ToUpperASCII(char c) { +- return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c; ++ return FXSYS_IsLowerASCII(c) ? (c + ('A' - 'a')) : c; + } + + inline bool FXSYS_iswalpha(wchar_t c) { +@@ -69,11 +73,11 @@ inline bool FXSYS_IsOctalDigit(char c) { + } + + inline bool FXSYS_IsHexDigit(char c) { +- return !((c & 0x80) || !std::isxdigit(c)); ++ return !((c & 0x80) || !isxdigit(c)); + } + + inline bool FXSYS_IsWideHexDigit(wchar_t c) { +- return !((c & 0xFFFFFF80) || !std::isxdigit(c)); ++ return !((c & 0xFFFFFF80) || !isxdigit(c)); + } + + inline int FXSYS_HexCharToInt(char c) { +@@ -86,16 +90,16 @@ inline int FXSYS_HexCharToInt(char c) { + inline int FXSYS_WideHexCharToInt(wchar_t c) { + if (!FXSYS_IsWideHexDigit(c)) + return 0; +- char upchar = std::toupper(static_cast(c)); ++ char upchar = toupper(static_cast(c)); + return upchar > '9' ? upchar - 'A' + 10 : upchar - '0'; + } + + inline bool FXSYS_IsDecimalDigit(char c) { +- return !((c & 0x80) || !std::isdigit(c)); ++ return !((c & 0x80) || !isdigit(c)); + } + + inline bool FXSYS_IsDecimalDigit(wchar_t c) { +- return !((c & 0xFFFFFF80) || !std::iswdigit(c)); ++ return !((c & 0xFFFFFF80) || !iswdigit(c)); + } + + inline int FXSYS_DecimalCharToInt(char c) { +@@ -112,18 +116,19 @@ void FXSYS_IntToFourHexChars(uint16_t n, char* buf); + size_t FXSYS_ToUTF16BE(uint32_t unicode, char* buf); + + // Strict order over floating types where NaNs may be present. ++// All NaNs are treated as equal to each other and greater than infinity. + template + bool FXSYS_SafeEQ(const T& lhs, const T& rhs) { +- return (std::isnan(lhs) && std::isnan(rhs)) || +- (!std::isnan(lhs) && !std::isnan(rhs) && lhs == rhs); ++ return (isnan(lhs) && isnan(rhs)) || ++ (!isnan(lhs) && !isnan(rhs) && lhs == rhs); + } + + template + bool FXSYS_SafeLT(const T& lhs, const T& rhs) { +- if (std::isnan(lhs) && std::isnan(rhs)) ++ if (isnan(lhs) && isnan(rhs)) + return false; +- if (std::isnan(lhs) || std::isnan(rhs)) +- return std::isnan(lhs) < std::isnan(rhs); ++ if (isnan(lhs) || isnan(rhs)) ++ return isnan(lhs) < isnan(rhs); + return lhs < rhs; + } + +diff --git a/core/fxcrt/fx_extension_unittest.cpp b/core/fxcrt/fx_extension_unittest.cpp +index 81fc4f70f..1ca94a7b2 100644 +--- a/core/fxcrt/fx_extension_unittest.cpp ++++ b/core/fxcrt/fx_extension_unittest.cpp +@@ -1,13 +1,58 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcrt/fx_extension.h" + ++#include ++ ++#include + #include + + #include "testing/gtest/include/gtest/gtest.h" + ++TEST(fxcrt, FXSYS_IsLowerASCII) { ++ EXPECT_TRUE(FXSYS_IsLowerASCII('a')); ++ EXPECT_TRUE(FXSYS_IsLowerASCII(L'a')); ++ EXPECT_TRUE(FXSYS_IsLowerASCII('b')); ++ EXPECT_TRUE(FXSYS_IsLowerASCII(L'b')); ++ EXPECT_TRUE(FXSYS_IsLowerASCII('y')); ++ EXPECT_TRUE(FXSYS_IsLowerASCII(L'y')); ++ EXPECT_TRUE(FXSYS_IsLowerASCII('z')); ++ EXPECT_TRUE(FXSYS_IsLowerASCII(L'z')); ++ EXPECT_FALSE(FXSYS_IsLowerASCII('`')); ++ EXPECT_FALSE(FXSYS_IsLowerASCII(L'`')); ++ EXPECT_FALSE(FXSYS_IsLowerASCII('{')); ++ EXPECT_FALSE(FXSYS_IsLowerASCII(L'{')); ++ EXPECT_FALSE(FXSYS_IsLowerASCII('Z')); ++ EXPECT_FALSE(FXSYS_IsLowerASCII(L'Z')); ++ EXPECT_FALSE(FXSYS_IsLowerASCII('7')); ++ EXPECT_FALSE(FXSYS_IsLowerASCII(L'7')); ++ EXPECT_FALSE(FXSYS_IsLowerASCII(static_cast(-78))); ++ EXPECT_FALSE(FXSYS_IsLowerASCII(static_cast(0xb2))); ++} ++ ++TEST(fxcrt, FXSYS_IsUpperASCII) { ++ EXPECT_TRUE(FXSYS_IsUpperASCII('A')); ++ EXPECT_TRUE(FXSYS_IsUpperASCII(L'A')); ++ EXPECT_TRUE(FXSYS_IsUpperASCII('B')); ++ EXPECT_TRUE(FXSYS_IsUpperASCII(L'B')); ++ EXPECT_TRUE(FXSYS_IsUpperASCII('Y')); ++ EXPECT_TRUE(FXSYS_IsUpperASCII(L'Y')); ++ EXPECT_TRUE(FXSYS_IsUpperASCII('Z')); ++ EXPECT_TRUE(FXSYS_IsUpperASCII(L'Z')); ++ EXPECT_FALSE(FXSYS_IsUpperASCII('@')); ++ EXPECT_FALSE(FXSYS_IsUpperASCII(L'@')); ++ EXPECT_FALSE(FXSYS_IsUpperASCII('[')); ++ EXPECT_FALSE(FXSYS_IsUpperASCII(L'[')); ++ EXPECT_FALSE(FXSYS_IsUpperASCII('z')); ++ EXPECT_FALSE(FXSYS_IsUpperASCII(L'z')); ++ EXPECT_FALSE(FXSYS_IsUpperASCII('7')); ++ EXPECT_FALSE(FXSYS_IsUpperASCII(L'7')); ++ EXPECT_FALSE(FXSYS_IsUpperASCII(static_cast(-78))); ++ EXPECT_FALSE(FXSYS_IsUpperASCII(static_cast(0xb2))); ++} ++ + TEST(fxcrt, FXSYS_HexCharToInt) { + EXPECT_EQ(10, FXSYS_HexCharToInt('a')); + EXPECT_EQ(10, FXSYS_HexCharToInt('A')); +@@ -84,40 +129,40 @@ TEST(fxcrt, FXSYS_ToUTF16BE) { + } + + TEST(fxcrt, FXSYS_wcstof) { +- int32_t used_len = 0; ++ size_t used_len = 0; + EXPECT_FLOAT_EQ(-12.0f, FXSYS_wcstof(L"-12", 3, &used_len)); +- EXPECT_EQ(3, used_len); ++ EXPECT_EQ(3u, used_len); + + used_len = 0; + EXPECT_FLOAT_EQ(1.5362f, FXSYS_wcstof(L"1.5362", 6, &used_len)); +- EXPECT_EQ(6, used_len); ++ EXPECT_EQ(6u, used_len); + + used_len = 0; + EXPECT_FLOAT_EQ(0.875f, FXSYS_wcstof(L"0.875", 5, &used_len)); +- EXPECT_EQ(5, used_len); ++ EXPECT_EQ(5u, used_len); + + used_len = 0; + EXPECT_FLOAT_EQ(5.56e-2f, FXSYS_wcstof(L"5.56e-2", 7, &used_len)); +- EXPECT_EQ(7, used_len); ++ EXPECT_EQ(7u, used_len); + + used_len = 0; + EXPECT_FLOAT_EQ(1.234e10f, FXSYS_wcstof(L"1.234E10", 8, &used_len)); +- EXPECT_EQ(8, used_len); ++ EXPECT_EQ(8u, used_len); + + used_len = 0; + EXPECT_FLOAT_EQ(0.0f, FXSYS_wcstof(L"1.234E100000000000000", 21, &used_len)); +- EXPECT_EQ(0, used_len); ++ EXPECT_EQ(0u, used_len); + + used_len = 0; + EXPECT_FLOAT_EQ(0.0f, FXSYS_wcstof(L"1.234E-128", 21, &used_len)); +- EXPECT_EQ(0, used_len); ++ EXPECT_EQ(0u, used_len); + + // TODO(dsinclair): This should round as per IEEE 64-bit values. + // EXPECT_EQ(L"123456789.01234567", FXSYS_wcstof(L"123456789.012345678")); + used_len = 0; + EXPECT_FLOAT_EQ(123456789.012345678f, + FXSYS_wcstof(L"123456789.012345678", 19, &used_len)); +- EXPECT_EQ(19, used_len); ++ EXPECT_EQ(19u, used_len); + + // TODO(dsinclair): This is spec'd as rounding when > 16 significant digits + // prior to the exponent. +@@ -125,25 +170,25 @@ TEST(fxcrt, FXSYS_wcstof) { + used_len = 0; + EXPECT_FLOAT_EQ(99999999999999999.0f, + FXSYS_wcstof(L"99999999999999999", 17, &used_len)); +- EXPECT_EQ(17, used_len); ++ EXPECT_EQ(17u, used_len); + + // For https://crbug.com/pdfium/1217 + EXPECT_FLOAT_EQ(0.0f, FXSYS_wcstof(L"e76", 3, nullptr)); + + // Overflow to infinity. + used_len = 0; +- EXPECT_TRUE(std::isinf(FXSYS_wcstof( ++ EXPECT_TRUE(isinf(FXSYS_wcstof( + L"88888888888888888888888888888888888888888888888888888888888888888888888" + L"88888888888888888888888888888888888888888888888888888888888", + 130, &used_len))); +- EXPECT_EQ(130, used_len); ++ EXPECT_EQ(130u, used_len); + + used_len = 0; +- EXPECT_TRUE(std::isinf(FXSYS_wcstof( ++ EXPECT_TRUE(isinf(FXSYS_wcstof( + L"-8888888888888888888888888888888888888888888888888888888888888888888888" + L"888888888888888888888888888888888888888888888888888888888888", + 131, &used_len))); +- EXPECT_EQ(131, used_len); ++ EXPECT_EQ(131u, used_len); + } + + TEST(fxcrt, FXSYS_SafeOps) { +@@ -153,8 +198,8 @@ TEST(fxcrt, FXSYS_SafeOps) { + const float fNan = std::numeric_limits::quiet_NaN(); + const float ascending[] = {fMin, 1.0f, 2.0f, fMax, fInf, fNan}; + +- for (size_t i = 0; i < FX_ArraySize(ascending); ++i) { +- for (size_t j = 0; j < FX_ArraySize(ascending); ++j) { ++ for (size_t i = 0; i < std::size(ascending); ++i) { ++ for (size_t j = 0; j < std::size(ascending); ++j) { + if (i == j) { + EXPECT_TRUE(FXSYS_SafeEQ(ascending[i], ascending[j])) + << " at " << i << " " << j; +diff --git a/core/fxcrt/fx_folder.h b/core/fxcrt/fx_folder.h +new file mode 100644 +index 000000000..46ace078e +--- /dev/null ++++ b/core/fxcrt/fx_folder.h +@@ -0,0 +1,24 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCRT_FX_FOLDER_H_ ++#define CORE_FXCRT_FX_FOLDER_H_ ++ ++#include ++ ++#include "core/fxcrt/bytestring.h" ++ ++class FX_Folder { ++ public: ++ static std::unique_ptr OpenFolder(const ByteString& path); ++ ++ virtual ~FX_Folder() = default; ++ ++ // `filename` and `folder` are required out-parameters. ++ virtual bool GetNextFile(ByteString* filename, bool* bFolder) = 0; ++}; ++ ++#endif // CORE_FXCRT_FX_FOLDER_H_ +diff --git a/core/fxcrt/fx_folder_posix.cpp b/core/fxcrt/fx_folder_posix.cpp +new file mode 100644 +index 000000000..b4cf9f9ec +--- /dev/null ++++ b/core/fxcrt/fx_folder_posix.cpp +@@ -0,0 +1,66 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxcrt/fx_folder.h" ++ ++#include ++ ++#include "build/build_config.h" ++#include "core/fxcrt/unowned_ptr.h" ++#include "third_party/base/ptr_util.h" ++ ++#if BUILDFLAG(IS_WIN) ++#error "built on wrong platform" ++#endif ++ ++#include ++#include ++#include ++ ++class FX_PosixFolder : public FX_Folder { ++ public: ++ ~FX_PosixFolder() override; ++ ++ bool GetNextFile(ByteString* filename, bool* bFolder) override; ++ ++ private: ++ friend class FX_Folder; ++ FX_PosixFolder(const ByteString& path, DIR* dir); ++ ++ const ByteString m_Path; ++ UnownedPtr m_Dir; ++}; ++ ++std::unique_ptr FX_Folder::OpenFolder(const ByteString& path) { ++ DIR* dir = opendir(path.c_str()); ++ if (!dir) ++ return nullptr; ++ ++ // Private ctor. ++ return pdfium::WrapUnique(new FX_PosixFolder(path, dir)); ++} ++ ++FX_PosixFolder::FX_PosixFolder(const ByteString& path, DIR* dir) ++ : m_Path(path), m_Dir(dir) {} ++ ++FX_PosixFolder::~FX_PosixFolder() { ++ closedir(m_Dir.ExtractAsDangling()); ++} ++ ++bool FX_PosixFolder::GetNextFile(ByteString* filename, bool* bFolder) { ++ struct dirent* de = readdir(m_Dir); ++ if (!de) ++ return false; ++ ++ ByteString fullpath = m_Path + "/" + de->d_name; ++ struct stat deStat; ++ if (stat(fullpath.c_str(), &deStat) < 0) ++ return false; ++ ++ *filename = de->d_name; ++ *bFolder = S_ISDIR(deStat.st_mode); ++ return true; ++} +diff --git a/core/fxcrt/fx_folder_windows.cpp b/core/fxcrt/fx_folder_windows.cpp +new file mode 100644 +index 000000000..500c73354 +--- /dev/null ++++ b/core/fxcrt/fx_folder_windows.cpp +@@ -0,0 +1,63 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxcrt/fx_folder.h" ++ ++#include ++ ++#include "build/build_config.h" ++#include "third_party/base/ptr_util.h" ++ ++#if !BUILDFLAG(IS_WIN) ++#error "built on wrong platform" ++#endif ++ ++#include ++ ++class FX_WindowsFolder : public FX_Folder { ++ public: ++ ~FX_WindowsFolder() override; ++ bool GetNextFile(ByteString* filename, bool* bFolder) override; ++ ++ private: ++ friend class FX_Folder; ++ FX_WindowsFolder(); ++ ++ HANDLE m_Handle = INVALID_HANDLE_VALUE; ++ bool m_bReachedEnd = false; ++ WIN32_FIND_DATAA m_FindData; ++}; ++ ++std::unique_ptr FX_Folder::OpenFolder(const ByteString& path) { ++ // Private ctor. ++ auto handle = pdfium::WrapUnique(new FX_WindowsFolder()); ++ ByteString search_path = path + "/*.*"; ++ handle->m_Handle = ++ FindFirstFileExA(search_path.c_str(), FindExInfoStandard, ++ &handle->m_FindData, FindExSearchNameMatch, nullptr, 0); ++ if (handle->m_Handle == INVALID_HANDLE_VALUE) ++ return nullptr; ++ ++ return handle; ++} ++ ++FX_WindowsFolder::FX_WindowsFolder() = default; ++ ++FX_WindowsFolder::~FX_WindowsFolder() { ++ if (m_Handle != INVALID_HANDLE_VALUE) ++ FindClose(m_Handle); ++} ++ ++bool FX_WindowsFolder::GetNextFile(ByteString* filename, bool* bFolder) { ++ if (m_bReachedEnd) ++ return false; ++ ++ *filename = m_FindData.cFileName; ++ *bFolder = !!(m_FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); ++ if (!FindNextFileA(m_Handle, &m_FindData)) ++ m_bReachedEnd = true; ++ return true; ++} +diff --git a/core/fxcrt/fx_memory.cpp b/core/fxcrt/fx_memory.cpp +index 1ed4949aa..7b6aa345d 100644 +--- a/core/fxcrt/fx_memory.cpp ++++ b/core/fxcrt/fx_memory.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,130 +8,96 @@ + + #include // For abort(). + ++#include + #include + + #include "build/build_config.h" +-#include "core/fxcrt/fx_safe_types.h" + #include "third_party/base/debug/alias.h" + +-pdfium::base::PartitionAllocatorGeneric& GetArrayBufferPartitionAllocator() { +- static pdfium::base::PartitionAllocatorGeneric s_array_buffer_allocator; +- return s_array_buffer_allocator; +-} +- +-pdfium::base::PartitionAllocatorGeneric& GetGeneralPartitionAllocator() { +- static pdfium::base::PartitionAllocatorGeneric s_general_allocator; +- return s_general_allocator; +-} +- +-pdfium::base::PartitionAllocatorGeneric& GetStringPartitionAllocator() { +- static pdfium::base::PartitionAllocatorGeneric s_string_allocator; +- return s_string_allocator; +-} +- +-void FXMEM_InitializePartitionAlloc() { +- static bool s_partition_allocators_initialized = false; +- if (!s_partition_allocators_initialized) { +- pdfium::base::PartitionAllocGlobalInit(FX_OutOfMemoryTerminate); +- GetArrayBufferPartitionAllocator().init(); +- GetGeneralPartitionAllocator().init(); +- GetStringPartitionAllocator().init(); +- s_partition_allocators_initialized = true; +- } +-} ++#if BUILDFLAG(IS_WIN) ++#include ++#endif + + void* FXMEM_DefaultAlloc(size_t byte_size) { +- return pdfium::base::PartitionAllocGenericFlags( +- GetGeneralPartitionAllocator().root(), +- pdfium::base::PartitionAllocReturnNull, byte_size, "GeneralPartition"); ++ return pdfium::internal::Alloc(byte_size, 1); + } + + void* FXMEM_DefaultCalloc(size_t num_elems, size_t byte_size) { +- return FX_SafeAlloc(num_elems, byte_size); ++ return pdfium::internal::Calloc(num_elems, byte_size); + } + + void* FXMEM_DefaultRealloc(void* pointer, size_t new_size) { +- return pdfium::base::PartitionReallocGenericFlags( +- GetGeneralPartitionAllocator().root(), +- pdfium::base::PartitionAllocReturnNull, pointer, new_size, +- "GeneralPartition"); ++ return pdfium::internal::Realloc(pointer, new_size, 1); + } + + void FXMEM_DefaultFree(void* pointer) { +- pdfium::base::PartitionFree(pointer); ++ FX_Free(pointer); + } + +-NOINLINE void FX_OutOfMemoryTerminate() { ++NOINLINE void FX_OutOfMemoryTerminate(size_t size) { + // Convince the linker this should not be folded with similar functions using + // Identical Code Folding. + static int make_this_function_aliased = 0xbd; + pdfium::base::debug::Alias(&make_this_function_aliased); + +- // Termimate cleanly if we can, else crash at a specific address (0xbd). +- abort(); +-#if !defined(OS_WIN) +- reinterpret_cast(0xbd)(); ++#if BUILDFLAG(IS_WIN) ++ // The same custom Windows exception code used in Chromium and Breakpad. ++ constexpr DWORD kOomExceptionCode = 0xe0000008; ++ ULONG_PTR exception_args[] = {size}; ++ ::RaiseException(kOomExceptionCode, EXCEPTION_NONCONTINUABLE, ++ std::size(exception_args), exception_args); + #endif +-} + +-void* FX_SafeAlloc(size_t num_members, size_t member_size) { +- FX_SAFE_SIZE_T total = member_size; +- total *= num_members; +- if (!total.IsValid()) +- return nullptr; +- +- constexpr int kFlags = pdfium::base::PartitionAllocReturnNull | +- pdfium::base::PartitionAllocZeroFill; +- return pdfium::base::PartitionAllocGenericFlags( +- GetGeneralPartitionAllocator().root(), kFlags, total.ValueOrDie(), +- "GeneralPartition"); ++ // Terminate cleanly. ++ abort(); + } + +-void* FX_SafeRealloc(void* ptr, size_t num_members, size_t member_size) { +- FX_SAFE_SIZE_T size = num_members; +- size *= member_size; +- if (!size.IsValid()) +- return nullptr; ++namespace pdfium { ++namespace internal { ++ ++void* AllocOrDie(size_t num_members, size_t member_size) { ++ void* result = Alloc(num_members, member_size); ++ if (!result) ++ FX_OutOfMemoryTerminate(0); // Never returns. + +- return pdfium::base::PartitionReallocGenericFlags( +- GetGeneralPartitionAllocator().root(), +- pdfium::base::PartitionAllocReturnNull, ptr, size.ValueOrDie(), +- "GeneralPartition"); ++ return result; + } + +-void* FX_AllocOrDie(size_t num_members, size_t member_size) { +- // TODO(tsepez): See if we can avoid the implicit memset(0). +- void* result = FX_SafeAlloc(num_members, member_size); ++void* AllocOrDie2D(size_t w, size_t h, size_t member_size) { ++ if (w >= std::numeric_limits::max() / h) ++ FX_OutOfMemoryTerminate(0); // Never returns. ++ ++ return AllocOrDie(w * h, member_size); ++} ++void* CallocOrDie(size_t num_members, size_t member_size) { ++ void* result = Calloc(num_members, member_size); + if (!result) +- FX_OutOfMemoryTerminate(); // Never returns. ++ FX_OutOfMemoryTerminate(0); // Never returns. + + return result; + } + +-void* FX_AllocOrDie2D(size_t w, size_t h, size_t member_size) { ++void* CallocOrDie2D(size_t w, size_t h, size_t member_size) { + if (w >= std::numeric_limits::max() / h) +- FX_OutOfMemoryTerminate(); // Never returns. ++ FX_OutOfMemoryTerminate(0); // Never returns. + +- return FX_AllocOrDie(w * h, member_size); ++ return CallocOrDie(w * h, member_size); + } + +-void* FX_ReallocOrDie(void* ptr, size_t num_members, size_t member_size) { +- void* result = FX_SafeRealloc(ptr, num_members, member_size); ++void* ReallocOrDie(void* ptr, size_t num_members, size_t member_size) { ++ void* result = Realloc(ptr, num_members, member_size); + if (!result) +- FX_OutOfMemoryTerminate(); // Never returns. ++ FX_OutOfMemoryTerminate(0); // Never returns. + + return result; + } + +-void FX_Free(void* ptr) { +- // TODO(palmer): Removing this check exposes crashes when PDFium callers +- // attempt to free |nullptr|. Although libc's |free| allows freeing |NULL|, no +- // other Partition Alloc callers need this tolerant behavior. Additionally, +- // checking for |nullptr| adds a branch to |PartitionFree|, and it's nice to +- // not have to have that. +- // +- // So this check is hiding (what I consider to be) bugs, and we should try to +- // fix them. https://bugs.chromium.org/p/pdfium/issues/detail?id=690 +- if (ptr) +- pdfium::base::PartitionFree(ptr); ++void* StringAllocOrDie(size_t num_members, size_t member_size) { ++ void* result = StringAlloc(num_members, member_size); ++ if (!result) ++ FX_OutOfMemoryTerminate(0); // Never returns. ++ ++ return result; + } ++} // namespace internal ++} // namespace pdfium +diff --git a/core/fxcrt/fx_memory.h b/core/fxcrt/fx_memory.h +index 6cb5f2e1d..2ae5003d2 100644 +--- a/core/fxcrt/fx_memory.h ++++ b/core/fxcrt/fx_memory.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,50 +22,84 @@ void FXMEM_DefaultFree(void* pointer); + #ifdef __cplusplus + } // extern "C" + +-#include "third_party/base/allocator/partition_allocator/partition_alloc.h" ++#include "third_party/base/compiler_specific.h" + +-pdfium::base::PartitionAllocatorGeneric& GetArrayBufferPartitionAllocator(); +-pdfium::base::PartitionAllocatorGeneric& GetGeneralPartitionAllocator(); +-pdfium::base::PartitionAllocatorGeneric& GetStringPartitionAllocator(); ++void FX_InitializeMemoryAllocators(); ++NOINLINE void FX_OutOfMemoryTerminate(size_t size); + +-void FXMEM_InitializePartitionAlloc(); +-NOINLINE void FX_OutOfMemoryTerminate(); ++// General Partition Allocators. + + // These never return nullptr, and must return cleared memory. + #define FX_Alloc(type, size) \ +- static_cast(FX_AllocOrDie(size, sizeof(type))) ++ static_cast(pdfium::internal::CallocOrDie(size, sizeof(type))) + #define FX_Alloc2D(type, w, h) \ +- static_cast(FX_AllocOrDie2D(w, h, sizeof(type))) ++ static_cast(pdfium::internal::CallocOrDie2D(w, h, sizeof(type))) + #define FX_Realloc(type, ptr, size) \ +- static_cast(FX_ReallocOrDie(ptr, size, sizeof(type))) ++ static_cast(pdfium::internal::ReallocOrDie(ptr, size, sizeof(type))) + + // May return nullptr, but returns cleared memory otherwise. + #define FX_TryAlloc(type, size) \ +- static_cast(FX_SafeAlloc(size, sizeof(type))) ++ static_cast(pdfium::internal::Calloc(size, sizeof(type))) + #define FX_TryRealloc(type, ptr, size) \ +- static_cast(FX_SafeRealloc(ptr, size, sizeof(type))) ++ static_cast(pdfium::internal::Realloc(ptr, size, sizeof(type))) + +-void* FX_SafeAlloc(size_t num_members, size_t member_size); +-void* FX_SafeRealloc(void* ptr, size_t num_members, size_t member_size); +-void* FX_AllocOrDie(size_t num_members, size_t member_size); +-void* FX_AllocOrDie2D(size_t w, size_t h, size_t member_size); +-void* FX_ReallocOrDie(void* ptr, size_t num_members, size_t member_size); ++// These never return nullptr, but return uninitialized memory. ++// TODO(thestig): Add FX_TryAllocUninit() if there is a use case. ++#define FX_AllocUninit(type, size) \ ++ static_cast(pdfium::internal::AllocOrDie(size, sizeof(type))) ++#define FX_AllocUninit2D(type, w, h) \ ++ static_cast(pdfium::internal::AllocOrDie2D(w, h, sizeof(type))) ++ ++// String Partition Allocators. ++ ++// This never returns nullptr, but returns uninitialized memory. ++#define FX_StringAlloc(type, size) \ ++ static_cast(pdfium::internal::StringAllocOrDie(size, sizeof(type))) ++ ++// FX_Free accepts memory from all of the above. + void FX_Free(void* ptr); + +-// The FX_ArraySize(arr) macro returns the # of elements in an array arr. +-// The expression is a compile-time constant, and therefore can be +-// used in defining new arrays, for example. If you use FX_ArraySize on +-// a pointer by mistake, you will get a compile-time error. +-// +-// One caveat is that FX_ArraySize() doesn't accept any array of an +-// anonymous type or a type defined inside a function. +-#define FX_ArraySize(array) (sizeof(ArraySizeHelper(array))) +- +-// This template function declaration is used in defining FX_ArraySize. +-// Note that the function doesn't need an implementation, as we only +-// use its type. +-template +-char (&ArraySizeHelper(T (&array)[N]))[N]; ++#ifndef V8_ENABLE_SANDBOX ++// V8 Array Buffer Partition Allocators. ++ ++// This never returns nullptr, and returns zeroed memory. ++void* FX_ArrayBufferAllocate(size_t length); ++ ++// This never returns nullptr, but returns uninitialized memory. ++void* FX_ArrayBufferAllocateUninitialized(size_t length); ++ ++// FX_ArrayBufferFree accepts memory from both of the above. ++void FX_ArrayBufferFree(void* data); ++#endif // V8_ENABLE_SANDBOX ++ ++namespace pdfium { ++namespace internal { ++ ++// General partition. ++void* Alloc(size_t num_members, size_t member_size); ++void* AllocOrDie(size_t num_members, size_t member_size); ++void* AllocOrDie2D(size_t w, size_t h, size_t member_size); ++void* Calloc(size_t num_members, size_t member_size); ++void* Realloc(void* ptr, size_t num_members, size_t member_size); ++void* CallocOrDie(size_t num_members, size_t member_size); ++void* CallocOrDie2D(size_t w, size_t h, size_t member_size); ++void* ReallocOrDie(void* ptr, size_t num_members, size_t member_size); ++ ++// String partition. ++void* StringAlloc(size_t num_members, size_t member_size); ++void* StringAllocOrDie(size_t num_members, size_t member_size); ++ ++} // namespace internal ++} // namespace pdfium ++ ++// Force stack allocation of a class. Classes that do complex work in a ++// destructor, such as the flushing of buffers, should be declared as ++// stack-allocated as possible, since future memory allocation schemes ++// may not run destructors in a predictable manner if an instance is ++// heap-allocated. ++#define FX_STACK_ALLOCATED() \ ++ void* operator new(size_t) = delete; \ ++ void* operator new(size_t, void*) = delete + + // Round up to the power-of-two boundary N. + template +diff --git a/core/fxcrt/fx_memory_malloc.cpp b/core/fxcrt/fx_memory_malloc.cpp +new file mode 100644 +index 000000000..44a05c6d0 +--- /dev/null ++++ b/core/fxcrt/fx_memory_malloc.cpp +@@ -0,0 +1,83 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxcrt/fx_memory.h" ++ ++#include ++ ++#include ++ ++#include "build/build_config.h" ++#include "core/fxcrt/fx_safe_types.h" ++ ++#if defined(PDF_USE_PARTITION_ALLOC) ++#error "File compiled under wrong build option." ++#endif ++ ++namespace pdfium { ++namespace internal { ++ ++// Slightly less than 2GB, typically. ++constexpr size_t kMallocSizeLimit = std::numeric_limits::max() - (1 << 12); ++ ++void* Alloc(size_t num_members, size_t member_size) { ++ FX_SAFE_SIZE_T total = member_size; ++ total *= num_members; ++ if (!total.IsValid() || total.ValueOrDie() >= kMallocSizeLimit) ++ return nullptr; ++ return malloc(total.ValueOrDie()); ++} ++ ++void* Calloc(size_t num_members, size_t member_size) { ++ FX_SAFE_SIZE_T total = member_size; ++ total *= num_members; ++ if (!total.IsValid() || total.ValueOrDie() >= kMallocSizeLimit) ++ return nullptr; ++ return calloc(num_members, member_size); ++} ++ ++void* Realloc(void* ptr, size_t num_members, size_t member_size) { ++ FX_SAFE_SIZE_T total = num_members; ++ total *= member_size; ++ if (!total.IsValid() || total.ValueOrDie() >= kMallocSizeLimit) ++ return nullptr; ++ return realloc(ptr, total.ValueOrDie()); ++} ++ ++void* StringAlloc(size_t num_members, size_t member_size) { ++ FX_SAFE_SIZE_T total = member_size; ++ total *= num_members; ++ if (!total.IsValid()) ++ return nullptr; ++ return malloc(total.ValueOrDie()); ++} ++ ++} // namespace internal ++} // namespace pdfium ++ ++void FX_InitializeMemoryAllocators() {} ++ ++void* FX_ArrayBufferAllocate(size_t length) { ++ void* result = calloc(length, 1); ++ if (!result) ++ FX_OutOfMemoryTerminate(length); ++ return result; ++} ++ ++void* FX_ArrayBufferAllocateUninitialized(size_t length) { ++ void* result = malloc(length); ++ if (!result) ++ FX_OutOfMemoryTerminate(length); ++ return result; ++} ++ ++void FX_ArrayBufferFree(void* data) { ++ free(data); ++} ++ ++void FX_Free(void* ptr) { ++ free(ptr); ++} +diff --git a/core/fxcrt/fx_memory_pa.cpp b/core/fxcrt/fx_memory_pa.cpp +new file mode 100644 +index 000000000..35c043a02 +--- /dev/null ++++ b/core/fxcrt/fx_memory_pa.cpp +@@ -0,0 +1,142 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxcrt/fx_memory.h" ++ ++#include "base/allocator/partition_allocator/partition_alloc.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "third_party/base/no_destructor.h" ++ ++#if !defined(PDF_USE_PARTITION_ALLOC) ++#error "File compiled under wrong build option." ++#endif ++ ++namespace { ++ ++constexpr partition_alloc::PartitionOptions kOptions = { ++ partition_alloc::PartitionOptions::AlignedAlloc::kDisallowed, ++ partition_alloc::PartitionOptions::ThreadCache::kDisabled, ++ partition_alloc::PartitionOptions::Quarantine::kDisallowed, ++ partition_alloc::PartitionOptions::Cookie::kAllowed, ++ partition_alloc::PartitionOptions::BackupRefPtr::kDisabled, ++ partition_alloc::PartitionOptions::BackupRefPtrZapping::kDisabled, ++ partition_alloc::PartitionOptions::UseConfigurablePool::kNo, ++}; ++ ++#ifndef V8_ENABLE_SANDBOX ++partition_alloc::PartitionAllocator& GetArrayBufferPartitionAllocator() { ++ static pdfium::base::NoDestructor ++ s_array_buffer_allocator; ++ return *s_array_buffer_allocator; ++} ++#endif // V8_ENABLE_SANDBOX ++ ++partition_alloc::PartitionAllocator& GetGeneralPartitionAllocator() { ++ static pdfium::base::NoDestructor ++ s_general_allocator; ++ return *s_general_allocator; ++} ++ ++partition_alloc::PartitionAllocator& GetStringPartitionAllocator() { ++ static pdfium::base::NoDestructor ++ s_string_allocator; ++ return *s_string_allocator; ++} ++ ++} // namespace ++ ++namespace pdfium { ++namespace internal { ++ ++void* Alloc(size_t num_members, size_t member_size) { ++ FX_SAFE_SIZE_T total = member_size; ++ total *= num_members; ++ if (!total.IsValid()) ++ return nullptr; ++ ++ return GetGeneralPartitionAllocator().root()->AllocWithFlags( ++ partition_alloc::AllocFlags::kReturnNull, total.ValueOrDie(), ++ "GeneralPartition"); ++} ++ ++void* Calloc(size_t num_members, size_t member_size) { ++ FX_SAFE_SIZE_T total = member_size; ++ total *= num_members; ++ if (!total.IsValid()) ++ return nullptr; ++ ++ return GetGeneralPartitionAllocator().root()->AllocWithFlags( ++ partition_alloc::AllocFlags::kReturnNull | ++ partition_alloc::AllocFlags::kZeroFill, ++ total.ValueOrDie(), "GeneralPartition"); ++} ++ ++void* Realloc(void* ptr, size_t num_members, size_t member_size) { ++ FX_SAFE_SIZE_T size = num_members; ++ size *= member_size; ++ if (!size.IsValid()) ++ return nullptr; ++ ++ return GetGeneralPartitionAllocator().root()->ReallocWithFlags( ++ partition_alloc::AllocFlags::kReturnNull, ptr, size.ValueOrDie(), ++ "GeneralPartition"); ++} ++ ++void* StringAlloc(size_t num_members, size_t member_size) { ++ FX_SAFE_SIZE_T total = member_size; ++ total *= num_members; ++ if (!total.IsValid()) ++ return nullptr; ++ ++ return GetStringPartitionAllocator().root()->AllocWithFlags( ++ partition_alloc::AllocFlags::kReturnNull, total.ValueOrDie(), ++ "StringPartition"); ++} ++ ++} // namespace internal ++} // namespace pdfium ++ ++void FX_InitializeMemoryAllocators() { ++ static bool s_partition_allocators_initialized = false; ++ if (!s_partition_allocators_initialized) { ++ partition_alloc::PartitionAllocGlobalInit(FX_OutOfMemoryTerminate); ++#ifndef V8_ENABLE_SANDBOX ++ GetArrayBufferPartitionAllocator().init(kOptions); ++#endif // V8_ENABLE_SANDBOX ++ GetGeneralPartitionAllocator().init(kOptions); ++ GetStringPartitionAllocator().init(kOptions); ++ s_partition_allocators_initialized = true; ++ } ++} ++ ++#ifndef V8_ENABLE_SANDBOX ++void* FX_ArrayBufferAllocate(size_t length) { ++ return GetArrayBufferPartitionAllocator().root()->AllocWithFlags( ++ partition_alloc::AllocFlags::kZeroFill, length, "FXArrayBuffer"); ++} ++ ++void* FX_ArrayBufferAllocateUninitialized(size_t length) { ++ return GetArrayBufferPartitionAllocator().root()->Alloc(length, ++ "FXArrayBuffer"); ++} ++ ++void FX_ArrayBufferFree(void* data) { ++ GetArrayBufferPartitionAllocator().root()->Free(data); ++} ++#endif // V8_ENABLE_SANDBOX ++ ++void FX_Free(void* ptr) { ++ // TODO(palmer): Removing this check exposes crashes when PDFium callers ++ // attempt to free |nullptr|. Although libc's |free| allows freeing |NULL|, no ++ // other Partition Alloc callers need this tolerant behavior. Additionally, ++ // checking for |nullptr| adds a branch to |PartitionFree|, and it's nice to ++ // not have to have that. ++ // ++ // So this check is hiding (what I consider to be) bugs, and we should try to ++ // fix them. https://bugs.chromium.org/p/pdfium/issues/detail?id=690 ++ if (ptr) ++ partition_alloc::ThreadSafePartitionRoot::Free(ptr); ++} +diff --git a/core/fxcrt/fx_memory_unittest.cpp b/core/fxcrt/fx_memory_unittest.cpp +index b3fd299b7..e0de4f4af 100644 +--- a/core/fxcrt/fx_memory_unittest.cpp ++++ b/core/fxcrt/fx_memory_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,11 +11,13 @@ + + namespace { + +-const size_t kMaxByteAlloc = std::numeric_limits::max(); +-const size_t kMaxIntAlloc = kMaxByteAlloc / sizeof(int); +-const size_t kOverflowIntAlloc = kMaxIntAlloc + 100; +-const size_t kWidth = 640; +-const size_t kOverflowIntAlloc2D = kMaxIntAlloc / kWidth + 10; ++constexpr size_t kMaxByteAlloc = std::numeric_limits::max(); ++constexpr size_t kMaxIntAlloc = kMaxByteAlloc / sizeof(int); ++constexpr size_t kOverflowIntAlloc = kMaxIntAlloc + 100; ++constexpr size_t kWidth = 640; ++constexpr size_t kOverflowIntAlloc2D = kMaxIntAlloc / kWidth + 10; ++constexpr size_t kCloseToMaxIntAlloc = kMaxIntAlloc - 100; ++constexpr size_t kCloseToMaxByteAlloc = kMaxByteAlloc - 100; + + } // namespace + +@@ -28,14 +30,13 @@ TEST(fxcrt, FX_AllocZero) { + FX_Free(ptr); + } + +-// TODO(tsepez): re-enable OOM tests if we can find a way to +-// prevent it from hosing the bots. +-TEST(fxcrt, DISABLED_FX_AllocOOM) { +- EXPECT_DEATH_IF_SUPPORTED((void)FX_Alloc(int, kMaxIntAlloc), ""); ++TEST(fxcrt, FXAllocOOM) { ++ EXPECT_DEATH_IF_SUPPORTED((void)FX_Alloc(int, kCloseToMaxIntAlloc), ""); + + int* ptr = FX_Alloc(int, 1); + EXPECT_TRUE(ptr); +- EXPECT_DEATH_IF_SUPPORTED((void)FX_Realloc(int, ptr, kMaxIntAlloc), ""); ++ EXPECT_DEATH_IF_SUPPORTED((void)FX_Realloc(int, ptr, kCloseToMaxIntAlloc), ++ ""); + FX_Free(ptr); + } + +@@ -60,12 +61,12 @@ TEST(fxcrt, FX_AllocOverflow2D) { + << ptr; + } + +-TEST(fxcrt, DISABLED_FX_TryAllocOOM) { +- EXPECT_FALSE(FX_TryAlloc(int, kMaxIntAlloc)); ++TEST(fxcrt, FXTryAllocOOM) { ++ EXPECT_FALSE(FX_TryAlloc(int, kCloseToMaxIntAlloc)); + + int* ptr = FX_Alloc(int, 1); + EXPECT_TRUE(ptr); +- EXPECT_FALSE(FX_TryRealloc(int, ptr, kMaxIntAlloc)); ++ EXPECT_FALSE(FX_TryRealloc(int, ptr, kCloseToMaxIntAlloc)); + FX_Free(ptr); + } + +@@ -85,15 +86,23 @@ TEST(fxcrt, FX_TryAllocOverflow) { + } + #endif + +-TEST(fxcrt, DISABLED_FXMEM_DefaultOOM) { +- EXPECT_FALSE(FXMEM_DefaultAlloc(kMaxByteAlloc)); ++TEST(fxcrt, FXMEMDefaultOOM) { ++ EXPECT_FALSE(FXMEM_DefaultAlloc(kCloseToMaxByteAlloc)); + + void* ptr = FXMEM_DefaultAlloc(1); + EXPECT_TRUE(ptr); +- EXPECT_FALSE(FXMEM_DefaultRealloc(ptr, kMaxByteAlloc)); ++ EXPECT_FALSE(FXMEM_DefaultRealloc(ptr, kCloseToMaxByteAlloc)); + FXMEM_DefaultFree(ptr); + } + ++TEST(fxcrt, AllocZeroesMemory) { ++ uint8_t* ptr = FX_Alloc(uint8_t, 32); ++ ASSERT_TRUE(ptr); ++ for (size_t i = 0; i < 32; ++i) ++ EXPECT_EQ(0, ptr[i]); ++ FX_Free(ptr); ++} ++ + TEST(fxcrt, FXAlign) { + static_assert(std::numeric_limits::max() % 2 == 1, + "numeric limit must be odd for this test"); +diff --git a/core/fxcrt/fx_memory_wrappers.h b/core/fxcrt/fx_memory_wrappers.h +index 823fe7669..9e8775411 100644 +--- a/core/fxcrt/fx_memory_wrappers.h ++++ b/core/fxcrt/fx_memory_wrappers.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,15 +16,28 @@ struct FxFreeDeleter { + inline void operator()(void* ptr) const { FX_Free(ptr); } + }; + +-// Used with std::vector<> to put purely numeric vectors into +-// the same "general" parition used by FX_Alloc(). Otherwise, +-// replacing FX_Alloc/FX_Free pairs with std::vector<> may undo +-// some of the nice segregation that we get from partition alloc. +-template +-struct FxAllocAllocator { ++// Escape hatch mechanism to allow non_arithmetic types into data partition. ++template ++struct IsFXDataPartitionException : std::false_type {}; ++ ++// Use with caution. No further checks are made to see if `T` is appropriate ++// for the Data Partition (e.g. no pointers, strings, vtables, etc.). This ++// declaration must occur in the top-level namespace. ++#define FX_DATA_PARTITION_EXCEPTION(T) \ ++ template <> \ ++ struct IsFXDataPartitionException : std::true_type {} ++ ++// Allocators for mapping STL containers onto Partition Alloc. ++// Otherwise, replacing e.g. the FX_AllocUninit/FX_Free pairs with STL may ++// undo some of the nice segregation that we get from PartitionAlloc. ++template ++struct FxPartitionAllocAllocator { + public: +- static_assert(std::is_arithmetic::value, ++#if !defined(COMPILER_MSVC) || defined(NDEBUG) ++ static_assert(std::is_arithmetic::value || std::is_enum::value || ++ IsFXDataPartitionException::value, + "Only numeric types allowed in this partition"); ++#endif + + using value_type = T; + using pointer = T*; +@@ -36,20 +49,22 @@ struct FxAllocAllocator { + + template + struct rebind { +- using other = FxAllocAllocator; ++ using other = FxPartitionAllocAllocator; + }; + +- FxAllocAllocator() noexcept = default; +- FxAllocAllocator(const FxAllocAllocator& other) noexcept = default; +- ~FxAllocAllocator() = default; ++ FxPartitionAllocAllocator() noexcept = default; ++ FxPartitionAllocAllocator(const FxPartitionAllocAllocator& other) noexcept = ++ default; ++ ~FxPartitionAllocAllocator() = default; + + template +- FxAllocAllocator(const FxAllocAllocator& other) noexcept {} ++ FxPartitionAllocAllocator( ++ const FxPartitionAllocAllocator& other) noexcept {} + + pointer address(reference x) const noexcept { return &x; } + const_pointer address(const_reference x) const noexcept { return &x; } + pointer allocate(size_type n, const void* hint = 0) { +- return static_cast(FX_AllocOrDie(n, sizeof(value_type))); ++ return static_cast(F(n, sizeof(value_type))); + } + void deallocate(pointer p, size_type n) { FX_Free(p); } + size_type max_size() const noexcept { +@@ -67,8 +82,23 @@ struct FxAllocAllocator { + } + + // There's no state, so they are all the same, +- bool operator==(const FxAllocAllocator& that) { return true; } +- bool operator!=(const FxAllocAllocator& that) { return false; } ++ bool operator==(const FxPartitionAllocAllocator& that) { return true; } ++ bool operator!=(const FxPartitionAllocAllocator& that) { return false; } + }; + ++// Used to put backing store for std::vector<> and such into the ++// general partition, ensuring they contain data only. ++template ::value || ++ std::is_enum::value || ++ IsFXDataPartitionException::value>> ++using FxAllocAllocator = ++ FxPartitionAllocAllocator; ++ ++// Used to put backing store for std::string<> and std::ostringstream<> ++// into the string partition. ++template ++using FxStringAllocator = ++ FxPartitionAllocAllocator; ++ + #endif // CORE_FXCRT_FX_MEMORY_WRAPPERS_H_ +diff --git a/core/fxcrt/fx_memory_wrappers_unittest.cpp b/core/fxcrt/fx_memory_wrappers_unittest.cpp +index 0927683d0..763836116 100644 +--- a/core/fxcrt/fx_memory_wrappers_unittest.cpp ++++ b/core/fxcrt/fx_memory_wrappers_unittest.cpp +@@ -1,15 +1,28 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcrt/fx_memory_wrappers.h" + + #include ++#include ++#include + #include + + #include "build/build_config.h" + #include "testing/gtest/include/gtest/gtest.h" + ++namespace { ++ ++struct OnlyNumbers { ++ int x; ++ int y; ++}; ++ ++} // namespace ++ ++FX_DATA_PARTITION_EXCEPTION(OnlyNumbers); ++ + TEST(fxcrt, FxFreeDeleter) { + std::unique_ptr empty(nullptr); + std::unique_ptr thing(FX_Alloc(int, 1)); +@@ -20,6 +33,7 @@ TEST(fxcrt, FxFreeDeleter) { + } + + TEST(fxcrt, FxAllocAllocator) { ++ // Let ASAN sanity check some simple operations. + std::vector> vec; + vec.push_back(42); + vec.reserve(100); +@@ -31,3 +45,22 @@ TEST(fxcrt, FxAllocAllocator) { + vec2.resize(0); + vec2.push_back(42); + } ++ ++TEST(fxcrt, FxStringAllocator) { ++ // Let ASAN sanity check some simple operations. ++ std::basic_ostringstream, ++ FxStringAllocator> ++ str; ++ str << 'B'; ++ str << "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; ++ str << "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; ++ str << "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; ++ str << 42.0f; ++} ++ ++TEST(fxcrt, FxAllocAllocatorStructException) { ++ std::vector> vec; ++ vec.push_back({42, 73}); ++ EXPECT_EQ(vec.back().x, 42); ++ EXPECT_EQ(vec.back().y, 73); ++} +diff --git a/core/fxcrt/fx_number.cpp b/core/fxcrt/fx_number.cpp +index 99354510d..a0f38e6be 100644 +--- a/core/fxcrt/fx_number.cpp ++++ b/core/fxcrt/fx_number.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,8 @@ + + #include "core/fxcrt/fx_number.h" + ++#include ++ + #include + + #include "core/fxcrt/fx_extension.h" +@@ -13,22 +15,22 @@ + #include "core/fxcrt/fx_string.h" + + FX_Number::FX_Number() +- : m_bInteger(true), m_bSigned(false), m_UnsignedValue(0) {} ++ : m_bIsInteger(true), m_bIsSigned(false), m_UnsignedValue(0) {} + + FX_Number::FX_Number(int32_t value) +- : m_bInteger(true), m_bSigned(true), m_SignedValue(value) {} ++ : m_bIsInteger(true), m_bIsSigned(true), m_SignedValue(value) {} + + FX_Number::FX_Number(float value) +- : m_bInteger(false), m_bSigned(true), m_FloatValue(value) {} ++ : m_bIsInteger(false), m_bIsSigned(true), m_FloatValue(value) {} + + FX_Number::FX_Number(ByteStringView strc) +- : m_bInteger(true), m_bSigned(false), m_UnsignedValue(0) { ++ : m_bIsInteger(true), m_bIsSigned(false), m_UnsignedValue(0) { + if (strc.IsEmpty()) + return; + + if (strc.Contains('.')) { +- m_bInteger = false; +- m_bSigned = true; ++ m_bIsInteger = false; ++ m_bIsSigned = true; + m_FloatValue = StringToFloat(strc); + return; + } +@@ -43,22 +45,22 @@ FX_Number::FX_Number(ByteStringView strc) + size_t cc = 0; + if (strc[0] == '+') { + cc++; +- m_bSigned = true; ++ m_bIsSigned = true; + } else if (strc[0] == '-') { + bNegative = true; +- m_bSigned = true; ++ m_bIsSigned = true; + cc++; + } + +- while (cc < strc.GetLength() && std::isdigit(strc[cc])) { +- unsigned_val = unsigned_val * 10 + FXSYS_DecimalCharToInt(strc.CharAt(cc)); +- if (!unsigned_val.IsValid()) +- break; +- cc++; ++ for (; cc < strc.GetLength() && isdigit(strc[cc]); ++cc) { ++ // Deliberately not using FXSYS_DecimalCharToInt() in a tight loop to avoid ++ // a duplicate isdigit() call. Note that the order of operation is ++ // important to avoid unintentional overflows. ++ unsigned_val = unsigned_val * 10 + (strc[cc] - '0'); + } + + uint32_t uValue = unsigned_val.ValueOrDefault(0); +- if (!m_bSigned) { ++ if (!m_bIsSigned) { + m_UnsignedValue = uValue; + return; + } +@@ -86,13 +88,13 @@ FX_Number::FX_Number(ByteStringView strc) + } + + int32_t FX_Number::GetSigned() const { +- return m_bInteger ? m_SignedValue : static_cast(m_FloatValue); ++ return m_bIsInteger ? m_SignedValue : static_cast(m_FloatValue); + } + + float FX_Number::GetFloat() const { +- if (!m_bInteger) ++ if (!m_bIsInteger) + return m_FloatValue; + +- return m_bSigned ? static_cast(m_SignedValue) +- : static_cast(m_UnsignedValue); ++ return m_bIsSigned ? static_cast(m_SignedValue) ++ : static_cast(m_UnsignedValue); + } +diff --git a/core/fxcrt/fx_number.h b/core/fxcrt/fx_number.h +index 0ee474468..3eda3eb22 100644 +--- a/core/fxcrt/fx_number.h ++++ b/core/fxcrt/fx_number.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -19,15 +19,15 @@ class FX_Number { + explicit FX_Number(float value); + explicit FX_Number(ByteStringView str); + +- bool IsInteger() const { return m_bInteger; } +- bool IsSigned() const { return m_bSigned; } ++ bool IsInteger() const { return m_bIsInteger; } ++ bool IsSigned() const { return m_bIsSigned; } + + int32_t GetSigned() const; // Underflow/Overflow possible. + float GetFloat() const; + + private: +- bool m_bInteger; // One of the two integers vs. float type. +- bool m_bSigned; // Only valid if |m_bInteger|. ++ bool m_bIsInteger; // One of the two integers vs. float type. ++ bool m_bIsSigned; // Only valid if |m_bInteger|. + union { + uint32_t m_UnsignedValue; + int32_t m_SignedValue; +diff --git a/core/fxcrt/fx_number_unittest.cpp b/core/fxcrt/fx_number_unittest.cpp +index a31dc55ac..deb4cf3c1 100644 +--- a/core/fxcrt/fx_number_unittest.cpp ++++ b/core/fxcrt/fx_number_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -46,39 +46,46 @@ TEST(fxnumber, FromFloat) { + } + + TEST(fxnumber, FromStringUnsigned) { +- { +- FX_Number number(""); +- EXPECT_TRUE(number.IsInteger()); +- EXPECT_FALSE(number.IsSigned()); +- } +- { +- FX_Number number("0"); +- EXPECT_TRUE(number.IsInteger()); +- EXPECT_FALSE(number.IsSigned()); +- } +- { +- FX_Number number("10"); +- EXPECT_TRUE(number.IsInteger()); +- EXPECT_FALSE(number.IsSigned()); +- } +- { +- FX_Number number("4294967295"); +- EXPECT_TRUE(number.IsInteger()); +- EXPECT_FALSE(number.IsSigned()); +- } +- { +- // Value overflows. +- FX_Number number("4223423494965252"); +- EXPECT_TRUE(number.IsInteger()); +- EXPECT_FALSE(number.IsSigned()); +- } +- { +- // No explicit sign will allow the number to go negative if we retrieve +- // it as a signed value. This is needed for things like the encryption +- // Permissions flag (Table 3.20 PDF 1.7 spec) +- FX_Number number("4294965252"); +- EXPECT_EQ(-2044, number.GetSigned()); +- } ++ struct TestCase { ++ const char* input; ++ int expected_output; ++ }; ++ ++ auto test_func = [](pdfium::span test_cases) { ++ for (const auto& test : test_cases) { ++ FX_Number number(test.input); ++ EXPECT_TRUE(number.IsInteger()); ++ EXPECT_FALSE(number.IsSigned()); ++ EXPECT_EQ(test.expected_output, number.GetSigned()); ++ } ++ }; ++ ++ static constexpr TestCase kNormalCases[] = { ++ {"", 0}, ++ {"0", 0}, ++ {"10", 10}, ++ }; ++ test_func(kNormalCases); ++ ++ static constexpr TestCase kOverflowCases[] = { ++ {"4223423494965252", 0}, ++ {"4294967296", 0}, ++ {"4294967297", 0}, ++ {"5000000000", 0}, ++ }; ++ test_func(kOverflowCases); ++ ++ // No explicit sign will allow the number to go negative if retrieved as a ++ // signed value. This is needed for things like the encryption permissions ++ // flag (Table 3.20 PDF 1.7 spec) ++ static constexpr TestCase kNegativeCases[] = { ++ {"4294965252", -2044}, ++ {"4294967247", -49}, ++ {"4294967248", -48}, ++ {"4294967292", -4}, ++ {"4294967295", -1}, ++ }; ++ test_func(kNegativeCases); + } + + TEST(fxnumber, FromStringSigned) { +diff --git a/core/fxcrt/fx_random.cpp b/core/fxcrt/fx_random.cpp +index ce796d9a1..cc40efceb 100644 +--- a/core/fxcrt/fx_random.cpp ++++ b/core/fxcrt/fx_random.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -17,7 +17,7 @@ + #define MT_Upper_Mask 0x80000000 + #define MT_Lower_Mask 0x7fffffff + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + #include + #else + #include +@@ -34,7 +34,7 @@ struct MTContext { + bool g_bHaveGlobalSeed = false; + uint32_t g_nGlobalSeed = 0; + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + bool GenerateSeedFromCryptoRandom(uint32_t* pSeed) { + HCRYPTPROV hCP = 0; + if (!::CryptAcquireContext(&hCP, nullptr, nullptr, PROV_RSA_FULL, 0) || +@@ -51,7 +51,7 @@ uint32_t GenerateSeedFromEnvironment() { + char c; + uintptr_t p = reinterpret_cast(&c); + uint32_t seed = ~static_cast(p >> 3); +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + SYSTEMTIME st; + GetSystemTime(&st); + seed ^= static_cast(st.wSecond) * 1000000; +@@ -69,7 +69,7 @@ uint32_t GenerateSeedFromEnvironment() { + + void* ContextFromNextGlobalSeed() { + if (!g_bHaveGlobalSeed) { +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + if (!GenerateSeedFromCryptoRandom(&g_nGlobalSeed)) + g_nGlobalSeed = GenerateSeedFromEnvironment(); + #else +diff --git a/core/fxcrt/fx_random.h b/core/fxcrt/fx_random.h +index 52d514ba5..c6cbd5ec3 100644 +--- a/core/fxcrt/fx_random.h ++++ b/core/fxcrt/fx_random.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcrt/fx_random_unittest.cpp b/core/fxcrt/fx_random_unittest.cpp +index 607f1409b..1361e2496 100644 +--- a/core/fxcrt/fx_random_unittest.cpp ++++ b/core/fxcrt/fx_random_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcrt/fx_safe_types.h b/core/fxcrt/fx_safe_types.h +index 6f5b967bd..d7b768b1f 100644 +--- a/core/fxcrt/fx_safe_types.h ++++ b/core/fxcrt/fx_safe_types.h +@@ -1,13 +1,14 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #ifndef CORE_FXCRT_FX_SAFE_TYPES_H_ + #define CORE_FXCRT_FX_SAFE_TYPES_H_ + +-#include // For size_t. ++#include ++#include + +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/fx_types.h" + #include "third_party/base/numerics/safe_math.h" + + using FX_SAFE_UINT32 = pdfium::base::CheckedNumeric; +diff --git a/core/fxcrt/fx_safe_types_unittest.cpp b/core/fxcrt/fx_safe_types_unittest.cpp +new file mode 100644 +index 000000000..bac918486 +--- /dev/null ++++ b/core/fxcrt/fx_safe_types_unittest.cpp +@@ -0,0 +1,22 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include ++ ++#include "core/fxcrt/fx_safe_types.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++// PDFium relies on safe types handling the --2147483648 boundary ++// condition without overflow. ++TEST(FXSafeTypes, UnaryMinus) { ++ FX_SAFE_INT32 safe_val = std::numeric_limits::min(); ++ EXPECT_TRUE(safe_val.IsValid()); ++ EXPECT_FALSE((-safe_val).IsValid()); ++} ++ ++TEST(FXSafeTypes, SubtractFromZero) { ++ FX_SAFE_INT32 safe_val = std::numeric_limits::min(); ++ EXPECT_TRUE(safe_val.IsValid()); ++ EXPECT_FALSE((0 - safe_val).IsValid()); ++} +diff --git a/core/fxcrt/fx_stream.cpp b/core/fxcrt/fx_stream.cpp +index dd3c1fb2a..1ceb539d2 100644 +--- a/core/fxcrt/fx_stream.cpp ++++ b/core/fxcrt/fx_stream.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,100 +6,75 @@ + + #include "core/fxcrt/fx_stream.h" + +-#include + #include + #include +-#include + +-#include "build/build_config.h" + #include "core/fxcrt/fileaccess_iface.h" +-#include "core/fxcrt/fx_safe_types.h" +-#include "third_party/base/ptr_util.h" +- +-#if defined(OS_WIN) +-#include +- +-struct FX_FolderHandle { +- HANDLE m_Handle; +- bool m_bEnd; +- WIN32_FIND_DATAA m_FindData; +-}; +-#else +-#include +-#include +-#include +-#include +- +-struct FX_FolderHandle { +- ByteString m_Path; +- DIR* m_Dir; +-}; +-#endif + + namespace { + + class CFX_CRTFileStream final : public IFX_SeekableStream { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // IFX_SeekableStream: + FX_FILESIZE GetSize() override { return m_pFile->GetSize(); } + bool IsEOF() override { return GetPosition() >= GetSize(); } + FX_FILESIZE GetPosition() override { return m_pFile->GetPosition(); } +- bool ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) override { +- return m_pFile->ReadPos(buffer, size, offset) > 0; ++ bool ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) override { ++ return m_pFile->ReadPos(buffer.data(), buffer.size(), offset) > 0; + } +- size_t ReadBlock(void* buffer, size_t size) override { +- return m_pFile->Read(buffer, size); ++ size_t ReadBlock(pdfium::span buffer) override { ++ return m_pFile->Read(buffer.data(), buffer.size()); + } +- bool WriteBlockAtOffset(const void* buffer, +- FX_FILESIZE offset, +- size_t size) override { +- return !!m_pFile->WritePos(buffer, size, offset); ++ bool WriteBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) override { ++ return !!m_pFile->WritePos(buffer.data(), buffer.size(), offset); + } + bool Flush() override { return m_pFile->Flush(); } + + private: + explicit CFX_CRTFileStream(std::unique_ptr pFA) + : m_pFile(std::move(pFA)) {} +- ~CFX_CRTFileStream() override {} ++ ~CFX_CRTFileStream() override = default; + + std::unique_ptr m_pFile; + }; + + } // namespace + +-// static +-RetainPtr IFX_SeekableStream::CreateFromFilename( +- const char* filename, +- uint32_t dwModes) { +- std::unique_ptr pFA = FileAccessIface::Create(); +- if (!pFA->Open(filename, dwModes)) +- return nullptr; +- return pdfium::MakeRetain(std::move(pFA)); ++bool IFX_WriteStream::WriteString(ByteStringView str) { ++ return WriteBlock(str.raw_span()); + } + +-// static +-RetainPtr IFX_SeekableStream::CreateFromFilename( +- const wchar_t* filename, +- uint32_t dwModes) { +- std::unique_ptr pFA = FileAccessIface::Create(); +- if (!pFA->Open(filename, dwModes)) +- return nullptr; +- return pdfium::MakeRetain(std::move(pFA)); ++bool IFX_WriteStream::WriteByte(uint8_t byte) { ++ return WriteBlock({&byte, 1}); ++} ++ ++bool IFX_WriteStream::WriteDWord(uint32_t i) { ++ char buf[20] = {}; ++ FXSYS_itoa(i, buf, 10); ++ return WriteBlock({reinterpret_cast(buf), strlen(buf)}); ++} ++ ++bool IFX_WriteStream::WriteFilesize(FX_FILESIZE size) { ++ char buf[20] = {}; ++ FXSYS_i64toa(size, buf, 10); ++ return WriteBlock({reinterpret_cast(buf), strlen(buf)}); + } + + // static + RetainPtr IFX_SeekableReadStream::CreateFromFilename( + const char* filename) { +- return IFX_SeekableStream::CreateFromFilename(filename, FX_FILEMODE_ReadOnly); ++ std::unique_ptr pFA = FileAccessIface::Create(); ++ if (!pFA->Open(filename)) ++ return nullptr; ++ return pdfium::MakeRetain(std::move(pFA)); + } + +-bool IFX_SeekableWriteStream::WriteBlock(const void* pData, size_t size) { +- return WriteBlockAtOffset(pData, GetSize(), size); ++bool IFX_SeekableWriteStream::WriteBlock(pdfium::span buffer) { ++ return WriteBlockAtOffset(buffer, GetSize()); + } + + bool IFX_SeekableReadStream::IsEOF() { +@@ -110,78 +85,10 @@ FX_FILESIZE IFX_SeekableReadStream::GetPosition() { + return 0; + } + +-size_t IFX_SeekableReadStream::ReadBlock(void* buffer, size_t size) { ++size_t IFX_SeekableReadStream::ReadBlock(pdfium::span buffer) { + return 0; + } + +-bool IFX_SeekableStream::WriteBlock(const void* buffer, size_t size) { +- return WriteBlockAtOffset(buffer, GetSize(), size); +-} +- +-bool IFX_SeekableStream::WriteString(ByteStringView str) { +- return WriteBlock(str.unterminated_c_str(), str.GetLength()); +-} +- +-FX_FolderHandle* FX_OpenFolder(const char* path) { +- auto handle = pdfium::MakeUnique(); +-#if defined(OS_WIN) +- handle->m_Handle = +- FindFirstFileExA((ByteString(path) + "/*.*").c_str(), FindExInfoStandard, +- &handle->m_FindData, FindExSearchNameMatch, nullptr, 0); +- if (handle->m_Handle == INVALID_HANDLE_VALUE) +- return nullptr; +- +- handle->m_bEnd = false; +-#else +- DIR* dir = opendir(path); +- if (!dir) +- return nullptr; +- +- handle->m_Path = path; +- handle->m_Dir = dir; +-#endif +- return handle.release(); +-} +- +-bool FX_GetNextFile(FX_FolderHandle* handle, +- ByteString* filename, +- bool* bFolder) { +- if (!handle) +- return false; +- +-#if defined(OS_WIN) +- if (handle->m_bEnd) +- return false; +- +- *filename = handle->m_FindData.cFileName; +- *bFolder = +- (handle->m_FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; +- if (!FindNextFileA(handle->m_Handle, &handle->m_FindData)) +- handle->m_bEnd = true; +- return true; +-#else +- struct dirent* de = readdir(handle->m_Dir); +- if (!de) +- return false; +- ByteString fullpath = handle->m_Path + "/" + de->d_name; +- struct stat deStat; +- if (stat(fullpath.c_str(), &deStat) < 0) +- return false; +- +- *filename = de->d_name; +- *bFolder = S_ISDIR(deStat.st_mode); +- return true; +-#endif +-} +- +-void FX_CloseFolder(FX_FolderHandle* handle) { +- if (!handle) +- return; +- +-#if defined(OS_WIN) +- FindClose(handle->m_Handle); +-#else +- closedir(handle->m_Dir); +-#endif +- delete handle; ++bool IFX_SeekableStream::WriteBlock(pdfium::span buffer) { ++ return WriteBlockAtOffset(buffer, GetSize()); + } +diff --git a/core/fxcrt/fx_stream.h b/core/fxcrt/fx_stream.h +index ef586607b..e6f1f555f 100644 +--- a/core/fxcrt/fx_stream.h ++++ b/core/fxcrt/fx_stream.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,31 +7,23 @@ + #ifndef CORE_FXCRT_FX_STREAM_H_ + #define CORE_FXCRT_FX_STREAM_H_ + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" +-#include "core/fxcrt/retain_ptr.h" +-#include "third_party/base/compiler_specific.h" +- +-struct FX_FolderHandle; +- +-FX_FolderHandle* FX_OpenFolder(const char* path); +-bool FX_GetNextFile(FX_FolderHandle* handle, +- ByteString* filename, +- bool* bFolder); +-void FX_CloseFolder(FX_FolderHandle* handle); +- +-// Used with std::unique_ptr to automatically call FX_CloseFolder(). +-struct FxFolderHandleCloser { +- inline void operator()(FX_FolderHandle* h) const { FX_CloseFolder(h); } +-}; ++#include ++#include + +-#define FX_FILEMODE_ReadOnly 1 +-#define FX_FILEMODE_Truncate 2 ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/fx_types.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "third_party/base/span.h" + + class IFX_WriteStream { + public: +- virtual bool WriteBlock(const void* pData, size_t size) = 0; +- virtual bool WriteString(ByteStringView str) = 0; ++ // When `size` is 0, treat it as a no-op and return true. ++ virtual bool WriteBlock(pdfium::span data) = 0; ++ ++ bool WriteString(ByteStringView str); ++ bool WriteByte(uint8_t byte); ++ bool WriteDWord(uint32_t i); ++ bool WriteFilesize(FX_FILESIZE size); + + protected: + virtual ~IFX_WriteStream() = default; +@@ -39,8 +31,6 @@ class IFX_WriteStream { + + class IFX_ArchiveStream : public IFX_WriteStream { + public: +- virtual bool WriteByte(uint8_t byte) = 0; +- virtual bool WriteDWord(uint32_t i) = 0; + virtual FX_FILESIZE CurrentOffset() const = 0; + }; + +@@ -56,12 +46,11 @@ class IFX_SeekableWriteStream : virtual public IFX_StreamWithSize, + public IFX_RetainableWriteStream { + public: + // IFX_WriteStream: +- bool WriteBlock(const void* pData, size_t size) override; ++ bool WriteBlock(pdfium::span buffer) override; + + virtual bool Flush() = 0; +- virtual bool WriteBlockAtOffset(const void* pData, +- FX_FILESIZE offset, +- size_t size) = 0; ++ virtual bool WriteBlockAtOffset(pdfium::span data, ++ FX_FILESIZE offset) = 0; + }; + + class IFX_SeekableReadStream : virtual public Retainable, +@@ -72,26 +61,16 @@ class IFX_SeekableReadStream : virtual public Retainable, + + virtual bool IsEOF(); + virtual FX_FILESIZE GetPosition(); +- virtual size_t ReadBlock(void* buffer, size_t size); +- +- virtual bool ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) WARN_UNUSED_RESULT = 0; ++ [[nodiscard]] virtual size_t ReadBlock(pdfium::span buffer); ++ [[nodiscard]] virtual bool ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) = 0; + }; + + class IFX_SeekableStream : public IFX_SeekableReadStream, + public IFX_SeekableWriteStream { + public: +- static RetainPtr CreateFromFilename(const char* filename, +- uint32_t dwModes); +- +- static RetainPtr CreateFromFilename( +- const wchar_t* filename, +- uint32_t dwModes); +- + // IFX_SeekableWriteStream: +- bool WriteBlock(const void* buffer, size_t size) override; +- bool WriteString(ByteStringView str) override; ++ bool WriteBlock(pdfium::span buffer) override; + }; + + #endif // CORE_FXCRT_FX_STREAM_H_ +diff --git a/core/fxcrt/fx_string.cpp b/core/fxcrt/fx_string.cpp +index d4a6d382d..76773a81e 100644 +--- a/core/fxcrt/fx_string.cpp ++++ b/core/fxcrt/fx_string.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,13 +6,14 @@ + + #include "core/fxcrt/fx_string.h" + +-#include +-#include ++#include + + #include "core/fxcrt/cfx_utf8decoder.h" + #include "core/fxcrt/cfx_utf8encoder.h" + #include "core/fxcrt/fx_extension.h" ++#include "core/fxcrt/span_util.h" + #include "third_party/base/compiler_specific.h" ++#include "third_party/base/span.h" + + ByteString FX_UTF8Encode(WideStringView wsStr) { + CFX_UTF8Encoder encoder; +@@ -23,14 +24,8 @@ ByteString FX_UTF8Encode(WideStringView wsStr) { + } + + WideString FX_UTF8Decode(ByteStringView bsStr) { +- if (bsStr.IsEmpty()) +- return WideString(); +- +- CFX_UTF8Decoder decoder; +- for (size_t i = 0; i < bsStr.GetLength(); i++) +- decoder.Input(bsStr[i]); +- +- return WideString(decoder.GetResult()); ++ CFX_UTF8Decoder decoder(bsStr); ++ return decoder.TakeResult(); + } + + namespace { +@@ -45,15 +40,13 @@ const double kFractionScalesDouble[] = { + 0.0000001, 0.00000001, 0.000000001, 0.0000000001, 0.00000000001}; + + template +-T StringTo(ByteStringView strc, +- const T fractional_scales[], +- size_t fractional_scales_size) { ++T StringTo(ByteStringView strc, pdfium::span fractional_scales) { + if (strc.IsEmpty()) + return 0; + +- int cc = 0; + bool bNegative = false; +- int len = strc.GetLength(); ++ size_t cc = 0; ++ size_t len = strc.GetLength(); + if (strc[0] == '+') { + cc++; + } else if (strc[0] == '-') { +@@ -79,7 +72,7 @@ T StringTo(ByteStringView strc, + value += + fractional_scales[scale] * FXSYS_DecimalCharToInt(strc.CharAt(cc)); + scale++; +- if (scale == fractional_scales_size) ++ if (scale == fractional_scales.size()) + break; + cc++; + } +@@ -88,7 +81,7 @@ T StringTo(ByteStringView strc, + } + + template +-size_t ToString(T value, int (*round_func)(T), char* buf) { ++size_t ToString(T value, int (*round_func)(T), pdfium::span buf) { + buf[0] = '0'; + buf[1] = '\0'; + if (value == 0) { +@@ -119,7 +112,7 @@ size_t ToString(T value, int (*round_func)(T), char* buf) { + int i = scaled / scale; + FXSYS_itoa(i, buf2, 10); + size_t len = strlen(buf2); +- memcpy(buf + buf_size, buf2, len); ++ fxcrt::spancpy(buf.subspan(buf_size), pdfium::make_span(buf2).first(len)); + buf_size += len; + int fraction = scaled % scale; + if (fraction == 0) { +@@ -138,27 +131,34 @@ size_t ToString(T value, int (*round_func)(T), char* buf) { + } // namespace + + float StringToFloat(ByteStringView strc) { +- return StringTo(strc, kFractionScalesFloat, +- FX_ArraySize(kFractionScalesFloat)); ++ return StringTo(strc, kFractionScalesFloat); + } + + float StringToFloat(WideStringView wsStr) { +- return StringToFloat(FX_UTF8Encode(wsStr).c_str()); ++ return StringToFloat(FX_UTF8Encode(wsStr).AsStringView()); + } + +-size_t FloatToString(float f, char* buf) { ++size_t FloatToString(float f, pdfium::span buf) { + return ToString(f, FXSYS_roundf, buf); + } + + double StringToDouble(ByteStringView strc) { +- return StringTo(strc, kFractionScalesDouble, +- FX_ArraySize(kFractionScalesDouble)); ++ return StringTo(strc, kFractionScalesDouble); + } + + double StringToDouble(WideStringView wsStr) { +- return StringToDouble(FX_UTF8Encode(wsStr).c_str()); ++ return StringToDouble(FX_UTF8Encode(wsStr).AsStringView()); + } + +-size_t DoubleToString(double d, char* buf) { ++size_t DoubleToString(double d, pdfium::span buf) { + return ToString(d, FXSYS_round, buf); + } ++ ++namespace fxcrt { ++ ++template std::vector Split(const ByteString& that, ++ ByteString::CharType ch); ++template std::vector Split(const WideString& that, ++ WideString::CharType ch); ++ ++} // namespace fxcrt +diff --git a/core/fxcrt/fx_string.h b/core/fxcrt/fx_string.h +index e27f43af7..41d73e462 100644 +--- a/core/fxcrt/fx_string.h ++++ b/core/fxcrt/fx_string.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,21 +13,23 @@ + + #include "core/fxcrt/bytestring.h" + #include "core/fxcrt/widestring.h" ++#include "third_party/base/span.h" + +-#define FXBSTR_ID(c1, c2, c3, c4) \ +- (((uint32_t)c1 << 24) | ((uint32_t)c2 << 16) | ((uint32_t)c3 << 8) | \ +- ((uint32_t)c4)) ++constexpr uint32_t FXBSTR_ID(uint8_t c1, uint8_t c2, uint8_t c3, uint8_t c4) { ++ return static_cast(c1) << 24 | static_cast(c2) << 16 | ++ static_cast(c3) << 8 | static_cast(c4); ++} + + ByteString FX_UTF8Encode(WideStringView wsStr); + WideString FX_UTF8Decode(ByteStringView bsStr); + + float StringToFloat(ByteStringView str); + float StringToFloat(WideStringView wsStr); +-size_t FloatToString(float f, char* buf); ++size_t FloatToString(float f, pdfium::span buf); + + double StringToDouble(ByteStringView str); + double StringToDouble(WideStringView wsStr); +-size_t DoubleToString(double d, char* buf); ++size_t DoubleToString(double d, pdfium::span buf); + + namespace fxcrt { + +@@ -35,8 +37,8 @@ template + std::vector Split(const StrType& that, typename StrType::CharType ch) { + std::vector result; + StringViewTemplate remaining(that.span()); +- while (1) { +- Optional index = remaining.Find(ch); ++ while (true) { ++ absl::optional index = remaining.Find(ch); + if (!index.has_value()) + break; + result.emplace_back(remaining.First(index.value())); +@@ -46,6 +48,13 @@ std::vector Split(const StrType& that, typename StrType::CharType ch) { + return result; + } + ++extern template std::vector Split( ++ const ByteString& that, ++ ByteString::CharType ch); ++extern template std::vector Split( ++ const WideString& that, ++ WideString::CharType ch); ++ + } // namespace fxcrt + + #endif // CORE_FXCRT_FX_STRING_H_ +diff --git a/core/fxcrt/fx_string_unittest.cpp b/core/fxcrt/fx_string_unittest.cpp +index 9556360e2..261986471 100644 +--- a/core/fxcrt/fx_string_unittest.cpp ++++ b/core/fxcrt/fx_string_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,17 +6,18 @@ + + #include "core/fxcrt/fx_string.h" + #include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/base/span.h" + +-char* TerminatedFloatToString(float value, char* buf) { ++char* TerminatedFloatToString(float value, pdfium::span buf) { + size_t buflen = FloatToString(value, buf); + buf[buflen] = '\0'; +- return buf; ++ return buf.data(); + } + +-char* TerminatedDoubleToString(double value, char* buf) { ++char* TerminatedDoubleToString(double value, pdfium::span buf) { + size_t buflen = DoubleToString(value, buf); + buf[buflen] = '\0'; +- return buf; ++ return buf.data(); + } + + TEST(fxstring, FX_UTF8Encode) { +diff --git a/core/fxcrt/fx_string_wrappers.h b/core/fxcrt/fx_string_wrappers.h +new file mode 100644 +index 000000000..528236b31 +--- /dev/null ++++ b/core/fxcrt/fx_string_wrappers.h +@@ -0,0 +1,25 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXCRT_FX_STRING_WRAPPERS_H_ ++#define CORE_FXCRT_FX_STRING_WRAPPERS_H_ ++ ++#include ++#include ++ ++#include "core/fxcrt/fx_memory_wrappers.h" ++ ++namespace fxcrt { ++ ++// String that uses partition alloc for backing store. ++using string = ++ std::basic_string, FxStringAllocator>; ++ ++// String stream that uses PartitionAlloc for backing store. ++using ostringstream = std:: ++ basic_ostringstream, FxStringAllocator>; ++ ++} // namespace fxcrt ++ ++#endif // CORE_FXCRT_FX_STRING_WRAPPERS_H_ +diff --git a/core/fxcrt/fx_string_wrappers_unittest.cpp b/core/fxcrt/fx_string_wrappers_unittest.cpp +new file mode 100644 +index 000000000..a7854e9a2 +--- /dev/null ++++ b/core/fxcrt/fx_string_wrappers_unittest.cpp +@@ -0,0 +1,26 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/fx_string_wrappers.h" ++ ++#include "testing/gtest/include/gtest/gtest.h" ++ ++TEST(STLUtil, string) { ++ fxcrt::string str; ++ str += '2'; ++ str += '2'; ++ str += "C is "; ++ str += '7'; ++ str += '1'; ++ str += '.'; ++ str += '6'; ++ str += "F"; ++ EXPECT_STREQ("22C is 71.6F", str.c_str()); ++} ++ ++TEST(STLUtil, OStringStream) { ++ fxcrt::ostringstream str; ++ str << 22 << "C is " << 71.6f << "F"; ++ EXPECT_STREQ("22C is 71.6F", str.str().c_str()); ++} +diff --git a/core/fxcrt/fx_system.cpp b/core/fxcrt/fx_system.cpp +index 7358b75f9..0286b07fe 100644 +--- a/core/fxcrt/fx_system.cpp ++++ b/core/fxcrt/fx_system.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,8 @@ + + #include "core/fxcrt/fx_system.h" + +-#include ++#include ++ + #include + + #include "build/build_config.h" +@@ -14,7 +15,7 @@ + + namespace { + +-#if !defined(OS_WIN) ++#if !BUILDFLAG(IS_WIN) + uint32_t g_last_error = 0; + #endif + +@@ -89,7 +90,7 @@ STR_T FXSYS_IntToStr(T value, STR_T str, int radix) { + } // namespace + + int FXSYS_roundf(float f) { +- if (std::isnan(f)) ++ if (isnan(f)) + return 0; + if (f < static_cast(std::numeric_limits::min())) + return std::numeric_limits::min(); +@@ -99,7 +100,7 @@ int FXSYS_roundf(float f) { + } + + int FXSYS_round(double d) { +- if (std::isnan(d)) ++ if (isnan(d)) + return 0; + if (d < static_cast(std::numeric_limits::min())) + return std::numeric_limits::min(); +@@ -124,7 +125,7 @@ const char* FXSYS_i64toa(int64_t value, char* str, int radix) { + return FXSYS_IntToStr(value, str, radix); + } + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + + size_t FXSYS_wcsftime(wchar_t* strDest, + size_t maxsize, +@@ -144,11 +145,7 @@ size_t FXSYS_wcsftime(wchar_t* strDest, + return wcsftime(strDest, maxsize, format, timeptr); + } + +-#else // defined(OS_WIN) +- +-int FXSYS_GetACP() { +- return 0; +-} ++#else // BUILDFLAG(IS_WIN) + + char* FXSYS_strlwr(char* str) { + if (!str) { +@@ -223,40 +220,6 @@ char* FXSYS_itoa(int value, char* str, int radix) { + return FXSYS_IntToStr(value, str, radix); + } + +-int FXSYS_WideCharToMultiByte(uint32_t codepage, +- uint32_t dwFlags, +- const wchar_t* wstr, +- int wlen, +- char* buf, +- int buflen, +- const char* default_str, +- int* pUseDefault) { +- int len = 0; +- for (int i = 0; i < wlen; i++) { +- if (wstr[i] < 0x100) { +- if (buf && len < buflen) +- buf[len] = static_cast(wstr[i]); +- len++; +- } +- } +- return len; +-} +- +-int FXSYS_MultiByteToWideChar(uint32_t codepage, +- uint32_t dwFlags, +- const char* bstr, +- int blen, +- wchar_t* buf, +- int buflen) { +- int wlen = 0; +- for (int i = 0; i < blen; i++) { +- if (buf && wlen < buflen) +- buf[wlen] = reinterpret_cast(bstr)[i]; +- wlen++; +- } +- return wlen; +-} +- + void FXSYS_SetLastError(uint32_t err) { + g_last_error = err; + } +@@ -264,4 +227,8 @@ void FXSYS_SetLastError(uint32_t err) { + uint32_t FXSYS_GetLastError() { + return g_last_error; + } +-#endif // defined(OS_WIN) ++#endif // BUILDFLAG(IS_WIN) ++ ++float FXSYS_sqrt2(float a, float b) { ++ return sqrtf(a * a + b * b); ++} +diff --git a/core/fxcrt/fx_system.h b/core/fxcrt/fx_system.h +index 9963b90cb..52f9ce54d 100644 +--- a/core/fxcrt/fx_system.h ++++ b/core/fxcrt/fx_system.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,9 +7,6 @@ + #ifndef CORE_FXCRT_FX_SYSTEM_H_ + #define CORE_FXCRT_FX_SYSTEM_H_ + +-#include +-#include +-#include + #include + #include + #include +@@ -17,22 +14,8 @@ + #include + #include + +-// _FX_PLATFORM_ values; +-#define _FX_PLATFORM_WINDOWS_ 1 +-#define _FX_PLATFORM_LINUX_ 2 +-#define _FX_PLATFORM_APPLE_ 3 +- +-#if defined(_WIN32) +-#define _FX_PLATFORM_ _FX_PLATFORM_WINDOWS_ +-#elif defined(_WIN64) +-#define _FX_PLATFORM_ _FX_PLATFORM_WINDOWS_ +-#elif defined(__linux__) +-#define _FX_PLATFORM_ _FX_PLATFORM_LINUX_ +-#elif defined(__APPLE__) +-#define _FX_PLATFORM_ _FX_PLATFORM_APPLE_ +-#elif defined(__asmjs__) || defined(__wasm__) +-#define _FX_PLATFORM_ _FX_PLATFORM_LINUX_ +-#endif ++#include "build/build_config.h" ++#include "core/fxcrt/fx_types.h" + + #if defined(_MSC_VER) && _MSC_VER < 1900 + #error Sorry, VC++ 2015 or later is required to compile PDFium. +@@ -42,46 +25,24 @@ + #error Cannot compile v8 with wasm. + #endif // PDF_ENABLE_V8 + +-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_ ++#if BUILDFLAG(IS_WIN) + #include +-#include +-#endif // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_ +- +-#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_ +-#include +-#include +-#endif // _FX_PLATFORM_ == _FX_PLATFORM_APPLE_ ++#endif // BUILDFLAG(IS_WIN) + + #ifdef __cplusplus + extern "C" { + #endif // __cplusplus + +-#define IsFloatZero(f) ((f) < 0.0001 && (f) > -0.0001) +-#define IsFloatBigger(fa, fb) ((fa) > (fb) && !IsFloatZero((fa) - (fb))) +-#define IsFloatSmaller(fa, fb) ((fa) < (fb) && !IsFloatZero((fa) - (fb))) +-#define IsFloatEqual(fa, fb) IsFloatZero((fa) - (fb)) +- +-// PDFium file sizes match the platform, but PDFium itself does not support +-// files larger than 2GB even if the platform does. The value must be signed +-// to support -1 error returns. +-// TODO(tsepez): support larger files. +-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_ +-#define FX_FILESIZE int32_t +-#else // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_ +-#define FX_FILESIZE off_t +-#endif // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_ +- +-#ifndef ASSERT +-#ifndef NDEBUG +-#define ASSERT assert +-#else +-#define ASSERT(a) +-#endif // NDEBUG +-#endif // ASSERT ++#define FXSYS_IsFloatZero(f) ((f) < 0.0001 && (f) > -0.0001) ++#define FXSYS_IsFloatBigger(fa, fb) \ ++ ((fa) > (fb) && !FXSYS_IsFloatZero((fa) - (fb))) ++#define FXSYS_IsFloatSmaller(fa, fb) \ ++ ((fa) < (fb) && !FXSYS_IsFloatZero((fa) - (fb))) ++#define FXSYS_IsFloatEqual(fa, fb) FXSYS_IsFloatZero((fa) - (fb)) + + // M_PI not universally present on all platforms. +-#define FX_PI 3.1415926535897932384626433832795f +-#define FX_BEZIER 0.5522847498308f ++#define FXSYS_PI 3.1415926535897932384626433832795f ++#define FXSYS_BEZIER 0.5522847498308f + + // NOTE: prevent use of the return value from snprintf() since some platforms + // have different return values. +@@ -90,98 +51,33 @@ extern "C" { + #define FXSYS_sprintf DO_NOT_USE_SPRINTF_DIE_DIE_DIE + #define FXSYS_vsprintf DO_NOT_USE_VSPRINTF_DIE_DIE_DIE + +-#ifdef __cplusplus +-} // extern "C" +- +-#include "third_party/base/numerics/safe_conversions.h" +- +-// Overloaded functions for C++ templates +-inline size_t FXSYS_len(const char* ptr) { +- return strlen(ptr); +-} +- +-inline size_t FXSYS_len(const wchar_t* ptr) { +- return wcslen(ptr); +-} +- +-inline int FXSYS_cmp(const char* ptr1, const char* ptr2, size_t len) { +- return memcmp(ptr1, ptr2, len); +-} +- +-inline int FXSYS_cmp(const wchar_t* ptr1, const wchar_t* ptr2, size_t len) { +- return wmemcmp(ptr1, ptr2, len); +-} +- +-inline const char* FXSYS_chr(const char* ptr, char ch, size_t len) { +- return reinterpret_cast(memchr(ptr, ch, len)); +-} +- +-inline const wchar_t* FXSYS_chr(const wchar_t* ptr, wchar_t ch, size_t len) { +- return wmemchr(ptr, ch, len); +-} +- +-extern "C" { +-#endif // __cplusplus +- +-#if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_ +-#define FXSYS_GetACP GetACP ++#if BUILDFLAG(IS_WIN) + #define FXSYS_itoa _itoa +-#define FXSYS_WideCharToMultiByte WideCharToMultiByte +-#define FXSYS_MultiByteToWideChar MultiByteToWideChar + #define FXSYS_strlwr _strlwr + #define FXSYS_strupr _strupr + #define FXSYS_stricmp _stricmp + #define FXSYS_wcsicmp _wcsicmp + #define FXSYS_wcslwr _wcslwr + #define FXSYS_wcsupr _wcsupr +-#define FXSYS_pow(a, b) (float)powf(a, b) + size_t FXSYS_wcsftime(wchar_t* strDest, + size_t maxsize, + const wchar_t* format, + const struct tm* timeptr); + #define FXSYS_SetLastError SetLastError + #define FXSYS_GetLastError GetLastError +-#else // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_ +-int FXSYS_GetACP(); ++#else // BUILDFLAG(IS_WIN) + char* FXSYS_itoa(int value, char* str, int radix); +-int FXSYS_WideCharToMultiByte(uint32_t codepage, +- uint32_t dwFlags, +- const wchar_t* wstr, +- int wlen, +- char* buf, +- int buflen, +- const char* default_str, +- int* pUseDefault); +-int FXSYS_MultiByteToWideChar(uint32_t codepage, +- uint32_t dwFlags, +- const char* bstr, +- int blen, +- wchar_t* buf, +- int buflen); + char* FXSYS_strlwr(char* str); + char* FXSYS_strupr(char* str); + int FXSYS_stricmp(const char* str1, const char* str2); + int FXSYS_wcsicmp(const wchar_t* str1, const wchar_t* str2); + wchar_t* FXSYS_wcslwr(wchar_t* str); + wchar_t* FXSYS_wcsupr(wchar_t* str); +-#define FXSYS_pow(a, b) (float)pow(a, b) + #define FXSYS_wcsftime wcsftime + void FXSYS_SetLastError(uint32_t err); + uint32_t FXSYS_GetLastError(); +-#endif // _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_ +- +-#define FXWORD_GET_LSBFIRST(p) \ +- (static_cast((static_cast(p[1]) << 8) | \ +- (static_cast(p[0])))) +-#define FXWORD_GET_MSBFIRST(p) \ +- (static_cast((static_cast(p[0]) << 8) | \ +- (static_cast(p[1])))) +-#define FXDWORD_GET_LSBFIRST(p) \ +- ((static_cast(p[3]) << 24) | (static_cast(p[2]) << 16) | \ +- (static_cast(p[1]) << 8) | (static_cast(p[0]))) +-#define FXDWORD_GET_MSBFIRST(p) \ +- ((static_cast(p[0]) << 24) | (static_cast(p[1]) << 16) | \ +- (static_cast(p[2]) << 8) | (static_cast(p[3]))) ++#endif // BUILDFLAG(IS_WIN) ++ + int32_t FXSYS_atoi(const char* str); + uint32_t FXSYS_atoui(const char* str); + int32_t FXSYS_wtoi(const wchar_t* str); +@@ -189,38 +85,57 @@ int64_t FXSYS_atoi64(const char* str); + const char* FXSYS_i64toa(int64_t value, char* str, int radix); + int FXSYS_roundf(float f); + int FXSYS_round(double d); +-#define FXSYS_sqrt2(a, b) (float)sqrt((a) * (a) + (b) * (b)) ++float FXSYS_sqrt2(float a, float b); ++ + #ifdef __cplusplus +-} // extern C +-#endif // __cplusplus ++} // extern "C" + +-// To print a size_t value in a portable way: +-// size_t size; +-// printf("xyz: %" PRIuS, size); +-// The "u" in the macro corresponds to %u, and S is for "size". +-#if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_ ++// C++-only section + +-#if (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64) +-#error "inttypes.h has already been included before this header file, but " +-#error "without __STDC_FORMAT_MACROS defined." +-#endif ++// Overloaded functions for C++ templates ++inline size_t FXSYS_len(const char* ptr) { ++ return strlen(ptr); ++} + +-#if !defined(__STDC_FORMAT_MACROS) +-#define __STDC_FORMAT_MACROS +-#endif ++inline size_t FXSYS_len(const wchar_t* ptr) { ++ return wcslen(ptr); ++} + +-#include ++inline int FXSYS_cmp(const char* ptr1, const char* ptr2, size_t len) { ++ return memcmp(ptr1, ptr2, len); ++} + +-#if !defined(PRIuS) +-#define PRIuS "zu" +-#endif ++inline int FXSYS_cmp(const wchar_t* ptr1, const wchar_t* ptr2, size_t len) { ++ return wmemcmp(ptr1, ptr2, len); ++} + +-#else // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_ ++inline const char* FXSYS_chr(const char* ptr, char ch, size_t len) { ++ return reinterpret_cast(memchr(ptr, ch, len)); ++} + +-#if !defined(PRIuS) +-#define PRIuS "Iu" +-#endif ++inline const wchar_t* FXSYS_chr(const wchar_t* ptr, wchar_t ch, size_t len) { ++ return wmemchr(ptr, ch, len); ++} + +-#endif // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_ ++// Could be C, but uses C++-style casting. ++#define FXSYS_UINT16_GET_LSBFIRST(p) \ ++ (static_cast( \ ++ (static_cast(static_cast((p)[1])) << 8) | \ ++ (static_cast(static_cast((p)[0]))))) ++#define FXSYS_UINT16_GET_MSBFIRST(p) \ ++ (static_cast( \ ++ (static_cast(static_cast((p)[0])) << 8) | \ ++ (static_cast(static_cast((p)[1]))))) ++#define FXSYS_UINT32_GET_LSBFIRST(p) \ ++ ((static_cast(static_cast((p)[3])) << 24) | \ ++ (static_cast(static_cast((p)[2])) << 16) | \ ++ (static_cast(static_cast((p)[1])) << 8) | \ ++ (static_cast(static_cast((p)[0])))) ++#define FXSYS_UINT32_GET_MSBFIRST(p) \ ++ ((static_cast(static_cast((p)[0])) << 24) | \ ++ (static_cast(static_cast((p)[1])) << 16) | \ ++ (static_cast(static_cast((p)[2])) << 8) | \ ++ (static_cast(static_cast((p)[3])))) ++#endif // __cplusplus + + #endif // CORE_FXCRT_FX_SYSTEM_H_ +diff --git a/core/fxcrt/fx_system_unittest.cpp b/core/fxcrt/fx_system_unittest.cpp +index bc5dbbacd..63f1021fc 100644 +--- a/core/fxcrt/fx_system_unittest.cpp ++++ b/core/fxcrt/fx_system_unittest.cpp +@@ -1,7 +1,10 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include ++ ++#include + #include + + #include "build/build_config.h" +@@ -12,7 +15,7 @@ + // Unit test covering cases where PDFium replaces well-known library + // functionality on any given platformn. + +-#if !defined(OS_WIN) ++#if !BUILDFLAG(IS_WIN) + + namespace { + +@@ -260,7 +263,7 @@ TEST(fxcrt, FXSYS_i64toa) { + "111111111111111111111111111111111111111111111111111111111111111"); + } + +-#endif // !defined(OS_WIN) ++#endif // !BUILDFLAG(IS_WIN) + + TEST(fxcrt, FXSYS_wcsftime) { + struct tm good_time = {}; +@@ -272,7 +275,7 @@ TEST(fxcrt, FXSYS_wcsftime) { + good_time.tm_sec = 59; + + wchar_t buf[100] = {}; +- EXPECT_EQ(19u, FXSYS_wcsftime(buf, FX_ArraySize(buf), L"%Y-%m-%dT%H:%M:%S", ++ EXPECT_EQ(19u, FXSYS_wcsftime(buf, std::size(buf), L"%Y-%m-%dT%H:%M:%S", + &good_time)); + EXPECT_STREQ(L"1974-08-09T11:59:59", buf); + +@@ -287,7 +290,7 @@ TEST(fxcrt, FXSYS_wcsftime) { + for (int year = -2500; year <= 8500; ++year) { + year_time.tm_year = year; + wchar_t year_buf[100] = {}; +- FXSYS_wcsftime(year_buf, FX_ArraySize(year_buf), L"%Y-%m-%dT%H:%M:%S", ++ FXSYS_wcsftime(year_buf, std::size(year_buf), L"%Y-%m-%dT%H:%M:%S", + &year_time); + } + +@@ -300,7 +303,7 @@ TEST(fxcrt, FXSYS_wcsftime) { + bad_time.tm_min = -1; + bad_time.tm_sec = -1; + +- FXSYS_wcsftime(buf, FX_ArraySize(buf), L"%y-%m-%dT%H:%M:%S", &bad_time); ++ FXSYS_wcsftime(buf, std::size(buf), L"%y-%m-%dT%H:%M:%S", &bad_time); + + // Ensure wcsftime handles bad-ish day without crashing (Feb 30). + struct tm feb_time = {}; +@@ -311,7 +314,7 @@ TEST(fxcrt, FXSYS_wcsftime) { + feb_time.tm_min = 00; + feb_time.tm_sec = 00; + +- FXSYS_wcsftime(buf, FX_ArraySize(buf), L"%y-%m-%dT%H:%M:%S", &feb_time); ++ FXSYS_wcsftime(buf, std::size(buf), L"%y-%m-%dT%H:%M:%S", &feb_time); + } + + TEST(fxcrt, FXSYS_atoi) { +diff --git a/core/fxcrt/fx_types.h b/core/fxcrt/fx_types.h +new file mode 100644 +index 000000000..9838773fc +--- /dev/null ++++ b/core/fxcrt/fx_types.h +@@ -0,0 +1,22 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCRT_FX_TYPES_H_ ++#define CORE_FXCRT_FX_TYPES_H_ ++ ++#include "build/build_config.h" ++ ++// PDFium file sizes match the platform. The value must be signed to support -1 ++// error returns. ++#if BUILDFLAG(IS_WIN) ++#include ++#define FX_FILESIZE int64_t ++#else ++#include // For off_t. ++#define FX_FILESIZE off_t ++#endif // BUILDFLAG(IS_WIN) ++ ++#endif // CORE_FXCRT_FX_TYPES_H_ +diff --git a/core/fxcrt/fx_unicode.cpp b/core/fxcrt/fx_unicode.cpp +index cbd4f0de0..e3fb97b61 100644 +--- a/core/fxcrt/fx_unicode.cpp ++++ b/core/fxcrt/fx_unicode.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,11 @@ + + #include "core/fxcrt/fx_unicode.h" + +-#include "core/fxcrt/fx_memory.h" ++#include ++ ++#include ++ ++#include "third_party/base/check.h" + + namespace { + +@@ -25,12 +29,12 @@ constexpr uint16_t kMirrorMax = (1 << kMirrorBitCount) - 1; + ((mirror << kMirrorBitPos) | \ + (static_cast(FX_BIDICLASS::bd) << kBidiClassBitPos)), + constexpr uint16_t kTextLayoutCodeProperties[] = { +-#include "core/fxcrt/fx_ucddata.inc" ++#include "core/fxcrt/fx_ucddata.inc" // NOLINT(build/include) + }; + #undef CHARPROP____ + + constexpr size_t kTextLayoutCodePropertiesSize = +- FX_ArraySize(kTextLayoutCodeProperties); ++ std::size(kTextLayoutCodeProperties); + + static_assert(kTextLayoutCodePropertiesSize == 65536, "missing characters"); + +@@ -58,12 +62,12 @@ constexpr uint16_t kCharTypeBitMask = + ((static_cast(FX_CHARTYPE::ct) << kCharTypeBitPos) | \ + (static_cast(FX_BREAKPROPERTY::bt) << kBreakTypeBitPos)), + constexpr uint16_t kExtendedTextLayoutCodeProperties[] = { +-#include "core/fxcrt/fx_ucddata.inc" ++#include "core/fxcrt/fx_ucddata.inc" // NOLINT(build/include) + }; + #undef CHARPROP____ + + constexpr size_t kExtendedTextLayoutCodePropertiesSize = +- FX_ArraySize(kExtendedTextLayoutCodeProperties); ++ std::size(kExtendedTextLayoutCodeProperties); + + static_assert(kExtendedTextLayoutCodePropertiesSize == 65536, + "missing characters"); +@@ -122,46 +126,52 @@ constexpr uint16_t kFXTextLayoutBidiMirror[] = { + }; + + constexpr size_t kFXTextLayoutBidiMirrorSize = +- FX_ArraySize(kFXTextLayoutBidiMirror); ++ std::size(kFXTextLayoutBidiMirror); + + // Check that the mirror indicies in the fx_ucddata.inc table are in bounds. + #undef CHARPROP____ + #define CHARPROP____(mirror, ct, bd, bt) \ + static_assert(mirror == kMirrorMax || mirror < kFXTextLayoutBidiMirrorSize, \ + "Bad mirror index"); +-#include "core/fxcrt/fx_ucddata.inc" ++#include "core/fxcrt/fx_ucddata.inc" // NOLINT(build/include) + #undef CHARPROP____ + + } // namespace + +-wchar_t FX_GetMirrorChar(wchar_t wch) { ++namespace pdfium { ++namespace unicode { ++ ++wchar_t GetMirrorChar(wchar_t wch) { + uint16_t prop = GetUnicodeProperties(wch); + size_t idx = prop >> kMirrorBitPos; + if (idx == kMirrorMax) + return wch; +- ASSERT(idx < kFXTextLayoutBidiMirrorSize); ++ DCHECK(idx < kFXTextLayoutBidiMirrorSize); + return kFXTextLayoutBidiMirror[idx]; + } + +-FX_BIDICLASS FX_GetBidiClass(wchar_t wch) { ++FX_BIDICLASS GetBidiClass(wchar_t wch) { + uint16_t prop = GetUnicodeProperties(wch); + uint16_t result = (prop & kBidiClassBitMask) >> kBidiClassBitPos; +- ASSERT(result <= static_cast(FX_BIDICLASS::kPDF)); ++ DCHECK(result <= static_cast(FX_BIDICLASS::kPDF)); + return static_cast(result); + } + + #ifdef PDF_ENABLE_XFA +-FX_CHARTYPE FX_GetCharType(wchar_t wch) { ++FX_CHARTYPE GetCharType(wchar_t wch) { + uint16_t prop = GetExtendedUnicodeProperties(wch); + uint16_t result = (prop & kCharTypeBitMask) >> kCharTypeBitPos; +- ASSERT(result <= static_cast(FX_CHARTYPE::kArabic)); ++ DCHECK(result <= static_cast(FX_CHARTYPE::kArabic)); + return static_cast(result); + } + +-FX_BREAKPROPERTY FX_GetBreakProperty(wchar_t wch) { ++FX_BREAKPROPERTY GetBreakProperty(wchar_t wch) { + uint16_t prop = GetExtendedUnicodeProperties(wch); + uint16_t result = (prop & kBreakTypeBitMask) >> kBreakTypeBitPos; +- ASSERT(result <= static_cast(FX_BREAKPROPERTY::kTB)); ++ DCHECK(result <= static_cast(FX_BREAKPROPERTY::kTB)); + return static_cast(result); + } + #endif // PDF_ENABLE_XFA ++ ++} // namespace unicode ++} // namespace pdfium +diff --git a/core/fxcrt/fx_unicode.h b/core/fxcrt/fx_unicode.h +index a88930495..5ad5ce04f 100644 +--- a/core/fxcrt/fx_unicode.h ++++ b/core/fxcrt/fx_unicode.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,7 @@ + #ifndef CORE_FXCRT_FX_UNICODE_H_ + #define CORE_FXCRT_FX_UNICODE_H_ + +-#include "core/fxcrt/fx_system.h" ++#include + + // NOTE: Order matters, less-than/greater-than comparisons are used. + enum class FX_BIDICLASS : uint8_t { +@@ -33,9 +33,6 @@ enum class FX_BIDICLASS : uint8_t { + kN = kON, + }; + +-wchar_t FX_GetMirrorChar(wchar_t wch); +-FX_BIDICLASS FX_GetBidiClass(wchar_t wch); +- + #ifdef PDF_ENABLE_XFA + // As defined in http://www.unicode.org/reports/tr14 + enum class FX_BREAKPROPERTY : uint8_t { +@@ -94,12 +91,29 @@ enum class FX_CHARTYPE : uint8_t { + kArabicForm, + kArabic, + }; ++#endif // PDF_ENABLE_XFA ++ ++namespace pdfium { ++namespace unicode { ++ ++constexpr wchar_t kRightSingleQuotationMark = 0x2019; ++constexpr wchar_t kLineSeparator = 0x2028; ++constexpr wchar_t kParagraphSeparator = 0x2029; ++constexpr wchar_t kBoxDrawingsLightVerical = 0x2502; ++constexpr wchar_t kZeroWidthNoBreakSpace = 0xfeff; + +-FX_CHARTYPE FX_GetCharType(wchar_t wch); ++wchar_t GetMirrorChar(wchar_t wch); ++FX_BIDICLASS GetBidiClass(wchar_t wch); ++ ++#ifdef PDF_ENABLE_XFA ++FX_CHARTYPE GetCharType(wchar_t wch); + + // Analagous to ULineBreak in icu's uchar.h, but permuted order, and a + // subset lacking some more recent additions. +-FX_BREAKPROPERTY FX_GetBreakProperty(wchar_t wch); ++FX_BREAKPROPERTY GetBreakProperty(wchar_t wch); + #endif // PDF_ENABLE_XFA + ++} // namespace unicode ++} // namespace pdfium ++ + #endif // CORE_FXCRT_FX_UNICODE_H_ +diff --git a/core/fxcrt/mask.h b/core/fxcrt/mask.h +new file mode 100644 +index 000000000..67e7a3dbc +--- /dev/null ++++ b/core/fxcrt/mask.h +@@ -0,0 +1,118 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXCRT_MASK_H_ ++#define CORE_FXCRT_MASK_H_ ++ ++#include ++ ++namespace fxcrt { ++ ++// Provides extremely strict type-checking on masks of enum class bitflags, ++// for code where flags may not be passed consistently. ++template ++class Mask { ++ public: ++ using UnderlyingType = typename std::underlying_type::type; ++ ++ // Escape hatch for when value comes aross an API, say. ++ static Mask FromUnderlyingUnchecked(UnderlyingType val) { return Mask(val); } ++ ++ constexpr Mask() = default; ++ constexpr Mask(const Mask& that) = default; ++ ++ // NOLINTNEXTLINE(runtime/explicit) ++ constexpr Mask(E val) : val_(static_cast(val)) {} ++ ++ // Unfortunately, std::initializer_list<> can't be used in constexpr ++ // methods per C++ standards, and we need constexpr for a zero-cost ++ // abstraction. Hence, expand out constructors of various arity. ++ constexpr Mask(E v1, E v2) ++ : val_(static_cast(v1) | ++ static_cast(v2)) {} ++ ++ constexpr Mask(E v1, E v2, E v3) ++ : val_(static_cast(v1) | static_cast(v2) | ++ static_cast(v3)) {} ++ ++ constexpr Mask(E v1, E v2, E v3, E v4) ++ : val_(static_cast(v1) | static_cast(v2) | ++ static_cast(v3) | ++ static_cast(v4)) {} ++ ++ constexpr Mask(E v1, E v2, E v3, E v4, E v5) ++ : val_(static_cast(v1) | static_cast(v2) | ++ static_cast(v3) | static_cast(v4) | ++ static_cast(v5)) {} ++ ++ constexpr Mask(E v1, E v2, E v3, E v4, E v5, E v6) ++ : val_(static_cast(v1) | static_cast(v2) | ++ static_cast(v3) | static_cast(v4) | ++ static_cast(v5) | ++ static_cast(v6)) {} ++ ++ constexpr Mask(E v1, E v2, E v3, E v4, E v5, E v6, E v7) ++ : val_(static_cast(v1) | static_cast(v2) | ++ static_cast(v3) | static_cast(v4) | ++ static_cast(v5) | static_cast(v6) | ++ static_cast(v7)) {} ++ ++ constexpr Mask(E v1, E v2, E v3, E v4, E v5, E v6, E v7, E v8) ++ : val_(static_cast(v1) | static_cast(v2) | ++ static_cast(v3) | static_cast(v4) | ++ static_cast(v5) | static_cast(v6) | ++ static_cast(v7) | ++ static_cast(v8)) {} ++ ++ explicit operator bool() const { return !!val_; } ++ Mask operator~() const { return Mask(~val_); } ++ constexpr Mask operator|(const Mask& that) const { ++ return Mask(val_ | that.val_); ++ } ++ constexpr Mask operator&(const Mask& that) const { ++ return Mask(val_ & that.val_); ++ } ++ constexpr Mask operator^(const Mask& that) const { ++ return Mask(val_ ^ that.val_); ++ } ++ Mask& operator=(const Mask& that) { ++ val_ = that.val_; ++ return *this; ++ } ++ Mask& operator|=(const Mask& that) { ++ val_ |= that.val_; ++ return *this; ++ } ++ Mask& operator&=(const Mask& that) { ++ val_ &= that.val_; ++ return *this; ++ } ++ Mask& operator^=(const Mask& that) { ++ val_ ^= that.val_; ++ return *this; ++ } ++ bool operator==(const Mask& that) const { return val_ == that.val_; } ++ bool operator!=(const Mask& that) const { return val_ != that.val_; } ++ ++ bool TestAll(const Mask& that) const { ++ return (val_ & that.val_) == that.val_; ++ } ++ ++ // Because ~ can't be applied to enum class without casting. ++ void Clear(const Mask& that) { val_ &= ~that.val_; } ++ ++ // Escape hatch, usage should be minimized. ++ UnderlyingType UncheckedValue() const { return val_; } ++ ++ private: ++ explicit constexpr Mask(UnderlyingType val) : val_(val) {} ++ ++ UnderlyingType val_ = 0; ++}; ++ ++} // namespace fxcrt ++ ++using fxcrt::Mask; ++ ++#endif // CORE_FXCRT_MASK_H_ +diff --git a/core/fxcrt/mask_unittest.cpp b/core/fxcrt/mask_unittest.cpp +new file mode 100644 +index 000000000..be4e4928f +--- /dev/null ++++ b/core/fxcrt/mask_unittest.cpp +@@ -0,0 +1,170 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/mask.h" ++ ++#include "testing/gtest/include/gtest/gtest.h" ++ ++namespace fxcrt { ++namespace { ++ ++enum class Privilege : uint8_t { ++ kPriv1 = 1 << 0, ++ kPriv2 = 1 << 1, ++ kPriv4 = 1 << 2, ++ kPriv8 = 1 << 3, ++ kPriv16 = 1 << 4, ++ kPriv32 = 1 << 5, ++ kPriv64 = 1 << 6, ++ kPriv128 = 1 << 7, ++}; ++ ++constexpr Mask kAllMask = { ++ Privilege::kPriv1, Privilege::kPriv2, Privilege::kPriv4, ++ Privilege::kPriv8, Privilege::kPriv16, Privilege::kPriv32, ++ Privilege::kPriv64, Privilege::kPriv128, ++}; ++ ++} // namespace ++ ++static_assert(sizeof(Mask) == sizeof(Privilege), ++ "Mask size must be the same as enum"); ++ ++TEST(Mask, Empty) { ++ constexpr Mask privs; ++ EXPECT_EQ(0u, privs.UncheckedValue()); ++ EXPECT_FALSE(privs & Privilege::kPriv1); ++ EXPECT_FALSE(privs & Privilege::kPriv4); ++ EXPECT_FALSE(privs & Privilege::kPriv8); ++ EXPECT_FALSE(privs & kAllMask); ++} ++ ++TEST(Mask, FromOne) { ++ Mask privs = Privilege::kPriv1; ++ EXPECT_EQ(1u, privs.UncheckedValue()); ++ EXPECT_TRUE(privs & Privilege::kPriv1); ++ EXPECT_FALSE(privs & Privilege::kPriv4); ++ EXPECT_FALSE(privs & Privilege::kPriv8); ++ EXPECT_TRUE(privs & kAllMask); ++} ++ ++TEST(Mask, FromTwo) { ++ // Not adjacent bits, just because. ++ Mask privs = {Privilege::kPriv1, Privilege::kPriv8}; ++ EXPECT_EQ(9u, privs.UncheckedValue()); ++ EXPECT_TRUE(privs & Privilege::kPriv1); ++ EXPECT_FALSE(privs & Privilege::kPriv4); ++ EXPECT_TRUE(privs & Privilege::kPriv8); ++ EXPECT_TRUE(privs & kAllMask); ++} ++ ++TEST(Mask, FromThree) { ++ Mask privs = { ++ Privilege::kPriv1, ++ Privilege::kPriv2, ++ Privilege::kPriv4, ++ }; ++ EXPECT_EQ(7u, privs.UncheckedValue()); ++} ++ ++TEST(Mask, FromFour) { ++ Mask privs = { ++ Privilege::kPriv1, ++ Privilege::kPriv2, ++ Privilege::kPriv4, ++ Privilege::kPriv8, ++ }; ++ EXPECT_EQ(15u, privs.UncheckedValue()); ++} ++ ++TEST(Mask, FromFive) { ++ Mask privs = { ++ Privilege::kPriv1, Privilege::kPriv2, Privilege::kPriv4, ++ Privilege::kPriv8, Privilege::kPriv16, ++ }; ++ EXPECT_EQ(31u, privs.UncheckedValue()); ++} ++ ++TEST(Mask, FromSix) { ++ Mask privs = { ++ Privilege::kPriv1, Privilege::kPriv2, Privilege::kPriv4, ++ Privilege::kPriv8, Privilege::kPriv16, Privilege::kPriv32, ++ }; ++ EXPECT_EQ(63u, privs.UncheckedValue()); ++} ++ ++TEST(Mask, FromSeven) { ++ Mask privs = { ++ Privilege::kPriv1, Privilege::kPriv2, Privilege::kPriv4, ++ Privilege::kPriv8, Privilege::kPriv16, Privilege::kPriv32, ++ Privilege::kPriv64, ++ }; ++ EXPECT_EQ(127u, privs.UncheckedValue()); ++} ++ ++TEST(Mask, FromEight) { ++ Mask privs = { ++ Privilege::kPriv1, Privilege::kPriv2, Privilege::kPriv4, ++ Privilege::kPriv8, Privilege::kPriv16, Privilege::kPriv32, ++ Privilege::kPriv64, Privilege::kPriv128, ++ }; ++ EXPECT_EQ(255u, privs.UncheckedValue()); ++} ++ ++TEST(Mask, FromUnderlying) { ++ auto privs = Mask::FromUnderlyingUnchecked(5); ++ EXPECT_EQ(5u, privs.UncheckedValue()); ++ EXPECT_TRUE(privs & Privilege::kPriv1); ++ EXPECT_TRUE(privs & Privilege::kPriv4); ++ EXPECT_FALSE(privs & Privilege::kPriv8); ++} ++ ++TEST(Mask, AssignAndEQ) { ++ Mask source = {Privilege::kPriv1, Privilege::kPriv8}; ++ Mask other = Privilege::kPriv1; ++ Mask dest; ++ dest = source; ++ EXPECT_EQ(9u, dest.UncheckedValue()); ++ EXPECT_EQ(source, dest); ++ EXPECT_NE(other, dest); ++} ++ ++TEST(Mask, OrAndAnd) { ++ Mask source = {Privilege::kPriv1, Privilege::kPriv8}; ++ Mask or_result = ++ source | Mask{Privilege::kPriv1, Privilege::kPriv4}; ++ Mask and_result = ++ source & Mask{Privilege::kPriv1, Privilege::kPriv4}; ++ EXPECT_EQ(13u, or_result.UncheckedValue()); ++ EXPECT_EQ(1u, and_result.UncheckedValue()); ++} ++ ++TEST(Mask, OrEqualsAndAndEquals) { ++ Mask source_or = {Privilege::kPriv1, Privilege::kPriv8}; ++ Mask source_and = {Privilege::kPriv1, Privilege::kPriv8}; ++ source_or |= {Privilege::kPriv1, Privilege::kPriv4}; ++ source_and &= {Privilege::kPriv1, Privilege::kPriv4}; ++ EXPECT_EQ(13u, source_or.UncheckedValue()); ++ EXPECT_EQ(1u, source_and.UncheckedValue()); ++} ++ ++TEST(Mask, Clear) { ++ Mask source = kAllMask; ++ source.Clear({Privilege::kPriv1, Privilege::kPriv4}); ++ EXPECT_EQ(250u, source.UncheckedValue()); ++} ++ ++TEST(Mask, TestAll) { ++ Mask source = { ++ Privilege::kPriv1, ++ Privilege::kPriv8, ++ Privilege::kPriv64, ++ }; ++ Mask passes = {Privilege::kPriv1, Privilege::kPriv64}; ++ Mask fails = {Privilege::kPriv1, Privilege::kPriv32}; ++ EXPECT_TRUE(source.TestAll(passes)); ++ EXPECT_FALSE(source.TestAll(fails)); ++} ++ ++} // namespace fxcrt +diff --git a/core/fxcrt/maybe_owned.h b/core/fxcrt/maybe_owned.h +index bbdcd4047..c599d3925 100644 +--- a/core/fxcrt/maybe_owned.h ++++ b/core/fxcrt/maybe_owned.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,8 +8,8 @@ + #include + #include + +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "third_party/abseil-cpp/absl/types/variant.h" + + namespace fxcrt { + +@@ -20,87 +20,83 @@ namespace fxcrt { + template > + class MaybeOwned { + public: ++ using OwnedType = std::unique_ptr; ++ using UnownedType = UnownedPtr; ++ + MaybeOwned() = default; +- explicit MaybeOwned(T* ptr) : m_pObj(ptr) {} +- explicit MaybeOwned(const UnownedPtr& ptr) : m_pObj(ptr.Get()) {} +- explicit MaybeOwned(std::unique_ptr ptr) +- : m_pOwnedObj(std::move(ptr)), m_pObj(m_pOwnedObj.get()) {} ++ explicit MaybeOwned(T* ptr) : ptr_(UnownedType(ptr)) {} ++ explicit MaybeOwned(const UnownedType& ptr) : ptr_(ptr) {} ++ explicit MaybeOwned(OwnedType ptr) : ptr_(std::move(ptr)) {} + + MaybeOwned(const MaybeOwned& that) = delete; +- MaybeOwned(MaybeOwned&& that) noexcept +- : m_pOwnedObj(that.m_pOwnedObj.release()), m_pObj(that.m_pObj) { +- that.m_pObj = nullptr; +- } ++ MaybeOwned(MaybeOwned&& that) noexcept = default; ++ ++ MaybeOwned& operator=(const MaybeOwned& that) = delete; ++ MaybeOwned& operator=(MaybeOwned&& that) noexcept = default; ++ ++ ~MaybeOwned() = default; ++ ++ void Reset(T* ptr = nullptr) { ptr_ = UnownedType(ptr); } ++ void Reset(OwnedType ptr) { ptr_ = std::move(ptr); } ++ ++ bool IsOwned() const { return absl::holds_alternative(ptr_); } + +- void Reset(std::unique_ptr ptr) { +- m_pObj = ptr.get(); +- m_pOwnedObj = std::move(ptr); +- } +- void Reset(T* ptr = nullptr) { +- m_pObj = ptr; +- m_pOwnedObj.reset(); +- } + // Helpful for untangling a collection of intertwined MaybeOwned<>. + void ResetIfUnowned() { + if (!IsOwned()) + Reset(); + } + +- T* Get() const { return m_pObj.Get(); } +- bool IsOwned() const { return !!m_pOwnedObj; } ++ T* Get() const& { ++ return absl::visit([](const auto& obj) { return obj.get(); }, ptr_); ++ } ++ T* Get() && { ++ auto local_variable_preventing_move_elision = std::move(ptr_); ++ return absl::visit([](const auto& obj) { return obj.get(); }, ++ local_variable_preventing_move_elision); ++ } + + // Downgrades to unowned, caller takes ownership. +- std::unique_ptr Release() { +- ASSERT(IsOwned()); +- return std::move(m_pOwnedObj); ++ OwnedType Release() { ++ auto result = std::move(absl::get(ptr_)); ++ ptr_ = UnownedType(result.get()); ++ return result; + } + + // Downgrades to empty, caller takes ownership. +- std::unique_ptr ReleaseAndClear() { +- ASSERT(IsOwned()); +- m_pObj = nullptr; +- return std::move(m_pOwnedObj); ++ OwnedType ReleaseAndClear() { ++ auto result = std::move(absl::get(ptr_)); ++ ptr_ = UnownedType(); ++ return result; + } + +- MaybeOwned& operator=(const MaybeOwned& that) = delete; +- MaybeOwned& operator=(MaybeOwned&& that) { +- m_pObj = that.m_pObj; +- m_pOwnedObj = std::move(that.m_pOwnedObj); +- that.m_pObj = nullptr; +- return *this; +- } + MaybeOwned& operator=(T* ptr) { + Reset(ptr); + return *this; + } +- MaybeOwned& operator=(const UnownedPtr& ptr) { +- Reset(ptr.Get()); ++ MaybeOwned& operator=(const UnownedType& ptr) { ++ Reset(ptr); + return *this; + } +- MaybeOwned& operator=(std::unique_ptr ptr) { ++ MaybeOwned& operator=(OwnedType ptr) { + Reset(std::move(ptr)); + return *this; + } + + bool operator==(const MaybeOwned& that) const { return Get() == that.Get(); } +- bool operator==(const std::unique_ptr& ptr) const { +- return Get() == ptr.get(); +- } ++ bool operator==(const OwnedType& ptr) const { return Get() == ptr.get(); } + bool operator==(T* ptr) const { return Get() == ptr; } + + bool operator!=(const MaybeOwned& that) const { return !(*this == that); } +- bool operator!=(const std::unique_ptr ptr) const { +- return !(*this == ptr); +- } ++ bool operator!=(const OwnedType ptr) const { return !(*this == ptr); } + bool operator!=(T* ptr) const { return !(*this == ptr); } + +- explicit operator bool() const { return !!m_pObj; } +- T& operator*() const { return *m_pObj; } +- T* operator->() const { return m_pObj.Get(); } ++ explicit operator bool() const { return !!Get(); } ++ T& operator*() const { return *Get(); } ++ T* operator->() const { return Get(); } + + private: +- std::unique_ptr m_pOwnedObj; +- UnownedPtr m_pObj; ++ absl::variant ptr_; + }; + + } // namespace fxcrt +diff --git a/core/fxcrt/maybe_owned_unittest.cpp b/core/fxcrt/maybe_owned_unittest.cpp +index 53cab6ca6..1785cf1c9 100644 +--- a/core/fxcrt/maybe_owned_unittest.cpp ++++ b/core/fxcrt/maybe_owned_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,7 +9,6 @@ + + #include "core/fxcrt/unowned_ptr.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + + namespace fxcrt { + namespace { +@@ -32,7 +31,7 @@ TEST(MaybeOwned, Null) { + MaybeOwned ptr1; + EXPECT_FALSE(ptr1.IsOwned()); + EXPECT_FALSE(ptr1); +- EXPECT_EQ(nullptr, ptr1.Get()); ++ EXPECT_FALSE(ptr1.Get()); + + MaybeOwned ptr2; + EXPECT_TRUE(ptr1 == ptr2); +@@ -72,7 +71,7 @@ TEST(MaybeOwned, NotOwned) { + { + MaybeOwned ptr(&thing1); + EXPECT_EQ(100, ptr->GetID()); +- ptr = pdfium::MakeUnique(300, &owned_delete_count); ++ ptr = std::make_unique(300, &owned_delete_count); + EXPECT_TRUE(ptr.IsOwned()); + EXPECT_EQ(300, ptr->GetID()); + } +@@ -99,7 +98,7 @@ TEST(MaybeOwned, Owned) { + int delete_count = 0; + { + MaybeOwned ptr( +- pdfium::MakeUnique(100, &delete_count)); ++ std::make_unique(100, &delete_count)); + EXPECT_TRUE(ptr.IsOwned()); + EXPECT_EQ(100, ptr->GetID()); + +@@ -112,8 +111,8 @@ TEST(MaybeOwned, Owned) { + delete_count = 0; + { + MaybeOwned ptr( +- pdfium::MakeUnique(200, &delete_count)); +- ptr = pdfium::MakeUnique(300, &delete_count); ++ std::make_unique(200, &delete_count)); ++ ptr = std::make_unique(300, &delete_count); + EXPECT_TRUE(ptr.IsOwned()); + EXPECT_EQ(300, ptr->GetID()); + EXPECT_EQ(1, delete_count); +@@ -125,7 +124,7 @@ TEST(MaybeOwned, Owned) { + PseudoDeletable thing2(400, &unowned_delete_count); + { + MaybeOwned ptr( +- pdfium::MakeUnique(500, &delete_count)); ++ std::make_unique(500, &delete_count)); + ptr = &thing2; + EXPECT_FALSE(ptr.IsOwned()); + EXPECT_EQ(400, ptr->GetID()); +@@ -142,7 +141,7 @@ TEST(MaybeOwned, Release) { + std::unique_ptr stolen; + { + MaybeOwned ptr( +- pdfium::MakeUnique(100, &delete_count)); ++ std::make_unique(100, &delete_count)); + EXPECT_TRUE(ptr.IsOwned()); + stolen = ptr.Release(); + EXPECT_FALSE(ptr.IsOwned()); +@@ -160,19 +159,19 @@ TEST(MaybeOwned, Move) { + { + MaybeOwned ptr1(&thing1); + MaybeOwned ptr2( +- pdfium::MakeUnique(200, &delete_count)); ++ std::make_unique(200, &delete_count)); + EXPECT_FALSE(ptr1.IsOwned()); + EXPECT_TRUE(ptr2.IsOwned()); + + MaybeOwned ptr3(std::move(ptr1)); + MaybeOwned ptr4(std::move(ptr2)); +- EXPECT_FALSE(ptr1.IsOwned()); +- EXPECT_FALSE(ptr2.IsOwned()); ++ EXPECT_FALSE(ptr1.IsOwned()); // Unowned and null. ++ EXPECT_FALSE(ptr1.Get()); ++ EXPECT_TRUE(ptr2.IsOwned()); // Owned but null. ++ EXPECT_FALSE(ptr2.Get()); + EXPECT_FALSE(ptr3.IsOwned()); + EXPECT_TRUE(ptr4.IsOwned()); + EXPECT_EQ(0, delete_count); +- EXPECT_EQ(nullptr, ptr1.Get()); +- EXPECT_EQ(nullptr, ptr2.Get()); + EXPECT_EQ(100, ptr3->GetID()); + EXPECT_EQ(200, ptr4->GetID()); + +@@ -180,17 +179,55 @@ TEST(MaybeOwned, Move) { + MaybeOwned ptr6; + ptr5 = std::move(ptr3); + ptr6 = std::move(ptr4); +- EXPECT_FALSE(ptr3.IsOwned()); +- EXPECT_FALSE(ptr4.IsOwned()); ++ EXPECT_FALSE(ptr3.IsOwned()); // Unowned and null. ++ EXPECT_FALSE(ptr3.Get()); ++ EXPECT_TRUE(ptr4.IsOwned()); // Owned but null. ++ EXPECT_FALSE(ptr4.Get()); + EXPECT_FALSE(ptr5.IsOwned()); + EXPECT_TRUE(ptr6.IsOwned()); + EXPECT_EQ(0, delete_count); +- EXPECT_EQ(nullptr, ptr3.Get()); +- EXPECT_EQ(nullptr, ptr4.Get()); + EXPECT_EQ(100, ptr5->GetID()); + EXPECT_EQ(200, ptr6->GetID()); + } + EXPECT_EQ(1, delete_count); + } + ++namespace { ++ ++class Thing { ++ public: ++ int x = 42; ++}; ++ ++class Owner { ++ public: ++ explicit Owner(std::unique_ptr thing) : thing_(std::move(thing)) {} ++ ++ private: ++ std::unique_ptr thing_; ++}; ++ ++class Manager { ++ public: ++ Manager() ++ : transient_(std::make_unique()), ++ owner_(std::make_unique(transient_.Release())), ++ thing_(std::move(transient_).Get()) {} ++ ++ bool has_transient() const { return !!transient_.Get(); } ++ ++ private: ++ MaybeOwned transient_; // For initializng next two members. ++ const std::unique_ptr owner_; // Must outlive thing_. ++ const UnownedPtr thing_; ++}; ++ ++} // namespace ++ ++TEST(MaybeOwned, MoveElisionThwarted) { ++ // Test fails if the std::move() in Manager::Manager() is elided. ++ Manager manager; ++ EXPECT_FALSE(manager.has_transient()); ++} ++ + } // namespace fxcrt +diff --git a/core/fxcrt/observed_ptr.cpp b/core/fxcrt/observed_ptr.cpp +index f192e6950..611624a81 100644 +--- a/core/fxcrt/observed_ptr.cpp ++++ b/core/fxcrt/observed_ptr.cpp +@@ -1,9 +1,12 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcrt/observed_ptr.h" + ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" ++ + namespace fxcrt { + + Observable::Observable() = default; +@@ -12,4 +15,20 @@ Observable::~Observable() { + NotifyObservers(); + } + ++void Observable::AddObserver(ObserverIface* pObserver) { ++ DCHECK(!pdfium::Contains(m_Observers, pObserver)); ++ m_Observers.insert(pObserver); ++} ++ ++void Observable::RemoveObserver(ObserverIface* pObserver) { ++ DCHECK(pdfium::Contains(m_Observers, pObserver)); ++ m_Observers.erase(pObserver); ++} ++ ++void Observable::NotifyObservers() { ++ for (auto* pObserver : m_Observers) ++ pObserver->OnObservableDestroyed(); ++ m_Observers.clear(); ++} ++ + } // namespace fxcrt +diff --git a/core/fxcrt/observed_ptr.h b/core/fxcrt/observed_ptr.h +index 956870555..7e55dc6d9 100644 +--- a/core/fxcrt/observed_ptr.h ++++ b/core/fxcrt/observed_ptr.h +@@ -1,14 +1,15 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #ifndef CORE_FXCRT_OBSERVED_PTR_H_ + #define CORE_FXCRT_OBSERVED_PTR_H_ + ++#include ++ + #include + +-#include "core/fxcrt/fx_system.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" + + namespace fxcrt { + +@@ -23,21 +24,12 @@ class Observable { + + Observable(); + Observable(const Observable& that) = delete; +- ~Observable(); +- void AddObserver(ObserverIface* pObserver) { +- ASSERT(!pdfium::ContainsKey(m_Observers, pObserver)); +- m_Observers.insert(pObserver); +- } +- void RemoveObserver(ObserverIface* pObserver) { +- ASSERT(pdfium::ContainsKey(m_Observers, pObserver)); +- m_Observers.erase(pObserver); +- } +- void NotifyObservers() { +- for (auto* pObserver : m_Observers) +- pObserver->OnObservableDestroyed(); +- m_Observers.clear(); +- } + Observable& operator=(const Observable& that) = delete; ++ ~Observable(); ++ ++ void AddObserver(ObserverIface* pObserver); ++ void RemoveObserver(ObserverIface* pObserver); ++ void NotifyObservers(); + + protected: + size_t ActiveObserversForTesting() const { return m_Observers.size(); } +@@ -47,6 +39,8 @@ class Observable { + }; + + // Simple case of a self-nulling pointer. ++// Generally, pass ObservedPtr<> by non-const reference since this saves ++// considerable work compared to pass by value. + template + class ObservedPtr final : public Observable::ObserverIface { + public: +@@ -68,7 +62,7 @@ class ObservedPtr final : public Observable::ObserverIface { + m_pObservable->AddObserver(this); + } + void OnObservableDestroyed() override { +- ASSERT(m_pObservable); ++ DCHECK(m_pObservable); + m_pObservable = nullptr; + } + bool HasObservable() const { return !!m_pObservable; } +diff --git a/core/fxcrt/observed_ptr_unittest.cpp b/core/fxcrt/observed_ptr_unittest.cpp +index 81e114f47..c748f1447 100644 +--- a/core/fxcrt/observed_ptr_unittest.cpp ++++ b/core/fxcrt/observed_ptr_unittest.cpp +@@ -1,14 +1,14 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcrt/observed_ptr.h" + ++#include + #include + #include + + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + + namespace fxcrt { + namespace { +@@ -28,25 +28,25 @@ class SelfObservable final : public Observable { + + TEST(ObservePtr, Null) { + ObservedPtr ptr; +- EXPECT_EQ(nullptr, ptr.Get()); ++ EXPECT_FALSE(ptr.Get()); + } + + TEST(ObservePtr, LivesLonger) { + ObservedPtr ptr; + { +- auto pObs = pdfium::MakeUnique(); ++ auto pObs = std::make_unique(); + ptr.Reset(pObs.get()); +- EXPECT_NE(nullptr, ptr.Get()); ++ EXPECT_TRUE(ptr.Get()); + EXPECT_EQ(1u, pObs->ActiveObservedPtrs()); + } +- EXPECT_EQ(nullptr, ptr.Get()); ++ EXPECT_FALSE(ptr.Get()); + } + + TEST(ObservePtr, LivesShorter) { + PseudoObservable obs; + { + ObservedPtr ptr(&obs); +- EXPECT_NE(nullptr, ptr.Get()); ++ EXPECT_TRUE(ptr.Get()); + EXPECT_EQ(1u, obs.ActiveObservedPtrs()); + } + EXPECT_EQ(0u, obs.ActiveObservedPtrs()); +@@ -56,11 +56,11 @@ TEST(ObservePtr, CopyConstruct) { + PseudoObservable obs; + { + ObservedPtr ptr(&obs); +- EXPECT_NE(nullptr, ptr.Get()); ++ EXPECT_TRUE(ptr.Get()); + EXPECT_EQ(1u, obs.ActiveObservedPtrs()); + { + ObservedPtr ptr2(ptr); +- EXPECT_NE(nullptr, ptr2.Get()); ++ EXPECT_TRUE(ptr2.Get()); + EXPECT_EQ(2u, obs.ActiveObservedPtrs()); + } + EXPECT_EQ(1u, obs.ActiveObservedPtrs()); +@@ -72,12 +72,12 @@ TEST(ObservePtr, CopyAssign) { + PseudoObservable obs; + { + ObservedPtr ptr(&obs); +- EXPECT_NE(nullptr, ptr.Get()); ++ EXPECT_TRUE(ptr.Get()); + EXPECT_EQ(1u, obs.ActiveObservedPtrs()); + { + ObservedPtr ptr2; + ptr2 = ptr; +- EXPECT_NE(nullptr, ptr2.Get()); ++ EXPECT_TRUE(ptr2.Get()); + EXPECT_EQ(2u, obs.ActiveObservedPtrs()); + } + EXPECT_EQ(1u, obs.ActiveObservedPtrs()); +@@ -92,12 +92,12 @@ TEST(ObservePtr, Vector) { + std::vector> vec2; + vec1.emplace_back(&obs); + vec1.emplace_back(&obs); +- EXPECT_NE(nullptr, vec1[0].Get()); +- EXPECT_NE(nullptr, vec1[1].Get()); ++ EXPECT_TRUE(vec1[0].Get()); ++ EXPECT_TRUE(vec1[1].Get()); + EXPECT_EQ(2u, obs.ActiveObservedPtrs()); + vec2 = vec1; +- EXPECT_NE(nullptr, vec2[0].Get()); +- EXPECT_NE(nullptr, vec2[1].Get()); ++ EXPECT_TRUE(vec2[0].Get()); ++ EXPECT_TRUE(vec2[1].Get()); + EXPECT_EQ(4u, obs.ActiveObservedPtrs()); + vec1.clear(); + EXPECT_EQ(2u, obs.ActiveObservedPtrs()); +@@ -115,12 +115,12 @@ TEST(ObservePtr, VectorAutoClear) { + PseudoObservable obs; + vec1.emplace_back(&obs); + vec1.emplace_back(&obs); +- EXPECT_NE(nullptr, vec1[0].Get()); +- EXPECT_NE(nullptr, vec1[1].Get()); ++ EXPECT_TRUE(vec1[0].Get()); ++ EXPECT_TRUE(vec1[1].Get()); + EXPECT_EQ(2u, obs.ActiveObservedPtrs()); + } +- EXPECT_EQ(nullptr, vec1[0].Get()); +- EXPECT_EQ(nullptr, vec1[1].Get()); ++ EXPECT_FALSE(vec1[0].Get()); ++ EXPECT_FALSE(vec1[1].Get()); + } + + TEST(ObservePtr, ResetNull) { +@@ -216,7 +216,7 @@ TEST(ObservePtr, PairwiseObservable) { + EXPECT_EQ(&thing2, thing1.m_pOther.Get()); + EXPECT_EQ(&thing1, thing2.m_pOther.Get()); + } +- EXPECT_EQ(nullptr, thing1.m_pOther.Get()); ++ EXPECT_FALSE(thing1.m_pOther.Get()); + // Must be no ASAN violations upon cleanup here. + } + +diff --git a/core/fxcrt/pauseindicator_iface.h b/core/fxcrt/pauseindicator_iface.h +index d5d856d94..6aa7d0e6f 100644 +--- a/core/fxcrt/pauseindicator_iface.h ++++ b/core/fxcrt/pauseindicator_iface.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcrt/pdfium_span_unittest.cpp b/core/fxcrt/pdfium_span_unittest.cpp +index 6778bbb14..d0dfee200 100644 +--- a/core/fxcrt/pdfium_span_unittest.cpp ++++ b/core/fxcrt/pdfium_span_unittest.cpp +@@ -1,7 +1,9 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include ++ + #include "testing/gtest/include/gtest/gtest.h" + #include "third_party/base/span.h" + +@@ -10,20 +12,60 @@ + + TEST(PdfiumSpan, EmptySpan) { + int stuff[] = {1, 2, 3}; ++ pdfium::span null_span; + pdfium::span stuff_span(stuff); + pdfium::span empty_first_span = stuff_span.first(0); + pdfium::span empty_last_span = stuff_span.last(0); + pdfium::span empty_sub_span1 = stuff_span.subspan(0, 0); + pdfium::span empty_sub_span2 = stuff_span.subspan(3, 0); ++ EXPECT_TRUE(null_span.empty()); + EXPECT_TRUE(empty_first_span.empty()); + EXPECT_TRUE(empty_last_span.empty()); + EXPECT_TRUE(empty_sub_span1.empty()); + EXPECT_TRUE(empty_sub_span2.empty()); + } + +-TEST(PdfiumSpan, EmptySpanDeath) { ++// Custom implementation of first()/last(). ++TEST(PdfiumSpan, FirstLast) { ++ int one[] = {1}; + int stuff[] = {1, 2, 3}; ++ pdfium::span one_span(one); + pdfium::span stuff_span(stuff); +- pdfium::span empty_span = stuff_span.last(0); ++ EXPECT_EQ(one_span.front(), 1); ++ EXPECT_EQ(one_span.back(), 1); ++ EXPECT_EQ(stuff_span.front(), 1); ++ EXPECT_EQ(stuff_span.back(), 3); ++} ++ ++TEST(PdfiumSpanDeathTest, EmptySpanIndex) { ++ pdfium::span empty_span; + EXPECT_DEATH(empty_span[0] += 1, ".*"); + } ++ ++TEST(PdfiumSpanDeathTest, EmptySpanFront) { ++ pdfium::span empty_span; ++ EXPECT_DEATH(empty_span.front() += 1, ".*"); ++} ++ ++TEST(PdfiumSpanDeathTest, EmptySpanBack) { ++ pdfium::span empty_span; ++ EXPECT_DEATH(empty_span.back() += 1, ".*"); ++} ++ ++#if defined(ADDRESS_SANITIZER) ++namespace { ++ ++void CreateDanglingSpan() { ++ pdfium::span data_span; ++ { ++ std::vector data(4); ++ data_span = pdfium::make_span(data); ++ } ++} ++ ++} // namespace ++ ++TEST(PdfiumSpanDeathTest, DanglingReference) { ++ EXPECT_DEATH(CreateDanglingSpan(), ".*"); ++} ++#endif // defined(ADDRESS_SANITIZER) +diff --git a/core/fxcrt/retain_ptr.h b/core/fxcrt/retain_ptr.h +index 916a12efe..26223b620 100644 +--- a/core/fxcrt/retain_ptr.h ++++ b/core/fxcrt/retain_ptr.h +@@ -1,16 +1,20 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #ifndef CORE_FXCRT_RETAIN_PTR_H_ + #define CORE_FXCRT_RETAIN_PTR_H_ + ++#include ++ + #include + #include ++#include + #include + +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "third_party/base/check.h" ++#include "third_party/base/compiler_specific.h" + + namespace fxcrt { + +@@ -22,29 +26,91 @@ struct ReleaseDeleter { + + // Analogous to base's scoped_refptr. + template +-class RetainPtr { ++class TRIVIAL_ABI RetainPtr { + public: +- explicit RetainPtr(T* pObj) : m_pObj(pObj) { ++ RetainPtr() noexcept = default; ++ ++ // Deliberately implicit to allow returning nullptrs. ++ // NOLINTNEXTLINE(runtime/explicit) ++ RetainPtr(std::nullptr_t ptr) {} ++ ++ explicit RetainPtr(T* pObj) noexcept : m_pObj(pObj) { + if (m_pObj) + m_pObj->Retain(); + } + +- RetainPtr() = default; +- RetainPtr(const RetainPtr& that) : RetainPtr(that.Get()) {} ++ // Copy-construct a RetainPtr. ++ // Required in addition to copy conversion constructor below. ++ RetainPtr(const RetainPtr& that) noexcept : RetainPtr(that.Get()) {} + + // Move-construct a RetainPtr. After construction, |that| will be NULL. +- RetainPtr(RetainPtr&& that) noexcept { Swap(that); } ++ // Required in addition to move conversion constructor below. ++ RetainPtr(RetainPtr&& that) noexcept { Unleak(that.Leak()); } + +- // Deliberately implicit to allow returning nullptrs. +- // NOLINTNEXTLINE(runtime/explicit) +- RetainPtr(std::nullptr_t ptr) {} ++ // Copy conversion constructor. ++ template ::value>::type> ++ RetainPtr(const RetainPtr& that) : RetainPtr(that.Get()) {} ++ ++ // Move-conversion constructor. ++ template ::value>::type> ++ RetainPtr(RetainPtr&& that) noexcept { ++ Unleak(that.Leak()); ++ } ++ ++ // Assign a RetainPtr from nullptr; ++ RetainPtr& operator=(std::nullptr_t) noexcept { ++ Reset(); ++ return *this; ++ } ++ ++ // Copy-assign a RetainPtr. ++ // Required in addition to copy conversion assignment below. ++ RetainPtr& operator=(const RetainPtr& that) { ++ if (*this != that) ++ Reset(that.Get()); ++ return *this; ++ } ++ ++ // Move-assign a RetainPtr. After assignment, |that| will be NULL. ++ // Required in addition to move conversion assignment below. ++ RetainPtr& operator=(RetainPtr&& that) noexcept { ++ Unleak(that.Leak()); ++ return *this; ++ } ++ ++ // Copy-convert assign a RetainPtr. ++ template ::value>::type> ++ RetainPtr& operator=(const RetainPtr& that) { ++ if (*this != that) ++ Reset(that.Get()); ++ return *this; ++ } ++ ++ // Move-convert assign a RetainPtr. After assignment, |that| will be NULL. ++ template ::value>::type> ++ RetainPtr& operator=(RetainPtr&& that) noexcept { ++ Unleak(that.Leak()); ++ return *this; ++ } ++ ++ ~RetainPtr() = default; + + template +- RetainPtr(const RetainPtr& that) : RetainPtr(that.Get()) {} ++ U* AsRaw() const { ++ return static_cast(Get()); ++ } + + template + RetainPtr As() const { +- return RetainPtr(static_cast(Get())); ++ return RetainPtr(AsRaw()); + } + + void Reset(T* obj = nullptr) { +@@ -53,7 +119,9 @@ class RetainPtr { + m_pObj.reset(obj); + } + +- T* Get() const { return m_pObj.get(); } ++ operator T*() const noexcept { return Get(); } ++ T* Get() const noexcept { return m_pObj.get(); } ++ + UnownedPtr BackPointer() const { return UnownedPtr(Get()); } + void Swap(RetainPtr& that) { m_pObj.swap(that.m_pObj); } + +@@ -61,21 +129,6 @@ class RetainPtr { + T* Leak() { return m_pObj.release(); } + void Unleak(T* ptr) { m_pObj.reset(ptr); } + +- RetainPtr& operator=(const RetainPtr& that) { +- if (*this != that) +- Reset(that.Get()); +- return *this; +- } +- +- // Move-assign a RetainPtr. After assignment, |that| will be NULL. +- RetainPtr& operator=(RetainPtr&& that) { +- m_pObj.reset(that.Leak()); +- return *this; +- } +- +- // Assigment from raw pointers is intentially not provided to make +- // reference count churn more visible where possible. +- + bool operator==(const RetainPtr& that) const { return Get() == that.Get(); } + bool operator!=(const RetainPtr& that) const { return !(*this == that); } + +@@ -121,26 +174,25 @@ class Retainable { + Retainable(const Retainable& that) = delete; + Retainable& operator=(const Retainable& that) = delete; + +- void Retain() const { ++m_nRefCount; } ++ // These need to be const methods operating on a mutable member so that ++ // RetainPtr can be used for an object that is otherwise const ++ // apart from the internal ref-counting. ++ void Retain() const { ++ ++m_nRefCount; ++ CHECK(m_nRefCount > 0); ++ } + void Release() const { +- ASSERT(m_nRefCount > 0); ++ CHECK(m_nRefCount > 0); + if (--m_nRefCount == 0) + delete this; + } + +- mutable intptr_t m_nRefCount = 0; ++ mutable uintptr_t m_nRefCount = 0; ++ static_assert(std::is_unsigned::value, ++ "m_nRefCount must be an unsigned type for overflow check" ++ "to work properly in Retain()"); + }; + +-template +-inline bool operator==(const U* lhs, const RetainPtr& rhs) { +- return rhs == lhs; +-} +- +-template +-inline bool operator!=(const U* lhs, const RetainPtr& rhs) { +- return rhs != lhs; +-} +- + } // namespace fxcrt + + using fxcrt::ReleaseDeleter; +@@ -149,16 +201,17 @@ using fxcrt::RetainPtr; + + namespace pdfium { + +-// Helper to make a RetainPtr along the lines of std::make_unique<>(), +-// or pdfium::MakeUnique<>(). Arguments are forwarded to T's constructor. +-// Classes managed by RetainPtr should have protected (or private) +-// constructors, and should friend this function. ++// Helper to make a RetainPtr along the lines of std::make_unique<>(). ++// Arguments are forwarded to T's constructor. Classes managed by RetainPtr ++// should have protected (or private) constructors, and should friend this ++// function. + template + RetainPtr MakeRetain(Args&&... args) { + return RetainPtr(new T(std::forward(args)...)); + } + +-// Type-deducing wrapper to make a RetainPtr from an ordinary pointer. ++// Type-deducing wrapper to make a RetainPtr from an ordinary pointer, ++// since equivalent constructor is explicit. + template + RetainPtr WrapRetain(T* that) { + return RetainPtr(that); +@@ -166,4 +219,10 @@ RetainPtr WrapRetain(T* that) { + + } // namespace pdfium + ++// Macro to allow construction via MakeRetain<>() only, when used ++// with a private constructor in a class. ++#define CONSTRUCT_VIA_MAKE_RETAIN \ ++ template \ ++ friend RetainPtr pdfium::MakeRetain(Args&&... args) ++ + #endif // CORE_FXCRT_RETAIN_PTR_H_ +diff --git a/core/fxcrt/retain_ptr_unittest.cpp b/core/fxcrt/retain_ptr_unittest.cpp +index 47277df63..b381e78d0 100644 +--- a/core/fxcrt/retain_ptr_unittest.cpp ++++ b/core/fxcrt/retain_ptr_unittest.cpp +@@ -1,23 +1,52 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcrt/retain_ptr.h" + ++#include ++#include + #include + #include + + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/pseudo_retainable.h" ++#include "third_party/base/containers/contains.h" ++ ++namespace { ++ ++template > ++class NoLinearSearchSet : public std::set { ++ public: ++ typename std::set::iterator begin() noexcept = delete; ++ typename std::set::const_iterator cbegin() const noexcept = delete; ++}; ++ ++} // namespace + + namespace fxcrt { ++namespace { ++ ++template > ++class NoLinearSearchSet : public std::set { ++ public: ++ typename std::set::iterator begin() noexcept = delete; ++ typename std::set::const_iterator cbegin() const noexcept = delete; ++}; ++ ++} // namespace + +-TEST(RetainPtr, Null) { ++TEST(RetainPtr, DefaultCtor) { + RetainPtr ptr; +- EXPECT_EQ(nullptr, ptr.Get()); ++ EXPECT_FALSE(ptr.Get()); + } + +-TEST(RetainPtr, Normal) { ++TEST(RetainPtr, NullptrCtor) { ++ RetainPtr ptr(nullptr); ++ EXPECT_FALSE(ptr.Get()); ++} ++ ++TEST(RetainPtr, RawCtor) { + PseudoRetainable obj; + { + RetainPtr ptr(&obj); +@@ -51,7 +80,143 @@ TEST(RetainPtr, MoveCtor) { + RetainPtr ptr1(&obj); + { + RetainPtr ptr2(std::move(ptr1)); +- EXPECT_EQ(nullptr, ptr1.Get()); ++ EXPECT_FALSE(ptr1.Get()); ++ EXPECT_EQ(&obj, ptr2.Get()); ++ EXPECT_EQ(1, obj.retain_count()); ++ EXPECT_EQ(0, obj.release_count()); ++ } ++ EXPECT_EQ(1, obj.retain_count()); ++ EXPECT_EQ(1, obj.release_count()); ++ } ++ EXPECT_EQ(1, obj.retain_count()); ++ EXPECT_EQ(1, obj.release_count()); ++} ++ ++TEST(RetainPtr, CopyConversionCtor) { ++ PseudoRetainable obj; ++ { ++ RetainPtr ptr1(&obj); ++ { ++ RetainPtr ptr2(ptr1); ++ EXPECT_EQ(2, obj.retain_count()); ++ EXPECT_EQ(0, obj.release_count()); ++ } ++ EXPECT_EQ(2, obj.retain_count()); ++ EXPECT_EQ(1, obj.release_count()); ++ } ++ EXPECT_EQ(2, obj.retain_count()); ++ EXPECT_EQ(2, obj.release_count()); ++} ++ ++TEST(RetainPtr, MoveConversionCtor) { ++ PseudoRetainable obj; ++ { ++ RetainPtr ptr1(&obj); ++ { ++ RetainPtr ptr2(std::move(ptr1)); ++ EXPECT_FALSE(ptr1.Get()); ++ EXPECT_EQ(&obj, ptr2.Get()); ++ EXPECT_EQ(1, obj.retain_count()); ++ EXPECT_EQ(0, obj.release_count()); ++ } ++ EXPECT_EQ(1, obj.retain_count()); ++ EXPECT_EQ(1, obj.release_count()); ++ } ++ EXPECT_EQ(1, obj.retain_count()); ++ EXPECT_EQ(1, obj.release_count()); ++} ++ ++TEST(RetainPtr, NullptrAssign) { ++ PseudoRetainable obj; ++ RetainPtr ptr(&obj); ++ ptr = nullptr; ++ EXPECT_FALSE(ptr); ++} ++ ++TEST(RetainPtr, RawAssign) { ++ PseudoRetainable obj; ++ RetainPtr ptr; ++ ptr = pdfium::WrapRetain(&obj); ++ EXPECT_EQ(&obj, ptr); ++} ++ ++TEST(RetainPtr, CopyAssign) { ++ PseudoRetainable obj; ++ { ++ RetainPtr ptr(&obj); ++ { ++ RetainPtr ptr2; ++ ptr2 = ptr; ++ EXPECT_EQ(2, obj.retain_count()); ++ EXPECT_EQ(0, obj.release_count()); ++ } ++ { ++ // Test assignment from wrapped underlying type. ++ RetainPtr ptr2; ++ ptr2 = pdfium::WrapRetain(ptr.Get()); ++ EXPECT_EQ(3, obj.retain_count()); ++ EXPECT_EQ(1, obj.release_count()); ++ } ++ EXPECT_EQ(3, obj.retain_count()); ++ EXPECT_EQ(2, obj.release_count()); ++ } ++ EXPECT_EQ(3, obj.retain_count()); ++ EXPECT_EQ(3, obj.release_count()); ++} ++ ++TEST(RetainPtr, MoveAssign) { ++ PseudoRetainable obj; ++ { ++ RetainPtr ptr1(&obj); ++ { ++ RetainPtr ptr2; ++ EXPECT_EQ(&obj, ptr1.Get()); ++ EXPECT_FALSE(ptr2.Get()); ++ ptr2 = std::move(ptr1); ++ EXPECT_FALSE(ptr1.Get()); ++ EXPECT_EQ(&obj, ptr2.Get()); ++ EXPECT_EQ(1, obj.retain_count()); ++ EXPECT_EQ(0, obj.release_count()); ++ } ++ EXPECT_EQ(1, obj.retain_count()); ++ EXPECT_EQ(1, obj.release_count()); ++ } ++ EXPECT_EQ(1, obj.retain_count()); ++ EXPECT_EQ(1, obj.release_count()); ++} ++ ++TEST(RetainPtr, CopyConvertAssign) { ++ PseudoRetainable obj; ++ { ++ RetainPtr ptr(&obj); ++ { ++ RetainPtr ptr2; ++ ptr2 = ptr; ++ EXPECT_EQ(2, obj.retain_count()); ++ EXPECT_EQ(0, obj.release_count()); ++ } ++ { ++ // Test assignment from wrapped underlying type. ++ RetainPtr ptr2; ++ ptr2 = pdfium::WrapRetain(ptr.Get()); ++ EXPECT_EQ(3, obj.retain_count()); ++ EXPECT_EQ(1, obj.release_count()); ++ } ++ EXPECT_EQ(3, obj.retain_count()); ++ EXPECT_EQ(2, obj.release_count()); ++ } ++ EXPECT_EQ(3, obj.retain_count()); ++ EXPECT_EQ(3, obj.release_count()); ++} ++ ++TEST(RetainPtr, MoveConvertAssign) { ++ PseudoRetainable obj; ++ { ++ RetainPtr ptr1(&obj); ++ { ++ RetainPtr ptr2; ++ ptr2 = std::move(ptr1); ++ EXPECT_FALSE(ptr1); + EXPECT_EQ(&obj, ptr2.Get()); + EXPECT_EQ(1, obj.retain_count()); + EXPECT_EQ(0, obj.release_count()); +@@ -63,6 +228,15 @@ TEST(RetainPtr, MoveCtor) { + EXPECT_EQ(1, obj.release_count()); + } + ++TEST(RetainPtr, AmbiguousExpression) { ++ class A : public Retainable {}; ++ class B : public A {}; ++ ++ // Test passes if it compiles without error. ++ RetainPtr var = (0) ? pdfium::MakeRetain() : pdfium::MakeRetain(); ++ EXPECT_TRUE(var); ++} ++ + TEST(RetainPtr, ResetNull) { + PseudoRetainable obj; + { +@@ -156,44 +330,6 @@ TEST(RetainPtr, SwapNull) { + EXPECT_EQ(1, obj1.release_count()); + } + +-TEST(RetainPtr, Assign) { +- PseudoRetainable obj; +- { +- RetainPtr ptr(&obj); +- { +- RetainPtr ptr2; +- ptr2 = ptr; +- EXPECT_EQ(2, obj.retain_count()); +- EXPECT_EQ(0, obj.release_count()); +- } +- EXPECT_EQ(2, obj.retain_count()); +- EXPECT_EQ(1, obj.release_count()); +- } +- EXPECT_EQ(2, obj.retain_count()); +- EXPECT_EQ(2, obj.release_count()); +-} +- +-TEST(RetainPtr, MoveAssign) { +- PseudoRetainable obj; +- { +- RetainPtr ptr1(&obj); +- { +- RetainPtr ptr2; +- EXPECT_EQ(&obj, ptr1.Get()); +- EXPECT_EQ(nullptr, ptr2.Get()); +- ptr2 = std::move(ptr1); +- EXPECT_EQ(nullptr, ptr1.Get()); +- EXPECT_EQ(&obj, ptr2.Get()); +- EXPECT_EQ(1, obj.retain_count()); +- EXPECT_EQ(0, obj.release_count()); +- } +- EXPECT_EQ(1, obj.retain_count()); +- EXPECT_EQ(1, obj.release_count()); +- } +- EXPECT_EQ(1, obj.retain_count()); +- EXPECT_EQ(1, obj.release_count()); +-} +- + TEST(RetainPtr, Equals) { + PseudoRetainable obj1; + PseudoRetainable obj2; +@@ -309,4 +445,79 @@ TEST(RetainPtr, VectorMove) { + EXPECT_EQ(1, obj.release_count()); + } + ++TEST(RetainPtr, SetContains) { ++ // Makes sure pdfium::Contains() works the same way with raw pointers and ++ // RetainPtrs for containers that use find(). ++ PseudoRetainable obj1; ++ PseudoRetainable obj2; ++ RetainPtr ptr1(&obj1); ++ RetainPtr ptr2(&obj2); ++ RetainPtr const_ptr1(&obj1); ++ RetainPtr const_ptr2(&obj2); ++ NoLinearSearchSet, std::less<>> the_set; ++ ++ // Intially, two smart pointers to each object. ++ EXPECT_EQ(2, obj1.retain_count()); ++ EXPECT_EQ(0, obj1.release_count()); ++ EXPECT_EQ(2, obj2.retain_count()); ++ EXPECT_EQ(0, obj2.release_count()); ++ ++ // Passed by const-ref, increment on copy into set's data structure. ++ the_set.insert(const_ptr1); ++ EXPECT_EQ(3, obj1.retain_count()); ++ EXPECT_EQ(0, obj1.release_count()); ++ EXPECT_EQ(2, obj2.retain_count()); ++ EXPECT_EQ(0, obj2.release_count()); ++ ++ // None of the following should cause any churn. ++ EXPECT_NE(the_set.end(), the_set.find(&obj1)); ++ EXPECT_EQ(the_set.end(), the_set.find(&obj2)); ++ EXPECT_TRUE(pdfium::Contains(the_set, &obj1)); ++ EXPECT_FALSE(pdfium::Contains(the_set, &obj2)); ++ EXPECT_EQ(3, obj1.retain_count()); ++ EXPECT_EQ(0, obj1.release_count()); ++ EXPECT_EQ(2, obj2.retain_count()); ++ EXPECT_EQ(0, obj2.release_count()); ++ ++ EXPECT_NE(the_set.end(), the_set.find(const_ptr1)); ++ EXPECT_EQ(the_set.end(), the_set.find(const_ptr2)); ++ EXPECT_TRUE(pdfium::Contains(the_set, const_ptr1)); ++ EXPECT_FALSE(pdfium::Contains(the_set, const_ptr2)); ++ EXPECT_EQ(3, obj1.retain_count()); ++ EXPECT_EQ(0, obj1.release_count()); ++ EXPECT_EQ(2, obj2.retain_count()); ++ EXPECT_EQ(0, obj2.release_count()); ++ ++ // These involve const-removing conversions which seem to churn. ++ EXPECT_NE(the_set.end(), the_set.find(ptr1)); ++ EXPECT_EQ(the_set.end(), the_set.find(ptr2)); ++ EXPECT_TRUE(pdfium::Contains(the_set, ptr1)); ++ EXPECT_FALSE(pdfium::Contains(the_set, ptr2)); ++ EXPECT_EQ(5, obj1.retain_count()); ++ EXPECT_EQ(2, obj1.release_count()); ++ EXPECT_EQ(4, obj2.retain_count()); ++ EXPECT_EQ(2, obj2.release_count()); ++} ++ ++TEST(RetainPtr, VectorContains) { ++ // Makes sure pdfium::Contains() works the same way with raw pointers and ++ // RetainPtrs. for containers that use begin()/end(). ++ PseudoRetainable obj1; ++ PseudoRetainable obj2; ++ std::vector vec; ++ vec.push_back(&obj1); ++ EXPECT_TRUE(pdfium::Contains(vec, &obj1)); ++ EXPECT_FALSE(pdfium::Contains(vec, &obj2)); ++ ++ RetainPtr ptr1(&obj1); ++ RetainPtr ptr2(&obj2); ++ EXPECT_TRUE(pdfium::Contains(vec, ptr1)); ++ EXPECT_FALSE(pdfium::Contains(vec, ptr2)); ++ ++ RetainPtr const_ptr1(&obj1); ++ RetainPtr const_ptr2(&obj2); ++ EXPECT_TRUE(pdfium::Contains(vec, const_ptr1)); ++ EXPECT_FALSE(pdfium::Contains(vec, const_ptr2)); ++} ++ + } // namespace fxcrt +diff --git a/core/fxcrt/retained_tree_node.h b/core/fxcrt/retained_tree_node.h +deleted file mode 100644 +index a859ae529..000000000 +--- a/core/fxcrt/retained_tree_node.h ++++ /dev/null +@@ -1,81 +0,0 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#ifndef CORE_FXCRT_RETAINED_TREE_NODE_H_ +-#define CORE_FXCRT_RETAINED_TREE_NODE_H_ +- +-#include "core/fxcrt/retain_ptr.h" +-#include "core/fxcrt/tree_node.h" +-#include "third_party/base/logging.h" +- +-namespace fxcrt { +- +-// For DOM/XML-ish trees, where references outside the tree are RetainPtr, +-// and the parent node also "retains" its children but doesn't always have +-// a direct pointer to them. +-template +-class RetainedTreeNode : public TreeNode { +- public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- +- void AppendFirstChild(const RetainPtr& child) { +- TreeNode::AppendFirstChild(child.Get()); +- } +- +- void AppendLastChild(const RetainPtr& child) { +- TreeNode::AppendLastChild(child.Get()); +- } +- +- void InsertBefore(const RetainPtr& child, T* other) { +- TreeNode::InsertBefore(child.Get(), other); +- } +- +- void InsertAfter(const RetainPtr& child, T* other) { +- TreeNode::InsertAfter(child.Get(), other); +- } +- +- void RemoveChild(const RetainPtr& child) { +- TreeNode::RemoveChild(child.Get()); +- } +- +- void RemoveSelfIfParented() { +- if (T* parent = TreeNode::GetParent()) { +- parent->TreeNode::RemoveChild( +- pdfium::WrapRetain(static_cast(this)).Get()); +- } +- } +- +- protected: +- RetainedTreeNode() = default; +- ~RetainedTreeNode() override { +- while (auto* pChild = TreeNode::GetFirstChild()) +- RemoveChild(pdfium::WrapRetain(pChild)); +- } +- +- private: +- template +- friend struct ReleaseDeleter; +- +- template +- friend class RetainPtr; +- +- RetainedTreeNode(const RetainedTreeNode& that) = delete; +- RetainedTreeNode& operator=(const RetainedTreeNode& that) = delete; +- +- void Retain() { ++m_nRefCount; } +- void Release() { +- ASSERT(m_nRefCount > 0); +- if (--m_nRefCount == 0 && !TreeNode::GetParent()) +- delete this; +- } +- +- intptr_t m_nRefCount = 0; +-}; +- +-} // namespace fxcrt +- +-using fxcrt::RetainedTreeNode; +- +-#endif // CORE_FXCRT_RETAINED_TREE_NODE_H_ +diff --git a/core/fxcrt/retained_tree_node_unittest.cpp b/core/fxcrt/retained_tree_node_unittest.cpp +deleted file mode 100644 +index d469ab3e4..000000000 +--- a/core/fxcrt/retained_tree_node_unittest.cpp ++++ /dev/null +@@ -1,188 +0,0 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#include "core/fxcrt/retained_tree_node.h" +- +-#include "core/fxcrt/observed_ptr.h" +-#include "core/fxcrt/retain_ptr.h" +-#include "testing/gtest/include/gtest/gtest.h" +- +-namespace fxcrt { +-namespace { +- +-class ObservableRetainedTreeNodeForTest +- : public RetainedTreeNode, +- public Observable { +- public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- +- private: +- ObservableRetainedTreeNodeForTest() = default; +-}; +- +-void AddClutterToFront( +- const RetainPtr& parent) { +- for (int i = 0; i < 4; ++i) { +- parent->AppendFirstChild( +- pdfium::MakeRetain()); +- } +-} +- +-void AddClutterToBack( +- const RetainPtr& parent) { +- for (int i = 0; i < 4; ++i) { +- parent->AppendLastChild( +- pdfium::MakeRetain()); +- } +-} +- +-} // namespace +- +-TEST(RetainedTreeNode, NoParent) { +- ObservedPtr watcher; +- { +- RetainPtr ptr = +- pdfium::MakeRetain(); +- EXPECT_FALSE(ptr->HasChild(ptr.Get())); +- watcher = ObservedPtr(ptr.Get()); +- EXPECT_TRUE(watcher.Get()); +- } +- EXPECT_FALSE(watcher.Get()); +-} +- +-TEST(RetainedTreeNode, FirstHasParent) { +- ObservedPtr watcher; +- RetainPtr parent = +- pdfium::MakeRetain(); +- { +- RetainPtr ptr = +- pdfium::MakeRetain(); +- watcher = ObservedPtr(ptr.Get()); +- parent->AppendFirstChild(ptr); +- EXPECT_FALSE(parent->HasChild(parent.Get())); +- EXPECT_TRUE(parent->HasChild(ptr.Get())); +- EXPECT_TRUE(watcher.Get()); +- } +- EXPECT_TRUE(watcher.Get()); +- parent->RemoveChild(pdfium::WrapRetain(watcher.Get())); +- EXPECT_FALSE(watcher.Get()); +- // Now add some clutter. +- { +- RetainPtr ptr = +- pdfium::MakeRetain(); +- watcher = ObservedPtr(ptr.Get()); +- parent->AppendFirstChild(ptr); +- AddClutterToFront(parent); +- AddClutterToBack(parent); +- EXPECT_TRUE(watcher.Get()); +- } +- EXPECT_TRUE(watcher.Get()); +- parent->RemoveChild(pdfium::WrapRetain(watcher.Get())); +- EXPECT_FALSE(watcher.Get()); +-} +- +-TEST(RetainedTreeNode, LastHasParent) { +- ObservedPtr watcher; +- RetainPtr parent = +- pdfium::MakeRetain(); +- { +- RetainPtr ptr = +- pdfium::MakeRetain(); +- watcher = ObservedPtr(ptr.Get()); +- parent->AppendLastChild(ptr); +- EXPECT_FALSE(parent->HasChild(parent.Get())); +- EXPECT_TRUE(parent->HasChild(ptr.Get())); +- EXPECT_TRUE(watcher.Get()); +- } +- { +- // Now add some clutter. +- RetainPtr ptr = +- pdfium::MakeRetain(); +- watcher = ObservedPtr(ptr.Get()); +- parent->AppendLastChild(ptr); +- AddClutterToFront(parent); +- AddClutterToBack(parent); +- EXPECT_TRUE(watcher.Get()); +- } +- EXPECT_TRUE(watcher.Get()); +- parent->RemoveChild(pdfium::WrapRetain(watcher.Get())); +- EXPECT_FALSE(watcher.Get()); +-} +- +-TEST(RetainedTreeNode, GrandChildCleanedUp) { +- ObservedPtr watcher; +- RetainPtr grandparent = +- pdfium::MakeRetain(); +- { +- RetainPtr parent = +- pdfium::MakeRetain(); +- grandparent->AppendFirstChild(parent); +- { +- RetainPtr ptr = +- pdfium::MakeRetain(); +- watcher = ObservedPtr(ptr.Get()); +- parent->AppendFirstChild(ptr); +- EXPECT_TRUE(watcher.Get()); +- } +- grandparent->RemoveChild(parent); +- EXPECT_TRUE(watcher.Get()); +- } +- EXPECT_FALSE(watcher.Get()); +-} +- +-TEST(RetainedTreeNode, RemoveSelf) { +- ObservedPtr watcher; +- auto parent = pdfium::MakeRetain(); +- { +- auto child = pdfium::MakeRetain(); +- watcher = ObservedPtr(child.Get()); +- parent->AppendFirstChild(child); +- } +- EXPECT_TRUE(watcher.Get()); +- watcher->RemoveSelfIfParented(); +- EXPECT_FALSE(watcher.Get()); +-} +- +-TEST(RetainedTreeNode, InsertBeforeAfter) { +- ObservedPtr watcher; +- RetainPtr parent = +- pdfium::MakeRetain(); +- +- AddClutterToFront(parent); +- { +- RetainPtr ptr = +- pdfium::MakeRetain(); +- watcher = ObservedPtr(ptr.Get()); +- parent->AppendFirstChild(ptr); +- parent->InsertBefore(pdfium::WrapRetain(parent->GetFirstChild()), +- parent->GetLastChild()); +- parent->InsertAfter(pdfium::WrapRetain(parent->GetLastChild()), +- parent->GetFirstChild()); +- EXPECT_TRUE(watcher.Get()); +- } +- parent->RemoveChild(pdfium::WrapRetain(watcher.Get())); +- EXPECT_FALSE(watcher.Get()); +-} +- +-TEST(RetainedTreeNode, Traversal) { +- RetainPtr parent = +- pdfium::MakeRetain(); +- +- AddClutterToFront(parent); +- int count = 0; +- for (ObservableRetainedTreeNodeForTest* pNode = parent->GetFirstChild(); +- pNode; pNode = pNode->GetNextSibling()) { +- ++count; +- } +- EXPECT_EQ(4, count); +- count = 0; +- for (ObservableRetainedTreeNodeForTest* pNode = parent->GetLastChild(); pNode; +- pNode = pNode->GetPrevSibling()) { +- ++count; +- } +- EXPECT_EQ(4, count); +-} +- +-} // namespace fxcrt +diff --git a/core/fxcrt/scoped_set_insertion.h b/core/fxcrt/scoped_set_insertion.h +new file mode 100644 +index 000000000..3a0ff0e6b +--- /dev/null ++++ b/core/fxcrt/scoped_set_insertion.h +@@ -0,0 +1,40 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXCRT_SCOPED_SET_INSERTION_H_ ++#define CORE_FXCRT_SCOPED_SET_INSERTION_H_ ++ ++#include ++#include ++ ++#include "core/fxcrt/fx_memory.h" ++#include "third_party/base/check.h" ++ ++namespace fxcrt { ++ ++// Track the addition of an object to a set, removing it automatically when ++// the ScopedSetInsertion goes out of scope. ++template ++class ScopedSetInsertion { ++ public: ++ FX_STACK_ALLOCATED(); ++ ++ ScopedSetInsertion(std::set* org_set, const T& elem) ++ : set_(org_set), insert_results_(set_->insert(elem)) { ++ CHECK(insert_results_.second); ++ } ++ ScopedSetInsertion(const ScopedSetInsertion&) = delete; ++ ScopedSetInsertion& operator=(const ScopedSetInsertion&) = delete; ++ ~ScopedSetInsertion() { set_->erase(insert_results_.first); } ++ ++ private: ++ std::set* const set_; ++ const std::pair::iterator, bool> insert_results_; ++}; ++ ++} // namespace fxcrt ++ ++using fxcrt::ScopedSetInsertion; ++ ++#endif // CORE_FXCRT_SCOPED_SET_INSERTION_H_ +diff --git a/core/fxcrt/scoped_set_insertion_unittest.cpp b/core/fxcrt/scoped_set_insertion_unittest.cpp +new file mode 100644 +index 000000000..6a6b990fc +--- /dev/null ++++ b/core/fxcrt/scoped_set_insertion_unittest.cpp +@@ -0,0 +1,24 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/scoped_set_insertion.h" ++ ++#include "testing/gmock/include/gmock/gmock.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++TEST(fxcrt, ScopedSetInsertion) { ++ std::set container; ++ { ++ ScopedSetInsertion insertion(&container, 5); ++ EXPECT_THAT(container, testing::UnorderedElementsAreArray({5})); ++ ++ { ++ ScopedSetInsertion insertion2(&container, 6); ++ EXPECT_THAT(container, testing::UnorderedElementsAreArray({5, 6})); ++ } ++ ++ EXPECT_THAT(container, testing::UnorderedElementsAreArray({5})); ++ } ++ EXPECT_TRUE(container.empty()); ++} +diff --git a/core/fxcrt/shared_copy_on_write.h b/core/fxcrt/shared_copy_on_write.h +index a672ddf2c..846696579 100644 +--- a/core/fxcrt/shared_copy_on_write.h ++++ b/core/fxcrt/shared_copy_on_write.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -20,10 +20,10 @@ namespace fxcrt { + template + class SharedCopyOnWrite { + public: +- SharedCopyOnWrite() {} ++ SharedCopyOnWrite() = default; + SharedCopyOnWrite(const SharedCopyOnWrite& other) + : m_pObject(other.m_pObject) {} +- ~SharedCopyOnWrite() {} ++ ~SharedCopyOnWrite() = default; + + template + ObjClass* Emplace(Args... params) { +diff --git a/core/fxcrt/shared_copy_on_write_unittest.cpp b/core/fxcrt/shared_copy_on_write_unittest.cpp +index f57935881..979058b9e 100644 +--- a/core/fxcrt/shared_copy_on_write_unittest.cpp ++++ b/core/fxcrt/shared_copy_on_write_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -53,7 +53,7 @@ TEST(SharedCopyOnWrite, Null) { + Observer observer; + { + SharedCopyOnWrite ptr; +- EXPECT_EQ(nullptr, ptr.GetObject()); ++ EXPECT_FALSE(ptr.GetObject()); + } + } + +@@ -112,16 +112,16 @@ TEST(SharedCopyOnWrite, GetModify) { + Observer observer; + { + SharedCopyOnWrite ptr; +- EXPECT_NE(nullptr, ptr.GetPrivateCopy(&observer, std::string("one"))); ++ EXPECT_TRUE(ptr.GetPrivateCopy(&observer, std::string("one"))); + EXPECT_EQ(1, observer.GetConstructionCount("one")); + EXPECT_EQ(0, observer.GetDestructionCount("one")); + +- EXPECT_NE(nullptr, ptr.GetPrivateCopy(&observer, std::string("one"))); ++ EXPECT_TRUE(ptr.GetPrivateCopy(&observer, std::string("one"))); + EXPECT_EQ(1, observer.GetConstructionCount("one")); + EXPECT_EQ(0, observer.GetDestructionCount("one")); + { + SharedCopyOnWrite other(ptr); +- EXPECT_NE(nullptr, ptr.GetPrivateCopy(&observer, std::string("one"))); ++ EXPECT_TRUE(ptr.GetPrivateCopy(&observer, std::string("one"))); + EXPECT_EQ(2, observer.GetConstructionCount("one")); + EXPECT_EQ(0, observer.GetDestructionCount("one")); + } +diff --git a/core/fxcrt/small_buffer.h b/core/fxcrt/small_buffer.h +new file mode 100644 +index 000000000..a0fd5c218 +--- /dev/null ++++ b/core/fxcrt/small_buffer.h +@@ -0,0 +1,49 @@ ++// Copyright 2017 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCRT_SMALL_BUFFER_H_ ++#define CORE_FXCRT_SMALL_BUFFER_H_ ++ ++#include ++ ++#include ++#include ++ ++#include "core/fxcrt/fx_memory_wrappers.h" ++ ++namespace fxcrt { ++ ++template ++class SmallBuffer { ++ public: ++ explicit SmallBuffer(size_t actual_size) : m_pSize(actual_size) { ++ if (actual_size > FixedSize) { ++ m_pDynamicData.reset(FX_Alloc(T, actual_size)); ++ return; ++ } ++ if (actual_size) ++ memset(m_FixedData.data(), 0, sizeof(T) * actual_size); ++ } ++ size_t size() const { return m_pSize; } ++ T* data() { ++ return m_pDynamicData ? m_pDynamicData.get() : m_FixedData.data(); ++ } ++ T* begin() { return data(); } ++ T* end() { return begin() + size(); } ++ ++ // Callers shouldn't need to know these details. ++ T* fixed_for_test() { return m_FixedData.data(); } ++ T* dynamic_for_test() { return m_pDynamicData.get(); } ++ ++ private: ++ const size_t m_pSize; ++ std::unique_ptr m_pDynamicData; ++ std::array m_FixedData; ++}; ++ ++} // namespace fxcrt ++ ++#endif // CORE_FXCRT_SMALL_BUFFER_H_ +diff --git a/core/fxcrt/small_buffer_unittest.cpp b/core/fxcrt/small_buffer_unittest.cpp +new file mode 100644 +index 000000000..cbf4bdffc +--- /dev/null ++++ b/core/fxcrt/small_buffer_unittest.cpp +@@ -0,0 +1,56 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/small_buffer.h" ++ ++#include ++ ++#include "testing/gtest/include/gtest/gtest.h" ++ ++namespace fxcrt { ++ ++TEST(SmallBuffer, Empty) { ++ SmallBuffer buffer(0); ++ EXPECT_EQ(buffer.data(), buffer.fixed_for_test()); ++ EXPECT_EQ(buffer.begin(), buffer.end()); ++} ++ ++TEST(SmallBuffer, NoFixed) { ++ SmallBuffer buffer(4); ++ EXPECT_EQ(buffer.data(), buffer.dynamic_for_test()); ++ std::fill(buffer.begin(), buffer.end(), 42); ++ int* ptr = buffer.data(); ++ EXPECT_EQ(42, ptr[0]); ++ EXPECT_EQ(42, ptr[1]); ++ EXPECT_EQ(42, ptr[2]); ++ EXPECT_EQ(42, ptr[3]); ++} ++ ++TEST(SmallBuffer, NoFixedEmpty) { ++ SmallBuffer buffer(0); ++ EXPECT_EQ(buffer.data(), buffer.fixed_for_test()); ++ EXPECT_EQ(buffer.begin(), buffer.end()); ++} ++ ++TEST(SmallBuffer, Fixed) { ++ SmallBuffer buffer(2); ++ EXPECT_EQ(buffer.data(), buffer.fixed_for_test()); ++ std::fill(buffer.begin(), buffer.end(), 42); ++ int* ptr = buffer.data(); ++ EXPECT_EQ(42, ptr[0]); ++ EXPECT_EQ(42, ptr[1]); ++} ++ ++TEST(SmallBuffer, Dynamic) { ++ SmallBuffer buffer(4); ++ EXPECT_EQ(buffer.data(), buffer.dynamic_for_test()); ++ std::fill(buffer.begin(), buffer.end(), 42); ++ int* ptr = buffer.data(); ++ EXPECT_EQ(42, ptr[0]); ++ EXPECT_EQ(42, ptr[1]); ++ EXPECT_EQ(42, ptr[2]); ++ EXPECT_EQ(42, ptr[3]); ++} ++ ++} // namespace fxcrt +diff --git a/core/fxcrt/span_util.h b/core/fxcrt/span_util.h +new file mode 100644 +index 000000000..c91161709 +--- /dev/null ++++ b/core/fxcrt/span_util.h +@@ -0,0 +1,47 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXCRT_SPAN_UTIL_H_ ++#define CORE_FXCRT_SPAN_UTIL_H_ ++ ++#include ++ ++#include "third_party/base/check_op.h" ++#include "third_party/base/span.h" ++ ++namespace fxcrt { ++ ++// Bounds-checked copies from spans into spans. ++template > ++void spancpy(pdfium::span dst, pdfium::span src) { ++ CHECK_GE(dst.size_bytes(), src.size_bytes()); ++ memcpy(dst.data(), src.data(), src.size_bytes()); ++} ++ ++// Bounds-checked moves from spans into spans. ++template > ++void spanmove(pdfium::span dst, pdfium::span src) { ++ CHECK_GE(dst.size_bytes(), src.size_bytes()); ++ memmove(dst.data(), src.data(), src.size_bytes()); ++} ++ ++// Bounds-checked sets into spans. ++template ++void spanset(pdfium::span dst, uint8_t val) { ++ memset(dst.data(), val, dst.size_bytes()); ++} ++ ++// Bounds-checked zeroing of spans. ++template ++void spanclr(pdfium::span dst) { ++ memset(dst.data(), 0, dst.size_bytes()); ++} ++ ++} // namespace fxcrt ++ ++#endif // CORE_FXCRT_SPAN_UTIL_H_ +diff --git a/core/fxcrt/span_util_unittest.cpp b/core/fxcrt/span_util_unittest.cpp +new file mode 100644 +index 000000000..5718ed87d +--- /dev/null ++++ b/core/fxcrt/span_util_unittest.cpp +@@ -0,0 +1,91 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/span_util.h" ++ ++#include ++ ++#include "testing/gtest/include/gtest/gtest.h" ++ ++TEST(Spanset, Fits) { ++ std::vector dst(4, 'B'); ++ fxcrt::spanset(pdfium::make_span(dst).first(2), 'A'); ++ EXPECT_EQ(dst[0], 'A'); ++ EXPECT_EQ(dst[1], 'A'); ++ EXPECT_EQ(dst[2], 'B'); ++ EXPECT_EQ(dst[3], 'B'); ++} ++ ++TEST(Spanset, Empty) { ++ std::vector dst(4, 'B'); ++ fxcrt::spanset(pdfium::make_span(dst).subspan(4), 'A'); ++ EXPECT_EQ(dst[0], 'B'); ++ EXPECT_EQ(dst[1], 'B'); ++ EXPECT_EQ(dst[2], 'B'); ++ EXPECT_EQ(dst[3], 'B'); ++} ++ ++TEST(Spancpy, FitsEntirely) { ++ std::vector src(4, 'A'); ++ std::vector dst(4, 'B'); ++ fxcrt::spancpy(pdfium::make_span(dst), pdfium::make_span(src)); ++ EXPECT_EQ(dst[0], 'A'); ++ EXPECT_EQ(dst[1], 'A'); ++ EXPECT_EQ(dst[2], 'A'); ++ EXPECT_EQ(dst[3], 'A'); ++} ++ ++TEST(Spancpy, FitsWithin) { ++ std::vector src(2, 'A'); ++ std::vector dst(4, 'B'); ++ // Also show that a const src argument is acceptable. ++ fxcrt::spancpy(pdfium::make_span(dst).subspan(1), ++ pdfium::span(src)); ++ EXPECT_EQ(dst[0], 'B'); ++ EXPECT_EQ(dst[1], 'A'); ++ EXPECT_EQ(dst[2], 'A'); ++ EXPECT_EQ(dst[3], 'B'); ++} ++ ++TEST(Spancpy, EmptyCopyWithin) { ++ std::vector src(2, 'A'); ++ std::vector dst(4, 'B'); ++ fxcrt::spancpy(pdfium::make_span(dst).subspan(1), ++ pdfium::make_span(src).subspan(2)); ++ EXPECT_EQ(dst[0], 'B'); ++ EXPECT_EQ(dst[1], 'B'); ++ EXPECT_EQ(dst[2], 'B'); ++ EXPECT_EQ(dst[3], 'B'); ++} ++ ++TEST(Spancpy, EmptyCopyToEmpty) { ++ std::vector src(2, 'A'); ++ std::vector dst(4, 'B'); ++ fxcrt::spancpy(pdfium::make_span(dst).subspan(4), ++ pdfium::make_span(src).subspan(2)); ++ EXPECT_EQ(dst[0], 'B'); ++ EXPECT_EQ(dst[1], 'B'); ++ EXPECT_EQ(dst[2], 'B'); ++ EXPECT_EQ(dst[3], 'B'); ++} ++ ++TEST(Spanmove, FitsWithin) { ++ std::vector src(2, 'A'); ++ std::vector dst(4, 'B'); ++ // Also show that a const src argument is acceptable. ++ fxcrt::spanmove(pdfium::make_span(dst).subspan(1), ++ pdfium::span(src)); ++ EXPECT_EQ(dst[0], 'B'); ++ EXPECT_EQ(dst[1], 'A'); ++ EXPECT_EQ(dst[2], 'A'); ++ EXPECT_EQ(dst[3], 'B'); ++} ++ ++TEST(Span, AssignOverOnePastEnd) { ++ std::vector src(2, 'A'); ++ pdfium::span span = pdfium::make_span(src); ++ span = span.subspan(2); ++ span = pdfium::make_span(src); ++ EXPECT_EQ(span.size(), 2u); ++} +diff --git a/core/fxcrt/stl_util.h b/core/fxcrt/stl_util.h +new file mode 100644 +index 000000000..c09730046 +--- /dev/null ++++ b/core/fxcrt/stl_util.h +@@ -0,0 +1,45 @@ ++// Copyright 2015 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXCRT_STL_UTIL_H_ ++#define CORE_FXCRT_STL_UTIL_H_ ++ ++#include ++ ++#include "third_party/base/numerics/safe_conversions.h" ++ ++namespace fxcrt { ++ ++// Means of generating a key for searching STL collections of std::unique_ptr ++// that avoids the side effect of deleting the pointer. ++template ++class FakeUniquePtr : public std::unique_ptr { ++ public: ++ using std::unique_ptr::unique_ptr; ++ ~FakeUniquePtr() { std::unique_ptr::release(); } ++}; ++ ++// Type-deducing wrapper for FakeUniquePtr. ++template ++FakeUniquePtr MakeFakeUniquePtr(T* arg) { ++ return FakeUniquePtr(arg); ++} ++ ++// Convenience routine for "int-fected" code, so that the stl collection ++// size_t size() method return values will be checked. ++template ++ResultType CollectionSize(const Collection& collection) { ++ return pdfium::base::checked_cast(collection.size()); ++} ++ ++// Convenience routine for "int-fected" code, to handle signed indicies. The ++// compiler can deduce the type, making this more convenient than the above. ++template ++bool IndexInBounds(const Collection& collection, IndexType index) { ++ return index >= 0 && index < CollectionSize(collection); ++} ++ ++} // namespace fxcrt ++ ++#endif // CORE_FXCRT_STL_UTIL_H_ +diff --git a/core/fxcrt/string_data_template.cpp b/core/fxcrt/string_data_template.cpp +new file mode 100644 +index 000000000..6e750075b +--- /dev/null ++++ b/core/fxcrt/string_data_template.cpp +@@ -0,0 +1,103 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxcrt/string_data_template.h" ++ ++#include ++ ++#include ++ ++#include "core/fxcrt/fx_memory.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++ ++namespace fxcrt { ++ ++// static ++template ++StringDataTemplate* StringDataTemplate::Create( ++ size_t nLen) { ++ DCHECK_GT(nLen, 0); ++ ++ // Calculate space needed for the fixed portion of the struct plus the ++ // NUL char that is not included in |m_nAllocLength|. ++ int overhead = offsetof(StringDataTemplate, m_String) + sizeof(CharType); ++ FX_SAFE_SIZE_T nSize = nLen; ++ nSize *= sizeof(CharType); ++ nSize += overhead; ++ ++ // Now round to an 16-byte boundary, assuming the underlying allocator is most ++ // likely PartitionAlloc, which has 16 byte chunks. This will help with cases ++ // where we can save a re-alloc when adding a few characters to a string by ++ // using this otherwise wasted space. ++ nSize += 15; ++ nSize &= ~15; ++ size_t totalSize = nSize.ValueOrDie(); ++ size_t usableLen = (totalSize - overhead) / sizeof(CharType); ++ DCHECK(usableLen >= nLen); ++ ++ void* pData = FX_StringAlloc(char, totalSize); ++ return new (pData) StringDataTemplate(nLen, usableLen); ++} ++ ++// static ++template ++StringDataTemplate* StringDataTemplate::Create( ++ const CharType* pStr, ++ size_t nLen) { ++ StringDataTemplate* result = Create(nLen); ++ result->CopyContents(pStr, nLen); ++ return result; ++} ++ ++template ++void StringDataTemplate::Release() { ++ if (--m_nRefs <= 0) ++ FX_Free(this); ++} ++ ++template ++void StringDataTemplate::CopyContents( ++ const StringDataTemplate& other) { ++ DCHECK(other.m_nDataLength <= m_nAllocLength); ++ memcpy(m_String, other.m_String, ++ (other.m_nDataLength + 1) * sizeof(CharType)); ++} ++ ++template ++void StringDataTemplate::CopyContents(const CharType* pStr, ++ size_t nLen) { ++ DCHECK_GE(nLen, 0); ++ DCHECK_LE(nLen, m_nAllocLength); ++ memcpy(m_String, pStr, nLen * sizeof(CharType)); ++ m_String[nLen] = 0; ++} ++ ++template ++void StringDataTemplate::CopyContentsAt(size_t offset, ++ const CharType* pStr, ++ size_t nLen) { ++ DCHECK_GE(offset, 0); ++ DCHECK_GE(nLen, 0); ++ DCHECK_LE(offset + nLen, m_nAllocLength); ++ memcpy(m_String + offset, pStr, nLen * sizeof(CharType)); ++ m_String[offset + nLen] = 0; ++} ++ ++template ++StringDataTemplate::StringDataTemplate(size_t dataLen, ++ size_t allocLen) ++ : m_nDataLength(dataLen), m_nAllocLength(allocLen) { ++ DCHECK_GE(dataLen, 0); ++ DCHECK_LE(dataLen, allocLen); ++ m_String[dataLen] = 0; ++} ++ ++template class StringDataTemplate; ++template class StringDataTemplate; ++ ++} // namespace fxcrt +diff --git a/core/fxcrt/string_data_template.h b/core/fxcrt/string_data_template.h +index cda41e0aa..bc5cac6fb 100644 +--- a/core/fxcrt/string_data_template.h ++++ b/core/fxcrt/string_data_template.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,103 +7,46 @@ + #ifndef CORE_FXCRT_STRING_DATA_TEMPLATE_H_ + #define CORE_FXCRT_STRING_DATA_TEMPLATE_H_ + +-#include "core/fxcrt/fx_memory.h" +-#include "core/fxcrt/fx_system.h" +-#include "third_party/base/numerics/safe_math.h" ++#include ++#include + + namespace fxcrt { + + template + class StringDataTemplate { + public: +- static StringDataTemplate* Create(size_t nLen) { +- ASSERT(nLen > 0); +- +- // Calculate space needed for the fixed portion of the struct plus the +- // NUL char that is not included in |m_nAllocLength|. +- int overhead = offsetof(StringDataTemplate, m_String) + sizeof(CharType); +- pdfium::base::CheckedNumeric nSize = nLen; +- nSize *= sizeof(CharType); +- nSize += overhead; +- +- // Now round to an 8-byte boundary. We'd expect that this is the minimum +- // granularity of any of the underlying allocators, so there may be cases +- // where we can save a re-alloc when adding a few characters to a string +- // by using this otherwise wasted space. +- nSize += 7; +- nSize &= ~7; +- size_t totalSize = nSize.ValueOrDie(); +- size_t usableLen = (totalSize - overhead) / sizeof(CharType); +- ASSERT(usableLen >= nLen); +- +- void* pData = GetStringPartitionAllocator().root()->Alloc( +- totalSize, "StringDataTemplate"); +- return new (pData) StringDataTemplate(nLen, usableLen); +- } +- +- static StringDataTemplate* Create(const CharType* pStr, size_t nLen) { +- StringDataTemplate* result = Create(nLen); +- result->CopyContents(pStr, nLen); +- return result; +- } ++ static StringDataTemplate* Create(size_t nLen); ++ static StringDataTemplate* Create(const CharType* pStr, size_t nLen); + + void Retain() { ++m_nRefs; } +- void Release() { +- if (--m_nRefs <= 0) +- GetStringPartitionAllocator().root()->Free(this); +- } ++ void Release(); + + bool CanOperateInPlace(size_t nTotalLen) const { + return m_nRefs <= 1 && nTotalLen <= m_nAllocLength; + } + +- void CopyContents(const StringDataTemplate& other) { +- ASSERT(other.m_nDataLength <= m_nAllocLength); +- memcpy(m_String, other.m_String, +- (other.m_nDataLength + 1) * sizeof(CharType)); +- } +- +- void CopyContents(const CharType* pStr, size_t nLen) { +- ASSERT(nLen >= 0); +- ASSERT(nLen <= m_nAllocLength); +- +- memcpy(m_String, pStr, nLen * sizeof(CharType)); +- m_String[nLen] = 0; +- } +- +- void CopyContentsAt(size_t offset, const CharType* pStr, size_t nLen) { +- ASSERT(offset >= 0); +- ASSERT(nLen >= 0); +- ASSERT(offset + nLen <= m_nAllocLength); +- +- memcpy(m_String + offset, pStr, nLen * sizeof(CharType)); +- m_String[offset + nLen] = 0; +- } ++ void CopyContents(const StringDataTemplate& other); ++ void CopyContents(const CharType* pStr, size_t nLen); ++ void CopyContentsAt(size_t offset, const CharType* pStr, size_t nLen); + + // To ensure ref counts do not overflow, consider the worst possible case: + // the entire address space contains nothing but pointers to this object. + // Since the count increments with each new pointer, the largest value is + // the number of pointers that can fit into the address space. The size of + // the address space itself is a good upper bound on it. +- intptr_t m_nRefs; ++ intptr_t m_nRefs = 0; + + // These lengths are in terms of number of characters, not bytes, and do not + // include the terminating NUL character, but the underlying buffer is sized + // to be capable of holding it. + size_t m_nDataLength; +- size_t m_nAllocLength; ++ const size_t m_nAllocLength; + + // Not really 1, variable size. + CharType m_String[1]; + + private: +- StringDataTemplate(size_t dataLen, size_t allocLen) +- : m_nRefs(0), m_nDataLength(dataLen), m_nAllocLength(allocLen) { +- ASSERT(dataLen >= 0); +- ASSERT(dataLen <= allocLen); +- m_String[dataLen] = 0; +- } +- ++ StringDataTemplate(size_t dataLen, size_t allocLen); + ~StringDataTemplate() = delete; + }; + +diff --git a/core/fxcrt/string_pool_template.h b/core/fxcrt/string_pool_template.h +index 2f9fb09a9..7ccb7a517 100644 +--- a/core/fxcrt/string_pool_template.h ++++ b/core/fxcrt/string_pool_template.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcrt/string_pool_template_unittest.cpp b/core/fxcrt/string_pool_template_unittest.cpp +index 717e33d91..2b9143c10 100644 +--- a/core/fxcrt/string_pool_template_unittest.cpp ++++ b/core/fxcrt/string_pool_template_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -17,8 +17,8 @@ TEST(StringPool, ByteString) { + ByteString goats2("goats"); + + // Underlying storage, if non-null, is not shared. +- EXPECT_EQ(nullptr, null1.m_pData.Get()); +- EXPECT_EQ(nullptr, null2.m_pData.Get()); ++ EXPECT_FALSE(null1.m_pData.Get()); ++ EXPECT_FALSE(null2.m_pData.Get()); + EXPECT_NE(goats1.m_pData, goats2.m_pData); + + ByteString interned_null1 = pool.Intern(null1); +@@ -33,8 +33,8 @@ TEST(StringPool, ByteString) { + EXPECT_EQ(goats2, interned_goats2); + + // Interned underlying storage, if non-null, belongs to first seen. +- EXPECT_EQ(nullptr, interned_null1.m_pData.Get()); +- EXPECT_EQ(nullptr, interned_null2.m_pData.Get()); ++ EXPECT_FALSE(interned_null1.m_pData.Get()); ++ EXPECT_FALSE(interned_null2.m_pData.Get()); + EXPECT_EQ(goats1.m_pData, interned_goats1.m_pData); + EXPECT_EQ(goats1.m_pData, interned_goats2.m_pData); + +@@ -45,8 +45,8 @@ TEST(StringPool, ByteString) { + ByteString reinterned_goats1 = pool.Intern(goats2); + + // After clearing pool, storage was re-interned using second strings. +- EXPECT_EQ(nullptr, interned_null1.m_pData.Get()); +- EXPECT_EQ(nullptr, interned_null2.m_pData.Get()); ++ EXPECT_FALSE(interned_null1.m_pData.Get()); ++ EXPECT_FALSE(interned_null2.m_pData.Get()); + EXPECT_EQ(goats2.m_pData, reinterned_goats1.m_pData); + EXPECT_EQ(goats2.m_pData, reinterned_goats2.m_pData); + } +@@ -60,8 +60,8 @@ TEST(StringPool, WideString) { + WideString goats2(L"goats"); + + // Underlying storage, if non-null, is not shared. +- EXPECT_EQ(nullptr, null1.m_pData.Get()); +- EXPECT_EQ(nullptr, null2.m_pData.Get()); ++ EXPECT_FALSE(null1.m_pData.Get()); ++ EXPECT_FALSE(null2.m_pData.Get()); + EXPECT_NE(goats1.m_pData, goats2.m_pData); + + WideString interned_null1 = pool.Intern(null1); +@@ -76,8 +76,8 @@ TEST(StringPool, WideString) { + EXPECT_EQ(goats2, interned_goats2); + + // Interned underlying storage, if non-null, belongs to first seen. +- EXPECT_EQ(nullptr, interned_null1.m_pData.Get()); +- EXPECT_EQ(nullptr, interned_null2.m_pData.Get()); ++ EXPECT_FALSE(interned_null1.m_pData.Get()); ++ EXPECT_FALSE(interned_null2.m_pData.Get()); + EXPECT_EQ(goats1.m_pData, interned_goats1.m_pData); + EXPECT_EQ(goats1.m_pData, interned_goats2.m_pData); + +@@ -88,8 +88,8 @@ TEST(StringPool, WideString) { + WideString reinterned_goats1 = pool.Intern(goats2); + + // After clearing pool, storage was re-interned using second strings. +- EXPECT_EQ(nullptr, interned_null1.m_pData.Get()); +- EXPECT_EQ(nullptr, interned_null2.m_pData.Get()); ++ EXPECT_FALSE(interned_null1.m_pData.Get()); ++ EXPECT_FALSE(interned_null2.m_pData.Get()); + EXPECT_EQ(goats2.m_pData, reinterned_goats1.m_pData); + EXPECT_EQ(goats2.m_pData, reinterned_goats2.m_pData); + } +diff --git a/core/fxcrt/string_view_template.h b/core/fxcrt/string_view_template.h +index e50a71d68..67cd3fff4 100644 +--- a/core/fxcrt/string_view_template.h ++++ b/core/fxcrt/string_view_template.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,15 +7,15 @@ + #ifndef CORE_FXCRT_STRING_VIEW_TEMPLATE_H_ + #define CORE_FXCRT_STRING_VIEW_TEMPLATE_H_ + ++#include ++ + #include + #include + #include +-#include + + #include "core/fxcrt/fx_system.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + #include "third_party/base/span.h" +-#include "third_party/base/stl_util.h" + + namespace fxcrt { + +@@ -25,6 +25,11 @@ namespace fxcrt { + // + // String view arguments should be passed by value, since they are small, + // rather than const-ref, even if they are not modified. ++// ++// Front() and Back() tolerate empty strings and must return NUL in those ++// cases. Substr(), First(), and Last() tolerate out-of-range indices and ++// must return an empty string view in those cases. The aim here is allowing ++// callers to avoid range-checking first. + template + class StringViewTemplate { + public: +@@ -43,41 +48,33 @@ class StringViewTemplate { + : m_Span(reinterpret_cast(ptr), + ptr ? FXSYS_len(ptr) : 0) {} + +- constexpr StringViewTemplate(const CharType* ptr, size_t len) noexcept +- : m_Span(reinterpret_cast(ptr), len) {} ++ constexpr StringViewTemplate(const CharType* ptr, size_t size) noexcept ++ : m_Span(reinterpret_cast(ptr), size) {} ++ ++ template ::value>::type> ++ constexpr StringViewTemplate(const UnsignedType* ptr, size_t size) noexcept ++ : m_Span(ptr, size) {} + + explicit constexpr StringViewTemplate( + const pdfium::span& other) noexcept +- : m_Span(reinterpret_cast(other.data()), ++ : m_Span(!other.empty() ++ ? reinterpret_cast(other.data()) ++ : nullptr, + other.size()) {} + +- template ++ template ::value>::type> + constexpr StringViewTemplate( +- const UnsignedType* ptr, +- size_t size, +- typename std::enable_if::value>::type* = +- 0) noexcept +- : m_Span(ptr, size) {} +- +- template +- StringViewTemplate( +- const pdfium::span other, +- typename std::enable_if::value>::type* = +- 0) noexcept +- : m_Span(other) {} ++ const pdfium::span& other) noexcept ++ : m_Span(!other.empty() ? other.data() : nullptr, other.size()) {} + +- // Deliberately implicit to avoid calling on every string literal. ++ // Deliberately implicit to avoid calling on every char literal. + // |ch| must be an lvalue that outlives the StringViewTemplate. + // NOLINTNEXTLINE(runtime/explicit) +- constexpr StringViewTemplate(CharType& ch) noexcept ++ constexpr StringViewTemplate(const CharType& ch) noexcept + : m_Span(reinterpret_cast(&ch), 1) {} + +- // Any changes to |vec| invalidate the string. +- template +- explicit StringViewTemplate( +- const std::vector& vec) noexcept +- : m_Span(!vec.empty() ? vec.data() : nullptr, vec.size()) {} +- + StringViewTemplate& operator=(const CharType* src) { + m_Span = pdfium::span( + reinterpret_cast(src), src ? FXSYS_len(src) : 0); +@@ -184,19 +181,26 @@ class StringViewTemplate { + return !m_Span.empty() ? m_Span[m_Span.size() - 1] : 0; + } + +- const CharType CharAt(const size_t index) const { ++ CharType CharAt(const size_t index) const { + return static_cast(m_Span[index]); + } + +- Optional Find(CharType ch) const { ++ absl::optional Find(CharType ch) const { + const auto* found = reinterpret_cast(FXSYS_chr( + reinterpret_cast(m_Span.data()), ch, m_Span.size())); + +- return found ? Optional(found - m_Span.data()) : Optional(); ++ return found ? absl::optional(found - m_Span.data()) ++ : absl::nullopt; + } + + bool Contains(CharType ch) const { return Find(ch).has_value(); } + ++ StringViewTemplate Substr(size_t offset) const { ++ // Unsigned underflow is well-defined and out-of-range is handled by ++ // Substr(). ++ return Substr(offset, GetLength() - offset); ++ } ++ + StringViewTemplate Substr(size_t first, size_t count) const { + if (!m_Span.data()) + return StringViewTemplate(); +@@ -214,14 +218,12 @@ class StringViewTemplate { + } + + StringViewTemplate First(size_t count) const { +- if (count == 0 || !IsValidLength(count)) +- return StringViewTemplate(); + return Substr(0, count); + } + + StringViewTemplate Last(size_t count) const { +- if (count == 0 || !IsValidLength(count)) +- return StringViewTemplate(); ++ // Unsigned underflow is well-defined and out-of-range is handled by ++ // Substr(). + return Substr(GetLength() - count, count); + } + +@@ -275,12 +277,8 @@ inline bool operator<(const T* lhs, const StringViewTemplate& rhs) { + return rhs > lhs; + } + +-// Workaround for one of the cases external template classes are +-// failing in GCC before version 7 with -O0 +-#if !defined(__GNUC__) || __GNUC__ >= 7 + extern template class StringViewTemplate; + extern template class StringViewTemplate; +-#endif + + using ByteStringView = StringViewTemplate; + using WideStringView = StringViewTemplate; +diff --git a/core/fxcrt/timerhandler_iface.h b/core/fxcrt/timerhandler_iface.h +deleted file mode 100644 +index 04e781d85..000000000 +--- a/core/fxcrt/timerhandler_iface.h ++++ /dev/null +@@ -1,29 +0,0 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXCRT_TIMERHANDLER_IFACE_H_ +-#define CORE_FXCRT_TIMERHANDLER_IFACE_H_ +- +-#include "core/fxcrt/fx_system.h" +- +-namespace fxcrt { +- +-class TimerHandlerIface { +- public: +- static constexpr int32_t kInvalidTimerID = 0; +- using TimerCallback = void (*)(int32_t idEvent); +- +- virtual ~TimerHandlerIface() = default; +- +- virtual int32_t SetTimer(int32_t uElapse, TimerCallback lpTimerFunc) = 0; +- virtual void KillTimer(int32_t nTimerID) = 0; +-}; +- +-} // namespace fxcrt +- +-using fxcrt::TimerHandlerIface; +- +-#endif // CORE_FXCRT_TIMERHANDLER_IFACE_H_ +diff --git a/core/fxcrt/tree_node.h b/core/fxcrt/tree_node.h +index 5b1d7dfd4..d30b6a405 100644 +--- a/core/fxcrt/tree_node.h ++++ b/core/fxcrt/tree_node.h +@@ -1,30 +1,42 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #ifndef CORE_FXCRT_TREE_NODE_H_ + #define CORE_FXCRT_TREE_NODE_H_ + +-#include "core/fxcrt/fx_system.h" +-#include "third_party/base/logging.h" ++#include ++ ++#include "third_party/base/check.h" + + namespace fxcrt { + +-// Implements the usual DOM/XML-ish trees. ++// Implements the usual DOM/XML-ish trees allowing for a variety of ++// pointer types with which to connect the nodes. Public methods maintain ++// the invariants of the tree. ++ + template +-class TreeNode { ++class TreeNodeBase { + public: +- TreeNode() = default; +- virtual ~TreeNode() = default; ++ TreeNodeBase() = default; ++ virtual ~TreeNodeBase() = default; + +- T* GetParent() const { return m_pParent; } +- T* GetFirstChild() const { return m_pFirstChild; } +- T* GetLastChild() const { return m_pLastChild; } +- T* GetNextSibling() const { return m_pNextSibling; } +- T* GetPrevSibling() const { return m_pPrevSibling; } ++ inline T* GetParent() const { return static_cast(this)->m_pParent; } ++ inline T* GetFirstChild() const { ++ return static_cast(this)->m_pFirstChild; ++ } ++ inline T* GetLastChild() const { ++ return static_cast(this)->m_pLastChild; ++ } ++ inline T* GetNextSibling() const { ++ return static_cast(this)->m_pNextSibling; ++ } ++ inline T* GetPrevSibling() const { ++ return static_cast(this)->m_pPrevSibling; ++ } + + bool HasChild(const T* child) const { +- return child != this && child->m_pParent == this; ++ return child != this && child->GetParent() == this; + } + + T* GetNthChild(int32_t n) { +@@ -39,29 +51,29 @@ class TreeNode { + + void AppendFirstChild(T* child) { + BecomeParent(child); +- if (m_pFirstChild) { +- CHECK(m_pLastChild); +- m_pFirstChild->m_pPrevSibling = child; +- child->m_pNextSibling = m_pFirstChild; +- m_pFirstChild = child; ++ if (GetFirstChild()) { ++ CHECK(GetLastChild()); ++ GetFirstChild()->SetPrevSibling(child); ++ child->SetNextSibling(GetFirstChild()); ++ SetFirstChild(child); + } else { +- CHECK(!m_pLastChild); +- m_pFirstChild = child; +- m_pLastChild = child; ++ CHECK(!GetLastChild()); ++ SetFirstChild(child); ++ SetLastChild(child); + } + } + + void AppendLastChild(T* child) { + BecomeParent(child); +- if (m_pLastChild) { +- CHECK(m_pFirstChild); +- m_pLastChild->m_pNextSibling = child; +- child->m_pPrevSibling = m_pLastChild; +- m_pLastChild = child; ++ if (GetLastChild()) { ++ CHECK(GetFirstChild()); ++ GetLastChild()->SetNextSibling(child); ++ child->SetPrevSibling(GetLastChild()); ++ SetLastChild(child); + } else { +- CHECK(!m_pFirstChild); +- m_pFirstChild = child; +- m_pLastChild = child; ++ CHECK(!GetFirstChild()); ++ SetFirstChild(child); ++ SetLastChild(child); + } + } + +@@ -72,13 +84,13 @@ class TreeNode { + } + BecomeParent(child); + CHECK(HasChild(other)); +- child->m_pNextSibling = other; +- child->m_pPrevSibling = other->m_pPrevSibling; +- if (m_pFirstChild == other) { +- CHECK(!other->m_pPrevSibling); +- m_pFirstChild = child; ++ child->SetNextSibling(other); ++ child->SetPrevSibling(other->GetPrevSibling()); ++ if (GetFirstChild() == other) { ++ CHECK(!other->GetPrevSibling()); ++ SetFirstChild(child); + } else { +- other->m_pPrevSibling->m_pNextSibling = child; ++ other->GetPrevSibling()->SetNextSibling(child); + } + other->m_pPrevSibling = child; + } +@@ -90,34 +102,34 @@ class TreeNode { + } + BecomeParent(child); + CHECK(HasChild(other)); +- child->m_pNextSibling = other->m_pNextSibling; +- child->m_pPrevSibling = other; +- if (m_pLastChild == other) { +- CHECK(!other->m_pNextSibling); +- m_pLastChild = child; ++ child->SetNextSibling(other->GetNextSibling()); ++ child->SetPrevSibling(other); ++ if (GetLastChild() == other) { ++ CHECK(!other->GetNextSibling()); ++ SetLastChild(child); + } else { +- other->m_pNextSibling->m_pPrevSibling = child; ++ other->GetNextSibling()->SetPrevSibling(child); + } +- other->m_pNextSibling = child; ++ other->SetNextSibling(child); + } + + void RemoveChild(T* child) { + CHECK(HasChild(child)); +- if (m_pLastChild == child) { +- CHECK(!child->m_pNextSibling); +- m_pLastChild = child->m_pPrevSibling; ++ if (GetLastChild() == child) { ++ CHECK(!child->GetNextSibling()); ++ SetLastChild(child->GetPrevSibling()); + } else { +- child->m_pNextSibling->m_pPrevSibling = child->m_pPrevSibling; ++ child->GetNextSibling()->SetPrevSibling(child->GetPrevSibling()); + } +- if (m_pFirstChild == child) { +- CHECK(!child->m_pPrevSibling); +- m_pFirstChild = child->m_pNextSibling; ++ if (GetFirstChild() == child) { ++ CHECK(!child->GetPrevSibling()); ++ SetFirstChild(child->GetNextSibling()); + } else { +- child->m_pPrevSibling->m_pNextSibling = child->m_pNextSibling; ++ child->GetPrevSibling()->SetNextSibling(child->GetNextSibling()); + } +- child->m_pParent = nullptr; +- child->m_pPrevSibling = nullptr; +- child->m_pNextSibling = nullptr; ++ child->SetParent(nullptr); ++ child->SetPrevSibling(nullptr); ++ child->SetNextSibling(nullptr); + } + + void RemoveAllChildren() { +@@ -131,15 +143,44 @@ class TreeNode { + } + + private: ++ // These are private because they may leave the tree in an invalid state ++ // until subsequent operations restore it. ++ inline void SetParent(T* pParent) { ++ static_cast(this)->m_pParent = pParent; ++ } ++ inline void SetFirstChild(T* pChild) { ++ static_cast(this)->m_pFirstChild = pChild; ++ } ++ inline void SetLastChild(T* pChild) { ++ static_cast(this)->m_pLastChild = pChild; ++ } ++ inline void SetNextSibling(T* pSibling) { ++ static_cast(this)->m_pNextSibling = pSibling; ++ } ++ inline void SetPrevSibling(T* pSibling) { ++ static_cast(this)->m_pPrevSibling = pSibling; ++ } ++ + // Child left in state where sibling members need subsequent adjustment. + void BecomeParent(T* child) { + CHECK(child != this); // Detect attempts at self-insertion. + if (child->m_pParent) +- child->m_pParent->TreeNode::RemoveChild(child); ++ child->m_pParent->TreeNodeBase::RemoveChild(child); + child->m_pParent = static_cast(this); + CHECK(!child->m_pNextSibling); + CHECK(!child->m_pPrevSibling); + } ++}; ++ ++// Tree connected using C-style pointers. ++template ++class TreeNode : public TreeNodeBase { ++ public: ++ TreeNode() = default; ++ virtual ~TreeNode() = default; ++ ++ private: ++ friend class TreeNodeBase; + + T* m_pParent = nullptr; // Raw, intra-tree pointer. + T* m_pFirstChild = nullptr; // Raw, intra-tree pointer. +diff --git a/core/fxcrt/tree_node_unittest.cpp b/core/fxcrt/tree_node_unittest.cpp +index 26dd610ce..ecd5b09f2 100644 +--- a/core/fxcrt/tree_node_unittest.cpp ++++ b/core/fxcrt/tree_node_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,6 @@ + #include + + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + + namespace fxcrt { + +@@ -17,114 +16,114 @@ class TestTreeNode : public TreeNode {}; + // These tests check that we trip CHECKS given bad calls. + + TEST(TreeNode, SelfAppendFirstChild) { +- auto pNode = pdfium::MakeUnique(); ++ auto pNode = std::make_unique(); + EXPECT_DEATH(pNode->AppendFirstChild(pNode.get()), ""); + } + + TEST(TreeNode, SelfAppendLastChild) { +- auto pNode = pdfium::MakeUnique(); ++ auto pNode = std::make_unique(); + EXPECT_DEATH(pNode->AppendLastChild(pNode.get()), ""); + } + + TEST(TreeNode, SelfInsertBeforeOther) { +- auto pNode = pdfium::MakeUnique(); +- auto pOther = pdfium::MakeUnique(); ++ auto pNode = std::make_unique(); ++ auto pOther = std::make_unique(); + pNode->AppendFirstChild(pOther.get()); + EXPECT_DEATH(pNode->InsertBefore(pNode.get(), pOther.get()), ""); + } + + TEST(TreeNode, InsertOtherBeforeSelf) { +- auto pNode = pdfium::MakeUnique(); +- auto pOther = pdfium::MakeUnique(); ++ auto pNode = std::make_unique(); ++ auto pOther = std::make_unique(); + pNode->AppendFirstChild(pOther.get()); + EXPECT_DEATH(pNode->InsertBefore(pOther.get(), pNode.get()), ""); + } + + TEST(TreeNode, SelfInsertAfterOther) { +- auto pNode = pdfium::MakeUnique(); +- auto pOther = pdfium::MakeUnique(); ++ auto pNode = std::make_unique(); ++ auto pOther = std::make_unique(); + pNode->AppendFirstChild(pOther.get()); + EXPECT_DEATH(pNode->InsertBefore(pNode.get(), pOther.get()), ""); + } + + TEST(TreeNode, InsertOtherAfterSelf) { +- auto pNode = pdfium::MakeUnique(); +- auto pOther = pdfium::MakeUnique(); ++ auto pNode = std::make_unique(); ++ auto pOther = std::make_unique(); + pNode->AppendFirstChild(pOther.get()); + EXPECT_DEATH(pNode->InsertBefore(pOther.get(), pNode.get()), ""); + } + + TEST(TreeNode, RemoveParentless) { +- auto pNode = pdfium::MakeUnique(); ++ auto pNode = std::make_unique(); + EXPECT_DEATH(pNode->GetParent()->RemoveChild(pNode.get()), ""); + } + + TEST(TreeNode, RemoveFromWrongParent) { +- auto pGoodParent = pdfium::MakeUnique(); +- auto pBadParent = pdfium::MakeUnique(); +- auto pNode = pdfium::MakeUnique(); ++ auto pGoodParent = std::make_unique(); ++ auto pBadParent = std::make_unique(); ++ auto pNode = std::make_unique(); + pGoodParent->AppendFirstChild(pNode.get()); + EXPECT_DEATH(pBadParent->RemoveChild(pNode.get()), ""); + } + + TEST(TreeNode, SafeRemove) { +- auto pParent = pdfium::MakeUnique(); +- auto pChild = pdfium::MakeUnique(); ++ auto pParent = std::make_unique(); ++ auto pChild = std::make_unique(); + pParent->AppendFirstChild(pChild.get()); + pChild->RemoveSelfIfParented(); +- EXPECT_EQ(nullptr, pParent->GetFirstChild()); +- EXPECT_EQ(nullptr, pChild->GetParent()); ++ EXPECT_FALSE(pParent->GetFirstChild()); ++ EXPECT_FALSE(pChild->GetParent()); + } + + TEST(TreeNode, SafeRemoveParentless) { +- auto pNode = pdfium::MakeUnique(); ++ auto pNode = std::make_unique(); + pNode->RemoveSelfIfParented(); +- EXPECT_EQ(nullptr, pNode->GetParent()); ++ EXPECT_FALSE(pNode->GetParent()); + } + + TEST(TreeNode, RemoveAllChildren) { +- auto pParent = pdfium::MakeUnique(); ++ auto pParent = std::make_unique(); + pParent->RemoveAllChildren(); +- EXPECT_EQ(nullptr, pParent->GetFirstChild()); ++ EXPECT_FALSE(pParent->GetFirstChild()); + +- auto p0 = pdfium::MakeUnique(); +- auto p1 = pdfium::MakeUnique(); +- auto p2 = pdfium::MakeUnique(); +- auto p3 = pdfium::MakeUnique(); ++ auto p0 = std::make_unique(); ++ auto p1 = std::make_unique(); ++ auto p2 = std::make_unique(); ++ auto p3 = std::make_unique(); + pParent->AppendLastChild(p0.get()); + pParent->AppendLastChild(p1.get()); + pParent->AppendLastChild(p2.get()); + pParent->AppendLastChild(p3.get()); + pParent->RemoveAllChildren(); +- EXPECT_EQ(nullptr, pParent->GetFirstChild()); ++ EXPECT_FALSE(pParent->GetFirstChild()); + } + + TEST(TreeNode, NthChild) { +- auto pParent = pdfium::MakeUnique(); +- EXPECT_EQ(nullptr, pParent->GetNthChild(-1)); +- EXPECT_EQ(nullptr, pParent->GetNthChild(0)); +- +- auto p0 = pdfium::MakeUnique(); +- auto p1 = pdfium::MakeUnique(); +- auto p2 = pdfium::MakeUnique(); +- auto p3 = pdfium::MakeUnique(); ++ auto pParent = std::make_unique(); ++ EXPECT_FALSE(pParent->GetNthChild(-1)); ++ EXPECT_FALSE(pParent->GetNthChild(0)); ++ ++ auto p0 = std::make_unique(); ++ auto p1 = std::make_unique(); ++ auto p2 = std::make_unique(); ++ auto p3 = std::make_unique(); + pParent->AppendLastChild(p0.get()); + pParent->AppendLastChild(p1.get()); + pParent->AppendLastChild(p2.get()); + pParent->AppendLastChild(p3.get()); +- EXPECT_EQ(nullptr, pParent->GetNthChild(-1)); ++ EXPECT_FALSE(pParent->GetNthChild(-1)); + EXPECT_EQ(p0.get(), pParent->GetNthChild(0)); + EXPECT_EQ(p1.get(), pParent->GetNthChild(1)); + EXPECT_EQ(p2.get(), pParent->GetNthChild(2)); + EXPECT_EQ(p3.get(), pParent->GetNthChild(3)); +- EXPECT_EQ(nullptr, pParent->GetNthChild(4)); ++ EXPECT_FALSE(pParent->GetNthChild(4)); + pParent->RemoveAllChildren(); + } + + TEST(TreeNode, AppendFirstChild) { +- auto parent = pdfium::MakeUnique(); +- auto child0 = pdfium::MakeUnique(); +- auto child1 = pdfium::MakeUnique(); ++ auto parent = std::make_unique(); ++ auto child0 = std::make_unique(); ++ auto child1 = std::make_unique(); + parent->AppendFirstChild(child0.get()); + EXPECT_EQ(child0.get(), parent->GetFirstChild()); + parent->AppendFirstChild(child1.get()); +@@ -134,9 +133,9 @@ TEST(TreeNode, AppendFirstChild) { + } + + TEST(TreeNode, RemoveChild) { +- auto parent = pdfium::MakeUnique(); +- auto child0 = pdfium::MakeUnique(); +- auto child1 = pdfium::MakeUnique(); ++ auto parent = std::make_unique(); ++ auto child0 = std::make_unique(); ++ auto child1 = std::make_unique(); + + parent->AppendFirstChild(child0.get()); + parent->AppendLastChild(child1.get()); +@@ -146,8 +145,8 @@ TEST(TreeNode, RemoveChild) { + EXPECT_EQ(child1.get(), parent->GetFirstChild()); + EXPECT_EQ(child1.get(), parent->GetLastChild()); + parent->RemoveChild(child1.get()); +- EXPECT_EQ(nullptr, parent->GetFirstChild()); +- EXPECT_EQ(nullptr, parent->GetLastChild()); ++ EXPECT_FALSE(parent->GetFirstChild()); ++ EXPECT_FALSE(parent->GetLastChild()); + + parent->AppendFirstChild(child0.get()); + parent->AppendLastChild(child1.get()); +@@ -157,8 +156,8 @@ TEST(TreeNode, RemoveChild) { + EXPECT_EQ(child0.get(), parent->GetFirstChild()); + EXPECT_EQ(child0.get(), parent->GetLastChild()); + parent->RemoveChild(child0.get()); +- EXPECT_EQ(nullptr, parent->GetFirstChild()); +- EXPECT_EQ(nullptr, parent->GetLastChild()); ++ EXPECT_FALSE(parent->GetFirstChild()); ++ EXPECT_FALSE(parent->GetLastChild()); + } + + } // namespace fxcrt +diff --git a/core/fxcrt/unowned_ptr.h b/core/fxcrt/unowned_ptr.h +index f7ff4808f..1a2c9bb2b 100644 +--- a/core/fxcrt/unowned_ptr.h ++++ b/core/fxcrt/unowned_ptr.h +@@ -1,15 +1,17 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #ifndef CORE_FXCRT_UNOWNED_PTR_H_ + #define CORE_FXCRT_UNOWNED_PTR_H_ + ++#include + #include +-#include + #include + #include + ++#include "third_party/base/compiler_specific.h" ++ + // UnownedPtr is a smart pointer class that behaves very much like a + // standard C-style pointer. The advantages of using it over raw + // pointers are: +@@ -45,65 +47,105 @@ class span; + namespace fxcrt { + + template +-class UnownedPtr { ++class TRIVIAL_ABI GSL_POINTER UnownedPtr { + public: + constexpr UnownedPtr() noexcept = default; +- constexpr UnownedPtr(const UnownedPtr& that) noexcept = default; +- +- // Move-construct an UnownedPtr. After construction, |that| will be NULL. +- constexpr UnownedPtr(UnownedPtr&& that) noexcept : m_pObj(that.Release()) {} +- +- template +- explicit constexpr UnownedPtr(U* pObj) noexcept : m_pObj(pObj) {} + + // Deliberately implicit to allow returning nullptrs. + // NOLINTNEXTLINE(runtime/explicit) +- constexpr UnownedPtr(std::nullptr_t ptr) noexcept {} ++ constexpr UnownedPtr(std::nullptr_t ptr) {} + +- ~UnownedPtr() { ProbeForLowSeverityLifetimeIssue(); } ++ explicit constexpr UnownedPtr(T* pObj) noexcept : m_pObj(pObj) {} + +- void Reset(T* obj = nullptr) { +- ProbeForLowSeverityLifetimeIssue(); +- m_pObj = obj; ++ // Copy-construct an UnownedPtr. ++ // Required in addition to copy conversion constructor below. ++ constexpr UnownedPtr(const UnownedPtr& that) noexcept ++ : m_pObj(static_cast(that)) {} ++ ++ // Move-construct an UnownedPtr. After construction, |that| will be NULL. ++ // Required in addition to move conversion constructor below. ++ constexpr UnownedPtr(UnownedPtr&& that) noexcept ++ : m_pObj(that.ExtractAsDangling()) {} ++ ++ // Copy-conversion constructor. ++ template ::value>::type> ++ UnownedPtr(const UnownedPtr& that) : UnownedPtr(static_cast(that)) {} ++ ++ // Move-conversion constructor. ++ template ::value>::type> ++ UnownedPtr(UnownedPtr&& that) noexcept { ++ Reset(that.ExtractAsDangling()); ++ } ++ ++ // Assign an UnownedPtr from nullptr. ++ UnownedPtr& operator=(std::nullptr_t) noexcept { ++ Reset(); ++ return *this; + } + ++ // Assign an UnownedPtr from a raw ptr. + UnownedPtr& operator=(T* that) noexcept { + Reset(that); + return *this; + } + ++ // Copy-assign an UnownedPtr. ++ // Required in addition to copy conversion assignment below. + UnownedPtr& operator=(const UnownedPtr& that) noexcept { + if (*this != that) +- Reset(that.Get()); ++ Reset(static_cast(that)); + return *this; + } + + // Move-assign an UnownedPtr. After assignment, |that| will be NULL. ++ // Required in addition to move conversion assignment below. + UnownedPtr& operator=(UnownedPtr&& that) noexcept { + if (*this != that) +- Reset(that.Release()); ++ Reset(that.ExtractAsDangling()); + return *this; + } + +- bool operator==(const UnownedPtr& that) const { return Get() == that.Get(); } +- bool operator!=(const UnownedPtr& that) const { return !(*this == that); } +- bool operator<(const UnownedPtr& that) const { +- return std::less()(Get(), that.Get()); ++ // Copy-convert assignment. ++ template ::value>::type> ++ UnownedPtr& operator=(const UnownedPtr& that) noexcept { ++ if (*this != that) ++ Reset(that); ++ return *this; + } + +- template +- bool operator==(const U* that) const { +- return Get() == that; ++ // Move-convert assignment. After assignment, |that| will be NULL. ++ template ::value>::type> ++ UnownedPtr& operator=(UnownedPtr&& that) noexcept { ++ if (*this != that) ++ Reset(that.ExtractAsDangling()); ++ return *this; + } + +- template +- bool operator!=(const U* that) const { +- return !(*this == that); ++ ~UnownedPtr() { ++ ProbeForLowSeverityLifetimeIssue(); ++ m_pObj = nullptr; + } + +- T* Get() const noexcept { return m_pObj; } ++ bool operator==(std::nullptr_t ptr) const { return m_pObj == nullptr; } ++ bool operator==(const UnownedPtr& that) const { ++ return m_pObj == static_cast(that); ++ } ++ bool operator<(const UnownedPtr& that) const { ++ return std::less()(m_pObj, static_cast(that)); ++ } ++ ++ operator T*() const noexcept { return m_pObj; } ++ T* get() const noexcept { return m_pObj; } + +- T* Release() { ++ T* ExtractAsDangling() { + ProbeForLowSeverityLifetimeIssue(); + T* pTemp = nullptr; + std::swap(pTemp, m_pObj); +@@ -117,6 +159,11 @@ class UnownedPtr { + private: + friend class pdfium::span; + ++ void Reset(T* obj = nullptr) { ++ ProbeForLowSeverityLifetimeIssue(); ++ m_pObj = obj; ++ } ++ + inline void ProbeForLowSeverityLifetimeIssue() { + #if defined(ADDRESS_SANITIZER) + if (m_pObj) +@@ -133,18 +180,19 @@ class UnownedPtr { + T* m_pObj = nullptr; + }; + +-template +-inline bool operator==(const U* lhs, const UnownedPtr& rhs) { +- return rhs == lhs; +-} +- +-template +-inline bool operator!=(const U* lhs, const UnownedPtr& rhs) { +- return rhs != lhs; +-} +- + } // namespace fxcrt + + using fxcrt::UnownedPtr; + ++namespace pdfium { ++ ++// Type-deducing wrapper to make an UnownedPtr from an ordinary pointer, ++// since equivalent constructor is explicit. ++template ++UnownedPtr WrapUnowned(T* that) { ++ return UnownedPtr(that); ++} ++ ++} // namespace pdfium ++ + #endif // CORE_FXCRT_UNOWNED_PTR_H_ +diff --git a/core/fxcrt/unowned_ptr_unittest.cpp b/core/fxcrt/unowned_ptr_unittest.cpp +index fa884d4c0..f0c10ee3f 100644 +--- a/core/fxcrt/unowned_ptr_unittest.cpp ++++ b/core/fxcrt/unowned_ptr_unittest.cpp +@@ -1,64 +1,162 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcrt/unowned_ptr.h" + ++#include ++#include ++#include + #include +-#include + + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/containers/contains.h" + + namespace fxcrt { + namespace { + ++template > ++class NoLinearSearchSet : public std::set { ++ public: ++ typename std::set::iterator begin() noexcept = delete; ++ typename std::set::const_iterator cbegin() const noexcept = delete; ++}; ++ + class Clink { + public: + UnownedPtr next_ = nullptr; + }; + + void DeleteDangling() { +- auto ptr2 = pdfium::MakeUnique(); ++ auto ptr2 = std::make_unique(); + { +- auto ptr1 = pdfium::MakeUnique(); ++ auto ptr1 = std::make_unique(); + ptr2->next_ = ptr1.get(); + } + } + +-void ResetDangling() { +- auto ptr2 = pdfium::MakeUnique(); +- { +- auto ptr1 = pdfium::MakeUnique(); +- ptr2->next_.Reset(ptr1.get()); +- } +- ptr2->next_.Reset(); +-} +- + void AssignDangling() { +- auto ptr2 = pdfium::MakeUnique(); ++ auto ptr2 = std::make_unique(); + { +- auto ptr1 = pdfium::MakeUnique(); ++ auto ptr1 = std::make_unique(); + ptr2->next_ = ptr1.get(); + } + ptr2->next_ = nullptr; + } + + void ReleaseDangling() { +- auto ptr2 = pdfium::MakeUnique(); ++ auto ptr2 = std::make_unique(); + { +- auto ptr1 = pdfium::MakeUnique(); ++ auto ptr1 = std::make_unique(); + ptr2->next_ = ptr1.get(); + } +- ptr2->next_.Release(); ++ ptr2->next_.ExtractAsDangling(); + } + + } // namespace + ++TEST(UnownedPtr, DefaultCtor) { ++ UnownedPtr ptr; ++ EXPECT_FALSE(ptr); ++} ++ ++TEST(UnownedPtr, NullptrCtor) { ++ UnownedPtr ptr(nullptr); ++ EXPECT_FALSE(ptr); ++} ++ ++TEST(UnownedPtr, RawCtor) { ++ auto obj = std::make_unique(); ++ UnownedPtr ptr(obj.get()); ++ EXPECT_EQ(obj.get(), ptr); ++} ++ ++TEST(UnownedPtr, CopyCtor) { ++ std::unique_ptr obj = std::make_unique(); ++ UnownedPtr ptr1(obj.get()); ++ UnownedPtr ptr2(ptr1); ++ EXPECT_EQ(obj.get(), ptr2); ++ EXPECT_EQ(obj.get(), ptr1); ++} ++ ++TEST(UnownedPtr, MoveCtor) { ++ std::unique_ptr obj = std::make_unique(); ++ UnownedPtr ptr1(obj.get()); ++ UnownedPtr ptr2(std::move(ptr1)); ++ EXPECT_EQ(obj.get(), ptr2); ++ EXPECT_FALSE(ptr1); ++} ++ ++TEST(UnownedPtr, CopyConversionCtor) { ++ std::unique_ptr obj = std::make_unique(); ++ UnownedPtr ptr1(obj.get()); ++ UnownedPtr ptr2(ptr1); ++ EXPECT_EQ(obj.get(), ptr2); ++ EXPECT_EQ(obj.get(), ptr1); ++} ++ ++TEST(UnownedPtr, MoveConversionCtor) { ++ std::unique_ptr obj = std::make_unique(); ++ UnownedPtr ptr1(obj.get()); ++ UnownedPtr ptr2(std::move(ptr1)); ++ EXPECT_EQ(obj.get(), ptr2); ++ EXPECT_FALSE(ptr1); ++} ++ ++TEST(UnownedPtr, NullptrAssign) { ++ std::unique_ptr obj = std::make_unique(); ++ UnownedPtr ptr(obj.get()); ++ ptr = nullptr; ++ EXPECT_FALSE(ptr); ++} ++ ++TEST(UnownedPtr, RawAssign) { ++ std::unique_ptr obj = std::make_unique(); ++ UnownedPtr ptr; ++ ptr = obj.get(); ++ EXPECT_EQ(obj.get(), ptr); ++} ++ ++TEST(UnownedPtr, CopyAssign) { ++ std::unique_ptr obj = std::make_unique(); ++ UnownedPtr ptr1(obj.get()); ++ UnownedPtr ptr2; ++ ptr2 = ptr1; ++ EXPECT_EQ(obj.get(), ptr1); ++ EXPECT_EQ(obj.get(), ptr2); ++} ++ ++TEST(UnownedPtr, MoveAssign) { ++ std::unique_ptr obj = std::make_unique(); ++ UnownedPtr ptr1(obj.get()); ++ UnownedPtr ptr2; ++ ptr2 = std::move(ptr1); ++ EXPECT_FALSE(ptr1); ++ EXPECT_EQ(obj.get(), ptr2); ++} ++ ++TEST(UnownedPtr, CopyConversionAssign) { ++ std::unique_ptr obj = std::make_unique(); ++ UnownedPtr ptr1(obj.get()); ++ UnownedPtr ptr2; ++ ptr2 = ptr1; ++ EXPECT_EQ(obj.get(), ptr1); ++ EXPECT_EQ(obj.get(), ptr2); ++} ++ ++TEST(UnownedPtr, MoveConversionAssign) { ++ std::unique_ptr obj = std::make_unique(); ++ UnownedPtr ptr1(obj.get()); ++ UnownedPtr ptr2; ++ ptr2 = std::move(ptr1); ++ EXPECT_FALSE(ptr1); ++ EXPECT_EQ(obj.get(), ptr2); ++} ++ + TEST(UnownedPtr, PtrOk) { +- auto ptr1 = pdfium::MakeUnique(); ++ auto ptr1 = std::make_unique(); + { +- auto ptr2 = pdfium::MakeUnique(); ++ auto ptr2 = std::make_unique(); + ptr2->next_ = ptr1.get(); + } + } +@@ -71,27 +169,10 @@ TEST(UnownedPtr, PtrNotOk) { + #endif + } + +-TEST(UnownedPtr, ResetOk) { +- auto ptr1 = pdfium::MakeUnique(); +- { +- auto ptr2 = pdfium::MakeUnique(); +- ptr2->next_.Reset(ptr1.get()); +- ptr2->next_.Reset(nullptr); +- } +-} +- +-TEST(UnownedPtr, ResetNotOk) { +-#if defined(ADDRESS_SANITIZER) +- EXPECT_DEATH(ResetDangling(), ""); +-#else +- ResetDangling(); +-#endif +-} +- + TEST(UnownedPtr, AssignOk) { +- auto ptr1 = pdfium::MakeUnique(); ++ auto ptr1 = std::make_unique(); + { +- auto ptr2 = pdfium::MakeUnique(); ++ auto ptr2 = std::make_unique(); + ptr2->next_ = ptr1.get(); + ptr2->next_ = nullptr; + } +@@ -106,32 +187,11 @@ TEST(UnownedPtr, AssignNotOk) { + } + + TEST(UnownedPtr, ReleaseOk) { +- auto ptr2 = pdfium::MakeUnique(); ++ auto ptr2 = std::make_unique(); + { +- auto ptr1 = pdfium::MakeUnique(); ++ auto ptr1 = std::make_unique(); + ptr2->next_ = ptr1.get(); +- ptr2->next_.Release(); +- } +-} +- +-TEST(UnownedPtr, MoveCtorOk) { +- UnownedPtr outer; +- { +- auto owned = pdfium::MakeUnique(); +- outer = owned.get(); +- UnownedPtr inner(std::move(outer)); +- EXPECT_EQ(nullptr, outer.Get()); +- } +-} +- +-TEST(UnownedPtr, MoveAssignOk) { +- UnownedPtr outer; +- { +- auto owned = pdfium::MakeUnique(); +- outer = owned.get(); +- UnownedPtr inner; +- inner = std::move(outer); +- EXPECT_EQ(nullptr, outer.Get()); ++ ptr2->next_.ExtractAsDangling(); + } + } + +@@ -187,4 +247,16 @@ TEST(UnownedPtr, OperatorLT) { + EXPECT_FALSE(ptr2 < ptr1); + } + ++TEST(UnownedPtr, TransparentCompare) { ++ int foos[2]; ++ UnownedPtr ptr1(&foos[0]); ++ UnownedPtr ptr2(&foos[1]); ++ NoLinearSearchSet, std::less<>> holder; ++ holder.insert(ptr1); ++ EXPECT_NE(holder.end(), holder.find(&foos[0])); ++ EXPECT_EQ(holder.end(), holder.find(&foos[1])); ++ EXPECT_TRUE(pdfium::Contains(holder, &foos[0])); ++ EXPECT_FALSE(pdfium::Contains(holder, &foos[1])); ++} ++ + } // namespace fxcrt +diff --git a/core/fxcrt/weak_ptr.h b/core/fxcrt/weak_ptr.h +index 7b345aa99..fec4260b9 100644 +--- a/core/fxcrt/weak_ptr.h ++++ b/core/fxcrt/weak_ptr.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,11 @@ + #ifndef CORE_FXCRT_WEAK_PTR_H_ + #define CORE_FXCRT_WEAK_PTR_H_ + +-#include ++#include ++ + #include + #include + +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" + + namespace fxcrt { +diff --git a/core/fxcrt/weak_ptr_unittest.cpp b/core/fxcrt/weak_ptr_unittest.cpp +index f4026fb0f..468d84024 100644 +--- a/core/fxcrt/weak_ptr_unittest.cpp ++++ b/core/fxcrt/weak_ptr_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -19,7 +19,7 @@ using UniqueTestPtr = + + class PseudoDeletable { + public: +- PseudoDeletable() : delete_count_(0) {} ++ PseudoDeletable() = default; + void Release() { + ++delete_count_; + next_.Reset(); +@@ -28,7 +28,7 @@ class PseudoDeletable { + int delete_count() const { return delete_count_; } + + private: +- int delete_count_; ++ int delete_count_ = 0; + WeakTestPtr next_; + }; + +@@ -88,7 +88,7 @@ TEST(WeakPtr, ResetNull) { + WeakTestPtr ptr2 = ptr1; + ptr1.Reset(); + EXPECT_FALSE(ptr1); +- EXPECT_EQ(nullptr, ptr1.Get()); ++ EXPECT_FALSE(ptr1.Get()); + EXPECT_TRUE(ptr2); + EXPECT_EQ(&thing, ptr2.Get()); + EXPECT_FALSE(ptr1 == ptr2); +@@ -128,9 +128,9 @@ TEST(WeakPtr, DeleteObject) { + WeakTestPtr ptr2 = ptr1; + ptr1.DeleteObject(); + EXPECT_FALSE(ptr1); +- EXPECT_EQ(nullptr, ptr1.Get()); ++ EXPECT_FALSE(ptr1.Get()); + EXPECT_FALSE(ptr2); +- EXPECT_EQ(nullptr, ptr2.Get()); ++ EXPECT_FALSE(ptr2.Get()); + EXPECT_FALSE(ptr1 == ptr2); + EXPECT_TRUE(ptr1 != ptr2); + EXPECT_EQ(1, thing.delete_count()); +diff --git a/core/fxcrt/widestring.cpp b/core/fxcrt/widestring.cpp +index 10a31c3fa..c2701e4cd 100644 +--- a/core/fxcrt/widestring.cpp ++++ b/core/fxcrt/widestring.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,17 +7,20 @@ + #include "core/fxcrt/widestring.h" + + #include ++#include + + #include +-#include +-#include ++#include + + #include "core/fxcrt/fx_codepage.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/string_pool_template.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/cxx17_backports.h" + #include "third_party/base/numerics/safe_math.h" +-#include "third_party/base/stl_util.h" + + template class fxcrt::StringDataTemplate; + template class fxcrt::StringViewTemplate; +@@ -33,69 +36,69 @@ namespace { + constexpr wchar_t kWideTrimChars[] = L"\x09\x0a\x0b\x0c\x0d\x20"; + + const wchar_t* FX_wcsstr(const wchar_t* haystack, +- int haystack_len, ++ size_t haystack_len, + const wchar_t* needle, +- int needle_len) { +- if (needle_len > haystack_len || needle_len == 0) { ++ size_t needle_len) { ++ if (needle_len > haystack_len || needle_len == 0) + return nullptr; +- } ++ + const wchar_t* end_ptr = haystack + haystack_len - needle_len; + while (haystack <= end_ptr) { +- int i = 0; +- while (1) { +- if (haystack[i] != needle[i]) { ++ size_t i = 0; ++ while (true) { ++ if (haystack[i] != needle[i]) + break; +- } ++ + i++; +- if (i == needle_len) { ++ if (i == needle_len) + return haystack; +- } + } + haystack++; + } + return nullptr; + } + +-Optional GuessSizeForVSWPrintf(const wchar_t* pFormat, +- va_list argList) { ++absl::optional GuessSizeForVSWPrintf(const wchar_t* pFormat, ++ va_list argList) { + size_t nMaxLen = 0; + for (const wchar_t* pStr = pFormat; *pStr != 0; pStr++) { + if (*pStr != '%' || *(pStr = pStr + 1) == '%') { + ++nMaxLen; + continue; + } +- int nItemLen = 0; +- int nWidth = 0; ++ int iWidth = 0; + for (; *pStr != 0; pStr++) { + if (*pStr == '#') { + nMaxLen += 2; + } else if (*pStr == '*') { +- nWidth = va_arg(argList, int); ++ iWidth = va_arg(argList, int); + } else if (*pStr != '-' && *pStr != '+' && *pStr != '0' && *pStr != ' ') { + break; + } + } +- if (nWidth == 0) { +- nWidth = FXSYS_wtoi(pStr); ++ if (iWidth == 0) { ++ iWidth = FXSYS_wtoi(pStr); + while (FXSYS_IsDecimalDigit(*pStr)) + ++pStr; + } +- if (nWidth < 0 || nWidth > 128 * 1024) +- return pdfium::nullopt; +- int nPrecision = 0; ++ if (iWidth < 0 || iWidth > 128 * 1024) ++ return absl::nullopt; ++ uint32_t nWidth = static_cast(iWidth); ++ int iPrecision = 0; + if (*pStr == '.') { + pStr++; + if (*pStr == '*') { +- nPrecision = va_arg(argList, int); ++ iPrecision = va_arg(argList, int); + pStr++; + } else { +- nPrecision = FXSYS_wtoi(pStr); ++ iPrecision = FXSYS_wtoi(pStr); + while (FXSYS_IsDecimalDigit(*pStr)) + ++pStr; + } + } +- if (nPrecision < 0 || nPrecision > 128 * 1024) +- return pdfium::nullopt; ++ if (iPrecision < 0 || iPrecision > 128 * 1024) ++ return absl::nullopt; ++ uint32_t nPrecision = static_cast(iPrecision); + int nModifier = 0; + if (*pStr == L'I' && *(pStr + 1) == L'6' && *(pStr + 2) == L'4') { + pStr += 3; +@@ -117,6 +120,7 @@ Optional GuessSizeForVSWPrintf(const wchar_t* pFormat, + break; + } + } ++ size_t nItemLen = 0; + switch (*pStr | nModifier) { + case 'c': + case 'C': +@@ -248,11 +252,11 @@ Optional GuessSizeForVSWPrintf(const wchar_t* pFormat, + } + + // Returns string unless we ran out of space. +-Optional TryVSWPrintf(size_t size, +- const wchar_t* pFormat, +- va_list argList) { ++absl::optional TryVSWPrintf(size_t size, ++ const wchar_t* pFormat, ++ va_list argList) { + if (!size) +- return {}; ++ return absl::nullopt; + + WideString str; + { +@@ -270,10 +274,10 @@ Optional TryVSWPrintf(size_t size, + + bool bSufficientBuffer = ret >= 0 || buffer[size - 1] == 0; + if (!bSufficientBuffer) +- return {}; ++ return absl::nullopt; + } + str.ReleaseBuffer(str.GetStringLength()); +- return {str}; ++ return str; + } + + } // namespace +@@ -283,6 +287,13 @@ namespace fxcrt { + static_assert(sizeof(WideString) <= sizeof(wchar_t*), + "Strings must not require more space than pointers"); + ++// static ++WideString WideString::FormatInteger(int i) { ++ wchar_t wbuf[32]; ++ swprintf(wbuf, std::size(wbuf), L"%d", i); ++ return WideString(wbuf); ++} ++ + // static + WideString WideString::FormatV(const wchar_t* format, va_list argList) { + va_list argListCopy; +@@ -302,12 +313,12 @@ WideString WideString::FormatV(const wchar_t* format, va_list argList) { + + while (maxLen < 32 * 1024) { + va_copy(argListCopy, argList); +- Optional ret = ++ absl::optional ret = + TryVSWPrintf(static_cast(maxLen), format, argListCopy); + va_end(argListCopy); ++ if (ret.has_value()) ++ return ret.value(); + +- if (ret) +- return *ret; + maxLen *= 2; + } + return WideString(); +@@ -322,7 +333,7 @@ WideString WideString::Format(const wchar_t* pFormat, ...) { + return ret; + } + +-WideString::WideString() {} ++WideString::WideString() = default; + + WideString::WideString(const WideString& other) : m_pData(other.m_pData) {} + +@@ -383,7 +394,15 @@ WideString::WideString(const std::initializer_list& list) { + } + } + +-WideString::~WideString() {} ++WideString::~WideString() = default; ++ ++void WideString::clear() { ++ if (m_pData && m_pData->CanOperateInPlace(0)) { ++ m_pData->m_nDataLength = 0; ++ return; ++ } ++ m_pData.Reset(); ++} + + WideString& WideString::operator=(const wchar_t* str) { + if (!str || !str[0]) +@@ -410,7 +429,7 @@ WideString& WideString::operator=(const WideString& that) { + return *this; + } + +-WideString& WideString::operator=(WideString&& that) { ++WideString& WideString::operator=(WideString&& that) noexcept { + if (m_pData != that.m_pData) + m_pData = std::move(that.m_pData); + +@@ -548,7 +567,7 @@ void WideString::ReleaseBuffer(size_t nNewLength) { + return; + } + +- ASSERT(m_pData->m_nRefs == 1); ++ DCHECK_EQ(m_pData->m_nRefs, 1); + m_pData->m_nDataLength = nNewLength; + m_pData->m_String[nNewLength] = 0; + if (m_pData->m_nAllocLength - nNewLength >= 32) { +@@ -653,9 +672,8 @@ ByteString WideString::ToLatin1() const { + } + + ByteString WideString::ToDefANSI() const { +- int src_len = GetLength(); +- int dest_len = FXSYS_WideCharToMultiByte( +- FX_CODEPAGE_DefANSI, 0, c_str(), src_len, nullptr, 0, nullptr, nullptr); ++ size_t dest_len = ++ FX_WideCharToMultiByte(FX_CodePage::kDefANSI, AsStringView(), {}); + if (!dest_len) + return ByteString(); + +@@ -663,8 +681,7 @@ ByteString WideString::ToDefANSI() const { + { + // Span's lifetime must end before ReleaseBuffer() below. + pdfium::span dest_buf = bstr.GetBuffer(dest_len); +- FXSYS_WideCharToMultiByte(FX_CODEPAGE_DefANSI, 0, c_str(), src_len, +- dest_buf.data(), dest_len, nullptr, nullptr); ++ FX_WideCharToMultiByte(FX_CodePage::kDefANSI, AsStringView(), dest_buf); + } + bstr.ReleaseBuffer(dest_len); + return bstr; +@@ -679,11 +696,11 @@ ByteString WideString::ToUTF16LE() const { + return ByteString("\0\0", 2); + + ByteString result; +- int len = m_pData->m_nDataLength; ++ size_t len = m_pData->m_nDataLength; + { + // Span's lifetime must end before ReleaseBuffer() below. + pdfium::span buffer = result.GetBuffer(len * 2 + 2); +- for (int i = 0; i < len; i++) { ++ for (size_t i = 0; i < len; i++) { + buffer[i * 2] = m_pData->m_String[i] & 0xff; + buffer[i * 2 + 1] = m_pData->m_String[i] >> 8; + } +@@ -694,6 +711,21 @@ ByteString WideString::ToUTF16LE() const { + return result; + } + ++WideString WideString::EncodeEntities() const { ++ WideString ret = *this; ++ ret.Replace(L"&", L"&"); ++ ret.Replace(L"<", L"<"); ++ ret.Replace(L">", L">"); ++ ret.Replace(L"\'", L"'"); ++ ret.Replace(L"\"", L"""); ++ return ret; ++} ++ ++WideString WideString::Substr(size_t offset) const { ++ // Unsigned underflow is well-defined and out-of-range is handled by Substr(). ++ return Substr(offset, GetLength() - offset); ++} ++ + WideString WideString::Substr(size_t first, size_t count) const { + if (!m_pData) + return WideString(); +@@ -716,14 +748,11 @@ WideString WideString::Substr(size_t first, size_t count) const { + } + + WideString WideString::First(size_t count) const { +- if (count == 0 || !IsValidLength(count)) +- return WideString(); + return Substr(0, count); + } + + WideString WideString::Last(size_t count) const { +- if (count == 0 || !IsValidLength(count)) +- return WideString(); ++ // Unsigned underflow is well-defined and out-of-range is handled by Substr(). + return Substr(GetLength() - count, count); + } + +@@ -752,47 +781,50 @@ size_t WideString::Insert(size_t index, wchar_t ch) { + return new_length; + } + +-Optional WideString::Find(wchar_t ch, size_t start) const { ++absl::optional WideString::Find(wchar_t ch, size_t start) const { + if (!m_pData) +- return pdfium::nullopt; ++ return absl::nullopt; + + if (!IsValidIndex(start)) +- return pdfium::nullopt; ++ return absl::nullopt; + + const wchar_t* pStr = + wmemchr(m_pData->m_String + start, ch, m_pData->m_nDataLength - start); +- return pStr ? Optional(static_cast(pStr - m_pData->m_String)) +- : pdfium::nullopt; ++ return pStr ? absl::optional( ++ static_cast(pStr - m_pData->m_String)) ++ : absl::nullopt; + } + +-Optional WideString::Find(WideStringView subStr, size_t start) const { ++absl::optional WideString::Find(WideStringView subStr, ++ size_t start) const { + if (!m_pData) +- return pdfium::nullopt; ++ return absl::nullopt; + + if (!IsValidIndex(start)) +- return pdfium::nullopt; ++ return absl::nullopt; + + const wchar_t* pStr = + FX_wcsstr(m_pData->m_String + start, m_pData->m_nDataLength - start, + subStr.unterminated_c_str(), subStr.GetLength()); +- return pStr ? Optional(static_cast(pStr - m_pData->m_String)) +- : pdfium::nullopt; ++ return pStr ? absl::optional( ++ static_cast(pStr - m_pData->m_String)) ++ : absl::nullopt; + } + +-Optional WideString::ReverseFind(wchar_t ch) const { ++absl::optional WideString::ReverseFind(wchar_t ch) const { + if (!m_pData) +- return pdfium::nullopt; ++ return absl::nullopt; + + size_t nLength = m_pData->m_nDataLength; + while (nLength--) { + if (m_pData->m_String[nLength] == ch) + return nLength; + } +- return pdfium::nullopt; ++ return absl::nullopt; + } + + void WideString::MakeLower() { +- if (!m_pData) ++ if (IsEmpty()) + return; + + ReallocBeforeWrite(m_pData->m_nDataLength); +@@ -800,7 +832,7 @@ void WideString::MakeLower() { + } + + void WideString::MakeUpper() { +- if (!m_pData) ++ if (IsEmpty()) + return; + + ReallocBeforeWrite(m_pData->m_nDataLength); +@@ -808,7 +840,7 @@ void WideString::MakeUpper() { + } + + size_t WideString::Remove(wchar_t chRemove) { +- if (!m_pData || m_pData->m_nDataLength == 0) ++ if (IsEmpty()) + return 0; + + wchar_t* pstrSource = m_pData->m_String; +@@ -850,7 +882,7 @@ size_t WideString::Replace(WideStringView pOld, WideStringView pNew) { + size_t count = 0; + const wchar_t* pStart = m_pData->m_String; + wchar_t* pEnd = m_pData->m_String + m_pData->m_nDataLength; +- while (1) { ++ while (true) { + const wchar_t* pTarget = + FX_wcsstr(pStart, static_cast(pEnd - pStart), + pOld.unterminated_c_str(), nSourceLen); +@@ -909,9 +941,7 @@ WideString WideString::FromLatin1(ByteStringView bstr) { + + // static + WideString WideString::FromDefANSI(ByteStringView bstr) { +- int src_len = bstr.GetLength(); +- int dest_len = FXSYS_MultiByteToWideChar( +- FX_CODEPAGE_DefANSI, 0, bstr.unterminated_c_str(), src_len, nullptr, 0); ++ size_t dest_len = FX_MultiByteToWideChar(FX_CodePage::kDefANSI, bstr, {}); + if (!dest_len) + return WideString(); + +@@ -919,8 +949,7 @@ WideString WideString::FromDefANSI(ByteStringView bstr) { + { + // Span's lifetime must end before ReleaseBuffer() below. + pdfium::span dest_buf = wstr.GetBuffer(dest_len); +- FXSYS_MultiByteToWideChar(FX_CODEPAGE_DefANSI, 0, bstr.unterminated_c_str(), +- src_len, dest_buf.data(), dest_len); ++ FX_MultiByteToWideChar(FX_CodePage::kDefANSI, bstr, dest_buf); + } + wstr.ReleaseBuffer(dest_len); + return wstr; +@@ -966,7 +995,7 @@ WideString WideString::FromUTF16BE(const unsigned short* wstr, size_t wlen) { + } + + void WideString::SetAt(size_t index, wchar_t c) { +- ASSERT(IsValidIndex(index)); ++ DCHECK(IsValidIndex(index)); + ReallocBeforeWrite(m_pData->m_nDataLength); + m_pData->m_String[index] = c; + } +@@ -1110,14 +1139,16 @@ std::ostream& operator<<(std::ostream& os, WideStringView str) { + + } // namespace fxcrt + +-uint32_t FX_HashCode_GetW(WideStringView str, bool bIgnoreCase) { ++uint32_t FX_HashCode_GetW(WideStringView str) { + uint32_t dwHashCode = 0; +- if (bIgnoreCase) { +- for (wchar_t c : str) // match FXSYS_towlower() arg type. +- dwHashCode = 1313 * dwHashCode + FXSYS_towlower(c); +- } else { +- for (WideStringView::UnsignedType c : str) +- dwHashCode = 1313 * dwHashCode + c; +- } ++ for (WideStringView::UnsignedType c : str) ++ dwHashCode = 1313 * dwHashCode + c; ++ return dwHashCode; ++} ++ ++uint32_t FX_HashCode_GetLoweredW(WideStringView str) { ++ uint32_t dwHashCode = 0; ++ for (wchar_t c : str) // match FXSYS_towlower() arg type. ++ dwHashCode = 1313 * dwHashCode + FXSYS_towlower(c); + return dwHashCode; + } +diff --git a/core/fxcrt/widestring.h b/core/fxcrt/widestring.h +index 58e75449f..24547dde4 100644 +--- a/core/fxcrt/widestring.h ++++ b/core/fxcrt/widestring.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,17 +7,21 @@ + #ifndef CORE_FXCRT_WIDESTRING_H_ + #define CORE_FXCRT_WIDESTRING_H_ + ++#include ++#include ++#include ++#include ++ + #include ++#include + #include +-#include + #include + +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/string_data_template.h" + #include "core/fxcrt/string_view_template.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/check.h" + #include "third_party/base/span.h" + + namespace fxcrt { +@@ -32,9 +36,10 @@ class WideString { + using const_iterator = const CharType*; + using const_reverse_iterator = std::reverse_iterator; + +- static WideString Format(const wchar_t* pFormat, ...) WARN_UNUSED_RESULT; +- static WideString FormatV(const wchar_t* lpszFormat, +- va_list argList) WARN_UNUSED_RESULT; ++ [[nodiscard]] static WideString FormatInteger(int i); ++ [[nodiscard]] static WideString Format(const wchar_t* pFormat, ...); ++ [[nodiscard]] static WideString FormatV(const wchar_t* lpszFormat, ++ va_list argList); + + WideString(); + WideString(const WideString& other); +@@ -42,10 +47,11 @@ class WideString { + // Move-construct a WideString. After construction, |other| is empty. + WideString(WideString&& other) noexcept; + ++ // Make a one-character string from one wide char. ++ explicit WideString(wchar_t ch); ++ + // Deliberately implicit to avoid calling on every string literal. + // NOLINTNEXTLINE(runtime/explicit) +- WideString(wchar_t ch); +- // NOLINTNEXTLINE(runtime/explicit) + WideString(const wchar_t* ptr); + + // No implicit conversions from byte strings. +@@ -60,16 +66,16 @@ class WideString { + + ~WideString(); + +- static WideString FromASCII(ByteStringView str) WARN_UNUSED_RESULT; +- static WideString FromLatin1(ByteStringView str) WARN_UNUSED_RESULT; +- static WideString FromDefANSI(ByteStringView str) WARN_UNUSED_RESULT; +- static WideString FromUTF8(ByteStringView str) WARN_UNUSED_RESULT; +- static WideString FromUTF16LE(const unsigned short* str, +- size_t len) WARN_UNUSED_RESULT; +- static WideString FromUTF16BE(const unsigned short* wstr, +- size_t wlen) WARN_UNUSED_RESULT; ++ [[nodiscard]] static WideString FromASCII(ByteStringView str); ++ [[nodiscard]] static WideString FromLatin1(ByteStringView str); ++ [[nodiscard]] static WideString FromDefANSI(ByteStringView str); ++ [[nodiscard]] static WideString FromUTF8(ByteStringView str); ++ [[nodiscard]] static WideString FromUTF16LE(const unsigned short* str, ++ size_t len); ++ [[nodiscard]] static WideString FromUTF16BE(const unsigned short* wstr, ++ size_t wlen); + +- static size_t WStringLength(const unsigned short* str) WARN_UNUSED_RESULT; ++ [[nodiscard]] static size_t WStringLength(const unsigned short* str); + + // Explicit conversion to C-style wide string. + // Note: Any subsequent modification of |this| will invalidate the result. +@@ -102,7 +108,9 @@ class WideString { + return const_reverse_iterator(begin()); + } + +- void clear() { m_pData.Reset(); } ++ // Holds on to buffer if possible for later re-use. Assign WideString() ++ // to force immediate release if desired. ++ void clear(); + + size_t GetLength() const { return m_pData ? m_pData->m_nDataLength : 0; } + size_t GetStringLength() const { +@@ -117,7 +125,7 @@ class WideString { + WideString& operator=(const WideString& that); + + // Move-assign a WideString. After assignment, |that| is empty. +- WideString& operator=(WideString&& that); ++ WideString& operator=(WideString&& that) noexcept; + + WideString& operator+=(const wchar_t* str); + WideString& operator+=(wchar_t ch); +@@ -150,6 +158,7 @@ class WideString { + int Compare(const WideString& str) const; + int CompareNoCase(const wchar_t* str) const; + ++ WideString Substr(size_t offset) const; + WideString Substr(size_t first, size_t count) const; + WideString First(size_t count) const; + WideString Last(size_t count) const; +@@ -183,9 +192,9 @@ class WideString { + + int GetInteger() const; + +- Optional Find(WideStringView subStr, size_t start = 0) const; +- Optional Find(wchar_t ch, size_t start = 0) const; +- Optional ReverseFind(wchar_t ch) const; ++ absl::optional Find(WideStringView subStr, size_t start = 0) const; ++ absl::optional Find(wchar_t ch, size_t start = 0) const; ++ absl::optional ReverseFind(wchar_t ch) const; + + bool Contains(WideStringView lpszSub, size_t start = 0) const { + return Find(lpszSub, start).has_value(); +@@ -216,6 +225,9 @@ class WideString { + // so GetLength() will include them. + ByteString ToUTF16LE() const; + ++ // Replace the characters &<>'" with HTML entities. ++ WideString EncodeEntities() const; ++ + protected: + using StringData = StringDataTemplate; + +@@ -247,7 +259,7 @@ inline WideString operator+(WideStringView str1, wchar_t ch) { + return WideString(str1, WideStringView(ch)); + } + inline WideString operator+(wchar_t ch, WideStringView str2) { +- return WideString(ch, str2); ++ return WideString(WideStringView(ch), str2); + } + inline WideString operator+(const WideString& str1, const WideString& str2) { + return WideString(str1.AsStringView(), str2.AsStringView()); +@@ -256,7 +268,7 @@ inline WideString operator+(const WideString& str1, wchar_t ch) { + return WideString(str1.AsStringView(), WideStringView(ch)); + } + inline WideString operator+(wchar_t ch, const WideString& str2) { +- return WideString(ch, str2.AsStringView()); ++ return WideString(WideStringView(ch), str2.AsStringView()); + } + inline WideString operator+(const WideString& str1, const wchar_t* str2) { + return WideString(str1.AsStringView(), str2); +@@ -295,14 +307,15 @@ std::ostream& operator<<(std::ostream& os, WideStringView str); + + using WideString = fxcrt::WideString; + +-uint32_t FX_HashCode_GetW(WideStringView str, bool bIgnoreCase); ++uint32_t FX_HashCode_GetW(WideStringView str); ++uint32_t FX_HashCode_GetLoweredW(WideStringView str); + + namespace std { + + template <> + struct hash { +- std::size_t operator()(const WideString& str) const { +- return FX_HashCode_GetW(str.AsStringView(), false); ++ size_t operator()(const WideString& str) const { ++ return FX_HashCode_GetW(str.AsStringView()); + } + }; + +diff --git a/core/fxcrt/widestring_unittest.cpp b/core/fxcrt/widestring_unittest.cpp +index 2445a47f1..d45c4e8e2 100644 +--- a/core/fxcrt/widestring_unittest.cpp ++++ b/core/fxcrt/widestring_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,8 +11,8 @@ + #include "build/build_config.h" + #include "core/fxcrt/fx_string.h" + #include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/base/containers/contains.h" + #include "third_party/base/span.h" +-#include "third_party/base/stl_util.h" + + namespace fxcrt { + +@@ -109,6 +109,26 @@ TEST(WideString, Assign) { + } + EXPECT_EQ(1, string1.ReferenceCountForTesting()); + } ++ { ++ // From wchar_t*. ++ WideString string1 = L"abc"; ++ EXPECT_EQ(L"abc", string1); ++ string1 = nullptr; ++ EXPECT_TRUE(string1.IsEmpty()); ++ string1 = L"def"; ++ EXPECT_EQ(L"def", string1); ++ string1 = L""; ++ EXPECT_TRUE(string1.IsEmpty()); ++ } ++ { ++ // From WideStringView. ++ WideString string1(WideStringView(L"abc")); ++ EXPECT_EQ(L"abc", string1); ++ string1 = WideStringView(L""); ++ EXPECT_TRUE(string1.IsEmpty()); ++ string1 = WideStringView(L"def"); ++ EXPECT_EQ(L"def", string1); ++ } + } + + TEST(WideString, OperatorLT) { +@@ -514,7 +534,16 @@ TEST(WideString, RemoveCopies) { + } + + TEST(WideString, Replace) { ++ WideString empty; ++ empty.Replace(L"", L"CLAMS"); ++ empty.Replace(L"xx", L"CLAMS"); ++ EXPECT_EQ(L"", empty); ++ + WideString fred(L"FRED"); ++ fred.Replace(L"", L""); ++ EXPECT_EQ(L"FRED", fred); ++ fred.Replace(L"", L"CLAMS"); ++ EXPECT_EQ(L"FRED", fred); + fred.Replace(L"FR", L"BL"); + EXPECT_EQ(L"BLED", fred); + fred.Replace(L"D", L"DDY"); +@@ -525,10 +554,20 @@ TEST(WideString, Replace) { + EXPECT_EQ(L"BY", fred); + fred.Replace(L"BY", L"HI"); + EXPECT_EQ(L"HI", fred); +- fred.Replace(L"", L"CLAMS"); +- EXPECT_EQ(L"HI", fred); +- fred.Replace(L"HI", L""); ++ fred.Replace(L"I", L"IHIHI"); ++ EXPECT_EQ(L"HIHIHI", fred); ++ fred.Replace(L"HI", L"HO"); ++ EXPECT_EQ(L"HOHOHO", fred); ++ fred.Replace(L"HO", L""); + EXPECT_EQ(L"", fred); ++ ++ WideString five_xs(L"xxxxx"); ++ five_xs.Replace(L"xx", L"xxx"); ++ EXPECT_EQ(L"xxxxxxx", five_xs); ++ ++ WideString five_ys(L"yyyyy"); ++ five_ys.Replace(L"yy", L"y"); ++ EXPECT_EQ(L"yyy", five_ys); + } + + TEST(WideString, Insert) { +@@ -611,7 +650,20 @@ TEST(WideString, Delete) { + EXPECT_EQ(L"", empty); + } + +-TEST(WideString, Substr) { ++TEST(WideString, OneArgSubstr) { ++ WideString fred(L"FRED"); ++ EXPECT_EQ(L"FRED", fred.Substr(0)); ++ EXPECT_EQ(L"RED", fred.Substr(1)); ++ EXPECT_EQ(L"ED", fred.Substr(2)); ++ EXPECT_EQ(L"D", fred.Substr(3)); ++ EXPECT_EQ(L"", fred.Substr(4)); ++ ++ WideString empty; ++ EXPECT_EQ(L"", empty.Substr(0)); ++ EXPECT_EQ(L"", empty.Substr(1)); ++} ++ ++TEST(WideString, TwoArgSubstr) { + WideString fred(L"FRED"); + EXPECT_EQ(L"", fred.Substr(0, 0)); + EXPECT_EQ(L"", fred.Substr(3, 0)); +@@ -673,9 +725,8 @@ TEST(WideString, Find) { + EXPECT_FALSE(empty_string.Find(L'a').has_value()); + EXPECT_FALSE(empty_string.Find(L'\0').has_value()); + +- Optional result; + WideString single_string(L"a"); +- result = single_string.Find(L'a'); ++ absl::optional result = single_string.Find(L'a'); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(0u, result.value()); + EXPECT_FALSE(single_string.Find(L'b').has_value()); +@@ -721,9 +772,8 @@ TEST(WideString, ReverseFind) { + EXPECT_FALSE(empty_string.ReverseFind(L'a').has_value()); + EXPECT_FALSE(empty_string.ReverseFind(L'\0').has_value()); + +- Optional result; + WideString single_string(L"a"); +- result = single_string.ReverseFind(L'a'); ++ absl::optional result = single_string.ReverseFind(L'a'); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(0u, result.value()); + EXPECT_FALSE(single_string.ReverseFind(L'b').has_value()); +@@ -758,6 +808,17 @@ TEST(WideString, UpperLower) { + EXPECT_EQ(L"", empty); + empty.MakeUpper(); + EXPECT_EQ(L"", empty); ++ ++ WideString empty_with_buffer(L"x"); ++ empty_with_buffer.Delete(0); ++ ++ WideString additional_empty_with_buffer_ref = empty_with_buffer; ++ additional_empty_with_buffer_ref.MakeLower(); ++ EXPECT_EQ(L"", additional_empty_with_buffer_ref); ++ ++ additional_empty_with_buffer_ref = empty_with_buffer; ++ additional_empty_with_buffer_ref.MakeUpper(); ++ EXPECT_EQ(L"", additional_empty_with_buffer_ref); + } + + TEST(WideString, Trim) { +@@ -1088,13 +1149,18 @@ TEST(WideString, ToUTF16LE) { + {L"\x3132\x6162", ByteString("\x32\x31\x62\x61\0\0", 6)}, + }; + +- for (size_t i = 0; i < FX_ArraySize(utf16le_encode_cases); ++i) { ++ for (size_t i = 0; i < std::size(utf16le_encode_cases); ++i) { + EXPECT_EQ(utf16le_encode_cases[i].bs, + utf16le_encode_cases[i].ws.ToUTF16LE()) + << " for case number " << i; + } + } + ++TEST(WideString, EncodeEntities) { ++ EXPECT_EQ(WideString(L"Symbols &<>'\".").EncodeEntities(), ++ L"Symbols &<>'"."); ++} ++ + TEST(WideString, IsASCII) { + EXPECT_TRUE(WideString(L"xy\u007fz").IsASCII()); + EXPECT_FALSE(WideString(L"xy\u0080z").IsASCII()); +@@ -1157,7 +1223,7 @@ TEST(WideString, ToLatin1) { + + TEST(WideString, ToDefANSI) { + EXPECT_EQ("", WideString().ToDefANSI()); +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + const char* kResult = + "x" + "?" +@@ -1209,7 +1275,7 @@ TEST(WideString, FromLatin1) { + + TEST(WideString, FromDefANSI) { + EXPECT_EQ(L"", WideString::FromDefANSI(ByteStringView())); +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + const wchar_t* kResult = + L"x" + L"\u20ac" +@@ -1244,7 +1310,7 @@ TEST(WideStringView, FromVector) { + cleared_vec.pop_back(); + WideStringView cleared_string(cleared_vec); + EXPECT_EQ(0u, cleared_string.GetLength()); +- EXPECT_EQ(nullptr, cleared_string.raw_str()); ++ EXPECT_FALSE(cleared_string.raw_str()); + } + + TEST(WideStringView, ElementAccess) { +@@ -1430,9 +1496,8 @@ TEST(WideStringView, Find) { + EXPECT_FALSE(empty_string.Find(L'a').has_value()); + EXPECT_FALSE(empty_string.Find(L'\0').has_value()); + +- Optional result; + WideStringView single_string(L"a"); +- result = single_string.Find(L'a'); ++ absl::optional result = single_string.Find(L'a'); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(0u, result.value()); + EXPECT_FALSE(single_string.Find(L'b').has_value()); +@@ -1582,9 +1647,9 @@ TEST(WideStringView, AnyAllNoneOf) { + EXPECT_TRUE(std::any_of(str.begin(), str.end(), + [](const wchar_t& c) { return c == L'a'; })); + +- EXPECT_TRUE(pdfium::ContainsValue(str, L'a')); +- EXPECT_TRUE(pdfium::ContainsValue(str, L'b')); +- EXPECT_FALSE(pdfium::ContainsValue(str, L'z')); ++ EXPECT_TRUE(pdfium::Contains(str, L'a')); ++ EXPECT_TRUE(pdfium::Contains(str, L'b')); ++ EXPECT_FALSE(pdfium::Contains(str, L'z')); + } + + TEST(WideStringView, TrimmedRight) { +@@ -1625,7 +1690,7 @@ TEST(WideString, FormatString) { + EXPECT_EQ(L"cla", WideString::Format(L"%.3ls", L"clams")); + EXPECT_EQ(L"\u043e\u043f", WideString(L"\u043e\u043f")); + +-#if !defined(OS_MACOSX) ++#if !BUILDFLAG(IS_APPLE) + // See https://bugs.chromium.org/p/pdfium/issues/detail?id=1132 + EXPECT_EQ(L"\u043e\u043f", WideString::Format(L"\u043e\u043f")); + EXPECT_EQ(L"\u043e\u043f", WideString::Format(L"%ls", L"\u043e\u043f")); +@@ -1639,12 +1704,12 @@ TEST(WideString, Empty) { + EXPECT_EQ(0u, empty_str.GetLength()); + + const wchar_t* cstr = empty_str.c_str(); +- EXPECT_NE(nullptr, cstr); ++ EXPECT_TRUE(cstr); + EXPECT_EQ(0u, wcslen(cstr)); + + pdfium::span cspan = empty_str.span(); + EXPECT_TRUE(cspan.empty()); +- EXPECT_EQ(nullptr, cspan.data()); ++ EXPECT_FALSE(cspan.data()); + } + + TEST(CFX_WidString, InitializerList) { +@@ -1722,9 +1787,9 @@ TEST(WideString, AnyAllNoneOf) { + EXPECT_TRUE(std::any_of(str.begin(), str.end(), + [](const wchar_t& c) { return c == L'a'; })); + +- EXPECT_TRUE(pdfium::ContainsValue(str, L'a')); +- EXPECT_TRUE(pdfium::ContainsValue(str, L'b')); +- EXPECT_FALSE(pdfium::ContainsValue(str, L'z')); ++ EXPECT_TRUE(pdfium::Contains(str, L'a')); ++ EXPECT_TRUE(pdfium::Contains(str, L'b')); ++ EXPECT_FALSE(pdfium::Contains(str, L'z')); + } + + TEST(WideString, OStreamOverload) { +@@ -1991,15 +2056,29 @@ TEST(WideStringView, WideOStreamOverload) { + } + } + ++TEST(WideString, FormatInteger) { ++ // Base case of 0. ++ EXPECT_EQ(L"0", WideString::FormatInteger(0)); ++ ++ // Positive ordinary number. ++ EXPECT_EQ(L"123456", WideString::FormatInteger(123456)); ++ ++ // Negative ordinary number. ++ EXPECT_EQ(L"-123456", WideString::FormatInteger(-123456)); ++ ++ // int limits. ++ EXPECT_EQ(L"2147483647", WideString::FormatInteger(INT_MAX)); ++ EXPECT_EQ(L"-2147483648", WideString::FormatInteger(INT_MIN)); ++} ++ + TEST(WideString, FX_HashCode_Wide) { +- EXPECT_EQ(0u, FX_HashCode_GetW(L"", false)); +- EXPECT_EQ(65u, FX_HashCode_GetW(L"A", false)); +- EXPECT_EQ(97u, FX_HashCode_GetW(L"A", true)); +- EXPECT_EQ(1313 * 65u + 66u, FX_HashCode_GetW(L"AB", false)); +- EXPECT_EQ(FX_HashCode_GetAsIfW("AB\xff", false), +- FX_HashCode_GetW(L"AB\xff", false)); +- EXPECT_EQ(FX_HashCode_GetAsIfW("AB\xff", true), +- FX_HashCode_GetW(L"AB\xff", true)); ++ EXPECT_EQ(0u, FX_HashCode_GetW(L"")); ++ EXPECT_EQ(65u, FX_HashCode_GetW(L"A")); ++ EXPECT_EQ(97u, FX_HashCode_GetLoweredW(L"A")); ++ EXPECT_EQ(1313 * 65u + 66u, FX_HashCode_GetW(L"AB")); ++ EXPECT_EQ(FX_HashCode_GetAsIfW("AB\xff"), FX_HashCode_GetW(L"AB\xff")); ++ EXPECT_EQ(FX_HashCode_GetLoweredAsIfW("AB\xff"), ++ FX_HashCode_GetLoweredW(L"AB\xff")); + } + + } // namespace fxcrt +diff --git a/core/fxcrt/widetext_buffer.cpp b/core/fxcrt/widetext_buffer.cpp +new file mode 100644 +index 000000000..145c0560d +--- /dev/null ++++ b/core/fxcrt/widetext_buffer.cpp +@@ -0,0 +1,86 @@ ++// Copyright 2017 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxcrt/widetext_buffer.h" ++ ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/fx_system.h" ++ ++namespace fxcrt { ++ ++size_t WideTextBuffer::GetLength() const { ++ return GetSize() / sizeof(wchar_t); ++} ++ ++pdfium::span WideTextBuffer::GetWideSpan() { ++ return pdfium::make_span(reinterpret_cast(m_buffer.data()), ++ GetLength()); ++} ++ ++pdfium::span WideTextBuffer::GetWideSpan() const { ++ return pdfium::make_span(reinterpret_cast(m_buffer.data()), ++ GetLength()); ++} ++ ++WideStringView WideTextBuffer::AsStringView() const { ++ return WideStringView(GetWideSpan()); ++} ++ ++WideString WideTextBuffer::MakeString() const { ++ return WideString(AsStringView()); ++} ++ ++void WideTextBuffer::AppendChar(wchar_t ch) { ++ pdfium::span new_span = ExpandWideBuf(1); ++ new_span[0] = ch; ++} ++ ++void WideTextBuffer::Delete(size_t start_index, size_t count) { ++ DeleteBuf(start_index * sizeof(wchar_t), count * sizeof(wchar_t)); ++} ++ ++void WideTextBuffer::AppendWideString(WideStringView str) { ++ AppendSpan(pdfium::as_bytes(str.span())); ++} ++ ++WideTextBuffer& WideTextBuffer::operator<<(ByteStringView ascii) { ++ pdfium::span new_span = ExpandWideBuf(ascii.GetLength()); ++ for (size_t i = 0; i < ascii.GetLength(); ++i) ++ new_span[i] = ascii[i]; ++ return *this; ++} ++ ++WideTextBuffer& WideTextBuffer::operator<<(WideStringView str) { ++ AppendWideString(str); ++ return *this; ++} ++ ++WideTextBuffer& WideTextBuffer::operator<<(const WideString& str) { ++ AppendWideString(str.AsStringView()); ++ return *this; ++} ++ ++WideTextBuffer& WideTextBuffer::operator<<(const wchar_t* lpsz) { ++ AppendWideString(WideStringView(lpsz)); ++ return *this; ++} ++ ++WideTextBuffer& WideTextBuffer::operator<<(const WideTextBuffer& buf) { ++ AppendWideString(buf.AsStringView()); ++ return *this; ++} ++ ++pdfium::span WideTextBuffer::ExpandWideBuf(size_t char_count) { ++ size_t original_count = GetLength(); ++ FX_SAFE_SIZE_T safe_bytes = char_count; ++ safe_bytes *= sizeof(wchar_t); ++ size_t bytes = safe_bytes.ValueOrDie(); ++ ExpandBuf(bytes); ++ m_DataSize += bytes; ++ return GetWideSpan().subspan(original_count); ++} ++ ++} // namespace fxcrt +diff --git a/core/fxcrt/widetext_buffer.h b/core/fxcrt/widetext_buffer.h +new file mode 100644 +index 000000000..a02d12861 +--- /dev/null ++++ b/core/fxcrt/widetext_buffer.h +@@ -0,0 +1,48 @@ ++// Copyright 2017 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXCRT_WIDETEXT_BUFFER_H_ ++#define CORE_FXCRT_WIDETEXT_BUFFER_H_ ++ ++#include ++ ++#include "core/fxcrt/binary_buffer.h" ++#include "core/fxcrt/fx_string.h" ++#include "third_party/base/span.h" ++ ++namespace fxcrt { ++ ++class WideTextBuffer final : public BinaryBuffer { ++ public: ++ // BinaryBuffer: ++ size_t GetLength() const override; ++ ++ pdfium::span GetWideSpan(); ++ pdfium::span GetWideSpan() const; ++ WideStringView AsStringView() const; ++ WideString MakeString() const; ++ ++ void AppendChar(wchar_t wch); ++ void Delete(size_t start_index, size_t count); ++ ++ WideTextBuffer& operator<<(ByteStringView ascii); ++ WideTextBuffer& operator<<(const wchar_t* lpsz); ++ WideTextBuffer& operator<<(WideStringView str); ++ WideTextBuffer& operator<<(const WideString& str); ++ WideTextBuffer& operator<<(const WideTextBuffer& buf); ++ ++ private: ++ void AppendWideString(WideStringView str); ++ ++ // Returned span is the newly-expanded space. ++ pdfium::span ExpandWideBuf(size_t char_count); ++}; ++ ++} // namespace fxcrt ++ ++using fxcrt::WideTextBuffer; ++ ++#endif // CORE_FXCRT_WIDETEXT_BUFFER_H_ +diff --git a/core/fxcrt/widetext_buffer_unittest.cpp b/core/fxcrt/widetext_buffer_unittest.cpp +new file mode 100644 +index 000000000..81c889d8b +--- /dev/null ++++ b/core/fxcrt/widetext_buffer_unittest.cpp +@@ -0,0 +1,62 @@ ++// Copyright 2018 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxcrt/widetext_buffer.h" ++ ++#include ++ ++#include "testing/gtest/include/gtest/gtest.h" ++ ++namespace fxcrt { ++ ++TEST(WideTextBuffer, EmptyBuf) { ++ WideTextBuffer wtb; ++ EXPECT_TRUE(wtb.GetWideSpan().empty()); ++ EXPECT_TRUE(wtb.AsStringView().IsEmpty()); ++ EXPECT_TRUE(wtb.MakeString().IsEmpty()); ++} ++ ++TEST(WideTextBuffer, OperatorLtLt) { ++ WideTextBuffer wtb; ++ wtb << "clams" << L"\u208c\u208e"; ++ EXPECT_EQ(wtb.MakeString(), L"clams\u208c\u208e"); ++} ++ ++TEST(WideTextBuffer, Deletion) { ++ WideTextBuffer wtb; ++ wtb << L"ABCDEFG"; ++ EXPECT_TRUE(wtb.AsStringView().EqualsASCII("ABCDEFG")); ++ ++ wtb.Delete(1, 3); ++ EXPECT_TRUE(wtb.AsStringView().EqualsASCII("AEFG")); ++ ++ wtb.Delete(1, 0); ++ EXPECT_TRUE(wtb.AsStringView().EqualsASCII("AEFG")); ++ ++ wtb.Delete(0, 2); ++ EXPECT_TRUE(wtb.AsStringView().EqualsASCII("FG")); ++ ++ wtb.Delete(0, 2); ++ EXPECT_TRUE(wtb.AsStringView().EqualsASCII("")); ++ ++ wtb.Delete(0, 0); ++ EXPECT_TRUE(wtb.AsStringView().EqualsASCII("")); ++} ++ ++TEST(WideTextBuffer, Move) { ++ WideTextBuffer wtb; ++ wtb << "clams"; ++ EXPECT_EQ(wtb.MakeString(), L"clams"); ++ ++ WideTextBuffer wtb2(std::move(wtb)); ++ EXPECT_EQ(wtb.MakeString(), L""); ++ EXPECT_EQ(wtb2.MakeString(), L"clams"); ++ ++ WideTextBuffer wtb3; ++ wtb3 = std::move(wtb2); ++ EXPECT_EQ(wtb2.MakeString(), L""); ++ EXPECT_EQ(wtb3.MakeString(), L"clams"); ++} ++ ++} // namespace fxcrt +diff --git a/core/fxcrt/xml/cfx_xmlchardata.cpp b/core/fxcrt/xml/cfx_xmlchardata.cpp +index 1d42bd067..88b8920f9 100644 +--- a/core/fxcrt/xml/cfx_xmlchardata.cpp ++++ b/core/fxcrt/xml/cfx_xmlchardata.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,7 +22,7 @@ CFX_XMLNode* CFX_XMLCharData::Clone(CFX_XMLDocument* doc) { + } + + void CFX_XMLCharData::Save( +- const RetainPtr& pXMLStream) { ++ const RetainPtr& pXMLStream) { + pXMLStream->WriteString("WriteString(GetText().ToUTF8().AsStringView()); + pXMLStream->WriteString("]]>"); +diff --git a/core/fxcrt/xml/cfx_xmlchardata.h b/core/fxcrt/xml/cfx_xmlchardata.h +index 4d3a7f01d..356d55548 100644 +--- a/core/fxcrt/xml/cfx_xmlchardata.h ++++ b/core/fxcrt/xml/cfx_xmlchardata.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,7 @@ + #ifndef CORE_FXCRT_XML_CFX_XMLCHARDATA_H_ + #define CORE_FXCRT_XML_CFX_XMLCHARDATA_H_ + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" + #include "core/fxcrt/xml/cfx_xmltext.h" + + class CFX_XMLDocument; +@@ -20,7 +20,7 @@ class CFX_XMLCharData final : public CFX_XMLText { + // CFX_XMLNode + Type GetType() const override; + CFX_XMLNode* Clone(CFX_XMLDocument* doc) override; +- void Save(const RetainPtr& pXMLStream) override; ++ void Save(const RetainPtr& pXMLStream) override; + }; + + inline CFX_XMLCharData* ToXMLCharData(CFX_XMLNode* pNode) { +diff --git a/core/fxcrt/xml/cfx_xmlchardata_unittest.cpp b/core/fxcrt/xml/cfx_xmlchardata_unittest.cpp +index 60798bd27..8afa2ca06 100644 +--- a/core/fxcrt/xml/cfx_xmlchardata_unittest.cpp ++++ b/core/fxcrt/xml/cfx_xmlchardata_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcrt/xml/cfx_xmldocument.cpp b/core/fxcrt/xml/cfx_xmldocument.cpp +index 1c6656d1c..f2c38da9e 100644 +--- a/core/fxcrt/xml/cfx_xmldocument.cpp ++++ b/core/fxcrt/xml/cfx_xmldocument.cpp +@@ -1,15 +1,15 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcrt/xml/cfx_xmldocument.h" + + #include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/xml/cfx_xmlelement.h" + #include "core/fxcrt/xml/cfx_xmlinstruction.h" + +-CFX_XMLDocument::CFX_XMLDocument() { +- root_ = CreateNode(L"root"); +-} ++CFX_XMLDocument::CFX_XMLDocument() ++ : root_(CreateNode(L"root")) {} + + CFX_XMLDocument::~CFX_XMLDocument() = default; + +diff --git a/core/fxcrt/xml/cfx_xmldocument.h b/core/fxcrt/xml/cfx_xmldocument.h +index 9931314ec..053cc610b 100644 +--- a/core/fxcrt/xml/cfx_xmldocument.h ++++ b/core/fxcrt/xml/cfx_xmldocument.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,9 +10,8 @@ + #include + + #include "core/fxcrt/unowned_ptr.h" +-#include "core/fxcrt/xml/cfx_xmlelement.h" +-#include "third_party/base/ptr_util.h" + ++class CFX_XMLElement; + class CFX_XMLNode; + + class CFX_XMLDocument { +@@ -20,11 +19,11 @@ class CFX_XMLDocument { + CFX_XMLDocument(); + ~CFX_XMLDocument(); + +- CFX_XMLElement* GetRoot() const { return root_.Get(); } ++ CFX_XMLElement* GetRoot() const { return root_; } + + template + T* CreateNode(Args&&... args) { +- nodes_.push_back(pdfium::MakeUnique(std::forward(args)...)); ++ nodes_.push_back(std::make_unique(std::forward(args)...)); + return static_cast(nodes_.back().get()); + } + +diff --git a/core/fxcrt/xml/cfx_xmldocument_unittest.cpp b/core/fxcrt/xml/cfx_xmldocument_unittest.cpp +index 8043cc69a..7faabffd0 100644 +--- a/core/fxcrt/xml/cfx_xmldocument_unittest.cpp ++++ b/core/fxcrt/xml/cfx_xmldocument_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcrt/xml/cfx_xmlelement.cpp b/core/fxcrt/xml/cfx_xmlelement.cpp +index b999f5cda..81b0865f9 100644 +--- a/core/fxcrt/xml/cfx_xmlelement.cpp ++++ b/core/fxcrt/xml/cfx_xmlelement.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,16 +6,14 @@ + + #include "core/fxcrt/xml/cfx_xmlelement.h" + +-#include +- +-#include "core/fxcrt/cfx_widetextbuf.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/xml/cfx_xmlchardata.h" + #include "core/fxcrt/xml/cfx_xmldocument.h" + #include "core/fxcrt/xml/cfx_xmltext.h" ++#include "third_party/base/check.h" + + CFX_XMLElement::CFX_XMLElement(const WideString& wsTag) : name_(wsTag) { +- ASSERT(!name_.IsEmpty()); ++ DCHECK(!name_.IsEmpty()); + } + + CFX_XMLElement::~CFX_XMLElement() = default; +@@ -69,24 +67,24 @@ WideString CFX_XMLElement::GetNamespaceURI() const { + } + + WideString CFX_XMLElement::GetTextData() const { +- CFX_WideTextBuf buffer; ++ WideString buffer; + for (CFX_XMLNode* pChild = GetFirstChild(); pChild; + pChild = pChild->GetNextSibling()) { + CFX_XMLText* pText = ToXMLText(pChild); + if (pText) +- buffer << pText->GetText(); ++ buffer += pText->GetText(); + } +- return buffer.MakeString(); ++ return buffer; + } + + void CFX_XMLElement::Save( +- const RetainPtr& pXMLStream) { ++ const RetainPtr& pXMLStream) { + ByteString bsNameEncoded = name_.ToUTF8(); + + pXMLStream->WriteString("<"); + pXMLStream->WriteString(bsNameEncoded.AsStringView()); + +- for (auto it : attrs_) { ++ for (const auto& it : attrs_) { + // Note, the space between attributes is added by AttributeToString which + // writes a blank as the first character. + pXMLStream->WriteString( +@@ -150,7 +148,7 @@ WideString CFX_XMLElement::AttributeToString(const WideString& name, + WideString ret = L" "; + ret += name; + ret += L"=\""; +- ret += EncodeEntities(value); ++ ret += value.EncodeEntities(); + ret += L"\""; + return ret; + } +diff --git a/core/fxcrt/xml/cfx_xmlelement.h b/core/fxcrt/xml/cfx_xmlelement.h +index efc9fbb11..d48bf9fe0 100644 +--- a/core/fxcrt/xml/cfx_xmlelement.h ++++ b/core/fxcrt/xml/cfx_xmlelement.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,7 +9,7 @@ + + #include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" + #include "core/fxcrt/xml/cfx_xmlnode.h" + + class CFX_XMLDocument; +@@ -22,7 +22,7 @@ class CFX_XMLElement final : public CFX_XMLNode { + // CFX_XMLNode + Type GetType() const override; + CFX_XMLNode* Clone(CFX_XMLDocument* doc) override; +- void Save(const RetainPtr& pXMLStream) override; ++ void Save(const RetainPtr& pXMLStream) override; + + const WideString& GetName() const { return name_; } + +@@ -32,7 +32,6 @@ class CFX_XMLElement final : public CFX_XMLNode { + bool HasAttribute(const WideString& name) const; + void SetAttribute(const WideString& name, const WideString& value); + WideString GetAttribute(const WideString& name) const; +- + void RemoveAttribute(const WideString& name); + + CFX_XMLElement* GetFirstChildNamed(WideStringView name) const; +diff --git a/core/fxcrt/xml/cfx_xmlelement_unittest.cpp b/core/fxcrt/xml/cfx_xmlelement_unittest.cpp +index 0053358b0..59d4b4088 100644 +--- a/core/fxcrt/xml/cfx_xmlelement_unittest.cpp ++++ b/core/fxcrt/xml/cfx_xmlelement_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxcrt/xml/cfx_xmlinstruction.cpp b/core/fxcrt/xml/cfx_xmlinstruction.cpp +index ac01f4e33..74389bb3e 100644 +--- a/core/fxcrt/xml/cfx_xmlinstruction.cpp ++++ b/core/fxcrt/xml/cfx_xmlinstruction.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,8 +6,6 @@ + + #include "core/fxcrt/xml/cfx_xmlinstruction.h" + +-#include +- + #include "core/fxcrt/fx_codepage.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/xml/cfx_xmldocument.h" +@@ -40,7 +38,7 @@ bool CFX_XMLInstruction::IsAcrobat() const { + } + + void CFX_XMLInstruction::Save( +- const RetainPtr& pXMLStream) { ++ const RetainPtr& pXMLStream) { + if (name_.EqualsASCIINoCase("xml")) { + pXMLStream->WriteString("\n"); + return; +diff --git a/core/fxcrt/xml/cfx_xmlinstruction.h b/core/fxcrt/xml/cfx_xmlinstruction.h +index f4ba112f2..72cdbdfe7 100644 +--- a/core/fxcrt/xml/cfx_xmlinstruction.h ++++ b/core/fxcrt/xml/cfx_xmlinstruction.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,7 +9,7 @@ + + #include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" + #include "core/fxcrt/xml/cfx_xmlnode.h" + + class CFX_XMLDocument; +@@ -22,7 +22,7 @@ class CFX_XMLInstruction final : public CFX_XMLNode { + // CFX_XMLNode + Type GetType() const override; + CFX_XMLNode* Clone(CFX_XMLDocument* doc) override; +- void Save(const RetainPtr& pXMLStream) override; ++ void Save(const RetainPtr& pXMLStream) override; + + bool IsOriginalXFAVersion() const; + bool IsAcrobat() const; +diff --git a/core/fxcrt/xml/cfx_xmlinstruction_unittest.cpp b/core/fxcrt/xml/cfx_xmlinstruction_unittest.cpp +index 97d9ddf56..3b7f1d908 100644 +--- a/core/fxcrt/xml/cfx_xmlinstruction_unittest.cpp ++++ b/core/fxcrt/xml/cfx_xmlinstruction_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include + +-#include "core/fxcrt/cfx_readonlymemorystream.h" ++#include "core/fxcrt/cfx_read_only_span_stream.h" + #include "core/fxcrt/xml/cfx_xmldocument.h" + #include "core/fxcrt/xml/cfx_xmlelement.h" + #include "core/fxcrt/xml/cfx_xmlparser.h" +@@ -87,7 +87,7 @@ TEST(CFX_XMLInstructionTest, ParseAndReSave) { + "\n" + ""; + +- auto in_stream = pdfium::MakeRetain( ++ auto in_stream = pdfium::MakeRetain( + pdfium::as_bytes(pdfium::make_span(input))); + + CFX_XMLParser parser(in_stream); +@@ -120,7 +120,7 @@ TEST(CFX_XMLInstructionTest, ParseAndReSaveInnerInstruction) { + "\n" + ""; + +- auto in_stream = pdfium::MakeRetain( ++ auto in_stream = pdfium::MakeRetain( + pdfium::as_bytes(pdfium::make_span(input))); + + CFX_XMLParser parser(in_stream); +diff --git a/core/fxcrt/xml/cfx_xmlnode.cpp b/core/fxcrt/xml/cfx_xmlnode.cpp +index 94b035b11..4ce2a60a9 100644 +--- a/core/fxcrt/xml/cfx_xmlnode.cpp ++++ b/core/fxcrt/xml/cfx_xmlnode.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -21,13 +21,3 @@ CFX_XMLNode* CFX_XMLNode::GetRoot() { + + return pParent; + } +- +-WideString CFX_XMLNode::EncodeEntities(const WideString& value) { +- WideString ret = value; +- ret.Replace(L"&", L"&"); +- ret.Replace(L"<", L"<"); +- ret.Replace(L">", L">"); +- ret.Replace(L"\'", L"'"); +- ret.Replace(L"\"", L"""); +- return ret; +-} +diff --git a/core/fxcrt/xml/cfx_xmlnode.h b/core/fxcrt/xml/cfx_xmlnode.h +index d4d48b1a2..5934bc703 100644 +--- a/core/fxcrt/xml/cfx_xmlnode.h ++++ b/core/fxcrt/xml/cfx_xmlnode.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,7 +8,6 @@ + #define CORE_FXCRT_XML_CFX_XMLNODE_H_ + + #include "core/fxcrt/fx_stream.h" +-#include "core/fxcrt/fx_string.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/tree_node.h" + +@@ -28,13 +27,10 @@ class CFX_XMLNode : public TreeNode { + + virtual Type GetType() const = 0; + virtual CFX_XMLNode* Clone(CFX_XMLDocument* doc) = 0; +- virtual void Save(const RetainPtr& pXMLStream) = 0; ++ virtual void Save(const RetainPtr& pXMLStream) = 0; + + CFX_XMLNode* GetRoot(); + void InsertChildNode(CFX_XMLNode* pNode, int32_t index); +- +- protected: +- WideString EncodeEntities(const WideString& value); + }; + + #endif // CORE_FXCRT_XML_CFX_XMLNODE_H_ +diff --git a/core/fxcrt/xml/cfx_xmlnode_unittest.cpp b/core/fxcrt/xml/cfx_xmlnode_unittest.cpp +index c879cfd73..ad075793d 100644 +--- a/core/fxcrt/xml/cfx_xmlnode_unittest.cpp ++++ b/core/fxcrt/xml/cfx_xmlnode_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -36,7 +36,7 @@ TEST(CFX_XMLNodeTest, GetParent) { + node1.AppendLastChild(&node2); + node2.AppendLastChild(&node3); + +- EXPECT_EQ(nullptr, node1.GetParent()); ++ EXPECT_FALSE(node1.GetParent()); + EXPECT_EQ(&node1, node2.GetParent()); + EXPECT_EQ(&node2, node3.GetParent()); + } +diff --git a/core/fxcrt/xml/cfx_xmlparser.cpp b/core/fxcrt/xml/cfx_xmlparser.cpp +index 9393bbdc9..a6f371f3b 100644 +--- a/core/fxcrt/xml/cfx_xmlparser.cpp ++++ b/core/fxcrt/xml/cfx_xmlparser.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,13 +6,15 @@ + + #include "core/fxcrt/xml/cfx_xmlparser.h" + ++#include ++ + #include +-#include + #include + #include + #include + + #include "core/fxcrt/cfx_seekablestreamproxy.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_codepage.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_safe_types.h" +@@ -22,7 +24,8 @@ + #include "core/fxcrt/xml/cfx_xmlinstruction.h" + #include "core/fxcrt/xml/cfx_xmlnode.h" + #include "core/fxcrt/xml/cfx_xmltext.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/notreached.h" + + namespace { + +@@ -39,7 +42,7 @@ struct FX_XMLNAMECHAR { + bool bStartChar; + }; + +-const FX_XMLNAMECHAR g_XMLNameChars[] = { ++constexpr FX_XMLNAMECHAR kXMLNameChars[] = { + {L'-', L'.', false}, {L'0', L'9', false}, {L':', L':', false}, + {L'A', L'Z', true}, {L'_', L'_', true}, {L'a', L'z', true}, + {0xB7, 0xB7, false}, {0xC0, 0xD6, true}, {0xD8, 0xF6, true}, +@@ -54,20 +57,20 @@ const FX_XMLNAMECHAR g_XMLNameChars[] = { + // static + bool CFX_XMLParser::IsXMLNameChar(wchar_t ch, bool bFirstChar) { + auto* it = std::lower_bound( +- std::begin(g_XMLNameChars), std::end(g_XMLNameChars), ch, ++ std::begin(kXMLNameChars), std::end(kXMLNameChars), ch, + [](const FX_XMLNAMECHAR& arg, wchar_t ch) { return arg.wEnd < ch; }); +- return it != std::end(g_XMLNameChars) && ch >= it->wStart && ++ return it != std::end(kXMLNameChars) && ch >= it->wStart && + (!bFirstChar || it->bStartChar); + } + + CFX_XMLParser::CFX_XMLParser(const RetainPtr& pStream) { +- ASSERT(pStream); ++ DCHECK(pStream); + + auto proxy = pdfium::MakeRetain(pStream); +- uint16_t wCodePage = proxy->GetCodePage(); +- if (wCodePage != FX_CODEPAGE_UTF16LE && wCodePage != FX_CODEPAGE_UTF16BE && +- wCodePage != FX_CODEPAGE_UTF8) { +- proxy->SetCodePage(FX_CODEPAGE_UTF8); ++ FX_CodePage wCodePage = proxy->GetCodePage(); ++ if (wCodePage != FX_CodePage::kUTF16LE && ++ wCodePage != FX_CodePage::kUTF16BE && wCodePage != FX_CodePage::kUTF8) { ++ proxy->SetCodePage(FX_CodePage::kUTF8); + } + stream_ = proxy; + +@@ -80,7 +83,7 @@ CFX_XMLParser::CFX_XMLParser(const RetainPtr& pStream) { + CFX_XMLParser::~CFX_XMLParser() = default; + + std::unique_ptr CFX_XMLParser::Parse() { +- auto doc = pdfium::MakeUnique(); ++ auto doc = std::make_unique(); + current_node_ = doc->GetRoot(); + + return DoSyntaxParse(doc.get()) ? std::move(doc) : nullptr; +@@ -95,17 +98,16 @@ bool CFX_XMLParser::DoSyntaxParse(CFX_XMLDocument* doc) { + if (!alloc_size_safe.IsValid()) + return false; + +- FX_FILESIZE current_buffer_idx = 0; +- FX_FILESIZE buffer_size = 0; ++ size_t current_buffer_idx = 0; ++ size_t buffer_size = 0; + +- std::vector> buffer; ++ DataVector buffer; + buffer.resize(alloc_size_safe.ValueOrDie()); + + std::stack character_to_skip_too_stack; + std::stack node_type_stack; + WideString current_attribute_name; + FDE_XmlSyntaxState current_parser_state = FDE_XmlSyntaxState::Text; +- int32_t iCount = 0; + wchar_t current_quote_character = 0; + wchar_t current_character_to_skip_to = 0; + +@@ -262,7 +264,7 @@ bool CFX_XMLParser::DoSyntaxParse(CFX_XMLDocument* doc) { + break; + case FDE_XmlSyntaxState::AttriValue: + if (ch == current_quote_character) { +- if (entity_start_ > -1) ++ if (entity_start_.has_value()) + return false; + + current_quote_character = 0; +@@ -328,7 +330,6 @@ bool CFX_XMLParser::DoSyntaxParse(CFX_XMLDocument* doc) { + } + + current_node_ = current_node_->GetParent(); +- iCount++; + } else if (!IsXMLWhiteSpace(ch)) { + return false; + } +@@ -470,27 +471,27 @@ bool CFX_XMLParser::DoSyntaxParse(CFX_XMLDocument* doc) { + void CFX_XMLParser::ProcessTextChar(wchar_t character) { + current_text_.push_back(character); + +- if (entity_start_ > -1 && character == L';') { ++ if (entity_start_.has_value() && character == L';') { + // Copy the entity out into a string and remove from the vector. When we + // copy the entity we don't want to copy out the & or the ; so we start + // shifted by one and want to copy 2 less characters in total. +- WideString csEntity(current_text_.data() + entity_start_ + 1, +- current_text_.size() - entity_start_ - 2); +- current_text_.erase(current_text_.begin() + entity_start_, ++ WideString csEntity(current_text_.data() + entity_start_.value() + 1, ++ current_text_.size() - entity_start_.value() - 2); ++ current_text_.erase(current_text_.begin() + entity_start_.value(), + current_text_.end()); + +- int32_t iLen = csEntity.GetLength(); ++ size_t iLen = csEntity.GetLength(); + if (iLen > 0) { + if (csEntity[0] == L'#') { + uint32_t ch = 0; + if (iLen > 1 && csEntity[1] == L'x') { +- for (int32_t i = 2; i < iLen; i++) { ++ for (size_t i = 2; i < iLen; i++) { + if (!FXSYS_IsHexDigit(csEntity[i])) + break; + ch = (ch << 4) + FXSYS_HexCharToInt(csEntity[i]); + } + } else { +- for (int32_t i = 1; i < iLen; i++) { ++ for (size_t i = 1; i < iLen; i++) { + if (!FXSYS_IsDecimalDigit(csEntity[i])) + break; + ch = ch * 10 + FXSYS_DecimalCharToInt(csEntity[i]); +@@ -503,22 +504,21 @@ void CFX_XMLParser::ProcessTextChar(wchar_t character) { + if (character != 0) + current_text_.push_back(character); + } else { +- if (csEntity.Compare(L"amp") == 0) { ++ if (csEntity == L"amp") { + current_text_.push_back(L'&'); +- } else if (csEntity.Compare(L"lt") == 0) { ++ } else if (csEntity == L"lt") { + current_text_.push_back(L'<'); +- } else if (csEntity.Compare(L"gt") == 0) { ++ } else if (csEntity == L"gt") { + current_text_.push_back(L'>'); +- } else if (csEntity.Compare(L"apos") == 0) { ++ } else if (csEntity == L"apos") { + current_text_.push_back(L'\''); +- } else if (csEntity.Compare(L"quot") == 0) { ++ } else if (csEntity == L"quot") { + current_text_.push_back(L'"'); + } + } + } +- +- entity_start_ = -1; +- } else if (entity_start_ < 0 && character == L'&') { ++ entity_start_ = absl::nullopt; ++ } else if (!entity_start_.has_value() && character == L'&') { + entity_start_ = current_text_.size() - 1; + } + } +@@ -535,7 +535,7 @@ void CFX_XMLParser::ProcessTargetData() { + + WideString CFX_XMLParser::GetTextData() { + WideString ret(current_text_.data(), current_text_.size()); +- entity_start_ = -1; ++ entity_start_ = absl::nullopt; + current_text_.clear(); + current_text_.reserve(kCurrentTextReserve); + return ret; +diff --git a/core/fxcrt/xml/cfx_xmlparser.h b/core/fxcrt/xml/cfx_xmlparser.h +index ef171c663..7a30b7c7f 100644 +--- a/core/fxcrt/xml/cfx_xmlparser.h ++++ b/core/fxcrt/xml/cfx_xmlparser.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,15 +8,14 @@ + #define CORE_FXCRT_XML_CFX_XMLPARSER_H_ + + #include +-#include + +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/retain_ptr.h" ++#include "core/fxcrt/widestring.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CFX_SeekableStreamProxy; + class CFX_XMLDocument; +-class CFX_XMLElement; + class CFX_XMLNode; + class IFX_SeekableReadStream; + +@@ -56,9 +55,9 @@ class CFX_XMLParser final { + + CFX_XMLNode* current_node_ = nullptr; + RetainPtr stream_; +- std::vector> current_text_; ++ DataVector current_text_; + size_t xml_plane_size_ = 1024; +- int32_t entity_start_ = -1; ++ absl::optional entity_start_; + }; + + #endif // CORE_FXCRT_XML_CFX_XMLPARSER_H_ +diff --git a/core/fxcrt/xml/cfx_xmlparser_unittest.cpp b/core/fxcrt/xml/cfx_xmlparser_unittest.cpp +index a1fa8f30a..c692a0b9d 100644 +--- a/core/fxcrt/xml/cfx_xmlparser_unittest.cpp ++++ b/core/fxcrt/xml/cfx_xmlparser_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include + +-#include "core/fxcrt/cfx_readonlymemorystream.h" ++#include "core/fxcrt/cfx_read_only_span_stream.h" + #include "core/fxcrt/fx_codepage.h" + #include "core/fxcrt/xml/cfx_xmldocument.h" + #include "core/fxcrt/xml/cfx_xmlelement.h" +@@ -17,7 +17,7 @@ class CFX_XMLParserTest : public testing::Test { + public: + std::unique_ptr Parse(pdfium::span input) { + CFX_XMLParser parser( +- pdfium::MakeRetain(pdfium::as_bytes(input))); ++ pdfium::MakeRetain(pdfium::as_bytes(input))); + return parser.Parse(); + } + }; +@@ -290,6 +290,27 @@ TEST_F(CFX_XMLParserTest, IsXMLNameChar) { + EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L'-', true)); + EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(L'-', false)); + ++ EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L'.', true)); ++ EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(L'.', false)); ++ ++ EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L'0', true)); ++ EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(L'0', false)); ++ ++ EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(L'a', true)); ++ EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(L'a', false)); ++ ++ EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(L'A', true)); ++ EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(L'A', false)); ++ ++ EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L'(', false)); ++ EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L'(', true)); ++ EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L')', false)); ++ EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L')', true)); ++ EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L'[', false)); ++ EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L'[', true)); ++ EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L']', false)); ++ EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(L']', true)); ++ + EXPECT_FALSE(CFX_XMLParser::IsXMLNameChar(0x2069, true)); + EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(0x2070, true)); + EXPECT_TRUE(CFX_XMLParser::IsXMLNameChar(0x2073, true)); +diff --git a/core/fxcrt/xml/cfx_xmltext.cpp b/core/fxcrt/xml/cfx_xmltext.cpp +index 67c35a56f..bfcf36884 100644 +--- a/core/fxcrt/xml/cfx_xmltext.cpp ++++ b/core/fxcrt/xml/cfx_xmltext.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -20,6 +20,6 @@ CFX_XMLNode* CFX_XMLText::Clone(CFX_XMLDocument* doc) { + return doc->CreateNode(text_); + } + +-void CFX_XMLText::Save(const RetainPtr& pXMLStream) { +- pXMLStream->WriteString(EncodeEntities(GetText()).ToUTF8().AsStringView()); ++void CFX_XMLText::Save(const RetainPtr& pXMLStream) { ++ pXMLStream->WriteString(GetText().EncodeEntities().ToUTF8().AsStringView()); + } +diff --git a/core/fxcrt/xml/cfx_xmltext.h b/core/fxcrt/xml/cfx_xmltext.h +index 72ca24238..096801a0a 100644 +--- a/core/fxcrt/xml/cfx_xmltext.h ++++ b/core/fxcrt/xml/cfx_xmltext.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,7 @@ + #ifndef CORE_FXCRT_XML_CFX_XMLTEXT_H_ + #define CORE_FXCRT_XML_CFX_XMLTEXT_H_ + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" + #include "core/fxcrt/xml/cfx_xmlnode.h" + + class CFX_XMLDocument; +@@ -20,7 +20,7 @@ class CFX_XMLText : public CFX_XMLNode { + // CFX_XMLNode + Type GetType() const override; + CFX_XMLNode* Clone(CFX_XMLDocument* doc) override; +- void Save(const RetainPtr& pXMLStream) override; ++ void Save(const RetainPtr& pXMLStream) override; + + const WideString& GetText() const { return text_; } + void SetText(const WideString& wsText) { text_ = wsText; } +diff --git a/core/fxcrt/xml/cfx_xmltext_unittest.cpp b/core/fxcrt/xml/cfx_xmltext_unittest.cpp +index 0df003a30..55e879c7d 100644 +--- a/core/fxcrt/xml/cfx_xmltext_unittest.cpp ++++ b/core/fxcrt/xml/cfx_xmltext_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxge/Android.bp b/core/fxge/Android.bp +index 973ca03d3..3518bd645 100644 +--- a/core/fxge/Android.bp ++++ b/core/fxge/Android.bp +@@ -23,6 +23,7 @@ cc_library_static { + "cfx_unicodeencodingex.cpp", + // is_win + "dib/cfx_dibextractor.cpp", ++ "cfx_windowsrenderdevice.cpp", + // is_linux + "fx_ge_linux.cpp", + ], +diff --git a/core/fxge/BUILD.gn b/core/fxge/BUILD.gn +index 04742d067..c526c06a9 100644 +--- a/core/fxge/BUILD.gn ++++ b/core/fxge/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -6,25 +6,23 @@ import("//build/config/freetype/freetype.gni") + import("../../pdfium.gni") + import("../../testing/test.gni") + +-config("fxge_warnings") { +- visibility = [ ":*" ] +- if (is_clang) { +- cflags = [ +- # http://code.google.com/p/pdfium/issues/detail?id=188 +- "-Wno-switch", +- ] +- } +-} +- + source_set("fxge") { + sources = [ ++ "agg/fx_agg_driver.cpp", ++ "agg/fx_agg_driver.h", ++ "calculate_pitch.cpp", ++ "calculate_pitch.h", + "cfx_cliprgn.cpp", + "cfx_cliprgn.h", + "cfx_color.cpp", + "cfx_color.h", ++ "cfx_defaultrenderdevice.cpp", + "cfx_defaultrenderdevice.h", ++ "cfx_drawutils.cpp", ++ "cfx_drawutils.h", + "cfx_face.cpp", + "cfx_face.h", ++ "cfx_fillrenderoptions.h", + "cfx_folderfontinfo.cpp", + "cfx_folderfontinfo.h", + "cfx_font.cpp", +@@ -45,12 +43,13 @@ source_set("fxge") { + "cfx_graphstate.h", + "cfx_graphstatedata.cpp", + "cfx_graphstatedata.h", +- "cfx_pathdata.cpp", +- "cfx_pathdata.h", ++ "cfx_path.cpp", ++ "cfx_path.h", + "cfx_renderdevice.cpp", + "cfx_renderdevice.h", + "cfx_substfont.cpp", + "cfx_substfont.h", ++ "cfx_textrenderoptions.h", + "cfx_unicodeencoding.cpp", + "cfx_unicodeencoding.h", + "dib/cfx_bitmapcomposer.cpp", +@@ -73,7 +72,8 @@ source_set("fxge") { + "dib/cfx_scanlinecompositor.h", + "dib/cstretchengine.cpp", + "dib/cstretchengine.h", +- "dib/fx_dib_main.cpp", ++ "dib/fx_dib.cpp", ++ "dib/fx_dib.h", + "dib/scanlinecomposer_iface.h", + "fontdata/chromefontdata/FoxitDingbats.cpp", + "fontdata/chromefontdata/FoxitFixed.cpp", +@@ -93,17 +93,14 @@ source_set("fxge") { + "fontdata/chromefontdata/FoxitSymbol.cpp", + "fontdata/chromefontdata/chromefontdata.h", + "freetype/fx_freetype.cpp", +- "fx_dib.h", ++ "freetype/fx_freetype.h", + "fx_font.cpp", + "fx_font.h", +- "fx_freetype.h", +- "fx_ge_fontmap.cpp", + "render_defines.h", + "renderdevicedriver_iface.cpp", + "renderdevicedriver_iface.h", + "scoped_font_transform.cpp", + "scoped_font_transform.h", +- "scoped_font_transform.h", + "systemfontinfo_iface.h", + "text_char_pos.cpp", + "text_char_pos.h", +@@ -112,11 +109,16 @@ source_set("fxge") { + ] + + configs += [ +- ":fxge_warnings", +- "../../:pdfium_core_config", ++ "../../:pdfium_strict_config", ++ "../../:pdfium_noshorten_config", + ] + +- deps = [ "../fxcrt" ] ++ deps = [ ++ "../../third_party:fx_agg", ++ "../fxcrt", ++ ] ++ ++ public_deps = [] + + if (is_component_build || use_system_freetype) { + # ft_adobe_glyph_list is not exported from the Freetype shared library so we +@@ -131,15 +133,9 @@ source_set("fxge") { + ] + } + +- if (pdf_use_skia || pdf_use_skia_paths) { ++ if (pdf_use_skia) { + sources += [ "skia/fx_skia_device.cpp" ] +- deps += [ "//skia" ] +- } else { +- sources += [ +- "agg/fx_agg_driver.cpp", +- "agg/fx_agg_driver.h", +- ] +- deps += [ "../../third_party:fx_agg" ] ++ public_deps += [ "//skia" ] + } + + if (is_android) { +@@ -154,39 +150,51 @@ source_set("fxge") { + "android/cfpf_skiapathfont.h", + "android/cfx_androidfontinfo.cpp", + "android/cfx_androidfontinfo.h", +- "android/fx_android_imp.cpp", ++ "android/fx_android_impl.cpp", + ] + } + +- if (is_linux) { +- sources += [ "fx_ge_linux.cpp" ] ++ if (is_linux || is_chromeos || is_fuchsia) { ++ sources += [ "linux/fx_linux_impl.cpp" ] + } + + if (is_mac) { + sources += [ +- "apple/apple_int.h", ++ "apple/fx_apple_impl.cpp", + "apple/fx_apple_platform.cpp", +- "apple/fx_mac_imp.cpp", ++ "apple/fx_apple_platform.h", + "apple/fx_quartz_device.cpp", ++ "apple/fx_quartz_device.h", + ] +- libs = [ "CoreGraphics.framework" ] ++ frameworks = [ "CoreGraphics.framework" ] + } + + if (is_win) { + sources += [ ++ "cfx_windowsrenderdevice.cpp", + "cfx_windowsrenderdevice.h", + "dib/cfx_dibextractor.cpp", + "dib/cfx_dibextractor.h", ++ "win32/cfx_psfonttracker.cpp", ++ "win32/cfx_psfonttracker.h", + "win32/cfx_psrenderer.cpp", + "win32/cfx_psrenderer.h", +- "win32/cfx_windowsdib.h", ++ "win32/cgdi_device_driver.cpp", ++ "win32/cgdi_device_driver.h", ++ "win32/cgdi_display_driver.cpp", ++ "win32/cgdi_display_driver.h", ++ "win32/cgdi_plus_ext.cpp", ++ "win32/cgdi_plus_ext.h", ++ "win32/cgdi_printer_driver.cpp", ++ "win32/cgdi_printer_driver.h", ++ "win32/cps_printer_driver.cpp", ++ "win32/cps_printer_driver.h", + "win32/cpsoutput.cpp", + "win32/cpsoutput.h", +- "win32/fx_win32_device.cpp", +- "win32/fx_win32_dib.cpp", +- "win32/fx_win32_gdipext.cpp", +- "win32/fx_win32_print.cpp", +- "win32/win32_int.h", ++ "win32/ctext_only_printer_driver.cpp", ++ "win32/ctext_only_printer_driver.h", ++ "win32/cwin32_platform.cpp", ++ "win32/cwin32_platform.h", + ] + configs -= [ "//build/config/win:lean_and_mean" ] + } +@@ -196,8 +204,12 @@ source_set("fxge") { + + pdfium_unittest_source_set("unittests") { + sources = [ ++ "cfx_defaultrenderdevice_unittest.cpp", ++ "cfx_folderfontinfo_unittest.cpp", + "cfx_fontmapper_unittest.cpp", ++ "cfx_path_unittest.cpp", + "dib/cfx_cmyk_to_srgb_unittest.cpp", ++ "dib/cfx_dibbase_unittest.cpp", + "dib/cfx_dibitmap_unittest.cpp", + "dib/cstretchengine_unittest.cpp", + "fx_font_unittest.cpp", +@@ -208,6 +220,10 @@ pdfium_unittest_source_set("unittests") { + "../fpdfapi/parser", + ] + pdfium_root_dir = "../../" ++ ++ if (is_win) { ++ sources += [ "win32/cfx_psrenderer_unittest.cpp" ] ++ } + } + + pdfium_embeddertest_source_set("embeddertests") { +@@ -215,7 +231,7 @@ pdfium_embeddertest_source_set("embeddertests") { + deps = [] + pdfium_root_dir = "../../" + +- if (pdf_use_skia || pdf_use_skia_paths) { ++ if (pdf_use_skia) { + sources += [ "skia/fx_skia_device_embeddertest.cpp" ] + deps += [ + ":fxge", +@@ -225,7 +241,7 @@ pdfium_embeddertest_source_set("embeddertests") { + } + + if (is_win) { +- sources += [ "win32/fx_win32_device_embeddertest.cpp" ] ++ sources += [ "cfx_windowsrenderdevice_embeddertest.cpp" ] + deps += [ + ":fxge", + "../fxcodec", +diff --git a/core/fxge/agg/fx_agg_driver.cpp b/core/fxge/agg/fx_agg_driver.cpp +index b2a7986df..a1949de54 100644 +--- a/core/fxge/agg/fx_agg_driver.cpp ++++ b/core/fxge/agg/fx_agg_driver.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,25 +6,34 @@ + + #include "core/fxge/agg/fx_agg_driver.h" + ++#include ++#include ++ + #include + #include + + #include "build/build_config.h" ++#include "core/fxcrt/fx_2d_size.h" ++#include "core/fxcrt/fx_safe_types.h" + #include "core/fxge/cfx_cliprgn.h" + #include "core/fxge/cfx_defaultrenderdevice.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/dib/cfx_dibitmap.h" + #include "core/fxge/dib/cfx_imagerenderer.h" + #include "core/fxge/dib/cfx_imagestretcher.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/cxx17_backports.h" ++#include "third_party/base/notreached.h" ++#include "third_party/base/span.h" + + // Ignore fallthrough warnings in agg23 headers. + #if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" + #endif ++#include "third_party/agg23/agg_clip_liang_barsky.h" + #include "third_party/agg23/agg_conv_dash.h" + #include "third_party/agg23/agg_conv_stroke.h" + #include "third_party/agg23/agg_curves.h" +@@ -37,6 +46,7 @@ + #pragma GCC diagnostic pop + #endif + ++namespace pdfium { + namespace { + + const float kMaxPos = 32000.0f; +@@ -46,26 +56,6 @@ CFX_PointF HardClip(const CFX_PointF& pos) { + pdfium::clamp(pos.y, -kMaxPos, kMaxPos)); + } + +-void RgbByteOrderSetPixel(const RetainPtr& pBitmap, +- int x, +- int y, +- uint32_t argb) { +- if (x < 0 || x >= pBitmap->GetWidth() || y < 0 || y >= pBitmap->GetHeight()) +- return; +- +- uint8_t* pos = pBitmap->GetBuffer() + y * pBitmap->GetPitch() + +- x * pBitmap->GetBPP() / 8; +- if (pBitmap->GetFormat() == FXDIB_Argb) { +- FXARGB_SETRGBORDERDIB(pos, argb); +- return; +- } +- +- int alpha = FXARGB_A(argb); +- pos[0] = (FXARGB_R(argb) * alpha + pos[0] * (255 - alpha)) / 255; +- pos[1] = (FXARGB_G(argb) * alpha + pos[1] * (255 - alpha)) / 255; +- pos[2] = (FXARGB_B(argb) * alpha + pos[2] * (255 - alpha)) / 255; +-} +- + void RgbByteOrderCompositeRect(const RetainPtr& pBitmap, + int left, + int top, +@@ -84,15 +74,13 @@ void RgbByteOrderCompositeRect(const RetainPtr& pBitmap, + int src_b = FXARGB_B(argb); + int Bpp = pBitmap->GetBPP() / 8; + int dib_argb = FXARGB_TOBGRORDERDIB(argb); +- uint8_t* pBuffer = pBitmap->GetBuffer(); ++ pdfium::span pBuffer = pBitmap->GetBuffer(); + if (src_alpha == 255) { + for (int row = rect.top; row < rect.bottom; row++) { + uint8_t* dest_scan = +- pBuffer + row * pBitmap->GetPitch() + rect.left * Bpp; ++ pBuffer.subspan(row * pBitmap->GetPitch() + rect.left * Bpp).data(); + if (Bpp == 4) { +- uint32_t* scan = reinterpret_cast(dest_scan); +- for (int col = 0; col < width; col++) +- *scan++ = dib_argb; ++ std::fill_n(reinterpret_cast(dest_scan), width, dib_argb); + } else { + for (int col = 0; col < width; col++) { + *dest_scan++ = src_r; +@@ -103,15 +91,15 @@ void RgbByteOrderCompositeRect(const RetainPtr& pBitmap, + } + return; + } +- bool bAlpha = pBitmap->HasAlpha(); ++ bool bAlpha = pBitmap->IsAlphaFormat(); + for (int row = rect.top; row < rect.bottom; row++) { +- uint8_t* dest_scan = pBuffer + row * pBitmap->GetPitch() + rect.left * Bpp; ++ uint8_t* dest_scan = ++ pBuffer.subspan(row * pBitmap->GetPitch() + rect.left * Bpp).data(); + if (bAlpha) { + for (int col = 0; col < width; col++) { + uint8_t back_alpha = dest_scan[3]; + if (back_alpha == 0) { +- FXARGB_SETRGBORDERDIB(dest_scan, +- ArgbEncode(src_alpha, src_r, src_g, src_b)); ++ FXARGB_SETRGBORDERDIB(dest_scan, argb); + dest_scan += 4; + continue; + } +@@ -142,113 +130,114 @@ void RgbByteOrderCompositeRect(const RetainPtr& pBitmap, + } + + void RgbByteOrderTransferBitmap(const RetainPtr& pBitmap, +- int dest_left, +- int dest_top, + int width, + int height, + const RetainPtr& pSrcBitmap, + int src_left, + int src_top) { +- if (!pBitmap) +- return; +- ++ int dest_left = 0; ++ int dest_top = 0; + if (!pBitmap->GetOverlapRect(dest_left, dest_top, width, height, + pSrcBitmap->GetWidth(), pSrcBitmap->GetHeight(), + src_left, src_top, nullptr)) { + return; + } + +- int Bpp = pBitmap->GetBPP() / 8; +- FXDIB_Format dest_format = pBitmap->GetFormat(); +- FXDIB_Format src_format = pSrcBitmap->GetFormat(); +- int pitch = pBitmap->GetPitch(); +- uint8_t* buffer = pBitmap->GetBuffer(); ++ const int Bpp = pBitmap->GetBPP() / 8; ++ const FXDIB_Format dest_format = pBitmap->GetFormat(); ++ const FXDIB_Format src_format = pSrcBitmap->GetFormat(); ++ const int dest_pitch = pBitmap->GetPitch(); ++ ++ const size_t dest_x_offset = Fx2DSizeOrDie(dest_left, Bpp); ++ const size_t dest_y_offset = Fx2DSizeOrDie(dest_top, dest_pitch); ++ ++ pdfium::span dest_span = ++ pBitmap->GetBuffer().subspan(dest_y_offset).subspan(dest_x_offset); + if (dest_format == src_format) { ++ const size_t src_x_offset = Fx2DSizeOrDie(src_left, Bpp); + for (int row = 0; row < height; row++) { +- uint8_t* dest_scan = buffer + (dest_top + row) * pitch + dest_left * Bpp; ++ uint8_t* dest_scan = dest_span.data(); + const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left * Bpp; ++ pSrcBitmap->GetScanline(src_top + row).subspan(src_x_offset).data(); + if (Bpp == 4) { + for (int col = 0; col < width; col++) { +- FXARGB_SETDIB(dest_scan, ArgbEncode(src_scan[3], src_scan[0], +- src_scan[1], src_scan[2])); ++ FXARGB_SETRGBORDERDIB(dest_scan, ++ *reinterpret_cast(src_scan)); + dest_scan += 4; + src_scan += 4; + } +- continue; +- } +- for (int col = 0; col < width; col++) { +- *dest_scan++ = src_scan[2]; +- *dest_scan++ = src_scan[1]; +- *dest_scan++ = src_scan[0]; +- src_scan += 3; ++ } else { ++ for (int col = 0; col < width; col++) { ++ *dest_scan++ = src_scan[2]; ++ *dest_scan++ = src_scan[1]; ++ *dest_scan++ = src_scan[0]; ++ src_scan += 3; ++ } + } ++ dest_span = dest_span.subspan(dest_pitch); + } + return; + } + +- uint8_t* dest_buf = buffer + dest_top * pitch + dest_left * Bpp; +- if (dest_format == FXDIB_Rgb) { +- ASSERT(src_format == FXDIB_Rgb32); ++ if (dest_format == FXDIB_Format::kRgb) { ++ DCHECK_EQ(src_format, FXDIB_Format::kRgb32); ++ const size_t src_x_offset = Fx2DSizeOrDie(src_left, 4); + for (int row = 0; row < height; row++) { +- uint8_t* dest_scan = dest_buf + row * pitch; ++ uint8_t* dest_scan = dest_span.data(); + const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left * 4; ++ pSrcBitmap->GetScanline(src_top + row).subspan(src_x_offset).data(); + for (int col = 0; col < width; col++) { + *dest_scan++ = src_scan[2]; + *dest_scan++ = src_scan[1]; + *dest_scan++ = src_scan[0]; + src_scan += 4; + } ++ if (row < height - 1) { ++ // Since `dest_scan` was initialized in a way that takes `dest_x_offset` ++ // and `dest_y_offset` into account, it may go past the end of the span ++ // after processing the last row. ++ dest_span = dest_span.subspan(dest_pitch); ++ } + } + return; + } + +- ASSERT(dest_format == FXDIB_Argb || dest_format == FXDIB_Rgb32); +- if (src_format == FXDIB_Rgb) { ++ DCHECK(dest_format == FXDIB_Format::kArgb || ++ dest_format == FXDIB_Format::kRgb32); ++ if (src_format == FXDIB_Format::kRgb) { ++ const size_t src_x_offset = Fx2DSizeOrDie(src_left, 3); + for (int row = 0; row < height; row++) { +- uint8_t* dest_scan = dest_buf + row * pitch; ++ uint8_t* dest_scan = dest_span.data(); + const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left * 3; ++ pSrcBitmap->GetScanline(src_top + row).subspan(src_x_offset).data(); + for (int col = 0; col < width; col++) { + FXARGB_SETDIB(dest_scan, + ArgbEncode(0xff, src_scan[0], src_scan[1], src_scan[2])); + dest_scan += 4; + src_scan += 3; + } ++ dest_span = dest_span.subspan(dest_pitch); + } + return; + } +- if (src_format != FXDIB_Rgb32) ++ if (src_format != FXDIB_Format::kRgb32) + return; +- ASSERT(dest_format == FXDIB_Argb); ++ DCHECK_EQ(dest_format, FXDIB_Format::kArgb); ++ const size_t src_x_offset = Fx2DSizeOrDie(src_left, 4); + for (int row = 0; row < height; row++) { +- uint8_t* dest_scan = dest_buf + row * pitch; ++ uint8_t* dest_scan = dest_span.data(); + const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left * 4; ++ pSrcBitmap->GetScanline(src_top + row).subspan(src_x_offset).data(); + for (int col = 0; col < width; col++) { + FXARGB_SETDIB(dest_scan, + ArgbEncode(0xff, src_scan[0], src_scan[1], src_scan[2])); + src_scan += 4; + dest_scan += 4; + } ++ dest_span = dest_span.subspan(dest_pitch); + } + } + +-bool DibSetPixel(const RetainPtr& pDevice, +- int x, +- int y, +- uint32_t color) { +- int alpha = FXARGB_A(color); +- if (pDevice->IsCmykImage()) +- return false; +- +- pDevice->SetPixel(x, y, color); +- if (pDevice->m_pAlphaMask) +- pDevice->m_pAlphaMask->SetPixel(x, y, alpha << 24); +- return true; +-} +- + void RasterizeStroke(agg::rasterizer_scanline_aa* rasterizer, + agg::path_storage* path_data, + const CFX_Matrix* pObject2Device, +@@ -257,10 +246,10 @@ void RasterizeStroke(agg::rasterizer_scanline_aa* rasterizer, + bool bTextMode) { + agg::line_cap_e cap; + switch (pGraphState->m_LineCap) { +- case CFX_GraphStateData::LineCapRound: ++ case CFX_GraphStateData::LineCap::kRound: + cap = agg::round_cap; + break; +- case CFX_GraphStateData::LineCapSquare: ++ case CFX_GraphStateData::LineCap::kSquare: + cap = agg::square_cap; + break; + default: +@@ -269,10 +258,10 @@ void RasterizeStroke(agg::rasterizer_scanline_aa* rasterizer, + } + agg::line_join_e join; + switch (pGraphState->m_LineJoin) { +- case CFX_GraphStateData::LineJoinRound: ++ case CFX_GraphStateData::LineJoin::kRound: + join = agg::round_join; + break; +- case CFX_GraphStateData::LineJoinBevel: ++ case CFX_GraphStateData::LineJoin::kBevel: + join = agg::bevel_join; + break; + default: +@@ -287,12 +276,12 @@ void RasterizeStroke(agg::rasterizer_scanline_aa* rasterizer, + } + width = std::max(width, unit); + if (!pGraphState->m_DashArray.empty()) { +- typedef agg::conv_dash dash_converter; +- dash_converter dash(*path_data); ++ using DashConverter = agg::conv_dash; ++ DashConverter dash(*path_data); + for (size_t i = 0; i < (pGraphState->m_DashArray.size() + 1) / 2; i++) { + float on = pGraphState->m_DashArray[i * 2]; + if (on <= 0.000001f) +- on = 1.0f / 10; ++ on = 0.1f; + float off = i * 2 + 1 == pGraphState->m_DashArray.size() + ? on + : pGraphState->m_DashArray[i * 2 + 1]; +@@ -300,8 +289,8 @@ void RasterizeStroke(agg::rasterizer_scanline_aa* rasterizer, + dash.add_dash(on * scale, off * scale); + } + dash.dash_start(pGraphState->m_DashPhase * scale); +- typedef agg::conv_stroke dash_stroke; +- dash_stroke stroke(dash); ++ using DashStroke = agg::conv_stroke; ++ DashStroke stroke(dash); + stroke.line_join(join); + stroke.line_cap(cap); + stroke.miter_limit(pGraphState->m_MiterLimit); +@@ -317,28 +306,43 @@ void RasterizeStroke(agg::rasterizer_scanline_aa* rasterizer, + rasterizer->add_path_transformed(stroke, pObject2Device); + } + +-constexpr int kAlternateOrWindingFillModeMask = +- FXFILL_ALTERNATE | FXFILL_WINDING; +- +-int GetAlternateOrWindingFillMode(int fill_mode) { +- return fill_mode & kAlternateOrWindingFillModeMask; ++agg::filling_rule_e GetAlternateOrWindingFillType( ++ const CFX_FillRenderOptions& fill_options) { ++ return fill_options.fill_type == CFX_FillRenderOptions::FillType::kWinding ++ ? agg::fill_non_zero ++ : agg::fill_even_odd; + } + +-bool IsAlternateOrWindingFillMode(int fill_mode) { +- return !!GetAlternateOrWindingFillMode(fill_mode); ++RetainPtr GetClipMaskFromRegion(const CFX_ClipRgn* r) { ++ return (r && r->GetType() == CFX_ClipRgn::kMaskF) ? r->GetMask() : nullptr; + } + +-agg::filling_rule_e GetAlternateOrWindingFillType(int fill_mode) { +- return GetAlternateOrWindingFillMode(fill_mode) == FXFILL_WINDING +- ? agg::fill_non_zero +- : agg::fill_even_odd; ++FX_RECT GetClipBoxFromRegion(const RetainPtr& device, ++ const CFX_ClipRgn* region) { ++ if (region) ++ return region->GetBox(); ++ return FX_RECT(0, 0, device->GetWidth(), device->GetHeight()); + } + + class CFX_Renderer { + public: ++ CFX_Renderer(const RetainPtr& pDevice, ++ const RetainPtr& pBackdropDevice, ++ const CFX_ClipRgn* pClipRgn, ++ uint32_t color, ++ bool bFullCover, ++ bool bRgbByteOrder); ++ + // Needed for agg caller + void prepare(unsigned) {} + ++ template ++ void render(const Scanline& sl); ++ ++ private: ++ using CompositeSpanFunc = void ( ++ CFX_Renderer::*)(uint8_t*, int, int, int, uint8_t*, int, int, uint8_t*); ++ + void CompositeSpan(uint8_t* dest_scan, + uint8_t* backdrop_scan, + int Bpp, +@@ -357,8 +361,7 @@ class CFX_Renderer { + uint8_t* cover_scan, + int clip_left, + int clip_right, +- uint8_t* clip_scan, +- uint8_t* dest_extra_alpha_scan); ++ uint8_t* clip_scan); + + void CompositeSpanGray(uint8_t* dest_scan, + int Bpp, +@@ -367,8 +370,7 @@ class CFX_Renderer { + uint8_t* cover_scan, + int clip_left, + int clip_right, +- uint8_t* clip_scan, +- uint8_t* dest_extra_alpha_scan); ++ uint8_t* clip_scan); + + void CompositeSpanARGB(uint8_t* dest_scan, + int Bpp, +@@ -377,8 +379,7 @@ class CFX_Renderer { + uint8_t* cover_scan, + int clip_left, + int clip_right, +- uint8_t* clip_scan, +- uint8_t* dest_extra_alpha_scan); ++ uint8_t* clip_scan); + + void CompositeSpanRGB(uint8_t* dest_scan, + int Bpp, +@@ -387,39 +388,7 @@ class CFX_Renderer { + uint8_t* cover_scan, + int clip_left, + int clip_right, +- uint8_t* clip_scan, +- uint8_t* dest_extra_alpha_scan); +- +- void CompositeSpanCMYK(uint8_t* dest_scan, +- int Bpp, +- int span_left, +- int span_len, +- uint8_t* cover_scan, +- int clip_left, +- int clip_right, +- uint8_t* clip_scan, +- uint8_t* dest_extra_alpha_scan); +- +- bool Init(const RetainPtr& pDevice, +- const RetainPtr& pBackdropDevice, +- const CFX_ClipRgn* pClipRgn, +- uint32_t color, +- bool bFullCover, +- bool bRgbByteOrder); +- +- template +- void render(const Scanline& sl); +- +- private: +- void (CFX_Renderer::*composite_span)(uint8_t*, +- int, +- int, +- int, +- uint8_t*, +- int, +- int, +- uint8_t*, +- uint8_t*); ++ uint8_t* clip_scan); + + void CompositeSpan1bppHelper(uint8_t* dest_scan, + int col_start, +@@ -428,6 +397,17 @@ class CFX_Renderer { + const uint8_t* clip_scan, + int span_left); + ++ static CompositeSpanFunc GetCompositeSpanFunc( ++ const RetainPtr& device) { ++ if (device->GetBPP() == 1) ++ return &CFX_Renderer::CompositeSpan1bpp; ++ if (device->GetBPP() == 8) ++ return &CFX_Renderer::CompositeSpanGray; ++ if (device->GetFormat() == FXDIB_Format::kArgb) ++ return &CFX_Renderer::CompositeSpanARGB; ++ return &CFX_Renderer::CompositeSpanRGB; ++ } ++ + inline int GetSrcAlpha(const uint8_t* clip_scan, int col) const { + return clip_scan ? m_Alpha * clip_scan[col] / 255 : m_Alpha; + } +@@ -453,14 +433,15 @@ class CFX_Renderer { + int m_Green; + int m_Blue; + int m_Gray; +- uint32_t m_Color; +- bool m_bFullCover; +- bool m_bRgbByteOrder; +- FX_RECT m_ClipBox; +- RetainPtr m_pBackdropDevice; +- RetainPtr m_pClipMask; +- RetainPtr m_pDevice; ++ const uint32_t m_Color; ++ const bool m_bFullCover; ++ const bool m_bRgbByteOrder; ++ const FX_RECT m_ClipBox; ++ RetainPtr const m_pBackdropDevice; ++ RetainPtr const m_pClipMask; ++ RetainPtr const m_pDevice; + UnownedPtr m_pClipRgn; ++ const CompositeSpanFunc m_CompositeSpanFunc; + }; + + void CFX_Renderer::CompositeSpan(uint8_t* dest_scan, +@@ -473,7 +454,6 @@ void CFX_Renderer::CompositeSpan(uint8_t* dest_scan, + int clip_left, + int clip_right, + uint8_t* clip_scan) { +- ASSERT(!m_pDevice->IsCmykImage()); + int col_start = GetColStart(span_left, clip_left); + int col_end = GetColEnd(span_left, span_len, clip_right); + if (Bpp) { +@@ -615,10 +595,8 @@ void CFX_Renderer::CompositeSpan1bpp(uint8_t* dest_scan, + uint8_t* cover_scan, + int clip_left, + int clip_right, +- uint8_t* clip_scan, +- uint8_t* dest_extra_alpha_scan) { +- ASSERT(!m_bRgbByteOrder); +- ASSERT(!m_pDevice->IsCmykImage()); ++ uint8_t* clip_scan) { ++ DCHECK(!m_bRgbByteOrder); + int col_start = GetColStart(span_left, clip_left); + int col_end = GetColEnd(span_left, span_len, clip_right); + dest_scan += col_start / 8; +@@ -633,35 +611,11 @@ void CFX_Renderer::CompositeSpanGray(uint8_t* dest_scan, + uint8_t* cover_scan, + int clip_left, + int clip_right, +- uint8_t* clip_scan, +- uint8_t* dest_extra_alpha_scan) { +- ASSERT(!m_bRgbByteOrder); ++ uint8_t* clip_scan) { ++ DCHECK(!m_bRgbByteOrder); + int col_start = GetColStart(span_left, clip_left); + int col_end = GetColEnd(span_left, span_len, clip_right); + dest_scan += col_start; +- if (dest_extra_alpha_scan) { +- for (int col = col_start; col < col_end; col++) { +- int src_alpha = m_bFullCover ? GetSrcAlpha(clip_scan, col) +- : GetSourceAlpha(cover_scan, clip_scan, col); +- if (src_alpha) { +- if (src_alpha == 255) { +- *dest_scan = m_Gray; +- *dest_extra_alpha_scan = m_Alpha; +- } else { +- uint8_t dest_alpha = (*dest_extra_alpha_scan) + src_alpha - +- (*dest_extra_alpha_scan) * src_alpha / 255; +- *dest_extra_alpha_scan++ = dest_alpha; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Gray, alpha_ratio); +- dest_scan++; +- continue; +- } +- } +- dest_extra_alpha_scan++; +- dest_scan++; +- } +- return; +- } + for (int col = col_start; col < col_end; col++) { + int src_alpha = GetSourceAlpha(cover_scan, clip_scan, col); + if (src_alpha) { +@@ -681,8 +635,7 @@ void CFX_Renderer::CompositeSpanARGB(uint8_t* dest_scan, + uint8_t* cover_scan, + int clip_left, + int clip_right, +- uint8_t* clip_scan, +- uint8_t* dest_extra_alpha_scan) { ++ uint8_t* clip_scan) { + int col_start = GetColStart(span_left, clip_left); + int col_end = GetColEnd(span_left, span_len, clip_right); + dest_scan += col_start * Bpp; +@@ -750,8 +703,7 @@ void CFX_Renderer::CompositeSpanRGB(uint8_t* dest_scan, + uint8_t* cover_scan, + int clip_left, + int clip_right, +- uint8_t* clip_scan, +- uint8_t* dest_extra_alpha_scan) { ++ uint8_t* clip_scan) { + int col_start = GetColStart(span_left, clip_left); + int col_end = GetColEnd(span_left, span_len, clip_right); + dest_scan += col_start * Bpp; +@@ -782,35 +734,6 @@ void CFX_Renderer::CompositeSpanRGB(uint8_t* dest_scan, + } + return; + } +- if (Bpp == 3 && dest_extra_alpha_scan) { +- for (int col = col_start; col < col_end; col++) { +- int src_alpha = m_bFullCover ? GetSrcAlpha(clip_scan, col) +- : GetSourceAlpha(cover_scan, clip_scan, col); +- if (src_alpha) { +- if (src_alpha == 255) { +- *dest_scan++ = static_cast(m_Blue); +- *dest_scan++ = static_cast(m_Green); +- *dest_scan++ = static_cast(m_Red); +- *dest_extra_alpha_scan++ = static_cast(m_Alpha); +- continue; +- } +- uint8_t dest_alpha = (*dest_extra_alpha_scan) + src_alpha - +- (*dest_extra_alpha_scan) * src_alpha / 255; +- *dest_extra_alpha_scan++ = dest_alpha; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Blue, alpha_ratio); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Green, alpha_ratio); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Red, alpha_ratio); +- dest_scan++; +- continue; +- } +- dest_extra_alpha_scan++; +- dest_scan += Bpp; +- } +- return; +- } + for (int col = col_start; col < col_end; col++) { + int src_alpha = m_bFullCover ? GetSrcAlpha(clip_scan, col) + : GetSourceAlpha(cover_scan, clip_scan, col); +@@ -838,165 +761,70 @@ void CFX_Renderer::CompositeSpanRGB(uint8_t* dest_scan, + } + } + +-void CFX_Renderer::CompositeSpanCMYK(uint8_t* dest_scan, +- int Bpp, +- int span_left, +- int span_len, +- uint8_t* cover_scan, +- int clip_left, +- int clip_right, +- uint8_t* clip_scan, +- uint8_t* dest_extra_alpha_scan) { +- ASSERT(!m_bRgbByteOrder); +- int col_start = GetColStart(span_left, clip_left); +- int col_end = GetColEnd(span_left, span_len, clip_right); +- dest_scan += col_start * 4; +- if (dest_extra_alpha_scan) { +- for (int col = col_start; col < col_end; col++) { +- int src_alpha = m_bFullCover ? GetSrcAlpha(clip_scan, col) +- : GetSourceAlpha(cover_scan, clip_scan, col); +- if (src_alpha) { +- if (src_alpha == 255) { +- *(reinterpret_cast(dest_scan)) = m_Color; +- *dest_extra_alpha_scan = static_cast(m_Alpha); +- } else { +- uint8_t dest_alpha = (*dest_extra_alpha_scan) + src_alpha - +- (*dest_extra_alpha_scan) * src_alpha / 255; +- *dest_extra_alpha_scan++ = dest_alpha; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Red, alpha_ratio); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Green, alpha_ratio); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Blue, alpha_ratio); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Gray, alpha_ratio); +- dest_scan++; +- continue; +- } +- } +- dest_extra_alpha_scan++; +- dest_scan += 4; +- } +- return; +- } +- for (int col = col_start; col < col_end; col++) { +- int src_alpha = GetSourceAlpha(cover_scan, clip_scan, col); +- if (src_alpha) { +- if (src_alpha == 255) { +- *(reinterpret_cast(dest_scan)) = m_Color; +- } else { +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Red, src_alpha); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Green, src_alpha); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Blue, src_alpha); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Gray, src_alpha); +- dest_scan++; +- continue; +- } +- } +- dest_scan += 4; +- } +-} +- +-bool CFX_Renderer::Init(const RetainPtr& pDevice, +- const RetainPtr& pBackdropDevice, +- const CFX_ClipRgn* pClipRgn, +- uint32_t color, +- bool bFullCover, +- bool bRgbByteOrder) { +- m_pDevice = pDevice; +- m_pClipRgn = pClipRgn; +- composite_span = nullptr; +- m_bRgbByteOrder = bRgbByteOrder; +- m_pBackdropDevice = pBackdropDevice; +- if (m_pClipRgn) { +- m_ClipBox = m_pClipRgn->GetBox(); +- } else { +- m_ClipBox.left = m_ClipBox.top = 0; +- m_ClipBox.right = m_pDevice->GetWidth(); +- m_ClipBox.bottom = m_pDevice->GetHeight(); +- } +- m_pClipMask = nullptr; +- if (m_pClipRgn && m_pClipRgn->GetType() == CFX_ClipRgn::MaskF) +- m_pClipMask = m_pClipRgn->GetMask(); +- m_bFullCover = bFullCover; +- bool bDeviceCMYK = pDevice->IsCmykImage(); +- m_Alpha = FXARGB_A(color); ++CFX_Renderer::CFX_Renderer(const RetainPtr& pDevice, ++ const RetainPtr& pBackdropDevice, ++ const CFX_ClipRgn* pClipRgn, ++ uint32_t color, ++ bool bFullCover, ++ bool bRgbByteOrder) ++ : m_Alpha(FXARGB_A(color)), ++ m_Color(bRgbByteOrder ? FXARGB_TOBGRORDERDIB(color) : color), ++ m_bFullCover(bFullCover), ++ m_bRgbByteOrder(bRgbByteOrder), ++ m_ClipBox(GetClipBoxFromRegion(pDevice, pClipRgn)), ++ m_pBackdropDevice(pBackdropDevice), ++ m_pClipMask(GetClipMaskFromRegion(pClipRgn)), ++ m_pDevice(pDevice), ++ m_pClipRgn(pClipRgn), ++ m_CompositeSpanFunc(GetCompositeSpanFunc(m_pDevice)) { + if (m_pDevice->GetBPP() == 8) { +- ASSERT(!m_bRgbByteOrder); +- composite_span = &CFX_Renderer::CompositeSpanGray; +- if (m_pDevice->IsAlphaMask()) ++ DCHECK(!m_bRgbByteOrder); ++ if (m_pDevice->IsMaskFormat()) + m_Gray = 255; + else + m_Gray = FXRGB2GRAY(FXARGB_R(color), FXARGB_G(color), FXARGB_B(color)); +- return true; +- } +- if (bDeviceCMYK) { +- ASSERT(!m_bRgbByteOrder); +- composite_span = &CFX_Renderer::CompositeSpanCMYK; +- return false; ++ return; + } +- composite_span = (pDevice->GetFormat() == FXDIB_Argb) +- ? &CFX_Renderer::CompositeSpanARGB +- : &CFX_Renderer::CompositeSpanRGB; +- if (m_bRgbByteOrder) +- m_Color = FXARGB_TOBGRORDERDIB(color); +- else +- m_Color = FXARGB_TODIB(color); ++ + std::tie(m_Alpha, m_Red, m_Green, m_Blue) = ArgbDecode(color); +- if (m_pDevice->GetBPP() == 1) +- composite_span = &CFX_Renderer::CompositeSpan1bpp; +- return true; + } + + template + void CFX_Renderer::render(const Scanline& sl) { +- if (!m_pBackdropDevice && !composite_span) +- return; +- + int y = sl.y(); + if (y < m_ClipBox.top || y >= m_ClipBox.bottom) + return; + +- uint8_t* dest_scan = m_pDevice->GetBuffer() + m_pDevice->GetPitch() * y; +- uint8_t* dest_scan_extra_alpha = nullptr; +- RetainPtr pAlphaMask = m_pDevice->m_pAlphaMask; +- if (pAlphaMask) { +- dest_scan_extra_alpha = +- pAlphaMask->GetBuffer() + pAlphaMask->GetPitch() * y; +- } ++ uint8_t* dest_scan = ++ m_pDevice->GetBuffer().subspan(m_pDevice->GetPitch() * y).data(); + uint8_t* backdrop_scan = nullptr; + if (m_pBackdropDevice) { +- backdrop_scan = +- m_pBackdropDevice->GetBuffer() + m_pBackdropDevice->GetPitch() * y; ++ backdrop_scan = m_pBackdropDevice->GetBuffer() ++ .subspan(m_pBackdropDevice->GetPitch() * y) ++ .data(); + } + int Bpp = m_pDevice->GetBPP() / 8; +- bool bDestAlpha = m_pDevice->HasAlpha() || m_pDevice->IsAlphaMask(); ++ bool bDestAlpha = m_pDevice->IsAlphaFormat() || m_pDevice->IsMaskFormat(); + unsigned num_spans = sl.num_spans(); + typename Scanline::const_iterator span = sl.begin(); +- while (1) { ++ while (true) { + if (span->len <= 0) + break; + + int x = span->x; + uint8_t* dest_pos = nullptr; +- uint8_t* dest_extra_alpha_pos = nullptr; + uint8_t* backdrop_pos = nullptr; + if (Bpp) { + backdrop_pos = backdrop_scan ? backdrop_scan + x * Bpp : nullptr; + dest_pos = dest_scan + x * Bpp; +- dest_extra_alpha_pos = +- dest_scan_extra_alpha ? dest_scan_extra_alpha + x : nullptr; + } else { + dest_pos = dest_scan + x / 8; + backdrop_pos = backdrop_scan ? backdrop_scan + x / 8 : nullptr; + } + uint8_t* clip_pos = nullptr; + if (m_pClipMask) { +- clip_pos = m_pClipMask->GetBuffer() + ++ // TODO(crbug.com/1382604): use subspan arithmetic. ++ clip_pos = m_pClipMask->GetBuffer().data() + + (y - m_ClipBox.top) * m_pClipMask->GetPitch() + x - + m_ClipBox.left; + } +@@ -1004,9 +832,8 @@ void CFX_Renderer::render(const Scanline& sl) { + CompositeSpan(dest_pos, backdrop_pos, Bpp, bDestAlpha, x, span->len, + span->covers, m_ClipBox.left, m_ClipBox.right, clip_pos); + } else { +- (this->*composite_span)(dest_pos, Bpp, x, span->len, span->covers, +- m_ClipBox.left, m_ClipBox.right, clip_pos, +- dest_extra_alpha_pos); ++ (this->*m_CompositeSpanFunc)(dest_pos, Bpp, x, span->len, span->covers, ++ m_ClipBox.left, m_ClipBox.right, clip_pos); + } + if (--num_spans == 0) + break; +@@ -1022,9 +849,9 @@ void CFX_Renderer::CompositeSpan1bppHelper(uint8_t* dest_scan, + const uint8_t* clip_scan, + int span_left) { + int index = 0; +- if (m_pDevice->GetPalette()) { ++ if (m_pDevice->HasPalette()) { + for (int i = 0; i < 2; i++) { +- if (FXARGB_TODIB(m_pDevice->GetPalette()[i]) == m_Color) ++ if (m_pDevice->GetPaletteSpan()[i] == m_Color) + index = i; + } + } else { +@@ -1043,16 +870,12 @@ void CFX_Renderer::CompositeSpan1bppHelper(uint8_t* dest_scan, + } + } + +-} // namespace +- +-namespace agg { +- + template +-class renderer_scanline_aa_offset { ++class RendererScanLineAaOffset { + public: + typedef BaseRenderer base_ren_type; + typedef typename base_ren_type::color_type color_type; +- renderer_scanline_aa_offset(base_ren_type& ren, unsigned left, unsigned top) ++ RendererScanLineAaOffset(base_ren_type& ren, unsigned left, unsigned top) + : m_ren(&ren), m_left(left), m_top(top) {} + void color(const color_type& c) { m_color = c; } + const color_type& color() const { return m_color; } +@@ -1062,7 +885,7 @@ class renderer_scanline_aa_offset { + int y = sl.y(); + unsigned num_spans = sl.num_spans(); + typename Scanline::const_iterator span = sl.begin(); +- while (1) { ++ while (true) { + int x = span->x; + if (span->len > 0) { + m_ren->blend_solid_hspan(x - m_left, y - m_top, (unsigned)span->len, +@@ -1081,36 +904,36 @@ class renderer_scanline_aa_offset { + private: + base_ren_type* m_ren; + color_type m_color; +- unsigned m_left, m_top; ++ unsigned m_left; ++ unsigned m_top; + }; + +-} // namespace agg +- +-void CAgg_PathData::BuildPath(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device) { +- const std::vector& pPoints = pPathData->GetPoints(); +- for (size_t i = 0; i < pPoints.size(); i++) { +- CFX_PointF pos = pPoints[i].m_Point; ++agg::path_storage BuildAggPath(const CFX_Path& path, ++ const CFX_Matrix* pObject2Device) { ++ agg::path_storage agg_path; ++ pdfium::span points = path.GetPoints(); ++ for (size_t i = 0; i < points.size(); ++i) { ++ CFX_PointF pos = points[i].m_Point; + if (pObject2Device) + pos = pObject2Device->Transform(pos); + + pos = HardClip(pos); +- FXPT_TYPE point_type = pPoints[i].m_Type; +- if (point_type == FXPT_TYPE::MoveTo) { +- m_PathData.move_to(pos.x, pos.y); +- } else if (point_type == FXPT_TYPE::LineTo) { +- if (i > 0 && pPoints[i - 1].IsTypeAndOpen(FXPT_TYPE::MoveTo) && +- (i == pPoints.size() - 1 || +- pPoints[i + 1].IsTypeAndOpen(FXPT_TYPE::MoveTo)) && +- pPoints[i].m_Point == pPoints[i - 1].m_Point) { ++ CFX_Path::Point::Type point_type = points[i].m_Type; ++ if (point_type == CFX_Path::Point::Type::kMove) { ++ agg_path.move_to(pos.x, pos.y); ++ } else if (point_type == CFX_Path::Point::Type::kLine) { ++ if (i > 0 && points[i - 1].IsTypeAndOpen(CFX_Path::Point::Type::kMove) && ++ (i == points.size() - 1 || ++ points[i + 1].IsTypeAndOpen(CFX_Path::Point::Type::kMove)) && ++ points[i].m_Point == points[i - 1].m_Point) { + pos.x += 1; + } +- m_PathData.line_to(pos.x, pos.y); +- } else if (point_type == FXPT_TYPE::BezierTo) { +- if (i > 0 && i + 2 < pPoints.size()) { +- CFX_PointF pos0 = pPoints[i - 1].m_Point; +- CFX_PointF pos2 = pPoints[i + 1].m_Point; +- CFX_PointF pos3 = pPoints[i + 2].m_Point; ++ agg_path.line_to(pos.x, pos.y); ++ } else if (point_type == CFX_Path::Point::Type::kBezier) { ++ if (i > 0 && i + 2 < points.size()) { ++ CFX_PointF pos0 = points[i - 1].m_Point; ++ CFX_PointF pos2 = points[i + 1].m_Point; ++ CFX_PointF pos3 = points[i + 2].m_Point; + if (pObject2Device) { + pos0 = pObject2Device->Transform(pos0); + pos2 = pObject2Device->Transform(pos2); +@@ -1122,24 +945,29 @@ void CAgg_PathData::BuildPath(const CFX_PathData* pPathData, + agg::curve4 curve(pos0.x, pos0.y, pos.x, pos.y, pos2.x, pos2.y, pos3.x, + pos3.y); + i += 2; +- m_PathData.add_path_curve(curve); ++ agg_path.add_path(curve); + } + } +- if (pPoints[i].m_CloseFigure) +- m_PathData.end_poly(); ++ if (points[i].m_CloseFigure) ++ agg_path.end_poly(); + } ++ return agg_path; + } + ++} // namespace ++ + CFX_AggDeviceDriver::CFX_AggDeviceDriver( +- const RetainPtr& pBitmap, ++ RetainPtr pBitmap, + bool bRgbByteOrder, +- const RetainPtr& pBackdropBitmap, ++ RetainPtr pBackdropBitmap, + bool bGroupKnockout) +- : m_pBitmap(pBitmap), ++ : m_pBitmap(std::move(pBitmap)), + m_bRgbByteOrder(bRgbByteOrder), + m_bGroupKnockout(bGroupKnockout), +- m_pBackdropBitmap(pBackdropBitmap) { +- ASSERT(m_pBitmap); ++ m_pBackdropBitmap(std::move(pBackdropBitmap)) { ++ DCHECK(m_pBitmap); ++ DCHECK_NE(m_pBitmap->GetFormat(), FXDIB_Format::k1bppMask); ++ DCHECK_NE(m_pBitmap->GetFormat(), FXDIB_Format::k1bppRgb); + InitPlatform(); + } + +@@ -1147,24 +975,21 @@ CFX_AggDeviceDriver::~CFX_AggDeviceDriver() { + DestroyPlatform(); + } + +-uint8_t* CFX_AggDeviceDriver::GetBuffer() const { +- return m_pBitmap->GetBuffer(); +-} +- +-#if !defined(OS_MACOSX) ++#if !BUILDFLAG(IS_APPLE) + void CFX_AggDeviceDriver::InitPlatform() {} + + void CFX_AggDeviceDriver::DestroyPlatform() {} + +-bool CFX_AggDeviceDriver::DrawDeviceText(int nChars, +- const TextCharPos* pCharPos, +- CFX_Font* pFont, +- const CFX_Matrix& mtObject2Device, +- float font_size, +- uint32_t color) { ++bool CFX_AggDeviceDriver::DrawDeviceText( ++ pdfium::span pCharPos, ++ CFX_Font* pFont, ++ const CFX_Matrix& mtObject2Device, ++ float font_size, ++ uint32_t color, ++ const CFX_TextRenderOptions& options) { + return false; + } +-#endif // !defined(OS_MACOSX) ++#endif // !BUILDFLAG(IS_APPLE) + + DeviceType CFX_AggDeviceDriver::GetDeviceType() const { + return DeviceType::kDisplay; +@@ -1184,16 +1009,14 @@ int CFX_AggDeviceDriver::GetDeviceCaps(int caps_id) const { + case FXDC_RENDER_CAPS: { + int flags = FXRC_GET_BITS | FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE | + FXRC_BLEND_MODE | FXRC_SOFT_CLIP; +- if (m_pBitmap->HasAlpha()) { ++ if (m_pBitmap->IsAlphaFormat()) { + flags |= FXRC_ALPHA_OUTPUT; +- } else if (m_pBitmap->IsAlphaMask()) { ++ } else if (m_pBitmap->IsMaskFormat()) { + if (m_pBitmap->GetBPP() == 1) + flags |= FXRC_BITMASK_OUTPUT; + else + flags |= FXRC_BYTEMASK_OUTPUT; + } +- if (m_pBitmap->IsCmykImage()) +- flags |= FXRC_CMYK_OUTPUT; + return flags; + } + default: +@@ -1205,7 +1028,7 @@ int CFX_AggDeviceDriver::GetDeviceCaps(int caps_id) const { + void CFX_AggDeviceDriver::SaveState() { + std::unique_ptr pClip; + if (m_pClipRgn) +- pClip = pdfium::MakeUnique(*m_pClipRgn); ++ pClip = std::make_unique(*m_pClipRgn); + m_StateStack.push_back(std::move(pClip)); + } + +@@ -1217,7 +1040,7 @@ void CFX_AggDeviceDriver::RestoreState(bool bKeepSaved) { + + if (bKeepSaved) { + if (m_StateStack.back()) +- m_pClipRgn = pdfium::MakeUnique(*m_StateStack.back()); ++ m_pClipRgn = std::make_unique(*m_StateStack.back()); + } else { + m_pClipRgn = std::move(m_StateStack.back()); + m_StateStack.pop_back(); +@@ -1229,75 +1052,71 @@ void CFX_AggDeviceDriver::SetClipMask(agg::rasterizer_scanline_aa& rasterizer) { + rasterizer.max_x() + 1, rasterizer.max_y() + 1); + path_rect.Intersect(m_pClipRgn->GetBox()); + auto pThisLayer = pdfium::MakeRetain(); +- pThisLayer->Create(path_rect.Width(), path_rect.Height(), FXDIB_8bppMask); +- pThisLayer->Clear(0); +- agg::rendering_buffer raw_buf(pThisLayer->GetBuffer(), pThisLayer->GetWidth(), +- pThisLayer->GetHeight(), ++ pThisLayer->Create(path_rect.Width(), path_rect.Height(), ++ FXDIB_Format::k8bppMask); ++ agg::rendering_buffer raw_buf(pThisLayer->GetBuffer().data(), ++ pThisLayer->GetWidth(), pThisLayer->GetHeight(), + pThisLayer->GetPitch()); + agg::pixfmt_gray8 pixel_buf(raw_buf); + agg::renderer_base base_buf(pixel_buf); +- agg::renderer_scanline_aa_offset > +- final_render(base_buf, path_rect.left, path_rect.top); ++ RendererScanLineAaOffset> final_render( ++ base_buf, path_rect.left, path_rect.top); + final_render.color(agg::gray8(255)); + agg::scanline_u8 scanline; + agg::render_scanlines(rasterizer, scanline, final_render, +- (m_FillFlags & FXFILL_NOPATHSMOOTH) != 0); +- m_pClipRgn->IntersectMaskF(path_rect.left, path_rect.top, pThisLayer); ++ m_FillOptions.aliased_path); ++ m_pClipRgn->IntersectMaskF(path_rect.left, path_rect.top, ++ std::move(pThisLayer)); + } + +-bool CFX_AggDeviceDriver::SetClip_PathFill(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- int fill_mode) { +- ASSERT(IsAlternateOrWindingFillMode(fill_mode)); +- ASSERT(GetAlternateOrWindingFillMode(fill_mode) != +- kAlternateOrWindingFillModeMask); ++bool CFX_AggDeviceDriver::SetClip_PathFill( ++ const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_FillRenderOptions& fill_options) { ++ DCHECK(fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill); + +- m_FillFlags = fill_mode; ++ m_FillOptions = fill_options; + if (!m_pClipRgn) { +- m_pClipRgn = pdfium::MakeUnique( ++ m_pClipRgn = std::make_unique( + GetDeviceCaps(FXDC_PIXEL_WIDTH), GetDeviceCaps(FXDC_PIXEL_HEIGHT)); + } +- size_t size = pPathData->GetPoints().size(); +- if (size == 5 || size == 4) { +- CFX_FloatRect rectf; +- if (pPathData->IsRect(pObject2Device, &rectf)) { +- rectf.Intersect(CFX_FloatRect( +- 0, 0, static_cast(GetDeviceCaps(FXDC_PIXEL_WIDTH)), +- static_cast(GetDeviceCaps(FXDC_PIXEL_HEIGHT)))); +- FX_RECT rect = rectf.GetOuterRect(); +- m_pClipRgn->IntersectRect(rect); +- return true; +- } ++ absl::optional maybe_rectf = path.GetRect(pObject2Device); ++ if (maybe_rectf.has_value()) { ++ CFX_FloatRect& rectf = maybe_rectf.value(); ++ rectf.Intersect( ++ CFX_FloatRect(0, 0, static_cast(GetDeviceCaps(FXDC_PIXEL_WIDTH)), ++ static_cast(GetDeviceCaps(FXDC_PIXEL_HEIGHT)))); ++ FX_RECT rect = rectf.GetOuterRect(); ++ m_pClipRgn->IntersectRect(rect); ++ return true; + } +- CAgg_PathData path_data; +- path_data.BuildPath(pPathData, pObject2Device); +- path_data.m_PathData.end_poly(); ++ agg::path_storage path_data = BuildAggPath(path, pObject2Device); ++ path_data.end_poly(); + agg::rasterizer_scanline_aa rasterizer; + rasterizer.clip_box(0.0f, 0.0f, + static_cast(GetDeviceCaps(FXDC_PIXEL_WIDTH)), + static_cast(GetDeviceCaps(FXDC_PIXEL_HEIGHT))); +- rasterizer.add_path(path_data.m_PathData); +- rasterizer.filling_rule(GetAlternateOrWindingFillType(fill_mode)); ++ rasterizer.add_path(path_data); ++ rasterizer.filling_rule(GetAlternateOrWindingFillType(fill_options)); + SetClipMask(rasterizer); + return true; + } + + bool CFX_AggDeviceDriver::SetClip_PathStroke( +- const CFX_PathData* pPathData, ++ const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState) { + if (!m_pClipRgn) { +- m_pClipRgn = pdfium::MakeUnique( ++ m_pClipRgn = std::make_unique( + GetDeviceCaps(FXDC_PIXEL_WIDTH), GetDeviceCaps(FXDC_PIXEL_HEIGHT)); + } +- CAgg_PathData path_data; +- path_data.BuildPath(pPathData, nullptr); ++ agg::path_storage path_data = BuildAggPath(path, nullptr); + agg::rasterizer_scanline_aa rasterizer; + rasterizer.clip_box(0.0f, 0.0f, + static_cast(GetDeviceCaps(FXDC_PIXEL_WIDTH)), + static_cast(GetDeviceCaps(FXDC_PIXEL_HEIGHT))); +- RasterizeStroke(&rasterizer, &path_data.m_PathData, pObject2Device, +- pGraphState, 1.0f, false); ++ RasterizeStroke(&rasterizer, &path_data, pObject2Device, pGraphState, 1.0f, ++ false); + rasterizer.filling_rule(agg::fill_non_zero); + SetClipMask(rasterizer); + return true; +@@ -1307,69 +1126,68 @@ int CFX_AggDeviceDriver::GetDriverType() const { + return 1; + } + +-bool CFX_AggDeviceDriver::RenderRasterizer( ++bool CFX_AggDeviceDriver::MultiplyAlpha(float alpha) { ++ return m_pBitmap->MultiplyAlpha(static_cast(alpha * 255)); ++} ++ ++bool CFX_AggDeviceDriver::MultiplyAlpha(const RetainPtr& mask) { ++ return m_pBitmap->MultiplyAlpha(mask); ++} ++ ++void CFX_AggDeviceDriver::RenderRasterizer( + agg::rasterizer_scanline_aa& rasterizer, + uint32_t color, + bool bFullCover, + bool bGroupKnockout) { + RetainPtr pt = bGroupKnockout ? m_pBackdropBitmap : nullptr; +- CFX_Renderer render; +- if (!render.Init(m_pBitmap, pt, m_pClipRgn.get(), color, bFullCover, +- m_bRgbByteOrder)) { +- return false; +- } ++ CFX_Renderer render(m_pBitmap, pt, m_pClipRgn.get(), color, bFullCover, ++ m_bRgbByteOrder); + agg::scanline_u8 scanline; + agg::render_scanlines(rasterizer, scanline, render, +- (m_FillFlags & FXFILL_NOPATHSMOOTH) != 0); +- return true; ++ m_FillOptions.aliased_path); + } + +-bool CFX_AggDeviceDriver::DrawPath(const CFX_PathData* pPathData, ++bool CFX_AggDeviceDriver::DrawPath(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState, + uint32_t fill_color, + uint32_t stroke_color, +- int fill_mode, ++ const CFX_FillRenderOptions& fill_options, + BlendMode blend_type) { +- ASSERT(GetAlternateOrWindingFillMode(fill_mode) != +- kAlternateOrWindingFillModeMask); +- + if (blend_type != BlendMode::kNormal) + return false; + +- if (!GetBuffer()) ++ if (m_pBitmap->GetBuffer().empty()) + return true; + +- m_FillFlags = fill_mode; +- if (IsAlternateOrWindingFillMode(fill_mode) && fill_color) { +- CAgg_PathData path_data; +- path_data.BuildPath(pPathData, pObject2Device); ++ m_FillOptions = fill_options; ++ if (fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill && ++ fill_color) { ++ agg::path_storage path_data = BuildAggPath(path, pObject2Device); + agg::rasterizer_scanline_aa rasterizer; + rasterizer.clip_box(0.0f, 0.0f, + static_cast(GetDeviceCaps(FXDC_PIXEL_WIDTH)), + static_cast(GetDeviceCaps(FXDC_PIXEL_HEIGHT))); +- rasterizer.add_path(path_data.m_PathData); +- rasterizer.filling_rule(GetAlternateOrWindingFillType(fill_mode)); +- if (!RenderRasterizer(rasterizer, fill_color, +- !!(fill_mode & FXFILL_FULLCOVER), false)) { +- return false; +- } ++ rasterizer.add_path(path_data); ++ rasterizer.filling_rule(GetAlternateOrWindingFillType(fill_options)); ++ RenderRasterizer(rasterizer, fill_color, fill_options.full_cover, ++ /*bGroupKnockout=*/false); + } + int stroke_alpha = FXARGB_A(stroke_color); + if (!pGraphState || !stroke_alpha) + return true; + +- if (fill_mode & FX_ZEROAREA_FILL) { +- CAgg_PathData path_data; +- path_data.BuildPath(pPathData, pObject2Device); ++ if (fill_options.zero_area) { ++ agg::path_storage path_data = BuildAggPath(path, pObject2Device); + agg::rasterizer_scanline_aa rasterizer; + rasterizer.clip_box(0.0f, 0.0f, + static_cast(GetDeviceCaps(FXDC_PIXEL_WIDTH)), + static_cast(GetDeviceCaps(FXDC_PIXEL_HEIGHT))); +- RasterizeStroke(&rasterizer, &path_data.m_PathData, nullptr, pGraphState, 1, +- !!(fill_mode & FX_STROKE_TEXT_MODE)); +- return RenderRasterizer(rasterizer, stroke_color, +- !!(fill_mode & FXFILL_FULLCOVER), m_bGroupKnockout); ++ RasterizeStroke(&rasterizer, &path_data, nullptr, pGraphState, 1, ++ fill_options.stroke_text_mode); ++ RenderRasterizer(rasterizer, stroke_color, fill_options.full_cover, ++ m_bGroupKnockout); ++ return true; + } + CFX_Matrix matrix1; + CFX_Matrix matrix2; +@@ -1383,48 +1201,16 @@ bool CFX_AggDeviceDriver::DrawPath(const CFX_PathData* pPathData, + matrix1 = *pObject2Device * matrix2.GetInverse(); + } + +- CAgg_PathData path_data; +- path_data.BuildPath(pPathData, &matrix1); ++ agg::path_storage path_data = BuildAggPath(path, &matrix1); + agg::rasterizer_scanline_aa rasterizer; + rasterizer.clip_box(0.0f, 0.0f, + static_cast(GetDeviceCaps(FXDC_PIXEL_WIDTH)), + static_cast(GetDeviceCaps(FXDC_PIXEL_HEIGHT))); +- RasterizeStroke(&rasterizer, &path_data.m_PathData, &matrix2, pGraphState, +- matrix1.a, !!(fill_mode & FX_STROKE_TEXT_MODE)); +- return RenderRasterizer(rasterizer, stroke_color, +- !!(fill_mode & FXFILL_FULLCOVER), m_bGroupKnockout); +-} +- +-bool CFX_AggDeviceDriver::SetPixel(int x, int y, uint32_t color) { +- if (!m_pBitmap->GetBuffer()) +- return true; +- +- if (!m_pClipRgn) { +- if (!m_bRgbByteOrder) +- return DibSetPixel(m_pBitmap, x, y, color); +- RgbByteOrderSetPixel(m_pBitmap, x, y, color); +- return true; +- } +- if (!m_pClipRgn->GetBox().Contains(x, y)) +- return true; +- +- if (m_pClipRgn->GetType() == CFX_ClipRgn::RectI) { +- if (!m_bRgbByteOrder) +- return DibSetPixel(m_pBitmap, x, y, color); +- RgbByteOrderSetPixel(m_pBitmap, x, y, color); +- return true; +- } +- if (m_pClipRgn->GetType() != CFX_ClipRgn::MaskF) +- return true; +- +- int new_alpha = +- FXARGB_A(color) * m_pClipRgn->GetMask()->GetScanline(y)[x] / 255; +- color = (color & 0xffffff) | (new_alpha << 24); +- if (m_bRgbByteOrder) { +- RgbByteOrderSetPixel(m_pBitmap, x, y, color); +- return true; +- } +- return DibSetPixel(m_pBitmap, x, y, color); ++ RasterizeStroke(&rasterizer, &path_data, &matrix2, pGraphState, matrix1.a, ++ fill_options.stroke_text_mode); ++ RenderRasterizer(rasterizer, stroke_color, fill_options.full_cover, ++ m_bGroupKnockout); ++ return true; + } + + bool CFX_AggDeviceDriver::FillRectWithBlend(const FX_RECT& rect, +@@ -1433,7 +1219,7 @@ bool CFX_AggDeviceDriver::FillRectWithBlend(const FX_RECT& rect, + if (blend_type != BlendMode::kNormal) + return false; + +- if (!m_pBitmap->GetBuffer()) ++ if (m_pBitmap->GetBuffer().empty()) + return true; + + FX_RECT clip_rect; +@@ -1443,14 +1229,14 @@ bool CFX_AggDeviceDriver::FillRectWithBlend(const FX_RECT& rect, + if (draw_rect.IsEmpty()) + return true; + +- if (!m_pClipRgn || m_pClipRgn->GetType() == CFX_ClipRgn::RectI) { ++ if (!m_pClipRgn || m_pClipRgn->GetType() == CFX_ClipRgn::kRectI) { + if (m_bRgbByteOrder) { + RgbByteOrderCompositeRect(m_pBitmap, draw_rect.left, draw_rect.top, + draw_rect.Width(), draw_rect.Height(), + fill_color); + } else { + m_pBitmap->CompositeRect(draw_rect.left, draw_rect.top, draw_rect.Width(), +- draw_rect.Height(), fill_color, 0); ++ draw_rect.Height(), fill_color); + } + return true; + } +@@ -1476,21 +1262,21 @@ bool CFX_AggDeviceDriver::GetClipBox(FX_RECT* pRect) { + bool CFX_AggDeviceDriver::GetDIBits(const RetainPtr& pBitmap, + int left, + int top) { +- if (!m_pBitmap->GetBuffer()) ++ if (m_pBitmap->GetBuffer().empty()) + return true; + + FX_RECT rect(left, top, left + pBitmap->GetWidth(), + top + pBitmap->GetHeight()); + RetainPtr pBack; + if (m_pBackdropBitmap) { +- pBack = m_pBackdropBitmap->Clone(&rect); ++ pBack = m_pBackdropBitmap->ClipTo(rect); + if (!pBack) + return true; + + pBack->CompositeBitmap(0, 0, pBack->GetWidth(), pBack->GetHeight(), + m_pBitmap, 0, 0, BlendMode::kNormal, nullptr, false); + } else { +- pBack = m_pBitmap->Clone(&rect); ++ pBack = m_pBitmap->ClipTo(rect); + if (!pBack) + return true; + } +@@ -1498,8 +1284,8 @@ bool CFX_AggDeviceDriver::GetDIBits(const RetainPtr& pBitmap, + left = std::min(left, 0); + top = std::min(top, 0); + if (m_bRgbByteOrder) { +- RgbByteOrderTransferBitmap(pBitmap, 0, 0, rect.Width(), rect.Height(), +- pBack, left, top); ++ RgbByteOrderTransferBitmap(pBitmap, rect.Width(), rect.Height(), pBack, ++ left, top); + return true; + } + return pBitmap->TransferBitmap(0, 0, rect.Width(), rect.Height(), pBack, left, +@@ -1516,10 +1302,10 @@ bool CFX_AggDeviceDriver::SetDIBits(const RetainPtr& pBitmap, + int left, + int top, + BlendMode blend_type) { +- if (!m_pBitmap->GetBuffer()) ++ if (m_pBitmap->GetBuffer().empty()) + return true; + +- if (pBitmap->IsAlphaMask()) { ++ if (pBitmap->IsMaskFormat()) { + return m_pBitmap->CompositeMask(left, top, src_rect.Width(), + src_rect.Height(), pBitmap, argb, + src_rect.left, src_rect.top, blend_type, +@@ -1539,7 +1325,7 @@ bool CFX_AggDeviceDriver::StretchDIBits(const RetainPtr& pSource, + const FX_RECT* pClipRect, + const FXDIB_ResampleOptions& options, + BlendMode blend_type) { +- if (!m_pBitmap->GetBuffer()) ++ if (m_pBitmap->GetBuffer().empty()) + return true; + + if (dest_width == pSource->GetWidth() && +@@ -1571,10 +1357,10 @@ bool CFX_AggDeviceDriver::StartDIBits( + const FXDIB_ResampleOptions& options, + std::unique_ptr* handle, + BlendMode blend_type) { +- if (!m_pBitmap->GetBuffer()) ++ if (m_pBitmap->GetBuffer().empty()) + return true; + +- *handle = pdfium::MakeUnique( ++ *handle = std::make_unique( + m_pBitmap, m_pClipRgn.get(), pSource, bitmap_alpha, argb, matrix, options, + m_bRgbByteOrder); + return true; +@@ -1582,41 +1368,37 @@ bool CFX_AggDeviceDriver::StartDIBits( + + bool CFX_AggDeviceDriver::ContinueDIBits(CFX_ImageRenderer* pHandle, + PauseIndicatorIface* pPause) { +- return !m_pBitmap->GetBuffer() || pHandle->Continue(pPause); ++ return m_pBitmap->GetBuffer().empty() || pHandle->Continue(pPause); + } + +-#ifndef _SKIA_SUPPORT_ +-CFX_DefaultRenderDevice::CFX_DefaultRenderDevice() {} ++} // namespace pdfium + +-CFX_DefaultRenderDevice::~CFX_DefaultRenderDevice() {} +- +-bool CFX_DefaultRenderDevice::Attach( +- const RetainPtr& pBitmap, ++bool CFX_DefaultRenderDevice::AttachAggImpl( ++ RetainPtr pBitmap, + bool bRgbByteOrder, +- const RetainPtr& pBackdropBitmap, ++ RetainPtr pBackdropBitmap, + bool bGroupKnockout) { + if (!pBitmap) + return false; + + SetBitmap(pBitmap); +- SetDeviceDriver(pdfium::MakeUnique( +- pBitmap, bRgbByteOrder, pBackdropBitmap, bGroupKnockout)); ++ SetDeviceDriver(std::make_unique( ++ std::move(pBitmap), bRgbByteOrder, std::move(pBackdropBitmap), ++ bGroupKnockout)); + return true; + } + +-bool CFX_DefaultRenderDevice::Create( ++bool CFX_DefaultRenderDevice::CreateAgg( + int width, + int height, + FXDIB_Format format, +- const RetainPtr& pBackdropBitmap) { ++ RetainPtr pBackdropBitmap) { + auto pBitmap = pdfium::MakeRetain(); + if (!pBitmap->Create(width, height, format)) + return false; + + SetBitmap(pBitmap); +- SetDeviceDriver(pdfium::MakeUnique( +- pBitmap, false, pBackdropBitmap, false)); ++ SetDeviceDriver(std::make_unique( ++ std::move(pBitmap), false, std::move(pBackdropBitmap), false)); + return true; + } +- +-#endif +diff --git a/core/fxge/agg/fx_agg_driver.h b/core/fxge/agg/fx_agg_driver.h +index 057842165..3b3d8dc23 100644 +--- a/core/fxge/agg/fx_agg_driver.h ++++ b/core/fxge/agg/fx_agg_driver.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,31 +11,26 @@ + #include + + #include "build/build_config.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxge/cfx_fillrenderoptions.h" + #include "core/fxge/renderdevicedriver_iface.h" +-#include "third_party/agg23/agg_clip_liang_barsky.h" +-#include "third_party/agg23/agg_path_storage.h" +-#include "third_party/agg23/agg_rasterizer_scanline_aa.h" + + class CFX_ClipRgn; + class CFX_GraphStateData; + class CFX_Matrix; +-class CFX_PathData; ++class CFX_Path; + +-class CAgg_PathData { +- public: +- CAgg_PathData() {} +- ~CAgg_PathData() {} +- void BuildPath(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device); ++namespace pdfium { + +- agg::path_storage m_PathData; +-}; ++namespace agg { ++class rasterizer_scanline_aa; ++} // namespace agg + + class CFX_AggDeviceDriver final : public RenderDeviceDriverIface { + public: +- CFX_AggDeviceDriver(const RetainPtr& pBitmap, ++ CFX_AggDeviceDriver(RetainPtr pBitmap, + bool bRgbByteOrder, +- const RetainPtr& pBackdropBitmap, ++ RetainPtr pBackdropBitmap, + bool bGroupKnockout); + ~CFX_AggDeviceDriver() override; + +@@ -47,20 +42,19 @@ class CFX_AggDeviceDriver final : public RenderDeviceDriverIface { + int GetDeviceCaps(int caps_id) const override; + void SaveState() override; + void RestoreState(bool bKeepSaved) override; +- bool SetClip_PathFill(const CFX_PathData* pPathData, ++ bool SetClip_PathFill(const CFX_Path& path, + const CFX_Matrix* pObject2Device, +- int fill_mode) override; +- bool SetClip_PathStroke(const CFX_PathData* pPathData, ++ const CFX_FillRenderOptions& fill_options) override; ++ bool SetClip_PathStroke(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState) override; +- bool DrawPath(const CFX_PathData* pPathData, ++ bool DrawPath(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState, + uint32_t fill_color, + uint32_t stroke_color, +- int fill_mode, ++ const CFX_FillRenderOptions& fill_options, + BlendMode blend_type) override; +- bool SetPixel(int x, int y, uint32_t color) override; + bool FillRectWithBlend(const FX_RECT& rect, + uint32_t fill_color, + BlendMode blend_type) override; +@@ -93,34 +87,36 @@ class CFX_AggDeviceDriver final : public RenderDeviceDriverIface { + BlendMode blend_type) override; + bool ContinueDIBits(CFX_ImageRenderer* handle, + PauseIndicatorIface* pPause) override; +- bool DrawDeviceText(int nChars, +- const TextCharPos* pCharPos, ++ bool DrawDeviceText(pdfium::span pCharPos, + CFX_Font* pFont, + const CFX_Matrix& mtObject2Device, + float font_size, +- uint32_t color) override; ++ uint32_t color, ++ const CFX_TextRenderOptions& options) override; + int GetDriverType() const override; ++ bool MultiplyAlpha(float alpha) override; ++ bool MultiplyAlpha(const RetainPtr& mask) override; + +- bool RenderRasterizer(agg::rasterizer_scanline_aa& rasterizer, ++ private: ++ void RenderRasterizer(pdfium::agg::rasterizer_scanline_aa& rasterizer, + uint32_t color, + bool bFullCover, + bool bGroupKnockout); + +- void SetClipMask(agg::rasterizer_scanline_aa& rasterizer); ++ void SetClipMask(pdfium::agg::rasterizer_scanline_aa& rasterizer); + +- virtual uint8_t* GetBuffer() const; +- +- private: + RetainPtr const m_pBitmap; + std::unique_ptr m_pClipRgn; + std::vector> m_StateStack; +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + void* m_pPlatformGraphics = nullptr; + #endif +- int m_FillFlags = 0; ++ CFX_FillRenderOptions m_FillOptions; + const bool m_bRgbByteOrder; + const bool m_bGroupKnockout; + RetainPtr m_pBackdropBitmap; + }; + ++} // namespace pdfium ++ + #endif // CORE_FXGE_AGG_FX_AGG_DRIVER_H_ +diff --git a/core/fxge/android/cfpf_skiadevicemodule.cpp b/core/fxge/android/cfpf_skiadevicemodule.cpp +index bb3f70afa..1170bb639 100644 +--- a/core/fxge/android/cfpf_skiadevicemodule.cpp ++++ b/core/fxge/android/cfpf_skiadevicemodule.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,7 +9,6 @@ + #include + + #include "core/fxge/android/cfpf_skiafontmgr.h" +-#include "third_party/base/ptr_util.h" + + namespace { + +@@ -23,9 +22,9 @@ CFPF_SkiaDeviceModule* CFPF_GetSkiaDeviceModule() { + return gs_pPFModule; + } + +-CFPF_SkiaDeviceModule::CFPF_SkiaDeviceModule() {} ++CFPF_SkiaDeviceModule::CFPF_SkiaDeviceModule() = default; + +-CFPF_SkiaDeviceModule::~CFPF_SkiaDeviceModule() {} ++CFPF_SkiaDeviceModule::~CFPF_SkiaDeviceModule() = default; + + void CFPF_SkiaDeviceModule::Destroy() { + delete gs_pPFModule; +@@ -34,7 +33,7 @@ void CFPF_SkiaDeviceModule::Destroy() { + + CFPF_SkiaFontMgr* CFPF_SkiaDeviceModule::GetFontMgr() { + if (!m_pFontMgr) { +- auto pNewMgr = pdfium::MakeUnique(); ++ auto pNewMgr = std::make_unique(); + if (!pNewMgr->InitFTLibrary()) + return nullptr; + m_pFontMgr = std::move(pNewMgr); +diff --git a/core/fxge/android/cfpf_skiadevicemodule.h b/core/fxge/android/cfpf_skiadevicemodule.h +index 558b08413..2a51f4d01 100644 +--- a/core/fxge/android/cfpf_skiadevicemodule.h ++++ b/core/fxge/android/cfpf_skiadevicemodule.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxge/android/cfpf_skiafont.cpp b/core/fxge/android/cfpf_skiafont.cpp +index 1c4eeced9..3039ac7f5 100644 +--- a/core/fxge/android/cfpf_skiafont.cpp ++++ b/core/fxge/android/cfpf_skiafont.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,18 +8,20 @@ + + #include + ++#include "core/fxcrt/fx_codepage.h" + #include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/fx_system.h" + #include "core/fxge/android/cfpf_skiafontmgr.h" + #include "core/fxge/android/cfpf_skiapathfont.h" +-#include "core/fxge/fx_freetype.h" ++#include "core/fxge/freetype/fx_freetype.h" ++#include "third_party/base/numerics/safe_conversions.h" + + #define FPF_EM_ADJUST(em, a) (em == 0 ? (a) : (a)*1000 / em) + + CFPF_SkiaFont::CFPF_SkiaFont(CFPF_SkiaFontMgr* pFontMgr, + const CFPF_SkiaPathFont* pFont, + uint32_t dwStyle, +- uint8_t uCharset) ++ FX_Charset uCharset) + : m_pFontMgr(pFontMgr), + m_pFont(pFont), + m_Face(m_pFontMgr->GetFontFace(m_pFont->path(), m_pFont->face_index())), +@@ -55,8 +57,9 @@ int32_t CFPF_SkiaFont::GetGlyphWidth(int32_t iGlyphIndex) { + FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH)) { + return 0; + } +- return FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), +- FXFT_Get_Glyph_HoriAdvance(GetFaceRec())); ++ return static_cast( ++ FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), ++ FXFT_Get_Glyph_HoriAdvance(GetFaceRec()))); + } + + int32_t CFPF_SkiaFont::GetAscent() const { +@@ -93,10 +96,10 @@ bool CFPF_SkiaFont::GetGlyphBBox(int32_t iGlyphIndex, FX_RECT& rtBBox) { + FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &cbox); + int32_t x_ppem = GetFaceRec()->size->metrics.x_ppem; + int32_t y_ppem = GetFaceRec()->size->metrics.y_ppem; +- rtBBox.left = FPF_EM_ADJUST(x_ppem, cbox.xMin); +- rtBBox.right = FPF_EM_ADJUST(x_ppem, cbox.xMax); +- rtBBox.top = FPF_EM_ADJUST(y_ppem, cbox.yMax); +- rtBBox.bottom = FPF_EM_ADJUST(y_ppem, cbox.yMin); ++ rtBBox.left = static_cast(FPF_EM_ADJUST(x_ppem, cbox.xMin)); ++ rtBBox.right = static_cast(FPF_EM_ADJUST(x_ppem, cbox.xMax)); ++ rtBBox.top = static_cast(FPF_EM_ADJUST(y_ppem, cbox.yMax)); ++ rtBBox.bottom = static_cast(FPF_EM_ADJUST(y_ppem, cbox.yMin)); + rtBBox.top = std::min(rtBBox.top, GetAscent()); + rtBBox.bottom = std::max(rtBBox.bottom, GetDescent()); + FT_Done_Glyph(glyph); +@@ -106,16 +109,20 @@ bool CFPF_SkiaFont::GetGlyphBBox(int32_t iGlyphIndex, FX_RECT& rtBBox) { + FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH)) { + return false; + } +- rtBBox.left = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), +- FXFT_Get_Glyph_HoriBearingX(GetFaceRec())); +- rtBBox.bottom = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), +- FXFT_Get_Glyph_HoriBearingY(GetFaceRec())); +- rtBBox.right = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), +- FXFT_Get_Glyph_HoriBearingX(GetFaceRec()) + +- FXFT_Get_Glyph_Width(GetFaceRec())); +- rtBBox.top = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), +- FXFT_Get_Glyph_HoriBearingY(GetFaceRec()) - +- FXFT_Get_Glyph_Height(GetFaceRec())); ++ rtBBox.left = static_cast( ++ FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), ++ FXFT_Get_Glyph_HoriBearingX(GetFaceRec()))); ++ rtBBox.bottom = static_cast( ++ FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), ++ FXFT_Get_Glyph_HoriBearingY(GetFaceRec()))); ++ rtBBox.right = static_cast( ++ FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), ++ FXFT_Get_Glyph_HoriBearingX(GetFaceRec()) + ++ FXFT_Get_Glyph_Width(GetFaceRec()))); ++ rtBBox.top = static_cast( ++ FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), ++ FXFT_Get_Glyph_HoriBearingY(GetFaceRec()) - ++ FXFT_Get_Glyph_Height(GetFaceRec()))); + return true; + } + +@@ -123,14 +130,18 @@ bool CFPF_SkiaFont::GetBBox(FX_RECT& rtBBox) { + if (!m_Face) { + return false; + } +- rtBBox.left = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), +- FXFT_Get_Face_xMin(GetFaceRec())); +- rtBBox.top = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), +- FXFT_Get_Face_yMin(GetFaceRec())); +- rtBBox.right = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), +- FXFT_Get_Face_xMax(GetFaceRec())); +- rtBBox.bottom = FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), +- FXFT_Get_Face_yMax(GetFaceRec())); ++ rtBBox.left = ++ static_cast(FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), ++ FXFT_Get_Face_xMin(GetFaceRec()))); ++ rtBBox.top = ++ static_cast(FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), ++ FXFT_Get_Face_yMin(GetFaceRec()))); ++ rtBBox.right = ++ static_cast(FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), ++ FXFT_Get_Face_xMax(GetFaceRec()))); ++ rtBBox.bottom = ++ static_cast(FPF_EM_ADJUST(FXFT_Get_Face_UnitsPerEM(GetFaceRec()), ++ FXFT_Get_Face_yMax(GetFaceRec()))); + return true; + } + +@@ -147,7 +158,7 @@ int32_t CFPF_SkiaFont::GetItalicAngle() const { + + auto* info = static_cast( + FT_Get_Sfnt_Table(GetFaceRec(), ft_sfnt_post)); +- return info ? info->italicAngle : 0; ++ return info ? static_cast(info->italicAngle) : 0; + } + + uint32_t CFPF_SkiaFont::GetFontData(uint32_t dwTable, +diff --git a/core/fxge/android/cfpf_skiafont.h b/core/fxge/android/cfpf_skiafont.h +index 702a38e7d..840db635f 100644 +--- a/core/fxge/android/cfpf_skiafont.h ++++ b/core/fxge/android/cfpf_skiafont.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,14 @@ + #ifndef CORE_FXGE_ANDROID_CFPF_SKIAFONT_H_ + #define CORE_FXGE_ANDROID_CFPF_SKIAFONT_H_ + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include ++ ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/fx_codepage_forward.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + #include "core/fxge/cfx_face.h" +-#include "core/fxge/fx_freetype.h" ++#include "core/fxge/freetype/fx_freetype.h" + #include "third_party/base/span.h" + + class CFPF_SkiaFontMgr; +@@ -23,7 +26,7 @@ class CFPF_SkiaFont { + CFPF_SkiaFont(CFPF_SkiaFontMgr* pFontMgr, + const CFPF_SkiaPathFont* pFont, + uint32_t dwStyle, +- uint8_t uCharset); ++ FX_Charset uCharset); + ~CFPF_SkiaFont(); + + bool IsValid() const { return !!m_Face; } +@@ -31,7 +34,7 @@ class CFPF_SkiaFont { + ByteString GetFamilyName(); + ByteString GetPsName(); + uint32_t GetFontStyle() const { return m_dwStyle; } +- uint8_t GetCharset() const { return m_uCharset; } ++ FX_Charset GetCharset() const { return m_uCharset; } + int32_t GetGlyphIndex(wchar_t wUnicode); + int32_t GetGlyphWidth(int32_t iGlyphIndex); + int32_t GetAscent() const; +@@ -48,7 +51,7 @@ class CFPF_SkiaFont { + UnownedPtr const m_pFont; + RetainPtr const m_Face; + const uint32_t m_dwStyle; +- const uint8_t m_uCharset; ++ const FX_Charset m_uCharset; + }; + + #endif // CORE_FXGE_ANDROID_CFPF_SKIAFONT_H_ +diff --git a/core/fxge/android/cfpf_skiafontmgr.cpp b/core/fxge/android/cfpf_skiafontmgr.cpp +index b5e7a20ae..036f81266 100644 +--- a/core/fxge/android/cfpf_skiafontmgr.cpp ++++ b/core/fxge/android/cfpf_skiafontmgr.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,18 +7,18 @@ + #include "core/fxge/android/cfpf_skiafontmgr.h" + + #include ++#include + #include + + #include "core/fxcrt/fx_codepage.h" + #include "core/fxcrt/fx_extension.h" +-#include "core/fxcrt/fx_memory.h" +-#include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/fx_folder.h" + #include "core/fxcrt/fx_system.h" + #include "core/fxge/android/cfpf_skiafont.h" + #include "core/fxge/android/cfpf_skiapathfont.h" ++#include "core/fxge/freetype/fx_freetype.h" + #include "core/fxge/fx_font.h" +-#include "core/fxge/fx_freetype.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/containers/adapters.h" + + namespace { + +@@ -32,7 +32,7 @@ struct FPF_SKIAFONTMAP { + uint32_t dwSubSt; + }; + +-const FPF_SKIAFONTMAP g_SkiaFontmap[] = { ++const FPF_SKIAFONTMAP kSkiaFontmap[] = { + {0x58c5083, 0xc8d2e345}, {0x5dfade2, 0xe1633081}, + {0x684317d, 0xe1633081}, {0x14ee2d13, 0xc8d2e345}, + {0x3918fe2d, 0xbbeeec72}, {0x3b98b31c, 0xe1633081}, +@@ -46,7 +46,7 @@ const FPF_SKIAFONTMAP g_SkiaFontmap[] = { + {0xfb4ce0de, 0xe1633081}, + }; + +-const FPF_SKIAFONTMAP g_SkiaSansFontMap[] = { ++const FPF_SKIAFONTMAP kSkiaSansFontMap[] = { + {0x58c5083, 0xd5b8d10f}, {0x14ee2d13, 0xd5b8d10f}, + {0x779ce19d, 0xd5b8d10f}, {0xcb7a04c8, 0xd5b8d10f}, + {0xfb4ce0de, 0xd5b8d10f}, +@@ -65,18 +65,6 @@ uint32_t FPF_SkiaGetSubstFont(uint32_t dwHash, + return 0; + } + +-uint32_t FPF_GetHashCode_StringA(const char* pStr, int32_t iLength) { +- if (!pStr) +- return 0; +- if (iLength < 0) +- iLength = strlen(pStr); +- const char* pStrEnd = pStr + iLength; +- uint32_t uHashCode = 0; +- while (pStr < pStrEnd) +- uHashCode = 31 * uHashCode + tolower(*pStr++); +- return uHashCode; +-} +- + enum FPF_SKIACHARSET { + FPF_SKIACHARSET_Ansi = 1 << 0, + FPF_SKIACHARSET_Default = 1 << 1, +@@ -99,58 +87,56 @@ enum FPF_SKIACHARSET { + FPF_SKIACHARSET_OEM = 1 << 18, + }; + +-uint32_t FPF_SkiaGetCharset(uint8_t uCharset) { ++uint32_t FPF_SkiaGetCharset(FX_Charset uCharset) { + switch (uCharset) { +- case FX_CHARSET_ANSI: ++ case FX_Charset::kANSI: + return FPF_SKIACHARSET_Ansi; +- case FX_CHARSET_Default: ++ case FX_Charset::kDefault: + return FPF_SKIACHARSET_Default; +- case FX_CHARSET_Symbol: ++ case FX_Charset::kSymbol: + return FPF_SKIACHARSET_Symbol; +- case FX_CHARSET_ShiftJIS: ++ case FX_Charset::kShiftJIS: + return FPF_SKIACHARSET_ShiftJIS; +- case FX_CHARSET_Hangul: ++ case FX_Charset::kHangul: + return FPF_SKIACHARSET_Korean; +- case FX_CHARSET_ChineseSimplified: ++ case FX_Charset::kChineseSimplified: + return FPF_SKIACHARSET_GB2312; +- case FX_CHARSET_ChineseTraditional: ++ case FX_Charset::kChineseTraditional: + return FPF_SKIACHARSET_BIG5; +- case FX_CHARSET_MSWin_Greek: ++ case FX_Charset::kMSWin_Greek: + return FPF_SKIACHARSET_Greek; +- case FX_CHARSET_MSWin_Turkish: ++ case FX_Charset::kMSWin_Turkish: + return FPF_SKIACHARSET_Turkish; +- case FX_CHARSET_MSWin_Hebrew: ++ case FX_Charset::kMSWin_Hebrew: + return FPF_SKIACHARSET_Hebrew; +- case FX_CHARSET_MSWin_Arabic: ++ case FX_Charset::kMSWin_Arabic: + return FPF_SKIACHARSET_Arabic; +- case FX_CHARSET_MSWin_Baltic: ++ case FX_Charset::kMSWin_Baltic: + return FPF_SKIACHARSET_Baltic; +- case FX_CHARSET_MSWin_Cyrillic: ++ case FX_Charset::kMSWin_Cyrillic: + return FPF_SKIACHARSET_Cyrillic; +- case FX_CHARSET_Thai: ++ case FX_Charset::kThai: + return FPF_SKIACHARSET_Thai; +- case FX_CHARSET_MSWin_EasternEuropean: ++ case FX_Charset::kMSWin_EasternEuropean: + return FPF_SKIACHARSET_EeasternEuropean; ++ default: ++ return FPF_SKIACHARSET_Default; + } +- return FPF_SKIACHARSET_Default; + } + +-uint32_t FPF_SKIANormalizeFontName(ByteStringView bsfamily) { +- uint32_t dwHash = 0; +- int32_t iLength = bsfamily.GetLength(); +- const char* pBuffer = bsfamily.unterminated_c_str(); +- for (int32_t i = 0; i < iLength; i++) { +- char ch = pBuffer[i]; ++uint32_t FPF_SKIANormalizeFontName(ByteStringView bsFamily) { ++ uint32_t uHashCode = 0; ++ for (unsigned char ch : bsFamily) { + if (ch == ' ' || ch == '-' || ch == ',') + continue; +- dwHash = 31 * dwHash + tolower(ch); ++ uHashCode = 31 * uHashCode + tolower(ch); + } +- return dwHash; ++ return uHashCode; + } + + uint32_t FPF_SKIAGetFamilyHash(ByteStringView bsFamily, + uint32_t dwStyle, +- uint8_t uCharset) { ++ FX_Charset uCharset) { + ByteString bsFont(bsFamily); + if (FontStyleIsForceBold(dwStyle)) + bsFont += "Bold"; +@@ -158,11 +144,11 @@ uint32_t FPF_SKIAGetFamilyHash(ByteStringView bsFamily, + bsFont += "Italic"; + if (FontStyleIsSerif(dwStyle)) + bsFont += "Serif"; +- bsFont += uCharset; +- return FPF_GetHashCode_StringA(bsFont.c_str(), bsFont.GetLength()); ++ bsFont += static_cast(uCharset); ++ return FX_HashCode_GetA(bsFont.AsStringView()); + } + +-bool FPF_SkiaIsCJK(uint8_t uCharset) { ++bool FPF_SkiaIsCJK(FX_Charset uCharset) { + return FX_CharSetIsCJK(uCharset); + } + +@@ -178,7 +164,7 @@ bool FPF_SkiaMaybeArabic(ByteStringView bsFacename) { + return bsName.Contains("arabic"); + } + +-const uint32_t g_FPFSkiaFontCharsets[] = { ++const uint32_t kFPFSkiaFontCharsets[] = { + FPF_SKIACHARSET_Ansi, + FPF_SKIACHARSET_EeasternEuropean, + FPF_SKIACHARSET_Cyrillic, +@@ -218,7 +204,7 @@ uint32_t FPF_SkiaGetFaceCharset(TT_OS2* pOS2) { + if (pOS2) { + for (int32_t i = 0; i < 32; i++) { + if (pOS2->ulCodePageRange1 & (1 << i)) +- dwCharset |= g_FPFSkiaFontCharsets[i]; ++ dwCharset |= kFPFSkiaFontCharsets[i]; + } + } + dwCharset |= FPF_SKIACHARSET_Default; +@@ -255,7 +241,7 @@ void CFPF_SkiaFontMgr::LoadSystemFonts() { + } + + CFPF_SkiaFont* CFPF_SkiaFontMgr::CreateFont(ByteStringView bsFamilyname, +- uint8_t uCharset, ++ FX_Charset uCharset, + uint32_t dwStyle) { + uint32_t dwHash = FPF_SKIAGetFamilyHash(bsFamilyname, dwStyle, uCharset); + auto family_iter = m_FamilyFonts.find(dwHash); +@@ -263,78 +249,77 @@ CFPF_SkiaFont* CFPF_SkiaFontMgr::CreateFont(ByteStringView bsFamilyname, + return family_iter->second.get(); + + uint32_t dwFaceName = FPF_SKIANormalizeFontName(bsFamilyname); +- uint32_t dwSubst = FPF_SkiaGetSubstFont(dwFaceName, g_SkiaFontmap, +- FX_ArraySize(g_SkiaFontmap)); +- uint32_t dwSubstSans = FPF_SkiaGetSubstFont(dwFaceName, g_SkiaSansFontMap, +- FX_ArraySize(g_SkiaSansFontMap)); ++ uint32_t dwSubst = ++ FPF_SkiaGetSubstFont(dwFaceName, kSkiaFontmap, std::size(kSkiaFontmap)); ++ uint32_t dwSubstSans = FPF_SkiaGetSubstFont(dwFaceName, kSkiaSansFontMap, ++ std::size(kSkiaSansFontMap)); + bool bMaybeSymbol = FPF_SkiaMaybeSymbol(bsFamilyname); +- if (uCharset != FX_CHARSET_MSWin_Arabic && ++ if (uCharset != FX_Charset::kMSWin_Arabic && + FPF_SkiaMaybeArabic(bsFamilyname)) { +- uCharset = FX_CHARSET_MSWin_Arabic; +- } else if (uCharset == FX_CHARSET_ANSI) { +- uCharset = FX_CHARSET_Default; ++ uCharset = FX_Charset::kMSWin_Arabic; ++ } else if (uCharset == FX_Charset::kANSI) { ++ uCharset = FX_Charset::kDefault; + } + int32_t nExpectVal = FPF_SKIAMATCHWEIGHT_NAME1 + FPF_SKIAMATCHWEIGHT_1 * 3 + + FPF_SKIAMATCHWEIGHT_2 * 2; + const CFPF_SkiaPathFont* pBestFont = nullptr; + int32_t nMax = -1; + int32_t nGlyphNum = 0; +- for (auto face_iter = m_FontFaces.rbegin(); face_iter != m_FontFaces.rend(); +- ++face_iter) { +- const CFPF_SkiaPathFont* pFont = face_iter->get(); +- if (!(pFont->charsets() & FPF_SkiaGetCharset(uCharset))) ++ for (const std::unique_ptr& font : ++ pdfium::base::Reversed(m_FontFaces)) { ++ if (!(font->charsets() & FPF_SkiaGetCharset(uCharset))) + continue; + int32_t nFind = 0; +- uint32_t dwSysFontName = FPF_SKIANormalizeFontName(pFont->family()); ++ uint32_t dwSysFontName = FPF_SKIANormalizeFontName(font->family()); + if (dwFaceName == dwSysFontName) + nFind += FPF_SKIAMATCHWEIGHT_NAME1; + bool bMatchedName = (nFind == FPF_SKIAMATCHWEIGHT_NAME1); +- if (FontStyleIsForceBold(dwStyle) == FontStyleIsForceBold(pFont->style())) ++ if (FontStyleIsForceBold(dwStyle) == FontStyleIsForceBold(font->style())) + nFind += FPF_SKIAMATCHWEIGHT_1; +- if (FontStyleIsItalic(dwStyle) == FontStyleIsItalic(pFont->style())) ++ if (FontStyleIsItalic(dwStyle) == FontStyleIsItalic(font->style())) + nFind += FPF_SKIAMATCHWEIGHT_1; + if (FontStyleIsFixedPitch(dwStyle) == +- FontStyleIsFixedPitch(pFont->style())) { ++ FontStyleIsFixedPitch(font->style())) { + nFind += FPF_SKIAMATCHWEIGHT_2; + } +- if (FontStyleIsSerif(dwStyle) == FontStyleIsSerif(pFont->style())) ++ if (FontStyleIsSerif(dwStyle) == FontStyleIsSerif(font->style())) + nFind += FPF_SKIAMATCHWEIGHT_1; +- if (FontStyleIsScript(dwStyle) == FontStyleIsScript(pFont->style())) ++ if (FontStyleIsScript(dwStyle) == FontStyleIsScript(font->style())) + nFind += FPF_SKIAMATCHWEIGHT_2; + if (dwSubst == dwSysFontName || dwSubstSans == dwSysFontName) { + nFind += FPF_SKIAMATCHWEIGHT_NAME2; + bMatchedName = true; + } +- if (uCharset == FX_CHARSET_Default || bMaybeSymbol) { ++ if (uCharset == FX_Charset::kDefault || bMaybeSymbol) { + if (nFind > nMax && bMatchedName) { + nMax = nFind; +- pBestFont = face_iter->get(); ++ pBestFont = font.get(); + } + } else if (FPF_SkiaIsCJK(uCharset)) { +- if (bMatchedName || pFont->glyph_num() > nGlyphNum) { +- pBestFont = face_iter->get(); +- nGlyphNum = pFont->glyph_num(); ++ if (bMatchedName || font->glyph_num() > nGlyphNum) { ++ pBestFont = font.get(); ++ nGlyphNum = font->glyph_num(); + } + } else if (nFind > nMax) { + nMax = nFind; +- pBestFont = face_iter->get(); ++ pBestFont = font.get(); + } + if (nExpectVal <= nFind) { +- pBestFont = face_iter->get(); ++ pBestFont = font.get(); + break; + } + } + if (!pBestFont) + return nullptr; + +- auto pFont = +- pdfium::MakeUnique(this, pBestFont, dwStyle, uCharset); +- if (!pFont->IsValid()) ++ auto font = ++ std::make_unique(this, pBestFont, dwStyle, uCharset); ++ if (!font->IsValid()) + return nullptr; + +- CFPF_SkiaFont* pRet = pFont.get(); +- m_FamilyFonts[dwHash] = std::move(pFont); +- return pRet; ++ CFPF_SkiaFont* ret = font.get(); ++ m_FamilyFonts[dwHash] = std::move(font); ++ return ret; + } + + RetainPtr CFPF_SkiaFontMgr::GetFontFace(ByteStringView bsFile, +@@ -358,14 +343,13 @@ RetainPtr CFPF_SkiaFontMgr::GetFontFace(ByteStringView bsFile, + } + + void CFPF_SkiaFontMgr::ScanPath(const ByteString& path) { +- std::unique_ptr handle( +- FX_OpenFolder(path.c_str())); ++ std::unique_ptr handle = FX_Folder::OpenFolder(path); + if (!handle) + return; + + ByteString filename; + bool bFolder = false; +- while (FX_GetNextFile(handle.get(), &filename, &bFolder)) { ++ while (handle->GetNextFile(&filename, &bFolder)) { + if (bFolder) { + if (filename == "." || filename == "..") + continue; +@@ -417,7 +401,7 @@ std::unique_ptr CFPF_SkiaFontMgr::ReportFace( + if (pOS2 && (pOS2->ulCodePageRange1 & (1 << 31))) + dwStyle |= FXFONT_SYMBOLIC; + +- return pdfium::MakeUnique( ++ return std::make_unique( + file, FXFT_Get_Face_Family_Name(face->GetRec()), dwStyle, + face->GetRec()->face_index, FPF_SkiaGetFaceCharset(pOS2), + face->GetRec()->num_glyphs); +diff --git a/core/fxge/android/cfpf_skiafontmgr.h b/core/fxge/android/cfpf_skiafontmgr.h +index 4d5c7888c..0095854cc 100644 +--- a/core/fxge/android/cfpf_skiafontmgr.h ++++ b/core/fxge/android/cfpf_skiafontmgr.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,10 +11,11 @@ + #include + #include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/fx_codepage_forward.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxge/cfx_face.h" +-#include "core/fxge/fx_freetype.h" ++#include "core/fxge/freetype/fx_freetype.h" + + class CFPF_SkiaFont; + class CFPF_SkiaPathFont; +@@ -26,7 +27,7 @@ class CFPF_SkiaFontMgr { + + void LoadSystemFonts(); + CFPF_SkiaFont* CreateFont(ByteStringView bsFamilyname, +- uint8_t uCharset, ++ FX_Charset uCharset, + uint32_t dwStyle); + + bool InitFTLibrary(); +diff --git a/core/fxge/android/cfpf_skiapathfont.cpp b/core/fxge/android/cfpf_skiapathfont.cpp +index a9f96ac05..a074b72a7 100644 +--- a/core/fxge/android/cfpf_skiapathfont.cpp ++++ b/core/fxge/android/cfpf_skiapathfont.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxge/android/cfpf_skiapathfont.h b/core/fxge/android/cfpf_skiapathfont.h +index d091dc91c..cad3545fa 100644 +--- a/core/fxge/android/cfpf_skiapathfont.h ++++ b/core/fxge/android/cfpf_skiapathfont.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,8 +7,9 @@ + #ifndef CORE_FXGE_ANDROID_CFPF_SKIAPATHFONT_H_ + #define CORE_FXGE_ANDROID_CFPF_SKIAPATHFONT_H_ + ++#include ++ + #include "core/fxcrt/bytestring.h" +-#include "core/fxcrt/fx_system.h" + + class CFPF_SkiaPathFont { + public: +diff --git a/core/fxge/android/cfx_androidfontinfo.cpp b/core/fxge/android/cfx_androidfontinfo.cpp +index 206003dc4..2799dcdc8 100644 +--- a/core/fxge/android/cfx_androidfontinfo.cpp ++++ b/core/fxge/android/cfx_androidfontinfo.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,8 +12,10 @@ + #include "core/fxge/cfx_fontmapper.h" + #include "core/fxge/fx_font.h" + +-CFX_AndroidFontInfo::CFX_AndroidFontInfo() : m_pFontMgr(nullptr) {} +-CFX_AndroidFontInfo::~CFX_AndroidFontInfo() {} ++CFX_AndroidFontInfo::CFX_AndroidFontInfo() = default; ++ ++CFX_AndroidFontInfo::~CFX_AndroidFontInfo() = default; ++ + bool CFX_AndroidFontInfo::Init(CFPF_SkiaFontMgr* pFontMgr) { + if (!pFontMgr) + return false; +@@ -29,9 +31,9 @@ bool CFX_AndroidFontInfo::EnumFontList(CFX_FontMapper* pMapper) { + + void* CFX_AndroidFontInfo::MapFont(int weight, + bool bItalic, +- int charset, ++ FX_Charset charset, + int pitch_family, +- const char* face) { ++ const ByteString& face) { + if (!m_pFontMgr) + return nullptr; + +@@ -46,16 +48,16 @@ void* CFX_AndroidFontInfo::MapFont(int weight, + dwStyle |= FXFONT_SCRIPT; + if (FontFamilyIsRoman(pitch_family)) + dwStyle |= FXFONT_SERIF; +- return m_pFontMgr->CreateFont(face, charset, dwStyle); ++ return m_pFontMgr->CreateFont(face.AsStringView(), charset, dwStyle); + } + +-void* CFX_AndroidFontInfo::GetFont(const char* face) { ++void* CFX_AndroidFontInfo::GetFont(const ByteString& face) { + return nullptr; + } + +-uint32_t CFX_AndroidFontInfo::GetFontData(void* hFont, +- uint32_t table, +- pdfium::span buffer) { ++size_t CFX_AndroidFontInfo::GetFontData(void* hFont, ++ uint32_t table, ++ pdfium::span buffer) { + if (!hFont) + return 0; + return static_cast(hFont)->GetFontData(table, buffer); +@@ -69,7 +71,7 @@ bool CFX_AndroidFontInfo::GetFaceName(void* hFont, ByteString* name) { + return true; + } + +-bool CFX_AndroidFontInfo::GetFontCharset(void* hFont, int* charset) { ++bool CFX_AndroidFontInfo::GetFontCharset(void* hFont, FX_Charset* charset) { + if (!hFont) + return false; + +diff --git a/core/fxge/android/cfx_androidfontinfo.h b/core/fxge/android/cfx_androidfontinfo.h +index c8b6d24a7..5f6889973 100644 +--- a/core/fxge/android/cfx_androidfontinfo.h ++++ b/core/fxge/android/cfx_androidfontinfo.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,8 @@ + #ifndef CORE_FXGE_ANDROID_CFX_ANDROIDFONTINFO_H_ + #define CORE_FXGE_ANDROID_CFX_ANDROIDFONTINFO_H_ + +-#include "core/fxcrt/fx_system.h" ++#include ++ + #include "core/fxcrt/unowned_ptr.h" + #include "core/fxge/cfx_fontmapper.h" + #include "core/fxge/systemfontinfo_iface.h" +@@ -26,15 +27,15 @@ class CFX_AndroidFontInfo final : public SystemFontInfoIface { + bool EnumFontList(CFX_FontMapper* pMapper) override; + void* MapFont(int weight, + bool bItalic, +- int charset, ++ FX_Charset charset, + int pitch_family, +- const char* face) override; +- void* GetFont(const char* face) override; +- uint32_t GetFontData(void* hFont, +- uint32_t table, +- pdfium::span buffer) override; ++ const ByteString& face) override; ++ void* GetFont(const ByteString& face) override; ++ size_t GetFontData(void* hFont, ++ uint32_t table, ++ pdfium::span buffer) override; + bool GetFaceName(void* hFont, ByteString* name) override; +- bool GetFontCharset(void* hFont, int* charset) override; ++ bool GetFontCharset(void* hFont, FX_Charset* charset) override; + void DeleteFont(void* hFont) override; + + private: +diff --git a/core/fxge/android/fx_android_imp.cpp b/core/fxge/android/fx_android_impl.cpp +similarity index 70% +rename from core/fxge/android/fx_android_imp.cpp +rename to core/fxge/android/fx_android_impl.cpp +index 147011cd0..e06d611ee 100644 +--- a/core/fxge/android/fx_android_imp.cpp ++++ b/core/fxge/android/fx_android_impl.cpp +@@ -1,18 +1,16 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#include "core/fxge/cfx_gemodule.h" +- + #include + #include + + #include "core/fxge/android/cfpf_skiadevicemodule.h" + #include "core/fxge/android/cfx_androidfontinfo.h" + #include "core/fxge/cfx_fontmgr.h" +-#include "third_party/base/ptr_util.h" ++#include "core/fxge/cfx_gemodule.h" + + class CAndroidPlatform : public CFX_GEModule::PlatformIface { + public: +@@ -22,15 +20,16 @@ class CAndroidPlatform : public CFX_GEModule::PlatformIface { + m_pDeviceModule->Destroy(); + } + +- void Init() override { +- m_pDeviceModule = CFPF_GetSkiaDeviceModule(); ++ void Init() override { m_pDeviceModule = CFPF_GetSkiaDeviceModule(); } ++ ++ std::unique_ptr CreateDefaultSystemFontInfo() override { + CFPF_SkiaFontMgr* pFontMgr = m_pDeviceModule->GetFontMgr(); + if (!pFontMgr) +- return; ++ return nullptr; + +- auto pFontInfo = pdfium::MakeUnique(); ++ auto pFontInfo = std::make_unique(); + pFontInfo->Init(pFontMgr); +- CFX_GEModule::Get()->GetFontMgr()->SetSystemFontInfo(std::move(pFontInfo)); ++ return pFontInfo; + } + + private: +@@ -40,5 +39,5 @@ class CAndroidPlatform : public CFX_GEModule::PlatformIface { + // static + std::unique_ptr + CFX_GEModule::PlatformIface::Create() { +- return pdfium::MakeUnique(); ++ return std::make_unique(); + } +diff --git a/core/fxge/apple/apple_int.h b/core/fxge/apple/apple_int.h +deleted file mode 100644 +index c58e75ccc..000000000 +--- a/core/fxge/apple/apple_int.h ++++ /dev/null +@@ -1,48 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXGE_APPLE_APPLE_INT_H_ +-#define CORE_FXGE_APPLE_APPLE_INT_H_ +- +-#include "core/fxcrt/fx_system.h" +- +-#include +- +-#include "core/fxge/cfx_gemodule.h" +-#include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" +-#include "core/fxge/fx_dib.h" +-#include "core/fxge/renderdevicedriver_iface.h" +- +-class CQuartz2D { +- public: +- void* CreateGraphics(const RetainPtr& bitmap); +- void DestroyGraphics(void* graphics); +- +- void* CreateFont(const uint8_t* pFontData, uint32_t dwFontSize); +- void DestroyFont(void* pFont); +- void SetGraphicsTextMatrix(void* graphics, const CFX_Matrix& matrix); +- bool DrawGraphicsString(void* graphics, +- void* font, +- float fontSize, +- uint16_t* glyphIndices, +- CGPoint* glyphPositions, +- int32_t chars, +- FX_ARGB argb); +-}; +- +-class CApplePlatform : public CFX_GEModule::PlatformIface { +- public: +- CApplePlatform(); +- ~CApplePlatform() override; +- +- // CFX_GEModule::PlatformIface: +- void Init() override; +- +- CQuartz2D m_quartz2d; +-}; +- +-#endif // CORE_FXGE_APPLE_APPLE_INT_H_ +diff --git a/core/fxge/apple/fx_apple_impl.cpp b/core/fxge/apple/fx_apple_impl.cpp +new file mode 100644 +index 000000000..54f694f1a +--- /dev/null ++++ b/core/fxge/apple/fx_apple_impl.cpp +@@ -0,0 +1,181 @@ ++// Copyright 2014 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include ++ ++#include ++#include ++ ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_system.h" ++#include "core/fxge/agg/fx_agg_driver.h" ++#include "core/fxge/apple/fx_apple_platform.h" ++#include "core/fxge/cfx_cliprgn.h" ++#include "core/fxge/cfx_font.h" ++#include "core/fxge/cfx_gemodule.h" ++#include "core/fxge/cfx_glyphbitmap.h" ++#include "core/fxge/cfx_glyphcache.h" ++#include "core/fxge/cfx_renderdevice.h" ++#include "core/fxge/cfx_substfont.h" ++#include "core/fxge/dib/cfx_dibitmap.h" ++#include "core/fxge/freetype/fx_freetype.h" ++#include "core/fxge/text_char_pos.h" ++#include "third_party/base/span.h" ++ ++namespace { ++ ++void DoNothing(void* info, const void* data, size_t size) {} ++ ++bool CGDrawGlyphRun(CGContextRef pContext, ++ pdfium::span pCharPos, ++ CFX_Font* pFont, ++ const CFX_Matrix& mtObject2Device, ++ float font_size, ++ uint32_t argb) { ++ if (pCharPos.empty()) ++ return true; ++ ++ bool bNegSize = font_size < 0; ++ if (bNegSize) ++ font_size = -font_size; ++ ++ CFX_Matrix new_matrix = mtObject2Device; ++ CQuartz2D& quartz2d = ++ static_cast(CFX_GEModule::Get()->GetPlatform()) ++ ->m_quartz2d; ++ if (!pFont->GetPlatformFont()) { ++ if (pFont->GetPsName() == "DFHeiStd-W5") ++ return false; ++ ++ pFont->SetPlatformFont(quartz2d.CreateFont(pFont->GetFontSpan())); ++ if (!pFont->GetPlatformFont()) ++ return false; ++ } ++ DataVector glyph_indices(pCharPos.size()); ++ std::vector glyph_positions(pCharPos.size()); ++ for (size_t i = 0; i < pCharPos.size(); i++) { ++ glyph_indices[i] = ++ pCharPos[i].m_ExtGID ? pCharPos[i].m_ExtGID : pCharPos[i].m_GlyphIndex; ++ if (bNegSize) ++ glyph_positions[i].x = -pCharPos[i].m_Origin.x; ++ else ++ glyph_positions[i].x = pCharPos[i].m_Origin.x; ++ glyph_positions[i].y = pCharPos[i].m_Origin.y; ++ } ++ if (bNegSize) { ++ new_matrix.a = -new_matrix.a; ++ new_matrix.c = -new_matrix.c; ++ } else { ++ new_matrix.b = -new_matrix.b; ++ new_matrix.d = -new_matrix.d; ++ } ++ quartz2d.SetGraphicsTextMatrix(pContext, new_matrix); ++ return quartz2d.DrawGraphicsString(pContext, pFont->GetPlatformFont(), ++ font_size, glyph_indices, glyph_positions, ++ argb); ++} ++ ++} // namespace ++ ++namespace pdfium { ++ ++void CFX_AggDeviceDriver::InitPlatform() { ++ CQuartz2D& quartz2d = ++ static_cast(CFX_GEModule::Get()->GetPlatform()) ++ ->m_quartz2d; ++ m_pPlatformGraphics = quartz2d.CreateGraphics(m_pBitmap); ++} ++ ++void CFX_AggDeviceDriver::DestroyPlatform() { ++ CQuartz2D& quartz2d = ++ static_cast(CFX_GEModule::Get()->GetPlatform()) ++ ->m_quartz2d; ++ if (m_pPlatformGraphics) { ++ quartz2d.DestroyGraphics(m_pPlatformGraphics); ++ m_pPlatformGraphics = nullptr; ++ } ++} ++ ++bool CFX_AggDeviceDriver::DrawDeviceText( ++ pdfium::span pCharPos, ++ CFX_Font* pFont, ++ const CFX_Matrix& mtObject2Device, ++ float font_size, ++ uint32_t argb, ++ const CFX_TextRenderOptions& /*options*/) { ++ if (!pFont) ++ return false; ++ ++ bool bBold = pFont->IsBold(); ++ if (!bBold && pFont->GetSubstFont() && ++ pFont->GetSubstFont()->m_Weight >= 500 && ++ pFont->GetSubstFont()->m_Weight <= 600) { ++ return false; ++ } ++ for (const auto& cp : pCharPos) { ++ if (cp.m_bGlyphAdjust) ++ return false; ++ } ++ CGContextRef ctx = CGContextRef(m_pPlatformGraphics); ++ if (!ctx) ++ return false; ++ ++ CGContextSaveGState(ctx); ++ CGContextSetTextDrawingMode(ctx, kCGTextFillClip); ++ CGRect rect_cg; ++ CGImageRef pImageCG = nullptr; ++ if (m_pClipRgn) { ++ rect_cg = ++ CGRectMake(m_pClipRgn->GetBox().left, m_pClipRgn->GetBox().top, ++ m_pClipRgn->GetBox().Width(), m_pClipRgn->GetBox().Height()); ++ RetainPtr pClipMask = m_pClipRgn->GetMask(); ++ if (pClipMask) { ++ CGDataProviderRef pClipMaskDataProvider = CGDataProviderCreateWithData( ++ nullptr, pClipMask->GetBuffer().data(), ++ pClipMask->GetPitch() * pClipMask->GetHeight(), DoNothing); ++ CGFloat decode_f[2] = {255.f, 0.f}; ++ pImageCG = CGImageMaskCreate( ++ pClipMask->GetWidth(), pClipMask->GetHeight(), 8, 8, ++ pClipMask->GetPitch(), pClipMaskDataProvider, decode_f, false); ++ CGDataProviderRelease(pClipMaskDataProvider); ++ } ++ } else { ++ rect_cg = CGRectMake(0, 0, m_pBitmap->GetWidth(), m_pBitmap->GetHeight()); ++ } ++ rect_cg = CGContextConvertRectToDeviceSpace(ctx, rect_cg); ++ if (pImageCG) ++ CGContextClipToMask(ctx, rect_cg, pImageCG); ++ else ++ CGContextClipToRect(ctx, rect_cg); ++ ++ bool ret = ++ CGDrawGlyphRun(ctx, pCharPos, pFont, mtObject2Device, font_size, argb); ++ if (pImageCG) ++ CGImageRelease(pImageCG); ++ CGContextRestoreGState(ctx); ++ return ret; ++} ++ ++} // namespace pdfium ++ ++std::unique_ptr CFX_GlyphCache::RenderGlyph_Nativetext( ++ const CFX_Font* pFont, ++ uint32_t glyph_index, ++ const CFX_Matrix& matrix, ++ int dest_width, ++ int anti_alias) { ++ return nullptr; ++} ++ ++void CFX_Font::ReleasePlatformResource() { ++ if (m_pPlatformFont) { ++ CQuartz2D& quartz2d = ++ static_cast(CFX_GEModule::Get()->GetPlatform()) ++ ->m_quartz2d; ++ quartz2d.DestroyFont(m_pPlatformFont); ++ m_pPlatformFont = nullptr; ++ } ++} +diff --git a/core/fxge/apple/fx_apple_platform.cpp b/core/fxge/apple/fx_apple_platform.cpp +index 96adc8818..492adc47e 100644 +--- a/core/fxge/apple/fx_apple_platform.cpp ++++ b/core/fxge/apple/fx_apple_platform.cpp +@@ -1,187 +1,164 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#include +-#include +- +-#include "core/fxcrt/fx_system.h" +- +-#include "core/fxge/apple/apple_int.h" +-#include "core/fxge/cfx_cliprgn.h" +-#include "core/fxge/cfx_font.h" +-#include "core/fxge/cfx_gemodule.h" +-#include "core/fxge/cfx_glyphbitmap.h" +-#include "core/fxge/cfx_glyphcache.h" +-#include "core/fxge/cfx_renderdevice.h" +-#include "core/fxge/cfx_substfont.h" +-#include "core/fxge/dib/cfx_dibitmap.h" +-#include "core/fxge/fx_freetype.h" +-#include "core/fxge/text_char_pos.h" +-#include "third_party/base/span.h" ++#include "core/fxge/apple/fx_apple_platform.h" + +-#ifndef _SKIA_SUPPORT_ +-#include "core/fxge/agg/fx_agg_driver.h" +-#endif ++#include ++#include + +-#ifndef _SKIA_SUPPORT_ ++#include "core/fxcrt/fx_codepage.h" ++#include "core/fxge/cfx_folderfontinfo.h" ++#include "core/fxge/cfx_fontmgr.h" ++#include "core/fxge/fx_font.h" ++#include "core/fxge/systemfontinfo_iface.h" + + namespace { + +-void DoNothing(void* info, const void* data, size_t size) {} +- +-bool CGDrawGlyphRun(CGContextRef pContext, +- int nChars, +- const TextCharPos* pCharPos, +- CFX_Font* pFont, +- const CFX_Matrix& mtObject2Device, +- float font_size, +- uint32_t argb) { +- if (nChars == 0) +- return true; +- +- bool bNegSize = font_size < 0; +- if (bNegSize) +- font_size = -font_size; +- +- CFX_Matrix new_matrix = mtObject2Device; +- CQuartz2D& quartz2d = +- static_cast(CFX_GEModule::Get()->GetPlatform()) +- ->m_quartz2d; +- if (!pFont->GetPlatformFont()) { +- if (pFont->GetPsName() == "DFHeiStd-W5") +- return false; +- +- pdfium::span span = pFont->GetFontSpan(); +- pFont->SetPlatformFont(quartz2d.CreateFont(span.data(), span.size())); +- if (!pFont->GetPlatformFont()) +- return false; +- } +- std::vector glyph_indices(nChars); +- std::vector glyph_positions(nChars); +- for (int i = 0; i < nChars; i++) { +- glyph_indices[i] = +- pCharPos[i].m_ExtGID ? pCharPos[i].m_ExtGID : pCharPos[i].m_GlyphIndex; +- if (bNegSize) +- glyph_positions[i].x = -pCharPos[i].m_Origin.x; +- else +- glyph_positions[i].x = pCharPos[i].m_Origin.x; +- glyph_positions[i].y = pCharPos[i].m_Origin.y; +- } +- if (bNegSize) { +- new_matrix.a = -new_matrix.a; +- new_matrix.c = -new_matrix.c; +- } else { +- new_matrix.b = -new_matrix.b; +- new_matrix.d = -new_matrix.d; +- } +- quartz2d.SetGraphicsTextMatrix(pContext, new_matrix); +- return quartz2d.DrawGraphicsString(pContext, pFont->GetPlatformFont(), +- font_size, glyph_indices.data(), +- glyph_positions.data(), nChars, argb); ++struct Substs { ++ const char* m_pName; ++ const char* m_pSubstName; ++}; ++ ++constexpr Substs kBase14Substs[] = { ++ {"Courier", "Courier New"}, ++ {"Courier-Bold", "Courier New Bold"}, ++ {"Courier-BoldOblique", "Courier New Bold Italic"}, ++ {"Courier-Oblique", "Courier New Italic"}, ++ {"Helvetica", "Arial"}, ++ {"Helvetica-Bold", "Arial Bold"}, ++ {"Helvetica-BoldOblique", "Arial Bold Italic"}, ++ {"Helvetica-Oblique", "Arial Italic"}, ++ {"Times-Roman", "Times New Roman"}, ++ {"Times-Bold", "Times New Roman Bold"}, ++ {"Times-BoldItalic", "Times New Roman Bold Italic"}, ++ {"Times-Italic", "Times New Roman Italic"}, ++}; ++ ++class CFX_MacFontInfo final : public CFX_FolderFontInfo { ++ public: ++ CFX_MacFontInfo() = default; ++ ~CFX_MacFontInfo() override = default; ++ ++ // CFX_FolderFontInfo ++ void* MapFont(int weight, ++ bool bItalic, ++ FX_Charset charset, ++ int pitch_family, ++ const ByteString& face) override; ++ ++ bool ParseFontCfg(const char** pUserPaths); ++}; ++ ++constexpr char kJapanGothic[] = "Hiragino Kaku Gothic Pro W6"; ++constexpr char kJapanMincho[] = "Hiragino Mincho Pro W6"; ++ ++ByteString GetJapanesePreference(const ByteString& face, ++ int weight, ++ int pitch_family) { ++ if (face.Contains("Gothic")) ++ return kJapanGothic; ++ if (FontFamilyIsRoman(pitch_family) || weight <= 400) ++ return kJapanMincho; ++ return kJapanGothic; + } + +-} // namespace ++void* CFX_MacFontInfo::MapFont(int weight, ++ bool bItalic, ++ FX_Charset charset, ++ int pitch_family, ++ const ByteString& face) { ++ for (const auto& sub : kBase14Substs) { ++ if (face == ByteStringView(sub.m_pName)) ++ return GetFont(sub.m_pSubstName); ++ } + +-void CFX_AggDeviceDriver::InitPlatform() { +- CQuartz2D& quartz2d = +- static_cast(CFX_GEModule::Get()->GetPlatform()) +- ->m_quartz2d; +- m_pPlatformGraphics = quartz2d.CreateGraphics(m_pBitmap); +-} ++ // The request may not ask for the bold and/or italic version of a font by ++ // name. So try to construct the appropriate name. This is not 100% foolproof ++ // as there are fonts that have "Oblique" or "BoldOblique" or "Heavy" in their ++ // names instead. But this at least works for common fonts like Arial and ++ // Times New Roman. A more sophisticated approach would be to find all the ++ // fonts in |m_FontList| with |face| in the name, and examine the fonts to ++ // see which best matches the requested characteristics. ++ if (!face.Contains("Bold") && !face.Contains("Italic")) { ++ ByteString new_face = face; ++ if (weight > 400) ++ new_face += " Bold"; ++ if (bItalic) ++ new_face += " Italic"; ++ auto it = m_FontList.find(new_face); ++ if (it != m_FontList.end()) ++ return it->second.get(); ++ } + +-void CFX_AggDeviceDriver::DestroyPlatform() { +- CQuartz2D& quartz2d = +- static_cast(CFX_GEModule::Get()->GetPlatform()) +- ->m_quartz2d; +- if (m_pPlatformGraphics) { +- quartz2d.DestroyGraphics(m_pPlatformGraphics); +- m_pPlatformGraphics = nullptr; ++ auto it = m_FontList.find(face); ++ if (it != m_FontList.end()) ++ return it->second.get(); ++ ++ if (charset == FX_Charset::kANSI && FontFamilyIsFixedPitch(pitch_family)) ++ return GetFont("Courier New"); ++ ++ if (charset == FX_Charset::kANSI || charset == FX_Charset::kSymbol) ++ return nullptr; ++ ++ ByteString other_face; ++ switch (charset) { ++ case FX_Charset::kShiftJIS: ++ other_face = GetJapanesePreference(face, weight, pitch_family); ++ break; ++ case FX_Charset::kChineseSimplified: ++ other_face = "STSong"; ++ break; ++ case FX_Charset::kHangul: ++ other_face = "AppleMyungjo"; ++ break; ++ case FX_Charset::kChineseTraditional: ++ other_face = "LiSong Pro Light"; ++ break; ++ default: ++ other_face = face; ++ break; + } ++ it = m_FontList.find(other_face); ++ return it != m_FontList.end() ? it->second.get() : nullptr; + } + +-bool CFX_AggDeviceDriver::DrawDeviceText(int nChars, +- const TextCharPos* pCharPos, +- CFX_Font* pFont, +- const CFX_Matrix& mtObject2Device, +- float font_size, +- uint32_t argb) { +- if (!pFont) ++bool CFX_MacFontInfo::ParseFontCfg(const char** pUserPaths) { ++ if (!pUserPaths) + return false; + +- bool bBold = pFont->IsBold(); +- if (!bBold && pFont->GetSubstFont() && +- pFont->GetSubstFont()->m_Weight >= 500 && +- pFont->GetSubstFont()->m_Weight <= 600) { +- return false; +- } +- for (int i = 0; i < nChars; i++) { +- if (pCharPos[i].m_bGlyphAdjust) +- return false; +- } +- CGContextRef ctx = CGContextRef(m_pPlatformGraphics); +- if (!ctx) +- return false; +- +- CGContextSaveGState(ctx); +- CGContextSetTextDrawingMode(ctx, kCGTextFillClip); +- CGRect rect_cg; +- CGImageRef pImageCG = nullptr; +- if (m_pClipRgn) { +- rect_cg = +- CGRectMake(m_pClipRgn->GetBox().left, m_pClipRgn->GetBox().top, +- m_pClipRgn->GetBox().Width(), m_pClipRgn->GetBox().Height()); +- RetainPtr pClipMask = m_pClipRgn->GetMask(); +- if (pClipMask) { +- CGDataProviderRef pClipMaskDataProvider = CGDataProviderCreateWithData( +- nullptr, pClipMask->GetBuffer(), +- pClipMask->GetPitch() * pClipMask->GetHeight(), DoNothing); +- CGFloat decode_f[2] = {255.f, 0.f}; +- pImageCG = CGImageMaskCreate( +- pClipMask->GetWidth(), pClipMask->GetHeight(), 8, 8, +- pClipMask->GetPitch(), pClipMaskDataProvider, decode_f, false); +- CGDataProviderRelease(pClipMaskDataProvider); +- } +- } else { +- rect_cg = CGRectMake(0, 0, m_pBitmap->GetWidth(), m_pBitmap->GetHeight()); +- } +- rect_cg = CGContextConvertRectToDeviceSpace(ctx, rect_cg); +- if (pImageCG) +- CGContextClipToMask(ctx, rect_cg, pImageCG); +- else +- CGContextClipToRect(ctx, rect_cg); +- +- bool ret = CGDrawGlyphRun(ctx, nChars, pCharPos, pFont, mtObject2Device, +- font_size, argb); +- if (pImageCG) +- CGImageRelease(pImageCG); +- CGContextRestoreGState(ctx); +- return ret; ++ for (const char** pPath = pUserPaths; *pPath; ++pPath) ++ AddPath(*pPath); ++ return true; + } ++} // namespace + +-#endif // _SKIA_SUPPORT_ ++CApplePlatform::CApplePlatform() = default; + +-void CFX_GlyphCache::InitPlatform() {} ++CApplePlatform::~CApplePlatform() = default; + +-void CFX_GlyphCache::DestroyPlatform() {} ++void CApplePlatform::Init() {} + +-std::unique_ptr CFX_GlyphCache::RenderGlyph_Nativetext( +- const CFX_Font* pFont, +- uint32_t glyph_index, +- const CFX_Matrix& matrix, +- uint32_t dest_width, +- int anti_alias) { +- return nullptr; ++std::unique_ptr ++CApplePlatform::CreateDefaultSystemFontInfo() { ++ auto pInfo = std::make_unique(); ++ if (!pInfo->ParseFontCfg(CFX_GEModule::Get()->GetUserFontPaths())) { ++ pInfo->AddPath("~/Library/Fonts"); ++ pInfo->AddPath("/Library/Fonts"); ++ pInfo->AddPath("/System/Library/Fonts"); ++ } ++ return std::move(pInfo); + } + +-void CFX_Font::ReleasePlatformResource() { +- if (m_pPlatformFont) { +- CQuartz2D& quartz2d = +- static_cast(CFX_GEModule::Get()->GetPlatform()) +- ->m_quartz2d; +- quartz2d.DestroyFont(m_pPlatformFont); +- m_pPlatformFont = nullptr; +- } ++void* CApplePlatform::CreatePlatformFont( ++ pdfium::span font_span) { ++ return m_quartz2d.CreateFont(font_span); ++} ++ ++// static ++std::unique_ptr ++CFX_GEModule::PlatformIface::Create() { ++ return std::make_unique(); + } +diff --git a/core/fxge/apple/fx_apple_platform.h b/core/fxge/apple/fx_apple_platform.h +new file mode 100644 +index 000000000..fef27e686 +--- /dev/null ++++ b/core/fxge/apple/fx_apple_platform.h +@@ -0,0 +1,28 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXGE_APPLE_FX_APPLE_PLATFORM_H_ ++#define CORE_FXGE_APPLE_FX_APPLE_PLATFORM_H_ ++ ++#include ++ ++#include "core/fxge/apple/fx_quartz_device.h" ++#include "core/fxge/cfx_gemodule.h" ++ ++class CApplePlatform final : public CFX_GEModule::PlatformIface { ++ public: ++ CApplePlatform(); ++ ~CApplePlatform() override; ++ ++ // CFX_GEModule::PlatformIface: ++ void Init() override; ++ std::unique_ptr CreateDefaultSystemFontInfo() override; ++ void* CreatePlatformFont(pdfium::span font_span) override; ++ ++ CQuartz2D m_quartz2d; ++}; ++ ++#endif // CORE_FXGE_APPLE_FX_APPLE_PLATFORM_H_ +diff --git a/core/fxge/apple/fx_mac_imp.cpp b/core/fxge/apple/fx_mac_imp.cpp +deleted file mode 100644 +index 64e50cc5f..000000000 +--- a/core/fxge/apple/fx_mac_imp.cpp ++++ /dev/null +@@ -1,159 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include +-#include +- +-#include "core/fxcrt/fx_codepage.h" +-#include "core/fxge/apple/apple_int.h" +-#include "core/fxge/cfx_folderfontinfo.h" +-#include "core/fxge/cfx_fontmgr.h" +-#include "core/fxge/cfx_gemodule.h" +-#include "core/fxge/fx_font.h" +-#include "core/fxge/systemfontinfo_iface.h" +-#include "third_party/base/ptr_util.h" +- +-namespace { +- +-const struct { +- const char* m_pName; +- const char* m_pSubstName; +-} g_Base14Substs[] = { +- {"Courier", "Courier New"}, +- {"Courier-Bold", "Courier New Bold"}, +- {"Courier-BoldOblique", "Courier New Bold Italic"}, +- {"Courier-Oblique", "Courier New Italic"}, +- {"Helvetica", "Arial"}, +- {"Helvetica-Bold", "Arial Bold"}, +- {"Helvetica-BoldOblique", "Arial Bold Italic"}, +- {"Helvetica-Oblique", "Arial Italic"}, +- {"Times-Roman", "Times New Roman"}, +- {"Times-Bold", "Times New Roman Bold"}, +- {"Times-BoldItalic", "Times New Roman Bold Italic"}, +- {"Times-Italic", "Times New Roman Italic"}, +-}; +- +-class CFX_MacFontInfo final : public CFX_FolderFontInfo { +- public: +- CFX_MacFontInfo() {} +- ~CFX_MacFontInfo() override {} +- +- // CFX_FolderFontInfo +- void* MapFont(int weight, +- bool bItalic, +- int charset, +- int pitch_family, +- const char* family) override; +- +- bool ParseFontCfg(const char** pUserPaths); +-}; +- +-const char JAPAN_GOTHIC[] = "Hiragino Kaku Gothic Pro W6"; +-const char JAPAN_MINCHO[] = "Hiragino Mincho Pro W6"; +- +-void GetJapanesePreference(ByteString* face, int weight, int pitch_family) { +- if (face->Contains("Gothic")) { +- *face = JAPAN_GOTHIC; +- return; +- } +- *face = (FontFamilyIsRoman(pitch_family) || weight <= 400) ? JAPAN_MINCHO +- : JAPAN_GOTHIC; +-} +- +-void* CFX_MacFontInfo::MapFont(int weight, +- bool bItalic, +- int charset, +- int pitch_family, +- const char* cstr_face) { +- ByteString face = cstr_face; +- for (const auto& sub : g_Base14Substs) { +- if (face == ByteStringView(sub.m_pName)) { +- face = sub.m_pSubstName; +- return GetFont(face.c_str()); +- } +- } +- +- // The request may not ask for the bold and/or italic version of a font by +- // name. So try to construct the appropriate name. This is not 100% foolproof +- // as there are fonts that have "Oblique" or "BoldOblique" or "Heavy" in their +- // names instead. But this at least works for common fonts like Arial and +- // Times New Roman. A more sophisticated approach would be to find all the +- // fonts in |m_FontList| with |face| in the name, and examine the fonts to +- // see which best matches the requested characteristics. +- if (!face.Contains("Bold") && !face.Contains("Italic")) { +- ByteString new_face = face; +- if (weight > 400) +- new_face += " Bold"; +- if (bItalic) +- new_face += " Italic"; +- auto it = m_FontList.find(new_face); +- if (it != m_FontList.end()) +- return it->second.get(); +- } +- +- auto it = m_FontList.find(face); +- if (it != m_FontList.end()) +- return it->second.get(); +- +- if (charset == FX_CHARSET_ANSI && FontFamilyIsFixedPitch(pitch_family)) +- return GetFont("Courier New"); +- +- if (charset == FX_CHARSET_ANSI || charset == FX_CHARSET_Symbol) +- return nullptr; +- +- switch (charset) { +- case FX_CHARSET_ShiftJIS: +- GetJapanesePreference(&face, weight, pitch_family); +- break; +- case FX_CHARSET_ChineseSimplified: +- face = "STSong"; +- break; +- case FX_CHARSET_Hangul: +- face = "AppleMyungjo"; +- break; +- case FX_CHARSET_ChineseTraditional: +- face = "LiSong Pro Light"; +- } +- it = m_FontList.find(face); +- return it != m_FontList.end() ? it->second.get() : nullptr; +-} +- +-bool CFX_MacFontInfo::ParseFontCfg(const char** pUserPaths) { +- if (!pUserPaths) +- return false; +- +- for (const char** pPath = pUserPaths; *pPath; ++pPath) +- AddPath(*pPath); +- return true; +-} +-} // namespace +- +-std::unique_ptr SystemFontInfoIface::CreateDefault( +- const char** pUserPaths) { +- auto pInfo = pdfium::MakeUnique(); +- if (!pInfo->ParseFontCfg(pUserPaths)) { +- pInfo->AddPath("~/Library/Fonts"); +- pInfo->AddPath("/Library/Fonts"); +- pInfo->AddPath("/System/Library/Fonts"); +- } +- return std::move(pInfo); +-} +- +-CApplePlatform::CApplePlatform() = default; +- +-CApplePlatform::~CApplePlatform() = default; +- +-void CApplePlatform::Init() { +- CFX_GEModule* pModule = CFX_GEModule::Get(); +- pModule->GetFontMgr()->SetSystemFontInfo( +- SystemFontInfoIface::CreateDefault(pModule->GetUserFontPaths())); +-} +- +-// static +-std::unique_ptr +-CFX_GEModule::PlatformIface::Create() { +- return pdfium::MakeUnique(); +-} +diff --git a/core/fxge/apple/fx_quartz_device.cpp b/core/fxge/apple/fx_quartz_device.cpp +index 2281ba5ca..9f5f13fef 100644 +--- a/core/fxge/apple/fx_quartz_device.cpp ++++ b/core/fxge/apple/fx_quartz_device.cpp +@@ -1,22 +1,20 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#include "core/fxcrt/fx_extension.h" ++#include "core/fxge/apple/fx_quartz_device.h" + +-#if !defined _SKIA_SUPPORT_ && !defined _SKIA_SUPPORT_PATHS_ +-#include "core/fxge/agg/fx_agg_driver.h" +-#endif ++#include + ++#include "core/fxcrt/fx_extension.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/cfx_renderdevice.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "core/fxge/fx_freetype.h" ++#include "core/fxge/freetype/fx_freetype.h" + +-#include "core/fxge/apple/apple_int.h" + #ifndef CGFLOAT_IS_DOUBLE + #error Expected CGFLOAT_IS_DOUBLE to be defined by CoreGraphics headers + #endif +@@ -26,16 +24,16 @@ void* CQuartz2D::CreateGraphics(const RetainPtr& pBitmap) { + return nullptr; + CGBitmapInfo bmpInfo = kCGBitmapByteOrder32Little; + switch (pBitmap->GetFormat()) { +- case FXDIB_Rgb32: ++ case FXDIB_Format::kRgb32: + bmpInfo |= kCGImageAlphaNoneSkipFirst; + break; +- case FXDIB_Argb: ++ case FXDIB_Format::kArgb: + default: + return nullptr; + } + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = CGBitmapContextCreate( +- pBitmap->GetBuffer(), pBitmap->GetWidth(), pBitmap->GetHeight(), 8, ++ pBitmap->GetBuffer().data(), pBitmap->GetWidth(), pBitmap->GetHeight(), 8, + pBitmap->GetPitch(), colorSpace, bmpInfo); + CGColorSpaceRelease(colorSpace); + return context; +@@ -46,9 +44,9 @@ void CQuartz2D::DestroyGraphics(void* graphics) { + CGContextRelease((CGContextRef)graphics); + } + +-void* CQuartz2D::CreateFont(const uint8_t* pFontData, uint32_t dwFontSize) { ++void* CQuartz2D::CreateFont(pdfium::span pFontData) { + CGDataProviderRef pDataProvider = CGDataProviderCreateWithData( +- nullptr, pFontData, static_cast(dwFontSize), nullptr); ++ nullptr, pFontData.data(), pFontData.size(), nullptr); + if (!pDataProvider) + return nullptr; + +@@ -75,9 +73,8 @@ void CQuartz2D::SetGraphicsTextMatrix(void* graphics, + bool CQuartz2D::DrawGraphicsString(void* graphics, + void* font, + float fontSize, +- uint16_t* glyphIndices, +- CGPoint* glyphPositions, +- int32_t charsCount, ++ pdfium::span glyphIndices, ++ pdfium::span glyphPositions, + FX_ARGB argb) { + if (!graphics) + return false; +@@ -94,17 +91,17 @@ bool CQuartz2D::DrawGraphicsString(void* graphics, + CGContextSetRGBFillColor(context, r / 255.f, g / 255.f, b / 255.f, a / 255.f); + CGContextSaveGState(context); + #if CGFLOAT_IS_DOUBLE +- CGPoint* glyphPositionsCG = new CGPoint[charsCount]; +- for (int index = 0; index < charsCount; ++index) { ++ CGPoint* glyphPositionsCG = new CGPoint[glyphPositions.size()]; ++ for (size_t index = 0; index < glyphPositions.size(); ++index) { + glyphPositionsCG[index].x = glyphPositions[index].x; + glyphPositionsCG[index].y = glyphPositions[index].y; + } + #else +- CGPoint* glyphPositionsCG = glyphPositions; ++ CGPoint* glyphPositionsCG = glyphPositions.data(); + #endif +- CGContextShowGlyphsAtPositions(context, +- reinterpret_cast(glyphIndices), +- glyphPositionsCG, charsCount); ++ CGContextShowGlyphsAtPositions( ++ context, reinterpret_cast(glyphIndices.data()), ++ glyphPositionsCG, glyphPositions.size()); + #if CGFLOAT_IS_DOUBLE + delete[] glyphPositionsCG; + #endif +diff --git a/core/fxge/apple/fx_quartz_device.h b/core/fxge/apple/fx_quartz_device.h +new file mode 100644 +index 000000000..0aaf520b1 +--- /dev/null ++++ b/core/fxge/apple/fx_quartz_device.h +@@ -0,0 +1,36 @@ ++// Copyright 2014 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXGE_APPLE_FX_QUARTZ_DEVICE_H_ ++#define CORE_FXGE_APPLE_FX_QUARTZ_DEVICE_H_ ++ ++#include ++#include ++ ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/span.h" ++ ++class CFX_DIBitmap; ++class CFX_Matrix; ++ ++class CQuartz2D { ++ public: ++ void* CreateGraphics(const RetainPtr& bitmap); ++ void DestroyGraphics(void* graphics); ++ ++ void* CreateFont(pdfium::span pFontData); ++ void DestroyFont(void* pFont); ++ void SetGraphicsTextMatrix(void* graphics, const CFX_Matrix& matrix); ++ bool DrawGraphicsString(void* graphics, ++ void* font, ++ float fontSize, ++ pdfium::span glyphIndices, ++ pdfium::span glyphPositions, ++ FX_ARGB argb); ++}; ++ ++#endif // CORE_FXGE_APPLE_FX_QUARTZ_DEVICE_H_ +diff --git a/core/fxge/calculate_pitch.cpp b/core/fxge/calculate_pitch.cpp +new file mode 100644 +index 000000000..799e10e88 +--- /dev/null ++++ b/core/fxge/calculate_pitch.cpp +@@ -0,0 +1,59 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxge/calculate_pitch.h" ++ ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxge/dib/fx_dib.h" ++ ++namespace fxge { ++namespace { ++ ++FX_SAFE_UINT32 CalculatePitch8Safely(uint32_t bpc, ++ uint32_t components, ++ int width) { ++ FX_SAFE_UINT32 pitch = bpc; ++ pitch *= components; ++ pitch *= width; ++ pitch += 7; ++ pitch /= 8; ++ return pitch; ++} ++ ++FX_SAFE_UINT32 CalculatePitch32Safely(int bpp, int width) { ++ FX_SAFE_UINT32 pitch = bpp; ++ pitch *= width; ++ pitch += 31; ++ pitch /= 32; // quantized to number of 32-bit words. ++ pitch *= 4; // and then back to bytes, (not just /8 in one step). ++ return pitch; ++} ++ ++} // namespace ++ ++uint32_t CalculatePitch8OrDie(uint32_t bpc, uint32_t components, int width) { ++ return CalculatePitch8Safely(bpc, components, width).ValueOrDie(); ++} ++ ++uint32_t CalculatePitch32OrDie(int bpp, int width) { ++ return CalculatePitch32Safely(bpp, width).ValueOrDie(); ++} ++ ++absl::optional CalculatePitch8(uint32_t bpc, ++ uint32_t components, ++ int width) { ++ FX_SAFE_UINT32 pitch = CalculatePitch8Safely(bpc, components, width); ++ if (!pitch.IsValid()) ++ return absl::nullopt; ++ return pitch.ValueOrDie(); ++} ++ ++absl::optional CalculatePitch32(int bpp, int width) { ++ FX_SAFE_UINT32 pitch = CalculatePitch32Safely(bpp, width); ++ if (!pitch.IsValid()) ++ return absl::nullopt; ++ return pitch.ValueOrDie(); ++} ++ ++} // namespace fxge +diff --git a/core/fxge/calculate_pitch.h b/core/fxge/calculate_pitch.h +new file mode 100644 +index 000000000..1de7fddd3 +--- /dev/null ++++ b/core/fxge/calculate_pitch.h +@@ -0,0 +1,23 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXGE_CALCULATE_PITCH_H_ ++#define CORE_FXGE_CALCULATE_PITCH_H_ ++ ++#include ++ ++#include "third_party/abseil-cpp/absl/types/optional.h" ++ ++namespace fxge { ++ ++uint32_t CalculatePitch8OrDie(uint32_t bpc, uint32_t components, int width); ++uint32_t CalculatePitch32OrDie(int bpp, int width); ++absl::optional CalculatePitch8(uint32_t bpc, ++ uint32_t components, ++ int width); ++absl::optional CalculatePitch32(int bpp, int width); ++ ++} // namespace fxge ++ ++#endif // CORE_FXGE_CALCULATE_PITCH_H_ +diff --git a/core/fxge/cfx_cliprgn.cpp b/core/fxge/cfx_cliprgn.cpp +index 8a3f94602..33eb14b08 100644 +--- a/core/fxge/cfx_cliprgn.cpp ++++ b/core/fxge/cfx_cliprgn.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,91 +6,85 @@ + + #include "core/fxge/cfx_cliprgn.h" + ++#include ++ + #include + ++#include "core/fxcrt/span_util.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "third_party/base/logging.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/notreached.h" + +-CFX_ClipRgn::CFX_ClipRgn(int width, int height) +- : m_Type(RectI), m_Box(0, 0, width, height) {} ++CFX_ClipRgn::CFX_ClipRgn(int width, int height) : m_Box(0, 0, width, height) {} + + CFX_ClipRgn::CFX_ClipRgn(const CFX_ClipRgn& src) = default; + + CFX_ClipRgn::~CFX_ClipRgn() = default; + + void CFX_ClipRgn::IntersectRect(const FX_RECT& rect) { +- if (m_Type == RectI) { ++ if (m_Type == kRectI) { + m_Box.Intersect(rect); + return; + } +- if (m_Type == MaskF) { +- IntersectMaskRect(rect, m_Box, m_Mask); +- return; +- } ++ IntersectMaskRect(rect, m_Box, m_Mask); + } + + void CFX_ClipRgn::IntersectMaskRect(FX_RECT rect, + FX_RECT mask_rect, +- const RetainPtr& pMask) { +- m_Type = MaskF; ++ RetainPtr pOldMask) { ++ m_Type = kMaskF; + m_Box = rect; + m_Box.Intersect(mask_rect); + if (m_Box.IsEmpty()) { +- m_Type = RectI; ++ m_Type = kRectI; + return; + } + if (m_Box == mask_rect) { +- m_Mask = pMask; ++ m_Mask = std::move(pOldMask); + return; + } +- RetainPtr pOldMask(pMask); + m_Mask = pdfium::MakeRetain(); +- m_Mask->Create(m_Box.Width(), m_Box.Height(), FXDIB_8bppMask); ++ m_Mask->Create(m_Box.Width(), m_Box.Height(), FXDIB_Format::k8bppMask); ++ const int offset = m_Box.left - mask_rect.left; + for (int row = m_Box.top; row < m_Box.bottom; row++) { +- uint8_t* dest_scan = +- m_Mask->GetBuffer() + m_Mask->GetPitch() * (row - m_Box.top); +- uint8_t* src_scan = +- pOldMask->GetBuffer() + pOldMask->GetPitch() * (row - mask_rect.top); +- for (int col = m_Box.left; col < m_Box.right; col++) +- dest_scan[col - m_Box.left] = src_scan[col - mask_rect.left]; ++ pdfium::span dest_scan = ++ m_Mask->GetWritableScanline(row - m_Box.top); ++ pdfium::span src_scan = ++ pOldMask->GetScanline(row - mask_rect.top); ++ fxcrt::spancpy(dest_scan, src_scan.subspan(offset, m_Box.Width())); + } + } + + void CFX_ClipRgn::IntersectMaskF(int left, + int top, +- const RetainPtr& pMask) { +- ASSERT(pMask->GetFormat() == FXDIB_8bppMask); ++ RetainPtr pMask) { ++ DCHECK_EQ(pMask->GetFormat(), FXDIB_Format::k8bppMask); + FX_RECT mask_box(left, top, left + pMask->GetWidth(), + top + pMask->GetHeight()); +- if (m_Type == RectI) { +- IntersectMaskRect(m_Box, mask_box, pMask); ++ if (m_Type == kRectI) { ++ IntersectMaskRect(m_Box, mask_box, std::move(pMask)); + return; + } +- if (m_Type == MaskF) { +- FX_RECT new_box = m_Box; +- new_box.Intersect(mask_box); +- if (new_box.IsEmpty()) { +- m_Type = RectI; +- m_Mask = nullptr; +- m_Box = new_box; +- return; +- } +- auto new_dib = pdfium::MakeRetain(); +- new_dib->Create(new_box.Width(), new_box.Height(), FXDIB_8bppMask); +- for (int row = new_box.top; row < new_box.bottom; row++) { +- uint8_t* old_scan = +- m_Mask->GetBuffer() + (row - m_Box.top) * m_Mask->GetPitch(); +- uint8_t* mask_scan = pMask->GetBuffer() + (row - top) * pMask->GetPitch(); +- uint8_t* new_scan = +- new_dib->GetBuffer() + (row - new_box.top) * new_dib->GetPitch(); +- for (int col = new_box.left; col < new_box.right; col++) { +- new_scan[col - new_box.left] = +- old_scan[col - m_Box.left] * mask_scan[col - left] / 255; +- } +- } ++ ++ FX_RECT new_box = m_Box; ++ new_box.Intersect(mask_box); ++ if (new_box.IsEmpty()) { ++ m_Type = kRectI; ++ m_Mask = nullptr; + m_Box = new_box; +- m_Mask = std::move(new_dib); + return; + } +- NOTREACHED(); ++ auto new_dib = pdfium::MakeRetain(); ++ new_dib->Create(new_box.Width(), new_box.Height(), FXDIB_Format::k8bppMask); ++ for (int row = new_box.top; row < new_box.bottom; row++) { ++ pdfium::span old_scan = m_Mask->GetScanline(row - m_Box.top); ++ pdfium::span mask_scan = pMask->GetScanline(row - top); ++ uint8_t* new_scan = new_dib->GetWritableScanline(row - new_box.top).data(); ++ for (int col = new_box.left; col < new_box.right; col++) { ++ new_scan[col - new_box.left] = ++ old_scan[col - m_Box.left] * mask_scan[col - left] / 255; ++ } ++ } ++ m_Box = new_box; ++ m_Mask = std::move(new_dib); + } +diff --git a/core/fxge/cfx_cliprgn.h b/core/fxge/cfx_cliprgn.h +index e1f9e2212..2884ac7cb 100644 +--- a/core/fxge/cfx_cliprgn.h ++++ b/core/fxge/cfx_cliprgn.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CFX_DIBitmap; + + class CFX_ClipRgn { + public: +- enum ClipType { RectI, MaskF }; ++ enum ClipType : bool { kRectI, kMaskF }; + + CFX_ClipRgn(int device_width, int device_height); + CFX_ClipRgn(const CFX_ClipRgn& src); +@@ -25,14 +25,14 @@ class CFX_ClipRgn { + RetainPtr GetMask() const { return m_Mask; } + + void IntersectRect(const FX_RECT& rect); +- void IntersectMaskF(int left, int top, const RetainPtr& Mask); ++ void IntersectMaskF(int left, int top, RetainPtr Mask); + + private: + void IntersectMaskRect(FX_RECT rect, + FX_RECT mask_rect, +- const RetainPtr& Mask); ++ RetainPtr pOldMask); + +- ClipType m_Type; ++ ClipType m_Type = kRectI; + FX_RECT m_Box; + RetainPtr m_Mask; + }; +diff --git a/core/fxge/cfx_color.cpp b/core/fxge/cfx_color.cpp +index 99330bc31..23cdd0faa 100644 +--- a/core/fxge/cfx_color.cpp ++++ b/core/fxge/cfx_color.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,13 +8,15 @@ + + #include + +-// Color types are orded by increasing number of components so we can +-// choose a best color type during some conversions. +-static_assert(CFX_Color::kTransparent < CFX_Color::kGray, ++#include "third_party/base/notreached.h" ++ ++// Color types are ordered by increasing number of components so we can choose ++// a best color type during some conversions. ++static_assert(CFX_Color::Type::kTransparent < CFX_Color::Type::kGray, + "color type values must be ordered"); +-static_assert(CFX_Color::kGray < CFX_Color::kRGB, ++static_assert(CFX_Color::Type::kGray < CFX_Color::Type::kRGB, + "color type values must be ordered"); +-static_assert(CFX_Color::kRGB < CFX_Color::kCMYK, ++static_assert(CFX_Color::Type::kRGB < CFX_Color::Type::kCMYK, + "color type values must be ordered"); + + namespace { +@@ -25,88 +27,104 @@ bool InRange(float comp) { + + CFX_Color ConvertCMYK2GRAY(float dC, float dM, float dY, float dK) { + if (!InRange(dC) || !InRange(dM) || !InRange(dY) || !InRange(dK)) +- return CFX_Color(CFX_Color::kGray); ++ return CFX_Color(CFX_Color::Type::kGray); + return CFX_Color( +- CFX_Color::kGray, ++ CFX_Color::Type::kGray, + 1.0f - std::min(1.0f, 0.3f * dC + 0.59f * dM + 0.11f * dY + dK)); + } + + CFX_Color ConvertGRAY2CMYK(float dGray) { + if (!InRange(dGray)) +- return CFX_Color(CFX_Color::kCMYK); +- return CFX_Color(CFX_Color::kCMYK, 0.0f, 0.0f, 0.0f, 1.0f - dGray); ++ return CFX_Color(CFX_Color::Type::kCMYK); ++ return CFX_Color(CFX_Color::Type::kCMYK, 0.0f, 0.0f, 0.0f, 1.0f - dGray); + } + + CFX_Color ConvertGRAY2RGB(float dGray) { + if (!InRange(dGray)) +- return CFX_Color(CFX_Color::kRGB); +- return CFX_Color(CFX_Color::kRGB, dGray, dGray, dGray); ++ return CFX_Color(CFX_Color::Type::kRGB); ++ return CFX_Color(CFX_Color::Type::kRGB, dGray, dGray, dGray); + } + + CFX_Color ConvertRGB2GRAY(float dR, float dG, float dB) { + if (!InRange(dR) || !InRange(dG) || !InRange(dB)) +- return CFX_Color(CFX_Color::kGray); +- return CFX_Color(CFX_Color::kGray, 0.3f * dR + 0.59f * dG + 0.11f * dB); ++ return CFX_Color(CFX_Color::Type::kGray); ++ return CFX_Color(CFX_Color::Type::kGray, 0.3f * dR + 0.59f * dG + 0.11f * dB); + } + + CFX_Color ConvertCMYK2RGB(float dC, float dM, float dY, float dK) { + if (!InRange(dC) || !InRange(dM) || !InRange(dY) || !InRange(dK)) +- return CFX_Color(CFX_Color::kRGB); +- return CFX_Color(CFX_Color::kRGB, 1.0f - std::min(1.0f, dC + dK), ++ return CFX_Color(CFX_Color::Type::kRGB); ++ return CFX_Color(CFX_Color::Type::kRGB, 1.0f - std::min(1.0f, dC + dK), + 1.0f - std::min(1.0f, dM + dK), + 1.0f - std::min(1.0f, dY + dK)); + } + + CFX_Color ConvertRGB2CMYK(float dR, float dG, float dB) { + if (!InRange(dR) || !InRange(dG) || !InRange(dB)) +- return CFX_Color(CFX_Color::kCMYK); ++ return CFX_Color(CFX_Color::Type::kCMYK); + + float c = 1.0f - dR; + float m = 1.0f - dG; + float y = 1.0f - dB; +- return CFX_Color(CFX_Color::kCMYK, c, m, y, std::min(c, std::min(m, y))); ++ return CFX_Color(CFX_Color::Type::kCMYK, c, m, y, ++ std::min(c, std::min(m, y))); + } + + } // namespace + +-CFX_Color CFX_Color::ConvertColorType(int32_t nConvertColorType) const { ++CFX_Color CFX_Color::ConvertColorType(Type nConvertColorType) const { + if (nColorType == nConvertColorType) + return *this; + + CFX_Color ret; + switch (nColorType) { +- case CFX_Color::kTransparent: ++ case CFX_Color::Type::kTransparent: + ret = *this; +- ret.nColorType = CFX_Color::kTransparent; ++ ret.nColorType = CFX_Color::Type::kTransparent; + break; +- case CFX_Color::kGray: ++ case CFX_Color::Type::kGray: + switch (nConvertColorType) { +- case CFX_Color::kRGB: ++ case CFX_Color::Type::kTransparent: ++ break; ++ case CFX_Color::Type::kGray: ++ NOTREACHED(); ++ break; ++ case CFX_Color::Type::kRGB: + ret = ConvertGRAY2RGB(fColor1); + break; +- case CFX_Color::kCMYK: ++ case CFX_Color::Type::kCMYK: + ret = ConvertGRAY2CMYK(fColor1); + break; + } + break; +- case CFX_Color::kRGB: ++ case CFX_Color::Type::kRGB: + switch (nConvertColorType) { +- case CFX_Color::kGray: ++ case CFX_Color::Type::kTransparent: ++ break; ++ case CFX_Color::Type::kGray: + ret = ConvertRGB2GRAY(fColor1, fColor2, fColor3); + break; +- case CFX_Color::kCMYK: ++ case CFX_Color::Type::kRGB: ++ NOTREACHED(); ++ break; ++ case CFX_Color::Type::kCMYK: + ret = ConvertRGB2CMYK(fColor1, fColor2, fColor3); + break; + } + break; +- case CFX_Color::kCMYK: ++ case CFX_Color::Type::kCMYK: + switch (nConvertColorType) { +- case CFX_Color::kGray: ++ case CFX_Color::Type::kTransparent: ++ break; ++ case CFX_Color::Type::kGray: + ret = ConvertCMYK2GRAY(fColor1, fColor2, fColor3, fColor4); + break; +- case CFX_Color::kRGB: ++ case CFX_Color::Type::kRGB: + ret = ConvertCMYK2RGB(fColor1, fColor2, fColor3, fColor4); + break; ++ case CFX_Color::Type::kCMYK: ++ NOTREACHED(); ++ break; + } + break; + } +@@ -116,21 +134,21 @@ CFX_Color CFX_Color::ConvertColorType(int32_t nConvertColorType) const { + FX_COLORREF CFX_Color::ToFXColor(int32_t nTransparency) const { + CFX_Color ret; + switch (nColorType) { +- case CFX_Color::kTransparent: { +- ret = CFX_Color(CFX_Color::kTransparent, 0, 0, 0, 0); ++ case CFX_Color::Type::kTransparent: { ++ ret = CFX_Color(CFX_Color::Type::kTransparent, 0, 0, 0, 0); + break; + } +- case CFX_Color::kGray: { ++ case CFX_Color::Type::kGray: { + ret = ConvertGRAY2RGB(fColor1); + ret.fColor4 = nTransparency; + break; + } +- case CFX_Color::kRGB: { +- ret = CFX_Color(CFX_Color::kRGB, fColor1, fColor2, fColor3); ++ case CFX_Color::Type::kRGB: { ++ ret = CFX_Color(CFX_Color::Type::kRGB, fColor1, fColor2, fColor3); + ret.fColor4 = nTransparency; + break; + } +- case CFX_Color::kCMYK: { ++ case CFX_Color::Type::kCMYK: { + ret = ConvertCMYK2RGB(fColor1, fColor2, fColor3, fColor4); + ret.fColor4 = nTransparency; + break; +@@ -144,15 +162,15 @@ FX_COLORREF CFX_Color::ToFXColor(int32_t nTransparency) const { + CFX_Color CFX_Color::operator-(float fColorSub) const { + CFX_Color sRet(nColorType); + switch (nColorType) { +- case CFX_Color::kTransparent: +- sRet.nColorType = CFX_Color::kRGB; ++ case CFX_Color::Type::kTransparent: ++ sRet.nColorType = CFX_Color::Type::kRGB; + sRet.fColor1 = std::max(1.0f - fColorSub, 0.0f); + sRet.fColor2 = std::max(1.0f - fColorSub, 0.0f); + sRet.fColor3 = std::max(1.0f - fColorSub, 0.0f); + break; +- case CFX_Color::kRGB: +- case CFX_Color::kGray: +- case CFX_Color::kCMYK: ++ case CFX_Color::Type::kRGB: ++ case CFX_Color::Type::kGray: ++ case CFX_Color::Type::kCMYK: + sRet.fColor1 = std::max(fColor1 - fColorSub, 0.0f); + sRet.fColor2 = std::max(fColor2 - fColorSub, 0.0f); + sRet.fColor3 = std::max(fColor3 - fColorSub, 0.0f); +@@ -165,15 +183,15 @@ CFX_Color CFX_Color::operator-(float fColorSub) const { + CFX_Color CFX_Color::operator/(float fColorDivide) const { + CFX_Color sRet(nColorType); + switch (nColorType) { +- case CFX_Color::kTransparent: +- sRet.nColorType = CFX_Color::kRGB; ++ case CFX_Color::Type::kTransparent: ++ sRet.nColorType = CFX_Color::Type::kRGB; + sRet.fColor1 = 1.0f / fColorDivide; + sRet.fColor2 = 1.0f / fColorDivide; + sRet.fColor3 = 1.0f / fColorDivide; + break; +- case CFX_Color::kRGB: +- case CFX_Color::kGray: +- case CFX_Color::kCMYK: ++ case CFX_Color::Type::kRGB: ++ case CFX_Color::Type::kGray: ++ case CFX_Color::Type::kCMYK: + sRet = *this; + sRet.fColor1 /= fColorDivide; + sRet.fColor2 /= fColorDivide; +diff --git a/core/fxge/cfx_color.h b/core/fxge/cfx_color.h +index fc941db96..17191299b 100644 +--- a/core/fxge/cfx_color.h ++++ b/core/fxge/cfx_color.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,51 +7,51 @@ + #ifndef CORE_FXGE_CFX_COLOR_H_ + #define CORE_FXGE_CFX_COLOR_H_ + +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" + + struct CFX_Color { + // Ordered by increasing number of components. +- enum Type { kTransparent = 0, kGray, kRGB, kCMYK }; ++ enum class Type { kTransparent = 0, kGray, kRGB, kCMYK }; + +- explicit CFX_Color(FX_COLORREF ref) ++ struct TypeAndARGB { ++ TypeAndARGB(CFX_Color::Type type_in, FX_ARGB argb_in) ++ : color_type(type_in), argb(argb_in) {} ++ ++ CFX_Color::Type color_type; ++ FX_ARGB argb; ++ }; ++ ++ explicit constexpr CFX_Color(FX_COLORREF ref) + : CFX_Color(FXARGB_R(ref), FXARGB_G(ref), FXARGB_B(ref)) {} + +- CFX_Color(int32_t type = CFX_Color::kTransparent, +- float color1 = 0.0f, +- float color2 = 0.0f, +- float color3 = 0.0f, +- float color4 = 0.0f) ++ constexpr CFX_Color(Type type = CFX_Color::Type::kTransparent, ++ float color1 = 0.0f, ++ float color2 = 0.0f, ++ float color3 = 0.0f, ++ float color4 = 0.0f) + : nColorType(type), + fColor1(color1), + fColor2(color2), + fColor3(color3), + fColor4(color4) {} + +- CFX_Color(int32_t r, int32_t g, int32_t b) +- : nColorType(CFX_Color::kRGB), ++ constexpr CFX_Color(int32_t r, int32_t g, int32_t b) ++ : nColorType(CFX_Color::Type::kRGB), + fColor1(r / 255.0f), + fColor2(g / 255.0f), + fColor3(b / 255.0f), + fColor4(0) {} + +- CFX_Color(const CFX_Color&) = default; ++ CFX_Color(const CFX_Color& that) = default; ++ CFX_Color& operator=(const CFX_Color& that) = default; + + CFX_Color operator/(float fColorDivide) const; + CFX_Color operator-(float fColorSub) const; + +- CFX_Color ConvertColorType(int32_t nConvertColorType) const; +- ++ CFX_Color ConvertColorType(Type nConvertColorType) const; + FX_COLORREF ToFXColor(int32_t nTransparency) const; + +- void Reset() { +- nColorType = CFX_Color::kTransparent; +- fColor1 = 0.0f; +- fColor2 = 0.0f; +- fColor3 = 0.0f; +- fColor4 = 0.0f; +- } +- +- int32_t nColorType; ++ Type nColorType; + float fColor1; + float fColor2; + float fColor3; +diff --git a/core/fxge/cfx_defaultrenderdevice.cpp b/core/fxge/cfx_defaultrenderdevice.cpp +new file mode 100644 +index 000000000..819be4ed5 +--- /dev/null ++++ b/core/fxge/cfx_defaultrenderdevice.cpp +@@ -0,0 +1,84 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxge/cfx_defaultrenderdevice.h" ++ ++#include ++ ++#include "core/fxge/dib/cfx_dibitmap.h" ++ ++namespace { ++ ++// When build variant is Skia then it is assumed as the default, but might be ++// overridden at runtime. ++#if defined(_SKIA_SUPPORT_) ++CFX_DefaultRenderDevice::RendererType g_default_renderer_type = ++ CFX_DefaultRenderDevice::RendererType::kSkia; ++#endif ++ ++} // namespace ++ ++// static ++bool CFX_DefaultRenderDevice::SkiaIsDefaultRenderer() { ++#if defined(_SKIA_SUPPORT_) ++ return g_default_renderer_type == RendererType::kSkia; ++#else ++ return false; ++#endif ++} ++ ++#if defined(_SKIA_SUPPORT_) ++// static ++void CFX_DefaultRenderDevice::SetDefaultRenderer(RendererType renderer_type) { ++ g_default_renderer_type = renderer_type; ++} ++#endif ++ ++CFX_DefaultRenderDevice::CFX_DefaultRenderDevice() = default; ++ ++CFX_DefaultRenderDevice::~CFX_DefaultRenderDevice() = default; ++ ++bool CFX_DefaultRenderDevice::Attach(RetainPtr pBitmap) { ++ return AttachWithRgbByteOrder(std::move(pBitmap), false); ++} ++ ++bool CFX_DefaultRenderDevice::AttachWithRgbByteOrder( ++ RetainPtr pBitmap, ++ bool bRgbByteOrder) { ++ return AttachImpl(std::move(pBitmap), bRgbByteOrder, nullptr, false); ++} ++ ++bool CFX_DefaultRenderDevice::AttachWithBackdropAndGroupKnockout( ++ RetainPtr pBitmap, ++ RetainPtr pBackdropBitmap, ++ bool bGroupKnockout) { ++ return AttachImpl(std::move(pBitmap), false, std::move(pBackdropBitmap), ++ bGroupKnockout); ++} ++ ++bool CFX_DefaultRenderDevice::CFX_DefaultRenderDevice::AttachImpl( ++ RetainPtr pBitmap, ++ bool bRgbByteOrder, ++ RetainPtr pBackdropBitmap, ++ bool bGroupKnockout) { ++#if defined(_SKIA_SUPPORT_) ++ if (SkiaIsDefaultRenderer()) { ++ return AttachSkiaImpl(std::move(pBitmap), bRgbByteOrder, ++ std::move(pBackdropBitmap), bGroupKnockout); ++ } ++#endif ++ return AttachAggImpl(std::move(pBitmap), bRgbByteOrder, ++ std::move(pBackdropBitmap), bGroupKnockout); ++} ++ ++bool CFX_DefaultRenderDevice::Create(int width, ++ int height, ++ FXDIB_Format format, ++ RetainPtr pBackdropBitmap) { ++#if defined(_SKIA_SUPPORT_) ++ if (SkiaIsDefaultRenderer()) ++ return CreateSkia(width, height, format, pBackdropBitmap); ++#endif ++ return CreateAgg(width, height, format, pBackdropBitmap); ++} +diff --git a/core/fxge/cfx_defaultrenderdevice.h b/core/fxge/cfx_defaultrenderdevice.h +index d001775b1..f9c20fe99 100644 +--- a/core/fxge/cfx_defaultrenderdevice.h ++++ b/core/fxge/cfx_defaultrenderdevice.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,30 +7,36 @@ + #ifndef CORE_FXGE_CFX_DEFAULTRENDERDEVICE_H_ + #define CORE_FXGE_CFX_DEFAULTRENDERDEVICE_H_ + ++#include ++ ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxge/cfx_renderdevice.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" + + class SkPictureRecorder; ++struct SkRect; + + class CFX_DefaultRenderDevice final : public CFX_RenderDevice { + public: + CFX_DefaultRenderDevice(); + ~CFX_DefaultRenderDevice() override; + +- bool Attach(const RetainPtr& pBitmap, +- bool bRgbByteOrder, +- const RetainPtr& pBackdropBitmap, +- bool bGroupKnockout); ++ bool Attach(RetainPtr pBitmap); ++ bool AttachWithRgbByteOrder(RetainPtr pBitmap, ++ bool bRgbByteOrder); ++ bool AttachWithBackdropAndGroupKnockout( ++ RetainPtr pBitmap, ++ RetainPtr pBackdropBitmap, ++ bool bGroupKnockout); + bool Create(int width, + int height, + FXDIB_Format format, +- const RetainPtr& pBackdropBitmap); ++ RetainPtr pBackdropBitmap); + +-#ifdef _SKIA_SUPPORT_ ++#if defined(_SKIA_SUPPORT_) + bool AttachRecorder(SkPictureRecorder* recorder); + void Clear(uint32_t color); +- SkPictureRecorder* CreateRecorder(int size_x, int size_y); +- void DebugVerifyBitmapIsPreMultiplied() const override; ++ std::unique_ptr CreateRecorder(const SkRect& bounds); + bool SetBitsWithMask(const RetainPtr& pBitmap, + const RetainPtr& pMask, + int left, +@@ -38,6 +44,51 @@ class CFX_DefaultRenderDevice final : public CFX_RenderDevice { + int bitmap_alpha, + BlendMode blend_type) override; + #endif ++ ++ // Runtime check to see if Skia is the renderer variant in use. ++ static bool SkiaIsDefaultRenderer(); ++ ++#if defined(_SKIA_SUPPORT_) ++ // This internal definition of renderer types must stay updated with respect ++ // to the public definition of `FPDF_RENDERER_TYPE`, so that all public ++ // definition values can be mapped to a value in ++ // `CFX_DefaultRenderDevice::RendererType`. ++ enum class RendererType { ++ kAgg = 0, ++ kSkia = 1, ++ }; ++ ++ // Update default renderer. ++ static void SetDefaultRenderer(RendererType renderer_type); ++#endif // defined(_SKIA_SUPPORT_) ++ ++ private: ++ bool AttachImpl(RetainPtr pBitmap, ++ bool bRgbByteOrder, ++ RetainPtr pBackdropBitmap, ++ bool bGroupKnockout); ++ ++ bool AttachAggImpl(RetainPtr pBitmap, ++ bool bRgbByteOrder, ++ RetainPtr pBackdropBitmap, ++ bool bGroupKnockout); ++ ++ bool CreateAgg(int width, ++ int height, ++ FXDIB_Format format, ++ RetainPtr pBackdropBitmap); ++ ++#if defined(_SKIA_SUPPORT_) ++ bool AttachSkiaImpl(RetainPtr pBitmap, ++ bool bRgbByteOrder, ++ RetainPtr pBackdropBitmap, ++ bool bGroupKnockout); ++ ++ bool CreateSkia(int width, ++ int height, ++ FXDIB_Format format, ++ RetainPtr pBackdropBitmap); ++#endif + }; + + #endif // CORE_FXGE_CFX_DEFAULTRENDERDEVICE_H_ +diff --git a/core/fxge/cfx_defaultrenderdevice_unittest.cpp b/core/fxge/cfx_defaultrenderdevice_unittest.cpp +new file mode 100644 +index 000000000..9304a8c6a +--- /dev/null ++++ b/core/fxge/cfx_defaultrenderdevice_unittest.cpp +@@ -0,0 +1,80 @@ ++// Copyright 2023 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxge/cfx_defaultrenderdevice.h" ++ ++#include "core/fxcrt/fx_coordinates.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxge/cfx_fillrenderoptions.h" ++#include "core/fxge/cfx_graphstatedata.h" ++#include "core/fxge/cfx_path.h" ++#include "core/fxge/dib/cfx_dibitmap.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++TEST(CFX_DefaultRenderDeviceTest, GetClipBox_Default) { ++ CFX_DefaultRenderDevice device; ++ ASSERT_TRUE(device.Create(/*width=*/16, /*height=*/16, FXDIB_Format::kArgb, ++ /*pBackdropBitmap=*/nullptr)); ++ ++ EXPECT_EQ(FX_RECT(0, 0, 16, 16), device.GetClipBox()); ++} ++ ++TEST(CFX_DefaultRenderDeviceTest, GetClipBox_PathFill) { ++ // Matrix that transposes and translates by 1 unit on each axis. ++ const CFX_Matrix object_to_device(0, 1, 1, 0, 1, -1); ++ ++ // Fill type cannot be none. ++ const CFX_FillRenderOptions fill_options( ++ CFX_FillRenderOptions::FillType::kEvenOdd); ++ ++ CFX_DefaultRenderDevice device; ++ ASSERT_TRUE(device.Create(/*width=*/16, /*height=*/16, FXDIB_Format::kArgb, ++ /*pBackdropBitmap=*/nullptr)); ++ ++ CFX_Path path; ++ path.AppendRect(2, 4, 14, 12); ++ EXPECT_TRUE(device.SetClip_PathFill(path, &object_to_device, fill_options)); ++ ++ EXPECT_EQ(FX_RECT(5, 1, 13, 13), device.GetClipBox()); ++} ++ ++TEST(CFX_DefaultRenderDeviceTest, GetClipBox_PathStroke) { ++ // Matrix that transposes and translates by 1 unit on each axis. ++ const CFX_Matrix object_to_device(0, 1, 1, 0, 1, -1); ++ ++ // Default line width is 1. ++ const CFX_GraphStateData graphics_state; ++ ++ CFX_DefaultRenderDevice device; ++ ASSERT_TRUE(device.Create(/*width=*/16, /*height=*/16, FXDIB_Format::kArgb, ++ /*pBackdropBitmap=*/nullptr)); ++ ++ CFX_Path path; ++ path.AppendRect(2, 4, 14, 12); ++ EXPECT_TRUE( ++ device.SetClip_PathStroke(path, &object_to_device, &graphics_state)); ++ ++ EXPECT_EQ(FX_RECT(4, 0, 14, 14), device.GetClipBox()); ++} ++ ++TEST(CFX_DefaultRenderDeviceTest, GetClipBox_Rect) { ++ CFX_DefaultRenderDevice device; ++ ASSERT_TRUE(device.Create(/*width=*/16, /*height=*/16, FXDIB_Format::kArgb, ++ /*pBackdropBitmap=*/nullptr)); ++ ++ EXPECT_TRUE(device.SetClip_Rect({2, 4, 14, 12})); ++ ++ EXPECT_EQ(FX_RECT(2, 4, 14, 12), device.GetClipBox()); ++} ++ ++TEST(CFX_DefaultRenderDeviceTest, GetClipBox_Empty) { ++ CFX_DefaultRenderDevice device; ++ ASSERT_TRUE(device.Create(/*width=*/16, /*height=*/16, FXDIB_Format::kArgb, ++ /*pBackdropBitmap=*/nullptr)); ++ ++ EXPECT_TRUE(device.SetClip_Rect({2, 8, 14, 8})); ++ ++ EXPECT_TRUE(device.GetClipBox().IsEmpty()); ++} +diff --git a/core/fxge/cfx_drawutils.cpp b/core/fxge/cfx_drawutils.cpp +new file mode 100644 +index 000000000..359147122 +--- /dev/null ++++ b/core/fxge/cfx_drawutils.cpp +@@ -0,0 +1,41 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxge/cfx_drawutils.h" ++ ++#include "core/fxge/cfx_fillrenderoptions.h" ++#include "core/fxge/cfx_graphstatedata.h" ++#include "core/fxge/cfx_path.h" ++#include "core/fxge/cfx_renderdevice.h" ++#include "third_party/base/check.h" ++ ++// static ++void CFX_DrawUtils::DrawFocusRect(CFX_RenderDevice* render_device, ++ const CFX_Matrix& user_to_device, ++ const CFX_FloatRect& view_bounding_box) { ++ DCHECK(render_device); ++ CFX_Path path; ++ path.AppendPoint(CFX_PointF(view_bounding_box.left, view_bounding_box.top), ++ CFX_Path::Point::Type::kMove); ++ path.AppendPoint(CFX_PointF(view_bounding_box.left, view_bounding_box.bottom), ++ CFX_Path::Point::Type::kLine); ++ path.AppendPoint( ++ CFX_PointF(view_bounding_box.right, view_bounding_box.bottom), ++ CFX_Path::Point::Type::kLine); ++ path.AppendPoint(CFX_PointF(view_bounding_box.right, view_bounding_box.top), ++ CFX_Path::Point::Type::kLine); ++ path.AppendPoint(CFX_PointF(view_bounding_box.left, view_bounding_box.top), ++ CFX_Path::Point::Type::kLine); ++ ++ CFX_GraphStateData graph_state_data; ++ graph_state_data.m_DashArray = {1.0f}; ++ graph_state_data.m_DashPhase = 0; ++ graph_state_data.m_LineWidth = 1.0f; ++ ++ render_device->DrawPath(path, &user_to_device, &graph_state_data, 0, ++ ArgbEncode(255, 0, 0, 0), ++ CFX_FillRenderOptions::EvenOddOptions()); ++} +diff --git a/core/fxge/cfx_drawutils.h b/core/fxge/cfx_drawutils.h +new file mode 100644 +index 000000000..5e4cd9edc +--- /dev/null ++++ b/core/fxge/cfx_drawutils.h +@@ -0,0 +1,24 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXGE_CFX_DRAWUTILS_H_ ++#define CORE_FXGE_CFX_DRAWUTILS_H_ ++ ++class CFX_FloatRect; ++class CFX_Matrix; ++class CFX_RenderDevice; ++ ++class CFX_DrawUtils { ++ public: ++ CFX_DrawUtils() = delete; ++ CFX_DrawUtils(const CFX_DrawUtils&) = delete; ++ CFX_DrawUtils& operator=(const CFX_DrawUtils&) = delete; ++ ++ static void DrawFocusRect(CFX_RenderDevice* render_device, ++ const CFX_Matrix& user_to_device, ++ const CFX_FloatRect& view_bounding_box); ++}; ++#endif // CORE_FXGE_CFX_DRAWUTILS_H_ +diff --git a/core/fxge/cfx_face.cpp b/core/fxge/cfx_face.cpp +index 6a4137627..90b11fe7e 100644 +--- a/core/fxge/cfx_face.cpp ++++ b/core/fxge/cfx_face.cpp +@@ -1,22 +1,27 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxge/cfx_face.h" + +-#include "third_party/base/ptr_util.h" ++#include ++ ++#include "third_party/base/check.h" ++#include "third_party/base/numerics/safe_conversions.h" + + // static + RetainPtr CFX_Face::New(FT_Library library, +- const RetainPtr& pDesc, ++ RetainPtr pDesc, + pdfium::span data, + FT_Long face_index) { + FXFT_FaceRec* pRec = nullptr; +- if (FT_New_Memory_Face(library, data.data(), data.size(), face_index, +- &pRec) != 0) { ++ if (FT_New_Memory_Face(library, data.data(), ++ pdfium::base::checked_cast(data.size()), ++ face_index, &pRec) != 0) { + return nullptr; + } +- return pdfium::WrapRetain(new CFX_Face(pRec, pDesc)); ++ // Private ctor. ++ return pdfium::WrapRetain(new CFX_Face(pRec, std::move(pDesc))); + } + + // static +@@ -27,12 +32,13 @@ RetainPtr CFX_Face::Open(FT_Library library, + if (FT_Open_Face(library, args, face_index, &pRec) != 0) + return nullptr; + ++ // Private ctor. + return pdfium::WrapRetain(new CFX_Face(pRec, nullptr)); + } + +-CFX_Face::CFX_Face(FXFT_FaceRec* rec, const RetainPtr& pDesc) +- : m_pRec(rec), m_pDesc(pDesc) { +- ASSERT(m_pRec); ++CFX_Face::CFX_Face(FXFT_FaceRec* rec, RetainPtr pDesc) ++ : m_pRec(rec), m_pDesc(std::move(pDesc)) { ++ DCHECK(m_pRec); + } + + CFX_Face::~CFX_Face() = default; +diff --git a/core/fxge/cfx_face.h b/core/fxge/cfx_face.h +index 241204310..6f1ee5a58 100644 +--- a/core/fxge/cfx_face.h ++++ b/core/fxge/cfx_face.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,13 +7,13 @@ + + #include "core/fxcrt/observed_ptr.h" + #include "core/fxcrt/retain_ptr.h" +-#include "core/fxge/fx_freetype.h" ++#include "core/fxge/freetype/fx_freetype.h" + #include "third_party/base/span.h" + +-class CFX_Face : public Retainable, public Observable { ++class CFX_Face final : public Retainable, public Observable { + public: + static RetainPtr New(FT_Library library, +- const RetainPtr& pDesc, ++ RetainPtr pDesc, + pdfium::span data, + FT_Long face_index); + +@@ -26,7 +26,7 @@ class CFX_Face : public Retainable, public Observable { + FXFT_FaceRec* GetRec() { return m_pRec.get(); } + + private: +- CFX_Face(FXFT_FaceRec* pRec, const RetainPtr& pDesc); ++ CFX_Face(FXFT_FaceRec* pRec, RetainPtr pDesc); + + ScopedFXFTFaceRec const m_pRec; + RetainPtr const m_pDesc; +diff --git a/core/fxge/cfx_fillrenderoptions.h b/core/fxge/cfx_fillrenderoptions.h +new file mode 100644 +index 000000000..d123f724d +--- /dev/null ++++ b/core/fxge/cfx_fillrenderoptions.h +@@ -0,0 +1,91 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXGE_CFX_FILLRENDEROPTIONS_H_ ++#define CORE_FXGE_CFX_FILLRENDEROPTIONS_H_ ++ ++#include ++ ++// Represents the options for filling paths. ++struct CFX_FillRenderOptions { ++ // FillType defines how path is filled. ++ enum class FillType : uint8_t { ++ // No filling needed. ++ kNoFill = 0, ++ ++ // Use even-odd or inverse even-odd algorithms to decide if the area needs ++ // to be filled. ++ kEvenOdd = 1, ++ ++ // Use winding or inverse winding algorithms to decide whether the area ++ // needs to be filled. ++ kWinding = 2, ++ }; ++ ++ static constexpr CFX_FillRenderOptions EvenOddOptions() { ++ return CFX_FillRenderOptions(FillType::kEvenOdd); ++ } ++ static constexpr CFX_FillRenderOptions WindingOptions() { ++ return CFX_FillRenderOptions(FillType::kWinding); ++ } ++ ++ constexpr CFX_FillRenderOptions() ++ : CFX_FillRenderOptions(FillType::kNoFill) {} ++ ++ // TODO(thestig): Switch to default member initializer for bit-fields when ++ // C++20 is available. ++ constexpr explicit CFX_FillRenderOptions(FillType fill_type) ++ : fill_type(fill_type), ++ adjust_stroke(false), ++ aliased_path(false), ++ full_cover(false), ++ rect_aa(false), ++ stroke(false), ++ stroke_text_mode(false), ++ text_mode(false), ++ zero_area(false) {} ++ ++ bool operator==(const CFX_FillRenderOptions& other) const { ++ return fill_type == other.fill_type && ++ adjust_stroke == other.adjust_stroke && ++ aliased_path == other.aliased_path && ++ full_cover == other.full_cover && rect_aa == other.rect_aa && ++ stroke == other.stroke && ++ stroke_text_mode == other.stroke_text_mode && ++ text_mode == other.text_mode && zero_area == other.zero_area; ++ } ++ ++ bool operator!=(const CFX_FillRenderOptions& other) const { ++ return !(*this == other); ++ } ++ ++ // Fill type. ++ FillType fill_type; ++ ++ // Adjusted stroke rendering is enabled. ++ bool adjust_stroke : 1; ++ ++ // Whether anti aliasing is enabled for path rendering. ++ bool aliased_path : 1; ++ ++ // Fills with the sum of colors from both cover and source. ++ bool full_cover : 1; ++ ++ // Rect paths use anti-aliasing. ++ bool rect_aa : 1; ++ ++ // Path is stroke. ++ bool stroke : 1; ++ ++ // Renders text by filling strokes. ++ bool stroke_text_mode : 1; ++ ++ // Path is text. ++ bool text_mode : 1; ++ ++ // Path encloses zero area. ++ bool zero_area : 1; ++}; ++ ++#endif // CORE_FXGE_CFX_FILLRENDEROPTIONS_H_ +diff --git a/core/fxge/cfx_folderfontinfo.cpp b/core/fxge/cfx_folderfontinfo.cpp +index c93b1f0dd..af8691a3a 100644 +--- a/core/fxge/cfx_folderfontinfo.cpp ++++ b/core/fxge/cfx_folderfontinfo.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,25 +6,20 @@ + + #include "core/fxge/cfx_folderfontinfo.h" + ++#include + #include + #include + + #include "build/build_config.h" + #include "core/fxcrt/fx_codepage.h" ++#include "core/fxcrt/fx_extension.h" ++#include "core/fxcrt/fx_folder.h" + #include "core/fxcrt/fx_memory_wrappers.h" + #include "core/fxcrt/fx_safe_types.h" +-#include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/fx_system.h" + #include "core/fxge/cfx_fontmapper.h" + #include "core/fxge/fx_font.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" +- +-#define CHARSET_FLAG_ANSI (1 << 0) +-#define CHARSET_FLAG_SYMBOL (1 << 1) +-#define CHARSET_FLAG_SHIFTJIS (1 << 2) +-#define CHARSET_FLAG_BIG5 (1 << 3) +-#define CHARSET_FLAG_GB (1 << 4) +-#define CHARSET_FLAG_KOREAN (1 << 5) ++#include "third_party/base/containers/contains.h" + + namespace { + +@@ -54,6 +49,25 @@ struct FxFileCloser { + } + }; + ++bool FindFamilyNameMatch(ByteStringView family_name, ++ const ByteString& installed_font_name) { ++ absl::optional result = installed_font_name.Find(family_name, 0); ++ if (!result.has_value()) ++ return false; ++ ++ size_t next_index = result.value() + family_name.GetLength(); ++ // Rule out the case that |family_name| is a substring of ++ // |installed_font_name| but their family names are actually different words. ++ // For example: "Univers" and "Universal" are not a match because they have ++ // different family names, but "Univers" and "Univers Bold" are a match. ++ if (installed_font_name.IsValidIndex(next_index) && ++ FXSYS_IsLowerASCII(installed_font_name[next_index])) { ++ return false; ++ } ++ ++ return true; ++} ++ + ByteString ReadStringFromFile(FILE* pFile, uint32_t size) { + ByteString result; + { +@@ -70,14 +84,15 @@ ByteString LoadTableFromTT(FILE* pFile, + const uint8_t* pTables, + uint32_t nTables, + uint32_t tag, +- uint32_t fileSize) { ++ FX_FILESIZE fileSize) { + for (uint32_t i = 0; i < nTables; i++) { + const uint8_t* p = pTables + i * 16; +- if (GET_TT_LONG(p) == tag) { +- uint32_t offset = GET_TT_LONG(p + 8); +- uint32_t size = GET_TT_LONG(p + 12); ++ if (FXSYS_UINT32_GET_MSBFIRST(p) == tag) { ++ uint32_t offset = FXSYS_UINT32_GET_MSBFIRST(p + 8); ++ uint32_t size = FXSYS_UINT32_GET_MSBFIRST(p + 12); + if (offset > std::numeric_limits::max() - size || +- offset + size > fileSize || fseek(pFile, offset, SEEK_SET) < 0) { ++ static_cast(offset + size) > fileSize || ++ fseek(pFile, offset, SEEK_SET) < 0) { + return ByteString(); + } + return ReadStringFromFile(pFile, size); +@@ -86,19 +101,19 @@ ByteString LoadTableFromTT(FILE* pFile, + return ByteString(); + } + +-uint32_t GetCharset(int charset) { ++uint32_t GetCharset(FX_Charset charset) { + switch (charset) { +- case FX_CHARSET_ShiftJIS: ++ case FX_Charset::kShiftJIS: + return CHARSET_FLAG_SHIFTJIS; +- case FX_CHARSET_ChineseSimplified: ++ case FX_Charset::kChineseSimplified: + return CHARSET_FLAG_GB; +- case FX_CHARSET_ChineseTraditional: ++ case FX_Charset::kChineseTraditional: + return CHARSET_FLAG_BIG5; +- case FX_CHARSET_Hangul: ++ case FX_Charset::kHangul: + return CHARSET_FLAG_KOREAN; +- case FX_CHARSET_Symbol: ++ case FX_Charset::kSymbol: + return CHARSET_FLAG_SYMBOL; +- case FX_CHARSET_ANSI: ++ case FX_Charset::kANSI: + return CHARSET_FLAG_ANSI; + default: + break; +@@ -109,8 +124,13 @@ uint32_t GetCharset(int charset) { + int32_t GetSimilarValue(int weight, + bool bItalic, + int pitch_family, +- uint32_t style) { ++ uint32_t style, ++ bool bMatchName, ++ size_t familyNameLength, ++ size_t bsNameLength) { + int32_t iSimilarValue = 0; ++ if (bMatchName && (familyNameLength == bsNameLength)) ++ iSimilarValue += 4; + if (FontStyleIsForceBold(style) == (weight > 400)) + iSimilarValue += 16; + if (FontStyleIsItalic(style) == bItalic) +@@ -142,14 +162,13 @@ bool CFX_FolderFontInfo::EnumFontList(CFX_FontMapper* pMapper) { + } + + void CFX_FolderFontInfo::ScanPath(const ByteString& path) { +- std::unique_ptr handle( +- FX_OpenFolder(path.c_str())); ++ std::unique_ptr handle = FX_Folder::OpenFolder(path); + if (!handle) + return; + + ByteString filename; + bool bFolder; +- while (FX_GetNextFile(handle.get(), &filename, &bFolder)) { ++ while (handle->GetNextFile(&filename, &bFolder)) { + if (bFolder) { + if (filename == "." || filename == "..") + continue; +@@ -161,7 +180,7 @@ void CFX_FolderFontInfo::ScanPath(const ByteString& path) { + } + + ByteString fullpath = path; +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + fullpath += "\\"; + #else + fullpath += "/"; +@@ -179,7 +198,7 @@ void CFX_FolderFontInfo::ScanFile(const ByteString& path) { + + fseek(pFile.get(), 0, SEEK_END); + +- uint32_t filesize = ftell(pFile.get()); ++ FX_FILESIZE filesize = ftell(pFile.get()); + uint8_t buffer[16]; + fseek(pFile.get(), 0, SEEK_SET); + +@@ -187,12 +206,12 @@ void CFX_FolderFontInfo::ScanFile(const ByteString& path) { + if (readCnt != 1) + return; + +- if (GET_TT_LONG(buffer) != kTableTTCF) { ++ if (FXSYS_UINT32_GET_MSBFIRST(buffer) != kTableTTCF) { + ReportFace(path, pFile.get(), filesize, 0); + return; + } + +- uint32_t nFaces = GET_TT_LONG(buffer + 8); ++ uint32_t nFaces = FXSYS_UINT32_GET_MSBFIRST(buffer + 8); + FX_SAFE_SIZE_T safe_face_bytes = nFaces; + safe_face_bytes *= 4; + if (!safe_face_bytes.IsValid()) +@@ -206,25 +225,29 @@ void CFX_FolderFontInfo::ScanFile(const ByteString& path) { + return; + + auto offsets_span = pdfium::make_span(offsets.get(), face_bytes); +- for (uint32_t i = 0; i < nFaces; i++) +- ReportFace(path, pFile.get(), filesize, GET_TT_LONG(&offsets_span[i * 4])); ++ for (uint32_t i = 0; i < nFaces; i++) { ++ ReportFace(path, pFile.get(), filesize, ++ FXSYS_UINT32_GET_MSBFIRST(&offsets_span[i * 4])); ++ } + } + + void CFX_FolderFontInfo::ReportFace(const ByteString& path, + FILE* pFile, +- uint32_t filesize, ++ FX_FILESIZE filesize, + uint32_t offset) { + char buffer[16]; + if (fseek(pFile, offset, SEEK_SET) < 0 || !fread(buffer, 12, 1, pFile)) + return; + +- uint32_t nTables = GET_TT_SHORT(buffer + 4); ++ uint32_t nTables = FXSYS_UINT16_GET_MSBFIRST(buffer + 4); + ByteString tables = ReadStringFromFile(pFile, nTables * 16); + if (tables.IsEmpty()) + return; + ++ static constexpr uint32_t kNameTag = ++ CFX_FontMapper::MakeTag('n', 'a', 'm', 'e'); + ByteString names = +- LoadTableFromTT(pFile, tables.raw_str(), nTables, 0x6e616d65, filesize); ++ LoadTableFromTT(pFile, tables.raw_str(), nTables, kNameTag, filesize); + if (names.IsEmpty()) + return; + +@@ -236,38 +259,40 @@ void CFX_FolderFontInfo::ReportFace(const ByteString& path, + if (style != "Regular") + facename += " " + style; + +- if (pdfium::ContainsKey(m_FontList, facename)) ++ if (pdfium::Contains(m_FontList, facename)) + return; + +- auto pInfo = pdfium::MakeUnique(path, facename, tables, offset, +- filesize); ++ auto pInfo = ++ std::make_unique(path, facename, tables, offset, filesize); ++ static constexpr uint32_t kOs2Tag = ++ CFX_FontMapper::MakeTag('O', 'S', '/', '2'); + ByteString os2 = +- LoadTableFromTT(pFile, tables.raw_str(), nTables, 0x4f532f32, filesize); ++ LoadTableFromTT(pFile, tables.raw_str(), nTables, kOs2Tag, filesize); + if (os2.GetLength() >= 86) { + const uint8_t* p = os2.raw_str() + 78; +- uint32_t codepages = GET_TT_LONG(p); ++ uint32_t codepages = FXSYS_UINT32_GET_MSBFIRST(p); + if (codepages & (1U << 17)) { +- m_pMapper->AddInstalledFont(facename, FX_CHARSET_ShiftJIS); ++ m_pMapper->AddInstalledFont(facename, FX_Charset::kShiftJIS); + pInfo->m_Charsets |= CHARSET_FLAG_SHIFTJIS; + } + if (codepages & (1U << 18)) { +- m_pMapper->AddInstalledFont(facename, FX_CHARSET_ChineseSimplified); ++ m_pMapper->AddInstalledFont(facename, FX_Charset::kChineseSimplified); + pInfo->m_Charsets |= CHARSET_FLAG_GB; + } + if (codepages & (1U << 20)) { +- m_pMapper->AddInstalledFont(facename, FX_CHARSET_ChineseTraditional); ++ m_pMapper->AddInstalledFont(facename, FX_Charset::kChineseTraditional); + pInfo->m_Charsets |= CHARSET_FLAG_BIG5; + } + if ((codepages & (1U << 19)) || (codepages & (1U << 21))) { +- m_pMapper->AddInstalledFont(facename, FX_CHARSET_Hangul); ++ m_pMapper->AddInstalledFont(facename, FX_Charset::kHangul); + pInfo->m_Charsets |= CHARSET_FLAG_KOREAN; + } + if (codepages & (1U << 31)) { +- m_pMapper->AddInstalledFont(facename, FX_CHARSET_Symbol); ++ m_pMapper->AddInstalledFont(facename, FX_Charset::kSymbol); + pInfo->m_Charsets |= CHARSET_FLAG_SYMBOL; + } + } +- m_pMapper->AddInstalledFont(facename, FX_CHARSET_ANSI); ++ m_pMapper->AddInstalledFont(facename, FX_Charset::kANSI); + pInfo->m_Charsets |= CHARSET_FLAG_ANSI; + pInfo->m_Styles = 0; + if (style.Contains("Bold")) +@@ -281,8 +306,7 @@ void CFX_FolderFontInfo::ReportFace(const ByteString& path, + } + + void* CFX_FolderFontInfo::GetSubstFont(const ByteString& face) { +- for (size_t iBaseFont = 0; iBaseFont < FX_ArraySize(Base14Substs); +- iBaseFont++) { ++ for (size_t iBaseFont = 0; iBaseFont < std::size(Base14Substs); iBaseFont++) { + if (face == Base14Substs[iBaseFont].m_pName) + return GetFont(Base14Substs[iBaseFont].m_pSubstName); + } +@@ -291,52 +315,62 @@ void* CFX_FolderFontInfo::GetSubstFont(const ByteString& face) { + + void* CFX_FolderFontInfo::FindFont(int weight, + bool bItalic, +- int charset, ++ FX_Charset charset, + int pitch_family, +- const char* family, ++ const ByteString& family, + bool bMatchName) { + FontFaceInfo* pFind = nullptr; +- if (charset == FX_CHARSET_ANSI && FontFamilyIsFixedPitch(pitch_family)) +- return GetFont("Courier New"); + +- ByteStringView bsFamily(family); ++ ByteStringView bsFamily = family.AsStringView(); + uint32_t charset_flag = GetCharset(charset); + int32_t iBestSimilar = 0; + for (const auto& it : m_FontList) { + const ByteString& bsName = it.first; + FontFaceInfo* pFont = it.second.get(); +- if (!(pFont->m_Charsets & charset_flag) && charset != FX_CHARSET_Default) ++ if (!(pFont->m_Charsets & charset_flag) && charset != FX_Charset::kDefault) + continue; + +- if (bMatchName && !bsName.Contains(bsFamily)) ++ if (bMatchName && !FindFamilyNameMatch(bsFamily, bsName)) + continue; + + int32_t iSimilarValue = +- GetSimilarValue(weight, bItalic, pitch_family, pFont->m_Styles); ++ GetSimilarValue(weight, bItalic, pitch_family, pFont->m_Styles, ++ bMatchName, bsFamily.GetLength(), bsName.GetLength()); + if (iSimilarValue > iBestSimilar) { + iBestSimilar = iSimilarValue; + pFind = pFont; + } + } +- return pFind; ++ ++ if (pFind) { ++ return pFind; ++ } ++ ++ if (charset == FX_Charset::kANSI && FontFamilyIsFixedPitch(pitch_family)) { ++ auto* courier_new = GetFont("Courier New"); ++ if (courier_new) ++ return courier_new; ++ } ++ ++ return nullptr; + } + + void* CFX_FolderFontInfo::MapFont(int weight, + bool bItalic, +- int charset, ++ FX_Charset charset, + int pitch_family, +- const char* family) { ++ const ByteString& face) { + return nullptr; + } + +-void* CFX_FolderFontInfo::GetFont(const char* face) { ++void* CFX_FolderFontInfo::GetFont(const ByteString& face) { + auto it = m_FontList.find(face); + return it != m_FontList.end() ? it->second.get() : nullptr; + } + +-uint32_t CFX_FolderFontInfo::GetFontData(void* hFont, +- uint32_t table, +- pdfium::span buffer) { ++size_t CFX_FolderFontInfo::GetFontData(void* hFont, ++ uint32_t table, ++ pdfium::span buffer) { + if (!hFont) + return 0; + +@@ -348,12 +382,12 @@ uint32_t CFX_FolderFontInfo::GetFontData(void* hFont, + } else if (table == kTableTTCF) { + datasize = pFont->m_FontOffset ? pFont->m_FileSize : 0; + } else { +- uint32_t nTables = pFont->m_FontTables.GetLength() / 16; +- for (uint32_t i = 0; i < nTables; i++) { ++ size_t nTables = pFont->m_FontTables.GetLength() / 16; ++ for (size_t i = 0; i < nTables; i++) { + const uint8_t* p = pFont->m_FontTables.raw_str() + i * 16; +- if (GET_TT_LONG(p) == table) { +- offset = GET_TT_LONG(p + 8); +- datasize = GET_TT_LONG(p + 12); ++ if (FXSYS_UINT32_GET_MSBFIRST(p) == table) { ++ offset = FXSYS_UINT32_GET_MSBFIRST(p + 8); ++ datasize = FXSYS_UINT32_GET_MSBFIRST(p + 12); + } + } + } +@@ -382,7 +416,7 @@ bool CFX_FolderFontInfo::GetFaceName(void* hFont, ByteString* name) { + return true; + } + +-bool CFX_FolderFontInfo::GetFontCharset(void* hFont, int* charset) { ++bool CFX_FolderFontInfo::GetFontCharset(void* hFont, FX_Charset* charset) { + return false; + } + +@@ -395,6 +429,4 @@ CFX_FolderFontInfo::FontFaceInfo::FontFaceInfo(ByteString filePath, + m_FaceName(faceName), + m_FontTables(fontTables), + m_FontOffset(fontOffset), +- m_FileSize(fileSize), +- m_Styles(0), +- m_Charsets(0) {} ++ m_FileSize(fileSize) {} +diff --git a/core/fxge/cfx_folderfontinfo.h b/core/fxge/cfx_folderfontinfo.h +index 4fd516d44..63d73c3a9 100644 +--- a/core/fxge/cfx_folderfontinfo.h ++++ b/core/fxge/cfx_folderfontinfo.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,10 +11,18 @@ + #include + #include + ++#include "core/fxcrt/fx_codepage_forward.h" + #include "core/fxcrt/unowned_ptr.h" + #include "core/fxge/cfx_fontmapper.h" + #include "core/fxge/systemfontinfo_iface.h" + ++#define CHARSET_FLAG_ANSI (1 << 0) ++#define CHARSET_FLAG_SYMBOL (1 << 1) ++#define CHARSET_FLAG_SHIFTJIS (1 << 2) ++#define CHARSET_FLAG_BIG5 (1 << 3) ++#define CHARSET_FLAG_GB (1 << 4) ++#define CHARSET_FLAG_KOREAN (1 << 5) ++ + class CFX_FolderFontInfo : public SystemFontInfoIface { + public: + CFX_FolderFontInfo(); +@@ -22,22 +30,24 @@ class CFX_FolderFontInfo : public SystemFontInfoIface { + + void AddPath(const ByteString& path); + +- // IFX_SytemFontInfo: ++ // SystemFontInfoIface: + bool EnumFontList(CFX_FontMapper* pMapper) override; + void* MapFont(int weight, + bool bItalic, +- int charset, ++ FX_Charset charset, + int pitch_family, +- const char* family) override; +- void* GetFont(const char* face) override; +- uint32_t GetFontData(void* hFont, +- uint32_t table, +- pdfium::span buffer) override; ++ const ByteString& face) override; ++ void* GetFont(const ByteString& face) override; ++ size_t GetFontData(void* hFont, ++ uint32_t table, ++ pdfium::span buffer) override; + void DeleteFont(void* hFont) override; + bool GetFaceName(void* hFont, ByteString* name) override; +- bool GetFontCharset(void* hFont, int* charset) override; ++ bool GetFontCharset(void* hFont, FX_Charset* charset) override; + + protected: ++ friend class CFX_FolderFontInfoTest; ++ + class FontFaceInfo { + public: + FontFaceInfo(ByteString filePath, +@@ -51,22 +61,22 @@ class CFX_FolderFontInfo : public SystemFontInfoIface { + const ByteString m_FontTables; + const uint32_t m_FontOffset; + const uint32_t m_FileSize; +- uint32_t m_Styles; +- uint32_t m_Charsets; ++ uint32_t m_Styles = 0; ++ uint32_t m_Charsets = 0; + }; + + void ScanPath(const ByteString& path); + void ScanFile(const ByteString& path); + void ReportFace(const ByteString& path, + FILE* pFile, +- uint32_t filesize, ++ FX_FILESIZE filesize, + uint32_t offset); + void* GetSubstFont(const ByteString& face); + void* FindFont(int weight, + bool bItalic, +- int charset, ++ FX_Charset charset, + int pitch_family, +- const char* family, ++ const ByteString& family, + bool bMatchName); + + std::map> m_FontList; +diff --git a/core/fxge/cfx_folderfontinfo_unittest.cpp b/core/fxge/cfx_folderfontinfo_unittest.cpp +new file mode 100644 +index 000000000..8cdb8b999 +--- /dev/null ++++ b/core/fxge/cfx_folderfontinfo_unittest.cpp +@@ -0,0 +1,135 @@ ++// Copyright 2019 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxge/cfx_folderfontinfo.h" ++ ++#include ++ ++#include "core/fxcrt/fx_codepage.h" ++#include "core/fxge/fx_font.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++namespace { ++ ++constexpr char kArial[] = "Arial"; ++constexpr char kCourierNew[] = "Courier New"; ++constexpr char kTimesNewRoman[] = "TimesNewRoman"; ++constexpr char kSymbol[] = "Symbol"; ++constexpr char kBookshelfSymbol7[] = "Bookshelf Symbol 7"; ++constexpr char kCalibri[] = "Calibri"; ++constexpr char kBookshelf[] = "Bookshelf"; ++constexpr char kBook[] = "Book"; ++constexpr char kTofuBold[] = "Tofu, Bold Italic"; ++constexpr char kTofu[] = "Tofu"; ++constexpr char kLatoUltraBold[] = "Lato Ultra-Bold"; ++constexpr char kLato[] = "Lato"; ++constexpr char kOxygenSansSansBold[] = "Oxygen-Sans Sans-Bold"; ++constexpr char kOxygenSans[] = "Oxygen-Sans"; ++constexpr char kOxygen[] = "Oxygen"; ++constexpr char kComicSansMS[] = "Comic Sans MS"; ++ ++} // namespace ++ ++class CFX_FolderFontInfoTest : public ::testing::Test { ++ public: ++ CFX_FolderFontInfoTest() { ++ AddDummyFont(kArial, CHARSET_FLAG_ANSI); ++ AddDummyFont(kCourierNew, CHARSET_FLAG_ANSI); ++ AddDummyFont(kTimesNewRoman, 0); ++ AddDummyFont(kBookshelfSymbol7, CHARSET_FLAG_SYMBOL); ++ AddDummyFont(kSymbol, CHARSET_FLAG_SYMBOL); ++ AddDummyFont(kTofuBold, CHARSET_FLAG_SYMBOL); ++ AddDummyFont(kLatoUltraBold, CHARSET_FLAG_ANSI); ++ AddDummyFont(kOxygenSansSansBold, CHARSET_FLAG_ANSI); ++ AddDummyFont(kComicSansMS, CHARSET_FLAG_ANSI); ++ } ++ ++ void* FindFont(int weight, ++ bool bItalic, ++ FX_Charset charset, ++ int pitch_family, ++ const char* family, ++ bool bMatchName) { ++ return font_info_.FindFont(weight, bItalic, charset, pitch_family, family, ++ bMatchName); ++ } ++ ++ ByteString GetFaceName(void* font) { ++ return static_cast(font)->m_FaceName; ++ } ++ ++ private: ++ void AddDummyFont(const char* font_name, uint32_t charsets) { ++ auto info = std::make_unique( ++ /*filePath=*/"", font_name, /*fontTables=*/"", ++ /*fontOffset=*/0, /*fileSize=*/0); ++ info->m_Charsets = charsets; ++ font_info_.m_FontList[font_name] = std::move(info); ++ } ++ ++ CFX_FolderFontInfo font_info_; ++}; ++ ++TEST_F(CFX_FolderFontInfoTest, TestFindFont) { ++ // Find "Symbol" font ++ void* font = FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kSymbol, ++ FXFONT_FF_ROMAN, kSymbol, /*bMatchName=*/true); ++ ASSERT_TRUE(font); ++ EXPECT_EQ(GetFaceName(font), kSymbol); ++ ++ // Find "Calibri" font that is not present in the installed fonts ++ EXPECT_FALSE(FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kSymbol, ++ FXFONT_FF_ROMAN, kCalibri, /*bMatchName=*/true)); ++ ++ // Find the closest matching font to "Bookshelf" font that is present in the ++ // installed fonts ++ font = FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kSymbol, ++ FXFONT_FF_ROMAN, kBookshelf, /*bMatchName=*/true); ++ ASSERT_TRUE(font); ++ EXPECT_EQ(GetFaceName(font), kBookshelfSymbol7); ++ ++ // Find "Book" font is expected to fail, because none of the installed fonts ++ // is in the same font family. ++ EXPECT_FALSE(FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kSymbol, ++ FXFONT_FF_ROMAN, kBook, /*bMatchName=*/true)); ++ ++ // Find the closest matching font for "Tofu" in the installed fonts, which ++ // has "," following the string "Tofu". ++ font = FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kSymbol, ++ FXFONT_FF_ROMAN, kTofu, /*bMatchName=*/true); ++ ASSERT_TRUE(font); ++ EXPECT_EQ(GetFaceName(font), kTofuBold); ++ ++ // Find the closest matching font for "Lato" in the installed fonts, which ++ // has a space character following the string "Lato". ++ font = FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kANSI, ++ FXFONT_FF_ROMAN, kLato, /*bMatchName=*/true); ++ ASSERT_TRUE(font); ++ EXPECT_EQ(GetFaceName(font), kLatoUltraBold); ++ ++ // Find the closest matching font for "Oxygen" in the installed fonts, ++ // which has "-" following the string "Oxygen". ++ font = FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kANSI, ++ FXFONT_FF_ROMAN, kOxygen, /*bMatchName=*/true); ++ ASSERT_TRUE(font); ++ EXPECT_EQ(GetFaceName(font), kOxygenSansSansBold); ++ ++ // Find the closest matching font for "Oxygen-Sans" in the installed fonts, ++ // to test matching a family name with "-". ++ font = FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kANSI, ++ FXFONT_FF_ROMAN, kOxygenSans, /*bMatchName=*/true); ++ ASSERT_TRUE(font); ++ EXPECT_EQ(GetFaceName(font), kOxygenSansSansBold); ++ ++ // Find "Symbol" font when name matching is false ++ font = FindFont(/*weight=*/0, /*bItalic=*/false, FX_Charset::kSymbol, ++ FXFONT_FF_ROMAN, kSymbol, /*bMatchName=*/false); ++ ASSERT_TRUE(font); ++ EXPECT_EQ(GetFaceName(font), kBookshelfSymbol7); ++ ++ font = FindFont(700, false, FX_Charset::kANSI, FXFONT_FF_FIXEDPITCH, ++ kComicSansMS, true); ++ ASSERT_TRUE(font); ++ EXPECT_EQ(GetFaceName(font), kComicSansMS); ++} +diff --git a/core/fxge/cfx_font.cpp b/core/fxge/cfx_font.cpp +index 5a51b6d2f..e9ca56b2a 100644 +--- a/core/fxge/cfx_font.cpp ++++ b/core/fxge/cfx_font.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,24 +6,30 @@ + + #include "core/fxge/cfx_font.h" + ++#include ++ + #include + #include + #include + #include +-#include + + #include "build/build_config.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_codepage.h" + #include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/unowned_ptr.h" + #include "core/fxge/cfx_fontcache.h" ++#include "core/fxge/cfx_fontmapper.h" + #include "core/fxge/cfx_fontmgr.h" + #include "core/fxge/cfx_gemodule.h" + #include "core/fxge/cfx_glyphcache.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/cfx_substfont.h" + #include "core/fxge/fx_font.h" + #include "core/fxge/scoped_font_transform.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/numerics/safe_conversions.h" ++#include "third_party/base/span.h" + + #define EM_ADJUST(em, a) (em == 0 ? (a) : (a)*1000 / em) + +@@ -33,14 +39,57 @@ constexpr int kThousandthMinInt = std::numeric_limits::min() / 1000; + constexpr int kThousandthMaxInt = std::numeric_limits::max() / 1000; + + struct OUTLINE_PARAMS { +- CFX_PathData* m_pPath; +- int m_CurX; +- int m_CurY; ++ UnownedPtr m_pPath; ++ FT_Pos m_CurX; ++ FT_Pos m_CurY; + float m_CoordUnit; + }; + +-#ifdef PDF_ENABLE_XFA ++// TODO(crbug.com/pdfium/1400): When FT_Done_MM_Var() is more likely to be ++// available to all users in the future, remove FreeMMVar() and use ++// FT_Done_MM_Var() directly. ++// ++// Use weak symbols to check if FT_Done_MM_Var() is available at runtime. ++#if !BUILDFLAG(IS_WIN) ++extern "C" __attribute__((weak)) decltype(FT_Done_MM_Var) FT_Done_MM_Var; ++#endif ++ ++void FreeMMVar(FXFT_FaceRec* rec, FXFT_MM_VarPtr variation_desc) { ++#if BUILDFLAG(IS_WIN) ++ // Assume `use_system_freetype` GN var is never set on Windows. ++ constexpr bool has_ft_done_mm_var_func = true; ++#else ++ static const bool has_ft_done_mm_var_func = !!FT_Done_MM_Var; ++#endif ++ if (has_ft_done_mm_var_func) { ++ FT_Done_MM_Var(CFX_GEModule::Get()->GetFontMgr()->GetFTLibrary(), ++ variation_desc); ++ } else { ++ FXFT_Free(rec, variation_desc); ++ } ++} + ++FX_RECT FXRectFromFTPos(FT_Pos left, FT_Pos top, FT_Pos right, FT_Pos bottom) { ++ return FX_RECT(pdfium::base::checked_cast(left), ++ pdfium::base::checked_cast(top), ++ pdfium::base::checked_cast(right), ++ pdfium::base::checked_cast(bottom)); ++} ++ ++FX_RECT ScaledFXRectFromFTPos(FT_Pos left, ++ FT_Pos top, ++ FT_Pos right, ++ FT_Pos bottom, ++ int x_scale, ++ int y_scale) { ++ if (x_scale == 0 || y_scale == 0) ++ return FXRectFromFTPos(left, top, right, bottom); ++ ++ return FXRectFromFTPos(left * 1000 / x_scale, top * 1000 / y_scale, ++ right * 1000 / x_scale, bottom * 1000 / y_scale); ++} ++ ++#ifdef PDF_ENABLE_XFA + unsigned long FTStreamRead(FXFT_StreamRec* stream, + unsigned long offset, + unsigned char* buffer, +@@ -50,52 +99,34 @@ unsigned long FTStreamRead(FXFT_StreamRec* stream, + + IFX_SeekableReadStream* pFile = + static_cast(stream->descriptor.pointer); +- return pFile && pFile->ReadBlockAtOffset(buffer, offset, count) ? count : 0; ++ return pFile && pFile->ReadBlockAtOffset({buffer, count}, offset) ? count : 0; + } + + void FTStreamClose(FXFT_StreamRec* stream) {} +- +-RetainPtr LoadFileImp(FXFT_LibraryRec* library, +- const RetainPtr& pFile, +- int32_t faceIndex, +- std::unique_ptr* stream) { +- auto stream1 = pdfium::MakeUnique(); +- stream1->base = nullptr; +- stream1->size = static_cast(pFile->GetSize()); +- stream1->pos = 0; +- stream1->descriptor.pointer = static_cast(pFile.Get()); +- stream1->close = FTStreamClose; +- stream1->read = FTStreamRead; +- +- FT_Open_Args args; +- args.flags = FT_OPEN_STREAM; +- args.stream = stream1.get(); +- +- RetainPtr face = CFX_Face::Open(library, &args, faceIndex); +- if (!face) +- return nullptr; +- +- *stream = std::move(stream1); +- return face; +-} + #endif // PDF_ENABLE_XFA + + void Outline_CheckEmptyContour(OUTLINE_PARAMS* param) { +- std::vector& points = param->m_pPath->GetPoints(); +- size_t size = points.size(); +- +- if (size >= 2 && points[size - 2].IsTypeAndOpen(FXPT_TYPE::MoveTo) && +- points[size - 2].m_Point == points[size - 1].m_Point) { +- size -= 2; +- } +- if (size >= 4 && points[size - 4].IsTypeAndOpen(FXPT_TYPE::MoveTo) && +- points[size - 3].IsTypeAndOpen(FXPT_TYPE::BezierTo) && +- points[size - 3].m_Point == points[size - 4].m_Point && +- points[size - 2].m_Point == points[size - 4].m_Point && +- points[size - 1].m_Point == points[size - 4].m_Point) { +- size -= 4; ++ size_t size; ++ { ++ pdfium::span points = param->m_pPath->GetPoints(); ++ size = points.size(); ++ ++ if (size >= 2 && ++ points[size - 2].IsTypeAndOpen(CFX_Path::Point::Type::kMove) && ++ points[size - 2].m_Point == points[size - 1].m_Point) { ++ size -= 2; ++ } ++ if (size >= 4 && ++ points[size - 4].IsTypeAndOpen(CFX_Path::Point::Type::kMove) && ++ points[size - 3].IsTypeAndOpen(CFX_Path::Point::Type::kBezier) && ++ points[size - 3].m_Point == points[size - 4].m_Point && ++ points[size - 2].m_Point == points[size - 4].m_Point && ++ points[size - 1].m_Point == points[size - 4].m_Point) { ++ size -= 4; ++ } + } +- points.resize(size); ++ // Only safe after |points| has been destroyed. ++ param->m_pPath->GetPoints().resize(size); + } + + int Outline_MoveTo(const FT_Vector* to, void* user) { +@@ -106,7 +137,7 @@ int Outline_MoveTo(const FT_Vector* to, void* user) { + param->m_pPath->ClosePath(); + param->m_pPath->AppendPoint( + CFX_PointF(to->x / param->m_CoordUnit, to->y / param->m_CoordUnit), +- FXPT_TYPE::MoveTo, false); ++ CFX_Path::Point::Type::kMove); + + param->m_CurX = to->x; + param->m_CurY = to->y; +@@ -118,7 +149,7 @@ int Outline_LineTo(const FT_Vector* to, void* user) { + + param->m_pPath->AppendPoint( + CFX_PointF(to->x / param->m_CoordUnit, to->y / param->m_CoordUnit), +- FXPT_TYPE::LineTo, false); ++ CFX_Path::Point::Type::kLine); + + param->m_CurX = to->x; + param->m_CurY = to->y; +@@ -133,16 +164,16 @@ int Outline_ConicTo(const FT_Vector* control, const FT_Vector* to, void* user) { + param->m_CoordUnit, + (param->m_CurY + (control->y - param->m_CurY) * 2 / 3) / + param->m_CoordUnit), +- FXPT_TYPE::BezierTo, false); ++ CFX_Path::Point::Type::kBezier); + + param->m_pPath->AppendPoint( + CFX_PointF((control->x + (to->x - control->x) / 3) / param->m_CoordUnit, + (control->y + (to->y - control->y) / 3) / param->m_CoordUnit), +- FXPT_TYPE::BezierTo, false); ++ CFX_Path::Point::Type::kBezier); + + param->m_pPath->AppendPoint( + CFX_PointF(to->x / param->m_CoordUnit, to->y / param->m_CoordUnit), +- FXPT_TYPE::BezierTo, false); ++ CFX_Path::Point::Type::kBezier); + + param->m_CurX = to->x; + param->m_CurY = to->y; +@@ -157,15 +188,15 @@ int Outline_CubicTo(const FT_Vector* control1, + + param->m_pPath->AppendPoint(CFX_PointF(control1->x / param->m_CoordUnit, + control1->y / param->m_CoordUnit), +- FXPT_TYPE::BezierTo, false); ++ CFX_Path::Point::Type::kBezier); + + param->m_pPath->AppendPoint(CFX_PointF(control2->x / param->m_CoordUnit, + control2->y / param->m_CoordUnit), +- FXPT_TYPE::BezierTo, false); ++ CFX_Path::Point::Type::kBezier); + + param->m_pPath->AppendPoint( + CFX_PointF(to->x / param->m_CoordUnit, to->y / param->m_CoordUnit), +- FXPT_TYPE::BezierTo, false); ++ CFX_Path::Point::Type::kBezier); + + param->m_CurX = to->x; + param->m_CurY = to->y; +@@ -176,23 +207,22 @@ bool ShouldAppendStyle(const ByteString& style) { + return !style.IsEmpty() && style != "Regular"; + } + +-} // namespace +- +-const char CFX_Font::s_AngleSkew[] = { +- 0, 2, 3, 5, 7, 9, 11, 12, 14, 16, 18, 19, 21, 23, 25, +- 27, 29, 31, 32, 34, 36, 38, 40, 42, 45, 47, 49, 51, 53, 55, ++constexpr int8_t kAngleSkew[] = { ++ -0, -2, -3, -5, -7, -9, -11, -12, -14, -16, -18, -19, -21, -23, -25, ++ -27, -29, -31, -32, -34, -36, -38, -40, -42, -45, -47, -49, -51, -53, -55, + }; + +-const uint8_t CFX_Font::s_WeightPow[] = { +- 0, 3, 6, 7, 8, 9, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, +- 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 35, 36, 36, 37, +- 37, 37, 38, 38, 38, 39, 39, 39, 40, 40, 40, 41, 41, 41, 42, 42, 42, +- 42, 43, 43, 43, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, +- 47, 47, 47, 48, 48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 50, +- 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, ++constexpr uint8_t kWeightPow[] = { ++ 0, 6, 12, 14, 16, 18, 22, 24, 28, 30, 32, 34, 36, 38, 40, ++ 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, ++ 70, 72, 72, 74, 74, 74, 76, 76, 76, 78, 78, 78, 80, 80, 80, ++ 82, 82, 82, 84, 84, 84, 84, 86, 86, 86, 88, 88, 88, 88, 90, ++ 90, 90, 90, 92, 92, 92, 92, 94, 94, 94, 94, 96, 96, 96, 96, ++ 96, 98, 98, 98, 98, 100, 100, 100, 100, 100, 102, 102, 102, 102, 102, ++ 104, 104, 104, 104, 104, 106, 106, 106, 106, 106, + }; + +-const uint8_t CFX_Font::s_WeightPow_11[] = { ++constexpr uint8_t kWeightPow11[] = { + 0, 4, 7, 8, 9, 10, 12, 13, 15, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 39, 39, 40, 40, 41, + 41, 41, 42, 42, 42, 43, 43, 43, 44, 44, 44, 45, 45, 45, 46, 46, 46, +@@ -201,28 +231,37 @@ const uint8_t CFX_Font::s_WeightPow_11[] = { + 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, + }; + +-const uint8_t CFX_Font::s_WeightPow_SHIFTJIS[] = { +- 0, 0, 1, 2, 3, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 21, +- 22, 24, 26, 28, 30, 32, 33, 35, 37, 39, 41, 43, 45, 48, 48, 48, 48, +- 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 52, 53, +- 53, 53, 53, 53, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 56, 56, 56, +- 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 58, +- 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 60, 60, ++constexpr uint8_t kWeightPowShiftJis[] = { ++ 0, 0, 2, 4, 6, 8, 10, 14, 16, 20, 22, 26, 28, 32, 34, ++ 38, 42, 44, 48, 52, 56, 60, 64, 66, 70, 74, 78, 82, 86, 90, ++ 96, 96, 96, 96, 98, 98, 98, 100, 100, 100, 100, 102, 102, 102, 102, ++ 104, 104, 104, 104, 104, 106, 106, 106, 106, 106, 108, 108, 108, 108, 108, ++ 110, 110, 110, 110, 110, 112, 112, 112, 112, 112, 112, 114, 114, 114, 114, ++ 114, 114, 114, 116, 116, 116, 116, 116, 116, 116, 118, 118, 118, 118, 118, ++ 118, 118, 120, 120, 120, 120, 120, 120, 120, 120, + }; + +-const CFX_Font::CharsetFontMap CFX_Font::defaultTTFMap[] = { +- {FX_CHARSET_ANSI, kDefaultAnsiFontName}, +- {FX_CHARSET_ChineseSimplified, "SimSun"}, +- {FX_CHARSET_ChineseTraditional, "MingLiU"}, +- {FX_CHARSET_ShiftJIS, "MS Gothic"}, +- {FX_CHARSET_Hangul, "Batang"}, +- {FX_CHARSET_MSWin_Cyrillic, "Arial"}, +-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_ || defined(OS_MACOSX) +- {FX_CHARSET_MSWin_EasternEuropean, "Arial"}, ++constexpr size_t kWeightPowArraySize = 100; ++static_assert(kWeightPowArraySize == std::size(kWeightPow), "Wrong size"); ++static_assert(kWeightPowArraySize == std::size(kWeightPow11), "Wrong size"); ++static_assert(kWeightPowArraySize == std::size(kWeightPowShiftJis), ++ "Wrong size"); ++ ++} // namespace ++ ++const CFX_Font::CharsetFontMap CFX_Font::kDefaultTTFMap[] = { ++ {static_cast(FX_Charset::kANSI), kDefaultAnsiFontName}, ++ {static_cast(FX_Charset::kChineseSimplified), "SimSun"}, ++ {static_cast(FX_Charset::kChineseTraditional), "MingLiU"}, ++ {static_cast(FX_Charset::kShiftJIS), "MS Gothic"}, ++ {static_cast(FX_Charset::kHangul), "Batang"}, ++ {static_cast(FX_Charset::kMSWin_Cyrillic), "Arial"}, ++#if BUILDFLAG(IS_WIN) ++ {static_cast(FX_Charset::kMSWin_EasternEuropean), "Tahoma"}, + #else +- {FX_CHARSET_MSWin_EasternEuropean, "Tahoma"}, ++ {static_cast(FX_Charset::kMSWin_EasternEuropean), "Arial"}, + #endif +- {FX_CHARSET_MSWin_Arabic, "Arial"}, ++ {static_cast(FX_Charset::kMSWin_Arabic), "Arial"}, + {-1, nullptr}}; + + // static +@@ -235,65 +274,63 @@ const char CFX_Font::kDefaultAnsiFontName[] = "Helvetica"; + const char CFX_Font::kUniversalDefaultFontName[] = "Arial Unicode MS"; + + // static +-ByteString CFX_Font::GetDefaultFontNameByCharset(uint8_t nCharset) { +- int i = 0; +- while (defaultTTFMap[i].charset != -1) { +- if (nCharset == static_cast(defaultTTFMap[i].charset)) +- return defaultTTFMap[i].fontname; +- ++i; ++ByteString CFX_Font::GetDefaultFontNameByCharset(FX_Charset nCharset) { ++ for (size_t i = 0; i < std::size(kDefaultTTFMap) - 1; ++i) { ++ if (static_cast(nCharset) == kDefaultTTFMap[i].charset) ++ return kDefaultTTFMap[i].fontname; + } + return kUniversalDefaultFontName; + } + + // static +-uint8_t CFX_Font::GetCharSetFromUnicode(uint16_t word) { ++FX_Charset CFX_Font::GetCharSetFromUnicode(uint16_t word) { + // to avoid CJK Font to show ASCII + if (word < 0x7F) +- return FX_CHARSET_ANSI; ++ return FX_Charset::kANSI; + + // find new charset + if ((word >= 0x4E00 && word <= 0x9FA5) || + (word >= 0xE7C7 && word <= 0xE7F3) || + (word >= 0x3000 && word <= 0x303F) || + (word >= 0x2000 && word <= 0x206F)) { +- return FX_CHARSET_ChineseSimplified; ++ return FX_Charset::kChineseSimplified; + } + + if (((word >= 0x3040) && (word <= 0x309F)) || + ((word >= 0x30A0) && (word <= 0x30FF)) || + ((word >= 0x31F0) && (word <= 0x31FF)) || + ((word >= 0xFF00) && (word <= 0xFFEF))) { +- return FX_CHARSET_ShiftJIS; ++ return FX_Charset::kShiftJIS; + } + + if (((word >= 0xAC00) && (word <= 0xD7AF)) || + ((word >= 0x1100) && (word <= 0x11FF)) || + ((word >= 0x3130) && (word <= 0x318F))) { +- return FX_CHARSET_Hangul; ++ return FX_Charset::kHangul; + } + + if (word >= 0x0E00 && word <= 0x0E7F) +- return FX_CHARSET_Thai; ++ return FX_Charset::kThai; + + if ((word >= 0x0370 && word <= 0x03FF) || (word >= 0x1F00 && word <= 0x1FFF)) +- return FX_CHARSET_MSWin_Greek; ++ return FX_Charset::kMSWin_Greek; + + if ((word >= 0x0600 && word <= 0x06FF) || (word >= 0xFB50 && word <= 0xFEFC)) +- return FX_CHARSET_MSWin_Arabic; ++ return FX_Charset::kMSWin_Arabic; + + if (word >= 0x0590 && word <= 0x05FF) +- return FX_CHARSET_MSWin_Hebrew; ++ return FX_Charset::kMSWin_Hebrew; + + if (word >= 0x0400 && word <= 0x04FF) +- return FX_CHARSET_MSWin_Cyrillic; ++ return FX_Charset::kMSWin_Cyrillic; + + if (word >= 0x0100 && word <= 0x024F) +- return FX_CHARSET_MSWin_EasternEuropean; ++ return FX_Charset::kMSWin_EasternEuropean; + + if (word >= 0x1E00 && word <= 0x1EFF) +- return FX_CHARSET_MSWin_Vietnamese; ++ return FX_Charset::kMSWin_Vietnamese; + +- return FX_CHARSET_ANSI; ++ return FX_Charset::kANSI; + } + + CFX_Font::CFX_Font() = default; +@@ -304,38 +341,52 @@ int CFX_Font::GetSubstFontItalicAngle() const { + } + + #ifdef PDF_ENABLE_XFA +-bool CFX_Font::LoadFile(const RetainPtr& pFile, ++bool CFX_Font::LoadFile(RetainPtr pFile, + int nFaceIndex) { + m_bEmbedded = false; ++ m_ObjectTag = 0; ++ ++ auto pStreamRec = std::make_unique(); ++ pStreamRec->base = nullptr; ++ pStreamRec->size = static_cast(pFile->GetSize()); ++ pStreamRec->pos = 0; ++ pStreamRec->descriptor.pointer = static_cast(pFile.Get()); ++ pStreamRec->close = FTStreamClose; ++ pStreamRec->read = FTStreamRead; + +- CFX_FontMgr* pFontMgr = CFX_GEModule::Get()->GetFontMgr(); +- std::unique_ptr stream; +- m_Face = LoadFileImp(pFontMgr->GetFTLibrary(), pFile, nFaceIndex, &stream); ++ FT_Open_Args args; ++ args.flags = FT_OPEN_STREAM; ++ args.stream = pStreamRec.get(); ++ ++ m_Face = CFX_Face::Open(CFX_GEModule::Get()->GetFontMgr()->GetFTLibrary(), ++ &args, nFaceIndex); + if (!m_Face) + return false; + +- m_pOwnedStream = std::move(stream); ++ m_pOwnedFile = std::move(pFile); ++ m_pOwnedStreamRec = std::move(pStreamRec); + FT_Set_Pixel_Sizes(m_Face->GetRec(), 0, 64); + return true; + } + +-#if !defined(OS_WIN) ++#if !BUILDFLAG(IS_WIN) + void CFX_Font::SetFace(RetainPtr face) { + ClearGlyphCache(); ++ m_ObjectTag = 0; + m_Face = face; + } + + void CFX_Font::SetSubstFont(std::unique_ptr subst) { + m_pSubstFont = std::move(subst); + } +-#endif // !defined(OS_WIN) ++#endif // !BUILDFLAG(IS_WIN) + #endif // PDF_ENABLE_XFA + + CFX_Font::~CFX_Font() { + m_FontData = {}; // m_FontData can't outive m_Face. + m_Face.Reset(); + +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + ReleasePlatformResource(); + #endif + } +@@ -345,13 +396,14 @@ void CFX_Font::LoadSubst(const ByteString& face_name, + uint32_t flags, + int weight, + int italic_angle, +- int CharsetCP, ++ FX_CodePage code_page, + bool bVertical) { + m_bEmbedded = false; + m_bVertical = bVertical; +- m_pSubstFont = pdfium::MakeUnique(); +- m_Face = CFX_GEModule::Get()->GetFontMgr()->FindSubstFont( +- face_name, bTrueType, flags, weight, italic_angle, CharsetCP, ++ m_ObjectTag = 0; ++ m_pSubstFont = std::make_unique(); ++ m_Face = CFX_GEModule::Get()->GetFontMgr()->GetBuiltinMapper()->FindSubstFont( ++ face_name, bTrueType, flags, weight, italic_angle, code_page, + m_pSubstFont.get()); + if (m_Face) { + m_FontData = {FXFT_Get_Face_Stream_Base(m_Face->GetRec()), +@@ -359,30 +411,44 @@ void CFX_Font::LoadSubst(const ByteString& face_name, + } + } + +-uint32_t CFX_Font::GetGlyphWidth(uint32_t glyph_index) { ++int CFX_Font::GetGlyphWidth(uint32_t glyph_index) const { ++ return GetGlyphWidth(glyph_index, 0, 0); ++} ++ ++int CFX_Font::GetGlyphWidth(uint32_t glyph_index, ++ int dest_width, ++ int weight) const { ++ return GetOrCreateGlyphCache()->GetGlyphWidth(this, glyph_index, dest_width, ++ weight); ++} ++ ++int CFX_Font::GetGlyphWidthImpl(uint32_t glyph_index, ++ int dest_width, ++ int weight) const { + if (!m_Face) + return 0; +- if (m_pSubstFont && m_pSubstFont->m_bFlagMM) +- AdjustMMParams(glyph_index, 0, 0); ++ if (m_pSubstFont && m_pSubstFont->IsBuiltInGenericFont()) ++ AdjustMMParams(glyph_index, dest_width, weight); + int err = + FT_Load_Glyph(m_Face->GetRec(), glyph_index, + FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); + if (err) + return 0; + +- int horiAdvance = FXFT_Get_Glyph_HoriAdvance(m_Face->GetRec()); +- if (horiAdvance < 0 || horiAdvance > kThousandthMaxInt) ++ FT_Pos horiAdvance = FXFT_Get_Glyph_HoriAdvance(m_Face->GetRec()); ++ if (horiAdvance < kThousandthMinInt || horiAdvance > kThousandthMaxInt) + return 0; + +- return EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face->GetRec()), horiAdvance); ++ return static_cast( ++ EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face->GetRec()), horiAdvance)); + } + + bool CFX_Font::LoadEmbedded(pdfium::span src_span, +- bool bForceAsVertical) { +- if (bForceAsVertical) +- m_bVertical = true; +- m_FontDataAllocation = std::vector>( +- src_span.begin(), src_span.end()); ++ bool force_vertical, ++ uint64_t object_tag) { ++ m_bVertical = force_vertical; ++ m_ObjectTag = object_tag; ++ m_FontDataAllocation = DataVector(src_span.begin(), src_span.end()); + m_Face = CFX_GEModule::Get()->GetFontMgr()->NewFixedFace( + nullptr, m_FontDataAllocation, 0); + m_bEmbedded = true; +@@ -416,70 +482,53 @@ int CFX_Font::GetDescent() const { + return EM_ADJUST(FXFT_Get_Face_UnitsPerEM(m_Face->GetRec()), descender); + } + +-bool CFX_Font::GetGlyphBBox(uint32_t glyph_index, FX_RECT* pBBox) { ++absl::optional CFX_Font::GetGlyphBBox(uint32_t glyph_index) { + if (!m_Face) +- return false; ++ return absl::nullopt; + + if (FXFT_Is_Face_Tricky(m_Face->GetRec())) { + int error = FT_Set_Char_Size(m_Face->GetRec(), 0, 1000 * 64, 72, 72); + if (error) +- return false; ++ return absl::nullopt; + + error = FT_Load_Glyph(m_Face->GetRec(), glyph_index, + FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); + if (error) +- return false; ++ return absl::nullopt; + +- FT_BBox cbox; + FT_Glyph glyph; + error = FT_Get_Glyph(m_Face->GetRec()->glyph, &glyph); + if (error) +- return false; ++ return absl::nullopt; + ++ FT_BBox cbox; + FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &cbox); + int pixel_size_x = m_Face->GetRec()->size->metrics.x_ppem; + int pixel_size_y = m_Face->GetRec()->size->metrics.y_ppem; +- if (pixel_size_x == 0 || pixel_size_y == 0) { +- pBBox->left = cbox.xMin; +- pBBox->right = cbox.xMax; +- pBBox->top = cbox.yMax; +- pBBox->bottom = cbox.yMin; +- } else { +- pBBox->left = cbox.xMin * 1000 / pixel_size_x; +- pBBox->right = cbox.xMax * 1000 / pixel_size_x; +- pBBox->top = cbox.yMax * 1000 / pixel_size_y; +- pBBox->bottom = cbox.yMin * 1000 / pixel_size_y; +- } +- pBBox->top = std::min( +- pBBox->top, +- static_cast(FXFT_Get_Face_Ascender(m_Face->GetRec()))); +- pBBox->bottom = std::max( +- pBBox->bottom, +- static_cast(FXFT_Get_Face_Descender(m_Face->GetRec()))); ++ FX_RECT result = ScaledFXRectFromFTPos( ++ cbox.xMin, cbox.yMax, cbox.xMax, cbox.yMin, pixel_size_x, pixel_size_y); ++ result.top = ++ std::min(result.top, pdfium::base::checked_cast( ++ FXFT_Get_Face_Ascender(m_Face->GetRec()))); ++ result.bottom = ++ std::max(result.bottom, pdfium::base::checked_cast( ++ FXFT_Get_Face_Descender(m_Face->GetRec()))); + FT_Done_Glyph(glyph); +- return FT_Set_Pixel_Sizes(m_Face->GetRec(), 0, 64) == 0; +- } +- if (FT_Load_Glyph(m_Face->GetRec(), glyph_index, +- FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH)) { +- return false; ++ if (FT_Set_Pixel_Sizes(m_Face->GetRec(), 0, 64) != 0) ++ return absl::nullopt; ++ return result; + } ++ constexpr int kFlag = FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; ++ if (FT_Load_Glyph(m_Face->GetRec(), glyph_index, kFlag) != 0) ++ return absl::nullopt; + int em = FXFT_Get_Face_UnitsPerEM(m_Face->GetRec()); +- if (em == 0) { +- pBBox->left = FXFT_Get_Glyph_HoriBearingX(m_Face->GetRec()); +- pBBox->bottom = FXFT_Get_Glyph_HoriBearingY(m_Face->GetRec()); +- pBBox->top = pBBox->bottom - FXFT_Get_Glyph_Height(m_Face->GetRec()); +- pBBox->right = pBBox->left + FXFT_Get_Glyph_Width(m_Face->GetRec()); +- } else { +- pBBox->left = FXFT_Get_Glyph_HoriBearingX(m_Face->GetRec()) * 1000 / em; +- pBBox->top = (FXFT_Get_Glyph_HoriBearingY(m_Face->GetRec()) - +- FXFT_Get_Glyph_Height(m_Face->GetRec())) * +- 1000 / em; +- pBBox->right = (FXFT_Get_Glyph_HoriBearingX(m_Face->GetRec()) + +- FXFT_Get_Glyph_Width(m_Face->GetRec())) * +- 1000 / em; +- pBBox->bottom = (FXFT_Get_Glyph_HoriBearingY(m_Face->GetRec())) * 1000 / em; +- } +- return true; ++ return ScaledFXRectFromFTPos(FXFT_Get_Glyph_HoriBearingX(m_Face->GetRec()), ++ FXFT_Get_Glyph_HoriBearingY(m_Face->GetRec()) - ++ FXFT_Get_Glyph_Height(m_Face->GetRec()), ++ FXFT_Get_Glyph_HoriBearingX(m_Face->GetRec()) + ++ FXFT_Get_Glyph_Width(m_Face->GetRec()), ++ FXFT_Get_Glyph_HoriBearingY(m_Face->GetRec()), ++ em, em); + } + + bool CFX_Font::IsItalic() const { +@@ -501,6 +550,13 @@ bool CFX_Font::IsFixedWidth() const { + return m_Face && FXFT_Is_Face_fixedwidth(m_Face->GetRec()) != 0; + } + ++#if defined(_SKIA_SUPPORT_) ++bool CFX_Font::IsSubstFontBold() const { ++ CFX_SubstFont* subst_font = GetSubstFont(); ++ return subst_font && subst_font->GetOriginalWeight() >= FXFONT_FW_BOLD; ++} ++#endif ++ + ByteString CFX_Font::GetPsName() const { + if (!m_Face) + return ByteString(); +@@ -537,12 +593,10 @@ ByteString CFX_Font::GetFaceName() const { + return m_pSubstFont->m_Family; + } + +-ByteString CFX_Font::GetBaseFontName(bool restrict_to_psname) const { ++ByteString CFX_Font::GetBaseFontName() const { + ByteString psname = GetPsName(); +- if (restrict_to_psname || (!psname.IsEmpty() && psname != kUntitledFontName)) ++ if (!psname.IsEmpty() && psname != kUntitledFontName) + return psname; +- if (!m_Face && !m_pSubstFont) +- return ByteString(); + if (m_Face) { + ByteString style = ByteString(FXFT_Get_Face_Style_Name(m_Face->GetRec())); + ByteString facename = GetFamilyNameOrUntitled(); +@@ -552,26 +606,39 @@ ByteString CFX_Font::GetBaseFontName(bool restrict_to_psname) const { + facename += (IsTTFont() ? "," : " ") + style; + return facename; + } +- return m_pSubstFont->m_Family; ++ if (m_pSubstFont) ++ return m_pSubstFont->m_Family; ++ return ByteString(); + } + +-bool CFX_Font::GetBBox(FX_RECT* pBBox) { ++absl::optional CFX_Font::GetRawBBox() const { + if (!m_Face) +- return false; ++ return absl::nullopt; ++ ++ return FXRectFromFTPos(FXFT_Get_Face_xMin(m_Face->GetRec()), ++ FXFT_Get_Face_yMin(m_Face->GetRec()), ++ FXFT_Get_Face_xMax(m_Face->GetRec()), ++ FXFT_Get_Face_yMax(m_Face->GetRec())); ++} ++ ++absl::optional CFX_Font::GetBBox() const { ++ absl::optional result = GetRawBBox(); ++ if (!result.has_value()) ++ return result; + + int em = FXFT_Get_Face_UnitsPerEM(m_Face->GetRec()); +- if (em == 0) { +- pBBox->left = FXFT_Get_Face_xMin(m_Face->GetRec()); +- pBBox->bottom = FXFT_Get_Face_yMax(m_Face->GetRec()); +- pBBox->top = FXFT_Get_Face_yMin(m_Face->GetRec()); +- pBBox->right = FXFT_Get_Face_xMax(m_Face->GetRec()); +- } else { +- pBBox->left = FXFT_Get_Face_xMin(m_Face->GetRec()) * 1000 / em; +- pBBox->top = FXFT_Get_Face_yMin(m_Face->GetRec()) * 1000 / em; +- pBBox->right = FXFT_Get_Face_xMax(m_Face->GetRec()) * 1000 / em; +- pBBox->bottom = FXFT_Get_Face_yMax(m_Face->GetRec()) * 1000 / em; ++ if (em != 0) { ++ FX_RECT& bbox = result.value(); ++ bbox.left = (bbox.left * 1000) / em; ++ bbox.top = (bbox.top * 1000) / em; ++ bbox.right = (bbox.right * 1000) / em; ++ bbox.bottom = (bbox.bottom * 1000) / em; + } +- return true; ++ return result; ++} ++ ++void CFX_Font::AllocSubData(size_t size) { ++ m_pSubData.reset(FX_Alloc(uint8_t, size)); + } + + RetainPtr CFX_Font::GetOrCreateGlyphCache() const { +@@ -587,13 +654,13 @@ void CFX_Font::ClearGlyphCache() { + void CFX_Font::AdjustMMParams(int glyph_index, + int dest_width, + int weight) const { +- ASSERT(dest_width >= 0); ++ DCHECK(dest_width >= 0); + FXFT_MM_VarPtr pMasters = nullptr; + FT_Get_MM_Var(m_Face->GetRec(), &pMasters); + if (!pMasters) + return; + +- long coords[2]; ++ FT_Pos coords[2]; + if (weight == 0) + coords[0] = FXFT_Get_MM_Axis_Def(FXFT_Get_MM_Axis(pMasters, 0)) / 65536; + else +@@ -602,34 +669,37 @@ void CFX_Font::AdjustMMParams(int glyph_index, + if (dest_width == 0) { + coords[1] = FXFT_Get_MM_Axis_Def(FXFT_Get_MM_Axis(pMasters, 1)) / 65536; + } else { +- int min_param = FXFT_Get_MM_Axis_Min(FXFT_Get_MM_Axis(pMasters, 1)) / 65536; +- int max_param = FXFT_Get_MM_Axis_Max(FXFT_Get_MM_Axis(pMasters, 1)) / 65536; ++ FT_Long min_param = ++ FXFT_Get_MM_Axis_Min(FXFT_Get_MM_Axis(pMasters, 1)) / 65536; ++ FT_Long max_param = ++ FXFT_Get_MM_Axis_Max(FXFT_Get_MM_Axis(pMasters, 1)) / 65536; + coords[1] = min_param; + FT_Set_MM_Design_Coordinates(m_Face->GetRec(), 2, coords); + FT_Load_Glyph(m_Face->GetRec(), glyph_index, + FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); +- int min_width = FXFT_Get_Glyph_HoriAdvance(m_Face->GetRec()) * 1000 / +- FXFT_Get_Face_UnitsPerEM(m_Face->GetRec()); ++ FT_Pos min_width = FXFT_Get_Glyph_HoriAdvance(m_Face->GetRec()) * 1000 / ++ FXFT_Get_Face_UnitsPerEM(m_Face->GetRec()); + coords[1] = max_param; + FT_Set_MM_Design_Coordinates(m_Face->GetRec(), 2, coords); + FT_Load_Glyph(m_Face->GetRec(), glyph_index, + FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); +- int max_width = FXFT_Get_Glyph_HoriAdvance(m_Face->GetRec()) * 1000 / +- FXFT_Get_Face_UnitsPerEM(m_Face->GetRec()); ++ FT_Pos max_width = FXFT_Get_Glyph_HoriAdvance(m_Face->GetRec()) * 1000 / ++ FXFT_Get_Face_UnitsPerEM(m_Face->GetRec()); + if (max_width == min_width) { +- FXFT_Free(m_Face->GetRec(), pMasters); ++ FreeMMVar(m_Face->GetRec(), pMasters); + return; + } +- int param = min_param + (max_param - min_param) * (dest_width - min_width) / +- (max_width - min_width); ++ FT_Pos param = min_param + (max_param - min_param) * ++ (dest_width - min_width) / ++ (max_width - min_width); + coords[1] = param; + } +- FXFT_Free(m_Face->GetRec(), pMasters); ++ FreeMMVar(m_Face->GetRec(), pMasters); + FT_Set_MM_Design_Coordinates(m_Face->GetRec(), 2, coords); + } + +-CFX_PathData* CFX_Font::LoadGlyphPathImpl(uint32_t glyph_index, +- uint32_t dest_width) const { ++std::unique_ptr CFX_Font::LoadGlyphPathImpl(uint32_t glyph_index, ++ int dest_width) const { + if (!m_Face) + return nullptr; + +@@ -637,21 +707,13 @@ CFX_PathData* CFX_Font::LoadGlyphPathImpl(uint32_t glyph_index, + FT_Matrix ft_matrix = {65536, 0, 0, 65536}; + if (m_pSubstFont) { + if (m_pSubstFont->m_ItalicAngle) { +- int skew = m_pSubstFont->m_ItalicAngle; +- // |skew| is nonpositive so |-skew| is used as the index. We need to make +- // sure |skew| != INT_MIN since -INT_MIN is undefined. +- if (skew <= 0 && skew != std::numeric_limits::min() && +- static_cast(-skew) < kAngleSkewArraySize) { +- skew = -s_AngleSkew[-skew]; +- } else { +- skew = -58; +- } ++ int skew = GetSkewFromAngle(m_pSubstFont->m_ItalicAngle); + if (m_bVertical) + ft_matrix.yx += ft_matrix.yy * skew / 100; + else + ft_matrix.xy -= ft_matrix.xx * skew / 100; + } +- if (m_pSubstFont->m_bFlagMM) ++ if (m_pSubstFont->IsBuiltInGenericFont()) + AdjustMMParams(glyph_index, dest_width, m_pSubstFont->m_Weight); + } + ScopedFontTransform scoped_transform(m_Face, &ft_matrix); +@@ -661,15 +723,15 @@ CFX_PathData* CFX_Font::LoadGlyphPathImpl(uint32_t glyph_index, + load_flags |= FT_LOAD_NO_HINTING; + if (FT_Load_Glyph(m_Face->GetRec(), glyph_index, load_flags)) + return nullptr; +- if (m_pSubstFont && !m_pSubstFont->m_bFlagMM && ++ if (m_pSubstFont && !m_pSubstFont->IsBuiltInGenericFont() && + m_pSubstFont->m_Weight > 400) { +- uint32_t index = (m_pSubstFont->m_Weight - 400) / 10; +- index = std::min(index, static_cast(kWeightPowArraySize - 1)); +- int level = 0; +- if (m_pSubstFont->m_Charset == FX_CHARSET_ShiftJIS) +- level = s_WeightPow_SHIFTJIS[index] * 2 * 65536 / 36655; ++ uint32_t index = std::min((m_pSubstFont->m_Weight - 400) / 10, ++ kWeightPowArraySize - 1); ++ int level; ++ if (m_pSubstFont->m_Charset == FX_Charset::kShiftJIS) ++ level = kWeightPowShiftJis[index] * 65536 / 36655; + else +- level = s_WeightPow[index] * 2; ++ level = kWeightPow[index]; + FT_Outline_Embolden(FXFT_Get_Glyph_Outline(m_Face->GetRec()), level); + } + +@@ -681,8 +743,8 @@ CFX_PathData* CFX_Font::LoadGlyphPathImpl(uint32_t glyph_index, + funcs.shift = 0; + funcs.delta = 0; + ++ auto pPath = std::make_unique(); + OUTLINE_PARAMS params; +- auto pPath = pdfium::MakeUnique(); + params.m_pPath = pPath.get(); + params.m_CurX = params.m_CurY = 0; + params.m_CoordUnit = 64 * 64.0; +@@ -694,27 +756,48 @@ CFX_PathData* CFX_Font::LoadGlyphPathImpl(uint32_t glyph_index, + + Outline_CheckEmptyContour(¶ms); + pPath->ClosePath(); +- +- return pPath.release(); ++ return pPath; + } + +-const CFX_GlyphBitmap* CFX_Font::LoadGlyphBitmap(uint32_t glyph_index, +- bool bFontStyle, +- const CFX_Matrix& matrix, +- uint32_t dest_width, +- int anti_alias, +- int* pTextFlags) const { ++const CFX_GlyphBitmap* CFX_Font::LoadGlyphBitmap( ++ uint32_t glyph_index, ++ bool bFontStyle, ++ const CFX_Matrix& matrix, ++ int dest_width, ++ int anti_alias, ++ CFX_TextRenderOptions* text_options) const { + return GetOrCreateGlyphCache()->LoadGlyphBitmap(this, glyph_index, bFontStyle, + matrix, dest_width, +- anti_alias, pTextFlags); ++ anti_alias, text_options); + } + +-const CFX_PathData* CFX_Font::LoadGlyphPath(uint32_t glyph_index, +- uint32_t dest_width) const { ++const CFX_Path* CFX_Font::LoadGlyphPath(uint32_t glyph_index, ++ int dest_width) const { + return GetOrCreateGlyphCache()->LoadGlyphPath(this, glyph_index, dest_width); + } + +-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_ ++// static ++int CFX_Font::GetWeightLevel(FX_Charset charset, size_t index) { ++ if (index >= kWeightPowArraySize) ++ return -1; ++ ++ if (charset == FX_Charset::kShiftJIS) ++ return kWeightPowShiftJis[index]; ++ return kWeightPow11[index]; ++} ++ ++// static ++int CFX_Font::GetSkewFromAngle(int angle) { ++ // |angle| is non-positive so |-angle| is used as the index. Need to make sure ++ // |angle| != INT_MIN since -INT_MIN is undefined. ++ if (angle > 0 || angle == std::numeric_limits::min() || ++ static_cast(-angle) >= std::size(kAngleSkew)) { ++ return -58; ++ } ++ return kAngleSkew[-angle]; ++} ++ ++#if defined(_SKIA_SUPPORT_) + CFX_TypeFace* CFX_Font::GetDeviceCache() const { + return GetOrCreateGlyphCache()->GetDeviceCache(this); + } +diff --git a/core/fxge/cfx_font.h b/core/fxge/cfx_font.h +index ead60f71c..bf948743b 100644 +--- a/core/fxge/cfx_font.h ++++ b/core/fxge/cfx_font.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,50 +7,80 @@ + #ifndef CORE_FXGE_CFX_FONT_H_ + #define CORE_FXGE_CFX_FONT_H_ + ++#include ++ + #include +-#include + + #include "build/build_config.h" + #include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_codepage_forward.h" + #include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxge/cfx_face.h" +-#include "core/fxge/fx_freetype.h" ++#include "core/fxge/freetype/fx_freetype.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + #include "third_party/base/span.h" + +-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ ++#if defined(_SKIA_SUPPORT_) + #include "core/fxge/fx_font.h" + #endif + +-class CFX_GlyphCache; + class CFX_GlyphBitmap; +-class CFX_PathData; ++class CFX_GlyphCache; ++class CFX_Path; + class CFX_SubstFont; + class IFX_SeekableReadStream; ++struct CFX_TextRenderOptions; + + class CFX_Font { + public: +- CFX_Font(); +- ~CFX_Font(); ++ // This struct should be the same as FPDF_CharsetFontMap. ++ struct CharsetFontMap { ++ int charset; // Character Set Enum value, see FX_Charset::kXXX. ++ const char* fontname; // Name of default font to use with that charset. ++ }; ++ ++ enum class FontType { ++ kUnknown, ++ kCIDTrueType, ++ }; ++ ++ // Pointer to the default character set to TT Font name map. The map is an ++ // array of CharsetFontMap structs, with its end indicated by a {-1, nullptr} ++ // entry. ++ static const CharsetFontMap kDefaultTTFMap[]; + + // Used when the font name is empty. + static const char kUntitledFontName[]; + + static const char kDefaultAnsiFontName[]; + static const char kUniversalDefaultFontName[]; +- static ByteString GetDefaultFontNameByCharset(uint8_t nCharset); +- static uint8_t GetCharSetFromUnicode(uint16_t word); ++ ++ // Returns negative values on failure. ++ static int GetWeightLevel(FX_Charset charset, size_t index); ++ ++ // |angle| is typically negative. ++ static int GetSkewFromAngle(int angle); ++ ++ static ByteString GetDefaultFontNameByCharset(FX_Charset nCharset); ++ static FX_Charset GetCharSetFromUnicode(uint16_t word); ++ ++ CFX_Font(); ++ ~CFX_Font(); + + void LoadSubst(const ByteString& face_name, + bool bTrueType, + uint32_t flags, + int weight, + int italic_angle, +- int CharsetCP, ++ FX_CodePage code_page, + bool bVertical); + + bool LoadEmbedded(pdfium::span src_span, +- bool bForceAsVertical); ++ bool force_vertical, ++ uint64_t object_tag); + RetainPtr GetFace() const { return m_Face; } + FXFT_FaceRec* GetFaceRec() const { + return m_Face ? m_Face->GetRec() : nullptr; +@@ -59,32 +89,28 @@ class CFX_Font { + int GetSubstFontItalicAngle() const; + + #if defined(PDF_ENABLE_XFA) +- bool LoadFile(const RetainPtr& pFile, int nFaceIndex); ++ bool LoadFile(RetainPtr pFile, int nFaceIndex); + +-#if !defined(OS_WIN) ++#if !BUILDFLAG(IS_WIN) + void SetFace(RetainPtr face); + void SetFontSpan(pdfium::span pSpan) { m_FontData = pSpan; } + void SetSubstFont(std::unique_ptr subst); +-#endif // !defined(OS_WIN) ++#endif // !BUILDFLAG(IS_WIN) + #endif // defined(PDF_ENABLE_XFA) + +- const CFX_GlyphBitmap* LoadGlyphBitmap(uint32_t glyph_index, +- bool bFontStyle, +- const CFX_Matrix& matrix, +- uint32_t dest_width, +- int anti_alias, +- int* pTextFlags) const; +- const CFX_PathData* LoadGlyphPath(uint32_t glyph_index, +- uint32_t dest_width) const; +- +-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ +- CFX_TypeFace* GetDeviceCache() const; +-#endif +- +- uint32_t GetGlyphWidth(uint32_t glyph_index); ++ const CFX_GlyphBitmap* LoadGlyphBitmap( ++ uint32_t glyph_index, ++ bool bFontStyle, ++ const CFX_Matrix& matrix, ++ int dest_width, ++ int anti_alias, ++ CFX_TextRenderOptions* text_options) const; ++ const CFX_Path* LoadGlyphPath(uint32_t glyph_index, int dest_width) const; ++ int GetGlyphWidth(uint32_t glyph_index) const; ++ int GetGlyphWidth(uint32_t glyph_index, int dest_width, int weight) const; + int GetAscent() const; + int GetDescent() const; +- bool GetGlyphBBox(uint32_t glyph_index, FX_RECT* pBBox); ++ absl::optional GetGlyphBBox(uint32_t glyph_index); + bool IsItalic() const; + bool IsBold() const; + bool IsFixedWidth() const; +@@ -92,61 +118,62 @@ class CFX_Font { + ByteString GetPsName() const; + ByteString GetFamilyName() const; + ByteString GetFaceName() const; +- ByteString GetBaseFontName(bool restrict_to_psname) const; ++ ByteString GetBaseFontName() const; + bool IsTTFont() const; +- bool GetBBox(FX_RECT* pBBox); ++ ++ // Raw bounding box. ++ absl::optional GetRawBBox() const; ++ ++ // Bounding box adjusted for font units. ++ absl::optional GetBBox() const; ++ + bool IsEmbedded() const { return m_bEmbedded; } +- uint8_t* GetSubData() const { return m_pGsubData.get(); } +- void SetSubData(uint8_t* data) { m_pGsubData.reset(data); } ++ void AllocSubData(size_t size); ++ uint8_t* GetSubData() const { return m_pSubData.get(); } ++ FontType GetFontType() const { return m_FontType; } ++ void SetFontType(FontType type) { m_FontType = type; } ++ uint64_t GetObjectTag() const { return m_ObjectTag; } + pdfium::span GetFontSpan() const { return m_FontData; } + void AdjustMMParams(int glyph_index, int dest_width, int weight) const; +- CFX_PathData* LoadGlyphPathImpl(uint32_t glyph_index, +- uint32_t dest_width) const; +-#if defined(OS_MACOSX) ++ std::unique_ptr LoadGlyphPathImpl(uint32_t glyph_index, ++ int dest_width) const; ++ int GetGlyphWidthImpl(uint32_t glyph_index, int dest_width, int weight) const; ++ ++#if defined(_SKIA_SUPPORT_) ++ CFX_TypeFace* GetDeviceCache() const; ++ bool IsSubstFontBold() const; ++#endif ++ ++#if BUILDFLAG(IS_APPLE) + void* GetPlatformFont() const { return m_pPlatformFont; } + void SetPlatformFont(void* font) { m_pPlatformFont = font; } + #endif + +- static const size_t kAngleSkewArraySize = 30; +- static const char s_AngleSkew[kAngleSkewArraySize]; +- static const size_t kWeightPowArraySize = 100; +- static const uint8_t s_WeightPow[kWeightPowArraySize]; +- static const uint8_t s_WeightPow_11[kWeightPowArraySize]; +- static const uint8_t s_WeightPow_SHIFTJIS[kWeightPowArraySize]; +- +- // This struct should be the same as FPDF_CharsetFontMap. +- struct CharsetFontMap { +- int charset; // Character Set Enum value, see FX_CHARSET_XXX. +- const char* fontname; // Name of default font to use with that charset. +- }; +- +- /** +- * Pointer to the default character set to TT Font name map. The +- * map is an array of CharsetFontMap structs, with its end indicated +- * by a { -1, NULL } entry. +- **/ +- static const CharsetFontMap defaultTTFMap[]; +- + private: + RetainPtr GetOrCreateGlyphCache() const; + void ClearGlyphCache(); +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + void ReleasePlatformResource(); + #endif + ByteString GetFamilyNameOrUntitled() const; + + #if defined(PDF_ENABLE_XFA) +- std::unique_ptr m_pOwnedStream; // Must outlive |m_Face|. ++ // |m_pOwnedFile| must outlive |m_pOwnedStreamRec|. ++ RetainPtr m_pOwnedFile; ++ std::unique_ptr m_pOwnedStreamRec; // Must outlive |m_Face|. + #endif ++ + mutable RetainPtr m_Face; + mutable RetainPtr m_GlyphCache; + std::unique_ptr m_pSubstFont; +- std::unique_ptr m_pGsubData; +- std::vector> m_FontDataAllocation; ++ std::unique_ptr m_pSubData; ++ DataVector m_FontDataAllocation; + pdfium::span m_FontData; ++ FontType m_FontType = FontType::kUnknown; ++ uint64_t m_ObjectTag = 0; + bool m_bEmbedded = false; + bool m_bVertical = false; +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + void* m_pPlatformFont = nullptr; + #endif + }; +diff --git a/core/fxge/cfx_fontcache.cpp b/core/fxge/cfx_fontcache.cpp +index 7289026e9..5ce96c9f7 100644 +--- a/core/fxge/cfx_fontcache.cpp ++++ b/core/fxge/cfx_fontcache.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,14 +6,10 @@ + + #include "core/fxge/cfx_fontcache.h" + +-#include +-#include +- + #include "core/fxge/cfx_font.h" + #include "core/fxge/cfx_glyphcache.h" ++#include "core/fxge/freetype/fx_freetype.h" + #include "core/fxge/fx_font.h" +-#include "core/fxge/fx_freetype.h" +-#include "third_party/base/ptr_util.h" + + CFX_FontCache::CFX_FontCache() = default; + +@@ -32,7 +28,7 @@ RetainPtr CFX_FontCache::GetGlyphCache(const CFX_Font* pFont) { + return new_cache; + } + +-#ifdef _SKIA_SUPPORT_ ++#if defined(_SKIA_SUPPORT_) + CFX_TypeFace* CFX_FontCache::GetDeviceCache(const CFX_Font* pFont) { + return GetGlyphCache(pFont)->GetDeviceCache(pFont); + } +diff --git a/core/fxge/cfx_fontcache.h b/core/fxge/cfx_fontcache.h +index 515811b67..9b7a426d4 100644 +--- a/core/fxge/cfx_fontcache.h ++++ b/core/fxge/cfx_fontcache.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,11 +8,11 @@ + #define CORE_FXGE_CFX_FONTCACHE_H_ + + #include +-#include + + #include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxge/cfx_glyphcache.h" +-#include "core/fxge/fx_freetype.h" ++#include "core/fxge/freetype/fx_freetype.h" + + class CFX_Font; + +@@ -22,7 +22,7 @@ class CFX_FontCache { + ~CFX_FontCache(); + + RetainPtr GetGlyphCache(const CFX_Font* pFont); +-#ifdef _SKIA_SUPPORT_ ++#if defined(_SKIA_SUPPORT_) + CFX_TypeFace* GetDeviceCache(const CFX_Font* pFont); + #endif + +diff --git a/core/fxge/cfx_fontmapper.cpp b/core/fxge/cfx_fontmapper.cpp +index 3b0bcae03..34c70a854 100644 +--- a/core/fxge/cfx_fontmapper.cpp ++++ b/core/fxge/cfx_fontmapper.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,29 +6,33 @@ + + #include "core/fxge/cfx_fontmapper.h" + ++#include ++ + #include + #include +-#include +-#include + #include +-#include + + #include "build/build_config.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_codepage.h" ++#include "core/fxcrt/fx_extension.h" ++#include "core/fxcrt/fx_memory.h" + #include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/stl_util.h" + #include "core/fxge/cfx_fontmgr.h" + #include "core/fxge/cfx_substfont.h" + #include "core/fxge/fx_font.h" + #include "core/fxge/systemfontinfo_iface.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/cxx17_backports.h" + + namespace { + +-const int kNumStandardFonts = 14; +-static_assert(CFX_FontMapper::kDingbats + 1 == kNumStandardFonts, ++static_assert(CFX_FontMapper::kLast + 1 == CFX_FontMapper::kNumStandardFonts, + "StandardFont enum count mismatch"); + +-const char* const g_Base14FontNames[kNumStandardFonts] = { ++const char* const kBase14FontNames[CFX_FontMapper::kNumStandardFonts] = { + "Courier", + "Courier-Bold", + "Courier-BoldOblique", +@@ -50,7 +54,7 @@ struct AltFontName { + CFX_FontMapper::StandardFont m_Index; + }; + +-const AltFontName g_AltFontNames[] = { ++constexpr AltFontName kAltFontNames[] = { + {"Arial", CFX_FontMapper::kHelvetica}, + {"Arial,Bold", CFX_FontMapper::kHelveticaBold}, + {"Arial,BoldItalic", CFX_FontMapper::kHelveticaBoldOblique}, +@@ -147,22 +151,21 @@ struct AltFontFamily { + const char* m_pFontFamily; // Raw, POD struct. + }; + +-const AltFontFamily g_AltFontFamilies[] = { ++constexpr AltFontFamily kAltFontFamilies[] = { + {"AGaramondPro", "Adobe Garamond Pro"}, + {"BankGothicBT-Medium", "BankGothic Md BT"}, + {"ForteMT", "Forte"}, + }; + +-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_ ++#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || defined(OS_ASMJS) + const char kNarrowFamily[] = "LiberationSansNarrow"; +-#elif defined(OS_ANDROID) ++#elif BUILDFLAG(IS_ANDROID) + const char kNarrowFamily[] = "RobotoCondensed"; + #else + const char kNarrowFamily[] = "ArialNarrow"; +-#endif // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_ ++#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || defined(OS_ASMJS) + +-ByteString TT_NormalizeName(const char* family) { +- ByteString norm(family); ++ByteString TT_NormalizeName(ByteString norm) { + norm.Remove(' '); + norm.Remove('-'); + norm.Remove(','); +@@ -185,7 +188,7 @@ void GetFontFamily(uint32_t nStyle, ByteString* fontName) { + *fontName = "FreeStyleScript"; + return; + } +- for (const auto& alternate : g_AltFontFamilies) { ++ for (const auto& alternate : kAltFontFamilies) { + if (fontName->Contains(alternate.m_pFontName)) { + *fontName = alternate.m_pFontFamily; + return; +@@ -193,69 +196,203 @@ void GetFontFamily(uint32_t nStyle, ByteString* fontName) { + } + } + +-ByteString ParseStyle(const char* pStyle, int iLen, int iIndex) { +- std::ostringstream buf; +- if (!iLen || iLen <= iIndex) +- return ByteString(buf); +- while (iIndex < iLen) { +- if (pStyle[iIndex] == ',') +- break; +- buf << pStyle[iIndex]; +- ++iIndex; +- } +- return ByteString(buf); ++ByteString ParseStyle(const ByteString& bsStyle, size_t iStart) { ++ ByteStringView bsRegion = bsStyle.AsStringView().Substr(iStart); ++ size_t iIndex = bsRegion.Find(',').value_or(bsRegion.GetLength()); ++ return ByteString(bsRegion.First(iIndex)); + } + +-const struct FX_FontStyle { ++struct FX_FontStyle { + const char* name; + size_t len; + uint32_t style; +-} g_FontStyles[] = { +- {"Bold", 4, FXFONT_FORCE_BOLD}, +- {"Italic", 6, FXFONT_ITALIC}, +- {"BoldItalic", 10, FXFONT_FORCE_BOLD | FXFONT_ITALIC}, +- {"Reg", 3, FXFONT_NORMAL}, ++}; ++ ++constexpr FX_FontStyle kFontStyles[] = { + {"Regular", 7, FXFONT_NORMAL}, ++ {"Reg", 3, FXFONT_NORMAL}, ++ {"BoldItalic", 10, FXFONT_FORCE_BOLD | FXFONT_ITALIC}, ++ {"Italic", 6, FXFONT_ITALIC}, ++ {"Bold", 4, FXFONT_FORCE_BOLD}, + }; + +-// +-std::tuple GetStyleType(const ByteString& bsStyle, +- bool bReverse) { +- if (bsStyle.IsEmpty()) +- return std::make_tuple(false, FXFONT_NORMAL, 0); ++const FX_FontStyle* GetStyleType(ByteStringView font_name, ++ bool reverse_search) { ++ if (font_name.IsEmpty()) ++ return nullptr; + +- for (int i = FX_ArraySize(g_FontStyles) - 1; i >= 0; --i) { +- const FX_FontStyle* pStyle = g_FontStyles + i; +- if (!pStyle || pStyle->len > bsStyle.GetLength()) ++ for (const FX_FontStyle& style : kFontStyles) { ++ if (style.len > font_name.GetLength()) + continue; + +- if (bReverse) { +- if (bsStyle.Last(pStyle->len).Compare(pStyle->name) == 0) +- return std::make_tuple(true, pStyle->style, pStyle->len); ++ ByteStringView style_view = ++ reverse_search ? font_name.Last(style.len) : font_name.First(style.len); ++ if (style_view == style.name) ++ return &style; ++ } ++ return nullptr; ++} ++ ++bool ParseStyles(const ByteString& style_str, ++ bool* is_style_available, ++ int* weight, ++ uint32_t* style) { ++ if (style_str.IsEmpty()) ++ return false; ++ ++ size_t i = 0; ++ bool is_first_item = true; ++ while (i < style_str.GetLength()) { ++ ByteString buf = ParseStyle(style_str, i); ++ const FX_FontStyle* style_result = ++ GetStyleType(buf.AsStringView(), /*reverse_search=*/false); ++ if ((i && !*is_style_available) || (!i && !style_result)) ++ return true; ++ ++ uint32_t parsed_style; ++ if (style_result) { ++ *is_style_available = true; ++ parsed_style = style_result->style; + } else { +- if (bsStyle.First(pStyle->len).Compare(pStyle->name) == 0) +- return std::make_tuple(true, pStyle->style, pStyle->len); ++ parsed_style = FXFONT_NORMAL; + } ++ ++ if (FontStyleIsForceBold(parsed_style)) { ++ // If we're already bold, then we're double bold, use special weight. ++ if (FontStyleIsForceBold(*style)) { ++ *weight = FXFONT_FW_BOLD_BOLD; ++ } else { ++ *weight = FXFONT_FW_BOLD; ++ *style |= FXFONT_FORCE_BOLD; ++ } ++ ++ is_first_item = false; ++ } ++ if (FontStyleIsItalic(parsed_style) && FontStyleIsForceBold(parsed_style)) { ++ *style |= FXFONT_ITALIC; ++ } else if (FontStyleIsItalic(parsed_style)) { ++ if (!is_first_item) ++ return true; ++ ++ *style |= FXFONT_ITALIC; ++ break; ++ } ++ i += buf.GetLength() + 1; + } +- return std::make_tuple(false, FXFONT_NORMAL, 0); ++ return false; + } + +-bool CheckSupportThirdPartFont(const ByteString& name, int* PitchFamily) { ++bool CheckSupportThirdPartFont(const ByteString& name, int* pitch_family) { + if (name != "MyriadPro") + return false; +- *PitchFamily &= ~FXFONT_FF_ROMAN; ++ *pitch_family &= ~FXFONT_FF_ROMAN; + return true; + } + +-void UpdatePitchFamily(uint32_t flags, int* PitchFamily) { ++uint32_t GetStyleFromBaseFont(int base_font) { ++ int pos = base_font % 4; ++ uint32_t style = FXFONT_NORMAL; ++ if (pos == 1 || pos == 2) ++ style |= FXFONT_FORCE_BOLD; ++ if (pos / 2) ++ style |= FXFONT_ITALIC; ++ return style; ++} ++ ++int GetPitchFamilyFromBaseFont(int base_font) { ++ if (base_font < 4) ++ return FXFONT_FF_FIXEDPITCH; ++ if (base_font >= 8) ++ return FXFONT_FF_ROMAN; ++ return 0; ++} ++ ++int GetPitchFamilyFromFlags(uint32_t flags) { ++ int pitch_family = 0; + if (FontStyleIsSerif(flags)) +- *PitchFamily |= FXFONT_FF_ROMAN; ++ pitch_family |= FXFONT_FF_ROMAN; + if (FontStyleIsScript(flags)) +- *PitchFamily |= FXFONT_FF_SCRIPT; ++ pitch_family |= FXFONT_FF_SCRIPT; + if (FontStyleIsFixedPitch(flags)) +- *PitchFamily |= FXFONT_FF_FIXEDPITCH; ++ pitch_family |= FXFONT_FF_FIXEDPITCH; ++ return pitch_family; ++} ++ ++int AdjustBaseFontForStyle(int base_font, uint32_t style) { ++ if (!style || (base_font % 4)) ++ return base_font; ++ ++ if (FontStyleIsForceBold(style) && FontStyleIsItalic(style)) ++ base_font += 2; ++ else if (FontStyleIsForceBold(style)) ++ base_font += 1; ++ else if (FontStyleIsItalic(style)) ++ base_font += 3; ++ return base_font; + } + ++FX_Charset GetCharset(FX_CodePage code_page, int base_font, uint32_t flags) { ++ if (code_page != FX_CodePage::kDefANSI) ++ return FX_GetCharsetFromCodePage(code_page); ++ if (FontStyleIsSymbolic(flags) && ++ base_font == CFX_FontMapper::kNumStandardFonts) { ++ return FX_Charset::kSymbol; ++ } ++ return FX_Charset::kANSI; ++} ++ ++bool IsStrUpper(const ByteString& str) { ++ for (size_t i = 0; i < str.GetLength(); ++i) { ++ if (!FXSYS_IsUpperASCII(str[i])) ++ return false; ++ } ++ return true; ++} ++ ++void RemoveSubsettedFontPrefix(ByteString* subst_name) { ++ constexpr size_t kPrefixLength = 6; ++ if (subst_name->GetLength() > kPrefixLength && ++ (*subst_name)[kPrefixLength] == '+' && ++ IsStrUpper(subst_name->First(kPrefixLength))) { ++ *subst_name = ++ subst_name->Last(subst_name->GetLength() - (kPrefixLength + 1)); ++ } ++} ++ ++ByteString GetSubstName(const ByteString& name, bool is_truetype) { ++ ByteString subst_name = name; ++ if (is_truetype && name.Front() == '@') ++ subst_name.Delete(0); ++ else ++ subst_name.Remove(' '); ++ RemoveSubsettedFontPrefix(&subst_name); ++ CFX_FontMapper::GetStandardFontName(&subst_name); ++ return subst_name; ++} ++ ++bool IsNarrowFontName(const ByteString& name) { ++ static const char kNarrowFonts[][10] = {"Narrow", "Condensed"}; ++ for (const char* font : kNarrowFonts) { ++ absl::optional pos = name.Find(font); ++ if (pos.has_value() && pos.value() != 0) ++ return true; ++ } ++ return false; ++} ++ ++class ScopedFontDeleter { ++ public: ++ FX_STACK_ALLOCATED(); ++ ++ ScopedFontDeleter(SystemFontInfoIface* font_info, void* font) ++ : font_info_(font_info), font_(font) {} ++ ~ScopedFontDeleter() { font_info_->DeleteFont(font_); } ++ ++ private: ++ UnownedPtr const font_info_; ++ void* const font_; ++}; ++ + } // namespace + + CFX_FontMapper::CFX_FontMapper(CFX_FontMgr* mgr) : m_pFontMgr(mgr) {} +@@ -267,13 +404,19 @@ void CFX_FontMapper::SetSystemFontInfo( + if (!pFontInfo) + return; + ++ m_bListLoaded = false; + m_pFontInfo = std::move(pFontInfo); + } + +-uint32_t CFX_FontMapper::GetChecksumFromTT(void* hFont) { ++std::unique_ptr CFX_FontMapper::TakeSystemFontInfo() { ++ return std::move(m_pFontInfo); ++} ++ ++uint32_t CFX_FontMapper::GetChecksumFromTT(void* font_handle) { + uint32_t buffer[256]; + m_pFontInfo->GetFontData( +- hFont, kTableTTCF, pdfium::as_writable_bytes(pdfium::make_span(buffer))); ++ font_handle, kTableTTCF, ++ pdfium::as_writable_bytes(pdfium::make_span(buffer))); + + uint32_t checksum = 0; + for (auto x : buffer) +@@ -282,17 +425,18 @@ uint32_t CFX_FontMapper::GetChecksumFromTT(void* hFont) { + return checksum; + } + +-ByteString CFX_FontMapper::GetPSNameFromTT(void* hFont) { +- uint32_t size = m_pFontInfo->GetFontData(hFont, kTableNAME, {}); ++ByteString CFX_FontMapper::GetPSNameFromTT(void* font_handle) { ++ size_t size = m_pFontInfo->GetFontData(font_handle, kTableNAME, {}); + if (!size) + return ByteString(); + +- std::vector buffer(size); +- uint32_t bytes_read = m_pFontInfo->GetFontData(hFont, kTableNAME, buffer); ++ DataVector buffer(size); ++ size_t bytes_read = m_pFontInfo->GetFontData(font_handle, kTableNAME, buffer); + return bytes_read == size ? GetNameFromTT(buffer, 6) : ByteString(); + } + +-void CFX_FontMapper::AddInstalledFont(const ByteString& name, int charset) { ++void CFX_FontMapper::AddInstalledFont(const ByteString& name, ++ FX_Charset charset) { + if (!m_pFontInfo) + return; + +@@ -300,22 +444,23 @@ void CFX_FontMapper::AddInstalledFont(const ByteString& name, int charset) { + if (name == m_LastFamily) + return; + +- bool bLocalized = std::any_of(name.begin(), name.end(), [](const char& c) { ++ bool is_localized = std::any_of(name.begin(), name.end(), [](const char& c) { + return static_cast(c) > 0x80; + }); + +- if (bLocalized) { +- void* hFont = m_pFontInfo->GetFont(name.c_str()); +- if (!hFont) { +- hFont = m_pFontInfo->MapFont(0, 0, FX_CHARSET_Default, 0, name.c_str()); +- if (!hFont) ++ if (is_localized) { ++ void* font_handle = m_pFontInfo->GetFont(name); ++ if (!font_handle) { ++ font_handle = ++ m_pFontInfo->MapFont(0, false, FX_Charset::kDefault, 0, name); ++ if (!font_handle) + return; + } + +- ByteString new_name = GetPSNameFromTT(hFont); ++ ScopedFontDeleter scoped_font(m_pFontInfo.get(), font_handle); ++ ByteString new_name = GetPSNameFromTT(font_handle); + if (!new_name.IsEmpty()) + m_LocalizedTTFonts.push_back(std::make_pair(new_name, name)); +- m_pFontInfo->DeleteFont(hFont); + } + m_InstalledTTFonts.push_back(name); + m_LastFamily = name; +@@ -332,63 +477,100 @@ void CFX_FontMapper::LoadInstalledFonts() { + ByteString CFX_FontMapper::MatchInstalledFonts(const ByteString& norm_name) { + LoadInstalledFonts(); + int i; +- for (i = pdfium::CollectionSize(m_InstalledTTFonts) - 1; i >= 0; i--) { +- ByteString norm1 = TT_NormalizeName(m_InstalledTTFonts[i].c_str()); +- if (norm1 == norm_name) ++ for (i = fxcrt::CollectionSize(m_InstalledTTFonts) - 1; i >= 0; i--) { ++ if (TT_NormalizeName(m_InstalledTTFonts[i]) == norm_name) + return m_InstalledTTFonts[i]; + } +- for (i = pdfium::CollectionSize(m_LocalizedTTFonts) - 1; i >= 0; i--) { +- ByteString norm1 = TT_NormalizeName(m_LocalizedTTFonts[i].first.c_str()); +- if (norm1 == norm_name) ++ for (i = fxcrt::CollectionSize(m_LocalizedTTFonts) - 1; i >= 0; i--) { ++ if (TT_NormalizeName(m_LocalizedTTFonts[i].first) == norm_name) + return m_LocalizedTTFonts[i].second; + } + return ByteString(); + } + +-RetainPtr CFX_FontMapper::UseInternalSubst(CFX_SubstFont* pSubstFont, +- int iBaseFont, +- int italic_angle, +- int weight, +- int pitch_family) { +- if (iBaseFont < kNumStandardFonts) { +- if (m_FoxitFaces[iBaseFont]) +- return m_FoxitFaces[iBaseFont]; +- Optional> font_data = +- m_pFontMgr->GetBuiltinFont(iBaseFont); +- if (font_data.has_value()) { +- m_FoxitFaces[iBaseFont] = +- m_pFontMgr->NewFixedFace(nullptr, font_data.value(), 0); +- return m_FoxitFaces[iBaseFont]; ++RetainPtr CFX_FontMapper::UseInternalSubst( ++ int base_font, ++ int weight, ++ int italic_angle, ++ int pitch_family, ++ CFX_SubstFont* subst_font) { ++ if (base_font < kNumStandardFonts) { ++ if (!m_StandardFaces[base_font]) { ++ m_StandardFaces[base_font] = m_pFontMgr->NewFixedFace( ++ nullptr, m_pFontMgr->GetStandardFont(base_font), 0); + } ++ return m_StandardFaces[base_font]; + } +- pSubstFont->m_bFlagMM = true; +- pSubstFont->m_ItalicAngle = italic_angle; ++ ++ subst_font->SetIsBuiltInGenericFont(); ++ subst_font->m_ItalicAngle = italic_angle; + if (weight) +- pSubstFont->m_Weight = weight; ++ subst_font->m_Weight = weight; + if (FontFamilyIsRoman(pitch_family)) { +- pSubstFont->m_Weight = pSubstFont->m_Weight * 4 / 5; +- pSubstFont->m_Family = "Chrome Serif"; +- if (!m_MMFaces[1]) { +- m_MMFaces[1] = m_pFontMgr->NewFixedFace( +- nullptr, m_pFontMgr->GetBuiltinFont(14).value(), 0); ++ subst_font->UseChromeSerif(); ++ if (!m_GenericSerifFace) { ++ m_GenericSerifFace = m_pFontMgr->NewFixedFace( ++ nullptr, m_pFontMgr->GetGenericSerifFont(), 0); + } +- return m_MMFaces[1]; ++ return m_GenericSerifFace; + } +- pSubstFont->m_Family = "Chrome Sans"; +- if (!m_MMFaces[0]) { +- m_MMFaces[0] = m_pFontMgr->NewFixedFace( +- nullptr, m_pFontMgr->GetBuiltinFont(15).value(), 0); ++ subst_font->m_Family = "Chrome Sans"; ++ if (!m_GenericSansFace) { ++ m_GenericSansFace = ++ m_pFontMgr->NewFixedFace(nullptr, m_pFontMgr->GetGenericSansFont(), 0); + } +- return m_MMFaces[0]; ++ return m_GenericSansFace; ++} ++ ++RetainPtr CFX_FontMapper::UseExternalSubst( ++ void* font_handle, ++ ByteString face_name, ++ int weight, ++ bool is_italic, ++ int italic_angle, ++ FX_Charset charset, ++ CFX_SubstFont* subst_font) { ++ DCHECK(font_handle); ++ ++ ScopedFontDeleter scoped_font(m_pFontInfo.get(), font_handle); ++ m_pFontInfo->GetFaceName(font_handle, &face_name); ++ if (charset == FX_Charset::kDefault) ++ m_pFontInfo->GetFontCharset(font_handle, &charset); ++ size_t ttc_size = m_pFontInfo->GetFontData(font_handle, kTableTTCF, {}); ++ size_t font_size = m_pFontInfo->GetFontData(font_handle, 0, {}); ++ if (font_size == 0 && ttc_size == 0) ++ return nullptr; ++ ++ RetainPtr face = ++ ttc_size ++ ? GetCachedTTCFace(font_handle, ttc_size, font_size) ++ : GetCachedFace(font_handle, face_name, weight, is_italic, font_size); ++ if (!face) ++ return nullptr; ++ ++ subst_font->m_Family = face_name; ++ subst_font->m_Charset = charset; ++ int face_weight = ++ FXFT_Is_Face_Bold(face->GetRec()) ? FXFONT_FW_BOLD : FXFONT_FW_NORMAL; ++ if (weight != face_weight) ++ subst_font->m_Weight = weight; ++ if (is_italic && !FXFT_Is_Face_Italic(face->GetRec())) { ++ if (italic_angle == 0) ++ italic_angle = -12; ++ else if (abs(italic_angle) < 5) ++ italic_angle = 0; ++ subst_font->m_ItalicAngle = italic_angle; ++ } ++ return face; + } + + RetainPtr CFX_FontMapper::FindSubstFont(const ByteString& name, +- bool bTrueType, ++ bool is_truetype, + uint32_t flags, + int weight, + int italic_angle, +- int CharsetCP, +- CFX_SubstFont* pSubstFont) { ++ FX_CodePage code_page, ++ CFX_SubstFont* subst_font) { + if (weight == 0) + weight = FXFONT_FW_NORMAL; + +@@ -396,165 +578,104 @@ RetainPtr CFX_FontMapper::FindSubstFont(const ByteString& name, + weight = FXFONT_FW_NORMAL; + italic_angle = 0; + } +- ByteString SubstName = name; +- SubstName.Remove(' '); +- if (bTrueType && name.GetLength() > 0 && name[0] == '@') +- SubstName = name.Last(name.GetLength() - 1); +- GetStandardFontName(&SubstName); +- if (SubstName == "Symbol" && !bTrueType) { +- pSubstFont->m_Family = "Chrome Symbol"; +- pSubstFont->m_Charset = FX_CHARSET_Symbol; +- return UseInternalSubst(pSubstFont, 12, italic_angle, weight, 0); +- } +- if (SubstName == "ZapfDingbats") { +- pSubstFont->m_Family = "Chrome Dingbats"; +- pSubstFont->m_Charset = FX_CHARSET_Symbol; +- return UseInternalSubst(pSubstFont, 13, italic_angle, weight, 0); +- } +- int iBaseFont = 0; ++ const ByteString subst_name = GetSubstName(name, is_truetype); ++ if (subst_name == "Symbol" && !is_truetype) { ++ subst_font->m_Family = "Chrome Symbol"; ++ subst_font->m_Charset = FX_Charset::kSymbol; ++ return UseInternalSubst(kSymbol, weight, italic_angle, 0, subst_font); ++ } ++ if (subst_name == "ZapfDingbats") { ++ subst_font->m_Family = "Chrome Dingbats"; ++ subst_font->m_Charset = FX_Charset::kSymbol; ++ return UseInternalSubst(kDingbats, weight, italic_angle, 0, subst_font); ++ } ++ int base_font = 0; + ByteString family; + ByteString style; +- bool bHasComma = false; +- bool bHasHyphen = false; ++ bool has_comma = false; ++ bool has_hyphen = false; + { +- Optional pos = SubstName.Find(",", 0); ++ absl::optional pos = subst_name.Find(","); + if (pos.has_value()) { +- family = SubstName.First(pos.value()); ++ family = subst_name.First(pos.value()); + GetStandardFontName(&family); +- style = SubstName.Last(SubstName.GetLength() - (pos.value() + 1)); +- bHasComma = true; ++ style = subst_name.Last(subst_name.GetLength() - (pos.value() + 1)); ++ has_comma = true; + } else { +- family = SubstName; ++ family = subst_name; + } + } +- for (; iBaseFont < 12; iBaseFont++) { +- if (family == ByteStringView(g_Base14FontNames[iBaseFont])) ++ for (; base_font < kSymbol; base_font++) { ++ if (family == kBase14FontNames[base_font]) + break; + } +- int PitchFamily = 0; +- uint32_t nStyle = FXFONT_NORMAL; +- bool bStyleAvail = false; +- if (iBaseFont < 12) { +- if ((iBaseFont % 4) == 1 || (iBaseFont % 4) == 2) +- nStyle |= FXFONT_FORCE_BOLD; +- if ((iBaseFont % 4) / 2) +- nStyle |= FXFONT_ITALIC; +- if (iBaseFont < 4) +- PitchFamily |= FXFONT_FF_FIXEDPITCH; +- if (iBaseFont >= 8) +- PitchFamily |= FXFONT_FF_ROMAN; ++ int pitch_family; ++ uint32_t nStyle; ++ bool is_style_available = false; ++ if (base_font < kSymbol) { ++ nStyle = GetStyleFromBaseFont(base_font); ++ pitch_family = GetPitchFamilyFromBaseFont(base_font); + } else { +- iBaseFont = kNumStandardFonts; +- if (!bHasComma) { +- Optional pos = family.ReverseFind('-'); ++ base_font = kNumStandardFonts; ++ nStyle = FXFONT_NORMAL; ++ if (!has_comma) { ++ absl::optional pos = family.ReverseFind('-'); + if (pos.has_value()) { + style = family.Last(family.GetLength() - (pos.value() + 1)); + family = family.First(pos.value()); +- bHasHyphen = true; ++ has_hyphen = true; + } + } +- if (!bHasHyphen) { +- int nLen = family.GetLength(); +- bool hasStyleType; +- uint32_t styleType; +- size_t len; +- std::tie(hasStyleType, styleType, len) = GetStyleType(family, true); +- if (hasStyleType) { +- family = family.First(nLen - len); +- nStyle |= styleType; ++ if (!has_hyphen) { ++ size_t nLen = family.GetLength(); ++ const FX_FontStyle* style_result = ++ GetStyleType(family.AsStringView(), /*reverse_search=*/true); ++ if (style_result) { ++ family = family.First(nLen - style_result->len); ++ nStyle |= style_result->style; + } + } +- UpdatePitchFamily(flags, &PitchFamily); ++ pitch_family = GetPitchFamilyFromFlags(flags); + } + + const int old_weight = weight; + if (FontStyleIsForceBold(nStyle)) + weight = FXFONT_FW_BOLD; + +- if (!style.IsEmpty()) { +- int nLen = style.GetLength(); +- const char* pStyle = style.c_str(); +- int i = 0; +- bool bFirstItem = true; +- ByteString buf; +- while (i < nLen) { +- buf = ParseStyle(pStyle, nLen, i); +- +- bool hasStyleType; +- uint32_t styleType; +- size_t len; +- std::tie(hasStyleType, styleType, len) = GetStyleType(buf, false); +- if ((i && !bStyleAvail) || (!i && !hasStyleType)) { +- family = SubstName; +- iBaseFont = kNumStandardFonts; +- break; +- } +- if (hasStyleType) +- bStyleAvail = true; +- +- if (FontStyleIsForceBold(styleType)) { +- // If we're already bold, then we're double bold, use special weight. +- if (FontStyleIsForceBold(nStyle)) { +- weight = FXFONT_FW_BOLD_BOLD; +- } else { +- weight = FXFONT_FW_BOLD; +- nStyle |= FXFONT_FORCE_BOLD; +- } +- +- bFirstItem = false; +- } +- if (FontStyleIsItalic(styleType) && FontStyleIsForceBold(styleType)) { +- nStyle |= FXFONT_ITALIC; +- } else if (FontStyleIsItalic(styleType)) { +- if (bFirstItem) { +- nStyle |= FXFONT_ITALIC; +- } else { +- family = SubstName; +- iBaseFont = kNumStandardFonts; +- } +- break; +- } +- i += buf.GetLength() + 1; +- } ++ if (ParseStyles(style, &is_style_available, &weight, &nStyle)) { ++ family = subst_name; ++ base_font = kNumStandardFonts; + } + + if (!m_pFontInfo) { +- return UseInternalSubst(pSubstFont, iBaseFont, italic_angle, old_weight, +- PitchFamily); ++ return UseInternalSubst(base_font, old_weight, italic_angle, pitch_family, ++ subst_font); + } + +- int Charset = FX_CHARSET_ANSI; +- if (CharsetCP) +- Charset = FX_GetCharsetFromCodePage(CharsetCP); +- else if (iBaseFont == kNumStandardFonts && FontStyleIsSymbolic(flags)) +- Charset = FX_CHARSET_Symbol; +- const bool bCJK = FX_CharSetIsCJK(Charset); +- bool bItalic = FontStyleIsItalic(nStyle); ++ const FX_Charset Charset = GetCharset(code_page, base_font, flags); ++ const bool is_cjk = FX_CharSetIsCJK(Charset); ++ bool is_italic = FontStyleIsItalic(nStyle); + + GetFontFamily(nStyle, &family); +- ByteString match = MatchInstalledFonts(TT_NormalizeName(family.c_str())); +- if (match.IsEmpty() && family != SubstName && +- (!bHasComma && (!bHasHyphen || (bHasHyphen && !bStyleAvail)))) { +- match = MatchInstalledFonts(TT_NormalizeName(SubstName.c_str())); +- } +- if (match.IsEmpty() && iBaseFont >= kNumStandardFonts) { +- if (!bCJK) { +- if (!CheckSupportThirdPartFont(family, &PitchFamily)) { +- bItalic = italic_angle != 0; ++ ByteString match = MatchInstalledFonts(TT_NormalizeName(family)); ++ if (match.IsEmpty() && family != subst_name && ++ (!has_comma && (!has_hyphen || (has_hyphen && !is_style_available)))) { ++ match = MatchInstalledFonts(TT_NormalizeName(subst_name)); ++ } ++ if (match.IsEmpty() && base_font >= kNumStandardFonts) { ++ if (!is_cjk) { ++ if (!CheckSupportThirdPartFont(family, &pitch_family)) { ++ is_italic = italic_angle != 0; + weight = old_weight; + } +- Optional pos = SubstName.Find("Narrow"); +- if (pos.has_value() && pos.value() != 0) +- family = kNarrowFamily; +- pos = SubstName.Find("Condensed"); +- if (pos.has_value() && pos.value() != 0) ++ if (IsNarrowFontName(subst_name)) + family = kNarrowFamily; + } else { +- pSubstFont->m_bSubstCJK = true; ++ subst_font->m_bSubstCJK = true; + if (nStyle) +- pSubstFont->m_WeightCJK = nStyle ? weight : FXFONT_FW_NORMAL; ++ subst_font->m_WeightCJK = nStyle ? weight : FXFONT_FW_NORMAL; + if (FontStyleIsItalic(nStyle)) +- pSubstFont->m_bItalicCJK = true; ++ subst_font->m_bItalicCJK = true; + } + } else { + italic_angle = 0; +@@ -562,163 +683,158 @@ RetainPtr CFX_FontMapper::FindSubstFont(const ByteString& name, + weight = FXFONT_FW_NORMAL; + } + +- if (!match.IsEmpty() || iBaseFont < kNumStandardFonts) { ++ if (!match.IsEmpty() || base_font < kNumStandardFonts) { + if (!match.IsEmpty()) + family = match; +- if (iBaseFont < kNumStandardFonts) { +- if (nStyle && !(iBaseFont % 4)) { +- if (FontStyleIsForceBold(nStyle) && FontStyleIsItalic(nStyle)) +- iBaseFont += 2; +- else if (FontStyleIsForceBold(nStyle)) +- iBaseFont += 1; +- else if (FontStyleIsItalic(nStyle)) +- iBaseFont += 3; +- } +- family = g_Base14FontNames[iBaseFont]; ++ if (base_font < kNumStandardFonts) { ++ base_font = AdjustBaseFontForStyle(base_font, nStyle); ++ family = kBase14FontNames[base_font]; + } + } else if (FontStyleIsItalic(flags)) { +- bItalic = true; +- } +- void* hFont = m_pFontInfo->MapFont(weight, bItalic, Charset, PitchFamily, +- family.c_str()); +- if (!hFont) { +- if (bCJK) { +- bItalic = italic_angle != 0; +- weight = old_weight; ++ is_italic = true; ++ } ++ void* font_handle = ++ m_pFontInfo->MapFont(weight, is_italic, Charset, pitch_family, family); ++ if (font_handle) { ++ return UseExternalSubst(font_handle, subst_name, weight, is_italic, ++ italic_angle, Charset, subst_font); ++ } ++ ++ if (is_cjk) { ++ is_italic = italic_angle != 0; ++ weight = old_weight; ++ } ++ if (!match.IsEmpty()) { ++ font_handle = m_pFontInfo->GetFont(match); ++ if (!font_handle) { ++ return UseInternalSubst(base_font, old_weight, italic_angle, pitch_family, ++ subst_font); + } +- if (!match.IsEmpty()) { +- hFont = m_pFontInfo->GetFont(match.c_str()); +- if (!hFont) { +- return UseInternalSubst(pSubstFont, iBaseFont, italic_angle, old_weight, +- PitchFamily); +- } +- } else { +- if (Charset == FX_CHARSET_Symbol) { +-#if defined(OS_MACOSX) || defined(OS_ANDROID) +- if (SubstName == "Symbol") { +- pSubstFont->m_Family = "Chrome Symbol"; +- pSubstFont->m_Charset = FX_CHARSET_Symbol; +- return UseInternalSubst(pSubstFont, 12, italic_angle, old_weight, +- PitchFamily); +- } +-#endif +- return FindSubstFont(family, bTrueType, flags & ~FXFONT_SYMBOLIC, +- weight, italic_angle, 0, pSubstFont); +- } +- if (Charset == FX_CHARSET_ANSI) { +- return UseInternalSubst(pSubstFont, iBaseFont, italic_angle, old_weight, +- PitchFamily); +- } ++ return UseExternalSubst(font_handle, subst_name, weight, is_italic, ++ italic_angle, Charset, subst_font); ++ } + +- auto it = +- std::find_if(m_FaceArray.begin(), m_FaceArray.end(), +- [Charset](const FaceData& face) { +- return face.charset == static_cast(Charset); +- }); +- if (it == m_FaceArray.end()) { +- return UseInternalSubst(pSubstFont, iBaseFont, italic_angle, old_weight, +- PitchFamily); +- } +- hFont = m_pFontInfo->GetFont(it->name.c_str()); ++ if (Charset == FX_Charset::kSymbol) { ++#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID) ++ if (subst_name == "Symbol") { ++ subst_font->m_Family = "Chrome Symbol"; ++ subst_font->m_Charset = FX_Charset::kSymbol; ++ return UseInternalSubst(kSymbol, old_weight, italic_angle, pitch_family, ++ subst_font); + } ++#endif ++ return FindSubstFont(family, is_truetype, flags & ~FXFONT_SYMBOLIC, weight, ++ italic_angle, FX_CodePage::kDefANSI, subst_font); + } +- if (!hFont) +- return nullptr; + +- m_pFontInfo->GetFaceName(hFont, &SubstName); +- if (Charset == FX_CHARSET_Default) +- m_pFontInfo->GetFontCharset(hFont, &Charset); +- uint32_t ttc_size = m_pFontInfo->GetFontData(hFont, kTableTTCF, {}); +- uint32_t font_size = m_pFontInfo->GetFontData(hFont, 0, {}); +- if (font_size == 0 && ttc_size == 0) { +- m_pFontInfo->DeleteFont(hFont); +- return nullptr; ++ if (Charset == FX_Charset::kANSI) { ++ return UseInternalSubst(base_font, old_weight, italic_angle, pitch_family, ++ subst_font); + } +- RetainPtr face; +- if (ttc_size) +- face = GetCachedTTCFace(hFont, ttc_size, font_size); +- else +- face = GetCachedFace(hFont, SubstName, weight, bItalic, font_size); +- if (!face) { +- m_pFontInfo->DeleteFont(hFont); ++ ++ auto it = std::find_if( ++ m_FaceArray.begin(), m_FaceArray.end(), [Charset](const FaceData& face) { ++ return face.charset == static_cast(Charset); ++ }); ++ if (it == m_FaceArray.end()) { ++ return UseInternalSubst(base_font, old_weight, italic_angle, pitch_family, ++ subst_font); ++ } ++ font_handle = m_pFontInfo->GetFont(it->name); ++ if (!font_handle) + return nullptr; ++ return UseExternalSubst(font_handle, subst_name, weight, is_italic, ++ italic_angle, Charset, subst_font); ++} ++ ++size_t CFX_FontMapper::GetFaceSize() const { ++ return m_FaceArray.size(); ++} ++ ++ByteString CFX_FontMapper::GetFaceName(size_t index) const { ++ CHECK_LT(index, m_FaceArray.size()); ++ return m_FaceArray[index].name; ++} ++ ++bool CFX_FontMapper::HasInstalledFont(ByteStringView name) const { ++ for (const auto& font : m_InstalledTTFonts) { ++ if (font == name) ++ return true; + } +- pSubstFont->m_Family = SubstName; +- pSubstFont->m_Charset = Charset; +- bool bNeedUpdateWeight = false; +- if (FXFT_Is_Face_Bold(face->GetRec())) +- bNeedUpdateWeight = weight != FXFONT_FW_BOLD; +- else +- bNeedUpdateWeight = weight != FXFONT_FW_NORMAL; +- if (bNeedUpdateWeight) +- pSubstFont->m_Weight = weight; +- if (bItalic && !FXFT_Is_Face_Italic(face->GetRec())) { +- if (italic_angle == 0) +- italic_angle = -12; +- else if (abs(italic_angle) < 5) +- italic_angle = 0; +- pSubstFont->m_ItalicAngle = italic_angle; ++ return false; ++} ++ ++bool CFX_FontMapper::HasLocalizedFont(ByteStringView name) const { ++ for (const auto& fontPair : m_LocalizedTTFonts) { ++ if (fontPair.first == name) ++ return true; + } +- m_pFontInfo->DeleteFont(hFont); +- return face; ++ return false; ++} ++ ++#if BUILDFLAG(IS_WIN) ++absl::optional CFX_FontMapper::InstalledFontNameStartingWith( ++ const ByteString& name) const { ++ for (const auto& thisname : m_InstalledTTFonts) { ++ if (thisname.First(name.GetLength()) == name) ++ return thisname; ++ } ++ return absl::nullopt; + } + +-int CFX_FontMapper::GetFaceSize() const { +- return pdfium::CollectionSize(m_FaceArray); ++absl::optional CFX_FontMapper::LocalizedFontNameStartingWith( ++ const ByteString& name) const { ++ for (const auto& thispair : m_LocalizedTTFonts) { ++ if (thispair.first.First(name.GetLength()) == name) ++ return thispair.second; ++ } ++ return absl::nullopt; + } ++#endif // BUILDFLAG(IS_WIN) + + #ifdef PDF_ENABLE_XFA +-std::unique_ptr CFX_FontMapper::RawBytesForIndex( +- uint32_t index, +- size_t* returned_length) { +- if (!m_pFontInfo) +- return nullptr; ++FixedUninitDataVector CFX_FontMapper::RawBytesForIndex(size_t index) { ++ CHECK_LT(index, m_FaceArray.size()); + +- void* hFont = m_pFontInfo->MapFont(0, 0, FX_CHARSET_Default, 0, +- GetFaceName(index).c_str()); +- if (!hFont) +- return nullptr; ++ void* font_handle = m_pFontInfo->MapFont(0, false, FX_Charset::kDefault, 0, ++ GetFaceName(index)); ++ if (!font_handle) ++ return FixedUninitDataVector(); + +- uint32_t required_size = m_pFontInfo->GetFontData(hFont, 0, {}); ++ ScopedFontDeleter scoped_font(m_pFontInfo.get(), font_handle); ++ size_t required_size = m_pFontInfo->GetFontData(font_handle, 0, {}); + if (required_size == 0) +- return nullptr; ++ return FixedUninitDataVector(); + +- std::unique_ptr pBuffer( +- FX_Alloc(uint8_t, required_size + 1)); +- *returned_length = +- m_pFontInfo->GetFontData(hFont, 0, {pBuffer.get(), required_size}); +- return pBuffer; +-} +-#endif // PDF_ENABLE_XFA ++ FixedUninitDataVector result(required_size); ++ size_t actual_size = ++ m_pFontInfo->GetFontData(font_handle, 0, result.writable_span()); ++ if (actual_size != required_size) ++ return FixedUninitDataVector(); + +-bool CFX_FontMapper::IsBuiltinFace(const RetainPtr& face) const { +- for (size_t i = 0; i < MM_FACE_COUNT; ++i) { +- if (m_MMFaces[i] == face) +- return true; +- } +- for (size_t i = 0; i < FOXIT_FACE_COUNT; ++i) { +- if (m_FoxitFaces[i] == face) +- return true; +- } +- return false; ++ return result; + } ++#endif // PDF_ENABLE_XFA + +-RetainPtr CFX_FontMapper::GetCachedTTCFace(void* hFont, +- uint32_t ttc_size, +- uint32_t font_size) { +- uint32_t checksum = GetChecksumFromTT(hFont); ++RetainPtr CFX_FontMapper::GetCachedTTCFace(void* font_handle, ++ size_t ttc_size, ++ size_t data_size) { ++ CHECK_GE(ttc_size, data_size); ++ uint32_t checksum = GetChecksumFromTT(font_handle); + RetainPtr pFontDesc = + m_pFontMgr->GetCachedTTCFontDesc(ttc_size, checksum); + if (!pFontDesc) { +- std::unique_ptr pFontData( +- FX_Alloc(uint8_t, ttc_size)); +- m_pFontInfo->GetFontData(hFont, kTableTTCF, {pFontData.get(), ttc_size}); +- pFontDesc = m_pFontMgr->AddCachedTTCFontDesc( +- ttc_size, checksum, std::move(pFontData), ttc_size); +- } +- ASSERT(ttc_size >= font_size); +- uint32_t font_offset = ttc_size - font_size; +- int face_index = ++ FixedUninitDataVector font_data(ttc_size); ++ size_t size = m_pFontInfo->GetFontData(font_handle, kTableTTCF, ++ font_data.writable_span()); ++ if (size != ttc_size) ++ return nullptr; ++ ++ pFontDesc = m_pFontMgr->AddCachedTTCFontDesc(ttc_size, checksum, ++ std::move(font_data)); ++ } ++ size_t font_offset = ttc_size - data_size; ++ size_t face_index = + GetTTCIndex(pFontDesc->FontData().first(ttc_size), font_offset); + RetainPtr pFace(pFontDesc->GetFace(face_index)); + if (pFace) +@@ -733,27 +849,29 @@ RetainPtr CFX_FontMapper::GetCachedTTCFace(void* hFont, + return pFace; + } + +-RetainPtr CFX_FontMapper::GetCachedFace(void* hFont, +- ByteString SubstName, ++RetainPtr CFX_FontMapper::GetCachedFace(void* font_handle, ++ ByteString subst_name, + int weight, +- bool bItalic, +- uint32_t font_size) { ++ bool is_italic, ++ size_t data_size) { + RetainPtr pFontDesc = +- m_pFontMgr->GetCachedFontDesc(SubstName, weight, bItalic); ++ m_pFontMgr->GetCachedFontDesc(subst_name, weight, is_italic); + if (!pFontDesc) { +- std::unique_ptr pFontData( +- FX_Alloc(uint8_t, font_size)); +- m_pFontInfo->GetFontData(hFont, 0, {pFontData.get(), font_size}); +- pFontDesc = m_pFontMgr->AddCachedFontDesc(SubstName, weight, bItalic, +- std::move(pFontData), font_size); ++ FixedUninitDataVector font_data(data_size); ++ size_t size = ++ m_pFontInfo->GetFontData(font_handle, 0, font_data.writable_span()); ++ if (size != data_size) ++ return nullptr; ++ ++ pFontDesc = m_pFontMgr->AddCachedFontDesc(subst_name, weight, is_italic, ++ std::move(font_data)); + } + RetainPtr pFace(pFontDesc->GetFace(0)); + if (pFace) + return pFace; + + pFace = m_pFontMgr->NewFixedFace(pFontDesc, +- pFontDesc->FontData().first(font_size), +- m_pFontInfo->GetFaceIndex(hFont)); ++ pFontDesc->FontData().first(data_size), 0); + if (!pFace) + return nullptr; + +@@ -762,21 +880,26 @@ RetainPtr CFX_FontMapper::GetCachedFace(void* hFont, + } + + // static +-Optional CFX_FontMapper::GetStandardFontName( +- ByteString* name) { +- const auto* end = std::end(g_AltFontNames); ++absl::optional ++CFX_FontMapper::GetStandardFontName(ByteString* name) { ++ const auto* end = std::end(kAltFontNames); + const auto* found = +- std::lower_bound(std::begin(g_AltFontNames), end, name->c_str(), ++ std::lower_bound(std::begin(kAltFontNames), end, name->c_str(), + [](const AltFontName& element, const char* name) { + return FXSYS_stricmp(element.m_pName, name) < 0; + }); + if (found == end || FXSYS_stricmp(found->m_pName, name->c_str())) +- return {}; ++ return absl::nullopt; + +- *name = g_Base14FontNames[static_cast(found->m_Index)]; ++ *name = kBase14FontNames[static_cast(found->m_Index)]; + return found->m_Index; + } + ++// static ++bool CFX_FontMapper::IsStandardFontName(const ByteString& name) { ++ return pdfium::Contains(kBase14FontNames, name); ++} ++ + // static + bool CFX_FontMapper::IsSymbolicFont(StandardFont font) { + return font == StandardFont::kSymbol || font == StandardFont::kDingbats; +diff --git a/core/fxge/cfx_fontmapper.h b/core/fxge/cfx_fontmapper.h +index c21c6cb4d..aa8f671c8 100644 +--- a/core/fxge/cfx_fontmapper.h ++++ b/core/fxge/cfx_fontmapper.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,10 +11,17 @@ + #include + #include + +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxcrt/fx_string.h" ++#include "build/build_config.h" ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/fx_codepage_forward.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxcrt/unowned_ptr.h" + #include "core/fxge/cfx_face.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++ ++#ifdef PDF_ENABLE_XFA ++#include "core/fxcrt/fixed_uninit_data_vector.h" ++#endif + + class CFX_FontMgr; + class CFX_SubstFont; +@@ -22,7 +29,7 @@ class SystemFontInfoIface; + + class CFX_FontMapper { + public: +- enum StandardFont { ++ enum StandardFont : uint8_t { + kCourier = 0, + kCourierBold, + kCourierBoldOblique, +@@ -37,12 +44,15 @@ class CFX_FontMapper { + kTimesOblique, + kSymbol, + kDingbats, ++ kLast = kDingbats + }; ++ static constexpr int kNumStandardFonts = 14; + + explicit CFX_FontMapper(CFX_FontMgr* mgr); + ~CFX_FontMapper(); + +- static Optional GetStandardFontName(ByteString* name); ++ static absl::optional GetStandardFontName(ByteString* name); ++ static bool IsStandardFontName(const ByteString& name); + static bool IsSymbolicFont(StandardFont font); + static bool IsFixedFont(StandardFont font); + static constexpr uint32_t MakeTag(char c1, char c2, char c3, char c4) { +@@ -51,50 +61,62 @@ class CFX_FontMapper { + } + + void SetSystemFontInfo(std::unique_ptr pFontInfo); +- void AddInstalledFont(const ByteString& name, int charset); ++ std::unique_ptr TakeSystemFontInfo(); ++ void AddInstalledFont(const ByteString& name, FX_Charset charset); + void LoadInstalledFonts(); + + RetainPtr FindSubstFont(const ByteString& face_name, +- bool bTrueType, ++ bool is_truetype, + uint32_t flags, + int weight, + int italic_angle, +- int CharsetCP, +- CFX_SubstFont* pSubstFont); +- +- bool IsBuiltinFace(const RetainPtr& face) const; +- int GetFaceSize() const; +- ByteString GetFaceName(int index) const { return m_FaceArray[index].name; } ++ FX_CodePage code_page, ++ CFX_SubstFont* subst_font); ++ ++ size_t GetFaceSize() const; ++ // `index` must be less than GetFaceSize(). ++ ByteString GetFaceName(size_t index) const; ++ bool HasInstalledFont(ByteStringView name) const; ++ bool HasLocalizedFont(ByteStringView name) const; ++ ++#if BUILDFLAG(IS_WIN) ++ absl::optional InstalledFontNameStartingWith( ++ const ByteString& name) const; ++ absl::optional LocalizedFontNameStartingWith( ++ const ByteString& name) const; ++#endif // BUILDFLAG(IS_WIN) + + #ifdef PDF_ENABLE_XFA +- std::unique_ptr RawBytesForIndex( +- uint32_t index, +- size_t* returned_length); ++ // `index` must be less than GetFaceSize(). ++ FixedUninitDataVector RawBytesForIndex(size_t index); + #endif // PDF_ENABLE_XFA + +- std::vector m_InstalledTTFonts; +- std::vector> m_LocalizedTTFonts; +- + private: +- static const size_t MM_FACE_COUNT = 2; +- static const size_t FOXIT_FACE_COUNT = 14; ++ friend class TestFontMapper; + +- uint32_t GetChecksumFromTT(void* hFont); +- ByteString GetPSNameFromTT(void* hFont); ++ uint32_t GetChecksumFromTT(void* font_handle); ++ ByteString GetPSNameFromTT(void* font_handle); + ByteString MatchInstalledFonts(const ByteString& norm_name); +- RetainPtr UseInternalSubst(CFX_SubstFont* pSubstFont, +- int iBaseFont, ++ RetainPtr UseInternalSubst(int base_font, ++ int weight, + int italic_angle, ++ int pitch_family, ++ CFX_SubstFont* subst_font); ++ RetainPtr UseExternalSubst(void* font_handle, ++ ByteString face_name, + int weight, +- int pitch_family); +- RetainPtr GetCachedTTCFace(void* hFont, +- uint32_t ttc_size, +- uint32_t font_size); +- RetainPtr GetCachedFace(void* hFont, +- ByteString SubstName, ++ bool is_italic, ++ int italic_angle, ++ FX_Charset charset, ++ CFX_SubstFont* subst_font); ++ RetainPtr GetCachedTTCFace(void* font_handle, ++ size_t ttc_size, ++ size_t data_size); ++ RetainPtr GetCachedFace(void* font_handle, ++ ByteString subst_name, + int weight, +- bool bItalic, +- uint32_t font_size); ++ bool is_italic, ++ size_t data_size); + + struct FaceData { + ByteString name; +@@ -106,8 +128,11 @@ class CFX_FontMapper { + std::vector m_FaceArray; + std::unique_ptr m_pFontInfo; + UnownedPtr const m_pFontMgr; +- RetainPtr m_MMFaces[MM_FACE_COUNT]; +- RetainPtr m_FoxitFaces[FOXIT_FACE_COUNT]; ++ std::vector m_InstalledTTFonts; ++ std::vector> m_LocalizedTTFonts; ++ RetainPtr m_StandardFaces[kNumStandardFonts]; ++ RetainPtr m_GenericSansFace; ++ RetainPtr m_GenericSerifFace; + }; + + #endif // CORE_FXGE_CFX_FONTMAPPER_H_ +diff --git a/core/fxge/cfx_fontmapper_unittest.cpp b/core/fxge/cfx_fontmapper_unittest.cpp +index e69387d37..ab0c85865 100644 +--- a/core/fxge/cfx_fontmapper_unittest.cpp ++++ b/core/fxge/cfx_fontmapper_unittest.cpp +@@ -1,14 +1,116 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxge/cfx_fontmapper.h" + ++#include ++#include ++#include ++ ++#include "core/fxcrt/fx_codepage.h" ++#include "core/fxge/cfx_gemodule.h" ++#include "core/fxge/systemfontinfo_iface.h" ++#include "testing/gmock/include/gmock/gmock.h" + #include "testing/gtest/include/gtest/gtest.h" + ++using testing::_; ++using testing::DoAll; ++using testing::ElementsAre; ++using testing::InSequence; ++using testing::Invoke; ++using testing::Return; ++using testing::WithArg; ++ ++class MockSystemFontInfo : public SystemFontInfoIface { ++ public: ++ MockSystemFontInfo() = default; ++ ~MockSystemFontInfo() override = default; ++ ++ // SystemFontInfoIface: ++ MOCK_METHOD(bool, EnumFontList, (CFX_FontMapper*), (override)); ++ MOCK_METHOD(void*, ++ MapFont, ++ (int, bool, FX_Charset, int, const ByteString&), ++ (override)); ++ MOCK_METHOD(void*, GetFont, (const ByteString&), (override)); ++ MOCK_METHOD(size_t, ++ GetFontData, ++ (void*, uint32_t, pdfium::span), ++ (override)); ++ MOCK_METHOD(bool, GetFaceName, (void*, ByteString*), (override)); ++ MOCK_METHOD(bool, GetFontCharset, (void*, FX_Charset*), (override)); ++ MOCK_METHOD(void, DeleteFont, (void*), (override)); ++}; ++ ++// Class that exposes private CFX_FontMapper methods. ++class TestFontMapper : public CFX_FontMapper { ++ public: ++ TestFontMapper() : CFX_FontMapper(CFX_GEModule::Get()->GetFontMgr()) {} ++ ++ RetainPtr GetCachedTTCFace(void* font_handle, ++ size_t ttc_size, ++ size_t data_size) { ++ return CFX_FontMapper::GetCachedTTCFace(font_handle, ttc_size, data_size); ++ } ++ ++ RetainPtr GetCachedFace(void* font_handle, ++ ByteString subst_name, ++ int weight, ++ bool is_italic, ++ size_t data_size) { ++ return CFX_FontMapper::GetCachedFace(font_handle, subst_name, weight, ++ is_italic, data_size); ++ } ++}; ++ ++class CFXFontMapperSystemFontInfoTest : public testing::Test { ++ protected: ++ CFXFontMapperSystemFontInfoTest() = default; ++ ~CFXFontMapperSystemFontInfoTest() override = default; ++ ++ void SetUp() override { ++ font_mapper_ = std::make_unique(); ++ auto system_font_info = std::make_unique(); ++ system_font_info_ = system_font_info.get(); ++ font_mapper_->SetSystemFontInfo(std::move(system_font_info)); ++ font_mapper_->AddInstalledFont("dummy", FX_Charset::kANSI); ++ } ++ ++ TestFontMapper& font_mapper() { return *font_mapper_; } ++ MockSystemFontInfo& system_font_info() { return *system_font_info_; } ++ ++ private: ++ // Must outlive `system_font_info_`. ++ std::unique_ptr font_mapper_; ++ UnownedPtr system_font_info_; ++}; ++ + // Deliberately give this global variable external linkage. + char g_maybe_changes = '\xff'; + ++TEST(CFX_FontMapper, IsStandardFontName) { ++ EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Courier")); ++ EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Courier-Bold")); ++ EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Courier-BoldOblique")); ++ EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Courier-Oblique")); ++ EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Helvetica")); ++ EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Helvetica-Bold")); ++ EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Helvetica-BoldOblique")); ++ EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Helvetica-Oblique")); ++ EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Times-Roman")); ++ EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Times-Bold")); ++ EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Times-BoldItalic")); ++ EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Times-Italic")); ++ EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("Symbol")); ++ EXPECT_TRUE(CFX_FontMapper::IsStandardFontName("ZapfDingbats")); ++ ++ EXPECT_FALSE(CFX_FontMapper::IsStandardFontName("Courie")); ++ EXPECT_FALSE(CFX_FontMapper::IsStandardFontName("Courier-")); ++ EXPECT_FALSE(CFX_FontMapper::IsStandardFontName("Helvetica+Bold")); ++ EXPECT_FALSE(CFX_FontMapper::IsStandardFontName("YapfDingbats")); ++} ++ + TEST(CFX_FontMapper, MakeTag) { + EXPECT_EQ(0x61626364u, CFX_FontMapper::MakeTag('a', 'b', 'c', 'd')); + EXPECT_EQ(0x00000000u, CFX_FontMapper::MakeTag('\0', '\0', '\0', '\0')); +@@ -17,4 +119,117 @@ TEST(CFX_FontMapper, MakeTag) { + CFX_FontMapper::MakeTag('\xff', '\xff', '\xff', '\xff')); + EXPECT_EQ(0xffffffffu, + CFX_FontMapper::MakeTag(g_maybe_changes, '\xff', '\xff', '\xff')); ++ EXPECT_EQ(0x6e616d65u, CFX_FontMapper::MakeTag('n', 'a', 'm', 'e')); ++ EXPECT_EQ(0x4f532f32u, CFX_FontMapper::MakeTag('O', 'S', '/', '2')); ++ EXPECT_EQ(FT_MAKE_TAG('G', 'S', 'U', 'B'), ++ CFX_FontMapper::MakeTag('G', 'S', 'U', 'B')); ++} ++ ++TEST(CFX_FontMapper, AddInstalledFontBasic) { ++ const char kFontName[] = "dummy"; ++ CFX_FontMapper font_mapper(nullptr); ++ font_mapper.SetSystemFontInfo(std::make_unique()); ++ ++ font_mapper.AddInstalledFont(kFontName, FX_Charset::kANSI); ++ EXPECT_EQ(1u, font_mapper.GetFaceSize()); ++ EXPECT_EQ(kFontName, font_mapper.GetFaceName(0)); ++} ++ ++#ifdef PDF_ENABLE_XFA ++TEST_F(CFXFontMapperSystemFontInfoTest, RawBytesForIndex) { ++ { ++ void* const kFontHandle = reinterpret_cast(12345); ++ ++ InSequence s; ++ EXPECT_CALL(system_font_info(), MapFont).WillOnce(Return(kFontHandle)); ++ EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _)) ++ .WillOnce(Return(2)); ++ EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _)) ++ .WillOnce(DoAll(WithArg<2>(Invoke([](pdfium::span buffer) { ++ buffer[0] = '0'; ++ buffer[1] = '1'; ++ })), ++ Return(2))); ++ EXPECT_CALL(system_font_info(), DeleteFont(kFontHandle)); ++ } ++ ++ FixedUninitDataVector data = font_mapper().RawBytesForIndex(0); ++ EXPECT_THAT(data.span(), ElementsAre('0', '1')); ++} ++ ++TEST_F(CFXFontMapperSystemFontInfoTest, RawBytesForIndexFailToMap) { ++ EXPECT_CALL(system_font_info(), MapFont).WillOnce(Return(nullptr)); ++ ++ FixedUninitDataVector data = font_mapper().RawBytesForIndex(0); ++ EXPECT_TRUE(data.empty()); ++} ++ ++TEST_F(CFXFontMapperSystemFontInfoTest, RawBytesForIndexFailToGetDataSize) { ++ { ++ void* const kFontHandle = reinterpret_cast(12345); ++ ++ InSequence s; ++ EXPECT_CALL(system_font_info(), MapFont).WillOnce(Return(kFontHandle)); ++ EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _)) ++ .WillOnce(Return(0)); ++ EXPECT_CALL(system_font_info(), DeleteFont(kFontHandle)); ++ } ++ ++ FixedUninitDataVector data = font_mapper().RawBytesForIndex(0); ++ EXPECT_TRUE(data.empty()); ++} ++ ++TEST_F(CFXFontMapperSystemFontInfoTest, RawBytesForIndexFailToGetData) { ++ { ++ void* const kFontHandle = reinterpret_cast(12345); ++ ++ InSequence s; ++ EXPECT_CALL(system_font_info(), MapFont).WillOnce(Return(kFontHandle)); ++ EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _)) ++ .WillOnce(Return(2)); ++ EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _)) ++ .WillOnce(Return(0)); ++ EXPECT_CALL(system_font_info(), DeleteFont(kFontHandle)); ++ } ++ ++ FixedUninitDataVector data = font_mapper().RawBytesForIndex(0); ++ EXPECT_TRUE(data.empty()); ++} ++#endif // PDF_ENABLE_XFA ++ ++// Regression test for crbug.com/1372234 - should not crash. ++TEST_F(CFXFontMapperSystemFontInfoTest, GetCachedTTCFaceFailToGetData) { ++ void* const kFontHandle = reinterpret_cast(12345); ++ constexpr size_t kTtcSize = 1024; ++ constexpr size_t kDataSize = 2; ++ ++ { ++ InSequence s; ++ EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, kTableTTCF, _)) ++ .WillOnce(DoAll(WithArg<2>(Invoke([&](pdfium::span buffer) { ++ EXPECT_EQ(kTtcSize, buffer.size()); ++ std::iota(buffer.begin(), buffer.end(), 0); ++ })), ++ Return(kTtcSize))); ++ EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, kTableTTCF, _)) ++ .WillOnce(Return(0)); ++ } ++ ++ EXPECT_FALSE( ++ font_mapper().GetCachedTTCFace(kFontHandle, kTtcSize, kDataSize)); ++} ++ ++// Regression test for crbug.com/1372234 - should not crash. ++TEST_F(CFXFontMapperSystemFontInfoTest, GetCachedFaceFailToGetData) { ++ void* const kFontHandle = reinterpret_cast(12345); ++ constexpr char kSubstName[] = "dummy_font"; ++ constexpr int kWeight = 400; ++ constexpr bool kItalic = false; ++ constexpr size_t kDataSize = 2; ++ ++ EXPECT_CALL(system_font_info(), GetFontData(kFontHandle, 0, _)) ++ .WillOnce(Return(0)); ++ ++ EXPECT_FALSE(font_mapper().GetCachedFace(kFontHandle, kSubstName, kWeight, ++ kItalic, kDataSize)); + } +diff --git a/core/fxge/cfx_fontmgr.cpp b/core/fxge/cfx_fontmgr.cpp +index 7a08d0f6b..e52979eee 100644 +--- a/core/fxge/cfx_fontmgr.cpp ++++ b/core/fxge/cfx_fontmgr.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,16 +6,17 @@ + + #include "core/fxge/cfx_fontmgr.h" + ++#include + #include + #include + +-#include "core/fxge/cfx_face.h" ++#include "core/fxcrt/fixed_uninit_data_vector.h" + #include "core/fxge/cfx_fontmapper.h" + #include "core/fxge/cfx_substfont.h" + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + #include "core/fxge/fx_font.h" + #include "core/fxge/systemfontinfo_iface.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check_op.h" + + namespace { + +@@ -24,41 +25,27 @@ struct BuiltinFont { + uint32_t m_dwSize; + }; + +-const BuiltinFont g_FoxitFonts[14] = { +- {g_FoxitFixedFontData, 17597}, +- {g_FoxitFixedBoldFontData, 18055}, +- {g_FoxitFixedBoldItalicFontData, 19151}, +- {g_FoxitFixedItalicFontData, 18746}, +- {g_FoxitSansFontData, 15025}, +- {g_FoxitSansBoldFontData, 16344}, +- {g_FoxitSansBoldItalicFontData, 16418}, +- {g_FoxitSansItalicFontData, 16339}, +- {g_FoxitSerifFontData, 19469}, +- {g_FoxitSerifBoldFontData, 19395}, +- {g_FoxitSerifBoldItalicFontData, 20733}, +- {g_FoxitSerifItalicFontData, 21227}, +- {g_FoxitSymbolFontData, 16729}, +- {g_FoxitDingbatsFontData, 29513}, ++constexpr BuiltinFont kFoxitFonts[] = { ++ {kFoxitFixedFontData, 17597}, ++ {kFoxitFixedBoldFontData, 18055}, ++ {kFoxitFixedBoldItalicFontData, 19151}, ++ {kFoxitFixedItalicFontData, 18746}, ++ {kFoxitSansFontData, 15025}, ++ {kFoxitSansBoldFontData, 16344}, ++ {kFoxitSansBoldItalicFontData, 16418}, ++ {kFoxitSansItalicFontData, 16339}, ++ {kFoxitSerifFontData, 19469}, ++ {kFoxitSerifBoldFontData, 19395}, ++ {kFoxitSerifBoldItalicFontData, 20733}, ++ {kFoxitSerifItalicFontData, 21227}, ++ {kFoxitSymbolFontData, 16729}, ++ {kFoxitDingbatsFontData, 29513}, + }; ++static_assert(std::size(kFoxitFonts) == CFX_FontMapper::kNumStandardFonts, ++ "Wrong font count"); + +-const BuiltinFont g_MMFonts[2] = { +- {g_FoxitSerifMMFontData, 113417}, +- {g_FoxitSansMMFontData, 66919}, +-}; +- +-ByteString KeyNameFromFace(const ByteString& face_name, +- int weight, +- bool bItalic) { +- ByteString key(face_name); +- key += ','; +- key += ByteString::FormatInteger(weight); +- key += bItalic ? 'I' : 'N'; +- return key; +-} +- +-ByteString KeyNameFromSize(int ttc_size, uint32_t checksum) { +- return ByteString::Format("%d:%d", ttc_size, checksum); +-} ++constexpr BuiltinFont kGenericSansFont = {kFoxitSansMMFontData, 66919}; ++constexpr BuiltinFont kGenericSerifFont = {kFoxitSerifMMFontData, 113417}; + + FXFT_LibraryRec* FTLibraryInitHelper() { + FXFT_LibraryRec* pLibrary = nullptr; +@@ -68,51 +55,34 @@ FXFT_LibraryRec* FTLibraryInitHelper() { + + } // namespace + +-CFX_FontMgr::FontDesc::FontDesc(std::unique_ptr pData, +- size_t size) +- : m_Size(size), m_pFontData(std::move(pData)) {} ++CFX_FontMgr::FontDesc::FontDesc(FixedUninitDataVector data) ++ : m_pFontData(std::move(data)) {} + + CFX_FontMgr::FontDesc::~FontDesc() = default; + + void CFX_FontMgr::FontDesc::SetFace(size_t index, CFX_Face* face) { +- ASSERT(index < FX_ArraySize(m_TTCFaces)); ++ CHECK_LT(index, std::size(m_TTCFaces)); + m_TTCFaces[index].Reset(face); + } + + CFX_Face* CFX_FontMgr::FontDesc::GetFace(size_t index) const { +- ASSERT(index < FX_ArraySize(m_TTCFaces)); ++ CHECK_LT(index, std::size(m_TTCFaces)); + return m_TTCFaces[index].Get(); + } + + CFX_FontMgr::CFX_FontMgr() + : m_FTLibrary(FTLibraryInitHelper()), +- m_pBuiltinMapper(pdfium::MakeUnique(this)), ++ m_pBuiltinMapper(std::make_unique(this)), + m_FTLibrarySupportsHinting(SetLcdFilterMode() || + FreeTypeVersionSupportsHinting()) {} + + CFX_FontMgr::~CFX_FontMgr() = default; + +-void CFX_FontMgr::SetSystemFontInfo( +- std::unique_ptr pFontInfo) { +- m_pBuiltinMapper->SetSystemFontInfo(std::move(pFontInfo)); +-} +- +-RetainPtr CFX_FontMgr::FindSubstFont(const ByteString& face_name, +- bool bTrueType, +- uint32_t flags, +- int weight, +- int italic_angle, +- int CharsetCP, +- CFX_SubstFont* pSubstFont) { +- return m_pBuiltinMapper->FindSubstFont(face_name, bTrueType, flags, weight, +- italic_angle, CharsetCP, pSubstFont); +-} +- + RetainPtr CFX_FontMgr::GetCachedFontDesc( + const ByteString& face_name, + int weight, + bool bItalic) { +- auto it = m_FaceMap.find(KeyNameFromFace(face_name, weight, bItalic)); ++ auto it = m_FaceMap.find({face_name, weight, bItalic}); + return it != m_FaceMap.end() ? pdfium::WrapRetain(it->second.Get()) : nullptr; + } + +@@ -120,35 +90,35 @@ RetainPtr CFX_FontMgr::AddCachedFontDesc( + const ByteString& face_name, + int weight, + bool bItalic, +- std::unique_ptr pData, +- uint32_t size) { +- auto pFontDesc = pdfium::MakeRetain(std::move(pData), size); +- m_FaceMap[KeyNameFromFace(face_name, weight, bItalic)].Reset(pFontDesc.Get()); ++ FixedUninitDataVector data) { ++ auto pFontDesc = pdfium::MakeRetain(std::move(data)); ++ m_FaceMap[{face_name, weight, bItalic}].Reset(pFontDesc.Get()); + return pFontDesc; + } + + RetainPtr CFX_FontMgr::GetCachedTTCFontDesc( +- int ttc_size, ++ size_t ttc_size, + uint32_t checksum) { +- auto it = m_FaceMap.find(KeyNameFromSize(ttc_size, checksum)); +- return it != m_FaceMap.end() ? pdfium::WrapRetain(it->second.Get()) : nullptr; ++ auto it = m_TTCFaceMap.find({ttc_size, checksum}); ++ return it != m_TTCFaceMap.end() ? pdfium::WrapRetain(it->second.Get()) ++ : nullptr; + } + + RetainPtr CFX_FontMgr::AddCachedTTCFontDesc( +- int ttc_size, ++ size_t ttc_size, + uint32_t checksum, +- std::unique_ptr pData, +- uint32_t size) { +- auto pNewDesc = pdfium::MakeRetain(std::move(pData), size); +- m_FaceMap[KeyNameFromSize(ttc_size, checksum)].Reset(pNewDesc.Get()); ++ FixedUninitDataVector data) { ++ auto pNewDesc = pdfium::MakeRetain(std::move(data)); ++ m_TTCFaceMap[{ttc_size, checksum}].Reset(pNewDesc.Get()); + return pNewDesc; + } + +-RetainPtr CFX_FontMgr::NewFixedFace(const RetainPtr& pDesc, ++RetainPtr CFX_FontMgr::NewFixedFace(RetainPtr pDesc, + pdfium::span span, +- int face_index) { ++ size_t face_index) { + RetainPtr face = +- CFX_Face::New(m_FTLibrary.get(), pDesc, span, face_index); ++ CFX_Face::New(m_FTLibrary.get(), std::move(pDesc), span, ++ static_cast(face_index)); + if (!face) + return nullptr; + +@@ -159,18 +129,22 @@ RetainPtr CFX_FontMgr::NewFixedFace(const RetainPtr& pDesc, + } + + // static +-Optional> CFX_FontMgr::GetBuiltinFont( +- size_t index) { +- if (index < FX_ArraySize(g_FoxitFonts)) { +- return pdfium::make_span(g_FoxitFonts[index].m_pFontData, +- g_FoxitFonts[index].m_dwSize); +- } +- size_t mm_index = index - FX_ArraySize(g_FoxitFonts); +- if (mm_index < FX_ArraySize(g_MMFonts)) { +- return pdfium::make_span(g_MMFonts[mm_index].m_pFontData, +- g_MMFonts[mm_index].m_dwSize); +- } +- return {}; ++pdfium::span CFX_FontMgr::GetStandardFont(size_t index) { ++ CHECK_LT(index, std::size(kFoxitFonts)); ++ return pdfium::make_span(kFoxitFonts[index].m_pFontData, ++ kFoxitFonts[index].m_dwSize); ++} ++ ++// static ++pdfium::span CFX_FontMgr::GetGenericSansFont() { ++ return pdfium::make_span(kGenericSansFont.m_pFontData, ++ kGenericSansFont.m_dwSize); ++} ++ ++// static ++pdfium::span CFX_FontMgr::GetGenericSerifFont() { ++ return pdfium::make_span(kGenericSerifFont.m_pFontData, ++ kGenericSerifFont.m_dwSize); + } + + bool CFX_FontMgr::FreeTypeVersionSupportsHinting() const { +diff --git a/core/fxge/cfx_fontmgr.h b/core/fxge/cfx_fontmgr.h +index 144961e35..31539aaad 100644 +--- a/core/fxge/cfx_fontmgr.h ++++ b/core/fxge/cfx_fontmgr.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,46 +7,45 @@ + #ifndef CORE_FXGE_CFX_FONTMGR_H_ + #define CORE_FXGE_CFX_FONTMGR_H_ + ++#include ++#include ++ + #include + #include ++#include + +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/fixed_uninit_data_vector.h" + #include "core/fxcrt/observed_ptr.h" + #include "core/fxcrt/retain_ptr.h" +-#include "core/fxge/fx_freetype.h" +-#include "third_party/base/optional.h" ++#include "core/fxge/cfx_face.h" ++#include "core/fxge/freetype/fx_freetype.h" + #include "third_party/base/span.h" + +-class CFX_Face; + class CFX_FontMapper; +-class CFX_SubstFont; +-class SystemFontInfoIface; + + class CFX_FontMgr { + public: + class FontDesc final : public Retainable, public Observable { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- ++ CONSTRUCT_VIA_MAKE_RETAIN; + ~FontDesc() override; + +- pdfium::span FontData() const { +- return {m_pFontData.get(), m_Size}; +- } ++ pdfium::span FontData() const { return m_pFontData; } + void SetFace(size_t index, CFX_Face* face); + CFX_Face* GetFace(size_t index) const; + + private: +- FontDesc(std::unique_ptr pData, size_t size); ++ explicit FontDesc(FixedUninitDataVector data); + +- const size_t m_Size; +- std::unique_ptr const m_pFontData; ++ const FixedUninitDataVector m_pFontData; + ObservedPtr m_TTCFaces[16]; + }; + +- static Optional> GetBuiltinFont(size_t index); ++ // `index` must be less than `CFX_FontMapper::kNumStandardFonts`. ++ static pdfium::span GetStandardFont(size_t index); ++ static pdfium::span GetGenericSansFont(); ++ static pdfium::span GetGenericSerifFont(); + + CFX_FontMgr(); + ~CFX_FontMgr(); +@@ -54,32 +53,19 @@ class CFX_FontMgr { + RetainPtr GetCachedFontDesc(const ByteString& face_name, + int weight, + bool bItalic); +- RetainPtr AddCachedFontDesc( +- const ByteString& face_name, +- int weight, +- bool bItalic, +- std::unique_ptr pData, +- uint32_t size); +- +- RetainPtr GetCachedTTCFontDesc(int ttc_size, uint32_t checksum); +- RetainPtr AddCachedTTCFontDesc( +- int ttc_size, +- uint32_t checksum, +- std::unique_ptr pData, +- uint32_t size); +- +- RetainPtr NewFixedFace(const RetainPtr& pDesc, +- pdfium::span span, +- int face_index); +- RetainPtr FindSubstFont(const ByteString& face_name, +- bool bTrueType, +- uint32_t flags, +- int weight, +- int italic_angle, +- int CharsetCP, +- CFX_SubstFont* pSubstFont); ++ RetainPtr AddCachedFontDesc(const ByteString& face_name, ++ int weight, ++ bool bItalic, ++ FixedUninitDataVector data); ++ ++ RetainPtr GetCachedTTCFontDesc(size_t ttc_size, uint32_t checksum); ++ RetainPtr AddCachedTTCFontDesc(size_t ttc_size, ++ uint32_t checksum, ++ FixedUninitDataVector data); + +- void SetSystemFontInfo(std::unique_ptr pFontInfo); ++ RetainPtr NewFixedFace(RetainPtr pDesc, ++ pdfium::span span, ++ size_t face_index); + + // Always present. + CFX_FontMapper* GetBuiltinMapper() const { return m_pBuiltinMapper.get(); } +@@ -94,7 +80,8 @@ class CFX_FontMgr { + // Must come before |m_pBuiltinMapper| and |m_FaceMap|. + ScopedFXFTLibraryRec const m_FTLibrary; + std::unique_ptr m_pBuiltinMapper; +- std::map> m_FaceMap; ++ std::map, ObservedPtr> m_FaceMap; ++ std::map, ObservedPtr> m_TTCFaceMap; + const bool m_FTLibrarySupportsHinting; + }; + +diff --git a/core/fxge/cfx_gemodule.cpp b/core/fxge/cfx_gemodule.cpp +index 5d268bf8b..dc06b9dfb 100644 +--- a/core/fxge/cfx_gemodule.cpp ++++ b/core/fxge/cfx_gemodule.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,7 +9,7 @@ + #include "core/fxge/cfx_folderfontinfo.h" + #include "core/fxge/cfx_fontcache.h" + #include "core/fxge/cfx_fontmgr.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -19,28 +19,30 @@ CFX_GEModule* g_pGEModule = nullptr; + + CFX_GEModule::CFX_GEModule(const char** pUserFontPaths) + : m_pPlatform(PlatformIface::Create()), +- m_pFontMgr(pdfium::MakeUnique()), +- m_pFontCache(pdfium::MakeUnique()), ++ m_pFontMgr(std::make_unique()), ++ m_pFontCache(std::make_unique()), + m_pUserFontPaths(pUserFontPaths) {} + + CFX_GEModule::~CFX_GEModule() = default; + + // static + void CFX_GEModule::Create(const char** pUserFontPaths) { +- ASSERT(!g_pGEModule); ++ DCHECK(!g_pGEModule); + g_pGEModule = new CFX_GEModule(pUserFontPaths); + g_pGEModule->m_pPlatform->Init(); ++ g_pGEModule->GetFontMgr()->GetBuiltinMapper()->SetSystemFontInfo( ++ g_pGEModule->m_pPlatform->CreateDefaultSystemFontInfo()); + } + + // static + void CFX_GEModule::Destroy() { +- ASSERT(g_pGEModule); ++ DCHECK(g_pGEModule); + delete g_pGEModule; + g_pGEModule = nullptr; + } + + // static + CFX_GEModule* CFX_GEModule::Get() { +- ASSERT(g_pGEModule); ++ DCHECK(g_pGEModule); + return g_pGEModule; + } +diff --git a/core/fxge/cfx_gemodule.h b/core/fxge/cfx_gemodule.h +index 47e266774..f1f00f6a9 100644 +--- a/core/fxge/cfx_gemodule.h ++++ b/core/fxge/cfx_gemodule.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,19 +7,33 @@ + #ifndef CORE_FXGE_CFX_GEMODULE_H_ + #define CORE_FXGE_CFX_GEMODULE_H_ + ++#include ++ + #include + ++#include "build/build_config.h" ++ ++#if BUILDFLAG(IS_APPLE) ++#include "third_party/base/span.h" ++#endif ++ + class CFX_FontCache; + class CFX_FontMgr; ++class SystemFontInfoIface; + + class CFX_GEModule { + public: + class PlatformIface { + public: + static std::unique_ptr Create(); +- virtual ~PlatformIface() {} ++ virtual ~PlatformIface() = default; + + virtual void Init() = 0; ++ virtual std::unique_ptr ++ CreateDefaultSystemFontInfo() = 0; ++#if BUILDFLAG(IS_APPLE) ++ virtual void* CreatePlatformFont(pdfium::span font_span) = 0; ++#endif + }; + + static void Create(const char** pUserFontPaths); +diff --git a/core/fxge/cfx_glyphbitmap.cpp b/core/fxge/cfx_glyphbitmap.cpp +index cc28d514f..f9898bae6 100644 +--- a/core/fxge/cfx_glyphbitmap.cpp ++++ b/core/fxge/cfx_glyphbitmap.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxge/cfx_glyphbitmap.h b/core/fxge/cfx_glyphbitmap.h +index 81a0547bd..cea62d254 100644 +--- a/core/fxge/cfx_glyphbitmap.h ++++ b/core/fxge/cfx_glyphbitmap.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,8 +7,6 @@ + #ifndef CORE_FXGE_CFX_GLYPHBITMAP_H_ + #define CORE_FXGE_CFX_GLYPHBITMAP_H_ + +-#include +- + #include "core/fxcrt/retain_ptr.h" + + class CFX_DIBitmap; +diff --git a/core/fxge/cfx_glyphcache.cpp b/core/fxge/cfx_glyphcache.cpp +index ec9c8863c..1f12d7bd3 100644 +--- a/core/fxge/cfx_glyphcache.cpp ++++ b/core/fxge/cfx_glyphcache.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,8 @@ + + #include "core/fxge/cfx_glyphcache.h" + ++#include ++ + #include + #include + #include +@@ -13,27 +15,30 @@ + + #include "build/build_config.h" + #include "core/fxcrt/fx_codepage.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" + #include "core/fxge/cfx_font.h" + #include "core/fxge/cfx_fontmgr.h" + #include "core/fxge/cfx_gemodule.h" + #include "core/fxge/cfx_glyphbitmap.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/cfx_substfont.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "core/fxge/fx_freetype.h" +-#include "core/fxge/render_defines.h" ++#include "core/fxge/freetype/fx_freetype.h" + #include "core/fxge/scoped_font_transform.h" + #include "third_party/base/numerics/safe_math.h" +-#include "third_party/base/ptr_util.h" + +-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_ +-#include "third_party/skia/include/core/SkStream.h" +-#include "third_party/skia/include/core/SkTypeface.h" ++#if defined(_SKIA_SUPPORT_) ++#include "third_party/skia/include/core/SkStream.h" // nogncheck ++#include "third_party/skia/include/core/SkTypeface.h" // nogncheck + +-#if defined(OS_WIN) +-#include "third_party/skia/include/core/SkFontMgr.h" +-#include "third_party/skia/include/ports/SkFontMgr_empty.h" ++#if BUILDFLAG(IS_WIN) ++#include "third_party/skia/include/core/SkFontMgr.h" // nogncheck ++#include "third_party/skia/include/ports/SkFontMgr_empty.h" // nogncheck ++#endif + #endif ++ ++#if BUILDFLAG(IS_APPLE) ++#include "core/fxge/cfx_textrenderoptions.h" + #endif + + namespace { +@@ -63,7 +68,7 @@ void UniqueKeyGen::Generate(int count, ...) { + void GenKey(UniqueKeyGen* pKeyGen, + const CFX_Font* pFont, + const CFX_Matrix& matrix, +- uint32_t dest_width, ++ int dest_width, + int anti_alias, + bool bNative) { + int nMatrixA = static_cast(matrix.a * 10000); +@@ -71,6 +76,7 @@ void GenKey(UniqueKeyGen* pKeyGen, + int nMatrixC = static_cast(matrix.c * 10000); + int nMatrixD = static_cast(matrix.d * 10000); + ++#if BUILDFLAG(IS_APPLE) + if (bNative) { + if (pFont->GetSubstFont()) { + pKeyGen->Generate(10, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width, +@@ -81,22 +87,27 @@ void GenKey(UniqueKeyGen* pKeyGen, + pKeyGen->Generate(7, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width, + anti_alias, 3); + } ++ return; ++ } ++#else ++ CHECK(!bNative); ++#endif ++ ++ if (pFont->GetSubstFont()) { ++ pKeyGen->Generate(9, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width, ++ anti_alias, pFont->GetSubstFont()->m_Weight, ++ pFont->GetSubstFont()->m_ItalicAngle, ++ pFont->IsVertical()); + } else { +- if (pFont->GetSubstFont()) { +- pKeyGen->Generate(9, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width, +- anti_alias, pFont->GetSubstFont()->m_Weight, +- pFont->GetSubstFont()->m_ItalicAngle, +- pFont->IsVertical()); +- } else { +- pKeyGen->Generate(6, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width, +- anti_alias); +- } ++ pKeyGen->Generate(6, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width, ++ anti_alias); + } + } + + } // namespace + +-CFX_GlyphCache::CFX_GlyphCache(RetainPtr face) : m_Face(face) {} ++CFX_GlyphCache::CFX_GlyphCache(RetainPtr face) ++ : m_Face(std::move(face)) {} + + CFX_GlyphCache::~CFX_GlyphCache() = default; + +@@ -105,7 +116,7 @@ std::unique_ptr CFX_GlyphCache::RenderGlyph( + uint32_t glyph_index, + bool bFontStyle, + const CFX_Matrix& matrix, +- uint32_t dest_width, ++ int dest_width, + int anti_alias) { + if (!GetFaceRec()) + return nullptr; +@@ -119,59 +130,52 @@ std::unique_ptr CFX_GlyphCache::RenderGlyph( + const CFX_SubstFont* pSubstFont = pFont->GetSubstFont(); + if (pSubstFont) { + bUseCJKSubFont = pSubstFont->m_bSubstCJK && bFontStyle; +- int skew = 0; ++ int angle; + if (bUseCJKSubFont) +- skew = pSubstFont->m_bItalicCJK ? -15 : 0; ++ angle = pSubstFont->m_bItalicCJK ? -15 : 0; + else +- skew = pSubstFont->m_ItalicAngle; +- if (skew) { +- // |skew| is nonpositive so |-skew| is used as the index. We need to make +- // sure |skew| != INT_MIN since -INT_MIN is undefined. +- if (skew <= 0 && skew != std::numeric_limits::min() && +- static_cast(-skew) < CFX_Font::kAngleSkewArraySize) { +- skew = -CFX_Font::s_AngleSkew[-skew]; +- } else { +- skew = -58; +- } ++ angle = pSubstFont->m_ItalicAngle; ++ if (angle) { ++ int skew = CFX_Font::GetSkewFromAngle(angle); + if (pFont->IsVertical()) + ft_matrix.yx += ft_matrix.yy * skew / 100; + else + ft_matrix.xy -= ft_matrix.xx * skew / 100; + } +- if (pSubstFont->m_bFlagMM) { ++ if (pSubstFont->IsBuiltInGenericFont()) { + pFont->AdjustMMParams(glyph_index, dest_width, + pFont->GetSubstFont()->m_Weight); + } + } ++ + ScopedFontTransform scoped_transform(GetFace(), &ft_matrix); +- int load_flags = (GetFaceRec()->face_flags & FT_FACE_FLAG_SFNT) +- ? FT_LOAD_NO_BITMAP +- : (FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING); ++ int load_flags = FT_LOAD_NO_BITMAP | FT_LOAD_PEDANTIC; ++ if (!(GetFaceRec()->face_flags & FT_FACE_FLAG_SFNT)) ++ load_flags |= FT_LOAD_NO_HINTING; + int error = FT_Load_Glyph(GetFaceRec(), glyph_index, load_flags); + if (error) { + // if an error is returned, try to reload glyphs without hinting. +- if (load_flags & FT_LOAD_NO_HINTING || load_flags & FT_LOAD_NO_SCALE) ++ if (load_flags & FT_LOAD_NO_HINTING) + return nullptr; + + load_flags |= FT_LOAD_NO_HINTING; ++ load_flags &= ~FT_LOAD_PEDANTIC; + error = FT_Load_Glyph(GetFaceRec(), glyph_index, load_flags); + if (error) + return nullptr; + } +- int weight = 0; ++ ++ int weight; + if (bUseCJKSubFont) + weight = pSubstFont->m_WeightCJK; + else + weight = pSubstFont ? pSubstFont->m_Weight : 0; +- if (pSubstFont && !pSubstFont->m_bFlagMM && weight > 400) { ++ if (pSubstFont && !pSubstFont->IsBuiltInGenericFont() && weight > 400) { + uint32_t index = (weight - 400) / 10; +- if (index >= CFX_Font::kWeightPowArraySize) ++ pdfium::base::CheckedNumeric level = ++ CFX_Font::GetWeightLevel(pSubstFont->m_Charset, index); ++ if (level.ValueOrDefault(-1) < 0) + return nullptr; +- pdfium::base::CheckedNumeric level = 0; +- if (pSubstFont->m_Charset == FX_CHARSET_ShiftJIS) +- level = CFX_Font::s_WeightPow_SHIFTJIS[index] * 2; +- else +- level = CFX_Font::s_WeightPow_11[index]; + + level = level * + (abs(static_cast(ft_matrix.xx)) + +@@ -191,15 +195,16 @@ std::unique_ptr CFX_GlyphCache::RenderGlyph( + if (bmwidth > kMaxGlyphDimension || bmheight > kMaxGlyphDimension) + return nullptr; + int dib_width = bmwidth; +- auto pGlyphBitmap = pdfium::MakeUnique( +- FXFT_Get_Glyph_BitmapLeft(GetFaceRec()), +- FXFT_Get_Glyph_BitmapTop(GetFaceRec())); +- pGlyphBitmap->GetBitmap()->Create( +- dib_width, bmheight, +- anti_alias == FT_RENDER_MODE_MONO ? FXDIB_1bppMask : FXDIB_8bppMask); ++ auto pGlyphBitmap = ++ std::make_unique(FXFT_Get_Glyph_BitmapLeft(GetFaceRec()), ++ FXFT_Get_Glyph_BitmapTop(GetFaceRec())); ++ pGlyphBitmap->GetBitmap()->Create(dib_width, bmheight, ++ anti_alias == FT_RENDER_MODE_MONO ++ ? FXDIB_Format::k1bppMask ++ : FXDIB_Format::k8bppMask); + int dest_pitch = pGlyphBitmap->GetBitmap()->GetPitch(); + int src_pitch = FXFT_Get_Bitmap_Pitch(FXFT_Get_Glyph_Bitmap(GetFaceRec())); +- uint8_t* pDestBuf = pGlyphBitmap->GetBitmap()->GetBuffer(); ++ uint8_t* pDestBuf = pGlyphBitmap->GetBitmap()->GetBuffer().data(); + uint8_t* pSrcBuf = static_cast( + FXFT_Get_Bitmap_Buffer(FXFT_Get_Glyph_Bitmap(GetFaceRec()))); + if (anti_alias != FT_RENDER_MODE_MONO && +@@ -223,9 +228,9 @@ std::unique_ptr CFX_GlyphCache::RenderGlyph( + return pGlyphBitmap; + } + +-const CFX_PathData* CFX_GlyphCache::LoadGlyphPath(const CFX_Font* pFont, +- uint32_t glyph_index, +- uint32_t dest_width) { ++const CFX_Path* CFX_GlyphCache::LoadGlyphPath(const CFX_Font* pFont, ++ uint32_t glyph_index, ++ int dest_width) { + if (!GetFaceRec() || glyph_index == kInvalidGlyphIndex) + return nullptr; + +@@ -239,33 +244,33 @@ const CFX_PathData* CFX_GlyphCache::LoadGlyphPath(const CFX_Font* pFont, + if (it != m_PathMap.end()) + return it->second.get(); + +- CFX_PathData* pGlyphPath = pFont->LoadGlyphPathImpl(glyph_index, dest_width); +- m_PathMap[key] = std::unique_ptr(pGlyphPath); +- return pGlyphPath; ++ m_PathMap[key] = pFont->LoadGlyphPathImpl(glyph_index, dest_width); ++ return m_PathMap[key].get(); + } + +-const CFX_GlyphBitmap* CFX_GlyphCache::LoadGlyphBitmap(const CFX_Font* pFont, +- uint32_t glyph_index, +- bool bFontStyle, +- const CFX_Matrix& matrix, +- uint32_t dest_width, +- int anti_alias, +- int* pTextFlags) { ++const CFX_GlyphBitmap* CFX_GlyphCache::LoadGlyphBitmap( ++ const CFX_Font* pFont, ++ uint32_t glyph_index, ++ bool bFontStyle, ++ const CFX_Matrix& matrix, ++ int dest_width, ++ int anti_alias, ++ CFX_TextRenderOptions* text_options) { + if (glyph_index == kInvalidGlyphIndex) + return nullptr; + + UniqueKeyGen keygen; +-#if defined(OS_MACOSX) +- const bool bNative = !(*pTextFlags & FXTEXT_NO_NATIVETEXT); ++#if BUILDFLAG(IS_APPLE) ++ const bool bNative = text_options->native_text; + #else + const bool bNative = false; + #endif + GenKey(&keygen, pFont, matrix, dest_width, anti_alias, bNative); + ByteString FaceGlyphsKey(keygen.key_, keygen.key_len_); + +-#if defined(OS_MACOSX) && !defined _SKIA_SUPPORT_ && \ +- !defined _SKIA_SUPPORT_PATHS_ +- const bool bDoLookUp = !!(*pTextFlags & FXTEXT_NO_NATIVETEXT); ++#if BUILDFLAG(IS_APPLE) ++ const bool bDoLookUp = !text_options->native_text || ++ CFX_DefaultRenderDevice::SkiaIsDefaultRenderer(); + #else + const bool bDoLookUp = true; + #endif +@@ -274,8 +279,9 @@ const CFX_GlyphBitmap* CFX_GlyphCache::LoadGlyphBitmap(const CFX_Font* pFont, + bFontStyle, dest_width, anti_alias); + } + +-#if defined(OS_MACOSX) && !defined _SKIA_SUPPORT_ && \ +- !defined _SKIA_SUPPORT_PATHS_ ++#if BUILDFLAG(IS_APPLE) ++ DCHECK(!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()); ++ + std::unique_ptr pGlyphBitmap; + auto it = m_SizeMap.find(FaceGlyphsKey); + if (it != m_SizeMap.end()) { +@@ -306,34 +312,44 @@ const CFX_GlyphBitmap* CFX_GlyphCache::LoadGlyphBitmap(const CFX_Font* pFont, + } + GenKey(&keygen, pFont, matrix, dest_width, anti_alias, /*bNative=*/false); + ByteString FaceGlyphsKey2(keygen.key_, keygen.key_len_); +- *pTextFlags |= FXTEXT_NO_NATIVETEXT; ++ text_options->native_text = false; + return LookUpGlyphBitmap(pFont, matrix, FaceGlyphsKey2, glyph_index, + bFontStyle, dest_width, anti_alias); +-#endif ++#endif // BUILDFLAG(IS_APPLE) + } + +-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ ++int CFX_GlyphCache::GetGlyphWidth(const CFX_Font* font, ++ uint32_t glyph_index, ++ int dest_width, ++ int weight) { ++ const WidthMapKey key = std::make_tuple(glyph_index, dest_width, weight); ++ auto it = m_WidthMap.find(key); ++ if (it != m_WidthMap.end()) { ++ return it->second; ++ } ++ ++ m_WidthMap[key] = font->GetGlyphWidthImpl(glyph_index, dest_width, weight); ++ return m_WidthMap[key]; ++} ++ ++#if defined(_SKIA_SUPPORT_) + CFX_TypeFace* CFX_GlyphCache::GetDeviceCache(const CFX_Font* pFont) { + if (!m_pTypeface) { + pdfium::span span = pFont->GetFontSpan(); + m_pTypeface = SkTypeface::MakeFromStream( +- pdfium::MakeUnique(span.data(), span.size())); ++ std::make_unique(span.data(), span.size())); + } +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + if (!m_pTypeface) { + sk_sp customMgr(SkFontMgr_New_Custom_Empty()); + pdfium::span span = pFont->GetFontSpan(); + m_pTypeface = customMgr->makeFromStream( +- pdfium::MakeUnique(span.data(), span.size())); ++ std::make_unique(span.data(), span.size())); + } +-#endif // defined(OS_WIN) ++#endif // BUILDFLAG(IS_WIN) + return m_pTypeface.get(); + } +-#endif // defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ +- +-#if !defined(OS_MACOSX) +-void CFX_GlyphCache::InitPlatform() {} +-#endif ++#endif // defined(_SKIA_SUPPORT_) + + CFX_GlyphBitmap* CFX_GlyphCache::LookUpGlyphBitmap( + const CFX_Font* pFont, +@@ -341,7 +357,7 @@ CFX_GlyphBitmap* CFX_GlyphCache::LookUpGlyphBitmap( + const ByteString& FaceGlyphsKey, + uint32_t glyph_index, + bool bFontStyle, +- uint32_t dest_width, ++ int dest_width, + int anti_alias) { + SizeGlyphCache* pSizeCache; + auto it = m_SizeMap.find(FaceGlyphsKey); +diff --git a/core/fxge/cfx_glyphcache.h b/core/fxge/cfx_glyphcache.h +index 5a2442461..ff2e7409c 100644 +--- a/core/fxge/cfx_glyphcache.h ++++ b/core/fxge/cfx_glyphcache.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,43 +11,46 @@ + #include + #include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/observed_ptr.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxge/cfx_face.h" + +-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_ ++#if defined(_SKIA_SUPPORT_) + #include "core/fxge/fx_font.h" +-#include "third_party/skia/include/core/SkTypeface.h" ++#include "third_party/skia/include/core/SkTypeface.h" // nogncheck + #endif + + class CFX_Font; + class CFX_GlyphBitmap; + class CFX_Matrix; +-class CFX_PathData; ++class CFX_Path; ++struct CFX_TextRenderOptions; + +-class CFX_GlyphCache : public Retainable, public Observable { ++class CFX_GlyphCache final : public Retainable, public Observable { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- ++ CONSTRUCT_VIA_MAKE_RETAIN; + ~CFX_GlyphCache() override; + + const CFX_GlyphBitmap* LoadGlyphBitmap(const CFX_Font* pFont, + uint32_t glyph_index, + bool bFontStyle, + const CFX_Matrix& matrix, +- uint32_t dest_width, ++ int dest_width, + int anti_alias, +- int* pTextFlags); +- const CFX_PathData* LoadGlyphPath(const CFX_Font* pFont, +- uint32_t glyph_index, +- uint32_t dest_width); ++ CFX_TextRenderOptions* text_options); ++ const CFX_Path* LoadGlyphPath(const CFX_Font* pFont, ++ uint32_t glyph_index, ++ int dest_width); ++ int GetGlyphWidth(const CFX_Font* font, ++ uint32_t glyph_index, ++ int dest_width, ++ int weight); + + RetainPtr GetFace() { return m_Face; } + FXFT_FaceRec* GetFaceRec() { return m_Face ? m_Face->GetRec() : nullptr; } + +-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_ ++#if defined(_SKIA_SUPPORT_) + CFX_TypeFace* GetDeviceCache(const CFX_Font* pFont); + #endif + +@@ -56,34 +59,34 @@ class CFX_GlyphCache : public Retainable, public Observable { + + using SizeGlyphCache = std::map>; + // +- using PathMapKey = std::tuple; ++ using PathMapKey = std::tuple; ++ // ++ using WidthMapKey = std::tuple; + + std::unique_ptr RenderGlyph(const CFX_Font* pFont, + uint32_t glyph_index, + bool bFontStyle, + const CFX_Matrix& matrix, +- uint32_t dest_width, ++ int dest_width, + int anti_alias); + std::unique_ptr RenderGlyph_Nativetext( + const CFX_Font* pFont, + uint32_t glyph_index, + const CFX_Matrix& matrix, +- uint32_t dest_width, ++ int dest_width, + int anti_alias); + CFX_GlyphBitmap* LookUpGlyphBitmap(const CFX_Font* pFont, + const CFX_Matrix& matrix, + const ByteString& FaceGlyphsKey, + uint32_t glyph_index, + bool bFontStyle, +- uint32_t dest_width, ++ int dest_width, + int anti_alias); +- void InitPlatform(); +- void DestroyPlatform(); +- + RetainPtr const m_Face; + std::map m_SizeMap; +- std::map> m_PathMap; +-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_ ++ std::map> m_PathMap; ++ std::map m_WidthMap; ++#if defined(_SKIA_SUPPORT_) + sk_sp m_pTypeface; + #endif + }; +diff --git a/core/fxge/cfx_graphstate.cpp b/core/fxge/cfx_graphstate.cpp +index 726c0c629..18f32a3b2 100644 +--- a/core/fxge/cfx_graphstate.cpp ++++ b/core/fxge/cfx_graphstate.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -29,8 +29,30 @@ void CFX_GraphState::SetLineDash(std::vector dashes, + pData->m_DashArray = std::move(dashes); + } + ++void CFX_GraphState::SetLineDashPhase(float phase) { ++ CFX_GraphStateData* pData = m_Ref.GetPrivateCopy(); ++ pData->m_DashPhase = phase; ++} ++ ++std::vector CFX_GraphState::GetLineDashArray() const { ++ std::vector ret; ++ ++ if (m_Ref.GetObject()) ++ ret = m_Ref.GetObject()->m_DashArray; ++ ++ return ret; ++} ++ ++size_t CFX_GraphState::GetLineDashSize() const { ++ return m_Ref.GetObject() ? m_Ref.GetObject()->m_DashArray.size() : 0; ++} ++ ++float CFX_GraphState::GetLineDashPhase() const { ++ return m_Ref.GetObject() ? m_Ref.GetObject()->m_DashPhase : 1.0f; ++} ++ + float CFX_GraphState::GetLineWidth() const { +- return m_Ref.GetObject() ? m_Ref.GetObject()->m_LineWidth : 1.f; ++ return m_Ref.GetObject() ? m_Ref.GetObject()->m_LineWidth : 1.0f; + } + + void CFX_GraphState::SetLineWidth(float width) { +@@ -39,7 +61,7 @@ void CFX_GraphState::SetLineWidth(float width) { + + CFX_GraphStateData::LineCap CFX_GraphState::GetLineCap() const { + return m_Ref.GetObject() ? m_Ref.GetObject()->m_LineCap +- : CFX_GraphStateData::LineCapButt; ++ : CFX_GraphStateData::LineCap::kButt; + } + void CFX_GraphState::SetLineCap(CFX_GraphStateData::LineCap cap) { + m_Ref.GetPrivateCopy()->m_LineCap = cap; +@@ -47,7 +69,7 @@ void CFX_GraphState::SetLineCap(CFX_GraphStateData::LineCap cap) { + + CFX_GraphStateData::LineJoin CFX_GraphState::GetLineJoin() const { + return m_Ref.GetObject() ? m_Ref.GetObject()->m_LineJoin +- : CFX_GraphStateData::LineJoinMiter; ++ : CFX_GraphStateData::LineJoin::kMiter; + } + + void CFX_GraphState::SetLineJoin(CFX_GraphStateData::LineJoin join) { +diff --git a/core/fxge/cfx_graphstate.h b/core/fxge/cfx_graphstate.h +index 20955f81b..ed5347971 100644 +--- a/core/fxge/cfx_graphstate.h ++++ b/core/fxge/cfx_graphstate.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,8 @@ + #ifndef CORE_FXGE_CFX_GRAPHSTATE_H_ + #define CORE_FXGE_CFX_GRAPHSTATE_H_ + ++#include ++ + #include + + #include "core/fxcrt/shared_copy_on_write.h" +@@ -21,6 +23,10 @@ class CFX_GraphState { + void Emplace(); + + void SetLineDash(std::vector dashes, float phase, float scale); ++ void SetLineDashPhase(float phase); ++ std::vector GetLineDashArray() const; ++ size_t GetLineDashSize() const; ++ float GetLineDashPhase() const; + + float GetLineWidth() const; + void SetLineWidth(float width); +diff --git a/core/fxge/cfx_graphstatedata.cpp b/core/fxge/cfx_graphstatedata.cpp +index 43f62548d..5bfa32e3b 100644 +--- a/core/fxge/cfx_graphstatedata.cpp ++++ b/core/fxge/cfx_graphstatedata.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,15 +10,16 @@ CFX_GraphStateData::CFX_GraphStateData() = default; + + CFX_GraphStateData::CFX_GraphStateData(const CFX_GraphStateData& src) = default; + +-CFX_GraphStateData::CFX_GraphStateData(CFX_GraphStateData&& src) = default; ++CFX_GraphStateData::CFX_GraphStateData(CFX_GraphStateData&& src) noexcept = ++ default; + + CFX_GraphStateData::~CFX_GraphStateData() = default; + + CFX_GraphStateData& CFX_GraphStateData::operator=( + const CFX_GraphStateData& that) = default; + +-CFX_GraphStateData& CFX_GraphStateData::operator=(CFX_GraphStateData&& that) = +- default; ++CFX_GraphStateData& CFX_GraphStateData::operator=( ++ CFX_GraphStateData&& that) noexcept = default; + + CFX_RetainableGraphStateData::CFX_RetainableGraphStateData() = default; + +diff --git a/core/fxge/cfx_graphstatedata.h b/core/fxge/cfx_graphstatedata.h +index e8ece9f72..e7a958ef4 100644 +--- a/core/fxge/cfx_graphstatedata.h ++++ b/core/fxge/cfx_graphstatedata.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,46 +7,38 @@ + #ifndef CORE_FXGE_CFX_GRAPHSTATEDATA_H_ + #define CORE_FXGE_CFX_GRAPHSTATEDATA_H_ + ++#include ++ + #include + +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" + + class CFX_GraphStateData { + public: +- enum LineCap : uint8_t { +- LineCapButt = 0, +- LineCapRound = 1, +- LineCapSquare = 2 +- }; +- +- enum LineJoin : uint8_t { +- LineJoinMiter = 0, +- LineJoinRound = 1, +- LineJoinBevel = 2 +- }; ++ enum class LineCap : uint8_t { kButt = 0, kRound = 1, kSquare = 2 }; ++ ++ enum class LineJoin : uint8_t { kMiter = 0, kRound = 1, kBevel = 2 }; + + CFX_GraphStateData(); + CFX_GraphStateData(const CFX_GraphStateData& src); +- CFX_GraphStateData(CFX_GraphStateData&& src); ++ CFX_GraphStateData(CFX_GraphStateData&& src) noexcept; + ~CFX_GraphStateData(); + + CFX_GraphStateData& operator=(const CFX_GraphStateData& that); +- CFX_GraphStateData& operator=(CFX_GraphStateData&& that); ++ CFX_GraphStateData& operator=(CFX_GraphStateData&& that) noexcept; + +- LineCap m_LineCap = LineCapButt; +- LineJoin m_LineJoin = LineJoinMiter; ++ LineCap m_LineCap = LineCap::kButt; ++ LineJoin m_LineJoin = LineJoin::kMiter; + float m_DashPhase = 0.0f; + float m_MiterLimit = 10.0f; + float m_LineWidth = 1.0f; + std::vector m_DashArray; + }; + +-class CFX_RetainableGraphStateData : public Retainable, +- public CFX_GraphStateData { ++class CFX_RetainableGraphStateData final : public Retainable, ++ public CFX_GraphStateData { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + RetainPtr Clone() const; + +diff --git a/core/fxge/cfx_path.cpp b/core/fxge/cfx_path.cpp +new file mode 100644 +index 000000000..d52c21ee7 +--- /dev/null ++++ b/core/fxge/cfx_path.cpp +@@ -0,0 +1,452 @@ ++// Copyright 2016 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxge/cfx_path.h" ++ ++#include ++ ++#include ++#include ++ ++#include "core/fxcrt/fx_system.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/numerics/safe_math.h" ++ ++namespace { ++ ++bool IsRectPreTransform(const std::vector& points) { ++ if (points.size() != 5 && points.size() != 4) ++ return false; ++ ++ if (points.size() == 5 && points[0].m_Point != points[4].m_Point) ++ return false; ++ ++ if (points[0].m_Point == points[2].m_Point || ++ points[1].m_Point == points[3].m_Point) { ++ return false; ++ } ++ ++ for (size_t i = 1; i < points.size(); ++i) { ++ if (points[i].m_Type != CFX_Path::Point::Type::kLine) ++ return false; ++ } ++ return true; ++} ++ ++bool XYBothNotEqual(const CFX_PointF& p1, const CFX_PointF& p2) { ++ return p1.x != p2.x && p1.y != p2.y; ++} ++ ++bool IsRectImpl(const std::vector& points) { ++ if (!IsRectPreTransform(points)) ++ return false; ++ ++ for (int i = 1; i < 4; i++) { ++ if (XYBothNotEqual(points[i].m_Point, points[i - 1].m_Point)) ++ return false; ++ } ++ ++ if (XYBothNotEqual(points[0].m_Point, points[3].m_Point)) ++ return false; ++ ++ return true; ++} ++ ++CFX_FloatRect CreateRectFromPoints(const CFX_PointF& p1, const CFX_PointF& p2) { ++ CFX_FloatRect rect(p1.x, p1.y, p2.x, p2.y); ++ rect.Normalize(); ++ return rect; ++} ++ ++bool PathPointsNeedNormalization(const std::vector& points) { ++ return points.size() > 5; ++} ++ ++std::vector GetNormalizedPoints( ++ const std::vector& points) { ++ DCHECK(PathPointsNeedNormalization(points)); ++ ++ if (points[0].m_Point != points.back().m_Point) ++ return {}; ++ ++ std::vector normalized; ++ normalized.reserve(6); ++ normalized.push_back(points[0]); ++ for (auto it = points.begin() + 1; it != points.end(); ++it) { ++ // Exactly 5 points left. Stop normalizing and take what is left. ++ if (normalized.size() + std::distance(it, points.end()) == 5) { ++ std::copy(it, points.end(), std::back_inserter(normalized)); ++ break; ++ } ++ ++ // If the line does not move, skip this point. ++ const auto& point = *it; ++ if (point.m_Type == CFX_Path::Point::Type::kLine && !point.m_CloseFigure && ++ !normalized.back().m_CloseFigure && ++ point.m_Point == normalized.back().m_Point) { ++ continue; ++ } ++ ++ normalized.push_back(point); ++ ++ // Too many points. Not considered as a rectangle. ++ if (normalized.size() > 5) ++ return {}; ++ } ++ ++ DCHECK_EQ(5u, normalized.size()); ++ return normalized; ++} ++ ++void UpdateLineEndPoints(CFX_FloatRect* rect, ++ const CFX_PointF& start_pos, ++ const CFX_PointF& end_pos, ++ float hw) { ++ if (start_pos.x == end_pos.x) { ++ if (start_pos.y == end_pos.y) { ++ rect->UpdateRect(end_pos + CFX_PointF(hw, hw)); ++ rect->UpdateRect(end_pos - CFX_PointF(hw, hw)); ++ return; ++ } ++ ++ float point_y; ++ if (end_pos.y < start_pos.y) ++ point_y = end_pos.y - hw; ++ else ++ point_y = end_pos.y + hw; ++ ++ rect->UpdateRect(CFX_PointF(end_pos.x + hw, point_y)); ++ rect->UpdateRect(CFX_PointF(end_pos.x - hw, point_y)); ++ return; ++ } ++ ++ if (start_pos.y == end_pos.y) { ++ float point_x; ++ if (end_pos.x < start_pos.x) ++ point_x = end_pos.x - hw; ++ else ++ point_x = end_pos.x + hw; ++ ++ rect->UpdateRect(CFX_PointF(point_x, end_pos.y + hw)); ++ rect->UpdateRect(CFX_PointF(point_x, end_pos.y - hw)); ++ return; ++ } ++ ++ CFX_PointF diff = end_pos - start_pos; ++ float ll = FXSYS_sqrt2(diff.x, diff.y); ++ float mx = end_pos.x + hw * diff.x / ll; ++ float my = end_pos.y + hw * diff.y / ll; ++ float dx1 = hw * diff.y / ll; ++ float dy1 = hw * diff.x / ll; ++ rect->UpdateRect(CFX_PointF(mx - dx1, my + dy1)); ++ rect->UpdateRect(CFX_PointF(mx + dx1, my - dy1)); ++} ++ ++void UpdateLineJoinPoints(CFX_FloatRect* rect, ++ const CFX_PointF& start_pos, ++ const CFX_PointF& mid_pos, ++ const CFX_PointF& end_pos, ++ float half_width, ++ float miter_limit) { ++ float start_k = 0; ++ float start_c = 0; ++ float end_k = 0; ++ float end_c = 0; ++ float start_len = 0; ++ float start_dc = 0; ++ float end_len = 0; ++ float end_dc = 0; ++ float one_twentieth = 1.0f / 20; ++ ++ bool bStartVert = fabs(start_pos.x - mid_pos.x) < one_twentieth; ++ bool bEndVert = fabs(mid_pos.x - end_pos.x) < one_twentieth; ++ if (bStartVert && bEndVert) { ++ int start_dir = mid_pos.y > start_pos.y ? 1 : -1; ++ float point_y = mid_pos.y + half_width * start_dir; ++ rect->UpdateRect(CFX_PointF(mid_pos.x + half_width, point_y)); ++ rect->UpdateRect(CFX_PointF(mid_pos.x - half_width, point_y)); ++ return; ++ } ++ ++ if (!bStartVert) { ++ CFX_PointF start_to_mid = start_pos - mid_pos; ++ start_k = (mid_pos.y - start_pos.y) / (mid_pos.x - start_pos.x); ++ start_c = mid_pos.y - (start_k * mid_pos.x); ++ start_len = FXSYS_sqrt2(start_to_mid.x, start_to_mid.y); ++ start_dc = fabsf(half_width * start_len / start_to_mid.x); ++ } ++ if (!bEndVert) { ++ CFX_PointF end_to_mid = end_pos - mid_pos; ++ end_k = end_to_mid.y / end_to_mid.x; ++ end_c = mid_pos.y - (end_k * mid_pos.x); ++ end_len = FXSYS_sqrt2(end_to_mid.x, end_to_mid.y); ++ end_dc = fabs(half_width * end_len / end_to_mid.x); ++ } ++ if (bStartVert) { ++ CFX_PointF outside(start_pos.x, 0); ++ if (end_pos.x < start_pos.x) ++ outside.x += half_width; ++ else ++ outside.x -= half_width; ++ ++ if (start_pos.y < (end_k * start_pos.x) + end_c) ++ outside.y = (end_k * outside.x) + end_c + end_dc; ++ else ++ outside.y = (end_k * outside.x) + end_c - end_dc; ++ ++ rect->UpdateRect(outside); ++ return; ++ } ++ ++ if (bEndVert) { ++ CFX_PointF outside(end_pos.x, 0); ++ if (start_pos.x < end_pos.x) ++ outside.x += half_width; ++ else ++ outside.x -= half_width; ++ ++ if (end_pos.y < (start_k * end_pos.x) + start_c) ++ outside.y = (start_k * outside.x) + start_c + start_dc; ++ else ++ outside.y = (start_k * outside.x) + start_c - start_dc; ++ ++ rect->UpdateRect(outside); ++ return; ++ } ++ ++ if (fabs(start_k - end_k) < one_twentieth) { ++ int start_dir = mid_pos.x > start_pos.x ? 1 : -1; ++ int end_dir = end_pos.x > mid_pos.x ? 1 : -1; ++ if (start_dir == end_dir) ++ UpdateLineEndPoints(rect, mid_pos, end_pos, half_width); ++ else ++ UpdateLineEndPoints(rect, start_pos, mid_pos, half_width); ++ return; ++ } ++ ++ float start_outside_c = start_c; ++ if (end_pos.y < (start_k * end_pos.x) + start_c) ++ start_outside_c += start_dc; ++ else ++ start_outside_c -= start_dc; ++ ++ float end_outside_c = end_c; ++ if (start_pos.y < (end_k * start_pos.x) + end_c) ++ end_outside_c += end_dc; ++ else ++ end_outside_c -= end_dc; ++ ++ float join_x = (end_outside_c - start_outside_c) / (start_k - end_k); ++ float join_y = start_k * join_x + start_outside_c; ++ rect->UpdateRect(CFX_PointF(join_x, join_y)); ++} ++ ++} // namespace ++ ++CFX_Path::Point::Point() = default; ++ ++CFX_Path::Point::Point(const CFX_PointF& point, Type type, bool close) ++ : m_Point(point), m_Type(type), m_CloseFigure(close) {} ++ ++CFX_Path::Point::Point(const Point& other) = default; ++ ++CFX_Path::Point::~Point() = default; ++ ++CFX_Path::CFX_Path() = default; ++ ++CFX_Path::CFX_Path(const CFX_Path& src) = default; ++ ++CFX_Path::CFX_Path(CFX_Path&& src) noexcept = default; ++ ++CFX_Path::~CFX_Path() = default; ++ ++void CFX_Path::Clear() { ++ m_Points.clear(); ++} ++ ++void CFX_Path::ClosePath() { ++ if (m_Points.empty()) ++ return; ++ m_Points.back().m_CloseFigure = true; ++} ++ ++void CFX_Path::Append(const CFX_Path& src, const CFX_Matrix* matrix) { ++ if (src.m_Points.empty()) ++ return; ++ ++ size_t cur_size = m_Points.size(); ++ m_Points.insert(m_Points.end(), src.m_Points.begin(), src.m_Points.end()); ++ ++ if (!matrix) ++ return; ++ ++ for (size_t i = cur_size; i < m_Points.size(); i++) ++ m_Points[i].m_Point = matrix->Transform(m_Points[i].m_Point); ++} ++ ++void CFX_Path::AppendPoint(const CFX_PointF& point, Point::Type type) { ++ m_Points.push_back(Point(point, type, /*close=*/false)); ++} ++ ++void CFX_Path::AppendPointAndClose(const CFX_PointF& point, Point::Type type) { ++ m_Points.push_back(Point(point, type, /*close=*/true)); ++} ++ ++void CFX_Path::AppendLine(const CFX_PointF& pt1, const CFX_PointF& pt2) { ++ if (m_Points.empty() || fabs(m_Points.back().m_Point.x - pt1.x) > 0.001 || ++ fabs(m_Points.back().m_Point.y - pt1.y) > 0.001) { ++ AppendPoint(pt1, CFX_Path::Point::Type::kMove); ++ } ++ AppendPoint(pt2, CFX_Path::Point::Type::kLine); ++} ++ ++void CFX_Path::AppendFloatRect(const CFX_FloatRect& rect) { ++ return AppendRect(rect.left, rect.bottom, rect.right, rect.top); ++} ++ ++void CFX_Path::AppendRect(float left, float bottom, float right, float top) { ++ CFX_PointF left_bottom(left, bottom); ++ CFX_PointF left_top(left, top); ++ CFX_PointF right_top(right, top); ++ CFX_PointF right_bottom(right, bottom); ++ ++ AppendLine(left_bottom, left_top); ++ AppendLine(left_top, right_top); ++ AppendLine(right_top, right_bottom); ++ AppendLine(right_bottom, left_bottom); ++ ClosePath(); ++} ++ ++CFX_FloatRect CFX_Path::GetBoundingBox() const { ++ if (m_Points.empty()) ++ return CFX_FloatRect(); ++ ++ CFX_FloatRect rect(m_Points[0].m_Point); ++ for (size_t i = 1; i < m_Points.size(); ++i) ++ rect.UpdateRect(m_Points[i].m_Point); ++ return rect; ++} ++ ++CFX_FloatRect CFX_Path::GetBoundingBoxForStrokePath(float line_width, ++ float miter_limit) const { ++ CFX_FloatRect rect(100000.0f, 100000.0f, -100000.0f, -100000.0f); ++ size_t iPoint = 0; ++ float half_width = line_width; ++ size_t iStartPoint = 0; ++ size_t iEndPoint = 0; ++ size_t iMiddlePoint = 0; ++ bool bJoin; ++ while (iPoint < m_Points.size()) { ++ if (m_Points[iPoint].m_Type == CFX_Path::Point::Type::kMove) { ++ if (iPoint + 1 == m_Points.size()) { ++ if (m_Points[iPoint].m_CloseFigure) { ++ // Update `rect` right away since this is the final point to be drawn. ++ rect.UpdateRect(m_Points[iPoint].m_Point); ++ } ++ break; ++ } ++ ++ iStartPoint = iPoint + 1; ++ iEndPoint = iPoint; ++ bJoin = false; ++ } else { ++ if (m_Points[iPoint].IsTypeAndOpen(CFX_Path::Point::Type::kBezier)) { ++ // Callers are responsible for adding Beziers in sets of 3. ++ CHECK_LT(iPoint + 2, m_Points.size()); ++ DCHECK_EQ(m_Points[iPoint + 1].m_Type, CFX_Path::Point::Type::kBezier); ++ DCHECK_EQ(m_Points[iPoint + 2].m_Type, CFX_Path::Point::Type::kBezier); ++ rect.UpdateRect(m_Points[iPoint].m_Point); ++ rect.UpdateRect(m_Points[iPoint + 1].m_Point); ++ iPoint += 2; ++ } ++ if (iPoint == m_Points.size() - 1 || ++ m_Points[iPoint + 1].m_Type == CFX_Path::Point::Type::kMove) { ++ iStartPoint = iPoint - 1; ++ iEndPoint = iPoint; ++ bJoin = false; ++ } else { ++ iStartPoint = iPoint - 1; ++ iMiddlePoint = iPoint; ++ iEndPoint = iPoint + 1; ++ bJoin = true; ++ } ++ } ++ CHECK_LT(iStartPoint, m_Points.size()); ++ CHECK_LT(iEndPoint, m_Points.size()); ++ if (bJoin) { ++ CHECK_LT(iMiddlePoint, m_Points.size()); ++ UpdateLineJoinPoints( ++ &rect, m_Points[iStartPoint].m_Point, m_Points[iMiddlePoint].m_Point, ++ m_Points[iEndPoint].m_Point, half_width, miter_limit); ++ } else { ++ UpdateLineEndPoints(&rect, m_Points[iStartPoint].m_Point, ++ m_Points[iEndPoint].m_Point, half_width); ++ } ++ ++iPoint; ++ } ++ return rect; ++} ++ ++void CFX_Path::Transform(const CFX_Matrix& matrix) { ++ for (auto& point : m_Points) ++ point.m_Point = matrix.Transform(point.m_Point); ++} ++ ++bool CFX_Path::IsRect() const { ++ if (PathPointsNeedNormalization(m_Points)) ++ return IsRectImpl(GetNormalizedPoints(m_Points)); ++ return IsRectImpl(m_Points); ++} ++ ++absl::optional CFX_Path::GetRect( ++ const CFX_Matrix* matrix) const { ++ bool do_normalize = PathPointsNeedNormalization(m_Points); ++ std::vector normalized; ++ if (do_normalize) ++ normalized = GetNormalizedPoints(m_Points); ++ const std::vector& path_points = do_normalize ? normalized : m_Points; ++ ++ if (!matrix) { ++ if (!IsRectImpl(path_points)) ++ return absl::nullopt; ++ ++ return CreateRectFromPoints(path_points[0].m_Point, path_points[2].m_Point); ++ } ++ ++ if (!IsRectPreTransform(path_points)) ++ return absl::nullopt; ++ ++ CFX_PointF points[5]; ++ for (size_t i = 0; i < path_points.size(); ++i) { ++ points[i] = matrix->Transform(path_points[i].m_Point); ++ ++ if (i == 0) ++ continue; ++ if (XYBothNotEqual(points[i], points[i - 1])) ++ return absl::nullopt; ++ } ++ ++ if (XYBothNotEqual(points[0], points[3])) ++ return absl::nullopt; ++ ++ return CreateRectFromPoints(points[0], points[2]); ++} ++ ++CFX_RetainablePath::CFX_RetainablePath() = default; ++ ++// Note: can't default the copy constructor since Retainable<> has a deleted ++// copy constructor (as it should). Instead, we want the default Retainable<> ++// constructor to be invoked so as to create a copy with a ref-count of 1 as ++// of the time it is created, then populate the remainder of the members from ++// the |src| object. ++CFX_RetainablePath::CFX_RetainablePath(const CFX_RetainablePath& src) ++ : CFX_Path(src) {} ++ ++CFX_RetainablePath::~CFX_RetainablePath() = default; ++ ++RetainPtr CFX_RetainablePath::Clone() const { ++ return pdfium::MakeRetain(*this); ++} +diff --git a/core/fxge/cfx_path.h b/core/fxge/cfx_path.h +new file mode 100644 +index 000000000..5c6199c64 +--- /dev/null ++++ b/core/fxge/cfx_path.h +@@ -0,0 +1,85 @@ ++// Copyright 2016 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXGE_CFX_PATH_H_ ++#define CORE_FXGE_CFX_PATH_H_ ++ ++#include ++ ++#include ++ ++#include "core/fxcrt/fx_coordinates.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++ ++class CFX_Path { ++ public: ++ class Point { ++ public: ++ enum class Type : uint8_t { kLine, kBezier, kMove }; ++ ++ Point(); ++ Point(const CFX_PointF& point, Type type, bool close); ++ Point(const Point& other); ++ ~Point(); ++ ++ bool IsTypeAndOpen(Type type) const { ++ return m_Type == type && !m_CloseFigure; ++ } ++ ++ CFX_PointF m_Point; ++ Type m_Type = Type::kLine; ++ bool m_CloseFigure = false; ++ }; ++ ++ CFX_Path(); ++ CFX_Path(const CFX_Path& src); ++ CFX_Path(CFX_Path&& src) noexcept; ++ ~CFX_Path(); ++ ++ void Clear(); ++ ++ Point::Type GetType(size_t index) const { return m_Points[index].m_Type; } ++ bool IsClosingFigure(size_t index) const { ++ return m_Points[index].m_CloseFigure; ++ } ++ CFX_PointF GetPoint(size_t index) const { return m_Points[index].m_Point; } ++ const std::vector& GetPoints() const { return m_Points; } ++ std::vector& GetPoints() { return m_Points; } ++ ++ CFX_FloatRect GetBoundingBox() const; ++ CFX_FloatRect GetBoundingBoxForStrokePath(float line_width, ++ float miter_limit) const; ++ ++ void Transform(const CFX_Matrix& matrix); ++ bool IsRect() const; ++ absl::optional GetRect(const CFX_Matrix* matrix) const; ++ ++ void Append(const CFX_Path& src, const CFX_Matrix* matrix); ++ void AppendFloatRect(const CFX_FloatRect& rect); ++ void AppendRect(float left, float bottom, float right, float top); ++ void AppendLine(const CFX_PointF& pt1, const CFX_PointF& pt2); ++ void AppendPoint(const CFX_PointF& point, Point::Type type); ++ void AppendPointAndClose(const CFX_PointF& point, Point::Type type); ++ void ClosePath(); ++ ++ private: ++ std::vector m_Points; ++}; ++ ++class CFX_RetainablePath final : public Retainable, public CFX_Path { ++ public: ++ CONSTRUCT_VIA_MAKE_RETAIN; ++ ++ RetainPtr Clone() const; ++ ++ private: ++ CFX_RetainablePath(); ++ CFX_RetainablePath(const CFX_RetainablePath& src); ++ ~CFX_RetainablePath() override; ++}; ++ ++#endif // CORE_FXGE_CFX_PATH_H_ +diff --git a/core/fxge/cfx_path_unittest.cpp b/core/fxge/cfx_path_unittest.cpp +new file mode 100644 +index 000000000..4ccb1c2df +--- /dev/null ++++ b/core/fxge/cfx_path_unittest.cpp +@@ -0,0 +1,407 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxge/cfx_path.h" ++ ++#include "core/fxcrt/fx_coordinates.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++TEST(CFX_Path, BasicTest) { ++ CFX_Path path; ++ path.AppendRect(/*left=*/1, /*bottom=*/2, /*right=*/3, /*top=*/5); ++ EXPECT_EQ(5u, path.GetPoints().size()); ++ EXPECT_TRUE(path.IsRect()); ++ absl::optional rect = path.GetRect(nullptr); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), rect.value()); ++ EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), path.GetBoundingBox()); ++ ++ const CFX_Matrix kScaleMatrix(1, 0, 0, 2, 60, 70); ++ rect = path.GetRect(&kScaleMatrix); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(61, 74, 63, 80), rect.value()); ++ EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), path.GetBoundingBox()); ++ ++ path.Clear(); ++ EXPECT_EQ(0u, path.GetPoints().size()); ++ EXPECT_FALSE(path.IsRect()); ++ EXPECT_EQ(CFX_FloatRect(), path.GetBoundingBox()); ++ ++ // 4 points without a closed path makes a rect. ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); ++ path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({1, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({1, 0}, CFX_Path::Point::Type::kLine); ++ EXPECT_EQ(4u, path.GetPoints().size()); ++ EXPECT_TRUE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), rect.value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), path.GetBoundingBox()); ++ ++ // 4 points with a closed path also makes a rect. ++ path.ClosePath(); ++ EXPECT_EQ(4u, path.GetPoints().size()); ++ EXPECT_TRUE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), rect.value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), path.GetBoundingBox()); ++ ++ path.Transform(kScaleMatrix); ++ EXPECT_TRUE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(60, 70, 61, 72), rect.value()); ++ EXPECT_EQ(CFX_FloatRect(60, 70, 61, 72), path.GetBoundingBox()); ++ ++ path.Clear(); ++ path.AppendFloatRect({1, 2, 3, 5}); ++ EXPECT_TRUE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), rect.value()); ++ EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), path.GetBoundingBox()); ++} ++ ++TEST(CFX_Path, ShearTransform) { ++ CFX_Path path; ++ path.AppendRect(/*left=*/1, /*bottom=*/2, /*right=*/3, /*top=*/5); ++ ++ const CFX_Matrix kShearMatrix(1, 2, 0, 1, 0, 0); ++ EXPECT_TRUE(path.IsRect()); ++ absl::optional rect = path.GetRect(&kShearMatrix); ++ EXPECT_FALSE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), path.GetBoundingBox()); ++ ++ path.Transform(kShearMatrix); ++ EXPECT_FALSE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ EXPECT_FALSE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(1, 4, 3, 11), path.GetBoundingBox()); ++ ++ const CFX_Matrix shear_inverse_matrix = kShearMatrix.GetInverse(); ++ rect = path.GetRect(&shear_inverse_matrix); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), rect.value()); ++ EXPECT_EQ(CFX_FloatRect(1, 4, 3, 11), path.GetBoundingBox()); ++ ++ path.Transform(shear_inverse_matrix); ++ EXPECT_TRUE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), rect.value()); ++ EXPECT_EQ(CFX_FloatRect(1, 2, 3, 5), path.GetBoundingBox()); ++} ++ ++TEST(CFX_Path, Hexagon) { ++ CFX_Path path; ++ path.AppendPoint({1, 0}, CFX_Path::Point::Type::kMove); ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({3, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({2, 2}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({1, 2}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); ++ ASSERT_EQ(6u, path.GetPoints().size()); ++ EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(5)); ++ EXPECT_FALSE(path.IsClosingFigure(5)); ++ EXPECT_FALSE(path.IsRect()); ++ EXPECT_FALSE(path.GetRect(nullptr).has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 3, 2), path.GetBoundingBox()); ++ ++ path.ClosePath(); ++ ASSERT_EQ(6u, path.GetPoints().size()); ++ EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(5)); ++ EXPECT_TRUE(path.IsClosingFigure(5)); ++ EXPECT_FALSE(path.IsRect()); ++ EXPECT_FALSE(path.GetRect(nullptr).has_value()); ++ ++ // Calling ClosePath() repeatedly makes no difference. ++ path.ClosePath(); ++ ASSERT_EQ(6u, path.GetPoints().size()); ++ EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(5)); ++ EXPECT_TRUE(path.IsClosingFigure(5)); ++ EXPECT_FALSE(path.IsRect()); ++ EXPECT_FALSE(path.GetRect(nullptr).has_value()); ++ ++ // A hexagon with the same start/end point is still not a rectangle. ++ path.Clear(); ++ path.AppendPoint({1, 0}, CFX_Path::Point::Type::kMove); ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({3, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({2, 2}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({1, 2}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({1, 0}, CFX_Path::Point::Type::kLine); ++ EXPECT_FALSE(path.IsRect()); ++ EXPECT_FALSE(path.GetRect(nullptr).has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 3, 2), path.GetBoundingBox()); ++} ++ ++TEST(CFX_Path, ClosePath) { ++ CFX_Path path; ++ path.AppendLine({0, 0}, {0, 1}); ++ path.AppendLine({0, 1}, {1, 1}); ++ path.AppendLine({1, 1}, {1, 0}); ++ ASSERT_EQ(4u, path.GetPoints().size()); ++ EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(3)); ++ EXPECT_FALSE(path.IsClosingFigure(3)); ++ EXPECT_TRUE(path.IsRect()); ++ absl::optional rect = path.GetRect(nullptr); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), rect.value()); ++ ++ const CFX_Matrix kIdentityMatrix; ++ ASSERT_TRUE(kIdentityMatrix.IsIdentity()); ++ rect = path.GetRect(&kIdentityMatrix); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), rect.value()); ++ ++ path.ClosePath(); ++ ASSERT_EQ(4u, path.GetPoints().size()); ++ EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(3)); ++ EXPECT_TRUE(path.IsClosingFigure(3)); ++ EXPECT_TRUE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), rect.value()); ++ ++ // Calling ClosePath() repeatedly makes no difference. ++ path.ClosePath(); ++ ASSERT_EQ(4u, path.GetPoints().size()); ++ EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(3)); ++ EXPECT_TRUE(path.IsClosingFigure(3)); ++ EXPECT_TRUE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), rect.value()); ++ ++ path.AppendPointAndClose({0, 0}, CFX_Path::Point::Type::kLine); ++ ASSERT_EQ(5u, path.GetPoints().size()); ++ EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(3)); ++ EXPECT_TRUE(path.IsClosingFigure(3)); ++ EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(4)); ++ EXPECT_TRUE(path.IsClosingFigure(4)); ++ EXPECT_TRUE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 1, 1), rect.value()); ++} ++ ++TEST(CFX_Path, FivePointRect) { ++ CFX_Path path; ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); ++ ASSERT_EQ(5u, path.GetPoints().size()); ++ EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(4)); ++ EXPECT_FALSE(path.IsClosingFigure(4)); ++ EXPECT_TRUE(path.IsRect()); ++ absl::optional rect = path.GetRect(nullptr); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), rect.value()); ++ ++ path.ClosePath(); ++ ASSERT_EQ(5u, path.GetPoints().size()); ++ EXPECT_EQ(CFX_Path::Point::Type::kLine, path.GetType(4)); ++ EXPECT_TRUE(path.IsClosingFigure(4)); ++ EXPECT_TRUE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), rect.value()); ++} ++ ++TEST(CFX_Path, SixPlusPointRect) { ++ CFX_Path path; ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); ++ EXPECT_TRUE(path.IsRect()); ++ absl::optional rect = path.GetRect(nullptr); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), rect.value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), path.GetBoundingBox()); ++ ++ path.Clear(); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); ++ EXPECT_TRUE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), rect.value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), path.GetBoundingBox()); ++} ++ ++TEST(CFX_Path, NotRect) { ++ CFX_Path path; ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 0.1f}, CFX_Path::Point::Type::kLine); ++ EXPECT_FALSE(path.IsRect()); ++ absl::optional rect = path.GetRect(nullptr); ++ EXPECT_FALSE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), path.GetBoundingBox()); ++ ++ path.ClosePath(); ++ EXPECT_FALSE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ EXPECT_FALSE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), path.GetBoundingBox()); ++ ++ path.Clear(); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({3, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPointAndClose({0, 1}, CFX_Path::Point::Type::kLine); ++ EXPECT_FALSE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ EXPECT_FALSE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 3, 1), path.GetBoundingBox()); ++ ++ path.Clear(); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPointAndClose({0, 1}, CFX_Path::Point::Type::kMove); ++ EXPECT_FALSE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ EXPECT_FALSE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), path.GetBoundingBox()); ++ ++ path.Clear(); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({3, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPointAndClose({0, 1}, CFX_Path::Point::Type::kLine); ++ EXPECT_FALSE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ EXPECT_FALSE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 3, 1), path.GetBoundingBox()); ++ ++ path.Clear(); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); ++ EXPECT_FALSE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ EXPECT_FALSE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), path.GetBoundingBox()); ++ ++ path.Clear(); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); ++ EXPECT_FALSE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ EXPECT_FALSE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 2, 1), path.GetBoundingBox()); ++ ++ path.Clear(); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({2, 2}, CFX_Path::Point::Type::kLine); ++ EXPECT_FALSE(path.IsRect()); ++ rect = path.GetRect(nullptr); ++ EXPECT_FALSE(rect.has_value()); ++ const CFX_Matrix kScaleMatrix(1, 0, 0, 2, 60, 70); ++ rect = path.GetRect(&kScaleMatrix); ++ EXPECT_FALSE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 2, 2), path.GetBoundingBox()); ++} ++ ++TEST(CFX_Path, EmptyRect) { ++ // Document existing behavior where an empty rect is still considered a rect. ++ CFX_Path path; ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kMove); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); ++ EXPECT_TRUE(path.IsRect()); ++ absl::optional rect = path.GetRect(nullptr); ++ ASSERT_TRUE(rect.has_value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 0, 1), rect.value()); ++ EXPECT_EQ(CFX_FloatRect(0, 0, 0, 1), path.GetBoundingBox()); ++} ++ ++TEST(CFX_Path, Append) { ++ CFX_Path path; ++ path.AppendPoint({5, 6}, CFX_Path::Point::Type::kMove); ++ ASSERT_EQ(1u, path.GetPoints().size()); ++ EXPECT_EQ(CFX_PointF(5, 6), path.GetPoint(0)); ++ ++ CFX_Path empty_path; ++ path.Append(empty_path, nullptr); ++ ASSERT_EQ(1u, path.GetPoints().size()); ++ EXPECT_EQ(CFX_PointF(5, 6), path.GetPoint(0)); ++ ++ path.Append(path, nullptr); ++ ASSERT_EQ(2u, path.GetPoints().size()); ++ EXPECT_EQ(CFX_PointF(5, 6), path.GetPoint(0)); ++ EXPECT_EQ(CFX_PointF(5, 6), path.GetPoint(1)); ++ ++ const CFX_Matrix kScaleMatrix(1, 0, 0, 2, 60, 70); ++ path.Append(path, &kScaleMatrix); ++ ASSERT_EQ(4u, path.GetPoints().size()); ++ EXPECT_EQ(CFX_PointF(5, 6), path.GetPoint(0)); ++ EXPECT_EQ(CFX_PointF(5, 6), path.GetPoint(1)); ++ EXPECT_EQ(CFX_PointF(65, 82), path.GetPoint(2)); ++ EXPECT_EQ(CFX_PointF(65, 82), path.GetPoint(3)); ++} ++ ++TEST(CFX_Path, GetBoundingBoxForStrokePath) { ++ static constexpr float kLineWidth = 1.0f; ++ static constexpr float kMiterLimit = 1.0f; ++ ++ { ++ // Test the case that the first/last point is "move" and it closes the ++ // paths. ++ CFX_Path path; ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kMove); ++ path.ClosePath(); ++ EXPECT_EQ(CFX_FloatRect(2, 0, 2, 0), ++ path.GetBoundingBoxForStrokePath(kLineWidth, kMiterLimit)); ++ } ++ ++ { ++ // Test on a regular rect path. ++ CFX_Path path; ++ path.AppendPoint({2, 0}, CFX_Path::Point::Type::kMove); ++ path.AppendPoint({2, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 1}, CFX_Path::Point::Type::kLine); ++ path.AppendPoint({0, 0}, CFX_Path::Point::Type::kLine); ++ path.ClosePath(); ++ EXPECT_EQ(CFX_FloatRect(-1, -1, 3, 2), ++ path.GetBoundingBoxForStrokePath(kLineWidth, kMiterLimit)); ++ ++ // If the final point is "move" and the path remains open, it should not ++ // affect the bounding rect. ++ path.AppendPoint({20, 20}, CFX_Path::Point::Type::kMove); ++ EXPECT_EQ(CFX_FloatRect(-1, -1, 3, 2), ++ path.GetBoundingBoxForStrokePath(kLineWidth, kMiterLimit)); ++ } ++} +diff --git a/core/fxge/cfx_pathdata.cpp b/core/fxge/cfx_pathdata.cpp +deleted file mode 100644 +index 55ce854eb..000000000 +--- a/core/fxge/cfx_pathdata.cpp ++++ /dev/null +@@ -1,522 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fxge/cfx_pathdata.h" +- +-#include "core/fxcrt/fx_system.h" +-#include "third_party/base/numerics/safe_math.h" +- +-namespace { +- +-bool IsFoldingVerticalLine(const CFX_PointF& a, +- const CFX_PointF& b, +- const CFX_PointF& c) { +- return a.x == b.x && b.x == c.x && (b.y - a.y) * (b.y - c.y) > 0; +-} +- +-bool IsFoldingHorizontalLine(const CFX_PointF& a, +- const CFX_PointF& b, +- const CFX_PointF& c) { +- return a.y == b.y && b.y == c.y && (b.x - a.x) * (b.x - c.x) > 0; +-} +- +-bool IsFoldingDiagonalLine(const CFX_PointF& a, +- const CFX_PointF& b, +- const CFX_PointF& c) { +- return a.x != b.x && c.x != b.x && a.y != b.y && c.y != b.y && +- (a.y - b.y) * (c.x - b.x) == (c.y - b.y) * (a.x - b.x); +-} +- +-void UpdateLineEndPoints(CFX_FloatRect* rect, +- const CFX_PointF& start_pos, +- const CFX_PointF& end_pos, +- float hw) { +- if (start_pos.x == end_pos.x) { +- if (start_pos.y == end_pos.y) { +- rect->UpdateRect(end_pos + CFX_PointF(hw, hw)); +- rect->UpdateRect(end_pos - CFX_PointF(hw, hw)); +- return; +- } +- +- float point_y; +- if (end_pos.y < start_pos.y) +- point_y = end_pos.y - hw; +- else +- point_y = end_pos.y + hw; +- +- rect->UpdateRect(CFX_PointF(end_pos.x + hw, point_y)); +- rect->UpdateRect(CFX_PointF(end_pos.x - hw, point_y)); +- return; +- } +- +- if (start_pos.y == end_pos.y) { +- float point_x; +- if (end_pos.x < start_pos.x) +- point_x = end_pos.x - hw; +- else +- point_x = end_pos.x + hw; +- +- rect->UpdateRect(CFX_PointF(point_x, end_pos.y + hw)); +- rect->UpdateRect(CFX_PointF(point_x, end_pos.y - hw)); +- return; +- } +- +- CFX_PointF diff = end_pos - start_pos; +- float ll = FXSYS_sqrt2(diff.x, diff.y); +- float mx = end_pos.x + hw * diff.x / ll; +- float my = end_pos.y + hw * diff.y / ll; +- float dx1 = hw * diff.y / ll; +- float dy1 = hw * diff.x / ll; +- rect->UpdateRect(CFX_PointF(mx - dx1, my + dy1)); +- rect->UpdateRect(CFX_PointF(mx + dx1, my - dy1)); +-} +- +-void UpdateLineJoinPoints(CFX_FloatRect* rect, +- const CFX_PointF& start_pos, +- const CFX_PointF& mid_pos, +- const CFX_PointF& end_pos, +- float half_width, +- float miter_limit) { +- float start_k = 0; +- float start_c = 0; +- float end_k = 0; +- float end_c = 0; +- float start_len = 0; +- float start_dc = 0; +- float end_len = 0; +- float end_dc = 0; +- float one_twentieth = 1.0f / 20; +- +- bool bStartVert = fabs(start_pos.x - mid_pos.x) < one_twentieth; +- bool bEndVert = fabs(mid_pos.x - end_pos.x) < one_twentieth; +- if (bStartVert && bEndVert) { +- int start_dir = mid_pos.y > start_pos.y ? 1 : -1; +- float point_y = mid_pos.y + half_width * start_dir; +- rect->UpdateRect(CFX_PointF(mid_pos.x + half_width, point_y)); +- rect->UpdateRect(CFX_PointF(mid_pos.x - half_width, point_y)); +- return; +- } +- +- if (!bStartVert) { +- CFX_PointF start_to_mid = start_pos - mid_pos; +- start_k = (mid_pos.y - start_pos.y) / (mid_pos.x - start_pos.x); +- start_c = mid_pos.y - (start_k * mid_pos.x); +- start_len = FXSYS_sqrt2(start_to_mid.x, start_to_mid.y); +- start_dc = +- static_cast(fabs(half_width * start_len / start_to_mid.x)); +- } +- if (!bEndVert) { +- CFX_PointF end_to_mid = end_pos - mid_pos; +- end_k = end_to_mid.y / end_to_mid.x; +- end_c = mid_pos.y - (end_k * mid_pos.x); +- end_len = FXSYS_sqrt2(end_to_mid.x, end_to_mid.y); +- end_dc = static_cast(fabs(half_width * end_len / end_to_mid.x)); +- } +- if (bStartVert) { +- CFX_PointF outside(start_pos.x, 0); +- if (end_pos.x < start_pos.x) +- outside.x += half_width; +- else +- outside.x -= half_width; +- +- if (start_pos.y < (end_k * start_pos.x) + end_c) +- outside.y = (end_k * outside.x) + end_c + end_dc; +- else +- outside.y = (end_k * outside.x) + end_c - end_dc; +- +- rect->UpdateRect(outside); +- return; +- } +- +- if (bEndVert) { +- CFX_PointF outside(end_pos.x, 0); +- if (start_pos.x < end_pos.x) +- outside.x += half_width; +- else +- outside.x -= half_width; +- +- if (end_pos.y < (start_k * end_pos.x) + start_c) +- outside.y = (start_k * outside.x) + start_c + start_dc; +- else +- outside.y = (start_k * outside.x) + start_c - start_dc; +- +- rect->UpdateRect(outside); +- return; +- } +- +- if (fabs(start_k - end_k) < one_twentieth) { +- int start_dir = mid_pos.x > start_pos.x ? 1 : -1; +- int end_dir = end_pos.x > mid_pos.x ? 1 : -1; +- if (start_dir == end_dir) +- UpdateLineEndPoints(rect, mid_pos, end_pos, half_width); +- else +- UpdateLineEndPoints(rect, start_pos, mid_pos, half_width); +- return; +- } +- +- float start_outside_c = start_c; +- if (end_pos.y < (start_k * end_pos.x) + start_c) +- start_outside_c += start_dc; +- else +- start_outside_c -= start_dc; +- +- float end_outside_c = end_c; +- if (start_pos.y < (end_k * start_pos.x) + end_c) +- end_outside_c += end_dc; +- else +- end_outside_c -= end_dc; +- +- float join_x = (end_outside_c - start_outside_c) / (start_k - end_k); +- float join_y = start_k * join_x + start_outside_c; +- rect->UpdateRect(CFX_PointF(join_x, join_y)); +-} +- +-} // namespace +- +-FX_PATHPOINT::FX_PATHPOINT() = default; +- +-FX_PATHPOINT::FX_PATHPOINT(const CFX_PointF& point, FXPT_TYPE type, bool close) +- : m_Point(point), m_Type(type), m_CloseFigure(close) {} +- +-FX_PATHPOINT::FX_PATHPOINT(const FX_PATHPOINT& other) = default; +- +-FX_PATHPOINT::~FX_PATHPOINT() = default; +- +-CFX_PathData::CFX_PathData() = default; +- +-CFX_PathData::CFX_PathData(const CFX_PathData& src) = default; +- +-CFX_PathData::CFX_PathData(CFX_PathData&& src) = default; +- +-CFX_PathData::~CFX_PathData() = default; +- +-void CFX_PathData::Clear() { +- m_Points.clear(); +-} +- +-void CFX_PathData::ClosePath() { +- if (m_Points.empty()) +- return; +- m_Points.back().m_CloseFigure = true; +-} +- +-void CFX_PathData::Append(const CFX_PathData* pSrc, const CFX_Matrix* pMatrix) { +- if (pSrc->m_Points.empty()) +- return; +- +- size_t cur_size = m_Points.size(); +- m_Points.insert(m_Points.end(), pSrc->m_Points.begin(), pSrc->m_Points.end()); +- +- if (!pMatrix) +- return; +- +- for (size_t i = cur_size; i < m_Points.size(); i++) +- m_Points[i].m_Point = pMatrix->Transform(m_Points[i].m_Point); +-} +- +-void CFX_PathData::AppendPoint(const CFX_PointF& point, +- FXPT_TYPE type, +- bool closeFigure) { +- m_Points.push_back(FX_PATHPOINT(point, type, closeFigure)); +-} +- +-void CFX_PathData::AppendLine(const CFX_PointF& pt1, const CFX_PointF& pt2) { +- if (m_Points.empty() || fabs(m_Points.back().m_Point.x - pt1.x) > 0.001 || +- fabs(m_Points.back().m_Point.y - pt1.y) > 0.001) { +- AppendPoint(pt1, FXPT_TYPE::MoveTo, false); +- } +- AppendPoint(pt2, FXPT_TYPE::LineTo, false); +-} +- +-void CFX_PathData::AppendFloatRect(const CFX_FloatRect& rect) { +- return AppendRect(rect.left, rect.bottom, rect.right, rect.top); +-} +- +-void CFX_PathData::AppendRect(float left, +- float bottom, +- float right, +- float top) { +- CFX_PointF left_bottom(left, bottom); +- CFX_PointF left_top(left, top); +- CFX_PointF right_top(right, top); +- CFX_PointF right_bottom(right, bottom); +- +- AppendLine(left_bottom, left_top); +- AppendLine(left_top, right_top); +- AppendLine(right_top, right_bottom); +- AppendLine(right_bottom, left_bottom); +- ClosePath(); +-} +- +-CFX_FloatRect CFX_PathData::GetBoundingBox() const { +- if (m_Points.empty()) +- return CFX_FloatRect(); +- +- CFX_FloatRect rect; +- rect.InitRect(m_Points[0].m_Point); +- for (size_t i = 1; i < m_Points.size(); i++) +- rect.UpdateRect(m_Points[i].m_Point); +- return rect; +-} +- +-CFX_FloatRect CFX_PathData::GetBoundingBox(float line_width, +- float miter_limit) const { +- CFX_FloatRect rect(100000.0f, 100000.0f, -100000.0f, -100000.0f); +- size_t iPoint = 0; +- float half_width = line_width; +- int iStartPoint = 0; +- int iEndPoint = 0; +- int iMiddlePoint = 0; +- bool bJoin; +- while (iPoint < m_Points.size()) { +- if (m_Points[iPoint].IsTypeAndOpen(FXPT_TYPE::MoveTo)) { +- if (iPoint + 1 == m_Points.size()) +- break; +- +- iStartPoint = iPoint + 1; +- iEndPoint = iPoint; +- bJoin = false; +- } else { +- if (m_Points[iPoint].IsTypeAndOpen(FXPT_TYPE::BezierTo)) { +- rect.UpdateRect(m_Points[iPoint].m_Point); +- rect.UpdateRect(m_Points[iPoint + 1].m_Point); +- iPoint += 2; +- } +- if (iPoint == m_Points.size() - 1 || +- m_Points[iPoint + 1].IsTypeAndOpen(FXPT_TYPE::MoveTo)) { +- iStartPoint = iPoint - 1; +- iEndPoint = iPoint; +- bJoin = false; +- } else { +- iStartPoint = iPoint - 1; +- iMiddlePoint = iPoint; +- iEndPoint = iPoint + 1; +- bJoin = true; +- } +- } +- +- CFX_PointF start_pos = m_Points[iStartPoint].m_Point; +- CFX_PointF end_pos = m_Points[iEndPoint].m_Point; +- if (bJoin) { +- CFX_PointF mid_pos = m_Points[iMiddlePoint].m_Point; +- UpdateLineJoinPoints(&rect, start_pos, mid_pos, end_pos, half_width, +- miter_limit); +- } else { +- UpdateLineEndPoints(&rect, start_pos, end_pos, half_width); +- } +- iPoint++; +- } +- return rect; +-} +- +-void CFX_PathData::Transform(const CFX_Matrix& matrix) { +- for (auto& point : m_Points) +- point.m_Point = matrix.Transform(point.m_Point); +-} +- +-bool CFX_PathData::GetZeroAreaPath(const CFX_Matrix* pMatrix, +- bool bAdjust, +- CFX_PathData* NewPath, +- bool* bThin, +- bool* setIdentity) const { +- *setIdentity = false; +- if (m_Points.size() < 3) +- return false; +- +- if (m_Points.size() == 3 && m_Points[0].m_Type == FXPT_TYPE::MoveTo && +- m_Points[1].m_Type == FXPT_TYPE::LineTo && +- m_Points[2].m_Type == FXPT_TYPE::LineTo && +- m_Points[0].m_Point == m_Points[2].m_Point) { +- for (size_t i = 0; i < 2; i++) { +- CFX_PointF point = m_Points[i].m_Point; +- if (bAdjust) { +- if (pMatrix) +- point = pMatrix->Transform(point); +- +- point = CFX_PointF(static_cast(point.x) + 0.5f, +- static_cast(point.y) + 0.5f); +- } +- NewPath->AppendPoint( +- point, i == 0 ? FXPT_TYPE::MoveTo : FXPT_TYPE::LineTo, false); +- } +- if (bAdjust && pMatrix) +- *setIdentity = true; +- +- // Note, they both have to be not equal. +- if (m_Points[0].m_Point.x != m_Points[1].m_Point.x && +- m_Points[0].m_Point.y != m_Points[1].m_Point.y) { +- *bThin = true; +- } +- return true; +- } +- +- if (((m_Points.size() > 3) && (m_Points.size() % 2))) { +- int mid = m_Points.size() / 2; +- bool bZeroArea = false; +- CFX_PathData t_path; +- for (int i = 0; i < mid; i++) { +- if (!(m_Points[mid - i - 1].m_Point == m_Points[mid + i + 1].m_Point && +- m_Points[mid - i - 1].m_Type != FXPT_TYPE::BezierTo && +- m_Points[mid + i + 1].m_Type != FXPT_TYPE::BezierTo)) { +- bZeroArea = true; +- break; +- } +- +- t_path.AppendPoint(m_Points[mid - i].m_Point, FXPT_TYPE::MoveTo, false); +- t_path.AppendPoint(m_Points[mid - i - 1].m_Point, FXPT_TYPE::LineTo, +- false); +- } +- if (!bZeroArea) { +- NewPath->Append(&t_path, nullptr); +- *bThin = true; +- return true; +- } +- } +- +- int startPoint = 0; +- for (size_t i = 0; i < m_Points.size(); i++) { +- FXPT_TYPE point_type = m_Points[i].m_Type; +- if (point_type == FXPT_TYPE::MoveTo) { +- startPoint = i; +- continue; +- } +- +- if (point_type == FXPT_TYPE::BezierTo) { +- i += 2; +- continue; +- } +- +- ASSERT(point_type == FXPT_TYPE::LineTo); +- int next_index = +- (i + 1 - startPoint) % (m_Points.size() - startPoint) + startPoint; +- const FX_PATHPOINT& next = m_Points[next_index]; +- if (next.m_Type == FXPT_TYPE::BezierTo || next.m_Type == FXPT_TYPE::MoveTo) +- continue; +- +- const FX_PATHPOINT& prev = m_Points[i - 1]; +- const FX_PATHPOINT& cur = m_Points[i]; +- if (IsFoldingVerticalLine(prev.m_Point, cur.m_Point, next.m_Point)) { +- bool use_prev = fabs(cur.m_Point.y - prev.m_Point.y) < +- fabs(cur.m_Point.y - next.m_Point.y); +- const FX_PATHPOINT& start = use_prev ? prev : cur; +- const FX_PATHPOINT& end = use_prev ? m_Points[next_index - 1] : next; +- NewPath->AppendPoint(start.m_Point, FXPT_TYPE::MoveTo, false); +- NewPath->AppendPoint(end.m_Point, FXPT_TYPE::LineTo, false); +- continue; +- } +- +- if (IsFoldingHorizontalLine(prev.m_Point, cur.m_Point, next.m_Point) || +- IsFoldingDiagonalLine(prev.m_Point, cur.m_Point, next.m_Point)) { +- bool use_prev = fabs(cur.m_Point.x - prev.m_Point.x) < +- fabs(cur.m_Point.x - next.m_Point.x); +- const FX_PATHPOINT& start = use_prev ? prev : cur; +- const FX_PATHPOINT& end = use_prev ? m_Points[next_index - 1] : next; +- NewPath->AppendPoint(start.m_Point, FXPT_TYPE::MoveTo, false); +- NewPath->AppendPoint(end.m_Point, FXPT_TYPE::LineTo, false); +- continue; +- } +- } +- +- size_t new_path_size = NewPath->GetPoints().size(); +- if (m_Points.size() > 3 && new_path_size > 0) +- *bThin = true; +- return new_path_size != 0; +-} +- +-bool CFX_PathData::IsRect() const { +- if (m_Points.size() != 5 && m_Points.size() != 4) +- return false; +- +- if ((m_Points.size() == 5 && m_Points[0].m_Point != m_Points[4].m_Point) || +- m_Points[0].m_Point == m_Points[2].m_Point || +- m_Points[1].m_Point == m_Points[3].m_Point) { +- return false; +- } +- // Note, both x,y have to not equal. +- if (m_Points[0].m_Point.x != m_Points[3].m_Point.x && +- m_Points[0].m_Point.y != m_Points[3].m_Point.y) { +- return false; +- } +- +- for (int i = 1; i < 4; i++) { +- if (m_Points[i].m_Type != FXPT_TYPE::LineTo) +- return false; +- // Note, both x,y have to not equal. +- if (m_Points[i].m_Point.x != m_Points[i - 1].m_Point.x && +- m_Points[i].m_Point.y != m_Points[i - 1].m_Point.y) { +- return false; +- } +- } +- return m_Points.size() == 5 || m_Points[3].m_CloseFigure; +-} +- +-bool CFX_PathData::IsRect(const CFX_Matrix* pMatrix, +- CFX_FloatRect* pRect) const { +- if (!pMatrix) { +- if (!IsRect()) +- return false; +- +- if (pRect) { +- pRect->left = m_Points[0].m_Point.x; +- pRect->right = m_Points[2].m_Point.x; +- pRect->bottom = m_Points[0].m_Point.y; +- pRect->top = m_Points[2].m_Point.y; +- pRect->Normalize(); +- } +- return true; +- } +- +- if (m_Points.size() != 5 && m_Points.size() != 4) +- return false; +- +- if ((m_Points.size() == 5 && m_Points[0].m_Point != m_Points[4].m_Point) || +- m_Points[1].m_Point == m_Points[3].m_Point) { +- return false; +- } +- // Note, both x,y not equal. +- if (m_Points.size() == 4 && m_Points[0].m_Point.x != m_Points[3].m_Point.x && +- m_Points[0].m_Point.y != m_Points[3].m_Point.y) { +- return false; +- } +- +- CFX_PointF points[5]; +- for (size_t i = 0; i < m_Points.size(); i++) { +- points[i] = pMatrix->Transform(m_Points[i].m_Point); +- +- if (i == 0) +- continue; +- if (m_Points[i].m_Type != FXPT_TYPE::LineTo) +- return false; +- if (points[i].x != points[i - 1].x && points[i].y != points[i - 1].y) +- return false; +- } +- +- if (pRect) { +- pRect->left = points[0].x; +- pRect->right = points[2].x; +- pRect->bottom = points[0].y; +- pRect->top = points[2].y; +- pRect->Normalize(); +- } +- return true; +-} +- +-CFX_RetainablePathData::CFX_RetainablePathData() = default; +- +-// Note: can't default the copy constructor since Retainable<> has a deleted +-// copy constructor (as it should). Instead, we want the default Retainable<> +-// constructor to be invoked so as to create a copy with a ref-count of 1 as +-// of the time it is created, then populate the remainder of the members from +-// the |src| object. +-CFX_RetainablePathData::CFX_RetainablePathData( +- const CFX_RetainablePathData& src) +- : CFX_PathData(src) {} +- +-CFX_RetainablePathData::~CFX_RetainablePathData() = default; +- +-RetainPtr CFX_RetainablePathData::Clone() const { +- return pdfium::MakeRetain(*this); +-} +diff --git a/core/fxge/cfx_pathdata.h b/core/fxge/cfx_pathdata.h +deleted file mode 100644 +index 3fd57e217..000000000 +--- a/core/fxge/cfx_pathdata.h ++++ /dev/null +@@ -1,88 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXGE_CFX_PATHDATA_H_ +-#define CORE_FXGE_CFX_PATHDATA_H_ +- +-#include +- +-#include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" +-#include "core/fxcrt/retain_ptr.h" +- +-enum class FXPT_TYPE : uint8_t { LineTo, BezierTo, MoveTo }; +- +-class FX_PATHPOINT { +- public: +- FX_PATHPOINT(); +- FX_PATHPOINT(const CFX_PointF& point, FXPT_TYPE type, bool close); +- FX_PATHPOINT(const FX_PATHPOINT& other); +- ~FX_PATHPOINT(); +- +- bool IsTypeAndOpen(FXPT_TYPE type) const { +- return m_Type == type && !m_CloseFigure; +- } +- +- CFX_PointF m_Point; +- FXPT_TYPE m_Type; +- bool m_CloseFigure; +-}; +- +-class CFX_PathData { +- public: +- CFX_PathData(); +- CFX_PathData(const CFX_PathData& src); +- CFX_PathData(CFX_PathData&& src); +- ~CFX_PathData(); +- +- void Clear(); +- +- FXPT_TYPE GetType(int index) const { return m_Points[index].m_Type; } +- bool IsClosingFigure(int index) const { +- return m_Points[index].m_CloseFigure; +- } +- +- CFX_PointF GetPoint(int index) const { return m_Points[index].m_Point; } +- const std::vector& GetPoints() const { return m_Points; } +- std::vector& GetPoints() { return m_Points; } +- +- CFX_FloatRect GetBoundingBox() const; +- CFX_FloatRect GetBoundingBox(float line_width, float miter_limit) const; +- +- void Transform(const CFX_Matrix& matrix); +- bool IsRect() const; +- bool GetZeroAreaPath(const CFX_Matrix* pMatrix, +- bool bAdjust, +- CFX_PathData* NewPath, +- bool* bThin, +- bool* setIdentity) const; +- bool IsRect(const CFX_Matrix* pMatrix, CFX_FloatRect* rect) const; +- +- void Append(const CFX_PathData* pSrc, const CFX_Matrix* pMatrix); +- void AppendFloatRect(const CFX_FloatRect& rect); +- void AppendRect(float left, float bottom, float right, float top); +- void AppendLine(const CFX_PointF& pt1, const CFX_PointF& pt2); +- void AppendPoint(const CFX_PointF& point, FXPT_TYPE type, bool closeFigure); +- void ClosePath(); +- +- private: +- std::vector m_Points; +-}; +- +-class CFX_RetainablePathData final : public Retainable, public CFX_PathData { +- public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- +- RetainPtr Clone() const; +- +- private: +- CFX_RetainablePathData(); +- CFX_RetainablePathData(const CFX_RetainablePathData& src); +- ~CFX_RetainablePathData() override; +-}; +- +-#endif // CORE_FXGE_CFX_PATHDATA_H_ +diff --git a/core/fxge/cfx_renderdevice.cpp b/core/fxge/cfx_renderdevice.cpp +index b1474c5e7..d39a02f06 100644 +--- a/core/fxge/cfx_renderdevice.cpp ++++ b/core/fxge/cfx_renderdevice.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,8 @@ + + #include "core/fxge/cfx_renderdevice.h" + ++#include ++ + #include + #include + #include +@@ -14,28 +16,33 @@ + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxge/cfx_color.h" + #include "core/fxge/cfx_defaultrenderdevice.h" ++#include "core/fxge/cfx_fillrenderoptions.h" + #include "core/fxge/cfx_font.h" + #include "core/fxge/cfx_fontmgr.h" + #include "core/fxge/cfx_gemodule.h" + #include "core/fxge/cfx_glyphbitmap.h" + #include "core/fxge/cfx_glyphcache.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" ++#include "core/fxge/cfx_textrenderoptions.h" + #include "core/fxge/dib/cfx_dibitmap.h" + #include "core/fxge/dib/cfx_imagerenderer.h" + #include "core/fxge/fx_font.h" + #include "core/fxge/renderdevicedriver_iface.h" + #include "core/fxge/text_char_pos.h" + #include "core/fxge/text_glyph_pos.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/span.h" + +-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ +-#include "third_party/skia/include/core/SkTypes.h" ++#if defined(_SKIA_SUPPORT_) ++#include "third_party/skia/include/core/SkTypes.h" // nogncheck + #endif + + namespace { + + void AdjustGlyphSpace(std::vector* pGlyphAndPos) { +- ASSERT(pGlyphAndPos->size() > 1); ++ DCHECK_GT(pGlyphAndPos->size(), 1u); + std::vector& glyphs = *pGlyphAndPos; + bool bVertical = glyphs.back().m_Origin.x == glyphs.front().m_Origin.x; + if (!bVertical && (glyphs.back().m_Origin.y != glyphs.front().m_Origin.y)) +@@ -44,12 +51,13 @@ void AdjustGlyphSpace(std::vector* pGlyphAndPos) { + for (size_t i = glyphs.size() - 1; i > 1; --i) { + const TextGlyphPos& next = glyphs[i]; + int next_origin = bVertical ? next.m_Origin.y : next.m_Origin.x; +- float next_origin_f = bVertical ? next.m_fOrigin.y : next.m_fOrigin.x; ++ float next_origin_f = ++ bVertical ? next.m_fDeviceOrigin.y : next.m_fDeviceOrigin.x; + + TextGlyphPos& current = glyphs[i - 1]; + int& current_origin = bVertical ? current.m_Origin.y : current.m_Origin.x; + float current_origin_f = +- bVertical ? current.m_fOrigin.y : current.m_fOrigin.x; ++ bVertical ? current.m_fDeviceOrigin.y : current.m_fDeviceOrigin.x; + + FX_SAFE_INT32 safe_space = next_origin; + safe_space -= current_origin; +@@ -71,7 +79,7 @@ void AdjustGlyphSpace(std::vector* pGlyphAndPos) { + } + } + +-const uint8_t g_TextGammaAdjust[256] = { ++constexpr uint8_t kTextGammaAdjust[256] = { + 0, 2, 3, 4, 6, 7, 8, 10, 11, 12, 13, 15, 16, 17, 18, + 19, 21, 22, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, + 36, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 51, 52, +@@ -93,35 +101,20 @@ const uint8_t g_TextGammaAdjust[256] = { + }; + + int TextGammaAdjust(int value) { +- ASSERT(value >= 0); +- ASSERT(value <= 255); +- return g_TextGammaAdjust[value]; ++ DCHECK_GE(value, 0); ++ DCHECK_LE(value, 255); ++ return kTextGammaAdjust[value]; + } + + int CalcAlpha(int src, int alpha) { + return src * alpha / 255; + } + +-void Merge(uint8_t src, int channel, int alpha, uint8_t* dest) { +- *dest = FXDIB_ALPHA_MERGE(*dest, channel, CalcAlpha(src, alpha)); +-} +- + void MergeGammaAdjust(uint8_t src, int channel, int alpha, uint8_t* dest) { + *dest = + FXDIB_ALPHA_MERGE(*dest, channel, CalcAlpha(TextGammaAdjust(src), alpha)); + } + +-void MergeGammaAdjustBgr(const uint8_t* src, +- int r, +- int g, +- int b, +- int a, +- uint8_t* dest) { +- MergeGammaAdjust(src[0], b, a, &dest[0]); +- MergeGammaAdjust(src[1], g, a, &dest[1]); +- MergeGammaAdjust(src[2], r, a, &dest[2]); +-} +- + void MergeGammaAdjustRgb(const uint8_t* src, + int r, + int g, +@@ -207,7 +200,7 @@ void NormalizeSrc(bool has_alpha, + NormalizeArgb(src_value, r, g, b, a, dest, src_alpha); + } + +-void NextPixel(uint8_t** src_scan, uint8_t** dst_scan, int bpp) { ++void NextPixel(const uint8_t** src_scan, uint8_t** dst_scan, int bpp) { + *src_scan += 3; + *dst_scan += bpp; + } +@@ -224,72 +217,26 @@ void DrawNormalTextHelper(const RetainPtr& bitmap, + int top, + int start_col, + int end_col, +- bool bNormal, +- bool bBGRStripe, ++ bool normalize, + int x_subpixel, + int a, + int r, + int g, + int b) { +- const bool has_alpha = bitmap->GetFormat() == FXDIB_Argb; +- uint8_t* src_buf = pGlyph->GetBuffer(); +- int src_pitch = pGlyph->GetPitch(); +- uint8_t* dest_buf = bitmap->GetBuffer(); +- int dest_pitch = bitmap->GetPitch(); ++ const bool has_alpha = bitmap->GetFormat() == FXDIB_Format::kArgb; + const int Bpp = has_alpha ? 4 : bitmap->GetBPP() / 8; + for (int row = 0; row < nrows; ++row) { + int dest_row = row + top; + if (dest_row < 0 || dest_row >= bitmap->GetHeight()) + continue; + +- uint8_t* src_scan = src_buf + row * src_pitch + (start_col - left) * 3; +- uint8_t* dest_scan = dest_buf + dest_row * dest_pitch + start_col * Bpp; +- if (bBGRStripe) { +- if (x_subpixel == 0) { +- for (int col = start_col; col < end_col; ++col) { +- if (has_alpha) { +- Merge(src_scan[2], r, a, &dest_scan[2]); +- Merge(src_scan[1], g, a, &dest_scan[1]); +- Merge(src_scan[0], b, a, &dest_scan[0]); +- } else { +- MergeGammaAdjustBgr(&src_scan[0], r, g, b, a, &dest_scan[0]); +- } +- SetAlpha(has_alpha, dest_scan); +- NextPixel(&src_scan, &dest_scan, Bpp); +- } +- continue; +- } +- if (x_subpixel == 1) { +- MergeGammaAdjust(src_scan[1], r, a, &dest_scan[2]); +- MergeGammaAdjust(src_scan[0], g, a, &dest_scan[1]); +- if (start_col > left) +- MergeGammaAdjust(src_scan[-1], b, a, &dest_scan[0]); +- SetAlpha(has_alpha, dest_scan); +- NextPixel(&src_scan, &dest_scan, Bpp); +- for (int col = start_col + 1; col < end_col - 1; ++col) { +- MergeGammaAdjustBgr(&src_scan[-1], r, g, b, a, &dest_scan[0]); +- SetAlpha(has_alpha, dest_scan); +- NextPixel(&src_scan, &dest_scan, Bpp); +- } +- continue; +- } +- MergeGammaAdjust(src_scan[0], r, a, &dest_scan[2]); +- if (start_col > left) { +- MergeGammaAdjust(src_scan[-1], g, a, &dest_scan[1]); +- MergeGammaAdjust(src_scan[-2], b, a, &dest_scan[0]); +- } +- SetAlpha(has_alpha, dest_scan); +- NextPixel(&src_scan, &dest_scan, Bpp); +- for (int col = start_col + 1; col < end_col - 1; ++col) { +- MergeGammaAdjustBgr(&src_scan[-2], r, g, b, a, &dest_scan[0]); +- SetAlpha(has_alpha, dest_scan); +- NextPixel(&src_scan, &dest_scan, Bpp); +- } +- continue; +- } ++ const uint8_t* src_scan = ++ pGlyph->GetScanline(row).subspan((start_col - left) * 3).data(); ++ uint8_t* dest_scan = ++ bitmap->GetWritableScanline(dest_row).subspan(start_col * Bpp).data(); + if (x_subpixel == 0) { + for (int col = start_col; col < end_col; ++col) { +- if (bNormal) { ++ if (normalize) { + int src_value = AverageRgb(&src_scan[0]); + NormalizeDest(has_alpha, src_value, r, g, b, a, dest_scan); + } else { +@@ -301,7 +248,7 @@ void DrawNormalTextHelper(const RetainPtr& bitmap, + continue; + } + if (x_subpixel == 1) { +- if (bNormal) { ++ if (normalize) { + int src_value = start_col > left ? AverageRgb(&src_scan[-1]) + : (src_scan[0] + src_scan[1]) / 3; + NormalizeSrc(has_alpha, src_value, r, g, b, a, dest_scan); +@@ -314,7 +261,7 @@ void DrawNormalTextHelper(const RetainPtr& bitmap, + } + NextPixel(&src_scan, &dest_scan, Bpp); + for (int col = start_col + 1; col < end_col; ++col) { +- if (bNormal) { ++ if (normalize) { + int src_value = AverageRgb(&src_scan[-1]); + NormalizeDest(has_alpha, src_value, r, g, b, a, dest_scan); + } else { +@@ -325,7 +272,7 @@ void DrawNormalTextHelper(const RetainPtr& bitmap, + } + continue; + } +- if (bNormal) { ++ if (normalize) { + int src_value = + start_col > left ? AverageRgb(&src_scan[-2]) : src_scan[0] / 3; + NormalizeSrc(has_alpha, src_value, r, g, b, a, dest_scan); +@@ -339,7 +286,7 @@ void DrawNormalTextHelper(const RetainPtr& bitmap, + } + NextPixel(&src_scan, &dest_scan, Bpp); + for (int col = start_col + 1; col < end_col; ++col) { +- if (bNormal) { ++ if (normalize) { + int src_value = AverageRgb(&src_scan[-2]); + NormalizeDest(has_alpha, src_value, r, g, b, a, dest_scan); + } else { +@@ -351,9 +298,10 @@ void DrawNormalTextHelper(const RetainPtr& bitmap, + } + } + +-bool ShouldDrawDeviceText(const CFX_Font* pFont, uint32_t text_flags) { +-#if defined(OS_MACOSX) +- if (text_flags & FXFONT_CIDFONT) ++bool ShouldDrawDeviceText(const CFX_Font* pFont, ++ const CFX_TextRenderOptions& options) { ++#if BUILDFLAG(IS_APPLE) ++ if (options.font_is_cid) + return false; + + const ByteString bsPsName = pFont->GetPsName(); +@@ -366,15 +314,178 @@ bool ShouldDrawDeviceText(const CFX_Font* pFont, uint32_t text_flags) { + return true; + } + ++// Returns true if the path is a 3-point path that draws A->B->A and forms a ++// zero area, or a 2-point path which draws A->B. ++bool CheckSimpleLinePath(pdfium::span points, ++ const CFX_Matrix* matrix, ++ bool adjust, ++ CFX_Path* new_path, ++ bool* thin, ++ bool* set_identity) { ++ if (points.size() != 2 && points.size() != 3) ++ return false; ++ ++ if (points[0].m_Type != CFX_Path::Point::Type::kMove || ++ points[1].m_Type != CFX_Path::Point::Type::kLine || ++ (points.size() == 3 && ++ (points[2].m_Type != CFX_Path::Point::Type::kLine || ++ points[0].m_Point != points[2].m_Point))) { ++ return false; ++ } ++ ++ // A special case that all points are identical, zero area is formed and no ++ // thin line needs to be drawn. ++ if (points[0].m_Point == points[1].m_Point) ++ return true; ++ ++ for (size_t i = 0; i < 2; i++) { ++ CFX_PointF point = points[i].m_Point; ++ if (adjust) { ++ if (matrix) ++ point = matrix->Transform(point); ++ ++ point = CFX_PointF(static_cast(point.x) + 0.5f, ++ static_cast(point.y) + 0.5f); ++ } ++ new_path->AppendPoint(point, points[i].m_Type); ++ } ++ if (adjust && matrix) ++ *set_identity = true; ++ ++ *thin = true; ++ return true; ++} ++ ++// Returns true if `points` is palindromic and forms zero area. Otherwise, ++// returns false. ++bool CheckPalindromicPath(pdfium::span points, ++ CFX_Path* new_path, ++ bool* thin) { ++ if (points.size() <= 3 || !(points.size() % 2)) ++ return false; ++ ++ const size_t mid = points.size() / 2; ++ CFX_Path temp_path; ++ for (size_t i = 0; i < mid; i++) { ++ const CFX_Path::Point& left = points[mid - i - 1]; ++ const CFX_Path::Point& right = points[mid + i + 1]; ++ bool zero_area = left.m_Point == right.m_Point && ++ left.m_Type != CFX_Path::Point::Type::kBezier && ++ right.m_Type != CFX_Path::Point::Type::kBezier; ++ if (!zero_area) ++ return false; ++ ++ temp_path.AppendPoint(points[mid - i].m_Point, ++ CFX_Path::Point::Type::kMove); ++ temp_path.AppendPoint(left.m_Point, CFX_Path::Point::Type::kLine); ++ } ++ ++ new_path->Append(temp_path, nullptr); ++ *thin = true; ++ return true; ++} ++ ++bool IsFoldingVerticalLine(const CFX_PointF& a, ++ const CFX_PointF& b, ++ const CFX_PointF& c) { ++ return a.x == b.x && b.x == c.x && (b.y - a.y) * (b.y - c.y) > 0; ++} ++ ++bool IsFoldingHorizontalLine(const CFX_PointF& a, ++ const CFX_PointF& b, ++ const CFX_PointF& c) { ++ return a.y == b.y && b.y == c.y && (b.x - a.x) * (b.x - c.x) > 0; ++} ++ ++bool IsFoldingDiagonalLine(const CFX_PointF& a, ++ const CFX_PointF& b, ++ const CFX_PointF& c) { ++ return a.x != b.x && c.x != b.x && a.y != b.y && c.y != b.y && ++ (a.y - b.y) * (c.x - b.x) == (c.y - b.y) * (a.x - b.x); ++} ++ ++bool GetZeroAreaPath(pdfium::span points, ++ const CFX_Matrix* matrix, ++ bool adjust, ++ CFX_Path* new_path, ++ bool* thin, ++ bool* set_identity) { ++ *set_identity = false; ++ ++ if (points.size() < 2) ++ return false; ++ ++ if (CheckSimpleLinePath(points, matrix, adjust, new_path, thin, ++ set_identity)) { ++ return true; ++ } ++ ++ if (CheckPalindromicPath(points, new_path, thin)) ++ return true; ++ ++ for (size_t i = 0; i < points.size(); i++) { ++ CFX_Path::Point::Type point_type = points[i].m_Type; ++ if (point_type == CFX_Path::Point::Type::kMove) { ++ DCHECK_EQ(0, i); ++ continue; ++ } ++ ++ if (point_type == CFX_Path::Point::Type::kBezier) { ++ i += 2; ++ DCHECK_LT(i, points.size()); ++ continue; ++ } ++ ++ DCHECK_EQ(point_type, CFX_Path::Point::Type::kLine); ++ size_t next_index = (i + 1) % (points.size()); ++ const CFX_Path::Point& next = points[next_index]; ++ if (next.m_Type != CFX_Path::Point::Type::kLine) ++ continue; ++ ++ const CFX_Path::Point& prev = points[i - 1]; ++ const CFX_Path::Point& cur = points[i]; ++ if (IsFoldingVerticalLine(prev.m_Point, cur.m_Point, next.m_Point)) { ++ bool use_prev = fabs(cur.m_Point.y - prev.m_Point.y) < ++ fabs(cur.m_Point.y - next.m_Point.y); ++ const CFX_Path::Point& start = use_prev ? prev : cur; ++ const CFX_Path::Point& end = use_prev ? cur : next; ++ new_path->AppendPoint(start.m_Point, CFX_Path::Point::Type::kMove); ++ new_path->AppendPoint(end.m_Point, CFX_Path::Point::Type::kLine); ++ continue; ++ } ++ ++ if (IsFoldingHorizontalLine(prev.m_Point, cur.m_Point, next.m_Point) || ++ IsFoldingDiagonalLine(prev.m_Point, cur.m_Point, next.m_Point)) { ++ bool use_prev = fabs(cur.m_Point.x - prev.m_Point.x) < ++ fabs(cur.m_Point.x - next.m_Point.x); ++ const CFX_Path::Point& start = use_prev ? prev : cur; ++ const CFX_Path::Point& end = use_prev ? cur : next; ++ new_path->AppendPoint(start.m_Point, CFX_Path::Point::Type::kMove); ++ new_path->AppendPoint(end.m_Point, CFX_Path::Point::Type::kLine); ++ continue; ++ } ++ } ++ ++ size_t new_path_size = new_path->GetPoints().size(); ++ if (points.size() > 3 && new_path_size > 0) ++ *thin = true; ++ return new_path_size != 0; ++} ++ ++FXDIB_Format GetCreateCompatibleBitmapFormat(int render_caps) { ++ if (render_caps & FXRC_BYTEMASK_OUTPUT) ++ return FXDIB_Format::k8bppMask; ++ if (render_caps & FXRC_ALPHA_OUTPUT) ++ return FXDIB_Format::kArgb; ++ return CFX_DIBBase::kPlatformRGBFormat; ++} ++ + } // namespace + + CFX_RenderDevice::CFX_RenderDevice() = default; + + CFX_RenderDevice::~CFX_RenderDevice() { + RestoreState(false); +-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ +- Flush(true); +-#endif + } + + // static +@@ -385,17 +496,10 @@ CFX_Matrix CFX_RenderDevice::GetFlipMatrix(float width, + return CFX_Matrix(width, 0, 0, -height, left, top + height); + } + +-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ +-void CFX_RenderDevice::Flush(bool release) { +- if (release) +- m_pDeviceDriver.reset(); +- else +- m_pDeviceDriver->Flush(); +-} +-#endif +- + void CFX_RenderDevice::SetDeviceDriver( + std::unique_ptr pDriver) { ++ DCHECK(pDriver); ++ DCHECK(!m_pDeviceDriver); + m_pDeviceDriver = std::move(pDriver); + InitDeviceInfo(); + } +@@ -441,54 +545,40 @@ bool CFX_RenderDevice::CreateCompatibleBitmap( + const RetainPtr& pDIB, + int width, + int height) const { +- if (m_RenderCaps & FXRC_CMYK_OUTPUT) { +- return pDIB->Create( +- width, height, +- m_RenderCaps & FXRC_ALPHA_OUTPUT ? FXDIB_Cmyka : FXDIB_Cmyk); +- } +- if (m_RenderCaps & FXRC_BYTEMASK_OUTPUT) +- return pDIB->Create(width, height, FXDIB_8bppMask); +-#if defined(OS_MACOSX) || defined _SKIA_SUPPORT_PATHS_ +- constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb32; +-#else +- constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb; +-#endif +- return pDIB->Create( +- width, height, +- m_RenderCaps & FXRC_ALPHA_OUTPUT ? FXDIB_Argb : kPlatformFormat); ++ return pDIB->Create(width, height, ++ GetCreateCompatibleBitmapFormat(m_RenderCaps)); + } + + void CFX_RenderDevice::SetBaseClip(const FX_RECT& rect) { + m_pDeviceDriver->SetBaseClip(rect); + } + +-bool CFX_RenderDevice::SetClip_PathFill(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- int fill_mode) { +- if (!m_pDeviceDriver->SetClip_PathFill(pPathData, pObject2Device, +- fill_mode)) { ++bool CFX_RenderDevice::SetClip_PathFill( ++ const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_FillRenderOptions& fill_options) { ++ if (!m_pDeviceDriver->SetClip_PathFill(path, pObject2Device, fill_options)) + return false; +- } ++ + UpdateClipBox(); + return true; + } + + bool CFX_RenderDevice::SetClip_PathStroke( +- const CFX_PathData* pPathData, ++ const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState) { +- if (!m_pDeviceDriver->SetClip_PathStroke(pPathData, pObject2Device, +- pGraphState)) { ++ if (!m_pDeviceDriver->SetClip_PathStroke(path, pObject2Device, pGraphState)) + return false; +- } ++ + UpdateClipBox(); + return true; + } + + bool CFX_RenderDevice::SetClip_Rect(const FX_RECT& rect) { +- CFX_PathData path; ++ CFX_Path path; + path.AppendRect(rect.left, rect.bottom, rect.right, rect.top); +- if (!SetClip_PathFill(&path, nullptr, FXFILL_WINDING)) ++ if (!SetClip_PathFill(path, nullptr, CFX_FillRenderOptions::WindingOptions())) + return false; + + UpdateClipBox(); +@@ -504,31 +594,44 @@ void CFX_RenderDevice::UpdateClipBox() { + m_ClipBox.bottom = m_Height; + } + +-bool CFX_RenderDevice::DrawPathWithBlend(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- const CFX_GraphStateData* pGraphState, +- uint32_t fill_color, +- uint32_t stroke_color, +- int fill_mode, +- BlendMode blend_type) { ++bool CFX_RenderDevice::DrawPath(const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_GraphStateData* pGraphState, ++ uint32_t fill_color, ++ uint32_t stroke_color, ++ const CFX_FillRenderOptions& fill_options) { ++ return DrawPathWithBlend(path, pObject2Device, pGraphState, fill_color, ++ stroke_color, fill_options, BlendMode::kNormal); ++} ++ ++bool CFX_RenderDevice::DrawPathWithBlend( ++ const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_GraphStateData* pGraphState, ++ uint32_t fill_color, ++ uint32_t stroke_color, ++ const CFX_FillRenderOptions& fill_options, ++ BlendMode blend_type) { ++ const bool fill = ++ fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill; ++ uint8_t fill_alpha = fill ? FXARGB_A(fill_color) : 0; + uint8_t stroke_alpha = pGraphState ? FXARGB_A(stroke_color) : 0; +- uint8_t fill_alpha = (fill_mode & 3) ? FXARGB_A(fill_color) : 0; +- const std::vector& pPoints = pPathData->GetPoints(); +- if (stroke_alpha == 0 && pPoints.size() == 2) { +- CFX_PointF pos1 = pPoints[0].m_Point; +- CFX_PointF pos2 = pPoints[1].m_Point; ++ pdfium::span points = path.GetPoints(); ++ if (stroke_alpha == 0 && points.size() == 2) { ++ CFX_PointF pos1 = points[0].m_Point; ++ CFX_PointF pos2 = points[1].m_Point; + if (pObject2Device) { + pos1 = pObject2Device->Transform(pos1); + pos2 = pObject2Device->Transform(pos2); + } +- DrawCosmeticLine(pos1, pos2, fill_color, fill_mode, blend_type); ++ DrawCosmeticLine(pos1, pos2, fill_color, fill_options, blend_type); + return true; + } + +- if ((pPoints.size() == 5 || pPoints.size() == 4) && stroke_alpha == 0) { +- CFX_FloatRect rect_f; +- if (!(fill_mode & FXFILL_RECT_AA) && +- pPathData->IsRect(pObject2Device, &rect_f)) { ++ if (stroke_alpha == 0 && !fill_options.rect_aa) { ++ absl::optional maybe_rect_f = path.GetRect(pObject2Device); ++ if (maybe_rect_f.has_value()) { ++ const CFX_FloatRect& rect_f = maybe_rect_f.value(); + FX_RECT rect_i = rect_f.GetOuterRect(); + + // Depending on the top/bottom, left/right values of the rect it's +@@ -569,64 +672,87 @@ bool CFX_RenderDevice::DrawPathWithBlend(const CFX_PathData* pPathData, + return true; + } + } +- if ((fill_mode & 3) && stroke_alpha == 0 && !(fill_mode & FX_FILL_STROKE) && +- !(fill_mode & FX_FILL_TEXT_MODE)) { +- CFX_PathData newPath; +- bool bThin = false; +- bool setIdentity = false; +- if (pPathData->GetZeroAreaPath(pObject2Device, +- !!m_pDeviceDriver->GetDriverType(), &newPath, +- &bThin, &setIdentity)) { +- CFX_GraphStateData graphState; +- graphState.m_LineWidth = 0.0f; +- +- uint32_t strokecolor = fill_color; +- if (bThin) +- strokecolor = (((fill_alpha >> 2) << 24) | (strokecolor & 0x00ffffff)); +- +- const CFX_Matrix* pMatrix = nullptr; +- if (pObject2Device && !pObject2Device->IsIdentity() && !setIdentity) +- pMatrix = pObject2Device; +- +- int smooth_path = FX_ZEROAREA_FILL; +- if (fill_mode & FXFILL_NOPATHSMOOTH) +- smooth_path |= FXFILL_NOPATHSMOOTH; +- +- m_pDeviceDriver->DrawPath(&newPath, pMatrix, &graphState, 0, strokecolor, +- smooth_path, blend_type); ++ ++ if (fill && stroke_alpha == 0 && !fill_options.stroke && ++ !fill_options.text_mode) { ++ bool adjust = !!m_pDeviceDriver->GetDriverType(); ++ std::vector sub_path; ++ for (size_t i = 0; i < points.size(); i++) { ++ CFX_Path::Point::Type point_type = points[i].m_Type; ++ if (point_type == CFX_Path::Point::Type::kMove) { ++ // Process the existing sub path. ++ DrawZeroAreaPath(sub_path, pObject2Device, adjust, ++ fill_options.aliased_path, fill_color, fill_alpha, ++ blend_type); ++ sub_path.clear(); ++ ++ // Start forming the next sub path. ++ sub_path.push_back(points[i]); ++ continue; ++ } ++ ++ if (point_type == CFX_Path::Point::Type::kBezier) { ++ sub_path.push_back(points[i]); ++ sub_path.push_back(points[i + 1]); ++ sub_path.push_back(points[i + 2]); ++ i += 2; ++ continue; ++ } ++ ++ DCHECK_EQ(point_type, CFX_Path::Point::Type::kLine); ++ sub_path.push_back(points[i]); + } ++ // Process the last sub paths. ++ DrawZeroAreaPath(sub_path, pObject2Device, adjust, ++ fill_options.aliased_path, fill_color, fill_alpha, ++ blend_type); + } +- if ((fill_mode & 3) && fill_alpha && stroke_alpha < 0xff && +- (fill_mode & FX_FILL_STROKE)) { ++ ++ if (fill && fill_alpha && stroke_alpha < 0xff && fill_options.stroke) { + if (m_RenderCaps & FXRC_FILLSTROKE_PATH) { +- return m_pDeviceDriver->DrawPath(pPathData, pObject2Device, pGraphState, +- fill_color, stroke_color, fill_mode, +- blend_type); ++#if defined(_SKIA_SUPPORT_) ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ m_pDeviceDriver->SetGroupKnockout(true); ++ } ++#endif ++ bool draw_fillstroke_path_result = m_pDeviceDriver->DrawPath( ++ path, pObject2Device, pGraphState, fill_color, stroke_color, ++ fill_options, blend_type); ++ ++#if defined(_SKIA_SUPPORT_) ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ // Restore the group knockout status for `m_pDeviceDriver` after ++ // finishing painting a fill-and-stroke path. ++ m_pDeviceDriver->SetGroupKnockout(false); ++ } ++#endif ++ return draw_fillstroke_path_result; + } +- return DrawFillStrokePath(pPathData, pObject2Device, pGraphState, +- fill_color, stroke_color, fill_mode, blend_type); ++ return DrawFillStrokePath(path, pObject2Device, pGraphState, fill_color, ++ stroke_color, fill_options, blend_type); + } +- return m_pDeviceDriver->DrawPath(pPathData, pObject2Device, pGraphState, +- fill_color, stroke_color, fill_mode, ++ return m_pDeviceDriver->DrawPath(path, pObject2Device, pGraphState, ++ fill_color, stroke_color, fill_options, + blend_type); + } + + // This can be removed once PDFium entirely relies on Skia +-bool CFX_RenderDevice::DrawFillStrokePath(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- const CFX_GraphStateData* pGraphState, +- uint32_t fill_color, +- uint32_t stroke_color, +- int fill_mode, +- BlendMode blend_type) { ++bool CFX_RenderDevice::DrawFillStrokePath( ++ const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_GraphStateData* pGraphState, ++ uint32_t fill_color, ++ uint32_t stroke_color, ++ const CFX_FillRenderOptions& fill_options, ++ BlendMode blend_type) { + if (!(m_RenderCaps & FXRC_GET_BITS)) + return false; + CFX_FloatRect bbox; + if (pGraphState) { +- bbox = pPathData->GetBoundingBox(pGraphState->m_LineWidth, +- pGraphState->m_MiterLimit); ++ bbox = path.GetBoundingBoxForStrokePath(pGraphState->m_LineWidth, ++ pGraphState->m_MiterLimit); + } else { +- bbox = pPathData->GetBoundingBox(); ++ bbox = path.GetBoundingBox(); + } + if (pObject2Device) + bbox = pObject2Device->TransformRect(bbox); +@@ -640,8 +766,7 @@ bool CFX_RenderDevice::DrawFillStrokePath(const CFX_PathData* pPathData, + if (!CreateCompatibleBitmap(bitmap, rect.Width(), rect.Height())) + return false; + +- if (bitmap->HasAlpha()) { +- bitmap->Clear(0); ++ if (bitmap->IsAlphaFormat()) { + backdrop->Copy(bitmap); + } else { + if (!m_pDeviceDriver->GetDIBits(bitmap, rect.left, rect.top)) +@@ -649,20 +774,18 @@ bool CFX_RenderDevice::DrawFillStrokePath(const CFX_PathData* pPathData, + backdrop->Copy(bitmap); + } + CFX_DefaultRenderDevice bitmap_device; +- bitmap_device.Attach(bitmap, false, backdrop, true); ++ bitmap_device.AttachWithBackdropAndGroupKnockout(bitmap, std::move(backdrop), ++ /*bGroupKnockout=*/true); + + CFX_Matrix matrix; + if (pObject2Device) + matrix = *pObject2Device; + matrix.Translate(-rect.left, -rect.top); +- if (!bitmap_device.GetDeviceDriver()->DrawPath( +- pPathData, &matrix, pGraphState, fill_color, stroke_color, fill_mode, +- blend_type)) { ++ if (!bitmap_device.GetDeviceDriver()->DrawPath(path, &matrix, pGraphState, ++ fill_color, stroke_color, ++ fill_options, blend_type)) { + return false; + } +-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ +- bitmap_device.GetDeviceDriver()->Flush(); +-#endif + FX_RECT src_rect(0, 0, rect.Width(), rect.Height()); + return m_pDeviceDriver->SetDIBits(bitmap, 0, src_rect, rect.left, rect.top, + BlendMode::kNormal); +@@ -684,31 +807,68 @@ bool CFX_RenderDevice::FillRectWithBlend(const FX_RECT& rect, + if (!m_pDeviceDriver->GetDIBits(bitmap, rect.left, rect.top)) + return false; + +- if (!bitmap->CompositeRect(0, 0, rect.Width(), rect.Height(), fill_color, +- 0)) { ++ if (!bitmap->CompositeRect(0, 0, rect.Width(), rect.Height(), fill_color)) + return false; +- } ++ + FX_RECT src_rect(0, 0, rect.Width(), rect.Height()); + m_pDeviceDriver->SetDIBits(bitmap, 0, src_rect, rect.left, rect.top, + BlendMode::kNormal); + return true; + } + +-bool CFX_RenderDevice::DrawCosmeticLine(const CFX_PointF& ptMoveTo, +- const CFX_PointF& ptLineTo, +- uint32_t color, +- int fill_mode, +- BlendMode blend_type) { ++bool CFX_RenderDevice::DrawCosmeticLine( ++ const CFX_PointF& ptMoveTo, ++ const CFX_PointF& ptLineTo, ++ uint32_t color, ++ const CFX_FillRenderOptions& fill_options, ++ BlendMode blend_type) { + if ((color >= 0xff000000) && m_pDeviceDriver->DrawCosmeticLine( + ptMoveTo, ptLineTo, color, blend_type)) { + return true; + } + CFX_GraphStateData graph_state; +- CFX_PathData path; +- path.AppendPoint(ptMoveTo, FXPT_TYPE::MoveTo, false); +- path.AppendPoint(ptLineTo, FXPT_TYPE::LineTo, false); +- return m_pDeviceDriver->DrawPath(&path, nullptr, &graph_state, 0, color, +- fill_mode, blend_type); ++ CFX_Path path; ++ path.AppendPoint(ptMoveTo, CFX_Path::Point::Type::kMove); ++ path.AppendPoint(ptLineTo, CFX_Path::Point::Type::kLine); ++ return m_pDeviceDriver->DrawPath(path, nullptr, &graph_state, 0, color, ++ fill_options, blend_type); ++} ++ ++void CFX_RenderDevice::DrawZeroAreaPath( ++ const std::vector& path, ++ const CFX_Matrix* matrix, ++ bool adjust, ++ bool aliased_path, ++ uint32_t fill_color, ++ uint8_t fill_alpha, ++ BlendMode blend_type) { ++ if (path.empty()) ++ return; ++ ++ CFX_Path new_path; ++ bool thin = false; ++ bool set_identity = false; ++ ++ if (!GetZeroAreaPath(path, matrix, adjust, &new_path, &thin, &set_identity)) ++ return; ++ ++ CFX_GraphStateData graph_state; ++ graph_state.m_LineWidth = 0.0f; ++ ++ uint32_t stroke_color = fill_color; ++ if (thin) ++ stroke_color = (((fill_alpha >> 2) << 24) | (stroke_color & 0x00ffffff)); ++ ++ const CFX_Matrix* new_matrix = nullptr; ++ if (matrix && !matrix->IsIdentity() && !set_identity) ++ new_matrix = matrix; ++ ++ CFX_FillRenderOptions path_options; ++ path_options.zero_area = true; ++ path_options.aliased_path = aliased_path; ++ ++ m_pDeviceDriver->DrawPath(new_path, new_matrix, &graph_state, 0, stroke_color, ++ path_options, blend_type); + } + + bool CFX_RenderDevice::GetDIBits(const RetainPtr& pBitmap, +@@ -726,7 +886,7 @@ bool CFX_RenderDevice::SetDIBitsWithBlend(const RetainPtr& pBitmap, + int left, + int top, + BlendMode blend_mode) { +- ASSERT(!pBitmap->IsAlphaMask()); ++ DCHECK(!pBitmap->IsMaskFormat()); + FX_RECT dest_rect(left, top, left + pBitmap->GetWidth(), + top + pBitmap->GetHeight()); + dest_rect.Intersect(m_ClipBox); +@@ -737,7 +897,7 @@ bool CFX_RenderDevice::SetDIBitsWithBlend(const RetainPtr& pBitmap, + dest_rect.left - left + dest_rect.Width(), + dest_rect.top - top + dest_rect.Height()); + if ((blend_mode == BlendMode::kNormal || (m_RenderCaps & FXRC_BLEND_MODE)) && +- (!pBitmap->HasAlpha() || (m_RenderCaps & FXRC_ALPHA_IMAGE))) { ++ (!pBitmap->IsAlphaFormat() || (m_RenderCaps & FXRC_ALPHA_IMAGE))) { + return m_pDeviceDriver->SetDIBits(pBitmap, 0, src_rect, dest_rect.left, + dest_rect.top, blend_mode); + } +@@ -747,14 +907,13 @@ bool CFX_RenderDevice::SetDIBitsWithBlend(const RetainPtr& pBitmap, + int bg_pixel_width = dest_rect.Width(); + int bg_pixel_height = dest_rect.Height(); + auto background = pdfium::MakeRetain(); +- if (!background->Create( +- bg_pixel_width, bg_pixel_height, +- (m_RenderCaps & FXRC_CMYK_OUTPUT) ? FXDIB_Cmyk : FXDIB_Rgb32)) { ++ if (!background->Create(bg_pixel_width, bg_pixel_height, ++ FXDIB_Format::kRgb32)) { + return false; + } +- if (!m_pDeviceDriver->GetDIBits(background, dest_rect.left, dest_rect.top)) { ++ if (!m_pDeviceDriver->GetDIBits(background, dest_rect.left, dest_rect.top)) + return false; +- } ++ + if (!background->CompositeBitmap(0, 0, bg_pixel_width, bg_pixel_height, + pBitmap, src_rect.left, src_rect.top, + blend_mode, nullptr, false)) { +@@ -833,11 +992,7 @@ bool CFX_RenderDevice::ContinueDIBits(CFX_ImageRenderer* handle, + return m_pDeviceDriver->ContinueDIBits(handle, pPause); + } + +-#ifdef _SKIA_SUPPORT_ +-void CFX_RenderDevice::DebugVerifyBitmapIsPreMultiplied() const { +- NOTREACHED(); +-} +- ++#if defined(_SKIA_SUPPORT_) + bool CFX_RenderDevice::SetBitsWithMask(const RetainPtr& pBitmap, + const RetainPtr& pMask, + int left, +@@ -849,97 +1004,99 @@ bool CFX_RenderDevice::SetBitsWithMask(const RetainPtr& pBitmap, + } + #endif + +-bool CFX_RenderDevice::DrawNormalText(int nChars, +- const TextCharPos* pCharPos, ++bool CFX_RenderDevice::DrawNormalText(pdfium::span pCharPos, + CFX_Font* pFont, + float font_size, + const CFX_Matrix& mtText2Device, + uint32_t fill_color, +- uint32_t text_flags) { +- int nativetext_flags = text_flags; +- if (m_DeviceType != DeviceType::kDisplay) { +- if (!(text_flags & FXTEXT_PRINTGRAPHICTEXT)) { +- if (ShouldDrawDeviceText(pFont, text_flags) && +- m_pDeviceDriver->DrawDeviceText( +- nChars, pCharPos, pFont, mtText2Device, font_size, fill_color)) { +- return true; +- } +- } +- if (FXARGB_A(fill_color) < 255) +- return false; +- } else if (!(text_flags & FXTEXT_NO_NATIVETEXT)) { +- if (ShouldDrawDeviceText(pFont, text_flags) && +- m_pDeviceDriver->DrawDeviceText(nChars, pCharPos, pFont, mtText2Device, +- font_size, fill_color)) { +- return true; +- } +- } +- CFX_Matrix char2device = mtText2Device; +- CFX_Matrix text2Device = mtText2Device; +- char2device.Scale(font_size, -font_size); +- if (fabs(char2device.a) + fabs(char2device.b) > 50 * 1.0f || +- (m_DeviceType == DeviceType::kPrinter && +- !(text_flags & FXTEXT_PRINTIMAGETEXT))) { +- if (pFont->GetFaceRec()) { +- int nPathFlags = +- (text_flags & FXTEXT_NOSMOOTH) == 0 ? 0 : FXFILL_NOPATHSMOOTH; +- return DrawTextPath(nChars, pCharPos, pFont, font_size, mtText2Device, +- nullptr, nullptr, fill_color, 0, nullptr, nPathFlags); +- } +- } ++ const CFX_TextRenderOptions& options) { ++ // `anti_alias` and `normalize` don't affect Skia rendering. + int anti_alias = FT_RENDER_MODE_MONO; +- bool bNormal = false; +- if ((text_flags & FXTEXT_NOSMOOTH) == 0) { +- if (m_DeviceType == DeviceType::kDisplay && m_bpp > 1) { ++ bool normalize = false; ++ const bool is_text_smooth = options.IsSmooth(); ++ // |text_options| has the potential to affect all derived classes of ++ // RenderDeviceDriverIface. But now it only affects Skia rendering. ++ CFX_TextRenderOptions text_options(options); ++ if (is_text_smooth) { ++ if (GetDeviceType() == DeviceType::kDisplay && m_bpp > 1) { + if (!CFX_GEModule::Get()->GetFontMgr()->FTLibrarySupportsHinting()) { + // Some Freetype implementations (like the one packaged with Fedora) do + // not support hinting due to patents 6219025, 6239783, 6307566, + // 6225973, 6243070, 6393145, 6421054, 6282327, and 6624828; the latest +- // one expires 10/7/19. This makes LCD antialiasing very ugly, so we +- // instead fall back on NORMAL antialiasing. ++ // one expires 10/7/19. This makes LCD anti-aliasing very ugly, so we ++ // instead fall back on NORMAL anti-aliasing. + anti_alias = FT_RENDER_MODE_NORMAL; +- } else if ((m_RenderCaps & (FXRC_ALPHA_OUTPUT | FXRC_CMYK_OUTPUT))) { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ // Since |anti_alias| doesn't affect Skia rendering, and Skia only ++ // follows strictly to the options provided by |text_options|, we need ++ // to update |text_options| so that Skia falls back on normal ++ // anti-aliasing as well. ++ text_options.aliasing_type = CFX_TextRenderOptions::kAntiAliasing; ++ } ++ } else if ((m_RenderCaps & FXRC_ALPHA_OUTPUT)) { ++ // Whether Skia uses LCD optimization should strictly follow the ++ // rendering options provided by |text_options|. No change needs to be ++ // done for |text_options| here. + anti_alias = FT_RENDER_MODE_LCD; +- bNormal = true; ++ normalize = true; + } else if (m_bpp < 16) { ++ // This case doesn't apply to Skia since Skia always have |m_bpp| = 32. + anti_alias = FT_RENDER_MODE_NORMAL; + } else { ++ // Whether Skia uses LCD optimization should strictly follow the ++ // rendering options provided by |text_options|. No change needs to be ++ // done for |text_options| here. + anti_alias = FT_RENDER_MODE_LCD; +- +- bool bClearType = false; +- if (pFont->GetFaceRec()) +- bClearType = !!(text_flags & FXTEXT_CLEARTYPE); +- bNormal = !bClearType; ++ normalize = !pFont->GetFaceRec() || ++ options.aliasing_type != CFX_TextRenderOptions::kLcd; + } + } + } +- std::vector glyphs(nChars); +- CFX_Matrix deviceCtm = char2device; + ++ if (GetDeviceType() != DeviceType::kDisplay) { ++ if (ShouldDrawDeviceText(pFont, options) && ++ m_pDeviceDriver->DrawDeviceText(pCharPos, pFont, mtText2Device, ++ font_size, fill_color, text_options)) { ++ return true; ++ } ++ if (FXARGB_A(fill_color) < 255) ++ return false; ++ } else if (options.native_text) { ++ if (ShouldDrawDeviceText(pFont, options) && ++ m_pDeviceDriver->DrawDeviceText(pCharPos, pFont, mtText2Device, ++ font_size, fill_color, text_options)) { ++ return true; ++ } ++ } ++ ++ CFX_Matrix char2device = mtText2Device; ++ CFX_Matrix text2Device = mtText2Device; ++ char2device.Scale(font_size, -font_size); ++ if (fabs(char2device.a) + fabs(char2device.b) > 50 * 1.0f || ++ GetDeviceType() == DeviceType::kPrinter) { ++ if (pFont->GetFaceRec()) { ++ CFX_FillRenderOptions path_options; ++ path_options.aliased_path = !is_text_smooth; ++ return DrawTextPath(pCharPos, pFont, font_size, mtText2Device, nullptr, ++ nullptr, fill_color, 0, nullptr, path_options); ++ } ++ } ++ std::vector glyphs(pCharPos.size()); + for (size_t i = 0; i < glyphs.size(); ++i) { + TextGlyphPos& glyph = glyphs[i]; + const TextCharPos& charpos = pCharPos[i]; + +- glyph.m_fOrigin = text2Device.Transform(charpos.m_Origin); ++ glyph.m_fDeviceOrigin = text2Device.Transform(charpos.m_Origin); + if (anti_alias < FT_RENDER_MODE_LCD) +- glyph.m_Origin.x = FXSYS_roundf(glyph.m_fOrigin.x); ++ glyph.m_Origin.x = FXSYS_roundf(glyph.m_fDeviceOrigin.x); + else +- glyph.m_Origin.x = static_cast(floor(glyph.m_fOrigin.x)); +- glyph.m_Origin.y = FXSYS_roundf(glyph.m_fOrigin.y); +- +- if (charpos.m_bGlyphAdjust) { +- CFX_Matrix new_matrix( +- charpos.m_AdjustMatrix[0], charpos.m_AdjustMatrix[1], +- charpos.m_AdjustMatrix[2], charpos.m_AdjustMatrix[3], 0, 0); +- new_matrix.Concat(deviceCtm); +- glyph.m_pGlyph = pFont->LoadGlyphBitmap( +- charpos.m_GlyphIndex, charpos.m_bFontStyle, new_matrix, +- charpos.m_FontCharWidth, anti_alias, &nativetext_flags); +- } else { +- glyph.m_pGlyph = pFont->LoadGlyphBitmap( +- charpos.m_GlyphIndex, charpos.m_bFontStyle, deviceCtm, +- charpos.m_FontCharWidth, anti_alias, &nativetext_flags); +- } ++ glyph.m_Origin.x = static_cast(floor(glyph.m_fDeviceOrigin.x)); ++ glyph.m_Origin.y = FXSYS_roundf(glyph.m_fDeviceOrigin.y); ++ ++ CFX_Matrix matrix = charpos.GetEffectiveMatrix(char2device); ++ glyph.m_pGlyph = pFont->LoadGlyphBitmap( ++ charpos.m_GlyphIndex, charpos.m_bFontStyle, matrix, ++ charpos.m_FontCharWidth, anti_alias, &text_options); + } + if (anti_alias < FT_RENDER_MODE_LCD && glyphs.size() > 1) + AdjustGlyphSpace(&glyphs); +@@ -955,40 +1112,36 @@ bool CFX_RenderDevice::DrawNormalText(int nChars, + int pixel_top = bmp_rect.top; + if (anti_alias == FT_RENDER_MODE_MONO) { + auto bitmap = pdfium::MakeRetain(); +- if (!bitmap->Create(pixel_width, pixel_height, FXDIB_1bppMask)) ++ if (!bitmap->Create(pixel_width, pixel_height, FXDIB_Format::k1bppMask)) + return false; +- bitmap->Clear(0); + for (const TextGlyphPos& glyph : glyphs) { + if (!glyph.m_pGlyph) + continue; + +- Optional point = glyph.GetOrigin({pixel_left, pixel_top}); ++ absl::optional point = ++ glyph.GetOrigin({pixel_left, pixel_top}); + if (!point.has_value()) + continue; + + const RetainPtr& pGlyph = glyph.m_pGlyph->GetBitmap(); +- bitmap->TransferBitmap(point.value().x, point.value().y, +- pGlyph->GetWidth(), pGlyph->GetHeight(), pGlyph, 0, +- 0); ++ bitmap->CompositeOneBPPMask(point.value().x, point.value().y, ++ pGlyph->GetWidth(), pGlyph->GetHeight(), ++ pGlyph, 0, 0); + } + return SetBitMask(bitmap, bmp_rect.left, bmp_rect.top, fill_color); + } + auto bitmap = pdfium::MakeRetain(); + if (m_bpp == 8) { +- if (!bitmap->Create(pixel_width, pixel_height, FXDIB_8bppMask)) ++ if (!bitmap->Create(pixel_width, pixel_height, FXDIB_Format::k8bppMask)) + return false; + } else { + if (!CreateCompatibleBitmap(bitmap, pixel_width, pixel_height)) + return false; + } +- if (!bitmap->HasAlpha() && !bitmap->IsAlphaMask()) { ++ if (!bitmap->IsAlphaFormat() && !bitmap->IsMaskFormat()) { + bitmap->Clear(0xFFFFFFFF); + if (!GetDIBits(bitmap, bmp_rect.left, bmp_rect.top)) + return false; +- } else { +- bitmap->Clear(0); +- if (bitmap->m_pAlphaMask) +- bitmap->m_pAlphaMask->Clear(0); + } + int dest_width = pixel_width; + int a = 0; +@@ -1002,7 +1155,7 @@ bool CFX_RenderDevice::DrawNormalText(int nChars, + if (!glyph.m_pGlyph) + continue; + +- Optional point = glyph.GetOrigin({pixel_left, pixel_top}); ++ absl::optional point = glyph.GetOrigin({pixel_left, pixel_top}); + if (!point.has_value()) + continue; + +@@ -1017,9 +1170,8 @@ bool CFX_RenderDevice::DrawNormalText(int nChars, + } + continue; + } +- bool bBGRStripe = !!(text_flags & FXTEXT_BGR_STRIPE); + ncols /= 3; +- int x_subpixel = static_cast(glyph.m_fOrigin.x * 3) % 3; ++ int x_subpixel = static_cast(glyph.m_fDeviceOrigin.x * 3) % 3; + int start_col = std::max(point->x, 0); + FX_SAFE_INT32 end_col_safe = point->x; + end_col_safe += ncols; +@@ -1031,17 +1183,25 @@ bool CFX_RenderDevice::DrawNormalText(int nChars, + continue; + + DrawNormalTextHelper(bitmap, pGlyph, nrows, point->x, point->y, start_col, +- end_col, bNormal, bBGRStripe, x_subpixel, a, r, g, b); ++ end_col, normalize, x_subpixel, a, r, g, b); + } +- if (bitmap->IsAlphaMask()) ++ ++#if defined(_SKIA_SUPPORT_) ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ // DrawNormalTextHelper() can result in unpremultiplied bitmaps for ++ // rendering glyphs. Make sure `bitmap` is premultiplied before proceeding. ++ bitmap->PreMultiply(); ++ } ++#endif ++ ++ if (bitmap->IsMaskFormat()) + SetBitMask(bitmap, bmp_rect.left, bmp_rect.top, fill_color); + else + SetDIBits(bitmap, bmp_rect.left, bmp_rect.top); + return true; + } + +-bool CFX_RenderDevice::DrawTextPath(int nChars, +- const TextCharPos* pCharPos, ++bool CFX_RenderDevice::DrawTextPath(pdfium::span pCharPos, + CFX_Font* pFont, + float font_size, + const CFX_Matrix& mtText2User, +@@ -1049,40 +1209,34 @@ bool CFX_RenderDevice::DrawTextPath(int nChars, + const CFX_GraphStateData* pGraphState, + uint32_t fill_color, + FX_ARGB stroke_color, +- CFX_PathData* pClippingPath, +- int nFlag) { +- for (int iChar = 0; iChar < nChars; ++iChar) { +- const TextCharPos& charpos = pCharPos[iChar]; +- CFX_Matrix matrix; +- if (charpos.m_bGlyphAdjust) { +- matrix = CFX_Matrix(charpos.m_AdjustMatrix[0], charpos.m_AdjustMatrix[1], +- charpos.m_AdjustMatrix[2], charpos.m_AdjustMatrix[3], +- 0, 0); +- } +- matrix.Concat(CFX_Matrix(font_size, 0, 0, font_size, charpos.m_Origin.x, +- charpos.m_Origin.y)); +- const CFX_PathData* pPath = ++ CFX_Path* pClippingPath, ++ const CFX_FillRenderOptions& fill_options) { ++ for (const auto& charpos : pCharPos) { ++ const CFX_Path* pPath = + pFont->LoadGlyphPath(charpos.m_GlyphIndex, charpos.m_FontCharWidth); + if (!pPath) + continue; + ++ CFX_Matrix matrix(font_size, 0, 0, font_size, charpos.m_Origin.x, ++ charpos.m_Origin.y); ++ matrix = charpos.GetEffectiveMatrix(matrix); + matrix.Concat(mtText2User); + +- CFX_PathData TransformedPath(*pPath); +- TransformedPath.Transform(matrix); ++ CFX_Path transformed_path(*pPath); ++ transformed_path.Transform(matrix); + if (fill_color || stroke_color) { +- int fill_mode = nFlag; ++ CFX_FillRenderOptions options(fill_options); + if (fill_color) +- fill_mode |= FXFILL_WINDING; +- fill_mode |= FX_FILL_TEXT_MODE; +- if (!DrawPathWithBlend(&TransformedPath, pUser2Device, pGraphState, +- fill_color, stroke_color, fill_mode, ++ options.fill_type = CFX_FillRenderOptions::FillType::kWinding; ++ options.text_mode = true; ++ if (!DrawPathWithBlend(transformed_path, pUser2Device, pGraphState, ++ fill_color, stroke_color, options, + BlendMode::kNormal)) { + return false; + } + } + if (pClippingPath) +- pClippingPath->Append(&TransformedPath, pUser2Device); ++ pClippingPath->Append(transformed_path, pUser2Device); + } + return true; + } +@@ -1090,21 +1244,23 @@ bool CFX_RenderDevice::DrawTextPath(int nChars, + void CFX_RenderDevice::DrawFillRect(const CFX_Matrix* pUser2Device, + const CFX_FloatRect& rect, + const FX_COLORREF& color) { +- CFX_PathData path; ++ CFX_Path path; + path.AppendFloatRect(rect); +- DrawPath(&path, pUser2Device, nullptr, color, 0, FXFILL_WINDING); ++ DrawPath(path, pUser2Device, nullptr, color, 0, ++ CFX_FillRenderOptions::WindingOptions()); + } + + void CFX_RenderDevice::DrawFillArea(const CFX_Matrix& mtUser2Device, + const std::vector& points, + const FX_COLORREF& color) { +- ASSERT(!points.empty()); +- CFX_PathData path; +- path.AppendPoint(points[0], FXPT_TYPE::MoveTo, false); ++ DCHECK(!points.empty()); ++ CFX_Path path; ++ path.AppendPoint(points[0], CFX_Path::Point::Type::kMove); + for (size_t i = 1; i < points.size(); ++i) +- path.AppendPoint(points[i], FXPT_TYPE::LineTo, false); ++ path.AppendPoint(points[i], CFX_Path::Point::Type::kLine); + +- DrawPath(&path, &mtUser2Device, nullptr, color, 0, FXFILL_ALTERNATE); ++ DrawPath(path, &mtUser2Device, nullptr, color, 0, ++ CFX_FillRenderOptions::EvenOddOptions()); + } + + void CFX_RenderDevice::DrawStrokeRect(const CFX_Matrix& mtUser2Device, +@@ -1114,9 +1270,10 @@ void CFX_RenderDevice::DrawStrokeRect(const CFX_Matrix& mtUser2Device, + CFX_GraphStateData gsd; + gsd.m_LineWidth = fWidth; + +- CFX_PathData path; ++ CFX_Path path; + path.AppendFloatRect(rect); +- DrawPath(&path, &mtUser2Device, &gsd, 0, color, FXFILL_ALTERNATE); ++ DrawPath(path, &mtUser2Device, &gsd, 0, color, ++ CFX_FillRenderOptions::EvenOddOptions()); + } + + void CFX_RenderDevice::DrawStrokeLine(const CFX_Matrix* pUser2Device, +@@ -1124,14 +1281,15 @@ void CFX_RenderDevice::DrawStrokeLine(const CFX_Matrix* pUser2Device, + const CFX_PointF& ptLineTo, + const FX_COLORREF& color, + float fWidth) { +- CFX_PathData path; +- path.AppendPoint(ptMoveTo, FXPT_TYPE::MoveTo, false); +- path.AppendPoint(ptLineTo, FXPT_TYPE::LineTo, false); ++ CFX_Path path; ++ path.AppendPoint(ptMoveTo, CFX_Path::Point::Type::kMove); ++ path.AppendPoint(ptLineTo, CFX_Path::Point::Type::kLine); + + CFX_GraphStateData gsd; + gsd.m_LineWidth = fWidth; + +- DrawPath(&path, pUser2Device, &gsd, 0, color, FXFILL_ALTERNATE); ++ DrawPath(path, pUser2Device, &gsd, 0, color, ++ CFX_FillRenderOptions::EvenOddOptions()); + } + + void CFX_RenderDevice::DrawFillRect(const CFX_Matrix* pUser2Device, +@@ -1142,35 +1300,35 @@ void CFX_RenderDevice::DrawFillRect(const CFX_Matrix* pUser2Device, + } + + void CFX_RenderDevice::DrawShadow(const CFX_Matrix& mtUser2Device, +- bool bVertical, +- bool bHorizontal, + const CFX_FloatRect& rect, + int32_t nTransparency, + int32_t nStartGray, + int32_t nEndGray) { +- float fStepGray = 1.0f; +- +- if (bVertical) { +- fStepGray = (nEndGray - nStartGray) / rect.Height(); +- +- for (float fy = rect.bottom + 0.5f; fy <= rect.top - 0.5f; fy += 1.0f) { +- int32_t nGray = nStartGray + (int32_t)(fStepGray * (fy - rect.bottom)); +- DrawStrokeLine(&mtUser2Device, CFX_PointF(rect.left, fy), +- CFX_PointF(rect.right, fy), +- ArgbEncode(nTransparency, nGray, nGray, nGray), 1.5f); +- } ++ constexpr float kBorder = 0.5f; ++ constexpr float kSegmentWidth = 1.0f; ++ constexpr float kLineWidth = 1.5f; ++ ++ float fStepGray = (nEndGray - nStartGray) / rect.Height(); ++ CFX_PointF start(rect.left, 0); ++ CFX_PointF end(rect.right, 0); ++ ++ for (float fy = rect.bottom + kBorder; fy <= rect.top - kBorder; ++ fy += kSegmentWidth) { ++ start.y = fy; ++ end.y = fy; ++ int nGray = nStartGray + static_cast(fStepGray * (fy - rect.bottom)); ++ FX_ARGB color = ArgbEncode(nTransparency, nGray, nGray, nGray); ++ DrawStrokeLine(&mtUser2Device, start, end, color, kLineWidth); + } ++} + +- if (bHorizontal) { +- fStepGray = (nEndGray - nStartGray) / rect.Width(); +- +- for (float fx = rect.left + 0.5f; fx <= rect.right - 0.5f; fx += 1.0f) { +- int32_t nGray = nStartGray + (int32_t)(fStepGray * (fx - rect.left)); +- DrawStrokeLine(&mtUser2Device, CFX_PointF(fx, rect.bottom), +- CFX_PointF(fx, rect.top), +- ArgbEncode(nTransparency, nGray, nGray, nGray), 1.5f); +- } +- } ++bool CFX_RenderDevice::DrawShading(const CPDF_ShadingPattern* pPattern, ++ const CFX_Matrix* pMatrix, ++ const FX_RECT& clip_rect, ++ int alpha, ++ bool bAlphaMode) { ++ return m_pDeviceDriver->DrawShading(pPattern, pMatrix, clip_rect, alpha, ++ bAlphaMode); + } + + void CFX_RenderDevice::DrawBorder(const CFX_Matrix* pUser2Device, +@@ -1181,130 +1339,132 @@ void CFX_RenderDevice::DrawBorder(const CFX_Matrix* pUser2Device, + const CFX_Color& crRightBottom, + BorderStyle nStyle, + int32_t nTransparency) { +- float fLeft = rect.left; +- float fRight = rect.right; +- float fTop = rect.top; +- float fBottom = rect.bottom; +- +- if (fWidth > 0.0f) { +- float fHalfWidth = fWidth / 2.0f; +- +- switch (nStyle) { +- default: +- case BorderStyle::SOLID: { +- CFX_PathData path; +- path.AppendRect(fLeft, fBottom, fRight, fTop); +- path.AppendRect(fLeft + fWidth, fBottom + fWidth, fRight - fWidth, +- fTop - fWidth); +- DrawPath(&path, pUser2Device, nullptr, color.ToFXColor(nTransparency), +- 0, FXFILL_ALTERNATE); +- break; +- } +- case BorderStyle::DASH: { +- CFX_PathData path; +- path.AppendPoint( +- CFX_PointF(fLeft + fWidth / 2.0f, fBottom + fWidth / 2.0f), +- FXPT_TYPE::MoveTo, false); +- path.AppendPoint( +- CFX_PointF(fLeft + fWidth / 2.0f, fTop - fWidth / 2.0f), +- FXPT_TYPE::LineTo, false); +- path.AppendPoint( +- CFX_PointF(fRight - fWidth / 2.0f, fTop - fWidth / 2.0f), +- FXPT_TYPE::LineTo, false); +- path.AppendPoint( +- CFX_PointF(fRight - fWidth / 2.0f, fBottom + fWidth / 2.0f), +- FXPT_TYPE::LineTo, false); +- path.AppendPoint( +- CFX_PointF(fLeft + fWidth / 2.0f, fBottom + fWidth / 2.0f), +- FXPT_TYPE::LineTo, false); +- +- CFX_GraphStateData gsd; +- gsd.m_DashArray = {3.0f, 3.0f}; +- gsd.m_DashPhase = 0; +- gsd.m_LineWidth = fWidth; +- DrawPath(&path, pUser2Device, &gsd, 0, color.ToFXColor(nTransparency), +- FXFILL_WINDING); +- break; +- } +- case BorderStyle::BEVELED: +- case BorderStyle::INSET: { +- CFX_GraphStateData gsd; +- gsd.m_LineWidth = fHalfWidth; +- +- CFX_PathData pathLT; +- +- pathLT.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth), +- FXPT_TYPE::MoveTo, false); +- pathLT.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fTop - fHalfWidth), +- FXPT_TYPE::LineTo, false); +- pathLT.AppendPoint(CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth), +- FXPT_TYPE::LineTo, false); +- pathLT.AppendPoint( +- CFX_PointF(fRight - fHalfWidth * 2, fTop - fHalfWidth * 2), +- FXPT_TYPE::LineTo, false); +- pathLT.AppendPoint( +- CFX_PointF(fLeft + fHalfWidth * 2, fTop - fHalfWidth * 2), +- FXPT_TYPE::LineTo, false); +- pathLT.AppendPoint( +- CFX_PointF(fLeft + fHalfWidth * 2, fBottom + fHalfWidth * 2), +- FXPT_TYPE::LineTo, false); +- pathLT.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth), +- FXPT_TYPE::LineTo, false); +- +- DrawPath(&pathLT, pUser2Device, &gsd, +- crLeftTop.ToFXColor(nTransparency), 0, FXFILL_ALTERNATE); +- +- CFX_PathData pathRB; +- pathRB.AppendPoint(CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth), +- FXPT_TYPE::MoveTo, false); +- pathRB.AppendPoint( +- CFX_PointF(fRight - fHalfWidth, fBottom + fHalfWidth), +- FXPT_TYPE::LineTo, false); +- pathRB.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth), +- FXPT_TYPE::LineTo, false); +- pathRB.AppendPoint( +- CFX_PointF(fLeft + fHalfWidth * 2, fBottom + fHalfWidth * 2), +- FXPT_TYPE::LineTo, false); +- pathRB.AppendPoint( +- CFX_PointF(fRight - fHalfWidth * 2, fBottom + fHalfWidth * 2), +- FXPT_TYPE::LineTo, false); +- pathRB.AppendPoint( +- CFX_PointF(fRight - fHalfWidth * 2, fTop - fHalfWidth * 2), +- FXPT_TYPE::LineTo, false); +- pathRB.AppendPoint(CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth), +- FXPT_TYPE::LineTo, false); +- +- DrawPath(&pathRB, pUser2Device, &gsd, +- crRightBottom.ToFXColor(nTransparency), 0, FXFILL_ALTERNATE); +- +- CFX_PathData path; +- +- path.AppendRect(fLeft, fBottom, fRight, fTop); +- path.AppendRect(fLeft + fHalfWidth, fBottom + fHalfWidth, +- fRight - fHalfWidth, fTop - fHalfWidth); +- +- DrawPath(&path, pUser2Device, &gsd, color.ToFXColor(nTransparency), 0, +- FXFILL_ALTERNATE); +- break; +- } +- case BorderStyle::UNDERLINE: { +- CFX_PathData path; +- path.AppendPoint(CFX_PointF(fLeft, fBottom + fWidth / 2), +- FXPT_TYPE::MoveTo, false); +- path.AppendPoint(CFX_PointF(fRight, fBottom + fWidth / 2), +- FXPT_TYPE::LineTo, false); +- +- CFX_GraphStateData gsd; +- gsd.m_LineWidth = fWidth; +- +- DrawPath(&path, pUser2Device, &gsd, 0, color.ToFXColor(nTransparency), +- FXFILL_ALTERNATE); +- break; +- } ++ if (fWidth <= 0.0f) ++ return; ++ ++ const float fLeft = rect.left; ++ const float fRight = rect.right; ++ const float fTop = rect.top; ++ const float fBottom = rect.bottom; ++ const float fHalfWidth = fWidth / 2.0f; ++ ++ switch (nStyle) { ++ default: ++ case BorderStyle::kSolid: { ++ CFX_Path path; ++ path.AppendRect(fLeft, fBottom, fRight, fTop); ++ path.AppendRect(fLeft + fWidth, fBottom + fWidth, fRight - fWidth, ++ fTop - fWidth); ++ DrawPath(path, pUser2Device, nullptr, color.ToFXColor(nTransparency), 0, ++ CFX_FillRenderOptions::EvenOddOptions()); ++ break; ++ } ++ case BorderStyle::kDash: { ++ CFX_GraphStateData gsd; ++ gsd.m_DashArray = {3.0f, 3.0f}; ++ gsd.m_DashPhase = 0; ++ gsd.m_LineWidth = fWidth; ++ ++ CFX_Path path; ++ path.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth), ++ CFX_Path::Point::Type::kMove); ++ path.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fTop - fHalfWidth), ++ CFX_Path::Point::Type::kLine); ++ path.AppendPoint(CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth), ++ CFX_Path::Point::Type::kLine); ++ path.AppendPoint(CFX_PointF(fRight - fHalfWidth, fBottom + fHalfWidth), ++ CFX_Path::Point::Type::kLine); ++ path.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth), ++ CFX_Path::Point::Type::kLine); ++ DrawPath(path, pUser2Device, &gsd, 0, color.ToFXColor(nTransparency), ++ CFX_FillRenderOptions::WindingOptions()); ++ break; ++ } ++ case BorderStyle::kBeveled: ++ case BorderStyle::kInset: { ++ CFX_GraphStateData gsd; ++ gsd.m_LineWidth = fHalfWidth; ++ ++ CFX_Path path_left_top; ++ path_left_top.AppendPoint( ++ CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth), ++ CFX_Path::Point::Type::kMove); ++ path_left_top.AppendPoint( ++ CFX_PointF(fLeft + fHalfWidth, fTop - fHalfWidth), ++ CFX_Path::Point::Type::kLine); ++ path_left_top.AppendPoint( ++ CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth), ++ CFX_Path::Point::Type::kLine); ++ path_left_top.AppendPoint(CFX_PointF(fRight - fWidth, fTop - fWidth), ++ CFX_Path::Point::Type::kLine); ++ path_left_top.AppendPoint(CFX_PointF(fLeft + fWidth, fTop - fWidth), ++ CFX_Path::Point::Type::kLine); ++ path_left_top.AppendPoint(CFX_PointF(fLeft + fWidth, fBottom + fWidth), ++ CFX_Path::Point::Type::kLine); ++ path_left_top.AppendPoint( ++ CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth), ++ CFX_Path::Point::Type::kLine); ++ DrawPath(path_left_top, pUser2Device, &gsd, ++ crLeftTop.ToFXColor(nTransparency), 0, ++ CFX_FillRenderOptions::EvenOddOptions()); ++ ++ CFX_Path path_right_bottom; ++ path_right_bottom.AppendPoint( ++ CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth), ++ CFX_Path::Point::Type::kMove); ++ path_right_bottom.AppendPoint( ++ CFX_PointF(fRight - fHalfWidth, fBottom + fHalfWidth), ++ CFX_Path::Point::Type::kLine); ++ path_right_bottom.AppendPoint( ++ CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth), ++ CFX_Path::Point::Type::kLine); ++ path_right_bottom.AppendPoint( ++ CFX_PointF(fLeft + fWidth, fBottom + fWidth), ++ CFX_Path::Point::Type::kLine); ++ path_right_bottom.AppendPoint( ++ CFX_PointF(fRight - fWidth, fBottom + fWidth), ++ CFX_Path::Point::Type::kLine); ++ path_right_bottom.AppendPoint(CFX_PointF(fRight - fWidth, fTop - fWidth), ++ CFX_Path::Point::Type::kLine); ++ path_right_bottom.AppendPoint( ++ CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth), ++ CFX_Path::Point::Type::kLine); ++ DrawPath(path_right_bottom, pUser2Device, &gsd, ++ crRightBottom.ToFXColor(nTransparency), 0, ++ CFX_FillRenderOptions::EvenOddOptions()); ++ ++ CFX_Path path; ++ path.AppendRect(fLeft, fBottom, fRight, fTop); ++ path.AppendRect(fLeft + fHalfWidth, fBottom + fHalfWidth, ++ fRight - fHalfWidth, fTop - fHalfWidth); ++ DrawPath(path, pUser2Device, &gsd, color.ToFXColor(nTransparency), 0, ++ CFX_FillRenderOptions::EvenOddOptions()); ++ break; ++ } ++ case BorderStyle::kUnderline: { ++ CFX_GraphStateData gsd; ++ gsd.m_LineWidth = fWidth; ++ ++ CFX_Path path; ++ path.AppendPoint(CFX_PointF(fLeft, fBottom + fHalfWidth), ++ CFX_Path::Point::Type::kMove); ++ path.AppendPoint(CFX_PointF(fRight, fBottom + fHalfWidth), ++ CFX_Path::Point::Type::kLine); ++ DrawPath(path, pUser2Device, &gsd, 0, color.ToFXColor(nTransparency), ++ CFX_FillRenderOptions::EvenOddOptions()); ++ break; + } + } + } + ++bool CFX_RenderDevice::MultiplyAlpha(float alpha) { ++ return m_pDeviceDriver->MultiplyAlpha(alpha); ++} ++ ++bool CFX_RenderDevice::MultiplyAlpha(const RetainPtr& mask) { ++ return m_pDeviceDriver->MultiplyAlpha(mask); ++} ++ + CFX_RenderDevice::StateRestorer::StateRestorer(CFX_RenderDevice* pDevice) + : m_pDevice(pDevice) { + m_pDevice->SaveState(); +diff --git a/core/fxge/cfx_renderdevice.h b/core/fxge/cfx_renderdevice.h +index 755f44532..e1d9487f2 100644 +--- a/core/fxge/cfx_renderdevice.h ++++ b/core/fxge/cfx_renderdevice.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,23 +12,30 @@ + + #include "build/build_config.h" + #include "core/fxcrt/fx_coordinates.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/cfx_path.h" ++#include "core/fxge/dib/fx_dib.h" + #include "core/fxge/render_defines.h" + #include "core/fxge/renderdevicedriver_iface.h" ++#include "third_party/base/span.h" + + class CFX_DIBBase; + class CFX_DIBitmap; + class CFX_Font; + class CFX_GraphStateData; + class CFX_ImageRenderer; +-class CFX_PathData; + class PauseIndicatorIface; + class TextCharPos; + struct CFX_Color; ++struct CFX_FillRenderOptions; ++struct CFX_TextRenderOptions; + +-enum class BorderStyle { SOLID, DASH, BEVELED, INSET, UNDERLINE }; ++enum class BorderStyle { kSolid, kDash, kBeveled, kInset, kUnderline }; + ++// Base class for all render devices. Derived classes must call ++// SetDeviceDriver() to fully initialize the class. Until then, class methods ++// are not safe to call, or may return invalid results. + class CFX_RenderDevice { + public: + class StateRestorer { +@@ -40,7 +47,6 @@ class CFX_RenderDevice { + UnownedPtr m_pDevice; + }; + +- CFX_RenderDevice(); + virtual ~CFX_RenderDevice(); + + static CFX_Matrix GetFlipMatrix(float width, +@@ -48,11 +54,6 @@ class CFX_RenderDevice { + float left, + float top); + +- void SetDeviceDriver(std::unique_ptr pDriver); +- RenderDeviceDriverIface* GetDeviceDriver() const { +- return m_pDeviceDriver.get(); +- } +- + void SaveState(); + void RestoreState(bool bKeepSaved); + +@@ -68,28 +69,25 @@ class CFX_RenderDevice { + int height) const; + const FX_RECT& GetClipBox() const { return m_ClipBox; } + void SetBaseClip(const FX_RECT& rect); +- bool SetClip_PathFill(const CFX_PathData* pPathData, ++ bool SetClip_PathFill(const CFX_Path& path, + const CFX_Matrix* pObject2Device, +- int fill_mode); +- bool SetClip_PathStroke(const CFX_PathData* pPathData, ++ const CFX_FillRenderOptions& fill_options); ++ bool SetClip_PathStroke(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState); + bool SetClip_Rect(const FX_RECT& pRect); +- bool DrawPath(const CFX_PathData* pPathData, ++ bool DrawPath(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState, + uint32_t fill_color, + uint32_t stroke_color, +- int fill_mode) { +- return DrawPathWithBlend(pPathData, pObject2Device, pGraphState, fill_color, +- stroke_color, fill_mode, BlendMode::kNormal); +- } +- bool DrawPathWithBlend(const CFX_PathData* pPathData, ++ const CFX_FillRenderOptions& fill_options); ++ bool DrawPathWithBlend(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState, + uint32_t fill_color, + uint32_t stroke_color, +- int fill_mode, ++ const CFX_FillRenderOptions& fill_options, + BlendMode blend_type); + bool FillRect(const FX_RECT& rect, uint32_t color) { + return FillRectWithBlend(rect, color, BlendMode::kNormal); +@@ -155,15 +153,13 @@ class CFX_RenderDevice { + BlendMode blend_mode); + bool ContinueDIBits(CFX_ImageRenderer* handle, PauseIndicatorIface* pPause); + +- bool DrawNormalText(int nChars, +- const TextCharPos* pCharPos, ++ bool DrawNormalText(pdfium::span pCharPos, + CFX_Font* pFont, + float font_size, + const CFX_Matrix& mtText2Device, + uint32_t fill_color, +- uint32_t text_flags); +- bool DrawTextPath(int nChars, +- const TextCharPos* pCharPos, ++ const CFX_TextRenderOptions& options); ++ bool DrawTextPath(pdfium::span pCharPos, + CFX_Font* pFont, + float font_size, + const CFX_Matrix& mtText2User, +@@ -171,8 +167,8 @@ class CFX_RenderDevice { + const CFX_GraphStateData* pGraphState, + uint32_t fill_color, + uint32_t stroke_color, +- CFX_PathData* pClippingPath, +- int nFlag); ++ CFX_Path* pClippingPath, ++ const CFX_FillRenderOptions& fill_options); + + void DrawFillRect(const CFX_Matrix* pUser2Device, + const CFX_FloatRect& rect, +@@ -202,15 +198,23 @@ class CFX_RenderDevice { + const std::vector& points, + const FX_COLORREF& color); + void DrawShadow(const CFX_Matrix& mtUser2Device, +- bool bVertical, +- bool bHorizontal, + const CFX_FloatRect& rect, + int32_t nTransparency, + int32_t nStartGray, + int32_t nEndGray); ++ bool DrawShading(const CPDF_ShadingPattern* pPattern, ++ const CFX_Matrix* pMatrix, ++ const FX_RECT& clip_rect, ++ int alpha, ++ bool bAlphaMode); ++ ++ // Multiplies the device by a constant alpha, returning `true` on success. ++ bool MultiplyAlpha(float alpha); ++ ++ // Multiplies the device by an alpha mask, returning `true` on success. ++ bool MultiplyAlpha(const RetainPtr& mask); + +-#ifdef _SKIA_SUPPORT_ +- virtual void DebugVerifyBitmapIsPreMultiplied() const; ++#if defined(_SKIA_SUPPORT_) + virtual bool SetBitsWithMask(const RetainPtr& pBitmap, + const RetainPtr& pMask, + int left, +@@ -218,24 +222,36 @@ class CFX_RenderDevice { + int bitmap_alpha, + BlendMode blend_type); + #endif +-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ +- void Flush(bool release); +-#endif ++ ++ protected: ++ CFX_RenderDevice(); ++ ++ void SetDeviceDriver(std::unique_ptr pDriver); ++ RenderDeviceDriverIface* GetDeviceDriver() const { ++ return m_pDeviceDriver.get(); ++ } + + private: + void InitDeviceInfo(); + void UpdateClipBox(); +- bool DrawFillStrokePath(const CFX_PathData* pPathData, ++ bool DrawFillStrokePath(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState, + uint32_t fill_color, + uint32_t stroke_color, +- int fill_mode, ++ const CFX_FillRenderOptions& fill_options, + BlendMode blend_type); + bool DrawCosmeticLine(const CFX_PointF& ptMoveTo, + const CFX_PointF& ptLineTo, + uint32_t color, +- int fill_mode, ++ const CFX_FillRenderOptions& fill_options, ++ BlendMode blend_type); ++ void DrawZeroAreaPath(const std::vector& path, ++ const CFX_Matrix* matrix, ++ bool adjust, ++ bool aliased_path, ++ uint32_t fill_color, ++ uint8_t fill_alpha, + BlendMode blend_type); + bool FillRectWithBlend(const FX_RECT& rect, + uint32_t color, +@@ -246,7 +262,7 @@ class CFX_RenderDevice { + int m_Height = 0; + int m_bpp = 0; + int m_RenderCaps = 0; +- DeviceType m_DeviceType = DeviceType::kUnknown; ++ DeviceType m_DeviceType = DeviceType::kDisplay; + FX_RECT m_ClipBox; + std::unique_ptr m_pDeviceDriver; + }; +diff --git a/core/fxge/cfx_substfont.cpp b/core/fxge/cfx_substfont.cpp +index b9936df2a..9fb243f44 100644 +--- a/core/fxge/cfx_substfont.cpp ++++ b/core/fxge/cfx_substfont.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,3 +9,20 @@ + CFX_SubstFont::CFX_SubstFont() = default; + + CFX_SubstFont::~CFX_SubstFont() = default; ++ ++#if defined(_SKIA_SUPPORT_) ++int CFX_SubstFont::GetOriginalWeight() const { ++ int weight = m_Weight; ++ ++ // Perform the inverse weight adjustment of UseChromeSerif() to get the ++ // original font weight. ++ if (m_Family == "Chrome Serif") ++ weight = weight * 5 / 4; ++ return weight; ++} ++#endif ++ ++void CFX_SubstFont::UseChromeSerif() { ++ m_Weight = m_Weight * 4 / 5; ++ m_Family = "Chrome Serif"; ++} +diff --git a/core/fxge/cfx_substfont.h b/core/fxge/cfx_substfont.h +index c9ffa40f8..c989e37f9 100644 +--- a/core/fxge/cfx_substfont.h ++++ b/core/fxge/cfx_substfont.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,21 +7,32 @@ + #ifndef CORE_FXGE_CFX_SUBSTFONT_H_ + #define CORE_FXGE_CFX_SUBSTFONT_H_ + ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/fx_codepage.h" +-#include "core/fxcrt/fx_string.h" + + class CFX_SubstFont { + public: + CFX_SubstFont(); + ~CFX_SubstFont(); + ++#if defined(_SKIA_SUPPORT_) ++ int GetOriginalWeight() const; ++#endif ++ void UseChromeSerif(); ++ ++ void SetIsBuiltInGenericFont() { m_bFlagMM = true; } ++ bool IsBuiltInGenericFont() const { return m_bFlagMM; } ++ + ByteString m_Family; +- int m_Charset = FX_CHARSET_ANSI; ++ FX_Charset m_Charset = FX_Charset::kANSI; + int m_Weight = 0; + int m_ItalicAngle = 0; + int m_WeightCJK = 0; ++ + bool m_bSubstCJK = false; + bool m_bItalicCJK = false; ++ ++ private: + bool m_bFlagMM = false; + }; + +diff --git a/core/fxge/cfx_textrenderoptions.h b/core/fxge/cfx_textrenderoptions.h +new file mode 100644 +index 000000000..7e7c48225 +--- /dev/null ++++ b/core/fxge/cfx_textrenderoptions.h +@@ -0,0 +1,57 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXGE_CFX_TEXTRENDEROPTIONS_H_ ++#define CORE_FXGE_CFX_TEXTRENDEROPTIONS_H_ ++ ++struct CFX_TextRenderOptions { ++ // AliasingType defines the options for drawing pixels on the edges of the ++ // text. The values are defined in an incrementing order due to the latter ++ // aliasing type's dependency on the previous one. ++ enum AliasingType { ++ // No transparent pixels on glyph edges. ++ kAliasing, ++ ++ // May have transparent pixels on glyph edges. ++ kAntiAliasing, ++ ++ // LCD optimization, can be enabled when anti-aliasing is allowed. ++ kLcd, ++ }; ++ ++ constexpr CFX_TextRenderOptions() = default; ++ constexpr explicit CFX_TextRenderOptions(AliasingType type) ++ : aliasing_type(type) {} ++ constexpr CFX_TextRenderOptions(const CFX_TextRenderOptions& other) = default; ++ CFX_TextRenderOptions& operator=(const CFX_TextRenderOptions& other) = ++ default; ++ ++ // Indicates whether anti-aliasing is enabled. ++ bool IsSmooth() const { ++ return aliasing_type == kAntiAliasing || aliasing_type == kLcd; ++ } ++ ++ // Aliasing option for fonts. ++ AliasingType aliasing_type = kAntiAliasing; ++ ++ // Font is CID font. ++ bool font_is_cid = false; ++ ++ // Using the native text output available on some platforms. ++ bool native_text = true; ++}; ++ ++inline bool operator==(const CFX_TextRenderOptions& lhs, ++ const CFX_TextRenderOptions& rhs) { ++ return lhs.aliasing_type == rhs.aliasing_type && ++ lhs.font_is_cid == rhs.font_is_cid && ++ lhs.native_text == rhs.native_text; ++} ++ ++inline bool operator!=(const CFX_TextRenderOptions& lhs, ++ const CFX_TextRenderOptions& rhs) { ++ return !(lhs == rhs); ++} ++ ++#endif // CORE_FXGE_CFX_TEXTRENDEROPTIONS_H_ +diff --git a/core/fxge/cfx_unicodeencoding.cpp b/core/fxge/cfx_unicodeencoding.cpp +index 6673fca2d..b8f818ade 100644 +--- a/core/fxge/cfx_unicodeencoding.cpp ++++ b/core/fxge/cfx_unicodeencoding.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,12 +9,13 @@ + #include "core/fxcrt/fx_codepage.h" + #include "core/fxge/cfx_font.h" + #include "core/fxge/cfx_substfont.h" ++#include "core/fxge/freetype/fx_freetype.h" + #include "core/fxge/fx_font.h" +-#include "core/fxge/fx_freetype.h" + +-CFX_UnicodeEncoding::CFX_UnicodeEncoding(CFX_Font* pFont) : m_pFont(pFont) {} ++CFX_UnicodeEncoding::CFX_UnicodeEncoding(const CFX_Font* pFont) ++ : m_pFont(pFont) {} + +-CFX_UnicodeEncoding::~CFX_UnicodeEncoding() {} ++CFX_UnicodeEncoding::~CFX_UnicodeEncoding() = default; + + uint32_t CFX_UnicodeEncoding::GlyphFromCharCode(uint32_t charcode) { + FXFT_FaceRec* face = m_pFont->GetFaceRec(); +@@ -25,7 +26,7 @@ uint32_t CFX_UnicodeEncoding::GlyphFromCharCode(uint32_t charcode) { + return FT_Get_Char_Index(face, charcode); + + if (m_pFont->GetSubstFont() && +- m_pFont->GetSubstFont()->m_Charset == FX_CHARSET_Symbol) { ++ m_pFont->GetSubstFont()->m_Charset == FX_Charset::kSymbol) { + uint32_t index = 0; + if (FT_Select_Charmap(face, FT_ENCODING_MS_SYMBOL) == 0) + index = FT_Get_Char_Index(face, charcode); +diff --git a/core/fxge/cfx_unicodeencoding.h b/core/fxge/cfx_unicodeencoding.h +index a730dc9d0..6ba80a7c3 100644 +--- a/core/fxge/cfx_unicodeencoding.h ++++ b/core/fxge/cfx_unicodeencoding.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -15,13 +15,13 @@ class CFX_Font; + + class CFX_UnicodeEncoding { + public: +- explicit CFX_UnicodeEncoding(CFX_Font* pFont); ++ explicit CFX_UnicodeEncoding(const CFX_Font* pFont); + virtual ~CFX_UnicodeEncoding(); + + virtual uint32_t GlyphFromCharCode(uint32_t charcode); + + protected: +- UnownedPtr const m_pFont; ++ UnownedPtr const m_pFont; + }; + + #endif // CORE_FXGE_CFX_UNICODEENCODING_H_ +diff --git a/core/fxge/cfx_unicodeencodingex.cpp b/core/fxge/cfx_unicodeencodingex.cpp +index a2fa24ace..683e2d06c 100644 +--- a/core/fxge/cfx_unicodeencodingex.cpp ++++ b/core/fxge/cfx_unicodeencodingex.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,37 +9,35 @@ + #include + + #include "core/fxge/cfx_font.h" ++#include "core/fxge/freetype/fx_freetype.h" + #include "core/fxge/fx_font.h" +-#include "core/fxge/fx_freetype.h" +-#include "third_party/base/ptr_util.h" + +-#define FXFM_ENC_TAG(a, b, c, d) \ ++#define ENC_TAG(a, b, c, d) \ + (((uint32_t)(a) << 24) | ((uint32_t)(b) << 16) | ((uint32_t)(c) << 8) | \ + (uint32_t)(d)) +-#define FXFM_ENCODING_MS_SYMBOL FXFM_ENC_TAG('s', 'y', 'm', 'b') +-#define FXFM_ENCODING_UNICODE FXFM_ENC_TAG('u', 'n', 'i', 'c') +-#define FXFM_ENCODING_MS_SJIS FXFM_ENC_TAG('s', 'j', 'i', 's') +-#define FXFM_ENCODING_MS_GB2312 FXFM_ENC_TAG('g', 'b', ' ', ' ') +-#define FXFM_ENCODING_MS_BIG5 FXFM_ENC_TAG('b', 'i', 'g', '5') +-#define FXFM_ENCODING_MS_WANSUNG FXFM_ENC_TAG('w', 'a', 'n', 's') +-#define FXFM_ENCODING_MS_JOHAB FXFM_ENC_TAG('j', 'o', 'h', 'a') +-#define FXFM_ENCODING_ADOBE_STANDARD FXFM_ENC_TAG('A', 'D', 'O', 'B') +-#define FXFM_ENCODING_ADOBE_EXPERT FXFM_ENC_TAG('A', 'D', 'B', 'E') +-#define FXFM_ENCODING_ADOBE_CUSTOM FXFM_ENC_TAG('A', 'D', 'B', 'C') +-#define FXFM_ENCODING_ADOBE_LATIN_1 FXFM_ENC_TAG('l', 'a', 't', '1') +-#define FXFM_ENCODING_OLD_LATIN_2 FXFM_ENC_TAG('l', 'a', 't', '2') +-#define FXFM_ENCODING_APPLE_ROMAN FXFM_ENC_TAG('a', 'r', 'm', 'n') + + namespace { + +-const uint32_t g_EncodingID[] = { +- FXFM_ENCODING_MS_SYMBOL, FXFM_ENCODING_UNICODE, +- FXFM_ENCODING_MS_SJIS, FXFM_ENCODING_MS_GB2312, +- FXFM_ENCODING_MS_BIG5, FXFM_ENCODING_MS_WANSUNG, +- FXFM_ENCODING_MS_JOHAB, FXFM_ENCODING_ADOBE_STANDARD, +- FXFM_ENCODING_ADOBE_EXPERT, FXFM_ENCODING_ADOBE_CUSTOM, +- FXFM_ENCODING_ADOBE_LATIN_1, FXFM_ENCODING_OLD_LATIN_2, +- FXFM_ENCODING_APPLE_ROMAN, ++constexpr uint32_t kEncodingExSymbol = ENC_TAG('s', 'y', 'm', 'b'); ++constexpr uint32_t kEncodingExUnicode = ENC_TAG('u', 'n', 'i', 'c'); ++constexpr uint32_t kEncodingExSjis = ENC_TAG('s', 'j', 'i', 's'); ++constexpr uint32_t kEncodingExGB2312 = ENC_TAG('g', 'b', ' ', ' '); ++constexpr uint32_t kEncodingExBig5 = ENC_TAG('b', 'i', 'g', '5'); ++constexpr uint32_t kEncodingExWansung = ENC_TAG('w', 'a', 'n', 's'); ++constexpr uint32_t kEncodingExJohab = ENC_TAG('j', 'o', 'h', 'a'); ++constexpr uint32_t kEncodingExAdobeStandard = ENC_TAG('A', 'D', 'O', 'B'); ++constexpr uint32_t kEncodingExAdobeExpert = ENC_TAG('A', 'D', 'B', 'E'); ++constexpr uint32_t kEncodingExAdobeCustom = ENC_TAG('A', 'D', 'B', 'C'); ++constexpr uint32_t kEncodingExLatin1 = ENC_TAG('l', 'a', 't', '1'); ++constexpr uint32_t kEncodingExOldLatin2 = ENC_TAG('l', 'a', 't', '2'); ++constexpr uint32_t kEncodingExAppleRoman = ENC_TAG('a', 'r', 'm', 'n'); ++ ++constexpr uint32_t kEncodingID[] = { ++ kEncodingExSymbol, kEncodingExUnicode, kEncodingExSjis, ++ kEncodingExGB2312, kEncodingExBig5, kEncodingExWansung, ++ kEncodingExJohab, kEncodingExAdobeStandard, kEncodingExAdobeExpert, ++ kEncodingExAdobeCustom, kEncodingExLatin1, kEncodingExOldLatin2, ++ kEncodingExAppleRoman, + }; + + std::unique_ptr FXFM_CreateFontEncoding( +@@ -47,7 +45,7 @@ std::unique_ptr FXFM_CreateFontEncoding( + uint32_t nEncodingID) { + if (FXFT_Select_Charmap(pFont->GetFaceRec(), nEncodingID)) + return nullptr; +- return pdfium::MakeUnique(pFont, nEncodingID); ++ return std::make_unique(pFont, nEncodingID); + } + + } // namespace +@@ -56,18 +54,16 @@ CFX_UnicodeEncodingEx::CFX_UnicodeEncodingEx(CFX_Font* pFont, + uint32_t EncodingID) + : CFX_UnicodeEncoding(pFont), m_nEncodingID(EncodingID) {} + +-CFX_UnicodeEncodingEx::~CFX_UnicodeEncodingEx() {} ++CFX_UnicodeEncodingEx::~CFX_UnicodeEncodingEx() = default; + + uint32_t CFX_UnicodeEncodingEx::GlyphFromCharCode(uint32_t charcode) { + FXFT_FaceRec* face = m_pFont->GetFaceRec(); + FT_UInt nIndex = FT_Get_Char_Index(face, charcode); + if (nIndex > 0) + return nIndex; +- int nmaps = FXFT_Get_Face_CharmapCount(face); + int m = 0; +- while (m < nmaps) { +- uint32_t nEncodingID = +- FXFT_Get_Charmap_Encoding(FXFT_Get_Face_Charmaps(face)[m++]); ++ while (m < face->num_charmaps) { ++ uint32_t nEncodingID = FXFT_Get_Charmap_Encoding(face->charmaps[m++]); + if (m_nEncodingID == nEncodingID) + continue; + int error = FXFT_Select_Charmap(face, nEncodingID); +@@ -84,17 +80,14 @@ uint32_t CFX_UnicodeEncodingEx::GlyphFromCharCode(uint32_t charcode) { + } + + uint32_t CFX_UnicodeEncodingEx::CharCodeFromUnicode(wchar_t Unicode) const { +- if (m_nEncodingID == FXFM_ENCODING_UNICODE || +- m_nEncodingID == FXFM_ENCODING_MS_SYMBOL) { ++ if (m_nEncodingID == kEncodingExUnicode || ++ m_nEncodingID == kEncodingExSymbol) { + return Unicode; + } + FXFT_FaceRec* face = m_pFont->GetFaceRec(); +- int nmaps = FXFT_Get_Face_CharmapCount(face); +- for (int i = 0; i < nmaps; i++) { +- int nEncodingID = +- FXFT_Get_Charmap_Encoding(FXFT_Get_Face_Charmaps(face)[i]); +- if (nEncodingID == FXFM_ENCODING_UNICODE || +- nEncodingID == FXFM_ENCODING_MS_SYMBOL) { ++ for (int i = 0; i < face->num_charmaps; i++) { ++ int nEncodingID = FXFT_Get_Charmap_Encoding(face->charmaps[i]); ++ if (nEncodingID == kEncodingExUnicode || nEncodingID == kEncodingExSymbol) { + return Unicode; + } + } +@@ -106,7 +99,7 @@ std::unique_ptr FX_CreateFontEncodingEx( + if (!pFont || !pFont->GetFaceRec()) + return nullptr; + +- for (uint32_t id : g_EncodingID) { ++ for (uint32_t id : kEncodingID) { + auto pFontEncoding = FXFM_CreateFontEncoding(pFont, id); + if (pFontEncoding) + return pFontEncoding; +diff --git a/core/fxge/cfx_unicodeencodingex.h b/core/fxge/cfx_unicodeencodingex.h +index a9c1f5819..da87d7eef 100644 +--- a/core/fxge/cfx_unicodeencodingex.h ++++ b/core/fxge/cfx_unicodeencodingex.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,9 +7,10 @@ + #ifndef CORE_FXGE_CFX_UNICODEENCODINGEX_H_ + #define CORE_FXGE_CFX_UNICODEENCODINGEX_H_ + ++#include ++ + #include + +-#include "core/fxcrt/fx_system.h" + #include "core/fxge/cfx_unicodeencoding.h" + + class CFX_UnicodeEncodingEx final : public CFX_UnicodeEncoding { +diff --git a/core/fxge/cfx_windowsrenderdevice.cpp b/core/fxge/cfx_windowsrenderdevice.cpp +new file mode 100644 +index 000000000..dd2601938 +--- /dev/null ++++ b/core/fxge/cfx_windowsrenderdevice.cpp +@@ -0,0 +1,55 @@ ++// Copyright 2014 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxge/cfx_windowsrenderdevice.h" ++ ++#include ++ ++#include "core/fxge/renderdevicedriver_iface.h" ++#include "core/fxge/win32/cgdi_display_driver.h" ++#include "core/fxge/win32/cgdi_printer_driver.h" ++#include "core/fxge/win32/cps_printer_driver.h" ++#include "core/fxge/win32/ctext_only_printer_driver.h" ++ ++namespace { ++ ++std::unique_ptr CreateDriver( ++ HDC hDC, ++ CFX_PSFontTracker* ps_font_tracker, ++ const EncoderIface* encoder_iface) { ++ int device_type = ::GetDeviceCaps(hDC, TECHNOLOGY); ++ int obj_type = ::GetObjectType(hDC); ++ bool use_printer = device_type == DT_RASPRINTER || ++ device_type == DT_PLOTTER || ++ device_type == DT_CHARSTREAM || obj_type == OBJ_ENHMETADC; ++ ++ if (!use_printer) ++ return std::make_unique(hDC); ++ ++ if (g_pdfium_print_mode == WindowsPrintMode::kEmf || ++ g_pdfium_print_mode == WindowsPrintMode::kEmfImageMasks) { ++ return std::make_unique(hDC); ++ } ++ ++ if (g_pdfium_print_mode == WindowsPrintMode::kTextOnly) ++ return std::make_unique(hDC); ++ ++ return std::make_unique(hDC, g_pdfium_print_mode, ++ ps_font_tracker, encoder_iface); ++} ++ ++} // namespace ++ ++WindowsPrintMode g_pdfium_print_mode = WindowsPrintMode::kEmf; ++ ++CFX_WindowsRenderDevice::CFX_WindowsRenderDevice( ++ HDC hDC, ++ CFX_PSFontTracker* ps_font_tracker, ++ const EncoderIface* encoder_iface) { ++ SetDeviceDriver(CreateDriver(hDC, ps_font_tracker, encoder_iface)); ++} ++ ++CFX_WindowsRenderDevice::~CFX_WindowsRenderDevice() = default; +diff --git a/core/fxge/cfx_windowsrenderdevice.h b/core/fxge/cfx_windowsrenderdevice.h +index 3d96207d3..7684fe30b 100644 +--- a/core/fxge/cfx_windowsrenderdevice.h ++++ b/core/fxge/cfx_windowsrenderdevice.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,38 +11,29 @@ + + #include "core/fxge/cfx_renderdevice.h" + +-enum WindowsPrintMode { +- kModeEmf = 0, +- kModeTextOnly = 1, +- kModePostScript2 = 2, +- kModePostScript3 = 3, +- kModePostScript2PassThrough = 4, +- kModePostScript3PassThrough = 5, ++enum class WindowsPrintMode { ++ kEmf = 0, ++ kTextOnly = 1, ++ kPostScript2 = 2, ++ kPostScript3 = 3, ++ kPostScript2PassThrough = 4, ++ kPostScript3PassThrough = 5, ++ kEmfImageMasks = 6, ++ kPostScript3Type42 = 7, ++ kPostScript3Type42PassThrough = 8, + }; + +-class RenderDeviceDriverIface; ++class CFX_PSFontTracker; + struct EncoderIface; + +-#if defined(PDFIUM_PRINT_TEXT_WITH_GDI) +-typedef void (*PDFiumEnsureTypefaceCharactersAccessible)(const LOGFONT* font, +- const wchar_t* text, +- size_t text_length); +- +-extern bool g_pdfium_print_text_with_gdi; +-extern PDFiumEnsureTypefaceCharactersAccessible +- g_pdfium_typeface_accessible_func; +-#endif + extern WindowsPrintMode g_pdfium_print_mode; + + class CFX_WindowsRenderDevice : public CFX_RenderDevice { + public: +- CFX_WindowsRenderDevice(HDC hDC, const EncoderIface* pEncoderIface); ++ CFX_WindowsRenderDevice(HDC hDC, ++ CFX_PSFontTracker* ps_font_tracker, ++ const EncoderIface* encoder_iface); + ~CFX_WindowsRenderDevice() override; +- +- private: +- static RenderDeviceDriverIface* CreateDriver( +- HDC hDC, +- const EncoderIface* pEncoderIface); + }; + + #endif // CORE_FXGE_CFX_WINDOWSRENDERDEVICE_H_ +diff --git a/core/fxge/win32/fx_win32_device_embeddertest.cpp b/core/fxge/cfx_windowsrenderdevice_embeddertest.cpp +similarity index 63% +rename from core/fxge/win32/fx_win32_device_embeddertest.cpp +rename to core/fxge/cfx_windowsrenderdevice_embeddertest.cpp +index cb088e758..b637b464c 100644 +--- a/core/fxge/win32/fx_win32_device_embeddertest.cpp ++++ b/core/fxge/cfx_windowsrenderdevice_embeddertest.cpp +@@ -1,15 +1,18 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include "core/fxge/win32/win32_int.h" ++#include "core/fxge/cfx_windowsrenderdevice.h" + + #include + + #include + ++#include "core/fxge/cfx_fillrenderoptions.h" ++#include "core/fxge/cfx_path.h" ++#include "core/fxge/win32/cfx_psfonttracker.h" ++#include "testing/embedder_test.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + + namespace { + +@@ -17,31 +20,34 @@ constexpr CFX_Matrix kIdentityMatrix; + + } // namespace + +-class CFX_WindowsRenderDeviceTest : public testing::Test { ++class CFX_WindowsRenderDeviceTest : public EmbedderTest { + public: + void SetUp() override { ++ EmbedderTest::SetUp(); ++ + // Get a device context with Windows GDI. + m_hDC = CreateCompatibleDC(nullptr); + ASSERT_TRUE(m_hDC); +- CFX_GEModule::Create(nullptr); +- m_driver = pdfium::MakeUnique(m_hDC, nullptr); ++ m_driver = std::make_unique( ++ m_hDC, &m_PSFontTracker, /*encoder_iface=*/nullptr); + m_driver->SaveState(); + } + + void TearDown() override { + m_driver->RestoreState(false); + m_driver.reset(); +- CFX_GEModule::Destroy(); + DeleteDC(m_hDC); ++ EmbedderTest::TearDown(); + } + + protected: + HDC m_hDC; ++ CFX_PSFontTracker m_PSFontTracker; + std::unique_ptr m_driver; + }; + + TEST_F(CFX_WindowsRenderDeviceTest, SimpleClipTriangle) { +- CFX_PathData path_data; ++ CFX_Path path_data; + CFX_PointF p1(0.0f, 0.0f); + CFX_PointF p2(0.0f, 100.0f); + CFX_PointF p3(100.0f, 100.0f); +@@ -50,21 +56,21 @@ TEST_F(CFX_WindowsRenderDeviceTest, SimpleClipTriangle) { + path_data.AppendLine(p2, p3); + path_data.AppendLine(p3, p1); + path_data.ClosePath(); +- EXPECT_TRUE( +- m_driver->SetClip_PathFill(&path_data, &kIdentityMatrix, FXFILL_WINDING)); ++ EXPECT_TRUE(m_driver->SetClip_PathFill( ++ path_data, &kIdentityMatrix, CFX_FillRenderOptions::WindingOptions())); + } + + TEST_F(CFX_WindowsRenderDeviceTest, SimpleClipRect) { +- CFX_PathData path_data; ++ CFX_Path path_data; + + path_data.AppendRect(0.0f, 100.0f, 200.0f, 0.0f); + path_data.ClosePath(); +- EXPECT_TRUE( +- m_driver->SetClip_PathFill(&path_data, &kIdentityMatrix, FXFILL_WINDING)); ++ EXPECT_TRUE(m_driver->SetClip_PathFill( ++ path_data, &kIdentityMatrix, CFX_FillRenderOptions::WindingOptions())); + } + + TEST_F(CFX_WindowsRenderDeviceTest, GargantuanClipRect) { +- CFX_PathData path_data; ++ CFX_Path path_data; + + path_data.AppendRect(-257698020.0f, -257697252.0f, 257698044.0f, + 257698812.0f); +@@ -73,12 +79,12 @@ TEST_F(CFX_WindowsRenderDeviceTest, GargantuanClipRect) { + // for a clip path should allow IntersectClipRect() to return success; + // however they do not because the GDI API IntersectClipRect() errors out and + // affect subsequent imaging. crbug.com/1019026 +- EXPECT_FALSE( +- m_driver->SetClip_PathFill(&path_data, &kIdentityMatrix, FXFILL_WINDING)); ++ EXPECT_FALSE(m_driver->SetClip_PathFill( ++ path_data, &kIdentityMatrix, CFX_FillRenderOptions::WindingOptions())); + } + + TEST_F(CFX_WindowsRenderDeviceTest, GargantuanClipRectWithBaseClip) { +- CFX_PathData path_data; ++ CFX_Path path_data; + const FX_RECT kBaseClip(0, 0, 5100, 6600); + + m_driver->SetBaseClip(kBaseClip); +@@ -87,6 +93,6 @@ TEST_F(CFX_WindowsRenderDeviceTest, GargantuanClipRectWithBaseClip) { + path_data.ClosePath(); + // Use of a reasonable base clip ensures that we avoid getting an error back + // from GDI API IntersectClipRect(). +- EXPECT_TRUE( +- m_driver->SetClip_PathFill(&path_data, &kIdentityMatrix, FXFILL_WINDING)); ++ EXPECT_TRUE(m_driver->SetClip_PathFill( ++ path_data, &kIdentityMatrix, CFX_FillRenderOptions::WindingOptions())); + } +diff --git a/core/fxge/dib/cfx_bitmapcomposer.cpp b/core/fxge/dib/cfx_bitmapcomposer.cpp +index beb02b7f1..009a61889 100644 +--- a/core/fxge/dib/cfx_bitmapcomposer.cpp ++++ b/core/fxge/dib/cfx_bitmapcomposer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,8 +6,15 @@ + + #include "core/fxge/dib/cfx_bitmapcomposer.h" + ++#include ++ ++#include "core/fxcrt/fx_2d_size.h" ++#include "core/fxcrt/fx_coordinates.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/span_util.h" + #include "core/fxge/cfx_cliprgn.h" + #include "core/fxge/dib/cfx_dibitmap.h" ++#include "third_party/base/check_op.h" + + CFX_BitmapComposer::CFX_BitmapComposer() = default; + +@@ -22,7 +29,7 @@ void CFX_BitmapComposer::Compose(const RetainPtr& pDest, + bool bFlipX, + bool bFlipY, + bool bRgbByteOrder, +- BlendMode blend_type) { ++ BlendMode blend_mode) { + m_pBitmap = pDest; + m_pClipRgn = pClipRgn; + m_DestLeft = dest_rect.left; +@@ -32,22 +39,24 @@ void CFX_BitmapComposer::Compose(const RetainPtr& pDest, + m_BitmapAlpha = bitmap_alpha; + m_MaskColor = mask_color; + m_pClipMask = nullptr; +- if (pClipRgn && pClipRgn->GetType() != CFX_ClipRgn::RectI) ++ if (pClipRgn && pClipRgn->GetType() != CFX_ClipRgn::kRectI) + m_pClipMask = pClipRgn->GetMask(); + m_bVertical = bVertical; + m_bFlipX = bFlipX; + m_bFlipY = bFlipY; + m_bRgbByteOrder = bRgbByteOrder; +- m_BlendType = blend_type; ++ m_BlendMode = blend_mode; + } + + bool CFX_BitmapComposer::SetInfo(int width, + int height, + FXDIB_Format src_format, +- uint32_t* pSrcPalette) { ++ pdfium::span src_palette) { ++ DCHECK_NE(src_format, FXDIB_Format::k1bppMask); ++ DCHECK_NE(src_format, FXDIB_Format::k1bppRgb); + m_SrcFormat = src_format; +- if (!m_Compositor.Init(m_pBitmap->GetFormat(), src_format, width, pSrcPalette, +- m_MaskColor, BlendMode::kNormal, ++ if (!m_Compositor.Init(m_pBitmap->GetFormat(), src_format, src_palette, ++ m_MaskColor, m_BlendMode, + m_pClipMask != nullptr || (m_BitmapAlpha < 255), + m_bRgbByteOrder)) { + return false; +@@ -55,8 +64,6 @@ bool CFX_BitmapComposer::SetInfo(int width, + if (m_bVertical) { + m_pScanlineV.resize(m_pBitmap->GetBPP() / 8 * width + 4); + m_pClipScanV.resize(m_pBitmap->GetHeight()); +- if (m_pBitmap->m_pAlphaMask) +- m_pScanlineAlphaV.resize(width + 4); + } + if (m_BitmapAlpha < 255) { + m_pAddClipScan.resize(m_bVertical ? m_pBitmap->GetHeight() +@@ -65,85 +72,78 @@ bool CFX_BitmapComposer::SetInfo(int width, + return true; + } + +-void CFX_BitmapComposer::DoCompose(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CFX_BitmapComposer::DoCompose(pdfium::span dest_scan, ++ pdfium::span src_scan, + int dest_width, +- const uint8_t* clip_scan, +- const uint8_t* src_extra_alpha, +- uint8_t* dst_extra_alpha) { +- uint8_t* pAddClipScan = m_pAddClipScan.data(); ++ pdfium::span clip_scan) { + if (m_BitmapAlpha < 255) { +- if (clip_scan) { ++ if (!clip_scan.empty()) { + for (int i = 0; i < dest_width; ++i) +- pAddClipScan[i] = clip_scan[i] * m_BitmapAlpha / 255; ++ m_pAddClipScan[i] = clip_scan[i] * m_BitmapAlpha / 255; + } else { +- memset(pAddClipScan, m_BitmapAlpha, dest_width); ++ fxcrt::spanset(pdfium::make_span(m_pAddClipScan).first(dest_width), ++ m_BitmapAlpha); + } +- clip_scan = pAddClipScan; ++ clip_scan = m_pAddClipScan; + } +- if (m_SrcFormat == FXDIB_8bppMask) { ++ if (m_SrcFormat == FXDIB_Format::k8bppMask) { + m_Compositor.CompositeByteMaskLine(dest_scan, src_scan, dest_width, +- clip_scan, dst_extra_alpha); +- } else if (GetBppFromFormat(m_SrcFormat) == 8) { ++ clip_scan); ++ } else if (m_SrcFormat == FXDIB_Format::k8bppRgb) { + m_Compositor.CompositePalBitmapLine(dest_scan, src_scan, 0, dest_width, +- clip_scan, src_extra_alpha, +- dst_extra_alpha); ++ clip_scan); + } else { + m_Compositor.CompositeRgbBitmapLine(dest_scan, src_scan, dest_width, +- clip_scan, src_extra_alpha, +- dst_extra_alpha); ++ clip_scan); + } + } + + void CFX_BitmapComposer::ComposeScanline(int line, +- const uint8_t* scanline, +- const uint8_t* scan_extra_alpha) { ++ pdfium::span scanline) { + if (m_bVertical) { +- ComposeScanlineV(line, scanline, scan_extra_alpha); ++ ComposeScanlineV(line, scanline); + return; + } +- const uint8_t* clip_scan = nullptr; ++ pdfium::span clip_scan; + if (m_pClipMask) { +- clip_scan = m_pClipMask->GetBuffer() + +- (m_DestTop + line - m_pClipRgn->GetBox().top) * +- m_pClipMask->GetPitch() + +- (m_DestLeft - m_pClipRgn->GetBox().left); ++ clip_scan = ++ m_pClipMask ++ ->GetWritableScanline(m_DestTop + line - m_pClipRgn->GetBox().top) ++ .subspan(m_DestLeft - m_pClipRgn->GetBox().left); ++ } ++ pdfium::span dest_scan = ++ m_pBitmap->GetWritableScanline(line + m_DestTop); ++ if (!dest_scan.empty()) { ++ FX_SAFE_UINT32 offset = m_DestLeft; ++ offset *= m_pBitmap->GetBPP(); ++ offset /= 8; ++ if (!offset.IsValid()) ++ return; ++ ++ dest_scan = dest_scan.subspan(offset.ValueOrDie()); + } +- uint8_t* dest_scan = m_pBitmap->GetWritableScanline(line + m_DestTop) + +- m_DestLeft * m_pBitmap->GetBPP() / 8; +- uint8_t* dest_alpha_scan = +- m_pBitmap->m_pAlphaMask +- ? m_pBitmap->m_pAlphaMask->GetWritableScanline(line + m_DestTop) + +- m_DestLeft +- : nullptr; +- DoCompose(dest_scan, scanline, m_DestWidth, clip_scan, scan_extra_alpha, +- dest_alpha_scan); ++ DoCompose(dest_scan, scanline, m_DestWidth, clip_scan); + } + +-void CFX_BitmapComposer::ComposeScanlineV(int line, +- const uint8_t* scanline, +- const uint8_t* scan_extra_alpha) { ++void CFX_BitmapComposer::ComposeScanlineV( ++ int line, ++ pdfium::span scanline) { + int Bpp = m_pBitmap->GetBPP() / 8; + int dest_pitch = m_pBitmap->GetPitch(); +- int dest_alpha_pitch = +- m_pBitmap->m_pAlphaMask ? m_pBitmap->m_pAlphaMask->GetPitch() : 0; + int dest_x = m_DestLeft + (m_bFlipX ? (m_DestWidth - line - 1) : line); +- uint8_t* dest_buf = +- m_pBitmap->GetBuffer() + dest_x * Bpp + m_DestTop * dest_pitch; +- uint8_t* dest_alpha_buf = m_pBitmap->m_pAlphaMask +- ? m_pBitmap->m_pAlphaMask->GetBuffer() + +- dest_x + m_DestTop * dest_alpha_pitch +- : nullptr; +- if (m_bFlipY) { +- dest_buf += dest_pitch * (m_DestHeight - 1); +- dest_alpha_buf += dest_alpha_pitch * (m_DestHeight - 1); +- } +- int y_step = dest_pitch; +- int y_alpha_step = dest_alpha_pitch; +- if (m_bFlipY) { +- y_step = -y_step; +- y_alpha_step = -y_alpha_step; ++ pdfium::span dest_span = m_pBitmap->GetBuffer(); ++ if (!dest_span.empty()) { ++ const size_t dest_x_offset = Fx2DSizeOrDie(dest_x, Bpp); ++ const size_t dest_y_offset = Fx2DSizeOrDie(m_DestTop, dest_pitch); ++ dest_span = dest_span.subspan(dest_y_offset).subspan(dest_x_offset); ++ if (m_bFlipY) { ++ const size_t dest_flip_offset = ++ Fx2DSizeOrDie(dest_pitch, m_DestHeight - 1); ++ dest_span = dest_span.subspan(dest_flip_offset); ++ } + } ++ uint8_t* dest_buf = dest_span.data(); ++ const int y_step = m_bFlipY ? -dest_pitch : dest_pitch; + uint8_t* src_scan = m_pScanlineV.data(); + uint8_t* dest_scan = dest_buf; + for (int i = 0; i < m_DestHeight; ++i) { +@@ -151,24 +151,16 @@ void CFX_BitmapComposer::ComposeScanlineV(int line, + *src_scan++ = dest_scan[j]; + dest_scan += y_step; + } +- uint8_t* src_alpha_scan = m_pScanlineAlphaV.data(); +- uint8_t* dest_alpha_scan = dest_alpha_buf; +- if (dest_alpha_scan) { +- for (int i = 0; i < m_DestHeight; ++i) { +- *src_alpha_scan++ = *dest_alpha_scan; +- dest_alpha_scan += y_alpha_step; +- } +- } +- uint8_t* clip_scan = nullptr; ++ pdfium::span clip_scan; + if (m_pClipMask) { +- clip_scan = m_pClipScanV.data(); ++ clip_scan = m_pClipScanV; + int clip_pitch = m_pClipMask->GetPitch(); + const uint8_t* src_clip = +- m_pClipMask->GetBuffer() + +- (m_DestTop - m_pClipRgn->GetBox().top) * clip_pitch + +- (dest_x - m_pClipRgn->GetBox().left); ++ m_pClipMask->GetScanline(m_DestTop - m_pClipRgn->GetBox().top) ++ .subspan(dest_x - m_pClipRgn->GetBox().left) ++ .data(); + if (m_bFlipY) { +- src_clip += clip_pitch * (m_DestHeight - 1); ++ src_clip += Fx2DSizeOrDie(clip_pitch, m_DestHeight - 1); + clip_pitch = -clip_pitch; + } + for (int i = 0; i < m_DestHeight; ++i) { +@@ -176,8 +168,7 @@ void CFX_BitmapComposer::ComposeScanlineV(int line, + src_clip += clip_pitch; + } + } +- DoCompose(m_pScanlineV.data(), scanline, m_DestHeight, clip_scan, +- scan_extra_alpha, m_pScanlineAlphaV.data()); ++ DoCompose(m_pScanlineV, scanline, m_DestHeight, clip_scan); + src_scan = m_pScanlineV.data(); + dest_scan = dest_buf; + for (int i = 0; i < m_DestHeight; ++i) { +@@ -185,12 +176,4 @@ void CFX_BitmapComposer::ComposeScanlineV(int line, + dest_scan[j] = *src_scan++; + dest_scan += y_step; + } +- src_alpha_scan = m_pScanlineAlphaV.data(); +- dest_alpha_scan = dest_alpha_buf; +- if (!dest_alpha_scan) +- return; +- for (int i = 0; i < m_DestHeight; ++i) { +- *dest_alpha_scan = *src_alpha_scan++; +- dest_alpha_scan += y_alpha_step; +- } + } +diff --git a/core/fxge/dib/cfx_bitmapcomposer.h b/core/fxge/dib/cfx_bitmapcomposer.h +index 74a7efa68..c4cc07d7c 100644 +--- a/core/fxge/dib/cfx_bitmapcomposer.h ++++ b/core/fxge/dib/cfx_bitmapcomposer.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,17 +7,18 @@ + #ifndef CORE_FXGE_DIB_CFX_BITMAPCOMPOSER_H_ + #define CORE_FXGE_DIB_CFX_BITMAPCOMPOSER_H_ + +-#include ++#include + +-#include "core/fxcrt/fx_coordinates.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + #include "core/fxge/dib/cfx_scanlinecompositor.h" ++#include "core/fxge/dib/fx_dib.h" + #include "core/fxge/dib/scanlinecomposer_iface.h" +-#include "core/fxge/fx_dib.h" + + class CFX_ClipRgn; + class CFX_DIBitmap; ++struct FX_RECT; + + class CFX_BitmapComposer final : public ScanlineComposerIface { + public: +@@ -33,28 +34,21 @@ class CFX_BitmapComposer final : public ScanlineComposerIface { + bool bFlipX, + bool bFlipY, + bool bRgbByteOrder, +- BlendMode blend_type); ++ BlendMode blend_mode); + +- // ScanlineComposerIface ++ // ScanlineComposerIface: + bool SetInfo(int width, + int height, + FXDIB_Format src_format, +- uint32_t* pSrcPalette) override; +- +- void ComposeScanline(int line, +- const uint8_t* scanline, +- const uint8_t* scan_extra_alpha) override; ++ pdfium::span src_palette) override; ++ void ComposeScanline(int line, pdfium::span scanline) override; + + private: +- void DoCompose(uint8_t* dest_scan, +- const uint8_t* src_scan, ++ void DoCompose(pdfium::span dest_scan, ++ pdfium::span src_scan, + int dest_width, +- const uint8_t* clip_scan, +- const uint8_t* src_extra_alpha, +- uint8_t* dst_extra_alpha); +- void ComposeScanlineV(int line, +- const uint8_t* scanline, +- const uint8_t* scan_extra_alpha); ++ pdfium::span clip_scan); ++ void ComposeScanlineV(int line, pdfium::span scanline); + + RetainPtr m_pBitmap; + UnownedPtr m_pClipRgn; +@@ -71,11 +65,10 @@ class CFX_BitmapComposer final : public ScanlineComposerIface { + bool m_bFlipX; + bool m_bFlipY; + bool m_bRgbByteOrder = false; +- BlendMode m_BlendType = BlendMode::kNormal; +- std::vector m_pScanlineV; +- std::vector m_pClipScanV; +- std::vector m_pAddClipScan; +- std::vector m_pScanlineAlphaV; ++ BlendMode m_BlendMode = BlendMode::kNormal; ++ DataVector m_pScanlineV; ++ DataVector m_pClipScanV; ++ DataVector m_pAddClipScan; + }; + + #endif // CORE_FXGE_DIB_CFX_BITMAPCOMPOSER_H_ +diff --git a/core/fxge/dib/cfx_bitmapstorer.cpp b/core/fxge/dib/cfx_bitmapstorer.cpp +index 97ddf6f0b..607b0cb49 100644 +--- a/core/fxge/dib/cfx_bitmapstorer.cpp ++++ b/core/fxge/dib/cfx_bitmapstorer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,13 +6,17 @@ + + #include "core/fxge/dib/cfx_bitmapstorer.h" + ++#include ++ + #include + ++#include "core/fxcrt/span_util.h" + #include "core/fxge/dib/cfx_dibitmap.h" ++#include "third_party/base/check_op.h" + +-CFX_BitmapStorer::CFX_BitmapStorer() {} ++CFX_BitmapStorer::CFX_BitmapStorer() = default; + +-CFX_BitmapStorer::~CFX_BitmapStorer() {} ++CFX_BitmapStorer::~CFX_BitmapStorer() = default; + + RetainPtr CFX_BitmapStorer::Detach() { + return std::move(m_pBitmap); +@@ -23,32 +27,24 @@ void CFX_BitmapStorer::Replace(RetainPtr&& pBitmap) { + } + + void CFX_BitmapStorer::ComposeScanline(int line, +- const uint8_t* scanline, +- const uint8_t* scan_extra_alpha) { +- uint8_t* dest_buf = m_pBitmap->GetWritableScanline(line); +- uint8_t* dest_alpha_buf = +- m_pBitmap->m_pAlphaMask +- ? m_pBitmap->m_pAlphaMask->GetWritableScanline(line) +- : nullptr; +- if (dest_buf) +- memcpy(dest_buf, scanline, m_pBitmap->GetPitch()); +- +- if (dest_alpha_buf) { +- memcpy(dest_alpha_buf, scan_extra_alpha, +- m_pBitmap->m_pAlphaMask->GetPitch()); +- } ++ pdfium::span scanline) { ++ pdfium::span dest_buf = m_pBitmap->GetWritableScanline(line); ++ if (!dest_buf.empty()) ++ fxcrt::spancpy(dest_buf, scanline); + } + + bool CFX_BitmapStorer::SetInfo(int width, + int height, + FXDIB_Format src_format, +- uint32_t* pSrcPalette) { ++ pdfium::span src_palette) { ++ DCHECK_NE(src_format, FXDIB_Format::k1bppMask); ++ DCHECK_NE(src_format, FXDIB_Format::k1bppRgb); + auto pBitmap = pdfium::MakeRetain(); + if (!pBitmap->Create(width, height, src_format)) + return false; + +- if (pSrcPalette) +- pBitmap->SetPalette(pSrcPalette); ++ if (!src_palette.empty()) ++ pBitmap->SetPalette(src_palette); + + m_pBitmap = std::move(pBitmap); + return true; +diff --git a/core/fxge/dib/cfx_bitmapstorer.h b/core/fxge/dib/cfx_bitmapstorer.h +index c8158a3d9..de934e22f 100644 +--- a/core/fxge/dib/cfx_bitmapstorer.h ++++ b/core/fxge/dib/cfx_bitmapstorer.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,9 +7,9 @@ + #ifndef CORE_FXGE_DIB_CFX_BITMAPSTORER_H_ + #define CORE_FXGE_DIB_CFX_BITMAPSTORER_H_ + +-#include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxge/dib/scanlinecomposer_iface.h" ++#include "third_party/base/span.h" + + class CFX_DIBitmap; + +@@ -18,14 +18,12 @@ class CFX_BitmapStorer final : public ScanlineComposerIface { + CFX_BitmapStorer(); + ~CFX_BitmapStorer() override; + +- // ScanlineComposerIface +- void ComposeScanline(int line, +- const uint8_t* scanline, +- const uint8_t* scan_extra_alpha) override; ++ // ScanlineComposerIface: ++ void ComposeScanline(int line, pdfium::span scanline) override; + bool SetInfo(int width, + int height, + FXDIB_Format src_format, +- uint32_t* pSrcPalette) override; ++ pdfium::span src_palette) override; + + RetainPtr GetBitmap() { return m_pBitmap; } + RetainPtr Detach(); +diff --git a/core/fxge/dib/cfx_cmyk_to_srgb.cpp b/core/fxge/dib/cfx_cmyk_to_srgb.cpp +index d75987cd0..655a6c005 100644 +--- a/core/fxge/dib/cfx_cmyk_to_srgb.cpp ++++ b/core/fxge/dib/cfx_cmyk_to_srgb.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,6 +10,7 @@ + #include + + #include "core/fxcrt/fx_system.h" ++#include "third_party/base/check_op.h" + + namespace fxge { + +@@ -1740,10 +1741,10 @@ std::tuple AdobeCMYK_to_sRGB(float c, + uint8_t y1 = static_cast(y * 255.f + rounding_offset); + uint8_t k1 = static_cast(k * 255.f + rounding_offset); + +- ASSERT(c1 == FXSYS_roundf(c * 255)); +- ASSERT(m1 == FXSYS_roundf(m * 255)); +- ASSERT(y1 == FXSYS_roundf(y * 255)); +- ASSERT(k1 == FXSYS_roundf(k * 255)); ++ DCHECK_EQ(c1, FXSYS_roundf(c * 255)); ++ DCHECK_EQ(m1, FXSYS_roundf(m * 255)); ++ DCHECK_EQ(y1, FXSYS_roundf(y * 255)); ++ DCHECK_EQ(k1, FXSYS_roundf(k * 255)); + + uint8_t r; + uint8_t g; +diff --git a/core/fxge/dib/cfx_cmyk_to_srgb.h b/core/fxge/dib/cfx_cmyk_to_srgb.h +index aebd27a40..e38e57cc2 100644 +--- a/core/fxge/dib/cfx_cmyk_to_srgb.h ++++ b/core/fxge/dib/cfx_cmyk_to_srgb.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxge/dib/cfx_cmyk_to_srgb_unittest.cpp b/core/fxge/dib/cfx_cmyk_to_srgb_unittest.cpp +index e1c0ffd09..d28f9cddb 100644 +--- a/core/fxge/dib/cfx_cmyk_to_srgb_unittest.cpp ++++ b/core/fxge/dib/cfx_cmyk_to_srgb_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxge/dib/cfx_dibbase.cpp b/core/fxge/dib/cfx_dibbase.cpp +index 4a4223a45..07c7b744d 100644 +--- a/core/fxge/dib/cfx_dibbase.cpp ++++ b/core/fxge/dib/cfx_dibbase.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,18 +6,28 @@ + + #include "core/fxge/dib/cfx_dibbase.h" + ++#include ++#include ++ + #include +-#include + #include + #include + ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_2d_size.h" ++#include "core/fxcrt/fx_coordinates.h" ++#include "core/fxcrt/fx_memory.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/span_util.h" + #include "core/fxge/cfx_cliprgn.h" + #include "core/fxge/dib/cfx_bitmapstorer.h" +-#include "core/fxge/dib/cfx_cmyk_to_srgb.h" + #include "core/fxge/dib/cfx_dibitmap.h" + #include "core/fxge/dib/cfx_imagestretcher.h" + #include "core/fxge/dib/cfx_imagetransformer.h" +-#include "third_party/base/logging.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/notreached.h" ++#include "third_party/base/span.h" + + namespace { + +@@ -48,7 +58,7 @@ void Obtain_Pal(std::pair* luts, + + class CFX_Palette { + public: +- explicit CFX_Palette(const RetainPtr& pBitmap); ++ explicit CFX_Palette(const RetainPtr& pBitmap); + ~CFX_Palette(); + + const uint32_t* GetPalette() { return m_Palette.data(); } +@@ -60,18 +70,19 @@ class CFX_Palette { + std::vector m_Palette; + // (Amount, Color) pairs + std::vector> m_Luts; +- int m_lut; ++ int m_lut = 0; + }; + +-CFX_Palette::CFX_Palette(const RetainPtr& pBitmap) +- : m_Palette(256), m_Luts(4096), m_lut(0) { ++CFX_Palette::CFX_Palette(const RetainPtr& pBitmap) ++ : m_Palette(256), m_Luts(4096) { + int bpp = pBitmap->GetBPP() / 8; + int width = pBitmap->GetWidth(); + int height = pBitmap->GetHeight(); + for (int row = 0; row < height; ++row) { +- const uint8_t* scan_line = pBitmap->GetScanline(row); ++ pdfium::span scan_line = pBitmap->GetScanline(row); + for (int col = 0; col < width; ++col) { +- const uint8_t* src_port = scan_line + col * bpp; ++ const uint8_t* src_port = ++ scan_line.subspan(Fx2DSizeOrDie(col, bpp)).data(); + uint32_t b = src_port[0] & 0xf0; + uint32_t g = src_port[1] & 0xf0; + uint32_t r = src_port[2] & 0xf0; +@@ -95,21 +106,25 @@ CFX_Palette::CFX_Palette(const RetainPtr& pBitmap) + Obtain_Pal(m_Luts.data(), m_Palette.data(), m_lut); + } + +-CFX_Palette::~CFX_Palette() {} ++CFX_Palette::~CFX_Palette() = default; + +-void ConvertBuffer_1bppMask2Gray(uint8_t* dest_buf, ++void ConvertBuffer_1bppMask2Gray(pdfium::span dest_buf, + int dest_pitch, + int width, + int height, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + int src_left, + int src_top) { + static constexpr uint8_t kSetGray = 0xff; + static constexpr uint8_t kResetGray = 0x00; + for (int row = 0; row < height; ++row) { +- uint8_t* dest_scan = dest_buf + row * dest_pitch; +- memset(dest_scan, kResetGray, width); +- const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row); ++ pdfium::span dest_span = ++ dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)); ++ pdfium::span src_span = ++ pSrcBitmap->GetScanline(src_top + row); ++ fxcrt::spanset(dest_span.first(width), kResetGray); ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); + for (int col = src_left; col < src_left + width; ++col) { + if (src_scan[col / 8] & (1 << (7 - col % 8))) + *dest_scan = kSetGray; +@@ -118,151 +133,111 @@ void ConvertBuffer_1bppMask2Gray(uint8_t* dest_buf, + } + } + +-void ConvertBuffer_8bppMask2Gray(uint8_t* dest_buf, ++void ConvertBuffer_8bppMask2Gray(pdfium::span dest_buf, + int dest_pitch, + int width, + int height, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + int src_left, + int src_top) { + for (int row = 0; row < height; ++row) { +- uint8_t* dest_scan = dest_buf + row * dest_pitch; +- const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left; +- memcpy(dest_scan, src_scan, width); ++ fxcrt::spancpy( ++ dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)), ++ pSrcBitmap->GetScanline(src_top + row).subspan(src_left, width)); + } + } + +-void ConvertBuffer_1bppPlt2Gray(uint8_t* dest_buf, ++void ConvertBuffer_1bppPlt2Gray(pdfium::span dest_buf, + int dest_pitch, + int width, + int height, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + int src_left, + int src_top) { +- uint32_t* src_plt = pSrcBitmap->GetPalette(); +- uint8_t gray[2]; +- uint8_t reset_r; +- uint8_t reset_g; +- uint8_t reset_b; +- uint8_t set_r; +- uint8_t set_g; +- uint8_t set_b; +- if (pSrcBitmap->IsCmykImage()) { +- std::tie(reset_r, reset_g, reset_b) = AdobeCMYK_to_sRGB1( +- FXSYS_GetCValue(src_plt[0]), FXSYS_GetMValue(src_plt[0]), +- FXSYS_GetYValue(src_plt[0]), FXSYS_GetKValue(src_plt[0])); +- std::tie(set_r, set_g, set_b) = AdobeCMYK_to_sRGB1( +- FXSYS_GetCValue(src_plt[1]), FXSYS_GetMValue(src_plt[1]), +- FXSYS_GetYValue(src_plt[1]), FXSYS_GetKValue(src_plt[1])); +- } else { +- reset_r = FXARGB_R(src_plt[0]); +- reset_g = FXARGB_G(src_plt[0]); +- reset_b = FXARGB_B(src_plt[0]); +- set_r = FXARGB_R(src_plt[1]); +- set_g = FXARGB_G(src_plt[1]); +- set_b = FXARGB_B(src_plt[1]); +- } +- gray[0] = FXRGB2GRAY(reset_r, reset_g, reset_b); +- gray[1] = FXRGB2GRAY(set_r, set_g, set_b); ++ pdfium::span src_palette = pSrcBitmap->GetPaletteSpan(); ++ const uint8_t reset_r = FXARGB_R(src_palette[0]); ++ const uint8_t reset_g = FXARGB_G(src_palette[0]); ++ const uint8_t reset_b = FXARGB_B(src_palette[0]); ++ const uint8_t set_r = FXARGB_R(src_palette[1]); ++ const uint8_t set_g = FXARGB_G(src_palette[1]); ++ const uint8_t set_b = FXARGB_B(src_palette[1]); ++ const uint8_t gray0 = FXRGB2GRAY(reset_r, reset_g, reset_b); ++ const uint8_t gray1 = FXRGB2GRAY(set_r, set_g, set_b); + + for (int row = 0; row < height; ++row) { +- uint8_t* dest_scan = dest_buf + row * dest_pitch; +- memset(dest_scan, gray[0], width); +- const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row); ++ pdfium::span dest_span = ++ dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)); ++ fxcrt::spanset(dest_span.first(width), gray0); ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row).data(); + for (int col = src_left; col < src_left + width; ++col) { + if (src_scan[col / 8] & (1 << (7 - col % 8))) +- *dest_scan = gray[1]; ++ *dest_scan = gray1; + ++dest_scan; + } + } + } + +-void ConvertBuffer_8bppPlt2Gray(uint8_t* dest_buf, ++void ConvertBuffer_8bppPlt2Gray(pdfium::span dest_buf, + int dest_pitch, + int width, + int height, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + int src_left, + int src_top) { +- uint32_t* src_plt = pSrcBitmap->GetPalette(); ++ pdfium::span src_palette = pSrcBitmap->GetPaletteSpan(); + uint8_t gray[256]; +- if (pSrcBitmap->IsCmykImage()) { +- uint8_t r; +- uint8_t g; +- uint8_t b; +- for (size_t i = 0; i < FX_ArraySize(gray); ++i) { +- std::tie(r, g, b) = AdobeCMYK_to_sRGB1( +- FXSYS_GetCValue(src_plt[i]), FXSYS_GetMValue(src_plt[i]), +- FXSYS_GetYValue(src_plt[i]), FXSYS_GetKValue(src_plt[i])); +- gray[i] = FXRGB2GRAY(r, g, b); +- } +- } else { +- for (size_t i = 0; i < FX_ArraySize(gray); ++i) { +- gray[i] = FXRGB2GRAY(FXARGB_R(src_plt[i]), FXARGB_G(src_plt[i]), +- FXARGB_B(src_plt[i])); +- } ++ for (size_t i = 0; i < std::size(gray); ++i) { ++ gray[i] = FXRGB2GRAY(FXARGB_R(src_palette[i]), FXARGB_G(src_palette[i]), ++ FXARGB_B(src_palette[i])); + } + + for (int row = 0; row < height; ++row) { +- uint8_t* dest_scan = dest_buf + row * dest_pitch; +- const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left; ++ uint8_t* dest_scan = ++ dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data(); ++ const uint8_t* src_scan = ++ pSrcBitmap->GetScanline(src_top + row).subspan(src_left).data(); + for (int col = 0; col < width; ++col) + *dest_scan++ = gray[*src_scan++]; + } + } + +-void ConvertBuffer_RgbOrCmyk2Gray(uint8_t* dest_buf, +- int dest_pitch, +- int width, +- int height, +- const RetainPtr& pSrcBitmap, +- int src_left, +- int src_top) { +- int Bpp = pSrcBitmap->GetBPP() / 8; +- if (pSrcBitmap->IsCmykImage()) { +- for (int row = 0; row < height; ++row) { +- uint8_t* dest_scan = dest_buf + row * dest_pitch; +- const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left * 4; +- for (int col = 0; col < width; ++col) { +- uint8_t r; +- uint8_t g; +- uint8_t b; +- std::tie(r, g, b) = AdobeCMYK_to_sRGB1( +- FXSYS_GetCValue(static_cast(src_scan[0])), +- FXSYS_GetMValue(static_cast(src_scan[1])), +- FXSYS_GetYValue(static_cast(src_scan[2])), +- FXSYS_GetKValue(static_cast(src_scan[3]))); +- *dest_scan++ = FXRGB2GRAY(r, g, b); +- src_scan += 4; +- } +- } +- } else { +- for (int row = 0; row < height; ++row) { +- uint8_t* dest_scan = dest_buf + row * dest_pitch; +- const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left * Bpp; +- for (int col = 0; col < width; ++col) { +- *dest_scan++ = FXRGB2GRAY(src_scan[2], src_scan[1], src_scan[0]); +- src_scan += Bpp; +- } ++void ConvertBuffer_Rgb2Gray(pdfium::span dest_buf, ++ int dest_pitch, ++ int width, ++ int height, ++ const RetainPtr& pSrcBitmap, ++ int src_left, ++ int src_top) { ++ const int Bpp = pSrcBitmap->GetBPP() / 8; ++ const size_t x_offset = Fx2DSizeOrDie(src_left, Bpp); ++ for (int row = 0; row < height; ++row) { ++ uint8_t* dest_scan = ++ dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data(); ++ const uint8_t* src_scan = ++ pSrcBitmap->GetScanline(src_top + row).subspan(x_offset).data(); ++ for (int col = 0; col < width; ++col) { ++ *dest_scan++ = FXRGB2GRAY(src_scan[2], src_scan[1], src_scan[0]); ++ src_scan += Bpp; + } + } + } + +-void ConvertBuffer_IndexCopy(uint8_t* dest_buf, ++void ConvertBuffer_IndexCopy(pdfium::span dest_buf, + int dest_pitch, + int width, + int height, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + int src_left, + int src_top) { + if (pSrcBitmap->GetBPP() == 1) { + for (int row = 0; row < height; ++row) { +- uint8_t* dest_scan = dest_buf + row * dest_pitch; ++ pdfium::span dest_span = ++ dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)); + // Set all destination pixels to be white initially. +- memset(dest_scan, 255, width); +- const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row); ++ fxcrt::spanset(dest_span.first(width), 255); ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row).data(); + for (int col = src_left; col < src_left + width; ++col) { + // If the source bit is set, then set the destination pixel to be black. + if (src_scan[col / 8] & (1 << (7 - col % 8))) +@@ -273,49 +248,40 @@ void ConvertBuffer_IndexCopy(uint8_t* dest_buf, + } + } else { + for (int row = 0; row < height; ++row) { +- uint8_t* dest_scan = dest_buf + row * dest_pitch; +- const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left; +- memcpy(dest_scan, src_scan, width); ++ fxcrt::spancpy( ++ dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)), ++ pSrcBitmap->GetScanline(src_top + row).subspan(src_left, width)); + } + } + } + +-void ConvertBuffer_Plt2PltRgb8(uint8_t* dest_buf, ++void ConvertBuffer_Plt2PltRgb8(pdfium::span dest_buf, + int dest_pitch, + int width, + int height, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + int src_left, + int src_top, +- uint32_t* dst_plt) { ++ pdfium::span dst_plt) { + ConvertBuffer_IndexCopy(dest_buf, dest_pitch, width, height, pSrcBitmap, + src_left, src_top); +- uint32_t* src_plt = pSrcBitmap->GetPalette(); +- size_t plt_size = pSrcBitmap->GetPaletteSize(); +- if (pSrcBitmap->IsCmykImage()) { +- for (size_t i = 0; i < plt_size; ++i) { +- uint8_t r; +- uint8_t g; +- uint8_t b; +- std::tie(r, g, b) = AdobeCMYK_to_sRGB1( +- FXSYS_GetCValue(src_plt[i]), FXSYS_GetMValue(src_plt[i]), +- FXSYS_GetYValue(src_plt[i]), FXSYS_GetKValue(src_plt[i])); +- dst_plt[i] = ArgbEncode(0xff, r, g, b); +- } +- } else { +- memcpy(dst_plt, src_plt, plt_size * 4); +- } ++ const size_t plt_size = pSrcBitmap->GetRequiredPaletteSize(); ++ pdfium::span src_span = pSrcBitmap->GetPaletteSpan(); ++ CHECK_LE(plt_size, src_span.size()); ++ ++ const uint32_t* src_plt = src_span.data(); ++ for (size_t i = 0; i < plt_size; ++i) ++ dst_plt[i] = src_plt[i]; + } + +-void ConvertBuffer_Rgb2PltRgb8(uint8_t* dest_buf, ++void ConvertBuffer_Rgb2PltRgb8(pdfium::span dest_buf, + int dest_pitch, + int width, + int height, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + int src_left, + int src_top, +- uint32_t* dst_plt) { ++ pdfium::span dst_plt) { + int bpp = pSrcBitmap->GetBPP() / 8; + CFX_Palette palette(pSrcBitmap); + const std::pair* Luts = palette.GetLuts(); +@@ -348,10 +314,13 @@ void ConvertBuffer_Rgb2PltRgb8(uint8_t* dest_buf, + } + int32_t lut_1 = lut - 1; + for (int row = 0; row < height; ++row) { +- const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left; +- uint8_t* dest_scan = dest_buf + row * dest_pitch; ++ pdfium::span src_span = ++ pSrcBitmap->GetScanline(src_top + row).subspan(src_left); ++ uint8_t* dest_scan = ++ dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data(); + for (int col = 0; col < width; ++col) { +- const uint8_t* src_port = src_scan + col * bpp; ++ const uint8_t* src_port = ++ src_span.subspan(Fx2DSizeOrDie(col, bpp)).data(); + int r = src_port[2] & 0xf0; + int g = src_port[1] & 0xf0; + int b = src_port[0] & 0xf0; +@@ -363,244 +332,183 @@ void ConvertBuffer_Rgb2PltRgb8(uint8_t* dest_buf, + } + } + } +- memcpy(dst_plt, pal, sizeof(uint32_t) * 256); ++ for (size_t i = 0; i < 256; ++i) ++ dst_plt[i] = pal[i]; + } + + void ConvertBuffer_1bppMask2Rgb(FXDIB_Format dest_format, +- uint8_t* dest_buf, ++ pdfium::span dest_buf, + int dest_pitch, + int width, + int height, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + int src_left, + int src_top) { + int comps = GetCompsFromFormat(dest_format); + static constexpr uint8_t kSetGray = 0xff; + static constexpr uint8_t kResetGray = 0x00; + for (int row = 0; row < height; ++row) { +- uint8_t* dest_scan = dest_buf + row * dest_pitch; +- const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row); ++ uint8_t* dest_scan = ++ dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data(); ++ const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row).data(); + for (int col = src_left; col < src_left + width; ++col) { +- if (src_scan[col / 8] & (1 << (7 - col % 8))) { +- dest_scan[0] = kSetGray; +- dest_scan[1] = kSetGray; +- dest_scan[2] = kSetGray; +- } else { +- dest_scan[0] = kResetGray; +- dest_scan[1] = kResetGray; +- dest_scan[2] = kResetGray; +- } ++ uint8_t value = ++ (src_scan[col / 8] & (1 << (7 - col % 8))) ? kSetGray : kResetGray; ++ memset(dest_scan, value, 3); + dest_scan += comps; + } + } + } + + void ConvertBuffer_8bppMask2Rgb(FXDIB_Format dest_format, +- uint8_t* dest_buf, ++ pdfium::span dest_buf, + int dest_pitch, + int width, + int height, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + int src_left, + int src_top) { + int comps = GetCompsFromFormat(dest_format); + for (int row = 0; row < height; ++row) { +- uint8_t* dest_scan = dest_buf + row * dest_pitch; +- const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left; +- uint8_t src_pixel; ++ uint8_t* dest_scan = ++ dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data(); ++ const uint8_t* src_scan = ++ pSrcBitmap->GetScanline(src_top + row).subspan(src_left).data(); + for (int col = 0; col < width; ++col) { +- src_pixel = *src_scan++; +- *dest_scan++ = src_pixel; +- *dest_scan++ = src_pixel; +- *dest_scan = src_pixel; +- dest_scan += comps - 2; ++ memset(dest_scan, *src_scan, 3); ++ dest_scan += comps; ++ ++src_scan; + } + } + } + + void ConvertBuffer_1bppPlt2Rgb(FXDIB_Format dest_format, +- uint8_t* dest_buf, ++ pdfium::span dest_buf, + int dest_pitch, + int width, + int height, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + int src_left, + int src_top) { ++ pdfium::span src_palette = pSrcBitmap->GetPaletteSpan(); ++ const uint8_t dst_palette[6] = { ++ FXARGB_B(src_palette[0]), FXARGB_G(src_palette[0]), ++ FXARGB_R(src_palette[0]), FXARGB_B(src_palette[1]), ++ FXARGB_G(src_palette[1]), FXARGB_R(src_palette[1])}; + int comps = GetCompsFromFormat(dest_format); +- uint32_t* src_plt = pSrcBitmap->GetPalette(); +- uint32_t plt[2]; +- uint8_t* bgr_ptr = reinterpret_cast(plt); +- if (pSrcBitmap->IsCmykImage()) { +- plt[0] = FXCMYK_TODIB(src_plt[0]); +- plt[1] = FXCMYK_TODIB(src_plt[1]); +- } else { +- bgr_ptr[0] = FXARGB_B(src_plt[0]); +- bgr_ptr[1] = FXARGB_G(src_plt[0]); +- bgr_ptr[2] = FXARGB_R(src_plt[0]); +- bgr_ptr[3] = FXARGB_B(src_plt[1]); +- bgr_ptr[4] = FXARGB_G(src_plt[1]); +- bgr_ptr[5] = FXARGB_R(src_plt[1]); +- } +- +- if (pSrcBitmap->IsCmykImage()) { +- std::tie(bgr_ptr[2], bgr_ptr[1], bgr_ptr[0]) = AdobeCMYK_to_sRGB1( +- FXSYS_GetCValue(src_plt[0]), FXSYS_GetMValue(src_plt[0]), +- FXSYS_GetYValue(src_plt[0]), FXSYS_GetKValue(src_plt[0])); +- std::tie(bgr_ptr[5], bgr_ptr[4], bgr_ptr[3]) = AdobeCMYK_to_sRGB1( +- FXSYS_GetCValue(src_plt[1]), FXSYS_GetMValue(src_plt[1]), +- FXSYS_GetYValue(src_plt[1]), FXSYS_GetKValue(src_plt[1])); +- } +- + for (int row = 0; row < height; ++row) { +- uint8_t* dest_scan = dest_buf + row * dest_pitch; +- const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row); ++ uint8_t* dest_scan = ++ dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data(); ++ const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row).data(); + for (int col = src_left; col < src_left + width; ++col) { +- if (src_scan[col / 8] & (1 << (7 - col % 8))) { +- *dest_scan++ = bgr_ptr[3]; +- *dest_scan++ = bgr_ptr[4]; +- *dest_scan = bgr_ptr[5]; +- } else { +- *dest_scan++ = bgr_ptr[0]; +- *dest_scan++ = bgr_ptr[1]; +- *dest_scan = bgr_ptr[2]; +- } +- dest_scan += comps - 2; ++ size_t offset = (src_scan[col / 8] & (1 << (7 - col % 8))) ? 3 : 0; ++ memcpy(dest_scan, dst_palette + offset, 3); ++ dest_scan += comps; + } + } + } + + void ConvertBuffer_8bppPlt2Rgb(FXDIB_Format dest_format, +- uint8_t* dest_buf, ++ pdfium::span dest_buf, + int dest_pitch, + int width, + int height, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + int src_left, + int src_top) { +- int comps = GetCompsFromFormat(dest_format); +- uint32_t* src_plt = pSrcBitmap->GetPalette(); +- uint32_t plt[256]; +- uint8_t* bgr_ptr = reinterpret_cast(plt); +- if (!pSrcBitmap->IsCmykImage()) { +- for (int i = 0; i < 256; ++i) { +- *bgr_ptr++ = FXARGB_B(src_plt[i]); +- *bgr_ptr++ = FXARGB_G(src_plt[i]); +- *bgr_ptr++ = FXARGB_R(src_plt[i]); +- } +- bgr_ptr = reinterpret_cast(plt); ++ pdfium::span src_palette = pSrcBitmap->GetPaletteSpan(); ++ uint8_t dst_palette[768]; ++ for (int i = 0; i < 256; ++i) { ++ dst_palette[3 * i] = FXARGB_B(src_palette[i]); ++ dst_palette[3 * i + 1] = FXARGB_G(src_palette[i]); ++ dst_palette[3 * i + 2] = FXARGB_R(src_palette[i]); + } +- +- if (pSrcBitmap->IsCmykImage()) { +- for (int i = 0; i < 256; ++i) { +- std::tie(bgr_ptr[2], bgr_ptr[1], bgr_ptr[0]) = AdobeCMYK_to_sRGB1( +- FXSYS_GetCValue(src_plt[i]), FXSYS_GetMValue(src_plt[i]), +- FXSYS_GetYValue(src_plt[i]), FXSYS_GetKValue(src_plt[i])); +- bgr_ptr += 3; +- } +- bgr_ptr = reinterpret_cast(plt); +- } +- ++ int comps = GetCompsFromFormat(dest_format); + for (int row = 0; row < height; ++row) { +- uint8_t* dest_scan = dest_buf + row * dest_pitch; +- const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row) + src_left; ++ uint8_t* dest_scan = ++ dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data(); ++ const uint8_t* src_scan = ++ pSrcBitmap->GetScanline(src_top + row).subspan(src_left).data(); + for (int col = 0; col < width; ++col) { +- uint8_t* src_pixel = bgr_ptr + 3 * (*src_scan++); +- *dest_scan++ = *src_pixel++; +- *dest_scan++ = *src_pixel++; +- *dest_scan = *src_pixel++; +- dest_scan += comps - 2; ++ uint8_t* src_pixel = dst_palette + 3 * (*src_scan++); ++ memcpy(dest_scan, src_pixel, 3); ++ dest_scan += comps; + } + } + } + +-void ConvertBuffer_24bppRgb2Rgb24(uint8_t* dest_buf, +- int dest_pitch, +- int width, +- int height, +- const RetainPtr& pSrcBitmap, +- int src_left, +- int src_top) { ++void ConvertBuffer_24bppRgb2Rgb24( ++ pdfium::span dest_buf, ++ int dest_pitch, ++ int width, ++ int height, ++ const RetainPtr& pSrcBitmap, ++ int src_left, ++ int src_top) { ++ const size_t x_offset = Fx2DSizeOrDie(src_left, 3); ++ const size_t byte_count = Fx2DSizeOrDie(width, 3); + for (int row = 0; row < height; ++row) { +- uint8_t* dest_scan = dest_buf + row * dest_pitch; +- const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left * 3; +- memcpy(dest_scan, src_scan, width * 3); ++ fxcrt::spancpy( ++ dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)), ++ pSrcBitmap->GetScanline(src_top + row).subspan(x_offset, byte_count)); + } + } + +-void ConvertBuffer_32bppRgb2Rgb24(uint8_t* dest_buf, +- int dest_pitch, +- int width, +- int height, +- const RetainPtr& pSrcBitmap, +- int src_left, +- int src_top) { ++void ConvertBuffer_32bppRgb2Rgb24( ++ pdfium::span dest_buf, ++ int dest_pitch, ++ int width, ++ int height, ++ const RetainPtr& pSrcBitmap, ++ int src_left, ++ int src_top) { ++ const size_t x_offset = Fx2DSizeOrDie(src_left, 4); + for (int row = 0; row < height; ++row) { +- uint8_t* dest_scan = dest_buf + row * dest_pitch; ++ uint8_t* dest_scan = ++ dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data(); + const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left * 4; ++ pSrcBitmap->GetScanline(src_top + row).subspan(x_offset).data(); + for (int col = 0; col < width; ++col) { +- *dest_scan++ = *src_scan++; +- *dest_scan++ = *src_scan++; +- *dest_scan++ = *src_scan++; +- ++src_scan; ++ memcpy(dest_scan, src_scan, 3); ++ dest_scan += 3; ++ src_scan += 4; + } + } + } + +-void ConvertBuffer_Rgb2Rgb32(uint8_t* dest_buf, ++void ConvertBuffer_Rgb2Rgb32(pdfium::span dest_buf, + int dest_pitch, + int width, + int height, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + int src_left, + int src_top) { +- int comps = pSrcBitmap->GetBPP() / 8; ++ const int comps = pSrcBitmap->GetBPP() / 8; ++ const size_t x_offset = Fx2DSizeOrDie(src_left, comps); + for (int row = 0; row < height; ++row) { +- uint8_t* dest_scan = dest_buf + row * dest_pitch; +- const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left * comps; +- for (int col = 0; col < width; ++col) { +- *dest_scan++ = *src_scan++; +- *dest_scan++ = *src_scan++; +- *dest_scan++ = *src_scan++; +- ++dest_scan; +- src_scan += comps - 3; +- } +- } +-} +- +-void ConvertBuffer_32bppCmyk2Rgb32(uint8_t* dest_buf, +- int dest_pitch, +- int width, +- int height, +- const RetainPtr& pSrcBitmap, +- int src_left, +- int src_top) { +- for (int row = 0; row < height; ++row) { +- uint8_t* dest_scan = dest_buf + row * dest_pitch; ++ uint8_t* dest_scan = ++ dest_buf.subspan(Fx2DSizeOrDie(row, dest_pitch)).data(); + const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left * 4; ++ pSrcBitmap->GetScanline(src_top + row).subspan(x_offset).data(); + for (int col = 0; col < width; ++col) { +- std::tie(dest_scan[2], dest_scan[1], dest_scan[0]) = AdobeCMYK_to_sRGB1( +- src_scan[0], src_scan[1], src_scan[2], src_scan[3]); ++ memcpy(dest_scan, src_scan, 3); + dest_scan += 4; +- src_scan += 4; ++ src_scan += comps; + } + } + } + + bool ConvertBuffer_8bppMask(int bpp, +- uint8_t* dest_buf, ++ pdfium::span dest_buf, + int dest_pitch, + int width, + int height, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + int src_left, + int src_top) { + switch (bpp) { + case 1: +- if (pSrcBitmap->GetPalette()) { ++ if (pSrcBitmap->HasPalette()) { + ConvertBuffer_1bppPlt2Gray(dest_buf, dest_pitch, width, height, + pSrcBitmap, src_left, src_top); + } else { +@@ -609,7 +517,7 @@ bool ConvertBuffer_8bppMask(int bpp, + } + return true; + case 8: +- if (pSrcBitmap->GetPalette()) { ++ if (pSrcBitmap->HasPalette()) { + ConvertBuffer_8bppPlt2Gray(dest_buf, dest_pitch, width, height, + pSrcBitmap, src_left, src_top); + } else { +@@ -619,8 +527,8 @@ bool ConvertBuffer_8bppMask(int bpp, + return true; + case 24: + case 32: +- ConvertBuffer_RgbOrCmyk2Gray(dest_buf, dest_pitch, width, height, +- pSrcBitmap, src_left, src_top); ++ ConvertBuffer_Rgb2Gray(dest_buf, dest_pitch, width, height, pSrcBitmap, ++ src_left, src_top); + return true; + default: + return false; +@@ -629,16 +537,16 @@ bool ConvertBuffer_8bppMask(int bpp, + + bool ConvertBuffer_Rgb(int bpp, + FXDIB_Format dest_format, +- uint8_t* dest_buf, ++ pdfium::span dest_buf, + int dest_pitch, + int width, + int height, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + int src_left, + int src_top) { + switch (bpp) { + case 1: +- if (pSrcBitmap->GetPalette()) { ++ if (pSrcBitmap->HasPalette()) { + ConvertBuffer_1bppPlt2Rgb(dest_format, dest_buf, dest_pitch, width, + height, pSrcBitmap, src_left, src_top); + } else { +@@ -647,7 +555,7 @@ bool ConvertBuffer_Rgb(int bpp, + } + return true; + case 8: +- if (pSrcBitmap->GetPalette()) { ++ if (pSrcBitmap->HasPalette()) { + ConvertBuffer_8bppPlt2Rgb(dest_format, dest_buf, dest_pitch, width, + height, pSrcBitmap, src_left, src_top); + } else { +@@ -669,18 +577,17 @@ bool ConvertBuffer_Rgb(int bpp, + } + + bool ConvertBuffer_Argb(int bpp, +- bool cmyk, + FXDIB_Format dest_format, +- uint8_t* dest_buf, ++ pdfium::span dest_buf, + int dest_pitch, + int width, + int height, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + int src_left, + int src_top) { + switch (bpp) { + case 1: +- if (pSrcBitmap->GetPalette()) { ++ if (pSrcBitmap->HasPalette()) { + ConvertBuffer_1bppPlt2Rgb(dest_format, dest_buf, dest_pitch, width, + height, pSrcBitmap, src_left, src_top); + } else { +@@ -689,7 +596,7 @@ bool ConvertBuffer_Argb(int bpp, + } + return true; + case 8: +- if (pSrcBitmap->GetPalette()) { ++ if (pSrcBitmap->HasPalette()) { + ConvertBuffer_8bppPlt2Rgb(dest_format, dest_buf, dest_pitch, width, + height, pSrcBitmap, src_left, src_top); + } else { +@@ -699,13 +606,8 @@ bool ConvertBuffer_Argb(int bpp, + return true; + case 24: + case 32: +- if (cmyk) { +- ConvertBuffer_32bppCmyk2Rgb32(dest_buf, dest_pitch, width, height, +- pSrcBitmap, src_left, src_top); +- } else { +- ConvertBuffer_Rgb2Rgb32(dest_buf, dest_pitch, width, height, pSrcBitmap, +- src_left, src_top); +- } ++ ConvertBuffer_Rgb2Rgb32(dest_buf, dest_pitch, width, height, pSrcBitmap, ++ src_left, src_top); + return true; + default: + return false; +@@ -714,20 +616,32 @@ bool ConvertBuffer_Argb(int bpp, + + } // namespace + +-CFX_DIBBase::CFX_DIBBase() +- : m_Width(0), m_Height(0), m_bpp(0), m_AlphaFlag(0), m_Pitch(0) {} ++CFX_DIBBase::CFX_DIBBase() = default; + +-CFX_DIBBase::~CFX_DIBBase() {} ++CFX_DIBBase::~CFX_DIBBase() = default; + +-uint8_t* CFX_DIBBase::GetBuffer() const { +- return nullptr; ++pdfium::span CFX_DIBBase::GetBuffer() const { ++ return pdfium::span(); + } + + bool CFX_DIBBase::SkipToScanline(int line, PauseIndicatorIface* pPause) const { + return false; + } + +-RetainPtr CFX_DIBBase::Clone(const FX_RECT* pClip) const { ++size_t CFX_DIBBase::GetEstimatedImageMemoryBurden() const { ++ return GetRequiredPaletteSize() * sizeof(uint32_t); ++} ++ ++RetainPtr CFX_DIBBase::Realize() const { ++ return ClipToInternal(nullptr); ++} ++ ++RetainPtr CFX_DIBBase::ClipTo(const FX_RECT& rect) const { ++ return ClipToInternal(&rect); ++} ++ ++RetainPtr CFX_DIBBase::ClipToInternal( ++ const FX_RECT* pClip) const { + FX_RECT rect(0, 0, m_Width, m_Height); + if (pClip) { + rect.Intersect(*pClip); +@@ -738,80 +652,67 @@ RetainPtr CFX_DIBBase::Clone(const FX_RECT* pClip) const { + if (!pNewBitmap->Create(rect.Width(), rect.Height(), GetFormat())) + return nullptr; + +- pNewBitmap->SetPalette(m_pPalette.get()); +- pNewBitmap->SetAlphaMask(m_pAlphaMask, pClip); ++ pNewBitmap->SetPalette(GetPaletteSpan()); + if (GetBPP() == 1 && rect.left % 8 != 0) { + int left_shift = rect.left % 32; + int right_shift = 32 - left_shift; + int dword_count = pNewBitmap->m_Pitch / 4; + for (int row = rect.top; row < rect.bottom; ++row) { + const uint32_t* src_scan = +- reinterpret_cast(GetScanline(row)) + rect.left / 32; ++ reinterpret_cast(GetScanline(row).data()) + ++ rect.left / 32; + uint32_t* dest_scan = reinterpret_cast( +- pNewBitmap->GetWritableScanline(row - rect.top)); ++ pNewBitmap->GetWritableScanline(row - rect.top).data()); + for (int i = 0; i < dword_count; ++i) { + dest_scan[i] = + (src_scan[i] << left_shift) | (src_scan[i + 1] >> right_shift); + } + } + } else { +- int copy_len = (pNewBitmap->GetWidth() * pNewBitmap->GetBPP() + 7) / 8; +- if (m_Pitch < static_cast(copy_len)) +- copy_len = m_Pitch; ++ FX_SAFE_UINT32 copy_len = pNewBitmap->GetWidth(); ++ copy_len *= pNewBitmap->GetBPP(); ++ copy_len += 7; ++ copy_len /= 8; ++ if (!copy_len.IsValid()) ++ return nullptr; ++ ++ copy_len = std::min(m_Pitch, copy_len.ValueOrDie()); ++ ++ FX_SAFE_UINT32 offset = rect.left; ++ offset *= GetBppFromFormat(m_Format); ++ offset /= 8; ++ if (!offset.IsValid()) ++ return nullptr; + + for (int row = rect.top; row < rect.bottom; ++row) { +- const uint8_t* src_scan = GetScanline(row) + rect.left * m_bpp / 8; +- uint8_t* dest_scan = pNewBitmap->GetWritableScanline(row - rect.top); +- memcpy(dest_scan, src_scan, copy_len); ++ const uint8_t* src_scan = ++ GetScanline(row).subspan(offset.ValueOrDie()).data(); ++ uint8_t* dest_scan = ++ pNewBitmap->GetWritableScanline(row - rect.top).data(); ++ memcpy(dest_scan, src_scan, copy_len.ValueOrDie()); + } + } + return pNewBitmap; + } + + void CFX_DIBBase::BuildPalette() { +- if (m_pPalette) ++ if (HasPalette()) + return; + + if (GetBPP() == 1) { +- m_pPalette.reset(FX_Alloc(uint32_t, 2)); +- if (IsCmykImage()) { +- m_pPalette.get()[0] = 0xff; +- m_pPalette.get()[1] = 0; +- } else { +- m_pPalette.get()[0] = 0xff000000; +- m_pPalette.get()[1] = 0xffffffff; +- } ++ m_palette = {0xff000000, 0xffffffff}; + } else if (GetBPP() == 8) { +- m_pPalette.reset(FX_Alloc(uint32_t, 256)); +- if (IsCmykImage()) { +- for (int i = 0; i < 256; ++i) +- m_pPalette.get()[i] = 0xff - i; +- } else { +- for (int i = 0; i < 256; ++i) +- m_pPalette.get()[i] = 0xff000000 | (i * 0x10101); +- } +- } +-} +- +-bool CFX_DIBBase::BuildAlphaMask() { +- if (m_pAlphaMask) +- return true; +- +- m_pAlphaMask = pdfium::MakeRetain(); +- if (!m_pAlphaMask->Create(m_Width, m_Height, FXDIB_8bppMask)) { +- m_pAlphaMask = nullptr; +- return false; ++ m_palette.resize(256); ++ for (int i = 0; i < 256; ++i) ++ m_palette[i] = ArgbEncode(0xff, i, i, i); + } +- memset(m_pAlphaMask->GetBuffer(), 0xff, +- m_pAlphaMask->GetHeight() * m_pAlphaMask->GetPitch()); +- return true; + } + +-size_t CFX_DIBBase::GetPaletteSize() const { +- if (IsAlphaMask()) ++size_t CFX_DIBBase::GetRequiredPaletteSize() const { ++ if (IsMaskFormat()) + return 0; + +- switch (m_bpp) { ++ switch (GetBppFromFormat(m_Format)) { + case 1: + return 2; + case 8: +@@ -822,50 +723,37 @@ size_t CFX_DIBBase::GetPaletteSize() const { + } + + uint32_t CFX_DIBBase::GetPaletteArgb(int index) const { +- ASSERT((GetBPP() == 1 || GetBPP() == 8) && !IsAlphaMask()); +- if (m_pPalette) +- return m_pPalette.get()[index]; +- +- if (IsCmykImage()) { +- if (GetBPP() == 1) +- return index ? 0 : 0xff; ++ DCHECK((GetBPP() == 1 || GetBPP() == 8) && !IsMaskFormat()); ++ if (HasPalette()) ++ return GetPaletteSpan()[index]; + +- return 0xff - index; +- } + if (GetBPP() == 1) + return index ? 0xffffffff : 0xff000000; + +- return index * 0x10101 | 0xff000000; ++ return ArgbEncode(0xff, index, index, index); + } + + void CFX_DIBBase::SetPaletteArgb(int index, uint32_t color) { +- ASSERT((GetBPP() == 1 || GetBPP() == 8) && !IsAlphaMask()); +- if (!m_pPalette) { +- BuildPalette(); +- } +- m_pPalette.get()[index] = color; ++ DCHECK((GetBPP() == 1 || GetBPP() == 8) && !IsMaskFormat()); ++ BuildPalette(); ++ m_palette[index] = color; + } + + int CFX_DIBBase::FindPalette(uint32_t color) const { +- ASSERT((GetBPP() == 1 || GetBPP() == 8) && !IsAlphaMask()); +- if (!m_pPalette) { +- if (IsCmykImage()) { +- if (GetBPP() == 1) +- return (static_cast(color) == 0xff) ? 0 : 1; +- +- return 0xff - static_cast(color); ++ DCHECK((GetBPP() == 1 || GetBPP() == 8) && !IsMaskFormat()); ++ if (HasPalette()) { ++ int palsize = (1 << GetBPP()); ++ pdfium::span palette = GetPaletteSpan(); ++ for (int i = 0; i < palsize; ++i) { ++ if (palette[i] == color) ++ return i; + } +- if (GetBPP() == 1) +- return (static_cast(color) == 0xff) ? 1 : 0; +- +- return static_cast(color); +- } +- int palsize = (1 << GetBPP()); +- for (int i = 0; i < palsize; ++i) { +- if (m_pPalette.get()[i] == color) +- return i; ++ return -1; + } +- return -1; ++ ++ if (GetBPP() == 1) ++ return (static_cast(color) == 0xff) ? 1 : 0; ++ return static_cast(color); + } + + bool CFX_DIBBase::GetOverlapRect(int& dest_left, +@@ -876,93 +764,115 @@ bool CFX_DIBBase::GetOverlapRect(int& dest_left, + int src_height, + int& src_left, + int& src_top, +- const CFX_ClipRgn* pClipRgn) { ++ const CFX_ClipRgn* pClipRgn) const { + if (width == 0 || height == 0) + return false; + +- ASSERT(width > 0); +- ASSERT(height > 0); ++ DCHECK_GT(width, 0); ++ DCHECK_GT(height, 0); + +- if (dest_left > m_Width || dest_top > m_Height) { +- width = 0; +- height = 0; ++ if (dest_left > m_Width || dest_top > m_Height) + return false; +- } +- int x_offset = dest_left - src_left; +- int y_offset = dest_top - src_top; +- FX_RECT src_rect(src_left, src_top, src_left + width, src_top + height); ++ ++ FX_SAFE_INT32 safe_src_width = src_left; ++ safe_src_width += width; ++ if (!safe_src_width.IsValid()) ++ return false; ++ ++ FX_SAFE_INT32 safe_src_height = src_top; ++ safe_src_height += height; ++ if (!safe_src_height.IsValid()) ++ return false; ++ ++ FX_RECT src_rect(src_left, src_top, safe_src_width.ValueOrDie(), ++ safe_src_height.ValueOrDie()); + FX_RECT src_bound(0, 0, src_width, src_height); + src_rect.Intersect(src_bound); +- FX_RECT dest_rect(src_rect.left + x_offset, src_rect.top + y_offset, +- src_rect.right + x_offset, src_rect.bottom + y_offset); ++ ++ FX_SAFE_INT32 safe_x_offset = dest_left; ++ safe_x_offset -= src_left; ++ if (!safe_x_offset.IsValid()) ++ return false; ++ ++ FX_SAFE_INT32 safe_y_offset = dest_top; ++ safe_y_offset -= src_top; ++ if (!safe_y_offset.IsValid()) ++ return false; ++ ++ FX_SAFE_INT32 safe_dest_left = safe_x_offset; ++ safe_dest_left += src_rect.left; ++ if (!safe_dest_left.IsValid()) ++ return false; ++ ++ FX_SAFE_INT32 safe_dest_top = safe_y_offset; ++ safe_dest_top += src_rect.top; ++ if (!safe_dest_top.IsValid()) ++ return false; ++ ++ FX_SAFE_INT32 safe_dest_right = safe_x_offset; ++ safe_dest_right += src_rect.right; ++ if (!safe_dest_right.IsValid()) ++ return false; ++ ++ FX_SAFE_INT32 safe_dest_bottom = safe_y_offset; ++ safe_dest_bottom += src_rect.bottom; ++ if (!safe_dest_bottom.IsValid()) ++ return false; ++ ++ FX_RECT dest_rect(safe_dest_left.ValueOrDie(), safe_dest_top.ValueOrDie(), ++ safe_dest_right.ValueOrDie(), ++ safe_dest_bottom.ValueOrDie()); + FX_RECT dest_bound(0, 0, m_Width, m_Height); + dest_rect.Intersect(dest_bound); ++ + if (pClipRgn) + dest_rect.Intersect(pClipRgn->GetBox()); + dest_left = dest_rect.left; + dest_top = dest_rect.top; + +- pdfium::base::CheckedNumeric safe_src_left = dest_left; +- safe_src_left -= x_offset; +- if (!safe_src_left.IsValid()) ++ FX_SAFE_INT32 safe_new_src_left = dest_left; ++ safe_new_src_left -= safe_x_offset; ++ if (!safe_new_src_left.IsValid()) ++ return false; ++ src_left = safe_new_src_left.ValueOrDie(); ++ ++ FX_SAFE_INT32 safe_new_src_top = dest_top; ++ safe_new_src_top -= safe_y_offset; ++ if (!safe_new_src_top.IsValid()) + return false; +- src_left = safe_src_left.ValueOrDie(); ++ src_top = safe_new_src_top.ValueOrDie(); + +- pdfium::base::CheckedNumeric safe_src_top = dest_top; +- safe_src_top -= y_offset; +- if (!safe_src_top.IsValid()) ++ if (dest_rect.IsEmpty()) + return false; +- src_top = safe_src_top.ValueOrDie(); + +- width = dest_rect.right - dest_rect.left; +- height = dest_rect.bottom - dest_rect.top; +- return width != 0 && height != 0; ++ width = dest_rect.Width(); ++ height = dest_rect.Height(); ++ return true; + } + +-void CFX_DIBBase::SetPalette(const uint32_t* pSrc) { +- static const uint32_t kPaletteSize = 256; +- if (!pSrc || GetBPP() > 8) { +- m_pPalette.reset(); ++void CFX_DIBBase::SetPalette(pdfium::span src_palette) { ++ if (src_palette.empty() || GetBPP() > 8) { ++ m_palette.clear(); + return; + } + uint32_t pal_size = 1 << GetBPP(); +- if (!m_pPalette) +- m_pPalette.reset(FX_Alloc(uint32_t, pal_size)); ++ if (m_palette.empty()) ++ m_palette.resize(pal_size); + pal_size = std::min(pal_size, kPaletteSize); +- memcpy(m_pPalette.get(), pSrc, pal_size * sizeof(uint32_t)); +-} +- +-void CFX_DIBBase::GetPalette(uint32_t* pal, int alpha) const { +- ASSERT(GetBPP() <= 8); +- ASSERT(!IsCmykImage()); +- +- if (GetBPP() == 1) { +- pal[0] = ((m_pPalette ? m_pPalette.get()[0] : 0xff000000) & 0xffffff) | +- (alpha << 24); +- pal[1] = ((m_pPalette ? m_pPalette.get()[1] : 0xffffffff) & 0xffffff) | +- (alpha << 24); +- return; +- } +- if (m_pPalette) { +- for (int i = 0; i < 256; ++i) +- pal[i] = (m_pPalette.get()[i] & 0x00ffffff) | (alpha << 24); +- } else { +- for (int i = 0; i < 256; ++i) +- pal[i] = (i * 0x10101) | (alpha << 24); +- } ++ for (size_t i = 0; i < pal_size; ++i) ++ m_palette[i] = src_palette[i]; + } + + RetainPtr CFX_DIBBase::CloneAlphaMask() const { +- ASSERT(GetFormat() == FXDIB_Argb); +- FX_RECT rect(0, 0, m_Width, m_Height); ++ DCHECK_EQ(GetFormat(), FXDIB_Format::kArgb); + auto pMask = pdfium::MakeRetain(); +- if (!pMask->Create(rect.Width(), rect.Height(), FXDIB_8bppMask)) ++ if (!pMask->Create(m_Width, m_Height, FXDIB_Format::k8bppMask)) + return nullptr; + +- for (int row = rect.top; row < rect.bottom; ++row) { +- const uint8_t* src_scan = GetScanline(row) + rect.left * 4 + 3; +- uint8_t* dest_scan = pMask->GetWritableScanline(row - rect.top); +- for (int col = rect.left; col < rect.right; ++col) { ++ for (int row = 0; row < m_Height; ++row) { ++ const uint8_t* src_scan = GetScanline(row).subspan(3).data(); ++ uint8_t* dest_scan = pMask->GetWritableScanline(row).data(); ++ for (int col = 0; col < m_Width; ++col) { + *dest_scan++ = *src_scan; + src_scan += 4; + } +@@ -970,51 +880,22 @@ RetainPtr CFX_DIBBase::CloneAlphaMask() const { + return pMask; + } + +-bool CFX_DIBBase::SetAlphaMask(const RetainPtr& pAlphaMask, +- const FX_RECT* pClip) { +- if (!HasAlpha() || GetFormat() == FXDIB_Argb) +- return false; +- +- if (!pAlphaMask) { +- m_pAlphaMask->Clear(0xff000000); +- return true; +- } +- FX_RECT rect(0, 0, pAlphaMask->m_Width, pAlphaMask->m_Height); +- if (pClip) { +- rect.Intersect(*pClip); +- if (rect.IsEmpty() || rect.Width() != m_Width || +- rect.Height() != m_Height) { +- return false; +- } +- } else { +- if (pAlphaMask->m_Width != m_Width || pAlphaMask->m_Height != m_Height) +- return false; +- } +- for (int row = 0; row < m_Height; ++row) { +- memcpy(m_pAlphaMask->GetWritableScanline(row), +- pAlphaMask->GetScanline(row + rect.top) + rect.left, +- m_pAlphaMask->m_Pitch); +- } +- return true; +-} +- + RetainPtr CFX_DIBBase::FlipImage(bool bXFlip, bool bYFlip) const { + auto pFlipped = pdfium::MakeRetain(); + if (!pFlipped->Create(m_Width, m_Height, GetFormat())) + return nullptr; + +- pFlipped->SetPalette(m_pPalette.get()); +- uint8_t* pDestBuffer = pFlipped->GetBuffer(); +- int Bpp = m_bpp / 8; ++ pFlipped->SetPalette(GetPaletteSpan()); ++ int Bpp = GetBppFromFormat(m_Format) / 8; + for (int row = 0; row < m_Height; ++row) { +- const uint8_t* src_scan = GetScanline(row); ++ const uint8_t* src_scan = GetScanline(row).data(); + uint8_t* dest_scan = +- pDestBuffer + m_Pitch * (bYFlip ? (m_Height - row - 1) : row); ++ pFlipped->GetWritableScanline(bYFlip ? m_Height - row - 1 : row).data(); + if (!bXFlip) { + memcpy(dest_scan, src_scan, m_Pitch); + continue; + } +- if (m_bpp == 1) { ++ if (GetBppFromFormat(m_Format) == 1) { + memset(dest_scan, 0, m_Pitch); + for (int col = 0; col < m_Width; ++col) { + if (src_scan[col / 8] & (1 << (7 - col % 8))) { +@@ -1034,14 +915,12 @@ RetainPtr CFX_DIBBase::FlipImage(bool bXFlip, bool bYFlip) const { + } + } else if (Bpp == 3) { + for (int col = 0; col < m_Width; ++col) { +- dest_scan[0] = src_scan[0]; +- dest_scan[1] = src_scan[1]; +- dest_scan[2] = src_scan[2]; ++ memcpy(dest_scan, src_scan, 3); + dest_scan -= 3; + src_scan += 3; + } + } else { +- ASSERT(Bpp == 4); ++ DCHECK_EQ(Bpp, 4); + for (int col = 0; col < m_Width; ++col) { + const auto* src_scan32 = reinterpret_cast(src_scan); + uint32_t* dest_scan32 = reinterpret_cast(dest_scan); +@@ -1051,62 +930,38 @@ RetainPtr CFX_DIBBase::FlipImage(bool bXFlip, bool bYFlip) const { + } + } + } +- if (m_pAlphaMask) { +- pDestBuffer = pFlipped->m_pAlphaMask->GetBuffer(); +- uint32_t dest_pitch = pFlipped->m_pAlphaMask->GetPitch(); +- for (int row = 0; row < m_Height; ++row) { +- const uint8_t* src_scan = m_pAlphaMask->GetScanline(row); +- uint8_t* dest_scan = +- pDestBuffer + dest_pitch * (bYFlip ? (m_Height - row - 1) : row); +- if (!bXFlip) { +- memcpy(dest_scan, src_scan, dest_pitch); +- continue; +- } +- dest_scan += (m_Width - 1); +- for (int col = 0; col < m_Width; ++col) { +- *dest_scan = *src_scan; +- --dest_scan; +- ++src_scan; +- } +- } +- } + return pFlipped; + } + +-RetainPtr CFX_DIBBase::CloneConvert(FXDIB_Format dest_format) { ++RetainPtr CFX_DIBBase::ConvertTo(FXDIB_Format dest_format) const { + if (dest_format == GetFormat()) +- return Clone(nullptr); ++ return Realize(); + + auto pClone = pdfium::MakeRetain(); + if (!pClone->Create(m_Width, m_Height, dest_format)) + return nullptr; + + RetainPtr pSrcAlpha; +- if (HasAlpha()) { +- pSrcAlpha = (GetFormat() == FXDIB_Argb) ? CloneAlphaMask() : m_pAlphaMask; ++ if (IsAlphaFormat()) { ++ pSrcAlpha = CloneAlphaMask(); + if (!pSrcAlpha) + return nullptr; + } +- if (GetIsAlphaFromFormat(dest_format)) { +- bool ret; +- if (dest_format == FXDIB_Argb) { +- ret = pSrcAlpha ? pClone->LoadChannelFromAlpha(FXDIB_Alpha, pSrcAlpha) +- : pClone->LoadChannel(FXDIB_Alpha, 0xff); +- } else { +- ret = pClone->SetAlphaMask(pSrcAlpha, nullptr); +- } ++ if (dest_format == FXDIB_Format::kArgb) { ++ bool ret = pSrcAlpha ? pClone->SetAlphaFromBitmap(pSrcAlpha) ++ : pClone->SetUniformOpaqueAlpha(); + if (!ret) + return nullptr; + } + +- RetainPtr holder(this); +- std::unique_ptr pal_8bpp; ++ RetainPtr holder(this); ++ DataVector pal_8bpp; + if (!ConvertBuffer(dest_format, pClone->GetBuffer(), pClone->GetPitch(), + m_Width, m_Height, holder, 0, 0, &pal_8bpp)) { + return nullptr; + } +- if (pal_8bpp) +- pClone->SetPalette(pal_8bpp.get()); ++ if (!pal_8bpp.empty()) ++ pClone->SetPalette(pal_8bpp); + + return pClone; + } +@@ -1117,28 +972,31 @@ RetainPtr CFX_DIBBase::SwapXY(bool bXFlip, bool bYFlip) const { + return nullptr; + + auto pTransBitmap = pdfium::MakeRetain(); +- int result_height = dest_clip.Height(); +- int result_width = dest_clip.Width(); ++ const int result_height = dest_clip.Height(); ++ const int result_width = dest_clip.Width(); + if (!pTransBitmap->Create(result_width, result_height, GetFormat())) + return nullptr; + +- pTransBitmap->SetPalette(m_pPalette.get()); +- int dest_pitch = pTransBitmap->GetPitch(); +- uint8_t* dest_buf = pTransBitmap->GetBuffer(); +- int row_start = bXFlip ? m_Height - dest_clip.right : dest_clip.left; +- int row_end = bXFlip ? m_Height - dest_clip.left : dest_clip.right; +- int col_start = bYFlip ? m_Width - dest_clip.bottom : dest_clip.top; +- int col_end = bYFlip ? m_Width - dest_clip.top : dest_clip.bottom; ++ pTransBitmap->SetPalette(GetPaletteSpan()); ++ const int dest_pitch = pTransBitmap->GetPitch(); ++ pdfium::span dest_span = ++ pTransBitmap->GetBuffer().first(Fx2DSizeOrDie(dest_pitch, result_height)); ++ const size_t dest_last_row_offset = ++ Fx2DSizeOrDie(dest_pitch, result_height - 1); ++ const int row_start = bXFlip ? m_Height - dest_clip.right : dest_clip.left; ++ const int row_end = bXFlip ? m_Height - dest_clip.left : dest_clip.right; ++ const int col_start = bYFlip ? m_Width - dest_clip.bottom : dest_clip.top; ++ const int col_end = bYFlip ? m_Width - dest_clip.top : dest_clip.bottom; + if (GetBPP() == 1) { +- memset(dest_buf, 0xff, dest_pitch * result_height); ++ fxcrt::spanset(dest_span, 0xff); ++ if (bYFlip) ++ dest_span = dest_span.subspan(dest_last_row_offset); ++ const int dest_step = bYFlip ? -dest_pitch : dest_pitch; + for (int row = row_start; row < row_end; ++row) { +- const uint8_t* src_scan = GetScanline(row); ++ const uint8_t* src_scan = GetScanline(row).data(); + int dest_col = (bXFlip ? dest_clip.right - (row - row_start) - 1 : row) - + dest_clip.left; +- uint8_t* dest_scan = dest_buf; +- if (bYFlip) +- dest_scan += (result_height - 1) * dest_pitch; +- int dest_step = bYFlip ? -dest_pitch : dest_pitch; ++ uint8_t* dest_scan = dest_span.data(); + for (int col = col_start; col < col_end; ++col) { + if (!(src_scan[col / 8] & (1 << (7 - col % 8)))) + dest_scan[dest_col / 8] &= ~(1 << (7 - dest_col % 8)); +@@ -1150,22 +1008,25 @@ RetainPtr CFX_DIBBase::SwapXY(bool bXFlip, bool bYFlip) const { + int dest_step = bYFlip ? -dest_pitch : dest_pitch; + if (nBytes == 3) + dest_step -= 2; ++ if (bYFlip) ++ dest_span = dest_span.subspan(dest_last_row_offset); + for (int row = row_start; row < row_end; ++row) { + int dest_col = (bXFlip ? dest_clip.right - (row - row_start) - 1 : row) - + dest_clip.left; +- uint8_t* dest_scan = dest_buf + dest_col * nBytes; +- if (bYFlip) +- dest_scan += (result_height - 1) * dest_pitch; ++ size_t dest_offset = Fx2DSizeOrDie(dest_col, nBytes); ++ uint8_t* dest_scan = dest_span.subspan(dest_offset).data(); + if (nBytes == 4) { + const uint32_t* src_scan = +- reinterpret_cast(GetScanline(row)) + col_start; ++ reinterpret_cast(GetScanline(row).data()) + ++ col_start; + for (int col = col_start; col < col_end; ++col) { + uint32_t* dest_scan32 = reinterpret_cast(dest_scan); + *dest_scan32 = *src_scan++; + dest_scan += dest_step; + } + } else { +- const uint8_t* src_scan = GetScanline(row) + col_start * nBytes; ++ const uint8_t* src_scan = ++ GetScanline(row).subspan(col_start * nBytes).data(); + if (nBytes == 1) { + for (int col = col_start; col < col_end; ++col) { + *dest_scan = *src_scan++; +@@ -1173,39 +1034,21 @@ RetainPtr CFX_DIBBase::SwapXY(bool bXFlip, bool bYFlip) const { + } + } else { + for (int col = col_start; col < col_end; ++col) { +- *dest_scan++ = *src_scan++; +- *dest_scan++ = *src_scan++; +- *dest_scan = *src_scan++; +- dest_scan += dest_step; ++ memcpy(dest_scan, src_scan, 3); ++ dest_scan += 2 + dest_step; ++ src_scan += 3; + } + } + } + } + } +- if (m_pAlphaMask) { +- dest_pitch = pTransBitmap->m_pAlphaMask->GetPitch(); +- dest_buf = pTransBitmap->m_pAlphaMask->GetBuffer(); +- int dest_step = bYFlip ? -dest_pitch : dest_pitch; +- for (int row = row_start; row < row_end; ++row) { +- int dest_col = (bXFlip ? dest_clip.right - (row - row_start) - 1 : row) - +- dest_clip.left; +- uint8_t* dest_scan = dest_buf + dest_col; +- if (bYFlip) +- dest_scan += (result_height - 1) * dest_pitch; +- const uint8_t* src_scan = m_pAlphaMask->GetScanline(row) + col_start; +- for (int col = col_start; col < col_end; ++col) { +- *dest_scan = *src_scan++; +- dest_scan += dest_step; +- } +- } +- } + return pTransBitmap; + } + + RetainPtr CFX_DIBBase::TransformTo(const CFX_Matrix& mtDest, + int* result_left, +- int* result_top) { +- RetainPtr holder(this); ++ int* result_top) const { ++ RetainPtr holder(this); + CFX_ImageTransformer transformer(holder, mtDest, FXDIB_ResampleOptions(), + nullptr); + transformer.Continue(nullptr); +@@ -1218,8 +1061,8 @@ RetainPtr CFX_DIBBase::StretchTo( + int dest_width, + int dest_height, + const FXDIB_ResampleOptions& options, +- const FX_RECT* pClip) { +- RetainPtr holder(this); ++ const FX_RECT* pClip) const { ++ RetainPtr holder(this); + FX_RECT clip_rect(0, 0, abs(dest_width), abs(dest_height)); + if (pClip) + clip_rect.Intersect(*pClip); +@@ -1228,7 +1071,7 @@ RetainPtr CFX_DIBBase::StretchTo( + return nullptr; + + if (dest_width == m_Width && dest_height == m_Height) +- return Clone(&clip_rect); ++ return ClipTo(clip_rect); + + CFX_BitmapStorer storer; + CFX_ImageStretcher stretcher(&storer, holder, dest_width, dest_height, +@@ -1240,52 +1083,48 @@ RetainPtr CFX_DIBBase::StretchTo( + } + + // static +-bool CFX_DIBBase::ConvertBuffer( +- FXDIB_Format dest_format, +- uint8_t* dest_buf, +- int dest_pitch, +- int width, +- int height, +- const RetainPtr& pSrcBitmap, +- int src_left, +- int src_top, +- std::unique_ptr* p_pal) { ++bool CFX_DIBBase::ConvertBuffer(FXDIB_Format dest_format, ++ pdfium::span dest_buf, ++ int dest_pitch, ++ int width, ++ int height, ++ const RetainPtr& pSrcBitmap, ++ int src_left, ++ int src_top, ++ DataVector* pal) { + FXDIB_Format src_format = pSrcBitmap->GetFormat(); + const int bpp = GetBppFromFormat(src_format); + switch (dest_format) { +- case FXDIB_8bppMask: { ++ case FXDIB_Format::k8bppMask: { + return ConvertBuffer_8bppMask(bpp, dest_buf, dest_pitch, width, height, + pSrcBitmap, src_left, src_top); + } +- case FXDIB_8bppRgb: +- case FXDIB_8bppRgba: { ++ case FXDIB_Format::k8bppRgb: { + const bool bpp_1_or_8 = (bpp == 1 || bpp == 8); +- if (bpp_1_or_8 && !pSrcBitmap->GetPalette()) { +- return ConvertBuffer(FXDIB_8bppMask, dest_buf, dest_pitch, width, +- height, pSrcBitmap, src_left, src_top, p_pal); ++ if (bpp_1_or_8 && !pSrcBitmap->HasPalette()) { ++ return ConvertBuffer(FXDIB_Format::k8bppMask, dest_buf, dest_pitch, ++ width, height, pSrcBitmap, src_left, src_top, pal); + } +- p_pal->reset(FX_Alloc(uint32_t, 256)); +- if (bpp_1_or_8 && pSrcBitmap->GetPalette()) { ++ pal->resize(256); ++ if (bpp_1_or_8 && pSrcBitmap->HasPalette()) { + ConvertBuffer_Plt2PltRgb8(dest_buf, dest_pitch, width, height, +- pSrcBitmap, src_left, src_top, p_pal->get()); ++ pSrcBitmap, src_left, src_top, *pal); + return true; + } + if (bpp >= 24) { + ConvertBuffer_Rgb2PltRgb8(dest_buf, dest_pitch, width, height, +- pSrcBitmap, src_left, src_top, p_pal->get()); ++ pSrcBitmap, src_left, src_top, *pal); + return true; + } + return false; + } +- case FXDIB_Rgb: +- case FXDIB_Rgba: { ++ case FXDIB_Format::kRgb: { + return ConvertBuffer_Rgb(bpp, dest_format, dest_buf, dest_pitch, width, + height, pSrcBitmap, src_left, src_top); + } +- case FXDIB_Argb: +- case FXDIB_Rgb32: { +- return ConvertBuffer_Argb(bpp, GetIsCmykFromFormat(src_format), +- dest_format, dest_buf, dest_pitch, width, ++ case FXDIB_Format::kArgb: ++ case FXDIB_Format::kRgb32: { ++ return ConvertBuffer_Argb(bpp, dest_format, dest_buf, dest_pitch, width, + height, pSrcBitmap, src_left, src_top); + } + default: +diff --git a/core/fxge/dib/cfx_dibbase.h b/core/fxge/dib/cfx_dibbase.h +index 15d37b731..9f0deeb52 100644 +--- a/core/fxge/dib/cfx_dibbase.h ++++ b/core/fxge/dib/cfx_dibbase.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,88 +7,76 @@ + #ifndef CORE_FXGE_DIB_CFX_DIBBASE_H_ + #define CORE_FXGE_DIB_CFX_DIBBASE_H_ + +-#include ++#include + +-#include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/retain_ptr.h" +-#include "core/fxge/fx_dib.h" +- +-enum FXDIB_Channel { +- FXDIB_Red = 1, +- FXDIB_Green, +- FXDIB_Blue, +- FXDIB_Cyan, +- FXDIB_Magenta, +- FXDIB_Yellow, +- FXDIB_Black, +- FXDIB_Alpha +-}; ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/span.h" + + class CFX_ClipRgn; + class CFX_DIBitmap; ++class CFX_Matrix; + class PauseIndicatorIface; ++struct FX_RECT; + + // Base class for all Device-Independent Bitmaps. + class CFX_DIBBase : public Retainable { + public: ++#if BUILDFLAG(IS_APPLE) ++ // Matches Apple's kCGBitmapByteOrder32Little in fx_quartz_device.cpp. ++ static constexpr FXDIB_Format kPlatformRGBFormat = FXDIB_Format::kRgb32; ++#else // BUILDFLAG(IS_APPLE) ++ static constexpr FXDIB_Format kPlatformRGBFormat = FXDIB_Format::kRgb; ++#endif // BUILDFLAG(IS_APPLE) ++ ++ static constexpr uint32_t kPaletteSize = 256; ++ + ~CFX_DIBBase() override; + +- virtual uint8_t* GetBuffer() const; +- virtual const uint8_t* GetScanline(int line) const = 0; ++ virtual pdfium::span GetBuffer() const; ++ virtual pdfium::span GetScanline(int line) const = 0; + virtual bool SkipToScanline(int line, PauseIndicatorIface* pPause) const; +- virtual void DownSampleScanline(int line, +- uint8_t* dest_scan, +- int dest_bpp, +- int dest_width, +- bool bFlipX, +- int clip_left, +- int clip_width) const = 0; +- +- uint8_t* GetWritableScanline(int line) { +- return const_cast(GetScanline(line)); ++ virtual size_t GetEstimatedImageMemoryBurden() const; ++ ++ pdfium::span GetWritableScanline(int line) { ++ pdfium::span src = GetScanline(line); ++ return {const_cast(src.data()), src.size()}; + } + int GetWidth() const { return m_Width; } + int GetHeight() const { return m_Height; } +- +- FXDIB_Format GetFormat() const { +- return static_cast(m_AlphaFlag * 0x100 + m_bpp); +- } + uint32_t GetPitch() const { return m_Pitch; } +- uint32_t* GetPalette() const { return m_pPalette.get(); } +- int GetBPP() const { return m_bpp; } + +- bool IsAlphaMask() const { return !!(m_AlphaFlag & 1); } +- bool HasAlpha() const { return !!(m_AlphaFlag & 2); } +- bool IsCmykImage() const { return !!(m_AlphaFlag & 4); } +- bool IsOpaqueImage() const { return !IsAlphaMask() && !HasAlpha(); } +- +- size_t GetPaletteSize() const; ++ FXDIB_Format GetFormat() const { return m_Format; } ++ int GetBPP() const { return GetBppFromFormat(m_Format); } ++ bool IsMaskFormat() const { return GetIsMaskFromFormat(m_Format); } ++ bool IsAlphaFormat() const { return m_Format == FXDIB_Format::kArgb; } ++ bool IsOpaqueImage() const { return !IsMaskFormat() && !IsAlphaFormat(); } + ++ bool HasPalette() const { return !m_palette.empty(); } ++ pdfium::span GetPaletteSpan() const { return m_palette; } ++ size_t GetRequiredPaletteSize() const; + uint32_t GetPaletteArgb(int index) const; + void SetPaletteArgb(int index, uint32_t color); + + // Copies into internally-owned palette. +- void SetPalette(const uint32_t* pSrcPal); ++ void SetPalette(pdfium::span src_palette); + +- RetainPtr Clone(const FX_RECT* pClip) const; +- RetainPtr CloneConvert(FXDIB_Format format); ++ RetainPtr Realize() const; ++ RetainPtr ClipTo(const FX_RECT& rect) const; ++ RetainPtr ConvertTo(FXDIB_Format format) const; + RetainPtr StretchTo(int dest_width, + int dest_height, + const FXDIB_ResampleOptions& options, +- const FX_RECT* pClip); ++ const FX_RECT* pClip) const; + RetainPtr TransformTo(const CFX_Matrix& mtDest, + int* left, +- int* top); ++ int* top) const; + RetainPtr SwapXY(bool bXFlip, bool bYFlip) const; + RetainPtr FlipImage(bool bXFlip, bool bYFlip) const; + + RetainPtr CloneAlphaMask() const; + +- // Copies into internally-owned mask. +- bool SetAlphaMask(const RetainPtr& pAlphaMask, +- const FX_RECT* pClip); +- + bool GetOverlapRect(int& dest_left, + int& dest_top, + int& width, +@@ -97,39 +85,30 @@ class CFX_DIBBase : public Retainable { + int src_height, + int& src_left, + int& src_top, +- const CFX_ClipRgn* pClipRgn); +- +-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ +- void DebugVerifyBitmapIsPreMultiplied(void* buffer) const; +-#endif +- +- RetainPtr m_pAlphaMask; ++ const CFX_ClipRgn* pClipRgn) const; + + protected: + CFX_DIBBase(); + + static bool ConvertBuffer(FXDIB_Format dest_format, +- uint8_t* dest_buf, ++ pdfium::span dest_buf, + int dest_pitch, + int width, + int height, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + int src_left, + int src_top, +- std::unique_ptr* pal); ++ DataVector* pal); + ++ RetainPtr ClipToInternal(const FX_RECT* pClip) const; + void BuildPalette(); +- bool BuildAlphaMask(); + int FindPalette(uint32_t color) const; +- void GetPalette(uint32_t* pal, int alpha) const; +- +- int m_Width; +- int m_Height; +- int m_bpp; +- uint32_t m_AlphaFlag; +- uint32_t m_Pitch; +- // TODO(weili): Use std::vector for this. +- std::unique_ptr m_pPalette; ++ ++ FXDIB_Format m_Format = FXDIB_Format::kInvalid; ++ int m_Width = 0; ++ int m_Height = 0; ++ uint32_t m_Pitch = 0; ++ DataVector m_palette; + }; + + #endif // CORE_FXGE_DIB_CFX_DIBBASE_H_ +diff --git a/core/fxge/dib/cfx_dibbase_unittest.cpp b/core/fxge/dib/cfx_dibbase_unittest.cpp +new file mode 100644 +index 000000000..ac05c09fc +--- /dev/null ++++ b/core/fxge/dib/cfx_dibbase_unittest.cpp +@@ -0,0 +1,163 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxge/dib/cfx_dibbase.h" ++ ++#include "core/fxcrt/fx_coordinates.h" ++#include "core/fxge/dib/cfx_dibitmap.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++namespace { ++ ++struct Input { ++ CFX_Point src_top_left; ++ CFX_Size src_size; ++ CFX_Point dest_top_left; ++ CFX_Size overlap_size; ++}; ++ ++struct Output { ++ CFX_Point src_top_left; ++ CFX_Point dest_top_left; ++ CFX_Size overlap_size; ++}; ++ ++void RunOverlapRectTest(const CFX_DIBitmap* bitmap, ++ const Input& input, ++ const Output* expected_output) { ++ // Initialize in-out parameters. ++ int src_left = input.src_top_left.x; ++ int src_top = input.src_top_left.y; ++ int dest_left = input.dest_top_left.x; ++ int dest_top = input.dest_top_left.y; ++ int overlap_width = input.overlap_size.width; ++ int overlap_height = input.overlap_size.height; ++ ++ bool success = bitmap->GetOverlapRect( ++ dest_left, dest_top, overlap_width, overlap_height, input.src_size.width, ++ input.src_size.height, src_left, src_top, ++ /*pClipRgn=*/nullptr); ++ if (success == !expected_output) { ++ ADD_FAILURE(); ++ return; ++ } ++ ++ if (expected_output) { ++ EXPECT_EQ(expected_output->src_top_left.x, src_left); ++ EXPECT_EQ(expected_output->src_top_left.y, src_top); ++ EXPECT_EQ(expected_output->dest_top_left.x, dest_left); ++ EXPECT_EQ(expected_output->dest_top_left.y, dest_top); ++ EXPECT_EQ(expected_output->overlap_size.width, overlap_width); ++ EXPECT_EQ(expected_output->overlap_size.height, overlap_height); ++ } ++} ++ ++} // namespace ++ ++TEST(CFX_DIBBaseTest, GetOverlapRectTrivialOverlap) { ++ auto bitmap = pdfium::MakeRetain(); ++ EXPECT_TRUE(bitmap->Create(400, 300, FXDIB_Format::k1bppRgb)); ++ ++ const Input kInput = {/*src_top_left=*/{0, 0}, /*src_size=*/{400, 300}, ++ /*dest_top_left=*/{0, 0}, ++ /*overlap_size=*/{400, 300}}; ++ const Output kExpectedOutput = {/*src_top_left=*/{0, 0}, ++ /*dest_top_left=*/{0, 0}, ++ /*overlap_size=*/{400, 300}}; ++ RunOverlapRectTest(bitmap.Get(), kInput, &kExpectedOutput); ++} ++ ++TEST(CFX_DIBBaseTest, GetOverlapRectOverlapNoLimit) { ++ auto bitmap = pdfium::MakeRetain(); ++ EXPECT_TRUE(bitmap->Create(400, 300, FXDIB_Format::k1bppRgb)); ++ ++ const Input kInput = {/*src_top_left=*/{35, 41}, /*src_size=*/{400, 300}, ++ /*dest_top_left=*/{123, 137}, ++ /*overlap_size=*/{200, 100}}; ++ const Output kExpectedOutput = {/*src_top_left=*/{35, 41}, ++ /*dest_top_left=*/{123, 137}, ++ /*overlap_size=*/{200, 100}}; ++ RunOverlapRectTest(bitmap.Get(), kInput, &kExpectedOutput); ++} ++ ++TEST(CFX_DIBBaseTest, GetOverlapRectOverlapLimitedBySource) { ++ auto bitmap = pdfium::MakeRetain(); ++ EXPECT_TRUE(bitmap->Create(400, 300, FXDIB_Format::k1bppRgb)); ++ ++ const Input kInput = {/*src_top_left=*/{141, 154}, /*src_size=*/{400, 300}, ++ /*dest_top_left=*/{35, 41}, ++ /*overlap_size=*/{270, 160}}; ++ const Output kExpectedOutput = {/*src_top_left=*/{141, 154}, ++ /*dest_top_left=*/{35, 41}, ++ /*overlap_size=*/{259, 146}}; ++ RunOverlapRectTest(bitmap.Get(), kInput, &kExpectedOutput); ++} ++ ++TEST(CFX_DIBBaseTest, GetOverlapRectOverlapLimitedByDestination) { ++ auto bitmap = pdfium::MakeRetain(); ++ EXPECT_TRUE(bitmap->Create(400, 300, FXDIB_Format::k1bppRgb)); ++ ++ const Input kInput = {/*src_top_left=*/{35, 41}, /*src_size=*/{400, 300}, ++ /*dest_top_left=*/{123, 137}, ++ /*overlap_size=*/{280, 170}}; ++ const Output kExpectedOutput = {/*src_top_left=*/{35, 41}, ++ /*dest_top_left=*/{123, 137}, ++ /*overlap_size=*/{277, 163}}; ++ RunOverlapRectTest(bitmap.Get(), kInput, &kExpectedOutput); ++} ++ ++TEST(CFX_DIBBaseTest, GetOverlapRectBadInputs) { ++ auto bitmap = pdfium::MakeRetain(); ++ EXPECT_TRUE(bitmap->Create(400, 300, FXDIB_Format::k1bppRgb)); ++ ++ const Input kEmptyInputs[] = { ++ // Empty source rect. ++ {/*src_top_left=*/{0, 0}, /*src_size=*/{0, 0}, ++ /*dest_top_left=*/{0, 0}, ++ /*overlap_size=*/{400, 300}}, ++ // Empty overlap size. ++ {/*src_top_left=*/{0, 0}, /*src_size=*/{400, 300}, ++ /*dest_top_left=*/{0, 0}, ++ /*overlap_size=*/{0, 0}}, ++ // Source out of bounds on x-axis. ++ {/*src_top_left=*/{-400, 0}, /*src_size=*/{400, 300}, ++ /*dest_top_left=*/{0, 0}, ++ /*overlap_size=*/{400, 300}}, ++ }; ++ for (const Input& input : kEmptyInputs) ++ RunOverlapRectTest(bitmap.Get(), input, /*expected_output=*/nullptr); ++ ++ const Input kOutOfBoundInputs[] = { ++ // Source out of bounds on x-axis. ++ {/*src_top_left=*/{400, 0}, /*src_size=*/{400, 300}, ++ /*dest_top_left=*/{0, 0}, ++ /*overlap_size=*/{400, 300}}, ++ // Source out of bounds on y-axis. ++ {/*src_top_left=*/{0, 300}, /*src_size=*/{400, 300}, ++ /*dest_top_left=*/{0, 0}, ++ /*overlap_size=*/{400, 300}}, ++ // Source out of bounds on y-axis. ++ {/*src_top_left=*/{0, -300}, /*src_size=*/{400, 300}, ++ /*dest_top_left=*/{0, 0}, ++ /*overlap_size=*/{400, 300}}, ++ // Destination out of bounds on x-axis. ++ {/*src_top_left=*/{0, 0}, /*src_size=*/{400, 300}, ++ /*dest_top_left=*/{-400, 0}, ++ /*overlap_size=*/{400, 300}}, ++ // Destination out of bounds on x-axis. ++ {/*src_top_left=*/{0, 0}, /*src_size=*/{400, 300}, ++ /*dest_top_left=*/{400, 0}, ++ /*overlap_size=*/{400, 300}}, ++ // Destination out of bounds on y-axis. ++ {/*src_top_left=*/{0, 0}, /*src_size=*/{400, 300}, ++ /*dest_top_left=*/{0, -300}, ++ /*overlap_size=*/{400, 300}}, ++ // Destination out of bounds on y-axis. ++ {/*src_top_left=*/{0, 0}, /*src_size=*/{400, 300}, ++ /*dest_top_left=*/{0, 300}, ++ /*overlap_size=*/{400, 300}}, ++ }; ++ for (const Input& input : kOutOfBoundInputs) ++ RunOverlapRectTest(bitmap.Get(), input, /*expected_output=*/nullptr); ++} +diff --git a/core/fxge/dib/cfx_dibextractor.cpp b/core/fxge/dib/cfx_dibextractor.cpp +index f6f31e8f7..9dcfbec52 100644 +--- a/core/fxge/dib/cfx_dibextractor.cpp ++++ b/core/fxge/dib/cfx_dibextractor.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,19 +10,19 @@ + #include "core/fxge/dib/cfx_dibitmap.h" + + CFX_DIBExtractor::CFX_DIBExtractor(const RetainPtr& pSrc) { +- if (!pSrc->GetBuffer()) { +- m_pBitmap = pSrc->Clone(nullptr); ++ if (pSrc->GetBuffer().empty()) { ++ m_pBitmap = pSrc->Realize(); + return; + } +- RetainPtr pOldSrc(pSrc); + m_pBitmap = pdfium::MakeRetain(); +- if (!m_pBitmap->Create(pOldSrc->GetWidth(), pOldSrc->GetHeight(), +- pOldSrc->GetFormat(), pOldSrc->GetBuffer(), 0)) { ++ if (!m_pBitmap->Create(pSrc->GetWidth(), pSrc->GetHeight(), pSrc->GetFormat(), ++ pSrc->GetBuffer().data(), ++ /*pitch=*/0)) { + m_pBitmap.Reset(); + return; + } +- m_pBitmap->SetPalette(pOldSrc->GetPalette()); +- m_pBitmap->SetAlphaMask(pOldSrc->m_pAlphaMask, nullptr); ++ ++ m_pBitmap->SetPalette(pSrc->GetPaletteSpan()); + } + +-CFX_DIBExtractor::~CFX_DIBExtractor() {} ++CFX_DIBExtractor::~CFX_DIBExtractor() = default; +diff --git a/core/fxge/dib/cfx_dibextractor.h b/core/fxge/dib/cfx_dibextractor.h +index eff96b121..455eca144 100644 +--- a/core/fxge/dib/cfx_dibextractor.h ++++ b/core/fxge/dib/cfx_dibextractor.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxge/dib/cfx_dibitmap.cpp b/core/fxge/dib/cfx_dibitmap.cpp +index c5ee2c506..62881a629 100644 +--- a/core/fxge/dib/cfx_dibitmap.cpp ++++ b/core/fxge/dib/cfx_dibitmap.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,28 +6,28 @@ + + #include "core/fxge/dib/cfx_dibitmap.h" + ++#include ++#include ++ + #include + #include + #include + + #include "build/build_config.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_coordinates.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/span_util.h" ++#include "core/fxge/calculate_pitch.h" + #include "core/fxge/cfx_cliprgn.h" +-#include "core/fxge/dib/cfx_cmyk_to_srgb.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" + #include "core/fxge/dib/cfx_scanlinecompositor.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/notreached.h" ++#include "third_party/base/numerics/safe_conversions.h" + +-namespace { +- +-constexpr size_t kMaxOOMLimit = 12000000; +-constexpr int8_t kChannelOffset[] = {0, 2, 1, 0, 0, 1, 2, 3, 3}; +- +-} // namespace +- +-CFX_DIBitmap::CFX_DIBitmap() { +- m_pPalette = nullptr; +-#ifdef _SKIA_SUPPORT_PATHS_ +- m_nFormat = Format::kCleared; +-#endif +-} ++CFX_DIBitmap::CFX_DIBitmap() = default; + + bool CFX_DIBitmap::Create(int width, int height, FXDIB_Format format) { + return Create(width, height, format, nullptr, 0); +@@ -39,47 +39,33 @@ bool CFX_DIBitmap::Create(int width, + uint8_t* pBuffer, + uint32_t pitch) { + m_pBuffer = nullptr; +- m_bpp = GetBppFromFormat(format); +- m_AlphaFlag = GetAlphaFlagFromFormat(format); ++ m_Format = format; + m_Width = 0; + m_Height = 0; + m_Pitch = 0; + +- uint32_t calculatedSize; +- if (!CalculatePitchAndSize(height, width, format, &pitch, &calculatedSize)) ++ absl::optional pitch_size = ++ CalculatePitchAndSize(width, height, format, pitch); ++ if (!pitch_size.has_value()) + return false; + + if (pBuffer) { + m_pBuffer.Reset(pBuffer); + } else { +- size_t bufferSize = calculatedSize + 4; +- if (bufferSize >= kMaxOOMLimit) { +- m_pBuffer = std::unique_ptr( +- FX_TryAlloc(uint8_t, bufferSize)); +- if (!m_pBuffer) +- return false; +- } else { +- m_pBuffer = std::unique_ptr( +- FX_Alloc(uint8_t, bufferSize)); +- } ++ FX_SAFE_SIZE_T safe_buffer_size = pitch_size.value().size; ++ safe_buffer_size += 4; ++ if (!safe_buffer_size.IsValid()) ++ return false; ++ ++ m_pBuffer = std::unique_ptr( ++ FX_TryAlloc(uint8_t, safe_buffer_size.ValueOrDie())); ++ if (!m_pBuffer) ++ return false; + } + m_Width = width; + m_Height = height; +- m_Pitch = pitch; +- if (!HasAlpha() || format == FXDIB_Argb) +- return true; +- +- if (BuildAlphaMask()) +- return true; +- +- if (pBuffer) +- return true; +- +- m_pBuffer = nullptr; +- m_Width = 0; +- m_Height = 0; +- m_Pitch = 0; +- return false; ++ m_Pitch = pitch_size.value().pitch; ++ return true; + } + + bool CFX_DIBitmap::Copy(const RetainPtr& pSrc) { +@@ -89,31 +75,46 @@ bool CFX_DIBitmap::Copy(const RetainPtr& pSrc) { + if (!Create(pSrc->GetWidth(), pSrc->GetHeight(), pSrc->GetFormat())) + return false; + +- SetPalette(pSrc->GetPalette()); +- SetAlphaMask(pSrc->m_pAlphaMask, nullptr); +- for (int row = 0; row < pSrc->GetHeight(); row++) +- memcpy(m_pBuffer.Get() + row * m_Pitch, pSrc->GetScanline(row), m_Pitch); ++ SetPalette(pSrc->GetPaletteSpan()); ++ for (int row = 0; row < pSrc->GetHeight(); row++) { ++ memcpy(m_pBuffer.Get() + row * m_Pitch, pSrc->GetScanline(row).data(), ++ m_Pitch); ++ } + return true; + } + +-CFX_DIBitmap::~CFX_DIBitmap() {} ++CFX_DIBitmap::~CFX_DIBitmap() = default; ++ ++pdfium::span CFX_DIBitmap::GetBuffer() const { ++ if (!m_pBuffer) ++ return pdfium::span(); + +-uint8_t* CFX_DIBitmap::GetBuffer() const { +- return m_pBuffer.Get(); ++ return {m_pBuffer.Get(), m_Height * m_Pitch}; + } + +-const uint8_t* CFX_DIBitmap::GetScanline(int line) const { +- return m_pBuffer.Get() ? m_pBuffer.Get() + line * m_Pitch : nullptr; ++pdfium::span CFX_DIBitmap::GetScanline(int line) const { ++ auto buffer_span = GetBuffer(); ++ if (buffer_span.empty()) ++ return pdfium::span(); ++ ++ return buffer_span.subspan(line * m_Pitch, m_Pitch); ++} ++ ++size_t CFX_DIBitmap::GetEstimatedImageMemoryBurden() const { ++ size_t result = CFX_DIBBase::GetEstimatedImageMemoryBurden(); ++ if (!GetBuffer().empty()) { ++ int height = GetHeight(); ++ CHECK(pdfium::base::IsValueInRangeForNumericType(height)); ++ result += static_cast(height) * GetPitch(); ++ } ++ return result; + } + + void CFX_DIBitmap::TakeOver(RetainPtr&& pSrcBitmap) { + m_pBuffer = std::move(pSrcBitmap->m_pBuffer); +- m_pPalette = std::move(pSrcBitmap->m_pPalette); +- m_pAlphaMask = pSrcBitmap->m_pAlphaMask; ++ m_palette = std::move(pSrcBitmap->m_palette); + pSrcBitmap->m_pBuffer = nullptr; +- pSrcBitmap->m_pAlphaMask = nullptr; +- m_bpp = pSrcBitmap->m_bpp; +- m_AlphaFlag = pSrcBitmap->m_AlphaFlag; ++ m_Format = pSrcBitmap->m_Format; + m_Width = pSrcBitmap->m_Width; + m_Height = pSrcBitmap->m_Height; + m_Pitch = pSrcBitmap->m_Pitch; +@@ -125,24 +126,23 @@ void CFX_DIBitmap::Clear(uint32_t color) { + + uint8_t* pBuffer = m_pBuffer.Get(); + switch (GetFormat()) { +- case FXDIB_1bppMask: ++ case FXDIB_Format::k1bppMask: + memset(pBuffer, (color & 0xff000000) ? 0xff : 0, m_Pitch * m_Height); + break; +- case FXDIB_1bppRgb: { ++ case FXDIB_Format::k1bppRgb: { + int index = FindPalette(color); + memset(pBuffer, index ? 0xff : 0, m_Pitch * m_Height); + break; + } +- case FXDIB_8bppMask: ++ case FXDIB_Format::k8bppMask: + memset(pBuffer, color >> 24, m_Pitch * m_Height); + break; +- case FXDIB_8bppRgb: { ++ case FXDIB_Format::k8bppRgb: { + int index = FindPalette(color); + memset(pBuffer, index, m_Pitch * m_Height); + break; + } +- case FXDIB_Rgb: +- case FXDIB_Rgba: { ++ case FXDIB_Format::kRgb: { + int a; + int r; + int g; +@@ -163,13 +163,14 @@ void CFX_DIBitmap::Clear(uint32_t color) { + } + break; + } +- case FXDIB_Rgb32: +- case FXDIB_Argb: { +- color = IsCmykImage() ? FXCMYK_TODIB(color) : FXARGB_TODIB(color); +-#ifdef _SKIA_SUPPORT_ +- if (FXDIB_Rgb32 == GetFormat() && !IsCmykImage()) ++ case FXDIB_Format::kRgb32: ++ case FXDIB_Format::kArgb: { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer() && ++ FXDIB_Format::kRgb32 == GetFormat()) { ++ // TODO(crbug.com/pdfium/2016): This is not reliable because alpha may ++ // be modified outside of this operation. + color |= 0xFF000000; +-#endif ++ } + for (int i = 0; i < m_Width; i++) + reinterpret_cast(pBuffer)[i] = color; + for (int row = 1; row < m_Height; row++) +@@ -224,20 +225,23 @@ bool CFX_DIBitmap::TransferWithUnequalFormats( + const RetainPtr& pSrcBitmap, + int src_left, + int src_top) { +- if (m_pPalette) ++ if (HasPalette()) + return false; + +- if (m_bpp == 8) +- dest_format = FXDIB_8bppMask; ++ if (GetBppFromFormat(m_Format) == 8) ++ dest_format = FXDIB_Format::k8bppMask; + +- uint8_t* dest_buf = +- m_pBuffer.Get() + dest_top * m_Pitch + dest_left * GetBPP() / 8; +- std::unique_ptr d_plt; +- if (!ConvertBuffer(dest_format, dest_buf, m_Pitch, width, height, pSrcBitmap, +- src_left, src_top, &d_plt)) { ++ FX_SAFE_UINT32 offset = dest_left; ++ offset *= GetBPP(); ++ offset /= 8; ++ if (!offset.IsValid()) + return false; +- } +- return true; ++ ++ pdfium::span dest_buf = GetBuffer().subspan( ++ dest_top * m_Pitch + static_cast(offset.ValueOrDie())); ++ DataVector d_plt; ++ return ConvertBuffer(dest_format, dest_buf, m_Pitch, width, height, ++ pSrcBitmap, src_left, src_top, &d_plt); + } + + void CFX_DIBitmap::TransferWithMultipleBPP( +@@ -253,7 +257,7 @@ void CFX_DIBitmap::TransferWithMultipleBPP( + uint8_t* dest_scan = + m_pBuffer.Get() + (dest_top + row) * m_Pitch + dest_left * Bpp; + const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left * Bpp; ++ pSrcBitmap->GetScanline(src_top + row).subspan(src_left * Bpp).data(); + memcpy(dest_scan, src_scan, width * Bpp); + } + } +@@ -268,7 +272,7 @@ void CFX_DIBitmap::TransferEqualFormatsOneBPP( + int src_top) { + for (int row = 0; row < height; ++row) { + uint8_t* dest_scan = m_pBuffer.Get() + (dest_top + row) * m_Pitch; +- const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row); ++ const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row).data(); + for (int col = 0; col < width; ++col) { + int src_idx = src_left + col; + int dest_idx = dest_left + col; +@@ -280,69 +284,50 @@ void CFX_DIBitmap::TransferEqualFormatsOneBPP( + } + } + +-bool CFX_DIBitmap::LoadChannelFromAlpha( +- FXDIB_Channel destChannel, ++bool CFX_DIBitmap::SetChannelFromBitmap( ++ Channel destChannel, + const RetainPtr& pSrcBitmap) { + if (!m_pBuffer) + return false; + + RetainPtr pSrcClone = pSrcBitmap; +- if (!pSrcBitmap->HasAlpha() && !pSrcBitmap->IsAlphaMask()) ++ if (!pSrcBitmap->IsAlphaFormat() && !pSrcBitmap->IsMaskFormat()) + return false; + + if (pSrcBitmap->GetBPP() == 1) { +- pSrcClone = pSrcBitmap->CloneConvert(FXDIB_8bppMask); ++ pSrcClone = pSrcBitmap->ConvertTo(FXDIB_Format::k8bppMask); + if (!pSrcClone) + return false; + } +- int srcOffset = pSrcBitmap->GetFormat() == FXDIB_Argb ? 3 : 0; ++ const int srcOffset = pSrcBitmap->GetFormat() == FXDIB_Format::kArgb ? 3 : 0; + int destOffset = 0; +- if (destChannel == FXDIB_Alpha) { +- if (IsAlphaMask()) { +- if (!ConvertFormat(FXDIB_8bppMask)) ++ if (destChannel == Channel::kAlpha) { ++ if (IsMaskFormat()) { ++ if (!ConvertFormat(FXDIB_Format::k8bppMask)) + return false; + } else { +- if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyka : FXDIB_Argb)) ++ if (!ConvertFormat(FXDIB_Format::kArgb)) + return false; + +- if (GetFormat() == FXDIB_Argb) +- destOffset = 3; ++ destOffset = 3; + } + } else { +- if (IsAlphaMask()) ++ DCHECK_EQ(destChannel, Channel::kRed); ++ if (IsMaskFormat()) + return false; + + if (GetBPP() < 24) { +- if (HasAlpha()) { +- if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyka : FXDIB_Argb)) ++ if (IsAlphaFormat()) { ++ if (!ConvertFormat(FXDIB_Format::kArgb)) + return false; + } else { +-#if defined(OS_MACOSX) +- constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb32; +-#else +- constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb; +-#endif +- if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyk : kPlatformFormat)) ++ if (!ConvertFormat(kPlatformRGBFormat)) + return false; + } + } +- destOffset = kChannelOffset[destChannel]; ++ destOffset = 2; + } +- if (pSrcClone->m_pAlphaMask) { +- RetainPtr pAlphaMask = pSrcClone->m_pAlphaMask; +- if (pSrcClone->GetWidth() != m_Width || +- pSrcClone->GetHeight() != m_Height) { +- if (pAlphaMask) { +- pAlphaMask = pAlphaMask->StretchTo(m_Width, m_Height, +- FXDIB_ResampleOptions(), nullptr); +- if (!pAlphaMask) +- return false; +- } +- } +- pSrcClone = std::move(pAlphaMask); +- srcOffset = 0; +- } else if (pSrcClone->GetWidth() != m_Width || +- pSrcClone->GetHeight() != m_Height) { ++ if (pSrcClone->GetWidth() != m_Width || pSrcClone->GetHeight() != m_Height) { + RetainPtr pSrcMatched = pSrcClone->StretchTo( + m_Width, m_Height, FXDIB_ResampleOptions(), nullptr); + if (!pSrcMatched) +@@ -351,15 +336,13 @@ bool CFX_DIBitmap::LoadChannelFromAlpha( + pSrcClone = pSrcMatched; + } + RetainPtr pDst(this); +- if (destChannel == FXDIB_Alpha && m_pAlphaMask) { +- pDst = m_pAlphaMask; +- destOffset = 0; +- } + int srcBytes = pSrcClone->GetBPP() / 8; + int destBytes = pDst->GetBPP() / 8; + for (int row = 0; row < m_Height; row++) { +- uint8_t* dest_pos = pDst->GetWritableScanline(row) + destOffset; +- const uint8_t* src_pos = pSrcClone->GetScanline(row) + srcOffset; ++ uint8_t* dest_pos = ++ pDst->GetWritableScanline(row).subspan(destOffset).data(); ++ const uint8_t* src_pos = ++ pSrcClone->GetScanline(row).subspan(srcOffset).data(); + for (int col = 0; col < m_Width; col++) { + *dest_pos = *src_pos; + dest_pos += destBytes; +@@ -369,61 +352,36 @@ bool CFX_DIBitmap::LoadChannelFromAlpha( + return true; + } + +-bool CFX_DIBitmap::LoadChannel(FXDIB_Channel destChannel, int value) { ++bool CFX_DIBitmap::SetAlphaFromBitmap( ++ const RetainPtr& pSrcBitmap) { ++ return SetChannelFromBitmap(Channel::kAlpha, pSrcBitmap); ++} ++ ++bool CFX_DIBitmap::SetRedFromBitmap(const RetainPtr& pSrcBitmap) { ++ return SetChannelFromBitmap(Channel::kRed, pSrcBitmap); ++} ++ ++bool CFX_DIBitmap::SetUniformOpaqueAlpha() { + if (!m_pBuffer) + return false; + +- int destOffset; +- if (destChannel == FXDIB_Alpha) { +- if (IsAlphaMask()) { +- if (!ConvertFormat(FXDIB_8bppMask)) { +- return false; +- } +- destOffset = 0; +- } else { +- destOffset = 0; +- if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyka : FXDIB_Argb)) { +- return false; +- } +- if (GetFormat() == FXDIB_Argb) { +- destOffset = 3; +- } +- } ++ if (IsMaskFormat()) { ++ if (!ConvertFormat(FXDIB_Format::k8bppMask)) ++ return false; + } else { +- if (IsAlphaMask()) { ++ if (!ConvertFormat(FXDIB_Format::kArgb)) + return false; +- } +- if (GetBPP() < 24) { +- if (HasAlpha()) { +- if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyka : FXDIB_Argb)) { +- return false; +- } +- } else { +-#if defined(OS_MACOSX) +- constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb; +-#else +- constexpr FXDIB_Format kPlatformFormat = FXDIB_Rgb32; +-#endif +- if (!ConvertFormat(IsCmykImage() ? FXDIB_Cmyk : kPlatformFormat)) +- return false; +- } +- } +- destOffset = kChannelOffset[destChannel]; + } +- int Bpp = GetBPP() / 8; ++ const int Bpp = GetBPP() / 8; + if (Bpp == 1) { +- memset(m_pBuffer.Get(), value, m_Height * m_Pitch); +- return true; +- } +- if (destChannel == FXDIB_Alpha && m_pAlphaMask) { +- memset(m_pAlphaMask->GetBuffer(), value, +- m_pAlphaMask->GetHeight() * m_pAlphaMask->GetPitch()); ++ memset(m_pBuffer.Get(), 0xff, m_Height * m_Pitch); + return true; + } ++ const int destOffset = GetFormat() == FXDIB_Format::kArgb ? 3 : 0; + for (int row = 0; row < m_Height; row++) { + uint8_t* scan_line = m_pBuffer.Get() + row * m_Pitch + destOffset; + for (int col = 0; col < m_Width; col++) { +- *scan_line = value; ++ *scan_line = 0xff; + scan_line += Bpp; + } + } +@@ -434,13 +392,13 @@ bool CFX_DIBitmap::MultiplyAlpha(const RetainPtr& pSrcBitmap) { + if (!m_pBuffer) + return false; + +- if (!pSrcBitmap->IsAlphaMask()) { ++ if (!pSrcBitmap->IsMaskFormat()) { + NOTREACHED(); + return false; + } + +- if (!IsAlphaMask() && !HasAlpha()) +- return LoadChannelFromAlpha(FXDIB_Alpha, pSrcBitmap); ++ if (IsOpaqueImage()) ++ return SetAlphaFromBitmap(pSrcBitmap); + + RetainPtr pSrcClone = pSrcBitmap.As(); + if (pSrcBitmap->GetWidth() != m_Width || +@@ -450,8 +408,8 @@ bool CFX_DIBitmap::MultiplyAlpha(const RetainPtr& pSrcBitmap) { + if (!pSrcClone) + return false; + } +- if (IsAlphaMask()) { +- if (!ConvertFormat(FXDIB_8bppMask)) ++ if (IsMaskFormat()) { ++ if (!ConvertFormat(FXDIB_Format::k8bppMask)) + return false; + + for (int row = 0; row < m_Height; row++) { +@@ -469,22 +427,19 @@ bool CFX_DIBitmap::MultiplyAlpha(const RetainPtr& pSrcBitmap) { + } + } + } +- } else { +- if (GetFormat() == FXDIB_Argb) { +- if (pSrcClone->GetBPP() == 1) +- return false; ++ return true; ++ } + +- for (int row = 0; row < m_Height; row++) { +- uint8_t* dest_scan = m_pBuffer.Get() + m_Pitch * row + 3; +- uint8_t* src_scan = +- pSrcClone->m_pBuffer.Get() + pSrcClone->m_Pitch * row; +- for (int col = 0; col < m_Width; col++) { +- *dest_scan = (*dest_scan) * src_scan[col] / 255; +- dest_scan += 4; +- } +- } +- } else { +- m_pAlphaMask->MultiplyAlpha(pSrcClone); ++ DCHECK_EQ(GetFormat(), FXDIB_Format::kArgb); ++ if (pSrcClone->GetBPP() == 1) ++ return false; ++ ++ for (int row = 0; row < m_Height; row++) { ++ uint8_t* dest_scan = m_pBuffer.Get() + m_Pitch * row + 3; ++ uint8_t* src_scan = pSrcClone->m_pBuffer.Get() + pSrcClone->m_Pitch * row; ++ for (int col = 0; col < m_Width; col++) { ++ *dest_scan = (*dest_scan) * src_scan[col] / 255; ++ dest_scan += 4; + } + } + return true; +@@ -495,13 +450,13 @@ bool CFX_DIBitmap::MultiplyAlpha(int alpha) { + return false; + + switch (GetFormat()) { +- case FXDIB_1bppMask: +- if (!ConvertFormat(FXDIB_8bppMask)) { ++ case FXDIB_Format::k1bppMask: ++ if (!ConvertFormat(FXDIB_Format::k8bppMask)) { + return false; + } + MultiplyAlpha(alpha); + break; +- case FXDIB_8bppMask: { ++ case FXDIB_Format::k8bppMask: { + for (int row = 0; row < m_Height; row++) { + uint8_t* scan_line = m_pBuffer.Get() + row * m_Pitch; + for (int col = 0; col < m_Width; col++) { +@@ -510,7 +465,7 @@ bool CFX_DIBitmap::MultiplyAlpha(int alpha) { + } + break; + } +- case FXDIB_Argb: { ++ case FXDIB_Format::kArgb: { + for (int row = 0; row < m_Height; row++) { + uint8_t* scan_line = m_pBuffer.Get() + row * m_Pitch + 3; + for (int col = 0; col < m_Width; col++) { +@@ -521,52 +476,50 @@ bool CFX_DIBitmap::MultiplyAlpha(int alpha) { + break; + } + default: +- if (HasAlpha()) { +- m_pAlphaMask->MultiplyAlpha(alpha); +- } else if (IsCmykImage()) { +- if (!ConvertFormat((FXDIB_Format)(GetFormat() | 0x0200))) { +- return false; +- } +- m_pAlphaMask->MultiplyAlpha(alpha); +- } else { +- if (!ConvertFormat(FXDIB_Argb)) { +- return false; +- } +- MultiplyAlpha(alpha); ++ DCHECK(!IsAlphaFormat()); ++ if (!ConvertFormat(FXDIB_Format::kArgb)) { ++ return false; + } ++ MultiplyAlpha(alpha); + break; + } + return true; + } + ++#if defined(_SKIA_SUPPORT_) + uint32_t CFX_DIBitmap::GetPixel(int x, int y) const { + if (!m_pBuffer) + return 0; + +- uint8_t* pos = m_pBuffer.Get() + y * m_Pitch + x * GetBPP() / 8; ++ FX_SAFE_UINT32 offset = x; ++ offset *= GetBPP(); ++ offset /= 8; ++ if (!offset.IsValid()) ++ return 0; ++ ++ uint8_t* pos = m_pBuffer.Get() + y * m_Pitch + offset.ValueOrDie(); + switch (GetFormat()) { +- case FXDIB_1bppMask: { ++ case FXDIB_Format::k1bppMask: { + if ((*pos) & (1 << (7 - x % 8))) { + return 0xff000000; + } + return 0; + } +- case FXDIB_1bppRgb: { ++ case FXDIB_Format::k1bppRgb: { + if ((*pos) & (1 << (7 - x % 8))) { +- return m_pPalette ? m_pPalette.get()[1] : 0xffffffff; ++ return HasPalette() ? GetPaletteSpan()[1] : 0xffffffff; + } +- return m_pPalette ? m_pPalette.get()[0] : 0xff000000; ++ return HasPalette() ? GetPaletteSpan()[0] : 0xff000000; + } +- case FXDIB_8bppMask: ++ case FXDIB_Format::k8bppMask: + return (*pos) << 24; +- case FXDIB_8bppRgb: +- return m_pPalette ? m_pPalette.get()[*pos] +- : (0xff000000 | ((*pos) * 0x10101)); +- case FXDIB_Rgb: +- case FXDIB_Rgba: +- case FXDIB_Rgb32: ++ case FXDIB_Format::k8bppRgb: ++ return HasPalette() ? GetPaletteSpan()[*pos] ++ : ArgbEncode(0xff, *pos, *pos, *pos); ++ case FXDIB_Format::kRgb: ++ case FXDIB_Format::kRgb32: + return FXARGB_GETDIB(pos) | 0xff000000; +- case FXDIB_Argb: ++ case FXDIB_Format::kArgb: + return FXARGB_GETDIB(pos); + default: + break; +@@ -581,18 +534,24 @@ void CFX_DIBitmap::SetPixel(int x, int y, uint32_t color) { + if (x < 0 || x >= m_Width || y < 0 || y >= m_Height) + return; + +- uint8_t* pos = m_pBuffer.Get() + y * m_Pitch + x * GetBPP() / 8; ++ FX_SAFE_UINT32 offset = x; ++ offset *= GetBPP(); ++ offset /= 8; ++ if (!offset.IsValid()) ++ return; ++ ++ uint8_t* pos = m_pBuffer.Get() + y * m_Pitch + offset.ValueOrDie(); + switch (GetFormat()) { +- case FXDIB_1bppMask: ++ case FXDIB_Format::k1bppMask: + if (color >> 24) { + *pos |= 1 << (7 - x % 8); + } else { + *pos &= ~(1 << (7 - x % 8)); + } + break; +- case FXDIB_1bppRgb: +- if (m_pPalette) { +- if (color == m_pPalette.get()[1]) { ++ case FXDIB_Format::k1bppRgb: ++ if (HasPalette()) { ++ if (color == GetPaletteSpan()[1]) { + *pos |= 1 << (7 - x % 8); + } else { + *pos &= ~(1 << (7 - x % 8)); +@@ -605,13 +564,14 @@ void CFX_DIBitmap::SetPixel(int x, int y, uint32_t color) { + } + } + break; +- case FXDIB_8bppMask: ++ case FXDIB_Format::k8bppMask: + *pos = (uint8_t)(color >> 24); + break; +- case FXDIB_8bppRgb: { +- if (m_pPalette) { ++ case FXDIB_Format::k8bppRgb: { ++ if (HasPalette()) { ++ pdfium::span palette = GetPaletteSpan(); + for (int i = 0; i < 256; i++) { +- if (m_pPalette.get()[i] == color) { ++ if (palette[i] == color) { + *pos = (uint8_t)i; + return; + } +@@ -622,92 +582,22 @@ void CFX_DIBitmap::SetPixel(int x, int y, uint32_t color) { + } + break; + } +- case FXDIB_Rgb: +- case FXDIB_Rgb32: { ++ case FXDIB_Format::kRgb: ++ case FXDIB_Format::kRgb32: { + int alpha = FXARGB_A(color); + pos[0] = (FXARGB_B(color) * alpha + pos[0] * (255 - alpha)) / 255; + pos[1] = (FXARGB_G(color) * alpha + pos[1] * (255 - alpha)) / 255; + pos[2] = (FXARGB_R(color) * alpha + pos[2] * (255 - alpha)) / 255; + break; + } +- case FXDIB_Rgba: { +- pos[0] = FXARGB_B(color); +- pos[1] = FXARGB_G(color); +- pos[2] = FXARGB_R(color); +- break; +- } +- case FXDIB_Argb: ++ case FXDIB_Format::kArgb: + FXARGB_SETDIB(pos, color); + break; + default: + break; + } + } +- +-void CFX_DIBitmap::DownSampleScanline(int line, +- uint8_t* dest_scan, +- int dest_bpp, +- int dest_width, +- bool bFlipX, +- int clip_left, +- int clip_width) const { +- if (!m_pBuffer) +- return; +- +- int src_Bpp = m_bpp / 8; +- uint8_t* scanline = m_pBuffer.Get() + line * m_Pitch; +- if (src_Bpp == 0) { +- for (int i = 0; i < clip_width; i++) { +- uint32_t dest_x = clip_left + i; +- uint32_t src_x = dest_x * m_Width / dest_width; +- if (bFlipX) { +- src_x = m_Width - src_x - 1; +- } +- src_x %= m_Width; +- dest_scan[i] = (scanline[src_x / 8] & (1 << (7 - src_x % 8))) ? 255 : 0; +- } +- } else if (src_Bpp == 1) { +- for (int i = 0; i < clip_width; i++) { +- uint32_t dest_x = clip_left + i; +- uint32_t src_x = dest_x * m_Width / dest_width; +- if (bFlipX) { +- src_x = m_Width - src_x - 1; +- } +- src_x %= m_Width; +- int dest_pos = i; +- if (m_pPalette) { +- if (!IsCmykImage()) { +- dest_pos *= 3; +- FX_ARGB argb = m_pPalette.get()[scanline[src_x]]; +- dest_scan[dest_pos] = FXARGB_B(argb); +- dest_scan[dest_pos + 1] = FXARGB_G(argb); +- dest_scan[dest_pos + 2] = FXARGB_R(argb); +- } else { +- dest_pos *= 4; +- FX_CMYK cmyk = m_pPalette.get()[scanline[src_x]]; +- dest_scan[dest_pos] = FXSYS_GetCValue(cmyk); +- dest_scan[dest_pos + 1] = FXSYS_GetMValue(cmyk); +- dest_scan[dest_pos + 2] = FXSYS_GetYValue(cmyk); +- dest_scan[dest_pos + 3] = FXSYS_GetKValue(cmyk); +- } +- } else { +- dest_scan[dest_pos] = scanline[src_x]; +- } +- } +- } else { +- for (int i = 0; i < clip_width; i++) { +- uint32_t dest_x = clip_left + i; +- uint32_t src_x = +- bFlipX ? (m_Width - dest_x * m_Width / dest_width - 1) * src_Bpp +- : (dest_x * m_Width / dest_width) * src_Bpp; +- src_x %= m_Width * src_Bpp; +- int dest_pos = i * src_Bpp; +- for (int b = 0; b < src_Bpp; b++) { +- dest_scan[dest_pos + b] = scanline[src_x + b]; +- } +- } +- } +-} ++#endif // defined(_SKIA_SUPPORT_) + + void CFX_DIBitmap::ConvertBGRColorScale(uint32_t forecolor, + uint32_t backcolor) { +@@ -717,17 +607,16 @@ void CFX_DIBitmap::ConvertBGRColorScale(uint32_t forecolor, + int br = FXSYS_GetRValue(backcolor); + int bg = FXSYS_GetGValue(backcolor); + int bb = FXSYS_GetBValue(backcolor); +- if (m_bpp <= 8) { +- if (forecolor == 0 && backcolor == 0xffffff && !m_pPalette) ++ if (GetBppFromFormat(m_Format) <= 8) { ++ if (forecolor == 0 && backcolor == 0xffffff && !HasPalette()) + return; +- if (!m_pPalette) +- BuildPalette(); +- int size = 1 << m_bpp; ++ ++ BuildPalette(); ++ int size = 1 << GetBppFromFormat(m_Format); + for (int i = 0; i < size; ++i) { +- int gray = FXRGB2GRAY(FXARGB_R(m_pPalette.get()[i]), +- FXARGB_G(m_pPalette.get()[i]), +- FXARGB_B(m_pPalette.get()[i])); +- m_pPalette.get()[i] = ++ int gray = FXRGB2GRAY(FXARGB_R(m_palette[i]), FXARGB_G(m_palette[i]), ++ FXARGB_B(m_palette[i])); ++ m_palette[i] = + ArgbEncode(0xff, br + (fr - br) * gray / 255, + bg + (fg - bg) * gray / 255, bb + (fb - bb) * gray / 255); + } +@@ -736,7 +625,7 @@ void CFX_DIBitmap::ConvertBGRColorScale(uint32_t forecolor, + if (forecolor == 0 && backcolor == 0xffffff) { + for (int row = 0; row < m_Height; ++row) { + uint8_t* scanline = m_pBuffer.Get() + row * m_Pitch; +- int gap = m_bpp / 8 - 2; ++ int gap = GetBppFromFormat(m_Format) / 8 - 2; + for (int col = 0; col < m_Width; ++col) { + int gray = FXRGB2GRAY(scanline[2], scanline[1], scanline[0]); + *scanline++ = gray; +@@ -749,7 +638,7 @@ void CFX_DIBitmap::ConvertBGRColorScale(uint32_t forecolor, + } + for (int row = 0; row < m_Height; ++row) { + uint8_t* scanline = m_pBuffer.Get() + row * m_Pitch; +- int gap = m_bpp / 8 - 2; ++ int gap = GetBppFromFormat(m_Format) / 8 - 2; + for (int col = 0; col < m_Width; ++col) { + int gray = FXRGB2GRAY(scanline[2], scanline[1], scanline[0]); + *scanline++ = bb + (fb - bb) * gray / 255; +@@ -760,107 +649,43 @@ void CFX_DIBitmap::ConvertBGRColorScale(uint32_t forecolor, + } + } + +-void CFX_DIBitmap::ConvertCMYKColorScale(uint32_t forecolor, +- uint32_t backcolor) { +- int fc = FXSYS_GetCValue(forecolor); +- int fm = FXSYS_GetMValue(forecolor); +- int fy = FXSYS_GetYValue(forecolor); +- int fk = FXSYS_GetKValue(forecolor); +- int bc = FXSYS_GetCValue(backcolor); +- int bm = FXSYS_GetMValue(backcolor); +- int by = FXSYS_GetYValue(backcolor); +- int bk = FXSYS_GetKValue(backcolor); +- if (m_bpp <= 8) { +- if (forecolor == 0xff && backcolor == 0 && !m_pPalette) +- return; +- if (!m_pPalette) +- BuildPalette(); +- int size = 1 << m_bpp; +- for (int i = 0; i < size; ++i) { +- uint8_t r; +- uint8_t g; +- uint8_t b; +- std::tie(r, g, b) = +- AdobeCMYK_to_sRGB1(FXSYS_GetCValue(m_pPalette.get()[i]), +- FXSYS_GetMValue(m_pPalette.get()[i]), +- FXSYS_GetYValue(m_pPalette.get()[i]), +- FXSYS_GetKValue(m_pPalette.get()[i])); +- int gray = 255 - FXRGB2GRAY(r, g, b); +- m_pPalette.get()[i] = +- CmykEncode(bc + (fc - bc) * gray / 255, bm + (fm - bm) * gray / 255, +- by + (fy - by) * gray / 255, bk + (fk - bk) * gray / 255); +- } +- return; +- } +- if (forecolor == 0xff && backcolor == 0x00) { +- for (int row = 0; row < m_Height; ++row) { +- uint8_t* scanline = m_pBuffer.Get() + row * m_Pitch; +- for (int col = 0; col < m_Width; ++col) { +- uint8_t r; +- uint8_t g; +- uint8_t b; +- std::tie(r, g, b) = AdobeCMYK_to_sRGB1(scanline[0], scanline[1], +- scanline[2], scanline[3]); +- *scanline++ = 0; +- *scanline++ = 0; +- *scanline++ = 0; +- *scanline++ = 255 - FXRGB2GRAY(r, g, b); +- } +- } +- return; +- } +- for (int row = 0; row < m_Height; ++row) { +- uint8_t* scanline = m_pBuffer.Get() + row * m_Pitch; +- for (int col = 0; col < m_Width; ++col) { +- uint8_t r; +- uint8_t g; +- uint8_t b; +- std::tie(r, g, b) = AdobeCMYK_to_sRGB1(scanline[0], scanline[1], +- scanline[2], scanline[3]); +- int gray = 255 - FXRGB2GRAY(r, g, b); +- *scanline++ = bc + (fc - bc) * gray / 255; +- *scanline++ = bm + (fm - bm) * gray / 255; +- *scanline++ = by + (fy - by) * gray / 255; +- *scanline++ = bk + (fk - bk) * gray / 255; +- } +- } +-} +- + bool CFX_DIBitmap::ConvertColorScale(uint32_t forecolor, uint32_t backcolor) { +- if (!m_pBuffer || IsAlphaMask()) ++ if (!m_pBuffer || IsMaskFormat()) + return false; + +- if (IsCmykImage()) +- ConvertCMYKColorScale(forecolor, backcolor); +- else +- ConvertBGRColorScale(forecolor, backcolor); ++ ConvertBGRColorScale(forecolor, backcolor); + return true; + } + + // static +-bool CFX_DIBitmap::CalculatePitchAndSize(int height, +- int width, +- FXDIB_Format format, +- uint32_t* pitch, +- uint32_t* size) { ++absl::optional CFX_DIBitmap::CalculatePitchAndSize( ++ int width, ++ int height, ++ FXDIB_Format format, ++ uint32_t pitch) { + if (width <= 0 || height <= 0) +- return false; ++ return absl::nullopt; + + int bpp = GetBppFromFormat(format); + if (!bpp) +- return false; ++ return absl::nullopt; + +- if ((INT_MAX - 31) / width < bpp) +- return false; ++ uint32_t actual_pitch = pitch; ++ if (actual_pitch == 0) { ++ absl::optional pitch32 = fxge::CalculatePitch32(bpp, width); ++ if (!pitch32.has_value()) { ++ return absl::nullopt; ++ } + +- if (!*pitch) +- *pitch = static_cast((width * bpp + 31) / 32 * 4); ++ actual_pitch = pitch32.value(); ++ } + +- if ((1 << 30) / *pitch < static_cast(height)) +- return false; ++ FX_SAFE_UINT32 safe_size = actual_pitch; ++ safe_size *= height; ++ if (!safe_size.IsValid()) ++ return absl::nullopt; + +- *size = *pitch * static_cast(height); +- return true; ++ return PitchAndSize{actual_pitch, safe_size.ValueOrDie()}; + } + + bool CFX_DIBitmap::CompositeBitmap(int dest_left, +@@ -873,13 +698,17 @@ bool CFX_DIBitmap::CompositeBitmap(int dest_left, + BlendMode blend_type, + const CFX_ClipRgn* pClipRgn, + bool bRgbByteOrder) { ++ if (pSrcBitmap->IsMaskFormat()) { ++ // Should have called CompositeMask(). ++ NOTREACHED(); ++ return false; ++ } ++ + if (!m_pBuffer) + return false; + +- if (pSrcBitmap->IsAlphaMask() || m_bpp < 8) { +- NOTREACHED(); ++ if (GetBppFromFormat(m_Format) < 8) + return false; +- } + + if (!GetOverlapRect(dest_left, dest_top, width, height, + pSrcBitmap->GetWidth(), pSrcBitmap->GetHeight(), src_left, +@@ -889,47 +718,37 @@ bool CFX_DIBitmap::CompositeBitmap(int dest_left, + + RetainPtr pClipMask; + FX_RECT clip_box; +- if (pClipRgn && pClipRgn->GetType() != CFX_ClipRgn::RectI) { +- ASSERT(pClipRgn->GetType() == CFX_ClipRgn::MaskF); ++ if (pClipRgn && pClipRgn->GetType() != CFX_ClipRgn::kRectI) { + pClipMask = pClipRgn->GetMask(); + clip_box = pClipRgn->GetBox(); + } + CFX_ScanlineCompositor compositor; +- if (!compositor.Init(GetFormat(), pSrcBitmap->GetFormat(), width, +- pSrcBitmap->GetPalette(), 0, blend_type, ++ if (!compositor.Init(GetFormat(), pSrcBitmap->GetFormat(), ++ pSrcBitmap->GetPaletteSpan(), 0, blend_type, + pClipMask != nullptr, bRgbByteOrder)) { + return false; + } +- int dest_Bpp = m_bpp / 8; +- int src_Bpp = pSrcBitmap->GetBPP() / 8; +- bool bRgb = src_Bpp > 1 && !pSrcBitmap->IsCmykImage(); +- RetainPtr pSrcAlphaMask = pSrcBitmap->m_pAlphaMask; ++ const int dest_Bpp = GetBppFromFormat(m_Format) / 8; ++ const int src_Bpp = pSrcBitmap->GetBPP() / 8; ++ const bool bRgb = src_Bpp > 1; ++ if (!bRgb && !pSrcBitmap->HasPalette()) ++ return false; ++ + for (int row = 0; row < height; row++) { +- uint8_t* dest_scan = +- m_pBuffer.Get() + (dest_top + row) * m_Pitch + dest_left * dest_Bpp; +- const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left * src_Bpp; +- const uint8_t* src_scan_extra_alpha = +- pSrcAlphaMask ? pSrcAlphaMask->GetScanline(src_top + row) + src_left +- : nullptr; +- uint8_t* dst_scan_extra_alpha = +- m_pAlphaMask +- ? m_pAlphaMask->GetWritableScanline(dest_top + row) + dest_left +- : nullptr; +- const uint8_t* clip_scan = nullptr; ++ pdfium::span dest_scan = ++ GetWritableScanline(dest_top + row).subspan(dest_left * dest_Bpp); ++ pdfium::span src_scan = ++ pSrcBitmap->GetScanline(src_top + row).subspan(src_left * src_Bpp); ++ pdfium::span clip_scan; + if (pClipMask) { +- clip_scan = pClipMask->m_pBuffer.Get() + +- (dest_top + row - clip_box.top) * pClipMask->m_Pitch + +- (dest_left - clip_box.left); ++ clip_scan = pClipMask->GetWritableScanline(dest_top + row - clip_box.top) ++ .subspan(dest_left - clip_box.left); + } + if (bRgb) { +- compositor.CompositeRgbBitmapLine(dest_scan, src_scan, width, clip_scan, +- src_scan_extra_alpha, +- dst_scan_extra_alpha); ++ compositor.CompositeRgbBitmapLine(dest_scan, src_scan, width, clip_scan); + } else { + compositor.CompositePalBitmapLine(dest_scan, src_scan, src_left, width, +- clip_scan, src_scan_extra_alpha, +- dst_scan_extra_alpha); ++ clip_scan); + } + } + return true; +@@ -946,13 +765,17 @@ bool CFX_DIBitmap::CompositeMask(int dest_left, + BlendMode blend_type, + const CFX_ClipRgn* pClipRgn, + bool bRgbByteOrder) { ++ if (!pMask->IsMaskFormat()) { ++ // Should have called CompositeBitmap(). ++ NOTREACHED(); ++ return false; ++ } ++ + if (!m_pBuffer) + return false; + +- if (!pMask->IsAlphaMask() || m_bpp < 8) { +- NOTREACHED(); ++ if (GetBppFromFormat(m_Format) < 8) + return false; +- } + + if (!GetOverlapRect(dest_left, dest_top, width, height, pMask->GetWidth(), + pMask->GetHeight(), src_left, src_top, pClipRgn)) { +@@ -965,53 +788,76 @@ bool CFX_DIBitmap::CompositeMask(int dest_left, + + RetainPtr pClipMask; + FX_RECT clip_box; +- if (pClipRgn && pClipRgn->GetType() != CFX_ClipRgn::RectI) { +- ASSERT(pClipRgn->GetType() == CFX_ClipRgn::MaskF); ++ if (pClipRgn && pClipRgn->GetType() != CFX_ClipRgn::kRectI) { + pClipMask = pClipRgn->GetMask(); + clip_box = pClipRgn->GetBox(); + } + int src_bpp = pMask->GetBPP(); + int Bpp = GetBPP() / 8; + CFX_ScanlineCompositor compositor; +- if (!compositor.Init(GetFormat(), pMask->GetFormat(), width, nullptr, color, +- blend_type, pClipMask != nullptr, bRgbByteOrder)) { ++ if (!compositor.Init(GetFormat(), pMask->GetFormat(), {}, color, blend_type, ++ pClipMask != nullptr, bRgbByteOrder)) { + return false; + } + for (int row = 0; row < height; row++) { +- uint8_t* dest_scan = +- m_pBuffer.Get() + (dest_top + row) * m_Pitch + dest_left * Bpp; +- const uint8_t* src_scan = pMask->GetScanline(src_top + row); +- uint8_t* dst_scan_extra_alpha = +- m_pAlphaMask +- ? m_pAlphaMask->GetWritableScanline(dest_top + row) + dest_left +- : nullptr; +- const uint8_t* clip_scan = nullptr; ++ pdfium::span dest_scan = ++ GetWritableScanline(dest_top + row).subspan(dest_left * Bpp); ++ pdfium::span src_scan = pMask->GetScanline(src_top + row); ++ pdfium::span clip_scan; + if (pClipMask) { +- clip_scan = pClipMask->m_pBuffer.Get() + +- (dest_top + row - clip_box.top) * pClipMask->m_Pitch + +- (dest_left - clip_box.left); ++ clip_scan = pClipMask->GetScanline(dest_top + row - clip_box.top) ++ .subspan(dest_left - clip_box.left); + } + if (src_bpp == 1) { + compositor.CompositeBitMaskLine(dest_scan, src_scan, src_left, width, +- clip_scan, dst_scan_extra_alpha); ++ clip_scan); + } else { +- compositor.CompositeByteMaskLine(dest_scan, src_scan + src_left, width, +- clip_scan, dst_scan_extra_alpha); ++ compositor.CompositeByteMaskLine(dest_scan, src_scan.subspan(src_left), ++ width, clip_scan); + } + } + return true; + } + ++void CFX_DIBitmap::CompositeOneBPPMask(int dest_left, ++ int dest_top, ++ int width, ++ int height, ++ const RetainPtr& pSrcBitmap, ++ int src_left, ++ int src_top) { ++ if (GetBPP() != 1) { ++ return; ++ } ++ ++ if (!GetOverlapRect(dest_left, dest_top, width, height, ++ pSrcBitmap->GetWidth(), pSrcBitmap->GetHeight(), src_left, ++ src_top, nullptr)) { ++ return; ++ } ++ ++ for (int row = 0; row < height; ++row) { ++ uint8_t* dest_scan = m_pBuffer.Get() + (dest_top + row) * m_Pitch; ++ const uint8_t* src_scan = pSrcBitmap->GetScanline(src_top + row).data(); ++ for (int col = 0; col < width; ++col) { ++ int src_idx = src_left + col; ++ int dest_idx = dest_left + col; ++ if (src_scan[src_idx / 8] & (1 << (7 - src_idx % 8))) { ++ dest_scan[dest_idx / 8] |= 1 << (7 - dest_idx % 8); ++ } ++ } ++ } ++} ++ + bool CFX_DIBitmap::CompositeRect(int left, + int top, + int width, + int height, +- uint32_t color, +- int alpha_flag) { ++ uint32_t color) { + if (!m_pBuffer) + return false; + +- int src_alpha = (alpha_flag >> 8) ? (alpha_flag & 0xff) : FXARGB_A(color); ++ int src_alpha = FXARGB_A(color); + if (src_alpha == 0) + return true; + +@@ -1021,29 +867,12 @@ bool CFX_DIBitmap::CompositeRect(int left, + return true; + + width = rect.Width(); +- uint32_t dst_color; +- if (alpha_flag >> 8) +- dst_color = FXCMYK_TODIB(color); +- else +- dst_color = FXARGB_TODIB(color); ++ uint32_t dst_color = color; + uint8_t* color_p = reinterpret_cast(&dst_color); +- if (m_bpp == 8) { +- uint8_t gray = 255; +- if (!IsAlphaMask()) { +- if (alpha_flag >> 8) { +- uint8_t r; +- uint8_t g; +- uint8_t b; +- std::tie(r, g, b) = +- AdobeCMYK_to_sRGB1(color_p[0], color_p[1], color_p[2], color_p[3]); +- gray = FXRGB2GRAY(r, g, b); +- } else { +- gray = (uint8_t)FXRGB2GRAY((int)color_p[2], color_p[1], color_p[0]); +- } +- if (IsCmykImage()) { +- gray = ~gray; +- } +- } ++ if (GetBppFromFormat(m_Format) == 8) { ++ uint8_t gray = IsMaskFormat() ? 255 ++ : (uint8_t)FXRGB2GRAY((int)color_p[2], ++ color_p[1], color_p[0]); + for (int row = rect.top; row < rect.bottom; row++) { + uint8_t* dest_scan = m_pBuffer.Get() + row * m_Pitch + rect.left; + if (src_alpha == 255) { +@@ -1057,25 +886,24 @@ bool CFX_DIBitmap::CompositeRect(int left, + } + return true; + } +- if (m_bpp == 1) { +- ASSERT(!IsCmykImage()); +- ASSERT(static_cast(alpha_flag >> 8) == 0); +- ++ if (GetBppFromFormat(m_Format) == 1) { + int left_shift = rect.left % 8; + int right_shift = rect.right % 8; + int new_width = rect.right / 8 - rect.left / 8; + int index = 0; +- if (m_pPalette) { ++ if (HasPalette()) { + for (int i = 0; i < 2; i++) { +- if (m_pPalette.get()[i] == color) ++ if (GetPaletteSpan()[i] == color) + index = i; + } + } else { + index = (static_cast(color) == 0xff) ? 1 : 0; + } + for (int row = rect.top; row < rect.bottom; row++) { +- uint8_t* dest_scan_top = GetWritableScanline(row) + rect.left / 8; +- uint8_t* dest_scan_top_r = GetWritableScanline(row) + rect.right / 8; ++ uint8_t* dest_scan_top = ++ GetWritableScanline(row).subspan(rect.left / 8).data(); ++ uint8_t* dest_scan_top_r = ++ GetWritableScanline(row).subspan(rect.right / 8).data(); + uint8_t left_flag = *dest_scan_top & (255 << (8 - left_shift)); + uint8_t right_flag = *dest_scan_top_r & (255 >> right_shift); + if (new_width) { +@@ -1098,33 +926,21 @@ bool CFX_DIBitmap::CompositeRect(int left, + return true; + } + +- if (m_bpp < 24) { ++ if (GetBppFromFormat(m_Format) < 24) { + NOTREACHED(); + return false; + } + +- if (!(alpha_flag >> 8) && IsCmykImage()) +- return false; +- +- if (alpha_flag >> 8 && !IsCmykImage()) { +- std::tie(color_p[2], color_p[1], color_p[0]) = +- AdobeCMYK_to_sRGB1(FXSYS_GetCValue(color), FXSYS_GetMValue(color), +- FXSYS_GetYValue(color), FXSYS_GetKValue(color)); ++ color_p[3] = static_cast(src_alpha); ++ int Bpp = GetBppFromFormat(m_Format) / 8; ++ const bool bAlpha = IsAlphaFormat(); ++ if (bAlpha) { ++ // Other formats with alpha have already been handled above. ++ DCHECK_EQ(GetFormat(), FXDIB_Format::kArgb); + } +- if (!IsCmykImage()) +- color_p[3] = static_cast(src_alpha); +- int Bpp = m_bpp / 8; +- bool bAlpha = HasAlpha(); +- bool bArgb = GetFormat() == FXDIB_Argb; + if (src_alpha == 255) { + for (int row = rect.top; row < rect.bottom; row++) { + uint8_t* dest_scan = m_pBuffer.Get() + row * m_Pitch + rect.left * Bpp; +- uint8_t* dest_scan_alpha = +- m_pAlphaMask ? m_pAlphaMask->GetWritableScanline(row) + rect.left +- : nullptr; +- if (dest_scan_alpha) +- memset(dest_scan_alpha, 0xff, width); +- + if (Bpp == 4) { + uint32_t* scan = reinterpret_cast(dest_scan); + for (int col = 0; col < width; col++) +@@ -1142,47 +958,24 @@ bool CFX_DIBitmap::CompositeRect(int left, + for (int row = rect.top; row < rect.bottom; row++) { + uint8_t* dest_scan = m_pBuffer.Get() + row * m_Pitch + rect.left * Bpp; + if (bAlpha) { +- if (bArgb) { +- for (int col = 0; col < width; col++) { +- uint8_t back_alpha = dest_scan[3]; +- if (back_alpha == 0) { +- FXARGB_SETDIB(dest_scan, ArgbEncode(src_alpha, color_p[2], +- color_p[1], color_p[0])); +- dest_scan += 4; +- continue; +- } +- uint8_t dest_alpha = +- back_alpha + src_alpha - back_alpha * src_alpha / 255; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[0], alpha_ratio); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[1], alpha_ratio); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[2], alpha_ratio); +- dest_scan++; +- *dest_scan++ = dest_alpha; +- } +- } else { +- uint8_t* dest_scan_alpha = +- m_pAlphaMask->GetWritableScanline(row) + rect.left; +- for (int col = 0; col < width; col++) { +- uint8_t back_alpha = *dest_scan_alpha; +- if (back_alpha == 0) { +- *dest_scan_alpha++ = src_alpha; +- memcpy(dest_scan, color_p, Bpp); +- dest_scan += Bpp; +- continue; +- } +- uint8_t dest_alpha = +- back_alpha + src_alpha - back_alpha * src_alpha / 255; +- *dest_scan_alpha++ = dest_alpha; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- for (int comps = 0; comps < Bpp; comps++) { +- *dest_scan = +- FXDIB_ALPHA_MERGE(*dest_scan, color_p[comps], alpha_ratio); +- dest_scan++; +- } ++ for (int col = 0; col < width; col++) { ++ uint8_t back_alpha = dest_scan[3]; ++ if (back_alpha == 0) { ++ FXARGB_SETDIB(dest_scan, ArgbEncode(src_alpha, color_p[2], color_p[1], ++ color_p[0])); ++ dest_scan += 4; ++ continue; + } ++ uint8_t dest_alpha = ++ back_alpha + src_alpha - back_alpha * src_alpha / 255; ++ int alpha_ratio = src_alpha * 255 / dest_alpha; ++ *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[0], alpha_ratio); ++ dest_scan++; ++ *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[1], alpha_ratio); ++ dest_scan++; ++ *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, color_p[2], alpha_ratio); ++ dest_scan++; ++ *dest_scan++ = dest_alpha; + } + } else { + for (int col = 0; col < width; col++) { +@@ -1201,17 +994,21 @@ bool CFX_DIBitmap::CompositeRect(int left, + } + + bool CFX_DIBitmap::ConvertFormat(FXDIB_Format dest_format) { +- FXDIB_Format src_format = GetFormat(); +- if (dest_format == src_format) ++ DCHECK(dest_format == FXDIB_Format::k8bppMask || ++ dest_format == FXDIB_Format::kArgb || ++ dest_format == FXDIB_Format::kRgb32 || ++ dest_format == FXDIB_Format::kRgb); ++ ++ if (dest_format == m_Format) + return true; + +- if (dest_format == FXDIB_8bppMask && src_format == FXDIB_8bppRgb && +- !m_pPalette) { +- m_AlphaFlag = 1; ++ if (dest_format == FXDIB_Format::k8bppMask && ++ m_Format == FXDIB_Format::k8bppRgb && !HasPalette()) { ++ m_Format = FXDIB_Format::k8bppMask; + return true; + } +- if (dest_format == FXDIB_Argb && src_format == FXDIB_Rgb32) { +- m_AlphaFlag = 2; ++ if (dest_format == FXDIB_Format::kArgb && m_Format == FXDIB_Format::kRgb32) { ++ m_Format = FXDIB_Format::kArgb; + for (int row = 0; row < m_Height; row++) { + uint8_t* scanline = m_pBuffer.Get() + row * m_Pitch + 3; + for (int col = 0; col < m_Width; col++) { +@@ -1222,53 +1019,26 @@ bool CFX_DIBitmap::ConvertFormat(FXDIB_Format dest_format) { + return true; + } + int dest_bpp = GetBppFromFormat(dest_format); +- int dest_pitch = (dest_bpp * m_Width + 31) / 32 * 4; ++ int dest_pitch = fxge::CalculatePitch32OrDie(dest_bpp, m_Width); ++ const size_t dest_buf_size = dest_pitch * m_Height + 4; + std::unique_ptr dest_buf( +- FX_TryAlloc(uint8_t, dest_pitch * m_Height + 4)); ++ FX_TryAlloc(uint8_t, dest_buf_size)); + if (!dest_buf) + return false; + +- RetainPtr pAlphaMask; +- if (dest_format == FXDIB_Argb) { +- memset(dest_buf.get(), 0xff, dest_pitch * m_Height + 4); +- if (m_pAlphaMask) { +- for (int row = 0; row < m_Height; row++) { +- uint8_t* pDstScanline = dest_buf.get() + row * dest_pitch + 3; +- const uint8_t* pSrcScanline = m_pAlphaMask->GetScanline(row); +- for (int col = 0; col < m_Width; col++) { +- *pDstScanline = *pSrcScanline++; +- pDstScanline += 4; +- } +- } +- } +- } else if (GetIsAlphaFromFormat(dest_format)) { +- if (src_format == FXDIB_Argb) { +- pAlphaMask = CloneAlphaMask(); +- if (!pAlphaMask) +- return false; +- } else { +- if (!m_pAlphaMask) { +- if (!BuildAlphaMask()) +- return false; +- pAlphaMask = std::move(m_pAlphaMask); +- } else { +- pAlphaMask = m_pAlphaMask; +- } +- } ++ if (dest_format == FXDIB_Format::kArgb) { ++ memset(dest_buf.get(), 0xff, dest_buf_size); + } +- bool ret = false; + RetainPtr holder(this); +- std::unique_ptr pal_8bpp; +- ret = ConvertBuffer(dest_format, dest_buf.get(), dest_pitch, m_Width, +- m_Height, holder, 0, 0, &pal_8bpp); +- if (!ret) ++ DataVector pal_8bpp; ++ if (!ConvertBuffer(dest_format, {dest_buf.get(), dest_buf_size}, dest_pitch, ++ m_Width, m_Height, holder, 0, 0, &pal_8bpp)) { + return false; ++ } + +- m_pAlphaMask = pAlphaMask; +- m_pPalette = std::move(pal_8bpp); ++ m_palette = std::move(pal_8bpp); + m_pBuffer = std::move(dest_buf); +- m_bpp = GetBppFromFormat(dest_format); +- m_AlphaFlag = GetAlphaFlagFromFormat(dest_format); ++ m_Format = dest_format; + m_Pitch = dest_pitch; + return true; + } +diff --git a/core/fxge/dib/cfx_dibitmap.h b/core/fxge/dib/cfx_dibitmap.h +index ce53df8d4..58171554d 100644 +--- a/core/fxge/dib/cfx_dibitmap.h ++++ b/core/fxge/dib/cfx_dibitmap.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,20 +7,23 @@ + #ifndef CORE_FXGE_DIB_CFX_DIBITMAP_H_ + #define CORE_FXGE_DIB_CFX_DIBITMAP_H_ + +-#include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/fx_memory_wrappers.h" + #include "core/fxcrt/maybe_owned.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxge/dib/cfx_dibbase.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + +-class CFX_DIBitmap : public CFX_DIBBase { ++class CFX_DIBitmap final : public CFX_DIBBase { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ struct PitchAndSize { ++ uint32_t pitch; ++ uint32_t size; ++ }; + +- bool Create(int width, int height, FXDIB_Format format); ++ CONSTRUCT_VIA_MAKE_RETAIN; + ++ bool Create(int width, int height, FXDIB_Format format); + bool Create(int width, + int height, + FXDIB_Format format, +@@ -30,27 +33,24 @@ class CFX_DIBitmap : public CFX_DIBBase { + bool Copy(const RetainPtr& pSrc); + + // CFX_DIBBase +- uint8_t* GetBuffer() const override; +- const uint8_t* GetScanline(int line) const override; +- void DownSampleScanline(int line, +- uint8_t* dest_scan, +- int dest_bpp, +- int dest_width, +- bool bFlipX, +- int clip_left, +- int clip_width) const override; ++ pdfium::span GetBuffer() const override; ++ pdfium::span GetScanline(int line) const override; ++ size_t GetEstimatedImageMemoryBurden() const override; + + void TakeOver(RetainPtr&& pSrcBitmap); + bool ConvertFormat(FXDIB_Format format); + void Clear(uint32_t color); + ++#if defined(_SKIA_SUPPORT_) + uint32_t GetPixel(int x, int y) const; + void SetPixel(int x, int y, uint32_t color); ++#endif // defined(_SKIA_SUPPORT_) + +- bool LoadChannelFromAlpha(FXDIB_Channel destChannel, +- const RetainPtr& pSrcBitmap); +- bool LoadChannel(FXDIB_Channel destChannel, int value); ++ bool SetRedFromBitmap(const RetainPtr& pSrcBitmap); ++ bool SetAlphaFromBitmap(const RetainPtr& pSrcBitmap); ++ bool SetUniformOpaqueAlpha(); + ++ // TODO(crbug.com/pdfium/2007): Migrate callers to `CFX_RenderDevice`. + bool MultiplyAlpha(int alpha); + bool MultiplyAlpha(const RetainPtr& pSrcBitmap); + +@@ -85,45 +85,64 @@ class CFX_DIBitmap : public CFX_DIBBase { + const CFX_ClipRgn* pClipRgn, + bool bRgbByteOrder); + ++ void CompositeOneBPPMask(int dest_left, ++ int dest_top, ++ int width, ++ int height, ++ const RetainPtr& pSrcBitmap, ++ int src_left, ++ int src_top); ++ + bool CompositeRect(int dest_left, + int dest_top, + int width, + int height, +- uint32_t color, +- int alpha_flag); ++ uint32_t color); + + bool ConvertColorScale(uint32_t forecolor, uint32_t backcolor); + +- static bool CalculatePitchAndSize(int height, +- int width, +- FXDIB_Format format, +- uint32_t* pitch, +- uint32_t* size); +- +-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_ ++ // |width| and |height| must be greater than 0. ++ // |format| must have a valid bits per pixel count. ++ // If |pitch| is zero, then the actual pitch will be calculated based on ++ // |width| and |format|. ++ // If |pitch| is non-zero, then that be used as the actual pitch. ++ // The actual pitch will be used to calculate the size. ++ // Returns the calculated pitch and size on success, or nullopt on failure. ++ static absl::optional CalculatePitchAndSize(int width, ++ int height, ++ FXDIB_Format format, ++ uint32_t pitch); ++ ++#if defined(_SKIA_SUPPORT_) ++ // Converts to pre-multiplied alpha if necessary. + void PreMultiply(); +-#endif +-#if defined _SKIA_SUPPORT_PATHS_ ++ ++ // Converts to un-pre-multiplied alpha if necessary. + void UnPreMultiply(); ++ ++ // Forces pre-multiplied alpha without conversion. ++ // TODO(crbug.com/pdfium/2011): Remove the need for this. ++ void ForcePreMultiply(); ++ ++ // Forces un-pre-multiplied alpha without conversion. ++ // TODO(crbug.com/pdfium/2011): Remove the need for this. ++ void ForceUnPreMultiply(); + #endif + +- protected: +- CFX_DIBitmap(); +- CFX_DIBitmap(const CFX_DIBitmap& src); +- ~CFX_DIBitmap() override; ++ private: ++ enum class Channel : uint8_t { kRed, kAlpha }; + +-#if defined _SKIA_SUPPORT_PATHS_ ++#if defined(_SKIA_SUPPORT_) + enum class Format { kCleared, kPreMultiplied, kUnPreMultiplied }; + #endif + +- MaybeOwned m_pBuffer; +-#if defined _SKIA_SUPPORT_PATHS_ +- Format m_nFormat; +-#endif ++ CFX_DIBitmap(); ++ CFX_DIBitmap(const CFX_DIBitmap& src); ++ ~CFX_DIBitmap() override; + +- private: ++ bool SetChannelFromBitmap(Channel destChannel, ++ const RetainPtr& pSrcBitmap); + void ConvertBGRColorScale(uint32_t forecolor, uint32_t backcolor); +- void ConvertCMYKColorScale(uint32_t forecolor, uint32_t backcolor); + bool TransferWithUnequalFormats(FXDIB_Format dest_format, + int dest_left, + int dest_top, +@@ -146,6 +165,11 @@ class CFX_DIBitmap : public CFX_DIBBase { + const RetainPtr& pSrcBitmap, + int src_left, + int src_top); ++ ++ MaybeOwned m_pBuffer; ++#if defined(_SKIA_SUPPORT_) ++ Format m_nFormat = Format::kCleared; ++#endif + }; + + #endif // CORE_FXGE_DIB_CFX_DIBITMAP_H_ +diff --git a/core/fxge/dib/cfx_dibitmap_unittest.cpp b/core/fxge/dib/cfx_dibitmap_unittest.cpp +index 67ca7055a..ce79a0180 100644 +--- a/core/fxge/dib/cfx_dibitmap_unittest.cpp ++++ b/core/fxge/dib/cfx_dibitmap_unittest.cpp +@@ -1,15 +1,202 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxge/dib/cfx_dibitmap.h" + ++#include ++ ++#include "core/fxcrt/fx_coordinates.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "testing/gmock/include/gmock/gmock.h" + #include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/base/span.h" ++ ++namespace { ++ ++using ::testing::ElementsAre; ++ ++} // namespace + + TEST(CFX_DIBitmap, Create) { + auto pBitmap = pdfium::MakeRetain(); +- EXPECT_FALSE(pBitmap->Create(400, 300, FXDIB_Invalid)); ++ EXPECT_FALSE(pBitmap->Create(400, 300, FXDIB_Format::kInvalid)); + + pBitmap = pdfium::MakeRetain(); +- EXPECT_TRUE(pBitmap->Create(400, 300, FXDIB_1bppRgb)); ++ EXPECT_TRUE(pBitmap->Create(400, 300, FXDIB_Format::k1bppRgb)); ++} ++ ++TEST(CFX_DIBitmap, CalculatePitchAndSizeGood) { ++ // Simple case with no provided pitch. ++ absl::optional result = ++ CFX_DIBitmap::CalculatePitchAndSize(100, 200, FXDIB_Format::kArgb, 0); ++ ASSERT_TRUE(result.has_value()); ++ EXPECT_EQ(400u, result.value().pitch); ++ EXPECT_EQ(80000u, result.value().size); ++ ++ // Simple case with no provided pitch and different format. ++ result = ++ CFX_DIBitmap::CalculatePitchAndSize(100, 200, FXDIB_Format::k8bppRgb, 0); ++ ASSERT_TRUE(result.has_value()); ++ EXPECT_EQ(100u, result.value().pitch); ++ EXPECT_EQ(20000u, result.value().size); ++ ++ // Simple case with provided pitch. ++ result = ++ CFX_DIBitmap::CalculatePitchAndSize(100, 200, FXDIB_Format::kArgb, 400); ++ ASSERT_TRUE(result.has_value()); ++ EXPECT_EQ(400u, result.value().pitch); ++ EXPECT_EQ(80000u, result.value().size); ++ ++ // Simple case with provided pitch, but pitch does not match width * bpp. ++ result = ++ CFX_DIBitmap::CalculatePitchAndSize(100, 200, FXDIB_Format::kArgb, 355); ++ ASSERT_TRUE(result.has_value()); ++ EXPECT_EQ(355u, result.value().pitch); ++ EXPECT_EQ(71000u, result.value().size); ++} ++ ++TEST(CFX_DIBitmap, CalculatePitchAndSizeBad) { ++ // Bad width / height. ++ static const CFX_Size kBadDimensions[] = { ++ {0, 0}, {-1, -1}, {-1, 0}, {0, -1}, ++ {0, 200}, {100, 0}, {-1, 200}, {100, -1}, ++ }; ++ for (const auto& dimension : kBadDimensions) { ++ EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize( ++ dimension.width, dimension.height, FXDIB_Format::kArgb, 0)); ++ EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize( ++ dimension.width, dimension.height, FXDIB_Format::kArgb, 1)); ++ } ++ ++ // Bad format. ++ EXPECT_FALSE( ++ CFX_DIBitmap::CalculatePitchAndSize(100, 200, FXDIB_Format::kInvalid, 0)); ++ EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize( ++ 100, 200, FXDIB_Format::kInvalid, 800)); ++ ++ // Overflow cases with calculated pitch. ++ EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize(1073747000, 1, ++ FXDIB_Format::kArgb, 0)); ++ EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize(1048576, 1024, ++ FXDIB_Format::kArgb, 0)); ++ EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize(4194304, 1024, ++ FXDIB_Format::k8bppRgb, 0)); ++ ++ // Overflow cases with provided pitch. ++ EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize( ++ 2147484000u, 1, FXDIB_Format::kArgb, 2147484000u)); ++ EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize( ++ 1048576, 1024, FXDIB_Format::kArgb, 4194304)); ++ EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize( ++ 4194304, 1024, FXDIB_Format::k8bppRgb, 4194304)); ++} ++ ++TEST(CFX_DIBitmap, CalculatePitchAndSizeBoundary) { ++ // Test boundary condition for pitch overflow. ++ absl::optional result = ++ CFX_DIBitmap::CalculatePitchAndSize(536870908, 4, FXDIB_Format::k8bppRgb, ++ 0); ++ ASSERT_TRUE(result); ++ EXPECT_EQ(536870908u, result.value().pitch); ++ EXPECT_EQ(2147483632u, result.value().size); ++ EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize(536870909, 4, ++ FXDIB_Format::k8bppRgb, 0)); ++ ++ // Test boundary condition for size overflow. ++ result = CFX_DIBitmap::CalculatePitchAndSize(68174084, 63, ++ FXDIB_Format::k8bppRgb, 0); ++ ASSERT_TRUE(result); ++ EXPECT_EQ(68174084u, result.value().pitch); ++ EXPECT_EQ(4294967292u, result.value().size); ++ EXPECT_FALSE(CFX_DIBitmap::CalculatePitchAndSize(68174085, 63, ++ FXDIB_Format::k8bppRgb, 0)); ++} ++ ++#if defined(_SKIA_SUPPORT_) ++TEST(CFX_DIBitmap, PreMultiply_FromCleared) { ++ auto bitmap = pdfium::MakeRetain(); ++ ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb)); ++ FXARGB_SETDIB(bitmap->GetBuffer().data(), 0x7f'ff'ff'ff); ++ ++ bitmap->PreMultiply(); ++ ++ EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0x7f, 0x7f, 0x7f, 0x7f)); ++} ++ ++TEST(CFX_DIBitmap, UnPreMultiply_FromCleared) { ++ auto bitmap = pdfium::MakeRetain(); ++ ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb)); ++ FXARGB_SETDIB(bitmap->GetBuffer().data(), 0x7f'7f'7f'7f); ++ ++ bitmap->UnPreMultiply(); ++ ++ EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0xff, 0xff, 0xff, 0x7f)); ++} ++ ++TEST(CFX_DIBitmap, PreMultiply_FromPreMultiplied) { ++ auto bitmap = pdfium::MakeRetain(); ++ ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb)); ++ bitmap->PreMultiply(); ++ FXARGB_SETDIB(bitmap->GetBuffer().data(), 0x7f'7f'7f'7f); ++ ++ bitmap->PreMultiply(); ++ ++ EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0x7f, 0x7f, 0x7f, 0x7f)); ++} ++ ++TEST(CFX_DIBitmap, UnPreMultiply_FromPreMultiplied) { ++ auto bitmap = pdfium::MakeRetain(); ++ ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb)); ++ bitmap->PreMultiply(); ++ FXARGB_SETDIB(bitmap->GetBuffer().data(), 0x7f'7f'7f'7f); ++ ++ bitmap->UnPreMultiply(); ++ ++ EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0xff, 0xff, 0xff, 0x7f)); ++} ++ ++TEST(CFX_DIBitmap, PreMultiply_FromUnPreMultiplied) { ++ auto bitmap = pdfium::MakeRetain(); ++ ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb)); ++ bitmap->UnPreMultiply(); ++ FXARGB_SETDIB(bitmap->GetBuffer().data(), 0x7f'ff'ff'ff); ++ ++ bitmap->PreMultiply(); ++ ++ EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0x7f, 0x7f, 0x7f, 0x7f)); ++} ++ ++TEST(CFX_DIBitmap, UnPreMultiply_FromUnPreMultiplied) { ++ auto bitmap = pdfium::MakeRetain(); ++ ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb)); ++ bitmap->UnPreMultiply(); ++ FXARGB_SETDIB(bitmap->GetBuffer().data(), 0x7f'ff'ff'ff); ++ ++ bitmap->UnPreMultiply(); ++ ++ EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0xff, 0xff, 0xff, 0x7f)); ++} ++ ++TEST(CFX_DIBitmap, ForcePreMultiply) { ++ auto bitmap = pdfium::MakeRetain(); ++ ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb)); ++ FXARGB_SETDIB(bitmap->GetBuffer().data(), 0x7f'7f'7f'7f); ++ ++ bitmap->ForcePreMultiply(); ++ ++ bitmap->PreMultiply(); ++ EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0x7f, 0x7f, 0x7f, 0x7f)); ++} ++ ++TEST(CFX_DIBitmap, ForceUnPreMultiply) { ++ auto bitmap = pdfium::MakeRetain(); ++ ASSERT_TRUE(bitmap->Create(1, 1, FXDIB_Format::kArgb)); ++ FXARGB_SETDIB(bitmap->GetBuffer().data(), 0x7f'ff'ff'ff); ++ ++ bitmap->ForceUnPreMultiply(); ++ ++ bitmap->UnPreMultiply(); ++ EXPECT_THAT(bitmap->GetBuffer(), ElementsAre(0xff, 0xff, 0xff, 0x7f)); + } ++#endif // defined(_SKIA_SUPPORT_) +diff --git a/core/fxge/dib/cfx_imagerenderer.cpp b/core/fxge/dib/cfx_imagerenderer.cpp +index 5c15a9cba..2e7f49663 100644 +--- a/core/fxge/dib/cfx_imagerenderer.cpp ++++ b/core/fxge/dib/cfx_imagerenderer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,13 +6,14 @@ + + #include "core/fxge/dib/cfx_imagerenderer.h" + ++#include ++ + #include + + #include "core/fxge/cfx_cliprgn.h" + #include "core/fxge/dib/cfx_dibitmap.h" + #include "core/fxge/dib/cfx_imagestretcher.h" + #include "core/fxge/dib/cfx_imagetransformer.h" +-#include "third_party/base/ptr_util.h" + + CFX_ImageRenderer::CFX_ImageRenderer(const RetainPtr& pDevice, + const CFX_ClipRgn* pClipRgn, +@@ -45,19 +46,19 @@ CFX_ImageRenderer::CFX_ImageRenderer(const RetainPtr& pDevice, + int dest_height = image_rect.Height(); + FX_RECT bitmap_clip = m_ClipBox; + bitmap_clip.Offset(-image_rect.left, -image_rect.top); +- bitmap_clip = FXDIB_SwapClipBox(bitmap_clip, dest_width, dest_height, +- m_Matrix.c > 0, m_Matrix.b < 0); ++ bitmap_clip = bitmap_clip.SwappedClipBox(dest_width, dest_height, ++ m_Matrix.c > 0, m_Matrix.b < 0); + m_Composer.Compose(pDevice, pClipRgn, bitmap_alpha, mask_color, m_ClipBox, + true, m_Matrix.c > 0, m_Matrix.b < 0, m_bRgbByteOrder, + BlendMode::kNormal); +- m_Stretcher = pdfium::MakeUnique( ++ m_Stretcher = std::make_unique( + &m_Composer, pSource, dest_height, dest_width, bitmap_clip, options); + if (m_Stretcher->Start()) +- m_Status = 1; ++ m_State = State::kStretching; + return; + } +- m_Status = 2; +- m_pTransformer = pdfium::MakeUnique( ++ m_State = State::kTransforming; ++ m_pTransformer = std::make_unique( + pSource, m_Matrix, options, &m_ClipBox); + return; + } +@@ -77,40 +78,40 @@ CFX_ImageRenderer::CFX_ImageRenderer(const RetainPtr& pDevice, + bitmap_clip.Offset(-image_rect.left, -image_rect.top); + m_Composer.Compose(pDevice, pClipRgn, bitmap_alpha, mask_color, m_ClipBox, + false, false, false, m_bRgbByteOrder, BlendMode::kNormal); +- m_Status = 1; +- m_Stretcher = pdfium::MakeUnique( ++ m_State = State::kStretching; ++ m_Stretcher = std::make_unique( + &m_Composer, pSource, dest_width, dest_height, bitmap_clip, options); + m_Stretcher->Start(); + } + +-CFX_ImageRenderer::~CFX_ImageRenderer() {} ++CFX_ImageRenderer::~CFX_ImageRenderer() = default; + + bool CFX_ImageRenderer::Continue(PauseIndicatorIface* pPause) { +- if (m_Status == 1) ++ if (m_State == State::kStretching) + return m_Stretcher->Continue(pPause); +- if (m_Status != 2) ++ if (m_State != State::kTransforming) + return false; + if (m_pTransformer->Continue(pPause)) + return true; + + RetainPtr pBitmap = m_pTransformer->DetachBitmap(); +- if (!pBitmap || !pBitmap->GetBuffer()) ++ if (!pBitmap || pBitmap->GetBuffer().empty()) + return false; + +- if (pBitmap->IsAlphaMask()) { ++ if (pBitmap->IsMaskFormat()) { + if (m_BitmapAlpha != 255) + m_MaskColor = FXARGB_MUL_ALPHA(m_MaskColor, m_BitmapAlpha); +- m_pDevice->CompositeMask( +- m_pTransformer->result().left, m_pTransformer->result().top, +- pBitmap->GetWidth(), pBitmap->GetHeight(), pBitmap, m_MaskColor, 0, 0, +- BlendMode::kNormal, m_pClipRgn.Get(), m_bRgbByteOrder); ++ m_pDevice->CompositeMask(m_pTransformer->result().left, ++ m_pTransformer->result().top, pBitmap->GetWidth(), ++ pBitmap->GetHeight(), pBitmap, m_MaskColor, 0, 0, ++ BlendMode::kNormal, m_pClipRgn, m_bRgbByteOrder); + } else { + if (m_BitmapAlpha != 255) + pBitmap->MultiplyAlpha(m_BitmapAlpha); + m_pDevice->CompositeBitmap( + m_pTransformer->result().left, m_pTransformer->result().top, + pBitmap->GetWidth(), pBitmap->GetHeight(), pBitmap, 0, 0, +- BlendMode::kNormal, m_pClipRgn.Get(), m_bRgbByteOrder); ++ BlendMode::kNormal, m_pClipRgn, m_bRgbByteOrder); + } + return false; + } +diff --git a/core/fxge/dib/cfx_imagerenderer.h b/core/fxge/dib/cfx_imagerenderer.h +index a64fd4aef..9c31e054a 100644 +--- a/core/fxge/dib/cfx_imagerenderer.h ++++ b/core/fxge/dib/cfx_imagerenderer.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -35,6 +35,8 @@ class CFX_ImageRenderer { + bool Continue(PauseIndicatorIface* pPause); + + private: ++ enum class State : uint8_t { kInitial = 0, kStretching, kTransforming }; ++ + RetainPtr const m_pDevice; + UnownedPtr const m_pClipRgn; + const CFX_Matrix m_Matrix; +@@ -43,8 +45,8 @@ class CFX_ImageRenderer { + CFX_BitmapComposer m_Composer; + FX_RECT m_ClipBox; + const int m_BitmapAlpha; +- int m_Status = 0; + uint32_t m_MaskColor; ++ State m_State = State::kInitial; + const bool m_bRgbByteOrder; + }; + +diff --git a/core/fxge/dib/cfx_imagestretcher.cpp b/core/fxge/dib/cfx_imagestretcher.cpp +index 1499e8a41..f9b828a9e 100644 +--- a/core/fxge/dib/cfx_imagestretcher.cpp ++++ b/core/fxge/dib/cfx_imagestretcher.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,16 +6,14 @@ + + #include "core/fxge/dib/cfx_imagestretcher.h" + +-#include +-#include +- + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxge/dib/cfx_dibbase.h" + #include "core/fxge/dib/cfx_dibitmap.h" + #include "core/fxge/dib/cstretchengine.h" +-#include "core/fxge/fx_dib.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/span.h" + + namespace { + +@@ -27,95 +25,85 @@ bool SourceSizeWithinLimit(int width, int height) { + + FXDIB_Format GetStretchedFormat(const CFX_DIBBase& src) { + FXDIB_Format format = src.GetFormat(); +- if (format == FXDIB_1bppMask) +- return FXDIB_8bppMask; +- if (format == FXDIB_1bppRgb) +- return FXDIB_8bppRgb; +- if (format == FXDIB_8bppRgb && src.GetPalette()) +- return FXDIB_Rgb; ++ if (format == FXDIB_Format::k1bppMask) ++ return FXDIB_Format::k8bppMask; ++ if (format == FXDIB_Format::k1bppRgb) ++ return FXDIB_Format::k8bppRgb; ++ if (format == FXDIB_Format::k8bppRgb && src.HasPalette()) ++ return FXDIB_Format::kRgb; + return format; + } + +-// Returns tuple c, m, y, k +-std::tuple CmykDecode(const uint32_t cmyk) { +- return std::make_tuple(FXSYS_GetCValue(cmyk), FXSYS_GetMValue(cmyk), +- FXSYS_GetYValue(cmyk), FXSYS_GetKValue(cmyk)); ++// Builds a new palette with a size of `CFX_DIBBase::kPaletteSize` from the ++// existing palette in `source`. Note: The caller must make sure that the ++// parameters meet the following conditions: ++// source - The format must be `FXDIB_Format::k1bppRgb` and it must ++// have a palette. ++// palette_span - The size must be `CFX_DIBBase::kPaletteSize` to be able ++// to hold the new palette. ++ ++void BuildPaletteFrom1BppSource(const RetainPtr& source, ++ pdfium::span palette_span) { ++ DCHECK_EQ(FXDIB_Format::k1bppRgb, source->GetFormat()); ++ DCHECK(source->HasPalette()); ++ DCHECK_EQ(CFX_DIBBase::kPaletteSize, palette_span.size()); ++ ++ int a0; ++ int r0; ++ int g0; ++ int b0; ++ std::tie(a0, r0, g0, b0) = ArgbDecode(source->GetPaletteArgb(0)); ++ int a1; ++ int r1; ++ int g1; ++ int b1; ++ std::tie(a1, r1, g1, b1) = ArgbDecode(source->GetPaletteArgb(1)); ++ DCHECK_EQ(255, a0); ++ DCHECK_EQ(255, a1); ++ ++ for (int i = 0; i < static_cast(CFX_DIBBase::kPaletteSize); ++i) { ++ int r = r0 + (r1 - r0) * i / 255; ++ int g = g0 + (g1 - g0) * i / 255; ++ int b = b0 + (b1 - b0) * i / 255; ++ palette_span[i] = ArgbEncode(255, r, g, b); ++ } + } + + } // namespace + +-CFX_ImageStretcher::CFX_ImageStretcher(ScanlineComposerIface* pDest, +- const RetainPtr& pSource, +- int dest_width, +- int dest_height, +- const FX_RECT& bitmap_rect, +- const FXDIB_ResampleOptions& options) ++CFX_ImageStretcher::CFX_ImageStretcher( ++ ScanlineComposerIface* pDest, ++ const RetainPtr& pSource, ++ int dest_width, ++ int dest_height, ++ const FX_RECT& bitmap_rect, ++ const FXDIB_ResampleOptions& options) + : m_pDest(pDest), + m_pSource(pSource), + m_ResampleOptions(options), + m_DestWidth(dest_width), + m_DestHeight(dest_height), + m_ClipRect(bitmap_rect), +- m_DestFormat(GetStretchedFormat(*pSource)), +- m_DestBPP(GetBppFromFormat(m_DestFormat)) { +- ASSERT(m_ClipRect.Valid()); ++ m_DestFormat(GetStretchedFormat(*pSource)) { ++ DCHECK(m_ClipRect.Valid()); + } + +-CFX_ImageStretcher::~CFX_ImageStretcher() {} ++CFX_ImageStretcher::~CFX_ImageStretcher() = default; + + bool CFX_ImageStretcher::Start() { + if (m_DestWidth == 0 || m_DestHeight == 0) + return false; + +- if (m_pSource->GetFormat() == FXDIB_1bppRgb && m_pSource->GetPalette()) { +- FX_ARGB pal[256]; +- int a0; +- int r0; +- int g0; +- int b0; +- std::tie(a0, r0, g0, b0) = ArgbDecode(m_pSource->GetPaletteArgb(0)); +- int a1; +- int r1; +- int g1; +- int b1; +- std::tie(a1, r1, g1, b1) = ArgbDecode(m_pSource->GetPaletteArgb(1)); +- for (int i = 0; i < 256; ++i) { +- int a = a0 + (a1 - a0) * i / 255; +- int r = r0 + (r1 - r0) * i / 255; +- int g = g0 + (g1 - g0) * i / 255; +- int b = b0 + (b1 - b0) * i / 255; +- pal[i] = ArgbEncode(a, r, g, b); +- } +- if (!m_pDest->SetInfo(m_ClipRect.Width(), m_ClipRect.Height(), m_DestFormat, +- pal)) { +- return false; +- } +- } else if (m_pSource->GetFormat() == FXDIB_1bppCmyk && +- m_pSource->GetPalette()) { +- FX_CMYK pal[256]; +- int c0; +- int m0; +- int y0; +- int k0; +- std::tie(c0, m0, y0, k0) = CmykDecode(m_pSource->GetPaletteArgb(0)); +- int c1; +- int m1; +- int y1; +- int k1; +- std::tie(c1, m1, y1, k1) = CmykDecode(m_pSource->GetPaletteArgb(1)); +- for (int i = 0; i < 256; ++i) { +- int c = c0 + (c1 - c0) * i / 255; +- int m = m0 + (m1 - m0) * i / 255; +- int y = y0 + (y1 - y0) * i / 255; +- int k = k0 + (k1 - k0) * i / 255; +- pal[i] = CmykEncode(c, m, y, k); +- } ++ if (m_pSource->GetFormat() == FXDIB_Format::k1bppRgb && ++ m_pSource->HasPalette()) { ++ FX_ARGB pal[CFX_DIBBase::kPaletteSize]; ++ BuildPaletteFrom1BppSource(m_pSource, pal); + if (!m_pDest->SetInfo(m_ClipRect.Width(), m_ClipRect.Height(), m_DestFormat, + pal)) { + return false; + } + } else if (!m_pDest->SetInfo(m_ClipRect.Width(), m_ClipRect.Height(), +- m_DestFormat, nullptr)) { ++ m_DestFormat, {})) { + return false; + } + return StartStretch(); +@@ -125,14 +113,14 @@ bool CFX_ImageStretcher::Continue(PauseIndicatorIface* pPause) { + return ContinueStretch(pPause); + } + +-RetainPtr CFX_ImageStretcher::source() { ++RetainPtr CFX_ImageStretcher::source() { + return m_pSource; + } + + bool CFX_ImageStretcher::StartStretch() { +- m_pStretchEngine = pdfium::MakeUnique( +- m_pDest.Get(), m_DestFormat, m_DestWidth, m_DestHeight, m_ClipRect, +- m_pSource, m_ResampleOptions); ++ m_pStretchEngine = std::make_unique( ++ m_pDest, m_DestFormat, m_DestWidth, m_DestHeight, m_ClipRect, m_pSource, ++ m_ResampleOptions); + m_pStretchEngine->StartStretchHorz(); + if (SourceSizeWithinLimit(m_pSource->GetWidth(), m_pSource->GetHeight())) { + m_pStretchEngine->Continue(nullptr); +diff --git a/core/fxge/dib/cfx_imagestretcher.h b/core/fxge/dib/cfx_imagestretcher.h +index 1ed9695e9..7e51dc726 100644 +--- a/core/fxge/dib/cfx_imagestretcher.h ++++ b/core/fxge/dib/cfx_imagestretcher.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,11 +10,10 @@ + #include + + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_memory_wrappers.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "core/fxge/dib/fx_dib.h" + #include "core/fxge/dib/scanlinecomposer_iface.h" +-#include "core/fxge/fx_dib.h" + + class CFX_DIBBase; + class CStretchEngine; +@@ -23,7 +22,7 @@ class PauseIndicatorIface; + class CFX_ImageStretcher { + public: + CFX_ImageStretcher(ScanlineComposerIface* pDest, +- const RetainPtr& pSource, ++ const RetainPtr& pSource, + int dest_width, + int dest_height, + const FX_RECT& bitmap_rect, +@@ -33,23 +32,20 @@ class CFX_ImageStretcher { + bool Start(); + bool Continue(PauseIndicatorIface* pPause); + +- RetainPtr source(); ++ RetainPtr source(); + + private: + bool StartStretch(); + bool ContinueStretch(PauseIndicatorIface* pPause); + + UnownedPtr const m_pDest; +- RetainPtr m_pSource; ++ RetainPtr const m_pSource; + std::unique_ptr m_pStretchEngine; +- std::unique_ptr m_pScanline; +- std::unique_ptr m_pMaskScanline; + const FXDIB_ResampleOptions m_ResampleOptions; +- int m_DestWidth; +- int m_DestHeight; ++ const int m_DestWidth; ++ const int m_DestHeight; + const FX_RECT m_ClipRect; + const FXDIB_Format m_DestFormat; +- const int m_DestBPP; + }; + + #endif // CORE_FXGE_DIB_CFX_IMAGESTRETCHER_H_ +diff --git a/core/fxge/dib/cfx_imagetransformer.cpp b/core/fxge/dib/cfx_imagetransformer.cpp +index 3afc48c25..904a7f526 100644 +--- a/core/fxge/dib/cfx_imagetransformer.cpp ++++ b/core/fxge/dib/cfx_imagetransformer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,17 +6,20 @@ + + #include "core/fxge/dib/cfx_imagetransformer.h" + +-#include ++#include ++ ++#include + #include + #include + ++#include "core/fxcrt/fx_system.h" + #include "core/fxge/dib/cfx_dibitmap.h" + #include "core/fxge/dib/cfx_imagestretcher.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/check.h" + #include "third_party/base/compiler_specific.h" ++#include "third_party/base/notreached.h" + #include "third_party/base/numerics/safe_conversions.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" + + namespace { + +@@ -24,140 +27,27 @@ constexpr int kBase = 256; + constexpr float kFix16 = 0.05f; + constexpr uint8_t kOpaqueAlpha = 0xff; + +-uint8_t bilinear_interpol(const uint8_t* buf, +- int row_offset_l, +- int row_offset_r, +- int src_col_l, +- int src_col_r, +- int res_x, +- int res_y, +- int bpp, +- int c_offset) { +- int i_resx = 255 - res_x; +- int col_bpp_l = src_col_l * bpp; +- int col_bpp_r = src_col_r * bpp; +- const uint8_t* buf_u = buf + row_offset_l + c_offset; +- const uint8_t* buf_d = buf + row_offset_r + c_offset; ++uint8_t BilinearInterpolate(const uint8_t* buf, ++ const CFX_ImageTransformer::BilinearData& data, ++ int bpp, ++ int c_offset) { ++ int i_resx = 255 - data.res_x; ++ int col_bpp_l = data.src_col_l * bpp; ++ int col_bpp_r = data.src_col_r * bpp; ++ const uint8_t* buf_u = buf + data.row_offset_l + c_offset; ++ const uint8_t* buf_d = buf + data.row_offset_r + c_offset; + const uint8_t* src_pos0 = buf_u + col_bpp_l; + const uint8_t* src_pos1 = buf_u + col_bpp_r; + const uint8_t* src_pos2 = buf_d + col_bpp_l; + const uint8_t* src_pos3 = buf_d + col_bpp_r; +- uint8_t r_pos_0 = (*src_pos0 * i_resx + *src_pos1 * res_x) >> 8; +- uint8_t r_pos_1 = (*src_pos2 * i_resx + *src_pos3 * res_x) >> 8; +- return (r_pos_0 * (255 - res_y) + r_pos_1 * res_y) >> 8; +-} +- +-uint8_t bicubic_interpol(const uint8_t* buf, +- uint32_t pitch, +- const int pos_pixel[], +- const int u_w[], +- const int v_w[], +- int res_x, +- int res_y, +- int bpp, +- int c_offset) { +- int s_result = 0; +- for (int i = 0; i < 4; i++) { +- int a_result = 0; +- for (int j = 0; j < 4; j++) { +- uint8_t val = +- *(buf + pos_pixel[i + 4] * pitch + pos_pixel[j] * bpp + c_offset); +- a_result += u_w[j] * val; +- } +- s_result += a_result * v_w[i]; +- } +- s_result >>= 16; +- return static_cast(pdfium::clamp(s_result, 0, 255)); +-} +- +-void bicubic_get_pos_weight(int pos_pixel[], +- int u_w[], +- int v_w[], +- int src_col_l, +- int src_row_l, +- int res_x, +- int res_y, +- int stretch_width, +- int stretch_height) { +- pos_pixel[0] = src_col_l - 1; +- pos_pixel[1] = src_col_l; +- pos_pixel[2] = src_col_l + 1; +- pos_pixel[3] = src_col_l + 2; +- pos_pixel[4] = src_row_l - 1; +- pos_pixel[5] = src_row_l; +- pos_pixel[6] = src_row_l + 1; +- pos_pixel[7] = src_row_l + 2; +- for (int i = 0; i < 4; i++) { +- pos_pixel[i] = pdfium::clamp(pos_pixel[i], 0, stretch_width - 1); +- pos_pixel[i + 4] = pdfium::clamp(pos_pixel[i + 4], 0, stretch_height - 1); +- } +- u_w[0] = SDP_Table[256 + res_x]; +- u_w[1] = SDP_Table[res_x]; +- u_w[2] = SDP_Table[256 - res_x]; +- u_w[3] = SDP_Table[512 - res_x]; +- v_w[0] = SDP_Table[256 + res_y]; +- v_w[1] = SDP_Table[res_y]; +- v_w[2] = SDP_Table[256 - res_y]; +- v_w[3] = SDP_Table[512 - res_y]; +-} +- +-FXDIB_Format GetTransformedFormat(const RetainPtr& pDrc) { +- if (pDrc->IsAlphaMask()) +- return FXDIB_8bppMask; +- +- FXDIB_Format format = pDrc->GetFormat(); +- if (format >= 1025) +- return FXDIB_Cmyka; +- if (format <= 32 || format == FXDIB_Argb) +- return FXDIB_Argb; +- return FXDIB_Rgba; ++ uint8_t r_pos_0 = (*src_pos0 * i_resx + *src_pos1 * data.res_x) >> 8; ++ uint8_t r_pos_1 = (*src_pos2 * i_resx + *src_pos3 * data.res_x) >> 8; ++ return (r_pos_0 * (255 - data.res_y) + r_pos_1 * data.res_y) >> 8; + } + +-void WriteMonoResult(uint32_t r_bgra_cmyk, FXDIB_Format format, uint8_t* dest) { +- if (format == FXDIB_Rgba) { +- dest[0] = static_cast(r_bgra_cmyk >> 24); +- dest[1] = static_cast(r_bgra_cmyk >> 16); +- dest[2] = static_cast(r_bgra_cmyk >> 8); +- } else { +- *reinterpret_cast(dest) = r_bgra_cmyk; +- } +-} +- +-// Let the compiler deduce the type for |func|, which cheaper than specifying it +-// with std::function. +-template +-void WriteColorResult(const F& func, +- bool bHasAlpha, +- FXDIB_Format format, +- uint8_t* dest) { +- uint8_t blue_c = func(0); +- uint8_t green_m = func(1); +- uint8_t red_y = func(2); +- +- uint32_t* dest32 = reinterpret_cast(dest); +- if (bHasAlpha) { +- if (format == FXDIB_Argb) { +- *dest32 = FXARGB_TODIB(ArgbEncode(func(3), red_y, green_m, blue_c)); +- } else if (format == FXDIB_Rgba) { +- dest[0] = blue_c; +- dest[1] = green_m; +- dest[2] = red_y; +- } else { +- *dest32 = FXCMYK_TODIB(CmykEncode(blue_c, green_m, red_y, func(3))); +- } +- return; +- } +- +- if (format == FXDIB_Cmyka) { +- *dest32 = FXCMYK_TODIB(CmykEncode(blue_c, green_m, red_y, func(3))); +- } else { +- *dest32 = FXARGB_TODIB(ArgbEncode(kOpaqueAlpha, red_y, green_m, blue_c)); +- } +-} +- +-class CPDF_FixedMatrix { ++class CFX_BilinearMatrix { + public: +- explicit CPDF_FixedMatrix(const CFX_Matrix& src) ++ explicit CFX_BilinearMatrix(const CFX_Matrix& src) + : a(FXSYS_roundf(src.a * kBase)), + b(FXSYS_roundf(src.b * kBase)), + c(FXSYS_roundf(src.c * kBase)), +@@ -165,16 +55,22 @@ class CPDF_FixedMatrix { + e(FXSYS_roundf(src.e * kBase)), + f(FXSYS_roundf(src.f * kBase)) {} + +- void Transform(int x, int y, int* x1, int* y1) const { +- std::pair val = TransformInternal(x, y); +- *x1 = pdfium::base::saturated_cast(val.first / kBase); +- *y1 = pdfium::base::saturated_cast(val.second / kBase); ++ void Transform(int x, int y, int* x1, int* y1, int* res_x, int* res_y) const { ++ CFX_PointF val = TransformInternal(CFX_PointF(x, y)); ++ *x1 = pdfium::base::saturated_cast(val.x / kBase); ++ *y1 = pdfium::base::saturated_cast(val.y / kBase); ++ *res_x = static_cast(val.x) % kBase; ++ *res_y = static_cast(val.y) % kBase; ++ if (*res_x < 0 && *res_x > -kBase) ++ *res_x = kBase + *res_x; ++ if (*res_y < 0 && *res_y > -kBase) ++ *res_y = kBase + *res_y; + } + +- protected: +- std::pair TransformInternal(float x, float y) const { +- return std::make_pair(a * x + c * y + e + kBase / 2, +- b * x + d * y + f + kBase / 2); ++ private: ++ CFX_PointF TransformInternal(CFX_PointF pt) const { ++ return CFX_PointF(a * pt.x + c * pt.y + e + kBase / 2, ++ b * pt.x + d * pt.y + f + kBase / 2); + } + + const int a; +@@ -185,24 +81,6 @@ class CPDF_FixedMatrix { + const int f; + }; + +-class CFX_BilinearMatrix final : public CPDF_FixedMatrix { +- public: +- explicit CFX_BilinearMatrix(const CFX_Matrix& src) : CPDF_FixedMatrix(src) {} +- +- void Transform(int x, int y, int* x1, int* y1, int* res_x, int* res_y) const { +- std::pair val = TransformInternal(x, y); +- *x1 = pdfium::base::saturated_cast(val.first / kBase); +- *y1 = pdfium::base::saturated_cast(val.second / kBase); +- +- *res_x = static_cast(val.first) % kBase; +- *res_y = static_cast(val.second) % kBase; +- if (*res_x < 0 && *res_x > -kBase) +- *res_x = kBase + *res_x; +- if (*res_y < 0 && *res_y > -kBase) +- *res_y = kBase + *res_y; +- } +-}; +- + bool InStretchBounds(const FX_RECT& clip_rect, int col, int row) { + return col >= 0 && col <= clip_rect.Width() && row >= 0 && + row <= clip_rect.Height(); +@@ -220,14 +98,14 @@ void AdjustCoords(const FX_RECT& clip_rect, int* col, int* row) { + // Let the compiler deduce the type for |func|, which cheaper than specifying it + // with std::function. + template +-void DoBilinearLoop(const CFX_ImageTransformer::CalcData& cdata, ++void DoBilinearLoop(const CFX_ImageTransformer::CalcData& calc_data, + const FX_RECT& result_rect, + const FX_RECT& clip_rect, + int increment, + const F& func) { +- CFX_BilinearMatrix matrix_fix(cdata.matrix); ++ CFX_BilinearMatrix matrix_fix(calc_data.matrix); + for (int row = 0; row < result_rect.Height(); row++) { +- uint8_t* dest = cdata.bitmap->GetWritableScanline(row); ++ uint8_t* dest = calc_data.bitmap->GetWritableScanline(row).data(); + for (int col = 0; col < result_rect.Width(); col++) { + CFX_ImageTransformer::BilinearData d; + d.res_x = 0; +@@ -241,64 +119,8 @@ void DoBilinearLoop(const CFX_ImageTransformer::CalcData& cdata, + d.src_col_r = d.src_col_l + 1; + d.src_row_r = d.src_row_l + 1; + AdjustCoords(clip_rect, &d.src_col_r, &d.src_row_r); +- d.row_offset_l = d.src_row_l * cdata.pitch; +- d.row_offset_r = d.src_row_r * cdata.pitch; +- func(d, dest); +- } +- dest += increment; +- } +- } +-} +- +-// Let the compiler deduce the type for |func|, which cheaper than specifying it +-// with std::function. +-template +-void DoBicubicLoop(const CFX_ImageTransformer::CalcData& cdata, +- const FX_RECT& result_rect, +- const FX_RECT& clip_rect, +- int increment, +- const F& func) { +- CFX_BilinearMatrix matrix_fix(cdata.matrix); +- for (int row = 0; row < result_rect.Height(); row++) { +- uint8_t* dest = cdata.bitmap->GetWritableScanline(row); +- for (int col = 0; col < result_rect.Width(); col++) { +- CFX_ImageTransformer::BicubicData d; +- d.res_x = 0; +- d.res_y = 0; +- d.src_col_l = 0; +- d.src_row_l = 0; +- matrix_fix.Transform(col, row, &d.src_col_l, &d.src_row_l, &d.res_x, +- &d.res_y); +- if (LIKELY(InStretchBounds(clip_rect, d.src_col_l, d.src_row_l))) { +- AdjustCoords(clip_rect, &d.src_col_l, &d.src_row_l); +- bicubic_get_pos_weight(d.pos_pixel, d.u_w, d.v_w, d.src_col_l, +- d.src_row_l, d.res_x, d.res_y, clip_rect.Width(), +- clip_rect.Height()); +- func(d, dest); +- } +- dest += increment; +- } +- } +-} +- +-// Let the compiler deduce the type for |func|, which cheaper than specifying it +-// with std::function. +-template +-void DoDownSampleLoop(const CFX_ImageTransformer::CalcData& cdata, +- const FX_RECT& result_rect, +- const FX_RECT& clip_rect, +- int increment, +- const F& func) { +- CPDF_FixedMatrix matrix_fix(cdata.matrix); +- for (int row = 0; row < result_rect.Height(); row++) { +- uint8_t* dest = cdata.bitmap->GetWritableScanline(row); +- for (int col = 0; col < result_rect.Width(); col++) { +- CFX_ImageTransformer::DownSampleData d; +- d.src_col = 0; +- d.src_row = 0; +- matrix_fix.Transform(col, row, &d.src_col, &d.src_row); +- if (LIKELY(InStretchBounds(clip_rect, d.src_col, d.src_row))) { +- AdjustCoords(clip_rect, &d.src_col, &d.src_row); ++ d.row_offset_l = d.src_row_l * calc_data.pitch; ++ d.row_offset_r = d.src_row_r * calc_data.pitch; + func(d, dest); + } + dest += increment; +@@ -308,10 +130,11 @@ void DoDownSampleLoop(const CFX_ImageTransformer::CalcData& cdata, + + } // namespace + +-CFX_ImageTransformer::CFX_ImageTransformer(const RetainPtr& pSrc, +- const CFX_Matrix& matrix, +- const FXDIB_ResampleOptions& options, +- const FX_RECT* pClip) ++CFX_ImageTransformer::CFX_ImageTransformer( ++ const RetainPtr& pSrc, ++ const CFX_Matrix& matrix, ++ const FXDIB_ResampleOptions& options, ++ const FX_RECT* pClip) + : m_pSrc(pSrc), m_matrix(matrix), m_ResampleOptions(options) { + FX_RECT result_rect = m_matrix.GetUnitRect().GetClosestRect(); + FX_RECT result_clip = result_rect; +@@ -328,9 +151,9 @@ CFX_ImageTransformer::CFX_ImageTransformer(const RetainPtr& pSrc, + int dest_width = result_rect.Width(); + int dest_height = result_rect.Height(); + result_clip.Offset(-result_rect.left, -result_rect.top); +- result_clip = FXDIB_SwapClipBox(result_clip, dest_width, dest_height, +- m_matrix.c > 0, m_matrix.b < 0); +- m_Stretcher = pdfium::MakeUnique( ++ result_clip = result_clip.SwappedClipBox(dest_width, dest_height, ++ m_matrix.c > 0, m_matrix.b < 0); ++ m_Stretcher = std::make_unique( + &m_Storer, m_pSrc, dest_height, dest_width, result_clip, + m_ResampleOptions); + m_Stretcher->Start(); +@@ -343,7 +166,7 @@ CFX_ImageTransformer::CFX_ImageTransformer(const RetainPtr& pSrc, + int dest_height = static_cast(m_matrix.d > 0 ? -ceil(m_matrix.d) + : -floor(m_matrix.d)); + result_clip.Offset(-result_rect.left, -result_rect.top); +- m_Stretcher = pdfium::MakeUnique( ++ m_Stretcher = std::make_unique( + &m_Storer, m_pSrc, dest_width, dest_height, result_clip, + m_ResampleOptions); + m_Stretcher->Start(); +@@ -373,7 +196,7 @@ CFX_ImageTransformer::CFX_ImageTransformer(const RetainPtr& pSrc, + + m_dest2stretch = dest_to_strech; + m_StretchClip = stretch_clip; +- m_Stretcher = pdfium::MakeUnique( ++ m_Stretcher = std::make_unique( + &m_Storer, m_pSrc, stretch_width, stretch_height, m_StretchClip, + m_ResampleOptions); + m_Stretcher->Start(); +@@ -417,45 +240,28 @@ void CFX_ImageTransformer::ContinueOther(PauseIndicatorIface* pPause) { + return; + + auto pTransformed = pdfium::MakeRetain(); +- FXDIB_Format format = GetTransformedFormat(m_Stretcher->source()); ++ FXDIB_Format format = m_Stretcher->source()->IsMaskFormat() ++ ? FXDIB_Format::k8bppMask ++ : FXDIB_Format::kArgb; + if (!pTransformed->Create(m_result.Width(), m_result.Height(), format)) + return; + +- const auto& pSrcMask = m_Storer.GetBitmap()->m_pAlphaMask; +- const uint8_t* pSrcMaskBuf = pSrcMask ? pSrcMask->GetBuffer() : nullptr; +- +- pTransformed->Clear(0); +- auto& pDestMask = pTransformed->m_pAlphaMask; +- if (pDestMask) +- pDestMask->Clear(0); +- + CFX_Matrix result2stretch(1.0f, 0.0f, 0.0f, 1.0f, m_result.left, + m_result.top); + result2stretch.Concat(m_dest2stretch); + result2stretch.Translate(-m_StretchClip.left, -m_StretchClip.top); +- if (!pSrcMaskBuf && pDestMask) { +- pDestMask->Clear(0xff000000); +- } else if (pDestMask) { +- CalcData cdata = { +- pDestMask.Get(), +- result2stretch, +- pSrcMaskBuf, +- m_Storer.GetBitmap()->m_pAlphaMask->GetPitch(), +- }; +- CalcMask(cdata); +- } + +- CalcData cdata = {pTransformed.Get(), result2stretch, +- m_Storer.GetBitmap()->GetBuffer(), +- m_Storer.GetBitmap()->GetPitch()}; +- if (m_Storer.GetBitmap()->IsAlphaMask()) { +- CalcAlpha(cdata); ++ CalcData calc_data = {pTransformed.Get(), result2stretch, ++ m_Storer.GetBitmap()->GetBuffer().data(), ++ m_Storer.GetBitmap()->GetPitch()}; ++ if (m_Storer.GetBitmap()->IsMaskFormat()) { ++ CalcAlpha(calc_data); + } else { + int Bpp = m_Storer.GetBitmap()->GetBPP() / 8; + if (Bpp == 1) +- CalcMono(cdata, format); ++ CalcMono(calc_data); + else +- CalcColor(cdata, format, Bpp); ++ CalcColor(calc_data, format, Bpp); + } + m_Storer.Replace(std::move(pTransformed)); + } +@@ -464,140 +270,68 @@ RetainPtr CFX_ImageTransformer::DetachBitmap() { + return m_Storer.Detach(); + } + +-void CFX_ImageTransformer::CalcMask(const CalcData& cdata) { +- if (IsBilinear()) { +- auto func = [&cdata](const BilinearData& data, uint8_t* dest) { +- *dest = bilinear_interpol(cdata.buf, data.row_offset_l, data.row_offset_r, +- data.src_col_l, data.src_col_r, data.res_x, +- data.res_y, 1, 0); +- }; +- DoBilinearLoop(cdata, m_result, m_StretchClip, 1, func); +- } else if (IsBiCubic()) { +- auto func = [&cdata](const BicubicData& data, uint8_t* dest) { +- *dest = bicubic_interpol(cdata.buf, cdata.pitch, data.pos_pixel, data.u_w, +- data.v_w, data.res_x, data.res_y, 1, 0); +- }; +- DoBicubicLoop(cdata, m_result, m_StretchClip, 1, func); +- } else { +- auto func = [&cdata](const DownSampleData& data, uint8_t* dest) { +- *dest = cdata.buf[data.src_row * cdata.pitch + data.src_col]; +- }; +- DoDownSampleLoop(cdata, m_result, m_StretchClip, 1, func); +- } +-} +- +-void CFX_ImageTransformer::CalcAlpha(const CalcData& cdata) { +- if (IsBilinear()) { +- auto func = [&cdata](const BilinearData& data, uint8_t* dest) { +- *dest = bilinear_interpol(cdata.buf, data.row_offset_l, data.row_offset_r, +- data.src_col_l, data.src_col_r, data.res_x, +- data.res_y, 1, 0); +- }; +- DoBilinearLoop(cdata, m_result, m_StretchClip, 1, func); +- } else if (IsBiCubic()) { +- auto func = [&cdata](const BicubicData& data, uint8_t* dest) { +- *dest = bicubic_interpol(cdata.buf, cdata.pitch, data.pos_pixel, data.u_w, +- data.v_w, data.res_x, data.res_y, 1, 0); +- }; +- DoBicubicLoop(cdata, m_result, m_StretchClip, 1, func); +- } else { +- auto func = [&cdata](const DownSampleData& data, uint8_t* dest) { +- const uint8_t* src_pixel = +- cdata.buf + cdata.pitch * data.src_row + data.src_col; +- *dest = *src_pixel; +- }; +- DoDownSampleLoop(cdata, m_result, m_StretchClip, 1, func); +- } ++void CFX_ImageTransformer::CalcAlpha(const CalcData& calc_data) { ++ auto func = [&calc_data](const BilinearData& data, uint8_t* dest) { ++ *dest = BilinearInterpolate(calc_data.buf, data, 1, 0); ++ }; ++ DoBilinearLoop(calc_data, m_result, m_StretchClip, 1, func); + } + +-void CFX_ImageTransformer::CalcMono(const CalcData& cdata, +- FXDIB_Format format) { ++void CFX_ImageTransformer::CalcMono(const CalcData& calc_data) { + uint32_t argb[256]; +- FX_ARGB* pPal = m_Storer.GetBitmap()->GetPalette(); +- if (pPal) { +- for (size_t i = 0; i < FX_ArraySize(argb); i++) +- argb[i] = pPal[i]; +- } else if (m_Storer.GetBitmap()->IsCmykImage()) { +- for (size_t i = 0; i < FX_ArraySize(argb); i++) +- argb[i] = 255 - i; +- } else { +- for (size_t i = 0; i < FX_ArraySize(argb); i++) +- argb[i] = 0xff000000 | (i * 0x010101); +- } +- int destBpp = cdata.bitmap->GetBPP() / 8; +- if (IsBilinear()) { +- auto func = [&cdata, format, &argb](const BilinearData& data, +- uint8_t* dest) { +- uint8_t idx = bilinear_interpol( +- cdata.buf, data.row_offset_l, data.row_offset_r, data.src_col_l, +- data.src_col_r, data.res_x, data.res_y, 1, 0); +- uint32_t r_bgra_cmyk = argb[idx]; +- WriteMonoResult(r_bgra_cmyk, format, dest); +- }; +- DoBilinearLoop(cdata, m_result, m_StretchClip, destBpp, func); +- } else if (IsBiCubic()) { +- auto func = [&cdata, format, &argb](const BicubicData& data, +- uint8_t* dest) { +- uint32_t r_bgra_cmyk = argb[bicubic_interpol( +- cdata.buf, cdata.pitch, data.pos_pixel, data.u_w, data.v_w, +- data.res_x, data.res_y, 1, 0)]; +- WriteMonoResult(r_bgra_cmyk, format, dest); +- }; +- DoBicubicLoop(cdata, m_result, m_StretchClip, destBpp, func); ++ if (m_Storer.GetBitmap()->HasPalette()) { ++ pdfium::span palette = ++ m_Storer.GetBitmap()->GetPaletteSpan(); ++ for (size_t i = 0; i < std::size(argb); i++) ++ argb[i] = palette[i]; + } else { +- auto func = [&cdata, format, &argb](const DownSampleData& data, +- uint8_t* dest) { +- uint32_t r_bgra_cmyk = +- argb[cdata.buf[data.src_row * cdata.pitch + data.src_col]]; +- WriteMonoResult(r_bgra_cmyk, format, dest); +- }; +- DoDownSampleLoop(cdata, m_result, m_StretchClip, destBpp, func); ++ for (size_t i = 0; i < std::size(argb); i++) { ++ uint32_t v = static_cast(i); ++ argb[i] = ArgbEncode(0xff, v, v, v); ++ } + } ++ int destBpp = calc_data.bitmap->GetBPP() / 8; ++ auto func = [&calc_data, &argb](const BilinearData& data, uint8_t* dest) { ++ uint8_t idx = BilinearInterpolate(calc_data.buf, data, 1, 0); ++ *reinterpret_cast(dest) = argb[idx]; ++ }; ++ DoBilinearLoop(calc_data, m_result, m_StretchClip, destBpp, func); + } + +-void CFX_ImageTransformer::CalcColor(const CalcData& cdata, ++void CFX_ImageTransformer::CalcColor(const CalcData& calc_data, + FXDIB_Format format, + int Bpp) { +- bool bHasAlpha = m_Storer.GetBitmap()->HasAlpha(); +- int destBpp = cdata.bitmap->GetBPP() / 8; +- if (IsBilinear()) { +- auto func = [&cdata, format, Bpp, bHasAlpha](const BilinearData& data, +- uint8_t* dest) { +- auto bilinear_interpol_func = [&cdata, &data, Bpp](int offset) { +- return bilinear_interpol( +- cdata.buf, data.row_offset_l, data.row_offset_r, data.src_col_l, +- data.src_col_r, data.res_x, data.res_y, Bpp, offset); +- }; +- WriteColorResult(bilinear_interpol_func, bHasAlpha, format, dest); +- }; +- DoBilinearLoop(cdata, m_result, m_StretchClip, destBpp, func); +- } else if (IsBiCubic()) { +- auto func = [&cdata, format, Bpp, bHasAlpha](const BicubicData& data, +- uint8_t* dest) { +- auto bicubic_interpol_func = [&cdata, &data, Bpp](int offset) { +- return bicubic_interpol(cdata.buf, cdata.pitch, data.pos_pixel, +- data.u_w, data.v_w, data.res_x, data.res_y, Bpp, +- offset); +- }; +- WriteColorResult(bicubic_interpol_func, bHasAlpha, format, dest); +- }; +- DoBicubicLoop(cdata, m_result, m_StretchClip, destBpp, func); +- } else { +- auto func = [&cdata, format, bHasAlpha, Bpp](const DownSampleData& data, +- uint8_t* dest) { +- const uint8_t* src_pos = +- cdata.buf + data.src_row * cdata.pitch + data.src_col * Bpp; +- auto sample_func = [src_pos](int offset) { return src_pos[offset]; }; +- WriteColorResult(sample_func, bHasAlpha, format, dest); ++ DCHECK(format == FXDIB_Format::k8bppMask || format == FXDIB_Format::kArgb); ++ const int destBpp = calc_data.bitmap->GetBPP() / 8; ++ if (!m_Storer.GetBitmap()->IsAlphaFormat()) { ++ auto func = [&calc_data, Bpp](const BilinearData& data, uint8_t* dest) { ++ uint8_t b = BilinearInterpolate(calc_data.buf, data, Bpp, 0); ++ uint8_t g = BilinearInterpolate(calc_data.buf, data, Bpp, 1); ++ uint8_t r = BilinearInterpolate(calc_data.buf, data, Bpp, 2); ++ *reinterpret_cast(dest) = ArgbEncode(kOpaqueAlpha, r, g, b); + }; +- DoDownSampleLoop(cdata, m_result, m_StretchClip, destBpp, func); ++ DoBilinearLoop(calc_data, m_result, m_StretchClip, destBpp, func); ++ return; + } +-} + +-bool CFX_ImageTransformer::IsBilinear() const { +- return !IsBiCubic(); +-} ++ if (format == FXDIB_Format::kArgb) { ++ auto func = [&calc_data, Bpp](const BilinearData& data, uint8_t* dest) { ++ uint8_t b = BilinearInterpolate(calc_data.buf, data, Bpp, 0); ++ uint8_t g = BilinearInterpolate(calc_data.buf, data, Bpp, 1); ++ uint8_t r = BilinearInterpolate(calc_data.buf, data, Bpp, 2); ++ uint8_t alpha = BilinearInterpolate(calc_data.buf, data, Bpp, 3); ++ *reinterpret_cast(dest) = ArgbEncode(alpha, r, g, b); ++ }; ++ DoBilinearLoop(calc_data, m_result, m_StretchClip, destBpp, func); ++ return; ++ } + +-bool CFX_ImageTransformer::IsBiCubic() const { +- return m_ResampleOptions.bInterpolateBicubic; ++ auto func = [&calc_data, Bpp](const BilinearData& data, uint8_t* dest) { ++ uint8_t c = BilinearInterpolate(calc_data.buf, data, Bpp, 0); ++ uint8_t m = BilinearInterpolate(calc_data.buf, data, Bpp, 1); ++ uint8_t y = BilinearInterpolate(calc_data.buf, data, Bpp, 2); ++ uint8_t k = BilinearInterpolate(calc_data.buf, data, Bpp, 3); ++ *reinterpret_cast(dest) = FXCMYK_TODIB(CmykEncode(c, m, y, k)); ++ }; ++ DoBilinearLoop(calc_data, m_result, m_StretchClip, destBpp, func); + } +diff --git a/core/fxge/dib/cfx_imagetransformer.h b/core/fxge/dib/cfx_imagetransformer.h +index 33720038d..abd60c477 100644 +--- a/core/fxge/dib/cfx_imagetransformer.h ++++ b/core/fxge/dib/cfx_imagetransformer.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -31,23 +31,6 @@ class CFX_ImageTransformer { + int row_offset_r; + }; + +- struct BicubicData { +- int res_x; +- int res_y; +- int src_col_l; +- int src_row_l; +- int src_col_r; +- int src_row_r; +- int pos_pixel[8]; +- int u_w[4]; +- int v_w[4]; +- }; +- +- struct DownSampleData { +- int src_col; +- int src_row; +- }; +- + struct CalcData { + CFX_DIBitmap* bitmap; + const CFX_Matrix& matrix; +@@ -55,7 +38,7 @@ class CFX_ImageTransformer { + uint32_t pitch; + }; + +- CFX_ImageTransformer(const RetainPtr& pSrc, ++ CFX_ImageTransformer(const RetainPtr& pSrc, + const CFX_Matrix& matrix, + const FXDIB_ResampleOptions& options, + const FX_RECT* pClip); +@@ -77,15 +60,11 @@ class CFX_ImageTransformer { + void ContinueRotate(PauseIndicatorIface* pPause); + void ContinueOther(PauseIndicatorIface* pPause); + +- void CalcMask(const CalcData& cdata); +- void CalcAlpha(const CalcData& cdata); +- void CalcMono(const CalcData& cdata, FXDIB_Format format); +- void CalcColor(const CalcData& cdata, FXDIB_Format format, int Bpp); +- +- bool IsBilinear() const; +- bool IsBiCubic() const; ++ void CalcAlpha(const CalcData& calc_data); ++ void CalcMono(const CalcData& calc_data); ++ void CalcColor(const CalcData& calc_data, FXDIB_Format format, int Bpp); + +- RetainPtr const m_pSrc; ++ RetainPtr const m_pSrc; + const CFX_Matrix m_matrix; + FX_RECT m_StretchClip; + FX_RECT m_result; +diff --git a/core/fxge/dib/cfx_scanlinecompositor.cpp b/core/fxge/dib/cfx_scanlinecompositor.cpp +index 3c90d9687..e9086b753 100644 +--- a/core/fxge/dib/cfx_scanlinecompositor.cpp ++++ b/core/fxge/dib/cfx_scanlinecompositor.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,15 +6,15 @@ + + #include "core/fxge/dib/cfx_scanlinecompositor.h" + ++#include ++ + #include + +-#include "core/fxge/dib/cfx_cmyk_to_srgb.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" + +-#define FX_CCOLOR(val) (255 - (val)) + #define FXDIB_ALPHA_UNION(dest, src) ((dest) + (src) - (dest) * (src) / 255) +-#define FXARGB_COPY(dest, src) \ +- *(dest) = *(src), *((dest) + 1) = *((src) + 1), \ +- *((dest) + 2) = *((src) + 2), *((dest) + 3) = *((src) + 3) + #define FXARGB_RGBORDERCOPY(dest, src) \ + *((dest) + 3) = *((src) + 3), *(dest) = *((src) + 2), \ + *((dest) + 1) = *((src) + 1), *((dest) + 2) = *((src)) +@@ -196,11 +196,14 @@ int GetAlphaWithSrc(uint8_t src_alpha, + return result / 255; + } + +-void CompositeRow_AlphaToMask(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_AlphaToMask(pdfium::span dest_span, ++ pdfium::span src_span, + int pixel_count, +- const uint8_t* clip_scan, ++ pdfium::span clip_span, + uint8_t stride) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + src_scan += stride - 1; + for (int col = 0; col < pixel_count; ++col) { + int src_alpha = GetAlpha(*src_scan, clip_scan, col); +@@ -214,10 +217,11 @@ void CompositeRow_AlphaToMask(uint8_t* dest_scan, + } + } + +-void CompositeRow_Rgb2Mask(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_Rgb2Mask(pdfium::span dest_span, + int width, +- const uint8_t* clip_scan) { ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + if (!clip_scan) { + memset(dest_scan, 0xff, width); + return; +@@ -256,83 +260,37 @@ uint8_t GetGrayWithBlend(const uint8_t* src_scan, + return gray; + } + +-void CompositeRow_Argb2Graya(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int pixel_count, +- BlendMode blend_type, +- const uint8_t* clip_scan, +- const uint8_t* src_alpha_scan, +- uint8_t* dst_alpha_scan) { +- uint8_t offset = src_alpha_scan ? 3 : 4; +- for (int col = 0; col < pixel_count; ++col) { +- const uint8_t* alpha_scan = +- src_alpha_scan ? src_alpha_scan++ : &src_scan[3]; +- uint8_t back_alpha = *dst_alpha_scan; +- if (back_alpha == 0) { +- int src_alpha = GetAlpha(*alpha_scan, clip_scan, col); +- if (src_alpha) { +- *dest_scan = GetGray(src_scan); +- *dst_alpha_scan = src_alpha; +- } +- ++dest_scan; +- ++dst_alpha_scan; +- src_scan += offset; +- continue; +- } +- uint8_t src_alpha = GetAlpha(*alpha_scan, clip_scan, col); +- if (src_alpha == 0) { +- ++dest_scan; +- ++dst_alpha_scan; +- src_scan += offset; +- continue; +- } +- *dst_alpha_scan = FXDIB_ALPHA_UNION(back_alpha, src_alpha); +- int alpha_ratio = src_alpha * 255 / (*dst_alpha_scan); +- uint8_t gray = GetGray(src_scan); +- // TODO(npm): Does this if really need src_alpha_scan or was that a bug? +- if (blend_type != BlendMode::kNormal && src_alpha_scan) { +- if (IsNonSeparableBlendMode(blend_type)) +- gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan; +- else +- gray = Blend(blend_type, *dest_scan, gray); +- } +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio); +- ++dest_scan; +- ++dst_alpha_scan; +- src_scan += offset; +- } +-} +- +-void CompositeRow_Argb2Gray(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_Argb2Gray(pdfium::span dest_span, ++ pdfium::span src_span, + int pixel_count, + BlendMode blend_type, +- const uint8_t* clip_scan, +- const uint8_t* src_alpha_scan) { +- uint8_t gray; +- uint8_t offset = src_alpha_scan ? 3 : 4; ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); ++ constexpr size_t kOffset = 4; + for (int col = 0; col < pixel_count; ++col) { +- const uint8_t* alpha_scan = +- src_alpha_scan ? src_alpha_scan++ : &src_scan[3]; +- int src_alpha = GetAlpha(*alpha_scan, clip_scan, col); ++ int src_alpha = GetAlpha(src_scan[3], clip_scan, col); + if (src_alpha) { +- gray = GetGrayWithBlend(src_scan, dest_scan, blend_type); ++ uint8_t gray = GetGrayWithBlend(src_scan, dest_scan, blend_type); + *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, src_alpha); + } + ++dest_scan; +- src_scan += offset; ++ src_scan += kOffset; + } + } + +-void CompositeRow_Rgb2Gray(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_Rgb2Gray(pdfium::span dest_span, ++ pdfium::span src_span, + int src_Bpp, + int pixel_count, + BlendMode blend_type, +- const uint8_t* clip_scan) { +- uint8_t gray; ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + for (int col = 0; col < pixel_count; ++col) { +- gray = GetGrayWithBlend(src_scan, dest_scan, blend_type); ++ uint8_t gray = GetGrayWithBlend(src_scan, dest_scan, blend_type); + if (clip_scan && clip_scan[col] < 255) + *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, clip_scan[col]); + else +@@ -342,106 +300,38 @@ void CompositeRow_Rgb2Gray(uint8_t* dest_scan, + } + } + +-void CompositeRow_Rgb2Graya(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int src_Bpp, +- int pixel_count, +- BlendMode blend_type, +- const uint8_t* clip_scan, +- uint8_t* dest_alpha_scan) { +- for (int col = 0; col < pixel_count; ++col) { +- if (blend_type != BlendMode::kNormal && *dest_alpha_scan == 0) { +- *dest_scan = GetGray(src_scan); +- ++dest_scan; +- ++dest_alpha_scan; +- src_scan += src_Bpp; +- continue; +- } +- int src_alpha = clip_scan ? clip_scan[col] : 255; +- if (src_alpha == 255) { +- *dest_scan = GetGrayWithBlend(src_scan, dest_scan, blend_type); +- ++dest_scan; +- *dest_alpha_scan = 255; +- ++dest_alpha_scan; +- src_scan += src_Bpp; +- continue; +- } +- if (src_alpha == 0) { +- ++dest_scan; +- ++dest_alpha_scan; +- src_scan += src_Bpp; +- continue; +- } +- int back_alpha = *dest_alpha_scan; +- uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255; +- *dest_alpha_scan = dest_alpha; +- ++dest_alpha_scan; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- uint8_t gray = GetGrayWithBlend(src_scan, dest_scan, blend_type); +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio); +- ++dest_scan; +- src_scan += src_Bpp; +- } +-} +- +-void CompositeRow_Argb2Argb(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_Argb2Argb(pdfium::span dest_span, ++ pdfium::span src_span, + int pixel_count, + BlendMode blend_type, +- const uint8_t* clip_scan, +- uint8_t* dest_alpha_scan, +- const uint8_t* src_alpha_scan) { ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + int blended_colors[3]; +- uint8_t dest_offset = dest_alpha_scan ? 3 : 4; +- uint8_t src_offset = src_alpha_scan ? 3 : 4; ++ constexpr size_t kOffset = 4; + bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); +- bool has_src = !!src_alpha_scan; +- bool has_dest = !!dest_alpha_scan; + for (int col = 0; col < pixel_count; ++col) { +- uint8_t back_alpha = has_dest ? *dest_alpha_scan : dest_scan[3]; +- const uint8_t* alpha_source = has_src ? src_alpha_scan++ : &src_scan[3]; +- uint8_t src_alpha = GetAlpha(*alpha_source, clip_scan, col); ++ uint8_t back_alpha = dest_scan[3]; ++ uint8_t src_alpha = GetAlpha(src_scan[3], clip_scan, col); + if (back_alpha == 0) { +- if (!has_dest && !has_src) { +- if (clip_scan) { +- FXARGB_SETDIB(dest_scan, (FXARGB_GETDIB(src_scan) & 0xffffff) | +- (src_alpha << 24)); +- } else { +- FXARGB_COPY(dest_scan, src_scan); +- } +- } else if (has_dest) { +- *dest_alpha_scan = src_alpha; +- for (int i = 0; i < 3; ++i) { +- *dest_scan = *src_scan++; +- ++dest_scan; +- } +- ++dest_alpha_scan; +- if (!has_src) +- ++src_scan; ++ if (clip_scan) { ++ FXARGB_SETDIB(dest_scan, ++ (FXARGB_GETDIB(src_scan) & 0xffffff) | (src_alpha << 24)); + } else { +- FXARGB_SETDIB(dest_scan, ArgbEncode((src_alpha << 24), src_scan[2], +- src_scan[1], *src_scan)); +- } +- if (!has_dest) { +- dest_scan += dest_offset; +- src_scan += src_offset; ++ memcpy(dest_scan, src_scan, 4); + } ++ dest_scan += kOffset; ++ src_scan += kOffset; + continue; + } + if (src_alpha == 0) { +- dest_scan += dest_offset; +- src_scan += src_offset; +- if (has_dest) +- ++dest_alpha_scan; ++ dest_scan += kOffset; ++ src_scan += kOffset; + continue; + } + uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255; +- if (has_dest) { +- *dest_alpha_scan = dest_alpha; +- ++dest_alpha_scan; +- } else { +- dest_scan[3] = dest_alpha; +- } ++ dest_scan[3] = dest_alpha; + int alpha_ratio = src_alpha * 255 / dest_alpha; + if (bNonseparableBlend) + RGB_Blend(blend_type, src_scan, dest_scan, blended_colors); +@@ -458,42 +348,32 @@ void CompositeRow_Argb2Argb(uint8_t* dest_scan, + ++dest_scan; + ++src_scan; + } +- if (!has_dest) +- ++dest_scan; +- if (!has_src) +- ++src_scan; ++ ++dest_scan; ++ ++src_scan; + } + } + +-void CompositeRow_Rgb2Argb_Blend_NoClip(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_Rgb2Argb_Blend_NoClip(pdfium::span dest_span, ++ pdfium::span src_span, + int width, + BlendMode blend_type, +- int src_Bpp, +- uint8_t* dest_alpha_scan) { ++ int src_Bpp) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); + int blended_colors[3]; + bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); + int src_gap = src_Bpp - 3; + for (int col = 0; col < width; ++col) { +- uint8_t* dest_alpha = dest_alpha_scan ? dest_alpha_scan : &dest_scan[3]; ++ uint8_t* dest_alpha = &dest_scan[3]; + uint8_t back_alpha = *dest_alpha; + if (back_alpha == 0) { +- if (dest_alpha_scan) { +- for (int i = 0; i < 3; ++i) { +- *dest_scan = *src_scan++; +- ++dest_scan; +- } +- *dest_alpha_scan = 0xff; +- ++dest_alpha_scan; ++ if (src_Bpp == 4) { ++ FXARGB_SETDIB(dest_scan, 0xff000000 | FXARGB_GETDIB(src_scan)); + } else { +- if (src_Bpp == 4) { +- FXARGB_SETDIB(dest_scan, 0xff000000 | FXARGB_GETDIB(src_scan)); +- } else { +- FXARGB_SETDIB(dest_scan, ArgbEncode(0xff, src_scan[2], src_scan[1], +- src_scan[0])); +- } +- dest_scan += 4; ++ FXARGB_SETDIB(dest_scan, ++ ArgbEncode(0xff, src_scan[2], src_scan[1], src_scan[0])); + } ++ dest_scan += 4; + src_scan += src_Bpp; + continue; + } +@@ -509,52 +389,40 @@ void CompositeRow_Rgb2Argb_Blend_NoClip(uint8_t* dest_scan, + ++dest_scan; + ++src_scan; + } +- if (dest_alpha_scan) +- ++dest_alpha_scan; +- else +- ++dest_scan; ++ ++dest_scan; + src_scan += src_gap; + } + } + +-void CompositeRow_Rgb2Argb_Blend_Clip(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_Rgb2Argb_Blend_Clip(pdfium::span dest_span, ++ pdfium::span src_span, + int width, + BlendMode blend_type, + int src_Bpp, +- const uint8_t* clip_scan, +- uint8_t* dest_alpha_scan) { ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + int blended_colors[3]; + bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); + int src_gap = src_Bpp - 3; +- bool has_dest = !!dest_alpha_scan; + for (int col = 0; col < width; ++col) { + int src_alpha = *clip_scan++; +- uint8_t back_alpha = has_dest ? *dest_alpha_scan : dest_scan[3]; ++ uint8_t back_alpha = dest_scan[3]; + if (back_alpha == 0) { +- for (int i = 0; i < 3; ++i) { +- *dest_scan = *src_scan++; +- ++dest_scan; +- } +- src_scan += src_gap; +- if (has_dest) +- dest_alpha_scan++; +- else +- dest_scan++; ++ memcpy(dest_scan, src_scan, 3); ++ dest_scan += 3; ++ src_scan += src_Bpp; ++ dest_scan++; + continue; + } + if (src_alpha == 0) { +- dest_scan += has_dest ? 3 : 4; +- if (has_dest) +- dest_alpha_scan++; ++ dest_scan += 4; + src_scan += src_Bpp; + continue; + } + uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255; +- if (has_dest) +- *dest_alpha_scan++ = dest_alpha; +- else +- dest_scan[3] = dest_alpha; ++ dest_scan[3] = dest_alpha; + int alpha_ratio = src_alpha * 255 / dest_alpha; + if (bNonseparableBlend) + RGB_Blend(blend_type, src_scan, dest_scan, blended_colors); +@@ -569,247 +437,151 @@ void CompositeRow_Rgb2Argb_Blend_Clip(uint8_t* dest_scan, + src_scan++; + } + src_scan += src_gap; +- if (!has_dest) +- dest_scan++; ++ dest_scan++; + } + } + +-void CompositeRow_Rgb2Argb_NoBlend_Clip(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_Rgb2Argb_NoBlend_Clip(pdfium::span dest_span, ++ pdfium::span src_span, + int width, + int src_Bpp, +- const uint8_t* clip_scan, +- uint8_t* dest_alpha_scan) { ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + int src_gap = src_Bpp - 3; +- if (dest_alpha_scan) { +- for (int col = 0; col < width; col++) { +- int src_alpha = clip_scan[col]; +- if (src_alpha == 255) { +- *dest_scan++ = *src_scan++; +- *dest_scan++ = *src_scan++; +- *dest_scan++ = *src_scan++; +- *dest_alpha_scan++ = 255; +- src_scan += src_gap; +- continue; +- } +- if (src_alpha == 0) { +- dest_scan += 3; +- dest_alpha_scan++; +- src_scan += src_Bpp; +- continue; +- } +- int back_alpha = *dest_alpha_scan; +- uint8_t dest_alpha = +- back_alpha + src_alpha - back_alpha * src_alpha / 255; +- *dest_alpha_scan++ = dest_alpha; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- for (int color = 0; color < 3; color++) { +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, *src_scan, alpha_ratio); +- dest_scan++; +- src_scan++; +- } +- src_scan += src_gap; ++ for (int col = 0; col < width; col++) { ++ int src_alpha = clip_scan[col]; ++ if (src_alpha == 255) { ++ memcpy(dest_scan, src_scan, 3); ++ dest_scan += 3; ++ *dest_scan++ = 255; ++ src_scan += src_Bpp; ++ continue; + } +- } else { +- for (int col = 0; col < width; col++) { +- int src_alpha = clip_scan[col]; +- if (src_alpha == 255) { +- *dest_scan++ = *src_scan++; +- *dest_scan++ = *src_scan++; +- *dest_scan++ = *src_scan++; +- *dest_scan++ = 255; +- src_scan += src_gap; +- continue; +- } +- if (src_alpha == 0) { +- dest_scan += 4; +- src_scan += src_Bpp; +- continue; +- } +- int back_alpha = dest_scan[3]; +- uint8_t dest_alpha = +- back_alpha + src_alpha - back_alpha * src_alpha / 255; +- dest_scan[3] = dest_alpha; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- for (int color = 0; color < 3; color++) { +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, *src_scan, alpha_ratio); +- dest_scan++; +- src_scan++; +- } ++ if (src_alpha == 0) { ++ dest_scan += 4; ++ src_scan += src_Bpp; ++ continue; ++ } ++ int back_alpha = dest_scan[3]; ++ uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255; ++ dest_scan[3] = dest_alpha; ++ int alpha_ratio = src_alpha * 255 / dest_alpha; ++ for (int color = 0; color < 3; color++) { ++ *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, *src_scan, alpha_ratio); + dest_scan++; +- src_scan += src_gap; ++ src_scan++; + } ++ dest_scan++; ++ src_scan += src_gap; + } + } + +-void CompositeRow_Rgb2Argb_NoBlend_NoClip(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_Rgb2Argb_NoBlend_NoClip(pdfium::span dest_span, ++ pdfium::span src_span, + int width, +- int src_Bpp, +- uint8_t* dest_alpha_scan) { +- if (dest_alpha_scan) { +- int src_gap = src_Bpp - 3; +- for (int col = 0; col < width; col++) { +- *dest_scan++ = *src_scan++; +- *dest_scan++ = *src_scan++; +- *dest_scan++ = *src_scan++; +- *dest_alpha_scan++ = 0xff; +- src_scan += src_gap; +- } +- } else { +- for (int col = 0; col < width; col++) { +- if (src_Bpp == 4) { +- FXARGB_SETDIB(dest_scan, 0xff000000 | FXARGB_GETDIB(src_scan)); +- } else { +- FXARGB_SETDIB(dest_scan, +- ArgbEncode(0xff, src_scan[2], src_scan[1], src_scan[0])); +- } +- dest_scan += 4; +- src_scan += src_Bpp; ++ int src_Bpp) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ for (int col = 0; col < width; col++) { ++ if (src_Bpp == 4) { ++ FXARGB_SETDIB(dest_scan, 0xff000000 | FXARGB_GETDIB(src_scan)); ++ } else { ++ FXARGB_SETDIB(dest_scan, ++ ArgbEncode(0xff, src_scan[2], src_scan[1], src_scan[0])); + } ++ dest_scan += 4; ++ src_scan += src_Bpp; + } + } + +-void CompositeRow_Argb2Rgb_Blend(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_Argb2Rgb_Blend(pdfium::span dest_span, ++ pdfium::span src_span, + int width, + BlendMode blend_type, + int dest_Bpp, +- const uint8_t* clip_scan, +- const uint8_t* src_alpha_scan) { ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + int blended_colors[3]; + bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); + int dest_gap = dest_Bpp - 3; +- if (src_alpha_scan) { +- for (int col = 0; col < width; col++) { +- uint8_t src_alpha; +- if (clip_scan) { +- src_alpha = (*src_alpha_scan++) * (*clip_scan++) / 255; +- } else { +- src_alpha = *src_alpha_scan++; +- } +- if (src_alpha == 0) { +- dest_scan += dest_Bpp; +- src_scan += 3; +- continue; +- } +- if (bNonseparableBlend) { +- RGB_Blend(blend_type, src_scan, dest_scan, blended_colors); +- } +- for (int color = 0; color < 3; color++) { +- int back_color = *dest_scan; +- int blended = bNonseparableBlend +- ? blended_colors[color] +- : Blend(blend_type, back_color, *src_scan); +- *dest_scan = FXDIB_ALPHA_MERGE(back_color, blended, src_alpha); +- dest_scan++; +- src_scan++; +- } +- dest_scan += dest_gap; ++ for (int col = 0; col < width; col++) { ++ uint8_t src_alpha; ++ if (clip_scan) { ++ src_alpha = src_scan[3] * (*clip_scan++) / 255; ++ } else { ++ src_alpha = src_scan[3]; + } +- } else { +- for (int col = 0; col < width; col++) { +- uint8_t src_alpha; +- if (clip_scan) { +- src_alpha = src_scan[3] * (*clip_scan++) / 255; +- } else { +- src_alpha = src_scan[3]; +- } +- if (src_alpha == 0) { +- dest_scan += dest_Bpp; +- src_scan += 4; +- continue; +- } +- if (bNonseparableBlend) { +- RGB_Blend(blend_type, src_scan, dest_scan, blended_colors); +- } +- for (int color = 0; color < 3; color++) { +- int back_color = *dest_scan; +- int blended = bNonseparableBlend +- ? blended_colors[color] +- : Blend(blend_type, back_color, *src_scan); +- *dest_scan = FXDIB_ALPHA_MERGE(back_color, blended, src_alpha); +- dest_scan++; +- src_scan++; +- } +- dest_scan += dest_gap; ++ if (src_alpha == 0) { ++ dest_scan += dest_Bpp; ++ src_scan += 4; ++ continue; ++ } ++ if (bNonseparableBlend) { ++ RGB_Blend(blend_type, src_scan, dest_scan, blended_colors); ++ } ++ for (int color = 0; color < 3; color++) { ++ int back_color = *dest_scan; ++ int blended = bNonseparableBlend ++ ? blended_colors[color] ++ : Blend(blend_type, back_color, *src_scan); ++ *dest_scan = FXDIB_ALPHA_MERGE(back_color, blended, src_alpha); ++ dest_scan++; + src_scan++; + } ++ dest_scan += dest_gap; ++ src_scan++; + } + } + +-void CompositeRow_Argb2Rgb_NoBlend(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_Argb2Rgb_NoBlend(pdfium::span dest_span, ++ pdfium::span src_span, + int width, + int dest_Bpp, +- const uint8_t* clip_scan, +- const uint8_t* src_alpha_scan) { ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + int dest_gap = dest_Bpp - 3; +- if (src_alpha_scan) { +- for (int col = 0; col < width; col++) { +- uint8_t src_alpha; +- if (clip_scan) { +- src_alpha = (*src_alpha_scan++) * (*clip_scan++) / 255; +- } else { +- src_alpha = *src_alpha_scan++; +- } +- if (src_alpha == 255) { +- *dest_scan++ = *src_scan++; +- *dest_scan++ = *src_scan++; +- *dest_scan++ = *src_scan++; +- dest_scan += dest_gap; +- continue; +- } +- if (src_alpha == 0) { +- dest_scan += dest_Bpp; +- src_scan += 3; +- continue; +- } +- for (int color = 0; color < 3; color++) { +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, *src_scan, src_alpha); +- dest_scan++; +- src_scan++; +- } +- dest_scan += dest_gap; ++ for (int col = 0; col < width; col++) { ++ uint8_t src_alpha; ++ if (clip_scan) { ++ src_alpha = src_scan[3] * (*clip_scan++) / 255; ++ } else { ++ src_alpha = src_scan[3]; + } +- } else { +- for (int col = 0; col < width; col++) { +- uint8_t src_alpha; +- if (clip_scan) { +- src_alpha = src_scan[3] * (*clip_scan++) / 255; +- } else { +- src_alpha = src_scan[3]; +- } +- if (src_alpha == 255) { +- *dest_scan++ = *src_scan++; +- *dest_scan++ = *src_scan++; +- *dest_scan++ = *src_scan++; +- dest_scan += dest_gap; +- src_scan++; +- continue; +- } +- if (src_alpha == 0) { +- dest_scan += dest_Bpp; +- src_scan += 4; +- continue; +- } +- for (int color = 0; color < 3; color++) { +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, *src_scan, src_alpha); +- dest_scan++; +- src_scan++; +- } +- dest_scan += dest_gap; ++ if (src_alpha == 255) { ++ memcpy(dest_scan, src_scan, 3); ++ dest_scan += dest_Bpp; ++ src_scan += 4; ++ continue; ++ } ++ if (src_alpha == 0) { ++ dest_scan += dest_Bpp; ++ src_scan += 4; ++ continue; ++ } ++ for (int color = 0; color < 3; color++) { ++ *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, *src_scan, src_alpha); ++ dest_scan++; + src_scan++; + } ++ dest_scan += dest_gap; ++ src_scan++; + } + } + +-void CompositeRow_Rgb2Rgb_Blend_NoClip(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_Rgb2Rgb_Blend_NoClip(pdfium::span dest_span, ++ pdfium::span src_span, + int width, + BlendMode blend_type, + int dest_Bpp, + int src_Bpp) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); + int blended_colors[3]; + bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); + int dest_gap = dest_Bpp - 3; +@@ -833,13 +605,16 @@ void CompositeRow_Rgb2Rgb_Blend_NoClip(uint8_t* dest_scan, + } + } + +-void CompositeRow_Rgb2Rgb_Blend_Clip(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_Rgb2Rgb_Blend_Clip(pdfium::span dest_span, ++ pdfium::span src_span, + int width, + BlendMode blend_type, + int dest_Bpp, + int src_Bpp, +- const uint8_t* clip_scan) { ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + int blended_colors[3]; + bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); + int dest_gap = dest_Bpp - 3; +@@ -869,36 +644,37 @@ void CompositeRow_Rgb2Rgb_Blend_Clip(uint8_t* dest_scan, + } + } + +-void CompositeRow_Rgb2Rgb_NoBlend_NoClip(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_Rgb2Rgb_NoBlend_NoClip(pdfium::span dest_span, ++ pdfium::span src_span, + int width, + int dest_Bpp, + int src_Bpp) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); + if (dest_Bpp == src_Bpp) { + memcpy(dest_scan, src_scan, width * dest_Bpp); + return; + } + for (int col = 0; col < width; col++) { +- dest_scan[0] = src_scan[0]; +- dest_scan[1] = src_scan[1]; +- dest_scan[2] = src_scan[2]; ++ memcpy(dest_scan, src_scan, 3); + dest_scan += dest_Bpp; + src_scan += src_Bpp; + } + } + +-void CompositeRow_Rgb2Rgb_NoBlend_Clip(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_Rgb2Rgb_NoBlend_Clip(pdfium::span dest_span, ++ pdfium::span src_span, + int width, + int dest_Bpp, + int src_Bpp, +- const uint8_t* clip_scan) { ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + for (int col = 0; col < width; col++) { + int src_alpha = clip_scan[col]; + if (src_alpha == 255) { +- dest_scan[0] = src_scan[0]; +- dest_scan[1] = src_scan[1]; +- dest_scan[2] = src_scan[2]; ++ memcpy(dest_scan, src_scan, 3); + } else if (src_alpha) { + *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, *src_scan, src_alpha); + dest_scan++; +@@ -916,62 +692,24 @@ void CompositeRow_Rgb2Rgb_NoBlend_Clip(uint8_t* dest_scan, + } + } + +-void CompositeRow_8bppPal2Gray(uint8_t* dest_scan, +- const uint8_t* src_scan, +- const uint8_t* pPalette, ++void CompositeRow_8bppPal2Gray(pdfium::span dest_span, ++ pdfium::span src_span, ++ pdfium::span palette_span, + int pixel_count, + BlendMode blend_type, +- const uint8_t* clip_scan, +- const uint8_t* src_alpha_scan) { +- if (src_alpha_scan) { +- if (blend_type != BlendMode::kNormal) { +- bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); +- for (int col = 0; col < pixel_count; col++) { +- uint8_t gray = pPalette[*src_scan]; +- int src_alpha = GetAlpha(*src_alpha_scan++, clip_scan, col); +- if (bNonseparableBlend) +- gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan; +- else +- gray = Blend(blend_type, *dest_scan, gray); +- if (src_alpha) +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, src_alpha); +- else +- *dest_scan = gray; +- dest_scan++; +- src_scan++; +- } +- return; +- } ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); ++ const uint8_t* pPalette = palette_span.data(); ++ if (blend_type != BlendMode::kNormal) { ++ bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); + for (int col = 0; col < pixel_count; col++) { + uint8_t gray = pPalette[*src_scan]; +- int src_alpha = GetAlpha(*src_alpha_scan++, clip_scan, col); +- if (src_alpha) +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, src_alpha); ++ if (bNonseparableBlend) ++ gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan; + else +- *dest_scan = gray; +- dest_scan++; +- src_scan++; +- } +- } else { +- if (blend_type != BlendMode::kNormal) { +- bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); +- for (int col = 0; col < pixel_count; col++) { +- uint8_t gray = pPalette[*src_scan]; +- if (bNonseparableBlend) +- gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan; +- else +- gray = Blend(blend_type, *dest_scan, gray); +- if (clip_scan && clip_scan[col] < 255) +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, clip_scan[col]); +- else +- *dest_scan = gray; +- dest_scan++; +- src_scan++; +- } +- return; +- } +- for (int col = 0; col < pixel_count; col++) { +- uint8_t gray = pPalette[*src_scan]; ++ gray = Blend(blend_type, *dest_scan, gray); + if (clip_scan && clip_scan[col] < 255) + *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, clip_scan[col]); + else +@@ -979,144 +717,31 @@ void CompositeRow_8bppPal2Gray(uint8_t* dest_scan, + dest_scan++; + src_scan++; + } ++ return; + } +-} +- +-void CompositeRow_8bppPal2Graya(uint8_t* dest_scan, +- const uint8_t* src_scan, +- const uint8_t* pPalette, +- int pixel_count, +- BlendMode blend_type, +- const uint8_t* clip_scan, +- uint8_t* dest_alpha_scan, +- const uint8_t* src_alpha_scan) { +- if (src_alpha_scan) { +- if (blend_type != BlendMode::kNormal) { +- bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); +- for (int col = 0; col < pixel_count; col++) { +- uint8_t gray = pPalette[*src_scan]; +- src_scan++; +- uint8_t back_alpha = *dest_alpha_scan; +- if (back_alpha == 0) { +- int src_alpha = GetAlpha(*src_alpha_scan++, clip_scan, col); +- if (src_alpha) { +- *dest_scan = gray; +- *dest_alpha_scan = src_alpha; +- } +- dest_scan++; +- dest_alpha_scan++; +- continue; +- } +- uint8_t src_alpha = GetAlpha(*src_alpha_scan++, clip_scan, col); +- if (src_alpha == 0) { +- dest_scan++; +- dest_alpha_scan++; +- continue; +- } +- *dest_alpha_scan = +- back_alpha + src_alpha - back_alpha * src_alpha / 255; +- int alpha_ratio = src_alpha * 255 / (*dest_alpha_scan); +- if (bNonseparableBlend) +- gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan; +- else +- gray = Blend(blend_type, *dest_scan, gray); +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio); +- dest_alpha_scan++; +- dest_scan++; +- } +- return; +- } +- for (int col = 0; col < pixel_count; col++) { +- uint8_t gray = pPalette[*src_scan]; +- src_scan++; +- uint8_t back_alpha = *dest_alpha_scan; +- if (back_alpha == 0) { +- int src_alpha = GetAlpha(*src_alpha_scan++, clip_scan, col); +- if (src_alpha) { +- *dest_scan = gray; +- *dest_alpha_scan = src_alpha; +- } +- dest_scan++; +- dest_alpha_scan++; +- continue; +- } +- uint8_t src_alpha = GetAlpha(*src_alpha_scan++, clip_scan, col); +- if (src_alpha == 0) { +- dest_scan++; +- dest_alpha_scan++; +- continue; +- } +- *dest_alpha_scan = back_alpha + src_alpha - back_alpha * src_alpha / 255; +- int alpha_ratio = src_alpha * 255 / (*dest_alpha_scan); +- dest_alpha_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio); +- dest_scan++; +- } +- } else { +- if (blend_type != BlendMode::kNormal) { +- bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); +- for (int col = 0; col < pixel_count; col++) { +- uint8_t gray = pPalette[*src_scan]; +- src_scan++; +- if (!clip_scan || clip_scan[col] == 255) { +- *dest_scan++ = gray; +- *dest_alpha_scan++ = 255; +- continue; +- } +- int src_alpha = clip_scan[col]; +- if (src_alpha == 0) { +- dest_scan++; +- dest_alpha_scan++; +- continue; +- } +- int back_alpha = *dest_alpha_scan; +- uint8_t dest_alpha = +- back_alpha + src_alpha - back_alpha * src_alpha / 255; +- *dest_alpha_scan++ = dest_alpha; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- if (bNonseparableBlend) +- gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan; +- else +- gray = Blend(blend_type, *dest_scan, gray); +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio); +- dest_scan++; +- } +- return; +- } +- for (int col = 0; col < pixel_count; col++) { +- uint8_t gray = pPalette[*src_scan]; +- src_scan++; +- if (!clip_scan || clip_scan[col] == 255) { +- *dest_scan++ = gray; +- *dest_alpha_scan++ = 255; +- continue; +- } +- int src_alpha = clip_scan[col]; +- if (src_alpha == 0) { +- dest_scan++; +- dest_alpha_scan++; +- continue; +- } +- int back_alpha = *dest_alpha_scan; +- uint8_t dest_alpha = +- back_alpha + src_alpha - back_alpha * src_alpha / 255; +- *dest_alpha_scan++ = dest_alpha; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio); +- dest_scan++; +- } ++ for (int col = 0; col < pixel_count; col++) { ++ uint8_t gray = pPalette[*src_scan]; ++ if (clip_scan && clip_scan[col] < 255) ++ *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, clip_scan[col]); ++ else ++ *dest_scan = gray; ++ dest_scan++; ++ src_scan++; + } + } + +-void CompositeRow_1bppPal2Gray(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_1bppPal2Gray(pdfium::span dest_span, ++ pdfium::span src_span, + int src_left, +- const uint8_t* pPalette, ++ pdfium::span src_palette, + int pixel_count, + BlendMode blend_type, +- const uint8_t* clip_scan) { +- int reset_gray = pPalette[0]; +- int set_gray = pPalette[1]; ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); ++ int reset_gray = src_palette[0]; ++ int set_gray = src_palette[1]; + if (blend_type != BlendMode::kNormal) { + bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); + for (int col = 0; col < pixel_count; col++) { +@@ -1151,158 +776,61 @@ void CompositeRow_1bppPal2Gray(uint8_t* dest_scan, + } + } + +-void CompositeRow_1bppPal2Graya(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int src_left, +- const uint8_t* pPalette, +- int pixel_count, +- BlendMode blend_type, +- const uint8_t* clip_scan, +- uint8_t* dest_alpha_scan) { +- int reset_gray = pPalette[0]; +- int set_gray = pPalette[1]; +- if (blend_type != BlendMode::kNormal) { +- bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); +- for (int col = 0; col < pixel_count; col++) { +- uint8_t gray = +- (src_scan[(col + src_left) / 8] & (1 << (7 - (col + src_left) % 8))) +- ? set_gray +- : reset_gray; +- if (!clip_scan || clip_scan[col] == 255) { +- *dest_scan++ = gray; +- *dest_alpha_scan++ = 255; +- continue; +- } +- int src_alpha = clip_scan[col]; +- if (src_alpha == 0) { +- dest_scan++; +- dest_alpha_scan++; +- continue; +- } +- int back_alpha = *dest_alpha_scan; +- uint8_t dest_alpha = +- back_alpha + src_alpha - back_alpha * src_alpha / 255; +- *dest_alpha_scan++ = dest_alpha; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- if (bNonseparableBlend) +- gray = blend_type == BlendMode::kLuminosity ? gray : *dest_scan; +- else +- gray = Blend(blend_type, *dest_scan, gray); +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio); +- dest_scan++; +- } +- return; +- } +- for (int col = 0; col < pixel_count; col++) { +- uint8_t gray = +- (src_scan[(col + src_left) / 8] & (1 << (7 - (col + src_left) % 8))) +- ? set_gray +- : reset_gray; +- if (!clip_scan || clip_scan[col] == 255) { +- *dest_scan++ = gray; +- *dest_alpha_scan++ = 255; +- continue; +- } +- int src_alpha = clip_scan[col]; +- if (src_alpha == 0) { +- dest_scan++; +- dest_alpha_scan++; +- continue; +- } +- int back_alpha = *dest_alpha_scan; +- uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255; +- *dest_alpha_scan++ = dest_alpha; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, alpha_ratio); +- dest_scan++; +- } +-} +- +-void CompositeRow_8bppRgb2Rgb_NoBlend(uint8_t* dest_scan, +- const uint8_t* src_scan, +- uint32_t* pPalette, ++void CompositeRow_8bppRgb2Rgb_NoBlend(pdfium::span dest_span, ++ pdfium::span src_span, ++ pdfium::span palette_span, + int pixel_count, + int DestBpp, +- const uint8_t* clip_scan, +- const uint8_t* src_alpha_scan) { +- if (src_alpha_scan) { +- int dest_gap = DestBpp - 3; +- FX_ARGB argb = 0; +- for (int col = 0; col < pixel_count; col++) { +- argb = pPalette[*src_scan]; +- int src_r = FXARGB_R(argb); +- int src_g = FXARGB_G(argb); +- int src_b = FXARGB_B(argb); +- src_scan++; +- uint8_t src_alpha = 0; +- if (clip_scan) { +- src_alpha = (*src_alpha_scan++) * (*clip_scan++) / 255; +- } else { +- src_alpha = *src_alpha_scan++; +- } +- if (src_alpha == 255) { +- *dest_scan++ = src_b; +- *dest_scan++ = src_g; +- *dest_scan++ = src_r; +- dest_scan += dest_gap; +- continue; +- } +- if (src_alpha == 0) { +- dest_scan += DestBpp; +- continue; +- } +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, src_alpha); ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); ++ const uint32_t* pPalette = palette_span.data(); ++ FX_ARGB argb = 0; ++ for (int col = 0; col < pixel_count; col++) { ++ argb = pPalette[*src_scan]; ++ int src_r = FXARGB_R(argb); ++ int src_g = FXARGB_G(argb); ++ int src_b = FXARGB_B(argb); ++ if (clip_scan && clip_scan[col] < 255) { ++ *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, clip_scan[col]); + dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, src_alpha); ++ *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, clip_scan[col]); + dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, src_alpha); ++ *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, clip_scan[col]); + dest_scan++; +- dest_scan += dest_gap; ++ } else { ++ *dest_scan++ = src_b; ++ *dest_scan++ = src_g; ++ *dest_scan++ = src_r; + } +- } else { +- FX_ARGB argb = 0; +- for (int col = 0; col < pixel_count; col++) { +- argb = pPalette[*src_scan]; +- int src_r = FXARGB_R(argb); +- int src_g = FXARGB_G(argb); +- int src_b = FXARGB_B(argb); +- if (clip_scan && clip_scan[col] < 255) { +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, clip_scan[col]); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, clip_scan[col]); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, clip_scan[col]); +- dest_scan++; +- } else { +- *dest_scan++ = src_b; +- *dest_scan++ = src_g; +- *dest_scan++ = src_r; +- } +- if (DestBpp == 4) { +- dest_scan++; +- } +- src_scan++; ++ if (DestBpp == 4) { ++ dest_scan++; + } ++ src_scan++; + } + } + +-void CompositeRow_1bppRgb2Rgb_NoBlend(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_1bppRgb2Rgb_NoBlend(pdfium::span dest_span, ++ pdfium::span src_span, + int src_left, +- uint32_t* pPalette, ++ pdfium::span src_palette, + int pixel_count, + int DestBpp, +- const uint8_t* clip_scan) { +- int reset_r, reset_g, reset_b; +- int set_r, set_g, set_b; +- reset_r = FXARGB_R(pPalette[0]); +- reset_g = FXARGB_G(pPalette[0]); +- reset_b = FXARGB_B(pPalette[0]); +- set_r = FXARGB_R(pPalette[1]); +- set_g = FXARGB_G(pPalette[1]); +- set_b = FXARGB_B(pPalette[1]); ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); ++ int reset_r = FXARGB_R(src_palette[0]); ++ int reset_g = FXARGB_G(src_palette[0]); ++ int reset_b = FXARGB_B(src_palette[0]); ++ int set_r = FXARGB_R(src_palette[1]); ++ int set_g = FXARGB_G(src_palette[1]); ++ int set_b = FXARGB_B(src_palette[1]); + for (int col = 0; col < pixel_count; col++) { +- int src_r, src_g, src_b; ++ int src_r; ++ int src_g; ++ int src_b; + if (src_scan[(col + src_left) / 8] & (1 << (7 - (col + src_left) % 8))) { + src_r = set_r; + src_g = set_g; +@@ -1330,122 +858,33 @@ void CompositeRow_1bppRgb2Rgb_NoBlend(uint8_t* dest_scan, + } + } + +-void CompositeRow_8bppRgb2Argb_NoBlend(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int width, +- uint32_t* pPalette, +- const uint8_t* clip_scan, +- const uint8_t* src_alpha_scan) { +- if (src_alpha_scan) { +- for (int col = 0; col < width; col++) { +- FX_ARGB argb = pPalette[*src_scan]; +- src_scan++; +- int src_r = FXARGB_R(argb); +- int src_g = FXARGB_G(argb); +- int src_b = FXARGB_B(argb); +- uint8_t back_alpha = dest_scan[3]; +- if (back_alpha == 0) { +- if (clip_scan) { +- int src_alpha = clip_scan[col] * (*src_alpha_scan) / 255; +- FXARGB_SETDIB(dest_scan, ArgbEncode(src_alpha, src_r, src_g, src_b)); +- } else { +- FXARGB_SETDIB(dest_scan, +- ArgbEncode(*src_alpha_scan, src_r, src_g, src_b)); +- } +- dest_scan += 4; +- src_alpha_scan++; +- continue; +- } +- uint8_t src_alpha = GetAlpha(*src_alpha_scan, clip_scan, col); +- ++src_alpha_scan; +- if (src_alpha == 0) { +- dest_scan += 4; +- continue; +- } +- uint8_t dest_alpha = +- back_alpha + src_alpha - back_alpha * src_alpha / 255; +- dest_scan[3] = dest_alpha; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, alpha_ratio); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, alpha_ratio); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, alpha_ratio); +- dest_scan++; +- dest_scan++; +- } +- } else { +- for (int col = 0; col < width; col++) { +- FX_ARGB argb = pPalette[*src_scan]; +- int src_r = FXARGB_R(argb); +- int src_g = FXARGB_G(argb); +- int src_b = FXARGB_B(argb); +- if (!clip_scan || clip_scan[col] == 255) { +- *dest_scan++ = src_b; +- *dest_scan++ = src_g; +- *dest_scan++ = src_r; +- *dest_scan++ = 255; +- src_scan++; +- continue; +- } +- int src_alpha = clip_scan[col]; +- if (src_alpha == 0) { +- dest_scan += 4; +- src_scan++; +- continue; +- } +- int back_alpha = dest_scan[3]; +- uint8_t dest_alpha = +- back_alpha + src_alpha - back_alpha * src_alpha / 255; +- dest_scan[3] = dest_alpha; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, alpha_ratio); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, alpha_ratio); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, alpha_ratio); +- dest_scan++; +- dest_scan++; +- src_scan++; +- } +- } +-} +- +-void CompositeRow_1bppRgb2Argb_NoBlend(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int src_left, +- int width, +- uint32_t* pPalette, +- const uint8_t* clip_scan) { +- int reset_r, reset_g, reset_b; +- int set_r, set_g, set_b; +- reset_r = FXARGB_R(pPalette[0]); +- reset_g = FXARGB_G(pPalette[0]); +- reset_b = FXARGB_B(pPalette[0]); +- set_r = FXARGB_R(pPalette[1]); +- set_g = FXARGB_G(pPalette[1]); +- set_b = FXARGB_B(pPalette[1]); ++void CompositeRow_8bppRgb2Argb_NoBlend( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int width, ++ pdfium::span palette_span, ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); ++ const uint32_t* pPalette = palette_span.data(); + for (int col = 0; col < width; col++) { +- int src_r, src_g, src_b; +- if (src_scan[(col + src_left) / 8] & (1 << (7 - (col + src_left) % 8))) { +- src_r = set_r; +- src_g = set_g; +- src_b = set_b; +- } else { +- src_r = reset_r; +- src_g = reset_g; +- src_b = reset_b; +- } ++ FX_ARGB argb = pPalette[*src_scan]; ++ int src_r = FXARGB_R(argb); ++ int src_g = FXARGB_G(argb); ++ int src_b = FXARGB_B(argb); + if (!clip_scan || clip_scan[col] == 255) { + *dest_scan++ = src_b; + *dest_scan++ = src_g; + *dest_scan++ = src_r; + *dest_scan++ = 255; ++ src_scan++; + continue; + } + int src_alpha = clip_scan[col]; + if (src_alpha == 0) { + dest_scan += 4; ++ src_scan++; + continue; + } + int back_alpha = dest_scan[3]; +@@ -1459,26 +898,29 @@ void CompositeRow_1bppRgb2Argb_NoBlend(uint8_t* dest_scan, + *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, alpha_ratio); + dest_scan++; + dest_scan++; ++ src_scan++; + } + } + +-void CompositeRow_1bppRgb2Rgba_NoBlend(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_1bppRgb2Argb_NoBlend(pdfium::span dest_span, ++ pdfium::span src_span, + int src_left, + int width, +- uint32_t* pPalette, +- const uint8_t* clip_scan, +- uint8_t* dest_alpha_scan) { +- int reset_r, reset_g, reset_b; +- int set_r, set_g, set_b; +- reset_r = FXARGB_R(pPalette[0]); +- reset_g = FXARGB_G(pPalette[0]); +- reset_b = FXARGB_B(pPalette[0]); +- set_r = FXARGB_R(pPalette[1]); +- set_g = FXARGB_G(pPalette[1]); +- set_b = FXARGB_B(pPalette[1]); ++ pdfium::span src_palette, ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); ++ int reset_r = FXARGB_R(src_palette[0]); ++ int reset_g = FXARGB_G(src_palette[0]); ++ int reset_b = FXARGB_B(src_palette[0]); ++ int set_r = FXARGB_R(src_palette[1]); ++ int set_g = FXARGB_G(src_palette[1]); ++ int set_b = FXARGB_B(src_palette[1]); + for (int col = 0; col < width; col++) { +- int src_r, src_g, src_b; ++ int src_r; ++ int src_g; ++ int src_b; + if (src_scan[(col + src_left) / 8] & (1 << (7 - (col + src_left) % 8))) { + src_r = set_r; + src_g = set_g; +@@ -1492,116 +934,54 @@ void CompositeRow_1bppRgb2Rgba_NoBlend(uint8_t* dest_scan, + *dest_scan++ = src_b; + *dest_scan++ = src_g; + *dest_scan++ = src_r; +- *dest_alpha_scan++ = 255; +- continue; +- } +- int src_alpha = clip_scan[col]; +- if (src_alpha == 0) { +- dest_scan += 3; +- dest_alpha_scan++; +- continue; +- } +- int back_alpha = *dest_alpha_scan; +- uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255; +- *dest_alpha_scan++ = dest_alpha; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, alpha_ratio); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, alpha_ratio); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, alpha_ratio); +- dest_scan++; +- } +-} +- +-void CompositeRow_ByteMask2Argb(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int mask_alpha, +- int src_r, +- int src_g, +- int src_b, +- int pixel_count, +- BlendMode blend_type, +- const uint8_t* clip_scan) { +- for (int col = 0; col < pixel_count; col++) { +- int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col); +- uint8_t back_alpha = dest_scan[3]; +- if (back_alpha == 0) { +- FXARGB_SETDIB(dest_scan, ArgbEncode(src_alpha, src_r, src_g, src_b)); +- dest_scan += 4; +- continue; +- } +- if (src_alpha == 0) { +- dest_scan += 4; ++ *dest_scan++ = 255; + continue; + } +- uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255; +- dest_scan[3] = dest_alpha; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- if (IsNonSeparableBlendMode(blend_type)) { +- int blended_colors[3]; +- uint8_t scan[3] = {static_cast(src_b), +- static_cast(src_g), +- static_cast(src_r)}; +- RGB_Blend(blend_type, scan, dest_scan, blended_colors); +- *dest_scan = +- FXDIB_ALPHA_MERGE(*dest_scan, blended_colors[0], alpha_ratio); +- dest_scan++; +- *dest_scan = +- FXDIB_ALPHA_MERGE(*dest_scan, blended_colors[1], alpha_ratio); +- dest_scan++; +- *dest_scan = +- FXDIB_ALPHA_MERGE(*dest_scan, blended_colors[2], alpha_ratio); +- } else if (blend_type != BlendMode::kNormal) { +- int blended = Blend(blend_type, *dest_scan, src_b); +- blended = FXDIB_ALPHA_MERGE(src_b, blended, back_alpha); +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, blended, alpha_ratio); +- dest_scan++; +- blended = Blend(blend_type, *dest_scan, src_g); +- blended = FXDIB_ALPHA_MERGE(src_g, blended, back_alpha); +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, blended, alpha_ratio); +- dest_scan++; +- blended = Blend(blend_type, *dest_scan, src_r); +- blended = FXDIB_ALPHA_MERGE(src_r, blended, back_alpha); +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, blended, alpha_ratio); +- } else { +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, alpha_ratio); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, alpha_ratio); +- dest_scan++; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, alpha_ratio); ++ int src_alpha = clip_scan[col]; ++ if (src_alpha == 0) { ++ dest_scan += 4; ++ continue; + } +- dest_scan += 2; ++ int back_alpha = dest_scan[3]; ++ uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255; ++ dest_scan[3] = dest_alpha; ++ int alpha_ratio = src_alpha * 255 / dest_alpha; ++ *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, alpha_ratio); ++ dest_scan++; ++ *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, alpha_ratio); ++ dest_scan++; ++ *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, alpha_ratio); ++ dest_scan++; ++ dest_scan++; + } + } + +-void CompositeRow_ByteMask2Rgba(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_ByteMask2Argb(pdfium::span dest_span, ++ pdfium::span src_span, + int mask_alpha, + int src_r, + int src_g, + int src_b, + int pixel_count, + BlendMode blend_type, +- const uint8_t* clip_scan, +- uint8_t* dest_alpha_scan) { ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + for (int col = 0; col < pixel_count; col++) { + int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col); +- uint8_t back_alpha = *dest_alpha_scan; ++ uint8_t back_alpha = dest_scan[3]; + if (back_alpha == 0) { +- *dest_scan++ = src_b; +- *dest_scan++ = src_g; +- *dest_scan++ = src_r; +- *dest_alpha_scan++ = src_alpha; ++ FXARGB_SETDIB(dest_scan, ArgbEncode(src_alpha, src_r, src_g, src_b)); ++ dest_scan += 4; + continue; + } + if (src_alpha == 0) { +- dest_scan += 3; +- dest_alpha_scan++; ++ dest_scan += 4; + continue; + } + uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255; +- *dest_alpha_scan++ = dest_alpha; ++ dest_scan[3] = dest_alpha; + int alpha_ratio = src_alpha * 255 / dest_alpha; + if (IsNonSeparableBlendMode(blend_type)) { + int blended_colors[3]; +@@ -1617,7 +997,6 @@ void CompositeRow_ByteMask2Rgba(uint8_t* dest_scan, + dest_scan++; + *dest_scan = + FXDIB_ALPHA_MERGE(*dest_scan, blended_colors[2], alpha_ratio); +- dest_scan++; + } else if (blend_type != BlendMode::kNormal) { + int blended = Blend(blend_type, *dest_scan, src_b); + blended = FXDIB_ALPHA_MERGE(src_b, blended, back_alpha); +@@ -1630,20 +1009,19 @@ void CompositeRow_ByteMask2Rgba(uint8_t* dest_scan, + blended = Blend(blend_type, *dest_scan, src_r); + blended = FXDIB_ALPHA_MERGE(src_r, blended, back_alpha); + *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, blended, alpha_ratio); +- dest_scan++; + } else { + *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, alpha_ratio); + dest_scan++; + *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, alpha_ratio); + dest_scan++; + *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, alpha_ratio); +- dest_scan++; + } ++ dest_scan += 2; + } + } + +-void CompositeRow_ByteMask2Rgb(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_ByteMask2Rgb(pdfium::span dest_span, ++ pdfium::span src_span, + int mask_alpha, + int src_r, + int src_g, +@@ -1651,7 +1029,10 @@ void CompositeRow_ByteMask2Rgb(uint8_t* dest_scan, + int pixel_count, + BlendMode blend_type, + int Bpp, +- const uint8_t* clip_scan) { ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + for (int col = 0; col < pixel_count; col++) { + int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col); + if (src_alpha == 0) { +@@ -1689,11 +1070,14 @@ void CompositeRow_ByteMask2Rgb(uint8_t* dest_scan, + } + } + +-void CompositeRow_ByteMask2Mask(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_ByteMask2Mask(pdfium::span dest_span, ++ pdfium::span src_span, + int mask_alpha, + int pixel_count, +- const uint8_t* clip_scan) { ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + for (int col = 0; col < pixel_count; col++) { + int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col); + uint8_t back_alpha = *dest_scan; +@@ -1706,12 +1090,15 @@ void CompositeRow_ByteMask2Mask(uint8_t* dest_scan, + } + } + +-void CompositeRow_ByteMask2Gray(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_ByteMask2Gray(pdfium::span dest_span, ++ pdfium::span src_span, + int mask_alpha, + int src_gray, + int pixel_count, +- const uint8_t* clip_scan) { ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + for (int col = 0; col < pixel_count; col++) { + int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col); + if (src_alpha) { +@@ -1721,36 +1108,8 @@ void CompositeRow_ByteMask2Gray(uint8_t* dest_scan, + } + } + +-void CompositeRow_ByteMask2Graya(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int mask_alpha, +- int src_gray, +- int pixel_count, +- const uint8_t* clip_scan, +- uint8_t* dest_alpha_scan) { +- for (int col = 0; col < pixel_count; col++) { +- int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col); +- uint8_t back_alpha = *dest_alpha_scan; +- if (back_alpha == 0) { +- *dest_scan++ = src_gray; +- *dest_alpha_scan++ = src_alpha; +- continue; +- } +- if (src_alpha == 0) { +- dest_scan++; +- dest_alpha_scan++; +- continue; +- } +- uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255; +- *dest_alpha_scan++ = dest_alpha; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_gray, alpha_ratio); +- dest_scan++; +- } +-} +- +-void CompositeRow_BitMask2Argb(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_BitMask2Argb(pdfium::span dest_span, ++ pdfium::span src_span, + int mask_alpha, + int src_r, + int src_g, +@@ -1758,7 +1117,10 @@ void CompositeRow_BitMask2Argb(uint8_t* dest_scan, + int src_left, + int pixel_count, + BlendMode blend_type, +- const uint8_t* clip_scan) { ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + if (blend_type == BlendMode::kNormal && !clip_scan && mask_alpha == 255) { + FX_ARGB argb = ArgbEncode(0xff, src_r, src_g, src_b); + for (int col = 0; col < pixel_count; col++) { +@@ -1821,8 +1183,8 @@ void CompositeRow_BitMask2Argb(uint8_t* dest_scan, + } + } + +-void CompositeRow_BitMask2Rgb(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_BitMask2Rgb(pdfium::span dest_span, ++ pdfium::span src_span, + int mask_alpha, + int src_r, + int src_g, +@@ -1831,7 +1193,10 @@ void CompositeRow_BitMask2Rgb(uint8_t* dest_scan, + int pixel_count, + BlendMode blend_type, + int Bpp, +- const uint8_t* clip_scan) { ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + if (blend_type == BlendMode::kNormal && !clip_scan && mask_alpha == 255) { + for (int col = 0; col < pixel_count; col++) { + if (src_scan[(src_left + col) / 8] & (1 << (7 - (src_left + col) % 8))) { +@@ -1884,12 +1249,15 @@ void CompositeRow_BitMask2Rgb(uint8_t* dest_scan, + } + } + +-void CompositeRow_BitMask2Mask(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_BitMask2Mask(pdfium::span dest_span, ++ pdfium::span src_span, + int mask_alpha, + int src_left, + int pixel_count, +- const uint8_t* clip_scan) { ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + for (int col = 0; col < pixel_count; col++) { + if (!(src_scan[(src_left + col) / 8] & (1 << (7 - (src_left + col) % 8)))) { + dest_scan++; +@@ -1906,13 +1274,16 @@ void CompositeRow_BitMask2Mask(uint8_t* dest_scan, + } + } + +-void CompositeRow_BitMask2Gray(uint8_t* dest_scan, +- const uint8_t* src_scan, ++void CompositeRow_BitMask2Gray(pdfium::span dest_span, ++ pdfium::span src_span, + int mask_alpha, + int src_gray, + int src_left, + int pixel_count, +- const uint8_t* clip_scan) { ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + for (int col = 0; col < pixel_count; col++) { + if (!(src_scan[(src_left + col) / 8] & (1 << (7 - (src_left + col) % 8)))) { + dest_scan++; +@@ -1926,56 +1297,24 @@ void CompositeRow_BitMask2Gray(uint8_t* dest_scan, + } + } + +-void CompositeRow_BitMask2Graya(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int mask_alpha, +- int src_gray, +- int src_left, +- int pixel_count, +- const uint8_t* clip_scan, +- uint8_t* dest_alpha_scan) { +- for (int col = 0; col < pixel_count; col++) { +- if (!(src_scan[(src_left + col) / 8] & (1 << (7 - (src_left + col) % 8)))) { +- dest_scan++; +- dest_alpha_scan++; +- continue; +- } +- int src_alpha = GetAlpha(mask_alpha, clip_scan, col); +- uint8_t back_alpha = *dest_alpha_scan; +- if (back_alpha == 0) { +- *dest_scan++ = src_gray; +- *dest_alpha_scan++ = src_alpha; +- continue; +- } +- if (src_alpha == 0) { +- dest_scan++; +- dest_alpha_scan++; +- continue; +- } +- uint8_t dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255; +- *dest_alpha_scan++ = dest_alpha; +- int alpha_ratio = src_alpha * 255 / dest_alpha; +- *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_gray, alpha_ratio); +- dest_scan++; +- } +-} +- +-void CompositeRow_Argb2Argb_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int pixel_count, +- BlendMode blend_type, +- const uint8_t* clip_scan) { +- int blended_colors[3]; ++void CompositeRow_Argb2Argb_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int pixel_count, ++ BlendMode blend_type, ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); ++ int blended_colors[3]; + for (int col = 0; col < pixel_count; col++) { + uint8_t back_alpha = dest_scan[3]; + if (back_alpha == 0) { + if (clip_scan) { + int src_alpha = clip_scan[col] * src_scan[3] / 255; ++ ReverseCopy3Bytes(dest_scan, src_scan); + dest_scan[3] = src_alpha; +- dest_scan[0] = src_scan[2]; +- dest_scan[1] = src_scan[1]; +- dest_scan[2] = src_scan[0]; + } else { + FXARGB_RGBORDERCOPY(dest_scan, src_scan); + } +@@ -1994,9 +1333,7 @@ void CompositeRow_Argb2Argb_RgbByteOrder(uint8_t* dest_scan, + int alpha_ratio = src_alpha * 255 / dest_alpha; + if (bNonseparableBlend) { + uint8_t dest_scan_o[3]; +- dest_scan_o[0] = dest_scan[2]; +- dest_scan_o[1] = dest_scan[1]; +- dest_scan_o[2] = dest_scan[0]; ++ ReverseCopy3Bytes(dest_scan_o, dest_scan); + RGB_Blend(blend_type, src_scan, dest_scan_o, blended_colors); + } + for (int color = 0; color < 3; color++) { +@@ -2019,14 +1356,17 @@ void CompositeRow_Argb2Argb_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_Rgb2Argb_Blend_NoClip_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int width, +- BlendMode blend_type, +- int src_Bpp) { +- int blended_colors[3]; ++void CompositeRow_Rgb2Argb_Blend_NoClip_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int width, ++ BlendMode blend_type, ++ int src_Bpp) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); + bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); + int src_gap = src_Bpp - 3; ++ int blended_colors[3]; + for (int col = 0; col < width; col++) { + uint8_t back_alpha = dest_scan[3]; + if (back_alpha == 0) { +@@ -2043,9 +1383,7 @@ void CompositeRow_Rgb2Argb_Blend_NoClip_RgbByteOrder(uint8_t* dest_scan, + dest_scan[3] = 0xff; + if (bNonseparableBlend) { + uint8_t dest_scan_o[3]; +- dest_scan_o[0] = dest_scan[2]; +- dest_scan_o[1] = dest_scan[1]; +- dest_scan_o[2] = dest_scan[0]; ++ ReverseCopy3Bytes(dest_scan_o, dest_scan); + RGB_Blend(blend_type, src_scan, dest_scan_o, blended_colors); + } + for (int color = 0; color < 3; color++) { +@@ -2062,14 +1400,18 @@ void CompositeRow_Rgb2Argb_Blend_NoClip_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_Argb2Rgb_Blend_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int width, +- BlendMode blend_type, +- int dest_Bpp, +- const uint8_t* clip_scan) { ++void CompositeRow_Argb2Rgb_Blend_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int width, ++ BlendMode blend_type, ++ int dest_Bpp, ++ pdfium::span clip_span) { + int blended_colors[3]; + bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + for (int col = 0; col < width; col++) { + uint8_t src_alpha; + if (clip_scan) { +@@ -2084,9 +1426,7 @@ void CompositeRow_Argb2Rgb_Blend_RgbByteOrder(uint8_t* dest_scan, + } + if (bNonseparableBlend) { + uint8_t dest_scan_o[3]; +- dest_scan_o[0] = dest_scan[2]; +- dest_scan_o[1] = dest_scan[1]; +- dest_scan_o[2] = dest_scan[0]; ++ ReverseCopy3Bytes(dest_scan_o, dest_scan); + RGB_Blend(blend_type, src_scan, dest_scan_o, blended_colors); + } + for (int color = 0; color < 3; color++) { +@@ -2103,10 +1443,13 @@ void CompositeRow_Argb2Rgb_Blend_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_Rgb2Argb_NoBlend_NoClip_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int width, +- int src_Bpp) { ++void CompositeRow_Rgb2Argb_NoBlend_NoClip_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int width, ++ int src_Bpp) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); + for (int col = 0; col < width; col++) { + if (src_Bpp == 4) { + FXARGB_SETRGBORDERDIB(dest_scan, 0xff000000 | FXARGB_GETDIB(src_scan)); +@@ -2119,21 +1462,22 @@ void CompositeRow_Rgb2Argb_NoBlend_NoClip_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_Rgb2Rgb_Blend_NoClip_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int width, +- BlendMode blend_type, +- int dest_Bpp, +- int src_Bpp) { ++void CompositeRow_Rgb2Rgb_Blend_NoClip_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int width, ++ BlendMode blend_type, ++ int dest_Bpp, ++ int src_Bpp) { + int blended_colors[3]; + bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); + int src_gap = src_Bpp - 3; + for (int col = 0; col < width; col++) { + if (bNonseparableBlend) { + uint8_t dest_scan_o[3]; +- dest_scan_o[0] = dest_scan[2]; +- dest_scan_o[1] = dest_scan[1]; +- dest_scan_o[2] = dest_scan[0]; ++ ReverseCopy3Bytes(dest_scan_o, dest_scan); + RGB_Blend(blend_type, src_scan, dest_scan_o, blended_colors); + } + for (int color = 0; color < 3; color++) { +@@ -2151,11 +1495,15 @@ void CompositeRow_Rgb2Rgb_Blend_NoClip_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_Argb2Rgb_NoBlend_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int width, +- int dest_Bpp, +- const uint8_t* clip_scan) { ++void CompositeRow_Argb2Rgb_NoBlend_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int width, ++ int dest_Bpp, ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + for (int col = 0; col < width; col++) { + uint8_t src_alpha; + if (clip_scan) { +@@ -2164,11 +1512,9 @@ void CompositeRow_Argb2Rgb_NoBlend_RgbByteOrder(uint8_t* dest_scan, + src_alpha = src_scan[3]; + } + if (src_alpha == 255) { +- dest_scan[2] = *src_scan++; +- dest_scan[1] = *src_scan++; +- dest_scan[0] = *src_scan++; ++ ReverseCopy3Bytes(dest_scan, src_scan); + dest_scan += dest_Bpp; +- src_scan++; ++ src_scan += 4; + continue; + } + if (src_alpha == 0) { +@@ -2187,26 +1533,31 @@ void CompositeRow_Argb2Rgb_NoBlend_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_Rgb2Rgb_NoBlend_NoClip_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int width, +- int dest_Bpp, +- int src_Bpp) { ++void CompositeRow_Rgb2Rgb_NoBlend_NoClip_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int width, ++ int dest_Bpp, ++ int src_Bpp) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); + for (int col = 0; col < width; col++) { +- dest_scan[2] = src_scan[0]; +- dest_scan[1] = src_scan[1]; +- dest_scan[0] = src_scan[2]; ++ ReverseCopy3Bytes(dest_scan, src_scan); + dest_scan += dest_Bpp; + src_scan += src_Bpp; + } + } + +-void CompositeRow_Rgb2Argb_Blend_Clip_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int width, +- BlendMode blend_type, +- int src_Bpp, +- const uint8_t* clip_scan) { ++void CompositeRow_Rgb2Argb_Blend_Clip_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int width, ++ BlendMode blend_type, ++ int src_Bpp, ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + int blended_colors[3]; + bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); + int src_gap = src_Bpp - 3; +@@ -2214,10 +1565,8 @@ void CompositeRow_Rgb2Argb_Blend_Clip_RgbByteOrder(uint8_t* dest_scan, + int src_alpha = *clip_scan++; + uint8_t back_alpha = dest_scan[3]; + if (back_alpha == 0) { +- dest_scan[2] = *src_scan++; +- dest_scan[1] = *src_scan++; +- dest_scan[0] = *src_scan++; +- src_scan += src_gap; ++ ReverseCopy3Bytes(dest_scan, src_scan); ++ src_scan += src_Bpp; + dest_scan += 4; + continue; + } +@@ -2231,9 +1580,7 @@ void CompositeRow_Rgb2Argb_Blend_Clip_RgbByteOrder(uint8_t* dest_scan, + int alpha_ratio = src_alpha * 255 / dest_alpha; + if (bNonseparableBlend) { + uint8_t dest_scan_o[3]; +- dest_scan_o[0] = dest_scan[2]; +- dest_scan_o[1] = dest_scan[1]; +- dest_scan_o[2] = dest_scan[0]; ++ ReverseCopy3Bytes(dest_scan_o, dest_scan); + RGB_Blend(blend_type, src_scan, dest_scan_o, blended_colors); + } + for (int color = 0; color < 3; color++) { +@@ -2252,13 +1599,17 @@ void CompositeRow_Rgb2Argb_Blend_Clip_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_Rgb2Rgb_Blend_Clip_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int width, +- BlendMode blend_type, +- int dest_Bpp, +- int src_Bpp, +- const uint8_t* clip_scan) { ++void CompositeRow_Rgb2Rgb_Blend_Clip_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int width, ++ BlendMode blend_type, ++ int dest_Bpp, ++ int src_Bpp, ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + int blended_colors[3]; + bool bNonseparableBlend = IsNonSeparableBlendMode(blend_type); + int src_gap = src_Bpp - 3; +@@ -2271,9 +1622,7 @@ void CompositeRow_Rgb2Rgb_Blend_Clip_RgbByteOrder(uint8_t* dest_scan, + } + if (bNonseparableBlend) { + uint8_t dest_scan_o[3]; +- dest_scan_o[0] = dest_scan[2]; +- dest_scan_o[1] = dest_scan[1]; +- dest_scan_o[2] = dest_scan[0]; ++ ReverseCopy3Bytes(dest_scan_o, dest_scan); + RGB_Blend(blend_type, src_scan, dest_scan_o, blended_colors); + } + for (int color = 0; color < 3; color++) { +@@ -2291,21 +1640,23 @@ void CompositeRow_Rgb2Rgb_Blend_Clip_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_Rgb2Argb_NoBlend_Clip_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int width, +- int src_Bpp, +- const uint8_t* clip_scan) { ++void CompositeRow_Rgb2Argb_NoBlend_Clip_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int width, ++ int src_Bpp, ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + int src_gap = src_Bpp - 3; + for (int col = 0; col < width; col++) { + int src_alpha = clip_scan[col]; + if (src_alpha == 255) { +- dest_scan[2] = *src_scan++; +- dest_scan[1] = *src_scan++; +- dest_scan[0] = *src_scan++; ++ ReverseCopy3Bytes(dest_scan, src_scan); + dest_scan[3] = 255; + dest_scan += 4; +- src_scan += src_gap; ++ src_scan += src_Bpp; + continue; + } + if (src_alpha == 0) { +@@ -2328,18 +1679,20 @@ void CompositeRow_Rgb2Argb_NoBlend_Clip_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_Rgb2Rgb_NoBlend_Clip_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int width, +- int dest_Bpp, +- int src_Bpp, +- const uint8_t* clip_scan) { ++void CompositeRow_Rgb2Rgb_NoBlend_Clip_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int width, ++ int dest_Bpp, ++ int src_Bpp, ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + for (int col = 0; col < width; col++) { + int src_alpha = clip_scan[col]; + if (src_alpha == 255) { +- dest_scan[2] = src_scan[0]; +- dest_scan[1] = src_scan[1]; +- dest_scan[0] = src_scan[2]; ++ ReverseCopy3Bytes(dest_scan, src_scan); + } else if (src_alpha) { + dest_scan[2] = FXDIB_ALPHA_MERGE(dest_scan[2], *src_scan, src_alpha); + src_scan++; +@@ -2355,14 +1708,19 @@ void CompositeRow_Rgb2Rgb_NoBlend_Clip_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_8bppRgb2Rgb_NoBlend_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- FX_ARGB* pPalette, +- int pixel_count, +- int DestBpp, +- const uint8_t* clip_scan) { ++void CompositeRow_8bppRgb2Rgb_NoBlend_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ const FX_ARGB* pPalette, ++ int pixel_count, ++ int DestBpp, ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + for (int col = 0; col < pixel_count; col++) { +- FX_ARGB argb = pPalette ? pPalette[*src_scan] : (*src_scan) * 0x010101; ++ FX_ARGB argb = pPalette ? pPalette[*src_scan] ++ : ArgbEncode(0, *src_scan, *src_scan, *src_scan); + int src_r = FXARGB_R(argb); + int src_g = FXARGB_G(argb); + int src_b = FXARGB_B(argb); +@@ -2380,28 +1738,38 @@ void CompositeRow_8bppRgb2Rgb_NoBlend_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_1bppRgb2Rgb_NoBlend_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int src_left, +- FX_ARGB* pPalette, +- int pixel_count, +- int DestBpp, +- const uint8_t* clip_scan) { +- int reset_r, reset_g, reset_b; +- int set_r, set_g, set_b; +- if (pPalette) { +- reset_r = FXARGB_R(pPalette[0]); +- reset_g = FXARGB_G(pPalette[0]); +- reset_b = FXARGB_B(pPalette[0]); +- set_r = FXARGB_R(pPalette[1]); +- set_g = FXARGB_G(pPalette[1]); +- set_b = FXARGB_B(pPalette[1]); ++void CompositeRow_1bppRgb2Rgb_NoBlend_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int src_left, ++ pdfium::span src_palette, ++ int pixel_count, ++ int DestBpp, ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); ++ int reset_r; ++ int reset_g; ++ int reset_b; ++ int set_r; ++ int set_g; ++ int set_b; ++ if (!src_palette.empty()) { ++ reset_r = FXARGB_R(src_palette[0]); ++ reset_g = FXARGB_G(src_palette[0]); ++ reset_b = FXARGB_B(src_palette[0]); ++ set_r = FXARGB_R(src_palette[1]); ++ set_g = FXARGB_G(src_palette[1]); ++ set_b = FXARGB_B(src_palette[1]); + } else { + reset_r = reset_g = reset_b = 0; + set_r = set_g = set_b = 255; + } + for (int col = 0; col < pixel_count; col++) { +- int src_r, src_g, src_b; ++ int src_r; ++ int src_g; ++ int src_b; + if (src_scan[(col + src_left) / 8] & (1 << (7 - (col + src_left) % 8))) { + src_r = set_r; + src_g = set_g; +@@ -2424,13 +1792,19 @@ void CompositeRow_1bppRgb2Rgb_NoBlend_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_8bppRgb2Argb_NoBlend_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int width, +- FX_ARGB* pPalette, +- const uint8_t* clip_scan) { ++void CompositeRow_8bppRgb2Argb_NoBlend_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int width, ++ const FX_ARGB* pPalette, ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + for (int col = 0; col < width; col++) { +- int src_r, src_g, src_b; ++ int src_r; ++ int src_g; ++ int src_b; + if (pPalette) { + FX_ARGB argb = pPalette[*src_scan]; + src_r = FXARGB_R(argb); +@@ -2466,27 +1840,37 @@ void CompositeRow_8bppRgb2Argb_NoBlend_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_1bppRgb2Argb_NoBlend_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int src_left, +- int width, +- FX_ARGB* pPalette, +- const uint8_t* clip_scan) { +- int reset_r, reset_g, reset_b; +- int set_r, set_g, set_b; +- if (pPalette) { +- reset_r = FXARGB_R(pPalette[0]); +- reset_g = FXARGB_G(pPalette[0]); +- reset_b = FXARGB_B(pPalette[0]); +- set_r = FXARGB_R(pPalette[1]); +- set_g = FXARGB_G(pPalette[1]); +- set_b = FXARGB_B(pPalette[1]); ++void CompositeRow_1bppRgb2Argb_NoBlend_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int src_left, ++ int width, ++ pdfium::span src_palette, ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); ++ int reset_r; ++ int reset_g; ++ int reset_b; ++ int set_r; ++ int set_g; ++ int set_b; ++ if (!src_palette.empty()) { ++ reset_r = FXARGB_R(src_palette[0]); ++ reset_g = FXARGB_G(src_palette[0]); ++ reset_b = FXARGB_B(src_palette[0]); ++ set_r = FXARGB_R(src_palette[1]); ++ set_g = FXARGB_G(src_palette[1]); ++ set_b = FXARGB_B(src_palette[1]); + } else { + reset_r = reset_g = reset_b = 0; + set_r = set_g = set_b = 255; + } + for (int col = 0; col < width; col++) { +- int src_r, src_g, src_b; ++ int src_r; ++ int src_g; ++ int src_b; + if (src_scan[(col + src_left) / 8] & (1 << (7 - (col + src_left) % 8))) { + src_r = set_r; + src_g = set_g; +@@ -2520,15 +1904,19 @@ void CompositeRow_1bppRgb2Argb_NoBlend_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_ByteMask2Argb_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int mask_alpha, +- int src_r, +- int src_g, +- int src_b, +- int pixel_count, +- BlendMode blend_type, +- const uint8_t* clip_scan) { ++void CompositeRow_ByteMask2Argb_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int mask_alpha, ++ int src_r, ++ int src_g, ++ int src_b, ++ int pixel_count, ++ BlendMode blend_type, ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + for (int col = 0; col < pixel_count; col++) { + int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col); + uint8_t back_alpha = dest_scan[3]; +@@ -2550,7 +1938,8 @@ void CompositeRow_ByteMask2Argb_RgbByteOrder(uint8_t* dest_scan, + uint8_t scan[3] = {static_cast(src_b), + static_cast(src_g), + static_cast(src_r)}; +- uint8_t dest_scan_o[3] = {dest_scan[2], dest_scan[1], dest_scan[0]}; ++ uint8_t dest_scan_o[3]; ++ ReverseCopy3Bytes(dest_scan_o, dest_scan); + RGB_Blend(blend_type, scan, dest_scan_o, blended_colors); + dest_scan[2] = + FXDIB_ALPHA_MERGE(dest_scan[2], blended_colors[0], alpha_ratio); +@@ -2577,16 +1966,20 @@ void CompositeRow_ByteMask2Argb_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_ByteMask2Rgb_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int mask_alpha, +- int src_r, +- int src_g, +- int src_b, +- int pixel_count, +- BlendMode blend_type, +- int Bpp, +- const uint8_t* clip_scan) { ++void CompositeRow_ByteMask2Rgb_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int mask_alpha, ++ int src_r, ++ int src_g, ++ int src_b, ++ int pixel_count, ++ BlendMode blend_type, ++ int Bpp, ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + for (int col = 0; col < pixel_count; col++) { + int src_alpha = GetAlphaWithSrc(mask_alpha, clip_scan, src_scan, col); + if (src_alpha == 0) { +@@ -2598,7 +1991,8 @@ void CompositeRow_ByteMask2Rgb_RgbByteOrder(uint8_t* dest_scan, + uint8_t scan[3] = {static_cast(src_b), + static_cast(src_g), + static_cast(src_r)}; +- uint8_t dest_scan_o[3] = {dest_scan[2], dest_scan[1], dest_scan[0]}; ++ uint8_t dest_scan_o[3]; ++ ReverseCopy3Bytes(dest_scan_o, dest_scan); + RGB_Blend(blend_type, scan, dest_scan_o, blended_colors); + dest_scan[2] = + FXDIB_ALPHA_MERGE(dest_scan[2], blended_colors[0], src_alpha); +@@ -2622,16 +2016,20 @@ void CompositeRow_ByteMask2Rgb_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_BitMask2Argb_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int mask_alpha, +- int src_r, +- int src_g, +- int src_b, +- int src_left, +- int pixel_count, +- BlendMode blend_type, +- const uint8_t* clip_scan) { ++void CompositeRow_BitMask2Argb_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int mask_alpha, ++ int src_r, ++ int src_g, ++ int src_b, ++ int src_left, ++ int pixel_count, ++ BlendMode blend_type, ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + if (blend_type == BlendMode::kNormal && !clip_scan && mask_alpha == 255) { + FX_ARGB argb = ArgbEncode(0xff, src_r, src_g, src_b); + for (int col = 0; col < pixel_count; col++) { +@@ -2663,7 +2061,8 @@ void CompositeRow_BitMask2Argb_RgbByteOrder(uint8_t* dest_scan, + uint8_t scan[3] = {static_cast(src_b), + static_cast(src_g), + static_cast(src_r)}; +- uint8_t dest_scan_o[3] = {dest_scan[2], dest_scan[1], dest_scan[0]}; ++ uint8_t dest_scan_o[3]; ++ ReverseCopy3Bytes(dest_scan_o, dest_scan); + RGB_Blend(blend_type, scan, dest_scan_o, blended_colors); + dest_scan[2] = + FXDIB_ALPHA_MERGE(dest_scan[2], blended_colors[0], alpha_ratio); +@@ -2690,17 +2089,21 @@ void CompositeRow_BitMask2Argb_RgbByteOrder(uint8_t* dest_scan, + } + } + +-void CompositeRow_BitMask2Rgb_RgbByteOrder(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int mask_alpha, +- int src_r, +- int src_g, +- int src_b, +- int src_left, +- int pixel_count, +- BlendMode blend_type, +- int Bpp, +- const uint8_t* clip_scan) { ++void CompositeRow_BitMask2Rgb_RgbByteOrder( ++ pdfium::span dest_span, ++ pdfium::span src_span, ++ int mask_alpha, ++ int src_r, ++ int src_g, ++ int src_b, ++ int src_left, ++ int pixel_count, ++ BlendMode blend_type, ++ int Bpp, ++ pdfium::span clip_span) { ++ uint8_t* dest_scan = dest_span.data(); ++ const uint8_t* src_scan = src_span.data(); ++ const uint8_t* clip_scan = clip_span.data(); + if (blend_type == BlendMode::kNormal && !clip_scan && mask_alpha == 255) { + for (int col = 0; col < pixel_count; col++) { + if (src_scan[(src_left + col) / 8] & (1 << (7 - (src_left + col) % 8))) { +@@ -2727,7 +2130,8 @@ void CompositeRow_BitMask2Rgb_RgbByteOrder(uint8_t* dest_scan, + uint8_t scan[3] = {static_cast(src_b), + static_cast(src_g), + static_cast(src_r)}; +- uint8_t dest_scan_o[3] = {dest_scan[2], dest_scan[1], dest_scan[0]}; ++ uint8_t dest_scan_o[3]; ++ ReverseCopy3Bytes(dest_scan_o, dest_scan); + RGB_Blend(blend_type, scan, dest_scan_o, blended_colors); + dest_scan[2] = + FXDIB_ALPHA_MERGE(dest_scan[2], blended_colors[0], src_alpha); +@@ -2762,8 +2166,7 @@ CFX_ScanlineCompositor::~CFX_ScanlineCompositor() = default; + + bool CFX_ScanlineCompositor::Init(FXDIB_Format dest_format, + FXDIB_Format src_format, +- int32_t width, +- uint32_t* pSrcPalette, ++ pdfium::span src_palette, + uint32_t mask_color, + BlendMode blend_type, + bool bClip, +@@ -2772,31 +2175,27 @@ bool CFX_ScanlineCompositor::Init(FXDIB_Format dest_format, + m_DestFormat = dest_format; + m_BlendType = blend_type; + m_bRgbByteOrder = bRgbByteOrder; +- if (GetBppFromFormat(dest_format) == 1) ++ m_bClip = bClip; ++ if (m_DestFormat == FXDIB_Format::k1bppMask || ++ m_DestFormat == FXDIB_Format::k1bppRgb) { + return false; +- if (m_SrcFormat == FXDIB_1bppMask || m_SrcFormat == FXDIB_8bppMask) { +- InitSourceMask(mask_color); +- return true; + } +- if (!GetIsCmykFromFormat(src_format) && GetIsCmykFromFormat(dest_format)) ++ ++ if (m_bRgbByteOrder && (m_DestFormat == FXDIB_Format::k8bppMask || ++ m_DestFormat == FXDIB_Format::k8bppRgb)) { + return false; +- if (GetBppFromFormat(m_SrcFormat) <= 8) { +- if (dest_format == FXDIB_8bppMask) +- return true; ++ } + +- InitSourcePalette(src_format, dest_format, pSrcPalette); +- m_iTransparency = (dest_format == FXDIB_Argb ? 1 : 0) + +- (GetIsAlphaFromFormat(dest_format) ? 2 : 0) + +- (GetIsCmykFromFormat(dest_format) ? 4 : 0) + +- (GetBppFromFormat(src_format) == 1 ? 8 : 0); ++ if (m_SrcFormat == FXDIB_Format::k1bppMask || ++ m_SrcFormat == FXDIB_Format::k8bppMask) { ++ InitSourceMask(mask_color); + return true; + } +- m_iTransparency = (GetIsAlphaFromFormat(src_format) ? 0 : 1) + +- (GetIsAlphaFromFormat(dest_format) ? 0 : 2) + +- (blend_type == BlendMode::kNormal ? 4 : 0) + +- (bClip ? 8 : 0) + +- (GetIsCmykFromFormat(src_format) ? 16 : 0) + +- (GetIsCmykFromFormat(dest_format) ? 32 : 0); ++ if ((m_SrcFormat == FXDIB_Format::k1bppRgb || ++ m_SrcFormat == FXDIB_Format::k8bppRgb) && ++ m_DestFormat != FXDIB_Format::k8bppMask) { ++ InitSourcePalette(src_palette); ++ } + return true; + } + +@@ -2805,382 +2204,314 @@ void CFX_ScanlineCompositor::InitSourceMask(uint32_t mask_color) { + m_MaskRed = FXARGB_R(mask_color); + m_MaskGreen = FXARGB_G(mask_color); + m_MaskBlue = FXARGB_B(mask_color); +- if (m_DestFormat == FXDIB_8bppMask) ++ if (m_DestFormat == FXDIB_Format::k8bppMask) + return; + +- if (GetBppFromFormat(m_DestFormat) == 8) { ++ if (m_DestFormat == FXDIB_Format::k8bppRgb) + m_MaskRed = FXRGB2GRAY(m_MaskRed, m_MaskGreen, m_MaskBlue); +- if (GetIsCmykFromFormat(m_DestFormat)) +- m_MaskRed = FX_CCOLOR(m_MaskRed); +- } + } + +-void CFX_ScanlineCompositor::InitSourcePalette(FXDIB_Format src_format, +- FXDIB_Format dest_format, +- const uint32_t* pSrcPalette) { +- bool bIsSrcCmyk = GetIsCmykFromFormat(src_format); +- bool bIsDstCmyk = GetIsCmykFromFormat(dest_format); +- bool bIsDestBpp8 = GetBppFromFormat(dest_format) == 8; +- int pal_count = 1 << GetBppFromFormat(src_format); +- m_pSrcPalette = nullptr; +- if (pSrcPalette) { ++void CFX_ScanlineCompositor::InitSourcePalette( ++ pdfium::span src_palette) { ++ DCHECK_NE(m_DestFormat, FXDIB_Format::k8bppMask); ++ ++ m_SrcPalette.Reset(); ++ const bool bIsDestBpp8 = m_DestFormat == FXDIB_Format::k8bppRgb; ++ const size_t pal_count = static_cast(1) ++ << GetBppFromFormat(m_SrcFormat); ++ ++ if (!src_palette.empty()) { + if (bIsDestBpp8) { +- uint8_t* gray_pal = FX_Alloc(uint8_t, pal_count); +- m_pSrcPalette.reset(reinterpret_cast(gray_pal)); +- if (bIsSrcCmyk) { +- for (int i = 0; i < pal_count; ++i) { +- FX_CMYK cmyk = pSrcPalette[i]; +- uint8_t r; +- uint8_t g; +- uint8_t b; +- std::tie(r, g, b) = +- AdobeCMYK_to_sRGB1(FXSYS_GetCValue(cmyk), FXSYS_GetMValue(cmyk), +- FXSYS_GetYValue(cmyk), FXSYS_GetKValue(cmyk)); +- *gray_pal++ = FXRGB2GRAY(r, g, b); +- } +- } else { +- for (int i = 0; i < pal_count; ++i) { +- FX_ARGB argb = pSrcPalette[i]; +- *gray_pal++ = +- FXRGB2GRAY(FXARGB_R(argb), FXARGB_G(argb), FXARGB_B(argb)); +- } ++ pdfium::span gray_pal = m_SrcPalette.Make8BitPalette(pal_count); ++ for (size_t i = 0; i < pal_count; ++i) { ++ FX_ARGB argb = src_palette[i]; ++ gray_pal[i] = ++ FXRGB2GRAY(FXARGB_R(argb), FXARGB_G(argb), FXARGB_B(argb)); + } + return; + } +- m_pSrcPalette.reset(FX_Alloc(uint32_t, pal_count)); +- uint32_t* pPalette = m_pSrcPalette.get(); +- if (bIsDstCmyk == bIsSrcCmyk) { +- memcpy(pPalette, pSrcPalette, pal_count * sizeof(uint32_t)); +- } else { +- for (int i = 0; i < pal_count; ++i) { +- FX_CMYK cmyk = pSrcPalette[i]; +- uint8_t r; +- uint8_t g; +- uint8_t b; +- std::tie(r, g, b) = +- AdobeCMYK_to_sRGB1(FXSYS_GetCValue(cmyk), FXSYS_GetMValue(cmyk), +- FXSYS_GetYValue(cmyk), FXSYS_GetKValue(cmyk)); +- pPalette[i] = ArgbEncode(0xff, r, g, b); +- } +- } ++ pdfium::span pPalette = m_SrcPalette.Make32BitPalette(pal_count); ++ for (size_t i = 0; i < pal_count; ++i) ++ pPalette[i] = src_palette[i]; + return; + } + if (bIsDestBpp8) { +- uint8_t* gray_pal = FX_Alloc(uint8_t, pal_count); ++ pdfium::span gray_pal = m_SrcPalette.Make8BitPalette(pal_count); + if (pal_count == 2) { + gray_pal[0] = 0; + gray_pal[1] = 255; + } else { +- for (int i = 0; i < pal_count; ++i) ++ for (size_t i = 0; i < pal_count; ++i) + gray_pal[i] = i; + } +- m_pSrcPalette.reset(reinterpret_cast(gray_pal)); + return; + } +- m_pSrcPalette.reset(FX_Alloc(uint32_t, pal_count)); +- uint32_t* pPalette = m_pSrcPalette.get(); ++ pdfium::span pPalette = m_SrcPalette.Make32BitPalette(pal_count); + if (pal_count == 2) { +- pPalette[0] = bIsSrcCmyk ? 255 : 0xff000000; +- pPalette[1] = bIsSrcCmyk ? 0 : 0xffffffff; ++ pPalette[0] = 0xff000000; ++ pPalette[1] = 0xffffffff; + } else { +- for (int i = 0; i < pal_count; ++i) +- pPalette[i] = bIsSrcCmyk ? FX_CCOLOR(i) : (i * 0x10101); +- } +- if (bIsSrcCmyk != bIsDstCmyk) { +- for (int i = 0; i < pal_count; ++i) { +- FX_CMYK cmyk = pPalette[i]; +- uint8_t r; +- uint8_t g; +- uint8_t b; +- std::tie(r, g, b) = +- AdobeCMYK_to_sRGB1(FXSYS_GetCValue(cmyk), FXSYS_GetMValue(cmyk), +- FXSYS_GetYValue(cmyk), FXSYS_GetKValue(cmyk)); +- pPalette[i] = ArgbEncode(0xff, r, g, b); ++ for (size_t i = 0; i < pal_count; ++i) { ++ uint32_t v = static_cast(i); ++ pPalette[i] = ArgbEncode(0, v, v, v); + } + } + } + + void CFX_ScanlineCompositor::CompositeRgbBitmapLine( +- uint8_t* dest_scan, +- const uint8_t* src_scan, ++ pdfium::span dest_scan, ++ pdfium::span src_scan, + int width, +- const uint8_t* clip_scan, +- const uint8_t* src_extra_alpha, +- uint8_t* dst_extra_alpha) { ++ pdfium::span clip_scan) const { ++ DCHECK(m_SrcFormat == FXDIB_Format::kRgb || ++ m_SrcFormat == FXDIB_Format::kRgb32 || ++ m_SrcFormat == FXDIB_Format::kArgb); ++ + int src_Bpp = GetCompsFromFormat(m_SrcFormat); + int dest_Bpp = GetCompsFromFormat(m_DestFormat); + if (m_bRgbByteOrder) { +- switch (m_iTransparency) { +- case 0: +- case 4: +- case 8: +- case 12: ++ if (m_SrcFormat == FXDIB_Format::kArgb) { ++ if (m_DestFormat == FXDIB_Format::kArgb) { + CompositeRow_Argb2Argb_RgbByteOrder(dest_scan, src_scan, width, + m_BlendType, clip_scan); +- break; +- case 1: +- CompositeRow_Rgb2Argb_Blend_NoClip_RgbByteOrder( +- dest_scan, src_scan, width, m_BlendType, src_Bpp); +- break; +- case 2: +- case 10: +- CompositeRow_Argb2Rgb_Blend_RgbByteOrder( +- dest_scan, src_scan, width, m_BlendType, dest_Bpp, clip_scan); +- break; +- case 3: +- CompositeRow_Rgb2Rgb_Blend_NoClip_RgbByteOrder( +- dest_scan, src_scan, width, m_BlendType, dest_Bpp, src_Bpp); +- break; +- case 5: +- CompositeRow_Rgb2Argb_NoBlend_NoClip_RgbByteOrder(dest_scan, src_scan, +- width, src_Bpp); +- break; +- case 6: +- case 14: ++ return; ++ } ++ if (m_BlendType == BlendMode::kNormal) { + CompositeRow_Argb2Rgb_NoBlend_RgbByteOrder(dest_scan, src_scan, width, + dest_Bpp, clip_scan); +- break; +- case 7: +- CompositeRow_Rgb2Rgb_NoBlend_NoClip_RgbByteOrder( +- dest_scan, src_scan, width, dest_Bpp, src_Bpp); +- break; +- case 9: ++ return; ++ } ++ CompositeRow_Argb2Rgb_Blend_RgbByteOrder( ++ dest_scan, src_scan, width, m_BlendType, dest_Bpp, clip_scan); ++ return; ++ } ++ ++ if (m_DestFormat == FXDIB_Format::kArgb) { ++ if (m_BlendType == BlendMode::kNormal) { ++ if (m_bClip) { ++ CompositeRow_Rgb2Argb_NoBlend_Clip_RgbByteOrder( ++ dest_scan, src_scan, width, src_Bpp, clip_scan); ++ return; ++ } ++ CompositeRow_Rgb2Argb_NoBlend_NoClip_RgbByteOrder(dest_scan, src_scan, ++ width, src_Bpp); ++ return; ++ } ++ if (m_bClip) { + CompositeRow_Rgb2Argb_Blend_Clip_RgbByteOrder( + dest_scan, src_scan, width, m_BlendType, src_Bpp, clip_scan); +- break; +- case 11: +- CompositeRow_Rgb2Rgb_Blend_Clip_RgbByteOrder(dest_scan, src_scan, width, +- m_BlendType, dest_Bpp, +- src_Bpp, clip_scan); +- break; +- case 13: +- CompositeRow_Rgb2Argb_NoBlend_Clip_RgbByteOrder( +- dest_scan, src_scan, width, src_Bpp, clip_scan); +- break; +- case 15: ++ return; ++ } ++ CompositeRow_Rgb2Argb_Blend_NoClip_RgbByteOrder( ++ dest_scan, src_scan, width, m_BlendType, src_Bpp); ++ return; ++ } ++ ++ if (m_BlendType == BlendMode::kNormal) { ++ if (m_bClip) { + CompositeRow_Rgb2Rgb_NoBlend_Clip_RgbByteOrder( + dest_scan, src_scan, width, dest_Bpp, src_Bpp, clip_scan); +- break; ++ return; ++ } ++ CompositeRow_Rgb2Rgb_NoBlend_NoClip_RgbByteOrder( ++ dest_scan, src_scan, width, dest_Bpp, src_Bpp); ++ return; ++ } ++ if (m_bClip) { ++ CompositeRow_Rgb2Rgb_Blend_Clip_RgbByteOrder(dest_scan, src_scan, width, ++ m_BlendType, dest_Bpp, ++ src_Bpp, clip_scan); ++ return; + } ++ CompositeRow_Rgb2Rgb_Blend_NoClip_RgbByteOrder( ++ dest_scan, src_scan, width, m_BlendType, dest_Bpp, src_Bpp); + return; + } +- if (m_DestFormat == FXDIB_8bppMask) { +- if (GetIsAlphaFromFormat(m_SrcFormat)) { +- if (m_SrcFormat == FXDIB_Argb) { +- CompositeRow_AlphaToMask(dest_scan, src_scan, width, clip_scan, 4); +- } else { +- CompositeRow_AlphaToMask(dest_scan, src_extra_alpha, width, clip_scan, +- 1); +- } ++ ++ if (m_DestFormat == FXDIB_Format::k8bppMask) { ++ if (m_SrcFormat == FXDIB_Format::kArgb) { ++ CompositeRow_AlphaToMask(dest_scan, src_scan, width, clip_scan, 4); + } else { +- CompositeRow_Rgb2Mask(dest_scan, src_scan, width, clip_scan); +- } +- } else if (GetBppFromFormat(m_DestFormat) == 8) { +- if (GetIsCmykFromFormat(m_DestFormat)) { +- for (int i = 0; i < width; ++i) { +- *dest_scan = ~*dest_scan; +- dest_scan++; +- } ++ CompositeRow_Rgb2Mask(dest_scan, width, clip_scan); + } +- if (GetIsAlphaFromFormat(m_SrcFormat)) { +- if (GetIsAlphaFromFormat(m_DestFormat)) { +- CompositeRow_Argb2Graya(dest_scan, src_scan, width, m_BlendType, +- clip_scan, src_extra_alpha, dst_extra_alpha); +- } else { +- CompositeRow_Argb2Gray(dest_scan, src_scan, width, m_BlendType, +- clip_scan, src_extra_alpha); +- } ++ return; ++ } ++ ++ if (m_DestFormat == FXDIB_Format::k8bppRgb) { ++ if (m_SrcFormat == FXDIB_Format::kArgb) { ++ CompositeRow_Argb2Gray(dest_scan, src_scan, width, m_BlendType, ++ clip_scan); + } else { +- if (GetIsAlphaFromFormat(m_DestFormat)) { +- CompositeRow_Rgb2Graya(dest_scan, src_scan, src_Bpp, width, m_BlendType, +- clip_scan, dst_extra_alpha); +- } else { +- CompositeRow_Rgb2Gray(dest_scan, src_scan, src_Bpp, width, m_BlendType, +- clip_scan); +- } ++ CompositeRow_Rgb2Gray(dest_scan, src_scan, src_Bpp, width, m_BlendType, ++ clip_scan); + } +- if (GetIsCmykFromFormat(m_DestFormat)) { +- for (int i = 0; i < width; ++i) { +- *dest_scan = ~*dest_scan; +- dest_scan++; +- } ++ return; ++ } ++ ++ // TODO(thestig): Tighten this check. ++ DCHECK_NE(GetBppFromFormat(m_DestFormat), 8); ++ ++ if (m_SrcFormat == FXDIB_Format::kArgb) { ++ if (m_DestFormat == FXDIB_Format::kArgb) { ++ CompositeRow_Argb2Argb(dest_scan, src_scan, width, m_BlendType, ++ clip_scan); ++ return; + } +- } else { +- switch (m_iTransparency) { +- case 0: +- case 4: +- case 8: +- case 4 + 8: { +- CompositeRow_Argb2Argb(dest_scan, src_scan, width, m_BlendType, +- clip_scan, dst_extra_alpha, src_extra_alpha); +- } break; +- case 1: +- CompositeRow_Rgb2Argb_Blend_NoClip( +- dest_scan, src_scan, width, m_BlendType, src_Bpp, dst_extra_alpha); +- break; +- case 1 + 8: +- CompositeRow_Rgb2Argb_Blend_Clip(dest_scan, src_scan, width, +- m_BlendType, src_Bpp, clip_scan, +- dst_extra_alpha); +- break; +- case 1 + 4: +- CompositeRow_Rgb2Argb_NoBlend_NoClip(dest_scan, src_scan, width, +- src_Bpp, dst_extra_alpha); +- break; +- case 1 + 4 + 8: ++ if (m_BlendType == BlendMode::kNormal) { ++ CompositeRow_Argb2Rgb_NoBlend(dest_scan, src_scan, width, dest_Bpp, ++ clip_scan); ++ return; ++ } ++ CompositeRow_Argb2Rgb_Blend(dest_scan, src_scan, width, m_BlendType, ++ dest_Bpp, clip_scan); ++ return; ++ } ++ ++ if (m_DestFormat == FXDIB_Format::kArgb) { ++ if (m_BlendType == BlendMode::kNormal) { ++ if (m_bClip) { + CompositeRow_Rgb2Argb_NoBlend_Clip(dest_scan, src_scan, width, src_Bpp, +- clip_scan, dst_extra_alpha); +- break; +- case 2: +- case 2 + 8: +- CompositeRow_Argb2Rgb_Blend(dest_scan, src_scan, width, m_BlendType, +- dest_Bpp, clip_scan, src_extra_alpha); +- break; +- case 2 + 4: +- case 2 + 4 + 8: +- CompositeRow_Argb2Rgb_NoBlend(dest_scan, src_scan, width, dest_Bpp, +- clip_scan, src_extra_alpha); +- break; +- case 1 + 2: +- CompositeRow_Rgb2Rgb_Blend_NoClip(dest_scan, src_scan, width, +- m_BlendType, dest_Bpp, src_Bpp); +- break; +- case 1 + 2 + 8: +- CompositeRow_Rgb2Rgb_Blend_Clip(dest_scan, src_scan, width, m_BlendType, +- dest_Bpp, src_Bpp, clip_scan); +- break; +- case 1 + 2 + 4: +- CompositeRow_Rgb2Rgb_NoBlend_NoClip(dest_scan, src_scan, width, +- dest_Bpp, src_Bpp); +- break; +- case 1 + 2 + 4 + 8: +- CompositeRow_Rgb2Rgb_NoBlend_Clip(dest_scan, src_scan, width, dest_Bpp, +- src_Bpp, clip_scan); +- break; ++ clip_scan); ++ return; ++ } ++ CompositeRow_Rgb2Argb_NoBlend_NoClip(dest_scan, src_scan, width, src_Bpp); ++ return; ++ } ++ if (m_bClip) { ++ CompositeRow_Rgb2Argb_Blend_Clip(dest_scan, src_scan, width, m_BlendType, ++ src_Bpp, clip_scan); ++ return; + } ++ CompositeRow_Rgb2Argb_Blend_NoClip(dest_scan, src_scan, width, m_BlendType, ++ src_Bpp); ++ return; ++ } ++ ++ if (m_BlendType == BlendMode::kNormal) { ++ if (m_bClip) { ++ CompositeRow_Rgb2Rgb_NoBlend_Clip(dest_scan, src_scan, width, dest_Bpp, ++ src_Bpp, clip_scan); ++ return; ++ } ++ CompositeRow_Rgb2Rgb_NoBlend_NoClip(dest_scan, src_scan, width, dest_Bpp, ++ src_Bpp); ++ return; ++ } ++ if (m_bClip) { ++ CompositeRow_Rgb2Rgb_Blend_Clip(dest_scan, src_scan, width, m_BlendType, ++ dest_Bpp, src_Bpp, clip_scan); ++ return; + } ++ CompositeRow_Rgb2Rgb_Blend_NoClip(dest_scan, src_scan, width, m_BlendType, ++ dest_Bpp, src_Bpp); + } + + void CFX_ScanlineCompositor::CompositePalBitmapLine( +- uint8_t* dest_scan, +- const uint8_t* src_scan, ++ pdfium::span dest_scan, ++ pdfium::span src_scan, + int src_left, + int width, +- const uint8_t* clip_scan, +- const uint8_t* src_extra_alpha, +- uint8_t* dst_extra_alpha) { ++ pdfium::span clip_scan) const { ++ DCHECK(m_SrcFormat == FXDIB_Format::k1bppRgb || ++ m_SrcFormat == FXDIB_Format::k8bppRgb); ++ + if (m_bRgbByteOrder) { +- if (m_SrcFormat == FXDIB_1bppRgb) { +- if (m_DestFormat == FXDIB_8bppRgb) { ++ if (m_SrcFormat == FXDIB_Format::k1bppRgb) { ++ if (m_DestFormat == FXDIB_Format::k8bppRgb) { + return; + } +- if (m_DestFormat == FXDIB_Argb) { ++ if (m_DestFormat == FXDIB_Format::kArgb) { + CompositeRow_1bppRgb2Argb_NoBlend_RgbByteOrder( +- dest_scan, src_scan, src_left, width, m_pSrcPalette.get(), +- clip_scan); ++ dest_scan, src_scan, src_left, width, ++ m_SrcPalette.Get32BitPalette(), clip_scan); + } else { + CompositeRow_1bppRgb2Rgb_NoBlend_RgbByteOrder( +- dest_scan, src_scan, src_left, m_pSrcPalette.get(), width, +- GetCompsFromFormat(m_DestFormat), clip_scan); ++ dest_scan, src_scan, src_left, m_SrcPalette.Get32BitPalette(), ++ width, GetCompsFromFormat(m_DestFormat), clip_scan); + } + } else { +- if (m_DestFormat == FXDIB_8bppRgb) { ++ if (m_DestFormat == FXDIB_Format::k8bppRgb) { + return; + } +- if (m_DestFormat == FXDIB_Argb) { ++ if (m_DestFormat == FXDIB_Format::kArgb) { + CompositeRow_8bppRgb2Argb_NoBlend_RgbByteOrder( +- dest_scan, src_scan, width, m_pSrcPalette.get(), clip_scan); ++ dest_scan, src_scan, width, m_SrcPalette.Get32BitPalette().data(), ++ clip_scan); + } else { + CompositeRow_8bppRgb2Rgb_NoBlend_RgbByteOrder( +- dest_scan, src_scan, m_pSrcPalette.get(), width, ++ dest_scan, src_scan, m_SrcPalette.Get32BitPalette().data(), width, + GetCompsFromFormat(m_DestFormat), clip_scan); + } + } + return; + } +- if (m_DestFormat == FXDIB_8bppMask) { +- CompositeRow_Rgb2Mask(dest_scan, src_scan, width, clip_scan); ++ ++ if (m_DestFormat == FXDIB_Format::k8bppMask) { ++ CompositeRow_Rgb2Mask(dest_scan, width, clip_scan); + return; + } +- if (GetBppFromFormat(m_DestFormat) == 8) { +- if (m_iTransparency & 8) { +- if (GetIsAlphaFromFormat(m_DestFormat)) { +- CompositeRow_1bppPal2Graya( +- dest_scan, src_scan, src_left, +- reinterpret_cast(m_pSrcPalette.get()), width, +- m_BlendType, clip_scan, dst_extra_alpha); +- } else { +- CompositeRow_1bppPal2Gray( +- dest_scan, src_scan, src_left, +- reinterpret_cast(m_pSrcPalette.get()), width, +- m_BlendType, clip_scan); +- } +- } else { +- if (GetIsAlphaFromFormat(m_DestFormat)) { +- CompositeRow_8bppPal2Graya( +- dest_scan, src_scan, +- reinterpret_cast(m_pSrcPalette.get()), width, +- m_BlendType, clip_scan, dst_extra_alpha, src_extra_alpha); +- } else { +- CompositeRow_8bppPal2Gray( +- dest_scan, src_scan, +- reinterpret_cast(m_pSrcPalette.get()), width, +- m_BlendType, clip_scan, src_extra_alpha); +- } ++ ++ if (m_DestFormat == FXDIB_Format::k8bppRgb) { ++ if (m_SrcFormat == FXDIB_Format::k1bppRgb) { ++ CompositeRow_1bppPal2Gray(dest_scan, src_scan, src_left, ++ m_SrcPalette.Get8BitPalette(), width, ++ m_BlendType, clip_scan); ++ return; + } +- } else { +- switch (m_iTransparency) { +- case 1 + 2: +- CompositeRow_8bppRgb2Argb_NoBlend(dest_scan, src_scan, width, +- m_pSrcPalette.get(), clip_scan, +- src_extra_alpha); +- break; +- case 1 + 2 + 8: +- CompositeRow_1bppRgb2Argb_NoBlend(dest_scan, src_scan, src_left, width, +- m_pSrcPalette.get(), clip_scan); +- break; +- case 0: +- CompositeRow_8bppRgb2Rgb_NoBlend( +- dest_scan, src_scan, m_pSrcPalette.get(), width, +- GetCompsFromFormat(m_DestFormat), clip_scan, src_extra_alpha); +- break; +- case 0 + 8: +- CompositeRow_1bppRgb2Rgb_NoBlend( +- dest_scan, src_scan, src_left, m_pSrcPalette.get(), width, +- GetCompsFromFormat(m_DestFormat), clip_scan); +- break; +- case 0 + 2: +- CompositeRow_8bppRgb2Rgb_NoBlend( +- dest_scan, src_scan, m_pSrcPalette.get(), width, +- GetCompsFromFormat(m_DestFormat), clip_scan, src_extra_alpha); +- break; +- case 0 + 2 + 8: +- CompositeRow_1bppRgb2Rgba_NoBlend(dest_scan, src_scan, src_left, width, +- m_pSrcPalette.get(), clip_scan, +- dst_extra_alpha); +- break; +- } +- } +-} +- +-void CFX_ScanlineCompositor::CompositeByteMaskLine(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int width, +- const uint8_t* clip_scan, +- uint8_t* dst_extra_alpha) { +- if (m_DestFormat == FXDIB_8bppMask) { ++ CompositeRow_8bppPal2Gray(dest_scan, src_scan, ++ m_SrcPalette.Get8BitPalette(), width, m_BlendType, ++ clip_scan); ++ return; ++ } ++ ++ // TODO(thestig): Tighten this check. ++ DCHECK_NE(GetBppFromFormat(m_DestFormat), 8); ++ ++ if (m_DestFormat == FXDIB_Format::kArgb) { ++ if (m_SrcFormat == FXDIB_Format::k1bppRgb) { ++ CompositeRow_1bppRgb2Argb_NoBlend(dest_scan, src_scan, src_left, width, ++ m_SrcPalette.Get32BitPalette(), ++ clip_scan); ++ return; ++ } ++ CompositeRow_8bppRgb2Argb_NoBlend( ++ dest_scan, src_scan, width, m_SrcPalette.Get32BitPalette(), clip_scan); ++ return; ++ } ++ ++ if (m_SrcFormat == FXDIB_Format::k8bppRgb) { ++ CompositeRow_8bppRgb2Rgb_NoBlend( ++ dest_scan, src_scan, m_SrcPalette.Get32BitPalette(), width, ++ GetCompsFromFormat(m_DestFormat), clip_scan); ++ return; ++ } ++ ++ CompositeRow_1bppRgb2Rgb_NoBlend(dest_scan, src_scan, src_left, ++ m_SrcPalette.Get32BitPalette(), width, ++ GetCompsFromFormat(m_DestFormat), clip_scan); ++} ++ ++void CFX_ScanlineCompositor::CompositeByteMaskLine( ++ pdfium::span dest_scan, ++ pdfium::span src_scan, ++ int width, ++ pdfium::span clip_scan) const { ++ if (m_DestFormat == FXDIB_Format::k8bppMask) { + CompositeRow_ByteMask2Mask(dest_scan, src_scan, m_MaskAlpha, width, + clip_scan); +- } else if (GetBppFromFormat(m_DestFormat) == 8) { +- if (GetIsAlphaFromFormat(m_DestFormat)) { +- CompositeRow_ByteMask2Graya(dest_scan, src_scan, m_MaskAlpha, m_MaskRed, +- width, clip_scan, dst_extra_alpha); +- } else { +- CompositeRow_ByteMask2Gray(dest_scan, src_scan, m_MaskAlpha, m_MaskRed, +- width, clip_scan); +- } +- } else if (m_bRgbByteOrder) { +- if (m_DestFormat == FXDIB_Argb) { ++ return; ++ } ++ if (m_DestFormat == FXDIB_Format::k8bppRgb) { ++ CompositeRow_ByteMask2Gray(dest_scan, src_scan, m_MaskAlpha, m_MaskRed, ++ width, clip_scan); ++ return; ++ } ++ ++ // TODO(thestig): Tighten this check. ++ DCHECK_NE(GetBppFromFormat(m_DestFormat), 8); ++ ++ if (m_bRgbByteOrder) { ++ if (m_DestFormat == FXDIB_Format::kArgb) { + CompositeRow_ByteMask2Argb_RgbByteOrder( + dest_scan, src_scan, m_MaskAlpha, m_MaskRed, m_MaskGreen, m_MaskBlue, + width, m_BlendType, clip_scan); +@@ -3189,40 +2520,50 @@ void CFX_ScanlineCompositor::CompositeByteMaskLine(uint8_t* dest_scan, + dest_scan, src_scan, m_MaskAlpha, m_MaskRed, m_MaskGreen, m_MaskBlue, + width, m_BlendType, GetCompsFromFormat(m_DestFormat), clip_scan); + } +- } else if (m_DestFormat == FXDIB_Argb) { ++ return; ++ } ++ ++ if (m_DestFormat == FXDIB_Format::kArgb) { + CompositeRow_ByteMask2Argb(dest_scan, src_scan, m_MaskAlpha, m_MaskRed, + m_MaskGreen, m_MaskBlue, width, m_BlendType, + clip_scan); +- } else if (m_DestFormat == FXDIB_Rgb || m_DestFormat == FXDIB_Rgb32) { ++ return; ++ } ++ ++ if (m_DestFormat == FXDIB_Format::kRgb || ++ m_DestFormat == FXDIB_Format::kRgb32) { + CompositeRow_ByteMask2Rgb(dest_scan, src_scan, m_MaskAlpha, m_MaskRed, + m_MaskGreen, m_MaskBlue, width, m_BlendType, + GetCompsFromFormat(m_DestFormat), clip_scan); +- } else if (m_DestFormat == FXDIB_Rgba) { +- CompositeRow_ByteMask2Rgba(dest_scan, src_scan, m_MaskAlpha, m_MaskRed, +- m_MaskGreen, m_MaskBlue, width, m_BlendType, +- clip_scan, dst_extra_alpha); ++ return; + } ++ ++ // TODO(thestig): Is this line reachable? + } + +-void CFX_ScanlineCompositor::CompositeBitMaskLine(uint8_t* dest_scan, +- const uint8_t* src_scan, +- int src_left, +- int width, +- const uint8_t* clip_scan, +- uint8_t* dst_extra_alpha) { +- if (m_DestFormat == FXDIB_8bppMask) { ++void CFX_ScanlineCompositor::CompositeBitMaskLine( ++ pdfium::span dest_scan, ++ pdfium::span src_scan, ++ int src_left, ++ int width, ++ pdfium::span clip_scan) const { ++ if (m_DestFormat == FXDIB_Format::k8bppMask) { + CompositeRow_BitMask2Mask(dest_scan, src_scan, m_MaskAlpha, src_left, width, + clip_scan); +- } else if (GetBppFromFormat(m_DestFormat) == 8) { +- if (GetIsAlphaFromFormat(m_DestFormat)) { +- CompositeRow_BitMask2Graya(dest_scan, src_scan, m_MaskAlpha, m_MaskRed, +- src_left, width, clip_scan, dst_extra_alpha); +- } else { +- CompositeRow_BitMask2Gray(dest_scan, src_scan, m_MaskAlpha, m_MaskRed, +- src_left, width, clip_scan); +- } +- } else if (m_bRgbByteOrder) { +- if (m_DestFormat == FXDIB_Argb) { ++ return; ++ } ++ ++ if (m_DestFormat == FXDIB_Format::k8bppRgb) { ++ CompositeRow_BitMask2Gray(dest_scan, src_scan, m_MaskAlpha, m_MaskRed, ++ src_left, width, clip_scan); ++ return; ++ } ++ ++ // TODO(thestig): Tighten this check. ++ DCHECK_NE(GetBppFromFormat(m_DestFormat), 8); ++ ++ if (m_bRgbByteOrder) { ++ if (m_DestFormat == FXDIB_Format::kArgb) { + CompositeRow_BitMask2Argb_RgbByteOrder( + dest_scan, src_scan, m_MaskAlpha, m_MaskRed, m_MaskGreen, m_MaskBlue, + src_left, width, m_BlendType, clip_scan); +@@ -3232,14 +2573,62 @@ void CFX_ScanlineCompositor::CompositeBitMaskLine(uint8_t* dest_scan, + src_left, width, m_BlendType, GetCompsFromFormat(m_DestFormat), + clip_scan); + } +- } else if (m_DestFormat == FXDIB_Argb) { ++ return; ++ } ++ ++ if (m_DestFormat == FXDIB_Format::kArgb) { + CompositeRow_BitMask2Argb(dest_scan, src_scan, m_MaskAlpha, m_MaskRed, + m_MaskGreen, m_MaskBlue, src_left, width, + m_BlendType, clip_scan); +- } else if (m_DestFormat == FXDIB_Rgb || m_DestFormat == FXDIB_Rgb32) { ++ return; ++ } ++ ++ if (m_DestFormat == FXDIB_Format::kRgb || ++ m_DestFormat == FXDIB_Format::kRgb32) { + CompositeRow_BitMask2Rgb(dest_scan, src_scan, m_MaskAlpha, m_MaskRed, + m_MaskGreen, m_MaskBlue, src_left, width, + m_BlendType, GetCompsFromFormat(m_DestFormat), + clip_scan); ++ return; + } ++ ++ // TODO(thestig): Is this line reachable? ++} ++ ++CFX_ScanlineCompositor::Palette::Palette() = default; ++ ++CFX_ScanlineCompositor::Palette::~Palette() = default; ++ ++void CFX_ScanlineCompositor::Palette::Reset() { ++ m_Width = 0; ++ m_nElements = 0; ++ m_pData.reset(); ++} ++ ++pdfium::span CFX_ScanlineCompositor::Palette::Make8BitPalette( ++ size_t nElements) { ++ m_Width = sizeof(uint8_t); ++ m_nElements = nElements; ++ m_pData.reset(reinterpret_cast(FX_Alloc(uint8_t, m_nElements))); ++ return {reinterpret_cast(m_pData.get()), m_nElements}; ++} ++ ++pdfium::span CFX_ScanlineCompositor::Palette::Make32BitPalette( ++ size_t nElements) { ++ m_Width = sizeof(uint32_t); ++ m_nElements = nElements; ++ m_pData.reset(FX_Alloc(uint32_t, m_nElements)); ++ return {m_pData.get(), m_nElements}; ++} ++ ++pdfium::span CFX_ScanlineCompositor::Palette::Get8BitPalette() ++ const { ++ CHECK(!m_pData || m_Width == sizeof(uint8_t)); ++ return {reinterpret_cast(m_pData.get()), m_nElements}; ++} ++ ++pdfium::span CFX_ScanlineCompositor::Palette::Get32BitPalette() ++ const { ++ CHECK(!m_pData || m_Width == sizeof(uint32_t)); ++ return {m_pData.get(), m_nElements}; + } +diff --git a/core/fxge/dib/cfx_scanlinecompositor.h b/core/fxge/dib/cfx_scanlinecompositor.h +index 0ec842dd3..9fa6a8691 100644 +--- a/core/fxge/dib/cfx_scanlinecompositor.h ++++ b/core/fxge/dib/cfx_scanlinecompositor.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,7 +10,8 @@ + #include + + #include "core/fxcrt/fx_memory_wrappers.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/span.h" + + class CFX_ScanlineCompositor { + public: +@@ -19,58 +20,71 @@ class CFX_ScanlineCompositor { + + bool Init(FXDIB_Format dest_format, + FXDIB_Format src_format, +- int32_t width, +- uint32_t* pSrcPalette, ++ pdfium::span src_palette, + uint32_t mask_color, + BlendMode blend_type, + bool bClip, + bool bRgbByteOrder); + +- void CompositeRgbBitmapLine(uint8_t* dest_scan, +- const uint8_t* src_scan, ++ void CompositeRgbBitmapLine(pdfium::span dest_scan, ++ pdfium::span src_scan, + int width, +- const uint8_t* clip_scan, +- const uint8_t* src_extra_alpha, +- uint8_t* dst_extra_alpha); ++ pdfium::span clip_scan) const; + +- void CompositePalBitmapLine(uint8_t* dest_scan, +- const uint8_t* src_scan, ++ void CompositePalBitmapLine(pdfium::span dest_scan, ++ pdfium::span src_scan, + int src_left, + int width, +- const uint8_t* clip_scan, +- const uint8_t* src_extra_alpha, +- uint8_t* dst_extra_alpha); ++ pdfium::span clip_scan) const; + +- void CompositeByteMaskLine(uint8_t* dest_scan, +- const uint8_t* src_scan, ++ void CompositeByteMaskLine(pdfium::span dest_scan, ++ pdfium::span src_scan, + int width, +- const uint8_t* clip_scan, +- uint8_t* dst_extra_alpha); ++ pdfium::span clip_scan) const; + +- void CompositeBitMaskLine(uint8_t* dest_scan, +- const uint8_t* src_scan, ++ void CompositeBitMaskLine(pdfium::span dest_scan, ++ pdfium::span src_scan, + int src_left, + int width, +- const uint8_t* clip_scan, +- uint8_t* dst_extra_alpha); ++ pdfium::span clip_scan) const; + + private: +- void InitSourcePalette(FXDIB_Format src_format, +- FXDIB_Format dest_format, +- const uint32_t* pSrcPalette); ++ class Palette { ++ public: ++ Palette(); ++ ~Palette(); ++ ++ void Reset(); ++ pdfium::span Make8BitPalette(size_t nElements); ++ pdfium::span Make32BitPalette(size_t nElements); ++ ++ // Hard CHECK() if mismatch between created and requested widths. ++ pdfium::span Get8BitPalette() const; ++ pdfium::span Get32BitPalette() const; ++ ++ private: ++ // If 0, then no |m_pData|. ++ // If 1, then |m_pData| is really uint8_t* instead. ++ // If 4, then |m_pData| is uint32_t* as expected. ++ size_t m_Width = 0; ++ size_t m_nElements = 0; ++ std::unique_ptr m_pData; ++ }; ++ ++ void InitSourcePalette(pdfium::span src_palette); + + void InitSourceMask(uint32_t mask_color); + +- int m_iTransparency; + FXDIB_Format m_SrcFormat; + FXDIB_Format m_DestFormat; +- std::unique_ptr m_pSrcPalette; ++ Palette m_SrcPalette; + int m_MaskAlpha; + int m_MaskRed; + int m_MaskGreen; + int m_MaskBlue; + BlendMode m_BlendType = BlendMode::kNormal; + bool m_bRgbByteOrder = false; ++ bool m_bClip = false; + }; + + #endif // CORE_FXGE_DIB_CFX_SCANLINECOMPOSITOR_H_ +diff --git a/core/fxge/dib/cstretchengine.cpp b/core/fxge/dib/cstretchengine.cpp +index 86364309e..e9629fc82 100644 +--- a/core/fxge/dib/cstretchengine.cpp ++++ b/core/fxge/dib/cstretchengine.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,161 +6,120 @@ + + #include "core/fxge/dib/cstretchengine.h" + ++#include ++ + #include ++#include + #include + ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/pauseindicator_iface.h" ++#include "core/fxge/calculate_pitch.h" + #include "core/fxge/dib/cfx_dibbase.h" + #include "core/fxge/dib/cfx_dibitmap.h" ++#include "core/fxge/dib/fx_dib.h" + #include "core/fxge/dib/scanlinecomposer_iface.h" +-#include "core/fxge/fx_dib.h" +-#include "third_party/base/stl_util.h" +- +-namespace { +- +-constexpr int kMaxDestValue = 16711680; ++#include "third_party/base/check.h" ++#include "third_party/base/cxx17_backports.h" ++ ++static_assert( ++ std::is_trivially_destructible::value, ++ "PixelWeight storage may be re-used without invoking its destructor"); ++ ++// static ++bool CStretchEngine::UseInterpolateBilinear( ++ const FXDIB_ResampleOptions& options, ++ int dest_width, ++ int dest_height, ++ int src_width, ++ int src_height) { ++ return !options.bInterpolateBilinear && !options.bNoSmoothing && ++ abs(dest_width) != 0 && ++ abs(dest_height) / 8 < ++ static_cast(src_width) * src_height / abs(dest_width); ++} + +-int GetPitchRoundUpTo4Bytes(int bits_per_pixel) { +- return (bits_per_pixel + 31) / 32 * 4; ++// static ++size_t CStretchEngine::PixelWeight::TotalBytesForWeightCount( ++ size_t weight_count) { ++ // Always room for one weight even for empty ranges due to declaration ++ // of m_Weights[1] in the header. Don't shrink below this since ++ // CalculateWeights() relies on this later. ++ const size_t extra_weights = weight_count > 0 ? weight_count - 1 : 0; ++ FX_SAFE_SIZE_T total_bytes = extra_weights; ++ total_bytes *= sizeof(m_Weights[0]); ++ total_bytes += sizeof(PixelWeight); ++ return total_bytes.ValueOrDie(); + } + +-} // namespace ++CStretchEngine::WeightTable::WeightTable() = default; + +-CStretchEngine::CWeightTable::CWeightTable() = default; ++CStretchEngine::WeightTable::~WeightTable() = default; + +-CStretchEngine::CWeightTable::~CWeightTable() = default; ++bool CStretchEngine::WeightTable::CalculateWeights( ++ int dest_len, ++ int dest_min, ++ int dest_max, ++ int src_len, ++ int src_min, ++ int src_max, ++ const FXDIB_ResampleOptions& options) { ++ // 512MB should be large enough for this while preventing OOM. ++ static constexpr size_t kMaxTableBytesAllowed = 512 * 1024 * 1024; + +-size_t CStretchEngine::CWeightTable::GetPixelWeightSize() const { +- return m_ItemSize / sizeof(int) - 2; +-} ++ // Help the compiler realize that these can't change during a loop iteration: ++ const bool bilinear = options.bInterpolateBilinear; + +-bool CStretchEngine::CWeightTable::Calc(int dest_len, +- int dest_min, +- int dest_max, +- int src_len, +- int src_min, +- int src_max, +- const FXDIB_ResampleOptions& options) { ++ m_DestMin = 0; ++ m_ItemSizeBytes = 0; ++ m_WeightTablesSizeBytes = 0; + m_WeightTables.clear(); +- m_dwWeightTablesSize = 0; +- const double scale = static_cast(src_len) / dest_len; +- const double base = dest_len < 0 ? src_len : 0; +- const int ext_size = options.bInterpolateBicubic ? 3 : 1; +- m_ItemSize = +- sizeof(int) * 2 + +- static_cast(sizeof(int) * +- (ceil(fabs(static_cast(scale))) + ext_size)); ++ if (dest_len == 0) ++ return true; ++ ++ if (dest_min > dest_max) ++ return false; + + m_DestMin = dest_min; +- if (dest_max - dest_min > static_cast((1U << 30) - 4) / m_ItemSize) ++ ++ const double scale = static_cast(src_len) / dest_len; ++ const double base = dest_len < 0 ? src_len : 0; ++ const size_t weight_count = static_cast(ceil(fabs(scale))) + 1; ++ m_ItemSizeBytes = PixelWeight::TotalBytesForWeightCount(weight_count); ++ ++ const size_t dest_range = static_cast(dest_max - dest_min); ++ const size_t kMaxTableItemsAllowed = kMaxTableBytesAllowed / m_ItemSizeBytes; ++ if (dest_range > kMaxTableItemsAllowed) + return false; + +- m_dwWeightTablesSize = (dest_max - dest_min) * m_ItemSize + 4; +- m_WeightTables.resize(m_dwWeightTablesSize); +- if (options.bNoSmoothing || fabs(static_cast(scale)) < 1.0f) { ++ m_WeightTablesSizeBytes = dest_range * m_ItemSizeBytes; ++ m_WeightTables.resize(m_WeightTablesSizeBytes); ++ if (options.bNoSmoothing || fabs(scale) < 1.0f) { + for (int dest_pixel = dest_min; dest_pixel < dest_max; ++dest_pixel) { + PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel); + double src_pos = dest_pixel * scale + scale / 2 + base; +- if (options.bInterpolateBilinear) { +- pixel_weights.m_SrcStart = +- static_cast(floor(static_cast(src_pos) - 1.0f / 2)); +- pixel_weights.m_SrcEnd = +- static_cast(floor(static_cast(src_pos) + 1.0f / 2)); +- pixel_weights.m_SrcStart = std::max(pixel_weights.m_SrcStart, src_min); +- pixel_weights.m_SrcEnd = std::min(pixel_weights.m_SrcEnd, src_max - 1); +- if (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd) { +- pixel_weights.m_Weights[0] = 65536; ++ if (bilinear) { ++ int src_start = static_cast(floor(src_pos - 0.5)); ++ int src_end = static_cast(floor(src_pos + 0.5)); ++ src_start = std::max(src_start, src_min); ++ src_end = std::min(src_end, src_max - 1); ++ pixel_weights.SetStartEnd(src_start, src_end, weight_count); ++ if (pixel_weights.m_SrcStart >= pixel_weights.m_SrcEnd) { ++ // Always room for one weight per size calculation. ++ pixel_weights.m_Weights[0] = kFixedPointOne; + } else { + pixel_weights.m_Weights[1] = +- FXSYS_roundf(static_cast( +- src_pos - pixel_weights.m_SrcStart - 1.0f / 2) * +- 65536); +- pixel_weights.m_Weights[0] = 65536 - pixel_weights.m_Weights[1]; +- } +- } else if (options.bInterpolateBicubic) { +- pixel_weights.m_SrcStart = +- static_cast(floor(static_cast(src_pos) - 1.0f / 2)); +- pixel_weights.m_SrcEnd = +- static_cast(floor(static_cast(src_pos) + 1.0f / 2)); +- int start = pixel_weights.m_SrcStart - 1; +- int end = pixel_weights.m_SrcEnd + 1; +- start = std::max(start, src_min); +- end = std::min(end, src_max - 1); +- if (pixel_weights.m_SrcStart < src_min) { +- src_pos += src_min - pixel_weights.m_SrcStart; +- pixel_weights.m_SrcStart = src_min; +- } +- pixel_weights.m_SrcEnd = std::min(pixel_weights.m_SrcEnd, src_max - 1); +- int weight = FXSYS_roundf( +- static_cast(src_pos - pixel_weights.m_SrcStart - 1.0f / 2) * +- 256); +- if (start == end) { +- pixel_weights.m_Weights[0] = +- (SDP_Table[256 + weight] + SDP_Table[weight] + +- SDP_Table[256 - weight] + SDP_Table[512 - weight]) +- << 8; +- } else if ((start == pixel_weights.m_SrcStart && +- (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd || +- end == pixel_weights.m_SrcEnd) && +- start < end) || +- (start < pixel_weights.m_SrcStart && +- pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd && +- end == pixel_weights.m_SrcEnd)) { +- if (start < pixel_weights.m_SrcStart) { +- pixel_weights.m_Weights[0] = SDP_Table[256 + weight] << 8; +- pixel_weights.m_Weights[1] = +- (SDP_Table[weight] + SDP_Table[256 - weight] + +- SDP_Table[512 - weight]) +- << 8; +- } else { +- if (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd) { +- pixel_weights.m_Weights[0] = +- (SDP_Table[256 + weight] + SDP_Table[weight] + +- SDP_Table[256 - weight]) +- << 8; +- pixel_weights.m_Weights[1] = SDP_Table[512 - weight] << 8; +- } else { +- pixel_weights.m_Weights[0] = +- (SDP_Table[256 + weight] + SDP_Table[weight]) << 8; +- pixel_weights.m_Weights[1] = +- (SDP_Table[256 - weight] + SDP_Table[512 - weight]) << 8; +- } +- } +- if (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd) { +- pixel_weights.m_SrcEnd = end; +- } +- if (start < pixel_weights.m_SrcStart) { +- pixel_weights.m_SrcStart = start; +- } +- } else if (start == pixel_weights.m_SrcStart && +- start < pixel_weights.m_SrcEnd && +- pixel_weights.m_SrcEnd < end) { ++ FixedFromDouble(src_pos - pixel_weights.m_SrcStart - 0.5f); + pixel_weights.m_Weights[0] = +- (SDP_Table[256 + weight] + SDP_Table[weight]) << 8; +- pixel_weights.m_Weights[1] = SDP_Table[256 - weight] << 8; +- pixel_weights.m_Weights[2] = SDP_Table[512 - weight] << 8; +- pixel_weights.m_SrcEnd = end; +- } else if (start < pixel_weights.m_SrcStart && +- pixel_weights.m_SrcStart < pixel_weights.m_SrcEnd && +- pixel_weights.m_SrcEnd == end) { +- pixel_weights.m_Weights[0] = SDP_Table[256 + weight] << 8; +- pixel_weights.m_Weights[1] = SDP_Table[weight] << 8; +- pixel_weights.m_Weights[2] = +- (SDP_Table[256 - weight] + SDP_Table[512 - weight]) << 8; +- pixel_weights.m_SrcStart = start; +- } else { +- pixel_weights.m_Weights[0] = SDP_Table[256 + weight] << 8; +- pixel_weights.m_Weights[1] = SDP_Table[weight] << 8; +- pixel_weights.m_Weights[2] = SDP_Table[256 - weight] << 8; +- pixel_weights.m_Weights[3] = SDP_Table[512 - weight] << 8; +- pixel_weights.m_SrcStart = start; +- pixel_weights.m_SrcEnd = end; ++ kFixedPointOne - pixel_weights.m_Weights[1]; + } + } else { +- int pixel_pos = static_cast(floor(static_cast(src_pos))); +- pixel_weights.m_SrcStart = std::max(pixel_pos, src_min); +- pixel_weights.m_SrcEnd = std::min(pixel_pos, src_max - 1); +- pixel_weights.m_Weights[0] = 65536; ++ int pixel_pos = static_cast(floor(src_pos)); ++ int src_start = std::max(pixel_pos, src_min); ++ int src_end = std::min(pixel_pos, src_max - 1); ++ pixel_weights.SetStartEnd(src_start, src_end, weight_count); ++ pixel_weights.m_Weights[0] = kFixedPointOne; + } + } + return true; +@@ -176,13 +135,13 @@ bool CStretchEngine::CWeightTable::Calc(int dest_len, + end_i = std::min(end_i, src_max - 1); + if (start_i > end_i) { + start_i = std::min(start_i, src_max - 1); +- pixel_weights.m_SrcStart = start_i; +- pixel_weights.m_SrcEnd = start_i; ++ pixel_weights.SetStartEnd(start_i, start_i, weight_count); + continue; + } +- pixel_weights.m_SrcStart = start_i; +- pixel_weights.m_SrcEnd = end_i; +- for (int j = start_i; j <= end_i; ++j) { ++ pixel_weights.SetStartEnd(start_i, end_i, weight_count); ++ uint32_t remaining = kFixedPointOne; ++ double rounding_error = 0.0; ++ for (int j = start_i; j < end_i; ++j) { + double dest_start = (j - base) / scale; + double dest_end = (j + 1 - base) / scale; + if (dest_start > dest_end) +@@ -190,34 +149,33 @@ bool CStretchEngine::CWeightTable::Calc(int dest_len, + double area_start = std::max(dest_start, static_cast(dest_pixel)); + double area_end = std::min(dest_end, static_cast(dest_pixel + 1)); + double weight = std::max(0.0, area_end - area_start); +- if (weight == 0 && j == end_i) { +- --pixel_weights.m_SrcEnd; +- break; +- } +- size_t idx = j - start_i; +- if (idx >= GetPixelWeightSize()) +- return false; +- +- pixel_weights.m_Weights[idx] = FXSYS_roundf(weight * 65536); ++ uint32_t fixed_weight = FixedFromDouble(weight + rounding_error); ++ pixel_weights.SetWeightForPosition(j, fixed_weight); ++ remaining -= fixed_weight; ++ rounding_error = ++ weight - static_cast(fixed_weight) / kFixedPointOne; ++ } ++ // Note: underflow is defined behaviour for unsigned types and will ++ // result in an out-of-range value. ++ if (remaining && remaining <= kFixedPointOne) { ++ pixel_weights.SetWeightForPosition(end_i, remaining); ++ } else { ++ pixel_weights.RemoveLastWeightAndAdjust(remaining); + } + } + return true; + } + +-const PixelWeight* CStretchEngine::CWeightTable::GetPixelWeight( ++const CStretchEngine::PixelWeight* CStretchEngine::WeightTable::GetPixelWeight( + int pixel) const { +- ASSERT(pixel >= m_DestMin); ++ DCHECK(pixel >= m_DestMin); + return reinterpret_cast( +- &m_WeightTables[(pixel - m_DestMin) * m_ItemSize]); ++ &m_WeightTables[(pixel - m_DestMin) * m_ItemSizeBytes]); + } + +-int* CStretchEngine::CWeightTable::GetValueFromPixelWeight(PixelWeight* pWeight, +- int index) const { +- if (index < pWeight->m_SrcStart) +- return nullptr; +- +- size_t idx = index - pWeight->m_SrcStart; +- return idx < GetPixelWeightSize() ? &pWeight->m_Weights[idx] : nullptr; ++CStretchEngine::PixelWeight* CStretchEngine::WeightTable::GetPixelWeight( ++ int pixel) { ++ return const_cast(std::as_const(*this).GetPixelWeight(pixel)); + } + + CStretchEngine::CStretchEngine(ScanlineComposerIface* pDestBitmap, +@@ -225,42 +183,42 @@ CStretchEngine::CStretchEngine(ScanlineComposerIface* pDestBitmap, + int dest_width, + int dest_height, + const FX_RECT& clip_rect, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + const FXDIB_ResampleOptions& options) + : m_DestFormat(dest_format), + m_DestBpp(GetBppFromFormat(dest_format)), +- m_SrcBpp(GetBppFromFormat(pSrcBitmap->GetFormat())), +- m_bHasAlpha(GetIsAlphaFromFormat(pSrcBitmap->GetFormat())), ++ m_SrcBpp(pSrcBitmap->GetBPP()), ++ m_bHasAlpha(pSrcBitmap->IsAlphaFormat()), + m_pSource(pSrcBitmap), +- m_pSrcPalette(pSrcBitmap->GetPalette()), ++ m_pSrcPalette(pSrcBitmap->GetPaletteSpan()), + m_SrcWidth(pSrcBitmap->GetWidth()), + m_SrcHeight(pSrcBitmap->GetHeight()), + m_pDestBitmap(pDestBitmap), + m_DestWidth(dest_width), + m_DestHeight(dest_height), + m_DestClip(clip_rect) { +- uint32_t size = clip_rect.Width(); +- if (size && m_DestBpp > static_cast(INT_MAX / size)) +- return; ++ if (m_bHasAlpha) { ++ DCHECK_EQ(m_DestFormat, FXDIB_Format::kArgb); ++ DCHECK_EQ(m_DestBpp, GetBppFromFormat(FXDIB_Format::kArgb)); ++ DCHECK_EQ(m_pSource->GetFormat(), FXDIB_Format::kArgb); ++ DCHECK_EQ(m_SrcBpp, GetBppFromFormat(FXDIB_Format::kArgb)); ++ } + +- size *= m_DestBpp; +- if (size > INT_MAX - 31) ++ absl::optional maybe_size = ++ fxge::CalculatePitch32(m_DestBpp, clip_rect.Width()); ++ if (!maybe_size.has_value()) + return; + +- size = GetPitchRoundUpTo4Bytes(size); +- m_DestScanline.resize(size); +- if (dest_format == FXDIB_Rgb32) ++ m_DestScanline.resize(maybe_size.value()); ++ if (dest_format == FXDIB_Format::kRgb32) + std::fill(m_DestScanline.begin(), m_DestScanline.end(), 255); +- m_InterPitch = GetPitchRoundUpTo4Bytes(m_DestClip.Width() * m_DestBpp); +- m_ExtraMaskPitch = GetPitchRoundUpTo4Bytes(m_DestClip.Width() * 8); ++ m_InterPitch = fxge::CalculatePitch32OrDie(m_DestBpp, m_DestClip.Width()); ++ m_ExtraMaskPitch = fxge::CalculatePitch32OrDie(8, m_DestClip.Width()); + if (options.bNoSmoothing) { + m_ResampleOptions.bNoSmoothing = true; + } else { +- bool bInterpol = +- options.bInterpolateBilinear || options.bInterpolateBicubic; +- if (!bInterpol && abs(dest_width) != 0 && +- abs(dest_height) / 8 < static_cast(m_SrcWidth) * +- m_SrcHeight / abs(dest_width)) { ++ if (UseInterpolateBilinear(options, dest_width, dest_height, m_SrcWidth, ++ m_SrcHeight)) { + m_ResampleOptions.bInterpolateBilinear = true; + } else { + m_ResampleOptions = options; +@@ -291,13 +249,8 @@ CStretchEngine::CStretchEngine(ScanlineComposerIface* pDestBitmap, + : TransformMethod::k1BppToManyBpp; + break; + case 8: +- if (m_DestBpp == 8) { +- m_TransMethod = m_bHasAlpha ? TransformMethod::k8BppTo8BppWithAlpha +- : TransformMethod::k8BppTo8Bpp; +- } else { +- m_TransMethod = m_bHasAlpha ? TransformMethod::k8BppToManyBppWithAlpha +- : TransformMethod::k8BppToManyBpp; +- } ++ m_TransMethod = m_DestBpp == 8 ? TransformMethod::k8BppTo8Bpp ++ : TransformMethod::k8BppToManyBpp; + break; + default: + m_TransMethod = m_bHasAlpha ? TransformMethod::kManyBpptoManyBppWithAlpha +@@ -306,7 +259,7 @@ CStretchEngine::CStretchEngine(ScanlineComposerIface* pDestBitmap, + } + } + +-CStretchEngine::~CStretchEngine() {} ++CStretchEngine::~CStretchEngine() = default; + + bool CStretchEngine::Continue(PauseIndicatorIface* pPause) { + while (m_State == State::kHorizontal) { +@@ -323,22 +276,21 @@ bool CStretchEngine::StartStretchHorz() { + if (m_DestWidth == 0 || m_InterPitch == 0 || m_DestScanline.empty()) + return false; + +- if (m_SrcClip.Height() == 0 || +- m_SrcClip.Height() > (1 << 29) / m_InterPitch) { ++ FX_SAFE_SIZE_T safe_size = m_SrcClip.Height(); ++ safe_size *= m_InterPitch; ++ const size_t size = safe_size.ValueOrDefault(0); ++ if (size == 0) + return false; +- } + +- m_InterBuf.resize(m_SrcClip.Height() * m_InterPitch); +- if (m_pSource && m_bHasAlpha && m_pSource->m_pAlphaMask) { +- m_ExtraAlphaBuf.resize(m_SrcClip.Height(), m_ExtraMaskPitch); +- m_DestMaskScanline.resize(m_ExtraMaskPitch); +- } +- bool ret = m_WeightTable.Calc(m_DestWidth, m_DestClip.left, m_DestClip.right, +- m_SrcWidth, m_SrcClip.left, m_SrcClip.right, +- m_ResampleOptions); +- if (!ret) ++ m_InterBuf = FixedTryAllocZeroedDataVector(size); ++ if (m_InterBuf.empty()) + return false; + ++ if (!m_WeightTable.CalculateWeights( ++ m_DestWidth, m_DestClip.left, m_DestClip.right, m_SrcWidth, ++ m_SrcClip.left, m_SrcClip.right, m_ResampleOptions)) { ++ return false; ++ } + m_CurRow = m_SrcClip.top; + m_State = State::kHorizontal; + return true; +@@ -361,220 +313,105 @@ bool CStretchEngine::ContinueStretchHorz(PauseIndicatorIface* pPause) { + rows_to_go = kStrechPauseRows; + } + +- const uint8_t* src_scan = m_pSource->GetScanline(m_CurRow); +- uint8_t* dest_scan = +- m_InterBuf.data() + (m_CurRow - m_SrcClip.top) * m_InterPitch; +- const uint8_t* src_scan_mask = nullptr; +- uint8_t* dest_scan_mask = nullptr; +- if (!m_ExtraAlphaBuf.empty()) { +- src_scan_mask = m_pSource->m_pAlphaMask->GetScanline(m_CurRow); +- dest_scan_mask = m_ExtraAlphaBuf.data() + +- (m_CurRow - m_SrcClip.top) * m_ExtraMaskPitch; +- } ++ const uint8_t* src_scan = m_pSource->GetScanline(m_CurRow).data(); ++ pdfium::span dest_span = m_InterBuf.writable_span().subspan( ++ (m_CurRow - m_SrcClip.top) * m_InterPitch, m_InterPitch); ++ size_t dest_span_index = 0; + // TODO(npm): reduce duplicated code here + switch (m_TransMethod) { + case TransformMethod::k1BppTo8Bpp: + case TransformMethod::k1BppToManyBpp: { + for (int col = m_DestClip.left; col < m_DestClip.right; ++col) { + PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col); +- int dest_a = 0; ++ uint32_t dest_a = 0; + for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) { +- int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j); +- if (!pWeight) +- return false; +- +- int pixel_weight = *pWeight; ++ uint32_t pixel_weight = pWeights->GetWeightForPosition(j); + if (src_scan[j / 8] & (1 << (7 - j % 8))) + dest_a += pixel_weight * 255; + } +- if (m_ResampleOptions.bInterpolateBicubic) +- dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue); +- *dest_scan++ = static_cast(dest_a >> 16); ++ dest_span[dest_span_index++] = PixelFromFixed(dest_a); + } + break; + } + case TransformMethod::k8BppTo8Bpp: { + for (int col = m_DestClip.left; col < m_DestClip.right; ++col) { + PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col); +- int dest_a = 0; ++ uint32_t dest_a = 0; + for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) { +- int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j); +- if (!pWeight) +- return false; +- +- int pixel_weight = *pWeight; ++ uint32_t pixel_weight = pWeights->GetWeightForPosition(j); + dest_a += pixel_weight * src_scan[j]; + } +- if (m_ResampleOptions.bInterpolateBicubic) +- dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue); +- *dest_scan++ = static_cast(dest_a >> 16); +- } +- break; +- } +- case TransformMethod::k8BppTo8BppWithAlpha: { +- for (int col = m_DestClip.left; col < m_DestClip.right; ++col) { +- PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col); +- int dest_a = 0; +- int dest_r = 0; +- for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) { +- int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j); +- if (!pWeight) +- return false; +- +- int pixel_weight = *pWeight; +- pixel_weight = pixel_weight * src_scan_mask[j] / 255; +- dest_r += pixel_weight * src_scan[j]; +- dest_a += pixel_weight; +- } +- if (m_ResampleOptions.bInterpolateBicubic) { +- dest_r = pdfium::clamp(dest_r, 0, kMaxDestValue); +- dest_a = pdfium::clamp(dest_a, 0, 65536); +- } +- *dest_scan++ = static_cast(dest_r >> 16); +- *dest_scan_mask++ = static_cast((dest_a * 255) >> 16); ++ dest_span[dest_span_index++] = PixelFromFixed(dest_a); + } + break; + } + case TransformMethod::k8BppToManyBpp: { + for (int col = m_DestClip.left; col < m_DestClip.right; ++col) { + PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col); +- int dest_r_y = 0; +- int dest_g_m = 0; +- int dest_b_c = 0; ++ uint32_t dest_r = 0; ++ uint32_t dest_g = 0; ++ uint32_t dest_b = 0; + for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) { +- int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j); +- if (!pWeight) +- return false; +- +- int pixel_weight = *pWeight; +- unsigned long argb_cmyk = m_pSrcPalette[src_scan[j]]; +- if (m_DestFormat == FXDIB_Rgb) { +- dest_r_y += pixel_weight * static_cast(argb_cmyk >> 16); +- dest_g_m += pixel_weight * static_cast(argb_cmyk >> 8); +- dest_b_c += pixel_weight * static_cast(argb_cmyk); ++ uint32_t pixel_weight = pWeights->GetWeightForPosition(j); ++ unsigned long argb = m_pSrcPalette[src_scan[j]]; ++ if (m_DestFormat == FXDIB_Format::kRgb) { ++ dest_r += pixel_weight * static_cast(argb >> 16); ++ dest_g += pixel_weight * static_cast(argb >> 8); ++ dest_b += pixel_weight * static_cast(argb); + } else { +- dest_b_c += pixel_weight * static_cast(argb_cmyk >> 24); +- dest_g_m += pixel_weight * static_cast(argb_cmyk >> 16); +- dest_r_y += pixel_weight * static_cast(argb_cmyk >> 8); ++ dest_b += pixel_weight * static_cast(argb >> 24); ++ dest_g += pixel_weight * static_cast(argb >> 16); ++ dest_r += pixel_weight * static_cast(argb >> 8); + } + } +- if (m_ResampleOptions.bInterpolateBicubic) { +- dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue); +- dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue); +- dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue); +- } +- *dest_scan++ = static_cast(dest_b_c >> 16); +- *dest_scan++ = static_cast(dest_g_m >> 16); +- *dest_scan++ = static_cast(dest_r_y >> 16); +- } +- break; +- } +- case TransformMethod::k8BppToManyBppWithAlpha: { +- for (int col = m_DestClip.left; col < m_DestClip.right; ++col) { +- PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col); +- int dest_a = 0; +- int dest_r_y = 0; +- int dest_g_m = 0; +- int dest_b_c = 0; +- for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) { +- int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j); +- if (!pWeight) +- return false; +- +- int pixel_weight = *pWeight; +- pixel_weight = pixel_weight * src_scan_mask[j] / 255; +- unsigned long argb_cmyk = m_pSrcPalette[src_scan[j]]; +- if (m_DestFormat == FXDIB_Rgba) { +- dest_r_y += pixel_weight * static_cast(argb_cmyk >> 16); +- dest_g_m += pixel_weight * static_cast(argb_cmyk >> 8); +- dest_b_c += pixel_weight * static_cast(argb_cmyk); +- } else { +- dest_b_c += pixel_weight * static_cast(argb_cmyk >> 24); +- dest_g_m += pixel_weight * static_cast(argb_cmyk >> 16); +- dest_r_y += pixel_weight * static_cast(argb_cmyk >> 8); +- } +- dest_a += pixel_weight; +- } +- if (m_ResampleOptions.bInterpolateBicubic) { +- dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue); +- dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue); +- dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue); +- dest_a = pdfium::clamp(dest_a, 0, 65536); +- } +- *dest_scan++ = static_cast(dest_b_c >> 16); +- *dest_scan++ = static_cast(dest_g_m >> 16); +- *dest_scan++ = static_cast(dest_r_y >> 16); +- *dest_scan_mask++ = static_cast((dest_a * 255) >> 16); ++ dest_span[dest_span_index++] = PixelFromFixed(dest_b); ++ dest_span[dest_span_index++] = PixelFromFixed(dest_g); ++ dest_span[dest_span_index++] = PixelFromFixed(dest_r); + } + break; + } + case TransformMethod::kManyBpptoManyBpp: { + for (int col = m_DestClip.left; col < m_DestClip.right; ++col) { + PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col); +- int dest_r_y = 0; +- int dest_g_m = 0; +- int dest_b_c = 0; ++ uint32_t dest_r = 0; ++ uint32_t dest_g = 0; ++ uint32_t dest_b = 0; + for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) { +- int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j); +- if (!pWeight) +- return false; +- +- int pixel_weight = *pWeight; ++ uint32_t pixel_weight = pWeights->GetWeightForPosition(j); + const uint8_t* src_pixel = src_scan + j * Bpp; +- dest_b_c += pixel_weight * (*src_pixel++); +- dest_g_m += pixel_weight * (*src_pixel++); +- dest_r_y += pixel_weight * (*src_pixel); +- } +- if (m_ResampleOptions.bInterpolateBicubic) { +- dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue); +- dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue); +- dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue); ++ dest_b += pixel_weight * (*src_pixel++); ++ dest_g += pixel_weight * (*src_pixel++); ++ dest_r += pixel_weight * (*src_pixel); + } +- *dest_scan++ = static_cast((dest_b_c) >> 16); +- *dest_scan++ = static_cast((dest_g_m) >> 16); +- *dest_scan++ = static_cast((dest_r_y) >> 16); +- dest_scan += Bpp - 3; ++ dest_span[dest_span_index++] = PixelFromFixed(dest_b); ++ dest_span[dest_span_index++] = PixelFromFixed(dest_g); ++ dest_span[dest_span_index++] = PixelFromFixed(dest_r); ++ dest_span_index += Bpp - 3; + } + break; + } + case TransformMethod::kManyBpptoManyBppWithAlpha: { ++ DCHECK(m_bHasAlpha); + for (int col = m_DestClip.left; col < m_DestClip.right; ++col) { + PixelWeight* pWeights = m_WeightTable.GetPixelWeight(col); +- int dest_a = 0; +- int dest_r_y = 0; +- int dest_g_m = 0; +- int dest_b_c = 0; ++ uint32_t dest_a = 0; ++ uint32_t dest_r = 0; ++ uint32_t dest_g = 0; ++ uint32_t dest_b = 0; + for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) { +- int* pWeight = m_WeightTable.GetValueFromPixelWeight(pWeights, j); +- if (!pWeight) +- return false; +- +- int pixel_weight = *pWeight; + const uint8_t* src_pixel = src_scan + j * Bpp; +- if (m_DestFormat == FXDIB_Argb) { +- pixel_weight = pixel_weight * src_pixel[3] / 255; +- } else { +- pixel_weight = pixel_weight * src_scan_mask[j] / 255; +- } +- dest_b_c += pixel_weight * (*src_pixel++); +- dest_g_m += pixel_weight * (*src_pixel++); +- dest_r_y += pixel_weight * (*src_pixel); ++ uint32_t pixel_weight = ++ pWeights->GetWeightForPosition(j) * src_pixel[3] / 255; ++ dest_b += pixel_weight * (*src_pixel++); ++ dest_g += pixel_weight * (*src_pixel++); ++ dest_r += pixel_weight * (*src_pixel); + dest_a += pixel_weight; + } +- if (m_ResampleOptions.bInterpolateBicubic) { +- dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue); +- dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue); +- dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue); +- dest_a = pdfium::clamp(dest_a, 0, 65536); +- } +- *dest_scan++ = static_cast((dest_b_c) >> 16); +- *dest_scan++ = static_cast((dest_g_m) >> 16); +- *dest_scan++ = static_cast((dest_r_y) >> 16); +- if (m_DestFormat == FXDIB_Argb) +- *dest_scan = static_cast((dest_a * 255) >> 16); +- if (dest_scan_mask) +- *dest_scan_mask++ = static_cast((dest_a * 255) >> 16); +- dest_scan += Bpp - 3; ++ dest_span[dest_span_index++] = PixelFromFixed(dest_b); ++ dest_span[dest_span_index++] = PixelFromFixed(dest_g); ++ dest_span[dest_span_index++] = PixelFromFixed(dest_r); ++ dest_span[dest_span_index] = PixelFromFixed(255 * dest_a); ++ dest_span_index += Bpp - 3; + } + break; + } +@@ -588,160 +425,91 @@ void CStretchEngine::StretchVert() { + if (m_DestHeight == 0) + return; + +- CWeightTable table; +- bool ret = +- table.Calc(m_DestHeight, m_DestClip.top, m_DestClip.bottom, m_SrcHeight, +- m_SrcClip.top, m_SrcClip.bottom, m_ResampleOptions); +- if (!ret) ++ WeightTable table; ++ if (!table.CalculateWeights(m_DestHeight, m_DestClip.top, m_DestClip.bottom, ++ m_SrcHeight, m_SrcClip.top, m_SrcClip.bottom, ++ m_ResampleOptions)) { + return; ++ } + + const int DestBpp = m_DestBpp / 8; + for (int row = m_DestClip.top; row < m_DestClip.bottom; ++row) { + unsigned char* dest_scan = m_DestScanline.data(); +- unsigned char* dest_scan_mask = m_DestMaskScanline.data(); + PixelWeight* pWeights = table.GetPixelWeight(row); + switch (m_TransMethod) { + case TransformMethod::k1BppTo8Bpp: + case TransformMethod::k1BppToManyBpp: + case TransformMethod::k8BppTo8Bpp: { + for (int col = m_DestClip.left; col < m_DestClip.right; ++col) { +- unsigned char* src_scan = +- m_InterBuf.data() + (col - m_DestClip.left) * DestBpp; +- int dest_a = 0; ++ pdfium::span src_span = ++ m_InterBuf.span().subspan((col - m_DestClip.left) * DestBpp); ++ uint32_t dest_a = 0; + for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) { +- int* pWeight = table.GetValueFromPixelWeight(pWeights, j); +- if (!pWeight) +- return; +- +- int pixel_weight = *pWeight; ++ uint32_t pixel_weight = pWeights->GetWeightForPosition(j); + dest_a += +- pixel_weight * src_scan[(j - m_SrcClip.top) * m_InterPitch]; ++ pixel_weight * src_span[(j - m_SrcClip.top) * m_InterPitch]; + } +- if (m_ResampleOptions.bInterpolateBicubic) +- dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue); +- *dest_scan = static_cast(dest_a >> 16); ++ *dest_scan = PixelFromFixed(dest_a); + dest_scan += DestBpp; + } + break; + } +- case TransformMethod::k8BppTo8BppWithAlpha: { +- for (int col = m_DestClip.left; col < m_DestClip.right; ++col) { +- unsigned char* src_scan = +- m_InterBuf.data() + (col - m_DestClip.left) * DestBpp; +- unsigned char* src_scan_mask = +- m_ExtraAlphaBuf.data() + (col - m_DestClip.left); +- int dest_a = 0; +- int dest_k = 0; +- for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) { +- int* pWeight = table.GetValueFromPixelWeight(pWeights, j); +- if (!pWeight) +- return; +- +- int pixel_weight = *pWeight; +- dest_k += +- pixel_weight * src_scan[(j - m_SrcClip.top) * m_InterPitch]; +- dest_a += pixel_weight * +- src_scan_mask[(j - m_SrcClip.top) * m_ExtraMaskPitch]; +- } +- if (m_ResampleOptions.bInterpolateBicubic) { +- dest_k = pdfium::clamp(dest_k, 0, kMaxDestValue); +- dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue); +- } +- *dest_scan = static_cast(dest_k >> 16); +- dest_scan += DestBpp; +- *dest_scan_mask++ = static_cast(dest_a >> 16); +- } +- break; +- } + case TransformMethod::k8BppToManyBpp: + case TransformMethod::kManyBpptoManyBpp: { + for (int col = m_DestClip.left; col < m_DestClip.right; ++col) { +- unsigned char* src_scan = +- m_InterBuf.data() + (col - m_DestClip.left) * DestBpp; +- int dest_r_y = 0; +- int dest_g_m = 0; +- int dest_b_c = 0; ++ pdfium::span src_span = ++ m_InterBuf.span().subspan((col - m_DestClip.left) * DestBpp); ++ uint32_t dest_r = 0; ++ uint32_t dest_g = 0; ++ uint32_t dest_b = 0; + for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) { +- int* pWeight = table.GetValueFromPixelWeight(pWeights, j); +- if (!pWeight) +- return; +- +- int pixel_weight = *pWeight; +- const uint8_t* src_pixel = +- src_scan + (j - m_SrcClip.top) * m_InterPitch; +- dest_b_c += pixel_weight * (*src_pixel++); +- dest_g_m += pixel_weight * (*src_pixel++); +- dest_r_y += pixel_weight * (*src_pixel); ++ uint32_t pixel_weight = pWeights->GetWeightForPosition(j); ++ pdfium::span src_pixel = ++ src_span.subspan((j - m_SrcClip.top) * m_InterPitch, 3); ++ dest_b += pixel_weight * src_pixel[0]; ++ dest_g += pixel_weight * src_pixel[1]; ++ dest_r += pixel_weight * src_pixel[2]; + } +- if (m_ResampleOptions.bInterpolateBicubic) { +- dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue); +- dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue); +- dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue); +- } +- dest_scan[0] = static_cast((dest_b_c) >> 16); +- dest_scan[1] = static_cast((dest_g_m) >> 16); +- dest_scan[2] = static_cast((dest_r_y) >> 16); ++ dest_scan[0] = PixelFromFixed(dest_b); ++ dest_scan[1] = PixelFromFixed(dest_g); ++ dest_scan[2] = PixelFromFixed(dest_r); + dest_scan += DestBpp; + } + break; + } +- case TransformMethod::k8BppToManyBppWithAlpha: + case TransformMethod::kManyBpptoManyBppWithAlpha: { ++ DCHECK(m_bHasAlpha); + for (int col = m_DestClip.left; col < m_DestClip.right; ++col) { +- unsigned char* src_scan = +- m_InterBuf.data() + (col - m_DestClip.left) * DestBpp; +- unsigned char* src_scan_mask = nullptr; +- if (m_DestFormat != FXDIB_Argb) +- src_scan_mask = m_ExtraAlphaBuf.data() + (col - m_DestClip.left); +- int dest_a = 0; +- int dest_r_y = 0; +- int dest_g_m = 0; +- int dest_b_c = 0; ++ pdfium::span src_span = ++ m_InterBuf.span().subspan((col - m_DestClip.left) * DestBpp); ++ uint32_t dest_a = 0; ++ uint32_t dest_r = 0; ++ uint32_t dest_g = 0; ++ uint32_t dest_b = 0; ++ constexpr size_t kPixelBytes = 4; + for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) { +- int* pWeight = table.GetValueFromPixelWeight(pWeights, j); +- if (!pWeight) +- return; +- +- int pixel_weight = *pWeight; +- const uint8_t* src_pixel = +- src_scan + (j - m_SrcClip.top) * m_InterPitch; +- int mask_v = 255; +- if (src_scan_mask) +- mask_v = src_scan_mask[(j - m_SrcClip.top) * m_ExtraMaskPitch]; +- dest_b_c += pixel_weight * (*src_pixel++); +- dest_g_m += pixel_weight * (*src_pixel++); +- dest_r_y += pixel_weight * (*src_pixel); +- if (m_DestFormat == FXDIB_Argb) +- dest_a += pixel_weight * (*(src_pixel + 1)); +- else +- dest_a += pixel_weight * mask_v; +- } +- if (m_ResampleOptions.bInterpolateBicubic) { +- dest_r_y = pdfium::clamp(dest_r_y, 0, kMaxDestValue); +- dest_g_m = pdfium::clamp(dest_g_m, 0, kMaxDestValue); +- dest_b_c = pdfium::clamp(dest_b_c, 0, kMaxDestValue); +- dest_a = pdfium::clamp(dest_a, 0, kMaxDestValue); ++ uint32_t pixel_weight = pWeights->GetWeightForPosition(j); ++ pdfium::span src_pixel = src_span.subspan( ++ (j - m_SrcClip.top) * m_InterPitch, kPixelBytes); ++ dest_b += pixel_weight * src_pixel[0]; ++ dest_g += pixel_weight * src_pixel[1]; ++ dest_r += pixel_weight * src_pixel[2]; ++ dest_a += pixel_weight * src_pixel[3]; + } + if (dest_a) { +- int r = static_cast(dest_r_y) * 255 / dest_a; +- int g = static_cast(dest_g_m) * 255 / dest_a; +- int b = static_cast(dest_b_c) * 255 / dest_a; ++ int r = static_cast(dest_r) * 255 / dest_a; ++ int g = static_cast(dest_g) * 255 / dest_a; ++ int b = static_cast(dest_b) * 255 / dest_a; + dest_scan[0] = pdfium::clamp(b, 0, 255); + dest_scan[1] = pdfium::clamp(g, 0, 255); + dest_scan[2] = pdfium::clamp(r, 0, 255); + } +- if (m_DestFormat == FXDIB_Argb) +- dest_scan[3] = static_cast((dest_a) >> 16); +- else +- *dest_scan_mask = static_cast((dest_a) >> 16); ++ dest_scan[3] = PixelFromFixed(dest_a); + dest_scan += DestBpp; +- if (dest_scan_mask) +- dest_scan_mask++; + } + break; + } + } +- m_pDestBitmap->ComposeScanline(row - m_DestClip.top, m_DestScanline.data(), +- m_DestMaskScanline.data()); ++ m_pDestBitmap->ComposeScanline(row - m_DestClip.top, m_DestScanline); + } + } +diff --git a/core/fxge/dib/cstretchengine.h b/core/fxge/dib/cstretchengine.h +index 179296336..2ee14774a 100644 +--- a/core/fxge/dib/cstretchengine.h ++++ b/core/fxge/dib/cstretchengine.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,17 @@ + #ifndef CORE_FXGE_DIB_CSTRETCHENGINE_H_ + #define CORE_FXGE_DIB_CSTRETCHENGINE_H_ + +-#include ++#include + ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fixed_try_alloc_zeroed_data_vector.h" + #include "core/fxcrt/fx_coordinates.h" ++#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/span.h" + + class CFX_DIBBase; + class PauseIndicatorIface; +@@ -20,59 +25,114 @@ class ScanlineComposerIface; + + class CStretchEngine { + public: ++ static constexpr uint32_t kFixedPointBits = 16; ++ static constexpr uint32_t kFixedPointOne = 1 << kFixedPointBits; ++ ++ static inline uint32_t FixedFromDouble(double d) { ++ return static_cast(FXSYS_round(d * kFixedPointOne)); ++ } ++ ++ static inline uint32_t FixedFromFloat(float f) { ++ return static_cast(FXSYS_roundf(f * kFixedPointOne)); ++ } ++ ++ static inline uint8_t PixelFromFixed(uint32_t fixed) { ++ return static_cast(fixed >> kFixedPointBits); ++ } ++ ++ // Indicates whether to manually set interpolate bilinear option to true to ++ // achieve a smoother rendering results. ++ static bool UseInterpolateBilinear(const FXDIB_ResampleOptions& options, ++ int dest_width, ++ int dest_height, ++ int src_width, ++ int src_height); ++ ++ struct PixelWeight { ++ static size_t TotalBytesForWeightCount(size_t weight_count); ++ ++ void SetStartEnd(int src_start, int src_end, size_t weight_count) { ++ CHECK_LT(src_end - src_start, static_cast(weight_count)); ++ m_SrcStart = src_start; ++ m_SrcEnd = src_end; ++ } ++ ++ uint32_t GetWeightForPosition(int position) const { ++ CHECK_GE(position, m_SrcStart); ++ CHECK_LE(position, m_SrcEnd); ++ return m_Weights[position - m_SrcStart]; ++ } ++ ++ void SetWeightForPosition(int position, uint32_t weight) { ++ CHECK_GE(position, m_SrcStart); ++ CHECK_LE(position, m_SrcEnd); ++ m_Weights[position - m_SrcStart] = weight; ++ } ++ ++ // NOTE: relies on defined behaviour for unsigned overflow to ++ // decrement the previous position, as needed. ++ void RemoveLastWeightAndAdjust(uint32_t weight_change) { ++ CHECK_GT(m_SrcEnd, m_SrcStart); ++ --m_SrcEnd; ++ m_Weights[m_SrcEnd - m_SrcStart] += weight_change; ++ } ++ ++ int m_SrcStart; ++ int m_SrcEnd; // Note: inclusive, [0, -1] for empty range at 0. ++ uint32_t m_Weights[1]; // Not really 1, variable size. ++ }; ++ ++ class WeightTable { ++ public: ++ WeightTable(); ++ ~WeightTable(); ++ ++ // Accepts a negative `dest_len` argument, producing a "mirror ++ // image" of the result if `dest_len` is negative. ++ bool CalculateWeights(int dest_len, ++ int dest_min, ++ int dest_max, ++ int src_len, ++ int src_min, ++ int src_max, ++ const FXDIB_ResampleOptions& options); ++ ++ const PixelWeight* GetPixelWeight(int pixel) const; ++ PixelWeight* GetPixelWeight(int pixel); ++ ++ private: ++ int m_DestMin = 0; ++ size_t m_ItemSizeBytes = 0; ++ size_t m_WeightTablesSizeBytes = 0; ++ DataVector m_WeightTables; ++ }; ++ + CStretchEngine(ScanlineComposerIface* pDestBitmap, + FXDIB_Format dest_format, + int dest_width, + int dest_height, + const FX_RECT& clip_rect, +- const RetainPtr& pSrcBitmap, ++ const RetainPtr& pSrcBitmap, + const FXDIB_ResampleOptions& options); + ~CStretchEngine(); + + bool Continue(PauseIndicatorIface* pPause); +- + bool StartStretchHorz(); + bool ContinueStretchHorz(PauseIndicatorIface* pPause); + void StretchVert(); + +- class CWeightTable { +- public: +- CWeightTable(); +- ~CWeightTable(); +- +- bool Calc(int dest_len, +- int dest_min, +- int dest_max, +- int src_len, +- int src_min, +- int src_max, +- const FXDIB_ResampleOptions& options); +- +- const PixelWeight* GetPixelWeight(int pixel) const; +- PixelWeight* GetPixelWeight(int pixel) { +- return const_cast( +- static_cast(this)->GetPixelWeight(pixel)); +- } +- +- int* GetValueFromPixelWeight(PixelWeight* pWeight, int index) const; +- size_t GetPixelWeightSize() const; +- +- private: +- int m_DestMin = 0; +- int m_ItemSize = 0; +- size_t m_dwWeightTablesSize = 0; +- std::vector m_WeightTables; +- }; ++ const FXDIB_ResampleOptions& GetResampleOptionsForTest() const { ++ return m_ResampleOptions; ++ } + ++ private: + enum class State : uint8_t { kInitial, kHorizontal, kVertical }; + + enum class TransformMethod : uint8_t { + k1BppTo8Bpp, + k1BppToManyBpp, + k8BppTo8Bpp, +- k8BppTo8BppWithAlpha, + k8BppToManyBpp, +- k8BppToManyBppWithAlpha, + kManyBpptoManyBpp, + kManyBpptoManyBppWithAlpha + }; +@@ -80,27 +140,25 @@ class CStretchEngine { + const FXDIB_Format m_DestFormat; + const int m_DestBpp; + const int m_SrcBpp; +- const int m_bHasAlpha; +- RetainPtr const m_pSource; +- const uint32_t* m_pSrcPalette; ++ const bool m_bHasAlpha; ++ RetainPtr const m_pSource; ++ pdfium::span m_pSrcPalette; + const int m_SrcWidth; + const int m_SrcHeight; + UnownedPtr const m_pDestBitmap; + const int m_DestWidth; + const int m_DestHeight; + const FX_RECT m_DestClip; +- std::vector m_DestScanline; +- std::vector m_DestMaskScanline; +- std::vector m_InterBuf; +- std::vector m_ExtraAlphaBuf; ++ DataVector m_DestScanline; ++ FixedTryAllocZeroedDataVector m_InterBuf; + FX_RECT m_SrcClip; + int m_InterPitch; + int m_ExtraMaskPitch; + FXDIB_ResampleOptions m_ResampleOptions; + TransformMethod m_TransMethod; + State m_State = State::kInitial; +- int m_CurRow; +- CWeightTable m_WeightTable; ++ int m_CurRow = 0; ++ WeightTable m_WeightTable; + }; + + #endif // CORE_FXGE_DIB_CSTRETCHENGINE_H_ +diff --git a/core/fxge/dib/cstretchengine_unittest.cpp b/core/fxge/dib/cstretchengine_unittest.cpp +index 8c360e776..d11ac9dc2 100644 +--- a/core/fxge/dib/cstretchengine_unittest.cpp ++++ b/core/fxge/dib/cstretchengine_unittest.cpp +@@ -1,19 +1,72 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxge/dib/cstretchengine.h" + +-#include + #include + + #include "core/fpdfapi/page/cpdf_dib.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fpdfapi/parser/cpdf_stream.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" ++ ++namespace { ++ ++// Discovered experimentally ++constexpr uint32_t kTooBigSrcLen = 20; ++constexpr uint32_t kTooBigDestLen = 32 * 1024 * 1024 + 1; ++ ++uint32_t PixelWeightSum(const CStretchEngine::PixelWeight* weights) { ++ uint32_t sum = 0; ++ for (int i = weights->m_SrcStart; i <= weights->m_SrcEnd; ++i) { ++ sum += weights->GetWeightForPosition(i); ++ } ++ return sum; ++} ++ ++void ExecuteOneStretchTest(int32_t dest_width, ++ int32_t src_width, ++ const FXDIB_ResampleOptions& options) { ++ constexpr uint32_t kExpectedSum = CStretchEngine::kFixedPointOne; ++ CStretchEngine::WeightTable table; ++ ASSERT_TRUE(table.CalculateWeights(dest_width, 0, dest_width, src_width, 0, ++ src_width, options)); ++ for (int32_t i = 0; i < dest_width; ++i) { ++ EXPECT_EQ(kExpectedSum, PixelWeightSum(table.GetPixelWeight(i))) ++ << "for { " << src_width << ", " << dest_width << " } at " << i; ++ } ++} ++ ++void ExecuteOneReversedStretchTest(int32_t dest_width, ++ int32_t src_width, ++ const FXDIB_ResampleOptions& options) { ++ constexpr uint32_t kExpectedSum = CStretchEngine::kFixedPointOne; ++ CStretchEngine::WeightTable table; ++ ASSERT_TRUE(table.CalculateWeights(-dest_width, 0, dest_width, src_width, 0, ++ src_width, options)); ++ for (int32_t i = 0; i < dest_width; ++i) { ++ EXPECT_EQ(kExpectedSum, PixelWeightSum(table.GetPixelWeight(i))) ++ << "for { " << src_width << ", " << dest_width << " } at " << i ++ << " (reversed)"; ++ } ++} ++ ++void ExecuteStretchTests(const FXDIB_ResampleOptions& options) { ++ // Can't test everything, few random values chosen. ++ constexpr int32_t kDestWidths[] = {1, 2, 337, 512, 808, 2550}; ++ constexpr int32_t kSrcWidths[] = {1, 2, 187, 256, 809, 1110}; ++ for (int32_t src_width : kSrcWidths) { ++ for (int32_t dest_width : kDestWidths) { ++ ExecuteOneStretchTest(dest_width, src_width, options); ++ ExecuteOneReversedStretchTest(dest_width, src_width, options); ++ } ++ } ++} ++ ++} // namespace + + TEST(CStretchEngine, OverflowInCtor) { + FX_RECT clip_rect; +@@ -21,14 +74,79 @@ TEST(CStretchEngine, OverflowInCtor) { + dict_obj->SetNewFor("Width", 71000); + dict_obj->SetNewFor("Height", 12500); + RetainPtr stream = +- pdfium::MakeRetain(nullptr, 0, std::move(dict_obj)); +- auto dib_source = pdfium::MakeRetain(); +- dib_source->Load(nullptr, stream.Get()); +- CStretchEngine engine(nullptr, FXDIB_8bppRgb, 500, 500, clip_rect, dib_source, +- FXDIB_ResampleOptions()); +- EXPECT_TRUE(engine.m_ResampleOptions.bInterpolateBilinear); +- EXPECT_FALSE(engine.m_ResampleOptions.bInterpolateBicubic); +- EXPECT_FALSE(engine.m_ResampleOptions.bHalftone); +- EXPECT_FALSE(engine.m_ResampleOptions.bNoSmoothing); +- EXPECT_FALSE(engine.m_ResampleOptions.bLossy); ++ pdfium::MakeRetain(std::move(dict_obj)); ++ auto dib_source = pdfium::MakeRetain(nullptr, stream); ++ dib_source->Load(); ++ CStretchEngine engine(nullptr, FXDIB_Format::k8bppRgb, 500, 500, clip_rect, ++ dib_source, FXDIB_ResampleOptions()); ++ EXPECT_TRUE(engine.GetResampleOptionsForTest().bInterpolateBilinear); ++ EXPECT_FALSE(engine.GetResampleOptionsForTest().bHalftone); ++ EXPECT_FALSE(engine.GetResampleOptionsForTest().bNoSmoothing); ++ EXPECT_FALSE(engine.GetResampleOptionsForTest().bLossy); ++} ++ ++TEST(CStretchEngine, WeightRounding) { ++ FXDIB_ResampleOptions options; ++ ExecuteStretchTests(options); ++} ++ ++TEST(CStretchEngine, WeightRoundingNoSmoothing) { ++ FXDIB_ResampleOptions options; ++ options.bNoSmoothing = true; ++ ExecuteStretchTests(options); ++} ++ ++TEST(CStretchEngine, WeightRoundingBilinear) { ++ FXDIB_ResampleOptions options; ++ options.bInterpolateBilinear = true; ++ ExecuteStretchTests(options); ++} ++ ++TEST(CStretchEngine, WeightRoundingNoSmoothingBilinear) { ++ FXDIB_ResampleOptions options; ++ options.bNoSmoothing = true; ++ options.bInterpolateBilinear = true; ++ ExecuteStretchTests(options); ++} ++ ++TEST(CStretchEngine, ZeroLengthSrc) { ++ FXDIB_ResampleOptions options; ++ CStretchEngine::WeightTable table; ++ ASSERT_TRUE(table.CalculateWeights(100, 0, 100, 0, 0, 0, options)); ++} ++ ++TEST(CStretchEngine, ZeroLengthSrcNoSmoothing) { ++ FXDIB_ResampleOptions options; ++ options.bNoSmoothing = true; ++ CStretchEngine::WeightTable table; ++ ASSERT_TRUE(table.CalculateWeights(100, 0, 100, 0, 0, 0, options)); ++} ++ ++TEST(CStretchEngine, ZeroLengthSrcBilinear) { ++ FXDIB_ResampleOptions options; ++ options.bInterpolateBilinear = true; ++ CStretchEngine::WeightTable table; ++ ASSERT_TRUE(table.CalculateWeights(100, 0, 100, 0, 0, 0, options)); ++} ++ ++TEST(CStretchEngine, ZeroLengthSrcNoSmoothingBilinear) { ++ FXDIB_ResampleOptions options; ++ options.bNoSmoothing = true; ++ options.bInterpolateBilinear = true; ++ CStretchEngine::WeightTable table; ++ ASSERT_TRUE(table.CalculateWeights(100, 0, 100, 0, 0, 0, options)); ++} ++ ++TEST(CStretchEngine, ZeroLengthDest) { ++ FXDIB_ResampleOptions options; ++ CStretchEngine::WeightTable table; ++ ASSERT_TRUE(table.CalculateWeights(0, 0, 0, 100, 0, 100, options)); ++} ++ ++TEST(CStretchEngine, TooManyWeights) { ++ FXDIB_ResampleOptions options; ++ CStretchEngine::WeightTable table; ++ ASSERT_FALSE(table.CalculateWeights(kTooBigDestLen, 0, kTooBigDestLen, ++ kTooBigSrcLen, 0, kTooBigSrcLen, ++ options)); + } +diff --git a/core/fxge/dib/fx_dib.cpp b/core/fxge/dib/fx_dib.cpp +new file mode 100644 +index 000000000..c8f9833a5 +--- /dev/null ++++ b/core/fxge/dib/fx_dib.cpp +@@ -0,0 +1,60 @@ ++// Copyright 2014 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxge/dib/fx_dib.h" ++ ++#include ++#include ++ ++#include "build/build_config.h" ++ ++#if BUILDFLAG(IS_WIN) ++#include ++#endif ++ ++#if BUILDFLAG(IS_WIN) ++static_assert(sizeof(FX_COLORREF) == sizeof(COLORREF), ++ "FX_COLORREF vs. COLORREF mismatch"); ++#endif ++ ++FXDIB_Format MakeRGBFormat(int bpp) { ++ switch (bpp) { ++ case 1: ++ return FXDIB_Format::k1bppRgb; ++ case 8: ++ return FXDIB_Format::k8bppRgb; ++ case 24: ++ return FXDIB_Format::kRgb; ++ case 32: ++ return FXDIB_Format::kRgb32; ++ default: ++ return FXDIB_Format::kInvalid; ++ } ++} ++ ++FXDIB_ResampleOptions::FXDIB_ResampleOptions() = default; ++ ++bool FXDIB_ResampleOptions::HasAnyOptions() const { ++ return bInterpolateBilinear || bHalftone || bNoSmoothing || bLossy; ++} ++ ++std::tuple ArgbDecode(FX_ARGB argb) { ++ return std::make_tuple(FXARGB_A(argb), FXARGB_R(argb), FXARGB_G(argb), ++ FXARGB_B(argb)); ++} ++ ++std::pair ArgbToAlphaAndColorRef(FX_ARGB argb) { ++ return {FXARGB_A(argb), ArgbToColorRef(argb)}; ++} ++ ++FX_COLORREF ArgbToColorRef(FX_ARGB argb) { ++ return FXSYS_BGR(FXARGB_B(argb), FXARGB_G(argb), FXARGB_R(argb)); ++} ++ ++FX_ARGB AlphaAndColorRefToArgb(int a, FX_COLORREF colorref) { ++ return ArgbEncode(a, FXSYS_GetRValue(colorref), FXSYS_GetGValue(colorref), ++ FXSYS_GetBValue(colorref)); ++} +diff --git a/core/fxge/fx_dib.h b/core/fxge/dib/fx_dib.h +similarity index 67% +rename from core/fxge/fx_dib.h +rename to core/fxge/dib/fx_dib.h +index b2dbe8ab4..bd458f94f 100644 +--- a/core/fxge/fx_dib.h ++++ b/core/fxge/dib/fx_dib.h +@@ -1,58 +1,44 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#ifndef CORE_FXGE_FX_DIB_H_ +-#define CORE_FXGE_FX_DIB_H_ ++#ifndef CORE_FXGE_DIB_FX_DIB_H_ ++#define CORE_FXGE_DIB_FX_DIB_H_ ++ ++#include + + #include + #include + +-#include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/widestring.h" +- +-enum FXDIB_Format { +- FXDIB_Invalid = 0, +- FXDIB_1bppRgb = 0x001, +- FXDIB_8bppRgb = 0x008, +- FXDIB_Rgb = 0x018, +- FXDIB_Rgb32 = 0x020, +- FXDIB_1bppMask = 0x101, +- FXDIB_8bppMask = 0x108, +- FXDIB_8bppRgba = 0x208, +- FXDIB_Rgba = 0x218, +- FXDIB_Argb = 0x220, +- FXDIB_1bppCmyk = 0x401, +- FXDIB_8bppCmyk = 0x408, +- FXDIB_Cmyk = 0x420, +- FXDIB_8bppCmyka = 0x608, +- FXDIB_Cmyka = 0x620, +-}; +- +-struct PixelWeight { +- int m_SrcStart; +- int m_SrcEnd; +- int m_Weights[1]; ++// Encoding: ++// - Bits-per-pixel: value & 0xFF ++// - Is mask: value & 0x100 ++// - Has alpha: value & 0x200 ++enum class FXDIB_Format : uint16_t { ++ kInvalid = 0, ++ k1bppRgb = 0x001, ++ k8bppRgb = 0x008, ++ kRgb = 0x018, ++ kRgb32 = 0x020, ++ k1bppMask = 0x101, ++ k8bppMask = 0x108, ++ kArgb = 0x220, + }; + + using FX_ARGB = uint32_t; ++using FX_CMYK = uint32_t; + + // FX_COLORREF, like win32 COLORREF, is BGR. + using FX_COLORREF = uint32_t; + +-using FX_CMYK = uint32_t; +- +-extern const int16_t SDP_Table[513]; +- + struct FXDIB_ResampleOptions { + FXDIB_ResampleOptions(); + + bool HasAnyOptions() const; + + bool bInterpolateBilinear = false; +- bool bInterpolateBicubic = false; + bool bHalftone = false; + bool bNoSmoothing = false; + bool bLossy = false; +@@ -100,34 +86,23 @@ constexpr unsigned int FXSYS_GetUnsignedAlpha(float alpha) { + return static_cast(alpha * 255.f + 0.5f); + } + +-#define FXSYS_GetCValue(cmyk) ((uint8_t)((cmyk) >> 24) & 0xff) +-#define FXSYS_GetMValue(cmyk) ((uint8_t)((cmyk) >> 16) & 0xff) +-#define FXSYS_GetYValue(cmyk) ((uint8_t)((cmyk) >> 8) & 0xff) +-#define FXSYS_GetKValue(cmyk) ((uint8_t)(cmyk)&0xff) +- + // Bits per pixel, not bytes. + inline int GetBppFromFormat(FXDIB_Format format) { +- return format & 0xff; ++ return static_cast(format) & 0xff; + } + + // AKA bytes per pixel, assuming 8-bits per component. + inline int GetCompsFromFormat(FXDIB_Format format) { +- return (format & 0xff) / 8; +-} +- +-inline uint32_t GetAlphaFlagFromFormat(FXDIB_Format format) { +- return (format >> 8) & 0xff; ++ return (static_cast(format) & 0xff) / 8; + } + +-inline bool GetIsAlphaFromFormat(FXDIB_Format format) { +- return format & 0x200; ++inline bool GetIsMaskFromFormat(FXDIB_Format format) { ++ return !!(static_cast(format) & 0x100); + } + +-inline bool GetIsCmykFromFormat(FXDIB_Format format) { +- return format & 0x400; +-} ++FXDIB_Format MakeRGBFormat(int bpp); + +-inline FX_CMYK CmykEncode(int c, int m, int y, int k) { ++constexpr FX_CMYK CmykEncode(uint32_t c, uint32_t m, uint32_t y, uint32_t k) { + return (c << 24) | (m << 16) | (y << 8) | k; + } + +@@ -140,14 +115,12 @@ std::pair ArgbToAlphaAndColorRef(FX_ARGB argb); + // Returns FX_COLORREF. + FX_COLORREF ArgbToColorRef(FX_ARGB argb); + +-constexpr FX_ARGB ArgbEncode(int a, int r, int g, int b) { ++constexpr FX_ARGB ArgbEncode(uint32_t a, uint32_t r, uint32_t g, uint32_t b) { + return (a << 24) | (r << 16) | (g << 8) | b; + } + + FX_ARGB AlphaAndColorRefToArgb(int a, FX_COLORREF colorref); + +-FX_ARGB StringToFXARGB(WideStringView view); +- + #define FXARGB_A(argb) ((uint8_t)((argb) >> 24)) + #define FXARGB_R(argb) ((uint8_t)((argb) >> 16)) + #define FXARGB_G(argb) ((uint8_t)((argb) >> 8)) +@@ -171,7 +144,6 @@ FX_ARGB StringToFXARGB(WideStringView view); + ((uint8_t*)(p))[0] = (uint8_t)((argb) >> 16), \ + ((uint8_t*)(p))[1] = (uint8_t)((argb) >> 8), \ + ((uint8_t*)(p))[2] = (uint8_t)(argb) +-#define FXARGB_TODIB(argb) (argb) + #define FXCMYK_TODIB(cmyk) \ + ((uint8_t)((cmyk) >> 24) | ((uint8_t)((cmyk) >> 16)) << 8 | \ + ((uint8_t)((cmyk) >> 8)) << 16 | ((uint8_t)(cmyk) << 24)) +@@ -179,10 +151,10 @@ FX_ARGB StringToFXARGB(WideStringView view); + ((uint8_t)(argb >> 16) | ((uint8_t)(argb >> 8)) << 8 | \ + ((uint8_t)(argb)) << 16 | ((uint8_t)(argb >> 24) << 24)) + +-FX_RECT FXDIB_SwapClipBox(const FX_RECT& clip, +- int width, +- int height, +- bool bFlipX, +- bool bFlipY); ++inline void ReverseCopy3Bytes(uint8_t* dest, const uint8_t* src) { ++ dest[2] = src[0]; ++ dest[1] = src[1]; ++ dest[0] = src[2]; ++} + +-#endif // CORE_FXGE_FX_DIB_H_ ++#endif // CORE_FXGE_DIB_FX_DIB_H_ +diff --git a/core/fxge/dib/fx_dib_main.cpp b/core/fxge/dib/fx_dib_main.cpp +deleted file mode 100644 +index 31e5919d1..000000000 +--- a/core/fxge/dib/fx_dib_main.cpp ++++ /dev/null +@@ -1,158 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "core/fxge/fx_dib.h" +- +-#include +-#include +- +-#include "build/build_config.h" +-#include "core/fxcrt/fx_extension.h" +- +-#if defined(OS_WIN) +-static_assert(sizeof(FX_COLORREF) == sizeof(COLORREF), +- "FX_COLORREF vs. COLORREF mismatch"); +-#endif +- +-const int16_t SDP_Table[513] = { +- 256, 256, 256, 256, 256, 256, 256, 256, 256, 255, 255, 255, 255, 255, 255, +- 254, 254, 254, 254, 253, 253, 253, 252, 252, 252, 251, 251, 251, 250, 250, +- 249, 249, 249, 248, 248, 247, 247, 246, 246, 245, 244, 244, 243, 243, 242, +- 242, 241, 240, 240, 239, 238, 238, 237, 236, 236, 235, 234, 233, 233, 232, +- 231, 230, 230, 229, 228, 227, 226, 226, 225, 224, 223, 222, 221, 220, 219, +- 218, 218, 217, 216, 215, 214, 213, 212, 211, 210, 209, 208, 207, 206, 205, +- 204, 203, 202, 201, 200, 199, 198, 196, 195, 194, 193, 192, 191, 190, 189, +- 188, 186, 185, 184, 183, 182, 181, 179, 178, 177, 176, 175, 173, 172, 171, +- 170, 169, 167, 166, 165, 164, 162, 161, 160, 159, 157, 156, 155, 154, 152, +- 151, 150, 149, 147, 146, 145, 143, 142, 141, 140, 138, 137, 136, 134, 133, +- 132, 130, 129, 128, 126, 125, 124, 122, 121, 120, 119, 117, 116, 115, 113, +- 112, 111, 109, 108, 107, 105, 104, 103, 101, 100, 99, 97, 96, 95, 93, +- 92, 91, 89, 88, 87, 85, 84, 83, 81, 80, 79, 77, 76, 75, 73, +- 72, 71, 69, 68, 67, 66, 64, 63, 62, 60, 59, 58, 57, 55, 54, +- 53, 52, 50, 49, 48, 47, 45, 44, 43, 42, 40, 39, 38, 37, 36, +- 34, 33, 32, 31, 30, 28, 27, 26, 25, 24, 23, 21, 20, 19, 18, +- 17, 16, 15, 14, 13, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, +- 1, 0, 0, -1, -2, -3, -4, -5, -6, -7, -7, -8, -9, -10, -11, +- -12, -12, -13, -14, -15, -15, -16, -17, -17, -18, -19, -19, -20, -21, -21, +- -22, -22, -23, -24, -24, -25, -25, -26, -26, -27, -27, -27, -28, -28, -29, +- -29, -30, -30, -30, -31, -31, -31, -32, -32, -32, -33, -33, -33, -33, -34, +- -34, -34, -34, -35, -35, -35, -35, -35, -36, -36, -36, -36, -36, -36, -36, +- -36, -36, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, +- -37, -37, -37, -37, -37, -37, -37, -37, -36, -36, -36, -36, -36, -36, -36, +- -36, -36, -35, -35, -35, -35, -35, -35, -34, -34, -34, -34, -34, -33, -33, +- -33, -33, -33, -32, -32, -32, -32, -31, -31, -31, -31, -30, -30, -30, -30, +- -29, -29, -29, -29, -28, -28, -28, -27, -27, -27, -27, -26, -26, -26, -25, +- -25, -25, -24, -24, -24, -23, -23, -23, -22, -22, -22, -22, -21, -21, -21, +- -20, -20, -20, -19, -19, -19, -18, -18, -18, -17, -17, -17, -16, -16, -16, +- -15, -15, -15, -14, -14, -14, -13, -13, -13, -12, -12, -12, -11, -11, -11, +- -10, -10, -10, -9, -9, -9, -9, -8, -8, -8, -7, -7, -7, -7, -6, +- -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, +- -3, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, +-}; +- +-FXDIB_ResampleOptions::FXDIB_ResampleOptions() = default; +- +-bool FXDIB_ResampleOptions::HasAnyOptions() const { +- return bInterpolateBilinear || bInterpolateBicubic || bHalftone || +- bNoSmoothing || bLossy; +-} +- +-FX_RECT FXDIB_SwapClipBox(const FX_RECT& clip, +- int width, +- int height, +- bool bFlipX, +- bool bFlipY) { +- FX_RECT rect; +- if (bFlipY) { +- rect.left = height - clip.top; +- rect.right = height - clip.bottom; +- } else { +- rect.left = clip.top; +- rect.right = clip.bottom; +- } +- if (bFlipX) { +- rect.top = width - clip.left; +- rect.bottom = width - clip.right; +- } else { +- rect.top = clip.left; +- rect.bottom = clip.right; +- } +- rect.Normalize(); +- return rect; +-} +- +-std::tuple ArgbDecode(FX_ARGB argb) { +- return std::make_tuple(FXARGB_A(argb), FXARGB_R(argb), FXARGB_G(argb), +- FXARGB_B(argb)); +-} +- +-std::pair ArgbToAlphaAndColorRef(FX_ARGB argb) { +- return {FXARGB_A(argb), ArgbToColorRef(argb)}; +-} +- +-FX_COLORREF ArgbToColorRef(FX_ARGB argb) { +- return FXSYS_BGR(FXARGB_B(argb), FXARGB_G(argb), FXARGB_R(argb)); +-} +- +-FX_ARGB AlphaAndColorRefToArgb(int a, FX_COLORREF colorref) { +- return ArgbEncode(a, FXSYS_GetRValue(colorref), FXSYS_GetGValue(colorref), +- FXSYS_GetBValue(colorref)); +-} +- +-FX_ARGB StringToFXARGB(WideStringView view) { +- static constexpr FX_ARGB kDefaultValue = 0xff000000; +- if (view.IsEmpty()) +- return kDefaultValue; +- +- int cc = 0; +- const wchar_t* str = view.unterminated_c_str(); +- int len = view.GetLength(); +- while (cc < len && FXSYS_iswspace(str[cc])) +- cc++; +- +- if (cc >= len) +- return kDefaultValue; +- +- uint8_t r = 0; +- uint8_t g = 0; +- uint8_t b = 0; +- while (cc < len) { +- if (str[cc] == ',' || !FXSYS_IsDecimalDigit(str[cc])) +- break; +- +- r = r * 10 + str[cc] - '0'; +- cc++; +- } +- if (cc < len && str[cc] == ',') { +- cc++; +- while (cc < len && FXSYS_iswspace(str[cc])) +- cc++; +- +- while (cc < len) { +- if (str[cc] == ',' || !FXSYS_IsDecimalDigit(str[cc])) +- break; +- +- g = g * 10 + str[cc] - '0'; +- cc++; +- } +- if (cc < len && str[cc] == ',') { +- cc++; +- while (cc < len && FXSYS_iswspace(str[cc])) +- cc++; +- +- while (cc < len) { +- if (str[cc] == ',' || !FXSYS_IsDecimalDigit(str[cc])) +- break; +- +- b = b * 10 + str[cc] - '0'; +- cc++; +- } +- } +- } +- return (0xffU << 24) | (r << 16) | (g << 8) | b; +-} +diff --git a/core/fxge/dib/scanlinecomposer_iface.h b/core/fxge/dib/scanlinecomposer_iface.h +index 316736f63..57baf9b77 100644 +--- a/core/fxge/dib/scanlinecomposer_iface.h ++++ b/core/fxge/dib/scanlinecomposer_iface.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,20 +7,22 @@ + #ifndef CORE_FXGE_DIB_SCANLINECOMPOSER_IFACE_H_ + #define CORE_FXGE_DIB_SCANLINECOMPOSER_IFACE_H_ + +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/span.h" + + class ScanlineComposerIface { + public: + virtual ~ScanlineComposerIface() = default; + + virtual void ComposeScanline(int line, +- const uint8_t* scanline, +- const uint8_t* scan_extra_alpha) = 0; ++ pdfium::span scanline) = 0; + ++ // `src_format` cannot be `FXDIB_Format::k1bppMask` or ++ // `FXDIB_Format::k1bppRgb`. + virtual bool SetInfo(int width, + int height, + FXDIB_Format src_format, +- uint32_t* pSrcPalette) = 0; ++ pdfium::span src_palette) = 0; + }; + + #endif // CORE_FXGE_DIB_SCANLINECOMPOSER_IFACE_H_ +diff --git a/core/fxge/fontdata/chromefontdata/FoxitDingbats.cpp b/core/fxge/fontdata/chromefontdata/FoxitDingbats.cpp +index 75088cef5..3c61db0af 100644 +--- a/core/fxge/fontdata/chromefontdata/FoxitDingbats.cpp ++++ b/core/fxge/fontdata/chromefontdata/FoxitDingbats.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + +-const unsigned char g_FoxitDingbatsFontData[29513] = { ++const unsigned char kFoxitDingbatsFontData[29513] = { + 0x1, 0x0, 0x4, 0x2, 0x0, 0x1, 0x1, 0x1, 0x11, 0x43, 0x68, 0x72, + 0x6f, 0x6d, 0x44, 0x69, 0x6e, 0x67, 0x62, 0x61, 0x74, 0x73, 0x4f, 0x54, + 0x46, 0x0, 0x1, 0x1, 0x1, 0x25, 0xf8, 0x10, 0x0, 0xf8, 0xe4, 0x1, +diff --git a/core/fxge/fontdata/chromefontdata/FoxitFixed.cpp b/core/fxge/fontdata/chromefontdata/FoxitFixed.cpp +index 61c085759..1ec59bf5a 100644 +--- a/core/fxge/fontdata/chromefontdata/FoxitFixed.cpp ++++ b/core/fxge/fontdata/chromefontdata/FoxitFixed.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + +-const unsigned char g_FoxitFixedFontData[17597] = { ++const unsigned char kFoxitFixedFontData[17597] = { + 0x1, 0x0, 0x4, 0x2, 0x0, 0x1, 0x1, 0x1, 0xe, 0x43, 0x68, 0x72, + 0x6f, 0x6d, 0x46, 0x69, 0x78, 0x65, 0x64, 0x4f, 0x54, 0x46, 0x0, 0x1, + 0x1, 0x1, 0x23, 0xf8, 0x10, 0x0, 0xf8, 0x2a, 0x1, 0xf8, 0x2b, 0x2, +diff --git a/core/fxge/fontdata/chromefontdata/FoxitFixedBold.cpp b/core/fxge/fontdata/chromefontdata/FoxitFixedBold.cpp +index c1494f51a..84a67686c 100644 +--- a/core/fxge/fontdata/chromefontdata/FoxitFixedBold.cpp ++++ b/core/fxge/fontdata/chromefontdata/FoxitFixedBold.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + +-const unsigned char g_FoxitFixedBoldFontData[18055] = { ++const unsigned char kFoxitFixedBoldFontData[18055] = { + 0x1, 0x0, 0x4, 0x2, 0x0, 0x1, 0x1, 0x1, 0x13, 0x43, 0x68, 0x72, + 0x6f, 0x6d, 0x46, 0x69, 0x78, 0x65, 0x64, 0x4f, 0x54, 0x46, 0x2d, 0x42, + 0x6f, 0x6c, 0x64, 0x0, 0x1, 0x1, 0x1, 0x24, 0xf8, 0x10, 0x0, 0xf8, +diff --git a/core/fxge/fontdata/chromefontdata/FoxitFixedBoldItalic.cpp b/core/fxge/fontdata/chromefontdata/FoxitFixedBoldItalic.cpp +index c9509e6c6..63da1ce91 100644 +--- a/core/fxge/fontdata/chromefontdata/FoxitFixedBoldItalic.cpp ++++ b/core/fxge/fontdata/chromefontdata/FoxitFixedBoldItalic.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + +-const unsigned char g_FoxitFixedBoldItalicFontData[19151] = { ++const unsigned char kFoxitFixedBoldItalicFontData[19151] = { + 0x1, 0x0, 0x4, 0x2, 0x0, 0x1, 0x1, 0x1, 0x19, 0x43, 0x68, 0x72, + 0x6f, 0x6d, 0x46, 0x69, 0x78, 0x65, 0x64, 0x4f, 0x54, 0x46, 0x2d, 0x42, + 0x6f, 0x6c, 0x64, 0x49, 0x74, 0x61, 0x6c, 0x69, 0x63, 0x0, 0x1, 0x1, +diff --git a/core/fxge/fontdata/chromefontdata/FoxitFixedItalic.cpp b/core/fxge/fontdata/chromefontdata/FoxitFixedItalic.cpp +index 7fe9f63c0..ba5670790 100644 +--- a/core/fxge/fontdata/chromefontdata/FoxitFixedItalic.cpp ++++ b/core/fxge/fontdata/chromefontdata/FoxitFixedItalic.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + +-const unsigned char g_FoxitFixedItalicFontData[18746] = { ++const unsigned char kFoxitFixedItalicFontData[18746] = { + 0x1, 0x0, 0x4, 0x2, 0x0, 0x1, 0x1, 0x1, 0x15, 0x43, 0x68, 0x72, + 0x6f, 0x6d, 0x46, 0x69, 0x78, 0x65, 0x64, 0x4f, 0x54, 0x46, 0x2d, 0x49, + 0x74, 0x61, 0x6c, 0x69, 0x63, 0x0, 0x1, 0x1, 0x1, 0x23, 0xf8, 0x10, +diff --git a/core/fxge/fontdata/chromefontdata/FoxitSans.cpp b/core/fxge/fontdata/chromefontdata/FoxitSans.cpp +index 16da03e54..79d194c0c 100644 +--- a/core/fxge/fontdata/chromefontdata/FoxitSans.cpp ++++ b/core/fxge/fontdata/chromefontdata/FoxitSans.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + +-const unsigned char g_FoxitSansFontData[15025] = { ++const unsigned char kFoxitSansFontData[15025] = { + 0x1, 0x0, 0x4, 0x2, 0x0, 0x1, 0x1, 0x1, 0xd, 0x43, 0x68, 0x72, + 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x4f, 0x54, 0x46, 0x0, 0x1, 0x1, + 0x1, 0x24, 0xf8, 0x10, 0x0, 0xf8, 0x1c, 0x1, 0xf8, 0x1d, 0x2, 0xf8, +diff --git a/core/fxge/fontdata/chromefontdata/FoxitSansBold.cpp b/core/fxge/fontdata/chromefontdata/FoxitSansBold.cpp +index 10a63a53c..5a54eae80 100644 +--- a/core/fxge/fontdata/chromefontdata/FoxitSansBold.cpp ++++ b/core/fxge/fontdata/chromefontdata/FoxitSansBold.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + +-const unsigned char g_FoxitSansBoldFontData[16344] = { ++const unsigned char kFoxitSansBoldFontData[16344] = { + 0x1, 0x0, 0x4, 0x2, 0x0, 0x1, 0x1, 0x1, 0x12, 0x43, 0x68, 0x72, + 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x4f, 0x54, 0x46, 0x2d, 0x42, 0x6f, + 0x6c, 0x64, 0x0, 0x1, 0x1, 0x1, 0x24, 0xf8, 0x10, 0x0, 0xf8, 0x1c, +diff --git a/core/fxge/fontdata/chromefontdata/FoxitSansBoldItalic.cpp b/core/fxge/fontdata/chromefontdata/FoxitSansBoldItalic.cpp +index 054976cce..475f8753f 100644 +--- a/core/fxge/fontdata/chromefontdata/FoxitSansBoldItalic.cpp ++++ b/core/fxge/fontdata/chromefontdata/FoxitSansBoldItalic.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + +-const unsigned char g_FoxitSansBoldItalicFontData[16418] = { ++const unsigned char kFoxitSansBoldItalicFontData[16418] = { + 0x1, 0x0, 0x4, 0x2, 0x0, 0x1, 0x1, 0x1, 0x17, 0x43, 0x68, 0x72, + 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x50, 0x53, 0x2d, 0x42, 0x6f, 0x6c, + 0x64, 0x49, 0x74, 0x61, 0x6c, 0x69, 0x63, 0x0, 0x1, 0x1, 0x1, 0x27, +diff --git a/core/fxge/fontdata/chromefontdata/FoxitSansItalic.cpp b/core/fxge/fontdata/chromefontdata/FoxitSansItalic.cpp +index 2912e1688..5af176d91 100644 +--- a/core/fxge/fontdata/chromefontdata/FoxitSansItalic.cpp ++++ b/core/fxge/fontdata/chromefontdata/FoxitSansItalic.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + +-const unsigned char g_FoxitSansItalicFontData[16339] = { ++const unsigned char kFoxitSansItalicFontData[16339] = { + 0x1, 0x0, 0x4, 0x2, 0x0, 0x1, 0x1, 0x1, 0x14, 0x43, 0x68, 0x72, + 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x4f, 0x54, 0x46, 0x2d, 0x49, 0x74, + 0x61, 0x6c, 0x69, 0x63, 0x0, 0x1, 0x1, 0x1, 0x27, 0xf8, 0x10, 0x0, +diff --git a/core/fxge/fontdata/chromefontdata/FoxitSansMM.cpp b/core/fxge/fontdata/chromefontdata/FoxitSansMM.cpp +index 7f540aef1..985a6bbfe 100644 +--- a/core/fxge/fontdata/chromefontdata/FoxitSansMM.cpp ++++ b/core/fxge/fontdata/chromefontdata/FoxitSansMM.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + +-const unsigned char g_FoxitSansMMFontData[66919] = { ++const unsigned char kFoxitSansMMFontData[66919] = { + 0x80, 0x01, 0xD2, 0x29, 0x00, 0x00, 0x25, 0x21, 0x50, 0x53, 0x2D, 0x41, + 0x64, 0x6F, 0x62, 0x65, 0x46, 0x6F, 0x6E, 0x74, 0x2D, 0x31, 0x2E, 0x30, + 0x3A, 0x20, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x53, 0x61, 0x6E, 0x73, +diff --git a/core/fxge/fontdata/chromefontdata/FoxitSerif.cpp b/core/fxge/fontdata/chromefontdata/FoxitSerif.cpp +index 5fa276de4..9ec872171 100644 +--- a/core/fxge/fontdata/chromefontdata/FoxitSerif.cpp ++++ b/core/fxge/fontdata/chromefontdata/FoxitSerif.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + +-const unsigned char g_FoxitSerifFontData[19469] = { ++const unsigned char kFoxitSerifFontData[19469] = { + 0x1, 0x0, 0x4, 0x2, 0x0, 0x1, 0x1, 0x1, 0xe, 0x43, 0x68, 0x72, + 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x69, 0x66, 0x4f, 0x54, 0x46, 0x0, 0x1, + 0x1, 0x1, 0x26, 0xf8, 0x10, 0x0, 0xf8, 0x1c, 0x1, 0xf8, 0x1d, 0x2, +diff --git a/core/fxge/fontdata/chromefontdata/FoxitSerifBold.cpp b/core/fxge/fontdata/chromefontdata/FoxitSerifBold.cpp +index 287586303..d88648370 100644 +--- a/core/fxge/fontdata/chromefontdata/FoxitSerifBold.cpp ++++ b/core/fxge/fontdata/chromefontdata/FoxitSerifBold.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + +-const unsigned char g_FoxitSerifBoldFontData[19395] = { ++const unsigned char kFoxitSerifBoldFontData[19395] = { + 0x1, 0x0, 0x4, 0x2, 0x0, 0x1, 0x1, 0x1, 0x13, 0x43, 0x68, 0x72, + 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x69, 0x66, 0x4f, 0x54, 0x46, 0x2d, 0x42, + 0x6f, 0x6c, 0x64, 0x0, 0x1, 0x1, 0x1, 0x26, 0xf8, 0x10, 0x0, 0xf8, +diff --git a/core/fxge/fontdata/chromefontdata/FoxitSerifBoldItalic.cpp b/core/fxge/fontdata/chromefontdata/FoxitSerifBoldItalic.cpp +index 159e53524..724537ad9 100644 +--- a/core/fxge/fontdata/chromefontdata/FoxitSerifBoldItalic.cpp ++++ b/core/fxge/fontdata/chromefontdata/FoxitSerifBoldItalic.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + +-const unsigned char g_FoxitSerifBoldItalicFontData[20733] = { ++const unsigned char kFoxitSerifBoldItalicFontData[20733] = { + 0x1, 0x0, 0x4, 0x2, 0x0, 0x1, 0x1, 0x1, 0x19, 0x43, 0x68, 0x72, + 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x69, 0x66, 0x4f, 0x54, 0x46, 0x2d, 0x42, + 0x6f, 0x6c, 0x64, 0x49, 0x74, 0x61, 0x6c, 0x69, 0x63, 0x0, 0x1, 0x1, +diff --git a/core/fxge/fontdata/chromefontdata/FoxitSerifItalic.cpp b/core/fxge/fontdata/chromefontdata/FoxitSerifItalic.cpp +index eec32d71d..8fcda855d 100644 +--- a/core/fxge/fontdata/chromefontdata/FoxitSerifItalic.cpp ++++ b/core/fxge/fontdata/chromefontdata/FoxitSerifItalic.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + +-const unsigned char g_FoxitSerifItalicFontData[21227] = { ++const unsigned char kFoxitSerifItalicFontData[21227] = { + 0x1, 0x0, 0x4, 0x2, 0x0, 0x1, 0x1, 0x1, 0x15, 0x43, 0x68, 0x72, + 0x6f, 0x6d, 0x53, 0x65, 0x72, 0x69, 0x66, 0x4f, 0x54, 0x46, 0x2d, 0x49, + 0x74, 0x61, 0x6c, 0x69, 0x63, 0x0, 0x1, 0x1, 0x1, 0x2a, 0xf8, 0x10, +diff --git a/core/fxge/fontdata/chromefontdata/FoxitSerifMM.cpp b/core/fxge/fontdata/chromefontdata/FoxitSerifMM.cpp +index 4195e9cb4..6389782ef 100644 +--- a/core/fxge/fontdata/chromefontdata/FoxitSerifMM.cpp ++++ b/core/fxge/fontdata/chromefontdata/FoxitSerifMM.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + +-const unsigned char g_FoxitSerifMMFontData[113417] = { ++const unsigned char kFoxitSerifMMFontData[113417] = { + 0x80, 0x01, 0xD6, 0x29, 0x00, 0x00, 0x25, 0x21, 0x50, 0x53, 0x2D, 0x41, + 0x64, 0x6F, 0x62, 0x65, 0x46, 0x6F, 0x6E, 0x74, 0x2D, 0x31, 0x2E, 0x30, + 0x3A, 0x20, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, +diff --git a/core/fxge/fontdata/chromefontdata/FoxitSymbol.cpp b/core/fxge/fontdata/chromefontdata/FoxitSymbol.cpp +index 37ce309f5..2a460e6f0 100644 +--- a/core/fxge/fontdata/chromefontdata/FoxitSymbol.cpp ++++ b/core/fxge/fontdata/chromefontdata/FoxitSymbol.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "core/fxge/fontdata/chromefontdata/chromefontdata.h" + +-const unsigned char g_FoxitSymbolFontData[16729] = { ++const unsigned char kFoxitSymbolFontData[16729] = { + 0x1, 0x0, 0x4, 0x2, 0x0, 0x1, 0x1, 0x1, 0xf, 0x43, 0x68, 0x72, + 0x6f, 0x6d, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x4f, 0x54, 0x46, 0x0, + 0x1, 0x1, 0x1, 0x26, 0xf8, 0x10, 0x0, 0xf8, 0xad, 0x1, 0xf8, 0xae, +diff --git a/core/fxge/fontdata/chromefontdata/chromefontdata.h b/core/fxge/fontdata/chromefontdata/chromefontdata.h +index cbd1ad2d6..7d6fb7825 100644 +--- a/core/fxge/fontdata/chromefontdata/chromefontdata.h ++++ b/core/fxge/fontdata/chromefontdata/chromefontdata.h +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,21 +7,21 @@ + #ifndef CORE_FXGE_FONTDATA_CHROMEFONTDATA_CHROMEFONTDATA_H_ + #define CORE_FXGE_FONTDATA_CHROMEFONTDATA_CHROMEFONTDATA_H_ + +-extern const unsigned char g_FoxitFixedItalicFontData[18746]; +-extern const unsigned char g_FoxitFixedFontData[17597]; +-extern const unsigned char g_FoxitSansItalicFontData[16339]; +-extern const unsigned char g_FoxitSansFontData[15025]; +-extern const unsigned char g_FoxitSerifItalicFontData[21227]; +-extern const unsigned char g_FoxitSerifFontData[19469]; +-extern const unsigned char g_FoxitFixedBoldItalicFontData[19151]; +-extern const unsigned char g_FoxitFixedBoldFontData[18055]; +-extern const unsigned char g_FoxitSansBoldItalicFontData[16418]; +-extern const unsigned char g_FoxitSansBoldFontData[16344]; +-extern const unsigned char g_FoxitSerifBoldItalicFontData[20733]; +-extern const unsigned char g_FoxitSerifBoldFontData[19395]; +-extern const unsigned char g_FoxitSymbolFontData[16729]; +-extern const unsigned char g_FoxitDingbatsFontData[29513]; +-extern const unsigned char g_FoxitSerifMMFontData[113417]; +-extern const unsigned char g_FoxitSansMMFontData[66919]; ++extern const unsigned char kFoxitFixedItalicFontData[18746]; ++extern const unsigned char kFoxitFixedFontData[17597]; ++extern const unsigned char kFoxitSansItalicFontData[16339]; ++extern const unsigned char kFoxitSansFontData[15025]; ++extern const unsigned char kFoxitSerifItalicFontData[21227]; ++extern const unsigned char kFoxitSerifFontData[19469]; ++extern const unsigned char kFoxitFixedBoldItalicFontData[19151]; ++extern const unsigned char kFoxitFixedBoldFontData[18055]; ++extern const unsigned char kFoxitSansBoldItalicFontData[16418]; ++extern const unsigned char kFoxitSansBoldFontData[16344]; ++extern const unsigned char kFoxitSerifBoldItalicFontData[20733]; ++extern const unsigned char kFoxitSerifBoldFontData[19395]; ++extern const unsigned char kFoxitSymbolFontData[16729]; ++extern const unsigned char kFoxitDingbatsFontData[29513]; ++extern const unsigned char kFoxitSerifMMFontData[113417]; ++extern const unsigned char kFoxitSansMMFontData[66919]; + + #endif // CORE_FXGE_FONTDATA_CHROMEFONTDATA_CHROMEFONTDATA_H_ +diff --git a/core/fxge/freetype/fx_freetype.cpp b/core/fxge/freetype/fx_freetype.cpp +index fb1c29a63..1c3deeebe 100644 +--- a/core/fxge/freetype/fx_freetype.cpp ++++ b/core/fxge/freetype/fx_freetype.cpp +@@ -1,22 +1,26 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#include "core/fxge/fx_freetype.h" ++#include "core/fxge/freetype/fx_freetype.h" ++ ++#include + + #define DEFINE_PS_TABLES + #include "third_party/freetype/include/pstables.h" + +-static int xyq_search_node(char* glyph_name, +- int name_offset, +- int table_offset, +- wchar_t unicode) { +- int i, count; ++namespace { ++ ++constexpr uint32_t kVariantBit = 0x80000000; + ++int xyq_search_node(char* glyph_name, ++ int name_offset, ++ int table_offset, ++ wchar_t unicode) { + // copy letters +- while (1) { ++ while (true) { + glyph_name[name_offset] = ft_adobe_glyph_list[table_offset] & 0x7f; + name_offset++; + table_offset++; +@@ -26,7 +30,7 @@ static int xyq_search_node(char* glyph_name, + glyph_name[name_offset] = 0; + + // get child count +- count = ft_adobe_glyph_list[table_offset] & 0x7f; ++ int count = ft_adobe_glyph_list[table_offset] & 0x7f; + + // check if we have value for this node + if (ft_adobe_glyph_list[table_offset] & 0x80) { +@@ -42,7 +46,8 @@ static int xyq_search_node(char* glyph_name, + // now search in sub-nodes + if (count == 0) + return 0; +- for (i = 0; i < count; i++) { ++ ++ for (int i = 0; i < count; i++) { + int child_offset = ft_adobe_glyph_list[table_offset + i * 2] * 256 + + ft_adobe_glyph_list[table_offset + i * 2 + 1]; + if (xyq_search_node(glyph_name, name_offset, child_offset, unicode)) +@@ -52,7 +57,7 @@ static int xyq_search_node(char* glyph_name, + return 0; + } + +-#define VARIANT_BIT 0x80000000UL ++} // namespace + + int FXFT_unicode_from_adobe_name(const char* glyph_name) { + /* If the name begins with `uni', then the glyph name may be a */ +@@ -70,9 +75,7 @@ int FXFT_unicode_from_adobe_name(const char* glyph_name) { + + for (count = 4; count > 0; count--, p++) { + char c = *p; +- unsigned int d; +- +- d = (unsigned char)c - '0'; ++ unsigned int d = (unsigned char)c - '0'; + if (d >= 10) { + d = (unsigned char)c - 'A'; + if (d >= 6) +@@ -95,7 +98,7 @@ int FXFT_unicode_from_adobe_name(const char* glyph_name) { + if (*p == '\0') + return value; + if (*p == '.') +- return (FT_UInt32)(value | VARIANT_BIT); ++ return (FT_UInt32)(value | kVariantBit); + } + } + +@@ -108,9 +111,7 @@ int FXFT_unicode_from_adobe_name(const char* glyph_name) { + + for (count = 6; count > 0; count--, p++) { + char c = *p; +- unsigned int d; +- +- d = (unsigned char)c - '0'; ++ unsigned int d = (unsigned char)c - '0'; + if (d >= 10) { + d = (unsigned char)c - 'A'; + if (d >= 6) +@@ -129,7 +130,7 @@ int FXFT_unicode_from_adobe_name(const char* glyph_name) { + if (*p == '\0') + return value; + if (*p == '.') +- return (FT_UInt32)(value | VARIANT_BIT); ++ return (FT_UInt32)(value | kVariantBit); + } + } + +@@ -149,18 +150,15 @@ int FXFT_unicode_from_adobe_name(const char* glyph_name) { + /* now look up the glyph in the Adobe Glyph List */ + if (!dot) + return (FT_UInt32)ft_get_adobe_glyph_index(glyph_name, p); +- else +- return (FT_UInt32)(ft_get_adobe_glyph_index(glyph_name, dot) | +- VARIANT_BIT); ++ ++ return (FT_UInt32)(ft_get_adobe_glyph_index(glyph_name, dot) | kVariantBit); + } + } + + void FXFT_adobe_name_from_unicode(char* glyph_name, wchar_t unicode) { +- int i, count; +- + // start from top level node +- count = ft_adobe_glyph_list[1]; +- for (i = 0; i < count; i++) { ++ int count = ft_adobe_glyph_list[1]; ++ for (int i = 0; i < count; i++) { + int child_offset = + ft_adobe_glyph_list[i * 2 + 2] * 256 + ft_adobe_glyph_list[i * 2 + 3]; + if (xyq_search_node(glyph_name, 0, child_offset, unicode)) +diff --git a/core/fxge/fx_freetype.h b/core/fxge/freetype/fx_freetype.h +similarity index 85% +rename from core/fxge/fx_freetype.h +rename to core/fxge/freetype/fx_freetype.h +index 2dd379a06..3da0372ef 100644 +--- a/core/fxge/fx_freetype.h ++++ b/core/fxge/freetype/fx_freetype.h +@@ -1,11 +1,11 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#ifndef CORE_FXGE_FX_FREETYPE_H_ +-#define CORE_FXGE_FX_FREETYPE_H_ ++#ifndef CORE_FXGE_FREETYPE_FX_FREETYPE_H_ ++#define CORE_FXGE_FREETYPE_FX_FREETYPE_H_ + + #include + +@@ -24,17 +24,11 @@ using FXFT_StreamRec = struct FT_StreamRec_; + using FXFT_MM_VarPtr = FT_MM_Var*; + + struct FXFTFaceRecDeleter { +- inline void operator()(FXFT_FaceRec* pRec) { +- if (pRec) +- FT_Done_Face(pRec); +- } ++ inline void operator()(FXFT_FaceRec* pRec) { FT_Done_Face(pRec); } + }; + + struct FXFTLibraryRecDeleter { +- inline void operator()(FXFT_LibraryRec* pRec) { +- if (pRec) +- FT_Done_FreeType(pRec); +- } ++ inline void operator()(FXFT_LibraryRec* pRec) { FT_Done_FreeType(pRec); } + }; + + using ScopedFXFTFaceRec = std::unique_ptr; +@@ -43,9 +37,6 @@ using ScopedFXFTLibraryRec = + + #define FXFT_Select_Charmap(face, encoding) \ + FT_Select_Charmap(face, static_cast(encoding)) +-#define FXFT_Get_Name_Index(face, name) \ +- FT_Get_Name_Index(face, const_cast(name)) +-#define FXFT_Get_Glyph_Outline(face) &((face)->glyph->outline) + #define FXFT_Render_Glyph(face, mode) \ + FT_Render_Glyph((face)->glyph, static_cast(mode)) + +@@ -65,14 +56,11 @@ using ScopedFXFTLibraryRec = + #define FXFT_Get_Face_Style_Name(face) (face)->style_name + #define FXFT_Is_Face_Italic(face) (((face)->style_flags) & FT_STYLE_FLAG_ITALIC) + #define FXFT_Is_Face_Bold(face) (((face)->style_flags) & FT_STYLE_FLAG_BOLD) +-#define FXFT_Get_Face_Charmaps(face) (face)->charmaps + #define FXFT_Get_Glyph_HoriBearingX(face) (face)->glyph->metrics.horiBearingX + #define FXFT_Get_Glyph_HoriBearingY(face) (face)->glyph->metrics.horiBearingY + #define FXFT_Get_Glyph_Width(face) (face)->glyph->metrics.width + #define FXFT_Get_Glyph_Height(face) (face)->glyph->metrics.height +-#define FXFT_Get_Face_CharmapCount(face) (face)->num_charmaps + #define FXFT_Get_Charmap_Encoding(charmap) (charmap)->encoding +-#define FXFT_Get_Face_Charmap(face) (face)->charmap + #define FXFT_Get_Charmap_PlatformID(charmap) (charmap)->platform_id + #define FXFT_Get_Charmap_EncodingID(charmap) (charmap)->encoding_id + #define FXFT_Get_Face_UnitsPerEM(face) (face)->units_per_EM +@@ -102,4 +90,4 @@ using ScopedFXFTLibraryRec = + int FXFT_unicode_from_adobe_name(const char* glyph_name); + void FXFT_adobe_name_from_unicode(char* name, wchar_t unicode); + +-#endif // CORE_FXGE_FX_FREETYPE_H_ ++#endif // CORE_FXGE_FREETYPE_FX_FREETYPE_H_ +diff --git a/core/fxge/fx_font.cpp b/core/fxge/fx_font.cpp +index fe2d6b184..a3bf3449f 100644 +--- a/core/fxge/fx_font.cpp ++++ b/core/fxge/fx_font.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,16 +7,17 @@ + #include + + #include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/widestring.h" + #include "core/fxge/cfx_glyphbitmap.h" + #include "core/fxge/dib/cfx_dibitmap.h" ++#include "core/fxge/freetype/fx_freetype.h" + #include "core/fxge/text_glyph_pos.h" + + namespace { + + // These numbers come from the OpenType name table specification. +-constexpr uint16_t kNamePlatformMac = 1; + constexpr uint16_t kNameMacEncodingRoman = 0; +-constexpr uint16_t kNamePlatformWindows = 3; + constexpr uint16_t kNameWindowsEncodingUnicode = 1; + + ByteString GetStringFromTable(pdfium::span string_span, +@@ -38,7 +39,7 @@ FX_RECT GetGlyphsBBox(const std::vector& glyphs, int anti_alias) { + if (!glyph.m_pGlyph) + continue; + +- Optional point = glyph.GetOrigin({0, 0}); ++ absl::optional point = glyph.GetOrigin({0, 0}); + if (!point.has_value()) + continue; + +@@ -80,8 +81,8 @@ ByteString GetNameFromTT(pdfium::span name_table, + if (name_table.size() < 6) + return ByteString(); + +- uint32_t name_count = GET_TT_SHORT(&name_table[2]); +- uint32_t string_offset = GET_TT_SHORT(&name_table[4]); ++ uint32_t name_count = FXSYS_UINT16_GET_MSBFIRST(&name_table[2]); ++ uint32_t string_offset = FXSYS_UINT16_GET_MSBFIRST(&name_table[4]); + // We will ignore the possibility of overlap of structures and + // string table as if it's all corrupt there's not a lot we can do. + if (name_table.size() < string_offset) +@@ -94,21 +95,24 @@ ByteString GetNameFromTT(pdfium::span name_table, + + for (uint32_t i = 0; i < name_count; + i++, name_table = name_table.subspan(12)) { +- if (GET_TT_SHORT(&name_table[6]) == name_id) { +- const uint16_t platform_identifier = GET_TT_SHORT(name_table); +- const uint16_t platform_encoding = GET_TT_SHORT(&name_table[2]); ++ if (FXSYS_UINT16_GET_MSBFIRST(&name_table[6]) == name_id) { ++ const uint16_t platform_identifier = ++ FXSYS_UINT16_GET_MSBFIRST(name_table); ++ const uint16_t platform_encoding = ++ FXSYS_UINT16_GET_MSBFIRST(&name_table[2]); + + if (platform_identifier == kNamePlatformMac && + platform_encoding == kNameMacEncodingRoman) { +- return GetStringFromTable(string_span, GET_TT_SHORT(&name_table[10]), +- GET_TT_SHORT(&name_table[8])); ++ return GetStringFromTable(string_span, ++ FXSYS_UINT16_GET_MSBFIRST(&name_table[10]), ++ FXSYS_UINT16_GET_MSBFIRST(&name_table[8])); + } + if (platform_identifier == kNamePlatformWindows && + platform_encoding == kNameWindowsEncodingUnicode) { + // This name is always UTF16-BE and we have to convert it to UTF8. +- ByteString utf16_be = +- GetStringFromTable(string_span, GET_TT_SHORT(&name_table[10]), +- GET_TT_SHORT(&name_table[8])); ++ ByteString utf16_be = GetStringFromTable( ++ string_span, FXSYS_UINT16_GET_MSBFIRST(&name_table[10]), ++ FXSYS_UINT16_GET_MSBFIRST(&name_table[8])); + if (utf16_be.IsEmpty() || utf16_be.GetLength() % 2 != 0) { + return ByteString(); + } +@@ -124,23 +128,22 @@ ByteString GetNameFromTT(pdfium::span name_table, + return ByteString(); + } + +-int GetTTCIndex(pdfium::span pFontData, uint32_t font_offset) { +- const uint8_t* p = pFontData.data() + 8; +- uint32_t nfont = GET_TT_LONG(p); +- uint32_t index; +- for (index = 0; index < nfont; index++) { +- p = pFontData.data() + 12 + index * 4; +- if (GET_TT_LONG(p) == font_offset) ++size_t GetTTCIndex(pdfium::span pFontData, size_t font_offset) { ++ pdfium::span p = pFontData.subspan(8); ++ size_t nfont = FXSYS_UINT32_GET_MSBFIRST(p.data()); ++ for (size_t index = 0; index < nfont; index++) { ++ p = pFontData.subspan(12 + index * 4); ++ if (FXSYS_UINT32_GET_MSBFIRST(p.data()) == font_offset) + return index; + } + return 0; + } + +-wchar_t PDF_UnicodeFromAdobeName(const char* name) { ++wchar_t UnicodeFromAdobeName(const char* name) { + return (wchar_t)(FXFT_unicode_from_adobe_name(name) & 0x7FFFFFFF); + } + +-ByteString PDF_AdobeNameFromUnicode(wchar_t unicode) { ++ByteString AdobeNameFromUnicode(wchar_t unicode) { + char glyph_name[64]; + FXFT_adobe_name_from_unicode(glyph_name, unicode); + return ByteString(glyph_name); +diff --git a/core/fxge/fx_font.h b/core/fxge/fx_font.h +index b748d55fd..60a27ed7a 100644 +--- a/core/fxge/fx_font.h ++++ b/core/fxge/fx_font.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,10 +9,8 @@ + + #include + ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" +-#include "core/fxge/fx_freetype.h" + #include "third_party/base/span.h" + + /* Font pitch and family flags */ +@@ -39,13 +37,13 @@ + + /* Other font flags */ + #define FXFONT_USEEXTERNATTR 0x80000 +-#define FXFONT_CIDFONT 0x100000 + +-#define GET_TT_SHORT(w) (uint16_t)(((w)[0] << 8) | (w)[1]) +-#define GET_TT_LONG(w) \ +- (uint32_t)(((w)[0] << 24) | ((w)[1] << 16) | ((w)[2] << 8) | (w)[3]) ++// These numbers come from the OpenType name table specification. ++constexpr uint16_t kNamePlatformAppleUnicode = 0; ++constexpr uint16_t kNamePlatformMac = 1; ++constexpr uint16_t kNamePlatformWindows = 3; + +-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ ++#if defined(_SKIA_SUPPORT_) + class SkTypeface; + + using CFX_TypeFace = SkTypeface; +@@ -56,7 +54,7 @@ class TextGlyphPos; + FX_RECT GetGlyphsBBox(const std::vector& glyphs, int anti_alias); + + ByteString GetNameFromTT(pdfium::span name_table, uint32_t name); +-int GetTTCIndex(pdfium::span pFontData, uint32_t font_offset); ++size_t GetTTCIndex(pdfium::span pFontData, size_t font_offset); + + inline bool FontStyleIsForceBold(uint32_t style) { + return !!(style & FXFONT_FORCE_BOLD); +@@ -93,7 +91,7 @@ inline bool FontFamilyIsScript(int32_t family) { + return !!(family & FXFONT_FF_SCRIPT); + } + +-wchar_t PDF_UnicodeFromAdobeName(const char* name); +-ByteString PDF_AdobeNameFromUnicode(wchar_t unicode); ++wchar_t UnicodeFromAdobeName(const char* name); ++ByteString AdobeNameFromUnicode(wchar_t unicode); + + #endif // CORE_FXGE_FX_FONT_H_ +diff --git a/core/fxge/fx_font_unittest.cpp b/core/fxge/fx_font_unittest.cpp +index e550f0a36..cc795d50d 100644 +--- a/core/fxge/fx_font_unittest.cpp ++++ b/core/fxge/fx_font_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,25 +6,36 @@ + + #include "core/fxge/cfx_folderfontinfo.h" + #include "core/fxge/cfx_fontmapper.h" ++#include "core/fxge/cfx_gemodule.h" + #include "core/fxge/fx_font.h" +- + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/utils/path_service.h" ++#include "third_party/base/check.h" ++ ++TEST(FXFontTest, UnicodeFromAdobeName) { ++ EXPECT_EQ(static_cast(0x0000), UnicodeFromAdobeName("nonesuch")); ++ EXPECT_EQ(static_cast(0x0000), UnicodeFromAdobeName("")); ++ EXPECT_EQ(static_cast(0x00b6), UnicodeFromAdobeName("paragraph")); ++ EXPECT_EQ(static_cast(0x00d3), UnicodeFromAdobeName("Oacute")); ++ EXPECT_EQ(static_cast(0x00fe), UnicodeFromAdobeName("thorn")); ++ EXPECT_EQ(static_cast(0x0384), UnicodeFromAdobeName("tonos")); ++ EXPECT_EQ(static_cast(0x2022), UnicodeFromAdobeName("bullet")); ++} + +-TEST(FXFontTest, PDF_AdobeNameFromUnicode) { +- EXPECT_STREQ("", PDF_AdobeNameFromUnicode(0x0000).c_str()); +- EXPECT_STREQ("divide", PDF_AdobeNameFromUnicode(0x00f7).c_str()); +- EXPECT_STREQ("Lslash", PDF_AdobeNameFromUnicode(0x0141).c_str()); +- EXPECT_STREQ("tonos", PDF_AdobeNameFromUnicode(0x0384).c_str()); +- EXPECT_STREQ("afii57513", PDF_AdobeNameFromUnicode(0x0691).c_str()); +- EXPECT_STREQ("angkhankhuthai", PDF_AdobeNameFromUnicode(0x0e5a).c_str()); +- EXPECT_STREQ("Euro", PDF_AdobeNameFromUnicode(0x20ac).c_str()); ++TEST(FXFontTest, AdobeNameFromUnicode) { ++ EXPECT_STREQ("", AdobeNameFromUnicode(0x0000).c_str()); ++ EXPECT_STREQ("divide", AdobeNameFromUnicode(0x00f7).c_str()); ++ EXPECT_STREQ("Lslash", AdobeNameFromUnicode(0x0141).c_str()); ++ EXPECT_STREQ("tonos", AdobeNameFromUnicode(0x0384).c_str()); ++ EXPECT_STREQ("afii57513", AdobeNameFromUnicode(0x0691).c_str()); ++ EXPECT_STREQ("angkhankhuthai", AdobeNameFromUnicode(0x0e5a).c_str()); ++ EXPECT_STREQ("Euro", AdobeNameFromUnicode(0x20ac).c_str()); + } + + TEST(FXFontTest, ReadFontNameFromMicrosoftEntries) { + std::string test_data_dir; + PathService::GetTestDataDir(&test_data_dir); +- ASSERT(!test_data_dir.empty()); ++ DCHECK(!test_data_dir.empty()); + + CFX_FontMapper font_mapper(nullptr); + +@@ -35,10 +46,11 @@ TEST(FXFontTest, ReadFontNameFromMicrosoftEntries) { + folder_font_info.AddPath( + (test_data_dir + PATH_SEPARATOR + "font_tests").c_str()); + +- font_mapper.SetSystemFontInfo(SystemFontInfoIface::CreateDefault(nullptr)); ++ font_mapper.SetSystemFontInfo( ++ CFX_GEModule::Get()->GetPlatform()->CreateDefaultSystemFontInfo()); + ASSERT_TRUE(folder_font_info.EnumFontList(&font_mapper)); + } + +- ASSERT_EQ(1, font_mapper.GetFaceSize()); ++ ASSERT_EQ(1u, font_mapper.GetFaceSize()); + ASSERT_EQ("Test", font_mapper.GetFaceName(0)); + } +diff --git a/core/fxge/fx_ge_fontmap.cpp b/core/fxge/fx_ge_fontmap.cpp +deleted file mode 100644 +index c99d08dc3..000000000 +--- a/core/fxge/fx_ge_fontmap.cpp ++++ /dev/null +@@ -1,21 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include +- +-#include "build/build_config.h" +-#include "core/fxge/systemfontinfo_iface.h" +- +-int SystemFontInfoIface::GetFaceIndex(void* hFont) { +- return 0; +-} +- +-#if defined(OS_ANDROID) +-std::unique_ptr SystemFontInfoIface::CreateDefault( +- const char** pUnused) { +- return nullptr; +-} +-#endif +diff --git a/core/fxge/fx_ge_text_embeddertest.cpp b/core/fxge/fx_ge_text_embeddertest.cpp +index e47ed5f7c..9a79620a7 100644 +--- a/core/fxge/fx_ge_text_embeddertest.cpp ++++ b/core/fxge/fx_ge_text_embeddertest.cpp +@@ -1,18 +1,28 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include +- + #include "public/cpp/fpdf_scopers.h" + #include "testing/embedder_test.h" ++#include "testing/embedder_test_environment.h" + #include "testing/gtest/include/gtest/gtest.h" + +-class FXGETextEmbedderTest : public EmbedderTest {}; ++class FXGETextEmbedderTest : public EmbedderTest { ++ public: ++ void TearDown() override { ++ EmbedderTest::TearDown(); ++ ++ // TODO(tsepez): determine how this is changing the environment, ++ // such that FPDFAnnotEmbedderTest.BUG_1206 will diff if run ++ // after this. ++ EmbedderTestEnvironment::GetInstance()->TearDown(); ++ EmbedderTestEnvironment::GetInstance()->SetUp(); ++ } ++}; + + TEST_F(FXGETextEmbedderTest, BadItalic) { + // Shouldn't crash. +- EXPECT_TRUE(OpenDocument("bug_601362.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_601362.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +diff --git a/core/fxge/fx_ge_linux.cpp b/core/fxge/linux/fx_linux_impl.cpp +similarity index 61% +rename from core/fxge/fx_ge_linux.cpp +rename to core/fxge/linux/fx_linux_impl.cpp +index d76d39d1b..c24043c2b 100644 +--- a/core/fxge/fx_ge_linux.cpp ++++ b/core/fxge/linux/fx_linux_impl.cpp +@@ -1,21 +1,27 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + ++#include + #include + #include + ++#include "build/build_config.h" + #include "core/fxcrt/fx_codepage.h" + #include "core/fxge/cfx_folderfontinfo.h" + #include "core/fxge/cfx_fontmgr.h" + #include "core/fxge/cfx_gemodule.h" + #include "core/fxge/fx_font.h" + #include "core/fxge/systemfontinfo_iface.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" ++ ++#if !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS) && !defined(OS_FUCHSIA) && \ ++ !defined(OS_ASMJS) ++#error "Included on the wrong platform" ++#endif + +-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_ + namespace { + + enum JpFontFamily : uint8_t { +@@ -26,33 +32,32 @@ enum JpFontFamily : uint8_t { + kCount + }; + +-const char* const g_LinuxJpFontList[][JpFontFamily::kCount] = { ++const char* const kLinuxJpFontList[][JpFontFamily::kCount] = { + {"TakaoPGothic", "VL PGothic", "IPAPGothic", "VL Gothic"}, + {"TakaoGothic", "VL Gothic", "IPAGothic", "Kochi Gothic"}, + {"TakaoPMincho", "IPAPMincho", "VL Gothic", "Kochi Mincho"}, + {"TakaoMincho", "IPAMincho", "VL Gothic", "Kochi Mincho"}, + }; + +-const char* const g_LinuxGbFontList[] = { ++const char* const kLinuxGbFontList[] = { + "AR PL UMing CN Light", + "WenQuanYi Micro Hei", + "AR PL UKai CN", + }; + +-const char* const g_LinuxB5FontList[] = { ++const char* const kLinuxB5FontList[] = { + "AR PL UMing TW Light", + "WenQuanYi Micro Hei", + "AR PL UKai TW", + }; + +-const char* const g_LinuxHGFontList[] = { ++const char* const kLinuxHGFontList[] = { + "UnDotum", + }; + +-uint8_t GetJapanesePreference(const char* facearr, +- int weight, +- int pitch_family) { +- ByteString face = facearr; ++JpFontFamily GetJapanesePreference(const ByteString& face, ++ int weight, ++ int pitch_family) { + if (face.Contains("Gothic") || + face.Contains("\x83\x53\x83\x56\x83\x62\x83\x4e")) { + if (face.Contains("PGothic") || +@@ -78,55 +83,55 @@ class CFX_LinuxFontInfo final : public CFX_FolderFontInfo { + CFX_LinuxFontInfo() = default; + ~CFX_LinuxFontInfo() override = default; + +- // CFX_LinuxFontInfo: ++ // CFX_FolderFontInfo: + void* MapFont(int weight, + bool bItalic, +- int charset, ++ FX_Charset charset, + int pitch_family, +- const char* family) override; ++ const ByteString& face) override; + + bool ParseFontCfg(const char** pUserPaths); + }; + + void* CFX_LinuxFontInfo::MapFont(int weight, + bool bItalic, +- int charset, ++ FX_Charset charset, + int pitch_family, +- const char* family) { +- void* font = GetSubstFont(family); ++ const ByteString& face) { ++ void* font = GetSubstFont(face); + if (font) + return font; + + bool bCJK = true; + switch (charset) { +- case FX_CHARSET_ShiftJIS: { +- uint8_t index = GetJapanesePreference(family, weight, pitch_family); +- ASSERT(index < FX_ArraySize(g_LinuxJpFontList)); +- for (const char* name : g_LinuxJpFontList[index]) { ++ case FX_Charset::kShiftJIS: { ++ JpFontFamily index = GetJapanesePreference(face, weight, pitch_family); ++ DCHECK(index < std::size(kLinuxJpFontList)); ++ for (const char* name : kLinuxJpFontList[index]) { + auto it = m_FontList.find(name); + if (it != m_FontList.end()) + return it->second.get(); + } + break; + } +- case FX_CHARSET_ChineseSimplified: { +- for (const char* name : g_LinuxGbFontList) { ++ case FX_Charset::kChineseSimplified: { ++ for (const char* name : kLinuxGbFontList) { + auto it = m_FontList.find(name); + if (it != m_FontList.end()) + return it->second.get(); + } + break; + } +- case FX_CHARSET_ChineseTraditional: { +- for (const char* name : g_LinuxB5FontList) { ++ case FX_Charset::kChineseTraditional: { ++ for (const char* name : kLinuxB5FontList) { + auto it = m_FontList.find(name); + if (it != m_FontList.end()) + return it->second.get(); + } + break; + } +- case FX_CHARSET_Hangul: { +- for (const char* name : g_LinuxHGFontList) { ++ case FX_Charset::kHangul: { ++ for (const char* name : kLinuxHGFontList) { + auto it = m_FontList.find(name); + if (it != m_FontList.end()) + return it->second.get(); +@@ -137,7 +142,7 @@ void* CFX_LinuxFontInfo::MapFont(int weight, + bCJK = false; + break; + } +- return FindFont(weight, bItalic, charset, pitch_family, family, !bCJK); ++ return FindFont(weight, bItalic, charset, pitch_family, face, !bCJK); + } + + bool CFX_LinuxFontInfo::ParseFontCfg(const char** pUserPaths) { +@@ -151,33 +156,27 @@ bool CFX_LinuxFontInfo::ParseFontCfg(const char** pUserPaths) { + + } // namespace + +-std::unique_ptr SystemFontInfoIface::CreateDefault( +- const char** pUserPaths) { +- auto pInfo = pdfium::MakeUnique(); +- if (!pInfo->ParseFontCfg(pUserPaths)) { +- pInfo->AddPath("/usr/share/fonts"); +- pInfo->AddPath("/usr/share/X11/fonts/Type1"); +- pInfo->AddPath("/usr/share/X11/fonts/TTF"); +- pInfo->AddPath("/usr/local/share/fonts"); +- } +- return std::move(pInfo); +-} +- + class CLinuxPlatform : public CFX_GEModule::PlatformIface { + public: + CLinuxPlatform() = default; + ~CLinuxPlatform() override = default; + +- void Init() override { +- CFX_GEModule* pModule = CFX_GEModule::Get(); +- pModule->GetFontMgr()->SetSystemFontInfo( +- SystemFontInfoIface::CreateDefault(pModule->GetUserFontPaths())); ++ void Init() override {} ++ ++ std::unique_ptr CreateDefaultSystemFontInfo() override { ++ auto pInfo = std::make_unique(); ++ if (!pInfo->ParseFontCfg(CFX_GEModule::Get()->GetUserFontPaths())) { ++ pInfo->AddPath("/usr/share/fonts"); ++ pInfo->AddPath("/usr/share/X11/fonts/Type1"); ++ pInfo->AddPath("/usr/share/X11/fonts/TTF"); ++ pInfo->AddPath("/usr/local/share/fonts"); ++ } ++ return pInfo; + } + }; + + // static + std::unique_ptr + CFX_GEModule::PlatformIface::Create() { +- return pdfium::MakeUnique(); ++ return std::make_unique(); + } +-#endif // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_ +diff --git a/core/fxge/render_defines.h b/core/fxge/render_defines.h +index ff52b4e87..0507e78a8 100644 +--- a/core/fxge/render_defines.h ++++ b/core/fxge/render_defines.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -21,29 +21,9 @@ + #define FXRC_ALPHA_OUTPUT 0x40 + #define FXRC_BLEND_MODE 0x80 + #define FXRC_SOFT_CLIP 0x100 +-#define FXRC_CMYK_OUTPUT 0x200 + #define FXRC_BITMASK_OUTPUT 0x400 + #define FXRC_BYTEMASK_OUTPUT 0x800 +-#define FXRENDER_IMAGE_LOSSY 0x1000 + #define FXRC_FILLSTROKE_PATH 0x2000 + #define FXRC_SHADING 0x4000 + +-#define FXFILL_ALTERNATE 1 +-#define FXFILL_WINDING 2 +-#define FXFILL_FULLCOVER 4 +-#define FXFILL_RECT_AA 8 +-#define FX_FILL_STROKE 16 +-#define FX_STROKE_ADJUST 32 +-#define FX_STROKE_TEXT_MODE 64 +-#define FX_FILL_TEXT_MODE 128 +-#define FX_ZEROAREA_FILL 256 +-#define FXFILL_NOPATHSMOOTH 512 +- +-#define FXTEXT_CLEARTYPE 0x01 +-#define FXTEXT_BGR_STRIPE 0x02 +-#define FXTEXT_PRINTGRAPHICTEXT 0x04 +-#define FXTEXT_NO_NATIVETEXT 0x08 +-#define FXTEXT_PRINTIMAGETEXT 0x10 +-#define FXTEXT_NOSMOOTH 0x20 +- + #endif // CORE_FXGE_RENDER_DEFINES_H_ +diff --git a/core/fxge/renderdevicedriver_iface.cpp b/core/fxge/renderdevicedriver_iface.cpp +index f8fcccf68..b549ea256 100644 +--- a/core/fxge/renderdevicedriver_iface.cpp ++++ b/core/fxge/renderdevicedriver_iface.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,19 +7,13 @@ + #include "core/fxge/renderdevicedriver_iface.h" + + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/dib/cfx_dibitmap.h" + +-RenderDeviceDriverIface::~RenderDeviceDriverIface() {} +- +-bool RenderDeviceDriverIface::StartRendering() { +- return true; +-} +- +-void RenderDeviceDriverIface::EndRendering() {} ++RenderDeviceDriverIface::~RenderDeviceDriverIface() = default; + + bool RenderDeviceDriverIface::SetClip_PathStroke( +- const CFX_PathData* pPathData, ++ const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState) { + return false; +@@ -27,10 +21,6 @@ bool RenderDeviceDriverIface::SetClip_PathStroke( + + void RenderDeviceDriverIface::SetBaseClip(const FX_RECT& rect) {} + +-bool RenderDeviceDriverIface::SetPixel(int x, int y, uint32_t color) { +- return false; +-} +- + bool RenderDeviceDriverIface::FillRectWithBlend(const FX_RECT& rect, + uint32_t fill_color, + BlendMode blend_type) { +@@ -59,12 +49,13 @@ bool RenderDeviceDriverIface::ContinueDIBits(CFX_ImageRenderer* handle, + return false; + } + +-bool RenderDeviceDriverIface::DrawDeviceText(int nChars, +- const TextCharPos* pCharPos, +- CFX_Font* pFont, +- const CFX_Matrix& mtObject2Device, +- float font_size, +- uint32_t color) { ++bool RenderDeviceDriverIface::DrawDeviceText( ++ pdfium::span pCharPos, ++ CFX_Font* pFont, ++ const CFX_Matrix& mtObject2Device, ++ float font_size, ++ uint32_t color, ++ const CFX_TextRenderOptions& options) { + return false; + } + +@@ -72,8 +63,6 @@ int RenderDeviceDriverIface::GetDriverType() const { + return 0; + } + +-void RenderDeviceDriverIface::ClearDriver() {} +- + bool RenderDeviceDriverIface::DrawShading(const CPDF_ShadingPattern* pPattern, + const CFX_Matrix* pMatrix, + const FX_RECT& clip_rect, +@@ -82,6 +71,7 @@ bool RenderDeviceDriverIface::DrawShading(const CPDF_ShadingPattern* pPattern, + return false; + } + ++#if defined(_SKIA_SUPPORT_) + bool RenderDeviceDriverIface::SetBitsWithMask( + const RetainPtr& pBitmap, + const RetainPtr& pMask, +@@ -92,6 +82,5 @@ bool RenderDeviceDriverIface::SetBitsWithMask( + return false; + } + +-#if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_ +-void RenderDeviceDriverIface::Flush() {} ++void RenderDeviceDriverIface::SetGroupKnockout(bool group_knockout) {} + #endif +diff --git a/core/fxge/renderdevicedriver_iface.h b/core/fxge/renderdevicedriver_iface.h +index 69e404b70..a25eca38e 100644 +--- a/core/fxge/renderdevicedriver_iface.h ++++ b/core/fxge/renderdevicedriver_iface.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,14 @@ + #ifndef CORE_FXGE_RENDERDEVICEDRIVER_IFACE_H_ + #define CORE_FXGE_RENDERDEVICEDRIVER_IFACE_H_ + ++#include ++ + #include + + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "third_party/base/span.h" + + class CFX_DIBBase; + class CFX_DIBitmap; +@@ -20,14 +22,15 @@ class CFX_Font; + class CFX_GraphStateData; + class CFX_ImageRenderer; + class CFX_Matrix; +-class CFX_PathData; ++class CFX_Path; + class CPDF_ShadingPattern; + class PauseIndicatorIface; + class TextCharPos; ++struct CFX_FillRenderOptions; ++struct CFX_TextRenderOptions; + struct FX_RECT; + +-enum class DeviceType : uint8_t { +- kUnknown, ++enum class DeviceType : bool { + kDisplay, + kPrinter, + }; +@@ -39,26 +42,23 @@ class RenderDeviceDriverIface { + virtual DeviceType GetDeviceType() const = 0; + virtual int GetDeviceCaps(int caps_id) const = 0; + +- virtual bool StartRendering(); +- virtual void EndRendering(); + virtual void SaveState() = 0; + virtual void RestoreState(bool bKeepSaved) = 0; + + virtual void SetBaseClip(const FX_RECT& rect); +- virtual bool SetClip_PathFill(const CFX_PathData* pPathData, ++ virtual bool SetClip_PathFill(const CFX_Path& path, + const CFX_Matrix* pObject2Device, +- int fill_mode) = 0; +- virtual bool SetClip_PathStroke(const CFX_PathData* pPathData, ++ const CFX_FillRenderOptions& fill_options) = 0; ++ virtual bool SetClip_PathStroke(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState); +- virtual bool DrawPath(const CFX_PathData* pPathData, ++ virtual bool DrawPath(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState, + uint32_t fill_color, + uint32_t stroke_color, +- int fill_mode, ++ const CFX_FillRenderOptions& fill_options, + BlendMode blend_type) = 0; +- virtual bool SetPixel(int x, int y, uint32_t color); + virtual bool FillRectWithBlend(const FX_RECT& rect, + uint32_t fill_color, + BlendMode blend_type); +@@ -96,28 +96,33 @@ class RenderDeviceDriverIface { + BlendMode blend_type) = 0; + virtual bool ContinueDIBits(CFX_ImageRenderer* handle, + PauseIndicatorIface* pPause); +- virtual bool DrawDeviceText(int nChars, +- const TextCharPos* pCharPos, ++ virtual bool DrawDeviceText(pdfium::span pCharPos, + CFX_Font* pFont, + const CFX_Matrix& mtObject2Device, + float font_size, +- uint32_t color); ++ uint32_t color, ++ const CFX_TextRenderOptions& options); + virtual int GetDriverType() const; +- virtual void ClearDriver(); + virtual bool DrawShading(const CPDF_ShadingPattern* pPattern, + const CFX_Matrix* pMatrix, + const FX_RECT& clip_rect, + int alpha, + bool bAlphaMode); ++#if defined(_SKIA_SUPPORT_) + virtual bool SetBitsWithMask(const RetainPtr& pBitmap, + const RetainPtr& pMask, + int left, + int top, + int bitmap_alpha, + BlendMode blend_type); +-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ +- virtual void Flush(); ++ virtual void SetGroupKnockout(bool group_knockout); + #endif ++ ++ // Multiplies the device by a constant alpha, returning `true` on success. ++ virtual bool MultiplyAlpha(float alpha) = 0; ++ ++ // Multiplies the device by an alpha mask, returning `true` on success. ++ virtual bool MultiplyAlpha(const RetainPtr& mask) = 0; + }; + + #endif // CORE_FXGE_RENDERDEVICEDRIVER_IFACE_H_ +diff --git a/core/fxge/scoped_font_transform.cpp b/core/fxge/scoped_font_transform.cpp +index 2bd53963e..76626b30b 100644 +--- a/core/fxge/scoped_font_transform.cpp ++++ b/core/fxge/scoped_font_transform.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/core/fxge/scoped_font_transform.h b/core/fxge/scoped_font_transform.h +index bcbbb5061..7530e17fc 100644 +--- a/core/fxge/scoped_font_transform.h ++++ b/core/fxge/scoped_font_transform.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,13 +7,17 @@ + #ifndef CORE_FXGE_SCOPED_FONT_TRANSFORM_H_ + #define CORE_FXGE_SCOPED_FONT_TRANSFORM_H_ + ++#include "core/fxcrt/fx_memory.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxge/cfx_face.h" +-#include "core/fxge/fx_freetype.h" ++#include "core/fxge/freetype/fx_freetype.h" + + // Sets the given transform on the font, and resets it to the identity when it + // goes out of scope. + class ScopedFontTransform { + public: ++ FX_STACK_ALLOCATED(); ++ + ScopedFontTransform(RetainPtr face, FT_Matrix* matrix); + ~ScopedFontTransform(); + +diff --git a/core/fxge/skia/fx_skia_device.cpp b/core/fxge/skia/fx_skia_device.cpp +index f29022f4f..ef3218d7f 100644 +--- a/core/fxge/skia/fx_skia_device.cpp ++++ b/core/fxge/skia/fx_skia_device.cpp +@@ -1,10 +1,15 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxge/skia/fx_skia_device.h" + ++#include ++#include ++ + #include ++#include ++#include + #include + #include + +@@ -20,27 +25,50 @@ + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" + #include "core/fxcrt/cfx_bitstream.h" +-#include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_2d_size.h" ++#include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/stl_util.h" ++#include "core/fxge/calculate_pitch.h" + #include "core/fxge/cfx_defaultrenderdevice.h" ++#include "core/fxge/cfx_fillrenderoptions.h" + #include "core/fxge/cfx_font.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/cfx_renderdevice.h" +-#include "core/fxge/dib/cfx_bitmapcomposer.h" ++#include "core/fxge/cfx_substfont.h" ++#include "core/fxge/cfx_textrenderoptions.h" + #include "core/fxge/dib/cfx_dibitmap.h" + #include "core/fxge/dib/cfx_imagerenderer.h" +-#include "core/fxge/dib/cfx_imagestretcher.h" ++#include "core/fxge/dib/cstretchengine.h" ++#include "core/fxge/dib/fx_dib.h" + #include "core/fxge/text_char_pos.h" +-#include "third_party/base/logging.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/notreached.h" ++#include "third_party/base/numerics/safe_conversions.h" + #include "third_party/base/ptr_util.h" ++#include "third_party/base/span.h" ++#include "third_party/skia/include/core/SkBitmap.h" ++#include "third_party/skia/include/core/SkBlendMode.h" + #include "third_party/skia/include/core/SkCanvas.h" + #include "third_party/skia/include/core/SkClipOp.h" ++#include "third_party/skia/include/core/SkColorFilter.h" + #include "third_party/skia/include/core/SkColorPriv.h" ++#include "third_party/skia/include/core/SkColorType.h" + #include "third_party/skia/include/core/SkImage.h" ++#include "third_party/skia/include/core/SkImageInfo.h" ++#include "third_party/skia/include/core/SkMaskFilter.h" + #include "third_party/skia/include/core/SkPaint.h" + #include "third_party/skia/include/core/SkPath.h" ++#include "third_party/skia/include/core/SkPathEffect.h" ++#include "third_party/skia/include/core/SkPathUtils.h" ++#include "third_party/skia/include/core/SkPictureRecorder.h" + #include "third_party/skia/include/core/SkRSXform.h" ++#include "third_party/skia/include/core/SkRect.h" ++#include "third_party/skia/include/core/SkRefCnt.h" ++#include "third_party/skia/include/core/SkSamplingOptions.h" + #include "third_party/skia/include/core/SkShader.h" + #include "third_party/skia/include/core/SkStream.h" + #include "third_party/skia/include/core/SkTextBlob.h" +@@ -49,126 +77,12 @@ + #include "third_party/skia/include/effects/SkGradientShader.h" + #include "third_party/skia/include/pathops/SkPathOps.h" + +-#ifdef _SKIA_SUPPORT_PATHS_ +-#include "core/fxge/cfx_cliprgn.h" +-#endif // _SKIA_SUPPORT_PATHS_ +- +-#ifdef _SKIA_SUPPORT_ +-#include "third_party/skia/include/core/SkColorFilter.h" +-#include "third_party/skia/include/core/SkMaskFilter.h" +-#include "third_party/skia/include/core/SkPictureRecorder.h" +-#endif // _SKIA_SUPPORT_ +- + namespace { + +-#ifdef _SKIA_SUPPORT_PATHS_ +-void RgbByteOrderTransferBitmap(const RetainPtr& pBitmap, +- int dest_left, +- int dest_top, +- int width, +- int height, +- const RetainPtr& pSrcBitmap, +- int src_left, +- int src_top) { +- if (!pBitmap) +- return; +- +- if (!pBitmap->GetOverlapRect(dest_left, dest_top, width, height, +- pSrcBitmap->GetWidth(), pSrcBitmap->GetHeight(), +- src_left, src_top, nullptr)) { +- return; +- } +- +- int Bpp = pBitmap->GetBPP() / 8; +- FXDIB_Format dest_format = pBitmap->GetFormat(); +- FXDIB_Format src_format = pSrcBitmap->GetFormat(); +- int pitch = pBitmap->GetPitch(); +- uint8_t* buffer = pBitmap->GetBuffer(); +- if (dest_format == src_format) { +- for (int row = 0; row < height; row++) { +- uint8_t* dest_scan = buffer + (dest_top + row) * pitch + dest_left * Bpp; +- const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left * Bpp; +- if (Bpp == 4) { +- for (int col = 0; col < width; col++) { +- FXARGB_SETDIB(dest_scan, ArgbEncode(src_scan[3], src_scan[0], +- src_scan[1], src_scan[2])); +- dest_scan += 4; +- src_scan += 4; +- } +- } else { +- for (int col = 0; col < width; col++) { +- *dest_scan++ = src_scan[2]; +- *dest_scan++ = src_scan[1]; +- *dest_scan++ = src_scan[0]; +- src_scan += 3; +- } +- } +- } +- return; +- } +- +- uint8_t* dest_buf = buffer + dest_top * pitch + dest_left * Bpp; +- if (dest_format == FXDIB_Rgb) { +- if (src_format == FXDIB_Rgb32) { +- for (int row = 0; row < height; row++) { +- uint8_t* dest_scan = dest_buf + row * pitch; +- const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left * 4; +- for (int col = 0; col < width; col++) { +- *dest_scan++ = src_scan[2]; +- *dest_scan++ = src_scan[1]; +- *dest_scan++ = src_scan[0]; +- src_scan += 4; +- } +- } +- } else { +- NOTREACHED(); +- } +- return; +- } +- +- if (dest_format == FXDIB_Argb || dest_format == FXDIB_Rgb32) { +- if (src_format == FXDIB_Rgb) { +- for (int row = 0; row < height; row++) { +- uint8_t* dest_scan = (uint8_t*)(dest_buf + row * pitch); +- const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left * 3; +- for (int col = 0; col < width; col++) { +- FXARGB_SETDIB(dest_scan, ArgbEncode(0xff, src_scan[0], src_scan[1], +- src_scan[2])); +- dest_scan += 4; +- src_scan += 3; +- } +- } +- } else if (src_format == FXDIB_Rgb32) { +- ASSERT(dest_format == FXDIB_Argb); +- for (int row = 0; row < height; row++) { +- uint8_t* dest_scan = dest_buf + row * pitch; +- const uint8_t* src_scan = +- pSrcBitmap->GetScanline(src_top + row) + src_left * 4; +- for (int col = 0; col < width; col++) { +- FXARGB_SETDIB(dest_scan, ArgbEncode(0xff, src_scan[0], src_scan[1], +- src_scan[2])); +- src_scan += 4; +- dest_scan += 4; +- } +- } +- } +- return; +- } +- +- NOTREACHED(); +-} +- +-#endif // _SKIA_SUPPORT_PATHS_ +- + #define SHOW_SKIA_PATH 0 // set to 1 to print the path contents + #if SHOW_SKIA_PATH + #define SHOW_SKIA_PATH_SHORTHAND 0 // set to 1 for abbreviated path contents + #endif +-#define DRAW_SKIA_CLIP 0 // set to 1 to draw a green rectangle around the clip +-#define SHOW_TEXT_GLYPHS 0 // set to 1 to print unichar equivalent of glyph + + #if SHOW_SKIA_PATH + void DebugShowSkiaPaint(const SkPaint& paint) { +@@ -187,11 +101,10 @@ void DebugShowSkiaPath(const SkPath& path) { + printf(" **\n"); + #else + SkDynamicMemoryWStream stream; +- path.dump(&stream, false, false); +- std::unique_ptr storage; +- storage.reset(FX_Alloc(char, stream.bytesWritten())); +- stream.copyTo(storage.get()); +- printf("%.*s", (int)stream.bytesWritten(), storage.get()); ++ path.dump(&stream, false); ++ DataVector storage(stream.bytesWritten()); ++ stream.copyTo(storage.data()); ++ printf("%.*s", static_cast(storage.size()), storage.data()); + #endif // SHOW_SKIA_PATH_SHORTHAND + #endif // SHOW_SKIA_PATH + } +@@ -241,96 +154,134 @@ void DebugShowSkiaDrawRect(CFX_SkiaDeviceDriver* driver, + #endif // SHOW_SKIA_PATH + } + +-#if DRAW_SKIA_CLIP +- +-SkPaint DebugClipPaint() { +- SkPaint paint; +- paint.setAntiAlias(true); +- paint.setColor(SK_ColorGREEN); +- paint.setStyle(SkPaint::kStroke_Style); +- return paint; +-} +- +-void DebugDrawSkiaClipRect(SkCanvas* canvas, const SkRect& rect) { +- SkPaint paint = DebugClipPaint(); +- canvas->drawRect(rect, paint); ++bool IsRGBColorGrayScale(uint32_t color) { ++ return FXARGB_R(color) == FXARGB_G(color) && ++ FXARGB_R(color) == FXARGB_B(color); + } + +-void DebugDrawSkiaClipPath(SkCanvas* canvas, const SkPath& path) { +- SkPaint paint = DebugClipPaint(); +- canvas->drawPath(path, paint); +-} ++// Called by Upsample, return a 32 bit-per-pixel buffer filled with 2 colors ++// from a 1 bit-per-pixel source palette. ++DataVector Fill32BppDestStorageWith1BppSource( ++ const RetainPtr& source) { ++ DCHECK_EQ(1, source->GetBPP()); ++ int width = source->GetWidth(); ++ int height = source->GetHeight(); ++ void* buffer = source->GetBuffer().data(); ++ DCHECK(buffer); + +-#else // DRAW_SKIA_CLIP ++ uint32_t color0 = source->GetPaletteArgb(0); ++ uint32_t color1 = source->GetPaletteArgb(1); ++ DataVector dst32_storage(Fx2DSizeOrDie(width, height)); ++ pdfium::span dst32_pixels(dst32_storage); + +-void DebugDrawSkiaClipRect(SkCanvas* canvas, const SkRect& rect) {} ++ for (int y = 0; y < height; ++y) { ++ const uint8_t* src_row = ++ static_cast(buffer) + y * source->GetPitch(); ++ pdfium::span dst_row = dst32_pixels.subspan(y * width); ++ for (int x = 0; x < width; ++x) { ++ bool use_color1 = src_row[x / 8] & (1 << (7 - x % 8)); ++ dst_row[x] = use_color1 ? color1 : color0; ++ } ++ } ++ return dst32_storage; ++} + +-void DebugDrawSkiaClipPath(SkCanvas* canvas, const SkPath& path) {} ++// Called by Upsample(), returns a 32 bit-per-pixel buffer filled with colors ++// from `palette`. ++DataVector Fill32BppDestStorageWithPalette( ++ const RetainPtr& source, ++ pdfium::span palette) { ++ DCHECK_EQ(8, source->GetBPP()); ++ int width = source->GetWidth(); ++ int height = source->GetHeight(); ++ void* buffer = source->GetBuffer().data(); ++ DCHECK(buffer); ++ DataVector dst32_storage(Fx2DSizeOrDie(width, height)); ++ pdfium::span dst32_pixels(dst32_storage); + +-#endif // DRAW_SKIA_CLIP ++ for (int y = 0; y < height; ++y) { ++ const uint8_t* src_row = ++ static_cast(buffer) + y * source->GetPitch(); ++ pdfium::span dst_row = dst32_pixels.subspan(y * width); ++ for (int x = 0; x < width; ++x) { ++ unsigned index = src_row[x]; ++ if (index >= palette.size()) { ++ index = 0; ++ } ++ dst_row[x] = palette[index]; ++ } ++ } ++ return dst32_storage; ++} + +-#ifdef _SKIA_SUPPORT_ + static void DebugValidate(const RetainPtr& bitmap, + const RetainPtr& device) { + if (bitmap) { +- ASSERT(bitmap->GetBPP() == 8 || bitmap->GetBPP() == 32); +- if (bitmap->GetBPP() == 32) { +- bitmap->DebugVerifyBitmapIsPreMultiplied(nullptr); +- } ++ DCHECK(bitmap->GetBPP() == 8 || bitmap->GetBPP() == 32); + } + if (device) { +- ASSERT(device->GetBPP() == 8 || device->GetBPP() == 32); +- if (device->GetBPP() == 32) { +- device->DebugVerifyBitmapIsPreMultiplied(nullptr); +- } ++ DCHECK(device->GetBPP() == 8 || device->GetBPP() == 32); + } + } +-#endif // _SKIA_SUPPORT_ +- +-constexpr int kAlternateOrWindingFillModeMask = +- FXFILL_ALTERNATE | FXFILL_WINDING; + +-int GetAlternateOrWindingFillMode(int fill_mode) { +- return fill_mode & kAlternateOrWindingFillModeMask; ++SkColorType Get32BitSkColorType(bool is_rgb_byte_order) { ++ return is_rgb_byte_order ? kRGBA_8888_SkColorType : kBGRA_8888_SkColorType; + } + +-bool IsAlternateFillMode(int fill_mode) { ++SkPathFillType GetAlternateOrWindingFillType( ++ const CFX_FillRenderOptions& fill_options) { + // TODO(thestig): This function should be able to assert +- // GetAlternateOrWindingFillMode(fill_mode) != 0. +- return GetAlternateOrWindingFillMode(fill_mode) == FXFILL_ALTERNATE; ++ // fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill. ++ return fill_options.fill_type == CFX_FillRenderOptions::FillType::kEvenOdd ++ ? SkPathFillType::kEvenOdd ++ : SkPathFillType::kWinding; + } + +-SkPathFillType GetAlternateOrWindingFillType(int fill_mode) { +- return IsAlternateFillMode(fill_mode) ? SkPathFillType::kEvenOdd +- : SkPathFillType::kWinding; ++SkFont::Edging GetFontEdgingType(const CFX_TextRenderOptions& text_options) { ++ if (text_options.aliasing_type == CFX_TextRenderOptions::kAliasing) ++ return SkFont::Edging::kAlias; ++ ++ if (text_options.aliasing_type == CFX_TextRenderOptions::kAntiAliasing) ++ return SkFont::Edging::kAntiAlias; ++ ++ DCHECK_EQ(text_options.aliasing_type, CFX_TextRenderOptions::kLcd); ++ return SkFont::Edging::kSubpixelAntiAlias; + } + +-bool IsEvenOddFillType(SkPathFillType fill) { +- return fill == SkPathFillType::kEvenOdd || +- fill == SkPathFillType::kInverseEvenOdd; ++bool IsPathAPoint(const SkPath& path) { ++ if (path.isEmpty()) ++ return false; ++ ++ if (path.countPoints() == 1) ++ return true; ++ ++ for (int i = 0; i < path.countPoints() - 1; ++i) { ++ if (path.getPoint(i) != path.getPoint(i + 1)) ++ return false; ++ } ++ return true; + } + +-SkPath BuildPath(const CFX_PathData* pPathData) { +- SkPath skPath; +- const CFX_PathData* pFPath = pPathData; +- const std::vector& pPoints = pFPath->GetPoints(); +- for (size_t i = 0; i < pPoints.size(); i++) { +- CFX_PointF point = pPoints[i].m_Point; +- FXPT_TYPE point_type = pPoints[i].m_Type; +- if (point_type == FXPT_TYPE::MoveTo) { +- skPath.moveTo(point.x, point.y); +- } else if (point_type == FXPT_TYPE::LineTo) { +- skPath.lineTo(point.x, point.y); +- } else if (point_type == FXPT_TYPE::BezierTo) { +- CFX_PointF point2 = pPoints[i + 1].m_Point; +- CFX_PointF point3 = pPoints[i + 2].m_Point; +- skPath.cubicTo(point.x, point.y, point2.x, point2.y, point3.x, point3.y); ++SkPath BuildPath(const CFX_Path& path) { ++ SkPath sk_path; ++ pdfium::span points = path.GetPoints(); ++ for (size_t i = 0; i < points.size(); ++i) { ++ const CFX_PointF& point = points[i].m_Point; ++ CFX_Path::Point::Type point_type = points[i].m_Type; ++ if (point_type == CFX_Path::Point::Type::kMove) { ++ sk_path.moveTo(point.x, point.y); ++ } else if (point_type == CFX_Path::Point::Type::kLine) { ++ sk_path.lineTo(point.x, point.y); ++ } else if (point_type == CFX_Path::Point::Type::kBezier) { ++ const CFX_PointF& point2 = points[i + 1].m_Point; ++ const CFX_PointF& point3 = points[i + 2].m_Point; ++ sk_path.cubicTo(point.x, point.y, point2.x, point2.y, point3.x, point3.y); + i += 2; + } +- if (pPoints[i].m_CloseFigure) +- skPath.close(); ++ if (points[i].m_CloseFigure) ++ sk_path.close(); + } +- return skPath; ++ return sk_path; + } + + SkMatrix ToSkMatrix(const CFX_Matrix& m) { +@@ -385,110 +336,120 @@ SkBlendMode GetSkiaBlendMode(BlendMode blend_type) { + } + } + +-// Add begin & end colors into |skColors| array for each gradient transition. ++// Add begin & end colors into `colors` array for each gradient transition. + // +-// |is_encode_reversed| must be set to true when the parent function of |pFunc| +-// has an Encode array, and the matching pair of encode values for |pFunc| are ++// `is_encode_reversed` must be set to true when the parent function of `func` ++// has an Encode array, and the matching pair of encode values for `func` are + // in decreasing order. +-bool AddColors(const CPDF_ExpIntFunc* pFunc, +- SkTDArray* skColors, ++bool AddColors(const CPDF_ExpIntFunc* func, ++ DataVector& colors, + bool is_encode_reversed) { +- if (pFunc->CountInputs() != 1) ++ if (func->CountInputs() != 1) { + return false; +- if (pFunc->m_Exponent != 1) ++ } ++ if (func->GetExponent() != 1) { + return false; +- if (pFunc->m_nOrigOutputs != 3) ++ } ++ if (func->GetOrigOutputs() != 3) { + return false; ++ } + +- auto begin_values = pFunc->m_BeginValues.begin(); +- auto end_values = pFunc->m_EndValues.begin(); ++ pdfium::span begin_values = func->GetBeginValues(); ++ pdfium::span end_values = func->GetEndValues(); + if (is_encode_reversed) + std::swap(begin_values, end_values); + +- skColors->push_back(SkColorSetARGB(0xFF, +- SkUnitScalarClampToByte(begin_values[0]), +- SkUnitScalarClampToByte(begin_values[1]), +- SkUnitScalarClampToByte(begin_values[2]))); +- skColors->push_back(SkColorSetARGB(0xFF, +- SkUnitScalarClampToByte(end_values[0]), +- SkUnitScalarClampToByte(end_values[1]), +- SkUnitScalarClampToByte(end_values[2]))); ++ colors.push_back(SkColorSetARGB(0xFF, ++ SkUnitScalarClampToByte(begin_values[0]), ++ SkUnitScalarClampToByte(begin_values[1]), ++ SkUnitScalarClampToByte(begin_values[2]))); ++ colors.push_back(SkColorSetARGB(0xFF, SkUnitScalarClampToByte(end_values[0]), ++ SkUnitScalarClampToByte(end_values[1]), ++ SkUnitScalarClampToByte(end_values[2]))); + return true; + } + + uint8_t FloatToByte(float f) { +- ASSERT(f >= 0); +- ASSERT(f <= 1); ++ DCHECK(f >= 0); ++ DCHECK(f <= 1); + return (uint8_t)(f * 255.99f); + } + +-bool AddSamples(const CPDF_SampledFunc* pFunc, +- SkTDArray* skColors, +- SkTDArray* skPos) { +- if (pFunc->CountInputs() != 1) ++bool AddSamples(const CPDF_SampledFunc* func, ++ DataVector& colors, ++ DataVector& pos) { ++ if (func->CountInputs() != 1) { + return false; +- if (pFunc->CountOutputs() != 3) // expect rgb ++ } ++ if (func->CountOutputs() != 3) { // expect rgb + return false; +- if (pFunc->GetEncodeInfo().empty()) ++ } ++ if (func->GetEncodeInfo().empty()) { + return false; +- const CPDF_SampledFunc::SampleEncodeInfo& encodeInfo = +- pFunc->GetEncodeInfo()[0]; +- if (encodeInfo.encode_min != 0) ++ } ++ const CPDF_SampledFunc::SampleEncodeInfo& encode_info = ++ func->GetEncodeInfo()[0]; ++ if (encode_info.encode_min != 0) { + return false; +- if (encodeInfo.encode_max != encodeInfo.sizes - 1) ++ } ++ if (encode_info.encode_max != encode_info.sizes - 1) { + return false; +- uint32_t sampleSize = pFunc->GetBitsPerSample(); +- uint32_t sampleCount = encodeInfo.sizes; +- if (sampleCount != 1U << sampleSize) ++ } ++ uint32_t sample_size = func->GetBitsPerSample(); ++ uint32_t sample_count = encode_info.sizes; ++ if (sample_count != 1U << sample_size) { + return false; +- if (pFunc->GetSampleStream()->GetSize() < sampleCount * 3 * sampleSize / 8) ++ } ++ if (func->GetSampleStream()->GetSize() < sample_count * 3 * sample_size / 8) { + return false; ++ } + +- float colorsMin[3]; +- float colorsMax[3]; ++ float colors_min[3]; ++ float colors_max[3]; + for (int i = 0; i < 3; ++i) { +- colorsMin[i] = pFunc->GetRange(i * 2); +- colorsMax[i] = pFunc->GetRange(i * 2 + 1); ++ colors_min[i] = func->GetRange(i * 2); ++ colors_max[i] = func->GetRange(i * 2 + 1); + } +- pdfium::span pSampleData = pFunc->GetSampleStream()->GetSpan(); +- CFX_BitStream bitstream(pSampleData); +- for (uint32_t i = 0; i < sampleCount; ++i) { +- float floatColors[3]; ++ pdfium::span sample_data = func->GetSampleStream()->GetSpan(); ++ CFX_BitStream bitstream(sample_data); ++ for (uint32_t i = 0; i < sample_count; ++i) { ++ float float_colors[3]; + for (uint32_t j = 0; j < 3; ++j) { +- float sample = static_cast(bitstream.GetBits(sampleSize)); +- float interp = sample / (sampleCount - 1); +- floatColors[j] = colorsMin[j] + (colorsMax[j] - colorsMin[j]) * interp; ++ float sample = static_cast(bitstream.GetBits(sample_size)); ++ float interp = sample / (sample_count - 1); ++ float_colors[j] = ++ colors_min[j] + (colors_max[j] - colors_min[j]) * interp; + } +- SkColor color = +- SkPackARGB32(0xFF, FloatToByte(floatColors[0]), +- FloatToByte(floatColors[1]), FloatToByte(floatColors[2])); +- skColors->push_back(color); +- skPos->push_back((float)i / (sampleCount - 1)); ++ colors.push_back(SkPackARGB32NoCheck(0xFF, FloatToByte(float_colors[0]), ++ FloatToByte(float_colors[1]), ++ FloatToByte(float_colors[2]))); ++ pos.push_back(static_cast(i) / (sample_count - 1)); + } + return true; + } + +-bool AddStitching(const CPDF_StitchFunc* pFunc, +- SkTDArray* skColors, +- SkTDArray* skPos) { +- float boundsStart = pFunc->GetDomain(0); ++bool AddStitching(const CPDF_StitchFunc* func, ++ DataVector& colors, ++ DataVector& pos) { ++ float bounds_start = func->GetDomain(0); + +- const auto& subFunctions = pFunc->GetSubFunctions(); +- int subFunctionCount = subFunctions.size(); +- for (int i = 0; i < subFunctionCount; ++i) { +- const CPDF_ExpIntFunc* pSubFunc = subFunctions[i]->ToExpIntFunc(); +- if (!pSubFunc) ++ const auto& sub_functions = func->GetSubFunctions(); ++ const size_t sub_function_count = sub_functions.size(); ++ for (size_t i = 0; i < sub_function_count; ++i) { ++ const CPDF_ExpIntFunc* sub_func = sub_functions[i]->ToExpIntFunc(); ++ if (!sub_func) + return false; + // Check if the matching encode values are reversed + bool is_encode_reversed = +- pFunc->GetEncode(2 * i) > pFunc->GetEncode(2 * i + 1); +- if (!AddColors(pSubFunc, skColors, is_encode_reversed)) ++ func->GetEncode(2 * i) > func->GetEncode(2 * i + 1); ++ if (!AddColors(sub_func, colors, is_encode_reversed)) { + return false; +- float boundsEnd = +- i < subFunctionCount - 1 ? pFunc->GetBound(i + 1) : pFunc->GetDomain(1); +- skPos->push_back(boundsStart); +- skPos->push_back(boundsEnd); +- boundsStart = boundsEnd; ++ } ++ float bounds_end = ++ i < sub_function_count - 1 ? func->GetBound(i + 1) : func->GetDomain(1); ++ pos.push_back(bounds_start); ++ pos.push_back(bounds_end); ++ bounds_start = bounds_end; + } + return true; + } +@@ -535,13 +496,13 @@ void ClipAngledGradient(const SkPoint pts[2], + if (sDist * eDist <= 0) // if the signs are different, + continue; // the point is inside the gradient + if (sDist < 0) { +- SkScalar smaller = SkTMin(sDist, eDist); ++ SkScalar smaller = std::min(sDist, eDist); + if (minPerpDist > smaller) { + minPerpDist = smaller; + minPerpPtIndex = i; + } + } else { +- SkScalar larger = SkTMax(sDist, eDist); ++ SkScalar larger = std::max(sDist, eDist); + if (maxPerpDist < larger) { + maxPerpDist = larger; + maxPerpPtIndex = i; +@@ -596,7 +557,77 @@ void ClipAngledGradient(const SkPoint pts[2], + clip->lineTo(IntersectSides(rectPts[maxBounds], slope, startEdgePt)); + } + +-#ifdef _SKIA_SUPPORT_ ++// Converts a stroking path to scanlines ++void PaintStroke(SkPaint* spaint, ++ const CFX_GraphStateData* graph_state, ++ const SkMatrix& matrix, ++ const CFX_FillRenderOptions& fill_options) { ++ SkPaint::Cap cap; ++ switch (graph_state->m_LineCap) { ++ case CFX_GraphStateData::LineCap::kRound: ++ cap = SkPaint::kRound_Cap; ++ break; ++ case CFX_GraphStateData::LineCap::kSquare: ++ cap = SkPaint::kSquare_Cap; ++ break; ++ default: ++ cap = SkPaint::kButt_Cap; ++ break; ++ } ++ SkPaint::Join join; ++ switch (graph_state->m_LineJoin) { ++ case CFX_GraphStateData::LineJoin::kRound: ++ join = SkPaint::kRound_Join; ++ break; ++ case CFX_GraphStateData::LineJoin::kBevel: ++ join = SkPaint::kBevel_Join; ++ break; ++ default: ++ join = SkPaint::kMiter_Join; ++ break; ++ } ++ SkMatrix inverse; ++ if (!matrix.invert(&inverse)) { ++ return; // give up if the matrix is degenerate, and not invertable ++ } ++ inverse.set(SkMatrix::kMTransX, 0); ++ inverse.set(SkMatrix::kMTransY, 0); ++ SkVector deviceUnits[2] = {{0, 1}, {1, 0}}; ++ inverse.mapPoints(deviceUnits, std::size(deviceUnits)); ++ ++ float width = fill_options.zero_area ++ ? 0.0f ++ : std::max(graph_state->m_LineWidth, ++ std::min(deviceUnits[0].length(), ++ deviceUnits[1].length())); ++ if (!graph_state->m_DashArray.empty()) { ++ size_t count = (graph_state->m_DashArray.size() + 1) / 2; ++ DataVector intervals(count * 2); ++ // Set dash pattern ++ for (size_t i = 0; i < count; i++) { ++ float on = graph_state->m_DashArray[i * 2]; ++ if (on <= 0.000001f) { ++ on = 0.1f; ++ } ++ float off = i * 2 + 1 == graph_state->m_DashArray.size() ++ ? on ++ : graph_state->m_DashArray[i * 2 + 1]; ++ off = std::max(off, 0.0f); ++ intervals[i * 2] = on; ++ intervals[i * 2 + 1] = off; ++ } ++ spaint->setPathEffect(SkDashPathEffect::Make( ++ intervals.data(), pdfium::base::checked_cast(intervals.size()), ++ graph_state->m_DashPhase)); ++ } ++ spaint->setStyle(SkPaint::kStroke_Style); ++ spaint->setAntiAlias(!fill_options.aliased_path); ++ spaint->setStrokeWidth(width); ++ spaint->setStrokeMiter(graph_state->m_MiterLimit); ++ spaint->setStrokeCap(cap); ++ spaint->setStrokeJoin(join); ++} ++ + void SetBitmapMatrix(const CFX_Matrix& m, + int width, + int height, +@@ -605,1061 +636,304 @@ void SetBitmapMatrix(const CFX_Matrix& m, + -m.d / height, m.d + m.f, 0, 0, 1); + } + +-void SetBitmapPaint(bool isAlphaMask, +- uint32_t argb, ++void SetBitmapPaint(bool is_mask, ++ bool anti_alias, + int bitmap_alpha, ++ uint32_t argb, + BlendMode blend_type, + SkPaint* paint) { +- paint->setAntiAlias(true); +- if (isAlphaMask) +- paint->setColorFilter(SkColorFilters::Blend(argb, SkBlendMode::kSrc)); ++ DCHECK_GE(bitmap_alpha, 0); ++ DCHECK_LE(bitmap_alpha, 255); ++ ++ if (is_mask) ++ paint->setColor(argb); ++ else if (bitmap_alpha != 255) ++ paint->setAlpha(bitmap_alpha); + +- // paint->setFilterQuality(kHigh_SkFilterQuality); ++ paint->setAntiAlias(anti_alias); + paint->setBlendMode(GetSkiaBlendMode(blend_type)); ++} ++ ++void SetBitmapPaintForMerge(bool is_mask, ++ bool anti_alias, ++ uint32_t argb, ++ int bitmap_alpha, ++ BlendMode blend_type, ++ SkPaint* paint) { ++ if (is_mask) ++ paint->setColorFilter(SkColorFilters::Blend(argb, SkBlendMode::kSrc)); ++ + paint->setAlpha(bitmap_alpha); ++ paint->setAntiAlias(anti_alias); ++ paint->setBlendMode(GetSkiaBlendMode(blend_type)); + } + + bool Upsample(const RetainPtr& pSource, +- std::unique_ptr& dst8Storage, +- std::unique_ptr& dst32Storage, ++ DataVector& dst32_storage, + SkBitmap* skBitmap, +- int* widthPtr, +- int* heightPtr, + bool forceAlpha) { +- void* buffer = pSource->GetBuffer(); ++ void* buffer = pSource->GetBuffer().data(); + if (!buffer) + return false; +- SkColorType colorType = forceAlpha || pSource->IsAlphaMask() ++ SkColorType colorType = forceAlpha || pSource->IsMaskFormat() + ? SkColorType::kAlpha_8_SkColorType + : SkColorType::kGray_8_SkColorType; +- SkAlphaType alphaType = +- pSource->IsAlphaMask() ? kPremul_SkAlphaType : kOpaque_SkAlphaType; ++ SkAlphaType alphaType = kPremul_SkAlphaType; + int width = pSource->GetWidth(); + int height = pSource->GetHeight(); + int rowBytes = pSource->GetPitch(); + switch (pSource->GetBPP()) { + case 1: { +- dst8Storage.reset(FX_Alloc2D(uint8_t, width, height)); +- uint8_t* dst8Pixels = dst8Storage.get(); ++ // By default, the two colors for grayscale are 0xFF and 0x00 unless they ++ // are specified in the palette. ++ uint8_t color0 = 0x00; ++ uint8_t color1 = 0xFF; ++ ++ if (pSource->GetFormat() == FXDIB_Format::k1bppRgb && ++ pSource->HasPalette()) { ++ uint32_t palette_color0 = pSource->GetPaletteArgb(0); ++ uint32_t palette_color1 = pSource->GetPaletteArgb(1); ++ bool use_gray_colors = IsRGBColorGrayScale(palette_color0) && ++ IsRGBColorGrayScale(palette_color1); ++ if (!use_gray_colors) { ++ dst32_storage = Fill32BppDestStorageWith1BppSource(pSource); ++ rowBytes = width * sizeof(uint32_t); ++ colorType = kBGRA_8888_SkColorType; ++ break; ++ } ++ ++ color0 = FXARGB_R(palette_color0); ++ color1 = FXARGB_R(palette_color1); ++ } ++ ++ const int src_row_bytes = rowBytes; // Save original value. ++ rowBytes = fxge::CalculatePitch32OrDie(/*bpp=*/8, width); ++ dst32_storage = DataVector(Fx2DSizeOrDie(rowBytes / 4, height)); ++ pdfium::span dst8_pixels = ++ pdfium::as_writable_bytes(pdfium::make_span(dst32_storage)); + for (int y = 0; y < height; ++y) { +- const uint8_t* srcRow = +- static_cast(buffer) + y * rowBytes; +- uint8_t* dstRow = dst8Pixels + y * width; ++ const uint8_t* src_row = ++ static_cast(buffer) + y * src_row_bytes; ++ pdfium::span dst_row = dst8_pixels.subspan(y * rowBytes); + for (int x = 0; x < width; ++x) +- dstRow[x] = srcRow[x >> 3] & (1 << (~x & 0x07)) ? 0xFF : 0x00; ++ dst_row[x] = src_row[x >> 3] & (1 << (~x & 0x07)) ? color1 : color0; + } +- buffer = dst8Storage.get(); +- rowBytes = width; + break; + } + case 8: + // we upscale ctables to 32bit. +- if (pSource->GetPalette()) { +- dst32Storage.reset(FX_Alloc2D(uint32_t, width, height)); +- SkPMColor* dst32Pixels = dst32Storage.get(); +- const SkPMColor* ctable = pSource->GetPalette(); +- const unsigned ctableSize = pSource->GetPaletteSize(); +- for (int y = 0; y < height; ++y) { +- const uint8_t* srcRow = +- static_cast(buffer) + y * rowBytes; +- uint32_t* dstRow = dst32Pixels + y * width; +- for (int x = 0; x < width; ++x) { +- unsigned index = srcRow[x]; +- if (index >= ctableSize) { +- index = 0; +- } +- dstRow[x] = ctable[index]; +- } +- } +- buffer = dst32Storage.get(); ++ if (pSource->HasPalette()) { ++ const size_t src_palette_size = pSource->GetRequiredPaletteSize(); ++ pdfium::span src_palette = pSource->GetPaletteSpan(); ++ CHECK_LE(src_palette_size, src_palette.size()); ++ if (src_palette_size < src_palette.size()) ++ src_palette = src_palette.first(src_palette_size); ++ ++ dst32_storage = Fill32BppDestStorageWithPalette(pSource, src_palette); + rowBytes = width * sizeof(uint32_t); +- colorType = SkColorType::kN32_SkColorType; ++ colorType = kBGRA_8888_SkColorType; + } + break; + case 24: { +- dst32Storage.reset(FX_Alloc2D(uint32_t, width, height)); +- uint32_t* dst32Pixels = dst32Storage.get(); ++ dst32_storage = DataVector(Fx2DSizeOrDie(width, height)); ++ pdfium::span dst32_pixels(dst32_storage); + for (int y = 0; y < height; ++y) { + const uint8_t* srcRow = + static_cast(buffer) + y * rowBytes; +- uint32_t* dstRow = dst32Pixels + y * width; ++ pdfium::span dst_row = dst32_pixels.subspan(y * width); + for (int x = 0; x < width; ++x) { +- dstRow[x] = SkPackARGB32(0xFF, srcRow[x * 3 + 2], srcRow[x * 3 + 1], +- srcRow[x * 3 + 0]); ++ dst_row[x] = SkPackARGB32NoCheck( ++ 0xFF, srcRow[x * 3 + 2], srcRow[x * 3 + 1], srcRow[x * 3 + 0]); + } + } +- buffer = dst32Storage.get(); + rowBytes = width * sizeof(uint32_t); +- colorType = SkColorType::kN32_SkColorType; ++ colorType = kBGRA_8888_SkColorType; + alphaType = kOpaque_SkAlphaType; + break; + } + case 32: +- colorType = SkColorType::kN32_SkColorType; +- alphaType = kPremul_SkAlphaType; +- pSource->DebugVerifyBitmapIsPreMultiplied(buffer); ++ colorType = kBGRA_8888_SkColorType; + break; + default: +- NOTREACHED(); // TODO(bug_11) ensure that all cases are covered +- colorType = SkColorType::kUnknown_SkColorType; ++ NOTREACHED(); ++ } ++ if (!dst32_storage.empty()) { ++ buffer = dst32_storage.data(); + } + SkImageInfo imageInfo = + SkImageInfo::Make(width, height, colorType, alphaType); + skBitmap->installPixels(imageInfo, buffer, rowBytes); +- *widthPtr = width; +- *heightPtr = height; + return true; + } +-#endif // _SKIA_SUPPORT_ + +-} // namespace ++// Makes a bitmap filled with a solid color for debugging with `SkPicture`. ++RetainPtr MakeDebugBitmap(int width, int height, uint32_t color) { ++ auto bitmap = pdfium::MakeRetain(); ++ if (!bitmap->Create(width, height, FXDIB_Format::kArgb)) ++ return nullptr; + +-// Encapsulate the state used for successive text and path draws so that +-// they can be combined. +-class SkiaState { +- public: +- enum class Clip { +- kSave, +- kPath, +- }; +- +- enum class Accumulator { +- kNone, +- kPath, +- kText, +- kOther, +- }; +- +- // mark all cached state as uninitialized +- explicit SkiaState(CFX_SkiaDeviceDriver* pDriver) : m_pDriver(pDriver) {} +- +- bool DrawPath(const CFX_PathData* pPathData, +- const CFX_Matrix* pMatrix, +- const CFX_GraphStateData* pDrawState, +- uint32_t fill_color, +- uint32_t stroke_color, +- int fill_mode, +- BlendMode blend_type) { +- if (m_debugDisable) +- return false; +- Dump(__func__); +- int drawIndex = SkTMin(m_drawIndex, m_commands.count()); +- if (Accumulator::kText == m_type || drawIndex != m_commandIndex || +- (Accumulator::kPath == m_type && +- DrawChanged(pMatrix, pDrawState, fill_color, stroke_color, fill_mode, +- blend_type, m_pDriver->GetGroupKnockout()))) { +- Flush(); +- } +- if (Accumulator::kPath != m_type) { +- m_skPath.reset(); +- m_fillFullCover = !!(fill_mode & FXFILL_FULLCOVER); +- m_fillPath = GetAlternateOrWindingFillMode(fill_mode) && fill_color; +- m_skPath.setFillType(GetAlternateOrWindingFillType(fill_mode)); +- if (pDrawState) +- m_drawState = *pDrawState; +- m_fillColor = fill_color; +- m_strokeColor = stroke_color; +- m_blendType = blend_type; +- m_groupKnockout = m_pDriver->GetGroupKnockout(); +- if (pMatrix) +- m_drawMatrix = *pMatrix; +- m_drawIndex = m_commandIndex; +- m_type = Accumulator::kPath; +- } +- SkPath skPath = BuildPath(pPathData); +- SkPoint delta; +- if (MatrixOffset(pMatrix, &delta)) +- skPath.offset(delta.fX, delta.fY); +- m_skPath.addPath(skPath); +- return true; +- } ++ bitmap->Clear(color); ++ return bitmap; ++} + +- void FlushPath() { +- Dump(__func__); +- SkMatrix skMatrix = ToSkMatrix(m_drawMatrix); +- SkPaint skPaint; +- skPaint.setAntiAlias(true); +- if (m_fillFullCover) +- skPaint.setBlendMode(SkBlendMode::kPlus); +- int stroke_alpha = FXARGB_A(m_strokeColor); +- if (stroke_alpha) +- m_pDriver->PaintStroke(&skPaint, &m_drawState, skMatrix); +- SkCanvas* skCanvas = m_pDriver->SkiaCanvas(); +- SkAutoCanvasRestore scoped_save_restore(skCanvas, /*doSave=*/true); +- skCanvas->concat(skMatrix); +- bool do_stroke = true; +- if (m_fillPath) { +- SkPath strokePath; +- const SkPath* fillPath = &m_skPath; +- if (stroke_alpha) { +- if (m_groupKnockout) { +- skPaint.getFillPath(m_skPath, &strokePath); +- if (m_strokeColor == m_fillColor && +- Op(m_skPath, strokePath, SkPathOp::kUnion_SkPathOp, +- &strokePath)) { +- fillPath = &strokePath; +- do_stroke = false; +- } else if (Op(m_skPath, strokePath, SkPathOp::kDifference_SkPathOp, +- &strokePath)) { +- fillPath = &strokePath; +- } +- } +- } +- skPaint.setStyle(SkPaint::kFill_Style); +- skPaint.setColor(m_fillColor); +-#ifdef _SKIA_SUPPORT_PATHS_ +- m_pDriver->PreMultiply(); +-#endif // _SKIA_SUPPORT_PATHS_ +- DebugShowSkiaDrawPath(m_pDriver.Get(), skCanvas, skPaint, *fillPath); +- skCanvas->drawPath(*fillPath, skPaint); +- } +- if (stroke_alpha && do_stroke) { +- skPaint.setStyle(SkPaint::kStroke_Style); +- skPaint.setColor(m_strokeColor); +-#ifdef _SKIA_SUPPORT_PATHS_ +- m_pDriver->PreMultiply(); +-#endif // _SKIA_SUPPORT_PATHS_ +- DebugShowSkiaDrawPath(m_pDriver.Get(), skCanvas, skPaint, m_skPath); +- skCanvas->drawPath(m_skPath, skPaint); ++bool HasRSX(pdfium::span char_pos, ++ float* scaleXPtr, ++ bool* oneAtATimePtr) { ++ bool useRSXform = false; ++ bool oneAtATime = false; ++ float scaleX = 1; ++ for (const TextCharPos& cp : char_pos) { ++ if (!cp.m_bGlyphAdjust) { ++ continue; + } +- m_drawIndex = INT_MAX; +- m_type = Accumulator::kNone; +- m_drawMatrix = CFX_Matrix(); +- } +- +- bool HasRSX(int nChars, +- const TextCharPos* pCharPos, +- float* scaleXPtr, +- bool* oneAtATimePtr) const { +- bool useRSXform = false; +- bool oneAtATime = false; +- float scaleX = 1; +- for (int index = 0; index < nChars; ++index) { +- const TextCharPos& cp = pCharPos[index]; +- if (!cp.m_bGlyphAdjust) +- continue; +- bool upright = 0 == cp.m_AdjustMatrix[1] && 0 == cp.m_AdjustMatrix[2]; +- if (cp.m_AdjustMatrix[0] != cp.m_AdjustMatrix[3]) { +- if (upright && 1 == cp.m_AdjustMatrix[3]) { +- if (1 == scaleX) +- scaleX = cp.m_AdjustMatrix[0]; +- else if (scaleX != cp.m_AdjustMatrix[0]) +- oneAtATime = true; +- } else { ++ bool upright = 0 == cp.m_AdjustMatrix[1] && 0 == cp.m_AdjustMatrix[2]; ++ if (cp.m_AdjustMatrix[0] != cp.m_AdjustMatrix[3]) { ++ if (upright && 1 == cp.m_AdjustMatrix[3]) { ++ if (1 == scaleX) { ++ scaleX = cp.m_AdjustMatrix[0]; ++ } else if (scaleX != cp.m_AdjustMatrix[0]) { + oneAtATime = true; + } +- } else if (cp.m_AdjustMatrix[1] != -cp.m_AdjustMatrix[2]) { +- oneAtATime = true; + } else { +- useRSXform = true; +- } +- } +- *oneAtATimePtr = oneAtATime; +- *scaleXPtr = oneAtATime ? 1 : scaleX; +- return oneAtATime ? false : useRSXform; +- } +- +- bool DrawText(int nChars, +- const TextCharPos* pCharPos, +- CFX_Font* pFont, +- const CFX_Matrix& matrix, +- float font_size, +- uint32_t color) { +- if (m_debugDisable) +- return false; +- Dump(__func__); +- float scaleX = 1; +- bool oneAtATime = false; +- bool hasRSX = HasRSX(nChars, pCharPos, &scaleX, &oneAtATime); +- if (oneAtATime) { +- Flush(); +- return false; +- } +- int drawIndex = SkTMin(m_drawIndex, m_commands.count()); +- if (Accumulator::kPath == m_type || drawIndex != m_commandIndex || +- (Accumulator::kText == m_type && +- (FontChanged(pFont, matrix, font_size, scaleX, color) || +- hasRSX == m_rsxform.isEmpty()))) { +- Flush(); +- } +- if (Accumulator::kText != m_type) { +- m_italicAngle = pFont->GetSubstFontItalicAngle(); +- m_charDetails.SetCount(0); +- m_rsxform.setCount(0); +- if (pFont->GetFaceRec()) +- m_pTypeFace.reset(SkSafeRef(pFont->GetDeviceCache())); +- else +- m_pTypeFace.reset(); +- m_fontSize = font_size; +- m_scaleX = scaleX; +- m_fillColor = color; +- m_drawMatrix = matrix; +- m_drawIndex = m_commandIndex; +- m_type = Accumulator::kText; +- m_pFont = pFont; +- } +- if (!hasRSX && !m_rsxform.isEmpty()) +- FlushText(); +- +- int count = m_charDetails.Count(); +- m_charDetails.SetCount(nChars + count); +- if (hasRSX) +- m_rsxform.setCount(nChars + count); +- +- SkScalar flip = m_fontSize < 0 ? -1 : 1; +- SkScalar vFlip = flip; +- if (pFont->IsVertical()) +- vFlip *= -1; +- for (int index = 0; index < nChars; ++index) { +- const TextCharPos& cp = pCharPos[index]; +- int cur_index = index + count; +- m_charDetails.SetPositionAt( +- cur_index, {cp.m_Origin.x * flip, cp.m_Origin.y * vFlip}); +- m_charDetails.SetGlyphAt(cur_index, +- static_cast(cp.m_GlyphIndex)); +- m_charDetails.SetFontCharWidthAt(cur_index, cp.m_FontCharWidth); +-#if defined(OS_MACOSX) +- if (cp.m_ExtGID) { +- m_charDetails.SetGlyphAt(cur_index, static_cast(cp.m_ExtGID)); +- } +-#endif +- } +- SkPoint delta; +- if (MatrixOffset(&matrix, &delta)) { +- for (int index = 0; index < nChars; ++index) { +- m_charDetails.OffsetPositionAt(index + count, delta.fX * flip, +- -delta.fY * flip); +- } +- } +- if (hasRSX) { +- const SkTDArray& positions = m_charDetails.GetPositions(); +- for (int index = 0; index < nChars; ++index) { +- const TextCharPos& cp = pCharPos[index]; +- SkRSXform* rsxform = &m_rsxform[index + count]; +- if (cp.m_bGlyphAdjust) { +- rsxform->fSCos = cp.m_AdjustMatrix[0]; +- rsxform->fSSin = cp.m_AdjustMatrix[1]; +- rsxform->fTx = cp.m_AdjustMatrix[0] * positions[index].fX; +- rsxform->fTy = cp.m_AdjustMatrix[1] * positions[index].fY; +- } else { +- rsxform->fSCos = 1; +- rsxform->fSSin = 0; +- rsxform->fTx = positions[index].fX; +- rsxform->fTy = positions[index].fY; +- } +- } +- } +- return true; +- } +- +- void FlushText() { +- Dump(__func__); +- SkPaint skPaint; +- skPaint.setAntiAlias(true); +- skPaint.setColor(m_fillColor); +- +- SkFont font; +- if (m_pTypeFace) { // exclude placeholder test fonts +- font.setTypeface(m_pTypeFace); +- } +- font.setHinting(SkFontHinting::kNone); +- font.setScaleX(m_scaleX); +- font.setSkewX(tanf(m_italicAngle * FX_PI / 180.0)); +- font.setSize(SkTAbs(m_fontSize)); +- font.setSubpixel(true); +- +- SkCanvas* skCanvas = m_pDriver->SkiaCanvas(); +- SkAutoCanvasRestore scoped_save_restore(skCanvas, /*doSave=*/true); +- SkScalar flip = m_fontSize < 0 ? -1 : 1; +- SkMatrix skMatrix = ToFlippedSkMatrix(m_drawMatrix, flip); +- skCanvas->concat(skMatrix); +- const SkTDArray& glyphs = m_charDetails.GetGlyphs(); +-#ifdef _SKIA_SUPPORT_PATHS_ +- m_pDriver->PreMultiply(); +-#endif // _SKIA_SUPPORT_PATHS_ +-#if SHOW_TEXT_GLYPHS +- SkTDArray text; +- // TODO(nigi): |m_glyphs| are deprecated and glyphToUnichars() takes 4 +- // parameters now. +- text.setCount(m_glyphs.count()); +- skPaint.glyphsToUnichars(m_glyphs.begin(), m_glyphs.count(), text.begin()); +- for (int i = 0; i < m_glyphs.count(); ++i) +- printf("%lc", m_glyphs[i]); +- printf("\n"); +-#endif +- +- if (m_rsxform.count()) { +- sk_sp blob = SkTextBlob::MakeFromRSXform( +- glyphs.begin(), glyphs.bytes(), m_rsxform.begin(), font, +- SkTextEncoding::kGlyphID); +- skCanvas->drawTextBlob(blob, 0, 0, skPaint); +- } else { +- const SkTDArray& positions = m_charDetails.GetPositions(); +- const SkTDArray& widths = m_charDetails.GetFontCharWidths(); +- for (int i = 0; i < m_charDetails.Count(); ++i) { +- uint32_t font_glyph_width = +- m_pFont ? m_pFont->GetGlyphWidth(glyphs[i]) : 0; +- uint32_t pdf_glyph_width = widths[i]; +- if (font_glyph_width && pdf_glyph_width && +- font_glyph_width > pdf_glyph_width) { +- font.setScaleX(SkIntToScalar(pdf_glyph_width) / font_glyph_width); +- } else { +- font.setScaleX(SkIntToScalar(1)); +- } +- sk_sp blob = SkTextBlob::MakeFromText( +- &glyphs[i], sizeof(glyphs[i]), font, SkTextEncoding::kGlyphID); +- skCanvas->drawTextBlob(blob, positions[i].fX, positions[i].fY, skPaint); +- } +- } +- +- m_drawIndex = INT_MAX; +- m_type = Accumulator::kNone; +- m_drawMatrix = CFX_Matrix(); +- m_pFont = nullptr; +- m_italicAngle = 0; +- } +- +- bool IsEmpty() const { return !m_commands.count(); } +- +- bool SetClipFill(const CFX_PathData* pPathData, +- const CFX_Matrix* pMatrix, +- int fill_mode) { +- if (m_debugDisable) +- return false; +- Dump(__func__); +- SkPath skClipPath; +- if (pPathData->GetPoints().size() == 5 || +- pPathData->GetPoints().size() == 4) { +- CFX_FloatRect rectf; +- if (pPathData->IsRect(pMatrix, &rectf)) { +- rectf.Intersect(CFX_FloatRect( +- 0, 0, +- static_cast(m_pDriver->GetDeviceCaps(FXDC_PIXEL_WIDTH)), +- static_cast(m_pDriver->GetDeviceCaps(FXDC_PIXEL_HEIGHT)))); +- FX_RECT outer = rectf.GetOuterRect(); +- // note that PDF's y-axis goes up; Skia's y-axis goes down +- skClipPath.addRect({(float)outer.left, (float)outer.bottom, +- (float)outer.right, (float)outer.top}); +- } +- } +- if (skClipPath.isEmpty()) { +- skClipPath = BuildPath(pPathData); +- skClipPath.setFillType(GetAlternateOrWindingFillType(fill_mode)); +- SkMatrix skMatrix = ToSkMatrix(*pMatrix); +- skClipPath.transform(skMatrix); +- } +- return SetClip(skClipPath); +- } +- +- bool SetClip(const SkPath& skClipPath) { +- // if a pending draw depends on clip state that is cached, flush it and draw +- if (m_commandIndex < m_commands.count()) { +- if (m_commands[m_commandIndex] == Clip::kPath && +- m_clips[m_commandIndex] == skClipPath) { +- ++m_commandIndex; +- return true; +- } +- Flush(); +- } +- while (m_clipIndex > m_commandIndex) { +- do { +- --m_clipIndex; +- ASSERT(m_clipIndex >= 0); +- } while (m_commands[m_clipIndex] != Clip::kSave); +- m_pDriver->SkiaCanvas()->restore(); +- } +- if (m_commandIndex < m_commands.count()) { +- m_commands[m_commandIndex] = Clip::kPath; +- m_clips[m_commandIndex] = skClipPath; +- } else { +- m_commands.push_back(Clip::kPath); +- m_clips.push_back(skClipPath); +- } +- ++m_commandIndex; +- return true; +- } +- +- bool SetClipStroke(const CFX_PathData* pPathData, +- const CFX_Matrix* pMatrix, +- const CFX_GraphStateData* pGraphState) { +- if (m_debugDisable) +- return false; +- Dump(__func__); +- SkPath skPath = BuildPath(pPathData); +- SkMatrix skMatrix = ToSkMatrix(*pMatrix); +- SkPaint skPaint; +- m_pDriver->PaintStroke(&skPaint, pGraphState, skMatrix); +- SkPath dst_path; +- skPaint.getFillPath(skPath, &dst_path); +- dst_path.transform(skMatrix); +- return SetClip(dst_path); +- } +- +- bool MatrixOffset(const CFX_Matrix* pMatrix, SkPoint* delta) { +- CFX_Matrix identityMatrix; +- if (!pMatrix) +- pMatrix = &identityMatrix; +- delta->set(pMatrix->e - m_drawMatrix.e, pMatrix->f - m_drawMatrix.f); +- if (!delta->fX && !delta->fY) +- return true; +- SkMatrix drawMatrix = ToSkMatrix(m_drawMatrix); +- if (!(drawMatrix.getType() & ~SkMatrix::kTranslate_Mask)) +- return true; +- SkMatrix invDrawMatrix; +- if (!drawMatrix.invert(&invDrawMatrix)) +- return false; +- SkMatrix invNewMatrix; +- SkMatrix newMatrix = ToSkMatrix(*pMatrix); +- if (!newMatrix.invert(&invNewMatrix)) +- return false; +- delta->set(invDrawMatrix.getTranslateX() - invNewMatrix.getTranslateX(), +- invDrawMatrix.getTranslateY() - invNewMatrix.getTranslateY()); +- return true; +- } +- +- // returns true if caller should apply command to skia canvas +- bool ClipSave() { +- if (m_debugDisable) +- return false; +- Dump(__func__); +- int count = m_commands.count(); +- if (m_commandIndex < count) { +- if (Clip::kSave == m_commands[m_commandIndex]) { +- ++m_commandIndex; +- return true; ++ oneAtATime = true; + } +- Flush(); +- AdjustClip(m_commandIndex); +- m_commands[m_commandIndex] = Clip::kSave; +- m_clips[m_commandIndex] = m_skEmptyPath; ++ } else if (cp.m_AdjustMatrix[1] != -cp.m_AdjustMatrix[2]) { ++ oneAtATime = true; + } else { +- AdjustClip(m_commandIndex); +- m_commands.push_back(Clip::kSave); +- m_clips.push_back(m_skEmptyPath); +- } +- ++m_commandIndex; +- return true; +- } +- +- bool ClipRestore() { +- if (m_debugDisable) +- return false; +- Dump(__func__); +- while (Clip::kSave != m_commands[--m_commandIndex]) { +- ASSERT(m_commandIndex > 0); +- } +- return true; +- } +- +- bool DrawChanged(const CFX_Matrix* pMatrix, +- const CFX_GraphStateData* pState, +- uint32_t fill_color, +- uint32_t stroke_color, +- int fill_mode, +- BlendMode blend_type, +- bool group_knockout) const { +- return MatrixChanged(pMatrix) || StateChanged(pState, m_drawState) || +- fill_color != m_fillColor || stroke_color != m_strokeColor || +- IsEvenOddFillType(m_skPath.getFillType()) || +- IsAlternateFillMode(fill_mode) || blend_type != m_blendType || +- group_knockout != m_groupKnockout; +- } +- +- bool FontChanged(CFX_Font* pFont, +- const CFX_Matrix& matrix, +- float font_size, +- float scaleX, +- uint32_t color) const { +- CFX_TypeFace* typeface = +- pFont->GetFaceRec() ? pFont->GetDeviceCache() : nullptr; +- return typeface != m_pTypeFace.get() || MatrixChanged(&matrix) || +- font_size != m_fontSize || scaleX != m_scaleX || +- color != m_fillColor || +- pFont->GetSubstFontItalicAngle() != m_italicAngle; +- } +- +- bool MatrixChanged(const CFX_Matrix* pMatrix) const { +- return pMatrix ? *pMatrix != m_drawMatrix : m_drawMatrix.IsIdentity(); +- } +- +- bool StateChanged(const CFX_GraphStateData* pState, +- const CFX_GraphStateData& refState) const { +- CFX_GraphStateData identityState; +- if (!pState) +- pState = &identityState; +- return pState->m_LineWidth != refState.m_LineWidth || +- pState->m_LineCap != refState.m_LineCap || +- pState->m_LineJoin != refState.m_LineJoin || +- pState->m_MiterLimit != refState.m_MiterLimit || +- DashChanged(pState, refState); +- } +- +- bool DashChanged(const CFX_GraphStateData* pState, +- const CFX_GraphStateData& refState) const { +- bool dashArray = pState && !pState->m_DashArray.empty(); +- if (!dashArray && refState.m_DashArray.empty()) +- return false; +- if (!dashArray || refState.m_DashArray.empty()) +- return true; +- return pState->m_DashPhase != refState.m_DashPhase || +- pState->m_DashArray != refState.m_DashArray; +- } +- +- void AdjustClip(int limit) { +- while (m_clipIndex > limit) { +- do { +- --m_clipIndex; +- ASSERT(m_clipIndex >= 0); +- } while (m_commands[m_clipIndex] != Clip::kSave); +- m_pDriver->SkiaCanvas()->restore(); +- } +- while (m_clipIndex < limit) { +- if (Clip::kSave == m_commands[m_clipIndex]) { +- m_pDriver->SkiaCanvas()->save(); +- } else { +- ASSERT(Clip::kPath == m_commands[m_clipIndex]); +- m_pDriver->SkiaCanvas()->clipPath(m_clips[m_clipIndex], +- SkClipOp::kIntersect, true); +- } +- ++m_clipIndex; +- } +- } +- +- void Flush() { +- if (m_debugDisable) +- return; +- Dump(__func__); +- if (Accumulator::kPath == m_type || Accumulator::kText == m_type) { +- AdjustClip(SkTMin(m_drawIndex, m_commands.count())); +- Accumulator::kPath == m_type ? FlushPath() : FlushText(); +- } +- } +- +- void FlushForDraw() { +- if (m_debugDisable) +- return; +- Flush(); // draw any pending text or path +- AdjustClip(m_commandIndex); // set up clip stack with any pending state +- } +- +-#if SHOW_SKIA_PATH +- void DumpPrefix(int index) const { +- if (index != m_commandIndex && index != m_drawIndex && +- index != m_clipIndex) { +- printf(" "); +- return; +- } +- printf("%c%c%c> ", index == m_commandIndex ? 'x' : '-', +- index == m_drawIndex ? 'd' : '-', index == m_clipIndex ? 'c' : '-'); +- } +- +- void DumpEndPrefix() const { +- int index = m_commands.count(); +- if (index != m_commandIndex && index > m_drawIndex && index != m_clipIndex) +- return; +- printf("%c%c%c>\n", index == m_commandIndex ? 'x' : '-', +- index <= m_drawIndex ? 'd' : '-', index == m_clipIndex ? 'c' : '-'); +- } +-#endif // SHOW_SKIA_PATH +- +- void Dump(const char* where) const { +-#if SHOW_SKIA_PATH +- if (m_debugDisable) +- return; +- printf( +- "\n%s\nSkia Save Count %d Agg Save Stack/Count %d/%d" +- " Cache Save Index/Count %d/%d\n", +- where, m_pDriver->SkiaCanvas()->getSaveCount(), +- (int)m_pDriver->stack().size(), AggSaveCount(m_pDriver), m_commandIndex, +- CacheSaveCount(m_commands, m_commandIndex)); +- printf("Cache:\n"); +-#if SHOW_SKIA_PATH_SHORTHAND +- bool dumpedPath = false; +-#endif +- for (int index = 0; index < m_commands.count(); ++index) { +-#if SHOW_SKIA_PATH_SHORTHAND +- if (Clip::kSave == m_commands[index] && dumpedPath) { +- printf("\n"); +- dumpedPath = false; +- } +-#endif +- DumpPrefix(index); +- switch (m_commands[index]) { +- case Clip::kSave: +- printf("Save %d\n", ++m_debugSaveCounter); +- break; +- case Clip::kPath: +-#if SHOW_SKIA_PATH_SHORTHAND +- printf("*"); +- dumpedPath = true; +-#else +- m_clips[index].dump(); +-#endif +- break; +- default: +- printf("unknown\n"); +- } +- } +-#if SHOW_SKIA_PATH_SHORTHAND +- if (dumpedPath) +- printf("\n"); +-#endif +- DumpEndPrefix(); +- int skCanvasSaveCount = m_pDriver->SkiaCanvas()->getSaveCount(); +- int cacheSaveCount = 1; +- ASSERT(m_clipIndex <= m_commands.count()); +- for (int index = 0; index < m_clipIndex; ++index) +- cacheSaveCount += Clip::kSave == m_commands[index]; +- ASSERT(skCanvasSaveCount == cacheSaveCount); +-#endif // SHOW_SKIA_PATH +- } +- +-#if SHOW_SKIA_PATH +- static int AggSaveCount(const UnownedPtr driver) { +- FX_RECT last; +- int aggSaveCount = 0; +- bool foundLast = false; +- for (int index = 0; index < (int)driver->stack().size(); ++index) { +- if (!driver->stack()[index]) { +- continue; +- } +- if (driver->stack()[index]->GetType() != CFX_ClipRgn::RectI) { +- aggSaveCount += 1; +- foundLast = false; +- continue; +- } +- if (!foundLast || +- memcmp(&last, &driver->stack()[index]->GetBox(), sizeof(FX_RECT))) { +- aggSaveCount += 1; +- foundLast = true; +- last = driver->stack()[index]->GetBox(); +- } +- } +- if (driver->clip_region()) { +- CFX_ClipRgn::ClipType clipType = driver->clip_region()->GetType(); +- if (clipType != CFX_ClipRgn::RectI || !foundLast || +- memcmp(&last, &driver->clip_region()->GetBox(), sizeof(FX_RECT))) { +- aggSaveCount += 1; +- } +- } +- return aggSaveCount; +- } +- +- static int CacheSaveCount(const SkTDArray& commands, +- int commandIndex) { +- int cacheSaveCount = 0; +- bool newPath = false; +- for (int index = 0; index < commandIndex; ++index) { +- if (Clip::kSave == commands[index]) { +- newPath = true; +- } else if (newPath) { +- ++cacheSaveCount; +- newPath = false; +- } ++ useRSXform = true; + } +- return cacheSaveCount; + } +-#endif ++ *oneAtATimePtr = oneAtATime; ++ *scaleXPtr = oneAtATime ? 1 : scaleX; ++ return oneAtATime ? false : useRSXform; ++} + +- void DebugCheckClip() { +-#if SHOW_SKIA_PATH +- if (m_debugDisable) +- return; +- int aggSaveCount = AggSaveCount(m_pDriver); +- int cacheSaveCount = CacheSaveCount(m_commands, m_commandIndex); +- ASSERT(m_clipIndex <= m_commands.count()); +- if (aggSaveCount != cacheSaveCount) { +- // may not signify a bug if counts don't match +- printf("aggSaveCount %d != cacheSaveCount %d\n", aggSaveCount, +- cacheSaveCount); +- DumpClipStacks(); +- } +- for (int aggIndex = 0; aggIndex < (int)m_pDriver->stack().size(); +- ++aggIndex) { +- if (!m_pDriver->stack()[aggIndex]) +- continue; +- if (m_pDriver->stack()[aggIndex]->GetType() != CFX_ClipRgn::RectI) +- continue; +- const FX_RECT& aggRect = m_pDriver->stack()[aggIndex]->GetBox(); +- SkRect skRect = SkRect::MakeLTRB(aggRect.left, aggRect.top, aggRect.right, +- aggRect.bottom); +- bool foundMatch = false; +- for (int skIndex = 0; skIndex < m_commandIndex; ++skIndex) { +- if (Clip::kPath != m_commands[skIndex]) +- continue; +- const SkPath& clip = m_clips[skIndex]; +- SkRect bounds; +- if (!clip.isRect(&bounds)) +- continue; +- bounds.roundOut(&bounds); +- if (skRect == bounds) { +- foundMatch = true; +- break; +- } +- } +- if (!foundMatch) { +- DumpClipStacks(); +- NOTREACHED(); +- } +- } +-#endif // SHOW_SKIA_PATH +- } ++} // namespace + +-#if SHOW_SKIA_PATH +- void DumpClipStacks() const { +- if (m_debugDisable) +- return; +- printf("\ncache\n"); +- for (int index = 0; index < m_commandIndex; ++index) { +- DumpPrefix(index); +- switch (m_commands[index]) { +- case Clip::kSave: +- printf("Save\n"); +- break; +- case Clip::kPath: +- m_clips[index].dump(); +- break; +- default: +- printf("unknown\n"); +- } +- } +- printf("\nagg\n"); +- for (int index = 0; index < (int)m_pDriver->stack().size(); ++index) { +- if (!m_pDriver->stack()[index]) { +- printf("null\n"); +- continue; +- } +- CFX_ClipRgn::ClipType clipType = m_pDriver->stack()[index]->GetType(); +- const FX_RECT& box = m_pDriver->stack()[index]->GetBox(); +- printf("stack rect: %d,%d,%d,%d mask=%s\n", box.left, box.top, box.right, +- box.bottom, +- CFX_ClipRgn::MaskF == clipType +- ? "1" +- : CFX_ClipRgn::RectI == clipType ? "0" : "?"); +- } +- if (m_pDriver->clip_region()) { +- const FX_RECT& box = m_pDriver->clip_region()->GetBox(); +- CFX_ClipRgn::ClipType clipType = m_pDriver->clip_region()->GetType(); +- printf("clip rect: %d,%d,%d,%d mask=%s\n", box.left, box.top, box.right, +- box.bottom, +- CFX_ClipRgn::MaskF == clipType +- ? "1" +- : CFX_ClipRgn::RectI == clipType ? "0" : "?"); +- } ++// static ++std::unique_ptr CFX_SkiaDeviceDriver::Create( ++ RetainPtr pBitmap, ++ bool bRgbByteOrder, ++ RetainPtr pBackdropBitmap, ++ bool bGroupKnockout) { ++ auto driver = pdfium::WrapUnique( ++ new CFX_SkiaDeviceDriver(std::move(pBitmap), bRgbByteOrder, ++ std::move(pBackdropBitmap), bGroupKnockout)); ++ if (!driver->m_pCanvas) { ++ return nullptr; + } +-#endif // SHOW_SKIA_PATH +- +- private: +- class CharDetail { +- public: +- CharDetail() = default; +- ~CharDetail() = default; +- +- const SkTDArray& GetPositions() const { return m_positions; } +- void SetPositionAt(int index, const SkPoint& position) { +- m_positions[index] = position; +- } +- void OffsetPositionAt(int index, SkScalar dx, SkScalar dy) { +- m_positions[index].offset(dx, dy); +- } +- const SkTDArray& GetGlyphs() const { return m_glyphs; } +- void SetGlyphAt(int index, uint16_t glyph) { m_glyphs[index] = glyph; } +- const SkTDArray& GetFontCharWidths() const { +- return m_fontCharWidths; +- } +- void SetFontCharWidthAt(int index, uint32_t width) { +- m_fontCharWidths[index] = width; +- } +- int Count() const { +- ASSERT(m_positions.count() == m_glyphs.count()); +- return m_glyphs.count(); +- } +- void SetCount(int count) { +- ASSERT(count >= 0); +- m_positions.setCount(count); +- m_glyphs.setCount(count); +- m_fontCharWidths.setCount(count); +- } +- +- private: +- SkTDArray m_positions; // accumulator for text positions +- SkTDArray m_glyphs; // accumulator for text glyphs +- // accumulator for glyphs' width defined in pdf +- SkTDArray m_fontCharWidths; +- }; +- +- SkTArray m_clips; // stack of clips that may be reused +- SkTDArray m_commands; // stack of clip-related commands +- CharDetail m_charDetails; +- SkTDArray m_rsxform; // accumulator for txt rotate/scale/translate +- SkPath m_skPath; // accumulator for path contours +- SkPath m_skEmptyPath; // used as placehold in the clips array +- UnownedPtr m_pFont; +- CFX_Matrix m_drawMatrix; +- CFX_GraphStateData m_clipState; +- CFX_GraphStateData m_drawState; +- CFX_Matrix m_clipMatrix; +- UnownedPtr const m_pDriver; +- sk_sp m_pTypeFace; +- float m_fontSize = 0; +- float m_scaleX = 0; +- uint32_t m_fillColor = 0; +- uint32_t m_strokeColor = 0; +- BlendMode m_blendType = BlendMode::kNormal; +- int m_commandIndex = 0; // active position in clip command stack +- int m_drawIndex = INT_MAX; // position of the pending path or text draw +- int m_clipIndex = 0; // position reflecting depth of canvas clip stacck +- int m_italicAngle = 0; +- Accumulator m_type = Accumulator::kNone; // type of pending draw +- bool m_fillFullCover = false; +- bool m_fillPath = false; +- bool m_groupKnockout = false; +- bool m_debugDisable = false; // turn off cache for debugging +-#if SHOW_SKIA_PATH +- public: +- mutable int m_debugSaveCounter = 0; +- static int m_debugInitCounter; +-#endif +-}; + +-#if SHOW_SKIA_PATH +-int SkiaState::m_debugInitCounter; +-#endif +- +-// convert a stroking path to scanlines +-void CFX_SkiaDeviceDriver::PaintStroke(SkPaint* spaint, +- const CFX_GraphStateData* pGraphState, +- const SkMatrix& matrix) { +- SkPaint::Cap cap; +- switch (pGraphState->m_LineCap) { +- case CFX_GraphStateData::LineCapRound: +- cap = SkPaint::kRound_Cap; +- break; +- case CFX_GraphStateData::LineCapSquare: +- cap = SkPaint::kSquare_Cap; +- break; +- default: +- cap = SkPaint::kButt_Cap; +- break; +- } +- SkPaint::Join join; +- switch (pGraphState->m_LineJoin) { +- case CFX_GraphStateData::LineJoinRound: +- join = SkPaint::kRound_Join; +- break; +- case CFX_GraphStateData::LineJoinBevel: +- join = SkPaint::kBevel_Join; +- break; +- default: +- join = SkPaint::kMiter_Join; +- break; +- } +- SkMatrix inverse; +- if (!matrix.invert(&inverse)) +- return; // give up if the matrix is degenerate, and not invertable +- inverse.set(SkMatrix::kMTransX, 0); +- inverse.set(SkMatrix::kMTransY, 0); +- SkVector deviceUnits[2] = {{0, 1}, {1, 0}}; +- inverse.mapPoints(deviceUnits, SK_ARRAY_COUNT(deviceUnits)); +- float width = +- SkTMax(pGraphState->m_LineWidth, +- SkTMin(deviceUnits[0].length(), deviceUnits[1].length())); +- if (!pGraphState->m_DashArray.empty()) { +- size_t count = (pGraphState->m_DashArray.size() + 1) / 2; +- std::unique_ptr intervals( +- FX_Alloc2D(SkScalar, count, sizeof(SkScalar))); +- // Set dash pattern +- for (size_t i = 0; i < count; i++) { +- float on = pGraphState->m_DashArray[i * 2]; +- if (on <= 0.000001f) +- on = 1.f / 10; +- float off = i * 2 + 1 == pGraphState->m_DashArray.size() +- ? on +- : pGraphState->m_DashArray[i * 2 + 1]; +- if (off < 0) +- off = 0; +- intervals.get()[i * 2] = on; +- intervals.get()[i * 2 + 1] = off; +- } +- spaint->setPathEffect(SkDashPathEffect::Make(intervals.get(), count * 2, +- pGraphState->m_DashPhase)); +- } +- spaint->setStyle(SkPaint::kStroke_Style); +- spaint->setAntiAlias(true); +- spaint->setStrokeWidth(width); +- spaint->setStrokeMiter(pGraphState->m_MiterLimit); +- spaint->setStrokeCap(cap); +- spaint->setStrokeJoin(join); ++ return driver; + } + + CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver( +- const RetainPtr& pBitmap, ++ RetainPtr pBitmap, + bool bRgbByteOrder, +- const RetainPtr& pBackdropBitmap, ++ RetainPtr pBackdropBitmap, + bool bGroupKnockout) +- : m_pBitmap(pBitmap), ++ : m_pBitmap(std::move(pBitmap)), + m_pBackdropBitmap(pBackdropBitmap), + m_pRecorder(nullptr), +- m_pCache(new SkiaState(this)), +-#ifdef _SKIA_SUPPORT_PATHS_ +- m_pClipRgn(nullptr), +- m_FillFlags(0), + m_bRgbByteOrder(bRgbByteOrder), +-#endif // _SKIA_SUPPORT_PATHS_ + m_bGroupKnockout(bGroupKnockout) { + SkBitmap skBitmap; +- ASSERT(pBitmap->GetBPP() == 8 || pBitmap->GetBPP() == 32); +- SkImageInfo imageInfo = SkImageInfo::Make( +- pBitmap->GetWidth(), pBitmap->GetHeight(), +- pBitmap->GetBPP() == 8 ? kAlpha_8_SkColorType : kN32_SkColorType, +- kOpaque_SkAlphaType); +- skBitmap.installPixels(imageInfo, pBitmap->GetBuffer(), pBitmap->GetPitch()); +- m_pCanvas = new SkCanvas(skBitmap); +-} ++ SkColorType color_type; ++ const int bpp = m_pBitmap->GetBPP(); ++ if (bpp == 8) { ++ color_type = m_pBitmap->IsAlphaFormat() || m_pBitmap->IsMaskFormat() ++ ? kAlpha_8_SkColorType ++ : kGray_8_SkColorType; ++ } else if (bpp == 24) { ++ DCHECK_EQ(m_pBitmap->GetFormat(), FXDIB_Format::kRgb); ++ ++ // Save the input bitmap as `m_pOriginalBitmap` and save its 32 bpp ++ // equivalent at `m_pBitmap` for Skia's internal process. ++ m_pOriginalBitmap = std::move(m_pBitmap); ++ m_pBitmap = pdfium::MakeRetain(); ++ if (!m_pBitmap->Copy(m_pOriginalBitmap) || ++ !m_pBitmap->ConvertFormat(FXDIB_Format::kArgb)) { ++ // Skip creating SkCanvas if we fail to create the 32 bpp bitmap to back ++ // it. ++ return; ++ } + +-#ifdef _SKIA_SUPPORT_ +-CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(int size_x, int size_y) +- : m_pBitmap(nullptr), +- m_pBackdropBitmap(nullptr), +- m_pRecorder(new SkPictureRecorder), +- m_pCache(new SkiaState(this)), +- m_bGroupKnockout(false) { +- m_pRecorder->beginRecording(SkIntToScalar(size_x), SkIntToScalar(size_y)); +- m_pCanvas = m_pRecorder->getRecordingCanvas(); ++ color_type = Get32BitSkColorType(bRgbByteOrder); ++ } else { ++ DCHECK_EQ(bpp, 32); ++ color_type = Get32BitSkColorType(bRgbByteOrder); ++ } ++ ++ SkImageInfo imageInfo = ++ SkImageInfo::Make(m_pBitmap->GetWidth(), m_pBitmap->GetHeight(), ++ color_type, kPremul_SkAlphaType); ++ skBitmap.installPixels(imageInfo, m_pBitmap->GetBuffer().data(), ++ m_pBitmap->GetPitch()); ++ m_pCanvas = new SkCanvas(skBitmap); + } + + CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(SkPictureRecorder* recorder) +- : m_pBitmap(nullptr), +- m_pBackdropBitmap(nullptr), +- m_pRecorder(recorder), +- m_pCache(new SkiaState(this)), +- m_bGroupKnockout(false) { ++ : m_pRecorder(recorder), m_bGroupKnockout(false) { + m_pCanvas = m_pRecorder->getRecordingCanvas(); ++ int width = m_pCanvas->imageInfo().width(); ++ int height = m_pCanvas->imageInfo().height(); ++ DCHECK_EQ(kUnknown_SkColorType, m_pCanvas->imageInfo().colorType()); ++ ++ constexpr uint32_t kMagenta = 0xffff00ff; ++ constexpr uint32_t kGreen = 0xff00ff00; ++ m_pBitmap = MakeDebugBitmap(width, height, kMagenta); ++ m_pBackdropBitmap = MakeDebugBitmap(width, height, kGreen); + } +-#endif // _SKIA_SUPPORT_ + + CFX_SkiaDeviceDriver::~CFX_SkiaDeviceDriver() { +- Flush(); +- if (!m_pRecorder) ++ // Convert and transfer the internal processed result to the original 24 bpp ++ // bitmap provided by the render device. ++ if (m_pOriginalBitmap && m_pBitmap->ConvertFormat(FXDIB_Format::kRgb)) { ++ int width = m_pOriginalBitmap->GetWidth(); ++ int height = m_pOriginalBitmap->GetHeight(); ++ DCHECK_EQ(width, m_pBitmap->GetWidth()); ++ DCHECK_EQ(height, m_pBitmap->GetHeight()); ++ DCHECK_EQ(FXDIB_Format::kRgb, m_pOriginalBitmap->GetFormat()); ++ m_pOriginalBitmap->TransferBitmap(/*dest_left=*/0, /*dest_top=*/0, width, ++ height, m_pBitmap, /*src_left=*/0, ++ /*src_top=*/0); ++ } ++ ++ if (!m_pRecorder) { + delete m_pCanvas; ++ } + } + +-void CFX_SkiaDeviceDriver::Flush() { +- m_pCache->Flush(); +-} ++bool CFX_SkiaDeviceDriver::DrawDeviceText( ++ pdfium::span pCharPos, ++ CFX_Font* pFont, ++ const CFX_Matrix& mtObject2Device, ++ float font_size, ++ uint32_t color, ++ const CFX_TextRenderOptions& options) { ++ // `SkTextBlob` is built from `pFont`'s font data. If `pFont` doesn't contain ++ // any font data, each text blob will have zero area to be drawn and the ++ // drawing command will be rejected. In this case, we fall back to drawing ++ // characters by their glyph bitmaps. ++ if (pFont->GetFontSpan().empty()) ++ return false; + +-void CFX_SkiaDeviceDriver::PreMultiply() { +- m_pBitmap->PreMultiply(); +-} ++ // If a glyph's default width is no less than its width defined in the PDF, ++ // draw the glyph with path since it can be scaled to avoid overlapping with ++ // the adjacent glyphs (if there are any). Otherwise, use the device driver ++ // to render the glyph without any adjustments. ++ const CFX_SubstFont* subst_font = pFont->GetSubstFont(); ++ const int subst_font_weight = ++ (subst_font && subst_font->IsBuiltInGenericFont()) ? subst_font->m_Weight ++ : 0; ++ for (const TextCharPos& cp : pCharPos) { ++ const int glyph_width = pFont->GetGlyphWidth( ++ cp.m_GlyphIndex, cp.m_FontCharWidth, subst_font_weight); ++ if (cp.m_FontCharWidth <= glyph_width) ++ return false; ++ } + +-bool CFX_SkiaDeviceDriver::DrawDeviceText(int nChars, +- const TextCharPos* pCharPos, +- CFX_Font* pFont, +- const CFX_Matrix& mtObject2Device, +- float font_size, +- uint32_t color) { +- if (m_pCache->DrawText(nChars, pCharPos, pFont, mtObject2Device, font_size, +- color)) { ++ if (TryDrawText(pCharPos, pFont, mtObject2Device, font_size, color, ++ options)) { + return true; + } + sk_sp typeface(SkSafeRef(pFont->GetDeviceCache())); +@@ -1669,123 +943,58 @@ bool CFX_SkiaDeviceDriver::DrawDeviceText(int nChars, + + SkFont font; + font.setTypeface(typeface); ++ font.setEmbolden(pFont->IsSubstFontBold()); + font.setHinting(SkFontHinting::kNone); + font.setSize(SkTAbs(font_size)); + font.setSubpixel(true); +- font.setSkewX(tanf(pFont->GetSubstFontItalicAngle() * FX_PI / 180.0)); ++ font.setSkewX(tanf(pFont->GetSubstFontItalicAngle() * FXSYS_PI / 180.0)); ++ font.setEdging(GetFontEdgingType(options)); + + SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true); +- SkScalar flip = font_size < 0 ? -1 : 1; +- SkScalar vFlip = flip; +- if (pFont->IsVertical()) +- vFlip *= -1; ++ const SkScalar flip = font_size < 0 ? -1 : 1; ++ const SkScalar vFlip = pFont->IsVertical() ? -1 : 1; + SkMatrix skMatrix = ToFlippedSkMatrix(mtObject2Device, flip); + m_pCanvas->concat(skMatrix); +- SkTDArray positions; +- positions.setCount(nChars); +- SkTDArray glyphs; +- glyphs.setCount(nChars); +- bool useRSXform = false; +- bool oneAtATime = false; +- for (int index = 0; index < nChars; ++index) { ++ DataVector positions(pCharPos.size()); ++ DataVector glyphs(pCharPos.size()); ++ ++ for (size_t index = 0; index < pCharPos.size(); ++index) { + const TextCharPos& cp = pCharPos[index]; + positions[index] = {cp.m_Origin.x * flip, cp.m_Origin.y * vFlip}; +- if (cp.m_bGlyphAdjust) { +- useRSXform = true; +- if (cp.m_AdjustMatrix[0] != cp.m_AdjustMatrix[3] || +- cp.m_AdjustMatrix[1] != -cp.m_AdjustMatrix[2]) { +- oneAtATime = true; +- } +- } + glyphs[index] = static_cast(cp.m_GlyphIndex); +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + if (cp.m_ExtGID) + glyphs[index] = static_cast(cp.m_ExtGID); + #endif + } +- if (oneAtATime) +- useRSXform = false; +-#if SHOW_TEXT_GLYPHS +- SkTDArray text; +- text.setCount(glyphs.count()); +- paint.glyphsToUnichars(glyphs.begin(), glyphs.count(), text.begin()); +- for (int i = 0; i < glyphs.count(); ++i) +- printf("%lc", text[i]); +- printf("\n"); +-#endif +-#ifdef _SKIA_SUPPORT_PATHS_ +- m_pBitmap->PreMultiply(); +-#endif // _SKIA_SUPPORT_PATHS_ +- if (useRSXform) { +- SkTDArray xforms; +- xforms.setCount(nChars); +- for (int index = 0; index < nChars; ++index) { +- const TextCharPos& cp = pCharPos[index]; +- SkRSXform* rsxform = &xforms[index]; +- if (cp.m_bGlyphAdjust) { +- rsxform->fSCos = cp.m_AdjustMatrix[0]; +- rsxform->fSSin = cp.m_AdjustMatrix[1]; +- rsxform->fTx = cp.m_AdjustMatrix[0] * positions[index].fX; +- rsxform->fTy = cp.m_AdjustMatrix[1] * positions[index].fY; +- } else { +- rsxform->fSCos = 1; +- rsxform->fSSin = 0; +- rsxform->fTx = positions[index].fX; +- rsxform->fTy = positions[index].fY; +- } +- } +- m_pCanvas->drawTextBlob( +- SkTextBlob::MakeFromRSXform(glyphs.begin(), nChars * 2, xforms.begin(), +- font, SkTextEncoding::kGlyphID), +- 0, 0, paint); +- } else if (oneAtATime) { +- for (int index = 0; index < nChars; ++index) { +- const TextCharPos& cp = pCharPos[index]; +- if (cp.m_bGlyphAdjust) { +- if (0 == cp.m_AdjustMatrix[1] && 0 == cp.m_AdjustMatrix[2] && +- 1 == cp.m_AdjustMatrix[3]) { +- font.setScaleX(cp.m_AdjustMatrix[0]); +- auto blob = +- SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]), +- font, SkTextEncoding::kGlyphID); +- m_pCanvas->drawTextBlob(blob, positions[index].fX, +- positions[index].fY, paint); +- font.setScaleX(SkIntToScalar(1)); +- } else { +- SkAutoCanvasRestore scoped_save_restore2(m_pCanvas, /*doSave=*/true); +- SkMatrix adjust; +- adjust.reset(); +- adjust.setScaleX(cp.m_AdjustMatrix[0]); +- adjust.setSkewX(cp.m_AdjustMatrix[1]); +- adjust.setSkewY(cp.m_AdjustMatrix[2]); +- adjust.setScaleY(cp.m_AdjustMatrix[3]); +- adjust.preTranslate(positions[index].fX, positions[index].fY); +- m_pCanvas->concat(adjust); +- auto blob = +- SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]), +- font, SkTextEncoding::kGlyphID); +- m_pCanvas->drawTextBlob(blob, 0, 0, paint); +- } +- } else { ++ ++ for (size_t index = 0; index < pCharPos.size(); ++index) { ++ const TextCharPos& cp = pCharPos[index]; ++ if (cp.m_bGlyphAdjust) { ++ if (0 == cp.m_AdjustMatrix[1] && 0 == cp.m_AdjustMatrix[2] && ++ 1 == cp.m_AdjustMatrix[3]) { ++ font.setScaleX(cp.m_AdjustMatrix[0]); + auto blob = + SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]), + font, SkTextEncoding::kGlyphID); + m_pCanvas->drawTextBlob(blob, positions[index].fX, positions[index].fY, + paint); +- } +- } +- } else { +- for (int index = 0; index < nChars; ++index) { +- const TextCharPos& cp = pCharPos[index]; +- uint32_t font_glyph_width = +- pFont ? pFont->GetGlyphWidth(cp.m_GlyphIndex) : 0; +- uint32_t pdf_glyph_width = cp.m_FontCharWidth; +- if (font_glyph_width && pdf_glyph_width && +- font_glyph_width > pdf_glyph_width) { +- font.setScaleX(SkIntToScalar(pdf_glyph_width) / font_glyph_width); +- } else { + font.setScaleX(SkIntToScalar(1)); ++ } else { ++ SkAutoCanvasRestore scoped_save_restore2(m_pCanvas, /*doSave=*/true); ++ SkMatrix adjust; ++ adjust.preTranslate(positions[index].fX, -positions[index].fY); ++ adjust.setScaleX(cp.m_AdjustMatrix[0]); ++ adjust.setSkewX(cp.m_AdjustMatrix[1]); ++ adjust.setSkewY(cp.m_AdjustMatrix[2]); ++ adjust.setScaleY(cp.m_AdjustMatrix[3]); ++ m_pCanvas->concat(adjust); ++ auto blob = ++ SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]), ++ font, SkTextEncoding::kGlyphID); ++ m_pCanvas->drawTextBlob(blob, 0, 0, paint); + } ++ } else { + auto blob = + SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]), font, + SkTextEncoding::kGlyphID); +@@ -1793,7 +1002,103 @@ bool CFX_SkiaDeviceDriver::DrawDeviceText(int nChars, + paint); + } + } ++ return true; ++} ++ ++// TODO(crbug.com/pdfium/1999): Merge with `DrawDeviceText()` and refactor ++// common logic. ++bool CFX_SkiaDeviceDriver::TryDrawText(pdfium::span char_pos, ++ const CFX_Font* pFont, ++ const CFX_Matrix& matrix, ++ float font_size, ++ uint32_t color, ++ const CFX_TextRenderOptions& options) { ++ float scaleX = 1; ++ bool oneAtATime = false; ++ bool hasRSX = HasRSX(char_pos, &scaleX, &oneAtATime); ++ if (oneAtATime) { ++ return false; ++ } ++ ++ m_charDetails.SetCount(0); ++ m_rsxform.resize(0); ++ ++ const size_t original_count = m_charDetails.Count(); ++ FX_SAFE_SIZE_T safe_count = original_count; ++ safe_count += char_pos.size(); ++ const size_t total_count = safe_count.ValueOrDie(); ++ m_charDetails.SetCount(total_count); ++ if (hasRSX) { ++ m_rsxform.resize(total_count); ++ } ++ ++ const SkScalar flip = font_size < 0 ? -1 : 1; ++ const SkScalar vFlip = pFont->IsVertical() ? -1 : 1; ++ for (size_t index = 0; index < char_pos.size(); ++index) { ++ const TextCharPos& cp = char_pos[index]; ++ size_t cur_index = index + original_count; ++ m_charDetails.SetPositionAt(cur_index, ++ {cp.m_Origin.x * flip, cp.m_Origin.y * vFlip}); ++ m_charDetails.SetGlyphAt(cur_index, static_cast(cp.m_GlyphIndex)); ++ m_charDetails.SetFontCharWidthAt(cur_index, cp.m_FontCharWidth); ++#if BUILDFLAG(IS_APPLE) ++ if (cp.m_ExtGID) { ++ m_charDetails.SetGlyphAt(cur_index, static_cast(cp.m_ExtGID)); ++ } ++#endif ++ } ++ if (hasRSX) { ++ const DataVector& positions = m_charDetails.GetPositions(); ++ for (size_t index = 0; index < char_pos.size(); ++index) { ++ const TextCharPos& cp = char_pos[index]; ++ SkRSXform& rsxform = m_rsxform[index + original_count]; ++ if (cp.m_bGlyphAdjust) { ++ rsxform.fSCos = cp.m_AdjustMatrix[0]; ++ rsxform.fSSin = cp.m_AdjustMatrix[1]; ++ rsxform.fTx = cp.m_AdjustMatrix[0] * positions[index].fX; ++ rsxform.fTy = -cp.m_AdjustMatrix[3] * positions[index].fY; ++ } else { ++ rsxform.fSCos = 1; ++ rsxform.fSSin = 0; ++ rsxform.fTx = positions[index].fX; ++ rsxform.fTy = positions[index].fY; ++ } ++ } ++ } ++ ++ SkPaint skPaint; ++ skPaint.setAntiAlias(true); ++ skPaint.setColor(color); ++ ++ SkFont font; ++ if (pFont->GetFaceRec()) { // exclude placeholder test fonts ++ font.setTypeface(sk_ref_sp(pFont->GetDeviceCache())); ++ } ++ font.setEmbolden(pFont->IsSubstFontBold()); ++ font.setHinting(SkFontHinting::kNone); ++ font.setScaleX(scaleX); ++ font.setSkewX(tanf(pFont->GetSubstFontItalicAngle() * FXSYS_PI / 180.0)); ++ font.setSize(SkTAbs(font_size)); ++ font.setSubpixel(true); ++ font.setEdging(GetFontEdgingType(options)); + ++ SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true); ++ m_pCanvas->concat(ToFlippedSkMatrix(matrix, flip)); ++ ++ const DataVector& glyphs = m_charDetails.GetGlyphs(); ++ if (m_rsxform.size()) { ++ sk_sp blob = SkTextBlob::MakeFromRSXform( ++ glyphs.data(), glyphs.size() * sizeof(uint16_t), m_rsxform.data(), font, ++ SkTextEncoding::kGlyphID); ++ m_pCanvas->drawTextBlob(blob, 0, 0, skPaint); ++ } else { ++ const DataVector& positions = m_charDetails.GetPositions(); ++ for (size_t i = 0; i < m_charDetails.Count(); ++i) { ++ sk_sp blob = SkTextBlob::MakeFromText( ++ &glyphs[i], sizeof(glyphs[i]), font, SkTextEncoding::kGlyphID); ++ m_pCanvas->drawTextBlob(blob, positions[i].fX, positions[i].fY, skPaint); ++ } ++ } + return true; + } + +@@ -1801,13 +1106,42 @@ int CFX_SkiaDeviceDriver::GetDriverType() const { + return 1; + } + ++bool CFX_SkiaDeviceDriver::MultiplyAlpha(float alpha) { ++ SkPaint paint; ++ paint.setAlphaf(alpha); ++ paint.setBlendMode(SkBlendMode::kDstIn); ++ m_pCanvas->drawPaint(paint); ++ return true; ++} ++ ++bool CFX_SkiaDeviceDriver::MultiplyAlpha(const RetainPtr& mask) { ++ if (!mask->IsMaskFormat()) { ++ NOTREACHED(); ++ return false; ++ } ++ ++ // Storage vector must outlive `skia_mask`. ++ DataVector dst32_storage; ++ SkBitmap skia_mask; ++ if (!Upsample(mask, dst32_storage, &skia_mask, /*forceAlpha=*/true)) { ++ return false; ++ } ++ skia_mask.setImmutable(); ++ ++ SkPaint paint; ++ paint.setBlendMode(SkBlendMode::kDstIn); ++ m_pCanvas->drawImageRect(skia_mask.asImage(), ++ SkRect::Make(m_pCanvas->imageInfo().bounds()), ++ SkSamplingOptions(), &paint); ++ return true; ++} ++ + DeviceType CFX_SkiaDeviceDriver::GetDeviceType() const { + return DeviceType::kDisplay; + } + + int CFX_SkiaDeviceDriver::GetDeviceCaps(int caps_id) const { + switch (caps_id) { +-#ifdef _SKIA_SUPPORT_ + case FXDC_PIXEL_WIDTH: + return m_pCanvas->imageInfo().width(); + case FXDC_PIXEL_HEIGHT: +@@ -1821,37 +1155,6 @@ int CFX_SkiaDeviceDriver::GetDeviceCaps(int caps_id) const { + return FXRC_GET_BITS | FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE | + FXRC_BLEND_MODE | FXRC_SOFT_CLIP | FXRC_ALPHA_OUTPUT | + FXRC_FILLSTROKE_PATH | FXRC_SHADING; +-#endif // _SKIA_SUPPORT_ +- +-#ifdef _SKIA_SUPPORT_PATHS_ +- case FXDC_PIXEL_WIDTH: +- return m_pBitmap->GetWidth(); +- case FXDC_PIXEL_HEIGHT: +- return m_pBitmap->GetHeight(); +- case FXDC_BITS_PIXEL: +- return m_pBitmap->GetBPP(); +- case FXDC_HORZ_SIZE: +- case FXDC_VERT_SIZE: +- return 0; +- case FXDC_RENDER_CAPS: { +- int flags = FXRC_GET_BITS | FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE | +- FXRC_BLEND_MODE | FXRC_SOFT_CLIP | FXRC_SHADING; +- if (m_pBitmap->HasAlpha()) { +- flags |= FXRC_ALPHA_OUTPUT; +- } else if (m_pBitmap->IsAlphaMask()) { +- if (m_pBitmap->GetBPP() == 1) { +- flags |= FXRC_BITMASK_OUTPUT; +- } else { +- flags |= FXRC_BYTEMASK_OUTPUT; +- } +- } +- if (m_pBitmap->IsCmykImage()) { +- flags |= FXRC_CMYK_OUTPUT; +- } +- return flags; +- } +-#endif // _SKIA_SUPPORT_PATHS_ +- + default: + NOTREACHED(); + return 0; +@@ -1859,212 +1162,108 @@ int CFX_SkiaDeviceDriver::GetDeviceCaps(int caps_id) const { + } + + void CFX_SkiaDeviceDriver::SaveState() { +- m_pCache->DebugCheckClip(); +- if (!m_pCache->ClipSave()) +- m_pCanvas->save(); +- +-#ifdef _SKIA_SUPPORT_PATHS_ +-#if SHOW_SKIA_PATH +- printf("SaveState %zd\n", stack().size()); +-#endif +- std::unique_ptr pClip; +- if (m_pClipRgn) +- pClip = pdfium::MakeUnique(*m_pClipRgn); +- m_StateStack.push_back(std::move(pClip)); +-#endif // _SKIA_SUPPORT_PATHS_ ++ m_pCanvas->save(); + } + + void CFX_SkiaDeviceDriver::RestoreState(bool bKeepSaved) { +-#ifdef _SKIA_SUPPORT_PATHS_ +- m_pClipRgn.reset(); +- +- if (m_StateStack.empty()) +- return; +-#else +- if (m_pCache->IsEmpty()) +- return; +-#endif +- if (!m_pCache->ClipRestore()) +- m_pCanvas->restore(); +- if (bKeepSaved && !m_pCache->ClipSave()) +- m_pCanvas->save(); +-#ifdef _SKIA_SUPPORT_PATHS_ +-#if SHOW_SKIA_PATH +- printf("RestoreState %zd %s\n", m_StateStack.size(), +- bKeepSaved ? "bKeepSaved" : ""); +-#endif ++ m_pCanvas->restore(); + if (bKeepSaved) { +- if (m_StateStack.back()) +- m_pClipRgn = pdfium::MakeUnique(*m_StateStack.back()); +- } else { +- m_pClipRgn = std::move(m_StateStack.back()); +- m_StateStack.pop_back(); ++ m_pCanvas->save(); + } +- m_pCache->DebugCheckClip(); +-#endif // _SKIA_SUPPORT_PATHS_ +-} +- +-#ifdef _SKIA_SUPPORT_PATHS_ +-void CFX_SkiaDeviceDriver::SetClipMask(const FX_RECT& clipBox, +- const SkPath& path) { +- FX_RECT path_rect(clipBox.left, clipBox.top, clipBox.right + 1, +- clipBox.bottom + 1); +- path_rect.Intersect(m_pClipRgn->GetBox()); +- auto pThisLayer = pdfium::MakeRetain(); +- pThisLayer->Create(path_rect.Width(), path_rect.Height(), FXDIB_8bppMask); +- pThisLayer->Clear(0); +- +- SkImageInfo imageInfo = +- SkImageInfo::Make(pThisLayer->GetWidth(), pThisLayer->GetHeight(), +- SkColorType::kAlpha_8_SkColorType, kOpaque_SkAlphaType); +- SkBitmap bitmap; +- bitmap.installPixels(imageInfo, pThisLayer->GetBuffer(), +- pThisLayer->GetPitch()); +- auto canvas = pdfium::MakeUnique(bitmap); +- canvas->translate( +- -path_rect.left, +- -path_rect.top); // FIXME(caryclark) wrong sign(s)? upside down? +- SkPaint paint; +- paint.setAntiAlias((m_FillFlags & FXFILL_NOPATHSMOOTH) == 0); +- canvas->drawPath(path, paint); +- m_pClipRgn->IntersectMaskF(path_rect.left, path_rect.top, pThisLayer); + } +-#endif // _SKIA_SUPPORT_PATHS_ + + bool CFX_SkiaDeviceDriver::SetClip_PathFill( +- const CFX_PathData* pPathData, // path info ++ const CFX_Path& path, // path info + const CFX_Matrix* pObject2Device, // flips object's y-axis +- int fill_mode // fill mode, WINDING or ALTERNATE +- ) { +- CFX_Matrix identity; +- const CFX_Matrix* deviceMatrix = pObject2Device ? pObject2Device : &identity; +- bool cached = m_pCache->SetClipFill(pPathData, deviceMatrix, fill_mode); +- +-#ifdef _SKIA_SUPPORT_PATHS_ +- m_FillFlags = fill_mode; +- if (!m_pClipRgn) { +- m_pClipRgn = pdfium::MakeUnique( +- GetDeviceCaps(FXDC_PIXEL_WIDTH), GetDeviceCaps(FXDC_PIXEL_HEIGHT)); +- } +-#endif // _SKIA_SUPPORT_PATHS_ +- if (pPathData->GetPoints().size() == 5 || +- pPathData->GetPoints().size() == 4) { +- CFX_FloatRect rectf; +- if (pPathData->IsRect(deviceMatrix, &rectf)) { ++ const CFX_FillRenderOptions& fill_options) { ++ m_FillOptions = fill_options; ++ const CFX_Matrix& deviceMatrix = ++ pObject2Device ? *pObject2Device : CFX_Matrix(); ++ ++ SkPath skClipPath; ++ if (path.GetPoints().size() == 5 || path.GetPoints().size() == 4) { ++ absl::optional maybe_rectf = path.GetRect(&deviceMatrix); ++ if (maybe_rectf.has_value()) { ++ CFX_FloatRect& rectf = maybe_rectf.value(); + rectf.Intersect(CFX_FloatRect(0, 0, + (float)GetDeviceCaps(FXDC_PIXEL_WIDTH), + (float)GetDeviceCaps(FXDC_PIXEL_HEIGHT))); ++ FX_RECT outer = rectf.GetOuterRect(); + // note that PDF's y-axis goes up; Skia's y-axis goes down +- if (!cached) { +- SkRect skClipRect = +- SkRect::MakeLTRB(rectf.left, rectf.bottom, rectf.right, rectf.top); +- DebugDrawSkiaClipRect(m_pCanvas, skClipRect); +- m_pCanvas->clipRect(skClipRect, SkClipOp::kIntersect, true); +- } +- +-#ifdef _SKIA_SUPPORT_PATHS_ +- FX_RECT rect = rectf.GetOuterRect(); +- m_pClipRgn->IntersectRect(rect); +-#endif // _SKIA_SUPPORT_PATHS_ +- DebugShowCanvasClip(this, m_pCanvas); +- return true; ++ skClipPath.addRect({(float)outer.left, (float)outer.bottom, ++ (float)outer.right, (float)outer.top}); + } + } +- SkPath skClipPath = BuildPath(pPathData); +- skClipPath.setFillType(GetAlternateOrWindingFillType(fill_mode)); +- SkMatrix skMatrix = ToSkMatrix(*deviceMatrix); +- skClipPath.transform(skMatrix); +- DebugShowSkiaPath(skClipPath); +- if (!cached) { +- DebugDrawSkiaClipPath(m_pCanvas, skClipPath); +- m_pCanvas->clipPath(skClipPath, SkClipOp::kIntersect, true); +- } +-#ifdef _SKIA_SUPPORT_PATHS_ +- FX_RECT clipBox(0, 0, GetDeviceCaps(FXDC_PIXEL_WIDTH), +- GetDeviceCaps(FXDC_PIXEL_HEIGHT)); +- SetClipMask(clipBox, skClipPath); +-#endif // _SKIA_SUPPORT_PATHS_ ++ if (skClipPath.isEmpty()) { ++ skClipPath = BuildPath(path); ++ skClipPath.setFillType(GetAlternateOrWindingFillType(fill_options)); ++ skClipPath.transform(ToSkMatrix(deviceMatrix)); ++ DebugShowSkiaPath(skClipPath); ++ } ++ m_pCanvas->clipPath(skClipPath, SkClipOp::kIntersect, true); + DebugShowCanvasClip(this, m_pCanvas); + return true; + } + + bool CFX_SkiaDeviceDriver::SetClip_PathStroke( +- const CFX_PathData* pPathData, // path info ++ const CFX_Path& path, // path info + const CFX_Matrix* pObject2Device, // required transformation + const CFX_GraphStateData* pGraphState // graphic state, for pen attributes +- ) { +- bool cached = m_pCache->SetClipStroke(pPathData, pObject2Device, pGraphState); +- +-#ifdef _SKIA_SUPPORT_PATHS_ +- if (!m_pClipRgn) { +- m_pClipRgn = pdfium::MakeUnique( +- GetDeviceCaps(FXDC_PIXEL_WIDTH), GetDeviceCaps(FXDC_PIXEL_HEIGHT)); +- } +-#endif // _SKIA_SUPPORT_PATHS_ +- // build path data +- SkPath skPath = BuildPath(pPathData); ++) { ++ SkPath skPath = BuildPath(path); + SkMatrix skMatrix = ToSkMatrix(*pObject2Device); + SkPaint skPaint; +- PaintStroke(&skPaint, pGraphState, skMatrix); ++ PaintStroke(&skPaint, pGraphState, skMatrix, CFX_FillRenderOptions()); + SkPath dst_path; +- skPaint.getFillPath(skPath, &dst_path); ++ skpathutils::FillPathWithPaint(skPath, skPaint, &dst_path); + dst_path.transform(skMatrix); +- if (!cached) { +- DebugDrawSkiaClipPath(m_pCanvas, dst_path); +- m_pCanvas->clipPath(dst_path, SkClipOp::kIntersect, true); +- } +-#ifdef _SKIA_SUPPORT_PATHS_ +- FX_RECT clipBox(0, 0, GetDeviceCaps(FXDC_PIXEL_WIDTH), +- GetDeviceCaps(FXDC_PIXEL_HEIGHT)); +- SetClipMask(clipBox, dst_path); +-#endif // _SKIA_SUPPORT_PATHS_ ++ m_pCanvas->clipPath(dst_path, SkClipOp::kIntersect, true); + DebugShowCanvasClip(this, m_pCanvas); + return true; + } + ++// TODO(crbug.com/pdfium/1963): `blend_type` isn't used? + bool CFX_SkiaDeviceDriver::DrawPath( +- const CFX_PathData* pPathData, // path info ++ const CFX_Path& path, // path info + const CFX_Matrix* pObject2Device, // optional transformation + const CFX_GraphStateData* pGraphState, // graphic state, for pen attributes + uint32_t fill_color, // fill color + uint32_t stroke_color, // stroke color +- int fill_mode, // fill mode, WINDING or ALTERNATE. 0 for not filled ++ const CFX_FillRenderOptions& fill_options, + BlendMode blend_type) { +- ASSERT(GetAlternateOrWindingFillMode(fill_mode) != +- kAlternateOrWindingFillModeMask); +- if (m_pCache->DrawPath(pPathData, pObject2Device, pGraphState, fill_color, +- stroke_color, fill_mode, blend_type)) { +- return true; +- } +- SkMatrix skMatrix; +- if (pObject2Device) +- skMatrix = ToSkMatrix(*pObject2Device); +- else +- skMatrix.setIdentity(); ++ m_FillOptions = fill_options; ++ ++ SkPath skia_path = BuildPath(path); ++ skia_path.setFillType(GetAlternateOrWindingFillType(fill_options)); ++ ++ SkMatrix skMatrix = pObject2Device ? ToSkMatrix(*pObject2Device) : SkMatrix(); + SkPaint skPaint; +- skPaint.setAntiAlias(true); +- if (fill_mode & FXFILL_FULLCOVER) ++ skPaint.setAntiAlias(!fill_options.aliased_path); ++ if (fill_options.full_cover) { + skPaint.setBlendMode(SkBlendMode::kPlus); ++ } + int stroke_alpha = FXARGB_A(stroke_color); +- bool is_paint_stroke = pGraphState && stroke_alpha; +- if (is_paint_stroke) +- PaintStroke(&skPaint, pGraphState, skMatrix); +- SkPath skPath = BuildPath(pPathData); ++ if (stroke_alpha) { ++ const CFX_GraphStateData& graph_state = ++ pGraphState ? *pGraphState : CFX_GraphStateData(); ++ PaintStroke(&skPaint, &graph_state, skMatrix, fill_options); ++ } ++ + SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true); + m_pCanvas->concat(skMatrix); + bool do_stroke = true; +- if (GetAlternateOrWindingFillMode(fill_mode) && fill_color) { +- skPath.setFillType(GetAlternateOrWindingFillType(fill_mode)); ++ if (fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill && ++ fill_color) { + SkPath strokePath; +- const SkPath* fillPath = &skPath; +- if (is_paint_stroke) { ++ const SkPath* fillPath = &skia_path; ++ if (stroke_alpha) { + if (m_bGroupKnockout) { +- skPaint.getFillPath(skPath, &strokePath); ++ skpathutils::FillPathWithPaint(skia_path, skPaint, &strokePath); + if (stroke_color == fill_color && +- Op(skPath, strokePath, SkPathOp::kUnion_SkPathOp, &strokePath)) { ++ Op(skia_path, strokePath, SkPathOp::kUnion_SkPathOp, &strokePath)) { + fillPath = &strokePath; + do_stroke = false; +- } else if (Op(skPath, strokePath, SkPathOp::kDifference_SkPathOp, ++ } else if (Op(skia_path, strokePath, SkPathOp::kDifference_SkPathOp, + &strokePath)) { + fillPath = &strokePath; + } +@@ -2072,20 +1271,23 @@ bool CFX_SkiaDeviceDriver::DrawPath( + } + skPaint.setStyle(SkPaint::kFill_Style); + skPaint.setColor(fill_color); +-#ifdef _SKIA_SUPPORT_PATHS_ +- m_pBitmap->PreMultiply(); +-#endif // _SKIA_SUPPORT_PATHS_ + DebugShowSkiaDrawPath(this, m_pCanvas, skPaint, *fillPath); + m_pCanvas->drawPath(*fillPath, skPaint); + } +- if (is_paint_stroke && do_stroke) { ++ if (stroke_alpha && do_stroke) { + skPaint.setStyle(SkPaint::kStroke_Style); + skPaint.setColor(stroke_color); +-#ifdef _SKIA_SUPPORT_PATHS_ +- m_pBitmap->PreMultiply(); +-#endif // _SKIA_SUPPORT_PATHS_ +- DebugShowSkiaDrawPath(this, m_pCanvas, skPaint, skPath); +- m_pCanvas->drawPath(skPath, skPaint); ++ if (!skia_path.isLastContourClosed() && IsPathAPoint(skia_path)) { ++ DCHECK_GE(skia_path.countPoints(), 1); ++ m_pCanvas->drawPoint(skia_path.getPoint(0), skPaint); ++ } else if (IsPathAPoint(skia_path) && ++ skPaint.getStrokeCap() != SkPaint::kRound_Cap) { ++ // Do nothing. A closed 0-length closed path can be rendered only if ++ // its line cap type is round. ++ } else { ++ DebugShowSkiaDrawPath(this, m_pCanvas, skPaint, skia_path); ++ m_pCanvas->drawPath(skia_path, skPaint); ++ } + } + return true; + } +@@ -2100,13 +1302,12 @@ bool CFX_SkiaDeviceDriver::DrawCosmeticLine(const CFX_PointF& ptMoveTo, + bool CFX_SkiaDeviceDriver::FillRectWithBlend(const FX_RECT& rect, + uint32_t fill_color, + BlendMode blend_type) { +- m_pCache->FlushForDraw(); + SkPaint spaint; + spaint.setAntiAlias(true); + spaint.setColor(fill_color); + spaint.setBlendMode(GetSkiaBlendMode(blend_type)); +- SkRect srect = SkRect::MakeLTRB(rect.left, SkTMin(rect.top, rect.bottom), +- rect.right, SkTMax(rect.bottom, rect.top)); ++ SkRect srect = SkRect::MakeLTRB(rect.left, std::min(rect.top, rect.bottom), ++ rect.right, std::max(rect.bottom, rect.top)); + DebugShowSkiaDrawRect(this, m_pCanvas, spaint, srect); + m_pCanvas->drawRect(srect, spaint); + return true; +@@ -2117,30 +1318,32 @@ bool CFX_SkiaDeviceDriver::DrawShading(const CPDF_ShadingPattern* pPattern, + const FX_RECT& clip_rect, + int alpha, + bool bAlphaMode) { +- m_pCache->FlushForDraw(); + ShadingType shadingType = pPattern->GetShadingType(); + if (kAxialShading != shadingType && kRadialShading != shadingType && + kCoonsPatchMeshShading != shadingType) { + // TODO(caryclark) more types + return false; + } +- int csFamily = pPattern->GetCS()->GetFamily(); +- if (PDFCS_DEVICERGB != csFamily && PDFCS_DEVICEGRAY != csFamily) ++ CPDF_ColorSpace::Family csFamily = pPattern->GetCS()->GetFamily(); ++ if (CPDF_ColorSpace::Family::kDeviceRGB != csFamily && ++ CPDF_ColorSpace::Family::kDeviceGray != csFamily) { + return false; ++ } + const std::vector>& pFuncs = + pPattern->GetFuncs(); +- int nFuncs = pFuncs.size(); ++ size_t nFuncs = pFuncs.size(); + if (nFuncs > 1) // TODO(caryclark) remove this restriction + return false; +- const CPDF_Dictionary* pDict = pPattern->GetShadingObject()->GetDict(); +- const CPDF_Array* pCoords = pDict->GetArrayFor("Coords"); ++ RetainPtr pDict = ++ pPattern->GetShadingObject()->GetDict(); ++ RetainPtr pCoords = pDict->GetArrayFor("Coords"); + if (!pCoords && kCoonsPatchMeshShading != shadingType) + return false; + // TODO(caryclark) Respect Domain[0], Domain[1]. (Don't know what they do + // yet.) +- SkTDArray skColors; +- SkTDArray skPos; +- for (int j = 0; j < nFuncs; j++) { ++ DataVector sk_colors; ++ DataVector sk_pos; ++ for (size_t j = 0; j < nFuncs; j++) { + if (!pFuncs[j]) + continue; + +@@ -2149,21 +1352,24 @@ bool CFX_SkiaDeviceDriver::DrawShading(const CPDF_ShadingPattern* pPattern, + Type 0 Sampled Functions in PostScript can also have an Order integer + in the dictionary. PDFium doesn't appear to check for this anywhere. + */ +- if (!AddSamples(pSampledFunc, &skColors, &skPos)) ++ if (!AddSamples(pSampledFunc, sk_colors, sk_pos)) { + return false; ++ } + } else if (const CPDF_ExpIntFunc* pExpIntFuc = pFuncs[j]->ToExpIntFunc()) { +- if (!AddColors(pExpIntFuc, &skColors, /*is_encode_reversed=*/false)) ++ if (!AddColors(pExpIntFuc, sk_colors, /*is_encode_reversed=*/false)) { + return false; +- skPos.push_back(0); +- skPos.push_back(1); ++ } ++ sk_pos.push_back(0); ++ sk_pos.push_back(1); + } else if (const CPDF_StitchFunc* pStitchFunc = pFuncs[j]->ToStitchFunc()) { +- if (!AddStitching(pStitchFunc, &skColors, &skPos)) ++ if (!AddStitching(pStitchFunc, sk_colors, sk_pos)) { + return false; ++ } + } else { + return false; + } + } +- const CPDF_Array* pArray = pDict->GetArrayFor("Extend"); ++ RetainPtr pArray = pDict->GetArrayFor("Extend"); + bool clipStart = !pArray || !pArray->GetIntegerAt(0); + bool clipEnd = !pArray || !pArray->GetIntegerAt(1); + SkPaint paint; +@@ -2175,15 +1381,15 @@ bool CFX_SkiaDeviceDriver::DrawShading(const CPDF_ShadingPattern* pPattern, + SkPath skClip; + SkPath skPath; + if (kAxialShading == shadingType) { +- float start_x = pCoords->GetNumberAt(0); +- float start_y = pCoords->GetNumberAt(1); +- float end_x = pCoords->GetNumberAt(2); +- float end_y = pCoords->GetNumberAt(3); ++ float start_x = pCoords->GetFloatAt(0); ++ float start_y = pCoords->GetFloatAt(1); ++ float end_x = pCoords->GetFloatAt(2); ++ float end_y = pCoords->GetFloatAt(3); + SkPoint pts[] = {{start_x, start_y}, {end_x, end_y}}; +- skMatrix.mapPoints(pts, SK_ARRAY_COUNT(pts)); +- paint.setShader( +- SkGradientShader::MakeLinear(pts, skColors.begin(), skPos.begin(), +- skColors.count(), SkTileMode::kClamp)); ++ skMatrix.mapPoints(pts, std::size(pts)); ++ paint.setShader(SkGradientShader::MakeLinear( ++ pts, sk_colors.data(), sk_pos.data(), ++ fxcrt::CollectionSize(sk_colors), SkTileMode::kClamp)); + if (clipStart || clipEnd) { + // if the gradient is horizontal or vertical, modify the draw rectangle + if (pts[0].fX == pts[1].fX) { // vertical +@@ -2192,18 +1398,18 @@ bool CFX_SkiaDeviceDriver::DrawShading(const CPDF_ShadingPattern* pPattern, + std::swap(clipStart, clipEnd); + } + if (clipStart) +- skRect.fTop = SkTMax(skRect.fTop, pts[0].fY); ++ skRect.fTop = std::max(skRect.fTop, pts[0].fY); + if (clipEnd) +- skRect.fBottom = SkTMin(skRect.fBottom, pts[1].fY); ++ skRect.fBottom = std::min(skRect.fBottom, pts[1].fY); + } else if (pts[0].fY == pts[1].fY) { // horizontal + if (pts[0].fX > pts[1].fX) { + std::swap(pts[0].fX, pts[1].fX); + std::swap(clipStart, clipEnd); + } + if (clipStart) +- skRect.fLeft = SkTMax(skRect.fLeft, pts[0].fX); ++ skRect.fLeft = std::max(skRect.fLeft, pts[0].fX); + if (clipEnd) +- skRect.fRight = SkTMin(skRect.fRight, pts[1].fX); ++ skRect.fRight = std::min(skRect.fRight, pts[1].fX); + } else { // if the gradient is angled and contained by the rect, clip + SkPoint rectPts[4] = {{skRect.fLeft, skRect.fTop}, + {skRect.fRight, skRect.fTop}, +@@ -2215,17 +1421,17 @@ bool CFX_SkiaDeviceDriver::DrawShading(const CPDF_ShadingPattern* pPattern, + skPath.addRect(skRect); + skMatrix.setIdentity(); + } else if (kRadialShading == shadingType) { +- float start_x = pCoords->GetNumberAt(0); +- float start_y = pCoords->GetNumberAt(1); +- float start_r = pCoords->GetNumberAt(2); +- float end_x = pCoords->GetNumberAt(3); +- float end_y = pCoords->GetNumberAt(4); +- float end_r = pCoords->GetNumberAt(5); ++ float start_x = pCoords->GetFloatAt(0); ++ float start_y = pCoords->GetFloatAt(1); ++ float start_r = pCoords->GetFloatAt(2); ++ float end_x = pCoords->GetFloatAt(3); ++ float end_y = pCoords->GetFloatAt(4); ++ float end_r = pCoords->GetFloatAt(5); + SkPoint pts[] = {{start_x, start_y}, {end_x, end_y}}; + + paint.setShader(SkGradientShader::MakeTwoPointConical( +- pts[0], start_r, pts[1], end_r, skColors.begin(), skPos.begin(), +- skColors.count(), SkTileMode::kClamp)); ++ pts[0], start_r, pts[1], end_r, sk_colors.data(), sk_pos.data(), ++ fxcrt::CollectionSize(sk_colors), SkTileMode::kClamp)); + if (clipStart || clipEnd) { + if (clipStart && start_r) + skClip.addCircle(pts[0].fX, pts[0].fY, start_r); +@@ -2241,12 +1447,13 @@ bool CFX_SkiaDeviceDriver::DrawShading(const CPDF_ShadingPattern* pPattern, + skPath.addRect(skRect); + skPath.transform(inverse); + } else { +- ASSERT(kCoonsPatchMeshShading == shadingType); +- const CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject()); ++ DCHECK_EQ(kCoonsPatchMeshShading, shadingType); ++ RetainPtr pStream = ++ ToStream(pPattern->GetShadingObject()); + if (!pStream) + return false; +- CPDF_MeshStream stream(shadingType, pPattern->GetFuncs(), pStream, +- pPattern->GetCS()); ++ CPDF_MeshStream stream(shadingType, pPattern->GetFuncs(), ++ std::move(pStream), pPattern->GetCS()); + if (!stream.Load()) + return false; + SkPoint cubics[12]; +@@ -2255,26 +1462,27 @@ bool CFX_SkiaDeviceDriver::DrawShading(const CPDF_ShadingPattern* pPattern, + if (!skClip.isEmpty()) + m_pCanvas->clipPath(skClip, SkClipOp::kIntersect, true); + m_pCanvas->concat(skMatrix); +- while (!stream.BitStream()->IsEOF()) { ++ while (!stream.IsEOF()) { + uint32_t flag = stream.ReadFlag(); +- int iStartPoint = flag ? 4 : 0; +- int iStartColor = flag ? 2 : 0; ++ size_t start_point = flag ? 4 : 0; ++ size_t start_color = flag ? 2 : 0; + if (flag) { +- SkPoint tempCubics[4]; +- for (int i = 0; i < (int)SK_ARRAY_COUNT(tempCubics); i++) +- tempCubics[i] = cubics[(flag * 3 + i) % 12]; +- memcpy(cubics, tempCubics, sizeof(tempCubics)); +- SkColor tempColors[2]; +- tempColors[0] = colors[flag]; +- tempColors[1] = colors[(flag + 1) % 4]; +- memcpy(colors, tempColors, sizeof(tempColors)); ++ SkPoint temp_cubics[4]; ++ for (size_t i = 0; i < std::size(temp_cubics); ++i) { ++ temp_cubics[i] = cubics[(flag * 3 + i) % 12]; ++ } ++ std::copy(std::begin(temp_cubics), std::end(temp_cubics), ++ std::begin(cubics)); ++ SkColor temp_colors[2] = {colors[flag % 4], colors[(flag + 1) % 4]}; ++ std::copy(std::begin(temp_colors), std::end(temp_colors), ++ std::begin(colors)); + } +- for (int i = iStartPoint; i < (int)SK_ARRAY_COUNT(cubics); i++) { ++ for (size_t i = start_point; i < std::size(cubics); ++i) { + CFX_PointF point = stream.ReadCoords(); + cubics[i].fX = point.x; + cubics[i].fY = point.y; + } +- for (int i = iStartColor; i < (int)SK_ARRAY_COUNT(colors); i++) { ++ for (size_t i = start_color; i < std::size(colors); ++i) { + float r; + float g; + float b; +@@ -2282,7 +1490,8 @@ bool CFX_SkiaDeviceDriver::DrawShading(const CPDF_ShadingPattern* pPattern, + colors[i] = SkColorSetARGB(0xFF, (U8CPU)(r * 255), (U8CPU)(g * 255), + (U8CPU)(b * 255)); + } +- m_pCanvas->drawPatch(cubics, colors, nullptr, paint); ++ m_pCanvas->drawPatch(cubics, colors, /*textCoords=*/nullptr, ++ SkBlendMode::kDst, paint); + } + return true; + } +@@ -2294,27 +1503,12 @@ bool CFX_SkiaDeviceDriver::DrawShading(const CPDF_ShadingPattern* pPattern, + return true; + } + +-uint8_t* CFX_SkiaDeviceDriver::GetBuffer() const { +- return m_pBitmap->GetBuffer(); +-} +- + bool CFX_SkiaDeviceDriver::GetClipBox(FX_RECT* pRect) { +-#ifdef _SKIA_SUPPORT_PATHS_ +- if (!m_pClipRgn) { +- pRect->left = pRect->top = 0; +- pRect->right = GetDeviceCaps(FXDC_PIXEL_WIDTH); +- pRect->bottom = GetDeviceCaps(FXDC_PIXEL_HEIGHT); +- return true; +- } +- *pRect = m_pClipRgn->GetBox(); +-#else +- // TODO(caryclark) call m_canvas->getClipDeviceBounds() instead +- pRect->left = 0; +- pRect->top = 0; +- const SkImageInfo& canvasSize = m_pCanvas->imageInfo(); +- pRect->right = canvasSize.width(); +- pRect->bottom = canvasSize.height(); +-#endif ++ SkIRect clip = m_pCanvas->getDeviceClipBounds(); ++ pRect->left = clip.fLeft; ++ pRect->top = clip.fTop; ++ pRect->right = clip.fRight; ++ pRect->bottom = clip.fBottom; + return true; + } + +@@ -2323,63 +1517,37 @@ bool CFX_SkiaDeviceDriver::GetDIBits(const RetainPtr& pBitmap, + int top) { + if (!m_pBitmap) + return true; +- uint8_t* srcBuffer = m_pBitmap->GetBuffer(); ++ ++ uint8_t* srcBuffer = m_pBitmap->GetBuffer().data(); + if (!srcBuffer) + return true; +-#ifdef _SKIA_SUPPORT_ +- m_pCache->FlushForDraw(); ++ + int srcWidth = m_pBitmap->GetWidth(); + int srcHeight = m_pBitmap->GetHeight(); +- int srcRowBytes = srcWidth * sizeof(uint32_t); ++ size_t srcRowBytes = m_pBitmap->GetPitch(); + SkImageInfo srcImageInfo = SkImageInfo::Make( + srcWidth, srcHeight, SkColorType::kN32_SkColorType, kPremul_SkAlphaType); + SkBitmap skSrcBitmap; + skSrcBitmap.installPixels(srcImageInfo, srcBuffer, srcRowBytes); +- uint8_t* dstBuffer = pBitmap->GetBuffer(); +- ASSERT(dstBuffer); ++ skSrcBitmap.setImmutable(); ++ ++ uint8_t* dstBuffer = pBitmap->GetBuffer().data(); ++ DCHECK(dstBuffer); ++ + int dstWidth = pBitmap->GetWidth(); + int dstHeight = pBitmap->GetHeight(); +- int dstRowBytes = dstWidth * sizeof(uint32_t); ++ size_t dstRowBytes = pBitmap->GetPitch(); + SkImageInfo dstImageInfo = SkImageInfo::Make( +- dstWidth, dstHeight, SkColorType::kN32_SkColorType, kPremul_SkAlphaType); ++ dstWidth, dstHeight, Get32BitSkColorType(m_bRgbByteOrder), ++ kPremul_SkAlphaType); + SkBitmap skDstBitmap; + skDstBitmap.installPixels(dstImageInfo, dstBuffer, dstRowBytes); ++ + SkCanvas canvas(skDstBitmap); +- canvas.drawBitmap(skSrcBitmap, left, top, nullptr); ++ canvas.drawImageRect(skSrcBitmap.asImage(), ++ SkRect::MakeXYWH(left, top, dstWidth, dstHeight), ++ SkSamplingOptions(), /*paint=*/nullptr); + return true; +-#endif // _SKIA_SUPPORT_ +- +-#ifdef _SKIA_SUPPORT_PATHS_ +- Flush(); +- m_pBitmap->UnPreMultiply(); +- FX_RECT rect(left, top, left + pBitmap->GetWidth(), +- top + pBitmap->GetHeight()); +- RetainPtr pBack; +- if (m_pBackdropBitmap) { +- pBack = m_pBackdropBitmap->Clone(&rect); +- if (!pBack) +- return true; +- +- pBack->CompositeBitmap(0, 0, pBack->GetWidth(), pBack->GetHeight(), +- m_pBitmap, 0, 0, BlendMode::kNormal, nullptr, false); +- } else { +- pBack = m_pBitmap->Clone(&rect); +- if (!pBack) +- return true; +- } +- +- bool bRet = true; +- left = std::min(left, 0); +- top = std::min(top, 0); +- if (m_bRgbByteOrder) { +- RgbByteOrderTransferBitmap(pBitmap, 0, 0, rect.Width(), rect.Height(), +- pBack, left, top); +- } else { +- bRet = pBitmap->TransferBitmap(0, 0, rect.Width(), rect.Height(), pBack, +- left, top); +- } +- return bRet; +-#endif // _SKIA_SUPPORT_PATHS_ + } + + RetainPtr CFX_SkiaDeviceDriver::GetBackDrop() { +@@ -2392,29 +1560,18 @@ bool CFX_SkiaDeviceDriver::SetDIBits(const RetainPtr& pBitmap, + int left, + int top, + BlendMode blend_type) { +- if (!m_pBitmap || !m_pBitmap->GetBuffer()) ++ if (!m_pBitmap || m_pBitmap->GetBuffer().empty()) + return true; + +-#ifdef _SKIA_SUPPORT_ + CFX_Matrix m = CFX_RenderDevice::GetFlipMatrix( + pBitmap->GetWidth(), pBitmap->GetHeight(), left, top); +- std::unique_ptr dummy; +- return StartDIBits(pBitmap, 0xFF, argb, m, FXDIB_ResampleOptions(), &dummy, +- blend_type); +-#endif // _SKIA_SUPPORT_ + +-#ifdef _SKIA_SUPPORT_PATHS_ +- Flush(); +- if (pBitmap->IsAlphaMask()) { +- return m_pBitmap->CompositeMask(left, top, src_rect.Width(), +- src_rect.Height(), pBitmap, argb, +- src_rect.left, src_rect.top, blend_type, +- m_pClipRgn.get(), m_bRgbByteOrder); +- } +- return m_pBitmap->CompositeBitmap( +- left, top, src_rect.Width(), src_rect.Height(), pBitmap, src_rect.left, +- src_rect.top, blend_type, m_pClipRgn.get(), m_bRgbByteOrder); +-#endif // _SKIA_SUPPORT_PATHS_ ++ // `bNoSmoothing` prevents linear sampling when rendering bitmaps. ++ FXDIB_ResampleOptions sampling_options; ++ sampling_options.bNoSmoothing = true; ++ ++ return StartDIBitsSkia(pBitmap, src_rect, 0xFF, argb, m, sampling_options, ++ blend_type); + } + + bool CFX_SkiaDeviceDriver::StretchDIBits(const RetainPtr& pSource, +@@ -2426,9 +1583,7 @@ bool CFX_SkiaDeviceDriver::StretchDIBits(const RetainPtr& pSource, + const FX_RECT* pClipRect, + const FXDIB_ResampleOptions& options, + BlendMode blend_type) { +-#ifdef _SKIA_SUPPORT_ +- m_pCache->FlushForDraw(); +- if (!m_pBitmap->GetBuffer()) ++ if (m_pBitmap->GetBuffer().empty()) + return true; + + CFX_Matrix m = CFX_RenderDevice::GetFlipMatrix(dest_width, dest_height, +@@ -2437,33 +1592,14 @@ bool CFX_SkiaDeviceDriver::StretchDIBits(const RetainPtr& pSource, + SkRect skClipRect = SkRect::MakeLTRB(pClipRect->left, pClipRect->bottom, + pClipRect->right, pClipRect->top); + m_pCanvas->clipRect(skClipRect, SkClipOp::kIntersect, true); +- std::unique_ptr dummy; +- return StartDIBits(pSource, 0xFF, argb, m, FXDIB_ResampleOptions(), &dummy, +- blend_type); +-#endif // _SKIA_SUPPORT_ +- +-#ifdef _SKIA_SUPPORT_PATHS_ +- if (dest_width == pSource->GetWidth() && +- dest_height == pSource->GetHeight()) { +- FX_RECT rect(0, 0, dest_width, dest_height); +- return SetDIBits(pSource, argb, rect, dest_left, dest_top, blend_type); +- } +- Flush(); +- FX_RECT dest_rect(dest_left, dest_top, dest_left + dest_width, +- dest_top + dest_height); +- dest_rect.Normalize(); +- FX_RECT dest_clip = dest_rect; +- dest_clip.Intersect(*pClipRect); +- CFX_BitmapComposer composer; +- composer.Compose(m_pBitmap, m_pClipRgn.get(), 255, argb, dest_clip, false, +- false, false, m_bRgbByteOrder, blend_type); +- dest_clip.Offset(-dest_rect.left, -dest_rect.top); +- CFX_ImageStretcher stretcher(&composer, pSource, dest_width, dest_height, +- dest_clip, options); +- if (stretcher.Start()) +- stretcher.Continue(nullptr); +- return true; +-#endif // _SKIA_SUPPORT_PATHS_ ++ ++ // `bNoSmoothing` prevents linear sampling when rendering bitmaps. ++ FXDIB_ResampleOptions sampling_options; ++ sampling_options.bNoSmoothing = true; ++ ++ return StartDIBitsSkia( ++ pSource, FX_RECT(0, 0, pSource->GetWidth(), pSource->GetHeight()), 0xFF, ++ argb, m, sampling_options, blend_type); + } + + bool CFX_SkiaDeviceDriver::StartDIBits( +@@ -2474,132 +1610,74 @@ bool CFX_SkiaDeviceDriver::StartDIBits( + const FXDIB_ResampleOptions& options, + std::unique_ptr* handle, + BlendMode blend_type) { +-#ifdef _SKIA_SUPPORT_ +- m_pCache->FlushForDraw(); +- DebugValidate(m_pBitmap, m_pBackdropBitmap); +- std::unique_ptr dst8Storage; +- std::unique_ptr dst32Storage; +- SkBitmap skBitmap; +- int width, height; +- if (!Upsample(pSource, dst8Storage, dst32Storage, &skBitmap, &width, &height, +- false)) { +- return false; +- } +- { +- SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true); +- SkMatrix skMatrix; +- SetBitmapMatrix(matrix, width, height, &skMatrix); +- m_pCanvas->concat(skMatrix); +- SkPaint paint; +- SetBitmapPaint(pSource->IsAlphaMask(), argb, bitmap_alpha, blend_type, +- &paint); +- // TODO(caryclark) Once Skia supports 8 bit src to 8 bit dst remove this +- if (m_pBitmap && m_pBitmap->GetBPP() == 8 && pSource->GetBPP() == 8) { +- SkMatrix inv; +- SkAssertResult(skMatrix.invert(&inv)); +- for (int y = 0; y < m_pBitmap->GetHeight(); ++y) { +- for (int x = 0; x < m_pBitmap->GetWidth(); ++x) { +- SkPoint src = {x + 0.5f, y + 0.5f}; +- inv.mapPoints(&src, 1); +- // TODO(caryclark) Why does the matrix map require clamping? +- src.fX = SkTMax(0.5f, SkTMin(src.fX, width - 0.5f)); +- src.fY = SkTMax(0.5f, SkTMin(src.fY, height - 0.5f)); +- m_pBitmap->SetPixel(x, y, skBitmap.getColor(src.fX, src.fY)); +- } +- } +- } else { +- m_pCanvas->drawBitmap(skBitmap, 0, 0, &paint); +- } +- } +- DebugValidate(m_pBitmap, m_pBackdropBitmap); +-#endif // _SKIA_SUPPORT_ +- +-#ifdef _SKIA_SUPPORT_PATHS_ +- Flush(); +- if (!m_pBitmap->GetBuffer()) +- return true; +- m_pBitmap->UnPreMultiply(); +- *handle = pdfium::MakeUnique( +- m_pBitmap, m_pClipRgn.get(), pSource, bitmap_alpha, argb, matrix, options, +- m_bRgbByteOrder); +-#endif // _SKIA_SUPPORT_PATHS_ +- return true; ++ return StartDIBitsSkia( ++ pSource, FX_RECT(0, 0, pSource->GetWidth(), pSource->GetHeight()), ++ bitmap_alpha, argb, matrix, options, blend_type); + } + + bool CFX_SkiaDeviceDriver::ContinueDIBits(CFX_ImageRenderer* handle, + PauseIndicatorIface* pPause) { +-#ifdef _SKIA_SUPPORT_ +- m_pCache->FlushForDraw(); + return false; +-#endif // _SKIA_SUPPORT_ +- +-#ifdef _SKIA_SUPPORT_PATHS_ +- Flush(); +- if (!m_pBitmap->GetBuffer()) { +- return true; +- } +- return handle->Continue(pPause); +-#endif // _SKIA_SUPPORT_PATHS_ +-} +- +-#if defined _SKIA_SUPPORT_ +-void CFX_SkiaDeviceDriver::PreMultiply( +- const RetainPtr& pDIBitmap) { +- pDIBitmap->PreMultiply(); + } +-#endif // _SKIA_SUPPORT_ + + void CFX_DIBitmap::PreMultiply() { +- if (this->GetBPP() != 32) ++ if (GetBPP() != 32) + return; +- void* buffer = this->GetBuffer(); ++ ++ void* buffer = GetBuffer().data(); + if (!buffer) + return; +-#if defined _SKIA_SUPPORT_PATHS_ +- Format priorFormat = m_nFormat; +- m_nFormat = Format::kPreMultiplied; +- if (priorFormat != Format::kUnPreMultiplied) ++ ++ Format prior_format = m_nFormat; ++ ForcePreMultiply(); ++ if (prior_format == Format::kPreMultiplied) + return; +-#endif +- int height = this->GetHeight(); +- int width = this->GetWidth(); +- int rowBytes = this->GetPitch(); +- SkImageInfo unpremultipliedInfo = ++ ++ int height = GetHeight(); ++ int width = GetWidth(); ++ int row_bytes = GetPitch(); ++ SkImageInfo unpremultiplied_info = + SkImageInfo::Make(width, height, kN32_SkColorType, kUnpremul_SkAlphaType); +- SkPixmap unpremultiplied(unpremultipliedInfo, buffer, rowBytes); +- SkImageInfo premultipliedInfo = ++ SkPixmap unpremultiplied(unpremultiplied_info, buffer, row_bytes); ++ SkImageInfo premultiplied_info = + SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType); +- SkPixmap premultiplied(premultipliedInfo, buffer, rowBytes); ++ SkPixmap premultiplied(premultiplied_info, buffer, row_bytes); + unpremultiplied.readPixels(premultiplied); +- this->DebugVerifyBitmapIsPreMultiplied(nullptr); + } + +-#ifdef _SKIA_SUPPORT_PATHS_ + void CFX_DIBitmap::UnPreMultiply() { +- if (this->GetBPP() != 32) ++ if (GetBPP() != 32) + return; +- void* buffer = this->GetBuffer(); ++ ++ void* buffer = GetBuffer().data(); + if (!buffer) + return; +- Format priorFormat = m_nFormat; +- m_nFormat = Format::kUnPreMultiplied; +- if (priorFormat != Format::kPreMultiplied) ++ ++ Format prior_format = m_nFormat; ++ ForceUnPreMultiply(); ++ if (prior_format == Format::kUnPreMultiplied) + return; +- this->DebugVerifyBitmapIsPreMultiplied(nullptr); +- int height = this->GetHeight(); +- int width = this->GetWidth(); +- int rowBytes = this->GetPitch(); +- SkImageInfo premultipliedInfo = ++ ++ int height = GetHeight(); ++ int width = GetWidth(); ++ int row_bytes = GetPitch(); ++ SkImageInfo premultiplied_info = + SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType); +- SkPixmap premultiplied(premultipliedInfo, buffer, rowBytes); +- SkImageInfo unpremultipliedInfo = ++ SkPixmap premultiplied(premultiplied_info, buffer, row_bytes); ++ SkImageInfo unpremultiplied_info = + SkImageInfo::Make(width, height, kN32_SkColorType, kUnpremul_SkAlphaType); +- SkPixmap unpremultiplied(unpremultipliedInfo, buffer, rowBytes); ++ SkPixmap unpremultiplied(unpremultiplied_info, buffer, row_bytes); + premultiplied.readPixels(unpremultiplied); + } +-#endif // _SKIA_SUPPORT_PATHS_ + +-#ifdef _SKIA_SUPPORT_ ++void CFX_DIBitmap::ForcePreMultiply() { ++ m_nFormat = Format::kPreMultiplied; ++} ++ ++void CFX_DIBitmap::ForceUnPreMultiply() { ++ m_nFormat = Format::kUnPreMultiplied; ++} ++ + bool CFX_SkiaDeviceDriver::DrawBitsWithMask( + const RetainPtr& pSource, + const RetainPtr& pMask, +@@ -2607,35 +1685,37 @@ bool CFX_SkiaDeviceDriver::DrawBitsWithMask( + const CFX_Matrix& matrix, + BlendMode blend_type) { + DebugValidate(m_pBitmap, m_pBackdropBitmap); +- std::unique_ptr src8Storage, mask8Storage; +- std::unique_ptr src32Storage, mask32Storage; +- SkBitmap skBitmap, skMask; +- int srcWidth, srcHeight, maskWidth, maskHeight; +- if (!Upsample(pSource, src8Storage, src32Storage, &skBitmap, &srcWidth, +- &srcHeight, false)) { ++ // Storage vectors must outlive `skBitmap` and `skMask`. ++ DataVector src32_storage; ++ DataVector mask32_storage; ++ SkBitmap skBitmap; ++ SkBitmap skMask; ++ if (!Upsample(pSource, src32_storage, &skBitmap, /*forceAlpha=*/false)) { + return false; + } +- if (!Upsample(pMask, mask8Storage, mask32Storage, &skMask, &maskWidth, +- &maskHeight, true)) { ++ if (!Upsample(pMask, mask32_storage, &skMask, /*forceAlpha=*/true)) { + return false; + } + { + SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true); ++ ++ const int src_width = pSource->GetWidth(); ++ const int src_height = pSource->GetHeight(); + SkMatrix skMatrix; +- SetBitmapMatrix(matrix, srcWidth, srcHeight, &skMatrix); ++ SetBitmapMatrix(matrix, src_width, src_height, &skMatrix); + m_pCanvas->concat(skMatrix); + SkPaint paint; +- SetBitmapPaint(pSource->IsAlphaMask(), 0xFFFFFFFF, bitmap_alpha, blend_type, +- &paint); +- sk_sp skSrc = SkImage::MakeFromBitmap(skBitmap); +- sk_sp skSrcShader = +- skSrc->makeShader(SkTileMode::kClamp, SkTileMode::kClamp); +- sk_sp skMaskImage = SkImage::MakeFromBitmap(skMask); +- sk_sp skMaskShader = +- skMaskImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp); ++ SetBitmapPaintForMerge(pSource->IsMaskFormat(), !m_FillOptions.aliased_path, ++ 0xFFFFFFFF, bitmap_alpha, blend_type, &paint); ++ sk_sp skSrc = SkImages::RasterFromBitmap(skBitmap); ++ sk_sp skSrcShader = skSrc->makeShader( ++ SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions()); ++ sk_sp skMaskImage = SkImages::RasterFromBitmap(skMask); ++ sk_sp skMaskShader = skMaskImage->makeShader( ++ SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions()); + paint.setShader( + SkShaders::Blend(SkBlendMode::kSrcIn, skMaskShader, skSrcShader)); +- SkRect r = {0, 0, SkIntToScalar(srcWidth), SkIntToScalar(srcHeight)}; ++ SkRect r = {0, 0, SkIntToScalar(src_width), SkIntToScalar(src_height)}; + m_pCanvas->drawRect(r, paint); + } + DebugValidate(m_pBitmap, m_pBackdropBitmap); +@@ -2649,7 +1729,7 @@ bool CFX_SkiaDeviceDriver::SetBitsWithMask( + int dest_top, + int bitmap_alpha, + BlendMode blend_type) { +- if (!m_pBitmap || !m_pBitmap->GetBuffer()) ++ if (!m_pBitmap || m_pBitmap->GetBuffer().empty()) + return true; + + CFX_Matrix m = CFX_RenderDevice::GetFlipMatrix( +@@ -2657,91 +1737,130 @@ bool CFX_SkiaDeviceDriver::SetBitsWithMask( + return DrawBitsWithMask(pBitmap, pMask, bitmap_alpha, m, blend_type); + } + ++void CFX_SkiaDeviceDriver::SetGroupKnockout(bool group_knockout) { ++ m_bGroupKnockout = group_knockout; ++} ++ + void CFX_SkiaDeviceDriver::Clear(uint32_t color) { + m_pCanvas->clear(color); + } +-#endif // _SKIA_SUPPORT_ + +-void CFX_SkiaDeviceDriver::Dump() const { +-#if SHOW_SKIA_PATH && defined _SKIA_SUPPORT_ +- if (m_pCache) +- m_pCache->Dump(this); +-#endif // SHOW_SKIA_PATH && defined _SKIA_SUPPORT_ +-} ++bool CFX_SkiaDeviceDriver::StartDIBitsSkia( ++ const RetainPtr& pSource, ++ const FX_RECT& src_rect, ++ int bitmap_alpha, ++ uint32_t argb, ++ const CFX_Matrix& matrix, ++ const FXDIB_ResampleOptions& options, ++ BlendMode blend_type) { ++ DebugValidate(m_pBitmap, m_pBackdropBitmap); ++ ++ // Storage vector must outlive `skBitmap`. ++ DataVector dst32_storage; ++ SkBitmap skBitmap; ++ if (!Upsample(pSource, dst32_storage, &skBitmap, /*forceAlpha=*/false)) { ++ return false; ++ } ++ skBitmap.setImmutable(); ++ ++ { ++ SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true); ++ ++ const int width = pSource->GetWidth(); ++ const int height = pSource->GetHeight(); ++ SkMatrix skMatrix; ++ SetBitmapMatrix(matrix, width, height, &skMatrix); ++ m_pCanvas->concat(skMatrix); ++ SkPaint paint; ++ SetBitmapPaint(pSource->IsMaskFormat(), !m_FillOptions.aliased_path, ++ bitmap_alpha, argb, blend_type, &paint); ++ ++ bool use_interpolate_bilinear = options.bInterpolateBilinear; ++ if (!use_interpolate_bilinear) { ++ float dest_width = ceilf(matrix.GetXUnit()); ++ float dest_height = ceilf(matrix.GetYUnit()); ++ if (pdfium::base::IsValueInRangeForNumericType(dest_width) && ++ pdfium::base::IsValueInRangeForNumericType(dest_height)) { ++ use_interpolate_bilinear = CStretchEngine::UseInterpolateBilinear( ++ options, static_cast(dest_width), ++ static_cast(dest_height), width, height); ++ } ++ } ++ SkSamplingOptions sampling_options; ++ if (use_interpolate_bilinear) { ++ sampling_options = ++ SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear); ++ } + +-#ifdef _SKIA_SUPPORT_ +-void CFX_SkiaDeviceDriver::DebugVerifyBitmapIsPreMultiplied() const { +- if (m_pBackdropBitmap) +- m_pBackdropBitmap->DebugVerifyBitmapIsPreMultiplied(nullptr); ++ m_pCanvas->drawImageRect( ++ skBitmap.asImage(), ++ SkRect::MakeLTRB(src_rect.left, src_rect.top, src_rect.right, ++ src_rect.bottom), ++ SkRect::MakeWH(src_rect.Width(), src_rect.Height()), sampling_options, ++ &paint, SkCanvas::kFast_SrcRectConstraint); ++ } ++ DebugValidate(m_pBitmap, m_pBackdropBitmap); ++ return true; + } +-#endif // _SKIA_SUPPORT_ + +-CFX_DefaultRenderDevice::CFX_DefaultRenderDevice() {} ++CFX_SkiaDeviceDriver::CharDetail::CharDetail() = default; ++CFX_SkiaDeviceDriver::CharDetail::~CharDetail() = default; + +-#ifdef _SKIA_SUPPORT_ + void CFX_DefaultRenderDevice::Clear(uint32_t color) { +- CFX_SkiaDeviceDriver* skDriver = +- static_cast(GetDeviceDriver()); +- skDriver->Clear(color); ++ static_cast(GetDeviceDriver())->Clear(color); + } + +-SkPictureRecorder* CFX_DefaultRenderDevice::CreateRecorder(int size_x, +- int size_y) { +- CFX_SkiaDeviceDriver* skDriver = new CFX_SkiaDeviceDriver(size_x, size_y); +- SetDeviceDriver(pdfium::WrapUnique(skDriver)); +- return skDriver->GetRecorder(); ++std::unique_ptr CFX_DefaultRenderDevice::CreateRecorder( ++ const SkRect& bounds) { ++ auto recorder = std::make_unique(); ++ recorder->beginRecording(bounds); ++ ++ SetDeviceDriver(std::make_unique(recorder.get())); ++ return recorder; + } +-#endif // _SKIA_SUPPORT_ + +-bool CFX_DefaultRenderDevice::Attach( +- const RetainPtr& pBitmap, ++bool CFX_DefaultRenderDevice::AttachSkiaImpl( ++ RetainPtr pBitmap, + bool bRgbByteOrder, +- const RetainPtr& pBackdropBitmap, ++ RetainPtr pBackdropBitmap, + bool bGroupKnockout) { + if (!pBitmap) + return false; + SetBitmap(pBitmap); +- SetDeviceDriver(pdfium::MakeUnique( +- pBitmap, bRgbByteOrder, pBackdropBitmap, bGroupKnockout)); ++ auto driver = ++ CFX_SkiaDeviceDriver::Create(std::move(pBitmap), bRgbByteOrder, ++ std::move(pBackdropBitmap), bGroupKnockout); ++ if (!driver) ++ return false; ++ ++ SetDeviceDriver(std::move(driver)); + return true; + } + +-#ifdef _SKIA_SUPPORT_ + bool CFX_DefaultRenderDevice::AttachRecorder(SkPictureRecorder* recorder) { + if (!recorder) + return false; +- SetDeviceDriver(pdfium::MakeUnique(recorder)); ++ SetDeviceDriver(std::make_unique(recorder)); + return true; + } +-#endif // _SKIA_SUPPORT_ + +-bool CFX_DefaultRenderDevice::Create( ++bool CFX_DefaultRenderDevice::CreateSkia( + int width, + int height, + FXDIB_Format format, +- const RetainPtr& pBackdropBitmap) { ++ RetainPtr pBackdropBitmap) { + auto pBitmap = pdfium::MakeRetain(); +- if (!pBitmap->Create(width, height, format)) { ++ if (!pBitmap->Create(width, height, format)) + return false; +- } +- SetBitmap(pBitmap); +- SetDeviceDriver(pdfium::MakeUnique( +- pBitmap, false, pBackdropBitmap, false)); +- return true; +-} + +-CFX_DefaultRenderDevice::~CFX_DefaultRenderDevice() { +- Flush(true); +-} ++ SetBitmap(pBitmap); ++ auto driver = CFX_SkiaDeviceDriver::Create(std::move(pBitmap), false, ++ std::move(pBackdropBitmap), false); ++ if (!driver) ++ return false; + +-#ifdef _SKIA_SUPPORT_ +-void CFX_DefaultRenderDevice::DebugVerifyBitmapIsPreMultiplied() const { +-#ifdef SK_DEBUG +- CFX_SkiaDeviceDriver* skDriver = +- static_cast(GetDeviceDriver()); +- if (skDriver) +- skDriver->DebugVerifyBitmapIsPreMultiplied(); +-#endif // SK_DEBUG ++ SetDeviceDriver(std::move(driver)); ++ return true; + } + + bool CFX_DefaultRenderDevice::SetBitsWithMask( +@@ -2751,34 +1870,6 @@ bool CFX_DefaultRenderDevice::SetBitsWithMask( + int top, + int bitmap_alpha, + BlendMode blend_type) { +- CFX_SkiaDeviceDriver* skDriver = +- static_cast(GetDeviceDriver()); +- if (skDriver) +- return skDriver->SetBitsWithMask(pBitmap, pMask, left, top, bitmap_alpha, +- blend_type); +- return false; +-} +-#endif // _SKIA_SUPPORT_ +- +-void CFX_DIBBase::DebugVerifyBitmapIsPreMultiplied(void* opt) const { +-#ifdef SK_DEBUG +- ASSERT(GetBPP() == 32); +- const uint32_t* buffer = (const uint32_t*)(opt ? opt : GetBuffer()); +- int width = GetWidth(); +- int height = GetHeight(); +- // verify that input is really premultiplied +- for (int y = 0; y < height; ++y) { +- const uint32_t* srcRow = buffer + y * width; +- for (int x = 0; x < width; ++x) { +- uint8_t a = SkGetPackedA32(srcRow[x]); +- uint8_t r = SkGetPackedR32(srcRow[x]); +- uint8_t g = SkGetPackedG32(srcRow[x]); +- uint8_t b = SkGetPackedB32(srcRow[x]); +- SkA32Assert(a); +- ASSERT(r <= a); +- ASSERT(g <= a); +- ASSERT(b <= a); +- } +- } +-#endif // SK_DEBUG ++ return static_cast(GetDeviceDriver()) ++ ->SetBitsWithMask(pBitmap, pMask, left, top, bitmap_alpha, blend_type); + } +diff --git a/core/fxge/skia/fx_skia_device.h b/core/fxge/skia/fx_skia_device.h +index 25717699e..beed9a308 100644 +--- a/core/fxge/skia/fx_skia_device.h ++++ b/core/fxge/skia/fx_skia_device.h +@@ -1,38 +1,45 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #ifndef CORE_FXGE_SKIA_FX_SKIA_DEVICE_H_ + #define CORE_FXGE_SKIA_FX_SKIA_DEVICE_H_ + +-#if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ ++#include + + #include +-#include + +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxge/cfx_fillrenderoptions.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/renderdevicedriver_iface.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/span.h" ++#include "third_party/skia/include/core/SkPoint.h" ++#include "third_party/skia/include/core/SkRSXform.h" + +-class CFX_ClipRgn; ++class CFX_Font; ++class CFX_Matrix; + class SkCanvas; +-class SkMatrix; +-class SkPaint; +-class SkPath; + class SkPictureRecorder; +-class SkiaState; + class TextCharPos; +-struct SkIRect; ++struct CFX_TextRenderOptions; ++ ++// Assumes Skia is not going to add non-data members to its fundamental types. ++FX_DATA_PARTITION_EXCEPTION(SkPoint); ++FX_DATA_PARTITION_EXCEPTION(SkRSXform); + + class CFX_SkiaDeviceDriver final : public RenderDeviceDriverIface { + public: +- CFX_SkiaDeviceDriver(const RetainPtr& pBitmap, +- bool bRgbByteOrder, +- const RetainPtr& pBackdropBitmap, +- bool bGroupKnockout); +-#ifdef _SKIA_SUPPORT_ ++ static std::unique_ptr Create( ++ RetainPtr pBitmap, ++ bool bRgbByteOrder, ++ RetainPtr pBackdropBitmap, ++ bool bGroupKnockout); ++ + explicit CFX_SkiaDeviceDriver(SkPictureRecorder* recorder); +- CFX_SkiaDeviceDriver(int size_x, int size_y); +-#endif + ~CFX_SkiaDeviceDriver() override; + + /** Options */ +@@ -45,25 +52,26 @@ class CFX_SkiaDeviceDriver final : public RenderDeviceDriverIface { + + /** Set clipping path using filled region */ + bool SetClip_PathFill( +- const CFX_PathData* pPathData, // path info +- const CFX_Matrix* pObject2Device, // optional transformation +- int fill_mode) override; // fill mode, WINDING or ALTERNATE ++ const CFX_Path& path, // path info ++ const CFX_Matrix* pObject2Device, // optional transformation ++ const CFX_FillRenderOptions& fill_options) // fill options ++ override; + + /** Set clipping path using stroked region */ + bool SetClip_PathStroke( +- const CFX_PathData* pPathData, // path info ++ const CFX_Path& path, // path info + const CFX_Matrix* pObject2Device, // required transformation + const CFX_GraphStateData* + pGraphState) // graphic state, for pen attributes + override; + + /** Draw a path */ +- bool DrawPath(const CFX_PathData* pPathData, ++ bool DrawPath(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState, + uint32_t fill_color, + uint32_t stroke_color, +- int fill_mode, ++ const CFX_FillRenderOptions& fill_options, + BlendMode blend_type) override; + + bool FillRectWithBlend(const FX_RECT& rect, +@@ -91,18 +99,13 @@ class CFX_SkiaDeviceDriver final : public RenderDeviceDriverIface { + int dest_left, + int dest_top, + BlendMode blend_type) override; +-#ifdef _SKIA_SUPPORT_ + bool SetBitsWithMask(const RetainPtr& pBitmap, + const RetainPtr& pMask, + int dest_left, + int dest_top, + int bitmap_alpha, + BlendMode blend_type) override; +-#endif +- +-#ifdef _SKIA_SUPPORT_PATHS_ +- void SetClipMask(const FX_RECT& clipBox, const SkPath& skClipPath); +-#endif ++ void SetGroupKnockout(bool group_knockout) override; + + bool StretchDIBits(const RetainPtr& pBitmap, + uint32_t color, +@@ -131,12 +134,12 @@ class CFX_SkiaDeviceDriver final : public RenderDeviceDriverIface { + const CFX_Matrix& matrix, + BlendMode blend_type); + +- bool DrawDeviceText(int nChars, +- const TextCharPos* pCharPos, ++ bool DrawDeviceText(pdfium::span pCharPos, + CFX_Font* pFont, + const CFX_Matrix& mtObject2Device, + float font_size, +- uint32_t color) override; ++ uint32_t color, ++ const CFX_TextRenderOptions& options) override; + + int GetDriverType() const override; + +@@ -146,43 +149,83 @@ class CFX_SkiaDeviceDriver final : public RenderDeviceDriverIface { + int alpha, + bool bAlphaMode) override; + +- virtual uint8_t* GetBuffer() const; ++ bool MultiplyAlpha(float alpha) override; ++ bool MultiplyAlpha(const RetainPtr& mask) override; + +- void PaintStroke(SkPaint* spaint, +- const CFX_GraphStateData* pGraphState, +- const SkMatrix& matrix); + void Clear(uint32_t color); +- void Flush() override; +- SkPictureRecorder* GetRecorder() const { return m_pRecorder; } +- void PreMultiply(); +- static void PreMultiply(const RetainPtr& pDIBitmap); +- SkCanvas* SkiaCanvas() { return m_pCanvas; } +- void DebugVerifyBitmapIsPreMultiplied() const; + void Dump() const; + +- bool GetGroupKnockout() const { return m_bGroupKnockout; } ++ private: ++ class CharDetail { ++ public: ++ CharDetail(); ++ ~CharDetail(); ++ ++ const DataVector& GetPositions() const { return m_positions; } ++ void SetPositionAt(size_t index, const SkPoint& position) { ++ m_positions[index] = position; ++ } ++ const DataVector& GetGlyphs() const { return m_glyphs; } ++ void SetGlyphAt(size_t index, uint16_t glyph) { m_glyphs[index] = glyph; } ++ const DataVector& GetFontCharWidths() const { ++ return m_fontCharWidths; ++ } ++ void SetFontCharWidthAt(size_t index, uint32_t width) { ++ m_fontCharWidths[index] = width; ++ } ++ size_t Count() const { ++ DCHECK_EQ(m_positions.size(), m_glyphs.size()); ++ return m_glyphs.size(); ++ } ++ void SetCount(size_t count) { ++ m_positions.resize(count); ++ m_glyphs.resize(count); ++ m_fontCharWidths.resize(count); ++ } ++ ++ private: ++ DataVector m_positions; // accumulator for text positions ++ DataVector m_glyphs; // accumulator for text glyphs ++ // accumulator for glyphs' width defined in pdf ++ DataVector m_fontCharWidths; ++ }; ++ ++ CFX_SkiaDeviceDriver(RetainPtr pBitmap, ++ bool bRgbByteOrder, ++ RetainPtr pBackdropBitmap, ++ bool bGroupKnockout); + +-#ifdef _SKIA_SUPPORT_PATHS_ +- const CFX_ClipRgn* clip_region() const { return m_pClipRgn.get(); } +- const std::vector>& stack() const { +- return m_StateStack; +- } +-#endif ++ bool TryDrawText(pdfium::span char_pos, ++ const CFX_Font* pFont, ++ const CFX_Matrix& matrix, ++ float font_size, ++ uint32_t color, ++ const CFX_TextRenderOptions& options); ++ ++ bool StartDIBitsSkia(const RetainPtr& pBitmap, ++ const FX_RECT& src_rect, ++ int bitmap_alpha, ++ uint32_t color, ++ const CFX_Matrix& matrix, ++ const FXDIB_ResampleOptions& options, ++ BlendMode blend_type); + +- private: + RetainPtr m_pBitmap; + RetainPtr m_pBackdropBitmap; ++ ++ // The input bitmap passed by the render device. Only used when the input ++ // bitmap is 24 bpp and cannot be directly used as the back of a SkCanvas. ++ RetainPtr m_pOriginalBitmap; ++ + SkCanvas* m_pCanvas; + SkPictureRecorder* const m_pRecorder; +- std::unique_ptr m_pCache; +-#ifdef _SKIA_SUPPORT_PATHS_ +- std::unique_ptr m_pClipRgn; +- std::vector> m_StateStack; +- int m_FillFlags; ++ CFX_FillRenderOptions m_FillOptions; + bool m_bRgbByteOrder; +-#endif + bool m_bGroupKnockout; ++ ++ CharDetail m_charDetails; ++ // accumulator for txt rotate/scale/translate ++ DataVector m_rsxform; + }; +-#endif // defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_ + + #endif // CORE_FXGE_SKIA_FX_SKIA_DEVICE_H_ +diff --git a/core/fxge/skia/fx_skia_device_embeddertest.cpp b/core/fxge/skia/fx_skia_device_embeddertest.cpp +index 6631e4d4d..517d86369 100644 +--- a/core/fxge/skia/fx_skia_device_embeddertest.cpp ++++ b/core/fxge/skia/fx_skia_device_embeddertest.cpp +@@ -1,13 +1,18 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include "core/fxge/skia/fx_skia_device.h" ++ ++#include ++ + #include "core/fxge/cfx_defaultrenderdevice.h" ++#include "core/fxge/cfx_fillrenderoptions.h" + #include "core/fxge/cfx_font.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/cfx_renderdevice.h" +-#include "core/fxge/skia/fx_skia_device.h" ++#include "core/fxge/cfx_textrenderoptions.h" + #include "core/fxge/text_char_pos.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "public/cpp/fpdf_scopers.h" +@@ -40,56 +45,60 @@ void CommonTest(CFX_SkiaDeviceDriver* driver, const State& state) { + TextCharPos charPos[1]; + charPos[0].m_Origin = CFX_PointF(0, 1); + charPos[0].m_GlyphIndex = 1; +- charPos[0].m_FontCharWidth = 4u; ++ charPos[0].m_FontCharWidth = 4; + + CFX_Font font; + float fontSize = 1; +- CFX_PathData clipPath, clipPath2; ++ CFX_Path clipPath; ++ CFX_Path clipPath2; + clipPath.AppendRect(0, 0, 3, 1); + clipPath2.AppendRect(0, 0, 2, 1); + CFX_Matrix clipMatrix; + CFX_Matrix clipMatrix2(1, 0, 0, 1, 0, 1); + driver->SaveState(); +- CFX_PathData path1; ++ CFX_Path path1; + path1.AppendRect(0, 0, 1, 2); + + CFX_Matrix matrix; + CFX_Matrix matrix2; + matrix2.Translate(1, 0); + CFX_GraphStateData graphState; ++ static constexpr CFX_TextRenderOptions kTextOptions; + if (state.m_save == State::Save::kYes) + driver->SaveState(); + if (state.m_clip != State::Clip::kNo) +- driver->SetClip_PathFill(&clipPath, &clipMatrix, 0); ++ driver->SetClip_PathFill(clipPath, &clipMatrix, CFX_FillRenderOptions()); + if (state.m_graphic == State::Graphic::kPath) { +- driver->DrawPath(&path1, &matrix, &graphState, 0xFF112233, 0, +- FXFILL_WINDING, BlendMode::kNormal); ++ driver->DrawPath(path1, &matrix, &graphState, 0xFF112233, 0, ++ CFX_FillRenderOptions::WindingOptions(), ++ BlendMode::kNormal); + } else if (state.m_graphic == State::Graphic::kText) { +- driver->DrawDeviceText(SK_ARRAY_COUNT(charPos), charPos, &font, matrix, +- fontSize, 0xFF445566); ++ driver->DrawDeviceText(charPos, &font, matrix, fontSize, 0xFF445566, ++ kTextOptions); + } + if (state.m_save == State::Save::kYes) + driver->RestoreState(true); +- CFX_PathData path2; ++ CFX_Path path2; + path2.AppendRect(0, 0, 2, 2); + if (state.m_change == State::Change::kYes) { + if (state.m_graphic == State::Graphic::kPath) +- graphState.m_LineCap = CFX_GraphStateData::LineCapRound; ++ graphState.m_LineCap = CFX_GraphStateData::LineCap::kRound; + else if (state.m_graphic == State::Graphic::kText) + fontSize = 2; + } + if (state.m_clip == State::Clip::kSame) +- driver->SetClip_PathFill(&clipPath, &clipMatrix, 0); ++ driver->SetClip_PathFill(clipPath, &clipMatrix, CFX_FillRenderOptions()); + else if (state.m_clip == State::Clip::kDifferentPath) +- driver->SetClip_PathFill(&clipPath2, &clipMatrix, 0); ++ driver->SetClip_PathFill(clipPath2, &clipMatrix, CFX_FillRenderOptions()); + else if (state.m_clip == State::Clip::kDifferentMatrix) +- driver->SetClip_PathFill(&clipPath, &clipMatrix2, 0); ++ driver->SetClip_PathFill(clipPath, &clipMatrix2, CFX_FillRenderOptions()); + if (state.m_graphic == State::Graphic::kPath) { +- driver->DrawPath(&path2, &matrix2, &graphState, 0xFF112233, 0, +- FXFILL_WINDING, BlendMode::kNormal); ++ driver->DrawPath(path2, &matrix2, &graphState, 0xFF112233, 0, ++ CFX_FillRenderOptions::WindingOptions(), ++ BlendMode::kNormal); + } else if (state.m_graphic == State::Graphic::kText) { +- driver->DrawDeviceText(SK_ARRAY_COUNT(charPos), charPos, &font, matrix2, +- fontSize, 0xFF445566); ++ driver->DrawDeviceText(charPos, &font, matrix2, fontSize, 0xFF445566, ++ kTextOptions); + } + if (state.m_save == State::Save::kYes) + driver->RestoreState(false); +@@ -97,22 +106,22 @@ void CommonTest(CFX_SkiaDeviceDriver* driver, const State& state) { + } + + void OutOfSequenceClipTest(CFX_SkiaDeviceDriver* driver, const State&) { +- CFX_PathData clipPath; ++ CFX_Path clipPath; + clipPath.AppendRect(1, 0, 3, 1); + CFX_Matrix clipMatrix; + driver->SaveState(); +- driver->SetClip_PathFill(&clipPath, &clipMatrix, 0); ++ driver->SetClip_PathFill(clipPath, &clipMatrix, CFX_FillRenderOptions()); + driver->RestoreState(true); + driver->SaveState(); +- driver->SetClip_PathFill(&clipPath, &clipMatrix, 0); ++ driver->SetClip_PathFill(clipPath, &clipMatrix, CFX_FillRenderOptions()); + driver->RestoreState(false); + driver->RestoreState(false); + + driver->SaveState(); + driver->SaveState(); +- driver->SetClip_PathFill(&clipPath, &clipMatrix, 0); ++ driver->SetClip_PathFill(clipPath, &clipMatrix, CFX_FillRenderOptions()); + driver->RestoreState(true); +- driver->SetClip_PathFill(&clipPath, &clipMatrix, 0); ++ driver->SetClip_PathFill(clipPath, &clipMatrix, CFX_FillRenderOptions()); + driver->RestoreState(false); + driver->RestoreState(false); + } +@@ -124,27 +133,25 @@ void Harness(void (*Test)(CFX_SkiaDeviceDriver*, const State&), + ScopedFPDFBitmap bitmap(FPDFBitmap_Create(kWidth, kHeight, 1)); + ASSERT_TRUE(bitmap); + FPDFBitmap_FillRect(bitmap.get(), 0, 0, kWidth, kHeight, 0x00000000); +- CFX_DefaultRenderDevice device; + RetainPtr pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap.get())); +- device.Attach(pBitmap, false, nullptr, false); +- auto* driver = static_cast(device.GetDeviceDriver()); +- (*Test)(driver, state); +- driver->Flush(); ++ auto driver = CFX_SkiaDeviceDriver::Create(pBitmap, false, nullptr, false); ++ ASSERT_TRUE(driver); ++ (*Test)(driver.get(), state); + uint32_t pixel = pBitmap->GetPixel(0, 0); + EXPECT_EQ(state.m_pixel, pixel); +-#ifdef SK_DEBUG +- if (!driver) // force dump to be linked in so it can be called from debugger +- driver->Dump(); +-#endif + } + + } // namespace + + TEST(fxge, SkiaStateEmpty) { ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return; + Harness(&EmptyTest, {}); + } + + TEST(fxge, SkiaStatePath) { ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return; + Harness(&CommonTest, {State::Change::kNo, State::Save::kYes, + State::Clip::kSame, State::Graphic::kPath, 0xFF112233}); + Harness(&CommonTest, +@@ -158,17 +165,20 @@ TEST(fxge, SkiaStatePath) { + State::Graphic::kPath, 0xFF112233}); + } + +-#ifdef _SKIA_SUPPORT_ + // TODO(crbug.com/pdfium/11): Fix this test and enable. + TEST(fxge, DISABLED_SkiaStateText) { ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return; ++ + Harness(&CommonTest, + {State::Change::kNo, State::Save::kYes, State::Clip::kDifferentMatrix, + State::Graphic::kText, 0xFF445566}); + Harness(&CommonTest, {State::Change::kNo, State::Save::kYes, + State::Clip::kSame, State::Graphic::kText, 0xFF445566}); + } +-#endif + + TEST(fxge, SkiaStateOOSClip) { ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return; + Harness(&OutOfSequenceClipTest, {}); + } +diff --git a/core/fxge/systemfontinfo_iface.h b/core/fxge/systemfontinfo_iface.h +index 9a6fbc7c0..3e661b863 100644 +--- a/core/fxge/systemfontinfo_iface.h ++++ b/core/fxge/systemfontinfo_iface.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,8 +7,11 @@ + #ifndef CORE_FXGE_SYSTEMFONTINFO_IFACE_H_ + #define CORE_FXGE_SYSTEMFONTINFO_IFACE_H_ + +-#include ++#include ++#include + ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/fx_codepage_forward.h" + #include "core/fxge/cfx_fontmapper.h" + #include "third_party/base/span.h" + +@@ -17,24 +20,20 @@ constexpr uint32_t kTableTTCF = CFX_FontMapper::MakeTag('t', 't', 'c', 'f'); + + class SystemFontInfoIface { + public: +- static std::unique_ptr CreateDefault( +- const char** pUserPaths); +- + virtual ~SystemFontInfoIface() = default; + + virtual bool EnumFontList(CFX_FontMapper* pMapper) = 0; + virtual void* MapFont(int weight, + bool bItalic, +- int charset, ++ FX_Charset charset, + int pitch_family, +- const char* face) = 0; +- virtual void* GetFont(const char* face) = 0; +- virtual uint32_t GetFontData(void* hFont, +- uint32_t table, +- pdfium::span buffer) = 0; ++ const ByteString& face) = 0; ++ virtual void* GetFont(const ByteString& face) = 0; ++ virtual size_t GetFontData(void* hFont, ++ uint32_t table, ++ pdfium::span buffer) = 0; + virtual bool GetFaceName(void* hFont, ByteString* name) = 0; +- virtual bool GetFontCharset(void* hFont, int* charset) = 0; +- virtual int GetFaceIndex(void* hFont); ++ virtual bool GetFontCharset(void* hFont, FX_Charset* charset) = 0; + virtual void DeleteFont(void* hFont) = 0; + }; + +diff --git a/core/fxge/text_char_pos.cpp b/core/fxge/text_char_pos.cpp +index cf88b96f6..52ad4c1e0 100644 +--- a/core/fxge/text_char_pos.cpp ++++ b/core/fxge/text_char_pos.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,3 +11,13 @@ TextCharPos::TextCharPos() = default; + TextCharPos::TextCharPos(const TextCharPos&) = default; + + TextCharPos::~TextCharPos() = default; ++ ++CFX_Matrix TextCharPos::GetEffectiveMatrix(const CFX_Matrix& matrix) const { ++ CFX_Matrix new_matrix; ++ if (m_bGlyphAdjust) { ++ new_matrix = CFX_Matrix(m_AdjustMatrix[0], m_AdjustMatrix[1], ++ m_AdjustMatrix[2], m_AdjustMatrix[3], 0, 0); ++ } ++ new_matrix.Concat(matrix); ++ return new_matrix; ++} +diff --git a/core/fxge/text_char_pos.h b/core/fxge/text_char_pos.h +index c211be8ef..0877a265c 100644 +--- a/core/fxge/text_char_pos.h ++++ b/core/fxge/text_char_pos.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,7 @@ + #ifndef CORE_FXGE_TEXT_CHAR_POS_H_ + #define CORE_FXGE_TEXT_CHAR_POS_H_ + ++#include "build/build_config.h" + #include "core/fxcrt/fx_coordinates.h" + + class TextCharPos { +@@ -15,17 +16,19 @@ class TextCharPos { + TextCharPos(const TextCharPos&); + ~TextCharPos(); + ++ CFX_Matrix GetEffectiveMatrix(const CFX_Matrix& matrix) const; ++ + CFX_PointF m_Origin; + uint32_t m_Unicode = 0; + uint32_t m_GlyphIndex = 0; +- uint32_t m_FontCharWidth = 0; +-#if defined(OS_MACOSX) ++ int m_FontCharWidth = 0; ++#if BUILDFLAG(IS_APPLE) + uint32_t m_ExtGID = 0; + #endif + int32_t m_FallbackFontPosition = 0; + bool m_bGlyphAdjust = false; + bool m_bFontStyle = false; +- float m_AdjustMatrix[4]; ++ float m_AdjustMatrix[4] = {}; + }; + + #endif // CORE_FXGE_TEXT_CHAR_POS_H_ +diff --git a/core/fxge/text_glyph_pos.cpp b/core/fxge/text_glyph_pos.cpp +index 3e3c4ad5e..d887d08e7 100644 +--- a/core/fxge/text_glyph_pos.cpp ++++ b/core/fxge/text_glyph_pos.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -15,18 +15,19 @@ TextGlyphPos::TextGlyphPos(const TextGlyphPos&) = default; + + TextGlyphPos::~TextGlyphPos() = default; + +-Optional TextGlyphPos::GetOrigin(const CFX_Point& offset) const { ++absl::optional TextGlyphPos::GetOrigin( ++ const CFX_Point& offset) const { + FX_SAFE_INT32 left = m_Origin.x; + left += m_pGlyph->left(); + left -= offset.x; + if (!left.IsValid()) +- return {}; ++ return absl::nullopt; + + FX_SAFE_INT32 top = m_Origin.y; + top -= m_pGlyph->top(); + top -= offset.y; + if (!top.IsValid()) +- return {}; ++ return absl::nullopt; + + return CFX_Point(left.ValueOrDie(), top.ValueOrDie()); + } +diff --git a/core/fxge/text_glyph_pos.h b/core/fxge/text_glyph_pos.h +index b78643249..36935d844 100644 +--- a/core/fxge/text_glyph_pos.h ++++ b/core/fxge/text_glyph_pos.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,7 +10,7 @@ + #include "core/fxcrt/fx_coordinates.h" + + #include "core/fxcrt/unowned_ptr.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CFX_GlyphBitmap; + +@@ -20,11 +20,11 @@ class TextGlyphPos { + TextGlyphPos(const TextGlyphPos&); + ~TextGlyphPos(); + +- Optional GetOrigin(const CFX_Point& offset) const; ++ absl::optional GetOrigin(const CFX_Point& offset) const; + + UnownedPtr m_pGlyph; + CFX_Point m_Origin; +- CFX_PointF m_fOrigin; ++ CFX_PointF m_fDeviceOrigin; + }; + + #endif // CORE_FXGE_TEXT_GLYPH_POS_H_ +diff --git a/core/fxge/win32/DEPS b/core/fxge/win32/DEPS +new file mode 100644 +index 000000000..fa830d115 +--- /dev/null ++++ b/core/fxge/win32/DEPS +@@ -0,0 +1,5 @@ ++specific_include_rules = { ++ 'cgdi_device_driver.cpp': [ ++ '+third_party/agg23/agg_clip_liang_barsky.h', ++ ] ++} +diff --git a/core/fxge/win32/cfx_psfonttracker.cpp b/core/fxge/win32/cfx_psfonttracker.cpp +new file mode 100644 +index 000000000..103ff6df4 +--- /dev/null ++++ b/core/fxge/win32/cfx_psfonttracker.cpp +@@ -0,0 +1,31 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxge/win32/cfx_psfonttracker.h" ++ ++#include "core/fxge/cfx_font.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" ++ ++CFX_PSFontTracker::CFX_PSFontTracker() = default; ++ ++CFX_PSFontTracker::~CFX_PSFontTracker() = default; ++ ++void CFX_PSFontTracker::AddFontObject(const CFX_Font* font) { ++ uint64_t tag = font->GetObjectTag(); ++ [[maybe_unused]] bool inserted; ++ if (tag != 0) { ++ inserted = seen_font_tags_.insert(tag).second; ++ } else { ++ inserted = seen_font_ptrs_.insert(UnownedPtr(font)).second; ++ } ++ DCHECK(inserted); ++} ++ ++bool CFX_PSFontTracker::SeenFontObject(const CFX_Font* font) const { ++ uint64_t tag = font->GetObjectTag(); ++ if (tag != 0) ++ return pdfium::Contains(seen_font_tags_, tag); ++ return pdfium::Contains(seen_font_ptrs_, font); ++} +diff --git a/core/fxge/win32/cfx_psfonttracker.h b/core/fxge/win32/cfx_psfonttracker.h +new file mode 100644 +index 000000000..e015f580f +--- /dev/null ++++ b/core/fxge/win32/cfx_psfonttracker.h +@@ -0,0 +1,35 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXGE_WIN32_CFX_PSFONTTRACKER_H_ ++#define CORE_FXGE_WIN32_CFX_PSFONTTRACKER_H_ ++ ++#include ++ ++#include ++#include ++ ++#include "core/fxcrt/unowned_ptr.h" ++ ++class CFX_Font; ++ ++class CFX_PSFontTracker { ++ public: ++ CFX_PSFontTracker(); ++ ~CFX_PSFontTracker(); ++ ++ void AddFontObject(const CFX_Font* font); ++ bool SeenFontObject(const CFX_Font* font) const; ++ ++ private: ++ // Tracks font objects via tags, so if two CFX_Font instances are for the same ++ // PDF object, then they are deduplicated. ++ std::set seen_font_tags_; ++ ++ // For fonts without valid tags, e.g. ones created in-memory, track them by ++ // pointer. ++ std::set, std::less<>> seen_font_ptrs_; ++}; ++ ++#endif // CORE_FXGE_WIN32_CFX_PSFONTTRACKER_H_ +diff --git a/core/fxge/win32/cfx_psrenderer.cpp b/core/fxge/win32/cfx_psrenderer.cpp +index f9f7de261..ae168f4b7 100644 +--- a/core/fxge/win32/cfx_psrenderer.cpp ++++ b/core/fxge/win32/cfx_psrenderer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,61 +6,249 @@ + + #include "core/fxge/win32/cfx_psrenderer.h" + ++#include ++#include ++ + #include ++#include + #include +-#include ++#include + #include + +-#include "core/fxcrt/maybe_owned.h" ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/fx_extension.h" ++#include "core/fxcrt/fx_memory.h" ++#include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/span_util.h" ++#include "core/fxge/cfx_fillrenderoptions.h" ++#include "core/fxge/cfx_font.h" + #include "core/fxge/cfx_fontcache.h" + #include "core/fxge/cfx_gemodule.h" + #include "core/fxge/cfx_glyphcache.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/cfx_renderdevice.h" + #include "core/fxge/dib/cfx_dibextractor.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "core/fxge/freetype/fx_freetype.h" + #include "core/fxge/text_char_pos.h" +-#include "core/fxge/win32/cpsoutput.h" +-#include "third_party/base/ptr_util.h" +- +-struct PSGlyph { +- UnownedPtr m_pFont; +- uint32_t m_GlyphIndex; +- bool m_bGlyphAdjust; +- float m_AdjustMatrix[4]; +-}; ++#include "core/fxge/win32/cfx_psfonttracker.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/numerics/safe_conversions.h" ++ ++namespace { ++ ++bool CanEmbed(CFX_Font* font) { ++ FT_UShort fstype = FT_Get_FSType_Flags(font->GetFaceRec()); ++ return (fstype & (FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING | ++ FT_FSTYPE_BITMAP_EMBEDDING_ONLY)) == 0; ++} ++ ++absl::optional GenerateType42SfntData( ++ const ByteString& psname, ++ pdfium::span font_data) { ++ if (font_data.empty()) ++ return absl::nullopt; ++ ++ // Per Type 42 font spec. ++ constexpr size_t kMaxSfntStringSize = 65535; ++ if (font_data.size() > kMaxSfntStringSize) { ++ // TODO(thestig): Fonts that are too big need to be written out in sections. ++ return absl::nullopt; ++ } ++ ++ // Each byte is written as 2 ASCIIHex characters, so really 64 chars per line. ++ constexpr size_t kMaxBytesPerLine = 32; ++ fxcrt::ostringstream output; ++ output << "/" << psname << "_sfnts [\n<\n"; ++ size_t bytes_per_line = 0; ++ char buf[2]; ++ for (uint8_t datum : font_data) { ++ FXSYS_IntToTwoHexChars(datum, buf); ++ output << buf[0]; ++ output << buf[1]; ++ bytes_per_line++; ++ if (bytes_per_line == kMaxBytesPerLine) { ++ output << "\n"; ++ bytes_per_line = 0; ++ } ++ } ++ ++ // Pad with ASCIIHex NUL character per Type 42 font spec if needed. ++ if (!FX_IsOdd(font_data.size())) ++ output << "00"; ++ ++ output << "\n>\n] def\n"; ++ return ByteString(output); ++} ++ ++// The value to use with GenerateType42FontDictionary() below, and the max ++// number of entries supported for non-CID fonts. ++// Also used to avoid buggy fonts by writing out at least this many entries, ++// per note in Poppler's Type 42 generation code. ++constexpr size_t kGlyphsPerDescendantFont = 256; ++ ++ByteString GenerateType42FontDictionary(const ByteString& psname, ++ const FX_RECT& bbox, ++ size_t num_glyphs, ++ size_t glyphs_per_descendant_font) { ++ DCHECK_LE(glyphs_per_descendant_font, kGlyphsPerDescendantFont); ++ CHECK_GT(glyphs_per_descendant_font, 0u); ++ ++ const size_t descendant_font_count = ++ (num_glyphs + glyphs_per_descendant_font - 1) / ++ glyphs_per_descendant_font; ++ ++ fxcrt::ostringstream output; ++ for (size_t i = 0; i < descendant_font_count; ++i) { ++ output << "8 dict begin\n"; ++ output << "/FontType 42 def\n"; ++ output << "/FontMatrix [1 0 0 1 0 0] def\n"; ++ output << "/FontName /" << psname << "_" << i << " def\n"; ++ ++ output << "/Encoding " << glyphs_per_descendant_font << " array\n"; ++ for (size_t j = 0, pos = i * glyphs_per_descendant_font; ++ j < glyphs_per_descendant_font; ++j, ++pos) { ++ if (pos >= num_glyphs) ++ break; ++ ++ output << ByteString::Format("dup %d /c%02x put\n", j, j); ++ } ++ output << "readonly def\n"; ++ ++ // Note: `bbox` is LTRB, while /FontBBox is LBRT. Writing it out as LTRB ++ // gets the correct values. ++ output << "/FontBBox [" << bbox.left << " " << bbox.top << " " << bbox.right ++ << " " << bbox.bottom << "] def\n"; ++ ++ output << "/PaintType 0 def\n"; ++ ++ output << "/CharStrings " << glyphs_per_descendant_font + 1 ++ << " dict dup begin\n"; ++ output << "/.notdef 0 def\n"; ++ for (size_t j = 0, pos = i * glyphs_per_descendant_font; ++ j < glyphs_per_descendant_font; ++j, ++pos) { ++ if (pos >= num_glyphs) ++ break; ++ ++ output << ByteString::Format("/c%02x %d def\n", j, pos); ++ } ++ output << "end readonly def\n"; ++ ++ output << "/sfnts " << psname << "_sfnts def\n"; ++ output << "FontName currentdict end definefont pop\n"; ++ } ++ ++ output << "6 dict begin\n"; ++ output << "/FontName /" << psname << " def\n"; ++ output << "/FontType 0 def\n"; ++ output << "/FontMatrix [1 0 0 1 0 0] def\n"; ++ output << "/FMapType 2 def\n"; ++ ++ output << "/Encoding [\n"; ++ for (size_t i = 0; i < descendant_font_count; ++i) ++ output << i << "\n"; ++ output << "] def\n"; ++ ++ output << "/FDepVector [\n"; ++ for (size_t i = 0; i < descendant_font_count; ++i) ++ output << "/" << psname << "_" << i << " findfont\n"; ++ output << "] def\n"; ++ ++ output << "FontName currentdict end definefont pop\n"; ++ output << "%%EndResource\n"; ++ ++ return ByteString(output); ++} + +-class CPSFont { +- public: +- int m_nGlyphs; +- PSGlyph m_Glyphs[256]; ++ByteString GenerateType42FontData(const CFX_Font* font) { ++ const FXFT_FaceRec* font_face_rec = font->GetFaceRec(); ++ if (!font_face_rec) ++ return ByteString(); ++ ++ const ByteString psname = font->GetPsName(); ++ DCHECK(!psname.IsEmpty()); ++ ++ absl::optional sfnt_data = ++ GenerateType42SfntData(psname, font->GetFontSpan()); ++ if (!sfnt_data.has_value()) ++ return ByteString(); ++ ++ ByteString output = "%%BeginResource: font "; ++ output += psname; ++ output += "\n"; ++ output += sfnt_data.value(); ++ output += GenerateType42FontDictionary(psname, font->GetRawBBox().value(), ++ font_face_rec->num_glyphs, ++ kGlyphsPerDescendantFont); ++ return output; ++} ++ ++} // namespace ++ ++struct CFX_PSRenderer::Glyph { ++ Glyph(CFX_Font* font, uint32_t glyph_index) ++ : font(font), glyph_index(glyph_index) {} ++ Glyph(const Glyph& other) = delete; ++ Glyph& operator=(const Glyph&) = delete; ++ ~Glyph() = default; ++ ++ UnownedPtr const font; ++ const uint32_t glyph_index; ++ absl::optional> adjust_matrix; + }; + +-CFX_PSRenderer::CFX_PSRenderer(const EncoderIface* pEncoderIface) +- : m_pEncoderIface(pEncoderIface) {} ++CFX_PSRenderer::FaxCompressResult::FaxCompressResult() = default; ++ ++CFX_PSRenderer::FaxCompressResult::FaxCompressResult( ++ FaxCompressResult&&) noexcept = default; ++ ++CFX_PSRenderer::FaxCompressResult& CFX_PSRenderer::FaxCompressResult::operator=( ++ FaxCompressResult&&) noexcept = default; ++ ++CFX_PSRenderer::FaxCompressResult::~FaxCompressResult() = default; ++ ++CFX_PSRenderer::PSCompressResult::PSCompressResult() = default; + +-CFX_PSRenderer::~CFX_PSRenderer() = default; ++CFX_PSRenderer::PSCompressResult::PSCompressResult( ++ PSCompressResult&&) noexcept = default; ++ ++CFX_PSRenderer::PSCompressResult& CFX_PSRenderer::PSCompressResult::operator=( ++ PSCompressResult&&) noexcept = default; ++ ++CFX_PSRenderer::PSCompressResult::~PSCompressResult() = default; ++ ++CFX_PSRenderer::CFX_PSRenderer(CFX_PSFontTracker* font_tracker, ++ const EncoderIface* encoder_iface) ++ : m_pFontTracker(font_tracker), m_pEncoderIface(encoder_iface) { ++ DCHECK(m_pFontTracker); ++} ++ ++CFX_PSRenderer::~CFX_PSRenderer() { ++ EndRendering(); ++} + + void CFX_PSRenderer::Init(const RetainPtr& pStream, +- int pslevel, ++ RenderingLevel level, + int width, +- int height, +- bool bCmykOutput) { +- m_PSLevel = pslevel; ++ int height) { ++ DCHECK(pStream); ++ ++ m_Level = level; + m_pStream = pStream; + m_ClipBox.left = 0; + m_ClipBox.top = 0; + m_ClipBox.right = width; + m_ClipBox.bottom = height; +- m_bCmykOutput = bCmykOutput; + } + +-bool CFX_PSRenderer::StartRendering() { ++void CFX_PSRenderer::StartRendering() { + if (m_bInited) +- return true; ++ return; + +- static const char init_str[] = ++ static const char kInitStr[] = + "\nsave\n/im/initmatrix load def\n" + "/n/newpath load def/m/moveto load def/l/lineto load def/c/curveto load " + "def/h/closepath load def\n" +@@ -74,30 +262,44 @@ bool CFX_PSRenderer::StartRendering() { + "load def\n" + "/cm/concat load def/Cm/currentmatrix load def/mx/matrix load " + "def/sm/setmatrix load def\n"; +- m_pStream->WriteString(init_str); ++ WriteString(kInitStr); + m_bInited = true; +- return true; + } + + void CFX_PSRenderer::EndRendering() { + if (!m_bInited) + return; + +- m_pStream->WriteString("\nrestore\n"); ++ WriteString("\nrestore\n"); + m_bInited = false; ++ ++ // Flush `m_PreambleOutput` if it is not empty. ++ std::streamoff preamble_pos = m_PreambleOutput.tellp(); ++ if (preamble_pos > 0) { ++ m_pStream->WriteBlock( ++ {reinterpret_cast(m_PreambleOutput.str().c_str()), ++ pdfium::base::checked_cast(preamble_pos)}); ++ m_PreambleOutput.str(""); ++ } ++ ++ // Flush `m_Output`. It's never empty because of the WriteString() call above. ++ m_pStream->WriteBlock( ++ {reinterpret_cast(m_Output.str().c_str()), ++ pdfium::base::checked_cast(std::streamoff(m_Output.tellp()))}); ++ m_Output.str(""); + } + + void CFX_PSRenderer::SaveState() { + StartRendering(); +- m_pStream->WriteString("q\n"); ++ WriteString("q\n"); + m_ClipBoxStack.push_back(m_ClipBox); + } + + void CFX_PSRenderer::RestoreState(bool bKeepSaved) { + StartRendering(); +- m_pStream->WriteString("Q\n"); ++ WriteString("Q\n"); + if (bKeepSaved) +- m_pStream->WriteString("q\n"); ++ WriteString("q\n"); + + m_bColorSet = false; + m_bGraphStateSet = false; +@@ -109,31 +311,31 @@ void CFX_PSRenderer::RestoreState(bool bKeepSaved) { + m_ClipBoxStack.pop_back(); + } + +-void CFX_PSRenderer::OutputPath(const CFX_PathData* pPathData, ++void CFX_PSRenderer::OutputPath(const CFX_Path& path, + const CFX_Matrix* pObject2Device) { +- std::ostringstream buf; +- size_t size = pPathData->GetPoints().size(); ++ fxcrt::ostringstream buf; ++ size_t size = path.GetPoints().size(); + + for (size_t i = 0; i < size; i++) { +- FXPT_TYPE type = pPathData->GetType(i); +- bool closing = pPathData->IsClosingFigure(i); +- CFX_PointF pos = pPathData->GetPoint(i); ++ CFX_Path::Point::Type type = path.GetType(i); ++ bool closing = path.IsClosingFigure(i); ++ CFX_PointF pos = path.GetPoint(i); + if (pObject2Device) + pos = pObject2Device->Transform(pos); + + buf << pos.x << " " << pos.y; + switch (type) { +- case FXPT_TYPE::MoveTo: ++ case CFX_Path::Point::Type::kMove: + buf << " m "; + break; +- case FXPT_TYPE::LineTo: ++ case CFX_Path::Point::Type::kLine: + buf << " l "; + if (closing) + buf << "h "; + break; +- case FXPT_TYPE::BezierTo: { +- CFX_PointF pos1 = pPathData->GetPoint(i + 1); +- CFX_PointF pos2 = pPathData->GetPoint(i + 2); ++ case CFX_Path::Point::Type::kBezier: { ++ CFX_PointF pos1 = path.GetPoint(i + 1); ++ CFX_PointF pos2 = path.GetPoint(i + 2); + if (pObject2Device) { + pos1 = pObject2Device->Transform(pos1); + pos2 = pObject2Device->Transform(pos2); +@@ -148,15 +350,16 @@ void CFX_PSRenderer::OutputPath(const CFX_PathData* pPathData, + } + } + } +- WriteToStream(&buf); ++ WriteStream(buf); + } + +-void CFX_PSRenderer::SetClip_PathFill(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- int fill_mode) { ++void CFX_PSRenderer::SetClip_PathFill( ++ const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_FillRenderOptions& fill_options) { + StartRendering(); +- OutputPath(pPathData, pObject2Device); +- CFX_FloatRect rect = pPathData->GetBoundingBox(); ++ OutputPath(path, pObject2Device); ++ CFX_FloatRect rect = path.GetBoundingBox(); + if (pObject2Device) + rect = pObject2Device->TransformRect(rect); + +@@ -165,38 +368,38 @@ void CFX_PSRenderer::SetClip_PathFill(const CFX_PathData* pPathData, + m_ClipBox.top = static_cast(rect.top + rect.bottom); + m_ClipBox.bottom = static_cast(rect.bottom); + +- m_pStream->WriteString("W"); +- if ((fill_mode & 3) != FXFILL_WINDING) +- m_pStream->WriteString("*"); +- m_pStream->WriteString(" n\n"); ++ WriteString("W"); ++ if (fill_options.fill_type != CFX_FillRenderOptions::FillType::kWinding) ++ WriteString("*"); ++ WriteString(" n\n"); + } + +-void CFX_PSRenderer::SetClip_PathStroke(const CFX_PathData* pPathData, ++void CFX_PSRenderer::SetClip_PathStroke(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState) { + StartRendering(); + SetGraphState(pGraphState); + +- std::ostringstream buf; ++ fxcrt::ostringstream buf; + buf << "mx Cm [" << pObject2Device->a << " " << pObject2Device->b << " " + << pObject2Device->c << " " << pObject2Device->d << " " + << pObject2Device->e << " " << pObject2Device->f << "]cm "; +- WriteToStream(&buf); ++ WriteStream(buf); + +- OutputPath(pPathData, nullptr); +- CFX_FloatRect rect = pPathData->GetBoundingBox(pGraphState->m_LineWidth, +- pGraphState->m_MiterLimit); ++ OutputPath(path, nullptr); ++ CFX_FloatRect rect = path.GetBoundingBoxForStrokePath( ++ pGraphState->m_LineWidth, pGraphState->m_MiterLimit); + m_ClipBox.Intersect(pObject2Device->TransformRect(rect).GetOuterRect()); + +- m_pStream->WriteString("strokepath W n sm\n"); ++ WriteString("strokepath W n sm\n"); + } + +-bool CFX_PSRenderer::DrawPath(const CFX_PathData* pPathData, ++bool CFX_PSRenderer::DrawPath(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState, + uint32_t fill_color, + uint32_t stroke_color, +- int fill_mode) { ++ const CFX_FillRenderOptions& fill_options) { + StartRendering(); + int fill_alpha = FXARGB_A(fill_color); + int stroke_alpha = FXARGB_A(stroke_color); +@@ -210,43 +413,45 @@ bool CFX_PSRenderer::DrawPath(const CFX_PathData* pPathData, + if (stroke_alpha) { + SetGraphState(pGraphState); + if (pObject2Device) { +- std::ostringstream buf; ++ fxcrt::ostringstream buf; + buf << "mx Cm [" << pObject2Device->a << " " << pObject2Device->b << " " + << pObject2Device->c << " " << pObject2Device->d << " " + << pObject2Device->e << " " << pObject2Device->f << "]cm "; +- WriteToStream(&buf); ++ WriteStream(buf); + } + } + +- OutputPath(pPathData, stroke_alpha ? nullptr : pObject2Device); +- if (fill_mode && fill_alpha) { ++ OutputPath(path, stroke_alpha ? nullptr : pObject2Device); ++ if (fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill && ++ fill_alpha) { + SetColor(fill_color); +- if ((fill_mode & 3) == FXFILL_WINDING) { ++ if (fill_options.fill_type == CFX_FillRenderOptions::FillType::kWinding) { + if (stroke_alpha) +- m_pStream->WriteString("q f Q "); ++ WriteString("q f Q "); + else +- m_pStream->WriteString("f"); +- } else if ((fill_mode & 3) == FXFILL_ALTERNATE) { ++ WriteString("f"); ++ } else if (fill_options.fill_type == ++ CFX_FillRenderOptions::FillType::kEvenOdd) { + if (stroke_alpha) +- m_pStream->WriteString("q F Q "); ++ WriteString("q F Q "); + else +- m_pStream->WriteString("F"); ++ WriteString("F"); + } + } + + if (stroke_alpha) { + SetColor(stroke_color); +- m_pStream->WriteString("s"); ++ WriteString("s"); + if (pObject2Device) +- m_pStream->WriteString(" sm"); ++ WriteString(" sm"); + } + +- m_pStream->WriteString("\n"); ++ WriteString("\n"); + return true; + } + + void CFX_PSRenderer::SetGraphState(const CFX_GraphStateData* pGraphState) { +- std::ostringstream buf; ++ fxcrt::ostringstream buf; + if (!m_bGraphStateSet || + m_CurGraphState.m_LineCap != pGraphState->m_LineCap) { + buf << static_cast(pGraphState->m_LineCap) << " J\n"; +@@ -272,7 +477,7 @@ void CFX_PSRenderer::SetGraphState(const CFX_GraphStateData* pGraphState) { + } + m_CurGraphState = *pGraphState; + m_bGraphStateSet = true; +- WriteToStream(&buf); ++ WriteStream(buf); + } + + bool CFX_PSRenderer::SetDIBits(const RetainPtr& pSource, +@@ -306,38 +511,29 @@ bool CFX_PSRenderer::DrawDIBits(const RetainPtr& pSource, + if ((matrix.a == 0 && matrix.b == 0) || (matrix.c == 0 && matrix.d == 0)) + return true; + +- if (pSource->HasAlpha()) ++ if (pSource->IsAlphaFormat()) + return false; + + int alpha = FXARGB_A(color); +- if (pSource->IsAlphaMask() && (alpha < 255 || pSource->GetBPP() != 1)) ++ if (pSource->IsMaskFormat() && (alpha < 255 || pSource->GetBPP() != 1)) + return false; + +- m_pStream->WriteString("q\n"); ++ WriteString("q\n"); + +- std::ostringstream buf; ++ fxcrt::ostringstream buf; + buf << "[" << matrix.a << " " << matrix.b << " " << matrix.c << " " + << matrix.d << " " << matrix.e << " " << matrix.f << "]cm "; + +- int width = pSource->GetWidth(); +- int height = pSource->GetHeight(); ++ const int width = pSource->GetWidth(); ++ const int height = pSource->GetHeight(); + buf << width << " " << height; + +- if (pSource->GetBPP() == 1 && !pSource->GetPalette()) { +- int pitch = (width + 7) / 8; +- uint32_t src_size = height * pitch; +- std::unique_ptr src_buf( +- FX_Alloc(uint8_t, src_size)); +- for (int row = 0; row < height; row++) { +- const uint8_t* src_scan = pSource->GetScanline(row); +- memcpy(src_buf.get() + row * pitch, src_scan, pitch); +- } ++ if (pSource->GetBPP() == 1 && !pSource->HasPalette()) { ++ FaxCompressResult compress_result = FaxCompressData(pSource); ++ if (compress_result.data.empty()) ++ return false; + +- std::unique_ptr output_buf; +- uint32_t output_size; +- bool compressed = FaxCompressData(std::move(src_buf), width, height, +- &output_buf, &output_size); +- if (pSource->IsAlphaMask()) { ++ if (pSource->IsMaskFormat()) { + SetColor(color); + m_bColorSet = false; + buf << " true["; +@@ -347,63 +543,55 @@ bool CFX_PSRenderer::DrawDIBits(const RetainPtr& pSource, + buf << width << " 0 0 -" << height << " 0 " << height + << "]currentfile/ASCII85Decode filter "; + +- if (compressed) { ++ if (compress_result.compressed) { + buf << "<>/CCITTFaxDecode filter "; + } +- if (pSource->IsAlphaMask()) ++ if (pSource->IsMaskFormat()) + buf << "iM\n"; + else + buf << "false 1 colorimage\n"; + +- WriteToStream(&buf); +- WritePSBinary(output_buf.get(), output_size); ++ WriteStream(buf); ++ WritePSBinary(compress_result.data); + } else { + CFX_DIBExtractor source_extractor(pSource); + RetainPtr pConverted = source_extractor.GetBitmap(); + if (!pConverted) + return false; + switch (pSource->GetFormat()) { +- case FXDIB_1bppRgb: +- case FXDIB_Rgb32: +- pConverted = pConverted->CloneConvert(FXDIB_Rgb); ++ case FXDIB_Format::k1bppRgb: ++ case FXDIB_Format::kRgb32: ++ pConverted = pConverted->ConvertTo(FXDIB_Format::kRgb); + break; +- case FXDIB_8bppRgb: +- if (pSource->GetPalette()) { +- pConverted = pConverted->CloneConvert(FXDIB_Rgb); +- } +- break; +- case FXDIB_1bppCmyk: +- pConverted = pConverted->CloneConvert(FXDIB_Cmyk); +- break; +- case FXDIB_8bppCmyk: +- if (pSource->GetPalette()) { +- pConverted = pConverted->CloneConvert(FXDIB_Cmyk); +- } ++ case FXDIB_Format::k8bppRgb: ++ if (pSource->HasPalette()) ++ pConverted = pConverted->ConvertTo(FXDIB_Format::kRgb); + break; + default: + break; + } + if (!pConverted) { +- m_pStream->WriteString("\nQ\n"); ++ WriteString("\nQ\n"); + return false; + } + + int bpp = pConverted->GetBPP() / 8; + uint8_t* output_buf = nullptr; + size_t output_size = 0; +- const char* filter = nullptr; +- if ((m_PSLevel == 2 || options.bLossy) && ++ bool output_buf_is_owned = true; ++ absl::optional compress_result; ++ ByteString filter; ++ if ((m_Level.value() == RenderingLevel::kLevel2 || options.bLossy) && + m_pEncoderIface->pJpegEncodeFunc(pConverted, &output_buf, + &output_size)) { + filter = "/DCTDecode filter "; +- } +- if (!filter) { ++ } else { + int src_pitch = width * bpp; + output_size = height * src_pitch; + output_buf = FX_Alloc(uint8_t, output_size); + for (int row = 0; row < height; row++) { +- const uint8_t* src_scan = pConverted->GetScanline(row); ++ const uint8_t* src_scan = pConverted->GetScanline(row).data(); + uint8_t* dest_scan = output_buf + row * src_pitch; + if (bpp == 3) { + for (int col = 0; col < width; col++) { +@@ -416,52 +604,43 @@ bool CFX_PSRenderer::DrawDIBits(const RetainPtr& pSource, + memcpy(dest_scan, src_scan, src_pitch); + } + } +- uint8_t* compressed_buf; +- uint32_t compressed_size; +- PSCompressData(output_buf, output_size, &compressed_buf, &compressed_size, +- &filter); +- if (output_buf != compressed_buf) ++ compress_result = PSCompressData({output_buf, output_size}); ++ if (compress_result.has_value()) { + FX_Free(output_buf); +- +- output_buf = compressed_buf; +- output_size = compressed_size; ++ output_buf_is_owned = false; ++ output_buf = compress_result.value().data.data(); ++ output_size = compress_result.value().data.size(); ++ filter = compress_result.value().filter; ++ } + } + buf << " 8["; + buf << width << " 0 0 -" << height << " 0 " << height << "]"; + buf << "currentfile/ASCII85Decode filter "; +- if (filter) ++ if (!filter.IsEmpty()) + buf << filter; + + buf << "false " << bpp; + buf << " colorimage\n"; +- WriteToStream(&buf); ++ WriteStream(buf); + +- WritePSBinary(output_buf, output_size); +- FX_Free(output_buf); ++ WritePSBinary({output_buf, output_size}); ++ if (output_buf_is_owned) ++ FX_Free(output_buf); + } +- m_pStream->WriteString("\nQ\n"); ++ WriteString("\nQ\n"); + return true; + } + + void CFX_PSRenderer::SetColor(uint32_t color) { +- bool bCMYK = false; +- if (bCMYK != m_bCmykOutput || !m_bColorSet || m_LastColor != color) { +- std::ostringstream buf; +- if (bCMYK) { +- buf << FXSYS_GetCValue(color) / 255.0 << " " +- << FXSYS_GetMValue(color) / 255.0 << " " +- << FXSYS_GetYValue(color) / 255.0 << " " +- << FXSYS_GetKValue(color) / 255.0 << " k\n"; +- } else { +- buf << FXARGB_R(color) / 255.0 << " " << FXARGB_G(color) / 255.0 << " " +- << FXARGB_B(color) / 255.0 << " rg\n"; +- } +- if (bCMYK == m_bCmykOutput) { +- m_bColorSet = true; +- m_LastColor = color; +- } +- WriteToStream(&buf); +- } ++ if (m_bColorSet && m_LastColor == color) ++ return; ++ ++ fxcrt::ostringstream buf; ++ buf << FXARGB_R(color) / 255.0 << " " << FXARGB_G(color) / 255.0 << " " ++ << FXARGB_B(color) / 255.0 << " rg\n"; ++ m_bColorSet = true; ++ m_LastColor = color; ++ WriteStream(buf); + } + + void CFX_PSRenderer::FindPSFontGlyph(CFX_GlyphCache* pGlyphCache, +@@ -469,33 +648,35 @@ void CFX_PSRenderer::FindPSFontGlyph(CFX_GlyphCache* pGlyphCache, + const TextCharPos& charpos, + int* ps_fontnum, + int* ps_glyphindex) { +- int i = 0; +- for (const auto& pPSFont : m_PSFontList) { +- for (int j = 0; j < pPSFont->m_nGlyphs; j++) { +- if (pPSFont->m_Glyphs[j].m_pFont == pFont && +- pPSFont->m_Glyphs[j].m_GlyphIndex == charpos.m_GlyphIndex && +- ((!pPSFont->m_Glyphs[j].m_bGlyphAdjust && !charpos.m_bGlyphAdjust) || +- (pPSFont->m_Glyphs[j].m_bGlyphAdjust && charpos.m_bGlyphAdjust && +- (fabs(pPSFont->m_Glyphs[j].m_AdjustMatrix[0] - +- charpos.m_AdjustMatrix[0]) < 0.01 && +- fabs(pPSFont->m_Glyphs[j].m_AdjustMatrix[1] - +- charpos.m_AdjustMatrix[1]) < 0.01 && +- fabs(pPSFont->m_Glyphs[j].m_AdjustMatrix[2] - +- charpos.m_AdjustMatrix[2]) < 0.01 && +- fabs(pPSFont->m_Glyphs[j].m_AdjustMatrix[3] - +- charpos.m_AdjustMatrix[3]) < 0.01)))) { +- *ps_fontnum = i; +- *ps_glyphindex = j; ++ for (size_t i = 0; i < m_PSFontList.size(); ++i) { ++ const Glyph& glyph = *m_PSFontList[i]; ++ if (glyph.font == pFont && glyph.glyph_index == charpos.m_GlyphIndex && ++ glyph.adjust_matrix.has_value() == charpos.m_bGlyphAdjust) { ++ bool found; ++ if (glyph.adjust_matrix.has_value()) { ++ constexpr float kEpsilon = 0.01f; ++ const auto& adjust_matrix = glyph.adjust_matrix.value(); ++ found = fabs(adjust_matrix[0] - charpos.m_AdjustMatrix[0]) < kEpsilon && ++ fabs(adjust_matrix[1] - charpos.m_AdjustMatrix[1]) < kEpsilon && ++ fabs(adjust_matrix[2] - charpos.m_AdjustMatrix[2]) < kEpsilon && ++ fabs(adjust_matrix[3] - charpos.m_AdjustMatrix[3]) < kEpsilon; ++ } else { ++ found = true; ++ } ++ if (found) { ++ *ps_fontnum = pdfium::base::checked_cast(i / 256); ++ *ps_glyphindex = i % 256; + return; + } + } +- ++i; + } + +- if (m_PSFontList.empty() || m_PSFontList.back()->m_nGlyphs == 256) { +- m_PSFontList.push_back(pdfium::MakeUnique()); +- m_PSFontList.back()->m_nGlyphs = 0; +- std::ostringstream buf; ++ m_PSFontList.push_back(std::make_unique(pFont, charpos.m_GlyphIndex)); ++ *ps_fontnum = ++ pdfium::base::checked_cast((m_PSFontList.size() - 1) / 256); ++ *ps_glyphindex = (m_PSFontList.size() - 1) % 256; ++ if (*ps_glyphindex == 0) { ++ fxcrt::ostringstream buf; + buf << "8 dict begin/FontType 3 def/FontMatrix[1 0 0 1 0 0]def\n" + "/FontBBox[0 0 0 0]def/Encoding 256 array def 0 1 255{Encoding " + "exch/.notdef put}for\n" +@@ -505,26 +686,15 @@ void CFX_PSRenderer::FindPSFontGlyph(CFX_GlyphCache* pGlyphCache, + "/BuildChar{1 index/Encoding get exch get 1 index/BuildGlyph get " + "exec}bind def\n" + "currentdict end\n"; +- buf << "/X" << static_cast(m_PSFontList.size() - 1) +- << " exch definefont pop\n"; +- WriteToStream(&buf); +- buf.str(""); ++ buf << "/X" << *ps_fontnum << " exch definefont pop\n"; ++ WriteStream(buf); + } + +- *ps_fontnum = m_PSFontList.size() - 1; +- CPSFont* pPSFont = m_PSFontList[*ps_fontnum].get(); +- int glyphindex = pPSFont->m_nGlyphs; +- *ps_glyphindex = glyphindex; +- pPSFont->m_Glyphs[glyphindex].m_GlyphIndex = charpos.m_GlyphIndex; +- pPSFont->m_Glyphs[glyphindex].m_pFont = pFont; +- pPSFont->m_Glyphs[glyphindex].m_bGlyphAdjust = charpos.m_bGlyphAdjust; + if (charpos.m_bGlyphAdjust) { +- pPSFont->m_Glyphs[glyphindex].m_AdjustMatrix[0] = charpos.m_AdjustMatrix[0]; +- pPSFont->m_Glyphs[glyphindex].m_AdjustMatrix[1] = charpos.m_AdjustMatrix[1]; +- pPSFont->m_Glyphs[glyphindex].m_AdjustMatrix[2] = charpos.m_AdjustMatrix[2]; +- pPSFont->m_Glyphs[glyphindex].m_AdjustMatrix[3] = charpos.m_AdjustMatrix[3]; ++ m_PSFontList.back()->adjust_matrix = std::array{ ++ charpos.m_AdjustMatrix[0], charpos.m_AdjustMatrix[1], ++ charpos.m_AdjustMatrix[2], charpos.m_AdjustMatrix[3]}; + } +- pPSFont->m_nGlyphs++; + + CFX_Matrix matrix; + if (charpos.m_bGlyphAdjust) { +@@ -532,30 +702,30 @@ void CFX_PSRenderer::FindPSFontGlyph(CFX_GlyphCache* pGlyphCache, + CFX_Matrix(charpos.m_AdjustMatrix[0], charpos.m_AdjustMatrix[1], + charpos.m_AdjustMatrix[2], charpos.m_AdjustMatrix[3], 0, 0); + } +- const CFX_PathData* pPathData = pGlyphCache->LoadGlyphPath( ++ const CFX_Path* pPath = pGlyphCache->LoadGlyphPath( + pFont, charpos.m_GlyphIndex, charpos.m_FontCharWidth); +- if (!pPathData) ++ if (!pPath) + return; + +- CFX_PathData TransformedPath(*pPathData); ++ CFX_Path TransformedPath(*pPath); + if (charpos.m_bGlyphAdjust) + TransformedPath.Transform(matrix); + +- std::ostringstream buf; +- buf << "/X" << *ps_fontnum << " Ff/CharProcs get begin/" << glyphindex ++ fxcrt::ostringstream buf; ++ buf << "/X" << *ps_fontnum << " Ff/CharProcs get begin/" << *ps_glyphindex + << "{n "; + for (size_t p = 0; p < TransformedPath.GetPoints().size(); p++) { + CFX_PointF point = TransformedPath.GetPoint(p); + switch (TransformedPath.GetType(p)) { +- case FXPT_TYPE::MoveTo: { ++ case CFX_Path::Point::Type::kMove: { + buf << point.x << " " << point.y << " m\n"; + break; + } +- case FXPT_TYPE::LineTo: { ++ case CFX_Path::Point::Type::kLine: { + buf << point.x << " " << point.y << " l\n"; + break; + } +- case FXPT_TYPE::BezierTo: { ++ case CFX_Path::Point::Type::kBezier: { + CFX_PointF point1 = TransformedPath.GetPoint(p + 1); + CFX_PointF point2 = TransformedPath.GetPoint(p + 2); + buf << point.x << " " << point.y << " " << point1.x << " " << point1.y +@@ -566,9 +736,64 @@ void CFX_PSRenderer::FindPSFontGlyph(CFX_GlyphCache* pGlyphCache, + } + } + buf << "f}bind def end\n"; +- buf << "/X" << *ps_fontnum << " Ff/Encoding get " << glyphindex << "/" +- << glyphindex << " put\n"; +- WriteToStream(&buf); ++ buf << "/X" << *ps_fontnum << " Ff/Encoding get " << *ps_glyphindex << "/" ++ << *ps_glyphindex << " put\n"; ++ WriteStream(buf); ++} ++ ++void CFX_PSRenderer::DrawTextAsType3Font(int char_count, ++ const TextCharPos* char_pos, ++ CFX_Font* font, ++ float font_size, ++ fxcrt::ostringstream& buf) { ++ CFX_FontCache* pCache = CFX_GEModule::Get()->GetFontCache(); ++ RetainPtr pGlyphCache = pCache->GetGlyphCache(font); ++ int last_fontnum = -1; ++ for (int i = 0; i < char_count; i++) { ++ int ps_fontnum; ++ int ps_glyphindex; ++ FindPSFontGlyph(pGlyphCache.Get(), font, char_pos[i], &ps_fontnum, ++ &ps_glyphindex); ++ if (last_fontnum != ps_fontnum) { ++ buf << "/X" << ps_fontnum << " Ff " << font_size << " Fs Sf "; ++ last_fontnum = ps_fontnum; ++ } ++ buf << char_pos[i].m_Origin.x << " " << char_pos[i].m_Origin.y << " m"; ++ ByteString hex = ByteString::Format("<%02X>", ps_glyphindex); ++ buf << hex.AsStringView() << "Tj\n"; ++ } ++} ++ ++bool CFX_PSRenderer::DrawTextAsType42Font(int char_count, ++ const TextCharPos* char_pos, ++ CFX_Font* font, ++ float font_size, ++ fxcrt::ostringstream& buf) { ++ if (m_Level != RenderingLevel::kLevel3Type42 || !CanEmbed(font)) ++ return false; ++ ++ if (font->GetFontType() != CFX_Font::FontType::kCIDTrueType) ++ return false; ++ ++ bool is_existing_font = m_pFontTracker->SeenFontObject(font); ++ if (!is_existing_font) { ++ ByteString font_data = GenerateType42FontData(font); ++ if (font_data.IsEmpty()) ++ return false; ++ ++ m_pFontTracker->AddFontObject(font); ++ WritePreambleString(font_data.AsStringView()); ++ } ++ ++ buf << "/" << font->GetPsName() << " " << font_size << " selectfont\n"; ++ for (int i = 0; i < char_count; ++i) { ++ buf << char_pos[i].m_Origin.x << " " << char_pos[i].m_Origin.y << " m"; ++ uint8_t hi = char_pos[i].m_GlyphIndex / 256; ++ uint8_t lo = char_pos[i].m_GlyphIndex % 256; ++ ByteString hex = ByteString::Format("<%02X%02X>", hi, lo); ++ buf << hex.AsStringView() << "Tj\n"; ++ } ++ return true; + } + + bool CFX_PSRenderer::DrawText(int nChars, +@@ -587,7 +812,7 @@ bool CFX_PSRenderer::DrawText(int nChars, + float scale = + std::min(mtObject2Device.GetXUnit(), mtObject2Device.GetYUnit()); + static constexpr float kEpsilon = 0.01f; +- if (std::fabs(font_size * scale) < kEpsilon) ++ if (fabsf(font_size * scale) < kEpsilon) + return true; + + StartRendering(); +@@ -596,97 +821,114 @@ bool CFX_PSRenderer::DrawText(int nChars, + return false; + + SetColor(color); +- std::ostringstream buf; ++ fxcrt::ostringstream buf; + buf << "q[" << mtObject2Device.a << " " << mtObject2Device.b << " " + << mtObject2Device.c << " " << mtObject2Device.d << " " + << mtObject2Device.e << " " << mtObject2Device.f << "]cm\n"; + +- CFX_FontCache* pCache = CFX_GEModule::Get()->GetFontCache(); +- RetainPtr pGlyphCache = pCache->GetGlyphCache(pFont); +- int last_fontnum = -1; +- for (int i = 0; i < nChars; i++) { +- int ps_fontnum, ps_glyphindex; +- FindPSFontGlyph(pGlyphCache.Get(), pFont, pCharPos[i], &ps_fontnum, +- &ps_glyphindex); +- if (last_fontnum != ps_fontnum) { +- buf << "/X" << ps_fontnum << " Ff " << font_size << " Fs Sf "; +- last_fontnum = ps_fontnum; +- } +- buf << pCharPos[i].m_Origin.x << " " << pCharPos[i].m_Origin.y << " m"; +- ByteString hex = ByteString::Format("<%02X>", ps_glyphindex); +- buf << hex.AsStringView() << "Tj\n"; ++ if (!DrawTextAsType42Font(nChars, pCharPos, pFont, font_size, buf)) { ++ DrawTextAsType3Font(nChars, pCharPos, pFont, font_size, buf); + } ++ + buf << "Q\n"; +- WriteToStream(&buf); ++ WriteStream(buf); + return true; + } + +-bool CFX_PSRenderer::FaxCompressData( +- std::unique_ptr src_buf, +- int width, +- int height, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size) const { +- if (width * height <= 128) { +- *dest_buf = std::move(src_buf); +- *dest_size = (width + 7) / 8 * height; +- return false; ++CFX_PSRenderer::FaxCompressResult CFX_PSRenderer::FaxCompressData( ++ RetainPtr src) const { ++ DCHECK_EQ(1, src->GetBPP()); ++ ++ FaxCompressResult result; ++ const int width = src->GetWidth(); ++ const int height = src->GetHeight(); ++ const int pitch = src->GetPitch(); ++ DCHECK_GE(width, pitch); ++ ++ FX_SAFE_UINT32 safe_pixel_count = width; ++ safe_pixel_count *= height; ++ if (!safe_pixel_count.IsValid()) ++ return result; ++ ++ if (safe_pixel_count.ValueOrDie() > 128) { ++ result.data = m_pEncoderIface->pFaxEncodeFunc(std::move(src)); ++ result.compressed = true; ++ return result; + } + +- m_pEncoderIface->pFaxEncodeFunc(src_buf.get(), width, height, (width + 7) / 8, +- dest_buf, dest_size); +- return true; ++ FX_SAFE_UINT32 safe_size = pitch; ++ safe_size *= height; ++ result.data.resize(safe_size.ValueOrDie()); ++ auto dest_span = pdfium::make_span(result.data); ++ for (int row = 0; row < height; row++) { ++ pdfium::span src_scan = src->GetScanline(row); ++ fxcrt::spancpy(dest_span.subspan(row * pitch, pitch), src_scan); ++ } ++ return result; + } + +-void CFX_PSRenderer::PSCompressData(uint8_t* src_buf, +- uint32_t src_size, +- uint8_t** output_buf, +- uint32_t* output_size, +- const char** filter) const { +- *output_buf = src_buf; +- *output_size = src_size; +- *filter = ""; +- if (src_size < 1024) +- return; +- +- uint8_t* dest_buf = nullptr; +- uint32_t dest_size = src_size; +- if (m_PSLevel >= 3) { +- std::unique_ptr dest_buf_unique; +- if (m_pEncoderIface->pFlateEncodeFunc(src_buf, src_size, &dest_buf_unique, +- &dest_size)) { +- dest_buf = dest_buf_unique.release(); +- *filter = "/FlateDecode filter "; +- } +- } else { +- std::unique_ptr dest_buf_unique; +- if (m_pEncoderIface->pRunLengthEncodeFunc({src_buf, src_size}, +- &dest_buf_unique, &dest_size)) { +- dest_buf = dest_buf_unique.release(); +- *filter = "/RunLengthDecode filter "; +- } +- } +- if (dest_size < src_size) { +- *output_buf = dest_buf; +- *output_size = dest_size; ++absl::optional CFX_PSRenderer::PSCompressData( ++ pdfium::span src_span) const { ++ if (src_span.size() < 1024) ++ return absl::nullopt; ++ ++ DataVector (*encode_func)(pdfium::span src_span); ++ ByteString filter; ++ if (m_Level.value() == RenderingLevel::kLevel3 || ++ m_Level.value() == RenderingLevel::kLevel3Type42) { ++ encode_func = m_pEncoderIface->pFlateEncodeFunc; ++ filter = "/FlateDecode filter "; + } else { +- *filter = nullptr; +- FX_Free(dest_buf); ++ encode_func = m_pEncoderIface->pRunLengthEncodeFunc; ++ filter = "/RunLengthDecode filter "; + } ++ ++ DataVector decode_result = encode_func(src_span); ++ if (decode_result.size() == 0 || decode_result.size() >= src_span.size()) ++ return absl::nullopt; ++ ++ PSCompressResult result; ++ result.data = std::move(decode_result); ++ result.filter = filter; ++ return result; + } + +-void CFX_PSRenderer::WritePSBinary(const uint8_t* data, int len) { +- std::unique_ptr dest_buf; +- uint32_t dest_size; +- if (m_pEncoderIface->pA85EncodeFunc({data, static_cast(len)}, +- &dest_buf, &dest_size)) { +- m_pStream->WriteBlock(dest_buf.get(), dest_size); +- } else { +- m_pStream->WriteBlock(data, len); ++void CFX_PSRenderer::WritePreambleString(ByteStringView str) { ++ m_PreambleOutput << str; ++} ++ ++void CFX_PSRenderer::WritePSBinary(pdfium::span data) { ++ DataVector encoded_data = m_pEncoderIface->pA85EncodeFunc(data); ++ pdfium::span result = ++ encoded_data.empty() ? data : encoded_data; ++ m_Output.write(reinterpret_cast(result.data()), result.size()); ++} ++ ++void CFX_PSRenderer::WriteStream(fxcrt::ostringstream& stream) { ++ std::streamoff output_pos = stream.tellp(); ++ if (output_pos > 0) { ++ m_Output.write(stream.str().c_str(), ++ pdfium::base::checked_cast(output_pos)); + } + } + +-void CFX_PSRenderer::WriteToStream(std::ostringstream* stringStream) { +- if (stringStream->tellp() > 0) +- m_pStream->WriteBlock(stringStream->str().c_str(), stringStream->tellp()); ++void CFX_PSRenderer::WriteString(ByteStringView str) { ++ m_Output << str; ++} ++ ++// static ++absl::optional CFX_PSRenderer::GenerateType42SfntDataForTesting( ++ const ByteString& psname, ++ pdfium::span font_data) { ++ return GenerateType42SfntData(psname, font_data); ++} ++ ++// static ++ByteString CFX_PSRenderer::GenerateType42FontDictionaryForTesting( ++ const ByteString& psname, ++ const FX_RECT& bbox, ++ size_t num_glyphs, ++ size_t glyphs_per_descendant_font) { ++ return GenerateType42FontDictionary(psname, bbox, num_glyphs, ++ glyphs_per_descendant_font); + } +diff --git a/core/fxge/win32/cfx_psrenderer.h b/core/fxge/win32/cfx_psrenderer.h +index 50e98a4ae..b8010f6d8 100644 +--- a/core/fxge/win32/cfx_psrenderer.h ++++ b/core/fxge/win32/cfx_psrenderer.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,74 +7,76 @@ + #ifndef CORE_FXGE_WIN32_CFX_PSRENDERER_H_ + #define CORE_FXGE_WIN32_CFX_PSRENDERER_H_ + ++#include ++#include ++ + #include ++#include + #include + ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/fx_memory_wrappers.h" + #include "core/fxcrt/fx_stream.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/fx_string_wrappers.h" + #include "core/fxcrt/retain_ptr.h" ++#include "core/fxcrt/unowned_ptr.h" + #include "core/fxge/cfx_graphstatedata.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/span.h" + + class CFX_DIBBase; +-class CFX_GlyphCache; + class CFX_Font; +-class CFX_PathData; +-class CPSFont; ++class CFX_GlyphCache; ++class CFX_PSFontTracker; ++class CFX_Path; + class TextCharPos; ++struct CFX_FillRenderOptions; + struct FXDIB_ResampleOptions; + + struct EncoderIface { +- bool (*pA85EncodeFunc)(pdfium::span src_buf, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size); +- void (*pFaxEncodeFunc)(const uint8_t* src_buf, +- int width, +- int height, +- int pitch, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size); +- bool (*pFlateEncodeFunc)(const uint8_t* src_buf, +- uint32_t src_size, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size); ++ DataVector (*pA85EncodeFunc)(pdfium::span src_span); ++ DataVector (*pFaxEncodeFunc)(RetainPtr src); ++ DataVector (*pFlateEncodeFunc)(pdfium::span src_span); + bool (*pJpegEncodeFunc)(const RetainPtr& pSource, + uint8_t** dest_buf, + size_t* dest_size); +- bool (*pRunLengthEncodeFunc)( +- pdfium::span src_buf, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size); ++ DataVector (*pRunLengthEncodeFunc)( ++ pdfium::span src_span); + }; + + class CFX_PSRenderer { + public: +- explicit CFX_PSRenderer(const EncoderIface* pEncoderIface); ++ enum class RenderingLevel { ++ kLevel2, ++ kLevel3, ++ kLevel3Type42, ++ }; ++ ++ CFX_PSRenderer(CFX_PSFontTracker* font_tracker, ++ const EncoderIface* encoder_iface); + ~CFX_PSRenderer(); + + void Init(const RetainPtr& stream, +- int pslevel, ++ RenderingLevel level, + int width, +- int height, +- bool bCmykOutput); +- bool StartRendering(); +- void EndRendering(); ++ int height); + void SaveState(); + void RestoreState(bool bKeepSaved); +- void SetClip_PathFill(const CFX_PathData* pPathData, ++ void SetClip_PathFill(const CFX_Path& path, + const CFX_Matrix* pObject2Device, +- int fill_mode); +- void SetClip_PathStroke(const CFX_PathData* pPathData, ++ const CFX_FillRenderOptions& fill_options); ++ void SetClip_PathStroke(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState); + FX_RECT GetClipBox() { return m_ClipBox; } +- bool DrawPath(const CFX_PathData* pPathData, ++ bool DrawPath(const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState, + uint32_t fill_color, + uint32_t stroke_color, +- int fill_mode); ++ const CFX_FillRenderOptions& fill_options); + bool SetDIBits(const RetainPtr& pBitmap, + uint32_t color, + int dest_left, +@@ -97,9 +99,46 @@ class CFX_PSRenderer { + float font_size, + uint32_t color); + ++ static absl::optional GenerateType42SfntDataForTesting( ++ const ByteString& psname, ++ pdfium::span font_data); ++ ++ static ByteString GenerateType42FontDictionaryForTesting( ++ const ByteString& psname, ++ const FX_RECT& bbox, ++ size_t num_glyphs, ++ size_t glyphs_per_descendant_font); ++ + private: +- void OutputPath(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device); ++ struct Glyph; ++ ++ struct FaxCompressResult { ++ FaxCompressResult(); ++ FaxCompressResult(const FaxCompressResult&) = delete; ++ FaxCompressResult& operator=(const FaxCompressResult&) = delete; ++ FaxCompressResult(FaxCompressResult&&) noexcept; ++ FaxCompressResult& operator=(FaxCompressResult&&) noexcept; ++ ~FaxCompressResult(); ++ ++ DataVector data; ++ bool compressed = false; ++ }; ++ ++ struct PSCompressResult { ++ PSCompressResult(); ++ PSCompressResult(const PSCompressResult&) = delete; ++ PSCompressResult& operator=(const PSCompressResult&) = delete; ++ PSCompressResult(PSCompressResult&&) noexcept; ++ PSCompressResult& operator=(PSCompressResult&&) noexcept; ++ ~PSCompressResult(); ++ ++ DataVector data; ++ ByteString filter; ++ }; ++ ++ void StartRendering(); ++ void EndRendering(); ++ void OutputPath(const CFX_Path& path, const CFX_Matrix* pObject2Device); + void SetGraphState(const CFX_GraphStateData* pGraphState); + void SetColor(uint32_t color); + void FindPSFontGlyph(CFX_GlyphCache* pGlyphCache, +@@ -107,30 +146,37 @@ class CFX_PSRenderer { + const TextCharPos& charpos, + int* ps_fontnum, + int* ps_glyphindex); +- bool FaxCompressData(std::unique_ptr src_buf, +- int width, +- int height, +- std::unique_ptr* dest_buf, +- uint32_t* dest_size) const; +- void PSCompressData(uint8_t* src_buf, +- uint32_t src_size, +- uint8_t** output_buf, +- uint32_t* output_size, +- const char** filter) const; +- void WritePSBinary(const uint8_t* data, int len); +- void WriteToStream(std::ostringstream* stringStream); ++ void DrawTextAsType3Font(int char_count, ++ const TextCharPos* char_pos, ++ CFX_Font* font, ++ float font_size, ++ fxcrt::ostringstream& buf); ++ bool DrawTextAsType42Font(int char_count, ++ const TextCharPos* char_pos, ++ CFX_Font* font, ++ float font_size, ++ fxcrt::ostringstream& buf); ++ FaxCompressResult FaxCompressData(RetainPtr src) const; ++ absl::optional PSCompressData( ++ pdfium::span src_span) const; ++ void WritePreambleString(ByteStringView str); ++ void WritePSBinary(pdfium::span data); ++ void WriteStream(fxcrt::ostringstream& stream); ++ void WriteString(ByteStringView str); + + bool m_bInited = false; + bool m_bGraphStateSet = false; +- bool m_bCmykOutput; + bool m_bColorSet = false; +- int m_PSLevel = 0; ++ absl::optional m_Level; + uint32_t m_LastColor = 0; + FX_RECT m_ClipBox; + CFX_GraphStateData m_CurGraphState; +- const EncoderIface* const m_pEncoderIface; ++ UnownedPtr const m_pFontTracker; ++ UnownedPtr const m_pEncoderIface; + RetainPtr m_pStream; +- std::vector> m_PSFontList; ++ std::vector> m_PSFontList; ++ fxcrt::ostringstream m_PreambleOutput; ++ fxcrt::ostringstream m_Output; + std::vector m_ClipBoxStack; + }; + +diff --git a/core/fxge/win32/cfx_psrenderer_unittest.cpp b/core/fxge/win32/cfx_psrenderer_unittest.cpp +new file mode 100644 +index 000000000..6186f0fe9 +--- /dev/null ++++ b/core/fxge/win32/cfx_psrenderer_unittest.cpp +@@ -0,0 +1,213 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxge/win32/cfx_psrenderer.h" ++ ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_coordinates.h" ++#include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxge/dib/cfx_dibitmap.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "core/fxge/win32/cfx_psfonttracker.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/span.h" ++ ++namespace { ++ ++DataVector FakeA85Encode(pdfium::span src_span) { ++ return DataVector({'d', 'u', 'm', 'm', 'y', 'a', '8', '5'}); ++} ++ ++class TestWriteStream final : public IFX_RetainableWriteStream { ++ public: ++ CONSTRUCT_VIA_MAKE_RETAIN; ++ ++ // IFX_RetainableWriteStream: ++ bool WriteBlock(pdfium::span buffer) override { ++ data_.insert(data_.end(), buffer.begin(), buffer.end()); ++ return true; ++ } ++ ++ pdfium::span GetSpan() const { return data_; } ++ ++ private: ++ DataVector data_; ++}; ++ ++} // namespace ++ ++TEST(PSRendererTest, GenerateType42SfntData) { ++ absl::optional result; ++ ++ result = CFX_PSRenderer::GenerateType42SfntDataForTesting("empty", {}); ++ EXPECT_FALSE(result.has_value()); ++ ++ constexpr uint8_t kOddByteCountTestData[] = {0, 32, 55}; ++ static constexpr char kExpectedOddByteCountResult[] = R"(/odd_sfnts [ ++< ++002037 ++> ++] def ++)"; ++ result = CFX_PSRenderer::GenerateType42SfntDataForTesting( ++ "odd", kOddByteCountTestData); ++ ASSERT_TRUE(result.has_value()); ++ EXPECT_STREQ(kExpectedOddByteCountResult, result.value().c_str()); ++ ++ // Requires padding. ++ constexpr uint8_t kEvenByteCountTestData[] = {0, 32, 66, 77}; ++ static constexpr char kExpectedEvenByteCountResult[] = R"(/even_sfnts [ ++< ++0020424D00 ++> ++] def ++)"; ++ result = CFX_PSRenderer::GenerateType42SfntDataForTesting( ++ "even", kEvenByteCountTestData); ++ ASSERT_TRUE(result.has_value()); ++ EXPECT_STREQ(kExpectedEvenByteCountResult, result.value().c_str()); ++} ++ ++TEST(PSRendererTest, GenerateType42FontDictionary) { ++ ByteString result; ++ ++ static constexpr char kExpected1DescendantFontResult[] = R"(8 dict begin ++/FontType 42 def ++/FontMatrix [1 0 0 1 0 0] def ++/FontName /1descendant_0 def ++/Encoding 3 array ++dup 0 /c00 put ++dup 1 /c01 put ++dup 2 /c02 put ++readonly def ++/FontBBox [1 2 3 4] def ++/PaintType 0 def ++/CharStrings 4 dict dup begin ++/.notdef 0 def ++/c00 0 def ++/c01 1 def ++/c02 2 def ++end readonly def ++/sfnts 1descendant_sfnts def ++FontName currentdict end definefont pop ++6 dict begin ++/FontName /1descendant def ++/FontType 0 def ++/FontMatrix [1 0 0 1 0 0] def ++/FMapType 2 def ++/Encoding [ ++0 ++] def ++/FDepVector [ ++/1descendant_0 findfont ++] def ++FontName currentdict end definefont pop ++%%EndResource ++)"; ++ result = CFX_PSRenderer::GenerateType42FontDictionaryForTesting( ++ "1descendant", FX_RECT(1, 2, 3, 4), /*num_glyphs=*/3, ++ /*glyphs_per_descendant_font=*/3); ++ EXPECT_STREQ(kExpected1DescendantFontResult, result.c_str()); ++ ++ static constexpr char kExpected2DescendantFontResult[] = R"(8 dict begin ++/FontType 42 def ++/FontMatrix [1 0 0 1 0 0] def ++/FontName /2descendant_0 def ++/Encoding 3 array ++dup 0 /c00 put ++dup 1 /c01 put ++dup 2 /c02 put ++readonly def ++/FontBBox [12 -5 34 199] def ++/PaintType 0 def ++/CharStrings 4 dict dup begin ++/.notdef 0 def ++/c00 0 def ++/c01 1 def ++/c02 2 def ++end readonly def ++/sfnts 2descendant_sfnts def ++FontName currentdict end definefont pop ++8 dict begin ++/FontType 42 def ++/FontMatrix [1 0 0 1 0 0] def ++/FontName /2descendant_1 def ++/Encoding 3 array ++dup 0 /c00 put ++dup 1 /c01 put ++readonly def ++/FontBBox [12 -5 34 199] def ++/PaintType 0 def ++/CharStrings 4 dict dup begin ++/.notdef 0 def ++/c00 3 def ++/c01 4 def ++end readonly def ++/sfnts 2descendant_sfnts def ++FontName currentdict end definefont pop ++6 dict begin ++/FontName /2descendant def ++/FontType 0 def ++/FontMatrix [1 0 0 1 0 0] def ++/FMapType 2 def ++/Encoding [ ++0 ++1 ++] def ++/FDepVector [ ++/2descendant_0 findfont ++/2descendant_1 findfont ++] def ++FontName currentdict end definefont pop ++%%EndResource ++)"; ++ result = CFX_PSRenderer::GenerateType42FontDictionaryForTesting( ++ "2descendant", FX_RECT(12, -5, 34, 199), /*num_glyphs=*/5, ++ /*glyphs_per_descendant_font=*/3); ++ EXPECT_STREQ(kExpected2DescendantFontResult, result.c_str()); ++} ++ ++TEST(PSRendererTest, DrawDIBits) { ++ static constexpr char kExpectedOutput[] = R"( ++save ++/im/initmatrix load def ++/n/newpath load def/m/moveto load def/l/lineto load def/c/curveto load def/h/closepath load def ++/f/fill load def/F/eofill load def/s/stroke load def/W/clip load def/W*/eoclip load def ++/rg/setrgbcolor load def/k/setcmykcolor load def ++/J/setlinecap load def/j/setlinejoin load def/w/setlinewidth load def/M/setmiterlimit load def/d/setdash load def ++/q/gsave load def/Q/grestore load def/iM/imagemask load def ++/Tj/show load def/Ff/findfont load def/Fs/scalefont load def/Sf/setfont load def ++/cm/concat load def/Cm/currentmatrix load def/mx/matrix load def/sm/setmatrix load def ++q ++[1 0 0 1 0 0]cm 10 2 1[10 0 0 -2 0 2]currentfile/ASCII85Decode filter false 1 colorimage ++dummya85 ++Q ++ ++restore ++)"; ++ auto output_stream = pdfium::MakeRetain(); ++ ++ { ++ constexpr int kWidth = 10; ++ constexpr int kHeight = 2; ++ CFX_PSFontTracker font_tracker; ++ const EncoderIface encoder_interface{&FakeA85Encode, nullptr, nullptr, ++ nullptr, nullptr}; ++ CFX_PSRenderer renderer(&font_tracker, &encoder_interface); ++ renderer.Init(output_stream, CFX_PSRenderer::RenderingLevel::kLevel2, ++ kWidth, kHeight); ++ ++ auto bitmap = pdfium::MakeRetain(); ++ bool result = bitmap->Create(kWidth, kHeight, FXDIB_Format::k1bppRgb); ++ ASSERT_TRUE(result); ++ ASSERT_TRUE(renderer.DrawDIBits(bitmap, /*color=*/0, CFX_Matrix(), ++ FXDIB_ResampleOptions())); ++ } ++ ++ ByteString output(output_stream->GetSpan()); ++ EXPECT_STREQ(output.c_str(), kExpectedOutput); ++} +diff --git a/core/fxge/win32/cfx_windowsdib.h b/core/fxge/win32/cfx_windowsdib.h +deleted file mode 100644 +index ef6127bb4..000000000 +--- a/core/fxge/win32/cfx_windowsdib.h ++++ /dev/null +@@ -1,52 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXGE_WIN32_CFX_WINDOWSDIB_H_ +-#define CORE_FXGE_WIN32_CFX_WINDOWSDIB_H_ +- +-#include +- +-#include "core/fxcrt/bytestring.h" +-#include "core/fxge/dib/cfx_dibitmap.h" +- +-#define WINDIB_OPEN_MEMORY 0x1 +-#define WINDIB_OPEN_PATHNAME 0x2 +- +-struct WINDIB_Open_Args_ { +- int flags; +- const uint8_t* memory_base; +- size_t memory_size; +- const wchar_t* path_name; +-}; +- +-class CFX_WindowsDIB final : public CFX_DIBitmap { +- public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); +- +- static ByteString GetBitmapInfo(const RetainPtr& pBitmap); +- static HBITMAP GetDDBitmap(const RetainPtr& pBitmap, HDC hDC); +- +- static RetainPtr LoadFromBuf(BITMAPINFO* pbmi, void* pData); +- static RetainPtr LoadFromFile(const wchar_t* filename); +- static RetainPtr LoadFromFile(const char* filename); +- static RetainPtr LoadDIBitmap(WINDIB_Open_Args_ args); +- +- HBITMAP GetWindowsBitmap() const { return m_hBitmap; } +- +- void LoadFromDevice(HDC hDC, int left, int top); +- void SetToDevice(HDC hDC, int left, int top); +- +- private: +- CFX_WindowsDIB(HDC hDC, int width, int height); +- ~CFX_WindowsDIB() override; +- +- HDC m_hMemDC; +- HBITMAP m_hBitmap; +- HBITMAP m_hOldBitmap; +-}; +- +-#endif // CORE_FXGE_WIN32_CFX_WINDOWSDIB_H_ +diff --git a/core/fxge/win32/cgdi_device_driver.cpp b/core/fxge/win32/cgdi_device_driver.cpp +new file mode 100644 +index 000000000..048377da7 +--- /dev/null ++++ b/core/fxge/win32/cgdi_device_driver.cpp +@@ -0,0 +1,749 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxge/win32/cgdi_device_driver.h" ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "core/fxcrt/fx_string.h" ++#include "core/fxge/agg/fx_agg_driver.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" ++#include "core/fxge/cfx_fillrenderoptions.h" ++#include "core/fxge/cfx_graphstatedata.h" ++#include "core/fxge/cfx_path.h" ++#include "core/fxge/dib/cfx_dibitmap.h" ++#include "core/fxge/render_defines.h" ++#include "core/fxge/win32/cwin32_platform.h" ++#include "third_party/agg23/agg_clip_liang_barsky.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/notreached.h" ++#include "third_party/base/numerics/safe_conversions.h" ++ ++namespace { ++ ++constexpr int FillTypeToGdiFillType(CFX_FillRenderOptions::FillType fill_type) { ++ return static_cast(fill_type); ++} ++ ++static_assert(FillTypeToGdiFillType( ++ CFX_FillRenderOptions::FillType::kEvenOdd) == ALTERNATE, ++ "CFX_FillRenderOptions::FillType::kEvenOdd value mismatch"); ++ ++static_assert( ++ FillTypeToGdiFillType(CFX_FillRenderOptions::FillType::kWinding) == WINDING, ++ "CFX_FillRenderOptions::FillType::kWinding value mismatch"); ++ ++HPEN CreateExtPen(const CFX_GraphStateData* pGraphState, ++ const CFX_Matrix* pMatrix, ++ uint32_t argb) { ++ DCHECK(pGraphState); ++ ++ float scale = 1.0f; ++ if (pMatrix) { ++ scale = fabs(pMatrix->a) > fabs(pMatrix->b) ? fabs(pMatrix->a) ++ : fabs(pMatrix->b); ++ } ++ float width = std::max(scale * pGraphState->m_LineWidth, 1.0f); ++ ++ uint32_t PenStyle = PS_GEOMETRIC; ++ if (!pGraphState->m_DashArray.empty()) ++ PenStyle |= PS_USERSTYLE; ++ else ++ PenStyle |= PS_SOLID; ++ ++ switch (pGraphState->m_LineCap) { ++ case CFX_GraphStateData::LineCap::kButt: ++ PenStyle |= PS_ENDCAP_FLAT; ++ break; ++ case CFX_GraphStateData::LineCap::kRound: ++ PenStyle |= PS_ENDCAP_ROUND; ++ break; ++ case CFX_GraphStateData::LineCap::kSquare: ++ PenStyle |= PS_ENDCAP_SQUARE; ++ break; ++ } ++ switch (pGraphState->m_LineJoin) { ++ case CFX_GraphStateData::LineJoin::kMiter: ++ PenStyle |= PS_JOIN_MITER; ++ break; ++ case CFX_GraphStateData::LineJoin::kRound: ++ PenStyle |= PS_JOIN_ROUND; ++ break; ++ case CFX_GraphStateData::LineJoin::kBevel: ++ PenStyle |= PS_JOIN_BEVEL; ++ break; ++ } ++ ++ FX_COLORREF colorref = ArgbToColorRef(argb); ++ LOGBRUSH lb; ++ lb.lbColor = colorref; ++ lb.lbStyle = BS_SOLID; ++ lb.lbHatch = 0; ++ std::vector dashes; ++ if (!pGraphState->m_DashArray.empty()) { ++ dashes.resize(pGraphState->m_DashArray.size()); ++ for (size_t i = 0; i < pGraphState->m_DashArray.size(); i++) { ++ dashes[i] = FXSYS_roundf( ++ pMatrix ? pMatrix->TransformDistance(pGraphState->m_DashArray[i]) ++ : pGraphState->m_DashArray[i]); ++ dashes[i] = std::max(dashes[i], 1U); ++ } ++ } ++ return ExtCreatePen( ++ PenStyle, (DWORD)ceil(width), &lb, ++ pdfium::base::checked_cast(pGraphState->m_DashArray.size()), ++ reinterpret_cast(dashes.data())); ++} ++ ++HBRUSH CreateBrush(uint32_t argb) { ++ return CreateSolidBrush(ArgbToColorRef(argb)); ++} ++ ++void SetPathToDC(HDC hDC, const CFX_Path& path, const CFX_Matrix* pMatrix) { ++ BeginPath(hDC); ++ ++ pdfium::span points = path.GetPoints(); ++ for (size_t i = 0; i < points.size(); ++i) { ++ CFX_PointF pos = points[i].m_Point; ++ if (pMatrix) ++ pos = pMatrix->Transform(pos); ++ ++ CFX_Point screen(FXSYS_roundf(pos.x), FXSYS_roundf(pos.y)); ++ CFX_Path::Point::Type point_type = points[i].m_Type; ++ if (point_type == CFX_Path::Point::Type::kMove) { ++ MoveToEx(hDC, screen.x, screen.y, nullptr); ++ } else if (point_type == CFX_Path::Point::Type::kLine) { ++ if (points[i].m_Point == points[i - 1].m_Point) ++ screen.x++; ++ ++ LineTo(hDC, screen.x, screen.y); ++ } else if (point_type == CFX_Path::Point::Type::kBezier) { ++ POINT lppt[3]; ++ lppt[0].x = screen.x; ++ lppt[0].y = screen.y; ++ ++ pos = points[i + 1].m_Point; ++ if (pMatrix) ++ pos = pMatrix->Transform(pos); ++ ++ lppt[1].x = FXSYS_roundf(pos.x); ++ lppt[1].y = FXSYS_roundf(pos.y); ++ ++ pos = points[i + 2].m_Point; ++ if (pMatrix) ++ pos = pMatrix->Transform(pos); ++ ++ lppt[2].x = FXSYS_roundf(pos.x); ++ lppt[2].y = FXSYS_roundf(pos.y); ++ PolyBezierTo(hDC, lppt, 3); ++ i += 2; ++ } ++ if (points[i].m_CloseFigure) ++ CloseFigure(hDC); ++ } ++ EndPath(hDC); ++} ++ ++ByteString GetBitmapInfo(const RetainPtr& pBitmap) { ++ int len = sizeof(BITMAPINFOHEADER); ++ if (pBitmap->GetBPP() == 1 || pBitmap->GetBPP() == 8) ++ len += sizeof(DWORD) * (int)(1 << pBitmap->GetBPP()); ++ ++ ByteString result; ++ { ++ // Span's lifetime must end before ReleaseBuffer() below. ++ pdfium::span cspan = result.GetBuffer(len); ++ BITMAPINFOHEADER* pbmih = reinterpret_cast(cspan.data()); ++ memset(pbmih, 0, sizeof(BITMAPINFOHEADER)); ++ pbmih->biSize = sizeof(BITMAPINFOHEADER); ++ pbmih->biBitCount = pBitmap->GetBPP(); ++ pbmih->biCompression = BI_RGB; ++ pbmih->biHeight = -(int)pBitmap->GetHeight(); ++ pbmih->biPlanes = 1; ++ pbmih->biWidth = pBitmap->GetWidth(); ++ if (pBitmap->GetBPP() == 8) { ++ uint32_t* pPalette = (uint32_t*)(pbmih + 1); ++ if (pBitmap->HasPalette()) { ++ pdfium::span palette = pBitmap->GetPaletteSpan(); ++ for (int i = 0; i < 256; i++) ++ pPalette[i] = palette[i]; ++ } else { ++ for (int i = 0; i < 256; i++) ++ pPalette[i] = ArgbEncode(0, i, i, i); ++ } ++ } ++ if (pBitmap->GetBPP() == 1) { ++ uint32_t* pPalette = (uint32_t*)(pbmih + 1); ++ if (pBitmap->HasPalette()) { ++ pPalette[0] = pBitmap->GetPaletteSpan()[0]; ++ pPalette[1] = pBitmap->GetPaletteSpan()[1]; ++ } else { ++ pPalette[0] = 0; ++ pPalette[1] = 0xffffff; ++ } ++ } ++ } ++ result.ReleaseBuffer(len); ++ return result; ++} ++ ++#if defined(_SKIA_SUPPORT_) ++// TODO(caryclark) This antigrain function is duplicated here to permit ++// removing the last remaining dependency. Eventually, this will be elminiated ++// altogether and replace by Skia code. ++ ++struct rect_base { ++ float x1; ++ float y1; ++ float x2; ++ float y2; ++}; ++ ++unsigned clip_liang_barsky(float x1, ++ float y1, ++ float x2, ++ float y2, ++ const rect_base& clip_box, ++ float* x, ++ float* y) { ++ const float nearzero = 1e-30f; ++ float deltax = x2 - x1; ++ float deltay = y2 - y1; ++ unsigned np = 0; ++ if (deltax == 0) ++ deltax = (x1 > clip_box.x1) ? -nearzero : nearzero; ++ float xin; ++ float xout; ++ if (deltax > 0) { ++ xin = clip_box.x1; ++ xout = clip_box.x2; ++ } else { ++ xin = clip_box.x2; ++ xout = clip_box.x1; ++ } ++ float tinx = (xin - x1) / deltax; ++ if (deltay == 0) ++ deltay = (y1 > clip_box.y1) ? -nearzero : nearzero; ++ float yin; ++ float yout; ++ if (deltay > 0) { ++ yin = clip_box.y1; ++ yout = clip_box.y2; ++ } else { ++ yin = clip_box.y2; ++ yout = clip_box.y1; ++ } ++ float tiny = (yin - y1) / deltay; ++ float tin1; ++ float tin2; ++ if (tinx < tiny) { ++ tin1 = tinx; ++ tin2 = tiny; ++ } else { ++ tin1 = tiny; ++ tin2 = tinx; ++ } ++ if (tin1 <= 1.0f) { ++ if (0 < tin1) { ++ *x++ = xin; ++ *y++ = yin; ++ ++np; ++ } ++ if (tin2 <= 1.0f) { ++ float toutx = (xout - x1) / deltax; ++ float touty = (yout - y1) / deltay; ++ float tout1 = (toutx < touty) ? toutx : touty; ++ if (tin2 > 0 || tout1 > 0) { ++ if (tin2 <= tout1) { ++ if (tin2 > 0) { ++ if (tinx > tiny) { ++ *x++ = xin; ++ *y++ = y1 + (deltay * tinx); ++ } else { ++ *x++ = x1 + (deltax * tiny); ++ *y++ = yin; ++ } ++ ++np; ++ } ++ if (tout1 < 1.0f) { ++ if (toutx < touty) { ++ *x++ = xout; ++ *y++ = y1 + (deltay * toutx); ++ } else { ++ *x++ = x1 + (deltax * touty); ++ *y++ = yout; ++ } ++ } else { ++ *x++ = x2; ++ *y++ = y2; ++ } ++ ++np; ++ } else { ++ if (tinx > tiny) { ++ *x++ = xin; ++ *y++ = yout; ++ } else { ++ *x++ = xout; ++ *y++ = yin; ++ } ++ ++np; ++ } ++ } ++ } ++ } ++ return np; ++} ++#endif // defined(_SKIA_SUPPORT_) ++ ++unsigned LineClip(float w, ++ float h, ++ float x1, ++ float y1, ++ float x2, ++ float y2, ++ float* x, ++ float* y) { ++#if defined(_SKIA_SUPPORT_) ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ // TODO(caryclark) temporary replacement of antigrain in line function to ++ // permit removing antigrain altogether ++ rect_base rect = {0.0f, 0.0f, w, h}; ++ return clip_liang_barsky(x1, y1, x2, y2, rect, x, y); ++ } ++#endif ++ pdfium::agg::rect_base rect(0.0f, 0.0f, w, h); ++ return pdfium::agg::clip_liang_barsky(x1, y1, x2, y2, rect, x, y); ++} ++ ++} // namespace ++ ++CGdiDeviceDriver::CGdiDeviceDriver(HDC hDC, DeviceType device_type) ++ : m_hDC(hDC), m_DeviceType(device_type) { ++ SetStretchBltMode(m_hDC, HALFTONE); ++ DWORD obj_type = GetObjectType(m_hDC); ++ m_bMetafileDCType = obj_type == OBJ_ENHMETADC || obj_type == OBJ_ENHMETAFILE; ++ if (obj_type == OBJ_MEMDC) { ++ HBITMAP hBitmap = CreateBitmap(1, 1, 1, 1, nullptr); ++ hBitmap = (HBITMAP)SelectObject(m_hDC, hBitmap); ++ BITMAP bitmap; ++ GetObject(hBitmap, sizeof bitmap, &bitmap); ++ m_nBitsPerPixel = bitmap.bmBitsPixel; ++ m_Width = bitmap.bmWidth; ++ m_Height = abs(bitmap.bmHeight); ++ hBitmap = (HBITMAP)SelectObject(m_hDC, hBitmap); ++ DeleteObject(hBitmap); ++ } else { ++ m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL); ++ m_Width = ::GetDeviceCaps(m_hDC, HORZRES); ++ m_Height = ::GetDeviceCaps(m_hDC, VERTRES); ++ } ++ if (m_DeviceType != DeviceType::kDisplay) { ++ m_RenderCaps = FXRC_BIT_MASK; ++ } else { ++ m_RenderCaps = FXRC_GET_BITS | FXRC_BIT_MASK; ++ } ++} ++ ++CGdiDeviceDriver::~CGdiDeviceDriver() = default; ++ ++DeviceType CGdiDeviceDriver::GetDeviceType() const { ++ return m_DeviceType; ++} ++ ++int CGdiDeviceDriver::GetDeviceCaps(int caps_id) const { ++ switch (caps_id) { ++ case FXDC_PIXEL_WIDTH: ++ return m_Width; ++ case FXDC_PIXEL_HEIGHT: ++ return m_Height; ++ case FXDC_BITS_PIXEL: ++ return m_nBitsPerPixel; ++ case FXDC_RENDER_CAPS: ++ return m_RenderCaps; ++ default: ++ NOTREACHED(); ++ return 0; ++ } ++} ++ ++void CGdiDeviceDriver::SaveState() { ++ SaveDC(m_hDC); ++} ++ ++void CGdiDeviceDriver::RestoreState(bool bKeepSaved) { ++ RestoreDC(m_hDC, -1); ++ if (bKeepSaved) ++ SaveDC(m_hDC); ++} ++ ++bool CGdiDeviceDriver::GDI_SetDIBits(const RetainPtr& pBitmap1, ++ const FX_RECT& src_rect, ++ int left, ++ int top) { ++ if (m_DeviceType == DeviceType::kPrinter) { ++ RetainPtr pBitmap = pBitmap1->FlipImage(false, true); ++ if (!pBitmap) ++ return false; ++ ++ LPBYTE pBuffer = pBitmap->GetBuffer().data(); ++ ByteString info = GetBitmapInfo(pBitmap); ++ ((BITMAPINFOHEADER*)info.c_str())->biHeight *= -1; ++ FX_RECT dst_rect(0, 0, src_rect.Width(), src_rect.Height()); ++ dst_rect.Intersect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight()); ++ int dst_width = dst_rect.Width(); ++ int dst_height = dst_rect.Height(); ++ ::StretchDIBits(m_hDC, left, top, dst_width, dst_height, 0, 0, dst_width, ++ dst_height, pBuffer, (BITMAPINFO*)info.c_str(), ++ DIB_RGB_COLORS, SRCCOPY); ++ return true; ++ } ++ ++ RetainPtr pBitmap = pBitmap1; ++ LPBYTE pBuffer = pBitmap->GetBuffer().data(); ++ ByteString info = GetBitmapInfo(pBitmap); ++ ::SetDIBitsToDevice(m_hDC, left, top, src_rect.Width(), src_rect.Height(), ++ src_rect.left, pBitmap->GetHeight() - src_rect.bottom, 0, ++ pBitmap->GetHeight(), pBuffer, (BITMAPINFO*)info.c_str(), ++ DIB_RGB_COLORS); ++ return true; ++} ++ ++bool CGdiDeviceDriver::GDI_StretchDIBits( ++ const RetainPtr& pBitmap1, ++ int dest_left, ++ int dest_top, ++ int dest_width, ++ int dest_height, ++ const FXDIB_ResampleOptions& options) { ++ RetainPtr pBitmap = pBitmap1; ++ if (!pBitmap || dest_width == 0 || dest_height == 0) ++ return false; ++ ++ ByteString info = GetBitmapInfo(pBitmap); ++ if ((int64_t)abs(dest_width) * abs(dest_height) < ++ (int64_t)pBitmap1->GetWidth() * pBitmap1->GetHeight() * 4 || ++ options.bInterpolateBilinear) { ++ SetStretchBltMode(m_hDC, HALFTONE); ++ } else { ++ SetStretchBltMode(m_hDC, COLORONCOLOR); ++ } ++ RetainPtr pToStrechBitmap = pBitmap; ++ if (m_DeviceType == DeviceType::kPrinter && ++ ((int64_t)pBitmap->GetWidth() * pBitmap->GetHeight() > ++ (int64_t)abs(dest_width) * abs(dest_height))) { ++ pToStrechBitmap = pBitmap->StretchTo(dest_width, dest_height, ++ FXDIB_ResampleOptions(), nullptr); ++ } ++ ByteString toStrechBitmapInfo = GetBitmapInfo(pToStrechBitmap); ++ ::StretchDIBits(m_hDC, dest_left, dest_top, dest_width, dest_height, 0, 0, ++ pToStrechBitmap->GetWidth(), pToStrechBitmap->GetHeight(), ++ pToStrechBitmap->GetBuffer().data(), ++ (BITMAPINFO*)toStrechBitmapInfo.c_str(), DIB_RGB_COLORS, ++ SRCCOPY); ++ return true; ++} ++ ++bool CGdiDeviceDriver::GDI_StretchBitMask( ++ const RetainPtr& pBitmap1, ++ int dest_left, ++ int dest_top, ++ int dest_width, ++ int dest_height, ++ uint32_t bitmap_color) { ++ RetainPtr pBitmap = pBitmap1; ++ if (!pBitmap || dest_width == 0 || dest_height == 0) ++ return false; ++ ++ int width = pBitmap->GetWidth(), height = pBitmap->GetHeight(); ++ struct { ++ BITMAPINFOHEADER bmiHeader; ++ uint32_t bmiColors[2]; ++ } bmi; ++ memset(&bmi.bmiHeader, 0, sizeof(BITMAPINFOHEADER)); ++ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); ++ bmi.bmiHeader.biBitCount = 1; ++ bmi.bmiHeader.biCompression = BI_RGB; ++ bmi.bmiHeader.biHeight = -height; ++ bmi.bmiHeader.biPlanes = 1; ++ bmi.bmiHeader.biWidth = width; ++ if (m_nBitsPerPixel != 1) { ++ SetStretchBltMode(m_hDC, HALFTONE); ++ } ++ bmi.bmiColors[0] = 0xffffff; ++ bmi.bmiColors[1] = 0; ++ ++ HBRUSH hPattern = CreateBrush(bitmap_color); ++ HBRUSH hOld = (HBRUSH)SelectObject(m_hDC, hPattern); ++ ++ // In PDF, when image mask is 1, use device bitmap; when mask is 0, use brush ++ // bitmap. ++ // A complete list of the boolen operations is as follows: ++ ++ /* P(bitmap_color) S(ImageMask) D(DeviceBitmap) Result ++ * 0 0 0 0 ++ * 0 0 1 0 ++ * 0 1 0 0 ++ * 0 1 1 1 ++ * 1 0 0 1 ++ * 1 0 1 1 ++ * 1 1 0 0 ++ * 1 1 1 1 ++ */ ++ // The boolen codes is B8. Based on ++ // http://msdn.microsoft.com/en-us/library/aa932106.aspx, the ROP3 code is ++ // 0xB8074A ++ ++ ::StretchDIBits(m_hDC, dest_left, dest_top, dest_width, dest_height, 0, 0, ++ width, height, pBitmap->GetBuffer().data(), (BITMAPINFO*)&bmi, ++ DIB_RGB_COLORS, 0xB8074A); ++ ++ SelectObject(m_hDC, hOld); ++ DeleteObject(hPattern); ++ ++ return true; ++} ++ ++bool CGdiDeviceDriver::GetClipBox(FX_RECT* pRect) { ++ return !!(::GetClipBox(m_hDC, (RECT*)pRect)); ++} ++ ++bool CGdiDeviceDriver::MultiplyAlpha(float alpha) { ++ // Not implemented. All callers are using `CFX_DIBitmap`-backed raster devices ++ // anyway. ++ NOTREACHED(); ++ return false; ++} ++ ++bool CGdiDeviceDriver::MultiplyAlpha(const RetainPtr& mask) { ++ // Not implemented. All callers are using `CFX_DIBitmap`-backed raster devices ++ // anyway. ++ NOTREACHED(); ++ return false; ++} ++ ++void CGdiDeviceDriver::DrawLine(float x1, float y1, float x2, float y2) { ++ if (!m_bMetafileDCType) { // EMF drawing is not bound to the DC. ++ int startOutOfBoundsFlag = (x1 < 0) | ((x1 > m_Width) << 1) | ++ ((y1 < 0) << 2) | ((y1 > m_Height) << 3); ++ int endOutOfBoundsFlag = (x2 < 0) | ((x2 > m_Width) << 1) | ++ ((y2 < 0) << 2) | ((y2 > m_Height) << 3); ++ if (startOutOfBoundsFlag & endOutOfBoundsFlag) ++ return; ++ ++ if (startOutOfBoundsFlag || endOutOfBoundsFlag) { ++ float x[2]; ++ float y[2]; ++ unsigned np = LineClip(m_Width, m_Height, x1, y1, x2, y2, x, y); ++ if (np == 0) ++ return; ++ ++ if (np == 1) { ++ x2 = x[0]; ++ y2 = y[0]; ++ } else { ++ DCHECK_EQ(np, 2); ++ x1 = x[0]; ++ y1 = y[0]; ++ x2 = x[1]; ++ y2 = y[1]; ++ } ++ } ++ } ++ ++ MoveToEx(m_hDC, FXSYS_roundf(x1), FXSYS_roundf(y1), nullptr); ++ LineTo(m_hDC, FXSYS_roundf(x2), FXSYS_roundf(y2)); ++} ++ ++bool CGdiDeviceDriver::DrawPath(const CFX_Path& path, ++ const CFX_Matrix* pMatrix, ++ const CFX_GraphStateData* pGraphState, ++ uint32_t fill_color, ++ uint32_t stroke_color, ++ const CFX_FillRenderOptions& fill_options, ++ BlendMode blend_type) { ++ if (blend_type != BlendMode::kNormal) ++ return false; ++ ++ auto* pPlatform = ++ static_cast(CFX_GEModule::Get()->GetPlatform()); ++ if (!(pGraphState || stroke_color == 0) && ++ !pPlatform->m_GdiplusExt.IsAvailable()) { ++ CFX_FloatRect bbox_f = path.GetBoundingBox(); ++ if (pMatrix) ++ bbox_f = pMatrix->TransformRect(bbox_f); ++ ++ FX_RECT bbox = bbox_f.GetInnerRect(); ++ if (bbox.Width() <= 0) { ++ return DrawCosmeticLine(CFX_PointF(bbox.left, bbox.top), ++ CFX_PointF(bbox.left, bbox.bottom + 1), ++ fill_color, BlendMode::kNormal); ++ } ++ if (bbox.Height() <= 0) { ++ return DrawCosmeticLine(CFX_PointF(bbox.left, bbox.top), ++ CFX_PointF(bbox.right + 1, bbox.top), fill_color, ++ BlendMode::kNormal); ++ } ++ } ++ int fill_alpha = FXARGB_A(fill_color); ++ int stroke_alpha = FXARGB_A(stroke_color); ++ bool bDrawAlpha = (fill_alpha > 0 && fill_alpha < 255) || ++ (stroke_alpha > 0 && stroke_alpha < 255 && pGraphState); ++ if (!pPlatform->m_GdiplusExt.IsAvailable() && bDrawAlpha) ++ return false; ++ ++ if (pPlatform->m_GdiplusExt.IsAvailable()) { ++ if (bDrawAlpha || ++ ((m_DeviceType != DeviceType::kPrinter && !fill_options.full_cover) || ++ (pGraphState && !pGraphState->m_DashArray.empty()))) { ++ if (!((!pMatrix || !pMatrix->WillScale()) && pGraphState && ++ pGraphState->m_LineWidth == 1.0f && path.IsRect())) { ++ if (pPlatform->m_GdiplusExt.DrawPath(m_hDC, path, pMatrix, pGraphState, ++ fill_color, stroke_color, ++ fill_options)) { ++ return true; ++ } ++ } ++ } ++ } ++ const bool fill = ++ fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill; ++ HPEN hPen = nullptr; ++ HBRUSH hBrush = nullptr; ++ if (pGraphState && stroke_alpha) { ++ SetMiterLimit(m_hDC, pGraphState->m_MiterLimit, nullptr); ++ hPen = CreateExtPen(pGraphState, pMatrix, stroke_color); ++ hPen = (HPEN)SelectObject(m_hDC, hPen); ++ } ++ if (fill && fill_alpha) { ++ SetPolyFillMode(m_hDC, FillTypeToGdiFillType(fill_options.fill_type)); ++ hBrush = CreateBrush(fill_color); ++ hBrush = (HBRUSH)SelectObject(m_hDC, hBrush); ++ } ++ if (path.GetPoints().size() == 2 && pGraphState && ++ !pGraphState->m_DashArray.empty()) { ++ CFX_PointF pos1 = path.GetPoint(0); ++ CFX_PointF pos2 = path.GetPoint(1); ++ if (pMatrix) { ++ pos1 = pMatrix->Transform(pos1); ++ pos2 = pMatrix->Transform(pos2); ++ } ++ DrawLine(pos1.x, pos1.y, pos2.x, pos2.y); ++ } else { ++ SetPathToDC(m_hDC, path, pMatrix); ++ if (pGraphState && stroke_alpha) { ++ if (fill && fill_alpha) { ++ if (fill_options.text_mode) { ++ StrokeAndFillPath(m_hDC); ++ } else { ++ FillPath(m_hDC); ++ SetPathToDC(m_hDC, path, pMatrix); ++ StrokePath(m_hDC); ++ } ++ } else { ++ StrokePath(m_hDC); ++ } ++ } else if (fill && fill_alpha) { ++ FillPath(m_hDC); ++ } ++ } ++ if (hPen) { ++ hPen = (HPEN)SelectObject(m_hDC, hPen); ++ DeleteObject(hPen); ++ } ++ if (hBrush) { ++ hBrush = (HBRUSH)SelectObject(m_hDC, hBrush); ++ DeleteObject(hBrush); ++ } ++ return true; ++} ++ ++bool CGdiDeviceDriver::FillRectWithBlend(const FX_RECT& rect, ++ uint32_t fill_color, ++ BlendMode blend_type) { ++ if (blend_type != BlendMode::kNormal) ++ return false; ++ ++ int alpha; ++ FX_COLORREF colorref; ++ std::tie(alpha, colorref) = ArgbToAlphaAndColorRef(fill_color); ++ if (alpha == 0) ++ return true; ++ ++ if (alpha < 255) ++ return false; ++ ++ HBRUSH hBrush = CreateSolidBrush(colorref); ++ const RECT* pRect = reinterpret_cast(&rect); ++ ::FillRect(m_hDC, pRect, hBrush); ++ DeleteObject(hBrush); ++ return true; ++} ++ ++void CGdiDeviceDriver::SetBaseClip(const FX_RECT& rect) { ++ m_BaseClipBox = rect; ++} ++ ++bool CGdiDeviceDriver::SetClip_PathFill( ++ const CFX_Path& path, ++ const CFX_Matrix* pMatrix, ++ const CFX_FillRenderOptions& fill_options) { ++ absl::optional maybe_rectf = path.GetRect(pMatrix); ++ if (maybe_rectf.has_value()) { ++ FX_RECT rect = maybe_rectf.value().GetOuterRect(); ++ // Can easily apply base clip to protect against wildly large rectangular ++ // clips. crbug.com/1019026 ++ if (m_BaseClipBox.has_value()) ++ rect.Intersect(m_BaseClipBox.value()); ++ return IntersectClipRect(m_hDC, rect.left, rect.top, rect.right, ++ rect.bottom) != ERROR; ++ } ++ SetPathToDC(m_hDC, path, pMatrix); ++ SetPolyFillMode(m_hDC, FillTypeToGdiFillType(fill_options.fill_type)); ++ SelectClipPath(m_hDC, RGN_AND); ++ return true; ++} ++ ++bool CGdiDeviceDriver::SetClip_PathStroke( ++ const CFX_Path& path, ++ const CFX_Matrix* pMatrix, ++ const CFX_GraphStateData* pGraphState) { ++ HPEN hPen = CreateExtPen(pGraphState, pMatrix, 0xff000000); ++ hPen = (HPEN)SelectObject(m_hDC, hPen); ++ SetPathToDC(m_hDC, path, pMatrix); ++ WidenPath(m_hDC); ++ SetPolyFillMode(m_hDC, WINDING); ++ bool ret = !!SelectClipPath(m_hDC, RGN_AND); ++ hPen = (HPEN)SelectObject(m_hDC, hPen); ++ DeleteObject(hPen); ++ return ret; ++} ++ ++bool CGdiDeviceDriver::DrawCosmeticLine(const CFX_PointF& ptMoveTo, ++ const CFX_PointF& ptLineTo, ++ uint32_t color, ++ BlendMode blend_type) { ++ if (blend_type != BlendMode::kNormal) ++ return false; ++ ++ int alpha; ++ FX_COLORREF colorref; ++ std::tie(alpha, colorref) = ArgbToAlphaAndColorRef(color); ++ if (alpha == 0) ++ return true; ++ ++ HPEN hPen = CreatePen(PS_SOLID, 1, colorref); ++ hPen = (HPEN)SelectObject(m_hDC, hPen); ++ MoveToEx(m_hDC, FXSYS_roundf(ptMoveTo.x), FXSYS_roundf(ptMoveTo.y), nullptr); ++ LineTo(m_hDC, FXSYS_roundf(ptLineTo.x), FXSYS_roundf(ptLineTo.y)); ++ hPen = (HPEN)SelectObject(m_hDC, hPen); ++ DeleteObject(hPen); ++ return true; ++} +diff --git a/core/fxge/win32/cgdi_device_driver.h b/core/fxge/win32/cgdi_device_driver.h +new file mode 100644 +index 000000000..59f24c0b2 +--- /dev/null ++++ b/core/fxge/win32/cgdi_device_driver.h +@@ -0,0 +1,80 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXGE_WIN32_CGDI_DEVICE_DRIVER_H_ ++#define CORE_FXGE_WIN32_CGDI_DEVICE_DRIVER_H_ ++ ++#include ++ ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxge/renderdevicedriver_iface.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++ ++class CGdiDeviceDriver : public RenderDeviceDriverIface { ++ protected: ++ CGdiDeviceDriver(HDC hDC, DeviceType device_type); ++ ~CGdiDeviceDriver() override; ++ ++ // RenderDeviceDriverIface: ++ DeviceType GetDeviceType() const override; ++ int GetDeviceCaps(int caps_id) const override; ++ void SaveState() override; ++ void RestoreState(bool bKeepSaved) override; ++ void SetBaseClip(const FX_RECT& rect) override; ++ bool SetClip_PathFill(const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_FillRenderOptions& fill_options) override; ++ bool SetClip_PathStroke(const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_GraphStateData* pGraphState) override; ++ bool DrawPath(const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_GraphStateData* pGraphState, ++ uint32_t fill_color, ++ uint32_t stroke_color, ++ const CFX_FillRenderOptions& fill_options, ++ BlendMode blend_type) override; ++ bool FillRectWithBlend(const FX_RECT& rect, ++ uint32_t fill_color, ++ BlendMode blend_type) override; ++ bool DrawCosmeticLine(const CFX_PointF& ptMoveTo, ++ const CFX_PointF& ptLineTo, ++ uint32_t color, ++ BlendMode blend_type) override; ++ bool GetClipBox(FX_RECT* pRect) override; ++ bool MultiplyAlpha(float alpha) override; ++ bool MultiplyAlpha(const RetainPtr& mask) override; ++ ++ void DrawLine(float x1, float y1, float x2, float y2); ++ ++ bool GDI_SetDIBits(const RetainPtr& pBitmap, ++ const FX_RECT& src_rect, ++ int left, ++ int top); ++ bool GDI_StretchDIBits(const RetainPtr& pBitmap, ++ int dest_left, ++ int dest_top, ++ int dest_width, ++ int dest_height, ++ const FXDIB_ResampleOptions& options); ++ bool GDI_StretchBitMask(const RetainPtr& pBitmap, ++ int dest_left, ++ int dest_top, ++ int dest_width, ++ int dest_height, ++ uint32_t bitmap_color); ++ ++ const HDC m_hDC; ++ bool m_bMetafileDCType; ++ int m_Width; ++ int m_Height; ++ int m_nBitsPerPixel; ++ const DeviceType m_DeviceType; ++ int m_RenderCaps; ++ absl::optional m_BaseClipBox; ++}; ++ ++#endif // CORE_FXGE_WIN32_CGDI_DEVICE_DRIVER_H_ +diff --git a/core/fxge/win32/cgdi_display_driver.cpp b/core/fxge/win32/cgdi_display_driver.cpp +new file mode 100644 +index 000000000..7ba2d766f +--- /dev/null ++++ b/core/fxge/win32/cgdi_display_driver.cpp +@@ -0,0 +1,229 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxge/win32/cgdi_display_driver.h" ++ ++#include "core/fxcrt/fx_coordinates.h" ++#include "core/fxcrt/fx_system.h" ++#include "core/fxge/dib/cfx_dibextractor.h" ++#include "core/fxge/dib/cfx_dibitmap.h" ++#include "core/fxge/render_defines.h" ++#include "core/fxge/win32/cwin32_platform.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++ ++CGdiDisplayDriver::CGdiDisplayDriver(HDC hDC) ++ : CGdiDeviceDriver(hDC, DeviceType::kDisplay) { ++ auto* pPlatform = ++ static_cast(CFX_GEModule::Get()->GetPlatform()); ++ if (pPlatform->m_GdiplusExt.IsAvailable()) { ++ m_RenderCaps |= FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE; ++ } ++} ++ ++CGdiDisplayDriver::~CGdiDisplayDriver() = default; ++ ++int CGdiDisplayDriver::GetDeviceCaps(int caps_id) const { ++ if (caps_id == FXDC_HORZ_SIZE || caps_id == FXDC_VERT_SIZE) ++ return 0; ++ return CGdiDeviceDriver::GetDeviceCaps(caps_id); ++} ++ ++bool CGdiDisplayDriver::GetDIBits(const RetainPtr& pBitmap, ++ int left, ++ int top) { ++ bool ret = false; ++ int width = pBitmap->GetWidth(); ++ int height = pBitmap->GetHeight(); ++ HBITMAP hbmp = CreateCompatibleBitmap(m_hDC, width, height); ++ HDC hDCMemory = CreateCompatibleDC(m_hDC); ++ HBITMAP holdbmp = (HBITMAP)SelectObject(hDCMemory, hbmp); ++ BitBlt(hDCMemory, 0, 0, width, height, m_hDC, left, top, SRCCOPY); ++ SelectObject(hDCMemory, holdbmp); ++ BITMAPINFO bmi; ++ memset(&bmi, 0, sizeof bmi); ++ bmi.bmiHeader.biSize = sizeof bmi.bmiHeader; ++ bmi.bmiHeader.biBitCount = pBitmap->GetBPP(); ++ bmi.bmiHeader.biHeight = -height; ++ bmi.bmiHeader.biPlanes = 1; ++ bmi.bmiHeader.biWidth = width; ++ if (pBitmap->GetBPP() > 8) { ++ ret = ::GetDIBits(hDCMemory, hbmp, 0, height, pBitmap->GetBuffer().data(), ++ &bmi, DIB_RGB_COLORS) == height; ++ } else { ++ auto bitmap = pdfium::MakeRetain(); ++ if (bitmap->Create(width, height, FXDIB_Format::kRgb)) { ++ bmi.bmiHeader.biBitCount = 24; ++ ::GetDIBits(hDCMemory, hbmp, 0, height, bitmap->GetBuffer().data(), &bmi, ++ DIB_RGB_COLORS); ++ ret = pBitmap->TransferBitmap(0, 0, width, height, bitmap, 0, 0); ++ } else { ++ ret = false; ++ } ++ } ++ if (ret && pBitmap->IsAlphaFormat()) ++ pBitmap->SetUniformOpaqueAlpha(); ++ ++ DeleteObject(hbmp); ++ DeleteObject(hDCMemory); ++ return ret; ++} ++ ++bool CGdiDisplayDriver::SetDIBits(const RetainPtr& pSource, ++ uint32_t color, ++ const FX_RECT& src_rect, ++ int left, ++ int top, ++ BlendMode blend_type) { ++ DCHECK_EQ(blend_type, BlendMode::kNormal); ++ if (pSource->IsMaskFormat()) { ++ int width = pSource->GetWidth(), height = pSource->GetHeight(); ++ int alpha = FXARGB_A(color); ++ if (pSource->GetBPP() != 1 || alpha != 255) { ++ auto background = pdfium::MakeRetain(); ++ if (!background->Create(width, height, FXDIB_Format::kRgb32) || ++ !GetDIBits(background, left, top) || ++ !background->CompositeMask(0, 0, width, height, pSource, color, 0, 0, ++ BlendMode::kNormal, nullptr, false)) { ++ return false; ++ } ++ FX_RECT alpha_src_rect(0, 0, width, height); ++ return SetDIBits(background, 0, alpha_src_rect, left, top, ++ BlendMode::kNormal); ++ } ++ FX_RECT clip_rect(left, top, left + src_rect.Width(), ++ top + src_rect.Height()); ++ return StretchDIBits(pSource, color, left - src_rect.left, ++ top - src_rect.top, width, height, &clip_rect, ++ FXDIB_ResampleOptions(), BlendMode::kNormal); ++ } ++ int width = src_rect.Width(); ++ int height = src_rect.Height(); ++ if (pSource->IsAlphaFormat()) { ++ auto bitmap = pdfium::MakeRetain(); ++ if (!bitmap->Create(width, height, FXDIB_Format::kRgb) || ++ !GetDIBits(bitmap, left, top) || ++ !bitmap->CompositeBitmap(0, 0, width, height, pSource, src_rect.left, ++ src_rect.top, BlendMode::kNormal, nullptr, ++ false)) { ++ return false; ++ } ++ FX_RECT alpha_src_rect(0, 0, width, height); ++ return SetDIBits(bitmap, 0, alpha_src_rect, left, top, BlendMode::kNormal); ++ } ++ CFX_DIBExtractor temp(pSource); ++ RetainPtr pBitmap = temp.GetBitmap(); ++ if (!pBitmap) ++ return false; ++ return GDI_SetDIBits(pBitmap, src_rect, left, top); ++} ++ ++bool CGdiDisplayDriver::UseFoxitStretchEngine( ++ const RetainPtr& pSource, ++ uint32_t color, ++ int dest_left, ++ int dest_top, ++ int dest_width, ++ int dest_height, ++ const FX_RECT* pClipRect, ++ const FXDIB_ResampleOptions& options) { ++ FX_RECT bitmap_clip = *pClipRect; ++ if (dest_width < 0) ++ dest_left += dest_width; ++ ++ if (dest_height < 0) ++ dest_top += dest_height; ++ ++ bitmap_clip.Offset(-dest_left, -dest_top); ++ RetainPtr pStretched = ++ pSource->StretchTo(dest_width, dest_height, options, &bitmap_clip); ++ if (!pStretched) ++ return true; ++ ++ FX_RECT src_rect(0, 0, pStretched->GetWidth(), pStretched->GetHeight()); ++ return SetDIBits(pStretched, color, src_rect, pClipRect->left, pClipRect->top, ++ BlendMode::kNormal); ++} ++ ++bool CGdiDisplayDriver::StretchDIBits(const RetainPtr& pSource, ++ uint32_t color, ++ int dest_left, ++ int dest_top, ++ int dest_width, ++ int dest_height, ++ const FX_RECT* pClipRect, ++ const FXDIB_ResampleOptions& options, ++ BlendMode blend_type) { ++ DCHECK(pSource); ++ DCHECK(pClipRect); ++ ++ if (options.HasAnyOptions() || dest_width > 10000 || dest_width < -10000 || ++ dest_height > 10000 || dest_height < -10000) { ++ return UseFoxitStretchEngine(pSource, color, dest_left, dest_top, ++ dest_width, dest_height, pClipRect, options); ++ } ++ if (pSource->IsMaskFormat()) { ++ FX_RECT image_rect; ++ image_rect.left = dest_width > 0 ? dest_left : dest_left + dest_width; ++ image_rect.right = dest_width > 0 ? dest_left + dest_width : dest_left; ++ image_rect.top = dest_height > 0 ? dest_top : dest_top + dest_height; ++ image_rect.bottom = dest_height > 0 ? dest_top + dest_height : dest_top; ++ FX_RECT clip_rect = image_rect; ++ clip_rect.Intersect(*pClipRect); ++ clip_rect.Offset(-image_rect.left, -image_rect.top); ++ int clip_width = clip_rect.Width(), clip_height = clip_rect.Height(); ++ RetainPtr pStretched(pSource->StretchTo( ++ dest_width, dest_height, FXDIB_ResampleOptions(), &clip_rect)); ++ if (!pStretched) ++ return true; ++ ++ auto background = pdfium::MakeRetain(); ++ if (!background->Create(clip_width, clip_height, FXDIB_Format::kRgb32) || ++ !GetDIBits(background, image_rect.left + clip_rect.left, ++ image_rect.top + clip_rect.top) || ++ !background->CompositeMask(0, 0, clip_width, clip_height, pStretched, ++ color, 0, 0, BlendMode::kNormal, nullptr, ++ false)) { ++ return false; ++ } ++ ++ FX_RECT src_rect(0, 0, clip_width, clip_height); ++ return SetDIBits(background, 0, src_rect, image_rect.left + clip_rect.left, ++ image_rect.top + clip_rect.top, BlendMode::kNormal); ++ } ++ if (pSource->IsAlphaFormat()) { ++ auto* pPlatform = ++ static_cast(CFX_GEModule::Get()->GetPlatform()); ++ if (pPlatform->m_GdiplusExt.IsAvailable()) { ++ CFX_DIBExtractor temp(pSource); ++ RetainPtr pBitmap = temp.GetBitmap(); ++ if (!pBitmap) ++ return false; ++ return pPlatform->m_GdiplusExt.StretchDIBits( ++ m_hDC, pBitmap, dest_left, dest_top, dest_width, dest_height, ++ pClipRect, FXDIB_ResampleOptions()); ++ } ++ return UseFoxitStretchEngine(pSource, color, dest_left, dest_top, ++ dest_width, dest_height, pClipRect, ++ FXDIB_ResampleOptions()); ++ } ++ CFX_DIBExtractor temp(pSource); ++ RetainPtr pBitmap = temp.GetBitmap(); ++ if (!pBitmap) ++ return false; ++ return GDI_StretchDIBits(pBitmap, dest_left, dest_top, dest_width, ++ dest_height, FXDIB_ResampleOptions()); ++} ++ ++bool CGdiDisplayDriver::StartDIBits(const RetainPtr& pBitmap, ++ int bitmap_alpha, ++ uint32_t color, ++ const CFX_Matrix& matrix, ++ const FXDIB_ResampleOptions& options, ++ std::unique_ptr* handle, ++ BlendMode blend_type) { ++ return false; ++} +diff --git a/core/fxge/win32/cgdi_display_driver.h b/core/fxge/win32/cgdi_display_driver.h +new file mode 100644 +index 000000000..2e4319483 +--- /dev/null ++++ b/core/fxge/win32/cgdi_display_driver.h +@@ -0,0 +1,66 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXGE_WIN32_CGDI_DISPLAY_DRIVER_H_ ++#define CORE_FXGE_WIN32_CGDI_DISPLAY_DRIVER_H_ ++ ++#include ++#include ++ ++#include ++ ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxge/win32/cgdi_device_driver.h" ++ ++class CFX_DIBBase; ++struct FXDIB_ResampleOptions; ++struct FX_RECT; ++ ++class CGdiDisplayDriver final : public CGdiDeviceDriver { ++ public: ++ explicit CGdiDisplayDriver(HDC hDC); ++ ~CGdiDisplayDriver() override; ++ ++ private: ++ // CGdiDisplayDriver: ++ int GetDeviceCaps(int caps_id) const override; ++ bool GetDIBits(const RetainPtr& pBitmap, ++ int left, ++ int top) override; ++ bool SetDIBits(const RetainPtr& pBitmap, ++ uint32_t color, ++ const FX_RECT& src_rect, ++ int left, ++ int top, ++ BlendMode blend_type) override; ++ bool StretchDIBits(const RetainPtr& pBitmap, ++ uint32_t color, ++ int dest_left, ++ int dest_top, ++ int dest_width, ++ int dest_height, ++ const FX_RECT* pClipRect, ++ const FXDIB_ResampleOptions& options, ++ BlendMode blend_type) override; ++ bool StartDIBits(const RetainPtr& pBitmap, ++ int bitmap_alpha, ++ uint32_t color, ++ const CFX_Matrix& matrix, ++ const FXDIB_ResampleOptions& options, ++ std::unique_ptr* handle, ++ BlendMode blend_type) override; ++ ++ bool UseFoxitStretchEngine(const RetainPtr& pSource, ++ uint32_t color, ++ int dest_left, ++ int dest_top, ++ int dest_width, ++ int dest_height, ++ const FX_RECT* pClipRect, ++ const FXDIB_ResampleOptions& options); ++}; ++ ++#endif // CORE_FXGE_WIN32_CGDI_DISPLAY_DRIVER_H_ +diff --git a/core/fxge/win32/fx_win32_gdipext.cpp b/core/fxge/win32/cgdi_plus_ext.cpp +similarity index 51% +rename from core/fxge/win32/fx_win32_gdipext.cpp +rename to core/fxge/win32/cgdi_plus_ext.cpp +index 272e4085d..06571dad1 100644 +--- a/core/fxge/win32/fx_win32_gdipext.cpp ++++ b/core/fxge/win32/cgdi_plus_ext.cpp +@@ -1,24 +1,33 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + ++#include "core/fxge/win32/cgdi_plus_ext.h" ++ + #include + + #include + + #include +-#include + #include + #include ++#include + ++#include "core/fxcrt/fx_memory.h" ++#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/fx_string_wrappers.h" + #include "core/fxcrt/fx_system.h" ++#include "core/fxge/cfx_fillrenderoptions.h" + #include "core/fxge/cfx_gemodule.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" +-#include "core/fxge/win32/cfx_windowsdib.h" +-#include "core/fxge/win32/win32_int.h" ++#include "core/fxge/cfx_path.h" ++#include "core/fxge/dib/cfx_dibitmap.h" ++#include "core/fxge/win32/cwin32_platform.h" ++#include "third_party/base/notreached.h" ++#include "third_party/base/numerics/safe_conversions.h" ++#include "third_party/base/span.h" + + // Has to come before gdiplus.h + namespace Gdiplus { +@@ -46,15 +55,6 @@ enum { + FuncId_GdipDeletePen, + FuncId_GdipDeletePath, + FuncId_GdipDeleteGraphics, +- FuncId_GdipCreateBitmapFromFileICM, +- FuncId_GdipCreateBitmapFromStreamICM, +- FuncId_GdipGetImageHeight, +- FuncId_GdipGetImageWidth, +- FuncId_GdipGetImagePixelFormat, +- FuncId_GdipBitmapLockBits, +- FuncId_GdipGetImagePaletteSize, +- FuncId_GdipGetImagePalette, +- FuncId_GdipBitmapUnlockBits, + FuncId_GdipDisposeImage, + FuncId_GdipCreateBitmapFromScan0, + FuncId_GdipSetImagePalette, +@@ -93,15 +93,6 @@ LPCSTR g_GdipFuncNames[] = { + "GdipDeletePen", + "GdipDeletePath", + "GdipDeleteGraphics", +- "GdipCreateBitmapFromFileICM", +- "GdipCreateBitmapFromStreamICM", +- "GdipGetImageHeight", +- "GdipGetImageWidth", +- "GdipGetImagePixelFormat", +- "GdipBitmapLockBits", +- "GdipGetImagePaletteSize", +- "GdipGetImagePalette", +- "GdipBitmapUnlockBits", + "GdipDisposeImage", + "GdipCreateBitmapFromScan0", + "GdipSetImagePalette", +@@ -123,181 +114,79 @@ LPCSTR g_GdipFuncNames[] = { + "GdipSetWorldTransform", + "GdipSetPixelOffsetMode", + }; +-static_assert(FX_ArraySize(g_GdipFuncNames) == ++static_assert(std::size(g_GdipFuncNames) == + static_cast(FuncId_GdipSetPixelOffsetMode) + 1, + "g_GdipFuncNames has wrong size"); + +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreatePath2)( +- GDIPCONST Gdiplus::GpPointF*, +- GDIPCONST BYTE*, +- INT, +- Gdiplus::GpFillMode, +- Gdiplus::GpPath** path); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashArray)( +- Gdiplus::GpPen* pen, +- GDIPCONST Gdiplus::REAL* dash, +- INT count); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenLineJoin)( +- Gdiplus::GpPen* pen, +- Gdiplus::GpLineJoin lineJoin); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateFromHDC)( +- HDC hdc, +- Gdiplus::GpGraphics** graphics); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPageUnit)( +- Gdiplus::GpGraphics* graphics, +- Gdiplus::GpUnit unit); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetSmoothingMode)( +- Gdiplus::GpGraphics* graphics, +- Gdiplus::SmoothingMode smoothingMode); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateSolidFill)( +- Gdiplus::ARGB color, +- Gdiplus::GpSolidFill** brush); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipFillPath)( +- Gdiplus::GpGraphics* graphics, +- Gdiplus::GpBrush* brush, +- Gdiplus::GpPath* path); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeleteBrush)( +- Gdiplus::GpBrush* brush); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreatePen1)( +- Gdiplus::ARGB color, +- Gdiplus::REAL width, +- Gdiplus::GpUnit unit, +- Gdiplus::GpPen** pen); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenMiterLimit)( +- Gdiplus::GpPen* pen, +- Gdiplus::REAL miterLimit); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDrawPath)( +- Gdiplus::GpGraphics* graphics, +- Gdiplus::GpPen* pen, +- Gdiplus::GpPath* path); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeletePen)( +- Gdiplus::GpPen* pen); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeletePath)( +- Gdiplus::GpPath* path); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeleteGraphics)( +- Gdiplus::GpGraphics* graphics); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromFileICM)( +- GDIPCONST WCHAR* filename, +- Gdiplus::GpBitmap** bitmap); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromStreamICM)( +- IStream* stream, +- Gdiplus::GpBitmap** bitmap); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImageWidth)( +- Gdiplus::GpImage* image, +- UINT* width); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImageHeight)( +- Gdiplus::GpImage* image, +- UINT* height); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImagePixelFormat)( +- Gdiplus::GpImage* image, +- Gdiplus::PixelFormat* format); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipBitmapLockBits)( +- Gdiplus::GpBitmap* bitmap, +- GDIPCONST Gdiplus::GpRect* rect, +- UINT flags, +- Gdiplus::PixelFormat format, +- Gdiplus::BitmapData* lockedBitmapData); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImagePalette)( +- Gdiplus::GpImage* image, +- Gdiplus::ColorPalette* palette, +- INT size); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImagePaletteSize)( +- Gdiplus::GpImage* image, +- INT* size); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipBitmapUnlockBits)( +- Gdiplus::GpBitmap* bitmap, +- Gdiplus::BitmapData* lockedBitmapData); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDisposeImage)( +- Gdiplus::GpImage* image); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromScan0)( +- INT width, +- INT height, +- INT stride, +- Gdiplus::PixelFormat format, +- BYTE* scan0, +- Gdiplus::GpBitmap** bitmap); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetImagePalette)( +- Gdiplus::GpImage* image, +- GDIPCONST Gdiplus::ColorPalette* palette); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetInterpolationMode)( +- Gdiplus::GpGraphics* graphics, +- Gdiplus::InterpolationMode interpolationMode); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDrawImagePointsI)( +- Gdiplus::GpGraphics* graphics, +- Gdiplus::GpImage* image, +- GDIPCONST Gdiplus::GpPoint* dstpoints, +- INT count); +-typedef Gdiplus::Status(WINAPI* FuncType_GdiplusStartup)( +- OUT uintptr_t* token, +- const Gdiplus::GdiplusStartupInput* input, +- OUT Gdiplus::GdiplusStartupOutput* output); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDrawLineI)( +- Gdiplus::GpGraphics* graphics, +- Gdiplus::GpPen* pen, +- int x1, +- int y1, +- int x2, +- int y2); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreatePath)( +- Gdiplus::GpFillMode brushMode, +- Gdiplus::GpPath** path); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPathFillMode)( +- Gdiplus::GpPath* path, +- Gdiplus::GpFillMode fillmode); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetClipRegion)( +- Gdiplus::GpGraphics* graphics, +- Gdiplus::GpRegion* region, +- Gdiplus::CombineMode combineMode); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipWidenPath)( +- Gdiplus::GpPath* nativePath, +- Gdiplus::GpPen* pen, +- Gdiplus::GpMatrix* matrix, +- Gdiplus::REAL flatness); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipAddPathLine)( +- Gdiplus::GpPath* path, +- Gdiplus::REAL x1, +- Gdiplus::REAL y1, +- Gdiplus::REAL x2, +- Gdiplus::REAL y2); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipAddPathRectangle)( +- Gdiplus::GpPath* path, +- Gdiplus::REAL x, +- Gdiplus::REAL y, +- Gdiplus::REAL width, +- Gdiplus::REAL height); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeleteRegion)( +- Gdiplus::GpRegion* region); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenLineCap197819)( +- Gdiplus::GpPen* pen, +- Gdiplus::GpLineCap startCap, +- Gdiplus::GpLineCap endCap, +- Gdiplus::GpDashCap dashCap); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashOffset)( +- Gdiplus::GpPen* pen, +- Gdiplus::REAL offset); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateMatrix2)( +- Gdiplus::REAL m11, +- Gdiplus::REAL m12, +- Gdiplus::REAL m21, +- Gdiplus::REAL m22, +- Gdiplus::REAL dx, +- Gdiplus::REAL dy, +- Gdiplus::GpMatrix** matrix); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeleteMatrix)( +- Gdiplus::GpMatrix* matrix); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetWorldTransform)( +- Gdiplus::GpGraphics* graphics, +- Gdiplus::GpMatrix* matrix); +-typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPixelOffsetMode)( +- Gdiplus::GpGraphics* graphics, +- Gdiplus::PixelOffsetMode pixelOffsetMode); ++using FuncType_GdipCreatePath2 = ++ decltype(&Gdiplus::DllExports::GdipCreatePath2); ++using FuncType_GdipSetPenDashArray = ++ decltype(&Gdiplus::DllExports::GdipSetPenDashArray); ++using FuncType_GdipSetPenLineJoin = ++ decltype(&Gdiplus::DllExports::GdipSetPenLineJoin); ++using FuncType_GdipCreateFromHDC = ++ decltype(&Gdiplus::DllExports::GdipCreateFromHDC); ++using FuncType_GdipSetPageUnit = ++ decltype(&Gdiplus::DllExports::GdipSetPageUnit); ++using FuncType_GdipSetSmoothingMode = ++ decltype(&Gdiplus::DllExports::GdipSetSmoothingMode); ++using FuncType_GdipCreateSolidFill = ++ decltype(&Gdiplus::DllExports::GdipCreateSolidFill); ++using FuncType_GdipFillPath = decltype(&Gdiplus::DllExports::GdipFillPath); ++using FuncType_GdipDeleteBrush = ++ decltype(&Gdiplus::DllExports::GdipDeleteBrush); ++using FuncType_GdipCreatePen1 = decltype(&Gdiplus::DllExports::GdipCreatePen1); ++using FuncType_GdipSetPenMiterLimit = ++ decltype(&Gdiplus::DllExports::GdipSetPenMiterLimit); ++using FuncType_GdipDrawPath = decltype(&Gdiplus::DllExports::GdipDrawPath); ++using FuncType_GdipDeletePen = decltype(&Gdiplus::DllExports::GdipDeletePen); ++using FuncType_GdipDeletePath = decltype(&Gdiplus::DllExports::GdipDeletePath); ++using FuncType_GdipDeleteGraphics = ++ decltype(&Gdiplus::DllExports::GdipDeleteGraphics); ++using FuncType_GdipDisposeImage = ++ decltype(&Gdiplus::DllExports::GdipDisposeImage); ++using FuncType_GdipCreateBitmapFromScan0 = ++ decltype(&Gdiplus::DllExports::GdipCreateBitmapFromScan0); ++using FuncType_GdipSetImagePalette = ++ decltype(&Gdiplus::DllExports::GdipSetImagePalette); ++using FuncType_GdipSetInterpolationMode = ++ decltype(&Gdiplus::DllExports::GdipSetInterpolationMode); ++using FuncType_GdipDrawImagePointsI = ++ decltype(&Gdiplus::DllExports::GdipDrawImagePointsI); ++using FuncType_GdiplusStartup = decltype(&Gdiplus::GdiplusStartup); ++using FuncType_GdipDrawLineI = decltype(&Gdiplus::DllExports::GdipDrawLineI); ++using FuncType_GdipCreatePath = decltype(&Gdiplus::DllExports::GdipCreatePath); ++using FuncType_GdipSetPathFillMode = ++ decltype(&Gdiplus::DllExports::GdipSetPathFillMode); ++using FuncType_GdipSetClipRegion = ++ decltype(&Gdiplus::DllExports::GdipSetClipRegion); ++using FuncType_GdipWidenPath = decltype(&Gdiplus::DllExports::GdipWidenPath); ++using FuncType_GdipAddPathLine = ++ decltype(&Gdiplus::DllExports::GdipAddPathLine); ++using FuncType_GdipAddPathRectangle = ++ decltype(&Gdiplus::DllExports::GdipAddPathRectangle); ++using FuncType_GdipDeleteRegion = ++ decltype(&Gdiplus::DllExports::GdipDeleteRegion); ++using FuncType_GdipSetPenLineCap197819 = ++ decltype(&Gdiplus::DllExports::GdipSetPenLineCap197819); ++using FuncType_GdipSetPenDashOffset = ++ decltype(&Gdiplus::DllExports::GdipSetPenDashOffset); ++using FuncType_GdipCreateMatrix2 = ++ decltype(&Gdiplus::DllExports::GdipCreateMatrix2); ++using FuncType_GdipDeleteMatrix = ++ decltype(&Gdiplus::DllExports::GdipDeleteMatrix); ++using FuncType_GdipSetWorldTransform = ++ decltype(&Gdiplus::DllExports::GdipSetWorldTransform); ++using FuncType_GdipSetPixelOffsetMode = ++ decltype(&Gdiplus::DllExports::GdipSetPixelOffsetMode); + #define CallFunc(funcname) \ + reinterpret_cast( \ + GdiplusExt.m_Functions[FuncId_##funcname]) + +-Gdiplus::GpFillMode GdiFillType2Gdip(int fill_type) { +- return fill_type == ALTERNATE ? Gdiplus::FillModeAlternate +- : Gdiplus::FillModeWinding; ++Gdiplus::GpFillMode FillType2Gdip(CFX_FillRenderOptions::FillType fill_type) { ++ return fill_type == CFX_FillRenderOptions::FillType::kEvenOdd ++ ? Gdiplus::FillModeAlternate ++ : Gdiplus::FillModeWinding; + } + + const CGdiplusExt& GetGdiplusExt() { +@@ -315,40 +204,43 @@ Gdiplus::GpBrush* GdipCreateBrushImpl(DWORD argb) { + + void OutputImage(Gdiplus::GpGraphics* pGraphics, + const RetainPtr& pBitmap, +- const FX_RECT* pSrcRect, ++ const FX_RECT& src_rect, + int dest_left, + int dest_top, + int dest_width, + int dest_height) { +- int src_width = pSrcRect->Width(), src_height = pSrcRect->Height(); ++ int src_width = src_rect.Width(); ++ int src_height = src_rect.Height(); + const CGdiplusExt& GdiplusExt = GetGdiplusExt(); +- if (pBitmap->GetBPP() == 1 && (pSrcRect->left % 8)) { ++ if (pBitmap->GetBPP() == 1 && (src_rect.left % 8)) { + FX_RECT new_rect(0, 0, src_width, src_height); +- RetainPtr pCloned = pBitmap->Clone(pSrcRect); ++ RetainPtr pCloned = pBitmap->ClipTo(src_rect); + if (!pCloned) + return; +- OutputImage(pGraphics, pCloned, &new_rect, dest_left, dest_top, dest_width, ++ OutputImage(pGraphics, pCloned, new_rect, dest_left, dest_top, dest_width, + dest_height); + return; + } + int src_pitch = pBitmap->GetPitch(); +- uint8_t* scan0 = pBitmap->GetBuffer() + pSrcRect->top * src_pitch + +- pBitmap->GetBPP() * pSrcRect->left / 8; ++ uint8_t* scan0 = pBitmap->GetBuffer() ++ .subspan(src_rect.top * src_pitch + ++ pBitmap->GetBPP() * src_rect.left / 8) ++ .data(); + Gdiplus::GpBitmap* bitmap = nullptr; + switch (pBitmap->GetFormat()) { +- case FXDIB_Argb: ++ case FXDIB_Format::kArgb: + CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch, + PixelFormat32bppARGB, scan0, &bitmap); + break; +- case FXDIB_Rgb32: ++ case FXDIB_Format::kRgb32: + CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch, + PixelFormat32bppRGB, scan0, &bitmap); + break; +- case FXDIB_Rgb: ++ case FXDIB_Format::kRgb: + CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch, + PixelFormat24bppRGB, scan0, &bitmap); + break; +- case FXDIB_8bppRgb: { ++ case FXDIB_Format::k8bppRgb: { + CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch, + PixelFormat8bppIndexed, scan0, + &bitmap); +@@ -360,12 +252,16 @@ void OutputImage(Gdiplus::GpGraphics* pGraphics, + CallFunc(GdipSetImagePalette)(bitmap, (Gdiplus::ColorPalette*)pal); + break; + } +- case FXDIB_1bppRgb: { ++ case FXDIB_Format::k1bppRgb: { + CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch, + PixelFormat1bppIndexed, scan0, + &bitmap); + break; + } ++ case FXDIB_Format::kInvalid: ++ case FXDIB_Format::k1bppMask: ++ case FXDIB_Format::k8bppMask: ++ NOTREACHED_NORETURN(); + } + if (dest_height < 0) { + dest_height--; +@@ -400,15 +296,15 @@ Gdiplus::GpPen* GdipCreatePenImpl(const CFX_GraphStateData* pGraphState, + Gdiplus::DashCap dashCap = Gdiplus::DashCapFlat; + bool bDashExtend = false; + switch (pGraphState->m_LineCap) { +- case CFX_GraphStateData::LineCapButt: ++ case CFX_GraphStateData::LineCap::kButt: + lineCap = Gdiplus::LineCapFlat; + break; +- case CFX_GraphStateData::LineCapRound: ++ case CFX_GraphStateData::LineCap::kRound: + lineCap = Gdiplus::LineCapRound; + dashCap = Gdiplus::DashCapRound; + bDashExtend = true; + break; +- case CFX_GraphStateData::LineCapSquare: ++ case CFX_GraphStateData::LineCap::kSquare: + lineCap = Gdiplus::LineCapSquare; + bDashExtend = true; + break; +@@ -416,13 +312,13 @@ Gdiplus::GpPen* GdipCreatePenImpl(const CFX_GraphStateData* pGraphState, + CallFunc(GdipSetPenLineCap197819)(pPen, lineCap, lineCap, dashCap); + Gdiplus::LineJoin lineJoin = Gdiplus::LineJoinMiterClipped; + switch (pGraphState->m_LineJoin) { +- case CFX_GraphStateData::LineJoinMiter: ++ case CFX_GraphStateData::LineJoin::kMiter: + lineJoin = Gdiplus::LineJoinMiterClipped; + break; +- case CFX_GraphStateData::LineJoinRound: ++ case CFX_GraphStateData::LineJoin::kRound: + lineJoin = Gdiplus::LineJoinRound; + break; +- case CFX_GraphStateData::LineJoinBevel: ++ case CFX_GraphStateData::LineJoin::kBevel: + lineJoin = Gdiplus::LineJoinBevel; + break; + } +@@ -431,7 +327,8 @@ Gdiplus::GpPen* GdipCreatePenImpl(const CFX_GraphStateData* pGraphState, + float* pDashArray = + FX_Alloc(float, FxAlignToBoundary<2>(pGraphState->m_DashArray.size())); + int nCount = 0; +- float on_leftover = 0, off_leftover = 0; ++ float on_leftover = 0; ++ float off_leftover = 0; + for (size_t i = 0; i < pGraphState->m_DashArray.size(); i += 2) { + float on_phase = pGraphState->m_DashArray[i]; + float off_phase; +@@ -442,15 +339,15 @@ Gdiplus::GpPen* GdipCreatePenImpl(const CFX_GraphStateData* pGraphState, + on_phase /= width; + off_phase /= width; + if (on_phase + off_phase <= 0.00002f) { +- on_phase = 1.0f / 10; +- off_phase = 1.0f / 10; ++ on_phase = 0.1f; ++ off_phase = 0.1f; + } + if (bDashExtend) { + if (off_phase < 1) + off_phase = 0; + else +- off_phase -= 1; +- on_phase += 1; ++ --off_phase; ++ ++on_phase; + } + if (on_phase == 0 || off_phase == 0) { + if (nCount == 0) { +@@ -483,12 +380,13 @@ Gdiplus::GpPen* GdipCreatePenImpl(const CFX_GraphStateData* pGraphState, + return pPen; + } + +-Optional> IsSmallTriangle(Gdiplus::PointF* points, +- const CFX_Matrix* pMatrix) { +- size_t pairs[] = {1, 2, 0, 2, 0, 1}; +- for (size_t i = 0; i < FX_ArraySize(pairs) / 2; i++) { +- size_t pair1 = pairs[i * 2]; +- size_t pair2 = pairs[i * 2 + 1]; ++absl::optional> IsSmallTriangle( ++ pdfium::span points, ++ const CFX_Matrix* pMatrix) { ++ static constexpr size_t kPairs[3][2] = {{1, 2}, {0, 2}, {0, 1}}; ++ for (size_t i = 0; i < std::size(kPairs); ++i) { ++ size_t pair1 = kPairs[i][0]; ++ size_t pair2 = kPairs[i][1]; + + CFX_PointF p1(points[pair1].X, points[pair1].Y); + CFX_PointF p2(points[pair2].X, points[pair2].Y); +@@ -499,15 +397,15 @@ Optional> IsSmallTriangle(Gdiplus::PointF* points, + + CFX_PointF diff = p1 - p2; + float distance_square = (diff.x * diff.x) + (diff.y * diff.y); +- if (distance_square < (1.0f * 2 + 1.0f / 4)) ++ if (distance_square < 2.25f) + return std::make_pair(i, pair1); + } +- return {}; ++ return absl::nullopt; + } + + class GpStream final : public IStream { + public: +- GpStream() : m_RefCount(1), m_ReadPos(0) {} ++ GpStream() = default; + ~GpStream() = default; + + // IUnknown +@@ -542,7 +440,8 @@ class GpStream final : public IStream { + if (m_ReadPos >= m_InterStream.tellp()) + return HRESULT_FROM_WIN32(ERROR_END_OF_MEDIA); + +- size_t bytes_left = m_InterStream.tellp() - m_ReadPos; ++ size_t bytes_left = pdfium::base::checked_cast( ++ std::streamoff(m_InterStream.tellp()) - m_ReadPos); + size_t bytes_out = + std::min(pdfium::base::checked_cast(cb), bytes_left); + memcpy(output, m_InterStream.str().c_str() + m_ReadPos, bytes_out); +@@ -636,112 +535,14 @@ class GpStream final : public IStream { + } + + private: +- LONG m_RefCount; +- std::streamoff m_ReadPos; +- std::ostringstream m_InterStream; +-}; +- +-struct PREVIEW3_DIBITMAP { +- BITMAPINFO* pbmi; +- int Stride; +- LPBYTE pScan0; +- Gdiplus::GpBitmap* pBitmap; +- Gdiplus::BitmapData* pBitmapData; +- GpStream* pStream; ++ LONG m_RefCount = 1; ++ std::streamoff m_ReadPos = 0; ++ fxcrt::ostringstream m_InterStream; + }; + +-PREVIEW3_DIBITMAP* LoadDIBitmap(WINDIB_Open_Args_ args) { +- Gdiplus::GpBitmap* pBitmap; +- GpStream* pStream = nullptr; +- const CGdiplusExt& GdiplusExt = GetGdiplusExt(); +- Gdiplus::Status status = Gdiplus::Ok; +- if (args.flags == WINDIB_OPEN_PATHNAME) { +- status = CallFunc(GdipCreateBitmapFromFileICM)(args.path_name, &pBitmap); +- } else { +- if (args.memory_size == 0 || !args.memory_base) +- return nullptr; +- +- pStream = new GpStream; +- pStream->Write(args.memory_base, (ULONG)args.memory_size, nullptr); +- status = CallFunc(GdipCreateBitmapFromStreamICM)(pStream, &pBitmap); +- } +- if (status != Gdiplus::Ok) { +- if (pStream) +- pStream->Release(); +- +- return nullptr; +- } +- UINT height, width; +- CallFunc(GdipGetImageHeight)(pBitmap, &height); +- CallFunc(GdipGetImageWidth)(pBitmap, &width); +- Gdiplus::PixelFormat pixel_format; +- CallFunc(GdipGetImagePixelFormat)(pBitmap, &pixel_format); +- int info_size = sizeof(BITMAPINFOHEADER); +- int bpp = 24; +- int dest_pixel_format = PixelFormat24bppRGB; +- if (pixel_format == PixelFormat1bppIndexed) { +- info_size += 8; +- bpp = 1; +- dest_pixel_format = PixelFormat1bppIndexed; +- } else if (pixel_format == PixelFormat8bppIndexed) { +- info_size += 1024; +- bpp = 8; +- dest_pixel_format = PixelFormat8bppIndexed; +- } else if (pixel_format == PixelFormat32bppARGB) { +- bpp = 32; +- dest_pixel_format = PixelFormat32bppARGB; +- } +- LPBYTE buf = FX_Alloc(BYTE, info_size); +- BITMAPINFOHEADER* pbmih = (BITMAPINFOHEADER*)buf; +- pbmih->biBitCount = bpp; +- pbmih->biCompression = BI_RGB; +- pbmih->biHeight = -(int)height; +- pbmih->biPlanes = 1; +- pbmih->biWidth = width; +- Gdiplus::Rect rect(0, 0, width, height); +- Gdiplus::BitmapData* pBitmapData = FX_Alloc(Gdiplus::BitmapData, 1); +- CallFunc(GdipBitmapLockBits)(pBitmap, &rect, Gdiplus::ImageLockModeRead, +- dest_pixel_format, pBitmapData); +- if (pixel_format == PixelFormat1bppIndexed || +- pixel_format == PixelFormat8bppIndexed) { +- DWORD* ppal = (DWORD*)(buf + sizeof(BITMAPINFOHEADER)); +- struct { +- UINT flags; +- UINT Count; +- DWORD Entries[256]; +- } pal; +- int size = 0; +- CallFunc(GdipGetImagePaletteSize)(pBitmap, &size); +- CallFunc(GdipGetImagePalette)(pBitmap, (Gdiplus::ColorPalette*)&pal, size); +- int entries = pixel_format == PixelFormat1bppIndexed ? 2 : 256; +- for (int i = 0; i < entries; i++) { +- ppal[i] = pal.Entries[i] & 0x00ffffff; +- } +- } +- PREVIEW3_DIBITMAP* pInfo = FX_Alloc(PREVIEW3_DIBITMAP, 1); +- pInfo->pbmi = (BITMAPINFO*)buf; +- pInfo->pScan0 = (LPBYTE)pBitmapData->Scan0; +- pInfo->Stride = pBitmapData->Stride; +- pInfo->pBitmap = pBitmap; +- pInfo->pBitmapData = pBitmapData; +- pInfo->pStream = pStream; +- return pInfo; +-} +- +-void FreeDIBitmap(PREVIEW3_DIBITMAP* pInfo) { +- const CGdiplusExt& GdiplusExt = GetGdiplusExt(); +- CallFunc(GdipBitmapUnlockBits)(pInfo->pBitmap, pInfo->pBitmapData); +- CallFunc(GdipDisposeImage)(pInfo->pBitmap); +- FX_Free(pInfo->pBitmapData); +- FX_Free((LPBYTE)pInfo->pbmi); +- if (pInfo->pStream) +- pInfo->pStream->Release(); +- FX_Free(pInfo); +-} +- + } // namespace + +-CGdiplusExt::CGdiplusExt() {} ++CGdiplusExt::CGdiplusExt() = default; + + CGdiplusExt::~CGdiplusExt() { + FreeLibrary(m_GdiModule); +@@ -757,8 +558,8 @@ void CGdiplusExt::Load() { + if (!m_hModule) + return; + +- m_Functions.resize(FX_ArraySize(g_GdipFuncNames)); +- for (size_t i = 0; i < FX_ArraySize(g_GdipFuncNames); ++i) { ++ m_Functions.resize(std::size(g_GdipFuncNames)); ++ for (size_t i = 0; i < std::size(g_GdipFuncNames); ++i) { + m_Functions[i] = GetProcAddress(m_hModule, g_GdipFuncNames[i]); + if (!m_Functions[i]) { + m_hModule = nullptr; +@@ -766,10 +567,10 @@ void CGdiplusExt::Load() { + } + } + +- uintptr_t gdiplusToken; +- Gdiplus::GdiplusStartupInput gdiplusStartupInput; ++ ULONG_PTR gdiplus_token; ++ Gdiplus::GdiplusStartupInput gdiplus_startup_input; + ((FuncType_GdiplusStartup)m_Functions[FuncId_GdiplusStartup])( +- &gdiplusToken, &gdiplusStartupInput, nullptr); ++ &gdiplus_token, &gdiplus_startup_input, nullptr); + m_GdiModule = LoadLibraryA("GDI32.DLL"); + } + +@@ -797,7 +598,7 @@ bool CGdiplusExt::StretchDIBits(HDC hDC, + Gdiplus::InterpolationModeBilinear); + } + FX_RECT src_rect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight()); +- OutputImage(pGraphics, pBitmap, &src_rect, dest_left, dest_top, dest_width, ++ OutputImage(pGraphics, pBitmap, src_rect, dest_left, dest_top, dest_width, + dest_height); + CallFunc(GdipDeleteGraphics)(pGraphics); + CallFunc(GdipDeleteGraphics)(pGraphics); +@@ -805,14 +606,14 @@ bool CGdiplusExt::StretchDIBits(HDC hDC, + } + + bool CGdiplusExt::DrawPath(HDC hDC, +- const CFX_PathData* pPathData, ++ const CFX_Path& path, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState, + uint32_t fill_argb, + uint32_t stroke_argb, +- int fill_mode) { +- auto& pPoints = pPathData->GetPoints(); +- if (pPoints.empty()) ++ const CFX_FillRenderOptions& fill_options) { ++ pdfium::span points = path.GetPoints(); ++ if (points.empty()) + return true; + + Gdiplus::GpGraphics* pGraphics = nullptr; +@@ -827,69 +628,74 @@ bool CGdiplusExt::DrawPath(HDC hDC, + pObject2Device->e, pObject2Device->f, &pMatrix); + CallFunc(GdipSetWorldTransform)(pGraphics, pMatrix); + } +- Gdiplus::PointF* points = FX_Alloc(Gdiplus::PointF, pPoints.size()); +- BYTE* types = FX_Alloc(BYTE, pPoints.size()); ++ std::vector gp_points(points.size()); ++ std::vector gp_types(points.size()); + int nSubPathes = 0; + bool bSubClose = false; +- int pos_subclose = 0; + bool bSmooth = false; +- int startpoint = 0; +- for (size_t i = 0; i < pPoints.size(); i++) { +- points[i].X = pPoints[i].m_Point.x; +- points[i].Y = pPoints[i].m_Point.y; ++ size_t pos_subclose = 0; ++ size_t startpoint = 0; ++ for (size_t i = 0; i < points.size(); ++i) { ++ gp_points[i].X = points[i].m_Point.x; ++ gp_points[i].Y = points[i].m_Point.y; + +- CFX_PointF pos = pPoints[i].m_Point; ++ CFX_PointF pos = points[i].m_Point; + if (pObject2Device) + pos = pObject2Device->Transform(pos); + +- if (pos.x > 50000 * 1.0f) +- points[i].X = 50000 * 1.0f; +- if (pos.x < -50000 * 1.0f) +- points[i].X = -50000 * 1.0f; +- if (pos.y > 50000 * 1.0f) +- points[i].Y = 50000 * 1.0f; +- if (pos.y < -50000 * 1.0f) +- points[i].Y = -50000 * 1.0f; +- +- FXPT_TYPE point_type = pPoints[i].m_Type; +- if (point_type == FXPT_TYPE::MoveTo) { +- types[i] = Gdiplus::PathPointTypeStart; ++ if (pos.x > 50000.0f) ++ gp_points[i].X = 50000.0f; ++ if (pos.x < -50000.0f) ++ gp_points[i].X = -50000.0f; ++ if (pos.y > 50000.0f) ++ gp_points[i].Y = 50000.0f; ++ if (pos.y < -50000.0f) ++ gp_points[i].Y = -50000.0f; ++ ++ CFX_Path::Point::Type point_type = points[i].m_Type; ++ if (point_type == CFX_Path::Point::Type::kMove) { ++ gp_types[i] = Gdiplus::PathPointTypeStart; + nSubPathes++; + bSubClose = false; + startpoint = i; +- } else if (point_type == FXPT_TYPE::LineTo) { +- types[i] = Gdiplus::PathPointTypeLine; +- if (pPoints[i - 1].IsTypeAndOpen(FXPT_TYPE::MoveTo) && +- (i == pPoints.size() - 1 || +- pPoints[i + 1].IsTypeAndOpen(FXPT_TYPE::MoveTo)) && +- points[i].Y == points[i - 1].Y && points[i].X == points[i - 1].X) { +- points[i].X += 0.01f; ++ } else if (point_type == CFX_Path::Point::Type::kLine) { ++ gp_types[i] = Gdiplus::PathPointTypeLine; ++ if (points[i - 1].IsTypeAndOpen(CFX_Path::Point::Type::kMove) && ++ (i == points.size() - 1 || ++ points[i + 1].IsTypeAndOpen(CFX_Path::Point::Type::kMove)) && ++ gp_points[i].Y == gp_points[i - 1].Y && ++ gp_points[i].X == gp_points[i - 1].X) { ++ gp_points[i].X += 0.01f; + continue; + } +- if (!bSmooth && points[i].X != points[i - 1].X && +- points[i].Y != points[i - 1].Y) ++ if (!bSmooth && gp_points[i].X != gp_points[i - 1].X && ++ gp_points[i].Y != gp_points[i - 1].Y) { + bSmooth = true; +- } else if (point_type == FXPT_TYPE::BezierTo) { +- types[i] = Gdiplus::PathPointTypeBezier; ++ } ++ } else if (point_type == CFX_Path::Point::Type::kBezier) { ++ gp_types[i] = Gdiplus::PathPointTypeBezier; + bSmooth = true; + } +- if (pPoints[i].m_CloseFigure) { ++ if (points[i].m_CloseFigure) { + if (bSubClose) +- types[pos_subclose] &= ~Gdiplus::PathPointTypeCloseSubpath; ++ gp_types[pos_subclose] &= ~Gdiplus::PathPointTypeCloseSubpath; + else + bSubClose = true; + pos_subclose = i; +- types[i] |= Gdiplus::PathPointTypeCloseSubpath; +- if (!bSmooth && points[i].X != points[startpoint].X && +- points[i].Y != points[startpoint].Y) ++ gp_types[i] |= Gdiplus::PathPointTypeCloseSubpath; ++ if (!bSmooth && gp_points[i].X != gp_points[startpoint].X && ++ gp_points[i].Y != gp_points[startpoint].Y) { + bSmooth = true; ++ } + } + } +- if (fill_mode & FXFILL_NOPATHSMOOTH) { ++ const bool fill = ++ fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill; ++ if (fill_options.aliased_path) { + bSmooth = false; + CallFunc(GdipSetSmoothingMode)(pGraphics, Gdiplus::SmoothingModeNone); +- } else if (!(fill_mode & FXFILL_FULLCOVER)) { +- if (!bSmooth && (fill_mode & 3)) ++ } else if (!fill_options.full_cover) { ++ if (!bSmooth && fill) + bSmooth = true; + + if (bSmooth || (pGraphState && pGraphState->m_LineWidth > 2)) { +@@ -897,56 +703,57 @@ bool CGdiplusExt::DrawPath(HDC hDC, + Gdiplus::SmoothingModeAntiAlias); + } + } +- int new_fill_mode = fill_mode & 3; +- if (pPoints.size() == 4 && !pGraphState) { +- auto indices = IsSmallTriangle(points, pObject2Device); ++ if (points.size() == 4 && !pGraphState) { ++ auto indices = IsSmallTriangle(gp_points, pObject2Device); + if (indices.has_value()) { + size_t v1; + size_t v2; + std::tie(v1, v2) = indices.value(); + Gdiplus::GpPen* pPen = nullptr; + CallFunc(GdipCreatePen1)(fill_argb, 1.0f, Gdiplus::UnitPixel, &pPen); +- CallFunc(GdipDrawLineI)(pGraphics, pPen, FXSYS_roundf(points[v1].X), +- FXSYS_roundf(points[v1].Y), +- FXSYS_roundf(points[v2].X), +- FXSYS_roundf(points[v2].Y)); ++ CallFunc(GdipDrawLineI)(pGraphics, pPen, FXSYS_roundf(gp_points[v1].X), ++ FXSYS_roundf(gp_points[v1].Y), ++ FXSYS_roundf(gp_points[v2].X), ++ FXSYS_roundf(gp_points[v2].Y)); + CallFunc(GdipDeletePen)(pPen); + return true; + } + } + Gdiplus::GpPath* pGpPath = nullptr; +- CallFunc(GdipCreatePath2)(points, types, pPoints.size(), +- GdiFillType2Gdip(new_fill_mode), &pGpPath); ++ const Gdiplus::GpFillMode gp_fill_mode = ++ FillType2Gdip(fill_options.fill_type); ++ CallFunc(GdipCreatePath2)(gp_points.data(), gp_types.data(), ++ pdfium::base::checked_cast(points.size()), ++ gp_fill_mode, &pGpPath); + if (!pGpPath) { + if (pMatrix) + CallFunc(GdipDeleteMatrix)(pMatrix); + +- FX_Free(points); +- FX_Free(types); + CallFunc(GdipDeleteGraphics)(pGraphics); + return false; + } +- if (new_fill_mode) { ++ if (fill) { + Gdiplus::GpBrush* pBrush = GdipCreateBrushImpl(fill_argb); +- CallFunc(GdipSetPathFillMode)(pGpPath, GdiFillType2Gdip(new_fill_mode)); ++ CallFunc(GdipSetPathFillMode)(pGpPath, gp_fill_mode); + CallFunc(GdipFillPath)(pGraphics, pBrush, pGpPath); + CallFunc(GdipDeleteBrush)(pBrush); + } + if (pGraphState && stroke_argb) { + Gdiplus::GpPen* pPen = + GdipCreatePenImpl(pGraphState, pObject2Device, stroke_argb, +- !!(fill_mode & FX_STROKE_TEXT_MODE)); ++ fill_options.stroke_text_mode); + if (nSubPathes == 1) { + CallFunc(GdipDrawPath)(pGraphics, pPen, pGpPath); + } else { +- int iStart = 0; +- for (size_t i = 0; i < pPoints.size(); i++) { +- if (i == pPoints.size() - 1 || +- types[i + 1] == Gdiplus::PathPointTypeStart) { ++ size_t iStart = 0; ++ for (size_t i = 0; i < points.size(); ++i) { ++ if (i == points.size() - 1 || ++ gp_types[i + 1] == Gdiplus::PathPointTypeStart) { + Gdiplus::GpPath* pSubPath; +- CallFunc(GdipCreatePath2)(points + iStart, types + iStart, +- i - iStart + 1, +- GdiFillType2Gdip(new_fill_mode), &pSubPath); ++ CallFunc(GdipCreatePath2)( ++ &gp_points[iStart], &gp_types[iStart], ++ pdfium::base::checked_cast(i - iStart + 1), gp_fill_mode, ++ &pSubPath); + iStart = i + 1; + CallFunc(GdipDrawPath)(pGraphics, pPen, pSubPath); + CallFunc(GdipDeletePath)(pSubPath); +@@ -957,33 +764,7 @@ bool CGdiplusExt::DrawPath(HDC hDC, + } + if (pMatrix) + CallFunc(GdipDeleteMatrix)(pMatrix); +- FX_Free(points); +- FX_Free(types); + CallFunc(GdipDeletePath)(pGpPath); + CallFunc(GdipDeleteGraphics)(pGraphics); + return true; + } +- +-RetainPtr CGdiplusExt::LoadDIBitmap(WINDIB_Open_Args_ args) { +- PREVIEW3_DIBITMAP* pInfo = ::LoadDIBitmap(args); +- if (!pInfo) +- return nullptr; +- +- int height = abs(pInfo->pbmi->bmiHeader.biHeight); +- int width = pInfo->pbmi->bmiHeader.biWidth; +- int dest_pitch = (width * pInfo->pbmi->bmiHeader.biBitCount + 31) / 32 * 4; +- LPBYTE pData = FX_Alloc2D(BYTE, dest_pitch, height); +- if (dest_pitch == pInfo->Stride) { +- memcpy(pData, pInfo->pScan0, dest_pitch * height); +- } else { +- for (int i = 0; i < height; i++) { +- memcpy(pData + dest_pitch * i, pInfo->pScan0 + pInfo->Stride * i, +- dest_pitch); +- } +- } +- RetainPtr pDIBitmap = FX_WindowsDIB_LoadFromBuf( +- pInfo->pbmi, pData, pInfo->pbmi->bmiHeader.biBitCount == 32); +- FX_Free(pData); +- FreeDIBitmap(pInfo); +- return pDIBitmap; +-} +diff --git a/core/fxge/win32/cgdi_plus_ext.h b/core/fxge/win32/cgdi_plus_ext.h +new file mode 100644 +index 000000000..ff6f1659d +--- /dev/null ++++ b/core/fxge/win32/cgdi_plus_ext.h +@@ -0,0 +1,55 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXGE_WIN32_CGDI_PLUS_EXT_H_ ++#define CORE_FXGE_WIN32_CGDI_PLUS_EXT_H_ ++ ++#include ++#include ++ ++#include ++ ++#include "core/fxcrt/retain_ptr.h" ++ ++class CFX_DIBitmap; ++class CFX_GraphStateData; ++class CFX_Matrix; ++class CFX_Path; ++struct CFX_FillRenderOptions; ++struct FXDIB_ResampleOptions; ++struct FX_RECT; ++ ++class CGdiplusExt { ++ public: ++ CGdiplusExt(); ++ ~CGdiplusExt(); ++ ++ void Load(); ++ bool IsAvailable() { return !!m_hModule; } ++ bool StretchDIBits(HDC hDC, ++ const RetainPtr& pBitmap, ++ int dest_left, ++ int dest_top, ++ int dest_width, ++ int dest_height, ++ const FX_RECT* pClipRect, ++ const FXDIB_ResampleOptions& options); ++ bool DrawPath(HDC hDC, ++ const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_GraphStateData* pGraphState, ++ uint32_t fill_argb, ++ uint32_t stroke_argb, ++ const CFX_FillRenderOptions& fill_options); ++ ++ std::vector m_Functions; ++ ++ private: ++ HMODULE m_hModule = nullptr; ++ HMODULE m_GdiModule = nullptr; ++}; ++ ++#endif // CORE_FXGE_WIN32_CGDI_PLUS_EXT_H_ +diff --git a/core/fxge/win32/cgdi_printer_driver.cpp b/core/fxge/win32/cgdi_printer_driver.cpp +new file mode 100644 +index 000000000..8286685e4 +--- /dev/null ++++ b/core/fxge/win32/cgdi_printer_driver.cpp +@@ -0,0 +1,178 @@ ++// Copyright 2014 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxge/win32/cgdi_printer_driver.h" ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "core/fxcrt/fx_memory.h" ++#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxge/cfx_font.h" ++#include "core/fxge/cfx_windowsrenderdevice.h" ++#include "core/fxge/dib/cfx_dibextractor.h" ++#include "core/fxge/dib/cfx_dibitmap.h" ++#include "core/fxge/render_defines.h" ++#include "core/fxge/text_char_pos.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++ ++CGdiPrinterDriver::CGdiPrinterDriver(HDC hDC) ++ : CGdiDeviceDriver(hDC, DeviceType::kPrinter), ++ m_HorzSize(::GetDeviceCaps(m_hDC, HORZSIZE)), ++ m_VertSize(::GetDeviceCaps(m_hDC, VERTSIZE)) {} ++ ++CGdiPrinterDriver::~CGdiPrinterDriver() = default; ++ ++int CGdiPrinterDriver::GetDeviceCaps(int caps_id) const { ++ if (caps_id == FXDC_HORZ_SIZE) ++ return m_HorzSize; ++ if (caps_id == FXDC_VERT_SIZE) ++ return m_VertSize; ++ return CGdiDeviceDriver::GetDeviceCaps(caps_id); ++} ++ ++bool CGdiPrinterDriver::SetDIBits(const RetainPtr& pSource, ++ uint32_t color, ++ const FX_RECT& src_rect, ++ int left, ++ int top, ++ BlendMode blend_type) { ++ if (pSource->IsMaskFormat()) { ++ FX_RECT clip_rect(left, top, left + src_rect.Width(), ++ top + src_rect.Height()); ++ return StretchDIBits(pSource, color, left - src_rect.left, ++ top - src_rect.top, pSource->GetWidth(), ++ pSource->GetHeight(), &clip_rect, ++ FXDIB_ResampleOptions(), BlendMode::kNormal); ++ } ++ DCHECK(pSource); ++ DCHECK(!pSource->IsMaskFormat()); ++ DCHECK_EQ(blend_type, BlendMode::kNormal); ++ if (pSource->IsAlphaFormat()) ++ return false; ++ ++ CFX_DIBExtractor temp(pSource); ++ RetainPtr pBitmap = temp.GetBitmap(); ++ if (!pBitmap) ++ return false; ++ ++ return GDI_SetDIBits(pBitmap, src_rect, left, top); ++} ++ ++bool CGdiPrinterDriver::StretchDIBits(const RetainPtr& pSource, ++ uint32_t color, ++ int dest_left, ++ int dest_top, ++ int dest_width, ++ int dest_height, ++ const FX_RECT* pClipRect, ++ const FXDIB_ResampleOptions& options, ++ BlendMode blend_type) { ++ if (pSource->IsMaskFormat()) { ++ int alpha = FXARGB_A(color); ++ if (pSource->GetBPP() != 1 || alpha != 255) ++ return false; ++ ++ if (dest_width < 0 || dest_height < 0) { ++ RetainPtr pFlipped = ++ pSource->FlipImage(dest_width < 0, dest_height < 0); ++ if (!pFlipped) ++ return false; ++ ++ if (dest_width < 0) ++ dest_left += dest_width; ++ if (dest_height < 0) ++ dest_top += dest_height; ++ ++ return GDI_StretchBitMask(pFlipped, dest_left, dest_top, abs(dest_width), ++ abs(dest_height), color); ++ } ++ ++ CFX_DIBExtractor temp(pSource); ++ RetainPtr pBitmap = temp.GetBitmap(); ++ if (!pBitmap) ++ return false; ++ return GDI_StretchBitMask(pBitmap, dest_left, dest_top, dest_width, ++ dest_height, color); ++ } ++ ++ if (pSource->IsAlphaFormat()) ++ return false; ++ ++ if (dest_width < 0 || dest_height < 0) { ++ RetainPtr pFlipped = ++ pSource->FlipImage(dest_width < 0, dest_height < 0); ++ if (!pFlipped) ++ return false; ++ ++ if (dest_width < 0) ++ dest_left += dest_width; ++ if (dest_height < 0) ++ dest_top += dest_height; ++ ++ return GDI_StretchDIBits(pFlipped, dest_left, dest_top, abs(dest_width), ++ abs(dest_height), options); ++ } ++ ++ CFX_DIBExtractor temp(pSource); ++ RetainPtr pBitmap = temp.GetBitmap(); ++ if (!pBitmap) ++ return false; ++ return GDI_StretchDIBits(pBitmap, dest_left, dest_top, dest_width, ++ dest_height, options); ++} ++ ++bool CGdiPrinterDriver::StartDIBits(const RetainPtr& pSource, ++ int bitmap_alpha, ++ uint32_t color, ++ const CFX_Matrix& matrix, ++ const FXDIB_ResampleOptions& options, ++ std::unique_ptr* handle, ++ BlendMode blend_type) { ++ if (bitmap_alpha < 255 || pSource->IsAlphaFormat() || ++ (pSource->IsMaskFormat() && (pSource->GetBPP() != 1))) { ++ return false; ++ } ++ CFX_FloatRect unit_rect = matrix.GetUnitRect(); ++ FX_RECT full_rect = unit_rect.GetOuterRect(); ++ if (fabs(matrix.b) < 0.5f && matrix.a != 0 && fabs(matrix.c) < 0.5f && ++ matrix.d != 0) { ++ bool bFlipX = matrix.a < 0; ++ bool bFlipY = matrix.d > 0; ++ return StretchDIBits(pSource, color, ++ bFlipX ? full_rect.right : full_rect.left, ++ bFlipY ? full_rect.bottom : full_rect.top, ++ bFlipX ? -full_rect.Width() : full_rect.Width(), ++ bFlipY ? -full_rect.Height() : full_rect.Height(), ++ nullptr, FXDIB_ResampleOptions(), blend_type); ++ } ++ if (fabs(matrix.a) >= 0.5f || fabs(matrix.d) >= 0.5f) ++ return false; ++ ++ RetainPtr pTransformed = ++ pSource->SwapXY(matrix.c > 0, matrix.b < 0); ++ if (!pTransformed) ++ return false; ++ ++ return StretchDIBits(pTransformed, color, full_rect.left, full_rect.top, ++ full_rect.Width(), full_rect.Height(), nullptr, ++ FXDIB_ResampleOptions(), blend_type); ++} ++ ++bool CGdiPrinterDriver::DrawDeviceText( ++ pdfium::span pCharPos, ++ CFX_Font* pFont, ++ const CFX_Matrix& mtObject2Device, ++ float font_size, ++ uint32_t color, ++ const CFX_TextRenderOptions& /*options*/) { ++ return false; ++} +diff --git a/core/fxge/win32/cgdi_printer_driver.h b/core/fxge/win32/cgdi_printer_driver.h +new file mode 100644 +index 000000000..5c4cfc067 +--- /dev/null ++++ b/core/fxge/win32/cgdi_printer_driver.h +@@ -0,0 +1,57 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXGE_WIN32_CGDI_PRINTER_DRIVER_H_ ++#define CORE_FXGE_WIN32_CGDI_PRINTER_DRIVER_H_ ++ ++#include ++ ++#include ++ ++#include "core/fxge/win32/cgdi_device_driver.h" ++ ++class CGdiPrinterDriver final : public CGdiDeviceDriver { ++ public: ++ explicit CGdiPrinterDriver(HDC hDC); ++ ~CGdiPrinterDriver() override; ++ ++ private: ++ // CGdiPrinterDriver: ++ int GetDeviceCaps(int caps_id) const override; ++ bool SetDIBits(const RetainPtr& pBitmap, ++ uint32_t color, ++ const FX_RECT& src_rect, ++ int left, ++ int top, ++ BlendMode blend_type) override; ++ bool StretchDIBits(const RetainPtr& pBitmap, ++ uint32_t color, ++ int dest_left, ++ int dest_top, ++ int dest_width, ++ int dest_height, ++ const FX_RECT* pClipRect, ++ const FXDIB_ResampleOptions& options, ++ BlendMode blend_type) override; ++ bool StartDIBits(const RetainPtr& pBitmap, ++ int bitmap_alpha, ++ uint32_t color, ++ const CFX_Matrix& matrix, ++ const FXDIB_ResampleOptions& options, ++ std::unique_ptr* handle, ++ BlendMode blend_type) override; ++ bool DrawDeviceText(pdfium::span pCharPos, ++ CFX_Font* pFont, ++ const CFX_Matrix& mtObject2Device, ++ float font_size, ++ uint32_t color, ++ const CFX_TextRenderOptions& options) override; ++ ++ const int m_HorzSize; ++ const int m_VertSize; ++}; ++ ++#endif // CORE_FXGE_WIN32_CGDI_PRINTER_DRIVER_H_ +diff --git a/core/fxge/win32/cps_printer_driver.cpp b/core/fxge/win32/cps_printer_driver.cpp +new file mode 100644 +index 000000000..f30b80ddb +--- /dev/null ++++ b/core/fxge/win32/cps_printer_driver.cpp +@@ -0,0 +1,226 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxge/win32/cps_printer_driver.h" ++ ++#include ++ ++#include ++ ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxge/cfx_fillrenderoptions.h" ++#include "core/fxge/cfx_path.h" ++#include "core/fxge/dib/cfx_imagerenderer.h" ++#include "core/fxge/win32/cpsoutput.h" ++#include "third_party/base/check.h" ++#include "third_party/base/notreached.h" ++ ++namespace { ++ ++CFX_PSRenderer::RenderingLevel RenderingLevelFromWindowsPrintMode( ++ WindowsPrintMode mode) { ++ switch (mode) { ++ case WindowsPrintMode::kPostScript2: ++ case WindowsPrintMode::kPostScript2PassThrough: ++ return CFX_PSRenderer::RenderingLevel::kLevel2; ++ case WindowsPrintMode::kPostScript3: ++ case WindowsPrintMode::kPostScript3PassThrough: ++ return CFX_PSRenderer::RenderingLevel::kLevel3; ++ case WindowsPrintMode::kPostScript3Type42: ++ case WindowsPrintMode::kPostScript3Type42PassThrough: ++ return CFX_PSRenderer::RenderingLevel::kLevel3Type42; ++ default: ++ // |mode| should be PostScript. ++ NOTREACHED(); ++ return CFX_PSRenderer::RenderingLevel::kLevel2; ++ } ++} ++ ++} // namespace ++ ++CPSPrinterDriver::CPSPrinterDriver(HDC hDC, ++ WindowsPrintMode mode, ++ CFX_PSFontTracker* ps_font_tracker, ++ const EncoderIface* encoder_iface) ++ : m_hDC(hDC), m_PSRenderer(ps_font_tracker, encoder_iface) { ++ CFX_PSRenderer::RenderingLevel level = ++ RenderingLevelFromWindowsPrintMode(mode); ++ CPSOutput::OutputMode output_mode = ++ (mode == WindowsPrintMode::kPostScript2 || ++ mode == WindowsPrintMode::kPostScript3 || ++ mode == WindowsPrintMode::kPostScript3Type42) ++ ? CPSOutput::OutputMode::kGdiComment ++ : CPSOutput::OutputMode::kExtEscape; ++ ++ m_HorzSize = ::GetDeviceCaps(m_hDC, HORZSIZE); ++ m_VertSize = ::GetDeviceCaps(m_hDC, VERTSIZE); ++ m_Width = ::GetDeviceCaps(m_hDC, HORZRES); ++ m_Height = ::GetDeviceCaps(m_hDC, VERTRES); ++ m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL); ++ ++ m_PSRenderer.Init(pdfium::MakeRetain(m_hDC, output_mode), level, ++ m_Width, m_Height); ++ HRGN hRgn = ::CreateRectRgn(0, 0, 1, 1); ++ if (::GetClipRgn(m_hDC, hRgn) == 1) { ++ DWORD dwCount = ::GetRegionData(hRgn, 0, nullptr); ++ if (dwCount) { ++ DataVector buffer(dwCount); ++ RGNDATA* pData = reinterpret_cast(buffer.data()); ++ if (::GetRegionData(hRgn, dwCount, pData)) { ++ CFX_Path path; ++ for (uint32_t i = 0; i < pData->rdh.nCount; i++) { ++ RECT* pRect = ++ reinterpret_cast(pData->Buffer + pData->rdh.nRgnSize * i); ++ path.AppendRect(static_cast(pRect->left), ++ static_cast(pRect->bottom), ++ static_cast(pRect->right), ++ static_cast(pRect->top)); ++ } ++ m_PSRenderer.SetClip_PathFill(path, nullptr, ++ CFX_FillRenderOptions::WindingOptions()); ++ } ++ } ++ } ++ ::DeleteObject(hRgn); ++} ++ ++CPSPrinterDriver::~CPSPrinterDriver() = default; ++ ++DeviceType CPSPrinterDriver::GetDeviceType() const { ++ return DeviceType::kPrinter; ++} ++ ++int CPSPrinterDriver::GetDeviceCaps(int caps_id) const { ++ switch (caps_id) { ++ case FXDC_PIXEL_WIDTH: ++ return m_Width; ++ case FXDC_PIXEL_HEIGHT: ++ return m_Height; ++ case FXDC_BITS_PIXEL: ++ return m_nBitsPerPixel; ++ case FXDC_RENDER_CAPS: ++ return FXRC_BIT_MASK; ++ case FXDC_HORZ_SIZE: ++ return m_HorzSize; ++ case FXDC_VERT_SIZE: ++ return m_VertSize; ++ default: ++ NOTREACHED(); ++ return 0; ++ } ++} ++ ++void CPSPrinterDriver::SaveState() { ++ m_PSRenderer.SaveState(); ++} ++ ++void CPSPrinterDriver::RestoreState(bool bKeepSaved) { ++ m_PSRenderer.RestoreState(bKeepSaved); ++} ++ ++bool CPSPrinterDriver::SetClip_PathFill( ++ const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_FillRenderOptions& fill_options) { ++ m_PSRenderer.SetClip_PathFill(path, pObject2Device, fill_options); ++ return true; ++} ++ ++bool CPSPrinterDriver::SetClip_PathStroke( ++ const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_GraphStateData* pGraphState) { ++ m_PSRenderer.SetClip_PathStroke(path, pObject2Device, pGraphState); ++ return true; ++} ++ ++bool CPSPrinterDriver::DrawPath(const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_GraphStateData* pGraphState, ++ FX_ARGB fill_color, ++ FX_ARGB stroke_color, ++ const CFX_FillRenderOptions& fill_options, ++ BlendMode blend_type) { ++ if (blend_type != BlendMode::kNormal) ++ return false; ++ return m_PSRenderer.DrawPath(path, pObject2Device, pGraphState, fill_color, ++ stroke_color, fill_options); ++} ++ ++bool CPSPrinterDriver::GetClipBox(FX_RECT* pRect) { ++ *pRect = m_PSRenderer.GetClipBox(); ++ return true; ++} ++ ++bool CPSPrinterDriver::SetDIBits(const RetainPtr& pBitmap, ++ uint32_t color, ++ const FX_RECT& src_rect, ++ int left, ++ int top, ++ BlendMode blend_type) { ++ if (blend_type != BlendMode::kNormal) ++ return false; ++ return m_PSRenderer.SetDIBits(pBitmap, color, left, top); ++} ++ ++bool CPSPrinterDriver::StretchDIBits(const RetainPtr& pBitmap, ++ uint32_t color, ++ int dest_left, ++ int dest_top, ++ int dest_width, ++ int dest_height, ++ const FX_RECT* pClipRect, ++ const FXDIB_ResampleOptions& options, ++ BlendMode blend_type) { ++ if (blend_type != BlendMode::kNormal) ++ return false; ++ return m_PSRenderer.StretchDIBits(pBitmap, color, dest_left, dest_top, ++ dest_width, dest_height, options); ++} ++ ++bool CPSPrinterDriver::StartDIBits(const RetainPtr& pBitmap, ++ int bitmap_alpha, ++ uint32_t color, ++ const CFX_Matrix& matrix, ++ const FXDIB_ResampleOptions& options, ++ std::unique_ptr* handle, ++ BlendMode blend_type) { ++ if (blend_type != BlendMode::kNormal) ++ return false; ++ ++ if (bitmap_alpha < 255) ++ return false; ++ ++ *handle = nullptr; ++ return m_PSRenderer.DrawDIBits(pBitmap, color, matrix, options); ++} ++ ++bool CPSPrinterDriver::DrawDeviceText( ++ pdfium::span pCharPos, ++ CFX_Font* pFont, ++ const CFX_Matrix& mtObject2Device, ++ float font_size, ++ uint32_t color, ++ const CFX_TextRenderOptions& /*options*/) { ++ return m_PSRenderer.DrawText(pCharPos.size(), pCharPos.data(), pFont, ++ mtObject2Device, font_size, color); ++} ++ ++bool CPSPrinterDriver::MultiplyAlpha(float alpha) { ++ // PostScript doesn't support transparency. All callers are using ++ // `CFX_DIBitmap`-backed raster devices anyway. ++ NOTREACHED(); ++ return false; ++} ++ ++bool CPSPrinterDriver::MultiplyAlpha(const RetainPtr& mask) { ++ // PostScript doesn't support transparency. All callers are using ++ // `CFX_DIBitmap`-backed raster devices anyway. ++ NOTREACHED(); ++ return false; ++} +diff --git a/core/fxge/win32/cps_printer_driver.h b/core/fxge/win32/cps_printer_driver.h +new file mode 100644 +index 000000000..4e2849760 +--- /dev/null ++++ b/core/fxge/win32/cps_printer_driver.h +@@ -0,0 +1,88 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXGE_WIN32_CPS_PRINTER_DRIVER_H_ ++#define CORE_FXGE_WIN32_CPS_PRINTER_DRIVER_H_ ++ ++#include ++ ++#include ++ ++#include "core/fxge/cfx_windowsrenderdevice.h" ++#include "core/fxge/renderdevicedriver_iface.h" ++#include "core/fxge/win32/cfx_psrenderer.h" ++ ++class CFX_PSFontTracker; ++ ++class CPSPrinterDriver final : public RenderDeviceDriverIface { ++ public: ++ CPSPrinterDriver(HDC hDC, ++ WindowsPrintMode mode, ++ CFX_PSFontTracker* ps_font_tracker, ++ const EncoderIface* encoder_iface); ++ ~CPSPrinterDriver() override; ++ ++ private: ++ // RenderDeviceDriverIface: ++ DeviceType GetDeviceType() const override; ++ int GetDeviceCaps(int caps_id) const override; ++ void SaveState() override; ++ void RestoreState(bool bKeepSaved) override; ++ bool SetClip_PathFill(const CFX_Path& paath, ++ const CFX_Matrix* pObject2Device, ++ const CFX_FillRenderOptions& fill_options) override; ++ bool SetClip_PathStroke(const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_GraphStateData* pGraphState) override; ++ bool DrawPath(const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_GraphStateData* pGraphState, ++ uint32_t fill_color, ++ uint32_t stroke_color, ++ const CFX_FillRenderOptions& fill_options, ++ BlendMode blend_type) override; ++ bool GetClipBox(FX_RECT* pRect) override; ++ bool SetDIBits(const RetainPtr& pBitmap, ++ uint32_t color, ++ const FX_RECT& src_rect, ++ int left, ++ int top, ++ BlendMode blend_type) override; ++ bool StretchDIBits(const RetainPtr& pBitmap, ++ uint32_t color, ++ int dest_left, ++ int dest_top, ++ int dest_width, ++ int dest_height, ++ const FX_RECT* pClipRect, ++ const FXDIB_ResampleOptions& options, ++ BlendMode blend_type) override; ++ bool StartDIBits(const RetainPtr& pBitmap, ++ int bitmap_alpha, ++ uint32_t color, ++ const CFX_Matrix& matrix, ++ const FXDIB_ResampleOptions& options, ++ std::unique_ptr* handle, ++ BlendMode blend_type) override; ++ bool DrawDeviceText(pdfium::span pCharPos, ++ CFX_Font* pFont, ++ const CFX_Matrix& mtObject2Device, ++ float font_size, ++ uint32_t color, ++ const CFX_TextRenderOptions& options) override; ++ bool MultiplyAlpha(float alpha) override; ++ bool MultiplyAlpha(const RetainPtr& mask) override; ++ ++ HDC m_hDC; ++ int m_Width; ++ int m_Height; ++ int m_nBitsPerPixel; ++ int m_HorzSize; ++ int m_VertSize; ++ CFX_PSRenderer m_PSRenderer; ++}; ++ ++#endif // CORE_FXGE_WIN32_CPS_PRINTER_DRIVER_H_ +diff --git a/core/fxge/win32/cpsoutput.cpp b/core/fxge/win32/cpsoutput.cpp +index 4170db1a1..763a4e562 100644 +--- a/core/fxge/win32/cpsoutput.cpp ++++ b/core/fxge/win32/cpsoutput.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -15,28 +15,22 @@ CPSOutput::CPSOutput(HDC hDC, OutputMode mode) : m_hDC(hDC), m_mode(mode) {} + + CPSOutput::~CPSOutput() = default; + +-bool CPSOutput::WriteBlock(const void* str, size_t len) { +- pdfium::span input(static_cast(str), len); ++bool CPSOutput::WriteBlock(pdfium::span input) { + while (!input.empty()) { + uint8_t buffer[1026]; + size_t send_len = std::min(input.size(), 1024); + *(reinterpret_cast(buffer)) = static_cast(send_len); + memcpy(buffer + 2, input.data(), send_len); +- + switch (m_mode) { + case OutputMode::kExtEscape: +- ExtEscape(m_hDC, PASSTHROUGH, send_len + 2, ++ ExtEscape(m_hDC, PASSTHROUGH, static_cast(send_len + 2), + reinterpret_cast(buffer), 0, nullptr); + break; + case OutputMode::kGdiComment: +- GdiComment(m_hDC, send_len + 2, buffer); ++ GdiComment(m_hDC, static_cast(send_len + 2), buffer); + break; + } + input = input.subspan(send_len); + } + return true; + } +- +-bool CPSOutput::WriteString(ByteStringView str) { +- return WriteBlock(str.unterminated_c_str(), str.GetLength()); +-} +diff --git a/core/fxge/win32/cpsoutput.h b/core/fxge/win32/cpsoutput.h +index 939f4cce9..efb93ceb7 100644 +--- a/core/fxge/win32/cpsoutput.h ++++ b/core/fxge/win32/cpsoutput.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,10 @@ + #ifndef CORE_FXGE_WIN32_CPSOUTPUT_H_ + #define CORE_FXGE_WIN32_CPSOUTPUT_H_ + ++#include + #include + + #include "core/fxcrt/fx_stream.h" +-#include "core/fxcrt/fx_system.h" + + class CPSOutput final : public IFX_RetainableWriteStream { + public: +@@ -19,9 +19,8 @@ class CPSOutput final : public IFX_RetainableWriteStream { + CPSOutput(HDC hDC, OutputMode mode); + ~CPSOutput() override; + +- // IFX_Writestream +- bool WriteBlock(const void* str, size_t len) override; +- bool WriteString(ByteStringView str) override; ++ // IFX_Writestream: ++ bool WriteBlock(pdfium::span input) override; + + private: + const HDC m_hDC; +diff --git a/core/fxge/win32/ctext_only_printer_driver.cpp b/core/fxge/win32/ctext_only_printer_driver.cpp +new file mode 100644 +index 000000000..7689f3f3c +--- /dev/null ++++ b/core/fxge/win32/ctext_only_printer_driver.cpp +@@ -0,0 +1,194 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "core/fxge/win32/ctext_only_printer_driver.h" ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/fx_system.h" ++#include "core/fxge/cfx_font.h" ++#include "core/fxge/text_char_pos.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/notreached.h" ++ ++CTextOnlyPrinterDriver::CTextOnlyPrinterDriver(HDC hDC) ++ : m_hDC(hDC), ++ m_Width(INT_MAX), ++ m_Height(INT_MAX), ++ m_HorzSize(INT_MAX), ++ m_VertSize(INT_MAX), ++ m_OriginY(0.0f), ++ m_SetOrigin(false) { ++ m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL); ++} ++ ++CTextOnlyPrinterDriver::~CTextOnlyPrinterDriver() = default; ++ ++DeviceType CTextOnlyPrinterDriver::GetDeviceType() const { ++ return DeviceType::kPrinter; ++} ++ ++int CTextOnlyPrinterDriver::GetDeviceCaps(int caps_id) const { ++ switch (caps_id) { ++ case FXDC_PIXEL_WIDTH: ++ return m_Width; ++ case FXDC_PIXEL_HEIGHT: ++ return m_Height; ++ case FXDC_BITS_PIXEL: ++ return m_nBitsPerPixel; ++ case FXDC_RENDER_CAPS: ++ return 0; ++ case FXDC_HORZ_SIZE: ++ return m_HorzSize; ++ case FXDC_VERT_SIZE: ++ return m_VertSize; ++ default: ++ NOTREACHED(); ++ return 0; ++ } ++} ++ ++void CTextOnlyPrinterDriver::SaveState() {} ++ ++void CTextOnlyPrinterDriver::RestoreState(bool bKeepSaved) {} ++ ++bool CTextOnlyPrinterDriver::SetClip_PathFill( ++ const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_FillRenderOptions& fill_options) { ++ return true; ++} ++ ++bool CTextOnlyPrinterDriver::SetClip_PathStroke( ++ const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_GraphStateData* pGraphState) { ++ return false; ++} ++ ++bool CTextOnlyPrinterDriver::DrawPath(const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_GraphStateData* pGraphState, ++ uint32_t fill_color, ++ uint32_t stroke_color, ++ const CFX_FillRenderOptions& fill_options, ++ BlendMode blend_type) { ++ return false; ++} ++ ++bool CTextOnlyPrinterDriver::SetDIBits(const RetainPtr& pBitmap, ++ uint32_t color, ++ const FX_RECT& src_rect, ++ int left, ++ int top, ++ BlendMode blend_type) { ++ return false; ++} ++ ++bool CTextOnlyPrinterDriver::GetClipBox(FX_RECT* pRect) { ++ pRect->left = 0; ++ pRect->right = m_Width; ++ pRect->top = 0; ++ pRect->bottom = m_Height; ++ return true; ++} ++ ++bool CTextOnlyPrinterDriver::StretchDIBits( ++ const RetainPtr& pBitmap, ++ uint32_t color, ++ int dest_left, ++ int dest_top, ++ int dest_width, ++ int dest_height, ++ const FX_RECT* pClipRect, ++ const FXDIB_ResampleOptions& options, ++ BlendMode blend_type) { ++ return false; ++} ++ ++bool CTextOnlyPrinterDriver::StartDIBits( ++ const RetainPtr& pBitmap, ++ int bitmap_alpha, ++ uint32_t color, ++ const CFX_Matrix& matrix, ++ const FXDIB_ResampleOptions& options, ++ std::unique_ptr* handle, ++ BlendMode blend_type) { ++ return false; ++} ++ ++bool CTextOnlyPrinterDriver::DrawDeviceText( ++ pdfium::span pCharPos, ++ CFX_Font* pFont, ++ const CFX_Matrix& mtObject2Device, ++ float font_size, ++ uint32_t color, ++ const CFX_TextRenderOptions& /*options*/) { ++ if (g_pdfium_print_mode != WindowsPrintMode::kTextOnly) ++ return false; ++ if (pCharPos.empty() || !pFont || !pFont->IsEmbedded() || !pFont->IsTTFont()) ++ return false; ++ ++ // Scale factor used to minimize the kerning problems caused by rounding ++ // errors below. Value chosen based on the title of https://crbug.com/18383 ++ const double kScaleFactor = 10; ++ ++ // Detect new lines and add clrf characters (since this is Windows only). ++ // These characters are removed by SkPDF, but the new line information is ++ // preserved in the text location. clrf characters seem to be ignored by ++ // label printers that use this driver. ++ WideString wsText; ++ size_t len = pCharPos.size(); ++ float fOffsetY = mtObject2Device.f * kScaleFactor; ++ if (m_SetOrigin && FXSYS_roundf(m_OriginY) != FXSYS_roundf(fOffsetY)) { ++ wsText += L"\r\n"; ++ len += 2; ++ } ++ wsText.Reserve(len); ++ m_OriginY = fOffsetY; ++ m_SetOrigin = true; ++ ++ // Text ++ for (const auto& charpos : pCharPos) { ++ // Only works with PDFs from Skia's PDF generator. Cannot handle arbitrary ++ // values from PDFs. ++ DCHECK_EQ(charpos.m_AdjustMatrix[0], 0); ++ DCHECK_EQ(charpos.m_AdjustMatrix[1], 0); ++ DCHECK_EQ(charpos.m_AdjustMatrix[2], 0); ++ DCHECK_EQ(charpos.m_AdjustMatrix[3], 0); ++ DCHECK_EQ(charpos.m_Origin.y, 0); ++ wsText += charpos.m_Unicode; ++ } ++ ByteString text = wsText.ToDefANSI(); ++ auto text_span = text.span(); ++ while (!text_span.empty()) { ++ uint8_t buffer[1026]; ++ size_t send_len = std::min(text_span.size(), 1024); ++ *(reinterpret_cast(buffer)) = static_cast(send_len); ++ memcpy(buffer + 2, text_span.data(), send_len); ++ ::GdiComment(m_hDC, static_cast(send_len + 2), buffer); ++ text_span = text_span.subspan(send_len); ++ } ++ return true; ++} ++ ++bool CTextOnlyPrinterDriver::MultiplyAlpha(float alpha) { ++ // Not needed. All callers are using `CFX_DIBitmap`-backed raster devices ++ // anyway. ++ NOTREACHED(); ++ return false; ++} ++ ++bool CTextOnlyPrinterDriver::MultiplyAlpha(const RetainPtr& mask) { ++ // Not needed. All callers are using `CFX_DIBitmap`-backed raster devices ++ // anyway. ++ NOTREACHED(); ++ return false; ++} +diff --git a/core/fxge/win32/ctext_only_printer_driver.h b/core/fxge/win32/ctext_only_printer_driver.h +new file mode 100644 +index 000000000..e029a7cc4 +--- /dev/null ++++ b/core/fxge/win32/ctext_only_printer_driver.h +@@ -0,0 +1,80 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef CORE_FXGE_WIN32_CTEXT_ONLY_PRINTER_DRIVER_H_ ++#define CORE_FXGE_WIN32_CTEXT_ONLY_PRINTER_DRIVER_H_ ++ ++#include ++ ++#include ++ ++#include "core/fxge/cfx_windowsrenderdevice.h" ++ ++class CTextOnlyPrinterDriver final : public RenderDeviceDriverIface { ++ public: ++ explicit CTextOnlyPrinterDriver(HDC hDC); ++ ~CTextOnlyPrinterDriver() override; ++ ++ private: ++ // RenderDeviceDriverIface: ++ DeviceType GetDeviceType() const override; ++ int GetDeviceCaps(int caps_id) const override; ++ void SaveState() override; ++ void RestoreState(bool bKeepSaved) override; ++ bool SetClip_PathFill(const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_FillRenderOptions& fill_options) override; ++ bool SetClip_PathStroke(const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_GraphStateData* pGraphState) override; ++ bool DrawPath(const CFX_Path& path, ++ const CFX_Matrix* pObject2Device, ++ const CFX_GraphStateData* pGraphState, ++ uint32_t fill_color, ++ uint32_t stroke_color, ++ const CFX_FillRenderOptions& fill_options, ++ BlendMode blend_type) override; ++ bool GetClipBox(FX_RECT* pRect) override; ++ bool SetDIBits(const RetainPtr& pBitmap, ++ uint32_t color, ++ const FX_RECT& src_rect, ++ int left, ++ int top, ++ BlendMode blend_type) override; ++ bool StretchDIBits(const RetainPtr& pBitmap, ++ uint32_t color, ++ int dest_left, ++ int dest_top, ++ int dest_width, ++ int dest_height, ++ const FX_RECT* pClipRect, ++ const FXDIB_ResampleOptions& options, ++ BlendMode blend_type) override; ++ bool StartDIBits(const RetainPtr& pBitmap, ++ int bitmap_alpha, ++ uint32_t color, ++ const CFX_Matrix& matrix, ++ const FXDIB_ResampleOptions& options, ++ std::unique_ptr* handle, ++ BlendMode blend_type) override; ++ bool DrawDeviceText(pdfium::span pCharPos, ++ CFX_Font* pFont, ++ const CFX_Matrix& mtObject2Device, ++ float font_size, ++ uint32_t color, ++ const CFX_TextRenderOptions& options) override; ++ bool MultiplyAlpha(float alpha) override; ++ bool MultiplyAlpha(const RetainPtr& mask) override; ++ ++ HDC m_hDC; ++ const int m_Width; ++ const int m_Height; ++ int m_nBitsPerPixel; ++ const int m_HorzSize; ++ const int m_VertSize; ++ float m_OriginY; ++ bool m_SetOrigin; ++}; ++ ++#endif // CORE_FXGE_WIN32_CTEXT_ONLY_PRINTER_DRIVER_H_ +diff --git a/core/fxge/win32/cwin32_platform.cpp b/core/fxge/win32/cwin32_platform.cpp +new file mode 100644 +index 000000000..5794e2ed8 +--- /dev/null ++++ b/core/fxge/win32/cwin32_platform.cpp +@@ -0,0 +1,490 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "core/fxge/win32/cwin32_platform.h" ++ ++#include ++#include ++#include ++ ++#include "core/fxcrt/fx_codepage.h" ++#include "core/fxge/cfx_folderfontinfo.h" ++#include "core/fxge/cfx_gemodule.h" ++#include "third_party/base/check.h" ++#include "third_party/base/numerics/safe_conversions.h" ++#include "third_party/base/span.h" ++#include "third_party/base/win/scoped_select_object.h" ++#include "third_party/base/win/win_util.h" ++ ++namespace { ++ ++using ScopedSelectObject = pdfium::base::win::ScopedSelectObject; ++ ++struct Variant { ++ const char* m_pFaceName; ++ const char* m_pVariantName; // Note: UTF-16LE terminator required. ++}; ++ ++constexpr Variant kVariantNames[] = { ++ {"DFKai-SB", "\x19\x6A\x77\x69\xD4\x9A\x00\x00"}, ++}; ++ ++struct Substs { ++ const char* m_pName; ++ const char* m_pWinName; ++ bool m_bBold; ++ bool m_bItalic; ++}; ++ ++constexpr Substs kBase14Substs[] = { ++ {"Courier", "Courier New", false, false}, ++ {"Courier-Bold", "Courier New", true, false}, ++ {"Courier-BoldOblique", "Courier New", true, true}, ++ {"Courier-Oblique", "Courier New", false, true}, ++ {"Helvetica", "Arial", false, false}, ++ {"Helvetica-Bold", "Arial", true, false}, ++ {"Helvetica-BoldOblique", "Arial", true, true}, ++ {"Helvetica-Oblique", "Arial", false, true}, ++ {"Times-Roman", "Times New Roman", false, false}, ++ {"Times-Bold", "Times New Roman", true, false}, ++ {"Times-BoldItalic", "Times New Roman", true, true}, ++ {"Times-Italic", "Times New Roman", false, true}, ++}; ++ ++struct FontNameMap { ++ const char* m_pSubFontName; ++ const char* m_pSrcFontName; ++}; ++ ++constexpr FontNameMap kJpFontNameMap[] = { ++ {"MS Mincho", "Heiseimin-W3"}, ++ {"MS Gothic", "Jun101-Light"}, ++}; ++ ++bool GetSubFontName(ByteString* name) { ++ for (size_t i = 0; i < std::size(kJpFontNameMap); ++i) { ++ if (!FXSYS_stricmp(name->c_str(), kJpFontNameMap[i].m_pSrcFontName)) { ++ *name = kJpFontNameMap[i].m_pSubFontName; ++ return true; ++ } ++ } ++ return false; ++} ++ ++// Wraps CreateFontA() so callers don't have to specify all the arguments. ++HFONT Win32CreateFont(int weight, ++ bool italic, ++ FX_Charset charset, ++ int pitch_family, ++ const char* face) { ++ return ::CreateFontA(-10, 0, 0, 0, weight, italic, 0, 0, ++ static_cast(charset), OUT_TT_ONLY_PRECIS, 0, 0, ++ pitch_family, face); ++} ++ ++class CFX_Win32FallbackFontInfo final : public CFX_FolderFontInfo { ++ public: ++ CFX_Win32FallbackFontInfo() = default; ++ ~CFX_Win32FallbackFontInfo() override = default; ++ ++ // CFX_FolderFontInfo: ++ void* MapFont(int weight, ++ bool bItalic, ++ FX_Charset charset, ++ int pitch_family, ++ const ByteString& face) override; ++}; ++ ++class CFX_Win32FontInfo final : public SystemFontInfoIface { ++ public: ++ CFX_Win32FontInfo(); ++ ~CFX_Win32FontInfo() override; ++ ++ // SystemFontInfoIface: ++ bool EnumFontList(CFX_FontMapper* pMapper) override; ++ void* MapFont(int weight, ++ bool bItalic, ++ FX_Charset charset, ++ int pitch_family, ++ const ByteString& face) override; ++ void* GetFont(const ByteString& face) override { return nullptr; } ++ size_t GetFontData(void* hFont, ++ uint32_t table, ++ pdfium::span buffer) override; ++ bool GetFaceName(void* hFont, ByteString* name) override; ++ bool GetFontCharset(void* hFont, FX_Charset* charset) override; ++ void DeleteFont(void* hFont) override; ++ ++ void AddInstalledFont(const LOGFONTA* plf, uint32_t font_type); ++ ++ private: ++ bool IsSupportedFont(const LOGFONTA* plf); ++ void GetGBPreference(ByteString& face, int weight, int pitch_family); ++ void GetJapanesePreference(ByteString& face, int weight, int pitch_family); ++ ByteString FindFont(const ByteString& name); ++ void* GetFontFromList(int weight, ++ bool italic, ++ FX_Charset charset, ++ int pitch_family, ++ pdfium::span font_faces); ++ ++ const HDC m_hDC; ++ UnownedPtr m_pMapper; ++ ByteString m_LastFamily; ++ ByteString m_KaiTi; ++ ByteString m_FangSong; ++}; ++ ++int CALLBACK FontEnumProc(const LOGFONTA* plf, ++ const TEXTMETRICA* lpntme, ++ uint32_t font_type, ++ LPARAM lParam) { ++ CFX_Win32FontInfo* pFontInfo = reinterpret_cast(lParam); ++ pFontInfo->AddInstalledFont(plf, font_type); ++ return 1; ++} ++ ++CFX_Win32FontInfo::CFX_Win32FontInfo() : m_hDC(CreateCompatibleDC(nullptr)) {} ++ ++CFX_Win32FontInfo::~CFX_Win32FontInfo() { ++ DeleteDC(m_hDC); ++} ++ ++bool CFX_Win32FontInfo::IsSupportedFont(const LOGFONTA* plf) { ++ HFONT hFont = CreateFontIndirectA(plf); ++ bool ret = false; ++ size_t font_size = GetFontData(hFont, 0, {}); ++ if (font_size != GDI_ERROR && font_size >= sizeof(uint32_t)) { ++ uint32_t header; ++ auto span = pdfium::as_writable_bytes(pdfium::make_span(&header, 1)); ++ GetFontData(hFont, 0, span); ++ header = FXSYS_UINT32_GET_MSBFIRST(span); ++ ret = header == FXBSTR_ID('O', 'T', 'T', 'O') || ++ header == FXBSTR_ID('t', 't', 'c', 'f') || ++ header == FXBSTR_ID('t', 'r', 'u', 'e') || header == 0x00010000 || ++ header == 0x00020000 || ++ (header & 0xFFFF0000) == FXBSTR_ID(0x80, 0x01, 0x00, 0x00) || ++ (header & 0xFFFF0000) == FXBSTR_ID('%', '!', 0, 0); ++ } ++ DeleteFont(hFont); ++ return ret; ++} ++ ++void CFX_Win32FontInfo::AddInstalledFont(const LOGFONTA* plf, ++ uint32_t font_type) { ++ ByteString name(plf->lfFaceName); ++ if (name.GetLength() > 0 && name[0] == '@') ++ return; ++ ++ if (name == m_LastFamily) { ++ m_pMapper->AddInstalledFont(name, FX_GetCharsetFromInt(plf->lfCharSet)); ++ return; ++ } ++ if (!(font_type & TRUETYPE_FONTTYPE)) { ++ if (!(font_type & DEVICE_FONTTYPE) || !IsSupportedFont(plf)) ++ return; ++ } ++ ++ m_pMapper->AddInstalledFont(name, FX_GetCharsetFromInt(plf->lfCharSet)); ++ m_LastFamily = name; ++} ++ ++bool CFX_Win32FontInfo::EnumFontList(CFX_FontMapper* pMapper) { ++ m_pMapper = pMapper; ++ LOGFONTA lf; ++ memset(&lf, 0, sizeof(LOGFONTA)); ++ lf.lfCharSet = static_cast(FX_Charset::kDefault); ++ lf.lfFaceName[0] = 0; ++ lf.lfPitchAndFamily = 0; ++ EnumFontFamiliesExA(m_hDC, &lf, reinterpret_cast(FontEnumProc), ++ reinterpret_cast(this), 0); ++ return true; ++} ++ ++ByteString CFX_Win32FontInfo::FindFont(const ByteString& name) { ++ if (!m_pMapper) ++ return name; ++ ++ absl::optional maybe_installed = ++ m_pMapper->InstalledFontNameStartingWith(name); ++ if (maybe_installed.has_value()) ++ return maybe_installed.value(); ++ ++ absl::optional maybe_localized = ++ m_pMapper->LocalizedFontNameStartingWith(name); ++ if (maybe_localized.has_value()) ++ return maybe_localized.value(); ++ ++ return ByteString(); ++} ++ ++void* CFX_Win32FontInfo::GetFontFromList( ++ int weight, ++ bool italic, ++ FX_Charset charset, ++ int pitch_family, ++ pdfium::span font_faces) { ++ DCHECK(!font_faces.empty()); ++ ++ // Initialization not needed because of DCHECK() above and the assignment in ++ // the for-loop below. ++ HFONT font; ++ for (const char* face : font_faces) { ++ font = Win32CreateFont(weight, italic, charset, pitch_family, face); ++ ByteString actual_face; ++ if (GetFaceName(font, &actual_face) && actual_face.EqualNoCase(face)) ++ break; ++ } ++ return font; ++} ++ ++void* CFX_Win32FallbackFontInfo::MapFont(int weight, ++ bool bItalic, ++ FX_Charset charset, ++ int pitch_family, ++ const ByteString& face) { ++ void* font = GetSubstFont(face); ++ if (font) ++ return font; ++ ++ bool bCJK = true; ++ switch (charset) { ++ case FX_Charset::kShiftJIS: ++ case FX_Charset::kChineseSimplified: ++ case FX_Charset::kChineseTraditional: ++ case FX_Charset::kHangul: ++ break; ++ default: ++ bCJK = false; ++ break; ++ } ++ return FindFont(weight, bItalic, charset, pitch_family, face, !bCJK); ++} ++ ++void CFX_Win32FontInfo::GetGBPreference(ByteString& face, ++ int weight, ++ int pitch_family) { ++ if (face.Contains("KaiTi") || face.Contains("\xbf\xac")) { ++ if (m_KaiTi.IsEmpty()) { ++ m_KaiTi = FindFont("KaiTi"); ++ if (m_KaiTi.IsEmpty()) { ++ m_KaiTi = "SimSun"; ++ } ++ } ++ face = m_KaiTi; ++ } else if (face.Contains("FangSong") || face.Contains("\xb7\xc2\xcb\xce")) { ++ if (m_FangSong.IsEmpty()) { ++ m_FangSong = FindFont("FangSong"); ++ if (m_FangSong.IsEmpty()) { ++ m_FangSong = "SimSun"; ++ } ++ } ++ face = m_FangSong; ++ } else if (face.Contains("SimSun") || face.Contains("\xcb\xce")) { ++ face = "SimSun"; ++ } else if (face.Contains("SimHei") || face.Contains("\xba\xda")) { ++ face = "SimHei"; ++ } else if (!(pitch_family & FF_ROMAN) && weight > 550) { ++ face = "SimHei"; ++ } else { ++ face = "SimSun"; ++ } ++} ++ ++void CFX_Win32FontInfo::GetJapanesePreference(ByteString& face, ++ int weight, ++ int pitch_family) { ++ if (face.Contains("Gothic") || ++ face.Contains("\x83\x53\x83\x56\x83\x62\x83\x4e")) { ++ if (face.Contains("PGothic") || ++ face.Contains("\x82\x6f\x83\x53\x83\x56\x83\x62\x83\x4e")) { ++ face = "MS PGothic"; ++ } else if (face.Contains("UI Gothic")) { ++ face = "MS UI Gothic"; ++ } else { ++ if (face.Contains("HGSGothicM") || face.Contains("HGMaruGothicMPRO")) { ++ face = "MS PGothic"; ++ } else { ++ face = "MS Gothic"; ++ } ++ } ++ return; ++ } ++ if (face.Contains("Mincho") || face.Contains("\x96\xbe\x92\xa9")) { ++ if (face.Contains("PMincho") || face.Contains("\x82\x6f\x96\xbe\x92\xa9")) { ++ face = "MS PMincho"; ++ } else { ++ face = "MS Mincho"; ++ } ++ return; ++ } ++ if (GetSubFontName(&face)) ++ return; ++ ++ if (!(pitch_family & FF_ROMAN) && weight > 400) { ++ face = "MS PGothic"; ++ } else { ++ face = "MS PMincho"; ++ } ++} ++ ++void* CFX_Win32FontInfo::MapFont(int weight, ++ bool bItalic, ++ FX_Charset charset, ++ int pitch_family, ++ const ByteString& face) { ++ ByteString new_face = face; ++ for (int iBaseFont = 0; iBaseFont < 12; iBaseFont++) { ++ if (new_face == ByteStringView(kBase14Substs[iBaseFont].m_pName)) { ++ new_face = kBase14Substs[iBaseFont].m_pWinName; ++ weight = kBase14Substs[iBaseFont].m_bBold ? FW_BOLD : FW_NORMAL; ++ bItalic = kBase14Substs[iBaseFont].m_bItalic; ++ break; ++ } ++ } ++ if (charset == FX_Charset::kANSI || charset == FX_Charset::kSymbol) ++ charset = FX_Charset::kDefault; ++ ++ int subst_pitch_family; ++ switch (charset) { ++ case FX_Charset::kShiftJIS: ++ subst_pitch_family = FF_ROMAN; ++ break; ++ case FX_Charset::kChineseTraditional: ++ case FX_Charset::kHangul: ++ case FX_Charset::kChineseSimplified: ++ subst_pitch_family = 0; ++ break; ++ default: ++ subst_pitch_family = pitch_family; ++ break; ++ } ++ HFONT hFont = Win32CreateFont(weight, bItalic, charset, subst_pitch_family, ++ new_face.c_str()); ++ ByteString actual_new_face; ++ if (GetFaceName(hFont, &actual_new_face) && ++ new_face.EqualNoCase(actual_new_face.AsStringView())) { ++ return hFont; ++ } ++ ++ WideString wsFace = WideString::FromDefANSI(actual_new_face.AsStringView()); ++ for (const Variant& variant : kVariantNames) { ++ if (new_face != variant.m_pFaceName) ++ continue; ++ ++ const auto* pName = ++ reinterpret_cast(variant.m_pVariantName); ++ size_t len = WideString::WStringLength(pName); ++ WideString wsName = WideString::FromUTF16LE(pName, len); ++ if (wsFace == wsName) ++ return hFont; ++ } ++ ::DeleteObject(hFont); ++ if (charset == FX_Charset::kDefault) ++ return nullptr; ++ ++ switch (charset) { ++ case FX_Charset::kShiftJIS: ++ GetJapanesePreference(new_face, weight, pitch_family); ++ break; ++ case FX_Charset::kChineseSimplified: ++ GetGBPreference(new_face, weight, pitch_family); ++ break; ++ case FX_Charset::kHangul: ++ new_face = "Gulim"; ++ break; ++ case FX_Charset::kChineseTraditional: { ++ static const char* const kMonospaceFonts[] = {"Microsoft YaHei", ++ "MingLiU"}; ++ static const char* const kProportionalFonts[] = {"Microsoft JHengHei", ++ "PMingLiU"}; ++ pdfium::span candidate_fonts = ++ new_face.Contains("MSung") ? kMonospaceFonts : kProportionalFonts; ++ return GetFontFromList(weight, bItalic, charset, subst_pitch_family, ++ candidate_fonts); ++ } ++ default: ++ break; ++ } ++ return Win32CreateFont(weight, bItalic, charset, subst_pitch_family, ++ new_face.c_str()); ++} ++ ++void CFX_Win32FontInfo::DeleteFont(void* hFont) { ++ ::DeleteObject(hFont); ++} ++ ++size_t CFX_Win32FontInfo::GetFontData(void* hFont, ++ uint32_t table, ++ pdfium::span buffer) { ++ ScopedSelectObject select_object(m_hDC, static_cast(hFont)); ++ table = FXSYS_UINT32_GET_MSBFIRST(reinterpret_cast(&table)); ++ size_t size = ::GetFontData(m_hDC, table, 0, buffer.data(), ++ pdfium::base::checked_cast(buffer.size())); ++ return size != GDI_ERROR ? size : 0; ++} ++ ++bool CFX_Win32FontInfo::GetFaceName(void* hFont, ByteString* name) { ++ ScopedSelectObject select_object(m_hDC, static_cast(hFont)); ++ char facebuf[100]; ++ if (::GetTextFaceA(m_hDC, std::size(facebuf), facebuf) == 0) ++ return false; ++ ++ *name = facebuf; ++ return true; ++} ++ ++bool CFX_Win32FontInfo::GetFontCharset(void* hFont, FX_Charset* charset) { ++ ScopedSelectObject select_object(m_hDC, static_cast(hFont)); ++ TEXTMETRIC tm; ++ ::GetTextMetrics(m_hDC, &tm); ++ *charset = FX_GetCharsetFromInt(tm.tmCharSet); ++ return true; ++} ++ ++} // namespace ++ ++CWin32Platform::CWin32Platform() = default; ++ ++CWin32Platform::~CWin32Platform() = default; ++ ++void CWin32Platform::Init() { ++ if (pdfium::base::win::IsUser32AndGdi32Available()) ++ m_GdiplusExt.Load(); ++} ++ ++std::unique_ptr ++CWin32Platform::CreateDefaultSystemFontInfo() { ++ auto** user_paths = CFX_GEModule::Get()->GetUserFontPaths(); ++ if (user_paths) { ++ auto font_info = std::make_unique(); ++ for (; *user_paths; user_paths++) ++ font_info->AddPath(*user_paths); ++ return std::move(font_info); ++ } ++ ++ if (pdfium::base::win::IsUser32AndGdi32Available()) ++ return std::make_unique(); ++ ++ // Select the fallback font information class if GDI is disabled. ++ auto fallback_info = std::make_unique(); ++ // Construct the font path manually, SHGetKnownFolderPath won't work under ++ // a restrictive sandbox. ++ CHAR windows_path[MAX_PATH] = {}; ++ DWORD path_len = ::GetWindowsDirectoryA(windows_path, MAX_PATH); ++ if (path_len > 0 && path_len < MAX_PATH) { ++ ByteString fonts_path(windows_path); ++ fonts_path += "\\Fonts"; ++ fallback_info->AddPath(fonts_path); ++ } ++ return fallback_info; ++} ++ ++// static ++std::unique_ptr ++CFX_GEModule::PlatformIface::Create() { ++ return std::make_unique(); ++} +diff --git a/core/fxge/win32/cwin32_platform.h b/core/fxge/win32/cwin32_platform.h +new file mode 100644 +index 000000000..a6c54e9d5 +--- /dev/null ++++ b/core/fxge/win32/cwin32_platform.h +@@ -0,0 +1,27 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef CORE_FXGE_WIN32_CWIN32_PLATFORM_H_ ++#define CORE_FXGE_WIN32_CWIN32_PLATFORM_H_ ++ ++#include ++ ++#include "core/fxge/cfx_gemodule.h" ++#include "core/fxge/win32/cgdi_plus_ext.h" ++ ++class CWin32Platform final : public CFX_GEModule::PlatformIface { ++ public: ++ CWin32Platform(); ++ ~CWin32Platform() override; ++ ++ // CFX_GEModule::PlatformIface: ++ void Init() override; ++ std::unique_ptr CreateDefaultSystemFontInfo() override; ++ ++ CGdiplusExt m_GdiplusExt; ++}; ++ ++#endif // CORE_FXGE_WIN32_CWIN32_PLATFORM_H_ +diff --git a/core/fxge/win32/fx_win32_device.cpp b/core/fxge/win32/fx_win32_device.cpp +deleted file mode 100644 +index 58cb504c3..000000000 +--- a/core/fxge/win32/fx_win32_device.cpp ++++ /dev/null +@@ -1,1385 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include +- +-#include +-#include +-#include +- +-#include "core/fxcrt/fx_codepage.h" +-#include "core/fxcrt/fx_memory.h" +-#include "core/fxcrt/fx_system.h" +-#include "core/fxcrt/maybe_owned.h" +-#include "core/fxge/cfx_folderfontinfo.h" +-#include "core/fxge/cfx_fontmgr.h" +-#include "core/fxge/cfx_gemodule.h" +-#include "core/fxge/cfx_windowsrenderdevice.h" +-#include "core/fxge/dib/cfx_dibextractor.h" +-#include "core/fxge/dib/cfx_imagerenderer.h" +-#include "core/fxge/dib/cstretchengine.h" +-#include "core/fxge/fx_font.h" +-#include "core/fxge/fx_freetype.h" +-#include "core/fxge/systemfontinfo_iface.h" +-#include "core/fxge/win32/cfx_windowsdib.h" +-#include "core/fxge/win32/win32_int.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/span.h" +-#include "third_party/base/win/win_util.h" +- +-#ifndef _SKIA_SUPPORT_ +-#include "core/fxge/agg/fx_agg_driver.h" +-#endif +- +-namespace { +- +-const struct { +- const char* m_pFaceName; +- const char* m_pVariantName; // Note: UTF-16LE terminator required. +-} g_VariantNames[] = { +- {"DFKai-SB", "\x19\x6A\x77\x69\xD4\x9A\x00\x00"}, +-}; +- +-const struct { +- const char* m_pName; +- const char* m_pWinName; +- bool m_bBold; +- bool m_bItalic; +-} g_Base14Substs[] = { +- {"Courier", "Courier New", false, false}, +- {"Courier-Bold", "Courier New", true, false}, +- {"Courier-BoldOblique", "Courier New", true, true}, +- {"Courier-Oblique", "Courier New", false, true}, +- {"Helvetica", "Arial", false, false}, +- {"Helvetica-Bold", "Arial", true, false}, +- {"Helvetica-BoldOblique", "Arial", true, true}, +- {"Helvetica-Oblique", "Arial", false, true}, +- {"Times-Roman", "Times New Roman", false, false}, +- {"Times-Bold", "Times New Roman", true, false}, +- {"Times-BoldItalic", "Times New Roman", true, true}, +- {"Times-Italic", "Times New Roman", false, true}, +-}; +- +-struct FontNameMap { +- const char* m_pSubFontName; +- const char* m_pSrcFontName; +-}; +-const FontNameMap g_JpFontNameMap[] = { +- {"MS Mincho", "Heiseimin-W3"}, +- {"MS Gothic", "Jun101-Light"}, +-}; +- +-bool GetSubFontName(ByteString* name) { +- for (size_t i = 0; i < FX_ArraySize(g_JpFontNameMap); ++i) { +- if (!FXSYS_stricmp(name->c_str(), g_JpFontNameMap[i].m_pSrcFontName)) { +- *name = g_JpFontNameMap[i].m_pSubFontName; +- return true; +- } +- } +- return false; +-} +- +-HPEN CreateExtPen(const CFX_GraphStateData* pGraphState, +- const CFX_Matrix* pMatrix, +- uint32_t argb) { +- ASSERT(pGraphState); +- +- float scale = 1.0f; +- if (pMatrix) { +- scale = fabs(pMatrix->a) > fabs(pMatrix->b) ? fabs(pMatrix->a) +- : fabs(pMatrix->b); +- } +- float width = std::max(scale * pGraphState->m_LineWidth, 1.0f); +- +- uint32_t PenStyle = PS_GEOMETRIC; +- if (!pGraphState->m_DashArray.empty()) +- PenStyle |= PS_USERSTYLE; +- else +- PenStyle |= PS_SOLID; +- +- switch (pGraphState->m_LineCap) { +- case CFX_GraphStateData::LineCapButt: +- PenStyle |= PS_ENDCAP_FLAT; +- break; +- case CFX_GraphStateData::LineCapRound: +- PenStyle |= PS_ENDCAP_ROUND; +- break; +- case CFX_GraphStateData::LineCapSquare: +- PenStyle |= PS_ENDCAP_SQUARE; +- break; +- } +- switch (pGraphState->m_LineJoin) { +- case CFX_GraphStateData::LineJoinMiter: +- PenStyle |= PS_JOIN_MITER; +- break; +- case CFX_GraphStateData::LineJoinRound: +- PenStyle |= PS_JOIN_ROUND; +- break; +- case CFX_GraphStateData::LineJoinBevel: +- PenStyle |= PS_JOIN_BEVEL; +- break; +- } +- +- FX_COLORREF colorref = ArgbToColorRef(argb); +- LOGBRUSH lb; +- lb.lbColor = colorref; +- lb.lbStyle = BS_SOLID; +- lb.lbHatch = 0; +- std::vector dashes; +- if (!pGraphState->m_DashArray.empty()) { +- dashes.resize(pGraphState->m_DashArray.size()); +- for (size_t i = 0; i < pGraphState->m_DashArray.size(); i++) { +- dashes[i] = FXSYS_roundf( +- pMatrix ? pMatrix->TransformDistance(pGraphState->m_DashArray[i]) +- : pGraphState->m_DashArray[i]); +- dashes[i] = std::max(dashes[i], 1U); +- } +- } +- return ExtCreatePen(PenStyle, (DWORD)ceil(width), &lb, +- pGraphState->m_DashArray.size(), +- reinterpret_cast(dashes.data())); +-} +- +-HBRUSH CreateBrush(uint32_t argb) { +- return CreateSolidBrush(ArgbToColorRef(argb)); +-} +- +-void SetPathToDC(HDC hDC, +- const CFX_PathData* pPathData, +- const CFX_Matrix* pMatrix) { +- BeginPath(hDC); +- +- const std::vector& pPoints = pPathData->GetPoints(); +- for (size_t i = 0; i < pPoints.size(); i++) { +- CFX_PointF pos = pPoints[i].m_Point; +- if (pMatrix) +- pos = pMatrix->Transform(pos); +- +- CFX_Point screen(FXSYS_roundf(pos.x), FXSYS_roundf(pos.y)); +- FXPT_TYPE point_type = pPoints[i].m_Type; +- if (point_type == FXPT_TYPE::MoveTo) { +- MoveToEx(hDC, screen.x, screen.y, nullptr); +- } else if (point_type == FXPT_TYPE::LineTo) { +- if (pPoints[i].m_Point == pPoints[i - 1].m_Point) +- screen.x++; +- +- LineTo(hDC, screen.x, screen.y); +- } else if (point_type == FXPT_TYPE::BezierTo) { +- POINT lppt[3]; +- lppt[0].x = screen.x; +- lppt[0].y = screen.y; +- +- pos = pPoints[i + 1].m_Point; +- if (pMatrix) +- pos = pMatrix->Transform(pos); +- +- lppt[1].x = FXSYS_roundf(pos.x); +- lppt[1].y = FXSYS_roundf(pos.y); +- +- pos = pPoints[i + 2].m_Point; +- if (pMatrix) +- pos = pMatrix->Transform(pos); +- +- lppt[2].x = FXSYS_roundf(pos.x); +- lppt[2].y = FXSYS_roundf(pos.y); +- PolyBezierTo(hDC, lppt, 3); +- i += 2; +- } +- if (pPoints[i].m_CloseFigure) +- CloseFigure(hDC); +- } +- EndPath(hDC); +-} +- +-#ifdef _SKIA_SUPPORT_ +-// TODO(caryclark) This antigrain function is duplicated here to permit +-// removing the last remaining dependency. Eventually, this will be elminiated +-// altogether and replace by Skia code. +- +-struct rect_base { +- float x1; +- float y1; +- float x2; +- float y2; +-}; +- +-unsigned clip_liang_barsky(float x1, +- float y1, +- float x2, +- float y2, +- const rect_base& clip_box, +- float* x, +- float* y) { +- const float nearzero = 1e-30f; +- float deltax = x2 - x1; +- float deltay = y2 - y1; +- unsigned np = 0; +- if (deltax == 0) +- deltax = (x1 > clip_box.x1) ? -nearzero : nearzero; +- float xin, xout; +- if (deltax > 0) { +- xin = clip_box.x1; +- xout = clip_box.x2; +- } else { +- xin = clip_box.x2; +- xout = clip_box.x1; +- } +- float tinx = (xin - x1) / deltax; +- if (deltay == 0) +- deltay = (y1 > clip_box.y1) ? -nearzero : nearzero; +- float yin, yout; +- if (deltay > 0) { +- yin = clip_box.y1; +- yout = clip_box.y2; +- } else { +- yin = clip_box.y2; +- yout = clip_box.y1; +- } +- float tiny = (yin - y1) / deltay; +- float tin1, tin2; +- if (tinx < tiny) { +- tin1 = tinx; +- tin2 = tiny; +- } else { +- tin1 = tiny; +- tin2 = tinx; +- } +- if (tin1 <= 1.0f) { +- if (0 < tin1) { +- *x++ = xin; +- *y++ = yin; +- ++np; +- } +- if (tin2 <= 1.0f) { +- float toutx = (xout - x1) / deltax; +- float touty = (yout - y1) / deltay; +- float tout1 = (toutx < touty) ? toutx : touty; +- if (tin2 > 0 || tout1 > 0) { +- if (tin2 <= tout1) { +- if (tin2 > 0) { +- if (tinx > tiny) { +- *x++ = xin; +- *y++ = y1 + (deltay * tinx); +- } else { +- *x++ = x1 + (deltax * tiny); +- *y++ = yin; +- } +- ++np; +- } +- if (tout1 < 1.0f) { +- if (toutx < touty) { +- *x++ = xout; +- *y++ = y1 + (deltay * toutx); +- } else { +- *x++ = x1 + (deltax * touty); +- *y++ = yout; +- } +- } else { +- *x++ = x2; +- *y++ = y2; +- } +- ++np; +- } else { +- if (tinx > tiny) { +- *x++ = xin; +- *y++ = yout; +- } else { +- *x++ = xout; +- *y++ = yin; +- } +- ++np; +- } +- } +- } +- } +- return np; +-} +-#endif // _SKIA_SUPPORT_ +- +-class CFX_Win32FallbackFontInfo final : public CFX_FolderFontInfo { +- public: +- CFX_Win32FallbackFontInfo() = default; +- ~CFX_Win32FallbackFontInfo() override = default; +- +- // CFX_FolderFontInfo: +- void* MapFont(int weight, +- bool bItalic, +- int charset, +- int pitch_family, +- const char* family) override; +-}; +- +-class CFX_Win32FontInfo final : public SystemFontInfoIface { +- public: +- CFX_Win32FontInfo(); +- ~CFX_Win32FontInfo() override; +- +- // SystemFontInfoIface +- bool EnumFontList(CFX_FontMapper* pMapper) override; +- void* MapFont(int weight, +- bool bItalic, +- int charset, +- int pitch_family, +- const char* face) override; +- void* GetFont(const char* face) override { return nullptr; } +- uint32_t GetFontData(void* hFont, +- uint32_t table, +- pdfium::span buffer) override; +- bool GetFaceName(void* hFont, ByteString* name) override; +- bool GetFontCharset(void* hFont, int* charset) override; +- void DeleteFont(void* hFont) override; +- +- bool IsOpenTypeFromDiv(const LOGFONTA* plf); +- bool IsSupportFontFormDiv(const LOGFONTA* plf); +- void AddInstalledFont(const LOGFONTA* plf, uint32_t FontType); +- void GetGBPreference(ByteString& face, int weight, int picth_family); +- void GetJapanesePreference(ByteString& face, int weight, int picth_family); +- ByteString FindFont(const ByteString& name); +- +- const HDC m_hDC; +- UnownedPtr m_pMapper; +- ByteString m_LastFamily; +- ByteString m_KaiTi; +- ByteString m_FangSong; +-}; +- +-int CALLBACK FontEnumProc(const LOGFONTA* plf, +- const TEXTMETRICA* lpntme, +- uint32_t FontType, +- LPARAM lParam) { +- CFX_Win32FontInfo* pFontInfo = reinterpret_cast(lParam); +- pFontInfo->AddInstalledFont(plf, FontType); +- return 1; +-} +- +-CFX_Win32FontInfo::CFX_Win32FontInfo() : m_hDC(CreateCompatibleDC(nullptr)) {} +- +-CFX_Win32FontInfo::~CFX_Win32FontInfo() { +- DeleteDC(m_hDC); +-} +- +-bool CFX_Win32FontInfo::IsOpenTypeFromDiv(const LOGFONTA* plf) { +- HFONT hFont = CreateFontIndirectA(plf); +- bool ret = false; +- uint32_t font_size = GetFontData(hFont, 0, {}); +- if (font_size != GDI_ERROR && font_size >= sizeof(uint32_t)) { +- uint32_t lVersion = 0; +- GetFontData(hFont, 0, {(uint8_t*)(&lVersion), sizeof(uint32_t)}); +- lVersion = (((uint32_t)(uint8_t)(lVersion)) << 24) | +- ((uint32_t)((uint8_t)(lVersion >> 8))) << 16 | +- ((uint32_t)((uint8_t)(lVersion >> 16))) << 8 | +- ((uint8_t)(lVersion >> 24)); +- if (lVersion == FXBSTR_ID('O', 'T', 'T', 'O') || lVersion == 0x00010000 || +- lVersion == FXBSTR_ID('t', 't', 'c', 'f') || +- lVersion == FXBSTR_ID('t', 'r', 'u', 'e') || lVersion == 0x00020000) { +- ret = true; +- } +- } +- DeleteFont(hFont); +- return ret; +-} +- +-bool CFX_Win32FontInfo::IsSupportFontFormDiv(const LOGFONTA* plf) { +- HFONT hFont = CreateFontIndirectA(plf); +- bool ret = false; +- uint32_t font_size = GetFontData(hFont, 0, {}); +- if (font_size != GDI_ERROR && font_size >= sizeof(uint32_t)) { +- uint32_t lVersion = 0; +- GetFontData(hFont, 0, {(uint8_t*)(&lVersion), sizeof(lVersion)}); +- lVersion = (((uint32_t)(uint8_t)(lVersion)) << 24) | +- ((uint32_t)((uint8_t)(lVersion >> 8))) << 16 | +- ((uint32_t)((uint8_t)(lVersion >> 16))) << 8 | +- ((uint8_t)(lVersion >> 24)); +- if (lVersion == FXBSTR_ID('O', 'T', 'T', 'O') || lVersion == 0x00010000 || +- lVersion == FXBSTR_ID('t', 't', 'c', 'f') || +- lVersion == FXBSTR_ID('t', 'r', 'u', 'e') || lVersion == 0x00020000 || +- (lVersion & 0xFFFF0000) == FXBSTR_ID(0x80, 0x01, 0x00, 0x00) || +- (lVersion & 0xFFFF0000) == FXBSTR_ID('%', '!', 0, 0)) { +- ret = true; +- } +- } +- DeleteFont(hFont); +- return ret; +-} +- +-void CFX_Win32FontInfo::AddInstalledFont(const LOGFONTA* plf, +- uint32_t FontType) { +- ByteString name(plf->lfFaceName); +- if (name.GetLength() > 0 && name[0] == '@') +- return; +- +- if (name == m_LastFamily) { +- m_pMapper->AddInstalledFont(name, plf->lfCharSet); +- return; +- } +- if (!(FontType & TRUETYPE_FONTTYPE)) { +- if (!(FontType & DEVICE_FONTTYPE) || !IsSupportFontFormDiv(plf)) +- return; +- } +- +- m_pMapper->AddInstalledFont(name, plf->lfCharSet); +- m_LastFamily = name; +-} +- +-bool CFX_Win32FontInfo::EnumFontList(CFX_FontMapper* pMapper) { +- m_pMapper = pMapper; +- LOGFONTA lf; +- memset(&lf, 0, sizeof(LOGFONTA)); +- lf.lfCharSet = FX_CHARSET_Default; +- lf.lfFaceName[0] = 0; +- lf.lfPitchAndFamily = 0; +- EnumFontFamiliesExA(m_hDC, &lf, (FONTENUMPROCA)FontEnumProc, (uintptr_t)this, +- 0); +- return true; +-} +- +-ByteString CFX_Win32FontInfo::FindFont(const ByteString& name) { +- if (!m_pMapper) +- return name; +- +- for (size_t i = 0; i < m_pMapper->m_InstalledTTFonts.size(); ++i) { +- ByteString thisname = m_pMapper->m_InstalledTTFonts[i]; +- if (thisname.First(name.GetLength()) == name) +- return m_pMapper->m_InstalledTTFonts[i]; +- } +- for (size_t i = 0; i < m_pMapper->m_LocalizedTTFonts.size(); ++i) { +- ByteString thisname = m_pMapper->m_LocalizedTTFonts[i].first; +- if (thisname.First(name.GetLength()) == name) +- return m_pMapper->m_LocalizedTTFonts[i].second; +- } +- return ByteString(); +-} +- +-void* CFX_Win32FallbackFontInfo::MapFont(int weight, +- bool bItalic, +- int charset, +- int pitch_family, +- const char* cstr_face) { +- void* font = GetSubstFont(cstr_face); +- if (font) +- return font; +- +- bool bCJK = true; +- switch (charset) { +- case FX_CHARSET_ShiftJIS: +- case FX_CHARSET_ChineseSimplified: +- case FX_CHARSET_ChineseTraditional: +- case FX_CHARSET_Hangul: +- break; +- default: +- bCJK = false; +- break; +- } +- return FindFont(weight, bItalic, charset, pitch_family, cstr_face, !bCJK); +-} +- +-void CFX_Win32FontInfo::GetGBPreference(ByteString& face, +- int weight, +- int picth_family) { +- if (face.Contains("KaiTi") || face.Contains("\xbf\xac")) { +- if (m_KaiTi.IsEmpty()) { +- m_KaiTi = FindFont("KaiTi"); +- if (m_KaiTi.IsEmpty()) { +- m_KaiTi = "SimSun"; +- } +- } +- face = m_KaiTi; +- } else if (face.Contains("FangSong") || face.Contains("\xb7\xc2\xcb\xce")) { +- if (m_FangSong.IsEmpty()) { +- m_FangSong = FindFont("FangSong"); +- if (m_FangSong.IsEmpty()) { +- m_FangSong = "SimSun"; +- } +- } +- face = m_FangSong; +- } else if (face.Contains("SimSun") || face.Contains("\xcb\xce")) { +- face = "SimSun"; +- } else if (face.Contains("SimHei") || face.Contains("\xba\xda")) { +- face = "SimHei"; +- } else if (!(picth_family & FF_ROMAN) && weight > 550) { +- face = "SimHei"; +- } else { +- face = "SimSun"; +- } +-} +- +-void CFX_Win32FontInfo::GetJapanesePreference(ByteString& face, +- int weight, +- int picth_family) { +- if (face.Contains("Gothic") || +- face.Contains("\x83\x53\x83\x56\x83\x62\x83\x4e")) { +- if (face.Contains("PGothic") || +- face.Contains("\x82\x6f\x83\x53\x83\x56\x83\x62\x83\x4e")) { +- face = "MS PGothic"; +- } else if (face.Contains("UI Gothic")) { +- face = "MS UI Gothic"; +- } else { +- if (face.Contains("HGSGothicM") || face.Contains("HGMaruGothicMPRO")) { +- face = "MS PGothic"; +- } else { +- face = "MS Gothic"; +- } +- } +- return; +- } +- if (face.Contains("Mincho") || face.Contains("\x96\xbe\x92\xa9")) { +- if (face.Contains("PMincho") || face.Contains("\x82\x6f\x96\xbe\x92\xa9")) { +- face = "MS PMincho"; +- } else { +- face = "MS Mincho"; +- } +- return; +- } +- if (GetSubFontName(&face)) +- return; +- +- if (!(picth_family & FF_ROMAN) && weight > 400) { +- face = "MS PGothic"; +- } else { +- face = "MS PMincho"; +- } +-} +- +-void* CFX_Win32FontInfo::MapFont(int weight, +- bool bItalic, +- int charset, +- int pitch_family, +- const char* cstr_face) { +- ByteString face = cstr_face; +- int iBaseFont; +- for (iBaseFont = 0; iBaseFont < 12; iBaseFont++) { +- if (face == ByteStringView(g_Base14Substs[iBaseFont].m_pName)) { +- face = g_Base14Substs[iBaseFont].m_pWinName; +- weight = g_Base14Substs[iBaseFont].m_bBold ? FW_BOLD : FW_NORMAL; +- bItalic = g_Base14Substs[iBaseFont].m_bItalic; +- break; +- } +- } +- if (charset == FX_CHARSET_ANSI || charset == FX_CHARSET_Symbol) +- charset = FX_CHARSET_Default; +- +- int subst_pitch_family = pitch_family; +- switch (charset) { +- case FX_CHARSET_ShiftJIS: +- subst_pitch_family = FF_ROMAN; +- break; +- case FX_CHARSET_ChineseTraditional: +- case FX_CHARSET_Hangul: +- case FX_CHARSET_ChineseSimplified: +- subst_pitch_family = 0; +- break; +- } +- HFONT hFont = +- ::CreateFontA(-10, 0, 0, 0, weight, bItalic, 0, 0, charset, +- OUT_TT_ONLY_PRECIS, 0, 0, subst_pitch_family, face.c_str()); +- char facebuf[100]; +- HFONT hOldFont = (HFONT)::SelectObject(m_hDC, hFont); +- ::GetTextFaceA(m_hDC, 100, facebuf); +- ::SelectObject(m_hDC, hOldFont); +- if (face.EqualNoCase(facebuf)) +- return hFont; +- +- WideString wsFace = WideString::FromDefANSI(facebuf); +- for (size_t i = 0; i < FX_ArraySize(g_VariantNames); ++i) { +- if (face != g_VariantNames[i].m_pFaceName) +- continue; +- +- const unsigned short* pName = reinterpret_cast( +- g_VariantNames[i].m_pVariantName); +- size_t len = WideString::WStringLength(pName); +- WideString wsName = WideString::FromUTF16LE(pName, len); +- if (wsFace == wsName) +- return hFont; +- } +- ::DeleteObject(hFont); +- if (charset == FX_CHARSET_Default) +- return nullptr; +- +- switch (charset) { +- case FX_CHARSET_ShiftJIS: +- GetJapanesePreference(face, weight, pitch_family); +- break; +- case FX_CHARSET_ChineseSimplified: +- GetGBPreference(face, weight, pitch_family); +- break; +- case FX_CHARSET_Hangul: +- face = "Gulim"; +- break; +- case FX_CHARSET_ChineseTraditional: +- if (face.Contains("MSung")) { +- face = "MingLiU"; +- } else { +- face = "PMingLiU"; +- } +- break; +- } +- hFont = +- ::CreateFontA(-10, 0, 0, 0, weight, bItalic, 0, 0, charset, +- OUT_TT_ONLY_PRECIS, 0, 0, subst_pitch_family, face.c_str()); +- return hFont; +-} +- +-void CFX_Win32FontInfo::DeleteFont(void* hFont) { +- ::DeleteObject(hFont); +-} +- +-uint32_t CFX_Win32FontInfo::GetFontData(void* hFont, +- uint32_t table, +- pdfium::span buffer) { +- HFONT hOldFont = (HFONT)::SelectObject(m_hDC, (HFONT)hFont); +- table = FXDWORD_GET_MSBFIRST(reinterpret_cast(&table)); +- uint32_t size = ::GetFontData(m_hDC, table, 0, buffer.data(), buffer.size()); +- ::SelectObject(m_hDC, hOldFont); +- if (size == GDI_ERROR) { +- return 0; +- } +- return size; +-} +- +-bool CFX_Win32FontInfo::GetFaceName(void* hFont, ByteString* name) { +- char facebuf[100]; +- HFONT hOldFont = (HFONT)::SelectObject(m_hDC, (HFONT)hFont); +- int ret = ::GetTextFaceA(m_hDC, 100, facebuf); +- ::SelectObject(m_hDC, hOldFont); +- if (ret == 0) { +- return false; +- } +- *name = facebuf; +- return true; +-} +- +-bool CFX_Win32FontInfo::GetFontCharset(void* hFont, int* charset) { +- TEXTMETRIC tm; +- HFONT hOldFont = (HFONT)::SelectObject(m_hDC, (HFONT)hFont); +- ::GetTextMetrics(m_hDC, &tm); +- ::SelectObject(m_hDC, hOldFont); +- *charset = tm.tmCharSet; +- return true; +-} +- +-} // namespace +- +-WindowsPrintMode g_pdfium_print_mode = WindowsPrintMode::kModeEmf; +- +-std::unique_ptr SystemFontInfoIface::CreateDefault( +- const char** pUnused) { +- if (pdfium::base::win::IsUser32AndGdi32Available()) +- return std::unique_ptr(new CFX_Win32FontInfo); +- +- // Select the fallback font information class if GDI is disabled. +- CFX_Win32FallbackFontInfo* pInfoFallback = new CFX_Win32FallbackFontInfo; +- // Construct the font path manually, SHGetKnownFolderPath won't work under +- // a restrictive sandbox. +- CHAR windows_path[MAX_PATH] = {}; +- DWORD path_len = ::GetWindowsDirectoryA(windows_path, MAX_PATH); +- if (path_len > 0 && path_len < MAX_PATH) { +- ByteString fonts_path(windows_path); +- fonts_path += "\\Fonts"; +- pInfoFallback->AddPath(fonts_path); +- } +- return std::unique_ptr(pInfoFallback); +-} +- +-CWin32Platform::CWin32Platform() = default; +- +-CWin32Platform::~CWin32Platform() = default; +- +-void CWin32Platform::Init() { +- OSVERSIONINFO ver; +- ver.dwOSVersionInfoSize = sizeof(ver); +- GetVersionEx(&ver); +- m_bHalfTone = ver.dwMajorVersion >= 5; +- if (pdfium::base::win::IsUser32AndGdi32Available()) +- m_GdiplusExt.Load(); +- CFX_GEModule::Get()->GetFontMgr()->SetSystemFontInfo( +- SystemFontInfoIface::CreateDefault(nullptr)); +-} +- +-// static +-std::unique_ptr +-CFX_GEModule::PlatformIface::Create() { +- return pdfium::MakeUnique(); +-} +- +-CGdiDeviceDriver::CGdiDeviceDriver(HDC hDC, DeviceType device_type) +- : m_hDC(hDC), m_DeviceType(device_type) { +- auto* pPlatform = +- static_cast(CFX_GEModule::Get()->GetPlatform()); +- SetStretchBltMode(m_hDC, pPlatform->m_bHalfTone ? HALFTONE : COLORONCOLOR); +- DWORD obj_type = GetObjectType(m_hDC); +- m_bMetafileDCType = obj_type == OBJ_ENHMETADC || obj_type == OBJ_ENHMETAFILE; +- if (obj_type == OBJ_MEMDC) { +- HBITMAP hBitmap = CreateBitmap(1, 1, 1, 1, nullptr); +- hBitmap = (HBITMAP)SelectObject(m_hDC, hBitmap); +- BITMAP bitmap; +- GetObject(hBitmap, sizeof bitmap, &bitmap); +- m_nBitsPerPixel = bitmap.bmBitsPixel; +- m_Width = bitmap.bmWidth; +- m_Height = abs(bitmap.bmHeight); +- hBitmap = (HBITMAP)SelectObject(m_hDC, hBitmap); +- DeleteObject(hBitmap); +- } else { +- m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL); +- m_Width = ::GetDeviceCaps(m_hDC, HORZRES); +- m_Height = ::GetDeviceCaps(m_hDC, VERTRES); +- } +- if (m_DeviceType != DeviceType::kDisplay) { +- m_RenderCaps = FXRC_BIT_MASK; +- } else { +- m_RenderCaps = FXRC_GET_BITS | FXRC_BIT_MASK; +- } +-} +- +-CGdiDeviceDriver::~CGdiDeviceDriver() = default; +- +-DeviceType CGdiDeviceDriver::GetDeviceType() const { +- return m_DeviceType; +-} +- +-int CGdiDeviceDriver::GetDeviceCaps(int caps_id) const { +- switch (caps_id) { +- case FXDC_PIXEL_WIDTH: +- return m_Width; +- case FXDC_PIXEL_HEIGHT: +- return m_Height; +- case FXDC_BITS_PIXEL: +- return m_nBitsPerPixel; +- case FXDC_RENDER_CAPS: +- return m_RenderCaps; +- default: +- NOTREACHED(); +- return 0; +- } +-} +- +-void CGdiDeviceDriver::SaveState() { +- SaveDC(m_hDC); +-} +- +-void CGdiDeviceDriver::RestoreState(bool bKeepSaved) { +- RestoreDC(m_hDC, -1); +- if (bKeepSaved) +- SaveDC(m_hDC); +-} +- +-bool CGdiDeviceDriver::GDI_SetDIBits(const RetainPtr& pBitmap1, +- const FX_RECT& src_rect, +- int left, +- int top) { +- if (m_DeviceType == DeviceType::kPrinter) { +- RetainPtr pBitmap = pBitmap1->FlipImage(false, true); +- if (!pBitmap) +- return false; +- +- if (pBitmap->IsCmykImage() && !pBitmap->ConvertFormat(FXDIB_Rgb)) +- return false; +- +- LPBYTE pBuffer = pBitmap->GetBuffer(); +- ByteString info = CFX_WindowsDIB::GetBitmapInfo(pBitmap); +- ((BITMAPINFOHEADER*)info.c_str())->biHeight *= -1; +- FX_RECT dst_rect(0, 0, src_rect.Width(), src_rect.Height()); +- dst_rect.Intersect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight()); +- int dst_width = dst_rect.Width(); +- int dst_height = dst_rect.Height(); +- ::StretchDIBits(m_hDC, left, top, dst_width, dst_height, 0, 0, dst_width, +- dst_height, pBuffer, (BITMAPINFO*)info.c_str(), +- DIB_RGB_COLORS, SRCCOPY); +- return true; +- } +- +- RetainPtr pBitmap = pBitmap1; +- if (pBitmap->IsCmykImage()) { +- pBitmap = pBitmap->CloneConvert(FXDIB_Rgb); +- if (!pBitmap) +- return false; +- } +- LPBYTE pBuffer = pBitmap->GetBuffer(); +- ByteString info = CFX_WindowsDIB::GetBitmapInfo(pBitmap); +- ::SetDIBitsToDevice(m_hDC, left, top, src_rect.Width(), src_rect.Height(), +- src_rect.left, pBitmap->GetHeight() - src_rect.bottom, 0, +- pBitmap->GetHeight(), pBuffer, (BITMAPINFO*)info.c_str(), +- DIB_RGB_COLORS); +- return true; +-} +- +-bool CGdiDeviceDriver::GDI_StretchDIBits( +- const RetainPtr& pBitmap1, +- int dest_left, +- int dest_top, +- int dest_width, +- int dest_height, +- const FXDIB_ResampleOptions& options) { +- RetainPtr pBitmap = pBitmap1; +- if (!pBitmap || dest_width == 0 || dest_height == 0) +- return false; +- +- if (pBitmap->IsCmykImage() && !pBitmap->ConvertFormat(FXDIB_Rgb)) +- return false; +- +- ByteString info = CFX_WindowsDIB::GetBitmapInfo(pBitmap); +- if ((int64_t)abs(dest_width) * abs(dest_height) < +- (int64_t)pBitmap1->GetWidth() * pBitmap1->GetHeight() * 4 || +- options.bInterpolateBilinear || options.bInterpolateBicubic) { +- SetStretchBltMode(m_hDC, HALFTONE); +- } else { +- SetStretchBltMode(m_hDC, COLORONCOLOR); +- } +- RetainPtr pToStrechBitmap = pBitmap; +- if (m_DeviceType == DeviceType::kPrinter && +- ((int64_t)pBitmap->GetWidth() * pBitmap->GetHeight() > +- (int64_t)abs(dest_width) * abs(dest_height))) { +- pToStrechBitmap = pBitmap->StretchTo(dest_width, dest_height, +- FXDIB_ResampleOptions(), nullptr); +- } +- ByteString toStrechBitmapInfo = +- CFX_WindowsDIB::GetBitmapInfo(pToStrechBitmap); +- ::StretchDIBits(m_hDC, dest_left, dest_top, dest_width, dest_height, 0, 0, +- pToStrechBitmap->GetWidth(), pToStrechBitmap->GetHeight(), +- pToStrechBitmap->GetBuffer(), +- (BITMAPINFO*)toStrechBitmapInfo.c_str(), DIB_RGB_COLORS, +- SRCCOPY); +- return true; +-} +- +-bool CGdiDeviceDriver::GDI_StretchBitMask( +- const RetainPtr& pBitmap1, +- int dest_left, +- int dest_top, +- int dest_width, +- int dest_height, +- uint32_t bitmap_color) { +- RetainPtr pBitmap = pBitmap1; +- if (!pBitmap || dest_width == 0 || dest_height == 0) +- return false; +- +- int width = pBitmap->GetWidth(), height = pBitmap->GetHeight(); +- struct { +- BITMAPINFOHEADER bmiHeader; +- uint32_t bmiColors[2]; +- } bmi; +- memset(&bmi.bmiHeader, 0, sizeof(BITMAPINFOHEADER)); +- bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); +- bmi.bmiHeader.biBitCount = 1; +- bmi.bmiHeader.biCompression = BI_RGB; +- bmi.bmiHeader.biHeight = -height; +- bmi.bmiHeader.biPlanes = 1; +- bmi.bmiHeader.biWidth = width; +- if (m_nBitsPerPixel != 1) { +- SetStretchBltMode(m_hDC, HALFTONE); +- } +- bmi.bmiColors[0] = 0xffffff; +- bmi.bmiColors[1] = 0; +- +- HBRUSH hPattern = CreateBrush(bitmap_color); +- HBRUSH hOld = (HBRUSH)SelectObject(m_hDC, hPattern); +- +- // In PDF, when image mask is 1, use device bitmap; when mask is 0, use brush +- // bitmap. +- // A complete list of the boolen operations is as follows: +- +- /* P(bitmap_color) S(ImageMask) D(DeviceBitmap) Result +- * 0 0 0 0 +- * 0 0 1 0 +- * 0 1 0 0 +- * 0 1 1 1 +- * 1 0 0 1 +- * 1 0 1 1 +- * 1 1 0 0 +- * 1 1 1 1 +- */ +- // The boolen codes is B8. Based on +- // http://msdn.microsoft.com/en-us/library/aa932106.aspx, the ROP3 code is +- // 0xB8074A +- +- ::StretchDIBits(m_hDC, dest_left, dest_top, dest_width, dest_height, 0, 0, +- width, height, pBitmap->GetBuffer(), (BITMAPINFO*)&bmi, +- DIB_RGB_COLORS, 0xB8074A); +- +- SelectObject(m_hDC, hOld); +- DeleteObject(hPattern); +- +- return true; +-} +- +-bool CGdiDeviceDriver::GetClipBox(FX_RECT* pRect) { +- return !!(::GetClipBox(m_hDC, (RECT*)pRect)); +-} +- +-void CGdiDeviceDriver::DrawLine(float x1, float y1, float x2, float y2) { +- if (!m_bMetafileDCType) { // EMF drawing is not bound to the DC. +- int startOutOfBoundsFlag = (x1 < 0) | ((x1 > m_Width) << 1) | +- ((y1 < 0) << 2) | ((y1 > m_Height) << 3); +- int endOutOfBoundsFlag = (x2 < 0) | ((x2 > m_Width) << 1) | +- ((y2 < 0) << 2) | ((y2 > m_Height) << 3); +- if (startOutOfBoundsFlag & endOutOfBoundsFlag) +- return; +- +- if (startOutOfBoundsFlag || endOutOfBoundsFlag) { +- float x[2]; +- float y[2]; +- int np; +-#ifdef _SKIA_SUPPORT_ +- // TODO(caryclark) temporary replacement of antigrain in line function +- // to permit removing antigrain altogether +- rect_base rect = {0.0f, 0.0f, (float)(m_Width), (float)(m_Height)}; +- np = clip_liang_barsky(x1, y1, x2, y2, rect, x, y); +-#else +- agg::rect_base rect(0.0f, 0.0f, (float)(m_Width), +- (float)(m_Height)); +- np = agg::clip_liang_barsky(x1, y1, x2, y2, rect, x, y); +-#endif +- if (np == 0) +- return; +- +- if (np == 1) { +- x2 = x[0]; +- y2 = y[0]; +- } else { +- ASSERT(np == 2); +- x1 = x[0]; +- y1 = y[0]; +- x2 = x[1]; +- y2 = y[1]; +- } +- } +- } +- +- MoveToEx(m_hDC, FXSYS_roundf(x1), FXSYS_roundf(y1), nullptr); +- LineTo(m_hDC, FXSYS_roundf(x2), FXSYS_roundf(y2)); +-} +- +-bool CGdiDeviceDriver::DrawPath(const CFX_PathData* pPathData, +- const CFX_Matrix* pMatrix, +- const CFX_GraphStateData* pGraphState, +- uint32_t fill_color, +- uint32_t stroke_color, +- int fill_mode, +- BlendMode blend_type) { +- if (blend_type != BlendMode::kNormal) +- return false; +- +- auto* pPlatform = +- static_cast(CFX_GEModule::Get()->GetPlatform()); +- if (!(pGraphState || stroke_color == 0) && +- !pPlatform->m_GdiplusExt.IsAvailable()) { +- CFX_FloatRect bbox_f = pPathData->GetBoundingBox(); +- if (pMatrix) +- bbox_f = pMatrix->TransformRect(bbox_f); +- +- FX_RECT bbox = bbox_f.GetInnerRect(); +- if (bbox.Width() <= 0) { +- return DrawCosmeticLine(CFX_PointF(bbox.left, bbox.top), +- CFX_PointF(bbox.left, bbox.bottom + 1), +- fill_color, BlendMode::kNormal); +- } +- if (bbox.Height() <= 0) { +- return DrawCosmeticLine(CFX_PointF(bbox.left, bbox.top), +- CFX_PointF(bbox.right + 1, bbox.top), fill_color, +- BlendMode::kNormal); +- } +- } +- int fill_alpha = FXARGB_A(fill_color); +- int stroke_alpha = FXARGB_A(stroke_color); +- bool bDrawAlpha = (fill_alpha > 0 && fill_alpha < 255) || +- (stroke_alpha > 0 && stroke_alpha < 255 && pGraphState); +- if (!pPlatform->m_GdiplusExt.IsAvailable() && bDrawAlpha) +- return false; +- +- if (pPlatform->m_GdiplusExt.IsAvailable()) { +- if (bDrawAlpha || ((m_DeviceType != DeviceType::kPrinter && +- !(fill_mode & FXFILL_FULLCOVER)) || +- (pGraphState && !pGraphState->m_DashArray.empty()))) { +- if (!((!pMatrix || !pMatrix->WillScale()) && pGraphState && +- pGraphState->m_LineWidth == 1.0f && +- (pPathData->GetPoints().size() == 5 || +- pPathData->GetPoints().size() == 4) && +- pPathData->IsRect())) { +- if (pPlatform->m_GdiplusExt.DrawPath(m_hDC, pPathData, pMatrix, +- pGraphState, fill_color, +- stroke_color, fill_mode)) { +- return true; +- } +- } +- } +- } +- int old_fill_mode = fill_mode; +- fill_mode &= 3; +- HPEN hPen = nullptr; +- HBRUSH hBrush = nullptr; +- if (pGraphState && stroke_alpha) { +- SetMiterLimit(m_hDC, pGraphState->m_MiterLimit, nullptr); +- hPen = CreateExtPen(pGraphState, pMatrix, stroke_color); +- hPen = (HPEN)SelectObject(m_hDC, hPen); +- } +- if (fill_mode && fill_alpha) { +- SetPolyFillMode(m_hDC, fill_mode); +- hBrush = CreateBrush(fill_color); +- hBrush = (HBRUSH)SelectObject(m_hDC, hBrush); +- } +- if (pPathData->GetPoints().size() == 2 && pGraphState && +- !pGraphState->m_DashArray.empty()) { +- CFX_PointF pos1 = pPathData->GetPoint(0); +- CFX_PointF pos2 = pPathData->GetPoint(1); +- if (pMatrix) { +- pos1 = pMatrix->Transform(pos1); +- pos2 = pMatrix->Transform(pos2); +- } +- DrawLine(pos1.x, pos1.y, pos2.x, pos2.y); +- } else { +- SetPathToDC(m_hDC, pPathData, pMatrix); +- if (pGraphState && stroke_alpha) { +- if (fill_mode && fill_alpha) { +- if (old_fill_mode & FX_FILL_TEXT_MODE) { +- StrokeAndFillPath(m_hDC); +- } else { +- FillPath(m_hDC); +- SetPathToDC(m_hDC, pPathData, pMatrix); +- StrokePath(m_hDC); +- } +- } else { +- StrokePath(m_hDC); +- } +- } else if (fill_mode && fill_alpha) { +- FillPath(m_hDC); +- } +- } +- if (hPen) { +- hPen = (HPEN)SelectObject(m_hDC, hPen); +- DeleteObject(hPen); +- } +- if (hBrush) { +- hBrush = (HBRUSH)SelectObject(m_hDC, hBrush); +- DeleteObject(hBrush); +- } +- return true; +-} +- +-bool CGdiDeviceDriver::FillRectWithBlend(const FX_RECT& rect, +- uint32_t fill_color, +- BlendMode blend_type) { +- if (blend_type != BlendMode::kNormal) +- return false; +- +- int alpha; +- FX_COLORREF colorref; +- std::tie(alpha, colorref) = ArgbToAlphaAndColorRef(fill_color); +- if (alpha == 0) +- return true; +- +- if (alpha < 255) +- return false; +- +- HBRUSH hBrush = CreateSolidBrush(colorref); +- const RECT* pRect = reinterpret_cast(&rect); +- ::FillRect(m_hDC, pRect, hBrush); +- DeleteObject(hBrush); +- return true; +-} +- +-void CGdiDeviceDriver::SetBaseClip(const FX_RECT& rect) { +- m_BaseClipBox = rect; +-} +- +-bool CGdiDeviceDriver::SetClip_PathFill(const CFX_PathData* pPathData, +- const CFX_Matrix* pMatrix, +- int fill_mode) { +- if (pPathData->GetPoints().size() == 5) { +- CFX_FloatRect rectf; +- if (pPathData->IsRect(pMatrix, &rectf)) { +- FX_RECT rect = rectf.GetOuterRect(); +- // Can easily apply base clip to protect against wildly large rectangular +- // clips. crbug.com/1019026 +- if (m_BaseClipBox.has_value()) +- rect.Intersect(m_BaseClipBox.value()); +- return IntersectClipRect(m_hDC, rect.left, rect.top, rect.right, +- rect.bottom) != ERROR; +- } +- } +- SetPathToDC(m_hDC, pPathData, pMatrix); +- SetPolyFillMode(m_hDC, fill_mode & 3); +- SelectClipPath(m_hDC, RGN_AND); +- return true; +-} +- +-bool CGdiDeviceDriver::SetClip_PathStroke( +- const CFX_PathData* pPathData, +- const CFX_Matrix* pMatrix, +- const CFX_GraphStateData* pGraphState) { +- HPEN hPen = CreateExtPen(pGraphState, pMatrix, 0xff000000); +- hPen = (HPEN)SelectObject(m_hDC, hPen); +- SetPathToDC(m_hDC, pPathData, pMatrix); +- WidenPath(m_hDC); +- SetPolyFillMode(m_hDC, WINDING); +- bool ret = !!SelectClipPath(m_hDC, RGN_AND); +- hPen = (HPEN)SelectObject(m_hDC, hPen); +- DeleteObject(hPen); +- return ret; +-} +- +-bool CGdiDeviceDriver::DrawCosmeticLine(const CFX_PointF& ptMoveTo, +- const CFX_PointF& ptLineTo, +- uint32_t color, +- BlendMode blend_type) { +- if (blend_type != BlendMode::kNormal) +- return false; +- +- int alpha; +- FX_COLORREF colorref; +- std::tie(alpha, colorref) = ArgbToAlphaAndColorRef(color); +- if (alpha == 0) +- return true; +- +- HPEN hPen = CreatePen(PS_SOLID, 1, colorref); +- hPen = (HPEN)SelectObject(m_hDC, hPen); +- MoveToEx(m_hDC, FXSYS_roundf(ptMoveTo.x), FXSYS_roundf(ptMoveTo.y), nullptr); +- LineTo(m_hDC, FXSYS_roundf(ptLineTo.x), FXSYS_roundf(ptLineTo.y)); +- hPen = (HPEN)SelectObject(m_hDC, hPen); +- DeleteObject(hPen); +- return true; +-} +- +-CGdiDisplayDriver::CGdiDisplayDriver(HDC hDC) +- : CGdiDeviceDriver(hDC, DeviceType::kDisplay) { +- auto* pPlatform = +- static_cast(CFX_GEModule::Get()->GetPlatform()); +- if (pPlatform->m_GdiplusExt.IsAvailable()) { +- m_RenderCaps |= FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE; +- } +-} +- +-CGdiDisplayDriver::~CGdiDisplayDriver() = default; +- +-int CGdiDisplayDriver::GetDeviceCaps(int caps_id) const { +- if (caps_id == FXDC_HORZ_SIZE || caps_id == FXDC_VERT_SIZE) +- return 0; +- return CGdiDeviceDriver::GetDeviceCaps(caps_id); +-} +- +-bool CGdiDisplayDriver::GetDIBits(const RetainPtr& pBitmap, +- int left, +- int top) { +- bool ret = false; +- int width = pBitmap->GetWidth(); +- int height = pBitmap->GetHeight(); +- HBITMAP hbmp = CreateCompatibleBitmap(m_hDC, width, height); +- HDC hDCMemory = CreateCompatibleDC(m_hDC); +- HBITMAP holdbmp = (HBITMAP)SelectObject(hDCMemory, hbmp); +- BitBlt(hDCMemory, 0, 0, width, height, m_hDC, left, top, SRCCOPY); +- SelectObject(hDCMemory, holdbmp); +- BITMAPINFO bmi; +- memset(&bmi, 0, sizeof bmi); +- bmi.bmiHeader.biSize = sizeof bmi.bmiHeader; +- bmi.bmiHeader.biBitCount = pBitmap->GetBPP(); +- bmi.bmiHeader.biHeight = -height; +- bmi.bmiHeader.biPlanes = 1; +- bmi.bmiHeader.biWidth = width; +- if (pBitmap->GetBPP() > 8 && !pBitmap->IsCmykImage()) { +- ret = ::GetDIBits(hDCMemory, hbmp, 0, height, pBitmap->GetBuffer(), &bmi, +- DIB_RGB_COLORS) == height; +- } else { +- auto bitmap = pdfium::MakeRetain(); +- if (bitmap->Create(width, height, FXDIB_Rgb)) { +- bmi.bmiHeader.biBitCount = 24; +- ::GetDIBits(hDCMemory, hbmp, 0, height, bitmap->GetBuffer(), &bmi, +- DIB_RGB_COLORS); +- ret = pBitmap->TransferBitmap(0, 0, width, height, bitmap, 0, 0); +- } else { +- ret = false; +- } +- } +- if (pBitmap->HasAlpha() && ret) +- pBitmap->LoadChannel(FXDIB_Alpha, 0xff); +- +- DeleteObject(hbmp); +- DeleteObject(hDCMemory); +- return ret; +-} +- +-bool CGdiDisplayDriver::SetDIBits(const RetainPtr& pSource, +- uint32_t color, +- const FX_RECT& src_rect, +- int left, +- int top, +- BlendMode blend_type) { +- ASSERT(blend_type == BlendMode::kNormal); +- if (pSource->IsAlphaMask()) { +- int width = pSource->GetWidth(), height = pSource->GetHeight(); +- int alpha = FXARGB_A(color); +- if (pSource->GetBPP() != 1 || alpha != 255) { +- auto background = pdfium::MakeRetain(); +- if (!background->Create(width, height, FXDIB_Rgb32) || +- !GetDIBits(background, left, top) || +- !background->CompositeMask(0, 0, width, height, pSource, color, 0, 0, +- BlendMode::kNormal, nullptr, false)) { +- return false; +- } +- FX_RECT alpha_src_rect(0, 0, width, height); +- return SetDIBits(background, 0, alpha_src_rect, left, top, +- BlendMode::kNormal); +- } +- FX_RECT clip_rect(left, top, left + src_rect.Width(), +- top + src_rect.Height()); +- return StretchDIBits(pSource, color, left - src_rect.left, +- top - src_rect.top, width, height, &clip_rect, +- FXDIB_ResampleOptions(), BlendMode::kNormal); +- } +- int width = src_rect.Width(); +- int height = src_rect.Height(); +- if (pSource->HasAlpha()) { +- auto bitmap = pdfium::MakeRetain(); +- if (!bitmap->Create(width, height, FXDIB_Rgb) || +- !GetDIBits(bitmap, left, top) || +- !bitmap->CompositeBitmap(0, 0, width, height, pSource, src_rect.left, +- src_rect.top, BlendMode::kNormal, nullptr, +- false)) { +- return false; +- } +- FX_RECT alpha_src_rect(0, 0, width, height); +- return SetDIBits(bitmap, 0, alpha_src_rect, left, top, BlendMode::kNormal); +- } +- CFX_DIBExtractor temp(pSource); +- RetainPtr pBitmap = temp.GetBitmap(); +- if (!pBitmap) +- return false; +- return GDI_SetDIBits(pBitmap, src_rect, left, top); +-} +- +-bool CGdiDisplayDriver::UseFoxitStretchEngine( +- const RetainPtr& pSource, +- uint32_t color, +- int dest_left, +- int dest_top, +- int dest_width, +- int dest_height, +- const FX_RECT* pClipRect, +- const FXDIB_ResampleOptions& options) { +- FX_RECT bitmap_clip = *pClipRect; +- if (dest_width < 0) +- dest_left += dest_width; +- +- if (dest_height < 0) +- dest_top += dest_height; +- +- bitmap_clip.Offset(-dest_left, -dest_top); +- RetainPtr pStretched = +- pSource->StretchTo(dest_width, dest_height, options, &bitmap_clip); +- if (!pStretched) +- return true; +- +- FX_RECT src_rect(0, 0, pStretched->GetWidth(), pStretched->GetHeight()); +- return SetDIBits(pStretched, color, src_rect, pClipRect->left, pClipRect->top, +- BlendMode::kNormal); +-} +- +-bool CGdiDisplayDriver::StretchDIBits(const RetainPtr& pSource, +- uint32_t color, +- int dest_left, +- int dest_top, +- int dest_width, +- int dest_height, +- const FX_RECT* pClipRect, +- const FXDIB_ResampleOptions& options, +- BlendMode blend_type) { +- ASSERT(pSource); +- ASSERT(pClipRect); +- +- if (options.HasAnyOptions() || dest_width > 10000 || dest_width < -10000 || +- dest_height > 10000 || dest_height < -10000) { +- return UseFoxitStretchEngine(pSource, color, dest_left, dest_top, +- dest_width, dest_height, pClipRect, options); +- } +- if (pSource->IsAlphaMask()) { +- FX_RECT image_rect; +- image_rect.left = dest_width > 0 ? dest_left : dest_left + dest_width; +- image_rect.right = dest_width > 0 ? dest_left + dest_width : dest_left; +- image_rect.top = dest_height > 0 ? dest_top : dest_top + dest_height; +- image_rect.bottom = dest_height > 0 ? dest_top + dest_height : dest_top; +- FX_RECT clip_rect = image_rect; +- clip_rect.Intersect(*pClipRect); +- clip_rect.Offset(-image_rect.left, -image_rect.top); +- int clip_width = clip_rect.Width(), clip_height = clip_rect.Height(); +- RetainPtr pStretched(pSource->StretchTo( +- dest_width, dest_height, FXDIB_ResampleOptions(), &clip_rect)); +- if (!pStretched) +- return true; +- +- auto background = pdfium::MakeRetain(); +- if (!background->Create(clip_width, clip_height, FXDIB_Rgb32) || +- !GetDIBits(background, image_rect.left + clip_rect.left, +- image_rect.top + clip_rect.top) || +- !background->CompositeMask(0, 0, clip_width, clip_height, pStretched, +- color, 0, 0, BlendMode::kNormal, nullptr, +- false)) { +- return false; +- } +- +- FX_RECT src_rect(0, 0, clip_width, clip_height); +- return SetDIBits(background, 0, src_rect, image_rect.left + clip_rect.left, +- image_rect.top + clip_rect.top, BlendMode::kNormal); +- } +- if (pSource->HasAlpha()) { +- auto* pPlatform = +- static_cast(CFX_GEModule::Get()->GetPlatform()); +- if (pPlatform->m_GdiplusExt.IsAvailable() && !pSource->IsCmykImage()) { +- CFX_DIBExtractor temp(pSource); +- RetainPtr pBitmap = temp.GetBitmap(); +- if (!pBitmap) +- return false; +- return pPlatform->m_GdiplusExt.StretchDIBits( +- m_hDC, pBitmap, dest_left, dest_top, dest_width, dest_height, +- pClipRect, FXDIB_ResampleOptions()); +- } +- return UseFoxitStretchEngine(pSource, color, dest_left, dest_top, +- dest_width, dest_height, pClipRect, +- FXDIB_ResampleOptions()); +- } +- CFX_DIBExtractor temp(pSource); +- RetainPtr pBitmap = temp.GetBitmap(); +- if (!pBitmap) +- return false; +- return GDI_StretchDIBits(pBitmap, dest_left, dest_top, dest_width, +- dest_height, FXDIB_ResampleOptions()); +-} +- +-bool CGdiDisplayDriver::StartDIBits(const RetainPtr& pBitmap, +- int bitmap_alpha, +- uint32_t color, +- const CFX_Matrix& matrix, +- const FXDIB_ResampleOptions& options, +- std::unique_ptr* handle, +- BlendMode blend_type) { +- return false; +-} +- +-CFX_WindowsRenderDevice::CFX_WindowsRenderDevice( +- HDC hDC, +- const EncoderIface* pEncoderIface) { +- SetDeviceDriver(pdfium::WrapUnique(CreateDriver(hDC, pEncoderIface))); +-} +- +-CFX_WindowsRenderDevice::~CFX_WindowsRenderDevice() = default; +- +-// static +-RenderDeviceDriverIface* CFX_WindowsRenderDevice::CreateDriver( +- HDC hDC, +- const EncoderIface* pEncoderIface) { +- int device_type = ::GetDeviceCaps(hDC, TECHNOLOGY); +- int obj_type = ::GetObjectType(hDC); +- bool use_printer = device_type == DT_RASPRINTER || +- device_type == DT_PLOTTER || +- device_type == DT_CHARSTREAM || obj_type == OBJ_ENHMETADC; +- +- if (!use_printer) +- return new CGdiDisplayDriver(hDC); +- +- if (g_pdfium_print_mode == WindowsPrintMode::kModeEmf) +- return new CGdiPrinterDriver(hDC); +- +- if (g_pdfium_print_mode == WindowsPrintMode::kModeTextOnly) +- return new CTextOnlyPrinterDriver(hDC); +- +- return new CPSPrinterDriver(hDC, g_pdfium_print_mode, false, pEncoderIface); +-} +diff --git a/core/fxge/win32/fx_win32_dib.cpp b/core/fxge/win32/fx_win32_dib.cpp +deleted file mode 100644 +index 5f9d42c2f..000000000 +--- a/core/fxge/win32/fx_win32_dib.cpp ++++ /dev/null +@@ -1,218 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include +- +-#include "core/fxcrt/fx_system.h" +-#include "core/fxge/cfx_gemodule.h" +-#include "core/fxge/win32/cfx_windowsdib.h" +-#include "core/fxge/win32/win32_int.h" +- +-ByteString CFX_WindowsDIB::GetBitmapInfo( +- const RetainPtr& pBitmap) { +- int len = sizeof(BITMAPINFOHEADER); +- if (pBitmap->GetBPP() == 1 || pBitmap->GetBPP() == 8) +- len += sizeof(DWORD) * (int)(1 << pBitmap->GetBPP()); +- +- ByteString result; +- { +- // Span's lifetime must end before ReleaseBuffer() below. +- pdfium::span cspan = result.GetBuffer(len); +- BITMAPINFOHEADER* pbmih = reinterpret_cast(cspan.data()); +- memset(pbmih, 0, sizeof(BITMAPINFOHEADER)); +- pbmih->biSize = sizeof(BITMAPINFOHEADER); +- pbmih->biBitCount = pBitmap->GetBPP(); +- pbmih->biCompression = BI_RGB; +- pbmih->biHeight = -(int)pBitmap->GetHeight(); +- pbmih->biPlanes = 1; +- pbmih->biWidth = pBitmap->GetWidth(); +- if (pBitmap->GetBPP() == 8) { +- uint32_t* pPalette = (uint32_t*)(pbmih + 1); +- if (pBitmap->GetPalette()) { +- for (int i = 0; i < 256; i++) { +- pPalette[i] = pBitmap->GetPalette()[i]; +- } +- } else { +- for (int i = 0; i < 256; i++) { +- pPalette[i] = i * 0x010101; +- } +- } +- } +- if (pBitmap->GetBPP() == 1) { +- uint32_t* pPalette = (uint32_t*)(pbmih + 1); +- if (pBitmap->GetPalette()) { +- pPalette[0] = pBitmap->GetPalette()[0]; +- pPalette[1] = pBitmap->GetPalette()[1]; +- } else { +- pPalette[0] = 0; +- pPalette[1] = 0xffffff; +- } +- } +- } +- result.ReleaseBuffer(len); +- return result; +-} +- +-RetainPtr FX_WindowsDIB_LoadFromBuf(BITMAPINFO* pbmi, +- LPVOID pData, +- bool bAlpha) { +- int width = pbmi->bmiHeader.biWidth; +- int height = pbmi->bmiHeader.biHeight; +- BOOL bBottomUp = true; +- if (height < 0) { +- height = -height; +- bBottomUp = false; +- } +- int pitch = (width * pbmi->bmiHeader.biBitCount + 31) / 32 * 4; +- auto pBitmap = pdfium::MakeRetain(); +- FXDIB_Format format = bAlpha +- ? (FXDIB_Format)(pbmi->bmiHeader.biBitCount + 0x200) +- : (FXDIB_Format)pbmi->bmiHeader.biBitCount; +- if (!pBitmap->Create(width, height, format)) +- return nullptr; +- +- memcpy(pBitmap->GetBuffer(), pData, pitch * height); +- if (bBottomUp) { +- std::vector temp_buf(pitch); +- int top = 0; +- int bottom = height - 1; +- while (top < bottom) { +- uint8_t* top_ptr = pBitmap->GetBuffer() + top * pitch; +- uint8_t* bottom_ptr = pBitmap->GetBuffer() + bottom * pitch; +- memcpy(temp_buf.data(), top_ptr, pitch); +- memcpy(top_ptr, bottom_ptr, pitch); +- memcpy(bottom_ptr, temp_buf.data(), pitch); +- top++; +- bottom--; +- } +- } +- if (pbmi->bmiHeader.biBitCount == 1) { +- for (int i = 0; i < 2; i++) +- pBitmap->SetPaletteArgb(i, ((uint32_t*)pbmi->bmiColors)[i] | 0xff000000); +- } else if (pbmi->bmiHeader.biBitCount == 8) { +- for (int i = 0; i < 256; i++) +- pBitmap->SetPaletteArgb(i, ((uint32_t*)pbmi->bmiColors)[i] | 0xff000000); +- } +- return pBitmap; +-} +- +-RetainPtr CFX_WindowsDIB::LoadFromBuf(BITMAPINFO* pbmi, +- LPVOID pData) { +- return FX_WindowsDIB_LoadFromBuf(pbmi, pData, false); +-} +- +-HBITMAP CFX_WindowsDIB::GetDDBitmap(const RetainPtr& pBitmap, +- HDC hDC) { +- ByteString info = GetBitmapInfo(pBitmap); +- return CreateDIBitmap(hDC, (BITMAPINFOHEADER*)info.c_str(), CBM_INIT, +- pBitmap->GetBuffer(), (BITMAPINFO*)info.c_str(), +- DIB_RGB_COLORS); +-} +- +-void GetBitmapSize(HBITMAP hBitmap, int& w, int& h) { +- BITMAP bmp; +- GetObject(hBitmap, sizeof bmp, &bmp); +- w = bmp.bmWidth; +- h = bmp.bmHeight; +-} +- +-RetainPtr CFX_WindowsDIB::LoadFromFile(const wchar_t* filename) { +- auto* pPlatform = +- static_cast(CFX_GEModule::Get()->GetPlatform()); +- if (pPlatform->m_GdiplusExt.IsAvailable()) { +- WINDIB_Open_Args_ args; +- args.flags = WINDIB_OPEN_PATHNAME; +- args.path_name = filename; +- return pPlatform->m_GdiplusExt.LoadDIBitmap(args); +- } +- HBITMAP hBitmap = (HBITMAP)LoadImageW(nullptr, (wchar_t*)filename, +- IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); +- if (!hBitmap) { +- return nullptr; +- } +- HDC hDC = CreateCompatibleDC(nullptr); +- int width; +- int height; +- GetBitmapSize(hBitmap, width, height); +- auto pDIBitmap = pdfium::MakeRetain(); +- if (!pDIBitmap->Create(width, height, FXDIB_Rgb)) { +- DeleteDC(hDC); +- return nullptr; +- } +- ByteString info = GetBitmapInfo(pDIBitmap); +- int ret = GetDIBits(hDC, hBitmap, 0, height, pDIBitmap->GetBuffer(), +- (BITMAPINFO*)info.c_str(), DIB_RGB_COLORS); +- DeleteDC(hDC); +- if (!ret) +- return nullptr; +- return pDIBitmap; +-} +- +-RetainPtr CFX_WindowsDIB::LoadFromFile(const char* filename) { +- return LoadFromFile(WideString::FromDefANSI(filename).c_str()); +-} +- +-RetainPtr CFX_WindowsDIB::LoadDIBitmap(WINDIB_Open_Args_ args) { +- auto* pPlatform = +- static_cast(CFX_GEModule::Get()->GetPlatform()); +- if (pPlatform->m_GdiplusExt.IsAvailable()) { +- return pPlatform->m_GdiplusExt.LoadDIBitmap(args); +- } +- if (args.flags == WINDIB_OPEN_MEMORY) { +- return nullptr; +- } +- HBITMAP hBitmap = (HBITMAP)LoadImageW(nullptr, (wchar_t*)args.path_name, +- IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); +- if (!hBitmap) { +- return nullptr; +- } +- HDC hDC = CreateCompatibleDC(nullptr); +- int width, height; +- GetBitmapSize(hBitmap, width, height); +- auto pDIBitmap = pdfium::MakeRetain(); +- if (!pDIBitmap->Create(width, height, FXDIB_Rgb)) { +- DeleteDC(hDC); +- return nullptr; +- } +- ByteString info = GetBitmapInfo(pDIBitmap); +- int ret = GetDIBits(hDC, hBitmap, 0, height, pDIBitmap->GetBuffer(), +- (BITMAPINFO*)info.c_str(), DIB_RGB_COLORS); +- DeleteDC(hDC); +- if (!ret) +- return nullptr; +- return pDIBitmap; +-} +- +-CFX_WindowsDIB::CFX_WindowsDIB(HDC hDC, int width, int height) { +- Create(width, height, FXDIB_Rgb, (uint8_t*)1, 0); +- BITMAPINFOHEADER bmih; +- memset(&bmih, 0, sizeof bmih); +- bmih.biSize = sizeof bmih; +- bmih.biBitCount = 24; +- bmih.biHeight = -height; +- bmih.biPlanes = 1; +- bmih.biWidth = width; +- LPVOID pData = nullptr; +- m_hBitmap = CreateDIBSection(hDC, (BITMAPINFO*)&bmih, DIB_RGB_COLORS, &pData, +- nullptr, 0); +- m_pBuffer.Reset(static_cast(pData)); +- m_hMemDC = CreateCompatibleDC(hDC); +- m_hOldBitmap = (HBITMAP)SelectObject(m_hMemDC, m_hBitmap); +-} +- +-CFX_WindowsDIB::~CFX_WindowsDIB() { +- SelectObject(m_hMemDC, m_hOldBitmap); +- DeleteDC(m_hMemDC); +- DeleteObject(m_hBitmap); +-} +- +-void CFX_WindowsDIB::LoadFromDevice(HDC hDC, int left, int top) { +- ::BitBlt(m_hMemDC, 0, 0, m_Width, m_Height, hDC, left, top, SRCCOPY); +-} +- +-void CFX_WindowsDIB::SetToDevice(HDC hDC, int left, int top) { +- ::BitBlt(hDC, left, top, m_Width, m_Height, m_hMemDC, 0, 0, SRCCOPY); +-} +diff --git a/core/fxge/win32/fx_win32_print.cpp b/core/fxge/win32/fx_win32_print.cpp +deleted file mode 100644 +index 9ae092aab..000000000 +--- a/core/fxge/win32/fx_win32_print.cpp ++++ /dev/null +@@ -1,673 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include +- +-#include +-#include +-#include +- +-#include "core/fxcrt/fx_system.h" +-#include "core/fxge/cfx_font.h" +-#include "core/fxge/cfx_windowsrenderdevice.h" +-#include "core/fxge/dib/cfx_dibextractor.h" +-#include "core/fxge/dib/cfx_dibitmap.h" +-#include "core/fxge/dib/cfx_imagerenderer.h" +-#include "core/fxge/dib/cstretchengine.h" +-#include "core/fxge/fx_freetype.h" +-#include "core/fxge/text_char_pos.h" +-#include "core/fxge/win32/cpsoutput.h" +-#include "core/fxge/win32/win32_int.h" +-#include "third_party/base/span.h" +- +-#if defined(PDFIUM_PRINT_TEXT_WITH_GDI) +-namespace { +- +-class ScopedState { +- public: +- ScopedState(HDC hDC, HFONT hFont) +- : m_hDC(hDC), +- m_iState(SaveDC(m_hDC)), +- m_hFont(SelectObject(m_hDC, hFont)) {} +- +- ScopedState(const ScopedState&) = delete; +- ScopedState& operator=(const ScopedState&) = delete; +- +- ~ScopedState() { +- HGDIOBJ hFont = SelectObject(m_hDC, m_hFont); +- DeleteObject(hFont); +- RestoreDC(m_hDC, m_iState); +- } +- +- private: +- const HDC m_hDC; +- const int m_iState; +- const HGDIOBJ m_hFont; +-}; +- +-} // namespace +- +-bool g_pdfium_print_text_with_gdi = false; +- +-PDFiumEnsureTypefaceCharactersAccessible g_pdfium_typeface_accessible_func = +- nullptr; +-#endif +- +-CGdiPrinterDriver::CGdiPrinterDriver(HDC hDC) +- : CGdiDeviceDriver(hDC, DeviceType::kPrinter), +- m_HorzSize(::GetDeviceCaps(m_hDC, HORZSIZE)), +- m_VertSize(::GetDeviceCaps(m_hDC, VERTSIZE)) {} +- +-CGdiPrinterDriver::~CGdiPrinterDriver() = default; +- +-int CGdiPrinterDriver::GetDeviceCaps(int caps_id) const { +- if (caps_id == FXDC_HORZ_SIZE) +- return m_HorzSize; +- if (caps_id == FXDC_VERT_SIZE) +- return m_VertSize; +- return CGdiDeviceDriver::GetDeviceCaps(caps_id); +-} +- +-bool CGdiPrinterDriver::SetDIBits(const RetainPtr& pSource, +- uint32_t color, +- const FX_RECT& src_rect, +- int left, +- int top, +- BlendMode blend_type) { +- if (pSource->IsAlphaMask()) { +- FX_RECT clip_rect(left, top, left + src_rect.Width(), +- top + src_rect.Height()); +- return StretchDIBits(pSource, color, left - src_rect.left, +- top - src_rect.top, pSource->GetWidth(), +- pSource->GetHeight(), &clip_rect, +- FXDIB_ResampleOptions(), BlendMode::kNormal); +- } +- ASSERT(pSource); +- ASSERT(!pSource->IsAlphaMask()); +- ASSERT(blend_type == BlendMode::kNormal); +- if (pSource->HasAlpha()) +- return false; +- +- CFX_DIBExtractor temp(pSource); +- RetainPtr pBitmap = temp.GetBitmap(); +- if (!pBitmap) +- return false; +- +- return GDI_SetDIBits(pBitmap, src_rect, left, top); +-} +- +-bool CGdiPrinterDriver::StretchDIBits(const RetainPtr& pSource, +- uint32_t color, +- int dest_left, +- int dest_top, +- int dest_width, +- int dest_height, +- const FX_RECT* pClipRect, +- const FXDIB_ResampleOptions& options, +- BlendMode blend_type) { +- if (pSource->IsAlphaMask()) { +- int alpha = FXARGB_A(color); +- if (pSource->GetBPP() != 1 || alpha != 255) +- return false; +- +- if (dest_width < 0 || dest_height < 0) { +- RetainPtr pFlipped = +- pSource->FlipImage(dest_width < 0, dest_height < 0); +- if (!pFlipped) +- return false; +- +- if (dest_width < 0) +- dest_left += dest_width; +- if (dest_height < 0) +- dest_top += dest_height; +- +- return GDI_StretchBitMask(pFlipped, dest_left, dest_top, abs(dest_width), +- abs(dest_height), color); +- } +- +- CFX_DIBExtractor temp(pSource); +- RetainPtr pBitmap = temp.GetBitmap(); +- if (!pBitmap) +- return false; +- return GDI_StretchBitMask(pBitmap, dest_left, dest_top, dest_width, +- dest_height, color); +- } +- +- if (pSource->HasAlpha()) +- return false; +- +- if (dest_width < 0 || dest_height < 0) { +- RetainPtr pFlipped = +- pSource->FlipImage(dest_width < 0, dest_height < 0); +- if (!pFlipped) +- return false; +- +- if (dest_width < 0) +- dest_left += dest_width; +- if (dest_height < 0) +- dest_top += dest_height; +- +- return GDI_StretchDIBits(pFlipped, dest_left, dest_top, abs(dest_width), +- abs(dest_height), options); +- } +- +- CFX_DIBExtractor temp(pSource); +- RetainPtr pBitmap = temp.GetBitmap(); +- if (!pBitmap) +- return false; +- return GDI_StretchDIBits(pBitmap, dest_left, dest_top, dest_width, +- dest_height, options); +-} +- +-bool CGdiPrinterDriver::StartDIBits(const RetainPtr& pSource, +- int bitmap_alpha, +- uint32_t color, +- const CFX_Matrix& matrix, +- const FXDIB_ResampleOptions& options, +- std::unique_ptr* handle, +- BlendMode blend_type) { +- if (bitmap_alpha < 255 || pSource->HasAlpha() || +- (pSource->IsAlphaMask() && (pSource->GetBPP() != 1))) { +- return false; +- } +- CFX_FloatRect unit_rect = matrix.GetUnitRect(); +- FX_RECT full_rect = unit_rect.GetOuterRect(); +- if (fabs(matrix.b) < 0.5f && matrix.a != 0 && fabs(matrix.c) < 0.5f && +- matrix.d != 0) { +- bool bFlipX = matrix.a < 0; +- bool bFlipY = matrix.d > 0; +- return StretchDIBits(pSource, color, +- bFlipX ? full_rect.right : full_rect.left, +- bFlipY ? full_rect.bottom : full_rect.top, +- bFlipX ? -full_rect.Width() : full_rect.Width(), +- bFlipY ? -full_rect.Height() : full_rect.Height(), +- nullptr, FXDIB_ResampleOptions(), blend_type); +- } +- if (fabs(matrix.a) >= 0.5f || fabs(matrix.d) >= 0.5f) +- return false; +- +- RetainPtr pTransformed = +- pSource->SwapXY(matrix.c > 0, matrix.b < 0); +- if (!pTransformed) +- return false; +- +- return StretchDIBits(pTransformed, color, full_rect.left, full_rect.top, +- full_rect.Width(), full_rect.Height(), nullptr, +- FXDIB_ResampleOptions(), blend_type); +-} +- +-bool CGdiPrinterDriver::DrawDeviceText(int nChars, +- const TextCharPos* pCharPos, +- CFX_Font* pFont, +- const CFX_Matrix& mtObject2Device, +- float font_size, +- uint32_t color) { +-#if defined(PDFIUM_PRINT_TEXT_WITH_GDI) +- if (!g_pdfium_print_text_with_gdi) +- return false; +- +- if (nChars < 1 || !pFont || !pFont->IsEmbedded() || !pFont->IsTTFont()) +- return false; +- +- // Scale factor used to minimize the kerning problems caused by rounding +- // errors below. Value chosen based on the title of https://crbug.com/18383 +- const double kScaleFactor = 10; +- +- // Font +- // +- // Note that |pFont| has the actual font to render with embedded within, but +- // but unfortunately AddFontMemResourceEx() does not seem to cooperate. +- // Loading font data to memory seems to work, but then enumerating the fonts +- // fails to find it. This requires more investigation. In the meanwhile, +- // assume the printing is happening on the machine that generated the PDF, so +- // the embedded font, if not a web font, is available through GDI anyway. +- // TODO(thestig): Figure out why AddFontMemResourceEx() does not work. +- // Generalize this method to work for all PDFs with embedded fonts. +- // In sandboxed environments, font loading may not work at all, so this may be +- // the best possible effort. +- LOGFONT lf = {}; +- lf.lfHeight = -font_size * kScaleFactor; +- lf.lfWeight = pFont->IsBold() ? FW_BOLD : FW_NORMAL; +- lf.lfItalic = pFont->IsItalic(); +- lf.lfCharSet = DEFAULT_CHARSET; +- +- const WideString wsName = +- WideString::FromUTF8(pFont->GetFaceName().AsStringView()); +- size_t iNameLen = +- std::min(wsName.GetLength(), static_cast(LF_FACESIZE - 1)); +- memcpy(lf.lfFaceName, wsName.c_str(), sizeof(lf.lfFaceName[0]) * iNameLen); +- lf.lfFaceName[iNameLen] = 0; +- +- HFONT hFont = CreateFontIndirect(&lf); +- if (!hFont) +- return false; +- +- ScopedState state(m_hDC, hFont); +- size_t nTextMetricSize = GetOutlineTextMetrics(m_hDC, 0, nullptr); +- if (nTextMetricSize == 0) { +- // Give up and fail if there is no way to get the font to try again. +- if (!g_pdfium_typeface_accessible_func) +- return false; +- +- // Try to get the font. Any letter will do. +- g_pdfium_typeface_accessible_func(&lf, L"A", 1); +- nTextMetricSize = GetOutlineTextMetrics(m_hDC, 0, nullptr); +- if (nTextMetricSize == 0) +- return false; +- } +- +- std::vector buf(nTextMetricSize); +- OUTLINETEXTMETRIC* pTextMetric = +- reinterpret_cast(buf.data()); +- if (GetOutlineTextMetrics(m_hDC, nTextMetricSize, pTextMetric) == 0) +- return false; +- +- // If the selected font is not the requested font, then bail out. This can +- // happen with web fonts, for example. +- wchar_t* wsSelectedName = reinterpret_cast( +- buf.data() + reinterpret_cast(pTextMetric->otmpFaceName)); +- if (wsName != wsSelectedName) +- return false; +- +- // Transforms +- SetGraphicsMode(m_hDC, GM_ADVANCED); +- XFORM xform; +- xform.eM11 = mtObject2Device.a / kScaleFactor; +- xform.eM12 = mtObject2Device.b / kScaleFactor; +- xform.eM21 = -mtObject2Device.c / kScaleFactor; +- xform.eM22 = -mtObject2Device.d / kScaleFactor; +- xform.eDx = mtObject2Device.e; +- xform.eDy = mtObject2Device.f; +- ModifyWorldTransform(m_hDC, &xform, MWT_LEFTMULTIPLY); +- +- // Color +- FX_COLORREF colorref = ArgbToColorRef(color); +- SetTextColor(m_hDC, colorref); +- SetBkMode(m_hDC, TRANSPARENT); +- +- // Text +- WideString wsText; +- std::vector spacing(nChars); +- float fPreviousOriginX = 0; +- for (int i = 0; i < nChars; ++i) { +- // Only works with PDFs from Skia's PDF generator. Cannot handle arbitrary +- // values from PDFs. +- const TextCharPos& charpos = pCharPos[i]; +- ASSERT(charpos.m_AdjustMatrix[0] == 0); +- ASSERT(charpos.m_AdjustMatrix[1] == 0); +- ASSERT(charpos.m_AdjustMatrix[2] == 0); +- ASSERT(charpos.m_AdjustMatrix[3] == 0); +- ASSERT(charpos.m_Origin.y == 0); +- +- // Round the spacing to the nearest integer, but keep track of the rounding +- // error for calculating the next spacing value. +- float fOriginX = charpos.m_Origin.x * kScaleFactor; +- float fPixelSpacing = fOriginX - fPreviousOriginX; +- spacing[i] = FXSYS_roundf(fPixelSpacing); +- fPreviousOriginX = fOriginX - (fPixelSpacing - spacing[i]); +- +- wsText += charpos.m_GlyphIndex; +- } +- +- // Draw +- SetTextAlign(m_hDC, TA_LEFT | TA_BASELINE); +- if (ExtTextOutW(m_hDC, 0, 0, ETO_GLYPH_INDEX, nullptr, wsText.c_str(), nChars, +- nChars > 1 ? &spacing[1] : nullptr)) { +- return true; +- } +- +- // Give up and fail if there is no way to get the font to try again. +- if (!g_pdfium_typeface_accessible_func) +- return false; +- +- // Try to get the font and draw again. +- g_pdfium_typeface_accessible_func(&lf, wsText.c_str(), nChars); +- return !!ExtTextOutW(m_hDC, 0, 0, ETO_GLYPH_INDEX, nullptr, wsText.c_str(), +- nChars, nChars > 1 ? &spacing[1] : nullptr); +-#else +- return false; +-#endif +-} +- +-CPSPrinterDriver::CPSPrinterDriver(HDC hDC, +- WindowsPrintMode mode, +- bool bCmykOutput, +- const EncoderIface* pEncoderIface) +- : m_hDC(hDC), m_bCmykOutput(bCmykOutput), m_PSRenderer(pEncoderIface) { +- // |mode| should be PostScript. +- ASSERT(mode == WindowsPrintMode::kModePostScript2 || +- mode == WindowsPrintMode::kModePostScript3 || +- mode == WindowsPrintMode::kModePostScript2PassThrough || +- mode == WindowsPrintMode::kModePostScript3PassThrough); +- int pslevel = (mode == WindowsPrintMode::kModePostScript2 || +- mode == WindowsPrintMode::kModePostScript2PassThrough) +- ? 2 +- : 3; +- CPSOutput::OutputMode output_mode = +- (mode == WindowsPrintMode::kModePostScript2 || +- mode == WindowsPrintMode::kModePostScript3) +- ? CPSOutput::OutputMode::kGdiComment +- : CPSOutput::OutputMode::kExtEscape; +- +- m_HorzSize = ::GetDeviceCaps(m_hDC, HORZSIZE); +- m_VertSize = ::GetDeviceCaps(m_hDC, VERTSIZE); +- m_Width = ::GetDeviceCaps(m_hDC, HORZRES); +- m_Height = ::GetDeviceCaps(m_hDC, VERTRES); +- m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL); +- +- m_PSRenderer.Init(pdfium::MakeRetain(m_hDC, output_mode), pslevel, +- m_Width, m_Height, bCmykOutput); +- HRGN hRgn = ::CreateRectRgn(0, 0, 1, 1); +- if (::GetClipRgn(m_hDC, hRgn) == 1) { +- DWORD dwCount = ::GetRegionData(hRgn, 0, nullptr); +- if (dwCount) { +- std::vector buffer(dwCount); +- RGNDATA* pData = reinterpret_cast(buffer.data()); +- if (::GetRegionData(hRgn, dwCount, pData)) { +- CFX_PathData path; +- for (uint32_t i = 0; i < pData->rdh.nCount; i++) { +- RECT* pRect = +- reinterpret_cast(pData->Buffer + pData->rdh.nRgnSize * i); +- path.AppendRect(static_cast(pRect->left), +- static_cast(pRect->bottom), +- static_cast(pRect->right), +- static_cast(pRect->top)); +- } +- m_PSRenderer.SetClip_PathFill(&path, nullptr, FXFILL_WINDING); +- } +- } +- } +- ::DeleteObject(hRgn); +-} +- +-CPSPrinterDriver::~CPSPrinterDriver() { +- EndRendering(); +-} +- +-DeviceType CPSPrinterDriver::GetDeviceType() const { +- return DeviceType::kPrinter; +-} +- +-int CPSPrinterDriver::GetDeviceCaps(int caps_id) const { +- switch (caps_id) { +- case FXDC_PIXEL_WIDTH: +- return m_Width; +- case FXDC_PIXEL_HEIGHT: +- return m_Height; +- case FXDC_BITS_PIXEL: +- return m_nBitsPerPixel; +- case FXDC_RENDER_CAPS: +- return m_bCmykOutput ? FXRC_BIT_MASK | FXRC_CMYK_OUTPUT : FXRC_BIT_MASK; +- case FXDC_HORZ_SIZE: +- return m_HorzSize; +- case FXDC_VERT_SIZE: +- return m_VertSize; +- default: +- NOTREACHED(); +- return 0; +- } +-} +- +-bool CPSPrinterDriver::StartRendering() { +- return m_PSRenderer.StartRendering(); +-} +- +-void CPSPrinterDriver::EndRendering() { +- m_PSRenderer.EndRendering(); +-} +- +-void CPSPrinterDriver::SaveState() { +- m_PSRenderer.SaveState(); +-} +- +-void CPSPrinterDriver::RestoreState(bool bKeepSaved) { +- m_PSRenderer.RestoreState(bKeepSaved); +-} +- +-bool CPSPrinterDriver::SetClip_PathFill(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- int fill_mode) { +- m_PSRenderer.SetClip_PathFill(pPathData, pObject2Device, fill_mode); +- return true; +-} +- +-bool CPSPrinterDriver::SetClip_PathStroke( +- const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- const CFX_GraphStateData* pGraphState) { +- m_PSRenderer.SetClip_PathStroke(pPathData, pObject2Device, pGraphState); +- return true; +-} +- +-bool CPSPrinterDriver::DrawPath(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- const CFX_GraphStateData* pGraphState, +- FX_ARGB fill_color, +- FX_ARGB stroke_color, +- int fill_mode, +- BlendMode blend_type) { +- if (blend_type != BlendMode::kNormal) +- return false; +- return m_PSRenderer.DrawPath(pPathData, pObject2Device, pGraphState, +- fill_color, stroke_color, fill_mode & 3); +-} +- +-bool CPSPrinterDriver::GetClipBox(FX_RECT* pRect) { +- *pRect = m_PSRenderer.GetClipBox(); +- return true; +-} +- +-bool CPSPrinterDriver::SetDIBits(const RetainPtr& pBitmap, +- uint32_t color, +- const FX_RECT& src_rect, +- int left, +- int top, +- BlendMode blend_type) { +- if (blend_type != BlendMode::kNormal) +- return false; +- return m_PSRenderer.SetDIBits(pBitmap, color, left, top); +-} +- +-bool CPSPrinterDriver::StretchDIBits(const RetainPtr& pBitmap, +- uint32_t color, +- int dest_left, +- int dest_top, +- int dest_width, +- int dest_height, +- const FX_RECT* pClipRect, +- const FXDIB_ResampleOptions& options, +- BlendMode blend_type) { +- if (blend_type != BlendMode::kNormal) +- return false; +- return m_PSRenderer.StretchDIBits(pBitmap, color, dest_left, dest_top, +- dest_width, dest_height, options); +-} +- +-bool CPSPrinterDriver::StartDIBits(const RetainPtr& pBitmap, +- int bitmap_alpha, +- uint32_t color, +- const CFX_Matrix& matrix, +- const FXDIB_ResampleOptions& options, +- std::unique_ptr* handle, +- BlendMode blend_type) { +- if (blend_type != BlendMode::kNormal) +- return false; +- +- if (bitmap_alpha < 255) +- return false; +- +- *handle = nullptr; +- return m_PSRenderer.DrawDIBits(pBitmap, color, matrix, options); +-} +- +-bool CPSPrinterDriver::DrawDeviceText(int nChars, +- const TextCharPos* pCharPos, +- CFX_Font* pFont, +- const CFX_Matrix& mtObject2Device, +- float font_size, +- uint32_t color) { +- return m_PSRenderer.DrawText(nChars, pCharPos, pFont, mtObject2Device, +- font_size, color); +-} +- +-CTextOnlyPrinterDriver::CTextOnlyPrinterDriver(HDC hDC) +- : m_hDC(hDC), +- m_Width(INT_MAX), +- m_Height(INT_MAX), +- m_HorzSize(INT_MAX), +- m_VertSize(INT_MAX), +- m_OriginY(0.0f), +- m_SetOrigin(false) { +- m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL); +-} +- +-CTextOnlyPrinterDriver::~CTextOnlyPrinterDriver() { +- EndRendering(); +-} +- +-DeviceType CTextOnlyPrinterDriver::GetDeviceType() const { +- return DeviceType::kPrinter; +-} +- +-int CTextOnlyPrinterDriver::GetDeviceCaps(int caps_id) const { +- switch (caps_id) { +- case FXDC_PIXEL_WIDTH: +- return m_Width; +- case FXDC_PIXEL_HEIGHT: +- return m_Height; +- case FXDC_BITS_PIXEL: +- return m_nBitsPerPixel; +- case FXDC_RENDER_CAPS: +- return 0; +- case FXDC_HORZ_SIZE: +- return m_HorzSize; +- case FXDC_VERT_SIZE: +- return m_VertSize; +- default: +- NOTREACHED(); +- return 0; +- } +-} +- +-bool CTextOnlyPrinterDriver::SetClip_PathFill(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- int fill_mode) { +- return true; +-} +- +-bool CTextOnlyPrinterDriver::SetClip_PathStroke( +- const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- const CFX_GraphStateData* pGraphState) { +- return false; +-} +- +-bool CTextOnlyPrinterDriver::DrawPath(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- const CFX_GraphStateData* pGraphState, +- uint32_t fill_color, +- uint32_t stroke_color, +- int fill_mode, +- BlendMode blend_type) { +- return false; +-} +- +-bool CTextOnlyPrinterDriver::SetDIBits(const RetainPtr& pBitmap, +- uint32_t color, +- const FX_RECT& src_rect, +- int left, +- int top, +- BlendMode blend_type) { +- return false; +-} +- +-bool CTextOnlyPrinterDriver::GetClipBox(FX_RECT* pRect) { +- pRect->left = 0; +- pRect->right = m_Width; +- pRect->top = 0; +- pRect->bottom = m_Height; +- return true; +-} +- +-bool CTextOnlyPrinterDriver::StretchDIBits( +- const RetainPtr& pBitmap, +- uint32_t color, +- int dest_left, +- int dest_top, +- int dest_width, +- int dest_height, +- const FX_RECT* pClipRect, +- const FXDIB_ResampleOptions& options, +- BlendMode blend_type) { +- return false; +-} +- +-bool CTextOnlyPrinterDriver::StartDIBits( +- const RetainPtr& pBitmap, +- int bitmap_alpha, +- uint32_t color, +- const CFX_Matrix& matrix, +- const FXDIB_ResampleOptions& options, +- std::unique_ptr* handle, +- BlendMode blend_type) { +- return false; +-} +- +-bool CTextOnlyPrinterDriver::DrawDeviceText(int nChars, +- const TextCharPos* pCharPos, +- CFX_Font* pFont, +- const CFX_Matrix& mtObject2Device, +- float font_size, +- uint32_t color) { +- if (g_pdfium_print_mode != 1) +- return false; +- if (nChars < 1 || !pFont || !pFont->IsEmbedded() || !pFont->IsTTFont()) +- return false; +- +- // Scale factor used to minimize the kerning problems caused by rounding +- // errors below. Value chosen based on the title of https://crbug.com/18383 +- const double kScaleFactor = 10; +- +- // Detect new lines and add clrf characters (since this is Windows only). +- // These characters are removed by SkPDF, but the new line information is +- // preserved in the text location. clrf characters seem to be ignored by +- // label printers that use this driver. +- WideString wsText; +- size_t len = nChars; +- float fOffsetY = mtObject2Device.f * kScaleFactor; +- if (m_SetOrigin && FXSYS_roundf(m_OriginY) != FXSYS_roundf(fOffsetY)) { +- wsText += L"\r\n"; +- len += 2; +- } +- wsText.Reserve(len); +- m_OriginY = fOffsetY; +- m_SetOrigin = true; +- +- // Text +- for (int i = 0; i < nChars; ++i) { +- // Only works with PDFs from Skia's PDF generator. Cannot handle arbitrary +- // values from PDFs. +- const TextCharPos& charpos = pCharPos[i]; +- ASSERT(charpos.m_AdjustMatrix[0] == 0); +- ASSERT(charpos.m_AdjustMatrix[1] == 0); +- ASSERT(charpos.m_AdjustMatrix[2] == 0); +- ASSERT(charpos.m_AdjustMatrix[3] == 0); +- ASSERT(charpos.m_Origin.y == 0); +- +- wsText += charpos.m_Unicode; +- } +- ByteString text = wsText.ToDefANSI(); +- auto text_span = text.span(); +- while (!text_span.empty()) { +- uint8_t buffer[1026]; +- size_t send_len = std::min(text_span.size(), 1024); +- *(reinterpret_cast(buffer)) = static_cast(send_len); +- memcpy(buffer + 2, text_span.data(), send_len); +- ::GdiComment(m_hDC, send_len + 2, buffer); +- text_span = text_span.subspan(send_len); +- } +- return true; +-} +diff --git a/core/fxge/win32/win32_int.h b/core/fxge/win32/win32_int.h +deleted file mode 100644 +index 7f299ef92..000000000 +--- a/core/fxge/win32/win32_int.h ++++ /dev/null +@@ -1,353 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef CORE_FXGE_WIN32_WIN32_INT_H_ +-#define CORE_FXGE_WIN32_WIN32_INT_H_ +- +-#include +- +-#include +-#include +- +-#include "core/fxcrt/retain_ptr.h" +-#include "core/fxge/cfx_gemodule.h" +-#include "core/fxge/cfx_pathdata.h" +-#include "core/fxge/cfx_windowsrenderdevice.h" +-#include "core/fxge/renderdevicedriver_iface.h" +-#include "core/fxge/win32/cfx_psrenderer.h" +-#include "core/fxge/win32/cpsoutput.h" +-#include "third_party/base/optional.h" +- +-class CFX_ImageRenderer; +-class TextCharPos; +-struct WINDIB_Open_Args_; +- +-RetainPtr FX_WindowsDIB_LoadFromBuf(BITMAPINFO* pbmi, +- LPVOID pData, +- bool bAlpha); +-class CGdiplusExt { +- public: +- CGdiplusExt(); +- ~CGdiplusExt(); +- +- void Load(); +- bool IsAvailable() { return !!m_hModule; } +- bool StretchDIBits(HDC hDC, +- const RetainPtr& pBitmap, +- int dest_left, +- int dest_top, +- int dest_width, +- int dest_height, +- const FX_RECT* pClipRect, +- const FXDIB_ResampleOptions& options); +- bool DrawPath(HDC hDC, +- const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- const CFX_GraphStateData* pGraphState, +- uint32_t fill_argb, +- uint32_t stroke_argb, +- int fill_mode); +- +- RetainPtr LoadDIBitmap(WINDIB_Open_Args_ args); +- +- std::vector m_Functions; +- +- protected: +- HMODULE m_hModule = nullptr; +- HMODULE m_GdiModule = nullptr; +-}; +- +-class CWin32Platform : public CFX_GEModule::PlatformIface { +- public: +- CWin32Platform(); +- ~CWin32Platform() override; +- +- // CFX_GEModule::PlatformIface: +- void Init() override; +- +- bool m_bHalfTone = false; +- CGdiplusExt m_GdiplusExt; +-}; +- +-class CGdiDeviceDriver : public RenderDeviceDriverIface { +- protected: +- CGdiDeviceDriver(HDC hDC, DeviceType device_type); +- ~CGdiDeviceDriver() override; +- +- // RenderDeviceDriverIface: +- DeviceType GetDeviceType() const override; +- int GetDeviceCaps(int caps_id) const override; +- void SaveState() override; +- void RestoreState(bool bKeepSaved) override; +- void SetBaseClip(const FX_RECT& rect) override; +- bool SetClip_PathFill(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- int fill_mode) override; +- bool SetClip_PathStroke(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- const CFX_GraphStateData* pGraphState) override; +- bool DrawPath(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- const CFX_GraphStateData* pGraphState, +- uint32_t fill_color, +- uint32_t stroke_color, +- int fill_mode, +- BlendMode blend_type) override; +- bool FillRectWithBlend(const FX_RECT& rect, +- uint32_t fill_color, +- BlendMode blend_type) override; +- bool DrawCosmeticLine(const CFX_PointF& ptMoveTo, +- const CFX_PointF& ptLineTo, +- uint32_t color, +- BlendMode blend_type) override; +- bool GetClipBox(FX_RECT* pRect) override; +- +- void DrawLine(float x1, float y1, float x2, float y2); +- +- bool GDI_SetDIBits(const RetainPtr& pBitmap, +- const FX_RECT& src_rect, +- int left, +- int top); +- bool GDI_StretchDIBits(const RetainPtr& pBitmap, +- int dest_left, +- int dest_top, +- int dest_width, +- int dest_height, +- const FXDIB_ResampleOptions& options); +- bool GDI_StretchBitMask(const RetainPtr& pBitmap, +- int dest_left, +- int dest_top, +- int dest_width, +- int dest_height, +- uint32_t bitmap_color); +- +- const HDC m_hDC; +- bool m_bMetafileDCType; +- int m_Width; +- int m_Height; +- int m_nBitsPerPixel; +- const DeviceType m_DeviceType; +- int m_RenderCaps; +- pdfium::Optional m_BaseClipBox; +-}; +- +-class CGdiDisplayDriver final : public CGdiDeviceDriver { +- public: +- explicit CGdiDisplayDriver(HDC hDC); +- ~CGdiDisplayDriver() override; +- +- private: +- // CGdiDisplayDriver: +- int GetDeviceCaps(int caps_id) const override; +- bool GetDIBits(const RetainPtr& pBitmap, +- int left, +- int top) override; +- bool SetDIBits(const RetainPtr& pBitmap, +- uint32_t color, +- const FX_RECT& src_rect, +- int left, +- int top, +- BlendMode blend_type) override; +- bool StretchDIBits(const RetainPtr& pBitmap, +- uint32_t color, +- int dest_left, +- int dest_top, +- int dest_width, +- int dest_height, +- const FX_RECT* pClipRect, +- const FXDIB_ResampleOptions& options, +- BlendMode blend_type) override; +- bool StartDIBits(const RetainPtr& pBitmap, +- int bitmap_alpha, +- uint32_t color, +- const CFX_Matrix& matrix, +- const FXDIB_ResampleOptions& options, +- std::unique_ptr* handle, +- BlendMode blend_type) override; +- bool UseFoxitStretchEngine(const RetainPtr& pSource, +- uint32_t color, +- int dest_left, +- int dest_top, +- int dest_width, +- int dest_height, +- const FX_RECT* pClipRect, +- const FXDIB_ResampleOptions& options); +-}; +- +-class CGdiPrinterDriver final : public CGdiDeviceDriver { +- public: +- explicit CGdiPrinterDriver(HDC hDC); +- ~CGdiPrinterDriver() override; +- +- private: +- // CGdiPrinterDriver: +- int GetDeviceCaps(int caps_id) const override; +- bool SetDIBits(const RetainPtr& pBitmap, +- uint32_t color, +- const FX_RECT& src_rect, +- int left, +- int top, +- BlendMode blend_type) override; +- bool StretchDIBits(const RetainPtr& pBitmap, +- uint32_t color, +- int dest_left, +- int dest_top, +- int dest_width, +- int dest_height, +- const FX_RECT* pClipRect, +- const FXDIB_ResampleOptions& options, +- BlendMode blend_type) override; +- bool StartDIBits(const RetainPtr& pBitmap, +- int bitmap_alpha, +- uint32_t color, +- const CFX_Matrix& matrix, +- const FXDIB_ResampleOptions& options, +- std::unique_ptr* handle, +- BlendMode blend_type) override; +- bool DrawDeviceText(int nChars, +- const TextCharPos* pCharPos, +- CFX_Font* pFont, +- const CFX_Matrix& mtObject2Device, +- float font_size, +- uint32_t color) override; +- +- const int m_HorzSize; +- const int m_VertSize; +-}; +- +-class CPSPrinterDriver final : public RenderDeviceDriverIface { +- public: +- CPSPrinterDriver(HDC hDC, +- WindowsPrintMode mode, +- bool bCmykOutput, +- const EncoderIface* pEncoderIface); +- ~CPSPrinterDriver() override; +- +- private: +- // RenderDeviceDriverIface: +- DeviceType GetDeviceType() const override; +- int GetDeviceCaps(int caps_id) const override; +- bool StartRendering() override; +- void EndRendering() override; +- void SaveState() override; +- void RestoreState(bool bKeepSaved) override; +- bool SetClip_PathFill(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- int fill_mode) override; +- bool SetClip_PathStroke(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- const CFX_GraphStateData* pGraphState) override; +- bool DrawPath(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- const CFX_GraphStateData* pGraphState, +- uint32_t fill_color, +- uint32_t stroke_color, +- int fill_mode, +- BlendMode blend_type) override; +- bool GetClipBox(FX_RECT* pRect) override; +- bool SetDIBits(const RetainPtr& pBitmap, +- uint32_t color, +- const FX_RECT& src_rect, +- int left, +- int top, +- BlendMode blend_type) override; +- bool StretchDIBits(const RetainPtr& pBitmap, +- uint32_t color, +- int dest_left, +- int dest_top, +- int dest_width, +- int dest_height, +- const FX_RECT* pClipRect, +- const FXDIB_ResampleOptions& options, +- BlendMode blend_type) override; +- bool StartDIBits(const RetainPtr& pBitmap, +- int bitmap_alpha, +- uint32_t color, +- const CFX_Matrix& matrix, +- const FXDIB_ResampleOptions& options, +- std::unique_ptr* handle, +- BlendMode blend_type) override; +- bool DrawDeviceText(int nChars, +- const TextCharPos* pCharPos, +- CFX_Font* pFont, +- const CFX_Matrix& mtObject2Device, +- float font_size, +- uint32_t color) override; +- +- HDC m_hDC; +- const bool m_bCmykOutput; +- int m_Width; +- int m_Height; +- int m_nBitsPerPixel; +- int m_HorzSize; +- int m_VertSize; +- CFX_PSRenderer m_PSRenderer; +-}; +- +-class CTextOnlyPrinterDriver final : public RenderDeviceDriverIface { +- public: +- explicit CTextOnlyPrinterDriver(HDC hDC); +- ~CTextOnlyPrinterDriver() override; +- +- private: +- // RenderDeviceDriverIface: +- DeviceType GetDeviceType() const override; +- int GetDeviceCaps(int caps_id) const override; +- void SaveState() override {} +- void RestoreState(bool bKeepSaved) override {} +- bool SetClip_PathFill(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- int fill_mode) override; +- bool SetClip_PathStroke(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- const CFX_GraphStateData* pGraphState) override; +- bool DrawPath(const CFX_PathData* pPathData, +- const CFX_Matrix* pObject2Device, +- const CFX_GraphStateData* pGraphState, +- uint32_t fill_color, +- uint32_t stroke_color, +- int fill_mode, +- BlendMode blend_type) override; +- bool GetClipBox(FX_RECT* pRect) override; +- bool SetDIBits(const RetainPtr& pBitmap, +- uint32_t color, +- const FX_RECT& src_rect, +- int left, +- int top, +- BlendMode blend_type) override; +- bool StretchDIBits(const RetainPtr& pBitmap, +- uint32_t color, +- int dest_left, +- int dest_top, +- int dest_width, +- int dest_height, +- const FX_RECT* pClipRect, +- const FXDIB_ResampleOptions& options, +- BlendMode blend_type) override; +- bool StartDIBits(const RetainPtr& pBitmap, +- int bitmap_alpha, +- uint32_t color, +- const CFX_Matrix& matrix, +- const FXDIB_ResampleOptions& options, +- std::unique_ptr* handle, +- BlendMode blend_type) override; +- bool DrawDeviceText(int nChars, +- const TextCharPos* pCharPos, +- CFX_Font* pFont, +- const CFX_Matrix& mtObject2Device, +- float font_size, +- uint32_t color) override; +- +- HDC m_hDC; +- const int m_Width; +- const int m_Height; +- int m_nBitsPerPixel; +- const int m_HorzSize; +- const int m_VertSize; +- float m_OriginY; +- bool m_SetOrigin; +-}; +-#endif // CORE_FXGE_WIN32_WIN32_INT_H_ +diff --git a/docs/getting-started.md b/docs/getting-started.md +index 6077d210b..dbba9a1c1 100644 +--- a/docs/getting-started.md ++++ b/docs/getting-started.md +@@ -75,7 +75,7 @@ the link line in order to compile. My build line was: + + ``` + PDF_LIBS="-lpdfium -lfpdfapi -lfxge -lfpdfdoc -lfxcrt -lfx_agg \ +--lfxcodec -lfx_lpng -lfx_libopenjpeg -lfx_lcms2 -lfx_freetype -ljpeg \ ++-lfxcodec -lpng -lfx_libopenjpeg -lfx_lcms2 -lfx_freetype -ljpeg \ + -lfdrm -lpwl -lbigint -lformfiller -ljavascript -lfxedit" + PDF_DIR= + +@@ -181,7 +181,7 @@ useful for loading documents over the network. + + + +-[chrome-plugin]: https://chromium.googlesource.com/chromium/src/+/master/pdf/ +-[pdfium-public]: https://pdfium.googlesource.com/pdfium/+/master/public/ ++[chrome-plugin]: https://chromium.googlesource.com/chromium/src/+/main/pdf/ ++[pdfium-public]: https://pdfium.googlesource.com/pdfium/+/main/public/ + [pdfium-v8]: /docs/v8-getting-started.md + [pdfium-edit-guide]: /docs/pdfium-edit-guide.md +diff --git a/docs/safetynet.md b/docs/safetynet.md +index bd0a791bf..8121bbc2f 100644 +--- a/docs/safetynet.md ++++ b/docs/safetynet.md +@@ -106,9 +106,6 @@ Make sure valgrind is installed: + $ valgrind + ``` + +-Add `ro_segment_workaround_for_valgrind=true` to `args.gn` for symbols to appear +-correctly. +- + This is a slow and accurate profiler. Expect variations of around 100 + instructions. However, this takes about 50 times longer to run than perf stat. + +diff --git a/fpdfsdk/Android.bp b/fpdfsdk/Android.bp +index f9c3861e3..f17a63b2e 100644 +--- a/fpdfsdk/Android.bp ++++ b/fpdfsdk/Android.bp +@@ -13,11 +13,8 @@ cc_library_static { + + visibility: ["//external/pdfium:__subpackages__"], + +- header_libs: [ +- "libpdfium-constants", +- ], +- + static_libs: [ ++ "libpdfium-constants", + "libpdfium-fdrm", + "libpdfium-edit", + "libpdfium-font", +diff --git a/fpdfsdk/BUILD.gn b/fpdfsdk/BUILD.gn +index 9eb95621b..b97d88141 100644 +--- a/fpdfsdk/BUILD.gn ++++ b/fpdfsdk/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -7,12 +7,8 @@ import("../testing/test.gni") + + source_set("fpdfsdk") { + sources = [ +- "cpdfsdk_actionhandler.cpp", +- "cpdfsdk_actionhandler.h", + "cpdfsdk_annot.cpp", + "cpdfsdk_annot.h", +- "cpdfsdk_annothandlermgr.cpp", +- "cpdfsdk_annothandlermgr.h", + "cpdfsdk_annotiteration.cpp", + "cpdfsdk_annotiteration.h", + "cpdfsdk_annotiterator.cpp", +@@ -21,12 +17,8 @@ source_set("fpdfsdk") { + "cpdfsdk_appstream.h", + "cpdfsdk_baannot.cpp", + "cpdfsdk_baannot.h", +- "cpdfsdk_baannothandler.cpp", +- "cpdfsdk_baannothandler.h", + "cpdfsdk_customaccess.cpp", + "cpdfsdk_customaccess.h", +- "cpdfsdk_fieldaction.cpp", +- "cpdfsdk_fieldaction.h", + "cpdfsdk_filewriteadapter.cpp", + "cpdfsdk_filewriteadapter.h", + "cpdfsdk_formfillenvironment.cpp", +@@ -43,8 +35,6 @@ source_set("fpdfsdk") { + "cpdfsdk_renderpage.h", + "cpdfsdk_widget.cpp", + "cpdfsdk_widget.h", +- "cpdfsdk_widgethandler.cpp", +- "cpdfsdk_widgethandler.h", + "fpdf_annot.cpp", + "fpdf_attachment.cpp", + "fpdf_catalog.cpp", +@@ -62,16 +52,19 @@ source_set("fpdfsdk") { + "fpdf_progressive.cpp", + "fpdf_save.cpp", + "fpdf_searchex.cpp", ++ "fpdf_signature.cpp", + "fpdf_structtree.cpp", + "fpdf_sysfontinfo.cpp", + "fpdf_text.cpp", + "fpdf_thumbnail.cpp", + "fpdf_transformpage.cpp", + "fpdf_view.cpp", +- "ipdfsdk_annothandler.h", + ] + +- configs += [ "../:pdfium_core_config" ] ++ configs += [ ++ "../:pdfium_strict_config", ++ "../:pdfium_noshorten_config", ++ ] + deps = [ + "../:pdfium_public_headers", + "../constants", +@@ -97,30 +90,34 @@ source_set("fpdfsdk") { + + if (pdf_enable_xfa) { + deps += [ +- "../fxbarcode", ++ "../xfa/fgas/font", + "../xfa/fxfa", + "../xfa/fxfa/parser", + "fpdfxfa", + ] + allow_circular_includes_from += [ "fpdfxfa" ] + } ++ ++ if (pdf_use_skia) { ++ deps += [ "//skia" ] ++ } + } + + pdfium_unittest_source_set("unittests") { + sources = [ +- "fpdf_annot_unittest.cpp", ++ "cpdfsdk_helpers_unittest.cpp", + "fpdf_catalog_unittest.cpp", + "fpdf_doc_unittest.cpp", + "fpdf_edit_unittest.cpp", +- "fpdf_editimg_unittest.cpp", + "fpdf_view_unittest.cpp", + ] + deps = [ + ":fpdfsdk", + "../:pdfium_public_headers", + "../constants", +- "../core/fpdfapi/page", ++ "../core/fpdfapi/page:unit_test_support", + "../core/fpdfapi/parser", ++ "../core/fpdfapi/parser:unit_test_support", + "../core/fpdfapi/render", + "../core/fpdfdoc", + ] +@@ -130,12 +127,13 @@ pdfium_unittest_source_set("unittests") { + pdfium_embeddertest_source_set("embeddertests") { + sources = [ + "cpdfsdk_annotiterator_embeddertest.cpp", +- "cpdfsdk_baannothandler_embeddertest.cpp", ++ "cpdfsdk_baannot_embeddertest.cpp", + "fpdf_annot_embeddertest.cpp", + "fpdf_attachment_embeddertest.cpp", + "fpdf_dataavail_embeddertest.cpp", + "fpdf_doc_embeddertest.cpp", + "fpdf_edit_embeddertest.cpp", ++ "fpdf_editimg_embeddertest.cpp", + "fpdf_editpage_embeddertest.cpp", + "fpdf_editpath_embeddertest.cpp", + "fpdf_ext_embeddertest.cpp", +@@ -145,6 +143,7 @@ pdfium_embeddertest_source_set("embeddertests") { + "fpdf_ppo_embeddertest.cpp", + "fpdf_save_embeddertest.cpp", + "fpdf_searchex_embeddertest.cpp", ++ "fpdf_signature_embeddertest.cpp", + "fpdf_structtree_embeddertest.cpp", + "fpdf_sysfontinfo_embeddertest.cpp", + "fpdf_text_embeddertest.cpp", +@@ -163,4 +162,8 @@ pdfium_embeddertest_source_set("embeddertests") { + "../core/fxge", + ] + pdfium_root_dir = "../" ++ ++ if (pdf_use_skia) { ++ deps += [ "//skia" ] ++ } + } +diff --git a/fpdfsdk/DEPS b/fpdfsdk/DEPS +index 5edee083e..14d2700e7 100644 +--- a/fpdfsdk/DEPS ++++ b/fpdfsdk/DEPS +@@ -2,9 +2,10 @@ include_rules = [ + '+core', + '+fxjs', + '+public', ++ '+third_party/skia/include', + '+v8', ++ '+xfa/fgas/font', ++ '+xfa/fgas/graphics', + '+xfa/fwl', +- '+fxbarcode', + '+xfa/fxfa', +- '+xfa/fxgraphics', + ] +diff --git a/fpdfsdk/PRESUBMIT.py b/fpdfsdk/PRESUBMIT.py +index 17f7c6abc..9e59329b9 100644 +--- a/fpdfsdk/PRESUBMIT.py ++++ b/fpdfsdk/PRESUBMIT.py +@@ -1,4 +1,4 @@ +-# Copyright 2019 The PDFium Authors. All rights reserved. ++# Copyright 2019 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -8,6 +8,9 @@ See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts + for more details about the presubmit API built into depot_tools. + """ + ++USE_PYTHON3 = True ++ ++ + def _CheckApiTestFile(input_api, output_api): + """Checks that the public headers match the API tests.""" + api_test_file = input_api.os_path.normpath('fpdfsdk/fpdf_view_c_api_test.c') +@@ -21,7 +24,7 @@ def _CheckApiTestFile(input_api, output_api): + src_path = input_api.os_path.dirname(input_api.PresubmitLocalPath()) + check_script = input_api.os_path.join( + src_path, 'testing' , 'tools' , 'api_check.py') +- cmd = [input_api.python_executable, check_script] ++ cmd = [input_api.python3_executable, check_script] + try: + input_api.subprocess.check_output(cmd) + return [] +@@ -32,11 +35,11 @@ def _CheckApiTestFile(input_api, output_api): + + def CheckChangeOnUpload(input_api, output_api): + results = [] +- results += _CheckApiTestFile(input_api, output_api) ++ results.extend(_CheckApiTestFile(input_api, output_api)) + return results + + + def CheckChangeOnCommit(input_api, output_api): + results = [] +- results += _CheckApiTestFile(input_api, output_api) ++ results.extend(_CheckApiTestFile(input_api, output_api)) + return results +diff --git a/fpdfsdk/cpdfsdk_actionhandler.cpp b/fpdfsdk/cpdfsdk_actionhandler.cpp +deleted file mode 100644 +index 6b2b4b95f..000000000 +--- a/fpdfsdk/cpdfsdk_actionhandler.cpp ++++ /dev/null +@@ -1,426 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "fpdfsdk/cpdfsdk_actionhandler.h" +- +-#include +-#include +- +-#include "core/fpdfapi/parser/cpdf_array.h" +-#include "core/fpdfdoc/cpdf_formfield.h" +-#include "core/fpdfdoc/cpdf_interactiveform.h" +-#include "fpdfsdk/cpdfsdk_formfillenvironment.h" +-#include "fpdfsdk/cpdfsdk_interactiveform.h" +-#include "fxjs/ijs_event_context.h" +-#include "fxjs/ijs_runtime.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/stl_util.h" +- +-bool CPDFSDK_ActionHandler::DoAction_DocOpen( +- const CPDF_Action& action, +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- std::set visited; +- return ExecuteDocumentOpenAction(action, pFormFillEnv, &visited); +-} +- +-bool CPDFSDK_ActionHandler::DoAction_JavaScript( +- const CPDF_Action& JsAction, +- WideString csJSName, +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- if (JsAction.GetType() == CPDF_Action::JavaScript) { +- WideString swJS = JsAction.GetJavaScript(); +- if (!swJS.IsEmpty()) { +- RunDocumentOpenJavaScript(pFormFillEnv, csJSName, swJS); +- return true; +- } +- } +- +- return false; +-} +- +-bool CPDFSDK_ActionHandler::DoAction_FieldJavaScript( +- const CPDF_Action& JsAction, +- CPDF_AAction::AActionType type, +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- CPDF_FormField* pFormField, +- CPDFSDK_FieldAction* data) { +- ASSERT(pFormFillEnv); +- if (pFormFillEnv->IsJSPlatformPresent() && +- JsAction.GetType() == CPDF_Action::JavaScript) { +- WideString swJS = JsAction.GetJavaScript(); +- if (!swJS.IsEmpty()) { +- RunFieldJavaScript(pFormFillEnv, pFormField, type, data, swJS); +- return true; +- } +- } +- return false; +-} +- +-bool CPDFSDK_ActionHandler::DoAction_Page( +- const CPDF_Action& action, +- enum CPDF_AAction::AActionType eType, +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- std::set visited; +- return ExecuteDocumentPageAction(action, eType, pFormFillEnv, &visited); +-} +- +-bool CPDFSDK_ActionHandler::DoAction_Document( +- const CPDF_Action& action, +- enum CPDF_AAction::AActionType eType, +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- std::set visited; +- return ExecuteDocumentPageAction(action, eType, pFormFillEnv, &visited); +-} +- +-bool CPDFSDK_ActionHandler::DoAction_Field( +- const CPDF_Action& action, +- CPDF_AAction::AActionType type, +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- CPDF_FormField* pFormField, +- CPDFSDK_FieldAction* data) { +- std::set visited; +- return ExecuteFieldAction(action, type, pFormFillEnv, pFormField, data, +- &visited); +-} +- +-bool CPDFSDK_ActionHandler::ExecuteDocumentOpenAction( +- const CPDF_Action& action, +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- std::set* visited) { +- const CPDF_Dictionary* pDict = action.GetDict(); +- if (pdfium::ContainsKey(*visited, pDict)) +- return false; +- +- visited->insert(pDict); +- +- ASSERT(pFormFillEnv); +- if (action.GetType() == CPDF_Action::JavaScript) { +- if (pFormFillEnv->IsJSPlatformPresent()) { +- WideString swJS = action.GetJavaScript(); +- if (!swJS.IsEmpty()) +- RunDocumentOpenJavaScript(pFormFillEnv, WideString(), swJS); +- } +- } else { +- DoAction_NoJs(action, CPDF_AAction::AActionType::kDocumentOpen, +- pFormFillEnv); +- } +- +- for (int32_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) { +- CPDF_Action subaction = action.GetSubAction(i); +- if (!ExecuteDocumentOpenAction(subaction, pFormFillEnv, visited)) +- return false; +- } +- +- return true; +-} +- +-bool CPDFSDK_ActionHandler::ExecuteDocumentPageAction( +- const CPDF_Action& action, +- CPDF_AAction::AActionType type, +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- std::set* visited) { +- const CPDF_Dictionary* pDict = action.GetDict(); +- if (pdfium::ContainsKey(*visited, pDict)) +- return false; +- +- visited->insert(pDict); +- +- ASSERT(pFormFillEnv); +- if (action.GetType() == CPDF_Action::JavaScript) { +- if (pFormFillEnv->IsJSPlatformPresent()) { +- WideString swJS = action.GetJavaScript(); +- if (!swJS.IsEmpty()) +- RunDocumentPageJavaScript(pFormFillEnv, type, swJS); +- } +- } else { +- DoAction_NoJs(action, type, pFormFillEnv); +- } +- +- ASSERT(pFormFillEnv); +- +- for (int32_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) { +- CPDF_Action subaction = action.GetSubAction(i); +- if (!ExecuteDocumentPageAction(subaction, type, pFormFillEnv, visited)) +- return false; +- } +- +- return true; +-} +- +-bool CPDFSDK_ActionHandler::IsValidField( +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- CPDF_Dictionary* pFieldDict) { +- ASSERT(pFieldDict); +- +- CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm(); +- CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm(); +- return !!pPDFForm->GetFieldByDict(pFieldDict); +-} +- +-bool CPDFSDK_ActionHandler::ExecuteFieldAction( +- const CPDF_Action& action, +- CPDF_AAction::AActionType type, +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- CPDF_FormField* pFormField, +- CPDFSDK_FieldAction* data, +- std::set* visited) { +- const CPDF_Dictionary* pDict = action.GetDict(); +- if (pdfium::ContainsKey(*visited, pDict)) +- return false; +- +- visited->insert(pDict); +- +- ASSERT(pFormFillEnv); +- if (action.GetType() == CPDF_Action::JavaScript) { +- if (pFormFillEnv->IsJSPlatformPresent()) { +- WideString swJS = action.GetJavaScript(); +- if (!swJS.IsEmpty()) { +- RunFieldJavaScript(pFormFillEnv, pFormField, type, data, swJS); +- if (!IsValidField(pFormFillEnv, pFormField->GetFieldDict())) +- return false; +- } +- } +- } else { +- DoAction_NoJs(action, type, pFormFillEnv); +- } +- +- for (int32_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) { +- CPDF_Action subaction = action.GetSubAction(i); +- if (!ExecuteFieldAction(subaction, type, pFormFillEnv, pFormField, data, +- visited)) +- return false; +- } +- +- return true; +-} +- +-void CPDFSDK_ActionHandler::DoAction_NoJs( +- const CPDF_Action& action, +- CPDF_AAction::AActionType type, +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- ASSERT(pFormFillEnv); +- +- switch (action.GetType()) { +- case CPDF_Action::GoTo: +- DoAction_GoTo(pFormFillEnv, action); +- break; +- case CPDF_Action::URI: +- if (CPDF_AAction::IsUserClick(type)) +- DoAction_URI(pFormFillEnv, action); +- break; +- case CPDF_Action::Hide: +- DoAction_Hide(action, pFormFillEnv); +- break; +- case CPDF_Action::Named: +- DoAction_Named(pFormFillEnv, action); +- break; +- case CPDF_Action::SubmitForm: +- if (CPDF_AAction::IsUserClick(type)) +- DoAction_SubmitForm(action, pFormFillEnv); +- break; +- case CPDF_Action::ResetForm: +- DoAction_ResetForm(action, pFormFillEnv); +- break; +- case CPDF_Action::JavaScript: +- NOTREACHED(); +- break; +- case CPDF_Action::SetOCGState: +- case CPDF_Action::Thread: +- case CPDF_Action::Sound: +- case CPDF_Action::Movie: +- case CPDF_Action::Rendition: +- case CPDF_Action::Trans: +- case CPDF_Action::GoTo3DView: +- case CPDF_Action::GoToR: +- case CPDF_Action::GoToE: +- case CPDF_Action::Launch: +- case CPDF_Action::ImportData: +- // Unimplemented +- break; +- default: +- break; +- } +-} +- +-void CPDFSDK_ActionHandler::DoAction_GoTo( +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const CPDF_Action& action) { +- ASSERT(action.GetDict()); +- +- CPDF_Document* pPDFDocument = pFormFillEnv->GetPDFDocument(); +- ASSERT(pPDFDocument); +- +- CPDF_Dest MyDest = action.GetDest(pPDFDocument); +- int nPageIndex = MyDest.GetDestPageIndex(pPDFDocument); +- int nFitType = MyDest.GetZoomMode(); +- const CPDF_Array* pMyArray = MyDest.GetArray(); +- std::vector posArray; +- if (pMyArray) { +- for (size_t i = 2; i < pMyArray->size(); i++) +- posArray.push_back(pMyArray->GetNumberAt(i)); +- } +- pFormFillEnv->DoGoToAction(nPageIndex, nFitType, posArray.data(), +- posArray.size()); +-} +- +-void CPDFSDK_ActionHandler::DoAction_URI( +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const CPDF_Action& action) { +- ASSERT(action.GetDict()); +- +- ByteString sURI = action.GetURI(pFormFillEnv->GetPDFDocument()); +- pFormFillEnv->DoURIAction(sURI.c_str()); +-} +- +-void CPDFSDK_ActionHandler::DoAction_Named( +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const CPDF_Action& action) { +- ASSERT(action.GetDict()); +- +- ByteString csName = action.GetNamedAction(); +- pFormFillEnv->ExecuteNamedAction(csName.c_str()); +-} +- +-void CPDFSDK_ActionHandler::RunFieldJavaScript( +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- CPDF_FormField* pFormField, +- CPDF_AAction::AActionType type, +- CPDFSDK_FieldAction* data, +- const WideString& script) { +- ASSERT(type != CPDF_AAction::kCalculate); +- ASSERT(type != CPDF_AAction::kFormat); +- +- RunScript(pFormFillEnv, script, +- [type, data, pFormField](IJS_EventContext* context) { +- switch (type) { +- case CPDF_AAction::kCursorEnter: +- context->OnField_MouseEnter(data->bModifier, data->bShift, +- pFormField); +- break; +- case CPDF_AAction::kCursorExit: +- context->OnField_MouseExit(data->bModifier, data->bShift, +- pFormField); +- break; +- case CPDF_AAction::kButtonDown: +- context->OnField_MouseDown(data->bModifier, data->bShift, +- pFormField); +- break; +- case CPDF_AAction::kButtonUp: +- context->OnField_MouseUp(data->bModifier, data->bShift, +- pFormField); +- break; +- case CPDF_AAction::kGetFocus: +- context->OnField_Focus(data->bModifier, data->bShift, +- pFormField, &data->sValue); +- break; +- case CPDF_AAction::kLoseFocus: +- context->OnField_Blur(data->bModifier, data->bShift, +- pFormField, &data->sValue); +- break; +- case CPDF_AAction::kKeyStroke: +- context->OnField_Keystroke( +- &data->sChange, data->sChangeEx, data->bKeyDown, +- data->bModifier, &data->nSelEnd, &data->nSelStart, +- data->bShift, pFormField, &data->sValue, +- data->bWillCommit, data->bFieldFull, &data->bRC); +- break; +- case CPDF_AAction::kValidate: +- context->OnField_Validate(&data->sChange, data->sChangeEx, +- data->bKeyDown, data->bModifier, +- data->bShift, pFormField, +- &data->sValue, &data->bRC); +- break; +- default: +- NOTREACHED(); +- break; +- } +- }); +-} +- +-void CPDFSDK_ActionHandler::RunDocumentOpenJavaScript( +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const WideString& sScriptName, +- const WideString& script) { +- RunScript(pFormFillEnv, script, +- [pFormFillEnv, sScriptName](IJS_EventContext* context) { +- context->OnDoc_Open(pFormFillEnv, sScriptName); +- }); +-} +- +-void CPDFSDK_ActionHandler::RunDocumentPageJavaScript( +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- CPDF_AAction::AActionType type, +- const WideString& script) { +- RunScript(pFormFillEnv, script, +- [type, pFormFillEnv](IJS_EventContext* context) { +- switch (type) { +- case CPDF_AAction::kOpenPage: +- context->OnPage_Open(pFormFillEnv); +- break; +- case CPDF_AAction::kClosePage: +- context->OnPage_Close(pFormFillEnv); +- break; +- case CPDF_AAction::kCloseDocument: +- context->OnDoc_WillClose(pFormFillEnv); +- break; +- case CPDF_AAction::kSaveDocument: +- context->OnDoc_WillSave(pFormFillEnv); +- break; +- case CPDF_AAction::kDocumentSaved: +- context->OnDoc_DidSave(pFormFillEnv); +- break; +- case CPDF_AAction::kPrintDocument: +- context->OnDoc_WillPrint(pFormFillEnv); +- break; +- case CPDF_AAction::kDocumentPrinted: +- context->OnDoc_DidPrint(pFormFillEnv); +- break; +- case CPDF_AAction::kPageVisible: +- context->OnPage_InView(pFormFillEnv); +- break; +- case CPDF_AAction::kPageInvisible: +- context->OnPage_OutView(pFormFillEnv); +- break; +- default: +- NOTREACHED(); +- break; +- } +- }); +-} +- +-bool CPDFSDK_ActionHandler::DoAction_Hide( +- const CPDF_Action& action, +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm(); +- if (pForm->DoAction_Hide(action)) { +- pFormFillEnv->SetChangeMark(); +- return true; +- } +- return false; +-} +- +-bool CPDFSDK_ActionHandler::DoAction_SubmitForm( +- const CPDF_Action& action, +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm(); +- return pForm->DoAction_SubmitForm(action); +-} +- +-void CPDFSDK_ActionHandler::DoAction_ResetForm( +- const CPDF_Action& action, +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm(); +- pForm->DoAction_ResetForm(action); +-} +- +-void CPDFSDK_ActionHandler::RunScript(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const WideString& script, +- const RunScriptCallback& cb) { +- IJS_Runtime::ScopedEventContext pContext(pFormFillEnv->GetIJSRuntime()); +- cb(pContext.Get()); +- pContext->RunScript(script); +- // TODO(dsinclair): Return error if RunScript returns a IJS_Runtime::JS_Error. +-} +diff --git a/fpdfsdk/cpdfsdk_actionhandler.h b/fpdfsdk/cpdfsdk_actionhandler.h +deleted file mode 100644 +index a8bd9cfef..000000000 +--- a/fpdfsdk/cpdfsdk_actionhandler.h ++++ /dev/null +@@ -1,104 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef FPDFSDK_CPDFSDK_ACTIONHANDLER_H_ +-#define FPDFSDK_CPDFSDK_ACTIONHANDLER_H_ +- +-#include +-#include +- +-#include "core/fpdfdoc/cpdf_aaction.h" +-#include "core/fpdfdoc/cpdf_action.h" +-#include "core/fxcrt/fx_string.h" +-#include "fpdfsdk/cpdfsdk_fieldaction.h" +- +-class CPDFSDK_Annot; +-class CPDFSDK_FormFillEnvironment; +-class CPDF_Dictionary; +-class CPDF_FormField; +-class IJS_EventContext; +- +-class CPDFSDK_ActionHandler { +- public: +- bool DoAction_DocOpen(const CPDF_Action& action, +- CPDFSDK_FormFillEnvironment* pFormFillEnv); +- bool DoAction_JavaScript(const CPDF_Action& JsAction, +- WideString csJSName, +- CPDFSDK_FormFillEnvironment* pFormFillEnv); +- bool DoAction_Page(const CPDF_Action& action, +- enum CPDF_AAction::AActionType eType, +- CPDFSDK_FormFillEnvironment* pFormFillEnv); +- bool DoAction_Document(const CPDF_Action& action, +- enum CPDF_AAction::AActionType eType, +- CPDFSDK_FormFillEnvironment* pFormFillEnv); +- bool DoAction_Field(const CPDF_Action& action, +- CPDF_AAction::AActionType type, +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- CPDF_FormField* pFormField, +- CPDFSDK_FieldAction* data); +- bool DoAction_FieldJavaScript(const CPDF_Action& JsAction, +- CPDF_AAction::AActionType type, +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- CPDF_FormField* pFormField, +- CPDFSDK_FieldAction* data); +- +- private: +- using RunScriptCallback = std::function; +- +- void RunScript(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const WideString& script, +- const RunScriptCallback& cb); +- +- bool ExecuteDocumentOpenAction(const CPDF_Action& action, +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- std::set* visited); +- bool ExecuteDocumentPageAction(const CPDF_Action& action, +- CPDF_AAction::AActionType type, +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- std::set* visited); +- bool ExecuteFieldAction(const CPDF_Action& action, +- CPDF_AAction::AActionType type, +- CPDFSDK_FormFillEnvironment* pFormFillEnv, +- CPDF_FormField* pFormField, +- CPDFSDK_FieldAction* data, +- std::set* visited); +- +- void DoAction_NoJs(const CPDF_Action& action, +- CPDF_AAction::AActionType type, +- CPDFSDK_FormFillEnvironment* pFormFillEnv); +- void RunDocumentPageJavaScript(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- CPDF_AAction::AActionType type, +- const WideString& script); +- void RunDocumentOpenJavaScript(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const WideString& sScriptName, +- const WideString& script); +- void RunFieldJavaScript(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- CPDF_FormField* pFormField, +- CPDF_AAction::AActionType type, +- CPDFSDK_FieldAction* data, +- const WideString& script); +- +- bool IsValidField(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- CPDF_Dictionary* pFieldDict); +- +- void DoAction_GoTo(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const CPDF_Action& action); +- void DoAction_Launch(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const CPDF_Action& action); +- void DoAction_URI(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const CPDF_Action& action); +- void DoAction_Named(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const CPDF_Action& action); +- +- bool DoAction_Hide(const CPDF_Action& action, +- CPDFSDK_FormFillEnvironment* pFormFillEnv); +- bool DoAction_SubmitForm(const CPDF_Action& action, +- CPDFSDK_FormFillEnvironment* pFormFillEnv); +- void DoAction_ResetForm(const CPDF_Action& action, +- CPDFSDK_FormFillEnvironment* pFormFillEnv); +-}; +- +-#endif // FPDFSDK_CPDFSDK_ACTIONHANDLER_H_ +diff --git a/fpdfsdk/cpdfsdk_annot.cpp b/fpdfsdk/cpdfsdk_annot.cpp +index d6712d09b..4845e67bb 100644 +--- a/fpdfsdk/cpdfsdk_annot.cpp ++++ b/fpdfsdk/cpdfsdk_annot.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,14 +6,15 @@ + + #include "fpdfsdk/cpdfsdk_annot.h" + +-#include +- + #include "fpdfsdk/cpdfsdk_pageview.h" ++#include "third_party/base/check.h" + + CPDFSDK_Annot::CPDFSDK_Annot(CPDFSDK_PageView* pPageView) +- : m_pPageView(pPageView) {} ++ : m_pPageView(pPageView) { ++ DCHECK(m_pPageView); ++} + +-CPDFSDK_Annot::~CPDFSDK_Annot() {} ++CPDFSDK_Annot::~CPDFSDK_Annot() = default; + + CPDFSDK_BAAnnot* CPDFSDK_Annot::AsBAAnnot() { + return nullptr; +@@ -23,34 +24,108 @@ CPDFXFA_Widget* CPDFSDK_Annot::AsXFAWidget() { + return nullptr; + } + +-IPDF_Page* CPDFSDK_Annot::GetXFAPage() { +-#ifdef PDF_ENABLE_XFA +- if (m_pPageView) +- return m_pPageView->GetXFAPage(); +-#endif +- return nullptr; ++// static ++void CPDFSDK_Annot::OnMouseEnter(ObservedPtr& pAnnot, ++ Mask nFlags) { ++ pAnnot->GetUnsafeInputHandlers()->OnMouseEnter(nFlags); + } + +-int CPDFSDK_Annot::GetLayoutOrder() const { +- return 5; ++// static ++void CPDFSDK_Annot::OnMouseExit(ObservedPtr& pAnnot, ++ Mask nFlags) { ++ pAnnot->GetUnsafeInputHandlers()->OnMouseExit(nFlags); + } + +-CPDF_Annot* CPDFSDK_Annot::GetPDFAnnot() const { +- return nullptr; ++// static ++bool CPDFSDK_Annot::OnLButtonDown(ObservedPtr& pAnnot, ++ Mask nFlags, ++ const CFX_PointF& point) { ++ return pAnnot->GetUnsafeInputHandlers()->OnLButtonDown(nFlags, point); + } + +-CPDF_Annot::Subtype CPDFSDK_Annot::GetAnnotSubtype() const { +- return CPDF_Annot::Subtype::UNKNOWN; ++// static ++bool CPDFSDK_Annot::OnLButtonUp(ObservedPtr& pAnnot, ++ Mask nFlags, ++ const CFX_PointF& point) { ++ return pAnnot->GetUnsafeInputHandlers()->OnLButtonUp(nFlags, point); + } + +-bool CPDFSDK_Annot::IsSignatureWidget() const { +- return false; ++// static ++bool CPDFSDK_Annot::OnLButtonDblClk(ObservedPtr& pAnnot, ++ Mask nFlags, ++ const CFX_PointF& point) { ++ return pAnnot->GetUnsafeInputHandlers()->OnLButtonDblClk(nFlags, point); + } + +-void CPDFSDK_Annot::SetRect(const CFX_FloatRect& rect) {} ++// static ++bool CPDFSDK_Annot::OnMouseMove(ObservedPtr& pAnnot, ++ Mask nFlags, ++ const CFX_PointF& point) { ++ return pAnnot->GetUnsafeInputHandlers()->OnMouseMove(nFlags, point); ++} ++ ++// static ++bool CPDFSDK_Annot::OnMouseWheel(ObservedPtr& pAnnot, ++ Mask nFlags, ++ const CFX_PointF& point, ++ const CFX_Vector& delta) { ++ return pAnnot->GetUnsafeInputHandlers()->OnMouseWheel(nFlags, point, delta); ++} ++ ++// static ++bool CPDFSDK_Annot::OnRButtonDown(ObservedPtr& pAnnot, ++ Mask nFlags, ++ const CFX_PointF& point) { ++ return pAnnot->GetUnsafeInputHandlers()->OnRButtonDown(nFlags, point); ++} ++ ++// static ++bool CPDFSDK_Annot::OnRButtonUp(ObservedPtr& pAnnot, ++ Mask nFlags, ++ const CFX_PointF& point) { ++ return pAnnot->GetUnsafeInputHandlers()->OnRButtonUp(nFlags, point); ++} ++ ++// static ++bool CPDFSDK_Annot::OnChar(ObservedPtr& pAnnot, ++ uint32_t nChar, ++ Mask nFlags) { ++ return pAnnot->GetUnsafeInputHandlers()->OnChar(nChar, nFlags); ++} + +-CFX_FloatRect CPDFSDK_Annot::GetRect() const { +- return CFX_FloatRect(); ++// static ++bool CPDFSDK_Annot::OnKeyDown(ObservedPtr& pAnnot, ++ FWL_VKEYCODE nKeyCode, ++ Mask nFlags) { ++ return pAnnot->GetUnsafeInputHandlers()->OnKeyDown(nKeyCode, nFlags); ++} ++ ++// static ++bool CPDFSDK_Annot::OnSetFocus(ObservedPtr& pAnnot, ++ Mask nFlags) { ++ return pAnnot->GetUnsafeInputHandlers()->OnSetFocus(nFlags); ++} ++ ++// static ++bool CPDFSDK_Annot::OnKillFocus(ObservedPtr& pAnnot, ++ Mask nFlags) { ++ return pAnnot->GetUnsafeInputHandlers()->OnKillFocus(nFlags); ++} ++ ++IPDF_Page* CPDFSDK_Annot::GetXFAPage() { ++#ifdef PDF_ENABLE_XFA ++ return m_pPageView->GetXFAPage(); ++#else ++ return nullptr; ++#endif ++} ++ ++int CPDFSDK_Annot::GetLayoutOrder() const { ++ return 5; ++} ++ ++CPDF_Annot* CPDFSDK_Annot::GetPDFAnnot() const { ++ return nullptr; + } + + IPDF_Page* CPDFSDK_Annot::GetPage() { +@@ -63,5 +138,5 @@ IPDF_Page* CPDFSDK_Annot::GetPage() { + } + + CPDF_Page* CPDFSDK_Annot::GetPDFPage() { +- return m_pPageView ? m_pPageView->GetPDFPage() : nullptr; ++ return m_pPageView->GetPDFPage(); + } +diff --git a/fpdfsdk/cpdfsdk_annot.h b/fpdfsdk/cpdfsdk_annot.h +index e722de180..1357594e9 100644 +--- a/fpdfsdk/cpdfsdk_annot.h ++++ b/fpdfsdk/cpdfsdk_annot.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,16 +7,15 @@ + #ifndef FPDFSDK_CPDFSDK_ANNOT_H_ + #define FPDFSDK_CPDFSDK_ANNOT_H_ + +-#include "core/fpdfdoc/cpdf_aaction.h" + #include "core/fpdfdoc/cpdf_annot.h" +-#include "core/fpdfdoc/cpdf_defaultappearance.h" ++#include "core/fxcrt/fx_coordinates.h" ++#include "core/fxcrt/mask.h" + #include "core/fxcrt/observed_ptr.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "core/fxcrt/widestring.h" ++#include "public/fpdf_fwlevent.h" + +-class CFX_Matrix; +-class CFX_RenderDevice; + class CPDF_Page; +-class CPDF_RenderOptions; + class CPDFSDK_BAAnnot; + class CPDFSDK_PageView; + class CPDFXFA_Widget; +@@ -24,34 +23,118 @@ class IPDF_Page; + + class CPDFSDK_Annot : public Observable { + public: +- explicit CPDFSDK_Annot(CPDFSDK_PageView* pPageView); ++ // These methods may destroy the class that implements them when called. ++ // Access through the static methods below of the same name. ++ class UnsafeInputHandlers { ++ public: ++ virtual void OnMouseEnter(Mask nFlags) = 0; ++ virtual void OnMouseExit(Mask nFlags) = 0; ++ virtual bool OnLButtonDown(Mask nFlags, ++ const CFX_PointF& point) = 0; ++ virtual bool OnLButtonUp(Mask nFlags, ++ const CFX_PointF& point) = 0; ++ virtual bool OnLButtonDblClk(Mask nFlags, ++ const CFX_PointF& point) = 0; ++ virtual bool OnMouseMove(Mask nFlags, ++ const CFX_PointF& point) = 0; ++ virtual bool OnMouseWheel(Mask nFlags, ++ const CFX_PointF& point, ++ const CFX_Vector& delta) = 0; ++ virtual bool OnRButtonDown(Mask nFlags, ++ const CFX_PointF& point) = 0; ++ virtual bool OnRButtonUp(Mask nFlags, ++ const CFX_PointF& point) = 0; ++ virtual bool OnChar(uint32_t nChar, Mask nFlags) = 0; ++ virtual bool OnKeyDown(FWL_VKEYCODE nKeyCode, ++ Mask nFlags) = 0; ++ virtual bool OnSetFocus(Mask nFlags) = 0; ++ virtual bool OnKillFocus(Mask nFlags) = 0; ++ }; ++ + virtual ~CPDFSDK_Annot(); + + virtual CPDFSDK_BAAnnot* AsBAAnnot(); + virtual CPDFXFA_Widget* AsXFAWidget(); + ++ // Never returns nullptr. ++ virtual UnsafeInputHandlers* GetUnsafeInputHandlers() = 0; ++ ++ virtual void OnLoad() {} + virtual int GetLayoutOrder() const; + virtual CPDF_Annot* GetPDFAnnot() const; +- virtual CPDF_Annot::Subtype GetAnnotSubtype() const; +- virtual bool IsSignatureWidget() const; +- virtual CFX_FloatRect GetRect() const; +- virtual void SetRect(const CFX_FloatRect& rect); ++ virtual CPDF_Annot::Subtype GetAnnotSubtype() const = 0; ++ virtual CFX_FloatRect GetRect() const = 0; ++ virtual void OnDraw(CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device, ++ bool bDrawAnnots) = 0; ++ virtual bool DoHitTest(const CFX_PointF& point) = 0; ++ virtual CFX_FloatRect GetViewBBox() = 0; ++ virtual bool CanUndo() = 0; ++ virtual bool CanRedo() = 0; ++ virtual bool Undo() = 0; ++ virtual bool Redo() = 0; ++ virtual WideString GetText() = 0; ++ virtual WideString GetSelectedText() = 0; ++ virtual void ReplaceAndKeepSelection(const WideString& text) = 0; ++ virtual void ReplaceSelection(const WideString& text) = 0; ++ virtual bool SelectAllText() = 0; ++ virtual bool SetIndexSelected(int index, bool selected) = 0; ++ virtual bool IsIndexSelected(int index) = 0; ++ ++ // Callers must check if `pAnnot` is still valid after calling these methods, ++ // before accessing them again. ++ static void OnMouseEnter(ObservedPtr& pAnnot, ++ Mask nFlags); ++ static void OnMouseExit(ObservedPtr& pAnnot, ++ Mask nFlags); ++ static bool OnLButtonDown(ObservedPtr& pAnnot, ++ Mask nFlags, ++ const CFX_PointF& point); ++ static bool OnLButtonUp(ObservedPtr& pAnnot, ++ Mask nFlags, ++ const CFX_PointF& point); ++ static bool OnLButtonDblClk(ObservedPtr& pAnnot, ++ Mask nFlags, ++ const CFX_PointF& point); ++ static bool OnMouseMove(ObservedPtr& pAnnot, ++ Mask nFlags, ++ const CFX_PointF& point); ++ static bool OnMouseWheel(ObservedPtr& pAnnot, ++ Mask nFlags, ++ const CFX_PointF& point, ++ const CFX_Vector& delta); ++ static bool OnRButtonDown(ObservedPtr& pAnnot, ++ Mask nFlags, ++ const CFX_PointF& point); ++ static bool OnRButtonUp(ObservedPtr& pAnnot, ++ Mask nFlags, ++ const CFX_PointF& point); ++ static bool OnChar(ObservedPtr& pAnnot, ++ uint32_t nChar, ++ Mask nFlags); ++ static bool OnKeyDown(ObservedPtr& pAnnot, ++ FWL_VKEYCODE nKeyCode, ++ Mask nFlags); ++ static bool OnSetFocus(ObservedPtr& pAnnot, ++ Mask nFlags); ++ static bool OnKillFocus(ObservedPtr& pAnnot, ++ Mask nFlags); + + // Three cases: PDF page only, XFA page only, or XFA page backed by PDF page. + IPDF_Page* GetPage(); // Returns XFA Page if possible, else PDF page. + CPDF_Page* GetPDFPage(); // Returns PDF page or nullptr. + IPDF_Page* GetXFAPage(); // Returns XFA page or nullptr. + +- CPDFSDK_PageView* GetPageView() const { return m_pPageView.Get(); } ++ // Never returns nullptr. ++ CPDFSDK_PageView* GetPageView() const { return m_pPageView; } + + protected: ++ explicit CPDFSDK_Annot(CPDFSDK_PageView* pPageView); ++ ++ private: + UnownedPtr const m_pPageView; + }; + +-inline CPDFSDK_BAAnnot* ToBAAnnot(CPDFSDK_Annot* pAnnot) { +- return pAnnot ? pAnnot->AsBAAnnot() : nullptr; +-} +- + inline CPDFXFA_Widget* ToXFAWidget(CPDFSDK_Annot* pAnnot) { + return pAnnot ? pAnnot->AsXFAWidget() : nullptr; + } +diff --git a/fpdfsdk/cpdfsdk_annothandlermgr.cpp b/fpdfsdk/cpdfsdk_annothandlermgr.cpp +deleted file mode 100644 +index 2a45b0d4e..000000000 +--- a/fpdfsdk/cpdfsdk_annothandlermgr.cpp ++++ /dev/null +@@ -1,332 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "fpdfsdk/cpdfsdk_annothandlermgr.h" +- +-#include +- +-#include "core/fpdfapi/parser/cpdf_number.h" +-#include "core/fpdfapi/parser/cpdf_string.h" +-#include "core/fpdfdoc/cpdf_annot.h" +-#include "fpdfsdk/cpdfsdk_annot.h" +-#include "fpdfsdk/cpdfsdk_annotiterator.h" +-#include "fpdfsdk/cpdfsdk_baannot.h" +-#include "fpdfsdk/cpdfsdk_baannothandler.h" +-#include "fpdfsdk/cpdfsdk_formfillenvironment.h" +-#include "fpdfsdk/cpdfsdk_pageview.h" +-#include "fpdfsdk/cpdfsdk_widget.h" +-#include "fpdfsdk/cpdfsdk_widgethandler.h" +-#include "fpdfsdk/pwl/cpwl_wnd.h" +-#include "public/fpdf_fwlevent.h" +-#include "third_party/base/ptr_util.h" +- +-#ifdef PDF_ENABLE_XFA +-#include "fpdfsdk/fpdfxfa/cpdfxfa_page.h" +-#include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h" +-#include "fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h" +-#endif // PDF_ENABLE_XFA +- +-CPDFSDK_AnnotHandlerMgr::CPDFSDK_AnnotHandlerMgr( +- std::unique_ptr pBAAnnotHandler, +- std::unique_ptr pWidgetHandler, +- std::unique_ptr pXFAWidgetHandler) +- : m_pBAAnnotHandler(std::move(pBAAnnotHandler)), +- m_pWidgetHandler(std::move(pWidgetHandler)), +- m_pXFAWidgetHandler(std::move(pXFAWidgetHandler)) { +- ASSERT(m_pBAAnnotHandler); +- ASSERT(m_pWidgetHandler); +-} +- +-CPDFSDK_AnnotHandlerMgr::~CPDFSDK_AnnotHandlerMgr() = default; +- +-void CPDFSDK_AnnotHandlerMgr::SetFormFillEnv( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- m_pBAAnnotHandler->SetFormFillEnvironment(pFormFillEnv); +- m_pWidgetHandler->SetFormFillEnvironment(pFormFillEnv); +- if (m_pXFAWidgetHandler) +- m_pXFAWidgetHandler->SetFormFillEnvironment(pFormFillEnv); +-} +- +-CPDFSDK_Annot* CPDFSDK_AnnotHandlerMgr::NewAnnot(CPDF_Annot* pAnnot, +- CPDFSDK_PageView* pPageView) { +- ASSERT(pPageView); +- return GetAnnotHandlerOfType(pAnnot->GetSubtype()) +- ->NewAnnot(pAnnot, pPageView); +-} +- +-#ifdef PDF_ENABLE_XFA +-std::unique_ptr CPDFSDK_AnnotHandlerMgr::NewXFAAnnot( +- CXFA_FFWidget* pAnnot, +- CPDFSDK_PageView* pPageView) { +- ASSERT(pAnnot); +- ASSERT(pPageView); +- return static_cast(m_pXFAWidgetHandler.get()) +- ->NewAnnotForXFA(pAnnot, pPageView); +-} +-#endif // PDF_ENABLE_XFA +- +-void CPDFSDK_AnnotHandlerMgr::ReleaseAnnot( +- std::unique_ptr pAnnot) { +- IPDFSDK_AnnotHandler* pAnnotHandler = GetAnnotHandler(pAnnot.get()); +- pAnnotHandler->ReleaseAnnot(std::move(pAnnot)); +-} +- +-void CPDFSDK_AnnotHandlerMgr::Annot_OnLoad(CPDFSDK_Annot* pAnnot) { +- ASSERT(pAnnot); +- GetAnnotHandler(pAnnot)->OnLoad(pAnnot); +-} +- +-WideString CPDFSDK_AnnotHandlerMgr::Annot_GetText(CPDFSDK_Annot* pAnnot) { +- return GetAnnotHandler(pAnnot)->GetText(pAnnot); +-} +- +-WideString CPDFSDK_AnnotHandlerMgr::Annot_GetSelectedText( +- CPDFSDK_Annot* pAnnot) { +- return GetAnnotHandler(pAnnot)->GetSelectedText(pAnnot); +-} +- +-void CPDFSDK_AnnotHandlerMgr::Annot_ReplaceSelection(CPDFSDK_Annot* pAnnot, +- const WideString& text) { +- GetAnnotHandler(pAnnot)->ReplaceSelection(pAnnot, text); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_CanUndo(CPDFSDK_Annot* pAnnot) { +- return GetAnnotHandler(pAnnot)->CanUndo(pAnnot); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_CanRedo(CPDFSDK_Annot* pAnnot) { +- return GetAnnotHandler(pAnnot)->CanRedo(pAnnot); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_Undo(CPDFSDK_Annot* pAnnot) { +- return GetAnnotHandler(pAnnot)->Undo(pAnnot); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_Redo(CPDFSDK_Annot* pAnnot) { +- return GetAnnotHandler(pAnnot)->Redo(pAnnot); +-} +- +-IPDFSDK_AnnotHandler* CPDFSDK_AnnotHandlerMgr::GetAnnotHandler( +- CPDFSDK_Annot* pAnnot) const { +- return GetAnnotHandlerOfType(pAnnot->GetAnnotSubtype()); +-} +- +-IPDFSDK_AnnotHandler* CPDFSDK_AnnotHandlerMgr::GetAnnotHandlerOfType( +- CPDF_Annot::Subtype nAnnotSubtype) const { +- if (nAnnotSubtype == CPDF_Annot::Subtype::WIDGET) +- return m_pWidgetHandler.get(); +- +-#ifdef PDF_ENABLE_XFA +- if (nAnnotSubtype == CPDF_Annot::Subtype::XFAWIDGET) +- return m_pXFAWidgetHandler.get(); +-#endif // PDF_ENABLE_XFA +- +- return m_pBAAnnotHandler.get(); +-} +- +-void CPDFSDK_AnnotHandlerMgr::Annot_OnDraw(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device, +- bool bDrawAnnots) { +- ASSERT(pAnnot); +- GetAnnotHandler(pAnnot)->OnDraw(pPageView, pAnnot, pDevice, mtUser2Device, +- bDrawAnnots); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_OnLButtonDown( +- CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- ASSERT(pAnnot->HasObservable()); +- return GetAnnotHandler(pAnnot->Get()) +- ->OnLButtonDown(pPageView, pAnnot, nFlags, point); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_OnLButtonUp( +- CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- ASSERT(pAnnot->HasObservable()); +- return GetAnnotHandler(pAnnot->Get()) +- ->OnLButtonUp(pPageView, pAnnot, nFlags, point); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_OnLButtonDblClk( +- CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- ASSERT(pAnnot->HasObservable()); +- return GetAnnotHandler(pAnnot->Get()) +- ->OnLButtonDblClk(pPageView, pAnnot, nFlags, point); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_OnMouseMove( +- CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- ASSERT(pAnnot->HasObservable()); +- return GetAnnotHandler(pAnnot->Get()) +- ->OnMouseMove(pPageView, pAnnot, nFlags, point); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_OnMouseWheel( +- CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- short zDelta, +- const CFX_PointF& point) { +- ASSERT(pAnnot->HasObservable()); +- return GetAnnotHandler(pAnnot->Get()) +- ->OnMouseWheel(pPageView, pAnnot, nFlags, zDelta, point); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_OnRButtonDown( +- CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- ASSERT(pAnnot->HasObservable()); +- return GetAnnotHandler(pAnnot->Get()) +- ->OnRButtonDown(pPageView, pAnnot, nFlags, point); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_OnRButtonUp( +- CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- ASSERT(pAnnot->HasObservable()); +- return GetAnnotHandler(pAnnot->Get()) +- ->OnRButtonUp(pPageView, pAnnot, nFlags, point); +-} +- +-void CPDFSDK_AnnotHandlerMgr::Annot_OnMouseEnter( +- CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) { +- ASSERT(pAnnot->HasObservable()); +- GetAnnotHandler(pAnnot->Get())->OnMouseEnter(pPageView, pAnnot, nFlag); +-} +- +-void CPDFSDK_AnnotHandlerMgr::Annot_OnMouseExit( +- CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) { +- ASSERT(pAnnot->HasObservable()); +- GetAnnotHandler(pAnnot->Get())->OnMouseExit(pPageView, pAnnot, nFlag); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_OnChar(CPDFSDK_Annot* pAnnot, +- uint32_t nChar, +- uint32_t nFlags) { +- return GetAnnotHandler(pAnnot)->OnChar(pAnnot, nChar, nFlags); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_OnKeyDown(CPDFSDK_Annot* pAnnot, +- int nKeyCode, +- int nFlag) { +- if (CPWL_Wnd::IsCTRLKeyDown(nFlag) || CPWL_Wnd::IsALTKeyDown(nFlag)) { +- return GetAnnotHandler(pAnnot)->OnKeyDown(pAnnot, nKeyCode, nFlag); +- } +- ObservedPtr pObservedAnnot(pAnnot); +- CPDFSDK_PageView* pPage = pAnnot->GetPageView(); +- CPDFSDK_Annot* pFocusAnnot = pPage->GetFocusAnnot(); +- if (pFocusAnnot && (nKeyCode == FWL_VKEY_Tab)) { +- ObservedPtr pNext( +- GetNextAnnot(pFocusAnnot, !CPWL_Wnd::IsSHIFTKeyDown(nFlag))); +- if (pNext && pNext.Get() != pFocusAnnot) { +- pPage->GetFormFillEnv()->SetFocusAnnot(&pNext); +- return true; +- } +- } +- +- // Check |pAnnot| again because JS may have destroyed it in |GetNextAnnot| +- if (!pObservedAnnot) +- return false; +- +- return GetAnnotHandler(pAnnot)->OnKeyDown(pAnnot, nKeyCode, nFlag); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_OnSetFocus( +- ObservedPtr* pAnnot, +- uint32_t nFlag) { +- ASSERT(pAnnot->HasObservable()); +- return GetAnnotHandler(pAnnot->Get())->OnSetFocus(pAnnot, nFlag); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_OnKillFocus( +- ObservedPtr* pAnnot, +- uint32_t nFlag) { +- ASSERT(pAnnot->HasObservable()); +- return GetAnnotHandler(pAnnot->Get())->OnKillFocus(pAnnot, nFlag); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_SetIndexSelected( +- ObservedPtr* pAnnot, +- int index, +- bool selected) { +- return GetAnnotHandler(pAnnot->Get()) +- ->SetIndexSelected(pAnnot, index, selected); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_IsIndexSelected( +- ObservedPtr* pAnnot, +- int index) { +- return GetAnnotHandler(pAnnot->Get())->IsIndexSelected(pAnnot, index); +-} +- +-#ifdef PDF_ENABLE_XFA +-bool CPDFSDK_AnnotHandlerMgr::Annot_OnChangeFocus( +- ObservedPtr* pSetAnnot, +- ObservedPtr* pKillAnnot) { +- CPDFXFA_Widget* pSetXFAWidget = ToXFAWidget(pSetAnnot->Get()); +- CPDFXFA_Widget* pKillXFAWidget = ToXFAWidget(pKillAnnot->Get()); +- bool bXFA = (pSetXFAWidget && pSetXFAWidget->GetXFAFFWidget()) || +- (pKillXFAWidget && pKillXFAWidget->GetXFAFFWidget()); +- +- return !bXFA || static_cast(m_pXFAWidgetHandler.get()) +- ->OnXFAChangedFocus(pKillAnnot, pSetAnnot); +-} +-#endif // PDF_ENABLE_XFA +- +-CFX_FloatRect CPDFSDK_AnnotHandlerMgr::Annot_OnGetViewBBox( +- CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot) { +- ASSERT(pAnnot); +- return GetAnnotHandler(pAnnot)->GetViewBBox(pPageView, pAnnot); +-} +- +-bool CPDFSDK_AnnotHandlerMgr::Annot_OnHitTest(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- const CFX_PointF& point) { +- ASSERT(pAnnot); +- IPDFSDK_AnnotHandler* pAnnotHandler = GetAnnotHandler(pAnnot); +- if (pAnnotHandler->CanAnswer(pAnnot)) +- return pAnnotHandler->HitTest(pPageView, pAnnot, point); +- +- return false; +-} +- +-CPDFSDK_Annot* CPDFSDK_AnnotHandlerMgr::GetNextAnnot(CPDFSDK_Annot* pSDKAnnot, +- bool bNext) { +-#ifdef PDF_ENABLE_XFA +- IPDF_Page* pPage = pSDKAnnot->GetPageView()->GetXFAPage(); +- if (pPage && !pPage->AsPDFPage()) { +- // For xfa annots in XFA pages not backed by PDF pages. +- return static_cast(pPage)->GetNextXFAAnnot(pSDKAnnot, bNext); +- } +-#endif // PDF_ENABLE_XFA +- +- // For PDF annots. +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pSDKAnnot); +- CPDFSDK_AnnotIterator ai(pWidget->GetPageView(), pWidget->GetAnnotSubtype()); +- return bNext ? ai.GetNextAnnot(pWidget) : ai.GetPrevAnnot(pWidget); +-} +diff --git a/fpdfsdk/cpdfsdk_annothandlermgr.h b/fpdfsdk/cpdfsdk_annothandlermgr.h +deleted file mode 100644 +index b88f61312..000000000 +--- a/fpdfsdk/cpdfsdk_annothandlermgr.h ++++ /dev/null +@@ -1,133 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef FPDFSDK_CPDFSDK_ANNOTHANDLERMGR_H_ +-#define FPDFSDK_CPDFSDK_ANNOTHANDLERMGR_H_ +- +-#include +- +-#include "core/fpdfdoc/cpdf_annot.h" +-#include "core/fxcrt/fx_coordinates.h" +-#include "fpdfsdk/cpdfsdk_annot.h" +- +-class CFX_Matrix; +-class CFX_RenderDevice; +-class CPDFSDK_FormFillEnvironment; +-class CPDFSDK_BAAnnotHandler; +-class CPDFSDK_WidgetHandler; +-class CPDFSDK_PageView; +-class IPDFSDK_AnnotHandler; +- +-#ifdef PDF_ENABLE_XFA +-class CXFA_FFWidget; +-#endif // PDF_ENABLE_XFA +- +-class CPDFSDK_AnnotHandlerMgr { +- public: +- CPDFSDK_AnnotHandlerMgr( +- std::unique_ptr pBAAnnotHandler, +- std::unique_ptr pWidgetHandler, +- std::unique_ptr pXFAWidgetHandler); +- +- ~CPDFSDK_AnnotHandlerMgr(); +- +- void SetFormFillEnv(CPDFSDK_FormFillEnvironment* pFormFillEnv); +- +- CPDFSDK_Annot* NewAnnot(CPDF_Annot* pAnnot, CPDFSDK_PageView* pPageView); +-#ifdef PDF_ENABLE_XFA +- std::unique_ptr NewXFAAnnot(CXFA_FFWidget* pAnnot, +- CPDFSDK_PageView* pPageView); +-#endif // PDF_ENABLE_XFA +- void ReleaseAnnot(std::unique_ptr pAnnot); +- +- void Annot_OnLoad(CPDFSDK_Annot* pAnnot); +- +- WideString Annot_GetText(CPDFSDK_Annot* pAnnot); +- WideString Annot_GetSelectedText(CPDFSDK_Annot* pAnnot); +- void Annot_ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text); +- +- bool Annot_CanUndo(CPDFSDK_Annot* pAnnot); +- bool Annot_CanRedo(CPDFSDK_Annot* pAnnot); +- bool Annot_Undo(CPDFSDK_Annot* pAnnot); +- bool Annot_Redo(CPDFSDK_Annot* pAnnot); +- +- void Annot_OnDraw(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device, +- bool bDrawAnnots); +- +- void Annot_OnMouseEnter(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags); +- void Annot_OnMouseExit(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags); +- bool Annot_OnLButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point); +- bool Annot_OnLButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point); +- bool Annot_OnLButtonDblClk(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point); +- bool Annot_OnMouseMove(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point); +- bool Annot_OnMouseWheel(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- short zDelta, +- const CFX_PointF& point); +- bool Annot_OnRButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point); +- bool Annot_OnRButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point); +- bool Annot_OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags); +- bool Annot_OnKeyDown(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag); +- bool Annot_OnSetFocus(ObservedPtr* pAnnot, uint32_t nFlag); +- bool Annot_OnKillFocus(ObservedPtr* pAnnot, uint32_t nFlag); +- bool Annot_SetIndexSelected(ObservedPtr* pAnnot, +- int index, +- bool selected); +- bool Annot_IsIndexSelected(ObservedPtr* pAnnot, int index); +- +-#ifdef PDF_ENABLE_XFA +- bool Annot_OnChangeFocus(ObservedPtr* pSetAnnot, +- ObservedPtr* pKillAnnot); +-#endif // PDF_ENABLE_XFA +- +- CFX_FloatRect Annot_OnGetViewBBox(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot); +- bool Annot_OnHitTest(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- const CFX_PointF& point); +- +- private: +- friend class CPDFSDK_BAAnnotHandlerTest; +- +- IPDFSDK_AnnotHandler* GetAnnotHandler(CPDFSDK_Annot* pAnnot) const; +- IPDFSDK_AnnotHandler* GetAnnotHandlerOfType( +- CPDF_Annot::Subtype nAnnotSubtype) const; +- CPDFSDK_Annot* GetNextAnnot(CPDFSDK_Annot* pSDKAnnot, bool bNext); +- +- // |m_pBAAnnotHandler| and |m_pWidgetHandler| are always present, but +- // |m_pXFAWidgetHandler| is only present in XFA mode. +- std::unique_ptr const m_pBAAnnotHandler; +- std::unique_ptr const m_pWidgetHandler; +- std::unique_ptr const m_pXFAWidgetHandler; +-}; +- +-#endif // FPDFSDK_CPDFSDK_ANNOTHANDLERMGR_H_ +diff --git a/fpdfsdk/cpdfsdk_annotiteration.cpp b/fpdfsdk/cpdfsdk_annotiteration.cpp +index d25695065..0ea37cc12 100644 +--- a/fpdfsdk/cpdfsdk_annotiteration.cpp ++++ b/fpdfsdk/cpdfsdk_annotiteration.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,34 +7,43 @@ + #include "fpdfsdk/cpdfsdk_annotiteration.h" + + #include +-#include + + #include "fpdfsdk/cpdfsdk_annot.h" + #include "fpdfsdk/cpdfsdk_pageview.h" + +-CPDFSDK_AnnotIteration::CPDFSDK_AnnotIteration(CPDFSDK_PageView* pPageView, +- bool bReverse) { +- // Copying/sorting ObservedPtrs is expensive, so do it once at the end. +- std::vector copiedList = pPageView->GetAnnotList(); +- std::stable_sort(copiedList.begin(), copiedList.end(), ++// static ++CPDFSDK_AnnotIteration CPDFSDK_AnnotIteration::CreateForDrawing( ++ CPDFSDK_PageView* page_view) { ++ CPDFSDK_AnnotIteration result(page_view); ++ return CPDFSDK_AnnotIteration(page_view, /*put_focused_annot_at_end=*/true); ++} ++ ++CPDFSDK_AnnotIteration::CPDFSDK_AnnotIteration(CPDFSDK_PageView* page_view) ++ : CPDFSDK_AnnotIteration(page_view, /*put_focused_annot_at_end=*/false) {} ++ ++CPDFSDK_AnnotIteration::CPDFSDK_AnnotIteration(CPDFSDK_PageView* page_view, ++ bool put_focused_annot_at_end) { ++ // Copying ObservedPtrs is expensive, so do it once at the end. ++ std::vector copied_list = page_view->GetAnnotList(); ++ std::stable_sort(copied_list.begin(), copied_list.end(), + [](const CPDFSDK_Annot* p1, const CPDFSDK_Annot* p2) { + return p1->GetLayoutOrder() < p2->GetLayoutOrder(); + }); + +- CPDFSDK_Annot* pTopMostAnnot = pPageView->GetFocusAnnot(); ++ CPDFSDK_Annot* pTopMostAnnot = page_view->GetFocusAnnot(); + if (pTopMostAnnot) { +- auto it = std::find(copiedList.begin(), copiedList.end(), pTopMostAnnot); +- if (it != copiedList.end()) { +- copiedList.erase(it); +- copiedList.insert(copiedList.begin(), pTopMostAnnot); ++ auto it = std::find(copied_list.begin(), copied_list.end(), pTopMostAnnot); ++ if (it != copied_list.end()) { ++ copied_list.erase(it); ++ auto insert_it = ++ put_focused_annot_at_end ? copied_list.end() : copied_list.begin(); ++ copied_list.insert(insert_it, pTopMostAnnot); + } + } +- if (bReverse) +- std::reverse(copiedList.begin(), copiedList.end()); + +- m_List.reserve(copiedList.size()); +- for (auto* pAnnot : copiedList) +- m_List.emplace_back(pAnnot); ++ list_.reserve(copied_list.size()); ++ for (auto* pAnnot : copied_list) ++ list_.emplace_back(pAnnot); + } + +-CPDFSDK_AnnotIteration::~CPDFSDK_AnnotIteration() {} ++CPDFSDK_AnnotIteration::~CPDFSDK_AnnotIteration() = default; +diff --git a/fpdfsdk/cpdfsdk_annotiteration.h b/fpdfsdk/cpdfsdk_annotiteration.h +index d3a0f39c0..ff6c2d710 100644 +--- a/fpdfsdk/cpdfsdk_annotiteration.h ++++ b/fpdfsdk/cpdfsdk_annotiteration.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -18,14 +18,21 @@ class CPDFSDK_AnnotIteration { + using const_iterator = + std::vector>::const_iterator; + +- CPDFSDK_AnnotIteration(CPDFSDK_PageView* pPageView, bool bReverse); ++ static CPDFSDK_AnnotIteration CreateForDrawing(CPDFSDK_PageView* page_view); ++ ++ explicit CPDFSDK_AnnotIteration(CPDFSDK_PageView* page_view); ++ CPDFSDK_AnnotIteration(const CPDFSDK_AnnotIteration&) = delete; ++ CPDFSDK_AnnotIteration& operator=(const CPDFSDK_AnnotIteration&) = delete; + ~CPDFSDK_AnnotIteration(); + +- const_iterator begin() const { return m_List.begin(); } +- const_iterator end() const { return m_List.end(); } ++ const_iterator begin() const { return list_.begin(); } ++ const_iterator end() const { return list_.end(); } + + private: +- std::vector> m_List; ++ CPDFSDK_AnnotIteration(CPDFSDK_PageView* page_view, ++ bool put_focused_annot_at_end); ++ ++ std::vector> list_; + }; + + #endif // FPDFSDK_CPDFSDK_ANNOTITERATION_H_ +diff --git a/fpdfsdk/cpdfsdk_annotiterator.cpp b/fpdfsdk/cpdfsdk_annotiterator.cpp +index f1919ab77..58ec08383 100644 +--- a/fpdfsdk/cpdfsdk_annotiterator.cpp ++++ b/fpdfsdk/cpdfsdk_annotiterator.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,8 +10,11 @@ + + #include "core/fpdfapi/page/cpdf_page.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fxcrt/stl_util.h" + #include "fpdfsdk/cpdfsdk_annot.h" + #include "fpdfsdk/cpdfsdk_pageview.h" ++#include "fpdfsdk/cpdfsdk_widget.h" ++#include "third_party/base/containers/contains.h" + + namespace { + +@@ -27,22 +30,13 @@ bool CompareByTopDescending(const CPDFSDK_Annot* p1, const CPDFSDK_Annot* p2) { + return GetAnnotRect(p1).top > GetAnnotRect(p2).top; + } + +-CPDFSDK_AnnotIterator::TabOrder GetTabOrder(CPDFSDK_PageView* pPageView) { +- CPDF_Page* pPDFPage = pPageView->GetPDFPage(); +- ByteString sTabs = pPDFPage->GetDict()->GetStringFor("Tabs"); +- if (sTabs == "R") +- return CPDFSDK_AnnotIterator::ROW; +- if (sTabs == "C") +- return CPDFSDK_AnnotIterator::COLUMN; +- return CPDFSDK_AnnotIterator::STRUCTURE; +-} +- + } // namespace + +-CPDFSDK_AnnotIterator::CPDFSDK_AnnotIterator(CPDFSDK_PageView* pPageView, +- CPDF_Annot::Subtype nAnnotSubtype) ++CPDFSDK_AnnotIterator::CPDFSDK_AnnotIterator( ++ CPDFSDK_PageView* pPageView, ++ const std::vector& subtypes_to_iterate) + : m_pPageView(pPageView), +- m_nAnnotSubtype(nAnnotSubtype), ++ m_subtypes(subtypes_to_iterate), + m_eTabOrder(GetTabOrder(pPageView)) { + GenerateResults(); + } +@@ -63,62 +57,75 @@ CPDFSDK_Annot* CPDFSDK_AnnotIterator::GetNextAnnot(CPDFSDK_Annot* pAnnot) { + return nullptr; + ++iter; + if (iter == m_Annots.end()) +- iter = m_Annots.begin(); ++ return nullptr; + return *iter; + } + + CPDFSDK_Annot* CPDFSDK_AnnotIterator::GetPrevAnnot(CPDFSDK_Annot* pAnnot) { + auto iter = std::find(m_Annots.begin(), m_Annots.end(), pAnnot); +- if (iter == m_Annots.end()) ++ if (iter == m_Annots.begin() || iter == m_Annots.end()) + return nullptr; +- if (iter == m_Annots.begin()) +- iter = m_Annots.end(); + return *(--iter); + } + +-void CPDFSDK_AnnotIterator::CollectAnnots(std::vector* pArray) { ++void CPDFSDK_AnnotIterator::CollectAnnots( ++ std::vector>* pArray) { + for (auto* pAnnot : m_pPageView->GetAnnotList()) { +- if (pAnnot->GetAnnotSubtype() == m_nAnnotSubtype && +- !pAnnot->IsSignatureWidget()) { +- pArray->push_back(pAnnot); ++ if (pdfium::Contains(m_subtypes, pAnnot->GetAnnotSubtype())) { ++ CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot); ++ if (!pWidget || !pWidget->IsSignatureWidget()) ++ pArray->emplace_back(pAnnot); + } + } + } + + CFX_FloatRect CPDFSDK_AnnotIterator::AddToAnnotsList( +- std::vector* sa, ++ std::vector>* sa, + size_t idx) { + CPDFSDK_Annot* pLeftTopAnnot = sa->at(idx); + CFX_FloatRect rcLeftTop = GetAnnotRect(pLeftTopAnnot); +- m_Annots.push_back(pLeftTopAnnot); ++ m_Annots.emplace_back(pLeftTopAnnot); + sa->erase(sa->begin() + idx); + return rcLeftTop; + } + +-void CPDFSDK_AnnotIterator::AddSelectedToAnnots(std::vector* sa, +- std::vector* aSelect) { ++void CPDFSDK_AnnotIterator::AddSelectedToAnnots( ++ std::vector>* sa, ++ std::vector* aSelect) { + for (size_t i = 0; i < aSelect->size(); ++i) +- m_Annots.push_back(sa->at(aSelect->at(i))); ++ m_Annots.emplace_back(sa->at(aSelect->at(i))); ++ ++ for (size_t i = aSelect->size(); i > 0; --i) ++ sa->erase(sa->begin() + aSelect->at(i - 1)); ++} + +- for (int i = aSelect->size() - 1; i >= 0; --i) +- sa->erase(sa->begin() + aSelect->at(i)); ++// static ++CPDFSDK_AnnotIterator::TabOrder CPDFSDK_AnnotIterator::GetTabOrder( ++ CPDFSDK_PageView* pPageView) { ++ CPDF_Page* pPDFPage = pPageView->GetPDFPage(); ++ ByteString sTabs = pPDFPage->GetDict()->GetByteStringFor("Tabs"); ++ if (sTabs == "R") ++ return kRow; ++ if (sTabs == "C") ++ return kColumn; ++ return kStructure; + } + + void CPDFSDK_AnnotIterator::GenerateResults() { + switch (m_eTabOrder) { +- case STRUCTURE: ++ case kStructure: + CollectAnnots(&m_Annots); + break; + +- case ROW: { +- std::vector sa; ++ case kRow: { ++ std::vector> sa; + CollectAnnots(&sa); + std::sort(sa.begin(), sa.end(), CompareByLeftAscending); + + while (!sa.empty()) { + int nLeftTopIndex = -1; + float fTop = 0.0f; +- for (int i = sa.size() - 1; i >= 0; i--) { ++ for (int i = fxcrt::CollectionSize(sa) - 1; i >= 0; i--) { + CFX_FloatRect rcAnnot = GetAnnotRect(sa[i]); + if (rcAnnot.top > fTop) { + nLeftTopIndex = i; +@@ -142,15 +149,15 @@ void CPDFSDK_AnnotIterator::GenerateResults() { + break; + } + +- case COLUMN: { +- std::vector sa; ++ case kColumn: { ++ std::vector> sa; + CollectAnnots(&sa); + std::sort(sa.begin(), sa.end(), CompareByTopDescending); + + while (!sa.empty()) { + int nLeftTopIndex = -1; + float fLeft = -1.0f; +- for (int i = sa.size() - 1; i >= 0; --i) { ++ for (int i = fxcrt::CollectionSize(sa) - 1; i >= 0; --i) { + CFX_FloatRect rcAnnot = GetAnnotRect(sa[i]); + if (fLeft < 0) { + nLeftTopIndex = 0; +diff --git a/fpdfsdk/cpdfsdk_annotiterator.h b/fpdfsdk/cpdfsdk_annotiterator.h +index fd7cbdc6b..74f93a40a 100644 +--- a/fpdfsdk/cpdfsdk_annotiterator.h ++++ b/fpdfsdk/cpdfsdk_annotiterator.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,7 +11,6 @@ + + #include "core/fpdfdoc/cpdf_annot.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" + #include "core/fxcrt/unowned_ptr.h" + + class CPDFSDK_Annot; +@@ -19,10 +18,9 @@ class CPDFSDK_PageView; + + class CPDFSDK_AnnotIterator { + public: +- enum TabOrder : uint8_t { STRUCTURE = 0, ROW, COLUMN }; +- +- CPDFSDK_AnnotIterator(CPDFSDK_PageView* pPageView, +- CPDF_Annot::Subtype nAnnotSubtype); ++ CPDFSDK_AnnotIterator( ++ CPDFSDK_PageView* pPageView, ++ const std::vector& subtypes_to_iterate); + ~CPDFSDK_AnnotIterator(); + + CPDFSDK_Annot* GetFirstAnnot(); +@@ -31,16 +29,21 @@ class CPDFSDK_AnnotIterator { + CPDFSDK_Annot* GetPrevAnnot(CPDFSDK_Annot* pAnnot); + + private: ++ enum TabOrder : uint8_t { kStructure = 0, kRow, kColumn }; ++ ++ static TabOrder GetTabOrder(CPDFSDK_PageView* pPageView); ++ + void GenerateResults(); +- void CollectAnnots(std::vector* pArray); +- CFX_FloatRect AddToAnnotsList(std::vector* sa, size_t idx); +- void AddSelectedToAnnots(std::vector* sa, ++ void CollectAnnots(std::vector>* pArray); ++ CFX_FloatRect AddToAnnotsList(std::vector>* sa, ++ size_t idx); ++ void AddSelectedToAnnots(std::vector>* sa, + std::vector* aSelect); + + UnownedPtr const m_pPageView; +- CPDF_Annot::Subtype m_nAnnotSubtype; ++ const std::vector m_subtypes; + const TabOrder m_eTabOrder; +- std::vector m_Annots; ++ std::vector> m_Annots; + }; + + #endif // FPDFSDK_CPDFSDK_ANNOTITERATOR_H_ +diff --git a/fpdfsdk/cpdfsdk_annotiterator_embeddertest.cpp b/fpdfsdk/cpdfsdk_annotiterator_embeddertest.cpp +index 161ae9517..1bb785f72 100644 +--- a/fpdfsdk/cpdfsdk_annotiterator_embeddertest.cpp ++++ b/fpdfsdk/cpdfsdk_annotiterator_embeddertest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -26,7 +26,7 @@ void CheckRect(const CFX_FloatRect& actual, const CFX_FloatRect& expected) { + class CPDFSDK_AnnotIteratorTest : public EmbedderTest {}; + + TEST_F(CPDFSDK_AnnotIteratorTest, CPDFSDK_AnnotIterator) { +- EXPECT_TRUE(OpenDocument("annotiter.pdf")); ++ ASSERT_TRUE(OpenDocument("annotiter.pdf")); + FPDF_PAGE page0 = LoadPage(0); + FPDF_PAGE page1 = LoadPage(1); + FPDF_PAGE page2 = LoadPage(2); +@@ -44,8 +44,8 @@ TEST_F(CPDFSDK_AnnotIteratorTest, CPDFSDK_AnnotIterator) { + + { + // Page 0 specifies "row order". +- CPDFSDK_AnnotIterator iter(pFormFillEnv->GetPageView(0), +- CPDF_Annot::Subtype::WIDGET); ++ CPDFSDK_AnnotIterator iter(pFormFillEnv->GetPageViewAtIndex(0), ++ {CPDF_Annot::Subtype::WIDGET}); + CPDFSDK_Annot* pAnnot = iter.GetFirstAnnot(); + CheckRect(pAnnot->GetRect(), RightTop); + pAnnot = iter.GetNextAnnot(pAnnot); +@@ -55,7 +55,7 @@ TEST_F(CPDFSDK_AnnotIteratorTest, CPDFSDK_AnnotIterator) { + pAnnot = iter.GetNextAnnot(pAnnot); + CheckRect(pAnnot->GetRect(), LeftBottom); + pAnnot = iter.GetNextAnnot(pAnnot); +- EXPECT_EQ(iter.GetFirstAnnot(), pAnnot); ++ EXPECT_FALSE(pAnnot); + + pAnnot = iter.GetLastAnnot(); + CheckRect(pAnnot->GetRect(), LeftBottom); +@@ -66,12 +66,12 @@ TEST_F(CPDFSDK_AnnotIteratorTest, CPDFSDK_AnnotIterator) { + pAnnot = iter.GetPrevAnnot(pAnnot); + CheckRect(pAnnot->GetRect(), RightTop); + pAnnot = iter.GetPrevAnnot(pAnnot); +- EXPECT_EQ(iter.GetLastAnnot(), pAnnot); ++ EXPECT_FALSE(pAnnot); + } + { + // Page 1 specifies "column order" +- CPDFSDK_AnnotIterator iter(pFormFillEnv->GetPageView(1), +- CPDF_Annot::Subtype::WIDGET); ++ CPDFSDK_AnnotIterator iter(pFormFillEnv->GetPageViewAtIndex(1), ++ {CPDF_Annot::Subtype::WIDGET}); + CPDFSDK_Annot* pAnnot = iter.GetFirstAnnot(); + CheckRect(pAnnot->GetRect(), RightTop); + pAnnot = iter.GetNextAnnot(pAnnot); +@@ -81,7 +81,7 @@ TEST_F(CPDFSDK_AnnotIteratorTest, CPDFSDK_AnnotIterator) { + pAnnot = iter.GetNextAnnot(pAnnot); + CheckRect(pAnnot->GetRect(), LeftBottom); + pAnnot = iter.GetNextAnnot(pAnnot); +- EXPECT_EQ(iter.GetFirstAnnot(), pAnnot); ++ EXPECT_FALSE(pAnnot); + + pAnnot = iter.GetLastAnnot(); + CheckRect(pAnnot->GetRect(), LeftBottom); +@@ -92,12 +92,12 @@ TEST_F(CPDFSDK_AnnotIteratorTest, CPDFSDK_AnnotIterator) { + pAnnot = iter.GetPrevAnnot(pAnnot); + CheckRect(pAnnot->GetRect(), RightTop); + pAnnot = iter.GetPrevAnnot(pAnnot); +- EXPECT_EQ(iter.GetLastAnnot(), pAnnot); ++ EXPECT_FALSE(pAnnot); + } + { + // Page 2 specifies "struct order" +- CPDFSDK_AnnotIterator iter(pFormFillEnv->GetPageView(2), +- CPDF_Annot::Subtype::WIDGET); ++ CPDFSDK_AnnotIterator iter(pFormFillEnv->GetPageViewAtIndex(2), ++ {CPDF_Annot::Subtype::WIDGET}); + CPDFSDK_Annot* pAnnot = iter.GetFirstAnnot(); + CheckRect(pAnnot->GetRect(), LeftBottom); + pAnnot = iter.GetNextAnnot(pAnnot); +@@ -107,7 +107,7 @@ TEST_F(CPDFSDK_AnnotIteratorTest, CPDFSDK_AnnotIterator) { + pAnnot = iter.GetNextAnnot(pAnnot); + CheckRect(pAnnot->GetRect(), RightBottom); + pAnnot = iter.GetNextAnnot(pAnnot); +- EXPECT_EQ(iter.GetFirstAnnot(), pAnnot); ++ EXPECT_FALSE(pAnnot); + + pAnnot = iter.GetLastAnnot(); + CheckRect(pAnnot->GetRect(), RightBottom); +@@ -118,7 +118,7 @@ TEST_F(CPDFSDK_AnnotIteratorTest, CPDFSDK_AnnotIterator) { + pAnnot = iter.GetPrevAnnot(pAnnot); + CheckRect(pAnnot->GetRect(), LeftBottom); + pAnnot = iter.GetPrevAnnot(pAnnot); +- EXPECT_EQ(iter.GetLastAnnot(), pAnnot); ++ EXPECT_FALSE(pAnnot); + } + UnloadPage(page2); + UnloadPage(page1); +diff --git a/fpdfsdk/cpdfsdk_appstream.cpp b/fpdfsdk/cpdfsdk_appstream.cpp +index b6600d27e..030ea1485 100644 +--- a/fpdfsdk/cpdfsdk_appstream.cpp ++++ b/fpdfsdk/cpdfsdk_appstream.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,9 +6,16 @@ + + #include "fpdfsdk/cpdfsdk_appstream.h" + ++#include ++ ++#include ++#include ++#include + #include + ++#include "constants/appearance.h" + #include "constants/form_flags.h" ++#include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h" + #include "core/fpdfapi/font/cpdf_font.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" +@@ -18,19 +25,21 @@ + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" +-#include "core/fpdfdoc/cba_fontmap.h" ++#include "core/fpdfapi/parser/fpdf_parser_utility.h" ++#include "core/fpdfdoc/cpdf_bafontmap.h" + #include "core/fpdfdoc/cpdf_formcontrol.h" + #include "core/fpdfdoc/cpdf_icon.h" + #include "core/fpdfdoc/cpvt_word.h" ++#include "core/fxcrt/fx_string_wrappers.h" + #include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fpdfsdk/cpdfsdk_interactiveform.h" + #include "fpdfsdk/cpdfsdk_pageview.h" + #include "fpdfsdk/cpdfsdk_widget.h" + #include "fpdfsdk/pwl/cpwl_edit.h" + #include "fpdfsdk/pwl/cpwl_edit_impl.h" +-#include "fpdfsdk/pwl/cpwl_icon.h" + #include "fpdfsdk/pwl/cpwl_wnd.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/numerics/safe_conversions.h" ++#include "third_party/base/span.h" + + namespace { + +@@ -60,7 +69,6 @@ const char kMarkedSequenceBeginOperator[] = "BMC"; + const char kMarkedSequenceEndOperator[] = "EMC"; + const char kMoveTextPositionOperator[] = "Td"; + const char kMoveToOperator[] = "m"; +-const char kSetCharacterSpacingOperator[] = "Tc"; + const char kSetCMYKOperator[] = "k"; + const char kSetCMKYStrokedOperator[] = "K"; + const char kSetDashOperator[] = "d"; +@@ -82,7 +90,7 @@ const char kTextEndOperator[] = "ET"; + + class AutoClosedCommand { + public: +- AutoClosedCommand(std::ostringstream* stream, ++ AutoClosedCommand(fxcrt::ostringstream* stream, + ByteString open, + ByteString close) + : stream_(stream), close_(close) { +@@ -92,43 +100,85 @@ class AutoClosedCommand { + virtual ~AutoClosedCommand() { *stream_ << close_ << "\n"; } + + private: +- std::ostringstream* stream_; ++ UnownedPtr const stream_; + ByteString close_; + }; + + class AutoClosedQCommand final : public AutoClosedCommand { + public: +- explicit AutoClosedQCommand(std::ostringstream* stream) ++ explicit AutoClosedQCommand(fxcrt::ostringstream* stream) + : AutoClosedCommand(stream, kStateSaveOperator, kStateRestoreOperator) {} +- ~AutoClosedQCommand() override {} ++ ~AutoClosedQCommand() override = default; + }; + +-ByteString GetColorAppStream(const CFX_Color& color, +- const bool& bFillOrStroke) { +- std::ostringstream sColorStream; ++void WriteMove(fxcrt::ostringstream& stream, const CFX_PointF& point) { ++ WritePoint(stream, point) << " " << kMoveToOperator << "\n"; ++} + ++void WriteLine(fxcrt::ostringstream& stream, const CFX_PointF& point) { ++ WritePoint(stream, point) << " " << kLineToOperator << "\n"; ++} ++ ++void WriteClosedLoop(fxcrt::ostringstream& stream, ++ pdfium::span points) { ++ WriteMove(stream, points[0]); ++ for (const auto& point : points.subspan(1)) ++ WriteLine(stream, point); ++ WriteLine(stream, points[0]); ++} ++ ++void WriteBezierCurve(fxcrt::ostringstream& stream, ++ const CFX_PointF& point1, ++ const CFX_PointF& point2, ++ const CFX_PointF& point3) { ++ WritePoint(stream, point1) << " "; ++ WritePoint(stream, point2) << " "; ++ WritePoint(stream, point3) << " " << kCurveToOperator << "\n"; ++} ++ ++void WriteAppendRect(fxcrt::ostringstream& stream, const CFX_FloatRect& rect) { ++ WriteRect(stream, rect) << " " << kAppendRectOperator << "\n"; ++} ++ ++ByteString GetStrokeColorAppStream(const CFX_Color& color) { ++ fxcrt::ostringstream sColorStream; + switch (color.nColorType) { +- case CFX_Color::kRGB: +- sColorStream << color.fColor1 << " " << color.fColor2 << " " +- << color.fColor3 << " " +- << (bFillOrStroke ? kSetRGBOperator : kSetRGBStrokedOperator) +- << "\n"; ++ case CFX_Color::Type::kTransparent: + break; +- case CFX_Color::kGray: +- sColorStream << color.fColor1 << " " +- << (bFillOrStroke ? kSetGrayOperator +- : kSetGrayStrokedOperator) +- << "\n"; ++ case CFX_Color::Type::kGray: ++ sColorStream << color.fColor1 << " " << kSetGrayStrokedOperator << "\n"; ++ break; ++ case CFX_Color::Type::kRGB: ++ sColorStream << color.fColor1 << " " << color.fColor2 << " " ++ << color.fColor3 << " " << kSetRGBStrokedOperator << "\n"; + break; +- case CFX_Color::kCMYK: ++ case CFX_Color::Type::kCMYK: + sColorStream << color.fColor1 << " " << color.fColor2 << " " + << color.fColor3 << " " << color.fColor4 << " " +- << (bFillOrStroke ? kSetCMYKOperator +- : kSetCMKYStrokedOperator) +- << "\n"; ++ << kSetCMKYStrokedOperator << "\n"; + break; + } ++ return ByteString(sColorStream); ++} + ++ByteString GetFillColorAppStream(const CFX_Color& color) { ++ fxcrt::ostringstream sColorStream; ++ switch (color.nColorType) { ++ case CFX_Color::Type::kTransparent: ++ break; ++ case CFX_Color::Type::kGray: ++ sColorStream << color.fColor1 << " " << kSetGrayOperator << "\n"; ++ break; ++ case CFX_Color::Type::kRGB: ++ sColorStream << color.fColor1 << " " << color.fColor2 << " " ++ << color.fColor3 << " " << kSetRGBOperator << "\n"; ++ break; ++ case CFX_Color::Type::kCMYK: ++ sColorStream << color.fColor1 << " " << color.fColor2 << " " ++ << color.fColor3 << " " << color.fColor4 << " " ++ << kSetCMYKOperator << "\n"; ++ break; ++ } + return ByteString(sColorStream); + } + +@@ -153,36 +203,37 @@ ByteString GetAP_Check(const CFX_FloatRect& crBBox) { + {CFX_PointF(0.40f, 0.60f), CFX_PointF(0.28f, 0.66f), + CFX_PointF(0.30f, 0.56f)}}; + +- for (size_t i = 0; i < FX_ArraySize(pts); ++i) { +- for (size_t j = 0; j < FX_ArraySize(pts[0]); ++j) { ++ for (size_t i = 0; i < std::size(pts); ++i) { ++ for (size_t j = 0; j < std::size(pts[0]); ++j) { + pts[i][j].x = pts[i][j].x * fWidth + crBBox.left; + pts[i][j].y *= pts[i][j].y * fHeight + crBBox.bottom; + } + } + +- std::ostringstream csAP; +- csAP << pts[0][0].x << " " << pts[0][0].y << " " << kMoveToOperator << "\n"; ++ fxcrt::ostringstream csAP; ++ WriteMove(csAP, pts[0][0]); + +- for (size_t i = 0; i < FX_ArraySize(pts); ++i) { +- size_t nNext = i < FX_ArraySize(pts) - 1 ? i + 1 : 0; ++ for (size_t i = 0; i < std::size(pts); ++i) { ++ size_t nNext = i < std::size(pts) - 1 ? i + 1 : 0; ++ const CFX_PointF& pt_next = pts[nNext][0]; + + float px1 = pts[i][1].x - pts[i][0].x; + float py1 = pts[i][1].y - pts[i][0].y; +- float px2 = pts[i][2].x - pts[nNext][0].x; +- float py2 = pts[i][2].y - pts[nNext][0].y; +- +- csAP << pts[i][0].x + px1 * FX_BEZIER << " " +- << pts[i][0].y + py1 * FX_BEZIER << " " +- << pts[nNext][0].x + px2 * FX_BEZIER << " " +- << pts[nNext][0].y + py2 * FX_BEZIER << " " << pts[nNext][0].x << " " +- << pts[nNext][0].y << " " << kCurveToOperator << "\n"; ++ float px2 = pts[i][2].x - pt_next.x; ++ float py2 = pts[i][2].y - pt_next.y; ++ ++ WriteBezierCurve( ++ csAP, ++ {pts[i][0].x + px1 * FXSYS_BEZIER, pts[i][0].y + py1 * FXSYS_BEZIER}, ++ {pt_next.x + px2 * FXSYS_BEZIER, pt_next.y + py2 * FXSYS_BEZIER}, ++ pt_next); + } + + return ByteString(csAP); + } + + ByteString GetAP_Circle(const CFX_FloatRect& crBBox) { +- std::ostringstream csAP; ++ fxcrt::ostringstream csAP; + + float fWidth = crBBox.Width(); + float fHeight = crBBox.Height(); +@@ -192,115 +243,101 @@ ByteString GetAP_Circle(const CFX_FloatRect& crBBox) { + CFX_PointF pt3(crBBox.right, crBBox.bottom + fHeight / 2); + CFX_PointF pt4(crBBox.left + fWidth / 2, crBBox.bottom); + +- csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n"; ++ WriteMove(csAP, pt1); + + float px = pt2.x - pt1.x; + float py = pt2.y - pt1.y; + +- csAP << pt1.x << " " << pt1.y + py * FX_BEZIER << " " +- << pt2.x - px * FX_BEZIER << " " << pt2.y << " " << pt2.x << " " << pt2.y +- << " " << kCurveToOperator << "\n"; ++ WriteBezierCurve(csAP, {pt1.x, pt1.y + py * FXSYS_BEZIER}, ++ {pt2.x - px * FXSYS_BEZIER, pt2.y}, pt2); + + px = pt3.x - pt2.x; + py = pt2.y - pt3.y; + +- csAP << pt2.x + px * FX_BEZIER << " " << pt2.y << " " << pt3.x << " " +- << pt3.y + py * FX_BEZIER << " " << pt3.x << " " << pt3.y << " " +- << kCurveToOperator << "\n"; ++ WriteBezierCurve(csAP, {pt2.x + px * FXSYS_BEZIER, pt2.y}, ++ {pt3.x, pt3.y + py * FXSYS_BEZIER}, pt3); + + px = pt3.x - pt4.x; + py = pt3.y - pt4.y; + +- csAP << pt3.x << " " << pt3.y - py * FX_BEZIER << " " +- << pt4.x + px * FX_BEZIER << " " << pt4.y << " " << pt4.x << " " << pt4.y +- << " " << kCurveToOperator << "\n"; ++ WriteBezierCurve(csAP, {pt3.x, pt3.y - py * FXSYS_BEZIER}, ++ {pt4.x + px * FXSYS_BEZIER, pt4.y}, pt4); + + px = pt4.x - pt1.x; + py = pt1.y - pt4.y; + +- csAP << pt4.x - px * FX_BEZIER << " " << pt4.y << " " << pt1.x << " " +- << pt1.y - py * FX_BEZIER << " " << pt1.x << " " << pt1.y << " " +- << kCurveToOperator << "\n"; ++ WriteBezierCurve(csAP, {pt4.x - px * FXSYS_BEZIER, pt4.y}, ++ {pt1.x, pt1.y - py * FXSYS_BEZIER}, pt1); + + return ByteString(csAP); + } + + ByteString GetAP_Cross(const CFX_FloatRect& crBBox) { +- std::ostringstream csAP; ++ fxcrt::ostringstream csAP; + +- csAP << crBBox.left << " " << crBBox.top << " " << kMoveToOperator << "\n"; +- csAP << crBBox.right << " " << crBBox.bottom << " " << kLineToOperator +- << "\n"; +- csAP << crBBox.left << " " << crBBox.bottom << " " << kMoveToOperator << "\n"; +- csAP << crBBox.right << " " << crBBox.top << " " << kLineToOperator << "\n"; ++ WriteMove(csAP, {crBBox.left, crBBox.top}); ++ WriteLine(csAP, {crBBox.right, crBBox.bottom}); ++ WriteMove(csAP, {crBBox.left, crBBox.bottom}); ++ WriteLine(csAP, {crBBox.right, crBBox.top}); + + return ByteString(csAP); + } + + ByteString GetAP_Diamond(const CFX_FloatRect& crBBox) { +- std::ostringstream csAP; ++ fxcrt::ostringstream csAP; + + float fWidth = crBBox.Width(); + float fHeight = crBBox.Height(); + +- CFX_PointF pt1(crBBox.left, crBBox.bottom + fHeight / 2); +- CFX_PointF pt2(crBBox.left + fWidth / 2, crBBox.top); +- CFX_PointF pt3(crBBox.right, crBBox.bottom + fHeight / 2); +- CFX_PointF pt4(crBBox.left + fWidth / 2, crBBox.bottom); +- +- csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n"; +- csAP << pt2.x << " " << pt2.y << " " << kLineToOperator << "\n"; +- csAP << pt3.x << " " << pt3.y << " " << kLineToOperator << "\n"; +- csAP << pt4.x << " " << pt4.y << " " << kLineToOperator << "\n"; +- csAP << pt1.x << " " << pt1.y << " " << kLineToOperator << "\n"; ++ const CFX_PointF points[] = {{crBBox.left, crBBox.bottom + fHeight / 2}, ++ {crBBox.left + fWidth / 2, crBBox.top}, ++ {crBBox.right, crBBox.bottom + fHeight / 2}, ++ {crBBox.left + fWidth / 2, crBBox.bottom}}; ++ WriteClosedLoop(csAP, points); + + return ByteString(csAP); + } + + ByteString GetAP_Square(const CFX_FloatRect& crBBox) { +- std::ostringstream csAP; ++ fxcrt::ostringstream csAP; + +- csAP << crBBox.left << " " << crBBox.top << " " << kMoveToOperator << "\n"; +- csAP << crBBox.right << " " << crBBox.top << " " << kLineToOperator << "\n"; +- csAP << crBBox.right << " " << crBBox.bottom << " " << kLineToOperator +- << "\n"; +- csAP << crBBox.left << " " << crBBox.bottom << " " << kLineToOperator << "\n"; +- csAP << crBBox.left << " " << crBBox.top << " " << kLineToOperator << "\n"; ++ const CFX_PointF points[] = {{crBBox.left, crBBox.top}, ++ {crBBox.right, crBBox.top}, ++ {crBBox.right, crBBox.bottom}, ++ {crBBox.left, crBBox.bottom}}; ++ WriteClosedLoop(csAP, points); + + return ByteString(csAP); + } + + ByteString GetAP_Star(const CFX_FloatRect& crBBox) { +- std::ostringstream csAP; ++ fxcrt::ostringstream csAP; + +- float fRadius = (crBBox.top - crBBox.bottom) / (1 + (float)cos(FX_PI / 5.0f)); ++ float fRadius = (crBBox.top - crBBox.bottom) / (1 + cosf(FXSYS_PI / 5.0f)); + CFX_PointF ptCenter = CFX_PointF((crBBox.left + crBBox.right) / 2.0f, + (crBBox.top + crBBox.bottom) / 2.0f); + +- float px[5]; +- float py[5]; +- float fAngel = FX_PI / 10.0f; +- for (int32_t i = 0; i < 5; i++) { +- px[i] = ptCenter.x + fRadius * (float)cos(fAngel); +- py[i] = ptCenter.y + fRadius * (float)sin(fAngel); +- fAngel += FX_PI * 2 / 5.0f; ++ CFX_PointF points[5]; ++ float fAngle = FXSYS_PI / 10.0f; ++ for (auto& point : points) { ++ point = ++ ptCenter + CFX_PointF(fRadius * cosf(fAngle), fRadius * sinf(fAngle)); ++ fAngle += FXSYS_PI * 2 / 5.0f; + } + +- csAP << px[0] << " " << py[0] << " " << kMoveToOperator << "\n"; ++ WriteMove(csAP, points[0]); + +- int32_t nNext = 0; +- for (int32_t j = 0; j < 5; j++) { +- nNext += 2; +- if (nNext >= 5) +- nNext -= 5; +- csAP << px[nNext] << " " << py[nNext] << " " << kLineToOperator << "\n"; ++ int next = 0; ++ for (size_t i = 0; i < std::size(points); ++i) { ++ next = (next + 2) % std::size(points); ++ WriteLine(csAP, points[next]); + } + + return ByteString(csAP); + } + + ByteString GetAP_HalfCircle(const CFX_FloatRect& crBBox, float fRotate) { +- std::ostringstream csAP; ++ fxcrt::ostringstream csAP; + + float fWidth = crBBox.Width(); + float fHeight = crBBox.Height(); +@@ -309,49 +346,45 @@ ByteString GetAP_HalfCircle(const CFX_FloatRect& crBBox, float fRotate) { + CFX_PointF pt2(0, fHeight / 2); + CFX_PointF pt3(fWidth / 2, 0); + +- float px; +- float py; ++ CFX_Matrix rotate_matrix(cos(fRotate), sin(fRotate), -sin(fRotate), ++ cos(fRotate), crBBox.left + fWidth / 2, ++ crBBox.bottom + fHeight / 2); ++ WriteMatrix(csAP, rotate_matrix) << " " << kConcatMatrixOperator << "\n"; + +- csAP << cos(fRotate) << " " << sin(fRotate) << " " << -sin(fRotate) << " " +- << cos(fRotate) << " " << crBBox.left + fWidth / 2 << " " +- << crBBox.bottom + fHeight / 2 << " " << kConcatMatrixOperator << "\n"; ++ WriteMove(csAP, pt1); + +- csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n"; +- +- px = pt2.x - pt1.x; +- py = pt2.y - pt1.y; ++ float px = pt2.x - pt1.x; ++ float py = pt2.y - pt1.y; + +- csAP << pt1.x << " " << pt1.y + py * FX_BEZIER << " " +- << pt2.x - px * FX_BEZIER << " " << pt2.y << " " << pt2.x << " " << pt2.y +- << " " << kCurveToOperator << "\n"; ++ WriteBezierCurve(csAP, {pt1.x, pt1.y + py * FXSYS_BEZIER}, ++ {pt2.x - px * FXSYS_BEZIER, pt2.y}, pt2); + + px = pt3.x - pt2.x; + py = pt2.y - pt3.y; + +- csAP << pt2.x + px * FX_BEZIER << " " << pt2.y << " " << pt3.x << " " +- << pt3.y + py * FX_BEZIER << " " << pt3.x << " " << pt3.y << " " +- << kCurveToOperator << "\n"; ++ WriteBezierCurve(csAP, {pt2.x + px * FXSYS_BEZIER, pt2.y}, ++ {pt3.x, pt3.y + py * FXSYS_BEZIER}, pt3); + + return ByteString(csAP); + } + + ByteString GetAppStream_Check(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { +- std::ostringstream sAP; ++ fxcrt::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); +- sAP << GetColorAppStream(crText, true) << GetAP_Check(rcBBox) +- << kFillOperator << "\n"; ++ sAP << GetFillColorAppStream(crText) << GetAP_Check(rcBBox) << kFillOperator ++ << "\n"; + } + return ByteString(sAP); + } + + ByteString GetAppStream_Circle(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { +- std::ostringstream sAP; ++ fxcrt::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); +- sAP << GetColorAppStream(crText, true) << GetAP_Circle(rcBBox) ++ sAP << GetFillColorAppStream(crText) << GetAP_Circle(rcBBox) + << kFillOperator << "\n"; + } + return ByteString(sAP); +@@ -359,10 +392,10 @@ ByteString GetAppStream_Circle(const CFX_FloatRect& rcBBox, + + ByteString GetAppStream_Cross(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { +- std::ostringstream sAP; ++ fxcrt::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); +- sAP << GetColorAppStream(crText, false) << GetAP_Cross(rcBBox) ++ sAP << GetStrokeColorAppStream(crText) << GetAP_Cross(rcBBox) + << kStrokeOperator << "\n"; + } + return ByteString(sAP); +@@ -370,11 +403,11 @@ ByteString GetAppStream_Cross(const CFX_FloatRect& rcBBox, + + ByteString GetAppStream_Diamond(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { +- std::ostringstream sAP; ++ fxcrt::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); + sAP << "1 " << kSetLineWidthOperator << "\n" +- << GetColorAppStream(crText, true) << GetAP_Diamond(rcBBox) ++ << GetFillColorAppStream(crText) << GetAP_Diamond(rcBBox) + << kFillOperator << "\n"; + } + return ByteString(sAP); +@@ -382,10 +415,10 @@ ByteString GetAppStream_Diamond(const CFX_FloatRect& rcBBox, + + ByteString GetAppStream_Square(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { +- std::ostringstream sAP; ++ fxcrt::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); +- sAP << GetColorAppStream(crText, true) << GetAP_Square(rcBBox) ++ sAP << GetFillColorAppStream(crText) << GetAP_Square(rcBBox) + << kFillOperator << "\n"; + } + return ByteString(sAP); +@@ -393,19 +426,19 @@ ByteString GetAppStream_Square(const CFX_FloatRect& rcBBox, + + ByteString GetAppStream_Star(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { +- std::ostringstream sAP; ++ fxcrt::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); +- sAP << GetColorAppStream(crText, true) << GetAP_Star(rcBBox) +- << kFillOperator << "\n"; ++ sAP << GetFillColorAppStream(crText) << GetAP_Star(rcBBox) << kFillOperator ++ << "\n"; + } + return ByteString(sAP); + } + + ByteString GetCircleFillAppStream(const CFX_FloatRect& rect, + const CFX_Color& color) { +- std::ostringstream sAppStream; +- ByteString sColor = GetColorAppStream(color, true); ++ fxcrt::ostringstream sAppStream; ++ ByteString sColor = GetFillColorAppStream(color); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q(&sAppStream); + sAppStream << sColor << GetAP_Circle(rect) << kFillOperator << "\n"; +@@ -420,7 +453,7 @@ ByteString GetCircleBorderAppStream(const CFX_FloatRect& rect, + const CFX_Color& crRightBottom, + BorderStyle nStyle, + const CPWL_Dash& dash) { +- std::ostringstream sAppStream; ++ fxcrt::ostringstream sAppStream; + ByteString sColor; + + if (fWidth > 0.0f) { +@@ -433,9 +466,9 @@ ByteString GetCircleBorderAppStream(const CFX_FloatRect& rect, + CFX_FloatRect rect_by_75 = rect.GetDeflated(div, div); + switch (nStyle) { + default: +- case BorderStyle::SOLID: +- case BorderStyle::UNDERLINE: { +- sColor = GetColorAppStream(color, false); ++ case BorderStyle::kSolid: ++ case BorderStyle::kUnderline: { ++ sColor = GetStrokeColorAppStream(color); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fWidth << " " << kSetLineWidthOperator << "\n" +@@ -443,8 +476,8 @@ ByteString GetCircleBorderAppStream(const CFX_FloatRect& rect, + << kStrokeOperator << "\n"; + } + } break; +- case BorderStyle::DASH: { +- sColor = GetColorAppStream(color, false); ++ case BorderStyle::kDash: { ++ sColor = GetStrokeColorAppStream(color); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fWidth << " " << kSetLineWidthOperator << "\n" +@@ -454,54 +487,52 @@ ByteString GetCircleBorderAppStream(const CFX_FloatRect& rect, + << kStrokeOperator << "\n"; + } + } break; +- case BorderStyle::BEVELED: { +- sColor = GetColorAppStream(color, false); ++ case BorderStyle::kBeveled: { ++ sColor = GetStrokeColorAppStream(color); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" + << sColor << GetAP_Circle(rect) << " " << kStrokeOperator + << "\n"; + } +- +- sColor = GetColorAppStream(crLeftTop, false); ++ sColor = GetStrokeColorAppStream(crLeftTop); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" +- << sColor << GetAP_HalfCircle(rect_by_75, FX_PI / 4.0f) ++ << sColor << GetAP_HalfCircle(rect_by_75, FXSYS_PI / 4.0f) + << " " << kStrokeOperator << "\n"; + } +- +- sColor = GetColorAppStream(crRightBottom, false); ++ sColor = GetStrokeColorAppStream(crRightBottom); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" +- << sColor << GetAP_HalfCircle(rect_by_75, FX_PI * 5 / 4.0f) +- << " " << kStrokeOperator << "\n"; ++ << sColor ++ << GetAP_HalfCircle(rect_by_75, FXSYS_PI * 5 / 4.0f) << " " ++ << kStrokeOperator << "\n"; + } + } break; +- case BorderStyle::INSET: { +- sColor = GetColorAppStream(color, false); ++ case BorderStyle::kInset: { ++ sColor = GetStrokeColorAppStream(color); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" + << sColor << GetAP_Circle(rect) << " " << kStrokeOperator + << "\n"; + } +- +- sColor = GetColorAppStream(crLeftTop, false); ++ sColor = GetStrokeColorAppStream(crLeftTop); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" +- << sColor << GetAP_HalfCircle(rect_by_75, FX_PI / 4.0f) ++ << sColor << GetAP_HalfCircle(rect_by_75, FXSYS_PI / 4.0f) + << " " << kStrokeOperator << "\n"; + } +- +- sColor = GetColorAppStream(crRightBottom, false); ++ sColor = GetStrokeColorAppStream(crRightBottom); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" +- << sColor << GetAP_HalfCircle(rect_by_75, FX_PI * 5 / 4.0f) +- << " " << kStrokeOperator << "\n"; ++ << sColor ++ << GetAP_HalfCircle(rect_by_75, FXSYS_PI * 5 / 4.0f) << " " ++ << kStrokeOperator << "\n"; + } + } break; + } +@@ -569,40 +600,39 @@ ByteString GetFontSetString(IPVT_FontMap* pFontMap, + if (sFontAlias.GetLength() <= 0 || fFontSize <= 0) + return ByteString(); + +- std::ostringstream sRet; ++ fxcrt::ostringstream sRet; + sRet << "/" << sFontAlias << " " << fFontSize << " " + << kSetTextFontAndSizeOperator << "\n"; + return ByteString(sRet); + } + +-ByteString GetWordRenderString(const ByteString& strWords) { +- if (strWords.GetLength() > 0) { +- return PDF_EncodeString(strWords, false) + " " + kShowTextOperator + "\n"; +- } +- return ByteString(); ++ByteString GetWordRenderString(ByteStringView strWords) { ++ if (strWords.IsEmpty()) ++ return ByteString(); ++ return PDF_EncodeString(strWords) + " " + kShowTextOperator + "\n"; + } + + ByteString GetEditAppStream(CPWL_EditImpl* pEdit, + const CFX_PointF& ptOffset, + bool bContinuous, + uint16_t SubWord) { +- CPWL_EditImpl_Iterator* pIterator = pEdit->GetIterator(); ++ CPWL_EditImpl::Iterator* pIterator = pEdit->GetIterator(); + pIterator->SetAt(0); + +- std::ostringstream sEditStream; +- std::ostringstream sWords; ++ fxcrt::ostringstream sEditStream; + int32_t nCurFontIndex = -1; + CFX_PointF ptOld; + CFX_PointF ptNew; + CPVT_WordPlace oldplace; ++ ByteString sWords; + + while (pIterator->NextWord()) { + CPVT_WordPlace place = pIterator->GetAt(); + if (bContinuous) { + if (place.LineCmp(oldplace) != 0) { +- if (sWords.tellp() > 0) { +- sEditStream << GetWordRenderString(ByteString(sWords)); +- sWords.str(""); ++ if (!sWords.IsEmpty()) { ++ sEditStream << GetWordRenderString(sWords.AsStringView()); ++ sWords.clear(); + } + + CPVT_Word word; +@@ -617,8 +647,8 @@ ByteString GetEditAppStream(CPWL_EditImpl* pEdit, + } + + if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) { +- sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y << " " +- << kMoveTextPositionOperator << "\n"; ++ WritePoint(sEditStream, {ptNew.x - ptOld.x, ptNew.y - ptOld.y}) ++ << " " << kMoveTextPositionOperator << "\n"; + + ptOld = ptNew; + } +@@ -627,18 +657,17 @@ ByteString GetEditAppStream(CPWL_EditImpl* pEdit, + CPVT_Word word; + if (pIterator->GetWord(word)) { + if (word.nFontIndex != nCurFontIndex) { +- if (sWords.tellp() > 0) { +- sEditStream << GetWordRenderString(ByteString(sWords)); +- sWords.str(""); ++ if (!sWords.IsEmpty()) { ++ sEditStream << GetWordRenderString(sWords.AsStringView()); ++ sWords.clear(); + } + sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex, + word.fFontSize); + nCurFontIndex = word.nFontIndex; + } + +- sWords << pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord); ++ sWords += pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord); + } +- + oldplace = place; + } else { + CPVT_Word word; +@@ -647,94 +676,79 @@ ByteString GetEditAppStream(CPWL_EditImpl* pEdit, + CFX_PointF(word.ptWord.x + ptOffset.x, word.ptWord.y + ptOffset.y); + + if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) { +- sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y << " " +- << kMoveTextPositionOperator << "\n"; ++ WritePoint(sEditStream, {ptNew.x - ptOld.x, ptNew.y - ptOld.y}) ++ << " " << kMoveTextPositionOperator << "\n"; + ptOld = ptNew; + } +- + if (word.nFontIndex != nCurFontIndex) { + sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex, + word.fFontSize); + nCurFontIndex = word.nFontIndex; + } +- + sEditStream << GetWordRenderString( +- pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord)); ++ pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord) ++ .AsStringView()); + } + } + } + +- if (sWords.tellp() > 0) { +- sEditStream << GetWordRenderString(ByteString(sWords)); +- sWords.str(""); +- } ++ if (!sWords.IsEmpty()) ++ sEditStream << GetWordRenderString(sWords.AsStringView()); + +- std::ostringstream sAppStream; ++ fxcrt::ostringstream sAppStream; + if (sEditStream.tellp() > 0) { +- float fCharSpace = pEdit->GetCharSpace(); +- if (!IsFloatZero(fCharSpace)) +- sAppStream << fCharSpace << " " << kSetCharacterSpacingOperator << "\n"; +- + sAppStream << sEditStream.str(); + } +- + return ByteString(sAppStream); + } + + ByteString GenerateIconAppStream(CPDF_IconFit& fit, +- CPDF_Stream* pIconStream, ++ RetainPtr pIconStream, + const CFX_FloatRect& rcIcon) { + if (rcIcon.IsEmpty() || !pIconStream) + return ByteString(); + +- CPWL_Wnd::CreateParams cp; ++ CPWL_Wnd::CreateParams cp(nullptr, nullptr, nullptr); + cp.dwFlags = PWS_VISIBLE; +- +- CPWL_Icon icon(cp, pdfium::MakeUnique(pIconStream), &fit); +- icon.Realize(); +- if (!icon.Move(rcIcon, false, false)) ++ auto pWnd = std::make_unique(cp, nullptr); ++ pWnd->Realize(); ++ if (!pWnd->Move(rcIcon, false, false)) + return ByteString(); + +- ByteString sAlias = icon.GetImageAlias(); ++ auto pPDFIcon = std::make_unique(std::move(pIconStream)); ++ ByteString sAlias = pPDFIcon->GetImageAlias(); + if (sAlias.GetLength() <= 0) + return ByteString(); + +- CFX_FloatRect rcPlate = icon.GetClientRect(); +- CFX_Matrix mt = icon.GetImageMatrix().GetInverse(); +- +- float fHScale; +- float fVScale; +- std::tie(fHScale, fVScale) = icon.GetScale(); ++ const CFX_FloatRect rcPlate = pWnd->GetClientRect(); ++ const CFX_SizeF image_size = pPDFIcon->GetImageSize(); ++ const CFX_Matrix mt = pPDFIcon->GetImageMatrix().GetInverse(); ++ const CFX_VectorF scale = fit.GetScale(image_size, rcPlate); ++ const CFX_VectorF offset = fit.GetImageOffset(image_size, scale, rcPlate); + +- float fx; +- float fy; +- std::tie(fx, fy) = icon.GetImageOffset(); +- +- std::ostringstream str; ++ fxcrt::ostringstream str; + { + AutoClosedQCommand q(&str); +- str << rcPlate.left << " " << rcPlate.bottom << " " +- << rcPlate.right - rcPlate.left << " " << rcPlate.top - rcPlate.bottom +- << " " << kAppendRectOperator << " " << kSetNonZeroWindingClipOperator +- << " " << kEndPathNoFillOrStrokeOperator << "\n"; ++ WriteAppendRect(str, rcPlate); ++ str << kSetNonZeroWindingClipOperator << " " ++ << kEndPathNoFillOrStrokeOperator << "\n"; + +- str << fHScale << " 0 0 " << fVScale << " " << rcPlate.left + fx << " " +- << rcPlate.bottom + fy << " " << kConcatMatrixOperator << "\n"; +- str << mt.a << " " << mt.b << " " << mt.c << " " << mt.d << " " << mt.e +- << " " << mt.f << " " << kConcatMatrixOperator << "\n"; ++ CFX_Matrix scale_matrix(scale.x, 0, 0, scale.y, rcPlate.left + offset.x, ++ rcPlate.bottom + offset.y); ++ WriteMatrix(str, scale_matrix) << " " << kConcatMatrixOperator << "\n"; ++ WriteMatrix(str, mt) << " " << kConcatMatrixOperator << "\n"; + + str << "0 " << kSetGrayOperator << " 0 " << kSetGrayStrokedOperator << " 1 " + << kSetLineWidthOperator << " /" << sAlias << " " + << kInvokeNamedXObjectOperator << "\n"; + } +- icon.Destroy(); +- ++ pWnd->Destroy(); + return ByteString(str); + } + + ByteString GetPushButtonAppStream(const CFX_FloatRect& rcBBox, + IPVT_FontMap* pFontMap, +- CPDF_Stream* pIconStream, ++ RetainPtr pIconStream, + CPDF_IconFit& IconFit, + const WideString& sLabel, + const CFX_Color& crText, +@@ -742,19 +756,20 @@ ByteString GetPushButtonAppStream(const CFX_FloatRect& rcBBox, + ButtonStyle nLayOut) { + const float fAutoFontScale = 1.0f / 3.0f; + +- auto pEdit = pdfium::MakeUnique(); ++ auto pEdit = std::make_unique(); + pEdit->SetFontMap(pFontMap); +- pEdit->SetAlignmentH(1, true); +- pEdit->SetAlignmentV(1, true); +- pEdit->SetMultiLine(false, true); +- pEdit->SetAutoReturn(false, true); +- if (IsFloatZero(fFontSize)) +- pEdit->SetAutoFontSize(true, true); ++ pEdit->SetAlignmentH(1); ++ pEdit->SetAlignmentV(1); ++ pEdit->SetMultiLine(false); ++ pEdit->SetAutoReturn(false); ++ if (FXSYS_IsFloatZero(fFontSize)) ++ pEdit->SetAutoFontSize(true); + else + pEdit->SetFontSize(fFontSize); + + pEdit->Initialize(); + pEdit->SetText(sLabel); ++ pEdit->Paint(); + + CFX_FloatRect rcLabelContent = pEdit->GetContentRect(); + CFX_FloatRect rcLabel; +@@ -771,7 +786,7 @@ ByteString GetPushButtonAppStream(const CFX_FloatRect& rcBBox, + break; + case ButtonStyle::kIconTopLabelBottom: + if (pIconStream) { +- if (IsFloatZero(fFontSize)) { ++ if (FXSYS_IsFloatZero(fFontSize)) { + fHeight = rcBBox.Height(); + rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right, + rcBBox.bottom + fHeight * fAutoFontScale); +@@ -795,7 +810,7 @@ ByteString GetPushButtonAppStream(const CFX_FloatRect& rcBBox, + break; + case ButtonStyle::kIconBottomLabelTop: + if (pIconStream) { +- if (IsFloatZero(fFontSize)) { ++ if (FXSYS_IsFloatZero(fFontSize)) { + fHeight = rcBBox.Height(); + rcLabel = + CFX_FloatRect(rcBBox.left, rcBBox.top - fHeight * fAutoFontScale, +@@ -820,7 +835,7 @@ ByteString GetPushButtonAppStream(const CFX_FloatRect& rcBBox, + break; + case ButtonStyle::kIconLeftLabelRight: + if (pIconStream) { +- if (IsFloatZero(fFontSize)) { ++ if (FXSYS_IsFloatZero(fFontSize)) { + fWidth = rcBBox.right - rcBBox.left; + if (rcLabelContent.Width() < fWidth * fAutoFontScale) { + rcLabel = CFX_FloatRect(rcBBox.right - fWidth * fAutoFontScale, +@@ -854,7 +869,7 @@ ByteString GetPushButtonAppStream(const CFX_FloatRect& rcBBox, + break; + case ButtonStyle::kIconRightLabelLeft: + if (pIconStream) { +- if (IsFloatZero(fFontSize)) { ++ if (FXSYS_IsFloatZero(fFontSize)) { + fWidth = rcBBox.right - rcBBox.left; + if (rcLabelContent.Width() < fWidth * fAutoFontScale) { + rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, +@@ -894,29 +909,28 @@ ByteString GetPushButtonAppStream(const CFX_FloatRect& rcBBox, + break; + } + +- std::ostringstream sTemp; +- sTemp << GenerateIconAppStream(IconFit, pIconStream, rcIcon); ++ fxcrt::ostringstream sTemp; ++ sTemp << GenerateIconAppStream(IconFit, std::move(pIconStream), rcIcon); + + if (!rcLabel.IsEmpty()) { + pEdit->SetPlateRect(rcLabel); ++ pEdit->Paint(); + ByteString sEdit = + GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, 0.0f), true, 0); + if (sEdit.GetLength() > 0) { + AutoClosedCommand bt(&sTemp, kTextBeginOperator, kTextEndOperator); +- sTemp << GetColorAppStream(crText, true) << sEdit; ++ sTemp << GetFillColorAppStream(crText) << sEdit; + } + } + + if (sTemp.tellp() <= 0) + return ByteString(); + +- std::ostringstream sAppStream; ++ fxcrt::ostringstream sAppStream; + { + AutoClosedQCommand q(&sAppStream); +- sAppStream << rcBBox.left << " " << rcBBox.bottom << " " +- << rcBBox.right - rcBBox.left << " " +- << rcBBox.top - rcBBox.bottom << " " << kAppendRectOperator +- << " " << kSetNonZeroWindingClipOperator << " " ++ WriteAppendRect(sAppStream, rcBBox); ++ sAppStream << kSetNonZeroWindingClipOperator << " " + << kEndPathNoFillOrStrokeOperator << "\n"; + sAppStream << sTemp.str().c_str(); + } +@@ -930,7 +944,7 @@ ByteString GetBorderAppStreamInternal(const CFX_FloatRect& rect, + const CFX_Color& crRightBottom, + BorderStyle nStyle, + const CPWL_Dash& dash) { +- std::ostringstream sAppStream; ++ fxcrt::ostringstream sAppStream; + ByteString sColor; + + float fLeft = rect.left; +@@ -944,103 +958,83 @@ ByteString GetBorderAppStreamInternal(const CFX_FloatRect& rect, + + switch (nStyle) { + default: +- case BorderStyle::SOLID: +- sColor = GetColorAppStream(color, true); ++ case BorderStyle::kSolid: ++ sColor = GetFillColorAppStream(color); + if (sColor.GetLength() > 0) { + sAppStream << sColor; +- sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " " +- << fTop - fBottom << " " << kAppendRectOperator << "\n"; +- sAppStream << fLeft + fWidth << " " << fBottom + fWidth << " " +- << fRight - fLeft - fWidth * 2 << " " +- << fTop - fBottom - fWidth * 2 << " " +- << kAppendRectOperator << "\n"; ++ WriteAppendRect(sAppStream, {fLeft, fBottom, fRight, fTop}); ++ WriteAppendRect(sAppStream, {fLeft + fWidth, fBottom + fWidth, ++ fRight - fWidth, fTop - fWidth}); + sAppStream << kFillEvenOddOperator << "\n"; + } + break; +- case BorderStyle::DASH: +- sColor = GetColorAppStream(color, false); ++ case BorderStyle::kDash: ++ sColor = GetStrokeColorAppStream(color); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fWidth << " " << kSetLineWidthOperator << " [" + << dash.nDash << " " << dash.nGap << "] " << dash.nPhase + << " " << kSetDashOperator << "\n"; +- sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2 << " " +- << kMoveToOperator << "\n"; +- sAppStream << fLeft + fWidth / 2 << " " << fTop - fWidth / 2 << " " +- << kLineToOperator << "\n"; +- sAppStream << fRight - fWidth / 2 << " " << fTop - fWidth / 2 << " " +- << kLineToOperator << "\n"; +- sAppStream << fRight - fWidth / 2 << " " << fBottom + fWidth / 2 +- << " " << kLineToOperator << "\n"; +- sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2 << " " +- << kLineToOperator << " " << kStrokeOperator << "\n"; ++ const CFX_PointF points[] = { ++ {fLeft + fWidth / 2, fBottom + fWidth / 2}, ++ {fLeft + fWidth / 2, fTop - fWidth / 2}, ++ {fRight - fWidth / 2, fTop - fWidth / 2}, ++ {fRight - fWidth / 2, fBottom + fWidth / 2}}; ++ WriteClosedLoop(sAppStream, points); ++ sAppStream << kStrokeOperator << "\n"; + } + break; +- case BorderStyle::BEVELED: +- case BorderStyle::INSET: +- sColor = GetColorAppStream(crLeftTop, true); ++ case BorderStyle::kBeveled: ++ case BorderStyle::kInset: ++ sColor = GetFillColorAppStream(crLeftTop); + if (sColor.GetLength() > 0) { + sAppStream << sColor; +- sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " " +- << kMoveToOperator << "\n"; +- sAppStream << fLeft + fHalfWidth << " " << fTop - fHalfWidth << " " +- << kLineToOperator << "\n"; +- sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth << " " +- << kLineToOperator << "\n"; +- sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2 +- << " " << kLineToOperator << "\n"; +- sAppStream << fLeft + fHalfWidth * 2 << " " << fTop - fHalfWidth * 2 +- << " " << kLineToOperator << "\n"; +- sAppStream << fLeft + fHalfWidth * 2 << " " +- << fBottom + fHalfWidth * 2 << " " << kLineToOperator +- << " " << kFillOperator << "\n"; ++ WriteMove(sAppStream, {fLeft + fHalfWidth, fBottom + fHalfWidth}); ++ WriteLine(sAppStream, {fLeft + fHalfWidth, fTop - fHalfWidth}); ++ WriteLine(sAppStream, {fRight - fHalfWidth, fTop - fHalfWidth}); ++ WriteLine(sAppStream, ++ {fRight - fHalfWidth * 2, fTop - fHalfWidth * 2}); ++ WriteLine(sAppStream, ++ {fLeft + fHalfWidth * 2, fTop - fHalfWidth * 2}); ++ WriteLine(sAppStream, ++ {fLeft + fHalfWidth * 2, fBottom + fHalfWidth * 2}); ++ sAppStream << kFillOperator << "\n"; + } +- +- sColor = GetColorAppStream(crRightBottom, true); ++ sColor = GetFillColorAppStream(crRightBottom); + if (sColor.GetLength() > 0) { + sAppStream << sColor; +- sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth << " " +- << kMoveToOperator << "\n"; +- sAppStream << fRight - fHalfWidth << " " << fBottom + fHalfWidth +- << " " << kLineToOperator << "\n"; +- sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " " +- << kLineToOperator << "\n"; +- sAppStream << fLeft + fHalfWidth * 2 << " " +- << fBottom + fHalfWidth * 2 << " " << kLineToOperator +- << "\n"; +- sAppStream << fRight - fHalfWidth * 2 << " " +- << fBottom + fHalfWidth * 2 << " " << kLineToOperator +- << "\n"; +- sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2 +- << " " << kLineToOperator << " " << kFillOperator << "\n"; ++ WriteMove(sAppStream, {fRight - fHalfWidth, fTop - fHalfWidth}); ++ WriteLine(sAppStream, {fRight - fHalfWidth, fBottom + fHalfWidth}); ++ WriteLine(sAppStream, {fLeft + fHalfWidth, fBottom + fHalfWidth}); ++ WriteLine(sAppStream, ++ {fLeft + fHalfWidth * 2, fBottom + fHalfWidth * 2}); ++ WriteLine(sAppStream, ++ {fRight - fHalfWidth * 2, fBottom + fHalfWidth * 2}); ++ WriteLine(sAppStream, ++ {fRight - fHalfWidth * 2, fTop - fHalfWidth * 2}); ++ sAppStream << kFillOperator << "\n"; + } +- +- sColor = GetColorAppStream(color, true); ++ sColor = GetFillColorAppStream(color); + if (sColor.GetLength() > 0) { + sAppStream << sColor; +- sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " " +- << fTop - fBottom << " " << kAppendRectOperator << "\n"; +- sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " " +- << fRight - fLeft - fHalfWidth * 2 << " " +- << fTop - fBottom - fHalfWidth * 2 << " " +- << kAppendRectOperator << " " << kFillEvenOddOperator +- << "\n"; ++ WriteAppendRect(sAppStream, {fLeft, fBottom, fRight, fTop}); ++ WriteAppendRect(sAppStream, {fLeft + fHalfWidth, fBottom + fHalfWidth, ++ fRight - fHalfWidth, fTop - fHalfWidth}); ++ sAppStream << kFillEvenOddOperator << "\n"; + } + break; +- case BorderStyle::UNDERLINE: +- sColor = GetColorAppStream(color, false); ++ case BorderStyle::kUnderline: ++ sColor = GetStrokeColorAppStream(color); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fWidth << " " << kSetLineWidthOperator << "\n"; +- sAppStream << fLeft << " " << fBottom + fWidth / 2 << " " +- << kMoveToOperator << "\n"; +- sAppStream << fRight << " " << fBottom + fWidth / 2 << " " +- << kLineToOperator << " " << kStrokeOperator << "\n"; ++ WriteMove(sAppStream, {fLeft, fBottom + fWidth / 2}); ++ WriteLine(sAppStream, {fRight, fBottom + fWidth / 2}); ++ sAppStream << kStrokeOperator << "\n"; + } + break; + } + } +- + return ByteString(sAppStream); + } + +@@ -1048,40 +1042,36 @@ ByteString GetDropButtonAppStream(const CFX_FloatRect& rcBBox) { + if (rcBBox.IsEmpty()) + return ByteString(); + +- std::ostringstream sAppStream; ++ fxcrt::ostringstream sAppStream; + { + AutoClosedQCommand q(&sAppStream); +- sAppStream << GetColorAppStream(CFX_Color(CFX_Color::kRGB, 220.0f / 255.0f, +- 220.0f / 255.0f, 220.0f / 255.0f), +- true) +- << rcBBox.left << " " << rcBBox.bottom << " " +- << rcBBox.right - rcBBox.left << " " +- << rcBBox.top - rcBBox.bottom << " " << kAppendRectOperator +- << " " << kFillOperator << "\n"; ++ sAppStream << GetFillColorAppStream( ++ CFX_Color(CFX_Color::Type::kRGB, 220.0f / 255.0f, 220.0f / 255.0f, ++ 220.0f / 255.0f)); ++ WriteAppendRect(sAppStream, rcBBox); ++ sAppStream << kFillOperator << "\n"; + } + + { + AutoClosedQCommand q(&sAppStream); + sAppStream << GetBorderAppStreamInternal( +- rcBBox, 2, CFX_Color(CFX_Color::kGray, 0), +- CFX_Color(CFX_Color::kGray, 1), CFX_Color(CFX_Color::kGray, 0.5), +- BorderStyle::BEVELED, CPWL_Dash(3, 0, 0)); ++ rcBBox, 2, CFX_Color(CFX_Color::Type::kGray, 0), ++ CFX_Color(CFX_Color::Type::kGray, 1), ++ CFX_Color(CFX_Color::Type::kGray, 0.5), BorderStyle::kBeveled, ++ CPWL_Dash(3, 0, 0)); + } + + CFX_PointF ptCenter = CFX_PointF((rcBBox.left + rcBBox.right) / 2, + (rcBBox.top + rcBBox.bottom) / 2); +- if (IsFloatBigger(rcBBox.right - rcBBox.left, 6) && +- IsFloatBigger(rcBBox.top - rcBBox.bottom, 6)) { ++ if (FXSYS_IsFloatBigger(rcBBox.right - rcBBox.left, 6) && ++ FXSYS_IsFloatBigger(rcBBox.top - rcBBox.bottom, 6)) { + AutoClosedQCommand q(&sAppStream); +- sAppStream << " 0 " << kSetGrayOperator << "\n" +- << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " " +- << kMoveToOperator << "\n" +- << ptCenter.x + 3 << " " << ptCenter.y + 1.5f << " " +- << kLineToOperator << "\n" +- << ptCenter.x << " " << ptCenter.y - 1.5f << " " +- << kLineToOperator << "\n" +- << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " " +- << kLineToOperator << " " << kFillOperator << "\n"; ++ const CFX_PointF points[] = {{ptCenter.x - 3, ptCenter.y + 1.5f}, ++ {ptCenter.x + 3, ptCenter.y + 1.5f}, ++ {ptCenter.x, ptCenter.y - 1.5f}}; ++ sAppStream << " 0 " << kSetGrayOperator << "\n"; ++ WriteClosedLoop(sAppStream, points); ++ sAppStream << kFillOperator << "\n"; + } + + return ByteString(sAppStream); +@@ -1089,13 +1079,13 @@ ByteString GetDropButtonAppStream(const CFX_FloatRect& rcBBox) { + + ByteString GetRectFillAppStream(const CFX_FloatRect& rect, + const CFX_Color& color) { +- std::ostringstream sAppStream; +- ByteString sColor = GetColorAppStream(color, true); ++ fxcrt::ostringstream sAppStream; ++ ByteString sColor = GetFillColorAppStream(color); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q(&sAppStream); +- sAppStream << sColor << rect.left << " " << rect.bottom << " " +- << rect.right - rect.left << " " << rect.top - rect.bottom << " " +- << kAppendRectOperator << " " << kFillOperator << "\n"; ++ sAppStream << sColor; ++ WriteAppendRect(sAppStream, rect); ++ sAppStream << kFillOperator << "\n"; + } + + return ByteString(sAppStream); +@@ -1105,7 +1095,7 @@ void SetDefaultIconName(CPDF_Stream* pIcon, const char* name) { + if (!pIcon) + return; + +- CPDF_Dictionary* pImageDict = pIcon->GetDict(); ++ RetainPtr pImageDict = pIcon->GetMutableDict(); + if (!pImageDict) + return; + +@@ -1115,13 +1105,36 @@ void SetDefaultIconName(CPDF_Stream* pIcon, const char* name) { + pImageDict->SetNewFor("Name", name, false); + } + ++absl::optional CheckStyleFromCaption(const WideString& caption) { ++ if (caption.IsEmpty()) ++ return absl::nullopt; ++ ++ // Character values are ZapfDingbats encodings of named glyphs. ++ switch (caption[0]) { ++ case L'4': ++ return CheckStyle::kCheck; ++ case L'8': ++ return CheckStyle::kCross; ++ case L'H': ++ return CheckStyle::kStar; ++ case L'l': ++ return CheckStyle::kCircle; ++ case L'n': ++ return CheckStyle::kSquare; ++ case L'u': ++ return CheckStyle::kDiamond; ++ default: ++ return absl::nullopt; ++ } ++} ++ + } // namespace + + CPDFSDK_AppStream::CPDFSDK_AppStream(CPDFSDK_Widget* widget, + CPDF_Dictionary* dict) + : widget_(widget), dict_(dict) {} + +-CPDFSDK_AppStream::~CPDFSDK_AppStream() {} ++CPDFSDK_AppStream::~CPDFSDK_AppStream() = default; + + void CPDFSDK_AppStream::SetAsPushButton() { + CPDF_FormControl* pControl = widget_->GetFormControl(); +@@ -1151,17 +1164,8 @@ void CPDFSDK_AppStream::SetAsPushButton() { + break; + } + +- CFX_Color crBackground; +- CFX_Color crBorder; +- int iColorType; +- float fc[4]; +- pControl->GetOriginalBackgroundColor(iColorType, fc); +- if (iColorType > 0) +- crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); +- +- pControl->GetOriginalBorderColor(iColorType, fc); +- if (iColorType > 0) +- crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); ++ CFX_Color crBackground = pControl->GetOriginalBackgroundColor(); ++ CFX_Color crBorder = pControl->GetOriginalBorderColor(); + + float fBorderWidth = static_cast(widget_->GetBorderWidth()); + CPWL_Dash dsBorder(3, 0, 0); +@@ -1170,37 +1174,33 @@ void CPDFSDK_AppStream::SetAsPushButton() { + + BorderStyle nBorderStyle = widget_->GetBorderStyle(); + switch (nBorderStyle) { +- case BorderStyle::DASH: ++ case BorderStyle::kDash: + dsBorder = CPWL_Dash(3, 3, 0); + break; +- case BorderStyle::BEVELED: ++ case BorderStyle::kBeveled: + fBorderWidth *= 2; +- crLeftTop = CFX_Color(CFX_Color::kGray, 1); ++ crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1); + crRightBottom = crBackground / 2.0f; + break; +- case BorderStyle::INSET: ++ case BorderStyle::kInset: + fBorderWidth *= 2; +- crLeftTop = CFX_Color(CFX_Color::kGray, 0.5); +- crRightBottom = CFX_Color(CFX_Color::kGray, 0.75); ++ crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5); ++ crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75); + break; + default: + break; + } + + CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth); +- CFX_Color crText(CFX_Color::kGray, 0); +- ByteString csNameTag; + CPDF_DefaultAppearance da = pControl->GetDefaultAppearance(); +- Optional color = da.GetColor(fc); +- if (color) { +- iColorType = *color; +- crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); +- } ++ absl::optional color = da.GetColor(); ++ CFX_Color crText = color.value_or(CFX_Color(CFX_Color::Type::kGray, 0)); + + float fFontSize; +- Optional font = da.GetFont(&fFontSize); +- if (font) +- csNameTag = *font; ++ ByteString csNameTag; ++ absl::optional font = da.GetFont(&fFontSize); ++ if (font.has_value()) ++ csNameTag = font.value(); + else + fFontSize = 12.0f; + +@@ -1208,58 +1208,63 @@ void CPDFSDK_AppStream::SetAsPushButton() { + WideString csNormalCaption; + WideString csRolloverCaption; + WideString csDownCaption; +- if (pControl->HasMKEntry("CA")) ++ if (pControl->HasMKEntry(pdfium::appearance::kCA)) + csNormalCaption = pControl->GetNormalCaption(); + +- if (pControl->HasMKEntry("RC")) ++ if (pControl->HasMKEntry(pdfium::appearance::kRC)) + csRolloverCaption = pControl->GetRolloverCaption(); + +- if (pControl->HasMKEntry("AC")) ++ if (pControl->HasMKEntry(pdfium::appearance::kAC)) + csDownCaption = pControl->GetDownCaption(); + +- CPDF_Stream* pNormalIcon = nullptr; +- CPDF_Stream* pRolloverIcon = nullptr; +- CPDF_Stream* pDownIcon = nullptr; +- if (pControl->HasMKEntry("I")) ++ RetainPtr pNormalIcon; ++ RetainPtr pRolloverIcon; ++ RetainPtr pDownIcon; ++ if (pControl->HasMKEntry(pdfium::appearance::kI)) + pNormalIcon = pControl->GetNormalIcon(); + +- if (pControl->HasMKEntry("RI")) ++ if (pControl->HasMKEntry(pdfium::appearance::kRI)) + pRolloverIcon = pControl->GetRolloverIcon(); + +- if (pControl->HasMKEntry("IX")) ++ if (pControl->HasMKEntry(pdfium::appearance::kIX)) + pDownIcon = pControl->GetDownIcon(); + +- SetDefaultIconName(pNormalIcon, "ImgA"); +- SetDefaultIconName(pRolloverIcon, "ImgB"); +- SetDefaultIconName(pDownIcon, "ImgC"); +- +- CBA_FontMap font_map(widget_->GetPDFPage()->GetDocument(), +- widget_->GetPDFAnnot()->GetAnnotDict()); +- font_map.SetAPType("N"); ++ SetDefaultIconName(pNormalIcon.Get(), "ImgA"); ++ SetDefaultIconName(pRolloverIcon.Get(), "ImgB"); ++ SetDefaultIconName(pDownIcon.Get(), "ImgC"); + + CPDF_IconFit iconFit = pControl->GetIconFit(); +- ByteString csAP = +- GetRectFillAppStream(rcWindow, crBackground) + +- GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, +- crRightBottom, nBorderStyle, dsBorder) + +- GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient, +- &font_map, pNormalIcon, iconFit, csNormalCaption, +- crText, fFontSize, nLayout); ++ { ++ CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(), ++ widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N"); ++ ByteString csAP = ++ GetRectFillAppStream(rcWindow, crBackground) + ++ GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, ++ crRightBottom, nBorderStyle, dsBorder) + ++ GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient, ++ &font_map, pNormalIcon, iconFit, csNormalCaption, ++ crText, fFontSize, nLayout); ++ ++ Write("N", csAP, ByteString()); ++ if (pNormalIcon) ++ AddImage("N", pNormalIcon.Get()); + +- Write("N", csAP, ByteString()); +- if (pNormalIcon) +- AddImage("N", pNormalIcon); ++ CPDF_FormControl::HighlightingMode eHLM = pControl->GetHighlightingMode(); ++ if (eHLM != CPDF_FormControl::kPush && eHLM != CPDF_FormControl::kToggle) { ++ Remove("D"); ++ Remove("R"); ++ return; ++ } + +- CPDF_FormControl::HighlightingMode eHLM = pControl->GetHighlightingMode(); +- if (eHLM == CPDF_FormControl::Push || eHLM == CPDF_FormControl::Toggle) { + if (csRolloverCaption.IsEmpty() && !pRolloverIcon) { + csRolloverCaption = csNormalCaption; + pRolloverIcon = pNormalIcon; + } +- +- font_map.SetAPType("R"); +- +- csAP = ++ } ++ { ++ CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(), ++ widget_->GetPDFAnnot()->GetMutableAnnotDict(), "R"); ++ ByteString csAP = + GetRectFillAppStream(rcWindow, crBackground) + + GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, + crRightBottom, nBorderStyle, dsBorder) + +@@ -1269,7 +1274,7 @@ void CPDFSDK_AppStream::SetAsPushButton() { + + Write("R", csAP, ByteString()); + if (pRolloverIcon) +- AddImage("R", pRolloverIcon); ++ AddImage("R", pRolloverIcon.Get()); + + if (csDownCaption.IsEmpty() && !pDownIcon) { + csDownCaption = csNormalCaption; +@@ -1277,24 +1282,25 @@ void CPDFSDK_AppStream::SetAsPushButton() { + } + + switch (nBorderStyle) { +- case BorderStyle::BEVELED: { ++ case BorderStyle::kBeveled: { + CFX_Color crTemp = crLeftTop; + crLeftTop = crRightBottom; + crRightBottom = crTemp; + break; + } +- case BorderStyle::INSET: { +- crLeftTop = CFX_Color(CFX_Color::kGray, 0); +- crRightBottom = CFX_Color(CFX_Color::kGray, 1); ++ case BorderStyle::kInset: { ++ crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0); ++ crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1); + break; + } + default: + break; + } +- +- font_map.SetAPType("D"); +- +- csAP = ++ } ++ { ++ CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(), ++ widget_->GetPDFAnnot()->GetMutableAnnotDict(), "D"); ++ ByteString csAP = + GetRectFillAppStream(rcWindow, crBackground - 0.25f) + + GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, + crRightBottom, nBorderStyle, dsBorder) + +@@ -1304,45 +1310,33 @@ void CPDFSDK_AppStream::SetAsPushButton() { + + Write("D", csAP, ByteString()); + if (pDownIcon) +- AddImage("D", pDownIcon); +- } else { +- Remove("D"); +- Remove("R"); ++ AddImage("D", pDownIcon.Get()); + } + } + + void CPDFSDK_AppStream::SetAsCheckBox() { + CPDF_FormControl* pControl = widget_->GetFormControl(); +- CFX_Color crBackground, crBorder, crText; +- int iColorType; +- float fc[4]; +- +- pControl->GetOriginalBackgroundColor(iColorType, fc); +- if (iColorType > 0) +- crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); +- +- pControl->GetOriginalBorderColor(iColorType, fc); +- if (iColorType > 0) +- crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); +- ++ CFX_Color crBackground = pControl->GetOriginalBackgroundColor(); ++ CFX_Color crBorder = pControl->GetOriginalBorderColor(); + float fBorderWidth = static_cast(widget_->GetBorderWidth()); + CPWL_Dash dsBorder(3, 0, 0); +- CFX_Color crLeftTop, crRightBottom; ++ CFX_Color crLeftTop; ++ CFX_Color crRightBottom; + + BorderStyle nBorderStyle = widget_->GetBorderStyle(); + switch (nBorderStyle) { +- case BorderStyle::DASH: ++ case BorderStyle::kDash: + dsBorder = CPWL_Dash(3, 3, 0); + break; +- case BorderStyle::BEVELED: ++ case BorderStyle::kBeveled: + fBorderWidth *= 2; +- crLeftTop = CFX_Color(CFX_Color::kGray, 1); ++ crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1); + crRightBottom = crBackground / 2.0f; + break; +- case BorderStyle::INSET: ++ case BorderStyle::kInset: + fBorderWidth *= 2; +- crLeftTop = CFX_Color(CFX_Color::kGray, 0.5); +- crRightBottom = CFX_Color(CFX_Color::kGray, 0.75); ++ crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5); ++ crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75); + break; + default: + break; +@@ -1350,38 +1344,11 @@ void CPDFSDK_AppStream::SetAsCheckBox() { + + CFX_FloatRect rcWindow = widget_->GetRotatedRect(); + CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth); +- CPDF_DefaultAppearance da = pControl->GetDefaultAppearance(); +- Optional color = da.GetColor(fc); +- if (color) { +- iColorType = *color; +- crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); +- } +- +- CheckStyle nStyle = CheckStyle::kCheck; +- WideString csWCaption = pControl->GetNormalCaption(); +- if (csWCaption.GetLength() > 0) { +- switch (csWCaption[0]) { +- case L'l': +- nStyle = CheckStyle::kCircle; +- break; +- case L'8': +- nStyle = CheckStyle::kCross; +- break; +- case L'u': +- nStyle = CheckStyle::kDiamond; +- break; +- case L'n': +- nStyle = CheckStyle::kSquare; +- break; +- case L'H': +- nStyle = CheckStyle::kStar; +- break; +- case L'4': +- default: +- nStyle = CheckStyle::kCheck; +- } +- } ++ absl::optional color = pControl->GetDefaultAppearance().GetColor(); ++ CFX_Color crText = color.value_or(CFX_Color()); + ++ CheckStyle nStyle = CheckStyleFromCaption(pControl->GetNormalCaption()) ++ .value_or(CheckStyle::kCheck); + ByteString csAP_N_ON = + GetRectFillAppStream(rcWindow, crBackground) + + GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, +@@ -1390,15 +1357,15 @@ void CPDFSDK_AppStream::SetAsCheckBox() { + ByteString csAP_N_OFF = csAP_N_ON; + + switch (nBorderStyle) { +- case BorderStyle::BEVELED: { ++ case BorderStyle::kBeveled: { + CFX_Color crTemp = crLeftTop; + crLeftTop = crRightBottom; + crRightBottom = crTemp; + break; + } +- case BorderStyle::INSET: { +- crLeftTop = CFX_Color(CFX_Color::kGray, 0); +- crRightBottom = CFX_Color(CFX_Color::kGray, 1); ++ case BorderStyle::kInset: { ++ crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0); ++ crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1); + break; + } + default: +@@ -1423,43 +1390,32 @@ void CPDFSDK_AppStream::SetAsCheckBox() { + + ByteString csAS = widget_->GetAppState(); + if (csAS.IsEmpty()) +- widget_->SetAppState("Off"); ++ widget_->SetAppStateOff(); + } + + void CPDFSDK_AppStream::SetAsRadioButton() { + CPDF_FormControl* pControl = widget_->GetFormControl(); +- CFX_Color crBackground; +- CFX_Color crBorder; +- CFX_Color crText; +- int iColorType; +- float fc[4]; +- +- pControl->GetOriginalBackgroundColor(iColorType, fc); +- if (iColorType > 0) +- crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); +- +- pControl->GetOriginalBorderColor(iColorType, fc); +- if (iColorType > 0) +- crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); +- ++ CFX_Color crBackground = pControl->GetOriginalBackgroundColor(); ++ CFX_Color crBorder = pControl->GetOriginalBorderColor(); + float fBorderWidth = static_cast(widget_->GetBorderWidth()); + CPWL_Dash dsBorder(3, 0, 0); + CFX_Color crLeftTop; + CFX_Color crRightBottom; ++ + BorderStyle nBorderStyle = widget_->GetBorderStyle(); + switch (nBorderStyle) { +- case BorderStyle::DASH: ++ case BorderStyle::kDash: + dsBorder = CPWL_Dash(3, 3, 0); + break; +- case BorderStyle::BEVELED: ++ case BorderStyle::kBeveled: + fBorderWidth *= 2; +- crLeftTop = CFX_Color(CFX_Color::kGray, 1); ++ crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1); + crRightBottom = crBackground / 2.0f; + break; +- case BorderStyle::INSET: ++ case BorderStyle::kInset: + fBorderWidth *= 2; +- crLeftTop = CFX_Color(CFX_Color::kGray, 0.5); +- crRightBottom = CFX_Color(CFX_Color::kGray, 0.75); ++ crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5); ++ crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75); + break; + default: + break; +@@ -1467,47 +1423,20 @@ void CPDFSDK_AppStream::SetAsRadioButton() { + + CFX_FloatRect rcWindow = widget_->GetRotatedRect(); + CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth); +- CPDF_DefaultAppearance da = pControl->GetDefaultAppearance(); +- Optional color = da.GetColor(fc); +- if (color) { +- iColorType = *color; +- crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); +- } +- +- CheckStyle nStyle = CheckStyle::kCircle; +- WideString csWCaption = pControl->GetNormalCaption(); +- if (csWCaption.GetLength() > 0) { +- switch (csWCaption[0]) { +- case L'8': +- nStyle = CheckStyle::kCross; +- break; +- case L'u': +- nStyle = CheckStyle::kDiamond; +- break; +- case L'n': +- nStyle = CheckStyle::kSquare; +- break; +- case L'H': +- nStyle = CheckStyle::kStar; +- break; +- case L'4': +- nStyle = CheckStyle::kCheck; +- break; +- case L'l': +- default: +- nStyle = CheckStyle::kCircle; +- } +- } ++ absl::optional color = pControl->GetDefaultAppearance().GetColor(); ++ CFX_Color crText = color.value_or(CFX_Color()); ++ CheckStyle nStyle = CheckStyleFromCaption(pControl->GetNormalCaption()) ++ .value_or(CheckStyle::kCircle); + + ByteString csAP_N_ON; + CFX_FloatRect rcCenter = rcWindow.GetCenterSquare().GetDeflated(1.0f, 1.0f); + if (nStyle == CheckStyle::kCircle) { +- if (nBorderStyle == BorderStyle::BEVELED) { +- crLeftTop = CFX_Color(CFX_Color::kGray, 1); ++ if (nBorderStyle == BorderStyle::kBeveled) { ++ crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1); + crRightBottom = crBackground - 0.25f; +- } else if (nBorderStyle == BorderStyle::INSET) { +- crLeftTop = CFX_Color(CFX_Color::kGray, 0.5f); +- crRightBottom = CFX_Color(CFX_Color::kGray, 0.75f); ++ } else if (nBorderStyle == BorderStyle::kInset) { ++ crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5f); ++ crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75f); + } + + csAP_N_ON = +@@ -1524,15 +1453,15 @@ void CPDFSDK_AppStream::SetAsRadioButton() { + ByteString csAP_N_OFF = csAP_N_ON; + + switch (nBorderStyle) { +- case BorderStyle::BEVELED: { ++ case BorderStyle::kBeveled: { + CFX_Color crTemp = crLeftTop; + crLeftTop = crRightBottom; + crRightBottom = crTemp; + break; + } +- case BorderStyle::INSET: { +- crLeftTop = CFX_Color(CFX_Color::kGray, 0); +- crRightBottom = CFX_Color(CFX_Color::kGray, 1); ++ case BorderStyle::kInset: { ++ crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0); ++ crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1); + break; + } + default: +@@ -1543,13 +1472,13 @@ void CPDFSDK_AppStream::SetAsRadioButton() { + + if (nStyle == CheckStyle::kCircle) { + CFX_Color crBK = crBackground - 0.25f; +- if (nBorderStyle == BorderStyle::BEVELED) { ++ if (nBorderStyle == BorderStyle::kBeveled) { + crLeftTop = crBackground - 0.25f; +- crRightBottom = CFX_Color(CFX_Color::kGray, 1); ++ crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1); + crBK = crBackground; +- } else if (nBorderStyle == BorderStyle::INSET) { +- crLeftTop = CFX_Color(CFX_Color::kGray, 0); +- crRightBottom = CFX_Color(CFX_Color::kGray, 1); ++ } else if (nBorderStyle == BorderStyle::kInset) { ++ crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0); ++ crRightBottom = CFX_Color(CFX_Color::Type::kGray, 1); + } + + csAP_D_ON = +@@ -1565,8 +1494,9 @@ void CPDFSDK_AppStream::SetAsRadioButton() { + + ByteString csAP_D_OFF = csAP_D_ON; + +- csAP_N_ON += GetRadioButtonAppStream(rcClient, nStyle, crText); +- csAP_D_ON += GetRadioButtonAppStream(rcClient, nStyle, crText); ++ ByteString app_stream = GetRadioButtonAppStream(rcClient, nStyle, crText); ++ csAP_N_ON += app_stream; ++ csAP_D_ON += app_stream; + + Write("N", csAP_N_ON, pControl->GetCheckedAPState()); + Write("N", csAP_N_OFF, "Off"); +@@ -1576,13 +1506,13 @@ void CPDFSDK_AppStream::SetAsRadioButton() { + + ByteString csAS = widget_->GetAppState(); + if (csAS.IsEmpty()) +- widget_->SetAppState("Off"); ++ widget_->SetAppStateOff(); + } + +-void CPDFSDK_AppStream::SetAsComboBox(Optional sValue) { ++void CPDFSDK_AppStream::SetAsComboBox(absl::optional sValue) { + CPDF_FormControl* pControl = widget_->GetFormControl(); + CPDF_FormField* pField = pControl->GetField(); +- std::ostringstream sBody; ++ fxcrt::ostringstream sBody; + + CFX_FloatRect rcClient = widget_->GetClientRect(); + CFX_FloatRect rcButton = rcClient; +@@ -1590,10 +1520,10 @@ void CPDFSDK_AppStream::SetAsComboBox(Optional sValue) { + rcButton.Normalize(); + + // Font map must outlive |pEdit|. +- CBA_FontMap font_map(widget_->GetPDFPage()->GetDocument(), +- widget_->GetPDFAnnot()->GetAnnotDict()); ++ CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(), ++ widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N"); + +- auto pEdit = pdfium::MakeUnique(); ++ auto pEdit = std::make_unique(); + pEdit->EnableRefresh(false); + pEdit->SetFontMap(&font_map); + +@@ -1602,25 +1532,26 @@ void CPDFSDK_AppStream::SetAsComboBox(Optional sValue) { + rcEdit.Normalize(); + + pEdit->SetPlateRect(rcEdit); +- pEdit->SetAlignmentV(1, true); ++ pEdit->SetAlignmentV(1); + + float fFontSize = widget_->GetFontSize(); +- if (IsFloatZero(fFontSize)) +- pEdit->SetAutoFontSize(true, true); ++ if (FXSYS_IsFloatZero(fFontSize)) ++ pEdit->SetAutoFontSize(true); + else + pEdit->SetFontSize(fFontSize); + + pEdit->Initialize(); +- + if (sValue.has_value()) { + pEdit->SetText(sValue.value()); + } else { + int32_t nCurSel = pField->GetSelectedIndex(0); +- if (nCurSel < 0) ++ if (nCurSel < 0) { + pEdit->SetText(pField->GetValue()); +- else ++ } else { + pEdit->SetText(pField->GetOptionLabel(nCurSel)); ++ } + } ++ pEdit->Paint(); + + CFX_FloatRect rcContent = pEdit->GetContentRect(); + ByteString sEdit = GetEditAppStream(pEdit.get(), CFX_PointF(), true, 0); +@@ -1632,15 +1563,14 @@ void CPDFSDK_AppStream::SetAsComboBox(Optional sValue) { + + if (rcContent.Width() > rcEdit.Width() || + rcContent.Height() > rcEdit.Height()) { +- sBody << rcEdit.left << " " << rcEdit.bottom << " " << rcEdit.Width() +- << " " << rcEdit.Height() << " " << kAppendRectOperator << "\n" +- << kSetNonZeroWindingClipOperator << "\n" ++ WriteAppendRect(sBody, rcEdit); ++ sBody << kSetNonZeroWindingClipOperator << "\n" + << kEndPathNoFillOrStrokeOperator << "\n"; + } + + CFX_Color crText = widget_->GetTextPWLColor(); + AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator); +- sBody << GetColorAppStream(crText, true) << sEdit; ++ sBody << GetFillColorAppStream(crText) << sEdit; + } + + sBody << GetDropButtonAppStream(rcButton); +@@ -1653,22 +1583,22 @@ void CPDFSDK_AppStream::SetAsListBox() { + CPDF_FormControl* pControl = widget_->GetFormControl(); + CPDF_FormField* pField = pControl->GetField(); + CFX_FloatRect rcClient = widget_->GetClientRect(); +- std::ostringstream sBody; ++ fxcrt::ostringstream sBody; + + // Font map must outlive |pEdit|. +- CBA_FontMap font_map(widget_->GetPDFPage()->GetDocument(), +- widget_->GetPDFAnnot()->GetAnnotDict()); ++ CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(), ++ widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N"); + +- auto pEdit = pdfium::MakeUnique(); ++ auto pEdit = std::make_unique(); + pEdit->EnableRefresh(false); + pEdit->SetFontMap(&font_map); + pEdit->SetPlateRect(CFX_FloatRect(rcClient.left, 0.0f, rcClient.right, 0.0f)); + + float fFontSize = widget_->GetFontSize(); +- pEdit->SetFontSize(IsFloatZero(fFontSize) ? 12.0f : fFontSize); ++ pEdit->SetFontSize(FXSYS_IsFloatZero(fFontSize) ? 12.0f : fFontSize); + pEdit->Initialize(); + +- std::ostringstream sList; ++ fxcrt::ostringstream sList; + float fy = rcClient.top; + + int32_t nTop = pField->GetTopVisibleIndex(); +@@ -1685,6 +1615,7 @@ void CPDFSDK_AppStream::SetAsListBox() { + } + + pEdit->SetText(pField->GetOptionLabel(i)); ++ pEdit->Paint(); + + CFX_FloatRect rcContent = pEdit->GetContentRect(); + float fItemHeight = rcContent.Height(); +@@ -1694,22 +1625,20 @@ void CPDFSDK_AppStream::SetAsListBox() { + CFX_FloatRect(rcClient.left, fy - fItemHeight, rcClient.right, fy); + { + AutoClosedQCommand q(&sList); +- sList << GetColorAppStream(CFX_Color(CFX_Color::kRGB, 0, 51.0f / 255.0f, +- 113.0f / 255.0f), +- true) +- << rcItem.left << " " << rcItem.bottom << " " << rcItem.Width() +- << " " << rcItem.Height() << " " << kAppendRectOperator << " " +- << kFillOperator << "\n"; ++ sList << GetFillColorAppStream(CFX_Color( ++ CFX_Color::Type::kRGB, 0, 51.0f / 255.0f, 113.0f / 255.0f)); ++ WriteAppendRect(sList, rcItem); ++ sList << kFillOperator << "\n"; + } + + AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator); +- sList << GetColorAppStream(CFX_Color(CFX_Color::kGray, 1), true) ++ sList << GetFillColorAppStream(CFX_Color(CFX_Color::Type::kGray, 1)) + << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0); + } else { + CFX_Color crText = widget_->GetTextPWLColor(); + + AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator); +- sList << GetColorAppStream(crText, true) ++ sList << GetFillColorAppStream(crText) + << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0); + } + +@@ -1722,9 +1651,8 @@ void CPDFSDK_AppStream::SetAsListBox() { + kMarkedSequenceEndOperator); + AutoClosedQCommand q(&sBody); + +- sBody << rcClient.left << " " << rcClient.bottom << " " << rcClient.Width() +- << " " << rcClient.Height() << " " << kAppendRectOperator << "\n" +- << kSetNonZeroWindingClipOperator << "\n" ++ WriteAppendRect(sBody, rcClient); ++ sBody << kSetNonZeroWindingClipOperator << "\n" + << kEndPathNoFillOrStrokeOperator << "\n" + << sList.str(); + } +@@ -1733,37 +1661,37 @@ void CPDFSDK_AppStream::SetAsListBox() { + ByteString()); + } + +-void CPDFSDK_AppStream::SetAsTextField(Optional sValue) { ++void CPDFSDK_AppStream::SetAsTextField(absl::optional sValue) { + CPDF_FormControl* pControl = widget_->GetFormControl(); + CPDF_FormField* pField = pControl->GetField(); +- std::ostringstream sBody; +- std::ostringstream sLines; ++ fxcrt::ostringstream sBody; ++ fxcrt::ostringstream sLines; + + // Font map must outlive |pEdit|. +- CBA_FontMap font_map(widget_->GetPDFPage()->GetDocument(), +- widget_->GetPDFAnnot()->GetAnnotDict()); ++ CPDF_BAFontMap font_map(widget_->GetPDFPage()->GetDocument(), ++ widget_->GetPDFAnnot()->GetMutableAnnotDict(), "N"); + +- auto pEdit = pdfium::MakeUnique(); ++ auto pEdit = std::make_unique(); + pEdit->EnableRefresh(false); + pEdit->SetFontMap(&font_map); + + CFX_FloatRect rcClient = widget_->GetClientRect(); + pEdit->SetPlateRect(rcClient); +- pEdit->SetAlignmentH(pControl->GetControlAlignment(), true); ++ pEdit->SetAlignmentH(pControl->GetControlAlignment()); + + uint32_t dwFieldFlags = pField->GetFieldFlags(); + bool bMultiLine = dwFieldFlags & pdfium::form_flags::kTextMultiline; + if (bMultiLine) { +- pEdit->SetMultiLine(true, true); +- pEdit->SetAutoReturn(true, true); ++ pEdit->SetMultiLine(true); ++ pEdit->SetAutoReturn(true); + } else { +- pEdit->SetAlignmentV(1, true); ++ pEdit->SetAlignmentV(1); + } + + uint16_t subWord = 0; + if (dwFieldFlags & pdfium::form_flags::kTextPassword) { + subWord = '*'; +- pEdit->SetPasswordChar(subWord, true); ++ pEdit->SetPasswordChar(subWord); + } + + int nMaxLen = pField->GetMaxLen(); +@@ -1778,24 +1706,25 @@ void CPDFSDK_AppStream::SetAsTextField(Optional sValue) { + if (nMaxLen > 0) { + if (bCharArray) { + pEdit->SetCharArray(nMaxLen); +- if (IsFloatZero(fFontSize)) { ++ if (FXSYS_IsFloatZero(fFontSize)) { + fFontSize = CPWL_Edit::GetCharArrayAutoFontSize( + font_map.GetPDFFont(0).Get(), rcClient, nMaxLen); + } + } else { + if (sValue.has_value()) +- nMaxLen = sValue.value().GetLength(); ++ nMaxLen = pdfium::base::checked_cast(sValue.value().GetLength()); + pEdit->SetLimitChar(nMaxLen); + } + } + +- if (IsFloatZero(fFontSize)) +- pEdit->SetAutoFontSize(true, true); ++ if (FXSYS_IsFloatZero(fFontSize)) ++ pEdit->SetAutoFontSize(true); + else + pEdit->SetFontSize(fFontSize); + + pEdit->Initialize(); + pEdit->SetText(sValue.value_or(pField->GetValue())); ++ pEdit->Paint(); + + CFX_FloatRect rcContent = pEdit->GetContentRect(); + ByteString sEdit = +@@ -1809,63 +1738,57 @@ void CPDFSDK_AppStream::SetAsTextField(Optional sValue) { + + if (rcContent.Width() > rcClient.Width() || + rcContent.Height() > rcClient.Height()) { +- sBody << rcClient.left << " " << rcClient.bottom << " " +- << rcClient.Width() << " " << rcClient.Height() << " " +- << kAppendRectOperator << "\n" +- << kSetNonZeroWindingClipOperator << "\n" ++ WriteAppendRect(sBody, rcClient); ++ sBody << kSetNonZeroWindingClipOperator << "\n" + << kEndPathNoFillOrStrokeOperator << "\n"; + } + CFX_Color crText = widget_->GetTextPWLColor(); + + AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator); +- sBody << GetColorAppStream(crText, true) << sEdit; ++ sBody << GetFillColorAppStream(crText) << sEdit; + } + + if (bCharArray) { + switch (widget_->GetBorderStyle()) { +- case BorderStyle::SOLID: { ++ case BorderStyle::kSolid: { + ByteString sColor = +- GetColorAppStream(widget_->GetBorderPWLColor(), false); ++ GetStrokeColorAppStream(widget_->GetBorderPWLColor()); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q(&sLines); + sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator + << "\n" +- << GetColorAppStream(widget_->GetBorderPWLColor(), false) ++ << GetStrokeColorAppStream(widget_->GetBorderPWLColor()) + << " 2 " << kSetLineCapStyleOperator << " 0 " + << kSetLineJoinStyleOperator << "\n"; + ++ const float width = rcClient.right - rcClient.left; + for (int32_t i = 1; i < nMaxLen; ++i) { +- sLines << rcClient.left + +- ((rcClient.right - rcClient.left) / nMaxLen) * i +- << " " << rcClient.bottom << " " << kMoveToOperator << "\n" +- << rcClient.left + +- ((rcClient.right - rcClient.left) / nMaxLen) * i +- << " " << rcClient.top << " " << kLineToOperator << " " +- << kStrokeOperator << "\n"; ++ const float left = rcClient.left + (width / nMaxLen) * i; ++ WriteMove(sLines, {left, rcClient.bottom}); ++ WriteLine(sLines, {left, rcClient.top}); ++ sLines << kStrokeOperator << "\n"; + } + } + break; + } +- case BorderStyle::DASH: { ++ case BorderStyle::kDash: { + ByteString sColor = +- GetColorAppStream(widget_->GetBorderPWLColor(), false); ++ GetStrokeColorAppStream(widget_->GetBorderPWLColor()); + if (sColor.GetLength() > 0) { + CPWL_Dash dsBorder = CPWL_Dash(3, 3, 0); + AutoClosedQCommand q(&sLines); + sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator + << "\n" +- << GetColorAppStream(widget_->GetBorderPWLColor(), false) +- << "[" << dsBorder.nDash << " " << dsBorder.nGap << "] " ++ << GetStrokeColorAppStream(widget_->GetBorderPWLColor()) << "[" ++ << dsBorder.nDash << " " << dsBorder.nGap << "] " + << dsBorder.nPhase << " " << kSetDashOperator << "\n"; + ++ const float width = rcClient.right - rcClient.left; + for (int32_t i = 1; i < nMaxLen; ++i) { +- sLines << rcClient.left + +- ((rcClient.right - rcClient.left) / nMaxLen) * i +- << " " << rcClient.bottom << " " << kMoveToOperator << "\n" +- << rcClient.left + +- ((rcClient.right - rcClient.left) / nMaxLen) * i +- << " " << rcClient.top << " " << kLineToOperator << " " +- << kStrokeOperator << "\n"; ++ const float left = rcClient.left + (width / nMaxLen) * i; ++ WriteMove(sLines, {left, rcClient.bottom}); ++ WriteLine(sLines, {left, rcClient.top}); ++ sLines << kStrokeOperator << "\n"; + } + } + break; +@@ -1883,22 +1806,17 @@ void CPDFSDK_AppStream::SetAsTextField(Optional sValue) { + + void CPDFSDK_AppStream::AddImage(const ByteString& sAPType, + CPDF_Stream* pImage) { +- CPDF_Stream* pStream = dict_->GetStreamFor(sAPType); +- CPDF_Dictionary* pStreamDict = pStream->GetDict(); ++ RetainPtr pStream = dict_->GetMutableStreamFor(sAPType); ++ RetainPtr pStreamDict = pStream->GetMutableDict(); + ByteString sImageAlias = "IMG"; + +- if (CPDF_Dictionary* pImageDict = pImage->GetDict()) { +- sImageAlias = pImageDict->GetStringFor("Name"); +- if (sImageAlias.IsEmpty()) +- sImageAlias = "IMG"; +- } +- +- CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources"); +- if (!pStreamResList) +- pStreamResList = pStreamDict->SetNewFor("Resources"); ++ RetainPtr pImageDict = pImage->GetDict(); ++ if (pImageDict) ++ sImageAlias = pImageDict->GetByteStringFor("Name"); + +- CPDF_Dictionary* pXObject = +- pStreamResList->SetNewFor("XObject"); ++ RetainPtr pStreamResList = ++ pStreamDict->GetOrCreateDictFor("Resources"); ++ auto pXObject = pStreamResList->SetNewFor("XObject"); + pXObject->SetNewFor(sImageAlias, + widget_->GetPageView()->GetPDFDocument(), + pImage->GetObjNum()); +@@ -1907,48 +1825,57 @@ void CPDFSDK_AppStream::AddImage(const ByteString& sAPType, + void CPDFSDK_AppStream::Write(const ByteString& sAPType, + const ByteString& sContents, + const ByteString& sAPState) { +- CPDF_Stream* pStream = nullptr; +- CPDF_Dictionary* pParentDict = nullptr; ++ RetainPtr pParentDict; ++ ByteString key; + if (sAPState.IsEmpty()) { +- pParentDict = dict_.Get(); +- pStream = dict_->GetStreamFor(sAPType); ++ pParentDict = dict_; ++ key = sAPType; + } else { +- CPDF_Dictionary* pAPTypeDict = dict_->GetDictFor(sAPType); +- if (!pAPTypeDict) +- pAPTypeDict = dict_->SetNewFor(sAPType); +- +- pParentDict = pAPTypeDict; +- pStream = pAPTypeDict->GetStreamFor(sAPState); ++ pParentDict = dict_->GetOrCreateDictFor(sAPType); ++ key = sAPState; + } + +- if (!pStream) { +- CPDF_Document* doc = widget_->GetPageView()->GetPDFDocument(); +- pStream = doc->NewIndirect(); +- pParentDict->SetNewFor(sAPType, doc, pStream->GetObjNum()); ++ RetainPtr pOrigStreamDict; ++ ++ // If `pStream` is created by CreateModifiedAPStream(), then it is safe to ++ // edit, as it is not shared. ++ RetainPtr pStream = pParentDict->GetMutableStreamFor(key); ++ CPDF_Document* doc = widget_->GetPageView()->GetPDFDocument(); ++ if (!doc->IsModifiedAPStream(pStream.Get())) { ++ if (pStream) ++ pOrigStreamDict = pStream->GetMutableDict(); ++ pStream.Reset(doc->CreateModifiedAPStream()); ++ pParentDict->SetNewFor(key, doc, pStream->GetObjNum()); + } + +- CPDF_Dictionary* pStreamDict = pStream->GetDict(); ++ RetainPtr pStreamDict = pStream->GetMutableDict(); + if (!pStreamDict) { +- auto pNewDict = +- widget_->GetPDFAnnot()->GetDocument()->New(); +- pStreamDict = pNewDict.Get(); ++ pStreamDict = doc->New(); + pStreamDict->SetNewFor("Type", "XObject"); + pStreamDict->SetNewFor("Subtype", "Form"); + pStreamDict->SetNewFor("FormType", 1); +- pStream->InitStream({}, std::move(pNewDict)); ++ ++ if (pOrigStreamDict) { ++ RetainPtr pResources = ++ pOrigStreamDict->GetDictFor("Resources"); ++ if (pResources) ++ pStreamDict->SetFor("Resources", pResources->Clone()); ++ } ++ ++ pStream->InitStreamWithEmptyData(pStreamDict); + } + pStreamDict->SetMatrixFor("Matrix", widget_->GetMatrix()); + pStreamDict->SetRectFor("BBox", widget_->GetRotatedRect()); + pStream->SetDataAndRemoveFilter(sContents.raw_span()); + } + +-void CPDFSDK_AppStream::Remove(const ByteString& sAPType) { ++void CPDFSDK_AppStream::Remove(ByteStringView sAPType) { + dict_->RemoveFor(sAPType); + } + + ByteString CPDFSDK_AppStream::GetBackgroundAppStream() const { + CFX_Color crBackground = widget_->GetFillPWLColor(); +- if (crBackground.nColorType != CFX_Color::kTransparent) ++ if (crBackground.nColorType != CFX_Color::Type::kTransparent) + return GetRectFillAppStream(widget_->GetRotatedRect(), crBackground); + + return ByteString(); +@@ -1966,18 +1893,18 @@ ByteString CPDFSDK_AppStream::GetBorderAppStream() const { + + BorderStyle nBorderStyle = widget_->GetBorderStyle(); + switch (nBorderStyle) { +- case BorderStyle::DASH: ++ case BorderStyle::kDash: + dsBorder = CPWL_Dash(3, 3, 0); + break; +- case BorderStyle::BEVELED: ++ case BorderStyle::kBeveled: + fBorderWidth *= 2; +- crLeftTop = CFX_Color(CFX_Color::kGray, 1); ++ crLeftTop = CFX_Color(CFX_Color::Type::kGray, 1); + crRightBottom = crBackground / 2.0f; + break; +- case BorderStyle::INSET: ++ case BorderStyle::kInset: + fBorderWidth *= 2; +- crLeftTop = CFX_Color(CFX_Color::kGray, 0.5); +- crRightBottom = CFX_Color(CFX_Color::kGray, 0.75); ++ crLeftTop = CFX_Color(CFX_Color::Type::kGray, 0.5); ++ crRightBottom = CFX_Color(CFX_Color::Type::kGray, 0.75); + break; + default: + break; +diff --git a/fpdfsdk/cpdfsdk_appstream.h b/fpdfsdk/cpdfsdk_appstream.h +index 8a38a8696..aabef2423 100644 +--- a/fpdfsdk/cpdfsdk_appstream.h ++++ b/fpdfsdk/cpdfsdk_appstream.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,8 +8,9 @@ + #define FPDFSDK_CPDFSDK_APPSTREAM_H_ + + #include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CPDFSDK_Widget; + class CPDF_Dictionary; +@@ -23,16 +24,16 @@ class CPDFSDK_AppStream { + void SetAsPushButton(); + void SetAsCheckBox(); + void SetAsRadioButton(); +- void SetAsComboBox(Optional sValue); ++ void SetAsComboBox(absl::optional sValue); + void SetAsListBox(); +- void SetAsTextField(Optional sValue); ++ void SetAsTextField(absl::optional sValue); + + private: + void AddImage(const ByteString& sAPType, CPDF_Stream* pImage); + void Write(const ByteString& sAPType, + const ByteString& sContents, + const ByteString& sAPState); +- void Remove(const ByteString& sAPType); ++ void Remove(ByteStringView sAPType); + + ByteString GetBackgroundAppStream() const; + ByteString GetBorderAppStream() const; +diff --git a/fpdfsdk/cpdfsdk_baannot.cpp b/fpdfsdk/cpdfsdk_baannot.cpp +index e21416328..a1751f967 100644 +--- a/fpdfsdk/cpdfsdk_baannot.cpp ++++ b/fpdfsdk/cpdfsdk_baannot.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,56 +6,65 @@ + + #include "fpdfsdk/cpdfsdk_baannot.h" + +-#include +-#include ++#include + + #include "constants/annotation_common.h" + #include "constants/annotation_flags.h" ++#include "constants/form_fields.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" +-#include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" ++#include "core/fpdfapi/parser/fpdf_parser_utility.h" ++#include "core/fxge/cfx_drawutils.h" ++#include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fpdfsdk/cpdfsdk_pageview.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" + + CPDFSDK_BAAnnot::CPDFSDK_BAAnnot(CPDF_Annot* pAnnot, + CPDFSDK_PageView* pPageView) + : CPDFSDK_Annot(pPageView), m_pAnnot(pAnnot) {} + +-CPDFSDK_BAAnnot::~CPDFSDK_BAAnnot() {} ++CPDFSDK_BAAnnot::~CPDFSDK_BAAnnot() = default; + + CPDFSDK_BAAnnot* CPDFSDK_BAAnnot::AsBAAnnot() { + return this; + } + +-CPDF_Annot* CPDFSDK_BAAnnot::GetPDFAnnot() const { +- return m_pAnnot.Get(); ++CPDFSDK_Annot::UnsafeInputHandlers* CPDFSDK_BAAnnot::GetUnsafeInputHandlers() { ++ return this; + } + +-CPDF_Annot* CPDFSDK_BAAnnot::GetPDFPopupAnnot() const { +- return m_pAnnot->GetPopupAnnot(); ++CPDF_Annot* CPDFSDK_BAAnnot::GetPDFAnnot() const { ++ return m_pAnnot; + } + +-CPDF_Dictionary* CPDFSDK_BAAnnot::GetAnnotDict() const { ++const CPDF_Dictionary* CPDFSDK_BAAnnot::GetAnnotDict() const { + return m_pAnnot->GetAnnotDict(); + } + +-CPDF_Dictionary* CPDFSDK_BAAnnot::GetAPDict() const { +- CPDF_Dictionary* pAPDict = +- GetAnnotDict()->GetDictFor(pdfium::annotation::kAP); +- if (pAPDict) +- return pAPDict; +- return GetAnnotDict()->SetNewFor(pdfium::annotation::kAP); ++RetainPtr CPDFSDK_BAAnnot::GetMutableAnnotDict() { ++ return m_pAnnot->GetMutableAnnotDict(); + } + +-void CPDFSDK_BAAnnot::SetRect(const CFX_FloatRect& rect) { +- ASSERT(rect.right - rect.left >= 1.0f); +- ASSERT(rect.top - rect.bottom >= 1.0f); +- GetAnnotDict()->SetRectFor(pdfium::annotation::kRect, rect); ++RetainPtr CPDFSDK_BAAnnot::GetAPDict() { ++ return GetMutableAnnotDict()->GetOrCreateDictFor(pdfium::annotation::kAP); ++} ++ ++void CPDFSDK_BAAnnot::ClearCachedAnnotAP() { ++ m_pAnnot->ClearCachedAP(); ++} ++ ++bool CPDFSDK_BAAnnot::IsFocusableAnnot( ++ const CPDF_Annot::Subtype& annot_type) const { ++ return pdfium::Contains( ++ GetPageView()->GetFormFillEnv()->GetFocusableAnnotSubtypes(), annot_type); + } + + CFX_FloatRect CPDFSDK_BAAnnot::GetRect() const { +@@ -68,10 +77,9 @@ CPDF_Annot::Subtype CPDFSDK_BAAnnot::GetAnnotSubtype() const { + + void CPDFSDK_BAAnnot::DrawAppearance(CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device, +- CPDF_Annot::AppearanceMode mode, +- const CPDF_RenderOptions* pOptions) { +- m_pAnnot->DrawAppearance(m_pPageView->GetPDFPage(), pDevice, mtUser2Device, +- mode, pOptions); ++ CPDF_Annot::AppearanceMode mode) { ++ m_pAnnot->DrawAppearance(GetPageView()->GetPDFPage(), pDevice, mtUser2Device, ++ mode); + } + + bool CPDFSDK_BAAnnot::IsAppearanceValid() { +@@ -79,11 +87,12 @@ bool CPDFSDK_BAAnnot::IsAppearanceValid() { + } + + void CPDFSDK_BAAnnot::SetAnnotName(const WideString& sName) { +- CPDF_Dictionary* pDict = GetAnnotDict(); +- if (sName.IsEmpty()) ++ RetainPtr pDict = GetMutableAnnotDict(); ++ if (sName.IsEmpty()) { + pDict->RemoveFor(pdfium::annotation::kNM); +- else +- pDict->SetNewFor(pdfium::annotation::kNM, sName); ++ return; ++ } ++ pDict->SetNewFor(pdfium::annotation::kNM, sName.AsStringView()); + } + + WideString CPDFSDK_BAAnnot::GetAnnotName() const { +@@ -91,71 +100,65 @@ WideString CPDFSDK_BAAnnot::GetAnnotName() const { + } + + void CPDFSDK_BAAnnot::SetFlags(uint32_t nFlags) { +- GetAnnotDict()->SetNewFor(pdfium::annotation::kF, +- static_cast(nFlags)); ++ GetMutableAnnotDict()->SetNewFor(pdfium::annotation::kF, ++ static_cast(nFlags)); + } + + uint32_t CPDFSDK_BAAnnot::GetFlags() const { + return GetAnnotDict()->GetIntegerFor(pdfium::annotation::kF); + } + +-void CPDFSDK_BAAnnot::SetAppState(const ByteString& str) { +- CPDF_Dictionary* pDict = GetAnnotDict(); +- if (str.IsEmpty()) +- pDict->RemoveFor(pdfium::annotation::kAS); +- else +- pDict->SetNewFor(pdfium::annotation::kAS, str, false); ++void CPDFSDK_BAAnnot::SetAppStateOff() { ++ RetainPtr pDict = GetMutableAnnotDict(); ++ pDict->SetNewFor(pdfium::annotation::kAS, "Off", false); + } + + ByteString CPDFSDK_BAAnnot::GetAppState() const { +- return GetAnnotDict()->GetStringFor(pdfium::annotation::kAS); ++ return GetAnnotDict()->GetByteStringFor(pdfium::annotation::kAS); + } + + void CPDFSDK_BAAnnot::SetBorderWidth(int nWidth) { +- CPDF_Array* pBorder = +- GetAnnotDict()->GetArrayFor(pdfium::annotation::kBorder); ++ RetainPtr pAnnotDict = GetMutableAnnotDict(); ++ RetainPtr pBorder = ++ pAnnotDict->GetMutableArrayFor(pdfium::annotation::kBorder); + if (pBorder) { + pBorder->SetNewAt(2, nWidth); +- } else { +- CPDF_Dictionary* pBSDict = GetAnnotDict()->GetDictFor("BS"); +- if (!pBSDict) +- pBSDict = GetAnnotDict()->SetNewFor("BS"); +- pBSDict->SetNewFor("W", nWidth); ++ return; + } ++ pAnnotDict->GetOrCreateDictFor("BS")->SetNewFor("W", nWidth); + } + + int CPDFSDK_BAAnnot::GetBorderWidth() const { +- if (const CPDF_Array* pBorder = +- GetAnnotDict()->GetArrayFor(pdfium::annotation::kBorder)) { ++ RetainPtr pBorder = ++ GetAnnotDict()->GetArrayFor(pdfium::annotation::kBorder); ++ if (pBorder) + return pBorder->GetIntegerAt(2); +- } + +- if (CPDF_Dictionary* pBSDict = GetAnnotDict()->GetDictFor("BS")) ++ RetainPtr pBSDict = GetAnnotDict()->GetDictFor("BS"); ++ if (pBSDict) + return pBSDict->GetIntegerFor("W", 1); + + return 1; + } + + void CPDFSDK_BAAnnot::SetBorderStyle(BorderStyle nStyle) { +- CPDF_Dictionary* pBSDict = GetAnnotDict()->GetDictFor("BS"); +- if (!pBSDict) +- pBSDict = GetAnnotDict()->SetNewFor("BS"); +- ++ RetainPtr pBSDict = ++ GetMutableAnnotDict()->GetOrCreateDictFor("BS"); + const char* name = nullptr; + switch (nStyle) { +- case BorderStyle::SOLID: ++ case BorderStyle::kSolid: + name = "S"; + break; +- case BorderStyle::DASH: ++ case BorderStyle::kDash: + name = "D"; + break; +- case BorderStyle::BEVELED: ++ case BorderStyle::kBeveled: + name = "B"; + break; +- case BorderStyle::INSET: ++ case BorderStyle::kInset: + name = "I"; + break; +- case BorderStyle::UNDERLINE: ++ case BorderStyle::kUnderline: + name = "U"; + break; + default: +@@ -165,32 +168,32 @@ void CPDFSDK_BAAnnot::SetBorderStyle(BorderStyle nStyle) { + } + + BorderStyle CPDFSDK_BAAnnot::GetBorderStyle() const { +- CPDF_Dictionary* pBSDict = GetAnnotDict()->GetDictFor("BS"); ++ RetainPtr pBSDict = GetAnnotDict()->GetDictFor("BS"); + if (pBSDict) { +- ByteString sBorderStyle = pBSDict->GetStringFor("S", "S"); ++ ByteString sBorderStyle = pBSDict->GetByteStringFor("S", "S"); + if (sBorderStyle == "S") +- return BorderStyle::SOLID; ++ return BorderStyle::kSolid; + if (sBorderStyle == "D") +- return BorderStyle::DASH; ++ return BorderStyle::kDash; + if (sBorderStyle == "B") +- return BorderStyle::BEVELED; ++ return BorderStyle::kBeveled; + if (sBorderStyle == "I") +- return BorderStyle::INSET; ++ return BorderStyle::kInset; + if (sBorderStyle == "U") +- return BorderStyle::UNDERLINE; ++ return BorderStyle::kUnderline; + } + +- const CPDF_Array* pBorder = ++ RetainPtr pBorder = + GetAnnotDict()->GetArrayFor(pdfium::annotation::kBorder); + if (pBorder) { + if (pBorder->size() >= 4) { +- const CPDF_Array* pDP = pBorder->GetArrayAt(3); ++ RetainPtr pDP = pBorder->GetArrayAt(3); + if (pDP && pDP->size() > 0) +- return BorderStyle::DASH; ++ return BorderStyle::kDash; + } + } + +- return BorderStyle::SOLID; ++ return BorderStyle::kSolid; + } + + bool CPDFSDK_BAAnnot::IsVisible() const { +@@ -205,7 +208,7 @@ CPDF_Action CPDFSDK_BAAnnot::GetAction() const { + } + + CPDF_AAction CPDFSDK_BAAnnot::GetAAction() const { +- return CPDF_AAction(GetAnnotDict()->GetDictFor("AA")); ++ return CPDF_AAction(GetAnnotDict()->GetDictFor(pdfium::form_fields::kAA)); + } + + CPDF_Action CPDFSDK_BAAnnot::GetAAction(CPDF_AAction::AActionType eAAT) { +@@ -213,15 +216,40 @@ CPDF_Action CPDFSDK_BAAnnot::GetAAction(CPDF_AAction::AActionType eAAT) { + if (AAction.ActionExist(eAAT)) + return AAction.GetAction(eAAT); + +- if (eAAT == CPDF_AAction::kButtonUp) ++ if (eAAT == CPDF_AAction::kButtonUp || eAAT == CPDF_AAction::kKeyStroke) + return GetAction(); + + return CPDF_Action(nullptr); + } + + void CPDFSDK_BAAnnot::SetOpenState(bool bOpenState) { +- if (CPDF_Annot* pAnnot = m_pAnnot->GetPopupAnnot()) +- pAnnot->SetOpenState(bOpenState); ++ m_pAnnot->SetPopupAnnotOpenState(bOpenState); ++} ++ ++void CPDFSDK_BAAnnot::UpdateAnnotRects() { ++ std::vector rects; ++ rects.push_back(GetRect()); ++ ++ absl::optional annot_rect = m_pAnnot->GetPopupAnnotRect(); ++ if (annot_rect.has_value()) ++ rects.push_back(annot_rect.value()); ++ ++ // Make the rects round up to avoid https://crbug.com/662804 ++ for (CFX_FloatRect& rect : rects) ++ rect.Inflate(1, 1); ++ ++ GetPageView()->UpdateRects(rects); ++} ++ ++void CPDFSDK_BAAnnot::InvalidateRect() { ++ CFX_FloatRect view_bounding_box = GetViewBBox(); ++ if (view_bounding_box.IsEmpty()) ++ return; ++ ++ view_bounding_box.Inflate(1, 1); ++ view_bounding_box.Normalize(); ++ FX_RECT rect = view_bounding_box.GetOuterRect(); ++ GetPageView()->GetFormFillEnv()->Invalidate(GetPage(), rect); + } + + int CPDFSDK_BAAnnot::GetLayoutOrder() const { +@@ -230,3 +258,173 @@ int CPDFSDK_BAAnnot::GetLayoutOrder() const { + + return CPDFSDK_Annot::GetLayoutOrder(); + } ++ ++void CPDFSDK_BAAnnot::OnDraw(CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device, ++ bool bDrawAnnots) { ++ if (!IsVisible()) ++ return; ++ ++ const CPDF_Annot::Subtype annot_type = GetAnnotSubtype(); ++ if (bDrawAnnots && annot_type == CPDF_Annot::Subtype::POPUP) { ++ DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::AppearanceMode::kNormal); ++ return; ++ } ++ ++ if (!is_focused_ || !IsFocusableAnnot(annot_type) || ++ this != GetPageView()->GetFormFillEnv()->GetFocusAnnot()) { ++ return; ++ } ++ ++ CFX_FloatRect view_bounding_box = GetViewBBox(); ++ if (view_bounding_box.IsEmpty()) ++ return; ++ ++ view_bounding_box.Normalize(); ++ CFX_DrawUtils::DrawFocusRect(pDevice, mtUser2Device, view_bounding_box); ++} ++ ++bool CPDFSDK_BAAnnot::DoHitTest(const CFX_PointF& point) { ++ return false; ++} ++ ++CFX_FloatRect CPDFSDK_BAAnnot::GetViewBBox() { ++ return GetRect(); ++} ++ ++void CPDFSDK_BAAnnot::OnMouseEnter(Mask nFlags) { ++ SetOpenState(true); ++ UpdateAnnotRects(); ++} ++ ++void CPDFSDK_BAAnnot::OnMouseExit(Mask nFlags) { ++ SetOpenState(false); ++ UpdateAnnotRects(); ++} ++ ++bool CPDFSDK_BAAnnot::OnLButtonDown(Mask nFlags, ++ const CFX_PointF& point) { ++ return false; ++} ++ ++bool CPDFSDK_BAAnnot::OnLButtonUp(Mask nFlags, ++ const CFX_PointF& point) { ++ return false; ++} ++ ++bool CPDFSDK_BAAnnot::OnLButtonDblClk(Mask nFlags, ++ const CFX_PointF& point) { ++ return false; ++} ++ ++bool CPDFSDK_BAAnnot::OnMouseMove(Mask nFlags, ++ const CFX_PointF& point) { ++ return false; ++} ++ ++bool CPDFSDK_BAAnnot::OnMouseWheel(Mask nFlags, ++ const CFX_PointF& point, ++ const CFX_Vector& delta) { ++ return false; ++} ++ ++bool CPDFSDK_BAAnnot::OnRButtonDown(Mask nFlags, ++ const CFX_PointF& point) { ++ return false; ++} ++ ++bool CPDFSDK_BAAnnot::OnRButtonUp(Mask nFlags, ++ const CFX_PointF& point) { ++ return false; ++} ++ ++bool CPDFSDK_BAAnnot::OnChar(uint32_t nChar, Mask nFlags) { ++ return false; ++} ++ ++bool CPDFSDK_BAAnnot::OnKeyDown(FWL_VKEYCODE nKeyCode, ++ Mask nFlags) { ++ // OnKeyDown() is implemented only for link annotations for now. As ++ // OnKeyDown() is implemented for other subtypes, following check should be ++ // modified. ++ if (nKeyCode != FWL_VKEY_Return || ++ GetAnnotSubtype() != CPDF_Annot::Subtype::LINK) { ++ return false; ++ } ++ ++ CPDF_Action action = GetAAction(CPDF_AAction::kKeyStroke); ++ CPDFSDK_FormFillEnvironment* env = GetPageView()->GetFormFillEnv(); ++ if (action.HasDict()) { ++ return env->DoActionLink(action, CPDF_AAction::kKeyStroke, nFlags); ++ } ++ ++ return env->DoActionDestination(GetDestination()); ++} ++ ++bool CPDFSDK_BAAnnot::OnSetFocus(Mask nFlags) { ++ if (!IsFocusableAnnot(GetAnnotSubtype())) ++ return false; ++ ++ is_focused_ = true; ++ InvalidateRect(); ++ return true; ++} ++ ++bool CPDFSDK_BAAnnot::OnKillFocus(Mask nFlags) { ++ if (!IsFocusableAnnot(GetAnnotSubtype())) ++ return false; ++ ++ is_focused_ = false; ++ InvalidateRect(); ++ return true; ++} ++ ++bool CPDFSDK_BAAnnot::CanUndo() { ++ return false; ++} ++ ++bool CPDFSDK_BAAnnot::CanRedo() { ++ return false; ++} ++ ++bool CPDFSDK_BAAnnot::Undo() { ++ return false; ++} ++ ++bool CPDFSDK_BAAnnot::Redo() { ++ return false; ++} ++ ++WideString CPDFSDK_BAAnnot::GetText() { ++ return WideString(); ++} ++ ++WideString CPDFSDK_BAAnnot::GetSelectedText() { ++ return WideString(); ++} ++ ++void CPDFSDK_BAAnnot::ReplaceAndKeepSelection(const WideString& text) {} ++ ++void CPDFSDK_BAAnnot::ReplaceSelection(const WideString& text) {} ++ ++bool CPDFSDK_BAAnnot::SelectAllText() { ++ return false; ++} ++ ++bool CPDFSDK_BAAnnot::SetIndexSelected(int index, bool selected) { ++ return false; ++} ++ ++bool CPDFSDK_BAAnnot::IsIndexSelected(int index) { ++ return false; ++} ++ ++CPDF_Dest CPDFSDK_BAAnnot::GetDestination() const { ++ if (m_pAnnot->GetSubtype() != CPDF_Annot::Subtype::LINK) ++ return CPDF_Dest(nullptr); ++ ++ // Link annotations can have "Dest" entry defined as an explicit array. ++ // See ISO 32000-1:2008 spec, section 12.3.2.1. ++ return CPDF_Dest::Create(GetPageView()->GetPDFDocument(), ++ GetAnnotDict()->GetDirectObjectFor("Dest")); ++} +diff --git a/fpdfsdk/cpdfsdk_baannot.h b/fpdfsdk/cpdfsdk_baannot.h +index 482aa0aba..0da13d810 100644 +--- a/fpdfsdk/cpdfsdk_baannot.h ++++ b/fpdfsdk/cpdfsdk_baannot.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,36 +12,49 @@ + #include "core/fpdfdoc/cpdf_annot.h" + #include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/unowned_ptr.h" + #include "core/fxge/cfx_renderdevice.h" + #include "fpdfsdk/cpdfsdk_annot.h" + + class CFX_Matrix; + class CPDF_Dictionary; +-class CPDF_RenderOptions; + class CPDFSDK_PageView; + +-class CPDFSDK_BAAnnot : public CPDFSDK_Annot { ++class CPDFSDK_BAAnnot : public CPDFSDK_Annot, ++ CPDFSDK_Annot::UnsafeInputHandlers { + public: + CPDFSDK_BAAnnot(CPDF_Annot* pAnnot, CPDFSDK_PageView* pPageView); + ~CPDFSDK_BAAnnot() override; + +- // CPDFSDK_Annot ++ // CPDFSDK_Annot: + CPDFSDK_BAAnnot* AsBAAnnot() override; ++ CPDFSDK_Annot::UnsafeInputHandlers* GetUnsafeInputHandlers() override; + CPDF_Annot::Subtype GetAnnotSubtype() const override; +- void SetRect(const CFX_FloatRect& rect) override; + CFX_FloatRect GetRect() const override; + CPDF_Annot* GetPDFAnnot() const override; + int GetLayoutOrder() const override; ++ void OnDraw(CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device, ++ bool bDrawAnnots) override; ++ bool DoHitTest(const CFX_PointF& point) override; ++ CFX_FloatRect GetViewBBox() override; ++ bool CanUndo() override; ++ bool CanRedo() override; ++ bool Undo() override; ++ bool Redo() override; ++ WideString GetText() override; ++ WideString GetSelectedText() override; ++ void ReplaceAndKeepSelection(const WideString& text) override; ++ void ReplaceSelection(const WideString& text) override; ++ bool SelectAllText() override; ++ bool SetIndexSelected(int index, bool selected) override; ++ bool IsIndexSelected(int index) override; + + virtual CPDF_Action GetAAction(CPDF_AAction::AActionType eAAT); + virtual bool IsAppearanceValid(); + virtual void DrawAppearance(CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device, +- CPDF_Annot::AppearanceMode mode, +- const CPDF_RenderOptions* pOptions); +- +- CPDF_Dictionary* GetAnnotDict() const; +- CPDF_Annot* GetPDFPopupAnnot() const; ++ CPDF_Annot::AppearanceMode mode); + + void SetAnnotName(const WideString& sName); + WideString GetAnnotName() const; +@@ -49,7 +62,7 @@ class CPDFSDK_BAAnnot : public CPDFSDK_Annot { + void SetFlags(uint32_t nFlags); + uint32_t GetFlags() const; + +- void SetAppState(const ByteString& str); ++ void SetAppStateOff(); + ByteString GetAppState() const; + + void SetBorderWidth(int nWidth); +@@ -61,14 +74,45 @@ class CPDFSDK_BAAnnot : public CPDFSDK_Annot { + bool IsVisible() const; + + CPDF_Action GetAction() const; +- + CPDF_AAction GetAAction() const; +- +- void SetOpenState(bool bOpenState); ++ CPDF_Dest GetDestination() const; + + protected: +- CPDF_Dictionary* GetAPDict() const; ++ const CPDF_Dictionary* GetAnnotDict() const; ++ RetainPtr GetMutableAnnotDict(); ++ RetainPtr GetAPDict(); ++ void ClearCachedAnnotAP(); ++ bool IsFocusableAnnot(const CPDF_Annot::Subtype& annot_type) const; ++ ++ private: ++ // CPDFSDK_Annot::UnsafeInputHandlers: ++ void OnMouseEnter(Mask nFlags) override; ++ void OnMouseExit(Mask nFlags) override; ++ bool OnLButtonDown(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnLButtonUp(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnLButtonDblClk(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnMouseMove(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnMouseWheel(Mask nFlags, ++ const CFX_PointF& point, ++ const CFX_Vector& delta) override; ++ bool OnRButtonDown(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnRButtonUp(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnChar(uint32_t nChar, Mask nFlags) override; ++ bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask nFlags) override; ++ bool OnSetFocus(Mask nFlags) override; ++ bool OnKillFocus(Mask nFlags) override; ++ ++ void SetOpenState(bool bOpenState); ++ void UpdateAnnotRects(); ++ void InvalidateRect(); + ++ bool is_focused_ = false; + UnownedPtr const m_pAnnot; + }; + +diff --git a/fpdfsdk/cpdfsdk_baannot_embeddertest.cpp b/fpdfsdk/cpdfsdk_baannot_embeddertest.cpp +new file mode 100644 +index 000000000..14362b0bb +--- /dev/null ++++ b/fpdfsdk/cpdfsdk_baannot_embeddertest.cpp +@@ -0,0 +1,93 @@ ++// Copyright 2019 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include ++ ++#include "fpdfsdk/cpdfsdk_annotiterator.h" ++#include "fpdfsdk/cpdfsdk_baannot.h" ++#include "fpdfsdk/cpdfsdk_formfillenvironment.h" ++#include "fpdfsdk/cpdfsdk_helpers.h" ++#include "fpdfsdk/cpdfsdk_pageview.h" ++#include "testing/embedder_test.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++ ++class CPDFSDK_BAAnnotTest : public EmbedderTest { ++ public: ++ void SetUp() override { ++ EmbedderTest::SetUp(); ++ SetUpBAAnnot(); ++ } ++ ++ void TearDown() override { ++ UnloadPage(m_page); ++ EmbedderTest::TearDown(); ++ } ++ ++ void SetUpBAAnnot() { ++ ASSERT_TRUE(OpenDocument("links_highlights_annots.pdf")); ++ m_page = LoadPage(0); ++ ASSERT_TRUE(m_page); ++ ++ m_pFormFillEnv = ++ CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle()); ++ ASSERT_TRUE(m_pFormFillEnv); ++ m_pPageView = ++ m_pFormFillEnv->GetOrCreatePageView(IPDFPageFromFPDFPage(m_page)); ++ ASSERT_TRUE(m_pPageView); ++ } ++ ++ CPDFSDK_FormFillEnvironment* GetFormFillEnv() const { return m_pFormFillEnv; } ++ CPDFSDK_PageView* GetPageView() const { return m_pPageView; } ++ ++ CPDFSDK_Annot* GetNthFocusableAnnot(size_t n) { ++ DCHECK_NE(n, 0); ++ CPDFSDK_AnnotIterator ai(GetPageView(), ++ m_pFormFillEnv->GetFocusableAnnotSubtypes()); ++ CPDFSDK_Annot* pAnnot = ai.GetFirstAnnot(); ++ DCHECK(pAnnot); ++ ++ for (size_t i = 1; i < n; i++) { ++ pAnnot = ai.GetNextAnnot(pAnnot); ++ DCHECK(pAnnot); ++ } ++ ++ return pAnnot; ++ } ++ ++ private: ++ FPDF_PAGE m_page = nullptr; ++ CPDFSDK_PageView* m_pPageView = nullptr; ++ CPDFSDK_FormFillEnvironment* m_pFormFillEnv = nullptr; ++}; ++ ++TEST_F(CPDFSDK_BAAnnotTest, TabToLinkOrHighlightAnnot) { ++ std::vector focusable_annot_types = { ++ CPDF_Annot::Subtype::WIDGET, CPDF_Annot::Subtype::LINK, ++ CPDF_Annot::Subtype::HIGHLIGHT}; ++ ++ GetFormFillEnv()->SetFocusableAnnotSubtypes(focusable_annot_types); ++ ++ // Get link annot. ++ CPDFSDK_Annot* pAnnot = GetNthFocusableAnnot(2); ++ ASSERT_TRUE(pAnnot); ++ EXPECT_EQ(pAnnot->GetAnnotSubtype(), CPDF_Annot::Subtype::LINK); ++ ++ { ++ ObservedPtr observer(pAnnot); ++ EXPECT_TRUE(CPDFSDK_Annot::OnSetFocus(observer, {})); ++ EXPECT_TRUE(CPDFSDK_Annot::OnKillFocus(observer, {})); ++ } ++ ++ // Get highlight annot. ++ pAnnot = GetNthFocusableAnnot(4); ++ ASSERT_TRUE(pAnnot); ++ EXPECT_EQ(pAnnot->GetAnnotSubtype(), CPDF_Annot::Subtype::HIGHLIGHT); ++ ++ { ++ ObservedPtr observer(pAnnot); ++ EXPECT_TRUE(CPDFSDK_Annot::OnSetFocus(observer, {})); ++ EXPECT_TRUE(CPDFSDK_Annot::OnKillFocus(observer, {})); ++ } ++} +diff --git a/fpdfsdk/cpdfsdk_baannothandler.cpp b/fpdfsdk/cpdfsdk_baannothandler.cpp +deleted file mode 100644 +index 6aa6de73e..000000000 +--- a/fpdfsdk/cpdfsdk_baannothandler.cpp ++++ /dev/null +@@ -1,227 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "fpdfsdk/cpdfsdk_baannothandler.h" +- +-#include +-#include +- +-#include "core/fpdfapi/page/cpdf_page.h" +-#include "core/fpdfapi/parser/cpdf_document.h" +-#include "core/fpdfdoc/cpdf_interactiveform.h" +-#include "fpdfsdk/cpdfsdk_annot.h" +-#include "fpdfsdk/cpdfsdk_baannot.h" +-#include "fpdfsdk/cpdfsdk_pageview.h" +-#include "fpdfsdk/formfiller/cffl_formfiller.h" +- +-namespace { +- +-void UpdateAnnotRects(CPDFSDK_PageView* pPageView, CPDFSDK_BAAnnot* pBAAnnot) { +- std::vector rects; +- rects.push_back(pBAAnnot->GetRect()); +- if (CPDF_Annot* pPopupAnnot = pBAAnnot->GetPDFPopupAnnot()) +- rects.push_back(pPopupAnnot->GetRect()); +- +- // Make the rects round up to avoid https://crbug.com/662804 +- for (CFX_FloatRect& rect : rects) +- rect.Inflate(1, 1); +- +- pPageView->UpdateRects(rects); +-} +- +-} // namespace +- +-CPDFSDK_BAAnnotHandler::CPDFSDK_BAAnnotHandler() {} +- +-CPDFSDK_BAAnnotHandler::~CPDFSDK_BAAnnotHandler() {} +- +-void CPDFSDK_BAAnnotHandler::SetFormFillEnvironment( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- // CPDFSDK_BAAnnotHandler does not need it. +-} +- +-bool CPDFSDK_BAAnnotHandler::CanAnswer(CPDFSDK_Annot* pAnnot) { +- return false; +-} +- +-CPDFSDK_Annot* CPDFSDK_BAAnnotHandler::NewAnnot(CPDF_Annot* pAnnot, +- CPDFSDK_PageView* pPage) { +- return new CPDFSDK_BAAnnot(pAnnot, pPage); +-} +- +-void CPDFSDK_BAAnnotHandler::ReleaseAnnot( +- std::unique_ptr pAnnot) { +- // pAnnot deleted by unique_ptr going out of scope. +-} +- +-void CPDFSDK_BAAnnotHandler::OnDraw(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device, +- bool bDrawAnnots) { +- if (pAnnot->AsXFAWidget()) +- return; +- +- if (bDrawAnnots && pAnnot->GetAnnotSubtype() == CPDF_Annot::Subtype::POPUP) { +- pAnnot->AsBAAnnot()->DrawAppearance(pDevice, mtUser2Device, +- CPDF_Annot::Normal, nullptr); +- } +-} +- +-void CPDFSDK_BAAnnotHandler::OnMouseEnter(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) { +- CPDFSDK_BAAnnot* pBAAnnot = (*pAnnot)->AsBAAnnot(); +- pBAAnnot->SetOpenState(true); +- UpdateAnnotRects(pPageView, pBAAnnot); +-} +- +-void CPDFSDK_BAAnnotHandler::OnMouseExit(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) { +- CPDFSDK_BAAnnot* pBAAnnot = (*pAnnot)->AsBAAnnot(); +- pBAAnnot->SetOpenState(false); +- UpdateAnnotRects(pPageView, pBAAnnot); +-} +- +-bool CPDFSDK_BAAnnotHandler::OnLButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::OnLButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::OnLButtonDblClk(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::OnMouseMove(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::OnMouseWheel(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- short zDelta, +- const CFX_PointF& point) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::OnRButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::OnRButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::OnRButtonDblClk(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::OnChar(CPDFSDK_Annot* pAnnot, +- uint32_t nChar, +- uint32_t nFlags) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::OnKeyDown(CPDFSDK_Annot* pAnnot, +- int nKeyCode, +- int nFlag) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::OnKeyUp(CPDFSDK_Annot* pAnnot, +- int nKeyCode, +- int nFlag) { +- return false; +-} +- +-void CPDFSDK_BAAnnotHandler::OnLoad(CPDFSDK_Annot* pAnnot) {} +- +-bool CPDFSDK_BAAnnotHandler::OnSetFocus(ObservedPtr* pAnnot, +- uint32_t nFlag) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::OnKillFocus(ObservedPtr* pAnnot, +- uint32_t nFlag) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::SetIndexSelected( +- ObservedPtr* pAnnot, +- int index, +- bool selected) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::IsIndexSelected(ObservedPtr* pAnnot, +- int index) { +- return false; +-} +- +-CFX_FloatRect CPDFSDK_BAAnnotHandler::GetViewBBox(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot) { +- return pAnnot->GetRect(); +-} +- +-WideString CPDFSDK_BAAnnotHandler::GetText(CPDFSDK_Annot* pAnnot) { +- return WideString(); +-} +- +-WideString CPDFSDK_BAAnnotHandler::GetSelectedText(CPDFSDK_Annot* pAnnot) { +- return WideString(); +-} +- +-void CPDFSDK_BAAnnotHandler::ReplaceSelection(CPDFSDK_Annot* pAnnot, +- const WideString& text) {} +- +-bool CPDFSDK_BAAnnotHandler::CanUndo(CPDFSDK_Annot* pAnnot) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::CanRedo(CPDFSDK_Annot* pAnnot) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::Undo(CPDFSDK_Annot* pAnnot) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::Redo(CPDFSDK_Annot* pAnnot) { +- return false; +-} +- +-bool CPDFSDK_BAAnnotHandler::HitTest(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- const CFX_PointF& point) { +- ASSERT(pPageView); +- ASSERT(pAnnot); +- return GetViewBBox(pPageView, pAnnot).Contains(point); +-} +diff --git a/fpdfsdk/cpdfsdk_baannothandler.h b/fpdfsdk/cpdfsdk_baannothandler.h +deleted file mode 100644 +index 295bf0d4f..000000000 +--- a/fpdfsdk/cpdfsdk_baannothandler.h ++++ /dev/null +@@ -1,103 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef FPDFSDK_CPDFSDK_BAANNOTHANDLER_H_ +-#define FPDFSDK_CPDFSDK_BAANNOTHANDLER_H_ +- +-#include +- +-#include "core/fxcrt/fx_coordinates.h" +-#include "fpdfsdk/ipdfsdk_annothandler.h" +- +-class CFFL_InteractiveFormFiller; +-class CFX_Matrix; +-class CFX_RenderDevice; +-class CPDF_Annot; +-class CPDFSDK_FormFillEnvironment; +-class CPDFSDK_Annot; +-class CPDFSDK_PageView; +- +-class CPDFSDK_BAAnnotHandler final : public IPDFSDK_AnnotHandler { +- public: +- CPDFSDK_BAAnnotHandler(); +- ~CPDFSDK_BAAnnotHandler() override; +- +- // IPDFSDK_AnnotHandler: +- void SetFormFillEnvironment( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) override; +- bool CanAnswer(CPDFSDK_Annot* pAnnot) override; +- CPDFSDK_Annot* NewAnnot(CPDF_Annot* pAnnot, CPDFSDK_PageView* pPage) override; +- void ReleaseAnnot(std::unique_ptr pAnnot) override; +- CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot) override; +- WideString GetText(CPDFSDK_Annot* pAnnot) override; +- WideString GetSelectedText(CPDFSDK_Annot* pAnnot) override; +- void ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text) override; +- bool CanUndo(CPDFSDK_Annot* pAnnot) override; +- bool CanRedo(CPDFSDK_Annot* pAnnot) override; +- bool Undo(CPDFSDK_Annot* pAnnot) override; +- bool Redo(CPDFSDK_Annot* pAnnot) override; +- bool HitTest(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- const CFX_PointF& point) override; +- void OnDraw(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device, +- bool bDrawAnnots) override; +- void OnLoad(CPDFSDK_Annot* pAnnot) override; +- +- void OnMouseEnter(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) override; +- void OnMouseExit(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) override; +- bool OnLButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnLButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnLButtonDblClk(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnMouseMove(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnMouseWheel(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- short zDelta, +- const CFX_PointF& point) override; +- bool OnRButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnRButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnRButtonDblClk(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override; +- bool OnKeyDown(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override; +- bool OnKeyUp(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override; +- bool OnSetFocus(ObservedPtr* pAnnot, uint32_t nFlag) override; +- bool OnKillFocus(ObservedPtr* pAnnot, uint32_t nFlag) override; +- bool SetIndexSelected(ObservedPtr* pAnnot, +- int index, +- bool selected) override; +- bool IsIndexSelected(ObservedPtr* pAnnot, int index) override; +-}; +- +-#endif // FPDFSDK_CPDFSDK_BAANNOTHANDLER_H_ +diff --git a/fpdfsdk/cpdfsdk_baannothandler_embeddertest.cpp b/fpdfsdk/cpdfsdk_baannothandler_embeddertest.cpp +deleted file mode 100644 +index 9a26e8327..000000000 +--- a/fpdfsdk/cpdfsdk_baannothandler_embeddertest.cpp ++++ /dev/null +@@ -1,88 +0,0 @@ +-// Copyright 2019 The PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#include "fpdfsdk/cpdfsdk_annotiterator.h" +-#include "fpdfsdk/cpdfsdk_baannothandler.h" +-#include "fpdfsdk/cpdfsdk_formfillenvironment.h" +-#include "fpdfsdk/cpdfsdk_helpers.h" +-#include "fpdfsdk/cpdfsdk_pageview.h" +-#include "public/fpdf_annot.h" +-#include "testing/embedder_test.h" +- +-class CPDFSDK_BAAnnotHandlerTest : public EmbedderTest { +- public: +- void SetUp() override { +- // Test behaviour with currently supported annot i.e. Widget. +- // TODO(crbug.com/994500): Add an API that can set list of focusable +- // subtypes once other annots(links & highlights) are also supported. +- EmbedderTest::SetUp(); +- SetUpBAAnnotHandler(); +- } +- +- void TearDown() override { +- UnloadPage(m_page); +- EmbedderTest::TearDown(); +- } +- +- void SetUpBAAnnotHandler() { +- EXPECT_TRUE(OpenDocument("links_highlights_annots.pdf")); +- m_page = LoadPage(0); +- ASSERT_TRUE(m_page); +- +- CPDFSDK_FormFillEnvironment* pFormFillEnv = +- CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle()); +- ASSERT_TRUE(pFormFillEnv); +- m_pPageView = pFormFillEnv->GetPageView(IPDFPageFromFPDFPage(m_page), true); +- ASSERT_TRUE(m_pPageView); +- +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- pFormFillEnv->GetAnnotHandlerMgr(); +- ASSERT_TRUE(pAnnotHandlerMgr); +- m_pBAAnnotHandler = pAnnotHandlerMgr->m_pBAAnnotHandler.get(); +- ASSERT_TRUE(m_pBAAnnotHandler); +- } +- +- CPDFSDK_PageView* GetPageView() const { return m_pPageView; } +- CPDFSDK_BAAnnotHandler* GetBAAnnotHandler() const { +- return m_pBAAnnotHandler; +- } +- +- private: +- FPDF_PAGE m_page = nullptr; +- CPDFSDK_PageView* m_pPageView = nullptr; +- CPDFSDK_BAAnnotHandler* m_pBAAnnotHandler = nullptr; +-}; +- +-TEST_F(CPDFSDK_BAAnnotHandlerTest, TabToLinkOrHighlightAnnot) { +- // TODO(crbug.com/994500): Create annot iterator with list of supported +- // focusable subtypes as provided by host. +- CPDFSDK_AnnotIterator ai(GetPageView(), CPDF_Annot::Subtype::LINK); +- CPDFSDK_Annot* pAnnot = ai.GetFirstAnnot(); +- ASSERT_TRUE(pAnnot); +- EXPECT_EQ(pAnnot->GetAnnotSubtype(), CPDF_Annot::Subtype::LINK); +- +- ObservedPtr pNonWidgetAnnot(pAnnot); +- +- // TODO(crbug.com/994500): Change expected value as true once +- // links & highlights are supported. +- EXPECT_FALSE(GetBAAnnotHandler()->OnSetFocus(&pNonWidgetAnnot, 0)); +- +- EXPECT_FALSE(GetBAAnnotHandler()->OnKillFocus(&pNonWidgetAnnot, 0)); +-} +- +-TEST_F(CPDFSDK_BAAnnotHandlerTest, TabToInvalidAnnot) { +- // TODO(crbug.com/994500): Create annot iterator with list of supported +- // focusable subtypes as provided by host. +- CPDFSDK_AnnotIterator ai(GetPageView(), CPDF_Annot::Subtype::WIDGET); +- CPDFSDK_Annot* pAnnot = ai.GetFirstAnnot(); +- ASSERT_TRUE(pAnnot); +- EXPECT_EQ(pAnnot->GetAnnotSubtype(), CPDF_Annot::Subtype::WIDGET); +- +- ObservedPtr pWidgetAnnot(pAnnot); +- +- // Passing wrong subtype to BAAnnotHandler, API should return false. +- EXPECT_FALSE(GetBAAnnotHandler()->OnSetFocus(&pWidgetAnnot, 0)); +- +- EXPECT_FALSE(GetBAAnnotHandler()->OnKillFocus(&pWidgetAnnot, 0)); +-} +diff --git a/fpdfsdk/cpdfsdk_customaccess.cpp b/fpdfsdk/cpdfsdk_customaccess.cpp +index 108ffbcb3..4f828239f 100644 +--- a/fpdfsdk/cpdfsdk_customaccess.cpp ++++ b/fpdfsdk/cpdfsdk_customaccess.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,7 @@ + #include "fpdfsdk/cpdfsdk_customaccess.h" + + #include "core/fxcrt/fx_safe_types.h" ++#include "third_party/base/numerics/safe_conversions.h" + + CPDFSDK_CustomAccess::CPDFSDK_CustomAccess(FPDF_FILEACCESS* pFileAccess) + : m_FileAccess(*pFileAccess) {} +@@ -17,18 +18,19 @@ FX_FILESIZE CPDFSDK_CustomAccess::GetSize() { + return m_FileAccess.m_FileLen; + } + +-bool CPDFSDK_CustomAccess::ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) { +- if (!buffer || offset < 0 || !size) ++bool CPDFSDK_CustomAccess::ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) { ++ if (buffer.empty() || offset < 0) + return false; + +- if (!pdfium::base::IsValueInRangeForNumericType(size)) ++ if (!pdfium::base::IsValueInRangeForNumericType(buffer.size())) + return false; + +- FX_SAFE_FILESIZE new_pos = size; ++ FX_SAFE_FILESIZE new_pos = buffer.size(); + new_pos += offset; + return new_pos.IsValid() && new_pos.ValueOrDie() <= GetSize() && +- m_FileAccess.m_GetBlock(m_FileAccess.m_Param, offset, +- static_cast(buffer), size); ++ m_FileAccess.m_GetBlock( ++ m_FileAccess.m_Param, ++ pdfium::base::checked_cast(offset), buffer.data(), ++ pdfium::base::checked_cast(buffer.size())); + } +diff --git a/fpdfsdk/cpdfsdk_customaccess.h b/fpdfsdk/cpdfsdk_customaccess.h +index 76940ce58..734baeb40 100644 +--- a/fpdfsdk/cpdfsdk_customaccess.h ++++ b/fpdfsdk/cpdfsdk_customaccess.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,18 +8,17 @@ + #define FPDFSDK_CPDFSDK_CUSTOMACCESS_H_ + + #include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/retain_ptr.h" + #include "public/fpdfview.h" + + class CPDFSDK_CustomAccess final : public IFX_SeekableReadStream { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // IFX_SeekableReadStream + FX_FILESIZE GetSize() override; +- bool ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) override; ++ bool ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) override; + + private: + explicit CPDFSDK_CustomAccess(FPDF_FILEACCESS* pFileAccess); +diff --git a/fpdfsdk/cpdfsdk_fieldaction.cpp b/fpdfsdk/cpdfsdk_fieldaction.cpp +deleted file mode 100644 +index 57fa1b608..000000000 +--- a/fpdfsdk/cpdfsdk_fieldaction.cpp ++++ /dev/null +@@ -1,11 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "fpdfsdk/cpdfsdk_fieldaction.h" +- +-CPDFSDK_FieldAction::CPDFSDK_FieldAction() = default; +- +-CPDFSDK_FieldAction::~CPDFSDK_FieldAction() = default; +diff --git a/fpdfsdk/cpdfsdk_filewriteadapter.cpp b/fpdfsdk/cpdfsdk_filewriteadapter.cpp +index 39a69a598..776c8de22 100644 +--- a/fpdfsdk/cpdfsdk_filewriteadapter.cpp ++++ b/fpdfsdk/cpdfsdk_filewriteadapter.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,17 +6,18 @@ + + #include "fpdfsdk/cpdfsdk_filewriteadapter.h" + ++#include "third_party/base/check.h" ++#include "third_party/base/numerics/safe_conversions.h" ++ + CPDFSDK_FileWriteAdapter::CPDFSDK_FileWriteAdapter(FPDF_FILEWRITE* file_write) + : file_write_(file_write) { +- ASSERT(file_write_); ++ DCHECK(file_write_); + } + +-CPDFSDK_FileWriteAdapter::~CPDFSDK_FileWriteAdapter() {} +- +-bool CPDFSDK_FileWriteAdapter::WriteBlock(const void* data, size_t size) { +- return file_write_->WriteBlock(file_write_.Get(), data, size) != 0; +-} ++CPDFSDK_FileWriteAdapter::~CPDFSDK_FileWriteAdapter() = default; + +-bool CPDFSDK_FileWriteAdapter::WriteString(ByteStringView str) { +- return WriteBlock(str.unterminated_c_str(), str.GetLength()); ++bool CPDFSDK_FileWriteAdapter::WriteBlock(pdfium::span buffer) { ++ return file_write_->WriteBlock( ++ file_write_, buffer.data(), ++ pdfium::base::checked_cast(buffer.size())) != 0; + } +diff --git a/fpdfsdk/cpdfsdk_filewriteadapter.h b/fpdfsdk/cpdfsdk_filewriteadapter.h +index fc42b7f41..477ec908b 100644 +--- a/fpdfsdk/cpdfsdk_filewriteadapter.h ++++ b/fpdfsdk/cpdfsdk_filewriteadapter.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,12 +14,10 @@ + + class CPDFSDK_FileWriteAdapter final : public IFX_RetainableWriteStream { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // IFX_WriteStream: +- bool WriteBlock(const void* data, size_t size) override; +- bool WriteString(ByteStringView str) override; ++ bool WriteBlock(pdfium::span buffer) override; + + private: + explicit CPDFSDK_FileWriteAdapter(FPDF_FILEWRITE* file_write); +diff --git a/fpdfsdk/cpdfsdk_formfillenvironment.cpp b/fpdfsdk/cpdfsdk_formfillenvironment.cpp +index 021ec6005..84df6e14d 100644 +--- a/fpdfsdk/cpdfsdk_formfillenvironment.cpp ++++ b/fpdfsdk/cpdfsdk_formfillenvironment.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,25 +6,53 @@ + + #include "fpdfsdk/cpdfsdk_formfillenvironment.h" + ++#include ++ + #include + #include + #include + ++#include "core/fpdfapi/page/cpdf_annotcontext.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfdoc/cpdf_nametree.h" +-#include "core/fxcrt/fx_memory_wrappers.h" +-#include "fpdfsdk/cpdfsdk_actionhandler.h" +-#include "fpdfsdk/cpdfsdk_annothandlermgr.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/stl_util.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "fpdfsdk/cpdfsdk_interactiveform.h" + #include "fpdfsdk/cpdfsdk_pageview.h" + #include "fpdfsdk/cpdfsdk_widget.h" +-#include "fpdfsdk/formfiller/cffl_formfiller.h" ++#include "fpdfsdk/formfiller/cffl_formfield.h" + #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h" ++#include "fxjs/ijs_event_context.h" + #include "fxjs/ijs_runtime.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/notreached.h" ++#include "third_party/base/numerics/safe_conversions.h" ++ ++#ifdef PDF_ENABLE_XFA ++#include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h" ++#endif ++ ++static_assert(FXCT_ARROW == ++ static_cast(IPWL_FillerNotify::CursorStyle::kArrow), ++ "kArrow value mismatch"); ++static_assert(FXCT_NESW == ++ static_cast(IPWL_FillerNotify::CursorStyle::kNESW), ++ "kNEWS value mismatch"); ++static_assert(FXCT_NWSE == ++ static_cast(IPWL_FillerNotify::CursorStyle::kNWSE), ++ "kNWSE value mismatch"); ++static_assert(FXCT_VBEAM == ++ static_cast(IPWL_FillerNotify::CursorStyle::kVBeam), ++ "kVBeam value mismatch"); ++static_assert(FXCT_HBEAM == ++ static_cast(IPWL_FillerNotify::CursorStyle::kHBeam), ++ "HBeam value mismatch"); ++static_assert(FXCT_HAND == ++ static_cast(IPWL_FillerNotify::CursorStyle::kHand), ++ "kHand value mismatch"); + + FPDF_WIDESTRING AsFPDFWideString(ByteString* bsUTF16LE) { + // Force a private version of the string, since we're about to hand it off +@@ -36,13 +64,12 @@ FPDF_WIDESTRING AsFPDFWideString(ByteString* bsUTF16LE) { + + CPDFSDK_FormFillEnvironment::CPDFSDK_FormFillEnvironment( + CPDF_Document* pDoc, +- FPDF_FORMFILLINFO* pFFinfo, +- std::unique_ptr pHandlerMgr) ++ FPDF_FORMFILLINFO* pFFinfo) + : m_pInfo(pFFinfo), + m_pCPDFDoc(pDoc), +- m_pAnnotHandlerMgr(std::move(pHandlerMgr)) { +- ASSERT(m_pCPDFDoc); +- m_pAnnotHandlerMgr->SetFormFillEnv(this); ++ m_pInteractiveFormFiller( ++ std::make_unique(this)) { ++ DCHECK(m_pCPDFDoc); + } + + CPDFSDK_FormFillEnvironment::~CPDFSDK_FormFillEnvironment() { +@@ -53,32 +80,23 @@ CPDFSDK_FormFillEnvironment::~CPDFSDK_FormFillEnvironment() { + // up. Make sure it is deleted before |m_pInteractiveForm|. + m_PageMap.clear(); + +- // |m_pAnnotHandlerMgr| will try to access |m_pFormFiller| when it cleans +- // itself up. Make sure it is deleted before |m_pFormFiller|. +- m_pAnnotHandlerMgr.reset(); +- +- // Must destroy the |m_pFormFiller| before the environment (|this|) ++ // Must destroy the |m_pInteractiveFormFiller| before the environment (|this|) + // because any created form widgets hold a pointer to the environment. + // Those widgets may call things like KillTimer() as they are shutdown. +- m_pFormFiller.reset(); ++ m_pInteractiveFormFiller.reset(); + + if (m_pInfo && m_pInfo->Release) + m_pInfo->Release(m_pInfo); + } + +-void CPDFSDK_FormFillEnvironment::InvalidateRect(PerWindowData* pWidgetData, ++void CPDFSDK_FormFillEnvironment::InvalidateRect(CPDFSDK_Widget* widget, + const CFX_FloatRect& rect) { +- auto* pPrivateData = static_cast(pWidgetData); +- CPDFSDK_Widget* widget = pPrivateData->pWidget.Get(); +- if (!widget) +- return; +- +- CPDFSDK_PageView* pPageView = widget->GetPageView(); + IPDF_Page* pPage = widget->GetPage(); +- if (!pPage || !pPageView) ++ if (!pPage) + return; + +- CFX_Matrix device2page = pPageView->GetCurrentMatrix().GetInverse(); ++ CFX_Matrix device2page = ++ widget->GetPageView()->GetCurrentMatrix().GetInverse(); + CFX_PointF left_top = device2page.Transform(CFX_PointF(rect.left, rect.top)); + CFX_PointF right_bottom = + device2page.Transform(CFX_PointF(rect.right, rect.bottom)); +@@ -89,16 +107,16 @@ void CPDFSDK_FormFillEnvironment::InvalidateRect(PerWindowData* pWidgetData, + } + + void CPDFSDK_FormFillEnvironment::OutputSelectedRect( +- CFFL_FormFiller* pFormFiller, ++ CFFL_FormField* pFormField, + const CFX_FloatRect& rect) { +- if (!pFormFiller || !m_pInfo || !m_pInfo->FFI_OutputSelectedRect) ++ if (!m_pInfo || !m_pInfo->FFI_OutputSelectedRect) + return; + +- auto* pPage = FPDFPageFromIPDFPage(pFormFiller->GetSDKAnnot()->GetPage()); +- ASSERT(pPage); ++ auto* pPage = FPDFPageFromIPDFPage(pFormField->GetSDKWidget()->GetPage()); ++ DCHECK(pPage); + +- CFX_PointF ptA = pFormFiller->PWLtoFFL(CFX_PointF(rect.left, rect.bottom)); +- CFX_PointF ptB = pFormFiller->PWLtoFFL(CFX_PointF(rect.right, rect.top)); ++ CFX_PointF ptA = pFormField->PWLtoFFL(CFX_PointF(rect.left, rect.bottom)); ++ CFX_PointF ptB = pFormField->PWLtoFFL(CFX_PointF(rect.right, rect.top)); + m_pInfo->FFI_OutputSelectedRect(m_pInfo, pPage, ptA.x, ptB.y, ptB.x, ptA.y); + } + +@@ -109,14 +127,14 @@ bool CPDFSDK_FormFillEnvironment::IsSelectionImplemented() const { + + #ifdef PDF_ENABLE_V8 + CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetCurrentView() { +- IPDF_Page* pPage = IPDFPageFromFPDFPage(GetCurrentPage()); +- return pPage ? GetPageView(pPage, true) : nullptr; ++ IPDF_Page* pPage = GetCurrentPage(); ++ return pPage ? GetOrCreatePageView(pPage) : nullptr; + } + +-FPDF_PAGE CPDFSDK_FormFillEnvironment::GetCurrentPage() const { ++IPDF_Page* CPDFSDK_FormFillEnvironment::GetCurrentPage() const { + if (m_pInfo && m_pInfo->FFI_GetCurrentPage) { +- return m_pInfo->FFI_GetCurrentPage( +- m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc.Get())); ++ return IPDFPageFromFPDFPage(m_pInfo->FFI_GetCurrentPage( ++ m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc))); + } + return nullptr; + } +@@ -130,7 +148,7 @@ WideString CPDFSDK_FormFillEnvironment::GetLanguage() { + if (nRequiredLen <= 0) + return WideString(); + +- std::vector> pBuff(nRequiredLen); ++ DataVector pBuff(nRequiredLen); + int nActualLen = + m_pInfo->FFI_GetLanguage(m_pInfo, pBuff.data(), nRequiredLen); + if (nActualLen <= 0 || nActualLen > nRequiredLen) +@@ -152,7 +170,7 @@ WideString CPDFSDK_FormFillEnvironment::GetPlatform() { + if (nRequiredLen <= 0) + return WideString(); + +- std::vector> pBuff(nRequiredLen); ++ DataVector pBuff(nRequiredLen); + int nActualLen = + m_pInfo->FFI_GetPlatform(m_pInfo, pBuff.data(), nRequiredLen); + if (nActualLen <= 0 || nActualLen > nRequiredLen) +@@ -169,89 +187,89 @@ int CPDFSDK_FormFillEnvironment::JS_appAlert(const WideString& Msg, + const WideString& Title, + int Type, + int Icon) { +- if (!m_pInfo || !m_pInfo->m_pJsPlatform || +- !m_pInfo->m_pJsPlatform->app_alert) { ++ IPDF_JSPLATFORM* js_platform = GetJSPlatform(); ++ if (!js_platform || !js_platform->app_alert) + return -1; +- } + + ByteString bsMsg = Msg.ToUTF16LE(); + ByteString bsTitle = Title.ToUTF16LE(); +- return m_pInfo->m_pJsPlatform->app_alert( +- m_pInfo->m_pJsPlatform, AsFPDFWideString(&bsMsg), +- AsFPDFWideString(&bsTitle), Type, Icon); +-} +- +-int CPDFSDK_FormFillEnvironment::JS_appResponse(const WideString& Question, +- const WideString& Title, +- const WideString& Default, +- const WideString& Label, +- FPDF_BOOL bPassword, +- void* response, +- int length) { +- if (!m_pInfo || !m_pInfo->m_pJsPlatform || +- !m_pInfo->m_pJsPlatform->app_response) { ++ return js_platform->app_alert(js_platform, AsFPDFWideString(&bsMsg), ++ AsFPDFWideString(&bsTitle), Type, Icon); ++} ++ ++int CPDFSDK_FormFillEnvironment::JS_appResponse( ++ const WideString& Question, ++ const WideString& Title, ++ const WideString& Default, ++ const WideString& Label, ++ FPDF_BOOL bPassword, ++ pdfium::span response) { ++ IPDF_JSPLATFORM* js_platform = GetJSPlatform(); ++ if (!js_platform || !js_platform->app_response) + return -1; +- } ++ + ByteString bsQuestion = Question.ToUTF16LE(); + ByteString bsTitle = Title.ToUTF16LE(); + ByteString bsDefault = Default.ToUTF16LE(); + ByteString bsLabel = Label.ToUTF16LE(); +- return m_pInfo->m_pJsPlatform->app_response( +- m_pInfo->m_pJsPlatform, AsFPDFWideString(&bsQuestion), +- AsFPDFWideString(&bsTitle), AsFPDFWideString(&bsDefault), +- AsFPDFWideString(&bsLabel), bPassword, response, length); ++ return js_platform->app_response( ++ js_platform, AsFPDFWideString(&bsQuestion), AsFPDFWideString(&bsTitle), ++ AsFPDFWideString(&bsDefault), AsFPDFWideString(&bsLabel), bPassword, ++ response.data(), pdfium::base::checked_cast(response.size())); + } + + void CPDFSDK_FormFillEnvironment::JS_appBeep(int nType) { +- if (!m_pInfo || !m_pInfo->m_pJsPlatform || +- !m_pInfo->m_pJsPlatform->app_beep) { ++ IPDF_JSPLATFORM* js_platform = GetJSPlatform(); ++ if (!js_platform || !js_platform->app_beep) + return; +- } +- m_pInfo->m_pJsPlatform->app_beep(m_pInfo->m_pJsPlatform, nType); ++ ++ js_platform->app_beep(js_platform, nType); + } + + WideString CPDFSDK_FormFillEnvironment::JS_fieldBrowse() { +- if (!m_pInfo || !m_pInfo->m_pJsPlatform || +- !m_pInfo->m_pJsPlatform->Field_browse) { ++ IPDF_JSPLATFORM* js_platform = GetJSPlatform(); ++ if (!js_platform || !js_platform->Field_browse) + return WideString(); +- } +- const int nRequiredLen = +- m_pInfo->m_pJsPlatform->Field_browse(m_pInfo->m_pJsPlatform, nullptr, 0); ++ ++ const int nRequiredLen = js_platform->Field_browse(js_platform, nullptr, 0); + if (nRequiredLen <= 0) + return WideString(); + +- std::vector> pBuff(nRequiredLen); +- const int nActualLen = m_pInfo->m_pJsPlatform->Field_browse( +- m_pInfo->m_pJsPlatform, pBuff.data(), nRequiredLen); ++ DataVector pBuff(nRequiredLen); ++ const int nActualLen = ++ js_platform->Field_browse(js_platform, pBuff.data(), nRequiredLen); + if (nActualLen <= 0 || nActualLen > nRequiredLen) + return WideString(); + + // Don't include trailing NUL. + pBuff.resize(nActualLen - 1); ++ ++ // Use FromDefANSI() per "local encoding" comment in fpdf_formfill.h. + return WideString::FromDefANSI(ByteStringView(pBuff)); + } + +-void CPDFSDK_FormFillEnvironment::JS_docmailForm(void* mailData, +- int length, +- FPDF_BOOL bUI, +- const WideString& To, +- const WideString& Subject, +- const WideString& CC, +- const WideString& BCC, +- const WideString& Msg) { +- if (!m_pInfo || !m_pInfo->m_pJsPlatform || +- !m_pInfo->m_pJsPlatform->Doc_mail) { ++void CPDFSDK_FormFillEnvironment::JS_docmailForm( ++ pdfium::span mailData, ++ FPDF_BOOL bUI, ++ const WideString& To, ++ const WideString& Subject, ++ const WideString& CC, ++ const WideString& BCC, ++ const WideString& Msg) { ++ IPDF_JSPLATFORM* js_platform = GetJSPlatform(); ++ if (!js_platform || !js_platform->Doc_mail) + return; +- } ++ + ByteString bsTo = To.ToUTF16LE(); + ByteString bsSubject = Subject.ToUTF16LE(); + ByteString bsCC = CC.ToUTF16LE(); + ByteString bsBcc = BCC.ToUTF16LE(); + ByteString bsMsg = Msg.ToUTF16LE(); +- m_pInfo->m_pJsPlatform->Doc_mail( +- m_pInfo->m_pJsPlatform, mailData, length, bUI, AsFPDFWideString(&bsTo), +- AsFPDFWideString(&bsSubject), AsFPDFWideString(&bsCC), +- AsFPDFWideString(&bsBcc), AsFPDFWideString(&bsMsg)); ++ js_platform->Doc_mail(js_platform, const_cast(mailData.data()), ++ pdfium::base::checked_cast(mailData.size()), bUI, ++ AsFPDFWideString(&bsTo), AsFPDFWideString(&bsSubject), ++ AsFPDFWideString(&bsCC), AsFPDFWideString(&bsBcc), ++ AsFPDFWideString(&bsMsg)); + } + + void CPDFSDK_FormFillEnvironment::JS_docprint(FPDF_BOOL bUI, +@@ -262,21 +280,20 @@ void CPDFSDK_FormFillEnvironment::JS_docprint(FPDF_BOOL bUI, + FPDF_BOOL bPrintAsImage, + FPDF_BOOL bReverse, + FPDF_BOOL bAnnotations) { +- if (!m_pInfo || !m_pInfo->m_pJsPlatform || +- !m_pInfo->m_pJsPlatform->Doc_print) { ++ IPDF_JSPLATFORM* js_platform = GetJSPlatform(); ++ if (!js_platform || !js_platform->Doc_print) + return; +- } +- m_pInfo->m_pJsPlatform->Doc_print(m_pInfo->m_pJsPlatform, bUI, nStart, nEnd, +- bSilent, bShrinkToFit, bPrintAsImage, +- bReverse, bAnnotations); ++ ++ js_platform->Doc_print(js_platform, bUI, nStart, nEnd, bSilent, bShrinkToFit, ++ bPrintAsImage, bReverse, bAnnotations); + } + + void CPDFSDK_FormFillEnvironment::JS_docgotoPage(int nPageNum) { +- if (!m_pInfo || !m_pInfo->m_pJsPlatform || +- !m_pInfo->m_pJsPlatform->Doc_gotoPage) { ++ IPDF_JSPLATFORM* js_platform = GetJSPlatform(); ++ if (!js_platform || !js_platform->Doc_gotoPage) + return; +- } +- m_pInfo->m_pJsPlatform->Doc_gotoPage(m_pInfo->m_pJsPlatform, nPageNum); ++ ++ js_platform->Doc_gotoPage(js_platform, nPageNum); + } + + WideString CPDFSDK_FormFillEnvironment::JS_docGetFilePath() { +@@ -285,36 +302,39 @@ WideString CPDFSDK_FormFillEnvironment::JS_docGetFilePath() { + #endif // PDF_ENABLE_V8 + + WideString CPDFSDK_FormFillEnvironment::GetFilePath() const { +- if (!m_pInfo || !m_pInfo->m_pJsPlatform || +- !m_pInfo->m_pJsPlatform->Doc_getFilePath) { ++ IPDF_JSPLATFORM* js_platform = GetJSPlatform(); ++ if (!js_platform || !js_platform->Doc_getFilePath) + return WideString(); +- } +- const int nRequiredLen = m_pInfo->m_pJsPlatform->Doc_getFilePath( +- m_pInfo->m_pJsPlatform, nullptr, 0); ++ ++ const int nRequiredLen = ++ js_platform->Doc_getFilePath(js_platform, nullptr, 0); + if (nRequiredLen <= 0) + return WideString(); + +- std::vector> pBuff(nRequiredLen); +- const int nActualLen = m_pInfo->m_pJsPlatform->Doc_getFilePath( +- m_pInfo->m_pJsPlatform, pBuff.data(), nRequiredLen); ++ DataVector pBuff(nRequiredLen); ++ const int nActualLen = ++ js_platform->Doc_getFilePath(js_platform, pBuff.data(), nRequiredLen); + if (nActualLen <= 0 || nActualLen > nRequiredLen) + return WideString(); + + // Don't include trailing NUL. + pBuff.resize(nActualLen - 1); ++ ++ // Use FromDefANSI() per "local encoding" comment in fpdf_formfill.h. + return WideString::FromDefANSI(ByteStringView(pBuff)); + } + +-void CPDFSDK_FormFillEnvironment::SubmitForm(pdfium::span form_data, +- const WideString& URL) { +- if (!m_pInfo || !m_pInfo->m_pJsPlatform || +- !m_pInfo->m_pJsPlatform->Doc_submitForm) { ++void CPDFSDK_FormFillEnvironment::SubmitForm( ++ pdfium::span form_data, ++ const WideString& URL) { ++ IPDF_JSPLATFORM* js_platform = GetJSPlatform(); ++ if (!js_platform || !js_platform->Doc_submitForm) + return; +- } ++ + ByteString bsUrl = URL.ToUTF16LE(); +- m_pInfo->m_pJsPlatform->Doc_submitForm(m_pInfo->m_pJsPlatform, +- form_data.data(), form_data.size(), +- AsFPDFWideString(&bsUrl)); ++ js_platform->Doc_submitForm( ++ js_platform, const_cast(form_data.data()), ++ fxcrt::CollectionSize(form_data), AsFPDFWideString(&bsUrl)); + } + + IJS_Runtime* CPDFSDK_FormFillEnvironment::GetIJSRuntime() { +@@ -323,23 +343,6 @@ IJS_Runtime* CPDFSDK_FormFillEnvironment::GetIJSRuntime() { + return m_pIJSRuntime.get(); + } + +-CPDFSDK_AnnotHandlerMgr* CPDFSDK_FormFillEnvironment::GetAnnotHandlerMgr() { +- return m_pAnnotHandlerMgr.get(); +-} +- +-CPDFSDK_ActionHandler* CPDFSDK_FormFillEnvironment::GetActionHandler() { +- if (!m_pActionHandler) +- m_pActionHandler = pdfium::MakeUnique(); +- return m_pActionHandler.get(); +-} +- +-CFFL_InteractiveFormFiller* +-CPDFSDK_FormFillEnvironment::GetInteractiveFormFiller() { +- if (!m_pFormFiller) +- m_pFormFiller = pdfium::MakeUnique(this); +- return m_pFormFiller.get(); +-} +- + void CPDFSDK_FormFillEnvironment::Invalidate(IPDF_Page* page, + const FX_RECT& rect) { + if (m_pInfo && m_pInfo->FFI_Invalidate) { +@@ -348,16 +351,17 @@ void CPDFSDK_FormFillEnvironment::Invalidate(IPDF_Page* page, + } + } + +-void CPDFSDK_FormFillEnvironment::SetCursor(int nCursorType) { ++void CPDFSDK_FormFillEnvironment::SetCursor( ++ IPWL_FillerNotify::CursorStyle nCursorType) { + if (m_pInfo && m_pInfo->FFI_SetCursor) +- m_pInfo->FFI_SetCursor(m_pInfo, nCursorType); ++ m_pInfo->FFI_SetCursor(m_pInfo, static_cast(nCursorType)); + } + + int CPDFSDK_FormFillEnvironment::SetTimer(int uElapse, + TimerCallback lpTimerFunc) { + if (m_pInfo && m_pInfo->FFI_SetTimer) + return m_pInfo->FFI_SetTimer(m_pInfo, uElapse, lpTimerFunc); +- return TimerHandlerIface::kInvalidTimerID; ++ return CFX_Timer::HandlerIface::kInvalidTimerID; + } + + void CPDFSDK_FormFillEnvironment::KillTimer(int nTimerID) { +@@ -370,37 +374,78 @@ void CPDFSDK_FormFillEnvironment::OnChange() { + m_pInfo->FFI_OnChange(m_pInfo); + } + +-void CPDFSDK_FormFillEnvironment::ExecuteNamedAction(const char* namedAction) { ++void CPDFSDK_FormFillEnvironment::ExecuteNamedAction( ++ const ByteString& namedAction) { + if (m_pInfo && m_pInfo->FFI_ExecuteNamedAction) +- m_pInfo->FFI_ExecuteNamedAction(m_pInfo, namedAction); ++ m_pInfo->FFI_ExecuteNamedAction(m_pInfo, namedAction.c_str()); + } + +-void CPDFSDK_FormFillEnvironment::OnSetFieldInputFocus( +- FPDF_WIDESTRING focusText, +- FPDF_DWORD nTextLen, ++void CPDFSDK_FormFillEnvironment::OnSetFieldInputFocus(const WideString& text) { ++ OnSetFieldInputFocusInternal(text, true); ++} ++ ++void CPDFSDK_FormFillEnvironment::OnSetFieldInputFocusInternal( ++ const WideString& text, + bool bFocus) { +- if (m_pInfo && m_pInfo->FFI_SetTextFieldFocus) +- m_pInfo->FFI_SetTextFieldFocus(m_pInfo, focusText, nTextLen, bFocus); ++ if (m_pInfo && m_pInfo->FFI_SetTextFieldFocus) { ++ size_t nCharacters = text.GetLength(); ++ ByteString bsUTFText = text.ToUTF16LE(); ++ auto* pBuffer = reinterpret_cast(bsUTFText.c_str()); ++ m_pInfo->FFI_SetTextFieldFocus( ++ m_pInfo, pBuffer, pdfium::base::checked_cast(nCharacters), ++ bFocus); ++ } + } + +-void CPDFSDK_FormFillEnvironment::DoURIAction(const char* bsURI) { +- if (m_pInfo && m_pInfo->FFI_DoURIAction) +- m_pInfo->FFI_DoURIAction(m_pInfo, bsURI); ++void CPDFSDK_FormFillEnvironment::OnCalculate( ++ ObservedPtr& pAnnot) { ++ CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot.Get()); ++ if (pWidget) ++ m_pInteractiveForm->OnCalculate(pWidget->GetFormField()); ++} ++ ++void CPDFSDK_FormFillEnvironment::OnFormat(ObservedPtr& pAnnot) { ++ CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot.Get()); ++ DCHECK(pWidget); ++ ++ absl::optional sValue = ++ m_pInteractiveForm->OnFormat(pWidget->GetFormField()); ++ if (!pAnnot) ++ return; ++ ++ if (sValue.has_value()) { ++ m_pInteractiveForm->ResetFieldAppearance(pWidget->GetFormField(), sValue); ++ m_pInteractiveForm->UpdateField(pWidget->GetFormField()); ++ } ++} ++ ++void CPDFSDK_FormFillEnvironment::DoURIAction(const ByteString& bsURI, ++ Mask modifiers) { ++ if (!m_pInfo) ++ return; ++ ++ if (m_pInfo->version >= 2 && m_pInfo->FFI_DoURIActionWithKeyboardModifier) { ++ m_pInfo->FFI_DoURIActionWithKeyboardModifier(m_pInfo, bsURI.c_str(), ++ modifiers.UncheckedValue()); ++ return; ++ } ++ ++ if (m_pInfo->FFI_DoURIAction) ++ m_pInfo->FFI_DoURIAction(m_pInfo, bsURI.c_str()); + } + + void CPDFSDK_FormFillEnvironment::DoGoToAction(int nPageIndex, + int zoomMode, +- float* fPosArray, +- int sizeOfArray) { ++ pdfium::span fPosArray) { + if (m_pInfo && m_pInfo->FFI_DoGoToAction) { +- m_pInfo->FFI_DoGoToAction(m_pInfo, nPageIndex, zoomMode, fPosArray, +- sizeOfArray); ++ m_pInfo->FFI_DoGoToAction(m_pInfo, nPageIndex, zoomMode, fPosArray.data(), ++ fxcrt::CollectionSize(fPosArray)); + } + } + + #ifdef PDF_ENABLE_XFA + int CPDFSDK_FormFillEnvironment::GetPageViewCount() const { +- return pdfium::CollectionSize(m_PageMap); ++ return fxcrt::CollectionSize(m_PageMap); + } + + void CPDFSDK_FormFillEnvironment::DisplayCaret(IPDF_Page* page, +@@ -419,14 +464,14 @@ int CPDFSDK_FormFillEnvironment::GetCurrentPageIndex() const { + if (!m_pInfo || m_pInfo->version < 2 || !m_pInfo->FFI_GetCurrentPageIndex) + return -1; + return m_pInfo->FFI_GetCurrentPageIndex( +- m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc.Get())); ++ m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc)); + } + + void CPDFSDK_FormFillEnvironment::SetCurrentPage(int iCurPage) { + if (!m_pInfo || m_pInfo->version < 2 || !m_pInfo->FFI_SetCurrentPage) + return; +- m_pInfo->FFI_SetCurrentPage( +- m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc.Get()), iCurPage); ++ m_pInfo->FFI_SetCurrentPage(m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc), ++ iCurPage); + } + + void CPDFSDK_FormFillEnvironment::GotoURL(const WideString& wsURL) { +@@ -434,7 +479,7 @@ void CPDFSDK_FormFillEnvironment::GotoURL(const WideString& wsURL) { + return; + + ByteString bsTo = wsURL.ToUTF16LE(); +- m_pInfo->FFI_GotoURL(m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc.Get()), ++ m_pInfo->FFI_GotoURL(m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc), + AsFPDFWideString(&bsTo)); + } + +@@ -458,11 +503,10 @@ FS_RECTF CPDFSDK_FormFillEnvironment::GetPageViewRect(IPDF_Page* page) { + } + + bool CPDFSDK_FormFillEnvironment::PopupMenu(IPDF_Page* page, +- FPDF_WIDGET hWidget, + int menuFlag, + const CFX_PointF& pt) { + return m_pInfo && m_pInfo->version >= 2 && m_pInfo->FFI_PopupMenu && +- m_pInfo->FFI_PopupMenu(m_pInfo, FPDFPageFromIPDFPage(page), hWidget, ++ m_pInfo->FFI_PopupMenu(m_pInfo, FPDFPageFromIPDFPage(page), nullptr, + menuFlag, pt.x, pt.y); + } + +@@ -527,7 +571,7 @@ WideString CPDFSDK_FormFillEnvironment::PostRequestURL( + + WideString wsRet = + WideString::FromUTF16LE(reinterpret_cast(response.str), +- response.len / sizeof(FPDF_WIDESTRING)); ++ response.len / sizeof(FPDF_WCHAR)); + + FPDF_BStr_Clear(&response); + return wsRet; +@@ -558,22 +602,22 @@ void CPDFSDK_FormFillEnvironment::PageEvent(int iPageCount, + + void CPDFSDK_FormFillEnvironment::ClearAllFocusedAnnots() { + for (auto& it : m_PageMap) { +- if (it.second->IsValidSDKAnnot(GetFocusAnnot())) +- KillFocusAnnot(0); ++ if (it.second->IsValidSDKAnnot(GetFocusAnnot())) { ++ ObservedPtr pObserved(it.second.get()); ++ KillFocusAnnot({}); ++ if (!pObserved) ++ break; ++ } + } + } + +-CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetPageView( +- IPDF_Page* pUnderlyingPage, +- bool renew) { +- auto it = m_PageMap.find(pUnderlyingPage); +- if (it != m_PageMap.end()) +- return it->second.get(); +- +- if (!renew) +- return nullptr; ++CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetOrCreatePageView( ++ IPDF_Page* pUnderlyingPage) { ++ CPDFSDK_PageView* pExisting = GetPageView(pUnderlyingPage); ++ if (pExisting) ++ return pExisting; + +- auto pNew = pdfium::MakeUnique(this, pUnderlyingPage); ++ auto pNew = std::make_unique(this, pUnderlyingPage); + CPDFSDK_PageView* pPageView = pNew.get(); + m_PageMap[pUnderlyingPage] = std::move(pNew); + +@@ -582,31 +626,40 @@ CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetPageView( + return pPageView; + } + +-CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetPageView(int nIndex) { +- IPDF_Page* pTempPage = GetPage(nIndex); +- if (!pTempPage) +- return nullptr; +- +- auto it = m_PageMap.find(pTempPage); ++CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetPageView( ++ IPDF_Page* pUnderlyingPage) { ++ auto it = m_PageMap.find(pUnderlyingPage); + return it != m_PageMap.end() ? it->second.get() : nullptr; + } + ++CFX_Timer::HandlerIface* CPDFSDK_FormFillEnvironment::GetTimerHandler() { ++ return this; ++} ++ ++CPDFSDK_PageView* CPDFSDK_FormFillEnvironment::GetPageViewAtIndex(int nIndex) { ++ IPDF_Page* pTempPage = GetPage(nIndex); ++ return pTempPage ? GetPageView(pTempPage) : nullptr; ++} ++ + void CPDFSDK_FormFillEnvironment::ProcJavascriptAction() { +- CPDF_NameTree docJS(m_pCPDFDoc.Get(), "JavaScript"); +- int iCount = docJS.GetCount(); +- for (int i = 0; i < iCount; i++) { ++ auto name_tree = CPDF_NameTree::Create(m_pCPDFDoc, "JavaScript"); ++ if (!name_tree) ++ return; ++ ++ size_t count = name_tree->GetCount(); ++ for (size_t i = 0; i < count; ++i) { + WideString name; +- CPDF_Action action(ToDictionary(docJS.LookupValueAndName(i, &name))); +- GetActionHandler()->DoAction_JavaScript(action, name, this); ++ CPDF_Action action(ToDictionary(name_tree->LookupValueAndName(i, &name))); ++ DoActionJavaScript(action, name); + } + } + + bool CPDFSDK_FormFillEnvironment::ProcOpenAction() { +- CPDF_Dictionary* pRoot = m_pCPDFDoc->GetRoot(); ++ const CPDF_Dictionary* pRoot = m_pCPDFDoc->GetRoot(); + if (!pRoot) + return false; + +- CPDF_Object* pOpenAction = pRoot->GetDictFor("OpenAction"); ++ RetainPtr pOpenAction(pRoot->GetDictFor("OpenAction")); + if (!pOpenAction) + pOpenAction = pRoot->GetArrayFor("OpenAction"); + if (!pOpenAction) +@@ -615,12 +668,11 @@ bool CPDFSDK_FormFillEnvironment::ProcOpenAction() { + if (pOpenAction->IsArray()) + return true; + +- CPDF_Dictionary* pDict = pOpenAction->AsDictionary(); ++ RetainPtr pDict = ToDictionary(pOpenAction); + if (!pDict) + return false; + +- CPDF_Action action(pDict); +- GetActionHandler()->DoAction_DocOpen(action, this); ++ DoActionDocOpen(CPDF_Action(std::move(pDict))); + return true; + } + +@@ -643,97 +695,102 @@ void CPDFSDK_FormFillEnvironment::RemovePageView(IPDF_Page* pUnderlyingPage) { + // be created. We then have two page views pointing to the same page and + // bad things happen. + if (pPageView->IsValidSDKAnnot(GetFocusAnnot())) +- KillFocusAnnot(0); ++ KillFocusAnnot({}); + + // Remove the page from the map to make sure we don't accidentally attempt + // to use the |pPageView| while we're cleaning it up. + m_PageMap.erase(it); + } + +-IPDF_Page* CPDFSDK_FormFillEnvironment::GetPage(int nIndex) { ++IPDF_Page* CPDFSDK_FormFillEnvironment::GetPage(int nIndex) const { + if (!m_pInfo || !m_pInfo->FFI_GetPage) + return nullptr; + return IPDFPageFromFPDFPage(m_pInfo->FFI_GetPage( +- m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc.Get()), nIndex)); ++ m_pInfo, FPDFDocumentFromCPDFDocument(m_pCPDFDoc), nIndex)); + } + + CPDFSDK_InteractiveForm* CPDFSDK_FormFillEnvironment::GetInteractiveForm() { + if (!m_pInteractiveForm) +- m_pInteractiveForm = pdfium::MakeUnique(this); ++ m_pInteractiveForm = std::make_unique(this); + return m_pInteractiveForm.get(); + } + +-void CPDFSDK_FormFillEnvironment::UpdateAllViews(CPDFSDK_PageView* pSender, +- CPDFSDK_Annot* pAnnot) { ++void CPDFSDK_FormFillEnvironment::UpdateAllViews(CPDFSDK_Annot* pAnnot) { + for (const auto& it : m_PageMap) { +- CPDFSDK_PageView* pPageView = it.second.get(); +- if (pPageView != pSender) +- pPageView->UpdateView(pAnnot); ++ ObservedPtr pObserved(it.second.get()); ++ if (pObserved) { ++ pObserved->UpdateView(pAnnot); ++ if (!pObserved) ++ break; ++ } + } + } + ++CPDFSDK_Annot* CPDFSDK_FormFillEnvironment::GetFocusAnnot() const { ++ return m_pFocusAnnot.Get(); ++} ++ + bool CPDFSDK_FormFillEnvironment::SetFocusAnnot( +- ObservedPtr* pAnnot) { ++ ObservedPtr& pAnnot) { + if (m_bBeingDestroyed) + return false; +- if (m_pFocusAnnot == *pAnnot) ++ if (m_pFocusAnnot == pAnnot) + return true; +- if (m_pFocusAnnot && !KillFocusAnnot(0)) ++ if (m_pFocusAnnot && !KillFocusAnnot({})) + return false; +- if (!pAnnot->HasObservable()) ++ if (!pAnnot) + return false; +- +- CPDFSDK_PageView* pPageView = (*pAnnot)->GetPageView(); +- if (!pPageView || !pPageView->IsValid()) ++ if (!pAnnot->GetPageView()->IsValid()) + return false; + +- CPDFSDK_AnnotHandlerMgr* pAnnotHandler = GetAnnotHandlerMgr(); + if (m_pFocusAnnot) + return false; + + #ifdef PDF_ENABLE_XFA +- ObservedPtr pLastFocusAnnot(m_pFocusAnnot.Get()); +- if (!pAnnotHandler->Annot_OnChangeFocus(pAnnot, &pLastFocusAnnot)) ++ CPDFXFA_Widget* pXFAWidget = pAnnot->AsXFAWidget(); ++ if (pXFAWidget && pXFAWidget->OnChangedFocus()) + return false; + +- // |pAnnot| may be destroyed in |Annot_OnChangeFocus|. +- if (!pAnnot->HasObservable()) ++ // `pAnnot` may be destroyed in `OnChangedFocus()`. ++ if (!pAnnot) + return false; + #endif // PDF_ENABLE_XFA +- if (!pAnnotHandler->Annot_OnSetFocus(pAnnot, 0)) ++ ++ if (!CPDFSDK_Annot::OnSetFocus(pAnnot, {})) + return false; + if (m_pFocusAnnot) + return false; + +- m_pFocusAnnot.Reset(pAnnot->Get()); ++ m_pFocusAnnot.Reset(pAnnot.Get()); ++ ++ // If we are not able to inform the client about the focus change, it ++ // shouldn't be considered as failure. ++ SendOnFocusChange(pAnnot); + return true; + } + +-bool CPDFSDK_FormFillEnvironment::KillFocusAnnot(uint32_t nFlag) { ++bool CPDFSDK_FormFillEnvironment::KillFocusAnnot(Mask nFlags) { + if (!m_pFocusAnnot) + return false; + +- CPDFSDK_AnnotHandlerMgr* pAnnotHandler = GetAnnotHandlerMgr(); + ObservedPtr pFocusAnnot(m_pFocusAnnot.Get()); + m_pFocusAnnot.Reset(); + +-#ifdef PDF_ENABLE_XFA +- ObservedPtr pNull; +- if (!pAnnotHandler->Annot_OnChangeFocus(&pNull, &pFocusAnnot)) +- return false; +-#endif // PDF_ENABLE_XFA +- +- if (!pAnnotHandler->Annot_OnKillFocus(&pFocusAnnot, nFlag)) { ++ if (!CPDFSDK_Annot::OnKillFocus(pFocusAnnot, nFlags)) { + m_pFocusAnnot.Reset(pFocusAnnot.Get()); + return false; + } + ++ // Might have been destroyed by OnKillFocus(). ++ if (!pFocusAnnot) ++ return false; ++ + if (pFocusAnnot->GetAnnotSubtype() == CPDF_Annot::Subtype::WIDGET) { + CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pFocusAnnot.Get()); + FormFieldType fieldType = pWidget->GetFieldType(); + if (fieldType == FormFieldType::kTextField || + fieldType == FormFieldType::kComboBox) { +- OnSetFieldInputFocus(nullptr, 0, false); ++ OnSetFieldInputFocusInternal(WideString(), false); + } + } + return !m_pFocusAnnot; +@@ -744,6 +801,404 @@ int CPDFSDK_FormFillEnvironment::GetPageCount() const { + return pExtension ? pExtension->GetPageCount() : m_pCPDFDoc->GetPageCount(); + } + +-bool CPDFSDK_FormFillEnvironment::GetPermissions(int nFlag) const { +- return !!(m_pCPDFDoc->GetUserPermissions() & nFlag); ++bool CPDFSDK_FormFillEnvironment::HasPermissions(uint32_t flags) const { ++ return !!(m_pCPDFDoc->GetUserPermissions() & flags); ++} ++ ++void CPDFSDK_FormFillEnvironment::SendOnFocusChange( ++ ObservedPtr& pAnnot) { ++ if (!m_pInfo || m_pInfo->version < 2 || !m_pInfo->FFI_OnFocusChange) ++ return; ++ ++ // TODO(crbug.com/pdfium/1482): Handle XFA case. ++ if (pAnnot->AsXFAWidget()) ++ return; ++ ++ CPDFSDK_PageView* pPageView = pAnnot->GetPageView(); ++ if (!pPageView->IsValid()) ++ return; ++ ++ IPDF_Page* page = pAnnot->GetPage(); ++ if (!page) ++ return; ++ ++ RetainPtr annot_dict = ++ pAnnot->GetPDFAnnot()->GetMutableAnnotDict(); ++ auto focused_annot = std::make_unique(annot_dict, page); ++ FPDF_ANNOTATION fpdf_annot = ++ FPDFAnnotationFromCPDFAnnotContext(focused_annot.get()); ++ ++ m_pInfo->FFI_OnFocusChange(m_pInfo, fpdf_annot, pPageView->GetPageIndex()); ++} ++ ++bool CPDFSDK_FormFillEnvironment::DoActionDocOpen(const CPDF_Action& action) { ++ std::set visited; ++ return ExecuteDocumentOpenAction(action, &visited); ++} ++ ++bool CPDFSDK_FormFillEnvironment::DoActionJavaScript( ++ const CPDF_Action& JsAction, ++ WideString csJSName) { ++ if (JsAction.GetType() == CPDF_Action::Type::kJavaScript) { ++ WideString swJS = JsAction.GetJavaScript(); ++ if (!swJS.IsEmpty()) { ++ RunDocumentOpenJavaScript(csJSName, swJS); ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++bool CPDFSDK_FormFillEnvironment::DoActionFieldJavaScript( ++ const CPDF_Action& JsAction, ++ CPDF_AAction::AActionType type, ++ CPDF_FormField* pFormField, ++ CFFL_FieldAction* data) { ++ if (IsJSPlatformPresent() && ++ JsAction.GetType() == CPDF_Action::Type::kJavaScript) { ++ WideString swJS = JsAction.GetJavaScript(); ++ if (!swJS.IsEmpty()) { ++ RunFieldJavaScript(pFormField, type, data, swJS); ++ return true; ++ } ++ } ++ return false; ++} ++ ++bool CPDFSDK_FormFillEnvironment::DoActionLink(const CPDF_Action& action, ++ CPDF_AAction::AActionType type, ++ Mask modifiers) { ++ if (!CPDF_AAction::IsUserInput(type)) ++ return false; ++ ++ switch (action.GetType()) { ++ case CPDF_Action::Type::kGoTo: ++ DoActionGoTo(action); ++ return true; ++ case CPDF_Action::Type::kURI: ++ DoActionURI(action, modifiers); ++ return true; ++ default: ++ return false; ++ } ++} ++ ++bool CPDFSDK_FormFillEnvironment::DoActionDestination(const CPDF_Dest& dest) { ++ CPDF_Document* document = GetPDFDocument(); ++ DCHECK(document); ++ ++ std::vector positions = dest.GetScrollPositionArray(); ++ DoGoToAction(dest.GetDestPageIndex(document), dest.GetZoomMode(), positions); ++ return true; ++} ++ ++bool CPDFSDK_FormFillEnvironment::DoActionPage( ++ const CPDF_Action& action, ++ CPDF_AAction::AActionType eType) { ++ std::set visited; ++ return ExecuteDocumentPageAction(action, eType, &visited); ++} ++ ++bool CPDFSDK_FormFillEnvironment::DoActionDocument( ++ const CPDF_Action& action, ++ CPDF_AAction::AActionType eType) { ++ std::set visited; ++ return ExecuteDocumentPageAction(action, eType, &visited); ++} ++ ++bool CPDFSDK_FormFillEnvironment::DoActionField(const CPDF_Action& action, ++ CPDF_AAction::AActionType type, ++ CPDF_FormField* pFormField, ++ CFFL_FieldAction* data) { ++ std::set visited; ++ return ExecuteFieldAction(action, type, pFormField, data, &visited); ++} ++ ++bool CPDFSDK_FormFillEnvironment::ExecuteDocumentOpenAction( ++ const CPDF_Action& action, ++ std::set* visited) { ++ const CPDF_Dictionary* pDict = action.GetDict(); ++ if (pdfium::Contains(*visited, pDict)) ++ return false; ++ ++ visited->insert(pDict); ++ ++ if (action.GetType() == CPDF_Action::Type::kJavaScript) { ++ if (IsJSPlatformPresent()) { ++ WideString swJS = action.GetJavaScript(); ++ if (!swJS.IsEmpty()) ++ RunDocumentOpenJavaScript(WideString(), swJS); ++ } ++ } else { ++ DoActionNoJs(action, CPDF_AAction::AActionType::kDocumentOpen); ++ } ++ ++ for (size_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) { ++ CPDF_Action subaction = action.GetSubAction(i); ++ if (!ExecuteDocumentOpenAction(subaction, visited)) ++ return false; ++ } ++ ++ return true; ++} ++ ++bool CPDFSDK_FormFillEnvironment::ExecuteDocumentPageAction( ++ const CPDF_Action& action, ++ CPDF_AAction::AActionType type, ++ std::set* visited) { ++ const CPDF_Dictionary* pDict = action.GetDict(); ++ if (pdfium::Contains(*visited, pDict)) ++ return false; ++ ++ visited->insert(pDict); ++ ++ if (action.GetType() == CPDF_Action::Type::kJavaScript) { ++ if (IsJSPlatformPresent()) { ++ WideString swJS = action.GetJavaScript(); ++ if (!swJS.IsEmpty()) ++ RunDocumentPageJavaScript(type, swJS); ++ } ++ } else { ++ DoActionNoJs(action, type); ++ } ++ ++ for (size_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) { ++ CPDF_Action subaction = action.GetSubAction(i); ++ if (!ExecuteDocumentPageAction(subaction, type, visited)) ++ return false; ++ } ++ ++ return true; ++} ++ ++bool CPDFSDK_FormFillEnvironment::IsValidField( ++ const CPDF_Dictionary* pFieldDict) { ++ DCHECK(pFieldDict); ++ ++ CPDFSDK_InteractiveForm* pForm = GetInteractiveForm(); ++ CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm(); ++ return !!pPDFForm->GetFieldByDict(pFieldDict); ++} ++ ++bool CPDFSDK_FormFillEnvironment::ExecuteFieldAction( ++ const CPDF_Action& action, ++ CPDF_AAction::AActionType type, ++ CPDF_FormField* pFormField, ++ CFFL_FieldAction* data, ++ std::set* visited) { ++ const CPDF_Dictionary* pDict = action.GetDict(); ++ if (pdfium::Contains(*visited, pDict)) ++ return false; ++ ++ visited->insert(pDict); ++ ++ if (action.GetType() == CPDF_Action::Type::kJavaScript) { ++ if (IsJSPlatformPresent()) { ++ WideString swJS = action.GetJavaScript(); ++ if (!swJS.IsEmpty()) { ++ RunFieldJavaScript(pFormField, type, data, swJS); ++ if (!IsValidField(pFormField->GetFieldDict())) ++ return false; ++ } ++ } ++ } else { ++ DoActionNoJs(action, type); ++ } ++ ++ for (size_t i = 0, sz = action.GetSubActionsCount(); i < sz; i++) { ++ CPDF_Action subaction = action.GetSubAction(i); ++ if (!ExecuteFieldAction(subaction, type, pFormField, data, visited)) ++ return false; ++ } ++ ++ return true; ++} ++ ++void CPDFSDK_FormFillEnvironment::DoActionNoJs(const CPDF_Action& action, ++ CPDF_AAction::AActionType type) { ++ switch (action.GetType()) { ++ case CPDF_Action::Type::kGoTo: ++ DoActionGoTo(action); ++ break; ++ case CPDF_Action::Type::kURI: ++ if (CPDF_AAction::IsUserInput(type)) ++ DoActionURI(action, Mask{}); ++ break; ++ case CPDF_Action::Type::kHide: ++ DoActionHide(action); ++ break; ++ case CPDF_Action::Type::kNamed: ++ DoActionNamed(action); ++ break; ++ case CPDF_Action::Type::kSubmitForm: ++ if (CPDF_AAction::IsUserInput(type)) ++ DoActionSubmitForm(action); ++ break; ++ case CPDF_Action::Type::kResetForm: ++ DoActionResetForm(action); ++ break; ++ case CPDF_Action::Type::kJavaScript: ++ NOTREACHED_NORETURN(); ++ break; ++ case CPDF_Action::Type::kSetOCGState: ++ case CPDF_Action::Type::kThread: ++ case CPDF_Action::Type::kSound: ++ case CPDF_Action::Type::kMovie: ++ case CPDF_Action::Type::kRendition: ++ case CPDF_Action::Type::kTrans: ++ case CPDF_Action::Type::kGoTo3DView: ++ case CPDF_Action::Type::kGoToR: ++ case CPDF_Action::Type::kGoToE: ++ case CPDF_Action::Type::kLaunch: ++ case CPDF_Action::Type::kImportData: ++ // Unimplemented ++ break; ++ default: ++ break; ++ } ++} ++ ++void CPDFSDK_FormFillEnvironment::DoActionGoTo(const CPDF_Action& action) { ++ DCHECK(action.GetDict()); ++ ++ CPDF_Document* pPDFDocument = GetPDFDocument(); ++ DCHECK(pPDFDocument); ++ ++ CPDF_Dest MyDest = action.GetDest(pPDFDocument); ++ DoActionDestination(MyDest); ++} ++ ++void CPDFSDK_FormFillEnvironment::DoActionURI(const CPDF_Action& action, ++ Mask modifiers) { ++ DCHECK(action.GetDict()); ++ DoURIAction(action.GetURI(GetPDFDocument()), modifiers); ++} ++ ++void CPDFSDK_FormFillEnvironment::DoActionNamed(const CPDF_Action& action) { ++ DCHECK(action.GetDict()); ++ ExecuteNamedAction(action.GetNamedAction()); ++} ++ ++void CPDFSDK_FormFillEnvironment::RunFieldJavaScript( ++ CPDF_FormField* pFormField, ++ CPDF_AAction::AActionType type, ++ CFFL_FieldAction* data, ++ const WideString& script) { ++ DCHECK(type != CPDF_AAction::kCalculate); ++ DCHECK(type != CPDF_AAction::kFormat); ++ ++ RunScript(script, [type, data, pFormField](IJS_EventContext* context) { ++ switch (type) { ++ case CPDF_AAction::kCursorEnter: ++ context->OnField_MouseEnter(data->bModifier, data->bShift, pFormField); ++ break; ++ case CPDF_AAction::kCursorExit: ++ context->OnField_MouseExit(data->bModifier, data->bShift, pFormField); ++ break; ++ case CPDF_AAction::kButtonDown: ++ context->OnField_MouseDown(data->bModifier, data->bShift, pFormField); ++ break; ++ case CPDF_AAction::kButtonUp: ++ context->OnField_MouseUp(data->bModifier, data->bShift, pFormField); ++ break; ++ case CPDF_AAction::kGetFocus: ++ context->OnField_Focus(data->bModifier, data->bShift, pFormField, ++ &data->sValue); ++ break; ++ case CPDF_AAction::kLoseFocus: ++ context->OnField_Blur(data->bModifier, data->bShift, pFormField, ++ &data->sValue); ++ break; ++ case CPDF_AAction::kKeyStroke: ++ context->OnField_Keystroke( ++ &data->sChange, data->sChangeEx, data->bKeyDown, data->bModifier, ++ &data->nSelEnd, &data->nSelStart, data->bShift, pFormField, ++ &data->sValue, data->bWillCommit, data->bFieldFull, &data->bRC); ++ break; ++ case CPDF_AAction::kValidate: ++ context->OnField_Validate(&data->sChange, data->sChangeEx, ++ data->bKeyDown, data->bModifier, data->bShift, ++ pFormField, &data->sValue, &data->bRC); ++ break; ++ default: ++ NOTREACHED_NORETURN(); ++ break; ++ } ++ }); ++} ++ ++void CPDFSDK_FormFillEnvironment::RunDocumentOpenJavaScript( ++ const WideString& sScriptName, ++ const WideString& script) { ++ RunScript(script, [sScriptName](IJS_EventContext* context) { ++ context->OnDoc_Open(sScriptName); ++ }); ++} ++ ++void CPDFSDK_FormFillEnvironment::RunDocumentPageJavaScript( ++ CPDF_AAction::AActionType type, ++ const WideString& script) { ++ RunScript(script, [type](IJS_EventContext* context) { ++ switch (type) { ++ case CPDF_AAction::kOpenPage: ++ context->OnPage_Open(); ++ break; ++ case CPDF_AAction::kClosePage: ++ context->OnPage_Close(); ++ break; ++ case CPDF_AAction::kCloseDocument: ++ context->OnDoc_WillClose(); ++ break; ++ case CPDF_AAction::kSaveDocument: ++ context->OnDoc_WillSave(); ++ break; ++ case CPDF_AAction::kDocumentSaved: ++ context->OnDoc_DidSave(); ++ break; ++ case CPDF_AAction::kPrintDocument: ++ context->OnDoc_WillPrint(); ++ break; ++ case CPDF_AAction::kDocumentPrinted: ++ context->OnDoc_DidPrint(); ++ break; ++ case CPDF_AAction::kPageVisible: ++ context->OnPage_InView(); ++ break; ++ case CPDF_AAction::kPageInvisible: ++ context->OnPage_OutView(); ++ break; ++ default: ++ NOTREACHED_NORETURN(); ++ break; ++ } ++ }); ++} ++ ++bool CPDFSDK_FormFillEnvironment::DoActionHide(const CPDF_Action& action) { ++ CPDFSDK_InteractiveForm* pForm = GetInteractiveForm(); ++ if (pForm->DoAction_Hide(action)) { ++ SetChangeMark(); ++ return true; ++ } ++ return false; ++} ++ ++bool CPDFSDK_FormFillEnvironment::DoActionSubmitForm( ++ const CPDF_Action& action) { ++ CPDFSDK_InteractiveForm* pForm = GetInteractiveForm(); ++ return pForm->DoAction_SubmitForm(action); ++} ++ ++void CPDFSDK_FormFillEnvironment::DoActionResetForm(const CPDF_Action& action) { ++ CPDFSDK_InteractiveForm* pForm = GetInteractiveForm(); ++ pForm->DoAction_ResetForm(action); ++} ++ ++void CPDFSDK_FormFillEnvironment::RunScript(const WideString& script, ++ const RunScriptCallback& cb) { ++ IJS_Runtime::ScopedEventContext pContext(GetIJSRuntime()); ++ cb(pContext.Get()); ++ pContext->RunScript(script); ++ // TODO(dsinclair): Return error if RunScript returns a IJS_Runtime::JS_Error. + } +diff --git a/fpdfsdk/cpdfsdk_formfillenvironment.h b/fpdfsdk/cpdfsdk_formfillenvironment.h +index ea673947e..f82f34696 100644 +--- a/fpdfsdk/cpdfsdk_formfillenvironment.h ++++ b/fpdfsdk/cpdfsdk_formfillenvironment.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,24 +7,37 @@ + #ifndef FPDFSDK_CPDFSDK_FORMFILLENVIRONMENT_H_ + #define FPDFSDK_CPDFSDK_FORMFILLENVIRONMENT_H_ + ++#include ++ + #include + #include ++#include ++#include ++#include + + #include "core/fpdfapi/page/cpdf_occontext.h" +-#include "core/fpdfapi/page/cpdf_page.h" + #include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fpdfdoc/cpdf_aaction.h" ++#include "core/fxcrt/cfx_timer.h" ++#include "core/fxcrt/mask.h" + #include "core/fxcrt/observed_ptr.h" +-#include "core/fxcrt/timerhandler_iface.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxcrt/unowned_ptr.h" + #include "fpdfsdk/cpdfsdk_annot.h" +-#include "fpdfsdk/pwl/ipwl_systemhandler.h" ++#include "fpdfsdk/formfiller/cffl_interactiveformfiller.h" ++#include "fpdfsdk/pwl/cpwl_wnd.h" ++#include "fpdfsdk/pwl/ipwl_fillernotify.h" + #include "public/fpdf_formfill.h" ++#include "third_party/base/span.h" + +-class CFFL_InteractiveFormFiller; +-class CPDFSDK_ActionHandler; +-class CPDFSDK_AnnotHandlerMgr; ++class CPDF_Action; ++class CPDF_FormField; + class CPDFSDK_InteractiveForm; + class CPDFSDK_PageView; ++class IJS_EventContext; + class IJS_Runtime; ++class IPDF_Page; ++struct CFFL_FieldAction; + + // NOTE: |bsUTF16LE| must outlive the use of the result. Care must be taken + // since modifying the result would impact |bsUTF16LE|. +@@ -41,14 +54,11 @@ FPDF_WIDESTRING AsFPDFWideString(ByteString* bsUTF16LE); + // hierarcy back to the form fill environment itself, so as to flag any + // lingering lifetime issues via the memory tools. + +-class CPDFSDK_FormFillEnvironment final : public Observable, +- public TimerHandlerIface, +- public IPWL_SystemHandler { ++class CPDFSDK_FormFillEnvironment final ++ : public CFX_Timer::HandlerIface, ++ public CFFL_InteractiveFormFiller::CallbackIface { + public: +- CPDFSDK_FormFillEnvironment( +- CPDF_Document* pDoc, +- FPDF_FORMFILLINFO* pFFinfo, +- std::unique_ptr pHandlerMgr); ++ CPDFSDK_FormFillEnvironment(CPDF_Document* pDoc, FPDF_FORMFILLINFO* pFFinfo); + + ~CPDFSDK_FormFillEnvironment() override; + +@@ -56,27 +66,33 @@ class CPDFSDK_FormFillEnvironment final : public Observable, + int32_t SetTimer(int32_t uElapse, TimerCallback lpTimerFunc) override; + void KillTimer(int32_t nTimerID) override; + +- // IPWL_SystemHandler: +- void InvalidateRect(PerWindowData* pWidgetData, ++ // CFFL_InteractiveFormFiller::CallbackIface: ++ void InvalidateRect(CPDFSDK_Widget* widget, + const CFX_FloatRect& rect) override; +- void OutputSelectedRect(CFFL_FormFiller* pFormFiller, ++ void OutputSelectedRect(CFFL_FormField* pFormField, + const CFX_FloatRect& rect) override; + bool IsSelectionImplemented() const override; +- void SetCursor(int32_t nCursorType) override; +- +- CPDFSDK_PageView* GetPageView(IPDF_Page* pUnderlyingPage, bool renew); +- CPDFSDK_PageView* GetPageView(int nIndex); +- ++ void SetCursor(IPWL_FillerNotify::CursorStyle nCursorType) override; ++ void OnSetFieldInputFocus(const WideString& text) override; ++ void OnCalculate(ObservedPtr& pAnnot) override; ++ void OnFormat(ObservedPtr& pAnnot) override; ++ void Invalidate(IPDF_Page* page, const FX_RECT& rect) override; ++ CPDFSDK_PageView* GetOrCreatePageView(IPDF_Page* pUnderlyingPage) override; ++ CPDFSDK_PageView* GetPageView(IPDF_Page* pUnderlyingPage) override; ++ CFX_Timer::HandlerIface* GetTimerHandler() override; ++ CPDFSDK_Annot* GetFocusAnnot() const override; ++ bool SetFocusAnnot(ObservedPtr& pAnnot) override; ++ bool HasPermissions(uint32_t flags) const override; ++ void OnChange() override; ++ ++ CPDFSDK_PageView* GetPageViewAtIndex(int nIndex); + void RemovePageView(IPDF_Page* pUnderlyingPage); +- void UpdateAllViews(CPDFSDK_PageView* pSender, CPDFSDK_Annot* pAnnot); ++ void UpdateAllViews(CPDFSDK_Annot* pAnnot); + +- CPDFSDK_Annot* GetFocusAnnot() const { return m_pFocusAnnot.Get(); } +- bool SetFocusAnnot(ObservedPtr* pAnnot); +- bool KillFocusAnnot(uint32_t nFlag); ++ bool KillFocusAnnot(Mask nFlags); + void ClearAllFocusedAnnots(); + + int GetPageCount() const; +- bool GetPermissions(int nFlag) const; + + bool GetChangeMark() const { return m_bChangeMask; } + void SetChangeMark() { m_bChangeMask = true; } +@@ -84,29 +100,53 @@ class CPDFSDK_FormFillEnvironment final : public Observable, + + void ProcJavascriptAction(); + bool ProcOpenAction(); +- void Invalidate(IPDF_Page* page, const FX_RECT& rect); +- +- void OnChange(); +- void ExecuteNamedAction(const char* namedAction); +- void OnSetFieldInputFocus(FPDF_WIDESTRING focusText, +- FPDF_DWORD nTextLen, +- bool bFocus); +- void DoURIAction(const char* bsURI); ++ ++ void ExecuteNamedAction(const ByteString& namedAction); ++ void DoURIAction(const ByteString& bsURI, Mask modifiers); + void DoGoToAction(int nPageIndex, + int zoomMode, +- float* fPosArray, +- int sizeOfArray); ++ pdfium::span fPosArray); + +- CPDF_Document* GetPDFDocument() const { return m_pCPDFDoc.Get(); } ++ CPDF_Document* GetPDFDocument() const { return m_pCPDFDoc; } + CPDF_Document::Extension* GetDocExtension() const { + return m_pCPDFDoc->GetExtension(); + } + + bool IsJSPlatformPresent() const { return m_pInfo && m_pInfo->m_pJsPlatform; } ++ IPDF_JSPLATFORM* GetJSPlatform() const { ++ return m_pInfo ? m_pInfo->m_pJsPlatform : nullptr; ++ } ++ ++ // Actions. ++ bool DoActionDocOpen(const CPDF_Action& action); ++ bool DoActionJavaScript(const CPDF_Action& JsAction, WideString csJSName); ++ bool DoActionPage(const CPDF_Action& action, CPDF_AAction::AActionType eType); ++ bool DoActionDocument(const CPDF_Action& action, ++ CPDF_AAction::AActionType eType); ++ bool DoActionField(const CPDF_Action& action, ++ CPDF_AAction::AActionType type, ++ CPDF_FormField* pFormField, ++ CFFL_FieldAction* data); ++ bool DoActionFieldJavaScript(const CPDF_Action& JsAction, ++ CPDF_AAction::AActionType type, ++ CPDF_FormField* pFormField, ++ CFFL_FieldAction* data); ++ bool DoActionLink(const CPDF_Action& action, ++ CPDF_AAction::AActionType type, ++ Mask modifiers); ++ bool DoActionDestination(const CPDF_Dest& dest); ++ void DoActionNoJs(const CPDF_Action& action, CPDF_AAction::AActionType type); ++ void DoActionGoTo(const CPDF_Action& action); ++ void DoActionLaunch(const CPDF_Action& action); ++ void DoActionURI(const CPDF_Action& action, Mask modifiers); ++ void DoActionNamed(const CPDF_Action& action); ++ bool DoActionHide(const CPDF_Action& action); ++ bool DoActionSubmitForm(const CPDF_Action& action); ++ void DoActionResetForm(const CPDF_Action& action); + + #ifdef PDF_ENABLE_V8 + CPDFSDK_PageView* GetCurrentView(); +- FPDF_PAGE GetCurrentPage() const; ++ IPDF_Page* GetCurrentPage() const; + + WideString GetLanguage(); + WideString GetPlatform(); +@@ -120,12 +160,10 @@ class CPDFSDK_FormFillEnvironment final : public Observable, + const WideString& Default, + const WideString& cLabel, + FPDF_BOOL bPassword, +- void* response, +- int length); ++ pdfium::span response); + void JS_appBeep(int nType); + WideString JS_fieldBrowse(); +- void JS_docmailForm(void* mailData, +- int length, ++ void JS_docmailForm(pdfium::span mailData, + FPDF_BOOL bUI, + const WideString& To, + const WideString& Subject, +@@ -159,10 +197,7 @@ class CPDFSDK_FormFillEnvironment final : public Observable, + + void GotoURL(const WideString& wsURL); + FS_RECTF GetPageViewRect(IPDF_Page* page); +- bool PopupMenu(IPDF_Page* page, +- FPDF_WIDGET hWidget, +- int menuFlag, +- const CFX_PointF& pt); ++ bool PopupMenu(IPDF_Page* page, int menuFlag, const CFX_PointF& pt); + void EmailTo(FPDF_FILEHANDLER* fileHandler, + FPDF_WIDESTRING pTo, + FPDF_WIDESTRING pSubject, +@@ -191,33 +226,71 @@ class CPDFSDK_FormFillEnvironment final : public Observable, + + WideString GetFilePath() const; + ByteString GetAppName() const { return ByteString(); } +- TimerHandlerIface* GetTimerHandler() { return this; } +- IPWL_SystemHandler* GetSysHandler() { return this; } + FPDF_FORMFILLINFO* GetFormFillInfo() const { return m_pInfo; } +- void SubmitForm(pdfium::span form_data, const WideString& URL); ++ void SubmitForm(pdfium::span form_data, const WideString& URL); ++ ++ void SetFocusableAnnotSubtypes( ++ const std::vector& focusableAnnotTypes) { ++ m_FocusableAnnotTypes = focusableAnnotTypes; ++ } ++ const std::vector& GetFocusableAnnotSubtypes() const { ++ return m_FocusableAnnotTypes; ++ } + +- CPDFSDK_AnnotHandlerMgr* GetAnnotHandlerMgr(); // Always present. ++ // Never returns null. ++ CFFL_InteractiveFormFiller* GetInteractiveFormFiller() { ++ return m_pInteractiveFormFiller.get(); ++ } + +- // Creates if not present. +- CFFL_InteractiveFormFiller* GetInteractiveFormFiller(); + IJS_Runtime* GetIJSRuntime(); // Creates if not present. +- CPDFSDK_ActionHandler* GetActionHandler(); // Creates if not present. + CPDFSDK_InteractiveForm* GetInteractiveForm(); // Creates if not present. + + private: +- IPDF_Page* GetPage(int nIndex); +- +- FPDF_FORMFILLINFO* const m_pInfo; +- std::unique_ptr m_pActionHandler; ++ using RunScriptCallback = std::function; ++ ++ IPDF_Page* GetPage(int nIndex) const; ++ void OnSetFieldInputFocusInternal(const WideString& text, bool bFocus); ++ void SendOnFocusChange(ObservedPtr& pAnnot); ++ ++ // Support methods for Actions. ++ void RunScript(const WideString& script, const RunScriptCallback& cb); ++ bool ExecuteDocumentOpenAction(const CPDF_Action& action, ++ std::set* visited); ++ bool ExecuteDocumentPageAction(const CPDF_Action& action, ++ CPDF_AAction::AActionType type, ++ std::set* visited); ++ bool ExecuteFieldAction(const CPDF_Action& action, ++ CPDF_AAction::AActionType type, ++ CPDF_FormField* pFormField, ++ CFFL_FieldAction* data, ++ std::set* visited); ++ void RunDocumentPageJavaScript(CPDF_AAction::AActionType type, ++ const WideString& script); ++ void RunDocumentOpenJavaScript(const WideString& sScriptName, ++ const WideString& script); ++ void RunFieldJavaScript(CPDF_FormField* pFormField, ++ CPDF_AAction::AActionType type, ++ CFFL_FieldAction* data, ++ const WideString& script); ++ bool IsValidField(const CPDF_Dictionary* pFieldDict); ++ ++ UnownedPtr const m_pInfo; + std::unique_ptr m_pIJSRuntime; ++ ++ // Iterator stability guarantees as provided by std::map<> required. + std::map> m_PageMap; ++ + std::unique_ptr m_pInteractiveForm; + ObservedPtr m_pFocusAnnot; + UnownedPtr const m_pCPDFDoc; +- std::unique_ptr m_pAnnotHandlerMgr; +- std::unique_ptr m_pFormFiller; ++ std::unique_ptr m_pInteractiveFormFiller; + bool m_bChangeMask = false; + bool m_bBeingDestroyed = false; ++ ++ // Holds the list of focusable annot types. ++ // Annotations of type WIDGET are by default focusable. ++ std::vector m_FocusableAnnotTypes = { ++ CPDF_Annot::Subtype::WIDGET}; + }; + + #endif // FPDFSDK_CPDFSDK_FORMFILLENVIRONMENT_H_ +diff --git a/fpdfsdk/cpdfsdk_helpers.cpp b/fpdfsdk/cpdfsdk_helpers.cpp +index 144149da7..c548420b9 100644 +--- a/fpdfsdk/cpdfsdk_helpers.cpp ++++ b/fpdfsdk/cpdfsdk_helpers.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,8 @@ + + #include "fpdfsdk/cpdfsdk_helpers.h" + ++#include ++ + #include "build/build_config.h" + #include "constants/form_fields.h" + #include "constants/stream_dict_common.h" +@@ -14,10 +16,15 @@ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" ++#include "core/fpdfapi/render/cpdf_renderoptions.h" + #include "core/fpdfdoc/cpdf_annot.h" + #include "core/fpdfdoc/cpdf_interactiveform.h" + #include "core/fpdfdoc/cpdf_metadata.h" ++#include "core/fxcrt/span_util.h" ++#include "core/fxcrt/unowned_ptr.h" + #include "fpdfsdk/cpdfsdk_formfillenvironment.h" ++#include "third_party/base/check.h" ++#include "third_party/base/numerics/safe_conversions.h" + + namespace { + +@@ -37,43 +44,48 @@ bool RaiseUnsupportedError(int nError) { + return true; + } + +-unsigned long GetStreamMaybeCopyAndReturnLengthImpl(const CPDF_Stream* stream, +- void* buffer, +- unsigned long buflen, +- bool decode) { +- ASSERT(stream); +- auto stream_acc = pdfium::MakeRetain(stream); ++// Use the existence of the XFA array as a signal for XFA forms. ++bool DocHasXFA(const CPDF_Document* doc) { ++ const CPDF_Dictionary* root = doc->GetRoot(); ++ if (!root) ++ return false; ++ ++ RetainPtr form = root->GetDictFor("AcroForm"); ++ return form && form->GetArrayFor("XFA"); ++} + ++unsigned long GetStreamMaybeCopyAndReturnLengthImpl( ++ RetainPtr stream, ++ pdfium::span buffer, ++ bool decode) { ++ DCHECK(stream); ++ auto stream_acc = pdfium::MakeRetain(std::move(stream)); + if (decode) + stream_acc->LoadAllDataFiltered(); + else + stream_acc->LoadAllDataRaw(); + +- const auto stream_data_size = stream_acc->GetSize(); +- if (!buffer || buflen < stream_data_size) +- return stream_data_size; ++ pdfium::span stream_data_span = stream_acc->GetSpan(); ++ if (!buffer.empty() && buffer.size() <= stream_data_span.size()) ++ fxcrt::spancpy(buffer, stream_data_span); + +- memcpy(buffer, stream_acc->GetData(), stream_data_size); +- return stream_data_size; ++ return pdfium::base::checked_cast(stream_data_span.size()); + } + + #ifdef PDF_ENABLE_XFA + class FPDF_FileHandlerContext final : public IFX_SeekableStream { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // IFX_SeekableStream: + FX_FILESIZE GetSize() override; +- bool IsEOF() override; + FX_FILESIZE GetPosition() override; +- bool ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) override; +- size_t ReadBlock(void* buffer, size_t size) override; +- bool WriteBlockAtOffset(const void* buffer, +- FX_FILESIZE offset, +- size_t size) override; ++ bool IsEOF() override; ++ size_t ReadBlock(pdfium::span buffer) override; ++ bool ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) override; ++ bool WriteBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) override; + bool Flush() override; + + void SetPosition(FX_FILESIZE pos) { m_nCurPos = pos; } +@@ -82,14 +94,12 @@ class FPDF_FileHandlerContext final : public IFX_SeekableStream { + explicit FPDF_FileHandlerContext(FPDF_FILEHANDLER* pFS); + ~FPDF_FileHandlerContext() override; + +- FPDF_FILEHANDLER* m_pFS; +- FX_FILESIZE m_nCurPos; ++ UnownedPtr const m_pFS; ++ FX_FILESIZE m_nCurPos = 0; + }; + +-FPDF_FileHandlerContext::FPDF_FileHandlerContext(FPDF_FILEHANDLER* pFS) { +- m_pFS = pFS; +- m_nCurPos = 0; +-} ++FPDF_FileHandlerContext::FPDF_FileHandlerContext(FPDF_FILEHANDLER* pFS) ++ : m_pFS(pFS) {} + + FPDF_FileHandlerContext::~FPDF_FileHandlerContext() { + if (m_pFS && m_pFS->Release) +@@ -98,7 +108,7 @@ FPDF_FileHandlerContext::~FPDF_FileHandlerContext() { + + FX_FILESIZE FPDF_FileHandlerContext::GetSize() { + if (m_pFS && m_pFS->GetSize) +- return (FX_FILESIZE)m_pFS->GetSize(m_pFS->clientData); ++ return static_cast(m_pFS->GetSize(m_pFS->clientData)); + return 0; + } + +@@ -110,48 +120,50 @@ FX_FILESIZE FPDF_FileHandlerContext::GetPosition() { + return m_nCurPos; + } + +-bool FPDF_FileHandlerContext::ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) { +- if (!buffer || !size || !m_pFS->ReadBlock) ++bool FPDF_FileHandlerContext::ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) { ++ if (buffer.empty() || !m_pFS->ReadBlock) + return false; + +- if (m_pFS->ReadBlock(m_pFS->clientData, (FPDF_DWORD)offset, buffer, +- (FPDF_DWORD)size) == 0) { +- m_nCurPos = offset + size; ++ if (m_pFS->ReadBlock(m_pFS->clientData, static_cast(offset), ++ buffer.data(), ++ static_cast(buffer.size())) == 0) { ++ m_nCurPos = offset + buffer.size(); + return true; + } + return false; + } + +-size_t FPDF_FileHandlerContext::ReadBlock(void* buffer, size_t size) { +- if (!buffer || !size || !m_pFS->ReadBlock) ++size_t FPDF_FileHandlerContext::ReadBlock(pdfium::span buffer) { ++ if (buffer.empty() || !m_pFS->ReadBlock) + return 0; + + FX_FILESIZE nSize = GetSize(); + if (m_nCurPos >= nSize) + return 0; + FX_FILESIZE dwAvail = nSize - m_nCurPos; +- if (dwAvail < (FX_FILESIZE)size) +- size = static_cast(dwAvail); +- if (m_pFS->ReadBlock(m_pFS->clientData, (FPDF_DWORD)m_nCurPos, buffer, +- (FPDF_DWORD)size) == 0) { +- m_nCurPos += size; +- return size; ++ if (dwAvail < (FX_FILESIZE)buffer.size()) ++ buffer = buffer.first(static_cast(dwAvail)); ++ if (m_pFS->ReadBlock(m_pFS->clientData, static_cast(m_nCurPos), ++ buffer.data(), ++ static_cast(buffer.size())) == 0) { ++ m_nCurPos += buffer.size(); ++ return buffer.size(); + } + + return 0; + } + +-bool FPDF_FileHandlerContext::WriteBlockAtOffset(const void* buffer, +- FX_FILESIZE offset, +- size_t size) { ++bool FPDF_FileHandlerContext::WriteBlockAtOffset( ++ pdfium::span buffer, ++ FX_FILESIZE offset) { + if (!m_pFS || !m_pFS->WriteBlock) + return false; + +- if (m_pFS->WriteBlock(m_pFS->clientData, (FPDF_DWORD)offset, buffer, +- (FPDF_DWORD)size) == 0) { +- m_nCurPos = offset + size; ++ if (m_pFS->WriteBlock(m_pFS->clientData, static_cast(offset), ++ buffer.data(), ++ static_cast(buffer.size())) == 0) { ++ m_nCurPos = offset + buffer.size(); + return true; + } + return false; +@@ -209,16 +221,18 @@ RetainPtr MakeSeekableStream( + } + #endif // PDF_ENABLE_XFA + +-const CPDF_Array* GetQuadPointsArrayFromDictionary( ++RetainPtr GetQuadPointsArrayFromDictionary( + const CPDF_Dictionary* dict) { + return dict->GetArrayFor("QuadPoints"); + } + +-CPDF_Array* GetQuadPointsArrayFromDictionary(CPDF_Dictionary* dict) { +- return dict->GetArrayFor("QuadPoints"); ++RetainPtr GetMutableQuadPointsArrayFromDictionary( ++ CPDF_Dictionary* dict) { ++ return pdfium::WrapRetain( ++ const_cast(GetQuadPointsArrayFromDictionary(dict).Get())); + } + +-CPDF_Array* AddQuadPointsArrayToDictionary(CPDF_Dictionary* dict) { ++RetainPtr AddQuadPointsArrayToDictionary(CPDF_Dictionary* dict) { + return dict->SetNewFor(kQuadPoints); + } + +@@ -226,24 +240,24 @@ bool IsValidQuadPointsIndex(const CPDF_Array* array, size_t index) { + return array && index < array->size() / 8; + } + +-bool GetQuadPointsAtIndex(const CPDF_Array* array, ++bool GetQuadPointsAtIndex(RetainPtr array, + size_t quad_index, + FS_QUADPOINTSF* quad_points) { +- ASSERT(quad_points); +- ASSERT(array); ++ DCHECK(quad_points); ++ DCHECK(array); + + if (!IsValidQuadPointsIndex(array, quad_index)) + return false; + + quad_index *= 8; +- quad_points->x1 = array->GetNumberAt(quad_index); +- quad_points->y1 = array->GetNumberAt(quad_index + 1); +- quad_points->x2 = array->GetNumberAt(quad_index + 2); +- quad_points->y2 = array->GetNumberAt(quad_index + 3); +- quad_points->x3 = array->GetNumberAt(quad_index + 4); +- quad_points->y3 = array->GetNumberAt(quad_index + 5); +- quad_points->x4 = array->GetNumberAt(quad_index + 6); +- quad_points->y4 = array->GetNumberAt(quad_index + 7); ++ quad_points->x1 = array->GetFloatAt(quad_index); ++ quad_points->y1 = array->GetFloatAt(quad_index + 1); ++ quad_points->x2 = array->GetFloatAt(quad_index + 2); ++ quad_points->y2 = array->GetFloatAt(quad_index + 3); ++ quad_points->x3 = array->GetFloatAt(quad_index + 4); ++ quad_points->y3 = array->GetFloatAt(quad_index + 5); ++ quad_points->x4 = array->GetFloatAt(quad_index + 6); ++ quad_points->y4 = array->GetFloatAt(quad_index + 7); + return true; + } + +@@ -267,27 +281,38 @@ FS_MATRIX FSMatrixFromCFXMatrix(const CFX_Matrix& matrix) { + return {matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f}; + } + ++unsigned long NulTerminateMaybeCopyAndReturnLength(const ByteString& text, ++ void* buffer, ++ unsigned long buflen) { ++ const unsigned long len = ++ pdfium::base::checked_cast(text.GetLength() + 1); ++ if (buffer && len <= buflen) ++ memcpy(buffer, text.c_str(), len); ++ return len; ++} ++ + unsigned long Utf16EncodeMaybeCopyAndReturnLength(const WideString& text, + void* buffer, + unsigned long buflen) { + ByteString encoded_text = text.ToUTF16LE(); +- unsigned long len = encoded_text.GetLength(); ++ const unsigned long len = ++ pdfium::base::checked_cast(encoded_text.GetLength()); + if (buffer && len <= buflen) + memcpy(buffer, encoded_text.c_str(), len); + return len; + } + +-unsigned long GetRawStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream, +- void* buffer, +- unsigned long buflen) { +- return GetStreamMaybeCopyAndReturnLengthImpl(stream, buffer, buflen, ++unsigned long GetRawStreamMaybeCopyAndReturnLength( ++ RetainPtr stream, ++ pdfium::span buffer) { ++ return GetStreamMaybeCopyAndReturnLengthImpl(std::move(stream), buffer, + /*decode=*/false); + } + +-unsigned long DecodeStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream, +- void* buffer, +- unsigned long buflen) { +- return GetStreamMaybeCopyAndReturnLengthImpl(stream, buffer, buflen, ++unsigned long DecodeStreamMaybeCopyAndReturnLength( ++ RetainPtr stream, ++ pdfium::span buffer) { ++ return GetStreamMaybeCopyAndReturnLengthImpl(std::move(stream), buffer, + /*decode=*/true); + } + +@@ -320,53 +345,47 @@ void SetPDFUnsupportInfo(UNSUPPORT_INFO* unsp_info) { + g_unsupport_info = unsp_info; + } + +-UNSUPPORT_INFO* GetPDFUnssuportInto() { +- return g_unsupport_info; +-} +- +-void ReportUnsupportedFeatures(CPDF_Document* pDoc) { ++void ReportUnsupportedFeatures(const CPDF_Document* pDoc) { + const CPDF_Dictionary* pRootDict = pDoc->GetRoot(); +- if (pRootDict) { +- // Portfolios and Packages +- if (pRootDict->KeyExist("Collection")) { +- RaiseUnsupportedError(FPDF_UNSP_DOC_PORTABLECOLLECTION); +- return; +- } +- if (pRootDict->KeyExist("Names")) { +- const CPDF_Dictionary* pNameDict = pRootDict->GetDictFor("Names"); +- if (pNameDict && pNameDict->KeyExist("EmbeddedFiles")) { +- RaiseUnsupportedError(FPDF_UNSP_DOC_ATTACHMENT); +- return; +- } +- if (pNameDict && pNameDict->KeyExist("JavaScript")) { +- const CPDF_Dictionary* pJSDict = pNameDict->GetDictFor("JavaScript"); +- const CPDF_Array* pArray = +- pJSDict ? pJSDict->GetArrayFor("Names") : nullptr; +- if (pArray) { +- for (size_t i = 0; i < pArray->size(); i++) { +- ByteString cbStr = pArray->GetStringAt(i); +- if (cbStr.Compare("com.adobe.acrobat.SharedReview.Register") == 0) { +- RaiseUnsupportedError(FPDF_UNSP_DOC_SHAREDREVIEW); +- return; +- } ++ if (!pRootDict) ++ return; ++ ++ // Portfolios and Packages ++ if (pRootDict->KeyExist("Collection")) ++ RaiseUnsupportedError(FPDF_UNSP_DOC_PORTABLECOLLECTION); ++ ++ RetainPtr pNameDict = pRootDict->GetDictFor("Names"); ++ if (pNameDict) { ++ if (pNameDict->KeyExist("EmbeddedFiles")) ++ RaiseUnsupportedError(FPDF_UNSP_DOC_ATTACHMENT); ++ ++ RetainPtr pJSDict = ++ pNameDict->GetDictFor("JavaScript"); ++ if (pJSDict) { ++ RetainPtr pArray = pJSDict->GetArrayFor("Names"); ++ if (pArray) { ++ for (size_t i = 0; i < pArray->size(); i++) { ++ ByteString cbStr = pArray->GetByteStringAt(i); ++ if (cbStr == "com.adobe.acrobat.SharedReview.Register") { ++ RaiseUnsupportedError(FPDF_UNSP_DOC_SHAREDREVIEW); ++ break; + } + } + } + } ++ } + +- // SharedForm +- const CPDF_Stream* pStream = pRootDict->GetStreamFor("Metadata"); +- if (pStream) { +- CPDF_Metadata metaData(pStream); +- for (const auto& err : metaData.CheckForSharedForm()) +- RaiseUnsupportedError(static_cast(err)); +- } ++ // SharedForm ++ RetainPtr pStream = pRootDict->GetStreamFor("Metadata"); ++ if (pStream) { ++ CPDF_Metadata metadata(std::move(pStream)); ++ for (const UnsupportedFeature& feature : metadata.CheckForSharedForm()) ++ RaiseUnsupportedError(static_cast(feature)); + } + } + +-void ReportUnsupportedXFA(CPDF_Document* pDoc) { +- // XFA Forms +- if (!pDoc->GetExtension() && CPDF_InteractiveForm(pDoc).HasXFAForm()) ++void ReportUnsupportedXFA(const CPDF_Document* pDoc) { ++ if (!pDoc->GetExtension() && DocHasXFA(pDoc)) + RaiseUnsupportedError(FPDF_UNSP_DOC_XFAFORM); + } + +@@ -383,7 +402,7 @@ void CheckForUnsupportedAnnot(const CPDF_Annot* pAnnot) { + break; + case CPDF_Annot::Subtype::SCREEN: { + const CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict(); +- ByteString cbString = pAnnotDict->GetStringFor("IT"); ++ ByteString cbString = pAnnotDict->GetByteStringFor("IT"); + if (cbString != "Img") + RaiseUnsupportedError(FPDF_UNSP_ANNOT_SCREEN_MEDIA); + break; +@@ -396,7 +415,8 @@ void CheckForUnsupportedAnnot(const CPDF_Annot* pAnnot) { + break; + case CPDF_Annot::Subtype::WIDGET: { + const CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict(); +- ByteString cbString = pAnnotDict->GetStringFor(pdfium::form_fields::kFT); ++ ByteString cbString = ++ pAnnotDict->GetByteStringFor(pdfium::form_fields::kFT); + if (cbString == pdfium::form_fields::kSig) + RaiseUnsupportedError(FPDF_UNSP_ANNOT_SIG); + break; +@@ -428,3 +448,55 @@ void ProcessParseError(CPDF_Parser::Error err) { + } + FXSYS_SetLastError(err_code); + } ++ ++void SetColorFromScheme(const FPDF_COLORSCHEME* pColorScheme, ++ CPDF_RenderOptions* pRenderOptions) { ++ CPDF_RenderOptions::ColorScheme color_scheme; ++ color_scheme.path_fill_color = ++ static_cast(pColorScheme->path_fill_color); ++ color_scheme.path_stroke_color = ++ static_cast(pColorScheme->path_stroke_color); ++ color_scheme.text_fill_color = ++ static_cast(pColorScheme->text_fill_color); ++ color_scheme.text_stroke_color = ++ static_cast(pColorScheme->text_stroke_color); ++ pRenderOptions->SetColorScheme(color_scheme); ++} ++ ++std::vector ParsePageRangeString(const ByteString& bsPageRange, ++ uint32_t nCount) { ++ ByteStringView alphabet(" 0123456789-,"); ++ for (const auto& ch : bsPageRange) { ++ if (!alphabet.Contains(ch)) ++ return std::vector(); ++ } ++ ++ ByteString bsStrippedPageRange = bsPageRange; ++ bsStrippedPageRange.Remove(' '); ++ ++ std::vector results; ++ for (const auto& entry : fxcrt::Split(bsStrippedPageRange, ',')) { ++ std::vector args = fxcrt::Split(entry, '-'); ++ if (args.size() == 1) { ++ uint32_t page_num = ++ pdfium::base::checked_cast(atoi(args[0].c_str())); ++ if (page_num == 0 || page_num > nCount) ++ return std::vector(); ++ results.push_back(page_num - 1); ++ } else if (args.size() == 2) { ++ uint32_t first_num = ++ pdfium::base::checked_cast(atoi(args[0].c_str())); ++ if (first_num == 0) ++ return std::vector(); ++ uint32_t last_num = ++ pdfium::base::checked_cast(atoi(args[1].c_str())); ++ if (last_num == 0 || first_num > last_num || last_num > nCount) ++ return std::vector(); ++ for (uint32_t i = first_num; i <= last_num; ++i) ++ results.push_back(i - 1); ++ } else { ++ return std::vector(); ++ } ++ } ++ return results; ++} +diff --git a/fpdfsdk/cpdfsdk_helpers.h b/fpdfsdk/cpdfsdk_helpers.h +index 327ec4cac..076cac77d 100644 +--- a/fpdfsdk/cpdfsdk_helpers.h ++++ b/fpdfsdk/cpdfsdk_helpers.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,9 +7,13 @@ + #ifndef FPDFSDK_CPDFSDK_HELPERS_H_ + #define FPDFSDK_CPDFSDK_HELPERS_H_ + ++#include ++ + #include "build/build_config.h" + #include "core/fpdfapi/page/cpdf_page.h" + #include "core/fpdfapi/parser/cpdf_parser.h" ++#include "core/fxcrt/retain_ptr.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/dib/cfx_dibitmap.h" + #include "public/fpdf_doc.h" + #include "public/fpdf_ext.h" +@@ -19,11 +23,6 @@ + #include "core/fxcrt/fx_stream.h" + #endif // PDF_ENABLE_XFA + +-#if defined(OS_WIN) +-#include +-#include +-#endif +- + class CPDF_Annot; + class CPDF_AnnotContext; + class CPDF_ClipPath; +@@ -32,6 +31,7 @@ class CPDF_Object; + class CPDF_Font; + class CPDF_LinkExtract; + class CPDF_PageObject; ++class CPDF_RenderOptions; + class CPDF_Stream; + class CPDF_StructElement; + class CPDF_StructTree; +@@ -39,8 +39,8 @@ class CPDF_TextPage; + class CPDF_TextPageFind; + class CPDFSDK_FormFillEnvironment; + class CPDFSDK_InteractiveForm; +-class FX_PATHPOINT; + struct CPDF_JavaScript; ++struct XObjectContext; + + // Conversions to/from underlying types. + IPDF_Page* IPDFPageFromFPDFPage(FPDF_PAGE page); +@@ -151,20 +151,20 @@ inline CPDF_ContentMarkItem* CPDFContentMarkItemFromFPDFPageObjectMark( + return reinterpret_cast(mark); + } + +-inline FPDF_PAGERANGE FPDFPageRangeFromCPDFArray(CPDF_Array* range) { ++inline FPDF_PAGERANGE FPDFPageRangeFromCPDFArray(const CPDF_Array* range) { + return reinterpret_cast(range); + } +-inline CPDF_Array* CPDFArrayFromFPDFPageRange(FPDF_PAGERANGE range) { +- return reinterpret_cast(range); ++inline const CPDF_Array* CPDFArrayFromFPDFPageRange(FPDF_PAGERANGE range) { ++ return reinterpret_cast(range); + } + + inline FPDF_PATHSEGMENT FPDFPathSegmentFromFXPathPoint( +- const FX_PATHPOINT* segment) { ++ const CFX_Path::Point* segment) { + return reinterpret_cast(segment); + } +-inline const FX_PATHPOINT* FXPathPointFromFPDFPathSegment( ++inline const CFX_Path::Point* FXPathPointFromFPDFPathSegment( + FPDF_PATHSEGMENT segment) { +- return reinterpret_cast(segment); ++ return reinterpret_cast(segment); + } + + inline FPDF_STRUCTTREE FPDFStructTreeFromCPDFStructTree( +@@ -185,6 +185,15 @@ inline CPDF_StructElement* CPDFStructElementFromFPDFStructElement( + return reinterpret_cast(struct_element); + } + ++inline FPDF_STRUCTELEMENT_ATTR FPDFStructElementAttrFromCPDFDictionary( ++ const CPDF_Dictionary* dictionary) { ++ return reinterpret_cast(dictionary); ++} ++inline const CPDF_Dictionary* CPDFDictionaryFromFPDFStructElementAttr( ++ FPDF_STRUCTELEMENT_ATTR struct_element_attr) { ++ return reinterpret_cast(struct_element_attr); ++} ++ + inline FPDF_TEXTPAGE FPDFTextPageFromCPDFTextPage(CPDF_TextPage* page) { + return reinterpret_cast(page); + } +@@ -210,6 +219,23 @@ CPDFSDKFormFillEnvironmentFromFPDFFormHandle(FPDF_FORMHANDLE handle) { + return reinterpret_cast(handle); + } + ++inline FPDF_SIGNATURE FPDFSignatureFromCPDFDictionary( ++ const CPDF_Dictionary* dictionary) { ++ return reinterpret_cast(dictionary); ++} ++inline const CPDF_Dictionary* CPDFDictionaryFromFPDFSignature( ++ FPDF_SIGNATURE signature) { ++ return reinterpret_cast(signature); ++} ++ ++inline FPDF_XOBJECT FPDFXObjectFromXObjectContext(XObjectContext* xobject) { ++ return reinterpret_cast(xobject); ++} ++ ++inline XObjectContext* XObjectContextFromFPDFXObject(FPDF_XOBJECT xobject) { ++ return reinterpret_cast(xobject); ++} ++ + CPDFSDK_InteractiveForm* FormHandleToInteractiveForm(FPDF_FORMHANDLE hHandle); + + ByteString ByteStringFromFPDFWideString(FPDF_WIDESTRING wide_string); +@@ -222,11 +248,13 @@ RetainPtr MakeSeekableStream( + FPDF_FILEHANDLER* pFileHandler); + #endif // PDF_ENABLE_XFA + +-const CPDF_Array* GetQuadPointsArrayFromDictionary(const CPDF_Dictionary* dict); +-CPDF_Array* GetQuadPointsArrayFromDictionary(CPDF_Dictionary* dict); +-CPDF_Array* AddQuadPointsArrayToDictionary(CPDF_Dictionary* dict); ++RetainPtr GetQuadPointsArrayFromDictionary( ++ const CPDF_Dictionary* dict); ++RetainPtr GetMutableQuadPointsArrayFromDictionary( ++ CPDF_Dictionary* dict); ++RetainPtr AddQuadPointsArrayToDictionary(CPDF_Dictionary* dict); + bool IsValidQuadPointsIndex(const CPDF_Array* array, size_t index); +-bool GetQuadPointsAtIndex(const CPDF_Array* array, ++bool GetQuadPointsAtIndex(RetainPtr array, + size_t quad_index, + FS_QUADPOINTSF* quad_points); + +@@ -238,34 +266,43 @@ FS_RECTF FSRectFFromCFXFloatRect(const CFX_FloatRect& rect); + CFX_Matrix CFXMatrixFromFSMatrix(const FS_MATRIX& matrix); + FS_MATRIX FSMatrixFromCFXMatrix(const CFX_Matrix& matrix); + ++unsigned long NulTerminateMaybeCopyAndReturnLength(const ByteString& text, ++ void* buffer, ++ unsigned long buflen); ++ + unsigned long Utf16EncodeMaybeCopyAndReturnLength(const WideString& text, + void* buffer, + unsigned long buflen); + + // Returns the length of the raw stream data from |stream|. The raw data is the + // stream's data as stored in the PDF without applying any filters. If |buffer| +-// is non-nullptr and |buflen| is large enough to contain the raw data, then ++// is non-empty and its length is large enough to contain the raw data, then + // the raw data is copied into |buffer|. +-unsigned long GetRawStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream, +- void* buffer, +- unsigned long buflen); ++unsigned long GetRawStreamMaybeCopyAndReturnLength( ++ RetainPtr stream, ++ pdfium::span buffer); + + // Return the length of the decoded stream data of |stream|. The decoded data is + // the uncompressed stream data, i.e. the raw stream data after having all +-// filters applied. If |buffer| is non-nullptr and |buflen| is large enough to ++// filters applied. If |buffer| is non-empty and its length is large enough to + // contain the decoded data, then the decoded data is copied into |buffer|. +-unsigned long DecodeStreamMaybeCopyAndReturnLength(const CPDF_Stream* stream, +- void* buffer, +- unsigned long buflen); ++unsigned long DecodeStreamMaybeCopyAndReturnLength( ++ RetainPtr stream, ++ pdfium::span buffer); + + void SetPDFSandboxPolicy(FPDF_DWORD policy, FPDF_BOOL enable); + FPDF_BOOL IsPDFSandboxPolicyEnabled(FPDF_DWORD policy); + + void SetPDFUnsupportInfo(UNSUPPORT_INFO* unsp_info); +-UNSUPPORT_INFO* GetPDFUnssuportInto(); +-void ReportUnsupportedFeatures(CPDF_Document* pDoc); +-void ReportUnsupportedXFA(CPDF_Document* pDoc); ++void ReportUnsupportedFeatures(const CPDF_Document* pDoc); ++void ReportUnsupportedXFA(const CPDF_Document* pDoc); + void CheckForUnsupportedAnnot(const CPDF_Annot* pAnnot); + void ProcessParseError(CPDF_Parser::Error err); ++void SetColorFromScheme(const FPDF_COLORSCHEME* pColorScheme, ++ CPDF_RenderOptions* pRenderOptions); ++ ++// Returns a vector of page indices given a page range string. ++std::vector ParsePageRangeString(const ByteString& bsPageRange, ++ uint32_t nCount); + + #endif // FPDFSDK_CPDFSDK_HELPERS_H_ +diff --git a/fpdfsdk/cpdfsdk_helpers_unittest.cpp b/fpdfsdk/cpdfsdk_helpers_unittest.cpp +new file mode 100644 +index 000000000..ac1320efd +--- /dev/null ++++ b/fpdfsdk/cpdfsdk_helpers_unittest.cpp +@@ -0,0 +1,88 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "fpdfsdk/cpdfsdk_helpers.h" ++ ++#include "testing/gmock/include/gmock/gmock.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++using ::testing::ElementsAre; ++using ::testing::IsEmpty; ++ ++TEST(CPDFSDK_HelpersTest, NulTerminateMaybeCopyAndReturnLength) { ++ { ++ const ByteString to_be_copied("toBeCopied"); ++ constexpr size_t kExpectedToBeCopiedLen = 10; ++ ASSERT_EQ(kExpectedToBeCopiedLen, to_be_copied.GetLength()); ++ ++ EXPECT_EQ(kExpectedToBeCopiedLen + 1, ++ NulTerminateMaybeCopyAndReturnLength(to_be_copied, nullptr, 0)); ++ ++ // Buffer should not change if declared length is too short. ++ char buf[kExpectedToBeCopiedLen + 1]; ++ memset(buf, 0x42, kExpectedToBeCopiedLen + 1); ++ ASSERT_EQ(kExpectedToBeCopiedLen + 1, ++ NulTerminateMaybeCopyAndReturnLength(to_be_copied, buf, ++ kExpectedToBeCopiedLen)); ++ for (char c : buf) ++ EXPECT_EQ(0x42, c); ++ ++ // Buffer should copy over if long enough. ++ ASSERT_EQ(kExpectedToBeCopiedLen + 1, ++ NulTerminateMaybeCopyAndReturnLength(to_be_copied, buf, ++ kExpectedToBeCopiedLen + 1)); ++ EXPECT_EQ(to_be_copied, ByteString(buf)); ++ } ++ { ++ // Empty ByteString should still copy NUL terminator. ++ const ByteString empty; ++ char buf[1]; ++ ASSERT_EQ(1u, NulTerminateMaybeCopyAndReturnLength(empty, buf, 1)); ++ EXPECT_EQ(empty, ByteString(buf)); ++ } ++} ++ ++TEST(CPDFSDK_HelpersTest, ParsePageRangeString) { ++ EXPECT_THAT(ParsePageRangeString("", 1), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString(" ", 1), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("clams", 1), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("0", 0), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("1", 0), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString(",1", 10), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("1,", 10), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("1,clams", 1), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("clams,1", 1), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("0-1", 10), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("1-0", 10), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("1-5", 4), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("1-11,", 10), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString(",1-1", 10), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("1-", 10), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("1-,", 10), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("-2,", 10), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("1-clams", 10), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("clams-1,", 10), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("1-2clams", 10), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("0,1", 10), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("1,0", 10), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("1-2,,,,3-4", 10), IsEmpty()); ++ EXPECT_THAT(ParsePageRangeString("1-2-", 10), IsEmpty()); ++ ++ EXPECT_THAT(ParsePageRangeString("1-1", 10), ElementsAre(0)); ++ EXPECT_THAT(ParsePageRangeString("1", 1), ElementsAre(0)); ++ EXPECT_THAT(ParsePageRangeString("1-4", 4), ElementsAre(0, 1, 2, 3)); ++ EXPECT_THAT(ParsePageRangeString("1- 4", 4), ElementsAre(0, 1, 2, 3)); ++ EXPECT_THAT(ParsePageRangeString("1 -4", 4), ElementsAre(0, 1, 2, 3)); ++ EXPECT_THAT(ParsePageRangeString("1,2", 10), ElementsAre(0, 1)); ++ EXPECT_THAT(ParsePageRangeString("2,1", 10), ElementsAre(1, 0)); ++ EXPECT_THAT(ParsePageRangeString("1,50,2", 100), ElementsAre(0, 49, 1)); ++ EXPECT_THAT(ParsePageRangeString("1-4,50", 100), ElementsAre(0, 1, 2, 3, 49)); ++ EXPECT_THAT(ParsePageRangeString("50,1-2", 100), ElementsAre(49, 0, 1)); ++ EXPECT_THAT(ParsePageRangeString("5 0, 1-2 ", 100), ++ ElementsAre(49, 0, 1)); // ??? ++ EXPECT_THAT(ParsePageRangeString("1-3,4-6", 10), ++ ElementsAre(0, 1, 2, 3, 4, 5)); ++ EXPECT_THAT(ParsePageRangeString("1-4,3-6", 10), ++ ElementsAre(0, 1, 2, 3, 2, 3, 4, 5)); ++} +diff --git a/fpdfsdk/cpdfsdk_interactiveform.cpp b/fpdfsdk/cpdfsdk_interactiveform.cpp +index 51145b9b8..021124f34 100644 +--- a/fpdfsdk/cpdfsdk_interactiveform.cpp ++++ b/fpdfsdk/cpdfsdk_interactiveform.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,10 +6,11 @@ + + #include "fpdfsdk/cpdfsdk_interactiveform.h" + ++#include ++ + #include + #include + #include +-#include + #include + #include + +@@ -24,19 +25,19 @@ + #include "core/fpdfdoc/cpdf_formcontrol.h" + #include "core/fpdfdoc/cpdf_interactiveform.h" + #include "core/fxcrt/autorestorer.h" ++#include "core/fxcrt/fx_string_wrappers.h" ++#include "core/fxcrt/stl_util.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" +-#include "fpdfsdk/cpdfsdk_actionhandler.h" ++#include "core/fxge/cfx_path.h" + #include "fpdfsdk/cpdfsdk_annot.h" + #include "fpdfsdk/cpdfsdk_annotiterator.h" + #include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fpdfsdk/cpdfsdk_pageview.h" + #include "fpdfsdk/cpdfsdk_widget.h" +-#include "fpdfsdk/formfiller/cffl_formfiller.h" +-#include "fpdfsdk/ipdfsdk_annothandler.h" ++#include "fpdfsdk/formfiller/cffl_formfield.h" + #include "fxjs/ijs_event_context.h" + #include "fxjs/ijs_runtime.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -70,42 +71,37 @@ bool IsFormFieldTypeXFA(FormFieldType fieldType) { + } + #endif // PDF_ENABLE_XFA + +-bool FDFToURLEncodedData(std::vector* pBuffer) { +- std::unique_ptr pFDF = CFDF_Document::ParseMemory(*pBuffer); ++ByteString FDFToURLEncodedData(ByteString buffer) { ++ std::unique_ptr pFDF = ++ CFDF_Document::ParseMemory(buffer.raw_span()); + if (!pFDF) +- return true; ++ return buffer; + +- CPDF_Dictionary* pMainDict = pFDF->GetRoot()->GetDictFor("FDF"); ++ RetainPtr pMainDict = ++ pFDF->GetRoot()->GetDictFor("FDF"); + if (!pMainDict) +- return false; ++ return ByteString(); + +- CPDF_Array* pFields = pMainDict->GetArrayFor("Fields"); ++ RetainPtr pFields = pMainDict->GetArrayFor("Fields"); + if (!pFields) +- return false; ++ return ByteString(); + +- std::ostringstream fdfEncodedData; ++ fxcrt::ostringstream encoded_data; + for (uint32_t i = 0; i < pFields->size(); i++) { +- CPDF_Dictionary* pField = pFields->GetDictAt(i); ++ RetainPtr pField = pFields->GetDictAt(i); + if (!pField) + continue; +- WideString name; +- name = pField->GetUnicodeTextFor("T"); ++ WideString name = pField->GetUnicodeTextFor("T"); + ByteString name_b = name.ToDefANSI(); +- ByteString csBValue = pField->GetStringFor("V"); ++ ByteString csBValue = pField->GetByteStringFor("V"); + WideString csWValue = PDF_DecodeText(csBValue.raw_span()); + ByteString csValue_b = csWValue.ToDefANSI(); +- fdfEncodedData << name_b << "=" << csValue_b; ++ encoded_data << name_b << "=" << csValue_b; + if (i != pFields->size() - 1) +- fdfEncodedData << "&"; ++ encoded_data << "&"; + } + +- size_t nBufSize = fdfEncodedData.tellp(); +- if (nBufSize <= 0) +- return false; +- +- pBuffer->resize(nBufSize); +- memcpy(pBuffer->data(), fdfEncodedData.str().c_str(), nBufSize); +- return true; ++ return ByteString(encoded_data); + } + + } // namespace +@@ -113,7 +109,7 @@ bool FDFToURLEncodedData(std::vector* pBuffer) { + CPDFSDK_InteractiveForm::CPDFSDK_InteractiveForm( + CPDFSDK_FormFillEnvironment* pFormFillEnv) + : m_pFormFillEnv(pFormFillEnv), +- m_pInteractiveForm(pdfium::MakeUnique( ++ m_pInteractiveForm(std::make_unique( + m_pFormFillEnv->GetPDFDocument())) { + m_pInteractiveForm->SetNotifierIface(this); + RemoveAllHighLights(); +@@ -133,20 +129,20 @@ CPDFSDK_Widget* CPDFSDK_InteractiveForm::GetWidget( + if (pWidget) + return pWidget; + +- CPDF_Dictionary* pControlDict = pControl->GetWidget(); + CPDF_Document* pDocument = m_pFormFillEnv->GetPDFDocument(); + CPDFSDK_PageView* pPage = nullptr; +- +- if (CPDF_Dictionary* pPageDict = pControlDict->GetDictFor("P")) { ++ RetainPtr pControlDict = pControl->GetWidgetDict(); ++ RetainPtr pPageDict = pControlDict->GetDictFor("P"); ++ if (pPageDict) { + int nPageIndex = pDocument->GetPageIndex(pPageDict->GetObjNum()); + if (nPageIndex >= 0) +- pPage = m_pFormFillEnv->GetPageView(nPageIndex); ++ pPage = m_pFormFillEnv->GetPageViewAtIndex(nPageIndex); + } + + if (!pPage) { + int nPageIndex = GetPageIndexByAnnotDict(pDocument, pControlDict); + if (nPageIndex >= 0) +- pPage = m_pFormFillEnv->GetPageView(nPageIndex); ++ pPage = m_pFormFillEnv->GetPageViewAtIndex(nPageIndex); + } + + return pPage ? ToCPDFSDKWidget(pPage->GetAnnotByDict(pControlDict)) : nullptr; +@@ -154,21 +150,21 @@ CPDFSDK_Widget* CPDFSDK_InteractiveForm::GetWidget( + + void CPDFSDK_InteractiveForm::GetWidgets( + const WideString& sFieldName, +- std::vector>* widgets) const { +- for (int i = 0, sz = m_pInteractiveForm->CountFields(sFieldName); i < sz; ++ std::vector>* widgets) const { ++ for (size_t i = 0, sz = m_pInteractiveForm->CountFields(sFieldName); i < sz; + ++i) { + CPDF_FormField* pFormField = m_pInteractiveForm->GetField(i, sFieldName); +- ASSERT(pFormField); ++ DCHECK(pFormField); + GetWidgets(pFormField, widgets); + } + } + + void CPDFSDK_InteractiveForm::GetWidgets( + CPDF_FormField* pField, +- std::vector>* widgets) const { ++ std::vector>* widgets) const { + for (int i = 0, sz = pField->CountControls(); i < sz; ++i) { + CPDF_FormControl* pFormCtrl = pField->GetControl(i); +- ASSERT(pFormCtrl); ++ DCHECK(pFormCtrl); + CPDFSDK_Widget* pWidget = GetWidget(pFormCtrl); + if (pWidget) + widgets->emplace_back(pWidget); +@@ -177,31 +173,37 @@ void CPDFSDK_InteractiveForm::GetWidgets( + + int CPDFSDK_InteractiveForm::GetPageIndexByAnnotDict( + CPDF_Document* pDocument, +- CPDF_Dictionary* pAnnotDict) const { +- ASSERT(pAnnotDict); ++ const CPDF_Dictionary* pAnnotDict) const { ++ DCHECK(pAnnotDict); + + for (int i = 0, sz = pDocument->GetPageCount(); i < sz; i++) { +- if (CPDF_Dictionary* pPageDict = pDocument->GetPageDictionary(i)) { +- if (CPDF_Array* pAnnots = pPageDict->GetArrayFor("Annots")) { +- for (int j = 0, jsz = pAnnots->size(); j < jsz; j++) { +- CPDF_Object* pDict = pAnnots->GetDirectObjectAt(j); +- if (pAnnotDict == pDict) +- return i; +- } +- } ++ RetainPtr pPageDict = ++ pDocument->GetPageDictionary(i); ++ if (!pPageDict) ++ continue; ++ ++ RetainPtr pAnnots = pPageDict->GetArrayFor("Annots"); ++ if (!pAnnots) ++ continue; ++ ++ for (size_t j = 0, jsz = pAnnots->size(); j < jsz; j++) { ++ RetainPtr pDict = pAnnots->GetDirectObjectAt(j); ++ if (pAnnotDict == pDict) ++ return i; + } + } +- + return -1; + } + + void CPDFSDK_InteractiveForm::AddMap(CPDF_FormControl* pControl, + CPDFSDK_Widget* pWidget) { +- m_Map[pControl] = pWidget; ++ m_Map[pdfium::WrapUnowned(pControl)] = pWidget; + } + + void CPDFSDK_InteractiveForm::RemoveMap(CPDF_FormControl* pControl) { +- m_Map.erase(pControl); ++ auto it = m_Map.find(pControl); ++ if (it != m_Map.end()) ++ m_Map.erase(it); + } + + void CPDFSDK_InteractiveForm::EnableCalculate(bool bEnabled) { +@@ -262,11 +264,11 @@ void CPDFSDK_InteractiveForm::OnCalculate(CPDF_FormField* pFormField) { + continue; + + CPDF_AAction aAction = pField->GetAdditionalAction(); +- if (!aAction.GetDict() || !aAction.ActionExist(CPDF_AAction::kCalculate)) ++ if (!aAction.ActionExist(CPDF_AAction::kCalculate)) + continue; + + CPDF_Action action = aAction.GetAction(CPDF_AAction::kCalculate); +- if (!action.GetDict()) ++ if (!action.HasDict()) + continue; + + WideString csJS = action.GetJavaScript(); +@@ -279,16 +281,16 @@ void CPDFSDK_InteractiveForm::OnCalculate(CPDF_FormField* pFormField) { + IJS_Runtime::ScopedEventContext pContext(pRuntime); + pContext->OnField_Calculate(pFormField, pField, &sValue, &bRC); + +- Optional err = pContext->RunScript(csJS); +- if (!err && bRC && sValue.Compare(sOldValue) != 0) ++ absl::optional err = pContext->RunScript(csJS); ++ if (!err.has_value() && bRC && sValue != sOldValue) + pField->SetValue(sValue, NotificationOption::kNotify); + } + } + +-Optional CPDFSDK_InteractiveForm::OnFormat( ++absl::optional CPDFSDK_InteractiveForm::OnFormat( + CPDF_FormField* pFormField) { + if (!m_pFormFillEnv->IsJSPlatformPresent()) +- return {}; ++ return absl::nullopt; + + WideString sValue = pFormField->GetValue(); + IJS_Runtime* pRuntime = m_pFormFillEnv->GetIJSRuntime(); +@@ -300,30 +302,30 @@ Optional CPDFSDK_InteractiveForm::OnFormat( + } + + CPDF_AAction aAction = pFormField->GetAdditionalAction(); +- if (aAction.GetDict() && aAction.ActionExist(CPDF_AAction::kFormat)) { ++ if (aAction.ActionExist(CPDF_AAction::kFormat)) { + CPDF_Action action = aAction.GetAction(CPDF_AAction::kFormat); +- if (action.GetDict()) { ++ if (action.HasDict()) { + WideString script = action.GetJavaScript(); + if (!script.IsEmpty()) { + IJS_Runtime::ScopedEventContext pContext(pRuntime); + pContext->OnField_Format(pFormField, &sValue); +- Optional err = pContext->RunScript(script); +- if (!err) ++ absl::optional err = pContext->RunScript(script); ++ if (!err.has_value()) + return sValue; + } + } + } +- return {}; ++ return absl::nullopt; + } + + void CPDFSDK_InteractiveForm::ResetFieldAppearance( + CPDF_FormField* pFormField, +- Optional sValue) { ++ absl::optional sValue) { + for (int i = 0, sz = pFormField->CountControls(); i < sz; i++) { + CPDF_FormControl* pFormCtrl = pFormField->GetControl(i); +- ASSERT(pFormCtrl); ++ DCHECK(pFormCtrl); + if (CPDFSDK_Widget* pWidget = GetWidget(pFormCtrl)) +- pWidget->ResetAppearance(sValue, true); ++ pWidget->ResetAppearance(sValue, CPDFSDK_Widget::kValueChanged); + } + } + +@@ -331,15 +333,15 @@ void CPDFSDK_InteractiveForm::UpdateField(CPDF_FormField* pFormField) { + auto* formfiller = m_pFormFillEnv->GetInteractiveFormFiller(); + for (int i = 0, sz = pFormField->CountControls(); i < sz; i++) { + CPDF_FormControl* pFormCtrl = pFormField->GetControl(i); +- ASSERT(pFormCtrl); ++ DCHECK(pFormCtrl); + + CPDFSDK_Widget* pWidget = GetWidget(pFormCtrl); + if (!pWidget) + continue; + + IPDF_Page* pPage = pWidget->GetPage(); +- FX_RECT rect = formfiller->GetViewBBox( +- m_pFormFillEnv->GetPageView(pPage, false), pWidget); ++ FX_RECT rect = ++ formfiller->GetViewBBox(m_pFormFillEnv->GetPageView(pPage), pWidget); + m_pFormFillEnv->Invalidate(pPage, rect); + } + } +@@ -347,43 +349,43 @@ void CPDFSDK_InteractiveForm::UpdateField(CPDF_FormField* pFormField) { + bool CPDFSDK_InteractiveForm::OnKeyStrokeCommit(CPDF_FormField* pFormField, + const WideString& csValue) { + CPDF_AAction aAction = pFormField->GetAdditionalAction(); +- if (!aAction.GetDict() || !aAction.ActionExist(CPDF_AAction::kKeyStroke)) ++ if (!aAction.ActionExist(CPDF_AAction::kKeyStroke)) + return true; + + CPDF_Action action = aAction.GetAction(CPDF_AAction::kKeyStroke); +- if (!action.GetDict()) ++ if (!action.HasDict()) + return true; + +- CPDFSDK_FieldAction fa; ++ CFFL_FieldAction fa; + fa.bModifier = false; + fa.bShift = false; + fa.sValue = csValue; +- m_pFormFillEnv->GetActionHandler()->DoAction_FieldJavaScript( +- action, CPDF_AAction::kKeyStroke, m_pFormFillEnv.Get(), pFormField, &fa); ++ m_pFormFillEnv->DoActionFieldJavaScript(action, CPDF_AAction::kKeyStroke, ++ pFormField, &fa); + return fa.bRC; + } + + bool CPDFSDK_InteractiveForm::OnValidate(CPDF_FormField* pFormField, + const WideString& csValue) { + CPDF_AAction aAction = pFormField->GetAdditionalAction(); +- if (!aAction.GetDict() || !aAction.ActionExist(CPDF_AAction::kValidate)) ++ if (!aAction.ActionExist(CPDF_AAction::kValidate)) + return true; + + CPDF_Action action = aAction.GetAction(CPDF_AAction::kValidate); +- if (!action.GetDict()) ++ if (!action.HasDict()) + return true; + +- CPDFSDK_FieldAction fa; ++ CFFL_FieldAction fa; + fa.bModifier = false; + fa.bShift = false; + fa.sValue = csValue; +- m_pFormFillEnv->GetActionHandler()->DoAction_FieldJavaScript( +- action, CPDF_AAction::kValidate, m_pFormFillEnv.Get(), pFormField, &fa); ++ m_pFormFillEnv->DoActionFieldJavaScript(action, CPDF_AAction::kValidate, ++ pFormField, &fa); + return fa.bRC; + } + + bool CPDFSDK_InteractiveForm::DoAction_Hide(const CPDF_Action& action) { +- ASSERT(action.GetDict()); ++ DCHECK(action.GetDict()); + std::vector fields = + GetFieldFromObjects(action.GetAllFields()); + bool bHide = action.GetHideStatus(); +@@ -392,7 +394,7 @@ bool CPDFSDK_InteractiveForm::DoAction_Hide(const CPDF_Action& action) { + for (CPDF_FormField* pField : fields) { + for (int i = 0, sz = pField->CountControls(); i < sz; ++i) { + CPDF_FormControl* pControl = pField->GetControl(i); +- ASSERT(pControl); ++ DCHECK(pControl); + + if (CPDFSDK_Widget* pWidget = GetWidget(pControl)) { + uint32_t nFlags = pWidget->GetFlags(); +@@ -417,8 +419,7 @@ bool CPDFSDK_InteractiveForm::DoAction_SubmitForm(const CPDF_Action& action) { + if (sDestination.IsEmpty()) + return false; + +- const CPDF_Dictionary* pActionDict = action.GetDict(); +- if (pActionDict->KeyExist("Fields")) { ++ if (action.HasFields()) { + uint32_t dwFlags = action.GetFlags(); + std::vector fields = + GetFieldFromObjects(action.GetAllFields()); +@@ -433,7 +434,7 @@ bool CPDFSDK_InteractiveForm::DoAction_SubmitForm(const CPDF_Action& action) { + if (!m_pInteractiveForm->CheckRequiredFields(nullptr, true)) + return false; + +- return SubmitForm(sDestination, false); ++ return SubmitForm(sDestination); + } + + bool CPDFSDK_InteractiveForm::SubmitFields( +@@ -441,15 +442,17 @@ bool CPDFSDK_InteractiveForm::SubmitFields( + const std::vector& fields, + bool bIncludeOrExclude, + bool bUrlEncoded) { +- ByteString textBuf = ExportFieldsToFDFTextBuf(fields, bIncludeOrExclude); +- if (textBuf.IsEmpty()) ++ ByteString text_buf = ExportFieldsToFDFTextBuf(fields, bIncludeOrExclude); ++ if (text_buf.IsEmpty()) + return false; + +- std::vector buffer(textBuf.begin(), textBuf.end()); +- if (bUrlEncoded && !FDFToURLEncodedData(&buffer)) +- return false; ++ if (bUrlEncoded) { ++ text_buf = FDFToURLEncodedData(text_buf); ++ if (text_buf.IsEmpty()) ++ return false; ++ } + +- m_pFormFillEnv->SubmitForm(buffer, csDestination); ++ m_pFormFillEnv->SubmitForm(text_buf.raw_span(), csDestination); + return true; + } + +@@ -457,56 +460,49 @@ ByteString CPDFSDK_InteractiveForm::ExportFieldsToFDFTextBuf( + const std::vector& fields, + bool bIncludeOrExclude) { + std::unique_ptr pFDF = m_pInteractiveForm->ExportToFDF( +- m_pFormFillEnv->GetFilePath(), fields, bIncludeOrExclude, false); ++ m_pFormFillEnv->GetFilePath(), fields, bIncludeOrExclude); + + return pFDF ? pFDF->WriteToString() : ByteString(); + } + +-bool CPDFSDK_InteractiveForm::SubmitForm(const WideString& sDestination, +- bool bUrlEncoded) { ++bool CPDFSDK_InteractiveForm::SubmitForm(const WideString& sDestination) { + if (sDestination.IsEmpty()) + return false; + + std::unique_ptr pFDFDoc = +- m_pInteractiveForm->ExportToFDF(m_pFormFillEnv->GetFilePath(), false); ++ m_pInteractiveForm->ExportToFDF(m_pFormFillEnv->GetFilePath()); + if (!pFDFDoc) + return false; + +- ByteString fdfBuffer = pFDFDoc->WriteToString(); +- if (fdfBuffer.IsEmpty()) +- return false; +- +- std::vector buffer(fdfBuffer.begin(), fdfBuffer.end()); +- if (bUrlEncoded && !FDFToURLEncodedData(&buffer)) ++ ByteString fdf_buffer = pFDFDoc->WriteToString(); ++ if (fdf_buffer.IsEmpty()) + return false; + +- m_pFormFillEnv->SubmitForm(buffer, sDestination); ++ m_pFormFillEnv->SubmitForm(fdf_buffer.raw_span(), sDestination); + return true; + } + + ByteString CPDFSDK_InteractiveForm::ExportFormToFDFTextBuf() { + std::unique_ptr pFDF = +- m_pInteractiveForm->ExportToFDF(m_pFormFillEnv->GetFilePath(), false); ++ m_pInteractiveForm->ExportToFDF(m_pFormFillEnv->GetFilePath()); + + return pFDF ? pFDF->WriteToString() : ByteString(); + } + + void CPDFSDK_InteractiveForm::DoAction_ResetForm(const CPDF_Action& action) { +- ASSERT(action.GetDict()); +- const CPDF_Dictionary* pActionDict = action.GetDict(); +- if (!pActionDict->KeyExist("Fields")) { +- m_pInteractiveForm->ResetForm(NotificationOption::kNotify); ++ DCHECK(action.GetDict()); ++ if (!action.HasFields()) { ++ m_pInteractiveForm->ResetForm(); + return; + } + uint32_t dwFlags = action.GetFlags(); + std::vector fields = + GetFieldFromObjects(action.GetAllFields()); +- m_pInteractiveForm->ResetForm(fields, !(dwFlags & 0x01), +- NotificationOption::kNotify); ++ m_pInteractiveForm->ResetForm(fields, !(dwFlags & 0x01)); + } + + std::vector CPDFSDK_InteractiveForm::GetFieldFromObjects( +- const std::vector& objects) const { ++ const std::vector>& objects) const { + std::vector fields; + for (const CPDF_Object* pObject : objects) { + if (!pObject || !pObject->IsString()) +@@ -558,7 +554,7 @@ void CPDFSDK_InteractiveForm::AfterSelectionChange(CPDF_FormField* pField) { + return; + + OnCalculate(pField); +- ResetFieldAppearance(pField, pdfium::nullopt); ++ ResetFieldAppearance(pField, absl::nullopt); + UpdateField(pField); + } + +@@ -592,9 +588,9 @@ bool CPDFSDK_InteractiveForm::IsNeedHighLight(FormFieldType fieldType) const { + } + + void CPDFSDK_InteractiveForm::RemoveAllHighLights() { +- std::fill(m_HighlightColor, m_HighlightColor + kFormFieldTypeCount, ++ std::fill(std::begin(m_HighlightColor), std::end(m_HighlightColor), + kWhiteBGR); +- std::fill(m_NeedsHighlight, m_NeedsHighlight + kFormFieldTypeCount, false); ++ std::fill(std::begin(m_NeedsHighlight), std::end(m_NeedsHighlight), false); + } + + void CPDFSDK_InteractiveForm::SetHighlightColor(FX_COLORREF clr, +diff --git a/fpdfsdk/cpdfsdk_interactiveform.h b/fpdfsdk/cpdfsdk_interactiveform.h +index df931d118..223e3d422 100644 +--- a/fpdfsdk/cpdfsdk_interactiveform.h ++++ b/fpdfsdk/cpdfsdk_interactiveform.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,7 @@ + #ifndef FPDFSDK_CPDFSDK_INTERACTIVEFORM_H_ + #define FPDFSDK_CPDFSDK_INTERACTIVEFORM_H_ + ++#include + #include + #include + #include +@@ -14,9 +15,9 @@ + #include "core/fpdfdoc/cpdf_action.h" + #include "core/fpdfdoc/cpdf_interactiveform.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" + #include "fpdfsdk/cpdfsdk_widget.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CPDF_Dictionary; + class CPDF_FormControl; +@@ -33,15 +34,12 @@ class CPDFSDK_InteractiveForm final + CPDF_InteractiveForm* GetInteractiveForm() const { + return m_pInteractiveForm.get(); + } +- CPDFSDK_FormFillEnvironment* GetFormFillEnv() const { +- return m_pFormFillEnv.Get(); +- } + + CPDFSDK_Widget* GetWidget(CPDF_FormControl* pControl) const; + void GetWidgets(const WideString& sFieldName, +- std::vector>* widgets) const; ++ std::vector>* widgets) const; + void GetWidgets(CPDF_FormField* pField, +- std::vector>* widgets) const; ++ std::vector>* widgets) const; + + void AddMap(CPDF_FormControl* pControl, CPDFSDK_Widget* pWidget); + void RemoveMap(CPDF_FormControl* pControl); +@@ -60,10 +58,10 @@ class CPDFSDK_InteractiveForm final + bool OnKeyStrokeCommit(CPDF_FormField* pFormField, const WideString& csValue); + bool OnValidate(CPDF_FormField* pFormField, const WideString& csValue); + void OnCalculate(CPDF_FormField* pFormField); +- Optional OnFormat(CPDF_FormField* pFormField); ++ absl::optional OnFormat(CPDF_FormField* pFormField); + + void ResetFieldAppearance(CPDF_FormField* pFormField, +- Optional sValue); ++ absl::optional sValue); + void UpdateField(CPDF_FormField* pFormField); + + bool DoAction_Hide(const CPDF_Action& action); +@@ -71,12 +69,12 @@ class CPDFSDK_InteractiveForm final + void DoAction_ResetForm(const CPDF_Action& action); + + std::vector GetFieldFromObjects( +- const std::vector& objects) const; ++ const std::vector>& objects) const; + bool SubmitFields(const WideString& csDestination, + const std::vector& fields, + bool bIncludeOrExclude, + bool bUrlEncoded); +- bool SubmitForm(const WideString& sDestination, bool bUrlEncoded); ++ bool SubmitForm(const WideString& sDestination); + ByteString ExportFormToFDFTextBuf(); + ByteString ExportFieldsToFDFTextBuf( + const std::vector& fields, +@@ -102,11 +100,14 @@ class CPDFSDK_InteractiveForm final + void AfterFormReset(CPDF_InteractiveForm* pForm) override; + + int GetPageIndexByAnnotDict(CPDF_Document* pDocument, +- CPDF_Dictionary* pAnnotDict) const; ++ const CPDF_Dictionary* pAnnotDict) const; + + UnownedPtr const m_pFormFillEnv; + std::unique_ptr const m_pInteractiveForm; +- std::map m_Map; ++ std::map, ++ UnownedPtr, ++ std::less<>> ++ m_Map; + #ifdef PDF_ENABLE_XFA + bool m_bXfaCalculate = true; + bool m_bXfaValidationsEnabled = true; +diff --git a/fpdfsdk/cpdfsdk_pageview.cpp b/fpdfsdk/cpdfsdk_pageview.cpp +index 9e4adc865..1bbaeebf3 100644 +--- a/fpdfsdk/cpdfsdk_pageview.cpp ++++ b/fpdfsdk/cpdfsdk_pageview.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,7 @@ + #include "fpdfsdk/cpdfsdk_pageview.h" + + #include ++#include + #include + + #include "core/fpdfapi/parser/cpdf_dictionary.h" +@@ -15,25 +16,26 @@ + #include "core/fpdfdoc/cpdf_annotlist.h" + #include "core/fpdfdoc/cpdf_interactiveform.h" + #include "core/fxcrt/autorestorer.h" ++#include "core/fxcrt/stl_util.h" + #include "fpdfsdk/cpdfsdk_annot.h" + #include "fpdfsdk/cpdfsdk_annotiteration.h" ++#include "fpdfsdk/cpdfsdk_annotiterator.h" + #include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "fpdfsdk/cpdfsdk_interactiveform.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" + + #ifdef PDF_ENABLE_XFA + #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h" + #include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h" +-#include "xfa/fxfa/cxfa_ffdocview.h" + #include "xfa/fxfa/cxfa_ffpageview.h" + #endif // PDF_ENABLE_XFA + + CPDFSDK_PageView::CPDFSDK_PageView(CPDFSDK_FormFillEnvironment* pFormFillEnv, + IPDF_Page* page) + : m_page(page), m_pFormFillEnv(pFormFillEnv) { +- ASSERT(m_page); ++ DCHECK(m_page); + CPDF_Page* pPDFPage = ToPDFPage(page); + if (pPDFPage) { + CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm(); +@@ -46,22 +48,26 @@ CPDFSDK_PageView::CPDFSDK_PageView(CPDFSDK_FormFillEnvironment* pFormFillEnv, + + CPDFSDK_PageView::~CPDFSDK_PageView() { + if (!m_page->AsXFAPage()) { +- // The call to |ReleaseAnnot| can cause the page pointed to by |m_page| to +- // be freed, which will cause issues if we try to cleanup the pageview +- // pointer in |m_page|. So, reset the pageview pointer before doing anything +- // else. ++ // Deleting from `m_SDKAnnotArray` below can cause the page pointed to by ++ // `m_page` to be freed, which will cause issues if we try to cleanup the ++ // pageview pointer in `m_page`. So, reset the pageview pointer before doing ++ // anything else. + m_page->AsPDFPage()->SetView(nullptr); + } + +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- for (CPDFSDK_Annot* pAnnot : m_SDKAnnotArray) +- pAnnotHandlerMgr->ReleaseAnnot(pdfium::WrapUnique(pAnnot)); ++ // Manually reset elements to ensure they are deleted in order. ++ for (std::unique_ptr& pAnnot : m_SDKAnnotArray) ++ pAnnot.reset(); + + m_SDKAnnotArray.clear(); + m_pAnnotList.reset(); + } + ++void CPDFSDK_PageView::ClearPage(CPDF_Page* pPage) { ++ if (!IsBeingDestroyed()) ++ GetFormFillEnv()->RemovePageView(pPage); ++} ++ + void CPDFSDK_PageView::PageView_OnDraw(CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device, + CPDF_RenderOptions* pOptions, +@@ -80,19 +86,41 @@ void CPDFSDK_PageView::PageView_OnDraw(CFX_RenderDevice* pDevice, + #endif // PDF_ENABLE_XFA + + // for pdf/static xfa. +- CPDFSDK_AnnotIteration annotIteration(this, true); +- for (const auto& pSDKAnnot : annotIteration) { +- m_pFormFillEnv->GetAnnotHandlerMgr()->Annot_OnDraw( +- this, pSDKAnnot.Get(), pDevice, mtUser2Device, +- pOptions->GetDrawAnnots()); ++ auto annot_iteration = CPDFSDK_AnnotIteration::CreateForDrawing(this); ++ for (const auto& pSDKAnnot : annot_iteration) { ++ pSDKAnnot->OnDraw(pDevice, mtUser2Device, pOptions->GetDrawAnnots()); ++ } ++} ++ ++std::unique_ptr CPDFSDK_PageView::NewAnnot(CPDF_Annot* annot) { ++ const CPDF_Annot::Subtype sub_type = annot->GetSubtype(); ++ if (sub_type == CPDF_Annot::Subtype::WIDGET) { ++ CPDFSDK_InteractiveForm* form = m_pFormFillEnv->GetInteractiveForm(); ++ CPDF_InteractiveForm* pdf_form = form->GetInteractiveForm(); ++ CPDF_FormControl* form_control = ++ pdf_form->GetControlByDict(annot->GetAnnotDict()); ++ if (!form_control) ++ return nullptr; ++ ++ auto ret = std::make_unique(annot, this, form); ++ form->AddMap(form_control, ret.get()); ++ if (pdf_form->NeedConstructAP()) ++ ret->ResetAppearance(absl::nullopt, CPDFSDK_Widget::kValueUnchanged); ++ return ret; + } ++ ++#ifdef PDF_ENABLE_XFA ++ if (sub_type == CPDF_Annot::Subtype::XFAWIDGET) ++ return nullptr; ++#endif // PDF_ENABLE_XFA ++ ++ return std::make_unique(annot, this); + } + + CPDFSDK_Annot* CPDFSDK_PageView::GetFXAnnotAtPoint(const CFX_PointF& point) { +- CPDFSDK_AnnotHandlerMgr* pAnnotMgr = m_pFormFillEnv->GetAnnotHandlerMgr(); +- CPDFSDK_AnnotIteration annotIteration(this, false); +- for (const auto& pSDKAnnot : annotIteration) { +- CFX_FloatRect rc = pAnnotMgr->Annot_OnGetViewBBox(this, pSDKAnnot.Get()); ++ CPDFSDK_AnnotIteration annot_iteration(this); ++ for (const auto& pSDKAnnot : annot_iteration) { ++ CFX_FloatRect rc = pSDKAnnot->GetViewBBox(); + if (pSDKAnnot->GetAnnotSubtype() == CPDF_Annot::Subtype::POPUP) + continue; + if (rc.Contains(point)) +@@ -102,67 +130,60 @@ CPDFSDK_Annot* CPDFSDK_PageView::GetFXAnnotAtPoint(const CFX_PointF& point) { + } + + CPDFSDK_Annot* CPDFSDK_PageView::GetFXWidgetAtPoint(const CFX_PointF& point) { +- CPDFSDK_AnnotHandlerMgr* pAnnotMgr = m_pFormFillEnv->GetAnnotHandlerMgr(); +- CPDFSDK_AnnotIteration annotIteration(this, false); +- for (const auto& pSDKAnnot : annotIteration) { +- bool bHitTest = pSDKAnnot->GetAnnotSubtype() == CPDF_Annot::Subtype::WIDGET; ++ CPDFSDK_AnnotIteration annot_iteration(this); ++ for (const auto& pSDKAnnot : annot_iteration) { ++ const CPDF_Annot::Subtype sub_type = pSDKAnnot->GetAnnotSubtype(); ++ bool do_hit_test = sub_type == CPDF_Annot::Subtype::WIDGET; + #ifdef PDF_ENABLE_XFA +- bHitTest = bHitTest || +- pSDKAnnot->GetAnnotSubtype() == CPDF_Annot::Subtype::XFAWIDGET; ++ do_hit_test = do_hit_test || sub_type == CPDF_Annot::Subtype::XFAWIDGET; + #endif // PDF_ENABLE_XFA +- if (bHitTest) { +- pAnnotMgr->Annot_OnGetViewBBox(this, pSDKAnnot.Get()); +- if (pAnnotMgr->Annot_OnHitTest(this, pSDKAnnot.Get(), point)) +- return pSDKAnnot.Get(); +- } ++ if (do_hit_test && pSDKAnnot->DoHitTest(point)) ++ return pSDKAnnot.Get(); + } + return nullptr; + } + + #ifdef PDF_ENABLE_XFA +-CPDFSDK_Annot* CPDFSDK_PageView::AddAnnot(CXFA_FFWidget* pPDFAnnot) { +- CPDFSDK_Annot* pSDKAnnot = GetAnnotByXFAWidget(pPDFAnnot); ++CPDFSDK_Annot* CPDFSDK_PageView::AddAnnotForFFWidget(CXFA_FFWidget* pWidget) { ++ CPDFSDK_Annot* pSDKAnnot = GetAnnotForFFWidget(pWidget); + if (pSDKAnnot) + return pSDKAnnot; + +- CPDFSDK_AnnotHandlerMgr* pAnnotHandler = m_pFormFillEnv->GetAnnotHandlerMgr(); +- std::unique_ptr pNewAnnot = +- pAnnotHandler->NewXFAAnnot(pPDFAnnot, this); +- ASSERT(pNewAnnot); +- pSDKAnnot = pNewAnnot.get(); +- // TODO(thestig): See if |m_SDKAnnotArray|, which takes ownership of +- // |pNewAnnot|, can hold std::unique_ptrs instead of raw pointers. +- m_SDKAnnotArray.push_back(pNewAnnot.release()); +- return pSDKAnnot; ++ m_SDKAnnotArray.push_back(std::make_unique(pWidget, this)); ++ return m_SDKAnnotArray.back().get(); + } + +-bool CPDFSDK_PageView::DeleteAnnot(CPDFSDK_Annot* pAnnot) { ++void CPDFSDK_PageView::DeleteAnnotForFFWidget(CXFA_FFWidget* pWidget) { ++ CPDFSDK_Annot* pAnnot = GetAnnotForFFWidget(pWidget); ++ if (!pAnnot) ++ return; ++ + IPDF_Page* pPage = pAnnot->GetXFAPage(); + if (!pPage) +- return false; ++ return; + + CPDF_Document::Extension* pContext = pPage->GetDocument()->GetExtension(); + if (pContext && !pContext->ContainsExtensionForm()) +- return false; ++ return; + + ObservedPtr pObserved(pAnnot); + if (GetFocusAnnot() == pAnnot) +- m_pFormFillEnv->KillFocusAnnot(0); // May invoke JS, invalidating pAnnot. ++ m_pFormFillEnv->KillFocusAnnot({}); // May invoke JS, invalidating pAnnot. + + if (pObserved) { +- CPDFSDK_AnnotHandlerMgr* pAnnotHandler = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- if (pAnnotHandler) +- pAnnotHandler->ReleaseAnnot(pdfium::WrapUnique(pObserved.Get())); ++ auto it = std::find(m_SDKAnnotArray.begin(), m_SDKAnnotArray.end(), ++ fxcrt::MakeFakeUniquePtr(pAnnot)); ++ if (it != m_SDKAnnotArray.end()) ++ m_SDKAnnotArray.erase(it); + } + +- auto it = std::find(m_SDKAnnotArray.begin(), m_SDKAnnotArray.end(), pAnnot); +- if (it != m_SDKAnnotArray.end()) +- m_SDKAnnotArray.erase(it); + if (m_pCaptureWidget.Get() == pAnnot) + m_pCaptureWidget.Reset(); ++} + +- return true; ++CPDFXFA_Page* CPDFSDK_PageView::XFAPageIfNotBackedByPDFPage() { ++ auto* pPage = static_cast(GetXFAPage()); ++ return pPage && !pPage->AsPDFPage() ? pPage : nullptr; + } + #endif // PDF_ENABLE_XFA + +@@ -174,22 +195,36 @@ CPDF_Page* CPDFSDK_PageView::GetPDFPage() const { + return ToPDFPage(m_page); + } + +-CPDFSDK_Annot* CPDFSDK_PageView::GetAnnotByDict(CPDF_Dictionary* pDict) { +- for (CPDFSDK_Annot* pAnnot : m_SDKAnnotArray) { +- if (pAnnot->GetPDFAnnot()->GetAnnotDict() == pDict) +- return pAnnot; ++CPDFSDK_InteractiveForm* CPDFSDK_PageView::GetInteractiveForm() const { ++ return m_pFormFillEnv->GetInteractiveForm(); ++} ++ ++std::vector CPDFSDK_PageView::GetAnnotList() const { ++ std::vector list; ++ list.reserve(m_SDKAnnotArray.size()); ++ for (const std::unique_ptr& elem : m_SDKAnnotArray) ++ list.push_back(elem.get()); ++ return list; ++} ++ ++CPDFSDK_Annot* CPDFSDK_PageView::GetAnnotByDict(const CPDF_Dictionary* pDict) { ++ for (std::unique_ptr& pAnnot : m_SDKAnnotArray) { ++ CPDF_Annot* pPDFAnnot = pAnnot->GetPDFAnnot(); ++ if (pPDFAnnot && pPDFAnnot->GetAnnotDict() == pDict) ++ return pAnnot.get(); + } + return nullptr; + } + + #ifdef PDF_ENABLE_XFA +-CPDFSDK_Annot* CPDFSDK_PageView::GetAnnotByXFAWidget(CXFA_FFWidget* hWidget) { +- if (!hWidget) ++CPDFSDK_Annot* CPDFSDK_PageView::GetAnnotForFFWidget(CXFA_FFWidget* pWidget) { ++ if (!pWidget) + return nullptr; + +- for (CPDFSDK_Annot* pAnnot : m_SDKAnnotArray) { +- if (ToXFAWidget(pAnnot)->GetXFAFFWidget() == hWidget) +- return pAnnot; ++ for (std::unique_ptr& pAnnot : m_SDKAnnotArray) { ++ CPDFXFA_Widget* pCurrentWidget = pAnnot->AsXFAWidget(); ++ if (pCurrentWidget && pCurrentWidget->GetXFAFFWidget() == pWidget) ++ return pAnnot.get(); + } + return nullptr; + } +@@ -200,281 +235,306 @@ IPDF_Page* CPDFSDK_PageView::GetXFAPage() { + #endif // PDF_ENABLE_XFA + + WideString CPDFSDK_PageView::GetFocusedFormText() { +- if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) { +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- return pAnnotHandlerMgr->Annot_GetText(pAnnot); +- } ++ CPDFSDK_Annot* annot = GetFocusAnnot(); ++ return annot ? annot->GetText() : WideString(); ++} ++ ++CPDFSDK_Annot* CPDFSDK_PageView::GetNextAnnot(CPDFSDK_Annot* pAnnot) { ++#ifdef PDF_ENABLE_XFA ++ CPDFXFA_Page* pXFAPage = XFAPageIfNotBackedByPDFPage(); ++ if (pXFAPage) ++ return pXFAPage->GetNextXFAAnnot(pAnnot); ++#endif // PDF_ENABLE_XFA ++ CPDFSDK_AnnotIterator ai(this, GetFormFillEnv()->GetFocusableAnnotSubtypes()); ++ return ai.GetNextAnnot(pAnnot); ++} ++ ++CPDFSDK_Annot* CPDFSDK_PageView::GetPrevAnnot(CPDFSDK_Annot* pAnnot) { ++#ifdef PDF_ENABLE_XFA ++ CPDFXFA_Page* pXFAPage = XFAPageIfNotBackedByPDFPage(); ++ if (pXFAPage) ++ return pXFAPage->GetPrevXFAAnnot(pAnnot); ++#endif // PDF_ENABLE_XFA ++ CPDFSDK_AnnotIterator ai(this, GetFormFillEnv()->GetFocusableAnnotSubtypes()); ++ return ai.GetPrevAnnot(pAnnot); ++} ++ ++CPDFSDK_Annot* CPDFSDK_PageView::GetFirstFocusableAnnot() { ++#ifdef PDF_ENABLE_XFA ++ CPDFXFA_Page* pXFAPage = XFAPageIfNotBackedByPDFPage(); ++ if (pXFAPage) ++ return pXFAPage->GetFirstXFAAnnot(this); ++#endif // PDF_ENABLE_XFA ++ CPDFSDK_AnnotIterator ai(this, GetFormFillEnv()->GetFocusableAnnotSubtypes()); ++ return ai.GetFirstAnnot(); ++} + +- return WideString(); ++CPDFSDK_Annot* CPDFSDK_PageView::GetLastFocusableAnnot() { ++#ifdef PDF_ENABLE_XFA ++ CPDFXFA_Page* pXFAPage = XFAPageIfNotBackedByPDFPage(); ++ if (pXFAPage) ++ return pXFAPage->GetLastXFAAnnot(this); ++#endif // PDF_ENABLE_XFA ++ CPDFSDK_AnnotIterator ai(this, GetFormFillEnv()->GetFocusableAnnotSubtypes()); ++ return ai.GetLastAnnot(); + } + + WideString CPDFSDK_PageView::GetSelectedText() { +- if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) { +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- return pAnnotHandlerMgr->Annot_GetSelectedText(pAnnot); +- } ++ CPDFSDK_Annot* annot = GetFocusAnnot(); ++ if (!annot) ++ return WideString(); ++ return annot->GetSelectedText(); ++} + +- return WideString(); ++void CPDFSDK_PageView::ReplaceAndKeepSelection(const WideString& text) { ++ CPDFSDK_Annot* annot = GetFocusAnnot(); ++ if (annot) ++ annot->ReplaceAndKeepSelection(text); + } + + void CPDFSDK_PageView::ReplaceSelection(const WideString& text) { +- if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) { +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- pAnnotHandlerMgr->Annot_ReplaceSelection(pAnnot, text); +- } ++ CPDFSDK_Annot* annot = GetFocusAnnot(); ++ if (annot) ++ annot->ReplaceSelection(text); ++} ++ ++bool CPDFSDK_PageView::SelectAllText() { ++ CPDFSDK_Annot* annot = GetFocusAnnot(); ++ return annot && annot->SelectAllText(); + } + + bool CPDFSDK_PageView::CanUndo() { +- if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) { +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- return pAnnotHandlerMgr->Annot_CanUndo(pAnnot); +- } +- return false; ++ CPDFSDK_Annot* annot = GetFocusAnnot(); ++ return annot && annot->CanUndo(); + } + + bool CPDFSDK_PageView::CanRedo() { +- if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) { +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- return pAnnotHandlerMgr->Annot_CanRedo(pAnnot); +- } +- return false; ++ CPDFSDK_Annot* annot = GetFocusAnnot(); ++ return annot && annot->CanRedo(); + } + + bool CPDFSDK_PageView::Undo() { +- if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) { +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- return pAnnotHandlerMgr->Annot_Undo(pAnnot); +- } +- return false; ++ CPDFSDK_Annot* annot = GetFocusAnnot(); ++ return annot && annot->Undo(); + } + + bool CPDFSDK_PageView::Redo() { +- if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) { +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- return pAnnotHandlerMgr->Annot_Redo(pAnnot); +- } +- return false; ++ CPDFSDK_Annot* annot = GetFocusAnnot(); ++ return annot && annot->Redo(); + } + +-bool CPDFSDK_PageView::OnFocus(const CFX_PointF& point, uint32_t nFlag) { ++bool CPDFSDK_PageView::OnFocus(Mask nFlags, ++ const CFX_PointF& point) { + ObservedPtr pAnnot(GetFXWidgetAtPoint(point)); + if (!pAnnot) { +- m_pFormFillEnv->KillFocusAnnot(nFlag); ++ m_pFormFillEnv->KillFocusAnnot(nFlags); + return false; + } + +- m_pFormFillEnv->SetFocusAnnot(&pAnnot); ++ m_pFormFillEnv->SetFocusAnnot(pAnnot); + return true; + } + +-bool CPDFSDK_PageView::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { ++bool CPDFSDK_PageView::OnLButtonDown(Mask nFlags, ++ const CFX_PointF& point) { + ObservedPtr pAnnot(GetFXWidgetAtPoint(point)); + if (!pAnnot) { +- m_pFormFillEnv->KillFocusAnnot(nFlag); ++ m_pFormFillEnv->KillFocusAnnot(nFlags); + return false; + } + +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- if (!pAnnotHandlerMgr->Annot_OnLButtonDown(this, &pAnnot, nFlag, point)) ++ if (!CPDFSDK_Annot::OnLButtonDown(pAnnot, nFlags, point)) + return false; + + if (!pAnnot) + return false; + +- m_pFormFillEnv->SetFocusAnnot(&pAnnot); ++ m_pFormFillEnv->SetFocusAnnot(pAnnot); + return true; + } + +-bool CPDFSDK_PageView::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); ++bool CPDFSDK_PageView::OnLButtonUp(Mask nFlags, ++ const CFX_PointF& point) { + ObservedPtr pFXAnnot(GetFXWidgetAtPoint(point)); + ObservedPtr pFocusAnnot(GetFocusAnnot()); + if (pFocusAnnot && pFocusAnnot != pFXAnnot) { + // Last focus Annot gets a chance to handle the event. +- if (pAnnotHandlerMgr->Annot_OnLButtonUp(this, &pFocusAnnot, nFlag, point)) ++ if (CPDFSDK_Annot::OnLButtonUp(pFocusAnnot, nFlags, point)) + return true; + } +- return pFXAnnot && +- pAnnotHandlerMgr->Annot_OnLButtonUp(this, &pFXAnnot, nFlag, point); ++ return pFXAnnot && CPDFSDK_Annot::OnLButtonUp(pFXAnnot, nFlags, point); + } + +-bool CPDFSDK_PageView::OnLButtonDblClk(const CFX_PointF& point, +- uint32_t nFlag) { ++bool CPDFSDK_PageView::OnLButtonDblClk(Mask nFlags, ++ const CFX_PointF& point) { + ObservedPtr pAnnot(GetFXWidgetAtPoint(point)); + if (!pAnnot) { +- m_pFormFillEnv->KillFocusAnnot(nFlag); ++ m_pFormFillEnv->KillFocusAnnot(nFlags); + return false; + } + +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- if (!pAnnotHandlerMgr->Annot_OnLButtonDblClk(this, &pAnnot, nFlag, point)) ++ if (!CPDFSDK_Annot::OnLButtonDblClk(pAnnot, nFlags, point)) + return false; + + if (!pAnnot) + return false; + +- m_pFormFillEnv->SetFocusAnnot(&pAnnot); ++ m_pFormFillEnv->SetFocusAnnot(pAnnot); + return true; + } + +-bool CPDFSDK_PageView::OnRButtonDown(const CFX_PointF& point, uint32_t nFlag) { ++bool CPDFSDK_PageView::OnRButtonDown(Mask nFlags, ++ const CFX_PointF& point) { + ObservedPtr pAnnot(GetFXWidgetAtPoint(point)); + if (!pAnnot) + return false; + +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- bool ok = pAnnotHandlerMgr->Annot_OnRButtonDown(this, &pAnnot, nFlag, point); ++ bool ok = CPDFSDK_Annot::OnRButtonDown(pAnnot, nFlags, point); + if (!pAnnot) + return false; + + if (ok) +- m_pFormFillEnv->SetFocusAnnot(&pAnnot); ++ m_pFormFillEnv->SetFocusAnnot(pAnnot); + + return true; + } + +-bool CPDFSDK_PageView::OnRButtonUp(const CFX_PointF& point, uint32_t nFlag) { ++bool CPDFSDK_PageView::OnRButtonUp(Mask nFlags, ++ const CFX_PointF& point) { + ObservedPtr pAnnot(GetFXWidgetAtPoint(point)); + if (!pAnnot) + return false; + +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- bool ok = pAnnotHandlerMgr->Annot_OnRButtonUp(this, &pAnnot, nFlag, point); ++ bool ok = CPDFSDK_Annot::OnRButtonUp(pAnnot, nFlags, point); + if (!pAnnot) + return false; + + if (ok) +- m_pFormFillEnv->SetFocusAnnot(&pAnnot); ++ m_pFormFillEnv->SetFocusAnnot(pAnnot); + + return true; + } + +-bool CPDFSDK_PageView::OnMouseMove(const CFX_PointF& point, int nFlag) { +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- ++bool CPDFSDK_PageView::OnMouseMove(Mask nFlags, ++ const CFX_PointF& point) { + ObservedPtr pFXAnnot(GetFXAnnotAtPoint(point)); + ObservedPtr pThis(this); + + if (m_bOnWidget && m_pCaptureWidget != pFXAnnot) +- ExitWidget(pAnnotHandlerMgr, true, nFlag); ++ ExitWidget(true, nFlags); + + // ExitWidget() may have invalidated objects. + if (!pThis || !pFXAnnot) + return false; + + if (!m_bOnWidget) { +- EnterWidget(pAnnotHandlerMgr, &pFXAnnot, nFlag); ++ EnterWidget(pFXAnnot, nFlags); + + // EnterWidget() may have invalidated objects. + if (!pThis) + return false; + + if (!pFXAnnot) { +- ExitWidget(pAnnotHandlerMgr, false, nFlag); ++ ExitWidget(false, nFlags); + return true; + } + } +- pAnnotHandlerMgr->Annot_OnMouseMove(this, &pFXAnnot, nFlag, point); ++ CPDFSDK_Annot::OnMouseMove(pFXAnnot, nFlags, point); + return true; + } + +-void CPDFSDK_PageView::EnterWidget(CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr, +- ObservedPtr* pAnnot, +- uint32_t nFlag) { ++void CPDFSDK_PageView::EnterWidget(ObservedPtr& pAnnot, ++ Mask nFlags) { + m_bOnWidget = true; +- m_pCaptureWidget.Reset(pAnnot->Get()); +- pAnnotHandlerMgr->Annot_OnMouseEnter(this, pAnnot, nFlag); ++ m_pCaptureWidget.Reset(pAnnot.Get()); ++ CPDFSDK_Annot::OnMouseEnter(m_pCaptureWidget, nFlags); + } + +-void CPDFSDK_PageView::ExitWidget(CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr, +- bool callExitCallback, +- uint32_t nFlag) { ++void CPDFSDK_PageView::ExitWidget(bool callExitCallback, ++ Mask nFlags) { + m_bOnWidget = false; +- if (m_pCaptureWidget) { +- if (callExitCallback) +- pAnnotHandlerMgr->Annot_OnMouseExit(this, &m_pCaptureWidget, nFlag); ++ if (!m_pCaptureWidget) ++ return; + +- m_pCaptureWidget.Reset(); ++ if (callExitCallback) { ++ ObservedPtr pThis(this); ++ CPDFSDK_Annot::OnMouseExit(m_pCaptureWidget, nFlags); ++ ++ // OnMouseExit() may have invalidated |this|. ++ if (!pThis) ++ return; + } ++ ++ m_pCaptureWidget.Reset(); + } + +-bool CPDFSDK_PageView::OnMouseWheel(double deltaX, +- double deltaY, ++bool CPDFSDK_PageView::OnMouseWheel(Mask nFlags, + const CFX_PointF& point, +- int nFlag) { ++ const CFX_Vector& delta) { + ObservedPtr pAnnot(GetFXWidgetAtPoint(point)); + if (!pAnnot) + return false; + +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- return pAnnotHandlerMgr->Annot_OnMouseWheel(this, &pAnnot, nFlag, +- static_cast(deltaY), point); ++ return CPDFSDK_Annot::OnMouseWheel(pAnnot, nFlags, point, delta); + } + + bool CPDFSDK_PageView::SetIndexSelected(int index, bool selected) { +- if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) { +- ObservedPtr pAnnotObserved(pAnnot); +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- return pAnnotHandlerMgr->Annot_SetIndexSelected(&pAnnotObserved, index, +- selected); +- } +- +- return false; ++ CPDFSDK_Annot* annot = GetFocusAnnot(); ++ return annot && annot->SetIndexSelected(index, selected); + } + + bool CPDFSDK_PageView::IsIndexSelected(int index) { +- if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) { +- ObservedPtr pAnnotObserved(pAnnot); +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- return pAnnotHandlerMgr->Annot_IsIndexSelected(&pAnnotObserved, index); +- } ++ CPDFSDK_Annot* annot = GetFocusAnnot(); ++ return annot && annot->IsIndexSelected(index); ++} + +- return false; ++bool CPDFSDK_PageView::OnChar(uint32_t nChar, Mask nFlags) { ++ ObservedPtr pAnnot(GetFocusAnnot()); ++ return pAnnot && CPDFSDK_Annot::OnChar(pAnnot, nChar, nFlags); + } + +-bool CPDFSDK_PageView::OnChar(int nChar, uint32_t nFlag) { +- if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) { +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- return pAnnotHandlerMgr->Annot_OnChar(pAnnot, nChar, nFlag); ++bool CPDFSDK_PageView::OnKeyDown(FWL_VKEYCODE nKeyCode, ++ Mask nFlags) { ++ ObservedPtr pAnnot(GetFocusAnnot()); ++ if (!pAnnot) { ++ // If pressed key is not tab then no action is needed. ++ if (nKeyCode != FWL_VKEY_Tab) ++ return false; ++ ++ // If ctrl key or alt key is pressed, then no action is needed. ++ if (CPWL_Wnd::IsCTRLKeyDown(nFlags) || CPWL_Wnd::IsALTKeyDown(nFlags)) ++ return false; ++ ++ ObservedPtr end_annot(CPWL_Wnd::IsSHIFTKeyDown(nFlags) ++ ? GetLastFocusableAnnot() ++ : GetFirstFocusableAnnot()); ++ return end_annot && m_pFormFillEnv->SetFocusAnnot(end_annot); + } + +- return false; +-} ++ if (CPWL_Wnd::IsCTRLKeyDown(nFlags) || CPWL_Wnd::IsALTKeyDown(nFlags)) ++ return CPDFSDK_Annot::OnKeyDown(pAnnot, nKeyCode, nFlags); + +-bool CPDFSDK_PageView::OnKeyDown(int nKeyCode, int nFlag) { +- if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) { +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- return pAnnotHandlerMgr->Annot_OnKeyDown(pAnnot, nKeyCode, nFlag); ++ CPDFSDK_Annot* pFocusAnnot = GetFocusAnnot(); ++ if (pFocusAnnot && (nKeyCode == FWL_VKEY_Tab)) { ++ ObservedPtr pNext(CPWL_Wnd::IsSHIFTKeyDown(nFlags) ++ ? GetPrevAnnot(pFocusAnnot) ++ : GetNextAnnot(pFocusAnnot)); ++ if (!pNext) ++ return false; ++ if (pNext.Get() != pFocusAnnot) { ++ GetFormFillEnv()->SetFocusAnnot(pNext); ++ return true; ++ } + } +- return false; +-} + +-bool CPDFSDK_PageView::OnKeyUp(int nKeyCode, int nFlag) { +- return false; ++ // Check |pAnnot| again because JS may have destroyed it in GetNextAnnot(). ++ if (!pAnnot) ++ return false; ++ ++ return CPDFSDK_Annot::OnKeyDown(pAnnot, nKeyCode, nFlags); + } + + void CPDFSDK_PageView::LoadFXAnnots() { +- CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr = +- m_pFormFillEnv->GetAnnotHandlerMgr(); +- + AutoRestorer lock(&m_bLocked); + m_bLocked = true; + +@@ -483,40 +543,36 @@ void CPDFSDK_PageView::LoadFXAnnots() { + CPDF_Document::Extension* pContext = m_pFormFillEnv->GetDocExtension(); + if (pContext && pContext->ContainsExtensionFullForm()) { + CXFA_FFPageView* pageView = protector->GetXFAPageView(); +- std::unique_ptr pWidgetHandler = +- pageView->CreateFormWidgetIterator(XFA_WidgetStatus_Visible | +- XFA_WidgetStatus_Viewable); +- +- while (CXFA_FFWidget* pXFAAnnot = pWidgetHandler->MoveToNext()) { +- std::unique_ptr pNewAnnot = +- pAnnotHandlerMgr->NewXFAAnnot(pXFAAnnot, this); +- ASSERT(pNewAnnot); +- CPDFSDK_Annot* pAnnot = pNewAnnot.get(); +- m_SDKAnnotArray.push_back(pNewAnnot.release()); +- pAnnotHandlerMgr->Annot_OnLoad(pAnnot); ++ CXFA_FFPageWidgetIterator pWidgetHandler( ++ pageView, Mask{XFA_WidgetStatus::kVisible, ++ XFA_WidgetStatus::kViewable}); ++ ++ while (CXFA_FFWidget* pXFAAnnot = pWidgetHandler.MoveToNext()) { ++ m_SDKAnnotArray.push_back( ++ std::make_unique(pXFAAnnot, this)); ++ m_SDKAnnotArray.back()->OnLoad(); + } +- + return; + } + #endif // PDF_ENABLE_XFA + + CPDF_Page* pPage = GetPDFPage(); +- ASSERT(pPage); ++ DCHECK(pPage); + bool bUpdateAP = CPDF_InteractiveForm::IsUpdateAPEnabled(); + // Disable the default AP construction. + CPDF_InteractiveForm::SetUpdateAP(false); +- m_pAnnotList = pdfium::MakeUnique(pPage); ++ m_pAnnotList = std::make_unique(pPage); + CPDF_InteractiveForm::SetUpdateAP(bUpdateAP); + + const size_t nCount = m_pAnnotList->Count(); + for (size_t i = 0; i < nCount; ++i) { + CPDF_Annot* pPDFAnnot = m_pAnnotList->GetAt(i); + CheckForUnsupportedAnnot(pPDFAnnot); +- CPDFSDK_Annot* pAnnot = pAnnotHandlerMgr->NewAnnot(pPDFAnnot, this); ++ std::unique_ptr pAnnot = NewAnnot(pPDFAnnot); + if (!pAnnot) + continue; +- m_SDKAnnotArray.push_back(pAnnot); +- pAnnotHandlerMgr->Annot_OnLoad(pAnnot); ++ m_SDKAnnotArray.push_back(std::move(pAnnot)); ++ m_SDKAnnotArray.back()->OnLoad(); + } + } + +@@ -542,21 +598,11 @@ int CPDFSDK_PageView::GetPageIndex() const { + } + + bool CPDFSDK_PageView::IsValidAnnot(const CPDF_Annot* p) const { +- if (!p) +- return false; +- +- const auto& annots = m_pAnnotList->All(); +- auto it = std::find_if(annots.begin(), annots.end(), +- [p](const std::unique_ptr& annot) { +- return annot.get() == p; +- }); +- return it != annots.end(); ++ return p && m_pAnnotList->Contains(p); + } + + bool CPDFSDK_PageView::IsValidSDKAnnot(const CPDFSDK_Annot* p) const { +- if (!p) +- return false; +- return pdfium::ContainsValue(m_SDKAnnotArray, p); ++ return p && pdfium::Contains(m_SDKAnnotArray, fxcrt::MakeFakeUniquePtr(p)); + } + + CPDFSDK_Annot* CPDFSDK_PageView::GetFocusAnnot() { +@@ -565,7 +611,6 @@ CPDFSDK_Annot* CPDFSDK_PageView::GetFocusAnnot() { + } + + int CPDFSDK_PageView::GetPageIndexForStaticPDF() const { +- const CPDF_Dictionary* pDict = GetPDFPage()->GetDict(); + CPDF_Document* pDoc = m_pFormFillEnv->GetPDFDocument(); +- return pDoc->GetPageIndex(pDict->GetObjNum()); ++ return pDoc->GetPageIndex(GetPDFPage()->GetDict()->GetObjNum()); + } +diff --git a/fpdfsdk/cpdfsdk_pageview.h b/fpdfsdk/cpdfsdk_pageview.h +index 9b040dbb4..b95f8442a 100644 +--- a/fpdfsdk/cpdfsdk_pageview.h ++++ b/fpdfsdk/cpdfsdk_pageview.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,25 +7,35 @@ + #ifndef FPDFSDK_CPDFSDK_PAGEVIEW_H_ + #define FPDFSDK_CPDFSDK_PAGEVIEW_H_ + ++#include ++ + #include + #include + + #include "core/fpdfapi/page/cpdf_page.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/mask.h" + #include "core/fxcrt/unowned_ptr.h" + #include "fpdfsdk/cpdfsdk_annot.h" +-#include "fpdfsdk/cpdfsdk_annothandlermgr.h" + + class CFX_RenderDevice; + class CPDF_AnnotList; + class CPDF_RenderOptions; + class CPDFSDK_FormFillEnvironment; ++class CPDFSDK_InteractiveForm; ++ ++#ifdef PDF_ENABLE_XFA ++class CPDFXFA_Page; ++class CXFA_FFWidget; ++#endif // PDF_ENABLE_XFA + + class CPDFSDK_PageView final : public CPDF_Page::View { + public: + CPDFSDK_PageView(CPDFSDK_FormFillEnvironment* pFormFillEnv, IPDF_Page* page); + ~CPDFSDK_PageView(); + ++ // CPDF_Page::View: ++ void ClearPage(CPDF_Page* pPage) override; ++ + void PageView_OnDraw(CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device, + CPDF_RenderOptions* pOptions, +@@ -33,50 +43,50 @@ class CPDFSDK_PageView final : public CPDF_Page::View { + + void LoadFXAnnots(); + CPDFSDK_Annot* GetFocusAnnot(); ++ CPDFSDK_Annot* GetNextAnnot(CPDFSDK_Annot* pAnnot); ++ CPDFSDK_Annot* GetPrevAnnot(CPDFSDK_Annot* pAnnot); ++ CPDFSDK_Annot* GetFirstFocusableAnnot(); ++ CPDFSDK_Annot* GetLastFocusableAnnot(); + bool IsValidAnnot(const CPDF_Annot* p) const; + bool IsValidSDKAnnot(const CPDFSDK_Annot* p) const; + +- const std::vector& GetAnnotList() const { +- return m_SDKAnnotArray; +- } +- CPDFSDK_Annot* GetAnnotByDict(CPDF_Dictionary* pDict); ++ std::vector GetAnnotList() const; ++ CPDFSDK_Annot* GetAnnotByDict(const CPDF_Dictionary* pDict); + + #ifdef PDF_ENABLE_XFA +- bool DeleteAnnot(CPDFSDK_Annot* pAnnot); +- CPDFSDK_Annot* AddAnnot(CXFA_FFWidget* pPDFAnnot); +- CPDFSDK_Annot* GetAnnotByXFAWidget(CXFA_FFWidget* hWidget); ++ CPDFSDK_Annot* AddAnnotForFFWidget(CXFA_FFWidget* pWidget); ++ void DeleteAnnotForFFWidget(CXFA_FFWidget* pWidget); ++ CPDFSDK_Annot* GetAnnotForFFWidget(CXFA_FFWidget* pWidget); + IPDF_Page* GetXFAPage(); + #endif // PDF_ENABLE_XFA + + CPDF_Page* GetPDFPage() const; + CPDF_Document* GetPDFDocument(); +- CPDFSDK_FormFillEnvironment* GetFormFillEnv() const { +- return m_pFormFillEnv.Get(); +- } ++ CPDFSDK_FormFillEnvironment* GetFormFillEnv() const { return m_pFormFillEnv; } + + WideString GetFocusedFormText(); + WideString GetSelectedText(); ++ void ReplaceAndKeepSelection(const WideString& text); + void ReplaceSelection(const WideString& text); ++ bool SelectAllText(); + + bool CanUndo(); + bool CanRedo(); + bool Undo(); + bool Redo(); + +- bool OnFocus(const CFX_PointF& point, uint32_t nFlag); +- bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag); +- bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag); +- bool OnLButtonDblClk(const CFX_PointF& point, uint32_t nFlag); +- bool OnRButtonDown(const CFX_PointF& point, uint32_t nFlag); +- bool OnRButtonUp(const CFX_PointF& point, uint32_t nFlag); +- bool OnChar(int nChar, uint32_t nFlag); +- bool OnKeyDown(int nKeyCode, int nFlag); +- bool OnKeyUp(int nKeyCode, int nFlag); +- bool OnMouseMove(const CFX_PointF& point, int nFlag); +- bool OnMouseWheel(double deltaX, +- double deltaY, ++ bool OnFocus(Mask nFlags, const CFX_PointF& point); ++ bool OnLButtonDown(Mask nFlags, const CFX_PointF& point); ++ bool OnLButtonUp(Mask nFlags, const CFX_PointF& point); ++ bool OnLButtonDblClk(Mask nFlags, const CFX_PointF& point); ++ bool OnRButtonDown(Mask nFlags, const CFX_PointF& point); ++ bool OnRButtonUp(Mask nFlags, const CFX_PointF& point); ++ bool OnChar(uint32_t nChar, Mask nFlags); ++ bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask nFlags); ++ bool OnMouseMove(Mask nFlags, const CFX_PointF& point); ++ bool OnMouseWheel(Mask nFlags, + const CFX_PointF& point, +- int nFlag); ++ const CFX_Vector& delta); + + bool SetIndexSelected(int index, bool selected); + bool IsIndexSelected(int index); +@@ -92,28 +102,30 @@ class CPDFSDK_PageView final : public CPDF_Page::View { + bool IsLocked() const { return m_bLocked; } + void SetBeingDestroyed() { m_bBeingDestroyed = true; } + bool IsBeingDestroyed() const { return m_bBeingDestroyed; } +- void TakePageOwnership() { m_pOwnsPage.Reset(ToPDFPage(m_page)); } + + private: ++#ifdef PDF_ENABLE_XFA ++ CPDFXFA_Page* XFAPageIfNotBackedByPDFPage(); ++#endif ++ ++ std::unique_ptr NewAnnot(CPDF_Annot* annot); ++ ++ CPDFSDK_InteractiveForm* GetInteractiveForm() const; + CPDFSDK_Annot* GetFXAnnotAtPoint(const CFX_PointF& point); + CPDFSDK_Annot* GetFXWidgetAtPoint(const CFX_PointF& point); + + int GetPageIndexForStaticPDF() const; + +- void EnterWidget(CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr, +- ObservedPtr* pAnnot, +- uint32_t nFlag); +- void ExitWidget(CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr, +- bool callExitCallback, +- uint32_t nFlag); ++ void EnterWidget(ObservedPtr& pAnnot, ++ Mask nFlags); ++ void ExitWidget(bool callExitCallback, Mask nFlags); + + CFX_Matrix m_curMatrix; +- IPDF_Page* const m_page; ++ UnownedPtr const m_page; + std::unique_ptr m_pAnnotList; +- std::vector m_SDKAnnotArray; ++ std::vector> m_SDKAnnotArray; + UnownedPtr const m_pFormFillEnv; + ObservedPtr m_pCaptureWidget; +- RetainPtr m_pOwnsPage; + bool m_bOnWidget = false; + bool m_bValid = false; + bool m_bLocked = false; +diff --git a/fpdfsdk/cpdfsdk_pauseadapter.cpp b/fpdfsdk/cpdfsdk_pauseadapter.cpp +index bf3f1c799..dfefbcf48 100644 +--- a/fpdfsdk/cpdfsdk_pauseadapter.cpp ++++ b/fpdfsdk/cpdfsdk_pauseadapter.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,5 +12,5 @@ CPDFSDK_PauseAdapter::CPDFSDK_PauseAdapter(IFSDK_PAUSE* IPause) + CPDFSDK_PauseAdapter::~CPDFSDK_PauseAdapter() = default; + + bool CPDFSDK_PauseAdapter::NeedToPauseNow() { +- return m_IPause->NeedToPauseNow && m_IPause->NeedToPauseNow(m_IPause.Get()); ++ return m_IPause->NeedToPauseNow && m_IPause->NeedToPauseNow(m_IPause); + } +diff --git a/fpdfsdk/cpdfsdk_pauseadapter.h b/fpdfsdk/cpdfsdk_pauseadapter.h +index dfbc2b412..e44eef1fb 100644 +--- a/fpdfsdk/cpdfsdk_pauseadapter.h ++++ b/fpdfsdk/cpdfsdk_pauseadapter.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,6 @@ + #ifndef FPDFSDK_CPDFSDK_PAUSEADAPTER_H_ + #define FPDFSDK_CPDFSDK_PAUSEADAPTER_H_ + +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/pauseindicator_iface.h" + #include "core/fxcrt/unowned_ptr.h" + #include "public/fpdf_progressive.h" +diff --git a/fpdfsdk/cpdfsdk_renderpage.cpp b/fpdfsdk/cpdfsdk_renderpage.cpp +index ce492e6fe..9ffd68317 100644 +--- a/fpdfsdk/cpdfsdk_renderpage.cpp ++++ b/fpdfsdk/cpdfsdk_renderpage.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2020 PDFium Authors. All rights reserved. ++// Copyright 2020 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,17 +6,17 @@ + + #include "fpdfsdk/cpdfsdk_renderpage.h" + ++#include + #include + +-#include "core/fpdfapi/render/cpdf_pagerendercache.h" ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" + #include "core/fpdfapi/render/cpdf_pagerendercontext.h" + #include "core/fpdfapi/render/cpdf_progressiverenderer.h" + #include "core/fpdfapi/render/cpdf_renderoptions.h" + #include "core/fpdfdoc/cpdf_annotlist.h" + #include "core/fxge/cfx_renderdevice.h" ++#include "fpdfsdk/cpdfsdk_helpers.h" + #include "fpdfsdk/cpdfsdk_pauseadapter.h" +-#include "public/fpdfview.h" +-#include "third_party/base/ptr_util.h" + + namespace { + +@@ -25,10 +25,11 @@ void RenderPageImpl(CPDF_PageRenderContext* pContext, + const CFX_Matrix& matrix, + const FX_RECT& clipping_rect, + int flags, ++ const FPDF_COLORSCHEME* color_scheme, + bool need_to_restore, + CPDFSDK_PauseAdapter* pause) { + if (!pContext->m_pOptions) +- pContext->m_pOptions = pdfium::MakeUnique(); ++ pContext->m_pOptions = std::make_unique(); + + auto& options = pContext->m_pOptions->GetOptions(); + options.bClearType = !!(flags & FPDF_LCD_TEXT); +@@ -43,31 +44,41 @@ void RenderPageImpl(CPDF_PageRenderContext* pContext, + if (flags & FPDF_GRAYSCALE) + pContext->m_pOptions->SetColorMode(CPDF_RenderOptions::kGray); + ++ if (color_scheme) { ++ pContext->m_pOptions->SetColorMode(CPDF_RenderOptions::kForcedColor); ++ SetColorFromScheme(color_scheme, pContext->m_pOptions.get()); ++ options.bConvertFillToStroke = !!(flags & FPDF_CONVERT_FILL_TO_STROKE); ++ } ++ + const CPDF_OCContext::UsageType usage = +- (flags & FPDF_PRINTING) ? CPDF_OCContext::Print : CPDF_OCContext::View; ++ (flags & FPDF_PRINTING) ? CPDF_OCContext::kPrint : CPDF_OCContext::kView; + pContext->m_pOptions->SetOCContext( + pdfium::MakeRetain(pPage->GetDocument(), usage)); + + pContext->m_pDevice->SaveState(); + pContext->m_pDevice->SetBaseClip(clipping_rect); + pContext->m_pDevice->SetClip_Rect(clipping_rect); +- pContext->m_pContext = pdfium::MakeUnique( +- pPage->GetDocument(), pPage->m_pPageResources.Get(), +- static_cast(pPage->GetRenderCache())); ++ pContext->m_pContext = std::make_unique( ++ pPage->GetDocument(), pPage->GetMutablePageResources(), ++ pPage->GetPageImageCache()); + +- pContext->m_pContext->AppendLayer(pPage, &matrix); ++ pContext->m_pContext->AppendLayer(pPage, matrix); + + if (flags & FPDF_ANNOT) { +- auto pOwnedList = pdfium::MakeUnique(pPage); ++ auto pOwnedList = std::make_unique(pPage); + CPDF_AnnotList* pList = pOwnedList.get(); + pContext->m_pAnnots = std::move(pOwnedList); + bool bPrinting = + pContext->m_pDevice->GetDeviceType() != DeviceType::kDisplay; +- pList->DisplayAnnots(pPage, pContext->m_pContext.get(), bPrinting, &matrix, +- false, nullptr); ++ ++ // TODO(https://crbug.com/pdfium/993) - maybe pass true here. ++ const bool bShowWidget = false; ++ pList->DisplayAnnots(pPage, pContext->m_pDevice.get(), ++ pContext->m_pContext.get(), bPrinting, matrix, ++ bShowWidget); + } + +- pContext->m_pRenderer = pdfium::MakeUnique( ++ pContext->m_pRenderer = std::make_unique( + pContext->m_pContext.get(), pContext->m_pDevice.get(), + pContext->m_pOptions.get()); + pContext->m_pRenderer->Start(pause); +@@ -81,8 +92,9 @@ void CPDFSDK_RenderPage(CPDF_PageRenderContext* pContext, + CPDF_Page* pPage, + const CFX_Matrix& matrix, + const FX_RECT& clipping_rect, +- int flags) { +- RenderPageImpl(pContext, pPage, matrix, clipping_rect, flags, ++ int flags, ++ const FPDF_COLORSCHEME* color_scheme) { ++ RenderPageImpl(pContext, pPage, matrix, clipping_rect, flags, color_scheme, + /*need_to_restore=*/true, /*pause=*/nullptr); + } + +@@ -94,9 +106,10 @@ void CPDFSDK_RenderPageWithContext(CPDF_PageRenderContext* pContext, + int size_y, + int rotate, + int flags, ++ const FPDF_COLORSCHEME* color_scheme, + bool need_to_restore, + CPDFSDK_PauseAdapter* pause) { + const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y); + RenderPageImpl(pContext, pPage, pPage->GetDisplayMatrix(rect, rotate), rect, +- flags, need_to_restore, pause); ++ flags, color_scheme, need_to_restore, pause); + } +diff --git a/fpdfsdk/cpdfsdk_renderpage.h b/fpdfsdk/cpdfsdk_renderpage.h +index cb7a600b2..9b33a817c 100644 +--- a/fpdfsdk/cpdfsdk_renderpage.h ++++ b/fpdfsdk/cpdfsdk_renderpage.h +@@ -1,4 +1,4 @@ +-// Copyright 2020 PDFium Authors. All rights reserved. ++// Copyright 2020 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,8 @@ + #ifndef FPDFSDK_CPDFSDK_RENDERPAGE_H_ + #define FPDFSDK_CPDFSDK_RENDERPAGE_H_ + ++#include "public/fpdfview.h" ++ + class CFX_Matrix; + class CPDFSDK_PauseAdapter; + class CPDF_Page; +@@ -17,7 +19,8 @@ void CPDFSDK_RenderPage(CPDF_PageRenderContext* pContext, + CPDF_Page* pPage, + const CFX_Matrix& matrix, + const FX_RECT& clipping_rect, +- int flags); ++ int flags, ++ const FPDF_COLORSCHEME* color_scheme); + + // TODO(thestig): Consider giving this a better name, and make its parameters + // more similar to those of CPDFSDK_RenderPage(). +@@ -29,6 +32,7 @@ void CPDFSDK_RenderPageWithContext(CPDF_PageRenderContext* pContext, + int size_y, + int rotate, + int flags, ++ const FPDF_COLORSCHEME* color_scheme, + bool need_to_restore, + CPDFSDK_PauseAdapter* pause); + +diff --git a/fpdfsdk/cpdfsdk_widget.cpp b/fpdfsdk/cpdfsdk_widget.cpp +index 6d92b74b5..c336f5e94 100644 +--- a/fpdfsdk/cpdfsdk_widget.cpp ++++ b/fpdfsdk/cpdfsdk_widget.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,32 +6,34 @@ + + #include "fpdfsdk/cpdfsdk_widget.h" + +-#include +-#include +- ++#include "constants/access_permissions.h" + #include "constants/annotation_common.h" ++#include "constants/appearance.h" ++#include "constants/form_flags.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_string.h" +-#include "core/fpdfdoc/cba_fontmap.h" ++#include "core/fpdfdoc/cpdf_bafontmap.h" + #include "core/fpdfdoc/cpdf_defaultappearance.h" + #include "core/fpdfdoc/cpdf_formcontrol.h" + #include "core/fpdfdoc/cpdf_formfield.h" + #include "core/fpdfdoc/cpdf_iconfit.h" + #include "core/fpdfdoc/cpdf_interactiveform.h" ++#include "core/fxge/cfx_fillrenderoptions.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/cfx_renderdevice.h" +-#include "fpdfsdk/cpdfsdk_actionhandler.h" + #include "fpdfsdk/cpdfsdk_appstream.h" +-#include "fpdfsdk/cpdfsdk_fieldaction.h" + #include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fpdfsdk/cpdfsdk_interactiveform.h" + #include "fpdfsdk/cpdfsdk_pageview.h" ++#include "fpdfsdk/formfiller/cffl_fieldaction.h" + #include "fpdfsdk/pwl/cpwl_edit.h" ++#include "third_party/base/check.h" ++#include "third_party/base/notreached.h" + + #ifdef PDF_ENABLE_XFA + #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h" +@@ -48,12 +50,15 @@ CPDFSDK_Widget::CPDFSDK_Widget(CPDF_Annot* pAnnot, + : CPDFSDK_BAAnnot(pAnnot, pPageView), + m_pInteractiveForm(pInteractiveForm) {} + +-CPDFSDK_Widget::~CPDFSDK_Widget() = default; ++CPDFSDK_Widget::~CPDFSDK_Widget() { ++ GetInteractiveFormFiller()->OnDelete(this); ++ m_pInteractiveForm->RemoveMap(GetFormControl()); ++} + + #ifdef PDF_ENABLE_XFA + CXFA_FFWidget* CPDFSDK_Widget::GetMixXFAWidget() const { + CPDF_Document::Extension* pContext = +- m_pPageView->GetFormFillEnv()->GetDocExtension(); ++ GetPageView()->GetFormFillEnv()->GetDocExtension(); + if (!pContext || !pContext->ContainsExtensionForegroundForm()) + return nullptr; + +@@ -79,7 +84,7 @@ CXFA_FFWidget* CPDFSDK_Widget::GetMixXFAWidget() const { + + CXFA_FFWidget* CPDFSDK_Widget::GetGroupMixXFAWidget() const { + CPDF_Document::Extension* pContext = +- m_pPageView->GetFormFillEnv()->GetDocExtension(); ++ GetPageView()->GetFormFillEnv()->GetDocExtension(); + if (!pContext || !pContext->ContainsExtensionForegroundForm()) + return nullptr; + +@@ -94,17 +99,13 @@ CXFA_FFWidget* CPDFSDK_Widget::GetGroupMixXFAWidget() const { + + CXFA_FFWidgetHandler* CPDFSDK_Widget::GetXFAWidgetHandler() const { + CPDF_Document::Extension* pContext = +- m_pPageView->GetFormFillEnv()->GetDocExtension(); ++ GetPageView()->GetFormFillEnv()->GetDocExtension(); + if (!pContext || !pContext->ContainsExtensionForegroundForm()) + return nullptr; + +- if (!m_pWidgetHandler) { +- CXFA_FFDocView* pDocView = +- static_cast(pContext)->GetXFADocView(); +- if (pDocView) +- m_pWidgetHandler = pDocView->GetWidgetHandler(); +- } +- return m_pWidgetHandler.Get(); ++ CXFA_FFDocView* pDocView = ++ static_cast(pContext)->GetXFADocView(); ++ return pDocView ? pDocView->GetWidgetHandler() : nullptr; + } + + static XFA_EVENTTYPE GetXFAEventType(PDFSDK_XFAAActionType eXFAAAT) { +@@ -175,7 +176,7 @@ static XFA_EVENTTYPE GetXFAEventType(CPDF_AAction::AActionType eAAT, + break; + case CPDF_AAction::kDocumentOpen: + case CPDF_AAction::kNumberOfActions: +- NOTREACHED(); ++ NOTREACHED_NORETURN(); + break; + } + +@@ -183,7 +184,7 @@ static XFA_EVENTTYPE GetXFAEventType(CPDF_AAction::AActionType eAAT, + } + + bool CPDFSDK_Widget::HasXFAAAction(PDFSDK_XFAAActionType eXFAAAT) const { +- ObservedPtr pWidget(GetMixXFAWidget()); ++ CXFA_FFWidget* pWidget = GetMixXFAWidget(); + if (!pWidget) + return false; + +@@ -201,20 +202,18 @@ bool CPDFSDK_Widget::HasXFAAAction(PDFSDK_XFAAActionType eXFAAAT) const { + } + } + +- // Check |pWidget| again because JS may have destroyed it in the block above. +- return pWidget && +- pWidget->HasEventUnderHandler(eEventType, pXFAWidgetHandler); ++ return pWidget->HasEventUnderHandler(eEventType, pXFAWidgetHandler); + } + + bool CPDFSDK_Widget::OnXFAAAction(PDFSDK_XFAAActionType eXFAAAT, +- CPDFSDK_FieldAction* data, +- CPDFSDK_PageView* pPageView) { ++ CFFL_FieldAction* data, ++ const CPDFSDK_PageView* pPageView) { + auto* pContext = static_cast( +- m_pPageView->GetFormFillEnv()->GetDocExtension()); ++ GetPageView()->GetFormFillEnv()->GetDocExtension()); + if (!pContext) + return false; + +- ObservedPtr pWidget(GetMixXFAWidget()); ++ CXFA_FFWidget* pWidget = GetMixXFAWidget(); + if (!pWidget) + return false; + +@@ -246,12 +245,9 @@ bool CPDFSDK_Widget::OnXFAAAction(PDFSDK_XFAAActionType eXFAAAT, + } + } + +- // Check |pWidget| again because JS may have destroyed it in the block above. +- if (!pWidget) +- return false; +- + bool ret = pWidget->ProcessEventUnderHandler(¶m, pXFAWidgetHandler); +- if (CXFA_FFDocView* pDocView = pContext->GetXFADocView()) ++ CXFA_FFDocView* pDocView = pContext->GetXFADocView(); ++ if (pDocView) + pDocView->UpdateDocView(); + + return ret; +@@ -271,25 +267,26 @@ void CPDFSDK_Widget::Synchronize(bool bSynchronizeElse) { + case FormFieldType::kCheckBox: + case FormFieldType::kRadioButton: { + CPDF_FormControl* pFormCtrl = GetFormControl(); +- XFA_CHECKSTATE eCheckState = +- pFormCtrl->IsChecked() ? XFA_CHECKSTATE_On : XFA_CHECKSTATE_Off; +- node->SetCheckState(eCheckState, true); ++ XFA_CheckState eCheckState = ++ pFormCtrl->IsChecked() ? XFA_CheckState::kOn : XFA_CheckState::kOff; ++ node->SetCheckState(eCheckState); + break; + } + case FormFieldType::kTextField: +- node->SetValue(XFA_VALUEPICTURE_Edit, pFormField->GetValue()); ++ node->SetValue(XFA_ValuePicture::kEdit, pFormField->GetValue()); + break; + case FormFieldType::kComboBox: + case FormFieldType::kListBox: { + node->ClearAllSelections(); +- + for (int i = 0; i < pFormField->CountSelectedItems(); ++i) { + int nIndex = pFormField->GetSelectedIndex(i); +- if (nIndex > -1 && nIndex < node->CountChoiceListItems(false)) +- node->SetItemState(nIndex, true, false, false, true); ++ if (nIndex > -1 && ++ static_cast(nIndex) < node->CountChoiceListItems(false)) { ++ node->SetItemState(nIndex, true, false, false); ++ } + } + if (GetFieldType() == FormFieldType::kComboBox) +- node->SetValue(XFA_VALUEPICTURE_Edit, pFormField->GetValue()); ++ node->SetValue(XFA_ValuePicture::kEdit, pFormField->GetValue()); + break; + } + default: +@@ -298,29 +295,70 @@ void CPDFSDK_Widget::Synchronize(bool bSynchronizeElse) { + + if (bSynchronizeElse) { + auto* context = static_cast( +- m_pPageView->GetFormFillEnv()->GetDocExtension()); ++ GetPageView()->GetFormFillEnv()->GetDocExtension()); + context->GetXFADocView()->ProcessValueChanged(node); + } + } ++ ++bool CPDFSDK_Widget::HandleXFAAAction( ++ CPDF_AAction::AActionType type, ++ CFFL_FieldAction* data, ++ CPDFSDK_FormFillEnvironment* pFormFillEnv) { ++ auto* pContext = ++ static_cast(pFormFillEnv->GetDocExtension()); ++ if (!pContext) ++ return false; ++ ++ CXFA_FFWidget* hWidget = GetMixXFAWidget(); ++ if (!hWidget) ++ return false; ++ ++ XFA_EVENTTYPE eEventType = GetXFAEventType(type, data->bWillCommit); ++ if (eEventType == XFA_EVENT_Unknown) ++ return false; ++ ++ CXFA_FFWidgetHandler* pXFAWidgetHandler = GetXFAWidgetHandler(); ++ if (!pXFAWidgetHandler) ++ return false; ++ ++ CXFA_EventParam param; ++ param.m_eType = eEventType; ++ param.m_wsChange = data->sChange; ++ param.m_iCommitKey = 0; ++ param.m_bShift = data->bShift; ++ param.m_iSelStart = data->nSelStart; ++ param.m_iSelEnd = data->nSelEnd; ++ param.m_wsFullText = data->sValue; ++ param.m_bKeyDown = data->bKeyDown; ++ param.m_bModifier = data->bModifier; ++ param.m_wsPrevText = data->sValue; ++ bool ret = hWidget->ProcessEventUnderHandler(¶m, pXFAWidgetHandler); ++ CXFA_FFDocView* pDocView = pContext->GetXFADocView(); ++ if (pDocView) ++ pDocView->UpdateDocView(); ++ ++ return ret; ++} + #endif // PDF_ENABLE_XFA + +-bool CPDFSDK_Widget::IsWidgetAppearanceValid(CPDF_Annot::AppearanceMode mode) { +- const CPDF_Dictionary* pAP = ++bool CPDFSDK_Widget::IsWidgetAppearanceValid( ++ CPDF_Annot::AppearanceMode mode) const { ++ RetainPtr pAP = + GetAnnotDict()->GetDictFor(pdfium::annotation::kAP); + if (!pAP) + return false; + + // Choose the right sub-ap + const char* ap_entry = "N"; +- if (mode == CPDF_Annot::Down) ++ if (mode == CPDF_Annot::AppearanceMode::kDown) + ap_entry = "D"; +- else if (mode == CPDF_Annot::Rollover) ++ else if (mode == CPDF_Annot::AppearanceMode::kRollover) + ap_entry = "R"; + if (!pAP->KeyExist(ap_entry)) + ap_entry = "N"; + + // Get the AP stream or subdirectory +- const CPDF_Object* pSub = pAP->GetDirectObjectFor(ap_entry); ++ RetainPtr pSub = pAP->GetDirectObjectFor(ap_entry); + if (!pSub) + return false; + +@@ -343,15 +381,25 @@ bool CPDFSDK_Widget::IsWidgetAppearanceValid(CPDF_Annot::AppearanceMode mode) { + } + } + ++bool CPDFSDK_Widget::IsPushHighlighted() const { ++ return GetFormControl()->GetHighlightingMode() == CPDF_FormControl::kPush; ++} ++ + FormFieldType CPDFSDK_Widget::GetFieldType() const { + CPDF_FormField* pField = GetFormField(); + return pField ? pField->GetFieldType() : FormFieldType::kUnknown; + } + ++void CPDFSDK_Widget::SetRect(const CFX_FloatRect& rect) { ++ DCHECK(rect.right - rect.left >= 1.0f); ++ DCHECK(rect.top - rect.bottom >= 1.0f); ++ GetMutableAnnotDict()->SetRectFor(pdfium::annotation::kRect, rect); ++} ++ + bool CPDFSDK_Widget::IsAppearanceValid() { + #ifdef PDF_ENABLE_XFA + CPDF_Document::Extension* pContext = +- m_pPageView->GetFormFillEnv()->GetDocExtension(); ++ GetPageView()->GetFormFillEnv()->GetDocExtension(); + if (pContext && pContext->ContainsExtensionFullForm()) + return true; + #endif // PDF_ENABLE_XFA +@@ -392,37 +440,37 @@ WideString CPDFSDK_Widget::GetName() const { + } + #endif // PDF_ENABLE_XFA + +-Optional CPDFSDK_Widget::GetFillColor() const { +- CPDF_FormControl* pFormCtrl = GetFormControl(); +- int iColorType = 0; +- FX_COLORREF color = ArgbToColorRef(pFormCtrl->GetBackgroundColor(iColorType)); +- if (iColorType == CFX_Color::kTransparent) +- return {}; +- return color; ++absl::optional CPDFSDK_Widget::GetFillColor() const { ++ CFX_Color::TypeAndARGB type_argb_pair = ++ GetFormControl()->GetColorARGB(pdfium::appearance::kBG); ++ ++ if (type_argb_pair.color_type == CFX_Color::Type::kTransparent) ++ return absl::nullopt; ++ ++ return ArgbToColorRef(type_argb_pair.argb); + } + +-Optional CPDFSDK_Widget::GetBorderColor() const { +- CPDF_FormControl* pFormCtrl = GetFormControl(); +- int iColorType = 0; +- FX_COLORREF color = ArgbToColorRef(pFormCtrl->GetBorderColor(iColorType)); +- if (iColorType == CFX_Color::kTransparent) +- return {}; +- return color; ++absl::optional CPDFSDK_Widget::GetBorderColor() const { ++ CFX_Color::TypeAndARGB type_argb_pair = ++ GetFormControl()->GetColorARGB(pdfium::appearance::kBC); ++ if (type_argb_pair.color_type == CFX_Color::Type::kTransparent) ++ return absl::nullopt; ++ ++ return ArgbToColorRef(type_argb_pair.argb); + } + +-Optional CPDFSDK_Widget::GetTextColor() const { +- CPDF_FormControl* pFormCtrl = GetFormControl(); +- CPDF_DefaultAppearance da = pFormCtrl->GetDefaultAppearance(); +- FX_ARGB argb; +- Optional iColorType; +- std::tie(iColorType, argb) = da.GetColor(); +- if (!iColorType.has_value()) +- return {}; ++absl::optional CPDFSDK_Widget::GetTextColor() const { ++ CPDF_DefaultAppearance da = GetFormControl()->GetDefaultAppearance(); ++ absl::optional maybe_type_argb_pair = ++ da.GetColorARGB(); + +- FX_COLORREF color = ArgbToColorRef(argb); +- if (iColorType.value() == CFX_Color::kTransparent) +- return {}; +- return color; ++ if (!maybe_type_argb_pair.has_value()) ++ return absl::nullopt; ++ ++ if (maybe_type_argb_pair.value().color_type == CFX_Color::Type::kTransparent) ++ return absl::nullopt; ++ ++ return ArgbToColorRef(maybe_type_argb_pair.value().argb); + } + + float CPDFSDK_Widget::GetFontSize() const { +@@ -452,16 +500,16 @@ WideString CPDFSDK_Widget::GetValue() const { + if (CXFA_FFWidget* hWidget = GetMixXFAWidget()) { + CXFA_Node* node = hWidget->GetNode(); + if (node->IsWidgetReady()) +- return node->GetValue(XFA_VALUEPICTURE_Display); ++ return node->GetValue(XFA_ValuePicture::kDisplay); + } + #endif // PDF_ENABLE_XFA + CPDF_FormField* pFormField = GetFormField(); + return pFormField->GetValue(); + } + +-WideString CPDFSDK_Widget::GetDefaultValue() const { +- CPDF_FormField* pFormField = GetFormField(); +- return pFormField->GetDefaultValue(); ++WideString CPDFSDK_Widget::GetExportValue() const { ++ CPDF_FormControl* pFormCtrl = GetFormControl(); ++ return pFormCtrl->GetExportValue(); + } + + WideString CPDFSDK_Widget::GetOptionLabel(int nIndex) const { +@@ -469,6 +517,21 @@ WideString CPDFSDK_Widget::GetOptionLabel(int nIndex) const { + return pFormField->GetOptionLabel(nIndex); + } + ++WideString CPDFSDK_Widget::GetSelectExportText(int nIndex) const { ++ if (nIndex < 0) ++ return WideString(); ++ ++ CPDF_FormField* pFormField = GetFormField(); ++ if (!pFormField) ++ return WideString(); ++ ++ WideString swRet = pFormField->GetOptionValue(nIndex); ++ if (!swRet.IsEmpty()) ++ return swRet; ++ ++ return pFormField->GetOptionLabel(nIndex); ++} ++ + int CPDFSDK_Widget::CountOptions() const { + CPDF_FormField* pFormField = GetFormField(); + return pFormField->CountOptions(); +@@ -479,9 +542,10 @@ bool CPDFSDK_Widget::IsOptionSelected(int nIndex) const { + if (CXFA_FFWidget* hWidget = GetMixXFAWidget()) { + CXFA_Node* node = hWidget->GetNode(); + if (node->IsWidgetReady()) { +- if (nIndex > -1 && nIndex < node->CountChoiceListItems(false)) ++ if (nIndex > -1 && ++ static_cast(nIndex) < node->CountChoiceListItems(false)) { + return node->GetItemState(nIndex); +- ++ } + return false; + } + } +@@ -500,7 +564,7 @@ bool CPDFSDK_Widget::IsChecked() const { + if (CXFA_FFWidget* hWidget = GetMixXFAWidget()) { + CXFA_Node* node = hWidget->GetNode(); + if (node->IsWidgetReady()) +- return node->GetCheckState() == XFA_CHECKSTATE_On; ++ return node->GetCheckState() == XFA_CheckState::kOn; + } + #endif // PDF_ENABLE_XFA + CPDF_FormControl* pFormCtrl = GetFormControl(); +@@ -517,46 +581,39 @@ int CPDFSDK_Widget::GetMaxLen() const { + return pFormField->GetMaxLen(); + } + +-void CPDFSDK_Widget::SetCheck(bool bChecked, NotificationOption notify) { ++void CPDFSDK_Widget::SetCheck(bool bChecked) { + CPDF_FormControl* pFormCtrl = GetFormControl(); + CPDF_FormField* pFormField = pFormCtrl->GetField(); + pFormField->CheckControl(pFormField->GetControlIndex(pFormCtrl), bChecked, +- notify); ++ NotificationOption::kDoNotNotify); + #ifdef PDF_ENABLE_XFA +- if (!IsWidgetAppearanceValid(CPDF_Annot::Normal)) +- ResetXFAAppearance(true); +- if (notify == NotificationOption::kDoNotNotify) +- Synchronize(true); ++ if (!IsWidgetAppearanceValid(CPDF_Annot::AppearanceMode::kNormal)) ++ ResetXFAAppearance(CPDFSDK_Widget::kValueChanged); ++ Synchronize(true); + #endif // PDF_ENABLE_XFA + } + +-void CPDFSDK_Widget::SetValue(const WideString& sValue, +- NotificationOption notify) { ++void CPDFSDK_Widget::SetValue(const WideString& sValue) { + CPDF_FormField* pFormField = GetFormField(); +- pFormField->SetValue(sValue, notify); ++ pFormField->SetValue(sValue, NotificationOption::kDoNotNotify); + #ifdef PDF_ENABLE_XFA +- if (notify == NotificationOption::kDoNotNotify) +- Synchronize(true); ++ Synchronize(true); + #endif // PDF_ENABLE_XFA + } + +-void CPDFSDK_Widget::SetOptionSelection(int index, +- bool bSelected, +- NotificationOption notify) { ++void CPDFSDK_Widget::SetOptionSelection(int index) { + CPDF_FormField* pFormField = GetFormField(); +- pFormField->SetItemSelection(index, bSelected, notify); ++ pFormField->SetItemSelection(index, NotificationOption::kDoNotNotify); + #ifdef PDF_ENABLE_XFA +- if (notify == NotificationOption::kDoNotNotify) +- Synchronize(true); ++ Synchronize(true); + #endif // PDF_ENABLE_XFA + } + +-void CPDFSDK_Widget::ClearSelection(NotificationOption notify) { ++void CPDFSDK_Widget::ClearSelection() { + CPDF_FormField* pFormField = GetFormField(); +- pFormField->ClearSelection(notify); ++ pFormField->ClearSelection(NotificationOption::kDoNotNotify); + #ifdef PDF_ENABLE_XFA +- if (notify == NotificationOption::kDoNotNotify) +- Synchronize(true); ++ Synchronize(true); + #endif // PDF_ENABLE_XFA + } + +@@ -575,29 +632,29 @@ bool CPDFSDK_Widget::IsAppModified() const { + } + + #ifdef PDF_ENABLE_XFA +-void CPDFSDK_Widget::ResetXFAAppearance(bool bValueChanged) { ++void CPDFSDK_Widget::ResetXFAAppearance(ValueChanged bValueChanged) { + switch (GetFieldType()) { + case FormFieldType::kTextField: + case FormFieldType::kComboBox: { +- ResetAppearance(OnFormat(), true); ++ ResetAppearance(OnFormat(), kValueChanged); + break; + } + default: +- ResetAppearance(pdfium::nullopt, false); ++ ResetAppearance(absl::nullopt, kValueUnchanged); + break; + } + } + #endif // PDF_ENABLE_XFA + +-void CPDFSDK_Widget::ResetAppearance(Optional sValue, +- bool bValueChanged) { ++void CPDFSDK_Widget::ResetAppearance(absl::optional sValue, ++ ValueChanged bValueChanged) { + SetAppModified(); + + m_nAppearanceAge++; +- if (bValueChanged) ++ if (bValueChanged == kValueChanged) + m_nValueAge++; + +- CPDFSDK_AppStream appStream(this, GetAPDict()); ++ CPDFSDK_AppStream appStream(this, GetAPDict().Get()); + switch (GetFieldType()) { + case FormFieldType::kPushButton: + appStream.SetAsPushButton(); +@@ -621,46 +678,260 @@ void CPDFSDK_Widget::ResetAppearance(Optional sValue, + break; + } + +- m_pAnnot->ClearCachedAP(); ++ ClearCachedAnnotAP(); + } + +-Optional CPDFSDK_Widget::OnFormat() { ++absl::optional CPDFSDK_Widget::OnFormat() { + CPDF_FormField* pFormField = GetFormField(); +- ASSERT(pFormField); ++ DCHECK(pFormField); + return m_pInteractiveForm->OnFormat(pFormField); + } + + void CPDFSDK_Widget::ResetFieldAppearance() { + CPDF_FormField* pFormField = GetFormField(); +- ASSERT(pFormField); +- m_pInteractiveForm->ResetFieldAppearance(pFormField, pdfium::nullopt); ++ DCHECK(pFormField); ++ m_pInteractiveForm->ResetFieldAppearance(pFormField, absl::nullopt); ++} ++ ++void CPDFSDK_Widget::OnDraw(CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device, ++ bool bDrawAnnots) { ++ if (IsSignatureWidget()) { ++ DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::AppearanceMode::kNormal); ++ return; ++ } ++ ++ GetInteractiveFormFiller()->OnDraw(GetPageView(), this, pDevice, ++ mtUser2Device); ++} ++ ++bool CPDFSDK_Widget::DoHitTest(const CFX_PointF& point) { ++ if (IsSignatureWidget() || !IsVisible()) ++ return false; ++ ++ if (GetFieldFlags() & pdfium::form_flags::kReadOnly) ++ return false; ++ ++ bool do_hit_test = GetFieldType() == FormFieldType::kPushButton; ++ if (!do_hit_test) { ++ uint32_t perms = GetPDFPage()->GetDocument()->GetUserPermissions(); ++ do_hit_test = (perms & pdfium::access_permissions::kFillForm) || ++ (perms & pdfium::access_permissions::kModifyAnnotation); ++ } ++ return do_hit_test && GetViewBBox().Contains(point); ++} ++ ++CFX_FloatRect CPDFSDK_Widget::GetViewBBox() { ++ if (IsSignatureWidget()) ++ return CFX_FloatRect(); ++ ++ auto* form_filler = GetInteractiveFormFiller(); ++ return CFX_FloatRect(form_filler->GetViewBBox(GetPageView(), this)); ++} ++ ++void CPDFSDK_Widget::OnMouseEnter(Mask nFlags) { ++ if (IsSignatureWidget()) ++ return; ++ ++ ObservedPtr observer(this); ++ GetInteractiveFormFiller()->OnMouseEnter(GetPageView(), observer, nFlags); ++} ++ ++void CPDFSDK_Widget::OnMouseExit(Mask nFlags) { ++ if (IsSignatureWidget()) ++ return; ++ ++ ObservedPtr observer(this); ++ GetInteractiveFormFiller()->OnMouseExit(GetPageView(), observer, nFlags); ++} ++ ++bool CPDFSDK_Widget::OnLButtonDown(Mask nFlags, ++ const CFX_PointF& point) { ++ if (IsSignatureWidget()) ++ return false; ++ ++ ObservedPtr observer(this); ++ return GetInteractiveFormFiller()->OnLButtonDown(GetPageView(), observer, ++ nFlags, point); ++} ++ ++bool CPDFSDK_Widget::OnLButtonUp(Mask nFlags, ++ const CFX_PointF& point) { ++ if (IsSignatureWidget()) ++ return false; ++ ++ ObservedPtr observer(this); ++ return GetInteractiveFormFiller()->OnLButtonUp(GetPageView(), observer, ++ nFlags, point); ++} ++ ++bool CPDFSDK_Widget::OnLButtonDblClk(Mask nFlags, ++ const CFX_PointF& point) { ++ if (IsSignatureWidget()) ++ return false; ++ ++ ObservedPtr observer(this); ++ return GetInteractiveFormFiller()->OnLButtonDblClk(GetPageView(), observer, ++ nFlags, point); ++} ++ ++bool CPDFSDK_Widget::OnMouseMove(Mask nFlags, ++ const CFX_PointF& point) { ++ if (IsSignatureWidget()) ++ return false; ++ ++ ObservedPtr observer(this); ++ return GetInteractiveFormFiller()->OnMouseMove(GetPageView(), observer, ++ nFlags, point); ++} ++ ++bool CPDFSDK_Widget::OnMouseWheel(Mask nFlags, ++ const CFX_PointF& point, ++ const CFX_Vector& delta) { ++ if (IsSignatureWidget()) ++ return false; ++ ++ ObservedPtr observer(this); ++ return GetInteractiveFormFiller()->OnMouseWheel(GetPageView(), observer, ++ nFlags, point, delta); ++} ++ ++bool CPDFSDK_Widget::OnRButtonDown(Mask nFlags, ++ const CFX_PointF& point) { ++ if (IsSignatureWidget()) ++ return false; ++ ++ ObservedPtr observer(this); ++ return GetInteractiveFormFiller()->OnRButtonDown(GetPageView(), observer, ++ nFlags, point); ++} ++ ++bool CPDFSDK_Widget::OnRButtonUp(Mask nFlags, ++ const CFX_PointF& point) { ++ if (IsSignatureWidget()) ++ return false; ++ ++ ObservedPtr observer(this); ++ return GetInteractiveFormFiller()->OnRButtonUp(GetPageView(), observer, ++ nFlags, point); ++} ++ ++bool CPDFSDK_Widget::OnChar(uint32_t nChar, Mask nFlags) { ++ return !IsSignatureWidget() && ++ GetInteractiveFormFiller()->OnChar(this, nChar, nFlags); ++} ++ ++bool CPDFSDK_Widget::OnKeyDown(FWL_VKEYCODE nKeyCode, ++ Mask nFlags) { ++ return !IsSignatureWidget() && ++ GetInteractiveFormFiller()->OnKeyDown(this, nKeyCode, nFlags); ++} ++ ++bool CPDFSDK_Widget::OnSetFocus(Mask nFlags) { ++ if (!IsFocusableAnnot(GetPDFAnnot()->GetSubtype())) ++ return false; ++ ++ if (IsSignatureWidget()) ++ return true; ++ ++ ObservedPtr observer(this); ++ return GetInteractiveFormFiller()->OnSetFocus(observer, nFlags); ++} ++ ++bool CPDFSDK_Widget::OnKillFocus(Mask nFlags) { ++ if (!IsFocusableAnnot(GetPDFAnnot()->GetSubtype())) ++ return false; ++ ++ if (IsSignatureWidget()) ++ return true; ++ ++ ObservedPtr observer(this); ++ return GetInteractiveFormFiller()->OnKillFocus(observer, nFlags); ++} ++ ++bool CPDFSDK_Widget::CanUndo() { ++ return !IsSignatureWidget() && GetInteractiveFormFiller()->CanUndo(this); ++} ++ ++bool CPDFSDK_Widget::CanRedo() { ++ return !IsSignatureWidget() && GetInteractiveFormFiller()->CanRedo(this); ++} ++ ++bool CPDFSDK_Widget::Undo() { ++ return !IsSignatureWidget() && GetInteractiveFormFiller()->Undo(this); ++} ++ ++bool CPDFSDK_Widget::Redo() { ++ return !IsSignatureWidget() && GetInteractiveFormFiller()->Redo(this); ++} ++ ++WideString CPDFSDK_Widget::GetText() { ++ if (IsSignatureWidget()) ++ return WideString(); ++ return GetInteractiveFormFiller()->GetText(this); ++} ++ ++WideString CPDFSDK_Widget::GetSelectedText() { ++ if (IsSignatureWidget()) ++ return WideString(); ++ return GetInteractiveFormFiller()->GetSelectedText(this); ++} ++ ++void CPDFSDK_Widget::ReplaceAndKeepSelection(const WideString& text) { ++ if (IsSignatureWidget()) ++ return; ++ ++ GetInteractiveFormFiller()->ReplaceAndKeepSelection(this, text); ++} ++ ++void CPDFSDK_Widget::ReplaceSelection(const WideString& text) { ++ if (IsSignatureWidget()) ++ return; ++ ++ GetInteractiveFormFiller()->ReplaceSelection(this, text); ++} ++ ++bool CPDFSDK_Widget::SelectAllText() { ++ return !IsSignatureWidget() && ++ GetInteractiveFormFiller()->SelectAllText(this); ++} ++ ++bool CPDFSDK_Widget::SetIndexSelected(int index, bool selected) { ++ ObservedPtr observer(this); ++ return !IsSignatureWidget() && GetInteractiveFormFiller()->SetIndexSelected( ++ observer, index, selected); ++} ++ ++bool CPDFSDK_Widget::IsIndexSelected(int index) { ++ ObservedPtr observer(this); ++ return !IsSignatureWidget() && ++ GetInteractiveFormFiller()->IsIndexSelected(observer, index); + } + + void CPDFSDK_Widget::DrawAppearance(CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device, +- CPDF_Annot::AppearanceMode mode, +- const CPDF_RenderOptions* pOptions) { ++ CPDF_Annot::AppearanceMode mode) { + FormFieldType fieldType = GetFieldType(); + + if ((fieldType == FormFieldType::kCheckBox || + fieldType == FormFieldType::kRadioButton) && +- mode == CPDF_Annot::Normal && +- !IsWidgetAppearanceValid(CPDF_Annot::Normal)) { ++ mode == CPDF_Annot::AppearanceMode::kNormal && ++ !IsWidgetAppearanceValid(CPDF_Annot::AppearanceMode::kNormal)) { + CFX_GraphStateData gsd; + gsd.m_LineWidth = 0.0f; + +- CFX_PathData pathData; +- pathData.AppendFloatRect(GetRect()); +- pDevice->DrawPath(&pathData, &mtUser2Device, &gsd, 0, 0xFFAAAAAA, +- FXFILL_ALTERNATE); ++ CFX_Path path; ++ path.AppendFloatRect(GetRect()); ++ pDevice->DrawPath(path, &mtUser2Device, &gsd, 0, 0xFFAAAAAA, ++ CFX_FillRenderOptions::EvenOddOptions()); + } else { +- CPDFSDK_BAAnnot::DrawAppearance(pDevice, mtUser2Device, mode, pOptions); ++ CPDFSDK_BAAnnot::DrawAppearance(pDevice, mtUser2Device, mode); + } + } + + void CPDFSDK_Widget::UpdateField() { + CPDF_FormField* pFormField = GetFormField(); +- ASSERT(pFormField); ++ DCHECK(pFormField); + m_pInteractiveForm->UpdateField(pFormField); + } + +@@ -693,8 +964,8 @@ CFX_FloatRect CPDFSDK_Widget::GetClientRect() const { + CFX_FloatRect rcWindow = GetRotatedRect(); + float fBorderWidth = GetBorderWidth(); + switch (GetBorderStyle()) { +- case BorderStyle::BEVELED: +- case BorderStyle::INSET: ++ case BorderStyle::kBeveled: ++ case BorderStyle::kInset: + fBorderWidth *= 2.0f; + break; + default: +@@ -751,90 +1022,67 @@ CFX_Matrix CPDFSDK_Widget::GetMatrix() const { + } + + CFX_Color CPDFSDK_Widget::GetTextPWLColor() const { +- CFX_Color crText = CFX_Color(CFX_Color::kGray, 0); +- + CPDF_FormControl* pFormCtrl = GetFormControl(); +- CPDF_DefaultAppearance da = pFormCtrl->GetDefaultAppearance(); +- +- float fc[4]; +- Optional iColorType = da.GetColor(fc); +- if (iColorType) +- crText = CFX_Color(*iColorType, fc[0], fc[1], fc[2], fc[3]); +- +- return crText; ++ absl::optional crText = ++ pFormCtrl->GetDefaultAppearance().GetColor(); ++ return crText.value_or(CFX_Color(CFX_Color::Type::kGray, 0)); + } + + CFX_Color CPDFSDK_Widget::GetBorderPWLColor() const { +- CFX_Color crBorder; +- + CPDF_FormControl* pFormCtrl = GetFormControl(); +- int32_t iColorType; +- float fc[4]; +- pFormCtrl->GetOriginalBorderColor(iColorType, fc); +- if (iColorType > 0) +- crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); +- +- return crBorder; ++ return pFormCtrl->GetOriginalBorderColor(); + } + + CFX_Color CPDFSDK_Widget::GetFillPWLColor() const { +- CFX_Color crFill; +- + CPDF_FormControl* pFormCtrl = GetFormControl(); +- int32_t iColorType; +- float fc[4]; +- pFormCtrl->GetOriginalBackgroundColor(iColorType, fc); +- if (iColorType > 0) +- crFill = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); +- +- return crFill; ++ return pFormCtrl->GetOriginalBackgroundColor(); + } + + bool CPDFSDK_Widget::OnAAction(CPDF_AAction::AActionType type, +- CPDFSDK_FieldAction* data, +- CPDFSDK_PageView* pPageView) { ++ CFFL_FieldAction* data, ++ const CPDFSDK_PageView* pPageView) { + CPDFSDK_FormFillEnvironment* pFormFillEnv = pPageView->GetFormFillEnv(); + + #ifdef PDF_ENABLE_XFA +- auto* pContext = +- static_cast(pFormFillEnv->GetDocExtension()); +- if (pContext) { +- CXFA_FFWidget* hWidget = GetMixXFAWidget(); +- if (hWidget) { +- XFA_EVENTTYPE eEventType = GetXFAEventType(type, data->bWillCommit); +- if (eEventType != XFA_EVENT_Unknown) { +- if (CXFA_FFWidgetHandler* pXFAWidgetHandler = GetXFAWidgetHandler()) { +- CXFA_EventParam param; +- param.m_eType = eEventType; +- param.m_wsChange = data->sChange; +- param.m_iCommitKey = 0; +- param.m_bShift = data->bShift; +- param.m_iSelStart = data->nSelStart; +- param.m_iSelEnd = data->nSelEnd; +- param.m_wsFullText = data->sValue; +- param.m_bKeyDown = data->bKeyDown; +- param.m_bModifier = data->bModifier; +- param.m_wsPrevText = data->sValue; +- bool ret = +- hWidget->ProcessEventUnderHandler(¶m, pXFAWidgetHandler); +- if (CXFA_FFDocView* pDocView = pContext->GetXFADocView()) +- pDocView->UpdateDocView(); +- if (ret) +- return true; +- } +- } +- } +- } ++ if (HandleXFAAAction(type, data, pFormFillEnv)) ++ return true; + #endif // PDF_ENABLE_XFA + + CPDF_Action action = GetAAction(type); +- if (action.GetType() != CPDF_Action::Unknown) { +- pFormFillEnv->GetActionHandler()->DoAction_Field(action, type, pFormFillEnv, +- GetFormField(), data); ++ if (action.GetType() != CPDF_Action::Type::kUnknown) { ++ pFormFillEnv->DoActionField(action, type, GetFormField(), data); + } + return false; + } + ++void CPDFSDK_Widget::OnLoad() { ++ if (IsSignatureWidget()) ++ return; ++ ++ if (!IsAppearanceValid()) ++ ResetAppearance(absl::nullopt, CPDFSDK_Widget::kValueUnchanged); ++ ++ FormFieldType field_type = GetFieldType(); ++ if (field_type == FormFieldType::kTextField || ++ field_type == FormFieldType::kComboBox) { ++ ObservedPtr pObserved(this); ++ absl::optional sValue = OnFormat(); ++ if (!pObserved) ++ return; ++ ++ if (sValue.has_value() && field_type == FormFieldType::kComboBox) ++ ResetAppearance(sValue, CPDFSDK_Widget::kValueUnchanged); ++ } ++ ++#ifdef PDF_ENABLE_XFA ++ auto* pContext = GetPageView()->GetFormFillEnv()->GetDocExtension(); ++ if (pContext && pContext->ContainsExtensionForegroundForm()) { ++ if (!IsAppearanceValid() && !GetValue().IsEmpty()) ++ ResetXFAAppearance(CPDFSDK_Widget::kValueUnchanged); ++ } ++#endif // PDF_ENABLE_XFA ++} ++ + CPDF_Action CPDFSDK_Widget::GetAAction(CPDF_AAction::AActionType eAAT) { + switch (eAAT) { + case CPDF_AAction::kCursorEnter: +@@ -854,7 +1102,7 @@ CPDF_Action CPDFSDK_Widget::GetAAction(CPDF_AAction::AActionType eAAT) { + case CPDF_AAction::kValidate: + case CPDF_AAction::kCalculate: { + CPDF_FormField* pField = GetFormField(); +- if (pField->GetAdditionalAction().GetDict()) ++ if (pField->GetAdditionalAction().HasDict()) + return pField->GetAdditionalAction().GetAction(eAAT); + return CPDFSDK_BAAnnot::GetAAction(eAAT); + } +@@ -865,7 +1113,6 @@ CPDF_Action CPDFSDK_Widget::GetAAction(CPDF_AAction::AActionType eAAT) { + return CPDF_Action(nullptr); + } + +-WideString CPDFSDK_Widget::GetAlternateName() const { +- CPDF_FormField* pFormField = GetFormField(); +- return pFormField->GetAlternateName(); ++CFFL_InteractiveFormFiller* CPDFSDK_Widget::GetInteractiveFormFiller() { ++ return GetPageView()->GetFormFillEnv()->GetInteractiveFormFiller(); + } +diff --git a/fpdfsdk/cpdfsdk_widget.h b/fpdfsdk/cpdfsdk_widget.h +index 3037a1678..ce68cfbbc 100644 +--- a/fpdfsdk/cpdfsdk_widget.h ++++ b/fpdfsdk/cpdfsdk_widget.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,22 +12,21 @@ + #include "core/fpdfdoc/cpdf_annot.h" + #include "core/fpdfdoc/cpdf_formfield.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "core/fxcrt/widestring.h" + #include "core/fxge/cfx_color.h" + #include "fpdfsdk/cpdfsdk_baannot.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + ++class CFFL_InteractiveFormFiller; + class CFX_RenderDevice; + class CPDF_Annot; +-class CPDF_Dictionary; + class CPDF_FormControl; + class CPDF_FormField; +-class CPDF_RenderOptions; +-class CPDF_Stream; ++class CPDFSDK_FormFillEnvironment; + class CPDFSDK_InteractiveForm; + class CPDFSDK_PageView; +-struct CPDFSDK_FieldAction; ++struct CFFL_FieldAction; + + #ifdef PDF_ENABLE_XFA + class CXFA_FFWidget; +@@ -43,72 +42,89 @@ enum PDFSDK_XFAAActionType { + + class CPDFSDK_Widget final : public CPDFSDK_BAAnnot { + public: +-#ifdef PDF_ENABLE_XFA +- CXFA_FFWidget* GetMixXFAWidget() const; +- CXFA_FFWidgetHandler* GetXFAWidgetHandler() const; +- +- bool HasXFAAAction(PDFSDK_XFAAActionType eXFAAAT) const; +- bool OnXFAAAction(PDFSDK_XFAAActionType eXFAAAT, +- CPDFSDK_FieldAction* data, +- CPDFSDK_PageView* pPageView); +- +- void Synchronize(bool bSynchronizeElse); +-#endif // PDF_ENABLE_XFA ++ enum ValueChanged : bool { kValueUnchanged = false, kValueChanged = true }; + + CPDFSDK_Widget(CPDF_Annot* pAnnot, + CPDFSDK_PageView* pPageView, + CPDFSDK_InteractiveForm* pInteractiveForm); + ~CPDFSDK_Widget() override; + +- bool IsSignatureWidget() const override; ++ // CPDFSDK_BAAnnot: ++ void OnLoad() override; + CPDF_Action GetAAction(CPDF_AAction::AActionType eAAT) override; + bool IsAppearanceValid() override; +- + int GetLayoutOrder() const override; ++ void OnDraw(CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device, ++ bool bDrawAnnots) override; ++ bool DoHitTest(const CFX_PointF& point) override; ++ CFX_FloatRect GetViewBBox() override; ++ bool CanUndo() override; ++ bool CanRedo() override; ++ bool Undo() override; ++ bool Redo() override; ++ WideString GetText() override; ++ WideString GetSelectedText() override; ++ void ReplaceAndKeepSelection(const WideString& text) override; ++ void ReplaceSelection(const WideString& text) override; ++ bool SelectAllText() override; ++ bool SetIndexSelected(int index, bool selected) override; ++ bool IsIndexSelected(int index) override; ++ void DrawAppearance(CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device, ++ CPDF_Annot::AppearanceMode mode) override; + ++ bool IsSignatureWidget() const; ++ void SetRect(const CFX_FloatRect& rect); + FormFieldType GetFieldType() const; + int GetFieldFlags() const; + int GetRotate() const; + +- Optional GetFillColor() const; +- Optional GetBorderColor() const; +- Optional GetTextColor() const; ++ absl::optional GetFillColor() const; ++ absl::optional GetBorderColor() const; ++ absl::optional GetTextColor() const; + float GetFontSize() const; + + int GetSelectedIndex(int nIndex) const; + WideString GetValue() const; +- WideString GetDefaultValue() const; ++ WideString GetExportValue() const; + WideString GetOptionLabel(int nIndex) const; ++ WideString GetSelectExportText(int nIndex) const; ++ + int CountOptions() const; + bool IsOptionSelected(int nIndex) const; + int GetTopVisibleIndex() const; + bool IsChecked() const; + int GetAlignment() const; + int GetMaxLen() const; +- WideString GetAlternateName() const; + +- void SetCheck(bool bChecked, NotificationOption notify); +- void SetValue(const WideString& sValue, NotificationOption notify); +- void SetOptionSelection(int index, bool bSelected, NotificationOption notify); +- void ClearSelection(NotificationOption notify); ++ void SetCheck(bool bChecked); ++ void SetValue(const WideString& sValue); ++ void SetOptionSelection(int index); ++ void ClearSelection(); + void SetTopVisibleIndex(int index); + + #ifdef PDF_ENABLE_XFA ++ CXFA_FFWidget* GetMixXFAWidget() const; ++ bool HasXFAAAction(PDFSDK_XFAAActionType eXFAAAT) const; ++ bool OnXFAAAction(PDFSDK_XFAAActionType eXFAAAT, ++ CFFL_FieldAction* data, ++ const CPDFSDK_PageView* pPageView); ++ void Synchronize(bool bSynchronizeElse); + // TODO(thestig): Figure out if the parameter should be used or removed. +- void ResetXFAAppearance(bool bValueChanged); ++ void ResetXFAAppearance(ValueChanged bValueChanged); + #endif // PDF_ENABLE_XFA +- void ResetAppearance(Optional sValue, bool bValueChanged); ++ ++ void ResetAppearance(absl::optional sValue, ++ ValueChanged bValueChanged); + void ResetFieldAppearance(); + void UpdateField(); +- Optional OnFormat(); ++ absl::optional OnFormat(); + + bool OnAAction(CPDF_AAction::AActionType type, +- CPDFSDK_FieldAction* data, +- CPDFSDK_PageView* pPageView); ++ CFFL_FieldAction* data, ++ const CPDFSDK_PageView* pPageView); + +- CPDFSDK_InteractiveForm* GetInteractiveForm() const { +- return m_pInteractiveForm.Get(); +- } + CPDF_FormField* GetFormField() const; + CPDF_FormControl* GetFormControl() const; + +@@ -121,12 +137,8 @@ class CPDFSDK_Widget final : public CPDFSDK_BAAnnot { + uint32_t GetAppearanceAge() const { return m_nAppearanceAge; } + uint32_t GetValueAge() const { return m_nValueAge; } + +- bool IsWidgetAppearanceValid(CPDF_Annot::AppearanceMode mode); +- void DrawAppearance(CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device, +- CPDF_Annot::AppearanceMode mode, +- const CPDF_RenderOptions* pOptions) override; +- ++ bool IsWidgetAppearanceValid(CPDF_Annot::AppearanceMode mode) const; ++ bool IsPushHighlighted() const; + CFX_Matrix GetMatrix() const; + CFX_FloatRect GetClientRect() const; + CFX_FloatRect GetRotatedRect() const; +@@ -135,19 +147,44 @@ class CPDFSDK_Widget final : public CPDFSDK_BAAnnot { + CFX_Color GetFillPWLColor() const; + + private: ++ // CPDFSDK_Annot::UnsafeInputHandlers: ++ void OnMouseEnter(Mask nFlags) override; ++ void OnMouseExit(Mask nFlags) override; ++ bool OnLButtonDown(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnLButtonUp(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnLButtonDblClk(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnMouseMove(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnMouseWheel(Mask nFlags, ++ const CFX_PointF& point, ++ const CFX_Vector& delta) override; ++ bool OnRButtonDown(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnRButtonUp(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnChar(uint32_t nChar, Mask nFlags) override; ++ bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask nFlags) override; ++ bool OnSetFocus(Mask nFlags) override; ++ bool OnKillFocus(Mask nFlags) override; ++ ++ CFFL_InteractiveFormFiller* GetInteractiveFormFiller(); ++ + #ifdef PDF_ENABLE_XFA ++ CXFA_FFWidgetHandler* GetXFAWidgetHandler() const; + CXFA_FFWidget* GetGroupMixXFAWidget() const; + WideString GetName() const; ++ bool HandleXFAAAction(CPDF_AAction::AActionType type, ++ CFFL_FieldAction* data, ++ CPDFSDK_FormFillEnvironment* pFormFillEnv); + #endif // PDF_ENABLE_XFA + + UnownedPtr const m_pInteractiveForm; + bool m_bAppModified = false; + uint32_t m_nAppearanceAge = 0; + uint32_t m_nValueAge = 0; +- +-#ifdef PDF_ENABLE_XFA +- mutable UnownedPtr m_pWidgetHandler; +-#endif // PDF_ENABLE_XFA + }; + + inline CPDFSDK_Widget* ToCPDFSDKWidget(CPDFSDK_Annot* pAnnot) { +diff --git a/fpdfsdk/cpdfsdk_widgethandler.cpp b/fpdfsdk/cpdfsdk_widgethandler.cpp +deleted file mode 100644 +index a3b239462..000000000 +--- a/fpdfsdk/cpdfsdk_widgethandler.cpp ++++ /dev/null +@@ -1,293 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "fpdfsdk/cpdfsdk_widgethandler.h" +- +-#include +-#include +- +-#include "constants/form_flags.h" +-#include "core/fpdfapi/page/cpdf_page.h" +-#include "core/fpdfapi/parser/cpdf_document.h" +-#include "core/fpdfdoc/cpdf_interactiveform.h" +-#include "fpdfsdk/cpdfsdk_annot.h" +-#include "fpdfsdk/cpdfsdk_formfillenvironment.h" +-#include "fpdfsdk/cpdfsdk_interactiveform.h" +-#include "fpdfsdk/cpdfsdk_pageview.h" +-#include "fpdfsdk/cpdfsdk_widget.h" +-#include "fpdfsdk/formfiller/cffl_formfiller.h" +- +-CPDFSDK_WidgetHandler::CPDFSDK_WidgetHandler() = default; +- +-CPDFSDK_WidgetHandler::~CPDFSDK_WidgetHandler() = default; +- +-void CPDFSDK_WidgetHandler::SetFormFillEnvironment( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- m_pFormFillEnv = pFormFillEnv; +- m_pFormFiller = m_pFormFillEnv->GetInteractiveFormFiller(); +-} +- +-bool CPDFSDK_WidgetHandler::CanAnswer(CPDFSDK_Annot* pAnnot) { +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot); +- if (pWidget->IsSignatureWidget()) +- return false; +- +- if (!pWidget->IsVisible()) +- return false; +- +- int nFieldFlags = pWidget->GetFieldFlags(); +- if (nFieldFlags & pdfium::form_flags::kReadOnly) +- return false; +- +- if (pWidget->GetFieldType() == FormFieldType::kPushButton) +- return true; +- +- CPDF_Page* pPage = pWidget->GetPDFPage(); +- uint32_t dwPermissions = pPage->GetDocument()->GetUserPermissions(); +- return (dwPermissions & FPDFPERM_FILL_FORM) || +- (dwPermissions & FPDFPERM_ANNOT_FORM); +-} +- +-CPDFSDK_Annot* CPDFSDK_WidgetHandler::NewAnnot(CPDF_Annot* pAnnot, +- CPDFSDK_PageView* pPage) { +- CPDFSDK_InteractiveForm* pForm = m_pFormFillEnv->GetInteractiveForm(); +- CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm(); +- CPDF_FormControl* pCtrl = pPDFForm->GetControlByDict(pAnnot->GetAnnotDict()); +- if (!pCtrl) +- return nullptr; +- +- CPDFSDK_Widget* pWidget = new CPDFSDK_Widget(pAnnot, pPage, pForm); +- pForm->AddMap(pCtrl, pWidget); +- if (pPDFForm->NeedConstructAP()) +- pWidget->ResetAppearance(pdfium::nullopt, false); +- return pWidget; +-} +- +-void CPDFSDK_WidgetHandler::ReleaseAnnot( +- std::unique_ptr pAnnot) { +- ASSERT(pAnnot); +- m_pFormFiller->OnDelete(pAnnot.get()); +- +- std::unique_ptr pWidget(ToCPDFSDKWidget(pAnnot.release())); +- CPDFSDK_InteractiveForm* pForm = pWidget->GetInteractiveForm(); +- CPDF_FormControl* pControl = pWidget->GetFormControl(); +- pForm->RemoveMap(pControl); +-} +- +-void CPDFSDK_WidgetHandler::OnDraw(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device, +- bool bDrawAnnots) { +- if (pAnnot->IsSignatureWidget()) { +- pAnnot->AsBAAnnot()->DrawAppearance(pDevice, mtUser2Device, +- CPDF_Annot::Normal, nullptr); +- } else { +- m_pFormFiller->OnDraw(pPageView, pAnnot, pDevice, mtUser2Device); +- } +-} +- +-void CPDFSDK_WidgetHandler::OnMouseEnter(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) { +- if (!(*pAnnot)->IsSignatureWidget()) +- m_pFormFiller->OnMouseEnter(pPageView, pAnnot, nFlag); +-} +- +-void CPDFSDK_WidgetHandler::OnMouseExit(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) { +- if (!(*pAnnot)->IsSignatureWidget()) +- m_pFormFiller->OnMouseExit(pPageView, pAnnot, nFlag); +-} +- +-bool CPDFSDK_WidgetHandler::OnLButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- return !(*pAnnot)->IsSignatureWidget() && +- m_pFormFiller->OnLButtonDown(pPageView, pAnnot, nFlags, point); +-} +- +-bool CPDFSDK_WidgetHandler::OnLButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- return !(*pAnnot)->IsSignatureWidget() && +- m_pFormFiller->OnLButtonUp(pPageView, pAnnot, nFlags, point); +-} +- +-bool CPDFSDK_WidgetHandler::OnLButtonDblClk(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- return !(*pAnnot)->IsSignatureWidget() && +- m_pFormFiller->OnLButtonDblClk(pPageView, pAnnot, nFlags, point); +-} +- +-bool CPDFSDK_WidgetHandler::OnMouseMove(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- return !(*pAnnot)->IsSignatureWidget() && +- m_pFormFiller->OnMouseMove(pPageView, pAnnot, nFlags, point); +-} +- +-bool CPDFSDK_WidgetHandler::OnMouseWheel(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- short zDelta, +- const CFX_PointF& point) { +- return !(*pAnnot)->IsSignatureWidget() && +- m_pFormFiller->OnMouseWheel(pPageView, pAnnot, nFlags, zDelta, point); +-} +- +-bool CPDFSDK_WidgetHandler::OnRButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- return !(*pAnnot)->IsSignatureWidget() && +- m_pFormFiller->OnRButtonDown(pPageView, pAnnot, nFlags, point); +-} +- +-bool CPDFSDK_WidgetHandler::OnRButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- return !(*pAnnot)->IsSignatureWidget() && +- m_pFormFiller->OnRButtonUp(pPageView, pAnnot, nFlags, point); +-} +- +-bool CPDFSDK_WidgetHandler::OnRButtonDblClk(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- return false; +-} +- +-bool CPDFSDK_WidgetHandler::OnChar(CPDFSDK_Annot* pAnnot, +- uint32_t nChar, +- uint32_t nFlags) { +- return !pAnnot->IsSignatureWidget() && +- m_pFormFiller->OnChar(pAnnot, nChar, nFlags); +-} +- +-bool CPDFSDK_WidgetHandler::OnKeyDown(CPDFSDK_Annot* pAnnot, +- int nKeyCode, +- int nFlag) { +- return !pAnnot->IsSignatureWidget() && +- m_pFormFiller->OnKeyDown(pAnnot, nKeyCode, nFlag); +-} +- +-bool CPDFSDK_WidgetHandler::OnKeyUp(CPDFSDK_Annot* pAnnot, +- int nKeyCode, +- int nFlag) { +- return false; +-} +- +-void CPDFSDK_WidgetHandler::OnLoad(CPDFSDK_Annot* pAnnot) { +- if (pAnnot->IsSignatureWidget()) +- return; +- +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot); +- if (!pWidget->IsAppearanceValid()) +- pWidget->ResetAppearance(pdfium::nullopt, false); +- +- FormFieldType fieldType = pWidget->GetFieldType(); +- if (fieldType == FormFieldType::kTextField || +- fieldType == FormFieldType::kComboBox) { +- ObservedPtr pObserved(pWidget); +- Optional sValue = pWidget->OnFormat(); +- if (!pObserved) +- return; +- +- if (sValue.has_value() && fieldType == FormFieldType::kComboBox) +- pWidget->ResetAppearance(sValue, false); +- } +- +-#ifdef PDF_ENABLE_XFA +- CPDFSDK_PageView* pPageView = pAnnot->GetPageView(); +- auto* pContext = pPageView->GetFormFillEnv()->GetDocExtension(); +- if (pContext && pContext->ContainsExtensionForegroundForm()) { +- if (!pWidget->IsAppearanceValid() && !pWidget->GetValue().IsEmpty()) +- pWidget->ResetXFAAppearance(false); +- } +-#endif // PDF_ENABLE_XFA +-} +- +-bool CPDFSDK_WidgetHandler::OnSetFocus(ObservedPtr* pAnnot, +- uint32_t nFlag) { +- return (*pAnnot)->IsSignatureWidget() || +- m_pFormFiller->OnSetFocus(pAnnot, nFlag); +-} +- +-bool CPDFSDK_WidgetHandler::OnKillFocus(ObservedPtr* pAnnot, +- uint32_t nFlag) { +- return (*pAnnot)->IsSignatureWidget() || +- m_pFormFiller->OnKillFocus(pAnnot, nFlag); +-} +- +-bool CPDFSDK_WidgetHandler::SetIndexSelected(ObservedPtr* pAnnot, +- int index, +- bool selected) { +- return !(*pAnnot)->IsSignatureWidget() && +- m_pFormFiller->SetIndexSelected(pAnnot, index, selected); +-} +- +-bool CPDFSDK_WidgetHandler::IsIndexSelected(ObservedPtr* pAnnot, +- int index) { +- return !(*pAnnot)->IsSignatureWidget() && +- m_pFormFiller->IsIndexSelected(pAnnot, index); +-} +- +-CFX_FloatRect CPDFSDK_WidgetHandler::GetViewBBox(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot) { +- if (!pAnnot->IsSignatureWidget()) +- return CFX_FloatRect(m_pFormFiller->GetViewBBox(pPageView, pAnnot)); +- return CFX_FloatRect(); +-} +- +-WideString CPDFSDK_WidgetHandler::GetText(CPDFSDK_Annot* pAnnot) { +- if (!pAnnot->IsSignatureWidget()) +- return m_pFormFiller->GetText(pAnnot); +- return WideString(); +-} +- +-WideString CPDFSDK_WidgetHandler::GetSelectedText(CPDFSDK_Annot* pAnnot) { +- if (!pAnnot->IsSignatureWidget()) +- return m_pFormFiller->GetSelectedText(pAnnot); +- return WideString(); +-} +- +-void CPDFSDK_WidgetHandler::ReplaceSelection(CPDFSDK_Annot* pAnnot, +- const WideString& text) { +- if (!pAnnot->IsSignatureWidget()) +- m_pFormFiller->ReplaceSelection(pAnnot, text); +-} +- +-bool CPDFSDK_WidgetHandler::CanUndo(CPDFSDK_Annot* pAnnot) { +- return !pAnnot->IsSignatureWidget() && m_pFormFiller->CanUndo(pAnnot); +-} +- +-bool CPDFSDK_WidgetHandler::CanRedo(CPDFSDK_Annot* pAnnot) { +- return !pAnnot->IsSignatureWidget() && m_pFormFiller->CanRedo(pAnnot); +-} +- +-bool CPDFSDK_WidgetHandler::Undo(CPDFSDK_Annot* pAnnot) { +- return !pAnnot->IsSignatureWidget() && m_pFormFiller->Undo(pAnnot); +-} +- +-bool CPDFSDK_WidgetHandler::Redo(CPDFSDK_Annot* pAnnot) { +- return !pAnnot->IsSignatureWidget() && m_pFormFiller->Redo(pAnnot); +-} +- +-bool CPDFSDK_WidgetHandler::HitTest(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- const CFX_PointF& point) { +- ASSERT(pPageView); +- ASSERT(pAnnot); +- return GetViewBBox(pPageView, pAnnot).Contains(point); +-} +diff --git a/fpdfsdk/cpdfsdk_widgethandler.h b/fpdfsdk/cpdfsdk_widgethandler.h +deleted file mode 100644 +index fa2ac9444..000000000 +--- a/fpdfsdk/cpdfsdk_widgethandler.h ++++ /dev/null +@@ -1,107 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef FPDFSDK_CPDFSDK_WIDGETHANDLER_H_ +-#define FPDFSDK_CPDFSDK_WIDGETHANDLER_H_ +- +-#include +- +-#include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/unowned_ptr.h" +-#include "fpdfsdk/ipdfsdk_annothandler.h" +- +-class CFFL_InteractiveFormFiller; +-class CFX_Matrix; +-class CFX_RenderDevice; +-class CPDF_Annot; +-class CPDFSDK_Annot; +-class CPDFSDK_FormFillEnvironment; +-class CPDFSDK_PageView; +- +-class CPDFSDK_WidgetHandler final : public IPDFSDK_AnnotHandler { +- public: +- CPDFSDK_WidgetHandler(); +- ~CPDFSDK_WidgetHandler() override; +- +- void SetFormFillEnvironment( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) override; +- bool CanAnswer(CPDFSDK_Annot* pAnnot) override; +- CPDFSDK_Annot* NewAnnot(CPDF_Annot* pAnnot, CPDFSDK_PageView* pPage) override; +- void ReleaseAnnot(std::unique_ptr pAnnot) override; +- CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot) override; +- WideString GetText(CPDFSDK_Annot* pAnnot) override; +- WideString GetSelectedText(CPDFSDK_Annot* pAnnot) override; +- void ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text) override; +- bool CanUndo(CPDFSDK_Annot* pAnnot) override; +- bool CanRedo(CPDFSDK_Annot* pAnnot) override; +- bool Undo(CPDFSDK_Annot* pAnnot) override; +- bool Redo(CPDFSDK_Annot* pAnnot) override; +- bool HitTest(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- const CFX_PointF& point) override; +- void OnDraw(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device, +- bool bDrawAnnots) override; +- void OnLoad(CPDFSDK_Annot* pAnnot) override; +- +- void OnMouseEnter(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) override; +- void OnMouseExit(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) override; +- bool OnLButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnLButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnLButtonDblClk(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnMouseMove(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnMouseWheel(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- short zDelta, +- const CFX_PointF& point) override; +- bool OnRButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnRButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnRButtonDblClk(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override; +- bool OnKeyDown(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override; +- bool OnKeyUp(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override; +- bool OnSetFocus(ObservedPtr* pAnnot, uint32_t nFlag) override; +- bool OnKillFocus(ObservedPtr* pAnnot, uint32_t nFlag) override; +- bool SetIndexSelected(ObservedPtr* pAnnot, +- int index, +- bool selected) override; +- bool IsIndexSelected(ObservedPtr* pAnnot, int index) override; +- +- private: +- UnownedPtr m_pFormFillEnv; +- UnownedPtr m_pFormFiller; +-}; +- +-#endif // FPDFSDK_CPDFSDK_WIDGETHANDLER_H_ +diff --git a/fpdfsdk/formfiller/Android.bp b/fpdfsdk/formfiller/Android.bp +index 960992a1a..8ed217e0b 100644 +--- a/fpdfsdk/formfiller/Android.bp ++++ b/fpdfsdk/formfiller/Android.bp +@@ -13,11 +13,8 @@ cc_library_static { + + visibility: ["//external/pdfium:__subpackages__"], + +- header_libs: [ +- "libpdfium-constants", +- ], +- + static_libs: [ ++ "libpdfium-constants", + "libpdfium-page", + "libpdfium-fpdfdoc", + "libpdfium-fxcrt", +diff --git a/fpdfsdk/formfiller/BUILD.gn b/fpdfsdk/formfiller/BUILD.gn +index e612ef406..857bb5a69 100644 +--- a/fpdfsdk/formfiller/BUILD.gn ++++ b/fpdfsdk/formfiller/BUILD.gn +@@ -1,8 +1,9 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + + import("../../pdfium.gni") ++import("../../testing/test.gni") + + source_set("formfiller") { + sources = [ +@@ -12,12 +13,16 @@ source_set("formfiller") { + "cffl_checkbox.h", + "cffl_combobox.cpp", + "cffl_combobox.h", +- "cffl_formfiller.cpp", +- "cffl_formfiller.h", ++ "cffl_fieldaction.cpp", ++ "cffl_fieldaction.h", ++ "cffl_formfield.cpp", ++ "cffl_formfield.h", + "cffl_interactiveformfiller.cpp", + "cffl_interactiveformfiller.h", + "cffl_listbox.cpp", + "cffl_listbox.h", ++ "cffl_perwindowdata.cpp", ++ "cffl_perwindowdata.h", + "cffl_pushbutton.cpp", + "cffl_pushbutton.h", + "cffl_radiobutton.cpp", +@@ -27,7 +32,10 @@ source_set("formfiller") { + "cffl_textobject.cpp", + "cffl_textobject.h", + ] +- configs += [ "../../:pdfium_core_config" ] ++ configs += [ ++ "../../:pdfium_strict_config", ++ "../../:pdfium_noshorten_config", ++ ] + deps = [ + "../../:pdfium_public_headers", + "../../constants", +@@ -39,3 +47,12 @@ source_set("formfiller") { + ] + visibility = [ "../../*" ] + } ++ ++pdfium_embeddertest_source_set("embeddertests") { ++ sources = [ "cffl_combobox_embeddertest.cpp" ] ++ deps = [ ++ ":formfiller", ++ "../pwl:embedder_test_support", ++ ] ++ pdfium_root_dir = "../../" ++} +diff --git a/fpdfsdk/formfiller/cffl_button.cpp b/fpdfsdk/formfiller/cffl_button.cpp +index 35209275d..6c466d5b1 100644 +--- a/fpdfsdk/formfiller/cffl_button.cpp ++++ b/fpdfsdk/formfiller/cffl_button.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,15 +6,14 @@ + + #include "fpdfsdk/formfiller/cffl_button.h" + +-#include "core/fpdfdoc/cpdf_formcontrol.h" ++#include "fpdfsdk/cpdfsdk_widget.h" ++#include "third_party/base/check.h" + +-CFFL_Button::CFFL_Button(CPDFSDK_FormFillEnvironment* pFormFillEnv, ++CFFL_Button::CFFL_Button(CFFL_InteractiveFormFiller* pFormFiller, + CPDFSDK_Widget* pWidget) +- : CFFL_FormFiller(pFormFillEnv, pWidget), +- m_bMouseIn(false), +- m_bMouseDown(false) {} ++ : CFFL_FormField(pFormFiller, pWidget) {} + +-CFFL_Button::~CFFL_Button() {} ++CFFL_Button::~CFFL_Button() = default; + + void CFFL_Button::OnMouseEnter(CPDFSDK_PageView* pPageView) { + m_bMouseIn = true; +@@ -25,14 +24,14 @@ void CFFL_Button::OnMouseExit(CPDFSDK_PageView* pPageView) { + m_bMouseIn = false; + InvalidateRect(GetViewBBox(pPageView)); + m_pTimer.reset(); +- ASSERT(m_pWidget); ++ DCHECK(m_pWidget); + } + + bool CFFL_Button::OnLButtonDown(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- uint32_t nFlags, ++ CPDFSDK_Widget* pWidget, ++ Mask nFlags, + const CFX_PointF& point) { +- if (!pAnnot->GetRect().Contains(point)) ++ if (!pWidget->GetRect().Contains(point)) + return false; + + m_bMouseDown = true; +@@ -42,63 +41,62 @@ bool CFFL_Button::OnLButtonDown(CPDFSDK_PageView* pPageView, + } + + bool CFFL_Button::OnLButtonUp(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- uint32_t nFlags, ++ CPDFSDK_Widget* pWidget, ++ Mask nFlags, + const CFX_PointF& point) { +- if (!pAnnot->GetRect().Contains(point)) ++ if (!pWidget->GetRect().Contains(point)) + return false; + + m_bMouseDown = false; +- m_pWidget->GetPDFPage(); + InvalidateRect(GetViewBBox(pPageView)); + return true; + } + + bool CFFL_Button::OnMouseMove(CPDFSDK_PageView* pPageView, +- uint32_t nFlags, ++ Mask nFlags, + const CFX_PointF& point) { + return true; + } + + void CFFL_Button::OnDraw(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, ++ CPDFSDK_Widget* pWidget, + CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device) { +- ASSERT(pPageView); +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot); +- CPDF_FormControl* pCtrl = pWidget->GetFormControl(); +- if (pCtrl->GetHighlightingMode() != CPDF_FormControl::Push) { +- pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Normal, +- nullptr); ++ DCHECK(pPageView); ++ if (!pWidget->IsPushHighlighted()) { ++ pWidget->DrawAppearance(pDevice, mtUser2Device, ++ CPDF_Annot::AppearanceMode::kNormal); + return; + } + if (m_bMouseDown) { +- if (pWidget->IsWidgetAppearanceValid(CPDF_Annot::Down)) { +- pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Down, +- nullptr); ++ if (pWidget->IsWidgetAppearanceValid(CPDF_Annot::AppearanceMode::kDown)) { ++ pWidget->DrawAppearance(pDevice, mtUser2Device, ++ CPDF_Annot::AppearanceMode::kDown); + } else { +- pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Normal, +- nullptr); ++ pWidget->DrawAppearance(pDevice, mtUser2Device, ++ CPDF_Annot::AppearanceMode::kNormal); + } + return; + } + if (m_bMouseIn) { +- if (pWidget->IsWidgetAppearanceValid(CPDF_Annot::Rollover)) { +- pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Rollover, +- nullptr); ++ if (pWidget->IsWidgetAppearanceValid( ++ CPDF_Annot::AppearanceMode::kRollover)) { ++ pWidget->DrawAppearance(pDevice, mtUser2Device, ++ CPDF_Annot::AppearanceMode::kRollover); + } else { +- pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Normal, +- nullptr); ++ pWidget->DrawAppearance(pDevice, mtUser2Device, ++ CPDF_Annot::AppearanceMode::kNormal); + } + return; + } + +- pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Normal, nullptr); ++ pWidget->DrawAppearance(pDevice, mtUser2Device, ++ CPDF_Annot::AppearanceMode::kNormal); + } + + void CFFL_Button::OnDrawDeactive(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, ++ CPDFSDK_Widget* pWidget, + CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device) { +- OnDraw(pPageView, pAnnot, pDevice, mtUser2Device); ++ OnDraw(pPageView, pWidget, pDevice, mtUser2Device); + } +diff --git a/fpdfsdk/formfiller/cffl_button.h b/fpdfsdk/formfiller/cffl_button.h +index 768c14424..eaa9ad623 100644 +--- a/fpdfsdk/formfiller/cffl_button.h ++++ b/fpdfsdk/formfiller/cffl_button.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,47 +8,44 @@ + #define FPDFSDK_FORMFILLER_CFFL_BUTTON_H_ + + #include "core/fxcrt/fx_coordinates.h" +-#include "fpdfsdk/formfiller/cffl_formfiller.h" ++#include "fpdfsdk/formfiller/cffl_formfield.h" + + class CFX_RenderDevice; + class CFX_Matrix; +-class CPDFSDK_Annot; +-class CPDFSDK_FormFillEnvironment; + class CPDFSDK_PageView; + class CPDFSDK_Widget; + +-class CFFL_Button : public CFFL_FormFiller { ++class CFFL_Button : public CFFL_FormField { + public: +- CFFL_Button(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- CPDFSDK_Widget* pWidget); ++ CFFL_Button(CFFL_InteractiveFormFiller* pFormFiller, CPDFSDK_Widget* pWidget); + ~CFFL_Button() override; + +- // CFFL_FormFiller ++ // CFFL_FormField + void OnMouseEnter(CPDFSDK_PageView* pPageView) override; + void OnMouseExit(CPDFSDK_PageView* pPageView) override; + bool OnLButtonDown(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- uint32_t nFlags, ++ CPDFSDK_Widget* pWidget, ++ Mask nFlags, + const CFX_PointF& point) override; + bool OnLButtonUp(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- uint32_t nFlags, ++ CPDFSDK_Widget* pWidget, ++ Mask nFlags, + const CFX_PointF& point) override; + bool OnMouseMove(CPDFSDK_PageView* pPageView, +- uint32_t nFlags, ++ Mask nFlags, + const CFX_PointF& point) override; + void OnDraw(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, ++ CPDFSDK_Widget* pWidget, + CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device) override; + void OnDrawDeactive(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, ++ CPDFSDK_Widget* pWidget, + CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device) override; + + private: +- bool m_bMouseIn; +- bool m_bMouseDown; ++ bool m_bMouseIn = false; ++ bool m_bMouseDown = false; + }; + + #endif // FPDFSDK_FORMFILLER_CFFL_BUTTON_H_ +diff --git a/fpdfsdk/formfiller/cffl_checkbox.cpp b/fpdfsdk/formfiller/cffl_checkbox.cpp +index b0e60e2b0..527720e0c 100644 +--- a/fpdfsdk/formfiller/cffl_checkbox.cpp ++++ b/fpdfsdk/formfiller/cffl_checkbox.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,50 +8,51 @@ + + #include + ++#include "constants/ascii.h" + #include "core/fpdfdoc/cpdf_formcontrol.h" +-#include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fpdfsdk/cpdfsdk_widget.h" +-#include "fpdfsdk/formfiller/cffl_formfiller.h" ++#include "fpdfsdk/formfiller/cffl_formfield.h" + #include "fpdfsdk/pwl/cpwl_special_button.h" + #include "public/fpdf_fwlevent.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + +-CFFL_CheckBox::CFFL_CheckBox(CPDFSDK_FormFillEnvironment* pApp, ++CFFL_CheckBox::CFFL_CheckBox(CFFL_InteractiveFormFiller* pFormFiller, + CPDFSDK_Widget* pWidget) +- : CFFL_Button(pApp, pWidget) {} ++ : CFFL_Button(pFormFiller, pWidget) {} + +-CFFL_CheckBox::~CFFL_CheckBox() {} ++CFFL_CheckBox::~CFFL_CheckBox() = default; + + std::unique_ptr CFFL_CheckBox::NewPWLWindow( + const CPWL_Wnd::CreateParams& cp, +- std::unique_ptr pAttachedData) { +- auto pWnd = pdfium::MakeUnique(cp, std::move(pAttachedData)); ++ std::unique_ptr pAttachedData) { ++ auto pWnd = std::make_unique(cp, std::move(pAttachedData)); + pWnd->Realize(); + pWnd->SetCheck(m_pWidget->IsChecked()); + return std::move(pWnd); + } + +-bool CFFL_CheckBox::OnKeyDown(uint32_t nKeyCode, uint32_t nFlags) { ++bool CFFL_CheckBox::OnKeyDown(FWL_VKEYCODE nKeyCode, ++ Mask nFlags) { + switch (nKeyCode) { + case FWL_VKEY_Return: + case FWL_VKEY_Space: + return true; + default: +- return CFFL_FormFiller::OnKeyDown(nKeyCode, nFlags); ++ return CFFL_FormField::OnKeyDown(nKeyCode, nFlags); + } + } +-bool CFFL_CheckBox::OnChar(CPDFSDK_Annot* pAnnot, ++ ++bool CFFL_CheckBox::OnChar(CPDFSDK_Widget* pWidget, + uint32_t nChar, +- uint32_t nFlags) { ++ Mask nFlags) { + switch (nChar) { +- case FWL_VKEY_Return: +- case FWL_VKEY_Space: { +- CPDFSDK_PageView* pPageView = pAnnot->GetPageView(); +- ASSERT(pPageView); ++ case pdfium::ascii::kReturn: ++ case pdfium::ascii::kSpace: { ++ CPDFSDK_PageView* pPageView = pWidget->GetPageView(); ++ DCHECK(pPageView); + +- ObservedPtr pObserved(m_pWidget.Get()); +- if (m_pFormFillEnv->GetInteractiveFormFiller()->OnButtonUp( +- &pObserved, pPageView, nFlags)) { ++ ObservedPtr pObserved(m_pWidget); ++ if (m_pFormFiller->OnButtonUp(pObserved, pPageView, nFlags)) { + if (!pObserved) + m_pWidget = nullptr; + return true; +@@ -61,63 +62,56 @@ bool CFFL_CheckBox::OnChar(CPDFSDK_Annot* pAnnot, + return true; + } + +- CFFL_FormFiller::OnChar(pAnnot, nChar, nFlags); ++ CFFL_FormField::OnChar(pWidget, nChar, nFlags); + +- CPWL_CheckBox* pWnd = GetCheckBox(pPageView, true); +- if (pWnd) { +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot); +- pWnd->SetCheck(!pWidget->IsChecked()); ++ CPWL_CheckBox* pWnd = CreateOrUpdatePWLCheckBox(pPageView); ++ if (pWnd && !pWnd->IsReadOnly()) { ++ ObservedPtr pObservedBox(pWnd); ++ const bool is_checked = pWidget->IsChecked(); ++ if (pObservedBox) { ++ pObservedBox->SetCheck(!is_checked); ++ } + } +- + return CommitData(pPageView, nFlags); + } + default: +- return CFFL_FormFiller::OnChar(pAnnot, nChar, nFlags); ++ return CFFL_FormField::OnChar(pWidget, nChar, nFlags); + } + } + + bool CFFL_CheckBox::OnLButtonUp(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- uint32_t nFlags, ++ CPDFSDK_Widget* pWidget, ++ Mask nFlags, + const CFX_PointF& point) { +- CFFL_Button::OnLButtonUp(pPageView, pAnnot, nFlags, point); +- +- if (!IsValid()) ++ CFFL_Button::OnLButtonUp(pPageView, pWidget, nFlags, point); ++ if (!IsValid()) { + return true; +- +- CPWL_CheckBox* pWnd = GetCheckBox(pPageView, true); ++ } ++ CPWL_CheckBox* pWnd = CreateOrUpdatePWLCheckBox(pPageView); + if (pWnd) { +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot); +- pWnd->SetCheck(!pWidget->IsChecked()); ++ ObservedPtr pObservedBox(pWnd); ++ const bool is_checked = pWidget->IsChecked(); ++ if (pObservedBox) { ++ pObservedBox->SetCheck(!is_checked); ++ } + } +- + return CommitData(pPageView, nFlags); + } + +-bool CFFL_CheckBox::IsDataChanged(CPDFSDK_PageView* pPageView) { +- CPWL_CheckBox* pWnd = GetCheckBox(pPageView, false); ++bool CFFL_CheckBox::IsDataChanged(const CPDFSDK_PageView* pPageView) { ++ CPWL_CheckBox* pWnd = GetPWLCheckBox(pPageView); + return pWnd && pWnd->IsChecked() != m_pWidget->IsChecked(); + } + +-void CFFL_CheckBox::SaveData(CPDFSDK_PageView* pPageView) { +- CPWL_CheckBox* pWnd = GetCheckBox(pPageView, false); ++void CFFL_CheckBox::SaveData(const CPDFSDK_PageView* pPageView) { ++ CPWL_CheckBox* pWnd = GetPWLCheckBox(pPageView); + if (!pWnd) + return; + + bool bNewChecked = pWnd->IsChecked(); +- if (bNewChecked) { +- CPDF_FormField* pField = m_pWidget->GetFormField(); +- for (int32_t i = 0, sz = pField->CountControls(); i < sz; i++) { +- if (CPDF_FormControl* pCtrl = pField->GetControl(i)) { +- if (pCtrl->IsChecked()) { +- break; +- } +- } +- } +- } +- ObservedPtr observed_widget(m_pWidget.Get()); ++ ObservedPtr observed_widget(m_pWidget); + ObservedPtr observed_this(this); +- m_pWidget->SetCheck(bNewChecked, NotificationOption::kDoNotNotify); ++ m_pWidget->SetCheck(bNewChecked); + if (!observed_widget) + return; + +@@ -128,7 +122,12 @@ void CFFL_CheckBox::SaveData(CPDFSDK_PageView* pPageView) { + SetChangeMark(); + } + +-CPWL_CheckBox* CFFL_CheckBox::GetCheckBox(CPDFSDK_PageView* pPageView, +- bool bNew) { +- return static_cast(GetPWLWindow(pPageView, bNew)); ++CPWL_CheckBox* CFFL_CheckBox::GetPWLCheckBox( ++ const CPDFSDK_PageView* pPageView) const { ++ return static_cast(GetPWLWindow(pPageView)); ++} ++ ++CPWL_CheckBox* CFFL_CheckBox::CreateOrUpdatePWLCheckBox( ++ const CPDFSDK_PageView* pPageView) { ++ return static_cast(CreateOrUpdatePWLWindow(pPageView)); + } +diff --git a/fpdfsdk/formfiller/cffl_checkbox.h b/fpdfsdk/formfiller/cffl_checkbox.h +index aff2762fc..5669429a8 100644 +--- a/fpdfsdk/formfiller/cffl_checkbox.h ++++ b/fpdfsdk/formfiller/cffl_checkbox.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -15,25 +15,28 @@ class CPWL_CheckBox; + + class CFFL_CheckBox final : public CFFL_Button { + public: +- CFFL_CheckBox(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget); ++ CFFL_CheckBox(CFFL_InteractiveFormFiller* pFormFiller, ++ CPDFSDK_Widget* pWidget); + ~CFFL_CheckBox() override; + + // CFFL_Button: + std::unique_ptr NewPWLWindow( + const CPWL_Wnd::CreateParams& cp, +- std::unique_ptr pAttachedData) +- override; +- bool OnKeyDown(uint32_t nKeyCode, uint32_t nFlags) override; +- bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override; ++ std::unique_ptr pAttachedData) override; ++ bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask nFlags) override; ++ bool OnChar(CPDFSDK_Widget* pWidget, ++ uint32_t nChar, ++ Mask nFlags) override; + bool OnLButtonUp(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- uint32_t nFlags, ++ CPDFSDK_Widget* pWidget, ++ Mask nFlags, + const CFX_PointF& point) override; +- bool IsDataChanged(CPDFSDK_PageView* pPageView) override; +- void SaveData(CPDFSDK_PageView* pPageView) override; ++ bool IsDataChanged(const CPDFSDK_PageView* pPageView) override; ++ void SaveData(const CPDFSDK_PageView* pPageView) override; + + private: +- CPWL_CheckBox* GetCheckBox(CPDFSDK_PageView* pPageView, bool bNew); ++ CPWL_CheckBox* GetPWLCheckBox(const CPDFSDK_PageView* pPageView) const; ++ CPWL_CheckBox* CreateOrUpdatePWLCheckBox(const CPDFSDK_PageView* pPageView); + }; + + #endif // FPDFSDK_FORMFILLER_CFFL_CHECKBOX_H_ +diff --git a/fpdfsdk/formfiller/cffl_combobox.cpp b/fpdfsdk/formfiller/cffl_combobox.cpp +index 588c50c2f..7364135e7 100644 +--- a/fpdfsdk/formfiller/cffl_combobox.cpp ++++ b/fpdfsdk/formfiller/cffl_combobox.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,22 +9,18 @@ + #include + + #include "constants/form_flags.h" +-#include "core/fpdfdoc/cba_fontmap.h" +-#include "fpdfsdk/cpdfsdk_formfillenvironment.h" ++#include "core/fpdfdoc/cpdf_bafontmap.h" + #include "fpdfsdk/cpdfsdk_widget.h" + #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h" ++#include "fpdfsdk/formfiller/cffl_perwindowdata.h" + #include "fpdfsdk/pwl/cpwl_combo_box.h" +-#include "third_party/base/ptr_util.h" ++#include "fpdfsdk/pwl/cpwl_edit.h" + +-CFFL_ComboBox::CFFL_ComboBox(CPDFSDK_FormFillEnvironment* pApp, ++CFFL_ComboBox::CFFL_ComboBox(CFFL_InteractiveFormFiller* pFormFiller, + CPDFSDK_Widget* pWidget) +- : CFFL_TextObject(pApp, pWidget) { +-} ++ : CFFL_TextObject(pFormFiller, pWidget) {} + + CFFL_ComboBox::~CFFL_ComboBox() { +- for (const auto& it : m_Maps) +- it.second->InvalidateFocusHandler(this); +- + // See comment in cffl_formfiller.h. + // The font map should be stored somewhere more appropriate so it will live + // until the PWL_Edit is done with it. pdfium:566 +@@ -36,22 +32,17 @@ CPWL_Wnd::CreateParams CFFL_ComboBox::GetCreateParam() { + if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceEdit) + cp.dwFlags |= PCBS_ALLOWCUSTOMTEXT; + +- cp.pFontMap = MaybeCreateFontMap(); +- cp.pFocusHandler = this; ++ cp.pFontMap = GetOrCreateFontMap(); + return cp; + } + + std::unique_ptr CFFL_ComboBox::NewPWLWindow( + const CPWL_Wnd::CreateParams& cp, +- std::unique_ptr pAttachedData) { +- auto pWnd = pdfium::MakeUnique(cp, std::move(pAttachedData)); +- pWnd->AttachFFLData(this); ++ std::unique_ptr pAttachedData) { ++ static_cast(pAttachedData.get())->SetFormField(this); ++ auto pWnd = std::make_unique(cp, std::move(pAttachedData)); + pWnd->Realize(); + +- CFFL_InteractiveFormFiller* pFormFiller = +- m_pFormFillEnv->GetInteractiveFormFiller(); +- pWnd->SetFillerNotify(pFormFiller); +- + int32_t nCurSel = m_pWidget->GetSelectedIndex(0); + WideString swText; + if (nCurSel < 0) +@@ -67,14 +58,14 @@ std::unique_ptr CFFL_ComboBox::NewPWLWindow( + return std::move(pWnd); + } + +-bool CFFL_ComboBox::OnChar(CPDFSDK_Annot* pAnnot, ++bool CFFL_ComboBox::OnChar(CPDFSDK_Widget* pWidget, + uint32_t nChar, +- uint32_t nFlags) { +- return CFFL_TextObject::OnChar(pAnnot, nChar, nFlags); ++ Mask nFlags) { ++ return CFFL_TextObject::OnChar(pWidget, nChar, nFlags); + } + +-bool CFFL_ComboBox::IsDataChanged(CPDFSDK_PageView* pPageView) { +- auto* pWnd = GetComboBox(pPageView, false); ++bool CFFL_ComboBox::IsDataChanged(const CPDFSDK_PageView* pPageView) { ++ auto* pWnd = GetPWLComboBox(pPageView); + if (!pWnd) + return false; + +@@ -88,8 +79,8 @@ bool CFFL_ComboBox::IsDataChanged(CPDFSDK_PageView* pPageView) { + return pWnd->GetText() != m_pWidget->GetValue(); + } + +-void CFFL_ComboBox::SaveData(CPDFSDK_PageView* pPageView) { +- CPWL_ComboBox* pWnd = GetComboBox(pPageView, false); ++void CFFL_ComboBox::SaveData(const CPDFSDK_PageView* pPageView) { ++ CPWL_ComboBox* pWnd = GetPWLComboBox(pPageView); + if (!pWnd) + return; + +@@ -100,13 +91,12 @@ void CFFL_ComboBox::SaveData(CPDFSDK_PageView* pPageView) { + bSetValue = (nCurSel < 0) || (swText != m_pWidget->GetOptionLabel(nCurSel)); + + if (bSetValue) { +- m_pWidget->SetValue(swText, NotificationOption::kDoNotNotify); ++ m_pWidget->SetValue(swText); + } else { + m_pWidget->GetSelectedIndex(0); +- m_pWidget->SetOptionSelection(nCurSel, true, +- NotificationOption::kDoNotNotify); ++ m_pWidget->SetOptionSelection(nCurSel); + } +- ObservedPtr observed_widget(m_pWidget.Get()); ++ ObservedPtr observed_widget(m_pWidget); + ObservedPtr observed_this(this); + m_pWidget->ResetFieldAppearance(); + if (!observed_widget) +@@ -117,22 +107,17 @@ void CFFL_ComboBox::SaveData(CPDFSDK_PageView* pPageView) { + return; + + SetChangeMark(); +- m_pWidget->GetPDFPage(); + } + +-void CFFL_ComboBox::GetActionData(CPDFSDK_PageView* pPageView, ++void CFFL_ComboBox::GetActionData(const CPDFSDK_PageView* pPageView, + CPDF_AAction::AActionType type, +- CPDFSDK_FieldAction& fa) { ++ CFFL_FieldAction& fa) { + switch (type) { + case CPDF_AAction::kKeyStroke: +- if (CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false)) { ++ if (CPWL_ComboBox* pComboBox = GetPWLComboBox(pPageView)) { + if (CPWL_Edit* pEdit = pComboBox->GetEdit()) { + fa.bFieldFull = pEdit->IsTextFull(); +- int nSelStart = 0; +- int nSelEnd = 0; +- pEdit->GetSelection(nSelStart, nSelEnd); +- fa.nSelEnd = nSelEnd; +- fa.nSelStart = nSelStart; ++ std::tie(fa.nSelStart, fa.nSelEnd) = pEdit->GetSelection(); + fa.sValue = pEdit->GetText(); + fa.sChangeEx = GetSelectExportText(); + +@@ -144,7 +129,7 @@ void CFFL_ComboBox::GetActionData(CPDFSDK_PageView* pPageView, + } + break; + case CPDF_AAction::kValidate: +- if (CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false)) { ++ if (CPWL_ComboBox* pComboBox = GetPWLComboBox(pPageView)) { + if (CPWL_Edit* pEdit = pComboBox->GetEdit()) { + fa.sValue = pEdit->GetText(); + } +@@ -159,12 +144,12 @@ void CFFL_ComboBox::GetActionData(CPDFSDK_PageView* pPageView, + } + } + +-void CFFL_ComboBox::SetActionData(CPDFSDK_PageView* pPageView, ++void CFFL_ComboBox::SetActionData(const CPDFSDK_PageView* pPageView, + CPDF_AAction::AActionType type, +- const CPDFSDK_FieldAction& fa) { ++ const CFFL_FieldAction& fa) { + switch (type) { + case CPDF_AAction::kKeyStroke: +- if (CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false)) { ++ if (CPWL_ComboBox* pComboBox = GetPWLComboBox(pPageView)) { + if (CPWL_Edit* pEdit = pComboBox->GetEdit()) { + pEdit->SetSelection(fa.nSelStart, fa.nSelEnd); + pEdit->ReplaceSelection(fa.sChange); +@@ -176,8 +161,8 @@ void CFFL_ComboBox::SetActionData(CPDFSDK_PageView* pPageView, + } + } + +-void CFFL_ComboBox::SaveState(CPDFSDK_PageView* pPageView) { +- CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false); ++void CFFL_ComboBox::SavePWLWindowState(const CPDFSDK_PageView* pPageView) { ++ CPWL_ComboBox* pComboBox = GetPWLComboBox(pPageView); + if (!pComboBox) + return; + +@@ -187,12 +172,13 @@ void CFFL_ComboBox::SaveState(CPDFSDK_PageView* pPageView) { + if (!pEdit) + return; + +- pEdit->GetSelection(m_State.nStart, m_State.nEnd); ++ std::tie(m_State.nStart, m_State.nEnd) = pEdit->GetSelection(); + m_State.sValue = pEdit->GetText(); + } + +-void CFFL_ComboBox::RestoreState(CPDFSDK_PageView* pPageView) { +- CPWL_ComboBox* pComboBox = GetComboBox(pPageView, true); ++void CFFL_ComboBox::RecreatePWLWindowFromSavedState( ++ const CPDFSDK_PageView* pPageView) { ++ CPWL_ComboBox* pComboBox = CreateOrUpdatePWLComboBox(pPageView); + if (!pComboBox) + return; + +@@ -216,7 +202,7 @@ bool CFFL_ComboBox::SetIndexSelected(int index, bool selected) { + if (index < 0 || index >= m_pWidget->CountOptions()) + return false; + +- CPWL_ComboBox* pWnd = GetComboBox(GetCurPageView(true), false); ++ CPWL_ComboBox* pWnd = GetPWLComboBox(GetCurPageView()); + if (!pWnd) + return false; + +@@ -231,13 +217,13 @@ bool CFFL_ComboBox::IsIndexSelected(int index) { + if (index < 0 || index >= m_pWidget->CountOptions()) + return false; + +- CPWL_ComboBox* pWnd = GetComboBox(GetCurPageView(true), false); ++ CPWL_ComboBox* pWnd = GetPWLComboBox(GetCurPageView()); + return pWnd && index == pWnd->GetSelect(); + } + + #ifdef PDF_ENABLE_XFA +-bool CFFL_ComboBox::IsFieldFull(CPDFSDK_PageView* pPageView) { +- CPWL_ComboBox* pComboBox = GetComboBox(pPageView, false); ++bool CFFL_ComboBox::IsFieldFull(const CPDFSDK_PageView* pPageView) { ++ CPWL_ComboBox* pComboBox = GetPWLComboBox(pPageView); + if (!pComboBox) + return false; + +@@ -246,35 +232,24 @@ bool CFFL_ComboBox::IsFieldFull(CPDFSDK_PageView* pPageView) { + } + #endif // PDF_ENABLE_XFA + +-void CFFL_ComboBox::OnSetFocus(CPWL_Edit* pEdit) { +- pEdit->SetCharSet(FX_CHARSET_ChineseSimplified); ++void CFFL_ComboBox::OnSetFocusForEdit(CPWL_Edit* pEdit) { ++ pEdit->SetCharSet(FX_Charset::kChineseSimplified); + pEdit->SetReadyToInput(); +- +- WideString wsText = pEdit->GetText(); +- int nCharacters = wsText.GetLength(); +- ByteString bsUTFText = wsText.ToUTF16LE(); +- auto* pBuffer = reinterpret_cast(bsUTFText.c_str()); +- m_pFormFillEnv->OnSetFieldInputFocus(pBuffer, nCharacters, true); ++ m_pFormFiller->OnSetFieldInputFocus(pEdit->GetText()); + } + + WideString CFFL_ComboBox::GetSelectExportText() { +- WideString swRet; +- +- CPWL_ComboBox* pComboBox = GetComboBox(GetCurPageView(true), false); ++ CPWL_ComboBox* pComboBox = GetPWLComboBox(GetCurPageView()); + int nExport = pComboBox ? pComboBox->GetSelect() : -1; ++ return m_pWidget->GetSelectExportText(nExport); ++} + +- if (nExport >= 0) { +- if (CPDF_FormField* pFormField = m_pWidget->GetFormField()) { +- swRet = pFormField->GetOptionValue(nExport); +- if (swRet.IsEmpty()) +- swRet = pFormField->GetOptionLabel(nExport); +- } +- } +- +- return swRet; ++CPWL_ComboBox* CFFL_ComboBox::GetPWLComboBox( ++ const CPDFSDK_PageView* pPageView) const { ++ return static_cast(GetPWLWindow(pPageView)); + } + +-CPWL_ComboBox* CFFL_ComboBox::GetComboBox(CPDFSDK_PageView* pPageView, +- bool bNew) { +- return static_cast(GetPWLWindow(pPageView, bNew)); ++CPWL_ComboBox* CFFL_ComboBox::CreateOrUpdatePWLComboBox( ++ const CPDFSDK_PageView* pPageView) { ++ return static_cast(CreateOrUpdatePWLWindow(pPageView)); + } +diff --git a/fpdfsdk/formfiller/cffl_combobox.h b/fpdfsdk/formfiller/cffl_combobox.h +index 250dba794..e2cc51e1e 100644 +--- a/fpdfsdk/formfiller/cffl_combobox.h ++++ b/fpdfsdk/formfiller/cffl_combobox.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,7 +9,7 @@ + + #include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" + #include "fpdfsdk/formfiller/cffl_textobject.h" + + class CPWL_ComboBox; +@@ -21,41 +21,44 @@ struct FFL_ComboBoxState { + WideString sValue; + }; + +-class CFFL_ComboBox final : public CFFL_TextObject, +- public CPWL_Wnd::FocusHandlerIface { ++class CFFL_ComboBox final : public CFFL_TextObject { + public: +- CFFL_ComboBox(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget); ++ CFFL_ComboBox(CFFL_InteractiveFormFiller* pFormFiller, ++ CPDFSDK_Widget* pWidget); + ~CFFL_ComboBox() override; + + // CFFL_TextObject: + CPWL_Wnd::CreateParams GetCreateParam() override; + std::unique_ptr NewPWLWindow( + const CPWL_Wnd::CreateParams& cp, +- std::unique_ptr pAttachedData) +- override; +- bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override; +- bool IsDataChanged(CPDFSDK_PageView* pPageView) override; +- void SaveData(CPDFSDK_PageView* pPageView) override; +- void GetActionData(CPDFSDK_PageView* pPageView, ++ std::unique_ptr pAttachedData) override; ++ bool OnChar(CPDFSDK_Widget* pWidget, ++ uint32_t nChar, ++ Mask nFlags) override; ++ bool IsDataChanged(const CPDFSDK_PageView* pPageView) override; ++ void SaveData(const CPDFSDK_PageView* pPageView) override; ++ void GetActionData(const CPDFSDK_PageView* pPageView, + CPDF_AAction::AActionType type, +- CPDFSDK_FieldAction& fa) override; +- void SetActionData(CPDFSDK_PageView* pPageView, ++ CFFL_FieldAction& fa) override; ++ void SetActionData(const CPDFSDK_PageView* pPageView, + CPDF_AAction::AActionType type, +- const CPDFSDK_FieldAction& fa) override; +- void SaveState(CPDFSDK_PageView* pPageView) override; +- void RestoreState(CPDFSDK_PageView* pPageView) override; ++ const CFFL_FieldAction& fa) override; ++ void SavePWLWindowState(const CPDFSDK_PageView* pPageView) override; ++ void RecreatePWLWindowFromSavedState( ++ const CPDFSDK_PageView* pPageView) override; + bool SetIndexSelected(int index, bool selected) override; + bool IsIndexSelected(int index) override; + #ifdef PDF_ENABLE_XFA +- bool IsFieldFull(CPDFSDK_PageView* pPageView) override; ++ bool IsFieldFull(const CPDFSDK_PageView* pPageView) override; + #endif + +- // CPWL_Wnd::FocusHandlerIface: +- void OnSetFocus(CPWL_Edit* pEdit) override; ++ // CPWL_Wnd::ProviderIface: ++ void OnSetFocusForEdit(CPWL_Edit* pEdit) override; + + private: + WideString GetSelectExportText(); +- CPWL_ComboBox* GetComboBox(CPDFSDK_PageView* pPageView, bool bNew); ++ CPWL_ComboBox* GetPWLComboBox(const CPDFSDK_PageView* pPageView) const; ++ CPWL_ComboBox* CreateOrUpdatePWLComboBox(const CPDFSDK_PageView* pPageView); + + FFL_ComboBoxState m_State; + }; +diff --git a/fpdfsdk/formfiller/cffl_combobox_embeddertest.cpp b/fpdfsdk/formfiller/cffl_combobox_embeddertest.cpp +new file mode 100644 +index 000000000..dbde07544 +--- /dev/null ++++ b/fpdfsdk/formfiller/cffl_combobox_embeddertest.cpp +@@ -0,0 +1,50 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "fpdfsdk/formfiller/cffl_combobox.h" ++ ++#include "fpdfsdk/pwl/cpwl_combo_box_embeddertest.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++class CFFLComboBoxEmbedderTest : public CPWLComboBoxEmbedderTest {}; ++ ++TEST_F(CFFLComboBoxEmbedderTest, GetActionData) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotNormal()); ++ { ++ CFFL_FieldAction result; ++ GetCFFLFormField()->GetActionData(GetPageView(), CPDF_AAction::kKeyStroke, ++ result); ++ EXPECT_EQ(L"Banana", result.sValue); ++ EXPECT_EQ(L"Banana", result.sChangeEx); ++ } ++ { ++ CFFL_FieldAction result; ++ GetCFFLFormField()->GetActionData(GetPageView(), CPDF_AAction::kValidate, ++ result); ++ EXPECT_EQ(L"Banana", result.sValue); ++ EXPECT_EQ(L"", result.sChangeEx); ++ } ++ { ++ CFFL_FieldAction result; ++ GetCFFLFormField()->GetActionData(GetPageView(), CPDF_AAction::kGetFocus, ++ result); ++ EXPECT_EQ(L"Banana", result.sValue); ++ EXPECT_EQ(L"", result.sChangeEx); ++ } ++} ++ ++TEST_F(CFFLComboBoxEmbedderTest, SetActionData) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotNormal()); ++ CFFL_FieldAction input_fa; ++ input_fa.nSelStart = 2; ++ input_fa.nSelEnd = 4; ++ input_fa.sChange = L"Hamster"; ++ GetCFFLFormField()->SetActionData(GetPageView(), CPDF_AAction::kKeyStroke, ++ input_fa); ++ ++ CFFL_FieldAction output_fa; ++ GetCFFLFormField()->GetActionData(GetPageView(), CPDF_AAction::kKeyStroke, ++ output_fa); ++ EXPECT_EQ(L"BaHamsterna", output_fa.sValue); ++} +diff --git a/fpdfsdk/formfiller/cffl_fieldaction.cpp b/fpdfsdk/formfiller/cffl_fieldaction.cpp +new file mode 100644 +index 000000000..8ee28cbaf +--- /dev/null ++++ b/fpdfsdk/formfiller/cffl_fieldaction.cpp +@@ -0,0 +1,11 @@ ++// Copyright 2016 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "fpdfsdk/formfiller/cffl_fieldaction.h" ++ ++CFFL_FieldAction::CFFL_FieldAction() = default; ++ ++CFFL_FieldAction::~CFFL_FieldAction() = default; +diff --git a/fpdfsdk/cpdfsdk_fieldaction.h b/fpdfsdk/formfiller/cffl_fieldaction.h +similarity index 55% +rename from fpdfsdk/cpdfsdk_fieldaction.h +rename to fpdfsdk/formfiller/cffl_fieldaction.h +index e287911ce..0bd6824cf 100644 +--- a/fpdfsdk/cpdfsdk_fieldaction.h ++++ b/fpdfsdk/formfiller/cffl_fieldaction.h +@@ -1,18 +1,18 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#ifndef FPDFSDK_CPDFSDK_FIELDACTION_H_ +-#define FPDFSDK_CPDFSDK_FIELDACTION_H_ ++#ifndef FPDFSDK_FORMFILLER_CFFL_FIELDACTION_H_ ++#define FPDFSDK_FORMFILLER_CFFL_FIELDACTION_H_ + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" + +-struct CPDFSDK_FieldAction { +- CPDFSDK_FieldAction(); +- CPDFSDK_FieldAction(const CPDFSDK_FieldAction& other) = delete; +- ~CPDFSDK_FieldAction(); ++struct CFFL_FieldAction { ++ CFFL_FieldAction(); ++ CFFL_FieldAction(const CFFL_FieldAction& other) = delete; ++ ~CFFL_FieldAction(); + + bool bModifier = false; + bool bShift = false; +@@ -27,4 +27,4 @@ struct CPDFSDK_FieldAction { + WideString sValue; + }; + +-#endif // FPDFSDK_CPDFSDK_FIELDACTION_H_ ++#endif // FPDFSDK_FORMFILLER_CFFL_FIELDACTION_H_ +diff --git a/fpdfsdk/formfiller/cffl_formfield.cpp b/fpdfsdk/formfiller/cffl_formfield.cpp +new file mode 100644 +index 000000000..88fb16a8c +--- /dev/null ++++ b/fpdfsdk/formfiller/cffl_formfield.cpp +@@ -0,0 +1,590 @@ ++// Copyright 2014 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "fpdfsdk/formfiller/cffl_formfield.h" ++ ++#include ++ ++#include "constants/form_flags.h" ++#include "core/fpdfapi/page/cpdf_page.h" ++#include "core/fxge/cfx_renderdevice.h" ++#include "fpdfsdk/cpdfsdk_pageview.h" ++#include "fpdfsdk/cpdfsdk_widget.h" ++#include "fpdfsdk/formfiller/cffl_perwindowdata.h" ++#include "third_party/base/check.h" ++ ++CFFL_FormField::CFFL_FormField(CFFL_InteractiveFormFiller* pFormFiller, ++ CPDFSDK_Widget* pWidget) ++ : m_pFormFiller(pFormFiller), m_pWidget(pWidget) { ++ DCHECK(m_pFormFiller); ++} ++ ++CFFL_FormField::~CFFL_FormField() { ++ DestroyWindows(); ++} ++ ++void CFFL_FormField::DestroyWindows() { ++ while (!m_Maps.empty()) { ++ auto it = m_Maps.begin(); ++ std::unique_ptr pWnd = std::move(it->second); ++ m_Maps.erase(it); ++ pWnd->InvalidateProvider(this); ++ pWnd->Destroy(); ++ } ++} ++ ++FX_RECT CFFL_FormField::GetViewBBox(const CPDFSDK_PageView* pPageView) { ++ CPWL_Wnd* pWnd = GetPWLWindow(pPageView); ++ CFX_FloatRect rcAnnot = ++ pWnd ? PWLtoFFL(pWnd->GetWindowRect()) : m_pWidget->GetRect(); ++ CFX_FloatRect rcFocus = GetFocusBox(pPageView); ++ ++ CFX_FloatRect rcWin = rcAnnot; ++ if (!rcFocus.IsEmpty()) ++ rcWin.Union(rcFocus); ++ if (!rcWin.IsEmpty()) { ++ rcWin.Inflate(1, 1); ++ rcWin.Normalize(); ++ } ++ ++ return rcWin.GetOuterRect(); ++} ++ ++void CFFL_FormField::OnDraw(CPDFSDK_PageView* pPageView, ++ CPDFSDK_Widget* pWidget, ++ CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device) { ++ CPWL_Wnd* pWnd = GetPWLWindow(pPageView); ++ if (pWnd) { ++ pWnd->DrawAppearance(pDevice, GetCurMatrix() * mtUser2Device); ++ return; ++ } ++ if (!CFFL_InteractiveFormFiller::IsVisible(pWidget)) ++ return; ++ ++ pWidget->DrawAppearance(pDevice, mtUser2Device, ++ CPDF_Annot::AppearanceMode::kNormal); ++} ++ ++void CFFL_FormField::OnDrawDeactive(CPDFSDK_PageView* pPageView, ++ CPDFSDK_Widget* pWidget, ++ CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device) { ++ pWidget->DrawAppearance(pDevice, mtUser2Device, ++ CPDF_Annot::AppearanceMode::kNormal); ++} ++ ++void CFFL_FormField::OnMouseEnter(CPDFSDK_PageView* pPageView) {} ++ ++void CFFL_FormField::OnMouseExit(CPDFSDK_PageView* pPageView) { ++ m_pTimer.reset(); ++ DCHECK(m_pWidget); ++} ++ ++bool CFFL_FormField::OnLButtonDown(CPDFSDK_PageView* pPageView, ++ CPDFSDK_Widget* pWidget, ++ Mask nFlags, ++ const CFX_PointF& point) { ++ CPWL_Wnd* pWnd = CreateOrUpdatePWLWindow(pPageView); ++ if (!pWnd) ++ return false; ++ ++ m_bValid = true; ++ FX_RECT rect = GetViewBBox(pPageView); ++ InvalidateRect(rect); ++ if (!rect.Contains(static_cast(point.x), static_cast(point.y))) ++ return false; ++ return pWnd->OnLButtonDown(nFlags, FFLtoPWL(point)); ++} ++ ++bool CFFL_FormField::OnLButtonUp(CPDFSDK_PageView* pPageView, ++ CPDFSDK_Widget* pWidget, ++ Mask nFlags, ++ const CFX_PointF& point) { ++ CPWL_Wnd* pWnd = GetPWLWindow(pPageView); ++ if (!pWnd) ++ return false; ++ ++ InvalidateRect(GetViewBBox(pPageView)); ++ pWnd->OnLButtonUp(nFlags, FFLtoPWL(point)); ++ return true; ++} ++ ++bool CFFL_FormField::OnLButtonDblClk(CPDFSDK_PageView* pPageView, ++ Mask nFlags, ++ const CFX_PointF& point) { ++ CPWL_Wnd* pWnd = GetPWLWindow(pPageView); ++ if (!pWnd) ++ return false; ++ ++ pWnd->OnLButtonDblClk(nFlags, FFLtoPWL(point)); ++ return true; ++} ++ ++bool CFFL_FormField::OnMouseMove(CPDFSDK_PageView* pPageView, ++ Mask nFlags, ++ const CFX_PointF& point) { ++ CPWL_Wnd* pWnd = GetPWLWindow(pPageView); ++ if (!pWnd) ++ return false; ++ ++ pWnd->OnMouseMove(nFlags, FFLtoPWL(point)); ++ return true; ++} ++ ++bool CFFL_FormField::OnMouseWheel(CPDFSDK_PageView* pPageView, ++ Mask nFlags, ++ const CFX_PointF& point, ++ const CFX_Vector& delta) { ++ if (!IsValid()) ++ return false; ++ ++ CPWL_Wnd* pWnd = CreateOrUpdatePWLWindow(pPageView); ++ return pWnd && pWnd->OnMouseWheel(nFlags, FFLtoPWL(point), delta); ++} ++ ++bool CFFL_FormField::OnRButtonDown(CPDFSDK_PageView* pPageView, ++ Mask nFlags, ++ const CFX_PointF& point) { ++ CPWL_Wnd* pWnd = CreateOrUpdatePWLWindow(pPageView); ++ return pWnd && pWnd->OnRButtonDown(nFlags, FFLtoPWL(point)); ++} ++ ++bool CFFL_FormField::OnRButtonUp(CPDFSDK_PageView* pPageView, ++ Mask nFlags, ++ const CFX_PointF& point) { ++ CPWL_Wnd* pWnd = GetPWLWindow(pPageView); ++ return pWnd && pWnd->OnRButtonUp(nFlags, FFLtoPWL(point)); ++} ++ ++bool CFFL_FormField::OnKeyDown(FWL_VKEYCODE nKeyCode, ++ Mask nFlags) { ++ if (!IsValid()) ++ return false; ++ ++ CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView()); ++ return pWnd && pWnd->OnKeyDown(nKeyCode, nFlags); ++} ++ ++bool CFFL_FormField::OnChar(CPDFSDK_Widget* pWidget, ++ uint32_t nChar, ++ Mask nFlags) { ++ if (!IsValid()) ++ return false; ++ ++ CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView()); ++ return pWnd && pWnd->OnChar(nChar, nFlags); ++} ++ ++bool CFFL_FormField::SetIndexSelected(int index, bool selected) { ++ return false; ++} ++ ++bool CFFL_FormField::IsIndexSelected(int index) { ++ return false; ++} ++ ++WideString CFFL_FormField::GetText() { ++ if (!IsValid()) ++ return WideString(); ++ ++ CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView()); ++ return pWnd ? pWnd->GetText() : WideString(); ++} ++ ++WideString CFFL_FormField::GetSelectedText() { ++ if (!IsValid()) ++ return WideString(); ++ ++ CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView()); ++ return pWnd ? pWnd->GetSelectedText() : WideString(); ++} ++ ++void CFFL_FormField::ReplaceAndKeepSelection(const WideString& text) { ++ if (!IsValid()) ++ return; ++ ++ CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView()); ++ if (!pWnd) ++ return; ++ ++ pWnd->ReplaceAndKeepSelection(text); ++} ++ ++void CFFL_FormField::ReplaceSelection(const WideString& text) { ++ if (!IsValid()) ++ return; ++ ++ CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView()); ++ if (!pWnd) ++ return; ++ ++ pWnd->ReplaceSelection(text); ++} ++ ++bool CFFL_FormField::SelectAllText() { ++ if (!IsValid()) ++ return false; ++ ++ CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView()); ++ return pWnd && pWnd->SelectAllText(); ++} ++ ++bool CFFL_FormField::CanUndo() { ++ if (!IsValid()) ++ return false; ++ ++ CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView()); ++ return pWnd && pWnd->CanUndo(); ++} ++ ++bool CFFL_FormField::CanRedo() { ++ if (!IsValid()) ++ return false; ++ ++ CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView()); ++ return pWnd && pWnd->CanRedo(); ++} ++ ++bool CFFL_FormField::Undo() { ++ if (!IsValid()) ++ return false; ++ ++ CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView()); ++ return pWnd && pWnd->Undo(); ++} ++ ++bool CFFL_FormField::Redo() { ++ if (!IsValid()) ++ return false; ++ ++ CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView()); ++ return pWnd && pWnd->Redo(); ++} ++ ++void CFFL_FormField::SetFocusForAnnot(CPDFSDK_Widget* pWidget, ++ Mask nFlag) { ++ CPDFSDK_PageView* pPageView = ++ m_pFormFiller->GetOrCreatePageView(pWidget->GetPage()); ++ CPWL_Wnd* pWnd = CreateOrUpdatePWLWindow(pPageView); ++ if (pWnd) ++ pWnd->SetFocus(); ++ ++ m_bValid = true; ++ InvalidateRect(GetViewBBox(pPageView)); ++} ++ ++void CFFL_FormField::KillFocusForAnnot(Mask nFlag) { ++ if (!IsValid()) ++ return; ++ ++ CPDFSDK_PageView* pPageView = ++ m_pFormFiller->GetPageView(m_pWidget->GetPage()); ++ if (!pPageView || !CommitData(pPageView, nFlag)) ++ return; ++ if (CPWL_Wnd* pWnd = GetPWLWindow(pPageView)) ++ pWnd->KillFocus(); ++ ++ bool bDestroyPWLWindow; ++ switch (m_pWidget->GetFieldType()) { ++ case FormFieldType::kPushButton: ++ case FormFieldType::kCheckBox: ++ case FormFieldType::kRadioButton: ++ bDestroyPWLWindow = true; ++ break; ++ default: ++ bDestroyPWLWindow = false; ++ break; ++ } ++ EscapeFiller(pPageView, bDestroyPWLWindow); ++} ++ ++bool CFFL_FormField::IsValid() const { ++ return m_bValid; ++} ++ ++CPWL_Wnd::CreateParams CFFL_FormField::GetCreateParam() { ++ CPWL_Wnd::CreateParams cp(m_pFormFiller->GetTimerHandler(), m_pFormFiller, ++ this); ++ ++ cp.rcRectWnd = GetPDFAnnotRect(); ++ ++ uint32_t dwCreateFlags = PWS_BORDER | PWS_BACKGROUND | PWS_VISIBLE; ++ uint32_t dwFieldFlag = m_pWidget->GetFieldFlags(); ++ if (dwFieldFlag & pdfium::form_flags::kReadOnly) ++ dwCreateFlags |= PWS_READONLY; ++ ++ absl::optional color = m_pWidget->GetFillColor(); ++ if (color.has_value()) ++ cp.sBackgroundColor = CFX_Color(color.value()); ++ color = m_pWidget->GetBorderColor(); ++ if (color.has_value()) ++ cp.sBorderColor = CFX_Color(color.value()); ++ ++ cp.sTextColor = CFX_Color(CFX_Color::Type::kGray, 0); ++ ++ color = m_pWidget->GetTextColor(); ++ if (color.has_value()) ++ cp.sTextColor = CFX_Color(color.value()); ++ ++ cp.fFontSize = m_pWidget->GetFontSize(); ++ cp.dwBorderWidth = m_pWidget->GetBorderWidth(); ++ ++ cp.nBorderStyle = m_pWidget->GetBorderStyle(); ++ switch (cp.nBorderStyle) { ++ case BorderStyle::kDash: ++ cp.sDash = CPWL_Dash(3, 3, 0); ++ break; ++ case BorderStyle::kBeveled: ++ case BorderStyle::kInset: ++ cp.dwBorderWidth *= 2; ++ break; ++ default: ++ break; ++ } ++ ++ if (cp.fFontSize <= 0) ++ dwCreateFlags |= PWS_AUTOFONTSIZE; ++ ++ cp.dwFlags = dwCreateFlags; ++ return cp; ++} ++ ++CPWL_Wnd* CFFL_FormField::GetPWLWindow( ++ const CPDFSDK_PageView* pPageView) const { ++ DCHECK(pPageView); ++ auto it = m_Maps.find(pPageView); ++ return it != m_Maps.end() ? it->second.get() : nullptr; ++} ++ ++CPWL_Wnd* CFFL_FormField::CreateOrUpdatePWLWindow( ++ const CPDFSDK_PageView* pPageView) { ++ DCHECK(pPageView); ++ CPWL_Wnd* pWnd = GetPWLWindow(pPageView); ++ if (!pWnd) { ++ CPWL_Wnd::CreateParams cp = GetCreateParam(); ++ // TODO(tsepez): maybe pass widget's value age as 4th arg. ++ auto pPrivateData = std::make_unique( ++ m_pWidget, pPageView, m_pWidget->GetAppearanceAge(), 0); ++ m_Maps[pPageView] = NewPWLWindow(cp, std::move(pPrivateData)); ++ return m_Maps[pPageView].get(); ++ } ++ const auto* pPrivateData = ++ static_cast(pWnd->GetAttachedData()); ++ if (pPrivateData->AppearanceAgeEquals(m_pWidget->GetAppearanceAge())) ++ return pWnd; ++ ++ return ResetPWLWindowForValueAgeInternal(pPageView, m_pWidget, ++ pPrivateData->GetValueAge()); ++} ++ ++void CFFL_FormField::DestroyPWLWindow(const CPDFSDK_PageView* pPageView) { ++ auto it = m_Maps.find(pPageView); ++ if (it == m_Maps.end()) ++ return; ++ ++ std::unique_ptr pWnd = std::move(it->second); ++ m_Maps.erase(it); ++ pWnd->Destroy(); ++} ++ ++CFX_Matrix CFFL_FormField::GetWindowMatrix( ++ const IPWL_FillerNotify::PerWindowData* pAttached) { ++ const auto* pPrivateData = static_cast(pAttached); ++ if (!pPrivateData) ++ return CFX_Matrix(); ++ ++ const CPDFSDK_PageView* pPageView = pPrivateData->GetPageView(); ++ if (!pPageView) ++ return CFX_Matrix(); ++ ++ return GetCurMatrix() * pPageView->GetCurrentMatrix(); ++} ++ ++void CFFL_FormField::OnSetFocusForEdit(CPWL_Edit* pEdit) { ++ // Only sub-classes might have a subordinate edit to focus. ++} ++ ++CFX_Matrix CFFL_FormField::GetCurMatrix() { ++ CFX_Matrix mt; ++ CFX_FloatRect rcDA = m_pWidget->GetPDFAnnot()->GetRect(); ++ switch (m_pWidget->GetRotate()) { ++ case 90: ++ mt = CFX_Matrix(0, 1, -1, 0, rcDA.right - rcDA.left, 0); ++ break; ++ case 180: ++ mt = CFX_Matrix(-1, 0, 0, -1, rcDA.right - rcDA.left, ++ rcDA.top - rcDA.bottom); ++ break; ++ case 270: ++ mt = CFX_Matrix(0, -1, 1, 0, 0, rcDA.top - rcDA.bottom); ++ break; ++ case 0: ++ default: ++ break; ++ } ++ mt.e += rcDA.left; ++ mt.f += rcDA.bottom; ++ ++ return mt; ++} ++ ++CFX_FloatRect CFFL_FormField::GetPDFAnnotRect() const { ++ CFX_FloatRect rectAnnot = m_pWidget->GetPDFAnnot()->GetRect(); ++ float fWidth = rectAnnot.Width(); ++ float fHeight = rectAnnot.Height(); ++ if ((m_pWidget->GetRotate() / 90) & 0x01) ++ std::swap(fWidth, fHeight); ++ return CFX_FloatRect(0, 0, fWidth, fHeight); ++} ++ ++CPDFSDK_PageView* CFFL_FormField::GetCurPageView() { ++ return m_pFormFiller->GetOrCreatePageView(m_pWidget->GetPage()); ++} ++ ++CFX_FloatRect CFFL_FormField::GetFocusBox(const CPDFSDK_PageView* pPageView) { ++ CPWL_Wnd* pWnd = GetPWLWindow(pPageView); ++ if (!pWnd) ++ return CFX_FloatRect(); ++ ++ CFX_FloatRect rcFocus = PWLtoFFL(pWnd->GetFocusRect()); ++ return pPageView->GetPDFPage()->GetBBox().Contains(rcFocus) ? rcFocus ++ : CFX_FloatRect(); ++} ++ ++CFX_FloatRect CFFL_FormField::FFLtoPWL(const CFX_FloatRect& rect) { ++ return GetCurMatrix().GetInverse().TransformRect(rect); ++} ++ ++CFX_FloatRect CFFL_FormField::PWLtoFFL(const CFX_FloatRect& rect) { ++ return GetCurMatrix().TransformRect(rect); ++} ++ ++CFX_PointF CFFL_FormField::FFLtoPWL(const CFX_PointF& point) { ++ return GetCurMatrix().GetInverse().Transform(point); ++} ++ ++CFX_PointF CFFL_FormField::PWLtoFFL(const CFX_PointF& point) { ++ return GetCurMatrix().Transform(point); ++} ++ ++bool CFFL_FormField::CommitData(const CPDFSDK_PageView* pPageView, ++ Mask nFlag) { ++ if (!IsDataChanged(pPageView)) ++ return true; ++ ++ ObservedPtr pObserved(m_pWidget); ++ if (!m_pFormFiller->OnKeyStrokeCommit(pObserved, pPageView, nFlag)) { ++ if (!pObserved) ++ return false; ++ ResetPWLWindow(pPageView); ++ return true; ++ } ++ if (!pObserved) ++ return false; ++ ++ if (!m_pFormFiller->OnValidate(pObserved, pPageView, nFlag)) { ++ if (!pObserved) ++ return false; ++ ResetPWLWindow(pPageView); ++ return true; ++ } ++ if (!pObserved) ++ return false; ++ ++ SaveData(pPageView); // may invoking JS to delete this widget. ++ if (!pObserved) ++ return false; ++ ++ m_pFormFiller->OnCalculate(pObserved); ++ if (!pObserved) ++ return false; ++ ++ m_pFormFiller->OnFormat(pObserved); ++ if (!pObserved) ++ return false; ++ ++ return true; ++} ++ ++bool CFFL_FormField::IsDataChanged(const CPDFSDK_PageView* pPageView) { ++ return false; ++} ++ ++void CFFL_FormField::SaveData(const CPDFSDK_PageView* pPageView) {} ++ ++#ifdef PDF_ENABLE_XFA ++bool CFFL_FormField::IsFieldFull(const CPDFSDK_PageView* pPageView) { ++ return false; ++} ++#endif // PDF_ENABLE_XFA ++ ++void CFFL_FormField::SetChangeMark() { ++ m_pFormFiller->OnChange(); ++} ++ ++void CFFL_FormField::GetActionData(const CPDFSDK_PageView* pPageView, ++ CPDF_AAction::AActionType type, ++ CFFL_FieldAction& fa) { ++ fa.sValue = m_pWidget->GetValue(); ++} ++ ++void CFFL_FormField::SetActionData(const CPDFSDK_PageView* pPageView, ++ CPDF_AAction::AActionType type, ++ const CFFL_FieldAction& fa) {} ++ ++void CFFL_FormField::SavePWLWindowState(const CPDFSDK_PageView* pPageView) {} ++ ++void CFFL_FormField::RecreatePWLWindowFromSavedState( ++ const CPDFSDK_PageView* pPageView) {} ++ ++CFFL_PerWindowData* CFFL_FormField::GetPerPWLWindowData( ++ const CPDFSDK_PageView* pPageView) { ++ CPWL_Wnd* pWnd = GetPWLWindow(pPageView); ++ if (!pWnd) ++ return nullptr; ++ ++ return static_cast(pWnd->GetAttachedData()); ++} ++ ++void CFFL_FormField::ResetPWLWindowForValueAge( ++ const CPDFSDK_PageView* pPageView, ++ CPDFSDK_Widget* pWidget, ++ uint32_t nValueAge) { ++ // Don't leak PWL_Wnd result to public callers. ++ ResetPWLWindowForValueAgeInternal(pPageView, pWidget, nValueAge); ++} ++ ++CPWL_Wnd* CFFL_FormField::ResetPWLWindowForValueAgeInternal( ++ const CPDFSDK_PageView* pPageView, ++ CPDFSDK_Widget* pWidget, ++ uint32_t nValueAge) { ++ return nValueAge == pWidget->GetValueAge() ? RestorePWLWindow(pPageView) ++ : ResetPWLWindow(pPageView); ++} ++ ++CPWL_Wnd* CFFL_FormField::ResetPWLWindow(const CPDFSDK_PageView* pPageView) { ++ return GetPWLWindow(pPageView); ++} ++ ++CPWL_Wnd* CFFL_FormField::RestorePWLWindow(const CPDFSDK_PageView* pPageView) { ++ return GetPWLWindow(pPageView); ++} ++ ++void CFFL_FormField::OnTimerFired() {} ++ ++void CFFL_FormField::EscapeFiller(CPDFSDK_PageView* pPageView, ++ bool bDestroyPWLWindow) { ++ m_bValid = false; ++ ++ InvalidateRect(GetViewBBox(pPageView)); ++ if (bDestroyPWLWindow) ++ DestroyPWLWindow(pPageView); ++} ++ ++void CFFL_FormField::InvalidateRect(const FX_RECT& rect) { ++ m_pFormFiller->Invalidate(m_pWidget->GetPage(), rect); ++} +diff --git a/fpdfsdk/formfiller/cffl_formfield.h b/fpdfsdk/formfiller/cffl_formfield.h +new file mode 100644 +index 000000000..1ddf73020 +--- /dev/null ++++ b/fpdfsdk/formfiller/cffl_formfield.h +@@ -0,0 +1,174 @@ ++// Copyright 2014 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef FPDFSDK_FORMFILLER_CFFL_FORMFIELD_H_ ++#define FPDFSDK_FORMFILLER_CFFL_FORMFIELD_H_ ++ ++#include ++#include ++ ++#include "core/fpdfdoc/cpdf_aaction.h" ++#include "core/fxcrt/cfx_timer.h" ++#include "core/fxcrt/mask.h" ++#include "core/fxcrt/unowned_ptr.h" ++#include "fpdfsdk/formfiller/cffl_fieldaction.h" ++#include "fpdfsdk/formfiller/cffl_interactiveformfiller.h" ++#include "fpdfsdk/pwl/cpwl_wnd.h" ++#include "fpdfsdk/pwl/ipwl_fillernotify.h" ++ ++class CFFL_PerWindowData; ++class CPDFSDK_PageView; ++class CPDFSDK_Widget; ++ ++class CFFL_FormField : public CPWL_Wnd::ProviderIface, ++ public CFX_Timer::CallbackIface { ++ public: ++ CFFL_FormField(CFFL_InteractiveFormFiller* pFormFiller, ++ CPDFSDK_Widget* pWidget); ++ ~CFFL_FormField() override; ++ ++ virtual void OnDraw(CPDFSDK_PageView* pPageView, ++ CPDFSDK_Widget* pWidget, ++ CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device); ++ virtual void OnDrawDeactive(CPDFSDK_PageView* pPageView, ++ CPDFSDK_Widget* pWidget, ++ CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device); ++ ++ virtual void OnMouseEnter(CPDFSDK_PageView* pPageView); ++ virtual void OnMouseExit(CPDFSDK_PageView* pPageView); ++ ++ virtual bool OnLButtonDown(CPDFSDK_PageView* pPageView, ++ CPDFSDK_Widget* pAnnot, ++ Mask nFlags, ++ const CFX_PointF& point); ++ virtual bool OnLButtonUp(CPDFSDK_PageView* pPageView, ++ CPDFSDK_Widget* pAnnot, ++ Mask nFlags, ++ const CFX_PointF& point); ++ virtual bool OnLButtonDblClk(CPDFSDK_PageView* pPageView, ++ Mask nFlags, ++ const CFX_PointF& point); ++ virtual bool OnMouseMove(CPDFSDK_PageView* pPageView, ++ Mask nFlags, ++ const CFX_PointF& point); ++ virtual bool OnMouseWheel(CPDFSDK_PageView* pPageView, ++ Mask nFlags, ++ const CFX_PointF& point, ++ const CFX_Vector& delta); ++ virtual bool OnRButtonDown(CPDFSDK_PageView* pPageView, ++ Mask nFlags, ++ const CFX_PointF& point); ++ virtual bool OnRButtonUp(CPDFSDK_PageView* pPageView, ++ Mask nFlags, ++ const CFX_PointF& point); ++ ++ virtual bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask nFlags); ++ virtual bool OnChar(CPDFSDK_Widget* pAnnot, ++ uint32_t nChar, ++ Mask nFlags); ++ virtual bool SetIndexSelected(int index, bool selected); ++ virtual bool IsIndexSelected(int index); ++ ++ FX_RECT GetViewBBox(const CPDFSDK_PageView* pPageView); ++ ++ WideString GetText(); ++ WideString GetSelectedText(); ++ void ReplaceAndKeepSelection(const WideString& text); ++ void ReplaceSelection(const WideString& text); ++ bool SelectAllText(); ++ ++ bool CanUndo(); ++ bool CanRedo(); ++ bool Undo(); ++ bool Redo(); ++ ++ void SetFocusForAnnot(CPDFSDK_Widget* pWidget, Mask nFlag); ++ void KillFocusForAnnot(Mask nFlag); ++ ++ // CFX_Timer::CallbackIface: ++ void OnTimerFired() override; ++ ++ // CPWL_Wnd::ProviderIface: ++ CFX_Matrix GetWindowMatrix( ++ const IPWL_FillerNotify::PerWindowData* pAttached) override; ++ void OnSetFocusForEdit(CPWL_Edit* pEdit) override; ++ ++ virtual void GetActionData(const CPDFSDK_PageView* pPageView, ++ CPDF_AAction::AActionType type, ++ CFFL_FieldAction& fa); ++ virtual void SetActionData(const CPDFSDK_PageView* pPageView, ++ CPDF_AAction::AActionType type, ++ const CFFL_FieldAction& fa); ++ virtual CPWL_Wnd::CreateParams GetCreateParam(); ++ virtual std::unique_ptr NewPWLWindow( ++ const CPWL_Wnd::CreateParams& cp, ++ std::unique_ptr pAttachedData) = 0; ++ virtual void SavePWLWindowState(const CPDFSDK_PageView* pPageView); ++ virtual void RecreatePWLWindowFromSavedState( ++ const CPDFSDK_PageView* pPageView); ++ virtual bool IsDataChanged(const CPDFSDK_PageView* pPageView); ++ virtual void SaveData(const CPDFSDK_PageView* pPageView); ++#ifdef PDF_ENABLE_XFA ++ virtual bool IsFieldFull(const CPDFSDK_PageView* pPageView); ++#endif // PDF_ENABLE_XFA ++ ++ CFX_Matrix GetCurMatrix(); ++ CFX_FloatRect GetFocusBox(const CPDFSDK_PageView* pPageView); ++ CFX_FloatRect FFLtoPWL(const CFX_FloatRect& rect); ++ CFX_FloatRect PWLtoFFL(const CFX_FloatRect& rect); ++ CFX_PointF FFLtoPWL(const CFX_PointF& point); ++ CFX_PointF PWLtoFFL(const CFX_PointF& point); ++ bool CommitData(const CPDFSDK_PageView* pPageView, Mask nFlag); ++ void DestroyPWLWindow(const CPDFSDK_PageView* pPageView); ++ void EscapeFiller(CPDFSDK_PageView* pPageView, bool bDestroyPWLWindow); ++ ++ bool IsValid() const; ++ CFX_FloatRect GetPDFAnnotRect() const; ++ ++ CPDFSDK_PageView* GetCurPageView(); ++ void SetChangeMark(); ++ ++ CPDFSDK_Widget* GetSDKWidget() const { return m_pWidget; } ++ ++ CFFL_PerWindowData* GetPerPWLWindowData(const CPDFSDK_PageView* pPageView); ++ void ResetPWLWindowForValueAge(const CPDFSDK_PageView* pPageView, ++ CPDFSDK_Widget* pWidget, ++ uint32_t nValueAge); ++ ++ protected: ++ friend class CPWLComboBoxEmbedderTest; ++ friend class CPWLEditEmbedderTest; ++ friend class CPWLSpecialButtonEmbedderTest; ++ ++ virtual CPWL_Wnd* ResetPWLWindow(const CPDFSDK_PageView* pPageView); ++ virtual CPWL_Wnd* RestorePWLWindow(const CPDFSDK_PageView* pPageView); ++ ++ CPWL_Wnd* GetPWLWindow(const CPDFSDK_PageView* pPageView) const; ++ CPWL_Wnd* CreateOrUpdatePWLWindow(const CPDFSDK_PageView* pPageView); ++ CPWL_Wnd* ResetPWLWindowForValueAgeInternal(const CPDFSDK_PageView* pPageView, ++ CPDFSDK_Widget* pWidget, ++ uint32_t nValueAge); ++ ++ // If the inheriting widget has its own fontmap and a PWL_Edit widget that ++ // access that fontmap then you have to call DestroyWindows before destroying ++ // the font map in order to not get a use-after-free. ++ // ++ // The font map should be stored somewhere more appropriate so it will live ++ // until the PWL_Edit is done with it. pdfium:566 ++ void DestroyWindows(); ++ ++ void InvalidateRect(const FX_RECT& rect); ++ ++ bool m_bValid = false; ++ UnownedPtr const m_pFormFiller; ++ UnownedPtr m_pWidget; ++ std::unique_ptr m_pTimer; ++ std::map> m_Maps; ++}; ++ ++#endif // FPDFSDK_FORMFILLER_CFFL_FORMFIELD_H_ +diff --git a/fpdfsdk/formfiller/cffl_formfiller.cpp b/fpdfsdk/formfiller/cffl_formfiller.cpp +deleted file mode 100644 +index a214920bd..000000000 +--- a/fpdfsdk/formfiller/cffl_formfiller.cpp ++++ /dev/null +@@ -1,547 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "fpdfsdk/formfiller/cffl_formfiller.h" +- +-#include +- +-#include "constants/form_flags.h" +-#include "core/fpdfapi/page/cpdf_page.h" +-#include "core/fxge/cfx_renderdevice.h" +-#include "fpdfsdk/cpdfsdk_formfillenvironment.h" +-#include "fpdfsdk/cpdfsdk_pageview.h" +-#include "fpdfsdk/cpdfsdk_widget.h" +- +-CFFL_FormFiller::CFFL_FormFiller(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- CPDFSDK_Widget* pWidget) +- : m_pFormFillEnv(pFormFillEnv), m_pWidget(pWidget) { +- ASSERT(m_pFormFillEnv); +-} +- +-CFFL_FormFiller::~CFFL_FormFiller() { +- DestroyWindows(); +-} +- +-void CFFL_FormFiller::DestroyWindows() { +- while (!m_Maps.empty()) { +- auto it = m_Maps.begin(); +- std::unique_ptr pWnd = std::move(it->second); +- m_Maps.erase(it); +- pWnd->InvalidateProvider(this); +- pWnd->Destroy(); +- } +-} +- +-FX_RECT CFFL_FormFiller::GetViewBBox(CPDFSDK_PageView* pPageView) { +- CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false); +- CFX_FloatRect rcAnnot = +- pWnd ? PWLtoFFL(pWnd->GetWindowRect()) : m_pWidget->GetRect(); +- CFX_FloatRect rcFocus = GetFocusBox(pPageView); +- +- CFX_FloatRect rcWin = rcAnnot; +- if (!rcFocus.IsEmpty()) +- rcWin.Union(rcFocus); +- if (!rcWin.IsEmpty()) { +- rcWin.Inflate(1, 1); +- rcWin.Normalize(); +- } +- +- return rcWin.GetOuterRect(); +-} +- +-void CFFL_FormFiller::OnDraw(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device) { +- CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false); +- if (pWnd) { +- pWnd->DrawAppearance(pDevice, GetCurMatrix() * mtUser2Device); +- return; +- } +- +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot); +- if (!CFFL_InteractiveFormFiller::IsVisible(pWidget)) +- return; +- +- pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Normal, nullptr); +-} +- +-void CFFL_FormFiller::OnDrawDeactive(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device) { +- ToCPDFSDKWidget(pAnnot)->DrawAppearance(pDevice, mtUser2Device, +- CPDF_Annot::Normal, nullptr); +-} +- +-void CFFL_FormFiller::OnMouseEnter(CPDFSDK_PageView* pPageView) {} +- +-void CFFL_FormFiller::OnMouseExit(CPDFSDK_PageView* pPageView) { +- m_pTimer.reset(); +- ASSERT(m_pWidget); +-} +- +-bool CFFL_FormFiller::OnLButtonDown(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- CPWL_Wnd* pWnd = GetPWLWindow(pPageView, true); +- if (!pWnd) +- return false; +- +- m_bValid = true; +- FX_RECT rect = GetViewBBox(pPageView); +- InvalidateRect(rect); +- if (!rect.Contains(static_cast(point.x), static_cast(point.y))) +- return false; +- return pWnd->OnLButtonDown(WndtoPWL(point), nFlags); +-} +- +-bool CFFL_FormFiller::OnLButtonUp(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false); +- if (!pWnd) +- return false; +- +- InvalidateRect(GetViewBBox(pPageView)); +- pWnd->OnLButtonUp(WndtoPWL(point), nFlags); +- return true; +-} +- +-bool CFFL_FormFiller::OnLButtonDblClk(CPDFSDK_PageView* pPageView, +- uint32_t nFlags, +- const CFX_PointF& point) { +- CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false); +- if (!pWnd) +- return false; +- +- pWnd->OnLButtonDblClk(WndtoPWL(point), nFlags); +- return true; +-} +- +-bool CFFL_FormFiller::OnMouseMove(CPDFSDK_PageView* pPageView, +- uint32_t nFlags, +- const CFX_PointF& point) { +- CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false); +- if (!pWnd) +- return false; +- +- pWnd->OnMouseMove(WndtoPWL(point), nFlags); +- return true; +-} +- +-bool CFFL_FormFiller::OnMouseWheel(CPDFSDK_PageView* pPageView, +- uint32_t nFlags, +- short zDelta, +- const CFX_PointF& point) { +- if (!IsValid()) +- return false; +- +- CPWL_Wnd* pWnd = GetPWLWindow(pPageView, true); +- return pWnd && pWnd->OnMouseWheel(zDelta, WndtoPWL(point), nFlags); +-} +- +-bool CFFL_FormFiller::OnRButtonDown(CPDFSDK_PageView* pPageView, +- uint32_t nFlags, +- const CFX_PointF& point) { +- CPWL_Wnd* pWnd = GetPWLWindow(pPageView, true); +- return pWnd && pWnd->OnRButtonDown(WndtoPWL(point), nFlags); +-} +- +-bool CFFL_FormFiller::OnRButtonUp(CPDFSDK_PageView* pPageView, +- uint32_t nFlags, +- const CFX_PointF& point) { +- CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false); +- return pWnd && pWnd->OnRButtonUp(WndtoPWL(point), nFlags); +-} +- +-bool CFFL_FormFiller::OnKeyDown(uint32_t nKeyCode, uint32_t nFlags) { +- if (!IsValid()) +- return false; +- +- CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false); +- return pWnd && pWnd->OnKeyDown(nKeyCode, nFlags); +-} +- +-bool CFFL_FormFiller::OnChar(CPDFSDK_Annot* pAnnot, +- uint32_t nChar, +- uint32_t nFlags) { +- if (!IsValid()) +- return false; +- +- CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false); +- return pWnd && pWnd->OnChar(nChar, nFlags); +-} +- +-bool CFFL_FormFiller::SetIndexSelected(int index, bool selected) { +- return false; +-} +- +-bool CFFL_FormFiller::IsIndexSelected(int index) { +- return false; +-} +- +-WideString CFFL_FormFiller::GetText() { +- if (!IsValid()) +- return WideString(); +- +- CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false); +- return pWnd ? pWnd->GetText() : WideString(); +-} +- +-WideString CFFL_FormFiller::GetSelectedText() { +- if (!IsValid()) +- return WideString(); +- +- CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false); +- return pWnd ? pWnd->GetSelectedText() : WideString(); +-} +- +-void CFFL_FormFiller::ReplaceSelection(const WideString& text) { +- if (!IsValid()) +- return; +- +- CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false); +- if (!pWnd) +- return; +- +- pWnd->ReplaceSelection(text); +-} +- +-bool CFFL_FormFiller::CanUndo() { +- if (!IsValid()) +- return false; +- +- CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false); +- return pWnd && pWnd->CanUndo(); +-} +- +-bool CFFL_FormFiller::CanRedo() { +- if (!IsValid()) +- return false; +- +- CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false); +- return pWnd && pWnd->CanRedo(); +-} +- +-bool CFFL_FormFiller::Undo() { +- if (!IsValid()) +- return false; +- +- CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false); +- return pWnd && pWnd->Undo(); +-} +- +-bool CFFL_FormFiller::Redo() { +- if (!IsValid()) +- return false; +- +- CPWL_Wnd* pWnd = GetPWLWindow(GetCurPageView(true), false); +- return pWnd && pWnd->Redo(); +-} +- +-void CFFL_FormFiller::SetFocusForAnnot(CPDFSDK_Annot* pAnnot, uint32_t nFlag) { +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot); +- IPDF_Page* pPage = pWidget->GetPage(); +- CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(pPage, true); +- if (CPWL_Wnd* pWnd = GetPWLWindow(pPageView, true)) +- pWnd->SetFocus(); +- +- m_bValid = true; +- InvalidateRect(GetViewBBox(pPageView)); +-} +- +-void CFFL_FormFiller::KillFocusForAnnot(uint32_t nFlag) { +- if (!IsValid()) +- return; +- +- CPDFSDK_PageView* pPageView = GetCurPageView(false); +- if (!pPageView || !CommitData(pPageView, nFlag)) +- return; +- if (CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false)) +- pWnd->KillFocus(); +- +- bool bDestroyPWLWindow; +- switch (m_pWidget->GetFieldType()) { +- case FormFieldType::kPushButton: +- case FormFieldType::kCheckBox: +- case FormFieldType::kRadioButton: +- bDestroyPWLWindow = true; +- break; +- default: +- bDestroyPWLWindow = false; +- break; +- } +- EscapeFiller(pPageView, bDestroyPWLWindow); +-} +- +-bool CFFL_FormFiller::IsValid() const { +- return m_bValid; +-} +- +-CPWL_Wnd::CreateParams CFFL_FormFiller::GetCreateParam() { +- CPWL_Wnd::CreateParams cp; +- cp.pProvider.Reset(this); +- cp.rcRectWnd = GetPDFAnnotRect(); +- +- uint32_t dwCreateFlags = PWS_BORDER | PWS_BACKGROUND | PWS_VISIBLE; +- uint32_t dwFieldFlag = m_pWidget->GetFieldFlags(); +- if (dwFieldFlag & pdfium::form_flags::kReadOnly) +- dwCreateFlags |= PWS_READONLY; +- +- Optional color = m_pWidget->GetFillColor(); +- if (color.has_value()) +- cp.sBackgroundColor = CFX_Color(color.value()); +- color = m_pWidget->GetBorderColor(); +- if (color.has_value()) +- cp.sBorderColor = CFX_Color(color.value()); +- +- cp.sTextColor = CFX_Color(CFX_Color::kGray, 0); +- +- color = m_pWidget->GetTextColor(); +- if (color.has_value()) +- cp.sTextColor = CFX_Color(color.value()); +- +- cp.fFontSize = m_pWidget->GetFontSize(); +- cp.dwBorderWidth = m_pWidget->GetBorderWidth(); +- +- cp.nBorderStyle = m_pWidget->GetBorderStyle(); +- switch (cp.nBorderStyle) { +- case BorderStyle::DASH: +- cp.sDash = CPWL_Dash(3, 3, 0); +- break; +- case BorderStyle::BEVELED: +- case BorderStyle::INSET: +- cp.dwBorderWidth *= 2; +- break; +- default: +- break; +- } +- +- if (cp.fFontSize <= 0) +- dwCreateFlags |= PWS_AUTOFONTSIZE; +- +- cp.dwFlags = dwCreateFlags; +- cp.pTimerHandler = m_pFormFillEnv->GetTimerHandler(); +- cp.pSystemHandler = m_pFormFillEnv->GetSysHandler(); +- return cp; +-} +- +-CPWL_Wnd* CFFL_FormFiller::GetPWLWindow(CPDFSDK_PageView* pPageView, +- bool bNew) { +- ASSERT(pPageView); +- auto it = m_Maps.find(pPageView); +- if (it == m_Maps.end()) { +- if (!bNew) +- return nullptr; +- +- CPWL_Wnd::CreateParams cp = GetCreateParam(); +- auto pPrivateData = pdfium::MakeUnique(); +- pPrivateData->pWidget.Reset(m_pWidget.Get()); +- pPrivateData->pPageView = pPageView; +- pPrivateData->nWidgetAppearanceAge = m_pWidget->GetAppearanceAge(); +- pPrivateData->nWidgetValueAge = 0; +- m_Maps[pPageView] = NewPWLWindow(cp, std::move(pPrivateData)); +- return m_Maps[pPageView].get(); +- } +- +- CPWL_Wnd* pWnd = it->second.get(); +- if (!bNew) +- return pWnd; +- +- const auto* pPrivateData = +- static_cast(pWnd->GetAttachedData()); +- if (pPrivateData->nWidgetAppearanceAge == m_pWidget->GetAppearanceAge()) +- return pWnd; +- +- return ResetPWLWindow( +- pPageView, pPrivateData->nWidgetValueAge == m_pWidget->GetValueAge()); +-} +- +-void CFFL_FormFiller::DestroyPWLWindow(CPDFSDK_PageView* pPageView) { +- auto it = m_Maps.find(pPageView); +- if (it == m_Maps.end()) +- return; +- +- std::unique_ptr pWnd = std::move(it->second); +- m_Maps.erase(it); +- pWnd->Destroy(); +-} +- +-CFX_Matrix CFFL_FormFiller::GetWindowMatrix( +- const IPWL_SystemHandler::PerWindowData* pAttached) { +- const auto* pPrivateData = static_cast(pAttached); +- if (!pPrivateData || !pPrivateData->pPageView) +- return CFX_Matrix(); +- +- return GetCurMatrix() * pPrivateData->pPageView->GetCurrentMatrix(); +-} +- +-CFX_Matrix CFFL_FormFiller::GetCurMatrix() { +- CFX_Matrix mt; +- CFX_FloatRect rcDA = m_pWidget->GetPDFAnnot()->GetRect(); +- switch (m_pWidget->GetRotate()) { +- case 90: +- mt = CFX_Matrix(0, 1, -1, 0, rcDA.right - rcDA.left, 0); +- break; +- case 180: +- mt = CFX_Matrix(-1, 0, 0, -1, rcDA.right - rcDA.left, +- rcDA.top - rcDA.bottom); +- break; +- case 270: +- mt = CFX_Matrix(0, -1, 1, 0, 0, rcDA.top - rcDA.bottom); +- break; +- case 0: +- default: +- break; +- } +- mt.e += rcDA.left; +- mt.f += rcDA.bottom; +- +- return mt; +-} +- +-CFX_FloatRect CFFL_FormFiller::GetPDFAnnotRect() const { +- CFX_FloatRect rectAnnot = m_pWidget->GetPDFAnnot()->GetRect(); +- +- float fWidth = rectAnnot.Width(); +- float fHeight = rectAnnot.Height(); +- if ((m_pWidget->GetRotate() / 90) & 0x01) +- std::swap(fWidth, fHeight); +- return CFX_FloatRect(0, 0, fWidth, fHeight); +-} +- +-CPDFSDK_PageView* CFFL_FormFiller::GetCurPageView(bool renew) { +- IPDF_Page* pPage = m_pWidget->GetPage(); +- return m_pFormFillEnv->GetPageView(pPage, renew); +-} +- +-CFX_FloatRect CFFL_FormFiller::GetFocusBox(CPDFSDK_PageView* pPageView) { +- CPWL_Wnd* pWnd = GetPWLWindow(pPageView, false); +- if (!pWnd) +- return CFX_FloatRect(); +- +- CFX_FloatRect rcFocus = FFLtoWnd(PWLtoFFL(pWnd->GetFocusRect())); +- return pPageView->GetPDFPage()->GetBBox().Contains(rcFocus) ? rcFocus +- : CFX_FloatRect(); +-} +- +-CFX_FloatRect CFFL_FormFiller::FFLtoPWL(const CFX_FloatRect& rect) { +- return GetCurMatrix().GetInverse().TransformRect(rect); +-} +- +-CFX_FloatRect CFFL_FormFiller::PWLtoFFL(const CFX_FloatRect& rect) { +- return GetCurMatrix().TransformRect(rect); +-} +- +-CFX_PointF CFFL_FormFiller::FFLtoPWL(const CFX_PointF& point) { +- return GetCurMatrix().GetInverse().Transform(point); +-} +- +-CFX_PointF CFFL_FormFiller::PWLtoFFL(const CFX_PointF& point) { +- return GetCurMatrix().Transform(point); +-} +- +-CFX_PointF CFFL_FormFiller::WndtoPWL(const CFX_PointF& pt) { +- return FFLtoPWL(pt); +-} +- +-CFX_FloatRect CFFL_FormFiller::FFLtoWnd(const CFX_FloatRect& rect) { +- return rect; +-} +- +-bool CFFL_FormFiller::CommitData(CPDFSDK_PageView* pPageView, uint32_t nFlag) { +- if (!IsDataChanged(pPageView)) +- return true; +- +- CFFL_InteractiveFormFiller* pFormFiller = +- m_pFormFillEnv->GetInteractiveFormFiller(); +- ObservedPtr pObserved(m_pWidget.Get()); +- +- if (!pFormFiller->OnKeyStrokeCommit(&pObserved, pPageView, nFlag)) { +- if (!pObserved) +- return false; +- ResetPWLWindow(pPageView, false); +- return true; +- } +- if (!pObserved) +- return false; +- +- if (!pFormFiller->OnValidate(&pObserved, pPageView, nFlag)) { +- if (!pObserved) +- return false; +- ResetPWLWindow(pPageView, false); +- return true; +- } +- if (!pObserved) +- return false; +- +- SaveData(pPageView); // may invoking JS to delete this widget. +- if (!pObserved) +- return false; +- +- pFormFiller->OnCalculate(&pObserved, pPageView, nFlag); +- if (!pObserved) +- return false; +- +- pFormFiller->OnFormat(&pObserved, pPageView, nFlag); +- if (!pObserved) +- return false; +- +- return true; +-} +- +-bool CFFL_FormFiller::IsDataChanged(CPDFSDK_PageView* pPageView) { +- return false; +-} +- +-void CFFL_FormFiller::SaveData(CPDFSDK_PageView* pPageView) {} +- +-#ifdef PDF_ENABLE_XFA +-bool CFFL_FormFiller::IsFieldFull(CPDFSDK_PageView* pPageView) { +- return false; +-} +-#endif // PDF_ENABLE_XFA +- +-void CFFL_FormFiller::SetChangeMark() { +- m_pFormFillEnv->OnChange(); +-} +- +-void CFFL_FormFiller::GetActionData(CPDFSDK_PageView* pPageView, +- CPDF_AAction::AActionType type, +- CPDFSDK_FieldAction& fa) { +- fa.sValue = m_pWidget->GetValue(); +-} +- +-void CFFL_FormFiller::SetActionData(CPDFSDK_PageView* pPageView, +- CPDF_AAction::AActionType type, +- const CPDFSDK_FieldAction& fa) {} +- +-void CFFL_FormFiller::SaveState(CPDFSDK_PageView* pPageView) {} +- +-void CFFL_FormFiller::RestoreState(CPDFSDK_PageView* pPageView) {} +- +-CPWL_Wnd* CFFL_FormFiller::ResetPWLWindow(CPDFSDK_PageView* pPageView, +- bool bRestoreValue) { +- return GetPWLWindow(pPageView, false); +-} +- +-void CFFL_FormFiller::OnTimerFired() {} +- +-void CFFL_FormFiller::EscapeFiller(CPDFSDK_PageView* pPageView, +- bool bDestroyPWLWindow) { +- m_bValid = false; +- +- InvalidateRect(GetViewBBox(pPageView)); +- if (bDestroyPWLWindow) +- DestroyPWLWindow(pPageView); +-} +- +-void CFFL_FormFiller::InvalidateRect(const FX_RECT& rect) { +- m_pFormFillEnv->Invalidate(m_pWidget->GetPage(), rect); +-} +diff --git a/fpdfsdk/formfiller/cffl_formfiller.h b/fpdfsdk/formfiller/cffl_formfiller.h +deleted file mode 100644 +index 05bacafbb..000000000 +--- a/fpdfsdk/formfiller/cffl_formfiller.h ++++ /dev/null +@@ -1,157 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef FPDFSDK_FORMFILLER_CFFL_FORMFILLER_H_ +-#define FPDFSDK_FORMFILLER_CFFL_FORMFILLER_H_ +- +-#include +-#include +- +-#include "core/fxcrt/cfx_timer.h" +-#include "core/fxcrt/unowned_ptr.h" +-#include "fpdfsdk/cpdfsdk_fieldaction.h" +-#include "fpdfsdk/cpdfsdk_widget.h" +-#include "fpdfsdk/formfiller/cffl_interactiveformfiller.h" +-#include "fpdfsdk/pwl/cpwl_wnd.h" +-#include "fpdfsdk/pwl/ipwl_systemhandler.h" +- +-class CPDFSDK_Annot; +-class CPDFSDK_FormFillEnvironment; +-class CPDFSDK_PageView; +- +-class CFFL_FormFiller : public CPWL_Wnd::ProviderIface, +- public CFX_Timer::CallbackIface { +- public: +- CFFL_FormFiller(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- CPDFSDK_Widget* pWidget); +- ~CFFL_FormFiller() override; +- +- virtual void OnDraw(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device); +- virtual void OnDrawDeactive(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device); +- +- virtual void OnMouseEnter(CPDFSDK_PageView* pPageView); +- virtual void OnMouseExit(CPDFSDK_PageView* pPageView); +- +- virtual bool OnLButtonDown(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point); +- virtual bool OnLButtonUp(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point); +- virtual bool OnLButtonDblClk(CPDFSDK_PageView* pPageView, +- uint32_t nFlags, +- const CFX_PointF& point); +- virtual bool OnMouseMove(CPDFSDK_PageView* pPageView, +- uint32_t nFlags, +- const CFX_PointF& point); +- virtual bool OnMouseWheel(CPDFSDK_PageView* pPageView, +- uint32_t nFlags, +- short zDelta, +- const CFX_PointF& point); +- virtual bool OnRButtonDown(CPDFSDK_PageView* pPageView, +- uint32_t nFlags, +- const CFX_PointF& point); +- virtual bool OnRButtonUp(CPDFSDK_PageView* pPageView, +- uint32_t nFlags, +- const CFX_PointF& point); +- +- virtual bool OnKeyDown(uint32_t nKeyCode, uint32_t nFlags); +- virtual bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags); +- virtual bool SetIndexSelected(int index, bool selected); +- virtual bool IsIndexSelected(int index); +- +- FX_RECT GetViewBBox(CPDFSDK_PageView* pPageView); +- +- WideString GetText(); +- WideString GetSelectedText(); +- void ReplaceSelection(const WideString& text); +- +- bool CanUndo(); +- bool CanRedo(); +- bool Undo(); +- bool Redo(); +- +- void SetFocusForAnnot(CPDFSDK_Annot* pAnnot, uint32_t nFlag); +- void KillFocusForAnnot(uint32_t nFlag); +- +- // CFX_Timer::CallbackIface: +- void OnTimerFired() override; +- +- // CPWL_Wnd::ProviderIface: +- CFX_Matrix GetWindowMatrix( +- const IPWL_SystemHandler::PerWindowData* pAttached) override; +- +- virtual void GetActionData(CPDFSDK_PageView* pPageView, +- CPDF_AAction::AActionType type, +- CPDFSDK_FieldAction& fa); +- virtual void SetActionData(CPDFSDK_PageView* pPageView, +- CPDF_AAction::AActionType type, +- const CPDFSDK_FieldAction& fa); +- virtual CPWL_Wnd::CreateParams GetCreateParam(); +- virtual std::unique_ptr NewPWLWindow( +- const CPWL_Wnd::CreateParams& cp, +- std::unique_ptr pAttachedData) = 0; +- virtual CPWL_Wnd* ResetPWLWindow(CPDFSDK_PageView* pPageView, +- bool bRestoreValue); +- virtual void SaveState(CPDFSDK_PageView* pPageView); +- virtual void RestoreState(CPDFSDK_PageView* pPageView); +- +- CFX_Matrix GetCurMatrix(); +- CFX_FloatRect GetFocusBox(CPDFSDK_PageView* pPageView); +- CFX_FloatRect FFLtoPWL(const CFX_FloatRect& rect); +- CFX_FloatRect PWLtoFFL(const CFX_FloatRect& rect); +- CFX_PointF FFLtoPWL(const CFX_PointF& point); +- CFX_PointF PWLtoFFL(const CFX_PointF& point); +- CFX_PointF WndtoPWL(const CFX_PointF& pt); +- CFX_FloatRect FFLtoWnd(const CFX_FloatRect& rect); +- +- bool CommitData(CPDFSDK_PageView* pPageView, uint32_t nFlag); +- virtual bool IsDataChanged(CPDFSDK_PageView* pPageView); +- virtual void SaveData(CPDFSDK_PageView* pPageView); +- +-#ifdef PDF_ENABLE_XFA +- virtual bool IsFieldFull(CPDFSDK_PageView* pPageView); +-#endif // PDF_ENABLE_XFA +- +- CPWL_Wnd* GetPWLWindow(CPDFSDK_PageView* pPageView, bool bNew); +- void DestroyPWLWindow(CPDFSDK_PageView* pPageView); +- void EscapeFiller(CPDFSDK_PageView* pPageView, bool bDestroyPWLWindow); +- +- bool IsValid() const; +- CFX_FloatRect GetPDFAnnotRect() const; +- +- CPDFSDK_PageView* GetCurPageView(bool renew); +- void SetChangeMark(); +- +- CPDFSDK_Annot* GetSDKAnnot() const { return m_pWidget.Get(); } +- +- protected: +- // If the inheriting widget has its own fontmap and a PWL_Edit widget that +- // access that fontmap then you have to call DestroyWindows before destroying +- // the font map in order to not get a use-after-free. +- // +- // The font map should be stored somewhere more appropriate so it will live +- // until the PWL_Edit is done with it. pdfium:566 +- void DestroyWindows(); +- +- void InvalidateRect(const FX_RECT& rect); +- +- bool m_bValid = false; +- UnownedPtr const m_pFormFillEnv; +- UnownedPtr m_pWidget; +- std::unique_ptr m_pTimer; +- std::map> m_Maps; +-}; +- +-#endif // FPDFSDK_FORMFILLER_CFFL_FORMFILLER_H_ +diff --git a/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp b/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp +index 6766e1cd0..5137429ff 100644 +--- a/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp ++++ b/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,47 +6,46 @@ + + #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h" + ++#include "constants/access_permissions.h" ++#include "constants/ascii.h" + #include "constants/form_flags.h" + #include "core/fpdfapi/page/cpdf_page.h" + #include "core/fxcrt/autorestorer.h" +-#include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" +-#include "core/fxge/cfx_renderdevice.h" +-#include "fpdfsdk/cpdfsdk_formfillenvironment.h" +-#include "fpdfsdk/cpdfsdk_interactiveform.h" ++#include "core/fxge/cfx_drawutils.h" + #include "fpdfsdk/cpdfsdk_pageview.h" + #include "fpdfsdk/cpdfsdk_widget.h" + #include "fpdfsdk/formfiller/cffl_checkbox.h" + #include "fpdfsdk/formfiller/cffl_combobox.h" +-#include "fpdfsdk/formfiller/cffl_formfiller.h" ++#include "fpdfsdk/formfiller/cffl_formfield.h" + #include "fpdfsdk/formfiller/cffl_listbox.h" ++#include "fpdfsdk/formfiller/cffl_perwindowdata.h" + #include "fpdfsdk/formfiller/cffl_pushbutton.h" + #include "fpdfsdk/formfiller/cffl_radiobutton.h" + #include "fpdfsdk/formfiller/cffl_textfield.h" + #include "public/fpdf_fwlevent.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/cxx17_backports.h" + + CFFL_InteractiveFormFiller::CFFL_InteractiveFormFiller( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) +- : m_pFormFillEnv(pFormFillEnv) {} ++ CallbackIface* pCallbackIface) ++ : m_pCallbackIface(pCallbackIface) {} + + CFFL_InteractiveFormFiller::~CFFL_InteractiveFormFiller() = default; + +-bool CFFL_InteractiveFormFiller::Annot_HitTest(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, ++bool CFFL_InteractiveFormFiller::Annot_HitTest(const CPDFSDK_Widget* pWidget, + const CFX_PointF& point) { +- return pAnnot->GetRect().Contains(point); ++ return pWidget->GetRect().Contains(point); + } + +-FX_RECT CFFL_InteractiveFormFiller::GetViewBBox(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot) { +- if (CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot)) +- return pFormFiller->GetViewBBox(pPageView); ++FX_RECT CFFL_InteractiveFormFiller::GetViewBBox( ++ const CPDFSDK_PageView* pPageView, ++ CPDFSDK_Widget* pWidget) { ++ if (CFFL_FormField* pFormField = GetFormField(pWidget)) ++ return pFormField->GetViewBBox(pPageView); + +- ASSERT(pPageView); ++ DCHECK(pPageView); + +- CPDF_Annot* pPDFAnnot = pAnnot->GetPDFAnnot(); ++ CPDF_Annot* pPDFAnnot = pWidget->GetPDFAnnot(); + CFX_FloatRect rcWin = pPDFAnnot->GetRect(); + if (!rcWin.IsEmpty()) { + rcWin.Inflate(1, 1); +@@ -56,181 +55,161 @@ FX_RECT CFFL_InteractiveFormFiller::GetViewBBox(CPDFSDK_PageView* pPageView, + } + + void CFFL_InteractiveFormFiller::OnDraw(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, ++ CPDFSDK_Widget* pWidget, + CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device) { +- ASSERT(pPageView); +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot); ++ DCHECK(pPageView); + if (!IsVisible(pWidget)) + return; + +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot); +- if (pFormFiller && pFormFiller->IsValid()) { +- pFormFiller->OnDraw(pPageView, pAnnot, pDevice, mtUser2Device); +- pAnnot->GetPDFPage(); +- if (m_pFormFillEnv->GetFocusAnnot() != pAnnot) ++ CFFL_FormField* pFormField = GetFormField(pWidget); ++ if (pFormField && pFormField->IsValid()) { ++ pFormField->OnDraw(pPageView, pWidget, pDevice, mtUser2Device); ++ if (m_pCallbackIface->GetFocusAnnot() != pWidget) + return; + +- CFX_FloatRect rcFocus = pFormFiller->GetFocusBox(pPageView); ++ CFX_FloatRect rcFocus = pFormField->GetFocusBox(pPageView); + if (rcFocus.IsEmpty()) + return; + +- CFX_PathData path; +- path.AppendPoint(CFX_PointF(rcFocus.left, rcFocus.top), FXPT_TYPE::MoveTo, +- false); +- path.AppendPoint(CFX_PointF(rcFocus.left, rcFocus.bottom), +- FXPT_TYPE::LineTo, false); +- path.AppendPoint(CFX_PointF(rcFocus.right, rcFocus.bottom), +- FXPT_TYPE::LineTo, false); +- path.AppendPoint(CFX_PointF(rcFocus.right, rcFocus.top), FXPT_TYPE::LineTo, +- false); +- path.AppendPoint(CFX_PointF(rcFocus.left, rcFocus.top), FXPT_TYPE::LineTo, +- false); +- +- CFX_GraphStateData gsd; +- gsd.m_DashArray = {1.0f}; +- gsd.m_DashPhase = 0; +- gsd.m_LineWidth = 1.0f; +- pDevice->DrawPath(&path, &mtUser2Device, &gsd, 0, ArgbEncode(255, 0, 0, 0), +- FXFILL_ALTERNATE); ++ CFX_DrawUtils::DrawFocusRect(pDevice, mtUser2Device, rcFocus); ++ + return; + } + +- if (pFormFiller) { +- pFormFiller->OnDrawDeactive(pPageView, pAnnot, pDevice, mtUser2Device); ++ if (pFormField) { ++ pFormField->OnDrawDeactive(pPageView, pWidget, pDevice, mtUser2Device); + } else { +- pWidget->DrawAppearance(pDevice, mtUser2Device, CPDF_Annot::Normal, +- nullptr); ++ pWidget->DrawAppearance(pDevice, mtUser2Device, ++ CPDF_Annot::AppearanceMode::kNormal); + } + + if (!IsReadOnly(pWidget) && IsFillingAllowed(pWidget)) + pWidget->DrawShadow(pDevice, pPageView); + } + +-void CFFL_InteractiveFormFiller::OnDelete(CPDFSDK_Annot* pAnnot) { +- UnRegisterFormFiller(pAnnot); ++void CFFL_InteractiveFormFiller::OnDelete(CPDFSDK_Widget* pWidget) { ++ UnregisterFormField(pWidget); + } + + void CFFL_InteractiveFormFiller::OnMouseEnter( + CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) { +- ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); ++ ObservedPtr& pWidget, ++ Mask nFlag) { + if (!m_bNotifying) { +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get()); +- if (pWidget->GetAAction(CPDF_AAction::kCursorEnter).GetDict()) { +- m_bNotifying = true; +- ++ if (pWidget->GetAAction(CPDF_AAction::kCursorEnter).HasDict()) { + uint32_t nValueAge = pWidget->GetValueAge(); + pWidget->ClearAppModified(); +- ASSERT(pPageView); +- +- CPDFSDK_FieldAction fa; +- fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); +- fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); +- pWidget->OnAAction(CPDF_AAction::kCursorEnter, &fa, pPageView); +- m_bNotifying = false; +- if (!pAnnot->HasObservable()) ++ DCHECK(pPageView); ++ { ++ AutoRestorer restorer(&m_bNotifying); ++ m_bNotifying = true; ++ ++ CFFL_FieldAction fa; ++ fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); ++ fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); ++ pWidget->OnAAction(CPDF_AAction::kCursorEnter, &fa, pPageView); ++ } ++ if (!pWidget) + return; + + if (pWidget->IsAppModified()) { +- if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget)) { +- pFormFiller->ResetPWLWindow(pPageView, +- pWidget->GetValueAge() == nValueAge); +- } ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ if (pFormField) ++ pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(), ++ nValueAge); + } + } + } +- if (CFFL_FormFiller* pFormFiller = GetOrCreateFormFiller(pAnnot->Get())) +- pFormFiller->OnMouseEnter(pPageView); ++ if (CFFL_FormField* pFormField = GetOrCreateFormField(pWidget.Get())) ++ pFormField->OnMouseEnter(pPageView); + } + +-void CFFL_InteractiveFormFiller::OnMouseExit(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) { +- ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); ++void CFFL_InteractiveFormFiller::OnMouseExit( ++ CPDFSDK_PageView* pPageView, ++ ObservedPtr& pWidget, ++ Mask nFlag) { + if (!m_bNotifying) { +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get()); +- if (pWidget->GetAAction(CPDF_AAction::kCursorExit).GetDict()) { +- m_bNotifying = true; +- ++ if (pWidget->GetAAction(CPDF_AAction::kCursorExit).HasDict()) { + uint32_t nValueAge = pWidget->GetValueAge(); + pWidget->ClearAppModified(); +- ASSERT(pPageView); +- +- CPDFSDK_FieldAction fa; +- fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); +- fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); +- pWidget->OnAAction(CPDF_AAction::kCursorExit, &fa, pPageView); +- m_bNotifying = false; +- if (!pAnnot->HasObservable()) ++ DCHECK(pPageView); ++ { ++ AutoRestorer restorer(&m_bNotifying); ++ m_bNotifying = true; ++ ++ CFFL_FieldAction fa; ++ fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); ++ fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); ++ pWidget->OnAAction(CPDF_AAction::kCursorExit, &fa, pPageView); ++ } ++ if (!pWidget) + return; + + if (pWidget->IsAppModified()) { +- if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget)) { +- pFormFiller->ResetPWLWindow(pPageView, +- nValueAge == pWidget->GetValueAge()); ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ if (pFormField) { ++ pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(), ++ nValueAge); + } + } + } + } +- if (CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get())) +- pFormFiller->OnMouseExit(pPageView); ++ if (CFFL_FormField* pFormField = GetFormField(pWidget.Get())) ++ pFormField->OnMouseExit(pPageView); + } + + bool CFFL_InteractiveFormFiller::OnLButtonDown( + CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, ++ ObservedPtr& pWidget, ++ Mask nFlags, + const CFX_PointF& point) { +- ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); + if (!m_bNotifying) { +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get()); +- if (Annot_HitTest(pPageView, pAnnot->Get(), point) && +- pWidget->GetAAction(CPDF_AAction::kButtonDown).GetDict()) { +- m_bNotifying = true; +- ++ if (Annot_HitTest(pWidget.Get(), point) && ++ pWidget->GetAAction(CPDF_AAction::kButtonDown).HasDict()) { + uint32_t nValueAge = pWidget->GetValueAge(); + pWidget->ClearAppModified(); +- ASSERT(pPageView); +- +- CPDFSDK_FieldAction fa; +- fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlags); +- fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlags); +- pWidget->OnAAction(CPDF_AAction::kButtonDown, &fa, pPageView); +- m_bNotifying = false; +- if (!pAnnot->HasObservable()) ++ DCHECK(pPageView); ++ { ++ AutoRestorer restorer(&m_bNotifying); ++ m_bNotifying = true; ++ ++ CFFL_FieldAction fa; ++ fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlags); ++ fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlags); ++ pWidget->OnAAction(CPDF_AAction::kButtonDown, &fa, pPageView); ++ } ++ if (!pWidget) + return true; + +- if (!IsValidAnnot(pPageView, pAnnot->Get())) ++ if (!IsValidAnnot(pPageView, pWidget.Get())) + return true; + + if (pWidget->IsAppModified()) { +- if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget)) { +- pFormFiller->ResetPWLWindow(pPageView, +- nValueAge == pWidget->GetValueAge()); ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ if (pFormField) { ++ pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(), ++ nValueAge); + } + } + } + } +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get()); +- return pFormFiller && +- pFormFiller->OnLButtonDown(pPageView, pAnnot->Get(), nFlags, point); ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ return pFormField && ++ pFormField->OnLButtonDown(pPageView, pWidget.Get(), nFlags, point); + } + +-bool CFFL_InteractiveFormFiller::OnLButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get()); +- ++bool CFFL_InteractiveFormFiller::OnLButtonUp( ++ CPDFSDK_PageView* pPageView, ++ ObservedPtr& pWidget, ++ Mask nFlags, ++ const CFX_PointF& point) { + bool bSetFocus; + switch (pWidget->GetFieldType()) { + case FormFieldType::kPushButton: + case FormFieldType::kCheckBox: + case FormFieldType::kRadioButton: { +- FX_RECT bbox = GetViewBBox(pPageView, pAnnot->Get()); ++ FX_RECT bbox = GetViewBBox(pPageView, pWidget.Get()); + bSetFocus = + bbox.Contains(static_cast(point.x), static_cast(point.y)); + break; +@@ -239,223 +218,244 @@ bool CFFL_InteractiveFormFiller::OnLButtonUp(CPDFSDK_PageView* pPageView, + bSetFocus = true; + break; + } +- if (bSetFocus) +- m_pFormFillEnv->SetFocusAnnot(pAnnot); ++ if (bSetFocus) { ++ ObservedPtr pObserved(pWidget.Get()); ++ m_pCallbackIface->SetFocusAnnot(pObserved); ++ } + +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get()); +- bool bRet = pFormFiller && +- pFormFiller->OnLButtonUp(pPageView, pAnnot->Get(), nFlags, point); +- if (m_pFormFillEnv->GetFocusAnnot() != pAnnot->Get()) ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ bool bRet = pFormField && ++ pFormField->OnLButtonUp(pPageView, pWidget.Get(), nFlags, point); ++ if (m_pCallbackIface->GetFocusAnnot() != pWidget.Get()) + return bRet; +- if (OnButtonUp(pAnnot, pPageView, nFlags) || !pAnnot) ++ if (OnButtonUp(pWidget, pPageView, nFlags) || !pWidget) + return true; + #ifdef PDF_ENABLE_XFA +- if (OnClick(pAnnot, pPageView, nFlags) || !pAnnot) ++ if (OnClick(pWidget, pPageView, nFlags) || !pWidget) + return true; + #endif // PDF_ENABLE_XFA + return bRet; + } + +-bool CFFL_InteractiveFormFiller::OnButtonUp(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag) { ++bool CFFL_InteractiveFormFiller::OnButtonUp( ++ ObservedPtr& pWidget, ++ const CPDFSDK_PageView* pPageView, ++ Mask nFlag) { + if (m_bNotifying) + return false; + +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get()); +- if (!pWidget->GetAAction(CPDF_AAction::kButtonUp).GetDict()) ++ if (!pWidget->GetAAction(CPDF_AAction::kButtonUp).HasDict()) + return false; + +- m_bNotifying = true; +- + uint32_t nAge = pWidget->GetAppearanceAge(); + uint32_t nValueAge = pWidget->GetValueAge(); +- ASSERT(pPageView); +- +- CPDFSDK_FieldAction fa; +- fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); +- fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); +- pWidget->OnAAction(CPDF_AAction::kButtonUp, &fa, pPageView); +- m_bNotifying = false; +- if (!pAnnot->HasObservable() || !IsValidAnnot(pPageView, pWidget)) ++ DCHECK(pPageView); ++ { ++ AutoRestorer restorer(&m_bNotifying); ++ m_bNotifying = true; ++ ++ CFFL_FieldAction fa; ++ fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); ++ fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); ++ pWidget->OnAAction(CPDF_AAction::kButtonUp, &fa, pPageView); ++ } ++ if (!pWidget || !IsValidAnnot(pPageView, pWidget.Get())) + return true; + if (nAge == pWidget->GetAppearanceAge()) + return false; + +- CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget); +- if (pFormFiller) +- pFormFiller->ResetPWLWindow(pPageView, nValueAge == pWidget->GetValueAge()); ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ if (pFormField) ++ pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(), nValueAge); + return true; + } + + bool CFFL_InteractiveFormFiller::SetIndexSelected( +- ObservedPtr* pAnnot, ++ ObservedPtr& pWidget, + int index, + bool selected) { +- ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get()); +- return pFormFiller && pFormFiller->SetIndexSelected(index, selected); ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ return pFormField && pFormField->SetIndexSelected(index, selected); + } + + bool CFFL_InteractiveFormFiller::IsIndexSelected( +- ObservedPtr* pAnnot, ++ ObservedPtr& pWidget, + int index) { +- ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get()); +- return pFormFiller && pFormFiller->IsIndexSelected(index); ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ return pFormField && pFormField->IsIndexSelected(index); + } + + bool CFFL_InteractiveFormFiller::OnLButtonDblClk( + CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, ++ ObservedPtr& pWidget, ++ Mask nFlags, + const CFX_PointF& point) { +- ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get()); +- return pFormFiller && pFormFiller->OnLButtonDblClk(pPageView, nFlags, point); ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ return pFormField && pFormField->OnLButtonDblClk(pPageView, nFlags, point); + } + +-bool CFFL_InteractiveFormFiller::OnMouseMove(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- CFFL_FormFiller* pFormFiller = GetOrCreateFormFiller(pAnnot->Get()); +- return pFormFiller && pFormFiller->OnMouseMove(pPageView, nFlags, point); ++bool CFFL_InteractiveFormFiller::OnMouseMove( ++ CPDFSDK_PageView* pPageView, ++ ObservedPtr& pWidget, ++ Mask nFlags, ++ const CFX_PointF& point) { ++ CFFL_FormField* pFormField = GetOrCreateFormField(pWidget.Get()); ++ return pFormField && pFormField->OnMouseMove(pPageView, nFlags, point); + } + + bool CFFL_InteractiveFormFiller::OnMouseWheel( + CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- short zDelta, +- const CFX_PointF& point) { +- ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get()); +- return pFormFiller && +- pFormFiller->OnMouseWheel(pPageView, nFlags, zDelta, point); ++ ObservedPtr& pWidget, ++ Mask nFlags, ++ const CFX_PointF& point, ++ const CFX_Vector& delta) { ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ return pFormField && ++ pFormField->OnMouseWheel(pPageView, nFlags, point, delta); + } + + bool CFFL_InteractiveFormFiller::OnRButtonDown( + CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, ++ ObservedPtr& pWidget, ++ Mask nFlags, + const CFX_PointF& point) { +- ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get()); +- return pFormFiller && pFormFiller->OnRButtonDown(pPageView, nFlags, point); ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ return pFormField && pFormField->OnRButtonDown(pPageView, nFlags, point); + } + +-bool CFFL_InteractiveFormFiller::OnRButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get()); +- return pFormFiller && pFormFiller->OnRButtonUp(pPageView, nFlags, point); ++bool CFFL_InteractiveFormFiller::OnRButtonUp( ++ CPDFSDK_PageView* pPageView, ++ ObservedPtr& pWidget, ++ Mask nFlags, ++ const CFX_PointF& point) { ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ return pFormField && pFormField->OnRButtonUp(pPageView, nFlags, point); + } + +-bool CFFL_InteractiveFormFiller::OnKeyDown(CPDFSDK_Annot* pAnnot, +- uint32_t nKeyCode, +- uint32_t nFlags) { +- ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot); +- return pFormFiller && pFormFiller->OnKeyDown(nKeyCode, nFlags); ++bool CFFL_InteractiveFormFiller::OnKeyDown(CPDFSDK_Widget* pWidget, ++ FWL_VKEYCODE nKeyCode, ++ Mask nFlags) { ++ CFFL_FormField* pFormField = GetFormField(pWidget); ++ return pFormField && pFormField->OnKeyDown(nKeyCode, nFlags); + } + +-bool CFFL_InteractiveFormFiller::OnChar(CPDFSDK_Annot* pAnnot, ++bool CFFL_InteractiveFormFiller::OnChar(CPDFSDK_Widget* pWidget, + uint32_t nChar, +- uint32_t nFlags) { +- ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- if (nChar == FWL_VKEY_Tab) ++ Mask nFlags) { ++ if (nChar == pdfium::ascii::kTab) + return true; + +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot); +- return pFormFiller && pFormFiller->OnChar(pAnnot, nChar, nFlags); ++ CFFL_FormField* pFormField = GetFormField(pWidget); ++ return pFormField && pFormField->OnChar(pWidget, nChar, nFlags); + } + +-bool CFFL_InteractiveFormFiller::OnSetFocus(ObservedPtr* pAnnot, +- uint32_t nFlag) { +- if (!pAnnot->HasObservable()) ++bool CFFL_InteractiveFormFiller::OnSetFocus( ++ ObservedPtr& pWidget, ++ Mask nFlag) { ++ if (!pWidget) + return false; + +- ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); + if (!m_bNotifying) { +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get()); +- if (pWidget->GetAAction(CPDF_AAction::kGetFocus).GetDict()) { +- m_bNotifying = true; +- ++ if (pWidget->GetAAction(CPDF_AAction::kGetFocus).HasDict()) { + uint32_t nValueAge = pWidget->GetValueAge(); + pWidget->ClearAppModified(); + +- CFFL_FormFiller* pFormFiller = GetOrCreateFormFiller(pWidget); +- if (!pFormFiller) ++ CFFL_FormField* pFormField = GetOrCreateFormField(pWidget.Get()); ++ if (!pFormField) + return false; + +- CPDFSDK_PageView* pPageView = (*pAnnot)->GetPageView(); +- ASSERT(pPageView); +- +- CPDFSDK_FieldAction fa; +- fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); +- fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); +- pFormFiller->GetActionData(pPageView, CPDF_AAction::kGetFocus, fa); +- pWidget->OnAAction(CPDF_AAction::kGetFocus, &fa, pPageView); +- m_bNotifying = false; +- if (!pAnnot->HasObservable()) ++ CPDFSDK_PageView* pPageView = pWidget->GetPageView(); ++ DCHECK(pPageView); ++ { ++ AutoRestorer restorer(&m_bNotifying); ++ m_bNotifying = true; ++ ++ CFFL_FieldAction fa; ++ fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); ++ fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); ++ pFormField->GetActionData(pPageView, CPDF_AAction::kGetFocus, fa); ++ pWidget->OnAAction(CPDF_AAction::kGetFocus, &fa, pPageView); ++ } ++ if (!pWidget) + return false; + + if (pWidget->IsAppModified()) { +- if (CFFL_FormFiller* pFiller = GetFormFiller(pWidget)) { +- pFiller->ResetPWLWindow(pPageView, +- nValueAge == pWidget->GetValueAge()); ++ CFFL_FormField* pFiller = GetFormField(pWidget.Get()); ++ if (pFiller) { ++ pFiller->ResetPWLWindowForValueAge(pPageView, pWidget.Get(), ++ nValueAge); + } + } + } + } + +- if (CFFL_FormFiller* pFormFiller = GetOrCreateFormFiller(pAnnot->Get())) +- pFormFiller->SetFocusForAnnot(pAnnot->Get(), nFlag); ++ if (CFFL_FormField* pFormField = GetOrCreateFormField(pWidget.Get())) ++ pFormField->SetFocusForAnnot(pWidget.Get(), nFlag); + + return true; + } + +-bool CFFL_InteractiveFormFiller::OnKillFocus(ObservedPtr* pAnnot, +- uint32_t nFlag) { +- if (!pAnnot->HasObservable()) ++bool CFFL_InteractiveFormFiller::OnKillFocus( ++ ObservedPtr& pWidget, ++ Mask nFlag) { ++ if (!pWidget) + return false; + +- ASSERT((*pAnnot)->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot->Get()); +- if (!pFormFiller) ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ if (!pFormField) + return true; + +- pFormFiller->KillFocusForAnnot(nFlag); +- if (!pAnnot->HasObservable()) ++ pFormField->KillFocusForAnnot(nFlag); ++ if (!pWidget) + return false; + + if (m_bNotifying) + return true; + +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get()); +- if (!pWidget->GetAAction(CPDF_AAction::kLoseFocus).GetDict()) ++ if (!pWidget->GetAAction(CPDF_AAction::kLoseFocus).HasDict()) + return true; + +- m_bNotifying = true; + pWidget->ClearAppModified(); + + CPDFSDK_PageView* pPageView = pWidget->GetPageView(); +- ASSERT(pPageView); ++ DCHECK(pPageView); ++ { ++ AutoRestorer restorer(&m_bNotifying); ++ m_bNotifying = true; ++ ++ CFFL_FieldAction fa; ++ fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); ++ fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); ++ pFormField->GetActionData(pPageView, CPDF_AAction::kLoseFocus, fa); ++ pWidget->OnAAction(CPDF_AAction::kLoseFocus, &fa, pPageView); ++ } ++ return !!pWidget; ++} + +- CPDFSDK_FieldAction fa; +- fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); +- fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); +- pFormFiller->GetActionData(pPageView, CPDF_AAction::kLoseFocus, fa); +- pWidget->OnAAction(CPDF_AAction::kLoseFocus, &fa, pPageView); +- m_bNotifying = false; +- return pAnnot->HasObservable(); ++void CFFL_InteractiveFormFiller::OnSetFieldInputFocus(const WideString& text) { ++ m_pCallbackIface->OnSetFieldInputFocus(text); ++} ++ ++void CFFL_InteractiveFormFiller::Invalidate(IPDF_Page* pPage, ++ const FX_RECT& rect) { ++ m_pCallbackIface->Invalidate(pPage, rect); ++} ++ ++CPDFSDK_PageView* CFFL_InteractiveFormFiller::GetOrCreatePageView( ++ IPDF_Page* pPage) { ++ return m_pCallbackIface->GetOrCreatePageView(pPage); ++} ++ ++CPDFSDK_PageView* CFFL_InteractiveFormFiller::GetPageView(IPDF_Page* pPage) { ++ return m_pCallbackIface->GetPageView(pPage); ++} ++ ++CFX_Timer::HandlerIface* CFFL_InteractiveFormFiller::GetTimerHandler() { ++ return m_pCallbackIface->GetTimerHandler(); ++} ++ ++void CFFL_InteractiveFormFiller::OnChange() { ++ m_pCallbackIface->OnChange(); + } + + bool CFFL_InteractiveFormFiller::IsVisible(CPDFSDK_Widget* pWidget) { +@@ -472,122 +472,156 @@ bool CFFL_InteractiveFormFiller::IsFillingAllowed( + if (pWidget->GetFieldType() == FormFieldType::kPushButton) + return false; + +- return m_pFormFillEnv->GetPermissions(FPDFPERM_FILL_FORM) || +- m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM) || +- m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY); ++ return m_pCallbackIface->HasPermissions( ++ pdfium::access_permissions::kFillForm | ++ pdfium::access_permissions::kModifyAnnotation | ++ pdfium::access_permissions::kModifyContent); + } + +-CFFL_FormFiller* CFFL_InteractiveFormFiller::GetFormFiller( +- CPDFSDK_Annot* pAnnot) { +- auto it = m_Map.find(pAnnot); ++CFFL_FormField* CFFL_InteractiveFormFiller::GetFormField( ++ CPDFSDK_Widget* pWidget) { ++ auto it = m_Map.find(pWidget); + return it != m_Map.end() ? it->second.get() : nullptr; + } + +-CFFL_FormFiller* CFFL_InteractiveFormFiller::GetOrCreateFormFiller( +- CPDFSDK_Annot* pAnnot) { +- CFFL_FormFiller* result = GetFormFiller(pAnnot); ++CFFL_FormField* CFFL_InteractiveFormFiller::GetOrCreateFormField( ++ CPDFSDK_Widget* pWidget) { ++ CFFL_FormField* result = GetFormField(pWidget); + if (result) + return result; + +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot); +- std::unique_ptr pFormFiller; ++ std::unique_ptr pFormField; + switch (pWidget->GetFieldType()) { + case FormFieldType::kPushButton: +- pFormFiller = +- pdfium::MakeUnique(m_pFormFillEnv.Get(), pWidget); ++ pFormField = std::make_unique(this, pWidget); + break; + case FormFieldType::kCheckBox: +- pFormFiller = +- pdfium::MakeUnique(m_pFormFillEnv.Get(), pWidget); ++ pFormField = std::make_unique(this, pWidget); + break; + case FormFieldType::kRadioButton: +- pFormFiller = +- pdfium::MakeUnique(m_pFormFillEnv.Get(), pWidget); ++ pFormField = std::make_unique(this, pWidget); + break; + case FormFieldType::kTextField: +- pFormFiller = +- pdfium::MakeUnique(m_pFormFillEnv.Get(), pWidget); ++ pFormField = std::make_unique(this, pWidget); + break; + case FormFieldType::kListBox: +- pFormFiller = +- pdfium::MakeUnique(m_pFormFillEnv.Get(), pWidget); ++ pFormField = std::make_unique(this, pWidget); + break; + case FormFieldType::kComboBox: +- pFormFiller = +- pdfium::MakeUnique(m_pFormFillEnv.Get(), pWidget); ++ pFormField = std::make_unique(this, pWidget); + break; + case FormFieldType::kUnknown: + default: + return nullptr; + } + +- result = pFormFiller.get(); +- m_Map[pAnnot] = std::move(pFormFiller); ++ result = pFormField.get(); ++ m_Map[pWidget] = std::move(pFormField); + return result; + } + +-WideString CFFL_InteractiveFormFiller::GetText(CPDFSDK_Annot* pAnnot) { +- ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot); +- return pFormFiller ? pFormFiller->GetText() : WideString(); ++WideString CFFL_InteractiveFormFiller::GetText(CPDFSDK_Widget* pWidget) { ++ CFFL_FormField* pFormField = GetFormField(pWidget); ++ return pFormField ? pFormField->GetText() : WideString(); + } + +-WideString CFFL_InteractiveFormFiller::GetSelectedText(CPDFSDK_Annot* pAnnot) { +- ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot); +- return pFormFiller ? pFormFiller->GetSelectedText() : WideString(); ++WideString CFFL_InteractiveFormFiller::GetSelectedText( ++ CPDFSDK_Widget* pWidget) { ++ CFFL_FormField* pFormField = GetFormField(pWidget); ++ return pFormField ? pFormField->GetSelectedText() : WideString(); + } + +-void CFFL_InteractiveFormFiller::ReplaceSelection(CPDFSDK_Annot* pAnnot, ++void CFFL_InteractiveFormFiller::ReplaceAndKeepSelection( ++ CPDFSDK_Widget* pWidget, ++ const WideString& text) { ++ CFFL_FormField* pFormField = GetFormField(pWidget); ++ if (!pFormField) ++ return; ++ ++ pFormField->ReplaceAndKeepSelection(text); ++} ++ ++void CFFL_InteractiveFormFiller::ReplaceSelection(CPDFSDK_Widget* pWidget, + const WideString& text) { +- ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot); +- if (!pFormFiller) ++ CFFL_FormField* pFormField = GetFormField(pWidget); ++ if (!pFormField) + return; + +- pFormFiller->ReplaceSelection(text); ++ pFormField->ReplaceSelection(text); ++} ++ ++bool CFFL_InteractiveFormFiller::SelectAllText(CPDFSDK_Widget* pWidget) { ++ CFFL_FormField* pFormField = GetFormField(pWidget); ++ return pFormField && pFormField->SelectAllText(); + } + +-bool CFFL_InteractiveFormFiller::CanUndo(CPDFSDK_Annot* pAnnot) { +- ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot); +- return pFormFiller && pFormFiller->CanUndo(); ++bool CFFL_InteractiveFormFiller::CanUndo(CPDFSDK_Widget* pWidget) { ++ CFFL_FormField* pFormField = GetFormField(pWidget); ++ return pFormField && pFormField->CanUndo(); + } + +-bool CFFL_InteractiveFormFiller::CanRedo(CPDFSDK_Annot* pAnnot) { +- ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot); +- return pFormFiller && pFormFiller->CanRedo(); ++bool CFFL_InteractiveFormFiller::CanRedo(CPDFSDK_Widget* pWidget) { ++ CFFL_FormField* pFormField = GetFormField(pWidget); ++ return pFormField && pFormField->CanRedo(); + } + +-bool CFFL_InteractiveFormFiller::Undo(CPDFSDK_Annot* pAnnot) { +- ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot); +- return pFormFiller && pFormFiller->Undo(); ++bool CFFL_InteractiveFormFiller::Undo(CPDFSDK_Widget* pWidget) { ++ CFFL_FormField* pFormField = GetFormField(pWidget); ++ return pFormField && pFormField->Undo(); + } + +-bool CFFL_InteractiveFormFiller::Redo(CPDFSDK_Annot* pAnnot) { +- ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET); +- CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot); +- return pFormFiller && pFormFiller->Redo(); ++bool CFFL_InteractiveFormFiller::Redo(CPDFSDK_Widget* pWidget) { ++ CFFL_FormField* pFormField = GetFormField(pWidget); ++ return pFormField && pFormField->Redo(); + } + +-void CFFL_InteractiveFormFiller::UnRegisterFormFiller(CPDFSDK_Annot* pAnnot) { +- auto it = m_Map.find(pAnnot); ++void CFFL_InteractiveFormFiller::UnregisterFormField(CPDFSDK_Widget* pWidget) { ++ auto it = m_Map.find(pWidget); + if (it == m_Map.end()) + return; + + m_Map.erase(it); + } + ++void CFFL_InteractiveFormFiller::InvalidateRect(PerWindowData* pWidgetData, ++ const CFX_FloatRect& rect) { ++ auto* pPrivateData = static_cast(pWidgetData); ++ CPDFSDK_Widget* pWidget = pPrivateData->GetWidget(); ++ if (!pWidget) ++ return; ++ ++ m_pCallbackIface->InvalidateRect(pWidget, rect); ++} ++ ++void CFFL_InteractiveFormFiller::OutputSelectedRect(PerWindowData* pWidgetData, ++ const CFX_FloatRect& rect) { ++ auto* pPrivateData = static_cast(pWidgetData); ++ if (!pPrivateData) ++ return; ++ ++ CFFL_FormField* pFormField = pPrivateData->GetFormField(); ++ if (!pFormField) ++ return; ++ ++ m_pCallbackIface->OutputSelectedRect(pFormField, rect); ++} ++ ++bool CFFL_InteractiveFormFiller::IsSelectionImplemented() const { ++ return m_pCallbackIface->IsSelectionImplemented(); ++} ++ ++void CFFL_InteractiveFormFiller::SetCursor(CursorStyle nCursorStyle) { ++ m_pCallbackIface->SetCursor(nCursorStyle); ++} ++ + void CFFL_InteractiveFormFiller::QueryWherePopup( +- const IPWL_SystemHandler::PerWindowData* pAttached, ++ const IPWL_FillerNotify::PerWindowData* pAttached, + float fPopupMin, + float fPopupMax, + bool* bBottom, + float* fPopupRet) { +- auto* pData = static_cast(pAttached); +- CPDFSDK_Widget* pWidget = pData->pWidget.Get(); ++ auto* pData = static_cast(pAttached); ++ CPDFSDK_Widget* pWidget = pData->GetWidget(); + CPDF_Page* pPage = pWidget->GetPDFPage(); + + CFX_FloatRect rcPageView(0, pPage->GetPageHeight(), pPage->GetPageWidth(), 0); +@@ -642,274 +676,260 @@ void CFFL_InteractiveFormFiller::QueryWherePopup( + } + + bool CFFL_InteractiveFormFiller::OnKeyStrokeCommit( +- ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag) { ++ ObservedPtr& pWidget, ++ const CPDFSDK_PageView* pPageView, ++ Mask nFlag) { + if (m_bNotifying) + return true; + +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get()); +- if (!pWidget->GetAAction(CPDF_AAction::kKeyStroke).GetDict()) ++ if (!pWidget->GetAAction(CPDF_AAction::kKeyStroke).HasDict()) + return true; + +- ASSERT(pPageView); +- m_bNotifying = true; ++ DCHECK(pPageView); + pWidget->ClearAppModified(); + +- CPDFSDK_FieldAction fa; ++ AutoRestorer restorer(&m_bNotifying); ++ m_bNotifying = true; ++ ++ CFFL_FieldAction fa; + fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); + fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); + fa.bWillCommit = true; + fa.bKeyDown = true; + fa.bRC = true; + +- CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget); +- pFormFiller->GetActionData(pPageView, CPDF_AAction::kKeyStroke, fa); +- pFormFiller->SaveState(pPageView); ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ pFormField->GetActionData(pPageView, CPDF_AAction::kKeyStroke, fa); ++ pFormField->SavePWLWindowState(pPageView); + pWidget->OnAAction(CPDF_AAction::kKeyStroke, &fa, pPageView); +- if (!pAnnot->HasObservable()) ++ ++ if (!pWidget) + return true; + +- m_bNotifying = false; + return fa.bRC; + } + +-bool CFFL_InteractiveFormFiller::OnValidate(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag) { ++bool CFFL_InteractiveFormFiller::OnValidate( ++ ObservedPtr& pWidget, ++ const CPDFSDK_PageView* pPageView, ++ Mask nFlag) { + if (m_bNotifying) + return true; + +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get()); +- if (!pWidget->GetAAction(CPDF_AAction::kValidate).GetDict()) ++ if (!pWidget->GetAAction(CPDF_AAction::kValidate).HasDict()) + return true; + +- ASSERT(pPageView); +- m_bNotifying = true; ++ DCHECK(pPageView); + pWidget->ClearAppModified(); + +- CPDFSDK_FieldAction fa; ++ AutoRestorer restorer(&m_bNotifying); ++ m_bNotifying = true; ++ ++ CFFL_FieldAction fa; + fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); + fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); + fa.bKeyDown = true; + fa.bRC = true; + +- CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget); +- pFormFiller->GetActionData(pPageView, CPDF_AAction::kValidate, fa); +- pFormFiller->SaveState(pPageView); ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ pFormField->GetActionData(pPageView, CPDF_AAction::kValidate, fa); ++ pFormField->SavePWLWindowState(pPageView); + pWidget->OnAAction(CPDF_AAction::kValidate, &fa, pPageView); +- if (!pAnnot->HasObservable()) ++ ++ if (!pWidget) + return true; + +- m_bNotifying = false; + return fa.bRC; + } + +-void CFFL_InteractiveFormFiller::OnCalculate(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag) { ++void CFFL_InteractiveFormFiller::OnCalculate( ++ ObservedPtr& pWidget) { + if (m_bNotifying) + return; + +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get()); +- if (pWidget) { +- CPDFSDK_InteractiveForm* pForm = +- pPageView->GetFormFillEnv()->GetInteractiveForm(); +- pForm->OnCalculate(pWidget->GetFormField()); +- } +- m_bNotifying = false; ++ ObservedPtr pObserved(pWidget.Get()); ++ m_pCallbackIface->OnCalculate(pObserved); + } + +-void CFFL_InteractiveFormFiller::OnFormat(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag) { ++void CFFL_InteractiveFormFiller::OnFormat( ++ ObservedPtr& pWidget) { + if (m_bNotifying) + return; + +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get()); +- ASSERT(pWidget); +- CPDFSDK_InteractiveForm* pForm = +- pPageView->GetFormFillEnv()->GetInteractiveForm(); +- +- Optional sValue = pForm->OnFormat(pWidget->GetFormField()); +- if (!pAnnot->HasObservable()) +- return; +- +- if (sValue.has_value()) { +- pForm->ResetFieldAppearance(pWidget->GetFormField(), sValue); +- pForm->UpdateField(pWidget->GetFormField()); +- } +- +- m_bNotifying = false; ++ ObservedPtr pObserved(pWidget.Get()); ++ m_pCallbackIface->OnFormat(pObserved); + } + + #ifdef PDF_ENABLE_XFA +-bool CFFL_InteractiveFormFiller::OnClick(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag) { ++bool CFFL_InteractiveFormFiller::OnClick(ObservedPtr& pWidget, ++ const CPDFSDK_PageView* pPageView, ++ Mask nFlag) { + if (m_bNotifying) + return false; + +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get()); + if (!pWidget->HasXFAAAction(PDFSDK_XFA_Click)) + return false; + +- m_bNotifying = true; + uint32_t nAge = pWidget->GetAppearanceAge(); + uint32_t nValueAge = pWidget->GetValueAge(); ++ { ++ AutoRestorer restorer(&m_bNotifying); ++ m_bNotifying = true; + +- CPDFSDK_FieldAction fa; +- fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); +- fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); ++ CFFL_FieldAction fa; ++ fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); ++ fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); + +- pWidget->OnXFAAAction(PDFSDK_XFA_Click, &fa, pPageView); +- m_bNotifying = false; +- if (!pAnnot->HasObservable() || !IsValidAnnot(pPageView, pWidget)) ++ pWidget->OnXFAAAction(PDFSDK_XFA_Click, &fa, pPageView); ++ } ++ if (!pWidget || !IsValidAnnot(pPageView, pWidget.Get())) + return true; + if (nAge == pWidget->GetAppearanceAge()) + return false; + +- if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget)) +- pFormFiller->ResetPWLWindow(pPageView, nValueAge == pWidget->GetValueAge()); ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ if (pFormField) ++ pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(), nValueAge); + return false; + } + +-bool CFFL_InteractiveFormFiller::OnFull(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag) { ++bool CFFL_InteractiveFormFiller::OnFull(ObservedPtr& pWidget, ++ const CPDFSDK_PageView* pPageView, ++ Mask nFlag) { + if (m_bNotifying) + return false; + +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get()); + if (!pWidget->HasXFAAAction(PDFSDK_XFA_Full)) + return false; + +- m_bNotifying = true; + uint32_t nAge = pWidget->GetAppearanceAge(); + uint32_t nValueAge = pWidget->GetValueAge(); +- +- CPDFSDK_FieldAction fa; +- fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); +- fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); +- +- pWidget->OnXFAAAction(PDFSDK_XFA_Full, &fa, pPageView); +- m_bNotifying = false; +- if (!pAnnot->HasObservable() || !IsValidAnnot(pPageView, pWidget)) ++ { ++ AutoRestorer restorer(&m_bNotifying); ++ m_bNotifying = true; ++ ++ CFFL_FieldAction fa; ++ fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); ++ fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); ++ pWidget->OnXFAAAction(PDFSDK_XFA_Full, &fa, pPageView); ++ } ++ if (!pWidget || !IsValidAnnot(pPageView, pWidget.Get())) + return true; + if (nAge == pWidget->GetAppearanceAge()) + return false; + +- if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget)) +- pFormFiller->ResetPWLWindow(pPageView, nValueAge == pWidget->GetValueAge()); +- ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ if (pFormField) ++ pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(), nValueAge); + return true; + } + +-bool CFFL_InteractiveFormFiller::OnPreOpen(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag) { ++bool CFFL_InteractiveFormFiller::OnPreOpen(ObservedPtr& pWidget, ++ const CPDFSDK_PageView* pPageView, ++ Mask nFlag) { + if (m_bNotifying) + return false; + +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get()); + if (!pWidget->HasXFAAAction(PDFSDK_XFA_PreOpen)) + return false; + +- m_bNotifying = true; + uint32_t nAge = pWidget->GetAppearanceAge(); + uint32_t nValueAge = pWidget->GetValueAge(); +- +- CPDFSDK_FieldAction fa; +- fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); +- fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); +- +- pWidget->OnXFAAAction(PDFSDK_XFA_PreOpen, &fa, pPageView); +- m_bNotifying = false; +- if (!pAnnot->HasObservable() || !IsValidAnnot(pPageView, pWidget)) ++ { ++ AutoRestorer restorer(&m_bNotifying); ++ m_bNotifying = true; ++ ++ CFFL_FieldAction fa; ++ fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); ++ fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); ++ pWidget->OnXFAAAction(PDFSDK_XFA_PreOpen, &fa, pPageView); ++ } ++ if (!pWidget || !IsValidAnnot(pPageView, pWidget.Get())) + return true; + if (nAge == pWidget->GetAppearanceAge()) + return false; + +- if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget)) +- pFormFiller->ResetPWLWindow(pPageView, nValueAge == pWidget->GetValueAge()); +- ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ if (pFormField) ++ pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(), nValueAge); + return true; + } + +-bool CFFL_InteractiveFormFiller::OnPostOpen(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag) { ++bool CFFL_InteractiveFormFiller::OnPostOpen( ++ ObservedPtr& pWidget, ++ const CPDFSDK_PageView* pPageView, ++ Mask nFlag) { + if (m_bNotifying) + return false; + +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot->Get()); + if (!pWidget->HasXFAAAction(PDFSDK_XFA_PostOpen)) + return false; + +- m_bNotifying = true; + uint32_t nAge = pWidget->GetAppearanceAge(); + uint32_t nValueAge = pWidget->GetValueAge(); +- +- CPDFSDK_FieldAction fa; +- fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); +- fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); +- +- pWidget->OnXFAAAction(PDFSDK_XFA_PostOpen, &fa, pPageView); +- m_bNotifying = false; +- if (!pAnnot->HasObservable() || !IsValidAnnot(pPageView, pWidget)) ++ { ++ AutoRestorer restorer(&m_bNotifying); ++ m_bNotifying = true; ++ ++ CFFL_FieldAction fa; ++ fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); ++ fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); ++ pWidget->OnXFAAAction(PDFSDK_XFA_PostOpen, &fa, pPageView); ++ } ++ if (!pWidget || !IsValidAnnot(pPageView, pWidget.Get())) + return true; ++ + if (nAge == pWidget->GetAppearanceAge()) + return false; + +- if (CFFL_FormFiller* pFormFiller = GetFormFiller(pWidget)) +- pFormFiller->ResetPWLWindow(pPageView, nValueAge == pWidget->GetValueAge()); +- ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); ++ if (pFormField) ++ pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(), nValueAge); + return true; + } + #endif // PDF_ENABLE_XFA + +-bool CFFL_InteractiveFormFiller::IsValidAnnot(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot) { +- return pPageView && pPageView->IsValidAnnot(pAnnot->GetPDFAnnot()); ++// static ++bool CFFL_InteractiveFormFiller::IsValidAnnot(const CPDFSDK_PageView* pPageView, ++ CPDFSDK_Widget* pWidget) { ++ return pPageView && pPageView->IsValidAnnot(pWidget->GetPDFAnnot()); + } + + std::pair CFFL_InteractiveFormFiller::OnBeforeKeyStroke( +- const IPWL_SystemHandler::PerWindowData* pAttached, ++ const IPWL_FillerNotify::PerWindowData* pAttached, + WideString& strChange, + const WideString& strChangeEx, + int nSelStart, + int nSelEnd, + bool bKeyDown, +- uint32_t nFlag) { +- // Copy the private data since the window owning it may not survive. +- CFFL_PrivateData privateData = +- *static_cast(pAttached); +- ASSERT(privateData.pWidget); ++ Mask nFlag) { ++ // Copy out of private data since the window owning it may not survive. ++ auto* pPrivateData = static_cast(pAttached); ++ const CPDFSDK_PageView* pPageView = pPrivateData->GetPageView(); ++ ObservedPtr pWidget(pPrivateData->GetWidget()); ++ DCHECK(pWidget); + +- CFFL_FormFiller* pFormFiller = GetFormFiller(privateData.GetWidget()); ++ CFFL_FormField* pFormField = GetFormField(pWidget.Get()); + + #ifdef PDF_ENABLE_XFA +- if (pFormFiller->IsFieldFull(privateData.pPageView)) { +- ObservedPtr pObserved(privateData.GetWidget()); +- if (OnFull(&pObserved, privateData.pPageView, nFlag) || !pObserved) ++ if (pFormField->IsFieldFull(pPageView)) { ++ if (OnFull(pWidget, pPageView, nFlag) || !pWidget) + return {true, true}; + } + #endif // PDF_ENABLE_XFA + + if (m_bNotifying || +- !privateData.pWidget->GetAAction(CPDF_AAction::kKeyStroke).GetDict()) { ++ !pWidget->GetAAction(CPDF_AAction::kKeyStroke).HasDict()) { + return {true, false}; + } + + AutoRestorer restorer(&m_bNotifying); + m_bNotifying = true; + +- uint32_t nAge = privateData.pWidget->GetAppearanceAge(); +- uint32_t nValueAge = privateData.pWidget->GetValueAge(); +- CPDFSDK_FormFillEnvironment* pFormFillEnv = +- privateData.pPageView->GetFormFillEnv(); ++ uint32_t nAge = pWidget->GetAppearanceAge(); ++ uint32_t nValueAge = pWidget->GetValueAge(); + +- CPDFSDK_FieldAction fa; ++ CFFL_FieldAction fa; + fa.bModifier = CPWL_Wnd::IsCTRLKeyDown(nFlag); + fa.bShift = CPWL_Wnd::IsSHIFTKeyDown(nFlag); + fa.sChange = strChange; +@@ -919,79 +939,65 @@ std::pair CFFL_InteractiveFormFiller::OnBeforeKeyStroke( + fa.bRC = true; + fa.nSelStart = nSelStart; + fa.nSelEnd = nSelEnd; +- pFormFiller->GetActionData(privateData.pPageView, CPDF_AAction::kKeyStroke, +- fa); +- pFormFiller->SaveState(privateData.pPageView); ++ pFormField->GetActionData(pPageView, CPDF_AAction::kKeyStroke, fa); ++ pFormField->SavePWLWindowState(pPageView); + +- ObservedPtr pObserved(privateData.GetWidget()); +- bool action_status = privateData.pWidget->OnAAction( +- CPDF_AAction::kKeyStroke, &fa, privateData.pPageView); ++ bool action_status = ++ pWidget->OnAAction(CPDF_AAction::kKeyStroke, &fa, pPageView); + +- if (!pObserved || +- !IsValidAnnot(privateData.pPageView, privateData.GetWidget())) { ++ if (!pWidget || !IsValidAnnot(pPageView, pWidget.Get())) { + return {true, true}; + } + if (!action_status) + return {true, false}; + + bool bExit = false; +- if (nAge != privateData.pWidget->GetAppearanceAge()) { +- CPWL_Wnd* pWnd = pFormFiller->ResetPWLWindow( +- privateData.pPageView, nValueAge == privateData.pWidget->GetValueAge()); +- if (!pWnd) ++ if (nAge != pWidget->GetAppearanceAge()) { ++ pFormField->ResetPWLWindowForValueAge(pPageView, pWidget.Get(), nValueAge); ++ pPrivateData = pFormField->GetPerPWLWindowData(pPageView); ++ if (!pPrivateData) + return {true, true}; +- privateData = +- *static_cast(pWnd->GetAttachedData()); ++ ++ pWidget.Reset(pPrivateData->GetWidget()); ++ pPageView = pPrivateData->GetPageView(); + bExit = true; + } + if (fa.bRC) { +- pFormFiller->SetActionData(privateData.pPageView, CPDF_AAction::kKeyStroke, +- fa); ++ pFormField->SetActionData(pPageView, CPDF_AAction::kKeyStroke, fa); + } else { +- pFormFiller->RestoreState(privateData.pPageView); ++ pFormField->RecreatePWLWindowFromSavedState(pPageView); + } +- if (pFormFillEnv->GetFocusAnnot() == privateData.GetWidget()) ++ if (m_pCallbackIface->GetFocusAnnot() == pWidget) + return {false, bExit}; + +- pFormFiller->CommitData(privateData.pPageView, nFlag); ++ pFormField->CommitData(pPageView, nFlag); + return {false, true}; + } + + bool CFFL_InteractiveFormFiller::OnPopupPreOpen( +- const IPWL_SystemHandler::PerWindowData* pAttached, +- uint32_t nFlag) { ++ const IPWL_FillerNotify::PerWindowData* pAttached, ++ Mask nFlag) { + #ifdef PDF_ENABLE_XFA +- auto* pData = static_cast(pAttached); +- ASSERT(pData->pWidget); ++ auto* pData = static_cast(pAttached); ++ DCHECK(pData->GetWidget()); + +- ObservedPtr pObserved(pData->GetWidget()); +- return OnPreOpen(&pObserved, pData->pPageView, nFlag) || !pObserved; ++ ObservedPtr pObserved(pData->GetWidget()); ++ return OnPreOpen(pObserved, pData->GetPageView(), nFlag) || !pObserved; + #else + return false; + #endif + } + + bool CFFL_InteractiveFormFiller::OnPopupPostOpen( +- const IPWL_SystemHandler::PerWindowData* pAttached, +- uint32_t nFlag) { ++ const IPWL_FillerNotify::PerWindowData* pAttached, ++ Mask nFlag) { + #ifdef PDF_ENABLE_XFA +- auto* pData = static_cast(pAttached); +- ASSERT(pData->pWidget); ++ auto* pData = static_cast(pAttached); ++ DCHECK(pData->GetWidget()); + +- ObservedPtr pObserved(pData->GetWidget()); +- return OnPostOpen(&pObserved, pData->pPageView, nFlag) || !pObserved; ++ ObservedPtr pObserved(pData->GetWidget()); ++ return OnPostOpen(pObserved, pData->GetPageView(), nFlag) || !pObserved; + #else + return false; + #endif + } +- +-CFFL_PrivateData::CFFL_PrivateData() = default; +- +-CFFL_PrivateData::CFFL_PrivateData(const CFFL_PrivateData& that) = default; +- +-CFFL_PrivateData::~CFFL_PrivateData() = default; +- +-std::unique_ptr CFFL_PrivateData::Clone() +- const { +- return pdfium::MakeUnique(*this); +-} +diff --git a/fpdfsdk/formfiller/cffl_interactiveformfiller.h b/fpdfsdk/formfiller/cffl_interactiveformfiller.h +index 3adfe4e6b..bbf757d81 100644 +--- a/fpdfsdk/formfiller/cffl_interactiveformfiller.h ++++ b/fpdfsdk/formfiller/cffl_interactiveformfiller.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,180 +11,205 @@ + #include + #include + ++#include "core/fxcrt/cfx_timer.h" ++#include "core/fxcrt/mask.h" + #include "core/fxcrt/observed_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + #include "fpdfsdk/cpdfsdk_annot.h" +-#include "fpdfsdk/pwl/cpwl_edit.h" +-#include "fpdfsdk/pwl/cpwl_wnd.h" +-#include "fpdfsdk/pwl/ipwl_systemhandler.h" ++#include "fpdfsdk/pwl/ipwl_fillernotify.h" ++#include "public/fpdf_fwlevent.h" + +-class CFFL_FormFiller; +-class CPDFSDK_FormFillEnvironment; ++class CFFL_FormField; + class CPDFSDK_PageView; + class CPDFSDK_Widget; + +-class CFFL_InteractiveFormFiller final : public IPWL_Filler_Notify { ++class CFFL_InteractiveFormFiller final : public IPWL_FillerNotify { + public: +- explicit CFFL_InteractiveFormFiller( +- CPDFSDK_FormFillEnvironment* pFormFillEnv); ++ class CallbackIface { ++ public: ++ virtual ~CallbackIface() = default; ++ ++ virtual void OnSetFieldInputFocus(const WideString& text) = 0; ++ virtual void OnCalculate(ObservedPtr& pAnnot) = 0; ++ virtual void OnFormat(ObservedPtr& pAnnot) = 0; ++ virtual void Invalidate(IPDF_Page* pPage, const FX_RECT& rect) = 0; ++ virtual CPDFSDK_PageView* GetOrCreatePageView(IPDF_Page* pPage) = 0; ++ virtual CPDFSDK_PageView* GetPageView(IPDF_Page* pPage) = 0; ++ virtual CFX_Timer::HandlerIface* GetTimerHandler() = 0; ++ virtual CPDFSDK_Annot* GetFocusAnnot() const = 0; ++ virtual bool SetFocusAnnot(ObservedPtr& pAnnot) = 0; ++ virtual void InvalidateRect(CPDFSDK_Widget* pWidget, ++ const CFX_FloatRect& rect) = 0; ++ virtual void OutputSelectedRect(CFFL_FormField* pFormField, ++ const CFX_FloatRect& rect) = 0; ++ virtual bool IsSelectionImplemented() const = 0; ++ virtual void SetCursor(CursorStyle nCursorStyle) = 0; ++ ++ // See PDF Reference 1.7, table 3.20 for the permission bits. Returns true ++ // if any bit in |flags| is set. ++ virtual bool HasPermissions(uint32_t flags) const = 0; ++ virtual void OnChange() = 0; ++ }; ++ ++ explicit CFFL_InteractiveFormFiller(CallbackIface* pCallbackIface); + ~CFFL_InteractiveFormFiller() override; + +- bool Annot_HitTest(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- const CFX_PointF& point); +- FX_RECT GetViewBBox(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot); ++ bool Annot_HitTest(const CPDFSDK_Widget* pWidget, const CFX_PointF& point); ++ FX_RECT GetViewBBox(const CPDFSDK_PageView* pPageView, ++ CPDFSDK_Widget* pWidget); ++ + void OnDraw(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, ++ CPDFSDK_Widget* pWidget, + CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device); +- +- void OnDelete(CPDFSDK_Annot* pAnnot); ++ void OnDelete(CPDFSDK_Widget* pWidget); + + void OnMouseEnter(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag); ++ ObservedPtr& pWidget, ++ Mask nFlag); + void OnMouseExit(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag); ++ ObservedPtr& pWidget, ++ Mask nFlag); + bool OnLButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, ++ ObservedPtr& pWidget, ++ Mask nFlags, + const CFX_PointF& point); + bool OnLButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, ++ ObservedPtr& pWidget, ++ Mask nFlags, + const CFX_PointF& point); + bool OnLButtonDblClk(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, ++ ObservedPtr& pWidget, ++ Mask nFlags, + const CFX_PointF& point); + bool OnMouseMove(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, ++ ObservedPtr& pWidget, ++ Mask nFlags, + const CFX_PointF& point); + bool OnMouseWheel(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- short zDelta, +- const CFX_PointF& point); ++ ObservedPtr& pWidget, ++ Mask nFlags, ++ const CFX_PointF& point, ++ const CFX_Vector& delta); + bool OnRButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, ++ ObservedPtr& pWidget, ++ Mask nFlags, + const CFX_PointF& point); + bool OnRButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, ++ ObservedPtr& pWidget, ++ Mask nFlags, + const CFX_PointF& point); + +- bool OnKeyDown(CPDFSDK_Annot* pAnnot, uint32_t nKeyCode, uint32_t nFlags); +- bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags); +- +- bool OnSetFocus(ObservedPtr* pAnnot, uint32_t nFlag); +- bool OnKillFocus(ObservedPtr* pAnnot, uint32_t nFlag); +- +- CFFL_FormFiller* GetFormFillerForTesting(CPDFSDK_Annot* pAnnot) { +- return GetFormFiller(pAnnot); ++ bool OnKeyDown(CPDFSDK_Widget* pWidget, ++ FWL_VKEYCODE nKeyCode, ++ Mask nFlags); ++ bool OnChar(CPDFSDK_Widget* pWidget, ++ uint32_t nChar, ++ Mask nFlags); ++ ++ bool OnSetFocus(ObservedPtr& pWidget, ++ Mask nFlag); ++ bool OnKillFocus(ObservedPtr& pWidget, ++ Mask nFlag); ++ ++ // Wrapper methods for CallbackIface ++ void OnSetFieldInputFocus(const WideString& text); ++ void Invalidate(IPDF_Page* pPage, const FX_RECT& rect); ++ CPDFSDK_PageView* GetOrCreatePageView(IPDF_Page* pPage); ++ CPDFSDK_PageView* GetPageView(IPDF_Page* pPage); ++ CFX_Timer::HandlerIface* GetTimerHandler(); ++ void OnChange(); ++ ++ CFFL_FormField* GetFormFieldForTesting(CPDFSDK_Widget* pAnnot) { ++ return GetFormField(pAnnot); + } + +- WideString GetText(CPDFSDK_Annot* pAnnot); +- WideString GetSelectedText(CPDFSDK_Annot* pAnnot); +- void ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text); ++ WideString GetText(CPDFSDK_Widget* pWidget); ++ WideString GetSelectedText(CPDFSDK_Widget* pWidget); ++ void ReplaceAndKeepSelection(CPDFSDK_Widget* pWidget, const WideString& text); ++ void ReplaceSelection(CPDFSDK_Widget* pWidget, const WideString& text); ++ bool SelectAllText(CPDFSDK_Widget* pWidget); + +- bool CanUndo(CPDFSDK_Annot* pAnnot); +- bool CanRedo(CPDFSDK_Annot* pAnnot); +- bool Undo(CPDFSDK_Annot* pAnnot); +- bool Redo(CPDFSDK_Annot* pAnnot); ++ bool CanUndo(CPDFSDK_Widget* pWidget); ++ bool CanRedo(CPDFSDK_Widget* pWidget); ++ bool Undo(CPDFSDK_Widget* pWidget); ++ bool Redo(CPDFSDK_Widget* pWidget); + + static bool IsVisible(CPDFSDK_Widget* pWidget); + static bool IsReadOnly(CPDFSDK_Widget* pWidget); +- static bool IsValidAnnot(CPDFSDK_PageView* pPageView, CPDFSDK_Annot* pAnnot); +- +- bool OnKeyStrokeCommit(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag); +- bool OnValidate(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag); +- void OnCalculate(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag); +- void OnFormat(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag); +- bool OnButtonUp(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag); +- +- bool SetIndexSelected(ObservedPtr* pAnnot, ++ static bool IsValidAnnot(const CPDFSDK_PageView* pPageView, ++ CPDFSDK_Widget* pWidget); ++ ++ bool OnKeyStrokeCommit(ObservedPtr& pWidget, ++ const CPDFSDK_PageView* pPageView, ++ Mask nFlag); ++ bool OnValidate(ObservedPtr& pWidget, ++ const CPDFSDK_PageView* pPageView, ++ Mask nFlag); ++ void OnCalculate(ObservedPtr& pWidget); ++ void OnFormat(ObservedPtr& pWidget); ++ bool OnButtonUp(ObservedPtr& pWidget, ++ const CPDFSDK_PageView* pPageView, ++ Mask nFlag); ++ ++ bool SetIndexSelected(ObservedPtr& pWidget, + int index, + bool selected); +- bool IsIndexSelected(ObservedPtr* pAnnot, int index); ++ bool IsIndexSelected(ObservedPtr& pWidget, int index); + + private: + using WidgetToFormFillerMap = +- std::map>; +- +- // IPWL_Filler_Notify: +- void QueryWherePopup(const IPWL_SystemHandler::PerWindowData* pAttached, ++ std::map>; ++ ++ // IPWL_FillerNotify: ++ void InvalidateRect(PerWindowData* pWidgetData, ++ const CFX_FloatRect& rect) override; ++ void OutputSelectedRect(PerWindowData* pWidgetData, ++ const CFX_FloatRect& rect) override; ++ bool IsSelectionImplemented() const override; ++ void SetCursor(CursorStyle nCursorStyle) override; ++ void QueryWherePopup(const PerWindowData* pAttached, + float fPopupMin, + float fPopupMax, + bool* bBottom, + float* fPopupRet) override; + // Returns {bRC, bExit}. +- std::pair OnBeforeKeyStroke( +- const IPWL_SystemHandler::PerWindowData* pAttached, +- WideString& strChange, +- const WideString& strChangeEx, +- int nSelStart, +- int nSelEnd, +- bool bKeyDown, +- uint32_t nFlag) override; +- bool OnPopupPreOpen(const IPWL_SystemHandler::PerWindowData* pAttached, +- uint32_t nFlag) override; +- bool OnPopupPostOpen(const IPWL_SystemHandler::PerWindowData* pAttached, +- uint32_t nFlag) override; ++ std::pair OnBeforeKeyStroke(const PerWindowData* pAttached, ++ WideString& strChange, ++ const WideString& strChangeEx, ++ int nSelStart, ++ int nSelEnd, ++ bool bKeyDown, ++ Mask nFlag) override; ++ bool OnPopupPreOpen(const PerWindowData* pAttached, ++ Mask nFlag) override; ++ bool OnPopupPostOpen(const PerWindowData* pAttached, ++ Mask nFlag) override; + + #ifdef PDF_ENABLE_XFA +- void SetFocusAnnotTab(CPDFSDK_Annot* pWidget, bool bSameField, bool bNext); +- bool OnClick(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag); +- bool OnFull(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag); +- bool OnPreOpen(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag); +- bool OnPostOpen(ObservedPtr* pAnnot, +- CPDFSDK_PageView* pPageView, +- uint32_t nFlag); ++ void SetFocusAnnotTab(CPDFSDK_Widget* pWidget, bool bSameField, bool bNext); ++ bool OnClick(ObservedPtr& pWidget, ++ const CPDFSDK_PageView* pPageView, ++ Mask nFlag); ++ bool OnFull(ObservedPtr& pAnnot, ++ const CPDFSDK_PageView* pPageView, ++ Mask nFlag); ++ bool OnPreOpen(ObservedPtr& pWidget, ++ const CPDFSDK_PageView* pPageView, ++ Mask nFlag); ++ bool OnPostOpen(ObservedPtr& pWidget, ++ const CPDFSDK_PageView* pPageView, ++ Mask nFlag); + #endif // PDF_ENABLE_XFA + + bool IsFillingAllowed(CPDFSDK_Widget* pWidget) const; +- CFFL_FormFiller* GetFormFiller(CPDFSDK_Annot* pAnnot); +- CFFL_FormFiller* GetOrCreateFormFiller(CPDFSDK_Annot* pAnnot); +- void UnRegisterFormFiller(CPDFSDK_Annot* pAnnot); ++ CFFL_FormField* GetFormField(CPDFSDK_Widget* pWidget); ++ CFFL_FormField* GetOrCreateFormField(CPDFSDK_Widget* pWidget); ++ void UnregisterFormField(CPDFSDK_Widget* pWidget); + +- UnownedPtr const m_pFormFillEnv; ++ UnownedPtr const m_pCallbackIface; + WidgetToFormFillerMap m_Map; + bool m_bNotifying = false; + }; + +-class CFFL_PrivateData final : public IPWL_SystemHandler::PerWindowData { +- public: +- CFFL_PrivateData(); +- CFFL_PrivateData(const CFFL_PrivateData& that); +- ~CFFL_PrivateData() override; +- +- // CPWL_Wnd::PrivateData: +- std::unique_ptr Clone() const override; +- +- CPDFSDK_Widget* GetWidget() const { return pWidget.Get(); } +- +- ObservedPtr pWidget; +- CPDFSDK_PageView* pPageView = nullptr; +- uint32_t nWidgetAppearanceAge = 0; +- uint32_t nWidgetValueAge = 0; +-}; +- + #endif // FPDFSDK_FORMFILLER_CFFL_INTERACTIVEFORMFILLER_H_ +diff --git a/fpdfsdk/formfiller/cffl_listbox.cpp b/fpdfsdk/formfiller/cffl_listbox.cpp +index 3d8025578..31134bb1e 100644 +--- a/fpdfsdk/formfiller/cffl_listbox.cpp ++++ b/fpdfsdk/formfiller/cffl_listbox.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,16 +9,16 @@ + #include + + #include "constants/form_flags.h" +-#include "core/fpdfdoc/cba_fontmap.h" +-#include "fpdfsdk/cpdfsdk_formfillenvironment.h" ++#include "core/fpdfdoc/cpdf_bafontmap.h" + #include "fpdfsdk/cpdfsdk_widget.h" + #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h" ++#include "fpdfsdk/formfiller/cffl_perwindowdata.h" + #include "fpdfsdk/pwl/cpwl_list_box.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/containers/contains.h" + +-CFFL_ListBox::CFFL_ListBox(CPDFSDK_FormFillEnvironment* pApp, ++CFFL_ListBox::CFFL_ListBox(CFFL_InteractiveFormFiller* pFormFiller, + CPDFSDK_Widget* pWidget) +- : CFFL_TextObject(pApp, pWidget) {} ++ : CFFL_TextObject(pFormFiller, pWidget) {} + + CFFL_ListBox::~CFFL_ListBox() = default; + +@@ -35,17 +35,16 @@ CPWL_Wnd::CreateParams CFFL_ListBox::GetCreateParam() { + cp.fFontSize = kDefaultListBoxFontSize; + } + +- cp.pFontMap = MaybeCreateFontMap(); ++ cp.pFontMap = GetOrCreateFontMap(); + return cp; + } + + std::unique_ptr CFFL_ListBox::NewPWLWindow( + const CPWL_Wnd::CreateParams& cp, +- std::unique_ptr pAttachedData) { +- auto pWnd = pdfium::MakeUnique(cp, std::move(pAttachedData)); +- pWnd->AttachFFLData(this); ++ std::unique_ptr pAttachedData) { ++ static_cast(pAttachedData.get())->SetFormField(this); ++ auto pWnd = std::make_unique(cp, std::move(pAttachedData)); + pWnd->Realize(); +- pWnd->SetFillerNotify(m_pFormFillEnv->GetInteractiveFormFiller()); + + for (int32_t i = 0, sz = m_pWidget->CountOptions(); i < sz; i++) + pWnd->AddString(m_pWidget->GetOptionLabel(i)); +@@ -77,14 +76,14 @@ std::unique_ptr CFFL_ListBox::NewPWLWindow( + return std::move(pWnd); + } + +-bool CFFL_ListBox::OnChar(CPDFSDK_Annot* pAnnot, ++bool CFFL_ListBox::OnChar(CPDFSDK_Widget* pWidget, + uint32_t nChar, +- uint32_t nFlags) { +- return CFFL_TextObject::OnChar(pAnnot, nChar, nFlags); ++ Mask nFlags) { ++ return CFFL_TextObject::OnChar(pWidget, nChar, nFlags); + } + +-bool CFFL_ListBox::IsDataChanged(CPDFSDK_PageView* pPageView) { +- CPWL_ListBox* pListBox = GetListBox(pPageView); ++bool CFFL_ListBox::IsDataChanged(const CPDFSDK_PageView* pPageView) { ++ CPWL_ListBox* pListBox = GetPWLListBox(pPageView); + if (!pListBox) + return false; + +@@ -92,7 +91,7 @@ bool CFFL_ListBox::IsDataChanged(CPDFSDK_PageView* pPageView) { + size_t nSelCount = 0; + for (int32_t i = 0, sz = pListBox->GetCount(); i < sz; ++i) { + if (pListBox->IsItemSelected(i)) { +- if (m_OriginSelections.count(i) == 0) ++ if (!pdfium::Contains(m_OriginSelections, i)) + return true; + + ++nSelCount; +@@ -104,50 +103,58 @@ bool CFFL_ListBox::IsDataChanged(CPDFSDK_PageView* pPageView) { + return pListBox->GetCurSel() != m_pWidget->GetSelectedIndex(0); + } + +-void CFFL_ListBox::SaveData(CPDFSDK_PageView* pPageView) { +- CPWL_ListBox* pListBox = GetListBox(pPageView); +- if (!pListBox) ++void CFFL_ListBox::SaveData(const CPDFSDK_PageView* pPageView) { ++ CPWL_ListBox* pListBox = GetPWLListBox(pPageView); ++ if (!pListBox) { + return; +- ++ } + int32_t nNewTopIndex = pListBox->GetTopVisibleIndex(); +- m_pWidget->ClearSelection(NotificationOption::kDoNotNotify); ++ ObservedPtr observed_box(pListBox); ++ m_pWidget->ClearSelection(); ++ if (!observed_box) { ++ return; ++ } + if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceMultiSelect) { + for (int32_t i = 0, sz = pListBox->GetCount(); i < sz; i++) { + if (pListBox->IsItemSelected(i)) { +- m_pWidget->SetOptionSelection(i, true, +- NotificationOption::kDoNotNotify); ++ m_pWidget->SetOptionSelection(i); ++ if (!observed_box) { ++ return; ++ } + } + } + } else { +- m_pWidget->SetOptionSelection(pListBox->GetCurSel(), true, +- NotificationOption::kDoNotNotify); ++ m_pWidget->SetOptionSelection(pListBox->GetCurSel()); ++ if (!observed_box) { ++ return; ++ } + } +- ObservedPtr observed_widget(m_pWidget.Get()); ++ ObservedPtr observed_widget(m_pWidget); + ObservedPtr observed_this(this); + m_pWidget->SetTopVisibleIndex(nNewTopIndex); +- if (!observed_widget) ++ if (!observed_widget) { + return; +- ++ } + m_pWidget->ResetFieldAppearance(); +- if (!observed_widget) ++ if (!observed_widget) { + return; +- ++ } + m_pWidget->UpdateField(); +- if (!observed_widget || !observed_this) ++ if (!observed_widget || !observed_this) { + return; +- ++ } + SetChangeMark(); + } + +-void CFFL_ListBox::GetActionData(CPDFSDK_PageView* pPageView, ++void CFFL_ListBox::GetActionData(const CPDFSDK_PageView* pPageView, + CPDF_AAction::AActionType type, +- CPDFSDK_FieldAction& fa) { ++ CFFL_FieldAction& fa) { + switch (type) { + case CPDF_AAction::kValidate: + if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kChoiceMultiSelect) { + fa.sValue.clear(); + } else { +- CPWL_ListBox* pListBox = GetListBox(pPageView); ++ CPWL_ListBox* pListBox = GetPWLListBox(pPageView); + if (pListBox) { + int32_t nCurSel = pListBox->GetCurSel(); + if (nCurSel >= 0) +@@ -170,8 +177,8 @@ void CFFL_ListBox::GetActionData(CPDFSDK_PageView* pPageView, + } + } + +-void CFFL_ListBox::SaveState(CPDFSDK_PageView* pPageView) { +- CPWL_ListBox* pListBox = GetListBox(pPageView); ++void CFFL_ListBox::SavePWLWindowState(const CPDFSDK_PageView* pPageView) { ++ CPWL_ListBox* pListBox = GetPWLListBox(pPageView); + if (!pListBox) + return; + +@@ -181,8 +188,9 @@ void CFFL_ListBox::SaveState(CPDFSDK_PageView* pPageView) { + } + } + +-void CFFL_ListBox::RestoreState(CPDFSDK_PageView* pPageView) { +- CPWL_ListBox* pListBox = GetListBox(pPageView); ++void CFFL_ListBox::RecreatePWLWindowFromSavedState( ++ const CPDFSDK_PageView* pPageView) { ++ CPWL_ListBox* pListBox = CreateOrUpdatePWLListBox(pPageView); + if (!pListBox) + return; + +@@ -197,7 +205,7 @@ bool CFFL_ListBox::SetIndexSelected(int index, bool selected) { + if (index < 0 || index >= m_pWidget->CountOptions()) + return false; + +- CPWL_ListBox* pListBox = GetListBox(GetCurPageView(true)); ++ CPWL_ListBox* pListBox = GetPWLListBox(GetCurPageView()); + if (!pListBox) + return false; + +@@ -219,10 +227,16 @@ bool CFFL_ListBox::IsIndexSelected(int index) { + if (index < 0 || index >= m_pWidget->CountOptions()) + return false; + +- CPWL_ListBox* pListBox = GetListBox(GetCurPageView(true)); ++ CPWL_ListBox* pListBox = GetPWLListBox(GetCurPageView()); + return pListBox && pListBox->IsItemSelected(index); + } + +-CPWL_ListBox* CFFL_ListBox::GetListBox(CPDFSDK_PageView* pPageView) { +- return static_cast(GetPWLWindow(pPageView, /*bNew=*/false)); ++CPWL_ListBox* CFFL_ListBox::GetPWLListBox( ++ const CPDFSDK_PageView* pPageView) const { ++ return static_cast(GetPWLWindow(pPageView)); ++} ++ ++CPWL_ListBox* CFFL_ListBox::CreateOrUpdatePWLListBox( ++ const CPDFSDK_PageView* pPageView) { ++ return static_cast(CreateOrUpdatePWLWindow(pPageView)); + } +diff --git a/fpdfsdk/formfiller/cffl_listbox.h b/fpdfsdk/formfiller/cffl_listbox.h +index 7f39f5a3c..9e9994640 100644 +--- a/fpdfsdk/formfiller/cffl_listbox.h ++++ b/fpdfsdk/formfiller/cffl_listbox.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -17,28 +17,32 @@ class CPWL_ListBox; + + class CFFL_ListBox final : public CFFL_TextObject { + public: +- CFFL_ListBox(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget); ++ CFFL_ListBox(CFFL_InteractiveFormFiller* pFormFiller, ++ CPDFSDK_Widget* pWidget); + ~CFFL_ListBox() override; + + // CFFL_TextObject: + CPWL_Wnd::CreateParams GetCreateParam() override; + std::unique_ptr NewPWLWindow( + const CPWL_Wnd::CreateParams& cp, +- std::unique_ptr pAttachedData) +- override; +- bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override; +- bool IsDataChanged(CPDFSDK_PageView* pPageView) override; +- void SaveData(CPDFSDK_PageView* pPageView) override; +- void GetActionData(CPDFSDK_PageView* pPageView, ++ std::unique_ptr pAttachedData) override; ++ bool OnChar(CPDFSDK_Widget* pWidget, ++ uint32_t nChar, ++ Mask nFlags) override; ++ bool IsDataChanged(const CPDFSDK_PageView* pPageView) override; ++ void SaveData(const CPDFSDK_PageView* pPageView) override; ++ void GetActionData(const CPDFSDK_PageView* pPageView, + CPDF_AAction::AActionType type, +- CPDFSDK_FieldAction& fa) override; +- void SaveState(CPDFSDK_PageView* pPageView) override; +- void RestoreState(CPDFSDK_PageView* pPageView) override; ++ CFFL_FieldAction& fa) override; ++ void SavePWLWindowState(const CPDFSDK_PageView* pPageView) override; ++ void RecreatePWLWindowFromSavedState( ++ const CPDFSDK_PageView* pPageView) override; + bool SetIndexSelected(int index, bool selected) override; + bool IsIndexSelected(int index) override; + + private: +- CPWL_ListBox* GetListBox(CPDFSDK_PageView* pPageView); ++ CPWL_ListBox* GetPWLListBox(const CPDFSDK_PageView* pPageView) const; ++ CPWL_ListBox* CreateOrUpdatePWLListBox(const CPDFSDK_PageView* pPageView); + + std::set m_OriginSelections; + std::vector m_State; +diff --git a/fpdfsdk/formfiller/cffl_perwindowdata.cpp b/fpdfsdk/formfiller/cffl_perwindowdata.cpp +new file mode 100644 +index 000000000..70dc696f0 +--- /dev/null ++++ b/fpdfsdk/formfiller/cffl_perwindowdata.cpp +@@ -0,0 +1,30 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "fpdfsdk/formfiller/cffl_perwindowdata.h" ++ ++#include "fpdfsdk/cpdfsdk_widget.h" ++#include "third_party/base/ptr_util.h" ++ ++CFFL_PerWindowData::CFFL_PerWindowData(CPDFSDK_Widget* pWidget, ++ const CPDFSDK_PageView* pPageView, ++ uint32_t nAppearanceAge, ++ uint32_t nValueAge) ++ : m_pWidget(pWidget), ++ m_pPageView(pPageView), ++ m_nAppearanceAge(nAppearanceAge), ++ m_nValueAge(nValueAge) {} ++ ++CFFL_PerWindowData::CFFL_PerWindowData(const CFFL_PerWindowData& that) = ++ default; ++ ++CFFL_PerWindowData::~CFFL_PerWindowData() = default; ++ ++std::unique_ptr CFFL_PerWindowData::Clone() ++ const { ++ // Private constructor. ++ return pdfium::WrapUnique(new CFFL_PerWindowData(*this)); ++} +diff --git a/fpdfsdk/formfiller/cffl_perwindowdata.h b/fpdfsdk/formfiller/cffl_perwindowdata.h +new file mode 100644 +index 000000000..b535027eb +--- /dev/null ++++ b/fpdfsdk/formfiller/cffl_perwindowdata.h +@@ -0,0 +1,52 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef FPDFSDK_FORMFILLER_CFFL_PERWINDOWDATA_H_ ++#define FPDFSDK_FORMFILLER_CFFL_PERWINDOWDATA_H_ ++ ++#include ++ ++#include "core/fxcrt/observed_ptr.h" ++#include "core/fxcrt/unowned_ptr.h" ++#include "fpdfsdk/pwl/ipwl_fillernotify.h" ++ ++class CFFL_FormField; ++class CPDFSDK_PageView; ++class CPDFSDK_Widget; ++ ++class CFFL_PerWindowData final : public IPWL_FillerNotify::PerWindowData { ++ public: ++ CFFL_PerWindowData(CPDFSDK_Widget* pWidget, ++ const CPDFSDK_PageView* pPageView, ++ uint32_t nAppearanceAge, ++ uint32_t nValueAge); ++ CFFL_PerWindowData& operator=(const CFFL_PerWindowData& that) = delete; ++ ~CFFL_PerWindowData() override; ++ ++ // IPWL_FillerNotify::PerWindowData: ++ std::unique_ptr Clone() const override; ++ ++ CPDFSDK_Widget* GetWidget() const { return m_pWidget.Get(); } ++ const CPDFSDK_PageView* GetPageView() const { return m_pPageView; } ++ bool AppearanceAgeEquals(uint32_t age) const { ++ return age == m_nAppearanceAge; ++ } ++ uint32_t GetValueAge() const { return m_nValueAge; } ++ ++ void SetFormField(CFFL_FormField* pFormField) { m_pFormField = pFormField; } ++ CFFL_FormField* GetFormField() { return m_pFormField; } ++ ++ private: ++ CFFL_PerWindowData(const CFFL_PerWindowData& that); ++ ++ ObservedPtr m_pWidget; ++ UnownedPtr const m_pPageView; ++ UnownedPtr m_pFormField; ++ const uint32_t m_nAppearanceAge; ++ const uint32_t m_nValueAge; ++}; ++ ++#endif // FPDFSDK_FORMFILLER_CFFL_PERWINDOWDATA_H_ +diff --git a/fpdfsdk/formfiller/cffl_pushbutton.cpp b/fpdfsdk/formfiller/cffl_pushbutton.cpp +index e9872f6b2..28892b01f 100644 +--- a/fpdfsdk/formfiller/cffl_pushbutton.cpp ++++ b/fpdfsdk/formfiller/cffl_pushbutton.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,20 +8,19 @@ + + #include + +-#include "fpdfsdk/formfiller/cffl_formfiller.h" ++#include "fpdfsdk/formfiller/cffl_formfield.h" + #include "fpdfsdk/pwl/cpwl_special_button.h" +-#include "third_party/base/ptr_util.h" + +-CFFL_PushButton::CFFL_PushButton(CPDFSDK_FormFillEnvironment* pApp, ++CFFL_PushButton::CFFL_PushButton(CFFL_InteractiveFormFiller* pFormFiller, + CPDFSDK_Widget* pWidget) +- : CFFL_Button(pApp, pWidget) {} ++ : CFFL_Button(pFormFiller, pWidget) {} + + CFFL_PushButton::~CFFL_PushButton() = default; + + std::unique_ptr CFFL_PushButton::NewPWLWindow( + const CPWL_Wnd::CreateParams& cp, +- std::unique_ptr pAttachedData) { +- auto pWnd = pdfium::MakeUnique(cp, std::move(pAttachedData)); ++ std::unique_ptr pAttachedData) { ++ auto pWnd = std::make_unique(cp, std::move(pAttachedData)); + pWnd->Realize(); + return std::move(pWnd); + } +diff --git a/fpdfsdk/formfiller/cffl_pushbutton.h b/fpdfsdk/formfiller/cffl_pushbutton.h +index 9ebaf6076..a9ecc80ac 100644 +--- a/fpdfsdk/formfiller/cffl_pushbutton.h ++++ b/fpdfsdk/formfiller/cffl_pushbutton.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,14 +13,14 @@ + + class CFFL_PushButton final : public CFFL_Button { + public: +- CFFL_PushButton(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget); ++ CFFL_PushButton(CFFL_InteractiveFormFiller* pFormFiller, ++ CPDFSDK_Widget* pWidget); + ~CFFL_PushButton() override; + + // CFFL_Button: + std::unique_ptr NewPWLWindow( + const CPWL_Wnd::CreateParams& cp, +- std::unique_ptr pAttachedData) +- override; ++ std::unique_ptr pAttachedData) override; + }; + + #endif // FPDFSDK_FORMFILLER_CFFL_PUSHBUTTON_H_ +diff --git a/fpdfsdk/formfiller/cffl_radiobutton.cpp b/fpdfsdk/formfiller/cffl_radiobutton.cpp +index caa634909..9d11e866a 100644 +--- a/fpdfsdk/formfiller/cffl_radiobutton.cpp ++++ b/fpdfsdk/formfiller/cffl_radiobutton.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,105 +8,96 @@ + + #include + ++#include "constants/ascii.h" + #include "core/fpdfdoc/cpdf_formcontrol.h" +-#include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fpdfsdk/cpdfsdk_widget.h" +-#include "fpdfsdk/formfiller/cffl_formfiller.h" ++#include "fpdfsdk/formfiller/cffl_formfield.h" + #include "fpdfsdk/pwl/cpwl_special_button.h" + #include "public/fpdf_fwlevent.h" ++#include "third_party/base/check.h" + +-CFFL_RadioButton::CFFL_RadioButton(CPDFSDK_FormFillEnvironment* pApp, ++CFFL_RadioButton::CFFL_RadioButton(CFFL_InteractiveFormFiller* pFormFiller, + CPDFSDK_Widget* pWidget) +- : CFFL_Button(pApp, pWidget) {} ++ : CFFL_Button(pFormFiller, pWidget) {} + +-CFFL_RadioButton::~CFFL_RadioButton() {} ++CFFL_RadioButton::~CFFL_RadioButton() = default; + + std::unique_ptr CFFL_RadioButton::NewPWLWindow( + const CPWL_Wnd::CreateParams& cp, +- std::unique_ptr pAttachedData) { +- auto pWnd = +- pdfium::MakeUnique(cp, std::move(pAttachedData)); ++ std::unique_ptr pAttachedData) { ++ auto pWnd = std::make_unique(cp, std::move(pAttachedData)); + pWnd->Realize(); + pWnd->SetCheck(m_pWidget->IsChecked()); + return std::move(pWnd); + } + +-bool CFFL_RadioButton::OnKeyDown(uint32_t nKeyCode, uint32_t nFlags) { ++bool CFFL_RadioButton::OnKeyDown(FWL_VKEYCODE nKeyCode, ++ Mask nFlags) { + switch (nKeyCode) { + case FWL_VKEY_Return: + case FWL_VKEY_Space: + return true; + default: +- return CFFL_FormFiller::OnKeyDown(nKeyCode, nFlags); ++ return CFFL_FormField::OnKeyDown(nKeyCode, nFlags); + } + } + +-bool CFFL_RadioButton::OnChar(CPDFSDK_Annot* pAnnot, ++bool CFFL_RadioButton::OnChar(CPDFSDK_Widget* pWidget, + uint32_t nChar, +- uint32_t nFlags) { ++ Mask nFlags) { + switch (nChar) { +- case FWL_VKEY_Return: +- case FWL_VKEY_Space: { +- CPDFSDK_PageView* pPageView = pAnnot->GetPageView(); +- ASSERT(pPageView); ++ case pdfium::ascii::kReturn: ++ case pdfium::ascii::kSpace: { ++ CPDFSDK_PageView* pPageView = pWidget->GetPageView(); ++ DCHECK(pPageView); + +- ObservedPtr pObserved(m_pWidget.Get()); +- if (m_pFormFillEnv->GetInteractiveFormFiller()->OnButtonUp( +- &pObserved, pPageView, nFlags) || ++ ObservedPtr pObserved(m_pWidget); ++ if (m_pFormFiller->OnButtonUp(pObserved, pPageView, nFlags) || + !pObserved) { + return true; + } + +- CFFL_FormFiller::OnChar(pAnnot, nChar, nFlags); +- CPWL_RadioButton* pWnd = GetRadioButton(pPageView, true); +- if (pWnd) ++ CFFL_FormField::OnChar(pWidget, nChar, nFlags); ++ CPWL_RadioButton* pWnd = CreateOrUpdatePWLRadioButton(pPageView); ++ if (pWnd && !pWnd->IsReadOnly()) + pWnd->SetCheck(true); + return CommitData(pPageView, nFlags); + } + default: +- return CFFL_FormFiller::OnChar(pAnnot, nChar, nFlags); ++ return CFFL_FormField::OnChar(pWidget, nChar, nFlags); + } + } + + bool CFFL_RadioButton::OnLButtonUp(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- uint32_t nFlags, ++ CPDFSDK_Widget* pWidget, ++ Mask nFlags, + const CFX_PointF& point) { +- CFFL_Button::OnLButtonUp(pPageView, pAnnot, nFlags, point); ++ CFFL_Button::OnLButtonUp(pPageView, pWidget, nFlags, point); + + if (!IsValid()) + return true; + +- CPWL_RadioButton* pWnd = GetRadioButton(pPageView, true); ++ CPWL_RadioButton* pWnd = CreateOrUpdatePWLRadioButton(pPageView); + if (pWnd) + pWnd->SetCheck(true); + + return CommitData(pPageView, nFlags); + } + +-bool CFFL_RadioButton::IsDataChanged(CPDFSDK_PageView* pPageView) { +- CPWL_RadioButton* pWnd = GetRadioButton(pPageView, false); ++bool CFFL_RadioButton::IsDataChanged(const CPDFSDK_PageView* pPageView) { ++ CPWL_RadioButton* pWnd = GetPWLRadioButton(pPageView); + return pWnd && pWnd->IsChecked() != m_pWidget->IsChecked(); + } + +-void CFFL_RadioButton::SaveData(CPDFSDK_PageView* pPageView) { +- CPWL_RadioButton* pWnd = GetRadioButton(pPageView, false); ++void CFFL_RadioButton::SaveData(const CPDFSDK_PageView* pPageView) { ++ CPWL_RadioButton* pWnd = GetPWLRadioButton(pPageView); + if (!pWnd) + return; + + bool bNewChecked = pWnd->IsChecked(); +- if (bNewChecked) { +- CPDF_FormField* pField = m_pWidget->GetFormField(); +- for (int32_t i = 0, sz = pField->CountControls(); i < sz; i++) { +- if (CPDF_FormControl* pCtrl = pField->GetControl(i)) { +- if (pCtrl->IsChecked()) +- break; +- } +- } +- } +- ObservedPtr observed_widget(m_pWidget.Get()); ++ ObservedPtr observed_widget(m_pWidget); + ObservedPtr observed_this(this); +- m_pWidget->SetCheck(bNewChecked, NotificationOption::kDoNotNotify); ++ m_pWidget->SetCheck(bNewChecked); + if (!observed_widget) + return; + +@@ -117,7 +108,12 @@ void CFFL_RadioButton::SaveData(CPDFSDK_PageView* pPageView) { + SetChangeMark(); + } + +-CPWL_RadioButton* CFFL_RadioButton::GetRadioButton(CPDFSDK_PageView* pPageView, +- bool bNew) { +- return static_cast(GetPWLWindow(pPageView, bNew)); ++CPWL_RadioButton* CFFL_RadioButton::GetPWLRadioButton( ++ const CPDFSDK_PageView* pPageView) const { ++ return static_cast(GetPWLWindow(pPageView)); ++} ++ ++CPWL_RadioButton* CFFL_RadioButton::CreateOrUpdatePWLRadioButton( ++ const CPDFSDK_PageView* pPageView) { ++ return static_cast(CreateOrUpdatePWLWindow(pPageView)); + } +diff --git a/fpdfsdk/formfiller/cffl_radiobutton.h b/fpdfsdk/formfiller/cffl_radiobutton.h +index 8ba2f285c..016d2826d 100644 +--- a/fpdfsdk/formfiller/cffl_radiobutton.h ++++ b/fpdfsdk/formfiller/cffl_radiobutton.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -15,25 +15,29 @@ class CPWL_RadioButton; + + class CFFL_RadioButton final : public CFFL_Button { + public: +- CFFL_RadioButton(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget); ++ CFFL_RadioButton(CFFL_InteractiveFormFiller* pFormFiller, ++ CPDFSDK_Widget* pWidget); + ~CFFL_RadioButton() override; + + // CFFL_Button: + std::unique_ptr NewPWLWindow( + const CPWL_Wnd::CreateParams& cp, +- std::unique_ptr pAttachedData) +- override; +- bool OnKeyDown(uint32_t nKeyCode, uint32_t nFlags) override; +- bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override; ++ std::unique_ptr pAttachedData) override; ++ bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask nFlags) override; ++ bool OnChar(CPDFSDK_Widget* pWidget, ++ uint32_t nChar, ++ Mask nFlags) override; + bool OnLButtonUp(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- uint32_t nFlags, ++ CPDFSDK_Widget* pWidget, ++ Mask nFlags, + const CFX_PointF& point) override; +- bool IsDataChanged(CPDFSDK_PageView* pPageView) override; +- void SaveData(CPDFSDK_PageView* pPageView) override; ++ bool IsDataChanged(const CPDFSDK_PageView* pPageView) override; ++ void SaveData(const CPDFSDK_PageView* pPageView) override; + + private: +- CPWL_RadioButton* GetRadioButton(CPDFSDK_PageView* pPageView, bool bNew); ++ CPWL_RadioButton* GetPWLRadioButton(const CPDFSDK_PageView* pPageView) const; ++ CPWL_RadioButton* CreateOrUpdatePWLRadioButton( ++ const CPDFSDK_PageView* pPageView); + }; + + #endif // FPDFSDK_FORMFILLER_CFFL_RADIOBUTTON_H_ +diff --git a/fpdfsdk/formfiller/cffl_textfield.cpp b/fpdfsdk/formfiller/cffl_textfield.cpp +index c3cd365c1..8096c8248 100644 +--- a/fpdfsdk/formfiller/cffl_textfield.cpp ++++ b/fpdfsdk/formfiller/cffl_textfield.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,12 +8,14 @@ + + #include + ++#include "constants/ascii.h" + #include "constants/form_flags.h" +-#include "core/fpdfdoc/cba_fontmap.h" +-#include "fpdfsdk/cpdfsdk_formfillenvironment.h" ++#include "core/fpdfdoc/cpdf_bafontmap.h" + #include "fpdfsdk/cpdfsdk_widget.h" ++#include "fpdfsdk/formfiller/cffl_perwindowdata.h" ++#include "fpdfsdk/pwl/cpwl_edit.h" + #include "public/fpdf_fwlevent.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -26,14 +28,11 @@ enum Alignment { + + } // namespace + +-CFFL_TextField::CFFL_TextField(CPDFSDK_FormFillEnvironment* pApp, ++CFFL_TextField::CFFL_TextField(CFFL_InteractiveFormFiller* pFormFiller, + CPDFSDK_Widget* pWidget) +- : CFFL_TextObject(pApp, pWidget) {} ++ : CFFL_TextObject(pFormFiller, pWidget) {} + + CFFL_TextField::~CFFL_TextField() { +- for (const auto& it : m_Maps) +- it.second->InvalidateFocusHandler(this); +- + // See comment in cffl_formfiller.h. + // The font map should be stored somewhere more appropriate so it will live + // until the PWL_Edit is done with it. pdfium:566 +@@ -76,18 +75,16 @@ CPWL_Wnd::CreateParams CFFL_TextField::GetCreateParam() { + cp.dwFlags |= PES_RIGHT; + break; + } +- cp.pFontMap = MaybeCreateFontMap(); +- cp.pFocusHandler = this; ++ cp.pFontMap = GetOrCreateFontMap(); + return cp; + } + + std::unique_ptr CFFL_TextField::NewPWLWindow( + const CPWL_Wnd::CreateParams& cp, +- std::unique_ptr pAttachedData) { +- auto pWnd = pdfium::MakeUnique(cp, std::move(pAttachedData)); +- pWnd->AttachFFLData(this); ++ std::unique_ptr pAttachedData) { ++ static_cast(pAttachedData.get())->SetFormField(this); ++ auto pWnd = std::make_unique(cp, std::move(pAttachedData)); + pWnd->Realize(); +- pWnd->SetFillerNotify(m_pFormFillEnv->GetInteractiveFormFiller()); + + int32_t nMaxLen = m_pWidget->GetMaxLen(); + WideString swValue = m_pWidget->GetValue(); +@@ -103,22 +100,21 @@ std::unique_ptr CFFL_TextField::NewPWLWindow( + return std::move(pWnd); + } + +-bool CFFL_TextField::OnChar(CPDFSDK_Annot* pAnnot, ++bool CFFL_TextField::OnChar(CPDFSDK_Widget* pWidget, + uint32_t nChar, +- uint32_t nFlags) { ++ Mask nFlags) { + switch (nChar) { +- case FWL_VKEY_Return: { ++ case pdfium::ascii::kReturn: { + if (m_pWidget->GetFieldFlags() & pdfium::form_flags::kTextMultiline) + break; + +- CPDFSDK_PageView* pPageView = GetCurPageView(true); +- ASSERT(pPageView); ++ CPDFSDK_PageView* pPageView = GetCurPageView(); ++ DCHECK(pPageView); + m_bValid = !m_bValid; +- m_pFormFillEnv->Invalidate(pAnnot->GetPage(), +- pAnnot->GetRect().GetOuterRect()); +- ++ m_pFormFiller->Invalidate(pWidget->GetPage(), ++ pWidget->GetRect().GetOuterRect()); + if (m_bValid) { +- if (CPWL_Wnd* pWnd = GetPWLWindow(pPageView, true)) ++ if (CPWL_Wnd* pWnd = CreateOrUpdatePWLWindow(pPageView)) + pWnd->SetFocus(); + break; + } +@@ -129,56 +125,57 @@ bool CFFL_TextField::OnChar(CPDFSDK_Annot* pAnnot, + DestroyPWLWindow(pPageView); + return true; + } +- case FWL_VKEY_Escape: { +- CPDFSDK_PageView* pPageView = GetCurPageView(true); +- ASSERT(pPageView); ++ case pdfium::ascii::kEscape: { ++ CPDFSDK_PageView* pPageView = GetCurPageView(); ++ DCHECK(pPageView); + EscapeFiller(pPageView, true); + return true; + } + } + +- return CFFL_TextObject::OnChar(pAnnot, nChar, nFlags); ++ return CFFL_TextObject::OnChar(pWidget, nChar, nFlags); + } + +-bool CFFL_TextField::IsDataChanged(CPDFSDK_PageView* pPageView) { +- CPWL_Edit* pEdit = GetEdit(pPageView, false); ++bool CFFL_TextField::IsDataChanged(const CPDFSDK_PageView* pPageView) { ++ CPWL_Edit* pEdit = GetPWLEdit(pPageView); + return pEdit && pEdit->GetText() != m_pWidget->GetValue(); + } + +-void CFFL_TextField::SaveData(CPDFSDK_PageView* pPageView) { +- CPWL_Edit* pWnd = GetEdit(pPageView, false); +- if (!pWnd) ++void CFFL_TextField::SaveData(const CPDFSDK_PageView* pPageView) { ++ ObservedPtr observed_edit(GetPWLEdit(pPageView)); ++ if (!observed_edit) { + return; +- ++ } + WideString sOldValue = m_pWidget->GetValue(); +- WideString sNewValue = pWnd->GetText(); +- ObservedPtr observed_widget(m_pWidget.Get()); ++ if (!observed_edit) { ++ return; ++ } ++ WideString sNewValue = observed_edit->GetText(); ++ ObservedPtr observed_widget(m_pWidget); + ObservedPtr observed_this(this); +- m_pWidget->SetValue(sNewValue, NotificationOption::kDoNotNotify); +- if (!observed_widget) ++ m_pWidget->SetValue(sNewValue); ++ if (!observed_widget) { + return; +- ++ } + m_pWidget->ResetFieldAppearance(); +- if (!observed_widget) ++ if (!observed_widget) { + return; +- ++ } + m_pWidget->UpdateField(); +- if (!observed_widget || !observed_this) ++ if (!observed_widget || !observed_this) { + return; +- ++ } + SetChangeMark(); + } + +-void CFFL_TextField::GetActionData(CPDFSDK_PageView* pPageView, ++void CFFL_TextField::GetActionData(const CPDFSDK_PageView* pPageView, + CPDF_AAction::AActionType type, +- CPDFSDK_FieldAction& fa) { ++ CFFL_FieldAction& fa) { + switch (type) { + case CPDF_AAction::kKeyStroke: +- if (CPWL_Edit* pWnd = GetEdit(pPageView, false)) { ++ if (CPWL_Edit* pWnd = GetPWLEdit(pPageView)) { + fa.bFieldFull = pWnd->IsTextFull(); +- + fa.sValue = pWnd->GetText(); +- + if (fa.bFieldFull) { + fa.sChange.clear(); + fa.sChangeEx.clear(); +@@ -186,7 +183,7 @@ void CFFL_TextField::GetActionData(CPDFSDK_PageView* pPageView, + } + break; + case CPDF_AAction::kValidate: +- if (CPWL_Edit* pWnd = GetEdit(pPageView, false)) { ++ if (CPWL_Edit* pWnd = GetPWLEdit(pPageView)) { + fa.sValue = pWnd->GetText(); + } + break; +@@ -199,12 +196,12 @@ void CFFL_TextField::GetActionData(CPDFSDK_PageView* pPageView, + } + } + +-void CFFL_TextField::SetActionData(CPDFSDK_PageView* pPageView, ++void CFFL_TextField::SetActionData(const CPDFSDK_PageView* pPageView, + CPDF_AAction::AActionType type, +- const CPDFSDK_FieldAction& fa) { ++ const CFFL_FieldAction& fa) { + switch (type) { + case CPDF_AAction::kKeyStroke: +- if (CPWL_Edit* pEdit = GetEdit(pPageView, false)) { ++ if (CPWL_Edit* pEdit = GetPWLEdit(pPageView)) { + pEdit->SetFocus(); + pEdit->SetSelection(fa.nSelStart, fa.nSelEnd); + pEdit->ReplaceSelection(fa.sChange); +@@ -215,17 +212,18 @@ void CFFL_TextField::SetActionData(CPDFSDK_PageView* pPageView, + } + } + +-void CFFL_TextField::SaveState(CPDFSDK_PageView* pPageView) { +- CPWL_Edit* pWnd = GetEdit(pPageView, false); ++void CFFL_TextField::SavePWLWindowState(const CPDFSDK_PageView* pPageView) { ++ CPWL_Edit* pWnd = GetPWLEdit(pPageView); + if (!pWnd) + return; + +- pWnd->GetSelection(m_State.nStart, m_State.nEnd); ++ std::tie(m_State.nStart, m_State.nEnd) = pWnd->GetSelection(); + m_State.sValue = pWnd->GetText(); + } + +-void CFFL_TextField::RestoreState(CPDFSDK_PageView* pPageView) { +- CPWL_Edit* pWnd = GetEdit(pPageView, true); ++void CFFL_TextField::RecreatePWLWindowFromSavedState( ++ const CPDFSDK_PageView* pPageView) { ++ CPWL_Edit* pWnd = CreateOrUpdatePWLEdit(pPageView); + if (!pWnd) + return; + +@@ -234,23 +232,23 @@ void CFFL_TextField::RestoreState(CPDFSDK_PageView* pPageView) { + } + + #ifdef PDF_ENABLE_XFA +-bool CFFL_TextField::IsFieldFull(CPDFSDK_PageView* pPageView) { +- CPWL_Edit* pWnd = GetEdit(pPageView, false); ++bool CFFL_TextField::IsFieldFull(const CPDFSDK_PageView* pPageView) { ++ CPWL_Edit* pWnd = GetPWLEdit(pPageView); + return pWnd && pWnd->IsTextFull(); + } + #endif // PDF_ENABLE_XFA + +-void CFFL_TextField::OnSetFocus(CPWL_Edit* pEdit) { +- pEdit->SetCharSet(FX_CHARSET_ChineseSimplified); ++void CFFL_TextField::OnSetFocusForEdit(CPWL_Edit* pEdit) { ++ pEdit->SetCharSet(FX_Charset::kChineseSimplified); + pEdit->SetReadyToInput(); ++ m_pFormFiller->OnSetFieldInputFocus(pEdit->GetText()); ++} + +- WideString wsText = pEdit->GetText(); +- int nCharacters = wsText.GetLength(); +- ByteString bsUTFText = wsText.ToUTF16LE(); +- auto* pBuffer = reinterpret_cast(bsUTFText.c_str()); +- m_pFormFillEnv->OnSetFieldInputFocus(pBuffer, nCharacters, true); ++CPWL_Edit* CFFL_TextField::GetPWLEdit(const CPDFSDK_PageView* pPageView) const { ++ return static_cast(GetPWLWindow(pPageView)); + } + +-CPWL_Edit* CFFL_TextField::GetEdit(CPDFSDK_PageView* pPageView, bool bNew) { +- return static_cast(GetPWLWindow(pPageView, bNew)); ++CPWL_Edit* CFFL_TextField::CreateOrUpdatePWLEdit( ++ const CPDFSDK_PageView* pPageView) { ++ return static_cast(CreateOrUpdatePWLWindow(pPageView)); + } +diff --git a/fpdfsdk/formfiller/cffl_textfield.h b/fpdfsdk/formfiller/cffl_textfield.h +index 5941b4267..f7f5d37dc 100644 +--- a/fpdfsdk/formfiller/cffl_textfield.h ++++ b/fpdfsdk/formfiller/cffl_textfield.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -19,38 +19,41 @@ struct FFL_TextFieldState { + WideString sValue; + }; + +-class CFFL_TextField final : public CFFL_TextObject, +- public CPWL_Wnd::FocusHandlerIface { ++class CFFL_TextField final : public CFFL_TextObject { + public: +- CFFL_TextField(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget); ++ CFFL_TextField(CFFL_InteractiveFormFiller* pFormFiller, ++ CPDFSDK_Widget* pWidget); + ~CFFL_TextField() override; + + // CFFL_TextObject: + CPWL_Wnd::CreateParams GetCreateParam() override; + std::unique_ptr NewPWLWindow( + const CPWL_Wnd::CreateParams& cp, +- std::unique_ptr pAttachedData) +- override; +- bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override; +- bool IsDataChanged(CPDFSDK_PageView* pPageView) override; +- void SaveData(CPDFSDK_PageView* pPageView) override; +- void GetActionData(CPDFSDK_PageView* pPageView, ++ std::unique_ptr pAttachedData) override; ++ bool OnChar(CPDFSDK_Widget* pWidget, ++ uint32_t nChar, ++ Mask nFlags) override; ++ bool IsDataChanged(const CPDFSDK_PageView* pPageView) override; ++ void SaveData(const CPDFSDK_PageView* pPageView) override; ++ void GetActionData(const CPDFSDK_PageView* pPageView, + CPDF_AAction::AActionType type, +- CPDFSDK_FieldAction& fa) override; +- void SetActionData(CPDFSDK_PageView* pPageView, ++ CFFL_FieldAction& fa) override; ++ void SetActionData(const CPDFSDK_PageView* pPageView, + CPDF_AAction::AActionType type, +- const CPDFSDK_FieldAction& fa) override; +- void SaveState(CPDFSDK_PageView* pPageView) override; +- void RestoreState(CPDFSDK_PageView* pPageView) override; ++ const CFFL_FieldAction& fa) override; ++ void SavePWLWindowState(const CPDFSDK_PageView* pPageView) override; ++ void RecreatePWLWindowFromSavedState( ++ const CPDFSDK_PageView* pPageView) override; + #ifdef PDF_ENABLE_XFA +- bool IsFieldFull(CPDFSDK_PageView* pPageView) override; ++ bool IsFieldFull(const CPDFSDK_PageView* pPageView) override; + #endif + +- // CPWL_Wnd::FocusHandlerIface: +- void OnSetFocus(CPWL_Edit* pEdit) override; ++ // CPWL_Wnd::ProviderIface: ++ void OnSetFocusForEdit(CPWL_Edit* pEdit) override; + + private: +- CPWL_Edit* GetEdit(CPDFSDK_PageView* pPageView, bool bNew); ++ CPWL_Edit* GetPWLEdit(const CPDFSDK_PageView* pPageView) const; ++ CPWL_Edit* CreateOrUpdatePWLEdit(const CPDFSDK_PageView* pPageView); + + FFL_TextFieldState m_State; + }; +diff --git a/fpdfsdk/formfiller/cffl_textobject.cpp b/fpdfsdk/formfiller/cffl_textobject.cpp +index 6d8496076..a7b7cd9f6 100644 +--- a/fpdfsdk/formfiller/cffl_textobject.cpp ++++ b/fpdfsdk/formfiller/cffl_textobject.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,26 +7,12 @@ + #include "fpdfsdk/formfiller/cffl_textobject.h" + + #include "core/fpdfapi/page/cpdf_page.h" +-#include "core/fpdfdoc/cba_fontmap.h" +-#include "third_party/base/ptr_util.h" ++#include "core/fpdfdoc/cpdf_bafontmap.h" ++#include "fpdfsdk/cpdfsdk_widget.h" + +-CPWL_Wnd* CFFL_TextObject::ResetPWLWindow(CPDFSDK_PageView* pPageView, +- bool bRestoreValue) { +- if (bRestoreValue) +- SaveState(pPageView); +- +- DestroyPWLWindow(pPageView); +- if (bRestoreValue) +- RestoreState(pPageView); +- +- ObservedPtr pRet(GetPWLWindow(pPageView, !bRestoreValue)); +- m_pWidget->UpdateField(); // May invoke JS, invalidating |pRet|. +- return pRet.Get(); +-} +- +-CFFL_TextObject::CFFL_TextObject(CPDFSDK_FormFillEnvironment* pApp, ++CFFL_TextObject::CFFL_TextObject(CFFL_InteractiveFormFiller* pFormFiller, + CPDFSDK_Widget* pWidget) +- : CFFL_FormFiller(pApp, pWidget) {} ++ : CFFL_FormField(pFormFiller, pWidget) {} + + CFFL_TextObject::~CFFL_TextObject() { + // Destroy view classes before this object's members are destroyed since +@@ -34,11 +20,27 @@ CFFL_TextObject::~CFFL_TextObject() { + DestroyWindows(); + } + +-CBA_FontMap* CFFL_TextObject::MaybeCreateFontMap() { ++CPWL_Wnd* CFFL_TextObject::ResetPWLWindow(const CPDFSDK_PageView* pPageView) { ++ DestroyPWLWindow(pPageView); ++ ObservedPtr pRet(CreateOrUpdatePWLWindow(pPageView)); ++ m_pWidget->UpdateField(); // May invoke JS, invalidating |pRet|. ++ return pRet.Get(); ++} ++ ++CPWL_Wnd* CFFL_TextObject::RestorePWLWindow(const CPDFSDK_PageView* pPageView) { ++ SavePWLWindowState(pPageView); ++ DestroyPWLWindow(pPageView); ++ RecreatePWLWindowFromSavedState(pPageView); ++ ObservedPtr pRet(GetPWLWindow(pPageView)); ++ m_pWidget->UpdateField(); // May invoke JS, invalidating |pRet|. ++ return pRet.Get(); ++} ++ ++CPDF_BAFontMap* CFFL_TextObject::GetOrCreateFontMap() { + if (!m_pFontMap) { +- m_pFontMap = pdfium::MakeUnique( ++ m_pFontMap = std::make_unique( + m_pWidget->GetPDFPage()->GetDocument(), +- m_pWidget->GetPDFAnnot()->GetAnnotDict()); ++ m_pWidget->GetPDFAnnot()->GetMutableAnnotDict(), "N"); + } + return m_pFontMap.get(); + } +diff --git a/fpdfsdk/formfiller/cffl_textobject.h b/fpdfsdk/formfiller/cffl_textobject.h +index a2381afb4..f2dc1b1e7 100644 +--- a/fpdfsdk/formfiller/cffl_textobject.h ++++ b/fpdfsdk/formfiller/cffl_textobject.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,26 +9,27 @@ + + #include + +-#include "fpdfsdk/formfiller/cffl_formfiller.h" ++#include "fpdfsdk/formfiller/cffl_formfield.h" + +-class CBA_FontMap; ++class CPDF_BAFontMap; + +-// Class to implement common functionality for CFFL_FormFiller sub-classes with ++// Class to implement common functionality for CFFL_FormField sub-classes with + // text fields. +-class CFFL_TextObject : public CFFL_FormFiller { ++class CFFL_TextObject : public CFFL_FormField { + public: +- // CFFL_FormFiller: +- CPWL_Wnd* ResetPWLWindow(CPDFSDK_PageView* pPageView, +- bool bRestoreValue) override; ++ // CFFL_FormField: ++ CPWL_Wnd* ResetPWLWindow(const CPDFSDK_PageView* pPageView) override; ++ CPWL_Wnd* RestorePWLWindow(const CPDFSDK_PageView* pPageView) override; + + protected: +- CFFL_TextObject(CPDFSDK_FormFillEnvironment* pApp, CPDFSDK_Widget* pWidget); ++ CFFL_TextObject(CFFL_InteractiveFormFiller* pFormFiller, ++ CPDFSDK_Widget* pWidget); + ~CFFL_TextObject() override; + +- CBA_FontMap* MaybeCreateFontMap(); ++ CPDF_BAFontMap* GetOrCreateFontMap(); + + private: +- std::unique_ptr m_pFontMap; ++ std::unique_ptr m_pFontMap; + }; + + #endif // FPDFSDK_FORMFILLER_CFFL_TEXTOBJECT_H_ +diff --git a/fpdfsdk/fpdf_annot.cpp b/fpdfsdk/fpdf_annot.cpp +index c08ba5a69..7c7d42044 100644 +--- a/fpdfsdk/fpdf_annot.cpp ++++ b/fpdfsdk/fpdf_annot.cpp +@@ -1,11 +1,13 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "public/fpdf_annot.h" + + #include ++#include + #include ++#include + + #include "constants/annotation_common.h" + #include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h" +@@ -22,16 +24,23 @@ + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_string.h" ++#include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fpdfdoc/cpdf_annot.h" + #include "core/fpdfdoc/cpdf_color_utils.h" + #include "core/fpdfdoc/cpdf_formfield.h" ++#include "core/fpdfdoc/cpdf_generateap.h" + #include "core/fpdfdoc/cpdf_interactiveform.h" +-#include "core/fpdfdoc/cpvt_generateap.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/fx_string_wrappers.h" ++#include "core/fxcrt/stl_util.h" + #include "core/fxge/cfx_color.h" ++#include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "fpdfsdk/cpdfsdk_interactiveform.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/numerics/safe_conversions.h" + #include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" + + namespace { + +@@ -112,16 +121,19 @@ static_assert(static_cast(CPDF_Annot::Subtype::RICHMEDIA) == + static_assert(static_cast(CPDF_Annot::Subtype::XFAWIDGET) == + FPDF_ANNOT_XFAWIDGET, + "CPDF_Annot::XFAWIDGET value mismatch"); ++static_assert(static_cast(CPDF_Annot::Subtype::REDACT) == ++ FPDF_ANNOT_REDACT, ++ "CPDF_Annot::REDACT value mismatch"); + + // These checks ensure the consistency of annotation appearance mode values + // across core/ and public. +-static_assert(static_cast(CPDF_Annot::AppearanceMode::Normal) == ++static_assert(static_cast(CPDF_Annot::AppearanceMode::kNormal) == + FPDF_ANNOT_APPEARANCEMODE_NORMAL, + "CPDF_Annot::AppearanceMode::Normal value mismatch"); +-static_assert(static_cast(CPDF_Annot::AppearanceMode::Rollover) == ++static_assert(static_cast(CPDF_Annot::AppearanceMode::kRollover) == + FPDF_ANNOT_APPEARANCEMODE_ROLLOVER, + "CPDF_Annot::AppearanceMode::Rollover value mismatch"); +-static_assert(static_cast(CPDF_Annot::AppearanceMode::Down) == ++static_assert(static_cast(CPDF_Annot::AppearanceMode::kDown) == + FPDF_ANNOT_APPEARANCEMODE_DOWN, + "CPDF_Annot::AppearanceMode::Down value mismatch"); + +@@ -153,16 +165,31 @@ static_assert(static_cast(CPDF_Object::Type::kReference) == + FPDF_OBJECT_REFERENCE, + "CPDF_Object::kReference value mismatch"); + ++// These checks ensure the consistency of annotation additional action event ++// values across core/ and public. ++static_assert(static_cast(CPDF_AAction::kKeyStroke) == ++ FPDF_ANNOT_AACTION_KEY_STROKE, ++ "CPDF_AAction::kKeyStroke value mismatch"); ++static_assert(static_cast(CPDF_AAction::kFormat) == ++ FPDF_ANNOT_AACTION_FORMAT, ++ "CPDF_AAction::kFormat value mismatch"); ++static_assert(static_cast(CPDF_AAction::kValidate) == ++ FPDF_ANNOT_AACTION_VALIDATE, ++ "CPDF_AAction::kValidate value mismatch"); ++static_assert(static_cast(CPDF_AAction::kCalculate) == ++ FPDF_ANNOT_AACTION_CALCULATE, ++ "CPDF_AAction::kCalculate value mismatch"); ++ + bool HasAPStream(CPDF_Dictionary* pAnnotDict) { +- return !!GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal); ++ return !!GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::kNormal); + } + + void UpdateContentStream(CPDF_Form* pForm, CPDF_Stream* pStream) { +- ASSERT(pForm); +- ASSERT(pStream); ++ DCHECK(pForm); ++ DCHECK(pStream); + + CPDF_PageContentGenerator generator(pForm); +- std::ostringstream buf; ++ fxcrt::ostringstream buf; + generator.ProcessPageObjects(&buf); + pStream->SetDataFromStringstreamAndRemoveFilter(&buf); + } +@@ -170,9 +197,9 @@ void UpdateContentStream(CPDF_Form* pForm, CPDF_Stream* pStream) { + void SetQuadPointsAtIndex(CPDF_Array* array, + size_t quad_index, + const FS_QUADPOINTSF* quad_points) { +- ASSERT(array); +- ASSERT(quad_points); +- ASSERT(IsValidQuadPointsIndex(array, quad_index)); ++ DCHECK(array); ++ DCHECK(quad_points); ++ DCHECK(IsValidQuadPointsIndex(array, quad_index)); + + size_t nIndex = quad_index * 8; + array->SetNewAt(nIndex, quad_points->x1); +@@ -186,38 +213,45 @@ void SetQuadPointsAtIndex(CPDF_Array* array, + } + + void AppendQuadPoints(CPDF_Array* array, const FS_QUADPOINTSF* quad_points) { +- ASSERT(quad_points); +- ASSERT(array); +- +- array->AddNew(quad_points->x1); +- array->AddNew(quad_points->y1); +- array->AddNew(quad_points->x2); +- array->AddNew(quad_points->y2); +- array->AddNew(quad_points->x3); +- array->AddNew(quad_points->y3); +- array->AddNew(quad_points->x4); +- array->AddNew(quad_points->y4); ++ DCHECK(quad_points); ++ DCHECK(array); ++ ++ array->AppendNew(quad_points->x1); ++ array->AppendNew(quad_points->y1); ++ array->AppendNew(quad_points->x2); ++ array->AppendNew(quad_points->y2); ++ array->AppendNew(quad_points->x3); ++ array->AppendNew(quad_points->y3); ++ array->AppendNew(quad_points->x4); ++ array->AppendNew(quad_points->y4); + } + + void UpdateBBox(CPDF_Dictionary* annot_dict) { +- ASSERT(annot_dict); ++ DCHECK(annot_dict); + // Update BBox entry in appearance stream based on the bounding rectangle + // of the annotation's quadpoints. +- CPDF_Stream* pStream = +- GetAnnotAP(annot_dict, CPDF_Annot::AppearanceMode::Normal); ++ RetainPtr pStream = ++ GetAnnotAP(annot_dict, CPDF_Annot::AppearanceMode::kNormal); + if (pStream) { + CFX_FloatRect boundingRect = + CPDF_Annot::BoundingRectFromQuadPoints(annot_dict); + if (boundingRect.Contains(pStream->GetDict()->GetRectFor("BBox"))) +- pStream->GetDict()->SetRectFor("BBox", boundingRect); ++ pStream->GetMutableDict()->SetRectFor("BBox", boundingRect); + } + } + +-CPDF_Dictionary* GetAnnotDictFromFPDFAnnotation(FPDF_ANNOTATION annot) { ++const CPDF_Dictionary* GetAnnotDictFromFPDFAnnotation( ++ const FPDF_ANNOTATION annot) { + CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot); + return context ? context->GetAnnotDict() : nullptr; + } + ++RetainPtr GetMutableAnnotDictFromFPDFAnnotation( ++ FPDF_ANNOTATION annot) { ++ CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot); ++ return context ? context->GetMutableAnnotDict() : nullptr; ++} ++ + RetainPtr SetExtGStateInResourceDict( + CPDF_Document* pDoc, + const CPDF_Dictionary* pAnnotDict, +@@ -230,7 +264,7 @@ RetainPtr SetExtGStateInResourceDict( + + // CA respresents current stroking alpha specifying constant opacity + // value that should be used in transparent imaging model. +- float fOpacity = pAnnotDict->GetNumberFor("CA"); ++ float fOpacity = pAnnotDict->GetFloatFor("CA"); + + pGSDict->SetNewFor("CA", fOpacity); + +@@ -257,7 +291,7 @@ RetainPtr SetExtGStateInResourceDict( + } + + CPDF_FormField* GetFormField(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) { +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); + if (!pAnnotDict) + return nullptr; + +@@ -269,17 +303,59 @@ CPDF_FormField* GetFormField(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) { + return pPDFForm->GetFieldByDict(pAnnotDict); + } + ++const CPDFSDK_Widget* GetRadioButtonOrCheckBoxWidget(FPDF_FORMHANDLE hHandle, ++ FPDF_ANNOTATION annot) { ++ const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ if (!pAnnotDict) ++ return nullptr; ++ ++ CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle); ++ if (!pForm) ++ return nullptr; ++ ++ CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm(); ++ CPDF_FormField* pFormField = pPDFForm->GetFieldByDict(pAnnotDict); ++ if (!pFormField || (pFormField->GetType() != CPDF_FormField::kCheckBox && ++ pFormField->GetType() != CPDF_FormField::kRadioButton)) { ++ return nullptr; ++ } ++ ++ CPDF_FormControl* pFormControl = pPDFForm->GetControlByDict(pAnnotDict); ++ return pFormControl ? pForm->GetWidget(pFormControl) : nullptr; ++} ++ ++RetainPtr GetInkList(FPDF_ANNOTATION annot) { ++ FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot); ++ if (subtype != FPDF_ANNOT_INK) ++ return nullptr; ++ ++ const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); ++ return annot_dict ? annot_dict->GetArrayFor(pdfium::annotation::kInkList) ++ : nullptr; ++} ++ + } // namespace + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFAnnot_IsSupportedSubtype(FPDF_ANNOTATION_SUBTYPE subtype) { + // The supported subtypes must also be communicated in the user doc. +- return subtype == FPDF_ANNOT_CIRCLE || subtype == FPDF_ANNOT_FREETEXT || +- subtype == FPDF_ANNOT_HIGHLIGHT || subtype == FPDF_ANNOT_INK || +- subtype == FPDF_ANNOT_POPUP || subtype == FPDF_ANNOT_SQUARE || +- subtype == FPDF_ANNOT_SQUIGGLY || subtype == FPDF_ANNOT_STAMP || +- subtype == FPDF_ANNOT_STRIKEOUT || subtype == FPDF_ANNOT_TEXT || +- subtype == FPDF_ANNOT_UNDERLINE; ++ switch (subtype) { ++ case FPDF_ANNOT_CIRCLE: ++ case FPDF_ANNOT_FREETEXT: ++ case FPDF_ANNOT_HIGHLIGHT: ++ case FPDF_ANNOT_INK: ++ case FPDF_ANNOT_LINK: ++ case FPDF_ANNOT_POPUP: ++ case FPDF_ANNOT_SQUARE: ++ case FPDF_ANNOT_SQUIGGLY: ++ case FPDF_ANNOT_STAMP: ++ case FPDF_ANNOT_STRIKEOUT: ++ case FPDF_ANNOT_TEXT: ++ case FPDF_ANNOT_UNDERLINE: ++ return true; ++ default: ++ return false; ++ } + } + + FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV +@@ -293,24 +369,23 @@ FPDFPage_CreateAnnot(FPDF_PAGE page, FPDF_ANNOTATION_SUBTYPE subtype) { + pDict->SetNewFor(pdfium::annotation::kSubtype, + CPDF_Annot::AnnotSubtypeToString( + static_cast(subtype))); +- auto pNewAnnot = pdfium::MakeUnique(pDict.Get(), pPage); ++ auto pNewAnnot = ++ std::make_unique(pDict, IPDFPageFromFPDFPage(page)); + +- CPDF_Array* pAnnotList = pPage->GetDict()->GetArrayFor("Annots"); +- if (!pAnnotList) +- pAnnotList = pPage->GetDict()->SetNewFor("Annots"); +- pAnnotList->Add(pDict); ++ RetainPtr pAnnotList = pPage->GetOrCreateAnnotsArray(); ++ pAnnotList->Append(pDict); + + // Caller takes ownership. + return FPDFAnnotationFromCPDFAnnotContext(pNewAnnot.release()); + } + + FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetAnnotCount(FPDF_PAGE page) { +- CPDF_Page* pPage = CPDFPageFromFPDFPage(page); ++ const CPDF_Page* pPage = CPDFPageFromFPDFPage(page); + if (!pPage) + return 0; + +- CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots"); +- return pAnnots ? pAnnots->size() : 0; ++ RetainPtr pAnnots = pPage->GetAnnotsArray(); ++ return pAnnots ? fxcrt::CollectionSize(*pAnnots) : 0; + } + + FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV FPDFPage_GetAnnot(FPDF_PAGE page, +@@ -319,15 +394,17 @@ FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV FPDFPage_GetAnnot(FPDF_PAGE page, + if (!pPage || index < 0) + return nullptr; + +- CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots"); ++ RetainPtr pAnnots = pPage->GetMutableAnnotsArray(); + if (!pAnnots || static_cast(index) >= pAnnots->size()) + return nullptr; + +- CPDF_Dictionary* pDict = ToDictionary(pAnnots->GetDirectObjectAt(index)); ++ RetainPtr pDict = ++ ToDictionary(pAnnots->GetMutableDirectObjectAt(index)); + if (!pDict) + return nullptr; + +- auto pNewAnnot = pdfium::MakeUnique(pDict, pPage); ++ auto pNewAnnot = std::make_unique( ++ std::move(pDict), IPDFPageFromFPDFPage(page)); + + // Caller takes ownership. + return FPDFAnnotationFromCPDFAnnotContext(pNewAnnot.release()); +@@ -335,15 +412,15 @@ FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV FPDFPage_GetAnnot(FPDF_PAGE page, + + FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetAnnotIndex(FPDF_PAGE page, + FPDF_ANNOTATION annot) { +- CPDF_Page* pPage = CPDFPageFromFPDFPage(page); ++ const CPDF_Page* pPage = CPDFPageFromFPDFPage(page); + if (!pPage) + return -1; + +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); + if (!pAnnotDict) + return -1; + +- CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots"); ++ RetainPtr pAnnots = pPage->GetAnnotsArray(); + if (!pAnnots) + return -1; + +@@ -356,7 +433,7 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetAnnotIndex(FPDF_PAGE page, + if (it == locker.end()) + return -1; + +- return it - locker.begin(); ++ return pdfium::base::checked_cast(it - locker.begin()); + } + + FPDF_EXPORT void FPDF_CALLCONV FPDFPage_CloseAnnot(FPDF_ANNOTATION annot) { +@@ -369,7 +446,7 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_RemoveAnnot(FPDF_PAGE page, + if (!pPage || index < 0) + return false; + +- CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots"); ++ RetainPtr pAnnots = pPage->GetMutableAnnotsArray(); + if (!pAnnots || static_cast(index) >= pAnnots->size()) + return false; + +@@ -379,12 +456,12 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_RemoveAnnot(FPDF_PAGE page, + + FPDF_EXPORT FPDF_ANNOTATION_SUBTYPE FPDF_CALLCONV + FPDFAnnot_GetSubtype(FPDF_ANNOTATION annot) { +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); + if (!pAnnotDict) + return FPDF_ANNOT_UNKNOWN; + + return static_cast(CPDF_Annot::StringToAnnotSubtype( +- pAnnotDict->GetStringFor(pdfium::annotation::kSubtype))); ++ pAnnotDict->GetNameFor(pdfium::annotation::kSubtype))); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +@@ -406,23 +483,55 @@ FPDFAnnot_UpdateObject(FPDF_ANNOTATION annot, FPDF_PAGEOBJECT obj) { + + // Check that the annotation already has an appearance stream, since an + // existing object is to be updated. +- CPDF_Stream* pStream = +- GetAnnotAP(pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal); ++ RetainPtr pAnnotDict = pAnnot->GetMutableAnnotDict(); ++ RetainPtr pStream = ++ GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal); + if (!pStream) + return false; + + // Check that the object is already in this annotation's object list. + CPDF_Form* pForm = pAnnot->GetForm(); +- auto it = +- std::find_if(pForm->begin(), pForm->end(), +- [pObj](const std::unique_ptr& candidate) { +- return candidate.get() == pObj; +- }); +- if (it == pForm->end()) ++ if (!pdfium::Contains(*pForm, fxcrt::MakeFakeUniquePtr(pObj))) + return false; + + // Update the content stream data in the annotation's AP stream. +- UpdateContentStream(pForm, pStream); ++ UpdateContentStream(pForm, pStream.Get()); ++ return true; ++} ++ ++FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_AddInkStroke(FPDF_ANNOTATION annot, ++ const FS_POINTF* points, ++ size_t point_count) { ++ if (FPDFAnnot_GetSubtype(annot) != FPDF_ANNOT_INK || !points || ++ point_count == 0 || ++ !pdfium::base::IsValueInRangeForNumericType(point_count)) { ++ return -1; ++ } ++ ++ RetainPtr annot_dict = ++ GetMutableAnnotDictFromFPDFAnnotation(annot); ++ RetainPtr inklist = annot_dict->GetOrCreateArrayFor("InkList"); ++ FX_SAFE_SIZE_T safe_ink_size = inklist->size(); ++ safe_ink_size += 1; ++ if (!safe_ink_size.IsValid()) ++ return -1; ++ ++ auto ink_coord_list = inklist->AppendNew(); ++ for (size_t i = 0; i < point_count; i++) { ++ ink_coord_list->AppendNew(points[i].x); ++ ink_coord_list->AppendNew(points[i].y); ++ } ++ return static_cast(inklist->size() - 1); ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFAnnot_RemoveInkList(FPDF_ANNOTATION annot) { ++ if (FPDFAnnot_GetSubtype(annot) != FPDF_ANNOT_INK) ++ return false; ++ ++ RetainPtr annot_dict = ++ CPDFAnnotContextFromFPDFAnnotation(annot)->GetMutableAnnotDict(); ++ annot_dict->RemoveFor("InkList"); + return true; + } + +@@ -438,13 +547,13 @@ FPDFAnnot_AppendObject(FPDF_ANNOTATION annot, FPDF_PAGEOBJECT obj) { + return false; + + // If the annotation does not have an AP stream yet, generate and set it. +- CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict(); +- CPDF_Stream* pStream = +- GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal); ++ RetainPtr pAnnotDict = pAnnot->GetMutableAnnotDict(); ++ RetainPtr pStream = ++ GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal); + if (!pStream) { +- CPVT_GenerateAP::GenerateEmptyAP(pAnnot->GetPage()->GetDocument(), +- pAnnotDict); +- pStream = GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal); ++ CPDF_GenerateAP::GenerateEmptyAP(pAnnot->GetPage()->GetDocument(), ++ pAnnotDict.Get()); ++ pStream = GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal); + if (!pStream) + return false; + } +@@ -459,19 +568,14 @@ FPDFAnnot_AppendObject(FPDF_ANNOTATION annot, FPDF_PAGEOBJECT obj) { + // Note that an object that came from a different annotation must not be + // passed here, since an object cannot belong to more than one annotation. + CPDF_Form* pForm = pAnnot->GetForm(); +- auto it = +- std::find_if(pForm->begin(), pForm->end(), +- [pObj](const std::unique_ptr& candidate) { +- return candidate.get() == pObj; +- }); +- if (it != pForm->end()) ++ if (pdfium::Contains(*pForm, fxcrt::MakeFakeUniquePtr(pObj))) + return false; + + // Append the object to the object list. + pForm->AppendPageObject(pdfium::WrapUnique(pObj)); + + // Set the content stream data in the annotation's AP stream. +- UpdateContentStream(pForm, pStream); ++ UpdateContentStream(pForm, pStream.Get()); + return true; + } + +@@ -481,14 +585,16 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_GetObjectCount(FPDF_ANNOTATION annot) { + return 0; + + if (!pAnnot->HasForm()) { +- CPDF_Stream* pStream = +- GetAnnotAP(pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal); ++ RetainPtr pDict = pAnnot->GetMutableAnnotDict(); ++ RetainPtr pStream = ++ GetAnnotAP(pDict.Get(), CPDF_Annot::AppearanceMode::kNormal); + if (!pStream) + return 0; + +- pAnnot->SetForm(pStream); ++ pAnnot->SetForm(std::move(pStream)); + } +- return pAnnot->GetForm()->GetPageObjectCount(); ++ return pdfium::base::checked_cast( ++ pAnnot->GetForm()->GetPageObjectCount()); + } + + FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV +@@ -498,12 +604,13 @@ FPDFAnnot_GetObject(FPDF_ANNOTATION annot, int index) { + return nullptr; + + if (!pAnnot->HasForm()) { +- CPDF_Stream* pStream = +- GetAnnotAP(pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal); ++ RetainPtr pAnnotDict = pAnnot->GetMutableAnnotDict(); ++ RetainPtr pStream = ++ GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal); + if (!pStream) + return nullptr; + +- pAnnot->SetForm(pStream); ++ pAnnot->SetForm(std::move(pStream)); + } + + return FPDFPageObjectFromCPDFPageObject( +@@ -522,15 +629,16 @@ FPDFAnnot_RemoveObject(FPDF_ANNOTATION annot, int index) { + + // Check that the annotation already has an appearance stream, since an + // existing object is to be deleted. +- CPDF_Stream* pStream = +- GetAnnotAP(pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal); ++ RetainPtr pAnnotDict = pAnnot->GetMutableAnnotDict(); ++ RetainPtr pStream = ++ GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal); + if (!pStream) + return false; + + if (!pAnnot->GetForm()->ErasePageObjectAtIndex(index)) + return false; + +- UpdateContentStream(pAnnot->GetForm(), pStream); ++ UpdateContentStream(pAnnot->GetForm(), pStream.Get()); + return true; + } + +@@ -540,14 +648,16 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetColor(FPDF_ANNOTATION annot, + unsigned int G, + unsigned int B, + unsigned int A) { +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ RetainPtr pAnnotDict = ++ GetMutableAnnotDictFromFPDFAnnotation(annot); ++ + if (!pAnnotDict || R > 255 || G > 255 || B > 255 || A > 255) + return false; + + // For annotations with their appearance streams already defined, the path + // stream's own color definitions take priority over the annotation color + // definitions set by this method, hence this method will simply fail. +- if (HasAPStream(pAnnotDict)) ++ if (HasAPStream(pAnnotDict.Get())) + return false; + + // Set the opacity of the annotation. +@@ -555,15 +665,15 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetColor(FPDF_ANNOTATION annot, + + // Set the color of the annotation. + ByteString key = type == FPDFANNOT_COLORTYPE_InteriorColor ? "IC" : "C"; +- CPDF_Array* pColor = pAnnotDict->GetArrayFor(key); ++ RetainPtr pColor = pAnnotDict->GetMutableArrayFor(key); + if (pColor) + pColor->Clear(); + else + pColor = pAnnotDict->SetNewFor(key); + +- pColor->AddNew(R / 255.f); +- pColor->AddNew(G / 255.f); +- pColor->AddNew(B / 255.f); ++ pColor->AppendNew(R / 255.f); ++ pColor->AppendNew(G / 255.f); ++ pColor->AppendNew(B / 255.f); + + return true; + } +@@ -574,25 +684,26 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetColor(FPDF_ANNOTATION annot, + unsigned int* G, + unsigned int* B, + unsigned int* A) { +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ RetainPtr pAnnotDict = ++ GetMutableAnnotDictFromFPDFAnnotation(annot); ++ + if (!pAnnotDict || !R || !G || !B || !A) + return false; + + // For annotations with their appearance streams already defined, the path + // stream's own color definitions take priority over the annotation color + // definitions retrieved by this method, hence this method will simply fail. +- if (HasAPStream(pAnnotDict)) ++ if (HasAPStream(pAnnotDict.Get())) + return false; + +- CPDF_Array* pColor = pAnnotDict->GetArrayFor( ++ RetainPtr pColor = pAnnotDict->GetArrayFor( + type == FPDFANNOT_COLORTYPE_InteriorColor ? "IC" : "C"); +- *A = +- (pAnnotDict->KeyExist("CA") ? pAnnotDict->GetNumberFor("CA") : 1) * 255.f; ++ *A = (pAnnotDict->KeyExist("CA") ? pAnnotDict->GetFloatFor("CA") : 1) * 255.f; + if (!pColor) { + // Use default color. The default colors must be consistent with the ones + // used to generate AP. See calls to GetColorStringWithDefault() in +- // CPVT_GenerateAP::Generate*AP(). +- if (pAnnotDict->GetStringFor(pdfium::annotation::kSubtype) == "Highlight") { ++ // CPDF_GenerateAP::Generate*AP(). ++ if (pAnnotDict->GetNameFor(pdfium::annotation::kSubtype) == "Highlight") { + *R = 255; + *G = 255; + *B = 0; +@@ -606,22 +717,22 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetColor(FPDF_ANNOTATION annot, + + CFX_Color color = fpdfdoc::CFXColorFromArray(*pColor); + switch (color.nColorType) { +- case CFX_Color::kRGB: ++ case CFX_Color::Type::kRGB: + *R = color.fColor1 * 255.f; + *G = color.fColor2 * 255.f; + *B = color.fColor3 * 255.f; + break; +- case CFX_Color::kGray: ++ case CFX_Color::Type::kGray: + *R = 255.f * color.fColor1; + *G = 255.f * color.fColor1; + *B = 255.f * color.fColor1; + break; +- case CFX_Color::kCMYK: ++ case CFX_Color::Type::kCMYK: + *R = 255.f * (1 - color.fColor1) * (1 - color.fColor4); + *G = 255.f * (1 - color.fColor2) * (1 - color.fColor4); + *B = 255.f * (1 - color.fColor3) * (1 - color.fColor4); + break; +- case CFX_Color::kTransparent: ++ case CFX_Color::Type::kTransparent: + *R = 0; + *G = 0; + *B = 0; +@@ -648,14 +759,15 @@ FPDFAnnot_SetAttachmentPoints(FPDF_ANNOTATION annot, + if (!FPDFAnnot_HasAttachmentPoints(annot) || !quad_points) + return false; + +- CPDF_Dictionary* pAnnotDict = +- CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict(); +- CPDF_Array* pQuadPointsArray = GetQuadPointsArrayFromDictionary(pAnnotDict); +- if (!IsValidQuadPointsIndex(pQuadPointsArray, quad_index)) ++ RetainPtr pAnnotDict = ++ CPDFAnnotContextFromFPDFAnnotation(annot)->GetMutableAnnotDict(); ++ RetainPtr pQuadPointsArray = ++ GetMutableQuadPointsArrayFromDictionary(pAnnotDict.Get()); ++ if (!IsValidQuadPointsIndex(pQuadPointsArray.Get(), quad_index)) + return false; + +- SetQuadPointsAtIndex(pQuadPointsArray, quad_index, quad_points); +- UpdateBBox(pAnnotDict); ++ SetQuadPointsAtIndex(pQuadPointsArray.Get(), quad_index, quad_points); ++ UpdateBBox(pAnnotDict.Get()); + return true; + } + +@@ -665,13 +777,14 @@ FPDFAnnot_AppendAttachmentPoints(FPDF_ANNOTATION annot, + if (!FPDFAnnot_HasAttachmentPoints(annot) || !quad_points) + return false; + +- CPDF_Dictionary* pAnnotDict = +- CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict(); +- CPDF_Array* pQuadPointsArray = GetQuadPointsArrayFromDictionary(pAnnotDict); ++ RetainPtr pAnnotDict = ++ CPDFAnnotContextFromFPDFAnnotation(annot)->GetMutableAnnotDict(); ++ RetainPtr pQuadPointsArray = ++ GetMutableQuadPointsArrayFromDictionary(pAnnotDict.Get()); + if (!pQuadPointsArray) +- pQuadPointsArray = AddQuadPointsArrayToDictionary(pAnnotDict); +- AppendQuadPoints(pQuadPointsArray, quad_points); +- UpdateBBox(pAnnotDict); ++ pQuadPointsArray = AddQuadPointsArrayToDictionary(pAnnotDict.Get()); ++ AppendQuadPoints(pQuadPointsArray.Get(), quad_points); ++ UpdateBBox(pAnnotDict.Get()); + return true; + } + +@@ -682,7 +795,8 @@ FPDFAnnot_CountAttachmentPoints(FPDF_ANNOTATION annot) { + + const CPDF_Dictionary* pAnnotDict = + CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict(); +- const CPDF_Array* pArray = GetQuadPointsArrayFromDictionary(pAnnotDict); ++ RetainPtr pArray = ++ GetQuadPointsArrayFromDictionary(pAnnotDict); + return pArray ? pArray->size() / 8 : 0; + } + +@@ -695,16 +809,18 @@ FPDFAnnot_GetAttachmentPoints(FPDF_ANNOTATION annot, + + const CPDF_Dictionary* pAnnotDict = + CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict(); +- const CPDF_Array* pArray = GetQuadPointsArrayFromDictionary(pAnnotDict); ++ RetainPtr pArray = ++ GetQuadPointsArrayFromDictionary(pAnnotDict); + if (!pArray) + return false; + +- return GetQuadPointsAtIndex(pArray, quad_index, quad_points); ++ return GetQuadPointsAtIndex(std::move(pArray), quad_index, quad_points); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetRect(FPDF_ANNOTATION annot, + const FS_RECTF* rect) { +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ RetainPtr pAnnotDict = ++ GetMutableAnnotDictFromFPDFAnnotation(annot); + if (!pAnnotDict || !rect) + return false; + +@@ -721,16 +837,16 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetRect(FPDF_ANNOTATION annot, + if (FPDFAnnot_HasAttachmentPoints(annot)) + return true; + +- CPDF_Stream* pStream = +- GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal); ++ RetainPtr pStream = ++ GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal); + if (pStream && newRect.Contains(pStream->GetDict()->GetRectFor("BBox"))) +- pStream->GetDict()->SetRectFor("BBox", newRect); ++ pStream->GetMutableDict()->SetRectFor("BBox", newRect); + return true; + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetRect(FPDF_ANNOTATION annot, + FS_RECTF* rect) { +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); + if (!pAnnotDict || !rect) + return false; + +@@ -739,9 +855,138 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetRect(FPDF_ANNOTATION annot, + return true; + } + ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFAnnot_GetVertices(FPDF_ANNOTATION annot, ++ FS_POINTF* buffer, ++ unsigned long length) { ++ FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot); ++ if (subtype != FPDF_ANNOT_POLYGON && subtype != FPDF_ANNOT_POLYLINE) ++ return 0; ++ ++ const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); ++ if (!annot_dict) ++ return 0; ++ ++ RetainPtr vertices = ++ annot_dict->GetArrayFor(pdfium::annotation::kVertices); ++ if (!vertices) ++ return 0; ++ ++ // Truncate to an even number. ++ const unsigned long points_len = ++ fxcrt::CollectionSize(*vertices) / 2; ++ if (buffer && length >= points_len) { ++ for (unsigned long i = 0; i < points_len; ++i) { ++ buffer[i].x = vertices->GetFloatAt(i * 2); ++ buffer[i].y = vertices->GetFloatAt(i * 2 + 1); ++ } ++ } ++ return points_len; ++} ++ ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFAnnot_GetInkListCount(FPDF_ANNOTATION annot) { ++ RetainPtr ink_list = GetInkList(annot); ++ return ink_list ? fxcrt::CollectionSize(*ink_list) : 0; ++} ++ ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFAnnot_GetInkListPath(FPDF_ANNOTATION annot, ++ unsigned long path_index, ++ FS_POINTF* buffer, ++ unsigned long length) { ++ RetainPtr ink_list = GetInkList(annot); ++ if (!ink_list) ++ return 0; ++ ++ RetainPtr path = ink_list->GetArrayAt(path_index); ++ if (!path) ++ return 0; ++ ++ // Truncate to an even number. ++ const unsigned long points_len = ++ fxcrt::CollectionSize(*path) / 2; ++ if (buffer && length >= points_len) { ++ for (unsigned long i = 0; i < points_len; ++i) { ++ buffer[i].x = path->GetFloatAt(i * 2); ++ buffer[i].y = path->GetFloatAt(i * 2 + 1); ++ } ++ } ++ return points_len; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetLine(FPDF_ANNOTATION annot, ++ FS_POINTF* start, ++ FS_POINTF* end) { ++ if (!start || !end) ++ return false; ++ ++ FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot); ++ if (subtype != FPDF_ANNOT_LINE) ++ return false; ++ ++ const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); ++ if (!annot_dict) ++ return false; ++ ++ RetainPtr line = ++ annot_dict->GetArrayFor(pdfium::annotation::kL); ++ if (!line || line->size() < 4) ++ return false; ++ ++ start->x = line->GetFloatAt(0); ++ start->y = line->GetFloatAt(1); ++ end->x = line->GetFloatAt(2); ++ end->y = line->GetFloatAt(3); ++ return true; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetBorder(FPDF_ANNOTATION annot, ++ float horizontal_radius, ++ float vertical_radius, ++ float border_width) { ++ RetainPtr annot_dict = ++ GetMutableAnnotDictFromFPDFAnnotation(annot); ++ if (!annot_dict) ++ return false; ++ ++ // Remove the appearance stream. Otherwise PDF viewers will render that and ++ // not use the border values. ++ annot_dict->RemoveFor(pdfium::annotation::kAP); ++ ++ auto border = annot_dict->SetNewFor(pdfium::annotation::kBorder); ++ border->AppendNew(horizontal_radius); ++ border->AppendNew(vertical_radius); ++ border->AppendNew(border_width); ++ return true; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFAnnot_GetBorder(FPDF_ANNOTATION annot, ++ float* horizontal_radius, ++ float* vertical_radius, ++ float* border_width) { ++ if (!horizontal_radius || !vertical_radius || !border_width) ++ return false; ++ ++ const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot); ++ if (!annot_dict) ++ return false; ++ ++ RetainPtr border = ++ annot_dict->GetArrayFor(pdfium::annotation::kBorder); ++ if (!border || border->size() < 3) ++ return false; ++ ++ *horizontal_radius = border->GetFloatAt(0); ++ *vertical_radius = border->GetFloatAt(1); ++ *border_width = border->GetFloatAt(2); ++ return true; ++} ++ + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_HasKey(FPDF_ANNOTATION annot, + FPDF_BYTESTRING key) { +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); + return pAnnotDict && pAnnotDict->KeyExist(key); + } + +@@ -750,8 +995,8 @@ FPDFAnnot_GetValueType(FPDF_ANNOTATION annot, FPDF_BYTESTRING key) { + if (!FPDFAnnot_HasKey(annot, key)) + return FPDF_OBJECT_UNKNOWN; + +- auto* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot); +- CPDF_Object* pObj = pAnnot->GetAnnotDict()->GetObjectFor(key); ++ const CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot); ++ RetainPtr pObj = pAnnot->GetAnnotDict()->GetObjectFor(key); + return pObj ? pObj->GetType() : FPDF_OBJECT_UNKNOWN; + } + +@@ -759,11 +1004,13 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFAnnot_SetStringValue(FPDF_ANNOTATION annot, + FPDF_BYTESTRING key, + FPDF_WIDESTRING value) { +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ RetainPtr pAnnotDict = ++ GetMutableAnnotDictFromFPDFAnnotation(annot); + if (!pAnnotDict) + return false; + +- pAnnotDict->SetNewFor(key, WideStringFromFPDFWideString(value)); ++ pAnnotDict->SetNewFor( ++ key, WideStringFromFPDFWideString(value).AsStringView()); + return true; + } + +@@ -772,7 +1019,7 @@ FPDFAnnot_GetStringValue(FPDF_ANNOTATION annot, + FPDF_BYTESTRING key, + FPDF_WCHAR* buffer, + unsigned long buflen) { +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); + if (!pAnnotDict) + return 0; + +@@ -787,11 +1034,11 @@ FPDFAnnot_GetNumberValue(FPDF_ANNOTATION annot, + if (!value) + return false; + +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); + if (!pAnnotDict) + return false; + +- const CPDF_Object* p = pAnnotDict->GetObjectFor(key); ++ RetainPtr p = pAnnotDict->GetObjectFor(key); + if (!p || p->GetType() != FPDF_OBJECT_NUMBER) + return false; + +@@ -803,7 +1050,8 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFAnnot_SetAP(FPDF_ANNOTATION annot, + FPDF_ANNOT_APPEARANCEMODE appearanceMode, + FPDF_WIDESTRING value) { +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ RetainPtr pAnnotDict = ++ GetMutableAnnotDictFromFPDFAnnotation(annot); + if (!pAnnotDict) + return false; + +@@ -811,13 +1059,13 @@ FPDFAnnot_SetAP(FPDF_ANNOTATION annot, + return false; + + static constexpr const char* kModeKeyForMode[] = {"N", "R", "D"}; +- static_assert( +- FX_ArraySize(kModeKeyForMode) == FPDF_ANNOT_APPEARANCEMODE_COUNT, +- "length of kModeKeyForMode should be equal to " +- "FPDF_ANNOT_APPEARANCEMODE_COUNT"); ++ static_assert(std::size(kModeKeyForMode) == FPDF_ANNOT_APPEARANCEMODE_COUNT, ++ "length of kModeKeyForMode should be equal to " ++ "FPDF_ANNOT_APPEARANCEMODE_COUNT"); + const char* modeKey = kModeKeyForMode[appearanceMode]; + +- CPDF_Dictionary* pApDict = pAnnotDict->GetDictFor(pdfium::annotation::kAP); ++ RetainPtr pApDict = ++ pAnnotDict->GetMutableDictFor(pdfium::annotation::kAP); + + // If value is null, we're in remove mode. Otherwise, we're in add/update + // mode. +@@ -836,13 +1084,13 @@ FPDFAnnot_SetAP(FPDF_ANNOTATION annot, + if (!pDoc) + return false; + +- CPDF_Stream* pNewIndirectStream = pDoc->NewIndirect(); +- ++ auto pNewIndirectStream = pDoc->NewIndirect(); + ByteString newAPStream = +- PDF_EncodeText(WideStringFromFPDFWideString(value)); ++ PDF_EncodeText(WideStringFromFPDFWideString(value).AsStringView()); + pNewIndirectStream->SetData(newAPStream.raw_span()); + +- CPDF_Dictionary* pStreamDict = pNewIndirectStream->GetDict(); ++ RetainPtr pStreamDict = ++ pNewIndirectStream->GetMutableDict(); + pStreamDict->SetNewFor(pdfium::annotation::kType, "XObject"); + pStreamDict->SetNewFor(pdfium::annotation::kSubtype, "Form"); + pStreamDict->SetRectFor("BBox", rect); +@@ -850,16 +1098,16 @@ FPDFAnnot_SetAP(FPDF_ANNOTATION annot, + // checking for value < 1 and not <= 1 so that the output PDF size does not + // unnecessarily bloat up by creating a new dictionary in case of solid + // color. +- if (pAnnotDict->KeyExist("CA") && pAnnotDict->GetNumberFor("CA") < 1.0f) { ++ if (pAnnotDict->KeyExist("CA") && pAnnotDict->GetFloatFor("CA") < 1.0f) { + RetainPtr pResourceDict = +- SetExtGStateInResourceDict(pDoc, pAnnotDict, "Normal"); ++ SetExtGStateInResourceDict(pDoc, pAnnotDict.Get(), "Normal"); + pStreamDict->SetFor("Resources", pResourceDict); + } + + // Storing reference to indirect object in annotation's AP +- if (!pApDict) ++ if (!pApDict) { + pApDict = pAnnotDict->SetNewFor(pdfium::annotation::kAP); +- ++ } + pApDict->SetNewFor(modeKey, pDoc, + pNewIndirectStream->GetObjNum()); + } else { +@@ -879,7 +1127,8 @@ FPDFAnnot_GetAP(FPDF_ANNOTATION annot, + FPDF_ANNOT_APPEARANCEMODE appearanceMode, + FPDF_WCHAR* buffer, + unsigned long buflen) { +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ RetainPtr pAnnotDict = ++ GetMutableAnnotDictFromFPDFAnnotation(annot); + if (!pAnnotDict) + return 0; + +@@ -889,7 +1138,7 @@ FPDFAnnot_GetAP(FPDF_ANNOTATION annot, + CPDF_Annot::AppearanceMode mode = + static_cast(appearanceMode); + +- CPDF_Stream* pStream = GetAnnotAPNoFallback(pAnnotDict, mode); ++ RetainPtr pStream = GetAnnotAPNoFallback(pAnnotDict.Get(), mode); + return Utf16EncodeMaybeCopyAndReturnLength( + pStream ? pStream->GetUnicodeText() : WideString(), buffer, buflen); + } +@@ -900,26 +1149,28 @@ FPDFAnnot_GetLinkedAnnot(FPDF_ANNOTATION annot, FPDF_BYTESTRING key) { + if (!pAnnot) + return nullptr; + +- CPDF_Dictionary* pLinkedDict = pAnnot->GetAnnotDict()->GetDictFor(key); +- if (!pLinkedDict || pLinkedDict->GetStringFor("Type") != "Annot") ++ RetainPtr pLinkedDict = ++ pAnnot->GetMutableAnnotDict()->GetMutableDictFor(key); ++ if (!pLinkedDict || pLinkedDict->GetNameFor("Type") != "Annot") + return nullptr; + +- auto pLinkedAnnot = +- pdfium::MakeUnique(pLinkedDict, pAnnot->GetPage()); ++ auto pLinkedAnnot = std::make_unique( ++ std::move(pLinkedDict), pAnnot->GetPage()); + + // Caller takes ownership. + return FPDFAnnotationFromCPDFAnnotContext(pLinkedAnnot.release()); + } + + FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_GetFlags(FPDF_ANNOTATION annot) { +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); + return pAnnotDict ? pAnnotDict->GetIntegerFor(pdfium::annotation::kF) + : FPDF_ANNOT_FLAG_NONE; + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetFlags(FPDF_ANNOTATION annot, + int flags) { +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ RetainPtr pAnnotDict = ++ GetMutableAnnotDictFromFPDFAnnotation(annot); + if (!pAnnotDict) + return false; + +@@ -940,17 +1191,17 @@ FPDFAnnot_GetFormFieldAtPoint(FPDF_FORMHANDLE hHandle, + if (!point) + return nullptr; + +- CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle); ++ const CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle); + if (!pForm) + return nullptr; + +- CPDF_Page* pPage = CPDFPageFromFPDFPage(page); ++ const CPDF_Page* pPage = CPDFPageFromFPDFPage(page); + if (!pPage) + return nullptr; + +- CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm(); ++ const CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm(); + int annot_index = -1; +- CPDF_FormControl* pFormCtrl = pPDFForm->GetControlAtPoint( ++ const CPDF_FormControl* pFormCtrl = pPDFForm->GetControlAtPoint( + pPage, CFXPointFFromFSPointF(*point), &annot_index); + if (!pFormCtrl || annot_index == -1) + return nullptr; +@@ -962,7 +1213,7 @@ FPDFAnnot_GetFormFieldName(FPDF_FORMHANDLE hHandle, + FPDF_ANNOTATION annot, + FPDF_WCHAR* buffer, + unsigned long buflen) { +- CPDF_FormField* pFormField = GetFormField(hHandle, annot); ++ const CPDF_FormField* pFormField = GetFormField(hHandle, annot); + if (!pFormField) + return 0; + return Utf16EncodeMaybeCopyAndReturnLength(pFormField->GetFullName(), buffer, +@@ -971,16 +1222,51 @@ FPDFAnnot_GetFormFieldName(FPDF_FORMHANDLE hHandle, + + FPDF_EXPORT int FPDF_CALLCONV + FPDFAnnot_GetFormFieldType(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) { +- CPDF_FormField* pFormField = GetFormField(hHandle, annot); ++ const CPDF_FormField* pFormField = GetFormField(hHandle, annot); + return pFormField ? static_cast(pFormField->GetFieldType()) : -1; + } + ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFAnnot_GetFormAdditionalActionJavaScript(FPDF_FORMHANDLE hHandle, ++ FPDF_ANNOTATION annot, ++ int event, ++ FPDF_WCHAR* buffer, ++ unsigned long buflen) { ++ const CPDF_FormField* pFormField = GetFormField(hHandle, annot); ++ if (!pFormField) ++ return 0; ++ ++ if (event < FPDF_ANNOT_AACTION_KEY_STROKE || ++ event > FPDF_ANNOT_AACTION_CALCULATE) { ++ return 0; ++ } ++ ++ auto type = static_cast(event); ++ CPDF_AAction additional_action = pFormField->GetAdditionalAction(); ++ CPDF_Action action = additional_action.GetAction(type); ++ return Utf16EncodeMaybeCopyAndReturnLength(action.GetJavaScript(), buffer, ++ buflen); ++} ++ ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFAnnot_GetFormFieldAlternateName(FPDF_FORMHANDLE hHandle, ++ FPDF_ANNOTATION annot, ++ FPDF_WCHAR* buffer, ++ unsigned long buflen) { ++ const CPDF_FormField* pFormField = GetFormField(hHandle, annot); ++ if (!pFormField) ++ return 0; ++ ++ return Utf16EncodeMaybeCopyAndReturnLength(pFormField->GetAlternateName(), ++ buffer, buflen); ++} ++ + FPDF_EXPORT unsigned long FPDF_CALLCONV + FPDFAnnot_GetFormFieldValue(FPDF_FORMHANDLE hHandle, + FPDF_ANNOTATION annot, + FPDF_WCHAR* buffer, + unsigned long buflen) { +- CPDF_FormField* pFormField = GetFormField(hHandle, annot); ++ const CPDF_FormField* pFormField = GetFormField(hHandle, annot); + if (!pFormField) + return 0; + return Utf16EncodeMaybeCopyAndReturnLength(pFormField->GetValue(), buffer, +@@ -989,7 +1275,7 @@ FPDFAnnot_GetFormFieldValue(FPDF_FORMHANDLE hHandle, + + FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_GetOptionCount(FPDF_FORMHANDLE hHandle, + FPDF_ANNOTATION annot) { +- CPDF_FormField* pFormField = GetFormField(hHandle, annot); ++ const CPDF_FormField* pFormField = GetFormField(hHandle, annot); + return pFormField ? pFormField->CountOptions() : -1; + } + +@@ -1002,7 +1288,7 @@ FPDFAnnot_GetOptionLabel(FPDF_FORMHANDLE hHandle, + if (index < 0) + return 0; + +- CPDF_FormField* pFormField = GetFormField(hHandle, annot); ++ const CPDF_FormField* pFormField = GetFormField(hHandle, annot); + if (!pFormField || index >= pFormField->CountOptions()) + return 0; + +@@ -1010,6 +1296,25 @@ FPDFAnnot_GetOptionLabel(FPDF_FORMHANDLE hHandle, + return Utf16EncodeMaybeCopyAndReturnLength(ws, buffer, buflen); + } + ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFAnnot_IsOptionSelected(FPDF_FORMHANDLE handle, ++ FPDF_ANNOTATION annot, ++ int index) { ++ if (index < 0) ++ return false; ++ ++ const CPDF_FormField* form_field = GetFormField(handle, annot); ++ if (!form_field || index >= form_field->CountOptions()) ++ return false; ++ ++ if (form_field->GetFieldType() != FormFieldType::kComboBox && ++ form_field->GetFieldType() != FormFieldType::kListBox) { ++ return false; ++ } ++ ++ return form_field->IsItemSelected(index); ++} ++ + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFAnnot_GetFontSize(FPDF_FORMHANDLE hHandle, + FPDF_ANNOTATION annot, +@@ -1021,7 +1326,7 @@ FPDFAnnot_GetFontSize(FPDF_FORMHANDLE hHandle, + if (!pForm) + return false; + +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); + if (!pAnnotDict) + return false; + +@@ -1040,28 +1345,127 @@ FPDFAnnot_GetFontSize(FPDF_FORMHANDLE hHandle, + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_IsChecked(FPDF_FORMHANDLE hHandle, + FPDF_ANNOTATION annot) { +- CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle); +- if (!pForm) ++ const CPDFSDK_Widget* pWidget = ++ GetRadioButtonOrCheckBoxWidget(hHandle, annot); ++ return pWidget && pWidget->IsChecked(); ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFAnnot_SetFocusableSubtypes(FPDF_FORMHANDLE hHandle, ++ const FPDF_ANNOTATION_SUBTYPE* subtypes, ++ size_t count) { ++ CPDFSDK_FormFillEnvironment* pFormFillEnv = ++ CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle); ++ if (!pFormFillEnv) + return false; + +- CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); +- if (!pAnnotDict) ++ if (count > 0 && !subtypes) + return false; + +- CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm(); +- CPDF_FormField* pFormField = pPDFForm->GetFieldByDict(pAnnotDict); +- if (!pFormField) ++ std::vector focusable_annot_types; ++ focusable_annot_types.reserve(count); ++ for (size_t i = 0; i < count; ++i) { ++ focusable_annot_types.push_back( ++ static_cast(subtypes[i])); ++ } ++ ++ pFormFillEnv->SetFocusableAnnotSubtypes(focusable_annot_types); ++ return true; ++} ++ ++FPDF_EXPORT int FPDF_CALLCONV ++FPDFAnnot_GetFocusableSubtypesCount(FPDF_FORMHANDLE hHandle) { ++ CPDFSDK_FormFillEnvironment* pFormFillEnv = ++ CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle); ++ if (!pFormFillEnv) ++ return -1; ++ ++ return fxcrt::CollectionSize(pFormFillEnv->GetFocusableAnnotSubtypes()); ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFAnnot_GetFocusableSubtypes(FPDF_FORMHANDLE hHandle, ++ FPDF_ANNOTATION_SUBTYPE* subtypes, ++ size_t count) { ++ CPDFSDK_FormFillEnvironment* pFormFillEnv = ++ CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle); ++ if (!pFormFillEnv) + return false; + +- if (pFormField->GetType() != CPDF_FormField::kCheckBox && +- pFormField->GetType() != CPDF_FormField::kRadioButton) { ++ if (!subtypes) + return false; ++ ++ const std::vector& focusable_annot_types = ++ pFormFillEnv->GetFocusableAnnotSubtypes(); ++ ++ // Host should allocate enough memory to get the list of currently supported ++ // focusable subtypes. ++ if (count < focusable_annot_types.size()) ++ return false; ++ ++ for (size_t i = 0; i < focusable_annot_types.size(); ++i) { ++ subtypes[i] = ++ static_cast(focusable_annot_types[i]); + } + ++ return true; ++} ++ ++FPDF_EXPORT FPDF_LINK FPDF_CALLCONV FPDFAnnot_GetLink(FPDF_ANNOTATION annot) { ++ if (FPDFAnnot_GetSubtype(annot) != FPDF_ANNOT_LINK) ++ return nullptr; ++ ++ // Unretained reference in public API. NOLINTNEXTLINE ++ return FPDFLinkFromCPDFDictionary( ++ CPDFAnnotContextFromFPDFAnnotation(annot)->GetMutableAnnotDict()); ++} ++ ++FPDF_EXPORT int FPDF_CALLCONV ++FPDFAnnot_GetFormControlCount(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) { ++ CPDF_FormField* pFormField = GetFormField(hHandle, annot); ++ return pFormField ? pFormField->CountControls() : -1; ++} ++ ++FPDF_EXPORT int FPDF_CALLCONV ++FPDFAnnot_GetFormControlIndex(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) { ++ const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot); ++ if (!pAnnotDict) ++ return -1; ++ ++ CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle); ++ if (!pForm) ++ return -1; ++ ++ CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm(); ++ CPDF_FormField* pFormField = pPDFForm->GetFieldByDict(pAnnotDict); + CPDF_FormControl* pFormControl = pPDFForm->GetControlByDict(pAnnotDict); +- if (!pFormControl) ++ return pFormField ? pFormField->GetControlIndex(pFormControl) : -1; ++} ++ ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFAnnot_GetFormFieldExportValue(FPDF_FORMHANDLE hHandle, ++ FPDF_ANNOTATION annot, ++ FPDF_WCHAR* buffer, ++ unsigned long buflen) { ++ const CPDFSDK_Widget* pWidget = ++ GetRadioButtonOrCheckBoxWidget(hHandle, annot); ++ if (!pWidget) ++ return 0; ++ ++ return Utf16EncodeMaybeCopyAndReturnLength(pWidget->GetExportValue(), buffer, ++ buflen); ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetURI(FPDF_ANNOTATION annot, ++ const char* uri) { ++ if (!uri || FPDFAnnot_GetSubtype(annot) != FPDF_ANNOT_LINK) + return false; + +- CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl); +- return pWidget && pWidget->IsChecked(); ++ RetainPtr annot_dict = ++ GetMutableAnnotDictFromFPDFAnnotation(annot); ++ auto action = annot_dict->SetNewFor("A"); ++ action->SetNewFor("Type", "Action"); ++ action->SetNewFor("S", "URI"); ++ action->SetNewFor("URI", uri, /*bHex=*/false); ++ return true; + } +diff --git a/fpdfsdk/fpdf_annot_embeddertest.cpp b/fpdfsdk/fpdf_annot_embeddertest.cpp +index 1e0ebd8c0..06da6a8d2 100644 +--- a/fpdfsdk/fpdf_annot_embeddertest.cpp ++++ b/fpdfsdk/fpdf_annot_embeddertest.cpp +@@ -1,29 +1,345 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include "public/fpdf_annot.h" ++ ++#include ++ + #include +-#include +-#include + #include + #include + + #include "build/build_config.h" + #include "constants/annotation_common.h" ++#include "core/fpdfapi/page/cpdf_annotcontext.h" ++#include "core/fpdfapi/parser/cpdf_array.h" ++#include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fxcrt/fx_system.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" ++#include "fpdfsdk/cpdfsdk_helpers.h" + #include "public/cpp/fpdf_scopers.h" +-#include "public/fpdf_annot.h" + #include "public/fpdf_edit.h" + #include "public/fpdf_formfill.h" + #include "public/fpdfview.h" + #include "testing/embedder_test.h" ++#include "testing/embedder_test_constants.h" + #include "testing/fx_string_testhelpers.h" + #include "testing/gmock/include/gmock/gmock-matchers.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/utils/hash.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/span.h" ++ ++using pdfium::AnnotationStampWithApChecksum; ++ ++namespace { ++ ++const wchar_t kStreamData[] = ++ L"/GS gs 0.0 0.0 0.0 RG 4 w 211.8 747.6 m 211.8 744.8 " ++ L"212.6 743.0 214.2 740.8 " ++ L"c 215.4 739.0 216.8 737.1 218.9 736.1 c 220.8 735.1 221.4 733.0 " ++ L"223.7 732.4 c 232.6 729.9 242.0 730.8 251.2 730.8 c 257.5 730.8 " ++ L"263.0 732.9 269.0 734.4 c S"; ++ ++void VerifyFocusableAnnotSubtypes( ++ FPDF_FORMHANDLE form_handle, ++ pdfium::span expected_subtypes) { ++ ASSERT_EQ(static_cast(expected_subtypes.size()), ++ FPDFAnnot_GetFocusableSubtypesCount(form_handle)); ++ ++ std::vector actual_subtypes( ++ expected_subtypes.size()); ++ ASSERT_TRUE(FPDFAnnot_GetFocusableSubtypes( ++ form_handle, actual_subtypes.data(), actual_subtypes.size())); ++ for (size_t i = 0; i < expected_subtypes.size(); ++i) ++ ASSERT_EQ(expected_subtypes[i], actual_subtypes[i]); ++} ++ ++void SetAndVerifyFocusableAnnotSubtypes( ++ FPDF_FORMHANDLE form_handle, ++ pdfium::span subtypes) { ++ ASSERT_TRUE(FPDFAnnot_SetFocusableSubtypes(form_handle, subtypes.data(), ++ subtypes.size())); ++ VerifyFocusableAnnotSubtypes(form_handle, subtypes); ++} ++ ++void VerifyAnnotationSubtypesAndFocusability( ++ FPDF_FORMHANDLE form_handle, ++ FPDF_PAGE page, ++ pdfium::span expected_subtypes, ++ pdfium::span expected_focusable_subtypes) { ++ ASSERT_EQ(static_cast(expected_subtypes.size()), ++ FPDFPage_GetAnnotCount(page)); ++ for (size_t i = 0; i < expected_subtypes.size(); ++i) { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, i)); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(expected_subtypes[i], FPDFAnnot_GetSubtype(annot.get())); ++ ++ bool expected_focusable = ++ pdfium::Contains(expected_focusable_subtypes, expected_subtypes[i]); ++ EXPECT_EQ(expected_focusable, ++ FORM_SetFocusedAnnot(form_handle, annot.get())); ++ ++ // Kill the focus so the next test starts in an unfocused state. ++ FORM_ForceToKillFocus(form_handle); ++ } ++} ++ ++void VerifyUriActionInLink(FPDF_DOCUMENT doc, ++ FPDF_LINK link, ++ const std::string& expected_uri) { ++ ASSERT_TRUE(link); ++ ++ FPDF_ACTION action = FPDFLink_GetAction(link); ++ ASSERT_TRUE(action); ++ EXPECT_EQ(static_cast(PDFACTION_URI), ++ FPDFAction_GetType(action)); ++ ++ unsigned long bufsize = FPDFAction_GetURIPath(doc, action, nullptr, 0); ++ ASSERT_EQ(expected_uri.size() + 1, bufsize); ++ ++ std::vector buffer(bufsize); ++ EXPECT_EQ(bufsize, ++ FPDFAction_GetURIPath(doc, action, buffer.data(), bufsize)); ++ EXPECT_STREQ(expected_uri.c_str(), buffer.data()); ++} ++ ++} // namespace + + class FPDFAnnotEmbedderTest : public EmbedderTest {}; + ++TEST_F(FPDFAnnotEmbedderTest, SetAP) { ++ ScopedFPDFDocument doc(FPDF_CreateNewDocument()); ++ ASSERT_TRUE(doc); ++ ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100)); ++ ASSERT_TRUE(page); ++ ScopedFPDFWideString ap_stream = GetFPDFWideString(kStreamData); ++ ASSERT_TRUE(ap_stream); ++ ++ ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK)); ++ ASSERT_TRUE(annot); ++ ++ // Negative case: FPDFAnnot_SetAP() should fail if bounding rect is not yet ++ // set on the annotation. ++ EXPECT_FALSE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, ++ ap_stream.get())); ++ ++ const FS_RECTF bounding_rect{206.0f, 753.0f, 339.0f, 709.0f}; ++ EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &bounding_rect)); ++ ++ ASSERT_TRUE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, ++ /*R=*/255, /*G=*/0, /*B=*/0, /*A=*/255)); ++ ++ EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, ++ ap_stream.get())); ++ ++ // Verify that appearance stream is created as form XObject ++ CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot.get()); ++ ASSERT_TRUE(context); ++ const CPDF_Dictionary* annot_dict = context->GetAnnotDict(); ++ ASSERT_TRUE(annot_dict); ++ RetainPtr ap_dict = ++ annot_dict->GetDictFor(pdfium::annotation::kAP); ++ ASSERT_TRUE(ap_dict); ++ RetainPtr stream_dict = ap_dict->GetDictFor("N"); ++ ASSERT_TRUE(stream_dict); ++ // Check for non-existence of resources dictionary in case of opaque color ++ RetainPtr resources_dict = ++ stream_dict->GetDictFor("Resources"); ++ ASSERT_FALSE(resources_dict); ++ ByteString type = stream_dict->GetByteStringFor(pdfium::annotation::kType); ++ EXPECT_EQ("XObject", type); ++ ByteString sub_type = ++ stream_dict->GetByteStringFor(pdfium::annotation::kSubtype); ++ EXPECT_EQ("Form", sub_type); ++ ++ // Check that the appearance stream is same as we just set. ++ const uint32_t kStreamDataSize = std::size(kStreamData) * sizeof(FPDF_WCHAR); ++ unsigned long normal_length_bytes = FPDFAnnot_GetAP( ++ annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, nullptr, 0); ++ ASSERT_EQ(kStreamDataSize, normal_length_bytes); ++ std::vector buf = GetFPDFWideStringBuffer(normal_length_bytes); ++ EXPECT_EQ(kStreamDataSize, ++ FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, ++ buf.data(), normal_length_bytes)); ++ EXPECT_EQ(kStreamData, GetPlatformWString(buf.data())); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, SetAPWithOpacity) { ++ ScopedFPDFDocument doc(FPDF_CreateNewDocument()); ++ ASSERT_TRUE(doc); ++ ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100)); ++ ASSERT_TRUE(page); ++ ScopedFPDFWideString ap_stream = GetFPDFWideString(kStreamData); ++ ASSERT_TRUE(ap_stream); ++ ++ ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK)); ++ ASSERT_TRUE(annot); ++ ++ ASSERT_TRUE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, ++ /*R=*/255, /*G=*/0, /*B=*/0, /*A=*/102)); ++ ++ const FS_RECTF bounding_rect{206.0f, 753.0f, 339.0f, 709.0f}; ++ EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &bounding_rect)); ++ ++ EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, ++ ap_stream.get())); ++ ++ CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot.get()); ++ ASSERT_TRUE(context); ++ const CPDF_Dictionary* annot_dict = context->GetAnnotDict(); ++ ASSERT_TRUE(annot_dict); ++ RetainPtr ap_dict = ++ annot_dict->GetDictFor(pdfium::annotation::kAP); ++ ASSERT_TRUE(ap_dict); ++ RetainPtr stream_dict = ap_dict->GetDictFor("N"); ++ ASSERT_TRUE(stream_dict); ++ RetainPtr resources_dict = ++ stream_dict->GetDictFor("Resources"); ++ ASSERT_TRUE(stream_dict); ++ RetainPtr extGState_dict = ++ resources_dict->GetDictFor("ExtGState"); ++ ASSERT_TRUE(extGState_dict); ++ RetainPtr gs_dict = extGState_dict->GetDictFor("GS"); ++ ASSERT_TRUE(gs_dict); ++ ByteString type = gs_dict->GetByteStringFor(pdfium::annotation::kType); ++ EXPECT_EQ("ExtGState", type); ++ float opacity = gs_dict->GetFloatFor("CA"); ++ // Opacity value of 102 is represented as 0.4f (=104/255) in /CA entry. ++ EXPECT_FLOAT_EQ(0.4f, opacity); ++ ByteString blend_mode = gs_dict->GetByteStringFor("BM"); ++ EXPECT_EQ("Normal", blend_mode); ++ bool alpha_source_flag = gs_dict->GetBooleanFor("AIS", true); ++ EXPECT_FALSE(alpha_source_flag); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, InkListAPIValidations) { ++ ScopedFPDFDocument doc(FPDF_CreateNewDocument()); ++ ASSERT_TRUE(doc); ++ ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100)); ++ ASSERT_TRUE(page); ++ ++ // Create a new ink annotation. ++ ScopedFPDFAnnotation ink_annot( ++ FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK)); ++ ASSERT_TRUE(ink_annot); ++ CPDF_AnnotContext* context = ++ CPDFAnnotContextFromFPDFAnnotation(ink_annot.get()); ++ ASSERT_TRUE(context); ++ const CPDF_Dictionary* annot_dict = context->GetAnnotDict(); ++ ASSERT_TRUE(annot_dict); ++ ++ static constexpr FS_POINTF kFirstInkStroke[] = { ++ {80.0f, 90.0f}, {81.0f, 91.0f}, {82.0f, 92.0f}, ++ {83.0f, 93.0f}, {84.0f, 94.0f}, {85.0f, 95.0f}}; ++ static constexpr size_t kFirstStrokePointCount = std::size(kFirstInkStroke); ++ ++ static constexpr FS_POINTF kSecondInkStroke[] = { ++ {70.0f, 90.0f}, {71.0f, 91.0f}, {72.0f, 92.0f}}; ++ static constexpr size_t kSecondStrokePointCount = std::size(kSecondInkStroke); ++ ++ static constexpr FS_POINTF kThirdInkStroke[] = {{60.0f, 90.0f}, ++ {61.0f, 91.0f}, ++ {62.0f, 92.0f}, ++ {63.0f, 93.0f}, ++ {64.0f, 94.0f}}; ++ static constexpr size_t kThirdStrokePointCount = std::size(kThirdInkStroke); ++ ++ // Negative test: |annot| is passed as nullptr. ++ EXPECT_EQ(-1, FPDFAnnot_AddInkStroke(nullptr, kFirstInkStroke, ++ kFirstStrokePointCount)); ++ ++ // Negative test: |annot| is not ink annotation. ++ // Create a new highlight annotation. ++ ScopedFPDFAnnotation highlight_annot( ++ FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_HIGHLIGHT)); ++ ASSERT_TRUE(highlight_annot); ++ EXPECT_EQ(-1, FPDFAnnot_AddInkStroke(highlight_annot.get(), kFirstInkStroke, ++ kFirstStrokePointCount)); ++ ++ // Negative test: passing |point_count| as 0. ++ EXPECT_EQ(-1, FPDFAnnot_AddInkStroke(ink_annot.get(), kFirstInkStroke, 0)); ++ ++ // Negative test: passing |points| array as nullptr. ++ EXPECT_EQ(-1, FPDFAnnot_AddInkStroke(ink_annot.get(), nullptr, ++ kFirstStrokePointCount)); ++ ++ // Negative test: passing |point_count| more than ULONG_MAX/2. ++ EXPECT_EQ(-1, FPDFAnnot_AddInkStroke(ink_annot.get(), kSecondInkStroke, ++ ULONG_MAX / 2 + 1)); ++ ++ // InkStroke should get added to ink annotation. Also inklist should get ++ // created. ++ EXPECT_EQ(0, FPDFAnnot_AddInkStroke(ink_annot.get(), kFirstInkStroke, ++ kFirstStrokePointCount)); ++ ++ RetainPtr inklist = annot_dict->GetArrayFor("InkList"); ++ ASSERT_TRUE(inklist); ++ EXPECT_EQ(1u, inklist->size()); ++ EXPECT_EQ(kFirstStrokePointCount * 2, inklist->GetArrayAt(0)->size()); ++ ++ // Adding another inkStroke to ink annotation with all valid paremeters. ++ // InkList already exists in ink_annot. ++ EXPECT_EQ(1, FPDFAnnot_AddInkStroke(ink_annot.get(), kSecondInkStroke, ++ kSecondStrokePointCount)); ++ EXPECT_EQ(2u, inklist->size()); ++ EXPECT_EQ(kSecondStrokePointCount * 2, inklist->GetArrayAt(1)->size()); ++ ++ // Adding one more InkStroke to the ink annotation. |point_count| passed is ++ // less than the data available in |buffer|. ++ EXPECT_EQ(2, FPDFAnnot_AddInkStroke(ink_annot.get(), kThirdInkStroke, ++ kThirdStrokePointCount - 1)); ++ EXPECT_EQ(3u, inklist->size()); ++ EXPECT_EQ((kThirdStrokePointCount - 1) * 2, inklist->GetArrayAt(2)->size()); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, RemoveInkList) { ++ ScopedFPDFDocument doc(FPDF_CreateNewDocument()); ++ ASSERT_TRUE(doc); ++ ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100)); ++ ASSERT_TRUE(page); ++ ++ // Negative test: |annot| is passed as nullptr. ++ EXPECT_FALSE(FPDFAnnot_RemoveInkList(nullptr)); ++ ++ // Negative test: |annot| is not ink annotation. ++ // Create a new highlight annotation. ++ ScopedFPDFAnnotation highlight_annot( ++ FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_HIGHLIGHT)); ++ ASSERT_TRUE(highlight_annot); ++ EXPECT_FALSE(FPDFAnnot_RemoveInkList(highlight_annot.get())); ++ ++ // Create a new ink annotation. ++ ScopedFPDFAnnotation ink_annot( ++ FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK)); ++ ASSERT_TRUE(ink_annot); ++ CPDF_AnnotContext* context = ++ CPDFAnnotContextFromFPDFAnnotation(ink_annot.get()); ++ ASSERT_TRUE(context); ++ const CPDF_Dictionary* annot_dict = context->GetAnnotDict(); ++ ASSERT_TRUE(annot_dict); ++ ++ static constexpr FS_POINTF kInkStroke[] = {{80.0f, 90.0f}, {81.0f, 91.0f}, ++ {82.0f, 92.0f}, {83.0f, 93.0f}, ++ {84.0f, 94.0f}, {85.0f, 95.0f}}; ++ static constexpr size_t kPointCount = std::size(kInkStroke); ++ ++ // InkStroke should get added to ink annotation. Also inklist should get ++ // created. ++ EXPECT_EQ(0, ++ FPDFAnnot_AddInkStroke(ink_annot.get(), kInkStroke, kPointCount)); ++ ++ RetainPtr inklist = annot_dict->GetArrayFor("InkList"); ++ ASSERT_TRUE(inklist); ++ ASSERT_EQ(1u, inklist->size()); ++ EXPECT_EQ(kPointCount * 2, inklist->GetArrayAt(0)->size()); ++ ++ // Remove inklist. ++ EXPECT_TRUE(FPDFAnnot_RemoveInkList(ink_annot.get())); ++ EXPECT_FALSE(annot_dict->KeyExist("InkList")); ++} ++ + TEST_F(FPDFAnnotEmbedderTest, BadParams) { + ASSERT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); +@@ -87,23 +403,20 @@ TEST_F(FPDFAnnotEmbedderTest, RenderAnnotWithOnlyRolloverAP) { + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_RenderMultilineMarkupAnnotWithoutAP \ +- DISABLED_RenderMultilineMarkupAnnotWithoutAP +-#else +-#define MAYBE_RenderMultilineMarkupAnnotWithoutAP \ +- RenderMultilineMarkupAnnotWithoutAP +-#endif +-TEST_F(FPDFAnnotEmbedderTest, MAYBE_RenderMultilineMarkupAnnotWithoutAP) { +- static const char kMd5[] = "76512832d88017668d9acc7aacd13dae"; ++TEST_F(FPDFAnnotEmbedderTest, RenderMultilineMarkupAnnotWithoutAP) { ++ const char* checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "ec1f4ccbd0aecfdea6d53893387a0101"; ++ return "76512832d88017668d9acc7aacd13dae"; ++ }(); ++ + // Open a file with multiline markup annotations. + ASSERT_TRUE(OpenDocument("annotation_markup_multiline_no_ap.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 595, 842, kMd5); ++ CompareBitmap(bitmap.get(), 595, 842, checksum); + + UnloadPage(page); + } +@@ -192,13 +505,7 @@ TEST_F(FPDFAnnotEmbedderTest, ExtractHighlightLongContent) { + UnloadPageNoEvents(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_ExtractInkMultiple DISABLED_ExtractInkMultiple +-#else +-#define MAYBE_ExtractInkMultiple ExtractInkMultiple +-#endif +-TEST_F(FPDFAnnotEmbedderTest, MAYBE_ExtractInkMultiple) { ++TEST_F(FPDFAnnotEmbedderTest, ExtractInkMultiple) { + // Open a file with three annotations and load its first page. + ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf")); + FPDF_PAGE page = LoadPageNoEvents(0); +@@ -239,8 +546,13 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_ExtractInkMultiple) { + EXPECT_EQ(681.535034f, rect.top); + } + { ++ const char* expected_hash = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "fad91b9c968fe8019a774f5e2419b8fc"; ++ return "354002e1c4386d38fdde29ef8d61074a"; ++ }(); + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 612, 792, "354002e1c4386d38fdde29ef8d61074a"); ++ CompareBitmap(bitmap.get(), 612, 792, expected_hash); + } + UnloadPageNoEvents(page); + } +@@ -342,14 +654,85 @@ TEST_F(FPDFAnnotEmbedderTest, AddFirstTextAnnotation) { + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_AddAndSaveUnderlineAnnotation \ +- DISABLED_AddAndSaveUnderlineAnnotation +-#else +-#define MAYBE_AddAndSaveUnderlineAnnotation AddAndSaveUnderlineAnnotation +-#endif +-TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndSaveUnderlineAnnotation) { ++TEST_F(FPDFAnnotEmbedderTest, AddAndSaveLinkAnnotation) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ { ++ ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); ++ CompareBitmap(bitmap.get(), 200, 200, pdfium::HelloWorldChecksum()); ++ } ++ EXPECT_EQ(0, FPDFPage_GetAnnotCount(page)); ++ ++ constexpr char kUri[] = "https://pdfium.org/"; ++ ++ { ++ // Add a link annotation to the page and set its URI. ++ ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_LINK)); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(1, FPDFPage_GetAnnotCount(page)); ++ EXPECT_EQ(FPDF_ANNOT_LINK, FPDFAnnot_GetSubtype(annot.get())); ++ EXPECT_TRUE(FPDFAnnot_SetURI(annot.get(), kUri)); ++ VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri); ++ ++ // Negative tests: ++ EXPECT_FALSE(FPDFAnnot_SetURI(nullptr, nullptr)); ++ VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri); ++ EXPECT_FALSE(FPDFAnnot_SetURI(annot.get(), nullptr)); ++ VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri); ++ EXPECT_FALSE(FPDFAnnot_SetURI(nullptr, kUri)); ++ VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri); ++ ++ // Position the link on top of "Hello, world!" without a border. ++ const FS_RECTF kRect = {19.0f, 48.0f, 85.0f, 60.0f}; ++ EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &kRect)); ++ EXPECT_TRUE(FPDFAnnot_SetBorder(annot.get(), /*horizontal_radius=*/0.0f, ++ /*vertical_radius=*/0.0f, ++ /*border_width=*/0.0f)); ++ ++ VerifyUriActionInLink(document(), FPDFLink_GetLinkAtPoint(page, 40.0, 50.0), ++ kUri); ++ } ++ ++ { ++ // Add an ink annotation to the page. Trying to add a link to it fails. ++ ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_INK)); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(2, FPDFPage_GetAnnotCount(page)); ++ EXPECT_EQ(FPDF_ANNOT_INK, FPDFAnnot_GetSubtype(annot.get())); ++ EXPECT_FALSE(FPDFAnnot_SetURI(annot.get(), kUri)); ++ } ++ ++ // Remove the ink annotation added above for negative testing. ++ EXPECT_TRUE(FPDFPage_RemoveAnnot(page, 1)); ++ EXPECT_EQ(1, FPDFPage_GetAnnotCount(page)); ++ ++ // Save the document, closing the page. ++ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ UnloadPage(page); ++ ++ // Reopen the document and make sure it still renders the same. Since the link ++ // does not have a border, it does not affect the rendering. ++ ASSERT_TRUE(OpenSavedDocument()); ++ page = LoadSavedPage(0); ++ ASSERT_TRUE(page); ++ VerifySavedRendering(page, 200, 200, pdfium::HelloWorldChecksum()); ++ EXPECT_EQ(1, FPDFPage_GetAnnotCount(page)); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(FPDF_ANNOT_LINK, FPDFAnnot_GetSubtype(annot.get())); ++ VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri); ++ VerifyUriActionInLink(document(), FPDFLink_GetLinkAtPoint(page, 40.0, 50.0), ++ kUri); ++ } ++ ++ CloseSavedPage(page); ++ CloseSavedDocument(); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, AddAndSaveUnderlineAnnotation) { + // Open a file with one annotation and load its first page. + ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf")); + FPDF_PAGE page = LoadPage(0); +@@ -379,16 +762,21 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndSaveUnderlineAnnotation) { + ASSERT_TRUE(FPDFAnnot_AppendAttachmentPoints(annot.get(), &quadpoints)); + } + +- // Save the document, closing the page and document. ++ // Save the document and close the page. + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + UnloadPage(page); + + // Open the saved document. +- static const char kMd5[] = "dba153419f67b7c0c0e3d22d3e8910d5"; ++ const char* checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "899387ae792390cd0d83cf7e2bbebfb5"; ++ return "dba153419f67b7c0c0e3d22d3e8910d5"; ++ }(); + + ASSERT_TRUE(OpenSavedDocument()); + page = LoadSavedPage(0); +- VerifySavedRendering(page, 612, 792, kMd5); ++ ASSERT_TRUE(page); ++ VerifySavedRendering(page, 612, 792, checksum); + + // Check that the saved document has 2 annotations on the first page + EXPECT_EQ(2, FPDFPage_GetAnnotCount(page)); +@@ -496,29 +884,34 @@ TEST_F(FPDFAnnotEmbedderTest, GetAndSetQuadPoints) { + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_ModifyRectQuadpointsWithAP DISABLED_ModifyRectQuadpointsWithAP ++TEST_F(FPDFAnnotEmbedderTest, ModifyRectQuadpointsWithAP) { ++ const char* md5_original = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "0dd4c099b93d24eed9926a948ac5101c"; ++#if BUILDFLAG(IS_APPLE) ++ return "fc59468d154f397fd298c69f47ef565a"; ++#else ++ return "0e27376094f11490f74c65f3dc3a42c5"; ++#endif ++ }(); ++ const char* md5_modified_highlight = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "92dfe7960d248635a694f43c66db7a4d"; ++#if BUILDFLAG(IS_APPLE) ++ return "e64bf648f6e9354d1f3eedb47a2c9498"; + #else +-#define MAYBE_ModifyRectQuadpointsWithAP ModifyRectQuadpointsWithAP ++ return "66f3caef3a7d488a4fa1ad37fc06310e"; + #endif +-TEST_F(FPDFAnnotEmbedderTest, MAYBE_ModifyRectQuadpointsWithAP) { +-#if defined(OS_MACOSX) +- static const char kMd5Original[] = "63af8432fab95a67cdebb7cd0e514941"; +- static const char kMd5ModifiedHighlight[] = +- "aec26075011349dec9bace891856b5f2"; +- static const char kMd5ModifiedSquare[] = "057f57a32be95975775e5ec513fdcb56"; +-#elif defined(OS_WIN) +- static const char kMd5Original[] = "0e27376094f11490f74c65f3dc3a42c5"; +- static const char kMd5ModifiedHighlight[] = +- "66f3caef3a7d488a4fa1ad37fc06310e"; +- static const char kMd5ModifiedSquare[] = "a456dad0bc6801ee2d6408a4394af563"; ++ }(); ++ const char* md5_modified_square = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "fac81632e33fd5c06f39082a26d06ba8"; ++#if BUILDFLAG(IS_APPLE) ++ return "a66591662c8e7ad3c6059952e234bebf"; + #else +- static const char kMd5Original[] = "0e27376094f11490f74c65f3dc3a42c5"; +- static const char kMd5ModifiedHighlight[] = +- "66f3caef3a7d488a4fa1ad37fc06310e"; +- static const char kMd5ModifiedSquare[] = "a456dad0bc6801ee2d6408a4394af563"; ++ return "a456dad0bc6801ee2d6408a4394af563"; + #endif ++ }(); + + // Open a file with four annotations and load its first page. + ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf")); +@@ -529,7 +922,7 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_ModifyRectQuadpointsWithAP) { + // Check that the original file renders correctly. + { + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 612, 792, kMd5Original); ++ CompareBitmap(bitmap.get(), 612, 792, md5_original); + } + + FS_RECTF rect; +@@ -569,7 +962,7 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_ModifyRectQuadpointsWithAP) { + // Check that updating quadpoints does not change the annotation's position. + { + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 612, 792, kMd5Original); ++ CompareBitmap(bitmap.get(), 612, 792, md5_original); + } + + // Verify its annotation rectangle. +@@ -590,7 +983,7 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_ModifyRectQuadpointsWithAP) { + // Check that updating the rectangle changes the annotation's position. + { + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 612, 792, kMd5ModifiedHighlight); ++ CompareBitmap(bitmap.get(), 612, 792, md5_modified_highlight); + } + + { +@@ -610,7 +1003,7 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_ModifyRectQuadpointsWithAP) { + // Check that updating the rectangle changes the square annotation's + // position. + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 612, 792, kMd5ModifiedSquare); ++ CompareBitmap(bitmap.get(), 612, 792, md5_modified_square); + } + + UnloadPage(page); +@@ -673,7 +1066,7 @@ TEST_F(FPDFAnnotEmbedderTest, RemoveAnnotation) { + EXPECT_EQ(2, FPDFPage_GetAnnotCount(page)); + EXPECT_FALSE(FPDFPage_GetAnnot(page, 2)); + +- // Save the document, closing the page and document. ++ // Save the document and close the page. + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + UnloadPageNoEvents(page); + +@@ -710,29 +1103,34 @@ TEST_F(FPDFAnnotEmbedderTest, RemoveAnnotation) { + FPDF_CloseDocument(new_doc); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_AddAndModifyPath DISABLED_AddAndModifyPath ++TEST_F(FPDFAnnotEmbedderTest, AddAndModifyPath) { ++ const char* md5_modified_path = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "f671765166acf45d80e833ea3aff8b90"; ++#if BUILDFLAG(IS_APPLE) ++ return "34614087e04b729b7b8c37739dcf9af9"; + #else +-#define MAYBE_AddAndModifyPath AddAndModifyPath ++ return "31a94d22460171cd83169daf6a6956ee"; + #endif +-TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyPath) { +-#if defined(OS_MACOSX) +- static const char kMd5Original[] = "c35408717759562d1f8bf33d317483d2"; +- static const char kMd5ModifiedPath[] = "9059723a045e17478753d2f0eb33bc03"; +- static const char kMd5TwoPaths[] = "7eed0cfba780f1d4dd8068f717d3a6bf"; +- static const char kMd5NewAnnot[] = "1de8212d43b7066a6df042095c2aca61"; +-#elif defined(OS_WIN) +- static const char kMd5Original[] = "6aa001a77ec05d0f1b0d1d22e28744d4"; +- static const char kMd5ModifiedPath[] = "a7a8d675a6ddbcbdfecee65a33ba19e1"; +- static const char kMd5TwoPaths[] = "7c0bdd4552329704c47a7cce47edbbd6"; +- static const char kMd5NewAnnot[] = "3c48d492b4f62941fed0fb62f729f31e"; ++ }(); ++ const char* md5_two_paths = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "491ce8fb274cc83b55b6099f15457b5d"; ++#if BUILDFLAG(IS_APPLE) ++ return "6cdaf6b3e5145f435d8ccae6db5cf9af"; + #else +- static const char kMd5Original[] = "b42cef463483e668eaf4055a65e4f1f5"; +- static const char kMd5ModifiedPath[] = "6ff77d6d1fec4ea571fabe0c7a19b517"; +- static const char kMd5TwoPaths[] = "ca37ad549e74ac5b359a055708f3e7b6"; +- static const char kMd5NewAnnot[] = "0d7a0e33fbf41ff7fa5d732ab2c5edff"; ++ return "ed49fefef45f14121f8150cde10006c4"; + #endif ++ }(); ++ const char* md5_new_annot = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "92bfb06058ff608571a3baf65f7fc05d"; ++#if BUILDFLAG(IS_APPLE) ++ return "55dab4f158fdc284e439b88c4306373c"; ++#else ++ return "cc08493b1f079803930388ecc703be9d"; ++#endif ++ }(); + + // Open a file with two annotations and load its first page. + ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf")); +@@ -743,7 +1141,7 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyPath) { + // Check that the page renders correctly. + { + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 595, 842, kMd5Original); ++ CompareBitmap(bitmap.get(), 595, 842, AnnotationStampWithApChecksum()); + } + + { +@@ -767,7 +1165,7 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyPath) { + // Check that the page with the modified annotation renders correctly. + { + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 595, 842, kMd5ModifiedPath); ++ CompareBitmap(bitmap.get(), 595, 842, md5_modified_path); + } + + // Add a second path object to the same annotation. +@@ -786,7 +1184,7 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyPath) { + // Check that the page with an annotation with two paths renders correctly. + { + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 595, 842, kMd5TwoPaths); ++ CompareBitmap(bitmap.get(), 595, 842, md5_two_paths); + } + + // Delete the newly added path object. +@@ -798,7 +1196,7 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyPath) { + // Check that the page renders the same as before. + { + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 595, 842, kMd5ModifiedPath); ++ CompareBitmap(bitmap.get(), 595, 842, md5_modified_path); + } + + FS_RECTF rect; +@@ -834,14 +1232,15 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyPath) { + EXPECT_EQ(rect.top, new_rect.top); + } + +- // Save the document, closing the page and document. ++ // Save the document and close the page. + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + UnloadPage(page); + + // Open the saved document. + ASSERT_TRUE(OpenSavedDocument()); + page = LoadSavedPage(0); +- VerifySavedRendering(page, 595, 842, kMd5NewAnnot); ++ ASSERT_TRUE(page); ++ VerifySavedRendering(page, 595, 842, md5_new_annot); + + // Check that the document has a correct count of annotations and objects. + EXPECT_EQ(3, FPDFPage_GetAnnotCount(page)); +@@ -883,8 +1282,15 @@ TEST_F(FPDFAnnotEmbedderTest, ModifyAnnotationFlags) { + + // Check that the original flag values are as expected. + int flags = FPDFAnnot_GetFlags(annot.get()); ++ EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_INVISIBLE); + EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_HIDDEN); + EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_PRINT); ++ EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_NOZOOM); ++ EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_NOROTATE); ++ EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_NOVIEW); ++ EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_READONLY); ++ EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_LOCKED); ++ EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_TOGGLENOVIEW); + + // Set the HIDDEN flag. + flags |= FPDF_ANNOT_FLAG_HIDDEN; +@@ -896,7 +1302,7 @@ TEST_F(FPDFAnnotEmbedderTest, ModifyAnnotationFlags) { + // Check that the page renders correctly without rendering the annotation. + { + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3"); ++ CompareBitmap(bitmap.get(), 612, 792, pdfium::kBlankPage612By792Checksum); + } + + // Unset the HIDDEN flag. +@@ -918,26 +1324,25 @@ TEST_F(FPDFAnnotEmbedderTest, ModifyAnnotationFlags) { + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_AddAndModifyImage DISABLED_AddAndModifyImage ++TEST_F(FPDFAnnotEmbedderTest, AddAndModifyImage) { ++ const char* md5_new_image = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "4ba31e174d873b3fda1d7a160d4a0e85"; ++#if BUILDFLAG(IS_APPLE) ++ return "17ac49518eabbb6a7632a547269c40a3"; + #else +-#define MAYBE_AddAndModifyImage AddAndModifyImage ++ return "e79446398d4508bc2cb47e6cf2a677ed"; + #endif +-TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyImage) { +-#if defined(OS_MACOSX) +- static const char kMd5Original[] = "c35408717759562d1f8bf33d317483d2"; +- static const char kMd5NewImage[] = "ff012f5697436dfcaec25b32d1333596"; +- static const char kMd5ModifiedImage[] = "86cf8cb2755a7a2046a543e66d9c1e61"; +-#elif defined(OS_WIN) +- static const char kMd5Original[] = "6aa001a77ec05d0f1b0d1d22e28744d4"; +- static const char kMd5NewImage[] = "3d77d06a971bcb9fb54db082f1082c8b"; +- static const char kMd5ModifiedImage[] = "dc4f4afc26c345418330d31c065020e1"; ++ }(); ++ const char* md5_modified_image = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "5806fadc1a192bc4bb07511a0711c957"; ++#if BUILDFLAG(IS_APPLE) ++ return "ce68959f74242d588af7fb82be5ba0ab"; + #else +- static const char kMd5Original[] = "b42cef463483e668eaf4055a65e4f1f5"; +- static const char kMd5NewImage[] = "528e6243dc29d54f36b61e0d3287d935"; +- static const char kMd5ModifiedImage[] = "6d9e59f3e57a1ff82fb258356b7eb731"; ++ return "425646a517a23104b9ef22881a19b3e2"; + #endif ++ }(); + + // Open a file with two annotations and load its first page. + ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf")); +@@ -948,7 +1353,7 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyImage) { + // Check that the page renders correctly. + { + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 595, 842, kMd5Original); ++ CompareBitmap(bitmap.get(), 595, 842, AnnotationStampWithApChecksum()); + } + + constexpr int kBitmapSize = 200; +@@ -973,8 +1378,9 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyImage) { + EXPECT_EQ(kBitmapSize, FPDFBitmap_GetHeight(image_bitmap)); + FPDF_PAGEOBJECT image_object = FPDFPageObj_NewImageObj(document()); + ASSERT_TRUE(FPDFImageObj_SetBitmap(&page, 0, image_object, image_bitmap)); +- ASSERT_TRUE(FPDFImageObj_SetMatrix(image_object, kBitmapSize, 0, 0, +- kBitmapSize, 0, 0)); ++ static constexpr FS_MATRIX kBitmapScaleMatrix{kBitmapSize, 0, 0, ++ kBitmapSize, 0, 0}; ++ ASSERT_TRUE(FPDFPageObj_SetMatrix(image_object, &kBitmapScaleMatrix)); + FPDFPageObj_Transform(image_object, 1, 0, 0, 1, 200, 600); + EXPECT_TRUE(FPDFAnnot_AppendObject(annot.get(), image_object)); + } +@@ -982,7 +1388,7 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyImage) { + // Check that the page renders correctly with the new image object. + { + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 595, 842, kMd5NewImage); ++ CompareBitmap(bitmap.get(), 595, 842, md5_new_image); + } + + { +@@ -1000,35 +1406,38 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyImage) { + EXPECT_TRUE(FPDFAnnot_UpdateObject(annot.get(), image_object)); + } + +- // Save the document, closing the page and document. ++ // Save the document and close the page. + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + UnloadPage(page); + FPDFBitmap_Destroy(image_bitmap); + + // Test that the saved document renders the modified image object correctly. +- VerifySavedDocument(595, 842, kMd5ModifiedImage); ++ VerifySavedDocument(595, 842, md5_modified_image); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_AddAndModifyText DISABLED_AddAndModifyText ++TEST_F(FPDFAnnotEmbedderTest, AddAndModifyText) { ++ const char* md5_new_text = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "63b931799a9ba21c36d9d4f9711f252b"; ++#if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_ARM64) ++ return "0c3448974a4e8da2395da917935e5de1"; ++#elif BUILDFLAG(IS_APPLE) && !defined(ARCH_CPU_ARM64) ++ return "5d449d36926c9f212c6cdb6c276d18cc"; + #else +-#define MAYBE_AddAndModifyText AddAndModifyText ++ return "a9532f555aca2fd099e2107fa40b61e6"; + #endif +-TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyText) { +-#if defined(OS_MACOSX) +- static const char kMd5Original[] = "c35408717759562d1f8bf33d317483d2"; +- static const char kMd5NewText[] = "60031c1b0330cf1e1575f7d46687d429"; +- static const char kMd5ModifiedText[] = "79f5cfb0b07caaf936f65f6a7a57ce77"; +-#elif defined(OS_WIN) +- static const char kMd5Original[] = "6aa001a77ec05d0f1b0d1d22e28744d4"; +- static const char kMd5NewText[] = "204cc01749a70b8afc246a4ca33c7eb6"; +- static const char kMd5ModifiedText[] = "641261a45e8dfd68c89b80bfd237660d"; ++ }(); ++ const char* md5_modified_text = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "e29ddba6a49d5c9c5cdde7d1693a251c"; ++#if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_ARM64) ++ return "9cf1c024a9d2d356bcdd14cb71a32324"; ++#elif BUILDFLAG(IS_APPLE) && !defined(ARCH_CPU_ARM64) ++ return "8c992808db99dbe3d74006358a671f05"; + #else +- static const char kMd5Original[] = "b42cef463483e668eaf4055a65e4f1f5"; +- static const char kMd5NewText[] = "00197ad6206f763febad5719e5935306"; +- static const char kMd5ModifiedText[] = "85853bc0aaa5a4e3af04e58b9cbfff23"; ++ return "03cae68322d6a6ba120e738ab325408c"; + #endif ++ }(); + + // Open a file with two annotations and load its first page. + ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf")); +@@ -1039,7 +1448,7 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyText) { + // Check that the page renders correctly. + { + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 595, 842, kMd5Original); ++ CompareBitmap(bitmap.get(), 595, 842, AnnotationStampWithApChecksum()); + } + + { +@@ -1068,7 +1477,7 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyText) { + // Check that the page renders correctly with the new text object. + { + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 595, 842, kMd5NewText); ++ CompareBitmap(bitmap.get(), 595, 842, md5_new_text); + } + + { +@@ -1088,26 +1497,20 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_AddAndModifyText) { + // Check that the page renders correctly with the modified text object. + { + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 595, 842, kMd5ModifiedText); ++ CompareBitmap(bitmap.get(), 595, 842, md5_modified_text); + } + + // Remove the new annotation, and check that the page renders as before. + EXPECT_TRUE(FPDFPage_RemoveAnnot(page, 2)); + { + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 595, 842, kMd5Original); ++ CompareBitmap(bitmap.get(), 595, 842, AnnotationStampWithApChecksum()); + } + + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_GetSetStringValue DISABLED_GetSetStringValue +-#else +-#define MAYBE_GetSetStringValue GetSetStringValue +-#endif +-TEST_F(FPDFAnnotEmbedderTest, MAYBE_GetSetStringValue) { ++TEST_F(FPDFAnnotEmbedderTest, GetSetStringValue) { + // Open a file with four annotations and load its first page. + ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf")); + FPDF_PAGE page = LoadPage(0); +@@ -1158,22 +1561,25 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_GetSetStringValue) { + text.get())); + } + +- // Save the document, closing the page and document. ++ // Save the document and close the page. + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + UnloadPage(page); + +-#if defined(OS_MACOSX) +- static const char kMd5[] = "4d64e61c9c0f8c60ab3cc3234bb73b1c"; +-#elif defined(OS_WIN) +- static const char kMd5[] = "20b612ebd46babcb44c48c903e2c5a48"; ++ const char* md5 = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "2b9078043cd6130fef4e8542dcda943e"; ++#if BUILDFLAG(IS_APPLE) ++ return "52e93c54796f7f7167edf64e81d12bd7"; + #else +- static const char kMd5[] = "1d7bea2042c6fea0558ff2aef05811b5"; ++ return "5143f9a98beb7b00ff40b89110a1089f"; + #endif ++ }(); + + // Open the saved annotation. + ASSERT_TRUE(OpenSavedDocument()); + page = LoadSavedPage(0); +- VerifySavedRendering(page, 595, 842, kMd5); ++ ASSERT_TRUE(page); ++ VerifySavedRendering(page, 595, 842, md5); + { + ScopedFPDFAnnotation new_annot(FPDFPage_GetAnnot(page, 0)); + +@@ -1269,8 +1675,8 @@ TEST_F(FPDFAnnotEmbedderTest, GetSetAP) { + FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, + buf.data(), normal_length_bytes)); + EXPECT_EQ(kMd5NormalAP, +- GenerateMD5Base16(reinterpret_cast(buf.data()), +- normal_length_bytes)); ++ GenerateMD5Base16({reinterpret_cast(buf.data()), ++ normal_length_bytes})); + + // Check that the string value of an AP is returned through a buffer that is + // larger than necessary. +@@ -1279,8 +1685,8 @@ TEST_F(FPDFAnnotEmbedderTest, GetSetAP) { + FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, + buf.data(), normal_length_bytes + 2)); + EXPECT_EQ(kMd5NormalAP, +- GenerateMD5Base16(reinterpret_cast(buf.data()), +- normal_length_bytes)); ++ GenerateMD5Base16({reinterpret_cast(buf.data()), ++ normal_length_bytes})); + + // Check that getting an AP for a mode that does not have an AP returns an + // empty string. +@@ -1322,8 +1728,8 @@ TEST_F(FPDFAnnotEmbedderTest, GetSetAP) { + FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, + buf.data(), normal_length_bytes)); + EXPECT_EQ(kMd5NormalAP, +- GenerateMD5Base16(reinterpret_cast(buf.data()), +- normal_length_bytes)); ++ GenerateMD5Base16({reinterpret_cast(buf.data()), ++ normal_length_bytes})); + } + + // Save the modified document, then reopen it. +@@ -1332,6 +1738,7 @@ TEST_F(FPDFAnnotEmbedderTest, GetSetAP) { + + ASSERT_TRUE(OpenSavedDocument()); + page = LoadSavedPage(0); ++ ASSERT_TRUE(page); + { + ScopedFPDFAnnotation new_annot(FPDFPage_GetAnnot(page, 0)); + +@@ -1481,6 +1888,9 @@ TEST_F(FPDFAnnotEmbedderTest, GetFormFieldFlagsTextField) { + // Check that the flag values are as expected. + int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get()); + EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_MULTILINE); + EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_PASSWORD); + } + +@@ -1492,6 +1902,9 @@ TEST_F(FPDFAnnotEmbedderTest, GetFormFieldFlagsTextField) { + // Check that the flag values are as expected. + int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get()); + EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_MULTILINE); + EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_PASSWORD); + } + +@@ -1503,6 +1916,9 @@ TEST_F(FPDFAnnotEmbedderTest, GetFormFieldFlagsTextField) { + // Check that the flag values are as expected. + int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get()); + EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_MULTILINE); + EXPECT_TRUE(flags & FPDF_FORMFLAG_TEXT_PASSWORD); + } + +@@ -1523,8 +1939,11 @@ TEST_F(FPDFAnnotEmbedderTest, GetFormFieldFlagsComboBox) { + // Check that the flag values are as expected. + int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get()); + EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT); + EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO); + EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_EDIT); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT); + } + + { +@@ -1535,8 +1954,11 @@ TEST_F(FPDFAnnotEmbedderTest, GetFormFieldFlagsComboBox) { + // Check that the flag values are as expected. + int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get()); + EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT); + EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO); + EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT); + } + + { +@@ -1547,8 +1969,11 @@ TEST_F(FPDFAnnotEmbedderTest, GetFormFieldFlagsComboBox) { + // Check that the flag values are as expected. + int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get()); + EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT); + EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO); + EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT); + } + + UnloadPage(page); +@@ -1556,7 +1981,7 @@ TEST_F(FPDFAnnotEmbedderTest, GetFormFieldFlagsComboBox) { + + TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotNull) { + // Open file with form text fields. +- EXPECT_TRUE(OpenDocument("text_form.pdf")); ++ ASSERT_TRUE(OpenDocument("text_form.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1584,7 +2009,7 @@ TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotNull) { + + TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotAndCheckFlagsTextField) { + // Open file with form text fields. +- EXPECT_TRUE(OpenDocument("text_form_multiple.pdf")); ++ ASSERT_TRUE(OpenDocument("text_form_multiple.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1597,6 +2022,8 @@ TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotAndCheckFlagsTextField) { + + // Check that interactive form annotation flag values are as expected. + int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get()); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT); + EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY); + } + +@@ -1610,6 +2037,8 @@ TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotAndCheckFlagsTextField) { + // Check that interactive form annotation flag values are as expected. + int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get()); + EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT); + } + + UnloadPage(page); +@@ -1617,7 +2046,7 @@ TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotAndCheckFlagsTextField) { + + TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotAndCheckFlagsComboBox) { + // Open file with form comboboxes. +- EXPECT_TRUE(OpenDocument("combobox_form.pdf")); ++ ASSERT_TRUE(OpenDocument("combobox_form.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1631,8 +2060,11 @@ TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotAndCheckFlagsComboBox) { + // Check that interactive form annotation flag values are as expected. + int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get()); + EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT); + EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO); + EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_EDIT); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT); + } + + { +@@ -1645,8 +2077,11 @@ TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotAndCheckFlagsComboBox) { + // Check that interactive form annotation flag values are as expected. + int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get()); + EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT); + EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO); + EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT); + } + + { +@@ -1659,22 +2094,23 @@ TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotAndCheckFlagsComboBox) { + // Check that interactive form annotation flag values are as expected. + int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get()); + EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT); + EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO); + EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT); ++ EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT); + } + + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_BUG_1206 DISABLED_BUG_1206 +-#else +-#define MAYBE_BUG_1206 BUG_1206 +-#endif +-TEST_F(FPDFAnnotEmbedderTest, MAYBE_BUG_1206) { +- static constexpr size_t kExpectedSize = 1609; +- static const char kExpectedBitmap[] = "0d9fc05c6762fd788bd23fd87a4967bc"; ++TEST_F(FPDFAnnotEmbedderTest, BUG_1206) { ++ const char* expected_bitmap = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "a1ea1ceebb26922fae576cb79ce63af0"; ++ return "0d9fc05c6762fd788bd23fd87a4967bc"; ++ }(); ++ static constexpr size_t kExpectedSize = 1590; + + ASSERT_TRUE(OpenDocument("bug_1206.pdf")); + +@@ -1687,7 +2123,7 @@ TEST_F(FPDFAnnotEmbedderTest, MAYBE_BUG_1206) { + + for (size_t i = 0; i < 10; ++i) { + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 612, 792, kExpectedBitmap); ++ CompareBitmap(bitmap.get(), 612, 792, expected_bitmap); + + ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + // TODO(https://crbug.com/pdfium/1206): This is wrong. The size should be +@@ -1995,6 +2431,147 @@ TEST_F(FPDFAnnotEmbedderTest, GetOptionLabelInvalidAnnotations) { + UnloadPage(page); + } + ++TEST_F(FPDFAnnotEmbedderTest, IsOptionSelectedCombobox) { ++ // Open a file with combobox widget annotations and load its first page. ++ ASSERT_TRUE(OpenDocument("combobox_form.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ ++ // Checks for Combobox with no Values (/V) or Selected Indices (/I) objects. ++ int count = FPDFAnnot_GetOptionCount(form_handle(), annot.get()); ++ ASSERT_EQ(3, count); ++ for (int i = 0; i < count; i++) { ++ EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i)); ++ } ++ ++ annot.reset(FPDFPage_GetAnnot(page, 1)); ++ ASSERT_TRUE(annot); ++ ++ // Checks for Combobox with Values (/V) object which is just a string. ++ count = FPDFAnnot_GetOptionCount(form_handle(), annot.get()); ++ ASSERT_EQ(26, count); ++ for (int i = 0; i < count; i++) { ++ EXPECT_EQ(i == 1, ++ FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i)); ++ } ++ ++ // Checks for index outside bound i.e. (index >= CountOption()). ++ EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), ++ /*index=*/26)); ++ // Checks for negetive index. ++ EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), ++ /*index=*/-1)); ++ ++ // Checks for bad form handle/annot. ++ EXPECT_FALSE(FPDFAnnot_IsOptionSelected(nullptr, nullptr, /*index=*/0)); ++ EXPECT_FALSE( ++ FPDFAnnot_IsOptionSelected(form_handle(), nullptr, /*index=*/0)); ++ EXPECT_FALSE(FPDFAnnot_IsOptionSelected(nullptr, annot.get(), /*index=*/0)); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, IsOptionSelectedListbox) { ++ // Open a file with listbox widget annotations and load its first page. ++ ASSERT_TRUE(OpenDocument("listbox_form.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ ++ // Checks for Listbox with no Values (/V) or Selected Indices (/I) objects. ++ int count = FPDFAnnot_GetOptionCount(form_handle(), annot.get()); ++ ASSERT_EQ(3, count); ++ for (int i = 0; i < count; i++) { ++ EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i)); ++ } ++ ++ annot.reset(FPDFPage_GetAnnot(page, 1)); ++ ASSERT_TRUE(annot); ++ ++ // Checks for Listbox with Values (/V) object which is just a string. ++ count = FPDFAnnot_GetOptionCount(form_handle(), annot.get()); ++ ASSERT_EQ(26, count); ++ for (int i = 0; i < count; i++) { ++ EXPECT_EQ(i == 1, ++ FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i)); ++ } ++ ++ annot.reset(FPDFPage_GetAnnot(page, 3)); ++ ASSERT_TRUE(annot); ++ ++ // Checks for Listbox with only Selected indices (/I) object which is an ++ // array with multiple objects. ++ count = FPDFAnnot_GetOptionCount(form_handle(), annot.get()); ++ ASSERT_EQ(5, count); ++ for (int i = 0; i < count; i++) { ++ bool expected = (i == 1 || i == 3); ++ EXPECT_EQ(expected, ++ FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i)); ++ } ++ ++ annot.reset(FPDFPage_GetAnnot(page, 4)); ++ ASSERT_TRUE(annot); ++ ++ // Checks for Listbox with Values (/V) object which is an array with ++ // multiple objects. ++ count = FPDFAnnot_GetOptionCount(form_handle(), annot.get()); ++ ASSERT_EQ(5, count); ++ for (int i = 0; i < count; i++) { ++ bool expected = (i == 2 || i == 4); ++ EXPECT_EQ(expected, ++ FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i)); ++ } ++ ++ annot.reset(FPDFPage_GetAnnot(page, 5)); ++ ASSERT_TRUE(annot); ++ ++ // Checks for Listbox with both Values (/V) and Selected Indices (/I) ++ // objects conflict with different lengths. ++ count = FPDFAnnot_GetOptionCount(form_handle(), annot.get()); ++ ASSERT_EQ(5, count); ++ for (int i = 0; i < count; i++) { ++ bool expected = (i == 0 || i == 2); ++ EXPECT_EQ(expected, ++ FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i)); ++ } ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, IsOptionSelectedInvalidAnnotations) { ++ // Open a file with multiple form field annotations and load its first page. ++ ASSERT_TRUE(OpenDocument("multiple_form_types.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ ++ // Checks for link annotation. ++ EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), ++ /*index=*/0)); ++ ++ annot.reset(FPDFPage_GetAnnot(page, 3)); ++ ASSERT_TRUE(annot); ++ ++ // Checks for text field annotation. ++ EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), ++ /*index=*/0)); ++ } ++ ++ UnloadPage(page); ++} ++ + TEST_F(FPDFAnnotEmbedderTest, GetFontSizeCombobox) { + // Open a file with combobox annotations and load its first page. + ASSERT_TRUE(OpenDocument("combobox_form.pdf")); +@@ -2238,34 +2815,30 @@ TEST_F(FPDFAnnotEmbedderTest, IsCheckedInvalidWidgetType) { + UnloadPage(page); + } + +-TEST_F(FPDFAnnotEmbedderTest, GetFormFieldTypeTextField) { +- ASSERT_TRUE(OpenDocument("text_form.pdf")); ++TEST_F(FPDFAnnotEmbedderTest, GetFormFieldType) { ++ ASSERT_TRUE(OpenDocument("multiple_form_types.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +- { +- EXPECT_EQ(-1, FPDFAnnot_GetFormFieldType(form_handle(), nullptr)); ++ EXPECT_EQ(-1, FPDFAnnot_GetFormFieldType(form_handle(), nullptr)); + +- ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1)); + ASSERT_TRUE(annot); +- + EXPECT_EQ(-1, FPDFAnnot_GetFormFieldType(nullptr, annot.get())); +- +- EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD, +- FPDFAnnot_GetFormFieldType(form_handle(), annot.get())); + } +- UnloadPage(page); +-} + +-TEST_F(FPDFAnnotEmbedderTest, GetFormFieldTypeComboBox) { +- ASSERT_TRUE(OpenDocument("combobox_form.pdf")); +- FPDF_PAGE page = LoadPage(0); +- ASSERT_TRUE(page); ++ constexpr int kExpectedAnnotTypes[] = {-1, ++ FPDF_FORMFIELD_COMBOBOX, ++ FPDF_FORMFIELD_LISTBOX, ++ FPDF_FORMFIELD_TEXTFIELD, ++ FPDF_FORMFIELD_CHECKBOX, ++ FPDF_FORMFIELD_RADIOBUTTON}; + +- { +- ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ for (size_t i = 0; i < std::size(kExpectedAnnotTypes); ++i) { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, i)); + ASSERT_TRUE(annot); +- EXPECT_EQ(FPDF_FORMFIELD_COMBOBOX, ++ EXPECT_EQ(kExpectedAnnotTypes[i], + FPDFAnnot_GetFormFieldType(form_handle(), annot.get())); + } + UnloadPage(page); +@@ -2385,3 +2958,744 @@ TEST_F(FPDFAnnotEmbedderTest, GetFormFieldNameComboBox) { + } + UnloadPage(page); + } ++ ++TEST_F(FPDFAnnotEmbedderTest, FocusableAnnotSubtypes) { ++ ASSERT_TRUE(OpenDocument("annots.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ // Verify widgets are by default focusable. ++ const FPDF_ANNOTATION_SUBTYPE kDefaultSubtypes[] = {FPDF_ANNOT_WIDGET}; ++ VerifyFocusableAnnotSubtypes(form_handle(), kDefaultSubtypes); ++ ++ // Expected annot subtypes for page 0 of annots.pdf. ++ const FPDF_ANNOTATION_SUBTYPE kExpectedAnnotSubtypes[] = { ++ FPDF_ANNOT_LINK, FPDF_ANNOT_LINK, FPDF_ANNOT_LINK, ++ FPDF_ANNOT_LINK, FPDF_ANNOT_HIGHLIGHT, FPDF_ANNOT_HIGHLIGHT, ++ FPDF_ANNOT_POPUP, FPDF_ANNOT_HIGHLIGHT, FPDF_ANNOT_WIDGET, ++ }; ++ ++ const FPDF_ANNOTATION_SUBTYPE kExpectedDefaultFocusableSubtypes[] = { ++ FPDF_ANNOT_WIDGET}; ++ VerifyAnnotationSubtypesAndFocusability(form_handle(), page, ++ kExpectedAnnotSubtypes, ++ kExpectedDefaultFocusableSubtypes); ++ ++ // Make no annotation type focusable using the preferred method. ++ ASSERT_TRUE(FPDFAnnot_SetFocusableSubtypes(form_handle(), nullptr, 0)); ++ ASSERT_EQ(0, FPDFAnnot_GetFocusableSubtypesCount(form_handle())); ++ ++ // Restore the focusable type count back to 1, then set it back to 0 using a ++ // different method. ++ SetAndVerifyFocusableAnnotSubtypes(form_handle(), kDefaultSubtypes); ++ ASSERT_TRUE( ++ FPDFAnnot_SetFocusableSubtypes(form_handle(), kDefaultSubtypes, 0)); ++ ASSERT_EQ(0, FPDFAnnot_GetFocusableSubtypesCount(form_handle())); ++ ++ VerifyAnnotationSubtypesAndFocusability(form_handle(), page, ++ kExpectedAnnotSubtypes, {}); ++ ++ // Now make links focusable. ++ const FPDF_ANNOTATION_SUBTYPE kLinkSubtypes[] = {FPDF_ANNOT_LINK}; ++ SetAndVerifyFocusableAnnotSubtypes(form_handle(), kLinkSubtypes); ++ ++ const FPDF_ANNOTATION_SUBTYPE kExpectedLinkocusableSubtypes[] = { ++ FPDF_ANNOT_LINK}; ++ VerifyAnnotationSubtypesAndFocusability(form_handle(), page, ++ kExpectedAnnotSubtypes, ++ kExpectedLinkocusableSubtypes); ++ ++ // Test invalid parameters. ++ EXPECT_FALSE(FPDFAnnot_SetFocusableSubtypes(nullptr, kDefaultSubtypes, ++ std::size(kDefaultSubtypes))); ++ EXPECT_FALSE(FPDFAnnot_SetFocusableSubtypes(form_handle(), nullptr, ++ std::size(kDefaultSubtypes))); ++ EXPECT_EQ(-1, FPDFAnnot_GetFocusableSubtypesCount(nullptr)); ++ ++ std::vector subtypes(1); ++ EXPECT_FALSE(FPDFAnnot_GetFocusableSubtypes(nullptr, subtypes.data(), ++ subtypes.size())); ++ EXPECT_FALSE( ++ FPDFAnnot_GetFocusableSubtypes(form_handle(), nullptr, subtypes.size())); ++ EXPECT_FALSE( ++ FPDFAnnot_GetFocusableSubtypes(form_handle(), subtypes.data(), 0)); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, FocusableAnnotRendering) { ++ ASSERT_TRUE(OpenDocument("annots.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ const char* md5_sum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "08b693ba461a24ee6b9c7f86b5dbe191"; ++#if BUILDFLAG(IS_APPLE) ++ return "108a46c517c4eaace9982ee83e8e3296"; ++#else ++ return "5550d8dcb4d1af1f50e8b4bcaef2ee60"; ++#endif ++ }(); ++ // Check the initial rendering. ++ ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); ++ CompareBitmap(bitmap.get(), 612, 792, md5_sum); ++ } ++ ++ // Make links and highlights focusable. ++ static constexpr FPDF_ANNOTATION_SUBTYPE kSubTypes[] = {FPDF_ANNOT_LINK, ++ FPDF_ANNOT_HIGHLIGHT}; ++ constexpr int kSubTypesCount = std::size(kSubTypes); ++ ASSERT_TRUE( ++ FPDFAnnot_SetFocusableSubtypes(form_handle(), kSubTypes, kSubTypesCount)); ++ ASSERT_EQ(kSubTypesCount, FPDFAnnot_GetFocusableSubtypesCount(form_handle())); ++ std::vector subtypes(kSubTypesCount); ++ ASSERT_TRUE(FPDFAnnot_GetFocusableSubtypes(form_handle(), subtypes.data(), ++ subtypes.size())); ++ ASSERT_EQ(FPDF_ANNOT_LINK, subtypes[0]); ++ ASSERT_EQ(FPDF_ANNOT_HIGHLIGHT, subtypes[1]); ++ ++ { ++ const char* md5_sum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "3e6ae77a45e49bfd909f3ee0351a4176"; ++#if BUILDFLAG(IS_APPLE) ++ return "eb3869335e7a219e1b5f25c1c6037b97"; ++#else ++ return "805fe7bb751ac4ed2b82bb66efe6db40"; ++#endif ++ }(); ++ // Focus the first link and check the rendering. ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(FPDF_ANNOT_LINK, FPDFAnnot_GetSubtype(annot.get())); ++ EXPECT_TRUE(FORM_SetFocusedAnnot(form_handle(), annot.get())); ++ ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); ++ CompareBitmap(bitmap.get(), 612, 792, md5_sum); ++ } ++ ++ { ++ const char* md5_sum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "12bb575d925923ad6672f427c574eef4"; ++#if BUILDFLAG(IS_APPLE) ++ return "d20b1978da2362d3942ea0fc6d230997"; ++#else ++ return "c5c5dcb462af3ef5f43b298ec048feef"; ++#endif ++ }(); ++ // Focus the first highlight and check the rendering. ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 4)); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot.get())); ++ EXPECT_TRUE(FORM_SetFocusedAnnot(form_handle(), annot.get())); ++ ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); ++ CompareBitmap(bitmap.get(), 612, 792, md5_sum); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, GetLinkFromAnnotation) { ++ ASSERT_TRUE(OpenDocument("annots.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ { ++ constexpr char kExpectedResult[] = ++ "https://cs.chromium.org/chromium/src/third_party/pdfium/public/" ++ "fpdf_text.h"; ++ ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 3)); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(FPDF_ANNOT_LINK, FPDFAnnot_GetSubtype(annot.get())); ++ VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), ++ kExpectedResult); ++ } ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 4)); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot.get())); ++ EXPECT_FALSE(FPDFAnnot_GetLink(annot.get())); ++ } ++ ++ EXPECT_FALSE(FPDFAnnot_GetLink(nullptr)); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, GetFormControlCountRadioButton) { ++ // Open a file with radio button widget annotations and load its first page. ++ ASSERT_TRUE(OpenDocument("click_form.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ // Checks for bad annot. ++ EXPECT_EQ(-1, ++ FPDFAnnot_GetFormControlCount(form_handle(), /*annot=*/nullptr)); ++ ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 3)); ++ ASSERT_TRUE(annot); ++ ++ // Checks for bad form handle. ++ EXPECT_EQ(-1, ++ FPDFAnnot_GetFormControlCount(/*hHandle=*/nullptr, annot.get())); ++ ++ EXPECT_EQ(3, FPDFAnnot_GetFormControlCount(form_handle(), annot.get())); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, GetFormControlCountCheckBox) { ++ // Open a file with checkbox widget annotations and load its first page. ++ ASSERT_TRUE(OpenDocument("click_form.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(1, FPDFAnnot_GetFormControlCount(form_handle(), annot.get())); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, GetFormControlCountInvalidAnnotation) { ++ // Open a file with ink annotations and load its first page. ++ ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(-1, FPDFAnnot_GetFormControlCount(form_handle(), annot.get())); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, GetFormControlIndexRadioButton) { ++ // Open a file with radio button widget annotations and load its first page. ++ ASSERT_TRUE(OpenDocument("click_form.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ // Checks for bad annot. ++ EXPECT_EQ(-1, ++ FPDFAnnot_GetFormControlIndex(form_handle(), /*annot=*/nullptr)); ++ ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 3)); ++ ASSERT_TRUE(annot); ++ ++ // Checks for bad form handle. ++ EXPECT_EQ(-1, ++ FPDFAnnot_GetFormControlIndex(/*hHandle=*/nullptr, annot.get())); ++ ++ EXPECT_EQ(1, FPDFAnnot_GetFormControlIndex(form_handle(), annot.get())); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, GetFormControlIndexCheckBox) { ++ // Open a file with checkbox widget annotations and load its first page. ++ ASSERT_TRUE(OpenDocument("click_form.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(0, FPDFAnnot_GetFormControlIndex(form_handle(), annot.get())); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, GetFormControlIndexInvalidAnnotation) { ++ // Open a file with ink annotations and load its first page. ++ ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(-1, FPDFAnnot_GetFormControlIndex(form_handle(), annot.get())); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, GetFormFieldExportValueRadioButton) { ++ // Open a file with radio button widget annotations and load its first page. ++ ASSERT_TRUE(OpenDocument("click_form.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ // Checks for bad annot. ++ EXPECT_EQ(0u, FPDFAnnot_GetFormFieldExportValue( ++ form_handle(), /*annot=*/nullptr, ++ /*buffer=*/nullptr, /*buflen=*/0)); ++ ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 6)); ++ ASSERT_TRUE(annot); ++ ++ // Checks for bad form handle. ++ EXPECT_EQ(0u, FPDFAnnot_GetFormFieldExportValue( ++ /*hHandle=*/nullptr, annot.get(), ++ /*buffer=*/nullptr, /*buflen=*/0)); ++ ++ unsigned long length_bytes = ++ FPDFAnnot_GetFormFieldExportValue(form_handle(), annot.get(), ++ /*buffer=*/nullptr, /*buflen=*/0); ++ ASSERT_EQ(14u, length_bytes); ++ std::vector buf = GetFPDFWideStringBuffer(length_bytes); ++ EXPECT_EQ(14u, FPDFAnnot_GetFormFieldExportValue(form_handle(), annot.get(), ++ buf.data(), length_bytes)); ++ EXPECT_EQ(L"value2", GetPlatformWString(buf.data())); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, GetFormFieldExportValueCheckBox) { ++ // Open a file with checkbox widget annotations and load its first page. ++ ASSERT_TRUE(OpenDocument("click_form.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ ++ unsigned long length_bytes = ++ FPDFAnnot_GetFormFieldExportValue(form_handle(), annot.get(), ++ /*buffer=*/nullptr, /*buflen=*/0); ++ ASSERT_EQ(8u, length_bytes); ++ std::vector buf = GetFPDFWideStringBuffer(length_bytes); ++ EXPECT_EQ(8u, FPDFAnnot_GetFormFieldExportValue(form_handle(), annot.get(), ++ buf.data(), length_bytes)); ++ EXPECT_EQ(L"Yes", GetPlatformWString(buf.data())); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, GetFormFieldExportValueInvalidAnnotation) { ++ // Open a file with ink annotations and load its first page. ++ ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(0u, FPDFAnnot_GetFormFieldExportValue(form_handle(), annot.get(), ++ /*buffer=*/nullptr, ++ /*buflen=*/0)); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, Redactannotation) { ++ ASSERT_TRUE(OpenDocument("redact_annot.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ EXPECT_EQ(1, FPDFPage_GetAnnotCount(page)); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(FPDF_ANNOT_REDACT, FPDFAnnot_GetSubtype(annot.get())); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, PolygonAnnotation) { ++ ASSERT_TRUE(OpenDocument("polygon_annot.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ EXPECT_EQ(2, FPDFPage_GetAnnotCount(page)); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ ++ // FPDFAnnot_GetVertices() positive testing. ++ unsigned long size = FPDFAnnot_GetVertices(annot.get(), nullptr, 0); ++ const size_t kExpectedSize = 3; ++ ASSERT_EQ(kExpectedSize, size); ++ std::vector vertices_buffer(size); ++ EXPECT_EQ(size, ++ FPDFAnnot_GetVertices(annot.get(), vertices_buffer.data(), size)); ++ EXPECT_FLOAT_EQ(159.0f, vertices_buffer[0].x); ++ EXPECT_FLOAT_EQ(296.0f, vertices_buffer[0].y); ++ EXPECT_FLOAT_EQ(350.0f, vertices_buffer[1].x); ++ EXPECT_FLOAT_EQ(411.0f, vertices_buffer[1].y); ++ EXPECT_FLOAT_EQ(472.0f, vertices_buffer[2].x); ++ EXPECT_FLOAT_EQ(243.42f, vertices_buffer[2].y); ++ ++ // FPDFAnnot_GetVertices() negative testing. ++ EXPECT_EQ(0U, FPDFAnnot_GetVertices(nullptr, nullptr, 0)); ++ ++ // vertices_buffer is not overwritten if it is too small. ++ vertices_buffer.resize(1); ++ vertices_buffer[0].x = 42; ++ vertices_buffer[0].y = 43; ++ size = FPDFAnnot_GetVertices(annot.get(), vertices_buffer.data(), ++ vertices_buffer.size()); ++ EXPECT_EQ(kExpectedSize, size); ++ EXPECT_FLOAT_EQ(42, vertices_buffer[0].x); ++ EXPECT_FLOAT_EQ(43, vertices_buffer[0].y); ++ } ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1)); ++ ASSERT_TRUE(annot); ++ ++ // This has an odd number of elements in the vertices array, ignore the last ++ // element. ++ unsigned long size = FPDFAnnot_GetVertices(annot.get(), nullptr, 0); ++ const size_t kExpectedSize = 3; ++ ASSERT_EQ(kExpectedSize, size); ++ std::vector vertices_buffer(size); ++ EXPECT_EQ(size, ++ FPDFAnnot_GetVertices(annot.get(), vertices_buffer.data(), size)); ++ EXPECT_FLOAT_EQ(259.0f, vertices_buffer[0].x); ++ EXPECT_FLOAT_EQ(396.0f, vertices_buffer[0].y); ++ EXPECT_FLOAT_EQ(450.0f, vertices_buffer[1].x); ++ EXPECT_FLOAT_EQ(511.0f, vertices_buffer[1].y); ++ EXPECT_FLOAT_EQ(572.0f, vertices_buffer[2].x); ++ EXPECT_FLOAT_EQ(343.0f, vertices_buffer[2].y); ++ } ++ ++ { ++ // Wrong annotation type. ++ ScopedFPDFAnnotation ink_annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_INK)); ++ EXPECT_EQ(0U, FPDFAnnot_GetVertices(ink_annot.get(), nullptr, 0)); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, InkAnnotation) { ++ ASSERT_TRUE(OpenDocument("ink_annot.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ EXPECT_EQ(2, FPDFPage_GetAnnotCount(page)); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ ++ // FPDFAnnot_GetInkListCount() and FPDFAnnot_GetInkListPath() positive ++ // testing. ++ unsigned long size = FPDFAnnot_GetInkListCount(annot.get()); ++ const size_t kExpectedSize = 1; ++ ASSERT_EQ(kExpectedSize, size); ++ const unsigned long kPathIndex = 0; ++ unsigned long path_size = ++ FPDFAnnot_GetInkListPath(annot.get(), kPathIndex, nullptr, 0); ++ const size_t kExpectedPathSize = 3; ++ ASSERT_EQ(kExpectedPathSize, path_size); ++ std::vector path_buffer(path_size); ++ EXPECT_EQ(path_size, ++ FPDFAnnot_GetInkListPath(annot.get(), kPathIndex, ++ path_buffer.data(), path_size)); ++ EXPECT_FLOAT_EQ(159.0f, path_buffer[0].x); ++ EXPECT_FLOAT_EQ(296.0f, path_buffer[0].y); ++ EXPECT_FLOAT_EQ(350.0f, path_buffer[1].x); ++ EXPECT_FLOAT_EQ(411.0f, path_buffer[1].y); ++ EXPECT_FLOAT_EQ(472.0f, path_buffer[2].x); ++ EXPECT_FLOAT_EQ(243.42f, path_buffer[2].y); ++ ++ // FPDFAnnot_GetInkListCount() and FPDFAnnot_GetInkListPath() negative ++ // testing. ++ EXPECT_EQ(0U, FPDFAnnot_GetInkListCount(nullptr)); ++ EXPECT_EQ(0U, FPDFAnnot_GetInkListPath(nullptr, 0, nullptr, 0)); ++ ++ // out of bounds path_index. ++ EXPECT_EQ(0U, FPDFAnnot_GetInkListPath(nullptr, 42, nullptr, 0)); ++ ++ // path_buffer is not overwritten if it is too small. ++ path_buffer.resize(1); ++ path_buffer[0].x = 42; ++ path_buffer[0].y = 43; ++ path_size = FPDFAnnot_GetInkListPath( ++ annot.get(), kPathIndex, path_buffer.data(), path_buffer.size()); ++ EXPECT_EQ(kExpectedPathSize, path_size); ++ EXPECT_FLOAT_EQ(42, path_buffer[0].x); ++ EXPECT_FLOAT_EQ(43, path_buffer[0].y); ++ } ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1)); ++ ASSERT_TRUE(annot); ++ ++ // This has an odd number of elements in the path array, ignore the last ++ // element. ++ unsigned long size = FPDFAnnot_GetInkListCount(annot.get()); ++ const size_t kExpectedSize = 1; ++ ASSERT_EQ(kExpectedSize, size); ++ const unsigned long kPathIndex = 0; ++ unsigned long path_size = ++ FPDFAnnot_GetInkListPath(annot.get(), kPathIndex, nullptr, 0); ++ const size_t kExpectedPathSize = 3; ++ ASSERT_EQ(kExpectedPathSize, path_size); ++ std::vector path_buffer(path_size); ++ EXPECT_EQ(path_size, ++ FPDFAnnot_GetInkListPath(annot.get(), kPathIndex, ++ path_buffer.data(), path_size)); ++ EXPECT_FLOAT_EQ(259.0f, path_buffer[0].x); ++ EXPECT_FLOAT_EQ(396.0f, path_buffer[0].y); ++ EXPECT_FLOAT_EQ(450.0f, path_buffer[1].x); ++ EXPECT_FLOAT_EQ(511.0f, path_buffer[1].y); ++ EXPECT_FLOAT_EQ(572.0f, path_buffer[2].x); ++ EXPECT_FLOAT_EQ(343.0f, path_buffer[2].y); ++ } ++ ++ { ++ // Wrong annotation type. ++ ScopedFPDFAnnotation polygon_annot( ++ FPDFPage_CreateAnnot(page, FPDF_ANNOT_POLYGON)); ++ EXPECT_EQ(0U, FPDFAnnot_GetInkListCount(polygon_annot.get())); ++ const unsigned long kPathIndex = 0; ++ EXPECT_EQ(0U, FPDFAnnot_GetInkListPath(polygon_annot.get(), kPathIndex, ++ nullptr, 0)); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, LineAnnotation) { ++ ASSERT_TRUE(OpenDocument("line_annot.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ EXPECT_EQ(2, FPDFPage_GetAnnotCount(page)); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ ++ // FPDFAnnot_GetVertices() positive testing. ++ FS_POINTF start; ++ FS_POINTF end; ++ ASSERT_TRUE(FPDFAnnot_GetLine(annot.get(), &start, &end)); ++ EXPECT_FLOAT_EQ(159.0f, start.x); ++ EXPECT_FLOAT_EQ(296.0f, start.y); ++ EXPECT_FLOAT_EQ(472.0f, end.x); ++ EXPECT_FLOAT_EQ(243.42f, end.y); ++ ++ // FPDFAnnot_GetVertices() negative testing. ++ EXPECT_FALSE(FPDFAnnot_GetLine(nullptr, nullptr, nullptr)); ++ EXPECT_FALSE(FPDFAnnot_GetLine(annot.get(), nullptr, nullptr)); ++ } ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1)); ++ ASSERT_TRUE(annot); ++ ++ // Too few elements in the line array. ++ FS_POINTF start; ++ FS_POINTF end; ++ EXPECT_FALSE(FPDFAnnot_GetLine(annot.get(), &start, &end)); ++ } ++ ++ { ++ // Wrong annotation type. ++ ScopedFPDFAnnotation ink_annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_INK)); ++ FS_POINTF start; ++ FS_POINTF end; ++ EXPECT_FALSE(FPDFAnnot_GetLine(ink_annot.get(), &start, &end)); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, AnnotationBorder) { ++ ASSERT_TRUE(OpenDocument("line_annot.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ EXPECT_EQ(2, FPDFPage_GetAnnotCount(page)); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ ++ // FPDFAnnot_GetBorder() positive testing. ++ float horizontal_radius; ++ float vertical_radius; ++ float border_width; ++ ASSERT_TRUE(FPDFAnnot_GetBorder(annot.get(), &horizontal_radius, ++ &vertical_radius, &border_width)); ++ EXPECT_FLOAT_EQ(0.25f, horizontal_radius); ++ EXPECT_FLOAT_EQ(0.5f, vertical_radius); ++ EXPECT_FLOAT_EQ(2.0f, border_width); ++ ++ // FPDFAnnot_GetBorder() negative testing. ++ EXPECT_FALSE(FPDFAnnot_GetBorder(nullptr, nullptr, nullptr, nullptr)); ++ EXPECT_FALSE(FPDFAnnot_GetBorder(annot.get(), nullptr, nullptr, nullptr)); ++ } ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1)); ++ ASSERT_TRUE(annot); ++ ++ // Too few elements in the border array. ++ float horizontal_radius; ++ float vertical_radius; ++ float border_width; ++ EXPECT_FALSE(FPDFAnnot_GetBorder(annot.get(), &horizontal_radius, ++ &vertical_radius, &border_width)); ++ ++ // FPDFAnnot_SetBorder() positive testing. ++ EXPECT_TRUE(FPDFAnnot_SetBorder(annot.get(), /*horizontal_radius=*/2.0f, ++ /*vertical_radius=*/3.5f, ++ /*border_width=*/4.0f)); ++ ++ EXPECT_TRUE(FPDFAnnot_GetBorder(annot.get(), &horizontal_radius, ++ &vertical_radius, &border_width)); ++ EXPECT_FLOAT_EQ(2.0f, horizontal_radius); ++ EXPECT_FLOAT_EQ(3.5f, vertical_radius); ++ EXPECT_FLOAT_EQ(4.0f, border_width); ++ ++ // FPDFAnnot_SetBorder() negative testing. ++ EXPECT_FALSE(FPDFAnnot_SetBorder(nullptr, /*horizontal_radius=*/1.0f, ++ /*vertical_radius=*/2.5f, ++ /*border_width=*/3.0f)); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, AnnotationJavaScript) { ++ ASSERT_TRUE(OpenDocument("annot_javascript.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ EXPECT_EQ(1, FPDFPage_GetAnnotCount(page)); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ ++ // FPDFAnnot_GetFormAdditionalActionJavaScript() positive testing. ++ unsigned long length_bytes = FPDFAnnot_GetFormAdditionalActionJavaScript( ++ form_handle(), annot.get(), FPDF_ANNOT_AACTION_FORMAT, nullptr, 0); ++ ASSERT_EQ(62u, length_bytes); ++ std::vector buf = GetFPDFWideStringBuffer(length_bytes); ++ EXPECT_EQ(62u, FPDFAnnot_GetFormAdditionalActionJavaScript( ++ form_handle(), annot.get(), FPDF_ANNOT_AACTION_FORMAT, ++ buf.data(), length_bytes)); ++ EXPECT_EQ(L"AFDate_FormatEx(\"yyyy-mm-dd\");", ++ GetPlatformWString(buf.data())); ++ ++ // FPDFAnnot_GetFormAdditionalActionJavaScript() negative testing. ++ EXPECT_EQ(0u, FPDFAnnot_GetFormAdditionalActionJavaScript( ++ form_handle(), nullptr, 0, nullptr, 0)); ++ EXPECT_EQ(0u, FPDFAnnot_GetFormAdditionalActionJavaScript( ++ nullptr, annot.get(), 0, nullptr, 0)); ++ EXPECT_EQ(0u, FPDFAnnot_GetFormAdditionalActionJavaScript( ++ form_handle(), annot.get(), 0, nullptr, 0)); ++ EXPECT_EQ(2u, FPDFAnnot_GetFormAdditionalActionJavaScript( ++ form_handle(), annot.get(), FPDF_ANNOT_AACTION_KEY_STROKE, ++ nullptr, 0)); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFAnnotEmbedderTest, FormFieldAlternateName) { ++ ASSERT_TRUE(OpenDocument("click_form.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ EXPECT_EQ(8, FPDFPage_GetAnnotCount(page)); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0)); ++ ASSERT_TRUE(annot); ++ ++ // FPDFAnnot_GetFormFieldAlternateName() positive testing. ++ unsigned long length_bytes = FPDFAnnot_GetFormFieldAlternateName( ++ form_handle(), annot.get(), nullptr, 0); ++ ASSERT_EQ(34u, length_bytes); ++ std::vector buf = GetFPDFWideStringBuffer(length_bytes); ++ EXPECT_EQ(34u, FPDFAnnot_GetFormFieldAlternateName( ++ form_handle(), annot.get(), buf.data(), length_bytes)); ++ EXPECT_EQ(L"readOnlyCheckbox", GetPlatformWString(buf.data())); ++ ++ // FPDFAnnot_GetFormFieldAlternateName() negative testing. ++ EXPECT_EQ(0u, FPDFAnnot_GetFormFieldAlternateName(form_handle(), nullptr, ++ nullptr, 0)); ++ EXPECT_EQ(0u, FPDFAnnot_GetFormFieldAlternateName(nullptr, annot.get(), ++ nullptr, 0)); ++ } ++ ++ UnloadPage(page); ++} ++ ++// Due to https://crbug.com/pdfium/570, the AnnotationBorder test above cannot ++// actually render the line annotations inside line_annot.pdf. For now, use a ++// square annotation in annots.pdf for testing. ++TEST_F(FPDFAnnotEmbedderTest, AnnotationBorderRendering) { ++ ASSERT_TRUE(OpenDocument("annots.pdf")); ++ FPDF_PAGE page = LoadPage(1); ++ ASSERT_TRUE(page); ++ EXPECT_EQ(3, FPDFPage_GetAnnotCount(page)); ++ ++ const char* original_checksum = []() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "522a4a6b6c7eab5bf95ded1f21ea372e"; ++#endif ++ return "12127303aecd80c6288460f7c0d79f3f"; ++ }(); ++ const char* modified_checksum = []() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "6844019e07b83cc01723415f58218d06"; ++#endif ++ return "73d06ff4c665fe85029acef30240dcca"; ++ }(); ++ ++ { ++ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2)); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(FPDF_ANNOT_SQUARE, FPDFAnnot_GetSubtype(annot.get())); ++ ++ { ++ ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); ++ CompareBitmap(bitmap.get(), 612, 792, original_checksum); ++ } ++ ++ EXPECT_TRUE(FPDFAnnot_SetBorder(annot.get(), /*horizontal_radius=*/2.0f, ++ /*vertical_radius=*/3.5f, ++ /*border_width=*/4.0f)); ++ ++ { ++ ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); ++ CompareBitmap(bitmap.get(), 612, 792, modified_checksum); ++ } ++ } ++ ++ // Save the document and close the page. ++ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ UnloadPage(page); ++ ++ ASSERT_TRUE(OpenSavedDocument()); ++ page = LoadSavedPage(1); ++ ASSERT_TRUE(page); ++ VerifySavedRendering(page, 612, 792, modified_checksum); ++ ++ CloseSavedPage(page); ++ CloseSavedDocument(); ++} +diff --git a/fpdfsdk/fpdf_annot_unittest.cpp b/fpdfsdk/fpdf_annot_unittest.cpp +deleted file mode 100644 +index a5f619e0d..000000000 +--- a/fpdfsdk/fpdf_annot_unittest.cpp ++++ /dev/null +@@ -1,137 +0,0 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#include "public/fpdf_annot.h" +- +-#include +- +-#include "constants/annotation_common.h" +-#include "core/fpdfapi/page/cpdf_annotcontext.h" +-#include "core/fpdfapi/page/cpdf_pagemodule.h" +-#include "core/fpdfapi/parser/cpdf_dictionary.h" +-#include "fpdfsdk/cpdfsdk_helpers.h" +-#include "public/cpp/fpdf_scopers.h" +-#include "public/fpdf_edit.h" +-#include "testing/fx_string_testhelpers.h" +-#include "testing/gtest/include/gtest/gtest.h" +- +-namespace { +- +-const wchar_t kStreamData[] = +- L"/GS gs 0.0 0.0 0.0 RG 4 w 211.8 747.6 m 211.8 744.8 " +- L"212.6 743.0 214.2 740.8 " +- L"c 215.4 739.0 216.8 737.1 218.9 736.1 c 220.8 735.1 221.4 733.0 " +- L"223.7 732.4 c 232.6 729.9 242.0 730.8 251.2 730.8 c 257.5 730.8 " +- L"263.0 732.9 269.0 734.4 c S"; +- +-} // namespace +- +-class PDFAnnotTest : public testing::Test { +- protected: +- PDFAnnotTest() = default; +- ~PDFAnnotTest() override = default; +- +- void SetUp() override { CPDF_PageModule::Create(); } +- void TearDown() override { CPDF_PageModule::Destroy(); } +-}; +- +-TEST_F(PDFAnnotTest, SetAP) { +- ScopedFPDFDocument doc(FPDF_CreateNewDocument()); +- ASSERT_TRUE(doc); +- ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100)); +- ASSERT_TRUE(page); +- ScopedFPDFWideString ap_stream = GetFPDFWideString(kStreamData); +- ASSERT_TRUE(ap_stream); +- +- ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK)); +- ASSERT_TRUE(annot); +- +- // Negative case: FPDFAnnot_SetAP() should fail if bounding rect is not yet +- // set on the annotation. +- EXPECT_FALSE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, +- ap_stream.get())); +- +- const FS_RECTF bounding_rect{206.0f, 753.0f, 339.0f, 709.0f}; +- EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &bounding_rect)); +- +- ASSERT_TRUE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, +- /*R=*/255, /*G=*/0, /*B=*/0, /*A=*/255)); +- +- EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, +- ap_stream.get())); +- +- // Verify that appearance stream is created as form XObject +- CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot.get()); +- ASSERT_TRUE(context); +- CPDF_Dictionary* annot_dict = context->GetAnnotDict(); +- ASSERT_TRUE(annot_dict); +- CPDF_Dictionary* ap_dict = annot_dict->GetDictFor(pdfium::annotation::kAP); +- ASSERT_TRUE(ap_dict); +- CPDF_Dictionary* stream_dict = ap_dict->GetDictFor("N"); +- ASSERT_TRUE(stream_dict); +- // Check for non-existence of resources dictionary in case of opaque color +- CPDF_Dictionary* resources_dict = stream_dict->GetDictFor("Resources"); +- ASSERT_FALSE(resources_dict); +- ByteString type = stream_dict->GetStringFor(pdfium::annotation::kType); +- EXPECT_EQ("XObject", type); +- ByteString sub_type = stream_dict->GetStringFor(pdfium::annotation::kSubtype); +- EXPECT_EQ("Form", sub_type); +- +- // Check that the appearance stream is same as we just set. +- const uint32_t kStreamDataSize = +- FX_ArraySize(kStreamData) * sizeof(FPDF_WCHAR); +- unsigned long normal_length_bytes = FPDFAnnot_GetAP( +- annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, nullptr, 0); +- ASSERT_EQ(kStreamDataSize, normal_length_bytes); +- std::vector buf = GetFPDFWideStringBuffer(normal_length_bytes); +- EXPECT_EQ(kStreamDataSize, +- FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, +- buf.data(), normal_length_bytes)); +- EXPECT_EQ(kStreamData, GetPlatformWString(buf.data())); +-} +- +-TEST_F(PDFAnnotTest, SetAPWithOpacity) { +- ScopedFPDFDocument doc(FPDF_CreateNewDocument()); +- ASSERT_TRUE(doc); +- ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100)); +- ASSERT_TRUE(page); +- ScopedFPDFWideString ap_stream = GetFPDFWideString(kStreamData); +- ASSERT_TRUE(ap_stream); +- +- ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK)); +- ASSERT_TRUE(annot); +- +- ASSERT_TRUE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, +- /*R=*/255, /*G=*/0, /*B=*/0, /*A=*/102)); +- +- const FS_RECTF bounding_rect{206.0f, 753.0f, 339.0f, 709.0f}; +- EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &bounding_rect)); +- +- EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, +- ap_stream.get())); +- +- CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot.get()); +- ASSERT_TRUE(context); +- CPDF_Dictionary* annot_dict = context->GetAnnotDict(); +- ASSERT_TRUE(annot_dict); +- CPDF_Dictionary* ap_dict = annot_dict->GetDictFor(pdfium::annotation::kAP); +- ASSERT_TRUE(ap_dict); +- CPDF_Dictionary* stream_dict = ap_dict->GetDictFor("N"); +- ASSERT_TRUE(stream_dict); +- CPDF_Dictionary* resources_dict = stream_dict->GetDictFor("Resources"); +- ASSERT_TRUE(stream_dict); +- CPDF_Dictionary* extGState_dict = resources_dict->GetDictFor("ExtGState"); +- ASSERT_TRUE(extGState_dict); +- CPDF_Dictionary* gs_dict = extGState_dict->GetDictFor("GS"); +- ASSERT_TRUE(gs_dict); +- ByteString type = gs_dict->GetStringFor(pdfium::annotation::kType); +- EXPECT_EQ("ExtGState", type); +- float opacity = gs_dict->GetNumberFor("CA"); +- // Opacity value of 102 is represented as 0.4f (=104/255) in /CA entry. +- EXPECT_FLOAT_EQ(0.4f, opacity); +- ByteString blend_mode = gs_dict->GetStringFor("BM"); +- EXPECT_EQ("Normal", blend_mode); +- bool alpha_source_flag = gs_dict->GetBooleanFor("AIS", true); +- EXPECT_FALSE(alpha_source_flag); +-} +diff --git a/fpdfsdk/fpdf_attachment.cpp b/fpdfsdk/fpdf_attachment.cpp +index 7f55691f9..04aed0243 100644 +--- a/fpdfsdk/fpdf_attachment.cpp ++++ b/fpdfsdk/fpdf_attachment.cpp +@@ -1,9 +1,11 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "public/fpdf_attachment.h" + ++#include ++ + #include + #include + +@@ -21,10 +23,11 @@ + #include "core/fpdfdoc/cpdf_filespec.h" + #include "core/fpdfdoc/cpdf_nametree.h" + #include "core/fxcrt/cfx_datetime.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_memory_wrappers.h" + #include "fpdfsdk/cpdfsdk_helpers.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/numerics/safe_conversions.h" + + namespace { + +@@ -55,7 +58,8 @@ FPDFDoc_GetAttachmentCount(FPDF_DOCUMENT document) { + if (!pDoc) + return 0; + +- return CPDF_NameTree(pDoc, "EmbeddedFiles").GetCount(); ++ auto name_tree = CPDF_NameTree::Create(pDoc, "EmbeddedFiles"); ++ return name_tree ? pdfium::base::checked_cast(name_tree->GetCount()) : 0; + } + + FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV +@@ -64,41 +68,26 @@ FPDFDoc_AddAttachment(FPDF_DOCUMENT document, FPDF_WIDESTRING name) { + if (!pDoc) + return nullptr; + +- CPDF_Dictionary* pRoot = pDoc->GetRoot(); +- if (!pRoot) +- return nullptr; +- + WideString wsName = WideStringFromFPDFWideString(name); + if (wsName.IsEmpty()) + return nullptr; + +- // Retrieve the document's Names dictionary; create it if missing. +- CPDF_Dictionary* pNames = pRoot->GetDictFor("Names"); +- if (!pNames) { +- pNames = pDoc->NewIndirect(); +- pRoot->SetNewFor("Names", pDoc, pNames->GetObjNum()); +- } +- +- // Create the EmbeddedFiles dictionary if missing. +- if (!pNames->GetDictFor("EmbeddedFiles")) { +- CPDF_Dictionary* pFiles = pDoc->NewIndirect(); +- pFiles->SetNewFor("Names"); +- pNames->SetNewFor("EmbeddedFiles", pDoc, +- pFiles->GetObjNum()); +- } ++ auto name_tree = ++ CPDF_NameTree::CreateWithRootNameArray(pDoc, "EmbeddedFiles"); ++ if (!name_tree) ++ return nullptr; + + // Set up the basic entries in the filespec dictionary. +- CPDF_Dictionary* pFile = pDoc->NewIndirect(); ++ auto pFile = pDoc->NewIndirect(); + pFile->SetNewFor("Type", "Filespec"); +- pFile->SetNewFor("UF", wsName); +- pFile->SetNewFor(pdfium::stream::kF, wsName); ++ pFile->SetNewFor("UF", wsName.AsStringView()); ++ pFile->SetNewFor(pdfium::stream::kF, wsName.AsStringView()); + + // Add the new attachment name and filespec into the document's EmbeddedFiles. +- CPDF_NameTree nameTree(pDoc, "EmbeddedFiles"); +- if (!nameTree.AddValueAndName(pFile->MakeReference(pDoc), wsName)) { ++ if (!name_tree->AddValueAndName(pFile->MakeReference(pDoc), wsName)) + return nullptr; +- } + ++ // Unretained reference in public API. NOLINTNEXTLINE + return FPDFAttachmentFromCPDFObject(pFile); + } + +@@ -108,13 +97,15 @@ FPDFDoc_GetAttachment(FPDF_DOCUMENT document, int index) { + if (!pDoc || index < 0) + return nullptr; + +- CPDF_NameTree nameTree(pDoc, "EmbeddedFiles"); +- if (static_cast(index) >= nameTree.GetCount()) ++ auto name_tree = CPDF_NameTree::Create(pDoc, "EmbeddedFiles"); ++ if (!name_tree || static_cast(index) >= name_tree->GetCount()) + return nullptr; + + WideString csName; ++ ++ // Unretained reference in public API. NOLINTNEXTLINE + return FPDFAttachmentFromCPDFObject( +- nameTree.LookupValueAndName(index, &csName)); ++ name_tree->LookupValueAndName(index, &csName)); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +@@ -123,11 +114,11 @@ FPDFDoc_DeleteAttachment(FPDF_DOCUMENT document, int index) { + if (!pDoc || index < 0) + return false; + +- CPDF_NameTree nameTree(pDoc, "EmbeddedFiles"); +- if (static_cast(index) >= nameTree.GetCount()) ++ auto name_tree = CPDF_NameTree::Create(pDoc, "EmbeddedFiles"); ++ if (!name_tree || static_cast(index) >= name_tree->GetCount()) + return false; + +- return nameTree.DeleteValueAndName(index); ++ return name_tree->DeleteValueAndName(index); + } + + FPDF_EXPORT unsigned long FPDF_CALLCONV +@@ -138,8 +129,9 @@ FPDFAttachment_GetName(FPDF_ATTACHMENT attachment, + if (!pFile) + return 0; + +- return Utf16EncodeMaybeCopyAndReturnLength(CPDF_FileSpec(pFile).GetFileName(), +- buffer, buflen); ++ CPDF_FileSpec spec(pdfium::WrapRetain(pFile)); ++ return Utf16EncodeMaybeCopyAndReturnLength(spec.GetFileName(), buffer, ++ buflen); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +@@ -148,7 +140,8 @@ FPDFAttachment_HasKey(FPDF_ATTACHMENT attachment, FPDF_BYTESTRING key) { + if (!pFile) + return 0; + +- CPDF_Dictionary* pParamsDict = CPDF_FileSpec(pFile).GetParamsDict(); ++ CPDF_FileSpec spec(pdfium::WrapRetain(pFile)); ++ RetainPtr pParamsDict = spec.GetParamsDict(); + return pParamsDict ? pParamsDict->KeyExist(key) : 0; + } + +@@ -157,8 +150,9 @@ FPDFAttachment_GetValueType(FPDF_ATTACHMENT attachment, FPDF_BYTESTRING key) { + if (!FPDFAttachment_HasKey(attachment, key)) + return FPDF_OBJECT_UNKNOWN; + +- CPDF_FileSpec spec(CPDFObjectFromFPDFAttachment(attachment)); +- CPDF_Object* pObj = spec.GetParamsDict()->GetObjectFor(key); ++ CPDF_FileSpec spec( ++ pdfium::WrapRetain(CPDFObjectFromFPDFAttachment(attachment))); ++ RetainPtr pObj = spec.GetParamsDict()->GetObjectFor(key); + return pObj ? pObj->GetType() : FPDF_OBJECT_UNKNOWN; + } + +@@ -170,7 +164,8 @@ FPDFAttachment_SetStringValue(FPDF_ATTACHMENT attachment, + if (!pFile) + return false; + +- CPDF_Dictionary* pParamsDict = CPDF_FileSpec(pFile).GetParamsDict(); ++ CPDF_FileSpec spec(pdfium::WrapRetain(pFile)); ++ RetainPtr pParamsDict = spec.GetMutableParamsDict(); + if (!pParamsDict) + return false; + +@@ -193,16 +188,19 @@ FPDFAttachment_GetStringValue(FPDF_ATTACHMENT attachment, + if (!pFile) + return 0; + +- CPDF_Dictionary* pParamsDict = CPDF_FileSpec(pFile).GetParamsDict(); ++ CPDF_FileSpec spec(pdfium::WrapRetain(pFile)); ++ RetainPtr pParamsDict = spec.GetParamsDict(); + if (!pParamsDict) + return 0; + + ByteString bsKey = key; + WideString value = pParamsDict->GetUnicodeTextFor(bsKey); + if (bsKey == kChecksumKey && !value.IsEmpty()) { +- CPDF_String* stringValue = pParamsDict->GetObjectFor(bsKey)->AsString(); ++ const CPDF_String* stringValue = ++ pParamsDict->GetObjectFor(bsKey)->AsString(); + if (stringValue->IsHex()) { +- ByteString encoded = PDF_EncodeString(stringValue->GetString(), true); ++ ByteString encoded = ++ PDF_HexEncodeString(stringValue->GetString().AsStringView()); + value = pdfium::MakeRetain(nullptr, encoded, false) + ->GetUnicodeText(); + } +@@ -215,7 +213,7 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFAttachment_SetFile(FPDF_ATTACHMENT attachment, + FPDF_DOCUMENT document, + const void* contents, +- const unsigned long len) { ++ unsigned long len) { + CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment); + CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document); + if (!pFile || !pFile->IsDictionary() || !pDoc || len > INT_MAX) +@@ -227,8 +225,7 @@ FPDFAttachment_SetFile(FPDF_ATTACHMENT attachment, + + // Create a dictionary for the new embedded file stream. + auto pFileStreamDict = pdfium::MakeRetain(); +- CPDF_Dictionary* pParamsDict = +- pFileStreamDict->SetNewFor("Params"); ++ auto pParamsDict = pFileStreamDict->SetNewFor("Params"); + + // Set the size of the new file in the dictionary. + pFileStreamDict->SetNewFor(pdfium::stream::kDL, +@@ -251,27 +248,34 @@ FPDFAttachment_SetFile(FPDF_ATTACHMENT attachment, + true); + + // Create the file stream and have the filespec dictionary link to it. +- std::unique_ptr stream(FX_Alloc(uint8_t, len)); +- memcpy(stream.get(), contents, len); +- CPDF_Stream* pFileStream = pDoc->NewIndirect( +- std::move(stream), len, std::move(pFileStreamDict)); +- CPDF_Dictionary* pEFDict = +- pFile->AsDictionary()->SetNewFor("EF"); ++ const uint8_t* contents_as_bytes = static_cast(contents); ++ auto pFileStream = pDoc->NewIndirect( ++ DataVector(contents_as_bytes, contents_as_bytes + len), ++ std::move(pFileStreamDict)); ++ auto pEFDict = pFile->AsMutableDictionary()->SetNewFor("EF"); + pEFDict->SetNewFor("F", pDoc, pFileStream->GetObjNum()); + return true; + } + +-FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFAttachment_GetFile(FPDF_ATTACHMENT attachment, + void* buffer, +- unsigned long buflen) { ++ unsigned long buflen, ++ unsigned long* out_buflen) { ++ if (!out_buflen) ++ return false; ++ + CPDF_Object* pFile = CPDFObjectFromFPDFAttachment(attachment); + if (!pFile) +- return 0; ++ return false; + +- CPDF_Stream* pFileStream = CPDF_FileSpec(pFile).GetFileStream(); ++ CPDF_FileSpec spec(pdfium::WrapRetain(pFile)); ++ RetainPtr pFileStream = spec.GetFileStream(); + if (!pFileStream) +- return 0; ++ return false; + +- return DecodeStreamMaybeCopyAndReturnLength(pFileStream, buffer, buflen); ++ *out_buflen = DecodeStreamMaybeCopyAndReturnLength( ++ std::move(pFileStream), ++ {static_cast(buffer), static_cast(buflen)}); ++ return true; + } +diff --git a/fpdfsdk/fpdf_attachment_embeddertest.cpp b/fpdfsdk/fpdf_attachment_embeddertest.cpp +index 7ce6003f9..6c156d621 100644 +--- a/fpdfsdk/fpdf_attachment_embeddertest.cpp ++++ b/fpdfsdk/fpdf_attachment_embeddertest.cpp +@@ -1,8 +1,7 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include + #include + #include + +@@ -10,6 +9,7 @@ + #include "public/fpdfview.h" + #include "testing/embedder_test.h" + #include "testing/fx_string_testhelpers.h" ++#include "testing/gmock/include/gmock/gmock.h" + #include "testing/utils/hash.h" + + static constexpr char kDateKey[] = "CreationDate"; +@@ -22,6 +22,10 @@ TEST_F(FPDFAttachmentEmbedderTest, ExtractAttachments) { + ASSERT_TRUE(OpenDocument("embedded_attachments.pdf")); + EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document())); + ++ // Try to retrieve attachments at bad indices. ++ EXPECT_FALSE(FPDFDoc_GetAttachment(document(), -1)); ++ EXPECT_FALSE(FPDFDoc_GetAttachment(document(), 2)); ++ + // Retrieve the first attachment. + FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(document(), 0); + ASSERT_TRUE(attachment); +@@ -33,12 +37,17 @@ TEST_F(FPDFAttachmentEmbedderTest, ExtractAttachments) { + EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes)); + EXPECT_EQ(L"1.txt", GetPlatformWString(buf.data())); + ++ // Check some unsuccessful cases of FPDFAttachment_GetFile. ++ EXPECT_FALSE(FPDFAttachment_GetFile(attachment, nullptr, 0, nullptr)); ++ EXPECT_FALSE(FPDFAttachment_GetFile(nullptr, nullptr, 0, &length_bytes)); ++ + // Check that the content of the first attachment is correct. +- length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0); +- std::vector content_buf(length_bytes); +- ASSERT_EQ( +- 4u, FPDFAttachment_GetFile(attachment, content_buf.data(), length_bytes)); +- EXPECT_EQ(std::string("test"), std::string(content_buf.data(), 4)); ++ ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes)); ++ std::vector content_buf(length_bytes); ++ unsigned long actual_length_bytes; ++ ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(), ++ length_bytes, &actual_length_bytes)); ++ ASSERT_THAT(content_buf, testing::ElementsAre('t', 'e', 's', 't')); + + // Check that a non-existent key does not exist. + EXPECT_FALSE(FPDFAttachment_HasKey(attachment, "none")); +@@ -64,17 +73,17 @@ TEST_F(FPDFAttachmentEmbedderTest, ExtractAttachments) { + ASSERT_TRUE(attachment); + + // Retrieve the second attachment file. +- length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0); ++ ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes)); + content_buf.clear(); + content_buf.resize(length_bytes); +- ASSERT_EQ(5869u, FPDFAttachment_GetFile(attachment, content_buf.data(), +- length_bytes)); ++ ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(), ++ length_bytes, &actual_length_bytes)); ++ ASSERT_EQ(5869u, actual_length_bytes); + + // Check that the calculated checksum of the file data matches expectation. + const char kCheckSum[] = "72afcddedf554dda63c0c88e06f1ce18"; + const wchar_t kCheckSumW[] = L"<72AFCDDEDF554DDA63C0C88E06F1CE18>"; +- const std::string generated_checksum = GenerateMD5Base16( +- reinterpret_cast(content_buf.data()), length_bytes); ++ const std::string generated_checksum = GenerateMD5Base16(content_buf); + EXPECT_EQ(kCheckSum, generated_checksum); + + // Check that the stored checksum matches expectation. +@@ -87,6 +96,40 @@ TEST_F(FPDFAttachmentEmbedderTest, ExtractAttachments) { + EXPECT_EQ(kCheckSumW, GetPlatformWString(buf.data())); + } + ++TEST_F(FPDFAttachmentEmbedderTest, NoAttachmentToExtract) { ++ // Open a file with no attachments. ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ EXPECT_EQ(0, FPDFDoc_GetAttachmentCount(document())); ++ ++ // Try to retrieve attachments at bad indices. ++ EXPECT_FALSE(FPDFDoc_GetAttachment(document(), -1)); ++ EXPECT_FALSE(FPDFDoc_GetAttachment(document(), 0)); ++} ++ ++TEST_F(FPDFAttachmentEmbedderTest, InvalidAttachmentData) { ++ // Open a file with an attachment that is missing the embedded file (/EF). ++ ASSERT_TRUE(OpenDocument("embedded_attachments_invalid_data.pdf")); ++ ASSERT_EQ(1, FPDFDoc_GetAttachmentCount(document())); ++ ++ // Retrieve the first attachment. ++ FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(document(), 0); ++ ASSERT_TRUE(attachment); ++ ++ // Check that the name of the attachment is correct. ++ unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0); ++ ASSERT_EQ(12u, length_bytes); ++ std::vector buf = GetFPDFWideStringBuffer(length_bytes); ++ EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes)); ++ EXPECT_EQ("1.txt", GetPlatformString(buf.data())); ++ ++ // Check that is is not possible to retrieve the file data. ++ EXPECT_FALSE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes)); ++ ++ // Check that the attachment can be deleted. ++ EXPECT_TRUE(FPDFDoc_DeleteAttachment(document(), 0)); ++ EXPECT_EQ(0, FPDFDoc_GetAttachmentCount(document())); ++} ++ + TEST_F(FPDFAttachmentEmbedderTest, AddAttachments) { + // Open a file with two attachments. + ASSERT_TRUE(OpenDocument("embedded_attachments.pdf")); +@@ -99,6 +142,7 @@ TEST_F(FPDFAttachmentEmbedderTest, AddAttachments) { + ScopedFPDFWideString file_name = GetFPDFWideString(L"0.txt"); + FPDF_ATTACHMENT attachment = + FPDFDoc_AddAttachment(document(), file_name.get()); ++ ASSERT_TRUE(attachment); + + // Check that writing to a file with nullptr but non-zero bytes would fail. + EXPECT_FALSE(FPDFAttachment_SetFile(attachment, document(), nullptr, 10)); +@@ -107,6 +151,7 @@ TEST_F(FPDFAttachmentEmbedderTest, AddAttachments) { + constexpr char kContents1[] = "Hello!"; + EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents1, + strlen(kContents1))); ++ EXPECT_EQ(3, FPDFDoc_GetAttachmentCount(document())); + + // Verify the name of the new attachment (i.e. the first attachment). + attachment = FPDFDoc_GetAttachment(document(), 0); +@@ -118,15 +163,18 @@ TEST_F(FPDFAttachmentEmbedderTest, AddAttachments) { + EXPECT_EQ(L"0.txt", GetPlatformWString(buf.data())); + + // Verify the content of the new attachment (i.e. the first attachment). +- length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0); ++ ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes)); + std::vector content_buf(length_bytes); +- ASSERT_EQ( +- 6u, FPDFAttachment_GetFile(attachment, content_buf.data(), length_bytes)); ++ unsigned long actual_length_bytes; ++ ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(), ++ length_bytes, &actual_length_bytes)); ++ ASSERT_EQ(6u, actual_length_bytes); + EXPECT_EQ(std::string(kContents1), std::string(content_buf.data(), 6)); + + // Add an attachment to the end of the embedded file list and set its file. + file_name = GetFPDFWideString(L"z.txt"); + attachment = FPDFDoc_AddAttachment(document(), file_name.get()); ++ ASSERT_TRUE(attachment); + constexpr char kContents2[] = "World!"; + EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents2, + strlen(kContents2))); +@@ -142,11 +190,12 @@ TEST_F(FPDFAttachmentEmbedderTest, AddAttachments) { + EXPECT_EQ(L"z.txt", GetPlatformWString(buf.data())); + + // Verify the content of the new attachment (i.e. the fourth attachment). +- length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0); ++ ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes)); + content_buf.clear(); + content_buf.resize(length_bytes); +- ASSERT_EQ( +- 6u, FPDFAttachment_GetFile(attachment, content_buf.data(), length_bytes)); ++ ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(), ++ length_bytes, &actual_length_bytes)); ++ ASSERT_EQ(6u, actual_length_bytes); + EXPECT_EQ(std::string(kContents2), std::string(content_buf.data(), 6)); + } + +@@ -159,6 +208,7 @@ TEST_F(FPDFAttachmentEmbedderTest, AddAttachmentsWithParams) { + ScopedFPDFWideString file_name = GetFPDFWideString(L"5.txt"); + FPDF_ATTACHMENT attachment = + FPDFDoc_AddAttachment(document(), file_name.get()); ++ ASSERT_TRUE(attachment); + constexpr char kContents[] = "Hello World!"; + EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents, + strlen(kContents))); +@@ -185,10 +235,12 @@ TEST_F(FPDFAttachmentEmbedderTest, AddAttachmentsWithParams) { + EXPECT_EQ(L"5.txt", GetPlatformWString(buf.data())); + + // Verify the content of the new attachment. +- length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0); ++ ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes)); + std::vector content_buf(length_bytes); +- ASSERT_EQ(12u, FPDFAttachment_GetFile(attachment, content_buf.data(), +- length_bytes)); ++ unsigned long actual_length_bytes; ++ ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(), ++ length_bytes, &actual_length_bytes)); ++ ASSERT_EQ(12u, actual_length_bytes); + EXPECT_EQ(std::string(kContents), std::string(content_buf.data(), 12)); + + // Verify the creation date of the new attachment. +@@ -212,7 +264,8 @@ TEST_F(FPDFAttachmentEmbedderTest, AddAttachmentsWithParams) { + // Overwrite the existing file with empty content, and check that the checksum + // gets updated to the correct value. + EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), nullptr, 0)); +- EXPECT_EQ(0u, FPDFAttachment_GetFile(attachment, nullptr, 0)); ++ ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes)); ++ EXPECT_EQ(0u, length_bytes); + length_bytes = + FPDFAttachment_GetStringValue(attachment, kChecksumKey, nullptr, 0); + ASSERT_EQ(70u, length_bytes); +@@ -223,6 +276,69 @@ TEST_F(FPDFAttachmentEmbedderTest, AddAttachmentsWithParams) { + GetPlatformWString(buf.data())); + } + ++TEST_F(FPDFAttachmentEmbedderTest, AddAttachmentsToFileWithNoAttachments) { ++ // Open a file with no attachments. ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ EXPECT_EQ(0, FPDFDoc_GetAttachmentCount(document())); ++ ++ // Add an attachment to the beginning of the embedded file list. ++ ScopedFPDFWideString file_name = GetFPDFWideString(L"0.txt"); ++ FPDF_ATTACHMENT attachment = ++ FPDFDoc_AddAttachment(document(), file_name.get()); ++ ASSERT_TRUE(attachment); ++ ++ // Set the new attachment's file. ++ constexpr char kContents1[] = "Hello!"; ++ EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents1, ++ strlen(kContents1))); ++ EXPECT_EQ(1, FPDFDoc_GetAttachmentCount(document())); ++ ++ // Verify the name of the new attachment (i.e. the first attachment). ++ attachment = FPDFDoc_GetAttachment(document(), 0); ++ ASSERT_TRUE(attachment); ++ unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0); ++ ASSERT_EQ(12u, length_bytes); ++ std::vector buf = GetFPDFWideStringBuffer(length_bytes); ++ EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes)); ++ EXPECT_EQ(L"0.txt", GetPlatformWString(buf.data())); ++ ++ // Verify the content of the new attachment (i.e. the first attachment). ++ ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes)); ++ std::vector content_buf(length_bytes); ++ unsigned long actual_length_bytes; ++ ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(), ++ length_bytes, &actual_length_bytes)); ++ ASSERT_EQ(6u, actual_length_bytes); ++ EXPECT_EQ(std::string(kContents1), std::string(content_buf.data(), 6)); ++ ++ // Add an attachment to the end of the embedded file list and set its file. ++ file_name = GetFPDFWideString(L"z.txt"); ++ attachment = FPDFDoc_AddAttachment(document(), file_name.get()); ++ ASSERT_TRUE(attachment); ++ constexpr char kContents2[] = "World!"; ++ EXPECT_TRUE(FPDFAttachment_SetFile(attachment, document(), kContents2, ++ strlen(kContents2))); ++ EXPECT_EQ(2, FPDFDoc_GetAttachmentCount(document())); ++ ++ // Verify the name of the new attachment (i.e. the second attachment). ++ attachment = FPDFDoc_GetAttachment(document(), 1); ++ ASSERT_TRUE(attachment); ++ length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0); ++ ASSERT_EQ(12u, length_bytes); ++ buf = GetFPDFWideStringBuffer(length_bytes); ++ EXPECT_EQ(12u, FPDFAttachment_GetName(attachment, buf.data(), length_bytes)); ++ EXPECT_EQ(L"z.txt", GetPlatformWString(buf.data())); ++ ++ // Verify the content of the new attachment (i.e. the second attachment). ++ ASSERT_TRUE(FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes)); ++ content_buf.clear(); ++ content_buf.resize(length_bytes); ++ ASSERT_TRUE(FPDFAttachment_GetFile(attachment, content_buf.data(), ++ length_bytes, &actual_length_bytes)); ++ ASSERT_EQ(6u, actual_length_bytes); ++ EXPECT_EQ(std::string(kContents2), std::string(content_buf.data(), 6)); ++} ++ + TEST_F(FPDFAttachmentEmbedderTest, DeleteAttachment) { + // Open a file with two attachments. + ASSERT_TRUE(OpenDocument("embedded_attachments.pdf")); +@@ -230,6 +346,7 @@ TEST_F(FPDFAttachmentEmbedderTest, DeleteAttachment) { + + // Verify the name of the first attachment. + FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(document(), 0); ++ ASSERT_TRUE(attachment); + unsigned long length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0); + ASSERT_EQ(12u, length_bytes); + std::vector buf = GetFPDFWideStringBuffer(length_bytes); +@@ -242,6 +359,7 @@ TEST_F(FPDFAttachmentEmbedderTest, DeleteAttachment) { + + // Verify the name of the new first attachment. + attachment = FPDFDoc_GetAttachment(document(), 0); ++ ASSERT_TRUE(attachment); + length_bytes = FPDFAttachment_GetName(attachment, nullptr, 0); + ASSERT_EQ(26u, length_bytes); + buf = GetFPDFWideStringBuffer(length_bytes); +diff --git a/fpdfsdk/fpdf_catalog.cpp b/fpdfsdk/fpdf_catalog.cpp +index 97bddde05..6147de86d 100644 +--- a/fpdfsdk/fpdf_catalog.cpp ++++ b/fpdfsdk/fpdf_catalog.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -18,6 +18,6 @@ FPDFCatalog_IsTagged(FPDF_DOCUMENT document) { + if (!pCatalog) + return false; + +- const CPDF_Dictionary* pMarkInfo = pCatalog->GetDictFor("MarkInfo"); ++ RetainPtr pMarkInfo = pCatalog->GetDictFor("MarkInfo"); + return pMarkInfo && pMarkInfo->GetIntegerFor("Marked") != 0; + } +diff --git a/fpdfsdk/fpdf_catalog_unittest.cpp b/fpdfsdk/fpdf_catalog_unittest.cpp +index 68655da31..7a45cb047 100644 +--- a/fpdfsdk/fpdf_catalog_unittest.cpp ++++ b/fpdfsdk/fpdf_catalog_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,40 +6,29 @@ + + #include + +-#include "core/fpdfapi/page/cpdf_docpagedata.h" +-#include "core/fpdfapi/page/cpdf_pagemodule.h" ++#include "core/fpdfapi/page/test_with_page_module.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fpdfapi/parser/cpdf_parser.h" + #include "core/fpdfapi/parser/cpdf_string.h" +-#include "core/fpdfapi/render/cpdf_docrenderdata.h" ++#include "core/fpdfapi/parser/cpdf_test_document.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "public/cpp/fpdf_scopers.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + +-class CPDF_TestDocument final : public CPDF_Document { +- public: +- CPDF_TestDocument() +- : CPDF_Document(pdfium::MakeUnique(), +- pdfium::MakeUnique()) {} +- +- void SetRoot(CPDF_Dictionary* root) { m_pRootDict.Reset(root); } +-}; +- +-class PDFCatalogTest : public testing::Test { ++class PDFCatalogTest : public TestWithPageModule { + public: + void SetUp() override { +- CPDF_PageModule::Create(); +- auto pTestDoc = pdfium::MakeUnique(); ++ TestWithPageModule::SetUp(); ++ auto pTestDoc = std::make_unique(); + m_pDoc.reset(FPDFDocumentFromCPDFDocument(pTestDoc.release())); + m_pRootObj = pdfium::MakeRetain(); + } + + void TearDown() override { + m_pDoc.reset(); +- CPDF_PageModule::Destroy(); ++ TestWithPageModule::TearDown(); + } + + protected: +@@ -59,7 +48,7 @@ TEST_F(PDFCatalogTest, IsTagged) { + EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get())); + + // Empty root +- pTestDoc->SetRoot(m_pRootObj.Get()); ++ pTestDoc->SetRoot(m_pRootObj); + EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get())); + + // Root with other key +@@ -67,8 +56,7 @@ TEST_F(PDFCatalogTest, IsTagged) { + EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get())); + + // Root with empty MarkInfo +- CPDF_Dictionary* markInfoDict = +- m_pRootObj->SetNewFor("MarkInfo"); ++ auto markInfoDict = m_pRootObj->SetNewFor("MarkInfo"); + EXPECT_FALSE(FPDFCatalog_IsTagged(m_pDoc.get())); + + // MarkInfo present but Marked is 0 +diff --git a/fpdfsdk/fpdf_dataavail.cpp b/fpdfsdk/fpdf_dataavail.cpp +index 11abff337..e4f85ebf1 100644 +--- a/fpdfsdk/fpdf_dataavail.cpp ++++ b/fpdfsdk/fpdf_dataavail.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,37 +16,39 @@ + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxcrt/fx_stream.h" + #include "core/fxcrt/retain_ptr.h" ++#include "core/fxcrt/unowned_ptr.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "public/fpdf_formfill.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/numerics/safe_conversions.h" + + #ifdef PDF_ENABLE_XFA + #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h" + #endif // PDF_ENABLE_XFA + + // These checks are here because core/ and public/ cannot depend on each other. +-static_assert(CPDF_DataAvail::DataError == PDF_DATA_ERROR, +- "CPDF_DataAvail::DataError value mismatch"); +-static_assert(CPDF_DataAvail::DataNotAvailable == PDF_DATA_NOTAVAIL, +- "CPDF_DataAvail::DataNotAvailable value mismatch"); +-static_assert(CPDF_DataAvail::DataAvailable == PDF_DATA_AVAIL, +- "CPDF_DataAvail::DataAvailable value mismatch"); +- +-static_assert(CPDF_DataAvail::LinearizationUnknown == PDF_LINEARIZATION_UNKNOWN, +- "CPDF_DataAvail::LinearizationUnknown value mismatch"); +-static_assert(CPDF_DataAvail::NotLinearized == PDF_NOT_LINEARIZED, +- "CPDF_DataAvail::NotLinearized value mismatch"); +-static_assert(CPDF_DataAvail::Linearized == PDF_LINEARIZED, +- "CPDF_DataAvail::Linearized value mismatch"); +- +-static_assert(CPDF_DataAvail::FormError == PDF_FORM_ERROR, +- "CPDF_DataAvail::FormError value mismatch"); +-static_assert(CPDF_DataAvail::FormNotAvailable == PDF_FORM_NOTAVAIL, +- "CPDF_DataAvail::FormNotAvailable value mismatch"); +-static_assert(CPDF_DataAvail::FormAvailable == PDF_FORM_AVAIL, +- "CPDF_DataAvail::FormAvailable value mismatch"); +-static_assert(CPDF_DataAvail::FormNotExist == PDF_FORM_NOTEXIST, +- "CPDF_DataAvail::FormNotExist value mismatch"); ++static_assert(CPDF_DataAvail::kDataError == PDF_DATA_ERROR, ++ "CPDF_DataAvail::kDataError value mismatch"); ++static_assert(CPDF_DataAvail::kDataNotAvailable == PDF_DATA_NOTAVAIL, ++ "CPDF_DataAvail::kDataNotAvailable value mismatch"); ++static_assert(CPDF_DataAvail::kDataAvailable == PDF_DATA_AVAIL, ++ "CPDF_DataAvail::kDataAvailable value mismatch"); ++ ++static_assert(CPDF_DataAvail::kLinearizationUnknown == ++ PDF_LINEARIZATION_UNKNOWN, ++ "CPDF_DataAvail::kLinearizationUnknown value mismatch"); ++static_assert(CPDF_DataAvail::kNotLinearized == PDF_NOT_LINEARIZED, ++ "CPDF_DataAvail::kNotLinearized value mismatch"); ++static_assert(CPDF_DataAvail::kLinearized == PDF_LINEARIZED, ++ "CPDF_DataAvail::kLinearized value mismatch"); ++ ++static_assert(CPDF_DataAvail::kFormError == PDF_FORM_ERROR, ++ "CPDF_DataAvail::kFormError value mismatch"); ++static_assert(CPDF_DataAvail::kFormNotAvailable == PDF_FORM_NOTAVAIL, ++ "CPDF_DataAvail::kFormNotAvailable value mismatch"); ++static_assert(CPDF_DataAvail::kFormAvailable == PDF_FORM_AVAIL, ++ "CPDF_DataAvail::kFormAvailable value mismatch"); ++static_assert(CPDF_DataAvail::kFormNotExist == PDF_FORM_NOTEXIST, ++ "CPDF_DataAvail::kFormNotExist value mismatch"); + + namespace { + +@@ -57,7 +59,8 @@ class FPDF_FileAvailContext final : public CPDF_DataAvail::FileAvail { + + // CPDF_DataAvail::FileAvail: + bool IsDataAvail(FX_FILESIZE offset, size_t size) override { +- return !!avail_->IsDataAvail(avail_, offset, size); ++ return !!avail_->IsDataAvail( ++ avail_, pdfium::base::checked_cast(offset), size); + } + + private: +@@ -66,26 +69,26 @@ class FPDF_FileAvailContext final : public CPDF_DataAvail::FileAvail { + + class FPDF_FileAccessContext final : public IFX_SeekableReadStream { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // IFX_SeekableReadStream: + FX_FILESIZE GetSize() override { return file_->m_FileLen; } + +- bool ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) override { +- if (!buffer || offset < 0 || !size) ++ bool ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) override { ++ if (buffer.empty() || offset < 0) + return false; + +- if (!pdfium::base::IsValueInRangeForNumericType(size)) ++ if (!pdfium::base::IsValueInRangeForNumericType(buffer.size())) + return false; + +- FX_SAFE_FILESIZE new_pos = size; ++ FX_SAFE_FILESIZE new_pos = buffer.size(); + new_pos += offset; + return new_pos.IsValid() && new_pos.ValueOrDie() <= GetSize() && +- file_->m_GetBlock(file_->m_Param, offset, +- static_cast(buffer), size); ++ file_->m_GetBlock( ++ file_->m_Param, ++ pdfium::base::checked_cast(offset), buffer.data(), ++ pdfium::base::checked_cast(buffer.size())); + } + + private: +@@ -97,30 +100,29 @@ class FPDF_FileAccessContext final : public IFX_SeekableReadStream { + + class FPDF_DownloadHintsContext final : public CPDF_DataAvail::DownloadHints { + public: +- explicit FPDF_DownloadHintsContext(FX_DOWNLOADHINTS* pDownloadHints) { +- m_pDownloadHints = pDownloadHints; +- } +- ~FPDF_DownloadHintsContext() override {} ++ explicit FPDF_DownloadHintsContext(FX_DOWNLOADHINTS* pDownloadHints) ++ : m_pDownloadHints(pDownloadHints) {} ++ ~FPDF_DownloadHintsContext() override = default; + +- public: + // IFX_DownloadHints + void AddSegment(FX_FILESIZE offset, size_t size) override { +- if (m_pDownloadHints) +- m_pDownloadHints->AddSegment(m_pDownloadHints, offset, size); ++ if (m_pDownloadHints) { ++ m_pDownloadHints->AddSegment(m_pDownloadHints, ++ static_cast(offset), size); ++ } + } + + private: +- FX_DOWNLOADHINTS* m_pDownloadHints; ++ UnownedPtr m_pDownloadHints; + }; + + class FPDF_AvailContext { + public: + FPDF_AvailContext(FX_FILEAVAIL* file_avail, FPDF_FILEACCESS* file) +- : file_avail_(pdfium::MakeUnique(file_avail)), ++ : file_avail_(std::make_unique(file_avail)), + file_read_(pdfium::MakeRetain(file)), +- data_avail_(pdfium::MakeUnique(file_avail_.get(), +- file_read_, +- true)) {} ++ data_avail_( ++ std::make_unique(file_avail_.get(), file_read_)) {} + ~FPDF_AvailContext() = default; + + CPDF_DataAvail* data_avail() { return data_avail_.get(); } +@@ -132,15 +134,21 @@ class FPDF_AvailContext { + }; + + FPDF_AvailContext* FPDFAvailContextFromFPDFAvail(FPDF_AVAIL avail) { +- return static_cast(avail); ++ return reinterpret_cast(avail); ++} ++ ++FPDF_AVAIL FPDFAvailFromFPDFAvailContext(FPDF_AvailContext* pAvailContext) { ++ return reinterpret_cast(pAvailContext); + } + + } // namespace + + FPDF_EXPORT FPDF_AVAIL FPDF_CALLCONV FPDFAvail_Create(FX_FILEAVAIL* file_avail, + FPDF_FILEACCESS* file) { +- auto pAvail = pdfium::MakeUnique(file_avail, file); +- return pAvail.release(); // Caller takes ownership. ++ auto pAvail = std::make_unique(file_avail, file); ++ ++ // Caller takes ownership. ++ return FPDFAvailFromFPDFAvailContext(pAvail.release()); + } + + FPDF_EXPORT void FPDF_CALLCONV FPDFAvail_Destroy(FPDF_AVAIL avail) { +@@ -165,17 +173,13 @@ FPDFAvail_GetDocument(FPDF_AVAIL avail, FPDF_BYTESTRING password) { + CPDF_Parser::Error error; + std::unique_ptr document; + std::tie(error, document) = avail_context->data_avail()->ParseDocument( +- pdfium::MakeUnique(), +- pdfium::MakeUnique(), password); ++ std::make_unique(), ++ std::make_unique(), password); + if (error != CPDF_Parser::SUCCESS) { + ProcessParseError(error); + return nullptr; + } + +-#ifdef PDF_ENABLE_XFA +- document->SetExtension(pdfium::MakeUnique(document.get())); +-#endif // PDF_ENABLE_XFA +- + ReportUnsupportedFeatures(document.get()); + return FPDFDocumentFromCPDFDocument(document.release()); + } +diff --git a/fpdfsdk/fpdf_dataavail_embeddertest.cpp b/fpdfsdk/fpdf_dataavail_embeddertest.cpp +index 02b481c6e..e03f2aca5 100644 +--- a/fpdfsdk/fpdf_dataavail_embeddertest.cpp ++++ b/fpdfsdk/fpdf_dataavail_embeddertest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,9 +9,10 @@ + #include + + #include "core/fxcrt/bytestring.h" +-#include "core/fxcrt/widestring.h" ++#include "public/fpdf_doc.h" + #include "public/fpdfview.h" + #include "testing/embedder_test.h" ++#include "testing/fx_string_testhelpers.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/range_set.h" + #include "testing/utils/file_util.h" +@@ -163,23 +164,23 @@ TEST_F(FPDFDataAvailEmbedderTest, TrailerUnterminated) { + // Document must load without crashing but is too malformed to be available. + EXPECT_FALSE(OpenDocument("trailer_unterminated.pdf")); + MockDownloadHints hints; +- EXPECT_FALSE(FPDFAvail_IsDocAvail(avail_, &hints)); ++ EXPECT_FALSE(FPDFAvail_IsDocAvail(avail(), &hints)); + } + + TEST_F(FPDFDataAvailEmbedderTest, TrailerAsHexstring) { + // Document must load without crashing but is too malformed to be available. + EXPECT_FALSE(OpenDocument("trailer_as_hexstring.pdf")); + MockDownloadHints hints; +- EXPECT_FALSE(FPDFAvail_IsDocAvail(avail_, &hints)); ++ EXPECT_FALSE(FPDFAvail_IsDocAvail(avail(), &hints)); + } + + TEST_F(FPDFDataAvailEmbedderTest, LoadUsingHintTables) { + TestAsyncLoader loader("feature_linearized_loading.pdf"); +- avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access()); +- ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail_, loader.hints())); +- document_ = FPDFAvail_GetDocument(avail_, nullptr); +- ASSERT_TRUE(document_); +- ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail_, 1, loader.hints())); ++ CreateAvail(loader.file_avail(), loader.file_access()); ++ ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints())); ++ SetDocumentFromAvail(); ++ ASSERT_TRUE(document()); ++ ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail(), 1, loader.hints())); + + // No new data available, to prevent load "Pages" node. + loader.set_is_new_data_available(false); +@@ -189,10 +190,10 @@ TEST_F(FPDFDataAvailEmbedderTest, LoadUsingHintTables) { + + TEST_F(FPDFDataAvailEmbedderTest, CheckFormAvailIfLinearized) { + TestAsyncLoader loader("feature_linearized_loading.pdf"); +- avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access()); +- ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail_, loader.hints())); +- document_ = FPDFAvail_GetDocument(avail_, nullptr); +- ASSERT_TRUE(document_); ++ CreateAvail(loader.file_avail(), loader.file_access()); ++ ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints())); ++ SetDocumentFromAvail(); ++ ASSERT_TRUE(document()); + + // Prevent access to non-requested data to coerce the parser to send new + // request for non available (non-requested before) data. +@@ -202,7 +203,7 @@ TEST_F(FPDFDataAvailEmbedderTest, CheckFormAvailIfLinearized) { + int status = PDF_FORM_NOTAVAIL; + while (status == PDF_FORM_NOTAVAIL) { + loader.FlushRequestedData(); +- status = FPDFAvail_IsFormAvail(avail_, loader.hints()); ++ status = FPDFAvail_IsFormAvail(avail(), loader.hints()); + } + EXPECT_NE(PDF_FORM_ERROR, status); + } +@@ -210,11 +211,11 @@ TEST_F(FPDFDataAvailEmbedderTest, CheckFormAvailIfLinearized) { + TEST_F(FPDFDataAvailEmbedderTest, + DoNotLoadMainCrossRefForFirstPageIfLinearized) { + TestAsyncLoader loader("feature_linearized_loading.pdf"); +- avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access()); +- ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail_, loader.hints())); +- document_ = FPDFAvail_GetDocument(avail_, nullptr); +- ASSERT_TRUE(document_); +- const int first_page_num = FPDFAvail_GetFirstPageNum(document_); ++ CreateAvail(loader.file_avail(), loader.file_access()); ++ ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints())); ++ SetDocumentFromAvail(); ++ ASSERT_TRUE(document()); ++ const int first_page_num = FPDFAvail_GetFirstPageNum(document()); + + // The main cross ref table should not be processed. + // (It is always at file end) +@@ -224,7 +225,7 @@ TEST_F(FPDFDataAvailEmbedderTest, + // Prevent access to non-requested data to coerce the parser to send new + // request for non available (non-requested before) data. + loader.set_is_new_data_available(false); +- FPDFAvail_IsPageAvail(avail_, first_page_num, loader.hints()); ++ FPDFAvail_IsPageAvail(avail(), first_page_num, loader.hints()); + + // The main cross ref table should not be requested. + // (It is always at file end) +@@ -233,7 +234,7 @@ TEST_F(FPDFDataAvailEmbedderTest, + // Allow parse page. + loader.set_is_new_data_available(true); + ASSERT_EQ(PDF_DATA_AVAIL, +- FPDFAvail_IsPageAvail(avail_, first_page_num, loader.hints())); ++ FPDFAvail_IsPageAvail(avail(), first_page_num, loader.hints())); + + // The main cross ref table should not be processed. + // (It is always at file end) +@@ -248,10 +249,10 @@ TEST_F(FPDFDataAvailEmbedderTest, + + TEST_F(FPDFDataAvailEmbedderTest, LoadSecondPageIfLinearizedWithHints) { + TestAsyncLoader loader("feature_linearized_loading.pdf"); +- avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access()); +- ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail_, loader.hints())); +- document_ = FPDFAvail_GetDocument(avail_, nullptr); +- ASSERT_TRUE(document_); ++ CreateAvail(loader.file_avail(), loader.file_access()); ++ ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints())); ++ SetDocumentFromAvail(); ++ ASSERT_TRUE(document()); + + static constexpr uint32_t kSecondPageNum = 1; + +@@ -263,7 +264,7 @@ TEST_F(FPDFDataAvailEmbedderTest, LoadSecondPageIfLinearizedWithHints) { + int status = PDF_DATA_NOTAVAIL; + while (status == PDF_DATA_NOTAVAIL) { + loader.FlushRequestedData(); +- status = FPDFAvail_IsPageAvail(avail_, kSecondPageNum, loader.hints()); ++ status = FPDFAvail_IsPageAvail(avail(), kSecondPageNum, loader.hints()); + } + EXPECT_EQ(PDF_DATA_AVAIL, status); + +@@ -276,23 +277,23 @@ TEST_F(FPDFDataAvailEmbedderTest, LoadSecondPageIfLinearizedWithHints) { + TEST_F(FPDFDataAvailEmbedderTest, LoadInfoAfterReceivingWholeDocument) { + TestAsyncLoader loader("linearized.pdf"); + loader.set_is_new_data_available(false); +- avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access()); +- while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail_, loader.hints())) { ++ CreateAvail(loader.file_avail(), loader.file_access()); ++ while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail(), loader.hints())) { + loader.FlushRequestedData(); + } + +- document_ = FPDFAvail_GetDocument(avail_, nullptr); +- ASSERT_TRUE(document_); ++ SetDocumentFromAvail(); ++ ASSERT_TRUE(document()); + + // The "info" dictionary should still be unavailable. +- EXPECT_FALSE(FPDF_GetMetaText(document_, "CreationDate", nullptr, 0)); ++ EXPECT_FALSE(FPDF_GetMetaText(document(), "CreationDate", nullptr, 0)); + + // Simulate receiving whole file. + loader.set_is_new_data_available(true); + // Load second page, to parse additional crossref sections. +- EXPECT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail_, 1, loader.hints())); ++ EXPECT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail(), 1, loader.hints())); + +- EXPECT_TRUE(FPDF_GetMetaText(document_, "CreationDate", nullptr, 0)); ++ EXPECT_TRUE(FPDF_GetMetaText(document(), "CreationDate", nullptr, 0)); + } + + TEST_F(FPDFDataAvailEmbedderTest, LoadInfoAfterReceivingFirstPage) { +@@ -300,79 +301,77 @@ TEST_F(FPDFDataAvailEmbedderTest, LoadInfoAfterReceivingFirstPage) { + // Map "Info" to an object within the first section without breaking + // linearization. + ByteString data(loader.file_contents(), loader.file_length()); +- Optional index = data.Find("/Info 27 0 R"); ++ absl::optional index = data.Find("/Info 27 0 R"); + ASSERT_TRUE(index); + memcpy(loader.file_contents() + *index, "/Info 29 0 R", 12); + + loader.set_is_new_data_available(false); +- avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access()); +- while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail_, loader.hints())) { ++ CreateAvail(loader.file_avail(), loader.file_access()); ++ while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail(), loader.hints())) { + loader.FlushRequestedData(); + } + +- document_ = FPDFAvail_GetDocument(avail_, nullptr); +- ASSERT_TRUE(document_); ++ SetDocumentFromAvail(); ++ ASSERT_TRUE(document()); + + // The "Info" dictionary should be available for the linearized document, if + // it is located in the first page section. + // Info was remapped to a dictionary with Type "Catalog" + unsigned short buffer[100] = {0}; +- EXPECT_TRUE(FPDF_GetMetaText(document_, "Type", buffer, sizeof(buffer))); +- constexpr wchar_t kExpectedValue[] = L"Catalog"; +- EXPECT_EQ(WideString(kExpectedValue), +- WideString::FromUTF16LE(buffer, FXSYS_len(kExpectedValue))); ++ EXPECT_TRUE(FPDF_GetMetaText(document(), "Type", buffer, sizeof(buffer))); ++ EXPECT_EQ(L"Catalog", GetPlatformWString(buffer)); + } + + TEST_F(FPDFDataAvailEmbedderTest, TryLoadInvalidInfo) { + TestAsyncLoader loader("linearized.pdf"); + // Map "Info" to an invalid object without breaking linearization. + ByteString data(loader.file_contents(), loader.file_length()); +- Optional index = data.Find("/Info 27 0 R"); ++ absl::optional index = data.Find("/Info 27 0 R"); + ASSERT_TRUE(index); + memcpy(loader.file_contents() + *index, "/Info 99 0 R", 12); + + loader.set_is_new_data_available(false); +- avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access()); +- while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail_, loader.hints())) { ++ CreateAvail(loader.file_avail(), loader.file_access()); ++ while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail(), loader.hints())) { + loader.FlushRequestedData(); + } + +- document_ = FPDFAvail_GetDocument(avail_, nullptr); +- ASSERT_TRUE(document_); ++ SetDocumentFromAvail(); ++ ASSERT_TRUE(document()); + + // Set all data available. + loader.set_is_new_data_available(true); + // Check second page, to load additional crossrefs. +- ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail_, 0, loader.hints())); ++ ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail(), 0, loader.hints())); + + // Test that api is robust enough to handle the bad case. +- EXPECT_FALSE(FPDF_GetMetaText(document_, "Type", nullptr, 0)); ++ EXPECT_FALSE(FPDF_GetMetaText(document(), "Type", nullptr, 0)); + } + + TEST_F(FPDFDataAvailEmbedderTest, TryLoadNonExistsInfo) { + TestAsyncLoader loader("linearized.pdf"); + // Break the "Info" parameter without breaking linearization. + ByteString data(loader.file_contents(), loader.file_length()); +- Optional index = data.Find("/Info 27 0 R"); ++ absl::optional index = data.Find("/Info 27 0 R"); + ASSERT_TRUE(index); + memcpy(loader.file_contents() + *index, "/I_fo 27 0 R", 12); + + loader.set_is_new_data_available(false); +- avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access()); +- while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail_, loader.hints())) { ++ CreateAvail(loader.file_avail(), loader.file_access()); ++ while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail(), loader.hints())) { + loader.FlushRequestedData(); + } + +- document_ = FPDFAvail_GetDocument(avail_, nullptr); +- ASSERT_TRUE(document_); ++ SetDocumentFromAvail(); ++ ASSERT_TRUE(document()); + + // Set all data available. + loader.set_is_new_data_available(true); + // Check second page, to load additional crossrefs. +- ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail_, 0, loader.hints())); ++ ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail(), 0, loader.hints())); + + // Test that api is robust enough to handle the bad case. +- EXPECT_FALSE(FPDF_GetMetaText(document_, "Type", nullptr, 0)); ++ EXPECT_FALSE(FPDF_GetMetaText(document(), "Type", nullptr, 0)); + } + + TEST_F(FPDFDataAvailEmbedderTest, BadInputsToAPIs) { +@@ -386,8 +385,22 @@ TEST_F(FPDFDataAvailEmbedderTest, BadInputsToAPIs) { + + TEST_F(FPDFDataAvailEmbedderTest, NegativePageIndex) { + TestAsyncLoader loader("linearized.pdf"); +- avail_ = FPDFAvail_Create(loader.file_avail(), loader.file_access()); +- ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail_, loader.hints())); ++ CreateAvail(loader.file_avail(), loader.file_access()); ++ ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints())); + EXPECT_EQ(PDF_DATA_NOTAVAIL, +- FPDFAvail_IsPageAvail(avail_, -1, loader.hints())); ++ FPDFAvail_IsPageAvail(avail(), -1, loader.hints())); ++} ++ ++TEST_F(FPDFDataAvailEmbedderTest, Bug_1324189) { ++ // Test passes if it doesn't crash. ++ TestAsyncLoader loader("bug_1324189.pdf"); ++ CreateAvail(loader.file_avail(), loader.file_access()); ++ ASSERT_EQ(PDF_DATA_NOTAVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints())); ++} ++ ++TEST_F(FPDFDataAvailEmbedderTest, Bug_1324503) { ++ // Test passes if it doesn't crash. ++ TestAsyncLoader loader("bug_1324503.pdf"); ++ CreateAvail(loader.file_avail(), loader.file_access()); ++ ASSERT_EQ(PDF_DATA_NOTAVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints())); + } +diff --git a/fpdfsdk/fpdf_doc.cpp b/fpdfsdk/fpdf_doc.cpp +index b0a2324a3..4f571443f 100644 +--- a/fpdfsdk/fpdf_doc.cpp ++++ b/fpdfsdk/fpdf_doc.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,19 +10,26 @@ + #include + #include + ++#include "constants/form_fields.h" ++#include "core/fpdfapi/page/cpdf_annotcontext.h" + #include "core/fpdfapi/page/cpdf_page.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_number.h" ++#include "core/fpdfapi/parser/cpdf_string.h" ++#include "core/fpdfapi/parser/fpdf_parser_decode.h" ++#include "core/fpdfdoc/cpdf_aaction.h" + #include "core/fpdfdoc/cpdf_bookmark.h" + #include "core/fpdfdoc/cpdf_bookmarktree.h" + #include "core/fpdfdoc/cpdf_dest.h" + #include "core/fpdfdoc/cpdf_linklist.h" + #include "core/fpdfdoc/cpdf_pagelabel.h" + #include "fpdfsdk/cpdfsdk_helpers.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "public/fpdf_formfill.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/numerics/safe_conversions.h" + + namespace { + +@@ -31,7 +38,7 @@ CPDF_Bookmark FindBookmark(const CPDF_BookmarkTree& tree, + const WideString& title, + std::set* visited) { + // Return if already checked to avoid circular calling. +- if (pdfium::ContainsKey(*visited, bookmark.GetDict())) ++ if (pdfium::Contains(*visited, bookmark.GetDict())) + return CPDF_Bookmark(); + visited->insert(bookmark.GetDict()); + +@@ -42,13 +49,13 @@ CPDF_Bookmark FindBookmark(const CPDF_BookmarkTree& tree, + } + + // Go into children items. +- CPDF_Bookmark child = tree.GetFirstChild(&bookmark); +- while (child.GetDict() && !pdfium::ContainsKey(*visited, child.GetDict())) { ++ CPDF_Bookmark child = tree.GetFirstChild(bookmark); ++ while (child.GetDict() && !pdfium::Contains(*visited, child.GetDict())) { + // Check this item and its children. + CPDF_Bookmark found = FindBookmark(tree, child, title, visited); + if (found.GetDict()) + return found; +- child = tree.GetNextSibling(&child); ++ child = tree.GetNextSibling(child); + } + return CPDF_Bookmark(); + } +@@ -59,7 +66,7 @@ CPDF_LinkList* GetLinkList(CPDF_Page* page) { + if (pList) + return pList; + +- auto pNewList = pdfium::MakeUnique(); ++ auto pNewList = std::make_unique(); + pList = pNewList.get(); + pDoc->SetLinksContext(std::move(pNewList)); + return pList; +@@ -73,9 +80,10 @@ FPDFBookmark_GetFirstChild(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark) { + if (!pDoc) + return nullptr; + CPDF_BookmarkTree tree(pDoc); +- CPDF_Bookmark cBookmark(CPDFDictionaryFromFPDFBookmark(bookmark)); ++ CPDF_Bookmark cBookmark( ++ pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark))); + return FPDFBookmarkFromCPDFDictionary( +- tree.GetFirstChild(&cBookmark).GetDict()); ++ tree.GetFirstChild(cBookmark).GetDict()); + } + + FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV +@@ -88,9 +96,10 @@ FPDFBookmark_GetNextSibling(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark) { + return nullptr; + + CPDF_BookmarkTree tree(pDoc); +- CPDF_Bookmark cBookmark(CPDFDictionaryFromFPDFBookmark(bookmark)); ++ CPDF_Bookmark cBookmark( ++ pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark))); + return FPDFBookmarkFromCPDFDictionary( +- tree.GetNextSibling(&cBookmark).GetDict()); ++ tree.GetNextSibling(cBookmark).GetDict()); + } + + FPDF_EXPORT unsigned long FPDF_CALLCONV +@@ -99,11 +108,20 @@ FPDFBookmark_GetTitle(FPDF_BOOKMARK bookmark, + unsigned long buflen) { + if (!bookmark) + return 0; +- CPDF_Bookmark cBookmark(CPDFDictionaryFromFPDFBookmark(bookmark)); ++ CPDF_Bookmark cBookmark( ++ pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark))); + WideString title = cBookmark.GetTitle(); + return Utf16EncodeMaybeCopyAndReturnLength(title, buffer, buflen); + } + ++FPDF_EXPORT int FPDF_CALLCONV FPDFBookmark_GetCount(FPDF_BOOKMARK bookmark) { ++ if (!bookmark) ++ return 0; ++ CPDF_Bookmark cBookmark( ++ pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark))); ++ return cBookmark.GetCount(); ++} ++ + FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV + FPDFBookmark_Find(FPDF_DOCUMENT document, FPDF_WIDESTRING title) { + CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document); +@@ -129,14 +147,15 @@ FPDFBookmark_GetDest(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark) { + if (!bookmark) + return nullptr; + +- CPDF_Bookmark cBookmark(CPDFDictionaryFromFPDFBookmark(bookmark)); ++ CPDF_Bookmark cBookmark( ++ pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark))); + CPDF_Dest dest = cBookmark.GetDest(pDoc); + if (dest.GetArray()) + return FPDFDestFromCPDFArray(dest.GetArray()); + // If this bookmark is not directly associated with a dest, we try to get + // action + CPDF_Action action = cBookmark.GetAction(); +- if (!action.GetDict()) ++ if (!action.HasDict()) + return nullptr; + return FPDFDestFromCPDFArray(action.GetDest(pDoc).GetArray()); + } +@@ -146,7 +165,8 @@ FPDFBookmark_GetAction(FPDF_BOOKMARK bookmark) { + if (!bookmark) + return nullptr; + +- CPDF_Bookmark cBookmark(CPDFDictionaryFromFPDFBookmark(bookmark)); ++ CPDF_Bookmark cBookmark( ++ pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark))); + return FPDFActionFromCPDFDictionary(cBookmark.GetAction().GetDict()); + } + +@@ -154,16 +174,17 @@ FPDF_EXPORT unsigned long FPDF_CALLCONV FPDFAction_GetType(FPDF_ACTION action) { + if (!action) + return PDFACTION_UNSUPPORTED; + +- CPDF_Action cAction(CPDFDictionaryFromFPDFAction(action)); +- CPDF_Action::ActionType type = cAction.GetType(); +- switch (type) { +- case CPDF_Action::GoTo: ++ CPDF_Action cAction(pdfium::WrapRetain(CPDFDictionaryFromFPDFAction(action))); ++ switch (cAction.GetType()) { ++ case CPDF_Action::Type::kGoTo: + return PDFACTION_GOTO; +- case CPDF_Action::GoToR: ++ case CPDF_Action::Type::kGoToR: + return PDFACTION_REMOTEGOTO; +- case CPDF_Action::URI: ++ case CPDF_Action::Type::kGoToE: ++ return PDFACTION_EMBEDDEDGOTO; ++ case CPDF_Action::Type::kURI: + return PDFACTION_URI; +- case CPDF_Action::Launch: ++ case CPDF_Action::Type::kLaunch: + return PDFACTION_LAUNCH; + default: + return PDFACTION_UNSUPPORTED; +@@ -177,25 +198,25 @@ FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFAction_GetDest(FPDF_DOCUMENT document, + return nullptr; + + unsigned long type = FPDFAction_GetType(action); +- if (type != PDFACTION_GOTO && type != PDFACTION_REMOTEGOTO) ++ if (type != PDFACTION_GOTO && type != PDFACTION_REMOTEGOTO && ++ type != PDFACTION_EMBEDDEDGOTO) { + return nullptr; +- +- CPDF_Action cAction(CPDFDictionaryFromFPDFAction(action)); ++ } ++ CPDF_Action cAction(pdfium::WrapRetain(CPDFDictionaryFromFPDFAction(action))); + return FPDFDestFromCPDFArray(cAction.GetDest(pDoc).GetArray()); + } + + FPDF_EXPORT unsigned long FPDF_CALLCONV + FPDFAction_GetFilePath(FPDF_ACTION action, void* buffer, unsigned long buflen) { + unsigned long type = FPDFAction_GetType(action); +- if (type != PDFACTION_REMOTEGOTO && type != PDFACTION_LAUNCH) ++ if (type != PDFACTION_REMOTEGOTO && type != PDFACTION_EMBEDDEDGOTO && ++ type != PDFACTION_LAUNCH) { + return 0; ++ } + +- CPDF_Action cAction(CPDFDictionaryFromFPDFAction(action)); ++ CPDF_Action cAction(pdfium::WrapRetain(CPDFDictionaryFromFPDFAction(action))); + ByteString path = cAction.GetFilePath().ToUTF8(); +- unsigned long len = path.GetLength() + 1; +- if (buffer && len <= buflen) +- memcpy(buffer, path.c_str(), len); +- return len; ++ return NulTerminateMaybeCopyAndReturnLength(path, buffer, buflen); + } + + FPDF_EXPORT unsigned long FPDF_CALLCONV +@@ -211,9 +232,11 @@ FPDFAction_GetURIPath(FPDF_DOCUMENT document, + if (type != PDFACTION_URI) + return 0; + +- CPDF_Action cAction(CPDFDictionaryFromFPDFAction(action)); ++ CPDF_Action cAction(pdfium::WrapRetain(CPDFDictionaryFromFPDFAction(action))); + ByteString path = cAction.GetURI(pDoc); +- unsigned long len = path.GetLength() + 1; ++ ++ const unsigned long len = ++ pdfium::base::checked_cast(path.GetLength() + 1); + if (buffer && len <= buflen) + memcpy(buffer, path.c_str(), len); + return len; +@@ -228,7 +251,7 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFDest_GetDestPageIndex(FPDF_DOCUMENT document, + if (!dest) + return -1; + +- CPDF_Dest destination(CPDFArrayFromFPDFDest(dest)); ++ CPDF_Dest destination(pdfium::WrapRetain(CPDFArrayFromFPDFDest(dest))); + return destination.GetDestPageIndex(pDoc); + } + +@@ -239,9 +262,10 @@ FPDFDest_GetView(FPDF_DEST dest, unsigned long* pNumParams, FS_FLOAT* pParams) { + return 0; + } + +- CPDF_Dest destination(CPDFArrayFromFPDFDest(dest)); +- unsigned long nParams = destination.GetNumParams(); +- ASSERT(nParams <= 4); ++ CPDF_Dest destination(pdfium::WrapRetain(CPDFArrayFromFPDFDest(dest))); ++ const unsigned long nParams = ++ pdfium::base::checked_cast(destination.GetNumParams()); ++ DCHECK(nParams <= 4); + *pNumParams = nParams; + for (unsigned long i = 0; i < nParams; ++i) + pParams[i] = destination.GetParam(i); +@@ -259,13 +283,13 @@ FPDFDest_GetLocationInPage(FPDF_DEST dest, + if (!dest) + return false; + +- auto destination = pdfium::MakeUnique(CPDFArrayFromFPDFDest(dest)); ++ CPDF_Dest destination(pdfium::WrapRetain(CPDFArrayFromFPDFDest(dest))); + + // FPDF_BOOL is an int, GetXYZ expects bools. + bool bHasX; + bool bHasY; + bool bHasZoom; +- if (!destination->GetXYZ(&bHasX, &bHasY, &bHasZoom, x, y, zoom)) ++ if (!destination.GetXYZ(&bHasX, &bHasY, &bHasZoom, x, y, zoom)) + return false; + + *hasXVal = bHasX; +@@ -288,7 +312,8 @@ FPDF_EXPORT FPDF_LINK FPDF_CALLCONV FPDFLink_GetLinkAtPoint(FPDF_PAGE page, + CPDF_Link link = pLinkList->GetLinkAtPoint( + pPage, CFX_PointF(static_cast(x), static_cast(y)), nullptr); + +- return FPDFLinkFromCPDFDictionary(link.GetDict()); ++ // Unretained reference in public API. NOLINTNEXTLINE ++ return FPDFLinkFromCPDFDictionary(link.GetMutableDict()); + } + + FPDF_EXPORT int FPDF_CALLCONV FPDFLink_GetLinkZOrderAtPoint(FPDF_PAGE page, +@@ -316,13 +341,13 @@ FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFLink_GetDest(FPDF_DOCUMENT document, + CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document); + if (!pDoc) + return nullptr; +- CPDF_Link cLink(CPDFDictionaryFromFPDFLink(link)); ++ CPDF_Link cLink(pdfium::WrapRetain(CPDFDictionaryFromFPDFLink(link))); + FPDF_DEST dest = FPDFDestFromCPDFArray(cLink.GetDest(pDoc).GetArray()); + if (dest) + return dest; + // If this link is not directly associated with a dest, we try to get action + CPDF_Action action = cLink.GetAction(); +- if (!action.GetDict()) ++ if (!action.HasDict()) + return nullptr; + return FPDFDestFromCPDFArray(action.GetDest(pDoc).GetArray()); + } +@@ -331,7 +356,7 @@ FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDFLink_GetAction(FPDF_LINK link) { + if (!link) + return nullptr; + +- CPDF_Link cLink(CPDFDictionaryFromFPDFLink(link)); ++ CPDF_Link cLink(pdfium::WrapRetain(CPDFDictionaryFromFPDFLink(link))); + return FPDFActionFromCPDFDictionary(cLink.GetAction().GetDict()); + } + +@@ -343,22 +368,37 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_Enumerate(FPDF_PAGE page, + CPDF_Page* pPage = CPDFPageFromFPDFPage(page); + if (!pPage) + return false; +- CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots"); ++ RetainPtr pAnnots = pPage->GetMutableAnnotsArray(); + if (!pAnnots) + return false; + for (size_t i = *start_pos; i < pAnnots->size(); i++) { +- CPDF_Dictionary* pDict = ToDictionary(pAnnots->GetDirectObjectAt(i)); ++ RetainPtr pDict = ++ ToDictionary(pAnnots->GetMutableDirectObjectAt(i)); + if (!pDict) + continue; +- if (pDict->GetStringFor("Subtype") == "Link") { ++ if (pDict->GetByteStringFor("Subtype") == "Link") { + *start_pos = static_cast(i + 1); +- *link_annot = FPDFLinkFromCPDFDictionary(pDict); ++ *link_annot = FPDFLinkFromCPDFDictionary(pDict.Get()); + return true; + } + } + return false; + } + ++FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV ++FPDFLink_GetAnnot(FPDF_PAGE page, FPDF_LINK link_annot) { ++ CPDF_Page* pPage = CPDFPageFromFPDFPage(page); ++ RetainPtr pAnnotDict(CPDFDictionaryFromFPDFLink(link_annot)); ++ if (!pPage || !pAnnotDict) ++ return nullptr; ++ ++ auto pAnnotContext = std::make_unique( ++ std::move(pAnnotDict), IPDFPageFromFPDFPage(page)); ++ ++ // Caller takes the ownership of the object. ++ return FPDFAnnotationFromCPDFAnnotContext(pAnnotContext.release()); ++} ++ + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetAnnotRect(FPDF_LINK link_annot, + FS_RECTF* rect) { + if (!link_annot || !rect) +@@ -370,7 +410,7 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetAnnotRect(FPDF_LINK link_annot, + } + + FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountQuadPoints(FPDF_LINK link_annot) { +- const CPDF_Array* pArray = ++ RetainPtr pArray = + GetQuadPointsArrayFromDictionary(CPDFDictionaryFromFPDFLink(link_annot)); + return pArray ? static_cast(pArray->size() / 8) : 0; + } +@@ -386,12 +426,62 @@ FPDFLink_GetQuadPoints(FPDF_LINK link_annot, + if (!pLinkDict) + return false; + +- const CPDF_Array* pArray = GetQuadPointsArrayFromDictionary(pLinkDict); ++ RetainPtr pArray = ++ GetQuadPointsArrayFromDictionary(pLinkDict); + if (!pArray) + return false; + +- return GetQuadPointsAtIndex(pArray, static_cast(quad_index), +- quad_points); ++ return GetQuadPointsAtIndex(std::move(pArray), ++ static_cast(quad_index), quad_points); ++} ++ ++FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDF_GetPageAAction(FPDF_PAGE page, ++ int aa_type) { ++ CPDF_Page* pdf_page = CPDFPageFromFPDFPage(page); ++ if (!pdf_page) ++ return nullptr; ++ ++ CPDF_AAction aa(pdf_page->GetDict()->GetDictFor(pdfium::form_fields::kAA)); ++ CPDF_AAction::AActionType type; ++ if (aa_type == FPDFPAGE_AACTION_OPEN) ++ type = CPDF_AAction::kOpenPage; ++ else if (aa_type == FPDFPAGE_AACTION_CLOSE) ++ type = CPDF_AAction::kClosePage; ++ else ++ return nullptr; ++ ++ if (!aa.ActionExist(type)) ++ return nullptr; ++ ++ CPDF_Action action = aa.GetAction(type); ++ return FPDFActionFromCPDFDictionary(action.GetDict()); ++} ++ ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDF_GetFileIdentifier(FPDF_DOCUMENT document, ++ FPDF_FILEIDTYPE id_type, ++ void* buffer, ++ unsigned long buflen) { ++ CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document); ++ if (!pDoc) ++ return 0; ++ ++ // Check if |id_type| is valid. ++ if (id_type != FILEIDTYPE_PERMANENT && id_type != FILEIDTYPE_CHANGING) ++ return 0; ++ ++ RetainPtr pFileId = pDoc->GetFileIdentifier(); ++ if (!pFileId) ++ return 0; ++ ++ size_t nIndex = id_type == FILEIDTYPE_PERMANENT ? 0 : 1; ++ RetainPtr pValue = ++ ToString(pFileId->GetDirectObjectAt(nIndex)); ++ if (!pValue) ++ return 0; ++ ++ return NulTerminateMaybeCopyAndReturnLength(pValue->GetString(), buffer, ++ buflen); + } + + FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetMetaText(FPDF_DOCUMENT document, +@@ -404,9 +494,10 @@ FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetMetaText(FPDF_DOCUMENT document, + if (!pDoc) + return 0; + +- const CPDF_Dictionary* pInfo = pDoc->GetInfo(); ++ RetainPtr pInfo = pDoc->GetInfo(); + if (!pInfo) + return 0; ++ + WideString text = pInfo->GetUnicodeTextFor(tag); + return Utf16EncodeMaybeCopyAndReturnLength(text, buffer, buflen); + } +@@ -421,7 +512,7 @@ FPDF_GetPageLabel(FPDF_DOCUMENT document, + + // CPDF_PageLabel can deal with NULL |document|. + CPDF_PageLabel label(CPDFDocumentFromFPDFDocument(document)); +- Optional str = label.GetLabel(page_index); ++ absl::optional str = label.GetLabel(page_index); + return str.has_value() + ? Utf16EncodeMaybeCopyAndReturnLength(str.value(), buffer, buflen) + : 0; +diff --git a/fpdfsdk/fpdf_doc_embeddertest.cpp b/fpdfsdk/fpdf_doc_embeddertest.cpp +index 84712ed8a..f06e596e6 100644 +--- a/fpdfsdk/fpdf_doc_embeddertest.cpp ++++ b/fpdfsdk/fpdf_doc_embeddertest.cpp +@@ -1,14 +1,12 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include + #include +-#include + #include + + #include "core/fpdfapi/parser/cpdf_document.h" +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/bytestring.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "public/cpp/fpdf_scopers.h" + #include "public/fpdf_doc.h" +@@ -21,7 +19,7 @@ + class FPDFDocEmbedderTest : public EmbedderTest {}; + + TEST_F(FPDFDocEmbedderTest, MultipleSamePage) { +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document()); + + std::set unique_pages; +@@ -40,7 +38,7 @@ TEST_F(FPDFDocEmbedderTest, MultipleSamePage) { + } + + TEST_F(FPDFDocEmbedderTest, DestGetPageIndex) { +- EXPECT_TRUE(OpenDocument("named_dests.pdf")); ++ ASSERT_TRUE(OpenDocument("named_dests.pdf")); + + // NULL argument cases. + EXPECT_EQ(-1, FPDFDest_GetDestPageIndex(nullptr, nullptr)); +@@ -68,7 +66,7 @@ TEST_F(FPDFDocEmbedderTest, DestGetPageIndex) { + } + + TEST_F(FPDFDocEmbedderTest, DestGetView) { +- EXPECT_TRUE(OpenDocument("named_dests.pdf")); ++ ASSERT_TRUE(OpenDocument("named_dests.pdf")); + + unsigned long numParams; + FS_FLOAT params[4]; +@@ -127,7 +125,7 @@ TEST_F(FPDFDocEmbedderTest, DestGetView) { + } + + TEST_F(FPDFDocEmbedderTest, DestGetLocationInPage) { +- EXPECT_TRUE(OpenDocument("named_dests.pdf")); ++ ASSERT_TRUE(OpenDocument("named_dests.pdf")); + + FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First"); + EXPECT_TRUE(dest); +@@ -154,8 +152,46 @@ TEST_F(FPDFDocEmbedderTest, DestGetLocationInPage) { + EXPECT_EQ(1, zoom); + } + ++TEST_F(FPDFDocEmbedderTest, BUG_1506_1) { ++ ASSERT_TRUE(OpenDocument("bug_1506.pdf")); ++ ++ FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First"); ++ ASSERT_TRUE(dest); ++ EXPECT_EQ(3, FPDFDest_GetDestPageIndex(document(), dest)); ++} ++ ++TEST_F(FPDFDocEmbedderTest, BUG_1506_2) { ++ ASSERT_TRUE(OpenDocument("bug_1506.pdf")); ++ ++ std::vector pages; ++ for (int i : {0, 2}) ++ pages.push_back(LoadPage(i)); ++ ++ FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First"); ++ ASSERT_TRUE(dest); ++ EXPECT_EQ(3, FPDFDest_GetDestPageIndex(document(), dest)); ++ ++ for (FPDF_PAGE page : pages) ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFDocEmbedderTest, BUG_1506_3) { ++ ASSERT_TRUE(OpenDocument("bug_1506.pdf")); ++ ++ std::vector pages; ++ for (int i : {0, 1, 3}) ++ pages.push_back(LoadPage(i)); ++ ++ FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First"); ++ ASSERT_TRUE(dest); ++ EXPECT_EQ(3, FPDFDest_GetDestPageIndex(document(), dest)); ++ ++ for (FPDF_PAGE page : pages) ++ UnloadPage(page); ++} ++ + TEST_F(FPDFDocEmbedderTest, BUG_680376) { +- EXPECT_TRUE(OpenDocument("bug_680376.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_680376.pdf")); + + // Page number directly in item from Dests NameTree. + FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First"); +@@ -164,12 +200,12 @@ TEST_F(FPDFDocEmbedderTest, BUG_680376) { + } + + TEST_F(FPDFDocEmbedderTest, BUG_821454) { +- EXPECT_TRUE(OpenDocument("bug_821454.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_821454.pdf")); + + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +- // Cover some NULL arg cases while we're at it. ++ // Cover some invalid argument cases while we're at it. + EXPECT_FALSE(FPDFLink_GetLinkAtPoint(nullptr, 150, 360)); + EXPECT_EQ(-1, FPDFLink_GetLinkZOrderAtPoint(nullptr, 150, 360)); + +@@ -186,6 +222,11 @@ TEST_F(FPDFDocEmbedderTest, BUG_821454) { + FPDF_DEST dest2 = FPDFLink_GetDest(document(), link2); + ASSERT_TRUE(dest2); + ++ // Cover more invalid argument cases while we're at it. ++ EXPECT_FALSE(FPDFLink_GetDest(nullptr, nullptr)); ++ EXPECT_FALSE(FPDFLink_GetDest(nullptr, link1)); ++ EXPECT_FALSE(FPDFLink_GetDest(document(), nullptr)); ++ + EXPECT_EQ(0, FPDFDest_GetDestPageIndex(document(), dest1)); + EXPECT_EQ(0, FPDFDest_GetDestPageIndex(document(), dest2)); + +@@ -226,19 +267,19 @@ TEST_F(FPDFDocEmbedderTest, BUG_821454) { + } + + TEST_F(FPDFDocEmbedderTest, ActionBadArguments) { +- EXPECT_TRUE(OpenDocument("launch_action.pdf")); ++ ASSERT_TRUE(OpenDocument("launch_action.pdf")); + EXPECT_EQ(static_cast(PDFACTION_UNSUPPORTED), + FPDFAction_GetType(nullptr)); + +- EXPECT_EQ(nullptr, FPDFAction_GetDest(nullptr, nullptr)); +- EXPECT_EQ(nullptr, FPDFAction_GetDest(document(), nullptr)); ++ EXPECT_FALSE(FPDFAction_GetDest(nullptr, nullptr)); ++ EXPECT_FALSE(FPDFAction_GetDest(document(), nullptr)); + EXPECT_EQ(0u, FPDFAction_GetFilePath(nullptr, nullptr, 0)); + EXPECT_EQ(0u, FPDFAction_GetURIPath(nullptr, nullptr, nullptr, 0)); + EXPECT_EQ(0u, FPDFAction_GetURIPath(document(), nullptr, nullptr, 0)); + } + + TEST_F(FPDFDocEmbedderTest, ActionLaunch) { +- EXPECT_TRUE(OpenDocument("launch_action.pdf")); ++ ASSERT_TRUE(OpenDocument("launch_action.pdf")); + + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); +@@ -262,14 +303,14 @@ TEST_F(FPDFDocEmbedderTest, ActionLaunch) { + EXPECT_STREQ(kExpectedResult, buf); + + // Other public methods are not appropriate for launch actions. +- EXPECT_EQ(nullptr, FPDFAction_GetDest(document(), action)); ++ EXPECT_FALSE(FPDFAction_GetDest(document(), action)); + EXPECT_EQ(0u, FPDFAction_GetURIPath(document(), action, buf, sizeof(buf))); + + UnloadPage(page); + } + +-TEST_F(FPDFDocEmbedderTest, ActionURI) { +- EXPECT_TRUE(OpenDocument("uri_action.pdf")); ++TEST_F(FPDFDocEmbedderTest, ActionUri) { ++ ASSERT_TRUE(OpenDocument("uri_action.pdf")); + + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); +@@ -293,14 +334,67 @@ TEST_F(FPDFDocEmbedderTest, ActionURI) { + EXPECT_STREQ(kExpectedResult, buf); + + // Other public methods are not appropriate for URI actions +- EXPECT_EQ(nullptr, FPDFAction_GetDest(document(), action)); ++ EXPECT_FALSE(FPDFAction_GetDest(document(), action)); + EXPECT_EQ(0u, FPDFAction_GetFilePath(action, buf, sizeof(buf))); + + UnloadPage(page); + } + ++TEST_F(FPDFDocEmbedderTest, ActionUriNonAscii) { ++ ASSERT_TRUE(OpenDocument("uri_action_nonascii.pdf")); ++ ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ // The target action is nearly the size of the whole page. ++ FPDF_LINK link = FPDFLink_GetLinkAtPoint(page, 100, 100); ++ ASSERT_TRUE(link); ++ ++ FPDF_ACTION action = FPDFLink_GetAction(link); ++ ASSERT_TRUE(action); ++ EXPECT_EQ(static_cast(PDFACTION_URI), ++ FPDFAction_GetType(action)); ++ ++ // FPDFAction_GetURIPath() may return data in any encoding, or even with bad ++ // encoding. ++ const char kExpectedResult[] = ++ "https://example.com/\xA5octal\xC7" ++ "chars"; ++ const unsigned long kExpectedLength = sizeof(kExpectedResult); ++ unsigned long bufsize = FPDFAction_GetURIPath(document(), action, nullptr, 0); ++ ASSERT_EQ(kExpectedLength, bufsize); ++ ++ char buf[1024]; ++ EXPECT_EQ(bufsize, FPDFAction_GetURIPath(document(), action, buf, bufsize)); ++ EXPECT_STREQ(kExpectedResult, buf); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFDocEmbedderTest, LinkToAnnotConversion) { ++ ASSERT_TRUE(OpenDocument("annots.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ { ++ FPDF_LINK first_link = FPDFLink_GetLinkAtPoint(page, 69.00, 653.00); ++ ScopedFPDFAnnotation first_annot(FPDFLink_GetAnnot(page, first_link)); ++ EXPECT_EQ(0, FPDFPage_GetAnnotIndex(page, first_annot.get())); ++ ++ FPDF_LINK second_link = FPDFLink_GetLinkAtPoint(page, 80.00, 633.00); ++ ScopedFPDFAnnotation second_annot(FPDFLink_GetAnnot(page, second_link)); ++ EXPECT_EQ(1, FPDFPage_GetAnnotIndex(page, second_annot.get())); ++ ++ // Also test invalid arguments. ++ EXPECT_FALSE(FPDFLink_GetAnnot(nullptr, nullptr)); ++ EXPECT_FALSE(FPDFLink_GetAnnot(page, nullptr)); ++ EXPECT_FALSE(FPDFLink_GetAnnot(nullptr, second_link)); ++ } ++ ++ UnloadPage(page); ++} ++ + TEST_F(FPDFDocEmbedderTest, ActionGoto) { +- EXPECT_TRUE(OpenDocument("goto_action.pdf")); ++ ASSERT_TRUE(OpenDocument("goto_action.pdf")); + + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); +@@ -324,8 +418,45 @@ TEST_F(FPDFDocEmbedderTest, ActionGoto) { + UnloadPage(page); + } + ++TEST_F(FPDFDocEmbedderTest, ActionEmbeddedGoto) { ++ ASSERT_TRUE(OpenDocument("gotoe_action.pdf")); ++ ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ // The target action is nearly the size of the whole page. ++ FPDF_LINK link = FPDFLink_GetLinkAtPoint(page, 100, 100); ++ ASSERT_TRUE(link); ++ ++ FPDF_ACTION action = FPDFLink_GetAction(link); ++ ASSERT_TRUE(action); ++ EXPECT_EQ(static_cast(PDFACTION_EMBEDDEDGOTO), ++ FPDFAction_GetType(action)); ++ ++ FPDF_DEST dest = FPDFAction_GetDest(document(), action); ++ EXPECT_TRUE(dest); ++ ++ unsigned long num_params = 42; ++ FS_FLOAT params[4]; ++ std::fill_n(params, 4, 42.4242f); ++ EXPECT_EQ(static_cast(PDFDEST_VIEW_FIT), ++ FPDFDest_GetView(dest, &num_params, params)); ++ EXPECT_EQ(0u, num_params); ++ EXPECT_FLOAT_EQ(42.4242f, params[0]); ++ ++ const char kExpectedResult[] = "ExampleFile.pdf"; ++ const unsigned long kExpectedLength = sizeof(kExpectedResult); ++ char buf[1024]; ++ unsigned long bufsize = FPDFAction_GetFilePath(action, nullptr, 0); ++ EXPECT_EQ(kExpectedLength, bufsize); ++ EXPECT_EQ(kExpectedLength, FPDFAction_GetFilePath(action, buf, bufsize)); ++ EXPECT_STREQ(kExpectedResult, buf); ++ ++ UnloadPage(page); ++} ++ + TEST_F(FPDFDocEmbedderTest, ActionNonesuch) { +- EXPECT_TRUE(OpenDocument("nonesuch_action.pdf")); ++ ASSERT_TRUE(OpenDocument("nonesuch_action.pdf")); + + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); +@@ -352,54 +483,70 @@ TEST_F(FPDFDocEmbedderTest, NoBookmarks) { + unsigned short buf[128]; + + // Open a file with no bookmarks. +- EXPECT_TRUE(OpenDocument("named_dests.pdf")); ++ ASSERT_TRUE(OpenDocument("named_dests.pdf")); + + // NULL argument cases. + EXPECT_EQ(0u, FPDFBookmark_GetTitle(nullptr, buf, sizeof(buf))); +- EXPECT_EQ(nullptr, FPDFBookmark_GetFirstChild(nullptr, nullptr)); +- EXPECT_EQ(nullptr, FPDFBookmark_GetFirstChild(document(), nullptr)); +- EXPECT_EQ(nullptr, FPDFBookmark_GetNextSibling(nullptr, nullptr)); +- EXPECT_EQ(nullptr, FPDFBookmark_GetNextSibling(document(), nullptr)); +- EXPECT_EQ(nullptr, FPDFBookmark_Find(nullptr, nullptr)); +- EXPECT_EQ(nullptr, FPDFBookmark_Find(document(), nullptr)); +- EXPECT_EQ(nullptr, FPDFBookmark_GetDest(nullptr, nullptr)); +- EXPECT_EQ(nullptr, FPDFBookmark_GetDest(document(), nullptr)); +- EXPECT_EQ(nullptr, FPDFBookmark_GetAction(nullptr)); ++ EXPECT_FALSE(FPDFBookmark_GetFirstChild(nullptr, nullptr)); ++ EXPECT_FALSE(FPDFBookmark_GetFirstChild(document(), nullptr)); ++ EXPECT_FALSE(FPDFBookmark_GetNextSibling(nullptr, nullptr)); ++ EXPECT_FALSE(FPDFBookmark_GetNextSibling(document(), nullptr)); ++ EXPECT_FALSE(FPDFBookmark_Find(nullptr, nullptr)); ++ EXPECT_FALSE(FPDFBookmark_Find(document(), nullptr)); ++ EXPECT_FALSE(FPDFBookmark_GetDest(nullptr, nullptr)); ++ EXPECT_FALSE(FPDFBookmark_GetDest(document(), nullptr)); ++ EXPECT_FALSE(FPDFBookmark_GetAction(nullptr)); + } + + TEST_F(FPDFDocEmbedderTest, Bookmarks) { + unsigned short buf[128]; + +- // Open a file with two bookmarks. +- EXPECT_TRUE(OpenDocument("bookmarks.pdf")); ++ // Open a file with many bookmarks. ++ ASSERT_TRUE(OpenDocument("bookmarks.pdf")); + + FPDF_BOOKMARK child = FPDFBookmark_GetFirstChild(document(), nullptr); + EXPECT_TRUE(child); + EXPECT_EQ(34u, FPDFBookmark_GetTitle(child, buf, sizeof(buf))); +- EXPECT_EQ(WideString(L"A Good Beginning"), WideString::FromUTF16LE(buf, 16)); +- +- FPDF_DEST dest = FPDFBookmark_GetDest(document(), child); +- EXPECT_FALSE(dest); // TODO(tsepez): put real dest into bookmarks.pdf ++ EXPECT_EQ(L"A Good Beginning", GetPlatformWString(buf)); ++ EXPECT_EQ(0, FPDFBookmark_GetCount(child)); ++ EXPECT_EQ(0, FPDFBookmark_GetCount(nullptr)); + +- FPDF_ACTION action = FPDFBookmark_GetAction(child); +- EXPECT_FALSE(action); // TODO(tsepez): put real action into bookmarks.pdf ++ EXPECT_FALSE(FPDFBookmark_GetDest(document(), child)); ++ EXPECT_FALSE(FPDFBookmark_GetAction(child)); + + FPDF_BOOKMARK grand_child = FPDFBookmark_GetFirstChild(document(), child); + EXPECT_FALSE(grand_child); + + FPDF_BOOKMARK sibling = FPDFBookmark_GetNextSibling(document(), child); + EXPECT_TRUE(sibling); +- EXPECT_EQ(28u, FPDFBookmark_GetTitle(sibling, buf, sizeof(buf))); +- EXPECT_EQ(WideString(L"A Good Ending"), WideString::FromUTF16LE(buf, 13)); +- +- EXPECT_EQ(nullptr, FPDFBookmark_GetNextSibling(document(), sibling)); ++ EXPECT_EQ(24u, FPDFBookmark_GetTitle(sibling, buf, sizeof(buf))); ++ EXPECT_EQ(L"Open Middle", GetPlatformWString(buf)); ++ EXPECT_TRUE(FPDFBookmark_GetAction(sibling)); ++ EXPECT_EQ(1, FPDFBookmark_GetCount(sibling)); ++ ++ FPDF_BOOKMARK sibling2 = FPDFBookmark_GetNextSibling(document(), sibling); ++ EXPECT_TRUE(sibling2); ++ EXPECT_EQ(42u, FPDFBookmark_GetTitle(sibling2, buf, sizeof(buf))); ++ EXPECT_EQ(L"A Good Closed Ending", GetPlatformWString(buf)); ++ EXPECT_EQ(-2, FPDFBookmark_GetCount(sibling2)); ++ ++ EXPECT_FALSE(FPDFBookmark_GetNextSibling(document(), sibling2)); ++ ++ grand_child = FPDFBookmark_GetFirstChild(document(), sibling); ++ EXPECT_TRUE(grand_child); ++ EXPECT_EQ(46u, FPDFBookmark_GetTitle(grand_child, buf, sizeof(buf))); ++ EXPECT_EQ(L"Open Middle Descendant", GetPlatformWString(buf)); ++ EXPECT_EQ(0, FPDFBookmark_GetCount(grand_child)); ++ EXPECT_TRUE(FPDFBookmark_GetDest(document(), grand_child)); ++ ++ EXPECT_FALSE(FPDFBookmark_GetNextSibling(document(), grand_child)); + } + + TEST_F(FPDFDocEmbedderTest, FindBookmarks) { + unsigned short buf[128]; + +- // Open a file with two bookmarks. +- EXPECT_TRUE(OpenDocument("bookmarks.pdf")); ++ // Open a file with many bookmarks. ++ ASSERT_TRUE(OpenDocument("bookmarks.pdf")); + + // Find the first one, based on its known title. + ScopedFPDFWideString title = GetFPDFWideString(L"A Good Beginning"); +@@ -408,28 +555,28 @@ TEST_F(FPDFDocEmbedderTest, FindBookmarks) { + + // Check that the string matches. + EXPECT_EQ(34u, FPDFBookmark_GetTitle(child, buf, sizeof(buf))); +- EXPECT_EQ(WideString(L"A Good Beginning"), WideString::FromUTF16LE(buf, 16)); ++ EXPECT_EQ(L"A Good Beginning", GetPlatformWString(buf)); + + // Check that it is them same as the one returned by GetFirstChild. + EXPECT_EQ(child, FPDFBookmark_GetFirstChild(document(), nullptr)); + + // Try to find one using a non-existent title. + ScopedFPDFWideString bad_title = GetFPDFWideString(L"A BAD Beginning"); +- EXPECT_EQ(nullptr, FPDFBookmark_Find(document(), bad_title.get())); ++ EXPECT_FALSE(FPDFBookmark_Find(document(), bad_title.get())); + } + + // Check circular bookmarks will not cause infinite loop. + TEST_F(FPDFDocEmbedderTest, FindBookmarks_bug420) { + // Open a file with circular bookmarks. +- EXPECT_TRUE(OpenDocument("bookmarks_circular.pdf")); ++ ASSERT_TRUE(OpenDocument("bookmarks_circular.pdf")); + + // Try to find a title. + ScopedFPDFWideString title = GetFPDFWideString(L"anything"); +- EXPECT_EQ(nullptr, FPDFBookmark_Find(document(), title.get())); ++ EXPECT_FALSE(FPDFBookmark_Find(document(), title.get())); + } + + TEST_F(FPDFDocEmbedderTest, DeletePage) { +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + EXPECT_EQ(1, FPDF_GetPageCount(document())); + + FPDFPage_Delete(nullptr, 0); +@@ -444,6 +591,57 @@ TEST_F(FPDFDocEmbedderTest, DeletePage) { + EXPECT_EQ(0, FPDF_GetPageCount(document())); + } + ++TEST_F(FPDFDocEmbedderTest, GetFileIdentifier) { ++ ASSERT_TRUE(OpenDocument("split_streams.pdf")); ++ constexpr size_t kMd5Length = 17; ++ char buf[kMd5Length]; ++ EXPECT_EQ(0u, ++ FPDF_GetFileIdentifier(document(), static_cast(-1), ++ buf, sizeof(buf))); ++ EXPECT_EQ(0u, ++ FPDF_GetFileIdentifier(document(), static_cast(2), ++ buf, sizeof(buf))); ++ EXPECT_EQ(0u, FPDF_GetFileIdentifier(nullptr, FILEIDTYPE_PERMANENT, buf, ++ sizeof(buf))); ++ EXPECT_EQ(kMd5Length, FPDF_GetFileIdentifier(document(), FILEIDTYPE_PERMANENT, ++ nullptr, 0)); ++ ++ constexpr char kExpectedPermanent[] = ++ "\xF3\x41\xAE\x65\x4A\x77\xAC\xD5\x06\x5A\x76\x45\xE5\x96\xE6\xE6"; ++ ASSERT_EQ(kMd5Length, FPDF_GetFileIdentifier(document(), FILEIDTYPE_PERMANENT, ++ buf, sizeof(buf))); ++ EXPECT_EQ(kExpectedPermanent, ByteString(buf)); ++ ++ constexpr char kExpectedChanging[] = ++ "\xBC\x37\x29\x8A\x3F\x87\xF4\x79\x22\x9B\xCE\x99\x7C\xA7\x91\xF7"; ++ ASSERT_EQ(kMd5Length, FPDF_GetFileIdentifier(document(), FILEIDTYPE_CHANGING, ++ buf, sizeof(buf))); ++ EXPECT_EQ(kExpectedChanging, ByteString(buf)); ++} ++ ++TEST_F(FPDFDocEmbedderTest, GetNonHexFileIdentifier) { ++ ASSERT_TRUE(OpenDocument("non_hex_file_id.pdf")); ++ char buf[18]; ++ ++ constexpr char kPermanentNonHex[] = "permanent non-hex"; ++ ASSERT_EQ(18u, FPDF_GetFileIdentifier(document(), FILEIDTYPE_PERMANENT, buf, ++ sizeof(buf))); ++ EXPECT_EQ(kPermanentNonHex, ByteString(buf)); ++ ++ constexpr char kChangingNonHex[] = "changing non-hex"; ++ ASSERT_EQ(17u, FPDF_GetFileIdentifier(document(), FILEIDTYPE_CHANGING, buf, ++ sizeof(buf))); ++ EXPECT_EQ(kChangingNonHex, ByteString(buf)); ++} ++ ++TEST_F(FPDFDocEmbedderTest, GetNonexistentFileIdentifier) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ EXPECT_EQ( ++ 0u, FPDF_GetFileIdentifier(document(), FILEIDTYPE_PERMANENT, nullptr, 0)); ++ EXPECT_EQ( ++ 0u, FPDF_GetFileIdentifier(document(), FILEIDTYPE_CHANGING, nullptr, 0)); ++} ++ + TEST_F(FPDFDocEmbedderTest, GetMetaText) { + ASSERT_TRUE(OpenDocument("bug_601362.pdf")); + +@@ -461,32 +659,24 @@ TEST_F(FPDFDocEmbedderTest, GetMetaText) { + ASSERT_EQ(2u, FPDF_GetMetaText(document(), "Keywords", buf, sizeof(buf))); + ASSERT_EQ(2u, FPDF_GetMetaText(document(), "Producer", buf, sizeof(buf))); + +- constexpr wchar_t kExpectedCreator[] = L"Microsoft Word"; + ASSERT_EQ(30u, FPDF_GetMetaText(document(), "Creator", buf, sizeof(buf))); +- EXPECT_EQ(WideString(kExpectedCreator), +- WideString::FromUTF16LE(buf, FXSYS_len(kExpectedCreator))); ++ EXPECT_EQ(L"Microsoft Word", GetPlatformWString(buf)); + +- constexpr wchar_t kExpectedCreationDate[] = L"D:20160411190039+00'00'"; + ASSERT_EQ(48u, + FPDF_GetMetaText(document(), "CreationDate", buf, sizeof(buf))); +- EXPECT_EQ(WideString(kExpectedCreationDate), +- WideString::FromUTF16LE(buf, FXSYS_len(kExpectedCreationDate))); ++ EXPECT_EQ(L"D:20160411190039+00'00'", GetPlatformWString(buf)); + +- constexpr wchar_t kExpectedModDate[] = L"D:20160411190039+00'00'"; + ASSERT_EQ(48u, FPDF_GetMetaText(document(), "ModDate", buf, sizeof(buf))); +- EXPECT_EQ(WideString(kExpectedModDate), +- WideString::FromUTF16LE(buf, FXSYS_len(kExpectedModDate))); ++ EXPECT_EQ(L"D:20160411190039+00'00'", GetPlatformWString(buf)); + } + + TEST_F(FPDFDocEmbedderTest, Bug_182) { + ASSERT_TRUE(OpenDocument("bug_182.pdf")); + + unsigned short buf[128]; +- constexpr wchar_t kExpectedTitle[] = L"Super Visual Formade å°åˆ·"; + + ASSERT_EQ(48u, FPDF_GetMetaText(document(), "Title", buf, sizeof(buf))); +- EXPECT_EQ(WideString(kExpectedTitle), +- WideString::FromUTF16LE(buf, FXSYS_len(kExpectedTitle))); ++ EXPECT_EQ(L"Super Visual Formade å°åˆ·", GetPlatformWString(buf)); + } + + TEST_F(FPDFDocEmbedderTest, GetMetaTextSameObjectNumber) { +@@ -496,10 +686,8 @@ TEST_F(FPDFDocEmbedderTest, GetMetaTextSameObjectNumber) { + // (1 0). Both objects are /Info dictionaries, but contain different data. + // Make sure ModDate is the date of the last modification. + unsigned short buf[128]; +- constexpr wchar_t kExpectedModDate[] = L"D:20170612232940-04'00'"; + ASSERT_EQ(48u, FPDF_GetMetaText(document(), "ModDate", buf, sizeof(buf))); +- EXPECT_EQ(WideString(kExpectedModDate), +- WideString::FromUTF16LE(buf, FXSYS_len(kExpectedModDate))); ++ EXPECT_EQ(L"D:20170612232940-04'00'", GetPlatformWString(buf)); + } + + TEST_F(FPDFDocEmbedderTest, GetMetaTextInAttachmentFile) { +@@ -507,10 +695,8 @@ TEST_F(FPDFDocEmbedderTest, GetMetaTextInAttachmentFile) { + + // Make sure this is the date from the PDF itself and not the attached PDF. + unsigned short buf[128]; +- constexpr wchar_t kExpectedModDate[] = L"D:20170712214448-07'00'"; + ASSERT_EQ(48u, FPDF_GetMetaText(document(), "ModDate", buf, sizeof(buf))); +- EXPECT_EQ(WideString(kExpectedModDate), +- WideString::FromUTF16LE(buf, FXSYS_len(kExpectedModDate))); ++ EXPECT_EQ(L"D:20170712214448-07'00'", GetPlatformWString(buf)); + } + + TEST_F(FPDFDocEmbedderTest, GetMetaTextFromNewDocument) { +@@ -520,15 +706,47 @@ TEST_F(FPDFDocEmbedderTest, GetMetaTextFromNewDocument) { + FPDF_CloseDocument(empty_doc); + } + ++TEST_F(FPDFDocEmbedderTest, GetPageAAction) { ++ ASSERT_TRUE(OpenDocument("get_page_aaction.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ EXPECT_TRUE(page); ++ ++ EXPECT_FALSE(FPDF_GetPageAAction(nullptr, FPDFPAGE_AACTION_OPEN)); ++ EXPECT_FALSE(FPDF_GetPageAAction(page, FPDFPAGE_AACTION_CLOSE)); ++ EXPECT_FALSE(FPDF_GetPageAAction(page, -1)); ++ EXPECT_FALSE(FPDF_GetPageAAction(page, 999)); ++ ++ FPDF_ACTION action = FPDF_GetPageAAction(page, FPDFPAGE_AACTION_OPEN); ++ EXPECT_EQ(static_cast(PDFACTION_EMBEDDEDGOTO), ++ FPDFAction_GetType(action)); ++ ++ const char kExpectedResult[] = "\\\\127.0.0.1\\c$\\Program Files\\test.exe"; ++ const unsigned long kExpectedLength = sizeof(kExpectedResult); ++ char buf[1024]; ++ ++ unsigned long bufsize = FPDFAction_GetFilePath(action, nullptr, 0); ++ EXPECT_EQ(kExpectedLength, bufsize); ++ EXPECT_EQ(kExpectedLength, FPDFAction_GetFilePath(action, buf, bufsize)); ++ EXPECT_STREQ(kExpectedResult, buf); ++ ++ UnloadPage(page); ++ ++ page = LoadPage(1); ++ EXPECT_TRUE(page); ++ EXPECT_FALSE(FPDF_GetPageAAction(page, -1)); ++ ++ UnloadPage(page); ++} ++ + TEST_F(FPDFDocEmbedderTest, NoPageLabels) { +- EXPECT_TRUE(OpenDocument("about_blank.pdf")); ++ ASSERT_TRUE(OpenDocument("about_blank.pdf")); + EXPECT_EQ(1, FPDF_GetPageCount(document())); + + ASSERT_EQ(0u, FPDF_GetPageLabel(document(), 0, nullptr, 0)); + } + + TEST_F(FPDFDocEmbedderTest, GetPageLabels) { +- EXPECT_TRUE(OpenDocument("page_labels.pdf")); ++ ASSERT_TRUE(OpenDocument("page_labels.pdf")); + EXPECT_EQ(7, FPDF_GetPageCount(document())); + + // We do not request labels, when use FPDFAvail_IsXXXAvail. +@@ -539,40 +757,26 @@ TEST_F(FPDFDocEmbedderTest, GetPageLabels) { + EXPECT_EQ(0u, FPDF_GetPageLabel(document(), -2, buf, sizeof(buf))); + EXPECT_EQ(0u, FPDF_GetPageLabel(document(), -1, buf, sizeof(buf))); + +- const wchar_t kExpectedPageLabel0[] = L"i"; + ASSERT_EQ(4u, FPDF_GetPageLabel(document(), 0, buf, sizeof(buf))); +- EXPECT_EQ(WideString(kExpectedPageLabel0), +- WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel0))); ++ EXPECT_EQ(L"i", GetPlatformWString(buf)); + +- const wchar_t kExpectedPageLabel1[] = L"ii"; + ASSERT_EQ(6u, FPDF_GetPageLabel(document(), 1, buf, sizeof(buf))); +- EXPECT_EQ(WideString(kExpectedPageLabel1), +- WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel1))); ++ EXPECT_EQ(L"ii", GetPlatformWString(buf)); + +- const wchar_t kExpectedPageLabel2[] = L"1"; + ASSERT_EQ(4u, FPDF_GetPageLabel(document(), 2, buf, sizeof(buf))); +- EXPECT_EQ(WideString(kExpectedPageLabel2), +- WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel2))); ++ EXPECT_EQ(L"1", GetPlatformWString(buf)); + +- const wchar_t kExpectedPageLabel3[] = L"2"; + ASSERT_EQ(4u, FPDF_GetPageLabel(document(), 3, buf, sizeof(buf))); +- EXPECT_EQ(WideString(kExpectedPageLabel3), +- WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel3))); ++ EXPECT_EQ(L"2", GetPlatformWString(buf)); + +- const wchar_t kExpectedPageLabel4[] = L"zzA"; + ASSERT_EQ(8u, FPDF_GetPageLabel(document(), 4, buf, sizeof(buf))); +- EXPECT_EQ(WideString(kExpectedPageLabel4), +- WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel4))); ++ EXPECT_EQ(L"zzA", GetPlatformWString(buf)); + +- const wchar_t kExpectedPageLabel5[] = L"zzB"; + ASSERT_EQ(8u, FPDF_GetPageLabel(document(), 5, buf, sizeof(buf))); +- EXPECT_EQ(WideString(kExpectedPageLabel5), +- WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel5))); ++ EXPECT_EQ(L"zzB", GetPlatformWString(buf)); + +- const wchar_t kExpectedPageLabel6[] = L""; + ASSERT_EQ(2u, FPDF_GetPageLabel(document(), 6, buf, sizeof(buf))); +- EXPECT_EQ(WideString(kExpectedPageLabel6), +- WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel6))); ++ EXPECT_EQ(L"", GetPlatformWString(buf)); + + ASSERT_EQ(0u, FPDF_GetPageLabel(document(), 7, buf, sizeof(buf))); + ASSERT_EQ(0u, FPDF_GetPageLabel(document(), 8, buf, sizeof(buf))); +@@ -580,7 +784,7 @@ TEST_F(FPDFDocEmbedderTest, GetPageLabels) { + + #ifdef PDF_ENABLE_XFA + TEST_F(FPDFDocEmbedderTest, GetXFALinks) { +- EXPECT_TRUE(OpenDocument("simple_xfa.pdf")); ++ ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + + ScopedFPDFPage page(FPDF_LoadPage(document(), 0)); + ASSERT_TRUE(page); +diff --git a/fpdfsdk/fpdf_doc_unittest.cpp b/fpdfsdk/fpdf_doc_unittest.cpp +index 2beadd4b2..6d456296f 100644 +--- a/fpdfsdk/fpdf_doc_unittest.cpp ++++ b/fpdfsdk/fpdf_doc_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,8 +7,7 @@ + #include + #include + +-#include "core/fpdfapi/page/cpdf_docpagedata.h" +-#include "core/fpdfapi/page/cpdf_pagemodule.h" ++#include "core/fpdfapi/page/test_with_page_module.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" +@@ -18,37 +17,26 @@ + #include "core/fpdfapi/parser/cpdf_parser.h" + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_string.h" +-#include "core/fpdfapi/render/cpdf_docrenderdata.h" ++#include "core/fpdfapi/parser/cpdf_test_document.h" + #include "core/fpdfdoc/cpdf_dest.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "public/cpp/fpdf_scopers.h" + #include "testing/fx_string_testhelpers.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" + +-class CPDF_TestDocument final : public CPDF_Document { +- public: +- CPDF_TestDocument() +- : CPDF_Document(pdfium::MakeUnique(), +- pdfium::MakeUnique()) {} +- +- void SetRoot(CPDF_Dictionary* root) { m_pRootDict.Reset(root); } +- CPDF_IndirectObjectHolder* GetHolder() { return this; } +-}; +- +-class PDFDocTest : public testing::Test { ++class PDFDocTest : public TestWithPageModule { + public: + struct DictObjInfo { + uint32_t num; +- CPDF_Dictionary* obj; ++ RetainPtr obj; + }; + + void SetUp() override { +- CPDF_PageModule::Create(); +- auto pTestDoc = pdfium::MakeUnique(); +- m_pIndirectObjs = pTestDoc->GetHolder(); +- m_pRootObj.Reset(m_pIndirectObjs->NewIndirect()); +- pTestDoc->SetRoot(m_pRootObj.Get()); ++ TestWithPageModule::SetUp(); ++ auto pTestDoc = std::make_unique(); ++ m_pIndirectObjs = pTestDoc.get(); ++ m_pRootObj = m_pIndirectObjs->NewIndirect(); ++ pTestDoc->SetRoot(m_pRootObj); + m_pDoc.reset(FPDFDocumentFromCPDFDocument(pTestDoc.release())); + } + +@@ -56,14 +44,13 @@ class PDFDocTest : public testing::Test { + m_pRootObj = nullptr; + m_pIndirectObjs = nullptr; + m_pDoc.reset(); +- CPDF_PageModule::Destroy(); ++ TestWithPageModule::TearDown(); + } + + std::vector CreateDictObjs(int num) { + std::vector info; + for (int i = 0; i < num; ++i) { +- // Objects created will be released by the document. +- CPDF_Dictionary* obj = m_pIndirectObjs->NewIndirect(); ++ auto obj = m_pIndirectObjs->NewIndirect(); + info.push_back({obj->GetObjNum(), obj}); + } + return info; +@@ -79,62 +66,62 @@ TEST_F(PDFDocTest, FindBookmark) { + { + // No bookmark information. + ScopedFPDFWideString title = GetFPDFWideString(L""); +- EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get())); ++ EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get())); + + title = GetFPDFWideString(L"Preface"); +- EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get())); ++ EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get())); + } + { + // Empty bookmark tree. + m_pRootObj->SetNewFor("Outlines"); + ScopedFPDFWideString title = GetFPDFWideString(L""); +- EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get())); ++ EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get())); + + title = GetFPDFWideString(L"Preface"); +- EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get())); ++ EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get())); + } + { + // Check on a regular bookmark tree. + auto bookmarks = CreateDictObjs(3); + + bookmarks[1].obj->SetNewFor("Title", L"Chapter 1"); +- bookmarks[1].obj->SetNewFor("Parent", m_pIndirectObjs.Get(), ++ bookmarks[1].obj->SetNewFor("Parent", m_pIndirectObjs, + bookmarks[0].num); +- bookmarks[1].obj->SetNewFor("Next", m_pIndirectObjs.Get(), ++ bookmarks[1].obj->SetNewFor("Next", m_pIndirectObjs, + bookmarks[2].num); + + bookmarks[2].obj->SetNewFor("Title", L"Chapter 2"); +- bookmarks[2].obj->SetNewFor("Parent", m_pIndirectObjs.Get(), ++ bookmarks[2].obj->SetNewFor("Parent", m_pIndirectObjs, + bookmarks[0].num); +- bookmarks[2].obj->SetNewFor("Prev", m_pIndirectObjs.Get(), ++ bookmarks[2].obj->SetNewFor("Prev", m_pIndirectObjs, + bookmarks[1].num); + + bookmarks[0].obj->SetNewFor("Type", "Outlines"); + bookmarks[0].obj->SetNewFor("Count", 2); +- bookmarks[0].obj->SetNewFor("First", m_pIndirectObjs.Get(), ++ bookmarks[0].obj->SetNewFor("First", m_pIndirectObjs, + bookmarks[1].num); +- bookmarks[0].obj->SetNewFor("Last", m_pIndirectObjs.Get(), ++ bookmarks[0].obj->SetNewFor("Last", m_pIndirectObjs, + bookmarks[2].num); + +- m_pRootObj->SetNewFor("Outlines", m_pIndirectObjs.Get(), ++ m_pRootObj->SetNewFor("Outlines", m_pIndirectObjs, + bookmarks[0].num); + + // Title with no match. + ScopedFPDFWideString title = GetFPDFWideString(L"Chapter 3"); +- EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get())); ++ EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get())); + + // Title with partial match only. + title = GetFPDFWideString(L"Chapter"); +- EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get())); ++ EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get())); + + // Title with a match. + title = GetFPDFWideString(L"Chapter 2"); +- EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj), ++ EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj.Get()), + FPDFBookmark_Find(m_pDoc.get(), title.get())); + + // Title match is case insensitive. + title = GetFPDFWideString(L"cHaPter 2"); +- EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj), ++ EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj.Get()), + FPDFBookmark_Find(m_pDoc.get(), title.get())); + } + { +@@ -142,34 +129,34 @@ TEST_F(PDFDocTest, FindBookmark) { + auto bookmarks = CreateDictObjs(3); + + bookmarks[1].obj->SetNewFor("Title", L"Chapter 1"); +- bookmarks[1].obj->SetNewFor("Parent", m_pIndirectObjs.Get(), ++ bookmarks[1].obj->SetNewFor("Parent", m_pIndirectObjs, + bookmarks[0].num); +- bookmarks[1].obj->SetNewFor("First", m_pIndirectObjs.Get(), ++ bookmarks[1].obj->SetNewFor("First", m_pIndirectObjs, + bookmarks[2].num); + + bookmarks[2].obj->SetNewFor("Title", L"Chapter 2"); +- bookmarks[2].obj->SetNewFor("Parent", m_pIndirectObjs.Get(), ++ bookmarks[2].obj->SetNewFor("Parent", m_pIndirectObjs, + bookmarks[1].num); +- bookmarks[2].obj->SetNewFor("First", m_pIndirectObjs.Get(), ++ bookmarks[2].obj->SetNewFor("First", m_pIndirectObjs, + bookmarks[1].num); + + bookmarks[0].obj->SetNewFor("Type", "Outlines"); + bookmarks[0].obj->SetNewFor("Count", 2); +- bookmarks[0].obj->SetNewFor("First", m_pIndirectObjs.Get(), ++ bookmarks[0].obj->SetNewFor("First", m_pIndirectObjs, + bookmarks[1].num); +- bookmarks[0].obj->SetNewFor("Last", m_pIndirectObjs.Get(), ++ bookmarks[0].obj->SetNewFor("Last", m_pIndirectObjs, + bookmarks[2].num); + +- m_pRootObj->SetNewFor("Outlines", m_pIndirectObjs.Get(), ++ m_pRootObj->SetNewFor("Outlines", m_pIndirectObjs, + bookmarks[0].num); + + // Title with no match. + ScopedFPDFWideString title = GetFPDFWideString(L"Chapter 3"); +- EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get())); ++ EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get())); + + // Title with a match. + title = GetFPDFWideString(L"Chapter 2"); +- EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj), ++ EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj.Get()), + FPDFBookmark_Find(m_pDoc.get(), title.get())); + } + { +@@ -177,51 +164,51 @@ TEST_F(PDFDocTest, FindBookmark) { + auto bookmarks = CreateDictObjs(4); + + bookmarks[1].obj->SetNewFor("Title", L"Chapter 1"); +- bookmarks[1].obj->SetNewFor("Parent", m_pIndirectObjs.Get(), ++ bookmarks[1].obj->SetNewFor("Parent", m_pIndirectObjs, + bookmarks[0].num); +- bookmarks[1].obj->SetNewFor("Next", m_pIndirectObjs.Get(), ++ bookmarks[1].obj->SetNewFor("Next", m_pIndirectObjs, + bookmarks[2].num); + + bookmarks[2].obj->SetNewFor("Title", L"Chapter 2"); +- bookmarks[2].obj->SetNewFor("Parent", m_pIndirectObjs.Get(), ++ bookmarks[2].obj->SetNewFor("Parent", m_pIndirectObjs, + bookmarks[0].num); +- bookmarks[2].obj->SetNewFor("Next", m_pIndirectObjs.Get(), ++ bookmarks[2].obj->SetNewFor("Next", m_pIndirectObjs, + bookmarks[3].num); + + bookmarks[3].obj->SetNewFor("Title", L"Chapter 3"); +- bookmarks[3].obj->SetNewFor("Parent", m_pIndirectObjs.Get(), ++ bookmarks[3].obj->SetNewFor("Parent", m_pIndirectObjs, + bookmarks[0].num); +- bookmarks[3].obj->SetNewFor("Next", m_pIndirectObjs.Get(), ++ bookmarks[3].obj->SetNewFor("Next", m_pIndirectObjs, + bookmarks[1].num); + + bookmarks[0].obj->SetNewFor("Type", "Outlines"); + bookmarks[0].obj->SetNewFor("Count", 2); +- bookmarks[0].obj->SetNewFor("First", m_pIndirectObjs.Get(), ++ bookmarks[0].obj->SetNewFor("First", m_pIndirectObjs, + bookmarks[1].num); +- bookmarks[0].obj->SetNewFor("Last", m_pIndirectObjs.Get(), ++ bookmarks[0].obj->SetNewFor("Last", m_pIndirectObjs, + bookmarks[2].num); + +- m_pRootObj->SetNewFor("Outlines", m_pIndirectObjs.Get(), ++ m_pRootObj->SetNewFor("Outlines", m_pIndirectObjs, + bookmarks[0].num); + + // Title with no match. + ScopedFPDFWideString title = GetFPDFWideString(L"Chapter 8"); +- EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get())); ++ EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get())); + + // Title with a match. + title = GetFPDFWideString(L"Chapter 3"); +- EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[3].obj), ++ EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[3].obj.Get()), + FPDFBookmark_Find(m_pDoc.get(), title.get())); + } + } + + TEST_F(PDFDocTest, GetLocationInPage) { + auto array = pdfium::MakeRetain(); +- array->AddNew(0); // Page Index. +- array->AddNew("XYZ"); +- array->AddNew(4); // X +- array->AddNew(5); // Y +- array->AddNew(6); // Zoom. ++ array->AppendNew(0); // Page Index. ++ array->AppendNew("XYZ"); ++ array->AppendNew(4); // X ++ array->AppendNew(5); // Y ++ array->AppendNew(6); // Zoom. + + FPDF_BOOL hasX; + FPDF_BOOL hasY; +diff --git a/fpdfsdk/fpdf_edit_embeddertest.cpp b/fpdfsdk/fpdf_edit_embeddertest.cpp +index b6cc84c19..670892a98 100644 +--- a/fpdfsdk/fpdf_edit_embeddertest.cpp ++++ b/fpdfsdk/fpdf_edit_embeddertest.cpp +@@ -1,7 +1,8 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include + #include + #include + #include +@@ -9,7 +10,6 @@ + + #include "build/build_config.h" + #include "core/fpdfapi/font/cpdf_font.h" +-#include "core/fpdfapi/page/cpdf_formobject.h" + #include "core/fpdfapi/page/cpdf_page.h" + #include "core/fpdfapi/page/cpdf_pageobject.h" + #include "core/fpdfapi/parser/cpdf_array.h" +@@ -17,7 +17,9 @@ + #include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" ++#include "core/fxcrt/fx_codepage.h" + #include "core/fxcrt/fx_system.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" + #include "core/fxge/fx_font.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "public/cpp/fpdf_scopers.h" +@@ -25,17 +27,67 @@ + #include "public/fpdf_edit.h" + #include "public/fpdfview.h" + #include "testing/embedder_test.h" ++#include "testing/embedder_test_constants.h" + #include "testing/fx_string_testhelpers.h" + #include "testing/gmock/include/gmock/gmock-matchers.h" + #include "testing/gtest/include/gtest/gtest.h" ++#include "testing/utils/file_util.h" + #include "testing/utils/hash.h" ++#include "testing/utils/path_service.h" ++#include "third_party/base/check.h" ++ ++using pdfium::HelloWorldChecksum; ++using testing::HasSubstr; ++using testing::Not; ++using testing::UnorderedElementsAreArray; ++ ++namespace { ++ ++const char kAllRemovedChecksum[] = "eee4600ac08b458ac7ac2320e225674c"; ++ ++const wchar_t kBottomText[] = L"I'm at the bottom of the page"; ++ ++const char* BottomTextChecksum() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "81636489006a31fcb00cf29efcdf7909"; ++#endif ++ return "891dcb6e914c8360998055f1f47c9727"; ++} ++ ++const char* FirstRemovedChecksum() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "a1dc2812692fcc7ee4f01ca77435df9d"; ++#endif ++ return "e1477dc3b5b3b9c560814c4d1135a02b"; ++} ++ ++const wchar_t kLoadedFontText[] = L"I am testing my loaded font, WEE."; ++ ++const char* LoadedFontTextChecksum() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "d58570cc045dfb818b92cbabbd1a364c"; ++#if BUILDFLAG(IS_APPLE) ++ return "0f3e4a7d71f9e7eb8a1a0d69403b9848"; ++#else ++ return "d58570cc045dfb818b92cbabbd1a364c"; ++#endif ++} ++ ++const char kRedRectangleChecksum[] = "66d02eaa6181e2c069ce2ea99beda497"; ++ ++// In embedded_images.pdf. ++const char kEmbeddedImage33Checksum[] = "cb3637934bb3b95a6e4ae1ea9eb9e56e"; ++ ++} // namespace + + class FPDFEditEmbedderTest : public EmbedderTest { + protected: + FPDF_DOCUMENT CreateNewDocument() { +- document_ = FPDF_CreateNewDocument(); +- cpdf_doc_ = CPDFDocumentFromFPDFDocument(document_); +- return document_; ++ CreateEmptyDocumentWithoutFormFillEnvironment(); ++ cpdf_doc_ = CPDFDocumentFromFPDFDocument(document()); ++ return document(); + } + + void CheckFontDescriptor(const CPDF_Dictionary* font_dict, +@@ -43,11 +95,13 @@ class FPDFEditEmbedderTest : public EmbedderTest { + bool bold, + bool italic, + pdfium::span span) { +- const CPDF_Dictionary* font_desc = font_dict->GetDictFor("FontDescriptor"); ++ RetainPtr font_desc = ++ font_dict->GetDictFor("FontDescriptor"); + ASSERT_TRUE(font_desc); +- EXPECT_EQ("FontDescriptor", font_desc->GetStringFor("Type")); +- EXPECT_EQ(font_dict->GetStringFor("BaseFont"), +- font_desc->GetStringFor("FontName")); ++ EXPECT_EQ("FontDescriptor", font_desc->GetNameFor("Type")); ++ ByteString font_name = font_desc->GetNameFor("FontName"); ++ EXPECT_FALSE(font_name.IsEmpty()); ++ EXPECT_EQ(font_dict->GetNameFor("BaseFont"), font_name); + + // Check that the font descriptor has the required keys according to spec + // 1.7 Table 5.19 +@@ -59,7 +113,7 @@ class FPDFEditEmbedderTest : public EmbedderTest { + EXPECT_TRUE(FontStyleIsNonSymbolic(font_flags)); + ASSERT_TRUE(font_desc->KeyExist("FontBBox")); + +- const CPDF_Array* fontBBox = font_desc->GetArrayFor("FontBBox"); ++ RetainPtr fontBBox = font_desc->GetArrayFor("FontBBox"); + ASSERT_TRUE(fontBBox); + EXPECT_EQ(4u, fontBBox->size()); + // Check that the coordinates are in the preferred order according to spec +@@ -86,11 +140,10 @@ class FPDFEditEmbedderTest : public EmbedderTest { + // Check that the font stream is the one that was provided + ASSERT_EQ(span.size(), streamAcc->GetSize()); + if (font_type == FPDF_FONT_TRUETYPE) { +- ASSERT_EQ(static_cast(span.size()), +- streamAcc->GetDict()->GetIntegerFor("Length1")); ++ ASSERT_EQ(static_cast(span.size()), streamAcc->GetLength1ForTest()); + } + +- const uint8_t* stream_data = streamAcc->GetData(); ++ pdfium::span stream_data = streamAcc->GetSpan(); + for (size_t j = 0; j < span.size(); j++) + EXPECT_EQ(span[j], stream_data[j]) << " at byte " << j; + } +@@ -104,17 +157,17 @@ class FPDFEditEmbedderTest : public EmbedderTest { + int num_cids_checked = 0; + int cur_cid = 0; + for (size_t idx = 0; idx < widths_array->size(); idx++) { +- int cid = widths_array->GetNumberAt(idx); ++ int cid = widths_array->GetFloatAt(idx); + EXPECT_GE(cid, cur_cid); + ASSERT_FALSE(++idx == widths_array->size()); +- const CPDF_Object* next = widths_array->GetObjectAt(idx); ++ RetainPtr next = widths_array->GetObjectAt(idx); + if (next->IsArray()) { + // We are in the c [w1 w2 ...] case + const CPDF_Array* arr = next->AsArray(); + int cnt = static_cast(arr->size()); + size_t inner_idx = 0; + for (cur_cid = cid; cur_cid < cid + cnt; cur_cid++) { +- uint32_t width = arr->GetNumberAt(inner_idx++); ++ int width = arr->GetFloatAt(inner_idx++); + EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid)) + << " at cid " << cur_cid; + } +@@ -125,7 +178,7 @@ class FPDFEditEmbedderTest : public EmbedderTest { + ASSERT_TRUE(next->IsNumber()); + int last_cid = next->AsNumber()->GetInteger(); + ASSERT_FALSE(++idx == widths_array->size()); +- uint32_t width = widths_array->GetNumberAt(idx); ++ int width = widths_array->GetFloatAt(idx); + for (cur_cid = cid; cur_cid <= last_cid; cur_cid++) { + EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid)) + << " at cid " << cur_cid; +@@ -133,7 +186,7 @@ class FPDFEditEmbedderTest : public EmbedderTest { + num_cids_checked += last_cid - cid + 1; + } + // Make sure we have a good amount of cids described +- EXPECT_GT(num_cids_checked, 900); ++ EXPECT_GT(num_cids_checked, 200); + } + CPDF_Document* cpdf_doc() { return cpdf_doc_; } + +@@ -157,36 +210,105 @@ const char kExpectedPDF[] = + "endobj\r\n" + "4 0 obj\r\n" + "<>>>" ++ "/Resources<<>>" + "/Rotate 0/Type/Page" + ">>\r\n" + "endobj\r\n" +- "5 0 obj\r\n" +- "<>\r\n" +- "endobj\r\n" + "xref\r\n" +- "0 6\r\n" ++ "0 5\r\n" + "0000000000 65535 f\r\n" + "0000000017 00000 n\r\n" + "0000000066 00000 n\r\n" + "0000000122 00000 n\r\n" + "0000000192 00000 n\r\n" +- "0000000311 00000 n\r\n" + "trailer\r\n" + "<<\r\n" + "/Root 1 0 R\r\n" + "/Info 3 0 R\r\n" +- "/Size 6/ID\\[<.*><.*>\\]>>\r\n" ++ "/Size 5/ID\\[<.*><.*>\\]>>\r\n" + "startxref\r\n" +- "354\r\n" ++ "285\r\n" + "%%EOF\r\n"; + + } // namespace + ++TEST_F(FPDFEditEmbedderTest, EmbedNotoSansSCFont) { ++ CreateEmptyDocument(); ++ ScopedFPDFPage page(FPDFPage_New(document(), 0, 400, 400)); ++ std::string font_path; ++ ASSERT_TRUE(PathService::GetThirdPartyFilePath( ++ "NotoSansCJK/NotoSansSC-Regular.subset.otf", &font_path)); ++ ++ size_t file_length = 0; ++ std::unique_ptr font_data = ++ GetFileContents(font_path.c_str(), &file_length); ++ ASSERT_TRUE(font_data); ++ ++ ScopedFPDFFont font(FPDFText_LoadFont( ++ document(), reinterpret_cast(font_data.get()), ++ file_length, FPDF_FONT_TRUETYPE, /*cid=*/true)); ++ FPDF_PAGEOBJECT text_object = ++ FPDFPageObj_CreateTextObj(document(), font.get(), 20.0f); ++ EXPECT_TRUE(text_object); ++ ++ // Test the characters which are either mapped to one single unicode or ++ // multiple unicodes in the embedded font. ++ ScopedFPDFWideString text = GetFPDFWideString(L"这是第一å¥ã€‚ 这是第二行。"); ++ EXPECT_TRUE(FPDFText_SetText(text_object, text.get())); ++ ++ FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 50, 200); ++ FPDFPage_InsertObject(page.get(), text_object); ++ EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); ++ ++ const char kChecksum[] = "9a31fb87d1c6d2346bba22d1196041cd"; ++ ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); ++ CompareBitmap(page_bitmap.get(), 400, 400, kChecksum); ++ ++ ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ VerifySavedDocument(400, 400, kChecksum); ++} ++ ++TEST_F(FPDFEditEmbedderTest, EmbedNotoSansSCFontWithCharcodes) { ++ CreateEmptyDocument(); ++ ScopedFPDFPage page(FPDFPage_New(document(), 0, 400, 400)); ++ std::string font_path; ++ ASSERT_TRUE(PathService::GetThirdPartyFilePath( ++ "NotoSansCJK/NotoSansSC-Regular.subset.otf", &font_path)); ++ ++ size_t file_length = 0; ++ std::unique_ptr font_data = ++ GetFileContents(font_path.c_str(), &file_length); ++ ASSERT_TRUE(font_data); ++ ++ ScopedFPDFFont font(FPDFText_LoadFont( ++ document(), reinterpret_cast(font_data.get()), ++ file_length, FPDF_FONT_TRUETYPE, /*cid=*/true)); ++ FPDF_PAGEOBJECT text_object = ++ FPDFPageObj_CreateTextObj(document(), font.get(), 20.0f); ++ EXPECT_TRUE(text_object); ++ ++ // Same as `text` in the EmbedNotoSansSCFont test case above. ++ const std::vector charcodes = {9, 6, 7, 3, 5, 2, 1, ++ 9, 6, 7, 4, 8, 2}; ++ EXPECT_TRUE( ++ FPDFText_SetCharcodes(text_object, charcodes.data(), charcodes.size())); ++ ++ FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 50, 200); ++ FPDFPage_InsertObject(page.get(), text_object); ++ EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); ++ ++ const char kChecksum[] = "9a31fb87d1c6d2346bba22d1196041cd"; ++ ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); ++ CompareBitmap(page_bitmap.get(), 400, 400, kChecksum); ++ ++ ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ VerifySavedDocument(400, 400, kChecksum); ++} ++ + TEST_F(FPDFEditEmbedderTest, EmptyCreation) { +- EXPECT_TRUE(CreateEmptyDocument()); ++ CreateEmptyDocument(); + FPDF_PAGE page = FPDFPage_New(document(), 0, 640.0, 480.0); +- EXPECT_NE(nullptr, page); ++ EXPECT_TRUE(page); + // The FPDFPage_GenerateContent call should do nothing. + EXPECT_TRUE(FPDFPage_GenerateContent(page)); + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); +@@ -198,16 +320,16 @@ TEST_F(FPDFEditEmbedderTest, EmptyCreation) { + + // Regression test for https://crbug.com/667012 + TEST_F(FPDFEditEmbedderTest, RasterizePDF) { +- const char kAllBlackMd5sum[] = "5708fc5c4a8bd0abde99c8e8f0390615"; ++ const char kAllBlackChecksum[] = "5708fc5c4a8bd0abde99c8e8f0390615"; + + // Get the bitmap for the original document. + ScopedFPDFBitmap orig_bitmap; + { +- EXPECT_TRUE(OpenDocument("black.pdf")); ++ ASSERT_TRUE(OpenDocument("black.pdf")); + FPDF_PAGE orig_page = LoadPage(0); + ASSERT_TRUE(orig_page); + orig_bitmap = RenderLoadedPage(orig_page); +- CompareBitmap(orig_bitmap.get(), 612, 792, kAllBlackMd5sum); ++ CompareBitmap(orig_bitmap.get(), 612, 792, kAllBlackChecksum); + UnloadPage(orig_page); + } + +@@ -221,7 +343,8 @@ TEST_F(FPDFEditEmbedderTest, RasterizePDF) { + FPDF_PAGEOBJECT temp_img = FPDFPageObj_NewImageObj(temp_doc); + EXPECT_TRUE( + FPDFImageObj_SetBitmap(&temp_page, 1, temp_img, orig_bitmap.get())); +- EXPECT_TRUE(FPDFImageObj_SetMatrix(temp_img, 612, 0, 0, 792, 0, 0)); ++ static constexpr FS_MATRIX kLetterScaleMatrix{612, 0, 0, 792, 0, 0}; ++ EXPECT_TRUE(FPDFPageObj_SetMatrix(temp_img, &kLetterScaleMatrix)); + FPDFPage_InsertObject(temp_page, temp_img); + EXPECT_TRUE(FPDFPage_GenerateContent(temp_page)); + EXPECT_TRUE(FPDF_SaveAsCopy(temp_doc, this, 0)); +@@ -232,16 +355,10 @@ TEST_F(FPDFEditEmbedderTest, RasterizePDF) { + // Get the generated content. Make sure it is at least as big as the original + // PDF. + EXPECT_GT(GetString().size(), 923u); +- VerifySavedDocument(612, 792, kAllBlackMd5sum); ++ VerifySavedDocument(612, 792, kAllBlackChecksum); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_AddPaths DISABLED_AddPaths +-#else +-#define MAYBE_AddPaths AddPaths +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_AddPaths) { ++TEST_F(FPDFEditEmbedderTest, AddPaths) { + // Start with a blank page + FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792); + ASSERT_TRUE(page); +@@ -263,13 +380,11 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddPaths) { + EXPECT_EQ(FPDF_FILLMODE_ALTERNATE, fillmode); + EXPECT_FALSE(stroke); + +- static const FS_MATRIX kMatrix = {1, 2, 3, 4, 5, 6}; +- EXPECT_FALSE(FPDFPath_SetMatrix(nullptr, &kMatrix)); +- EXPECT_TRUE(FPDFPath_SetMatrix(red_rect, &kMatrix)); ++ static constexpr FS_MATRIX kMatrix = {1, 2, 3, 4, 5, 6}; ++ EXPECT_TRUE(FPDFPageObj_SetMatrix(red_rect, &kMatrix)); + + FS_MATRIX matrix; +- EXPECT_FALSE(FPDFPath_GetMatrix(nullptr, &matrix)); +- EXPECT_TRUE(FPDFPath_GetMatrix(red_rect, &matrix)); ++ EXPECT_TRUE(FPDFPageObj_GetMatrix(red_rect, &matrix)); + EXPECT_FLOAT_EQ(1.0f, matrix.a); + EXPECT_FLOAT_EQ(2.0f, matrix.b); + EXPECT_FLOAT_EQ(3.0f, matrix.c); +@@ -279,13 +394,12 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddPaths) { + + // Set back the identity matrix. + matrix = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}; +- EXPECT_TRUE(FPDFPath_SetMatrix(red_rect, &matrix)); ++ EXPECT_TRUE(FPDFPageObj_SetMatrix(red_rect, &matrix)); + + FPDFPage_InsertObject(page, red_rect); + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 612, 792, +- "66d02eaa6181e2c069ce2ea99beda497"); ++ CompareBitmap(page_bitmap.get(), 612, 792, kRedRectangleChecksum); + } + + // Now add to that a green rectangle with some medium alpha +@@ -306,8 +420,8 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddPaths) { + EXPECT_EQ(0u, B); + EXPECT_EQ(128u, A); + +- // Make sure the path has 5 points (1 FXPT_TYPE::MoveTo and 4 +- // FXPT_TYPE::LineTo). ++ // Make sure the path has 5 points (1 CFX_Path::Point::Type::kMove and 4 ++ // CFX_Path::Point::Type::kLine). + ASSERT_EQ(5, FPDFPath_CountSegments(green_rect)); + // Verify actual coordinates. + FPDF_PATHSEGMENT segment = FPDFPath_GetPathSegment(green_rect, 0); +@@ -359,8 +473,8 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddPaths) { + EXPECT_TRUE(FPDFPath_LineTo(black_path, 300, 100)); + EXPECT_TRUE(FPDFPath_Close(black_path)); + +- // Make sure the path has 3 points (1 FXPT_TYPE::MoveTo and 2 +- // FXPT_TYPE::LineTo). ++ // Make sure the path has 3 points (1 CFX_Path::Point::Type::kMove and 2 ++ // CFX_Path::Point::Type::kLine). + ASSERT_EQ(3, FPDFPath_CountSegments(black_path)); + // Verify actual coordinates. + segment = FPDFPath_GetPathSegment(black_path, 0); +@@ -382,7 +496,7 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddPaths) { + EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment)); + EXPECT_TRUE(FPDFPathSegment_GetClose(segment)); + // Make sure out of bounds index access fails properly. +- EXPECT_EQ(nullptr, FPDFPath_GetPathSegment(black_path, 3)); ++ EXPECT_FALSE(FPDFPath_GetPathSegment(black_path, 3)); + + FPDFPage_InsertObject(page, black_path); + { +@@ -402,10 +516,14 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddPaths) { + EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 375, 330, 390, 360, 400, 400)); + EXPECT_TRUE(FPDFPath_Close(blue_path)); + FPDFPage_InsertObject(page, blue_path); +- const char kLastMD5[] = "9823e1a21bd9b72b6a442ba4f12af946"; ++ const char* last_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "ed14c60702b1489c597c7d46ece7f86d"; ++ return "9823e1a21bd9b72b6a442ba4f12af946"; ++ }(); + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 612, 792, kLastMD5); ++ CompareBitmap(page_bitmap.get(), 612, 792, last_checksum); + } + + // Now save the result, closing the page and document +@@ -414,12 +532,12 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddPaths) { + FPDF_ClosePage(page); + + // Render the saved result +- VerifySavedDocument(612, 792, kLastMD5); ++ VerifySavedDocument(612, 792, last_checksum); + } + + TEST_F(FPDFEditEmbedderTest, ClipPath) { + // Load document with a clipped rectangle. +- EXPECT_TRUE(OpenDocument("clip_path.pdf")); ++ ASSERT_TRUE(OpenDocument("clip_path.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -515,7 +633,7 @@ TEST_F(FPDFEditEmbedderTest, ClipPath) { + + TEST_F(FPDFEditEmbedderTest, BUG_1399) { + // Load document with a clipped rectangle. +- EXPECT_TRUE(OpenDocument("bug_1399.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_1399.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -552,15 +670,39 @@ TEST_F(FPDFEditEmbedderTest, BUG_1399) { + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_SetText DISABLED_SetText +-#else +-#define MAYBE_SetText SetText +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_SetText) { ++TEST_F(FPDFEditEmbedderTest, BUG_1549) { ++ static const char kOriginalChecksum[] = "126366fb95e6caf8ea196780075b22b2"; ++ static const char kRemovedChecksum[] = "6ec2f27531927882624b37bc7d8e12f4"; ++ ++ ASSERT_TRUE(OpenDocument("bug_1549.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ++ { ++ ScopedFPDFBitmap bitmap = RenderLoadedPage(page); ++ CompareBitmap(bitmap.get(), 100, 150, kOriginalChecksum); ++ ++ ScopedFPDFPageObject obj(FPDFPage_GetObject(page, 0)); ++ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj.get())); ++ ASSERT_TRUE(FPDFPage_RemoveObject(page, obj.get())); ++ } ++ ++ ASSERT_TRUE(FPDFPage_GenerateContent(page)); ++ ++ { ++ ScopedFPDFBitmap bitmap = RenderLoadedPage(page); ++ CompareBitmap(bitmap.get(), 100, 150, kRemovedChecksum); ++ } ++ ++ ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ UnloadPage(page); ++ ++ // TODO(crbug.com/pdfium/1549): Should be `kRemovedChecksum`. ++ VerifySavedDocument(100, 150, "4f9889cd5993db20f1ab37d677ac8d26"); ++} ++ ++TEST_F(FPDFEditEmbedderTest, SetText) { + // Load document with some text. +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -574,16 +716,17 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_SetText) { + // Verify the "Hello, world!" text is gone and "Changed for SetText test" is + // now displayed. + ASSERT_EQ(2, FPDFPage_CountObjects(page)); +-#if defined(OS_MACOSX) +- const char kChangedMD5[] = "94c1e7a5af7dd9d77dc2223b1091acb7"; +-#elif defined(OS_WIN) +- const char kChangedMD5[] = "3137fdb27962671f5c3963a5e965eff5"; +-#else +- const char kChangedMD5[] = "a0c4ea6620772991f66bf7130379b08a"; ++ ++ const char* changed_checksum = []() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "b720e83476fd6819d47c533f1f43c728"; + #endif ++ return "9a85b9354a69c61772ed24151c140f46"; ++ }(); + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 200, kChangedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, changed_checksum); + } + + // Now save the result. +@@ -595,63 +738,386 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_SetText) { + // Re-open the file and check the changes were kept in the saved .pdf. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + EXPECT_EQ(2, FPDFPage_CountObjects(saved_page)); + { + ScopedFPDFBitmap page_bitmap = RenderPage(saved_page); +- CompareBitmap(page_bitmap.get(), 200, 200, kChangedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, changed_checksum); + } + + CloseSavedPage(saved_page); + CloseSavedDocument(); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_RemovePageObject DISABLED_RemovePageObject +-#else +-#define MAYBE_RemovePageObject RemovePageObject ++TEST_F(FPDFEditEmbedderTest, SetCharcodesBadParams) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ ASSERT_EQ(2, FPDFPage_CountObjects(page)); ++ FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0); ++ ASSERT_TRUE(page_object); ++ ++ const uint32_t kDummyValue = 42; ++ EXPECT_FALSE(FPDFText_SetCharcodes(nullptr, nullptr, 0)); ++ EXPECT_FALSE(FPDFText_SetCharcodes(nullptr, nullptr, 1)); ++ EXPECT_FALSE(FPDFText_SetCharcodes(nullptr, &kDummyValue, 0)); ++ EXPECT_FALSE(FPDFText_SetCharcodes(nullptr, &kDummyValue, 1)); ++ EXPECT_FALSE(FPDFText_SetCharcodes(page_object, nullptr, 1)); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditEmbedderTest, SetTextKeepClippingPath) { ++ // Load document with some text, with parts clipped. ++ ASSERT_TRUE(OpenDocument("bug_1558.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ const char* original_checksum = []() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "ae7a25c85e0e2dd0c5cb9dd5cd37f6df"; ++#endif ++ return "7af7fe5b281298261eb66ac2d22f5054"; ++ }(); ++ { ++ // When opened before any editing and saving, the clipping path is rendered. ++ ScopedFPDFBitmap original_bitmap = RenderPage(page); ++ CompareBitmap(original_bitmap.get(), 200, 200, original_checksum); ++ } ++ ++ // "Change" the text in the objects to their current values to force them to ++ // regenerate when saving. ++ { ++ ScopedFPDFTextPage text_page(FPDFText_LoadPage(page)); ++ ASSERT_TRUE(text_page); ++ const int obj_count = FPDFPage_CountObjects(page); ++ ASSERT_EQ(2, obj_count); ++ for (int i = 0; i < obj_count; ++i) { ++ FPDF_PAGEOBJECT text_obj = FPDFPage_GetObject(page, i); ++ ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_obj)); ++ unsigned long size = ++ FPDFTextObj_GetText(text_obj, text_page.get(), ++ /*buffer=*/nullptr, /*length=*/0); ++ ASSERT_GT(size, 0u); ++ std::vector buffer = GetFPDFWideStringBuffer(size); ++ ASSERT_EQ(size, FPDFTextObj_GetText(text_obj, text_page.get(), ++ buffer.data(), size)); ++ EXPECT_TRUE(FPDFText_SetText(text_obj, buffer.data())); ++ } ++ } ++ ++ { ++ // After editing but before saving, the clipping path is retained. ++ ScopedFPDFBitmap edited_bitmap = RenderPage(page); ++ CompareBitmap(edited_bitmap.get(), 200, 200, original_checksum); ++ } ++ ++ // Save the file. ++ EXPECT_TRUE(FPDFPage_GenerateContent(page)); ++ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ UnloadPage(page); ++ ++ // Open the saved copy and render it. ++ ASSERT_TRUE(OpenSavedDocument()); ++ FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); ++ ++ { ++ ScopedFPDFBitmap saved_bitmap = RenderSavedPage(saved_page); ++ CompareBitmap(saved_bitmap.get(), 200, 200, original_checksum); ++ } ++ ++ CloseSavedPage(saved_page); ++ CloseSavedDocument(); ++} ++ ++TEST_F(FPDFEditEmbedderTest, BUG_1574) { ++ // Load document with some text within a clipping path. ++ ASSERT_TRUE(OpenDocument("bug_1574.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ const char* original_checksum = []() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "1226bc2b8072622eb28f52321876e814"; + #endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_RemovePageObject) { ++ return "c5241eef60b9eac68ed1f2a5fd002703"; ++ }(); ++ { ++ // When opened before any editing and saving, the text object is rendered. ++ ScopedFPDFBitmap original_bitmap = RenderPage(page); ++ CompareBitmap(original_bitmap.get(), 200, 300, original_checksum); ++ } ++ ++ // "Change" the text in the objects to their current values to force them to ++ // regenerate when saving. ++ { ++ ScopedFPDFTextPage text_page(FPDFText_LoadPage(page)); ++ ASSERT_TRUE(text_page); ++ ++ ASSERT_EQ(2, FPDFPage_CountObjects(page)); ++ FPDF_PAGEOBJECT text_obj = FPDFPage_GetObject(page, 1); ++ ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_obj)); ++ ++ unsigned long size = FPDFTextObj_GetText(text_obj, text_page.get(), ++ /*buffer=*/nullptr, /*length=*/0); ++ ASSERT_GT(size, 0u); ++ std::vector buffer = GetFPDFWideStringBuffer(size); ++ ASSERT_EQ(size, FPDFTextObj_GetText(text_obj, text_page.get(), ++ buffer.data(), size)); ++ EXPECT_TRUE(FPDFText_SetText(text_obj, buffer.data())); ++ } ++ ++ // Save the file. ++ EXPECT_TRUE(FPDFPage_GenerateContent(page)); ++ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ UnloadPage(page); ++ ++ // Open the saved copy and render it. ++ ASSERT_TRUE(OpenSavedDocument()); ++ FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); ++ ++ { ++ ScopedFPDFBitmap saved_bitmap = RenderSavedPage(saved_page); ++ CompareBitmap(saved_bitmap.get(), 200, 300, original_checksum); ++ } ++ ++ CloseSavedPage(saved_page); ++ CloseSavedDocument(); ++} ++ ++TEST_F(FPDFEditEmbedderTest, RemoveTextObject) { + // Load document with some text. +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + // Show what the original file looks like. + { +-#if defined(OS_MACOSX) +- const char kOriginalMD5[] = "b90475ca64d1348c3bf5e2b77ad9187a"; +-#elif defined(OS_WIN) +- const char kOriginalMD5[] = "795b7ce1626931aa06af0fa23b7d80bb"; +-#else +- const char kOriginalMD5[] = "2baa4c0e1758deba1b9c908e1fbd04ed"; +-#endif + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum()); + } + + // Get the "Hello, world!" text object and remove it. + ASSERT_EQ(2, FPDFPage_CountObjects(page)); +- FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0); +- ASSERT_TRUE(page_object); +- EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object)); ++ { ++ ScopedFPDFPageObject page_object(FPDFPage_GetObject(page, 0)); ++ ASSERT_TRUE(page_object); ++ ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(page_object.get())); ++ EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object.get())); ++ } ++ ASSERT_EQ(1, FPDFPage_CountObjects(page)); + + // Verify the "Hello, world!" text is gone. + { +-#if defined(OS_MACOSX) +- const char kRemovedMD5[] = "af760c4702467cb1492a57fb8215efaa"; +-#elif defined(OS_WIN) +- const char kRemovedMD5[] = "aae6c5334721f90ec30d3d59f4ef7deb"; +-#else +- const char kRemovedMD5[] = "b76df015fe88009c3c342395df96abf1"; +-#endif + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 200, kRemovedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, FirstRemovedChecksum()); + } +- ASSERT_EQ(1, FPDFPage_CountObjects(page)); ++ ++ // Verify the rendering again after calling FPDFPage_GenerateContent(). ++ ASSERT_TRUE(FPDFPage_GenerateContent(page)); ++ { ++ ScopedFPDFBitmap page_bitmap = RenderPage(page); ++ CompareBitmap(page_bitmap.get(), 200, 200, FirstRemovedChecksum()); ++ } ++ ++ // Save the document and verify it after reloading. ++ ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ VerifySavedDocument(200, 200, FirstRemovedChecksum()); ++ ++ // Verify removed/renamed resources are no longer there. ++ EXPECT_THAT(GetString(), Not(HasSubstr("/F1"))); ++ EXPECT_THAT(GetString(), Not(HasSubstr("/F2"))); ++ EXPECT_THAT(GetString(), Not(HasSubstr("/Times-Roman"))); + + UnloadPage(page); +- FPDFPageObj_Destroy(page_object); ++} ++ ++TEST_F(FPDFEditEmbedderTest, ++ RemoveTextObjectWithTwoPagesSharingContentStreamAndResources) { ++ // Load document with some text. ++ ASSERT_TRUE(OpenDocument("hello_world_2_pages.pdf")); ++ FPDF_PAGE page1 = LoadPage(0); ++ ASSERT_TRUE(page1); ++ FPDF_PAGE page2 = LoadPage(1); ++ ASSERT_TRUE(page2); ++ ++ // Show what the original file looks like. ++ { ++ ScopedFPDFBitmap page1_bitmap = RenderPage(page1); ++ CompareBitmap(page1_bitmap.get(), 200, 200, HelloWorldChecksum()); ++ ScopedFPDFBitmap page2_bitmap = RenderPage(page2); ++ CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum()); ++ } ++ ++ // Get the "Hello, world!" text object from page 1 and remove it. ++ ASSERT_EQ(2, FPDFPage_CountObjects(page1)); ++ { ++ ScopedFPDFPageObject page_object(FPDFPage_GetObject(page1, 0)); ++ ASSERT_TRUE(page_object); ++ ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(page_object.get())); ++ EXPECT_TRUE(FPDFPage_RemoveObject(page1, page_object.get())); ++ } ++ ASSERT_EQ(1, FPDFPage_CountObjects(page1)); ++ ++ // Verify the "Hello, world!" text is gone from page 1. ++ { ++ ScopedFPDFBitmap page1_bitmap = RenderPage(page1); ++ CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum()); ++ ScopedFPDFBitmap page2_bitmap = RenderPage(page2); ++ CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum()); ++ } ++ ++ // Verify the rendering again after calling FPDFPage_GenerateContent(). ++ ASSERT_TRUE(FPDFPage_GenerateContent(page1)); ++ { ++ ScopedFPDFBitmap page1_bitmap = RenderPage(page1); ++ CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum()); ++ ScopedFPDFBitmap page2_bitmap = RenderPage(page2); ++ CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum()); ++ } ++ ++ // Save the document and verify it after reloading. ++ ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ ASSERT_TRUE(OpenSavedDocument()); ++ FPDF_PAGE saved_page1 = LoadSavedPage(0); ++ VerifySavedRendering(saved_page1, 200, 200, FirstRemovedChecksum()); ++ CloseSavedPage(saved_page1); ++ FPDF_PAGE saved_page2 = LoadSavedPage(1); ++ VerifySavedRendering(saved_page2, 200, 200, HelloWorldChecksum()); ++ CloseSavedPage(saved_page2); ++ CloseSavedDocument(); ++ ++ std::vector split_saved_data = StringSplit(GetString(), '\n'); ++ // Verify removed/renamed resources are in the save PDF the correct number of ++ // times. ++ EXPECT_THAT(split_saved_data, Contains(HasSubstr("/F1")).Times(1)); ++ EXPECT_THAT(split_saved_data, Contains(HasSubstr("/F2")).Times(1)); ++ EXPECT_THAT(split_saved_data, Contains(HasSubstr("/Times-Roman")).Times(1)); ++ ++ UnloadPage(page1); ++ UnloadPage(page2); ++} ++ ++TEST_F(FPDFEditEmbedderTest, ++ RemoveTextObjectWithTwoPagesSharingContentArrayAndResources) { ++ // Load document with some text. ++ ASSERT_TRUE(OpenDocument("hello_world_2_pages_split_streams.pdf")); ++ FPDF_PAGE page1 = LoadPage(0); ++ ASSERT_TRUE(page1); ++ FPDF_PAGE page2 = LoadPage(1); ++ ASSERT_TRUE(page2); ++ ++ // Show what the original file looks like. ++ { ++ ScopedFPDFBitmap page1_bitmap = RenderPage(page1); ++ CompareBitmap(page1_bitmap.get(), 200, 200, HelloWorldChecksum()); ++ ScopedFPDFBitmap page2_bitmap = RenderPage(page2); ++ CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum()); ++ } ++ ++ // Get the "Hello, world!" text object from page 1 and remove it. ++ ASSERT_EQ(2, FPDFPage_CountObjects(page1)); ++ { ++ ScopedFPDFPageObject page_object(FPDFPage_GetObject(page1, 0)); ++ ASSERT_TRUE(page_object); ++ ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(page_object.get())); ++ EXPECT_TRUE(FPDFPage_RemoveObject(page1, page_object.get())); ++ } ++ ASSERT_EQ(1, FPDFPage_CountObjects(page1)); ++ ++ // Verify the "Hello, world!" text is gone from page 1. ++ { ++ ScopedFPDFBitmap page1_bitmap = RenderPage(page1); ++ CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum()); ++ ScopedFPDFBitmap page2_bitmap = RenderPage(page2); ++ CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum()); ++ } ++ ++ // Verify the rendering again after calling FPDFPage_GenerateContent(). ++ ASSERT_TRUE(FPDFPage_GenerateContent(page1)); ++ { ++ ScopedFPDFBitmap page1_bitmap = RenderPage(page1); ++ CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum()); ++ ScopedFPDFBitmap page2_bitmap = RenderPage(page2); ++ CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum()); ++ } ++ ++ // Save the document and verify it after reloading. ++ ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ ASSERT_TRUE(OpenSavedDocument()); ++ FPDF_PAGE saved_page1 = LoadSavedPage(0); ++ VerifySavedRendering(saved_page1, 200, 200, FirstRemovedChecksum()); ++ CloseSavedPage(saved_page1); ++ FPDF_PAGE saved_page2 = LoadSavedPage(1); ++ VerifySavedRendering(saved_page2, 200, 200, HelloWorldChecksum()); ++ CloseSavedPage(saved_page2); ++ CloseSavedDocument(); ++ ++ UnloadPage(page1); ++ UnloadPage(page2); ++} ++ ++TEST_F(FPDFEditEmbedderTest, RemoveTextObjectWithTwoPagesSharingResourcesDict) { ++ // Load document with some text. ++ ASSERT_TRUE(OpenDocument("hello_world_2_pages_shared_resources_dict.pdf")); ++ FPDF_PAGE page1 = LoadPage(0); ++ ASSERT_TRUE(page1); ++ FPDF_PAGE page2 = LoadPage(1); ++ ASSERT_TRUE(page2); ++ ++ // Show what the original file looks like. ++ { ++ ScopedFPDFBitmap page1_bitmap = RenderPage(page1); ++ CompareBitmap(page1_bitmap.get(), 200, 200, HelloWorldChecksum()); ++ ScopedFPDFBitmap page2_bitmap = RenderPage(page2); ++ CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum()); ++ } ++ ++ // Get the "Hello, world!" text object from page 1 and remove it. ++ ASSERT_EQ(2, FPDFPage_CountObjects(page1)); ++ { ++ ScopedFPDFPageObject page_object(FPDFPage_GetObject(page1, 0)); ++ ASSERT_TRUE(page_object); ++ ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(page_object.get())); ++ EXPECT_TRUE(FPDFPage_RemoveObject(page1, page_object.get())); ++ } ++ ASSERT_EQ(1, FPDFPage_CountObjects(page1)); ++ ++ // Verify the "Hello, world!" text is gone from page 1 ++ { ++ ScopedFPDFBitmap page1_bitmap = RenderPage(page1); ++ CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum()); ++ ScopedFPDFBitmap page2_bitmap = RenderPage(page2); ++ CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum()); ++ } ++ ++ // Verify the rendering again after calling FPDFPage_GenerateContent(). ++ ASSERT_TRUE(FPDFPage_GenerateContent(page1)); ++ { ++ ScopedFPDFBitmap page1_bitmap = RenderPage(page1); ++ CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum()); ++ ScopedFPDFBitmap page2_bitmap = RenderPage(page2); ++ CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum()); ++ } ++ ++ // Save the document and verify it after reloading. ++ ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ ASSERT_TRUE(OpenSavedDocument()); ++ FPDF_PAGE saved_page1 = LoadSavedPage(0); ++ VerifySavedRendering(saved_page1, 200, 200, FirstRemovedChecksum()); ++ CloseSavedPage(saved_page1); ++ FPDF_PAGE saved_page2 = LoadSavedPage(1); ++ VerifySavedRendering(saved_page2, 200, 200, HelloWorldChecksum()); ++ CloseSavedPage(saved_page2); ++ CloseSavedDocument(); ++ ++ UnloadPage(page1); ++ UnloadPage(page2); + } + + void CheckMarkCounts(FPDF_PAGE page, +@@ -758,7 +1224,7 @@ void CheckMarkCounts(FPDF_PAGE page, + + TEST_F(FPDFEditEmbedderTest, ReadMarkedObjectsIndirectDict) { + // Load document with some text marked with an indirect property. +- EXPECT_TRUE(OpenDocument("text_in_page_marked_indirect.pdf")); ++ ASSERT_TRUE(OpenDocument("text_in_page_marked_indirect.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -767,29 +1233,29 @@ TEST_F(FPDFEditEmbedderTest, ReadMarkedObjectsIndirectDict) { + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_RemoveMarkedObjectsPrime DISABLED_RemoveMarkedObjectsPrime +-#else +-#define MAYBE_RemoveMarkedObjectsPrime RemoveMarkedObjectsPrime +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveMarkedObjectsPrime) { ++TEST_F(FPDFEditEmbedderTest, RemoveMarkedObjectsPrime) { + // Load document with some text. +- EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf")); ++ ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + // Show what the original file looks like. + { +-#if defined(OS_MACOSX) +- const char kOriginalMD5[] = "5a5eb63cb21cc15084fea1f14284b8df"; +-#elif defined(OS_WIN) +- const char kOriginalMD5[] = "00542ee435b37749c4453be63bf7bdb6"; ++ const char* original_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "34ac4e0f3ba510760be09d0e19c1b43e"; ++#if BUILDFLAG(IS_APPLE) ++#ifdef ARCH_CPU_ARM64 ++ return "cdc8e22cf1e7e06999dc456288672a3b"; + #else +- const char kOriginalMD5[] = "41647268d5911d049801803b15c2dfb0"; +-#endif ++ return "966579fb98206858ce2f0a1f94a74d05"; ++#endif // ARCH_CPU_ARM64 ++#else ++ return "3d5a3de53d5866044c2b6bf339742c97"; ++#endif // BUILDFLAG(IS_APPLE) ++ }(); + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, original_checksum); + } + + constexpr int expected_object_count = 19; +@@ -825,20 +1291,35 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveMarkedObjectsPrime) { + } + + EXPECT_EQ(11, FPDFPage_CountObjects(page)); +- +-#if defined(OS_MACOSX) +- const char kNonPrimesMD5[] = "57e76dc7375d896704f0fd6d6d1b9e65"; +- const char kNonPrimesAfterSaveMD5[] = "6304512d0150bbd5578e8e22d3121103"; +-#elif defined(OS_WIN) +- const char kNonPrimesMD5[] = "86e371fdae30c2471f476631f3f93413"; +- const char kNonPrimesAfterSaveMD5[] = "86e371fdae30c2471f476631f3f93413"; ++ const char* non_primes_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "c671fa07b63e85c4201b9926e880fda8"; ++#if BUILDFLAG(IS_APPLE) ++#ifdef ARCH_CPU_ARM64 ++ return "23c4aec321547f51591fe7363a9ea2d6"; + #else +- const char kNonPrimesMD5[] = "67ab13115d0cc34e99a1003c28047b40"; +- const char kNonPrimesAfterSaveMD5[] = "67ab13115d0cc34e99a1003c28047b40"; +-#endif ++ return "6e19a4dd674b522cd39cf41956559bd6"; ++#endif // ARCH_CPU_ARM64 ++#else ++ return "bc8623c052f12376c3d8dd09a6cd27df"; ++#endif // BUILDFLAG(IS_APPLE) ++ }(); ++ const char* non_primes_after_save_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "c671fa07b63e85c4201b9926e880fda8"; ++#if BUILDFLAG(IS_APPLE) ++#ifdef ARCH_CPU_ARM64 ++ return "6bb1ea0d0a512f29edabda33064a0725"; ++#else ++ return "3cb35c681f8fb5a43a49146ac7caa818"; ++#endif // ARCH_CPU_ARM64 ++#else ++ return "bc8623c052f12376c3d8dd09a6cd27df"; ++#endif // BUILDFLAG(IS_APPLE) ++ }(); + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 200, kNonPrimesMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, non_primes_checksum); + } + + // Save the file. +@@ -849,11 +1330,12 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveMarkedObjectsPrime) { + // Re-open the file and check the prime marks are not there anymore. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + EXPECT_EQ(11, FPDFPage_CountObjects(saved_page)); + + { + ScopedFPDFBitmap page_bitmap = RenderPage(saved_page); +- CompareBitmap(page_bitmap.get(), 200, 200, kNonPrimesAfterSaveMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, non_primes_after_save_checksum); + } + + CloseSavedPage(saved_page); +@@ -862,7 +1344,7 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveMarkedObjectsPrime) { + + TEST_F(FPDFEditEmbedderTest, RemoveMarks) { + // Load document with some text. +- EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf")); ++ ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -906,6 +1388,7 @@ TEST_F(FPDFEditEmbedderTest, RemoveMarks) { + // Re-open the file and check the prime marks are not there anymore. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + + CheckMarkCounts(saved_page, 1, kExpectedObjectCount, 0, 4, 9, 1); + +@@ -915,7 +1398,7 @@ TEST_F(FPDFEditEmbedderTest, RemoveMarks) { + + TEST_F(FPDFEditEmbedderTest, RemoveMarkParam) { + // Load document with some text. +- EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf")); ++ ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -962,6 +1445,7 @@ TEST_F(FPDFEditEmbedderTest, RemoveMarkParam) { + // Re-open the file and check the "Factor" parameters are still gone. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + + size_t square_count = 0; + for (int i = 0; i < kExpectedObjectCount; ++i) { +@@ -999,7 +1483,7 @@ TEST_F(FPDFEditEmbedderTest, RemoveMarkParam) { + + TEST_F(FPDFEditEmbedderTest, MaintainMarkedObjects) { + // Load document with some text. +- EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf")); ++ ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1021,6 +1505,7 @@ TEST_F(FPDFEditEmbedderTest, MaintainMarkedObjects) { + + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + + CheckMarkCounts(saved_page, 2, 18, 8, 3, 9, 1); + +@@ -1030,7 +1515,7 @@ TEST_F(FPDFEditEmbedderTest, MaintainMarkedObjects) { + + TEST_F(FPDFEditEmbedderTest, MaintainIndirectMarkedObjects) { + // Load document with some text. +- EXPECT_TRUE(OpenDocument("text_in_page_marked_indirect.pdf")); ++ ASSERT_TRUE(OpenDocument("text_in_page_marked_indirect.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1052,6 +1537,7 @@ TEST_F(FPDFEditEmbedderTest, MaintainIndirectMarkedObjects) { + + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + + CheckMarkCounts(saved_page, 2, 18, 8, 3, 9, 1); + +@@ -1061,7 +1547,7 @@ TEST_F(FPDFEditEmbedderTest, MaintainIndirectMarkedObjects) { + + TEST_F(FPDFEditEmbedderTest, RemoveExistingPageObject) { + // Load document with some text. +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1083,23 +1569,15 @@ TEST_F(FPDFEditEmbedderTest, RemoveExistingPageObject) { + // Re-open the file and check the page object count is still 1. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + EXPECT_EQ(1, FPDFPage_CountObjects(saved_page)); + CloseSavedPage(saved_page); + CloseSavedDocument(); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_RemoveExistingPageObjectSplitStreamsNotLonely \ +- DISABLED_RemoveExistingPageObjectSplitStreamsNotLonely +-#else +-#define MAYBE_RemoveExistingPageObjectSplitStreamsNotLonely \ +- RemoveExistingPageObjectSplitStreamsNotLonely +-#endif +-TEST_F(FPDFEditEmbedderTest, +- MAYBE_RemoveExistingPageObjectSplitStreamsNotLonely) { ++TEST_F(FPDFEditEmbedderTest, RemoveExistingPageObjectSplitStreamsNotLonely) { + // Load document with some text. +- EXPECT_TRUE(OpenDocument("hello_world_split_streams.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world_split_streams.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1112,16 +1590,16 @@ TEST_F(FPDFEditEmbedderTest, + + // Verify the "Hello, world!" text is gone. + ASSERT_EQ(2, FPDFPage_CountObjects(page)); +-#if defined(OS_MACOSX) +- const char kHelloRemovedMD5[] = "e07a62d412728fc4d6e3ff42f2dd0e11"; +-#elif defined(OS_WIN) +- const char kHelloRemovedMD5[] = "a97d4c72c969ba373c2dce675d277e65"; +-#else +- const char kHelloRemovedMD5[] = "95b92950647a2190e1230911e7a1a0e9"; ++ const char* hello_removed_checksum = []() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "5508c2f06d104050f74f655693e38c2c"; + #endif ++ return "a8cd82499cf744e0862ca468c9d4ceb8"; ++ }(); + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 200, kHelloRemovedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, hello_removed_checksum); + } + + // Save the file +@@ -1133,28 +1611,21 @@ TEST_F(FPDFEditEmbedderTest, + // Re-open the file and check the page object count is still 2. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + + EXPECT_EQ(2, FPDFPage_CountObjects(saved_page)); + { + ScopedFPDFBitmap page_bitmap = RenderPage(saved_page); +- CompareBitmap(page_bitmap.get(), 200, 200, kHelloRemovedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, hello_removed_checksum); + } + + CloseSavedPage(saved_page); + CloseSavedDocument(); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_RemoveExistingPageObjectSplitStreamsLonely \ +- DISABLED_RemoveExistingPageObjectSplitStreamsLonely +-#else +-#define MAYBE_RemoveExistingPageObjectSplitStreamsLonely \ +- RemoveExistingPageObjectSplitStreamsLonely +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveExistingPageObjectSplitStreamsLonely) { ++TEST_F(FPDFEditEmbedderTest, RemoveExistingPageObjectSplitStreamsLonely) { + // Load document with some text. +- EXPECT_TRUE(OpenDocument("hello_world_split_streams.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world_split_streams.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1167,16 +1638,9 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveExistingPageObjectSplitStreamsLonely) { + + // Verify the "Greetings, world!" text is gone. + ASSERT_EQ(2, FPDFPage_CountObjects(page)); +-#if defined(OS_MACOSX) +- const char kGreetingsRemovedMD5[] = "b90475ca64d1348c3bf5e2b77ad9187a"; +-#elif defined(OS_WIN) +- const char kGreetingsRemovedMD5[] = "795b7ce1626931aa06af0fa23b7d80bb"; +-#else +- const char kGreetingsRemovedMD5[] = "2baa4c0e1758deba1b9c908e1fbd04ed"; +-#endif + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 200, kGreetingsRemovedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum()); + } + + // Save the file +@@ -1188,11 +1652,12 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveExistingPageObjectSplitStreamsLonely) { + // Re-open the file and check the page object count is still 2. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + + EXPECT_EQ(2, FPDFPage_CountObjects(saved_page)); + { + ScopedFPDFBitmap page_bitmap = RenderPage(saved_page); +- CompareBitmap(page_bitmap.get(), 200, 200, kGreetingsRemovedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum()); + } + + CloseSavedPage(saved_page); +@@ -1201,7 +1666,7 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveExistingPageObjectSplitStreamsLonely) { + + TEST_F(FPDFEditEmbedderTest, GetContentStream) { + // Load document with some text split across streams. +- EXPECT_TRUE(OpenDocument("split_streams.pdf")); ++ ASSERT_TRUE(OpenDocument("split_streams.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1225,15 +1690,9 @@ TEST_F(FPDFEditEmbedderTest, GetContentStream) { + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_RemoveAllFromStream DISABLED_RemoveAllFromStream +-#else +-#define MAYBE_RemoveAllFromStream RemoveAllFromStream +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveAllFromStream) { ++TEST_F(FPDFEditEmbedderTest, RemoveAllFromStream) { + // Load document with some text split across streams. +- EXPECT_TRUE(OpenDocument("split_streams.pdf")); ++ ASSERT_TRUE(OpenDocument("split_streams.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1289,16 +1748,22 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveAllFromStream) { + EXPECT_EQ(1, cpdf_page_object->GetContentStream()) << i; + } + +-#if defined(OS_MACOSX) +- const char kStream1RemovedMD5[] = "d2e21fbd5a6de563f619feeeb6163331"; +-#elif defined(OS_WIN) +- const char kStream1RemovedMD5[] = "b4140f203523e38793283a5943d8075b"; ++ const char* stream1_removed_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "b9bb0acfdba4bb5d2e578e7b73341baf"; ++#if BUILDFLAG(IS_APPLE) ++#if ARCH_CPU_ARM64 ++ return "08505db7b598f7397a2260ecb1f6d86d"; + #else +- const char kStream1RemovedMD5[] = "e86a3efc160ede6cfcb1f59bcacf1105"; +-#endif ++ return "3cdc75af44c15bed80998facd6e674c9"; ++#endif // ARCH_CPU_ARM64 ++#else ++ return "b474826df1acedb05c7b82e1e49e64a6"; ++#endif // BUILDFLAG(IS_APPLE) ++ }(); + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 200, kStream1RemovedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, stream1_removed_checksum); + } + + // Save the file +@@ -1309,6 +1774,7 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveAllFromStream) { + // content stream 1 was removed. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + + // Content stream 0: page objects 0-14. + // Content stream 1: page object 15. +@@ -1326,7 +1792,7 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveAllFromStream) { + + { + ScopedFPDFBitmap page_bitmap = RenderPage(saved_page); +- CompareBitmap(page_bitmap.get(), 200, 200, kStream1RemovedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, stream1_removed_checksum); + } + + CloseSavedPage(saved_page); +@@ -1335,7 +1801,7 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveAllFromStream) { + + TEST_F(FPDFEditEmbedderTest, RemoveAllFromSingleStream) { + // Load document with a single stream. +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1363,10 +1829,9 @@ TEST_F(FPDFEditEmbedderTest, RemoveAllFromSingleStream) { + + ASSERT_EQ(0, FPDFPage_CountObjects(page)); + +- const char kAllRemovedMD5[] = "eee4600ac08b458ac7ac2320e225674c"; + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedChecksum); + } + + // Save the file +@@ -1376,26 +1841,21 @@ TEST_F(FPDFEditEmbedderTest, RemoveAllFromSingleStream) { + // Re-open the file and check the page object count is still 0. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + + EXPECT_EQ(0, FPDFPage_CountObjects(saved_page)); + { + ScopedFPDFBitmap page_bitmap = RenderPage(saved_page); +- CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedChecksum); + } + + CloseSavedPage(saved_page); + CloseSavedDocument(); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_RemoveFirstFromSingleStream DISABLED_RemoveFirstFromSingleStream +-#else +-#define MAYBE_RemoveFirstFromSingleStream RemoveFirstFromSingleStream +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveFirstFromSingleStream) { ++TEST_F(FPDFEditEmbedderTest, RemoveFirstFromSingleStream) { + // Load document with a single stream. +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1427,16 +1887,9 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveFirstFromSingleStream) { + cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object); + ASSERT_EQ(0, cpdf_page_object->GetContentStream()); + +-#if defined(OS_MACOSX) +- const char kFirstRemovedMD5[] = "af760c4702467cb1492a57fb8215efaa"; +-#elif defined(OS_WIN) +- const char kFirstRemovedMD5[] = "aae6c5334721f90ec30d3d59f4ef7deb"; +-#else +- const char kFirstRemovedMD5[] = "b76df015fe88009c3c342395df96abf1"; +-#endif + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 200, kFirstRemovedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, FirstRemovedChecksum()); + } + + // Save the file +@@ -1446,6 +1899,7 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveFirstFromSingleStream) { + // Re-open the file and check the page object count is still 0. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + + ASSERT_EQ(1, FPDFPage_CountObjects(saved_page)); + page_object = FPDFPage_GetObject(saved_page, 0); +@@ -1454,22 +1908,16 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveFirstFromSingleStream) { + ASSERT_EQ(0, cpdf_page_object->GetContentStream()); + { + ScopedFPDFBitmap page_bitmap = RenderPage(saved_page); +- CompareBitmap(page_bitmap.get(), 200, 200, kFirstRemovedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, FirstRemovedChecksum()); + } + + CloseSavedPage(saved_page); + CloseSavedDocument(); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_RemoveLastFromSingleStream DISABLED_RemoveLastFromSingleStream +-#else +-#define MAYBE_RemoveLastFromSingleStream RemoveLastFromSingleStream +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveLastFromSingleStream) { ++TEST_F(FPDFEditEmbedderTest, RemoveLastFromSingleStream) { + // Load document with a single stream. +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1501,16 +1949,10 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveLastFromSingleStream) { + cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object); + ASSERT_EQ(0, cpdf_page_object->GetContentStream()); + +-#if defined(OS_MACOSX) +- const char kLastRemovedMD5[] = "f8fbd14a048b9e2ea8e5f059f22a910e"; +-#elif defined(OS_WIN) +- const char kLastRemovedMD5[] = "93db13099042bafefb3c22a165bad684"; +-#else +- const char kLastRemovedMD5[] = "93dcc09055f87a2792c8e3065af99a1b"; +-#endif ++ using pdfium::HelloWorldRemovedChecksum; + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 200, kLastRemovedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldRemovedChecksum()); + } + + // Save the file +@@ -1520,6 +1962,7 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveLastFromSingleStream) { + // Re-open the file and check the page object count is still 0. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + + ASSERT_EQ(1, FPDFPage_CountObjects(saved_page)); + page_object = FPDFPage_GetObject(saved_page, 0); +@@ -1528,7 +1971,7 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveLastFromSingleStream) { + ASSERT_EQ(0, cpdf_page_object->GetContentStream()); + { + ScopedFPDFBitmap page_bitmap = RenderPage(saved_page); +- CompareBitmap(page_bitmap.get(), 200, 200, kLastRemovedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldRemovedChecksum()); + } + + CloseSavedPage(saved_page); +@@ -1537,7 +1980,7 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveLastFromSingleStream) { + + TEST_F(FPDFEditEmbedderTest, RemoveAllFromMultipleStreams) { + // Load document with some text. +- EXPECT_TRUE(OpenDocument("hello_world_split_streams.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world_split_streams.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1563,10 +2006,9 @@ TEST_F(FPDFEditEmbedderTest, RemoveAllFromMultipleStreams) { + + ASSERT_EQ(0, FPDFPage_CountObjects(page)); + +- const char kAllRemovedMD5[] = "eee4600ac08b458ac7ac2320e225674c"; + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedChecksum); + } + + // Save the file +@@ -1576,11 +2018,12 @@ TEST_F(FPDFEditEmbedderTest, RemoveAllFromMultipleStreams) { + // Re-open the file and check the page object count is still 0. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + + EXPECT_EQ(0, FPDFPage_CountObjects(saved_page)); + { + ScopedFPDFBitmap page_bitmap = RenderPage(saved_page); +- CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedChecksum); + } + + CloseSavedPage(saved_page); +@@ -1589,7 +2032,7 @@ TEST_F(FPDFEditEmbedderTest, RemoveAllFromMultipleStreams) { + + TEST_F(FPDFEditEmbedderTest, InsertPageObjectAndSave) { + // Load document with some text. +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1611,6 +2054,7 @@ TEST_F(FPDFEditEmbedderTest, InsertPageObjectAndSave) { + // Re-open the file and check the page object count is still 3. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + EXPECT_EQ(3, FPDFPage_CountObjects(saved_page)); + CloseSavedPage(saved_page); + CloseSavedDocument(); +@@ -1618,7 +2062,7 @@ TEST_F(FPDFEditEmbedderTest, InsertPageObjectAndSave) { + + TEST_F(FPDFEditEmbedderTest, InsertPageObjectEditAndSave) { + // Load document with some text. +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1644,28 +2088,24 @@ TEST_F(FPDFEditEmbedderTest, InsertPageObjectEditAndSave) { + // Re-open the file and check the page object count is still 3. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + EXPECT_EQ(3, FPDFPage_CountObjects(saved_page)); + CloseSavedPage(saved_page); + CloseSavedDocument(); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_InsertAndRemoveLargeFile DISABLED_InsertAndRemoveLargeFile +-#else +-#define MAYBE_InsertAndRemoveLargeFile InsertAndRemoveLargeFile +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_InsertAndRemoveLargeFile) { ++TEST_F(FPDFEditEmbedderTest, InsertAndRemoveLargeFile) { + const int kOriginalObjectCount = 600; + + // Load document with many objects. +- EXPECT_TRUE(OpenDocument("many_rectangles.pdf")); ++ ASSERT_TRUE(OpenDocument("many_rectangles.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); +- const char kOriginalMD5[] = "b0170c575b65ecb93ebafada0ff0f038"; ++ ++ using pdfium::ManyRectanglesChecksum; + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 300, kOriginalMD5); ++ CompareBitmap(page_bitmap.get(), 200, 300, ManyRectanglesChecksum()); + } + + // Add a black rectangle. +@@ -1677,10 +2117,14 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_InsertAndRemoveLargeFile) { + + // Verify the black rectangle was added. + ASSERT_EQ(kOriginalObjectCount + 1, FPDFPage_CountObjects(page)); +- const char kPlusRectangleMD5[] = "6b9396ab570754b32b04ca629e902f77"; ++ const char* plus_rectangle_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "0d3715fcfb9bd0dd25dcce60800bff47"; ++ return "6b9396ab570754b32b04ca629e902f77"; ++ }(); + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 300, kPlusRectangleMD5); ++ CompareBitmap(page_bitmap.get(), 200, 300, plus_rectangle_checksum); + } + + // Save the file. +@@ -1691,10 +2135,11 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_InsertAndRemoveLargeFile) { + // Re-open the file and check the rectangle added is still there. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + EXPECT_EQ(kOriginalObjectCount + 1, FPDFPage_CountObjects(saved_page)); + { + ScopedFPDFBitmap page_bitmap = RenderPage(saved_page); +- CompareBitmap(page_bitmap.get(), 200, 300, kPlusRectangleMD5); ++ CompareBitmap(page_bitmap.get(), 200, 300, plus_rectangle_checksum); + } + + // Remove the added rectangle. +@@ -1704,13 +2149,13 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_InsertAndRemoveLargeFile) { + FPDFPageObj_Destroy(added_object); + { + ScopedFPDFBitmap page_bitmap = RenderPage(saved_page); +- CompareBitmap(page_bitmap.get(), 200, 300, kOriginalMD5); ++ CompareBitmap(page_bitmap.get(), 200, 300, ManyRectanglesChecksum()); + } + EXPECT_EQ(kOriginalObjectCount, FPDFPage_CountObjects(saved_page)); + + // Save the file again. + EXPECT_TRUE(FPDFPage_GenerateContent(saved_page)); +- EXPECT_TRUE(FPDF_SaveAsCopy(saved_document_, this, 0)); ++ EXPECT_TRUE(FPDF_SaveAsCopy(saved_document(), this, 0)); + + CloseSavedPage(saved_page); + CloseSavedDocument(); +@@ -1719,10 +2164,11 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_InsertAndRemoveLargeFile) { + // rest is intact. + ASSERT_TRUE(OpenSavedDocument()); + saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + EXPECT_EQ(kOriginalObjectCount, FPDFPage_CountObjects(saved_page)); + { + ScopedFPDFBitmap page_bitmap = RenderPage(saved_page); +- CompareBitmap(page_bitmap.get(), 200, 300, kOriginalMD5); ++ CompareBitmap(page_bitmap.get(), 200, 300, ManyRectanglesChecksum()); + } + + CloseSavedPage(saved_page); +@@ -1735,10 +2181,10 @@ TEST_F(FPDFEditEmbedderTest, AddAndRemovePaths) { + ASSERT_TRUE(page); + + // Render the blank page and verify it's a blank bitmap. +- const char kBlankMD5[] = "1940568c9ba33bac5d0b1ee9558c76b3"; ++ using pdfium::kBlankPage612By792Checksum; + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 612, 792, kBlankMD5); ++ CompareBitmap(page_bitmap.get(), 612, 792, kBlankPage612By792Checksum); + } + ASSERT_EQ(0, FPDFPage_CountObjects(page)); + +@@ -1748,10 +2194,9 @@ TEST_F(FPDFEditEmbedderTest, AddAndRemovePaths) { + EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255)); + EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0)); + FPDFPage_InsertObject(page, red_rect); +- const char kRedRectangleMD5[] = "66d02eaa6181e2c069ce2ea99beda497"; + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 612, 792, kRedRectangleMD5); ++ CompareBitmap(page_bitmap.get(), 612, 792, kRedRectangleChecksum); + } + EXPECT_EQ(1, FPDFPage_CountObjects(page)); + +@@ -1760,7 +2205,7 @@ TEST_F(FPDFEditEmbedderTest, AddAndRemovePaths) { + EXPECT_TRUE(FPDFPage_RemoveObject(page, red_rect)); + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 612, 792, kBlankMD5); ++ CompareBitmap(page_bitmap.get(), 612, 792, kBlankPage612By792Checksum); + } + EXPECT_EQ(0, FPDFPage_CountObjects(page)); + +@@ -1773,7 +2218,7 @@ TEST_F(FPDFEditEmbedderTest, AddAndRemovePaths) { + + TEST_F(FPDFEditEmbedderTest, PathsPoints) { + CreateNewDocument(); +- FPDF_PAGEOBJECT img = FPDFPageObj_NewImageObj(document_); ++ FPDF_PAGEOBJECT img = FPDFPageObj_NewImageObj(document()); + // This should fail gracefully, even if img is not a path. + ASSERT_EQ(-1, FPDFPath_CountSegments(img)); + +@@ -1798,15 +2243,9 @@ TEST_F(FPDFEditEmbedderTest, PathsPoints) { + FPDFPageObj_Destroy(img); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_PathOnTopOfText DISABLED_PathOnTopOfText +-#else +-#define MAYBE_PathOnTopOfText PathOnTopOfText +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_PathOnTopOfText) { ++TEST_F(FPDFEditEmbedderTest, PathOnTopOfText) { + // Load document with some text +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1825,28 +2264,24 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_PathOnTopOfText) { + EXPECT_TRUE(FPDFPath_Close(black_path)); + FPDFPage_InsertObject(page, black_path); + +- // Render and check the result. Text is slightly different on Mac. ++ // Render and check the result. + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +-#if defined(OS_MACOSX) +- const char md5[] = "f9e6fa74230f234286bfcada9f7606d8"; +-#elif defined(OS_WIN) +- const char md5[] = "74dd9c393b8b2578d2b7feb032b7daad"; ++ const char* checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "8a48b019826492331454f2809990aba8"; ++#if BUILDFLAG(IS_APPLE) ++ return "279693baca9f48da2d75a8e289aed58e"; + #else +- const char md5[] = "aa71b09b93b55f467f1290e5111babee"; ++ return "fe415d47945c10b9cc8e9ca08887369e"; + #endif +- CompareBitmap(bitmap.get(), 200, 200, md5); ++ }(); ++ CompareBitmap(bitmap.get(), 200, 200, checksum); + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_EditOverExistingContent DISABLED_EditOverExistingContent +-#else +-#define MAYBE_EditOverExistingContent EditOverExistingContent +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_EditOverExistingContent) { ++TEST_F(FPDFEditEmbedderTest, EditOverExistingContent) { + // Load document with existing content +- EXPECT_TRUE(OpenDocument("bug_717.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_717.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1862,8 +2297,13 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_EditOverExistingContent) { + EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0)); + FPDFPage_InsertObject(page, red_rect); + ++ const char* original_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "1e82fbdd21490cee9d3479fe6125af67"; ++ return "ad04e5bd0f471a9a564fb034bd0fb073"; ++ }(); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), 612, 792, "ad04e5bd0f471a9a564fb034bd0fb073"); ++ CompareBitmap(bitmap.get(), 612, 792, original_checksum); + EXPECT_TRUE(FPDFPage_GenerateContent(page)); + + // Now save the result, closing the page and document +@@ -1872,8 +2312,8 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_EditOverExistingContent) { + + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); +- VerifySavedRendering(saved_page, 612, 792, +- "ad04e5bd0f471a9a564fb034bd0fb073"); ++ ASSERT_TRUE(saved_page); ++ VerifySavedRendering(saved_page, 612, 792, original_checksum); + + ClearString(); + // Add another opaque rectangle on top of the existing content +@@ -1887,30 +2327,28 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_EditOverExistingContent) { + EXPECT_TRUE(FPDFPageObj_SetFillColor(green_rect2, 0, 255, 0, 100)); + EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect2, FPDF_FILLMODE_ALTERNATE, 0)); + FPDFPage_InsertObject(saved_page, green_rect2); +- const char kLastMD5[] = "4b5b00f824620f8c9b8801ebb98e1cdd"; ++ const char* last_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "8705d023e5fec3499d1e30cf2bcc5dc1"; ++ return "4b5b00f824620f8c9b8801ebb98e1cdd"; ++ }(); + { + ScopedFPDFBitmap new_bitmap = RenderSavedPage(saved_page); +- CompareBitmap(new_bitmap.get(), 612, 792, kLastMD5); ++ CompareBitmap(new_bitmap.get(), 612, 792, last_checksum); + } + EXPECT_TRUE(FPDFPage_GenerateContent(saved_page)); + + // Now save the result, closing the page and document +- EXPECT_TRUE(FPDF_SaveAsCopy(saved_document_, this, 0)); ++ EXPECT_TRUE(FPDF_SaveAsCopy(saved_document(), this, 0)); + + CloseSavedPage(saved_page); + CloseSavedDocument(); + + // Render the saved result +- VerifySavedDocument(612, 792, kLastMD5); ++ VerifySavedDocument(612, 792, last_checksum); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_AddStrokedPaths DISABLED_AddStrokedPaths +-#else +-#define MAYBE_AddStrokedPaths AddStrokedPaths +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_AddStrokedPaths) { ++TEST_F(FPDFEditEmbedderTest, AddStrokedPaths) { + // Start with a blank page + FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792); + +@@ -1928,8 +2366,12 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddStrokedPaths) { + FPDFPage_InsertObject(page, rect); + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 612, 792, +- "64bd31f862a89e0a9e505a5af6efd506"); ++ const char* checksum_1 = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "1469acf60e7647ebeb8e1fb08c5d6c7a"; ++ return "64bd31f862a89e0a9e505a5af6efd506"; ++ }(); ++ CompareBitmap(page_bitmap.get(), 612, 792, checksum_1); + } + + // Add crossed-checkmark +@@ -1944,8 +2386,12 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddStrokedPaths) { + FPDFPage_InsertObject(page, check); + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 612, 792, +- "4b6f3b9d25c4e194821217d5016c3724"); ++ const char* checksum_2 = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "68b3194f74abd9d471695ce1415be43f"; ++ return "4b6f3b9d25c4e194821217d5016c3724"; ++ }(); ++ CompareBitmap(page_bitmap.get(), 612, 792, checksum_2); + } + + // Add stroked and filled oval-ish path. +@@ -1961,46 +2407,37 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddStrokedPaths) { + FPDFPage_InsertObject(page, path); + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 612, 792, +- "ff3e6a22326754944cc6e56609acd73b"); ++ const char* checksum_3 = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "ea784068651df2b9ba132ce9215e6780"; ++ return "ff3e6a22326754944cc6e56609acd73b"; ++ }(); ++ CompareBitmap(page_bitmap.get(), 612, 792, checksum_3); + } + FPDF_ClosePage(page); + } + + // Tests adding text from standard font using FPDFPageObj_NewTextObj. +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_AddStandardFontText DISABLED_AddStandardFontText +-#else +-#define MAYBE_AddStandardFontText AddStandardFontText +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_AddStandardFontText) { ++TEST_F(FPDFEditEmbedderTest, AddStandardFontText) { + // Start with a blank page +- FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792); ++ ScopedFPDFPage page(FPDFPage_New(CreateNewDocument(), 0, 612, 792)); + + // Add some text to the page + FPDF_PAGEOBJECT text_object1 = + FPDFPageObj_NewTextObj(document(), "Arial", 12.0f); + EXPECT_TRUE(text_object1); +- ScopedFPDFWideString text1 = +- GetFPDFWideString(L"I'm at the bottom of the page"); ++ ScopedFPDFWideString text1 = GetFPDFWideString(kBottomText); + EXPECT_TRUE(FPDFText_SetText(text_object1, text1.get())); +- FPDFPageObj_Transform(text_object1, 1, 0, 0, 1, 20, 20); +- FPDFPage_InsertObject(page, text_object1); +- EXPECT_TRUE(FPDFPage_GenerateContent(page)); ++ static constexpr FS_MATRIX kMatrix1{1, 0, 0, 1, 20, 20}; ++ EXPECT_TRUE(FPDFPageObj_SetMatrix(text_object1, &kMatrix1)); ++ FPDFPage_InsertObject(page.get(), text_object1); ++ EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); + { +- ScopedFPDFBitmap page_bitmap = RenderPage(page); +-#if defined(OS_MACOSX) +- const char md5[] = "a4dddc1a3930fa694bbff9789dab4161"; +-#elif defined(OS_WIN) +- const char md5[] = "08d1ff3e5a42801bee6077fd366bef00"; +-#else +- const char md5[] = "eacaa24573b8ce997b3882595f096f00"; +-#endif +- CompareBitmap(page_bitmap.get(), 612, 792, md5); ++ ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); ++ CompareBitmap(page_bitmap.get(), 612, 792, BottomTextChecksum()); + + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); +- VerifySavedDocument(612, 792, md5); ++ VerifySavedDocument(612, 792, BottomTextChecksum()); + } + + // Try another font +@@ -2011,21 +2448,21 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddStandardFontText) { + GetFPDFWideString(L"Hi, I'm Bold. Times New Roman Bold."); + EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get())); + FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 100, 600); +- FPDFPage_InsertObject(page, text_object2); +- EXPECT_TRUE(FPDFPage_GenerateContent(page)); ++ FPDFPage_InsertObject(page.get(), text_object2); ++ EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); + { +- ScopedFPDFBitmap page_bitmap = RenderPage(page); +-#if defined(OS_MACOSX) +- const char md5[] = "a5c4ace4c6f27644094813fe1441a21c"; +-#elif defined(OS_WIN) +- const char md5[] = "3755dd35abd4c605755369401ee85b2d"; +-#else +- const char md5[] = "76fcc7d08aa15445efd2e2ceb7c6cc3b"; ++ ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); ++ const char* checksum = []() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "983baaa1f688eff7a14b1bf91c171a1a"; + #endif +- CompareBitmap(page_bitmap.get(), 612, 792, md5); ++ return "161523e196eb5341604cd73e12c97922"; ++ }(); ++ CompareBitmap(page_bitmap.get(), 612, 792, checksum); + + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); +- VerifySavedDocument(612, 792, md5); ++ VerifySavedDocument(612, 792, checksum); + } + + // And some randomly transformed text +@@ -2035,26 +2472,25 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddStandardFontText) { + ScopedFPDFWideString text3 = GetFPDFWideString(L"Can you read me? <:)>"); + EXPECT_TRUE(FPDFText_SetText(text_object3, text3.get())); + FPDFPageObj_Transform(text_object3, 1, 1.5, 2, 0.5, 200, 200); +- FPDFPage_InsertObject(page, text_object3); +- EXPECT_TRUE(FPDFPage_GenerateContent(page)); ++ FPDFPage_InsertObject(page.get(), text_object3); ++ EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); + { +- ScopedFPDFBitmap page_bitmap = RenderPage(page); +-#if defined(OS_MACOSX) +- const char md5[] = "40b3ef04f915ff4c4208948001763544"; +-#elif defined(OS_WIN) +- const char md5[] = "5ded49fe157f89627903553771431e3d"; +-#else +- const char md5[] = "344534539aa7c5cc78404cfff4bde7fb"; ++ ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); ++ const char* checksum = []() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "e0b3493c5c16e41d0d892ffb48e63fba"; + #endif +- CompareBitmap(page_bitmap.get(), 612, 792, md5); ++ return "1fbf772dca8d82b960631e6683934964"; ++ }(); ++ CompareBitmap(page_bitmap.get(), 612, 792, checksum); + + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); +- VerifySavedDocument(612, 792, md5); ++ VerifySavedDocument(612, 792, checksum); + } + + FS_MATRIX matrix; +- EXPECT_FALSE(FPDFTextObj_GetMatrix(nullptr, &matrix)); +- EXPECT_TRUE(FPDFTextObj_GetMatrix(text_object3, &matrix)); ++ EXPECT_TRUE(FPDFPageObj_GetMatrix(text_object3, &matrix)); + EXPECT_FLOAT_EQ(1.0f, matrix.a); + EXPECT_FLOAT_EQ(1.5f, matrix.b); + EXPECT_FLOAT_EQ(2.0f, matrix.c); +@@ -2062,16 +2498,47 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddStandardFontText) { + EXPECT_FLOAT_EQ(200.0f, matrix.e); + EXPECT_FLOAT_EQ(200.0f, matrix.f); + +- EXPECT_EQ(0, FPDFTextObj_GetFontSize(nullptr)); +- EXPECT_EQ(20, FPDFTextObj_GetFontSize(text_object3)); ++ EXPECT_FALSE(FPDFTextObj_GetFontSize(nullptr, nullptr)); ++ float size = 55; ++ EXPECT_FALSE(FPDFTextObj_GetFontSize(nullptr, &size)); ++ EXPECT_EQ(55, size); ++ EXPECT_TRUE(FPDFTextObj_GetFontSize(text_object3, &size)); ++ EXPECT_EQ(20, size); + + // TODO(npm): Why are there issues with text rotated by 90 degrees? + // TODO(npm): FPDF_SaveAsCopy not giving the desired result after this. +- FPDF_ClosePage(page); ++} ++ ++TEST_F(FPDFEditEmbedderTest, AddStandardFontTextOfSizeZero) { ++ // Start with a blank page ++ ScopedFPDFPage page(FPDFPage_New(CreateNewDocument(), 0, 612, 792)); ++ ++ // Add some text of size 0 to the page. ++ FPDF_PAGEOBJECT text_object = ++ FPDFPageObj_NewTextObj(document(), "Arial", 0.0f); ++ EXPECT_TRUE(text_object); ++ ScopedFPDFWideString text = GetFPDFWideString(kBottomText); ++ EXPECT_TRUE(FPDFText_SetText(text_object, text.get())); ++ FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 20, 20); ++ ++ float size = -1; // Make sure 'size' gets changed. ++ EXPECT_TRUE(FPDFTextObj_GetFontSize(text_object, &size)); ++ EXPECT_EQ(0.0f, size); ++ ++ FPDFPage_InsertObject(page.get(), text_object); ++ EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); ++ { ++ ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); ++ CompareBitmap(page_bitmap.get(), 612, 792, ++ pdfium::kBlankPage612By792Checksum); ++ ++ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ VerifySavedDocument(612, 792, pdfium::kBlankPage612By792Checksum); ++ } + } + + TEST_F(FPDFEditEmbedderTest, GetTextRenderMode) { +- EXPECT_TRUE(OpenDocument("text_render_mode.pdf")); ++ ASSERT_TRUE(OpenDocument("text_render_mode.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + ASSERT_EQ(2, FPDFPage_CountObjects(page)); +@@ -2089,90 +2556,291 @@ TEST_F(FPDFEditEmbedderTest, GetTextRenderMode) { + } + + TEST_F(FPDFEditEmbedderTest, SetTextRenderMode) { +- EXPECT_TRUE(OpenDocument("text_render_mode.pdf")); +- FPDF_PAGE page = LoadPage(0); +- ASSERT_TRUE(page); +- ASSERT_EQ(2, FPDFPage_CountObjects(page)); +- +- // Check the bitmap +- { +-#if defined(OS_MACOSX) +- const char md5[] = "139846b4ffbd34b1fd67e3b82cf33b7e"; +-#elif defined(OS_WIN) +- const char md5[] = "de6e86bad3e9fda753a8471a45cfbb58"; ++ const char* original_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "39a4ac8f1fdc6653edd3b91862ea7b75"; ++#if BUILDFLAG(IS_APPLE) ++ return "c488514ce0fc949069ff560407edacd2"; + #else +- const char md5[] = "5a012d2920ac075c39ffa9437ea42faa"; ++ return "97a4fcf3c9581e19917895631af31d41"; + #endif +- ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 612, 446, md5); ++ }(); ++ const char* stroke_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "d16eb1bb4748eeb5fb801594da70d519"; ++ return "e06ee84aeebe926e8c980b7822027e8a"; ++ }(); ++ ++ { ++ ASSERT_TRUE(OpenDocument("text_render_mode.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ASSERT_EQ(2, FPDFPage_CountObjects(page)); ++ ++ // Check the bitmap ++ { ++ ScopedFPDFBitmap page_bitmap = RenderPage(page); ++ CompareBitmap(page_bitmap.get(), 612, 446, original_checksum); ++ } ++ ++ // Cannot set on a null object. ++ EXPECT_FALSE( ++ FPDFTextObj_SetTextRenderMode(nullptr, FPDF_TEXTRENDERMODE_UNKNOWN)); ++ EXPECT_FALSE( ++ FPDFTextObj_SetTextRenderMode(nullptr, FPDF_TEXTRENDERMODE_INVISIBLE)); ++ ++ FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0); ++ ASSERT_TRUE(page_object); ++ EXPECT_EQ(FPDF_TEXTRENDERMODE_FILL, ++ FPDFTextObj_GetTextRenderMode(page_object)); ++ ++ // Cannot set UNKNOWN as a render mode. ++ EXPECT_FALSE(FPDFTextObj_SetTextRenderMode(page_object, ++ FPDF_TEXTRENDERMODE_UNKNOWN)); ++ ++ EXPECT_TRUE( ++ FPDFTextObj_SetTextRenderMode(page_object, FPDF_TEXTRENDERMODE_STROKE)); ++ EXPECT_EQ(FPDF_TEXTRENDERMODE_STROKE, ++ FPDFTextObj_GetTextRenderMode(page_object)); ++ ++ // Check that bitmap displays changed content ++ { ++ ScopedFPDFBitmap page_bitmap = RenderPage(page); ++ CompareBitmap(page_bitmap.get(), 612, 446, stroke_checksum); ++ } ++ ++ // Save a copy. ++ EXPECT_TRUE(FPDFPage_GenerateContent(page)); ++ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ ++ UnloadPage(page); + } + +- // Cannot set on a null object. +- EXPECT_FALSE( +- FPDFTextObj_SetTextRenderMode(nullptr, FPDF_TEXTRENDERMODE_UNKNOWN)); +- EXPECT_FALSE( +- FPDFTextObj_SetTextRenderMode(nullptr, FPDF_TEXTRENDERMODE_INVISIBLE)); ++ { ++ // Open the saved copy and render it. Check that the changed text render ++ // mode is kept in the saved copy. ++ ASSERT_TRUE(OpenSavedDocument()); ++ FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); ++ ++ FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(saved_page, 0); ++ EXPECT_TRUE(page_object); ++ EXPECT_EQ(FPDF_TEXTRENDERMODE_STROKE, ++ FPDFTextObj_GetTextRenderMode(page_object)); ++ ++ ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page); ++ CompareBitmap(bitmap.get(), 612, 446, stroke_checksum); ++ ++ CloseSavedPage(saved_page); ++ CloseSavedDocument(); ++ } ++} + +- FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0); +- ASSERT_TRUE(page_object); +- EXPECT_EQ(FPDF_TEXTRENDERMODE_FILL, +- FPDFTextObj_GetTextRenderMode(page_object)); ++TEST_F(FPDFEditEmbedderTest, TextFontProperties) { ++ // bad object tests ++ EXPECT_FALSE(FPDFTextObj_GetFont(nullptr)); ++ EXPECT_EQ(0U, FPDFFont_GetFontName(nullptr, nullptr, 5)); ++ EXPECT_EQ(-1, FPDFFont_GetFlags(nullptr)); ++ EXPECT_EQ(-1, FPDFFont_GetWeight(nullptr)); ++ EXPECT_FALSE(FPDFFont_GetItalicAngle(nullptr, nullptr)); ++ EXPECT_FALSE(FPDFFont_GetAscent(nullptr, 12.f, nullptr)); ++ EXPECT_FALSE(FPDFFont_GetDescent(nullptr, 12.f, nullptr)); ++ EXPECT_FALSE(FPDFFont_GetGlyphWidth(nullptr, 's', 12.f, nullptr)); ++ EXPECT_FALSE(FPDFFont_GetGlyphPath(nullptr, 's', 12.f)); ++ ++ // good object tests ++ ASSERT_TRUE(OpenDocument("text_font.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ASSERT_EQ(1, FPDFPage_CountObjects(page)); ++ FPDF_PAGEOBJECT text = FPDFPage_GetObject(page, 0); ++ ASSERT_TRUE(text); ++ float font_size; ++ ASSERT_TRUE(FPDFTextObj_GetFontSize(text, &font_size)); ++ FPDF_FONT font = FPDFTextObj_GetFont(text); ++ ASSERT_TRUE(font); + +- // Cannot set UNKNOWN as a render mode. +- EXPECT_FALSE( +- FPDFTextObj_SetTextRenderMode(page_object, FPDF_TEXTRENDERMODE_UNKNOWN)); ++ // null return pointer tests ++ EXPECT_FALSE(FPDFFont_GetItalicAngle(font, nullptr)); ++ EXPECT_FALSE(FPDFFont_GetAscent(font, font_size, nullptr)); ++ EXPECT_FALSE(FPDFFont_GetDescent(font, font_size, nullptr)); ++ EXPECT_FALSE(FPDFFont_GetGlyphWidth(font, 's', font_size, nullptr)); + +- EXPECT_TRUE( +- FPDFTextObj_SetTextRenderMode(page_object, FPDF_TEXTRENDERMODE_STROKE)); +- EXPECT_EQ(FPDF_TEXTRENDERMODE_STROKE, +- FPDFTextObj_GetTextRenderMode(page_object)); ++ // correct property tests ++ { ++ EXPECT_EQ(4, FPDFFont_GetFlags(font)); ++ EXPECT_EQ(400, FPDFFont_GetWeight(font)); ++ ++ int angle; ++ EXPECT_TRUE(FPDFFont_GetItalicAngle(font, &angle)); ++ EXPECT_EQ(0, angle); ++ ++ float ascent; ++ EXPECT_TRUE(FPDFFont_GetAscent(font, font_size, &ascent)); ++ EXPECT_FLOAT_EQ(891 * font_size / 1000.0f, ascent); ++ ++ float descent; ++ EXPECT_TRUE(FPDFFont_GetDescent(font, font_size, &descent)); ++ EXPECT_FLOAT_EQ(-216 * font_size / 1000.0f, descent); ++ ++ float a12; ++ float a24; ++ EXPECT_TRUE(FPDFFont_GetGlyphWidth(font, 'a', 12.0f, &a12)); ++ EXPECT_FLOAT_EQ(a12, 5.316f); ++ EXPECT_TRUE(FPDFFont_GetGlyphWidth(font, 'a', 24.0f, &a24)); ++ EXPECT_FLOAT_EQ(a24, 10.632f); ++ } + +- // Check that bitmap displays changed content + { +- const char md5[] = "412e52e621b46bd77baf2162e1fb1a1d"; +- ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 612, 446, md5); ++ // FPDFFont_GetFontName() positive testing. ++ unsigned long size = FPDFFont_GetFontName(font, nullptr, 0); ++ const char kExpectedFontName[] = "Liberation Serif"; ++ ASSERT_EQ(sizeof(kExpectedFontName), size); ++ std::vector font_name(size); ++ ASSERT_EQ(size, FPDFFont_GetFontName(font, font_name.data(), size)); ++ ASSERT_STREQ(kExpectedFontName, font_name.data()); ++ ++ // FPDFFont_GetFontName() negative testing. ++ ASSERT_EQ(0U, FPDFFont_GetFontName(nullptr, nullptr, 0)); ++ ++ font_name.resize(2); ++ font_name[0] = 'x'; ++ font_name[1] = '\0'; ++ size = FPDFFont_GetFontName(font, font_name.data(), font_name.size()); ++ ASSERT_EQ(sizeof(kExpectedFontName), size); ++ ASSERT_STREQ("x", font_name.data()); ++ } ++ ++ { ++ // FPDFFont_GetFontData() positive testing. ++ constexpr size_t kExpectedSize = 8268; ++ std::vector buf; ++ size_t buf_bytes_required = 123; ++ ASSERT_TRUE(FPDFFont_GetFontData(font, nullptr, 0, &buf_bytes_required)); ++ ASSERT_EQ(kExpectedSize, buf_bytes_required); ++ ++ buf.resize(kExpectedSize); ++ EXPECT_EQ("495800b8e56e2d37f3bc48a1b52db952", GenerateMD5Base16(buf)); ++ buf_bytes_required = 234; ++ // Test with buffer that is too small. Make sure `buf` is unchanged. ++ EXPECT_TRUE(FPDFFont_GetFontData(font, buf.data(), buf.size() - 1, ++ &buf_bytes_required)); ++ EXPECT_EQ("495800b8e56e2d37f3bc48a1b52db952", GenerateMD5Base16(buf)); ++ EXPECT_EQ(kExpectedSize, buf_bytes_required); ++ ++ // Test with buffer of the correct size. ++ buf_bytes_required = 234; ++ EXPECT_TRUE(FPDFFont_GetFontData(font, buf.data(), buf.size(), ++ &buf_bytes_required)); ++ EXPECT_EQ("1a67be75f719b6c476804d85bb9e4844", GenerateMD5Base16(buf)); ++ EXPECT_EQ(kExpectedSize, buf_bytes_required); ++ ++ // FPDFFont_GetFontData() negative testing. ++ EXPECT_FALSE(FPDFFont_GetFontData(nullptr, nullptr, 0, nullptr)); ++ EXPECT_FALSE(FPDFFont_GetFontData(font, nullptr, 0, nullptr)); ++ ++ buf_bytes_required = 345; ++ EXPECT_FALSE( ++ FPDFFont_GetFontData(nullptr, nullptr, 0, &buf_bytes_required)); ++ EXPECT_EQ(345u, buf_bytes_required); ++ ++ EXPECT_FALSE( ++ FPDFFont_GetFontData(nullptr, buf.data(), buf.size(), nullptr)); ++ EXPECT_FALSE(FPDFFont_GetFontData(font, buf.data(), buf.size(), nullptr)); ++ ++ buf_bytes_required = 345; ++ EXPECT_FALSE(FPDFFont_GetFontData(nullptr, buf.data(), buf.size(), ++ &buf_bytes_required)); ++ EXPECT_EQ(345u, buf_bytes_required); ++ } ++ { ++ ASSERT_EQ(1, FPDFFont_GetIsEmbedded(font)); ++ ASSERT_EQ(-1, FPDFFont_GetIsEmbedded(nullptr)); + } + + UnloadPage(page); + } + +-TEST_F(FPDFEditEmbedderTest, TestGetTextFontName) { +- EXPECT_TRUE(OpenDocument("text_font.pdf")); ++TEST_F(FPDFEditEmbedderTest, NoEmbeddedFontData) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); +- ASSERT_EQ(1, FPDFPage_CountObjects(page)); ++ ASSERT_EQ(2, FPDFPage_CountObjects(page)); ++ ++ // Since hello_world.pdf does not embed any font data, FPDFFont_GetFontData() ++ // will return the substitution font data. Since pdfium_embeddertest is ++ // hermetic, this first object consistently maps to Tinos-Regular.ttf. ++ constexpr size_t kTinosRegularSize = 469968; ++ FPDF_PAGEOBJECT text = FPDFPage_GetObject(page, 0); ++ ASSERT_TRUE(text); ++ FPDF_FONT font = FPDFTextObj_GetFont(text); ++ ASSERT_TRUE(font); ++ std::vector buf; ++ buf.resize(kTinosRegularSize); ++ size_t buf_bytes_required; ++ ASSERT_TRUE( ++ FPDFFont_GetFontData(font, buf.data(), buf.size(), &buf_bytes_required)); ++ EXPECT_EQ(kTinosRegularSize, buf_bytes_required); ++ EXPECT_EQ("2b019558f2c2de0b7cbc0a6e64b20599", GenerateMD5Base16(buf)); ++ EXPECT_EQ(0, FPDFFont_GetIsEmbedded(font)); ++ ++ // Similarly, the second object consistently maps to Arimo-Regular.ttf. ++ constexpr size_t kArimoRegularSize = 436180; ++ text = FPDFPage_GetObject(page, 1); ++ ASSERT_TRUE(text); ++ font = FPDFTextObj_GetFont(text); ++ ASSERT_TRUE(font); ++ buf.resize(kArimoRegularSize); ++ ASSERT_TRUE( ++ FPDFFont_GetFontData(font, buf.data(), buf.size(), &buf_bytes_required)); ++ EXPECT_EQ(kArimoRegularSize, buf_bytes_required); ++ EXPECT_EQ("7ac02a544211773d9636e056e9da6c35", GenerateMD5Base16(buf)); ++ EXPECT_EQ(0, FPDFFont_GetIsEmbedded(font)); + +- // FPDFTextObj_GetFontName() positive testing. ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditEmbedderTest, GlyphPaths) { ++ // bad glyphpath ++ EXPECT_EQ(-1, FPDFGlyphPath_CountGlyphSegments(nullptr)); ++ EXPECT_FALSE(FPDFGlyphPath_GetGlyphPathSegment(nullptr, 1)); ++ ++ ASSERT_TRUE(OpenDocument("text_font.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ASSERT_EQ(1, FPDFPage_CountObjects(page)); + FPDF_PAGEOBJECT text = FPDFPage_GetObject(page, 0); +- unsigned long size = FPDFTextObj_GetFontName(text, nullptr, 0); +- const char kExpectedFontName[] = "Liberation Serif"; +- ASSERT_EQ(sizeof(kExpectedFontName), size); +- std::vector font_name(size); +- ASSERT_EQ(size, FPDFTextObj_GetFontName(text, font_name.data(), size)); +- ASSERT_STREQ(kExpectedFontName, font_name.data()); +- +- // FPDFTextObj_GetFontName() negative testing. +- ASSERT_EQ(0U, FPDFTextObj_GetFontName(nullptr, nullptr, 0)); +- +- font_name.resize(2); +- font_name[0] = 'x'; +- font_name[1] = '\0'; +- size = FPDFTextObj_GetFontName(text, font_name.data(), font_name.size()); +- ASSERT_EQ(sizeof(kExpectedFontName), size); +- ASSERT_EQ(std::string("x"), std::string(font_name.data())); ++ ASSERT_TRUE(text); ++ FPDF_FONT font = FPDFTextObj_GetFont(text); ++ ASSERT_TRUE(font); ++ ++ // bad glyph argument. ++ ASSERT_FALSE(FPDFFont_GetGlyphPath(font, 1, 12.0f)); ++ ++ // good glyphpath ++ FPDF_GLYPHPATH gpath = FPDFFont_GetGlyphPath(font, 's', 12.0f); ++ ASSERT_TRUE(gpath); ++ ++ int count = FPDFGlyphPath_CountGlyphSegments(gpath); ++ ASSERT_GT(count, 0); ++ EXPECT_FALSE(FPDFGlyphPath_GetGlyphPathSegment(gpath, -1)); ++ EXPECT_FALSE(FPDFGlyphPath_GetGlyphPathSegment(gpath, count)); ++ ++ FPDF_PATHSEGMENT segment = FPDFGlyphPath_GetGlyphPathSegment(gpath, 1); ++ ASSERT_TRUE(segment); ++ EXPECT_EQ(FPDF_SEGMENT_BEZIERTO, FPDFPathSegment_GetType(segment)); + + UnloadPage(page); + } + +-TEST_F(FPDFEditEmbedderTest, TestFormGetObjects) { +- EXPECT_TRUE(OpenDocument("form_object.pdf")); ++TEST_F(FPDFEditEmbedderTest, FormGetObjects) { ++ ASSERT_TRUE(OpenDocument("form_object.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + ASSERT_EQ(1, FPDFPage_CountObjects(page)); + + FPDF_PAGEOBJECT form = FPDFPage_GetObject(page, 0); +- EXPECT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(form)); ++ ASSERT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(form)); + ASSERT_EQ(-1, FPDFFormObj_CountObjects(nullptr)); + ASSERT_EQ(2, FPDFFormObj_CountObjects(form)); + +@@ -2196,17 +2864,12 @@ TEST_F(FPDFEditEmbedderTest, TestFormGetObjects) { + ASSERT_EQ(nullptr, FPDFFormObj_GetObject(form, -1)); + ASSERT_EQ(nullptr, FPDFFormObj_GetObject(form, 2)); + +- // Reset the form object matrix to identity. +- auto* pPageObj = CPDFPageObjectFromFPDFPageObject(form); +- CPDF_FormObject* pFormObj = pPageObj->AsForm(); +- pFormObj->Transform(pFormObj->form_matrix().GetInverse()); +- +- // FPDFFormObj_GetMatrix() positive testing. ++ // FPDFPageObj_GetMatrix() positive testing for forms. + static constexpr FS_MATRIX kMatrix = {1.0f, 1.5f, 2.0f, 2.5f, 100.0f, 200.0f}; +- pFormObj->Transform(CFXMatrixFromFSMatrix(kMatrix)); ++ EXPECT_TRUE(FPDFPageObj_SetMatrix(form, &kMatrix)); + + FS_MATRIX matrix; +- EXPECT_TRUE(FPDFFormObj_GetMatrix(form, &matrix)); ++ EXPECT_TRUE(FPDFPageObj_GetMatrix(form, &matrix)); + EXPECT_FLOAT_EQ(kMatrix.a, matrix.a); + EXPECT_FLOAT_EQ(kMatrix.b, matrix.b); + EXPECT_FLOAT_EQ(kMatrix.c, matrix.c); +@@ -2214,22 +2877,57 @@ TEST_F(FPDFEditEmbedderTest, TestFormGetObjects) { + EXPECT_FLOAT_EQ(kMatrix.e, matrix.e); + EXPECT_FLOAT_EQ(kMatrix.f, matrix.f); + +- // FPDFFormObj_GetMatrix() negative testing. +- EXPECT_FALSE(FPDFFormObj_GetMatrix(nullptr, &matrix)); +- EXPECT_FALSE(FPDFFormObj_GetMatrix(form, nullptr)); +- EXPECT_FALSE(FPDFFormObj_GetMatrix(nullptr, nullptr)); ++ // FPDFPageObj_GetMatrix() negative testing for forms. ++ EXPECT_FALSE(FPDFPageObj_GetMatrix(form, nullptr)); + + UnloadPage(page); + } + +-// Tests adding text from standard font using FPDFText_LoadStandardFont. +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_AddStandardFontText2 DISABLED_AddStandardFontText2 +-#else +-#define MAYBE_AddStandardFontText2 AddStandardFontText2 ++TEST_F(FPDFEditEmbedderTest, ModifyFormObject) { ++ const char* orig_checksum = []() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "a637057185f50aac1aa5490f726aef95"; + #endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_AddStandardFontText2) { ++ return "34a9ec0a9581a7970e073c0bcc4ca676"; ++ }(); ++ const char* new_checksum = []() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "8ad9d79b02b609ff734e2a2195c96e2d"; ++#endif ++ return "609b5632a21c886fa93182dbc290bf7a"; ++ }(); ++ ++ ASSERT_TRUE(OpenDocument("form_object.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ASSERT_EQ(1, FPDFPage_CountObjects(page)); ++ ++ { ++ ScopedFPDFBitmap bitmap = RenderLoadedPage(page); ++ CompareBitmap(bitmap.get(), 62, 69, orig_checksum); ++ } ++ ++ FPDF_PAGEOBJECT form = FPDFPage_GetObject(page, 0); ++ ASSERT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(form)); ++ ++ FPDFPageObj_Transform(form, 0.5, 0, 0, 0.5, 0, 0); ++ EXPECT_TRUE(FPDFPage_GenerateContent(page)); ++ ++ { ++ ScopedFPDFBitmap bitmap = RenderLoadedPage(page); ++ CompareBitmap(bitmap.get(), 62, 69, new_checksum); ++ } ++ ++ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ VerifySavedDocument(62, 69, new_checksum); ++ ++ UnloadPage(page); ++} ++ ++// Tests adding text from standard font using FPDFText_LoadStandardFont. ++TEST_F(FPDFEditEmbedderTest, AddStandardFontText2) { + // Start with a blank page + ScopedFPDFPage page(FPDFPage_New(CreateNewDocument(), 0, 612, 792)); + +@@ -2241,20 +2939,12 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddStandardFontText2) { + FPDF_PAGEOBJECT text_object = + FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f); + EXPECT_TRUE(text_object); +- ScopedFPDFWideString text = +- GetFPDFWideString(L"I'm at the bottom of the page"); ++ ScopedFPDFWideString text = GetFPDFWideString(kBottomText); + EXPECT_TRUE(FPDFText_SetText(text_object, text.get())); + FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 20, 20); + FPDFPage_InsertObject(page.get(), text_object); + ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); +-#if defined(OS_MACOSX) +- const char md5[] = "a4dddc1a3930fa694bbff9789dab4161"; +-#elif defined(OS_WIN) +- const char md5[] = "08d1ff3e5a42801bee6077fd366bef00"; +-#else +- const char md5[] = "eacaa24573b8ce997b3882595f096f00"; +-#endif +- CompareBitmap(page_bitmap.get(), 612, 792, md5); ++ CompareBitmap(page_bitmap.get(), 612, 792, BottomTextChecksum()); + } + + TEST_F(FPDFEditEmbedderTest, LoadStandardFonts) { +@@ -2310,9 +3000,11 @@ TEST_F(FPDFEditEmbedderTest, GraphicsData) { + + // Check that the ExtGState was created + CPDF_Page* cpage = CPDFPageFromFPDFPage(page.get()); +- CPDF_Dictionary* graphics_dict = cpage->m_pResources->GetDictFor("ExtGState"); ++ RetainPtr graphics_dict = ++ cpage->GetResources()->GetDictFor("ExtGState"); + ASSERT_TRUE(graphics_dict); +- EXPECT_EQ(2u, graphics_dict->size()); ++ EXPECT_THAT(graphics_dict->GetKeys(), ++ UnorderedElementsAreArray({"FXE1", "FXE2"})); + + // Add a text object causing no change to the graphics dictionary + FPDF_PAGEOBJECT text1 = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f); +@@ -2321,7 +3013,8 @@ TEST_F(FPDFEditEmbedderTest, GraphicsData) { + EXPECT_TRUE(FPDFPageObj_SetFillColor(text1, 100, 100, 100, 255)); + FPDFPage_InsertObject(page.get(), text1); + EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); +- EXPECT_EQ(2u, graphics_dict->size()); ++ EXPECT_THAT(graphics_dict->GetKeys(), ++ UnorderedElementsAreArray({"FXE1", "FXE2"})); + + // Add a text object increasing the size of the graphics dictionary + FPDF_PAGEOBJECT text2 = +@@ -2330,7 +3023,8 @@ TEST_F(FPDFEditEmbedderTest, GraphicsData) { + FPDFPageObj_SetBlendMode(text2, "Darken"); + EXPECT_TRUE(FPDFPageObj_SetFillColor(text2, 0, 0, 255, 150)); + EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); +- EXPECT_EQ(3u, graphics_dict->size()); ++ EXPECT_THAT(graphics_dict->GetKeys(), ++ UnorderedElementsAreArray({"FXE1", "FXE2", "FXE3"})); + + // Add a path that should reuse graphics + FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(400, 100); +@@ -2338,7 +3032,8 @@ TEST_F(FPDFEditEmbedderTest, GraphicsData) { + EXPECT_TRUE(FPDFPageObj_SetFillColor(path, 200, 200, 100, 150)); + FPDFPage_InsertObject(page.get(), path); + EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); +- EXPECT_EQ(3u, graphics_dict->size()); ++ EXPECT_THAT(graphics_dict->GetKeys(), ++ UnorderedElementsAreArray({"FXE1", "FXE2", "FXE3"})); + + // Add a rect increasing the size of the graphics dictionary + FPDF_PAGEOBJECT rect2 = FPDFPageObj_CreateNewRect(10, 10, 100, 100); +@@ -2347,7 +3042,8 @@ TEST_F(FPDFEditEmbedderTest, GraphicsData) { + EXPECT_TRUE(FPDFPageObj_SetStrokeColor(rect2, 0, 0, 0, 200)); + FPDFPage_InsertObject(page.get(), rect2); + EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); +- EXPECT_EQ(4u, graphics_dict->size()); ++ EXPECT_THAT(graphics_dict->GetKeys(), ++ UnorderedElementsAreArray({"FXE1", "FXE2", "FXE3", "FXE4"})); + } + + TEST_F(FPDFEditEmbedderTest, DoubleGenerating) { +@@ -2363,9 +3059,11 @@ TEST_F(FPDFEditEmbedderTest, DoubleGenerating) { + + // Check the ExtGState + CPDF_Page* cpage = CPDFPageFromFPDFPage(page); +- CPDF_Dictionary* graphics_dict = cpage->m_pResources->GetDictFor("ExtGState"); ++ RetainPtr graphics_dict = ++ cpage->GetResources()->GetDictFor("ExtGState"); + ASSERT_TRUE(graphics_dict); +- EXPECT_EQ(2u, graphics_dict->size()); ++ EXPECT_THAT(graphics_dict->GetKeys(), ++ UnorderedElementsAreArray({"FXE1", "FXE2"})); + + // Check the bitmap + { +@@ -2374,10 +3072,12 @@ TEST_F(FPDFEditEmbedderTest, DoubleGenerating) { + "5384da3406d62360ffb5cac4476fff1c"); + } + +- // Never mind, my new favorite color is blue, increase alpha ++ // Never mind, my new favorite color is blue, increase alpha. ++ // The red graphics state goes away. + EXPECT_TRUE(FPDFPageObj_SetFillColor(rect, 0, 0, 255, 180)); + EXPECT_TRUE(FPDFPage_GenerateContent(page)); +- EXPECT_EQ(3u, graphics_dict->size()); ++ EXPECT_THAT(graphics_dict->GetKeys(), ++ UnorderedElementsAreArray({"FXE1", "FXE3"})); + + // Check that bitmap displays changed content + { +@@ -2388,14 +3088,18 @@ TEST_F(FPDFEditEmbedderTest, DoubleGenerating) { + + // And now generate, without changes + EXPECT_TRUE(FPDFPage_GenerateContent(page)); +- EXPECT_EQ(3u, graphics_dict->size()); ++ EXPECT_THAT(graphics_dict->GetKeys(), ++ UnorderedElementsAreArray({"FXE1", "FXE3"})); + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); + CompareBitmap(page_bitmap.get(), 612, 792, + "2e51656f5073b0bee611d9cd086aa09c"); + } + +- // Add some text to the page ++ // Add some text to the page, which starts out with no fonts. ++ RetainPtr font_dict = ++ cpage->GetResources()->GetDictFor("Font"); ++ EXPECT_FALSE(font_dict); + FPDF_PAGEOBJECT text_object = + FPDFPageObj_NewTextObj(document(), "Arial", 12.0f); + ScopedFPDFWideString text = +@@ -2404,14 +3108,19 @@ TEST_F(FPDFEditEmbedderTest, DoubleGenerating) { + FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 300, 300); + FPDFPage_InsertObject(page, text_object); + EXPECT_TRUE(FPDFPage_GenerateContent(page)); +- CPDF_Dictionary* font_dict = cpage->m_pResources->GetDictFor("Font"); ++ ++ // After generating the content, there should now be a font resource. ++ font_dict = cpage->GetResources()->GetDictFor("Font"); + ASSERT_TRUE(font_dict); +- EXPECT_EQ(1u, font_dict->size()); ++ EXPECT_THAT(graphics_dict->GetKeys(), ++ UnorderedElementsAreArray({"FXE1", "FXE3"})); ++ EXPECT_THAT(font_dict->GetKeys(), UnorderedElementsAreArray({"FXF1"})); + + // Generate yet again, check dicts are reasonably sized + EXPECT_TRUE(FPDFPage_GenerateContent(page)); +- EXPECT_EQ(3u, graphics_dict->size()); +- EXPECT_EQ(1u, font_dict->size()); ++ EXPECT_THAT(graphics_dict->GetKeys(), ++ UnorderedElementsAreArray({"FXE1", "FXE3"})); ++ EXPECT_THAT(font_dict->GetKeys(), UnorderedElementsAreArray({"FXF1"})); + FPDF_ClosePage(page); + } + +@@ -2427,21 +3136,21 @@ TEST_F(FPDFEditEmbedderTest, LoadSimpleType1Font) { + CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get()); + EXPECT_TRUE(typed_font->IsType1Font()); + +- const CPDF_Dictionary* font_dict = typed_font->GetFontDict(); +- EXPECT_EQ("Font", font_dict->GetStringFor("Type")); +- EXPECT_EQ("Type1", font_dict->GetStringFor("Subtype")); +- EXPECT_EQ("TimesNewRomanPS-BoldMT", font_dict->GetStringFor("BaseFont")); ++ RetainPtr font_dict = typed_font->GetFontDict(); ++ EXPECT_EQ("Font", font_dict->GetNameFor("Type")); ++ EXPECT_EQ("Type1", font_dict->GetNameFor("Subtype")); ++ EXPECT_EQ("Tinos-Bold", font_dict->GetNameFor("BaseFont")); + ASSERT_TRUE(font_dict->KeyExist("FirstChar")); + ASSERT_TRUE(font_dict->KeyExist("LastChar")); + EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar")); + EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar")); + +- const CPDF_Array* widths_array = font_dict->GetArrayFor("Widths"); ++ RetainPtr widths_array = font_dict->GetArrayFor("Widths"); + ASSERT_TRUE(widths_array); + ASSERT_EQ(224u, widths_array->size()); +- EXPECT_EQ(250, widths_array->GetNumberAt(0)); +- EXPECT_EQ(569, widths_array->GetNumberAt(11)); +- EXPECT_EQ(500, widths_array->GetNumberAt(223)); ++ EXPECT_EQ(250, widths_array->GetFloatAt(0)); ++ EXPECT_EQ(569, widths_array->GetFloatAt(11)); ++ EXPECT_EQ(500, widths_array->GetFloatAt(223)); + CheckFontDescriptor(font_dict, FPDF_FONT_TYPE1, true, false, span); + } + +@@ -2456,21 +3165,21 @@ TEST_F(FPDFEditEmbedderTest, LoadSimpleTrueTypeFont) { + CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get()); + EXPECT_TRUE(typed_font->IsTrueTypeFont()); + +- const CPDF_Dictionary* font_dict = typed_font->GetFontDict(); +- EXPECT_EQ("Font", font_dict->GetStringFor("Type")); +- EXPECT_EQ("TrueType", font_dict->GetStringFor("Subtype")); +- EXPECT_EQ("CourierNewPSMT", font_dict->GetStringFor("BaseFont")); ++ RetainPtr font_dict = typed_font->GetFontDict(); ++ EXPECT_EQ("Font", font_dict->GetNameFor("Type")); ++ EXPECT_EQ("TrueType", font_dict->GetNameFor("Subtype")); ++ EXPECT_EQ("Cousine-Regular", font_dict->GetNameFor("BaseFont")); + ASSERT_TRUE(font_dict->KeyExist("FirstChar")); + ASSERT_TRUE(font_dict->KeyExist("LastChar")); + EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar")); + EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar")); + +- const CPDF_Array* widths_array = font_dict->GetArrayFor("Widths"); ++ RetainPtr widths_array = font_dict->GetArrayFor("Widths"); + ASSERT_TRUE(widths_array); + ASSERT_EQ(224u, widths_array->size()); +- EXPECT_EQ(600, widths_array->GetNumberAt(33)); +- EXPECT_EQ(600, widths_array->GetNumberAt(74)); +- EXPECT_EQ(600, widths_array->GetNumberAt(223)); ++ EXPECT_EQ(600, widths_array->GetFloatAt(33)); ++ EXPECT_EQ(600, widths_array->GetFloatAt(74)); ++ EXPECT_EQ(600, widths_array->GetFloatAt(223)); + CheckFontDescriptor(font_dict, FPDF_FONT_TRUETYPE, false, false, span); + } + +@@ -2486,41 +3195,43 @@ TEST_F(FPDFEditEmbedderTest, LoadCIDType0Font) { + EXPECT_TRUE(typed_font->IsCIDFont()); + + // Check font dictionary entries +- const CPDF_Dictionary* font_dict = typed_font->GetFontDict(); +- EXPECT_EQ("Font", font_dict->GetStringFor("Type")); +- EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype")); +- EXPECT_EQ("TimesNewRomanPSMT-Identity-H", +- font_dict->GetStringFor("BaseFont")); +- EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding")); +- const CPDF_Array* descendant_array = ++ RetainPtr font_dict = typed_font->GetFontDict(); ++ EXPECT_EQ("Font", font_dict->GetNameFor("Type")); ++ EXPECT_EQ("Type0", font_dict->GetNameFor("Subtype")); ++ EXPECT_EQ("Tinos-Regular-Identity-H", font_dict->GetNameFor("BaseFont")); ++ EXPECT_EQ("Identity-H", font_dict->GetNameFor("Encoding")); ++ RetainPtr descendant_array = + font_dict->GetArrayFor("DescendantFonts"); + ASSERT_TRUE(descendant_array); + EXPECT_EQ(1u, descendant_array->size()); + + // Check the CIDFontDict +- const CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0); +- EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type")); +- EXPECT_EQ("CIDFontType0", cidfont_dict->GetStringFor("Subtype")); +- EXPECT_EQ("TimesNewRomanPSMT", cidfont_dict->GetStringFor("BaseFont")); +- const CPDF_Dictionary* cidinfo_dict = ++ RetainPtr cidfont_dict = ++ descendant_array->GetDictAt(0); ++ EXPECT_EQ("Font", cidfont_dict->GetNameFor("Type")); ++ EXPECT_EQ("CIDFontType0", cidfont_dict->GetNameFor("Subtype")); ++ EXPECT_EQ("Tinos-Regular", cidfont_dict->GetNameFor("BaseFont")); ++ RetainPtr cidinfo_dict = + cidfont_dict->GetDictFor("CIDSystemInfo"); + ASSERT_TRUE(cidinfo_dict); +- const CPDF_Object* registry = cidinfo_dict->GetObjectFor("Registry"); ++ RetainPtr registry = ++ cidinfo_dict->GetObjectFor("Registry"); + ASSERT_TRUE(registry); + EXPECT_EQ(CPDF_Object::kString, registry->GetType()); + EXPECT_EQ("Adobe", registry->GetString()); +- const CPDF_Object* ordering = cidinfo_dict->GetObjectFor("Ordering"); ++ RetainPtr ordering = ++ cidinfo_dict->GetObjectFor("Ordering"); + ASSERT_TRUE(ordering); + EXPECT_EQ(CPDF_Object::kString, ordering->GetType()); + EXPECT_EQ("Identity", ordering->GetString()); +- EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement")); +- CheckFontDescriptor(cidfont_dict, FPDF_FONT_TYPE1, false, false, span); ++ EXPECT_EQ(0, cidinfo_dict->GetFloatFor("Supplement")); ++ CheckFontDescriptor(cidfont_dict.Get(), FPDF_FONT_TYPE1, false, false, span); + + // Check widths +- const CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W"); ++ RetainPtr widths_array = cidfont_dict->GetArrayFor("W"); + ASSERT_TRUE(widths_array); + EXPECT_GT(widths_array->size(), 1u); +- CheckCompositeFontWidths(widths_array, typed_font); ++ CheckCompositeFontWidths(widths_array.Get(), typed_font); + } + + TEST_F(FPDFEditEmbedderTest, LoadCIDType2Font) { +@@ -2535,52 +3246,48 @@ TEST_F(FPDFEditEmbedderTest, LoadCIDType2Font) { + EXPECT_TRUE(typed_font->IsCIDFont()); + + // Check font dictionary entries +- const CPDF_Dictionary* font_dict = typed_font->GetFontDict(); +- EXPECT_EQ("Font", font_dict->GetStringFor("Type")); +- EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype")); +- EXPECT_EQ("Arial-ItalicMT", font_dict->GetStringFor("BaseFont")); +- EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding")); +- const CPDF_Array* descendant_array = ++ RetainPtr font_dict = typed_font->GetFontDict(); ++ EXPECT_EQ("Font", font_dict->GetNameFor("Type")); ++ EXPECT_EQ("Type0", font_dict->GetNameFor("Subtype")); ++ EXPECT_EQ("Arimo-Italic", font_dict->GetNameFor("BaseFont")); ++ EXPECT_EQ("Identity-H", font_dict->GetNameFor("Encoding")); ++ RetainPtr descendant_array = + font_dict->GetArrayFor("DescendantFonts"); + ASSERT_TRUE(descendant_array); + EXPECT_EQ(1u, descendant_array->size()); + + // Check the CIDFontDict +- const CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0); +- EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type")); +- EXPECT_EQ("CIDFontType2", cidfont_dict->GetStringFor("Subtype")); +- EXPECT_EQ("Arial-ItalicMT", cidfont_dict->GetStringFor("BaseFont")); +- const CPDF_Dictionary* cidinfo_dict = ++ RetainPtr cidfont_dict = ++ descendant_array->GetDictAt(0); ++ EXPECT_EQ("Font", cidfont_dict->GetNameFor("Type")); ++ EXPECT_EQ("CIDFontType2", cidfont_dict->GetNameFor("Subtype")); ++ EXPECT_EQ("Arimo-Italic", cidfont_dict->GetNameFor("BaseFont")); ++ RetainPtr cidinfo_dict = + cidfont_dict->GetDictFor("CIDSystemInfo"); + ASSERT_TRUE(cidinfo_dict); +- EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry")); +- EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering")); +- EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement")); +- CheckFontDescriptor(cidfont_dict, FPDF_FONT_TRUETYPE, false, true, span); ++ EXPECT_EQ("Adobe", cidinfo_dict->GetByteStringFor("Registry")); ++ EXPECT_EQ("Identity", cidinfo_dict->GetByteStringFor("Ordering")); ++ EXPECT_EQ(0, cidinfo_dict->GetFloatFor("Supplement")); ++ CheckFontDescriptor(cidfont_dict.Get(), FPDF_FONT_TRUETYPE, false, true, ++ span); + + // Check widths +- const CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W"); ++ RetainPtr widths_array = cidfont_dict->GetArrayFor("W"); + ASSERT_TRUE(widths_array); +- CheckCompositeFontWidths(widths_array, typed_font); ++ CheckCompositeFontWidths(widths_array.Get(), typed_font); + } + + TEST_F(FPDFEditEmbedderTest, NormalizeNegativeRotation) { + // Load document with a -90 degree rotation +- EXPECT_TRUE(OpenDocument("bug_713197.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_713197.pdf")); + FPDF_PAGE page = LoadPage(0); +- EXPECT_NE(nullptr, page); ++ EXPECT_TRUE(page); + + EXPECT_EQ(3, FPDFPage_GetRotation(page)); + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_AddTrueTypeFontText DISABLED_AddTrueTypeFontText +-#else +-#define MAYBE_AddTrueTypeFontText AddTrueTypeFontText +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_AddTrueTypeFontText) { ++TEST_F(FPDFEditEmbedderTest, AddTrueTypeFontText) { + // Start with a blank page + FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792); + { +@@ -2595,20 +3302,12 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddTrueTypeFontText) { + FPDF_PAGEOBJECT text_object = + FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f); + EXPECT_TRUE(text_object); +- ScopedFPDFWideString text = +- GetFPDFWideString(L"I am testing my loaded font, WEE."); ++ ScopedFPDFWideString text = GetFPDFWideString(kLoadedFontText); + EXPECT_TRUE(FPDFText_SetText(text_object, text.get())); + FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 400, 400); + FPDFPage_InsertObject(page, text_object); + ScopedFPDFBitmap page_bitmap = RenderPage(page); +-#if defined(OS_MACOSX) +- const char md5[] = "17d2b6cd574cf66170b09c8927529a94"; +-#elif defined(OS_WIN) +- const char md5[] = "d60ba39f9698e32360d99e727dd93165"; +-#else +- const char md5[] = "70592859010ffbf532a2237b8118bcc4"; +-#endif +- CompareBitmap(page_bitmap.get(), 612, 792, md5); ++ CompareBitmap(page_bitmap.get(), 612, 792, LoadedFontTextChecksum()); + + // Add some more text, same font + FPDF_PAGEOBJECT text_object2 = +@@ -2619,20 +3318,22 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddTrueTypeFontText) { + FPDFPage_InsertObject(page, text_object2); + } + ScopedFPDFBitmap page_bitmap2 = RenderPage(page); +-#if defined(OS_MACOSX) +- const char md5_2[] = "8eded4193ff1f0f77b8b600a825e97ea"; +-#elif defined(OS_WIN) +- const char md5_2[] = "2199b579c49ab5f80c246a586a80ee90"; ++ const char* insert_true_type_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "683f4a385a891494100192cb338b11f0"; ++#if BUILDFLAG(IS_APPLE) ++ return "c7e2271a7f30e5b919a13ead47cea105"; + #else +- const char md5_2[] = "c1d10cce1761c4a998a16b2562030568"; ++ return "683f4a385a891494100192cb338b11f0"; + #endif +- CompareBitmap(page_bitmap2.get(), 612, 792, md5_2); ++ }(); ++ CompareBitmap(page_bitmap2.get(), 612, 792, insert_true_type_checksum); + + EXPECT_TRUE(FPDFPage_GenerateContent(page)); + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + FPDF_ClosePage(page); + +- VerifySavedDocument(612, 792, md5_2); ++ VerifySavedDocument(612, 792, insert_true_type_checksum); + } + + TEST_F(FPDFEditEmbedderTest, TransformAnnot) { +@@ -2655,21 +3356,16 @@ TEST_F(FPDFEditEmbedderTest, TransformAnnot) { + } + + // TODO(npm): Add tests using Japanese fonts in other OS. +-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_ +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_AddCIDFontText DISABLED_AddCIDFontText +-#else +-#define MAYBE_AddCIDFontText AddCIDFontText +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_AddCIDFontText) { ++#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ++TEST_F(FPDFEditEmbedderTest, AddCIDFontText) { + // Start with a blank page + FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792); + CFX_Font CIDfont; + { + // First, get the data from the font +- CIDfont.LoadSubst("IPAGothic", 1, 0, 400, 0, 932, 0); +- EXPECT_EQ("IPAGothic", CIDfont.GetFaceName()); ++ CIDfont.LoadSubst("Noto Sans CJK JP", true, 0, 400, 0, ++ FX_CodePage::kShiftJIS, false); ++ EXPECT_EQ("Noto Sans CJK JP", CIDfont.GetFaceName()); + pdfium::span span = CIDfont.GetFontSpan(); + + // Load the data into a FPDF_Font. +@@ -2701,10 +3397,10 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddCIDFontText) { + } + + // Check that the text renders properly. +- const char md5[] = "5159a72903fe57bf0cf645c894de8a74"; ++ static constexpr char kChecksum[] = "84d31d11b76845423a2cfc1879c0fbb9"; + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 612, 792, md5); ++ CompareBitmap(page_bitmap.get(), 612, 792, kChecksum); + } + + // Save the document, close the page. +@@ -2712,27 +3408,25 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddCIDFontText) { + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + FPDF_ClosePage(page); + +- VerifySavedDocument(612, 792, md5); ++ VerifySavedDocument(612, 792, kChecksum); + } +-#endif // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_ +- +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_SaveAndRender DISABLED_SaveAndRender +-#else +-#define MAYBE_SaveAndRender SaveAndRender +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_SaveAndRender) { +- const char md5[] = "3c20472b0552c0c22b88ab1ed8c6202b"; ++#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ++ ++TEST_F(FPDFEditEmbedderTest, SaveAndRender) { ++ const char* checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "9a78649e85e69d220c22e0fc316da740"; ++ return "3c20472b0552c0c22b88ab1ed8c6202b"; ++ }(); + { +- EXPECT_TRUE(OpenDocument("bug_779.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_779.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_NE(nullptr, page); + +- // Now add a more complex blue path. ++ // Now add a more complex green path. + FPDF_PAGEOBJECT green_path = FPDFPageObj_CreateNewPath(20, 20); + EXPECT_TRUE(FPDFPageObj_SetFillColor(green_path, 0, 255, 0, 200)); +- // TODO(npm): stroking will cause the MD5s to differ. ++ // TODO(npm): stroking will cause the checksums to differ. + EXPECT_TRUE(FPDFPath_SetDrawMode(green_path, FPDF_FILLMODE_WINDING, 0)); + EXPECT_TRUE(FPDFPath_LineTo(green_path, 20, 63)); + EXPECT_TRUE(FPDFPath_BezierTo(green_path, 55, 55, 78, 78, 90, 90)); +@@ -2742,7 +3436,7 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_SaveAndRender) { + EXPECT_TRUE(FPDFPath_Close(green_path)); + FPDFPage_InsertObject(page, green_path); + ScopedFPDFBitmap page_bitmap = RenderLoadedPage(page); +- CompareBitmap(page_bitmap.get(), 612, 792, md5); ++ CompareBitmap(page_bitmap.get(), 612, 792, checksum); + + // Now save the result, closing the page and document + EXPECT_TRUE(FPDFPage_GenerateContent(page)); +@@ -2750,12 +3444,12 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_SaveAndRender) { + UnloadPage(page); + } + +- VerifySavedDocument(612, 792, md5); ++ VerifySavedDocument(612, 792, checksum); + } + + TEST_F(FPDFEditEmbedderTest, AddMark) { + // Load document with some text. +- EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf")); ++ ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -2778,6 +3472,7 @@ TEST_F(FPDFEditEmbedderTest, AddMark) { + // Re-open the file and check the new mark is present. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + + CheckMarkCounts(saved_page, 1, 19, 8, 4, 9, 2); + +@@ -2785,30 +3480,16 @@ TEST_F(FPDFEditEmbedderTest, AddMark) { + CloseSavedDocument(); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_AddMarkCompressedStream DISABLED_AddMarkCompressedStream +-#else +-#define MAYBE_AddMarkCompressedStream AddMarkCompressedStream +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_AddMarkCompressedStream) { +-#if defined(OS_MACOSX) +- const char kOriginalMD5[] = "b90475ca64d1348c3bf5e2b77ad9187a"; +-#elif defined(OS_WIN) +- const char kOriginalMD5[] = "795b7ce1626931aa06af0fa23b7d80bb"; +-#else +- const char kOriginalMD5[] = "2baa4c0e1758deba1b9c908e1fbd04ed"; +-#endif +- ++TEST_F(FPDFEditEmbedderTest, AddMarkCompressedStream) { + // Load document with some text in a compressed stream. +- EXPECT_TRUE(OpenDocument("hello_world_compressed_stream.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world_compressed_stream.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + // Render and check there are no marks. + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum()); + } + CheckMarkCounts(page, 0, 2, 0, 0, 0, 0); + +@@ -2822,7 +3503,7 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddMarkCompressedStream) { + // Render and check there is 1 mark. + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum()); + } + CheckMarkCounts(page, 0, 2, 0, 0, 0, 1); + +@@ -2834,10 +3515,11 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddMarkCompressedStream) { + // Re-open the file and check the new mark is present. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + + { + ScopedFPDFBitmap page_bitmap = RenderPage(saved_page); +- CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5); ++ CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum()); + } + CheckMarkCounts(saved_page, 0, 2, 0, 0, 0, 1); + +@@ -2847,7 +3529,7 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddMarkCompressedStream) { + + TEST_F(FPDFEditEmbedderTest, SetMarkParam) { + // Load document with some text. +- EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf")); ++ ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -2896,6 +3578,7 @@ TEST_F(FPDFEditEmbedderTest, SetMarkParam) { + // Re-open the file and cerify "Position" still maps to "End". + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + + CheckMarkCounts(saved_page, 1, kExpectedObjectCount, 8, 4, 9, 1); + page_object = FPDFPage_GetObject(saved_page, 18); +@@ -2909,13 +3592,7 @@ TEST_F(FPDFEditEmbedderTest, SetMarkParam) { + CloseSavedDocument(); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_AddMarkedText DISABLED_AddMarkedText +-#else +-#define MAYBE_AddMarkedText AddMarkedText +-#endif +-TEST_F(FPDFEditEmbedderTest, MAYBE_AddMarkedText) { ++TEST_F(FPDFEditEmbedderTest, AddMarkedText) { + // Start with a blank page. + FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792); + +@@ -2931,8 +3608,7 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddMarkedText) { + FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f); + + EXPECT_TRUE(text_object); +- ScopedFPDFWideString text1 = +- GetFPDFWideString(L"I am testing my loaded font, WEE."); ++ ScopedFPDFWideString text1 = GetFPDFWideString(kLoadedFontText); + EXPECT_TRUE(FPDFText_SetText(text_object, text1.get())); + FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 400, 400); + FPDFPage_InsertObject(page, text_object); +@@ -2992,17 +3668,10 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddMarkedText) { + EXPECT_EQ(kBlobLen, out_buffer_len); + EXPECT_EQ(0, memcmp(block_value, buffer, kBlobLen)); + +-// Render and check the bitmap is the expected one. +-#if defined(OS_MACOSX) +- const char md5[] = "17d2b6cd574cf66170b09c8927529a94"; +-#elif defined(OS_WIN) +- const char md5[] = "d60ba39f9698e32360d99e727dd93165"; +-#else +- const char md5[] = "70592859010ffbf532a2237b8118bcc4"; +-#endif ++ // Render and check the bitmap is the expected one. + { + ScopedFPDFBitmap page_bitmap = RenderPage(page); +- CompareBitmap(page_bitmap.get(), 612, 792, md5); ++ CompareBitmap(page_bitmap.get(), 612, 792, LoadedFontTextChecksum()); + } + + // Now save the result. +@@ -3015,6 +3684,7 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddMarkedText) { + // Re-open the file and check the changes were kept in the saved .pdf. + ASSERT_TRUE(OpenSavedDocument()); + FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); + EXPECT_EQ(1, FPDFPage_CountObjects(saved_page)); + + text_object = FPDFPage_GetObject(saved_page, 0); +@@ -3034,7 +3704,7 @@ TEST_F(FPDFEditEmbedderTest, MAYBE_AddMarkedText) { + } + + TEST_F(FPDFEditEmbedderTest, MarkGetName) { +- EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf")); ++ ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 18); +@@ -3067,7 +3737,7 @@ TEST_F(FPDFEditEmbedderTest, MarkGetName) { + } + + TEST_F(FPDFEditEmbedderTest, MarkGetParamKey) { +- EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf")); ++ ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 18); +@@ -3107,7 +3777,7 @@ TEST_F(FPDFEditEmbedderTest, MarkGetParamKey) { + } + + TEST_F(FPDFEditEmbedderTest, MarkGetIntParam) { +- EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf")); ++ ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 8); +@@ -3143,7 +3813,7 @@ TEST_F(FPDFEditEmbedderTest, MarkGetIntParam) { + } + + TEST_F(FPDFEditEmbedderTest, MarkGetStringParam) { +- EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf")); ++ ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 18); +@@ -3190,7 +3860,68 @@ TEST_F(FPDFEditEmbedderTest, MarkGetStringParam) { + UnloadPage(page); + } + +-TEST_F(FPDFEditEmbedderTest, ExtractImageBitmap) { ++// See also FPDFStructTreeEmbedderTest.GetMarkedContentID, which traverses the ++// marked contents using FPDF_StructTree_GetForPage() and related API. ++TEST_F(FPDFEditEmbedderTest, TraverseMarkedContentID) { ++ ASSERT_TRUE(OpenDocument("marked_content_id.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ ASSERT_EQ(2, FPDFPage_CountObjects(page)); ++ FPDF_PAGEOBJECT object1 = FPDFPage_GetObject(page, 0); ++ ASSERT_TRUE(object1); ++ ASSERT_EQ(1, FPDFPageObj_CountMarks(object1)); ++ ++ FPDF_PAGEOBJECTMARK mark11 = FPDFPageObj_GetMark(object1, 0); ++ ASSERT_TRUE(mark11); ++ unsigned long len = 0; ++ unsigned short buf[40]; ++ ASSERT_TRUE(FPDFPageObjMark_GetName(mark11, buf, sizeof(buf), &len)); ++ EXPECT_EQ(18u, len); ++ EXPECT_EQ(L"Artifact", GetPlatformWString(buf)); ++ ASSERT_EQ(2, FPDFPageObjMark_CountParams(mark11)); ++ ASSERT_TRUE(FPDFPageObjMark_GetParamKey(mark11, 0, buf, sizeof(buf), &len)); ++ EXPECT_EQ(10u, len); ++ EXPECT_EQ(L"BBox", GetPlatformWString(buf)); ++ EXPECT_EQ(FPDF_OBJECT_ARRAY, ++ FPDFPageObjMark_GetParamValueType(mark11, "BBox")); ++ ASSERT_TRUE(FPDFPageObjMark_GetParamKey(mark11, 1, buf, sizeof(buf), &len)); ++ EXPECT_EQ(10u, len); ++ EXPECT_EQ(L"Type", GetPlatformWString(buf)); ++ EXPECT_EQ(FPDF_OBJECT_NAME, ++ FPDFPageObjMark_GetParamValueType(mark11, "Type")); ++ ++ FPDF_PAGEOBJECT object2 = FPDFPage_GetObject(page, 1); ++ ASSERT_TRUE(object2); ++ ASSERT_EQ(2, FPDFPageObj_CountMarks(object2)); ++ EXPECT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(object2)); ++ ++ FPDF_PAGEOBJECTMARK mark21 = FPDFPageObj_GetMark(object2, 0); ++ ASSERT_TRUE(mark21); ++ ASSERT_TRUE(FPDFPageObjMark_GetName(mark21, buf, sizeof(buf), &len)); ++ EXPECT_EQ(14u, len); ++ EXPECT_EQ(L"Figure", GetPlatformWString(buf)); ++ ASSERT_EQ(1, FPDFPageObjMark_CountParams(mark21)); ++ ASSERT_TRUE(FPDFPageObjMark_GetParamKey(mark21, 0, buf, sizeof(buf), &len)); ++ EXPECT_EQ(10u, len); ++ EXPECT_EQ(L"MCID", GetPlatformWString(buf)); ++ ASSERT_EQ(FPDF_OBJECT_NUMBER, ++ FPDFPageObjMark_GetParamValueType(mark21, "MCID")); ++ int mcid = -1; ++ ASSERT_TRUE(FPDFPageObjMark_GetParamIntValue(mark21, "MCID", &mcid)); ++ EXPECT_EQ(0, mcid); ++ ++ FPDF_PAGEOBJECTMARK mark22 = FPDFPageObj_GetMark(object2, 1); ++ ASSERT_TRUE(mark22); ++ ASSERT_TRUE(FPDFPageObjMark_GetName(mark22, buf, sizeof(buf), &len)); ++ EXPECT_EQ(18u, len); ++ EXPECT_EQ(L"ClipSpan", GetPlatformWString(buf)); ++ EXPECT_EQ(0, FPDFPageObjMark_CountParams(mark22)); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditEmbedderTest, GetBitmap) { + ASSERT_TRUE(OpenDocument("embedded_images.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); +@@ -3200,51 +3931,108 @@ TEST_F(FPDFEditEmbedderTest, ExtractImageBitmap) { + EXPECT_NE(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); + EXPECT_FALSE(FPDFImageObj_GetBitmap(obj)); + +- obj = FPDFPage_GetObject(page, 33); +- ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); +- FPDF_BITMAP bitmap = FPDFImageObj_GetBitmap(obj); +- EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap)); +- CompareBitmap(bitmap, 109, 88, "cb3637934bb3b95a6e4ae1ea9eb9e56e"); +- FPDFBitmap_Destroy(bitmap); ++ { ++ obj = FPDFPage_GetObject(page, 33); ++ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); ++ ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj)); ++ EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get())); ++ CompareBitmap(bitmap.get(), 109, 88, kEmbeddedImage33Checksum); ++ } + +- obj = FPDFPage_GetObject(page, 34); +- ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); +- bitmap = FPDFImageObj_GetBitmap(obj); +- EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap)); +- CompareBitmap(bitmap, 103, 75, "c8d51fa6821ceb2a67f08446ff236c40"); +- FPDFBitmap_Destroy(bitmap); ++ { ++ obj = FPDFPage_GetObject(page, 34); ++ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); ++ ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj)); ++ EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get())); ++ CompareBitmap(bitmap.get(), 103, 75, "c8d51fa6821ceb2a67f08446ff236c40"); ++ } + +- obj = FPDFPage_GetObject(page, 35); +- ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); +- bitmap = FPDFImageObj_GetBitmap(obj); +- EXPECT_EQ(FPDFBitmap_Gray, FPDFBitmap_GetFormat(bitmap)); +- CompareBitmap(bitmap, 92, 68, "9c6d76cb1e37ef8514f9455d759391f3"); +- FPDFBitmap_Destroy(bitmap); ++ { ++ obj = FPDFPage_GetObject(page, 35); ++ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); ++ ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj)); ++ EXPECT_EQ(FPDFBitmap_Gray, FPDFBitmap_GetFormat(bitmap.get())); ++ CompareBitmap(bitmap.get(), 92, 68, "9c6d76cb1e37ef8514f9455d759391f3"); ++ } + +- obj = FPDFPage_GetObject(page, 36); +- ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); +- bitmap = FPDFImageObj_GetBitmap(obj); +- EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap)); +- CompareBitmap(bitmap, 79, 60, "f4e72fb783a01c7b4614cdc25eaa98ac"); +- FPDFBitmap_Destroy(bitmap); ++ { ++ obj = FPDFPage_GetObject(page, 36); ++ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); ++ ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj)); ++ EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get())); ++ CompareBitmap(bitmap.get(), 79, 60, "f4e72fb783a01c7b4614cdc25eaa98ac"); ++ } + +- obj = FPDFPage_GetObject(page, 37); +- ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); +- bitmap = FPDFImageObj_GetBitmap(obj); +- EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap)); +- CompareBitmap(bitmap, 126, 106, "2cf9e66414c72461f4ccbf9cdebdfa1b"); +- FPDFBitmap_Destroy(bitmap); ++ { ++ obj = FPDFPage_GetObject(page, 37); ++ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); ++ ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj)); ++ EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get())); ++ CompareBitmap(bitmap.get(), 126, 106, "2cf9e66414c72461f4ccbf9cdebdfa1b"); ++ } + +- obj = FPDFPage_GetObject(page, 38); ++ { ++ obj = FPDFPage_GetObject(page, 38); ++ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); ++ ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj)); ++ EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get())); ++ CompareBitmap(bitmap.get(), 194, 119, "a8f3a126cec274dab8242fd2ccdc1b8b"); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditEmbedderTest, GetBitmapIgnoresSetMatrix) { ++ ASSERT_TRUE(OpenDocument("embedded_images.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ASSERT_EQ(39, FPDFPage_CountObjects(page)); ++ ++ FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 33); + ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); +- bitmap = FPDFImageObj_GetBitmap(obj); +- EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap)); +- CompareBitmap(bitmap, 194, 119, "a8f3a126cec274dab8242fd2ccdc1b8b"); +- FPDFBitmap_Destroy(bitmap); ++ ++ { ++ // Render |obj| as is. ++ ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj)); ++ EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get())); ++ CompareBitmap(bitmap.get(), 109, 88, kEmbeddedImage33Checksum); ++ } ++ ++ // Check the matrix for |obj|. ++ FS_MATRIX matrix; ++ EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix)); ++ EXPECT_FLOAT_EQ(53.0f, matrix.a); ++ EXPECT_FLOAT_EQ(0.0f, matrix.b); ++ EXPECT_FLOAT_EQ(0.0f, matrix.c); ++ EXPECT_FLOAT_EQ(43.0f, matrix.d); ++ EXPECT_FLOAT_EQ(72.0f, matrix.e); ++ EXPECT_FLOAT_EQ(646.510009765625f, matrix.f); ++ ++ // Modify the matrix for |obj|. ++ matrix.a = 120.0; ++ EXPECT_TRUE(FPDFPageObj_SetMatrix(obj, &matrix)); ++ ++ // Make sure the matrix modification took place. ++ EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix)); ++ EXPECT_FLOAT_EQ(120.0f, matrix.a); ++ EXPECT_FLOAT_EQ(0.0f, matrix.b); ++ EXPECT_FLOAT_EQ(0.0f, matrix.c); ++ EXPECT_FLOAT_EQ(43.0f, matrix.d); ++ EXPECT_FLOAT_EQ(72.0f, matrix.e); ++ EXPECT_FLOAT_EQ(646.510009765625f, matrix.f); ++ ++ { ++ // Render |obj| again. Note that the FPDFPageObj_SetMatrix() call has no ++ // effect. ++ ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj)); ++ EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get())); ++ CompareBitmap(bitmap.get(), 109, 88, kEmbeddedImage33Checksum); ++ } ++ + UnloadPage(page); + } + +-TEST_F(FPDFEditEmbedderTest, ExtractJBigImageBitmap) { ++TEST_F(FPDFEditEmbedderTest, GetBitmapForJBigImage) { + ASSERT_TRUE(OpenDocument("bug_631912.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); +@@ -3262,8 +4050,148 @@ TEST_F(FPDFEditEmbedderTest, ExtractJBigImageBitmap) { + UnloadPage(page); + } + ++TEST_F(FPDFEditEmbedderTest, GetBitmapIgnoresSMask) { ++ ASSERT_TRUE(OpenDocument("matte.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ constexpr int kExpectedObjects = 4; ++ ASSERT_EQ(kExpectedObjects, FPDFPage_CountObjects(page)); ++ ++ for (int i = 0; i < kExpectedObjects; ++i) { ++ FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i); ++ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); ++ ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj)); ++ ASSERT_TRUE(bitmap); ++ EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get())); ++ CompareBitmap(bitmap.get(), 50, 50, "46c9a1dbe0b44765ce46017ad629a2fe"); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapHandlesSetMatrix) { ++ ASSERT_TRUE(OpenDocument("embedded_images.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ASSERT_EQ(39, FPDFPage_CountObjects(page)); ++ ++ FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 33); ++ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); ++ ++ { ++ // Render `obj` as is. ++ ScopedFPDFBitmap bitmap( ++ FPDFImageObj_GetRenderedBitmap(document(), page, obj)); ++ EXPECT_EQ(FPDFBitmap_BGRA, FPDFBitmap_GetFormat(bitmap.get())); ++ const char* checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "3b51fc066ee18efbf70bab0501763603"; ++ return "582ca300e003f512d7b552c7b5b45d2e"; ++ }(); ++ CompareBitmap(bitmap.get(), 53, 43, checksum); ++ } ++ ++ // Check the matrix for `obj`. ++ FS_MATRIX matrix; ++ EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix)); ++ EXPECT_FLOAT_EQ(53.0f, matrix.a); ++ EXPECT_FLOAT_EQ(0.0f, matrix.b); ++ EXPECT_FLOAT_EQ(0.0f, matrix.c); ++ EXPECT_FLOAT_EQ(43.0f, matrix.d); ++ EXPECT_FLOAT_EQ(72.0f, matrix.e); ++ EXPECT_FLOAT_EQ(646.510009765625f, matrix.f); ++ ++ // Modify the matrix for `obj`. ++ matrix.a = 120.0; ++ EXPECT_TRUE(FPDFPageObj_SetMatrix(obj, &matrix)); ++ ++ // Make sure the matrix modification took place. ++ EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix)); ++ EXPECT_FLOAT_EQ(120.0f, matrix.a); ++ EXPECT_FLOAT_EQ(0.0f, matrix.b); ++ EXPECT_FLOAT_EQ(0.0f, matrix.c); ++ EXPECT_FLOAT_EQ(43.0f, matrix.d); ++ EXPECT_FLOAT_EQ(72.0f, matrix.e); ++ EXPECT_FLOAT_EQ(646.510009765625f, matrix.f); ++ ++ { ++ // Render `obj` again. Note that the FPDFPageObj_SetMatrix() call has an ++ // effect. ++ ScopedFPDFBitmap bitmap( ++ FPDFImageObj_GetRenderedBitmap(document(), page, obj)); ++ EXPECT_EQ(FPDFBitmap_BGRA, FPDFBitmap_GetFormat(bitmap.get())); ++ const char* checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "1003585870ad0fe37baf1c5bb3f5fd76"; ++ return "0824c16dcf2dfcef44b45d88db1fddce"; ++ }(); ++ CompareBitmap(bitmap.get(), 120, 43, checksum); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapHandlesSMask) { ++ ASSERT_TRUE(OpenDocument("matte.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ constexpr int kExpectedObjects = 4; ++ ASSERT_EQ(kExpectedObjects, FPDFPage_CountObjects(page)); ++ ++ const char* smask_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "0653a18f3bf9b4d8413a2aa10bc11c38"; ++ return "5a3ae4a660ce919e29c42ec2258142f1"; ++ }(); ++ const char* no_smask_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "d568afc908d595224d804448d5d3672f"; ++ return "67504e83f5d78214ea00efc19082c5c1"; ++ }(); ++ ++ for (int i = 0; i < kExpectedObjects; ++i) { ++ FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i); ++ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); ++ ScopedFPDFBitmap bitmap( ++ FPDFImageObj_GetRenderedBitmap(document(), page, obj)); ++ ASSERT_TRUE(bitmap); ++ EXPECT_EQ(FPDFBitmap_BGRA, FPDFBitmap_GetFormat(bitmap.get())); ++ if (i == 0) ++ CompareBitmap(bitmap.get(), 40, 60, smask_checksum); ++ else ++ CompareBitmap(bitmap.get(), 40, 60, no_smask_checksum); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapBadParams) { ++ ASSERT_TRUE(OpenDocument("embedded_images.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 33); ++ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); ++ ++ // Test various null parameters. ++ EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(nullptr, nullptr, nullptr)); ++ EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(document(), nullptr, nullptr)); ++ EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(nullptr, page, nullptr)); ++ EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(nullptr, nullptr, obj)); ++ EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(document(), page, nullptr)); ++ EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(nullptr, page, obj)); ++ ++ // Test mismatch between document and page parameters. ++ ScopedFPDFDocument new_document(FPDF_CreateNewDocument()); ++ EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(new_document.get(), page, obj)); ++ ++ UnloadPage(page); ++} ++ + TEST_F(FPDFEditEmbedderTest, GetImageData) { +- EXPECT_TRUE(OpenDocument("embedded_images.pdf")); ++ ASSERT_TRUE(OpenDocument("embedded_images.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + ASSERT_EQ(39, FPDFPage_CountObjects(page)); +@@ -3274,18 +4202,16 @@ TEST_F(FPDFEditEmbedderTest, GetImageData) { + + // Check that the raw image data has the correct length and hash value. + unsigned long len = FPDFImageObj_GetImageDataRaw(obj, nullptr, 0); +- std::vector buf(len); ++ std::vector buf(len); + EXPECT_EQ(4091u, FPDFImageObj_GetImageDataRaw(obj, buf.data(), len)); +- EXPECT_EQ("f73802327d2e88e890f653961bcda81a", +- GenerateMD5Base16(reinterpret_cast(buf.data()), len)); ++ EXPECT_EQ("f73802327d2e88e890f653961bcda81a", GenerateMD5Base16(buf)); + + // Check that the decoded image data has the correct length and hash value. + len = FPDFImageObj_GetImageDataDecoded(obj, nullptr, 0); + buf.clear(); + buf.resize(len); + EXPECT_EQ(28776u, FPDFImageObj_GetImageDataDecoded(obj, buf.data(), len)); +- EXPECT_EQ("cb3637934bb3b95a6e4ae1ea9eb9e56e", +- GenerateMD5Base16(reinterpret_cast(buf.data()), len)); ++ EXPECT_EQ(kEmbeddedImage33Checksum, GenerateMD5Base16(buf)); + + // Retrieve an image object with DCTDecode-encoded data stream. + obj = FPDFPage_GetObject(page, 37); +@@ -3296,8 +4222,7 @@ TEST_F(FPDFEditEmbedderTest, GetImageData) { + buf.clear(); + buf.resize(len); + EXPECT_EQ(4370u, FPDFImageObj_GetImageDataRaw(obj, buf.data(), len)); +- EXPECT_EQ("6aae1f3710335023a9e12191be66b64b", +- GenerateMD5Base16(reinterpret_cast(buf.data()), len)); ++ EXPECT_EQ("6aae1f3710335023a9e12191be66b64b", GenerateMD5Base16(buf)); + + // Check that the decoded image data has the correct length and hash value, + // which should be the same as those of the raw data, since this image is +@@ -3306,8 +4231,7 @@ TEST_F(FPDFEditEmbedderTest, GetImageData) { + buf.clear(); + buf.resize(len); + EXPECT_EQ(4370u, FPDFImageObj_GetImageDataDecoded(obj, buf.data(), len)); +- EXPECT_EQ("6aae1f3710335023a9e12191be66b64b", +- GenerateMD5Base16(reinterpret_cast(buf.data()), len)); ++ EXPECT_EQ("6aae1f3710335023a9e12191be66b64b", GenerateMD5Base16(buf)); + + UnloadPage(page); + } +@@ -3319,72 +4243,67 @@ TEST_F(FPDFEditEmbedderTest, GetImageMatrix) { + ASSERT_EQ(39, FPDFPage_CountObjects(page)); + + FPDF_PAGEOBJECT obj; +- double a; +- double b; +- double c; +- double d; +- double e; +- double f; ++ FS_MATRIX matrix; + + obj = FPDFPage_GetObject(page, 33); + ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); +- EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f)); +- EXPECT_DOUBLE_EQ(53.0, a); +- EXPECT_DOUBLE_EQ(0.0, b); +- EXPECT_DOUBLE_EQ(0.0, c); +- EXPECT_DOUBLE_EQ(43.0, d); +- EXPECT_DOUBLE_EQ(72.0, e); +- EXPECT_DOUBLE_EQ(646.510009765625, f); ++ EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix)); ++ EXPECT_FLOAT_EQ(53.0f, matrix.a); ++ EXPECT_FLOAT_EQ(0.0f, matrix.b); ++ EXPECT_FLOAT_EQ(0.0f, matrix.c); ++ EXPECT_FLOAT_EQ(43.0f, matrix.d); ++ EXPECT_FLOAT_EQ(72.0f, matrix.e); ++ EXPECT_FLOAT_EQ(646.510009765625f, matrix.f); + + obj = FPDFPage_GetObject(page, 34); + ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); +- EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f)); +- EXPECT_DOUBLE_EQ(70.0, a); +- EXPECT_DOUBLE_EQ(0.0, b); +- EXPECT_DOUBLE_EQ(0.0, c); +- EXPECT_DOUBLE_EQ(51.0, d); +- EXPECT_DOUBLE_EQ(216.0, e); +- EXPECT_DOUBLE_EQ(646.510009765625, f); ++ EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix)); ++ EXPECT_FLOAT_EQ(70.0f, matrix.a); ++ EXPECT_FLOAT_EQ(0.0f, matrix.b); ++ EXPECT_FLOAT_EQ(0.0f, matrix.c); ++ EXPECT_FLOAT_EQ(51.0f, matrix.d); ++ EXPECT_FLOAT_EQ(216.0f, matrix.e); ++ EXPECT_FLOAT_EQ(646.510009765625f, matrix.f); + + obj = FPDFPage_GetObject(page, 35); + ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); +- EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f)); +- EXPECT_DOUBLE_EQ(69.0, a); +- EXPECT_DOUBLE_EQ(0.0, b); +- EXPECT_DOUBLE_EQ(0.0, c); +- EXPECT_DOUBLE_EQ(51.0, d); +- EXPECT_DOUBLE_EQ(360.0, e); +- EXPECT_DOUBLE_EQ(646.510009765625, f); ++ EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix)); ++ EXPECT_FLOAT_EQ(69.0f, matrix.a); ++ EXPECT_FLOAT_EQ(0.0f, matrix.b); ++ EXPECT_FLOAT_EQ(0.0f, matrix.c); ++ EXPECT_FLOAT_EQ(51.0f, matrix.d); ++ EXPECT_FLOAT_EQ(360.0f, matrix.e); ++ EXPECT_FLOAT_EQ(646.510009765625f, matrix.f); + + obj = FPDFPage_GetObject(page, 36); + ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); +- EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f)); +- EXPECT_DOUBLE_EQ(59.0, a); +- EXPECT_DOUBLE_EQ(0.0, b); +- EXPECT_DOUBLE_EQ(0.0, c); +- EXPECT_DOUBLE_EQ(45.0, d); +- EXPECT_DOUBLE_EQ(72.0, e); +- EXPECT_DOUBLE_EQ(553.510009765625, f); ++ EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix)); ++ EXPECT_FLOAT_EQ(59.0f, matrix.a); ++ EXPECT_FLOAT_EQ(0.0f, matrix.b); ++ EXPECT_FLOAT_EQ(0.0f, matrix.c); ++ EXPECT_FLOAT_EQ(45.0f, matrix.d); ++ EXPECT_FLOAT_EQ(72.0f, matrix.e); ++ EXPECT_FLOAT_EQ(553.510009765625f, matrix.f); + + obj = FPDFPage_GetObject(page, 37); + ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); +- EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f)); +- EXPECT_DOUBLE_EQ(55.94000244140625, a); +- EXPECT_DOUBLE_EQ(0.0, b); +- EXPECT_DOUBLE_EQ(0.0, c); +- EXPECT_DOUBLE_EQ(46.950000762939453, d); +- EXPECT_DOUBLE_EQ(216.0, e); +- EXPECT_DOUBLE_EQ(552.510009765625, f); ++ EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix)); ++ EXPECT_FLOAT_EQ(55.94000244140625f, matrix.a); ++ EXPECT_FLOAT_EQ(0.0f, matrix.b); ++ EXPECT_FLOAT_EQ(0.0f, matrix.c); ++ EXPECT_FLOAT_EQ(46.950000762939453f, matrix.d); ++ EXPECT_FLOAT_EQ(216.0f, matrix.e); ++ EXPECT_FLOAT_EQ(552.510009765625f, matrix.f); + + obj = FPDFPage_GetObject(page, 38); + ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); +- EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f)); +- EXPECT_DOUBLE_EQ(70.528999328613281, a); +- EXPECT_DOUBLE_EQ(0.0, b); +- EXPECT_DOUBLE_EQ(0.0, c); +- EXPECT_DOUBLE_EQ(43.149997711181641, d); +- EXPECT_DOUBLE_EQ(360.0, e); +- EXPECT_DOUBLE_EQ(553.3599853515625, f); ++ EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix)); ++ EXPECT_FLOAT_EQ(70.528999328613281f, matrix.a); ++ EXPECT_FLOAT_EQ(0.0f, matrix.b); ++ EXPECT_FLOAT_EQ(0.0f, matrix.c); ++ EXPECT_FLOAT_EQ(43.149997711181641f, matrix.d); ++ EXPECT_FLOAT_EQ(360.0f, matrix.e); ++ EXPECT_FLOAT_EQ(553.3599853515625f, matrix.f); + + UnloadPage(page); + } +@@ -3398,7 +4317,7 @@ TEST_F(FPDFEditEmbedderTest, DestroyPageObject) { + } + + TEST_F(FPDFEditEmbedderTest, GetImageFilters) { +- EXPECT_TRUE(OpenDocument("embedded_images.pdf")); ++ ASSERT_TRUE(OpenDocument("embedded_images.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -3492,3 +4411,239 @@ TEST_F(FPDFEditEmbedderTest, GetImageMetadata) { + + UnloadPage(page); + } ++ ++TEST_F(FPDFEditEmbedderTest, GetImageMetadataJpxLzw) { ++ ASSERT_TRUE(OpenDocument("jpx_lzw.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 0); ++ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); ++ ++ FPDF_IMAGEOBJ_METADATA metadata; ++ ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, page, &metadata)); ++ EXPECT_EQ(-1, metadata.marked_content_id); ++ EXPECT_EQ(612u, metadata.width); ++ EXPECT_EQ(792u, metadata.height); ++ EXPECT_FLOAT_EQ(72.0f, metadata.horizontal_dpi); ++ EXPECT_FLOAT_EQ(72.0f, metadata.vertical_dpi); ++ EXPECT_EQ(24u, metadata.bits_per_pixel); ++ EXPECT_EQ(FPDF_COLORSPACE_UNKNOWN, metadata.colorspace); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditEmbedderTest, GetImagePixelSize) { ++ ASSERT_TRUE(OpenDocument("embedded_images.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ // Check that getting the size of a null object would fail. ++ unsigned int width = 0; ++ unsigned int height = 0; ++ EXPECT_FALSE(FPDFImageObj_GetImagePixelSize(nullptr, &width, &height)); ++ ++ // Check that receiving the size with a null width and height pointers would ++ // fail. ++ FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 35); ++ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); ++ EXPECT_FALSE(FPDFImageObj_GetImagePixelSize(obj, nullptr, nullptr)); ++ EXPECT_FALSE(FPDFImageObj_GetImagePixelSize(obj, nullptr, &height)); ++ EXPECT_FALSE(FPDFImageObj_GetImagePixelSize(obj, &width, nullptr)); ++ ++ // Verify the pixel size of image. ++ ASSERT_TRUE(FPDFImageObj_GetImagePixelSize(obj, &width, &height)); ++ EXPECT_EQ(92u, width); ++ EXPECT_EQ(68u, height); ++ ++ obj = FPDFPage_GetObject(page, 37); ++ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); ++ ASSERT_TRUE(FPDFImageObj_GetImagePixelSize(obj, &width, &height)); ++ EXPECT_EQ(126u, width); ++ EXPECT_EQ(106u, height); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForHelloWorldText) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ FPDF_PAGEOBJECT text_object = FPDFPage_GetObject(page, 0); ++ ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object)); ++ ++ ScopedFPDFBitmap bitmap( ++ FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 1)); ++ ASSERT_TRUE(bitmap); ++ const char kChecksum[] = "bb0abe1accca1cfeaaf78afa35762350"; ++ CompareBitmap(bitmap.get(), 64, 11, kChecksum); ++ ++ ScopedFPDFBitmap x2_bitmap( ++ FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 2.4f)); ++ ASSERT_TRUE(x2_bitmap); ++ const char kX2Checksum[] = "80db528ec7146d92247f2339a8f10ba5"; ++ CompareBitmap(x2_bitmap.get(), 153, 25, kX2Checksum); ++ ++ ScopedFPDFBitmap x10_bitmap( ++ FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 10)); ++ ASSERT_TRUE(x10_bitmap); ++ const char* x10_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "f5e86739b9603838cbfb0c7f8e54e4ae"; ++ return "149f63de758ab01d3b75605cdfd4c176"; ++ }(); ++ CompareBitmap(x10_bitmap.get(), 631, 103, x10_checksum); ++ } ++ ++ { ++ FPDF_PAGEOBJECT text_object = FPDFPage_GetObject(page, 1); ++ ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object)); ++ ++ ScopedFPDFBitmap bitmap( ++ FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 1)); ++ ASSERT_TRUE(bitmap); ++ const char kChecksum[] = "3fc1101b2408c5484adc24ba0a11ff3d"; ++ CompareBitmap(bitmap.get(), 116, 16, kChecksum); ++ ++ ScopedFPDFBitmap x2_bitmap( ++ FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 2.4f)); ++ ASSERT_TRUE(x2_bitmap); ++ const char kX2Checksum[] = "429960ae7b822f0c630432535e637465"; ++ CompareBitmap(x2_bitmap.get(), 276, 36, kX2Checksum); ++ ++ ScopedFPDFBitmap x10_bitmap( ++ FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 10)); ++ ASSERT_TRUE(x10_bitmap); ++ const char* x10_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "59d0b5f1fc2a1cc0c392e213909bbbb6"; ++ return "f5f93bf64de579b59e775d7076ca0a5a"; ++ }(); ++ CompareBitmap(x10_bitmap.get(), 1143, 150, x10_checksum); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForRotatedText) { ++ ASSERT_TRUE(OpenDocument("rotated_text.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ FPDF_PAGEOBJECT text_object = FPDFPage_GetObject(page, 0); ++ ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object)); ++ ++ ScopedFPDFBitmap bitmap( ++ FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 1)); ++ ASSERT_TRUE(bitmap); ++ const char kChecksum[] = "08ada0802f780d3fefb161dc6fb45977"; ++ CompareBitmap(bitmap.get(), 29, 28, kChecksum); ++ ++ ScopedFPDFBitmap x2_bitmap( ++ FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 2.4f)); ++ ASSERT_TRUE(x2_bitmap); ++ const char kX2Checksum[] = "09d7ddb647b8653cb59aede349a0c3e1"; ++ CompareBitmap(x2_bitmap.get(), 67, 67, kX2Checksum); ++ ++ ScopedFPDFBitmap x10_bitmap( ++ FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 10)); ++ ASSERT_TRUE(x10_bitmap); ++ const char* x10_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "839dc0eed66eab6d545833f25b37031e"; ++ return "bbd3842a4b50dbfcbce4eee2b067a297"; ++ }(); ++ CompareBitmap(x10_bitmap.get(), 275, 275, x10_checksum); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForColorText) { ++ ASSERT_TRUE(OpenDocument("text_color.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ FPDF_PAGEOBJECT text_object = FPDFPage_GetObject(page, 0); ++ ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object)); ++ ++ ScopedFPDFBitmap bitmap( ++ FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 7.3f)); ++ ASSERT_TRUE(bitmap); ++ const char* checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "1d74731d23a056c0e3fb88f2f85b2581"; ++ return "e8154fa8ededf4d9b8b35b5260897b6c"; ++ }(); ++ CompareBitmap(bitmap.get(), 120, 186, checksum); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForNewlyCreatedText) { ++ // Start with a blank document. ++ ASSERT_TRUE(CreateNewDocument()); ++ ++ // Create a new text object. ++ ScopedFPDFPageObject text_object( ++ FPDFPageObj_NewTextObj(document(), "Arial", 12.0f)); ++ ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object.get())); ++ ScopedFPDFWideString text = GetFPDFWideString(kBottomText); ++ EXPECT_TRUE(FPDFText_SetText(text_object.get(), text.get())); ++ ++ ScopedFPDFBitmap bitmap( ++ FPDFTextObj_GetRenderedBitmap(document(), nullptr, text_object.get(), 1)); ++ ASSERT_TRUE(bitmap); ++ const char kChecksum[] = "fa947759dab76d68a07ccf6f97b2d9c2"; ++ CompareBitmap(bitmap.get(), 151, 12, kChecksum); ++} ++ ++TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForTextWithBadParameters) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ FPDF_PAGEOBJECT text_object = FPDFPage_GetObject(page, 0); ++ ASSERT_TRUE(text_object); ++ ++ // Simple bad parameters testing. ++ EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, nullptr, nullptr, 0)); ++ EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(document(), nullptr, nullptr, 0)); ++ EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, page, nullptr, 0)); ++ EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, nullptr, text_object, 0)); ++ EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, nullptr, nullptr, 1)); ++ EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(document(), page, nullptr, 0)); ++ EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(document(), nullptr, nullptr, 1)); ++ EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, page, text_object, 0)); ++ EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, page, nullptr, 1)); ++ EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, nullptr, text_object, 1)); ++ EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(document(), page, nullptr, 1)); ++ EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, page, text_object, 1)); ++ ++ // Test bad scale values. ++ EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 0)); ++ EXPECT_FALSE( ++ FPDFTextObj_GetRenderedBitmap(document(), page, text_object, -1)); ++ EXPECT_FALSE( ++ FPDFTextObj_GetRenderedBitmap(document(), page, text_object, 10000)); ++ EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap( ++ document(), page, text_object, std::numeric_limits::max())); ++ EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap( ++ document(), page, text_object, std::numeric_limits::infinity())); ++ ++ { ++ // `text_object` will render without `page`, but may not render correctly ++ // without the resources from `page`. Although it does in this simple case. ++ ScopedFPDFBitmap bitmap( ++ FPDFTextObj_GetRenderedBitmap(document(), nullptr, text_object, 1)); ++ EXPECT_TRUE(bitmap); ++ } ++ ++ // Mismatch between the document and the page fails too. ++ ScopedFPDFDocument empty_document(FPDF_CreateNewDocument()); ++ EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(empty_document.get(), page, ++ text_object, 1)); ++ ++ UnloadPage(page); ++} +diff --git a/fpdfsdk/fpdf_edit_unittest.cpp b/fpdfsdk/fpdf_edit_unittest.cpp +index 6aa24f419..5f6fb0fa7 100644 +--- a/fpdfsdk/fpdf_edit_unittest.cpp ++++ b/fpdfsdk/fpdf_edit_unittest.cpp +@@ -1,16 +1,13 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "public/fpdf_edit.h" + +-#include "core/fpdfapi/page/cpdf_pagemodule.h" ++#include "core/fpdfapi/page/test_with_page_module.h" + #include "testing/gtest/include/gtest/gtest.h" + +-class PDFEditTest : public testing::Test { +- void SetUp() override { CPDF_PageModule::Create(); } +- void TearDown() override { CPDF_PageModule::Destroy(); } +-}; ++using PDFEditTest = TestWithPageModule; + + TEST_F(PDFEditTest, LineJoin) { + EXPECT_FALSE(FPDFPageObj_SetLineJoin(nullptr, -1)); +diff --git a/fpdfsdk/fpdf_editimg.cpp b/fpdfsdk/fpdf_editimg.cpp +index ecd11d16b..3fae5b4ec 100644 +--- a/fpdfsdk/fpdf_editimg.cpp ++++ b/fpdfsdk/fpdf_editimg.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,7 @@ + + #include "public/fpdf_edit.h" + ++#include + #include + + #include "core/fpdfapi/page/cpdf_dib.h" +@@ -16,36 +17,53 @@ + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_name.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" ++#include "core/fpdfapi/render/cpdf_imagerenderer.h" ++#include "core/fpdfapi/render/cpdf_rendercontext.h" ++#include "core/fpdfapi/render/cpdf_renderstatus.h" ++#include "core/fxcrt/stl_util.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" + #include "fpdfsdk/cpdfsdk_customaccess.h" + #include "fpdfsdk/cpdfsdk_helpers.h" +-#include "third_party/base/ptr_util.h" + + namespace { + + // These checks ensure the consistency of colorspace values across core/ and + // public/. +-static_assert(PDFCS_DEVICEGRAY == FPDF_COLORSPACE_DEVICEGRAY, +- "PDFCS_DEVICEGRAY value mismatch"); +-static_assert(PDFCS_DEVICERGB == FPDF_COLORSPACE_DEVICERGB, +- "PDFCS_DEVICERGB value mismatch"); +-static_assert(PDFCS_DEVICECMYK == FPDF_COLORSPACE_DEVICECMYK, +- "PDFCS_DEVICECMYK value mismatch"); +-static_assert(PDFCS_CALGRAY == FPDF_COLORSPACE_CALGRAY, +- "PDFCS_CALGRAY value mismatch"); +-static_assert(PDFCS_CALRGB == FPDF_COLORSPACE_CALRGB, +- "PDFCS_CALRGB value mismatch"); +-static_assert(PDFCS_LAB == FPDF_COLORSPACE_LAB, "PDFCS_LAB value mismatch"); +-static_assert(PDFCS_ICCBASED == FPDF_COLORSPACE_ICCBASED, +- "PDFCS_ICCBASED value mismatch"); +-static_assert(PDFCS_SEPARATION == FPDF_COLORSPACE_SEPARATION, +- "PDFCS_SEPARATION value mismatch"); +-static_assert(PDFCS_DEVICEN == FPDF_COLORSPACE_DEVICEN, +- "PDFCS_DEVICEN value mismatch"); +-static_assert(PDFCS_INDEXED == FPDF_COLORSPACE_INDEXED, +- "PDFCS_INDEXED value mismatch"); +-static_assert(PDFCS_PATTERN == FPDF_COLORSPACE_PATTERN, +- "PDFCS_PATTERN value mismatch"); ++static_assert(static_cast(CPDF_ColorSpace::Family::kDeviceGray) == ++ FPDF_COLORSPACE_DEVICEGRAY, ++ "kDeviceGray value mismatch"); ++static_assert(static_cast(CPDF_ColorSpace::Family::kDeviceRGB) == ++ FPDF_COLORSPACE_DEVICERGB, ++ "kDeviceRGB value mismatch"); ++static_assert(static_cast(CPDF_ColorSpace::Family::kDeviceCMYK) == ++ FPDF_COLORSPACE_DEVICECMYK, ++ "kDeviceCMYK value mismatch"); ++static_assert(static_cast(CPDF_ColorSpace::Family::kCalGray) == ++ FPDF_COLORSPACE_CALGRAY, ++ "kCalGray value mismatch"); ++static_assert(static_cast(CPDF_ColorSpace::Family::kCalRGB) == ++ FPDF_COLORSPACE_CALRGB, ++ "kCalRGB value mismatch"); ++static_assert(static_cast(CPDF_ColorSpace::Family::kLab) == ++ FPDF_COLORSPACE_LAB, ++ "kLab value mismatch"); ++static_assert(static_cast(CPDF_ColorSpace::Family::kICCBased) == ++ FPDF_COLORSPACE_ICCBASED, ++ "kICCBased value mismatch"); ++static_assert(static_cast(CPDF_ColorSpace::Family::kSeparation) == ++ FPDF_COLORSPACE_SEPARATION, ++ "kSeparation value mismatch"); ++static_assert(static_cast(CPDF_ColorSpace::Family::kDeviceN) == ++ FPDF_COLORSPACE_DEVICEN, ++ "kDeviceN value mismatch"); ++static_assert(static_cast(CPDF_ColorSpace::Family::kIndexed) == ++ FPDF_COLORSPACE_INDEXED, ++ "kIndexed value mismatch"); ++static_assert(static_cast(CPDF_ColorSpace::Family::kPattern) == ++ FPDF_COLORSPACE_PATTERN, ++ "kPattern value mismatch"); + + RetainPtr MakeSeekableReadStream( + FPDF_FILEACCESS* pFileAccess) { +@@ -80,9 +98,10 @@ bool LoadJpegHelper(FPDF_PAGE* pages, + + RetainPtr pFile = MakeSeekableReadStream(file_access); + if (inline_jpeg) +- pImgObj->GetImage()->SetJpegImageInline(pFile); ++ pImgObj->GetImage()->SetJpegImageInline(std::move(pFile)); + else +- pImgObj->GetImage()->SetJpegImage(pFile); ++ pImgObj->GetImage()->SetJpegImage(std::move(pFile)); ++ + pImgObj->SetDirty(true); + return true; + } +@@ -95,7 +114,7 @@ FPDFPageObj_NewImageObj(FPDF_DOCUMENT document) { + if (!pDoc) + return nullptr; + +- auto pImageObj = pdfium::MakeUnique(); ++ auto pImageObj = std::make_unique(); + pImageObj->SetImage(pdfium::MakeRetain(pDoc)); + + // Caller takes ownership. +@@ -118,28 +137,6 @@ FPDFImageObj_LoadJpegFileInline(FPDF_PAGE* pages, + return LoadJpegHelper(pages, count, image_object, file_access, true); + } + +-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +-FPDFImageObj_GetMatrix(FPDF_PAGEOBJECT image_object, +- double* a, +- double* b, +- double* c, +- double* d, +- double* e, +- double* f) { +- CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object); +- if (!pImgObj || !a || !b || !c || !d || !e || !f) +- return false; +- +- const CFX_Matrix& matrix = pImgObj->matrix(); +- *a = matrix.a; +- *b = matrix.b; +- *c = matrix.c; +- *d = matrix.d; +- *e = matrix.e; +- *f = matrix.f; +- return true; +-} +- + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFImageObj_SetMatrix(FPDF_PAGEOBJECT image_object, + double a, +@@ -152,10 +149,9 @@ FPDFImageObj_SetMatrix(FPDF_PAGEOBJECT image_object, + if (!pImgObj) + return false; + +- pImgObj->set_matrix(CFX_Matrix(static_cast(a), static_cast(b), +- static_cast(c), static_cast(d), +- static_cast(e), static_cast(f))); +- pImgObj->CalcBoundingBox(); ++ pImgObj->SetImageMatrix(CFX_Matrix( ++ static_cast(a), static_cast(b), static_cast(c), ++ static_cast(d), static_cast(e), static_cast(f))); + pImgObj->SetDirty(true); + return true; + } +@@ -201,19 +197,75 @@ FPDFImageObj_GetBitmap(FPDF_PAGEOBJECT image_object) { + if (!pSource) + return nullptr; + +- RetainPtr pBitmap; + // If the source image has a representation of 1 bit per pixel, then convert + // it to a grayscale bitmap having 1 byte per pixel, since bitmaps have no + // concept of bits. Otherwise, convert the source image to a bitmap directly, + // retaining its color representation. +- if (pSource->GetBPP() == 1) +- pBitmap = pSource->CloneConvert(FXDIB_8bppRgb); +- else +- pBitmap = pSource->Clone(nullptr); ++ RetainPtr pBitmap = ++ pSource->GetBPP() == 1 ? pSource->ConvertTo(FXDIB_Format::k8bppRgb) ++ : pSource->Realize(); + + return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak()); + } + ++FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV ++FPDFImageObj_GetRenderedBitmap(FPDF_DOCUMENT document, ++ FPDF_PAGE page, ++ FPDF_PAGEOBJECT image_object) { ++ CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document); ++ if (!doc) ++ return nullptr; ++ ++ CPDF_Page* optional_page = CPDFPageFromFPDFPage(page); ++ if (optional_page && optional_page->GetDocument() != doc) ++ return nullptr; ++ ++ CPDF_ImageObject* image = CPDFImageObjectFromFPDFPageObject(image_object); ++ if (!image) ++ return nullptr; ++ ++ // Create |result_bitmap|. ++ const CFX_Matrix& image_matrix = image->matrix(); ++ int output_width = image_matrix.a; ++ int output_height = image_matrix.d; ++ auto result_bitmap = pdfium::MakeRetain(); ++ if (!result_bitmap->Create(output_width, output_height, FXDIB_Format::kArgb)) ++ return nullptr; ++ ++ // Set up all the rendering code. ++ RetainPtr page_resources = ++ optional_page ? optional_page->GetMutablePageResources() : nullptr; ++ CPDF_RenderContext context(doc, std::move(page_resources), ++ /*pPageCache=*/nullptr); ++ CFX_DefaultRenderDevice device; ++ device.Attach(result_bitmap); ++ CPDF_RenderStatus status(&context, &device); ++ CPDF_ImageRenderer renderer(&status); ++ ++ // Need to first flip the image, as expected by |renderer|. ++ CFX_Matrix render_matrix(1, 0, 0, -1, 0, output_height); ++ ++ // Then take |image_matrix|'s offset into account. ++ render_matrix.Translate(-image_matrix.e, image_matrix.f); ++ ++ // Do the actual rendering. ++ bool should_continue = renderer.Start(image, render_matrix, ++ /*bStdCS=*/false, BlendMode::kNormal); ++ while (should_continue) ++ should_continue = renderer.Continue(/*pPause=*/nullptr); ++ ++ if (!renderer.GetResult()) ++ return nullptr; ++ ++#if defined(_SKIA_SUPPORT_) ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ result_bitmap->UnPreMultiply(); ++#endif ++ ++ // Caller takes ownership. ++ return FPDFBitmapFromCFXDIBitmap(result_bitmap.Leak()); ++} ++ + FPDF_EXPORT unsigned long FPDF_CALLCONV + FPDFImageObj_GetImageDataDecoded(FPDF_PAGEOBJECT image_object, + void* buffer, +@@ -226,11 +278,13 @@ FPDFImageObj_GetImageDataDecoded(FPDF_PAGEOBJECT image_object, + if (!pImg) + return 0; + +- CPDF_Stream* pImgStream = pImg->GetStream(); ++ RetainPtr pImgStream = pImg->GetStream(); + if (!pImgStream) + return 0; + +- return DecodeStreamMaybeCopyAndReturnLength(pImgStream, buffer, buflen); ++ return DecodeStreamMaybeCopyAndReturnLength( ++ std::move(pImgStream), ++ {static_cast(buffer), static_cast(buflen)}); + } + + FPDF_EXPORT unsigned long FPDF_CALLCONV +@@ -245,11 +299,13 @@ FPDFImageObj_GetImageDataRaw(FPDF_PAGEOBJECT image_object, + if (!pImg) + return 0; + +- CPDF_Stream* pImgStream = pImg->GetStream(); ++ RetainPtr pImgStream = pImg->GetStream(); + if (!pImgStream) + return 0; + +- return GetRawStreamMaybeCopyAndReturnLength(pImgStream, buffer, buflen); ++ return GetRawStreamMaybeCopyAndReturnLength( ++ std::move(pImgStream), ++ {static_cast(buffer), static_cast(buflen)}); + } + + FPDF_EXPORT int FPDF_CALLCONV +@@ -262,13 +318,17 @@ FPDFImageObj_GetImageFilterCount(FPDF_PAGEOBJECT image_object) { + if (!pImg) + return 0; + +- CPDF_Dictionary* pDict = pImg->GetDict(); +- CPDF_Object* pFilter = pDict ? pDict->GetDirectObjectFor("Filter") : nullptr; ++ RetainPtr pDict = pImg->GetDict(); ++ if (!pDict) ++ return 0; ++ ++ RetainPtr pFilter = pDict->GetDirectObjectFor("Filter"); + if (!pFilter) + return 0; + + if (pFilter->IsArray()) +- return pFilter->AsArray()->size(); ++ return fxcrt::CollectionSize(*pFilter->AsArray()); ++ + if (pFilter->IsName()) + return 1; + +@@ -284,18 +344,14 @@ FPDFImageObj_GetImageFilter(FPDF_PAGEOBJECT image_object, + return 0; + + CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(image_object); +- CPDF_Object* pFilter = +- pObj->AsImage()->GetImage()->GetDict()->GetDirectObjectFor("Filter"); +- ByteString bsFilter; +- if (pFilter->IsName()) +- bsFilter = pFilter->AsName()->GetString(); +- else +- bsFilter = pFilter->AsArray()->GetStringAt(index); +- +- unsigned long len = bsFilter.GetLength() + 1; +- if (buffer && len <= buflen) +- memcpy(buffer, bsFilter.c_str(), len); +- return len; ++ RetainPtr pDict = ++ pObj->AsImage()->GetImage()->GetDict(); ++ RetainPtr pFilter = pDict->GetDirectObjectFor("Filter"); ++ ByteString bsFilter = pFilter->IsName() ++ ? pFilter->AsName()->GetString() ++ : pFilter->AsArray()->GetByteStringAt(index); ++ ++ return NulTerminateMaybeCopyAndReturnLength(bsFilter, buffer, buflen); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +@@ -310,7 +366,8 @@ FPDFImageObj_GetImageMetadata(FPDF_PAGEOBJECT image_object, + if (!pImg) + return false; + +- metadata->marked_content_id = pImgObj->m_ContentMarks.GetMarkedContentID(); ++ metadata->marked_content_id = ++ pImgObj->GetContentMarks()->GetMarkedContentID(); + + const int nPixelWidth = pImg->GetPixelWidth(); + const int nPixelHeight = pImg->GetPixelHeight(); +@@ -332,16 +389,40 @@ FPDFImageObj_GetImageMetadata(FPDF_PAGEOBJECT image_object, + if (!pPage || !pPage->GetDocument() || !pImg->GetStream()) + return true; + +- auto pSource = pdfium::MakeRetain(); ++ // A cross-document image may have come from the embedder. ++ if (pPage->GetDocument() != pImg->GetDocument()) ++ return false; ++ ++ RetainPtr pSource = pImg->CreateNewDIB(); + CPDF_DIB::LoadState ret = pSource->StartLoadDIBBase( +- pPage->GetDocument(), pImg->GetStream(), false, nullptr, +- pPage->m_pPageResources.Get(), false, 0, false); ++ false, nullptr, pPage->GetPageResources().Get(), false, ++ CPDF_ColorSpace::Family::kUnknown, false, {0, 0}); + if (ret == CPDF_DIB::LoadState::kFail) + return true; + + metadata->bits_per_pixel = pSource->GetBPP(); +- if (pSource->GetColorSpace()) +- metadata->colorspace = pSource->GetColorSpace()->GetFamily(); ++ if (pSource->GetColorSpace()) { ++ metadata->colorspace = ++ static_cast(pSource->GetColorSpace()->GetFamily()); ++ } ++ return true; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFImageObj_GetImagePixelSize(FPDF_PAGEOBJECT image_object, ++ unsigned int* width, ++ unsigned int* height) { ++ CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object); ++ if (!pImgObj || !width || !height) { ++ return false; ++ } ++ ++ RetainPtr pImg = pImgObj->GetImage(); ++ if (!pImg) { ++ return false; ++ } + ++ *width = pImg->GetPixelWidth(); ++ *height = pImg->GetPixelHeight(); + return true; + } +diff --git a/fpdfsdk/fpdf_editimg_unittest.cpp b/fpdfsdk/fpdf_editimg_embeddertest.cpp +similarity index 60% +rename from fpdfsdk/fpdf_editimg_unittest.cpp +rename to fpdfsdk/fpdf_editimg_embeddertest.cpp +index 09ec71e0d..5038def6e 100644 +--- a/fpdfsdk/fpdf_editimg_unittest.cpp ++++ b/fpdfsdk/fpdf_editimg_embeddertest.cpp +@@ -1,18 +1,14 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "public/fpdf_edit.h" + +-#include "core/fpdfapi/page/cpdf_pagemodule.h" + #include "public/cpp/fpdf_scopers.h" +-#include "testing/gtest/include/gtest/gtest.h" ++#include "testing/embedder_test.h" + #include "testing/utils/file_util.h" + +-class PDFEditImgTest : public testing::Test { +- void SetUp() override { CPDF_PageModule::Create(); } +- void TearDown() override { CPDF_PageModule::Destroy(); } +-}; ++class PDFEditImgTest : public EmbedderTest {}; + + TEST_F(PDFEditImgTest, InsertObjectWithInvalidPage) { + FPDF_DOCUMENT doc = FPDF_CreateNewDocument(); +@@ -60,8 +56,9 @@ TEST_F(PDFEditImgTest, NewImageObjGenerateContent) { + + FPDF_PAGEOBJECT page_image = FPDFPageObj_NewImageObj(doc); + ASSERT_TRUE(FPDFImageObj_SetBitmap(&page, 0, page_image, bitmap)); +- ASSERT_TRUE( +- FPDFImageObj_SetMatrix(page_image, kBitmapSize, 0, 0, kBitmapSize, 0, 0)); ++ static constexpr FS_MATRIX kScaleBitmapMatrix{kBitmapSize, 0, 0, ++ kBitmapSize, 0, 0}; ++ ASSERT_TRUE(FPDFPageObj_SetMatrix(page_image, &kScaleBitmapMatrix)); + FPDFPage_InsertObject(page, page_image); + EXPECT_EQ(1, FPDFPage_CountObjects(page)); + EXPECT_TRUE(FPDFPage_GenerateContent(page)); +@@ -127,58 +124,28 @@ TEST_F(PDFEditImgTest, GetSetImageMatrix) { + FPDF_DOCUMENT doc = FPDF_CreateNewDocument(); + FPDF_PAGEOBJECT image = FPDFPageObj_NewImageObj(doc); + +- double a; +- double b; +- double c; +- double d; +- double e; +- double f; +- EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, nullptr, nullptr, nullptr, +- nullptr, nullptr, nullptr)); +- EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, &a, nullptr, nullptr, nullptr, +- nullptr, nullptr)); +- EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, &a, &b, nullptr, nullptr, +- nullptr, nullptr)); +- EXPECT_FALSE( +- FPDFImageObj_GetMatrix(nullptr, &a, &b, &c, nullptr, nullptr, nullptr)); +- EXPECT_FALSE( +- FPDFImageObj_GetMatrix(nullptr, &a, &b, &c, nullptr, nullptr, nullptr)); +- EXPECT_FALSE( +- FPDFImageObj_GetMatrix(nullptr, &a, &b, &c, &d, nullptr, nullptr)); +- EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, &a, &b, &c, &d, &e, nullptr)); +- EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, &a, &b, &c, &d, &e, &f)); +- EXPECT_FALSE(FPDFImageObj_GetMatrix(nullptr, &a, nullptr, &c, &d, &e, &f)); +- +- EXPECT_FALSE(FPDFImageObj_GetMatrix(image, nullptr, nullptr, nullptr, nullptr, +- nullptr, nullptr)); +- EXPECT_FALSE(FPDFImageObj_GetMatrix(image, &a, nullptr, nullptr, nullptr, +- nullptr, nullptr)); +- EXPECT_FALSE(FPDFImageObj_GetMatrix(image, &a, &b, nullptr, nullptr, nullptr, +- nullptr)); +- EXPECT_FALSE( +- FPDFImageObj_GetMatrix(image, &a, &b, &c, nullptr, nullptr, nullptr)); +- EXPECT_FALSE( +- FPDFImageObj_GetMatrix(image, &a, &b, &c, nullptr, nullptr, nullptr)); +- EXPECT_FALSE(FPDFImageObj_GetMatrix(image, &a, &b, &c, &d, nullptr, nullptr)); +- EXPECT_FALSE(FPDFImageObj_GetMatrix(image, &a, &b, &c, &d, &e, nullptr)); +- EXPECT_FALSE(FPDFImageObj_GetMatrix(image, &a, nullptr, &c, &d, &e, &f)); +- +- EXPECT_TRUE(FPDFImageObj_GetMatrix(image, &a, &b, &c, &d, &e, &f)); +- EXPECT_DOUBLE_EQ(1.0, a); +- EXPECT_DOUBLE_EQ(0.0, b); +- EXPECT_DOUBLE_EQ(0.0, c); +- EXPECT_DOUBLE_EQ(1.0, d); +- EXPECT_DOUBLE_EQ(0.0, e); +- EXPECT_DOUBLE_EQ(0.0, f); +- +- EXPECT_TRUE(FPDFImageObj_SetMatrix(image, 1, 2, 3, 4, 5, 6)); +- EXPECT_TRUE(FPDFImageObj_GetMatrix(image, &a, &b, &c, &d, &e, &f)); +- EXPECT_DOUBLE_EQ(1.0, a); +- EXPECT_DOUBLE_EQ(2.0, b); +- EXPECT_DOUBLE_EQ(3.0, c); +- EXPECT_DOUBLE_EQ(4.0, d); +- EXPECT_DOUBLE_EQ(5.0, e); +- EXPECT_DOUBLE_EQ(6.0, f); ++ FS_MATRIX matrix; ++ EXPECT_FALSE(FPDFPageObj_GetMatrix(nullptr, nullptr)); ++ EXPECT_FALSE(FPDFPageObj_GetMatrix(nullptr, &matrix)); ++ EXPECT_FALSE(FPDFPageObj_GetMatrix(image, nullptr)); ++ ++ EXPECT_TRUE(FPDFPageObj_GetMatrix(image, &matrix)); ++ EXPECT_FLOAT_EQ(1.0f, matrix.a); ++ EXPECT_FLOAT_EQ(0.0f, matrix.b); ++ EXPECT_FLOAT_EQ(0.0f, matrix.c); ++ EXPECT_FLOAT_EQ(1.0f, matrix.d); ++ EXPECT_FLOAT_EQ(0.0f, matrix.e); ++ EXPECT_FLOAT_EQ(0.0f, matrix.f); ++ ++ static constexpr FS_MATRIX kMatrix{1, 2, 3, 4, 5, 6}; ++ EXPECT_TRUE(FPDFPageObj_SetMatrix(image, &kMatrix)); ++ EXPECT_TRUE(FPDFPageObj_GetMatrix(image, &matrix)); ++ EXPECT_FLOAT_EQ(1.0f, matrix.a); ++ EXPECT_FLOAT_EQ(2.0f, matrix.b); ++ EXPECT_FLOAT_EQ(3.0f, matrix.c); ++ EXPECT_FLOAT_EQ(4.0f, matrix.d); ++ EXPECT_FLOAT_EQ(5.0f, matrix.e); ++ EXPECT_FLOAT_EQ(6.0f, matrix.f); + + FPDFPageObj_Destroy(image); + FPDF_CloseDocument(doc); +diff --git a/fpdfsdk/fpdf_editpage.cpp b/fpdfsdk/fpdf_editpage.cpp +index 8e979e021..6cc64f2c4 100644 +--- a/fpdfsdk/fpdf_editpage.cpp ++++ b/fpdfsdk/fpdf_editpage.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -19,24 +19,26 @@ + #include "core/fpdfapi/page/cpdf_formobject.h" + #include "core/fpdfapi/page/cpdf_imageobject.h" + #include "core/fpdfapi/page/cpdf_page.h" ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" + #include "core/fpdfapi/page/cpdf_pageobject.h" + #include "core/fpdfapi/page/cpdf_pathobject.h" + #include "core/fpdfapi/page/cpdf_shadingobject.h" ++#include "core/fpdfapi/page/cpdf_textobject.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fpdfapi/render/cpdf_docrenderdata.h" +-#include "core/fpdfapi/render/cpdf_pagerendercache.h" + #include "core/fpdfdoc/cpdf_annot.h" + #include "core/fpdfdoc/cpdf_annotlist.h" + #include "core/fxcrt/fx_extension.h" ++#include "core/fxcrt/stl_util.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "public/fpdf_formfill.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/cxx17_backports.h" ++#include "third_party/base/numerics/safe_conversions.h" + + #ifdef PDF_ENABLE_XFA + #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h" +@@ -45,69 +47,71 @@ + + namespace { + +-static_assert(FPDF_PAGEOBJ_TEXT == CPDF_PageObject::TEXT, ++static_assert(FPDF_PAGEOBJ_TEXT == ++ static_cast(CPDF_PageObject::Type::kText), + "FPDF_PAGEOBJ_TEXT/CPDF_PageObject::TEXT mismatch"); +-static_assert(FPDF_PAGEOBJ_PATH == CPDF_PageObject::PATH, ++static_assert(FPDF_PAGEOBJ_PATH == ++ static_cast(CPDF_PageObject::Type::kPath), + "FPDF_PAGEOBJ_PATH/CPDF_PageObject::PATH mismatch"); +-static_assert(FPDF_PAGEOBJ_IMAGE == CPDF_PageObject::IMAGE, ++static_assert(FPDF_PAGEOBJ_IMAGE == ++ static_cast(CPDF_PageObject::Type::kImage), + "FPDF_PAGEOBJ_IMAGE/CPDF_PageObject::IMAGE mismatch"); +-static_assert(FPDF_PAGEOBJ_SHADING == CPDF_PageObject::SHADING, ++static_assert(FPDF_PAGEOBJ_SHADING == ++ static_cast(CPDF_PageObject::Type::kShading), + "FPDF_PAGEOBJ_SHADING/CPDF_PageObject::SHADING mismatch"); +-static_assert(FPDF_PAGEOBJ_FORM == CPDF_PageObject::FORM, ++static_assert(FPDF_PAGEOBJ_FORM == ++ static_cast(CPDF_PageObject::Type::kForm), + "FPDF_PAGEOBJ_FORM/CPDF_PageObject::FORM mismatch"); + + bool IsPageObject(CPDF_Page* pPage) { + if (!pPage) + return false; + +- const CPDF_Dictionary* pFormDict = pPage->GetDict(); +- if (!pFormDict->KeyExist("Type")) ++ RetainPtr pFormDict = pPage->GetDict(); ++ if (!pFormDict->KeyExist(pdfium::page_object::kType)) + return false; + +- const CPDF_Object* pObject = pFormDict->GetObjectFor("Type")->GetDirect(); +- return pObject && !pObject->GetString().Compare("Page"); ++ RetainPtr pName = ++ ToName(pFormDict->GetObjectFor(pdfium::page_object::kType)->GetDirect()); ++ return pName && pName->GetString() == "Page"; + } + + void CalcBoundingBox(CPDF_PageObject* pPageObj) { + switch (pPageObj->GetType()) { +- case CPDF_PageObject::TEXT: { ++ case CPDF_PageObject::Type::kText: { + break; + } +- case CPDF_PageObject::PATH: { ++ case CPDF_PageObject::Type::kPath: { + CPDF_PathObject* pPathObj = pPageObj->AsPath(); + pPathObj->CalcBoundingBox(); + break; + } +- case CPDF_PageObject::IMAGE: { ++ case CPDF_PageObject::Type::kImage: { + CPDF_ImageObject* pImageObj = pPageObj->AsImage(); + pImageObj->CalcBoundingBox(); + break; + } +- case CPDF_PageObject::SHADING: { ++ case CPDF_PageObject::Type::kShading: { + CPDF_ShadingObject* pShadingObj = pPageObj->AsShading(); + pShadingObj->CalcBoundingBox(); + break; + } +- case CPDF_PageObject::FORM: { ++ case CPDF_PageObject::Type::kForm: { + CPDF_FormObject* pFormObj = pPageObj->AsForm(); + pFormObj->CalcBoundingBox(); + break; + } +- default: { +- NOTREACHED(); +- break; +- } + } + } + +-CPDF_Dictionary* GetMarkParamDict(FPDF_PAGEOBJECTMARK mark) { ++RetainPtr GetMarkParamDict(FPDF_PAGEOBJECTMARK mark) { + CPDF_ContentMarkItem* pMarkItem = + CPDFContentMarkItemFromFPDFPageObjectMark(mark); + return pMarkItem ? pMarkItem->GetParam() : nullptr; + } + +-CPDF_Dictionary* GetOrCreateMarkParamsDict(FPDF_DOCUMENT document, +- FPDF_PAGEOBJECTMARK mark) { ++RetainPtr GetOrCreateMarkParamsDict(FPDF_DOCUMENT document, ++ FPDF_PAGEOBJECTMARK mark) { + CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document); + if (!pDoc) + return nullptr; +@@ -117,15 +121,11 @@ CPDF_Dictionary* GetOrCreateMarkParamsDict(FPDF_DOCUMENT document, + if (!pMarkItem) + return nullptr; + +- CPDF_Dictionary* pParams = pMarkItem->GetParam(); +- +- // If the Params dict does not exist, create a new one. ++ RetainPtr pParams = pMarkItem->GetParam(); + if (!pParams) { +- auto new_dict = pDoc->New(); +- pParams = new_dict.Get(); +- pMarkItem->SetDirectDict(std::move(new_dict)); ++ pParams = pDoc->New(); ++ pMarkItem->SetDirectDict(pParams); + } +- + return pParams; + } + +@@ -133,7 +133,7 @@ bool PageObjectContainsMark(CPDF_PageObject* pPageObj, + FPDF_PAGEOBJECTMARK mark) { + const CPDF_ContentMarkItem* pMarkItem = + CPDFContentMarkItemFromFPDFPageObjectMark(mark); +- return pMarkItem && pPageObj->m_ContentMarks.ContainsItem(pMarkItem); ++ return pMarkItem && pPageObj->GetContentMarks()->ContainsItem(pMarkItem); + } + + CPDF_FormObject* CPDFFormObjectFromFPDFPageObject(FPDF_PAGEOBJECT page_object) { +@@ -150,9 +150,9 @@ const CPDF_PageObjectHolder* CPDFPageObjHolderFromFPDFFormObject( + } // namespace + + FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV FPDF_CreateNewDocument() { +- auto pDoc = pdfium::MakeUnique( +- pdfium::MakeUnique(), +- pdfium::MakeUnique()); ++ auto pDoc = ++ std::make_unique(std::make_unique(), ++ std::make_unique()); + pDoc->CreateNewDoc(); + + time_t currentTime; +@@ -168,17 +168,13 @@ FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV FPDF_CreateNewDocument() { + } + } + +- CPDF_Dictionary* pInfoDict = pDoc->GetInfo(); ++ RetainPtr pInfoDict = pDoc->GetInfo(); + if (pInfoDict) { + if (IsPDFSandboxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS)) + pInfoDict->SetNewFor("CreationDate", DateStr, false); + pInfoDict->SetNewFor("Creator", L"PDFium"); + } + +-#ifdef PDF_ENABLE_XFA +- pDoc->SetExtension(pdfium::MakeUnique(pDoc.get())); +-#endif // PDF_ENABLE_XFA +- + // Caller takes ownership of pDoc. + return FPDFDocumentFromCPDFDocument(pDoc.release()); + } +@@ -207,7 +203,7 @@ FPDF_EXPORT FPDF_PAGE FPDF_CALLCONV FPDFPage_New(FPDF_DOCUMENT document, + return nullptr; + + page_index = pdfium::clamp(page_index, 0, pDoc->GetPageCount()); +- CPDF_Dictionary* pPageDict = pDoc->CreateNewPage(page_index); ++ RetainPtr pPageDict(pDoc->CreateNewPage(page_index)); + if (!pPageDict) + return nullptr; + +@@ -225,7 +221,7 @@ FPDF_EXPORT FPDF_PAGE FPDF_CALLCONV FPDFPage_New(FPDF_DOCUMENT document, + #endif // PDF_ENABLE_XFA + + auto pPage = pdfium::MakeRetain(pDoc, pPageDict); +- pPage->SetRenderCache(pdfium::MakeUnique(pPage.Get())); ++ pPage->AddPageImageCache(); + pPage->ParseContent(); + + return FPDFPageFromIPDFPage(pPage.Leak()); // Caller takes ownership. +@@ -262,7 +258,8 @@ FPDFPage_RemoveObject(FPDF_PAGE page, FPDF_PAGEOBJECT page_obj) { + if (!IsPageObject(pPage)) + return false; + +- return pPage->RemovePageObject(pPageObj); ++ // Caller takes ownership. ++ return !!pPage->RemovePageObject(pPageObj).release(); + } + + FPDF_EXPORT int FPDF_CALLCONV FPDFPage_CountObjects(FPDF_PAGE page) { +@@ -270,7 +267,7 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFPage_CountObjects(FPDF_PAGE page) { + if (!IsPageObject(pPage)) + return -1; + +- return pPage->GetPageObjectCount(); ++ return pdfium::base::checked_cast(pPage->GetPageObjectCount()); + } + + FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV FPDFPage_GetObject(FPDF_PAGE page, +@@ -297,7 +294,8 @@ FPDFPageObj_CountMarks(FPDF_PAGEOBJECT page_object) { + if (!pPageObj) + return -1; + +- return pPageObj->m_ContentMarks.CountItems(); ++ return pdfium::base::checked_cast( ++ pPageObj->GetContentMarks()->CountItems()); + } + + FPDF_EXPORT FPDF_PAGEOBJECTMARK FPDF_CALLCONV +@@ -306,11 +304,11 @@ FPDFPageObj_GetMark(FPDF_PAGEOBJECT page_object, unsigned long index) { + if (!pPageObj) + return nullptr; + +- auto& mark = pPageObj->m_ContentMarks; +- if (index >= mark.CountItems()) ++ CPDF_ContentMarks* pMarks = pPageObj->GetContentMarks(); ++ if (index >= pMarks->CountItems()) + return nullptr; + +- return FPDFPageObjectMarkFromCPDFContentMarkItem(mark.GetItem(index)); ++ return FPDFPageObjectMarkFromCPDFContentMarkItem(pMarks->GetItem(index)); + } + + FPDF_EXPORT FPDF_PAGEOBJECTMARK FPDF_CALLCONV +@@ -319,11 +317,12 @@ FPDFPageObj_AddMark(FPDF_PAGEOBJECT page_object, FPDF_BYTESTRING name) { + if (!pPageObj) + return nullptr; + +- auto& mark = pPageObj->m_ContentMarks; +- mark.AddMark(name); +- unsigned long index = mark.CountItems() - 1; ++ CPDF_ContentMarks* pMarks = pPageObj->GetContentMarks(); ++ pMarks->AddMark(name); + pPageObj->SetDirty(true); +- return FPDFPageObjectMarkFromCPDFContentMarkItem(mark.GetItem(index)); ++ ++ const size_t index = pMarks->CountItems() - 1; ++ return FPDFPageObjectMarkFromCPDFContentMarkItem(pMarks->GetItem(index)); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +@@ -334,11 +333,11 @@ FPDFPageObj_RemoveMark(FPDF_PAGEOBJECT page_object, FPDF_PAGEOBJECTMARK mark) { + if (!pPageObj || !pMarkItem) + return false; + +- bool result = pPageObj->m_ContentMarks.RemoveMark(pMarkItem); +- if (result) +- pPageObj->SetDirty(true); ++ if (!pPageObj->GetContentMarks()->RemoveMark(pMarkItem)) ++ return false; + +- return result; ++ pPageObj->SetDirty(true); ++ return true; + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +@@ -346,11 +345,10 @@ FPDFPageObjMark_GetName(FPDF_PAGEOBJECTMARK mark, + void* buffer, + unsigned long buflen, + unsigned long* out_buflen) { +- if (!mark || !out_buflen) +- return false; +- + const CPDF_ContentMarkItem* pMarkItem = + CPDFContentMarkItemFromFPDFPageObjectMark(mark); ++ if (!pMarkItem || !out_buflen) ++ return false; + + *out_buflen = Utf16EncodeMaybeCopyAndReturnLength( + WideString::FromUTF8(pMarkItem->GetName().AsStringView()), buffer, +@@ -360,14 +358,13 @@ FPDFPageObjMark_GetName(FPDF_PAGEOBJECTMARK mark, + + FPDF_EXPORT int FPDF_CALLCONV + FPDFPageObjMark_CountParams(FPDF_PAGEOBJECTMARK mark) { +- if (!mark) +- return -1; +- + const CPDF_ContentMarkItem* pMarkItem = + CPDFContentMarkItemFromFPDFPageObjectMark(mark); ++ if (!pMarkItem) ++ return -1; + +- const CPDF_Dictionary* pParams = pMarkItem->GetParam(); +- return pParams ? pParams->size() : 0; ++ RetainPtr pParams = pMarkItem->GetParam(); ++ return pParams ? fxcrt::CollectionSize(*pParams) : 0; + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +@@ -379,7 +376,7 @@ FPDFPageObjMark_GetParamKey(FPDF_PAGEOBJECTMARK mark, + if (!out_buflen) + return false; + +- const CPDF_Dictionary* pParams = GetMarkParamDict(mark); ++ RetainPtr pParams = GetMarkParamDict(mark); + if (!pParams) + return false; + +@@ -399,11 +396,11 @@ FPDFPageObjMark_GetParamKey(FPDF_PAGEOBJECTMARK mark, + FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV + FPDFPageObjMark_GetParamValueType(FPDF_PAGEOBJECTMARK mark, + FPDF_BYTESTRING key) { +- const CPDF_Dictionary* pParams = GetMarkParamDict(mark); ++ RetainPtr pParams = GetMarkParamDict(mark); + if (!pParams) + return FPDF_OBJECT_UNKNOWN; + +- const CPDF_Object* pObject = pParams->GetObjectFor(key); ++ RetainPtr pObject = pParams->GetObjectFor(key); + return pObject ? pObject->GetType() : FPDF_OBJECT_UNKNOWN; + } + +@@ -414,11 +411,11 @@ FPDFPageObjMark_GetParamIntValue(FPDF_PAGEOBJECTMARK mark, + if (!out_value) + return false; + +- const CPDF_Dictionary* pParams = GetMarkParamDict(mark); ++ RetainPtr pParams = GetMarkParamDict(mark); + if (!pParams) + return false; + +- const CPDF_Object* pObj = pParams->GetObjectFor(key); ++ RetainPtr pObj = pParams->GetObjectFor(key); + if (!pObj || !pObj->IsNumber()) + return false; + +@@ -435,11 +432,11 @@ FPDFPageObjMark_GetParamStringValue(FPDF_PAGEOBJECTMARK mark, + if (!out_buflen) + return false; + +- const CPDF_Dictionary* pParams = GetMarkParamDict(mark); ++ RetainPtr pParams = GetMarkParamDict(mark); + if (!pParams) + return false; + +- const CPDF_Object* pObj = pParams->GetObjectFor(key); ++ RetainPtr pObj = pParams->GetObjectFor(key); + if (!pObj || !pObj->IsString()) + return false; + +@@ -457,16 +454,17 @@ FPDFPageObjMark_GetParamBlobValue(FPDF_PAGEOBJECTMARK mark, + if (!out_buflen) + return false; + +- const CPDF_Dictionary* pParams = GetMarkParamDict(mark); ++ RetainPtr pParams = GetMarkParamDict(mark); + if (!pParams) + return false; + +- const CPDF_Object* pObj = pParams->GetObjectFor(key); ++ RetainPtr pObj = pParams->GetObjectFor(key); + if (!pObj || !pObj->IsString()) + return false; + + ByteString result = pObj->GetString(); +- unsigned long len = result.GetLength(); ++ const unsigned long len = ++ pdfium::base::checked_cast(result.GetLength()); + + if (buffer && len <= buflen) + memcpy(buffer, result.c_str(), len); +@@ -484,9 +482,7 @@ FPDFPageObj_HasTransparency(FPDF_PAGEOBJECT page_object) { + if (pPageObj->m_GeneralState.GetBlendType() != BlendMode::kNormal) + return true; + +- const CPDF_Dictionary* pSMaskDict = +- ToDictionary(pPageObj->m_GeneralState.GetSoftMask()); +- if (pSMaskDict) ++ if (pPageObj->m_GeneralState.GetSoftMask()) + return true; + + if (pPageObj->m_GeneralState.GetFillAlpha() != 1.0f) +@@ -516,7 +512,8 @@ FPDFPageObjMark_SetIntParam(FPDF_DOCUMENT document, + if (!pPageObj || !PageObjectContainsMark(pPageObj, mark)) + return false; + +- CPDF_Dictionary* pParams = GetOrCreateMarkParamsDict(document, mark); ++ RetainPtr pParams = ++ GetOrCreateMarkParamsDict(document, mark); + if (!pParams) + return false; + +@@ -535,7 +532,8 @@ FPDFPageObjMark_SetStringParam(FPDF_DOCUMENT document, + if (!pPageObj || !PageObjectContainsMark(pPageObj, mark)) + return false; + +- CPDF_Dictionary* pParams = GetOrCreateMarkParamsDict(document, mark); ++ RetainPtr pParams = ++ GetOrCreateMarkParamsDict(document, mark); + if (!pParams) + return false; + +@@ -555,7 +553,8 @@ FPDFPageObjMark_SetBlobParam(FPDF_DOCUMENT document, + if (!pPageObj || !PageObjectContainsMark(pPageObj, mark)) + return false; + +- CPDF_Dictionary* pParams = GetOrCreateMarkParamsDict(document, mark); ++ RetainPtr pParams = ++ GetOrCreateMarkParamsDict(document, mark); + if (!pParams) + return false; + +@@ -576,7 +575,7 @@ FPDFPageObjMark_RemoveParam(FPDF_PAGEOBJECT page_object, + if (!pPageObj) + return false; + +- CPDF_Dictionary* pParams = GetMarkParamDict(mark); ++ RetainPtr pParams = GetMarkParamDict(mark); + if (!pParams) + return false; + +@@ -590,7 +589,8 @@ FPDFPageObjMark_RemoveParam(FPDF_PAGEOBJECT page_object, + + FPDF_EXPORT int FPDF_CALLCONV FPDFPageObj_GetType(FPDF_PAGEOBJECT page_object) { + CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object); +- return pPageObj ? pPageObj->GetType() : FPDF_PAGEOBJ_UNKNOWN; ++ return pPageObj ? static_cast(pPageObj->GetType()) ++ : FPDF_PAGEOBJ_UNKNOWN; + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GenerateContent(FPDF_PAGE page) { +@@ -619,6 +619,57 @@ FPDFPageObj_Transform(FPDF_PAGEOBJECT page_object, + pPageObj->Transform(matrix); + } + ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFPageObj_GetMatrix(FPDF_PAGEOBJECT page_object, FS_MATRIX* matrix) { ++ CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object); ++ if (!pPageObj || !matrix) ++ return false; ++ ++ switch (pPageObj->GetType()) { ++ case CPDF_PageObject::Type::kText: ++ *matrix = FSMatrixFromCFXMatrix(pPageObj->AsText()->GetTextMatrix()); ++ return true; ++ case CPDF_PageObject::Type::kPath: ++ *matrix = FSMatrixFromCFXMatrix(pPageObj->AsPath()->matrix()); ++ return true; ++ case CPDF_PageObject::Type::kImage: ++ *matrix = FSMatrixFromCFXMatrix(pPageObj->AsImage()->matrix()); ++ return true; ++ case CPDF_PageObject::Type::kShading: ++ return false; ++ case CPDF_PageObject::Type::kForm: ++ *matrix = FSMatrixFromCFXMatrix(pPageObj->AsForm()->form_matrix()); ++ return true; ++ } ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFPageObj_SetMatrix(FPDF_PAGEOBJECT page_object, const FS_MATRIX* matrix) { ++ CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object); ++ if (!pPageObj || !matrix) ++ return false; ++ ++ CFX_Matrix cmatrix = CFXMatrixFromFSMatrix(*matrix); ++ switch (pPageObj->GetType()) { ++ case CPDF_PageObject::Type::kText: ++ pPageObj->AsText()->SetTextMatrix(cmatrix); ++ break; ++ case CPDF_PageObject::Type::kPath: ++ pPageObj->AsPath()->SetPathMatrix(cmatrix); ++ break; ++ case CPDF_PageObject::Type::kImage: ++ pPageObj->AsImage()->SetImageMatrix(cmatrix); ++ break; ++ case CPDF_PageObject::Type::kShading: ++ return false; ++ case CPDF_PageObject::Type::kForm: ++ pPageObj->AsForm()->SetFormMatrix(cmatrix); ++ break; ++ } ++ pPageObj->SetDirty(true); ++ return true; ++} ++ + FPDF_EXPORT void FPDF_CALLCONV + FPDFPageObj_SetBlendMode(FPDF_PAGEOBJECT page_object, + FPDF_BYTESTRING blend_mode) { +@@ -648,17 +699,17 @@ FPDF_EXPORT void FPDF_CALLCONV FPDFPage_TransformAnnots(FPDF_PAGE page, + (float)f); + CFX_FloatRect rect = matrix.TransformRect(pAnnot->GetRect()); + +- CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict(); +- CPDF_Array* pRectArray = pAnnotDict->GetArrayFor("Rect"); ++ RetainPtr pAnnotDict = pAnnot->GetMutableAnnotDict(); ++ RetainPtr pRectArray = pAnnotDict->GetMutableArrayFor("Rect"); + if (pRectArray) + pRectArray->Clear(); + else + pRectArray = pAnnotDict->SetNewFor("Rect"); + +- pRectArray->AddNew(rect.left); +- pRectArray->AddNew(rect.bottom); +- pRectArray->AddNew(rect.right); +- pRectArray->AddNew(rect.top); ++ pRectArray->AppendNew(rect.left); ++ pRectArray->AppendNew(rect.bottom); ++ pRectArray->AppendNew(rect.right); ++ pRectArray->AppendNew(rect.top); + + // TODO(unknown): Transform AP's rectangle + } +@@ -671,8 +722,8 @@ FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetRotation(FPDF_PAGE page, + return; + + rotate %= 4; +- pPage->GetDict()->SetNewFor(pdfium::page_object::kRotate, +- rotate * 90); ++ pPage->GetMutableDict()->SetNewFor(pdfium::page_object::kRotate, ++ rotate * 90); + pPage->UpdateDimensions(); + } + +@@ -688,7 +739,8 @@ FPDF_BOOL FPDFPageObj_SetFillColor(FPDF_PAGEOBJECT page_object, + std::vector rgb = {R / 255.f, G / 255.f, B / 255.f}; + pPageObj->m_GeneralState.SetFillAlpha(A / 255.f); + pPageObj->m_ColorState.SetFillColor( +- CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB), rgb); ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB), ++ std::move(rgb)); + pPageObj->SetDirty(true); + return true; + } +@@ -732,6 +784,44 @@ FPDFPageObj_GetBounds(FPDF_PAGEOBJECT page_object, + return true; + } + ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFPageObj_GetRotatedBounds(FPDF_PAGEOBJECT page_object, ++ FS_QUADPOINTSF* quad_points) { ++ CPDF_PageObject* cpage_object = CPDFPageObjectFromFPDFPageObject(page_object); ++ if (!cpage_object || !quad_points) ++ return false; ++ ++ CFX_Matrix matrix; ++ switch (cpage_object->GetType()) { ++ case CPDF_PageObject::Type::kText: ++ matrix = cpage_object->AsText()->GetTextMatrix(); ++ break; ++ case CPDF_PageObject::Type::kImage: ++ matrix = cpage_object->AsImage()->matrix(); ++ break; ++ default: ++ // TODO(crbug.com/pdfium/1840): Support more object types. ++ return false; ++ } ++ ++ const CFX_FloatRect& bbox = cpage_object->GetOriginalRect(); ++ const CFX_PointF bottom_left = matrix.Transform({bbox.left, bbox.bottom}); ++ const CFX_PointF bottom_right = matrix.Transform({bbox.right, bbox.bottom}); ++ const CFX_PointF top_right = matrix.Transform({bbox.right, bbox.top}); ++ const CFX_PointF top_left = matrix.Transform({bbox.left, bbox.top}); ++ ++ // See PDF 32000-1:2008, figure 64 for the QuadPoints ordering. ++ quad_points->x1 = bottom_left.x; ++ quad_points->y1 = bottom_left.y; ++ quad_points->x2 = bottom_right.x; ++ quad_points->y2 = bottom_right.y; ++ quad_points->x3 = top_right.x; ++ quad_points->y3 = top_right.y; ++ quad_points->x4 = top_left.x; ++ quad_points->y4 = top_left.y; ++ return true; ++} ++ + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFPageObj_SetStrokeColor(FPDF_PAGEOBJECT page_object, + unsigned int R, +@@ -745,7 +835,8 @@ FPDFPageObj_SetStrokeColor(FPDF_PAGEOBJECT page_object, + std::vector rgb = {R / 255.f, G / 255.f, B / 255.f}; + pPageObj->m_GeneralState.SetStrokeAlpha(A / 255.f); + pPageObj->m_ColorState.SetStrokeColor( +- CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB), rgb); ++ CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB), ++ std::move(rgb)); + pPageObj->SetDirty(true); + return true; + } +@@ -795,7 +886,7 @@ FPDFPageObj_GetStrokeWidth(FPDF_PAGEOBJECT page_object, float* width) { + FPDF_EXPORT int FPDF_CALLCONV + FPDFPageObj_GetLineJoin(FPDF_PAGEOBJECT page_object) { + auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object); +- return pPageObj ? pPageObj->m_GraphState.GetLineJoin() : -1; ++ return pPageObj ? static_cast(pPageObj->m_GraphState.GetLineJoin()) : -1; + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +@@ -804,11 +895,7 @@ FPDFPageObj_SetLineJoin(FPDF_PAGEOBJECT page_object, int line_join) { + if (!pPageObj) + return false; + +- constexpr int kLineJoinMiter = +- static_cast(CFX_GraphStateData::LineJoin::LineJoinMiter); +- constexpr int kLineJoinBevel = +- static_cast(CFX_GraphStateData::LineJoin::LineJoinBevel); +- if (line_join < kLineJoinMiter || line_join > kLineJoinBevel) ++ if (line_join < FPDF_LINEJOIN_MITER || line_join > FPDF_LINEJOIN_BEVEL) + return false; + + pPageObj->m_GraphState.SetLineJoin( +@@ -820,7 +907,7 @@ FPDFPageObj_SetLineJoin(FPDF_PAGEOBJECT page_object, int line_join) { + FPDF_EXPORT int FPDF_CALLCONV + FPDFPageObj_GetLineCap(FPDF_PAGEOBJECT page_object) { + auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object); +- return pPageObj ? pPageObj->m_GraphState.GetLineCap() : -1; ++ return pPageObj ? static_cast(pPageObj->m_GraphState.GetLineCap()) : -1; + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +@@ -829,23 +916,91 @@ FPDFPageObj_SetLineCap(FPDF_PAGEOBJECT page_object, int line_cap) { + if (!pPageObj) + return false; + +- constexpr int kLineCapButt = +- static_cast(CFX_GraphStateData::LineCap::LineCapButt); +- constexpr int kLineCapSquare = +- static_cast(CFX_GraphStateData::LineCap::LineCapSquare); +- if (line_cap < kLineCapButt || line_cap > kLineCapSquare) ++ if (line_cap < FPDF_LINECAP_BUTT || ++ line_cap > FPDF_LINECAP_PROJECTING_SQUARE) { + return false; +- ++ } + pPageObj->m_GraphState.SetLineCap( + static_cast(line_cap)); + pPageObj->SetDirty(true); + return true; + } + ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFPageObj_GetDashPhase(FPDF_PAGEOBJECT page_object, float* phase) { ++ auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object); ++ if (!pPageObj || !phase) ++ return false; ++ ++ *phase = pPageObj->m_GraphState.GetLineDashPhase(); ++ return true; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFPageObj_SetDashPhase(FPDF_PAGEOBJECT page_object, float phase) { ++ auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object); ++ if (!pPageObj) ++ return false; ++ ++ pPageObj->m_GraphState.SetLineDashPhase(phase); ++ pPageObj->SetDirty(true); ++ return true; ++} ++ ++FPDF_EXPORT int FPDF_CALLCONV ++FPDFPageObj_GetDashCount(FPDF_PAGEOBJECT page_object) { ++ auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object); ++ return pPageObj ? pdfium::base::checked_cast( ++ pPageObj->m_GraphState.GetLineDashSize()) ++ : -1; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFPageObj_GetDashArray(FPDF_PAGEOBJECT page_object, ++ float* dash_array, ++ size_t dash_count) { ++ auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object); ++ if (!pPageObj || !dash_array) ++ return false; ++ ++ auto dash_vector = pPageObj->m_GraphState.GetLineDashArray(); ++ if (dash_vector.size() > dash_count) ++ return false; ++ ++ memcpy(dash_array, dash_vector.data(), dash_vector.size() * sizeof(float)); ++ return true; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFPageObj_SetDashArray(FPDF_PAGEOBJECT page_object, ++ const float* dash_array, ++ size_t dash_count, ++ float phase) { ++ if (dash_count > 0 && !dash_array) ++ return false; ++ ++ auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object); ++ if (!pPageObj) ++ return false; ++ ++ std::vector dashes; ++ if (dash_count > 0) { ++ dashes.reserve(dash_count); ++ dashes.assign(dash_array, dash_array + dash_count); ++ } ++ ++ pPageObj->m_GraphState.SetLineDash(dashes, phase, 1.0f); ++ ++ pPageObj->SetDirty(true); ++ return true; ++} ++ + FPDF_EXPORT int FPDF_CALLCONV + FPDFFormObj_CountObjects(FPDF_PAGEOBJECT form_object) { + const auto* pObjectList = CPDFPageObjHolderFromFPDFFormObject(form_object); +- return pObjectList ? pObjectList->GetPageObjectCount() : -1; ++ return pObjectList ? pdfium::base::checked_cast( ++ pObjectList->GetPageObjectCount()) ++ : -1; + } + + FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV +@@ -857,13 +1012,3 @@ FPDFFormObj_GetObject(FPDF_PAGEOBJECT form_object, unsigned long index) { + return FPDFPageObjectFromCPDFPageObject( + pObjectList->GetPageObjectByIndex(index)); + } +- +-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +-FPDFFormObj_GetMatrix(FPDF_PAGEOBJECT form_object, FS_MATRIX* matrix) { +- CPDF_FormObject* pFormObj = CPDFFormObjectFromFPDFPageObject(form_object); +- if (!pFormObj || !matrix) +- return false; +- +- *matrix = FSMatrixFromCFXMatrix(pFormObj->form_matrix()); +- return true; +-} +diff --git a/fpdfsdk/fpdf_editpage_embeddertest.cpp b/fpdfsdk/fpdf_editpage_embeddertest.cpp +index 6650b403d..c74d6dcd1 100644 +--- a/fpdfsdk/fpdf_editpage_embeddertest.cpp ++++ b/fpdfsdk/fpdf_editpage_embeddertest.cpp +@@ -1,22 +1,21 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "core/fxcrt/fx_system.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" + #include "public/fpdf_edit.h" + #include "testing/embedder_test.h" ++#include "testing/embedder_test_constants.h" + + class FPDFEditPageEmbedderTest : public EmbedderTest {}; + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_Rotation DISABLED_Rotation +-#else +-#define MAYBE_Rotation Rotation +-#endif +-TEST_F(FPDFEditPageEmbedderTest, MAYBE_Rotation) { +- const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe"; +- const char kRotatedMD5[] = "d599429574ff0dcad3bc898ea8b874ca"; ++TEST_F(FPDFEditPageEmbedderTest, Rotation) { ++ const char* rotated_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "eded83f75f3d0332c584c416c571c0df"; ++ return "d599429574ff0dcad3bc898ea8b874ca"; ++ }(); + + { + ASSERT_TRUE(OpenDocument("rectangles.pdf")); +@@ -31,7 +30,8 @@ TEST_F(FPDFEditPageEmbedderTest, MAYBE_Rotation) { + EXPECT_EQ(200, page_width); + EXPECT_EQ(300, page_height); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5); ++ CompareBitmap(bitmap.get(), page_width, page_height, ++ pdfium::RectanglesChecksum()); + } + + FPDFPage_SetRotation(page, 1); +@@ -46,7 +46,7 @@ TEST_F(FPDFEditPageEmbedderTest, MAYBE_Rotation) { + EXPECT_EQ(300, page_width); + EXPECT_EQ(200, page_height); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), page_width, page_height, kRotatedMD5); ++ CompareBitmap(bitmap.get(), page_width, page_height, rotated_checksum); + } + + UnloadPage(page); +@@ -66,7 +66,7 @@ TEST_F(FPDFEditPageEmbedderTest, MAYBE_Rotation) { + EXPECT_EQ(300, page_width); + EXPECT_EQ(200, page_height); + ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page); +- CompareBitmap(bitmap.get(), page_width, page_height, kRotatedMD5); ++ CompareBitmap(bitmap.get(), page_width, page_height, rotated_checksum); + + CloseSavedPage(saved_page); + CloseSavedDocument(); +@@ -97,7 +97,7 @@ TEST_F(FPDFEditPageEmbedderTest, HasTransparencyInvalid) { + + TEST_F(FPDFEditPageEmbedderTest, HasTransparencyPath) { + constexpr int kExpectedObjectCount = 8; +- EXPECT_TRUE(OpenDocument("rectangles.pdf")); ++ ASSERT_TRUE(OpenDocument("rectangles.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + ASSERT_EQ(kExpectedObjectCount, FPDFPage_CountObjects(page)); +@@ -115,7 +115,7 @@ TEST_F(FPDFEditPageEmbedderTest, HasTransparencyPath) { + + TEST_F(FPDFEditPageEmbedderTest, HasTransparencyText) { + constexpr int kExpectedObjectCount = 2; +- EXPECT_TRUE(OpenDocument("text_render_mode.pdf")); ++ ASSERT_TRUE(OpenDocument("text_render_mode.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + ASSERT_EQ(kExpectedObjectCount, FPDFPage_CountObjects(page)); +@@ -155,3 +155,305 @@ TEST_F(FPDFEditPageEmbedderTest, GetFillAndStrokeForImage) { + + UnloadPage(page); + } ++ ++TEST_F(FPDFEditPageEmbedderTest, DashingArrayAndPhase) { ++ { ++ EXPECT_FALSE(FPDFPageObj_GetDashPhase(nullptr, nullptr)); ++ ++ float phase = -1123.5f; ++ EXPECT_FALSE(FPDFPageObj_GetDashPhase(nullptr, &phase)); ++ EXPECT_FLOAT_EQ(-1123.5f, phase); ++ ++ EXPECT_EQ(-1, FPDFPageObj_GetDashCount(nullptr)); ++ ++ EXPECT_FALSE(FPDFPageObj_GetDashArray(nullptr, nullptr, 3)); ++ ++ float get_array[] = {-1.0f, -1.0f, -1.0f}; ++ EXPECT_FALSE(FPDFPageObj_GetDashArray(nullptr, get_array, 3)); ++ for (int i = 0; i < 3; i++) ++ EXPECT_FLOAT_EQ(-1.0f, get_array[i]); ++ ++ EXPECT_FALSE(FPDFPageObj_SetDashPhase(nullptr, 5.0f)); ++ EXPECT_FALSE(FPDFPageObj_SetDashArray(nullptr, nullptr, 3, 5.0f)); ++ ++ float set_array[] = {1.0f, 2.0f, 3.0f}; ++ EXPECT_FALSE(FPDFPageObj_SetDashArray(nullptr, set_array, 3, 5.0f)); ++ } ++ ++ constexpr int kExpectedObjectCount = 3; ++ ASSERT_TRUE(OpenDocument("dashed_lines.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ ASSERT_EQ(kExpectedObjectCount, FPDFPage_CountObjects(page)); ++ ++ { ++ FPDF_PAGEOBJECT path = FPDFPage_GetObject(page, 0); ++ ASSERT_TRUE(path); ++ EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(path)); ++ ++ EXPECT_FALSE(FPDFPageObj_GetDashPhase(path, nullptr)); ++ EXPECT_FALSE(FPDFPageObj_GetDashArray(path, nullptr, 3)); ++ EXPECT_FALSE(FPDFPageObj_SetDashArray(path, nullptr, 3, 5.0f)); ++ ++ float phase = -1123.5f; ++ EXPECT_TRUE(FPDFPageObj_GetDashPhase(path, &phase)); ++ EXPECT_FLOAT_EQ(0.0f, phase); ++ EXPECT_EQ(0, FPDFPageObj_GetDashCount(path)); ++ ++ float get_array[] = {-1.0f, -1.0f, -1.0f}; ++ EXPECT_TRUE(FPDFPageObj_GetDashArray(path, get_array, 3)); ++ for (int i = 0; i < 3; i++) ++ EXPECT_FLOAT_EQ(-1.0f, get_array[i]); ++ } ++ ++ { ++ FPDF_PAGEOBJECT path = FPDFPage_GetObject(page, 1); ++ ASSERT_TRUE(path); ++ EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(path)); ++ ++ float phase = -1123.5f; ++ EXPECT_TRUE(FPDFPageObj_GetDashPhase(path, &phase)); ++ EXPECT_LT(0.0f, phase); ++ ASSERT_EQ(6, FPDFPageObj_GetDashCount(path)); ++ ++ float dash_array[] = {-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f}; ++ ASSERT_TRUE(FPDFPageObj_GetDashArray(path, dash_array, 6)); ++ ++ for (int i = 0; i < 6; i++) ++ EXPECT_LT(0.0f, dash_array[i]); ++ ++ // the array is decreasing in value. ++ for (int i = 0; i < 5; i++) ++ EXPECT_GT(dash_array[i], dash_array[i + 1]); ++ ++ // modify phase ++ EXPECT_TRUE(FPDFPageObj_SetDashPhase(path, 1.0f)); ++ ++ phase = -1123.5f; ++ EXPECT_TRUE(FPDFPageObj_GetDashPhase(path, &phase)); ++ EXPECT_FLOAT_EQ(1.0f, phase); ++ ++ // clear array ++ EXPECT_TRUE(FPDFPageObj_SetDashArray(path, nullptr, 0, 0.0f)); ++ EXPECT_EQ(0, FPDFPageObj_GetDashCount(path)); ++ ++ phase = -1123.5f; ++ EXPECT_TRUE(FPDFPageObj_GetDashPhase(path, &phase)); ++ EXPECT_FLOAT_EQ(0.0f, phase); ++ } ++ ++ { ++ FPDF_PAGEOBJECT path = FPDFPage_GetObject(page, 2); ++ ASSERT_TRUE(path); ++ EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(path)); ++ ++ float phase = -1123.5f; ++ EXPECT_TRUE(FPDFPageObj_GetDashPhase(path, &phase)); ++ EXPECT_FLOAT_EQ(0.0f, phase); ++ ++ EXPECT_EQ(0, FPDFPageObj_GetDashCount(path)); ++ ++ // `get_array` should be unmodified ++ float get_array[] = {-1.0f, -1.0f, -1.0f, -1.0f}; ++ EXPECT_TRUE(FPDFPageObj_GetDashArray(path, get_array, 4)); ++ for (int i = 0; i < 4; i++) ++ EXPECT_FLOAT_EQ(-1.0f, get_array[i]); ++ ++ // modify dash_array and phase ++ const float set_array[] = {1.0f, 2.0f, 3.0f}; ++ EXPECT_TRUE(FPDFPageObj_SetDashArray(path, set_array, 3, 5.0f)); ++ ++ phase = -1123.5f; ++ EXPECT_TRUE(FPDFPageObj_GetDashPhase(path, &phase)); ++ EXPECT_FLOAT_EQ(5.0f, phase); ++ ASSERT_EQ(3, FPDFPageObj_GetDashCount(path)); ++ ++ // Pretend `get_array` has too few members. ++ EXPECT_FALSE(FPDFPageObj_GetDashArray(path, get_array, 2)); ++ for (int i = 0; i < 4; i++) ++ EXPECT_FLOAT_EQ(-1.0f, get_array[i]); ++ ++ ASSERT_TRUE(FPDFPageObj_GetDashArray(path, get_array, 4)); ++ ++ // `get_array` should be modified only up to dash_count ++ for (int i = 0; i < 3; i++) ++ EXPECT_FLOAT_EQ(static_cast(i + 1), get_array[i]); ++ ++ EXPECT_FLOAT_EQ(-1.0f, get_array[3]); ++ ++ // clear array ++ EXPECT_TRUE(FPDFPageObj_SetDashArray(path, set_array, 0, 4.0f)); ++ EXPECT_EQ(0, FPDFPageObj_GetDashCount(path)); ++ ++ phase = -1123.5f; ++ EXPECT_TRUE(FPDFPageObj_GetDashPhase(path, &phase)); ++ EXPECT_FLOAT_EQ(4.0f, phase); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditPageEmbedderTest, GetRotatedBoundsBadParameters) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 0); ++ ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(obj)); ++ ++ FS_QUADPOINTSF quad; ++ ASSERT_FALSE(FPDFPageObj_GetRotatedBounds(nullptr, nullptr)); ++ ASSERT_FALSE(FPDFPageObj_GetRotatedBounds(obj, nullptr)); ++ ASSERT_FALSE(FPDFPageObj_GetRotatedBounds(nullptr, &quad)); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditPageEmbedderTest, GetBoundsForNormalText) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 0); ++ ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(obj)); ++ ++ constexpr float kExpectedLeft = 20.348f; ++ constexpr float kExpectedBottom = 48.164f; ++ constexpr float kExpectedRight = 83.36f; ++ constexpr float kExpectedTop = 58.328f; ++ ++ float left; ++ float bottom; ++ float right; ++ float top; ++ ASSERT_TRUE(FPDFPageObj_GetBounds(obj, &left, &bottom, &right, &top)); ++ EXPECT_FLOAT_EQ(kExpectedLeft, left); ++ EXPECT_FLOAT_EQ(kExpectedBottom, bottom); ++ EXPECT_FLOAT_EQ(kExpectedRight, right); ++ EXPECT_FLOAT_EQ(kExpectedTop, top); ++ ++ FS_QUADPOINTSF quad; ++ ASSERT_TRUE(FPDFPageObj_GetRotatedBounds(obj, &quad)); ++ EXPECT_FLOAT_EQ(kExpectedLeft, quad.x1); ++ EXPECT_FLOAT_EQ(kExpectedBottom, quad.y1); ++ EXPECT_FLOAT_EQ(kExpectedRight, quad.x2); ++ EXPECT_FLOAT_EQ(kExpectedBottom, quad.y2); ++ EXPECT_FLOAT_EQ(kExpectedRight, quad.x3); ++ EXPECT_FLOAT_EQ(kExpectedTop, quad.y3); ++ EXPECT_FLOAT_EQ(kExpectedLeft, quad.x4); ++ EXPECT_FLOAT_EQ(kExpectedTop, quad.y4); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditPageEmbedderTest, GetBoundsForRotatedText) { ++ ASSERT_TRUE(OpenDocument("rotated_text.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 0); ++ ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(obj)); ++ ++ constexpr float kExpectedLeft = 98.9478f; ++ constexpr float kExpectedBottom = 78.2607f; ++ constexpr float kExpectedRight = 126.32983f; ++ constexpr float kExpectedTop = 105.64272f; ++ ++ float left; ++ float bottom; ++ float right; ++ float top; ++ ASSERT_TRUE(FPDFPageObj_GetBounds(obj, &left, &bottom, &right, &top)); ++ EXPECT_FLOAT_EQ(kExpectedLeft, left); ++ EXPECT_FLOAT_EQ(kExpectedBottom, bottom); ++ EXPECT_FLOAT_EQ(kExpectedRight, right); ++ EXPECT_FLOAT_EQ(kExpectedTop, top); ++ ++ FS_QUADPOINTSF quad; ++ ASSERT_TRUE(FPDFPageObj_GetRotatedBounds(obj, &quad)); ++ EXPECT_FLOAT_EQ(kExpectedLeft, quad.x1); ++ EXPECT_FLOAT_EQ(98.4557f, quad.y1); ++ EXPECT_FLOAT_EQ(119.14279f, quad.x2); ++ EXPECT_FLOAT_EQ(kExpectedBottom, quad.y2); ++ EXPECT_FLOAT_EQ(kExpectedRight, quad.x3); ++ EXPECT_FLOAT_EQ(85.447739f, quad.y3); ++ EXPECT_FLOAT_EQ(106.13486f, quad.x4); ++ EXPECT_FLOAT_EQ(kExpectedTop, quad.y4); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditPageEmbedderTest, GetBoundsForNormalImage) { ++ ASSERT_TRUE(OpenDocument("matte.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 2); ++ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); ++ ++ constexpr float kExpectedLeft = 0.0f; ++ constexpr float kExpectedBottom = 90.0f; ++ constexpr float kExpectedRight = 40.0f; ++ constexpr float kExpectedTop = 150.0f; ++ ++ float left; ++ float bottom; ++ float right; ++ float top; ++ ASSERT_TRUE(FPDFPageObj_GetBounds(obj, &left, &bottom, &right, &top)); ++ EXPECT_FLOAT_EQ(kExpectedLeft, left); ++ EXPECT_FLOAT_EQ(kExpectedBottom, bottom); ++ EXPECT_FLOAT_EQ(kExpectedRight, right); ++ EXPECT_FLOAT_EQ(kExpectedTop, top); ++ ++ FS_QUADPOINTSF quad; ++ ASSERT_TRUE(FPDFPageObj_GetRotatedBounds(obj, &quad)); ++ EXPECT_FLOAT_EQ(kExpectedLeft, quad.x1); ++ EXPECT_FLOAT_EQ(kExpectedBottom, quad.y1); ++ EXPECT_FLOAT_EQ(kExpectedRight, quad.x2); ++ EXPECT_FLOAT_EQ(kExpectedBottom, quad.y2); ++ EXPECT_FLOAT_EQ(kExpectedRight, quad.x3); ++ EXPECT_FLOAT_EQ(kExpectedTop, quad.y3); ++ EXPECT_FLOAT_EQ(kExpectedLeft, quad.x4); ++ EXPECT_FLOAT_EQ(kExpectedTop, quad.y4); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFEditPageEmbedderTest, GetBoundsForRotatedImage) { ++ ASSERT_TRUE(OpenDocument("rotated_image.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 0); ++ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj)); ++ ++ constexpr float kExpectedLeft = 100.0f; ++ constexpr float kExpectedBottom = 70.0f; ++ constexpr float kExpectedRight = 170.0f; ++ constexpr float kExpectedTop = 140.0f; ++ ++ float left; ++ float bottom; ++ float right; ++ float top; ++ ASSERT_TRUE(FPDFPageObj_GetBounds(obj, &left, &bottom, &right, &top)); ++ EXPECT_FLOAT_EQ(kExpectedLeft, left); ++ EXPECT_FLOAT_EQ(kExpectedBottom, bottom); ++ EXPECT_FLOAT_EQ(kExpectedRight, right); ++ EXPECT_FLOAT_EQ(kExpectedTop, top); ++ ++ FS_QUADPOINTSF quad; ++ ASSERT_TRUE(FPDFPageObj_GetRotatedBounds(obj, &quad)); ++ EXPECT_FLOAT_EQ(kExpectedLeft, quad.x1); ++ EXPECT_FLOAT_EQ(100.0f, quad.y1); ++ EXPECT_FLOAT_EQ(130.0f, quad.x2); ++ EXPECT_FLOAT_EQ(kExpectedBottom, quad.y2); ++ EXPECT_FLOAT_EQ(kExpectedRight, quad.x3); ++ EXPECT_FLOAT_EQ(110.0f, quad.y3); ++ EXPECT_FLOAT_EQ(140.0f, quad.x4); ++ EXPECT_FLOAT_EQ(kExpectedTop, quad.y4); ++ ++ UnloadPage(page); ++} +diff --git a/fpdfsdk/fpdf_editpath.cpp b/fpdfsdk/fpdf_editpath.cpp +index aaa4b72fe..4d481f832 100644 +--- a/fpdfsdk/fpdf_editpath.cpp ++++ b/fpdfsdk/fpdf_editpath.cpp +@@ -1,41 +1,49 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "public/fpdf_edit.h" + ++#include + #include +-#include + + #include "core/fpdfapi/page/cpdf_path.h" + #include "core/fpdfapi/page/cpdf_pathobject.h" + #include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/stl_util.h" + #include "fpdfsdk/cpdfsdk_helpers.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/span.h" + + // These checks are here because core/ and public/ cannot depend on each other. +-static_assert(CFX_GraphStateData::LineCapButt == FPDF_LINECAP_BUTT, +- "CFX_GraphStateData::LineCapButt value mismatch"); +-static_assert(CFX_GraphStateData::LineCapRound == FPDF_LINECAP_ROUND, +- "CFX_GraphStateData::LineCapRound value mismatch"); +-static_assert(CFX_GraphStateData::LineCapSquare == ++static_assert(static_cast(CFX_GraphStateData::LineCap::kButt) == ++ FPDF_LINECAP_BUTT, ++ "CFX_GraphStateData::LineCap::kButt value mismatch"); ++static_assert(static_cast(CFX_GraphStateData::LineCap::kRound) == ++ FPDF_LINECAP_ROUND, ++ "CFX_GraphStateData::LineCap::kRound value mismatch"); ++static_assert(static_cast(CFX_GraphStateData::LineCap::kSquare) == + FPDF_LINECAP_PROJECTING_SQUARE, +- "CFX_GraphStateData::LineCapSquare value mismatch"); +- +-static_assert(CFX_GraphStateData::LineJoinMiter == FPDF_LINEJOIN_MITER, +- "CFX_GraphStateData::LineJoinMiter value mismatch"); +-static_assert(CFX_GraphStateData::LineJoinRound == FPDF_LINEJOIN_ROUND, +- "CFX_GraphStateData::LineJoinRound value mismatch"); +-static_assert(CFX_GraphStateData::LineJoinBevel == FPDF_LINEJOIN_BEVEL, +- "CFX_GraphStateData::LineJoinBevel value mismatch"); +- +-static_assert(static_cast(FXPT_TYPE::LineTo) == FPDF_SEGMENT_LINETO, +- "FXPT_TYPE::LineTo value mismatch"); +-static_assert(static_cast(FXPT_TYPE::BezierTo) == FPDF_SEGMENT_BEZIERTO, +- "FXPT_TYPE::BezierTo value mismatch"); +-static_assert(static_cast(FXPT_TYPE::MoveTo) == FPDF_SEGMENT_MOVETO, +- "FXPT_TYPE::MoveTo value mismatch"); ++ "CFX_GraphStateData::LineCap::kSquare value mismatch"); ++ ++static_assert(static_cast(CFX_GraphStateData::LineJoin::kMiter) == ++ FPDF_LINEJOIN_MITER, ++ "CFX_GraphStateData::LineJoin::kMiter value mismatch"); ++static_assert(static_cast(CFX_GraphStateData::LineJoin::kRound) == ++ FPDF_LINEJOIN_ROUND, ++ "CFX_GraphStateData::LineJoin::kRound value mismatch"); ++static_assert(static_cast(CFX_GraphStateData::LineJoin::kBevel) == ++ FPDF_LINEJOIN_BEVEL, ++ "CFX_GraphStateData::LineJoin::kBevel value mismatch"); ++ ++static_assert(static_cast(CFX_Path::Point::Type::kLine) == ++ FPDF_SEGMENT_LINETO, ++ "CFX_Path::Point::Type::kLine value mismatch"); ++static_assert(static_cast(CFX_Path::Point::Type::kBezier) == ++ FPDF_SEGMENT_BEZIERTO, ++ "CFX_Path::Point::Type::kBezier value mismatch"); ++static_assert(static_cast(CFX_Path::Point::Type::kMove) == ++ FPDF_SEGMENT_MOVETO, ++ "CFX_Path::Point::Type::kMove value mismatch"); + + namespace { + +@@ -48,8 +56,8 @@ CPDF_PathObject* CPDFPathObjectFromFPDFPageObject(FPDF_PAGEOBJECT page_object) { + + FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV FPDFPageObj_CreateNewPath(float x, + float y) { +- auto pPathObj = pdfium::MakeUnique(); +- pPathObj->path().AppendPoint(CFX_PointF(x, y), FXPT_TYPE::MoveTo, false); ++ auto pPathObj = std::make_unique(); ++ pPathObj->path().AppendPoint(CFX_PointF(x, y), CFX_Path::Point::Type::kMove); + pPathObj->DefaultStates(); + + // Caller takes ownership. +@@ -60,7 +68,7 @@ FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV FPDFPageObj_CreateNewRect(float x, + float y, + float w, + float h) { +- auto pPathObj = pdfium::MakeUnique(); ++ auto pPathObj = std::make_unique(); + pPathObj->path().AppendRect(x, y, x + w, y + h); + pPathObj->DefaultStates(); + +@@ -72,7 +80,7 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFPath_CountSegments(FPDF_PAGEOBJECT path) { + auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path); + if (!pPathObj) + return -1; +- return pdfium::CollectionSize(pPathObj->path().GetPoints()); ++ return fxcrt::CollectionSize(pPathObj->path().GetPoints()); + } + + FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV +@@ -81,8 +89,8 @@ FPDFPath_GetPathSegment(FPDF_PAGEOBJECT path, int index) { + if (!pPathObj) + return nullptr; + +- const std::vector& points = pPathObj->path().GetPoints(); +- if (!pdfium::IndexInBounds(points, index)) ++ pdfium::span points = pPathObj->path().GetPoints(); ++ if (!fxcrt::IndexInBounds(points, index)) + return nullptr; + + return FPDFPathSegmentFromFXPathPoint(&points[index]); +@@ -95,7 +103,7 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_MoveTo(FPDF_PAGEOBJECT path, + if (!pPathObj) + return false; + +- pPathObj->path().AppendPoint(CFX_PointF(x, y), FXPT_TYPE::MoveTo, false); ++ pPathObj->path().AppendPoint(CFX_PointF(x, y), CFX_Path::Point::Type::kMove); + pPathObj->SetDirty(true); + return true; + } +@@ -107,7 +115,7 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_LineTo(FPDF_PAGEOBJECT path, + if (!pPathObj) + return false; + +- pPathObj->path().AppendPoint(CFX_PointF(x, y), FXPT_TYPE::LineTo, false); ++ pPathObj->path().AppendPoint(CFX_PointF(x, y), CFX_Path::Point::Type::kLine); + pPathObj->SetDirty(true); + return true; + } +@@ -124,9 +132,9 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_BezierTo(FPDF_PAGEOBJECT path, + return false; + + CPDF_Path& cpath = pPathObj->path(); +- cpath.AppendPoint(CFX_PointF(x1, y1), FXPT_TYPE::BezierTo, false); +- cpath.AppendPoint(CFX_PointF(x2, y2), FXPT_TYPE::BezierTo, false); +- cpath.AppendPoint(CFX_PointF(x3, y3), FXPT_TYPE::BezierTo, false); ++ cpath.AppendPoint(CFX_PointF(x1, y1), CFX_Path::Point::Type::kBezier); ++ cpath.AppendPoint(CFX_PointF(x2, y2), CFX_Path::Point::Type::kBezier); ++ cpath.AppendPoint(CFX_PointF(x3, y3), CFX_Path::Point::Type::kBezier); + pPathObj->SetDirty(true); + return true; + } +@@ -181,33 +189,6 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_GetDrawMode(FPDF_PAGEOBJECT path, + return true; + } + +-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_GetMatrix(FPDF_PAGEOBJECT path, +- FS_MATRIX* matrix) { +- if (!path || !matrix) +- return false; +- +- CPDF_PathObject* pPathObj = CPDFPathObjectFromFPDFPageObject(path); +- if (!pPathObj) +- return false; +- +- *matrix = FSMatrixFromCFXMatrix(pPathObj->matrix()); +- return true; +-} +- +-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +-FPDFPath_SetMatrix(FPDF_PAGEOBJECT path, const FS_MATRIX* matrix) { +- if (!matrix) +- return false; +- +- CPDF_PathObject* pPathObj = CPDFPathObjectFromFPDFPageObject(path); +- if (!pPathObj) +- return false; +- +- pPathObj->set_matrix(CFXMatrixFromFSMatrix(*matrix)); +- pPathObj->SetDirty(true); +- return true; +-} +- + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFPathSegment_GetPoint(FPDF_PATHSEGMENT segment, float* x, float* y) { + auto* pPathPoint = FXPathPointFromFPDFPathSegment(segment); +diff --git a/fpdfsdk/fpdf_editpath_embeddertest.cpp b/fpdfsdk/fpdf_editpath_embeddertest.cpp +index 2a8fb6a03..4891004cf 100644 +--- a/fpdfsdk/fpdf_editpath_embeddertest.cpp ++++ b/fpdfsdk/fpdf_editpath_embeddertest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,7 @@ + #include "public/fpdf_edit.h" + #include "testing/embedder_test.h" + #include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/base/check.h" + + class FPDFEditPathEmbedderTest : public EmbedderTest {}; + +@@ -33,7 +34,7 @@ TEST_F(FPDFEditPathEmbedderTest, VerifyCorrectColoursReturned) { + + ASSERT_TRUE(OpenSavedDocument()); + page = LoadSavedPage(0); +- ASSERT(page); ++ ASSERT_TRUE(page); + + for (size_t i = 0; i < kObjectCount; ++i) { + FPDF_PAGEOBJECT path = FPDFPage_GetObject(page, i); +diff --git a/fpdfsdk/fpdf_edittext.cpp b/fpdfsdk/fpdf_edittext.cpp +index a3b4208aa..467a8f39c 100644 +--- a/fpdfsdk/fpdf_edittext.cpp ++++ b/fpdfsdk/fpdf_edittext.cpp +@@ -1,16 +1,15 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include +-#include + #include + #include ++#include + #include + #include + ++#include "core/fpdfapi/font/cpdf_cidfont.h" + #include "core/fpdfapi/font/cpdf_font.h" +-#include "core/fpdfapi/font/cpdf_type1font.h" + #include "core/fpdfapi/page/cpdf_docpagedata.h" + #include "core/fpdfapi/page/cpdf_textobject.h" + #include "core/fpdfapi/page/cpdf_textstate.h" +@@ -22,13 +21,26 @@ + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_string.h" ++#include "core/fpdfapi/render/charposlist.h" ++#include "core/fpdfapi/render/cpdf_pagerendercontext.h" ++#include "core/fpdfapi/render/cpdf_rendercontext.h" ++#include "core/fpdfapi/render/cpdf_renderstatus.h" ++#include "core/fpdfapi/render/cpdf_textrenderer.h" + #include "core/fpdftext/cpdf_textpage.h" + #include "core/fxcrt/fx_extension.h" ++#include "core/fxcrt/fx_string_wrappers.h" ++#include "core/fxcrt/span_util.h" ++#include "core/fxcrt/stl_util.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" + #include "core/fxge/cfx_fontmgr.h" + #include "core/fxge/fx_font.h" ++#include "core/fxge/text_char_pos.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "public/fpdf_edit.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/numerics/safe_conversions.h" + + // These checks are here because core/ and public/ cannot depend on each other. + static_assert(static_cast(TextRenderingMode::MODE_UNKNOWN) == +@@ -64,12 +76,21 @@ static_assert(static_cast(TextRenderingMode::MODE_LAST) == + + namespace { + +-CPDF_Dictionary* LoadFontDesc(CPDF_Document* pDoc, +- const ByteString& font_name, +- CFX_Font* pFont, +- pdfium::span span, +- int font_type) { +- CPDF_Dictionary* pFontDesc = pDoc->NewIndirect(); ++ByteString BaseFontNameForType(CFX_Font* pFont, int font_type) { ++ ByteString name = font_type == FPDF_FONT_TYPE1 ? pFont->GetPsName() ++ : pFont->GetBaseFontName(); ++ if (!name.IsEmpty()) ++ return name; ++ ++ return CFX_Font::kUntitledFontName; ++} ++ ++RetainPtr LoadFontDesc(CPDF_Document* pDoc, ++ const ByteString& font_name, ++ CFX_Font* pFont, ++ pdfium::span span, ++ int font_type) { ++ auto pFontDesc = pDoc->NewIndirect(); + pFontDesc->SetNewFor("Type", "FontDescriptor"); + pFontDesc->SetNewFor("FontName", font_name); + int flags = 0; +@@ -86,8 +107,7 @@ CPDF_Dictionary* LoadFontDesc(CPDF_Document* pDoc, + flags |= FXFONT_NONSYMBOLIC; + + pFontDesc->SetNewFor("Flags", flags); +- FX_RECT bbox; +- pFont->GetBBox(&bbox); ++ FX_RECT bbox = pFont->GetBBox().value_or(FX_RECT()); + pFontDesc->SetRectFor("FontBBox", CFX_FloatRect(bbox)); + + // TODO(npm): calculate italic angle correctly +@@ -100,12 +120,12 @@ CPDF_Dictionary* LoadFontDesc(CPDF_Document* pDoc, + pFontDesc->SetNewFor("CapHeight", pFont->GetAscent()); + pFontDesc->SetNewFor("StemV", pFont->IsBold() ? 120 : 70); + +- CPDF_Stream* pStream = pDoc->NewIndirect(); ++ auto pStream = pDoc->NewIndirect(); + pStream->SetData(span); + // TODO(npm): Lengths for Type1 fonts. + if (font_type == FPDF_FONT_TRUETYPE) { +- pStream->GetDict()->SetNewFor("Length1", +- static_cast(span.size())); ++ pStream->GetMutableDict()->SetNewFor( ++ "Length1", static_cast(span.size())); + } + ByteString fontFile = font_type == FPDF_FONT_TYPE1 ? "FontFile" : "FontFile2"; + pFontDesc->SetNewFor(fontFile, pDoc, pStream->GetObjNum()); +@@ -133,8 +153,8 @@ const char ToUnicodeEnd[] = + "end\n" + "end\n"; + +-void AddCharcode(std::ostringstream* pBuffer, uint32_t number) { +- ASSERT(number <= 0xFFFF); ++void AddCharcode(fxcrt::ostringstream* pBuffer, uint32_t number) { ++ DCHECK(number <= 0xFFFF); + *pBuffer << "<"; + char ans[4]; + FXSYS_IntToFourHexChars(number, ans); +@@ -145,7 +165,7 @@ void AddCharcode(std::ostringstream* pBuffer, uint32_t number) { + + // PDF spec 1.7 Section 5.9.2: "Unicode character sequences as expressed in + // UTF-16BE encoding." See https://en.wikipedia.org/wiki/UTF-16#Description +-void AddUnicode(std::ostringstream* pBuffer, uint32_t unicode) { ++void AddUnicode(fxcrt::ostringstream* pBuffer, uint32_t unicode) { + if (unicode >= 0xD800 && unicode <= 0xDFFF) + unicode = 0; + +@@ -158,8 +178,9 @@ void AddUnicode(std::ostringstream* pBuffer, uint32_t unicode) { + } + + // Loads the charcode to unicode mapping into a stream +-CPDF_Stream* LoadUnicode(CPDF_Document* pDoc, +- const std::map& to_unicode) { ++RetainPtr LoadUnicode( ++ CPDF_Document* pDoc, ++ const std::multimap& to_unicode) { + // A map charcode->unicode + std::map char_to_uni; + // A map to vector v of unicode characters of size (end +@@ -205,7 +226,7 @@ CPDF_Stream* LoadUnicode(CPDF_Document* pDoc, + unicodes.push_back(iter->second); + next_it = std::next(iter); + } +- ASSERT(iter->first - firstCharcode + 1 == unicodes.size()); ++ DCHECK_EQ(iter->first - firstCharcode + 1, unicodes.size()); + map_range_vector[std::make_pair(firstCharcode, iter->first)] = unicodes; + continue; + } +@@ -222,7 +243,7 @@ CPDF_Stream* LoadUnicode(CPDF_Document* pDoc, + } + map_range[std::make_pair(firstCharcode, curCharcode)] = firstUnicode; + } +- std::ostringstream buffer; ++ fxcrt::ostringstream buffer; + buffer << ToUnicodeStart; + // Add maps to buffer + buffer << static_cast(char_to_uni.size()) << " beginbfchar\n"; +@@ -262,7 +283,7 @@ CPDF_Stream* LoadUnicode(CPDF_Document* pDoc, + buffer << "endbfrange\n"; + buffer << ToUnicodeEnd; + // TODO(npm): Encrypt / Compress? +- CPDF_Stream* stream = pDoc->NewIndirect(); ++ auto stream = pDoc->NewIndirect(); + stream->SetDataFromStringstream(&buffer); + return stream; + } +@@ -271,67 +292,60 @@ RetainPtr LoadSimpleFont(CPDF_Document* pDoc, + std::unique_ptr pFont, + pdfium::span span, + int font_type) { +- CPDF_Dictionary* pFontDict = pDoc->NewIndirect(); ++ auto pFontDict = pDoc->NewIndirect(); + pFontDict->SetNewFor("Type", "Font"); + pFontDict->SetNewFor( + "Subtype", font_type == FPDF_FONT_TYPE1 ? "Type1" : "TrueType"); +- ByteString name = pFont->GetBaseFontName(font_type == FPDF_FONT_TYPE1); +- if (name.IsEmpty()) +- name = CFX_Font::kUntitledFontName; ++ ByteString name = BaseFontNameForType(pFont.get(), font_type); + pFontDict->SetNewFor("BaseFont", name); + + uint32_t dwGlyphIndex; +- uint32_t dwCurrentChar = +- FT_Get_First_Char(pFont->GetFaceRec(), &dwGlyphIndex); ++ uint32_t dwCurrentChar = static_cast( ++ FT_Get_First_Char(pFont->GetFaceRec(), &dwGlyphIndex)); + static constexpr uint32_t kMaxSimpleFontChar = 0xFF; + if (dwCurrentChar > kMaxSimpleFontChar || dwGlyphIndex == 0) + return nullptr; + pFontDict->SetNewFor("FirstChar", + static_cast(dwCurrentChar)); +- CPDF_Array* widthsArray = pDoc->NewIndirect(); ++ auto widthsArray = pDoc->NewIndirect(); + while (true) { +- uint32_t width = +- std::min(pFont->GetGlyphWidth(dwGlyphIndex), +- static_cast(std::numeric_limits::max())); +- widthsArray->AddNew(static_cast(width)); +- uint32_t nextChar = +- FT_Get_Next_Char(pFont->GetFaceRec(), dwCurrentChar, &dwGlyphIndex); ++ widthsArray->AppendNew(pFont->GetGlyphWidth(dwGlyphIndex)); ++ uint32_t nextChar = static_cast( ++ FT_Get_Next_Char(pFont->GetFaceRec(), dwCurrentChar, &dwGlyphIndex)); + // Simple fonts have 1-byte charcodes only. + if (nextChar > kMaxSimpleFontChar || dwGlyphIndex == 0) + break; + for (uint32_t i = dwCurrentChar + 1; i < nextChar; i++) +- widthsArray->AddNew(0); ++ widthsArray->AppendNew(0); + dwCurrentChar = nextChar; + } + pFontDict->SetNewFor("LastChar", + static_cast(dwCurrentChar)); + pFontDict->SetNewFor("Widths", pDoc, + widthsArray->GetObjNum()); +- CPDF_Dictionary* pFontDesc = ++ RetainPtr pFontDesc = + LoadFontDesc(pDoc, name, pFont.get(), span, font_type); + + pFontDict->SetNewFor("FontDescriptor", pDoc, + pFontDesc->GetObjNum()); +- return CPDF_DocPageData::FromDocument(pDoc)->GetFont(pFontDict); ++ return CPDF_DocPageData::FromDocument(pDoc)->GetFont(std::move(pFontDict)); + } + + RetainPtr LoadCompositeFont(CPDF_Document* pDoc, + std::unique_ptr pFont, + pdfium::span span, + int font_type) { +- CPDF_Dictionary* pFontDict = pDoc->NewIndirect(); ++ auto pFontDict = pDoc->NewIndirect(); + pFontDict->SetNewFor("Type", "Font"); + pFontDict->SetNewFor("Subtype", "Type0"); + // TODO(npm): Get the correct encoding, if it's not identity. + ByteString encoding = "Identity-H"; + pFontDict->SetNewFor("Encoding", encoding); +- ByteString name = pFont->GetBaseFontName(font_type == FPDF_FONT_TYPE1); +- if (name.IsEmpty()) +- name = CFX_Font::kUntitledFontName; ++ ByteString name = BaseFontNameForType(pFont.get(), font_type); + pFontDict->SetNewFor( + "BaseFont", font_type == FPDF_FONT_TYPE1 ? name + "-" + encoding : name); + +- CPDF_Dictionary* pCIDFont = pDoc->NewIndirect(); ++ auto pCIDFont = pDoc->NewIndirect(); + pCIDFont->SetNewFor("Type", "Font"); + pCIDFont->SetNewFor("Subtype", font_type == FPDF_FONT_TYPE1 + ? "CIDFontType0" +@@ -340,50 +354,50 @@ RetainPtr LoadCompositeFont(CPDF_Document* pDoc, + + // TODO(npm): Maybe use FT_Get_CID_Registry_Ordering_Supplement to get the + // CIDSystemInfo +- CPDF_Dictionary* pCIDSystemInfo = pDoc->NewIndirect(); ++ auto pCIDSystemInfo = pDoc->NewIndirect(); + pCIDSystemInfo->SetNewFor("Registry", "Adobe", false); + pCIDSystemInfo->SetNewFor("Ordering", "Identity", false); + pCIDSystemInfo->SetNewFor("Supplement", 0); + pCIDFont->SetNewFor("CIDSystemInfo", pDoc, + pCIDSystemInfo->GetObjNum()); + +- CPDF_Dictionary* pFontDesc = ++ RetainPtr pFontDesc = + LoadFontDesc(pDoc, name, pFont.get(), span, font_type); + pCIDFont->SetNewFor("FontDescriptor", pDoc, + pFontDesc->GetObjNum()); + + uint32_t dwGlyphIndex; +- uint32_t dwCurrentChar = +- FT_Get_First_Char(pFont->GetFaceRec(), &dwGlyphIndex); ++ uint32_t dwCurrentChar = static_cast( ++ FT_Get_First_Char(pFont->GetFaceRec(), &dwGlyphIndex)); + static constexpr uint32_t kMaxUnicode = 0x10FFFF; + // If it doesn't have a single char, just fail + if (dwGlyphIndex == 0 || dwCurrentChar > kMaxUnicode) + return nullptr; + +- std::map to_unicode; ++ std::multimap to_unicode; + std::map widths; + while (true) { + if (dwCurrentChar > kMaxUnicode) + break; + +- if (!pdfium::ContainsKey(widths, dwGlyphIndex)) ++ if (!pdfium::Contains(widths, dwGlyphIndex)) + widths[dwGlyphIndex] = pFont->GetGlyphWidth(dwGlyphIndex); +- to_unicode[dwGlyphIndex] = dwCurrentChar; +- dwCurrentChar = +- FT_Get_Next_Char(pFont->GetFaceRec(), dwCurrentChar, &dwGlyphIndex); ++ to_unicode.emplace(dwGlyphIndex, dwCurrentChar); ++ dwCurrentChar = static_cast( ++ FT_Get_Next_Char(pFont->GetFaceRec(), dwCurrentChar, &dwGlyphIndex)); + if (dwGlyphIndex == 0) + break; + } +- CPDF_Array* widthsArray = pDoc->NewIndirect(); ++ auto widthsArray = pDoc->NewIndirect(); + for (auto it = widths.begin(); it != widths.end(); ++it) { + int ch = it->first; + int w = it->second; + if (std::next(it) == widths.end()) { + // Only one char left, use format c [w] + auto oneW = pdfium::MakeRetain(); +- oneW->AddNew(w); +- widthsArray->AddNew(ch); +- widthsArray->Add(oneW); ++ oneW->AppendNew(w); ++ widthsArray->AppendNew(ch); ++ widthsArray->Append(oneW); + break; + } + ++it; +@@ -392,7 +406,7 @@ RetainPtr LoadCompositeFont(CPDF_Document* pDoc, + if (next_ch == ch + 1 && next_w == w) { + // The array can have a group c_first c_last w: all CIDs in the range from + // c_first to c_last will have width w +- widthsArray->AddNew(ch); ++ widthsArray->AppendNew(ch); + ch = next_ch; + while (true) { + auto next_it = std::next(it); +@@ -403,33 +417,33 @@ RetainPtr LoadCompositeFont(CPDF_Document* pDoc, + ++it; + ch = it->first; + } +- widthsArray->AddNew(ch); +- widthsArray->AddNew(w); ++ widthsArray->AppendNew(ch); ++ widthsArray->AppendNew(w); + continue; + } + // Otherwise we can have a group of the form c [w1 w2 ...]: c has width + // w1, c+1 has width w2, etc. +- widthsArray->AddNew(ch); ++ widthsArray->AppendNew(ch); + auto curWidthArray = pdfium::MakeRetain(); +- curWidthArray->AddNew(w); +- curWidthArray->AddNew(next_w); ++ curWidthArray->AppendNew(w); ++ curWidthArray->AppendNew(next_w); + while (true) { + auto next_it = std::next(it); + if (next_it == widths.end() || next_it->first != it->first + 1) + break; + ++it; +- curWidthArray->AddNew(static_cast(it->second)); ++ curWidthArray->AppendNew(static_cast(it->second)); + } +- widthsArray->Add(curWidthArray); ++ widthsArray->Append(curWidthArray); + } + pCIDFont->SetNewFor("W", pDoc, widthsArray->GetObjNum()); + + // TODO(npm): Support vertical writing + +- auto* pDescendant = pFontDict->SetNewFor("DescendantFonts"); +- pDescendant->AddNew(pDoc, pCIDFont->GetObjNum()); ++ auto pDescendant = pFontDict->SetNewFor("DescendantFonts"); ++ pDescendant->AppendNew(pDoc, pCIDFont->GetObjNum()); + +- CPDF_Stream* toUnicodeStream = LoadUnicode(pDoc, to_unicode); ++ RetainPtr toUnicodeStream = LoadUnicode(pDoc, to_unicode); + pFontDict->SetNewFor("ToUnicode", pDoc, + toUnicodeStream->GetObjNum()); + return CPDF_DocPageData::FromDocument(pDoc)->GetFont(pFontDict); +@@ -440,6 +454,13 @@ CPDF_TextObject* CPDFTextObjectFromFPDFPageObject(FPDF_PAGEOBJECT page_object) { + return obj ? obj->AsText() : nullptr; + } + ++FPDF_GLYPHPATH FPDFGlyphPathFromCFXPath(const CFX_Path* path) { ++ return reinterpret_cast(path); ++} ++const CFX_Path* CFXPathFromFPDFGlyphPath(FPDF_GLYPHPATH path) { ++ return reinterpret_cast(path); ++} ++ + } // namespace + + FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV +@@ -455,8 +476,8 @@ FPDFPageObj_NewTextObj(FPDF_DOCUMENT document, + if (!pFont) + return nullptr; + +- auto pTextObj = pdfium::MakeUnique(); +- pTextObj->m_TextState.SetFont(pFont); ++ auto pTextObj = std::make_unique(); ++ pTextObj->m_TextState.SetFont(std::move(pFont)); + pTextObj->m_TextState.SetFontSize(font_size); + pTextObj->DefaultStates(); + +@@ -480,6 +501,27 @@ FPDFText_SetText(FPDF_PAGEOBJECT text_object, FPDF_WIDESTRING text) { + return true; + } + ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFText_SetCharcodes(FPDF_PAGEOBJECT text_object, ++ const uint32_t* charcodes, ++ size_t count) { ++ CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text_object); ++ if (!pTextObj) ++ return false; ++ ++ if (!charcodes && count) ++ return false; ++ ++ ByteString byte_text; ++ if (charcodes) { ++ for (size_t i = 0; i < count; ++i) { ++ pTextObj->GetFont()->AppendChar(&byte_text, charcodes[i]); ++ } ++ } ++ pTextObj->SetText(byte_text); ++ return true; ++} ++ + FPDF_EXPORT FPDF_FONT FPDF_CALLCONV FPDFText_LoadFont(FPDF_DOCUMENT document, + const uint8_t* data, + uint32_t size, +@@ -492,12 +534,12 @@ FPDF_EXPORT FPDF_FONT FPDF_CALLCONV FPDFText_LoadFont(FPDF_DOCUMENT document, + } + + auto span = pdfium::make_span(data, size); +- auto pFont = pdfium::MakeUnique(); ++ auto pFont = std::make_unique(); + + // TODO(npm): Maybe use FT_Get_X11_Font_Format to check format? Otherwise, we + // are allowing giving any font that can be loaded on freetype and setting it + // as any font type. +- if (!pFont->LoadEmbedded(span, false)) ++ if (!pFont->LoadEmbedded(span, /*force_vertical=*/false, /*object_tag=*/0)) + return nullptr; + + // Caller takes ownership. +@@ -517,46 +559,23 @@ FPDFText_LoadStandardFont(FPDF_DOCUMENT document, FPDF_BYTESTRING font) { + CPDF_Font::GetStockFont(pDoc, ByteStringView(font)).Leak()); + } + +-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFTextObj_GetMatrix(FPDF_PAGEOBJECT text, +- FS_MATRIX* matrix) { +- if (!matrix) ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFTextObj_GetFontSize(FPDF_PAGEOBJECT text, float* size) { ++ if (!size) + return false; + + CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text); + if (!pTextObj) + return false; + +- *matrix = FSMatrixFromCFXMatrix(pTextObj->GetTextMatrix()); ++ *size = pTextObj->GetFontSize(); + return true; + } + +-FPDF_EXPORT float FPDF_CALLCONV FPDFTextObj_GetFontSize(FPDF_PAGEOBJECT text) { +- CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text); +- return pTextObj ? pTextObj->GetFontSize() : 0.0f; +-} +- +-FPDF_EXPORT unsigned long FPDF_CALLCONV +-FPDFTextObj_GetFontName(FPDF_PAGEOBJECT text, +- void* buffer, +- unsigned long length) { +- CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text); +- if (!pTextObj) +- return 0; +- +- RetainPtr pPdfFont = pTextObj->GetFont(); +- CFX_Font* pFont = pPdfFont->GetFont(); +- ByteString name = pFont->GetFamilyName(); +- unsigned long dwStringLen = name.GetLength() + 1; +- if (buffer && length >= dwStringLen) +- memcpy(buffer, name.c_str(), dwStringLen); +- +- return dwStringLen; +-} +- + FPDF_EXPORT unsigned long FPDF_CALLCONV + FPDFTextObj_GetText(FPDF_PAGEOBJECT text_object, + FPDF_TEXTPAGE text_page, +- void* buffer, ++ FPDF_WCHAR* buffer, + unsigned long length) { + CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text_object); + if (!pTextObj) +@@ -570,6 +589,70 @@ FPDFTextObj_GetText(FPDF_PAGEOBJECT text_object, + return Utf16EncodeMaybeCopyAndReturnLength(text, buffer, length); + } + ++FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV ++FPDFTextObj_GetRenderedBitmap(FPDF_DOCUMENT document, ++ FPDF_PAGE page, ++ FPDF_PAGEOBJECT text_object, ++ float scale) { ++ CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document); ++ if (!doc) ++ return nullptr; ++ ++ CPDF_Page* optional_page = CPDFPageFromFPDFPage(page); ++ if (optional_page && optional_page->GetDocument() != doc) ++ return nullptr; ++ ++ CPDF_TextObject* text = CPDFTextObjectFromFPDFPageObject(text_object); ++ if (!text) ++ return nullptr; ++ ++ if (scale <= 0) ++ return nullptr; ++ ++ const CFX_Matrix scale_matrix(scale, 0, 0, scale, 0, 0); ++ const CFX_FloatRect& text_rect = text->GetRect(); ++ const CFX_FloatRect scaled_text_rect = scale_matrix.TransformRect(text_rect); ++ ++ // `rect` has to use integer values. Round up as needed. ++ const FX_RECT rect = scaled_text_rect.GetOuterRect(); ++ if (rect.IsEmpty()) ++ return nullptr; ++ ++ auto result_bitmap = pdfium::MakeRetain(); ++ if (!result_bitmap->Create(rect.Width(), rect.Height(), FXDIB_Format::kArgb)) ++ return nullptr; ++ ++ auto render_context = std::make_unique(); ++ CPDF_PageRenderContext* render_context_ptr = render_context.get(); ++ CPDF_Page::RenderContextClearer clearer(optional_page); ++ if (optional_page) ++ optional_page->SetRenderContext(std::move(render_context)); ++ ++ RetainPtr page_resources = ++ optional_page ? optional_page->GetMutablePageResources() : nullptr; ++ ++ auto device = std::make_unique(); ++ CFX_DefaultRenderDevice* device_ptr = device.get(); ++ render_context_ptr->m_pDevice = std::move(device); ++ render_context_ptr->m_pContext = std::make_unique( ++ doc, std::move(page_resources), /*pPageCache=*/nullptr); ++ ++ device_ptr->Attach(result_bitmap); ++ ++ CFX_Matrix device_matrix(rect.Width(), 0, 0, rect.Height(), 0, 0); ++ CPDF_RenderStatus status(render_context_ptr->m_pContext.get(), device_ptr); ++ status.SetDeviceMatrix(device_matrix); ++ status.Initialize(nullptr, nullptr); ++ ++ // Need to flip the rendering and also move it to fit within `result_bitmap`. ++ CFX_Matrix render_matrix(1, 0, 0, -1, -text_rect.left, text_rect.top); ++ render_matrix *= scale_matrix; ++ status.RenderSingleObject(text, render_matrix); ++ ++ // Caller takes ownership. ++ return FPDFBitmapFromCFXDIBitmap(result_bitmap.Leak()); ++} ++ + FPDF_EXPORT void FPDF_CALLCONV FPDFFont_Close(FPDF_FONT font) { + // Take back ownership from caller and release. + RetainPtr().Unleak(CPDFFontFromFPDFFont(font)); +@@ -584,9 +667,9 @@ FPDFPageObj_CreateTextObj(FPDF_DOCUMENT document, + if (!pDoc || !pFont) + return nullptr; + +- auto pTextObj = pdfium::MakeUnique(); +- pTextObj->m_TextState.SetFont( +- CPDF_DocPageData::FromDocument(pDoc)->GetFont(pFont->GetFontDict())); ++ auto pTextObj = std::make_unique(); ++ pTextObj->m_TextState.SetFont(CPDF_DocPageData::FromDocument(pDoc)->GetFont( ++ pFont->GetMutableFontDict())); + pTextObj->m_TextState.SetFontSize(font_size); + pTextObj->DefaultStates(); + return FPDFPageObjectFromCPDFPageObject(pTextObj.release()); +@@ -597,7 +680,7 @@ FPDFTextObj_GetTextRenderMode(FPDF_PAGEOBJECT text) { + CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text); + if (!pTextObj) + return FPDF_TEXTRENDERMODE_UNKNOWN; +- return static_cast(pTextObj->m_TextState.GetTextMode()); ++ return static_cast(pTextObj->GetTextRenderMode()); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +@@ -612,7 +695,174 @@ FPDFTextObj_SetTextRenderMode(FPDF_PAGEOBJECT text, + if (!pTextObj) + return false; + +- pTextObj->m_TextState.SetTextMode( +- static_cast(render_mode)); ++ pTextObj->SetTextRenderMode(static_cast(render_mode)); + return true; + } ++ ++FPDF_EXPORT FPDF_FONT FPDF_CALLCONV FPDFTextObj_GetFont(FPDF_PAGEOBJECT text) { ++ CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text); ++ if (!pTextObj) ++ return nullptr; ++ ++ // Unretained reference in public API. NOLINTNEXTLINE ++ return FPDFFontFromCPDFFont(pTextObj->GetFont()); ++} ++ ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFFont_GetFontName(FPDF_FONT font, char* buffer, unsigned long length) { ++ auto* pFont = CPDFFontFromFPDFFont(font); ++ if (!pFont) ++ return 0; ++ ++ CFX_Font* pCfxFont = pFont->GetFont(); ++ ByteString name = pCfxFont->GetFamilyName(); ++ const unsigned long dwStringLen = ++ pdfium::base::checked_cast(name.GetLength() + 1); ++ if (buffer && length >= dwStringLen) ++ memcpy(buffer, name.c_str(), dwStringLen); ++ ++ return dwStringLen; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetFontData(FPDF_FONT font, ++ uint8_t* buffer, ++ size_t buflen, ++ size_t* out_buflen) { ++ auto* cfont = CPDFFontFromFPDFFont(font); ++ if (!cfont || !out_buflen) ++ return false; ++ ++ pdfium::span data = cfont->GetFont()->GetFontSpan(); ++ if (buffer && buflen >= data.size()) ++ fxcrt::spancpy(pdfium::make_span(buffer, buflen), data); ++ *out_buflen = data.size(); ++ return true; ++} ++ ++FPDF_EXPORT int FPDF_CALLCONV FPDFFont_GetIsEmbedded(FPDF_FONT font) { ++ auto* cfont = CPDFFontFromFPDFFont(font); ++ if (!cfont) ++ return -1; ++ return cfont->IsEmbedded() ? 1 : 0; ++} ++ ++FPDF_EXPORT int FPDF_CALLCONV FPDFFont_GetFlags(FPDF_FONT font) { ++ auto* pFont = CPDFFontFromFPDFFont(font); ++ if (!pFont) ++ return -1; ++ ++ // Return only flags from ISO 32000-1:2008, table 123. ++ return pFont->GetFontFlags() & 0x7ffff; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetWeight(FPDF_FONT font) { ++ auto* pFont = CPDFFontFromFPDFFont(font); ++ return pFont ? pFont->GetFontWeight() : -1; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetItalicAngle(FPDF_FONT font, ++ int* angle) { ++ auto* pFont = CPDFFontFromFPDFFont(font); ++ if (!pFont || !angle) ++ return false; ++ ++ *angle = pFont->GetItalicAngle(); ++ return true; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetAscent(FPDF_FONT font, ++ float font_size, ++ float* ascent) { ++ auto* pFont = CPDFFontFromFPDFFont(font); ++ if (!pFont || !ascent) ++ return false; ++ ++ *ascent = pFont->GetTypeAscent() * font_size / 1000.f; ++ return true; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetDescent(FPDF_FONT font, ++ float font_size, ++ float* descent) { ++ auto* pFont = CPDFFontFromFPDFFont(font); ++ if (!pFont || !descent) ++ return false; ++ ++ *descent = pFont->GetTypeDescent() * font_size / 1000.f; ++ return true; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetGlyphWidth(FPDF_FONT font, ++ uint32_t glyph, ++ float font_size, ++ float* width) { ++ auto* pFont = CPDFFontFromFPDFFont(font); ++ if (!pFont || !width) ++ return false; ++ ++ uint32_t charcode = pFont->CharCodeFromUnicode(static_cast(glyph)); ++ ++ CPDF_CIDFont* pCIDFont = pFont->AsCIDFont(); ++ if (pCIDFont && pCIDFont->IsVertWriting()) { ++ uint16_t cid = pCIDFont->CIDFromCharCode(charcode); ++ *width = pCIDFont->GetVertWidth(cid) * font_size / 1000.f; ++ } else { ++ *width = pFont->GetCharWidthF(charcode) * font_size / 1000.f; ++ } ++ ++ return true; ++} ++ ++FPDF_EXPORT FPDF_GLYPHPATH FPDF_CALLCONV ++FPDFFont_GetGlyphPath(FPDF_FONT font, uint32_t glyph, float font_size) { ++ auto* pFont = CPDFFontFromFPDFFont(font); ++ if (!pFont) ++ return nullptr; ++ ++ if (!pdfium::base::IsValueInRangeForNumericType(glyph)) ++ return nullptr; ++ ++ uint32_t charcode = pFont->CharCodeFromUnicode(static_cast(glyph)); ++ std::vector pos = ++ GetCharPosList(pdfium::make_span(&charcode, 1), ++ pdfium::span(), pFont, font_size); ++ if (pos.empty()) ++ return nullptr; ++ ++ CFX_Font* pCfxFont; ++ if (pos[0].m_FallbackFontPosition == -1) { ++ pCfxFont = pFont->GetFont(); ++ DCHECK(pCfxFont); // Never null. ++ } else { ++ pCfxFont = pFont->GetFontFallback(pos[0].m_FallbackFontPosition); ++ if (!pCfxFont) ++ return nullptr; ++ } ++ ++ const CFX_Path* pPath = ++ pCfxFont->LoadGlyphPath(pos[0].m_GlyphIndex, pos[0].m_FontCharWidth); ++ ++ return FPDFGlyphPathFromCFXPath(pPath); ++} ++ ++FPDF_EXPORT int FPDF_CALLCONV ++FPDFGlyphPath_CountGlyphSegments(FPDF_GLYPHPATH glyphpath) { ++ auto* pPath = CFXPathFromFPDFGlyphPath(glyphpath); ++ if (!pPath) ++ return -1; ++ ++ return fxcrt::CollectionSize(pPath->GetPoints()); ++} ++ ++FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV ++FPDFGlyphPath_GetGlyphPathSegment(FPDF_GLYPHPATH glyphpath, int index) { ++ auto* pPath = CFXPathFromFPDFGlyphPath(glyphpath); ++ if (!pPath) ++ return nullptr; ++ ++ pdfium::span points = pPath->GetPoints(); ++ if (!fxcrt::IndexInBounds(points, index)) ++ return nullptr; ++ ++ return FPDFPathSegmentFromFXPathPoint(&points[index]); ++} +diff --git a/fpdfsdk/fpdf_ext.cpp b/fpdfsdk/fpdf_ext.cpp +index dc928f628..44f329f04 100644 +--- a/fpdfsdk/fpdf_ext.cpp ++++ b/fpdfsdk/fpdf_ext.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,7 +12,6 @@ + #include "core/fpdfdoc/cpdf_metadata.h" + #include "core/fxcrt/fx_extension.h" + #include "fpdfsdk/cpdfsdk_helpers.h" +-#include "third_party/base/ptr_util.h" + + static_assert(static_cast(UnsupportedFeature::kDocumentXFAForm) == + FPDF_UNSP_DOC_XFAFORM, +@@ -91,7 +90,7 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFDoc_GetPageMode(FPDF_DOCUMENT document) { + if (!pRoot) + return PAGEMODE_UNKNOWN; + +- const CPDF_Object* pName = pRoot->GetObjectFor("PageMode"); ++ RetainPtr pName = pRoot->GetObjectFor("PageMode"); + if (!pName) + return PAGEMODE_USENONE; + +diff --git a/fpdfsdk/fpdf_ext_embeddertest.cpp b/fpdfsdk/fpdf_ext_embeddertest.cpp +index f3ae06e22..e3506752d 100644 +--- a/fpdfsdk/fpdf_ext_embeddertest.cpp ++++ b/fpdfsdk/fpdf_ext_embeddertest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,11 +14,11 @@ TEST_F(FPDFExtEmbedderTest, PageModeUnknown) { + } + + TEST_F(FPDFExtEmbedderTest, PageModeUseNone) { +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + EXPECT_EQ(PAGEMODE_USENONE, FPDFDoc_GetPageMode(document())); + } + + TEST_F(FPDFExtEmbedderTest, PageModeUseOutlines) { +- EXPECT_TRUE(OpenDocument("use_outlines.pdf")); ++ ASSERT_TRUE(OpenDocument("use_outlines.pdf")); + EXPECT_EQ(PAGEMODE_USEOUTLINES, FPDFDoc_GetPageMode(document())); + } +diff --git a/fpdfsdk/fpdf_flatten.cpp b/fpdfsdk/fpdf_flatten.cpp +index 424e1b411..4eb8ebe5a 100644 +--- a/fpdfsdk/fpdf_flatten.cpp ++++ b/fpdfsdk/fpdf_flatten.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,8 +6,10 @@ + + #include "public/fpdf_flatten.h" + ++#include ++ + #include +-#include ++#include + #include + #include + +@@ -25,8 +27,11 @@ + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" ++#include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fpdfdoc/cpdf_annot.h" ++#include "core/fxcrt/fx_string_wrappers.h" + #include "fpdfsdk/cpdfsdk_helpers.h" ++#include "third_party/base/notreached.h" + + enum FPDF_TYPE { MAX, MIN }; + enum FPDF_VALUE { TOP, LEFT, RIGHT, BOTTOM }; +@@ -49,7 +54,7 @@ bool IsValidRect(const CFX_FloatRect& rect, const CFX_FloatRect& rcPage) { + } + + void GetContentsRect(CPDF_Document* pDoc, +- CPDF_Dictionary* pDict, ++ RetainPtr pDict, + std::vector* pRectArray) { + auto pPDFPage = pdfium::MakeRetain(pDoc, pDict); + pPDFPage->ParseContent(); +@@ -61,7 +66,7 @@ void GetContentsRect(CPDF_Document* pDoc, + } + } + +-void ParserStream(CPDF_Dictionary* pPageDic, ++void ParserStream(const CPDF_Dictionary* pPageDic, + CPDF_Dictionary* pStream, + std::vector* pRectArray, + std::vector* pObjectArray) { +@@ -80,7 +85,7 @@ void ParserStream(CPDF_Dictionary* pPageDic, + } + + int ParserAnnots(CPDF_Document* pSourceDoc, +- CPDF_Dictionary* pPageDic, ++ RetainPtr pPageDic, + std::vector* pRectArray, + std::vector* pObjectArray, + int nUsage) { +@@ -88,18 +93,19 @@ int ParserAnnots(CPDF_Document* pSourceDoc, + return FLATTEN_FAIL; + + GetContentsRect(pSourceDoc, pPageDic, pRectArray); +- CPDF_Array* pAnnots = pPageDic->GetArrayFor("Annots"); ++ RetainPtr pAnnots = pPageDic->GetArrayFor("Annots"); + if (!pAnnots) + return FLATTEN_NOTHINGTODO; + + CPDF_ArrayLocker locker(pAnnots); + for (const auto& pAnnot : locker) { +- CPDF_Dictionary* pAnnotDict = ToDictionary(pAnnot->GetDirect()); ++ RetainPtr pAnnotDict = ++ ToDictionary(pAnnot->GetMutableDirect()); + if (!pAnnotDict) + continue; + + ByteString sSubtype = +- pAnnotDict->GetStringFor(pdfium::annotation::kSubtype); ++ pAnnotDict->GetByteStringFor(pdfium::annotation::kSubtype); + if (sSubtype == "Popup") + continue; + +@@ -113,7 +119,7 @@ int ParserAnnots(CPDF_Document* pSourceDoc, + else + bParseStream = !!(nAnnotFlag & pdfium::annotation_flags::kPrint); + if (bParseStream) +- ParserStream(pPageDic, pAnnotDict, pRectArray, pObjectArray); ++ ParserStream(pPageDic.Get(), pAnnotDict.Get(), pRectArray, pObjectArray); + } + return FLATTEN_SUCCESS; + } +@@ -144,7 +150,7 @@ float GetMinMaxValue(const std::vector& array, + pArray[i] = array[i].bottom; + break; + default: +- NOTREACHED(); ++ NOTREACHED_NORETURN(); + return 0.0f; + } + +@@ -174,27 +180,27 @@ ByteString GenerateFlattenedContent(const ByteString& key) { + return "q 1 0 0 1 0 0 cm /" + key + " Do Q"; + } + +-CPDF_Object* NewIndirectContentsStream(CPDF_Document* pDocument, +- const ByteString& contents) { +- CPDF_Stream* pNewContents = pDocument->NewIndirect( +- nullptr, 0, pDocument->New()); ++RetainPtr NewIndirectContentsStreamReference( ++ CPDF_Document* pDocument, ++ const ByteString& contents) { ++ auto pNewContents = ++ pDocument->NewIndirect(pDocument->New()); + pNewContents->SetData(contents.raw_span()); +- return pNewContents; ++ return pNewContents->MakeReference(pDocument); + } + + void SetPageContents(const ByteString& key, + CPDF_Dictionary* pPage, + CPDF_Document* pDocument) { +- CPDF_Array* pContentsArray = +- pPage->GetArrayFor(pdfium::page_object::kContents); +- CPDF_Stream* pContentsStream = +- pPage->GetStreamFor(pdfium::page_object::kContents); ++ RetainPtr pContentsArray = ++ pPage->GetMutableArrayFor(pdfium::page_object::kContents); ++ RetainPtr pContentsStream = ++ pPage->GetMutableStreamFor(pdfium::page_object::kContents); + if (!pContentsStream && !pContentsArray) { + if (!key.IsEmpty()) { +- pPage->SetFor( +- pdfium::page_object::kContents, +- NewIndirectContentsStream(pDocument, GenerateFlattenedContent(key)) +- ->MakeReference(pDocument)); ++ pPage->SetFor(pdfium::page_object::kContents, ++ NewIndirectContentsStreamReference( ++ pDocument, GenerateFlattenedContent(key))); + } + return; + } +@@ -202,9 +208,8 @@ void SetPageContents(const ByteString& key, + pPage->ConvertToIndirectObjectFor(pdfium::page_object::kContents, pDocument); + if (pContentsArray) { + pContentsArray->InsertAt( +- 0, NewIndirectContentsStream(pDocument, "q")->MakeReference(pDocument)); +- pContentsArray->Add( +- NewIndirectContentsStream(pDocument, "Q")->MakeReference(pDocument)); ++ 0, NewIndirectContentsStreamReference(pDocument, "q")); ++ pContentsArray->Append(NewIndirectContentsStreamReference(pDocument, "Q")); + } else { + ByteString sStream = "q\n"; + { +@@ -215,15 +220,14 @@ void SetPageContents(const ByteString& key, + } + pContentsStream->SetDataAndRemoveFilter(sStream.raw_span()); + pContentsArray = pDocument->NewIndirect(); +- pContentsArray->AddNew(pDocument, +- pContentsStream->GetObjNum()); ++ pContentsArray->AppendNew(pDocument, ++ pContentsStream->GetObjNum()); + pPage->SetNewFor(pdfium::page_object::kContents, pDocument, + pContentsArray->GetObjNum()); + } + if (!key.IsEmpty()) { +- pContentsArray->Add( +- NewIndirectContentsStream(pDocument, GenerateFlattenedContent(key)) +- ->MakeReference(pDocument)); ++ pContentsArray->Append(NewIndirectContentsStreamReference( ++ pDocument, GenerateFlattenedContent(key))); + } + } + +@@ -252,7 +256,7 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFPage_Flatten(FPDF_PAGE page, int nFlag) { + return FLATTEN_FAIL; + + CPDF_Document* pDocument = pPage->GetDocument(); +- CPDF_Dictionary* pPageDict = pPage->GetDict(); ++ RetainPtr pPageDict = pPage->GetMutableDict(); + if (!pDocument) + return FLATTEN_FAIL; + +@@ -269,12 +273,15 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFPage_Flatten(FPDF_PAGE page, int nFlag) { + if (pPageDict->KeyExist(pdfium::page_object::kCropBox)) + rcOriginalMB = pPageDict->GetRectFor(pdfium::page_object::kCropBox); + ++ rcOriginalMB.Normalize(); + if (rcOriginalMB.IsEmpty()) + rcOriginalMB = CFX_FloatRect(0.0f, 0.0f, 612.0f, 792.0f); + + CFX_FloatRect rcOriginalCB; +- if (pPageDict->KeyExist(pdfium::page_object::kCropBox)) ++ if (pPageDict->KeyExist(pdfium::page_object::kCropBox)) { + rcOriginalCB = pPageDict->GetRectFor(pdfium::page_object::kCropBox); ++ rcOriginalCB.Normalize(); ++ } + if (rcOriginalCB.IsEmpty()) + rcOriginalCB = rcOriginalMB; + +@@ -286,19 +293,11 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFPage_Flatten(FPDF_PAGE page, int nFlag) { + pPageDict->SetRectFor(pdfium::page_object::kMediaBox, rcOriginalMB); + pPageDict->SetRectFor(pdfium::page_object::kCropBox, rcOriginalCB); + +- CPDF_Dictionary* pRes = +- pPageDict->GetDictFor(pdfium::page_object::kResources); +- if (!pRes) { +- pRes = +- pPageDict->SetNewFor(pdfium::page_object::kResources); +- } +- +- CPDF_Stream* pNewXObject = pDocument->NewIndirect( +- nullptr, 0, pDocument->New()); +- +- CPDF_Dictionary* pPageXObject = pRes->GetDictFor("XObject"); +- if (!pPageXObject) +- pPageXObject = pRes->SetNewFor("XObject"); ++ RetainPtr pRes = ++ pPageDict->GetOrCreateDictFor(pdfium::page_object::kResources); ++ auto pNewXObject = ++ pDocument->NewIndirect(pDocument->New()); ++ RetainPtr pPageXObject = pRes->GetOrCreateDictFor("XObject"); + + ByteString key; + if (!ObjectArray.empty()) { +@@ -313,14 +312,14 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFPage_Flatten(FPDF_PAGE page, int nFlag) { + } + } + +- SetPageContents(key, pPageDict, pDocument); ++ SetPageContents(key, pPageDict.Get(), pDocument); + +- CPDF_Dictionary* pNewXORes = nullptr; ++ RetainPtr pNewXORes; + if (!key.IsEmpty()) { + pPageXObject->SetNewFor(key, pDocument, + pNewXObject->GetObjNum()); + +- CPDF_Dictionary* pNewOXbjectDic = pNewXObject->GetDict(); ++ RetainPtr pNewOXbjectDic = pNewXObject->GetMutableDict(); + pNewXORes = pNewOXbjectDic->SetNewFor("Resources"); + pNewOXbjectDic->SetNewFor("Type", "XObject"); + pNewOXbjectDic->SetNewFor("Subtype", "Form"); +@@ -336,29 +335,30 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFPage_Flatten(FPDF_PAGE page, int nFlag) { + CFX_FloatRect rcAnnot = pAnnotDict->GetRectFor(pdfium::annotation::kRect); + rcAnnot.Normalize(); + +- ByteString sAnnotState = pAnnotDict->GetStringFor("AS"); +- CPDF_Dictionary* pAnnotAP = pAnnotDict->GetDictFor(pdfium::annotation::kAP); ++ ByteString sAnnotState = pAnnotDict->GetByteStringFor("AS"); ++ RetainPtr pAnnotAP = ++ pAnnotDict->GetMutableDictFor(pdfium::annotation::kAP); + if (!pAnnotAP) + continue; + +- CPDF_Stream* pAPStream = pAnnotAP->GetStreamFor("N"); ++ RetainPtr pAPStream = pAnnotAP->GetMutableStreamFor("N"); + if (!pAPStream) { +- CPDF_Dictionary* pAPDict = pAnnotAP->GetDictFor("N"); ++ RetainPtr pAPDict = pAnnotAP->GetMutableDictFor("N"); + if (!pAPDict) + continue; + + if (!sAnnotState.IsEmpty()) { +- pAPStream = pAPDict->GetStreamFor(sAnnotState); ++ pAPStream = pAPDict->GetMutableStreamFor(sAnnotState); + } else { + if (pAPDict->size() > 0) { + CPDF_DictionaryLocker locker(pAPDict); +- CPDF_Object* pFirstObj = locker.begin()->second.Get(); ++ RetainPtr pFirstObj = locker.begin()->second; + if (pFirstObj) { + if (pFirstObj->IsReference()) +- pFirstObj = pFirstObj->GetDirect(); ++ pFirstObj = pFirstObj->GetMutableDirect(); + if (!pFirstObj->IsStream()) + continue; +- pAPStream = pFirstObj->AsStream(); ++ pAPStream.Reset(pFirstObj->AsMutableStream()); + } + } + } +@@ -366,7 +366,7 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFPage_Flatten(FPDF_PAGE page, int nFlag) { + if (!pAPStream) + continue; + +- CPDF_Dictionary* pAPDict = pAPStream->GetDict(); ++ RetainPtr pAPDict = pAPStream->GetDict(); + CFX_FloatRect rcStream; + if (pAPDict->KeyExist("Rect")) + rcStream = pAPDict->GetRectFor("Rect"); +@@ -377,23 +377,20 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFPage_Flatten(FPDF_PAGE page, int nFlag) { + if (rcStream.IsEmpty()) + continue; + +- CPDF_Object* pObj = pAPStream; ++ RetainPtr pObj = pAPStream; + if (pObj->IsInline()) { +- RetainPtr pNew = pObj->Clone(); +- pObj = pNew.Get(); +- pDocument->AddIndirectObject(std::move(pNew)); ++ pObj = pObj->Clone(); ++ pDocument->AddIndirectObject(pObj); + } + +- CPDF_Dictionary* pObjDict = pObj->GetDict(); ++ RetainPtr pObjDict = pObj->GetMutableDict(); + if (pObjDict) { + pObjDict->SetNewFor("Type", "XObject"); + pObjDict->SetNewFor("Subtype", "Form"); + } + +- CPDF_Dictionary* pXObject = pNewXORes->GetDictFor("XObject"); +- if (!pXObject) +- pXObject = pNewXORes->SetNewFor("XObject"); +- ++ RetainPtr pXObject = ++ pNewXORes->GetOrCreateDictFor("XObject"); + ByteString sFormName = ByteString::Format("F%d", i); + pXObject->SetNewFor(sFormName, pDocument, + pObj->GetObjNum()); +@@ -408,8 +405,8 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFPage_Flatten(FPDF_PAGE page, int nFlag) { + CFX_Matrix m = GetMatrix(rcAnnot, rcStream, matrix); + m.b = 0; + m.c = 0; +- std::ostringstream buf; +- buf << m; ++ fxcrt::ostringstream buf; ++ WriteMatrix(buf, m); + ByteString str(buf); + sStream += ByteString::Format("q %s cm /%s Do Q\n", str.c_str(), + sFormName.c_str()); +diff --git a/fpdfsdk/fpdf_flatten_embeddertest.cpp b/fpdfsdk/fpdf_flatten_embeddertest.cpp +index 2b244c409..235ab889f 100644 +--- a/fpdfsdk/fpdf_flatten_embeddertest.cpp ++++ b/fpdfsdk/fpdf_flatten_embeddertest.cpp +@@ -1,8 +1,9 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "build/build_config.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" + #include "public/fpdf_flatten.h" + #include "public/fpdfview.h" + #include "testing/embedder_test.h" +@@ -15,7 +16,7 @@ class FPDFFlattenEmbedderTest : public EmbedderTest {}; + } // namespace + + TEST_F(FPDFFlattenEmbedderTest, FlatNothing) { +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + EXPECT_EQ(FLATTEN_NOTHINGTODO, FPDFPage_Flatten(page, FLAT_NORMALDISPLAY)); +@@ -23,7 +24,7 @@ TEST_F(FPDFFlattenEmbedderTest, FlatNothing) { + } + + TEST_F(FPDFFlattenEmbedderTest, FlatNormal) { +- EXPECT_TRUE(OpenDocument("annotiter.pdf")); ++ ASSERT_TRUE(OpenDocument("annotiter.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + EXPECT_EQ(FLATTEN_SUCCESS, FPDFPage_Flatten(page, FLAT_NORMALDISPLAY)); +@@ -31,34 +32,30 @@ TEST_F(FPDFFlattenEmbedderTest, FlatNormal) { + } + + TEST_F(FPDFFlattenEmbedderTest, FlatPrint) { +- EXPECT_TRUE(OpenDocument("annotiter.pdf")); ++ ASSERT_TRUE(OpenDocument("annotiter.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + EXPECT_EQ(FLATTEN_SUCCESS, FPDFPage_Flatten(page, FLAT_PRINT)); + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_BUG_861842 DISABLED_BUG_861842 ++TEST_F(FPDFFlattenEmbedderTest, BUG_861842) { ++ const char* checkbox_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "252fd5f2299cc16e5a07565df7c30565"; ++#if BUILDFLAG(IS_APPLE) ++ return "6aafcb2d98da222964bcdbf5aa1f4f1f"; + #else +-#define MAYBE_BUG_861842 BUG_861842 +-#endif +-TEST_F(FPDFFlattenEmbedderTest, MAYBE_BUG_861842) { +-#if defined(OS_WIN) +- static const char kCheckboxHash[] = "95fba3cb7bce7e0d3c94279f60984e17"; +-#elif defined(OS_MACOSX) +- static const char kCheckboxHash[] = "0a1d1d63d4452bc26a1c5c547d309655"; +-#else +- static const char kCheckboxHash[] = "594265790b81df2d93120d33b72a6ada"; ++ return "594265790b81df2d93120d33b72a6ada"; + #endif ++ }(); + +- EXPECT_TRUE(OpenDocument("bug_861842.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_861842.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 100, 120, kCheckboxHash); ++ CompareBitmap(bitmap.get(), 100, 120, checkbox_checksum); + + EXPECT_EQ(FLATTEN_SUCCESS, FPDFPage_Flatten(page, FLAT_PRINT)); + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); +@@ -66,52 +63,82 @@ TEST_F(FPDFFlattenEmbedderTest, MAYBE_BUG_861842) { + UnloadPage(page); + + // TODO(crbug.com/861842): This should not render blank. +- static const char kBlankPageHash[] = "48400809c3862dae64b0cd00d51057a4"; ++ static constexpr char kBlankPageHash[] = "48400809c3862dae64b0cd00d51057a4"; + VerifySavedDocument(100, 120, kBlankPageHash); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_BUG_890322 DISABLED_BUG_890322 ++TEST_F(FPDFFlattenEmbedderTest, BUG_889099) { ++ const char* page_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "73678f308625e22f31940e9f732b68bf"; ++#if BUILDFLAG(IS_APPLE) ++ return "049ed3f1e21fc72f929af3410c64bc8f"; + #else +-#define MAYBE_BUG_890322 BUG_890322 ++ return "3db87245e3f4e37f4cb18654bbe22d97"; ++#endif ++ }(); ++ const char* flattened_page_checksum = []() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "41debc60cf2a8f74c710ec6082d77b18"; + #endif +-TEST_F(FPDFFlattenEmbedderTest, MAYBE_BUG_890322) { +- static const char md5_hash[] = "6c674642154408e877d88c6c082d67e9"; +- EXPECT_TRUE(OpenDocument("bug_890322.pdf")); ++ return "0832157462ea70fbbf053e14b1d6457f"; ++ }(); ++ ++ ASSERT_TRUE(OpenDocument("bug_889099.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + ++ // The original document has a malformed media box; the height is -400. + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 200, 200, md5_hash); ++ CompareBitmap(bitmap.get(), 300, 400, page_checksum); + + EXPECT_EQ(FLATTEN_SUCCESS, FPDFPage_Flatten(page, FLAT_PRINT)); + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + + UnloadPage(page); + +- VerifySavedDocument(200, 200, md5_hash); ++ VerifySavedDocument(300, 400, flattened_page_checksum); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_BUG_896366 DISABLED_BUG_896366 +-#else +-#define MAYBE_BUG_896366 BUG_896366 +-#endif +-TEST_F(FPDFFlattenEmbedderTest, MAYBE_BUG_896366) { +- static const char md5_hash[] = "f71ab085c52c8445ae785eca3ec858b1"; +- EXPECT_TRUE(OpenDocument("bug_896366.pdf")); ++TEST_F(FPDFFlattenEmbedderTest, BUG_890322) { ++ const char* checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "793689536cf64fe792c2f241888c0cf3"; ++ return "6c674642154408e877d88c6c082d67e9"; ++ }(); ++ ASSERT_TRUE(OpenDocument("bug_890322.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); ++ CompareBitmap(bitmap.get(), 200, 200, checksum); ++ ++ EXPECT_EQ(FLATTEN_SUCCESS, FPDFPage_Flatten(page, FLAT_PRINT)); ++ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ ++ UnloadPage(page); ++ ++ VerifySavedDocument(200, 200, checksum); ++} ++ ++TEST_F(FPDFFlattenEmbedderTest, BUG_896366) { ++ const char* checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "c3cccfadc4c5249e6aa0675e511fa4c3"; ++ return "f71ab085c52c8445ae785eca3ec858b1"; ++ }(); ++ ASSERT_TRUE(OpenDocument("bug_896366.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT); +- CompareBitmap(bitmap.get(), 612, 792, md5_hash); ++ CompareBitmap(bitmap.get(), 612, 792, checksum); + + EXPECT_EQ(FLATTEN_SUCCESS, FPDFPage_Flatten(page, FLAT_PRINT)); + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + + UnloadPage(page); + +- VerifySavedDocument(612, 792, md5_hash); ++ VerifySavedDocument(612, 792, checksum); + } +diff --git a/fpdfsdk/fpdf_formfill.cpp b/fpdfsdk/fpdf_formfill.cpp +index a3f3022b7..8c1a083e5 100644 +--- a/fpdfsdk/fpdf_formfill.cpp ++++ b/fpdfsdk/fpdf_formfill.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,31 +8,29 @@ + + #include + #include +-#include + ++#include "constants/form_fields.h" ++#include "core/fpdfapi/page/cpdf_annotcontext.h" + #include "core/fpdfapi/page/cpdf_occontext.h" + #include "core/fpdfapi/page/cpdf_page.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/render/cpdf_renderoptions.h" + #include "core/fpdfdoc/cpdf_formcontrol.h" + #include "core/fpdfdoc/cpdf_formfield.h" + #include "core/fpdfdoc/cpdf_interactiveform.h" + #include "core/fxge/cfx_defaultrenderdevice.h" +-#include "fpdfsdk/cpdfsdk_actionhandler.h" +-#include "fpdfsdk/cpdfsdk_baannothandler.h" ++#include "fpdfsdk/cpdfsdk_annot.h" + #include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "fpdfsdk/cpdfsdk_interactiveform.h" + #include "fpdfsdk/cpdfsdk_pageview.h" +-#include "fpdfsdk/cpdfsdk_widgethandler.h" + #include "public/fpdfview.h" +-#include "third_party/base/ptr_util.h" + + #ifdef PDF_ENABLE_XFA + #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h" + #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h" +-#include "fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h" + + static_assert(static_cast(AlertButton::kDefault) == + JSPLATFORM_ALERT_BUTTON_DEFAULT, +@@ -168,7 +166,7 @@ CPDFSDK_PageView* FormHandleToPageView(FPDF_FORMHANDLE hHandle, + + CPDFSDK_FormFillEnvironment* pFormFillEnv = + CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle); +- return pFormFillEnv ? pFormFillEnv->GetPageView(pPage, true) : nullptr; ++ return pFormFillEnv ? pFormFillEnv->GetOrCreatePageView(pPage) : nullptr; + } + + void FFLCommon(FPDF_FORMHANDLE hHandle, +@@ -194,13 +192,14 @@ void FFLCommon(FPDF_FORMHANDLE hHandle, + const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y); + CFX_Matrix matrix = pPage->GetDisplayMatrix(rect, rotate); + +- auto pDevice = pdfium::MakeUnique(); +-#ifdef _SKIA_SUPPORT_ +- pDevice->AttachRecorder(static_cast(recorder)); ++ auto pDevice = std::make_unique(); ++#if defined(_SKIA_SUPPORT_) ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ pDevice->AttachRecorder(static_cast(recorder)); + #endif + + RetainPtr holder(CFXDIBitmapFromFPDFBitmap(bitmap)); +- pDevice->Attach(holder, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false); ++ pDevice->AttachWithRgbByteOrder(holder, !!(flags & FPDF_REVERSE_BYTE_ORDER)); + { + CFX_RenderDevice::StateRestorer restorer(pDevice.get()); + pDevice->SetClip_Rect(rect); +@@ -214,16 +213,25 @@ void FFLCommon(FPDF_FORMHANDLE hHandle, + + options.SetDrawAnnots(flags & FPDF_ANNOT); + options.SetOCContext( +- pdfium::MakeRetain(pPDFDoc, CPDF_OCContext::View)); ++ pdfium::MakeRetain(pPDFDoc, CPDF_OCContext::kView)); + + if (pPageView) + pPageView->PageView_OnDraw(pDevice.get(), matrix, &options, rect); + } ++} + +-#ifdef _SKIA_SUPPORT_PATHS_ +- pDevice->Flush(true); +- holder->UnPreMultiply(); +-#endif ++// Returns true if formfill version is correctly set. See |version| in ++// FPDF_FORMFILLINFO for details regarding correct version. ++bool CheckFormfillVersion(FPDF_FORMFILLINFO* formInfo) { ++ if (!formInfo || formInfo->version < 1 || formInfo->version > 2) ++ return false; ++ ++#ifdef PDF_ENABLE_XFA ++ if (formInfo->version != 2) ++ return false; ++#endif // PDF_ENABLE_XFA ++ ++ return true; + } + + } // namespace +@@ -233,25 +241,25 @@ FPDFPage_HasFormFieldAtPoint(FPDF_FORMHANDLE hHandle, + FPDF_PAGE page, + double page_x, + double page_y) { +- CPDF_Page* pPage = CPDFPageFromFPDFPage(page); ++ const CPDF_Page* pPage = CPDFPageFromFPDFPage(page); + if (pPage) { + CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle); + if (!pForm) + return -1; + +- CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm(); +- CPDF_FormControl* pFormCtrl = pPDFForm->GetControlAtPoint( ++ const CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm(); ++ const CPDF_FormControl* pFormCtrl = pPDFForm->GetControlAtPoint( + pPage, + CFX_PointF(static_cast(page_x), static_cast(page_y)), + nullptr); + if (!pFormCtrl) + return -1; +- CPDF_FormField* pFormField = pFormCtrl->GetField(); ++ const CPDF_FormField* pFormField = pFormCtrl->GetField(); + return pFormField ? static_cast(pFormField->GetFieldType()) : -1; + } + + #ifdef PDF_ENABLE_XFA +- CPDFXFA_Page* pXFAPage = ToXFAPage(IPDFPageFromFPDFPage(page)); ++ const CPDFXFA_Page* pXFAPage = ToXFAPage(IPDFPageFromFPDFPage(page)); + if (pXFAPage) { + return pXFAPage->HasFormFieldAtPoint( + CFX_PointF(static_cast(page_x), static_cast(page_y))); +@@ -285,24 +293,18 @@ FPDFPage_FormFieldZOrderAtPoint(FPDF_FORMHANDLE hHandle, + FPDF_EXPORT FPDF_FORMHANDLE FPDF_CALLCONV + FPDFDOC_InitFormFillEnvironment(FPDF_DOCUMENT document, + FPDF_FORMFILLINFO* formInfo) { +-#ifdef PDF_ENABLE_XFA +- constexpr int kRequiredVersion = 2; +-#else // PDF_ENABLE_XFA +- constexpr int kRequiredVersion = 1; +-#endif // PDF_ENABLE_XFA +- if (!formInfo || formInfo->version != kRequiredVersion) ++ if (!CheckFormfillVersion(formInfo)) + return nullptr; + + auto* pDocument = CPDFDocumentFromFPDFDocument(document); + if (!pDocument) + return nullptr; + +- std::unique_ptr pXFAHandler; + #ifdef PDF_ENABLE_XFA + CPDFXFA_Context* pContext = nullptr; + if (!formInfo->xfa_disabled) { + if (!pDocument->GetExtension()) { +- pDocument->SetExtension(pdfium::MakeUnique(pDocument)); ++ pDocument->SetExtension(std::make_unique(pDocument)); + } + + // If the CPDFXFA_Context has a FormFillEnvironment already then we've done +@@ -313,15 +315,11 @@ FPDFDOC_InitFormFillEnvironment(FPDF_DOCUMENT document, + return FPDFFormHandleFromCPDFSDKFormFillEnvironment( + pContext->GetFormFillEnv()); + } +- pXFAHandler = pdfium::MakeUnique(); + } + #endif // PDF_ENABLE_XFA + +- auto pFormFillEnv = pdfium::MakeUnique( +- pDocument, formInfo, +- pdfium::MakeUnique( +- pdfium::MakeUnique(), +- pdfium::MakeUnique(), std::move(pXFAHandler))); ++ auto pFormFillEnv = ++ std::make_unique(pDocument, formInfo); + + #ifdef PDF_ENABLE_XFA + if (pContext) +@@ -363,9 +361,27 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnMouseMove(FPDF_FORMHANDLE hHandle, + double page_x, + double page_y) { + CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); +- if (!pPageView) ++ return pPageView && ++ pPageView->OnMouseMove( ++ Mask::FromUnderlyingUnchecked(modifier), ++ CFX_PointF(page_x, page_y)); ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FORM_OnMouseWheel(FPDF_FORMHANDLE hHandle, ++ FPDF_PAGE page, ++ int modifier, ++ const FS_POINTF* page_coord, ++ int delta_x, ++ int delta_y) { ++ if (!page_coord) + return false; +- return pPageView->OnMouseMove(CFX_PointF(page_x, page_y), modifier); ++ ++ CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); ++ return pPageView && ++ pPageView->OnMouseWheel( ++ Mask::FromUnderlyingUnchecked(modifier), ++ CFXPointFFromFSPointF(*page_coord), CFX_Vector(delta_x, delta_y)); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnFocus(FPDF_FORMHANDLE hHandle, +@@ -374,9 +390,10 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnFocus(FPDF_FORMHANDLE hHandle, + double page_x, + double page_y) { + CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); +- if (!pPageView) +- return false; +- return pPageView->OnFocus(CFX_PointF(page_x, page_y), modifier); ++ return pPageView && ++ pPageView->OnFocus( ++ Mask::FromUnderlyingUnchecked(modifier), ++ CFX_PointF(page_x, page_y)); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnLButtonDown(FPDF_FORMHANDLE hHandle, +@@ -384,14 +401,15 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnLButtonDown(FPDF_FORMHANDLE hHandle, + int modifier, + double page_x, + double page_y) { +- CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); +- if (!pPageView) +- return false; + #ifdef PDF_ENABLE_CLICK_LOGGING + fprintf(stderr, "mousedown,left,%d,%d\n", static_cast(round(page_x)), + static_cast(round(page_y))); + #endif // PDF_ENABLE_CLICK_LOGGING +- return pPageView->OnLButtonDown(CFX_PointF(page_x, page_y), modifier); ++ CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); ++ return pPageView && ++ pPageView->OnLButtonDown( ++ Mask::FromUnderlyingUnchecked(modifier), ++ CFX_PointF(page_x, page_y)); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnLButtonUp(FPDF_FORMHANDLE hHandle, +@@ -399,14 +417,15 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnLButtonUp(FPDF_FORMHANDLE hHandle, + int modifier, + double page_x, + double page_y) { +- CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); +- if (!pPageView) +- return false; + #ifdef PDF_ENABLE_CLICK_LOGGING + fprintf(stderr, "mouseup,left,%d,%d\n", static_cast(round(page_x)), + static_cast(round(page_y))); + #endif // PDF_ENABLE_CLICK_LOGGING +- return pPageView->OnLButtonUp(CFX_PointF(page_x, page_y), modifier); ++ CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); ++ return pPageView && ++ pPageView->OnLButtonUp( ++ Mask::FromUnderlyingUnchecked(modifier), ++ CFX_PointF(page_x, page_y)); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +@@ -415,14 +434,15 @@ FORM_OnLButtonDoubleClick(FPDF_FORMHANDLE hHandle, + int modifier, + double page_x, + double page_y) { +- CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); +- if (!pPageView) +- return false; + #ifdef PDF_ENABLE_CLICK_LOGGING + fprintf(stderr, "mousedown,doubleleft,%d,%d\n", + static_cast(round(page_x)), static_cast(round(page_y))); + #endif // PDF_ENABLE_CLICK_LOGGING +- return pPageView->OnLButtonDblClk(CFX_PointF(page_x, page_y), modifier); ++ CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); ++ return pPageView && ++ pPageView->OnLButtonDblClk( ++ Mask::FromUnderlyingUnchecked(modifier), ++ CFX_PointF(page_x, page_y)); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonDown(FPDF_FORMHANDLE hHandle, +@@ -430,14 +450,15 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonDown(FPDF_FORMHANDLE hHandle, + int modifier, + double page_x, + double page_y) { +- CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); +- if (!pPageView) +- return false; + #ifdef PDF_ENABLE_CLICK_LOGGING + fprintf(stderr, "mousedown,right,%d,%d\n", static_cast(round(page_x)), + static_cast(round(page_y))); + #endif // PDF_ENABLE_CLICK_LOGGING +- return pPageView->OnRButtonDown(CFX_PointF(page_x, page_y), modifier); ++ CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); ++ return pPageView && ++ pPageView->OnRButtonDown( ++ Mask::FromUnderlyingUnchecked(modifier), ++ CFX_PointF(page_x, page_y)); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonUp(FPDF_FORMHANDLE hHandle, +@@ -445,14 +466,15 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonUp(FPDF_FORMHANDLE hHandle, + int modifier, + double page_x, + double page_y) { +- CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); +- if (!pPageView) +- return false; + #ifdef PDF_ENABLE_CLICK_LOGGING + fprintf(stderr, "mouseup,right,%d,%d\n", static_cast(round(page_x)), + static_cast(round(page_y))); + #endif // PDF_ENABLE_CLICK_LOGGING +- return pPageView->OnRButtonUp(CFX_PointF(page_x, page_y), modifier); ++ CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); ++ return pPageView && ++ pPageView->OnRButtonUp( ++ Mask::FromUnderlyingUnchecked(modifier), ++ CFX_PointF(page_x, page_y)); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyDown(FPDF_FORMHANDLE hHandle, +@@ -460,19 +482,17 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyDown(FPDF_FORMHANDLE hHandle, + int nKeyCode, + int modifier) { + CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); +- if (!pPageView) +- return false; +- return pPageView->OnKeyDown(nKeyCode, modifier); ++ return pPageView && ++ pPageView->OnKeyDown( ++ static_cast(nKeyCode), ++ Mask::FromUnderlyingUnchecked(modifier)); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyUp(FPDF_FORMHANDLE hHandle, + FPDF_PAGE page, + int nKeyCode, + int modifier) { +- CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); +- if (!pPageView) +- return false; +- return pPageView->OnKeyUp(nKeyCode, modifier); ++ return false; + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnChar(FPDF_FORMHANDLE hHandle, +@@ -480,9 +500,9 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnChar(FPDF_FORMHANDLE hHandle, + int nChar, + int modifier) { + CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); +- if (!pPageView) +- return false; +- return pPageView->OnChar(nChar, modifier); ++ return pPageView && ++ pPageView->OnChar( ++ nChar, Mask::FromUnderlyingUnchecked(modifier)); + } + + FPDF_EXPORT unsigned long FPDF_CALLCONV +@@ -511,6 +531,17 @@ FORM_GetSelectedText(FPDF_FORMHANDLE hHandle, + buffer, buflen); + } + ++FPDF_EXPORT void FPDF_CALLCONV ++FORM_ReplaceAndKeepSelection(FPDF_FORMHANDLE hHandle, ++ FPDF_PAGE page, ++ FPDF_WIDESTRING wsText) { ++ CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); ++ if (!pPageView) ++ return; ++ ++ pPageView->ReplaceAndKeepSelection(WideStringFromFPDFWideString(wsText)); ++} ++ + FPDF_EXPORT void FPDF_CALLCONV FORM_ReplaceSelection(FPDF_FORMHANDLE hHandle, + FPDF_PAGE page, + FPDF_WIDESTRING wsText) { +@@ -521,6 +552,12 @@ FPDF_EXPORT void FPDF_CALLCONV FORM_ReplaceSelection(FPDF_FORMHANDLE hHandle, + pPageView->ReplaceSelection(WideStringFromFPDFWideString(wsText)); + } + ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_SelectAllText(FPDF_FORMHANDLE hHandle, ++ FPDF_PAGE page) { ++ CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); ++ return pPageView && pPageView->SelectAllText(); ++} ++ + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_CanUndo(FPDF_FORMHANDLE hHandle, + FPDF_PAGE page) { + CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); +@@ -559,7 +596,76 @@ FORM_ForceToKillFocus(FPDF_FORMHANDLE hHandle) { + CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle); + if (!pFormFillEnv) + return false; +- return pFormFillEnv->KillFocusAnnot(0); ++ return pFormFillEnv->KillFocusAnnot({}); ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FORM_GetFocusedAnnot(FPDF_FORMHANDLE handle, ++ int* page_index, ++ FPDF_ANNOTATION* annot) { ++ if (!page_index || !annot) ++ return false; ++ ++ CPDFSDK_FormFillEnvironment* form_fill_env = ++ CPDFSDKFormFillEnvironmentFromFPDFFormHandle(handle); ++ if (!form_fill_env) ++ return false; ++ ++ // Set |page_index| and |annot| to default values. This is returned when there ++ // is no focused annotation. ++ *page_index = -1; ++ *annot = nullptr; ++ ++ CPDFSDK_Annot* cpdfsdk_annot = form_fill_env->GetFocusAnnot(); ++ if (!cpdfsdk_annot) ++ return true; ++ ++ // TODO(crbug.com/pdfium/1482): Handle XFA case. ++ if (cpdfsdk_annot->AsXFAWidget()) ++ return true; ++ ++ CPDFSDK_PageView* page_view = cpdfsdk_annot->GetPageView(); ++ if (!page_view->IsValid()) ++ return true; ++ ++ IPDF_Page* page = cpdfsdk_annot->GetPage(); ++ if (!page) ++ return true; ++ ++ RetainPtr annot_dict = ++ cpdfsdk_annot->GetPDFAnnot()->GetMutableAnnotDict(); ++ auto annot_context = ++ std::make_unique(std::move(annot_dict), page); ++ ++ *page_index = page_view->GetPageIndex(); ++ // Caller takes ownership. ++ *annot = FPDFAnnotationFromCPDFAnnotContext(annot_context.release()); ++ return true; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FORM_SetFocusedAnnot(FPDF_FORMHANDLE handle, FPDF_ANNOTATION annot) { ++ CPDFSDK_FormFillEnvironment* form_fill_env = ++ CPDFSDKFormFillEnvironmentFromFPDFFormHandle(handle); ++ if (!form_fill_env) ++ return false; ++ ++ CPDF_AnnotContext* annot_context = CPDFAnnotContextFromFPDFAnnotation(annot); ++ if (!annot_context) ++ return false; ++ ++ CPDFSDK_PageView* page_view = ++ form_fill_env->GetOrCreatePageView(annot_context->GetPage()); ++ if (!page_view->IsValid()) ++ return false; ++ ++ RetainPtr annot_dict = annot_context->GetMutableAnnotDict(); ++ ObservedPtr cpdfsdk_annot( ++ page_view->GetAnnotByDict(annot_dict.Get())); ++ if (!cpdfsdk_annot) ++ return false; ++ ++ return form_fill_env->SetFocusAnnot(cpdfsdk_annot); + } + + FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLDraw(FPDF_FORMHANDLE hHandle, +@@ -575,7 +681,7 @@ FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLDraw(FPDF_FORMHANDLE hHandle, + rotate, flags); + } + +-#ifdef _SKIA_SUPPORT_ ++#if defined(_SKIA_SUPPORT_) + FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLRecord(FPDF_FORMHANDLE hHandle, + FPDF_RECORDER recorder, + FPDF_PAGE page, +@@ -598,15 +704,17 @@ FPDF_SetFormFieldHighlightColor(FPDF_FORMHANDLE hHandle, + if (!pForm) + return; + +- Optional cast_input = ++ absl::optional cast_input = + CPDF_FormField::IntToFormFieldType(fieldType); +- if (!cast_input) ++ if (!cast_input.has_value()) + return; + +- if (cast_input.value() == FormFieldType::kUnknown) +- pForm->SetAllHighlightColors(color); +- else +- pForm->SetHighlightColor(color, cast_input.value()); ++ if (cast_input.value() == FormFieldType::kUnknown) { ++ pForm->SetAllHighlightColors(static_cast(color)); ++ } else { ++ pForm->SetHighlightColor(static_cast(color), ++ cast_input.value()); ++ } + } + + FPDF_EXPORT void FPDF_CALLCONV +@@ -638,7 +746,7 @@ FPDF_EXPORT void FPDF_CALLCONV FORM_OnBeforeClosePage(FPDF_PAGE page, + if (!pPage) + return; + +- CPDFSDK_PageView* pPageView = pFormFillEnv->GetPageView(pPage, false); ++ CPDFSDK_PageView* pPageView = pFormFillEnv->GetPageView(pPage); + if (pPageView) { + pPageView->SetValid(false); + // RemovePageView() takes care of the delete for us. +@@ -658,7 +766,7 @@ FPDF_EXPORT void FPDF_CALLCONV + FORM_DoDocumentOpenAction(FPDF_FORMHANDLE hHandle) { + CPDFSDK_FormFillEnvironment* pFormFillEnv = + CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle); +- if (pFormFillEnv && pFormFillEnv->IsJSPlatformPresent()) ++ if (pFormFillEnv) + pFormFillEnv->ProcOpenAction(); + } + +@@ -670,17 +778,14 @@ FPDF_EXPORT void FPDF_CALLCONV FORM_DoDocumentAAction(FPDF_FORMHANDLE hHandle, + return; + + CPDF_Document* pDoc = pFormFillEnv->GetPDFDocument(); +- CPDF_Dictionary* pDict = pDoc->GetRoot(); ++ const CPDF_Dictionary* pDict = pDoc->GetRoot(); + if (!pDict) + return; + +- CPDF_AAction aa(pDict->GetDictFor("AA")); ++ CPDF_AAction aa(pDict->GetDictFor(pdfium::form_fields::kAA)); + auto type = static_cast(aaType); +- if (aa.ActionExist(type)) { +- CPDF_Action action = aa.GetAction(type); +- pFormFillEnv->GetActionHandler()->DoAction_Document(action, type, +- pFormFillEnv); +- } ++ if (aa.ActionExist(type)) ++ pFormFillEnv->DoActionDocument(aa.GetAction(type), type); + } + + FPDF_EXPORT void FPDF_CALLCONV FORM_DoPageAAction(FPDF_PAGE page, +@@ -696,19 +801,15 @@ FPDF_EXPORT void FPDF_CALLCONV FORM_DoPageAAction(FPDF_PAGE page, + if (!pPDFPage) + return; + +- if (!pFormFillEnv->GetPageView(pPage, false)) ++ if (!pFormFillEnv->GetPageView(pPage)) + return; + +- CPDFSDK_ActionHandler* pActionHandler = pFormFillEnv->GetActionHandler(); +- CPDF_Dictionary* pPageDict = pPDFPage->GetDict(); +- CPDF_AAction aa(pPageDict->GetDictFor("AA")); ++ CPDF_AAction aa(pPDFPage->GetDict()->GetDictFor(pdfium::form_fields::kAA)); + CPDF_AAction::AActionType type = aaType == FPDFPAGE_AACTION_OPEN + ? CPDF_AAction::kOpenPage + : CPDF_AAction::kClosePage; +- if (aa.ActionExist(type)) { +- CPDF_Action action = aa.GetAction(type); +- pActionHandler->DoAction_Page(action, type, pFormFillEnv); +- } ++ if (aa.ActionExist(type)) ++ pFormFillEnv->DoActionPage(aa.GetAction(type), type); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +@@ -717,15 +818,11 @@ FORM_SetIndexSelected(FPDF_FORMHANDLE hHandle, + int index, + FPDF_BOOL selected) { + CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); +- if (!pPageView) +- return false; +- return pPageView->SetIndexSelected(index, selected); ++ return pPageView && pPageView->SetIndexSelected(index, selected); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FORM_IsIndexSelected(FPDF_FORMHANDLE hHandle, FPDF_PAGE page, int index) { + CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page); +- if (!pPageView) +- return false; +- return pPageView->IsIndexSelected(index); ++ return pPageView && pPageView->IsIndexSelected(index); + } +diff --git a/fpdfsdk/fpdf_formfill_embeddertest.cpp b/fpdfsdk/fpdf_formfill_embeddertest.cpp +index 3ae272b8d..ea4af5743 100644 +--- a/fpdfsdk/fpdf_formfill_embeddertest.cpp ++++ b/fpdfsdk/fpdf_formfill_embeddertest.cpp +@@ -1,26 +1,34 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include +-#include + #include + + #include "build/build_config.h" ++#include "constants/ascii.h" + #include "core/fxcrt/fx_coordinates.h" + #include "core/fxcrt/fx_string.h" + #include "core/fxcrt/fx_system.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" + #include "public/cpp/fpdf_scopers.h" + #include "public/fpdf_formfill.h" + #include "public/fpdf_fwlevent.h" + #include "public/fpdf_progressive.h" + #include "testing/embedder_test.h" ++#include "testing/embedder_test_constants.h" + #include "testing/embedder_test_mock_delegate.h" + #include "testing/embedder_test_timer_handling_delegate.h" + #include "testing/gmock/include/gmock/gmock.h" + #include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++ ++using pdfium::TextFormChecksum; + + using testing::_; ++using testing::InSequence; ++using testing::NiceMock; ++using testing::StrEq; + + using FPDFFormFillEmbedderTest = EmbedderTest; + +@@ -103,7 +111,7 @@ class FPDFFormFillInteractiveEmbedderTest : public FPDFFormFillEmbedderTest { + + // Uses the mouse to navigate to text field and select text. + void SelectTextWithMouse(const CFX_PointF& start, const CFX_PointF& end) { +- ASSERT(start.y == end.y); ++ DCHECK_EQ(start.y, end.y); + + // Navigate to starting position and click mouse. + FORM_OnMouseMove(form_handle(), page_, 0, start.x, start.y); +@@ -114,6 +122,11 @@ class FPDFFormFillInteractiveEmbedderTest : public FPDFFormFillEmbedderTest { + FORM_OnLButtonUp(form_handle(), page_, 0, end.x, end.y); + } + ++ void SelectAllTextAtPoint(const CFX_PointF& point) { ++ FocusOnPoint(point); ++ EXPECT_TRUE(FORM_SelectAllText(form_handle(), page_)); ++ } ++ + void CheckSelection(WideStringView expected_string) { + unsigned long actual_len = + FORM_GetSelectedText(form_handle(), page_, nullptr, 0); +@@ -128,6 +141,10 @@ class FPDFFormFillInteractiveEmbedderTest : public FPDFFormFillEmbedderTest { + EXPECT_EQ(expected_string, WideString::FromUTF16LE(buf.data(), num_chars)); + } + ++ void FocusOnPoint(const CFX_PointF& point) { ++ EXPECT_TRUE(FORM_OnFocus(form_handle(), page(), 0, point.x, point.y)); ++ } ++ + void CheckFocusedFieldText(WideStringView expected_string) { + unsigned long actual_len = + FORM_GetFocusedText(form_handle(), page_, nullptr, 0); +@@ -194,11 +211,11 @@ class FPDFFormFillTextFormEmbedderTest + } + + void SelectAllCharLimitFormTextWithMouse() { +- SelectTextWithMouse(CharLimitFormEnd(), CharLimitFormBegin()); ++ SelectAllTextAtPoint(CharLimitFormBegin()); + } + + void SelectAllRegularFormTextWithMouse() { +- SelectTextWithMouse(RegularFormEnd(), RegularFormBegin()); ++ SelectAllTextAtPoint(RegularFormBegin()); + } + + const CFX_PointF& CharLimitFormBegin() const { +@@ -222,14 +239,14 @@ class FPDFFormFillTextFormEmbedderTest + } + + static CFX_PointF CharLimitFormAtX(float x) { +- ASSERT(x >= kFormBeginX); +- ASSERT(x <= kFormEndX); ++ DCHECK(x >= kFormBeginX); ++ DCHECK(x <= kFormEndX); + return CFX_PointF(x, kCharLimitFormY); + } + + static CFX_PointF RegularFormAtX(float x) { +- ASSERT(x >= kFormBeginX); +- ASSERT(x <= kFormEndX); ++ DCHECK(x >= kFormBeginX); ++ DCHECK(x <= kFormEndX); + return CFX_PointF(x, kRegularFormY); + } + +@@ -266,29 +283,25 @@ class FPDFFormFillComboBoxFormEmbedderTest + } + + void SelectEditableFormOption(int item_index) { +- ASSERT(item_index >= 0); +- ASSERT(item_index < 3); ++ DCHECK(item_index >= 0); ++ DCHECK(item_index < 3); + SelectOption(item_index, EditableFormDropDown()); + } + + void SelectNonEditableFormOption(int item_index) { +- ASSERT(item_index >= 0); +- ASSERT(item_index < 26); ++ DCHECK(item_index >= 0); ++ DCHECK(item_index < 26); + SelectOption(item_index, NonEditableFormDropDown()); + } + + void SelectAllEditableFormTextWithMouse() { +- SelectTextWithMouse(EditableFormEnd(), EditableFormBegin()); ++ SelectAllTextAtPoint(EditableFormBegin()); + } + + void FocusOnEditableForm() { FocusOnPoint(EditableFormDropDown()); } + + void FocusOnNonEditableForm() { FocusOnPoint(NonEditableFormDropDown()); } + +- void FocusOnPoint(const CFX_PointF& point) { +- EXPECT_EQ(true, FORM_OnFocus(form_handle(), page(), 0, point.x, point.y)); +- } +- + const CFX_PointF& EditableFormBegin() const { + static const CFX_PointF point = EditableFormAtX(kFormBeginX); + return point; +@@ -320,14 +333,14 @@ class FPDFFormFillComboBoxFormEmbedderTest + } + + static CFX_PointF EditableFormAtX(float x) { +- ASSERT(x >= kFormBeginX); +- ASSERT(x <= kFormEndX); ++ DCHECK(x >= kFormBeginX); ++ DCHECK(x <= kFormEndX); + return CFX_PointF(x, kEditableFormY); + } + + static CFX_PointF NonEditableFormAtX(float x) { +- ASSERT(x >= kFormBeginX); +- ASSERT(x <= kFormEndX); ++ DCHECK(x >= kFormBeginX); ++ DCHECK(x <= kFormEndX); + return CFX_PointF(x, kNonEditableFormY); + } + +@@ -368,10 +381,12 @@ class FPDFFormFillListBoxFormEmbedderTest + // - "Listbox_SingleSelect" - Ff: 0, 3 options with pair values. + // - "Listbox_MultiSelect" - Ff: 2097152, 26 options with single values. + // - "Listbox_ReadOnly" - Ff: 1, 3 options with single values. +- // - "Listbox_MultiSelectMultipleSelected" - Ff: 2097152, 5 options with +- // single values. ++ // - "Listbox_MultiSelectMultipleIndices" - Ff: 2097152, 5 options with ++ // single values. ++ // - "Listbox_MultiSelectMultipleValues" - same configs as above. ++ // - "Listbox_MultiSelectMultipleMismatch" - same configs as above. + // - "Listbox_SingleSelectLastSelected" - Ff: 0, 10 options with single +- // values. ++ // values. + return "listbox_form.pdf"; + } + +@@ -388,10 +403,22 @@ class FPDFFormFillListBoxFormEmbedderTest + GetFormTypeAtPoint(MultiSelectSecondVisibleOption())); + EXPECT_EQ( + GetFormType(), +- GetFormTypeAtPoint(MultiSelectMultipleSelectedFirstVisibleOption())); ++ GetFormTypeAtPoint(MultiSelectMultipleIndicesFirstVisibleOption())); ++ EXPECT_EQ( ++ GetFormType(), ++ GetFormTypeAtPoint(MultiSelectMultipleIndicesSecondVisibleOption())); ++ EXPECT_EQ( ++ GetFormType(), ++ GetFormTypeAtPoint(MultiSelectMultipleValuesFirstVisibleOption())); + EXPECT_EQ( + GetFormType(), +- GetFormTypeAtPoint(MultiSelectMultipleSelectedSecondVisibleOption())); ++ GetFormTypeAtPoint(MultiSelectMultipleValuesSecondVisibleOption())); ++ EXPECT_EQ( ++ GetFormType(), ++ GetFormTypeAtPoint(MultiSelectMultipleMismatchFirstVisibleOption())); ++ EXPECT_EQ( ++ GetFormType(), ++ GetFormTypeAtPoint(MultiSelectMultipleMismatchSecondVisibleOption())); + EXPECT_EQ(GetFormType(), + GetFormTypeAtPoint(SingleSelectLastSelectedFirstVisibleOption())); + EXPECT_EQ( +@@ -402,8 +429,8 @@ class FPDFFormFillListBoxFormEmbedderTest + void ClickOnSingleSelectFormOption(int item_index) { + // Only the first two indices are visible so can only click on those + // without scrolling. +- ASSERT(item_index >= 0); +- ASSERT(item_index < 2); ++ DCHECK(item_index >= 0); ++ DCHECK(item_index < 2); + if (item_index == 0) { + ClickOnFormFieldAtPoint(SingleSelectFirstVisibleOption()); + } else { +@@ -414,8 +441,8 @@ class FPDFFormFillListBoxFormEmbedderTest + void ClickOnMultiSelectFormOption(int item_index) { + // Only the first two indices are visible so can only click on those + // without scrolling. +- ASSERT(item_index >= 0); +- ASSERT(item_index < 2); ++ DCHECK(item_index >= 0); ++ DCHECK(item_index < 2); + if (item_index == 0) { + ClickOnFormFieldAtPoint(MultiSelectFirstVisibleOption()); + } else { +@@ -423,23 +450,23 @@ class FPDFFormFillListBoxFormEmbedderTest + } + } + +- void ClickOnMultiSelectMultipleSelectedFormOption(int item_index) { ++ void ClickOnMultiSelectMultipleValuesFormOption(int item_index) { + // Only two indices are visible so can only click on those + // without scrolling. +- ASSERT(item_index >= 0); +- ASSERT(item_index < 2); ++ DCHECK(item_index >= 0); ++ DCHECK(item_index < 2); + if (item_index == 0) { +- ClickOnFormFieldAtPoint(MultiSelectMultipleSelectedFirstVisibleOption()); ++ ClickOnFormFieldAtPoint(MultiSelectMultipleValuesFirstVisibleOption()); + } else { +- ClickOnFormFieldAtPoint(MultiSelectMultipleSelectedSecondVisibleOption()); ++ ClickOnFormFieldAtPoint(MultiSelectMultipleValuesSecondVisibleOption()); + } + } + + void ClickOnSingleSelectLastSelectedFormOption(int item_index) { + // Only two indices are visible so can only click on those + // without scrolling. +- ASSERT(item_index >= 0); +- ASSERT(item_index < 2); ++ DCHECK(item_index >= 0); ++ DCHECK(item_index < 2); + if (item_index == 0) { + ClickOnFormFieldAtPoint(SingleSelectLastSelectedFirstVisibleOption()); + } else { +@@ -455,8 +482,16 @@ class FPDFFormFillListBoxFormEmbedderTest + FocusOnPoint(MultiSelectFirstVisibleOption()); + } + +- void FocusOnMultiSelectMultipleSelectedForm() { +- FocusOnPoint(MultiSelectMultipleSelectedFirstVisibleOption()); ++ void FocusOnMultiSelectMultipleIndicesForm() { ++ FocusOnPoint(MultiSelectMultipleIndicesFirstVisibleOption()); ++ } ++ ++ void FocusOnMultiSelectMultipleValuesForm() { ++ FocusOnPoint(MultiSelectMultipleValuesFirstVisibleOption()); ++ } ++ ++ void FocusOnMultiSelectMultipleMismatchForm() { ++ FocusOnPoint(MultiSelectMultipleMismatchFirstVisibleOption()); + } + + void FocusOnSingleSelectLastSelectedForm() { +@@ -487,15 +522,39 @@ class FPDFFormFillListBoxFormEmbedderTest + return point; + } + +- const CFX_PointF& MultiSelectMultipleSelectedFirstVisibleOption() const { ++ const CFX_PointF& MultiSelectMultipleIndicesFirstVisibleOption() const { ++ static const CFX_PointF point(kFormBeginX, ++ kMultiFormMultipleIndicesYFirstVisibleOption); ++ return point; ++ } ++ ++ const CFX_PointF& MultiSelectMultipleIndicesSecondVisibleOption() const { + static const CFX_PointF point( +- kFormBeginX, kMultiFormMultipleSelectedYFirstVisibleOption); ++ kFormBeginX, kMultiFormMultipleIndicesYSecondVisibleOption); + return point; + } + +- const CFX_PointF& MultiSelectMultipleSelectedSecondVisibleOption() const { ++ const CFX_PointF& MultiSelectMultipleValuesFirstVisibleOption() const { ++ static const CFX_PointF point(kFormBeginX, ++ kMultiFormMultipleValuesYFirstVisibleOption); ++ return point; ++ } ++ ++ const CFX_PointF& MultiSelectMultipleValuesSecondVisibleOption() const { ++ static const CFX_PointF point(kFormBeginX, ++ kMultiFormMultipleValuesYSecondVisibleOption); ++ return point; ++ } ++ ++ const CFX_PointF& MultiSelectMultipleMismatchFirstVisibleOption() const { + static const CFX_PointF point( +- kFormBeginX, kMultiFormMultipleSelectedYSecondVisibleOption); ++ kFormBeginX, kMultiFormMultipleMismatchYFirstVisibleOption); ++ return point; ++ } ++ ++ const CFX_PointF& MultiSelectMultipleMismatchSecondVisibleOption() const { ++ static const CFX_PointF point( ++ kFormBeginX, kMultiFormMultipleMismatchYSecondVisibleOption); + return point; + } + +@@ -517,21 +576,37 @@ class FPDFFormFillListBoxFormEmbedderTest + static constexpr float kSingleFormYSecondVisibleOption = 358.0; + static constexpr float kMultiFormYFirstVisibleOption = 423.0; + static constexpr float kMultiFormYSecondVisibleOption = 408.0; +- static constexpr float kMultiFormMultipleSelectedYFirstVisibleOption = 223.0; +- static constexpr float kMultiFormMultipleSelectedYSecondVisibleOption = 208.0; ++ static constexpr float kMultiFormMultipleIndicesYFirstVisibleOption = 273.0; ++ static constexpr float kMultiFormMultipleIndicesYSecondVisibleOption = 258.0; ++ static constexpr float kMultiFormMultipleValuesYFirstVisibleOption = 223.0; ++ static constexpr float kMultiFormMultipleValuesYSecondVisibleOption = 208.0; ++ static constexpr float kMultiFormMultipleMismatchYFirstVisibleOption = 173.0; ++ static constexpr float kMultiFormMultipleMismatchYSecondVisibleOption = 158.0; + static constexpr float kSingleFormLastSelectedYFirstVisibleOption = 123.0; + static constexpr float kSingleFormLastSelectedYSecondVisibleOption = 108.0; + }; + ++class FPDFFormFillTextFormEmbedderTestVersion2 ++ : public FPDFFormFillTextFormEmbedderTest { ++ void SetUp() override { ++ SetFormFillInfoVersion(2); ++ FPDFFormFillInteractiveEmbedderTest::SetUp(); ++ } ++}; ++ + TEST_F(FPDFFormFillEmbedderTest, FirstTest) { + EmbedderTestMockDelegate mock; + EXPECT_CALL(mock, Alert(_, _, _, _)).Times(0); + EXPECT_CALL(mock, UnsupportedHandler(_)).Times(0); + EXPECT_CALL(mock, SetTimer(_, _)).Times(0); + EXPECT_CALL(mock, KillTimer(_)).Times(0); ++ EXPECT_CALL(mock, OnFocusChange(_, _, _)).Times(0); ++ EXPECT_CALL(mock, DoURIAction(_)).Times(0); ++ EXPECT_CALL(mock, DoURIActionWithKeyboardModifier(_, _, _)).Times(0); ++ EXPECT_CALL(mock, DoGoToAction(_, _, _, _, _)).Times(0); + SetDelegate(&mock); + +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + UnloadPage(page); +@@ -541,7 +616,7 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_487928) { + EmbedderTestTimerHandlingDelegate delegate; + SetDelegate(&delegate); + +- EXPECT_TRUE(OpenDocument("bug_487928.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_487928.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + DoOpenActions(); +@@ -553,7 +628,7 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_507316) { + EmbedderTestTimerHandlingDelegate delegate; + SetDelegate(&delegate); + +- EXPECT_TRUE(OpenDocument("bug_507316.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_507316.pdf")); + FPDF_PAGE page = LoadPage(2); + EXPECT_TRUE(page); + DoOpenActions(); +@@ -562,7 +637,7 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_507316) { + } + + TEST_F(FPDFFormFillEmbedderTest, BUG_514690) { +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + +@@ -577,7 +652,7 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_900552) { + EmbedderTestTimerHandlingDelegate delegate; + SetDelegate(&delegate); + +- EXPECT_TRUE(OpenDocument("bug_900552.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_900552.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + DoOpenActions(); +@@ -595,7 +670,7 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_901654) { + EmbedderTestTimerHandlingDelegate delegate; + SetDelegate(&delegate); + +- EXPECT_TRUE(OpenDocument("bug_901654.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_901654.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + DoOpenActions(); +@@ -614,7 +689,7 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_901654_2) { + EmbedderTestTimerHandlingDelegate delegate; + SetDelegate(&delegate); + +- EXPECT_TRUE(OpenDocument("bug_901654_2.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_901654_2.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + DoOpenActions(); +@@ -629,6 +704,296 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_901654_2) { + UnloadPage(page); + } + ++TEST_F(FPDFFormFillEmbedderTest, GetFocusedAnnotation) { ++ ASSERT_TRUE(OpenDocument("annotiter.pdf")); ++ std::vector pages; ++ for (size_t i = 0; i < 3; ++i) { ++ pages.push_back(LoadPage(i)); ++ ASSERT_TRUE(pages.back()); ++ } ++ ++ // Ensure that there is no focused annotation. ++ FPDF_ANNOTATION annot = nullptr; ++ int page_index = -2; ++ ASSERT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot)); ++ EXPECT_FALSE(annot); ++ EXPECT_EQ(-1, page_index); ++ ++ // Validate that nullptr values are handled properly. ++ EXPECT_FALSE(FORM_GetFocusedAnnot(nullptr, &page_index, &annot)); ++ EXPECT_FALSE(FORM_GetFocusedAnnot(form_handle(), &page_index, nullptr)); ++ EXPECT_FALSE(FORM_GetFocusedAnnot(form_handle(), nullptr, &annot)); ++ ++ const CFX_PointF right_bottom_annot_point(410.0f, 210.0f); ++ constexpr int kExpectedAnnotIndex = 3; ++ ++ for (size_t i = 0; i < pages.size(); ++i) { ++ // Invoke click on the form field to bring it to focus. ++ FORM_OnMouseMove(form_handle(), pages[i], 0, right_bottom_annot_point.x, ++ right_bottom_annot_point.y); ++ FORM_OnLButtonDown(form_handle(), pages[i], 0, right_bottom_annot_point.x, ++ right_bottom_annot_point.y); ++ FORM_OnLButtonUp(form_handle(), pages[i], 0, right_bottom_annot_point.x, ++ right_bottom_annot_point.y); ++ ++ ASSERT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot)); ++ ASSERT_TRUE(annot); ++ ++ EXPECT_EQ(kExpectedAnnotIndex, FPDFPage_GetAnnotIndex(pages[i], annot)); ++ EXPECT_EQ(static_cast(i), page_index); ++ ++ FPDFPage_CloseAnnot(annot); ++ } ++ ++ for (FPDF_PAGE page : pages) ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFFormFillEmbedderTest, SetFocusedAnnotation) { ++ ASSERT_TRUE(OpenDocument("annotiter.pdf")); ++ std::vector pages; ++ for (size_t i = 0; i < 3; ++i) { ++ pages.push_back(LoadPage(i)); ++ ASSERT_TRUE(pages.back()); ++ } ++ ++ // Ensure that there is no focused annotation. ++ FPDF_ANNOTATION annot = nullptr; ++ int page_index = -2; ++ ASSERT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot)); ++ EXPECT_FALSE(annot); ++ EXPECT_EQ(-1, page_index); ++ ++ // Validate that nullptr values are handled properly. ++ EXPECT_FALSE(FORM_SetFocusedAnnot(nullptr, annot)); ++ EXPECT_FALSE(FORM_SetFocusedAnnot(form_handle(), nullptr)); ++ ++ constexpr int kExpectedAnnotIndex = 2; ++ ++ for (size_t i = 0; i < pages.size(); ++i) { ++ // Setting focus on an annotation on page i. ++ ScopedFPDFAnnotation focused_annot( ++ FPDFPage_GetAnnot(pages[i], kExpectedAnnotIndex)); ++ ASSERT_TRUE(focused_annot); ++ ++ ASSERT_TRUE(FORM_SetFocusedAnnot(form_handle(), focused_annot.get())); ++ ++ ASSERT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot)); ++ EXPECT_EQ(kExpectedAnnotIndex, FPDFPage_GetAnnotIndex(pages[i], annot)); ++ EXPECT_EQ(static_cast(i), page_index); ++ ++ FPDFPage_CloseAnnot(annot); ++ } ++ ++ for (FPDF_PAGE page : pages) ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFFormFillEmbedderTest, FormFillFirstTab) { ++ ASSERT_TRUE(OpenDocument("annotiter.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ // Invoking first tab on the page. ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0)); ++ int page_index = -2; ++ FPDF_ANNOTATION annot = nullptr; ++ EXPECT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot)); ++ EXPECT_EQ(0, page_index); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(1, FPDFPage_GetAnnotIndex(page, annot)); ++ FPDFPage_CloseAnnot(annot); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFFormFillEmbedderTest, FormFillFirstShiftTab) { ++ ASSERT_TRUE(OpenDocument("annotiter.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ // Invoking first shift-tab on the page. ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, ++ FWL_EVENTFLAG_ShiftKey)); ++ int page_index = -2; ++ FPDF_ANNOTATION annot = nullptr; ++ EXPECT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot)); ++ EXPECT_EQ(0, page_index); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(0, FPDFPage_GetAnnotIndex(page, annot)); ++ FPDFPage_CloseAnnot(annot); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFFormFillEmbedderTest, FormFillContinuousTab) { ++ ASSERT_TRUE(OpenDocument("annotiter.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ static constexpr int kExpectedAnnotIndex[] = {1, 2, 3, 0}; ++ // Tabs should iterate focus over annotations. ++ for (size_t i = 0; i < std::size(kExpectedAnnotIndex); ++i) { ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0)); ++ int page_index = -2; ++ FPDF_ANNOTATION annot = nullptr; ++ EXPECT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot)); ++ EXPECT_EQ(0, page_index); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(kExpectedAnnotIndex[i], FPDFPage_GetAnnotIndex(page, annot)); ++ FPDFPage_CloseAnnot(annot); ++ } ++ ++ // Tab should not be handled as the last annotation of the page is in focus. ++ ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0)); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFFormFillEmbedderTest, FormFillContinuousShiftTab) { ++ ASSERT_TRUE(OpenDocument("annotiter.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ static constexpr int kExpectedAnnotIndex[] = {0, 3, 2, 1}; ++ // Shift-tabs should iterate focus over annotations. ++ for (size_t i = 0; i < std::size(kExpectedAnnotIndex); ++i) { ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, ++ FWL_EVENTFLAG_ShiftKey)); ++ int page_index = -2; ++ FPDF_ANNOTATION annot = nullptr; ++ EXPECT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot)); ++ EXPECT_EQ(0, page_index); ++ ASSERT_TRUE(annot); ++ EXPECT_EQ(kExpectedAnnotIndex[i], FPDFPage_GetAnnotIndex(page, annot)); ++ FPDFPage_CloseAnnot(annot); ++ } ++ ++ // Shift-tab should not be handled as the first annotation of the page is in ++ // focus. ++ ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, ++ FWL_EVENTFLAG_ShiftKey)); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFFormFillEmbedderTest, TabWithModifiers) { ++ ASSERT_TRUE(OpenDocument("annotiter.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, ++ FWL_EVENTFLAG_ControlKey)); ++ ++ ASSERT_FALSE( ++ FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, FWL_EVENTFLAG_AltKey)); ++ ++ ASSERT_FALSE( ++ FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, ++ (FWL_EVENTFLAG_ControlKey | FWL_EVENTFLAG_ShiftKey))); ++ ++ ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, ++ (FWL_EVENTFLAG_AltKey | FWL_EVENTFLAG_ShiftKey))); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFFormFillEmbedderTest, KeyPressWithNoFocusedAnnot) { ++ ASSERT_TRUE(OpenDocument("annotiter.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ // There should be no focused annotation to start with. ++ int page_index = -2; ++ FPDF_ANNOTATION annot = nullptr; ++ EXPECT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot)); ++ EXPECT_EQ(-1, page_index); ++ EXPECT_FALSE(annot); ++ ++ static constexpr int kKeysToPress[] = { ++ FWL_VKEY_NewLine, FWL_VKEY_Return, FWL_VKEY_Space, ++ FWL_VKEY_Delete, FWL_VKEY_0, FWL_VKEY_9, ++ FWL_VKEY_A, FWL_VKEY_Z, FWL_VKEY_F1, ++ }; ++ for (int key : kKeysToPress) { ++ // Pressing random keys when there is no focus should not trigger focus. ++ EXPECT_FALSE(FORM_OnKeyDown(form_handle(), page, key, 0)); ++ page_index = -2; ++ annot = nullptr; ++ EXPECT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot)); ++ EXPECT_EQ(-1, page_index); ++ EXPECT_FALSE(annot); ++ } ++ ++ UnloadPage(page); ++} ++ ++#ifdef PDF_ENABLE_XFA ++TEST_F(FPDFFormFillEmbedderTest, XFAFormFillFirstTab) { ++ ASSERT_TRUE(OpenDocument("xfa/email_recommended.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ // Invoking first tab on the page. ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0)); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFFormFillEmbedderTest, XFAFormFillFirstShiftTab) { ++ ASSERT_TRUE(OpenDocument("xfa/email_recommended.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ // Invoking first shift-tab on the page. ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, ++ FWL_EVENTFLAG_ShiftKey)); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFFormFillEmbedderTest, XFAFormFillContinuousTab) { ++ ASSERT_TRUE(OpenDocument("xfa/email_recommended.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ // Invoking first tab on the page. ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0)); ++ ++ // Subsequent tabs should move focus over annotations. ++ for (size_t i = 0; i < 9; ++i) ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0)); ++ ++ // Tab should not be handled as the last annotation of the page is in focus. ++ ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0)); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFFormFillEmbedderTest, XFAFormFillContinuousShiftTab) { ++ ASSERT_TRUE(OpenDocument("xfa/email_recommended.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ // Invoking first shift-tab on the page. ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, ++ FWL_EVENTFLAG_ShiftKey)); ++ ++ // Subsequent shift-tabs should move focus over annotations. ++ for (size_t i = 0; i < 9; ++i) { ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, ++ FWL_EVENTFLAG_ShiftKey)); ++ } ++ ++ // Shift-tab should not be handled as the first annotation of the page is in ++ // focus. ++ ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, ++ FWL_EVENTFLAG_ShiftKey)); ++ ++ UnloadPage(page); ++} ++#endif // PDF_ENABLE_XFA ++ + class DoURIActionBlockedDelegate final : public EmbedderTest::Delegate { + public: + void DoURIAction(FPDF_BYTESTRING uri) override { +@@ -640,7 +1005,7 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_851821) { + DoURIActionBlockedDelegate delegate; + SetDelegate(&delegate); + +- EXPECT_TRUE(OpenDocument("redirect.pdf")); ++ ASSERT_TRUE(OpenDocument("redirect.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + DoOpenActions(); +@@ -648,13 +1013,82 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_851821) { + UnloadPage(page); + } + ++TEST_F(FPDFFormFillEmbedderTest, CheckReadOnlyInCheckbox) { ++ EmbedderTestTimerHandlingDelegate delegate; ++ SetDelegate(&delegate); ++ ++ ASSERT_TRUE(OpenDocument("click_form.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ // Check for read-only checkbox. ++ ScopedFPDFAnnotation focused_annot(FPDFPage_GetAnnot(page, 1)); ++ ASSERT_TRUE(FORM_SetFocusedAnnot(form_handle(), focused_annot.get())); ++ ++ // Shift-tab to the previous control. ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, ++ FWL_EVENTFLAG_ShiftKey)); ++ FPDF_ANNOTATION annot = nullptr; ++ int page_index = -1; ++ ASSERT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot)); ++ EXPECT_EQ(0, FPDFPage_GetAnnotIndex(page, annot)); ++ ++ // The read-only checkbox is initially in checked state. ++ EXPECT_TRUE(FPDFAnnot_IsChecked(form_handle(), annot)); ++ ++ EXPECT_TRUE(FORM_OnChar(form_handle(), page, pdfium::ascii::kReturn, 0)); ++ EXPECT_TRUE(FPDFAnnot_IsChecked(form_handle(), annot)); ++ ++ EXPECT_TRUE(FORM_OnChar(form_handle(), page, pdfium::ascii::kSpace, 0)); ++ EXPECT_TRUE(FPDFAnnot_IsChecked(form_handle(), annot)); ++ ++ FPDFPage_CloseAnnot(annot); ++ } ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFFormFillEmbedderTest, CheckReadOnlyInRadiobutton) { ++ EmbedderTestTimerHandlingDelegate delegate; ++ SetDelegate(&delegate); ++ ++ ASSERT_TRUE(OpenDocument("click_form.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ // Check for read-only radio button. ++ ScopedFPDFAnnotation focused_annot(FPDFPage_GetAnnot(page, 1)); ++ ASSERT_TRUE(FORM_SetFocusedAnnot(form_handle(), focused_annot.get())); ++ ++ // Tab to the next control. ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0)); ++ ++ FPDF_ANNOTATION annot = nullptr; ++ int page_index = -1; ++ ASSERT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot)); ++ EXPECT_EQ(2, FPDFPage_GetAnnotIndex(page, annot)); ++ // The read-only radio button is initially in checked state. ++ EXPECT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot)); ++ ++ EXPECT_TRUE(FORM_OnChar(form_handle(), page, pdfium::ascii::kReturn, 0)); ++ EXPECT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot)); ++ ++ EXPECT_TRUE(FORM_OnChar(form_handle(), page, pdfium::ascii::kSpace, 0)); ++ EXPECT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot)); ++ ++ FPDFPage_CloseAnnot(annot); ++ } ++ UnloadPage(page); ++} ++ + #ifdef PDF_ENABLE_V8 + TEST_F(FPDFFormFillEmbedderTest, DisableJavaScript) { + // Test that timers and intervals can't fire without JS. + EmbedderTestTimerHandlingDelegate delegate; + SetDelegate(&delegate); + +- EXPECT_TRUE(OpenDocumentWithoutJavaScript("bug_551248.pdf")); ++ ASSERT_TRUE(OpenDocumentWithoutJavaScript("bug_551248.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + DoOpenActions(); +@@ -683,7 +1117,7 @@ TEST_F(FPDFFormFillEmbedderTest, DocumentAActions) { + EmbedderTestTimerHandlingDelegate delegate; + SetDelegate(&delegate); + +- EXPECT_TRUE(OpenDocument("document_aactions.pdf")); ++ ASSERT_TRUE(OpenDocument("document_aactions.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + +@@ -707,7 +1141,7 @@ TEST_F(FPDFFormFillEmbedderTest, DocumentAActionsDisableJavaScript) { + EmbedderTestTimerHandlingDelegate delegate; + SetDelegate(&delegate); + +- EXPECT_TRUE(OpenDocumentWithoutJavaScript("document_aactions.pdf")); ++ ASSERT_TRUE(OpenDocumentWithoutJavaScript("document_aactions.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + +@@ -728,7 +1162,7 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_551248) { + EmbedderTestTimerHandlingDelegate delegate; + SetDelegate(&delegate); + +- EXPECT_TRUE(OpenDocument("bug_551248.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_551248.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + DoOpenActions(); +@@ -780,7 +1214,7 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_620428) { + EmbedderTestTimerHandlingDelegate delegate; + SetDelegate(&delegate); + +- EXPECT_TRUE(OpenDocument("bug_620428.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_620428.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + DoOpenActions(); +@@ -797,7 +1231,7 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_634394) { + EmbedderTestTimerHandlingDelegate delegate; + SetDelegate(&delegate); + +- EXPECT_TRUE(OpenDocument("bug_634394.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_634394.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + DoOpenActions(); +@@ -819,7 +1253,7 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_634716) { + EmbedderTestTimerHandlingDelegate delegate; + SetDelegate(&delegate); + +- EXPECT_TRUE(OpenDocument("bug_634716.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_634716.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + DoOpenActions(); +@@ -841,7 +1275,7 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_679649) { + EmbedderTestTimerHandlingDelegate delegate; + SetDelegate(&delegate); + +- EXPECT_TRUE(OpenDocument("bug_679649.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_679649.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + +@@ -858,7 +1292,7 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_707673) { + EmbedderTestTimerHandlingDelegate delegate; + SetDelegate(&delegate); + +- EXPECT_TRUE(OpenDocument("bug_707673.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_707673.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + +@@ -873,7 +1307,7 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_707673) { + } + + TEST_F(FPDFFormFillEmbedderTest, BUG_765384) { +- EXPECT_TRUE(OpenDocument("bug_765384.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_765384.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + +@@ -884,32 +1318,31 @@ TEST_F(FPDFFormFillEmbedderTest, BUG_765384) { + } + #endif // PDF_ENABLE_V8 + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_FormText DISABLED_FormText ++TEST_F(FPDFFormFillEmbedderTest, FormText) { ++ const char* focused_text_form_with_abc_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "07a179a9dfb8f5462746262984109a99"; ++#if BUILDFLAG(IS_APPLE) ++ return "9fb14198d75ca0a107060c60ca21b0c7"; + #else +-#define MAYBE_FormText FormText ++ return "6e6f790bb14c4fc6107faf8c17d23dbd"; + #endif +-TEST_F(FPDFFormFillEmbedderTest, MAYBE_FormText) { +-#if defined(OS_MACOSX) +- const char md5_1[] = "5f11dbe575fe197a37c3fb422559f8ff"; +- const char md5_2[] = "35b1a4b679eafc749a0b6fda750c0e8d"; +- const char md5_3[] = "65c64a7c355388f719a752aa1e23f6fe"; +-#elif defined(OS_WIN) +- const char md5_1[] = "d3204faa62b607f0bd3893c9c22cabcb"; +- const char md5_2[] = "29d1c3fd226ca6a69597f75937690320"; +- const char md5_3[] = "5e678a55912cb568fd677bf34abb8727"; ++ }(); ++ const char* unfocused_text_form_with_abc_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "a21b74cc620db8a9891ebd69e1aeda98"; ++#if BUILDFLAG(IS_APPLE) ++ return "3c3209357e0c057a0620afa7d83eb784"; + #else +- const char md5_1[] = "b890950d4b9bc163b1a96797f3004b53"; +- const char md5_2[] = "11487d5597599a26e8912b9c1d9422cb"; +- const char md5_3[] = "bffe0ecea9a533f217047ee41d6be466"; ++ return "94b7e10ac8c662b73e33628ca2f5e63b"; + #endif ++ }(); + { +- EXPECT_TRUE(OpenDocument("text_form.pdf")); ++ ASSERT_TRUE(OpenDocument("text_form.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + ScopedFPDFBitmap bitmap1 = RenderLoadedPage(page); +- CompareBitmap(bitmap1.get(), 300, 300, md5_1); ++ CompareBitmap(bitmap1.get(), 300, 300, TextFormChecksum()); + + // Click on the textfield + EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD, +@@ -921,25 +1354,26 @@ TEST_F(FPDFFormFillEmbedderTest, MAYBE_FormText) { + FORM_OnLButtonUp(form_handle(), page, 0, 120.0, 120.0); + + // Write "ABC" +- FORM_OnChar(form_handle(), page, 65, 0); +- FORM_OnChar(form_handle(), page, 66, 0); +- FORM_OnChar(form_handle(), page, 67, 0); ++ FORM_OnChar(form_handle(), page, 'A', 0); ++ FORM_OnChar(form_handle(), page, 'B', 0); ++ FORM_OnChar(form_handle(), page, 'C', 0); + ScopedFPDFBitmap bitmap2 = RenderLoadedPage(page); +- CompareBitmap(bitmap2.get(), 300, 300, md5_2); ++ CompareBitmap(bitmap2.get(), 300, 300, focused_text_form_with_abc_checksum); + + // Focus remains despite right clicking out of the textfield + FORM_OnMouseMove(form_handle(), page, 0, 15.0, 15.0); + FORM_OnRButtonDown(form_handle(), page, 0, 15.0, 15.0); + FORM_OnRButtonUp(form_handle(), page, 0, 15.0, 15.0); + ScopedFPDFBitmap bitmap3 = RenderLoadedPage(page); +- CompareBitmap(bitmap3.get(), 300, 300, md5_2); ++ CompareBitmap(bitmap3.get(), 300, 300, focused_text_form_with_abc_checksum); + + // Take out focus by clicking out of the textfield + FORM_OnMouseMove(form_handle(), page, 0, 15.0, 15.0); + FORM_OnLButtonDown(form_handle(), page, 0, 15.0, 15.0); + FORM_OnLButtonUp(form_handle(), page, 0, 15.0, 15.0); + ScopedFPDFBitmap bitmap4 = RenderLoadedPage(page); +- CompareBitmap(bitmap4.get(), 300, 300, md5_3); ++ CompareBitmap(bitmap4.get(), 300, 300, ++ unfocused_text_form_with_abc_checksum); + + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + +@@ -947,90 +1381,206 @@ TEST_F(FPDFFormFillEmbedderTest, MAYBE_FormText) { + UnloadPage(page); + } + // Check saved document +- VerifySavedDocument(300, 300, md5_3); ++ VerifySavedDocument(300, 300, unfocused_text_form_with_abc_checksum); + } + + // Tests using FPDF_REVERSE_BYTE_ORDER with FPDF_FFLDraw(). The two rendered + // bitmaps should be different. +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_BUG_1281 DISABLED_BUG_1281 +-#else +-#define MAYBE_BUG_1281 BUG_1281 +-#endif +-TEST_F(FPDFFormFillEmbedderTest, MAYBE_BUG_1281) { +- const char kMd5Normal[] = "6c674642154408e877d88c6c082d67e9"; +- const char kMd5ReverseByteOrder[] = "24fff03d1e663b7ece5f6e69ad837124"; ++TEST_F(FPDFFormFillEmbedderTest, BUG_1281) { ++ const char* reverse_byte_order_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "8077970bbd10333f18186a9bb459bbe6"; ++ return "24fff03d1e663b7ece5f6e69ad837124"; ++ }(); + + ASSERT_TRUE(OpenDocument("bug_890322.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + ScopedFPDFBitmap bitmap_normal = RenderLoadedPage(page); +- CompareBitmap(bitmap_normal.get(), 200, 200, kMd5Normal); ++ CompareBitmap(bitmap_normal.get(), 200, 200, pdfium::Bug890322Checksum()); + + ScopedFPDFBitmap bitmap_reverse_byte_order = + RenderLoadedPageWithFlags(page, FPDF_REVERSE_BYTE_ORDER); + CompareBitmap(bitmap_reverse_byte_order.get(), 200, 200, +- kMd5ReverseByteOrder); ++ reverse_byte_order_checksum); + + UnloadPage(page); + } + +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_RemoveFormFieldHighlight DISABLED_RemoveFormFieldHighlight ++TEST_F(FPDFFormFillEmbedderTest, Bug1302455RenderOnly) { ++ const char* checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "520c4415c9977f40d6b4af5a0a94d764"; ++ return "bbee92af1daec2340c81f482878744d8"; ++ }(); ++ { ++ ASSERT_TRUE(OpenDocument("bug_1302455.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ ScopedFPDFBitmap bitmap = RenderLoadedPage(page); ++ CompareBitmap(bitmap.get(), 300, 300, checksum); ++ ++ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ ++ UnloadPage(page); ++ } ++ VerifySavedDocument(300, 300, checksum); ++} ++ ++TEST_F(FPDFFormFillEmbedderTest, Bug1302455EditFirstForm) { ++ const char* checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "29a06da3e47f67535e266b090a5ac82d"; ++#if BUILDFLAG(IS_APPLE) ++ return "bf5423874f188427d2500a2bc4abebbe"; ++#else ++ return "6a4ac9a15d2c34589616c8f2b05fbedd"; ++#endif ++ }(); ++ { ++ ASSERT_TRUE(OpenDocument("bug_1302455.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD, ++ FPDFPage_HasFormFieldAtPoint(form_handle(), page, 110, 110)); ++ FORM_OnMouseMove(form_handle(), page, 0, 110, 110); ++ FORM_OnLButtonDown(form_handle(), page, 0, 110, 110); ++ FORM_OnLButtonUp(form_handle(), page, 0, 110, 110); ++ FORM_OnChar(form_handle(), page, 'A', 0); ++ ++ FORM_ForceToKillFocus(form_handle()); ++ ScopedFPDFBitmap bitmap = RenderLoadedPage(page); ++ CompareBitmap(bitmap.get(), 300, 300, checksum); ++ ++ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ ++ UnloadPage(page); ++ } ++ VerifySavedDocument(300, 300, checksum); ++} ++ ++TEST_F(FPDFFormFillEmbedderTest, Bug1302455EditSecondForm) { ++ const char* checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "19f8574d6378ee36e349376d88b7a2c4"; ++#if BUILDFLAG(IS_APPLE) ++ return "8a0fd8772dba6e1e952e49d159cc64b5"; + #else +-#define MAYBE_RemoveFormFieldHighlight RemoveFormFieldHighlight ++ return "45a7694933c2ba3c5dc8f6cc18b79175"; + #endif +-TEST_F(FPDFFormFillEmbedderTest, MAYBE_RemoveFormFieldHighlight) { +-#if defined(OS_MACOSX) +- const char kMd5Normal[] = "5f11dbe575fe197a37c3fb422559f8ff"; +- const char kMd5NoHighlight[] = "575ec237c790950f40bfcaefb2e3923c"; +-#elif defined(OS_WIN) +- const char kMd5Normal[] = "d3204faa62b607f0bd3893c9c22cabcb"; +- const char kMd5NoHighlight[] = "3ec0938828e0a37ef23f687ee95a80e1"; ++ }(); ++ { ++ ASSERT_TRUE(OpenDocument("bug_1302455.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD, ++ FPDFPage_HasFormFieldAtPoint(form_handle(), page, 110, 170)); ++ FORM_OnMouseMove(form_handle(), page, 0, 110, 170); ++ FORM_OnLButtonDown(form_handle(), page, 0, 110, 170); ++ FORM_OnLButtonUp(form_handle(), page, 0, 110, 170); ++ FORM_OnChar(form_handle(), page, 'B', 0); ++ ++ FORM_ForceToKillFocus(form_handle()); ++ ScopedFPDFBitmap bitmap = RenderLoadedPage(page); ++ CompareBitmap(bitmap.get(), 300, 300, checksum); ++ ++ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ ++ UnloadPage(page); ++ } ++ VerifySavedDocument(300, 300, checksum); ++} ++ ++TEST_F(FPDFFormFillEmbedderTest, Bug1302455EditBothForms) { ++ const char* checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "edbc9b0e190118a9039fffc11e494081"; ++#if BUILDFLAG(IS_APPLE) ++ return "1f422ee1c520ad74b1a993b64bd4dc4a"; + #else +- const char kMd5Normal[] = "b890950d4b9bc163b1a96797f3004b53"; +- const char kMd5NoHighlight[] = "006010c318457810a518aa5e0b33c498"; ++ return "13984969b1e141079ab5f4aa80185463"; + #endif ++ }(); ++ { ++ ASSERT_TRUE(OpenDocument("bug_1302455.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD, ++ FPDFPage_HasFormFieldAtPoint(form_handle(), page, 110, 110)); ++ FORM_OnMouseMove(form_handle(), page, 0, 110, 110); ++ FORM_OnLButtonDown(form_handle(), page, 0, 110, 110); ++ FORM_OnLButtonUp(form_handle(), page, 0, 110, 110); ++ FORM_OnChar(form_handle(), page, 'A', 0); + +- EXPECT_TRUE(OpenDocument("text_form.pdf")); ++ EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD, ++ FPDFPage_HasFormFieldAtPoint(form_handle(), page, 110, 170)); ++ FORM_OnMouseMove(form_handle(), page, 0, 110, 170); ++ FORM_OnLButtonDown(form_handle(), page, 0, 110, 170); ++ FORM_OnLButtonUp(form_handle(), page, 0, 110, 170); ++ FORM_OnChar(form_handle(), page, 'B', 0); ++ ++ FORM_ForceToKillFocus(form_handle()); ++ ScopedFPDFBitmap bitmap = RenderLoadedPage(page); ++ CompareBitmap(bitmap.get(), 300, 300, checksum); ++ ++ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ ++ UnloadPage(page); ++ } ++ VerifySavedDocument(300, 300, checksum); ++} ++ ++TEST_F(FPDFFormFillEmbedderTest, RemoveFormFieldHighlight) { ++ const char* no_highlight_checksum = []() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "5c82aa43e3b478aa1e4c94bb9ef1f11f"; ++#endif ++ return "a6268304f7eedfa9ee98fac3caaf2efb"; ++ }(); ++ ++ ASSERT_TRUE(OpenDocument("text_form.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + ScopedFPDFBitmap bitmap1 = RenderLoadedPage(page); +- CompareBitmap(bitmap1.get(), 300, 300, kMd5Normal); ++ CompareBitmap(bitmap1.get(), 300, 300, TextFormChecksum()); + + // Removing the highlight changes the rendering. + FPDF_RemoveFormFieldHighlight(form_handle()); + ScopedFPDFBitmap bitmap2 = RenderLoadedPage(page); +- CompareBitmap(bitmap2.get(), 300, 300, kMd5NoHighlight); ++ CompareBitmap(bitmap2.get(), 300, 300, no_highlight_checksum); + + // Restoring it gives the original rendering. + SetInitialFormFieldHighlight(form_handle()); + ScopedFPDFBitmap bitmap3 = RenderLoadedPage(page); +- CompareBitmap(bitmap3.get(), 300, 300, kMd5Normal); ++ CompareBitmap(bitmap3.get(), 300, 300, TextFormChecksum()); + + UnloadPage(page); + } + + TEST_F(FPDFFormFillEmbedderTest, HasFormInfoNone) { +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); +- EXPECT_EQ(FORMTYPE_NONE, FPDF_GetFormType(document_)); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ EXPECT_EQ(FORMTYPE_NONE, FPDF_GetFormType(document())); + } + + TEST_F(FPDFFormFillEmbedderTest, HasFormInfoAcroForm) { +- EXPECT_TRUE(OpenDocument("text_form.pdf")); +- EXPECT_EQ(FORMTYPE_ACRO_FORM, FPDF_GetFormType(document_)); ++ ASSERT_TRUE(OpenDocument("text_form.pdf")); ++ EXPECT_EQ(FORMTYPE_ACRO_FORM, FPDF_GetFormType(document())); + } + + TEST_F(FPDFFormFillEmbedderTest, HasFormInfoXFAFull) { +- EXPECT_TRUE(OpenDocument("simple_xfa.pdf")); +- EXPECT_EQ(FORMTYPE_XFA_FULL, FPDF_GetFormType(document_)); ++ ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); ++ EXPECT_EQ(FORMTYPE_XFA_FULL, FPDF_GetFormType(document())); + } + + TEST_F(FPDFFormFillEmbedderTest, HasFormInfoXFAForeground) { +- EXPECT_TRUE(OpenDocument("bug_216.pdf")); +- EXPECT_EQ(FORMTYPE_XFA_FOREGROUND, FPDF_GetFormType(document_)); ++ ASSERT_TRUE(OpenDocument("bug_216.pdf")); ++ EXPECT_EQ(FORMTYPE_XFA_FOREGROUND, FPDF_GetFormType(document())); + } + + TEST_F(FPDFFormFillEmbedderTest, BadApiInputsText) { +@@ -1076,6 +1626,62 @@ TEST_F(FPDFFormFillEmbedderTest, BadApiInputsListBox) { + UnloadPage(page); + } + ++TEST_F(FPDFFormFillEmbedderTest, HasFormFieldAtPointForXFADoc) { ++ ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ EXPECT_EQ(-1, FPDFPage_HasFormFieldAtPoint(form_handle(), page, 612, 792)); ++ ++#ifdef PDF_ENABLE_XFA ++ constexpr int kExpectedFieldType = FPDF_FORMFIELD_XFA_TEXTFIELD; ++#else ++ constexpr int kExpectedFieldType = -1; ++#endif ++ EXPECT_EQ(kExpectedFieldType, ++ FPDFPage_HasFormFieldAtPoint(form_handle(), page, 50, 30)); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFFormFillEmbedderTest, SelectAllText) { ++ ASSERT_TRUE(OpenDocument("text_form.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ // Test bad arguments. ++ EXPECT_FALSE(FORM_SelectAllText(nullptr, nullptr)); ++ EXPECT_FALSE(FORM_SelectAllText(form_handle(), nullptr)); ++ EXPECT_FALSE(FORM_SelectAllText(nullptr, page)); ++ ++ // Focus on the text field and add some text. ++ EXPECT_TRUE(FORM_OnFocus(form_handle(), page, 0, 115, 115)); ++ ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"Hello"); ++ FORM_ReplaceSelection(form_handle(), page, text_to_insert.get()); ++ ++ // Sanity check text field data. ++ uint16_t buffer[6]; ++ ASSERT_EQ(12u, FORM_GetFocusedText(form_handle(), page, nullptr, 0)); ++ ASSERT_EQ(12u, ++ FORM_GetFocusedText(form_handle(), page, buffer, sizeof(buffer))); ++ EXPECT_EQ("Hello", GetPlatformString(buffer)); ++ ++ // Check there is no selection. ++ ASSERT_EQ(2u, FORM_GetSelectedText(form_handle(), page, nullptr, 0)); ++ ASSERT_EQ(2u, ++ FORM_GetSelectedText(form_handle(), page, buffer, sizeof(buffer))); ++ EXPECT_EQ("", GetPlatformString(buffer)); ++ ++ // Check FORM_SelectAllText() works. ++ EXPECT_TRUE(FORM_SelectAllText(form_handle(), page)); ++ ASSERT_EQ(12u, FORM_GetSelectedText(form_handle(), page, nullptr, 0)); ++ ASSERT_EQ(12u, ++ FORM_GetSelectedText(form_handle(), page, buffer, sizeof(buffer))); ++ EXPECT_EQ("Hello", GetPlatformString(buffer)); ++ ++ UnloadPage(page); ++} ++ + TEST_F(FPDFFormFillTextFormEmbedderTest, GetSelectedTextEmptyAndBasicKeyboard) { + // Test empty selection. + CheckFocusedFieldText(L""); +@@ -1852,6 +2458,81 @@ TEST_F(FPDFFormFillComboBoxFormEmbedderTest, + CheckSelection(L"ABCDEHello"); + } + ++TEST_F(FPDFFormFillComboBoxFormEmbedderTest, ++ CheckIfEnterAndSpaceKeyAreHandled) { ++ // Non-editable field is set to 'Banana' (index 1) upon opening. ++ ClickOnFormFieldAtPoint(NonEditableFormBegin()); ++ CheckIsIndexSelected(0, false); ++ CheckIsIndexSelected(1, true); ++ ++ // Verify that the Return character is handled. ++ EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kReturn, 0)); ++ ++ // Change the selection in the combo-box using the arrow down key. ++ EXPECT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Down, 0)); ++ CheckIsIndexSelected(1, false); ++ CheckIsIndexSelected(2, true); ++ ++ // Tab to the next control. ++ EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kTab, 0)); ++ ++ // Shift-tab to the previous control. ++ EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kTab, ++ FWL_EVENTFLAG_ShiftKey)); ++ ++ // Verify that the selection is unchanged. ++ CheckIsIndexSelected(2, true); ++ ++ // Verify that the Space character is handled. ++ EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kSpace, 0)); ++ ++ // Change the selection in the combo-box using the arrow down key. ++ EXPECT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Down, 0)); ++ CheckIsIndexSelected(3, true); ++ ++ // Tab to the next control. ++ EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kTab, 0)); ++ ++ // Shift-tab to the previous control. ++ EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kTab, ++ FWL_EVENTFLAG_ShiftKey)); ++ ++ // Verify that the selection is unchanged. ++ CheckIsIndexSelected(3, true); ++} ++ ++TEST_F(FPDFFormFillComboBoxFormEmbedderTest, ++ CheckIfEnterAndSpaceKeyAreHandledOnEditableFormField) { ++ // Non-editable field is set to 'Banana' (index 1) upon opening. ++ ClickOnFormFieldAtPoint(EditableFormBegin()); ++ CheckIsIndexSelected(0, false); ++ CheckIsIndexSelected(1, false); ++ ++ // Verify that the Return character is handled. ++ EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kReturn, 0)); ++ ++ // Change the selection in the combo-box using the arrow down key. ++ EXPECT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Down, 0)); ++ CheckIsIndexSelected(0, true); ++ CheckIsIndexSelected(1, false); ++ ++ // Tab to the next control. ++ EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kTab, 0)); ++ ++ // Shift-tab to the previous control. ++ EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kTab, ++ FWL_EVENTFLAG_ShiftKey)); ++ ++ // Verify that the selection is unchanged. ++ CheckIsIndexSelected(0, true); ++ ++ // Verify that the Space character is handled. ++ EXPECT_TRUE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kSpace, 0)); ++ ++ CheckFocusedFieldText(L" "); ++ CheckIsIndexSelected(0, false); ++} ++ + TEST_F(FPDFFormFillTextFormEmbedderTest, + InsertTextInEmptyCharLimitTextFieldOverflow) { + // Click on the textfield. +@@ -2056,6 +2737,30 @@ TEST_F(FPDFFormFillTextFormEmbedderTest, DoubleClickInTextField) { + CheckSelection(L"Hello World"); + } + ++TEST_F(FPDFFormFillTextFormEmbedderTest, FocusAnnotationUpdateToEmbedder) { ++ testing::NiceMock mock; ++ SetDelegate(&mock); ++ CheckFocusedFieldText(L""); ++ ++#ifdef PDF_ENABLE_XFA ++ EXPECT_CALL(mock, OnFocusChange(_, _, 0)).Times(1); ++#else // PDF_ENABLE_XFA ++ EXPECT_CALL(mock, OnFocusChange(_, _, 0)).Times(0); ++#endif // PDF_ENABLE_XFA ++ ++ ClickOnFormFieldAtPoint(RegularFormBegin()); ++} ++ ++TEST_F(FPDFFormFillTextFormEmbedderTestVersion2, ++ FocusAnnotationUpdateToEmbedder) { ++ testing::NiceMock mock; ++ SetDelegate(&mock); ++ CheckFocusedFieldText(L""); ++ ++ EXPECT_CALL(mock, OnFocusChange(_, _, 0)).Times(1); ++ ClickOnFormFieldAtPoint(RegularFormBegin()); ++} ++ + TEST_F(FPDFFormFillTextFormEmbedderTest, FocusChanges) { + static const CFX_PointF kNonFormPoint(1, 1); + CheckFocusedFieldText(L""); +@@ -2428,13 +3133,32 @@ TEST_F(FPDFFormFillListBoxFormEmbedderTest, + CheckFocusedFieldText(L"Banana"); + } + +-TEST_F(FPDFFormFillListBoxFormEmbedderTest, CheckIfMultipleSelected) { ++TEST_F(FPDFFormFillListBoxFormEmbedderTest, CheckIfMultipleSelectedIndices) { ++ // Multiselect field set to 'Belgium' (index 1) and 'Denmark' (index 3) upon ++ // opening. ++ FocusOnMultiSelectMultipleIndicesForm(); ++ for (int i = 0; i < 5; i++) { ++ bool expected = (i == 1 || i == 3); ++ CheckIsIndexSelected(i, expected); ++ } ++} ++ ++TEST_F(FPDFFormFillListBoxFormEmbedderTest, CheckIfMultipleSelectedValues) { + // Multiselect field set to 'Gamma' (index 2) and 'Epsilon' (index 4) upon + // opening. +- FocusOnMultiSelectMultipleSelectedForm(); ++ FocusOnMultiSelectMultipleValuesForm(); + for (int i = 0; i < 5; i++) { +- // TODO(bug_1377): Should be selected at index 2 and index 4. +- bool expected = false; ++ bool expected = (i == 2 || i == 4); ++ CheckIsIndexSelected(i, expected); ++ } ++} ++ ++TEST_F(FPDFFormFillListBoxFormEmbedderTest, CheckIfMultipleSelectedMismatch) { ++ // Multiselect field set to 'Alligator' (index 0) and 'Cougar' (index 2) upon ++ // opening. ++ FocusOnMultiSelectMultipleMismatchForm(); ++ for (int i = 0; i < 5; i++) { ++ bool expected = (i == 0 || i == 2); + CheckIsIndexSelected(i, expected); + } + } +@@ -2447,7 +3171,7 @@ TEST_F(FPDFFormFillListBoxFormEmbedderTest, + // TODO(bug_1377): Behavior should be changed to the one described below. + // The top visible option is 'Gamma' (index 2), so the first selection should + // not change. The second selection, 'Epsilon,' should be deselected. +- ClickOnMultiSelectMultipleSelectedFormOption(0); ++ ClickOnMultiSelectMultipleValuesFormOption(0); + for (int i = 0; i < 5; i++) { + bool expected = i == 0; + CheckIsIndexSelected(i, expected); +@@ -2473,6 +3197,39 @@ TEST_F(FPDFFormFillListBoxFormEmbedderTest, CheckForNoOverscroll) { + } + } + ++TEST_F(FPDFFormFillTextFormEmbedderTest, ReplaceAndKeepSelection) { ++ ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"XYZ"); ++ ClickOnFormFieldAtPoint(RegularFormBegin()); ++ CheckCanUndo(false); ++ CheckCanRedo(false); ++ ++ TypeTextIntoTextField(2, RegularFormBegin()); ++ CheckFocusedFieldText(L"AB"); ++ CheckSelection(L""); ++ SelectTextWithKeyboard(1, FWL_VKEY_Right, RegularFormBegin()); ++ CheckSelection(L"A"); ++ ++ FORM_ReplaceAndKeepSelection(form_handle(), page(), text_to_insert.get()); ++ CheckFocusedFieldText(L"XYZB"); ++ CheckSelection(L"XYZ"); ++ CheckCanUndo(true); ++ CheckCanRedo(false); ++ ++ PerformUndo(); ++ CheckFocusedFieldText(L"AB"); ++ CheckCanUndo(true); ++ CheckCanRedo(true); ++ ++ SelectTextWithKeyboard(1, FWL_VKEY_Left, RegularFormEnd()); ++ CheckSelection(L"B"); ++ ++ FORM_ReplaceAndKeepSelection(form_handle(), page(), text_to_insert.get()); ++ CheckFocusedFieldText(L"AXYZ"); ++ CheckSelection(L"XYZ"); ++ CheckCanUndo(true); ++ CheckCanRedo(false); ++} ++ + TEST_F(FPDFFormFillTextFormEmbedderTest, ReplaceSelection) { + ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"XYZ"); + ClickOnFormFieldAtPoint(RegularFormBegin()); +@@ -2520,3 +3277,247 @@ TEST_F(FPDFFormFillTextFormEmbedderTest, ReplaceSelection) { + CheckCanUndo(true); + CheckCanRedo(false); + } ++ ++TEST_F(FPDFFormFillTextFormEmbedderTest, SelectAllWithKeyboardShortcut) { ++ // Start with a couple of letters in the text form. ++ TypeTextIntoTextField(2, RegularFormBegin()); ++ CheckFocusedFieldText(L"AB"); ++ CheckSelection(L""); ++ ++ // Select all with the keyboard shortcut. ++#if BUILDFLAG(IS_APPLE) ++ constexpr int kCorrectModifier = FWL_EVENTFLAG_MetaKey; ++#else ++ constexpr int kCorrectModifier = FWL_EVENTFLAG_ControlKey; ++#endif ++ FORM_OnChar(form_handle(), page(), pdfium::ascii::kControlA, ++ kCorrectModifier); ++ CheckSelection(L"AB"); ++ ++ // Reset the selection again. ++ ClickOnFormFieldAtPoint(RegularFormBegin()); ++ CheckSelection(L""); ++ ++ // Select all with the keyboard shortcut using the wrong modifier key. ++#if BUILDFLAG(IS_APPLE) ++ constexpr int kWrongModifier = FWL_EVENTFLAG_ControlKey; ++#else ++ constexpr int kWrongModifier = FWL_EVENTFLAG_MetaKey; ++#endif ++ FORM_OnChar(form_handle(), page(), pdfium::ascii::kControlA, kWrongModifier); ++ CheckSelection(L""); ++} ++ ++class FPDFXFAFormBug1055869EmbedderTest ++ : public FPDFFormFillInteractiveEmbedderTest { ++ protected: ++ FPDFXFAFormBug1055869EmbedderTest() = default; ++ ~FPDFXFAFormBug1055869EmbedderTest() override = default; ++ ++ const char* GetDocumentName() const override { return "bug_1055869.pdf"; } ++ int GetFormType() const override { return FORMTYPE_XFA_FULL; } ++}; ++ ++TEST_F(FPDFXFAFormBug1055869EmbedderTest, Paste) { ++ ScopedFPDFWideString text_to_insert = GetFPDFWideString(L"XYZ"); ++ DoubleClickOnFormFieldAtPoint(CFX_PointF(100, 100)); ++ FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get()); ++} ++ ++class FPDFXFAFormBug1058653EmbedderTest ++ : public FPDFFormFillInteractiveEmbedderTest { ++ protected: ++ FPDFXFAFormBug1058653EmbedderTest() = default; ++ ~FPDFXFAFormBug1058653EmbedderTest() override = default; ++ ++ const char* GetDocumentName() const override { return "bug_1058653.pdf"; } ++ int GetFormType() const override { return FORMTYPE_XFA_FULL; } ++}; ++ ++TEST_F(FPDFXFAFormBug1058653EmbedderTest, Paste) { ++ ScopedFPDFWideString text_to_insert = GetFPDFWideString(L""); ++ DoubleClickOnFormFieldAtPoint(CFX_PointF(22, 22)); ++ FORM_ReplaceSelection(form_handle(), page(), text_to_insert.get()); ++} ++ ++class FPDFFormFillActionUriTest : public EmbedderTest { ++ protected: ++ FPDFFormFillActionUriTest() = default; ++ ~FPDFFormFillActionUriTest() override = default; ++ ++ void SetUp() override { ++ EmbedderTest::SetUp(); ++ ASSERT_TRUE(OpenDocument("annots_action_handling.pdf")); ++ page_ = LoadPage(0); ++ ASSERT_TRUE(page_); ++ ++ // Set Widget and Link as supported tabbable annots. ++ constexpr FPDF_ANNOTATION_SUBTYPE kFocusableSubtypes[] = {FPDF_ANNOT_WIDGET, ++ FPDF_ANNOT_LINK}; ++ constexpr size_t kSubtypeCount = std::size(kFocusableSubtypes); ++ ASSERT_TRUE(FPDFAnnot_SetFocusableSubtypes( ++ form_handle(), kFocusableSubtypes, kSubtypeCount)); ++ } ++ ++ void TearDown() override { ++ UnloadPage(page_); ++ EmbedderTest::TearDown(); ++ } ++ ++ void SetFocusOnNthAnnot(size_t n) { ++ DCHECK_NE(n, 0); ++ // Setting focus on first annot. ++ FORM_OnMouseMove(form_handle(), page(), /*modifier=*/0, 100, 680); ++ FORM_OnLButtonDown(form_handle(), page(), /*modifier=*/0, 100, 680); ++ FORM_OnLButtonUp(form_handle(), page(), /*modifier=*/0, 100, 680); ++ for (size_t i = 1; i < n; i++) ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Tab, 0)); ++ } ++ ++ FPDF_PAGE page() { return page_; } ++ ++ private: ++ FPDF_PAGE page_ = nullptr; ++}; ++ ++TEST_F(FPDFFormFillActionUriTest, ButtonActionInvokeTest) { ++ NiceMock mock; ++ // TODO(crbug.com/1028991): DoURIAction expect call should be 1. ++ EXPECT_CALL(mock, DoURIAction(_)).Times(0); ++ SetDelegate(&mock); ++ ++ SetFocusOnNthAnnot(1); ++ ++ // Tab once from first form to go to button widget. ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Tab, 0)); ++ ++ // TODO(crbug.com/1028991): Following should be changed to ASSERT_TRUE after ++ // handling key press implementation on buttons. ++ ASSERT_FALSE(FORM_OnChar(form_handle(), page(), pdfium::ascii::kReturn, 0)); ++} ++ ++TEST_F(FPDFFormFillActionUriTest, LinkActionInvokeTest) { ++ NiceMock mock; ++ { ++ InSequence sequence; ++ const char kExpectedUri[] = "https://cs.chromium.org/"; ++#ifdef PDF_ENABLE_XFA ++ EXPECT_CALL(mock, ++ DoURIActionWithKeyboardModifier(_, StrEq(kExpectedUri), _)) ++ .Times(4); ++#else // PDF_ENABLE_XFA ++ EXPECT_CALL(mock, DoURIAction(StrEq(kExpectedUri))).Times(4); ++ EXPECT_CALL(mock, DoURIActionWithKeyboardModifier(_, _, _)).Times(0); ++#endif // PDF_ENABLE_XFA ++ } ++ SetDelegate(&mock); ++ SetFocusOnNthAnnot(3); ++ int modifier = 0; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ modifier = FWL_EVENTFLAG_ControlKey; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ modifier = FWL_EVENTFLAG_ShiftKey; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ modifier |= FWL_EVENTFLAG_ControlKey; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ ++ ASSERT_FALSE(FORM_OnKeyDown(nullptr, page(), FWL_VKEY_Return, modifier)); ++ ASSERT_FALSE( ++ FORM_OnKeyDown(form_handle(), nullptr, FWL_VKEY_Return, modifier)); ++ // Following checks should be changed to ASSERT_TRUE if FORM_OnKeyDown starts ++ // handling for Shift/Space/Control. ++ ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Shift, modifier)); ++ ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Space, modifier)); ++ ASSERT_FALSE( ++ FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Control, modifier)); ++} ++ ++TEST_F(FPDFFormFillActionUriTest, InternalLinkActionInvokeTest) { ++ NiceMock mock; ++ EXPECT_CALL(mock, DoGoToAction(_, _, 1, _, _)).Times(12); ++ SetDelegate(&mock); ++ ++ SetFocusOnNthAnnot(4); ++ int modifier = 0; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ modifier = FWL_EVENTFLAG_ControlKey; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ modifier = FWL_EVENTFLAG_ShiftKey; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ modifier |= FWL_EVENTFLAG_ControlKey; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ ++ SetFocusOnNthAnnot(5); ++ modifier = 0; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ modifier = FWL_EVENTFLAG_ControlKey; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ modifier = FWL_EVENTFLAG_ShiftKey; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ modifier |= FWL_EVENTFLAG_ControlKey; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ ++ SetFocusOnNthAnnot(6); ++ modifier = 0; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ modifier = FWL_EVENTFLAG_ControlKey; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ modifier = FWL_EVENTFLAG_ShiftKey; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ modifier |= FWL_EVENTFLAG_ControlKey; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ ++ ASSERT_FALSE(FORM_OnKeyDown(nullptr, page(), FWL_VKEY_Return, modifier)); ++ ASSERT_FALSE( ++ FORM_OnKeyDown(form_handle(), nullptr, FWL_VKEY_Return, modifier)); ++ // Following checks should be changed to ASSERT_TRUE if FORM_OnKeyDown starts ++ // handling for Shift/Space/Control. ++ ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Shift, modifier)); ++ ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Space, modifier)); ++ ASSERT_FALSE( ++ FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Control, modifier)); ++} ++ ++class FPDFFormFillActionUriTestVersion2 : public FPDFFormFillActionUriTest { ++ void SetUp() override { ++ SetFormFillInfoVersion(2); ++ FPDFFormFillActionUriTest::SetUp(); ++ } ++}; ++ ++TEST_F(FPDFFormFillActionUriTestVersion2, LinkActionInvokeTest) { ++ NiceMock mock; ++ { ++ InSequence sequence; ++ EXPECT_CALL(mock, DoURIAction(_)).Times(0); ++ const char kExpectedUri[] = "https://cs.chromium.org/"; ++ EXPECT_CALL(mock, ++ DoURIActionWithKeyboardModifier(_, StrEq(kExpectedUri), 0)); ++ EXPECT_CALL(mock, DoURIActionWithKeyboardModifier( ++ _, StrEq(kExpectedUri), FWL_EVENTFLAG_ControlKey)); ++ EXPECT_CALL(mock, DoURIActionWithKeyboardModifier(_, StrEq(kExpectedUri), ++ FWL_EVENTFLAG_ShiftKey)); ++ EXPECT_CALL(mock, ++ DoURIActionWithKeyboardModifier(_, StrEq(kExpectedUri), 3)); ++ } ++ SetDelegate(&mock); ++ SetFocusOnNthAnnot(3); ++ int modifier = 0; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ modifier = FWL_EVENTFLAG_ControlKey; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ modifier = FWL_EVENTFLAG_ShiftKey; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ modifier |= FWL_EVENTFLAG_ControlKey; ++ ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier)); ++ ++ ASSERT_FALSE(FORM_OnKeyDown(nullptr, page(), FWL_VKEY_Return, modifier)); ++ ASSERT_FALSE( ++ FORM_OnKeyDown(form_handle(), nullptr, FWL_VKEY_Return, modifier)); ++ // Following checks should be changed to ASSERT_TRUE if FORM_OnKeyDown starts ++ // handling for Shift/Space/Control. ++ ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Shift, modifier)); ++ ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Space, modifier)); ++ ASSERT_FALSE( ++ FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Control, modifier)); ++} +diff --git a/fpdfsdk/fpdf_javascript.cpp b/fpdfsdk/fpdf_javascript.cpp +index c2d119bb2..ed91ac696 100644 +--- a/fpdfsdk/fpdf_javascript.cpp ++++ b/fpdfsdk/fpdf_javascript.cpp +@@ -1,17 +1,18 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "public/fpdf_javascript.h" + + #include ++#include + + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfdoc/cpdf_action.h" + #include "core/fpdfdoc/cpdf_nametree.h" + #include "fpdfsdk/cpdfsdk_helpers.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/numerics/safe_conversions.h" + + struct CPDF_JavaScript { + WideString name; +@@ -21,7 +22,11 @@ struct CPDF_JavaScript { + FPDF_EXPORT int FPDF_CALLCONV + FPDFDoc_GetJavaScriptActionCount(FPDF_DOCUMENT document) { + CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document); +- return doc ? CPDF_NameTree(doc, "JavaScript").GetCount() : -1; ++ if (!doc) ++ return -1; ++ ++ auto name_tree = CPDF_NameTree::Create(doc, "JavaScript"); ++ return name_tree ? pdfium::base::checked_cast(name_tree->GetCount()) : 0; + } + + FPDF_EXPORT FPDF_JAVASCRIPT_ACTION FPDF_CALLCONV +@@ -30,26 +35,26 @@ FPDFDoc_GetJavaScriptAction(FPDF_DOCUMENT document, int index) { + if (!doc || index < 0) + return nullptr; + +- CPDF_NameTree name_tree(doc, "JavaScript"); +- if (static_cast(index) >= name_tree.GetCount()) ++ auto name_tree = CPDF_NameTree::Create(doc, "JavaScript"); ++ if (!name_tree || static_cast(index) >= name_tree->GetCount()) + return nullptr; + + WideString name; +- CPDF_Dictionary* obj = +- ToDictionary(name_tree.LookupValueAndName(index, &name)); ++ RetainPtr obj = ++ ToDictionary(name_tree->LookupValueAndName(index, &name)); + if (!obj) + return nullptr; + + // Validate |obj|. Type is optional, but must be valid if present. +- CPDF_Action action(obj); +- if (action.GetType() != CPDF_Action::JavaScript) ++ CPDF_Action action(std::move(obj)); ++ if (action.GetType() != CPDF_Action::Type::kJavaScript) + return nullptr; + +- Optional script = action.MaybeGetJavaScript(); ++ absl::optional script = action.MaybeGetJavaScript(); + if (!script.has_value()) + return nullptr; + +- auto js = pdfium::MakeUnique(); ++ auto js = std::make_unique(); + js->name = name; + js->script = script.value(); + return FPDFJavaScriptActionFromCPDFJavaScriptAction(js.release()); +diff --git a/fpdfsdk/fpdf_javascript_embeddertest.cpp b/fpdfsdk/fpdf_javascript_embeddertest.cpp +index 35deb6979..9a67f0220 100644 +--- a/fpdfsdk/fpdf_javascript_embeddertest.cpp ++++ b/fpdfsdk/fpdf_javascript_embeddertest.cpp +@@ -1,9 +1,7 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include +-#include + #include + + #include "core/fxcrt/fx_memory.h" +@@ -32,12 +30,12 @@ TEST_F(FPDFJavaScriptEmbedderTest, CountNoJS) { + + TEST_F(FPDFJavaScriptEmbedderTest, GetJS) { + ASSERT_TRUE(OpenDocument("js.pdf")); +- EXPECT_EQ(6, FPDFDoc_GetJavaScriptActionCount(document())); ++ EXPECT_EQ(5, FPDFDoc_GetJavaScriptActionCount(document())); + + ScopedFPDFJavaScriptAction js; + js.reset(FPDFDoc_GetJavaScriptAction(document(), -1)); + EXPECT_FALSE(js); +- js.reset(FPDFDoc_GetJavaScriptAction(document(), 6)); ++ js.reset(FPDFDoc_GetJavaScriptAction(document(), 5)); + EXPECT_FALSE(js); + js.reset(FPDFDoc_GetJavaScriptAction(nullptr, -1)); + EXPECT_FALSE(js); +@@ -49,21 +47,17 @@ TEST_F(FPDFJavaScriptEmbedderTest, GetJS) { + EXPECT_FALSE(js); + js.reset(FPDFDoc_GetJavaScriptAction(nullptr, 5)); + EXPECT_FALSE(js); +- js.reset(FPDFDoc_GetJavaScriptAction(nullptr, 6)); +- EXPECT_FALSE(js); + + js.reset(FPDFDoc_GetJavaScriptAction(document(), 0)); + EXPECT_TRUE(js); + js.reset(FPDFDoc_GetJavaScriptAction(document(), 1)); + EXPECT_TRUE(js); + js.reset(FPDFDoc_GetJavaScriptAction(document(), 2)); +- EXPECT_TRUE(js); ++ EXPECT_FALSE(js); + js.reset(FPDFDoc_GetJavaScriptAction(document(), 3)); + EXPECT_FALSE(js); + js.reset(FPDFDoc_GetJavaScriptAction(document(), 4)); + EXPECT_FALSE(js); +- js.reset(FPDFDoc_GetJavaScriptAction(document(), 5)); +- EXPECT_FALSE(js); + } + + TEST_F(FPDFJavaScriptEmbedderTest, GetJSName) { +diff --git a/fpdfsdk/fpdf_ppo.cpp b/fpdfsdk/fpdf_ppo.cpp +index 819ba05c5..f1fde2290 100644 +--- a/fpdfsdk/fpdf_ppo.cpp ++++ b/fpdfsdk/fpdf_ppo.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,11 +9,16 @@ + #include + #include + #include ++#include ++#include + #include + #include + + #include "constants/page_object.h" ++#include "core/fpdfapi/page/cpdf_form.h" ++#include "core/fpdfapi/page/cpdf_formobject.h" + #include "core/fpdfapi/page/cpdf_page.h" ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" + #include "core/fpdfapi/page/cpdf_pageobject.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" +@@ -25,13 +30,20 @@ + #include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfapi/parser/cpdf_stream_acc.h" + #include "core/fpdfapi/parser/cpdf_string.h" +-#include "core/fpdfapi/render/cpdf_pagerendercache.h" ++#include "core/fpdfapi/parser/fpdf_parser_utility.h" + #include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/fx_string_wrappers.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "public/cpp/fpdf_scopers.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/span.h" ++ ++struct XObjectContext { ++ UnownedPtr dest_doc; ++ RetainPtr xobject; ++}; + + namespace { + +@@ -40,7 +52,7 @@ namespace { + // scaled down, and scale is in range of (0, 1) exclusive. + struct NupPageSettings { + CFX_PointF subPageStartPoint; +- float scale; ++ float scale = 0.0f; + }; + + // Calculates the N-up parameters. When importing multiple pages into one page. +@@ -87,10 +99,10 @@ NupState::NupState(const CFX_SizeF& pagesize, + m_nPagesOnXAxis(nPagesOnXAxis), + m_nPagesOnYAxis(nPagesOnYAxis), + m_nPagesPerSheet(nPagesOnXAxis * nPagesOnYAxis) { +- ASSERT(m_nPagesOnXAxis > 0); +- ASSERT(m_nPagesOnYAxis > 0); +- ASSERT(m_destPageSize.width > 0); +- ASSERT(m_destPageSize.height > 0); ++ DCHECK(m_nPagesOnXAxis > 0); ++ DCHECK(m_nPagesOnYAxis > 0); ++ DCHECK(m_destPageSize.width > 0); ++ DCHECK(m_destPageSize.height > 0); + + m_subPageSize.width = m_destPageSize.width / m_nPagesOnXAxis; + m_subPageSize.height = m_destPageSize.height / m_nPagesOnYAxis; +@@ -137,8 +149,9 @@ NupPageSettings NupState::CalculateNewPagePosition(const CFX_SizeF& pagesize) { + return CalculatePageEdit(iSubX, iSubY, pagesize); + } + +-const CPDF_Object* PageDictGetInheritableTag(const CPDF_Dictionary* pDict, +- const ByteString& bsSrcTag) { ++RetainPtr PageDictGetInheritableTag( ++ RetainPtr pDict, ++ const ByteString& bsSrcTag) { + if (!pDict || bsSrcTag.IsEmpty()) + return nullptr; + if (!pDict->KeyExist(pdfium::page_object::kParent) || +@@ -146,14 +159,12 @@ const CPDF_Object* PageDictGetInheritableTag(const CPDF_Dictionary* pDict, + return nullptr; + } + +- const CPDF_Object* pType = +- pDict->GetObjectFor(pdfium::page_object::kType)->GetDirect(); +- if (!ToName(pType)) +- return nullptr; +- if (pType->GetString().Compare("Page")) ++ RetainPtr pName = ++ ToName(pDict->GetObjectFor(pdfium::page_object::kType)->GetDirect()); ++ if (!pName || pName->GetString() != "Page") + return nullptr; + +- const CPDF_Dictionary* pp = ToDictionary( ++ RetainPtr pp = ToDictionary( + pDict->GetObjectFor(pdfium::page_object::kParent)->GetDirect()); + if (!pp) + return nullptr; +@@ -172,29 +183,14 @@ const CPDF_Object* PageDictGetInheritableTag(const CPDF_Dictionary* pDict, + return nullptr; + } + +-CFX_FloatRect GetMediaBox(const CPDF_Dictionary* pPageDict) { +- const CPDF_Object* pMediaBox = +- PageDictGetInheritableTag(pPageDict, pdfium::page_object::kMediaBox); +- const CPDF_Array* pArray = ToArray(pMediaBox->GetDirect()); +- if (!pArray) +- return CFX_FloatRect(); +- return pArray->GetRect(); +-} +- +-CFX_FloatRect GetCropBox(const CPDF_Dictionary* pPageDict) { +- if (pPageDict->KeyExist(pdfium::page_object::kCropBox)) +- return pPageDict->GetRectFor(pdfium::page_object::kCropBox); +- return GetMediaBox(pPageDict); +-} +- +-bool CopyInheritable(CPDF_Dictionary* pDestPageDict, +- const CPDF_Dictionary* pSrcPageDict, ++bool CopyInheritable(RetainPtr pDestPageDict, ++ RetainPtr pSrcPageDict, + const ByteString& key) { + if (pDestPageDict->KeyExist(key)) + return true; + +- const CPDF_Object* pInheritable = +- PageDictGetInheritableTag(pSrcPageDict, key); ++ RetainPtr pInheritable = ++ PageDictGetInheritableTag(std::move(pSrcPageDict), key); + if (!pInheritable) + return false; + +@@ -202,74 +198,15 @@ bool CopyInheritable(CPDF_Dictionary* pDestPageDict, + return true; + } + +-bool ParsePageRangeString(const ByteString& bsPageRange, +- uint32_t nCount, +- std::vector* pageArray) { +- ByteString bsStrippedPageRange = bsPageRange; +- bsStrippedPageRange.Remove(' '); +- size_t nLength = bsStrippedPageRange.GetLength(); +- if (nLength == 0) +- return true; +- +- static const ByteString cbCompareString("0123456789-,"); +- for (size_t i = 0; i < nLength; ++i) { +- if (!cbCompareString.Contains(bsStrippedPageRange[i])) +- return false; +- } +- +- ByteString cbMidRange; +- size_t nStringFrom = 0; +- size_t nStringTo = 0; +- while (nStringTo < nLength) { +- nStringTo = bsStrippedPageRange.Find(',', nStringFrom).value_or(nLength); +- cbMidRange = +- bsStrippedPageRange.Substr(nStringFrom, nStringTo - nStringFrom); +- Optional nDashPosition = cbMidRange.Find('-'); +- if (nDashPosition) { +- size_t nMid = nDashPosition.value(); +- uint32_t nStartPageNum = pdfium::base::checked_cast( +- atoi(cbMidRange.First(nMid).c_str())); +- if (nStartPageNum == 0) +- return false; +- +- ++nMid; +- size_t nEnd = cbMidRange.GetLength() - nMid; +- if (nEnd == 0) +- return false; +- +- uint32_t nEndPageNum = pdfium::base::checked_cast( +- atoi(cbMidRange.Substr(nMid, nEnd).c_str())); +- if (nStartPageNum < 0 || nStartPageNum > nEndPageNum || +- nEndPageNum > nCount) { +- return false; +- } +- for (uint32_t i = nStartPageNum; i <= nEndPageNum; ++i) { +- pageArray->push_back(i); +- } +- } else { +- uint32_t nPageNum = +- pdfium::base::checked_cast(atoi(cbMidRange.c_str())); +- if (nPageNum <= 0 || nPageNum > nCount) +- return false; +- pageArray->push_back(nPageNum); +- } +- nStringFrom = nStringTo + 1; +- } +- return true; +-} +- +-std::vector GetPageNumbers(const CPDF_Document& doc, ++std::vector GetPageIndices(const CPDF_Document& doc, + const ByteString& bsPageRange) { +- std::vector page_numbers; + uint32_t nCount = doc.GetPageCount(); +- if (bsPageRange.IsEmpty()) { +- for (uint32_t i = 1; i <= nCount; ++i) +- page_numbers.push_back(i); +- } else { +- if (!ParsePageRangeString(bsPageRange, nCount, &page_numbers)) +- page_numbers.clear(); +- } +- return page_numbers; ++ if (!bsPageRange.IsEmpty()) ++ return ParsePageRangeString(bsPageRange, nCount); ++ ++ std::vector page_indices(nCount); ++ std::iota(page_indices.begin(), page_indices.end(), 0); ++ return page_indices; + } + + class CPDF_PageOrganizer { +@@ -280,13 +217,13 @@ class CPDF_PageOrganizer { + // Must be called after construction before doing anything else. + bool Init(); + +- bool UpdateReference(CPDF_Object* pObj); ++ bool UpdateReference(RetainPtr pObj); + +- CPDF_Document* dest() { return m_pDestDoc.Get(); } +- const CPDF_Document* dest() const { return m_pDestDoc.Get(); } ++ CPDF_Document* dest() { return m_pDestDoc; } ++ const CPDF_Document* dest() const { return m_pDestDoc; } + +- CPDF_Document* src() { return m_pSrcDoc.Get(); } +- const CPDF_Document* src() const { return m_pSrcDoc.Get(); } ++ CPDF_Document* src() { return m_pSrcDoc; } ++ const CPDF_Document* src() const { return m_pSrcDoc; } + + void AddObjectMapping(uint32_t dwOldPageObj, uint32_t dwNewPageObj) { + m_ObjectNumberMap[dwOldPageObj] = dwNewPageObj; +@@ -311,37 +248,37 @@ CPDF_PageOrganizer::CPDF_PageOrganizer(CPDF_Document* pDestDoc, + CPDF_PageOrganizer::~CPDF_PageOrganizer() = default; + + bool CPDF_PageOrganizer::Init() { +- ASSERT(m_pDestDoc); +- ASSERT(m_pSrcDoc); ++ DCHECK(m_pDestDoc); ++ DCHECK(m_pSrcDoc); + +- CPDF_Dictionary* pNewRoot = dest()->GetRoot(); ++ RetainPtr pNewRoot = dest()->GetMutableRoot(); + if (!pNewRoot) + return false; + +- CPDF_Dictionary* pDocInfoDict = dest()->GetInfo(); ++ RetainPtr pDocInfoDict = dest()->GetInfo(); + if (!pDocInfoDict) + return false; + + pDocInfoDict->SetNewFor("Producer", "PDFium", false); + +- ByteString cbRootType = pNewRoot->GetStringFor("Type", ByteString()); ++ ByteString cbRootType = pNewRoot->GetByteStringFor("Type", ByteString()); + if (cbRootType.IsEmpty()) + pNewRoot->SetNewFor("Type", "Catalog"); + +- CPDF_Object* pElement = pNewRoot->GetObjectFor("Pages"); +- CPDF_Dictionary* pNewPages = +- pElement ? ToDictionary(pElement->GetDirect()) : nullptr; ++ RetainPtr pElement = pNewRoot->GetMutableObjectFor("Pages"); ++ RetainPtr pNewPages = ++ pElement ? ToDictionary(pElement->GetMutableDirect()) : nullptr; + if (!pNewPages) { + pNewPages = dest()->NewIndirect(); + pNewRoot->SetNewFor("Pages", dest(), + pNewPages->GetObjNum()); + } +- ByteString cbPageType = pNewPages->GetStringFor("Type", ByteString()); ++ ByteString cbPageType = pNewPages->GetByteStringFor("Type", ByteString()); + if (cbPageType.IsEmpty()) + pNewPages->SetNewFor("Type", "Pages"); + + if (!pNewPages->GetArrayFor("Kids")) { +- auto* pNewArray = dest()->NewIndirect(); ++ auto pNewArray = dest()->NewIndirect(); + pNewPages->SetNewFor("Count", 0); + pNewPages->SetNewFor("Kids", dest(), + pNewArray->GetObjNum()); +@@ -349,10 +286,10 @@ bool CPDF_PageOrganizer::Init() { + return true; + } + +-bool CPDF_PageOrganizer::UpdateReference(CPDF_Object* pObj) { ++bool CPDF_PageOrganizer::UpdateReference(RetainPtr pObj) { + switch (pObj->GetType()) { + case CPDF_Object::kReference: { +- CPDF_Reference* pReference = pObj->AsReference(); ++ CPDF_Reference* pReference = pObj->AsMutableReference(); + uint32_t newobjnum = GetNewObjId(pReference); + if (newobjnum == 0) + return false; +@@ -360,7 +297,7 @@ bool CPDF_PageOrganizer::UpdateReference(CPDF_Object* pObj) { + return true; + } + case CPDF_Object::kDictionary: { +- CPDF_Dictionary* pDict = pObj->AsDictionary(); ++ CPDF_Dictionary* pDict = pObj->AsMutableDictionary(); + std::vector bad_keys; + { + CPDF_DictionaryLocker locker(pDict); +@@ -368,30 +305,27 @@ bool CPDF_PageOrganizer::UpdateReference(CPDF_Object* pObj) { + const ByteString& key = it.first; + if (key == "Parent" || key == "Prev" || key == "First") + continue; +- CPDF_Object* pNextObj = it.second.Get(); +- if (!pNextObj) +- return false; ++ RetainPtr pNextObj = it.second; + if (!UpdateReference(pNextObj)) + bad_keys.push_back(key); + } + } + for (const auto& key : bad_keys) +- pDict->RemoveFor(key); ++ pDict->RemoveFor(key.AsStringView()); + return true; + } + case CPDF_Object::kArray: { +- CPDF_Array* pArray = pObj->AsArray(); ++ CPDF_Array* pArray = pObj->AsMutableArray(); + for (size_t i = 0; i < pArray->size(); ++i) { +- CPDF_Object* pNextObj = pArray->GetObjectAt(i); +- if (!pNextObj || !UpdateReference(pNextObj)) ++ if (!UpdateReference(pArray->GetMutableObjectAt(i))) + return false; + } + return true; + } + case CPDF_Object::kStream: { +- CPDF_Stream* pStream = pObj->AsStream(); +- CPDF_Dictionary* pDict = pStream->GetDict(); +- return pDict && UpdateReference(pDict); ++ CPDF_Stream* pStream = pObj->AsMutableStream(); ++ RetainPtr pDict = pStream->GetMutableDict(); ++ return pDict && UpdateReference(std::move(pDict)); + } + default: + return true; +@@ -410,24 +344,23 @@ uint32_t CPDF_PageOrganizer::GetNewObjId(CPDF_Reference* pRef) { + if (dwNewObjNum) + return dwNewObjNum; + +- CPDF_Object* pDirect = pRef->GetDirect(); ++ RetainPtr pDirect = pRef->GetDirect(); + if (!pDirect) + return 0; + + RetainPtr pClone = pDirect->Clone(); +- if (CPDF_Dictionary* pDictClone = pClone->AsDictionary()) { +- if (pDictClone->KeyExist("Type")) { +- ByteString strType = pDictClone->GetStringFor("Type"); +- if (!FXSYS_stricmp(strType.c_str(), "Pages")) +- return 4; +- if (!FXSYS_stricmp(strType.c_str(), "Page")) +- return 0; +- } ++ const CPDF_Dictionary* pDictClone = pClone->AsDictionary(); ++ if (pDictClone && pDictClone->KeyExist("Type")) { ++ ByteString strType = pDictClone->GetByteStringFor("Type"); ++ if (strType.EqualNoCase("Pages")) ++ return 4; ++ if (strType.EqualNoCase("Page")) ++ return 0; + } +- CPDF_Object* pUnownedClone = dest()->AddIndirectObject(std::move(pClone)); +- dwNewObjNum = pUnownedClone->GetObjNum(); ++ ++ dwNewObjNum = dest()->AddIndirectObject(pClone); + AddObjectMapping(dwObjnum, dwNewObjNum); +- if (!UpdateReference(pUnownedClone)) ++ if (!UpdateReference(std::move(pClone))) + return 0; + + return dwNewObjNum; +@@ -440,11 +373,10 @@ class CPDF_PageExporter final : public CPDF_PageOrganizer { + CPDF_PageExporter(CPDF_Document* pDestDoc, CPDF_Document* pSrcDoc); + ~CPDF_PageExporter(); + +- // For the pages from the source document with |pageNums| as their page +- // numbers, insert them into the destination document at page |nIndex|. +- // |pageNums| is 1-based. +- // |nIndex| is 0-based. +- bool ExportPage(const std::vector& pageNums, int nIndex); ++ // For the pages from the source document with |pageIndices| as their page ++ // indices, insert them into the destination document at page |nIndex|. ++ // |pageIndices| and |nIndex| are 0-based. ++ bool ExportPage(pdfium::span pageIndices, int nIndex); + }; + + CPDF_PageExporter::CPDF_PageExporter(CPDF_Document* pDestDoc, +@@ -453,15 +385,16 @@ CPDF_PageExporter::CPDF_PageExporter(CPDF_Document* pDestDoc, + + CPDF_PageExporter::~CPDF_PageExporter() = default; + +-bool CPDF_PageExporter::ExportPage(const std::vector& pageNums, ++bool CPDF_PageExporter::ExportPage(pdfium::span pageIndices, + int nIndex) { + if (!Init()) + return false; + + int curpage = nIndex; +- for (size_t i = 0; i < pageNums.size(); ++i) { +- CPDF_Dictionary* pDestPageDict = dest()->CreateNewPage(curpage); +- auto* pSrcPageDict = src()->GetPageDictionary(pageNums[i] - 1); ++ for (uint32_t pageIndex : pageIndices) { ++ RetainPtr pDestPageDict = dest()->CreateNewPage(curpage); ++ RetainPtr pSrcPageDict = ++ src()->GetPageDictionary(pageIndex); + if (!pSrcPageDict || !pDestPageDict) + return false; + +@@ -469,12 +402,11 @@ bool CPDF_PageExporter::ExportPage(const std::vector& pageNums, + CPDF_DictionaryLocker locker(pSrcPageDict); + for (const auto& it : locker) { + const ByteString& cbSrcKeyStr = it.first; ++ const RetainPtr& pObj = it.second; + if (cbSrcKeyStr == pdfium::page_object::kType || + cbSrcKeyStr == pdfium::page_object::kParent) { + continue; + } +- +- CPDF_Object* pObj = it.second.Get(); + pDestPageDict->SetFor(cbSrcKeyStr, pObj->Clone()); + } + +@@ -486,7 +418,7 @@ bool CPDF_PageExporter::ExportPage(const std::vector& pageNums, + pdfium::page_object::kMediaBox)) { + // Search for "CropBox" in the source page dictionary. + // If it does not exist, use the default letter size. +- const CPDF_Object* pInheritable = PageDictGetInheritableTag( ++ RetainPtr pInheritable = PageDictGetInheritableTag( + pSrcPageDict, pdfium::page_object::kCropBox); + if (pInheritable) { + pDestPageDict->SetFor(pdfium::page_object::kMediaBox, +@@ -531,37 +463,43 @@ class CPDF_NPageToOneExporter final : public CPDF_PageOrganizer { + CPDF_NPageToOneExporter(CPDF_Document* pDestDoc, CPDF_Document* pSrcDoc); + ~CPDF_NPageToOneExporter(); + +- // For the pages from the source document with |pageNums| as their page +- // numbers, insert them into the destination document, starting at page 0. +- // |pageNums| is 1-based. ++ // For the pages from the source document with |pageIndices| as their page ++ // indices, insert them into the destination document, starting at page index ++ // 0. ++ // |pageIndices| is 0-based. + // |destPageSize| is the destination document page dimensions, measured in + // PDF "user space" units. + // |nPagesOnXAxis| and |nPagesOnXAxis| together defines how many source + // pages fit on one destination page. +- bool ExportNPagesToOne(const std::vector& pageNums, ++ bool ExportNPagesToOne(pdfium::span pageIndices, + const CFX_SizeF& destPageSize, + size_t nPagesOnXAxis, + size_t nPagesOnYAxis); + ++ std::unique_ptr CreateXObjectContextFromPage( ++ int src_page_index); ++ + private: + // Map page object number to XObject object name. + using PageXObjectMap = std::map; + +- // Creates an XObject from |pSrcPageDict|, or find an existing XObject that +- // represents |pSrcPageDict|. The transformation matrix is specified in ++ // Creates an XObject from |pSrcPage|, or find an existing XObject that ++ // represents |pSrcPage|. The transformation matrix is specified in + // |settings|. + // Returns the XObject reference surrounded by the transformation matrix. +- ByteString AddSubPage(const CPDF_Dictionary* pSrcPageDict, ++ ByteString AddSubPage(const RetainPtr& pSrcPage, + const NupPageSettings& settings); + +- // Creates an XObject from |pSrcPageDict|. Updates mapping as needed. ++ // Creates an XObject from |pSrcPage|. Updates mapping as needed. + // Returns the name of the newly created XObject. +- ByteString MakeXObjectFromPage(const CPDF_Dictionary* pSrcPageDict); ++ ByteString MakeXObjectFromPage(RetainPtr pSrcPage); ++ RetainPtr MakeXObjectFromPageRaw(RetainPtr pSrcPage); + + // Adds |bsContent| as the Contents key in |pDestPageDict|. + // Adds the objects in |m_XObjectNameToNumberMap| to the XObject dictionary in + // |pDestPageDict|'s Resources dictionary. +- void FinishPage(CPDF_Dictionary* pDestPageDict, const ByteString& bsContent); ++ void FinishPage(RetainPtr pDestPageDict, ++ const ByteString& bsContent); + + // Counter for giving new XObjects unique names. + uint32_t m_nObjectNumber = 0; +@@ -583,7 +521,7 @@ CPDF_NPageToOneExporter::CPDF_NPageToOneExporter(CPDF_Document* pDestDoc, + CPDF_NPageToOneExporter::~CPDF_NPageToOneExporter() = default; + + bool CPDF_NPageToOneExporter::ExportNPagesToOne( +- const std::vector& pageNums, ++ pdfium::span pageIndices, + const CFX_SizeF& destPageSize, + size_t nPagesOnXAxis, + size_t nPagesOnYAxis) { +@@ -600,33 +538,33 @@ bool CPDF_NPageToOneExporter::ExportNPagesToOne( + size_t nPagesPerSheet = nSafePagesPerSheet.ValueOrDie(); + NupState nupState(destPageSize, nPagesOnXAxis, nPagesOnYAxis); + +- size_t curpage = 0; ++ FX_SAFE_INT32 curpage = 0; + const CFX_FloatRect destPageRect(0, 0, destPageSize.width, + destPageSize.height); +- for (size_t iOuterPage = 0; iOuterPage < pageNums.size(); ++ for (size_t iOuterPage = 0; iOuterPage < pageIndices.size(); + iOuterPage += nPagesPerSheet) { + m_XObjectNameToNumberMap.clear(); + +- // Create a new page +- CPDF_Dictionary* pDestPageDict = dest()->CreateNewPage(curpage); ++ RetainPtr pDestPageDict = ++ dest()->CreateNewPage(curpage.ValueOrDie()); + if (!pDestPageDict) + return false; + + pDestPageDict->SetRectFor(pdfium::page_object::kMediaBox, destPageRect); + ByteString bsContent; + size_t iInnerPageMax = +- std::min(iOuterPage + nPagesPerSheet, pageNums.size()); ++ std::min(iOuterPage + nPagesPerSheet, pageIndices.size()); + for (size_t i = iOuterPage; i < iInnerPageMax; ++i) { +- auto* pSrcPageDict = src()->GetPageDictionary(pageNums[i] - 1); ++ RetainPtr pSrcPageDict = ++ src()->GetMutablePageDictionary(pageIndices[i]); + if (!pSrcPageDict) + return false; + + auto pSrcPage = pdfium::MakeRetain(src(), pSrcPageDict); +- pSrcPage->SetRenderCache( +- pdfium::MakeUnique(pSrcPage.Get())); ++ pSrcPage->AddPageImageCache(); + NupPageSettings settings = + nupState.CalculateNewPagePosition(pSrcPage->GetPageSize()); +- bsContent += AddSubPage(pSrcPageDict, settings); ++ bsContent += AddSubPage(pSrcPage, settings); + } + + FinishPage(pDestPageDict, bsContent); +@@ -637,19 +575,19 @@ bool CPDF_NPageToOneExporter::ExportNPagesToOne( + } + + ByteString CPDF_NPageToOneExporter::AddSubPage( +- const CPDF_Dictionary* pSrcPageDict, ++ const RetainPtr& pSrcPage, + const NupPageSettings& settings) { +- uint32_t dwSrcPageObjnum = pSrcPageDict->GetObjNum(); ++ uint32_t dwSrcPageObjnum = pSrcPage->GetDict()->GetObjNum(); + const auto it = m_SrcPageXObjectMap.find(dwSrcPageObjnum); + ByteString bsXObjectName = it != m_SrcPageXObjectMap.end() + ? it->second +- : MakeXObjectFromPage(pSrcPageDict); ++ : MakeXObjectFromPage(pSrcPage); + + CFX_Matrix matrix; + matrix.Scale(settings.scale, settings.scale); + matrix.Translate(settings.subPageStartPoint.x, settings.subPageStartPoint.y); + +- std::ostringstream contentStream; ++ fxcrt::ostringstream contentStream; + contentStream << "q\n" + << matrix.a << " " << matrix.b << " " << matrix.c << " " + << matrix.d << " " << matrix.e << " " << matrix.f << " cm\n" +@@ -657,16 +595,15 @@ ByteString CPDF_NPageToOneExporter::AddSubPage( + return ByteString(contentStream); + } + +-ByteString CPDF_NPageToOneExporter::MakeXObjectFromPage( +- const CPDF_Dictionary* pSrcPageDict) { +- ASSERT(pSrcPageDict); +- +- const CPDF_Object* pSrcContentObj = ++RetainPtr CPDF_NPageToOneExporter::MakeXObjectFromPageRaw( ++ RetainPtr pSrcPage) { ++ RetainPtr pSrcPageDict = pSrcPage->GetDict(); ++ RetainPtr pSrcContentObj = + pSrcPageDict->GetDirectObjectFor(pdfium::page_object::kContents); + +- CPDF_Stream* pNewXObject = dest()->NewIndirect( +- nullptr, 0, dest()->New()); +- CPDF_Dictionary* pNewXObjectDict = pNewXObject->GetDict(); ++ auto pNewXObject = ++ dest()->NewIndirect(dest()->New()); ++ RetainPtr pNewXObjectDict = pNewXObject->GetMutableDict(); + static const char kResourceString[] = "Resources"; + if (!CopyInheritable(pNewXObjectDict, pSrcPageDict, kResourceString)) { + // Use a default empty resources if it does not exist. +@@ -676,68 +613,135 @@ ByteString CPDF_NPageToOneExporter::MakeXObjectFromPage( + uint32_t dwNewXobjectObj = pNewXObjectDict->GetObjNum(); + AddObjectMapping(dwSrcPageObj, dwNewXobjectObj); + UpdateReference(pNewXObjectDict); +- + pNewXObjectDict->SetNewFor("Type", "XObject"); + pNewXObjectDict->SetNewFor("Subtype", "Form"); + pNewXObjectDict->SetNewFor("FormType", 1); +- pNewXObjectDict->SetRectFor("BBox", GetCropBox(pSrcPageDict)); +- // TODO(xlou): add matrix field to pNewXObjectDict. ++ pNewXObjectDict->SetRectFor("BBox", pSrcPage->GetBBox()); ++ pNewXObjectDict->SetMatrixFor("Matrix", pSrcPage->GetPageMatrix()); + + if (pSrcContentObj) { + ByteString bsSrcContentStream; +- const CPDF_Array* pSrcContentArray = ToArray(pSrcContentObj); ++ const CPDF_Array* pSrcContentArray = pSrcContentObj->AsArray(); + if (pSrcContentArray) { + for (size_t i = 0; i < pSrcContentArray->size(); ++i) { +- const CPDF_Stream* pStream = pSrcContentArray->GetStreamAt(i); +- auto pAcc = pdfium::MakeRetain(pStream); ++ RetainPtr pStream = pSrcContentArray->GetStreamAt(i); ++ auto pAcc = pdfium::MakeRetain(std::move(pStream)); + pAcc->LoadAllDataFiltered(); + bsSrcContentStream += ByteString(pAcc->GetSpan()); + bsSrcContentStream += "\n"; + } + } else { +- const CPDF_Stream* pStream = pSrcContentObj->AsStream(); +- auto pAcc = pdfium::MakeRetain(pStream); ++ RetainPtr pStream(pSrcContentObj->AsStream()); ++ auto pAcc = pdfium::MakeRetain(std::move(pStream)); + pAcc->LoadAllDataFiltered(); + bsSrcContentStream = ByteString(pAcc->GetSpan()); + } + pNewXObject->SetDataAndRemoveFilter(bsSrcContentStream.raw_span()); + } ++ return pNewXObject; ++} ++ ++ByteString CPDF_NPageToOneExporter::MakeXObjectFromPage( ++ RetainPtr pSrcPage) { ++ RetainPtr pNewXObject = MakeXObjectFromPageRaw(pSrcPage); + + // TODO(xlou): A better name schema to avoid possible object name collision. + ByteString bsXObjectName = ByteString::Format("X%d", ++m_nObjectNumber); + m_XObjectNameToNumberMap[bsXObjectName] = pNewXObject->GetObjNum(); +- m_SrcPageXObjectMap[pSrcPageDict->GetObjNum()] = bsXObjectName; ++ m_SrcPageXObjectMap[pSrcPage->GetDict()->GetObjNum()] = bsXObjectName; + return bsXObjectName; + } + +-void CPDF_NPageToOneExporter::FinishPage(CPDF_Dictionary* pDestPageDict, +- const ByteString& bsContent) { +- ASSERT(pDestPageDict); +- +- CPDF_Dictionary* pRes = +- pDestPageDict->GetDictFor(pdfium::page_object::kResources); +- if (!pRes) { +- pRes = pDestPageDict->SetNewFor( +- pdfium::page_object::kResources); +- } ++std::unique_ptr ++CPDF_NPageToOneExporter::CreateXObjectContextFromPage(int src_page_index) { ++ RetainPtr src_page_dict = ++ src()->GetMutablePageDictionary(src_page_index); ++ if (!src_page_dict) ++ return nullptr; + +- CPDF_Dictionary* pPageXObject = pRes->GetDictFor("XObject"); +- if (!pPageXObject) +- pPageXObject = pRes->SetNewFor("XObject"); ++ auto src_page = pdfium::MakeRetain(src(), src_page_dict); ++ auto xobject = std::make_unique(); ++ xobject->dest_doc = dest(); ++ xobject->xobject.Reset(MakeXObjectFromPageRaw(src_page)); ++ return xobject; ++} + ++void CPDF_NPageToOneExporter::FinishPage( ++ RetainPtr pDestPageDict, ++ const ByteString& bsContent) { ++ RetainPtr pRes = ++ pDestPageDict->GetOrCreateDictFor(pdfium::page_object::kResources); ++ RetainPtr pPageXObject = pRes->GetOrCreateDictFor("XObject"); + for (auto& it : m_XObjectNameToNumberMap) + pPageXObject->SetNewFor(it.first, dest(), it.second); + +- auto pDict = dest()->New(); +- CPDF_Stream* pStream = +- dest()->NewIndirect(nullptr, 0, std::move(pDict)); ++ auto pStream = ++ dest()->NewIndirect(dest()->New()); + pStream->SetData(bsContent.raw_span()); + pDestPageDict->SetNewFor(pdfium::page_object::kContents, + dest(), pStream->GetObjNum()); + } + ++// Make sure arrays only contain objects of basic types. ++bool IsValidViewerPreferencesArray(const CPDF_Array* array) { ++ CPDF_ArrayLocker locker(array); ++ for (const auto& obj : locker) { ++ if (obj->IsArray() || obj->IsDictionary() || obj->IsReference() || ++ obj->IsStream()) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool IsValidViewerPreferencesObject(const CPDF_Object* obj) { ++ // Per spec, there are no valid entries of these types. ++ if (obj->IsDictionary() || obj->IsNull() || obj->IsReference() || ++ obj->IsStream()) { ++ return false; ++ } ++ ++ const CPDF_Array* array = obj->AsArray(); ++ if (!array) { ++ return true; ++ } ++ ++ return IsValidViewerPreferencesArray(array); ++} ++ + } // namespace + ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDF_ImportPagesByIndex(FPDF_DOCUMENT dest_doc, ++ FPDF_DOCUMENT src_doc, ++ const int* page_indices, ++ unsigned long length, ++ int index) { ++ CPDF_Document* pDestDoc = CPDFDocumentFromFPDFDocument(dest_doc); ++ if (!dest_doc) ++ return false; ++ ++ CPDF_Document* pSrcDoc = CPDFDocumentFromFPDFDocument(src_doc); ++ if (!pSrcDoc) ++ return false; ++ ++ CPDF_PageExporter exporter(pDestDoc, pSrcDoc); ++ ++ if (!page_indices) { ++ std::vector page_indices_vec(pSrcDoc->GetPageCount()); ++ std::iota(page_indices_vec.begin(), page_indices_vec.end(), 0); ++ return exporter.ExportPage(page_indices_vec, index); ++ } ++ ++ if (length == 0) ++ return false; ++ ++ return exporter.ExportPage( ++ pdfium::make_span(reinterpret_cast(page_indices), ++ length), ++ index); ++} ++ + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_ImportPages(FPDF_DOCUMENT dest_doc, + FPDF_DOCUMENT src_doc, + FPDF_BYTESTRING pagerange, +@@ -750,12 +754,12 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_ImportPages(FPDF_DOCUMENT dest_doc, + if (!pSrcDoc) + return false; + +- std::vector page_numbers = GetPageNumbers(*pSrcDoc, pagerange); +- if (page_numbers.empty()) ++ std::vector page_indices = GetPageIndices(*pSrcDoc, pagerange); ++ if (page_indices.empty()) + return false; + + CPDF_PageExporter exporter(pDestDoc, pSrcDoc); +- return exporter.ExportPage(page_numbers, index); ++ return exporter.ExportPage(page_indices, index); + } + + FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV +@@ -778,21 +782,21 @@ FPDF_ImportNPagesToOne(FPDF_DOCUMENT src_doc, + return nullptr; + + CPDF_Document* pDestDoc = CPDFDocumentFromFPDFDocument(output_doc.get()); +- ASSERT(pDestDoc); ++ DCHECK(pDestDoc); + +- std::vector page_numbers = GetPageNumbers(*pSrcDoc, ByteString()); +- if (page_numbers.empty()) ++ std::vector page_indices = GetPageIndices(*pSrcDoc, ByteString()); ++ if (page_indices.empty()) + return nullptr; + + if (num_pages_on_x_axis == 1 && num_pages_on_y_axis == 1) { + CPDF_PageExporter exporter(pDestDoc, pSrcDoc); +- if (!exporter.ExportPage(page_numbers, 0)) ++ if (!exporter.ExportPage(page_indices, 0)) + return nullptr; + return output_doc.release(); + } + + CPDF_NPageToOneExporter exporter(pDestDoc, pSrcDoc); +- if (!exporter.ExportNPagesToOne(page_numbers, ++ if (!exporter.ExportNPagesToOne(page_indices, + CFX_SizeF(output_width, output_height), + num_pages_on_x_axis, num_pages_on_y_axis)) { + return nullptr; +@@ -800,6 +804,43 @@ FPDF_ImportNPagesToOne(FPDF_DOCUMENT src_doc, + return output_doc.release(); + } + ++FPDF_EXPORT FPDF_XOBJECT FPDF_CALLCONV ++FPDF_NewXObjectFromPage(FPDF_DOCUMENT dest_doc, ++ FPDF_DOCUMENT src_doc, ++ int src_page_index) { ++ CPDF_Document* dest = CPDFDocumentFromFPDFDocument(dest_doc); ++ if (!dest) ++ return nullptr; ++ ++ CPDF_Document* src = CPDFDocumentFromFPDFDocument(src_doc); ++ if (!src) ++ return nullptr; ++ ++ CPDF_NPageToOneExporter exporter(dest, src); ++ std::unique_ptr xobject = ++ exporter.CreateXObjectContextFromPage(src_page_index); ++ return FPDFXObjectFromXObjectContext(xobject.release()); ++} ++ ++FPDF_EXPORT void FPDF_CALLCONV FPDF_CloseXObject(FPDF_XOBJECT xobject) { ++ std::unique_ptr xobject_deleter( ++ XObjectContextFromFPDFXObject(xobject)); ++} ++ ++FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV ++FPDF_NewFormObjectFromXObject(FPDF_XOBJECT xobject) { ++ XObjectContext* xobj = XObjectContextFromFPDFXObject(xobject); ++ if (!xobj) ++ return nullptr; ++ ++ auto form = std::make_unique(xobj->dest_doc, nullptr, ++ xobj->xobject, nullptr); ++ form->ParseContent(nullptr, nullptr, nullptr); ++ auto form_object = std::make_unique( ++ CPDF_PageObject::kNoContentStream, std::move(form), CFX_Matrix()); ++ return FPDFPageObjectFromCPDFPageObject(form_object.release()); ++} ++ + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDF_CopyViewerPreferences(FPDF_DOCUMENT dest_doc, FPDF_DOCUMENT src_doc) { + CPDF_Document* pDstDoc = CPDFDocumentFromFPDFDocument(dest_doc); +@@ -810,15 +851,23 @@ FPDF_CopyViewerPreferences(FPDF_DOCUMENT dest_doc, FPDF_DOCUMENT src_doc) { + if (!pSrcDoc) + return false; + +- const CPDF_Dictionary* pSrcDict = pSrcDoc->GetRoot(); +- pSrcDict = pSrcDict->GetDictFor("ViewerPreferences"); +- if (!pSrcDict) ++ RetainPtr pPrefDict = ++ pSrcDoc->GetRoot()->GetDictFor("ViewerPreferences"); ++ if (!pPrefDict) + return false; + +- CPDF_Dictionary* pDstDict = pDstDoc->GetRoot(); ++ RetainPtr pDstDict = pDstDoc->GetMutableRoot(); + if (!pDstDict) + return false; + +- pDstDict->SetFor("ViewerPreferences", pSrcDict->CloneDirectObject()); ++ auto cloned_dict = pdfium::MakeRetain(); ++ CPDF_DictionaryLocker locker(pPrefDict); ++ for (const auto& it : locker) { ++ if (IsValidViewerPreferencesObject(it.second)) { ++ cloned_dict->SetFor(it.first, it.second->Clone()); ++ } ++ } ++ ++ pDstDict->SetFor("ViewerPreferences", std::move(cloned_dict)); + return true; + } +diff --git a/fpdfsdk/fpdf_ppo_embeddertest.cpp b/fpdfsdk/fpdf_ppo_embeddertest.cpp +index 568468016..5750947f0 100644 +--- a/fpdfsdk/fpdf_ppo_embeddertest.cpp ++++ b/fpdfsdk/fpdf_ppo_embeddertest.cpp +@@ -1,16 +1,27 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include +-#include +- ++#include ++ ++#include "core/fpdfapi/page/cpdf_form.h" ++#include "core/fpdfapi/page/cpdf_formobject.h" ++#include "core/fpdfapi/parser/cpdf_array.h" ++#include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fpdfapi/parser/cpdf_name.h" ++#include "core/fpdfapi/parser/cpdf_number.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" ++#include "core/fpdfapi/parser/cpdf_string.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" ++#include "fpdfsdk/cpdfsdk_helpers.h" + #include "public/cpp/fpdf_scopers.h" + #include "public/fpdf_edit.h" + #include "public/fpdf_ppo.h" + #include "public/fpdf_save.h" + #include "public/fpdfview.h" + #include "testing/embedder_test.h" ++#include "testing/embedder_test_constants.h" + #include "testing/gtest/include/gtest/gtest.h" + + namespace { +@@ -20,13 +31,40 @@ class FPDFPPOEmbedderTest : public EmbedderTest {}; + int FakeBlockWriter(FPDF_FILEWRITE* pThis, + const void* pData, + unsigned long size) { +- return size; ++ return 1; // Always succeeds. ++} ++ ++constexpr int kRectanglesMultiPagesPageCount = 2; ++ ++const char* RectanglesMultiPagesExpectedChecksum(int page_index) { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ static constexpr const char* kChecksums[kRectanglesMultiPagesPageCount] = { ++ "07606a12487bd0c28a88f23fa00fc313", "94ea6e1eef220833a3ec14d6a1c612b0"}; ++ return kChecksums[page_index]; ++ } ++ static constexpr const char* kChecksums[kRectanglesMultiPagesPageCount] = { ++ "72d0d7a19a2f40e010ca6a1133b33e1e", "fb18142190d770cfbc329d2b071aee4d"}; ++ return kChecksums[page_index]; ++} ++ ++const char* Bug750568PageHash(int page_index) { ++ constexpr int kBug750568PageCount = 4; ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ static constexpr const char* kChecksums[kBug750568PageCount] = { ++ "eaa139e944eafb43d31e8742a0e158de", "226485e9d4fa6a67dfe0a88723f12060", ++ "c5601a3492ae5dcc5dd25140fc463bfe", "1f60055b54de4fac8a59c65e90da156e"}; ++ return kChecksums[page_index]; ++ } ++ static constexpr const char* kChecksums[kBug750568PageCount] = { ++ "64ad08132a1c5a166768298c8a578f57", "83b83e2f6bc80707d0a917c7634140b9", ++ "913cd3723a451e4e46fbc2c05702d1ee", "81fb7cfd4860f855eb468f73dfeb6d60"}; ++ return kChecksums[page_index]; + } + + } // namespace + + TEST_F(FPDFPPOEmbedderTest, NoViewerPreferences) { +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + + FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument(); + EXPECT_TRUE(output_doc); +@@ -35,7 +73,7 @@ TEST_F(FPDFPPOEmbedderTest, NoViewerPreferences) { + } + + TEST_F(FPDFPPOEmbedderTest, ViewerPreferences) { +- EXPECT_TRUE(OpenDocument("viewer_ref.pdf")); ++ ASSERT_TRUE(OpenDocument("viewer_ref.pdf")); + + FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument(); + EXPECT_TRUE(output_doc); +@@ -43,6 +81,24 @@ TEST_F(FPDFPPOEmbedderTest, ViewerPreferences) { + FPDF_CloseDocument(output_doc); + } + ++TEST_F(FPDFPPOEmbedderTest, ImportPagesByIndex) { ++ ASSERT_TRUE(OpenDocument("viewer_ref.pdf")); ++ ++ FPDF_PAGE page = LoadPage(0); ++ EXPECT_TRUE(page); ++ ++ ScopedFPDFDocument output_doc(FPDF_CreateNewDocument()); ++ ASSERT_TRUE(output_doc); ++ EXPECT_TRUE(FPDF_CopyViewerPreferences(output_doc.get(), document())); ++ ++ static constexpr int kPageIndices[] = {1}; ++ EXPECT_TRUE(FPDF_ImportPagesByIndex( ++ output_doc.get(), document(), kPageIndices, std::size(kPageIndices), 0)); ++ EXPECT_EQ(1, FPDF_GetPageCount(output_doc.get())); ++ ++ UnloadPage(page); ++} ++ + TEST_F(FPDFPPOEmbedderTest, ImportPages) { + ASSERT_TRUE(OpenDocument("viewer_ref.pdf")); + +@@ -99,29 +155,192 @@ TEST_F(FPDFPPOEmbedderTest, BadNupParams) { + + // TODO(Xlou): Add more tests to check output doc content of + // FPDF_ImportNPagesToOne() +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_NupRenderImage DISABLED_NupRenderImage +-#else +-#define MAYBE_NupRenderImage NupRenderImage +-#endif +-TEST_F(FPDFPPOEmbedderTest, MAYBE_NupRenderImage) { ++TEST_F(FPDFPPOEmbedderTest, NupRenderImage) { + ASSERT_TRUE(OpenDocument("rectangles_multi_pages.pdf")); +- const int kPageCount = 2; +- static constexpr const char* kExpectedMD5s[kPageCount] = { +- "4d225b961da0f1bced7c83273e64c9b6", "fb18142190d770cfbc329d2b071aee4d"}; + ScopedFPDFDocument output_doc_3up( + FPDF_ImportNPagesToOne(document(), 792, 612, 3, 1)); + ASSERT_TRUE(output_doc_3up); +- ASSERT_EQ(kPageCount, FPDF_GetPageCount(output_doc_3up.get())); +- for (int i = 0; i < kPageCount; ++i) { ++ ASSERT_EQ(kRectanglesMultiPagesPageCount, ++ FPDF_GetPageCount(output_doc_3up.get())); ++ for (int i = 0; i < kRectanglesMultiPagesPageCount; ++i) { + ScopedFPDFPage page(FPDF_LoadPage(output_doc_3up.get(), i)); + ASSERT_TRUE(page); + ScopedFPDFBitmap bitmap = RenderPage(page.get()); + EXPECT_EQ(792, FPDFBitmap_GetWidth(bitmap.get())); + EXPECT_EQ(612, FPDFBitmap_GetHeight(bitmap.get())); +- EXPECT_EQ(kExpectedMD5s[i], HashBitmap(bitmap.get())); ++ EXPECT_EQ(RectanglesMultiPagesExpectedChecksum(i), ++ HashBitmap(bitmap.get())); ++ } ++} ++ ++TEST_F(FPDFPPOEmbedderTest, ImportPageToXObject) { ++ const char* checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "d6ebc0a8afc22fe0137f54ce54e1a19c"; ++ return "2d88d180af7109eb346439f7c855bb29"; ++ }(); ++ ++ ASSERT_TRUE(OpenDocument("rectangles.pdf")); ++ ++ { ++ ScopedFPDFDocument output_doc(FPDF_CreateNewDocument()); ++ ASSERT_TRUE(output_doc); ++ ++ FPDF_XOBJECT xobject = ++ FPDF_NewXObjectFromPage(output_doc.get(), document(), 0); ++ ASSERT_TRUE(xobject); ++ ++ for (int i = 0; i < 2; ++i) { ++ ScopedFPDFPage page(FPDFPage_New(output_doc.get(), 0, 612, 792)); ++ ASSERT_TRUE(page); ++ ++ FPDF_PAGEOBJECT page_object = FPDF_NewFormObjectFromXObject(xobject); ++ ASSERT_TRUE(page_object); ++ EXPECT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(page_object)); ++ FPDFPage_InsertObject(page.get(), page_object); ++ EXPECT_TRUE(FPDFPage_GenerateContent(page.get())); ++ ++ ScopedFPDFBitmap page_bitmap = RenderPage(page.get()); ++ CompareBitmap(page_bitmap.get(), 612, 792, checksum); ++ ++ float left; ++ float bottom; ++ float right; ++ float top; ++ ASSERT_TRUE( ++ FPDFPageObj_GetBounds(page_object, &left, &bottom, &right, &top)); ++ EXPECT_FLOAT_EQ(-1.0f, left); ++ EXPECT_FLOAT_EQ(-1.0f, bottom); ++ EXPECT_FLOAT_EQ(201.0f, right); ++ EXPECT_FLOAT_EQ(301.0f, top); ++ } ++ ++ EXPECT_TRUE(FPDF_SaveAsCopy(output_doc.get(), this, 0)); ++ ++ FPDF_CloseXObject(xobject); ++ } ++ ++ constexpr int kExpectedPageCount = 2; ++ ASSERT_TRUE(OpenSavedDocument()); ++ ++ FPDF_PAGE saved_pages[kExpectedPageCount]; ++ FPDF_PAGEOBJECT xobjects[kExpectedPageCount]; ++ for (int i = 0; i < kExpectedPageCount; ++i) { ++ saved_pages[i] = LoadSavedPage(i); ++ ASSERT_TRUE(saved_pages[i]); ++ ++ EXPECT_EQ(1, FPDFPage_CountObjects(saved_pages[i])); ++ xobjects[i] = FPDFPage_GetObject(saved_pages[i], 0); ++ ASSERT_TRUE(xobjects[i]); ++ ASSERT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(xobjects[i])); ++ EXPECT_EQ(8, FPDFFormObj_CountObjects(xobjects[i])); ++ ++ { ++ ScopedFPDFBitmap page_bitmap = RenderPage(saved_pages[i]); ++ CompareBitmap(page_bitmap.get(), 612, 792, checksum); ++ } ++ } ++ ++ for (int i = 0; i < kExpectedPageCount; ++i) { ++ float left; ++ float bottom; ++ float right; ++ float top; ++ ASSERT_TRUE( ++ FPDFPageObj_GetBounds(xobjects[i], &left, &bottom, &right, &top)); ++ EXPECT_FLOAT_EQ(-1.0f, left); ++ EXPECT_FLOAT_EQ(-1.0f, bottom); ++ EXPECT_FLOAT_EQ(201.0f, right); ++ EXPECT_FLOAT_EQ(301.0f, top); ++ } ++ ++ // Peek at object internals to make sure the two XObjects use the same stream. ++ EXPECT_NE(xobjects[0], xobjects[1]); ++ CPDF_PageObject* obj1 = CPDFPageObjectFromFPDFPageObject(xobjects[0]); ++ ASSERT_TRUE(obj1->AsForm()); ++ ASSERT_TRUE(obj1->AsForm()->form()); ++ ASSERT_TRUE(obj1->AsForm()->form()->GetStream()); ++ CPDF_PageObject* obj2 = CPDFPageObjectFromFPDFPageObject(xobjects[1]); ++ ASSERT_TRUE(obj2->AsForm()); ++ ASSERT_TRUE(obj2->AsForm()->form()); ++ ASSERT_TRUE(obj2->AsForm()->form()->GetStream()); ++ EXPECT_EQ(obj1->AsForm()->form()->GetStream(), ++ obj2->AsForm()->form()->GetStream()); ++ ++ for (FPDF_PAGE saved_page : saved_pages) ++ CloseSavedPage(saved_page); ++ ++ CloseSavedDocument(); ++} ++ ++TEST_F(FPDFPPOEmbedderTest, ImportPageToXObjectWithSameDoc) { ++ const char* checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "8e7d672f49f9ca98fb9157824cefc204"; ++ return "4d5ca14827b7707f8283e639b33c121a"; ++ }(); ++ ++ ASSERT_TRUE(OpenDocument("rectangles.pdf")); ++ ++ FPDF_XOBJECT xobject = FPDF_NewXObjectFromPage(document(), document(), 0); ++ ASSERT_TRUE(xobject); ++ ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFBitmap bitmap = RenderLoadedPage(page); ++ CompareBitmap(bitmap.get(), 200, 300, pdfium::RectanglesChecksum()); ++ } ++ ++ FPDF_PAGEOBJECT page_object = FPDF_NewFormObjectFromXObject(xobject); ++ ASSERT_TRUE(page_object); ++ ASSERT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(page_object)); ++ ++ static constexpr FS_MATRIX kMatrix = {0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}; ++ EXPECT_TRUE(FPDFPageObj_SetMatrix(page_object, &kMatrix)); ++ ++ FPDFPage_InsertObject(page, page_object); ++ EXPECT_TRUE(FPDFPage_GenerateContent(page)); ++ ++ { ++ ScopedFPDFBitmap bitmap = RenderLoadedPage(page); ++ CompareBitmap(bitmap.get(), 200, 300, checksum); ++ } ++ ++ FPDF_CloseXObject(xobject); ++ ++ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ VerifySavedDocument(200, 300, checksum); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFPPOEmbedderTest, XObjectNullParams) { ++ ASSERT_TRUE(OpenDocument("rectangles.pdf")); ++ ASSERT_EQ(1, FPDF_GetPageCount(document())); ++ ++ EXPECT_FALSE(FPDF_NewXObjectFromPage(nullptr, nullptr, -1)); ++ EXPECT_FALSE(FPDF_NewXObjectFromPage(nullptr, nullptr, 0)); ++ EXPECT_FALSE(FPDF_NewXObjectFromPage(nullptr, nullptr, 1)); ++ EXPECT_FALSE(FPDF_NewXObjectFromPage(document(), nullptr, -1)); ++ EXPECT_FALSE(FPDF_NewXObjectFromPage(document(), nullptr, 0)); ++ EXPECT_FALSE(FPDF_NewXObjectFromPage(document(), nullptr, 1)); ++ EXPECT_FALSE(FPDF_NewXObjectFromPage(nullptr, document(), -1)); ++ EXPECT_FALSE(FPDF_NewXObjectFromPage(nullptr, document(), 0)); ++ EXPECT_FALSE(FPDF_NewXObjectFromPage(nullptr, document(), 1)); ++ ++ { ++ ScopedFPDFDocument output_doc(FPDF_CreateNewDocument()); ++ ASSERT_TRUE(output_doc); ++ EXPECT_FALSE(FPDF_NewXObjectFromPage(output_doc.get(), document(), -1)); ++ EXPECT_FALSE(FPDF_NewXObjectFromPage(output_doc.get(), document(), 1)); + } ++ ++ // Should be a no-op. ++ FPDF_CloseXObject(nullptr); ++ ++ EXPECT_FALSE(FPDF_NewFormObjectFromXObject(nullptr)); + } + + TEST_F(FPDFPPOEmbedderTest, BUG_925981) { +@@ -131,6 +350,34 @@ TEST_F(FPDFPPOEmbedderTest, BUG_925981) { + EXPECT_EQ(1, FPDF_GetPageCount(output_doc_2up.get())); + } + ++TEST_F(FPDFPPOEmbedderTest, BUG_1229106) { ++ static constexpr int kPageCount = 4; ++ static constexpr int kTwoUpPageCount = 2; ++ static const char kRectsChecksum[] = "140d629b3c96a07ced2e3e408ea85a1d"; ++ static const char kTwoUpChecksum[] = "fa4501562301b2e75da354bd067495ec"; ++ ++ ASSERT_TRUE(OpenDocument("bug_1229106.pdf")); ++ ++ // Show all pages render the same. ++ ASSERT_EQ(kPageCount, FPDF_GetPageCount(document())); ++ for (int i = 0; i < kPageCount; ++i) { ++ FPDF_PAGE page = LoadPage(0); ++ ScopedFPDFBitmap bitmap = RenderLoadedPage(page); ++ CompareBitmap(bitmap.get(), 792, 612, kRectsChecksum); ++ UnloadPage(page); ++ } ++ ++ // Create a 2-up PDF. ++ ScopedFPDFDocument output_doc_2up( ++ FPDF_ImportNPagesToOne(document(), 612, 792, 1, 2)); ++ ASSERT_EQ(kTwoUpPageCount, FPDF_GetPageCount(output_doc_2up.get())); ++ for (int i = 0; i < kTwoUpPageCount; ++i) { ++ ScopedFPDFPage page(FPDF_LoadPage(output_doc_2up.get(), i)); ++ ScopedFPDFBitmap bitmap = RenderPage(page.get()); ++ CompareBitmap(bitmap.get(), 612, 792, kTwoUpChecksum); ++ } ++} ++ + TEST_F(FPDFPPOEmbedderTest, BadRepeatViewerPref) { + ASSERT_TRUE(OpenDocument("repeat_viewer_ref.pdf")); + +@@ -161,8 +408,117 @@ TEST_F(FPDFPPOEmbedderTest, BadCircularViewerPref) { + FPDF_CloseDocument(output_doc); + } + ++TEST_F(FPDFPPOEmbedderTest, CopyViewerPrefTypes) { ++ ASSERT_TRUE(OpenDocument("viewer_pref_types.pdf")); ++ ++ ScopedFPDFDocument output_doc(FPDF_CreateNewDocument()); ++ ASSERT_TRUE(output_doc); ++ EXPECT_TRUE(FPDF_CopyViewerPreferences(output_doc.get(), document())); ++ ++ // Peek under the hook to check the result. ++ const CPDF_Document* output_doc_impl = ++ CPDFDocumentFromFPDFDocument(output_doc.get()); ++ RetainPtr prefs = ++ output_doc_impl->GetRoot()->GetDictFor("ViewerPreferences"); ++ ASSERT_TRUE(prefs); ++ EXPECT_EQ(6u, prefs->size()); ++ ++ RetainPtr bool_obj = prefs->GetObjectFor("Bool"); ++ ASSERT_TRUE(bool_obj); ++ EXPECT_TRUE(bool_obj->IsBoolean()); ++ ++ RetainPtr num_obj = prefs->GetNumberFor("Num"); ++ ASSERT_TRUE(num_obj); ++ EXPECT_TRUE(num_obj->IsInteger()); ++ EXPECT_EQ(1, num_obj->GetInteger()); ++ ++ RetainPtr str_obj = prefs->GetStringFor("Str"); ++ ASSERT_TRUE(str_obj); ++ EXPECT_EQ("str", str_obj->GetString()); ++ ++ EXPECT_EQ("name", prefs->GetNameFor("Name")); ++ ++ RetainPtr empty_array_obj = ++ prefs->GetArrayFor("EmptyArray"); ++ ASSERT_TRUE(empty_array_obj); ++ EXPECT_TRUE(empty_array_obj->IsEmpty()); ++ ++ RetainPtr good_array_obj = prefs->GetArrayFor("GoodArray"); ++ ASSERT_TRUE(good_array_obj); ++ EXPECT_EQ(4u, good_array_obj->size()); ++} ++ ++TEST_F(FPDFPPOEmbedderTest, BadIndices) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ ++ FPDF_PAGE page = LoadPage(0); ++ EXPECT_TRUE(page); ++ ++ ScopedFPDFDocument output_doc(FPDF_CreateNewDocument()); ++ EXPECT_TRUE(output_doc); ++ ++ static constexpr int kBadIndices1[] = {-1}; ++ EXPECT_FALSE(FPDF_ImportPagesByIndex( ++ output_doc.get(), document(), kBadIndices1, std::size(kBadIndices1), 0)); ++ ++ static constexpr int kBadIndices2[] = {1}; ++ EXPECT_FALSE(FPDF_ImportPagesByIndex( ++ output_doc.get(), document(), kBadIndices2, std::size(kBadIndices2), 0)); ++ ++ static constexpr int kBadIndices3[] = {-1, 0, 1}; ++ EXPECT_FALSE(FPDF_ImportPagesByIndex( ++ output_doc.get(), document(), kBadIndices3, std::size(kBadIndices3), 0)); ++ ++ static constexpr int kBadIndices4[] = {42}; ++ EXPECT_FALSE(FPDF_ImportPagesByIndex( ++ output_doc.get(), document(), kBadIndices4, std::size(kBadIndices4), 0)); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFPPOEmbedderTest, GoodIndices) { ++ ASSERT_TRUE(OpenDocument("viewer_ref.pdf")); ++ ++ FPDF_PAGE page = LoadPage(0); ++ EXPECT_TRUE(page); ++ ++ ScopedFPDFDocument output_doc(FPDF_CreateNewDocument()); ++ EXPECT_TRUE(output_doc); ++ ++ static constexpr int kGoodIndices1[] = {0, 0, 0, 0}; ++ EXPECT_TRUE(FPDF_ImportPagesByIndex(output_doc.get(), document(), ++ kGoodIndices1, std::size(kGoodIndices1), ++ 0)); ++ EXPECT_EQ(4, FPDF_GetPageCount(output_doc.get())); ++ ++ static constexpr int kGoodIndices2[] = {0}; ++ EXPECT_TRUE(FPDF_ImportPagesByIndex(output_doc.get(), document(), ++ kGoodIndices2, std::size(kGoodIndices2), ++ 0)); ++ EXPECT_EQ(5, FPDF_GetPageCount(output_doc.get())); ++ ++ static constexpr int kGoodIndices3[] = {4}; ++ EXPECT_TRUE(FPDF_ImportPagesByIndex(output_doc.get(), document(), ++ kGoodIndices3, std::size(kGoodIndices3), ++ 0)); ++ EXPECT_EQ(6, FPDF_GetPageCount(output_doc.get())); ++ ++ static constexpr int kGoodIndices4[] = {1, 2, 3}; ++ EXPECT_TRUE(FPDF_ImportPagesByIndex(output_doc.get(), document(), ++ kGoodIndices4, std::size(kGoodIndices4), ++ 0)); ++ EXPECT_EQ(9, FPDF_GetPageCount(output_doc.get())); ++ ++ // Passing in a nullptr should import all the pages. ++ EXPECT_TRUE( ++ FPDF_ImportPagesByIndex(output_doc.get(), document(), nullptr, 0, 0)); ++ EXPECT_EQ(14, FPDF_GetPageCount(output_doc.get())); ++ ++ UnloadPage(page); ++} ++ + TEST_F(FPDFPPOEmbedderTest, BadRanges) { +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); +@@ -185,7 +541,7 @@ TEST_F(FPDFPPOEmbedderTest, BadRanges) { + } + + TEST_F(FPDFPPOEmbedderTest, GoodRanges) { +- EXPECT_TRUE(OpenDocument("viewer_ref.pdf")); ++ ASSERT_TRUE(OpenDocument("viewer_ref.pdf")); + + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); +@@ -207,30 +563,23 @@ TEST_F(FPDFPPOEmbedderTest, GoodRanges) { + } + + TEST_F(FPDFPPOEmbedderTest, BUG_664284) { +- EXPECT_TRUE(OpenDocument("bug_664284.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_664284.pdf")); + + FPDF_PAGE page = LoadPage(0); + ASSERT_NE(nullptr, page); + + FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument(); + EXPECT_TRUE(output_doc); +- EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "1", 0)); ++ ++ static constexpr int kIndices[] = {0}; ++ EXPECT_TRUE(FPDF_ImportPagesByIndex(output_doc, document(), kIndices, ++ std::size(kIndices), 0)); + FPDF_CloseDocument(output_doc); + + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_BUG_750568 DISABLED_BUG_750568 +-#else +-#define MAYBE_BUG_750568 BUG_750568 +-#endif +-TEST_F(FPDFPPOEmbedderTest, MAYBE_BUG_750568) { +- const char* const kHashes[] = { +- "64ad08132a1c5a166768298c8a578f57", "83b83e2f6bc80707d0a917c7634140b9", +- "913cd3723a451e4e46fbc2c05702d1ee", "81fb7cfd4860f855eb468f73dfeb6d60"}; +- ++TEST_F(FPDFPPOEmbedderTest, BUG_750568) { + ASSERT_TRUE(OpenDocument("bug_750568.pdf")); + ASSERT_EQ(4, FPDF_GetPageCount(document())); + +@@ -239,59 +588,47 @@ TEST_F(FPDFPPOEmbedderTest, MAYBE_BUG_750568) { + ASSERT_TRUE(page); + + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- ASSERT_EQ(200, FPDFBitmap_GetWidth(bitmap.get())); +- ASSERT_EQ(200, FPDFBitmap_GetHeight(bitmap.get())); +- ASSERT_EQ(800, FPDFBitmap_GetStride(bitmap.get())); +- +- EXPECT_EQ(kHashes[i], HashBitmap(bitmap.get())); ++ CompareBitmap(bitmap.get(), 200, 200, Bug750568PageHash(i)); + UnloadPage(page); + } + + FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument(); + ASSERT_TRUE(output_doc); +- EXPECT_TRUE(FPDF_ImportPages(output_doc, document(), "1,2,3,4", 0)); ++ ++ static constexpr int kIndices[] = {0, 1, 2, 3}; ++ EXPECT_TRUE(FPDF_ImportPagesByIndex(output_doc, document(), kIndices, ++ std::size(kIndices), 0)); + ASSERT_EQ(4, FPDF_GetPageCount(output_doc)); + for (size_t i = 0; i < 4; ++i) { + FPDF_PAGE page = FPDF_LoadPage(output_doc, i); + ASSERT_TRUE(page); + + ScopedFPDFBitmap bitmap = RenderPage(page); +- ASSERT_EQ(200, FPDFBitmap_GetWidth(bitmap.get())); +- ASSERT_EQ(200, FPDFBitmap_GetHeight(bitmap.get())); +- ASSERT_EQ(800, FPDFBitmap_GetStride(bitmap.get())); +- +- EXPECT_EQ(kHashes[i], HashBitmap(bitmap.get())); ++ CompareBitmap(bitmap.get(), 200, 200, Bug750568PageHash(i)); + FPDF_ClosePage(page); + } + FPDF_CloseDocument(output_doc); + } + + TEST_F(FPDFPPOEmbedderTest, ImportWithZeroLengthStream) { +- EXPECT_TRUE(OpenDocument("zero_length_stream.pdf")); ++ ASSERT_TRUE(OpenDocument("zero_length_stream.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- ASSERT_EQ(200, FPDFBitmap_GetWidth(bitmap.get())); +- ASSERT_EQ(200, FPDFBitmap_GetHeight(bitmap.get())); +- ASSERT_EQ(800, FPDFBitmap_GetStride(bitmap.get())); +- +- std::string digest = HashBitmap(bitmap.get()); ++ CompareBitmap(bitmap.get(), 200, 200, pdfium::HelloWorldChecksum()); + UnloadPage(page); + +- FPDF_DOCUMENT new_doc = FPDF_CreateNewDocument(); +- EXPECT_TRUE(new_doc); +- EXPECT_TRUE(FPDF_ImportPages(new_doc, document(), "1", 0)); +- +- EXPECT_EQ(1, FPDF_GetPageCount(new_doc)); +- FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0); +- ASSERT_NE(nullptr, new_page); +- ScopedFPDFBitmap new_bitmap = RenderPage(new_page); +- ASSERT_EQ(200, FPDFBitmap_GetWidth(new_bitmap.get())); +- ASSERT_EQ(200, FPDFBitmap_GetHeight(new_bitmap.get())); +- ASSERT_EQ(800, FPDFBitmap_GetStride(new_bitmap.get())); +- +- EXPECT_EQ(digest, HashBitmap(new_bitmap.get())); +- FPDF_ClosePage(new_page); +- FPDF_CloseDocument(new_doc); ++ ScopedFPDFDocument new_doc(FPDF_CreateNewDocument()); ++ ASSERT_TRUE(new_doc); ++ ++ static constexpr int kIndices[] = {0}; ++ EXPECT_TRUE(FPDF_ImportPagesByIndex(new_doc.get(), document(), kIndices, ++ std::size(kIndices), 0)); ++ ++ EXPECT_EQ(1, FPDF_GetPageCount(new_doc.get())); ++ ScopedFPDFPage new_page(FPDF_LoadPage(new_doc.get(), 0)); ++ ASSERT_TRUE(new_page); ++ ScopedFPDFBitmap new_bitmap = RenderPage(new_page.get()); ++ CompareBitmap(new_bitmap.get(), 200, 200, pdfium::HelloWorldChecksum()); + } +diff --git a/fpdfsdk/fpdf_progressive.cpp b/fpdfsdk/fpdf_progressive.cpp +index 99a92303f..af2cc4747 100644 +--- a/fpdfsdk/fpdf_progressive.cpp ++++ b/fpdfsdk/fpdf_progressive.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,7 @@ + + #include "public/fpdf_progressive.h" + ++#include + #include + + #include "core/fpdfapi/page/cpdf_page.h" +@@ -16,11 +17,6 @@ + #include "fpdfsdk/cpdfsdk_pauseadapter.h" + #include "fpdfsdk/cpdfsdk_renderpage.h" + #include "public/fpdfview.h" +-#include "third_party/base/ptr_util.h" +- +-#ifdef _SKIA_SUPPORT_PATHS_ +-#include "core/fxge/cfx_renderdevice.h" +-#endif + + // These checks are here because core/ and public/ cannot depend on each other. + static_assert(CPDF_ProgressiveRenderer::kReady == FPDF_RENDER_READY, +@@ -41,15 +37,17 @@ int ToFPDFStatus(CPDF_ProgressiveRenderer::Status status) { + + } // namespace + +-FPDF_EXPORT int FPDF_CALLCONV FPDF_RenderPageBitmap_Start(FPDF_BITMAP bitmap, +- FPDF_PAGE page, +- int start_x, +- int start_y, +- int size_x, +- int size_y, +- int rotate, +- int flags, +- IFSDK_PAUSE* pause) { ++FPDF_EXPORT int FPDF_CALLCONV ++FPDF_RenderPageBitmapWithColorScheme_Start(FPDF_BITMAP bitmap, ++ FPDF_PAGE page, ++ int start_x, ++ int start_y, ++ int size_x, ++ int size_y, ++ int rotate, ++ int flags, ++ const FPDF_COLORSCHEME* color_scheme, ++ IFSDK_PAUSE* pause) { + if (!bitmap || !pause || pause->version != 1) + return FPDF_RENDER_FAILED; + +@@ -57,25 +55,26 @@ FPDF_EXPORT int FPDF_CALLCONV FPDF_RenderPageBitmap_Start(FPDF_BITMAP bitmap, + if (!pPage) + return FPDF_RENDER_FAILED; + +- auto pOwnedContext = pdfium::MakeUnique(); ++ auto pOwnedContext = std::make_unique(); + CPDF_PageRenderContext* pContext = pOwnedContext.get(); + pPage->SetRenderContext(std::move(pOwnedContext)); + + RetainPtr pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap)); +- auto pOwnedDevice = pdfium::MakeUnique(); ++ auto pOwnedDevice = std::make_unique(); + CFX_DefaultRenderDevice* pDevice = pOwnedDevice.get(); + pContext->m_pDevice = std::move(pOwnedDevice); +- pDevice->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false); ++ pDevice->AttachWithRgbByteOrder(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER)); + + CPDFSDK_PauseAdapter pause_adapter(pause); + CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x, +- size_y, rotate, flags, ++ size_y, rotate, flags, color_scheme, + /*need_to_restore=*/false, &pause_adapter); + +-#ifdef _SKIA_SUPPORT_PATHS_ +- pDevice->Flush(false); +- pBitmap->UnPreMultiply(); +-#endif ++#if defined(_SKIA_SUPPORT_) ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ pBitmap->UnPreMultiply(); ++ } ++#endif // defined(_SKIA_SUPPORT_) + + if (!pContext->m_pRenderer) + return FPDF_RENDER_FAILED; +@@ -83,6 +82,20 @@ FPDF_EXPORT int FPDF_CALLCONV FPDF_RenderPageBitmap_Start(FPDF_BITMAP bitmap, + return ToFPDFStatus(pContext->m_pRenderer->GetStatus()); + } + ++FPDF_EXPORT int FPDF_CALLCONV FPDF_RenderPageBitmap_Start(FPDF_BITMAP bitmap, ++ FPDF_PAGE page, ++ int start_x, ++ int start_y, ++ int size_x, ++ int size_y, ++ int rotate, ++ int flags, ++ IFSDK_PAUSE* pause) { ++ return FPDF_RenderPageBitmapWithColorScheme_Start( ++ bitmap, page, start_x, start_y, size_x, size_y, rotate, flags, ++ /*color_scheme=*/nullptr, pause); ++} ++ + FPDF_EXPORT int FPDF_CALLCONV FPDF_RenderPage_Continue(FPDF_PAGE page, + IFSDK_PAUSE* pause) { + if (!pause || pause->version != 1) +@@ -99,26 +112,17 @@ FPDF_EXPORT int FPDF_CALLCONV FPDF_RenderPage_Continue(FPDF_PAGE page, + + CPDFSDK_PauseAdapter pause_adapter(pause); + pContext->m_pRenderer->Continue(&pause_adapter); +-#ifdef _SKIA_SUPPORT_PATHS_ +- CFX_RenderDevice* pDevice = pContext->m_pDevice.get(); +- pDevice->Flush(false); +- pDevice->GetBitmap()->UnPreMultiply(); +-#endif ++ ++#if defined(_SKIA_SUPPORT_) ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ pContext->m_pDevice->GetBitmap()->UnPreMultiply(); ++ } ++#endif // defined(_SKIA_SUPPORT_) + return ToFPDFStatus(pContext->m_pRenderer->GetStatus()); + } + + FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPage_Close(FPDF_PAGE page) { + CPDF_Page* pPage = CPDFPageFromFPDFPage(page); +- if (pPage) { +-#ifdef _SKIA_SUPPORT_PATHS_ +- auto* pContext = +- static_cast(pPage->GetRenderContext()); +- if (pContext && pContext->m_pRenderer) { +- CFX_RenderDevice* pDevice = pContext->m_pDevice.get(); +- pDevice->Flush(true); +- pDevice->GetBitmap()->UnPreMultiply(); +- } +-#endif +- pPage->SetRenderContext(nullptr); +- } ++ if (pPage) ++ pPage->ClearRenderContext(); + } +diff --git a/fpdfsdk/fpdf_save.cpp b/fpdfsdk/fpdf_save.cpp +index 78e0e58e8..37a190553 100644 +--- a/fpdfsdk/fpdf_save.cpp ++++ b/fpdfsdk/fpdf_save.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,6 @@ + + #include "public/fpdf_save.h" + +-#include + #include + #include + +@@ -19,10 +18,11 @@ + #include "core/fpdfapi/parser/cpdf_stream_acc.h" + #include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fxcrt/fx_extension.h" ++#include "core/fxcrt/stl_util.h" + #include "fpdfsdk/cpdfsdk_filewriteadapter.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "public/fpdf_edit.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + #ifdef PDF_ENABLE_XFA + #include "core/fpdfapi/parser/cpdf_stream.h" +@@ -31,12 +31,6 @@ + #include "public/fpdf_formfill.h" + #endif + +-#if defined(OS_ANDROID) +-#include +-#else +-#include +-#endif +- + namespace { + + #ifdef PDF_ENABLE_XFA +@@ -52,27 +46,27 @@ bool SaveXFADocumentData(CPDFXFA_Context* pContext, + if (!pPDFDocument) + return false; + +- CPDF_Dictionary* pRoot = pPDFDocument->GetRoot(); ++ RetainPtr pRoot = pPDFDocument->GetMutableRoot(); + if (!pRoot) + return false; + +- CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm"); ++ RetainPtr pAcroForm = pRoot->GetMutableDictFor("AcroForm"); + if (!pAcroForm) + return false; + +- CPDF_Object* pXFA = pAcroForm->GetObjectFor("XFA"); ++ RetainPtr pXFA = pAcroForm->GetMutableObjectFor("XFA"); + if (!pXFA) + return true; + +- CPDF_Array* pArray = pXFA->AsArray(); ++ CPDF_Array* pArray = pXFA->AsMutableArray(); + if (!pArray) + return false; + +- int size = pArray->size(); ++ int size = fxcrt::CollectionSize(*pArray); + int iFormIndex = -1; + int iDataSetsIndex = -1; + for (int i = 0; i < size - 1; i++) { +- const CPDF_Object* pPDFObj = pArray->GetObjectAt(i); ++ RetainPtr pPDFObj = pArray->GetObjectAt(i); + if (!pPDFObj->IsString()) + continue; + if (pPDFObj->GetString() == "form") +@@ -81,32 +75,34 @@ bool SaveXFADocumentData(CPDFXFA_Context* pContext, + iDataSetsIndex = i + 1; + } + +- CPDF_Stream* pFormStream = nullptr; ++ RetainPtr pFormStream; + if (iFormIndex != -1) { + // Get form CPDF_Stream +- CPDF_Object* pFormPDFObj = pArray->GetObjectAt(iFormIndex); ++ RetainPtr pFormPDFObj = pArray->GetMutableObjectAt(iFormIndex); + if (pFormPDFObj->IsReference()) { +- CPDF_Object* pFormDirectObj = pFormPDFObj->GetDirect(); ++ RetainPtr pFormDirectObj = pFormPDFObj->GetMutableDirect(); + if (pFormDirectObj && pFormDirectObj->IsStream()) { +- pFormStream = pFormDirectObj->AsStream(); ++ pFormStream.Reset(pFormDirectObj->AsMutableStream()); + } + } else if (pFormPDFObj->IsStream()) { +- pFormStream = pFormPDFObj->AsStream(); ++ pFormStream.Reset(pFormPDFObj->AsMutableStream()); + } + } + +- CPDF_Stream* pDataSetsStream = nullptr; ++ RetainPtr pDataSetsStream; + if (iDataSetsIndex != -1) { + // Get datasets CPDF_Stream +- CPDF_Object* pDataSetsPDFObj = pArray->GetObjectAt(iDataSetsIndex); ++ RetainPtr pDataSetsPDFObj = ++ pArray->GetMutableObjectAt(iDataSetsIndex); + if (pDataSetsPDFObj->IsReference()) { +- CPDF_Reference* pDataSetsRefObj = pDataSetsPDFObj->AsReference(); +- CPDF_Object* pDataSetsDirectObj = pDataSetsRefObj->GetDirect(); ++ CPDF_Reference* pDataSetsRefObj = pDataSetsPDFObj->AsMutableReference(); ++ RetainPtr pDataSetsDirectObj = ++ pDataSetsRefObj->GetMutableDirect(); + if (pDataSetsDirectObj && pDataSetsDirectObj->IsStream()) { +- pDataSetsStream = pDataSetsDirectObj->AsStream(); ++ pDataSetsStream.Reset(pDataSetsDirectObj->AsMutableStream()); + } + } else if (pDataSetsPDFObj->IsStream()) { +- pDataSetsStream = pDataSetsPDFObj->AsStream(); ++ pDataSetsStream.Reset(pDataSetsPDFObj->AsMutableStream()); + } + } + // L"datasets" +@@ -121,9 +117,9 @@ bool SaveXFADocumentData(CPDFXFA_Context* pContext, + pDataSetsStream->InitStreamFromFile(pFileWrite, std::move(pDataDict)); + } + } else { +- CPDF_Stream* pData = pPDFDocument->NewIndirect(); ++ auto pData = pPDFDocument->NewIndirect(); + pData->InitStreamFromFile(pFileWrite, std::move(pDataDict)); +- int iLast = pArray->size() - 2; ++ int iLast = fxcrt::CollectionSize(*pArray) - 2; + pArray->InsertNewAt(iLast, "datasets", false); + pArray->InsertNewAt(iLast + 1, pPDFDocument, + pData->GetObjNum()); +@@ -141,9 +137,9 @@ bool SaveXFADocumentData(CPDFXFA_Context* pContext, + if (pFormStream) + pFormStream->InitStreamFromFile(pFileWrite, std::move(pDataDict)); + } else { +- CPDF_Stream* pData = pPDFDocument->NewIndirect(); ++ auto pData = pPDFDocument->NewIndirect(); + pData->InitStreamFromFile(pFileWrite, std::move(pDataDict)); +- int iLast = pArray->size() - 2; ++ int iLast = fxcrt::CollectionSize(*pArray) - 2; + pArray->InsertNewAt(iLast, "form", false); + pArray->InsertNewAt(iLast + 1, pPDFDocument, + pData->GetObjNum()); +@@ -158,10 +154,10 @@ bool SaveXFADocumentData(CPDFXFA_Context* pContext, + bool DoDocSave(FPDF_DOCUMENT document, + FPDF_FILEWRITE* pFileWrite, + FPDF_DWORD flags, +- Optional version) { ++ absl::optional version) { + CPDF_Document* pPDFDoc = CPDFDocumentFromFPDFDocument(document); + if (!pPDFDoc) +- return 0; ++ return false; + + #ifdef PDF_ENABLE_XFA + auto* pContext = static_cast(pPDFDoc->GetExtension()); +@@ -184,7 +180,7 @@ bool DoDocSave(FPDF_DOCUMENT document, + fileMaker.RemoveSecurity(); + } + +- bool bRet = fileMaker.Create(flags); ++ bool bRet = fileMaker.Create(static_cast(flags)); + + #ifdef PDF_ENABLE_XFA + if (pContext) +diff --git a/fpdfsdk/fpdf_save_embeddertest.cpp b/fpdfsdk/fpdf_save_embeddertest.cpp +index 614b4f4d8..3f5efd8b8 100644 +--- a/fpdfsdk/fpdf_save_embeddertest.cpp ++++ b/fpdfsdk/fpdf_save_embeddertest.cpp +@@ -1,8 +1,7 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include + #include + + #include "core/fxcrt/fx_string.h" +@@ -12,40 +11,76 @@ + #include "public/fpdf_save.h" + #include "public/fpdfview.h" + #include "testing/embedder_test.h" ++#include "testing/embedder_test_constants.h" + #include "testing/gmock/include/gmock/gmock-matchers.h" + #include "testing/gtest/include/gtest/gtest.h" + ++using testing::HasSubstr; ++using testing::Not; ++using testing::StartsWith; ++ + class FPDFSaveEmbedderTest : public EmbedderTest {}; + + TEST_F(FPDFSaveEmbedderTest, SaveSimpleDoc) { +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); +- EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n")); +- EXPECT_EQ(805u, GetString().length()); ++ EXPECT_THAT(GetString(), StartsWith("%PDF-1.7\r\n")); ++ EXPECT_EQ(805u, GetString().size()); + } + + TEST_F(FPDFSaveEmbedderTest, SaveSimpleDocWithVersion) { +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 0, 14)); +- EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.4\r\n")); +- EXPECT_EQ(805u, GetString().length()); ++ EXPECT_THAT(GetString(), StartsWith("%PDF-1.4\r\n")); ++ EXPECT_EQ(805u, GetString().size()); + } ++ + TEST_F(FPDFSaveEmbedderTest, SaveSimpleDocWithBadVersion) { +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 0, -1)); +- EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n")); ++ EXPECT_THAT(GetString(), StartsWith("%PDF-1.7\r\n")); + + ClearString(); + EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 0, 0)); +- EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n")); ++ EXPECT_THAT(GetString(), StartsWith("%PDF-1.7\r\n")); + + ClearString(); + EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 0, 18)); +- EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n")); ++ EXPECT_THAT(GetString(), StartsWith("%PDF-1.7\r\n")); ++} ++ ++TEST_F(FPDFSaveEmbedderTest, SaveSimpleDocIncremental) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, FPDF_INCREMENTAL, 14)); ++ // Version gets taken as-is from input document. ++ EXPECT_THAT(GetString(), StartsWith("%PDF-1.7\n%\xa0\xf2\xa4\xf4")); ++ // Additional output produced vs. non incremental. ++ EXPECT_EQ(985u, GetString().size()); ++} ++ ++TEST_F(FPDFSaveEmbedderTest, SaveSimpleDocNoIncremental) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, FPDF_NO_INCREMENTAL, 14)); ++ EXPECT_THAT(GetString(), StartsWith("%PDF-1.4\r\n")); ++ EXPECT_EQ(805u, GetString().size()); ++} ++ ++TEST_F(FPDFSaveEmbedderTest, SaveSimpleDocRemoveSecurity) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, FPDF_REMOVE_SECURITY, 14)); ++ EXPECT_THAT(GetString(), StartsWith("%PDF-1.4\r\n")); ++ EXPECT_EQ(805u, GetString().size()); ++} ++ ++TEST_F(FPDFSaveEmbedderTest, SaveSimpleDocBadFlags) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ EXPECT_TRUE(FPDF_SaveWithVersion(document(), this, 999999, 14)); ++ EXPECT_THAT(GetString(), StartsWith("%PDF-1.4\r\n")); ++ EXPECT_EQ(805u, GetString().size()); + } + + TEST_F(FPDFSaveEmbedderTest, SaveCopiedDoc) { +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); +@@ -63,7 +98,7 @@ TEST_F(FPDFSaveEmbedderTest, SaveLinearizedDoc) { + const int kPageCount = 3; + std::string original_md5[kPageCount]; + +- EXPECT_TRUE(OpenDocument("linearized.pdf")); ++ ASSERT_TRUE(OpenDocument("linearized.pdf")); + for (int i = 0; i < kPageCount; ++i) { + FPDF_PAGE page = LoadPage(i); + ASSERT_TRUE(page); +@@ -75,10 +110,15 @@ TEST_F(FPDFSaveEmbedderTest, SaveLinearizedDoc) { + } + + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); +- EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.6\r\n")); +- EXPECT_THAT(GetString(), testing::HasSubstr("/Root ")); +- EXPECT_THAT(GetString(), testing::HasSubstr("/Info ")); +- EXPECT_EQ(8219u, GetString().length()); ++ EXPECT_THAT(GetString(), StartsWith("%PDF-1.6\r\n")); ++ EXPECT_THAT(GetString(), HasSubstr("/Root ")); ++ EXPECT_THAT(GetString(), HasSubstr("/Info ")); ++ EXPECT_THAT(GetString(), HasSubstr("/Size 37")); ++ EXPECT_THAT(GetString(), HasSubstr("35 0 obj")); ++ EXPECT_THAT(GetString(), HasSubstr("36 0 obj")); ++ EXPECT_THAT(GetString(), Not(HasSubstr("37 0 obj"))); ++ EXPECT_THAT(GetString(), Not(HasSubstr("38 0 obj"))); ++ EXPECT_EQ(7908u, GetString().size()); + + // Make sure new document renders the same as the old one. + ASSERT_TRUE(OpenSavedDocument()); +@@ -92,11 +132,40 @@ TEST_F(FPDFSaveEmbedderTest, SaveLinearizedDoc) { + CloseSavedDocument(); + } + ++TEST_F(FPDFSaveEmbedderTest, Bug1409) { ++ ASSERT_TRUE(OpenDocument("jpx_lzw.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ while (FPDFPage_CountObjects(page) > 0) { ++ ScopedFPDFPageObject object(FPDFPage_GetObject(page, 0)); ++ ASSERT_TRUE(object); ++ ASSERT_TRUE(FPDFPage_RemoveObject(page, object.get())); ++ } ++ ASSERT_TRUE(FPDFPage_GenerateContent(page)); ++ UnloadPage(page); ++ ++ ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ ++ // The new document should render as empty. ++ ASSERT_TRUE(OpenSavedDocument()); ++ FPDF_PAGE saved_page = LoadSavedPage(0); ++ ASSERT_TRUE(saved_page); ++ ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page); ++ EXPECT_EQ(pdfium::kBlankPage612By792Checksum, HashBitmap(bitmap.get())); ++ CloseSavedPage(saved_page); ++ CloseSavedDocument(); ++ ++ EXPECT_THAT(GetString(), StartsWith("%PDF-1.7\r\n")); ++ EXPECT_THAT(GetString(), HasSubstr("/Root ")); ++ EXPECT_THAT(GetString(), Not(HasSubstr("/Image"))); ++ EXPECT_LT(GetString().size(), 600u); ++} ++ + #ifdef PDF_ENABLE_XFA + TEST_F(FPDFSaveEmbedderTest, SaveXFADoc) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); +- EXPECT_THAT(GetString(), testing::StartsWith("%PDF-1.7\r\n")); ++ EXPECT_THAT(GetString(), StartsWith("%PDF-1.7\r\n")); + ASSERT_TRUE(OpenSavedDocument()); + // TODO(tsepez): check for XFA forms in document + CloseSavedDocument(); +@@ -104,15 +173,22 @@ TEST_F(FPDFSaveEmbedderTest, SaveXFADoc) { + #endif // PDF_ENABLE_XFA + + TEST_F(FPDFSaveEmbedderTest, BUG_342) { +- EXPECT_TRUE(OpenDocument("hello_world.pdf")); ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); +- EXPECT_THAT(GetString(), testing::HasSubstr("0000000000 65535 f\r\n")); +- EXPECT_THAT(GetString(), +- testing::Not(testing::HasSubstr("0000000000 65536 f\r\n"))); ++ EXPECT_THAT(GetString(), HasSubstr("0000000000 65535 f\r\n")); ++ EXPECT_THAT(GetString(), Not(HasSubstr("0000000000 65536 f\r\n"))); + } + + TEST_F(FPDFSaveEmbedderTest, BUG_905142) { +- EXPECT_TRUE(OpenDocument("bug_905142.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_905142.pdf")); ++ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); ++ EXPECT_THAT(GetString(), HasSubstr("/Length 0")); ++} ++ ++// Should not trigger a DCHECK() failure in CFX_FileBufferArchive. ++// Fails because the PDF is malformed. ++TEST_F(FPDFSaveEmbedderTest, Bug1328389) { ++ ASSERT_TRUE(OpenDocument("bug_1328389.pdf")); + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); +- EXPECT_THAT(GetString(), testing::HasSubstr("/Length 0")); ++ EXPECT_THAT(GetString(), HasSubstr("/Foo/")); + } +diff --git a/fpdfsdk/fpdf_searchex.cpp b/fpdfsdk/fpdf_searchex.cpp +index dbc2d2abc..74cdff126 100644 +--- a/fpdfsdk/fpdf_searchex.cpp ++++ b/fpdfsdk/fpdf_searchex.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fpdfsdk/fpdf_searchex_embeddertest.cpp b/fpdfsdk/fpdf_searchex_embeddertest.cpp +index 54b4799d5..5740789f6 100644 +--- a/fpdfsdk/fpdf_searchex_embeddertest.cpp ++++ b/fpdfsdk/fpdf_searchex_embeddertest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fpdfsdk/fpdf_signature.cpp b/fpdfsdk/fpdf_signature.cpp +new file mode 100644 +index 000000000..2e7d267fe +--- /dev/null ++++ b/fpdfsdk/fpdf_signature.cpp +@@ -0,0 +1,222 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "public/fpdf_signature.h" ++ ++#include ++#include ++ ++#include "constants/form_fields.h" ++#include "core/fpdfapi/parser/cpdf_array.h" ++#include "core/fpdfapi/parser/cpdf_dictionary.h" ++#include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fxcrt/stl_util.h" ++#include "fpdfsdk/cpdfsdk_helpers.h" ++#include "third_party/base/numerics/safe_conversions.h" ++ ++namespace { ++ ++std::vector> CollectSignatures( ++ CPDF_Document* doc) { ++ std::vector> signatures; ++ const CPDF_Dictionary* root = doc->GetRoot(); ++ if (!root) ++ return signatures; ++ ++ RetainPtr acro_form = root->GetDictFor("AcroForm"); ++ if (!acro_form) ++ return signatures; ++ ++ RetainPtr fields = acro_form->GetArrayFor("Fields"); ++ if (!fields) ++ return signatures; ++ ++ CPDF_ArrayLocker locker(std::move(fields)); ++ for (auto& field : locker) { ++ RetainPtr field_dict = field->GetDict(); ++ if (field_dict && field_dict->GetNameFor(pdfium::form_fields::kFT) == ++ pdfium::form_fields::kSig) { ++ signatures.push_back(std::move(field_dict)); ++ } ++ } ++ return signatures; ++} ++ ++} // namespace ++ ++FPDF_EXPORT int FPDF_CALLCONV FPDF_GetSignatureCount(FPDF_DOCUMENT document) { ++ auto* doc = CPDFDocumentFromFPDFDocument(document); ++ if (!doc) ++ return -1; ++ ++ return fxcrt::CollectionSize(CollectSignatures(doc)); ++} ++ ++FPDF_EXPORT FPDF_SIGNATURE FPDF_CALLCONV ++FPDF_GetSignatureObject(FPDF_DOCUMENT document, int index) { ++ auto* doc = CPDFDocumentFromFPDFDocument(document); ++ if (!doc) ++ return nullptr; ++ ++ std::vector> signatures = ++ CollectSignatures(doc); ++ if (!fxcrt::IndexInBounds(signatures, index)) ++ return nullptr; ++ ++ return FPDFSignatureFromCPDFDictionary(signatures[index].Get()); ++} ++ ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFSignatureObj_GetContents(FPDF_SIGNATURE signature, ++ void* buffer, ++ unsigned long length) { ++ const CPDF_Dictionary* signature_dict = ++ CPDFDictionaryFromFPDFSignature(signature); ++ if (!signature_dict) ++ return 0; ++ ++ RetainPtr value_dict = ++ signature_dict->GetDictFor(pdfium::form_fields::kV); ++ if (!value_dict) ++ return 0; ++ ++ ByteString contents = value_dict->GetByteStringFor("Contents"); ++ const unsigned long contents_len = ++ pdfium::base::checked_cast(contents.GetLength()); ++ if (buffer && length >= contents_len) ++ memcpy(buffer, contents.c_str(), contents_len); ++ ++ return contents_len; ++} ++ ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFSignatureObj_GetByteRange(FPDF_SIGNATURE signature, ++ int* buffer, ++ unsigned long length) { ++ const CPDF_Dictionary* signature_dict = ++ CPDFDictionaryFromFPDFSignature(signature); ++ if (!signature_dict) ++ return 0; ++ ++ RetainPtr value_dict = ++ signature_dict->GetDictFor(pdfium::form_fields::kV); ++ if (!value_dict) ++ return 0; ++ ++ RetainPtr byte_range = value_dict->GetArrayFor("ByteRange"); ++ if (!byte_range) ++ return 0; ++ ++ const unsigned long byte_range_len = ++ fxcrt::CollectionSize(*byte_range); ++ if (buffer && length >= byte_range_len) { ++ for (size_t i = 0; i < byte_range_len; ++i) ++ buffer[i] = byte_range->GetIntegerAt(i); ++ } ++ return byte_range_len; ++} ++ ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFSignatureObj_GetSubFilter(FPDF_SIGNATURE signature, ++ char* buffer, ++ unsigned long length) { ++ const CPDF_Dictionary* signature_dict = ++ CPDFDictionaryFromFPDFSignature(signature); ++ if (!signature_dict) ++ return 0; ++ ++ RetainPtr value_dict = ++ signature_dict->GetDictFor(pdfium::form_fields::kV); ++ if (!value_dict || !value_dict->KeyExist("SubFilter")) ++ return 0; ++ ++ ByteString sub_filter = value_dict->GetNameFor("SubFilter"); ++ return NulTerminateMaybeCopyAndReturnLength(sub_filter, buffer, length); ++} ++ ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFSignatureObj_GetReason(FPDF_SIGNATURE signature, ++ void* buffer, ++ unsigned long length) { ++ const CPDF_Dictionary* signature_dict = ++ CPDFDictionaryFromFPDFSignature(signature); ++ if (!signature_dict) ++ return 0; ++ ++ RetainPtr value_dict = ++ signature_dict->GetDictFor(pdfium::form_fields::kV); ++ if (!value_dict) ++ return 0; ++ ++ RetainPtr obj = value_dict->GetObjectFor("Reason"); ++ if (!obj || !obj->IsString()) ++ return 0; ++ ++ return Utf16EncodeMaybeCopyAndReturnLength(obj->GetUnicodeText(), buffer, ++ length); ++} ++ ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFSignatureObj_GetTime(FPDF_SIGNATURE signature, ++ char* buffer, ++ unsigned long length) { ++ const CPDF_Dictionary* signature_dict = ++ CPDFDictionaryFromFPDFSignature(signature); ++ if (!signature_dict) ++ return 0; ++ ++ RetainPtr value_dict = ++ signature_dict->GetDictFor(pdfium::form_fields::kV); ++ if (!value_dict) ++ return 0; ++ ++ RetainPtr obj = value_dict->GetObjectFor("M"); ++ if (!obj || !obj->IsString()) ++ return 0; ++ ++ return NulTerminateMaybeCopyAndReturnLength(obj->GetString(), buffer, length); ++} ++ ++FPDF_EXPORT unsigned int FPDF_CALLCONV ++FPDFSignatureObj_GetDocMDPPermission(FPDF_SIGNATURE signature) { ++ int permission = 0; ++ const CPDF_Dictionary* signature_dict = ++ CPDFDictionaryFromFPDFSignature(signature); ++ if (!signature_dict) ++ return permission; ++ ++ RetainPtr value_dict = ++ signature_dict->GetDictFor(pdfium::form_fields::kV); ++ if (!value_dict) ++ return permission; ++ ++ RetainPtr references = value_dict->GetArrayFor("Reference"); ++ if (!references) ++ return permission; ++ ++ CPDF_ArrayLocker locker(std::move(references)); ++ for (auto& reference : locker) { ++ RetainPtr reference_dict = reference->GetDict(); ++ if (!reference_dict) ++ continue; ++ ++ ByteString transform_method = reference_dict->GetNameFor("TransformMethod"); ++ if (transform_method != "DocMDP") ++ continue; ++ ++ RetainPtr transform_params = ++ reference_dict->GetDictFor("TransformParams"); ++ if (!transform_params) ++ continue; ++ ++ // Valid values are 1, 2 and 3; 2 is the default. ++ permission = transform_params->GetIntegerFor("P", 2); ++ if (permission < 1 || permission > 3) ++ permission = 0; ++ ++ return permission; ++ } ++ ++ return permission; ++} +diff --git a/fpdfsdk/fpdf_signature_embeddertest.cpp b/fpdfsdk/fpdf_signature_embeddertest.cpp +new file mode 100644 +index 000000000..02ae80128 +--- /dev/null ++++ b/fpdfsdk/fpdf_signature_embeddertest.cpp +@@ -0,0 +1,203 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include ++ ++#include "public/fpdf_signature.h" ++#include "testing/embedder_test.h" ++#include "testing/fx_string_testhelpers.h" ++ ++class FPDFSignatureEmbedderTest : public EmbedderTest {}; ++ ++TEST_F(FPDFSignatureEmbedderTest, GetSignatureCount) { ++ ASSERT_TRUE(OpenDocument("two_signatures.pdf")); ++ EXPECT_EQ(2, FPDF_GetSignatureCount(document())); ++} ++ ++TEST_F(FPDFSignatureEmbedderTest, GetSignatureCountZero) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ EXPECT_EQ(0, FPDF_GetSignatureCount(document())); ++ ++ // Provide no document. ++ EXPECT_EQ(-1, FPDF_GetSignatureCount(nullptr)); ++} ++ ++TEST_F(FPDFSignatureEmbedderTest, GetSignatureObject) { ++ ASSERT_TRUE(OpenDocument("two_signatures.pdf")); ++ // Different, non-null signature objects are returned. ++ FPDF_SIGNATURE signature1 = FPDF_GetSignatureObject(document(), 0); ++ EXPECT_TRUE(signature1); ++ FPDF_SIGNATURE signature2 = FPDF_GetSignatureObject(document(), 1); ++ EXPECT_TRUE(signature2); ++ EXPECT_NE(signature1, signature2); ++ ++ // Out of bounds. ++ EXPECT_FALSE(FPDF_GetSignatureObject(document(), -1)); ++ EXPECT_FALSE(FPDF_GetSignatureObject(document(), 2)); ++ ++ // Provide no document. ++ EXPECT_FALSE(FPDF_GetSignatureObject(nullptr, 0)); ++} ++ ++TEST_F(FPDFSignatureEmbedderTest, GetContents) { ++ ASSERT_TRUE(OpenDocument("two_signatures.pdf")); ++ FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0); ++ EXPECT_TRUE(signature); ++ ++ // FPDFSignatureObj_GetContents() positive testing. ++ unsigned long size = FPDFSignatureObj_GetContents(signature, nullptr, 0); ++ const uint8_t kExpectedContents[] = {0x30, 0x80, 0x06, 0x09, 0x2A, 0x86, 0x48, ++ 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02, 0xA0, ++ 0x80, 0x30, 0x80, 0x02, 0x01, 0x01}; ++ ASSERT_EQ(sizeof(kExpectedContents), size); ++ std::vector contents(size); ++ ASSERT_EQ(size, ++ FPDFSignatureObj_GetContents(signature, contents.data(), size)); ++ ASSERT_EQ(0, memcmp(kExpectedContents, contents.data(), size)); ++ ++ // FPDFSignatureObj_GetContents() negative testing. ++ ASSERT_EQ(0U, FPDFSignatureObj_GetContents(nullptr, nullptr, 0)); ++ ++ contents.resize(2); ++ contents[0] = 'x'; ++ contents[1] = '\0'; ++ size = ++ FPDFSignatureObj_GetContents(signature, contents.data(), contents.size()); ++ ASSERT_EQ(sizeof(kExpectedContents), size); ++ EXPECT_EQ('x', contents[0]); ++ EXPECT_EQ('\0', contents[1]); ++} ++ ++TEST_F(FPDFSignatureEmbedderTest, GetByteRange) { ++ ASSERT_TRUE(OpenDocument("two_signatures.pdf")); ++ FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0); ++ EXPECT_TRUE(signature); ++ ++ // FPDFSignatureObj_GetByteRange() positive testing. ++ unsigned long size = FPDFSignatureObj_GetByteRange(signature, nullptr, 0); ++ const std::vector kExpectedByteRange{0, 10, 30, 10}; ++ ASSERT_EQ(kExpectedByteRange.size(), size); ++ std::vector byte_range(size); ++ ASSERT_EQ(size, ++ FPDFSignatureObj_GetByteRange(signature, byte_range.data(), size)); ++ ASSERT_EQ(kExpectedByteRange, byte_range); ++ ++ // FPDFSignatureObj_GetByteRange() negative testing. ++ ASSERT_EQ(0U, FPDFSignatureObj_GetByteRange(nullptr, nullptr, 0)); ++ ++ byte_range.resize(2); ++ byte_range[0] = 0; ++ byte_range[1] = 1; ++ size = FPDFSignatureObj_GetByteRange(signature, byte_range.data(), ++ byte_range.size()); ++ ASSERT_EQ(kExpectedByteRange.size(), size); ++ EXPECT_EQ(0, byte_range[0]); ++ EXPECT_EQ(1, byte_range[1]); ++} ++ ++TEST_F(FPDFSignatureEmbedderTest, GetSubFilter) { ++ ASSERT_TRUE(OpenDocument("two_signatures.pdf")); ++ FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0); ++ EXPECT_TRUE(signature); ++ ++ // FPDFSignatureObj_GetSubFilter() positive testing. ++ unsigned long size = FPDFSignatureObj_GetSubFilter(signature, nullptr, 0); ++ const char kExpectedSubFilter[] = "ETSI.CAdES.detached"; ++ ASSERT_EQ(sizeof(kExpectedSubFilter), size); ++ std::vector sub_filter(size); ++ ASSERT_EQ(size, ++ FPDFSignatureObj_GetSubFilter(signature, sub_filter.data(), size)); ++ ASSERT_EQ(0, memcmp(kExpectedSubFilter, sub_filter.data(), size)); ++ ++ // FPDFSignatureObj_GetSubFilter() negative testing. ++ ASSERT_EQ(0U, FPDFSignatureObj_GetSubFilter(nullptr, nullptr, 0)); ++ ++ sub_filter.resize(2); ++ sub_filter[0] = 'x'; ++ sub_filter[1] = '\0'; ++ size = FPDFSignatureObj_GetSubFilter(signature, sub_filter.data(), ++ sub_filter.size()); ++ ASSERT_EQ(sizeof(kExpectedSubFilter), size); ++ EXPECT_EQ('x', sub_filter[0]); ++ EXPECT_EQ('\0', sub_filter[1]); ++} ++ ++TEST_F(FPDFSignatureEmbedderTest, GetSubFilterNoKeyExists) { ++ ASSERT_TRUE(OpenDocument("signature_no_sub_filter.pdf")); ++ FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0); ++ EXPECT_TRUE(signature); ++ ++ // FPDFSignatureObj_GetSubFilter() negative testing: no SubFilter ++ ASSERT_EQ(0U, FPDFSignatureObj_GetSubFilter(signature, nullptr, 0)); ++} ++ ++TEST_F(FPDFSignatureEmbedderTest, GetReason) { ++ ASSERT_TRUE(OpenDocument("signature_reason.pdf")); ++ FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0); ++ EXPECT_TRUE(signature); ++ ++ // FPDFSignatureObj_GetReason() positive testing. ++ constexpr char kReason[] = "test reason"; ++ // Return value includes the terminating NUL that is provided. ++ constexpr unsigned long kReasonUTF16Size = std::size(kReason) * 2; ++ constexpr wchar_t kReasonWide[] = L"test reason"; ++ unsigned long size = FPDFSignatureObj_GetReason(signature, nullptr, 0); ++ ASSERT_EQ(kReasonUTF16Size, size); ++ ++ std::vector buffer(size); ++ ASSERT_EQ(size, FPDFSignatureObj_GetReason(signature, buffer.data(), size)); ++ ASSERT_EQ(kReasonWide, GetPlatformWString(buffer.data())); ++ ++ // FPDFSignatureObj_GetReason() negative testing. ++ ASSERT_EQ(0U, FPDFSignatureObj_GetReason(nullptr, nullptr, 0)); ++ ++ // Buffer is too small, ensure it's not modified. ++ buffer.resize(2); ++ buffer[0] = 'x'; ++ buffer[1] = '\0'; ++ size = FPDFSignatureObj_GetReason(signature, buffer.data(), buffer.size()); ++ ASSERT_EQ(kReasonUTF16Size, size); ++ EXPECT_EQ('x', buffer[0]); ++ EXPECT_EQ('\0', buffer[1]); ++} ++ ++TEST_F(FPDFSignatureEmbedderTest, GetTime) { ++ ASSERT_TRUE(OpenDocument("two_signatures.pdf")); ++ FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0); ++ EXPECT_TRUE(signature); ++ ++ // FPDFSignatureObj_GetTime() positive testing. ++ unsigned long size = FPDFSignatureObj_GetTime(signature, nullptr, 0); ++ const char kExpectedTime[] = "D:20200624093114+02'00'"; ++ ASSERT_EQ(sizeof(kExpectedTime), size); ++ std::vector time_buffer(size); ++ ASSERT_EQ(size, ++ FPDFSignatureObj_GetTime(signature, time_buffer.data(), size)); ++ ASSERT_EQ(0, memcmp(kExpectedTime, time_buffer.data(), size)); ++ ++ // FPDFSignatureObj_GetTime() negative testing. ++ ASSERT_EQ(0U, FPDFSignatureObj_GetTime(nullptr, nullptr, 0)); ++ ++ time_buffer.resize(2); ++ time_buffer[0] = 'x'; ++ time_buffer[1] = '\0'; ++ size = FPDFSignatureObj_GetTime(signature, time_buffer.data(), ++ time_buffer.size()); ++ ASSERT_EQ(sizeof(kExpectedTime), size); ++ EXPECT_EQ('x', time_buffer[0]); ++ EXPECT_EQ('\0', time_buffer[1]); ++} ++ ++TEST_F(FPDFSignatureEmbedderTest, GetDocMDPPermission) { ++ ASSERT_TRUE(OpenDocument("docmdp.pdf")); ++ FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0); ++ ASSERT_NE(nullptr, signature); ++ ++ // FPDFSignatureObj_GetDocMDPPermission() positive testing. ++ unsigned int permission = FPDFSignatureObj_GetDocMDPPermission(signature); ++ EXPECT_EQ(1U, permission); ++ ++ // FPDFSignatureObj_GetDocMDPPermission() negative testing. ++ EXPECT_EQ(0U, FPDFSignatureObj_GetDocMDPPermission(nullptr)); ++} +diff --git a/fpdfsdk/fpdf_structtree.cpp b/fpdfsdk/fpdf_structtree.cpp +index cfd57a836..76a30beda 100644 +--- a/fpdfsdk/fpdf_structtree.cpp ++++ b/fpdfsdk/fpdf_structtree.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,14 @@ + #include + + #include "core/fpdfapi/page/cpdf_page.h" ++#include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfdoc/cpdf_structelement.h" + #include "core/fpdfdoc/cpdf_structtree.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/stl_util.h" + #include "fpdfsdk/cpdfsdk_helpers.h" ++#include "third_party/base/numerics/safe_conversions.h" + + namespace { + +@@ -21,12 +25,22 @@ unsigned long WideStringToBuffer(const WideString& str, + return 0; + + ByteString encodedStr = str.ToUTF16LE(); +- const unsigned long len = encodedStr.GetLength(); ++ const unsigned long len = ++ pdfium::base::checked_cast(encodedStr.GetLength()); + if (buffer && len <= buflen) + memcpy(buffer, encodedStr.c_str(), len); + return len; + } + ++int GetMcidFromDict(const CPDF_Dictionary* dict) { ++ if (dict && dict->GetNameFor("Type") == "MCR") { ++ RetainPtr obj = dict->GetObjectFor("MCID"); ++ if (obj && obj->IsNumber()) ++ return obj->GetInteger(); ++ } ++ return -1; ++} ++ + } // namespace + + FPDF_EXPORT FPDF_STRUCTTREE FPDF_CALLCONV +@@ -53,7 +67,7 @@ FPDF_StructTree_CountChildren(FPDF_STRUCTTREE struct_tree) { + if (!tree) + return -1; + +- pdfium::base::CheckedNumeric tmp_size = tree->CountTopElements(); ++ FX_SAFE_INT32 tmp_size = tree->CountTopElements(); + return tmp_size.ValueOrDefault(-1); + } + +@@ -77,11 +91,119 @@ FPDF_StructElement_GetAltText(FPDF_STRUCTELEMENT struct_element, + return elem ? WideStringToBuffer(elem->GetAltText(), buffer, buflen) : 0; + } + ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDF_StructElement_GetActualText(FPDF_STRUCTELEMENT struct_element, ++ void* buffer, ++ unsigned long buflen) { ++ CPDF_StructElement* elem = ++ CPDFStructElementFromFPDFStructElement(struct_element); ++ return elem ? WideStringToBuffer(elem->GetActualText(), buffer, buflen) : 0; ++} ++ ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDF_StructElement_GetID(FPDF_STRUCTELEMENT struct_element, ++ void* buffer, ++ unsigned long buflen) { ++ CPDF_StructElement* elem = ++ CPDFStructElementFromFPDFStructElement(struct_element); ++ if (!elem) ++ return 0; ++ absl::optional id = elem->GetID(); ++ if (!id.has_value()) ++ return 0; ++ return Utf16EncodeMaybeCopyAndReturnLength(id.value(), buffer, buflen); ++} ++ ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDF_StructElement_GetLang(FPDF_STRUCTELEMENT struct_element, ++ void* buffer, ++ unsigned long buflen) { ++ CPDF_StructElement* elem = ++ CPDFStructElementFromFPDFStructElement(struct_element); ++ if (!elem) ++ return 0; ++ absl::optional lang = elem->GetLang(); ++ if (!lang.has_value()) ++ return 0; ++ return Utf16EncodeMaybeCopyAndReturnLength(lang.value(), buffer, buflen); ++} ++ ++FPDF_EXPORT int FPDF_CALLCONV ++FPDF_StructElement_GetAttributeCount(FPDF_STRUCTELEMENT struct_element) { ++ CPDF_StructElement* elem = ++ CPDFStructElementFromFPDFStructElement(struct_element); ++ if (!elem) ++ return -1; ++ RetainPtr attr_obj = elem->GetA(); ++ if (!attr_obj) ++ return -1; ++ if (attr_obj->IsArray()) ++ return fxcrt::CollectionSize(*attr_obj->AsArray()); ++ return attr_obj->IsDictionary() ? 1 : -1; ++} ++ ++FPDF_EXPORT FPDF_STRUCTELEMENT_ATTR FPDF_CALLCONV ++FPDF_StructElement_GetAttributeAtIndex(FPDF_STRUCTELEMENT struct_element, ++ int index) { ++ CPDF_StructElement* elem = ++ CPDFStructElementFromFPDFStructElement(struct_element); ++ if (!elem) ++ return nullptr; ++ ++ RetainPtr attr_obj = elem->GetA(); ++ if (!attr_obj) ++ return nullptr; ++ ++ if (attr_obj->IsDictionary()) { ++ return index == 0 ? FPDFStructElementAttrFromCPDFDictionary( ++ attr_obj->AsDictionary()) ++ : nullptr; ++ } ++ if (attr_obj->IsArray()) { ++ const CPDF_Array* array = attr_obj->AsArray(); ++ if (index < 0 || static_cast(index) >= array->size()) ++ return nullptr; ++ ++ // TODO(tsepez): should embedder take a reference here? ++ // Unretained reference in public API. NOLINTNEXTLINE ++ return FPDFStructElementAttrFromCPDFDictionary(array->GetDictAt(index)); ++ } ++ return nullptr; ++} ++ ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDF_StructElement_GetStringAttribute(FPDF_STRUCTELEMENT struct_element, ++ FPDF_BYTESTRING attr_name, ++ void* buffer, ++ unsigned long buflen) { ++ CPDF_StructElement* elem = ++ CPDFStructElementFromFPDFStructElement(struct_element); ++ if (!elem) ++ return 0; ++ RetainPtr array = ToArray(elem->GetA()); ++ if (!array) ++ return 0; ++ CPDF_ArrayLocker locker(array); ++ for (const RetainPtr& obj : locker) { ++ const CPDF_Dictionary* obj_dict = obj->AsDictionary(); ++ if (!obj_dict) ++ continue; ++ RetainPtr attr = obj_dict->GetObjectFor(attr_name); ++ if (!attr || !(attr->IsString() || attr->IsName())) ++ continue; ++ return Utf16EncodeMaybeCopyAndReturnLength(attr->GetUnicodeText(), buffer, ++ buflen); ++ } ++ return 0; ++} ++ + FPDF_EXPORT int FPDF_CALLCONV + FPDF_StructElement_GetMarkedContentID(FPDF_STRUCTELEMENT struct_element) { + CPDF_StructElement* elem = + CPDFStructElementFromFPDFStructElement(struct_element); +- const CPDF_Object* p = elem ? elem->GetDict()->GetObjectFor("K") : nullptr; ++ if (!elem) ++ return -1; ++ RetainPtr p = elem->GetK(); + return p && p->IsNumber() ? p->GetInteger() : -1; + } + +@@ -97,6 +219,18 @@ FPDF_StructElement_GetType(FPDF_STRUCTELEMENT struct_element, + : 0; + } + ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDF_StructElement_GetObjType(FPDF_STRUCTELEMENT struct_element, ++ void* buffer, ++ unsigned long buflen) { ++ CPDF_StructElement* elem = ++ CPDFStructElementFromFPDFStructElement(struct_element); ++ return elem ? WideStringToBuffer( ++ WideString::FromUTF8(elem->GetObjType().AsStringView()), ++ buffer, buflen) ++ : 0; ++} ++ + FPDF_EXPORT unsigned long FPDF_CALLCONV + FPDF_StructElement_GetTitle(FPDF_STRUCTELEMENT struct_element, + void* buffer, +@@ -113,7 +247,7 @@ FPDF_StructElement_CountChildren(FPDF_STRUCTELEMENT struct_element) { + if (!elem) + return -1; + +- pdfium::base::CheckedNumeric tmp_size = elem->CountKids(); ++ FX_SAFE_INT32 tmp_size = elem->CountKids(); + return tmp_size.ValueOrDefault(-1); + } + +@@ -125,6 +259,201 @@ FPDF_StructElement_GetChildAtIndex(FPDF_STRUCTELEMENT struct_element, + if (!elem || index < 0 || static_cast(index) >= elem->CountKids()) + return nullptr; + +- return FPDFStructElementFromCPDFStructElement( +- elem->GetKidIfElement(static_cast(index))); ++ return FPDFStructElementFromCPDFStructElement(elem->GetKidIfElement(index)); ++} ++ ++FPDF_EXPORT FPDF_STRUCTELEMENT FPDF_CALLCONV ++FPDF_StructElement_GetParent(FPDF_STRUCTELEMENT struct_element) { ++ CPDF_StructElement* elem = ++ CPDFStructElementFromFPDFStructElement(struct_element); ++ CPDF_StructElement* parent = elem ? elem->GetParent() : nullptr; ++ if (!parent) { ++ return nullptr; ++ } ++ return FPDFStructElementFromCPDFStructElement(parent); ++} ++ ++FPDF_EXPORT int FPDF_CALLCONV ++FPDF_StructElement_Attr_GetCount(FPDF_STRUCTELEMENT_ATTR struct_attribute) { ++ const CPDF_Dictionary* dict = ++ CPDFDictionaryFromFPDFStructElementAttr(struct_attribute); ++ if (!dict) ++ return -1; ++ return fxcrt::CollectionSize(*dict); ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDF_StructElement_Attr_GetName(FPDF_STRUCTELEMENT_ATTR struct_attribute, ++ int index, ++ void* buffer, ++ unsigned long buflen, ++ unsigned long* out_buflen) { ++ if (!out_buflen || !buffer) ++ return false; ++ ++ const CPDF_Dictionary* dict = ++ CPDFDictionaryFromFPDFStructElementAttr(struct_attribute); ++ if (!dict) ++ return false; ++ ++ CPDF_DictionaryLocker locker(dict); ++ for (auto& it : locker) { ++ if (index == 0) { ++ *out_buflen = ++ NulTerminateMaybeCopyAndReturnLength(it.first, buffer, buflen); ++ return true; ++ } ++ --index; ++ } ++ return false; ++} ++ ++FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV ++FPDF_StructElement_Attr_GetType(FPDF_STRUCTELEMENT_ATTR struct_attribute, ++ FPDF_BYTESTRING name) { ++ const CPDF_Dictionary* dict = ++ CPDFDictionaryFromFPDFStructElementAttr(struct_attribute); ++ if (!dict) ++ return FPDF_OBJECT_UNKNOWN; ++ ++ RetainPtr obj = dict->GetObjectFor(name); ++ return obj ? obj->GetType() : FPDF_OBJECT_UNKNOWN; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_StructElement_Attr_GetBooleanValue( ++ FPDF_STRUCTELEMENT_ATTR struct_attribute, ++ FPDF_BYTESTRING name, ++ FPDF_BOOL* out_value) { ++ if (!out_value) ++ return false; ++ ++ const CPDF_Dictionary* dict = ++ CPDFDictionaryFromFPDFStructElementAttr(struct_attribute); ++ if (!dict) ++ return false; ++ ++ RetainPtr obj = dict->GetObjectFor(name); ++ if (!obj || !obj->IsBoolean()) ++ return false; ++ ++ *out_value = obj->GetInteger(); ++ return true; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDF_StructElement_Attr_GetNumberValue(FPDF_STRUCTELEMENT_ATTR struct_attribute, ++ FPDF_BYTESTRING name, ++ float* out_value) { ++ if (!out_value) ++ return false; ++ ++ const CPDF_Dictionary* dict = ++ CPDFDictionaryFromFPDFStructElementAttr(struct_attribute); ++ if (!dict) ++ return false; ++ ++ RetainPtr obj = dict->GetObjectFor(name); ++ if (!obj || !obj->IsNumber()) ++ return false; ++ ++ *out_value = obj->GetNumber(); ++ return true; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDF_StructElement_Attr_GetStringValue(FPDF_STRUCTELEMENT_ATTR struct_attribute, ++ FPDF_BYTESTRING name, ++ void* buffer, ++ unsigned long buflen, ++ unsigned long* out_buflen) { ++ if (!out_buflen) ++ return false; ++ ++ const CPDF_Dictionary* dict = ++ CPDFDictionaryFromFPDFStructElementAttr(struct_attribute); ++ if (!dict) ++ return false; ++ ++ RetainPtr obj = dict->GetObjectFor(name); ++ if (!obj || !(obj->IsString() || obj->IsName())) ++ return false; ++ ++ *out_buflen = Utf16EncodeMaybeCopyAndReturnLength( ++ WideString::FromUTF8(obj->GetString().AsStringView()), buffer, buflen); ++ return true; ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDF_StructElement_Attr_GetBlobValue(FPDF_STRUCTELEMENT_ATTR struct_attribute, ++ FPDF_BYTESTRING name, ++ void* buffer, ++ unsigned long buflen, ++ unsigned long* out_buflen) { ++ if (!out_buflen) ++ return false; ++ ++ const CPDF_Dictionary* dict = ++ CPDFDictionaryFromFPDFStructElementAttr(struct_attribute); ++ if (!dict) ++ return false; ++ ++ RetainPtr obj = dict->GetObjectFor(name); ++ if (!obj || !obj->IsString()) ++ return false; ++ ++ ByteString result = obj->GetString(); ++ const unsigned long len = ++ pdfium::base::checked_cast(result.GetLength()); ++ if (buffer && len <= buflen) ++ memcpy(buffer, result.c_str(), len); ++ ++ *out_buflen = len; ++ return true; ++} ++ ++FPDF_EXPORT int FPDF_CALLCONV ++FPDF_StructElement_GetMarkedContentIdCount(FPDF_STRUCTELEMENT struct_element) { ++ CPDF_StructElement* elem = ++ CPDFStructElementFromFPDFStructElement(struct_element); ++ if (!elem) ++ return -1; ++ RetainPtr p = elem->GetK(); ++ if (!p) ++ return -1; ++ ++ if (p->IsNumber() || p->IsDictionary()) ++ return 1; ++ ++ return p->IsArray() ? fxcrt::CollectionSize(*p->AsArray()) : -1; ++} ++ ++FPDF_EXPORT int FPDF_CALLCONV ++FPDF_StructElement_GetMarkedContentIdAtIndex(FPDF_STRUCTELEMENT struct_element, ++ int index) { ++ CPDF_StructElement* elem = ++ CPDFStructElementFromFPDFStructElement(struct_element); ++ if (!elem) ++ return -1; ++ RetainPtr p = elem->GetK(); ++ if (!p) ++ return -1; ++ ++ if (p->IsNumber()) ++ return index == 0 ? p->GetInteger() : -1; ++ ++ if (p->IsDictionary()) ++ return GetMcidFromDict(p->GetDict().Get()); ++ ++ if (p->IsArray()) { ++ const CPDF_Array* array = p->AsArray(); ++ if (index < 0 || static_cast(index) >= array->size()) ++ return -1; ++ RetainPtr array_elem = array->GetObjectAt(index); ++ if (array_elem->IsNumber()) ++ return array_elem->GetInteger(); ++ if (array_elem->IsDictionary()) { ++ return GetMcidFromDict(array_elem->GetDict().Get()); ++ } ++ } ++ return -1; + } +diff --git a/fpdfsdk/fpdf_structtree_embeddertest.cpp b/fpdfsdk/fpdf_structtree_embeddertest.cpp +index bbaa11588..472a3a986 100644 +--- a/fpdfsdk/fpdf_structtree_embeddertest.cpp ++++ b/fpdfsdk/fpdf_structtree_embeddertest.cpp +@@ -1,11 +1,13 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include "core/fxcrt/fx_string.h" ++#include ++ + #include "public/fpdf_structtree.h" + #include "testing/embedder_test.h" +-#include "third_party/base/optional.h" ++#include "testing/fx_string_testhelpers.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class FPDFStructTreeEmbedderTest : public EmbedderTest {}; + +@@ -21,33 +23,33 @@ TEST_F(FPDFStructTreeEmbedderTest, GetAltText) { + + FPDF_STRUCTELEMENT element = + FPDF_StructTree_GetChildAtIndex(struct_tree.get(), -1); +- EXPECT_EQ(nullptr, element); ++ EXPECT_FALSE(element); + element = FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 1); +- EXPECT_EQ(nullptr, element); ++ EXPECT_FALSE(element); + element = FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0); +- ASSERT_NE(nullptr, element); ++ ASSERT_TRUE(element); + EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(element)); + EXPECT_EQ(0U, FPDF_StructElement_GetAltText(element, nullptr, 0)); + + ASSERT_EQ(1, FPDF_StructElement_CountChildren(element)); + FPDF_STRUCTELEMENT child_element = + FPDF_StructElement_GetChildAtIndex(element, -1); +- EXPECT_EQ(nullptr, child_element); ++ EXPECT_FALSE(child_element); + child_element = FPDF_StructElement_GetChildAtIndex(element, 1); +- EXPECT_EQ(nullptr, child_element); ++ EXPECT_FALSE(child_element); + child_element = FPDF_StructElement_GetChildAtIndex(element, 0); +- ASSERT_NE(nullptr, child_element); ++ ASSERT_TRUE(child_element); + EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(child_element)); + EXPECT_EQ(0U, FPDF_StructElement_GetAltText(child_element, nullptr, 0)); + + ASSERT_EQ(1, FPDF_StructElement_CountChildren(child_element)); + FPDF_STRUCTELEMENT gchild_element = + FPDF_StructElement_GetChildAtIndex(child_element, -1); +- EXPECT_EQ(nullptr, gchild_element); ++ EXPECT_FALSE(gchild_element); + gchild_element = FPDF_StructElement_GetChildAtIndex(child_element, 1); +- EXPECT_EQ(nullptr, gchild_element); ++ EXPECT_FALSE(gchild_element); + gchild_element = FPDF_StructElement_GetChildAtIndex(child_element, 0); +- ASSERT_NE(nullptr, gchild_element); ++ ASSERT_TRUE(gchild_element); + EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(gchild_element)); + ASSERT_EQ(24U, FPDF_StructElement_GetAltText(gchild_element, nullptr, 0)); + +@@ -56,25 +58,265 @@ TEST_F(FPDFStructTreeEmbedderTest, GetAltText) { + // Deliberately pass in a small buffer size to make sure |buffer| remains + // untouched. + ASSERT_EQ(24U, FPDF_StructElement_GetAltText(gchild_element, buffer, 1)); +- for (size_t i = 0; i < FX_ArraySize(buffer); ++i) ++ for (size_t i = 0; i < std::size(buffer); ++i) + EXPECT_EQ(0U, buffer[i]); + + EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(gchild_element)); + ASSERT_EQ(24U, FPDF_StructElement_GetAltText(gchild_element, buffer, + sizeof(buffer))); +- const wchar_t kExpected[] = L"Black Image"; +- EXPECT_EQ(WideString(kExpected), +- WideString::FromUTF16LE(buffer, FXSYS_len(kExpected))); ++ EXPECT_EQ(L"Black Image", GetPlatformWString(buffer)); + + ASSERT_EQ(1, FPDF_StructElement_CountChildren(gchild_element)); + FPDF_STRUCTELEMENT ggchild_element = + FPDF_StructElement_GetChildAtIndex(gchild_element, 0); +- EXPECT_EQ(nullptr, ggchild_element); ++ EXPECT_FALSE(ggchild_element); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFStructTreeEmbedderTest, GetActualText) { ++ ASSERT_TRUE(OpenDocument("tagged_actual_text.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page)); ++ ASSERT_TRUE(struct_tree); ++ ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get())); ++ ++ EXPECT_EQ(0U, FPDF_StructElement_GetActualText(nullptr, nullptr, 0)); ++ ++ FPDF_STRUCTELEMENT element = ++ FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0); ++ ASSERT_TRUE(element); ++ EXPECT_EQ(0U, FPDF_StructElement_GetActualText(element, nullptr, 0)); ++ ++ ASSERT_EQ(1, FPDF_StructElement_CountChildren(element)); ++ FPDF_STRUCTELEMENT child_element = ++ FPDF_StructElement_GetChildAtIndex(element, 0); ++ ASSERT_TRUE(child_element); ++ EXPECT_EQ(0U, FPDF_StructElement_GetActualText(child_element, nullptr, 0)); ++ ++ ASSERT_EQ(1, FPDF_StructElement_CountChildren(child_element)); ++ FPDF_STRUCTELEMENT gchild_element = ++ FPDF_StructElement_GetChildAtIndex(child_element, 0); ++ ASSERT_TRUE(gchild_element); ++ ASSERT_EQ(24U, ++ FPDF_StructElement_GetActualText(gchild_element, nullptr, 0)); ++ ++ unsigned short buffer[12] = {}; ++ // Deliberately pass in a small buffer size to make sure |buffer| remains ++ // untouched. ++ ASSERT_EQ(24U, FPDF_StructElement_GetActualText(gchild_element, buffer, 1)); ++ for (size_t i = 0; i < std::size(buffer); ++i) ++ EXPECT_EQ(0U, buffer[i]); ++ ASSERT_EQ(24U, FPDF_StructElement_GetActualText(gchild_element, buffer, ++ sizeof(buffer))); ++ EXPECT_EQ(L"Actual Text", GetPlatformWString(buffer)); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFStructTreeEmbedderTest, GetStringAttribute) { ++ ASSERT_TRUE(OpenDocument("tagged_table.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page)); ++ ASSERT_TRUE(struct_tree); ++ ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get())); ++ ++ FPDF_STRUCTELEMENT document = ++ FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0); ++ ASSERT_TRUE(document); ++ ++ constexpr int kBufLen = 100; ++ uint16_t buffer[kBufLen] = {0}; ++ EXPECT_EQ(18U, FPDF_StructElement_GetType(document, buffer, kBufLen)); ++ EXPECT_EQ("Document", GetPlatformString(buffer)); ++ ++ ASSERT_EQ(1, FPDF_StructElement_CountChildren(document)); ++ FPDF_STRUCTELEMENT table = FPDF_StructElement_GetChildAtIndex(document, 0); ++ ASSERT_TRUE(table); ++ ++ EXPECT_EQ(12U, FPDF_StructElement_GetType(table, buffer, kBufLen)); ++ EXPECT_EQ("Table", GetPlatformString(buffer)); ++ ++ // The table should have an attribute "Summary" set to the empty string. ++ EXPECT_EQ(2U, FPDF_StructElement_GetStringAttribute(table, "Summary", ++ buffer, kBufLen)); ++ ++ ASSERT_EQ(2, FPDF_StructElement_CountChildren(table)); ++ FPDF_STRUCTELEMENT row = FPDF_StructElement_GetChildAtIndex(table, 0); ++ ASSERT_TRUE(row); ++ ++ ASSERT_EQ(2, FPDF_StructElement_CountChildren(row)); ++ FPDF_STRUCTELEMENT header_cell = FPDF_StructElement_GetChildAtIndex(row, 0); ++ ASSERT_TRUE(header_cell); ++ ++ EXPECT_EQ(6U, FPDF_StructElement_GetType(header_cell, buffer, kBufLen)); ++ EXPECT_EQ("TH", GetPlatformString(buffer)); ++ ++ // The header should have an attribute "Scope" with a scope of "Row". ++ EXPECT_EQ(8U, FPDF_StructElement_GetStringAttribute(header_cell, "Scope", ++ buffer, kBufLen)); ++ EXPECT_EQ("Row", GetPlatformString(buffer)); ++ ++ // The header has an attribute "ColSpan", but it's not a string so it ++ // returns null. ++ EXPECT_EQ(0U, FPDF_StructElement_GetStringAttribute(header_cell, "ColSpan", ++ buffer, kBufLen)); ++ ++ // An unsupported attribute should return 0. ++ EXPECT_EQ(0U, FPDF_StructElement_GetStringAttribute(header_cell, "Other", ++ buffer, kBufLen)); ++ ++ // A null struct element should not crash. ++ EXPECT_EQ(0U, FPDF_StructElement_GetStringAttribute(nullptr, "Other", ++ buffer, kBufLen)); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFStructTreeEmbedderTest, GetStringAttributeBadStructElement) { ++ ASSERT_TRUE(OpenDocument("tagged_table_bad_elem.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page)); ++ ASSERT_TRUE(struct_tree); ++ ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get())); ++ ++ FPDF_STRUCTELEMENT document = ++ FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0); ++ ASSERT_TRUE(document); ++ ++ constexpr int kBufLen = 100; ++ uint16_t buffer[kBufLen] = {0}; ++ EXPECT_EQ(18U, FPDF_StructElement_GetType(document, buffer, kBufLen)); ++ EXPECT_EQ("Document", GetPlatformString(buffer)); ++ ++ // The table can be retrieved, even though it does not have /Type. ++ ASSERT_EQ(1, FPDF_StructElement_CountChildren(document)); ++ FPDF_STRUCTELEMENT table = FPDF_StructElement_GetChildAtIndex(document, 0); ++ ASSERT_TRUE(table); ++ ++ EXPECT_EQ(12U, FPDF_StructElement_GetType(table, buffer, kBufLen)); ++ EXPECT_EQ("Table", GetPlatformString(buffer)); ++ ++ // The table entry cannot be retrieved, as the element is malformed. ++ EXPECT_EQ(0U, FPDF_StructElement_GetStringAttribute(table, "Summary", ++ buffer, kBufLen)); ++ ++ // The row can be retrieved, even though it had an invalid /Type. ++ ASSERT_EQ(1, FPDF_StructElement_CountChildren(table)); ++ FPDF_STRUCTELEMENT row = FPDF_StructElement_GetChildAtIndex(table, 0); ++ EXPECT_TRUE(row); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFStructTreeEmbedderTest, GetID) { ++ ASSERT_TRUE(OpenDocument("tagged_table.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page)); ++ ASSERT_TRUE(struct_tree); ++ ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get())); ++ ++ FPDF_STRUCTELEMENT document = ++ FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0); ++ ASSERT_TRUE(document); ++ ++ constexpr int kBufLen = 100; ++ uint16_t buffer[kBufLen] = {0}; ++ EXPECT_EQ(18U, FPDF_StructElement_GetType(document, buffer, kBufLen)); ++ EXPECT_EQ("Document", GetPlatformString(buffer)); ++ ++ // The document has no ID. ++ EXPECT_EQ(0U, FPDF_StructElement_GetID(document, buffer, kBufLen)); ++ ++ ASSERT_EQ(1, FPDF_StructElement_CountChildren(document)); ++ FPDF_STRUCTELEMENT table = FPDF_StructElement_GetChildAtIndex(document, 0); ++ ASSERT_TRUE(table); ++ ++ EXPECT_EQ(12U, FPDF_StructElement_GetType(table, buffer, kBufLen)); ++ EXPECT_EQ("Table", GetPlatformString(buffer)); ++ ++ // The table has an ID. ++ EXPECT_EQ(14U, FPDF_StructElement_GetID(table, buffer, kBufLen)); ++ EXPECT_EQ("node12", GetPlatformString(buffer)); ++ ++ // The first child of the table is a row, which has an empty ID. ++ // It returns 2U, the length of an empty string, instead of 0U, ++ // representing null. ++ ASSERT_EQ(2, FPDF_StructElement_CountChildren(table)); ++ FPDF_STRUCTELEMENT row = FPDF_StructElement_GetChildAtIndex(table, 0); ++ ASSERT_TRUE(row); ++ EXPECT_EQ(2U, FPDF_StructElement_GetID(row, buffer, kBufLen)); + } + + UnloadPage(page); + } + ++TEST_F(FPDFStructTreeEmbedderTest, GetLang) { ++ ASSERT_TRUE(OpenDocument("tagged_table.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page)); ++ ASSERT_TRUE(struct_tree); ++ ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get())); ++ ++ FPDF_STRUCTELEMENT document = ++ FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0); ++ ASSERT_TRUE(document); ++ ++ constexpr int kBufLen = 100; ++ uint16_t buffer[kBufLen] = {0}; ++ EXPECT_EQ(18U, FPDF_StructElement_GetType(document, buffer, kBufLen)); ++ EXPECT_EQ("Document", GetPlatformString(buffer)); ++ ++ // Nullptr test ++ EXPECT_EQ(0U, FPDF_StructElement_GetLang(nullptr, buffer, kBufLen)); ++ ++ // The document has a language. ++ EXPECT_EQ(12U, FPDF_StructElement_GetLang(document, buffer, kBufLen)); ++ EXPECT_EQ("en-US", GetPlatformString(buffer)); ++ ++ ASSERT_EQ(1, FPDF_StructElement_CountChildren(document)); ++ FPDF_STRUCTELEMENT table = FPDF_StructElement_GetChildAtIndex(document, 0); ++ ASSERT_TRUE(table); ++ ++ // The first child is a table, with a language. ++ EXPECT_EQ(12U, FPDF_StructElement_GetType(table, buffer, kBufLen)); ++ EXPECT_EQ("Table", GetPlatformString(buffer)); ++ ++ EXPECT_EQ(6U, FPDF_StructElement_GetLang(table, buffer, kBufLen)); ++ EXPECT_EQ("hu", GetPlatformString(buffer)); ++ ++ // The first child of the table is a row, which doesn't have a ++ // language explicitly set on it. ++ ASSERT_EQ(2, FPDF_StructElement_CountChildren(table)); ++ FPDF_STRUCTELEMENT row = FPDF_StructElement_GetChildAtIndex(table, 0); ++ ASSERT_TRUE(row); ++ EXPECT_EQ(0U, FPDF_StructElement_GetLang(row, buffer, kBufLen)); ++ } ++ ++ UnloadPage(page); ++} ++ ++// See also FPDFEditEmbedderTest.TraverseMarkedContentID, which traverses the ++// marked contents using FPDFPageObj_GetMark() and related API. + TEST_F(FPDFStructTreeEmbedderTest, GetMarkedContentID) { + ASSERT_TRUE(OpenDocument("marked_content_id.pdf")); + FPDF_PAGE page = LoadPage(0); +@@ -93,6 +335,60 @@ TEST_F(FPDFStructTreeEmbedderTest, GetMarkedContentID) { + UnloadPage(page); + } + ++TEST_F(FPDFStructTreeEmbedderTest, GetMarkedContentIdAtIndex) { ++ ASSERT_TRUE(OpenDocument("tagged_marked_content.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page)); ++ ASSERT_TRUE(struct_tree); ++ ASSERT_EQ(4, FPDF_StructTree_CountChildren(struct_tree.get())); ++ ++ // K is an integer MCID ++ FPDF_STRUCTELEMENT child1 = ++ FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0); ++ ASSERT_TRUE(child1); ++ // Legacy API ++ EXPECT_EQ(0, FPDF_StructElement_GetMarkedContentID(child1)); ++ ++ // K is a dict containing MCR object reference ++ FPDF_STRUCTELEMENT child2 = ++ FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 1); ++ ASSERT_TRUE(child2); ++ ++ // K is an array containing dict MCR object reference and integer MCID ++ FPDF_STRUCTELEMENT child3 = ++ FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 2); ++ ASSERT_TRUE(child3); ++ ++ // K does not exist ++ FPDF_STRUCTELEMENT child4 = ++ FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 3); ++ ASSERT_TRUE(child4); ++ ++ // New APIs ++ EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdCount(nullptr)); ++ EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdAtIndex(nullptr, 0)); ++ EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdAtIndex(child1, -1)); ++ EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdAtIndex(child1, 1)); ++ EXPECT_EQ(1, FPDF_StructElement_GetMarkedContentIdCount(child1)); ++ EXPECT_EQ(0, FPDF_StructElement_GetMarkedContentIdAtIndex(child1, 0)); ++ ++ EXPECT_EQ(1, FPDF_StructElement_GetMarkedContentIdCount(child2)); ++ EXPECT_EQ(1, FPDF_StructElement_GetMarkedContentIdAtIndex(child2, 0)); ++ ++ EXPECT_EQ(2, FPDF_StructElement_GetMarkedContentIdCount(child3)); ++ EXPECT_EQ(2, FPDF_StructElement_GetMarkedContentIdAtIndex(child3, 0)); ++ EXPECT_EQ(3, FPDF_StructElement_GetMarkedContentIdAtIndex(child3, 1)); ++ ++ EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdCount(child4)); ++ EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdAtIndex(child4, 0)); ++ } ++ ++ UnloadPage(page); ++} ++ + TEST_F(FPDFStructTreeEmbedderTest, GetType) { + ASSERT_TRUE(OpenDocument("tagged_alt_text.pdf")); + FPDF_PAGE page = LoadPage(0); +@@ -105,7 +401,7 @@ TEST_F(FPDFStructTreeEmbedderTest, GetType) { + + FPDF_STRUCTELEMENT element = + FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0); +- ASSERT_NE(nullptr, element); ++ ASSERT_TRUE(element); + + // test nullptr inputs + unsigned short buffer[12]; +@@ -117,13 +413,95 @@ TEST_F(FPDFStructTreeEmbedderTest, GetType) { + // Deliberately pass in a small buffer size to make sure |buffer| remains + // untouched. + ASSERT_EQ(18U, FPDF_StructElement_GetType(element, buffer, 1)); +- for (size_t i = 0; i < FX_ArraySize(buffer); ++i) ++ for (size_t i = 0; i < std::size(buffer); ++i) + EXPECT_EQ(0U, buffer[i]); + + ASSERT_EQ(18U, FPDF_StructElement_GetType(element, buffer, sizeof(buffer))); +- const wchar_t kExpected[] = L"Document"; +- EXPECT_EQ(WideString(kExpected), +- WideString::FromUTF16LE(buffer, FXSYS_len(kExpected))); ++ EXPECT_EQ(L"Document", GetPlatformWString(buffer)); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFStructTreeEmbedderTest, GetObjType) { ++ ASSERT_TRUE(OpenDocument("tagged_table_bad_elem.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page)); ++ ASSERT_TRUE(struct_tree); ++ ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get())); ++ ++ FPDF_STRUCTELEMENT child = ++ FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0); ++ ASSERT_TRUE(child); ++ ++ // test nullptr inputs ++ unsigned short buffer[28] = {}; ++ ASSERT_EQ(0U, ++ FPDF_StructElement_GetObjType(nullptr, buffer, sizeof(buffer))); ++ ASSERT_EQ(0U, FPDF_StructElement_GetObjType(nullptr, nullptr, 0)); ++ ASSERT_EQ(22U, FPDF_StructElement_GetObjType(child, nullptr, 0)); ++ ++ // Deliberately pass in a small buffer size to make sure `buffer` remains ++ // untouched. ++ ASSERT_EQ(22U, FPDF_StructElement_GetObjType(child, buffer, 1)); ++ for (size_t i = 0; i < std::size(buffer); ++i) ++ EXPECT_EQ(0U, buffer[i]); ++ ++ ASSERT_EQ(22U, ++ FPDF_StructElement_GetObjType(child, buffer, sizeof(buffer))); ++ EXPECT_EQ(L"StructElem", GetPlatformWString(buffer)); ++ ++ ASSERT_EQ(1, FPDF_StructElement_CountChildren(child)); ++ FPDF_STRUCTELEMENT gchild = FPDF_StructElement_GetChildAtIndex(child, 0); ++ memset(buffer, 0, sizeof(buffer)); ++ // Missing /Type in `gchild` ++ ASSERT_EQ(0U, ++ FPDF_StructElement_GetObjType(gchild, buffer, sizeof(buffer))); ++ // Buffer is untouched. ++ for (size_t i = 0; i < std::size(buffer); ++i) ++ EXPECT_EQ(0U, buffer[i]); ++ ++ ASSERT_EQ(1, FPDF_StructElement_CountChildren(gchild)); ++ FPDF_STRUCTELEMENT ggchild = FPDF_StructElement_GetChildAtIndex(gchild, 0); ++ ASSERT_EQ(28U, ++ FPDF_StructElement_GetObjType(ggchild, buffer, sizeof(buffer))); ++ // Reading bad elem also works. ++ EXPECT_EQ(L"NotStructElem", GetPlatformWString(buffer)); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFStructTreeEmbedderTest, GetParent) { ++ ASSERT_TRUE(OpenDocument("tagged_alt_text.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page)); ++ ASSERT_TRUE(struct_tree); ++ ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get())); ++ ++ FPDF_STRUCTELEMENT parent = ++ FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0); ++ ASSERT_TRUE(parent); ++ ++ ASSERT_EQ(1, FPDF_StructElement_CountChildren(parent)); ++ ++ FPDF_STRUCTELEMENT child = FPDF_StructElement_GetChildAtIndex(parent, 0); ++ ASSERT_TRUE(child); ++ ++ // test nullptr inputs ++ ASSERT_EQ(nullptr, FPDF_StructElement_GetParent(nullptr)); ++ ++ ASSERT_EQ(parent, FPDF_StructElement_GetParent(child)); ++ ++ // The parent of `parent` is StructTreeRoot and no longer a StructElement. ++ // We currently handle this case by returning a nullptr. ++ ASSERT_EQ(nullptr, FPDF_StructElement_GetParent(parent)); + } + + UnloadPage(page); +@@ -141,7 +519,7 @@ TEST_F(FPDFStructTreeEmbedderTest, GetTitle) { + + FPDF_STRUCTELEMENT element = + FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0); +- ASSERT_NE(nullptr, element); ++ ASSERT_TRUE(element); + + // test nullptr inputs + unsigned short buffer[13]; +@@ -153,26 +531,302 @@ TEST_F(FPDFStructTreeEmbedderTest, GetTitle) { + // Deliberately pass in a small buffer size to make sure |buffer| remains + // untouched. + ASSERT_EQ(20U, FPDF_StructElement_GetTitle(element, buffer, 1)); +- for (size_t i = 0; i < FX_ArraySize(buffer); ++i) ++ for (size_t i = 0; i < std::size(buffer); ++i) + EXPECT_EQ(0U, buffer[i]); + + ASSERT_EQ(20U, + FPDF_StructElement_GetTitle(element, buffer, sizeof(buffer))); + +- const wchar_t kExpected[] = L"TitleText"; +- EXPECT_EQ(WideString(kExpected), +- WideString::FromUTF16LE(buffer, FXSYS_len(kExpected))); ++ EXPECT_EQ(L"TitleText", GetPlatformWString(buffer)); + + ASSERT_EQ(1, FPDF_StructElement_CountChildren(element)); + FPDF_STRUCTELEMENT child_element = + FPDF_StructElement_GetChildAtIndex(element, 0); +- ASSERT_NE(nullptr, element); ++ ASSERT_TRUE(element); + + ASSERT_EQ(26U, FPDF_StructElement_GetTitle(child_element, buffer, + sizeof(buffer))); +- const wchar_t kChildExpected[] = L"symbol: 100k"; +- EXPECT_EQ(WideString(kChildExpected), +- WideString::FromUTF16LE(buffer, FXSYS_len(kChildExpected))); ++ EXPECT_EQ(L"symbol: 100k", GetPlatformWString(buffer)); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFStructTreeEmbedderTest, GetAttributes) { ++ ASSERT_TRUE(OpenDocument("tagged_table.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page)); ++ ASSERT_TRUE(struct_tree); ++ ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get())); ++ ++ FPDF_STRUCTELEMENT document = ++ FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0); ++ ASSERT_TRUE(document); ++ ++ ASSERT_EQ(1, FPDF_StructElement_CountChildren(document)); ++ ASSERT_EQ(-1, FPDF_StructElement_GetAttributeCount(document)); ++ FPDF_STRUCTELEMENT table = FPDF_StructElement_GetChildAtIndex(document, 0); ++ ASSERT_TRUE(table); ++ ++ ASSERT_EQ(2, FPDF_StructElement_CountChildren(table)); ++ ++ { ++ FPDF_STRUCTELEMENT tr = FPDF_StructElement_GetChildAtIndex(table, 0); ++ ASSERT_TRUE(tr); ++ ++ ASSERT_EQ(2, FPDF_StructElement_CountChildren(tr)); ++ FPDF_STRUCTELEMENT th = FPDF_StructElement_GetChildAtIndex(tr, 0); ++ ASSERT_TRUE(th); ++ ++ ASSERT_EQ(2, FPDF_StructElement_GetAttributeCount(th)); ++ ++ // nullptr test ++ ASSERT_EQ(nullptr, FPDF_StructElement_GetAttributeAtIndex(document, 0)); ++ ASSERT_EQ(nullptr, FPDF_StructElement_GetAttributeAtIndex(document, -1)); ++ ASSERT_EQ(nullptr, FPDF_StructElement_GetAttributeAtIndex(th, 2)); ++ ++ FPDF_STRUCTELEMENT_ATTR attr = ++ FPDF_StructElement_GetAttributeAtIndex(th, 1); ++ ASSERT_TRUE(attr); ++ ++ ASSERT_EQ(2, FPDF_StructElement_Attr_GetCount(attr)); ++ ASSERT_FALSE( ++ FPDF_StructElement_Attr_GetName(attr, 1, nullptr, 0U, nullptr)); ++ char buffer[8] = {}; ++ unsigned long out_len = ULONG_MAX; ++ // Deliberately pass in a small buffer size to make sure `buffer` remains ++ // untouched. ++ ASSERT_TRUE( ++ FPDF_StructElement_Attr_GetName(attr, 1, buffer, 1, &out_len)); ++ EXPECT_EQ(2U, out_len); ++ for (size_t i = 0; i < std::size(buffer); ++i) ++ EXPECT_EQ(0, buffer[i]); ++ ++ ASSERT_TRUE(FPDF_StructElement_Attr_GetName(attr, 1, buffer, ++ sizeof(buffer), &out_len)); ++ EXPECT_EQ(2U, out_len); ++ EXPECT_STREQ("O", buffer); ++ EXPECT_EQ(FPDF_OBJECT_NAME, ++ FPDF_StructElement_Attr_GetType(attr, buffer)); ++ ++ unsigned short str_val[12] = {}; ++ ASSERT_TRUE(FPDF_StructElement_Attr_GetStringValue( ++ attr, buffer, str_val, sizeof(str_val), &out_len)); ++ EXPECT_EQ(12U, out_len); ++ EXPECT_EQ(L"Table", GetPlatformWString(str_val)); ++ ++ memset(buffer, 0, sizeof(buffer)); ++ ASSERT_TRUE(FPDF_StructElement_Attr_GetName(attr, 0, buffer, ++ sizeof(buffer), &out_len)); ++ EXPECT_EQ(8U, out_len); ++ EXPECT_STREQ("ColSpan", buffer); ++ EXPECT_EQ(FPDF_OBJECT_NUMBER, ++ FPDF_StructElement_Attr_GetType(attr, buffer)); ++ float num_val; ++ ASSERT_TRUE( ++ FPDF_StructElement_Attr_GetNumberValue(attr, buffer, &num_val)); ++ EXPECT_FLOAT_EQ(2.0f, num_val); ++ } ++ ++ { ++ FPDF_STRUCTELEMENT tr = FPDF_StructElement_GetChildAtIndex(table, 1); ++ ASSERT_TRUE(tr); ++ ++ ASSERT_EQ(1, FPDF_StructElement_GetAttributeCount(tr)); ++ // nullptr when index out of range ++ ASSERT_EQ(nullptr, FPDF_StructElement_GetAttributeAtIndex(tr, 1)); ++ ++ ASSERT_EQ(2, FPDF_StructElement_CountChildren(tr)); ++ FPDF_STRUCTELEMENT td = FPDF_StructElement_GetChildAtIndex(tr, 1); ++ ASSERT_TRUE(td); ++ { ++ ASSERT_EQ(1, FPDF_StructElement_GetAttributeCount(td)); ++ FPDF_STRUCTELEMENT_ATTR attr = ++ FPDF_StructElement_GetAttributeAtIndex(td, 0); ++ ASSERT_TRUE(attr); ++ ASSERT_EQ(3, FPDF_StructElement_Attr_GetCount(attr)); ++ // Test string and blob type ++ { ++ char buffer[16] = {}; ++ unsigned long out_len = ULONG_MAX; ++ ASSERT_TRUE(FPDF_StructElement_Attr_GetName( ++ attr, 0, buffer, sizeof(buffer), &out_len)); ++ EXPECT_EQ(8U, out_len); ++ EXPECT_STREQ("ColProp", buffer); ++ ++ EXPECT_EQ(FPDF_OBJECT_STRING, ++ FPDF_StructElement_Attr_GetType(attr, buffer)); ++ ++ unsigned short str_val[12] = {}; ++ ASSERT_TRUE(FPDF_StructElement_Attr_GetStringValue( ++ attr, buffer, str_val, sizeof(str_val), &out_len)); ++ EXPECT_EQ(8U, out_len); ++ EXPECT_EQ(L"Sum", GetPlatformWString(str_val)); ++ ++ char blob_val[3] = {}; ++ ASSERT_TRUE(FPDF_StructElement_Attr_GetBlobValue( ++ attr, buffer, blob_val, sizeof(blob_val), &out_len)); ++ EXPECT_EQ(3U, out_len); ++ EXPECT_EQ('S', blob_val[0]); ++ EXPECT_EQ('u', blob_val[1]); ++ EXPECT_EQ('m', blob_val[2]); ++ } ++ ++ // Test boolean type ++ { ++ char buffer[16] = {}; ++ unsigned long out_len = ULONG_MAX; ++ ASSERT_TRUE(FPDF_StructElement_Attr_GetName( ++ attr, 1, buffer, sizeof(buffer), &out_len)); ++ EXPECT_EQ(7U, out_len); ++ EXPECT_STREQ("CurUSD", buffer); ++ ++ EXPECT_EQ(FPDF_OBJECT_BOOLEAN, ++ FPDF_StructElement_Attr_GetType(attr, buffer)); ++ FPDF_BOOL val; ++ ASSERT_TRUE( ++ FPDF_StructElement_Attr_GetBooleanValue(attr, buffer, &val)); ++ EXPECT_TRUE(val); ++ } ++ } ++ } ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFStructTreeEmbedderTest, GetStructTreeForNestedTaggedPDF) { ++ ASSERT_TRUE(OpenDocument("tagged_nested.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ // This call should not crash. https://crbug.com/pdfium/1480 ++ ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page)); ++ ASSERT_TRUE(struct_tree); ++ } ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFStructTreeEmbedderTest, MarkedContentReferenceAndObjectReference) { ++ ASSERT_TRUE(OpenDocument("tagged_mcr_objr.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page)); ++ ASSERT_TRUE(struct_tree); ++ ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get())); ++ ++ FPDF_STRUCTELEMENT object8 = ++ FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0); ++ ASSERT_TRUE(object8); ++ unsigned short buffer[12]; ++ ASSERT_EQ(18U, FPDF_StructElement_GetType(object8, buffer, sizeof(buffer))); ++ EXPECT_EQ(L"Document", GetPlatformWString(buffer)); ++ EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object8)); ++ ASSERT_EQ(2, FPDF_StructElement_CountChildren(object8)); ++ ++ // First branch. 10 -> 12 -> 13 -> Inline dict. ++ FPDF_STRUCTELEMENT object10 = ++ FPDF_StructElement_GetChildAtIndex(object8, 0); ++ ASSERT_TRUE(object10); ++ ASSERT_EQ(20U, ++ FPDF_StructElement_GetType(object10, buffer, sizeof(buffer))); ++ EXPECT_EQ(L"NonStruct", GetPlatformWString(buffer)); ++ EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object10)); ++ ASSERT_EQ(1, FPDF_StructElement_CountChildren(object10)); ++ ++ FPDF_STRUCTELEMENT object12 = ++ FPDF_StructElement_GetChildAtIndex(object10, 0); ++ ASSERT_TRUE(object12); ++ ASSERT_EQ(4U, FPDF_StructElement_GetType(object12, buffer, sizeof(buffer))); ++ EXPECT_EQ(L"P", GetPlatformWString(buffer)); ++ EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object12)); ++ ASSERT_EQ(1, FPDF_StructElement_CountChildren(object12)); ++ ++ FPDF_STRUCTELEMENT object13 = ++ FPDF_StructElement_GetChildAtIndex(object12, 0); ++ ASSERT_TRUE(object13); ++ ASSERT_EQ(20U, ++ FPDF_StructElement_GetType(object13, buffer, sizeof(buffer))); ++ EXPECT_EQ(L"NonStruct", GetPlatformWString(buffer)); ++ EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object13)); ++ ASSERT_EQ(1, FPDF_StructElement_CountChildren(object13)); ++ ++ // TODO(crbug.com/pdfium/672): Fetch this child element. ++ EXPECT_FALSE(FPDF_StructElement_GetChildAtIndex(object13, 0)); ++ ++ // Second branch. 11 -> 14 -> Inline dict. ++ // -> 15 -> Inline dict. ++ FPDF_STRUCTELEMENT object11 = ++ FPDF_StructElement_GetChildAtIndex(object8, 1); ++ ASSERT_TRUE(object11); ++ ASSERT_EQ(4U, FPDF_StructElement_GetType(object11, buffer, sizeof(buffer))); ++ EXPECT_EQ(L"P", GetPlatformWString(buffer)); ++ EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object11)); ++ ASSERT_EQ(1, FPDF_StructElement_CountChildren(object11)); ++ ++ FPDF_STRUCTELEMENT object14 = ++ FPDF_StructElement_GetChildAtIndex(object11, 0); ++ ASSERT_TRUE(object14); ++ ASSERT_EQ(20U, ++ FPDF_StructElement_GetType(object14, buffer, sizeof(buffer))); ++ EXPECT_EQ(L"NonStruct", GetPlatformWString(buffer)); ++ EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object14)); ++ ASSERT_EQ(2, FPDF_StructElement_CountChildren(object14)); ++ ++ // TODO(crbug.com/pdfium/672): Object 15 should be at index 1. ++ EXPECT_FALSE(FPDF_StructElement_GetChildAtIndex(object14, 1)); ++ FPDF_STRUCTELEMENT object15 = ++ FPDF_StructElement_GetChildAtIndex(object14, 0); ++ ASSERT_TRUE(object15); ++ ASSERT_EQ(20U, ++ FPDF_StructElement_GetType(object15, buffer, sizeof(buffer))); ++ EXPECT_EQ(L"NonStruct", GetPlatformWString(buffer)); ++ EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object15)); ++ ASSERT_EQ(1, FPDF_StructElement_CountChildren(object15)); ++ ++ // TODO(crbug.com/pdfium/672): Fetch this child element. ++ EXPECT_FALSE(FPDF_StructElement_GetChildAtIndex(object15, 0)); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFStructTreeEmbedderTest, Bug1768) { ++ ASSERT_TRUE(OpenDocument("bug_1768.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page)); ++ ASSERT_TRUE(struct_tree); ++ ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get())); ++ ++ // TODO(crbug.com/pdfium/1768): Fetch this child element. Then consider ++ // writing more of the test to make sure other elements in the tree can be ++ // fetched correctly as well. ++ EXPECT_FALSE(FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0)); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFStructTreeEmbedderTest, Bug1296920) { ++ ASSERT_TRUE(OpenDocument("bug_1296920.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page)); ++ ASSERT_TRUE(struct_tree); ++ ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get())); ++ ++ // Destroying this tree should not crash. + } + + UnloadPage(page); +diff --git a/fpdfsdk/fpdf_sysfontinfo.cpp b/fpdfsdk/fpdf_sysfontinfo.cpp +index 96ceef23a..9c38bfacb 100644 +--- a/fpdfsdk/fpdf_sysfontinfo.cpp ++++ b/fpdfsdk/fpdf_sysfontinfo.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,32 +11,57 @@ + #include + + #include "core/fxcrt/fx_codepage.h" ++#include "core/fxcrt/stl_util.h" ++#include "core/fxcrt/unowned_ptr.h" + #include "core/fxge/cfx_font.h" + #include "core/fxge/cfx_fontmapper.h" + #include "core/fxge/cfx_fontmgr.h" + #include "core/fxge/cfx_gemodule.h" + #include "core/fxge/fx_font.h" + #include "core/fxge/systemfontinfo_iface.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/numerics/safe_conversions.h" + +-static_assert(FXFONT_ANSI_CHARSET == FX_CHARSET_ANSI, "Charset must match"); +-static_assert(FXFONT_DEFAULT_CHARSET == FX_CHARSET_Default, ++#ifdef PDF_ENABLE_XFA ++#include "xfa/fgas/font/cfgas_fontmgr.h" ++#include "xfa/fgas/font/cfgas_gemodule.h" ++#endif ++ ++static_assert(FXFONT_ANSI_CHARSET == static_cast(FX_Charset::kANSI), ++ "Charset must match"); ++static_assert(FXFONT_DEFAULT_CHARSET == static_cast(FX_Charset::kDefault), ++ "Charset must match"); ++static_assert(FXFONT_SYMBOL_CHARSET == static_cast(FX_Charset::kSymbol), ++ "Charset must match"); ++static_assert(FXFONT_SHIFTJIS_CHARSET == ++ static_cast(FX_Charset::kShiftJIS), ++ "Charset must match"); ++static_assert(FXFONT_HANGEUL_CHARSET == static_cast(FX_Charset::kHangul), + "Charset must match"); +-static_assert(FXFONT_SYMBOL_CHARSET == FX_CHARSET_Symbol, "Charset must match"); +-static_assert(FXFONT_SHIFTJIS_CHARSET == FX_CHARSET_ShiftJIS, ++static_assert(FXFONT_GB2312_CHARSET == ++ static_cast(FX_Charset::kChineseSimplified), + "Charset must match"); +-static_assert(FXFONT_HANGEUL_CHARSET == FX_CHARSET_Hangul, ++static_assert(FXFONT_CHINESEBIG5_CHARSET == ++ static_cast(FX_Charset::kChineseTraditional), + "Charset must match"); +-static_assert(FXFONT_GB2312_CHARSET == FX_CHARSET_ChineseSimplified, ++static_assert(FXFONT_GREEK_CHARSET == ++ static_cast(FX_Charset::kMSWin_Greek), + "Charset must match"); +-static_assert(FXFONT_CHINESEBIG5_CHARSET == FX_CHARSET_ChineseTraditional, ++static_assert(FXFONT_VIETNAMESE_CHARSET == ++ static_cast(FX_Charset::kMSWin_Vietnamese), + "Charset must match"); +-static_assert(FXFONT_ARABIC_CHARSET == FX_CHARSET_MSWin_Arabic, ++static_assert(FXFONT_HEBREW_CHARSET == ++ static_cast(FX_Charset::kMSWin_Hebrew), + "Charset must match"); +-static_assert(FXFONT_CYRILLIC_CHARSET == FX_CHARSET_MSWin_Cyrillic, ++static_assert(FXFONT_ARABIC_CHARSET == ++ static_cast(FX_Charset::kMSWin_Arabic), ++ "Charset must match"); ++static_assert(FXFONT_CYRILLIC_CHARSET == ++ static_cast(FX_Charset::kMSWin_Cyrillic), ++ "Charset must match"); ++static_assert(FXFONT_THAI_CHARSET == static_cast(FX_Charset::kThai), + "Charset must match"); + static_assert(FXFONT_EASTERNEUROPEAN_CHARSET == +- FX_CHARSET_MSWin_EasternEuropean, ++ static_cast(FX_Charset::kMSWin_EasternEuropean), + "Charset must match"); + static_assert(offsetof(CFX_Font::CharsetFontMap, charset) == + offsetof(FPDF_CharsetFontMap, charset), +@@ -65,36 +90,36 @@ class CFX_ExternalFontInfo final : public SystemFontInfoIface { + + void* MapFont(int weight, + bool bItalic, +- int charset, ++ FX_Charset charset, + int pitch_family, +- const char* family) override { ++ const ByteString& face) override { + if (!m_pInfo->MapFont) + return nullptr; + + int iExact; +- return m_pInfo->MapFont(m_pInfo, weight, bItalic, charset, pitch_family, +- family, &iExact); ++ return m_pInfo->MapFont(m_pInfo, weight, bItalic, static_cast(charset), ++ pitch_family, face.c_str(), &iExact); + } + +- void* GetFont(const char* family) override { ++ void* GetFont(const ByteString& family) override { + if (!m_pInfo->GetFont) + return nullptr; +- return m_pInfo->GetFont(m_pInfo, family); ++ return m_pInfo->GetFont(m_pInfo, family.c_str()); + } + +- uint32_t GetFontData(void* hFont, +- uint32_t table, +- pdfium::span buffer) override { ++ size_t GetFontData(void* hFont, ++ uint32_t table, ++ pdfium::span buffer) override { + if (!m_pInfo->GetFontData) + return 0; + return m_pInfo->GetFontData(m_pInfo, hFont, table, buffer.data(), +- buffer.size()); ++ fxcrt::CollectionSize(buffer)); + } + + bool GetFaceName(void* hFont, ByteString* name) override { + if (!m_pInfo->GetFaceName) + return false; +- uint32_t size = m_pInfo->GetFaceName(m_pInfo, hFont, nullptr, 0); ++ unsigned long size = m_pInfo->GetFaceName(m_pInfo, hFont, nullptr, 0); + if (size == 0) + return false; + char* buffer = FX_Alloc(char, size); +@@ -104,11 +129,11 @@ class CFX_ExternalFontInfo final : public SystemFontInfoIface { + return true; + } + +- bool GetFontCharset(void* hFont, int* charset) override { ++ bool GetFontCharset(void* hFont, FX_Charset* charset) override { + if (!m_pInfo->GetFontCharset) + return false; + +- *charset = m_pInfo->GetFontCharset(m_pInfo, hFont); ++ *charset = FX_GetCharsetFromInt(m_pInfo->GetFontCharset(m_pInfo, hFont)); + return true; + } + +@@ -118,14 +143,14 @@ class CFX_ExternalFontInfo final : public SystemFontInfoIface { + } + + private: +- FPDF_SYSFONTINFO* const m_pInfo; ++ UnownedPtr const m_pInfo; + }; + + FPDF_EXPORT void FPDF_CALLCONV FPDF_AddInstalledFont(void* mapper, + const char* face, + int charset) { + CFX_FontMapper* pMapper = static_cast(mapper); +- pMapper->AddInstalledFont(face, charset); ++ pMapper->AddInstalledFont(face, FX_GetCharsetFromInt(charset)); + } + + FPDF_EXPORT void FPDF_CALLCONV +@@ -133,12 +158,16 @@ FPDF_SetSystemFontInfo(FPDF_SYSFONTINFO* pFontInfoExt) { + if (pFontInfoExt->version != 1) + return; + +- CFX_GEModule::Get()->GetFontMgr()->SetSystemFontInfo( +- pdfium::MakeUnique(pFontInfoExt)); ++ CFX_GEModule::Get()->GetFontMgr()->GetBuiltinMapper()->SetSystemFontInfo( ++ std::make_unique(pFontInfoExt)); ++ ++#ifdef PDF_ENABLE_XFA ++ CFGAS_GEModule::Get()->GetFontMgr()->EnumFonts(); ++#endif + } + + FPDF_EXPORT const FPDF_CharsetFontMap* FPDF_CALLCONV FPDF_GetDefaultTTFMap() { +- return reinterpret_cast(CFX_Font::defaultTTFMap); ++ return reinterpret_cast(CFX_Font::kDefaultTTFMap); + } + + struct FPDF_SYSFONTINFO_DEFAULT final : public FPDF_SYSFONTINFO { +@@ -147,7 +176,7 @@ struct FPDF_SYSFONTINFO_DEFAULT final : public FPDF_SYSFONTINFO { + + static void DefaultRelease(struct _FPDF_SYSFONTINFO* pThis) { + auto* pDefault = static_cast(pThis); +- delete pDefault->m_pFontInfo.Release(); ++ delete pDefault->m_pFontInfo.ExtractAsDangling(); + } + + static void DefaultEnumFonts(struct _FPDF_SYSFONTINFO* pThis, void* pMapper) { +@@ -163,8 +192,8 @@ static void* DefaultMapFont(struct _FPDF_SYSFONTINFO* pThis, + const char* family, + int* bExact) { + auto* pDefault = static_cast(pThis); +- return pDefault->m_pFontInfo->MapFont(weight, !!bItalic, charset, +- pitch_family, family); ++ return pDefault->m_pFontInfo->MapFont( ++ weight, !!bItalic, FX_GetCharsetFromInt(charset), pitch_family, family); + } + + void* DefaultGetFont(struct _FPDF_SYSFONTINFO* pThis, const char* family) { +@@ -178,7 +207,8 @@ static unsigned long DefaultGetFontData(struct _FPDF_SYSFONTINFO* pThis, + unsigned char* buffer, + unsigned long buf_size) { + auto* pDefault = static_cast(pThis); +- return pDefault->m_pFontInfo->GetFontData(hFont, table, {buffer, buf_size}); ++ return pdfium::base::checked_cast( ++ pDefault->m_pFontInfo->GetFontData(hFont, table, {buffer, buf_size})); + } + + static unsigned long DefaultGetFaceName(struct _FPDF_SYSFONTINFO* pThis, +@@ -189,20 +219,21 @@ static unsigned long DefaultGetFaceName(struct _FPDF_SYSFONTINFO* pThis, + auto* pDefault = static_cast(pThis); + if (!pDefault->m_pFontInfo->GetFaceName(hFont, &name)) + return 0; +- if (name.GetLength() >= static_cast(buf_size)) +- return name.GetLength() + 1; + +- strncpy(buffer, name.c_str(), +- (name.GetLength() + 1) * sizeof(ByteString::CharType)); +- return name.GetLength() + 1; ++ const unsigned long copy_length = ++ pdfium::base::checked_cast(name.GetLength() + 1); ++ if (copy_length <= buf_size) ++ strncpy(buffer, name.c_str(), copy_length * sizeof(ByteString::CharType)); ++ ++ return copy_length; + } + + static int DefaultGetFontCharset(struct _FPDF_SYSFONTINFO* pThis, void* hFont) { +- int charset; ++ FX_Charset charset; + auto* pDefault = static_cast(pThis); + if (!pDefault->m_pFontInfo->GetFontCharset(hFont, &charset)) + return 0; +- return charset; ++ return static_cast(charset); + } + + static void DefaultDeleteFont(struct _FPDF_SYSFONTINFO* pThis, void* hFont) { +@@ -212,7 +243,7 @@ static void DefaultDeleteFont(struct _FPDF_SYSFONTINFO* pThis, void* hFont) { + + FPDF_EXPORT FPDF_SYSFONTINFO* FPDF_CALLCONV FPDF_GetDefaultSystemFontInfo() { + std::unique_ptr pFontInfo = +- SystemFontInfoIface::CreateDefault(nullptr); ++ CFX_GEModule::Get()->GetPlatform()->CreateDefaultSystemFontInfo(); + if (!pFontInfo) + return nullptr; + +diff --git a/fpdfsdk/fpdf_sysfontinfo_embeddertest.cpp b/fpdfsdk/fpdf_sysfontinfo_embeddertest.cpp +index 61cec049c..24df525d1 100644 +--- a/fpdfsdk/fpdf_sysfontinfo_embeddertest.cpp ++++ b/fpdfsdk/fpdf_sysfontinfo_embeddertest.cpp +@@ -1,12 +1,15 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "public/fpdf_sysfontinfo.h" + ++#include ++ + #include "testing/embedder_test.h" ++#include "testing/embedder_test_environment.h" ++#include "testing/gmock/include/gmock/gmock.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/stl_util.h" + + namespace { + +@@ -73,6 +76,15 @@ class FPDFUnavailableSysFontInfoEmbedderTest : public EmbedderTest { + FPDF_SetSystemFontInfo(&font_info_); + } + ++ void TearDown() override { ++ EmbedderTest::TearDown(); ++ ++ // Bouncing the library is the only reliable way to undo the ++ // FPDF_SetSystemFontInfo() call at the moment. ++ EmbedderTestEnvironment::GetInstance()->TearDown(); ++ EmbedderTestEnvironment::GetInstance()->SetUp(); ++ } ++ + FPDF_SYSFONTINFO font_info_; + }; + +@@ -90,7 +102,15 @@ class FPDFSysFontInfoEmbedderTest : public EmbedderTest { + + void TearDown() override { + EmbedderTest::TearDown(); ++ ++ // Bouncing the library is the only reliable way to undo the ++ // FPDF_SetSystemFontInfo() call at the moment. ++ EmbedderTestEnvironment::GetInstance()->TearDown(); ++ ++ // After shutdown, it is safe to release the font info. + FPDF_FreeDefaultSystemFontInfo(font_info_); ++ ++ EmbedderTestEnvironment::GetInstance()->SetUp(); + } + + FPDF_SYSFONTINFO* font_info_; +@@ -125,31 +145,26 @@ TEST_F(FPDFSysFontInfoEmbedderTest, DefaultSystemFontInfo) { + } + + TEST_F(FPDFSysFontInfoEmbedderTest, DefaultTTFMap) { +- static const int kAllowedCharsets[] = { +- FXFONT_ANSI_CHARSET, FXFONT_DEFAULT_CHARSET, +- FXFONT_SYMBOL_CHARSET, FXFONT_SHIFTJIS_CHARSET, ++ static constexpr int kExpectedCharsets[] = { ++ FXFONT_ANSI_CHARSET, FXFONT_SHIFTJIS_CHARSET, + FXFONT_HANGEUL_CHARSET, FXFONT_GB2312_CHARSET, + FXFONT_CHINESEBIG5_CHARSET, FXFONT_ARABIC_CHARSET, + FXFONT_CYRILLIC_CHARSET, FXFONT_EASTERNEUROPEAN_CHARSET, + }; +- std::set seen_charsets; ++ std::vector charsets; + + const FPDF_CharsetFontMap* cfmap = FPDF_GetDefaultTTFMap(); + ASSERT_TRUE(cfmap); + + // Stop at either end mark. + while (cfmap->charset != -1 && cfmap->fontname) { +- // Only returns values described as legitimate in public header. +- EXPECT_TRUE(pdfium::ContainsValue(kAllowedCharsets, cfmap->charset)) +- << " for " << cfmap->charset; +- +- // Duplicates are not allowed. +- EXPECT_TRUE(seen_charsets.insert(cfmap->charset).second) +- << " for " << cfmap->charset; ++ charsets.push_back(cfmap->charset); + ++cfmap; + } + + // Confirm end marks only occur as a pair. + EXPECT_EQ(cfmap->charset, -1); + EXPECT_EQ(cfmap->fontname, nullptr); ++ ++ EXPECT_THAT(charsets, testing::UnorderedElementsAreArray(kExpectedCharsets)); + } +diff --git a/fpdfsdk/fpdf_text.cpp b/fpdfsdk/fpdf_text.cpp +index 5edbf27c2..d022ac355 100644 +--- a/fpdfsdk/fpdf_text.cpp ++++ b/fpdfsdk/fpdf_text.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,7 +11,6 @@ + #include + + #include "build/build_config.h" +-#include "core/fpdfapi/font/cpdf_cidfont.h" + #include "core/fpdfapi/font/cpdf_font.h" + #include "core/fpdfapi/page/cpdf_page.h" + #include "core/fpdfapi/page/cpdf_textobject.h" +@@ -19,14 +18,10 @@ + #include "core/fpdftext/cpdf_linkextract.h" + #include "core/fpdftext/cpdf_textpage.h" + #include "core/fpdftext/cpdf_textpagefind.h" ++#include "core/fxcrt/stl_util.h" + #include "fpdfsdk/cpdfsdk_helpers.h" ++#include "third_party/base/check_op.h" + #include "third_party/base/numerics/safe_conversions.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" +- +-#if defined(OS_WIN) +-#include +-#endif + + namespace { + +@@ -49,7 +44,7 @@ FPDF_EXPORT FPDF_TEXTPAGE FPDF_CALLCONV FPDFText_LoadPage(FPDF_PAGE page) { + + CPDF_ViewerPreferences viewRef(pPDFPage->GetDocument()); + auto textpage = +- pdfium::MakeUnique(pPDFPage, viewRef.IsDirectionR2L()); ++ std::make_unique(pPDFPage, viewRef.IsDirectionR2L()); + + // Caller takes ownership. + return FPDFTextPageFromCPDFTextPage(textpage.release()); +@@ -62,11 +57,8 @@ FPDF_EXPORT void FPDF_CALLCONV FPDFText_ClosePage(FPDF_TEXTPAGE text_page) { + } + + FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountChars(FPDF_TEXTPAGE text_page) { +- if (!text_page) +- return -1; +- + CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page); +- return textpage->CountChars(); ++ return textpage ? textpage->CountChars() : -1; + } + + FPDF_EXPORT unsigned int FPDF_CALLCONV +@@ -79,6 +71,26 @@ FPDFText_GetUnicode(FPDF_TEXTPAGE text_page, int index) { + return charinfo.m_Unicode; + } + ++FPDF_EXPORT int FPDF_CALLCONV FPDFText_IsGenerated(FPDF_TEXTPAGE text_page, ++ int index) { ++ CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index); ++ if (!textpage) ++ return -1; ++ ++ const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index); ++ return charinfo.m_CharType == CPDF_TextPage::CharType::kGenerated ? 1 : 0; ++} ++ ++FPDF_EXPORT int FPDF_CALLCONV ++FPDFText_HasUnicodeMapError(FPDF_TEXTPAGE text_page, int index) { ++ CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index); ++ if (!textpage) ++ return -1; ++ ++ const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index); ++ return charinfo.m_CharType == CPDF_TextPage::CharType::kNotUnicode; ++} ++ + FPDF_EXPORT double FPDF_CALLCONV FPDFText_GetFontSize(FPDF_TEXTPAGE text_page, + int index) { + CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index); +@@ -107,7 +119,8 @@ FPDFText_GetFontInfo(FPDF_TEXTPAGE text_page, + *flags = font->GetFontFlags(); + + ByteString basefont = font->GetBaseFontName(); +- unsigned long length = basefont.GetLength() + 1; ++ const unsigned long length = ++ pdfium::base::checked_cast(basefont.GetLength() + 1); + if (buffer && buflen >= length) + memcpy(buffer, basefont.c_str(), length); + +@@ -205,7 +218,7 @@ FPDF_EXPORT float FPDF_CALLCONV FPDFText_GetCharAngle(FPDF_TEXTPAGE text_page, + // Calculate the angle of the vector + float angle = atan2f(charinfo.m_Matrix.c, charinfo.m_Matrix.a); + if (angle < 0) +- angle = 2 * FX_PI + angle; ++ angle = 2 * FXSYS_PI + angle; + + return angle; + } +@@ -240,46 +253,7 @@ FPDFText_GetLooseCharBox(FPDF_TEXTPAGE text_page, int index, FS_RECTF* rect) { + if (!textpage) + return false; + +- const CPDF_TextPage::CharInfo& charinfo = textpage->GetCharInfo(index); +- float font_size = textpage->GetCharFontSize(index); +- +- if (charinfo.m_pTextObj && !IsFloatZero(font_size)) { +- bool is_vert_writing = charinfo.m_pTextObj->GetFont()->IsVertWriting(); +- if (is_vert_writing && charinfo.m_pTextObj->GetFont()->IsCIDFont()) { +- CPDF_CIDFont* pCIDFont = charinfo.m_pTextObj->GetFont()->AsCIDFont(); +- uint16_t cid = pCIDFont->CIDFromCharCode(charinfo.m_CharCode); +- +- short vx; +- short vy; +- pCIDFont->GetVertOrigin(cid, vx, vy); +- double offsetx = (vx - 500) * font_size / 1000.0; +- double offsety = vy * font_size / 1000.0; +- short vert_width = pCIDFont->GetVertWidth(cid); +- double height = vert_width * font_size / 1000.0; +- +- rect->left = charinfo.m_Origin.x + offsetx; +- rect->right = rect->left + font_size; +- rect->bottom = charinfo.m_Origin.y + offsety; +- rect->top = rect->bottom + height; +- return true; +- } +- +- int ascent = charinfo.m_pTextObj->GetFont()->GetTypeAscent(); +- int descent = charinfo.m_pTextObj->GetFont()->GetTypeDescent(); +- if (ascent != descent) { +- float width = charinfo.m_pTextObj->GetCharWidth(charinfo.m_CharCode); +- float font_scale = font_size / (ascent - descent); +- +- rect->left = charinfo.m_Origin.x; +- rect->right = charinfo.m_Origin.x + (is_vert_writing ? -width : width); +- rect->bottom = charinfo.m_Origin.y + descent * font_scale; +- rect->top = charinfo.m_Origin.y + ascent * font_scale; +- return true; +- } +- } +- +- // Fallback to the tight bounds in empty text scenarios, or bad font metrics +- *rect = FSRectFFromCFXFloatRect(charinfo.m_CharBox); ++ *rect = FSRectFFromCFXFloatRect(textpage->GetCharLooseBounds(index)); + return true; + } + +@@ -319,10 +293,10 @@ FPDFText_GetCharIndexAtPos(FPDF_TEXTPAGE text_page, + double y, + double xTolerance, + double yTolerance) { +- if (!text_page) ++ CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page); ++ if (!textpage) + return -3; + +- CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page); + return textpage->GetIndexAtPos( + CFX_PointF(static_cast(x), static_cast(y)), + CFX_SizeF(static_cast(xTolerance), +@@ -333,10 +307,10 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetText(FPDF_TEXTPAGE page, + int start_index, + int char_count, + unsigned short* result) { +- if (!page || start_index < 0 || char_count < 0 || !result) ++ CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(page); ++ if (!textpage || start_index < 0 || char_count < 0 || !result) + return 0; + +- CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(page); + int char_available = textpage->CountChars() - start_index; + if (char_available <= 0) + return 0; +@@ -357,21 +331,20 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetText(FPDF_TEXTPAGE page, + // the number of items to stay the same. + ByteString byte_str = str.ToUTF16LE(); + size_t byte_str_len = byte_str.GetLength(); +- int ret_count = byte_str_len / kBytesPerCharacter; ++ size_t ret_count = byte_str_len / kBytesPerCharacter; ++ ++ // +1 to account for the NUL terminator. ++ DCHECK_LE(ret_count, static_cast(char_count) + 1); + +- ASSERT(ret_count <= char_count + 1); // +1 to account for the NUL terminator. + memcpy(result, byte_str.c_str(), byte_str_len); +- return ret_count; ++ return pdfium::base::checked_cast(ret_count); + } + + FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountRects(FPDF_TEXTPAGE text_page, + int start, + int count) { +- if (!text_page) +- return 0; +- + CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page); +- return textpage->CountRects(start, count); ++ return textpage ? textpage->CountRects(start, count) : 0; + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetRect(FPDF_TEXTPAGE text_page, +@@ -380,10 +353,10 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFText_GetRect(FPDF_TEXTPAGE text_page, + double* top, + double* right, + double* bottom) { +- if (!text_page) ++ CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page); ++ if (!textpage) + return false; + +- CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page); + CFX_FloatRect rect; + bool result = textpage->GetRect(rect_index, &rect); + +@@ -401,22 +374,22 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetBoundedText(FPDF_TEXTPAGE text_page, + double bottom, + unsigned short* buffer, + int buflen) { +- if (!text_page) ++ CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page); ++ if (!textpage) + return 0; + +- CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page); + CFX_FloatRect rect((float)left, (float)bottom, (float)right, (float)top); + WideString str = textpage->GetTextByRect(rect); + + if (buflen <= 0 || !buffer) +- return str.GetLength(); ++ return pdfium::base::checked_cast(str.GetLength()); + + ByteString cbUTF16Str = str.ToUTF16LE(); +- int len = cbUTF16Str.GetLength() / sizeof(unsigned short); ++ int len = pdfium::base::checked_cast(cbUTF16Str.GetLength()) / ++ sizeof(unsigned short); + int size = buflen > len ? len : buflen; + memcpy(buffer, cbUTF16Str.c_str(), size * sizeof(unsigned short)); + cbUTF16Str.ReleaseBuffer(size * sizeof(unsigned short)); +- + return size; + } + +@@ -425,7 +398,8 @@ FPDFText_FindStart(FPDF_TEXTPAGE text_page, + FPDF_WIDESTRING findwhat, + unsigned long flags, + int start_index) { +- if (!text_page) ++ CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page); ++ if (!textpage) + return nullptr; + + CPDF_TextPageFind::Options options; +@@ -433,9 +407,8 @@ FPDFText_FindStart(FPDF_TEXTPAGE text_page, + options.bMatchWholeWord = !!(flags & FPDF_MATCHWHOLEWORD); + options.bConsecutive = !!(flags & FPDF_CONSECUTIVE); + auto find = CPDF_TextPageFind::Create( +- CPDFTextPageFromFPDFTextPage(text_page), +- WideStringFromFPDFWideString(findwhat), options, +- start_index >= 0 ? Optional(start_index) : pdfium::nullopt); ++ textpage, WideStringFromFPDFWideString(findwhat), options, ++ start_index >= 0 ? absl::optional(start_index) : absl::nullopt); + + // Caller takes ownership. + return FPDFSchHandleFromCPDFTextPageFind(find.release()); +@@ -486,15 +459,15 @@ FPDF_EXPORT void FPDF_CALLCONV FPDFText_FindClose(FPDF_SCHHANDLE handle) { + // web link + FPDF_EXPORT FPDF_PAGELINK FPDF_CALLCONV + FPDFLink_LoadWebLinks(FPDF_TEXTPAGE text_page) { +- if (!text_page) ++ CPDF_TextPage* textpage = CPDFTextPageFromFPDFTextPage(text_page); ++ if (!textpage) + return nullptr; + +- CPDF_TextPage* pPage = CPDFTextPageFromFPDFTextPage(text_page); +- auto pageLink = pdfium::MakeUnique(pPage); +- pageLink->ExtractLinks(); ++ auto pagelink = std::make_unique(textpage); ++ pagelink->ExtractLinks(); + + // Caller takes ownership. +- return FPDFPageLinkFromCPDFLinkExtract(pageLink.release()); ++ return FPDFPageLinkFromCPDFLinkExtract(pagelink.release()); + } + + FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountWebLinks(FPDF_PAGELINK link_page) { +@@ -515,7 +488,8 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFLink_GetURL(FPDF_PAGELINK link_page, + wsUrl = pageLink->GetURL(link_index); + } + ByteString cbUTF16URL = wsUrl.ToUTF16LE(); +- int required = cbUTF16URL.GetLength() / sizeof(unsigned short); ++ int required = pdfium::base::checked_cast(cbUTF16URL.GetLength() / ++ sizeof(unsigned short)); + if (!buffer || buflen <= 0) + return required; + +@@ -533,7 +507,7 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountRects(FPDF_PAGELINK link_page, + return 0; + + CPDF_LinkExtract* pageLink = CPDFLinkExtractFromFPDFPageLink(link_page); +- return pdfium::CollectionSize(pageLink->GetRects(link_index)); ++ return fxcrt::CollectionSize(pageLink->GetRects(link_index)); + } + + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetRect(FPDF_PAGELINK link_page, +@@ -548,7 +522,7 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetRect(FPDF_PAGELINK link_page, + + CPDF_LinkExtract* pageLink = CPDFLinkExtractFromFPDFPageLink(link_page); + std::vector rectArray = pageLink->GetRects(link_index); +- if (rect_index >= pdfium::CollectionSize(rectArray)) ++ if (rect_index >= fxcrt::CollectionSize(rectArray)) + return false; + + *left = rectArray[rect_index].left; +@@ -567,7 +541,14 @@ FPDFLink_GetTextRange(FPDF_PAGELINK link_page, + return false; + + CPDF_LinkExtract* page_link = CPDFLinkExtractFromFPDFPageLink(link_page); +- return page_link->GetTextRange(link_index, start_char_index, char_count); ++ auto maybe_range = page_link->GetTextRange(link_index); ++ if (!maybe_range.has_value()) ++ return false; ++ ++ *start_char_index = ++ pdfium::base::checked_cast(maybe_range.value().m_Start); ++ *char_count = pdfium::base::checked_cast(maybe_range.value().m_Count); ++ return true; + } + + FPDF_EXPORT void FPDF_CALLCONV FPDFLink_CloseWebLinks(FPDF_PAGELINK link_page) { +diff --git a/fpdfsdk/fpdf_text_embeddertest.cpp b/fpdfsdk/fpdf_text_embeddertest.cpp +index 06ff4cc51..8d8b4ee11 100644 +--- a/fpdfsdk/fpdf_text_embeddertest.cpp ++++ b/fpdfsdk/fpdf_text_embeddertest.cpp +@@ -1,16 +1,15 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include +-#include + #include + #include + + #include "build/build_config.h" +-#include "core/fxcrt/fx_memory.h" + #include "core/fxge/fx_font.h" + #include "public/cpp/fpdf_scopers.h" ++#include "public/fpdf_doc.h" + #include "public/fpdf_text.h" + #include "public/fpdf_transformpage.h" + #include "public/fpdfview.h" +@@ -21,7 +20,7 @@ + namespace { + + constexpr char kHelloGoodbyeText[] = "Hello, world!\r\nGoodbye, world!"; +-constexpr int kHelloGoodbyeTextSize = FX_ArraySize(kHelloGoodbyeText); ++constexpr int kHelloGoodbyeTextSize = std::size(kHelloGoodbyeText); + + bool check_unsigned_shorts(const char* expected, + const unsigned short* actual, +@@ -120,10 +119,10 @@ TEST_F(FPDFTextEmbedderTest, Text) { + FPDFText_GetCharBox(textpage, 4, nullptr, nullptr, nullptr, nullptr)); + + EXPECT_TRUE(FPDFText_GetCharBox(textpage, 4, &left, &right, &bottom, &top)); +- EXPECT_NEAR(41.071, left, 0.001); +- EXPECT_NEAR(46.243, right, 0.001); +- EXPECT_NEAR(49.844, bottom, 0.001); +- EXPECT_NEAR(55.520, top, 0.001); ++ EXPECT_NEAR(41.120, left, 0.001); ++ EXPECT_NEAR(46.208, right, 0.001); ++ EXPECT_NEAR(49.892, bottom, 0.001); ++ EXPECT_NEAR(55.652, top, 0.001); + + FS_RECTF rect = {4.0f, 1.0f, 3.0f, 2.0f}; + EXPECT_FALSE(FPDFText_GetLooseCharBox(nullptr, 4, &rect)); +@@ -172,10 +171,10 @@ TEST_F(FPDFTextEmbedderTest, Text) { + bottom = 0.0; + top = 0.0; + EXPECT_TRUE(FPDFText_GetRect(textpage, 1, &left, &top, &right, &bottom)); +- EXPECT_NEAR(20.847, left, 0.001); +- EXPECT_NEAR(135.167, right, 0.001); +- EXPECT_NEAR(96.655, bottom, 0.001); +- EXPECT_NEAR(116.000, top, 0.001); ++ EXPECT_NEAR(20.800, left, 0.001); ++ EXPECT_NEAR(135.040, right, 0.001); ++ EXPECT_NEAR(96.688, bottom, 0.001); ++ EXPECT_NEAR(111.600, top, 0.001); + + // Test out of range indicies set outputs to (0.0, 0.0, 0.0, 0.0). + left = -1.0; +@@ -211,7 +210,7 @@ TEST_F(FPDFTextEmbedderTest, Text) { + memset(buffer, 0xbd, sizeof(buffer)); + EXPECT_EQ( + 9, FPDFText_GetBoundedText(textpage, 41.0, 56.0, 82.0, 48.0, buffer, 9)); +- EXPECT_TRUE(check_unsigned_shorts(kHelloGoodbyeText + 4, buffer, 9)); ++ EXPECT_TRUE(check_unsigned_shorts(kHelloGoodbyeText + 4, buffer, 8)); + EXPECT_EQ(0xbdbd, buffer[9]); + + memset(buffer, 0xbd, sizeof(buffer)); +@@ -262,6 +261,37 @@ TEST_F(FPDFTextEmbedderTest, TextVertical) { + UnloadPage(page); + } + ++TEST_F(FPDFTextEmbedderTest, TextHebrewMirrored) { ++ ASSERT_TRUE(OpenDocument("hebrew_mirrored.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFTextPage textpage(FPDFText_LoadPage(page)); ++ ASSERT_TRUE(textpage); ++ ++ constexpr int kCharCount = 10; ++ ASSERT_EQ(kCharCount, FPDFText_CountChars(textpage.get())); ++ ++ unsigned short buffer[kCharCount + 1]; ++ memset(buffer, 0x42, sizeof(buffer)); ++ EXPECT_EQ(kCharCount + 1, ++ FPDFText_GetText(textpage.get(), 0, kCharCount, buffer)); ++ EXPECT_EQ(0x05d1, buffer[0]); ++ EXPECT_EQ(0x05e0, buffer[1]); ++ EXPECT_EQ(0x05d9, buffer[2]); ++ EXPECT_EQ(0x05de, buffer[3]); ++ EXPECT_EQ(0x05d9, buffer[4]); ++ EXPECT_EQ(0x05df, buffer[5]); ++ EXPECT_EQ(0x000d, buffer[6]); ++ EXPECT_EQ(0x000a, buffer[7]); ++ EXPECT_EQ(0x05df, buffer[8]); ++ EXPECT_EQ(0x05d1, buffer[9]); ++ } ++ ++ UnloadPage(page); ++} ++ + TEST_F(FPDFTextEmbedderTest, TextSearch) { + ASSERT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); +@@ -463,7 +493,7 @@ TEST_F(FPDFTextEmbedderTest, TextSearchConsecutive) { + } + + // Fails on Windows. https://crbug.com/pdfium/1370 +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + #define MAYBE_TextSearchLatinExtended DISABLED_TextSearchLatinExtended + #else + #define MAYBE_TextSearchLatinExtended TextSearchLatinExtended +@@ -593,10 +623,10 @@ TEST_F(FPDFTextEmbedderTest, WebLinks) { + double top = 0.0; + double bottom = 0.0; + EXPECT_TRUE(FPDFLink_GetRect(pagelink, 0, 0, &left, &top, &right, &bottom)); +- EXPECT_NEAR(50.791, left, 0.001); +- EXPECT_NEAR(187.963, right, 0.001); +- EXPECT_NEAR(97.624, bottom, 0.001); +- EXPECT_NEAR(108.736, top, 0.001); ++ EXPECT_NEAR(50.828, left, 0.001); ++ EXPECT_NEAR(187.904, right, 0.001); ++ EXPECT_NEAR(97.516, bottom, 0.001); ++ EXPECT_NEAR(108.700, top, 0.001); + + // Check that valid link with invalid rect index leaves parameters unchanged. + left = -1.0; +@@ -645,7 +675,7 @@ TEST_F(FPDFTextEmbedderTest, WebLinksAcrossLines) { + "http://example.com/", + "http://www.abc.com", + }; +- static const int kNumLinks = static_cast(FX_ArraySize(kExpectedUrls)); ++ static const int kNumLinks = static_cast(std::size(kExpectedUrls)); + + EXPECT_EQ(kNumLinks, FPDFLink_CountWebLinks(pagelink)); + +@@ -656,7 +686,7 @@ TEST_F(FPDFTextEmbedderTest, WebLinksAcrossLines) { + EXPECT_EQ(static_cast(expected_len), + FPDFLink_GetURL(pagelink, i, nullptr, 0)); + EXPECT_EQ(static_cast(expected_len), +- FPDFLink_GetURL(pagelink, i, buffer, FX_ArraySize(buffer))); ++ FPDFLink_GetURL(pagelink, i, buffer, std::size(buffer))); + EXPECT_TRUE(check_unsigned_shorts(kExpectedUrls[i], buffer, expected_len)); + } + +@@ -683,8 +713,7 @@ TEST_F(FPDFTextEmbedderTest, WebLinksAcrossLinesBug) { + static const int kUrlSize = static_cast(sizeof(kExpectedUrl)); + + EXPECT_EQ(kUrlSize, FPDFLink_GetURL(pagelink, 1, nullptr, 0)); +- EXPECT_EQ(kUrlSize, +- FPDFLink_GetURL(pagelink, 1, buffer, FX_ArraySize(buffer))); ++ EXPECT_EQ(kUrlSize, FPDFLink_GetURL(pagelink, 1, buffer, std::size(buffer))); + EXPECT_TRUE(check_unsigned_shorts(kExpectedUrl, buffer, kUrlSize)); + + FPDFLink_CloseWebLinks(pagelink); +@@ -741,13 +770,13 @@ TEST_F(FPDFTextEmbedderTest, WebLinksCharRanges) { + } + + TEST_F(FPDFTextEmbedderTest, AnnotLinks) { +- ASSERT_TRUE(OpenDocument("link_annots.pdf")); ++ ASSERT_TRUE(OpenDocument("annots.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + // Get link count via checking annotation subtype + int annot_count = FPDFPage_GetAnnotCount(page); +- ASSERT_EQ(8, annot_count); ++ ASSERT_EQ(9, annot_count); + int annot_subtype_link_count = 0; + for (int i = 0; i < annot_count; ++i) { + ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, i)); +@@ -818,7 +847,7 @@ TEST_F(FPDFTextEmbedderTest, GetFontSize) { + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}; + + int count = FPDFText_CountChars(textpage); +- ASSERT_EQ(FX_ArraySize(kExpectedFontsSizes), static_cast(count)); ++ ASSERT_EQ(std::size(kExpectedFontsSizes), static_cast(count)); + for (int i = 0; i < count; ++i) + EXPECT_EQ(kExpectedFontsSizes[i], FPDFText_GetFontSize(textpage, i)) << i; + +@@ -923,6 +952,67 @@ TEST_F(FPDFTextEmbedderTest, ToUnicode) { + UnloadPage(page); + } + ++TEST_F(FPDFTextEmbedderTest, IsGenerated) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFTextPage textpage(FPDFText_LoadPage(page)); ++ ASSERT_TRUE(textpage); ++ ++ EXPECT_EQ(static_cast('H'), ++ FPDFText_GetUnicode(textpage.get(), 0)); ++ EXPECT_EQ(0, FPDFText_IsGenerated(textpage.get(), 0)); ++ EXPECT_EQ(static_cast(' '), ++ FPDFText_GetUnicode(textpage.get(), 6)); ++ EXPECT_EQ(0, FPDFText_IsGenerated(textpage.get(), 6)); ++ ++ EXPECT_EQ(static_cast('\r'), ++ FPDFText_GetUnicode(textpage.get(), 13)); ++ EXPECT_EQ(1, FPDFText_IsGenerated(textpage.get(), 13)); ++ EXPECT_EQ(static_cast('\n'), ++ FPDFText_GetUnicode(textpage.get(), 14)); ++ EXPECT_EQ(1, FPDFText_IsGenerated(textpage.get(), 14)); ++ ++ EXPECT_EQ(-1, FPDFText_IsGenerated(textpage.get(), -1)); ++ EXPECT_EQ(-1, FPDFText_IsGenerated(textpage.get(), kHelloGoodbyeTextSize)); ++ EXPECT_EQ(-1, FPDFText_IsGenerated(nullptr, 6)); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFTextEmbedderTest, IsInvalidUnicode) { ++ ASSERT_TRUE(OpenDocument("bug_1388_2.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ constexpr int kExpectedCharCount = 5; ++ ScopedFPDFTextPage textpage(FPDFText_LoadPage(page)); ++ ASSERT_TRUE(textpage); ++ EXPECT_EQ(kExpectedCharCount, FPDFText_CountChars(textpage.get())); ++ ++ EXPECT_EQ(static_cast('X'), ++ FPDFText_GetUnicode(textpage.get(), 0)); ++ EXPECT_EQ(0, FPDFText_HasUnicodeMapError(textpage.get(), 0)); ++ EXPECT_EQ(static_cast(' '), ++ FPDFText_GetUnicode(textpage.get(), 1)); ++ EXPECT_EQ(0, FPDFText_HasUnicodeMapError(textpage.get(), 1)); ++ ++ EXPECT_EQ(31u, FPDFText_GetUnicode(textpage.get(), 2)); ++ EXPECT_EQ(1, FPDFText_HasUnicodeMapError(textpage.get(), 2)); ++ ++ EXPECT_EQ(-1, FPDFText_HasUnicodeMapError(textpage.get(), -1)); ++ EXPECT_EQ(-1, ++ FPDFText_HasUnicodeMapError(textpage.get(), kExpectedCharCount)); ++ EXPECT_EQ(-1, FPDFText_HasUnicodeMapError(nullptr, 0)); ++ } ++ ++ UnloadPage(page); ++} ++ + TEST_F(FPDFTextEmbedderTest, Bug_921) { + ASSERT_TRUE(OpenDocument("bug_921.pdf")); + FPDF_PAGE page = LoadPage(0); +@@ -937,18 +1027,17 @@ TEST_F(FPDFTextEmbedderTest, Bug_921) { + static constexpr int kStartIndex = 238; + + ASSERT_EQ(268, FPDFText_CountChars(textpage)); +- for (size_t i = 0; i < FX_ArraySize(kData); ++i) ++ for (size_t i = 0; i < std::size(kData); ++i) + EXPECT_EQ(kData[i], FPDFText_GetUnicode(textpage, kStartIndex + i)); + +- unsigned short buffer[FX_ArraySize(kData) + 1]; ++ unsigned short buffer[std::size(kData) + 1]; + memset(buffer, 0xbd, sizeof(buffer)); +- int count = +- FPDFText_GetText(textpage, kStartIndex, FX_ArraySize(kData), buffer); ++ int count = FPDFText_GetText(textpage, kStartIndex, std::size(kData), buffer); + ASSERT_GT(count, 0); +- ASSERT_EQ(FX_ArraySize(kData) + 1, static_cast(count)); +- for (size_t i = 0; i < FX_ArraySize(kData); ++i) ++ ASSERT_EQ(std::size(kData) + 1, static_cast(count)); ++ for (size_t i = 0; i < std::size(kData); ++i) + EXPECT_EQ(kData[i], buffer[i]); +- EXPECT_EQ(0, buffer[FX_ArraySize(kData)]); ++ EXPECT_EQ(0, buffer[std::size(kData)]); + + FPDFText_ClosePage(textpage); + UnloadPage(page); +@@ -970,8 +1059,8 @@ TEST_F(FPDFTextEmbedderTest, GetTextWithHyphen) { + 0x0056, 0x0065, 0x0072, 0x0069, 0x0074, 0x0061, 0xfffe, + 0x0073, 0x0065, 0x0072, 0x0075, 0x006D, 0x0000}; + { +- constexpr int count = FX_ArraySize(soft_expected) - 1; +- unsigned short buffer[FX_ArraySize(soft_expected)]; ++ constexpr int count = std::size(soft_expected) - 1; ++ unsigned short buffer[std::size(soft_expected)]; + memset(buffer, 0, sizeof(buffer)); + + EXPECT_EQ(count + 1, FPDFText_GetText(textpage, 0, count, buffer)); +@@ -983,14 +1072,14 @@ TEST_F(FPDFTextEmbedderTest, GetTextWithHyphen) { + { + // There isn't the \0 in the actual doc, but there is a \r\n, so need to + // add 1 to get aligned. +- constexpr size_t offset = FX_ArraySize(soft_expected) + 1; +- // Expecting 'User-\r\ngenerated', the - is a unicode character, so cannnot ++ constexpr size_t offset = std::size(soft_expected) + 1; ++ // Expecting 'User-\r\ngenerated', the - is a unicode character, so cannot + // store in a char[]. + constexpr unsigned short hard_expected[] = { + 0x0055, 0x0073, 0x0065, 0x0072, 0x2010, 0x000d, 0x000a, 0x0067, 0x0065, + 0x006e, 0x0065, 0x0072, 0x0061, 0x0074, 0x0065, 0x0064, 0x0000}; +- constexpr int count = FX_ArraySize(hard_expected) - 1; +- unsigned short buffer[FX_ArraySize(hard_expected)]; ++ constexpr int count = std::size(hard_expected) - 1; ++ unsigned short buffer[std::size(hard_expected)]; + + EXPECT_EQ(count + 1, FPDFText_GetText(textpage, offset, count, buffer)); + for (int i = 0; i < count; i++) +@@ -1072,7 +1161,7 @@ TEST_F(FPDFTextEmbedderTest, bug_1029) { + 0x0061, 0x0073, 0x0020, 0x0063, 0x006f, 0x006d, 0x006d, 0x0069, + 0x0074, 0x0074, 0x0065, 0x0064, 0x002c, 0x0020, 0x0069, 0x0074, + 0x0020, 0x006e, 0x006f, 0x0074, 0x0069, 0x0002, 0x0066, 0x0069}; +- static_assert(page_range_length == FX_ArraySize(expected), ++ static_assert(page_range_length == std::size(expected), + "Expected should be the same size as the range being " + "extracted from page."); + EXPECT_LT(page_range_offset + page_range_length, +@@ -1174,7 +1263,7 @@ TEST_F(FPDFTextEmbedderTest, GetText) { + // Positive testing. + constexpr char kHelloText[] = "Hello, world!"; + // Return value includes the terminating NUL that is provided. +- constexpr unsigned long kHelloUTF16Size = FX_ArraySize(kHelloText) * 2; ++ constexpr unsigned long kHelloUTF16Size = std::size(kHelloText) * 2; + constexpr wchar_t kHelloWideText[] = L"Hello, world!"; + unsigned long size = FPDFTextObj_GetText(text_object, text_page, nullptr, 0); + ASSERT_EQ(kHelloUTF16Size, size); +@@ -1292,14 +1381,14 @@ TEST_F(FPDFTextEmbedderTest, Bug_642) { + ASSERT_TRUE(text_page); + + constexpr char kText[] = "ABCD"; +- constexpr size_t kTextSize = FX_ArraySize(kText); ++ constexpr size_t kTextSize = std::size(kText); + // -1 for CountChars not including the \0 + EXPECT_EQ(static_cast(kTextSize) - 1, + FPDFText_CountChars(text_page.get())); + + unsigned short buffer[kTextSize]; + int num_chars = +- FPDFText_GetText(text_page.get(), 0, FX_ArraySize(buffer) - 1, buffer); ++ FPDFText_GetText(text_page.get(), 0, std::size(buffer) - 1, buffer); + ASSERT_EQ(static_cast(kTextSize), num_chars); + EXPECT_TRUE(check_unsigned_shorts(kText, buffer, kTextSize)); + } +@@ -1315,9 +1404,8 @@ TEST_F(FPDFTextEmbedderTest, GetCharAngle) { + FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page); + ASSERT_TRUE(text_page); + +- static constexpr int kSubstringsSize[] = {FX_ArraySize("Hello,"), +- FX_ArraySize(" world!\r\n"), +- FX_ArraySize("Goodbye,")}; ++ static constexpr int kSubstringsSize[] = { ++ std::size("Hello,"), std::size(" world!\r\n"), std::size("Goodbye,")}; + + // -1 for CountChars not including the \0, but +1 for the extra control + // character. +@@ -1329,15 +1417,15 @@ TEST_F(FPDFTextEmbedderTest, GetCharAngle) { + FPDFText_GetCharAngle(text_page, kHelloGoodbyeTextSize + 1)); + + // Test GetCharAngle for every quadrant +- EXPECT_NEAR(FX_PI / 4.0, FPDFText_GetCharAngle(text_page, 0), 0.001); +- EXPECT_NEAR(3 * FX_PI / 4.0, ++ EXPECT_NEAR(FXSYS_PI / 4.0, FPDFText_GetCharAngle(text_page, 0), 0.001); ++ EXPECT_NEAR(3 * FXSYS_PI / 4.0, + FPDFText_GetCharAngle(text_page, kSubstringsSize[0]), 0.001); + EXPECT_NEAR( +- 5 * FX_PI / 4.0, ++ 5 * FXSYS_PI / 4.0, + FPDFText_GetCharAngle(text_page, kSubstringsSize[0] + kSubstringsSize[1]), + 0.001); + EXPECT_NEAR( +- 7 * FX_PI / 4.0, ++ 7 * FXSYS_PI / 4.0, + FPDFText_GetCharAngle(text_page, kSubstringsSize[0] + kSubstringsSize[1] + + kSubstringsSize[2]), + 0.001); +@@ -1374,7 +1462,7 @@ TEST_F(FPDFTextEmbedderTest, GetFontWeight) { + } + + TEST_F(FPDFTextEmbedderTest, GetTextRenderMode) { +- EXPECT_TRUE(OpenDocument("text_render_mode.pdf")); ++ ASSERT_TRUE(OpenDocument("text_render_mode.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -1467,7 +1555,7 @@ TEST_F(FPDFTextEmbedderTest, GetStrokeColor) { + + TEST_F(FPDFTextEmbedderTest, GetMatrix) { + constexpr char kExpectedText[] = "A1\r\nA2\r\nA3"; +- constexpr size_t kExpectedTextSize = FX_ArraySize(kExpectedText); ++ constexpr size_t kExpectedTextSize = std::size(kExpectedText); + constexpr FS_MATRIX kExpectedMatrices[] = { + {12.0f, 0.0f, 0.0f, 10.0f, 66.0f, 90.0f}, + {12.0f, 0.0f, 0.0f, 10.0f, 66.0f, 90.0f}, +@@ -1480,14 +1568,10 @@ TEST_F(FPDFTextEmbedderTest, GetMatrix) { + {1.0f, 0.0f, 0.0f, 0.833333, 60.0f, 130.0f}, + {1.0f, 0.0f, 0.0f, 0.833333, 60.0f, 130.0f}, + }; +- constexpr size_t kExpectedCount = FX_ArraySize(kExpectedMatrices); ++ constexpr size_t kExpectedCount = std::size(kExpectedMatrices); + static_assert(kExpectedCount + 1 == kExpectedTextSize, + "Bad expected matrix size"); + +- // For a size 12 letter 'A'. +- constexpr double kExpectedCharWidth = 8.436; +- constexpr double kExpectedCharHeight = 6.77; +- + ASSERT_TRUE(OpenDocument("font_matrix.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); +@@ -1507,26 +1591,6 @@ TEST_F(FPDFTextEmbedderTest, GetMatrix) { + check_unsigned_shorts(kExpectedText, buffer, kExpectedTextSize)); + } + +- { +- // Check the character box size. +- double left; +- double right; +- double bottom; +- double top; +- ASSERT_TRUE(FPDFText_GetCharBox(text_page.get(), 0, &left, &right, +- &bottom, &top)); +- EXPECT_NEAR(kExpectedCharWidth, right - left, 0.001); +- EXPECT_NEAR(kExpectedCharHeight, top - bottom, 0.001); +- ASSERT_TRUE(FPDFText_GetCharBox(text_page.get(), 4, &left, &right, +- &bottom, &top)); +- EXPECT_NEAR(kExpectedCharWidth, right - left, 0.001); +- EXPECT_NEAR(kExpectedCharHeight, top - bottom, 0.001); +- ASSERT_TRUE(FPDFText_GetCharBox(text_page.get(), 8, &left, &right, +- &bottom, &top)); +- EXPECT_NEAR(kExpectedCharWidth, right - left, 0.001); +- EXPECT_NEAR(kExpectedCharHeight, top - bottom, 0.001); +- } +- + // Check the character matrix. + FS_MATRIX matrix; + for (size_t i = 0; i < kExpectedCount; ++i) { +@@ -1548,3 +1612,154 @@ TEST_F(FPDFTextEmbedderTest, GetMatrix) { + + UnloadPage(page); + } ++ ++TEST_F(FPDFTextEmbedderTest, CharBox) { ++ // For a size 12 letter 'A'. ++ constexpr double kExpectedCharWidth = 8.460; ++ constexpr double kExpectedCharHeight = 6.600; ++ constexpr float kExpectedLooseCharWidth = 8.664f; ++ constexpr float kExpectedLooseCharHeight = 12.0f; ++ ++ ASSERT_TRUE(OpenDocument("font_matrix.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFTextPage text_page(FPDFText_LoadPage(page)); ++ ASSERT_TRUE(text_page); ++ ++ // Check the character box size. ++ double left; ++ double right; ++ double bottom; ++ double top; ++ ASSERT_TRUE( ++ FPDFText_GetCharBox(text_page.get(), 0, &left, &right, &bottom, &top)); ++ EXPECT_NEAR(kExpectedCharWidth, right - left, 0.001); ++ EXPECT_NEAR(kExpectedCharHeight, top - bottom, 0.001); ++ ASSERT_TRUE( ++ FPDFText_GetCharBox(text_page.get(), 4, &left, &right, &bottom, &top)); ++ EXPECT_NEAR(kExpectedCharWidth, right - left, 0.001); ++ EXPECT_NEAR(kExpectedCharHeight, top - bottom, 0.001); ++ ASSERT_TRUE( ++ FPDFText_GetCharBox(text_page.get(), 8, &left, &right, &bottom, &top)); ++ EXPECT_NEAR(kExpectedCharWidth, right - left, 0.001); ++ EXPECT_NEAR(kExpectedCharHeight, top - bottom, 0.001); ++ ++ // Check the loose character box size. ++ FS_RECTF rect; ++ ASSERT_TRUE(FPDFText_GetLooseCharBox(text_page.get(), 0, &rect)); ++ EXPECT_FLOAT_EQ(kExpectedLooseCharWidth, rect.right - rect.left); ++ EXPECT_FLOAT_EQ(kExpectedLooseCharHeight, rect.top - rect.bottom); ++ ASSERT_TRUE(FPDFText_GetLooseCharBox(text_page.get(), 4, &rect)); ++ EXPECT_FLOAT_EQ(kExpectedLooseCharWidth, rect.right - rect.left); ++ EXPECT_FLOAT_EQ(kExpectedLooseCharHeight, rect.top - rect.bottom); ++ ASSERT_TRUE(FPDFText_GetLooseCharBox(text_page.get(), 8, &rect)); ++ EXPECT_FLOAT_EQ(kExpectedLooseCharWidth, rect.right - rect.left); ++ EXPECT_NEAR(kExpectedLooseCharHeight, rect.top - rect.bottom, 0.00001); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFTextEmbedderTest, SmallType3Glyph) { ++ ASSERT_TRUE(OpenDocument("bug_1591.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFTextPage text_page(FPDFText_LoadPage(page)); ++ ASSERT_TRUE(text_page); ++ ASSERT_EQ(5, FPDFText_CountChars(text_page.get())); ++ ++ EXPECT_EQ(49u, FPDFText_GetUnicode(text_page.get(), 0)); ++ EXPECT_EQ(32u, FPDFText_GetUnicode(text_page.get(), 1)); ++ EXPECT_EQ(50u, FPDFText_GetUnicode(text_page.get(), 2)); ++ EXPECT_EQ(32u, FPDFText_GetUnicode(text_page.get(), 3)); ++ EXPECT_EQ(49u, FPDFText_GetUnicode(text_page.get(), 4)); ++ ++ // Check the character box size. ++ double left; ++ double right; ++ double bottom; ++ double top; ++ ASSERT_TRUE( ++ FPDFText_GetCharBox(text_page.get(), 0, &left, &right, &bottom, &top)); ++ EXPECT_DOUBLE_EQ(63.439998626708984, left); ++ EXPECT_DOUBLE_EQ(65.360000610351562, right); ++ EXPECT_DOUBLE_EQ(50.0, bottom); ++ EXPECT_DOUBLE_EQ(61.520000457763672, top); ++ ASSERT_TRUE( ++ FPDFText_GetCharBox(text_page.get(), 1, &left, &right, &bottom, &top)); ++ EXPECT_DOUBLE_EQ(62.007999420166016, left); ++ EXPECT_DOUBLE_EQ(62.007999420166016, right); ++ EXPECT_DOUBLE_EQ(50.0, bottom); ++ EXPECT_DOUBLE_EQ(50.0, top); ++ ASSERT_TRUE( ++ FPDFText_GetCharBox(text_page.get(), 2, &left, &right, &bottom, &top)); ++ EXPECT_DOUBLE_EQ(86.0, left); ++ EXPECT_DOUBLE_EQ(88.400001525878906, right); ++ EXPECT_DOUBLE_EQ(50.0, bottom); ++ EXPECT_DOUBLE_EQ(50.240001678466797, top); ++ ASSERT_TRUE( ++ FPDFText_GetCharBox(text_page.get(), 3, &left, &right, &bottom, &top)); ++ EXPECT_DOUBLE_EQ(86.010002136230469, left); ++ EXPECT_DOUBLE_EQ(86.010002136230469, right); ++ EXPECT_DOUBLE_EQ(50.0, bottom); ++ EXPECT_DOUBLE_EQ(50.0, top); ++ ASSERT_TRUE( ++ FPDFText_GetCharBox(text_page.get(), 4, &left, &right, &bottom, &top)); ++ EXPECT_DOUBLE_EQ(99.44000244140625, left); ++ EXPECT_DOUBLE_EQ(101.36000061035156, right); ++ EXPECT_DOUBLE_EQ(50.0, bottom); ++ EXPECT_DOUBLE_EQ(61.520000457763672, top); ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFTextEmbedderTest, BigtableTextExtraction) { ++ constexpr char kExpectedText[] = ++ "{fay,jeff,sanjay,wilsonh,kerr,m3b,tushar,\x02k es,gruber}@google.com"; ++ constexpr int kExpectedTextCount = std::size(kExpectedText) - 1; ++ ++ ASSERT_TRUE(OpenDocument("bigtable_mini.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFTextPage text_page(FPDFText_LoadPage(page)); ++ ASSERT_TRUE(text_page); ++ int char_count = FPDFText_CountChars(text_page.get()); ++ ASSERT_GE(char_count, 0); ++ ASSERT_EQ(kExpectedTextCount, char_count); ++ ++ for (int i = 0; i < kExpectedTextCount; ++i) { ++ EXPECT_EQ(static_cast(kExpectedText[i]), ++ FPDFText_GetUnicode(text_page.get(), i)); ++ } ++ } ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFTextEmbedderTest, Bug1769) { ++ ASSERT_TRUE(OpenDocument("bug_1769.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ { ++ ScopedFPDFTextPage textpage(FPDFText_LoadPage(page)); ++ ASSERT_TRUE(textpage); ++ ++ unsigned short buffer[128] = {}; ++ // TODO(crbug.com/pdfium/1769): Improve text extraction. ++ // The first instance of "world" is visible to the human eye and should be ++ // extracted as is. The second instance is not, so how it should be ++ // extracted is debatable. ++ ASSERT_EQ(10, FPDFText_GetText(textpage.get(), 0, 128, buffer)); ++ EXPECT_TRUE(check_unsigned_shorts("wo d wo d", buffer, 10)); ++ } ++ ++ UnloadPage(page); ++} +diff --git a/fpdfsdk/fpdf_thumbnail.cpp b/fpdfsdk/fpdf_thumbnail.cpp +index 5c22c8c47..0f1331dea 100644 +--- a/fpdfsdk/fpdf_thumbnail.cpp ++++ b/fpdfsdk/fpdf_thumbnail.cpp +@@ -1,10 +1,10 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "public/fpdf_thumbnail.h" + +-#include ++#include + + #include "core/fpdfapi/page/cpdf_dib.h" + #include "core/fpdfapi/page/cpdf_page.h" +@@ -17,12 +17,12 @@ + + namespace { + +-const CPDF_Stream* CPDFStreamForThumbnailFromPage(FPDF_PAGE page) { +- const CPDF_Page* p_page = CPDFPageFromFPDFPage(page); +- if (!p_page) ++RetainPtr CPDFStreamForThumbnailFromPage(FPDF_PAGE page) { ++ const CPDF_Page* pdf_page = CPDFPageFromFPDFPage(page); ++ if (!pdf_page) + return nullptr; + +- const CPDF_Dictionary* page_dict = p_page->GetDict(); ++ RetainPtr page_dict = pdf_page->GetDict(); + if (!page_dict->KeyExist("Type")) + return nullptr; + +@@ -35,41 +35,48 @@ FPDF_EXPORT unsigned long FPDF_CALLCONV + FPDFPage_GetDecodedThumbnailData(FPDF_PAGE page, + void* buffer, + unsigned long buflen) { +- const CPDF_Stream* thumb_stream = CPDFStreamForThumbnailFromPage(page); ++ RetainPtr thumb_stream = ++ CPDFStreamForThumbnailFromPage(page); + if (!thumb_stream) + return 0u; + +- return DecodeStreamMaybeCopyAndReturnLength(thumb_stream, buffer, buflen); ++ return DecodeStreamMaybeCopyAndReturnLength( ++ std::move(thumb_stream), ++ {static_cast(buffer), static_cast(buflen)}); + } + + FPDF_EXPORT unsigned long FPDF_CALLCONV + FPDFPage_GetRawThumbnailData(FPDF_PAGE page, + void* buffer, + unsigned long buflen) { +- const CPDF_Stream* thumb_stream = CPDFStreamForThumbnailFromPage(page); ++ RetainPtr thumb_stream = ++ CPDFStreamForThumbnailFromPage(page); + if (!thumb_stream) + return 0u; + +- return GetRawStreamMaybeCopyAndReturnLength(thumb_stream, buffer, buflen); ++ return GetRawStreamMaybeCopyAndReturnLength( ++ std::move(thumb_stream), ++ {static_cast(buffer), static_cast(buflen)}); + } + + FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV + FPDFPage_GetThumbnailAsBitmap(FPDF_PAGE page) { +- const CPDF_Stream* thumb_stream = CPDFStreamForThumbnailFromPage(page); ++ RetainPtr thumb_stream = ++ CPDFStreamForThumbnailFromPage(page); + if (!thumb_stream) + return nullptr; + +- const CPDF_Page* p_page = CPDFPageFromFPDFPage(page); +- +- auto p_source = pdfium::MakeRetain(); +- const CPDF_DIB::LoadState start_status = p_source->StartLoadDIBBase( +- p_page->GetDocument(), thumb_stream, false, nullptr, +- p_page->m_pPageResources.Get(), false, 0, false); ++ const CPDF_Page* pdf_page = CPDFPageFromFPDFPage(page); ++ auto dib_source = pdfium::MakeRetain(pdf_page->GetDocument(), ++ std::move(thumb_stream)); ++ const CPDF_DIB::LoadState start_status = dib_source->StartLoadDIBBase( ++ false, nullptr, pdf_page->GetPageResources().Get(), false, ++ CPDF_ColorSpace::Family::kUnknown, false, {0, 0}); + if (start_status == CPDF_DIB::LoadState::kFail) + return nullptr; + + auto thumb_bitmap = pdfium::MakeRetain(); +- if (!thumb_bitmap->Copy(p_source)) ++ if (!thumb_bitmap->Copy(dib_source)) + return nullptr; + + return FPDFBitmapFromCFXDIBitmap(thumb_bitmap.Leak()); +diff --git a/fpdfsdk/fpdf_thumbnail_embeddertest.cpp b/fpdfsdk/fpdf_thumbnail_embeddertest.cpp +index 101e1e4c5..023e8ba5d 100644 +--- a/fpdfsdk/fpdf_thumbnail_embeddertest.cpp ++++ b/fpdfsdk/fpdf_thumbnail_embeddertest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,6 +9,14 @@ + #include "testing/embedder_test.h" + #include "testing/utils/hash.h" + ++namespace { ++ ++const char kSimpleThumbnailChecksum[] = "f6a8e8db01cccd52abb91ea433a17373"; ++const char kThumbnailWithNoFiltersChecksum[] = ++ "b5696e586382b3373741f8a1d651cab0"; ++ ++} // namespace ++ + class FPDFThumbnailEmbedderTest : public EmbedderTest {}; + + TEST_F(FPDFThumbnailEmbedderTest, GetDecodedThumbnailDataFromPageWithFilters) { +@@ -28,8 +36,7 @@ TEST_F(FPDFThumbnailEmbedderTest, GetDecodedThumbnailDataFromPageWithFilters) { + + EXPECT_EQ(kExpectedSize, FPDFPage_GetDecodedThumbnailData( + page, thumb_buf.data(), length_bytes)); +- EXPECT_EQ(kHashedDecodedData, +- GenerateMD5Base16(thumb_buf.data(), kExpectedSize)); ++ EXPECT_EQ(kHashedDecodedData, GenerateMD5Base16(thumb_buf)); + + UnloadPage(page); + } +@@ -48,8 +55,7 @@ TEST_F(FPDFThumbnailEmbedderTest, GetDecodedThumbnailDataFromPageWithFilters) { + + EXPECT_EQ(kExpectedSize, FPDFPage_GetDecodedThumbnailData( + page, thumb_buf.data(), length_bytes)); +- EXPECT_EQ(kHashedDecodedData, +- GenerateMD5Base16(thumb_buf.data(), kExpectedSize)); ++ EXPECT_EQ(kHashedDecodedData, GenerateMD5Base16(thumb_buf)); + + UnloadPage(page); + } +@@ -59,7 +65,6 @@ TEST_F(FPDFThumbnailEmbedderTest, + GetDecodedThumbnailDataFromPageWithNoFilters) { + ASSERT_TRUE(OpenDocument("thumbnail_with_no_filters.pdf")); + +- const char kHashedDecodedData[] = "b5696e586382b3373741f8a1d651cab0"; + const unsigned long kExpectedSize = 301u; + + FPDF_PAGE page = LoadPage(0); +@@ -72,8 +77,7 @@ TEST_F(FPDFThumbnailEmbedderTest, + + EXPECT_EQ(kExpectedSize, FPDFPage_GetDecodedThumbnailData( + page, thumb_buf.data(), length_bytes)); +- EXPECT_EQ(kHashedDecodedData, +- GenerateMD5Base16(thumb_buf.data(), kExpectedSize)); ++ EXPECT_EQ(kThumbnailWithNoFiltersChecksum, GenerateMD5Base16(thumb_buf)); + + UnloadPage(page); + } +@@ -98,7 +102,6 @@ TEST_F(FPDFThumbnailEmbedderTest, GetRawThumbnailDataFromPageWithFilters) { + ASSERT_TRUE(OpenDocument("simple_thumbnail.pdf")); + + { +- const char kHashedRawData[] = "f6a8e8db01cccd52abb91ea433a17373"; + const unsigned long kExpectedSize = 1851u; + + FPDF_PAGE page = LoadPage(0); +@@ -110,8 +113,7 @@ TEST_F(FPDFThumbnailEmbedderTest, GetRawThumbnailDataFromPageWithFilters) { + + EXPECT_EQ(kExpectedSize, FPDFPage_GetRawThumbnailData( + page, thumb_buf.data(), length_bytes)); +- EXPECT_EQ(kHashedRawData, +- GenerateMD5Base16(thumb_buf.data(), kExpectedSize)); ++ EXPECT_EQ(kSimpleThumbnailChecksum, GenerateMD5Base16(thumb_buf)); + + UnloadPage(page); + } +@@ -129,8 +131,7 @@ TEST_F(FPDFThumbnailEmbedderTest, GetRawThumbnailDataFromPageWithFilters) { + + EXPECT_EQ(kExpectedSize, FPDFPage_GetRawThumbnailData( + page, thumb_buf.data(), length_bytes)); +- EXPECT_EQ(kHashedRawData, +- GenerateMD5Base16(thumb_buf.data(), kExpectedSize)); ++ EXPECT_EQ(kHashedRawData, GenerateMD5Base16(thumb_buf)); + + UnloadPage(page); + } +@@ -139,7 +140,6 @@ TEST_F(FPDFThumbnailEmbedderTest, GetRawThumbnailDataFromPageWithFilters) { + TEST_F(FPDFThumbnailEmbedderTest, GetRawThumbnailDataFromPageWithNoFilters) { + ASSERT_TRUE(OpenDocument("thumbnail_with_no_filters.pdf")); + +- const char kHashedRawData[] = "b5696e586382b3373741f8a1d651cab0"; + const unsigned long kExpectedSize = 301u; + + FPDF_PAGE page = LoadPage(0); +@@ -151,7 +151,7 @@ TEST_F(FPDFThumbnailEmbedderTest, GetRawThumbnailDataFromPageWithNoFilters) { + + EXPECT_EQ(kExpectedSize, + FPDFPage_GetRawThumbnailData(page, thumb_buf.data(), length_bytes)); +- EXPECT_EQ(kHashedRawData, GenerateMD5Base16(thumb_buf.data(), kExpectedSize)); ++ EXPECT_EQ(kThumbnailWithNoFiltersChecksum, GenerateMD5Base16(thumb_buf)); + + UnloadPage(page); + } +@@ -251,7 +251,6 @@ TEST_F(FPDFThumbnailEmbedderTest, + TEST_F(FPDFThumbnailEmbedderTest, GetThumbnailDoesNotAlterPage) { + ASSERT_TRUE(OpenDocument("simple_thumbnail.pdf")); + +- const char kHashedRawData[] = "f6a8e8db01cccd52abb91ea433a17373"; + const unsigned long kExpectedRawSize = 1851u; + + FPDF_PAGE page = LoadPage(0); +@@ -264,8 +263,7 @@ TEST_F(FPDFThumbnailEmbedderTest, GetThumbnailDoesNotAlterPage) { + + EXPECT_EQ(kExpectedRawSize, + FPDFPage_GetRawThumbnailData(page, raw_thumb_buf.data(), raw_size)); +- EXPECT_EQ(kHashedRawData, +- GenerateMD5Base16(raw_thumb_buf.data(), kExpectedRawSize)); ++ EXPECT_EQ(kSimpleThumbnailChecksum, GenerateMD5Base16(raw_thumb_buf)); + + // Get the thumbnail + ScopedFPDFBitmap thumb_bitmap(FPDFPage_GetThumbnailAsBitmap(page)); +@@ -283,12 +281,11 @@ TEST_F(FPDFThumbnailEmbedderTest, GetThumbnailDoesNotAlterPage) { + EXPECT_EQ(kExpectedRawSize, + FPDFPage_GetRawThumbnailData(page, new_raw_thumb_buf.data(), + new_raw_size)); +- EXPECT_EQ(kHashedRawData, +- GenerateMD5Base16(new_raw_thumb_buf.data(), kExpectedRawSize)); ++ EXPECT_EQ(kSimpleThumbnailChecksum, GenerateMD5Base16(new_raw_thumb_buf)); + + UnloadPage(page); + } + + TEST_F(FPDFThumbnailEmbedderTest, GetThumbnailAsBitmapFromPageNullPage) { +- EXPECT_EQ(nullptr, FPDFPage_GetThumbnailAsBitmap(nullptr)); ++ EXPECT_FALSE(FPDFPage_GetThumbnailAsBitmap(nullptr)); + } +diff --git a/fpdfsdk/fpdf_transformpage.cpp b/fpdfsdk/fpdf_transformpage.cpp +index cc268885f..9814da663 100644 +--- a/fpdfsdk/fpdf_transformpage.cpp ++++ b/fpdfsdk/fpdf_transformpage.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,7 +8,6 @@ + + #include + #include +-#include + + #include "constants/page_object.h" + #include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h" +@@ -22,10 +21,13 @@ + #include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fpdfapi/parser/cpdf_reference.h" + #include "core/fpdfapi/parser/cpdf_stream.h" +-#include "core/fxge/cfx_pathdata.h" +-#include "core/fxge/render_defines.h" ++#include "core/fxcrt/fx_string_wrappers.h" ++#include "core/fxcrt/stl_util.h" ++#include "core/fxge/cfx_fillrenderoptions.h" ++#include "core/fxge/cfx_path.h" + #include "fpdfsdk/cpdfsdk_helpers.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/numerics/safe_conversions.h" ++#include "third_party/base/span.h" + + namespace { + +@@ -35,11 +37,11 @@ void SetBoundingBox(CPDF_Page* page, + if (!page) + return; + +- page->GetDict()->SetRectFor(key, rect); ++ page->GetMutableDict()->SetRectFor(key, rect); + page->UpdateDimensions(); + } + +-bool GetBoundingBox(CPDF_Page* page, ++bool GetBoundingBox(const CPDF_Page* page, + const ByteString& key, + float* left, + float* bottom, +@@ -48,52 +50,51 @@ bool GetBoundingBox(CPDF_Page* page, + if (!page || !left || !bottom || !right || !top) + return false; + +- CPDF_Array* pArray = page->GetDict()->GetArrayFor(key); ++ RetainPtr pArray = page->GetDict()->GetArrayFor(key); + if (!pArray) + return false; + +- *left = pArray->GetNumberAt(0); +- *bottom = pArray->GetNumberAt(1); +- *right = pArray->GetNumberAt(2); +- *top = pArray->GetNumberAt(3); ++ *left = pArray->GetFloatAt(0); ++ *bottom = pArray->GetFloatAt(1); ++ *right = pArray->GetFloatAt(2); ++ *top = pArray->GetFloatAt(3); + return true; + } + +-CPDF_Object* GetPageContent(CPDF_Dictionary* pPageDict) { +- return pPageDict->GetDirectObjectFor(pdfium::page_object::kContents); ++RetainPtr GetPageContent(CPDF_Dictionary* pPageDict) { ++ return pPageDict->GetMutableDirectObjectFor(pdfium::page_object::kContents); + } + +-void OutputPath(std::ostringstream& buf, CPDF_Path path) { +- const CFX_PathData* pPathData = path.GetObject(); +- if (!pPathData) ++void OutputPath(fxcrt::ostringstream& buf, CPDF_Path path) { ++ const CFX_Path* pPath = path.GetObject(); ++ if (!pPath) + return; + +- const std::vector& pPoints = pPathData->GetPoints(); ++ pdfium::span points = pPath->GetPoints(); + if (path.IsRect()) { +- CFX_PointF diff = pPoints[2].m_Point - pPoints[0].m_Point; +- buf << pPoints[0].m_Point.x << " " << pPoints[0].m_Point.y << " " << diff.x ++ CFX_PointF diff = points[2].m_Point - points[0].m_Point; ++ buf << points[0].m_Point.x << " " << points[0].m_Point.y << " " << diff.x + << " " << diff.y << " re\n"; + return; + } + +- ByteString temp; +- for (size_t i = 0; i < pPoints.size(); i++) { +- buf << pPoints[i].m_Point.x << " " << pPoints[i].m_Point.y; +- FXPT_TYPE point_type = pPoints[i].m_Type; +- if (point_type == FXPT_TYPE::MoveTo) { ++ for (size_t i = 0; i < points.size(); ++i) { ++ buf << points[i].m_Point.x << " " << points[i].m_Point.y; ++ CFX_Path::Point::Type point_type = points[i].m_Type; ++ if (point_type == CFX_Path::Point::Type::kMove) { + buf << " m\n"; +- } else if (point_type == FXPT_TYPE::BezierTo) { +- buf << " " << pPoints[i + 1].m_Point.x << " " << pPoints[i + 1].m_Point.y +- << " " << pPoints[i + 2].m_Point.x << " " << pPoints[i + 2].m_Point.y; ++ } else if (point_type == CFX_Path::Point::Type::kBezier) { ++ buf << " " << points[i + 1].m_Point.x << " " << points[i + 1].m_Point.y ++ << " " << points[i + 2].m_Point.x << " " << points[i + 2].m_Point.y; + buf << " c"; +- if (pPoints[i + 2].m_CloseFigure) ++ if (points[i + 2].m_CloseFigure) + buf << " h"; + buf << "\n"; + + i += 2; +- } else if (point_type == FXPT_TYPE::LineTo) { ++ } else if (point_type == CFX_Path::Point::Type::kLine) { + buf << " l"; +- if (pPoints[i].m_CloseFigure) ++ if (points[i].m_CloseFigure) + buf << " h"; + buf << "\n"; + } +@@ -207,8 +208,8 @@ FPDFPage_TransFormWithClip(FPDF_PAGE page, + if (!pPage) + return false; + +- CPDF_Dictionary* pPageDict = pPage->GetDict(); +- CPDF_Object* pContentObj = GetPageContent(pPageDict); ++ RetainPtr pPageDict = pPage->GetMutableDict(); ++ RetainPtr pContentObj = GetPageContent(pPageDict.Get()); + if (!pContentObj) + return false; + +@@ -216,64 +217,58 @@ FPDFPage_TransFormWithClip(FPDF_PAGE page, + if (!pDoc) + return false; + +- std::ostringstream text_buf; ++ fxcrt::ostringstream text_buf; + text_buf << "q "; + + if (clipRect) { + CFX_FloatRect rect = CFXFloatRectFromFSRectF(*clipRect); + rect.Normalize(); +- +- WriteFloat(text_buf, rect.left) << " "; +- WriteFloat(text_buf, rect.bottom) << " "; +- WriteFloat(text_buf, rect.Width()) << " "; +- WriteFloat(text_buf, rect.Height()) << " re W* n "; +- } +- if (matrix) { +- CFX_Matrix m = CFXMatrixFromFSMatrix(*matrix); +- text_buf << m << " cm "; ++ WriteRect(text_buf, rect) << " re W* n "; + } ++ if (matrix) ++ WriteMatrix(text_buf, CFXMatrixFromFSMatrix(*matrix)) << " cm "; + +- CPDF_Stream* pStream = +- pDoc->NewIndirect(nullptr, 0, pDoc->New()); ++ auto pStream = pDoc->NewIndirect(pDoc->New()); + pStream->SetDataFromStringstream(&text_buf); + +- CPDF_Stream* pEndStream = +- pDoc->NewIndirect(nullptr, 0, pDoc->New()); ++ auto pEndStream = ++ pDoc->NewIndirect(pDoc->New()); + pEndStream->SetData(ByteStringView(" Q").raw_span()); + +- if (CPDF_Array* pContentArray = ToArray(pContentObj)) { ++ RetainPtr pContentArray = ToArray(pContentObj); ++ if (pContentArray) { + pContentArray->InsertNewAt(0, pDoc, pStream->GetObjNum()); +- pContentArray->AddNew(pDoc, pEndStream->GetObjNum()); ++ pContentArray->AppendNew(pDoc, pEndStream->GetObjNum()); + } else if (pContentObj->IsStream() && !pContentObj->IsInline()) { + pContentArray = pDoc->NewIndirect(); +- pContentArray->AddNew(pDoc, pStream->GetObjNum()); +- pContentArray->AddNew(pDoc, pContentObj->GetObjNum()); +- pContentArray->AddNew(pDoc, pEndStream->GetObjNum()); ++ pContentArray->AppendNew(pDoc, pStream->GetObjNum()); ++ pContentArray->AppendNew(pDoc, pContentObj->GetObjNum()); ++ pContentArray->AppendNew(pDoc, pEndStream->GetObjNum()); + pPageDict->SetNewFor(pdfium::page_object::kContents, pDoc, + pContentArray->GetObjNum()); + } + + // Need to transform the patterns as well. +- CPDF_Dictionary* pRes = ++ RetainPtr pRes = + pPageDict->GetDictFor(pdfium::page_object::kResources); + if (!pRes) + return true; + +- CPDF_Dictionary* pPatternDict = pRes->GetDictFor("Pattern"); ++ RetainPtr pPatternDict = pRes->GetDictFor("Pattern"); + if (!pPatternDict) + return true; + + CPDF_DictionaryLocker locker(pPatternDict); + for (const auto& it : locker) { +- CPDF_Object* pObj = it.second.Get(); ++ RetainPtr pObj = it.second; + if (pObj->IsReference()) +- pObj = pObj->GetDirect(); ++ pObj = pObj->GetMutableDirect(); + +- CPDF_Dictionary* pDict = nullptr; ++ RetainPtr pDict; + if (pObj->IsDictionary()) +- pDict = pObj->AsDictionary(); +- else if (CPDF_Stream* pObjStream = pObj->AsStream()) +- pDict = pObjStream->GetDict(); ++ pDict.Reset(pObj->AsMutableDictionary()); ++ else if (CPDF_Stream* pObjStream = pObj->AsMutableStream()) ++ pDict = pObjStream->GetMutableDict(); + else + continue; + +@@ -321,7 +316,7 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFClipPath_CountPaths(FPDF_CLIPPATH clip_path) { + if (!pClipPath || !pClipPath->HasRef()) + return -1; + +- return pClipPath->GetPathCount(); ++ return pdfium::base::checked_cast(pClipPath->GetPathCount()); + } + + FPDF_EXPORT int FPDF_CALLCONV +@@ -335,8 +330,7 @@ FPDFClipPath_CountPathSegments(FPDF_CLIPPATH clip_path, int path_index) { + return -1; + } + +- return pdfium::CollectionSize( +- pClipPath->GetPath(path_index).GetPoints()); ++ return fxcrt::CollectionSize(pClipPath->GetPath(path_index).GetPoints()); + } + + FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV +@@ -352,9 +346,9 @@ FPDFClipPath_GetPathSegment(FPDF_CLIPPATH clip_path, + return nullptr; + } + +- const std::vector& points = ++ pdfium::span points = + pClipPath->GetPath(path_index).GetPoints(); +- if (!pdfium::IndexInBounds(points, segment_index)) ++ if (!fxcrt::IndexInBounds(points, segment_index)) + return nullptr; + + return FPDFPathSegmentFromFXPathPoint(&points[segment_index]); +@@ -367,8 +361,8 @@ FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV FPDF_CreateClipPath(float left, + CPDF_Path Path; + Path.AppendRect(left, bottom, right, top); + +- auto pNewClipPath = pdfium::MakeUnique(); +- pNewClipPath->AppendPath(Path, FXFILL_ALTERNATE, false); ++ auto pNewClipPath = std::make_unique(); ++ pNewClipPath->AppendPath(Path, CFX_FillRenderOptions::FillType::kEvenOdd); + + // Caller takes ownership. + return FPDFClipPathFromCPDFClipPath(pNewClipPath.release()); +@@ -385,12 +379,12 @@ FPDF_EXPORT void FPDF_CALLCONV FPDFPage_InsertClipPath(FPDF_PAGE page, + if (!pPage) + return; + +- CPDF_Dictionary* pPageDict = pPage->GetDict(); +- CPDF_Object* pContentObj = GetPageContent(pPageDict); ++ RetainPtr pPageDict = pPage->GetMutableDict(); ++ RetainPtr pContentObj = GetPageContent(pPageDict.Get()); + if (!pContentObj) + return; + +- std::ostringstream strClip; ++ fxcrt::ostringstream strClip; + CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clipPath); + for (size_t i = 0; i < pClipPath->GetPathCount(); ++i) { + CPDF_Path path = pClipPath->GetPath(i); +@@ -399,26 +393,28 @@ FPDF_EXPORT void FPDF_CALLCONV FPDFPage_InsertClipPath(FPDF_PAGE page, + strClip << "0 0 m W n "; + } else { + OutputPath(strClip, path); +- if (pClipPath->GetClipType(i) == FXFILL_WINDING) ++ if (pClipPath->GetClipType(i) == ++ CFX_FillRenderOptions::FillType::kWinding) { + strClip << "W n\n"; +- else ++ } else { + strClip << "W* n\n"; ++ } + } + } + CPDF_Document* pDoc = pPage->GetDocument(); + if (!pDoc) + return; + +- CPDF_Stream* pStream = +- pDoc->NewIndirect(nullptr, 0, pDoc->New()); ++ auto pStream = pDoc->NewIndirect(pDoc->New()); + pStream->SetDataFromStringstream(&strClip); + +- if (CPDF_Array* pArray = ToArray(pContentObj)) { ++ RetainPtr pArray = ToArray(pContentObj); ++ if (pArray) { + pArray->InsertNewAt(0, pDoc, pStream->GetObjNum()); + } else if (pContentObj->IsStream() && !pContentObj->IsInline()) { +- CPDF_Array* pContentArray = pDoc->NewIndirect(); +- pContentArray->AddNew(pDoc, pStream->GetObjNum()); +- pContentArray->AddNew(pDoc, pContentObj->GetObjNum()); ++ auto pContentArray = pDoc->NewIndirect(); ++ pContentArray->AppendNew(pDoc, pStream->GetObjNum()); ++ pContentArray->AppendNew(pDoc, pContentObj->GetObjNum()); + pPageDict->SetNewFor(pdfium::page_object::kContents, pDoc, + pContentArray->GetObjNum()); + } +diff --git a/fpdfsdk/fpdf_transformpage_embeddertest.cpp b/fpdfsdk/fpdf_transformpage_embeddertest.cpp +index 6ebb8d209..807226205 100644 +--- a/fpdfsdk/fpdf_transformpage_embeddertest.cpp ++++ b/fpdfsdk/fpdf_transformpage_embeddertest.cpp +@@ -1,16 +1,30 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "public/fpdf_transformpage.h" + + #include "build/build_config.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" + #include "testing/embedder_test.h" ++#include "testing/embedder_test_constants.h" + +-#if defined(OS_LINUX) || defined(OS_FUCHSIA) ++#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA) + #include "third_party/base/test/scoped_locale.h" + #endif + ++using pdfium::RectanglesChecksum; ++ ++namespace { ++ ++const char* ShrunkChecksum() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "78c52d6029283090036e6db6683401e2"; ++ return "f4136cc9209207ab60eb8381a3df2e69"; ++} ++ ++} // namespace ++ + class FPDFTransformEmbedderTest : public EmbedderTest {}; + + TEST_F(FPDFTransformEmbedderTest, GetBoundingBoxes) { +@@ -194,16 +208,12 @@ TEST_F(FPDFTransformEmbedderTest, NoArtBox) { + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_SetCropBox DISABLED_SetCropBox +-#else +-#define MAYBE_SetCropBox SetCropBox +-#endif +-TEST_F(FPDFTransformEmbedderTest, MAYBE_SetCropBox) { +- const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe"; +- const char kCroppedMD5[] = "9937883715d5144c079fb8f7e3d4f395"; +- ++TEST_F(FPDFTransformEmbedderTest, SetCropBox) { ++ const char* cropped_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "4b9d2d2246be61c583f454245fe3172f"; ++ return "9937883715d5144c079fb8f7e3d4f395"; ++ }(); + { + ASSERT_TRUE(OpenDocument("rectangles.pdf")); + FPDF_PAGE page = LoadPage(0); +@@ -219,7 +229,8 @@ TEST_F(FPDFTransformEmbedderTest, MAYBE_SetCropBox) { + EXPECT_EQ(200, page_width); + EXPECT_EQ(300, page_height); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5); ++ CompareBitmap(bitmap.get(), page_width, page_height, ++ RectanglesChecksum()); + } + + FPDFPage_SetCropBox(page, 10, 20, 100, 150); +@@ -240,7 +251,7 @@ TEST_F(FPDFTransformEmbedderTest, MAYBE_SetCropBox) { + EXPECT_EQ(90, page_width); + EXPECT_EQ(130, page_height); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), page_width, page_height, kCroppedMD5); ++ CompareBitmap(bitmap.get(), page_width, page_height, cropped_checksum); + } + + UnloadPage(page); +@@ -266,22 +277,19 @@ TEST_F(FPDFTransformEmbedderTest, MAYBE_SetCropBox) { + EXPECT_EQ(90, page_width); + EXPECT_EQ(130, page_height); + ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page); +- CompareBitmap(bitmap.get(), page_width, page_height, kCroppedMD5); ++ CompareBitmap(bitmap.get(), page_width, page_height, cropped_checksum); + + CloseSavedPage(saved_page); + CloseSavedDocument(); + } + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_SetMediaBox DISABLED_SetMediaBox +-#else +-#define MAYBE_SetMediaBox SetMediaBox +-#endif +-TEST_F(FPDFTransformEmbedderTest, MAYBE_SetMediaBox) { +- const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe"; +- const char kShrunkMD5[] = "eab5958f62f7ce65d7c32de98389fee1"; ++TEST_F(FPDFTransformEmbedderTest, SetMediaBox) { ++ const char* shrunk_checksum_set_media_box = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "9f28f0610a7f789c24cfd5f9bd5dc3de"; ++ return "eab5958f62f7ce65d7c32de98389fee1"; ++ }(); + + { + ASSERT_TRUE(OpenDocument("rectangles.pdf")); +@@ -298,7 +306,8 @@ TEST_F(FPDFTransformEmbedderTest, MAYBE_SetMediaBox) { + EXPECT_EQ(200, page_width); + EXPECT_EQ(300, page_height); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5); ++ CompareBitmap(bitmap.get(), page_width, page_height, ++ RectanglesChecksum()); + } + + FPDFPage_SetMediaBox(page, 20, 30, 100, 150); +@@ -319,7 +328,8 @@ TEST_F(FPDFTransformEmbedderTest, MAYBE_SetMediaBox) { + EXPECT_EQ(80, page_width); + EXPECT_EQ(120, page_height); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), page_width, page_height, kShrunkMD5); ++ CompareBitmap(bitmap.get(), page_width, page_height, ++ shrunk_checksum_set_media_box); + } + + UnloadPage(page); +@@ -346,7 +356,8 @@ TEST_F(FPDFTransformEmbedderTest, MAYBE_SetMediaBox) { + EXPECT_EQ(80, page_width); + EXPECT_EQ(120, page_height); + ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page); +- CompareBitmap(bitmap.get(), page_width, page_height, kShrunkMD5); ++ CompareBitmap(bitmap.get(), page_width, page_height, ++ shrunk_checksum_set_media_box); + + CloseSavedPage(saved_page); + CloseSavedDocument(); +@@ -412,16 +423,7 @@ TEST_F(FPDFTransformEmbedderTest, TransFormWithClipWithPatterns) { + UnloadPage(page); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_TransFormWithClipAndSave DISABLED_TransFormWithClipAndSave +-#else +-#define MAYBE_TransFormWithClipAndSave TransFormWithClipAndSave +-#endif +-TEST_F(FPDFTransformEmbedderTest, MAYBE_TransFormWithClipAndSave) { +- const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe"; +- const char kShrunkMD5[] = "f4136cc9209207ab60eb8381a3df2e69"; +- ++TEST_F(FPDFTransformEmbedderTest, TransFormWithClipAndSave) { + { + ASSERT_TRUE(OpenDocument("rectangles.pdf")); + FPDF_PAGE page = LoadPage(0); +@@ -434,14 +436,16 @@ TEST_F(FPDFTransformEmbedderTest, MAYBE_TransFormWithClipAndSave) { + EXPECT_EQ(200, page_width); + EXPECT_EQ(300, page_height); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5); ++ CompareBitmap(bitmap.get(), page_width, page_height, ++ RectanglesChecksum()); + } + + { + // Render the page after transforming. + // Note that the change should affect the rendering, but does not. + // It should behaves just like the case below, rather than the case above. +- // TODO(bug_1328): The checksum below should be |kShrunkMD5|. ++ // TODO(crbug.com/pdfium/1328): The checksum after invoking ++ // `FPDFPage_TransFormWithClip()` below should match `ShrunkChecksum()`. + const FS_MATRIX half_matrix{0.5, 0, 0, 0.5, 0, 0}; + EXPECT_TRUE(FPDFPage_TransFormWithClip(page, &half_matrix, nullptr)); + const int page_width = static_cast(FPDF_GetPageWidth(page)); +@@ -449,7 +453,8 @@ TEST_F(FPDFTransformEmbedderTest, MAYBE_TransFormWithClipAndSave) { + EXPECT_EQ(200, page_width); + EXPECT_EQ(300, page_height); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5); ++ CompareBitmap(bitmap.get(), page_width, page_height, ++ RectanglesChecksum()); + } + + UnloadPage(page); +@@ -468,26 +473,15 @@ TEST_F(FPDFTransformEmbedderTest, MAYBE_TransFormWithClipAndSave) { + EXPECT_EQ(200, page_width); + EXPECT_EQ(300, page_height); + ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page); +- CompareBitmap(bitmap.get(), page_width, page_height, kShrunkMD5); ++ CompareBitmap(bitmap.get(), page_width, page_height, ShrunkChecksum()); + + CloseSavedPage(saved_page); + CloseSavedDocument(); + } + } + +-#if defined(OS_LINUX) || defined(OS_FUCHSIA) +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_TransFormWithClipAndSaveWithLocale \ +- DISABLED_TransFormWithClipAndSaveWithLocale +-#else +-#define MAYBE_TransFormWithClipAndSaveWithLocale \ +- TransFormWithClipAndSaveWithLocale +-#endif +-TEST_F(FPDFTransformEmbedderTest, MAYBE_TransFormWithClipAndSaveWithLocale) { +- const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe"; +- const char kShrunkMD5[] = "f4136cc9209207ab60eb8381a3df2e69"; +- ++#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA) ++TEST_F(FPDFTransformEmbedderTest, TransFormWithClipAndSaveWithLocale) { + pdfium::base::ScopedLocale scoped_locale("da_DK.UTF-8"); + + { +@@ -502,14 +496,16 @@ TEST_F(FPDFTransformEmbedderTest, MAYBE_TransFormWithClipAndSaveWithLocale) { + EXPECT_EQ(200, page_width); + EXPECT_EQ(300, page_height); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5); ++ CompareBitmap(bitmap.get(), page_width, page_height, ++ RectanglesChecksum()); + } + + { + // Render the page after transforming. + // Note that the change should affect the rendering, but does not. + // It should behaves just like the case below, rather than the case above. +- // TODO(bug_1328): The checksum below should be |kShrunkMD5|. ++ // TODO(crbug.com/pdfium/1328): The checksum after invoking ++ // `FPDFPage_TransFormWithClip()` below should match `ShrunkChecksum()`. + const FS_MATRIX half_matrix{0.5, 0, 0, 0.5, 0, 0}; + EXPECT_TRUE(FPDFPage_TransFormWithClip(page, &half_matrix, nullptr)); + const int page_width = static_cast(FPDF_GetPageWidth(page)); +@@ -517,7 +513,8 @@ TEST_F(FPDFTransformEmbedderTest, MAYBE_TransFormWithClipAndSaveWithLocale) { + EXPECT_EQ(200, page_width); + EXPECT_EQ(300, page_height); + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5); ++ CompareBitmap(bitmap.get(), page_width, page_height, ++ RectanglesChecksum()); + } + + UnloadPage(page); +@@ -536,10 +533,11 @@ TEST_F(FPDFTransformEmbedderTest, MAYBE_TransFormWithClipAndSaveWithLocale) { + EXPECT_EQ(200, page_width); + EXPECT_EQ(300, page_height); + ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page); +- CompareBitmap(bitmap.get(), page_width, page_height, kShrunkMD5); ++ CompareBitmap(bitmap.get(), page_width, page_height, ShrunkChecksum()); + + CloseSavedPage(saved_page); + CloseSavedDocument(); + } + } +-#endif // defined(OS_LINUX) || defined(OS_FUCHSIA) ++#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || ++ // BUILDFLAG(IS_FUCHSIA) +diff --git a/fpdfsdk/fpdf_view.cpp b/fpdfsdk/fpdf_view.cpp +index da2530877..d65579d32 100644 +--- a/fpdfsdk/fpdf_view.cpp ++++ b/fpdfsdk/fpdf_view.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,23 +14,28 @@ + #include "core/fpdfapi/page/cpdf_docpagedata.h" + #include "core/fpdfapi/page/cpdf_occontext.h" + #include "core/fpdfapi/page/cpdf_page.h" ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" + #include "core/fpdfapi/page/cpdf_pagemodule.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_parser.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" ++#include "core/fpdfapi/parser/cpdf_string.h" + #include "core/fpdfapi/parser/fpdf_parser_decode.h" + #include "core/fpdfapi/render/cpdf_docrenderdata.h" +-#include "core/fpdfapi/render/cpdf_pagerendercache.h" + #include "core/fpdfapi/render/cpdf_pagerendercontext.h" + #include "core/fpdfapi/render/cpdf_rendercontext.h" + #include "core/fpdfapi/render/cpdf_renderoptions.h" + #include "core/fpdfdoc/cpdf_nametree.h" + #include "core/fpdfdoc/cpdf_viewerpreferences.h" +-#include "core/fxcrt/cfx_readonlymemorystream.h" ++#include "core/fxcrt/cfx_read_only_span_stream.h" ++#include "core/fxcrt/fx_safe_types.h" + #include "core/fxcrt/fx_stream.h" + #include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/span_util.h" ++#include "core/fxcrt/stl_util.h" + #include "core/fxcrt/unowned_ptr.h" + #include "core/fxge/cfx_defaultrenderdevice.h" + #include "core/fxge/cfx_gemodule.h" +@@ -42,54 +47,160 @@ + #include "fpdfsdk/cpdfsdk_renderpage.h" + #include "fxjs/ijs_runtime.h" + #include "public/fpdf_formfill.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/numerics/safe_conversions.h" + #include "third_party/base/ptr_util.h" + #include "third_party/base/span.h" + ++#if defined(_SKIA_SUPPORT_) ++#include "third_party/skia/include/core/SkPictureRecorder.h" // nogncheck ++#include "third_party/skia/include/core/SkRect.h" // nogncheck ++#endif // defined(_SKIA_SUPPORT_) ++ ++#ifdef PDF_ENABLE_V8 ++#include "fxjs/cfx_v8_array_buffer_allocator.h" ++#include "third_party/base/no_destructor.h" ++#endif ++ + #ifdef PDF_ENABLE_XFA + #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h" + #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h" +-#include "fxbarcode/BC_Library.h" + #endif // PDF_ENABLE_XFA + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + #include "core/fpdfapi/render/cpdf_progressiverenderer.h" + #include "core/fpdfapi/render/cpdf_windowsrenderdevice.h" + #include "public/fpdf_edit.h" + + // These checks are here because core/ and public/ cannot depend on each other. +-static_assert(WindowsPrintMode::kModeEmf == FPDF_PRINTMODE_EMF, +- "WindowsPrintMode::kModeEmf value mismatch"); +-static_assert(WindowsPrintMode::kModeTextOnly == FPDF_PRINTMODE_TEXTONLY, +- "WindowsPrintMode::kModeTextOnly value mismatch"); +-static_assert(WindowsPrintMode::kModePostScript2 == FPDF_PRINTMODE_POSTSCRIPT2, +- "WindowsPrintMode::kModePostScript2 value mismatch"); +-static_assert(WindowsPrintMode::kModePostScript3 == FPDF_PRINTMODE_POSTSCRIPT3, +- "WindowsPrintMode::kModePostScript3 value mismatch"); +-static_assert(WindowsPrintMode::kModePostScript2PassThrough == ++static_assert(static_cast(WindowsPrintMode::kEmf) == FPDF_PRINTMODE_EMF, ++ "WindowsPrintMode::kEmf value mismatch"); ++static_assert(static_cast(WindowsPrintMode::kTextOnly) == ++ FPDF_PRINTMODE_TEXTONLY, ++ "WindowsPrintMode::kTextOnly value mismatch"); ++static_assert(static_cast(WindowsPrintMode::kPostScript2) == ++ FPDF_PRINTMODE_POSTSCRIPT2, ++ "WindowsPrintMode::kPostScript2 value mismatch"); ++static_assert(static_cast(WindowsPrintMode::kPostScript3) == ++ FPDF_PRINTMODE_POSTSCRIPT3, ++ "WindowsPrintMode::kPostScript3 value mismatch"); ++static_assert(static_cast(WindowsPrintMode::kPostScript2PassThrough) == + FPDF_PRINTMODE_POSTSCRIPT2_PASSTHROUGH, +- "WindowsPrintMode::kModePostScript2PassThrough value mismatch"); +-static_assert(WindowsPrintMode::kModePostScript3PassThrough == ++ "WindowsPrintMode::kPostScript2PassThrough value mismatch"); ++static_assert(static_cast(WindowsPrintMode::kPostScript3PassThrough) == + FPDF_PRINTMODE_POSTSCRIPT3_PASSTHROUGH, +- "WindowsPrintMode::kModePostScript3PassThrough value mismatch"); +-#endif // defined(OS_WIN) ++ "WindowsPrintMode::kPostScript3PassThrough value mismatch"); ++static_assert(static_cast(WindowsPrintMode::kEmfImageMasks) == ++ FPDF_PRINTMODE_EMF_IMAGE_MASKS, ++ "WindowsPrintMode::kEmfImageMasks value mismatch"); ++static_assert(static_cast(WindowsPrintMode::kPostScript3Type42) == ++ FPDF_PRINTMODE_POSTSCRIPT3_TYPE42, ++ "WindowsPrintMode::kPostScript3Type42 value mismatch"); ++static_assert( ++ static_cast(WindowsPrintMode::kPostScript3Type42PassThrough) == ++ FPDF_PRINTMODE_POSTSCRIPT3_TYPE42_PASSTHROUGH, ++ "WindowsPrintMode::kPostScript3Type42PassThrough value mismatch"); ++#endif // BUILDFLAG(IS_WIN) ++ ++#if defined(_SKIA_SUPPORT_) ++// These checks are here because core/ and public/ cannot depend on each other. ++static_assert(static_cast(CFX_DefaultRenderDevice::RendererType::kAgg) == ++ FPDF_RENDERERTYPE_AGG, ++ "CFX_DefaultRenderDevice::RendererType::kAGG value mismatch"); ++static_assert(static_cast(CFX_DefaultRenderDevice::RendererType::kSkia) == ++ FPDF_RENDERERTYPE_SKIA, ++ "CFX_DefaultRenderDevice::RendererType::kSkia value mismatch"); ++#endif // defined(_SKIA_SUPPORT_) + + namespace { + + bool g_bLibraryInitialized = false; + +-FPDF_DOCUMENT LoadDocumentImpl( +- const RetainPtr& pFileAccess, +- FPDF_BYTESTRING password) { ++void UseRendererType(FPDF_RENDERER_TYPE public_type) { ++ // Internal definition of renderer types must stay updated with respect to ++ // the public definition, such that all public definitions can be mapped to ++ // an internal definition in `CFX_DefaultRenderDevice`. A public definition ++ // value might not be meaningful for a particular build configuration, which ++ // would mean use of that value is an error for that build. ++ ++ // AGG is always present in a build. |FPDF_RENDERERTYPE_SKIA| is valid to use ++ // only if it is included in the build. ++#if defined(_SKIA_SUPPORT_) ++ // This build configuration has the option for runtime renderer selection. ++ if (public_type == FPDF_RENDERERTYPE_AGG || ++ public_type == FPDF_RENDERERTYPE_SKIA) { ++ CFX_DefaultRenderDevice::SetDefaultRenderer( ++ static_cast(public_type)); ++ return; ++ } ++ CHECK(false); ++#else ++ // `FPDF_RENDERERTYPE_AGG` is used for fully AGG builds. ++ CHECK_EQ(public_type, FPDF_RENDERERTYPE_AGG); ++#endif ++} ++ ++RetainPtr GetXFAEntryFromDocument(const CPDF_Document* doc) { ++ const CPDF_Dictionary* root = doc->GetRoot(); ++ if (!root) ++ return nullptr; ++ ++ RetainPtr acro_form = root->GetDictFor("AcroForm"); ++ return acro_form ? acro_form->GetObjectFor("XFA") : nullptr; ++} ++ ++struct XFAPacket { ++ ByteString name; ++ RetainPtr data; ++}; ++ ++std::vector GetXFAPackets(RetainPtr xfa_object) { ++ std::vector packets; ++ ++ if (!xfa_object) ++ return packets; ++ ++ RetainPtr xfa_stream = ToStream(xfa_object->GetDirect()); ++ if (xfa_stream) { ++ packets.push_back({"", std::move(xfa_stream)}); ++ return packets; ++ } ++ ++ RetainPtr xfa_array = ToArray(xfa_object->GetDirect()); ++ if (!xfa_array) ++ return packets; ++ ++ packets.reserve(1 + (xfa_array->size() / 2)); ++ for (size_t i = 0; i < xfa_array->size(); i += 2) { ++ if (i + 1 == xfa_array->size()) ++ break; ++ ++ RetainPtr name = xfa_array->GetStringAt(i); ++ if (!name) ++ continue; ++ ++ RetainPtr data = xfa_array->GetStreamAt(i + 1); ++ if (!data) ++ continue; ++ ++ packets.push_back({name->GetString(), std::move(data)}); ++ } ++ return packets; ++} ++ ++FPDF_DOCUMENT LoadDocumentImpl(RetainPtr pFileAccess, ++ FPDF_BYTESTRING password) { + if (!pFileAccess) { + ProcessParseError(CPDF_Parser::FILE_ERROR); + return nullptr; + } + +- auto pDocument = pdfium::MakeUnique( +- pdfium::MakeUnique(), +- pdfium::MakeUnique()); ++ auto pDocument = ++ std::make_unique(std::make_unique(), ++ std::make_unique()); + +- CPDF_Parser::Error error = pDocument->LoadDoc(pFileAccess, password); ++ CPDF_Parser::Error error = ++ pDocument->LoadDoc(std::move(pFileAccess), password); + if (error != CPDF_Parser::SUCCESS) { + ProcessParseError(error); + return nullptr; +@@ -110,16 +221,22 @@ FPDF_InitLibraryWithConfig(const FPDF_LIBRARY_CONFIG* config) { + if (g_bLibraryInitialized) + return; + +- FXMEM_InitializePartitionAlloc(); ++ FX_InitializeMemoryAllocators(); + CFX_GEModule::Create(config ? config->m_pUserFontPaths : nullptr); + CPDF_PageModule::Create(); + + #ifdef PDF_ENABLE_XFA +- BC_Library_Init(); ++ CPDFXFA_ModuleInit(); + #endif // PDF_ENABLE_XFA +- if (config && config->version >= 2) +- IJS_Runtime::Initialize(config->m_v8EmbedderSlot, config->m_pIsolate); + ++ if (config && config->version >= 2) { ++ void* platform = config->version >= 3 ? config->m_pPlatform : nullptr; ++ IJS_Runtime::Initialize(config->m_v8EmbedderSlot, config->m_pIsolate, ++ platform); ++ ++ if (config->version >= 4) ++ UseRendererType(config->m_RendererType); ++ } + g_bLibraryInitialized = true; + } + +@@ -128,7 +245,7 @@ FPDF_EXPORT void FPDF_CALLCONV FPDF_DestroyLibrary() { + return; + + #ifdef PDF_ENABLE_XFA +- BC_Library_Destroy(); ++ CPDFXFA_ModuleDestroy(); + #endif // PDF_ENABLE_XFA + + CPDF_PageModule::Destroy(); +@@ -143,27 +260,17 @@ FPDF_EXPORT void FPDF_CALLCONV FPDF_SetSandBoxPolicy(FPDF_DWORD policy, + return SetPDFSandboxPolicy(policy, enable); + } + +-#if defined(OS_WIN) +-#if defined(PDFIUM_PRINT_TEXT_WITH_GDI) +-FPDF_EXPORT void FPDF_CALLCONV +-FPDF_SetTypefaceAccessibleFunc(PDFiumEnsureTypefaceCharactersAccessible func) { +- g_pdfium_typeface_accessible_func = func; +-} +- +-FPDF_EXPORT void FPDF_CALLCONV FPDF_SetPrintTextWithGDI(FPDF_BOOL use_gdi) { +- g_pdfium_print_text_with_gdi = !!use_gdi; +-} +-#endif // PDFIUM_PRINT_TEXT_WITH_GDI +- ++#if BUILDFLAG(IS_WIN) + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SetPrintMode(int mode) { + if (mode < FPDF_PRINTMODE_EMF || +- mode > FPDF_PRINTMODE_POSTSCRIPT3_PASSTHROUGH) { ++ mode > FPDF_PRINTMODE_POSTSCRIPT3_TYPE42_PASSTHROUGH) { + return FALSE; + } ++ + g_pdfium_print_mode = static_cast(mode); + return TRUE; + } +-#endif // defined(OS_WIN) ++#endif // BUILDFLAG(IS_WIN) + + FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV + FPDF_LoadDocument(FPDF_STRING file_path, FPDF_BYTESTRING password) { +@@ -182,11 +289,11 @@ FPDF_EXPORT int FPDF_CALLCONV FPDF_GetFormType(FPDF_DOCUMENT document) { + if (!pRoot) + return FORMTYPE_NONE; + +- const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm"); ++ RetainPtr pAcroForm = pRoot->GetDictFor("AcroForm"); + if (!pAcroForm) + return FORMTYPE_NONE; + +- const CPDF_Object* pXFA = pAcroForm->GetObjectFor("XFA"); ++ RetainPtr pXFA = pAcroForm->GetObjectFor("XFA"); + if (!pXFA) + return FORMTYPE_ACRO_FORM; + +@@ -210,7 +317,17 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_LoadXFA(FPDF_DOCUMENT document) { + FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV + FPDF_LoadMemDocument(const void* data_buf, int size, FPDF_BYTESTRING password) { + return LoadDocumentImpl( +- pdfium::MakeRetain( ++ pdfium::MakeRetain( ++ pdfium::make_span(static_cast(data_buf), size)), ++ password); ++} ++ ++FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV ++FPDF_LoadMemDocument64(const void* data_buf, ++ size_t size, ++ FPDF_BYTESTRING password) { ++ return LoadDocumentImpl( ++ pdfium::MakeRetain( + pdfium::make_span(static_cast(data_buf), size)), + password); + } +@@ -260,7 +377,7 @@ FPDF_GetSecurityHandlerRevision(FPDF_DOCUMENT document) { + if (!pDoc || !pDoc->GetParser()) + return -1; + +- const CPDF_Dictionary* pDict = pDoc->GetParser()->GetEncryptDict(); ++ RetainPtr pDict = pDoc->GetParser()->GetEncryptDict(); + return pDict ? pDict->GetIntegerFor("R") : -1; + } + +@@ -284,17 +401,20 @@ FPDF_EXPORT FPDF_PAGE FPDF_CALLCONV FPDF_LoadPage(FPDF_DOCUMENT document, + + #ifdef PDF_ENABLE_XFA + auto* pContext = static_cast(pDoc->GetExtension()); +- if (pContext) +- return FPDFPageFromIPDFPage(pContext->GetXFAPage(page_index).Leak()); ++ if (pContext) { ++ return FPDFPageFromIPDFPage( ++ pContext->GetOrCreateXFAPage(page_index).Leak()); ++ } + #endif // PDF_ENABLE_XFA + +- CPDF_Dictionary* pDict = pDoc->GetPageDictionary(page_index); ++ RetainPtr pDict = pDoc->GetMutablePageDictionary(page_index); + if (!pDict) + return nullptr; + +- auto pPage = pdfium::MakeRetain(pDoc, pDict); +- pPage->SetRenderCache(pdfium::MakeUnique(pPage.Get())); ++ auto pPage = pdfium::MakeRetain(pDoc, std::move(pDict)); ++ pPage->AddPageImageCache(); + pPage->ParseContent(); ++ + return FPDFPageFromIPDFPage(pPage.Leak()); + } + +@@ -329,84 +449,27 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_GetPageBoundingBox(FPDF_PAGE page, + return true; + } + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + namespace { + +-const double kEpsilonSize = 0.01f; +- +-void GetScaling(CPDF_Page* pPage, +- int size_x, +- int size_y, +- int rotate, +- double* scale_x, +- double* scale_y) { +- ASSERT(pPage); +- ASSERT(scale_x); +- ASSERT(scale_y); +- double page_width = pPage->GetPageWidth(); +- double page_height = pPage->GetPageHeight(); +- if (page_width < kEpsilonSize || page_height < kEpsilonSize) +- return; ++constexpr float kEpsilonSize = 0.01f; + +- if (rotate % 2 == 0) { +- *scale_x = size_x / page_width; +- *scale_y = size_y / page_height; +- } else { +- *scale_x = size_y / page_width; +- *scale_y = size_x / page_height; +- } ++bool IsPageTooSmall(const CPDF_Page* page) { ++ const CFX_SizeF& page_size = page->GetPageSize(); ++ return page_size.width < kEpsilonSize || page_size.height < kEpsilonSize; + } + +-FX_RECT GetMaskDimensionsAndOffsets(CPDF_Page* pPage, +- int start_x, +- int start_y, +- int size_x, +- int size_y, +- int rotate, +- const CFX_FloatRect& mask_box) { +- double scale_x = 0.0f; +- double scale_y = 0.0f; +- GetScaling(pPage, size_x, size_y, rotate, &scale_x, &scale_y); +- if (scale_x < kEpsilonSize || scale_y < kEpsilonSize) +- return FX_RECT(); +- +- // Compute sizes in page points. Round down to catch the entire bitmap. +- int start_x_bm = static_cast(mask_box.left * scale_x); +- int start_y_bm = static_cast(mask_box.bottom * scale_y); +- int size_x_bm = static_cast(mask_box.right * scale_x + 1.0f) - +- static_cast(mask_box.left * scale_x); +- int size_y_bm = static_cast(mask_box.top * scale_y + 1.0f) - +- static_cast(mask_box.bottom * scale_y); +- +- // Get page rotation +- int page_rotation = pPage->GetPageRotation(); +- +- // Compute offsets +- int offset_x = 0; +- int offset_y = 0; +- if (size_x > size_y) +- std::swap(size_x_bm, size_y_bm); +- +- switch ((rotate + page_rotation) % 4) { +- case 0: +- offset_x = start_x_bm + start_x; +- offset_y = start_y + size_y - size_y_bm - start_y_bm; +- break; +- case 1: +- offset_x = start_y_bm + start_x; +- offset_y = start_x_bm + start_y; +- break; +- case 2: +- offset_x = start_x + size_x - size_x_bm - start_x_bm; +- offset_y = start_y_bm + start_y; +- break; +- case 3: +- offset_x = start_x + size_x - size_x_bm - start_y_bm; +- offset_y = start_y + size_y - size_y_bm - start_x_bm; +- break; ++bool IsScalingTooSmall(const CFX_Matrix& matrix) { ++ float horizontal; ++ float vertical; ++ if (matrix.a == 0.0f && matrix.d == 0.0f) { ++ horizontal = matrix.b; ++ vertical = matrix.c; ++ } else { ++ horizontal = matrix.a; ++ vertical = matrix.d; + } +- return FX_RECT(offset_x, offset_y, offset_x + size_x_bm, +- offset_y + size_y_bm); ++ return fabsf(horizontal) < kEpsilonSize || fabsf(vertical) < kEpsilonSize; + } + + // Get a bitmap of just the mask section defined by |mask_box| from a full page +@@ -420,17 +483,24 @@ RetainPtr GetMaskBitmap(CPDF_Page* pPage, + const RetainPtr& pSrc, + const CFX_FloatRect& mask_box, + FX_RECT* bitmap_area) { +- ASSERT(bitmap_area); +- *bitmap_area = GetMaskDimensionsAndOffsets(pPage, start_x, start_y, size_x, +- size_y, rotate, mask_box); ++ if (IsPageTooSmall(pPage)) ++ return nullptr; ++ ++ FX_RECT page_rect(start_x, start_y, start_x + size_x, start_y + size_y); ++ CFX_Matrix matrix = pPage->GetDisplayMatrix(page_rect, rotate); ++ if (IsScalingTooSmall(matrix)) ++ return nullptr; ++ ++ *bitmap_area = matrix.TransformRect(mask_box).GetOuterRect(); + if (bitmap_area->IsEmpty()) + return nullptr; + + // Create a new bitmap to transfer part of the page bitmap to. + RetainPtr pDst = pdfium::MakeRetain(); +- if (!pDst->Create(bitmap_area->Width(), bitmap_area->Height(), FXDIB_Argb)) ++ if (!pDst->Create(bitmap_area->Width(), bitmap_area->Height(), ++ FXDIB_Format::kArgb)) { + return nullptr; +- ++ } + pDst->Clear(0x00ffffff); + pDst->TransferBitmap(0, 0, bitmap_area->Width(), bitmap_area->Height(), pSrc, + bitmap_area->left, bitmap_area->top); +@@ -447,7 +517,7 @@ void RenderBitmap(CFX_RenderDevice* device, + + // Create a new bitmap from the old one + RetainPtr pDst = pdfium::MakeRetain(); +- if (!pDst->Create(size_x_bm, size_y_bm, FXDIB_Rgb32)) ++ if (!pDst->Create(size_x_bm, size_y_bm, FXDIB_Format::kRgb32)) + return; + + pDst->Clear(0xffffffff); +@@ -476,7 +546,7 @@ FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPage(HDC dc, + if (!pPage) + return; + +- auto pOwnedContext = pdfium::MakeUnique(); ++ auto pOwnedContext = std::make_unique(); + CPDF_PageRenderContext* pContext = pOwnedContext.get(); + CPDF_Page::RenderContextClearer clearer(pPage); + pPage->SetRenderContext(std::move(pOwnedContext)); +@@ -485,17 +555,20 @@ FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPage(HDC dc, + // of masks. Full page bitmaps result in large spool sizes, so they should + // only be used when necessary. For large numbers of masks, rendering each + // individually is inefficient and unlikely to significantly improve spool +- // size. TODO(rbpotter): Find out why this still breaks printing for some +- // PDFs (see crbug.com/777837). +- const bool bEnableImageMasks = false; ++ // size. ++ const bool bEnableImageMasks = ++ g_pdfium_print_mode == WindowsPrintMode::kEmfImageMasks; + const bool bNewBitmap = pPage->BackgroundAlphaNeeded() || + (pPage->HasImageMask() && !bEnableImageMasks) || + pPage->GetMaskBoundingBoxes().size() > 100; + const bool bHasMask = pPage->HasImageMask() && !bNewBitmap; ++ auto* render_data = CPDF_DocRenderData::FromDocument(pPage->GetDocument()); + if (!bNewBitmap && !bHasMask) { +- pContext->m_pDevice = pdfium::MakeUnique(dc); ++ pContext->m_pDevice = std::make_unique( ++ dc, render_data->GetPSFontTracker()); + CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x, + size_y, rotate, flags, ++ /*color_scheme=*/nullptr, + /*need_to_restore=*/true, /*pause=*/nullptr); + return; + } +@@ -503,35 +576,37 @@ FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPage(HDC dc, + RetainPtr pBitmap = pdfium::MakeRetain(); + // Create will probably work fine even if it fails here: we will just attach + // a zero-sized bitmap to |pDevice|. +- pBitmap->Create(size_x, size_y, FXDIB_Argb); ++ pBitmap->Create(size_x, size_y, FXDIB_Format::kArgb); + pBitmap->Clear(0x00ffffff); + CFX_DefaultRenderDevice* pDevice = new CFX_DefaultRenderDevice; + pContext->m_pDevice = pdfium::WrapUnique(pDevice); +- pDevice->Attach(pBitmap, false, nullptr, false); ++ pDevice->Attach(pBitmap); + if (bHasMask) { +- pContext->m_pOptions = pdfium::MakeUnique(); ++ pContext->m_pOptions = std::make_unique(); + pContext->m_pOptions->GetOptions().bBreakForMasks = true; + } + + CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x, +- size_y, rotate, flags, /*need_to_restore=*/true, ++ size_y, rotate, flags, /*color_scheme=*/nullptr, ++ /*need_to_restore=*/true, + /*pause=*/nullptr); + + if (!bHasMask) { +- CPDF_WindowsRenderDevice WinDC(dc); ++ CPDF_WindowsRenderDevice win_dc(dc, render_data->GetPSFontTracker()); + bool bitsStretched = false; +- if (WinDC.GetDeviceType() == DeviceType::kPrinter) { ++ if (win_dc.GetDeviceType() == DeviceType::kPrinter) { + auto pDst = pdfium::MakeRetain(); +- if (pDst->Create(size_x, size_y, FXDIB_Rgb32)) { +- memset(pDst->GetBuffer(), -1, pBitmap->GetPitch() * size_y); ++ if (pDst->Create(size_x, size_y, FXDIB_Format::kRgb32)) { ++ fxcrt::spanset(pDst->GetBuffer().first(pBitmap->GetPitch() * size_y), ++ -1); + pDst->CompositeBitmap(0, 0, size_x, size_y, pBitmap, 0, 0, + BlendMode::kNormal, nullptr, false); +- WinDC.StretchDIBits(pDst, 0, 0, size_x, size_y); ++ win_dc.StretchDIBits(pDst, 0, 0, size_x, size_y); + bitsStretched = true; + } + } + if (!bitsStretched) +- WinDC.SetDIBits(pBitmap, 0, 0); ++ win_dc.SetDIBits(pBitmap, 0, 0); + return; + } + +@@ -548,15 +623,18 @@ FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPage(HDC dc, + + // Begin rendering to the printer. Add flag to indicate the renderer should + // pause after each image mask. +- pOwnedContext = pdfium::MakeUnique(); ++ pPage->ClearRenderContext(); ++ pOwnedContext = std::make_unique(); + pContext = pOwnedContext.get(); + pPage->SetRenderContext(std::move(pOwnedContext)); +- pContext->m_pDevice = pdfium::MakeUnique(dc); +- pContext->m_pOptions = pdfium::MakeUnique(); ++ pContext->m_pDevice = std::make_unique( ++ dc, render_data->GetPSFontTracker()); ++ pContext->m_pOptions = std::make_unique(); + pContext->m_pOptions->GetOptions().bBreakForMasks = true; + + CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x, +- size_y, rotate, flags, /*need_to_restore=*/true, ++ size_y, rotate, flags, /*color_scheme=*/nullptr, ++ /*need_to_restore=*/true, + /*pause=*/nullptr); + + // Render masks +@@ -569,7 +647,7 @@ FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPage(HDC dc, + pContext->m_pRenderer->Continue(nullptr); + } + } +-#endif // defined(OS_WIN) ++#endif // BUILDFLAG(IS_WIN) + + FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPageBitmap(FPDF_BITMAP bitmap, + FPDF_PAGE page, +@@ -586,24 +664,26 @@ FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPageBitmap(FPDF_BITMAP bitmap, + if (!pPage) + return; + +- auto pOwnedContext = pdfium::MakeUnique(); ++ auto pOwnedContext = std::make_unique(); + CPDF_PageRenderContext* pContext = pOwnedContext.get(); + CPDF_Page::RenderContextClearer clearer(pPage); + pPage->SetRenderContext(std::move(pOwnedContext)); + +- auto pOwnedDevice = pdfium::MakeUnique(); ++ auto pOwnedDevice = std::make_unique(); + CFX_DefaultRenderDevice* pDevice = pOwnedDevice.get(); + pContext->m_pDevice = std::move(pOwnedDevice); + + RetainPtr pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap)); +- pDevice->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false); ++ pDevice->AttachWithRgbByteOrder(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER)); + CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x, +- size_y, rotate, flags, /*need_to_restore=*/true, ++ size_y, rotate, flags, /*color_scheme=*/nullptr, ++ /*need_to_restore=*/true, + /*pause=*/nullptr); + +-#ifdef _SKIA_SUPPORT_PATHS_ +- pDevice->Flush(true); +- pBitmap->UnPreMultiply(); ++#if defined(_SKIA_SUPPORT_) ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ pBitmap->UnPreMultiply(); ++ } + #endif + } + +@@ -620,17 +700,18 @@ FPDF_RenderPageBitmapWithMatrix(FPDF_BITMAP bitmap, + if (!pPage) + return; + +- auto pOwnedContext = pdfium::MakeUnique(); ++ auto pOwnedContext = std::make_unique(); + CPDF_PageRenderContext* pContext = pOwnedContext.get(); + CPDF_Page::RenderContextClearer clearer(pPage); + pPage->SetRenderContext(std::move(pOwnedContext)); + +- auto pOwnedDevice = pdfium::MakeUnique(); ++ auto pOwnedDevice = std::make_unique(); + CFX_DefaultRenderDevice* pDevice = pOwnedDevice.get(); + pContext->m_pDevice = std::move(pOwnedDevice); + + RetainPtr pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap)); +- pDevice->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false); ++ pDevice->AttachWithRgbByteOrder(std::move(pBitmap), ++ !!(flags & FPDF_REVERSE_BYTE_ORDER)); + + CFX_FloatRect clipping_rect; + if (clipping) +@@ -641,31 +722,43 @@ FPDF_RenderPageBitmapWithMatrix(FPDF_BITMAP bitmap, + CFX_Matrix transform_matrix = pPage->GetDisplayMatrix(rect, 0); + if (matrix) + transform_matrix *= CFXMatrixFromFSMatrix(*matrix); +- CPDFSDK_RenderPage(pContext, pPage, transform_matrix, clip_rect, flags); ++ CPDFSDK_RenderPage(pContext, pPage, transform_matrix, clip_rect, flags, ++ /*color_scheme=*/nullptr); + } + +-#ifdef _SKIA_SUPPORT_ ++#if defined(_SKIA_SUPPORT_) + FPDF_EXPORT FPDF_RECORDER FPDF_CALLCONV FPDF_RenderPageSkp(FPDF_PAGE page, + int size_x, + int size_y) { ++ auto skDevice = std::make_unique(); ++ std::unique_ptr recorder = ++ skDevice->CreateRecorder(SkRect::MakeWH(size_x, size_y)); ++ + CPDF_Page* pPage = CPDFPageFromFPDFPage(page); +- if (!pPage) +- return nullptr; ++ if (!pPage) { ++ // The equivalent bitmap APIs don't signal failure in this case, but defer ++ // the real work to a later call to `FPDF_FFLDraw()`. This is the case for ++ // XFA pages, for example. ++ // ++ // The caller still needs the `SkPictureRecorder` in order to call ++ // `FPDF_FFLRecord()` later. ++ return recorder.release(); ++ } ++ ++ auto pOwnedContext = std::make_unique(); ++ pOwnedContext->m_pDevice = std::move(skDevice); + +- auto pOwnedContext = pdfium::MakeUnique(); +- CPDF_PageRenderContext* pContext = pOwnedContext.get(); + CPDF_Page::RenderContextClearer clearer(pPage); ++ CPDF_PageRenderContext* pContext = pOwnedContext.get(); + pPage->SetRenderContext(std::move(pOwnedContext)); + +- auto skDevice = pdfium::MakeUnique(); +- FPDF_RECORDER recorder = skDevice->CreateRecorder(size_x, size_y); +- pContext->m_pDevice = std::move(skDevice); +- + CPDFSDK_RenderPageWithContext(pContext, pPage, 0, 0, size_x, size_y, 0, 0, ++ /*color_scheme=*/nullptr, + /*need_to_restore=*/true, /*pause=*/nullptr); +- return recorder; ++ ++ return recorder.release(); + } +-#endif // _SKIA_SUPPORT_ ++#endif // defined(_SKIA_SUPPORT_) + + FPDF_EXPORT void FPDF_CALLCONV FPDF_ClosePage(FPDF_PAGE page) { + if (!page) +@@ -678,20 +771,10 @@ FPDF_EXPORT void FPDF_CALLCONV FPDF_ClosePage(FPDF_PAGE page) { + if (pPage->AsXFAPage()) + return; + +- CPDFSDK_PageView* pPageView = +- static_cast(pPage->AsPDFPage()->GetView()); +- if (!pPageView || pPageView->IsBeingDestroyed()) +- return; +- +- if (pPageView->IsLocked()) { +- pPageView->TakePageOwnership(); +- return; +- } +- +- // This will delete the |pPageView| object. We must cleanup the PageView +- // first because it will attempt to reset the View on the |pPage| during +- // destruction. +- pPageView->GetFormFillEnv()->RemovePageView(pPage.Get()); ++ // This will delete the PageView object corresponding to |pPage|. We must ++ // cleanup the PageView before releasing the reference on |pPage| as it will ++ // attempt to reset the PageView during destruction. ++ pPage->AsPDFPage()->ClearView(); + } + + FPDF_EXPORT void FPDF_CALLCONV FPDF_CloseDocument(FPDF_DOCUMENT document) { +@@ -718,9 +801,9 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_DeviceToPage(FPDF_PAGE page, + + IPDF_Page* pPage = IPDFPageFromFPDFPage(page); + const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y); +- Optional pos = ++ absl::optional pos = + pPage->DeviceToPage(rect, rotate, CFX_PointF(device_x, device_y)); +- if (!pos) ++ if (!pos.has_value()) + return false; + + *page_x = pos->x; +@@ -744,8 +827,9 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_PageToDevice(FPDF_PAGE page, + IPDF_Page* pPage = IPDFPageFromFPDFPage(page); + const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y); + CFX_PointF page_point(static_cast(page_x), static_cast(page_y)); +- Optional pos = pPage->PageToDevice(rect, rotate, page_point); +- if (!pos) ++ absl::optional pos = ++ pPage->PageToDevice(rect, rotate, page_point); ++ if (!pos.has_value()) + return false; + + *device_x = FXSYS_roundf(pos->x); +@@ -757,9 +841,10 @@ FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_Create(int width, + int height, + int alpha) { + auto pBitmap = pdfium::MakeRetain(); +- if (!pBitmap->Create(width, height, alpha ? FXDIB_Argb : FXDIB_Rgb32)) ++ if (!pBitmap->Create(width, height, ++ alpha ? FXDIB_Format::kArgb : FXDIB_Format::kRgb32)) { + return nullptr; +- ++ } + return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak()); + } + +@@ -771,16 +856,16 @@ FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_CreateEx(int width, + FXDIB_Format fx_format; + switch (format) { + case FPDFBitmap_Gray: +- fx_format = FXDIB_8bppRgb; ++ fx_format = FXDIB_Format::k8bppRgb; + break; + case FPDFBitmap_BGR: +- fx_format = FXDIB_Rgb; ++ fx_format = FXDIB_Format::kRgb; + break; + case FPDFBitmap_BGRx: +- fx_format = FXDIB_Rgb32; ++ fx_format = FXDIB_Format::kRgb32; + break; + case FPDFBitmap_BGRA: +- fx_format = FXDIB_Argb; ++ fx_format = FXDIB_Format::kArgb; + break; + default: + return nullptr; +@@ -789,7 +874,7 @@ FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_CreateEx(int width, + // Ensure external memory is good at least for the duration of this call. + UnownedPtr pChecker(static_cast(first_scan)); + auto pBitmap = pdfium::MakeRetain(); +- if (!pBitmap->Create(width, height, fx_format, pChecker.Get(), stride)) ++ if (!pBitmap->Create(width, height, fx_format, pChecker, stride)) + return nullptr; + + return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak()); +@@ -801,14 +886,14 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetFormat(FPDF_BITMAP bitmap) { + + FXDIB_Format format = CFXDIBitmapFromFPDFBitmap(bitmap)->GetFormat(); + switch (format) { +- case FXDIB_8bppRgb: +- case FXDIB_8bppMask: ++ case FXDIB_Format::k8bppRgb: ++ case FXDIB_Format::k8bppMask: + return FPDFBitmap_Gray; +- case FXDIB_Rgb: ++ case FXDIB_Format::kRgb: + return FPDFBitmap_BGR; +- case FXDIB_Rgb32: ++ case FXDIB_Format::kRgb32: + return FPDFBitmap_BGRx; +- case FXDIB_Argb: ++ case FXDIB_Format::kArgb: + return FPDFBitmap_BGRA; + default: + return FPDFBitmap_Unknown; +@@ -826,14 +911,16 @@ FPDF_EXPORT void FPDF_CALLCONV FPDFBitmap_FillRect(FPDF_BITMAP bitmap, + + CFX_DefaultRenderDevice device; + RetainPtr pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap)); +- device.Attach(pBitmap, false, nullptr, false); +- if (!pBitmap->HasAlpha()) ++ device.Attach(pBitmap); ++ if (!pBitmap->IsAlphaFormat()) + color |= 0xFF000000; +- device.FillRect(FX_RECT(left, top, left + width, top + height), color); ++ device.FillRect(FX_RECT(left, top, left + width, top + height), ++ static_cast(color)); + } + + FPDF_EXPORT void* FPDF_CALLCONV FPDFBitmap_GetBuffer(FPDF_BITMAP bitmap) { +- return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetBuffer() : nullptr; ++ return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetBuffer().data() ++ : nullptr; + } + + FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetWidth(FPDF_BITMAP bitmap) { +@@ -870,7 +957,7 @@ FPDF_GetPageSizeByIndexF(FPDF_DOCUMENT document, + + auto* pContext = static_cast(pDoc->GetExtension()); + if (pContext) { +- RetainPtr pPage = pContext->GetXFAPage(page_index); ++ RetainPtr pPage = pContext->GetOrCreateXFAPage(page_index); + if (!pPage) + return false; + +@@ -880,12 +967,12 @@ FPDF_GetPageSizeByIndexF(FPDF_DOCUMENT document, + } + #endif // PDF_ENABLE_XFA + +- CPDF_Dictionary* pDict = pDoc->GetPageDictionary(page_index); ++ RetainPtr pDict = pDoc->GetMutablePageDictionary(page_index); + if (!pDict) + return false; + +- auto page = pdfium::MakeRetain(pDoc, pDict); +- page->SetRenderCache(pdfium::MakeUnique(page.Get())); ++ auto page = pdfium::MakeRetain(pDoc, std::move(pDict)); ++ page->AddPageImageCache(); + size->width = page->GetPageWidth(); + size->height = page->GetPageHeight(); + return true; +@@ -931,6 +1018,8 @@ FPDF_VIEWERREF_GetPrintPageRange(FPDF_DOCUMENT document) { + if (!pDoc) + return nullptr; + CPDF_ViewerPreferences viewRef(pDoc); ++ ++ // Unretained reference in public API. NOLINTNEXTLINE + return FPDFPageRangeFromCPDFArray(viewRef.PrintPageRange()); + } + +@@ -975,14 +1064,11 @@ FPDF_VIEWERREF_GetName(FPDF_DOCUMENT document, + return 0; + + CPDF_ViewerPreferences viewRef(pDoc); +- Optional bsVal = viewRef.GenericName(key); +- if (!bsVal) ++ absl::optional bsVal = viewRef.GenericName(key); ++ if (!bsVal.has_value()) + return 0; + +- unsigned long dwStringLen = bsVal->GetLength() + 1; +- if (buffer && length >= dwStringLen) +- memcpy(buffer, bsVal->c_str(), dwStringLen); +- return dwStringLen; ++ return NulTerminateMaybeCopyAndReturnLength(bsVal.value(), buffer, length); + } + + FPDF_EXPORT FPDF_DWORD FPDF_CALLCONV +@@ -995,16 +1081,12 @@ FPDF_CountNamedDests(FPDF_DOCUMENT document) { + if (!pRoot) + return 0; + +- CPDF_NameTree nameTree(pDoc, "Dests"); +- pdfium::base::CheckedNumeric count = nameTree.GetCount(); +- const CPDF_Dictionary* pDest = pRoot->GetDictFor("Dests"); +- if (pDest) +- count += pDest->size(); +- +- if (!count.IsValid()) +- return 0; +- +- return count.ValueOrDie(); ++ auto name_tree = CPDF_NameTree::Create(pDoc, "Dests"); ++ FX_SAFE_UINT32 count = name_tree ? name_tree->GetCount() : 0; ++ RetainPtr pOldStyleDests = pRoot->GetDictFor("Dests"); ++ if (pOldStyleDests) ++ count += pOldStyleDests->size(); ++ return count.ValueOrDefault(0); + } + + FPDF_EXPORT FPDF_DEST FPDF_CALLCONV +@@ -1016,17 +1098,24 @@ FPDF_GetNamedDestByName(FPDF_DOCUMENT document, FPDF_BYTESTRING name) { + if (!pDoc) + return nullptr; + +- CPDF_NameTree name_tree(pDoc, "Dests"); +- ByteStringView name_view(name); +- return FPDFDestFromCPDFArray( +- name_tree.LookupNamedDest(pDoc, PDF_DecodeText(name_view.raw_span()))); ++ ByteString dest_name(name); ++ ++ // TODO(tsepez): murky ownership, should caller get a reference? ++ // Unretained reference in public API. NOLINTNEXTLINE ++ return FPDFDestFromCPDFArray(CPDF_NameTree::LookupNamedDest(pDoc, dest_name)); + } + + #ifdef PDF_ENABLE_V8 + FPDF_EXPORT const char* FPDF_CALLCONV FPDF_GetRecommendedV8Flags() { +- // Reduce exposure since no PDF should contain web assembly. +- // Use interpreted JS only to avoid RWX pages in our address space. +- return "--no-expose-wasm --jitless"; ++ // Use interpreted JS only to avoid RWX pages in our address space. Also, ++ // --jitless implies --no-expose-wasm, which reduce exposure since no PDF ++ // should contain web assembly. ++ return "--jitless"; ++} ++ ++FPDF_EXPORT void* FPDF_CALLCONV FPDF_GetArrayBufferAllocatorSharedInstance() { ++ static pdfium::base::NoDestructor allocator; ++ return allocator.get(); + } + #endif // PDF_ENABLE_V8 + +@@ -1047,7 +1136,7 @@ FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Set(FPDF_BSTR* bstr, + return -1; + + if (length == -1) +- length = strlen(cstr); ++ length = pdfium::base::checked_cast(strlen(cstr)); + + if (length == 0) { + FPDF_BStr_Clear(bstr); +@@ -1096,40 +1185,40 @@ FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDF_GetNamedDest(FPDF_DOCUMENT document, + if (!pRoot) + return nullptr; + +- CPDF_Object* pDestObj = nullptr; ++ auto name_tree = CPDF_NameTree::Create(pDoc, "Dests"); ++ size_t name_tree_count = name_tree ? name_tree->GetCount() : 0; ++ RetainPtr pDestObj; + WideString wsName; +- CPDF_NameTree nameTree(pDoc, "Dests"); +- int count = nameTree.GetCount(); +- if (index >= count) { +- const CPDF_Dictionary* pDest = pRoot->GetDictFor("Dests"); ++ if (static_cast(index) >= name_tree_count) { ++ // If |index| is out of bounds, then try to retrieve the Nth old style named ++ // destination. Where N is 0-indexed, with N = index - name_tree_count. ++ RetainPtr pDest = pRoot->GetDictFor("Dests"); + if (!pDest) + return nullptr; + +- pdfium::base::CheckedNumeric checked_count = count; ++ FX_SAFE_INT32 checked_count = name_tree_count; + checked_count += pDest->size(); + if (!checked_count.IsValid() || index >= checked_count.ValueOrDie()) + return nullptr; + +- index -= count; ++ index -= name_tree_count; + int i = 0; + ByteStringView bsName; + CPDF_DictionaryLocker locker(pDest); + for (const auto& it : locker) { + bsName = it.first.AsStringView(); +- pDestObj = it.second.Get(); +- if (!pDestObj) +- continue; ++ pDestObj = it.second; + if (i == index) + break; + i++; + } + wsName = PDF_DecodeText(bsName.raw_span()); + } else { +- pDestObj = nameTree.LookupValueAndName(index, &wsName); ++ pDestObj = name_tree->LookupValueAndName(index, &wsName); + } + if (!pDestObj) + return nullptr; +- if (CPDF_Dictionary* pDict = pDestObj->AsDictionary()) { ++ if (const CPDF_Dictionary* pDict = pDestObj->AsDictionary()) { + pDestObj = pDict->GetArrayFor("D"); + if (!pDestObj) + return nullptr; +@@ -1138,7 +1227,7 @@ FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDF_GetNamedDest(FPDF_DOCUMENT document, + return nullptr; + + ByteString utf16Name = wsName.ToUTF16LE(); +- int len = utf16Name.GetLength(); ++ int len = pdfium::base::checked_cast(utf16Name.GetLength()); + if (!buffer) { + *buflen = len; + } else if (len <= *buflen) { +@@ -1149,3 +1238,72 @@ FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDF_GetNamedDest(FPDF_DOCUMENT document, + } + return FPDFDestFromCPDFArray(pDestObj->AsArray()); + } ++ ++FPDF_EXPORT int FPDF_CALLCONV FPDF_GetXFAPacketCount(FPDF_DOCUMENT document) { ++ CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document); ++ if (!doc) ++ return -1; ++ ++ return fxcrt::CollectionSize( ++ GetXFAPackets(GetXFAEntryFromDocument(doc))); ++} ++ ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDF_GetXFAPacketName(FPDF_DOCUMENT document, ++ int index, ++ void* buffer, ++ unsigned long buflen) { ++ CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document); ++ if (!doc || index < 0) ++ return 0; ++ ++ std::vector xfa_packets = ++ GetXFAPackets(GetXFAEntryFromDocument(doc)); ++ if (static_cast(index) >= xfa_packets.size()) ++ return 0; ++ ++ return NulTerminateMaybeCopyAndReturnLength(xfa_packets[index].name, buffer, ++ buflen); ++} ++ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDF_GetXFAPacketContent(FPDF_DOCUMENT document, ++ int index, ++ void* buffer, ++ unsigned long buflen, ++ unsigned long* out_buflen) { ++ CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document); ++ if (!doc || index < 0 || !out_buflen) ++ return false; ++ ++ std::vector xfa_packets = ++ GetXFAPackets(GetXFAEntryFromDocument(doc)); ++ if (static_cast(index) >= xfa_packets.size()) ++ return false; ++ ++ *out_buflen = DecodeStreamMaybeCopyAndReturnLength( ++ xfa_packets[index].data, ++ {static_cast(buffer), static_cast(buflen)}); ++ return true; ++} ++ ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDF_GetTrailerEnds(FPDF_DOCUMENT document, ++ unsigned int* buffer, ++ unsigned long length) { ++ auto* doc = CPDFDocumentFromFPDFDocument(document); ++ if (!doc) ++ return 0; ++ ++ // Start recording trailer ends. ++ auto* parser = doc->GetParser(); ++ std::vector trailer_ends = parser->GetTrailerEnds(); ++ const unsigned long trailer_ends_len = ++ fxcrt::CollectionSize(trailer_ends); ++ if (buffer && length >= trailer_ends_len) { ++ for (size_t i = 0; i < trailer_ends_len; ++i) ++ buffer[i] = trailer_ends[i]; ++ } ++ ++ return trailer_ends_len; ++} +diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c +index 94cd3cebd..b66fe6221 100644 +--- a/fpdfsdk/fpdf_view_c_api_test.c ++++ b/fpdfsdk/fpdf_view_c_api_test.c +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -24,6 +24,7 @@ + #include "public/fpdf_progressive.h" + #include "public/fpdf_save.h" + #include "public/fpdf_searchex.h" ++#include "public/fpdf_signature.h" + #include "public/fpdf_structtree.h" + #include "public/fpdf_sysfontinfo.h" + #include "public/fpdf_text.h" +@@ -39,19 +40,32 @@ fnptr g_c_api_test_fnptr = NULL; // Extern, so can't know it doesn't change. + // Function to call from gtest harness to ensure linker resolution. + int CheckPDFiumCApi() { + // fpdf_annot.h ++ CHK(FPDFAnnot_AddInkStroke); + CHK(FPDFAnnot_AppendAttachmentPoints); + CHK(FPDFAnnot_AppendObject); + CHK(FPDFAnnot_CountAttachmentPoints); + CHK(FPDFAnnot_GetAP); + CHK(FPDFAnnot_GetAttachmentPoints); ++ CHK(FPDFAnnot_GetBorder); + CHK(FPDFAnnot_GetColor); + CHK(FPDFAnnot_GetFlags); ++ CHK(FPDFAnnot_GetFocusableSubtypes); ++ CHK(FPDFAnnot_GetFocusableSubtypesCount); + CHK(FPDFAnnot_GetFontSize); ++ CHK(FPDFAnnot_GetFormAdditionalActionJavaScript); ++ CHK(FPDFAnnot_GetFormControlCount); ++ CHK(FPDFAnnot_GetFormControlIndex); ++ CHK(FPDFAnnot_GetFormFieldAlternateName); + CHK(FPDFAnnot_GetFormFieldAtPoint); ++ CHK(FPDFAnnot_GetFormFieldExportValue); + CHK(FPDFAnnot_GetFormFieldFlags); + CHK(FPDFAnnot_GetFormFieldName); + CHK(FPDFAnnot_GetFormFieldType); + CHK(FPDFAnnot_GetFormFieldValue); ++ CHK(FPDFAnnot_GetInkListCount); ++ CHK(FPDFAnnot_GetInkListPath); ++ CHK(FPDFAnnot_GetLine); ++ CHK(FPDFAnnot_GetLink); + CHK(FPDFAnnot_GetLinkedAnnot); + CHK(FPDFAnnot_GetNumberValue); + CHK(FPDFAnnot_GetObject); +@@ -62,18 +76,24 @@ int CheckPDFiumCApi() { + CHK(FPDFAnnot_GetStringValue); + CHK(FPDFAnnot_GetSubtype); + CHK(FPDFAnnot_GetValueType); ++ CHK(FPDFAnnot_GetVertices); + CHK(FPDFAnnot_HasAttachmentPoints); + CHK(FPDFAnnot_HasKey); + CHK(FPDFAnnot_IsChecked); + CHK(FPDFAnnot_IsObjectSupportedSubtype); ++ CHK(FPDFAnnot_IsOptionSelected); + CHK(FPDFAnnot_IsSupportedSubtype); ++ CHK(FPDFAnnot_RemoveInkList); + CHK(FPDFAnnot_RemoveObject); + CHK(FPDFAnnot_SetAP); + CHK(FPDFAnnot_SetAttachmentPoints); ++ CHK(FPDFAnnot_SetBorder); + CHK(FPDFAnnot_SetColor); + CHK(FPDFAnnot_SetFlags); ++ CHK(FPDFAnnot_SetFocusableSubtypes); + CHK(FPDFAnnot_SetRect); + CHK(FPDFAnnot_SetStringValue); ++ CHK(FPDFAnnot_SetURI); + CHK(FPDFAnnot_UpdateObject); + CHK(FPDFPage_CloseAnnot); + CHK(FPDFPage_CreateAnnot); +@@ -115,6 +135,7 @@ int CheckPDFiumCApi() { + CHK(FPDFAction_GetURIPath); + CHK(FPDFBookmark_Find); + CHK(FPDFBookmark_GetAction); ++ CHK(FPDFBookmark_GetCount); + CHK(FPDFBookmark_GetDest); + CHK(FPDFBookmark_GetFirstChild); + CHK(FPDFBookmark_GetNextSibling); +@@ -125,26 +146,41 @@ int CheckPDFiumCApi() { + CHK(FPDFLink_CountQuadPoints); + CHK(FPDFLink_Enumerate); + CHK(FPDFLink_GetAction); ++ CHK(FPDFLink_GetAnnot); + CHK(FPDFLink_GetAnnotRect); + CHK(FPDFLink_GetDest); + CHK(FPDFLink_GetLinkAtPoint); + CHK(FPDFLink_GetLinkZOrderAtPoint); + CHK(FPDFLink_GetQuadPoints); ++ CHK(FPDF_GetFileIdentifier); + CHK(FPDF_GetMetaText); ++ CHK(FPDF_GetPageAAction); + CHK(FPDF_GetPageLabel); + + // fpdf_edit.h + CHK(FPDFFont_Close); ++ CHK(FPDFFont_GetAscent); ++ CHK(FPDFFont_GetDescent); ++ CHK(FPDFFont_GetFlags); ++ CHK(FPDFFont_GetFontData); ++ CHK(FPDFFont_GetFontName); ++ CHK(FPDFFont_GetGlyphPath); ++ CHK(FPDFFont_GetGlyphWidth); ++ CHK(FPDFFont_GetIsEmbedded); ++ CHK(FPDFFont_GetItalicAngle); ++ CHK(FPDFFont_GetWeight); + CHK(FPDFFormObj_CountObjects); +- CHK(FPDFFormObj_GetMatrix); + CHK(FPDFFormObj_GetObject); ++ CHK(FPDFGlyphPath_CountGlyphSegments); ++ CHK(FPDFGlyphPath_GetGlyphPathSegment); + CHK(FPDFImageObj_GetBitmap); + CHK(FPDFImageObj_GetImageDataDecoded); + CHK(FPDFImageObj_GetImageDataRaw); + CHK(FPDFImageObj_GetImageFilter); + CHK(FPDFImageObj_GetImageFilterCount); + CHK(FPDFImageObj_GetImageMetadata); +- CHK(FPDFImageObj_GetMatrix); ++ CHK(FPDFImageObj_GetImagePixelSize); ++ CHK(FPDFImageObj_GetRenderedBitmap); + CHK(FPDFImageObj_LoadJpegFile); + CHK(FPDFImageObj_LoadJpegFileInline); + CHK(FPDFImageObj_SetBitmap); +@@ -167,10 +203,15 @@ int CheckPDFiumCApi() { + CHK(FPDFPageObj_CreateTextObj); + CHK(FPDFPageObj_Destroy); + CHK(FPDFPageObj_GetBounds); ++ CHK(FPDFPageObj_GetDashArray); ++ CHK(FPDFPageObj_GetDashCount); ++ CHK(FPDFPageObj_GetDashPhase); + CHK(FPDFPageObj_GetFillColor); + CHK(FPDFPageObj_GetLineCap); + CHK(FPDFPageObj_GetLineJoin); + CHK(FPDFPageObj_GetMark); ++ CHK(FPDFPageObj_GetMatrix); ++ CHK(FPDFPageObj_GetRotatedBounds); + CHK(FPDFPageObj_GetStrokeColor); + CHK(FPDFPageObj_GetStrokeWidth); + CHK(FPDFPageObj_GetType); +@@ -179,9 +220,12 @@ int CheckPDFiumCApi() { + CHK(FPDFPageObj_NewTextObj); + CHK(FPDFPageObj_RemoveMark); + CHK(FPDFPageObj_SetBlendMode); ++ CHK(FPDFPageObj_SetDashArray); ++ CHK(FPDFPageObj_SetDashPhase); + CHK(FPDFPageObj_SetFillColor); + CHK(FPDFPageObj_SetLineCap); + CHK(FPDFPageObj_SetLineJoin); ++ CHK(FPDFPageObj_SetMatrix); + CHK(FPDFPageObj_SetStrokeColor); + CHK(FPDFPageObj_SetStrokeWidth); + CHK(FPDFPageObj_Transform); +@@ -203,20 +247,19 @@ int CheckPDFiumCApi() { + CHK(FPDFPath_Close); + CHK(FPDFPath_CountSegments); + CHK(FPDFPath_GetDrawMode); +- CHK(FPDFPath_GetMatrix); + CHK(FPDFPath_GetPathSegment); + CHK(FPDFPath_LineTo); + CHK(FPDFPath_MoveTo); + CHK(FPDFPath_SetDrawMode); +- CHK(FPDFPath_SetMatrix); +- CHK(FPDFTextObj_GetFontName); ++ CHK(FPDFTextObj_GetFont); + CHK(FPDFTextObj_GetFontSize); +- CHK(FPDFTextObj_GetMatrix); ++ CHK(FPDFTextObj_GetRenderedBitmap); + CHK(FPDFTextObj_GetText); + CHK(FPDFTextObj_GetTextRenderMode); + CHK(FPDFTextObj_SetTextRenderMode); + CHK(FPDFText_LoadFont); + CHK(FPDFText_LoadStandardFont); ++ CHK(FPDFText_SetCharcodes); + CHK(FPDFText_SetText); + CHK(FPDF_CreateNewDocument); + +@@ -239,6 +282,7 @@ int CheckPDFiumCApi() { + CHK(FORM_DoDocumentOpenAction); + CHK(FORM_DoPageAAction); + CHK(FORM_ForceToKillFocus); ++ CHK(FORM_GetFocusedAnnot); + CHK(FORM_GetFocusedText); + CHK(FORM_GetSelectedText); + CHK(FORM_IsIndexSelected); +@@ -252,10 +296,14 @@ int CheckPDFiumCApi() { + CHK(FORM_OnLButtonDown); + CHK(FORM_OnLButtonUp); + CHK(FORM_OnMouseMove); ++ CHK(FORM_OnMouseWheel); + CHK(FORM_OnRButtonDown); + CHK(FORM_OnRButtonUp); + CHK(FORM_Redo); ++ CHK(FORM_ReplaceAndKeepSelection); + CHK(FORM_ReplaceSelection); ++ CHK(FORM_SelectAllText); ++ CHK(FORM_SetFocusedAnnot); + CHK(FORM_SetIndexSelected); + CHK(FORM_Undo); + CHK(FPDFDOC_ExitFormFillEnvironment); +@@ -263,7 +311,7 @@ int CheckPDFiumCApi() { + CHK(FPDFPage_FormFieldZOrderAtPoint); + CHK(FPDFPage_HasFormFieldAtPoint); + CHK(FPDF_FFLDraw); +-#ifdef _SKIA_SUPPORT_ ++#if defined(_SKIA_SUPPORT_) + CHK(FPDF_FFLRecord); + #endif + CHK(FPDF_GetFormType); +@@ -280,11 +328,16 @@ int CheckPDFiumCApi() { + CHK(FPDFJavaScriptAction_GetScript); + + // fpdf_ppo.h ++ CHK(FPDF_CloseXObject); + CHK(FPDF_CopyViewerPreferences); + CHK(FPDF_ImportNPagesToOne); + CHK(FPDF_ImportPages); ++ CHK(FPDF_ImportPagesByIndex); ++ CHK(FPDF_NewFormObjectFromXObject); ++ CHK(FPDF_NewXObjectFromPage); + + // fpdf_progressive.h ++ CHK(FPDF_RenderPageBitmapWithColorScheme_Start); + CHK(FPDF_RenderPageBitmap_Start); + CHK(FPDF_RenderPage_Close); + CHK(FPDF_RenderPage_Continue); +@@ -297,11 +350,38 @@ int CheckPDFiumCApi() { + CHK(FPDFText_GetCharIndexFromTextIndex); + CHK(FPDFText_GetTextIndexFromCharIndex); + ++ // fpdf_signature.h ++ CHK(FPDFSignatureObj_GetByteRange); ++ CHK(FPDFSignatureObj_GetContents); ++ CHK(FPDFSignatureObj_GetDocMDPPermission); ++ CHK(FPDFSignatureObj_GetReason); ++ CHK(FPDFSignatureObj_GetSubFilter); ++ CHK(FPDFSignatureObj_GetTime); ++ CHK(FPDF_GetSignatureCount); ++ CHK(FPDF_GetSignatureObject); ++ + // fpdf_structtree.h ++ CHK(FPDF_StructElement_Attr_GetBlobValue); ++ CHK(FPDF_StructElement_Attr_GetBooleanValue); ++ CHK(FPDF_StructElement_Attr_GetCount); ++ CHK(FPDF_StructElement_Attr_GetName); ++ CHK(FPDF_StructElement_Attr_GetNumberValue); ++ CHK(FPDF_StructElement_Attr_GetStringValue); ++ CHK(FPDF_StructElement_Attr_GetType); + CHK(FPDF_StructElement_CountChildren); ++ CHK(FPDF_StructElement_GetActualText); + CHK(FPDF_StructElement_GetAltText); ++ CHK(FPDF_StructElement_GetAttributeAtIndex); ++ CHK(FPDF_StructElement_GetAttributeCount); + CHK(FPDF_StructElement_GetChildAtIndex); ++ CHK(FPDF_StructElement_GetID); ++ CHK(FPDF_StructElement_GetLang); + CHK(FPDF_StructElement_GetMarkedContentID); ++ CHK(FPDF_StructElement_GetMarkedContentIdAtIndex); ++ CHK(FPDF_StructElement_GetMarkedContentIdCount); ++ CHK(FPDF_StructElement_GetObjType); ++ CHK(FPDF_StructElement_GetParent); ++ CHK(FPDF_StructElement_GetStringAttribute); + CHK(FPDF_StructElement_GetTitle); + CHK(FPDF_StructElement_GetType); + CHK(FPDF_StructTree_Close); +@@ -349,6 +429,8 @@ int CheckPDFiumCApi() { + CHK(FPDFText_GetText); + CHK(FPDFText_GetTextRenderMode); + CHK(FPDFText_GetUnicode); ++ CHK(FPDFText_HasUnicodeMapError); ++ CHK(FPDFText_IsGenerated); + CHK(FPDFText_LoadPage); + + // fpdf_thumbnail.h +@@ -398,6 +480,9 @@ int CheckPDFiumCApi() { + CHK(FPDF_DestroyLibrary); + CHK(FPDF_DeviceToPage); + CHK(FPDF_DocumentHasValidCrossReferenceTable); ++#ifdef PDF_ENABLE_V8 ++ CHK(FPDF_GetArrayBufferAllocatorSharedInstance); ++#endif + CHK(FPDF_GetDocPermissions); + CHK(FPDF_GetFileVersion); + CHK(FPDF_GetLastError); +@@ -415,11 +500,16 @@ int CheckPDFiumCApi() { + CHK(FPDF_GetRecommendedV8Flags); + #endif + CHK(FPDF_GetSecurityHandlerRevision); ++ CHK(FPDF_GetTrailerEnds); ++ CHK(FPDF_GetXFAPacketContent); ++ CHK(FPDF_GetXFAPacketCount); ++ CHK(FPDF_GetXFAPacketName); + CHK(FPDF_InitLibrary); + CHK(FPDF_InitLibraryWithConfig); + CHK(FPDF_LoadCustomDocument); + CHK(FPDF_LoadDocument); + CHK(FPDF_LoadMemDocument); ++ CHK(FPDF_LoadMemDocument64); + CHK(FPDF_LoadPage); + CHK(FPDF_PageToDevice); + #ifdef _WIN32 +@@ -427,19 +517,13 @@ int CheckPDFiumCApi() { + #endif + CHK(FPDF_RenderPageBitmap); + CHK(FPDF_RenderPageBitmapWithMatrix); +-#ifdef _SKIA_SUPPORT_ ++#if defined(_SKIA_SUPPORT_) + CHK(FPDF_RenderPageSkp); + #endif + #if defined(_WIN32) + CHK(FPDF_SetPrintMode); +-#if defined(PDFIUM_PRINT_TEXT_WITH_GDI) +- CHK(FPDF_SetPrintTextWithGDI); +-#endif + #endif + CHK(FPDF_SetSandBoxPolicy); +-#if defined(_WIN32) && defined(PDFIUM_PRINT_TEXT_WITH_GDI) +- CHK(FPDF_SetTypefaceAccessibleFunc); +-#endif + CHK(FPDF_VIEWERREF_GetDuplex); + CHK(FPDF_VIEWERREF_GetName); + CHK(FPDF_VIEWERREF_GetNumCopies); +diff --git a/fpdfsdk/fpdf_view_c_api_test.h b/fpdfsdk/fpdf_view_c_api_test.h +index c662f4006..87603f053 100644 +--- a/fpdfsdk/fpdf_view_c_api_test.h ++++ b/fpdfsdk/fpdf_view_c_api_test.h +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fpdfsdk/fpdf_view_embeddertest.cpp b/fpdfsdk/fpdf_view_embeddertest.cpp +index fe209706a..469e9bdc7 100644 +--- a/fpdfsdk/fpdf_view_embeddertest.cpp ++++ b/fpdfsdk/fpdf_view_embeddertest.cpp +@@ -1,27 +1,53 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include ++#include ++ + #include + #include + #include ++#include + #include + + #include "build/build_config.h" + #include "core/fpdfapi/parser/cpdf_document.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "fpdfsdk/fpdf_view_c_api_test.h" + #include "public/cpp/fpdf_scopers.h" + #include "public/fpdfview.h" + #include "testing/embedder_test.h" ++#include "testing/embedder_test_constants.h" ++#include "testing/embedder_test_environment.h" ++#include "testing/fx_string_testhelpers.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/utils/file_util.h" ++#include "testing/utils/hash.h" + #include "testing/utils/path_service.h" ++#include "third_party/base/check.h" ++ ++#if defined(_SKIA_SUPPORT_) ++#include "third_party/skia/include/core/SkCanvas.h" // nogncheck ++#include "third_party/skia/include/core/SkColor.h" // nogncheck ++#include "third_party/skia/include/core/SkColorType.h" // nogncheck ++#include "third_party/skia/include/core/SkImage.h" // nogncheck ++#include "third_party/skia/include/core/SkImageInfo.h" // nogncheck ++#include "third_party/skia/include/core/SkPicture.h" // nogncheck ++#include "third_party/skia/include/core/SkPictureRecorder.h" // nogncheck ++#include "third_party/skia/include/core/SkRefCnt.h" // nogncheck ++#include "third_party/skia/include/core/SkSize.h" // nogncheck ++#include "third_party/skia/include/core/SkSurface.h" // nogncheck ++#endif // defined(_SKIA_SUPPORT_) ++ ++using pdfium::ManyRectanglesChecksum; + + namespace { + +-#if defined(OS_WIN) ++constexpr char kFirstAlternate[] = "FirstAlternate"; ++constexpr char kLastAlternate[] = "LastAlternate"; ++ ++#if BUILDFLAG(IS_WIN) + const char kExpectedRectanglePostScript[] = R"( + save + /im/initmatrix load def +@@ -68,7 +94,7 @@ Q + + restore + )"; +-#endif // defined(OS_WIN) ++#endif // BUILDFLAG(IS_WIN) + + class MockDownloadHints final : public FX_DOWNLOADHINTS { + public: +@@ -80,9 +106,51 @@ class MockDownloadHints final : public FX_DOWNLOADHINTS { + FX_DOWNLOADHINTS::AddSegment = SAddSegment; + } + +- ~MockDownloadHints() {} ++ ~MockDownloadHints() = default; + }; + ++#if defined(_SKIA_SUPPORT_) ++ScopedFPDFBitmap SkImageToPdfiumBitmap(const SkImage& image) { ++ ScopedFPDFBitmap bitmap( ++ FPDFBitmap_Create(image.width(), image.height(), /*alpha=*/1)); ++ if (!bitmap) { ++ ADD_FAILURE() << "Could not create FPDF_BITMAP"; ++ return nullptr; ++ } ++ ++ if (!image.readPixels(/*context=*/nullptr, ++ image.imageInfo().makeColorType(kBGRA_8888_SkColorType), ++ FPDFBitmap_GetBuffer(bitmap.get()), ++ FPDFBitmap_GetStride(bitmap.get()), ++ /*srcX=*/0, /*srcY=*/0)) { ++ ADD_FAILURE() << "Could not read pixels from SkImage"; ++ return nullptr; ++ } ++ ++ return bitmap; ++} ++ ++ScopedFPDFBitmap SkPictureToPdfiumBitmap(sk_sp picture, ++ const SkISize& size) { ++ sk_sp surface = ++ SkSurface::MakeRasterN32Premul(size.width(), size.height()); ++ if (!surface) { ++ ADD_FAILURE() << "Could not create SkSurface"; ++ return nullptr; ++ } ++ ++ surface->getCanvas()->clear(SK_ColorWHITE); ++ surface->getCanvas()->drawPicture(picture); ++ sk_sp image = surface->makeImageSnapshot(); ++ if (!image) { ++ ADD_FAILURE() << "Could not snapshot SkSurface"; ++ return nullptr; ++ } ++ ++ return SkImageToPdfiumBitmap(*image); ++} ++#endif // defined(_SKIA_SUPPORT_) ++ + } // namespace + + TEST(fpdf, CApiTest) { +@@ -96,17 +164,17 @@ class FPDFViewEmbedderTest : public EmbedderTest { + int bitmap_height, + const FS_MATRIX& matrix, + const FS_RECTF& rect, +- const char* expected_md5) { ++ const char* expected_checksum) { + ScopedFPDFBitmap bitmap(FPDFBitmap_Create(bitmap_width, bitmap_height, 0)); + FPDFBitmap_FillRect(bitmap.get(), 0, 0, bitmap_width, bitmap_height, + 0xFFFFFFFF); + FPDF_RenderPageBitmapWithMatrix(bitmap.get(), page, &matrix, &rect, 0); +- CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, expected_md5); ++ CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, expected_checksum); + } + + void TestRenderPageBitmapWithFlags(FPDF_PAGE page, + int flags, +- const char* expected_md5) { ++ const char* expected_checksum) { + int bitmap_width = static_cast(FPDF_GetPageWidth(page)); + int bitmap_height = static_cast(FPDF_GetPageHeight(page)); + ScopedFPDFBitmap bitmap(FPDFBitmap_Create(bitmap_width, bitmap_height, 0)); +@@ -114,35 +182,106 @@ class FPDFViewEmbedderTest : public EmbedderTest { + 0xFFFFFFFF); + FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, bitmap_width, bitmap_height, + 0, flags); +- CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, expected_md5); ++ CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, expected_checksum); + } + +- void TestRenderPageBitmapWithExternalMemory(FPDF_PAGE page, ++ void TestRenderPageBitmapWithInternalMemory(FPDF_PAGE page, + int format, +- const char* expected_md5) { ++ const char* expected_checksum) { ++ TestRenderPageBitmapWithInternalMemoryAndStride( ++ page, format, /*bitmap_stride=*/0, expected_checksum); ++ } ++ ++ void TestRenderPageBitmapWithInternalMemoryAndStride( ++ FPDF_PAGE page, ++ int format, ++ int bitmap_stride, ++ const char* expected_checksum) { + int bitmap_width = static_cast(FPDF_GetPageWidth(page)); + int bitmap_height = static_cast(FPDF_GetPageHeight(page)); + int bytes_per_pixel = BytesPerPixelForFormat(format); + ASSERT_NE(0, bytes_per_pixel); + ++ ScopedFPDFBitmap bitmap(FPDFBitmap_CreateEx( ++ bitmap_width, bitmap_height, format, nullptr, bitmap_stride)); ++ RenderPageToBitmapAndCheck(page, bitmap.get(), expected_checksum); ++ } ++ ++ void TestRenderPageBitmapWithExternalMemory(FPDF_PAGE page, ++ int format, ++ const char* expected_checksum) { ++ int bitmap_width = static_cast(FPDF_GetPageWidth(page)); ++ int bytes_per_pixel = BytesPerPixelForFormat(format); ++ ASSERT_NE(0, bytes_per_pixel); ++ + int bitmap_stride = bytes_per_pixel * bitmap_width; ++ return TestRenderPageBitmapWithExternalMemoryImpl( ++ page, format, bitmap_stride, expected_checksum); ++ } ++ ++ void TestRenderPageBitmapWithExternalMemoryAndNoStride( ++ FPDF_PAGE page, ++ int format, ++ const char* expected_checksum) { ++ return TestRenderPageBitmapWithExternalMemoryImpl( ++ page, format, /*bitmap_stride=*/0, expected_checksum); ++ } ++ ++#if defined(_SKIA_SUPPORT_) ++ void TestRenderPageSkp(FPDF_PAGE page, const char* expected_checksum) { ++ int width = static_cast(FPDF_GetPageWidth(page)); ++ int height = static_cast(FPDF_GetPageHeight(page)); ++ ++ FPDF_RECORDER opaque_recorder = FPDF_RenderPageSkp(page, width, height); ++ ASSERT_TRUE(opaque_recorder); ++ ++ SkPictureRecorder* recorder = ++ reinterpret_cast(opaque_recorder); ++ sk_sp picture = recorder->finishRecordingAsPicture(); ++ delete recorder; ++ ASSERT_TRUE(picture); ++ ++ ScopedFPDFBitmap bitmap = SkPictureToPdfiumBitmap( ++ std::move(picture), SkISize::Make(width, height)); ++ CompareBitmap(bitmap.get(), width, height, expected_checksum); ++ } ++#endif // defined(_SKIA_SUPPORT_) ++ ++ private: ++ void TestRenderPageBitmapWithExternalMemoryImpl( ++ FPDF_PAGE page, ++ int format, ++ int bitmap_stride, ++ const char* expected_checksum) { ++ int bitmap_width = static_cast(FPDF_GetPageWidth(page)); ++ int bitmap_height = static_cast(FPDF_GetPageHeight(page)); ++ + std::vector external_memory(bitmap_stride * bitmap_height); + ScopedFPDFBitmap bitmap(FPDFBitmap_CreateEx(bitmap_width, bitmap_height, + format, external_memory.data(), + bitmap_stride)); +- FPDFBitmap_FillRect(bitmap.get(), 0, 0, bitmap_width, bitmap_height, +- 0xFFFFFFFF); +- FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, bitmap_width, bitmap_height, +- 0, 0); +- CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, expected_md5); ++ RenderPageToBitmapAndCheck(page, bitmap.get(), expected_checksum); ++ } ++ ++ void RenderPageToBitmapAndCheck(FPDF_PAGE page, ++ FPDF_BITMAP bitmap, ++ const char* expected_checksum) { ++ int bitmap_width = FPDFBitmap_GetWidth(bitmap); ++ int bitmap_height = FPDFBitmap_GetHeight(bitmap); ++ EXPECT_EQ(bitmap_width, static_cast(FPDF_GetPageWidth(page))); ++ EXPECT_EQ(bitmap_height, static_cast(FPDF_GetPageHeight(page))); ++ FPDFBitmap_FillRect(bitmap, 0, 0, bitmap_width, bitmap_height, 0xFFFFFFFF); ++ FPDF_RenderPageBitmap(bitmap, page, 0, 0, bitmap_width, bitmap_height, 0, ++ 0); ++ CompareBitmap(bitmap, bitmap_width, bitmap_height, expected_checksum); + } + }; + + // Test for conversion of a point in device coordinates to page coordinates + TEST_F(FPDFViewEmbedderTest, DeviceCoordinatesToPageCoordinates) { +- EXPECT_TRUE(OpenDocument("about_blank.pdf")); ++ ASSERT_TRUE(OpenDocument("about_blank.pdf")); + FPDF_PAGE page = LoadPage(0); +- EXPECT_NE(nullptr, page); ++ EXPECT_TRUE(page); + + // Error tolerance for floating point comparison + const double kTolerance = 0.0001; +@@ -223,9 +362,9 @@ TEST_F(FPDFViewEmbedderTest, DeviceCoordinatesToPageCoordinates) { + + // Test for conversion of a point in page coordinates to device coordinates. + TEST_F(FPDFViewEmbedderTest, PageCoordinatesToDeviceCoordinates) { +- EXPECT_TRUE(OpenDocument("about_blank.pdf")); ++ ASSERT_TRUE(OpenDocument("about_blank.pdf")); + FPDF_PAGE page = LoadPage(0); +- EXPECT_NE(nullptr, page); ++ EXPECT_TRUE(page); + + // Display bounds in device coordinates + int start_x = 0; +@@ -303,20 +442,38 @@ TEST_F(FPDFViewEmbedderTest, PageCoordinatesToDeviceCoordinates) { + } + + TEST_F(FPDFViewEmbedderTest, MultipleInitDestroy) { +- FPDF_InitLibrary(); // Redundant given call in SetUp(), but safe. ++ FPDF_InitLibrary(); // Redundant given SetUp() in environment, but safe. + FPDF_InitLibrary(); // Doubly-redundant even, but safe. + +- EXPECT_TRUE(OpenDocument("about_blank.pdf")); ++ ASSERT_TRUE(OpenDocument("about_blank.pdf")); + CloseDocument(); + CloseDocument(); // Redundant given above, but safe. + CloseDocument(); // Doubly-redundant even, but safe. + +- FPDF_DestroyLibrary(); // Doubly-redundant even, but safe. +- FPDF_DestroyLibrary(); // Redundant given call in TearDown(), but safe. ++ FPDF_DestroyLibrary(); // Doubly-Redundant even, but safe. ++ FPDF_DestroyLibrary(); // Redundant given call to TearDown(), but safe. ++ ++ EmbedderTestEnvironment::GetInstance()->TearDown(); ++ EmbedderTestEnvironment::GetInstance()->SetUp(); ++} ++ ++TEST_F(FPDFViewEmbedderTest, RepeatedInitDestroy) { ++ for (int i = 0; i < 3; ++i) { ++ if (!OpenDocument("about_blank.pdf")) ++ ADD_FAILURE(); ++ CloseDocument(); ++ ++ FPDF_DestroyLibrary(); ++ FPDF_InitLibrary(); ++ } ++ ++ // Puts the test environment back the way it was. ++ EmbedderTestEnvironment::GetInstance()->TearDown(); ++ EmbedderTestEnvironment::GetInstance()->SetUp(); + } + + TEST_F(FPDFViewEmbedderTest, Document) { +- EXPECT_TRUE(OpenDocument("about_blank.pdf")); ++ ASSERT_TRUE(OpenDocument("about_blank.pdf")); + EXPECT_EQ(1, GetPageCount()); + EXPECT_EQ(0, GetFirstPageNum()); + +@@ -326,6 +483,37 @@ TEST_F(FPDFViewEmbedderTest, Document) { + + EXPECT_EQ(0xFFFFFFFF, FPDF_GetDocPermissions(document())); + EXPECT_EQ(-1, FPDF_GetSecurityHandlerRevision(document())); ++ CloseDocument(); ++ ++ // Safe to open again and do the same things all over again. ++ ASSERT_TRUE(OpenDocument("about_blank.pdf")); ++ EXPECT_EQ(1, GetPageCount()); ++ EXPECT_EQ(0, GetFirstPageNum()); ++ ++ version = 42; ++ EXPECT_TRUE(FPDF_GetFileVersion(document(), &version)); ++ EXPECT_EQ(14, version); ++ ++ EXPECT_EQ(0xFFFFFFFF, FPDF_GetDocPermissions(document())); ++ EXPECT_EQ(-1, FPDF_GetSecurityHandlerRevision(document())); ++ // CloseDocument() called by TearDown(). ++} ++ ++TEST_F(FPDFViewEmbedderTest, LoadDocument64) { ++ std::string file_path; ++ ASSERT_TRUE(PathService::GetTestFilePath("about_blank.pdf", &file_path)); ++ ++ size_t file_length = 0; ++ std::unique_ptr file_contents = ++ GetFileContents(file_path.c_str(), &file_length); ++ DCHECK(file_contents); ++ ScopedFPDFDocument doc( ++ FPDF_LoadMemDocument64(file_contents.get(), file_length, nullptr)); ++ ASSERT_TRUE(doc); ++ ++ int version; ++ EXPECT_TRUE(FPDF_GetFileVersion(doc.get(), &version)); ++ EXPECT_EQ(14, version); + } + + TEST_F(FPDFViewEmbedderTest, LoadNonexistentDocument) { +@@ -334,9 +522,19 @@ TEST_F(FPDFViewEmbedderTest, LoadNonexistentDocument) { + EXPECT_EQ(static_cast(FPDF_GetLastError()), FPDF_ERR_FILE); + } + ++TEST_F(FPDFViewEmbedderTest, DocumentWithNoPageCount) { ++ ASSERT_TRUE(OpenDocument("no_page_count.pdf")); ++ ASSERT_EQ(6, FPDF_GetPageCount(document())); ++} ++ ++TEST_F(FPDFViewEmbedderTest, DocumentWithEmptyPageTreeNode) { ++ ASSERT_TRUE(OpenDocument("page_tree_empty_node.pdf")); ++ ASSERT_EQ(2, FPDF_GetPageCount(document())); ++} ++ + // See https://crbug.com/pdfium/465 + TEST_F(FPDFViewEmbedderTest, EmptyDocument) { +- EXPECT_TRUE(CreateEmptyDocument()); ++ CreateEmptyDocument(); + { + int version = 42; + EXPECT_FALSE(FPDF_GetFileVersion(document(), &version)); +@@ -366,14 +564,14 @@ TEST_F(FPDFViewEmbedderTest, SandboxDocument) { + uint16_t buf[200]; + unsigned long len; + +- ASSERT_TRUE(CreateEmptyDocument()); ++ CreateEmptyDocument(); + len = FPDF_GetMetaText(document(), "CreationDate", buf, sizeof(buf)); + EXPECT_GT(len, 2u); // Not just "double NUL" end-of-string indicator. + EXPECT_NE(0u, buf[0]); + CloseDocument(); + + FPDF_SetSandBoxPolicy(FPDF_POLICY_MACHINETIME_ACCESS, false); +- ASSERT_TRUE(CreateEmptyDocument()); ++ CreateEmptyDocument(); + len = FPDF_GetMetaText(document(), "CreationDate", buf, sizeof(buf)); + EXPECT_EQ(2u, len); // Only a "double NUL" end-of-string indicator. + EXPECT_EQ(0u, buf[0]); +@@ -381,14 +579,14 @@ TEST_F(FPDFViewEmbedderTest, SandboxDocument) { + + constexpr unsigned long kNoSuchPolicy = 102; + FPDF_SetSandBoxPolicy(kNoSuchPolicy, true); +- ASSERT_TRUE(CreateEmptyDocument()); ++ CreateEmptyDocument(); + len = FPDF_GetMetaText(document(), "CreationDate", buf, sizeof(buf)); + EXPECT_EQ(2u, len); // Only a "double NUL" end-of-string indicator. + EXPECT_EQ(0u, buf[0]); + CloseDocument(); + + FPDF_SetSandBoxPolicy(FPDF_POLICY_MACHINETIME_ACCESS, true); +- ASSERT_TRUE(CreateEmptyDocument()); ++ CreateEmptyDocument(); + len = FPDF_GetMetaText(document(), "CreationDate", buf, sizeof(buf)); + EXPECT_GT(len, 2u); // Not just "double NUL" end-of-string indicator. + EXPECT_NE(0u, buf[0]); +@@ -396,7 +594,7 @@ TEST_F(FPDFViewEmbedderTest, SandboxDocument) { + } + + TEST_F(FPDFViewEmbedderTest, LinearizedDocument) { +- EXPECT_TRUE(OpenDocumentLinearized("feature_linearized_loading.pdf")); ++ ASSERT_TRUE(OpenDocumentLinearized("feature_linearized_loading.pdf")); + int version; + EXPECT_TRUE(FPDF_GetFileVersion(document(), &version)); + EXPECT_EQ(16, version); +@@ -438,7 +636,7 @@ TEST_F(FPDFViewEmbedderTest, LoadCustomDocumentWithShortLivedFileAccess) { + } + + TEST_F(FPDFViewEmbedderTest, Page) { +- EXPECT_TRUE(OpenDocument("about_blank.pdf")); ++ ASSERT_TRUE(OpenDocument("about_blank.pdf")); + FPDF_PAGE page = LoadPage(0); + EXPECT_TRUE(page); + +@@ -463,7 +661,7 @@ TEST_F(FPDFViewEmbedderTest, Page) { + } + + TEST_F(FPDFViewEmbedderTest, ViewerRefDummy) { +- EXPECT_TRUE(OpenDocument("about_blank.pdf")); ++ ASSERT_TRUE(OpenDocument("about_blank.pdf")); + EXPECT_TRUE(FPDF_VIEWERREF_GetPrintScaling(document())); + EXPECT_EQ(1, FPDF_VIEWERREF_GetNumCopies(document())); + EXPECT_EQ(DuplexUndefined, FPDF_VIEWERREF_GetDuplex(document())); +@@ -480,7 +678,7 @@ TEST_F(FPDFViewEmbedderTest, ViewerRefDummy) { + } + + TEST_F(FPDFViewEmbedderTest, ViewerRef) { +- EXPECT_TRUE(OpenDocument("viewer_ref.pdf")); ++ ASSERT_TRUE(OpenDocument("viewer_ref.pdf")); + EXPECT_TRUE(FPDF_VIEWERREF_GetPrintScaling(document())); + EXPECT_EQ(5, FPDF_VIEWERREF_GetNumCopies(document())); + EXPECT_EQ(DuplexUndefined, FPDF_VIEWERREF_GetDuplex(document())); +@@ -528,7 +726,9 @@ TEST_F(FPDFViewEmbedderTest, ViewerRef) { + } + + TEST_F(FPDFViewEmbedderTest, NamedDests) { +- EXPECT_TRUE(OpenDocument("named_dests.pdf")); ++ ASSERT_TRUE(OpenDocument("named_dests.pdf")); ++ EXPECT_EQ(6u, FPDF_CountNamedDests(document())); ++ + long buffer_size; + char fixed_buffer[512]; + FPDF_DEST dest; +@@ -536,39 +736,39 @@ TEST_F(FPDFViewEmbedderTest, NamedDests) { + // Query the size of the first item. + buffer_size = 2000000; // Absurdly large, check not used for this case. + dest = FPDF_GetNamedDest(document(), 0, nullptr, &buffer_size); +- EXPECT_NE(nullptr, dest); ++ EXPECT_TRUE(dest); + EXPECT_EQ(12, buffer_size); + + // Try to retrieve the first item with too small a buffer. + buffer_size = 10; + dest = FPDF_GetNamedDest(document(), 0, fixed_buffer, &buffer_size); +- EXPECT_NE(nullptr, dest); ++ EXPECT_TRUE(dest); + EXPECT_EQ(-1, buffer_size); + + // Try to retrieve the first item with correctly sized buffer. Item is + // taken from Dests NameTree in named_dests.pdf. + buffer_size = 12; + dest = FPDF_GetNamedDest(document(), 0, fixed_buffer, &buffer_size); +- EXPECT_NE(nullptr, dest); ++ EXPECT_TRUE(dest); + EXPECT_EQ(12, buffer_size); +- EXPECT_EQ(std::string("F\0i\0r\0s\0t\0\0\0", 12), +- std::string(fixed_buffer, buffer_size)); ++ EXPECT_EQ("First", ++ GetPlatformString(reinterpret_cast(fixed_buffer))); + + // Try to retrieve the second item with ample buffer. Item is taken + // from Dests NameTree but has a sub-dictionary in named_dests.pdf. + buffer_size = sizeof(fixed_buffer); + dest = FPDF_GetNamedDest(document(), 1, fixed_buffer, &buffer_size); +- EXPECT_NE(nullptr, dest); ++ EXPECT_TRUE(dest); + EXPECT_EQ(10, buffer_size); +- EXPECT_EQ(std::string("N\0e\0x\0t\0\0\0", 10), +- std::string(fixed_buffer, buffer_size)); ++ EXPECT_EQ("Next", ++ GetPlatformString(reinterpret_cast(fixed_buffer))); + + // Try to retrieve third item with ample buffer. Item is taken + // from Dests NameTree but has a bad sub-dictionary in named_dests.pdf. + // in named_dests.pdf). + buffer_size = sizeof(fixed_buffer); + dest = FPDF_GetNamedDest(document(), 2, fixed_buffer, &buffer_size); +- EXPECT_EQ(nullptr, dest); ++ EXPECT_FALSE(dest); + EXPECT_EQ(sizeof(fixed_buffer), + static_cast(buffer_size)); // unmodified. + +@@ -576,7 +776,7 @@ TEST_F(FPDFViewEmbedderTest, NamedDests) { + // from Dests NameTree but has a vale of the wrong type in named_dests.pdf. + buffer_size = sizeof(fixed_buffer); + dest = FPDF_GetNamedDest(document(), 3, fixed_buffer, &buffer_size); +- EXPECT_EQ(nullptr, dest); ++ EXPECT_FALSE(dest); + EXPECT_EQ(sizeof(fixed_buffer), + static_cast(buffer_size)); // unmodified. + +@@ -584,25 +784,25 @@ TEST_F(FPDFViewEmbedderTest, NamedDests) { + // old-style Dests dictionary object in named_dests.pdf. + buffer_size = sizeof(fixed_buffer); + dest = FPDF_GetNamedDest(document(), 4, fixed_buffer, &buffer_size); +- EXPECT_NE(nullptr, dest); ++ EXPECT_TRUE(dest); + EXPECT_EQ(30, buffer_size); +- EXPECT_EQ(std::string("F\0i\0r\0s\0t\0A\0l\0t\0e\0r\0n\0a\0t\0e\0\0\0", 30), +- std::string(fixed_buffer, buffer_size)); ++ EXPECT_EQ(kFirstAlternate, ++ GetPlatformString(reinterpret_cast(fixed_buffer))); + + // Try to retrieve sixth item with ample buffer. Item istaken from the + // old-style Dests dictionary object but has a sub-dictionary in + // named_dests.pdf. + buffer_size = sizeof(fixed_buffer); + dest = FPDF_GetNamedDest(document(), 5, fixed_buffer, &buffer_size); +- EXPECT_NE(nullptr, dest); ++ EXPECT_TRUE(dest); + EXPECT_EQ(28, buffer_size); +- EXPECT_EQ(std::string("L\0a\0s\0t\0A\0l\0t\0e\0r\0n\0a\0t\0e\0\0\0", 28), +- std::string(fixed_buffer, buffer_size)); ++ EXPECT_EQ(kLastAlternate, ++ GetPlatformString(reinterpret_cast(fixed_buffer))); + + // Try to retrieve non-existent item with ample buffer. + buffer_size = sizeof(fixed_buffer); + dest = FPDF_GetNamedDest(document(), 6, fixed_buffer, &buffer_size); +- EXPECT_EQ(nullptr, dest); ++ EXPECT_FALSE(dest); + EXPECT_EQ(sizeof(fixed_buffer), + static_cast(buffer_size)); // unmodified. + +@@ -610,38 +810,38 @@ TEST_F(FPDFViewEmbedderTest, NamedDests) { + buffer_size = sizeof(fixed_buffer); + dest = FPDF_GetNamedDest(document(), std::numeric_limits::max(), + fixed_buffer, &buffer_size); +- EXPECT_EQ(nullptr, dest); ++ EXPECT_FALSE(dest); + EXPECT_EQ(sizeof(fixed_buffer), + static_cast(buffer_size)); // unmodified. + + buffer_size = sizeof(fixed_buffer); + dest = FPDF_GetNamedDest(document(), std::numeric_limits::min(), + fixed_buffer, &buffer_size); +- EXPECT_EQ(nullptr, dest); ++ EXPECT_FALSE(dest); + EXPECT_EQ(sizeof(fixed_buffer), + static_cast(buffer_size)); // unmodified. + + buffer_size = sizeof(fixed_buffer); + dest = FPDF_GetNamedDest(document(), -1, fixed_buffer, &buffer_size); +- EXPECT_EQ(nullptr, dest); ++ EXPECT_FALSE(dest); + EXPECT_EQ(sizeof(fixed_buffer), + static_cast(buffer_size)); // unmodified. + } + + TEST_F(FPDFViewEmbedderTest, NamedDestsByName) { +- EXPECT_TRUE(OpenDocument("named_dests.pdf")); ++ ASSERT_TRUE(OpenDocument("named_dests.pdf")); + + // Null pointer returns nullptr. + FPDF_DEST dest = FPDF_GetNamedDestByName(document(), nullptr); +- EXPECT_EQ(nullptr, dest); ++ EXPECT_FALSE(dest); + + // Empty string returns nullptr. + dest = FPDF_GetNamedDestByName(document(), ""); +- EXPECT_EQ(nullptr, dest); ++ EXPECT_FALSE(dest); + + // Item from Dests NameTree. + dest = FPDF_GetNamedDestByName(document(), "First"); +- EXPECT_NE(nullptr, dest); ++ EXPECT_TRUE(dest); + + long ignore_len = 0; + FPDF_DEST dest_by_index = +@@ -649,8 +849,8 @@ TEST_F(FPDFViewEmbedderTest, NamedDestsByName) { + EXPECT_EQ(dest_by_index, dest); + + // Item from Dests dictionary. +- dest = FPDF_GetNamedDestByName(document(), "FirstAlternate"); +- EXPECT_NE(nullptr, dest); ++ dest = FPDF_GetNamedDestByName(document(), kFirstAlternate); ++ EXPECT_TRUE(dest); + + ignore_len = 0; + dest_by_index = FPDF_GetNamedDest(document(), 4, nullptr, &ignore_len); +@@ -658,16 +858,53 @@ TEST_F(FPDFViewEmbedderTest, NamedDestsByName) { + + // Bad value type for item from Dests NameTree array. + dest = FPDF_GetNamedDestByName(document(), "WrongType"); +- EXPECT_EQ(nullptr, dest); ++ EXPECT_FALSE(dest); + + // No such destination in either Dest NameTree or dictionary. + dest = FPDF_GetNamedDestByName(document(), "Bogus"); +- EXPECT_EQ(nullptr, dest); ++ EXPECT_FALSE(dest); ++} ++ ++TEST_F(FPDFViewEmbedderTest, NamedDestsOldStyle) { ++ ASSERT_TRUE(OpenDocument("named_dests_old_style.pdf")); ++ EXPECT_EQ(2u, FPDF_CountNamedDests(document())); ++ ++ // Test bad parameters. ++ EXPECT_FALSE(FPDF_GetNamedDestByName(document(), nullptr)); ++ EXPECT_FALSE(FPDF_GetNamedDestByName(document(), "")); ++ EXPECT_FALSE(FPDF_GetNamedDestByName(document(), "NoSuchName")); ++ ++ // These should return a valid destination. ++ EXPECT_TRUE(FPDF_GetNamedDestByName(document(), kFirstAlternate)); ++ EXPECT_TRUE(FPDF_GetNamedDestByName(document(), kLastAlternate)); ++ ++ char buffer[512]; ++ constexpr long kBufferSize = sizeof(buffer); ++ long size = kBufferSize; ++ ++ // Test bad indices. ++ EXPECT_FALSE(FPDF_GetNamedDest(document(), -1, buffer, &size)); ++ EXPECT_EQ(kBufferSize, size); ++ size = kBufferSize; ++ EXPECT_FALSE(FPDF_GetNamedDest(document(), 2, buffer, &size)); ++ EXPECT_EQ(kBufferSize, size); ++ ++ // These should return a valid destination. ++ size = kBufferSize; ++ ASSERT_TRUE(FPDF_GetNamedDest(document(), 0, buffer, &size)); ++ ASSERT_EQ(static_cast(sizeof(kFirstAlternate) * 2), size); ++ EXPECT_EQ(kFirstAlternate, ++ GetPlatformString(reinterpret_cast(buffer))); ++ size = kBufferSize; ++ ASSERT_TRUE(FPDF_GetNamedDest(document(), 1, buffer, &size)); ++ ASSERT_EQ(static_cast(sizeof(kLastAlternate) * 2), size); ++ EXPECT_EQ(kLastAlternate, ++ GetPlatformString(reinterpret_cast(buffer))); + } + + // The following tests pass if the document opens without crashing. + TEST_F(FPDFViewEmbedderTest, Crasher_113) { +- EXPECT_TRUE(OpenDocument("bug_113.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_113.pdf")); + } + + TEST_F(FPDFViewEmbedderTest, Crasher_451830) { +@@ -676,9 +913,9 @@ TEST_F(FPDFViewEmbedderTest, Crasher_451830) { + } + + TEST_F(FPDFViewEmbedderTest, Crasher_452455) { +- EXPECT_TRUE(OpenDocument("bug_452455.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_452455.pdf")); + FPDF_PAGE page = LoadPage(0); +- EXPECT_NE(nullptr, page); ++ EXPECT_TRUE(page); + UnloadPage(page); + } + +@@ -688,13 +925,13 @@ TEST_F(FPDFViewEmbedderTest, Crasher_454695) { + } + + TEST_F(FPDFViewEmbedderTest, Crasher_572871) { +- EXPECT_TRUE(OpenDocument("bug_572871.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_572871.pdf")); + } + + // It tests that document can still be loaded even the trailer has no 'Size' + // field if other information is right. + TEST_F(FPDFViewEmbedderTest, Failed_213) { +- EXPECT_TRUE(OpenDocument("bug_213.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_213.pdf")); + } + + // The following tests pass if the document opens without infinite looping. +@@ -703,7 +940,7 @@ TEST_F(FPDFViewEmbedderTest, Hang_298) { + } + + TEST_F(FPDFViewEmbedderTest, Crasher_773229) { +- EXPECT_TRUE(OpenDocument("bug_773229.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_773229.pdf")); + } + + // Test if the document opens without infinite looping. +@@ -711,14 +948,14 @@ TEST_F(FPDFViewEmbedderTest, Crasher_773229) { + // the fix, LoadAllCrossRefV4 will return false after detecting a cross + // reference loop. Cross references will be rebuilt successfully. + TEST_F(FPDFViewEmbedderTest, CrossRefV4Loop) { +- EXPECT_TRUE(OpenDocument("bug_xrefv4_loop.pdf")); ++ ASSERT_TRUE(OpenDocument("bug_xrefv4_loop.pdf")); + MockDownloadHints hints; + + // Make sure calling FPDFAvail_IsDocAvail() on this file does not infinite + // loop either. See bug 875. + int ret = PDF_DATA_NOTAVAIL; + while (ret == PDF_DATA_NOTAVAIL) +- ret = FPDFAvail_IsDocAvail(avail_, &hints); ++ ret = FPDFAvail_IsDocAvail(avail(), &hints); + EXPECT_EQ(PDF_DATA_AVAIL, ret); + } + +@@ -747,47 +984,85 @@ TEST_F(FPDFViewEmbedderTest, Hang_360) { + // Deliberately damaged version of linearized.pdf with bad data in the shared + // object hint table. + TEST_F(FPDFViewEmbedderTest, Hang_1055) { +- EXPECT_TRUE(OpenDocumentLinearized("linearized_bug_1055.pdf")); ++ ASSERT_TRUE(OpenDocumentLinearized("linearized_bug_1055.pdf")); + int version; + EXPECT_TRUE(FPDF_GetFileVersion(document(), &version)); + EXPECT_EQ(16, version); + } + +-// TODO(crbug.com/pdfium/11): Fix this test and enable. +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) +-#define MAYBE_FPDF_RenderPageBitmapWithMatrix \ +- DISABLED_FPDF_RenderPageBitmapWithMatrix +-#else +-#define MAYBE_FPDF_RenderPageBitmapWithMatrix FPDF_RenderPageBitmapWithMatrix +-#endif +-TEST_F(FPDFViewEmbedderTest, MAYBE_FPDF_RenderPageBitmapWithMatrix) { +- const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe"; +- const char kClippedMD5[] = "a84cab93c102b9b9290fba3047ba702c"; +- const char kTopLeftQuarterMD5[] = "f11a11137c8834389e31cf555a4a6979"; +- const char kHoriStretchedMD5[] = "48ef9205941ed19691ccfa00d717187e"; +- const char kRotated90ClockwiseMD5[] = "d8da2c7bf77521550d0f2752b9cf3482"; +- const char kRotated180ClockwiseMD5[] = "0113386bb0bd45125bacc6dee78bfe78"; +- const char kRotated270ClockwiseMD5[] = "a287e0f74ce203699cda89f9cc97a240"; +- const char kMirrorHoriMD5[] = "6e8d7a6fde39d8e720fb9e620102918c"; +- const char kMirrorVertMD5[] = "8f3a555ef9c0d5031831ae3715273707"; +- const char kLargerTopLeftQuarterMD5[] = "172a2f4adafbadbe98017b1c025b9e27"; +- const char kLargerMD5[] = "c806145641c3e6fc4e022c7065343749"; +- const char kLargerClippedMD5[] = "091d3b1c7933c8f6945eb2cb41e588e9"; +- const char kLargerRotatedMD5[] = "115f13353ebfc82ddb392d1f0059eb12"; +- const char kLargerRotatedLandscapeMD5[] = "c901239d17d84ac84cb6f2124da71b0d"; +- const char kLargerRotatedDiagonalMD5[] = "3d62417468bdaff0eb14391a0c30a3b1"; +- const char kTileMD5[] = "0a190003c97220bf8877684c8d7e89cf"; +- +- EXPECT_TRUE(OpenDocument("rectangles.pdf")); ++TEST_F(FPDFViewEmbedderTest, FPDF_RenderPageBitmapWithMatrix) { ++ const char* clipped_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "d2929fae285593cd1c1d446750d47d60"; ++ return "a84cab93c102b9b9290fba3047ba702c"; ++ }(); ++ const char* top_left_quarter_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "31d24d8c6a2bac380b2f5c393e77ecc9"; ++ return "f11a11137c8834389e31cf555a4a6979"; ++ }(); ++ const char* hori_stretched_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "6d3776d7bb21cbb7195126b8e95dfba2"; ++ return "48ef9205941ed19691ccfa00d717187e"; ++ }(); ++ const char* rotated_90_clockwise_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "b4baa001d201baed576cd6d5d0d5a160"; ++ return "d8da2c7bf77521550d0f2752b9cf3482"; ++ }(); ++ const char* rotated_180_clockwise_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "51819227d0863222aed366d5d7c5d9c8"; ++ return "0113386bb0bd45125bacc6dee78bfe78"; ++ }(); ++ const char* rotated_270_clockwise_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "f2b046e46c2751cebc777a9725ae2f3e"; ++ return "a287e0f74ce203699cda89f9cc97a240"; ++ }(); ++ const char* mirror_hori_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "c7fbec322b4fc6bcf46ec1eb89661c41"; ++ return "6e8d7a6fde39d8e720fb9e620102918c"; ++ }(); ++ const char* mirror_vert_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "a8b00bc40677a73c15a08b9769d1b576"; ++ return "8f3a555ef9c0d5031831ae3715273707"; ++ }(); ++ const char* larger_top_left_quarter_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "35deb5ed4b73675ce33f68328a33c687"; ++ return "172a2f4adafbadbe98017b1c025b9e27"; ++ }(); ++ const char* larger_rotated_diagonal_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "85c41bb892c1a09882f432aa2f4a5ef6"; ++ return "3d62417468bdaff0eb14391a0c30a3b1"; ++ }(); ++ const char* tile_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "387be3a84774f39aaa955314d2fe7106"; ++ return "0a190003c97220bf8877684c8d7e89cf"; ++ }(); ++ const char kLargerChecksum[] = "c806145641c3e6fc4e022c7065343749"; ++ const char kLargerClippedChecksum[] = "091d3b1c7933c8f6945eb2cb41e588e9"; ++ const char kLargerRotatedChecksum[] = "115f13353ebfc82ddb392d1f0059eb12"; ++ const char kLargerRotatedLandscapeChecksum[] = ++ "c901239d17d84ac84cb6f2124da71b0d"; ++ ++ ASSERT_TRUE(OpenDocument("rectangles.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); +- const int page_width = static_cast(FPDF_GetPageWidthF(page)); +- const int page_height = static_cast(FPDF_GetPageHeightF(page)); +- EXPECT_EQ(200, page_width); +- EXPECT_EQ(300, page_height); ++ const float page_width = FPDF_GetPageWidthF(page); ++ const float page_height = FPDF_GetPageHeightF(page); ++ EXPECT_FLOAT_EQ(200, page_width); ++ EXPECT_FLOAT_EQ(300, page_height); + ++ using pdfium::RectanglesChecksum; + ScopedFPDFBitmap bitmap = RenderLoadedPage(page); +- CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5); ++ CompareBitmap(bitmap.get(), page_width, page_height, RectanglesChecksum()); + + FS_RECTF page_rect{0, 0, page_width, page_height}; + +@@ -795,89 +1070,92 @@ TEST_F(FPDFViewEmbedderTest, MAYBE_FPDF_RenderPageBitmapWithMatrix) { + // the RenderLoadedPage() output. + FS_MATRIX identity_matrix{1, 0, 0, 1, 0, 0}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, identity_matrix, +- page_rect, kOriginalMD5); ++ page_rect, RectanglesChecksum()); + + // Again render with an identity matrix but with a smaller clipping rect. + FS_RECTF middle_of_page_rect{page_width / 4, page_height / 4, + page_width * 3 / 4, page_height * 3 / 4}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, identity_matrix, +- middle_of_page_rect, kClippedMD5); ++ middle_of_page_rect, clipped_checksum); + + // Now render again with the image scaled smaller. + FS_MATRIX half_scale_matrix{0.5, 0, 0, 0.5, 0, 0}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, + half_scale_matrix, page_rect, +- kTopLeftQuarterMD5); ++ top_left_quarter_checksum); + + // Now render again with the image scaled larger horizontally (the right half + // will be clipped). + FS_MATRIX stretch_x_matrix{2, 0, 0, 1, 0, 0}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, + stretch_x_matrix, page_rect, +- kHoriStretchedMD5); ++ hori_stretched_checksum); + + // Try a 90 degree rotation clockwise but with the same bitmap size, so part + // will be clipped. + FS_MATRIX rotate_90_matrix{0, 1, -1, 0, page_width, 0}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, + rotate_90_matrix, page_rect, +- kRotated90ClockwiseMD5); ++ rotated_90_clockwise_checksum); + + // 180 degree rotation clockwise. + FS_MATRIX rotate_180_matrix{-1, 0, 0, -1, page_width, page_height}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, + rotate_180_matrix, page_rect, +- kRotated180ClockwiseMD5); ++ rotated_180_clockwise_checksum); + + // 270 degree rotation clockwise. + FS_MATRIX rotate_270_matrix{0, -1, 1, 0, 0, page_width}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, + rotate_270_matrix, page_rect, +- kRotated270ClockwiseMD5); ++ rotated_270_clockwise_checksum); + + // Mirror horizontally. + FS_MATRIX mirror_hori_matrix{-1, 0, 0, 1, page_width, 0}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, +- mirror_hori_matrix, page_rect, kMirrorHoriMD5); ++ mirror_hori_matrix, page_rect, ++ mirror_hori_checksum); + + // Mirror vertically. + FS_MATRIX mirror_vert_matrix{1, 0, 0, -1, 0, page_height}; + TestRenderPageBitmapWithMatrix(page, page_width, page_height, +- mirror_vert_matrix, page_rect, kMirrorVertMD5); ++ mirror_vert_matrix, page_rect, ++ mirror_vert_checksum); + + // Tests rendering to a larger bitmap +- const int bitmap_width = page_width * 2; +- const int bitmap_height = page_height * 2; ++ const float bitmap_width = page_width * 2; ++ const float bitmap_height = page_height * 2; + + // Render using an identity matrix and the whole bitmap area as clipping rect. + FS_RECTF bitmap_rect{0, 0, bitmap_width, bitmap_height}; + TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height, + identity_matrix, bitmap_rect, +- kLargerTopLeftQuarterMD5); ++ larger_top_left_quarter_checksum); + + // Render using a scaling matrix to fill the larger bitmap. + FS_MATRIX double_scale_matrix{2, 0, 0, 2, 0, 0}; + TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height, +- double_scale_matrix, bitmap_rect, kLargerMD5); ++ double_scale_matrix, bitmap_rect, ++ kLargerChecksum); + + // Render the larger image again but with clipping. + FS_RECTF middle_of_bitmap_rect{bitmap_width / 4, bitmap_height / 4, + bitmap_width * 3 / 4, bitmap_height * 3 / 4}; + TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height, + double_scale_matrix, middle_of_bitmap_rect, +- kLargerClippedMD5); ++ kLargerClippedChecksum); + + // On the larger bitmap, try a 90 degree rotation but with the same bitmap + // size, so part will be clipped. + FS_MATRIX rotate_90_scale_2_matrix{0, 2, -2, 0, bitmap_width, 0}; + TestRenderPageBitmapWithMatrix(page, bitmap_width, bitmap_height, + rotate_90_scale_2_matrix, bitmap_rect, +- kLargerRotatedMD5); ++ kLargerRotatedChecksum); + + // On the larger bitmap, apply 90 degree rotation to a bitmap with the + // appropriate dimensions. +- const int landscape_bitmap_width = bitmap_height; +- const int landscape_bitmap_height = bitmap_width; ++ const float landscape_bitmap_width = bitmap_height; ++ const float landscape_bitmap_height = bitmap_width; + FS_RECTF landscape_bitmap_rect{0, 0, landscape_bitmap_width, + landscape_bitmap_height}; + FS_MATRIX landscape_rotate_90_scale_2_matrix{ +@@ -885,12 +1163,13 @@ TEST_F(FPDFViewEmbedderTest, MAYBE_FPDF_RenderPageBitmapWithMatrix) { + TestRenderPageBitmapWithMatrix( + page, landscape_bitmap_width, landscape_bitmap_height, + landscape_rotate_90_scale_2_matrix, landscape_bitmap_rect, +- kLargerRotatedLandscapeMD5); ++ kLargerRotatedLandscapeChecksum); + + // On the larger bitmap, apply 45 degree rotation to a bitmap with the + // appropriate dimensions. + const float sqrt2 = 1.41421356f; +- const int diagonal_bitmap_size = ceil((bitmap_width + bitmap_height) / sqrt2); ++ const float diagonal_bitmap_size = ++ ceil((bitmap_width + bitmap_height) / sqrt2); + FS_RECTF diagonal_bitmap_rect{0, 0, diagonal_bitmap_size, + diagonal_bitmap_size}; + FS_MATRIX rotate_45_scale_2_matrix{ +@@ -898,7 +1177,7 @@ TEST_F(FPDFViewEmbedderTest, MAYBE_FPDF_RenderPageBitmapWithMatrix) { + TestRenderPageBitmapWithMatrix(page, diagonal_bitmap_size, + diagonal_bitmap_size, rotate_45_scale_2_matrix, + diagonal_bitmap_rect, +- kLargerRotatedDiagonalMD5); ++ larger_rotated_diagonal_checksum); + + // Render the (2, 1) tile of the page (third column, second row) when the page + // is divided in 50x50 pixel tiles. The tile is scaled by a factor of 7. +@@ -906,7 +1185,7 @@ TEST_F(FPDFViewEmbedderTest, MAYBE_FPDF_RenderPageBitmapWithMatrix) { + const int tile_size = 50; + const int tile_x = 2; + const int tile_y = 1; +- int tile_bitmap_size = scale * tile_size; ++ float tile_bitmap_size = scale * tile_size; + FS_RECTF tile_bitmap_rect{0, 0, tile_bitmap_size, tile_bitmap_size}; + FS_MATRIX tile_2_1_matrix{scale, + 0, +@@ -915,13 +1194,14 @@ TEST_F(FPDFViewEmbedderTest, MAYBE_FPDF_RenderPageBitmapWithMatrix) { + -tile_x * tile_bitmap_size, + -tile_y * tile_bitmap_size}; + TestRenderPageBitmapWithMatrix(page, tile_bitmap_size, tile_bitmap_size, +- tile_2_1_matrix, tile_bitmap_rect, kTileMD5); ++ tile_2_1_matrix, tile_bitmap_rect, ++ tile_checksum); + + UnloadPage(page); + } + + TEST_F(FPDFViewEmbedderTest, FPDF_GetPageSizeByIndexF) { +- EXPECT_TRUE(OpenDocument("rectangles.pdf")); ++ ASSERT_TRUE(OpenDocument("rectangles.pdf")); + + FS_SIZEF size; + EXPECT_FALSE(FPDF_GetPageSizeByIndexF(nullptr, 0, &size)); +@@ -956,7 +1236,7 @@ TEST_F(FPDFViewEmbedderTest, FPDF_GetPageSizeByIndexF) { + } + + TEST_F(FPDFViewEmbedderTest, FPDF_GetPageSizeByIndex) { +- EXPECT_TRUE(OpenDocument("rectangles.pdf")); ++ ASSERT_TRUE(OpenDocument("rectangles.pdf")); + + double width = 0; + double height = 0; +@@ -993,6 +1273,96 @@ TEST_F(FPDFViewEmbedderTest, FPDF_GetPageSizeByIndex) { + UnloadPage(page); + } + ++TEST_F(FPDFViewEmbedderTest, GetXFAArrayData) { ++ ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); ++ ++ static constexpr struct { ++ const char* name; ++ size_t content_length; ++ const char* content_checksum; ++ } kExpectedResults[]{ ++ {"preamble", 124u, "71be364e53292596412242bfcdb46eab"}, ++ {"config", 642u, "bcd1ca1d420ee31a561273a54a06435f"}, ++ {"template", 541u, "0f48cb2fa1bb9cbf9eee802d66e81bf4"}, ++ {"localeSet", 3455u, "bb1f253d3e5c719ac0da87d055bc164e"}, ++ {"postamble", 11u, "6b79e25da35d86634ea27c38f64cf243"}, ++ }; ++ ++ ASSERT_EQ(static_cast(std::size(kExpectedResults)), ++ FPDF_GetXFAPacketCount(document())); ++ for (size_t i = 0; i < std::size(kExpectedResults); ++i) { ++ char name_buffer[20] = {}; ++ ASSERT_EQ(strlen(kExpectedResults[i].name) + 1, ++ FPDF_GetXFAPacketName(document(), i, nullptr, 0)); ++ EXPECT_EQ( ++ strlen(kExpectedResults[i].name) + 1, ++ FPDF_GetXFAPacketName(document(), i, name_buffer, sizeof(name_buffer))); ++ EXPECT_STREQ(kExpectedResults[i].name, name_buffer); ++ ++ unsigned long buflen; ++ ASSERT_TRUE(FPDF_GetXFAPacketContent(document(), i, nullptr, 0, &buflen)); ++ ASSERT_EQ(kExpectedResults[i].content_length, buflen); ++ std::vector data_buffer(buflen); ++ EXPECT_TRUE(FPDF_GetXFAPacketContent(document(), i, data_buffer.data(), ++ data_buffer.size(), &buflen)); ++ EXPECT_EQ(kExpectedResults[i].content_length, buflen); ++ EXPECT_STREQ(kExpectedResults[i].content_checksum, ++ GenerateMD5Base16(data_buffer).c_str()); ++ } ++ ++ // Test bad parameters. ++ EXPECT_EQ(-1, FPDF_GetXFAPacketCount(nullptr)); ++ ++ EXPECT_EQ(0u, FPDF_GetXFAPacketName(nullptr, 0, nullptr, 0)); ++ EXPECT_EQ(0u, FPDF_GetXFAPacketName(document(), -1, nullptr, 0)); ++ EXPECT_EQ(0u, FPDF_GetXFAPacketName(document(), std::size(kExpectedResults), ++ nullptr, 0)); ++ ++ unsigned long buflen = 123; ++ EXPECT_FALSE(FPDF_GetXFAPacketContent(nullptr, 0, nullptr, 0, &buflen)); ++ EXPECT_EQ(123u, buflen); ++ EXPECT_FALSE(FPDF_GetXFAPacketContent(document(), -1, nullptr, 0, &buflen)); ++ EXPECT_EQ(123u, buflen); ++ EXPECT_FALSE(FPDF_GetXFAPacketContent(document(), std::size(kExpectedResults), ++ nullptr, 0, &buflen)); ++ EXPECT_EQ(123u, buflen); ++ EXPECT_FALSE(FPDF_GetXFAPacketContent(document(), 0, nullptr, 0, nullptr)); ++} ++ ++TEST_F(FPDFViewEmbedderTest, GetXFAStreamData) { ++ ASSERT_TRUE(OpenDocument("bug_1265.pdf")); ++ ++ ASSERT_EQ(1, FPDF_GetXFAPacketCount(document())); ++ ++ char name_buffer[20] = {}; ++ ASSERT_EQ(1u, FPDF_GetXFAPacketName(document(), 0, nullptr, 0)); ++ EXPECT_EQ(1u, FPDF_GetXFAPacketName(document(), 0, name_buffer, ++ sizeof(name_buffer))); ++ EXPECT_STREQ("", name_buffer); ++ ++ unsigned long buflen; ++ ASSERT_TRUE(FPDF_GetXFAPacketContent(document(), 0, nullptr, 0, &buflen)); ++ ASSERT_EQ(121u, buflen); ++ std::vector data_buffer(buflen); ++ EXPECT_TRUE(FPDF_GetXFAPacketContent(document(), 0, data_buffer.data(), ++ data_buffer.size(), &buflen)); ++ EXPECT_EQ(121u, buflen); ++ EXPECT_STREQ("8f912eaa1e66c9341cb3032ede71e147", ++ GenerateMD5Base16(data_buffer).c_str()); ++} ++ ++TEST_F(FPDFViewEmbedderTest, GetXFADataForNoForm) { ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ ++ EXPECT_EQ(0, FPDF_GetXFAPacketCount(document())); ++} ++ ++TEST_F(FPDFViewEmbedderTest, GetXFADataForAcroForm) { ++ ASSERT_TRUE(OpenDocument("text_form.pdf")); ++ ++ EXPECT_EQ(0, FPDF_GetXFAPacketCount(document())); ++} ++ + class RecordUnsupportedErrorDelegate final : public EmbedderTest::Delegate { + public: + RecordUnsupportedErrorDelegate() = default; +@@ -1061,7 +1431,7 @@ TEST_F(FPDFViewEmbedderTest, LoadDocumentWithEmptyXRefConsistently) { + size_t file_length = 0; + std::unique_ptr file_contents = + GetFileContents(file_path.c_str(), &file_length); +- ASSERT(file_contents); ++ DCHECK(file_contents); + ScopedFPDFDocument doc( + FPDF_LoadMemDocument(file_contents.get(), file_length, "")); + ASSERT_TRUE(doc); +@@ -1069,77 +1439,245 @@ TEST_F(FPDFViewEmbedderTest, LoadDocumentWithEmptyXRefConsistently) { + } + } + ++TEST_F(FPDFViewEmbedderTest, RenderBug664284WithNoNativeText) { ++ // For Skia, since the font used in bug_664284.pdf is not a CID font, ++ // ShouldDrawDeviceText() will always return true. Therefore ++ // FPDF_NO_NATIVETEXT and the font widths defined in the PDF determines ++ // whether to go through the rendering path in ++ // CFX_SkiaDeviceDriver::DrawDeviceText(). In this case, it returns false and ++ // affects the rendering results across all platforms. ++ ++ // For AGG, since CFX_AggDeviceDriver::DrawDeviceText() always returns false, ++ // FPDF_NO_NATIVETEXT won't affect device-specific rendering path and it will ++ // only disable native text support on macOS. Therefore Windows and Linux ++ // rendering results remain the same as rendering with no flags, while the ++ // macOS rendering result doesn't. ++ ++ const char* original_checksum = []() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "0e339d606aafb63077f49e238dc27cb0"; ++#endif ++ return "288502887ffc63291f35a0573b944375"; ++ }(); ++ static const char kNoNativeTextChecksum[] = ++ "288502887ffc63291f35a0573b944375"; ++ ASSERT_TRUE(OpenDocument("bug_664284.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ TestRenderPageBitmapWithFlags(page, 0, original_checksum); ++ TestRenderPageBitmapWithFlags(page, FPDF_NO_NATIVETEXT, ++ kNoNativeTextChecksum); ++ ++ UnloadPage(page); ++} ++ ++// TODO(crbug.com/pdfium/1955): Remove this test once pixel tests can pass with ++// `reverse-byte-order` option. ++TEST_F(FPDFViewEmbedderTest, RenderBlueAndRedImagesWithReverByteOrderFlag) { ++ // When rendering with `FPDF_REVERSE_BYTE_ORDER` flag, the blue and red ++ // channels should be reversed. ++ ASSERT_TRUE(OpenDocument("bug_1396264.pdf")); ++ ScopedFPDFPage page(FPDF_LoadPage(document(), 0)); ++ ASSERT_TRUE(page); ++ ++ TestRenderPageBitmapWithFlags(page.get(), 0, ++ "81e7f4498090977c848a21b5c6510d3a"); ++ TestRenderPageBitmapWithFlags(page.get(), FPDF_REVERSE_BYTE_ORDER, ++ "505ba6d1c7f4044c11c91873452a8bde"); ++} ++ ++TEST_F(FPDFViewEmbedderTest, RenderJpxLzwImageWithFlags) { ++ static const char kNormalChecksum[] = "4bcd56cae1ca2622403e8af07242e71a"; ++ static const char kGrayscaleChecksum[] = "fe45ad56efe868ba82285fa5ffedc0cb"; ++ ++ ASSERT_TRUE(OpenDocument("jpx_lzw.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ TestRenderPageBitmapWithFlags(page, 0, kNormalChecksum); ++ TestRenderPageBitmapWithFlags(page, FPDF_ANNOT, kNormalChecksum); ++ TestRenderPageBitmapWithFlags(page, FPDF_LCD_TEXT, kNormalChecksum); ++ TestRenderPageBitmapWithFlags(page, FPDF_NO_NATIVETEXT, kNormalChecksum); ++ TestRenderPageBitmapWithFlags(page, FPDF_GRAYSCALE, kGrayscaleChecksum); ++ TestRenderPageBitmapWithFlags(page, FPDF_RENDER_LIMITEDIMAGECACHE, ++ kNormalChecksum); ++ TestRenderPageBitmapWithFlags(page, FPDF_RENDER_FORCEHALFTONE, ++ kNormalChecksum); ++ TestRenderPageBitmapWithFlags(page, FPDF_PRINTING, kNormalChecksum); ++ TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHTEXT, ++ kNormalChecksum); ++ TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHIMAGE, ++ kNormalChecksum); ++ TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHPATH, ++ kNormalChecksum); ++ ++ UnloadPage(page); ++} ++ + TEST_F(FPDFViewEmbedderTest, RenderManyRectanglesWithFlags) { +- static const char kNormalMD5[] = "b0170c575b65ecb93ebafada0ff0f038"; +- static const char kGrayscaleMD5[] = "7b553f1052069a9c61237a05db0955d6"; +- static const char kNoSmoothpathMD5[] = "ff6e5c509d1f6984bcdfd18b26a4203a"; ++ const char* grayscale_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "b596ac8bbe64e7bff31888ab05e4dcf4"; ++ return "7b553f1052069a9c61237a05db0955d6"; ++ }(); ++ const char* no_smoothpath_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "4d71ed53d9f6e6a761876ebb4ff23e19"; ++ return "ff6e5c509d1f6984bcdfd18b26a4203a"; ++ }(); + + ASSERT_TRUE(OpenDocument("many_rectangles.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +- TestRenderPageBitmapWithFlags(page, 0, kNormalMD5); +- TestRenderPageBitmapWithFlags(page, FPDF_ANNOT, kNormalMD5); +- TestRenderPageBitmapWithFlags(page, FPDF_LCD_TEXT, kNormalMD5); +- TestRenderPageBitmapWithFlags(page, FPDF_NO_NATIVETEXT, kNormalMD5); +- TestRenderPageBitmapWithFlags(page, FPDF_GRAYSCALE, kGrayscaleMD5); ++ TestRenderPageBitmapWithFlags(page, 0, ManyRectanglesChecksum()); ++ TestRenderPageBitmapWithFlags(page, FPDF_ANNOT, ManyRectanglesChecksum()); ++ TestRenderPageBitmapWithFlags(page, FPDF_LCD_TEXT, ManyRectanglesChecksum()); ++ TestRenderPageBitmapWithFlags(page, FPDF_NO_NATIVETEXT, ++ ManyRectanglesChecksum()); ++ TestRenderPageBitmapWithFlags(page, FPDF_GRAYSCALE, grayscale_checksum); + TestRenderPageBitmapWithFlags(page, FPDF_RENDER_LIMITEDIMAGECACHE, +- kNormalMD5); +- TestRenderPageBitmapWithFlags(page, FPDF_RENDER_FORCEHALFTONE, kNormalMD5); +- TestRenderPageBitmapWithFlags(page, FPDF_PRINTING, kNormalMD5); +- TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHTEXT, kNormalMD5); +- TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHIMAGE, kNormalMD5); ++ ManyRectanglesChecksum()); ++ TestRenderPageBitmapWithFlags(page, FPDF_RENDER_FORCEHALFTONE, ++ ManyRectanglesChecksum()); ++ TestRenderPageBitmapWithFlags(page, FPDF_PRINTING, ManyRectanglesChecksum()); ++ TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHTEXT, ++ ManyRectanglesChecksum()); ++ TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHIMAGE, ++ ManyRectanglesChecksum()); + TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHPATH, +- kNoSmoothpathMD5); ++ no_smoothpath_checksum); + + UnloadPage(page); + } + +-TEST_F(FPDFViewEmbedderTest, RenderManyRectanglesWithExternalMemory) { +- static const char kNormalMD5[] = "b0170c575b65ecb93ebafada0ff0f038"; +- static const char kGrayMD5[] = "b561c11edc44dc3972125a9b8744fa2f"; +- static const char kBgrMD5[] = "ab6312e04c0d3f4e46fb302a45173d05"; +- ++TEST_F(FPDFViewEmbedderTest, RenderManyRectanglesWithAndWithoutExternalMemory) { + ASSERT_TRUE(OpenDocument("many_rectangles.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +- TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_Gray, kGrayMD5); +- TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGR, kBgrMD5); +- TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGRx, kNormalMD5); +- TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGRA, kNormalMD5); ++ const char* bgr_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "4d52e5cc1d4a8067bf918b85b232fff0"; ++ return "ab6312e04c0d3f4e46fb302a45173d05"; ++ }(); ++ static constexpr int kBgrStride = 600; // Width of 200 * 24 bits per pixel. ++ TestRenderPageBitmapWithInternalMemory(page, FPDFBitmap_BGR, bgr_checksum); ++ TestRenderPageBitmapWithInternalMemoryAndStride(page, FPDFBitmap_BGR, ++ kBgrStride, bgr_checksum); ++ TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGR, bgr_checksum); ++ TestRenderPageBitmapWithExternalMemoryAndNoStride(page, FPDFBitmap_BGR, ++ bgr_checksum); ++ ++ const char* gray_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "3dfe1fc3889123d68e1748fefac65e72"; ++ return "b561c11edc44dc3972125a9b8744fa2f"; ++ }(); ++ ++ TestRenderPageBitmapWithInternalMemory(page, FPDFBitmap_Gray, gray_checksum); ++ static constexpr int kGrayStride = 200; // Width of 200 * 8 bits per pixel. ++ TestRenderPageBitmapWithInternalMemoryAndStride(page, FPDFBitmap_Gray, ++ kGrayStride, gray_checksum); ++ TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_Gray, gray_checksum); ++ TestRenderPageBitmapWithExternalMemoryAndNoStride(page, FPDFBitmap_Gray, ++ gray_checksum); ++ ++ static constexpr int kBgrxStride = 800; // Width of 200 * 32 bits per pixel. ++ TestRenderPageBitmapWithInternalMemory(page, FPDFBitmap_BGRx, ++ ManyRectanglesChecksum()); ++ TestRenderPageBitmapWithInternalMemoryAndStride( ++ page, FPDFBitmap_BGRx, kBgrxStride, ManyRectanglesChecksum()); ++ TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGRx, ++ ManyRectanglesChecksum()); ++ TestRenderPageBitmapWithExternalMemoryAndNoStride(page, FPDFBitmap_BGRx, ++ ManyRectanglesChecksum()); ++ ++ TestRenderPageBitmapWithInternalMemory(page, FPDFBitmap_BGRA, ++ ManyRectanglesChecksum()); ++ TestRenderPageBitmapWithInternalMemoryAndStride( ++ page, FPDFBitmap_BGRA, kBgrxStride, ManyRectanglesChecksum()); ++ TestRenderPageBitmapWithExternalMemory(page, FPDFBitmap_BGRA, ++ ManyRectanglesChecksum()); ++ TestRenderPageBitmapWithExternalMemoryAndNoStride(page, FPDFBitmap_BGRA, ++ ManyRectanglesChecksum()); ++ + UnloadPage(page); + } + +-#if defined(OS_LINUX) + TEST_F(FPDFViewEmbedderTest, RenderHelloWorldWithFlags) { +- static const char kNormalMD5[] = "2baa4c0e1758deba1b9c908e1fbd04ed"; +- static const char kLcdTextMD5[] = "825e881f39e48254e64e2808987a6b8c"; +- static const char kNoSmoothtextMD5[] = "3d01e234120b783a3fffb27273ea1ea8"; +- + ASSERT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +- TestRenderPageBitmapWithFlags(page, 0, kNormalMD5); +- TestRenderPageBitmapWithFlags(page, FPDF_ANNOT, kNormalMD5); +- TestRenderPageBitmapWithFlags(page, FPDF_LCD_TEXT, kLcdTextMD5); +- TestRenderPageBitmapWithFlags(page, FPDF_NO_NATIVETEXT, kNormalMD5); +- TestRenderPageBitmapWithFlags(page, FPDF_GRAYSCALE, kNormalMD5); ++ using pdfium::HelloWorldChecksum; ++ TestRenderPageBitmapWithFlags(page, 0, HelloWorldChecksum()); ++ TestRenderPageBitmapWithFlags(page, FPDF_ANNOT, HelloWorldChecksum()); ++ TestRenderPageBitmapWithFlags(page, FPDF_GRAYSCALE, HelloWorldChecksum()); + TestRenderPageBitmapWithFlags(page, FPDF_RENDER_LIMITEDIMAGECACHE, +- kNormalMD5); +- TestRenderPageBitmapWithFlags(page, FPDF_RENDER_FORCEHALFTONE, kNormalMD5); +- TestRenderPageBitmapWithFlags(page, FPDF_PRINTING, kNormalMD5); ++ HelloWorldChecksum()); ++ TestRenderPageBitmapWithFlags(page, FPDF_RENDER_FORCEHALFTONE, ++ HelloWorldChecksum()); ++ TestRenderPageBitmapWithFlags(page, FPDF_PRINTING, HelloWorldChecksum()); ++ TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHIMAGE, ++ HelloWorldChecksum()); ++ TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHPATH, ++ HelloWorldChecksum()); ++ ++ const char* lcd_text_checksum = []() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "c1c548442e0e0f949c5550d89bf8ae3b"; ++#if BUILDFLAG(IS_APPLE) ++ return "6eef7237f7591f07616e238422086737"; ++#else ++ return "09152e25e51fa8ca31fc28d0937bf477"; ++#endif // BUILDFLAG(IS_APPLE) ++ }(); ++ const char* no_smoothtext_checksum = []() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "6eef7237f7591f07616e238422086737"; ++#endif ++ return "37d0b34e1762fdda4c05ce7ea357b828"; ++ }(); ++ ++ TestRenderPageBitmapWithFlags(page, FPDF_LCD_TEXT, lcd_text_checksum); + TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHTEXT, +- kNoSmoothtextMD5); +- TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHIMAGE, kNormalMD5); +- TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHPATH, kNormalMD5); ++ no_smoothtext_checksum); ++ ++ // For text rendering, When anti-aliasing is disabled, LCD Optimization flag ++ // will be ignored. ++ TestRenderPageBitmapWithFlags(page, FPDF_LCD_TEXT | FPDF_RENDER_NO_SMOOTHTEXT, ++ no_smoothtext_checksum); ++ ++ UnloadPage(page); ++} ++ ++// Deliberately disabled because this test case renders a large bitmap, which is ++// very slow for debug builds. ++#if defined(NDEBUG) ++#define MAYBE_LargeImageDoesNotRenderBlank LargeImageDoesNotRenderBlank ++#else ++#define MAYBE_LargeImageDoesNotRenderBlank DISABLED_LargeImageDoesNotRenderBlank ++#endif ++TEST_F(FPDFViewEmbedderTest, MAYBE_LargeImageDoesNotRenderBlank) { ++ static const char kChecksum[] = "a6056db6961f4e65c42ab2e246171fe1"; ++ ++ ASSERT_TRUE(OpenDocument("bug_1646.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ constexpr int kWidth = 40000; ++ constexpr int kHeight = 100; ++ TestRenderPageBitmapWithMatrix(page, kWidth, kHeight, {1000, 0, 0, 1, 0, 0}, ++ {0, 0, kWidth, kHeight}, kChecksum); + + UnloadPage(page); + } +-#endif // defined(OS_LINUX) + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + TEST_F(FPDFViewEmbedderTest, FPDFRenderPageEmf) { + ASSERT_TRUE(OpenDocument("rectangles.pdf")); + FPDF_PAGE page = LoadPage(0); +@@ -1343,4 +1881,164 @@ restore + + UnloadPage(page); + } +-#endif // defined(OS_WIN) ++ ++TEST_F(FPDFViewEmbedderTest, ImageMask) { ++ // TODO(crbug.com/pdfium/1500): Fix this test and enable. ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return; ++ ++ ASSERT_TRUE(OpenDocument("bug_674771.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ // Render the page with more efficient processing of image masks. ++ FPDF_SetPrintMode(FPDF_PRINTMODE_EMF_IMAGE_MASKS); ++ std::vector emf_image_masks = RenderPageWithFlagsToEmf(page, 0); ++ ++ // Render the page normally. ++ FPDF_SetPrintMode(FPDF_PRINTMODE_EMF); ++ std::vector emf_normal = RenderPageWithFlagsToEmf(page, 0); ++ ++ EXPECT_LT(emf_image_masks.size(), emf_normal.size()); ++ ++ UnloadPage(page); ++} ++#endif // BUILDFLAG(IS_WIN) ++ ++TEST_F(FPDFViewEmbedderTest, GetTrailerEnds) { ++ ASSERT_TRUE(OpenDocument("two_signatures.pdf")); ++ ++ // FPDF_GetTrailerEnds() positive testing. ++ unsigned long size = FPDF_GetTrailerEnds(document(), nullptr, 0); ++ const std::vector kExpectedEnds{633, 1703, 2781}; ++ ASSERT_EQ(kExpectedEnds.size(), size); ++ std::vector ends(size); ++ ASSERT_EQ(size, FPDF_GetTrailerEnds(document(), ends.data(), size)); ++ ASSERT_EQ(kExpectedEnds, ends); ++ ++ // FPDF_GetTrailerEnds() negative testing. ++ ASSERT_EQ(0U, FPDF_GetTrailerEnds(nullptr, nullptr, 0)); ++ ++ ends.resize(2); ++ ends[0] = 0; ++ ends[1] = 1; ++ size = FPDF_GetTrailerEnds(document(), ends.data(), ends.size()); ++ ASSERT_EQ(kExpectedEnds.size(), size); ++ EXPECT_EQ(0U, ends[0]); ++ EXPECT_EQ(1U, ends[1]); ++} ++ ++TEST_F(FPDFViewEmbedderTest, GetTrailerEndsHelloWorld) { ++ // Single trailer, \n line ending at the trailer end. ++ ASSERT_TRUE(OpenDocument("hello_world.pdf")); ++ ++ // FPDF_GetTrailerEnds() positive testing. ++ unsigned long size = FPDF_GetTrailerEnds(document(), nullptr, 0); ++ const std::vector kExpectedEnds{840}; ++ ASSERT_EQ(kExpectedEnds.size(), size); ++ std::vector ends(size); ++ ASSERT_EQ(size, FPDF_GetTrailerEnds(document(), ends.data(), size)); ++ ASSERT_EQ(kExpectedEnds, ends); ++} ++ ++TEST_F(FPDFViewEmbedderTest, GetTrailerEndsAnnotationStamp) { ++ // Multiple trailers, \r\n line ending at the trailer ends. ++ ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf")); ++ ++ // FPDF_GetTrailerEnds() positive testing. ++ unsigned long size = FPDF_GetTrailerEnds(document(), nullptr, 0); ++ const std::vector kExpectedEnds{441, 7945, 101719}; ++ ASSERT_EQ(kExpectedEnds.size(), size); ++ std::vector ends(size); ++ ASSERT_EQ(size, FPDF_GetTrailerEnds(document(), ends.data(), size)); ++ ASSERT_EQ(kExpectedEnds, ends); ++} ++ ++TEST_F(FPDFViewEmbedderTest, GetTrailerEndsLinearized) { ++ // Set up linearized PDF. ++ FileAccessForTesting file_acc("linearized.pdf"); ++ FakeFileAccess fake_acc(&file_acc); ++ CreateAvail(fake_acc.GetFileAvail(), fake_acc.GetFileAccess()); ++ fake_acc.SetWholeFileAvailable(); ++ ++ // Multiple trailers, \r line ending at the trailer ends (no \n). ++ SetDocumentFromAvail(); ++ ASSERT_TRUE(document()); ++ ++ // FPDF_GetTrailerEnds() positive testing. ++ unsigned long size = FPDF_GetTrailerEnds(document(), nullptr, 0); ++ const std::vector kExpectedEnds{474, 11384}; ++ ASSERT_EQ(kExpectedEnds.size(), size); ++ std::vector ends(size); ++ ASSERT_EQ(size, FPDF_GetTrailerEnds(document(), ends.data(), size)); ++ ASSERT_EQ(kExpectedEnds, ends); ++} ++ ++TEST_F(FPDFViewEmbedderTest, GetTrailerEndsWhitespace) { ++ // Whitespace between 'endstream'/'endobj' and the newline. ++ ASSERT_TRUE(OpenDocument("trailer_end_trailing_space.pdf")); ++ ++ unsigned long size = FPDF_GetTrailerEnds(document(), nullptr, 0); ++ const std::vector kExpectedEnds{1193}; ++ // Without the accompanying fix in place, this test would have failed, as the ++ // size was 0, not 1, i.e. no trailer ends were found. ++ ASSERT_EQ(kExpectedEnds.size(), size); ++ std::vector ends(size); ++ ASSERT_EQ(size, FPDF_GetTrailerEnds(document(), ends.data(), size)); ++ EXPECT_EQ(kExpectedEnds, ends); ++} ++ ++TEST_F(FPDFViewEmbedderTest, RenderXfaPage) { ++ ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); ++ ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ // Should always be blank, as we're not testing `FPDF_FFLDraw()` here. ++ TestRenderPageBitmapWithFlags(page, 0, pdfium::kBlankPage612By792Checksum); ++ ++ UnloadPage(page); ++} ++ ++#if defined(_SKIA_SUPPORT_) ++TEST_F(FPDFViewEmbedderTest, RenderPageToSkp) { ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ GTEST_SKIP() << "FPDF_RenderPageSkp() only makes sense with Skia"; ++ } ++ ++ ASSERT_TRUE(OpenDocument("rectangles.pdf")); ++ ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ TestRenderPageSkp(page, pdfium::RectanglesChecksum()); ++ ++ UnloadPage(page); ++} ++ ++TEST_F(FPDFViewEmbedderTest, RenderXfaPageToSkp) { ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) { ++ GTEST_SKIP() << "FPDF_RenderPageSkp() only makes sense with Skia"; ++ } ++ ++ ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); ++ ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ // Should always be blank, as we're not testing `FPDF_FFLRecord()` here. ++ TestRenderPageSkp(page, pdfium::kBlankPage612By792Checksum); ++ ++ UnloadPage(page); ++} ++#endif // defined(_SKIA_SUPPORT_) ++ ++TEST_F(FPDFViewEmbedderTest, NoSmoothTextItalicOverlappingGlyphs) { ++ ASSERT_TRUE(OpenDocument("bug_1919.pdf")); ++ FPDF_PAGE page = LoadPage(0); ++ ASSERT_TRUE(page); ++ ++ TestRenderPageBitmapWithFlags(page, FPDF_RENDER_NO_SMOOTHTEXT, ++ "4ef1f65ab1ac76acb97a3540dcb10b4e"); ++ UnloadPage(page); ++} +diff --git a/fpdfsdk/fpdf_view_unittest.cpp b/fpdfsdk/fpdf_view_unittest.cpp +index c08c49bd7..26a643194 100644 +--- a/fpdfsdk/fpdf_view_unittest.cpp ++++ b/fpdfsdk/fpdf_view_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fpdfsdk/fpdfxfa/BUILD.gn b/fpdfsdk/fpdfxfa/BUILD.gn +index 4e88c4cd2..b3d5fe0a1 100644 +--- a/fpdfsdk/fpdfxfa/BUILD.gn ++++ b/fpdfsdk/fpdfxfa/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -17,8 +17,10 @@ source_set("fpdfxfa") { + "cpdfxfa_page.h", + "cpdfxfa_widget.cpp", + "cpdfxfa_widget.h", +- "cpdfxfa_widgethandler.cpp", +- "cpdfxfa_widgethandler.h", ++ ] ++ configs += [ ++ "../../:pdfium_strict_config", ++ "../../:pdfium_noshorten_config", + ] + deps = [ + "../../:pdfium_public_headers", +@@ -26,19 +28,28 @@ source_set("fpdfxfa") { + "../../core/fpdfapi/parser", + "../../core/fpdfapi/render", + "../../core/fxcrt", ++ "../../fxbarcode", + "../../fxjs", ++ "../../fxjs:gc", ++ "../../xfa/fgas/font", ++ "../../xfa/fgas/graphics", + "../../xfa/fwl", + "../../xfa/fxfa", + "../../xfa/fxfa/parser", +- "../../xfa/fxgraphics", + ] +- configs += [ "../../:pdfium_core_config" ] + visibility = [ "../../*" ] + } + + pdfium_embeddertest_source_set("embeddertests") { +- sources = [ "cpdfxfa_docenvironment_embeddertest.cpp" ] ++ sources = [ ++ "cpdfxfa_context_embeddertest.cpp", ++ "cpdfxfa_docenvironment_embeddertest.cpp", ++ ] + configs = [ "//v8:external_startup_data" ] +- deps = [ "../../fxjs" ] ++ deps = [ ++ ":fpdfxfa", ++ "../:fpdfsdk", ++ "../../fxjs", ++ ] + pdfium_root_dir = "../../" + } +diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp +index 7cb4a2c0b..7318cee39 100644 +--- a/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp ++++ b/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,8 @@ + + #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h" + ++#include ++ + #include + #include + +@@ -13,14 +15,22 @@ + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_document.h" + #include "core/fpdfapi/parser/cpdf_seekablemultistream.h" ++#include "core/fxcrt/autonuller.h" ++#include "core/fxcrt/fixed_zeroed_data_vector.h" ++#include "core/fxcrt/stl_util.h" ++#include "core/fxcrt/xml/cfx_xmldocument.h" ++#include "core/fxcrt/xml/cfx_xmlparser.h" + #include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fpdfsdk/cpdfsdk_pageview.h" ++#include "fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h" + #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h" ++#include "fxbarcode/BC_Library.h" + #include "fxjs/cjs_runtime.h" + #include "fxjs/ijs_runtime.h" + #include "public/fpdf_formfill.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "v8/include/cppgc/allocation.h" ++#include "xfa/fgas/font/cfgas_gemodule.h" + #include "xfa/fxfa/cxfa_eventparam.h" + #include "xfa/fxfa/cxfa_ffapp.h" + #include "xfa/fxfa/cxfa_ffdoc.h" +@@ -53,62 +63,61 @@ RetainPtr CreateXFAMultiStream( + if (!pRoot) + return nullptr; + +- const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm"); ++ RetainPtr pAcroForm = pRoot->GetDictFor("AcroForm"); + if (!pAcroForm) + return nullptr; + +- const CPDF_Object* pElementXFA = pAcroForm->GetDirectObjectFor("XFA"); ++ RetainPtr pElementXFA = ++ pAcroForm->GetDirectObjectFor("XFA"); + if (!pElementXFA) + return nullptr; + +- std::vector xfaStreams; ++ std::vector> xfa_streams; + if (pElementXFA->IsArray()) { + const CPDF_Array* pXFAArray = pElementXFA->AsArray(); + for (size_t i = 0; i < pXFAArray->size() / 2; i++) { +- if (const CPDF_Stream* pStream = pXFAArray->GetStreamAt(i * 2 + 1)) +- xfaStreams.push_back(pStream); ++ RetainPtr pStream = pXFAArray->GetStreamAt(i * 2 + 1); ++ if (pStream) ++ xfa_streams.push_back(std::move(pStream)); + } + } else if (pElementXFA->IsStream()) { +- xfaStreams.push_back(pElementXFA->AsStream()); ++ xfa_streams.push_back(ToStream(pElementXFA)); + } +- if (xfaStreams.empty()) ++ if (xfa_streams.empty()) + return nullptr; + +- return pdfium::MakeRetain(xfaStreams); ++ return pdfium::MakeRetain(std::move(xfa_streams)); + } + + } // namespace + +-CPDFXFA_Context::CPDFXFA_Context(CPDF_Document* pPDFDoc) +- : m_pPDFDoc(pPDFDoc), +- m_pXFAApp(pdfium::MakeUnique(this)), +- m_DocEnv(this) { +- ASSERT(m_pPDFDoc); ++void CPDFXFA_ModuleInit() { ++ CFGAS_GEModule::Create(); ++ BC_Library_Init(); + } + +-CPDFXFA_Context::~CPDFXFA_Context() { +- m_nLoadStatus = FXFA_LOADSTATUS_CLOSING; +- +- // Must happen before we remove the form fill environment. +- CloseXFADoc(); ++void CPDFXFA_ModuleDestroy() { ++ BC_Library_Destroy(); ++ CFGAS_GEModule::Destroy(); ++} + +- if (m_pFormFillEnv) { +- m_pFormFillEnv->ClearAllFocusedAnnots(); +- // Once we're deleted the FormFillEnvironment will point at a bad underlying +- // doc so we need to reset it ... +- m_pFormFillEnv->GetPDFDocument()->SetExtension(nullptr); +- m_pFormFillEnv.Reset(); ++CPDFXFA_Context::CPDFXFA_Context(CPDF_Document* pPDFDoc) ++ : m_pPDFDoc(pPDFDoc), ++ m_pDocEnv(std::make_unique(this)), ++ m_pGCHeap(FXGC_CreateHeap()) { ++ DCHECK(m_pPDFDoc); ++ ++ // There might not be a heap when JS not initialized. ++ if (m_pGCHeap) { ++ m_pXFAApp = cppgc::MakeGarbageCollected( ++ m_pGCHeap->GetAllocationHandle(), this); + } +- +- m_nLoadStatus = FXFA_LOADSTATUS_CLOSED; + } + +-void CPDFXFA_Context::CloseXFADoc() { +- if (!m_pXFADoc) +- return; +- +- m_pXFADocView = nullptr; +- m_pXFADoc.reset(); ++CPDFXFA_Context::~CPDFXFA_Context() { ++ m_nLoadStatus = LoadStatus::kClosing; ++ if (m_pFormFillEnv) ++ m_pFormFillEnv->ClearAllFocusedAnnots(); + } + + void CPDFXFA_Context::SetFormFillEnv( +@@ -117,58 +126,75 @@ void CPDFXFA_Context::SetFormFillEnv( + // context will be different if the form fill environment closes, so, force + // the layout data to clear. + if (m_pXFADoc && m_pXFADoc->GetXFADoc()) { +- // The CPDF_XFADocView has a pointer to the CXFA_LayoutProcessor which is +- // owned by the CXFA_Document. The Layout Processor will be freed with the +- // ClearLayoutData() call. Make sure the doc view has already released the +- // pointer. +- if (m_pXFADocView) +- m_pXFADocView->ResetLayoutProcessor(); +- + m_pXFADoc->GetXFADoc()->ClearLayoutData(); ++ m_pXFADocView.Clear(); ++ m_pXFADoc.Clear(); ++ m_pXFAApp.Clear(); ++ FXGC_ForceGarbageCollection(m_pGCHeap.get()); + } +- + m_pFormFillEnv.Reset(pFormFillEnv); + } + + bool CPDFXFA_Context::LoadXFADoc() { +- m_nLoadStatus = FXFA_LOADSTATUS_LOADING; ++ m_nLoadStatus = LoadStatus::kLoading; + m_XFAPageList.clear(); + +- auto stream = CreateXFAMultiStream(m_pPDFDoc.Get()); ++ CJS_Runtime* actual_runtime = GetCJSRuntime(); // Null if a stub. ++ if (!actual_runtime) { ++ FXSYS_SetLastError(FPDF_ERR_XFALOAD); ++ return false; ++ } ++ ++ auto stream = CreateXFAMultiStream(m_pPDFDoc); + if (!stream) { + FXSYS_SetLastError(FPDF_ERR_XFALOAD); + return false; + } + +- m_pXFADoc = CXFA_FFDoc::CreateAndOpen(m_pXFAApp.get(), &m_DocEnv, +- m_pPDFDoc.Get(), stream); +- if (!m_pXFADoc) { ++ CFX_XMLParser parser(stream); ++ m_pXML = parser.Parse(); ++ if (!m_pXML) { + FXSYS_SetLastError(FPDF_ERR_XFALOAD); + return false; + } + +- CJS_Runtime* actual_runtime = GetCJSRuntime(); // Null if a stub. +- if (!actual_runtime) { ++ AutoNuller> doc_nuller(&m_pXFADoc); ++ m_pXFADoc = cppgc::MakeGarbageCollected( ++ m_pGCHeap->GetAllocationHandle(), m_pXFAApp, m_pDocEnv.get(), m_pPDFDoc, ++ m_pGCHeap.get()); ++ ++ if (!m_pXFADoc->OpenDoc(m_pXML.get())) { + FXSYS_SetLastError(FPDF_ERR_XFALOAD); + return false; + } + ++ if (!m_pXFAApp->LoadFWLTheme(m_pXFADoc)) { ++ FXSYS_SetLastError(FPDF_ERR_XFALAYOUT); ++ return false; ++ } ++ + m_pXFADoc->GetXFADoc()->InitScriptContext(actual_runtime); + if (m_pXFADoc->GetFormType() == FormType::kXFAFull) + m_FormType = FormType::kXFAFull; + else + m_FormType = FormType::kXFAForeground; + ++ AutoNuller> view_nuller(&m_pXFADocView); + m_pXFADocView = m_pXFADoc->CreateDocView(); ++ + if (m_pXFADocView->StartLayout() < 0) { +- CloseXFADoc(); ++ m_pXFADoc->GetXFADoc()->ClearLayoutData(); ++ FXGC_ForceGarbageCollection(m_pGCHeap.get()); + FXSYS_SetLastError(FPDF_ERR_XFALAYOUT); + return false; + } + + m_pXFADocView->DoLayout(); + m_pXFADocView->StopLayout(); +- m_nLoadStatus = FXFA_LOADSTATUS_LOADED; ++ ++ view_nuller.AbandonNullification(); ++ doc_nuller.AbandonNullification(); ++ m_nLoadStatus = LoadStatus::kLoaded; + return true; + } + +@@ -181,15 +207,13 @@ int CPDFXFA_Context::GetPageCount() const { + case FormType::kXFAFull: + return m_pXFADoc ? m_pXFADocView->CountPageViews() : 0; + } +- NOTREACHED(); +- return 0; + } + +-RetainPtr CPDFXFA_Context::GetXFAPage(int page_index) { ++RetainPtr CPDFXFA_Context::GetOrCreateXFAPage(int page_index) { + if (page_index < 0) + return nullptr; + +- if (pdfium::IndexInBounds(m_XFAPageList, page_index)) { ++ if (fxcrt::IndexInBounds(m_XFAPageList, page_index)) { + if (m_XFAPageList[page_index]) + return m_XFAPageList[page_index]; + } else { +@@ -201,12 +225,19 @@ RetainPtr CPDFXFA_Context::GetXFAPage(int page_index) { + if (!pPage->LoadPage()) + return nullptr; + +- if (pdfium::IndexInBounds(m_XFAPageList, page_index)) ++ if (fxcrt::IndexInBounds(m_XFAPageList, page_index)) + m_XFAPageList[page_index] = pPage; + + return pPage; + } + ++RetainPtr CPDFXFA_Context::GetXFAPage(int page_index) { ++ if (!fxcrt::IndexInBounds(m_XFAPageList, page_index)) ++ return nullptr; ++ ++ return m_XFAPageList[page_index]; ++} ++ + RetainPtr CPDFXFA_Context::GetXFAPage( + CXFA_FFPageView* pPage) const { + if (!pPage) +@@ -225,17 +256,13 @@ RetainPtr CPDFXFA_Context::GetXFAPage( + return nullptr; + } + +-CPDF_Document* CPDFXFA_Context::GetPDFDoc() const { +- return m_pPDFDoc.Get(); +-} +- + void CPDFXFA_Context::DeletePage(int page_index) { + // Delete from the document first because, if GetPage was never called for + // this |page_index| then |m_XFAPageList| may have size < |page_index| even + // if it's a valid page in the document. + m_pPDFDoc->DeletePage(page_index); + +- if (pdfium::IndexInBounds(m_XFAPageList, page_index)) ++ if (fxcrt::IndexInBounds(m_XFAPageList, page_index)) + m_XFAPageList[page_index].Reset(); + } + +@@ -294,7 +321,7 @@ int32_t CPDFXFA_Context::MsgBox(const WideString& wsMessage, + const WideString& wsTitle, + uint32_t dwIconType, + uint32_t dwButtonType) { +- if (!m_pFormFillEnv || m_nLoadStatus != FXFA_LOADSTATUS_LOADED) ++ if (!m_pFormFillEnv || m_nLoadStatus != LoadStatus::kLoaded) + return -1; + + int iconType = +@@ -312,19 +339,18 @@ WideString CPDFXFA_Context::Response(const WideString& wsQuestion, + if (!m_pFormFillEnv) + return WideString(); + +- int nLength = 2048; +- std::vector pBuff(nLength); +- nLength = m_pFormFillEnv->JS_appResponse(wsQuestion, wsTitle, wsDefaultAnswer, +- WideString(), bMark, pBuff.data(), +- nLength); +- if (nLength <= 0) ++ constexpr int kMaxWideChars = 1024; ++ FixedZeroedDataVector buffer(kMaxWideChars); ++ pdfium::span buffer_span = buffer.writable_span(); ++ int byte_length = m_pFormFillEnv->JS_appResponse( ++ wsQuestion, wsTitle, wsDefaultAnswer, WideString(), bMark, ++ pdfium::as_writable_bytes(buffer_span)); ++ if (byte_length <= 0) + return WideString(); + +- nLength = std::min(2046, nLength); +- pBuff[nLength] = 0; +- pBuff[nLength + 1] = 0; +- return WideString::FromUTF16LE(reinterpret_cast(pBuff.data()), +- nLength / sizeof(uint16_t)); ++ buffer_span = buffer_span.first( ++ std::min(kMaxWideChars, byte_length / sizeof(uint16_t))); ++ return WideString::FromUTF16LE(buffer_span.data(), buffer_span.size()); + } + + RetainPtr CPDFXFA_Context::DownloadURL( +@@ -353,10 +379,14 @@ bool CPDFXFA_Context::PutRequestURL(const WideString& wsURL, + m_pFormFillEnv->PutRequestURL(wsURL, wsData, wsEncode); + } + +-TimerHandlerIface* CPDFXFA_Context::GetTimerHandler() const { ++CFX_Timer::HandlerIface* CPDFXFA_Context::GetTimerHandler() const { + return m_pFormFillEnv ? m_pFormFillEnv->GetTimerHandler() : nullptr; + } + ++cppgc::Heap* CPDFXFA_Context::GetGCHeap() const { ++ return m_pGCHeap.get(); ++} ++ + bool CPDFXFA_Context::SaveDatasetsPackage( + const RetainPtr& pStream) { + return SavePackage(pStream, XFA_HASHCODE_Datasets); +@@ -387,10 +417,11 @@ void CPDFXFA_Context::SendPostSaveToXFADoc() { + return; + + CXFA_FFWidgetHandler* pWidgetHandler = pXFADocView->GetWidgetHandler(); +- auto it = pXFADocView->CreateReadyNodeIterator(); +- while (CXFA_Node* pNode = it->MoveToNext()) { ++ CXFA_ReadyNodeIterator it(pXFADocView->GetRootSubform()); ++ while (CXFA_Node* pNode = it.MoveToNext()) { + CXFA_EventParam preParam; + preParam.m_eType = XFA_EVENT_PostSave; ++ preParam.m_bTargeted = false; + pWidgetHandler->ProcessEvent(pNode, &preParam); + } + pXFADocView->UpdateDocView(); +@@ -407,10 +438,11 @@ void CPDFXFA_Context::SendPreSaveToXFADoc( + return; + + CXFA_FFWidgetHandler* pWidgetHandler = pXFADocView->GetWidgetHandler(); +- auto it = pXFADocView->CreateReadyNodeIterator(); +- while (CXFA_Node* pNode = it->MoveToNext()) { ++ CXFA_ReadyNodeIterator it(pXFADocView->GetRootSubform()); ++ while (CXFA_Node* pNode = it.MoveToNext()) { + CXFA_EventParam preParam; + preParam.m_eType = XFA_EVENT_PreSave; ++ preParam.m_bTargeted = false; + pWidgetHandler->ProcessEvent(pNode, &preParam); + } + pXFADocView->UpdateDocView(); +diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_context.h b/fpdfsdk/fpdfxfa/cpdfxfa_context.h +index 0f6a14bbe..4188438be 100644 +--- a/fpdfsdk/fpdfxfa/cpdfxfa_context.h ++++ b/fpdfsdk/fpdfxfa/cpdfxfa_context.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,53 +7,67 @@ + #ifndef FPDFSDK_FPDFXFA_CPDFXFA_CONTEXT_H_ + #define FPDFSDK_FPDFXFA_CPDFXFA_CONTEXT_H_ + ++#include ++ + #include + #include + + #include "core/fpdfapi/parser/cpdf_document.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/cfx_timer.h" + #include "core/fxcrt/observed_ptr.h" +-#include "core/fxcrt/timerhandler_iface.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + #include "fpdfsdk/cpdfsdk_formfillenvironment.h" +-#include "fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h" + #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h" ++#include "fxjs/gc/heap.h" ++#include "v8/include/cppgc/persistent.h" ++#include "xfa/fxfa/cxfa_ffapp.h" + #include "xfa/fxfa/cxfa_ffdoc.h" + ++class CFX_XMLDocument; + class CJS_Runtime; +-class CXFA_FFDocHandler; +-class IJS_EventContext; +-class IJS_Runtime; +- +-enum LoadStatus { +- FXFA_LOADSTATUS_PRELOAD = 0, +- FXFA_LOADSTATUS_LOADING, +- FXFA_LOADSTATUS_LOADED, +- FXFA_LOADSTATUS_CLOSING, +- FXFA_LOADSTATUS_CLOSED +-}; ++class CPDFXFA_DocEnvironment; ++ ++// Per-process initializations. ++void CPDFXFA_ModuleInit(); ++void CPDFXFA_ModuleDestroy(); + + class CPDFXFA_Context final : public CPDF_Document::Extension, +- public IXFA_AppProvider { ++ public CXFA_FFApp::CallbackIface { + public: ++ enum class LoadStatus : uint8_t { ++ kPreload = 0, ++ kLoading, ++ kLoaded, ++ kClosing, ++ }; ++ + explicit CPDFXFA_Context(CPDF_Document* pPDFDoc); + ~CPDFXFA_Context() override; + + bool LoadXFADoc(); +- CXFA_FFDoc* GetXFADoc() { return m_pXFADoc.get(); } +- CXFA_FFDocView* GetXFADocView() const { return m_pXFADocView.Get(); } ++ LoadStatus GetLoadStatus() const { return m_nLoadStatus; } + FormType GetFormType() const { return m_FormType; } ++ int GetOriginalPageCount() const { return m_nPageCount; } ++ void SetOriginalPageCount(int count) { ++ m_nPageCount = count; ++ m_XFAPageList.resize(count); ++ } ++ ++ CPDF_Document* GetPDFDoc() const { return m_pPDFDoc; } ++ CFX_XMLDocument* GetXMLDoc() { return m_pXML.get(); } ++ CXFA_FFDoc* GetXFADoc() { return m_pXFADoc; } ++ CXFA_FFDocView* GetXFADocView() const { return m_pXFADocView.Get(); } + CPDFSDK_FormFillEnvironment* GetFormFillEnv() const { + return m_pFormFillEnv.Get(); + } + void SetFormFillEnv(CPDFSDK_FormFillEnvironment* pFormFillEnv); +- ++ RetainPtr GetOrCreateXFAPage(int page_index); + RetainPtr GetXFAPage(int page_index); + RetainPtr GetXFAPage(CXFA_FFPageView* pPage) const; + void ClearChangeMark(); + + // CPDF_Document::Extension: +- CPDF_Document* GetPDFDoc() const override; + int GetPageCount() const override; + void DeletePage(int page_index) override; + uint32_t GetUserPermissions() const override; +@@ -61,7 +75,7 @@ class CPDFXFA_Context final : public CPDF_Document::Extension, + bool ContainsExtensionFullForm() const override; + bool ContainsExtensionForegroundForm() const override; + +- // IFXA_AppProvider: ++ // CXFA_FFApp::CallbackIface: + WideString GetLanguage() override; + WideString GetPlatform() override; + WideString GetAppName() override; +@@ -86,7 +100,8 @@ class CPDFXFA_Context final : public CPDF_Document::Extension, + bool PutRequestURL(const WideString& wsURL, + const WideString& wsData, + const WideString& wsEncode) override; +- TimerHandlerIface* GetTimerHandler() const override; ++ CFX_Timer::HandlerIface* GetTimerHandler() const override; ++ cppgc::Heap* GetGCHeap() const override; + + bool SaveDatasetsPackage(const RetainPtr& pStream); + bool SaveFormPackage(const RetainPtr& pStream); +@@ -95,37 +110,27 @@ class CPDFXFA_Context final : public CPDF_Document::Extension, + std::vector>* fileList); + + private: +- friend class CPDFXFA_DocEnvironment; +- +- int GetOriginalPageCount() const { return m_nPageCount; } +- void SetOriginalPageCount(int count) { +- m_nPageCount = count; +- m_XFAPageList.resize(count); +- } +- +- LoadStatus GetLoadStatus() const { return m_nLoadStatus; } +- std::vector>* GetXFAPageList() { +- return &m_XFAPageList; +- } +- + CJS_Runtime* GetCJSRuntime() const; + bool SavePackage(const RetainPtr& pStream, + XFA_HashCode code); +- void CloseXFADoc(); + + FormType m_FormType = FormType::kNone; ++ LoadStatus m_nLoadStatus = LoadStatus::kPreload; ++ int m_nPageCount = 0; ++ ++ // The order in which the following members are destroyed is critical. + UnownedPtr const m_pPDFDoc; +- std::unique_ptr m_pXFADoc; ++ std::unique_ptr m_pXML; + ObservedPtr m_pFormFillEnv; +- UnownedPtr m_pXFADocView; +- std::unique_ptr const m_pXFAApp; +- std::unique_ptr m_pRuntime; + std::vector> m_XFAPageList; +- LoadStatus m_nLoadStatus = FXFA_LOADSTATUS_PRELOAD; +- int m_nPageCount = 0; + +- // Must be destroyed before |m_pFormFillEnv|. +- CPDFXFA_DocEnvironment m_DocEnv; ++ // Can't outlive |m_pFormFillEnv|. ++ std::unique_ptr m_pDocEnv; ++ ++ FXGCScopedHeap m_pGCHeap; ++ cppgc::Persistent m_pXFAApp; // can't outlive |m_pGCHeap| ++ cppgc::Persistent m_pXFADoc; // Can't outlive |m_pGCHeap| ++ cppgc::Persistent m_pXFADocView; // Can't outlive |m_pGCHeap| + }; + + #endif // FPDFSDK_FPDFXFA_CPDFXFA_CONTEXT_H_ +diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_context_embeddertest.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_context_embeddertest.cpp +new file mode 100644 +index 000000000..36b1ad05b +--- /dev/null ++++ b/fpdfsdk/fpdfxfa/cpdfxfa_context_embeddertest.cpp +@@ -0,0 +1,20 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h" ++ ++#include "fpdfsdk/cpdfsdk_helpers.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "testing/xfa_js_embedder_test.h" ++ ++class CPDFXFAContextEmbedderTest : public XFAJSEmbedderTest {}; ++ ++// Should not crash. ++TEST_F(CPDFXFAContextEmbedderTest, HasHeap) { ++ ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); ++ ++ CPDF_Document* pDocument = CPDFDocumentFromFPDFDocument(document()); ++ auto* pContext = static_cast(pDocument->GetExtension()); ++ EXPECT_TRUE(pContext->GetGCHeap()); ++} +diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp +index 11d1e4968..ff93008a9 100644 +--- a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp ++++ b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,6 @@ + + #include "fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h" + +-#include + #include + + #include "core/fpdfapi/parser/cpdf_array.h" +@@ -21,6 +20,7 @@ + #include "fpdfsdk/cpdfsdk_pageview.h" + #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h" + #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h" ++#include "third_party/base/check.h" + #include "xfa/fxfa/cxfa_ffdocview.h" + #include "xfa/fxfa/cxfa_ffwidget.h" + #include "xfa/fxfa/cxfa_ffwidgethandler.h" +@@ -43,12 +43,21 @@ + #define FXFA_PDF 0x10000000 + #define FXFA_XFA_ALL 0x01111111 + ++// Although there isn't direct casting between these types at present, ++// keep the internal and exernal types in sync. ++static_assert(FXFA_PAGEVIEWEVENT_POSTADDED == ++ static_cast(CXFA_FFDoc::PageViewEvent::kPostAdded), ++ "kPostAdded mismatch"); ++static_assert(FXFA_PAGEVIEWEVENT_POSTREMOVED == ++ static_cast(CXFA_FFDoc::PageViewEvent::kPostRemoved), ++ "kPostRemoved mismatch"); ++ + CPDFXFA_DocEnvironment::CPDFXFA_DocEnvironment(CPDFXFA_Context* pContext) + : m_pContext(pContext) { +- ASSERT(m_pContext); ++ DCHECK(m_pContext); + } + +-CPDFXFA_DocEnvironment::~CPDFXFA_DocEnvironment() {} ++CPDFXFA_DocEnvironment::~CPDFXFA_DocEnvironment() = default; + + void CPDFXFA_DocEnvironment::SetChangeMark(CXFA_FFDoc* hDoc) { + if (hDoc == m_pContext->GetXFADoc() && m_pContext->GetFormFillEnv()) +@@ -266,20 +275,20 @@ bool CPDFXFA_DocEnvironment::PopupMenu(CXFA_FFWidget* hWidget, + if (hWidget->CanSelectAll()) + menuFlag |= FXFA_MENU_SELECTALL; + +- return pFormFillEnv->PopupMenu(pPage.Get(), nullptr, menuFlag, ptPopup); ++ return pFormFillEnv->PopupMenu(pPage.Get(), menuFlag, ptPopup); + } + +-void CPDFXFA_DocEnvironment::PageViewEvent(CXFA_FFPageView* pPageView, +- uint32_t dwFlags) { ++void CPDFXFA_DocEnvironment::OnPageViewEvent(CXFA_FFPageView* pPageView, ++ CXFA_FFDoc::PageViewEvent eEvent) { + CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv(); + if (!pFormFillEnv) + return; + +- if (m_pContext->GetLoadStatus() == FXFA_LOADSTATUS_LOADING || +- m_pContext->GetLoadStatus() == FXFA_LOADSTATUS_CLOSING || +- XFA_PAGEVIEWEVENT_StopLayout != dwFlags) ++ if (m_pContext->GetLoadStatus() == CPDFXFA_Context::LoadStatus::kLoading || ++ m_pContext->GetLoadStatus() == CPDFXFA_Context::LoadStatus::kClosing || ++ eEvent != CXFA_FFDoc::PageViewEvent::kStopLayout) { + return; +- ++ } + int nNewCount = m_pContext->GetPageCount(); + if (nNewCount == m_pContext->GetOriginalPageCount()) + return; +@@ -288,14 +297,13 @@ void CPDFXFA_DocEnvironment::PageViewEvent(CXFA_FFPageView* pPageView, + if (!pXFADocView) + return; + +- for (int iPageIter = 0; iPageIter < m_pContext->GetOriginalPageCount(); +- iPageIter++) { +- RetainPtr pPage = (*m_pContext->GetXFAPageList())[iPageIter]; ++ for (int i = 0; i < m_pContext->GetOriginalPageCount(); ++i) { ++ RetainPtr pPage = m_pContext->GetXFAPage(i); + if (!pPage) + continue; + + m_pContext->GetFormFillEnv()->RemovePageView(pPage.Get()); +- pPage->SetXFAPageViewIndex(iPageIter); ++ pPage->SetXFAPageViewIndex(i); + } + + int flag = (nNewCount < m_pContext->GetOriginalPageCount()) +@@ -307,7 +315,7 @@ void CPDFXFA_DocEnvironment::PageViewEvent(CXFA_FFPageView* pPageView, + } + + void CPDFXFA_DocEnvironment::WidgetPostAdd(CXFA_FFWidget* hWidget) { +- if (m_pContext->GetFormType() != FormType::kXFAFull || !hWidget) ++ if (m_pContext->GetFormType() != FormType::kXFAFull) + return; + + CXFA_FFPageView* pPageView = hWidget->GetPageView(); +@@ -318,13 +326,12 @@ void CPDFXFA_DocEnvironment::WidgetPostAdd(CXFA_FFWidget* hWidget) { + if (!pXFAPage) + return; + +- m_pContext->GetFormFillEnv() +- ->GetPageView(pXFAPage.Get(), true) +- ->AddAnnot(hWidget); ++ auto* formfill = m_pContext->GetFormFillEnv(); ++ formfill->GetOrCreatePageView(pXFAPage.Get())->AddAnnotForFFWidget(hWidget); + } + + void CPDFXFA_DocEnvironment::WidgetPreRemove(CXFA_FFWidget* hWidget) { +- if (m_pContext->GetFormType() != FormType::kXFAFull || !hWidget) ++ if (m_pContext->GetFormType() != FormType::kXFAFull) + return; + + CXFA_FFPageView* pPageView = hWidget->GetPageView(); +@@ -336,19 +343,17 @@ void CPDFXFA_DocEnvironment::WidgetPreRemove(CXFA_FFWidget* hWidget) { + return; + + CPDFSDK_PageView* pSdkPageView = +- m_pContext->GetFormFillEnv()->GetPageView(pXFAPage.Get(), true); +- CPDFSDK_Annot* pAnnot = pSdkPageView->GetAnnotByXFAWidget(hWidget); +- if (pAnnot) +- pSdkPageView->DeleteAnnot(pAnnot); ++ m_pContext->GetFormFillEnv()->GetOrCreatePageView(pXFAPage.Get()); ++ pSdkPageView->DeleteAnnotForFFWidget(hWidget); + } + +-int32_t CPDFXFA_DocEnvironment::CountPages(CXFA_FFDoc* hDoc) { ++int32_t CPDFXFA_DocEnvironment::CountPages(const CXFA_FFDoc* hDoc) const { + if (hDoc == m_pContext->GetXFADoc() && m_pContext->GetFormFillEnv()) + return m_pContext->GetPageCount(); + return 0; + } + +-int32_t CPDFXFA_DocEnvironment::GetCurrentPage(CXFA_FFDoc* hDoc) { ++int32_t CPDFXFA_DocEnvironment::GetCurrentPage(const CXFA_FFDoc* hDoc) const { + if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv()) + return -1; + +@@ -374,7 +379,8 @@ void CPDFXFA_DocEnvironment::SetCurrentPage(CXFA_FFDoc* hDoc, + pFormFillEnv->SetCurrentPage(iCurPage); + } + +-bool CPDFXFA_DocEnvironment::IsCalculationsEnabled(CXFA_FFDoc* hDoc) { ++bool CPDFXFA_DocEnvironment::IsCalculationsEnabled( ++ const CXFA_FFDoc* hDoc) const { + if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv()) + return false; + auto* pForm = m_pContext->GetFormFillEnv()->GetInteractiveForm(); +@@ -389,26 +395,34 @@ void CPDFXFA_DocEnvironment::SetCalculationsEnabled(CXFA_FFDoc* hDoc, + bEnabled); + } + +-void CPDFXFA_DocEnvironment::GetTitle(CXFA_FFDoc* hDoc, WideString& wsTitle) { +- if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetPDFDoc()) +- return; ++WideString CPDFXFA_DocEnvironment::GetTitle(const CXFA_FFDoc* hDoc) const { ++ if (hDoc != m_pContext->GetXFADoc()) ++ return WideString(); ++ ++ CPDF_Document* pPDFDoc = m_pContext->GetPDFDoc(); ++ if (!pPDFDoc) ++ return WideString(); + +- const CPDF_Dictionary* pInfoDict = m_pContext->GetPDFDoc()->GetInfo(); ++ RetainPtr pInfoDict = pPDFDoc->GetInfo(); + if (!pInfoDict) +- return; ++ return WideString(); + +- ByteString csTitle = pInfoDict->GetStringFor("Title"); +- wsTitle = WideString::FromDefANSI(csTitle.AsStringView()); ++ ByteString csTitle = pInfoDict->GetByteStringFor("Title"); ++ return WideString::FromDefANSI(csTitle.AsStringView()); + } + + void CPDFXFA_DocEnvironment::SetTitle(CXFA_FFDoc* hDoc, + const WideString& wsTitle) { +- if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetPDFDoc()) ++ if (hDoc != m_pContext->GetXFADoc()) + return; + +- CPDF_Dictionary* pInfoDict = m_pContext->GetPDFDoc()->GetInfo(); ++ CPDF_Document* pPDFDoc = m_pContext->GetPDFDoc(); ++ if (!pPDFDoc) ++ return; ++ ++ RetainPtr pInfoDict = pPDFDoc->GetInfo(); + if (pInfoDict) +- pInfoDict->SetNewFor("Title", wsTitle); ++ pInfoDict->SetNewFor("Title", wsTitle.AsStringView()); + } + + void CPDFXFA_DocEnvironment::ExportData(CXFA_FFDoc* hDoc, +@@ -454,23 +468,24 @@ void CPDFXFA_DocEnvironment::ExportData(CXFA_FFDoc* hDoc, + if (!pRoot) + return; + +- const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm"); ++ RetainPtr pAcroForm = pRoot->GetDictFor("AcroForm"); + if (!pAcroForm) + return; + +- const CPDF_Array* pArray = ToArray(pAcroForm->GetObjectFor("XFA")); ++ RetainPtr pArray = ++ ToArray(pAcroForm->GetObjectFor("XFA")); + if (!pArray) + return; + + for (size_t i = 1; i < pArray->size(); i += 2) { +- const CPDF_Object* pPDFObj = pArray->GetObjectAt(i); +- const CPDF_Object* pPrePDFObj = pArray->GetObjectAt(i - 1); ++ RetainPtr pPDFObj = pArray->GetObjectAt(i); ++ RetainPtr pPrePDFObj = pArray->GetObjectAt(i - 1); + if (!pPrePDFObj->IsString()) + continue; + if (!pPDFObj->IsReference()) + continue; + +- const CPDF_Stream* pStream = ToStream(pPDFObj->GetDirect()); ++ RetainPtr pStream = ToStream(pPDFObj->GetDirect()); + if (!pStream) + continue; + if (pPrePDFObj->GetString() == "form") { +@@ -497,9 +512,9 @@ void CPDFXFA_DocEnvironment::ExportData(CXFA_FFDoc* hDoc, + ByteString content = ByteString::Format(kFormat, bPath.c_str()); + fileWrite->WriteString(content.AsStringView()); + } +- auto pAcc = pdfium::MakeRetain(pStream); ++ auto pAcc = pdfium::MakeRetain(std::move(pStream)); + pAcc->LoadAllDataFiltered(); +- fileWrite->WriteBlock(pAcc->GetData(), pAcc->GetSize()); ++ fileWrite->WriteBlock(pAcc->GetSpan()); + } + } + fileWrite->Flush(); +@@ -520,7 +535,8 @@ void CPDFXFA_DocEnvironment::GotoURL(CXFA_FFDoc* hDoc, + pFormFillEnv->GotoURL(wsURL); + } + +-bool CPDFXFA_DocEnvironment::IsValidationsEnabled(CXFA_FFDoc* hDoc) { ++bool CPDFXFA_DocEnvironment::IsValidationsEnabled( ++ const CXFA_FFDoc* hDoc) const { + if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv()) + return false; + +@@ -544,19 +560,20 @@ void CPDFXFA_DocEnvironment::SetFocusWidget(CXFA_FFDoc* hDoc, + + if (!hWidget) { + ObservedPtr pNull; +- m_pContext->GetFormFillEnv()->SetFocusAnnot(&pNull); ++ m_pContext->GetFormFillEnv()->SetFocusAnnot(pNull); + return; + } + + int pageViewCount = m_pContext->GetFormFillEnv()->GetPageViewCount(); + for (int i = 0; i < pageViewCount; i++) { +- CPDFSDK_PageView* pPageView = m_pContext->GetFormFillEnv()->GetPageView(i); ++ CPDFSDK_PageView* pPageView = ++ m_pContext->GetFormFillEnv()->GetPageViewAtIndex(i); + if (!pPageView) + continue; + +- ObservedPtr pAnnot(pPageView->GetAnnotByXFAWidget(hWidget)); ++ ObservedPtr pAnnot(pPageView->GetAnnotForFFWidget(hWidget)); + if (pAnnot) { +- m_pContext->GetFormFillEnv()->SetFocusAnnot(&pAnnot); ++ m_pContext->GetFormFillEnv()->SetFocusAnnot(pAnnot); + break; + } + } +@@ -565,7 +582,7 @@ void CPDFXFA_DocEnvironment::SetFocusWidget(CXFA_FFDoc* hDoc, + void CPDFXFA_DocEnvironment::Print(CXFA_FFDoc* hDoc, + int32_t nStartPage, + int32_t nEndPage, +- uint32_t dwOptions) { ++ Mask dwOptions) { + if (hDoc != m_pContext->GetXFADoc()) + return; + +@@ -578,13 +595,16 @@ void CPDFXFA_DocEnvironment::Print(CXFA_FFDoc* hDoc, + + pFormFillEnv->GetFormFillInfo()->m_pJsPlatform->Doc_print( + pFormFillEnv->GetFormFillInfo()->m_pJsPlatform, +- dwOptions & XFA_PRINTOPT_ShowDialog, nStartPage, nEndPage, +- dwOptions & XFA_PRINTOPT_CanCancel, dwOptions & XFA_PRINTOPT_ShrinkPage, +- dwOptions & XFA_PRINTOPT_AsImage, dwOptions & XFA_PRINTOPT_ReverseOrder, +- dwOptions & XFA_PRINTOPT_PrintAnnot); ++ !!(dwOptions & XFA_PrintOpt::kShowDialog), nStartPage, nEndPage, ++ !!(dwOptions & XFA_PrintOpt::kCanCancel), ++ !!(dwOptions & XFA_PrintOpt::kShrinkPage), ++ !!(dwOptions & XFA_PrintOpt::kAsImage), ++ !!(dwOptions & XFA_PrintOpt::kReverseOrder), ++ !!(dwOptions & XFA_PrintOpt::kPrintAnnot)); + } + +-FX_ARGB CPDFXFA_DocEnvironment::GetHighlightColor(CXFA_FFDoc* hDoc) { ++FX_ARGB CPDFXFA_DocEnvironment::GetHighlightColor( ++ const CXFA_FFDoc* hDoc) const { + if (hDoc != m_pContext->GetXFADoc() || !m_pContext->GetFormFillEnv()) + return 0; + +@@ -594,7 +614,8 @@ FX_ARGB CPDFXFA_DocEnvironment::GetHighlightColor(CXFA_FFDoc* hDoc) { + pForm->GetHighlightColor(FormFieldType::kXFA)); + } + +-IJS_Runtime* CPDFXFA_DocEnvironment::GetIJSRuntime(CXFA_FFDoc* hDoc) const { ++IJS_Runtime* CPDFXFA_DocEnvironment::GetIJSRuntime( ++ const CXFA_FFDoc* hDoc) const { + if (hDoc != m_pContext->GetXFADoc()) + return nullptr; + +@@ -602,6 +623,10 @@ IJS_Runtime* CPDFXFA_DocEnvironment::GetIJSRuntime(CXFA_FFDoc* hDoc) const { + return pFormFillEnv ? pFormFillEnv->GetIJSRuntime() : nullptr; + } + ++CFX_XMLDocument* CPDFXFA_DocEnvironment::GetXMLDoc() const { ++ return m_pContext->GetXMLDoc(); ++} ++ + RetainPtr CPDFXFA_DocEnvironment::OpenLinkedFile( + CXFA_FFDoc* hDoc, + const WideString& wsLink) { +@@ -620,12 +645,12 @@ RetainPtr CPDFXFA_DocEnvironment::OpenLinkedFile( + + #ifdef PDF_XFA_ELEMENT_SUBMIT_ENABLED + bool CPDFXFA_DocEnvironment::Submit(CXFA_FFDoc* hDoc, CXFA_Submit* submit) { +- if (!NotifySubmit(true) || !m_pContext->GetXFADocView()) ++ if (!OnBeforeNotifySubmit() || !m_pContext->GetXFADocView()) + return false; + + m_pContext->GetXFADocView()->UpdateDocView(); + bool ret = SubmitInternal(hDoc, submit); +- NotifySubmit(false); ++ OnAfterNotifySubmit(); + return ret; + } + +@@ -733,27 +758,27 @@ bool CPDFXFA_DocEnvironment::ExportSubmitFile(FPDF_FILEHANDLER* pFileHandler, + return false; + } + +- const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm"); ++ RetainPtr pAcroForm = pRoot->GetDictFor("AcroForm"); + if (!pAcroForm) { + fileStream->Flush(); + return false; + } + +- const CPDF_Array* pArray = ToArray(pAcroForm->GetObjectFor("XFA")); ++ RetainPtr pArray = ToArray(pAcroForm->GetObjectFor("XFA")); + if (!pArray) { + fileStream->Flush(); + return false; + } + + for (size_t i = 1; i < pArray->size(); i += 2) { +- const CPDF_Object* pPDFObj = pArray->GetObjectAt(i); +- const CPDF_Object* pPrePDFObj = pArray->GetObjectAt(i - 1); ++ RetainPtr pPDFObj = pArray->GetObjectAt(i); ++ RetainPtr pPrePDFObj = pArray->GetObjectAt(i - 1); + if (!pPrePDFObj->IsString()) + continue; + if (!pPDFObj->IsReference()) + continue; + +- const CPDF_Object* pDirectObj = pPDFObj->GetDirect(); ++ RetainPtr pDirectObj = pPDFObj->GetDirect(); + if (!pDirectObj->IsStream()) + continue; + ByteString bsType = pPrePDFObj->GetString(); +@@ -878,14 +903,6 @@ void CPDFXFA_DocEnvironment::OnAfterNotifySubmit() { + m_pContext->GetXFADocView()->UpdateDocView(); + } + +-bool CPDFXFA_DocEnvironment::NotifySubmit(bool bPrevOrPost) { +- if (bPrevOrPost) +- return OnBeforeNotifySubmit(); +- +- OnAfterNotifySubmit(); +- return true; +-} +- + bool CPDFXFA_DocEnvironment::SubmitInternal(CXFA_FFDoc* hDoc, + CXFA_Submit* submit) { + CPDFSDK_FormFillEnvironment* pFormFillEnv = m_pContext->GetFormFillEnv(); +diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h +index ebb86c4e6..6022434e5 100644 +--- a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h ++++ b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,17 +10,18 @@ + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" + #include "public/fpdfview.h" +-#include "xfa/fxfa/fxfa.h" ++#include "xfa/fxfa/cxfa_ffdoc.h" + ++class CFX_XMLDocument; + class CPDFXFA_Context; + class IJS_Runtime; + +-class CPDFXFA_DocEnvironment final : public IXFA_DocEnvironment { ++class CPDFXFA_DocEnvironment final : public CXFA_FFDoc::CallbackIface { + public: + explicit CPDFXFA_DocEnvironment(CPDFXFA_Context*); + ~CPDFXFA_DocEnvironment() override; + +- // IXFA_DocEnvironment: ++ // CFXA_FFDoc::CallbackIface: + void SetChangeMark(CXFA_FFDoc* hDoc) override; + void InvalidateRect(CXFA_FFPageView* pPageView, const CFX_RectF& rt) override; + void DisplayCaret(CXFA_FFWidget* hWidget, +@@ -32,29 +33,31 @@ class CPDFXFA_DocEnvironment final : public IXFA_DocEnvironment { + const CFX_RectF& rtAnchor, + CFX_RectF* pPopupRect) override; + bool PopupMenu(CXFA_FFWidget* hWidget, const CFX_PointF& ptPopup) override; +- void PageViewEvent(CXFA_FFPageView* pPageView, uint32_t dwFlags) override; ++ void OnPageViewEvent(CXFA_FFPageView* pPageView, ++ CXFA_FFDoc::PageViewEvent eEvent) override; + void WidgetPostAdd(CXFA_FFWidget* hWidget) override; + void WidgetPreRemove(CXFA_FFWidget* hWidget) override; +- int32_t CountPages(CXFA_FFDoc* hDoc) override; +- int32_t GetCurrentPage(CXFA_FFDoc* hDoc) override; ++ int32_t CountPages(const CXFA_FFDoc* hDoc) const override; ++ int32_t GetCurrentPage(const CXFA_FFDoc* hDoc) const override; + void SetCurrentPage(CXFA_FFDoc* hDoc, int32_t iCurPage) override; +- bool IsCalculationsEnabled(CXFA_FFDoc* hDoc) override; ++ bool IsCalculationsEnabled(const CXFA_FFDoc* hDoc) const override; + void SetCalculationsEnabled(CXFA_FFDoc* hDoc, bool bEnabled) override; +- void GetTitle(CXFA_FFDoc* hDoc, WideString& wsTitle) override; ++ WideString GetTitle(const CXFA_FFDoc* hDoc) const override; + void SetTitle(CXFA_FFDoc* hDoc, const WideString& wsTitle) override; + void ExportData(CXFA_FFDoc* hDoc, + const WideString& wsFilePath, + bool bXDP) override; + void GotoURL(CXFA_FFDoc* hDoc, const WideString& bsURL) override; +- bool IsValidationsEnabled(CXFA_FFDoc* hDoc) override; ++ bool IsValidationsEnabled(const CXFA_FFDoc* hDoc) const override; + void SetValidationsEnabled(CXFA_FFDoc* hDoc, bool bEnabled) override; + void SetFocusWidget(CXFA_FFDoc* hDoc, CXFA_FFWidget* hWidget) override; + void Print(CXFA_FFDoc* hDoc, + int32_t nStartPage, + int32_t nEndPage, +- uint32_t dwOptions) override; +- FX_ARGB GetHighlightColor(CXFA_FFDoc* hDoc) override; +- IJS_Runtime* GetIJSRuntime(CXFA_FFDoc* hDoc) const override; ++ Mask dwOptions) override; ++ FX_ARGB GetHighlightColor(const CXFA_FFDoc* hDoc) const override; ++ IJS_Runtime* GetIJSRuntime(const CXFA_FFDoc* hDoc) const override; ++ CFX_XMLDocument* GetXMLDoc() const override; + RetainPtr OpenLinkedFile( + CXFA_FFDoc* hDoc, + const WideString& wsLink) override; +@@ -78,7 +81,6 @@ class CPDFXFA_DocEnvironment final : public IXFA_DocEnvironment { + void ToXFAContentFlags(WideString csSrcContent, FPDF_DWORD& flag); + bool OnBeforeNotifySubmit(); + void OnAfterNotifySubmit(); +- bool NotifySubmit(bool bPrevOrPost); + bool SubmitInternal(CXFA_FFDoc* hDoc, CXFA_Submit* submit); + #endif // PDF_XFA_ELEMENT_SUBMIT_ENABLED + +diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment_embeddertest.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment_embeddertest.cpp +index 1dce59c4d..09c3d1ea7 100644 +--- a/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment_embeddertest.cpp ++++ b/fpdfsdk/fpdfxfa/cpdfxfa_docenvironment_embeddertest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_page.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_page.cpp +index 979742964..6f4f2b701 100644 +--- a/fpdfsdk/fpdfxfa/cpdfxfa_page.cpp ++++ b/fpdfsdk/fpdfxfa/cpdfxfa_page.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,25 +7,73 @@ + #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h" + + #include ++#include + + #include "core/fpdfapi/page/cpdf_page.h" ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" + #include "core/fpdfapi/parser/cpdf_document.h" +-#include "core/fpdfapi/render/cpdf_pagerendercache.h" + #include "fpdfsdk/cpdfsdk_pageview.h" + #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h" + #include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" ++#include "xfa/fgas/graphics/cfgas_gegraphics.h" + #include "xfa/fxfa/cxfa_ffdocview.h" + #include "xfa/fxfa/cxfa_ffpageview.h" + #include "xfa/fxfa/cxfa_ffwidget.h" + #include "xfa/fxfa/cxfa_ffwidgethandler.h" +-#include "xfa/fxfa/cxfa_rendercontext.h" +-#include "xfa/fxgraphics/cxfa_graphics.h" ++ ++namespace { ++ ++constexpr Mask kIteratorFilter = { ++ XFA_WidgetStatus::kVisible, ++ XFA_WidgetStatus::kViewable, ++ XFA_WidgetStatus::kFocused, ++}; ++ ++CXFA_FFWidget::IteratorIface* GCedWidgetIteratorForPage( ++ CXFA_FFPageView* pFFPageView, ++ CPDFSDK_PageView* pPageView) { ++ if (!pFFPageView) ++ return nullptr; ++ ++ ObservedPtr pWatchedPageView(pPageView); ++ CXFA_FFWidget::IteratorIface* pIterator = ++ pFFPageView->CreateGCedTraverseWidgetIterator(kIteratorFilter); ++ ++ // Check |pPageView| again because JS may have destroyed it. ++ return pWatchedPageView ? pIterator : nullptr; ++} ++ ++CXFA_FFWidget::IteratorIface* GCedWidgetIteratorForAnnot( ++ CXFA_FFPageView* pFFPageView, ++ CPDFSDK_Annot* pSDKAnnot) { ++ if (!pFFPageView) ++ return nullptr; ++ ++ CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pSDKAnnot); ++ if (!pXFAWidget) ++ return nullptr; ++ ++ ObservedPtr pObservedAnnot(pSDKAnnot); ++ CXFA_FFWidget::IteratorIface* pWidgetIterator = ++ pFFPageView->CreateGCedTraverseWidgetIterator(kIteratorFilter); ++ ++ // Check |pSDKAnnot| again because JS may have destroyed it. ++ if (!pObservedAnnot) ++ return nullptr; ++ ++ if (pWidgetIterator->GetCurrentWidget() != pXFAWidget->GetXFAFFWidget()) ++ pWidgetIterator->SetCurrentWidget(pXFAWidget->GetXFAFFWidget()); ++ ++ return pWidgetIterator; ++} ++ ++} // namespace + + CPDFXFA_Page::CPDFXFA_Page(CPDF_Document* pDocument, int page_index) + : m_pDocument(pDocument), m_iPageIndex(page_index) { +- ASSERT(m_pDocument->GetExtension()); +- ASSERT(m_iPageIndex >= 0); ++ DCHECK(m_pDocument->GetExtension()); ++ DCHECK(m_iPageIndex >= 0); + } + + CPDFXFA_Page::~CPDFXFA_Page() = default; +@@ -39,17 +87,17 @@ CPDFXFA_Page* CPDFXFA_Page::AsXFAPage() { + } + + CPDF_Document* CPDFXFA_Page::GetDocument() const { +- return m_pDocument.Get(); ++ return m_pDocument; + } + + bool CPDFXFA_Page::LoadPDFPage() { +- CPDF_Document* pPDFDoc = GetDocument(); +- CPDF_Dictionary* pDict = pPDFDoc->GetPageDictionary(m_iPageIndex); ++ RetainPtr pDict = ++ GetDocument()->GetMutablePageDictionary(m_iPageIndex); + if (!pDict) + return false; + + if (!m_pPDFPage || m_pPDFPage->GetDict() != pDict) +- LoadPDFPageFromDict(pDict); ++ LoadPDFPageFromDict(std::move(pDict)); + + return true; + } +@@ -70,15 +118,13 @@ bool CPDFXFA_Page::LoadPage() { + case FormType::kXFAFull: + return !!GetXFAPageView(); + } +- NOTREACHED(); +- return false; + } + +-void CPDFXFA_Page::LoadPDFPageFromDict(CPDF_Dictionary* pPageDict) { +- ASSERT(pPageDict); +- m_pPDFPage = pdfium::MakeRetain(GetDocument(), pPageDict); +- m_pPDFPage->SetRenderCache( +- pdfium::MakeUnique(m_pPDFPage.Get())); ++void CPDFXFA_Page::LoadPDFPageFromDict(RetainPtr pPageDict) { ++ DCHECK(pPageDict); ++ m_pPDFPage = ++ pdfium::MakeRetain(GetDocument(), std::move(pPageDict)); ++ m_pPDFPage->AddPageImageCache(); + m_pPDFPage->ParseContent(); + } + +@@ -94,7 +140,7 @@ float CPDFXFA_Page::GetPageWidth() const { + case FormType::kXFAForeground: + if (m_pPDFPage) + return m_pPDFPage->GetPageWidth(); +- FALLTHROUGH; ++ [[fallthrough]]; + case FormType::kXFAFull: + if (pPageView) + return pPageView->GetPageViewRect().width; +@@ -116,7 +162,7 @@ float CPDFXFA_Page::GetPageHeight() const { + case FormType::kXFAForeground: + if (m_pPDFPage) + return m_pPDFPage->GetPageHeight(); +- FALLTHROUGH; ++ [[fallthrough]]; + case FormType::kXFAFull: + if (pPageView) + return pPageView->GetPageViewRect().height; +@@ -126,26 +172,25 @@ float CPDFXFA_Page::GetPageHeight() const { + return 0.0f; + } + +-Optional CPDFXFA_Page::DeviceToPage( ++absl::optional CPDFXFA_Page::DeviceToPage( + const FX_RECT& rect, + int rotate, + const CFX_PointF& device_point) const { + CXFA_FFPageView* pPageView = GetXFAPageView(); + if (!m_pPDFPage && !pPageView) +- return {}; ++ return absl::nullopt; + +- CFX_PointF pos = +- GetDisplayMatrix(rect, rotate).GetInverse().Transform(device_point); +- return pos; ++ CFX_Matrix page2device = GetDisplayMatrix(rect, rotate); ++ return page2device.GetInverse().Transform(device_point); + } + +-Optional CPDFXFA_Page::PageToDevice( ++absl::optional CPDFXFA_Page::PageToDevice( + const FX_RECT& rect, + int rotate, + const CFX_PointF& page_point) const { + CXFA_FFPageView* pPageView = GetXFAPageView(); + if (!m_pPDFPage && !pPageView) +- return {}; ++ return absl::nullopt; + + CFX_Matrix page2device = GetDisplayMatrix(rect, rotate); + return page2device.Transform(page_point); +@@ -164,7 +209,7 @@ CFX_Matrix CPDFXFA_Page::GetDisplayMatrix(const FX_RECT& rect, + case FormType::kXFAForeground: + if (m_pPDFPage) + return m_pPDFPage->GetDisplayMatrix(rect, iRotate); +- FALLTHROUGH; ++ [[fallthrough]]; + case FormType::kXFAFull: + if (pPageView) + return pPageView->GetDisplayMatrix(rect, iRotate); +@@ -174,32 +219,44 @@ CFX_Matrix CPDFXFA_Page::GetDisplayMatrix(const FX_RECT& rect, + return CFX_Matrix(); + } + +-CPDFSDK_Annot* CPDFXFA_Page::GetNextXFAAnnot(CPDFSDK_Annot* pSDKAnnot, +- bool bNext) { +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pSDKAnnot); +- if (!pXFAWidget) ++CPDFSDK_Annot* CPDFXFA_Page::GetNextXFAAnnot(CPDFSDK_Annot* pSDKAnnot) const { ++ CXFA_FFWidget::IteratorIface* pWidgetIterator = ++ GCedWidgetIteratorForAnnot(GetXFAPageView(), pSDKAnnot); ++ if (!pWidgetIterator) + return nullptr; + +- ObservedPtr pObservedAnnot(pSDKAnnot); +- CPDFSDK_PageView* pPageView = pSDKAnnot->GetPageView(); +- std::unique_ptr pWidgetIterator = +- GetXFAPageView()->CreateTraverseWidgetIterator(XFA_WidgetStatus_Visible | +- XFA_WidgetStatus_Viewable | +- XFA_WidgetStatus_Focused); ++ return pSDKAnnot->GetPageView()->GetAnnotForFFWidget( ++ pWidgetIterator->MoveToNext()); ++} + +- // Check |pSDKAnnot| again because JS may have destroyed it +- if (!pObservedAnnot) ++CPDFSDK_Annot* CPDFXFA_Page::GetPrevXFAAnnot(CPDFSDK_Annot* pSDKAnnot) const { ++ CXFA_FFWidget::IteratorIface* pWidgetIterator = ++ GCedWidgetIteratorForAnnot(GetXFAPageView(), pSDKAnnot); ++ if (!pWidgetIterator) + return nullptr; + +- if (pWidgetIterator->GetCurrentWidget() != pXFAWidget->GetXFAFFWidget()) +- pWidgetIterator->SetCurrentWidget(pXFAWidget->GetXFAFFWidget()); ++ return pSDKAnnot->GetPageView()->GetAnnotForFFWidget( ++ pWidgetIterator->MoveToPrevious()); ++} + +- CXFA_FFWidget* hNextFocus = +- bNext ? pWidgetIterator->MoveToNext() : pWidgetIterator->MoveToPrevious(); +- if (!hNextFocus && pSDKAnnot) +- hNextFocus = pWidgetIterator->MoveToFirst(); ++CPDFSDK_Annot* CPDFXFA_Page::GetFirstXFAAnnot( ++ CPDFSDK_PageView* page_view) const { ++ CXFA_FFWidget::IteratorIface* pWidgetIterator = ++ GCedWidgetIteratorForPage(GetXFAPageView(), page_view); ++ if (!pWidgetIterator) ++ return nullptr; ++ ++ return page_view->GetAnnotForFFWidget(pWidgetIterator->MoveToFirst()); ++} ++ ++CPDFSDK_Annot* CPDFXFA_Page::GetLastXFAAnnot( ++ CPDFSDK_PageView* page_view) const { ++ CXFA_FFWidget::IteratorIface* pWidgetIterator = ++ GCedWidgetIteratorForPage(GetXFAPageView(), page_view); ++ if (!pWidgetIterator) ++ return nullptr; + +- return pPageView->GetAnnotByXFAWidget(hNextFocus); ++ return page_view->GetAnnotForFFWidget(pWidgetIterator->MoveToLast()); + } + + int CPDFXFA_Page::HasFormFieldAtPoint(const CFX_PointF& point) const { +@@ -214,12 +271,11 @@ int CPDFXFA_Page::HasFormFieldAtPoint(const CFX_PointF& point) const { + CXFA_FFWidgetHandler* pWidgetHandler = pDocView->GetWidgetHandler(); + if (!pWidgetHandler) + return -1; +- +- std::unique_ptr pWidgetIterator = +- pPageView->CreateFormWidgetIterator(XFA_WidgetStatus_Viewable); ++ CXFA_FFPageWidgetIterator pWidgetIterator(pPageView, ++ XFA_WidgetStatus::kViewable); + + CXFA_FFWidget* pXFAAnnot; +- while ((pXFAAnnot = pWidgetIterator->MoveToNext()) != nullptr) { ++ while ((pXFAAnnot = pWidgetIterator.MoveToNext()) != nullptr) { + if (pXFAAnnot->GetFormFieldType() == FormFieldType::kXFA) + continue; + +@@ -237,12 +293,25 @@ void CPDFXFA_Page::DrawFocusAnnot(CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device, + const FX_RECT& rtClip) { + CFX_RectF rectClip(rtClip); +- CXFA_Graphics gs(pDevice); ++ CFGAS_GEGraphics gs(pDevice); + gs.SetClipRect(rectClip); + + CXFA_FFPageView* xfaView = GetXFAPageView(); +- CXFA_RenderContext renderContext(xfaView, rectClip, mtUser2Device); +- renderContext.DoRender(&gs); ++ CXFA_FFPageWidgetIterator pWidgetIterator( ++ xfaView, Mask{XFA_WidgetStatus::kVisible, ++ XFA_WidgetStatus::kViewable}); ++ ++ while (true) { ++ CXFA_FFWidget* pWidget = pWidgetIterator.MoveToNext(); ++ if (!pWidget) ++ break; ++ ++ CFX_RectF rtWidgetBox = pWidget->GetBBox(CXFA_FFWidget::kDoNotDrawFocus); ++ ++rtWidgetBox.width; ++ ++rtWidgetBox.height; ++ if (rtWidgetBox.IntersectWith(rectClip)) ++ pWidget->RenderWidget(&gs, mtUser2Device, CXFA_FFWidget::kHighlight); ++ } + + CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot); + if (!pXFAWidget) +diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_page.h b/fpdfsdk/fpdfxfa/cpdfxfa_page.h +index 73d542112..1b05562cd 100644 +--- a/fpdfsdk/fpdfxfa/cpdfxfa_page.h ++++ b/fpdfsdk/fpdfxfa/cpdfxfa_page.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,21 +10,20 @@ + #include "core/fpdfapi/page/cpdf_page.h" + #include "core/fpdfapi/page/ipdf_page.h" + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/retain_ptr.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CFX_RenderDevice; + class CPDF_Dictionary; + class CPDF_Document; + class CPDFSDK_Annot; ++class CPDFSDK_PageView; + class CXFA_FFPageView; + + class CPDFXFA_Page final : public IPDF_Page { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // IPDF_Page: + CPDF_Page* AsPDFPage() override; +@@ -33,21 +32,24 @@ class CPDFXFA_Page final : public IPDF_Page { + float GetPageWidth() const override; + float GetPageHeight() const override; + CFX_Matrix GetDisplayMatrix(const FX_RECT& rect, int iRotate) const override; +- Optional DeviceToPage( ++ absl::optional DeviceToPage( + const FX_RECT& rect, + int rotate, + const CFX_PointF& device_point) const override; +- Optional PageToDevice( ++ absl::optional PageToDevice( + const FX_RECT& rect, + int rotate, + const CFX_PointF& page_point) const override; + + bool LoadPage(); +- void LoadPDFPageFromDict(CPDF_Dictionary* pPageDict); ++ void LoadPDFPageFromDict(RetainPtr pPageDict); + int GetPageIndex() const { return m_iPageIndex; } + void SetXFAPageViewIndex(int index) { m_iPageIndex = index; } + CXFA_FFPageView* GetXFAPageView() const; +- CPDFSDK_Annot* GetNextXFAAnnot(CPDFSDK_Annot* pSDKAnnot, bool bNext); ++ CPDFSDK_Annot* GetNextXFAAnnot(CPDFSDK_Annot* pSDKAnnot) const; ++ CPDFSDK_Annot* GetPrevXFAAnnot(CPDFSDK_Annot* pSDKAnnot) const; ++ CPDFSDK_Annot* GetFirstXFAAnnot(CPDFSDK_PageView* page_view) const; ++ CPDFSDK_Annot* GetLastXFAAnnot(CPDFSDK_PageView* page_view) const; + int HasFormFieldAtPoint(const CFX_PointF& point) const; + void DrawFocusAnnot(CFX_RenderDevice* pDevice, + CPDFSDK_Annot* pAnnot, +diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_widget.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_widget.cpp +index f4c9934f1..1367c1ab7 100644 +--- a/fpdfsdk/fpdfxfa/cpdfxfa_widget.cpp ++++ b/fpdfsdk/fpdfxfa/cpdfxfa_widget.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,15 +6,218 @@ + + #include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h" + +-#include "fpdfsdk/ipdfsdk_annothandler.h" ++#include "fpdfsdk/cpdfsdk_formfillenvironment.h" ++#include "fpdfsdk/cpdfsdk_pageview.h" ++#include "third_party/base/check.h" ++#include "xfa/fgas/graphics/cfgas_gegraphics.h" ++#include "xfa/fxfa/cxfa_ffdocview.h" ++#include "xfa/fxfa/cxfa_ffpageview.h" + #include "xfa/fxfa/cxfa_ffwidget.h" ++#include "xfa/fxfa/cxfa_ffwidgethandler.h" ++#include "xfa/fxfa/parser/cxfa_node.h" ++ ++#define CHECK_FWL_VKEY_ENUM____(name) \ ++ static_assert(static_cast(name) == static_cast(XFA_##name), \ ++ "FWL_VKEYCODE enum mismatch") ++ ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Back); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Tab); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NewLine); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Clear); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Return); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Shift); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Control); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Menu); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Pause); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Capital); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Kana); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Hangul); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Junja); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Final); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Hanja); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Kanji); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Escape); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Convert); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NonConvert); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Accept); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_ModeChange); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Space); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Prior); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Next); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_End); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Home); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Left); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Up); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Right); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Down); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Select); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Print); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Execute); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Snapshot); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Insert); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Delete); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Help); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_0); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_1); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_2); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_3); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_4); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_5); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_6); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_7); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_8); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_9); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_A); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_B); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_C); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_D); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_E); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_G); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_H); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_I); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_J); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_K); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_L); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_M); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_N); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_O); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_P); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Q); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_R); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_S); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_T); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_U); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_V); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_W); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_X); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Y); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Z); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LWin); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Command); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RWin); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Apps); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Sleep); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad0); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad1); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad2); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad3); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad4); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad5); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad6); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad7); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad8); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad9); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Multiply); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Add); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Separator); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Subtract); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Decimal); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Divide); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F1); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F2); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F3); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F4); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F5); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F6); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F7); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F8); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F9); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F10); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F11); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F12); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F13); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F14); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F15); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F16); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F17); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F18); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F19); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F20); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F21); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F22); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F23); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F24); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NunLock); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Scroll); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LShift); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RShift); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LControl); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RControl); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LMenu); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RMenu); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Back); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Forward); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Refresh); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Stop); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Search); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Favorites); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Home); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_VOLUME_Mute); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_VOLUME_Down); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_VOLUME_Up); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_NEXT_Track); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_PREV_Track); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_Stop); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_PLAY_Pause); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_Mail); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_MEDIA_Select); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_APP1); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_APP2); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_1); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Plus); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Comma); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Minus); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Period); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_2); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_3); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_4); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_5); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_6); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_7); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_8); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_102); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_ProcessKey); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Packet); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Attn); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Crsel); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Exsel); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Ereof); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Play); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Zoom); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NoName); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_PA1); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Clear); ++CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Unknown); ++ ++#undef CHECK_FWL_VKEY_ENUM____ ++ ++namespace { ++ ++Mask GetKeyFlags(Mask input) { ++ Mask results; ++ ++ if (input & FWL_EVENTFLAG_ControlKey) ++ results |= XFA_FWL_KeyFlag::kCtrl; ++ if (input & FWL_EVENTFLAG_LeftButtonDown) ++ results |= XFA_FWL_KeyFlag::kLButton; ++ if (input & FWL_EVENTFLAG_MiddleButtonDown) ++ results |= XFA_FWL_KeyFlag::kMButton; ++ if (input & FWL_EVENTFLAG_RightButtonDown) ++ results |= XFA_FWL_KeyFlag::kRButton; ++ if (input & FWL_EVENTFLAG_ShiftKey) ++ results |= XFA_FWL_KeyFlag::kShift; ++ if (input & FWL_EVENTFLAG_AltKey) ++ results |= XFA_FWL_KeyFlag::kAlt; ++ ++ return results; ++} ++ ++} // namespace + + CPDFXFA_Widget::CPDFXFA_Widget(CXFA_FFWidget* pXFAFFWidget, +- CPDFSDK_PageView* pPageView, +- CPDFSDK_InteractiveForm* pInteractiveForm) +- : CPDFSDK_Annot(pPageView), +- m_pInteractiveForm(pInteractiveForm), +- m_pXFAFFWidget(pXFAFFWidget) {} ++ CPDFSDK_PageView* pPageView) ++ : CPDFSDK_Annot(pPageView), m_pXFAFFWidget(pXFAFFWidget) {} + + CPDFXFA_Widget::~CPDFXFA_Widget() = default; + +@@ -22,10 +225,223 @@ CPDFXFA_Widget* CPDFXFA_Widget::AsXFAWidget() { + return this; + } + ++CPDFSDK_Annot::UnsafeInputHandlers* CPDFXFA_Widget::GetUnsafeInputHandlers() { ++ return this; ++} ++ + CPDF_Annot::Subtype CPDFXFA_Widget::GetAnnotSubtype() const { + return CPDF_Annot::Subtype::XFAWIDGET; + } + + CFX_FloatRect CPDFXFA_Widget::GetRect() const { +- return GetXFAFFWidget()->GetLayoutItem()->GetRect(false).ToFloatRect(); ++ return GetXFAFFWidget()->GetLayoutItem()->GetAbsoluteRect().ToFloatRect(); ++} ++ ++void CPDFXFA_Widget::OnDraw(CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device, ++ bool bDrawAnnots) { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ if (!widget_handler) ++ return; ++ ++ CFGAS_GEGraphics gs(pDevice); ++ bool is_highlight = GetPageView()->GetFormFillEnv()->GetFocusAnnot() != this; ++ widget_handler->RenderWidget(GetXFAFFWidget(), &gs, mtUser2Device, ++ is_highlight); ++ ++ // to do highlight and shadow ++} ++ ++bool CPDFXFA_Widget::DoHitTest(const CFX_PointF& point) { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ if (!widget_handler) ++ return false; ++ ++ return widget_handler->HitTest(GetXFAFFWidget(), point) != ++ FWL_WidgetHit::Unknown; ++} ++ ++bool CPDFXFA_Widget::OnChangedFocus() { ++ CXFA_FFDocView* doc_view = GetDocView(); ++ if (!doc_view) ++ return false; ++ ++ CXFA_FFWidget* widget = GetXFAFFWidget(); ++ if (doc_view->SetFocus(widget)) ++ return false; ++ ++ return doc_view->GetFocusWidget() != widget; ++} ++ ++CFX_FloatRect CPDFXFA_Widget::GetViewBBox() { ++ CXFA_FFWidget* widget = GetXFAFFWidget(); ++ CXFA_Node* node = widget->GetNode(); ++ DCHECK(node->IsWidgetReady()); ++ ++ CFX_RectF bbox = ++ widget->GetBBox(node->GetFFWidgetType() == XFA_FFWidgetType::kSignature ++ ? CXFA_FFWidget::kDrawFocus ++ : CXFA_FFWidget::kDoNotDrawFocus); ++ ++ CFX_FloatRect result = bbox.ToFloatRect(); ++ result.Inflate(1.0f, 1.0f); ++ return result; ++} ++ ++void CPDFXFA_Widget::OnMouseEnter(Mask nFlags) { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ if (widget_handler) ++ widget_handler->OnMouseEnter(GetXFAFFWidget()); ++} ++ ++void CPDFXFA_Widget::OnMouseExit(Mask nFlags) { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ if (widget_handler) ++ widget_handler->OnMouseExit(GetXFAFFWidget()); ++} ++ ++bool CPDFXFA_Widget::OnLButtonDown(Mask nFlags, ++ const CFX_PointF& point) { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ return widget_handler && widget_handler->OnLButtonDown( ++ GetXFAFFWidget(), GetKeyFlags(nFlags), point); ++} ++ ++bool CPDFXFA_Widget::OnLButtonUp(Mask nFlags, ++ const CFX_PointF& point) { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ return widget_handler && widget_handler->OnLButtonUp( ++ GetXFAFFWidget(), GetKeyFlags(nFlags), point); ++} ++ ++bool CPDFXFA_Widget::OnLButtonDblClk(Mask nFlags, ++ const CFX_PointF& point) { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ return widget_handler && widget_handler->OnLButtonDblClk( ++ GetXFAFFWidget(), GetKeyFlags(nFlags), point); ++} ++ ++bool CPDFXFA_Widget::OnMouseMove(Mask nFlags, ++ const CFX_PointF& point) { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ return widget_handler && widget_handler->OnMouseMove( ++ GetXFAFFWidget(), GetKeyFlags(nFlags), point); ++} ++ ++bool CPDFXFA_Widget::OnMouseWheel(Mask nFlags, ++ const CFX_PointF& point, ++ const CFX_Vector& delta) { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ return widget_handler && ++ widget_handler->OnMouseWheel(GetXFAFFWidget(), GetKeyFlags(nFlags), ++ point, delta); ++} ++ ++bool CPDFXFA_Widget::OnRButtonDown(Mask nFlags, ++ const CFX_PointF& point) { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ return widget_handler && widget_handler->OnRButtonDown( ++ GetXFAFFWidget(), GetKeyFlags(nFlags), point); ++} ++ ++bool CPDFXFA_Widget::OnRButtonUp(Mask nFlags, ++ const CFX_PointF& point) { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ return widget_handler && widget_handler->OnRButtonUp( ++ GetXFAFFWidget(), GetKeyFlags(nFlags), point); ++} ++ ++bool CPDFXFA_Widget::OnChar(uint32_t nChar, Mask nFlags) { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ return widget_handler && ++ widget_handler->OnChar(GetXFAFFWidget(), nChar, GetKeyFlags(nFlags)); ++} ++ ++bool CPDFXFA_Widget::OnKeyDown(FWL_VKEYCODE nKeyCode, ++ Mask nFlags) { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ return widget_handler && ++ widget_handler->OnKeyDown(GetXFAFFWidget(), ++ static_cast(nKeyCode), ++ GetKeyFlags(nFlags)); ++} ++ ++bool CPDFXFA_Widget::OnSetFocus(Mask nFlags) { ++ return true; ++} ++ ++bool CPDFXFA_Widget::OnKillFocus(Mask nFlags) { ++ CXFA_FFDocView* doc_view = GetDocView(); ++ if (doc_view) ++ doc_view->SetFocus(nullptr); ++ return true; ++} ++ ++bool CPDFXFA_Widget::CanUndo() { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ return widget_handler && widget_handler->CanUndo(GetXFAFFWidget()); ++} ++ ++bool CPDFXFA_Widget::CanRedo() { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ return widget_handler && widget_handler->CanRedo(GetXFAFFWidget()); ++} ++ ++bool CPDFXFA_Widget::Undo() { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ return widget_handler && widget_handler->Undo(GetXFAFFWidget()); ++} ++ ++bool CPDFXFA_Widget::Redo() { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ return widget_handler && widget_handler->Redo(GetXFAFFWidget()); ++} ++ ++WideString CPDFXFA_Widget::GetText() { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ if (!widget_handler) ++ return WideString(); ++ return widget_handler->GetText(GetXFAFFWidget()); ++} ++ ++WideString CPDFXFA_Widget::GetSelectedText() { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ if (!widget_handler) ++ return WideString(); ++ return widget_handler->GetSelectedText(GetXFAFFWidget()); ++} ++ ++void CPDFXFA_Widget::ReplaceAndKeepSelection(const WideString& text) { ++ // XFA does not seem to support IME input at all. Therefore we don't bother ++ // to keep selection for IMEs. ++ ReplaceSelection(text); ++} ++ ++void CPDFXFA_Widget::ReplaceSelection(const WideString& text) { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ if (widget_handler) ++ widget_handler->PasteText(GetXFAFFWidget(), text); ++} ++ ++bool CPDFXFA_Widget::SelectAllText() { ++ CXFA_FFWidgetHandler* widget_handler = GetWidgetHandler(); ++ return widget_handler && widget_handler->SelectAllText(GetXFAFFWidget()); ++} ++ ++bool CPDFXFA_Widget::SetIndexSelected(int index, bool selected) { ++ return false; ++} ++ ++bool CPDFXFA_Widget::IsIndexSelected(int index) { ++ return false; ++} ++ ++CXFA_FFDocView* CPDFXFA_Widget::GetDocView() { ++ CXFA_FFPageView* page_view = GetXFAFFWidget()->GetPageView(); ++ return page_view ? page_view->GetDocView() : nullptr; ++} ++ ++CXFA_FFWidgetHandler* CPDFXFA_Widget::GetWidgetHandler() { ++ CXFA_FFDocView* doc_view = GetDocView(); ++ return doc_view ? doc_view->GetWidgetHandler() : nullptr; + } +diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_widget.h b/fpdfsdk/fpdfxfa/cpdfxfa_widget.h +index 76d98785c..1a742130a 100644 +--- a/fpdfsdk/fpdfxfa/cpdfxfa_widget.h ++++ b/fpdfsdk/fpdfxfa/cpdfxfa_widget.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,35 +8,74 @@ + #define FPDFSDK_FPDFXFA_CPDFXFA_WIDGET_H_ + + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/observed_ptr.h" +-#include "core/fxcrt/unowned_ptr.h" + #include "fpdfsdk/cpdfsdk_annot.h" ++#include "v8/include/cppgc/persistent.h" + #include "xfa/fxfa/cxfa_ffwidget.h" + +-class CPDFSDK_InteractiveForm; + class CPDFSDK_PageView; ++class CXFA_FFDocView; ++class CXFA_FFWidgetHandler; + +-class CPDFXFA_Widget final : public CPDFSDK_Annot { ++class CPDFXFA_Widget final : public CPDFSDK_Annot, ++ CPDFSDK_Annot::UnsafeInputHandlers { + public: +- CPDFXFA_Widget(CXFA_FFWidget* pXFAFFWidget, +- CPDFSDK_PageView* pPageView, +- CPDFSDK_InteractiveForm* pInteractiveForm); ++ CPDFXFA_Widget(CXFA_FFWidget* pXFAFFWidget, CPDFSDK_PageView* pPageView); + ~CPDFXFA_Widget() override; + + // CPDFSDK_Annot: + CPDFXFA_Widget* AsXFAWidget() override; ++ CPDFSDK_Annot::UnsafeInputHandlers* GetUnsafeInputHandlers() override; + CPDF_Annot::Subtype GetAnnotSubtype() const override; + CFX_FloatRect GetRect() const override; ++ void OnDraw(CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device, ++ bool bDrawAnnots) override; ++ bool DoHitTest(const CFX_PointF& point) override; ++ CFX_FloatRect GetViewBBox() override; ++ bool CanUndo() override; ++ bool CanRedo() override; ++ bool Undo() override; ++ bool Redo() override; ++ WideString GetText() override; ++ WideString GetSelectedText() override; ++ void ReplaceAndKeepSelection(const WideString& text) override; ++ void ReplaceSelection(const WideString& text) override; ++ bool SelectAllText() override; ++ bool SetIndexSelected(int index, bool selected) override; ++ bool IsIndexSelected(int index) override; + + CXFA_FFWidget* GetXFAFFWidget() const { return m_pXFAFFWidget.Get(); } +- CPDFSDK_InteractiveForm* GetInteractiveForm() const { +- return m_pInteractiveForm.Get(); +- } ++ ++ bool OnChangedFocus(); + + private: +- UnownedPtr const m_pInteractiveForm; +- ObservedPtr const m_pXFAFFWidget; ++ // CPDFSDK_Annot::UnsafeInputHandlers: ++ void OnMouseEnter(Mask nFlags) override; ++ void OnMouseExit(Mask nFlags) override; ++ bool OnLButtonDown(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnLButtonUp(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnLButtonDblClk(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnMouseMove(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnMouseWheel(Mask nFlags, ++ const CFX_PointF& point, ++ const CFX_Vector& delta) override; ++ bool OnRButtonDown(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnRButtonUp(Mask nFlags, ++ const CFX_PointF& point) override; ++ bool OnChar(uint32_t nChar, Mask nFlags) override; ++ bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask nFlags) override; ++ bool OnSetFocus(Mask nFlags) override; ++ bool OnKillFocus(Mask nFlags) override; ++ ++ CXFA_FFDocView* GetDocView(); ++ CXFA_FFWidgetHandler* GetWidgetHandler(); ++ ++ cppgc::Persistent const m_pXFAFFWidget; + }; + + #endif // FPDFSDK_FPDFXFA_CPDFXFA_WIDGET_H_ +diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.cpp +deleted file mode 100644 +index 3012b0d66..000000000 +--- a/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.cpp ++++ /dev/null +@@ -1,667 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h" +- +-#include "fpdfsdk/cpdfsdk_annot.h" +-#include "fpdfsdk/cpdfsdk_formfillenvironment.h" +-#include "fpdfsdk/cpdfsdk_interactiveform.h" +-#include "fpdfsdk/cpdfsdk_pageview.h" +-#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h" +-#include "fpdfsdk/fpdfxfa/cpdfxfa_widget.h" +-#include "public/fpdf_fwlevent.h" +-#include "xfa/fwl/cfwl_app.h" +-#include "xfa/fwl/fwl_widgetdef.h" +-#include "xfa/fwl/fwl_widgethit.h" +-#include "xfa/fxfa/cxfa_ffdocview.h" +-#include "xfa/fxfa/cxfa_ffpageview.h" +-#include "xfa/fxfa/cxfa_ffwidget.h" +-#include "xfa/fxfa/cxfa_ffwidgethandler.h" +-#include "xfa/fxfa/fxfa_basic.h" +-#include "xfa/fxfa/parser/cxfa_node.h" +-#include "xfa/fxgraphics/cxfa_graphics.h" +- +-#define CHECK_FWL_VKEY_ENUM____(name) \ +- static_assert(static_cast(name) == static_cast(XFA_##name), \ +- "FWL_VKEYCODE enum mismatch") +- +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Back); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Tab); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NewLine); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Clear); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Return); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Shift); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Control); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Menu); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Pause); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Capital); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Kana); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Hangul); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Junja); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Final); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Hanja); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Kanji); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Escape); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Convert); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NonConvert); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Accept); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_ModeChange); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Space); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Prior); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Next); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_End); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Home); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Left); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Up); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Right); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Down); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Select); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Print); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Execute); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Snapshot); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Insert); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Delete); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Help); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_0); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_1); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_2); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_3); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_4); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_5); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_6); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_7); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_8); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_9); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_A); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_B); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_C); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_D); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_E); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_G); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_H); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_I); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_J); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_K); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_L); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_M); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_N); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_O); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_P); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Q); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_R); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_S); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_T); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_U); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_V); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_W); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_X); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Y); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Z); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LWin); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Command); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RWin); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Apps); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Sleep); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad0); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad1); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad2); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad3); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad4); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad5); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad6); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad7); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad8); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NumPad9); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Multiply); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Add); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Separator); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Subtract); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Decimal); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Divide); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F1); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F2); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F3); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F4); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F5); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F6); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F7); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F8); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F9); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F10); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F11); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F12); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F13); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F14); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F15); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F16); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F17); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F18); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F19); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F20); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F21); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F22); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F23); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_F24); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NunLock); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Scroll); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LShift); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RShift); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LControl); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RControl); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_LMenu); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_RMenu); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Back); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Forward); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Refresh); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Stop); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Search); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Favorites); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_BROWSER_Home); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_VOLUME_Mute); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_VOLUME_Down); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_VOLUME_Up); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_NEXT_Track); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_PREV_Track); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_Stop); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_PLAY_Pause); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_Mail); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_MEDIA_Select); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_APP1); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_MEDIA_LAUNCH_APP2); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_1); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Plus); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Comma); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Minus); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Period); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_2); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_3); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_4); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_5); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_6); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_7); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_8); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_102); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_ProcessKey); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Packet); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Attn); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Crsel); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Exsel); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Ereof); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Play); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Zoom); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_NoName); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_PA1); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_OEM_Clear); +-CHECK_FWL_VKEY_ENUM____(FWL_VKEY_Unknown); +- +-#undef CHECK_FWL_VKEY_ENUM____ +- +-CPDFXFA_WidgetHandler::CPDFXFA_WidgetHandler() = default; +- +-CPDFXFA_WidgetHandler::~CPDFXFA_WidgetHandler() = default; +- +-void CPDFXFA_WidgetHandler::SetFormFillEnvironment( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- m_pFormFillEnv = pFormFillEnv; +-} +- +-bool CPDFXFA_WidgetHandler::CanAnswer(CPDFSDK_Annot* pAnnot) { +- CPDFXFA_Widget* pWidget = ToXFAWidget(pAnnot); +- return pWidget && pWidget->GetXFAFFWidget(); +-} +- +-CPDFSDK_Annot* CPDFXFA_WidgetHandler::NewAnnot(CPDF_Annot* pAnnot, +- CPDFSDK_PageView* pPage) { +- return nullptr; +-} +- +-std::unique_ptr CPDFXFA_WidgetHandler::NewAnnotForXFA( +- CXFA_FFWidget* pAnnot, +- CPDFSDK_PageView* pPage) { +- CPDFSDK_InteractiveForm* pForm = m_pFormFillEnv->GetInteractiveForm(); +- return pdfium::MakeUnique(pAnnot, pPage, pForm); +-} +- +-void CPDFXFA_WidgetHandler::OnDraw(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device, +- bool bDrawAnnots) { +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot); +- ASSERT(pXFAWidget); +- +- bool bIsHighlight = false; +- if (pPageView->GetFormFillEnv()->GetFocusAnnot() != pAnnot) +- bIsHighlight = true; +- +- CXFA_Graphics gs(pDevice); +- GetXFAFFWidgetHandler(pXFAWidget) +- ->RenderWidget(pXFAWidget->GetXFAFFWidget(), &gs, mtUser2Device, +- bIsHighlight); +- +- // to do highlight and shadow +-} +- +-void CPDFXFA_WidgetHandler::OnLoad(CPDFSDK_Annot* pAnnot) {} +- +-void CPDFXFA_WidgetHandler::ReleaseAnnot( +- std::unique_ptr pAnnot) {} +- +-CFX_FloatRect CPDFXFA_WidgetHandler::GetViewBBox(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot) { +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot); +- CXFA_Node* node = pXFAWidget->GetXFAFFWidget()->GetNode(); +- ASSERT(node->IsWidgetReady()); +- +- CFX_RectF rcBBox = pXFAWidget->GetXFAFFWidget()->GetBBox( +- node->GetFFWidgetType() == XFA_FFWidgetType::kSignature +- ? CXFA_FFWidget::kDrawFocus +- : CXFA_FFWidget::kDoNotDrawFocus); +- +- CFX_FloatRect rcWidget = rcBBox.ToFloatRect(); +- rcWidget.Inflate(1.0f, 1.0f); +- return rcWidget; +-} +- +-WideString CPDFXFA_WidgetHandler::GetText(CPDFSDK_Annot* pAnnot) { +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot); +- if (!pXFAWidget) +- return WideString(); +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->GetText(pXFAWidget->GetXFAFFWidget()); +-} +- +-WideString CPDFXFA_WidgetHandler::GetSelectedText(CPDFSDK_Annot* pAnnot) { +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot); +- if (!pXFAWidget) +- return WideString(); +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->GetSelectedText(pXFAWidget->GetXFAFFWidget()); +-} +- +-void CPDFXFA_WidgetHandler::ReplaceSelection(CPDFSDK_Annot* pAnnot, +- const WideString& text) { +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot); +- if (!pXFAWidget) +- return; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->PasteText(pXFAWidget->GetXFAFFWidget(), text); +-} +- +-bool CPDFXFA_WidgetHandler::CanUndo(CPDFSDK_Annot* pAnnot) { +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot); +- if (!pXFAWidget) +- return false; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->CanUndo(pXFAWidget->GetXFAFFWidget()); +-} +- +-bool CPDFXFA_WidgetHandler::CanRedo(CPDFSDK_Annot* pAnnot) { +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot); +- if (!pXFAWidget) +- return false; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->CanRedo(pXFAWidget->GetXFAFFWidget()); +-} +- +-bool CPDFXFA_WidgetHandler::Undo(CPDFSDK_Annot* pAnnot) { +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot); +- if (!pXFAWidget) +- return false; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->Undo(pXFAWidget->GetXFAFFWidget()); +-} +- +-bool CPDFXFA_WidgetHandler::Redo(CPDFSDK_Annot* pAnnot) { +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot); +- if (!pXFAWidget) +- return false; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->Redo(pXFAWidget->GetXFAFFWidget()); +-} +- +-bool CPDFXFA_WidgetHandler::HitTest(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- const CFX_PointF& point) { +- if (!pPageView) +- return false; +- +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot); +- if (!pXFAWidget) +- return false; +- +- CPDFSDK_FormFillEnvironment* pFormFillEnv = pPageView->GetFormFillEnv(); +- if (!pFormFillEnv) +- return false; +- +- auto* pContext = +- static_cast(pFormFillEnv->GetDocExtension()); +- if (!pContext) +- return false; +- +- CXFA_FFDocView* pDocView = pContext->GetXFADocView(); +- if (!pDocView) +- return false; +- +- CXFA_FFWidgetHandler* pWidgetHandler = pDocView->GetWidgetHandler(); +- return pWidgetHandler && +- pWidgetHandler->HitTest(pXFAWidget->GetXFAFFWidget(), point) != +- FWL_WidgetHit::Unknown; +-} +- +-void CPDFXFA_WidgetHandler::OnMouseEnter(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) { +- if (!pPageView) +- return; +- +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get()); +- if (!pXFAWidget) +- return; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- pWidgetHandler->OnMouseEnter(pXFAWidget->GetXFAFFWidget()); +-} +- +-void CPDFXFA_WidgetHandler::OnMouseExit(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) { +- if (!pPageView) +- return; +- +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get()); +- if (!pXFAWidget) +- return; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- pWidgetHandler->OnMouseExit(pXFAWidget->GetXFAFFWidget()); +-} +- +-bool CPDFXFA_WidgetHandler::OnLButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- if (!pPageView) +- return false; +- +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get()); +- if (!pXFAWidget) +- return false; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->OnLButtonDown(pXFAWidget->GetXFAFFWidget(), +- GetFWLFlags(nFlags), point); +-} +- +-bool CPDFXFA_WidgetHandler::OnLButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- if (!pPageView) +- return false; +- +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get()); +- if (!pXFAWidget) +- return false; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->OnLButtonUp(pXFAWidget->GetXFAFFWidget(), +- GetFWLFlags(nFlags), point); +-} +- +-bool CPDFXFA_WidgetHandler::OnLButtonDblClk(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- if (!pPageView) +- return false; +- +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get()); +- if (!pXFAWidget) +- return false; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->OnLButtonDblClk(pXFAWidget->GetXFAFFWidget(), +- GetFWLFlags(nFlags), point); +-} +- +-bool CPDFXFA_WidgetHandler::OnMouseMove(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- if (!pPageView) +- return false; +- +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get()); +- if (!pXFAWidget) +- return false; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->OnMouseMove(pXFAWidget->GetXFAFFWidget(), +- GetFWLFlags(nFlags), point); +-} +- +-bool CPDFXFA_WidgetHandler::OnMouseWheel(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- short zDelta, +- const CFX_PointF& point) { +- if (!pPageView) +- return false; +- +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get()); +- if (!pXFAWidget) +- return false; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->OnMouseWheel(pXFAWidget->GetXFAFFWidget(), +- GetFWLFlags(nFlags), zDelta, point); +-} +- +-bool CPDFXFA_WidgetHandler::OnRButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- if (!pPageView) +- return false; +- +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get()); +- if (!pXFAWidget) +- return false; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->OnRButtonDown(pXFAWidget->GetXFAFFWidget(), +- GetFWLFlags(nFlags), point); +-} +- +-bool CPDFXFA_WidgetHandler::OnRButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- if (!pPageView) +- return false; +- +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get()); +- if (!pXFAWidget) +- return false; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->OnRButtonUp(pXFAWidget->GetXFAFFWidget(), +- GetFWLFlags(nFlags), point); +-} +- +-bool CPDFXFA_WidgetHandler::OnRButtonDblClk(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) { +- if (!pPageView) +- return false; +- +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get()); +- if (!pXFAWidget) +- return false; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->OnRButtonDblClk(pXFAWidget->GetXFAFFWidget(), +- GetFWLFlags(nFlags), point); +-} +- +-bool CPDFXFA_WidgetHandler::OnChar(CPDFSDK_Annot* pAnnot, +- uint32_t nChar, +- uint32_t nFlags) { +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot); +- if (!pXFAWidget) +- return false; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->OnChar(pXFAWidget->GetXFAFFWidget(), nChar, +- GetFWLFlags(nFlags)); +-} +- +-bool CPDFXFA_WidgetHandler::OnKeyDown(CPDFSDK_Annot* pAnnot, +- int nKeyCode, +- int nFlag) { +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot); +- if (!pXFAWidget) +- return false; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->OnKeyDown(pXFAWidget->GetXFAFFWidget(), nKeyCode, +- GetFWLFlags(nFlag)); +-} +- +-bool CPDFXFA_WidgetHandler::OnKeyUp(CPDFSDK_Annot* pAnnot, +- int nKeyCode, +- int nFlag) { +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot); +- if (!pXFAWidget) +- return false; +- +- CXFA_FFWidgetHandler* pWidgetHandler = GetXFAFFWidgetHandler(pXFAWidget); +- return pWidgetHandler->OnKeyUp(pXFAWidget->GetXFAFFWidget(), nKeyCode, +- GetFWLFlags(nFlag)); +-} +- +-bool CPDFXFA_WidgetHandler::OnSetFocus(ObservedPtr* pAnnot, +- uint32_t nFlag) { +- return true; +-} +- +-bool CPDFXFA_WidgetHandler::OnKillFocus(ObservedPtr* pAnnot, +- uint32_t nFlag) { +- CPDFXFA_Widget* pXFAWidget = ToXFAWidget(pAnnot->Get()); +- if (!pXFAWidget) +- return true; +- +- CXFA_FFWidget* hWidget = pXFAWidget->GetXFAFFWidget(); +- if (!hWidget) +- return true; +- +- CXFA_FFPageView* pXFAPageView = hWidget->GetPageView(); +- if (!pXFAPageView) +- return true; +- +- pXFAPageView->GetDocView()->SetFocus(nullptr); +- return true; +-} +- +-bool CPDFXFA_WidgetHandler::OnXFAChangedFocus( +- ObservedPtr* pOldAnnot, +- ObservedPtr* pNewAnnot) { +- CXFA_FFWidgetHandler* pWidgetHandler = nullptr; +- if (pOldAnnot->HasObservable()) +- pWidgetHandler = GetXFAFFWidgetHandler(pOldAnnot->Get()); +- else if (pNewAnnot->HasObservable()) +- pWidgetHandler = GetXFAFFWidgetHandler(pNewAnnot->Get()); +- +- if (!pWidgetHandler) +- return true; +- +- CPDFXFA_Widget* pNewXFAWidget = ToXFAWidget(pNewAnnot->Get()); +- if (!pNewXFAWidget) +- return true; +- +- CXFA_FFWidget* hWidget = pNewXFAWidget->GetXFAFFWidget(); +- if (!hWidget) +- return true; +- +- CXFA_FFPageView* pXFAPageView = hWidget->GetPageView(); +- if (!pXFAPageView) +- return true; +- +- ObservedPtr pObservedXFAPageView(pXFAPageView); +- bool bRet = pXFAPageView->GetDocView()->SetFocus(hWidget); +- +- // Check |pXFAPageView| again because |SetFocus| can trigger JS to destroy it. +- if (pObservedXFAPageView && +- pXFAPageView->GetDocView()->GetFocusWidget() == hWidget) { +- bRet = true; +- } +- +- return bRet; +-} +- +-bool CPDFXFA_WidgetHandler::SetIndexSelected(ObservedPtr* pAnnot, +- int index, +- bool selected) { +- return false; +-} +- +-bool CPDFXFA_WidgetHandler::IsIndexSelected(ObservedPtr* pAnnot, +- int index) { +- return false; +-} +- +-CXFA_FFWidgetHandler* CPDFXFA_WidgetHandler::GetXFAFFWidgetHandler( +- CPDFSDK_Annot* pAnnot) { +- if (!pAnnot) +- return nullptr; +- +- CPDFSDK_PageView* pPageView = pAnnot->GetPageView(); +- if (!pPageView) +- return nullptr; +- +- CPDFSDK_FormFillEnvironment* pFormFillEnv = pPageView->GetFormFillEnv(); +- if (!pFormFillEnv) +- return nullptr; +- +- auto* pDoc = static_cast(pFormFillEnv->GetDocExtension()); +- if (!pDoc) +- return nullptr; +- +- CXFA_FFDocView* pDocView = pDoc->GetXFADocView(); +- if (!pDocView) +- return nullptr; +- +- return pDocView->GetWidgetHandler(); +-} +- +-uint32_t CPDFXFA_WidgetHandler::GetFWLFlags(uint32_t dwFlag) { +- uint32_t dwFWLFlag = 0; +- +- if (dwFlag & FWL_EVENTFLAG_ControlKey) +- dwFWLFlag |= FWL_KEYFLAG_Ctrl; +- if (dwFlag & FWL_EVENTFLAG_LeftButtonDown) +- dwFWLFlag |= FWL_KEYFLAG_LButton; +- if (dwFlag & FWL_EVENTFLAG_MiddleButtonDown) +- dwFWLFlag |= FWL_KEYFLAG_MButton; +- if (dwFlag & FWL_EVENTFLAG_RightButtonDown) +- dwFWLFlag |= FWL_KEYFLAG_RButton; +- if (dwFlag & FWL_EVENTFLAG_ShiftKey) +- dwFWLFlag |= FWL_KEYFLAG_Shift; +- if (dwFlag & FWL_EVENTFLAG_AltKey) +- dwFWLFlag |= FWL_KEYFLAG_Alt; +- +- return dwFWLFlag; +-} +diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h b/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h +deleted file mode 100644 +index e18e0cad1..000000000 +--- a/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.h ++++ /dev/null +@@ -1,115 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef FPDFSDK_FPDFXFA_CPDFXFA_WIDGETHANDLER_H_ +-#define FPDFSDK_FPDFXFA_CPDFXFA_WIDGETHANDLER_H_ +- +-#include +- +-#include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/unowned_ptr.h" +-#include "fpdfsdk/ipdfsdk_annothandler.h" +- +-class CFX_Matrix; +-class CFX_RenderDevice; +-class CPDF_Annot; +-class CPDFSDK_FormFillEnvironment; +-class CPDFSDK_Annot; +-class CPDFSDK_PageView; +-class CXFA_FFWidget; +-class CXFA_FFWidgetHandler; +- +-class CPDFXFA_WidgetHandler final : public IPDFSDK_AnnotHandler { +- public: +- CPDFXFA_WidgetHandler(); +- ~CPDFXFA_WidgetHandler() override; +- +- // IPDFSDK_AnnotHandler: +- void SetFormFillEnvironment( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) override; +- bool CanAnswer(CPDFSDK_Annot* pAnnot) override; +- CPDFSDK_Annot* NewAnnot(CPDF_Annot* pAnnot, CPDFSDK_PageView* pPage) override; +- void ReleaseAnnot(std::unique_ptr pAnnot) override; +- CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot) override; +- WideString GetText(CPDFSDK_Annot* pAnnot) override; +- WideString GetSelectedText(CPDFSDK_Annot* pAnnot) override; +- void ReplaceSelection(CPDFSDK_Annot* pAnnot, const WideString& text) override; +- bool CanUndo(CPDFSDK_Annot* pAnnot) override; +- bool CanRedo(CPDFSDK_Annot* pAnnot) override; +- bool Undo(CPDFSDK_Annot* pAnnot) override; +- bool Redo(CPDFSDK_Annot* pAnnot) override; +- bool HitTest(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- const CFX_PointF& point) override; +- void OnDraw(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device, +- bool bDrawAnnots) override; +- void OnLoad(CPDFSDK_Annot* pAnnot) override; +- void OnMouseEnter(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) override; +- void OnMouseExit(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) override; +- bool OnLButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnLButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnLButtonDblClk(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnMouseMove(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnMouseWheel(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- short zDelta, +- const CFX_PointF& point) override; +- bool OnRButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnRButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnRButtonDblClk(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) override; +- bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags) override; +- bool OnKeyDown(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override; +- bool OnKeyUp(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) override; +- bool OnSetFocus(ObservedPtr* pAnnot, uint32_t nFlag) override; +- bool OnKillFocus(ObservedPtr* pAnnot, uint32_t nFlag) override; +- bool SetIndexSelected(ObservedPtr* pAnnot, +- int index, +- bool selected) override; +- bool IsIndexSelected(ObservedPtr* pAnnot, int index) override; +- +- std::unique_ptr NewAnnotForXFA(CXFA_FFWidget* pAnnot, +- CPDFSDK_PageView* pPage); +- bool OnXFAChangedFocus(ObservedPtr* pOldAnnot, +- ObservedPtr* pNewAnnot); +- +- private: +- CXFA_FFWidgetHandler* GetXFAFFWidgetHandler(CPDFSDK_Annot* pAnnot); +- uint32_t GetFWLFlags(uint32_t dwFlag); +- +- UnownedPtr m_pFormFillEnv; +-}; +- +-#endif // FPDFSDK_FPDFXFA_CPDFXFA_WIDGETHANDLER_H_ +diff --git a/fpdfsdk/ipdfsdk_annothandler.h b/fpdfsdk/ipdfsdk_annothandler.h +deleted file mode 100644 +index 24b6ff044..000000000 +--- a/fpdfsdk/ipdfsdk_annothandler.h ++++ /dev/null +@@ -1,105 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef FPDFSDK_IPDFSDK_ANNOTHANDLER_H_ +-#define FPDFSDK_IPDFSDK_ANNOTHANDLER_H_ +- +-#include +- +-#include "core/fxcrt/fx_coordinates.h" +-#include "fpdfsdk/cpdfsdk_annot.h" +- +-class CFX_Matrix; +-class CFX_RenderDevice; +-class CPDF_Annot; +-class CPDFSDK_FormFillEnvironment; +-class CPDFSDK_PageView; +- +-class IPDFSDK_AnnotHandler { +- public: +- virtual ~IPDFSDK_AnnotHandler() = default; +- +- virtual void SetFormFillEnvironment( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0; +- virtual bool CanAnswer(CPDFSDK_Annot* pAnnot) = 0; +- virtual CPDFSDK_Annot* NewAnnot(CPDF_Annot* pAnnot, +- CPDFSDK_PageView* pPage) = 0; +- virtual void ReleaseAnnot(std::unique_ptr pAnnot) = 0; +- virtual CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot) = 0; +- virtual WideString GetText(CPDFSDK_Annot* pAnnot) = 0; +- virtual WideString GetSelectedText(CPDFSDK_Annot* pAnnot) = 0; +- virtual void ReplaceSelection(CPDFSDK_Annot* pAnnot, +- const WideString& text) = 0; +- virtual bool CanUndo(CPDFSDK_Annot* pAnnot) = 0; +- virtual bool CanRedo(CPDFSDK_Annot* pAnnot) = 0; +- virtual bool Undo(CPDFSDK_Annot* pAnnot) = 0; +- virtual bool Redo(CPDFSDK_Annot* pAnnot) = 0; +- virtual bool HitTest(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- const CFX_PointF& point) = 0; +- virtual void OnDraw(CPDFSDK_PageView* pPageView, +- CPDFSDK_Annot* pAnnot, +- CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device, +- bool bDrawAnnots) = 0; +- virtual void OnLoad(CPDFSDK_Annot* pAnnot) = 0; +- virtual void OnMouseEnter(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) = 0; +- virtual void OnMouseExit(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlag) = 0; +- virtual bool OnLButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) = 0; +- virtual bool OnLButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) = 0; +- virtual bool OnLButtonDblClk(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) = 0; +- virtual bool OnMouseMove(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) = 0; +- virtual bool OnMouseWheel(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- short zDelta, +- const CFX_PointF& point) = 0; +- virtual bool OnRButtonDown(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) = 0; +- virtual bool OnRButtonUp(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) = 0; +- virtual bool OnRButtonDblClk(CPDFSDK_PageView* pPageView, +- ObservedPtr* pAnnot, +- uint32_t nFlags, +- const CFX_PointF& point) = 0; +- virtual bool OnChar(CPDFSDK_Annot* pAnnot, +- uint32_t nChar, +- uint32_t nFlags) = 0; +- virtual bool OnKeyDown(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) = 0; +- virtual bool OnKeyUp(CPDFSDK_Annot* pAnnot, int nKeyCode, int nFlag) = 0; +- virtual bool OnSetFocus(ObservedPtr* pAnnot, +- uint32_t nFlag) = 0; +- virtual bool OnKillFocus(ObservedPtr* pAnnot, +- uint32_t nFlag) = 0; +- virtual bool SetIndexSelected(ObservedPtr* pAnnot, +- int index, +- bool selected) = 0; +- virtual bool IsIndexSelected(ObservedPtr* pAnnot, +- int index) = 0; +-}; +- +-#endif // FPDFSDK_IPDFSDK_ANNOTHANDLER_H_ +diff --git a/fpdfsdk/pwl/Android.bp b/fpdfsdk/pwl/Android.bp +index 74fc563a7..e02cc17bc 100644 +--- a/fpdfsdk/pwl/Android.bp ++++ b/fpdfsdk/pwl/Android.bp +@@ -13,11 +13,8 @@ cc_library_static { + + visibility: ["//external/pdfium:__subpackages__"], + +- header_libs: [ +- "libpdfium-constants", +- ], +- + static_libs: [ ++ "libpdfium-constants", + "libpdfium-font", + "libpdfium-render", + "libpdfium-fpdfdoc", +diff --git a/fpdfsdk/pwl/BUILD.gn b/fpdfsdk/pwl/BUILD.gn +index ea26c1342..e1722a8b1 100644 +--- a/fpdfsdk/pwl/BUILD.gn ++++ b/fpdfsdk/pwl/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -11,29 +11,34 @@ source_set("pwl") { + "cpwl_button.h", + "cpwl_caret.cpp", + "cpwl_caret.h", ++ "cpwl_cbbutton.cpp", ++ "cpwl_cbbutton.h", ++ "cpwl_cblistbox.cpp", ++ "cpwl_cblistbox.h", + "cpwl_combo_box.cpp", + "cpwl_combo_box.h", + "cpwl_edit.cpp", + "cpwl_edit.h", +- "cpwl_edit_ctrl.cpp", +- "cpwl_edit_ctrl.h", + "cpwl_edit_impl.cpp", + "cpwl_edit_impl.h", +- "cpwl_icon.cpp", +- "cpwl_icon.h", + "cpwl_list_box.cpp", + "cpwl_list_box.h", +- "cpwl_list_impl.cpp", +- "cpwl_list_impl.h", ++ "cpwl_list_ctrl.cpp", ++ "cpwl_list_ctrl.h", ++ "cpwl_sbbutton.cpp", ++ "cpwl_sbbutton.h", + "cpwl_scroll_bar.cpp", + "cpwl_scroll_bar.h", + "cpwl_special_button.cpp", + "cpwl_special_button.h", + "cpwl_wnd.cpp", + "cpwl_wnd.h", +- "ipwl_systemhandler.h", ++ "ipwl_fillernotify.h", ++ ] ++ configs += [ ++ "../../:pdfium_strict_config", ++ "../../:pdfium_noshorten_config", + ] +- configs += [ "../../:pdfium_core_config" ] + deps = [ + "../../:pdfium_public_headers", + "../../constants", +@@ -46,12 +51,31 @@ source_set("pwl") { + visibility = [ "../../*" ] + } + +-pdfium_embeddertest_source_set("embeddertests") { ++source_set("embedder_test_support") { ++ testonly = true + sources = [ + "cpwl_combo_box_embeddertest.cpp", ++ "cpwl_combo_box_embeddertest.h", ++ ] ++ configs += [ "../../:pdfium_strict_config" ] ++ deps = [ ++ ":pwl", ++ "../:fpdfsdk", ++ "../../:pdfium_public_headers", ++ "../../testing:embedder_test_support", ++ "../formfiller", ++ "//testing/gtest", ++ ] ++} ++ ++pdfium_embeddertest_source_set("embeddertests") { ++ sources = [ ++ "cpwl_combo_box_edit_embeddertest.cpp", + "cpwl_edit_embeddertest.cpp", ++ "cpwl_special_button_embeddertest.cpp", + ] + deps = [ ++ ":embedder_test_support", + ":pwl", + "../:fpdfsdk", + "../formfiller", +diff --git a/fpdfsdk/pwl/README.md b/fpdfsdk/pwl/README.md +index d9be3b129..9381b5f27 100644 +--- a/fpdfsdk/pwl/README.md ++++ b/fpdfsdk/pwl/README.md +@@ -11,7 +11,6 @@ classes are controllers for each widget. The hierarchy is: + * CPWL_Caret + * CPWL_EditCtrl + * CPWL_Edit +- * CPWL_Icon + * CPWL_ListBox + * CPWL_CBListBox (combo box) + * CPWL_ScrollBar +diff --git a/fpdfsdk/pwl/cpwl_button.cpp b/fpdfsdk/pwl/cpwl_button.cpp +index b81afae89..7b1cd936b 100644 +--- a/fpdfsdk/pwl/cpwl_button.cpp ++++ b/fpdfsdk/pwl/cpwl_button.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,22 +10,24 @@ + + CPWL_Button::CPWL_Button( + const CreateParams& cp, +- std::unique_ptr pAttachedData) ++ std::unique_ptr pAttachedData) + : CPWL_Wnd(cp, std::move(pAttachedData)) { +- GetCreationParams()->eCursorType = FXCT_HAND; ++ GetCreationParams()->eCursorType = IPWL_FillerNotify::CursorStyle::kHand; + } + + CPWL_Button::~CPWL_Button() = default; + +-bool CPWL_Button::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnLButtonDown(point, nFlag); ++bool CPWL_Button::OnLButtonDown(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnLButtonDown(nFlag, point); + m_bMouseDown = true; + SetCapture(); + return true; + } + +-bool CPWL_Button::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnLButtonUp(point, nFlag); ++bool CPWL_Button::OnLButtonUp(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnLButtonUp(nFlag, point); + ReleaseCapture(); + m_bMouseDown = false; + return true; +diff --git a/fpdfsdk/pwl/cpwl_button.h b/fpdfsdk/pwl/cpwl_button.h +index e7760dd85..56dbe6c36 100644 +--- a/fpdfsdk/pwl/cpwl_button.h ++++ b/fpdfsdk/pwl/cpwl_button.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,17 +10,18 @@ + #include + + #include "fpdfsdk/pwl/cpwl_wnd.h" +-#include "fpdfsdk/pwl/ipwl_systemhandler.h" ++#include "fpdfsdk/pwl/ipwl_fillernotify.h" + + class CPWL_Button : public CPWL_Wnd { + public: + CPWL_Button(const CreateParams& cp, +- std::unique_ptr pAttachedData); ++ std::unique_ptr pAttachedData); + ~CPWL_Button() override; + + // CPWL_Wnd +- bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override; +- bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; ++ bool OnLButtonDown(Mask nFlag, ++ const CFX_PointF& point) override; ++ bool OnLButtonUp(Mask nFlag, const CFX_PointF& point) override; + + protected: + bool m_bMouseDown = false; +diff --git a/fpdfsdk/pwl/cpwl_caret.cpp b/fpdfsdk/pwl/cpwl_caret.cpp +index 739fd3704..fe293886c 100644 +--- a/fpdfsdk/pwl/cpwl_caret.cpp ++++ b/fpdfsdk/pwl/cpwl_caret.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,14 +9,14 @@ + #include + #include + ++#include "core/fxge/cfx_fillrenderoptions.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/cfx_renderdevice.h" +-#include "third_party/base/ptr_util.h" + + CPWL_Caret::CPWL_Caret( + const CreateParams& cp, +- std::unique_ptr pAttachedData) ++ std::unique_ptr pAttachedData) + : CPWL_Wnd(cp, std::move(pAttachedData)) {} + + CPWL_Caret::~CPWL_Caret() = default; +@@ -28,7 +28,6 @@ void CPWL_Caret::DrawThisAppearance(CFX_RenderDevice* pDevice, + + CFX_FloatRect rcRect = GetCaretRect(); + CFX_FloatRect rcClip = GetClipRect(); +- CFX_PathData path; + + float fCaretX = rcRect.left + m_fWidth * 0.5f; + float fCaretTop = rcRect.top; +@@ -42,13 +41,16 @@ void CPWL_Caret::DrawThisAppearance(CFX_RenderDevice* pDevice, + fCaretBottom = rcRect.bottom; + } + +- path.AppendPoint(CFX_PointF(fCaretX, fCaretBottom), FXPT_TYPE::MoveTo, false); +- path.AppendPoint(CFX_PointF(fCaretX, fCaretTop), FXPT_TYPE::LineTo, false); ++ CFX_Path path; ++ path.AppendPoint(CFX_PointF(fCaretX, fCaretBottom), ++ CFX_Path::Point::Type::kMove); ++ path.AppendPoint(CFX_PointF(fCaretX, fCaretTop), ++ CFX_Path::Point::Type::kLine); + + CFX_GraphStateData gsd; + gsd.m_LineWidth = m_fWidth; +- pDevice->DrawPath(&path, &mtUser2Device, &gsd, 0, ArgbEncode(255, 0, 0, 0), +- FXFILL_ALTERNATE); ++ pDevice->DrawPath(path, &mtUser2Device, &gsd, 0, ArgbEncode(255, 0, 0, 0), ++ CFX_FillRenderOptions::EvenOddOptions()); + } + + void CPWL_Caret::OnTimerFired() { +@@ -85,8 +87,8 @@ void CPWL_Caret::SetCaret(bool bVisible, + + m_ptHead = ptHead; + m_ptFoot = ptFoot; +- m_pTimer = pdfium::MakeUnique(GetTimerHandler(), this, +- kCaretFlashIntervalMs); ++ m_pTimer = std::make_unique(GetTimerHandler(), this, ++ kCaretFlashIntervalMs); + + if (!CPWL_Wnd::SetVisible(true)) + return; +@@ -109,10 +111,9 @@ void CPWL_Caret::SetCaret(bool bVisible, + // needs to be done, check the return value of Move(). + } + +-bool CPWL_Caret::InvalidateRect(CFX_FloatRect* pRect) { +- if (!pRect) { ++bool CPWL_Caret::InvalidateRect(const CFX_FloatRect* pRect) { ++ if (!pRect) + return CPWL_Wnd::InvalidateRect(nullptr); +- } + + CFX_FloatRect rcRefresh = *pRect; + if (!rcRefresh.IsEmpty()) { +diff --git a/fpdfsdk/pwl/cpwl_caret.h b/fpdfsdk/pwl/cpwl_caret.h +index 71788efb1..c3e584146 100644 +--- a/fpdfsdk/pwl/cpwl_caret.h ++++ b/fpdfsdk/pwl/cpwl_caret.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -15,13 +15,13 @@ + class CPWL_Caret final : public CPWL_Wnd, public CFX_Timer::CallbackIface { + public: + CPWL_Caret(const CreateParams& cp, +- std::unique_ptr pAttachedData); ++ std::unique_ptr pAttachedData); + ~CPWL_Caret() override; + + // CPWL_Wnd: + void DrawThisAppearance(CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device) override; +- bool InvalidateRect(CFX_FloatRect* pRect) override; ++ bool InvalidateRect(const CFX_FloatRect* pRect) override; + bool SetVisible(bool bVisible) override; + + // CFX_Timer::CallbackIface: +diff --git a/fpdfsdk/pwl/cpwl_cbbutton.cpp b/fpdfsdk/pwl/cpwl_cbbutton.cpp +new file mode 100644 +index 000000000..de248ce55 +--- /dev/null ++++ b/fpdfsdk/pwl/cpwl_cbbutton.cpp +@@ -0,0 +1,79 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "fpdfsdk/pwl/cpwl_cbbutton.h" ++ ++#include ++ ++#include "core/fxge/cfx_fillrenderoptions.h" ++#include "core/fxge/cfx_path.h" ++#include "core/fxge/cfx_renderdevice.h" ++ ++CPWL_CBButton::CPWL_CBButton( ++ const CreateParams& cp, ++ std::unique_ptr pAttachedData) ++ : CPWL_Wnd(cp, std::move(pAttachedData)) {} ++ ++CPWL_CBButton::~CPWL_CBButton() = default; ++ ++void CPWL_CBButton::DrawThisAppearance(CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device) { ++ CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device); ++ ++ if (!IsVisible()) ++ return; ++ ++ CFX_FloatRect window = CPWL_Wnd::GetWindowRect(); ++ if (window.IsEmpty()) ++ return; ++ ++ constexpr float kComboBoxTriangleLength = 6.0f; ++ constexpr float kComboBoxTriangleHalfLength = kComboBoxTriangleLength / 2; ++ constexpr float kComboBoxTriangleQuarterLength = kComboBoxTriangleLength / 4; ++ if (!FXSYS_IsFloatBigger(window.right - window.left, ++ kComboBoxTriangleLength) || ++ !FXSYS_IsFloatBigger(window.top - window.bottom, ++ kComboBoxTriangleHalfLength)) { ++ return; ++ } ++ ++ CFX_PointF ptCenter = GetCenterPoint(); ++ CFX_PointF pt1(ptCenter.x - kComboBoxTriangleHalfLength, ++ ptCenter.y + kComboBoxTriangleQuarterLength); ++ CFX_PointF pt2(ptCenter.x + kComboBoxTriangleHalfLength, ++ ptCenter.y + kComboBoxTriangleQuarterLength); ++ CFX_PointF pt3(ptCenter.x, ptCenter.y - kComboBoxTriangleQuarterLength); ++ ++ CFX_Path path; ++ path.AppendPoint(pt1, CFX_Path::Point::Type::kMove); ++ path.AppendPoint(pt2, CFX_Path::Point::Type::kLine); ++ path.AppendPoint(pt3, CFX_Path::Point::Type::kLine); ++ path.AppendPoint(pt1, CFX_Path::Point::Type::kLine); ++ ++ pDevice->DrawPath(path, &mtUser2Device, nullptr, ++ kDefaultBlackColor.ToFXColor(GetTransparency()), 0, ++ CFX_FillRenderOptions::EvenOddOptions()); ++} ++ ++bool CPWL_CBButton::OnLButtonDown(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnLButtonDown(nFlag, point); ++ ++ SetCapture(); ++ CPWL_Wnd* pParent = GetParentWindow(); ++ if (pParent) ++ pParent->NotifyLButtonDown(this, point); ++ ++ return true; ++} ++ ++bool CPWL_CBButton::OnLButtonUp(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnLButtonUp(nFlag, point); ++ ++ ReleaseCapture(); ++ return true; ++} +diff --git a/fpdfsdk/pwl/cpwl_cbbutton.h b/fpdfsdk/pwl/cpwl_cbbutton.h +new file mode 100644 +index 000000000..a57942e7d +--- /dev/null ++++ b/fpdfsdk/pwl/cpwl_cbbutton.h +@@ -0,0 +1,30 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef FPDFSDK_PWL_CPWL_CBBUTTON_H_ ++#define FPDFSDK_PWL_CPWL_CBBUTTON_H_ ++ ++#include ++ ++#include "fpdfsdk/pwl/cpwl_wnd.h" ++#include "fpdfsdk/pwl/ipwl_fillernotify.h" ++ ++class CPWL_CBButton final : public CPWL_Wnd { ++ public: ++ CPWL_CBButton( ++ const CreateParams& cp, ++ std::unique_ptr pAttachedData); ++ ~CPWL_CBButton() override; ++ ++ // CPWL_Wnd: ++ void DrawThisAppearance(CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device) override; ++ bool OnLButtonDown(Mask nFlag, ++ const CFX_PointF& point) override; ++ bool OnLButtonUp(Mask nFlag, const CFX_PointF& point) override; ++}; ++ ++#endif // FPDFSDK_PWL_CPWL_CBBUTTON_H_ +diff --git a/fpdfsdk/pwl/cpwl_cblistbox.cpp b/fpdfsdk/pwl/cpwl_cblistbox.cpp +new file mode 100644 +index 000000000..492b6ae47 +--- /dev/null ++++ b/fpdfsdk/pwl/cpwl_cblistbox.cpp +@@ -0,0 +1,93 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "fpdfsdk/pwl/cpwl_cblistbox.h" ++ ++#include ++ ++#include "fpdfsdk/pwl/cpwl_combo_box.h" ++#include "fpdfsdk/pwl/cpwl_list_ctrl.h" ++#include "public/fpdf_fwlevent.h" ++#include "third_party/base/notreached.h" ++ ++CPWL_CBListBox::CPWL_CBListBox( ++ const CreateParams& cp, ++ std::unique_ptr pAttachedData) ++ : CPWL_ListBox(cp, std::move(pAttachedData)) {} ++ ++CPWL_CBListBox::~CPWL_CBListBox() = default; ++ ++bool CPWL_CBListBox::OnLButtonUp(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnLButtonUp(nFlag, point); ++ ++ if (!m_bMouseDown) ++ return true; ++ ++ ReleaseCapture(); ++ m_bMouseDown = false; ++ ++ if (!ClientHitTest(point)) ++ return true; ++ if (CPWL_Wnd* pParent = GetParentWindow()) ++ pParent->NotifyLButtonUp(this, point); ++ ++ return !OnNotifySelectionChanged(false, nFlag); ++} ++ ++bool CPWL_CBListBox::IsMovementKey(FWL_VKEYCODE nKeyCode) const { ++ switch (nKeyCode) { ++ case FWL_VKEY_Up: ++ case FWL_VKEY_Down: ++ case FWL_VKEY_Home: ++ case FWL_VKEY_Left: ++ case FWL_VKEY_End: ++ case FWL_VKEY_Right: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++bool CPWL_CBListBox::OnMovementKeyDown(FWL_VKEYCODE nKeyCode, ++ Mask nFlag) { ++ switch (nKeyCode) { ++ case FWL_VKEY_Up: ++ m_pListCtrl->OnVK_UP(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)); ++ break; ++ case FWL_VKEY_Down: ++ m_pListCtrl->OnVK_DOWN(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)); ++ break; ++ case FWL_VKEY_Home: ++ m_pListCtrl->OnVK_HOME(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)); ++ break; ++ case FWL_VKEY_Left: ++ m_pListCtrl->OnVK_LEFT(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)); ++ break; ++ case FWL_VKEY_End: ++ m_pListCtrl->OnVK_END(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)); ++ break; ++ case FWL_VKEY_Right: ++ m_pListCtrl->OnVK_RIGHT(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)); ++ break; ++ default: ++ NOTREACHED_NORETURN(); ++ break; ++ } ++ return OnNotifySelectionChanged(true, nFlag); ++} ++ ++bool CPWL_CBListBox::IsChar(uint16_t nChar, Mask nFlag) const { ++ return m_pListCtrl->OnChar(nChar, IsSHIFTKeyDown(nFlag), ++ IsCTRLKeyDown(nFlag)); ++} ++ ++bool CPWL_CBListBox::OnCharNotify(uint16_t nChar, Mask nFlag) { ++ if (auto* pComboBox = static_cast(GetParentWindow())) ++ pComboBox->SetSelectText(); ++ ++ return OnNotifySelectionChanged(true, nFlag); ++} +diff --git a/fpdfsdk/pwl/cpwl_cblistbox.h b/fpdfsdk/pwl/cpwl_cblistbox.h +new file mode 100644 +index 000000000..0f97eddfe +--- /dev/null ++++ b/fpdfsdk/pwl/cpwl_cblistbox.h +@@ -0,0 +1,32 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef FPDFSDK_PWL_CPWL_CBLISTBOX_H_ ++#define FPDFSDK_PWL_CPWL_CBLISTBOX_H_ ++ ++#include ++ ++#include "fpdfsdk/pwl/cpwl_list_box.h" ++#include "fpdfsdk/pwl/ipwl_fillernotify.h" ++#include "public/fpdf_fwlevent.h" ++ ++class CPWL_CBListBox final : public CPWL_ListBox { ++ public: ++ CPWL_CBListBox( ++ const CreateParams& cp, ++ std::unique_ptr pAttachedData); ++ ~CPWL_CBListBox() override; ++ ++ // CPWL_ListBox ++ bool OnLButtonUp(Mask nFlag, const CFX_PointF& point) override; ++ ++ bool IsMovementKey(FWL_VKEYCODE nKeyCode) const; ++ bool OnMovementKeyDown(FWL_VKEYCODE nKeyCode, Mask nFlag); ++ bool IsChar(uint16_t nChar, Mask nFlag) const; ++ bool OnCharNotify(uint16_t nChar, Mask nFlag); ++}; ++ ++#endif // FPDFSDK_PWL_CPWL_CBLISTBOX_H_ +diff --git a/fpdfsdk/pwl/cpwl_combo_box.cpp b/fpdfsdk/pwl/cpwl_combo_box.cpp +index 8192da27f..c84b7c071 100644 +--- a/fpdfsdk/pwl/cpwl_combo_box.cpp ++++ b/fpdfsdk/pwl/cpwl_combo_box.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,165 +7,26 @@ + #include "fpdfsdk/pwl/cpwl_combo_box.h" + + #include +-#include + #include + +-#include "core/fxge/cfx_pathdata.h" +-#include "core/fxge/cfx_renderdevice.h" ++#include "constants/ascii.h" ++#include "fpdfsdk/pwl/cpwl_cbbutton.h" ++#include "fpdfsdk/pwl/cpwl_cblistbox.h" + #include "fpdfsdk/pwl/cpwl_edit.h" +-#include "fpdfsdk/pwl/cpwl_edit_ctrl.h" +-#include "fpdfsdk/pwl/cpwl_list_box.h" +-#include "fpdfsdk/pwl/cpwl_list_impl.h" +-#include "fpdfsdk/pwl/cpwl_wnd.h" ++#include "fpdfsdk/pwl/ipwl_fillernotify.h" + #include "public/fpdf_fwlevent.h" +-#include "third_party/base/ptr_util.h" + + namespace { + + constexpr float kComboBoxDefaultFontSize = 12.0f; +-constexpr float kComboBoxTriangleHalfLength = 3.0f; + constexpr int kDefaultButtonWidth = 13; + + } // namespace + +-CPWL_CBListBox::CPWL_CBListBox( +- const CreateParams& cp, +- std::unique_ptr pAttachedData) +- : CPWL_ListBox(cp, std::move(pAttachedData)) {} +- +-CPWL_CBListBox::~CPWL_CBListBox() = default; +- +-bool CPWL_CBListBox::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnLButtonUp(point, nFlag); +- +- if (!m_bMouseDown) +- return true; +- +- ReleaseCapture(); +- m_bMouseDown = false; +- +- if (!ClientHitTest(point)) +- return true; +- if (CPWL_Wnd* pParent = GetParentWindow()) +- pParent->NotifyLButtonUp(this, point); +- +- return !OnNotifySelectionChanged(false, nFlag); +-} +- +-bool CPWL_CBListBox::IsMovementKey(uint16_t nChar) const { +- switch (nChar) { +- case FWL_VKEY_Up: +- case FWL_VKEY_Down: +- case FWL_VKEY_Home: +- case FWL_VKEY_Left: +- case FWL_VKEY_End: +- case FWL_VKEY_Right: +- return true; +- default: +- return false; +- } +-} +- +-bool CPWL_CBListBox::OnMovementKeyDown(uint16_t nChar, uint32_t nFlag) { +- ASSERT(IsMovementKey(nChar)); +- +- switch (nChar) { +- case FWL_VKEY_Up: +- m_pList->OnVK_UP(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); +- break; +- case FWL_VKEY_Down: +- m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); +- break; +- case FWL_VKEY_Home: +- m_pList->OnVK_HOME(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); +- break; +- case FWL_VKEY_Left: +- m_pList->OnVK_LEFT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); +- break; +- case FWL_VKEY_End: +- m_pList->OnVK_END(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); +- break; +- case FWL_VKEY_Right: +- m_pList->OnVK_RIGHT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); +- break; +- } +- return OnNotifySelectionChanged(true, nFlag); +-} +- +-bool CPWL_CBListBox::IsChar(uint16_t nChar, uint32_t nFlag) const { +- return m_pList->OnChar(nChar, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); +-} +- +-bool CPWL_CBListBox::OnCharNotify(uint16_t nChar, uint32_t nFlag) { +- if (auto* pComboBox = static_cast(GetParentWindow())) +- pComboBox->SetSelectText(); +- +- return OnNotifySelectionChanged(true, nFlag); +-} +- +-CPWL_CBButton::CPWL_CBButton( +- const CreateParams& cp, +- std::unique_ptr pAttachedData) +- : CPWL_Wnd(cp, std::move(pAttachedData)) {} +- +-CPWL_CBButton::~CPWL_CBButton() = default; +- +-void CPWL_CBButton::DrawThisAppearance(CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device) { +- CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device); +- +- CFX_FloatRect rectWnd = CPWL_Wnd::GetWindowRect(); +- if (!IsVisible() || rectWnd.IsEmpty()) +- return; +- +- CFX_PointF ptCenter = GetCenterPoint(); +- +- static constexpr float kComboBoxTriangleQuarterLength = +- kComboBoxTriangleHalfLength * 0.5; +- CFX_PointF pt1(ptCenter.x - kComboBoxTriangleHalfLength, +- ptCenter.y + kComboBoxTriangleQuarterLength); +- CFX_PointF pt2(ptCenter.x + kComboBoxTriangleHalfLength, +- ptCenter.y + kComboBoxTriangleQuarterLength); +- CFX_PointF pt3(ptCenter.x, ptCenter.y - kComboBoxTriangleQuarterLength); +- +- if (IsFloatBigger(rectWnd.right - rectWnd.left, +- kComboBoxTriangleHalfLength * 2) && +- IsFloatBigger(rectWnd.top - rectWnd.bottom, +- kComboBoxTriangleHalfLength)) { +- CFX_PathData path; +- path.AppendPoint(pt1, FXPT_TYPE::MoveTo, false); +- path.AppendPoint(pt2, FXPT_TYPE::LineTo, false); +- path.AppendPoint(pt3, FXPT_TYPE::LineTo, false); +- path.AppendPoint(pt1, FXPT_TYPE::LineTo, false); +- +- pDevice->DrawPath(&path, &mtUser2Device, nullptr, +- PWL_DEFAULT_BLACKCOLOR.ToFXColor(GetTransparency()), 0, +- FXFILL_ALTERNATE); +- } +-} +- +-bool CPWL_CBButton::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnLButtonDown(point, nFlag); +- +- SetCapture(); +- if (CPWL_Wnd* pParent = GetParentWindow()) +- pParent->NotifyLButtonDown(this, point); +- +- return true; +-} +- +-bool CPWL_CBButton::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnLButtonUp(point, nFlag); +- +- ReleaseCapture(); +- return true; +-} +- + CPWL_ComboBox::CPWL_ComboBox( + const CreateParams& cp, +- std::unique_ptr pAttachedData) ++ std::unique_ptr pAttachedData) + : CPWL_Wnd(cp, std::move(pAttachedData)) { +- GetCreationParams()->dwFlags &= ~PWS_HSCROLL; + GetCreationParams()->dwFlags &= ~PWS_VSCROLL; + } + +@@ -176,9 +37,9 @@ void CPWL_ComboBox::OnDestroy() { + // subclasses, implement the virtual OnDestroy method that does the + // cleanup first, then invokes the superclass OnDestroy ... gee, + // like a dtor would. +- m_pList.Release(); +- m_pButton.Release(); +- m_pEdit.Release(); ++ m_pList.ExtractAsDangling(); ++ m_pButton.ExtractAsDangling(); ++ m_pEdit.ExtractAsDangling(); + CPWL_Wnd::OnDestroy(); + } + +@@ -201,11 +62,20 @@ WideString CPWL_ComboBox::GetSelectedText() { + return WideString(); + } + ++void CPWL_ComboBox::ReplaceAndKeepSelection(const WideString& text) { ++ if (m_pEdit) ++ m_pEdit->ReplaceAndKeepSelection(text); ++} ++ + void CPWL_ComboBox::ReplaceSelection(const WideString& text) { + if (m_pEdit) + m_pEdit->ReplaceSelection(text); + } + ++bool CPWL_ComboBox::SelectAllText() { ++ return m_pEdit && m_pEdit->SelectAllText(); ++} ++ + bool CPWL_ComboBox::CanUndo() { + return m_pEdit && m_pEdit->CanUndo(); + } +@@ -253,15 +123,6 @@ void CPWL_ComboBox::SetEditSelection(int32_t nStartChar, int32_t nEndChar) { + m_pEdit->SetSelection(nStartChar, nEndChar); + } + +-void CPWL_ComboBox::GetEditSelection(int32_t& nStartChar, +- int32_t& nEndChar) const { +- nStartChar = -1; +- nEndChar = -1; +- +- if (m_pEdit) +- m_pEdit->GetSelection(nStartChar, nEndChar); +-} +- + void CPWL_ComboBox::ClearSelection() { + if (m_pEdit) + m_pEdit->ClearSelection(); +@@ -278,8 +139,8 @@ void CPWL_ComboBox::CreateEdit(const CreateParams& cp) { + return; + + CreateParams ecp = cp; +- ecp.dwFlags = PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PES_CENTER | +- PES_AUTOSCROLL | PES_UNDO; ++ ecp.dwFlags = ++ PWS_VISIBLE | PWS_BORDER | PES_CENTER | PES_AUTOSCROLL | PES_UNDO; + + if (HasFlag(PWS_AUTOFONTSIZE)) + ecp.dwFlags |= PWS_AUTOFONTSIZE; +@@ -289,11 +150,10 @@ void CPWL_ComboBox::CreateEdit(const CreateParams& cp) { + + ecp.rcRectWnd = CFX_FloatRect(); + ecp.dwBorderWidth = 0; +- ecp.nBorderStyle = BorderStyle::SOLID; ++ ecp.nBorderStyle = BorderStyle::kSolid; + +- auto pEdit = pdfium::MakeUnique(ecp, CloneAttachedData()); ++ auto pEdit = std::make_unique(ecp, CloneAttachedData()); + m_pEdit = pEdit.get(); +- m_pEdit->AttachFFLData(m_pFormFiller.Get()); + AddChild(std::move(pEdit)); + m_pEdit->Realize(); + } +@@ -303,15 +163,15 @@ void CPWL_ComboBox::CreateButton(const CreateParams& cp) { + return; + + CreateParams bcp = cp; +- bcp.dwFlags = PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PWS_BACKGROUND; +- bcp.sBackgroundColor = CFX_Color(CFX_Color::kRGB, 220.0f / 255.0f, ++ bcp.dwFlags = PWS_VISIBLE | PWS_BORDER | PWS_BACKGROUND; ++ bcp.sBackgroundColor = CFX_Color(CFX_Color::Type::kRGB, 220.0f / 255.0f, + 220.0f / 255.0f, 220.0f / 255.0f); +- bcp.sBorderColor = PWL_DEFAULT_BLACKCOLOR; ++ bcp.sBorderColor = kDefaultBlackColor; + bcp.dwBorderWidth = 2; +- bcp.nBorderStyle = BorderStyle::BEVELED; +- bcp.eCursorType = FXCT_ARROW; ++ bcp.nBorderStyle = BorderStyle::kBeveled; ++ bcp.eCursorType = IPWL_FillerNotify::CursorStyle::kArrow; + +- auto pButton = pdfium::MakeUnique(bcp, CloneAttachedData()); ++ auto pButton = std::make_unique(bcp, CloneAttachedData()); + m_pButton = pButton.get(); + AddChild(std::move(pButton)); + m_pButton->Realize(); +@@ -322,25 +182,22 @@ void CPWL_ComboBox::CreateListBox(const CreateParams& cp) { + return; + + CreateParams lcp = cp; +- lcp.dwFlags = +- PWS_CHILD | PWS_BORDER | PWS_BACKGROUND | PLBS_HOVERSEL | PWS_VSCROLL; +- lcp.nBorderStyle = BorderStyle::SOLID; ++ lcp.dwFlags = PWS_BORDER | PWS_BACKGROUND | PLBS_HOVERSEL | PWS_VSCROLL; ++ lcp.nBorderStyle = BorderStyle::kSolid; + lcp.dwBorderWidth = 1; +- lcp.eCursorType = FXCT_ARROW; ++ lcp.eCursorType = IPWL_FillerNotify::CursorStyle::kArrow; + lcp.rcRectWnd = CFX_FloatRect(); +- + lcp.fFontSize = + (cp.dwFlags & PWS_AUTOFONTSIZE) ? kComboBoxDefaultFontSize : cp.fFontSize; + +- if (cp.sBorderColor.nColorType == CFX_Color::kTransparent) +- lcp.sBorderColor = PWL_DEFAULT_BLACKCOLOR; ++ if (cp.sBorderColor.nColorType == CFX_Color::Type::kTransparent) ++ lcp.sBorderColor = kDefaultBlackColor; + +- if (cp.sBackgroundColor.nColorType == CFX_Color::kTransparent) +- lcp.sBackgroundColor = PWL_DEFAULT_WHITECOLOR; ++ if (cp.sBackgroundColor.nColorType == CFX_Color::Type::kTransparent) ++ lcp.sBackgroundColor = kDefaultWhiteColor; + +- auto pList = pdfium::MakeUnique(lcp, CloneAttachedData()); ++ auto pList = std::make_unique(lcp, CloneAttachedData()); + m_pList = pList.get(); +- m_pList->AttachFFLData(m_pFormFiller.Get()); + AddChild(std::move(pList)); + m_pList->Realize(); + } +@@ -423,7 +280,7 @@ bool CPWL_ComboBox::RePosChildWnd() { + + void CPWL_ComboBox::SelectAll() { + if (m_pEdit && HasFlag(PCBS_ALLOWCUSTOMTEXT)) +- m_pEdit->SelectAll(); ++ m_pEdit->SelectAllText(); + } + + CFX_FloatRect CPWL_ComboBox::GetFocusRect() const { +@@ -436,7 +293,7 @@ bool CPWL_ComboBox::SetPopup(bool bPopup) { + if (bPopup == m_bPopup) + return true; + float fListHeight = m_pList->GetContentRect().Height(); +- if (!IsFloatBigger(fListHeight, 0.0f)) ++ if (!FXSYS_IsFloatBigger(fListHeight, 0.0f)) + return true; + + if (!bPopup) { +@@ -444,11 +301,8 @@ bool CPWL_ComboBox::SetPopup(bool bPopup) { + return Move(m_rcOldWindow, true, true); + } + +- if (!m_pFillerNotify) +- return true; +- + ObservedPtr thisObserved(this); +- if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), 0)) ++ if (GetFillerNotify()->OnPopupPreOpen(GetAttachedData(), {})) + return !!thisObserved; + if (!thisObserved) + return false; +@@ -461,9 +315,9 @@ bool CPWL_ComboBox::SetPopup(bool bPopup) { + + bool bBottom; + float fPopupRet; +- m_pFillerNotify->QueryWherePopup(GetAttachedData(), fPopupMin, fPopupMax, +- &bBottom, &fPopupRet); +- if (!IsFloatBigger(fPopupRet, 0.0f)) ++ GetFillerNotify()->QueryWherePopup(GetAttachedData(), fPopupMin, fPopupMax, ++ &bBottom, &fPopupRet); ++ if (!FXSYS_IsFloatBigger(fPopupRet, 0.0f)) + return true; + + m_rcOldWindow = CPWL_Wnd::GetWindowRect(); +@@ -479,73 +333,112 @@ bool CPWL_ComboBox::SetPopup(bool bPopup) { + if (!Move(rcWindow, true, true)) + return false; + +- m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), 0); ++ GetFillerNotify()->OnPopupPostOpen(GetAttachedData(), {}); + return !!thisObserved; + } + +-bool CPWL_ComboBox::OnKeyDown(uint16_t nChar, uint32_t nFlag) { ++bool CPWL_ComboBox::OnKeyDown(FWL_VKEYCODE nKeyCode, ++ Mask nFlag) { + if (!m_pList) + return false; + if (!m_pEdit) + return false; + ++ ObservedPtr thisObserved(this); + m_nSelectItem = -1; + +- switch (nChar) { ++ switch (nKeyCode) { + case FWL_VKEY_Up: + if (m_pList->GetCurSel() > 0) { +- if (m_pFillerNotify) { +- if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), nFlag)) +- return false; +- if (m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), nFlag)) +- return false; ++ if (GetFillerNotify()->OnPopupPreOpen(GetAttachedData(), nFlag) || ++ !thisObserved) { ++ return false; ++ } ++ if (GetFillerNotify()->OnPopupPostOpen(GetAttachedData(), nFlag) || ++ !thisObserved) { ++ return false; + } +- if (m_pList->IsMovementKey(nChar)) { +- if (m_pList->OnMovementKeyDown(nChar, nFlag)) ++ if (m_pList->IsMovementKey(nKeyCode)) { ++ if (m_pList->OnMovementKeyDown(nKeyCode, nFlag) || !thisObserved) { + return false; ++ } + SetSelectText(); + } + } + return true; + case FWL_VKEY_Down: + if (m_pList->GetCurSel() < m_pList->GetCount() - 1) { +- if (m_pFillerNotify) { +- if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), nFlag)) +- return false; +- if (m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), nFlag)) +- return false; ++ if (GetFillerNotify()->OnPopupPreOpen(GetAttachedData(), nFlag) || ++ !thisObserved) { ++ return false; ++ } ++ if (GetFillerNotify()->OnPopupPostOpen(GetAttachedData(), nFlag) || ++ !thisObserved) { ++ return false; + } +- if (m_pList->IsMovementKey(nChar)) { +- if (m_pList->OnMovementKeyDown(nChar, nFlag)) ++ if (m_pList->IsMovementKey(nKeyCode)) { ++ if (m_pList->OnMovementKeyDown(nKeyCode, nFlag) || !thisObserved) { + return false; ++ } + SetSelectText(); + } + } + return true; ++ default: ++ break; + } + + if (HasFlag(PCBS_ALLOWCUSTOMTEXT)) +- return m_pEdit->OnKeyDown(nChar, nFlag); ++ return m_pEdit->OnKeyDown(nKeyCode, nFlag); + + return false; + } + +-bool CPWL_ComboBox::OnChar(uint16_t nChar, uint32_t nFlag) { ++bool CPWL_ComboBox::OnChar(uint16_t nChar, Mask nFlag) { + if (!m_pList) + return false; + + if (!m_pEdit) + return false; + ++ // In a combo box if the ENTER/SPACE key is pressed, show the combo box ++ // options. ++ switch (nChar) { ++ case pdfium::ascii::kReturn: ++ if (!SetPopup(!IsPopup())) { ++ return false; ++ } ++ SetSelectText(); ++ return true; ++ case pdfium::ascii::kSpace: ++ // Show the combo box options with space only if the combo box is not ++ // editable ++ if (!HasFlag(PCBS_ALLOWCUSTOMTEXT)) { ++ if (!IsPopup()) { ++ if (!SetPopup(/*bPopUp=*/true)) { ++ return false; ++ } ++ SetSelectText(); ++ } ++ return true; ++ } ++ break; ++ default: ++ break; ++ } ++ + m_nSelectItem = -1; + if (HasFlag(PCBS_ALLOWCUSTOMTEXT)) + return m_pEdit->OnChar(nChar, nFlag); + +- if (m_pFillerNotify) { +- if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), nFlag)) +- return false; +- if (m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), nFlag)) +- return false; ++ ObservedPtr thisObserved(this); ++ if (GetFillerNotify()->OnPopupPreOpen(GetAttachedData(), nFlag) || ++ !thisObserved) { ++ return false; ++ } ++ if (GetFillerNotify()->OnPopupPostOpen(GetAttachedData(), nFlag) || ++ !thisObserved) { ++ return false; + } + if (!m_pList->IsChar(nChar, nFlag)) + return false; +@@ -554,7 +447,7 @@ bool CPWL_ComboBox::OnChar(uint16_t nChar, uint32_t nFlag) { + + void CPWL_ComboBox::NotifyLButtonDown(CPWL_Wnd* child, const CFX_PointF& pos) { + if (child == m_pButton) { +- SetPopup(!m_bPopup); ++ (void)SetPopup(!m_bPopup); + // Note, |this| may no longer be viable at this point. If more work needs to + // be done, check the return value of SetPopup(). + } +@@ -565,9 +458,9 @@ void CPWL_ComboBox::NotifyLButtonUp(CPWL_Wnd* child, const CFX_PointF& pos) { + return; + + SetSelectText(); +- SelectAll(); ++ SelectAllText(); + m_pEdit->SetFocus(); +- SetPopup(false); ++ (void)SetPopup(false); + // Note, |this| may no longer be viable at this point. If more work needs to + // be done, check the return value of SetPopup(). + } +@@ -577,18 +470,8 @@ bool CPWL_ComboBox::IsPopup() const { + } + + void CPWL_ComboBox::SetSelectText() { +- m_pEdit->SelectAll(); ++ m_pEdit->SelectAllText(); + m_pEdit->ReplaceSelection(m_pList->GetText()); +- m_pEdit->SelectAll(); ++ m_pEdit->SelectAllText(); + m_nSelectItem = m_pList->GetCurSel(); + } +- +-void CPWL_ComboBox::SetFillerNotify(IPWL_Filler_Notify* pNotify) { +- m_pFillerNotify = pNotify; +- +- if (m_pEdit) +- m_pEdit->SetFillerNotify(pNotify); +- +- if (m_pList) +- m_pList->SetFillerNotify(pNotify); +-} +diff --git a/fpdfsdk/pwl/cpwl_combo_box.h b/fpdfsdk/pwl/cpwl_combo_box.h +index 8b9bf122d..87749fa78 100644 +--- a/fpdfsdk/pwl/cpwl_combo_box.h ++++ b/fpdfsdk/pwl/cpwl_combo_box.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,53 +10,27 @@ + #include + + #include "core/fxcrt/unowned_ptr.h" +-#include "fpdfsdk/pwl/cpwl_edit.h" +-#include "fpdfsdk/pwl/cpwl_list_box.h" + #include "fpdfsdk/pwl/cpwl_wnd.h" ++#include "fpdfsdk/pwl/ipwl_fillernotify.h" + +-class CPWL_CBListBox final : public CPWL_ListBox { +- public: +- CPWL_CBListBox( +- const CreateParams& cp, +- std::unique_ptr pAttachedData); +- ~CPWL_CBListBox() override; +- +- // CPWL_ListBox +- bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; +- +- bool IsMovementKey(uint16_t nChar) const; +- bool OnMovementKeyDown(uint16_t nChar, uint32_t nFlag); +- bool IsChar(uint16_t nChar, uint32_t nFlag) const; +- bool OnCharNotify(uint16_t nChar, uint32_t nFlag); +-}; +- +-class CPWL_CBButton final : public CPWL_Wnd { +- public: +- CPWL_CBButton( +- const CreateParams& cp, +- std::unique_ptr pAttachedData); +- ~CPWL_CBButton() override; +- +- // CPWL_Wnd +- void DrawThisAppearance(CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device) override; +- bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override; +- bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; +-}; ++class CPWL_Edit; ++class CPWL_CBButton; ++class CPWL_CBListBox; ++class IPWL_FillerNotify; + + class CPWL_ComboBox final : public CPWL_Wnd { + public: + CPWL_ComboBox( + const CreateParams& cp, +- std::unique_ptr pAttachedData); ++ std::unique_ptr pAttachedData); + ~CPWL_ComboBox() override; + +- CPWL_Edit* GetEdit() const { return m_pEdit.Get(); } ++ CPWL_Edit* GetEdit() const { return m_pEdit; } + + // CPWL_Wnd: + void OnDestroy() override; +- bool OnKeyDown(uint16_t nChar, uint32_t nFlag) override; +- bool OnChar(uint16_t nChar, uint32_t nFlag) override; ++ bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask nFlag) override; ++ bool OnChar(uint16_t nChar, Mask nFlag) override; + void NotifyLButtonDown(CPWL_Wnd* child, const CFX_PointF& pos) override; + void NotifyLButtonUp(CPWL_Wnd* child, const CFX_PointF& pos) override; + void CreateChildWnd(const CreateParams& cp) override; +@@ -66,26 +40,24 @@ class CPWL_ComboBox final : public CPWL_Wnd { + void KillFocus() override; + WideString GetText() override; + WideString GetSelectedText() override; ++ void ReplaceAndKeepSelection(const WideString& text) override; + void ReplaceSelection(const WideString& text) override; ++ bool SelectAllText() override; + bool CanUndo() override; + bool CanRedo() override; + bool Undo() override; + bool Redo() override; + +- void SetFillerNotify(IPWL_Filler_Notify* pNotify); +- + void SetText(const WideString& text); + void AddString(const WideString& str); + int32_t GetSelect() const; + void SetSelect(int32_t nItemIndex); + + void SetEditSelection(int32_t nStartChar, int32_t nEndChar); +- void GetEditSelection(int32_t& nStartChar, int32_t& nEndChar) const; + void ClearSelection(); + void SelectAll(); + bool IsPopup() const; + void SetSelectText(); +- void AttachFFLData(CFFL_FormFiller* pData) { m_pFormFiller = pData; } + + private: + void CreateEdit(const CreateParams& cp); +@@ -93,7 +65,7 @@ class CPWL_ComboBox final : public CPWL_Wnd { + void CreateListBox(const CreateParams& cp); + + // Returns |true| iff this instance is still allocated. +- bool SetPopup(bool bPopup); ++ [[nodiscard]] bool SetPopup(bool bPopup); + + UnownedPtr m_pEdit; + UnownedPtr m_pButton; +@@ -102,8 +74,6 @@ class CPWL_ComboBox final : public CPWL_Wnd { + bool m_bPopup = false; + bool m_bBottom = true; + int32_t m_nSelectItem = -1; +- UnownedPtr m_pFillerNotify; +- UnownedPtr m_pFormFiller; + }; + + #endif // FPDFSDK_PWL_CPWL_COMBO_BOX_H_ +diff --git a/fpdfsdk/pwl/cpwl_combo_box_edit_embeddertest.cpp b/fpdfsdk/pwl/cpwl_combo_box_edit_embeddertest.cpp +new file mode 100644 +index 000000000..3a9d8efb0 +--- /dev/null ++++ b/fpdfsdk/pwl/cpwl_combo_box_edit_embeddertest.cpp +@@ -0,0 +1,283 @@ ++// Copyright 2017 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "fpdfsdk/pwl/cpwl_combo_box_embeddertest.h" ++ ++#include "fpdfsdk/cpdfsdk_formfillenvironment.h" ++#include "fpdfsdk/cpdfsdk_helpers.h" ++#include "fpdfsdk/cpdfsdk_widget.h" ++#include "fpdfsdk/formfiller/cffl_formfield.h" ++#include "fpdfsdk/formfiller/cffl_interactiveformfiller.h" ++#include "fpdfsdk/pwl/cpwl_combo_box.h" ++#include "fpdfsdk/pwl/cpwl_wnd.h" ++#include "public/fpdf_fwlevent.h" ++#include "testing/embedder_test.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++class CPWLComboBoxEditEmbedderTest : public CPWLComboBoxEmbedderTest {}; ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextEmptyAndBasicNormal) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotNormal()); ++ ++ // Automatically pre-filled with "Banana". ++ EXPECT_FALSE(GetCPWLComboBox()->GetText().IsEmpty()); ++ EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetText().c_str()); ++ ++ // Check that selection is initially empty, then select entire word. ++ EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); ++ GetCPWLComboBox()->SetSelectText(); ++ EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ // Select other options. ++ GetCPWLComboBox()->SetSelect(0); ++ EXPECT_STREQ(L"Apple", GetCPWLComboBox()->GetSelectedText().c_str()); ++ GetCPWLComboBox()->SetSelect(2); ++ EXPECT_STREQ(L"Cherry", GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ // Verify that combobox text cannot be edited. ++ EXPECT_FALSE(GetCFFLFormField()->OnChar(GetCPDFSDKAnnotNormal(), 'a', {})); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextFragmentsNormal) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotNormal()); ++ EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetText().c_str()); ++ ++ GetCPWLComboBox()->SetEditSelection(0, 0); ++ EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); ++ ++ GetCPWLComboBox()->SetEditSelection(0, 1); ++ EXPECT_STREQ(L"B", GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ GetCPWLComboBox()->SetEditSelection(0, -1); ++ EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ GetCPWLComboBox()->SetEditSelection(-8, -1); ++ EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); ++ ++ GetCPWLComboBox()->SetEditSelection(4, 1); ++ EXPECT_STREQ(L"ana", GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ GetCPWLComboBox()->SetEditSelection(1, 4); ++ EXPECT_STREQ(L"ana", GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ GetCPWLComboBox()->SetEditSelection(5, 6); ++ EXPECT_STREQ(L"a", GetCPWLComboBox()->GetSelectedText().c_str()); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextEmptyAndBasicEditable) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); ++ EXPECT_TRUE(GetCPWLComboBox()->GetText().IsEmpty()); ++ ++ // Check selection is initially empty, then select a provided option. ++ EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); ++ GetCPWLComboBox()->SetSelect(0); ++ GetCPWLComboBox()->SetSelectText(); ++ EXPECT_STREQ(L"Foo", GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ // Select another option and then select last char of that option. ++ GetCPWLComboBox()->SetSelect(1); ++ EXPECT_STREQ(L"Bar", GetCPWLComboBox()->GetSelectedText().c_str()); ++ GetCPWLComboBox()->SetEditSelection(2, 3); ++ EXPECT_STREQ(L"r", GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ // Type into editable combobox text field and select new text. ++ EXPECT_TRUE( ++ GetCFFLFormField()->OnChar(GetCPDFSDKAnnotUserEditable(), 'a', {})); ++ EXPECT_TRUE( ++ GetCFFLFormField()->OnChar(GetCPDFSDKAnnotUserEditable(), 'b', {})); ++ EXPECT_TRUE( ++ GetCFFLFormField()->OnChar(GetCPDFSDKAnnotUserEditable(), 'c', {})); ++ ++ EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); ++ GetCPWLComboBox()->SetEditSelection(0, 5); ++ EXPECT_STREQ(L"Baabc", GetCPWLComboBox()->GetSelectedText().c_str()); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextFragmentsEditable) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); ++ TypeTextIntoTextField(50); ++ ++ GetCPWLComboBox()->SetEditSelection(0, 0); ++ EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); ++ ++ GetCPWLComboBox()->SetEditSelection(0, 1); ++ EXPECT_STREQ(L"A", GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ GetCPWLComboBox()->SetEditSelection(0, -1); ++ EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", ++ GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ GetCPWLComboBox()->SetEditSelection(-8, -1); ++ EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); ++ ++ GetCPWLComboBox()->SetEditSelection(23, 12); ++ EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ GetCPWLComboBox()->SetEditSelection(12, 23); ++ EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ GetCPWLComboBox()->SetEditSelection(49, 50); ++ EXPECT_STREQ(L"r", GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ GetCPWLComboBox()->SetEditSelection(49, 55); ++ EXPECT_STREQ(L"r", GetCPWLComboBox()->GetSelectedText().c_str()); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, DeleteEntireTextSelection) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); ++ TypeTextIntoTextField(50); ++ ++ GetCPWLComboBox()->SetEditSelection(0, -1); ++ EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", ++ GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ GetCPWLComboBox()->ReplaceSelection(L""); ++ EXPECT_TRUE(GetCPWLComboBox()->GetText().IsEmpty()); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, DeleteTextSelectionMiddle) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); ++ TypeTextIntoTextField(50); ++ ++ GetCPWLComboBox()->SetEditSelection(12, 23); ++ EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ GetCPWLComboBox()->ReplaceSelection(L""); ++ EXPECT_STREQ(L"ABCDEFGHIJKLXYZ[\\]^_`abcdefghijklmnopqr", ++ GetCPWLComboBox()->GetText().c_str()); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, DeleteTextSelectionLeft) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); ++ TypeTextIntoTextField(50); ++ ++ GetCPWLComboBox()->SetEditSelection(0, 5); ++ EXPECT_STREQ(L"ABCDE", GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ GetCPWLComboBox()->ReplaceSelection(L""); ++ EXPECT_STREQ(L"FGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", ++ GetCPWLComboBox()->GetText().c_str()); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, DeleteTextSelectionRight) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); ++ TypeTextIntoTextField(50); ++ ++ GetCPWLComboBox()->SetEditSelection(45, 50); ++ EXPECT_STREQ(L"nopqr", GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ GetCPWLComboBox()->ReplaceSelection(L""); ++ EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklm", ++ GetCPWLComboBox()->GetText().c_str()); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, DeleteEmptyTextSelection) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); ++ TypeTextIntoTextField(50); ++ ++ GetCPWLComboBox()->ReplaceSelection(L""); ++ EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", ++ GetCPWLComboBox()->GetText().c_str()); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, InsertTextInEmptyEditableComboBox) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); ++ GetCPWLComboBox()->ReplaceSelection(L"Hello"); ++ EXPECT_STREQ(L"Hello", GetCPWLComboBox()->GetText().c_str()); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, ++ InsertTextInPopulatedEditableComboBoxLeft) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); ++ TypeTextIntoTextField(10); ++ ++ // Move cursor to beginning of user-editable combobox text field. ++ EXPECT_TRUE(GetCFFLFormField()->OnKeyDown(FWL_VKEY_Home, {})); ++ ++ GetCPWLComboBox()->ReplaceSelection(L"Hello"); ++ EXPECT_STREQ(L"HelloABCDEFGHIJ", GetCPWLComboBox()->GetText().c_str()); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, ++ InsertTextInPopulatedEditableComboBoxMiddle) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); ++ TypeTextIntoTextField(10); ++ ++ // Move cursor to middle of user-editable combobox text field. ++ for (int i = 0; i < 5; ++i) { ++ EXPECT_TRUE(GetCFFLFormField()->OnKeyDown(FWL_VKEY_Left, {})); ++ } ++ ++ GetCPWLComboBox()->ReplaceSelection(L"Hello"); ++ EXPECT_STREQ(L"ABCDEHelloFGHIJ", GetCPWLComboBox()->GetText().c_str()); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, ++ InsertTextInPopulatedEditableComboBoxRight) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); ++ TypeTextIntoTextField(10); ++ ++ GetCPWLComboBox()->ReplaceSelection(L"Hello"); ++ EXPECT_STREQ(L"ABCDEFGHIJHello", GetCPWLComboBox()->GetText().c_str()); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, ++ InsertTextAndReplaceSelectionInPopulatedEditableComboBoxWhole) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); ++ TypeTextIntoTextField(10); ++ ++ GetCPWLComboBox()->SetEditSelection(0, -1); ++ EXPECT_STREQ(L"ABCDEFGHIJ", GetCPWLComboBox()->GetSelectedText().c_str()); ++ GetCPWLComboBox()->ReplaceSelection(L"Hello"); ++ EXPECT_STREQ(L"Hello", GetCPWLComboBox()->GetText().c_str()); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, ++ InsertTextAndReplaceSelectionInPopulatedEditableComboBoxLeft) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); ++ TypeTextIntoTextField(10); ++ ++ GetCPWLComboBox()->SetEditSelection(0, 5); ++ EXPECT_STREQ(L"ABCDE", GetCPWLComboBox()->GetSelectedText().c_str()); ++ GetCPWLComboBox()->ReplaceSelection(L"Hello"); ++ EXPECT_STREQ(L"HelloFGHIJ", GetCPWLComboBox()->GetText().c_str()); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, ++ InsertTextAndReplaceSelectionInPopulatedEditableComboBoxMiddle) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); ++ TypeTextIntoTextField(10); ++ ++ GetCPWLComboBox()->SetEditSelection(2, 7); ++ EXPECT_STREQ(L"CDEFG", GetCPWLComboBox()->GetSelectedText().c_str()); ++ GetCPWLComboBox()->ReplaceSelection(L"Hello"); ++ EXPECT_STREQ(L"ABHelloHIJ", GetCPWLComboBox()->GetText().c_str()); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, ++ InsertTextAndReplaceSelectionInPopulatedEditableComboBoxRight) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); ++ TypeTextIntoTextField(10); ++ ++ GetCPWLComboBox()->SetEditSelection(5, 10); ++ EXPECT_STREQ(L"FGHIJ", GetCPWLComboBox()->GetSelectedText().c_str()); ++ GetCPWLComboBox()->ReplaceSelection(L"Hello"); ++ EXPECT_STREQ(L"ABCDEHello", GetCPWLComboBox()->GetText().c_str()); ++} ++ ++TEST_F(CPWLComboBoxEditEmbedderTest, ReplaceAndKeepSelection) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); ++ TypeTextIntoTextField(10); ++ ++ GetCPWLComboBox()->SetEditSelection(1, 3); ++ EXPECT_STREQ(L"ABCDEFGHIJ", GetCPWLComboBox()->GetText().c_str()); ++ GetCPWLComboBox()->ReplaceAndKeepSelection(L"xyz"); ++ EXPECT_STREQ(L"AxyzDEFGHIJ", GetCPWLComboBox()->GetText().c_str()); ++ EXPECT_STREQ(L"xyz", GetCPWLComboBox()->GetSelectedText().c_str()); ++ ++ GetCPWLComboBox()->SetEditSelection(4, 1); ++ GetCPWLComboBox()->ReplaceAndKeepSelection(L"12"); ++ EXPECT_STREQ(L"A12DEFGHIJ", GetCPWLComboBox()->GetText().c_str()); ++ EXPECT_STREQ(L"12", GetCPWLComboBox()->GetSelectedText().c_str()); ++} +diff --git a/fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp b/fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp +index 7d3dd4d36..299672f28 100644 +--- a/fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp ++++ b/fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp +@@ -1,348 +1,75 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include "fpdfsdk/cpdfsdk_annot.h" ++#include "fpdfsdk/pwl/cpwl_combo_box_embeddertest.h" ++ + #include "fpdfsdk/cpdfsdk_annotiterator.h" + #include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fpdfsdk/cpdfsdk_helpers.h" +-#include "fpdfsdk/formfiller/cffl_formfiller.h" ++#include "fpdfsdk/cpdfsdk_widget.h" ++#include "fpdfsdk/formfiller/cffl_formfield.h" + #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h" + #include "fpdfsdk/pwl/cpwl_combo_box.h" + #include "fpdfsdk/pwl/cpwl_wnd.h" + #include "public/fpdf_fwlevent.h" +-#include "testing/embedder_test.h" + #include "testing/gtest/include/gtest/gtest.h" + +-class CPWLComboBoxEditEmbedderTest : public EmbedderTest { +- protected: +- void SetUp() override { +- EmbedderTest::SetUp(); +- CreateAndInitializeFormComboboxPDF(); +- } +- +- void TearDown() override { +- UnloadPage(GetPage()); +- EmbedderTest::TearDown(); +- } +- +- void CreateAndInitializeFormComboboxPDF() { +- EXPECT_TRUE(OpenDocument("combobox_form.pdf")); +- m_page = LoadPage(0); +- ASSERT_TRUE(m_page); +- +- m_pFormFillEnv = +- CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle()); +- CPDFSDK_AnnotIterator iter(m_pFormFillEnv->GetPageView(0), +- CPDF_Annot::Subtype::WIDGET); +- +- // User editable combobox. +- m_pAnnotEditable = iter.GetFirstAnnot(); +- ASSERT_TRUE(m_pAnnotEditable); +- ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, m_pAnnotEditable->GetAnnotSubtype()); +- +- // Normal combobox with pre-selected value. +- m_pAnnotNormal = iter.GetNextAnnot(m_pAnnotEditable); +- ASSERT_TRUE(m_pAnnotNormal); +- ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, m_pAnnotNormal->GetAnnotSubtype()); +- +- // Read-only combobox. +- CPDFSDK_Annot* pAnnotReadOnly = iter.GetNextAnnot(m_pAnnotNormal); +- CPDFSDK_Annot* pLastAnnot = iter.GetLastAnnot(); +- ASSERT_EQ(pAnnotReadOnly, pLastAnnot); +- } +- +- void FormFillerAndWindowSetup(CPDFSDK_Annot* pAnnotCombobox) { +- CFFL_InteractiveFormFiller* pInteractiveFormFiller = +- m_pFormFillEnv->GetInteractiveFormFiller(); +- { +- ObservedPtr pObserved(pAnnotCombobox); +- EXPECT_TRUE(pInteractiveFormFiller->OnSetFocus(&pObserved, 0)); +- } +- +- m_pFormFiller = +- pInteractiveFormFiller->GetFormFillerForTesting(pAnnotCombobox); +- ASSERT_TRUE(m_pFormFiller); +- +- CPWL_Wnd* pWindow = +- m_pFormFiller->GetPWLWindow(m_pFormFillEnv->GetPageView(0), false); +- ASSERT_TRUE(pWindow); +- m_pComboBox = static_cast(pWindow); +- } +- +- void TypeTextIntoTextField(int num_chars) { +- // Type text starting with 'A' to as many chars as specified by |num_chars|. +- for (int i = 0; i < num_chars; ++i) { +- EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(), +- i + 'A', 0)); +- } +- } +- +- FPDF_PAGE GetPage() const { return m_page; } +- CPWL_ComboBox* GetCPWLComboBox() const { return m_pComboBox; } +- CFFL_FormFiller* GetCFFLFormFiller() const { return m_pFormFiller; } +- CPDFSDK_Annot* GetCPDFSDKAnnotNormal() const { return m_pAnnotNormal; } +- CPDFSDK_Annot* GetCPDFSDKAnnotUserEditable() const { +- return m_pAnnotEditable; +- } +- CPDFSDK_FormFillEnvironment* GetCPDFSDKFormFillEnv() const { +- return m_pFormFillEnv; +- } +- +- private: +- FPDF_PAGE m_page; +- CPWL_ComboBox* m_pComboBox; +- CFFL_FormFiller* m_pFormFiller; +- CPDFSDK_Annot* m_pAnnotNormal; +- CPDFSDK_Annot* m_pAnnotEditable; +- CPDFSDK_FormFillEnvironment* m_pFormFillEnv; +-}; +- +-TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextEmptyAndBasicNormal) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotNormal()); +- +- // Automatically pre-filled with "Banana". +- EXPECT_FALSE(GetCPWLComboBox()->GetText().IsEmpty()); +- EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetText().c_str()); +- +- // Check that selection is intially empty, then select entire word. +- EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); +- GetCPWLComboBox()->SetSelectText(); +- EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetSelectedText().c_str()); +- +- // Select other options. +- GetCPWLComboBox()->SetSelect(0); +- EXPECT_STREQ(L"Apple", GetCPWLComboBox()->GetSelectedText().c_str()); +- GetCPWLComboBox()->SetSelect(2); +- EXPECT_STREQ(L"Cherry", GetCPWLComboBox()->GetSelectedText().c_str()); +- +- // Verify that combobox text cannot be edited. +- EXPECT_FALSE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotNormal(), 'a', 0)); ++void CPWLComboBoxEmbedderTest::SetUp() { ++ EmbedderTest::SetUp(); ++ CreateAndInitializeFormComboboxPDF(); + } + +-TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextFragmentsNormal) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotNormal()); +- EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetText().c_str()); +- +- GetCPWLComboBox()->SetEditSelection(0, 0); +- EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); +- +- GetCPWLComboBox()->SetEditSelection(0, 1); +- EXPECT_STREQ(L"B", GetCPWLComboBox()->GetSelectedText().c_str()); +- +- GetCPWLComboBox()->SetEditSelection(0, -1); +- EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetSelectedText().c_str()); +- +- GetCPWLComboBox()->SetEditSelection(-8, -1); +- EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); +- +- GetCPWLComboBox()->SetEditSelection(4, 1); +- EXPECT_STREQ(L"ana", GetCPWLComboBox()->GetSelectedText().c_str()); +- +- GetCPWLComboBox()->SetEditSelection(1, 4); +- EXPECT_STREQ(L"ana", GetCPWLComboBox()->GetSelectedText().c_str()); +- +- GetCPWLComboBox()->SetEditSelection(5, 6); +- EXPECT_STREQ(L"a", GetCPWLComboBox()->GetSelectedText().c_str()); ++void CPWLComboBoxEmbedderTest::TearDown() { ++ UnloadPage(GetPage()); ++ EmbedderTest::TearDown(); + } + +-TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextEmptyAndBasicEditable) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); +- EXPECT_TRUE(GetCPWLComboBox()->GetText().IsEmpty()); ++void CPWLComboBoxEmbedderTest::CreateAndInitializeFormComboboxPDF() { ++ ASSERT_TRUE(OpenDocument("combobox_form.pdf")); ++ m_page = LoadPage(0); ++ ASSERT_TRUE(m_page); + +- // Check selection is intially empty, then select a provided option. +- EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); +- GetCPWLComboBox()->SetSelect(0); +- GetCPWLComboBox()->SetSelectText(); +- EXPECT_STREQ(L"Foo", GetCPWLComboBox()->GetSelectedText().c_str()); ++ m_pFormFillEnv = CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle()); ++ m_pPageView = m_pFormFillEnv->GetPageViewAtIndex(0); ++ CPDFSDK_AnnotIterator iter(m_pPageView, {CPDF_Annot::Subtype::WIDGET}); + +- // Select another option and then select last char of that option. +- GetCPWLComboBox()->SetSelect(1); +- EXPECT_STREQ(L"Bar", GetCPWLComboBox()->GetSelectedText().c_str()); +- GetCPWLComboBox()->SetEditSelection(2, 3); +- EXPECT_STREQ(L"r", GetCPWLComboBox()->GetSelectedText().c_str()); ++ // User editable combobox. ++ m_pAnnotEditable = ToCPDFSDKWidget(iter.GetFirstAnnot()); ++ ASSERT_TRUE(m_pAnnotEditable); + +- // Type into editable combobox text field and select new text. +- EXPECT_TRUE( +- GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(), 'a', 0)); +- EXPECT_TRUE( +- GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(), 'b', 0)); +- EXPECT_TRUE( +- GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(), 'c', 0)); ++ // Normal combobox with pre-selected value. ++ m_pAnnotNormal = ToCPDFSDKWidget(iter.GetNextAnnot(m_pAnnotEditable)); ++ ASSERT_TRUE(m_pAnnotNormal); + +- EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); +- GetCPWLComboBox()->SetEditSelection(0, 5); +- EXPECT_STREQ(L"Baabc", GetCPWLComboBox()->GetSelectedText().c_str()); ++ // Read-only combobox. ++ CPDFSDK_Annot* pAnnotReadOnly = iter.GetNextAnnot(m_pAnnotNormal); ++ CPDFSDK_Annot* pLastAnnot = iter.GetLastAnnot(); ++ ASSERT_EQ(pAnnotReadOnly, pLastAnnot); + } + +-TEST_F(CPWLComboBoxEditEmbedderTest, GetSelectedTextFragmentsEditable) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); +- TypeTextIntoTextField(50); +- +- GetCPWLComboBox()->SetEditSelection(0, 0); +- EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); +- +- GetCPWLComboBox()->SetEditSelection(0, 1); +- EXPECT_STREQ(L"A", GetCPWLComboBox()->GetSelectedText().c_str()); +- +- GetCPWLComboBox()->SetEditSelection(0, -1); +- EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", +- GetCPWLComboBox()->GetSelectedText().c_str()); +- +- GetCPWLComboBox()->SetEditSelection(-8, -1); +- EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); +- +- GetCPWLComboBox()->SetEditSelection(23, 12); +- EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLComboBox()->GetSelectedText().c_str()); +- +- GetCPWLComboBox()->SetEditSelection(12, 23); +- EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLComboBox()->GetSelectedText().c_str()); +- +- GetCPWLComboBox()->SetEditSelection(49, 50); +- EXPECT_STREQ(L"r", GetCPWLComboBox()->GetSelectedText().c_str()); +- +- GetCPWLComboBox()->SetEditSelection(49, 55); +- EXPECT_STREQ(L"r", GetCPWLComboBox()->GetSelectedText().c_str()); +-} +- +-TEST_F(CPWLComboBoxEditEmbedderTest, DeleteEntireTextSelection) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); +- TypeTextIntoTextField(50); +- +- GetCPWLComboBox()->SetEditSelection(0, -1); +- EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", +- GetCPWLComboBox()->GetSelectedText().c_str()); +- +- GetCPWLComboBox()->ReplaceSelection(L""); +- EXPECT_TRUE(GetCPWLComboBox()->GetText().IsEmpty()); +-} +- +-TEST_F(CPWLComboBoxEditEmbedderTest, DeleteTextSelectionMiddle) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); +- TypeTextIntoTextField(50); +- +- GetCPWLComboBox()->SetEditSelection(12, 23); +- EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLComboBox()->GetSelectedText().c_str()); +- +- GetCPWLComboBox()->ReplaceSelection(L""); +- EXPECT_STREQ(L"ABCDEFGHIJKLXYZ[\\]^_`abcdefghijklmnopqr", +- GetCPWLComboBox()->GetText().c_str()); +-} +- +-TEST_F(CPWLComboBoxEditEmbedderTest, DeleteTextSelectionLeft) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); +- TypeTextIntoTextField(50); +- +- GetCPWLComboBox()->SetEditSelection(0, 5); +- EXPECT_STREQ(L"ABCDE", GetCPWLComboBox()->GetSelectedText().c_str()); +- +- GetCPWLComboBox()->ReplaceSelection(L""); +- EXPECT_STREQ(L"FGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", +- GetCPWLComboBox()->GetText().c_str()); +-} +- +-TEST_F(CPWLComboBoxEditEmbedderTest, DeleteTextSelectionRight) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); +- TypeTextIntoTextField(50); +- +- GetCPWLComboBox()->SetEditSelection(45, 50); +- EXPECT_STREQ(L"nopqr", GetCPWLComboBox()->GetSelectedText().c_str()); +- +- GetCPWLComboBox()->ReplaceSelection(L""); +- EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklm", +- GetCPWLComboBox()->GetText().c_str()); +-} +- +-TEST_F(CPWLComboBoxEditEmbedderTest, DeleteEmptyTextSelection) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); +- TypeTextIntoTextField(50); +- +- GetCPWLComboBox()->ReplaceSelection(L""); +- EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", +- GetCPWLComboBox()->GetText().c_str()); +-} +- +-TEST_F(CPWLComboBoxEditEmbedderTest, InsertTextInEmptyEditableComboBox) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); +- GetCPWLComboBox()->ReplaceSelection(L"Hello"); +- EXPECT_STREQ(L"Hello", GetCPWLComboBox()->GetText().c_str()); +-} +- +-TEST_F(CPWLComboBoxEditEmbedderTest, +- InsertTextInPopulatedEditableComboBoxLeft) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); +- TypeTextIntoTextField(10); +- +- // Move cursor to beginning of user-editable combobox text field. +- EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Home, 0)); +- +- GetCPWLComboBox()->ReplaceSelection(L"Hello"); +- EXPECT_STREQ(L"HelloABCDEFGHIJ", GetCPWLComboBox()->GetText().c_str()); +-} +- +-TEST_F(CPWLComboBoxEditEmbedderTest, +- InsertTextInPopulatedEditableComboBoxMiddle) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); +- TypeTextIntoTextField(10); +- +- // Move cursor to middle of user-editable combobox text field. +- for (int i = 0; i < 5; ++i) { +- EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Left, 0)); ++void CPWLComboBoxEmbedderTest::FormFillerAndWindowSetup( ++ CPDFSDK_Widget* pAnnotCombobox) { ++ CFFL_InteractiveFormFiller* pInteractiveFormFiller = ++ m_pFormFillEnv->GetInteractiveFormFiller(); ++ { ++ ObservedPtr pObserved(pAnnotCombobox); ++ EXPECT_TRUE(pInteractiveFormFiller->OnSetFocus(pObserved, {})); + } + +- GetCPWLComboBox()->ReplaceSelection(L"Hello"); +- EXPECT_STREQ(L"ABCDEHelloFGHIJ", GetCPWLComboBox()->GetText().c_str()); +-} +- +-TEST_F(CPWLComboBoxEditEmbedderTest, +- InsertTextInPopulatedEditableComboBoxRight) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); +- TypeTextIntoTextField(10); +- +- GetCPWLComboBox()->ReplaceSelection(L"Hello"); +- EXPECT_STREQ(L"ABCDEFGHIJHello", GetCPWLComboBox()->GetText().c_str()); +-} +- +-TEST_F(CPWLComboBoxEditEmbedderTest, +- InsertTextAndReplaceSelectionInPopulatedEditableComboBoxWhole) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); +- TypeTextIntoTextField(10); +- +- GetCPWLComboBox()->SetEditSelection(0, -1); +- EXPECT_STREQ(L"ABCDEFGHIJ", GetCPWLComboBox()->GetSelectedText().c_str()); +- GetCPWLComboBox()->ReplaceSelection(L"Hello"); +- EXPECT_STREQ(L"Hello", GetCPWLComboBox()->GetText().c_str()); +-} +- +-TEST_F(CPWLComboBoxEditEmbedderTest, +- InsertTextAndReplaceSelectionInPopulatedEditableComboBoxLeft) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); +- TypeTextIntoTextField(10); ++ m_pFormField = pInteractiveFormFiller->GetFormFieldForTesting(pAnnotCombobox); ++ ASSERT_TRUE(m_pFormField); + +- GetCPWLComboBox()->SetEditSelection(0, 5); +- EXPECT_STREQ(L"ABCDE", GetCPWLComboBox()->GetSelectedText().c_str()); +- GetCPWLComboBox()->ReplaceSelection(L"Hello"); +- EXPECT_STREQ(L"HelloFGHIJ", GetCPWLComboBox()->GetText().c_str()); ++ CPWL_Wnd* pWindow = ++ m_pFormField->GetPWLWindow(m_pFormFillEnv->GetPageViewAtIndex(0)); ++ ASSERT_TRUE(pWindow); ++ m_pComboBox = static_cast(pWindow); + } + +-TEST_F(CPWLComboBoxEditEmbedderTest, +- InsertTextAndReplaceSelectionInPopulatedEditableComboBoxMiddle) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); +- TypeTextIntoTextField(10); +- +- GetCPWLComboBox()->SetEditSelection(2, 7); +- EXPECT_STREQ(L"CDEFG", GetCPWLComboBox()->GetSelectedText().c_str()); +- GetCPWLComboBox()->ReplaceSelection(L"Hello"); +- EXPECT_STREQ(L"ABHelloHIJ", GetCPWLComboBox()->GetText().c_str()); +-} +- +-TEST_F(CPWLComboBoxEditEmbedderTest, +- InsertTextAndReplaceSelectionInPopulatedEditableComboBoxRight) { +- FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); +- TypeTextIntoTextField(10); +- +- GetCPWLComboBox()->SetEditSelection(5, 10); +- EXPECT_STREQ(L"FGHIJ", GetCPWLComboBox()->GetSelectedText().c_str()); +- GetCPWLComboBox()->ReplaceSelection(L"Hello"); +- EXPECT_STREQ(L"ABCDEHello", GetCPWLComboBox()->GetText().c_str()); ++void CPWLComboBoxEmbedderTest::TypeTextIntoTextField(int num_chars) { ++ // Type text starting with 'A' to as many chars as specified by |num_chars|. ++ for (int i = 0; i < num_chars; ++i) { ++ EXPECT_TRUE( ++ GetCFFLFormField()->OnChar(GetCPDFSDKAnnotUserEditable(), i + 'A', {})); ++ } + } +diff --git a/fpdfsdk/pwl/cpwl_combo_box_embeddertest.h b/fpdfsdk/pwl/cpwl_combo_box_embeddertest.h +new file mode 100644 +index 000000000..eaf0678fe +--- /dev/null ++++ b/fpdfsdk/pwl/cpwl_combo_box_embeddertest.h +@@ -0,0 +1,48 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef FPDFSDK_PWL_CPWL_COMBO_BOX_EMBEDDERTEST_H_ ++#define FPDFSDK_PWL_CPWL_COMBO_BOX_EMBEDDERTEST_H_ ++ ++#include "public/fpdfview.h" ++#include "testing/embedder_test.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++class CFFL_FormField; ++class CPDFSDK_FormFillEnvironment; ++class CPDFSDK_PageView; ++class CPDFSDK_Widget; ++class CPWL_ComboBox; ++ ++class CPWLComboBoxEmbedderTest : public EmbedderTest { ++ protected: ++ void SetUp() override; ++ void TearDown() override; ++ ++ void CreateAndInitializeFormComboboxPDF(); ++ void FormFillerAndWindowSetup(CPDFSDK_Widget* pAnnotCombobox); ++ void TypeTextIntoTextField(int num_chars); ++ FPDF_PAGE GetPage() const { return m_page; } ++ CPWL_ComboBox* GetCPWLComboBox() const { return m_pComboBox; } ++ CFFL_FormField* GetCFFLFormField() const { return m_pFormField; } ++ CPDFSDK_Widget* GetCPDFSDKAnnotNormal() const { return m_pAnnotNormal; } ++ CPDFSDK_Widget* GetCPDFSDKAnnotUserEditable() const { ++ return m_pAnnotEditable; ++ } ++ CPDFSDK_FormFillEnvironment* GetCPDFSDKFormFillEnv() const { ++ return m_pFormFillEnv; ++ } ++ CPDFSDK_PageView* GetPageView() const { return m_pPageView; } ++ ++ private: ++ FPDF_PAGE m_page; ++ CPWL_ComboBox* m_pComboBox = nullptr; ++ CFFL_FormField* m_pFormField = nullptr; ++ CPDFSDK_Widget* m_pAnnotNormal = nullptr; ++ CPDFSDK_Widget* m_pAnnotEditable = nullptr; ++ CPDFSDK_FormFillEnvironment* m_pFormFillEnv = nullptr; ++ CPDFSDK_PageView* m_pPageView = nullptr; ++}; ++ ++#endif // FPDFSDK_PWL_CPWL_COMBO_BOX_EMBEDDERTEST_H_ +diff --git a/fpdfsdk/pwl/cpwl_edit.cpp b/fpdfsdk/pwl/cpwl_edit.cpp +index 77491dad6..e41b339c4 100644 +--- a/fpdfsdk/pwl/cpwl_edit.cpp ++++ b/fpdfsdk/pwl/cpwl_edit.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,34 +10,40 @@ + #include + #include + #include +-#include + ++#include "constants/ascii.h" + #include "core/fpdfapi/font/cpdf_font.h" + #include "core/fpdfdoc/cpvt_word.h" + #include "core/fpdfdoc/ipvt_fontmap.h" + #include "core/fxcrt/fx_safe_types.h" ++#include "core/fxge/cfx_fillrenderoptions.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/cfx_renderdevice.h" + #include "core/fxge/fx_font.h" + #include "fpdfsdk/pwl/cpwl_caret.h" +-#include "fpdfsdk/pwl/cpwl_edit_ctrl.h" + #include "fpdfsdk/pwl/cpwl_edit_impl.h" + #include "fpdfsdk/pwl/cpwl_scroll_bar.h" + #include "fpdfsdk/pwl/cpwl_wnd.h" ++#include "fpdfsdk/pwl/ipwl_fillernotify.h" + #include "public/fpdf_fwlevent.h" ++#include "third_party/base/check.h" + + CPWL_Edit::CPWL_Edit( + const CreateParams& cp, +- std::unique_ptr pAttachedData) +- : CPWL_EditCtrl(cp, std::move(pAttachedData)) {} ++ std::unique_ptr pAttachedData) ++ : CPWL_Wnd(cp, std::move(pAttachedData)), ++ m_pEditImpl(std::make_unique()) { ++ GetCreationParams()->eCursorType = IPWL_FillerNotify::CursorStyle::kVBeam; ++} + + CPWL_Edit::~CPWL_Edit() { +- ASSERT(!m_bFocus); ++ DCHECK(!m_bFocus); + } + + void CPWL_Edit::SetText(const WideString& csText) { +- m_pEdit->SetText(csText); ++ m_pEditImpl->SetText(csText); ++ m_pEditImpl->Paint(); + } + + bool CPWL_Edit::RePosChildWnd() { +@@ -45,7 +51,7 @@ bool CPWL_Edit::RePosChildWnd() { + CFX_FloatRect rcWindow = m_rcOldWindow; + CFX_FloatRect rcVScroll = + CFX_FloatRect(rcWindow.right, rcWindow.bottom, +- rcWindow.right + PWL_SCROLLBAR_WIDTH, rcWindow.top); ++ rcWindow.right + CPWL_ScrollBar::kWidth, rcWindow.top); + + ObservedPtr thisObserved(this); + pVSB->Move(rcVScroll, true, false); +@@ -53,55 +59,58 @@ bool CPWL_Edit::RePosChildWnd() { + return false; + } + +- if (m_pEditCaret && !HasFlag(PES_TEXTOVERFLOW)) { ++ if (m_pCaret && !HasFlag(PES_TEXTOVERFLOW)) { + CFX_FloatRect rect = GetClientRect(); + if (!rect.IsEmpty()) { + // +1 for caret beside border + rect.Inflate(1.0f, 1.0f); + rect.Normalize(); + } +- m_pEditCaret->SetClipRect(rect); ++ m_pCaret->SetClipRect(rect); + } + +- return CPWL_EditCtrl::RePosChildWnd(); ++ m_pEditImpl->SetPlateRect(GetClientRect()); ++ m_pEditImpl->Paint(); ++ return true; + } + + CFX_FloatRect CPWL_Edit::GetClientRect() const { + float width = static_cast(GetBorderWidth() + GetInnerBorderWidth()); + CFX_FloatRect rcClient = GetWindowRect().GetDeflated(width, width); +- if (CPWL_ScrollBar* pVSB = GetVScrollBar()) { +- if (pVSB->IsVisible()) { +- rcClient.right -= PWL_SCROLLBAR_WIDTH; +- } +- } +- ++ CPWL_ScrollBar* pVSB = GetVScrollBar(); ++ if (pVSB && pVSB->IsVisible()) ++ rcClient.right -= CPWL_ScrollBar::kWidth; + return rcClient; + } + + void CPWL_Edit::SetAlignFormatVerticalCenter() { +- m_pEdit->SetAlignmentV(static_cast(PEAV_CENTER), true); ++ m_pEditImpl->SetAlignmentV(static_cast(PEAV_CENTER)); ++ m_pEditImpl->Paint(); + } + + bool CPWL_Edit::CanSelectAll() const { +- return GetSelectWordRange() != m_pEdit->GetWholeWordRange(); ++ return GetSelectWordRange() != m_pEditImpl->GetWholeWordRange(); + } + + bool CPWL_Edit::CanCopy() const { +- return !HasFlag(PES_PASSWORD) && !HasFlag(PES_NOREAD) && +- m_pEdit->IsSelected(); ++ return !HasFlag(PES_PASSWORD) && m_pEditImpl->IsSelected(); + } + + bool CPWL_Edit::CanCut() const { + return CanCopy() && !IsReadOnly(); + } ++ + void CPWL_Edit::CutText() { + if (!CanCut()) + return; +- m_pEdit->ClearSelection(); ++ m_pEditImpl->ClearSelection(); + } + + void CPWL_Edit::OnCreated() { +- CPWL_EditCtrl::OnCreated(); ++ SetFontSize(GetCreationParams()->fFontSize); ++ m_pEditImpl->SetFontMap(GetFontMap()); ++ m_pEditImpl->SetNotify(this); ++ m_pEditImpl->Initialize(); + + if (CPWL_ScrollBar* pScroll = GetVScrollBar()) { + pScroll->RemoveFlag(PWS_AUTOTRANSPARENT); +@@ -109,51 +118,46 @@ void CPWL_Edit::OnCreated() { + } + + SetParamByFlag(); +- + m_rcOldWindow = GetWindowRect(); +- +- m_pEdit->SetOperationNotify(this); + } + + void CPWL_Edit::SetParamByFlag() { + if (HasFlag(PES_RIGHT)) { +- m_pEdit->SetAlignmentH(2, false); ++ m_pEditImpl->SetAlignmentH(2); + } else if (HasFlag(PES_MIDDLE)) { +- m_pEdit->SetAlignmentH(1, false); ++ m_pEditImpl->SetAlignmentH(1); + } else { +- m_pEdit->SetAlignmentH(0, false); ++ m_pEditImpl->SetAlignmentH(0); + } + +- if (HasFlag(PES_BOTTOM)) { +- m_pEdit->SetAlignmentV(2, false); +- } else if (HasFlag(PES_CENTER)) { +- m_pEdit->SetAlignmentV(1, false); ++ if (HasFlag(PES_CENTER)) { ++ m_pEditImpl->SetAlignmentV(1); + } else { +- m_pEdit->SetAlignmentV(0, false); ++ m_pEditImpl->SetAlignmentV(0); + } + + if (HasFlag(PES_PASSWORD)) { +- m_pEdit->SetPasswordChar('*', false); ++ m_pEditImpl->SetPasswordChar('*'); + } + +- m_pEdit->SetMultiLine(HasFlag(PES_MULTILINE), false); +- m_pEdit->SetAutoReturn(HasFlag(PES_AUTORETURN), false); +- m_pEdit->SetAutoFontSize(HasFlag(PWS_AUTOFONTSIZE), false); +- m_pEdit->SetAutoScroll(HasFlag(PES_AUTOSCROLL), false); +- m_pEdit->EnableUndo(HasFlag(PES_UNDO)); ++ m_pEditImpl->SetMultiLine(HasFlag(PES_MULTILINE)); ++ m_pEditImpl->SetAutoReturn(HasFlag(PES_AUTORETURN)); ++ m_pEditImpl->SetAutoFontSize(HasFlag(PWS_AUTOFONTSIZE)); ++ m_pEditImpl->SetAutoScroll(HasFlag(PES_AUTOSCROLL)); ++ m_pEditImpl->EnableUndo(HasFlag(PES_UNDO)); + + if (HasFlag(PES_TEXTOVERFLOW)) { + SetClipRect(CFX_FloatRect()); +- m_pEdit->SetTextOverflow(true, false); ++ m_pEditImpl->SetTextOverflow(true); + } else { +- if (m_pEditCaret) { ++ if (m_pCaret) { + CFX_FloatRect rect = GetClientRect(); + if (!rect.IsEmpty()) { + // +1 for caret beside border + rect.Inflate(1.0f, 1.0f); + rect.Normalize(); + } +- m_pEditCaret->SetClipRect(rect); ++ m_pCaret->SetClipRect(rect); + } + } + } +@@ -162,126 +166,54 @@ void CPWL_Edit::DrawThisAppearance(CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device) { + CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device); + +- CFX_FloatRect rcClient = GetClientRect(); +- +- int32_t nCharArray = m_pEdit->GetCharArray(); +- FX_SAFE_INT32 nCharArraySafe = nCharArray; +- nCharArraySafe -= 1; +- nCharArraySafe *= 2; +- +- if (nCharArray > 0 && nCharArraySafe.IsValid()) { +- switch (GetBorderStyle()) { +- case BorderStyle::SOLID: { +- CFX_GraphStateData gsd; +- gsd.m_LineWidth = GetBorderWidth(); +- +- CFX_PathData path; +- +- for (int32_t i = 0; i < nCharArray - 1; i++) { +- path.AppendPoint( +- CFX_PointF( +- rcClient.left + +- ((rcClient.right - rcClient.left) / nCharArray) * (i + 1), +- rcClient.bottom), +- FXPT_TYPE::MoveTo, false); +- path.AppendPoint( +- CFX_PointF( +- rcClient.left + +- ((rcClient.right - rcClient.left) / nCharArray) * (i + 1), +- rcClient.top), +- FXPT_TYPE::LineTo, false); +- } +- if (!path.GetPoints().empty()) { +- pDevice->DrawPath(&path, &mtUser2Device, &gsd, 0, +- GetBorderColor().ToFXColor(255), FXFILL_ALTERNATE); +- } +- break; +- } +- case BorderStyle::DASH: { +- CFX_GraphStateData gsd; +- gsd.m_LineWidth = static_cast(GetBorderWidth()); +- gsd.m_DashArray = {static_cast(GetBorderDash().nDash), +- static_cast(GetBorderDash().nGap)}; +- gsd.m_DashPhase = static_cast(GetBorderDash().nPhase); +- +- CFX_PathData path; +- for (int32_t i = 0; i < nCharArray - 1; i++) { +- path.AppendPoint( +- CFX_PointF( +- rcClient.left + +- ((rcClient.right - rcClient.left) / nCharArray) * (i + 1), +- rcClient.bottom), +- FXPT_TYPE::MoveTo, false); +- path.AppendPoint( +- CFX_PointF( +- rcClient.left + +- ((rcClient.right - rcClient.left) / nCharArray) * (i + 1), +- rcClient.top), +- FXPT_TYPE::LineTo, false); +- } +- if (!path.GetPoints().empty()) { +- pDevice->DrawPath(&path, &mtUser2Device, &gsd, 0, +- GetBorderColor().ToFXColor(255), FXFILL_ALTERNATE); +- } +- break; +- } +- default: +- break; ++ const CFX_FloatRect rcClient = GetClientRect(); ++ const BorderStyle border_style = GetBorderStyle(); ++ const int32_t nCharArray = m_pEditImpl->GetCharArray(); ++ bool draw_border = nCharArray > 0 && (border_style == BorderStyle::kSolid || ++ border_style == BorderStyle::kDash); ++ if (draw_border) { ++ FX_SAFE_INT32 nCharArraySafe = nCharArray; ++ nCharArraySafe -= 1; ++ nCharArraySafe *= 2; ++ draw_border = nCharArraySafe.IsValid(); ++ } ++ ++ if (draw_border) { ++ CFX_GraphStateData gsd; ++ gsd.m_LineWidth = GetBorderWidth(); ++ if (border_style == BorderStyle::kDash) { ++ gsd.m_DashArray = {static_cast(GetBorderDash().nDash), ++ static_cast(GetBorderDash().nGap)}; ++ gsd.m_DashPhase = GetBorderDash().nPhase; ++ } ++ ++ const float width = (rcClient.right - rcClient.left) / nCharArray; ++ CFX_Path path; ++ CFX_PointF bottom(0, rcClient.bottom); ++ CFX_PointF top(0, rcClient.top); ++ for (int32_t i = 0; i < nCharArray - 1; ++i) { ++ bottom.x = rcClient.left + width * (i + 1); ++ top.x = bottom.x; ++ path.AppendPoint(bottom, CFX_Path::Point::Type::kMove); ++ path.AppendPoint(top, CFX_Path::Point::Type::kLine); ++ } ++ if (!path.GetPoints().empty()) { ++ pDevice->DrawPath(path, &mtUser2Device, &gsd, 0, ++ GetBorderColor().ToFXColor(255), ++ CFX_FillRenderOptions::EvenOddOptions()); + } + } + + CFX_FloatRect rcClip; +- CPVT_WordRange wrRange = m_pEdit->GetVisibleWordRange(); ++ CPVT_WordRange wrRange = m_pEditImpl->GetVisibleWordRange(); + CPVT_WordRange* pRange = nullptr; + if (!HasFlag(PES_TEXTOVERFLOW)) { + rcClip = GetClientRect(); + pRange = &wrRange; + } +- +- CPWL_EditImpl::DrawEdit(pDevice, mtUser2Device, m_pEdit.get(), +- GetTextColor().ToFXColor(GetTransparency()), rcClip, +- CFX_PointF(), pRange, GetSystemHandler(), +- m_pFormFiller.Get()); +-} +- +-bool CPWL_Edit::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnLButtonDown(point, nFlag); +- +- if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) { +- if (m_bMouseDown && !InvalidateRect(nullptr)) +- return true; +- +- m_bMouseDown = true; +- SetCapture(); +- +- m_pEdit->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); +- } +- +- return true; +-} +- +-bool CPWL_Edit::OnLButtonDblClk(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnLButtonDblClk(point, nFlag); +- +- if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) { +- m_pEdit->SelectAll(); +- } +- +- return true; +-} +- +-bool CPWL_Edit::OnRButtonUp(const CFX_PointF& point, uint32_t nFlag) { +- if (m_bMouseDown) +- return false; +- +- CPWL_Wnd::OnRButtonUp(point, nFlag); +- +- if (!HasFlag(PES_TEXTOVERFLOW) && !ClientHitTest(point)) +- return true; +- +- SetFocus(); +- +- return false; ++ m_pEditImpl->DrawEdit( ++ pDevice, mtUser2Device, GetTextColor().ToFXColor(GetTransparency()), ++ rcClip, CFX_PointF(), pRange, GetFillerNotify(), GetAttachedData()); + } + + void CPWL_Edit::OnSetFocus() { +@@ -291,8 +223,9 @@ void CPWL_Edit::OnSetFocus() { + return; + + if (!IsReadOnly()) { +- if (CPWL_Wnd::FocusHandlerIface* pFocusHandler = GetFocusHandler()) { +- pFocusHandler->OnSetFocus(this); ++ CPWL_Wnd::ProviderIface* pProvider = GetProvider(); ++ if (pProvider) { ++ pProvider->OnSetFocusForEdit(this); + if (!observed_ptr) + return; + } +@@ -312,51 +245,32 @@ void CPWL_Edit::OnKillFocus() { + return; + } + +- m_pEdit->SelectNone(); ++ m_pEditImpl->SelectNone(); + if (!observed_ptr) + return; + + if (!SetCaret(false, CFX_PointF(), CFX_PointF())) + return; + +- SetCharSet(FX_CHARSET_ANSI); ++ SetCharSet(FX_Charset::kANSI); + m_bFocus = false; + } + +-void CPWL_Edit::SetCharSpace(float fCharSpace) { +- m_pEdit->SetCharSpace(fCharSpace); +-} +- + CPVT_WordRange CPWL_Edit::GetSelectWordRange() const { +- if (!m_pEdit->IsSelected()) ++ if (!m_pEditImpl->IsSelected()) + return CPVT_WordRange(); + +- int32_t nStart = -1; +- int32_t nEnd = -1; +- +- m_pEdit->GetSelection(nStart, nEnd); +- +- CPVT_WordPlace wpStart = m_pEdit->WordIndexToWordPlace(nStart); +- CPVT_WordPlace wpEnd = m_pEdit->WordIndexToWordPlace(nEnd); ++ int32_t nStart; ++ int32_t nEnd; ++ std::tie(nStart, nEnd) = m_pEditImpl->GetSelection(); + ++ CPVT_WordPlace wpStart = m_pEditImpl->WordIndexToWordPlace(nStart); ++ CPVT_WordPlace wpEnd = m_pEditImpl->WordIndexToWordPlace(nEnd); + return CPVT_WordRange(wpStart, wpEnd); + } + +-CFX_PointF CPWL_Edit::GetWordRightBottomPoint(const CPVT_WordPlace& wpWord) { +- CPWL_EditImpl_Iterator* pIterator = m_pEdit->GetIterator(); +- CPVT_WordPlace wpOld = pIterator->GetAt(); +- pIterator->SetAt(wpWord); +- +- CFX_PointF pt; +- CPVT_Word word; +- if (pIterator->GetWord(word)) +- pt = CFX_PointF(word.ptWord.x + word.fWidth, word.ptWord.y + word.fDescent); +- pIterator->SetAt(wpOld); +- return pt; +-} +- + bool CPWL_Edit::IsTextFull() const { +- return m_pEdit->IsTextFull(); ++ return m_pEditImpl->IsTextFull(); + } + + float CPWL_Edit::GetCharArrayAutoFontSize(const CPDF_Font* pFont, +@@ -378,8 +292,9 @@ void CPWL_Edit::SetCharArray(int32_t nCharArray) { + if (!HasFlag(PES_CHARARRAY) || nCharArray <= 0) + return; + +- m_pEdit->SetCharArray(nCharArray); +- m_pEdit->SetTextOverflow(true, true); ++ m_pEditImpl->SetCharArray(nCharArray); ++ m_pEditImpl->SetTextOverflow(true); ++ m_pEditImpl->Paint(); + + if (!HasFlag(PWS_AUTOFONTSIZE)) + return; +@@ -393,12 +308,14 @@ void CPWL_Edit::SetCharArray(int32_t nCharArray) { + if (fFontSize <= 0.0f) + return; + +- m_pEdit->SetAutoFontSize(false, true); +- m_pEdit->SetFontSize(fFontSize); ++ m_pEditImpl->SetAutoFontSize(false); ++ m_pEditImpl->SetFontSize(fFontSize); ++ m_pEditImpl->Paint(); + } + + void CPWL_Edit::SetLimitChar(int32_t nLimitChar) { +- m_pEdit->SetLimitChar(nLimitChar); ++ m_pEditImpl->SetLimitChar(nLimitChar); ++ m_pEditImpl->Paint(); + } + + CFX_FloatRect CPWL_Edit::GetFocusRect() const { +@@ -410,61 +327,60 @@ bool CPWL_Edit::IsVScrollBarVisible() const { + return pScroll && pScroll->IsVisible(); + } + +-bool CPWL_Edit::OnKeyDown(uint16_t nChar, uint32_t nFlag) { ++bool CPWL_Edit::OnKeyDown(FWL_VKEYCODE nKeyCode, Mask nFlag) { + if (m_bMouseDown) + return true; + +- if (nChar == FWL_VKEY_Delete) { +- if (m_pFillerNotify) { +- WideString strChange; +- WideString strChangeEx; ++ if (nKeyCode == FWL_VKEY_Delete) { ++ WideString strChange; ++ WideString strChangeEx; + +- int nSelStart = 0; +- int nSelEnd = 0; +- GetSelection(nSelStart, nSelEnd); ++ int nSelStart; ++ int nSelEnd; ++ std::tie(nSelStart, nSelEnd) = GetSelection(); + +- if (nSelStart == nSelEnd) +- nSelEnd = nSelStart + 1; ++ if (nSelStart == nSelEnd) ++ nSelEnd = nSelStart + 1; + +- ObservedPtr thisObserved(this); ++ ObservedPtr thisObserved(this); + +- bool bRC; +- bool bExit; +- std::tie(bRC, bExit) = m_pFillerNotify->OnBeforeKeyStroke( +- GetAttachedData(), strChange, strChangeEx, nSelStart, nSelEnd, true, +- nFlag); ++ bool bRC; ++ bool bExit; ++ std::tie(bRC, bExit) = GetFillerNotify()->OnBeforeKeyStroke( ++ GetAttachedData(), strChange, strChangeEx, nSelStart, nSelEnd, true, ++ nFlag); + +- if (!thisObserved) +- return false; ++ if (!thisObserved) ++ return false; + +- if (!bRC) +- return false; +- if (bExit) +- return false; +- } ++ if (!bRC) ++ return false; ++ if (bExit) ++ return false; + } + +- bool bRet = CPWL_EditCtrl::OnKeyDown(nChar, nFlag); ++ bool bRet = OnKeyDownInternal(nKeyCode, nFlag); + + // In case of implementation swallow the OnKeyDown event. +- if (IsProceedtoOnChar(nChar, nFlag)) ++ if (IsProceedtoOnChar(nKeyCode, nFlag)) + return true; + + return bRet; + } + + // static +-bool CPWL_Edit::IsProceedtoOnChar(uint16_t nKeyCode, uint32_t nFlag) { +- bool bCtrl = IsCTRLpressed(nFlag); +- bool bAlt = IsALTpressed(nFlag); ++bool CPWL_Edit::IsProceedtoOnChar(FWL_VKEYCODE nKeyCode, ++ Mask nFlag) { ++ bool bCtrl = IsPlatformShortcutKey(nFlag); ++ bool bAlt = IsALTKeyDown(nFlag); + if (bCtrl && !bAlt) { + // hot keys for edit control. + switch (nKeyCode) { +- case 'C': +- case 'V': +- case 'X': +- case 'A': +- case 'Z': ++ case FWL_VKEY_A: ++ case FWL_VKEY_C: ++ case FWL_VKEY_V: ++ case FWL_VKEY_X: ++ case FWL_VKEY_Z: + return true; + default: + break; +@@ -482,43 +398,40 @@ bool CPWL_Edit::IsProceedtoOnChar(uint16_t nKeyCode, uint32_t nFlag) { + } + } + +-bool CPWL_Edit::OnChar(uint16_t nChar, uint32_t nFlag) { ++bool CPWL_Edit::OnChar(uint16_t nChar, Mask nFlag) { + if (m_bMouseDown) + return true; + + bool bRC = true; + bool bExit = false; + +- if (!IsCTRLpressed(nFlag)) { +- if (m_pFillerNotify) { +- WideString swChange; +- +- int nSelStart = 0; +- int nSelEnd = 0; +- GetSelection(nSelStart, nSelEnd); +- +- switch (nChar) { +- case FWL_VKEY_Back: +- if (nSelStart == nSelEnd) +- nSelStart = nSelEnd - 1; +- break; +- case FWL_VKEY_Return: +- break; +- default: +- swChange += nChar; +- break; +- } ++ if (!IsCTRLKeyDown(nFlag)) { ++ WideString swChange; ++ int nSelStart; ++ int nSelEnd; ++ std::tie(nSelStart, nSelEnd) = GetSelection(); ++ ++ switch (nChar) { ++ case pdfium::ascii::kBackspace: ++ if (nSelStart == nSelEnd) ++ nSelStart = nSelEnd - 1; ++ break; ++ case pdfium::ascii::kReturn: ++ break; ++ default: ++ swChange += nChar; ++ break; ++ } + +- ObservedPtr thisObserved(this); ++ ObservedPtr thisObserved(this); + +- WideString strChangeEx; +- std::tie(bRC, bExit) = m_pFillerNotify->OnBeforeKeyStroke( +- GetAttachedData(), swChange, strChangeEx, nSelStart, nSelEnd, true, +- nFlag); ++ WideString strChangeEx; ++ std::tie(bRC, bExit) = GetFillerNotify()->OnBeforeKeyStroke( ++ GetAttachedData(), swChange, strChangeEx, nSelStart, nSelEnd, true, ++ nFlag); + +- if (!thisObserved) +- return false; +- } ++ if (!thisObserved) ++ return false; + } + + if (!bRC) +@@ -527,25 +440,25 @@ bool CPWL_Edit::OnChar(uint16_t nChar, uint32_t nFlag) { + return false; + + if (IPVT_FontMap* pFontMap = GetFontMap()) { +- int32_t nOldCharSet = GetCharSet(); +- int32_t nNewCharSet = +- pFontMap->CharSetFromUnicode(nChar, FX_CHARSET_Default); ++ FX_Charset nOldCharSet = GetCharSet(); ++ FX_Charset nNewCharSet = ++ pFontMap->CharSetFromUnicode(nChar, FX_Charset::kDefault); + if (nOldCharSet != nNewCharSet) { + SetCharSet(nNewCharSet); + } + } + +- return CPWL_EditCtrl::OnChar(nChar, nFlag); ++ return OnCharInternal(nChar, nFlag); + } + +-bool CPWL_Edit::OnMouseWheel(short zDelta, ++bool CPWL_Edit::OnMouseWheel(Mask nFlag, + const CFX_PointF& point, +- uint32_t nFlag) { ++ const CFX_Vector& delta) { + if (!HasFlag(PES_MULTILINE)) + return false; + + CFX_PointF ptScroll = GetScrollPos(); +- if (zDelta > 0) ++ if (delta.y > 0) + ptScroll.y += GetFontSize(); + else + ptScroll.y -= GetFontSize(); +@@ -553,117 +466,401 @@ bool CPWL_Edit::OnMouseWheel(short zDelta, + return true; + } + +-void CPWL_Edit::OnInsertReturn(const CPVT_WordPlace& place, +- const CPVT_WordPlace& oldplace) { +- if (HasFlag(PES_SPELLCHECK)) { +- m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), +- GetLatinWordsRange(place))); ++void CPWL_Edit::OnDestroy() { ++ m_pCaret.ExtractAsDangling(); ++} ++ ++bool CPWL_Edit::IsWndHorV() const { ++ CFX_Matrix mt = GetWindowMatrix(); ++ return mt.Transform(CFX_PointF(1, 1)).y == mt.Transform(CFX_PointF(0, 1)).y; ++} ++ ++void CPWL_Edit::SetCursor() { ++ if (IsValid()) { ++ GetFillerNotify()->SetCursor(IsWndHorV() ++ ? IPWL_FillerNotify::CursorStyle::kVBeam ++ : IPWL_FillerNotify::CursorStyle::kHBeam); + } + } + +-void CPWL_Edit::OnBackSpace(const CPVT_WordPlace& place, +- const CPVT_WordPlace& oldplace) { +- if (HasFlag(PES_SPELLCHECK)) { +- m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), +- GetLatinWordsRange(place))); ++WideString CPWL_Edit::GetSelectedText() { ++ return m_pEditImpl->GetSelectedText(); ++} ++ ++void CPWL_Edit::ReplaceAndKeepSelection(const WideString& text) { ++ m_pEditImpl->ReplaceAndKeepSelection(text); ++} ++ ++void CPWL_Edit::ReplaceSelection(const WideString& text) { ++ m_pEditImpl->ReplaceSelection(text); ++} ++ ++bool CPWL_Edit::SelectAllText() { ++ m_pEditImpl->SelectAll(); ++ return true; ++} ++ ++void CPWL_Edit::SetScrollInfo(const PWL_SCROLL_INFO& info) { ++ if (CPWL_Wnd* pChild = GetVScrollBar()) ++ pChild->SetScrollInfo(info); ++} ++ ++void CPWL_Edit::SetScrollPosition(float pos) { ++ if (CPWL_Wnd* pChild = GetVScrollBar()) ++ pChild->SetScrollPosition(pos); ++} ++ ++void CPWL_Edit::ScrollWindowVertically(float pos) { ++ m_pEditImpl->SetScrollPos(CFX_PointF(m_pEditImpl->GetScrollPos().x, pos)); ++} ++ ++void CPWL_Edit::CreateChildWnd(const CreateParams& cp) { ++ if (!IsReadOnly()) ++ CreateEditCaret(cp); ++} ++ ++void CPWL_Edit::CreateEditCaret(const CreateParams& cp) { ++ if (m_pCaret) ++ return; ++ ++ CreateParams ecp = cp; ++ ecp.dwFlags = PWS_NOREFRESHCLIP; ++ ecp.dwBorderWidth = 0; ++ ecp.nBorderStyle = BorderStyle::kSolid; ++ ecp.rcRectWnd = CFX_FloatRect(); ++ ++ auto pCaret = std::make_unique(ecp, CloneAttachedData()); ++ m_pCaret = pCaret.get(); ++ m_pCaret->SetInvalidRect(GetClientRect()); ++ AddChild(std::move(pCaret)); ++ m_pCaret->Realize(); ++} ++ ++void CPWL_Edit::SetFontSize(float fFontSize) { ++ m_pEditImpl->SetFontSize(fFontSize); ++ m_pEditImpl->Paint(); ++} ++ ++float CPWL_Edit::GetFontSize() const { ++ return m_pEditImpl->GetFontSize(); ++} ++ ++bool CPWL_Edit::OnKeyDownInternal(FWL_VKEYCODE nKeyCode, ++ Mask nFlag) { ++ if (m_bMouseDown) ++ return true; ++ ++ bool bRet = CPWL_Wnd::OnKeyDown(nKeyCode, nFlag); ++ ++ // FILTER ++ switch (nKeyCode) { ++ default: ++ return false; ++ case FWL_VKEY_Delete: ++ case FWL_VKEY_Up: ++ case FWL_VKEY_Down: ++ case FWL_VKEY_Left: ++ case FWL_VKEY_Right: ++ case FWL_VKEY_Home: ++ case FWL_VKEY_End: ++ case FWL_VKEY_Insert: ++ case FWL_VKEY_A: ++ case FWL_VKEY_C: ++ case FWL_VKEY_V: ++ case FWL_VKEY_X: ++ case FWL_VKEY_Z: ++ break; ++ } ++ ++ if (nKeyCode == FWL_VKEY_Delete && m_pEditImpl->IsSelected()) ++ nKeyCode = FWL_VKEY_Unknown; ++ ++ switch (nKeyCode) { ++ case FWL_VKEY_Delete: ++ Delete(); ++ return true; ++ case FWL_VKEY_Insert: ++ if (IsSHIFTKeyDown(nFlag)) ++ PasteText(); ++ return true; ++ case FWL_VKEY_Up: ++ m_pEditImpl->OnVK_UP(IsSHIFTKeyDown(nFlag)); ++ return true; ++ case FWL_VKEY_Down: ++ m_pEditImpl->OnVK_DOWN(IsSHIFTKeyDown(nFlag)); ++ return true; ++ case FWL_VKEY_Left: ++ m_pEditImpl->OnVK_LEFT(IsSHIFTKeyDown(nFlag)); ++ return true; ++ case FWL_VKEY_Right: ++ m_pEditImpl->OnVK_RIGHT(IsSHIFTKeyDown(nFlag)); ++ return true; ++ case FWL_VKEY_Home: ++ m_pEditImpl->OnVK_HOME(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)); ++ return true; ++ case FWL_VKEY_End: ++ m_pEditImpl->OnVK_END(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)); ++ return true; ++ case FWL_VKEY_Unknown: ++ if (!IsSHIFTKeyDown(nFlag)) ++ ClearSelection(); ++ else ++ CutText(); ++ return true; ++ default: ++ break; + } ++ ++ return bRet; + } + +-void CPWL_Edit::OnDelete(const CPVT_WordPlace& place, +- const CPVT_WordPlace& oldplace) { +- if (HasFlag(PES_SPELLCHECK)) { +- m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), +- GetLatinWordsRange(place))); ++bool CPWL_Edit::OnCharInternal(uint16_t nChar, Mask nFlag) { ++ if (m_bMouseDown) ++ return true; ++ ++ CPWL_Wnd::OnChar(nChar, nFlag); ++ ++ // FILTER ++ switch (nChar) { ++ case pdfium::ascii::kNewline: ++ case pdfium::ascii::kEscape: ++ return false; ++ default: ++ break; ++ } ++ ++ bool bCtrl = IsPlatformShortcutKey(nFlag); ++ bool bAlt = IsALTKeyDown(nFlag); ++ bool bShift = IsSHIFTKeyDown(nFlag); ++ ++ uint16_t word = nChar; ++ ++ if (bCtrl && !bAlt) { ++ switch (nChar) { ++ case pdfium::ascii::kControlC: ++ CopyText(); ++ return true; ++ case pdfium::ascii::kControlV: ++ PasteText(); ++ return true; ++ case pdfium::ascii::kControlX: ++ CutText(); ++ return true; ++ case pdfium::ascii::kControlA: ++ SelectAllText(); ++ return true; ++ case pdfium::ascii::kControlZ: ++ if (bShift) ++ Redo(); ++ else ++ Undo(); ++ return true; ++ default: ++ if (nChar < 32) ++ return false; ++ } ++ } ++ ++ if (IsReadOnly()) ++ return true; ++ ++ if (m_pEditImpl->IsSelected() && word == pdfium::ascii::kBackspace) ++ word = pdfium::ascii::kNul; ++ ++ ClearSelection(); ++ ++ switch (word) { ++ case pdfium::ascii::kBackspace: ++ Backspace(); ++ break; ++ case pdfium::ascii::kReturn: ++ InsertReturn(); ++ break; ++ case pdfium::ascii::kNul: ++ break; ++ default: ++ InsertWord(word, GetCharSet()); ++ break; + } ++ ++ return true; + } + +-void CPWL_Edit::OnClear(const CPVT_WordPlace& place, +- const CPVT_WordPlace& oldplace) { +- if (HasFlag(PES_SPELLCHECK)) { +- m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), +- GetLatinWordsRange(place))); ++bool CPWL_Edit::OnLButtonDown(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnLButtonDown(nFlag, point); ++ if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) { ++ if (m_bMouseDown && !InvalidateRect(nullptr)) ++ return true; ++ ++ m_bMouseDown = true; ++ SetCapture(); ++ m_pEditImpl->OnMouseDown(point, IsSHIFTKeyDown(nFlag), ++ IsCTRLKeyDown(nFlag)); + } ++ return true; + } + +-void CPWL_Edit::OnInsertWord(const CPVT_WordPlace& place, +- const CPVT_WordPlace& oldplace) { +- if (HasFlag(PES_SPELLCHECK)) { +- m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), +- GetLatinWordsRange(place))); ++bool CPWL_Edit::OnLButtonUp(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnLButtonUp(nFlag, point); ++ if (m_bMouseDown) { ++ // can receive keybord message ++ if (ClientHitTest(point) && !IsFocused()) ++ SetFocus(); ++ ++ ReleaseCapture(); ++ m_bMouseDown = false; + } ++ return true; ++} ++ ++bool CPWL_Edit::OnLButtonDblClk(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnLButtonDblClk(nFlag, point); ++ if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) ++ m_pEditImpl->SelectAll(); ++ ++ return true; ++} ++ ++bool CPWL_Edit::OnRButtonUp(Mask nFlag, ++ const CFX_PointF& point) { ++ if (m_bMouseDown) ++ return false; ++ ++ CPWL_Wnd::OnRButtonUp(nFlag, point); ++ if (!HasFlag(PES_TEXTOVERFLOW) && !ClientHitTest(point)) ++ return true; ++ ++ SetFocus(); ++ return false; ++} ++ ++bool CPWL_Edit::OnMouseMove(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnMouseMove(nFlag, point); ++ ++ if (m_bMouseDown) ++ m_pEditImpl->OnMouseMove(point, false, false); ++ ++ return true; + } + +-void CPWL_Edit::OnInsertText(const CPVT_WordPlace& place, +- const CPVT_WordPlace& oldplace) { +- if (HasFlag(PES_SPELLCHECK)) { +- m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), +- GetLatinWordsRange(place))); ++void CPWL_Edit::SetEditCaret(bool bVisible) { ++ CFX_PointF ptHead; ++ CFX_PointF ptFoot; ++ if (bVisible) ++ GetCaretInfo(&ptHead, &ptFoot); ++ ++ SetCaret(bVisible, ptHead, ptFoot); ++ // Note, |this| may no longer be viable at this point. If more work needs to ++ // be done, check the return value of SetCaret(). ++} ++ ++void CPWL_Edit::GetCaretInfo(CFX_PointF* ptHead, CFX_PointF* ptFoot) const { ++ CPWL_EditImpl::Iterator* pIterator = m_pEditImpl->GetIterator(); ++ pIterator->SetAt(m_pEditImpl->GetCaret()); ++ CPVT_Word word; ++ CPVT_Line line; ++ if (pIterator->GetWord(word)) { ++ ptHead->x = word.ptWord.x + word.fWidth; ++ ptHead->y = word.ptWord.y + word.fAscent; ++ ptFoot->x = word.ptWord.x + word.fWidth; ++ ptFoot->y = word.ptWord.y + word.fDescent; ++ } else if (pIterator->GetLine(line)) { ++ ptHead->x = line.ptLine.x; ++ ptHead->y = line.ptLine.y + line.fLineAscent; ++ ptFoot->x = line.ptLine.x; ++ ptFoot->y = line.ptLine.y + line.fLineDescent; + } + } + +-CPVT_WordRange CPWL_Edit::CombineWordRange(const CPVT_WordRange& wr1, +- const CPVT_WordRange& wr2) { +- return CPVT_WordRange(std::min(wr1.BeginPos, wr2.BeginPos), +- std::max(wr1.EndPos, wr2.EndPos)); ++bool CPWL_Edit::SetCaret(bool bVisible, ++ const CFX_PointF& ptHead, ++ const CFX_PointF& ptFoot) { ++ if (!m_pCaret) ++ return true; ++ ++ if (!IsFocused() || m_pEditImpl->IsSelected()) ++ bVisible = false; ++ ++ ObservedPtr thisObserved(this); ++ m_pCaret->SetCaret(bVisible, ptHead, ptFoot); ++ if (!thisObserved) ++ return false; ++ ++ return true; ++} ++ ++WideString CPWL_Edit::GetText() { ++ return m_pEditImpl->GetText(); + } + +-CPVT_WordRange CPWL_Edit::GetLatinWordsRange(const CFX_PointF& point) const { +- return GetSameWordsRange(m_pEdit->SearchWordPlace(point), true, false); ++void CPWL_Edit::SetSelection(int32_t nStartChar, int32_t nEndChar) { ++ m_pEditImpl->SetSelection(nStartChar, nEndChar); + } + +-CPVT_WordRange CPWL_Edit::GetLatinWordsRange( +- const CPVT_WordPlace& place) const { +- return GetSameWordsRange(place, true, false); ++std::pair CPWL_Edit::GetSelection() const { ++ return m_pEditImpl->GetSelection(); + } + +-#define PWL_ISARABICWORD(word) \ +- ((word >= 0x0600 && word <= 0x06FF) || (word >= 0xFB50 && word <= 0xFEFC)) ++void CPWL_Edit::ClearSelection() { ++ if (!IsReadOnly()) ++ m_pEditImpl->ClearSelection(); ++} + +-CPVT_WordRange CPWL_Edit::GetSameWordsRange(const CPVT_WordPlace& place, +- bool bLatin, +- bool bArabic) const { +- CPWL_EditImpl_Iterator* pIterator = m_pEdit->GetIterator(); +- CPVT_Word wordinfo; +- CPVT_WordPlace wpStart(place), wpEnd(place); +- pIterator->SetAt(place); ++void CPWL_Edit::SetScrollPos(const CFX_PointF& point) { ++ m_pEditImpl->SetScrollPos(point); ++} + +- if (bLatin) { +- while (pIterator->NextWord()) { +- if (!pIterator->GetWord(wordinfo) || +- !FX_EDIT_ISLATINWORD(wordinfo.Word)) { +- break; +- } ++CFX_PointF CPWL_Edit::GetScrollPos() const { ++ return m_pEditImpl->GetScrollPos(); ++} + +- wpEnd = pIterator->GetAt(); +- } +- } else if (bArabic) { +- while (pIterator->NextWord()) { +- if (!pIterator->GetWord(wordinfo) || !PWL_ISARABICWORD(wordinfo.Word)) +- break; ++void CPWL_Edit::CopyText() {} + +- wpEnd = pIterator->GetAt(); +- } +- } ++void CPWL_Edit::PasteText() {} + +- pIterator->SetAt(place); ++void CPWL_Edit::InsertWord(uint16_t word, FX_Charset nCharset) { ++ if (!IsReadOnly()) ++ m_pEditImpl->InsertWord(word, nCharset); ++} + +- if (bLatin) { +- do { +- if (!pIterator->GetWord(wordinfo) || +- !FX_EDIT_ISLATINWORD(wordinfo.Word)) { +- break; +- } ++void CPWL_Edit::InsertReturn() { ++ if (!IsReadOnly()) ++ m_pEditImpl->InsertReturn(); ++} + +- wpStart = pIterator->GetAt(); +- } while (pIterator->PrevWord()); +- } else if (bArabic) { +- do { +- if (!pIterator->GetWord(wordinfo) || !PWL_ISARABICWORD(wordinfo.Word)) +- break; ++void CPWL_Edit::Delete() { ++ if (!IsReadOnly()) ++ m_pEditImpl->Delete(); ++} + +- wpStart = pIterator->GetAt(); +- } while (pIterator->PrevWord()); +- } ++void CPWL_Edit::Backspace() { ++ if (!IsReadOnly()) ++ m_pEditImpl->Backspace(); ++} + +- return CPVT_WordRange(wpStart, wpEnd); ++bool CPWL_Edit::CanUndo() { ++ return !IsReadOnly() && m_pEditImpl->CanUndo(); ++} ++ ++bool CPWL_Edit::CanRedo() { ++ return !IsReadOnly() && m_pEditImpl->CanRedo(); ++} ++ ++bool CPWL_Edit::Undo() { ++ return CanUndo() && m_pEditImpl->Undo(); ++} ++ ++bool CPWL_Edit::Redo() { ++ return CanRedo() && m_pEditImpl->Redo(); ++} ++ ++void CPWL_Edit::SetReadyToInput() { ++ if (m_bMouseDown) { ++ ReleaseCapture(); ++ m_bMouseDown = false; ++ } + } +diff --git a/fpdfsdk/pwl/cpwl_edit.h b/fpdfsdk/pwl/cpwl_edit.h +index 1de382e06..696e32ca2 100644 +--- a/fpdfsdk/pwl/cpwl_edit.h ++++ b/fpdfsdk/pwl/cpwl_edit.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,125 +11,127 @@ + #include + + #include "core/fpdfdoc/cpvt_wordrange.h" ++#include "core/fxcrt/fx_codepage.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "fpdfsdk/pwl/cpwl_edit_ctrl.h" +-#include "fpdfsdk/pwl/ipwl_systemhandler.h" ++#include "core/fxcrt/widestring.h" ++#include "fpdfsdk/pwl/cpwl_wnd.h" ++#include "fpdfsdk/pwl/ipwl_fillernotify.h" + + class CPDF_Font; ++class CPWL_Caret; ++class CPWL_EditImpl; ++class IPWL_FillerNotify; + +-class IPWL_Filler_Notify { +- public: +- virtual ~IPWL_Filler_Notify() = default; +- +- // Must write to |bBottom| and |fPopupRet|. +- virtual void QueryWherePopup( +- const IPWL_SystemHandler::PerWindowData* pAttached, +- float fPopupMin, +- float fPopupMax, +- bool* bBottom, +- float* fPopupRet) = 0; +- +- virtual std::pair OnBeforeKeyStroke( +- const IPWL_SystemHandler::PerWindowData* pAttached, +- WideString& strChange, +- const WideString& strChangeEx, +- int nSelStart, +- int nSelEnd, +- bool bKeyDown, +- uint32_t nFlag) = 0; +- +- virtual bool OnPopupPreOpen( +- const IPWL_SystemHandler::PerWindowData* pAttached, +- uint32_t nFlag) = 0; +- +- virtual bool OnPopupPostOpen( +- const IPWL_SystemHandler::PerWindowData* pAttached, +- uint32_t nFlag) = 0; +-}; ++enum PWL_EDIT_ALIGNFORMAT_H { PEAH_LEFT = 0, PEAH_MIDDLE, PEAH_RIGHT }; ++ ++enum PWL_EDIT_ALIGNFORMAT_V { PEAV_TOP = 0, PEAV_CENTER, PEAV_BOTTOM }; + +-class CPWL_Edit final : public CPWL_EditCtrl { ++class CPWL_Edit final : public CPWL_Wnd { + public: + CPWL_Edit(const CreateParams& cp, +- std::unique_ptr pAttachedData); ++ std::unique_ptr pAttachedData); + ~CPWL_Edit() override; + +- // CPWL_EditCtrl +- void OnCreated() override; ++ // CPWL_Wnd: + bool RePosChildWnd() override; + CFX_FloatRect GetClientRect() const override; + void DrawThisAppearance(CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device) override; +- bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override; +- bool OnLButtonDblClk(const CFX_PointF& point, uint32_t nFlag) override; +- bool OnRButtonUp(const CFX_PointF& point, uint32_t nFlag) override; +- bool OnMouseWheel(short zDelta, ++ bool OnMouseWheel(Mask nFlag, + const CFX_PointF& point, +- uint32_t nFlag) override; +- bool OnKeyDown(uint16_t nChar, uint32_t nFlag) override; +- bool OnChar(uint16_t nChar, uint32_t nFlag) override; ++ const CFX_Vector& delta) override; ++ bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask nFlag) override; ++ bool OnChar(uint16_t nChar, Mask nFlag) override; + CFX_FloatRect GetFocusRect() const override; + void OnSetFocus() override; + void OnKillFocus() override; +- ++ void OnCreated() override; ++ void OnDestroy() override; ++ bool OnLButtonDown(Mask nFlag, ++ const CFX_PointF& point) override; ++ bool OnLButtonUp(Mask nFlag, const CFX_PointF& point) override; ++ bool OnLButtonDblClk(Mask nFlag, ++ const CFX_PointF& point) override; ++ bool OnRButtonUp(Mask nFlag, const CFX_PointF& point) override; ++ bool OnMouseMove(Mask nFlag, const CFX_PointF& point) override; ++ void SetScrollInfo(const PWL_SCROLL_INFO& info) override; ++ void SetScrollPosition(float pos) override; ++ void ScrollWindowVertically(float pos) override; ++ void CreateChildWnd(const CreateParams& cp) override; ++ void SetFontSize(float fFontSize) override; ++ float GetFontSize() const override; ++ void SetCursor() override; ++ WideString GetText() override; ++ WideString GetSelectedText() override; ++ void ReplaceAndKeepSelection(const WideString& text) override; ++ void ReplaceSelection(const WideString& text) override; ++ bool SelectAllText() override; ++ bool CanUndo() override; ++ bool CanRedo() override; ++ bool Undo() override; ++ bool Redo() override; ++ ++ void SetSelection(int32_t nStartChar, int32_t nEndChar); ++ std::pair GetSelection() const; ++ void ClearSelection(); ++ ++ CFX_PointF GetScrollPos() const; ++ void SetScrollPos(const CFX_PointF& point); ++ ++ void SetCharSet(FX_Charset nCharSet) { m_nCharSet = nCharSet; } ++ FX_Charset GetCharSet() const { return m_nCharSet; } ++ ++ void SetReadyToInput(); + void SetAlignFormatVerticalCenter(); + void SetCharArray(int32_t nCharArray); + void SetLimitChar(int32_t nLimitChar); +- void SetCharSpace(float fCharSpace); +- + bool CanSelectAll() const; + bool CanCopy() const; + bool CanCut() const; +- + void CutText(); +- + void SetText(const WideString& csText); +- + bool IsTextFull() const; + + static float GetCharArrayAutoFontSize(const CPDF_Font* pFont, + const CFX_FloatRect& rcPlate, + int32_t nCharArray); + +- void SetFillerNotify(IPWL_Filler_Notify* pNotify) { +- m_pFillerNotify = pNotify; +- } +- +- void AttachFFLData(CFFL_FormFiller* pData) { m_pFormFiller = pData; } +- +- void OnInsertWord(const CPVT_WordPlace& place, +- const CPVT_WordPlace& oldplace); +- void OnInsertReturn(const CPVT_WordPlace& place, +- const CPVT_WordPlace& oldplace); +- void OnBackSpace(const CPVT_WordPlace& place, const CPVT_WordPlace& oldplace); +- void OnDelete(const CPVT_WordPlace& place, const CPVT_WordPlace& oldplace); +- void OnClear(const CPVT_WordPlace& place, const CPVT_WordPlace& oldplace); +- void OnInsertText(const CPVT_WordPlace& place, +- const CPVT_WordPlace& oldplace); ++ bool SetCaret(bool bVisible, ++ const CFX_PointF& ptHead, ++ const CFX_PointF& ptFoot); + + private: + // In case of implementation swallow the OnKeyDown event. If the event is + // swallowed, implementation may do other unexpected things, which is not the + // control means to do. +- static bool IsProceedtoOnChar(uint16_t nKeyCode, uint32_t nFlag); ++ static bool IsProceedtoOnChar(FWL_VKEYCODE nKeyCode, ++ Mask nFlag); ++ ++ bool OnKeyDownInternal(FWL_VKEYCODE nKeyCode, Mask nFlag); ++ bool OnCharInternal(uint16_t nChar, Mask nFlag); ++ ++ void CopyText(); ++ void PasteText(); ++ void InsertWord(uint16_t word, FX_Charset nCharset); ++ void InsertReturn(); ++ bool IsWndHorV() const; ++ void Delete(); ++ void Backspace(); ++ void GetCaretInfo(CFX_PointF* ptHead, CFX_PointF* ptFoot) const; ++ void SetEditCaret(bool bVisible); ++ ++ void CreateEditCaret(const CreateParams& cp); + + CPVT_WordRange GetSelectWordRange() const; + bool IsVScrollBarVisible() const; + void SetParamByFlag(); + +- CFX_PointF GetWordRightBottomPoint(const CPVT_WordPlace& wpWord); +- +- CPVT_WordRange CombineWordRange(const CPVT_WordRange& wr1, +- const CPVT_WordRange& wr2); +- CPVT_WordRange GetLatinWordsRange(const CFX_PointF& point) const; +- CPVT_WordRange GetLatinWordsRange(const CPVT_WordPlace& place) const; +- CPVT_WordRange GetSameWordsRange(const CPVT_WordPlace& place, +- bool bLatin, +- bool bArabic) const; +- ++ bool m_bMouseDown = false; + bool m_bFocus = false; ++ FX_Charset m_nCharSet = FX_Charset::kDefault; + CFX_FloatRect m_rcOldWindow; +- UnownedPtr m_pFillerNotify; +- UnownedPtr m_pFormFiller; ++ std::unique_ptr const m_pEditImpl; ++ UnownedPtr m_pCaret; + }; + + #endif // FPDFSDK_PWL_CPWL_EDIT_H_ +diff --git a/fpdfsdk/pwl/cpwl_edit_ctrl.cpp b/fpdfsdk/pwl/cpwl_edit_ctrl.cpp +deleted file mode 100644 +index 54fe91a34..000000000 +--- a/fpdfsdk/pwl/cpwl_edit_ctrl.cpp ++++ /dev/null +@@ -1,415 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "fpdfsdk/pwl/cpwl_edit_ctrl.h" +- +-#include +- +-#include "core/fpdfdoc/cpvt_word.h" +-#include "core/fxge/fx_font.h" +-#include "fpdfsdk/pwl/cpwl_caret.h" +-#include "fpdfsdk/pwl/cpwl_edit_impl.h" +-#include "fpdfsdk/pwl/cpwl_scroll_bar.h" +-#include "fpdfsdk/pwl/cpwl_wnd.h" +-#include "public/fpdf_fwlevent.h" +-#include "third_party/base/ptr_util.h" +- +-CPWL_EditCtrl::CPWL_EditCtrl( +- const CreateParams& cp, +- std::unique_ptr pAttachedData) +- : CPWL_Wnd(cp, std::move(pAttachedData)), +- m_pEdit(pdfium::MakeUnique()) { +- GetCreationParams()->eCursorType = FXCT_VBEAM; +-} +- +-CPWL_EditCtrl::~CPWL_EditCtrl() = default; +- +-void CPWL_EditCtrl::OnCreated() { +- SetFontSize(GetCreationParams()->fFontSize); +- m_pEdit->SetFontMap(GetFontMap()); +- m_pEdit->SetNotify(this); +- m_pEdit->Initialize(); +-} +- +-bool CPWL_EditCtrl::IsWndHorV() const { +- CFX_Matrix mt = GetWindowMatrix(); +- return mt.Transform(CFX_PointF(1, 1)).y == mt.Transform(CFX_PointF(0, 1)).y; +-} +- +-void CPWL_EditCtrl::SetCursor() { +- if (IsValid()) +- GetSystemHandler()->SetCursor(IsWndHorV() ? FXCT_VBEAM : FXCT_HBEAM); +-} +- +-WideString CPWL_EditCtrl::GetSelectedText() { +- return m_pEdit->GetSelectedText(); +-} +- +-void CPWL_EditCtrl::ReplaceSelection(const WideString& text) { +- m_pEdit->ReplaceSelection(text); +-} +- +-bool CPWL_EditCtrl::RePosChildWnd() { +- m_pEdit->SetPlateRect(GetClientRect()); +- return true; +-} +- +-void CPWL_EditCtrl::SetScrollInfo(const PWL_SCROLL_INFO& info) { +- if (CPWL_Wnd* pChild = GetVScrollBar()) +- pChild->SetScrollInfo(info); +-} +- +-void CPWL_EditCtrl::SetScrollPosition(float pos) { +- if (CPWL_Wnd* pChild = GetVScrollBar()) +- pChild->SetScrollPosition(pos); +-} +- +-void CPWL_EditCtrl::ScrollWindowVertically(float pos) { +- m_pEdit->SetScrollPos(CFX_PointF(m_pEdit->GetScrollPos().x, pos)); +-} +- +-void CPWL_EditCtrl::CreateChildWnd(const CreateParams& cp) { +- if (!IsReadOnly()) +- CreateEditCaret(cp); +-} +- +-void CPWL_EditCtrl::CreateEditCaret(const CreateParams& cp) { +- if (m_pEditCaret) +- return; +- +- CreateParams ecp = cp; +- ecp.dwFlags = PWS_CHILD | PWS_NOREFRESHCLIP; +- ecp.dwBorderWidth = 0; +- ecp.nBorderStyle = BorderStyle::SOLID; +- ecp.rcRectWnd = CFX_FloatRect(); +- +- auto pCaret = pdfium::MakeUnique(ecp, CloneAttachedData()); +- m_pEditCaret = pCaret.get(); +- m_pEditCaret->SetInvalidRect(GetClientRect()); +- AddChild(std::move(pCaret)); +- m_pEditCaret->Realize(); +-} +- +-void CPWL_EditCtrl::SetFontSize(float fFontSize) { +- m_pEdit->SetFontSize(fFontSize); +-} +- +-float CPWL_EditCtrl::GetFontSize() const { +- return m_pEdit->GetFontSize(); +-} +- +-bool CPWL_EditCtrl::OnKeyDown(uint16_t nChar, uint32_t nFlag) { +- if (m_bMouseDown) +- return true; +- +- bool bRet = CPWL_Wnd::OnKeyDown(nChar, nFlag); +- +- // FILTER +- switch (nChar) { +- default: +- return false; +- case FWL_VKEY_Delete: +- case FWL_VKEY_Up: +- case FWL_VKEY_Down: +- case FWL_VKEY_Left: +- case FWL_VKEY_Right: +- case FWL_VKEY_Home: +- case FWL_VKEY_End: +- case FWL_VKEY_Insert: +- case 'C': +- case 'V': +- case 'X': +- case 'A': +- case 'Z': +- case 'c': +- case 'v': +- case 'x': +- case 'a': +- case 'z': +- break; +- } +- +- if (nChar == FWL_VKEY_Delete && m_pEdit->IsSelected()) +- nChar = FWL_VKEY_Unknown; +- +- switch (nChar) { +- case FWL_VKEY_Delete: +- Delete(); +- return true; +- case FWL_VKEY_Insert: +- if (IsSHIFTpressed(nFlag)) +- PasteText(); +- return true; +- case FWL_VKEY_Up: +- m_pEdit->OnVK_UP(IsSHIFTpressed(nFlag), false); +- return true; +- case FWL_VKEY_Down: +- m_pEdit->OnVK_DOWN(IsSHIFTpressed(nFlag), false); +- return true; +- case FWL_VKEY_Left: +- m_pEdit->OnVK_LEFT(IsSHIFTpressed(nFlag), false); +- return true; +- case FWL_VKEY_Right: +- m_pEdit->OnVK_RIGHT(IsSHIFTpressed(nFlag), false); +- return true; +- case FWL_VKEY_Home: +- m_pEdit->OnVK_HOME(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); +- return true; +- case FWL_VKEY_End: +- m_pEdit->OnVK_END(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); +- return true; +- case FWL_VKEY_Unknown: +- if (!IsSHIFTpressed(nFlag)) +- ClearSelection(); +- else +- CutText(); +- return true; +- default: +- break; +- } +- +- return bRet; +-} +- +-bool CPWL_EditCtrl::OnChar(uint16_t nChar, uint32_t nFlag) { +- if (m_bMouseDown) +- return true; +- +- CPWL_Wnd::OnChar(nChar, nFlag); +- +- // FILTER +- switch (nChar) { +- case 0x0A: +- case 0x1B: +- return false; +- default: +- break; +- } +- +- bool bCtrl = IsCTRLpressed(nFlag); +- bool bAlt = IsALTpressed(nFlag); +- bool bShift = IsSHIFTpressed(nFlag); +- +- uint16_t word = nChar; +- +- if (bCtrl && !bAlt) { +- switch (nChar) { +- case 'C' - 'A' + 1: +- CopyText(); +- return true; +- case 'V' - 'A' + 1: +- PasteText(); +- return true; +- case 'X' - 'A' + 1: +- CutText(); +- return true; +- case 'A' - 'A' + 1: +- SelectAll(); +- return true; +- case 'Z' - 'A' + 1: +- if (bShift) +- Redo(); +- else +- Undo(); +- return true; +- default: +- if (nChar < 32) +- return false; +- } +- } +- +- if (IsReadOnly()) +- return true; +- +- if (m_pEdit->IsSelected() && word == FWL_VKEY_Back) +- word = FWL_VKEY_Unknown; +- +- ClearSelection(); +- +- switch (word) { +- case FWL_VKEY_Back: +- Backspace(); +- break; +- case FWL_VKEY_Return: +- InsertReturn(); +- break; +- case FWL_VKEY_Unknown: +- break; +- default: +- InsertWord(word, GetCharSet()); +- break; +- } +- +- return true; +-} +- +-bool CPWL_EditCtrl::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnLButtonDown(point, nFlag); +- +- if (ClientHitTest(point)) { +- if (m_bMouseDown && !InvalidateRect(nullptr)) +- return true; +- +- m_bMouseDown = true; +- SetCapture(); +- +- m_pEdit->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); +- } +- +- return true; +-} +- +-bool CPWL_EditCtrl::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnLButtonUp(point, nFlag); +- +- if (m_bMouseDown) { +- // can receive keybord message +- if (ClientHitTest(point) && !IsFocused()) +- SetFocus(); +- +- ReleaseCapture(); +- m_bMouseDown = false; +- } +- +- return true; +-} +- +-bool CPWL_EditCtrl::OnMouseMove(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnMouseMove(point, nFlag); +- +- if (m_bMouseDown) +- m_pEdit->OnMouseMove(point, false, false); +- +- return true; +-} +- +-void CPWL_EditCtrl::SetEditCaret(bool bVisible) { +- CFX_PointF ptHead; +- CFX_PointF ptFoot; +- if (bVisible) +- GetCaretInfo(&ptHead, &ptFoot); +- +- SetCaret(bVisible, ptHead, ptFoot); +- // Note, |this| may no longer be viable at this point. If more work needs to +- // be done, check the return value of SetCaret(). +-} +- +-void CPWL_EditCtrl::GetCaretInfo(CFX_PointF* ptHead, CFX_PointF* ptFoot) const { +- CPWL_EditImpl_Iterator* pIterator = m_pEdit->GetIterator(); +- pIterator->SetAt(m_pEdit->GetCaret()); +- CPVT_Word word; +- CPVT_Line line; +- if (pIterator->GetWord(word)) { +- ptHead->x = word.ptWord.x + word.fWidth; +- ptHead->y = word.ptWord.y + word.fAscent; +- ptFoot->x = word.ptWord.x + word.fWidth; +- ptFoot->y = word.ptWord.y + word.fDescent; +- } else if (pIterator->GetLine(line)) { +- ptHead->x = line.ptLine.x; +- ptHead->y = line.ptLine.y + line.fLineAscent; +- ptFoot->x = line.ptLine.x; +- ptFoot->y = line.ptLine.y + line.fLineDescent; +- } +-} +- +-bool CPWL_EditCtrl::SetCaret(bool bVisible, +- const CFX_PointF& ptHead, +- const CFX_PointF& ptFoot) { +- if (!m_pEditCaret) +- return true; +- +- if (!IsFocused() || m_pEdit->IsSelected()) +- bVisible = false; +- +- ObservedPtr thisObserved(this); +- m_pEditCaret->SetCaret(bVisible, ptHead, ptFoot); +- if (!thisObserved) +- return false; +- +- return true; +-} +- +-WideString CPWL_EditCtrl::GetText() { +- return m_pEdit->GetText(); +-} +- +-void CPWL_EditCtrl::SetSelection(int32_t nStartChar, int32_t nEndChar) { +- m_pEdit->SetSelection(nStartChar, nEndChar); +-} +- +-void CPWL_EditCtrl::GetSelection(int32_t& nStartChar, int32_t& nEndChar) const { +- m_pEdit->GetSelection(nStartChar, nEndChar); +-} +- +-void CPWL_EditCtrl::ClearSelection() { +- if (!IsReadOnly()) +- m_pEdit->ClearSelection(); +-} +- +-void CPWL_EditCtrl::SelectAll() { +- m_pEdit->SelectAll(); +-} +- +-void CPWL_EditCtrl::SetScrollPos(const CFX_PointF& point) { +- m_pEdit->SetScrollPos(point); +-} +- +-CFX_PointF CPWL_EditCtrl::GetScrollPos() const { +- return m_pEdit->GetScrollPos(); +-} +- +-void CPWL_EditCtrl::CopyText() {} +- +-void CPWL_EditCtrl::PasteText() {} +- +-void CPWL_EditCtrl::CutText() {} +- +-void CPWL_EditCtrl::InsertWord(uint16_t word, int32_t nCharset) { +- if (!IsReadOnly()) +- m_pEdit->InsertWord(word, nCharset); +-} +- +-void CPWL_EditCtrl::InsertReturn() { +- if (!IsReadOnly()) +- m_pEdit->InsertReturn(); +-} +- +-void CPWL_EditCtrl::Delete() { +- if (!IsReadOnly()) +- m_pEdit->Delete(); +-} +- +-void CPWL_EditCtrl::Backspace() { +- if (!IsReadOnly()) +- m_pEdit->Backspace(); +-} +- +-bool CPWL_EditCtrl::CanUndo() { +- return !IsReadOnly() && m_pEdit->CanUndo(); +-} +- +-bool CPWL_EditCtrl::CanRedo() { +- return !IsReadOnly() && m_pEdit->CanRedo(); +-} +- +-bool CPWL_EditCtrl::Undo() { +- return CanUndo() && m_pEdit->Undo(); +-} +- +-bool CPWL_EditCtrl::Redo() { +- return CanRedo() && m_pEdit->Redo(); +-} +- +-int32_t CPWL_EditCtrl::GetCharSet() const { +- return m_nCharSet < 0 ? FX_CHARSET_Default : m_nCharSet; +-} +- +-void CPWL_EditCtrl::SetReadyToInput() { +- if (m_bMouseDown) { +- ReleaseCapture(); +- m_bMouseDown = false; +- } +-} +diff --git a/fpdfsdk/pwl/cpwl_edit_ctrl.h b/fpdfsdk/pwl/cpwl_edit_ctrl.h +deleted file mode 100644 +index 9cd92bfeb..000000000 +--- a/fpdfsdk/pwl/cpwl_edit_ctrl.h ++++ /dev/null +@@ -1,94 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef FPDFSDK_PWL_CPWL_EDIT_CTRL_H_ +-#define FPDFSDK_PWL_CPWL_EDIT_CTRL_H_ +- +-#include +- +-#include "core/fxcrt/fx_codepage.h" +-#include "core/fxcrt/fx_string.h" +-#include "fpdfsdk/pwl/cpwl_wnd.h" +- +-class CPWL_EditImpl; +-class CPWL_Caret; +-struct CPVT_WordPlace; +- +-enum PWL_EDIT_ALIGNFORMAT_H { PEAH_LEFT = 0, PEAH_MIDDLE, PEAH_RIGHT }; +- +-enum PWL_EDIT_ALIGNFORMAT_V { PEAV_TOP = 0, PEAV_CENTER, PEAV_BOTTOM }; +- +-class CPWL_EditCtrl : public CPWL_Wnd { +- public: +- CPWL_EditCtrl( +- const CreateParams& cp, +- std::unique_ptr pAttachedData); +- ~CPWL_EditCtrl() override; +- +- void SetSelection(int32_t nStartChar, int32_t nEndChar); +- void GetSelection(int32_t& nStartChar, int32_t& nEndChar) const; +- void ClearSelection(); +- void SelectAll(); +- +- CFX_PointF GetScrollPos() const; +- void SetScrollPos(const CFX_PointF& point); +- +- void SetCharSet(uint8_t nCharSet) { m_nCharSet = nCharSet; } +- int32_t GetCharSet() const; +- +- bool CanUndo() override; +- bool CanRedo() override; +- bool Undo() override; +- bool Redo() override; +- +- void SetReadyToInput(); +- +- // CPWL_Wnd: +- void OnCreated() override; +- bool OnKeyDown(uint16_t nChar, uint32_t nFlag) override; +- bool OnChar(uint16_t nChar, uint32_t nFlag) override; +- bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override; +- bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; +- bool OnMouseMove(const CFX_PointF& point, uint32_t nFlag) override; +- void SetScrollInfo(const PWL_SCROLL_INFO& info) override; +- void SetScrollPosition(float pos) override; +- void ScrollWindowVertically(float pos) override; +- void CreateChildWnd(const CreateParams& cp) override; +- bool RePosChildWnd() override; +- void SetFontSize(float fFontSize) override; +- float GetFontSize() const override; +- void SetCursor() override; +- WideString GetText() override; +- WideString GetSelectedText() override; +- void ReplaceSelection(const WideString& text) override; +- +- bool SetCaret(bool bVisible, +- const CFX_PointF& ptHead, +- const CFX_PointF& ptFoot); +- +- protected: +- void CopyText(); +- void PasteText(); +- void CutText(); +- void InsertWord(uint16_t word, int32_t nCharset); +- void InsertReturn(); +- bool IsWndHorV() const; +- void Delete(); +- void Backspace(); +- void GetCaretInfo(CFX_PointF* ptHead, CFX_PointF* ptFoot) const; +- void SetEditCaret(bool bVisible); +- +- std::unique_ptr const m_pEdit; +- CPWL_Caret* m_pEditCaret = nullptr; +- bool m_bMouseDown = false; +- +- private: +- void CreateEditCaret(const CreateParams& cp); +- +- int32_t m_nCharSet = FX_CHARSET_Default; +-}; +- +-#endif // FPDFSDK_PWL_CPWL_EDIT_CTRL_H_ +diff --git a/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp b/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp +index 8b68afa2f..f0e13d2d7 100644 +--- a/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp ++++ b/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp +@@ -1,14 +1,16 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fpdfsdk/pwl/cpwl_edit.h" + +-#include "fpdfsdk/cpdfsdk_annot.h" ++#include ++ + #include "fpdfsdk/cpdfsdk_annotiterator.h" + #include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fpdfsdk/cpdfsdk_helpers.h" +-#include "fpdfsdk/formfiller/cffl_formfiller.h" ++#include "fpdfsdk/cpdfsdk_widget.h" ++#include "fpdfsdk/formfiller/cffl_formfield.h" + #include "fpdfsdk/formfiller/cffl_interactiveformfiller.h" + #include "public/fpdf_fwlevent.h" + #include "testing/embedder_test.h" +@@ -27,27 +29,24 @@ class CPWLEditEmbedderTest : public EmbedderTest { + } + + void CreateAndInitializeFormPDF() { +- EXPECT_TRUE(OpenDocument("text_form_multiple.pdf")); ++ ASSERT_TRUE(OpenDocument("text_form_multiple.pdf")); + m_page = LoadPage(0); + ASSERT_TRUE(m_page); + + m_pFormFillEnv = + CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle()); +- CPDFSDK_AnnotIterator iter(m_pFormFillEnv->GetPageView(0), +- CPDF_Annot::Subtype::WIDGET); ++ CPDFSDK_AnnotIterator iter(m_pFormFillEnv->GetPageViewAtIndex(0), ++ {CPDF_Annot::Subtype::WIDGET}); + // Normal text field. +- m_pAnnot = iter.GetFirstAnnot(); ++ m_pAnnot = ToCPDFSDKWidget(iter.GetFirstAnnot()); + ASSERT_TRUE(m_pAnnot); +- ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, m_pAnnot->GetAnnotSubtype()); + + // Read-only text field. + CPDFSDK_Annot* pAnnotReadOnly = iter.GetNextAnnot(m_pAnnot); + + // Pre-filled text field with char limit of 10. +- m_pAnnotCharLimit = iter.GetNextAnnot(pAnnotReadOnly); ++ m_pAnnotCharLimit = ToCPDFSDKWidget(iter.GetNextAnnot(pAnnotReadOnly)); + ASSERT_TRUE(m_pAnnotCharLimit); +- ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, +- m_pAnnotCharLimit->GetAnnotSubtype()); + + // Password text field. + CPDFSDK_Annot* password_annot = iter.GetNextAnnot(m_pAnnotCharLimit); +@@ -58,20 +57,20 @@ class CPWLEditEmbedderTest : public EmbedderTest { + ASSERT_EQ(password_annot, pLastAnnot); + } + +- void FormFillerAndWindowSetup(CPDFSDK_Annot* pAnnotTextField) { ++ void FormFillerAndWindowSetup(CPDFSDK_Widget* pAnnotTextField) { + CFFL_InteractiveFormFiller* pInteractiveFormFiller = + m_pFormFillEnv->GetInteractiveFormFiller(); + { +- ObservedPtr pObserved(pAnnotTextField); +- EXPECT_TRUE(pInteractiveFormFiller->OnSetFocus(&pObserved, 0)); ++ ObservedPtr pObserved(pAnnotTextField); ++ EXPECT_TRUE(pInteractiveFormFiller->OnSetFocus(pObserved, {})); + } + + m_pFormFiller = +- pInteractiveFormFiller->GetFormFillerForTesting(pAnnotTextField); ++ pInteractiveFormFiller->GetFormFieldForTesting(pAnnotTextField); + ASSERT_TRUE(m_pFormFiller); + + CPWL_Wnd* pWindow = +- m_pFormFiller->GetPWLWindow(m_pFormFillEnv->GetPageView(0), false); ++ m_pFormFiller->GetPWLWindow(m_pFormFillEnv->GetPageViewAtIndex(0)); + ASSERT_TRUE(pWindow); + m_pEdit = static_cast(pWindow); + } +@@ -79,31 +78,31 @@ class CPWLEditEmbedderTest : public EmbedderTest { + void TypeTextIntoTextField(int num_chars) { + // Type text starting with 'A' to as many chars as specified by |num_chars|. + for (int i = 0; i < num_chars; ++i) { +- EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), i + 'A', 0)); ++ EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), i + 'A', {})); + } + } + + FPDF_PAGE GetPage() { return m_page; } + CPWL_Edit* GetCPWLEdit() { return m_pEdit; } +- CFFL_FormFiller* GetCFFLFormFiller() { return m_pFormFiller; } +- CPDFSDK_Annot* GetCPDFSDKAnnot() { return m_pAnnot; } +- CPDFSDK_Annot* GetCPDFSDKAnnotCharLimit() { return m_pAnnotCharLimit; } ++ CFFL_FormField* GetCFFLFormFiller() { return m_pFormFiller; } ++ CPDFSDK_Widget* GetCPDFSDKAnnot() { return m_pAnnot; } ++ CPDFSDK_Widget* GetCPDFSDKAnnotCharLimit() { return m_pAnnotCharLimit; } + + private: + FPDF_PAGE m_page; + CPWL_Edit* m_pEdit; +- CFFL_FormFiller* m_pFormFiller; +- CPDFSDK_Annot* m_pAnnot; +- CPDFSDK_Annot* m_pAnnotCharLimit; ++ CFFL_FormField* m_pFormFiller; ++ CPDFSDK_Widget* m_pAnnot; ++ CPDFSDK_Widget* m_pAnnotCharLimit; + CPDFSDK_FormFillEnvironment* m_pFormFillEnv; + }; + + TEST_F(CPWLEditEmbedderTest, TypeText) { + FormFillerAndWindowSetup(GetCPDFSDKAnnot()); + EXPECT_TRUE(GetCPWLEdit()->GetText().IsEmpty()); +- EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'a', 0)); +- EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'b', 0)); +- EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'c', 0)); ++ EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'a', {})); ++ EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'b', {})); ++ EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'c', {})); + + EXPECT_STREQ(L"abc", GetCPWLEdit()->GetText().c_str()); + } +@@ -117,9 +116,9 @@ TEST_F(CPWLEditEmbedderTest, GetSelectedTextEmptyAndBasic) { + GetCPWLEdit()->SetSelection(0, 3); + EXPECT_TRUE(GetCPWLEdit()->GetSelectedText().IsEmpty()); + +- EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'a', 0)); +- EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'b', 0)); +- EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'c', 0)); ++ EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'a', {})); ++ EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'b', {})); ++ EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'c', {})); + GetCPWLEdit()->SetSelection(0, 2); + + EXPECT_STREQ(L"ab", GetCPWLEdit()->GetSelectedText().c_str()); +@@ -223,7 +222,7 @@ TEST_F(CPWLEditEmbedderTest, InsertTextInPopulatedTextFieldLeft) { + TypeTextIntoTextField(10); + + // Move cursor to beginning of text field. +- EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Home, 0)); ++ EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Home, {})); + + GetCPWLEdit()->ReplaceSelection(L"Hello"); + EXPECT_STREQ(L"HelloABCDEFGHIJ", GetCPWLEdit()->GetText().c_str()); +@@ -235,7 +234,7 @@ TEST_F(CPWLEditEmbedderTest, InsertTextInPopulatedTextFieldMiddle) { + + // Move cursor to middle of text field. + for (int i = 0; i < 5; ++i) { +- EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Left, 0)); ++ EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Left, {})); + } + + GetCPWLEdit()->ReplaceSelection(L"Hello"); +@@ -324,7 +323,7 @@ TEST_F(CPWLEditEmbedderTest, InsertTextInPopulatedCharLimitTextFieldMiddle) { + FormFillerAndWindowSetup(GetCPDFSDKAnnotCharLimit()); + // Move cursor to middle of text field. + for (int i = 0; i < 5; ++i) { +- EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Right, 0)); ++ EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_Right, {})); + } + + GetCPWLEdit()->ReplaceSelection(L"Hippopotamus"); +@@ -334,7 +333,7 @@ TEST_F(CPWLEditEmbedderTest, InsertTextInPopulatedCharLimitTextFieldMiddle) { + TEST_F(CPWLEditEmbedderTest, InsertTextInPopulatedCharLimitTextFieldRight) { + FormFillerAndWindowSetup(GetCPDFSDKAnnotCharLimit()); + // Move cursor to end of text field. +- EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_End, 0)); ++ EXPECT_TRUE(GetCFFLFormFiller()->OnKeyDown(FWL_VKEY_End, {})); + + GetCPWLEdit()->ReplaceSelection(L"Hippopotamus"); + EXPECT_STREQ(L"ElephantHi", GetCPWLEdit()->GetText().c_str()); +@@ -423,3 +422,21 @@ TEST_F(CPWLEditEmbedderTest, SetTextWithBodyNewLineAndCarriageFeed) { + GetCPWLEdit()->SetText(L"Foo\n\rBar"); + EXPECT_STREQ(L"FooBar", GetCPWLEdit()->GetText().c_str()); + } ++ ++TEST_F(CPWLEditEmbedderTest, ReplaceAndKeepSelection) { ++ FormFillerAndWindowSetup(GetCPDFSDKAnnot()); ++ TypeTextIntoTextField(10); ++ ++ GetCPWLEdit()->SetSelection(1, 3); ++ EXPECT_STREQ(L"ABCDEFGHIJ", GetCPWLEdit()->GetText().c_str()); ++ GetCPWLEdit()->ReplaceAndKeepSelection(L"xyz"); ++ EXPECT_STREQ(L"AxyzDEFGHIJ", GetCPWLEdit()->GetText().c_str()); ++ EXPECT_STREQ(L"xyz", GetCPWLEdit()->GetSelectedText().c_str()); ++ EXPECT_EQ(GetCPWLEdit()->GetSelection(), std::make_pair(1, 4)); ++ ++ GetCPWLEdit()->SetSelection(4, 1); ++ GetCPWLEdit()->ReplaceAndKeepSelection(L"12"); ++ EXPECT_STREQ(L"A12DEFGHIJ", GetCPWLEdit()->GetText().c_str()); ++ EXPECT_STREQ(L"12", GetCPWLEdit()->GetSelectedText().c_str()); ++ EXPECT_EQ(GetCPWLEdit()->GetSelection(), std::make_pair(1, 3)); ++} +diff --git a/fpdfsdk/pwl/cpwl_edit_impl.cpp b/fpdfsdk/pwl/cpwl_edit_impl.cpp +index a9ee19e75..50e2c42ea 100644 +--- a/fpdfsdk/pwl/cpwl_edit_impl.cpp ++++ b/fpdfsdk/pwl/cpwl_edit_impl.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,7 +8,6 @@ + + #include + #include +-#include + #include + + #include "core/fpdfapi/font/cpdf_font.h" +@@ -18,15 +17,15 @@ + #include "core/fpdfdoc/ipvt_fontmap.h" + #include "core/fxcrt/autorestorer.h" + #include "core/fxcrt/fx_codepage.h" ++#include "core/fxge/cfx_fillrenderoptions.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/cfx_renderdevice.h" + #include "fpdfsdk/pwl/cpwl_edit.h" +-#include "fpdfsdk/pwl/cpwl_edit_ctrl.h" + #include "fpdfsdk/pwl/cpwl_scroll_bar.h" +-#include "fpdfsdk/pwl/ipwl_systemhandler.h" +-#include "third_party/base/compiler_specific.h" +-#include "third_party/base/ptr_util.h" ++#include "fpdfsdk/pwl/ipwl_fillernotify.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" + + namespace { + +@@ -44,7 +43,7 @@ void DrawTextString(CFX_RenderDevice* pDevice, + + CFX_PointF pos = mtUser2Device.Transform(pt); + CPDF_RenderOptions ro; +- ASSERT(ro.GetOptions().bClearType); ++ DCHECK(ro.GetOptions().bClearType); + ro.SetColorMode(CPDF_RenderOptions::kNormal); + CPDF_TextRenderer::DrawTextString(pDevice, pos.x, pos.y, pFont, fFontSize, + mtUser2Device, str, crTextFill, ro); +@@ -52,23 +51,18 @@ void DrawTextString(CFX_RenderDevice* pDevice, + + } // namespace + +-CPWL_EditImpl_Iterator::CPWL_EditImpl_Iterator( +- CPWL_EditImpl* pEdit, +- CPDF_VariableText::Iterator* pVTIterator) ++CPWL_EditImpl::Iterator::Iterator(CPWL_EditImpl* pEdit, ++ CPVT_VariableText::Iterator* pVTIterator) + : m_pEdit(pEdit), m_pVTIterator(pVTIterator) {} + +-CPWL_EditImpl_Iterator::~CPWL_EditImpl_Iterator() {} ++CPWL_EditImpl::Iterator::~Iterator() = default; + +-bool CPWL_EditImpl_Iterator::NextWord() { ++bool CPWL_EditImpl::Iterator::NextWord() { + return m_pVTIterator->NextWord(); + } + +-bool CPWL_EditImpl_Iterator::PrevWord() { +- return m_pVTIterator->PrevWord(); +-} +- +-bool CPWL_EditImpl_Iterator::GetWord(CPVT_Word& word) const { +- ASSERT(m_pEdit); ++bool CPWL_EditImpl::Iterator::GetWord(CPVT_Word& word) const { ++ DCHECK(m_pEdit); + + if (m_pVTIterator->GetWord(word)) { + word.ptWord = m_pEdit->VTToEdit(word.ptWord); +@@ -77,8 +71,8 @@ bool CPWL_EditImpl_Iterator::GetWord(CPVT_Word& word) const { + return false; + } + +-bool CPWL_EditImpl_Iterator::GetLine(CPVT_Line& line) const { +- ASSERT(m_pEdit); ++bool CPWL_EditImpl::Iterator::GetLine(CPVT_Line& line) const { ++ DCHECK(m_pEdit); + + if (m_pVTIterator->GetLine(line)) { + line.ptLine = m_pEdit->VTToEdit(line.ptLine); +@@ -87,85 +81,71 @@ bool CPWL_EditImpl_Iterator::GetLine(CPVT_Line& line) const { + return false; + } + +-void CPWL_EditImpl_Iterator::SetAt(int32_t nWordIndex) { ++void CPWL_EditImpl::Iterator::SetAt(int32_t nWordIndex) { + m_pVTIterator->SetAt(nWordIndex); + } + +-void CPWL_EditImpl_Iterator::SetAt(const CPVT_WordPlace& place) { ++void CPWL_EditImpl::Iterator::SetAt(const CPVT_WordPlace& place) { + m_pVTIterator->SetAt(place); + } + +-const CPVT_WordPlace& CPWL_EditImpl_Iterator::GetAt() const { ++const CPVT_WordPlace& CPWL_EditImpl::Iterator::GetAt() const { + return m_pVTIterator->GetWordPlace(); + } + +-CPWL_EditImpl_Provider::CPWL_EditImpl_Provider(IPVT_FontMap* pFontMap) +- : CPDF_VariableText::Provider(pFontMap), m_pFontMap(pFontMap) { +- ASSERT(m_pFontMap); +-} ++class CPWL_EditImpl::Provider final : public CPVT_VariableText::Provider { ++ public: ++ explicit Provider(IPVT_FontMap* pFontMap); ++ ~Provider() override; + +-CPWL_EditImpl_Provider::~CPWL_EditImpl_Provider() {} ++ // CPVT_VariableText::Provider: ++ int GetCharWidth(int32_t nFontIndex, uint16_t word) override; ++ int32_t GetWordFontIndex(uint16_t word, ++ FX_Charset charset, ++ int32_t nFontIndex) override; ++}; + +-IPVT_FontMap* CPWL_EditImpl_Provider::GetFontMap() const { +- return m_pFontMap; +-} ++CPWL_EditImpl::Provider::Provider(IPVT_FontMap* pFontMap) ++ : CPVT_VariableText::Provider(pFontMap) {} ++ ++CPWL_EditImpl::Provider::~Provider() = default; + +-uint32_t CPWL_EditImpl_Provider::GetCharWidth(int32_t nFontIndex, +- uint16_t word) { +- RetainPtr pPDFFont = m_pFontMap->GetPDFFont(nFontIndex); ++int CPWL_EditImpl::Provider::GetCharWidth(int32_t nFontIndex, uint16_t word) { ++ RetainPtr pPDFFont = GetFontMap()->GetPDFFont(nFontIndex); + if (!pPDFFont) + return 0; + + uint32_t charcode = pPDFFont->IsUnicodeCompatible() + ? pPDFFont->CharCodeFromUnicode(word) +- : m_pFontMap->CharCodeFromUnicode(nFontIndex, word); +- ++ : GetFontMap()->CharCodeFromUnicode(nFontIndex, word); + if (charcode == CPDF_Font::kInvalidCharCode) + return 0; + + return pPDFFont->GetCharWidthF(charcode); + } + +-int32_t CPWL_EditImpl_Provider::GetTypeAscent(int32_t nFontIndex) { +- RetainPtr pPDFFont = m_pFontMap->GetPDFFont(nFontIndex); +- return pPDFFont ? pPDFFont->GetTypeAscent() : 0; +-} +- +-int32_t CPWL_EditImpl_Provider::GetTypeDescent(int32_t nFontIndex) { +- RetainPtr pPDFFont = m_pFontMap->GetPDFFont(nFontIndex); +- return pPDFFont ? pPDFFont->GetTypeDescent() : 0; +-} +- +-int32_t CPWL_EditImpl_Provider::GetWordFontIndex(uint16_t word, +- int32_t charset, +- int32_t nFontIndex) { +- return m_pFontMap->GetWordFontIndex(word, charset, nFontIndex); +-} +- +-int32_t CPWL_EditImpl_Provider::GetDefaultFontIndex() { +- return 0; +-} +- +-bool CPWL_EditImpl_Provider::IsLatinWord(uint16_t word) { +- return FX_EDIT_ISLATINWORD(word); ++int32_t CPWL_EditImpl::Provider::GetWordFontIndex(uint16_t word, ++ FX_Charset charset, ++ int32_t nFontIndex) { ++ return GetFontMap()->GetWordFontIndex(word, charset, nFontIndex); + } + +-CPWL_EditImpl_Refresh::CPWL_EditImpl_Refresh() {} ++CPWL_EditImpl::RefreshState::RefreshState() = default; + +-CPWL_EditImpl_Refresh::~CPWL_EditImpl_Refresh() {} ++CPWL_EditImpl::RefreshState::~RefreshState() = default; + +-void CPWL_EditImpl_Refresh::BeginRefresh() { ++void CPWL_EditImpl::RefreshState::BeginRefresh() { + m_OldLineRects = std::move(m_NewLineRects); + m_NewLineRects.clear(); + m_RefreshRects.clear(); + } + +-void CPWL_EditImpl_Refresh::Push(const CPVT_WordRange& linerange, +- const CFX_FloatRect& rect) { +- m_NewLineRects.emplace_back(CPWL_EditImpl_LineRect(linerange, rect)); ++void CPWL_EditImpl::RefreshState::Push(const CPVT_WordRange& linerange, ++ const CFX_FloatRect& rect) { ++ m_NewLineRects.emplace_back(LineRect(linerange, rect)); + } + +-void CPWL_EditImpl_Refresh::NoAnalyse() { ++void CPWL_EditImpl::RefreshState::NoAnalyse() { + for (const auto& lineRect : m_OldLineRects) + Add(lineRect.m_rcLine); + +@@ -173,15 +153,15 @@ void CPWL_EditImpl_Refresh::NoAnalyse() { + Add(lineRect.m_rcLine); + } + +-std::vector* CPWL_EditImpl_Refresh::GetRefreshRects() { ++std::vector* CPWL_EditImpl::RefreshState::GetRefreshRects() { + return &m_RefreshRects; + } + +-void CPWL_EditImpl_Refresh::EndRefresh() { ++void CPWL_EditImpl::RefreshState::EndRefresh() { + m_RefreshRects.clear(); + } + +-void CPWL_EditImpl_Refresh::Add(const CFX_FloatRect& new_rect) { ++void CPWL_EditImpl::RefreshState::Add(const CFX_FloatRect& new_rect) { + // Check for overlapped area. + for (const auto& rect : m_RefreshRects) { + if (rect.Contains(new_rect)) +@@ -190,17 +170,16 @@ void CPWL_EditImpl_Refresh::Add(const CFX_FloatRect& new_rect) { + m_RefreshRects.emplace_back(CFX_FloatRect(new_rect)); + } + +-CPWL_EditImpl_Undo::CPWL_EditImpl_Undo() +- : m_nCurUndoPos(0), m_bWorking(false) {} ++CPWL_EditImpl::UndoStack::UndoStack() = default; + +-CPWL_EditImpl_Undo::~CPWL_EditImpl_Undo() {} ++CPWL_EditImpl::UndoStack::~UndoStack() = default; + +-bool CPWL_EditImpl_Undo::CanUndo() const { ++bool CPWL_EditImpl::UndoStack::CanUndo() const { + return m_nCurUndoPos > 0; + } + +-void CPWL_EditImpl_Undo::Undo() { +- ASSERT(!m_bWorking); ++void CPWL_EditImpl::UndoStack::Undo() { ++ DCHECK(!m_bWorking); + m_bWorking = true; + int nUndoRemain = 1; + while (CanUndo() && nUndoRemain > 0) { +@@ -208,17 +187,17 @@ void CPWL_EditImpl_Undo::Undo() { + m_nCurUndoPos--; + nUndoRemain--; + } +- ASSERT(nUndoRemain == 0); +- ASSERT(m_bWorking); ++ DCHECK_EQ(nUndoRemain, 0); ++ DCHECK(m_bWorking); + m_bWorking = false; + } + +-bool CPWL_EditImpl_Undo::CanRedo() const { ++bool CPWL_EditImpl::UndoStack::CanRedo() const { + return m_nCurUndoPos < m_UndoItemStack.size(); + } + +-void CPWL_EditImpl_Undo::Redo() { +- ASSERT(!m_bWorking); ++void CPWL_EditImpl::UndoStack::Redo() { ++ DCHECK(!m_bWorking); + m_bWorking = true; + int nRedoRemain = 1; + while (CanRedo() && nRedoRemain > 0) { +@@ -226,14 +205,14 @@ void CPWL_EditImpl_Undo::Redo() { + m_nCurUndoPos++; + nRedoRemain--; + } +- ASSERT(nRedoRemain == 0); +- ASSERT(m_bWorking); ++ DCHECK_EQ(nRedoRemain, 0); ++ DCHECK(m_bWorking); + m_bWorking = false; + } + +-void CPWL_EditImpl_Undo::AddItem(std::unique_ptr pItem) { +- ASSERT(!m_bWorking); +- ASSERT(pItem); ++void CPWL_EditImpl::UndoStack::AddItem(std::unique_ptr pItem) { ++ DCHECK(!m_bWorking); ++ DCHECK(pItem); + if (CanRedo()) + RemoveTails(); + +@@ -244,77 +223,137 @@ void CPWL_EditImpl_Undo::AddItem(std::unique_ptr pItem) { + m_nCurUndoPos = m_UndoItemStack.size(); + } + +-void CPWL_EditImpl_Undo::RemoveHeads() { +- ASSERT(m_UndoItemStack.size() > 1); ++void CPWL_EditImpl::UndoStack::RemoveHeads() { ++ DCHECK(m_UndoItemStack.size() > 1); + m_UndoItemStack.pop_front(); + } + +-void CPWL_EditImpl_Undo::RemoveTails() { ++void CPWL_EditImpl::UndoStack::RemoveTails() { + while (CanRedo()) + m_UndoItemStack.pop_back(); + } + +-CFXEU_InsertWord::CFXEU_InsertWord(CPWL_EditImpl* pEdit, +- const CPVT_WordPlace& wpOldPlace, +- const CPVT_WordPlace& wpNewPlace, +- uint16_t word, +- int32_t charset) ++class CPWL_EditImpl::UndoInsertWord final ++ : public CPWL_EditImpl::UndoItemIface { ++ public: ++ UndoInsertWord(CPWL_EditImpl* pEdit, ++ const CPVT_WordPlace& wpOldPlace, ++ const CPVT_WordPlace& wpNewPlace, ++ uint16_t word, ++ FX_Charset charset); ++ ~UndoInsertWord() override; ++ ++ // UndoItemIface: ++ int Redo() override; ++ int Undo() override; ++ ++ private: ++ UnownedPtr m_pEdit; ++ ++ CPVT_WordPlace m_wpOld; ++ CPVT_WordPlace m_wpNew; ++ uint16_t m_Word; ++ FX_Charset m_nCharset; ++}; ++ ++CPWL_EditImpl::UndoInsertWord::UndoInsertWord(CPWL_EditImpl* pEdit, ++ const CPVT_WordPlace& wpOldPlace, ++ const CPVT_WordPlace& wpNewPlace, ++ uint16_t word, ++ FX_Charset charset) + : m_pEdit(pEdit), + m_wpOld(wpOldPlace), + m_wpNew(wpNewPlace), + m_Word(word), + m_nCharset(charset) { +- ASSERT(m_pEdit); ++ DCHECK(m_pEdit); + } + +-CFXEU_InsertWord::~CFXEU_InsertWord() {} ++CPWL_EditImpl::UndoInsertWord::~UndoInsertWord() = default; + +-int CFXEU_InsertWord::Redo() { ++int CPWL_EditImpl::UndoInsertWord::Redo() { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpOld); +- m_pEdit->InsertWord(m_Word, m_nCharset, false, true); ++ m_pEdit->InsertWord(m_Word, m_nCharset, false); + return 0; + } + +-int CFXEU_InsertWord::Undo() { ++int CPWL_EditImpl::UndoInsertWord::Undo() { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpNew); +- m_pEdit->Backspace(false, true); ++ m_pEdit->Backspace(false); + return 0; + } + +-CFXEU_InsertReturn::CFXEU_InsertReturn(CPWL_EditImpl* pEdit, +- const CPVT_WordPlace& wpOldPlace, +- const CPVT_WordPlace& wpNewPlace) ++class CPWL_EditImpl::UndoInsertReturn final ++ : public CPWL_EditImpl::UndoItemIface { ++ public: ++ UndoInsertReturn(CPWL_EditImpl* pEdit, ++ const CPVT_WordPlace& wpOldPlace, ++ const CPVT_WordPlace& wpNewPlace); ++ ~UndoInsertReturn() override; ++ ++ // UndoItemIface: ++ int Redo() override; ++ int Undo() override; ++ ++ private: ++ UnownedPtr m_pEdit; ++ ++ CPVT_WordPlace m_wpOld; ++ CPVT_WordPlace m_wpNew; ++}; ++ ++CPWL_EditImpl::UndoInsertReturn::UndoInsertReturn( ++ CPWL_EditImpl* pEdit, ++ const CPVT_WordPlace& wpOldPlace, ++ const CPVT_WordPlace& wpNewPlace) + : m_pEdit(pEdit), m_wpOld(wpOldPlace), m_wpNew(wpNewPlace) { +- ASSERT(m_pEdit); ++ DCHECK(m_pEdit); + } + +-CFXEU_InsertReturn::~CFXEU_InsertReturn() {} ++CPWL_EditImpl::UndoInsertReturn::~UndoInsertReturn() = default; + +-int CFXEU_InsertReturn::Redo() { ++int CPWL_EditImpl::UndoInsertReturn::Redo() { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpOld); +- m_pEdit->InsertReturn(false, true); ++ m_pEdit->InsertReturn(false); + return 0; + } + +-int CFXEU_InsertReturn::Undo() { ++int CPWL_EditImpl::UndoInsertReturn::Undo() { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpNew); +- m_pEdit->Backspace(false, true); ++ m_pEdit->Backspace(false); + return 0; + } + +-CFXEU_ReplaceSelection::CFXEU_ReplaceSelection(CPWL_EditImpl* pEdit, +- bool bIsEnd) ++class CPWL_EditImpl::UndoReplaceSelection final ++ : public CPWL_EditImpl::UndoItemIface { ++ public: ++ UndoReplaceSelection(CPWL_EditImpl* pEdit, bool bIsEnd); ++ ~UndoReplaceSelection() override; ++ ++ // UndoItemIface: ++ int Redo() override; ++ int Undo() override; ++ ++ private: ++ bool IsEnd() const { return m_bEnd; } ++ ++ UnownedPtr m_pEdit; ++ const bool m_bEnd; // indicate whether this is the end of replace action ++}; ++ ++CPWL_EditImpl::UndoReplaceSelection::UndoReplaceSelection(CPWL_EditImpl* pEdit, ++ bool bIsEnd) + : m_pEdit(pEdit), m_bEnd(bIsEnd) { +- ASSERT(m_pEdit); ++ DCHECK(m_pEdit); + } + +-CFXEU_ReplaceSelection::~CFXEU_ReplaceSelection() {} ++CPWL_EditImpl::UndoReplaceSelection::~UndoReplaceSelection() = default; + +-int CFXEU_ReplaceSelection::Redo() { ++int CPWL_EditImpl::UndoReplaceSelection::Redo() { + m_pEdit->SelectNone(); + if (IsEnd()) + return 0; +@@ -322,7 +361,7 @@ int CFXEU_ReplaceSelection::Redo() { + return 3; + } + +-int CFXEU_ReplaceSelection::Undo() { ++int CPWL_EditImpl::UndoReplaceSelection::Undo() { + m_pEdit->SelectNone(); + if (!IsEnd()) + return 0; +@@ -331,155 +370,238 @@ int CFXEU_ReplaceSelection::Undo() { + return 3; + } + +-CFXEU_Backspace::CFXEU_Backspace(CPWL_EditImpl* pEdit, +- const CPVT_WordPlace& wpOldPlace, +- const CPVT_WordPlace& wpNewPlace, +- uint16_t word, +- int32_t charset) ++class CPWL_EditImpl::UndoBackspace final : public CPWL_EditImpl::UndoItemIface { ++ public: ++ UndoBackspace(CPWL_EditImpl* pEdit, ++ const CPVT_WordPlace& wpOldPlace, ++ const CPVT_WordPlace& wpNewPlace, ++ uint16_t word, ++ FX_Charset charset); ++ ~UndoBackspace() override; ++ ++ // UndoItemIface: ++ int Redo() override; ++ int Undo() override; ++ ++ private: ++ UnownedPtr m_pEdit; ++ ++ CPVT_WordPlace m_wpOld; ++ CPVT_WordPlace m_wpNew; ++ uint16_t m_Word; ++ FX_Charset m_nCharset; ++}; ++ ++CPWL_EditImpl::UndoBackspace::UndoBackspace(CPWL_EditImpl* pEdit, ++ const CPVT_WordPlace& wpOldPlace, ++ const CPVT_WordPlace& wpNewPlace, ++ uint16_t word, ++ FX_Charset charset) + : m_pEdit(pEdit), + m_wpOld(wpOldPlace), + m_wpNew(wpNewPlace), + m_Word(word), + m_nCharset(charset) { +- ASSERT(m_pEdit); ++ DCHECK(m_pEdit); + } + +-CFXEU_Backspace::~CFXEU_Backspace() {} ++CPWL_EditImpl::UndoBackspace::~UndoBackspace() = default; + +-int CFXEU_Backspace::Redo() { ++int CPWL_EditImpl::UndoBackspace::Redo() { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpOld); +- m_pEdit->Backspace(false, true); ++ m_pEdit->Backspace(false); + return 0; + } + +-int CFXEU_Backspace::Undo() { ++int CPWL_EditImpl::UndoBackspace::Undo() { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpNew); + if (m_wpNew.nSecIndex != m_wpOld.nSecIndex) +- m_pEdit->InsertReturn(false, true); ++ m_pEdit->InsertReturn(false); + else +- m_pEdit->InsertWord(m_Word, m_nCharset, false, true); ++ m_pEdit->InsertWord(m_Word, m_nCharset, false); + return 0; + } + +-CFXEU_Delete::CFXEU_Delete(CPWL_EditImpl* pEdit, +- const CPVT_WordPlace& wpOldPlace, +- const CPVT_WordPlace& wpNewPlace, +- uint16_t word, +- int32_t charset, +- bool bSecEnd) ++class CPWL_EditImpl::UndoDelete final : public CPWL_EditImpl::UndoItemIface { ++ public: ++ UndoDelete(CPWL_EditImpl* pEdit, ++ const CPVT_WordPlace& wpOldPlace, ++ const CPVT_WordPlace& wpNewPlace, ++ uint16_t word, ++ FX_Charset charset, ++ bool bSecEnd); ++ ~UndoDelete() override; ++ ++ // UndoItemIface: ++ int Redo() override; ++ int Undo() override; ++ ++ private: ++ UnownedPtr m_pEdit; ++ ++ CPVT_WordPlace m_wpOld; ++ CPVT_WordPlace m_wpNew; ++ uint16_t m_Word; ++ FX_Charset m_nCharset; ++ bool m_bSecEnd; ++}; ++ ++CPWL_EditImpl::UndoDelete::UndoDelete(CPWL_EditImpl* pEdit, ++ const CPVT_WordPlace& wpOldPlace, ++ const CPVT_WordPlace& wpNewPlace, ++ uint16_t word, ++ FX_Charset charset, ++ bool bSecEnd) + : m_pEdit(pEdit), + m_wpOld(wpOldPlace), + m_wpNew(wpNewPlace), + m_Word(word), + m_nCharset(charset), + m_bSecEnd(bSecEnd) { +- ASSERT(m_pEdit); ++ DCHECK(m_pEdit); + } + +-CFXEU_Delete::~CFXEU_Delete() {} ++CPWL_EditImpl::UndoDelete::~UndoDelete() = default; + +-int CFXEU_Delete::Redo() { ++int CPWL_EditImpl::UndoDelete::Redo() { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpOld); +- m_pEdit->Delete(false, true); ++ m_pEdit->Delete(false); + return 0; + } + +-int CFXEU_Delete::Undo() { ++int CPWL_EditImpl::UndoDelete::Undo() { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpNew); + if (m_bSecEnd) +- m_pEdit->InsertReturn(false, true); ++ m_pEdit->InsertReturn(false); + else +- m_pEdit->InsertWord(m_Word, m_nCharset, false, true); ++ m_pEdit->InsertWord(m_Word, m_nCharset, false); + return 0; + } + +-CFXEU_Clear::CFXEU_Clear(CPWL_EditImpl* pEdit, +- const CPVT_WordRange& wrSel, +- const WideString& swText) ++class CPWL_EditImpl::UndoClear final : public CPWL_EditImpl::UndoItemIface { ++ public: ++ UndoClear(CPWL_EditImpl* pEdit, ++ const CPVT_WordRange& wrSel, ++ const WideString& swText); ++ ~UndoClear() override; ++ ++ // UndoItemIface: ++ int Redo() override; ++ int Undo() override; ++ ++ private: ++ UnownedPtr m_pEdit; ++ ++ CPVT_WordRange m_wrSel; ++ WideString m_swText; ++}; ++ ++CPWL_EditImpl::UndoClear::UndoClear(CPWL_EditImpl* pEdit, ++ const CPVT_WordRange& wrSel, ++ const WideString& swText) + : m_pEdit(pEdit), m_wrSel(wrSel), m_swText(swText) { +- ASSERT(m_pEdit); ++ DCHECK(m_pEdit); + } + +-CFXEU_Clear::~CFXEU_Clear() {} ++CPWL_EditImpl::UndoClear::~UndoClear() = default; + +-int CFXEU_Clear::Redo() { ++int CPWL_EditImpl::UndoClear::Redo() { + m_pEdit->SelectNone(); + m_pEdit->SetSelection(m_wrSel.BeginPos, m_wrSel.EndPos); +- m_pEdit->Clear(false, true); ++ m_pEdit->Clear(false); + return 0; + } + +-int CFXEU_Clear::Undo() { ++int CPWL_EditImpl::UndoClear::Undo() { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wrSel.BeginPos); +- m_pEdit->InsertText(m_swText, FX_CHARSET_Default, false, true); ++ m_pEdit->InsertText(m_swText, FX_Charset::kDefault, false); + m_pEdit->SetSelection(m_wrSel.BeginPos, m_wrSel.EndPos); + return 0; + } + +-CFXEU_InsertText::CFXEU_InsertText(CPWL_EditImpl* pEdit, +- const CPVT_WordPlace& wpOldPlace, +- const CPVT_WordPlace& wpNewPlace, +- const WideString& swText, +- int32_t charset) ++class CPWL_EditImpl::UndoInsertText final ++ : public CPWL_EditImpl::UndoItemIface { ++ public: ++ UndoInsertText(CPWL_EditImpl* pEdit, ++ const CPVT_WordPlace& wpOldPlace, ++ const CPVT_WordPlace& wpNewPlace, ++ const WideString& swText, ++ FX_Charset charset); ++ ~UndoInsertText() override; ++ ++ // UndoItemIface: ++ int Redo() override; ++ int Undo() override; ++ ++ private: ++ UnownedPtr m_pEdit; ++ ++ CPVT_WordPlace m_wpOld; ++ CPVT_WordPlace m_wpNew; ++ WideString m_swText; ++ FX_Charset m_nCharset; ++}; ++ ++CPWL_EditImpl::UndoInsertText::UndoInsertText(CPWL_EditImpl* pEdit, ++ const CPVT_WordPlace& wpOldPlace, ++ const CPVT_WordPlace& wpNewPlace, ++ const WideString& swText, ++ FX_Charset charset) + : m_pEdit(pEdit), + m_wpOld(wpOldPlace), + m_wpNew(wpNewPlace), + m_swText(swText), + m_nCharset(charset) { +- ASSERT(m_pEdit); ++ DCHECK(m_pEdit); + } + +-CFXEU_InsertText::~CFXEU_InsertText() {} ++CPWL_EditImpl::UndoInsertText::~UndoInsertText() = default; + +-int CFXEU_InsertText::Redo() { ++int CPWL_EditImpl::UndoInsertText::Redo() { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpOld); +- m_pEdit->InsertText(m_swText, m_nCharset, false, true); ++ m_pEdit->InsertText(m_swText, m_nCharset, false); + return 0; + } + +-int CFXEU_InsertText::Undo() { ++int CPWL_EditImpl::UndoInsertText::Undo() { + m_pEdit->SelectNone(); + m_pEdit->SetSelection(m_wpOld, m_wpNew); +- m_pEdit->Clear(false, true); ++ m_pEdit->Clear(false); + return 0; + } + +-// static + void CPWL_EditImpl::DrawEdit(CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device, +- CPWL_EditImpl* pEdit, + FX_COLORREF crTextFill, + const CFX_FloatRect& rcClip, + const CFX_PointF& ptOffset, + const CPVT_WordRange* pRange, +- IPWL_SystemHandler* pSystemHandler, +- CFFL_FormFiller* pFFLData) { +- const bool bContinuous = +- pEdit->GetCharArray() == 0 && pEdit->GetCharSpace() <= 0.0f; +- uint16_t SubWord = pEdit->GetPasswordChar(); +- float fFontSize = pEdit->GetFontSize(); +- CPVT_WordRange wrSelect = pEdit->GetSelectWordRange(); ++ IPWL_FillerNotify* pFillerNotify, ++ IPWL_FillerNotify::PerWindowData* pSystemData) { ++ const bool bContinuous = GetCharArray() == 0; ++ uint16_t SubWord = GetPasswordChar(); ++ float fFontSize = GetFontSize(); ++ CPVT_WordRange wrSelect = GetSelectWordRange(); + FX_COLORREF crCurFill = crTextFill; + FX_COLORREF crOldFill = crCurFill; + bool bSelect = false; + const FX_COLORREF crWhite = ArgbEncode(255, 255, 255, 255); + const FX_COLORREF crSelBK = ArgbEncode(255, 0, 51, 113); + +- std::ostringstream sTextBuf; + int32_t nFontIndex = -1; + CFX_PointF ptBT; + CFX_RenderDevice::StateRestorer restorer(pDevice); + if (!rcClip.IsEmpty()) + pDevice->SetClip_Rect(mtUser2Device.TransformRect(rcClip).ToFxRect()); + +- CPWL_EditImpl_Iterator* pIterator = pEdit->GetIterator(); +- IPVT_FontMap* pFontMap = pEdit->GetFontMap(); ++ Iterator* pIterator = GetIterator(); ++ IPVT_FontMap* pFontMap = GetFontMap(); + if (!pFontMap) + return; + +@@ -488,6 +610,7 @@ void CPWL_EditImpl::DrawEdit(CFX_RenderDevice* pDevice, + else + pIterator->SetAt(0); + ++ ByteString sTextBuf; + CPVT_WordPlace oldplace; + while (pIterator->NextWord()) { + CPVT_WordPlace place = pIterator->GetAt(); +@@ -498,7 +621,7 @@ void CPWL_EditImpl::DrawEdit(CFX_RenderDevice* pDevice, + bSelect = place > wrSelect.BeginPos && place <= wrSelect.EndPos; + crCurFill = bSelect ? crWhite : crTextFill; + } +- if (pSystemHandler->IsSelectionImplemented()) { ++ if (pFillerNotify->IsSelectionImplemented()) { + crCurFill = crTextFill; + crOldFill = crCurFill; + } +@@ -507,73 +630,60 @@ void CPWL_EditImpl::DrawEdit(CFX_RenderDevice* pDevice, + if (bSelect) { + CPVT_Line line; + pIterator->GetLine(line); +- +- if (pSystemHandler->IsSelectionImplemented()) { ++ if (pFillerNotify->IsSelectionImplemented()) { + CFX_FloatRect rc(word.ptWord.x, line.ptLine.y + line.fLineDescent, + word.ptWord.x + word.fWidth, + line.ptLine.y + line.fLineAscent); + rc.Intersect(rcClip); +- pSystemHandler->OutputSelectedRect(pFFLData, rc); ++ pFillerNotify->OutputSelectedRect(pSystemData, rc); + } else { +- CFX_PathData pathSelBK; ++ CFX_Path pathSelBK; + pathSelBK.AppendRect(word.ptWord.x, line.ptLine.y + line.fLineDescent, + word.ptWord.x + word.fWidth, + line.ptLine.y + line.fLineAscent); + +- pDevice->DrawPath(&pathSelBK, &mtUser2Device, nullptr, crSelBK, 0, +- FXFILL_WINDING); ++ pDevice->DrawPath(pathSelBK, &mtUser2Device, nullptr, crSelBK, 0, ++ CFX_FillRenderOptions::WindingOptions()); + } + } +- + if (bContinuous) { + if (place.LineCmp(oldplace) != 0 || word.nFontIndex != nFontIndex || + crOldFill != crCurFill) { +- if (sTextBuf.tellp() > 0) { ++ if (!sTextBuf.IsEmpty()) { + DrawTextString(pDevice, + CFX_PointF(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y), + pFontMap->GetPDFFont(nFontIndex).Get(), fFontSize, +- mtUser2Device, ByteString(sTextBuf), crOldFill); +- +- sTextBuf.str(""); ++ mtUser2Device, sTextBuf, crOldFill); ++ sTextBuf.clear(); + } + nFontIndex = word.nFontIndex; + ptBT = word.ptWord; + crOldFill = crCurFill; + } +- +- sTextBuf << pEdit->GetPDFWordString(word.nFontIndex, word.Word, +- SubWord); ++ sTextBuf += GetPDFWordString(word.nFontIndex, word.Word, SubWord); + } else { + DrawTextString( + pDevice, + CFX_PointF(word.ptWord.x + ptOffset.x, word.ptWord.y + ptOffset.y), + pFontMap->GetPDFFont(word.nFontIndex).Get(), fFontSize, + mtUser2Device, +- pEdit->GetPDFWordString(word.nFontIndex, word.Word, SubWord), +- crCurFill); ++ GetPDFWordString(word.nFontIndex, word.Word, SubWord), crCurFill); + } + oldplace = place; + } + } +- +- if (sTextBuf.tellp() > 0) { ++ if (!sTextBuf.IsEmpty()) { + DrawTextString(pDevice, + CFX_PointF(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y), + pFontMap->GetPDFFont(nFontIndex).Get(), fFontSize, +- mtUser2Device, ByteString(sTextBuf), crOldFill); ++ mtUser2Device, sTextBuf, crOldFill); + } + } + + CPWL_EditImpl::CPWL_EditImpl() +- : m_pVT(pdfium::MakeUnique()), +- m_bEnableScroll(false), +- m_nAlignment(0), +- m_bNotifyFlag(false), +- m_bEnableOverflow(false), +- m_bEnableRefresh(true), +- m_bEnableUndo(true) {} ++ : m_pVT(std::make_unique(nullptr)) {} + +-CPWL_EditImpl::~CPWL_EditImpl() {} ++CPWL_EditImpl::~CPWL_EditImpl() = default; + + void CPWL_EditImpl::Initialize() { + m_pVT->Initialize(); +@@ -582,23 +692,17 @@ void CPWL_EditImpl::Initialize() { + } + + void CPWL_EditImpl::SetFontMap(IPVT_FontMap* pFontMap) { +- m_pVTProvider = pdfium::MakeUnique(pFontMap); ++ m_pVTProvider = std::make_unique(pFontMap); + m_pVT->SetProvider(m_pVTProvider.get()); + } + +-void CPWL_EditImpl::SetNotify(CPWL_EditCtrl* pNotify) { ++void CPWL_EditImpl::SetNotify(CPWL_Edit* pNotify) { + m_pNotify = pNotify; + } + +-void CPWL_EditImpl::SetOperationNotify(CPWL_Edit* pOperationNotify) { +- m_pOperationNotify = pOperationNotify; +-} +- +-CPWL_EditImpl_Iterator* CPWL_EditImpl::GetIterator() { +- if (!m_pIterator) { +- m_pIterator = +- pdfium::MakeUnique(this, m_pVT->GetIterator()); +- } ++CPWL_EditImpl::Iterator* CPWL_EditImpl::GetIterator() { ++ if (!m_pIterator) ++ m_pIterator = std::make_unique(this, m_pVT->GetIterator()); + return m_pIterator.get(); + } + +@@ -609,75 +713,50 @@ IPVT_FontMap* CPWL_EditImpl::GetFontMap() { + void CPWL_EditImpl::SetPlateRect(const CFX_FloatRect& rect) { + m_pVT->SetPlateRect(rect); + m_ptScrollPos = CFX_PointF(rect.left, rect.top); +- Paint(); + } + +-void CPWL_EditImpl::SetAlignmentH(int32_t nFormat, bool bPaint) { ++void CPWL_EditImpl::SetAlignmentH(int32_t nFormat) { + m_pVT->SetAlignment(nFormat); +- if (bPaint) +- Paint(); + } + +-void CPWL_EditImpl::SetAlignmentV(int32_t nFormat, bool bPaint) { ++void CPWL_EditImpl::SetAlignmentV(int32_t nFormat) { + m_nAlignment = nFormat; +- if (bPaint) +- Paint(); + } + +-void CPWL_EditImpl::SetPasswordChar(uint16_t wSubWord, bool bPaint) { ++void CPWL_EditImpl::SetPasswordChar(uint16_t wSubWord) { + m_pVT->SetPasswordChar(wSubWord); +- if (bPaint) +- Paint(); + } + + void CPWL_EditImpl::SetLimitChar(int32_t nLimitChar) { + m_pVT->SetLimitChar(nLimitChar); +- Paint(); + } + + void CPWL_EditImpl::SetCharArray(int32_t nCharArray) { + m_pVT->SetCharArray(nCharArray); +- Paint(); + } + +-void CPWL_EditImpl::SetCharSpace(float fCharSpace) { +- m_pVT->SetCharSpace(fCharSpace); +- Paint(); +-} +- +-void CPWL_EditImpl::SetMultiLine(bool bMultiLine, bool bPaint) { ++void CPWL_EditImpl::SetMultiLine(bool bMultiLine) { + m_pVT->SetMultiLine(bMultiLine); +- if (bPaint) +- Paint(); + } + +-void CPWL_EditImpl::SetAutoReturn(bool bAuto, bool bPaint) { ++void CPWL_EditImpl::SetAutoReturn(bool bAuto) { + m_pVT->SetAutoReturn(bAuto); +- if (bPaint) +- Paint(); + } + +-void CPWL_EditImpl::SetAutoFontSize(bool bAuto, bool bPaint) { ++void CPWL_EditImpl::SetAutoFontSize(bool bAuto) { + m_pVT->SetAutoFontSize(bAuto); +- if (bPaint) +- Paint(); + } + + void CPWL_EditImpl::SetFontSize(float fFontSize) { + m_pVT->SetFontSize(fFontSize); +- Paint(); + } + +-void CPWL_EditImpl::SetAutoScroll(bool bAuto, bool bPaint) { ++void CPWL_EditImpl::SetAutoScroll(bool bAuto) { + m_bEnableScroll = bAuto; +- if (bPaint) +- Paint(); + } + +-void CPWL_EditImpl::SetTextOverflow(bool bAllowed, bool bPaint) { ++void CPWL_EditImpl::SetTextOverflow(bool bAllowed) { + m_bEnableOverflow = bAllowed; +- if (bPaint) +- Paint(); + } + + void CPWL_EditImpl::SetSelection(int32_t nStartChar, int32_t nEndChar) { +@@ -712,24 +791,20 @@ void CPWL_EditImpl::SetSelection(const CPVT_WordPlace& begin, + SetCaretInfo(); + } + +-void CPWL_EditImpl::GetSelection(int32_t& nStartChar, int32_t& nEndChar) const { +- nStartChar = -1; +- nEndChar = -1; ++std::pair CPWL_EditImpl::GetSelection() const { + if (!m_pVT->IsValid()) +- return; ++ return std::make_pair(-1, -1); + + if (m_SelState.IsEmpty()) { +- nStartChar = m_pVT->WordPlaceToWordIndex(m_wpCaret); +- nEndChar = m_pVT->WordPlaceToWordIndex(m_wpCaret); +- return; ++ return std::make_pair(m_pVT->WordPlaceToWordIndex(m_wpCaret), ++ m_pVT->WordPlaceToWordIndex(m_wpCaret)); + } + if (m_SelState.BeginPos < m_SelState.EndPos) { +- nStartChar = m_pVT->WordPlaceToWordIndex(m_SelState.BeginPos); +- nEndChar = m_pVT->WordPlaceToWordIndex(m_SelState.EndPos); +- return; ++ return std::make_pair(m_pVT->WordPlaceToWordIndex(m_SelState.BeginPos), ++ m_pVT->WordPlaceToWordIndex(m_SelState.EndPos)); + } +- nStartChar = m_pVT->WordPlaceToWordIndex(m_SelState.EndPos); +- nEndChar = m_pVT->WordPlaceToWordIndex(m_SelState.BeginPos); ++ return std::make_pair(m_pVT->WordPlaceToWordIndex(m_SelState.EndPos), ++ m_pVT->WordPlaceToWordIndex(m_SelState.BeginPos)); + } + + int32_t CPWL_EditImpl::GetCaret() const { +@@ -748,7 +823,7 @@ WideString CPWL_EditImpl::GetText() const { + if (!m_pVT->IsValid()) + return swRet; + +- CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); ++ CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + pIterator->SetAt(0); + + CPVT_Word wordinfo; +@@ -769,7 +844,7 @@ WideString CPWL_EditImpl::GetRangeText(const CPVT_WordRange& range) const { + if (!m_pVT->IsValid()) + return swRet; + +- CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); ++ CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + CPVT_WordRange wrTemp = range; + m_pVT->UpdateWordPlace(wrTemp.BeginPos); + m_pVT->UpdateWordPlace(wrTemp.EndPos); +@@ -797,7 +872,7 @@ WideString CPWL_EditImpl::GetSelectedText() const { + int32_t CPWL_EditImpl::GetTotalLines() const { + int32_t nLines = 1; + +- CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); ++ CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + pIterator->SetAt(0); + while (pIterator->NextLine()) + ++nLines; +@@ -811,32 +886,31 @@ CPVT_WordRange CPWL_EditImpl::GetSelectWordRange() const { + + void CPWL_EditImpl::SetText(const WideString& sText) { + Clear(); +- DoInsertText(CPVT_WordPlace(0, 0, -1), sText, FX_CHARSET_Default); +- Paint(); ++ DoInsertText(CPVT_WordPlace(0, 0, -1), sText, FX_Charset::kDefault); + } + +-bool CPWL_EditImpl::InsertWord(uint16_t word, int32_t charset) { +- return InsertWord(word, charset, true, true); ++bool CPWL_EditImpl::InsertWord(uint16_t word, FX_Charset charset) { ++ return InsertWord(word, charset, true); + } + + bool CPWL_EditImpl::InsertReturn() { +- return InsertReturn(true, true); ++ return InsertReturn(true); + } + + bool CPWL_EditImpl::Backspace() { +- return Backspace(true, true); ++ return Backspace(true); + } + + bool CPWL_EditImpl::Delete() { +- return Delete(true, true); ++ return Delete(true); + } + + bool CPWL_EditImpl::ClearSelection() { +- return Clear(true, true); ++ return Clear(true); + } + +-bool CPWL_EditImpl::InsertText(const WideString& sText, int32_t charset) { +- return InsertText(sText, charset, true, true); ++bool CPWL_EditImpl::InsertText(const WideString& sText, FX_Charset charset) { ++ return InsertText(sText, charset, true); + } + + float CPWL_EditImpl::GetFontSize() const { +@@ -855,10 +929,6 @@ CFX_FloatRect CPWL_EditImpl::GetContentRect() const { + return VTToEdit(m_pVT->GetContentRect()); + } + +-float CPWL_EditImpl::GetCharSpace() const { +- return m_pVT->GetCharSpace(); +-} +- + CPVT_WordRange CPWL_EditImpl::GetWholeWordRange() const { + if (m_pVT->IsValid()) + return CPVT_WordRange(m_pVT->GetBeginWordPlace(), m_pVT->GetEndWordPlace()); +@@ -935,7 +1005,7 @@ void CPWL_EditImpl::SetContentChanged() { + void CPWL_EditImpl::SelectAll() { + if (!m_pVT->IsValid()) + return; +- m_SelState = CPWL_EditImpl_Select(GetWholeWordRange()); ++ m_SelState = SelectState(GetWholeWordRange()); + SetCaret(m_SelState.EndPos); + ScrollToCaret(); + Refresh(); +@@ -1032,7 +1102,7 @@ void CPWL_EditImpl::SetScrollPosX(float fx) { + return; + + if (m_pVT->IsValid()) { +- if (!IsFloatEqual(m_ptScrollPos.x, fx)) { ++ if (!FXSYS_IsFloatEqual(m_ptScrollPos.x, fx)) { + m_ptScrollPos.x = fx; + Refresh(); + } +@@ -1044,7 +1114,7 @@ void CPWL_EditImpl::SetScrollPosY(float fy) { + return; + + if (m_pVT->IsValid()) { +- if (!IsFloatEqual(m_ptScrollPos.y, fy)) { ++ if (!FXSYS_IsFloatEqual(m_ptScrollPos.y, fy)) { + m_ptScrollPos.y = fy; + Refresh(); + +@@ -1078,10 +1148,10 @@ void CPWL_EditImpl::SetScrollLimit() { + if (rcPlate.Width() > rcContent.Width()) { + SetScrollPosX(rcPlate.left); + } else { +- if (IsFloatSmaller(m_ptScrollPos.x, rcContent.left)) { ++ if (FXSYS_IsFloatSmaller(m_ptScrollPos.x, rcContent.left)) { + SetScrollPosX(rcContent.left); +- } else if (IsFloatBigger(m_ptScrollPos.x, +- rcContent.right - rcPlate.Width())) { ++ } else if (FXSYS_IsFloatBigger(m_ptScrollPos.x, ++ rcContent.right - rcPlate.Width())) { + SetScrollPosX(rcContent.right - rcPlate.Width()); + } + } +@@ -1089,10 +1159,10 @@ void CPWL_EditImpl::SetScrollLimit() { + if (rcPlate.Height() > rcContent.Height()) { + SetScrollPosY(rcPlate.top); + } else { +- if (IsFloatSmaller(m_ptScrollPos.y, +- rcContent.bottom + rcPlate.Height())) { ++ if (FXSYS_IsFloatSmaller(m_ptScrollPos.y, ++ rcContent.bottom + rcPlate.Height())) { + SetScrollPosY(rcContent.bottom + rcPlate.Height()); +- } else if (IsFloatBigger(m_ptScrollPos.y, rcContent.top)) { ++ } else if (FXSYS_IsFloatBigger(m_ptScrollPos.y, rcContent.top)) { + SetScrollPosY(rcContent.top); + } + } +@@ -1105,7 +1175,7 @@ void CPWL_EditImpl::ScrollToCaret() { + if (!m_pVT->IsValid()) + return; + +- CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); ++ CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + pIterator->SetAt(m_wpCaret); + + CFX_PointF ptHead; +@@ -1127,23 +1197,23 @@ void CPWL_EditImpl::ScrollToCaret() { + CFX_PointF ptHeadEdit = VTToEdit(ptHead); + CFX_PointF ptFootEdit = VTToEdit(ptFoot); + CFX_FloatRect rcPlate = m_pVT->GetPlateRect(); +- if (!IsFloatEqual(rcPlate.left, rcPlate.right)) { +- if (IsFloatSmaller(ptHeadEdit.x, rcPlate.left) || +- IsFloatEqual(ptHeadEdit.x, rcPlate.left)) { ++ if (!FXSYS_IsFloatEqual(rcPlate.left, rcPlate.right)) { ++ if (FXSYS_IsFloatSmaller(ptHeadEdit.x, rcPlate.left) || ++ FXSYS_IsFloatEqual(ptHeadEdit.x, rcPlate.left)) { + SetScrollPosX(ptHead.x); +- } else if (IsFloatBigger(ptHeadEdit.x, rcPlate.right)) { ++ } else if (FXSYS_IsFloatBigger(ptHeadEdit.x, rcPlate.right)) { + SetScrollPosX(ptHead.x - rcPlate.Width()); + } + } + +- if (!IsFloatEqual(rcPlate.top, rcPlate.bottom)) { +- if (IsFloatSmaller(ptFootEdit.y, rcPlate.bottom) || +- IsFloatEqual(ptFootEdit.y, rcPlate.bottom)) { +- if (IsFloatSmaller(ptHeadEdit.y, rcPlate.top)) { ++ if (!FXSYS_IsFloatEqual(rcPlate.top, rcPlate.bottom)) { ++ if (FXSYS_IsFloatSmaller(ptFootEdit.y, rcPlate.bottom) || ++ FXSYS_IsFloatEqual(ptFootEdit.y, rcPlate.bottom)) { ++ if (FXSYS_IsFloatSmaller(ptHeadEdit.y, rcPlate.top)) { + SetScrollPosY(ptFoot.y + rcPlate.Height()); + } +- } else if (IsFloatBigger(ptHeadEdit.y, rcPlate.top)) { +- if (IsFloatBigger(ptFootEdit.y, rcPlate.bottom)) { ++ } else if (FXSYS_IsFloatBigger(ptHeadEdit.y, rcPlate.top)) { ++ if (FXSYS_IsFloatBigger(ptFootEdit.y, rcPlate.bottom)) { + SetScrollPosY(ptHead.y); + } + } +@@ -1177,7 +1247,7 @@ void CPWL_EditImpl::RefreshPushLineRects(const CPVT_WordRange& wr) { + if (!m_pVT->IsValid()) + return; + +- CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); ++ CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + CPVT_WordPlace wpBegin = wr.BeginPos; + m_pVT->UpdateWordPlace(wpBegin); + CPVT_WordPlace wpEnd = wr.EndPos; +@@ -1202,7 +1272,7 @@ void CPWL_EditImpl::RefreshPushLineRects(const CPVT_WordRange& wr) { + } + + void CPWL_EditImpl::RefreshWordRange(const CPVT_WordRange& wr) { +- CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); ++ CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + CPVT_WordRange wrTemp = wr; + + m_pVT->UpdateWordPlace(wrTemp.BeginPos); +@@ -1263,7 +1333,7 @@ void CPWL_EditImpl::SetCaret(const CPVT_WordPlace& place) { + void CPWL_EditImpl::SetCaretInfo() { + if (m_pNotify) { + if (!m_bNotifyFlag) { +- CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); ++ CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + pIterator->SetAt(m_wpCaret); + + CFX_PointF ptHead; +@@ -1321,7 +1391,7 @@ void CPWL_EditImpl::OnMouseMove(const CFX_PointF& point, + SetCaretInfo(); + } + +-void CPWL_EditImpl::OnVK_UP(bool bShift, bool bCtrl) { ++void CPWL_EditImpl::OnVK_UP(bool bShift) { + if (!m_pVT->IsValid()) + return; + +@@ -1344,7 +1414,7 @@ void CPWL_EditImpl::OnVK_UP(bool bShift, bool bCtrl) { + } + } + +-void CPWL_EditImpl::OnVK_DOWN(bool bShift, bool bCtrl) { ++void CPWL_EditImpl::OnVK_DOWN(bool bShift) { + if (!m_pVT->IsValid()) + return; + +@@ -1367,7 +1437,7 @@ void CPWL_EditImpl::OnVK_DOWN(bool bShift, bool bCtrl) { + } + } + +-void CPWL_EditImpl::OnVK_LEFT(bool bShift, bool bCtrl) { ++void CPWL_EditImpl::OnVK_LEFT(bool bShift) { + if (!m_pVT->IsValid()) + return; + +@@ -1410,7 +1480,7 @@ void CPWL_EditImpl::OnVK_LEFT(bool bShift, bool bCtrl) { + } + } + +-void CPWL_EditImpl::OnVK_RIGHT(bool bShift, bool bCtrl) { ++void CPWL_EditImpl::OnVK_RIGHT(bool bShift) { + if (!m_pVT->IsValid()) + return; + +@@ -1528,9 +1598,8 @@ void CPWL_EditImpl::OnVK_END(bool bShift, bool bCtrl) { + } + + bool CPWL_EditImpl::InsertWord(uint16_t word, +- int32_t charset, +- bool bAddUndo, +- bool bPaint) { ++ FX_Charset charset, ++ bool bAddUndo) { + if (IsTextOverflow() || !m_pVT->IsValid()) + return false; + +@@ -1542,19 +1611,14 @@ bool CPWL_EditImpl::InsertWord(uint16_t word, + return false; + + if (bAddUndo && m_bEnableUndo) { +- AddEditUndoItem(pdfium::MakeUnique( +- this, m_wpOldCaret, m_wpCaret, word, charset)); ++ AddEditUndoItem(std::make_unique(this, m_wpOldCaret, ++ m_wpCaret, word, charset)); + } +- if (bPaint) +- PaintInsertText(m_wpOldCaret, m_wpCaret); +- +- if (m_pOperationNotify) +- m_pOperationNotify->OnInsertWord(m_wpCaret, m_wpOldCaret); +- ++ PaintInsertText(m_wpOldCaret, m_wpCaret); + return true; + } + +-bool CPWL_EditImpl::InsertReturn(bool bAddUndo, bool bPaint) { ++bool CPWL_EditImpl::InsertReturn(bool bAddUndo) { + if (IsTextOverflow() || !m_pVT->IsValid()) + return false; + +@@ -1566,28 +1630,23 @@ bool CPWL_EditImpl::InsertReturn(bool bAddUndo, bool bPaint) { + + if (bAddUndo && m_bEnableUndo) { + AddEditUndoItem( +- pdfium::MakeUnique(this, m_wpOldCaret, m_wpCaret)); ++ std::make_unique(this, m_wpOldCaret, m_wpCaret)); + } +- if (bPaint) { +- RearrangePart(CPVT_WordRange(m_wpOldCaret, m_wpCaret)); +- ScrollToCaret(); +- Refresh(); +- SetCaretOrigin(); +- SetCaretInfo(); +- } +- if (m_pOperationNotify) +- m_pOperationNotify->OnInsertReturn(m_wpCaret, m_wpOldCaret); +- ++ RearrangePart(CPVT_WordRange(m_wpOldCaret, m_wpCaret)); ++ ScrollToCaret(); ++ Refresh(); ++ SetCaretOrigin(); ++ SetCaretInfo(); + return true; + } + +-bool CPWL_EditImpl::Backspace(bool bAddUndo, bool bPaint) { ++bool CPWL_EditImpl::Backspace(bool bAddUndo) { + if (!m_pVT->IsValid() || m_wpCaret == m_pVT->GetBeginWordPlace()) + return false; + + CPVT_Word word; + if (bAddUndo) { +- CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); ++ CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + pIterator->SetAt(m_wpCaret); + pIterator->GetWord(word); + } +@@ -1598,29 +1657,24 @@ bool CPWL_EditImpl::Backspace(bool bAddUndo, bool bPaint) { + return false; + + if (bAddUndo && m_bEnableUndo) { +- AddEditUndoItem(pdfium::MakeUnique( ++ AddEditUndoItem(std::make_unique( + this, m_wpOldCaret, m_wpCaret, word.Word, word.nCharset)); + } +- if (bPaint) { +- RearrangePart(CPVT_WordRange(m_wpCaret, m_wpOldCaret)); +- ScrollToCaret(); +- Refresh(); +- SetCaretOrigin(); +- SetCaretInfo(); +- } +- if (m_pOperationNotify) +- m_pOperationNotify->OnBackSpace(m_wpCaret, m_wpOldCaret); +- ++ RearrangePart(CPVT_WordRange(m_wpCaret, m_wpOldCaret)); ++ ScrollToCaret(); ++ Refresh(); ++ SetCaretOrigin(); ++ SetCaretInfo(); + return true; + } + +-bool CPWL_EditImpl::Delete(bool bAddUndo, bool bPaint) { ++bool CPWL_EditImpl::Delete(bool bAddUndo) { + if (!m_pVT->IsValid() || m_wpCaret == m_pVT->GetEndWordPlace()) + return false; + + CPVT_Word word; + if (bAddUndo) { +- CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); ++ CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + pIterator->SetAt(m_pVT->GetNextWordPlace(m_wpCaret)); + pIterator->GetWord(word); + } +@@ -1630,23 +1684,18 @@ bool CPWL_EditImpl::Delete(bool bAddUndo, bool bPaint) { + m_SelState.Set(m_wpCaret, m_wpCaret); + if (bAddUndo && m_bEnableUndo) { + if (bSecEnd) { +- AddEditUndoItem(pdfium::MakeUnique( ++ AddEditUndoItem(std::make_unique( + this, m_wpOldCaret, m_wpCaret, word.Word, word.nCharset, bSecEnd)); + } else { +- AddEditUndoItem(pdfium::MakeUnique( ++ AddEditUndoItem(std::make_unique( + this, m_wpOldCaret, m_wpCaret, word.Word, word.nCharset, bSecEnd)); + } + } +- if (bPaint) { +- RearrangePart(CPVT_WordRange(m_wpOldCaret, m_wpCaret)); +- ScrollToCaret(); +- Refresh(); +- SetCaretOrigin(); +- SetCaretInfo(); +- } +- if (m_pOperationNotify) +- m_pOperationNotify->OnDelete(m_wpCaret, m_wpOldCaret); +- ++ RearrangePart(CPVT_WordRange(m_wpOldCaret, m_wpCaret)); ++ ScrollToCaret(); ++ Refresh(); ++ SetCaretOrigin(); ++ SetCaretInfo(); + return true; + } + +@@ -1661,36 +1710,29 @@ bool CPWL_EditImpl::Clear() { + return false; + } + +-bool CPWL_EditImpl::Clear(bool bAddUndo, bool bPaint) { ++bool CPWL_EditImpl::Clear(bool bAddUndo) { + if (!m_pVT->IsValid() || m_SelState.IsEmpty()) + return false; + + CPVT_WordRange range = m_SelState.ConvertToWordRange(); + if (bAddUndo && m_bEnableUndo) { + AddEditUndoItem( +- pdfium::MakeUnique(this, range, GetSelectedText())); ++ std::make_unique(this, range, GetSelectedText())); + } +- + SelectNone(); + SetCaret(m_pVT->DeleteWords(range)); + m_SelState.Set(m_wpCaret, m_wpCaret); +- if (bPaint) { +- RearrangePart(range); +- ScrollToCaret(); +- Refresh(); +- SetCaretOrigin(); +- SetCaretInfo(); +- } +- if (m_pOperationNotify) +- m_pOperationNotify->OnClear(m_wpCaret, m_wpOldCaret); +- ++ RearrangePart(range); ++ ScrollToCaret(); ++ Refresh(); ++ SetCaretOrigin(); ++ SetCaretInfo(); + return true; + } + + bool CPWL_EditImpl::InsertText(const WideString& sText, +- int32_t charset, +- bool bAddUndo, +- bool bPaint) { ++ FX_Charset charset, ++ bool bAddUndo) { + if (IsTextOverflow()) + return false; + +@@ -1701,15 +1743,10 @@ bool CPWL_EditImpl::InsertText(const WideString& sText, + return false; + + if (bAddUndo && m_bEnableUndo) { +- AddEditUndoItem(pdfium::MakeUnique( ++ AddEditUndoItem(std::make_unique( + this, m_wpOldCaret, m_wpCaret, sText, charset)); + } +- if (bPaint) +- PaintInsertText(m_wpOldCaret, m_wpCaret); +- +- if (m_pOperationNotify) +- m_pOperationNotify->OnInsertText(m_wpCaret, m_wpOldCaret); +- ++ PaintInsertText(m_wpOldCaret, m_wpCaret); + return true; + } + +@@ -1724,11 +1761,24 @@ void CPWL_EditImpl::PaintInsertText(const CPVT_WordPlace& wpOld, + } + } + ++void CPWL_EditImpl::ReplaceAndKeepSelection(const WideString& text) { ++ AddEditUndoItem(std::make_unique(this, false)); ++ ClearSelection(); ++ ++ // Select the inserted text. ++ CPVT_WordPlace caret_before_insert = m_wpCaret; ++ InsertText(text, FX_Charset::kDefault); ++ CPVT_WordPlace caret_after_insert = m_wpCaret; ++ m_SelState.Set(caret_before_insert, caret_after_insert); ++ ++ AddEditUndoItem(std::make_unique(this, true)); ++} ++ + void CPWL_EditImpl::ReplaceSelection(const WideString& text) { +- AddEditUndoItem(pdfium::MakeUnique(this, false)); ++ AddEditUndoItem(std::make_unique(this, false)); + ClearSelection(); +- InsertText(text, FX_CHARSET_Default); +- AddEditUndoItem(pdfium::MakeUnique(this, true)); ++ InsertText(text, FX_Charset::kDefault); ++ AddEditUndoItem(std::make_unique(this, true)); + } + + bool CPWL_EditImpl::Redo() { +@@ -1757,7 +1807,7 @@ void CPWL_EditImpl::SetCaretOrigin() { + if (!m_pVT->IsValid()) + return; + +- CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); ++ CPVT_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + pIterator->SetAt(m_wpCaret); + CPVT_Word word; + CPVT_Line line; +@@ -1792,11 +1842,11 @@ bool CPWL_EditImpl::IsTextOverflow() const { + CFX_FloatRect rcContent = m_pVT->GetContentRect(); + + if (m_pVT->IsMultiLine() && GetTotalLines() > 1 && +- IsFloatBigger(rcContent.Height(), rcPlate.Height())) { ++ FXSYS_IsFloatBigger(rcContent.Height(), rcPlate.Height())) { + return true; + } + +- if (IsFloatBigger(rcContent.Width(), rcPlate.Width())) ++ if (FXSYS_IsFloatBigger(rcContent.Width(), rcPlate.Width())) + return true; + } + +@@ -1829,44 +1879,42 @@ void CPWL_EditImpl::EnableUndo(bool bUndo) { + + CPVT_WordPlace CPWL_EditImpl::DoInsertText(const CPVT_WordPlace& place, + const WideString& sText, +- int32_t charset) { +- CPVT_WordPlace wp = place; ++ FX_Charset charset) { ++ if (!m_pVT->IsValid()) ++ return place; + +- if (m_pVT->IsValid()) { +- for (int32_t i = 0, sz = sText.GetLength(); i < sz; i++) { +- uint16_t word = sText[i]; +- switch (word) { +- case '\r': +- wp = m_pVT->InsertSection(wp); +- if (i + 1 < sz && sText[i + 1] == '\n') +- i++; +- break; +- case '\n': +- wp = m_pVT->InsertSection(wp); +- break; +- case '\t': +- word = ' '; +- FALLTHROUGH; +- default: +- wp = +- m_pVT->InsertWord(wp, word, GetCharSetFromUnicode(word, charset)); +- break; +- } ++ CPVT_WordPlace wp = place; ++ for (size_t i = 0; i < sText.GetLength(); ++i) { ++ uint16_t word = sText[i]; ++ switch (word) { ++ case '\r': ++ wp = m_pVT->InsertSection(wp); ++ if (i + 1 < sText.GetLength() && sText[i + 1] == '\n') ++ i++; ++ break; ++ case '\n': ++ wp = m_pVT->InsertSection(wp); ++ break; ++ case '\t': ++ word = ' '; ++ [[fallthrough]]; ++ default: ++ wp = m_pVT->InsertWord(wp, word, GetCharSetFromUnicode(word, charset)); ++ break; + } + } +- + return wp; + } + +-int32_t CPWL_EditImpl::GetCharSetFromUnicode(uint16_t word, +- int32_t nOldCharset) { ++FX_Charset CPWL_EditImpl::GetCharSetFromUnicode(uint16_t word, ++ FX_Charset nOldCharset) { + if (IPVT_FontMap* pFontMap = GetFontMap()) + return pFontMap->CharSetFromUnicode(word, nOldCharset); + return nOldCharset; + } + + void CPWL_EditImpl::AddEditUndoItem( +- std::unique_ptr pEditUndoItem) { ++ std::unique_ptr pEditUndoItem) { + m_Undo.AddItem(std::move(pEditUndoItem)); + } + +@@ -1894,31 +1942,31 @@ ByteString CPWL_EditImpl::GetPDFWordString(int32_t nFontIndex, + return sWord; + } + +-CPWL_EditImpl_Select::CPWL_EditImpl_Select() {} ++CPWL_EditImpl::SelectState::SelectState() = default; + +-CPWL_EditImpl_Select::CPWL_EditImpl_Select(const CPVT_WordRange& range) { ++CPWL_EditImpl::SelectState::SelectState(const CPVT_WordRange& range) { + Set(range.BeginPos, range.EndPos); + } + +-CPVT_WordRange CPWL_EditImpl_Select::ConvertToWordRange() const { ++CPVT_WordRange CPWL_EditImpl::SelectState::ConvertToWordRange() const { + return CPVT_WordRange(BeginPos, EndPos); + } + +-void CPWL_EditImpl_Select::Reset() { ++void CPWL_EditImpl::SelectState::Reset() { + BeginPos.Reset(); + EndPos.Reset(); + } + +-void CPWL_EditImpl_Select::Set(const CPVT_WordPlace& begin, +- const CPVT_WordPlace& end) { ++void CPWL_EditImpl::SelectState::Set(const CPVT_WordPlace& begin, ++ const CPVT_WordPlace& end) { + BeginPos = begin; + EndPos = end; + } + +-void CPWL_EditImpl_Select::SetEndPos(const CPVT_WordPlace& end) { ++void CPWL_EditImpl::SelectState::SetEndPos(const CPVT_WordPlace& end) { + EndPos = end; + } + +-bool CPWL_EditImpl_Select::IsEmpty() const { ++bool CPWL_EditImpl::SelectState::IsEmpty() const { + return BeginPos == EndPos; + } +diff --git a/fpdfsdk/pwl/cpwl_edit_impl.h b/fpdfsdk/pwl/cpwl_edit_impl.h +index e9ac7de51..0b3bc76df 100644 +--- a/fpdfsdk/pwl/cpwl_edit_impl.h ++++ b/fpdfsdk/pwl/cpwl_edit_impl.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,270 +9,56 @@ + + #include + #include ++#include + #include + +-#include "core/fpdfdoc/cpdf_variabletext.h" ++#include "core/fpdfdoc/cpvt_variabletext.h" + #include "core/fpdfdoc/cpvt_wordrange.h" ++#include "core/fxcrt/bytestring.h" ++#include "core/fxcrt/fx_codepage_forward.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" ++#include "fpdfsdk/pwl/ipwl_fillernotify.h" + +-#define FX_EDIT_ISLATINWORD(u) \ +- (u == 0x2D || (u <= 0x005A && u >= 0x0041) || \ +- (u <= 0x007A && u >= 0x0061) || (u <= 0x02AF && u >= 0x00C0)) +- +-class CFFL_FormFiller; +-class CPWL_EditImpl; +-class CPWL_EditImpl_Iterator; +-class CPWL_EditImpl_Provider; + class CFX_RenderDevice; + class CPWL_Edit; +-class CPWL_EditCtrl; +-class IPWL_SystemHandler; +-class IFX_Edit_UndoItem; +- +-struct CPWL_EditImpl_LineRect { +- CPWL_EditImpl_LineRect(const CPVT_WordRange& wrLine, +- const CFX_FloatRect& rcLine) +- : m_wrLine(wrLine), m_rcLine(rcLine) {} +- +- CPVT_WordRange m_wrLine; +- CFX_FloatRect m_rcLine; +-}; +- +-class CPWL_EditImpl_Refresh { +- public: +- CPWL_EditImpl_Refresh(); +- ~CPWL_EditImpl_Refresh(); +- +- void BeginRefresh(); +- void Push(const CPVT_WordRange& linerange, const CFX_FloatRect& rect); +- void NoAnalyse(); +- std::vector* GetRefreshRects(); +- void EndRefresh(); +- +- private: +- void Add(const CFX_FloatRect& new_rect); +- +- std::vector m_NewLineRects; +- std::vector m_OldLineRects; +- std::vector m_RefreshRects; +-}; +- +-class CPWL_EditImpl_Select { +- public: +- CPWL_EditImpl_Select(); +- explicit CPWL_EditImpl_Select(const CPVT_WordRange& range); +- +- void Reset(); +- void Set(const CPVT_WordPlace& begin, const CPVT_WordPlace& end); +- void SetEndPos(const CPVT_WordPlace& end); +- +- CPVT_WordRange ConvertToWordRange() const; +- bool IsEmpty() const; +- +- CPVT_WordPlace BeginPos; +- CPVT_WordPlace EndPos; +-}; +- +-class CPWL_EditImpl_Undo { +- public: +- CPWL_EditImpl_Undo(); +- ~CPWL_EditImpl_Undo(); +- +- void AddItem(std::unique_ptr pItem); +- void Undo(); +- void Redo(); +- bool CanUndo() const; +- bool CanRedo() const; +- +- private: +- void RemoveHeads(); +- void RemoveTails(); +- +- std::deque> m_UndoItemStack; +- size_t m_nCurUndoPos; +- bool m_bWorking; +-}; +- +-class IFX_Edit_UndoItem { +- public: +- virtual ~IFX_Edit_UndoItem() = default; +- +- // Undo/Redo the current undo item and returns the number of additional items +- // to be processed in |m_UndoItemStack| to fully undo/redo the action. (An +- // example is CFXEU_ReplaceSelection::Undo(), if CFXEU_ReplaceSelection marks +- // the end of a replace action, CFXEU_ReplaceSelection::Undo() returns 3 +- // because 3 more undo items need to be processed to revert the replace +- // action: insert text, clear selection and the CFXEU_ReplaceSelection which +- // marks the beginning of replace action.) Implementations should return 0 by +- // default. +- virtual int Undo() = 0; +- virtual int Redo() = 0; +-}; +- +-class CFXEU_InsertWord final : public IFX_Edit_UndoItem { +- public: +- CFXEU_InsertWord(CPWL_EditImpl* pEdit, +- const CPVT_WordPlace& wpOldPlace, +- const CPVT_WordPlace& wpNewPlace, +- uint16_t word, +- int32_t charset); +- ~CFXEU_InsertWord() override; +- +- // IFX_Edit_UndoItem: +- int Redo() override; +- int Undo() override; +- +- private: +- UnownedPtr m_pEdit; +- +- CPVT_WordPlace m_wpOld; +- CPVT_WordPlace m_wpNew; +- uint16_t m_Word; +- int32_t m_nCharset; +-}; +- +-class CFXEU_InsertReturn final : public IFX_Edit_UndoItem { +- public: +- CFXEU_InsertReturn(CPWL_EditImpl* pEdit, +- const CPVT_WordPlace& wpOldPlace, +- const CPVT_WordPlace& wpNewPlace); +- ~CFXEU_InsertReturn() override; +- +- // IFX_Edit_UndoItem: +- int Redo() override; +- int Undo() override; +- +- private: +- UnownedPtr m_pEdit; +- +- CPVT_WordPlace m_wpOld; +- CPVT_WordPlace m_wpNew; +-}; +- +-class CFXEU_ReplaceSelection final : public IFX_Edit_UndoItem { +- public: +- CFXEU_ReplaceSelection(CPWL_EditImpl* pEdit, bool bIsEnd); +- ~CFXEU_ReplaceSelection() override; +- +- // IFX_Edit_UndoItem: +- int Redo() override; +- int Undo() override; +- +- private: +- bool IsEnd() const { return m_bEnd; } +- +- UnownedPtr m_pEdit; +- const bool m_bEnd; // indicate whether this is the end of replace action +-}; +- +-class CFXEU_Backspace final : public IFX_Edit_UndoItem { +- public: +- CFXEU_Backspace(CPWL_EditImpl* pEdit, +- const CPVT_WordPlace& wpOldPlace, +- const CPVT_WordPlace& wpNewPlace, +- uint16_t word, +- int32_t charset); +- ~CFXEU_Backspace() override; +- +- // IFX_Edit_UndoItem: +- int Redo() override; +- int Undo() override; +- +- private: +- UnownedPtr m_pEdit; +- +- CPVT_WordPlace m_wpOld; +- CPVT_WordPlace m_wpNew; +- uint16_t m_Word; +- int32_t m_nCharset; +-}; +- +-class CFXEU_Delete final : public IFX_Edit_UndoItem { +- public: +- CFXEU_Delete(CPWL_EditImpl* pEdit, +- const CPVT_WordPlace& wpOldPlace, +- const CPVT_WordPlace& wpNewPlace, +- uint16_t word, +- int32_t charset, +- bool bSecEnd); +- ~CFXEU_Delete() override; +- +- // IFX_Edit_UndoItem: +- int Redo() override; +- int Undo() override; +- +- private: +- UnownedPtr m_pEdit; +- +- CPVT_WordPlace m_wpOld; +- CPVT_WordPlace m_wpNew; +- uint16_t m_Word; +- int32_t m_nCharset; +- bool m_bSecEnd; +-}; +- +-class CFXEU_Clear final : public IFX_Edit_UndoItem { +- public: +- CFXEU_Clear(CPWL_EditImpl* pEdit, +- const CPVT_WordRange& wrSel, +- const WideString& swText); +- ~CFXEU_Clear() override; +- +- // IFX_Edit_UndoItem: +- int Redo() override; +- int Undo() override; +- +- private: +- UnownedPtr m_pEdit; +- +- CPVT_WordRange m_wrSel; +- WideString m_swText; +-}; +- +-class CFXEU_InsertText final : public IFX_Edit_UndoItem { +- public: +- CFXEU_InsertText(CPWL_EditImpl* pEdit, +- const CPVT_WordPlace& wpOldPlace, +- const CPVT_WordPlace& wpNewPlace, +- const WideString& swText, +- int32_t charset); +- ~CFXEU_InsertText() override; +- +- // IFX_Edit_UndoItem: +- int Redo() override; +- int Undo() override; +- +- private: +- UnownedPtr m_pEdit; +- +- CPVT_WordPlace m_wpOld; +- CPVT_WordPlace m_wpNew; +- WideString m_swText; +- int32_t m_nCharset; +-}; + + class CPWL_EditImpl { + public: +- static void DrawEdit(CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device, +- CPWL_EditImpl* pEdit, +- FX_COLORREF crTextFill, +- const CFX_FloatRect& rcClip, +- const CFX_PointF& ptOffset, +- const CPVT_WordRange* pRange, +- IPWL_SystemHandler* pSystemHandler, +- CFFL_FormFiller* pFFLData); ++ class Iterator { ++ public: ++ Iterator(CPWL_EditImpl* pEdit, CPVT_VariableText::Iterator* pVTIterator); ++ ~Iterator(); ++ ++ bool NextWord(); ++ bool GetWord(CPVT_Word& word) const; ++ bool GetLine(CPVT_Line& line) const; ++ void SetAt(int32_t nWordIndex); ++ void SetAt(const CPVT_WordPlace& place); ++ const CPVT_WordPlace& GetAt() const; ++ ++ private: ++ UnownedPtr m_pEdit; ++ UnownedPtr m_pVTIterator; ++ }; + + CPWL_EditImpl(); + ~CPWL_EditImpl(); + ++ void DrawEdit(CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device, ++ FX_COLORREF crTextFill, ++ const CFX_FloatRect& rcClip, ++ const CFX_PointF& ptOffset, ++ const CPVT_WordRange* pRange, ++ IPWL_FillerNotify* pFillerNotify, ++ IPWL_FillerNotify::PerWindowData* pSystemData); ++ + void SetFontMap(IPVT_FontMap* pFontMap); +- void SetNotify(CPWL_EditCtrl* pNotify); +- void SetOperationNotify(CPWL_Edit* pOperationNotify); ++ void SetNotify(CPWL_Edit* pNotify); + + // Returns an iterator for the contents. Should not be released. +- CPWL_EditImpl_Iterator* GetIterator(); ++ Iterator* GetIterator(); + IPVT_FontMap* GetFontMap(); + void Initialize(); + +@@ -281,38 +67,39 @@ class CPWL_EditImpl { + void SetScrollPos(const CFX_PointF& point); + + // Set the horizontal text alignment. (nFormat [0:left, 1:middle, 2:right]) +- void SetAlignmentH(int32_t nFormat, bool bPaint); ++ void SetAlignmentH(int32_t nFormat); ++ + // Set the vertical text alignment. (nFormat [0:left, 1:middle, 2:right]) +- void SetAlignmentV(int32_t nFormat, bool bPaint); ++ void SetAlignmentV(int32_t nFormat); + + // Set the substitution character for hidden text. +- void SetPasswordChar(uint16_t wSubWord, bool bPaint); ++ void SetPasswordChar(uint16_t wSubWord); + + // Set the maximum number of words in the text. + void SetLimitChar(int32_t nLimitChar); + void SetCharArray(int32_t nCharArray); +- void SetCharSpace(float fCharSpace); +- void SetMultiLine(bool bMultiLine, bool bPaint); +- void SetAutoReturn(bool bAuto, bool bPaint); +- void SetAutoFontSize(bool bAuto, bool bPaint); +- void SetAutoScroll(bool bAuto, bool bPaint); ++ void SetMultiLine(bool bMultiLine); ++ void SetAutoReturn(bool bAuto); ++ void SetAutoFontSize(bool bAuto); ++ void SetAutoScroll(bool bAuto); + void SetFontSize(float fFontSize); +- void SetTextOverflow(bool bAllowed, bool bPaint); ++ void SetTextOverflow(bool bAllowed); + void OnMouseDown(const CFX_PointF& point, bool bShift, bool bCtrl); + void OnMouseMove(const CFX_PointF& point, bool bShift, bool bCtrl); +- void OnVK_UP(bool bShift, bool bCtrl); +- void OnVK_DOWN(bool bShift, bool bCtrl); +- void OnVK_LEFT(bool bShift, bool bCtrl); +- void OnVK_RIGHT(bool bShift, bool bCtrl); ++ void OnVK_UP(bool bShift); ++ void OnVK_DOWN(bool bShift); ++ void OnVK_LEFT(bool bShift); ++ void OnVK_RIGHT(bool bShift); + void OnVK_HOME(bool bShift, bool bCtrl); + void OnVK_END(bool bShift, bool bCtrl); + void SetText(const WideString& sText); +- bool InsertWord(uint16_t word, int32_t charset); ++ bool InsertWord(uint16_t word, FX_Charset charset); + bool InsertReturn(); + bool Backspace(); + bool Delete(); + bool ClearSelection(); +- bool InsertText(const WideString& sText, int32_t charset); ++ bool InsertText(const WideString& sText, FX_Charset charset); ++ void ReplaceAndKeepSelection(const WideString& text); + void ReplaceSelection(const WideString& text); + bool Redo(); + bool Undo(); +@@ -328,9 +115,8 @@ class CPWL_EditImpl { + int32_t GetCharArray() const; + CFX_FloatRect GetContentRect() const; + WideString GetRangeText(const CPVT_WordRange& range) const; +- float GetCharSpace() const; + void SetSelection(int32_t nStartChar, int32_t nEndChar); +- void GetSelection(int32_t& nStartChar, int32_t& nEndChar) const; ++ std::pair GetSelection() const; + void SelectAll(); + void SelectNone(); + bool IsSelected() const; +@@ -341,40 +127,121 @@ class CPWL_EditImpl { + CPVT_WordRange GetSelectWordRange() const; + void EnableUndo(bool bUndo); + bool IsTextFull() const; +- bool IsTextOverflow() const; + bool CanUndo() const; + bool CanRedo() const; + CPVT_WordRange GetVisibleWordRange() const; + +- bool Clear(); +- +- CPVT_WordPlace DoInsertText(const CPVT_WordPlace& place, +- const WideString& sText, +- int32_t charset); +- int32_t GetCharSetFromUnicode(uint16_t word, int32_t nOldCharset); +- +- int32_t GetTotalLines() const; +- + ByteString GetPDFWordString(int32_t nFontIndex, + uint16_t Word, + uint16_t SubWord); + +- void SetSelection(const CPVT_WordPlace& begin, const CPVT_WordPlace& end); ++ private: ++ class RefreshState { ++ public: ++ RefreshState(); ++ ~RefreshState(); ++ ++ void BeginRefresh(); ++ void Push(const CPVT_WordRange& linerange, const CFX_FloatRect& rect); ++ void NoAnalyse(); ++ std::vector* GetRefreshRects(); ++ void EndRefresh(); ++ ++ private: ++ struct LineRect { ++ LineRect(const CPVT_WordRange& wrLine, const CFX_FloatRect& rcLine) ++ : m_wrLine(wrLine), m_rcLine(rcLine) {} ++ ++ CPVT_WordRange m_wrLine; ++ CFX_FloatRect m_rcLine; ++ }; ++ ++ void Add(const CFX_FloatRect& new_rect); ++ ++ std::vector m_NewLineRects; ++ std::vector m_OldLineRects; ++ std::vector m_RefreshRects; ++ }; ++ ++ class SelectState { ++ public: ++ SelectState(); ++ explicit SelectState(const CPVT_WordRange& range); ++ ++ void Reset(); ++ void Set(const CPVT_WordPlace& begin, const CPVT_WordPlace& end); ++ void SetEndPos(const CPVT_WordPlace& end); ++ ++ CPVT_WordRange ConvertToWordRange() const; ++ bool IsEmpty() const; ++ ++ CPVT_WordPlace BeginPos; ++ CPVT_WordPlace EndPos; ++ }; ++ ++ class UndoItemIface { ++ public: ++ virtual ~UndoItemIface() = default; ++ ++ // Undo/Redo the current undo item and returns the number of additional ++ // items to be processed in |m_UndoItemStack| to fully undo/redo the action. ++ // (An example is UndoReplaceSelection::Undo(), if UndoReplaceSelection ++ // marks the end of a replace action, UndoReplaceSelection::Undo() returns 3 ++ // because 3 more undo items need to be processed to revert the replace ++ // action: insert text, clear selection and the UndoReplaceSelection which ++ // marks the beginning of replace action.) Implementations should return 0 ++ // by default. ++ virtual int Undo() = 0; ++ virtual int Redo() = 0; ++ }; ++ ++ class UndoStack { ++ public: ++ UndoStack(); ++ ~UndoStack(); ++ ++ void AddItem(std::unique_ptr pItem); ++ void Undo(); ++ void Redo(); ++ bool CanUndo() const; ++ bool CanRedo() const; ++ ++ private: ++ void RemoveHeads(); ++ void RemoveTails(); ++ ++ std::deque> m_UndoItemStack; ++ size_t m_nCurUndoPos = 0; ++ bool m_bWorking = false; ++ }; ++ ++ class Provider; ++ class UndoBackspace; ++ class UndoClear; ++ class UndoDelete; ++ class UndoInsertReturn; ++ class UndoInsertText; ++ class UndoInsertWord; ++ class UndoReplaceSelection; + +- bool Delete(bool bAddUndo, bool bPaint); +- bool Clear(bool bAddUndo, bool bPaint); +- bool InsertText(const WideString& sText, +- int32_t charset, +- bool bAddUndo, +- bool bPaint); +- bool InsertWord(uint16_t word, int32_t charset, bool bAddUndo, bool bPaint); +- bool InsertReturn(bool bAddUndo, bool bPaint); +- bool Backspace(bool bAddUndo, bool bPaint); ++ bool IsTextOverflow() const; ++ bool Clear(); ++ CPVT_WordPlace DoInsertText(const CPVT_WordPlace& place, ++ const WideString& sText, ++ FX_Charset charset); ++ FX_Charset GetCharSetFromUnicode(uint16_t word, FX_Charset nOldCharset); ++ int32_t GetTotalLines() const; ++ void SetSelection(const CPVT_WordPlace& begin, const CPVT_WordPlace& end); ++ bool Delete(bool bAddUndo); ++ bool Clear(bool bAddUndo); ++ bool InsertText(const WideString& sText, FX_Charset charset, bool bAddUndo); ++ bool InsertWord(uint16_t word, FX_Charset charset, bool bAddUndo); ++ bool InsertReturn(bool bAddUndo); ++ bool Backspace(bool bAddUndo); + void SetCaret(const CPVT_WordPlace& place); + + CFX_PointF VTToEdit(const CFX_PointF& point) const; + +- private: + void RearrangeAll(); + void RearrangePart(const CPVT_WordRange& range); + void ScrollToCaret(); +@@ -396,68 +263,27 @@ class CPWL_EditImpl { + void SetCaretInfo(); + void SetCaretOrigin(); + +- void AddEditUndoItem(std::unique_ptr pEditUndoItem); +- +- std::unique_ptr m_pVTProvider; +- std::unique_ptr m_pVT; // Must outlive |m_pVTProvider|. +- UnownedPtr m_pNotify; +- UnownedPtr m_pOperationNotify; ++ void AddEditUndoItem(std::unique_ptr pEditUndoItem); ++ ++ bool m_bEnableScroll = false; ++ bool m_bNotifyFlag = false; ++ bool m_bEnableOverflow = false; ++ bool m_bEnableRefresh = true; ++ bool m_bEnableUndo = true; ++ int32_t m_nAlignment = 0; ++ std::unique_ptr m_pVTProvider; ++ std::unique_ptr m_pVT; // Must outlive |m_pVTProvider|. ++ UnownedPtr m_pNotify; + CPVT_WordPlace m_wpCaret; + CPVT_WordPlace m_wpOldCaret; +- CPWL_EditImpl_Select m_SelState; ++ SelectState m_SelState; + CFX_PointF m_ptScrollPos; + CFX_PointF m_ptRefreshScrollPos; +- bool m_bEnableScroll; +- std::unique_ptr m_pIterator; +- CPWL_EditImpl_Refresh m_Refresh; ++ std::unique_ptr m_pIterator; ++ RefreshState m_Refresh; + CFX_PointF m_ptCaret; +- CPWL_EditImpl_Undo m_Undo; +- int32_t m_nAlignment; +- bool m_bNotifyFlag; +- bool m_bEnableOverflow; +- bool m_bEnableRefresh; ++ UndoStack m_Undo; + CFX_FloatRect m_rcOldContent; +- bool m_bEnableUndo; +-}; +- +-class CPWL_EditImpl_Iterator { +- public: +- CPWL_EditImpl_Iterator(CPWL_EditImpl* pEdit, +- CPDF_VariableText::Iterator* pVTIterator); +- ~CPWL_EditImpl_Iterator(); +- +- bool NextWord(); +- bool PrevWord(); +- bool GetWord(CPVT_Word& word) const; +- bool GetLine(CPVT_Line& line) const; +- void SetAt(int32_t nWordIndex); +- void SetAt(const CPVT_WordPlace& place); +- const CPVT_WordPlace& GetAt() const; +- +- private: +- UnownedPtr m_pEdit; +- CPDF_VariableText::Iterator* m_pVTIterator; +-}; +- +-class CPWL_EditImpl_Provider final : public CPDF_VariableText::Provider { +- public: +- explicit CPWL_EditImpl_Provider(IPVT_FontMap* pFontMap); +- ~CPWL_EditImpl_Provider() override; +- +- IPVT_FontMap* GetFontMap() const; +- +- // CPDF_VariableText::Provider: +- uint32_t GetCharWidth(int32_t nFontIndex, uint16_t word) override; +- int32_t GetTypeAscent(int32_t nFontIndex) override; +- int32_t GetTypeDescent(int32_t nFontIndex) override; +- int32_t GetWordFontIndex(uint16_t word, +- int32_t charset, +- int32_t nFontIndex) override; +- int32_t GetDefaultFontIndex() override; +- bool IsLatinWord(uint16_t word) override; +- +- private: +- IPVT_FontMap* m_pFontMap; + }; + + #endif // FPDFSDK_PWL_CPWL_EDIT_IMPL_H_ +diff --git a/fpdfsdk/pwl/cpwl_icon.cpp b/fpdfsdk/pwl/cpwl_icon.cpp +deleted file mode 100644 +index 67df09e5a..000000000 +--- a/fpdfsdk/pwl/cpwl_icon.cpp ++++ /dev/null +@@ -1,110 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "fpdfsdk/pwl/cpwl_icon.h" +- +-#include +-#include +-#include +- +-#include "core/fpdfdoc/cpdf_icon.h" +-#include "core/fpdfdoc/cpdf_iconfit.h" +-#include "fpdfsdk/pwl/cpwl_wnd.h" +- +-CPWL_Icon::CPWL_Icon(const CreateParams& cp, +- std::unique_ptr pIcon, +- CPDF_IconFit* pFit) +- : CPWL_Wnd(cp, nullptr), m_pIcon(std::move(pIcon)), m_pIconFit(pFit) { +- ASSERT(m_pIcon); +-} +- +-CPWL_Icon::~CPWL_Icon() = default; +- +-CFX_SizeF CPWL_Icon::GetImageSize() { +- return m_pIcon->GetImageSize(); +-} +- +-CFX_Matrix CPWL_Icon::GetImageMatrix() { +- return m_pIcon->GetImageMatrix(); +-} +- +-ByteString CPWL_Icon::GetImageAlias() { +- return m_pIcon->GetImageAlias(); +-} +- +-CFX_PointF CPWL_Icon::GetIconPosition() { +- if (!m_pIconFit) +- return CFX_PointF(); +- +- return m_pIconFit->GetIconPosition(); +-} +- +-std::pair CPWL_Icon::GetScale() { +- float fHScale = 1.0f; +- float fVScale = 1.0f; +- +- CFX_FloatRect rcPlate = GetClientRect(); +- float fPlateWidth = rcPlate.Width(); +- float fPlateHeight = rcPlate.Height(); +- +- CFX_SizeF image_size = GetImageSize(); +- float fImageWidth = image_size.width; +- float fImageHeight = image_size.height; +- int32_t nScaleMethod = m_pIconFit ? m_pIconFit->GetScaleMethod() : 0; +- +- switch (nScaleMethod) { +- default: +- case 0: +- fHScale = fPlateWidth / std::max(fImageWidth, 1.0f); +- fVScale = fPlateHeight / std::max(fImageHeight, 1.0f); +- break; +- case 1: +- if (fPlateWidth < fImageWidth) +- fHScale = fPlateWidth / std::max(fImageWidth, 1.0f); +- if (fPlateHeight < fImageHeight) +- fVScale = fPlateHeight / std::max(fImageHeight, 1.0f); +- break; +- case 2: +- if (fPlateWidth > fImageWidth) +- fHScale = fPlateWidth / std::max(fImageWidth, 1.0f); +- if (fPlateHeight > fImageHeight) +- fVScale = fPlateHeight / std::max(fImageHeight, 1.0f); +- break; +- case 3: +- break; +- } +- +- float fMinScale; +- if (m_pIconFit && m_pIconFit->IsProportionalScale()) { +- fMinScale = std::min(fHScale, fVScale); +- fHScale = fMinScale; +- fVScale = fMinScale; +- } +- return {fHScale, fVScale}; +-} +- +-std::pair CPWL_Icon::GetImageOffset() { +- CFX_PointF icon_position = GetIconPosition(); +- float fLeft = icon_position.x; +- float fBottom = icon_position.y; +- +- CFX_SizeF image_size = GetImageSize(); +- float fImageWidth = image_size.width; +- float fImageHeight = image_size.height; +- +- float fHScale, fVScale; +- std::tie(fHScale, fVScale) = GetScale(); +- +- float fImageFactWidth = fImageWidth * fHScale; +- float fImageFactHeight = fImageHeight * fVScale; +- +- CFX_FloatRect rcPlate = GetClientRect(); +- float fPlateWidth = rcPlate.Width(); +- float fPlateHeight = rcPlate.Height(); +- +- return {(fPlateWidth - fImageFactWidth) * fLeft, +- (fPlateHeight - fImageFactHeight) * fBottom}; +-} +diff --git a/fpdfsdk/pwl/cpwl_icon.h b/fpdfsdk/pwl/cpwl_icon.h +deleted file mode 100644 +index aa9b3d77e..000000000 +--- a/fpdfsdk/pwl/cpwl_icon.h ++++ /dev/null +@@ -1,44 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef FPDFSDK_PWL_CPWL_ICON_H_ +-#define FPDFSDK_PWL_CPWL_ICON_H_ +- +-#include +-#include +- +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/unowned_ptr.h" +-#include "fpdfsdk/pwl/cpwl_wnd.h" +- +-class CPDF_Icon; +-class CPDF_IconFit; +- +-class CPWL_Icon final : public CPWL_Wnd { +- public: +- CPWL_Icon(const CreateParams& cp, +- std::unique_ptr pIcon, +- CPDF_IconFit* pFit); +- ~CPWL_Icon() override; +- +- // horizontal scale, vertical scale +- std::pair GetScale(); +- +- // x, y +- std::pair GetImageOffset(); +- +- CFX_Matrix GetImageMatrix(); +- ByteString GetImageAlias(); +- +- private: +- CFX_PointF GetIconPosition(); // left, bottom coordinates. +- CFX_SizeF GetImageSize(); +- +- std::unique_ptr const m_pIcon; +- UnownedPtr const m_pIconFit; +-}; +- +-#endif // FPDFSDK_PWL_CPWL_ICON_H_ +diff --git a/fpdfsdk/pwl/cpwl_list_box.cpp b/fpdfsdk/pwl/cpwl_list_box.cpp +index a0e203901..083e24074 100644 +--- a/fpdfsdk/pwl/cpwl_list_box.cpp ++++ b/fpdfsdk/pwl/cpwl_list_box.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,76 +11,27 @@ + + #include "core/fxge/cfx_renderdevice.h" + #include "fpdfsdk/pwl/cpwl_edit.h" +-#include "fpdfsdk/pwl/cpwl_edit_ctrl.h" + #include "fpdfsdk/pwl/cpwl_edit_impl.h" +-#include "fpdfsdk/pwl/cpwl_list_impl.h" + #include "fpdfsdk/pwl/cpwl_scroll_bar.h" +-#include "fpdfsdk/pwl/cpwl_wnd.h" ++#include "fpdfsdk/pwl/ipwl_fillernotify.h" + #include "public/fpdf_fwlevent.h" +-#include "third_party/base/ptr_util.h" +- +-CPWL_List_Notify::CPWL_List_Notify(CPWL_ListBox* pList) : m_pList(pList) { +- ASSERT(m_pList); +-} +- +-CPWL_List_Notify::~CPWL_List_Notify() {} +- +-void CPWL_List_Notify::IOnSetScrollInfoY(float fPlateMin, +- float fPlateMax, +- float fContentMin, +- float fContentMax, +- float fSmallStep, +- float fBigStep) { +- PWL_SCROLL_INFO Info; +- Info.fPlateWidth = fPlateMax - fPlateMin; +- Info.fContentMin = fContentMin; +- Info.fContentMax = fContentMax; +- Info.fSmallStep = fSmallStep; +- Info.fBigStep = fBigStep; +- m_pList->SetScrollInfo(Info); +- +- CPWL_ScrollBar* pScroll = m_pList->GetVScrollBar(); +- if (!pScroll) +- return; +- +- if (IsFloatBigger(Info.fPlateWidth, Info.fContentMax - Info.fContentMin) || +- IsFloatEqual(Info.fPlateWidth, Info.fContentMax - Info.fContentMin)) { +- if (pScroll->IsVisible()) { +- pScroll->SetVisible(false); +- m_pList->RePosChildWnd(); +- } +- } else { +- if (!pScroll->IsVisible()) { +- pScroll->SetVisible(true); +- m_pList->RePosChildWnd(); +- } +- } +-} +- +-void CPWL_List_Notify::IOnSetScrollPosY(float fy) { +- m_pList->SetScrollPosition(fy); +-} +- +-void CPWL_List_Notify::IOnInvalidateRect(CFX_FloatRect* pRect) { +- m_pList->InvalidateRect(pRect); +-} ++#include "third_party/base/numerics/safe_conversions.h" + + CPWL_ListBox::CPWL_ListBox( + const CreateParams& cp, +- std::unique_ptr pAttachedData) ++ std::unique_ptr pAttachedData) + : CPWL_Wnd(cp, std::move(pAttachedData)), +- m_pList(pdfium::MakeUnique()) {} ++ m_pListCtrl(std::make_unique()) {} + + CPWL_ListBox::~CPWL_ListBox() = default; + + void CPWL_ListBox::OnCreated() { +- m_pList->SetFontMap(GetFontMap()); +- m_pListNotify = pdfium::MakeUnique(this); +- m_pList->SetNotify(m_pListNotify.get()); ++ m_pListCtrl->SetFontMap(GetFontMap()); ++ m_pListCtrl->SetNotify(this); + + SetHoverSel(HasFlag(PLBS_HOVERSEL)); +- m_pList->SetMultipleSel(HasFlag(PLBS_MULTIPLESEL)); +- m_pList->SetFontSize(GetCreationParams()->fFontSize); ++ m_pListCtrl->SetMultipleSel(HasFlag(PLBS_MULTIPLESEL)); ++ m_pListCtrl->SetFontSize(GetCreationParams()->fFontSize); + + m_bHoverSel = HasFlag(PLBS_HOVERSEL); + } +@@ -88,57 +39,55 @@ void CPWL_ListBox::OnCreated() { + void CPWL_ListBox::OnDestroy() { + // Make sure the notifier is removed from the list as we are about to + // destroy the notifier and don't want to leave a dangling pointer. +- m_pList->SetNotify(nullptr); +- m_pListNotify.reset(); ++ m_pListCtrl->SetNotify(nullptr); + } + + void CPWL_ListBox::DrawThisAppearance(CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device) { + CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device); + +- CFX_FloatRect rcPlate = m_pList->GetPlateRect(); ++ CFX_FloatRect rcPlate = m_pListCtrl->GetPlateRect(); + CFX_FloatRect rcList = GetListRect(); + CFX_FloatRect rcClient = GetClientRect(); + +- for (int32_t i = 0, sz = m_pList->GetCount(); i < sz; i++) { +- CFX_FloatRect rcItem = m_pList->GetItemRect(i); ++ for (int32_t i = 0, sz = m_pListCtrl->GetCount(); i < sz; i++) { ++ CFX_FloatRect rcItem = m_pListCtrl->GetItemRect(i); + if (rcItem.bottom > rcPlate.top || rcItem.top < rcPlate.bottom) + continue; + + CFX_PointF ptOffset(rcItem.left, (rcItem.top + rcItem.bottom) * 0.5f); +- if (CPWL_EditImpl* pEdit = m_pList->GetItemEdit(i)) { ++ if (CPWL_EditImpl* pEdit = m_pListCtrl->GetItemEdit(i)) { + CFX_FloatRect rcContent = pEdit->GetContentRect(); + rcItem.Intersect(rcContent.Width() > rcClient.Width() ? rcList + : rcClient); + } + +- IPWL_SystemHandler* pSysHandler = GetSystemHandler(); +- if (m_pList->IsItemSelected(i)) { ++ IPWL_FillerNotify* pSysHandler = GetFillerNotify(); ++ if (m_pListCtrl->IsItemSelected(i)) { + if (pSysHandler->IsSelectionImplemented()) { +- CPWL_EditImpl::DrawEdit(pDevice, mtUser2Device, m_pList->GetItemEdit(i), +- GetTextColor().ToFXColor(255), rcList, ptOffset, +- nullptr, pSysHandler, m_pFormFiller.Get()); +- pSysHandler->OutputSelectedRect(m_pFormFiller.Get(), rcItem); ++ m_pListCtrl->GetItemEdit(i)->DrawEdit( ++ pDevice, mtUser2Device, GetTextColor().ToFXColor(255), rcList, ++ ptOffset, nullptr, pSysHandler, GetAttachedData()); ++ pSysHandler->OutputSelectedRect(GetAttachedData(), rcItem); + } else { + pDevice->DrawFillRect(&mtUser2Device, rcItem, + ArgbEncode(255, 0, 51, 113)); +- CPWL_EditImpl::DrawEdit(pDevice, mtUser2Device, m_pList->GetItemEdit(i), +- ArgbEncode(255, 255, 255, 255), rcList, +- ptOffset, nullptr, pSysHandler, +- m_pFormFiller.Get()); ++ m_pListCtrl->GetItemEdit(i)->DrawEdit( ++ pDevice, mtUser2Device, ArgbEncode(255, 255, 255, 255), rcList, ++ ptOffset, nullptr, pSysHandler, GetAttachedData()); + } + } else { +- CPWL_EditImpl::DrawEdit(pDevice, mtUser2Device, m_pList->GetItemEdit(i), +- GetTextColor().ToFXColor(255), rcList, ptOffset, +- nullptr, pSysHandler, nullptr); ++ m_pListCtrl->GetItemEdit(i)->DrawEdit( ++ pDevice, mtUser2Device, GetTextColor().ToFXColor(255), rcList, ++ ptOffset, nullptr, pSysHandler, nullptr); + } + } + } + +-bool CPWL_ListBox::OnKeyDown(uint16_t nChar, uint32_t nFlag) { +- CPWL_Wnd::OnKeyDown(nChar, nFlag); ++bool CPWL_ListBox::OnKeyDown(FWL_VKEYCODE nKeyCode, Mask nFlag) { ++ CPWL_Wnd::OnKeyDown(nKeyCode, nFlag); + +- switch (nChar) { ++ switch (nKeyCode) { + default: + return false; + case FWL_VKEY_Up: +@@ -150,58 +99,61 @@ bool CPWL_ListBox::OnKeyDown(uint16_t nChar, uint32_t nFlag) { + break; + } + +- switch (nChar) { ++ switch (nKeyCode) { + case FWL_VKEY_Up: +- m_pList->OnVK_UP(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); ++ m_pListCtrl->OnVK_UP(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)); + break; + case FWL_VKEY_Down: +- m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); ++ m_pListCtrl->OnVK_DOWN(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)); + break; + case FWL_VKEY_Home: +- m_pList->OnVK_HOME(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); ++ m_pListCtrl->OnVK_HOME(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)); + break; + case FWL_VKEY_Left: +- m_pList->OnVK_LEFT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); ++ m_pListCtrl->OnVK_LEFT(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)); + break; + case FWL_VKEY_End: +- m_pList->OnVK_END(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); ++ m_pListCtrl->OnVK_END(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)); + break; + case FWL_VKEY_Right: +- m_pList->OnVK_RIGHT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); ++ m_pListCtrl->OnVK_RIGHT(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)); + break; +- case FWL_VKEY_Delete: ++ default: + break; + } + OnNotifySelectionChanged(true, nFlag); + return true; + } + +-bool CPWL_ListBox::OnChar(uint16_t nChar, uint32_t nFlag) { ++bool CPWL_ListBox::OnChar(uint16_t nChar, Mask nFlag) { + CPWL_Wnd::OnChar(nChar, nFlag); + +- if (!m_pList->OnChar(nChar, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag))) ++ if (!m_pListCtrl->OnChar(nChar, IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag))) + return false; + + OnNotifySelectionChanged(true, nFlag); + return true; + } + +-bool CPWL_ListBox::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnLButtonDown(point, nFlag); ++bool CPWL_ListBox::OnLButtonDown(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnLButtonDown(nFlag, point); + + if (ClientHitTest(point)) { + m_bMouseDown = true; + SetFocus(); + SetCapture(); + +- m_pList->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); ++ m_pListCtrl->OnMouseDown(point, IsSHIFTKeyDown(nFlag), ++ IsCTRLKeyDown(nFlag)); + } + + return true; + } + +-bool CPWL_ListBox::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnLButtonUp(point, nFlag); ++bool CPWL_ListBox::OnLButtonUp(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnLButtonUp(nFlag, point); + + if (m_bMouseDown) { + ReleaseCapture(); +@@ -215,13 +167,15 @@ void CPWL_ListBox::SetHoverSel(bool bHoverSel) { + m_bHoverSel = bHoverSel; + } + +-bool CPWL_ListBox::OnMouseMove(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnMouseMove(point, nFlag); ++bool CPWL_ListBox::OnMouseMove(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnMouseMove(nFlag, point); + + if (m_bHoverSel && !IsCaptureMouse() && ClientHitTest(point)) +- m_pList->Select(m_pList->GetItemIndex(point)); ++ m_pListCtrl->Select(m_pListCtrl->GetItemIndex(point)); + if (m_bMouseDown) +- m_pList->OnMouseMove(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); ++ m_pListCtrl->OnMouseMove(point, IsSHIFTKeyDown(nFlag), ++ IsCTRLKeyDown(nFlag)); + + return true; + } +@@ -237,30 +191,28 @@ void CPWL_ListBox::SetScrollPosition(float pos) { + } + + void CPWL_ListBox::ScrollWindowVertically(float pos) { +- m_pList->SetScrollPos(CFX_PointF(0, pos)); ++ m_pListCtrl->SetScrollPos(CFX_PointF(0, pos)); + } + + bool CPWL_ListBox::RePosChildWnd() { + if (!CPWL_Wnd::RePosChildWnd()) + return false; + +- m_pList->SetPlateRect(GetListRect()); ++ m_pListCtrl->SetPlateRect(GetListRect()); + return true; + } + +-bool CPWL_ListBox::OnNotifySelectionChanged(bool bKeyDown, uint32_t nFlag) { +- if (!m_pFillerNotify) +- return false; +- ++bool CPWL_ListBox::OnNotifySelectionChanged(bool bKeyDown, ++ Mask nFlag) { + ObservedPtr thisObserved(this); + + WideString swChange = GetText(); + WideString strChangeEx; + int nSelStart = 0; +- int nSelEnd = swChange.GetLength(); ++ int nSelEnd = pdfium::base::checked_cast(swChange.GetLength()); + bool bRC; + bool bExit; +- std::tie(bRC, bExit) = m_pFillerNotify->OnBeforeKeyStroke( ++ std::tie(bRC, bExit) = GetFillerNotify()->OnBeforeKeyStroke( + GetAttachedData(), swChange, strChangeEx, nSelStart, nSelEnd, bKeyDown, + nFlag); + +@@ -271,8 +223,8 @@ bool CPWL_ListBox::OnNotifySelectionChanged(bool bKeyDown, uint32_t nFlag) { + } + + CFX_FloatRect CPWL_ListBox::GetFocusRect() const { +- if (m_pList->IsMultipleSel()) { +- CFX_FloatRect rcCaret = m_pList->GetItemRect(m_pList->GetCaret()); ++ if (m_pListCtrl->IsMultipleSel()) { ++ CFX_FloatRect rcCaret = m_pListCtrl->GetItemRect(m_pListCtrl->GetCaret()); + rcCaret.Intersect(GetClientRect()); + return rcCaret; + } +@@ -281,84 +233,114 @@ CFX_FloatRect CPWL_ListBox::GetFocusRect() const { + } + + void CPWL_ListBox::AddString(const WideString& str) { +- m_pList->AddString(str); ++ m_pListCtrl->AddString(str); + } + + WideString CPWL_ListBox::GetText() { +- return m_pList->GetText(); ++ return m_pListCtrl->GetText(); + } + + void CPWL_ListBox::SetFontSize(float fFontSize) { +- m_pList->SetFontSize(fFontSize); ++ m_pListCtrl->SetFontSize(fFontSize); + } + + float CPWL_ListBox::GetFontSize() const { +- return m_pList->GetFontSize(); ++ return m_pListCtrl->GetFontSize(); ++} ++ ++void CPWL_ListBox::OnSetScrollInfoY(float fPlateMin, ++ float fPlateMax, ++ float fContentMin, ++ float fContentMax, ++ float fSmallStep, ++ float fBigStep) { ++ PWL_SCROLL_INFO Info; ++ Info.fPlateWidth = fPlateMax - fPlateMin; ++ Info.fContentMin = fContentMin; ++ Info.fContentMax = fContentMax; ++ Info.fSmallStep = fSmallStep; ++ Info.fBigStep = fBigStep; ++ SetScrollInfo(Info); ++ ++ CPWL_ScrollBar* pScroll = GetVScrollBar(); ++ if (!pScroll) ++ return; ++ ++ if (FXSYS_IsFloatBigger(Info.fPlateWidth, ++ Info.fContentMax - Info.fContentMin) || ++ FXSYS_IsFloatEqual(Info.fPlateWidth, ++ Info.fContentMax - Info.fContentMin)) { ++ if (pScroll->IsVisible()) { ++ pScroll->SetVisible(false); ++ RePosChildWnd(); ++ } ++ } else { ++ if (!pScroll->IsVisible()) { ++ pScroll->SetVisible(true); ++ RePosChildWnd(); ++ } ++ } ++} ++ ++void CPWL_ListBox::OnSetScrollPosY(float fy) { ++ SetScrollPosition(fy); ++} ++ ++void CPWL_ListBox::OnInvalidateRect(const CFX_FloatRect& rect) { ++ InvalidateRect(&rect); + } + + void CPWL_ListBox::Select(int32_t nItemIndex) { +- m_pList->Select(nItemIndex); ++ m_pListCtrl->Select(nItemIndex); + } + + void CPWL_ListBox::Deselect(int32_t nItemIndex) { +- m_pList->Deselect(nItemIndex); ++ m_pListCtrl->Deselect(nItemIndex); + } + + void CPWL_ListBox::SetCaret(int32_t nItemIndex) { +- m_pList->SetCaret(nItemIndex); ++ m_pListCtrl->SetCaret(nItemIndex); + } + + void CPWL_ListBox::SetTopVisibleIndex(int32_t nItemIndex) { +- m_pList->SetTopItem(nItemIndex); ++ m_pListCtrl->SetTopItem(nItemIndex); + } + + void CPWL_ListBox::ScrollToListItem(int32_t nItemIndex) { +- m_pList->ScrollToListItem(nItemIndex); +-} +- +-void CPWL_ListBox::ResetContent() { +- m_pList->Clear(); +-} +- +-void CPWL_ListBox::Reset() { +- m_pList->Cancel(); ++ m_pListCtrl->ScrollToListItem(nItemIndex); + } + + bool CPWL_ListBox::IsMultipleSel() const { +- return m_pList->IsMultipleSel(); ++ return m_pListCtrl->IsMultipleSel(); + } + + int32_t CPWL_ListBox::GetCaretIndex() const { +- return m_pList->GetCaret(); ++ return m_pListCtrl->GetCaret(); + } + + int32_t CPWL_ListBox::GetCurSel() const { +- return m_pList->GetSelect(); ++ return m_pListCtrl->GetSelect(); + } + + bool CPWL_ListBox::IsItemSelected(int32_t nItemIndex) const { +- return m_pList->IsItemSelected(nItemIndex); ++ return m_pListCtrl->IsItemSelected(nItemIndex); + } + + int32_t CPWL_ListBox::GetTopVisibleIndex() const { +- m_pList->ScrollToListItem(m_pList->GetFirstSelected()); +- return m_pList->GetTopItem(); ++ m_pListCtrl->ScrollToListItem(m_pListCtrl->GetFirstSelected()); ++ return m_pListCtrl->GetTopItem(); + } + + int32_t CPWL_ListBox::GetCount() const { +- return m_pList->GetCount(); +-} +- +-int32_t CPWL_ListBox::FindNext(int32_t nIndex, wchar_t nChar) const { +- return m_pList->FindNext(nIndex, nChar); ++ return m_pListCtrl->GetCount(); + } + + CFX_FloatRect CPWL_ListBox::GetContentRect() const { +- return m_pList->GetContentRect(); ++ return m_pListCtrl->GetContentRect(); + } + + float CPWL_ListBox::GetFirstHeight() const { +- return m_pList->GetFirstHeight(); ++ return m_pListCtrl->GetFirstHeight(); + } + + CFX_FloatRect CPWL_ListBox::GetListRect() const { +@@ -366,13 +348,13 @@ CFX_FloatRect CPWL_ListBox::GetListRect() const { + return GetWindowRect().GetDeflated(width, width); + } + +-bool CPWL_ListBox::OnMouseWheel(short zDelta, ++bool CPWL_ListBox::OnMouseWheel(Mask nFlag, + const CFX_PointF& point, +- uint32_t nFlag) { +- if (zDelta < 0) +- m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); ++ const CFX_Vector& delta) { ++ if (delta.y < 0) ++ m_pListCtrl->OnVK_DOWN(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)); + else +- m_pList->OnVK_UP(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); ++ m_pListCtrl->OnVK_UP(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)); + + OnNotifySelectionChanged(false, nFlag); + return true; +diff --git a/fpdfsdk/pwl/cpwl_list_box.h b/fpdfsdk/pwl/cpwl_list_box.h +index ba3a653bd..49d93637f 100644 +--- a/fpdfsdk/pwl/cpwl_list_box.h ++++ b/fpdfsdk/pwl/cpwl_list_box.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,52 +10,31 @@ + #include + + #include "core/fxcrt/unowned_ptr.h" ++#include "fpdfsdk/pwl/cpwl_list_ctrl.h" + #include "fpdfsdk/pwl/cpwl_wnd.h" + +-class CPWL_ListCtrl; +-class CPWL_List_Notify; +-class CPWL_ListBox; +-class IPWL_Filler_Notify; +-struct CPVT_WordPlace; ++class IPWL_FillerNotify; + +-class CPWL_List_Notify { ++class CPWL_ListBox : public CPWL_Wnd, public CPWL_ListCtrl::NotifyIface { + public: +- explicit CPWL_List_Notify(CPWL_ListBox* pList); +- ~CPWL_List_Notify(); +- +- void IOnSetScrollInfoY(float fPlateMin, +- float fPlateMax, +- float fContentMin, +- float fContentMax, +- float fSmallStep, +- float fBigStep); +- void IOnSetScrollPosY(float fy); +- void IOnInvalidateRect(CFX_FloatRect* pRect); +- +- private: +- UnownedPtr m_pList; +-}; +- +-class CPWL_ListBox : public CPWL_Wnd { +- public: +- CPWL_ListBox( +- const CreateParams& cp, +- std::unique_ptr pAttachedData); ++ CPWL_ListBox(const CreateParams& cp, ++ std::unique_ptr pAttachedData); + ~CPWL_ListBox() override; + +- // CPWL_Wnd ++ // CPWL_Wnd: + void OnCreated() override; + void OnDestroy() override; + void DrawThisAppearance(CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device) override; +- bool OnKeyDown(uint16_t nChar, uint32_t nFlag) override; +- bool OnChar(uint16_t nChar, uint32_t nFlag) override; +- bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override; +- bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; +- bool OnMouseMove(const CFX_PointF& point, uint32_t nFlag) override; +- bool OnMouseWheel(short zDelta, ++ bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask nFlag) override; ++ bool OnChar(uint16_t nChar, Mask nFlag) override; ++ bool OnLButtonDown(Mask nFlag, ++ const CFX_PointF& point) override; ++ bool OnLButtonUp(Mask nFlag, const CFX_PointF& point) override; ++ bool OnMouseMove(Mask nFlag, const CFX_PointF& point) override; ++ bool OnMouseWheel(Mask nFlag, + const CFX_PointF& point, +- uint32_t nFlag) override; ++ const CFX_Vector& delta) override; + WideString GetText() override; + void SetScrollInfo(const PWL_SCROLL_INFO& info) override; + void SetScrollPosition(float pos) override; +@@ -65,13 +44,22 @@ class CPWL_ListBox : public CPWL_Wnd { + void SetFontSize(float fFontSize) override; + float GetFontSize() const override; + +- bool OnNotifySelectionChanged(bool bKeyDown, uint32_t nFlag); ++ // CPWL_ListCtrl::NotifyIface: ++ void OnSetScrollInfoY(float fPlateMin, ++ float fPlateMax, ++ float fContentMin, ++ float fContentMax, ++ float fSmallStep, ++ float fBigStep) override; ++ void OnSetScrollPosY(float fy) override; ++ void OnInvalidateRect(const CFX_FloatRect& pRect) override; ++ ++ bool OnNotifySelectionChanged(bool bKeyDown, Mask nFlag); + + void AddString(const WideString& str); + void SetTopVisibleIndex(int32_t nItemIndex); + void ScrollToListItem(int32_t nItemIndex); +- void ResetContent(); +- void Reset(); ++ + void Select(int32_t nItemIndex); + void Deselect(int32_t nItemIndex); + void SetCaret(int32_t nItemIndex); +@@ -83,26 +71,14 @@ class CPWL_ListBox : public CPWL_Wnd { + int32_t GetCurSel() const; + bool IsItemSelected(int32_t nItemIndex) const; + int32_t GetTopVisibleIndex() const; +- int32_t FindNext(int32_t nIndex, wchar_t nChar) const; + CFX_FloatRect GetContentRect() const; + float GetFirstHeight() const; + CFX_FloatRect GetListRect() const; + +- void SetFillerNotify(IPWL_Filler_Notify* pNotify) { +- m_pFillerNotify = pNotify; +- } +- +- void AttachFFLData(CFFL_FormFiller* pData) { m_pFormFiller = pData; } +- + protected: + bool m_bMouseDown = false; + bool m_bHoverSel = false; +- std::unique_ptr m_pList; +- std::unique_ptr m_pListNotify; +- UnownedPtr m_pFillerNotify; +- +- private: +- UnownedPtr m_pFormFiller; ++ std::unique_ptr m_pListCtrl; + }; + + #endif // FPDFSDK_PWL_CPWL_LIST_BOX_H_ +diff --git a/fpdfsdk/pwl/cpwl_list_impl.cpp b/fpdfsdk/pwl/cpwl_list_ctrl.cpp +similarity index 81% +rename from fpdfsdk/pwl/cpwl_list_impl.cpp +rename to fpdfsdk/pwl/cpwl_list_ctrl.cpp +index a7ceeb9d2..046e772ed 100644 +--- a/fpdfsdk/pwl/cpwl_list_impl.cpp ++++ b/fpdfsdk/pwl/cpwl_list_ctrl.cpp +@@ -1,23 +1,25 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#include "fpdfsdk/pwl/cpwl_list_impl.h" ++#include "fpdfsdk/pwl/cpwl_list_ctrl.h" + + #include + #include + + #include "core/fpdfdoc/cpvt_word.h" + #include "core/fxcrt/fx_extension.h" ++#include "core/fxcrt/stl_util.h" + #include "fpdfsdk/pwl/cpwl_edit_impl.h" + #include "fpdfsdk/pwl/cpwl_list_box.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/numerics/safe_conversions.h" + +-CPWL_ListCtrl::Item::Item() : m_pEdit(pdfium::MakeUnique()) { +- m_pEdit->SetAlignmentV(1, true); ++CPWL_ListCtrl::NotifyIface::~NotifyIface() = default; ++ ++CPWL_ListCtrl::Item::Item() : m_pEdit(std::make_unique()) { ++ m_pEdit->SetAlignmentV(1); + m_pEdit->Initialize(); + } + +@@ -29,10 +31,12 @@ void CPWL_ListCtrl::Item::SetFontMap(IPVT_FontMap* pFontMap) { + + void CPWL_ListCtrl::Item::SetText(const WideString& text) { + m_pEdit->SetText(text); ++ m_pEdit->Paint(); + } + + void CPWL_ListCtrl::Item::SetFontSize(float fFontSize) { + m_pEdit->SetFontSize(fFontSize); ++ m_pEdit->Paint(); + } + + float CPWL_ListCtrl::Item::GetItemHeight() const { +@@ -41,7 +45,7 @@ float CPWL_ListCtrl::Item::GetItemHeight() const { + + uint16_t CPWL_ListCtrl::Item::GetFirstChar() const { + CPVT_Word word; +- CPWL_EditImpl_Iterator* pIterator = m_pEdit->GetIterator(); ++ CPWL_EditImpl::Iterator* pIterator = m_pEdit->GetIterator(); + pIterator->SetAt(1); + pIterator->GetWord(word); + return word.Word; +@@ -51,15 +55,15 @@ WideString CPWL_ListCtrl::Item::GetText() const { + return m_pEdit->GetText(); + } + +-CPLST_Select::CPLST_Select() {} ++CPWL_ListCtrl::SelectState::SelectState() = default; + +-CPLST_Select::~CPLST_Select() {} ++CPWL_ListCtrl::SelectState::~SelectState() = default; + +-void CPLST_Select::Add(int32_t nItemIndex) { ++void CPWL_ListCtrl::SelectState::Add(int32_t nItemIndex) { + m_Items[nItemIndex] = SELECTING; + } + +-void CPLST_Select::Add(int32_t nBeginIndex, int32_t nEndIndex) { ++void CPWL_ListCtrl::SelectState::Add(int32_t nBeginIndex, int32_t nEndIndex) { + if (nBeginIndex > nEndIndex) + std::swap(nBeginIndex, nEndIndex); + +@@ -67,13 +71,13 @@ void CPLST_Select::Add(int32_t nBeginIndex, int32_t nEndIndex) { + Add(i); + } + +-void CPLST_Select::Sub(int32_t nItemIndex) { ++void CPWL_ListCtrl::SelectState::Sub(int32_t nItemIndex) { + auto it = m_Items.find(nItemIndex); + if (it != m_Items.end()) + it->second = DESELECTING; + } + +-void CPLST_Select::Sub(int32_t nBeginIndex, int32_t nEndIndex) { ++void CPWL_ListCtrl::SelectState::Sub(int32_t nBeginIndex, int32_t nEndIndex) { + if (nBeginIndex > nEndIndex) + std::swap(nBeginIndex, nEndIndex); + +@@ -81,12 +85,12 @@ void CPLST_Select::Sub(int32_t nBeginIndex, int32_t nEndIndex) { + Sub(i); + } + +-void CPLST_Select::DeselectAll() { ++void CPWL_ListCtrl::SelectState::DeselectAll() { + for (auto& item : m_Items) + item.second = DESELECTING; + } + +-void CPLST_Select::Done() { ++void CPWL_ListCtrl::SelectState::Done() { + auto it = m_Items.begin(); + while (it != m_Items.end()) { + if (it->second == DESELECTING) +@@ -96,19 +100,11 @@ void CPLST_Select::Done() { + } + } + +-CPWL_ListCtrl::CPWL_ListCtrl() +- : m_pNotify(nullptr), +- m_bNotifyFlag(false), +- m_nSelItem(-1), +- m_nFootIndex(-1), +- m_bCtrlSel(false), +- m_nCaretIndex(-1), +- m_fFontSize(0.0f), +- m_pFontMap(nullptr), +- m_bMultiple(false) {} ++CPWL_ListCtrl::CPWL_ListCtrl() = default; + + CPWL_ListCtrl::~CPWL_ListCtrl() { +- Clear(); ++ m_ListItems.clear(); ++ InvalidateItem(-1); + } + + CFX_PointF CPWL_ListCtrl::InToOut(const CFX_PointF& point) const { +@@ -167,23 +163,23 @@ void CPWL_ListCtrl::OnMouseDown(const CFX_PointF& point, + if (IsMultipleSel()) { + if (bCtrl) { + if (IsItemSelected(nHitIndex)) { +- m_aSelItems.Sub(nHitIndex); ++ m_SelectState.Sub(nHitIndex); + SelectItems(); + m_bCtrlSel = false; + } else { +- m_aSelItems.Add(nHitIndex); ++ m_SelectState.Add(nHitIndex); + SelectItems(); + m_bCtrlSel = true; + } + + m_nFootIndex = nHitIndex; + } else if (bShift) { +- m_aSelItems.DeselectAll(); +- m_aSelItems.Add(m_nFootIndex, nHitIndex); ++ m_SelectState.DeselectAll(); ++ m_SelectState.Add(m_nFootIndex, nHitIndex); + SelectItems(); + } else { +- m_aSelItems.DeselectAll(); +- m_aSelItems.Add(nHitIndex); ++ m_SelectState.DeselectAll(); ++ m_SelectState.Add(nHitIndex); + SelectItems(); + + m_nFootIndex = nHitIndex; +@@ -206,14 +202,14 @@ void CPWL_ListCtrl::OnMouseMove(const CFX_PointF& point, + if (IsMultipleSel()) { + if (bCtrl) { + if (m_bCtrlSel) +- m_aSelItems.Add(m_nFootIndex, nHitIndex); ++ m_SelectState.Add(m_nFootIndex, nHitIndex); + else +- m_aSelItems.Sub(m_nFootIndex, nHitIndex); ++ m_SelectState.Sub(m_nFootIndex, nHitIndex); + + SelectItems(); + } else { +- m_aSelItems.DeselectAll(); +- m_aSelItems.Add(m_nFootIndex, nHitIndex); ++ m_SelectState.DeselectAll(); ++ m_SelectState.Add(m_nFootIndex, nHitIndex); + SelectItems(); + } + +@@ -231,12 +227,12 @@ void CPWL_ListCtrl::OnVK(int32_t nItemIndex, bool bShift, bool bCtrl) { + if (nItemIndex >= 0 && nItemIndex < GetCount()) { + if (bCtrl) { + } else if (bShift) { +- m_aSelItems.DeselectAll(); +- m_aSelItems.Add(m_nFootIndex, nItemIndex); ++ m_SelectState.DeselectAll(); ++ m_SelectState.Add(m_nFootIndex, nItemIndex); + SelectItems(); + } else { +- m_aSelItems.DeselectAll(); +- m_aSelItems.Add(nItemIndex); ++ m_SelectState.DeselectAll(); ++ m_SelectState.Add(nItemIndex); + SelectItems(); + m_nFootIndex = nItemIndex; + } +@@ -365,7 +361,7 @@ void CPWL_ListCtrl::InvalidateItem(int32_t nItemIndex) { + if (!m_bNotifyFlag) { + m_bNotifyFlag = true; + CFX_FloatRect rcRefresh = m_rcPlate; +- m_pNotify->IOnInvalidateRect(&rcRefresh); ++ m_pNotify->OnInvalidateRect(rcRefresh); + m_bNotifyFlag = false; + } + } else { +@@ -377,7 +373,7 @@ void CPWL_ListCtrl::InvalidateItem(int32_t nItemIndex) { + rcRefresh.bottom -= 1.0f; + rcRefresh.top += 1.0f; + +- m_pNotify->IOnInvalidateRect(&rcRefresh); ++ m_pNotify->OnInvalidateRect(rcRefresh); + m_bNotifyFlag = false; + } + } +@@ -385,11 +381,11 @@ void CPWL_ListCtrl::InvalidateItem(int32_t nItemIndex) { + } + + void CPWL_ListCtrl::SelectItems() { +- for (const auto& item : m_aSelItems) { +- if (item.second != CPLST_Select::NORMAL) +- SetMultipleSelect(item.first, item.second == CPLST_Select::SELECTING); ++ for (const auto& item : m_SelectState) { ++ if (item.second != SelectState::NORMAL) ++ SetMultipleSelect(item.first, item.second == SelectState::SELECTING); + } +- m_aSelItems.Done(); ++ m_SelectState.Done(); + } + + void CPWL_ListCtrl::Select(int32_t nItemIndex) { +@@ -397,7 +393,7 @@ void CPWL_ListCtrl::Select(int32_t nItemIndex) { + return; + + if (IsMultipleSel()) { +- m_aSelItems.Add(nItemIndex); ++ m_SelectState.Add(nItemIndex); + SelectItems(); + } else { + SetSingleSelect(nItemIndex); +@@ -429,12 +425,12 @@ void CPWL_ListCtrl::ScrollToListItem(int32_t nItemIndex) { + CFX_FloatRect rcItem = GetItemRectInternal(nItemIndex); + CFX_FloatRect rcItemCtrl = GetItemRect(nItemIndex); + +- if (IsFloatSmaller(rcItemCtrl.bottom, rcPlate.bottom)) { +- if (IsFloatSmaller(rcItemCtrl.top, rcPlate.top)) { ++ if (FXSYS_IsFloatSmaller(rcItemCtrl.bottom, rcPlate.bottom)) { ++ if (FXSYS_IsFloatSmaller(rcItemCtrl.top, rcPlate.top)) { + SetScrollPosY(rcItem.bottom + rcPlate.Height()); + } +- } else if (IsFloatBigger(rcItemCtrl.top, rcPlate.top)) { +- if (IsFloatBigger(rcItemCtrl.bottom, rcPlate.bottom)) { ++ } else if (FXSYS_IsFloatBigger(rcItemCtrl.top, rcPlate.top)) { ++ if (FXSYS_IsFloatBigger(rcItemCtrl.bottom, rcPlate.bottom)) { + SetScrollPosY(rcItem.top); + } + } +@@ -447,9 +443,9 @@ void CPWL_ListCtrl::SetScrollInfo() { + + if (!m_bNotifyFlag) { + m_bNotifyFlag = true; +- m_pNotify->IOnSetScrollInfoY(rcPlate.bottom, rcPlate.top, +- rcContent.bottom, rcContent.top, +- GetFirstHeight(), rcPlate.Height()); ++ m_pNotify->OnSetScrollInfoY(rcPlate.bottom, rcPlate.top, rcContent.bottom, ++ rcContent.top, GetFirstHeight(), ++ rcPlate.Height()); + m_bNotifyFlag = false; + } + } +@@ -460,16 +456,16 @@ void CPWL_ListCtrl::SetScrollPos(const CFX_PointF& point) { + } + + void CPWL_ListCtrl::SetScrollPosY(float fy) { +- if (!IsFloatEqual(m_ptScrollPos.y, fy)) { ++ if (!FXSYS_IsFloatEqual(m_ptScrollPos.y, fy)) { + CFX_FloatRect rcPlate = m_rcPlate; + CFX_FloatRect rcContent = GetContentRectInternal(); + + if (rcPlate.Height() > rcContent.Height()) { + fy = rcPlate.top; + } else { +- if (IsFloatSmaller(fy - rcPlate.Height(), rcContent.bottom)) { ++ if (FXSYS_IsFloatSmaller(fy - rcPlate.Height(), rcContent.bottom)) { + fy = rcContent.bottom + rcPlate.Height(); +- } else if (IsFloatBigger(fy, rcContent.top)) { ++ } else if (FXSYS_IsFloatBigger(fy, rcContent.top)) { + fy = rcContent.top; + } + } +@@ -480,7 +476,7 @@ void CPWL_ListCtrl::SetScrollPosY(float fy) { + if (m_pNotify) { + if (!m_bNotifyFlag) { + m_bNotifyFlag = true; +- m_pNotify->IOnSetScrollPosY(fy); ++ m_pNotify->OnSetScrollPosY(fy); + m_bNotifyFlag = false; + } + } +@@ -506,7 +502,7 @@ void CPWL_ListCtrl::ReArrange(int32_t nItemIndex) { + CFX_FloatRect(0.0f, fPosY + fListItemHeight, 0.0f, fPosY)); + fPosY += fListItemHeight; + } +- SetContentRect(CFX_FloatRect(0.0f, fPosY, 0.0f, 0.0f)); ++ m_rcContent = CFX_FloatRect(0.0f, fPosY, 0.0f, 0.0f); + SetScrollInfo(); + } + +@@ -525,27 +521,20 @@ int32_t CPWL_ListCtrl::GetTopItem() const { + return nItemIndex; + } + +-void CPWL_ListCtrl::Clear() { +- m_ListItems.clear(); +- InvalidateItem(-1); +-} +- +-void CPWL_ListCtrl::Cancel() { +- m_aSelItems.DeselectAll(); +-} +- + int32_t CPWL_ListCtrl::GetItemIndex(const CFX_PointF& point) const { + CFX_PointF pt = OuterToInner(OutToIn(point)); + bool bFirst = true; + bool bLast = true; + for (const auto& pListItem : m_ListItems) { + CFX_FloatRect rcListItem = pListItem->GetRect(); +- if (IsFloatBigger(pt.y, rcListItem.top)) ++ if (FXSYS_IsFloatBigger(pt.y, rcListItem.top)) + bFirst = false; +- if (IsFloatSmaller(pt.y, rcListItem.bottom)) ++ if (FXSYS_IsFloatSmaller(pt.y, rcListItem.bottom)) + bLast = false; +- if (pt.y >= rcListItem.top && pt.y < rcListItem.bottom) +- return &pListItem - &m_ListItems.front(); ++ if (pt.y >= rcListItem.top && pt.y < rcListItem.bottom) { ++ return pdfium::base::checked_cast(&pListItem - ++ &m_ListItems.front()); ++ } + } + if (bFirst) + return 0; +@@ -561,8 +550,8 @@ WideString CPWL_ListCtrl::GetText() const { + } + + void CPWL_ListCtrl::AddItem(const WideString& str) { +- auto pListItem = pdfium::MakeUnique(); +- pListItem->SetFontMap(m_pFontMap.Get()); ++ auto pListItem = std::make_unique(); ++ pListItem->SetFontMap(m_pFontMap); + pListItem->SetFontSize(m_fFontSize); + pListItem->SetText(str); + m_ListItems.push_back(std::move(pListItem)); +@@ -575,7 +564,7 @@ CPWL_EditImpl* CPWL_ListCtrl::GetItemEdit(int32_t nIndex) const { + } + + int32_t CPWL_ListCtrl::GetCount() const { +- return pdfium::CollectionSize(m_ListItems); ++ return fxcrt::CollectionSize(m_ListItems); + } + + float CPWL_ListCtrl::GetFirstHeight() const { +@@ -597,7 +586,7 @@ int32_t CPWL_ListCtrl::GetFirstSelected() const { + int32_t CPWL_ListCtrl::GetLastSelected() const { + for (auto iter = m_ListItems.rbegin(); iter != m_ListItems.rend(); ++iter) { + if ((*iter)->IsSelected()) +- return &*iter - &m_ListItems.front(); ++ return pdfium::base::checked_cast(&*iter - &m_ListItems.front()); + } + return -1; + } +@@ -629,7 +618,7 @@ void CPWL_ListCtrl::SetItemSelect(int32_t nIndex, bool bSelected) { + } + + bool CPWL_ListCtrl::IsValid(int32_t nItemIndex) const { +- return pdfium::IndexInBounds(m_ListItems, nItemIndex); ++ return fxcrt::IndexInBounds(m_ListItems, nItemIndex); + } + + WideString CPWL_ListCtrl::GetItemText(int32_t nIndex) const { +diff --git a/fpdfsdk/pwl/cpwl_list_impl.h b/fpdfsdk/pwl/cpwl_list_ctrl.h +similarity index 71% +rename from fpdfsdk/pwl/cpwl_list_impl.h +rename to fpdfsdk/pwl/cpwl_list_ctrl.h +index c7a86ab09..3f36354ab 100644 +--- a/fpdfsdk/pwl/cpwl_list_impl.h ++++ b/fpdfsdk/pwl/cpwl_list_ctrl.h +@@ -1,53 +1,43 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#ifndef FPDFSDK_PWL_CPWL_LIST_IMPL_H_ +-#define FPDFSDK_PWL_CPWL_LIST_IMPL_H_ ++#ifndef FPDFSDK_PWL_CPWL_LIST_CTRL_H_ ++#define FPDFSDK_PWL_CPWL_LIST_CTRL_H_ + + #include + #include + #include + + #include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "core/fxcrt/widestring.h" ++#include "fpdfsdk/pwl/cpwl_edit_impl.h" + +-class CPWL_EditImpl; +-class CPWL_EditImpl_Iterator; +-class CPWL_List_Notify; + class IPVT_FontMap; + +-class CPLST_Select final { +- public: +- enum State { DESELECTING = -1, NORMAL = 0, SELECTING = 1 }; +- using const_iterator = std::map::const_iterator; +- +- CPLST_Select(); +- ~CPLST_Select(); +- +- void Add(int32_t nItemIndex); +- void Add(int32_t nBeginIndex, int32_t nEndIndex); +- void Sub(int32_t nItemIndex); +- void Sub(int32_t nBeginIndex, int32_t nEndIndex); +- void DeselectAll(); +- void Done(); +- +- const_iterator begin() const { return m_Items.begin(); } +- const_iterator end() const { return m_Items.end(); } +- +- private: +- std::map m_Items; +-}; +- + class CPWL_ListCtrl { + public: ++ class NotifyIface { ++ public: ++ virtual ~NotifyIface(); ++ ++ virtual void OnSetScrollInfoY(float fPlateMin, ++ float fPlateMax, ++ float fContentMin, ++ float fContentMax, ++ float fSmallStep, ++ float fBigStep) = 0; ++ virtual void OnSetScrollPosY(float fy) = 0; ++ virtual void OnInvalidateRect(const CFX_FloatRect& rect) = 0; ++ }; ++ + CPWL_ListCtrl(); + ~CPWL_ListCtrl(); + +- void SetNotify(CPWL_List_Notify* pNotify) { m_pNotify = pNotify; } ++ void SetNotify(NotifyIface* pNotify) { m_pNotify = pNotify; } + void OnMouseDown(const CFX_PointF& point, bool bShift, bool bCtrl); + void OnMouseMove(const CFX_PointF& point, bool bShift, bool bCtrl); + void OnVK_UP(bool bShift, bool bCtrl); +@@ -64,7 +54,6 @@ class CPWL_ListCtrl { + int32_t GetCaret() const { return m_nCaretIndex; } + int32_t GetSelect() const { return m_nSelItem; } + int32_t GetTopItem() const; +- void SetContentRect(const CFX_FloatRect& rect) { m_rcContent = rect; } + CFX_FloatRect GetContentRect() const; + + int32_t GetItemIndex(const CFX_PointF& point) const; +@@ -73,8 +62,6 @@ class CPWL_ListCtrl { + void Select(int32_t nItemIndex); + void Deselect(int32_t nItemIndex); + void SetCaret(int32_t nItemIndex); +- void Clear(); +- void Cancel(); + WideString GetText() const; + + void SetFontMap(IPVT_FontMap* pFontMap) { m_pFontMap = pFontMap; } +@@ -113,13 +100,35 @@ class CPWL_ListCtrl { + uint16_t GetFirstChar() const; + + private: +- CPWL_EditImpl_Iterator* GetIterator() const; ++ CPWL_EditImpl::Iterator* GetIterator() const; + + bool m_bSelected = false; + CFX_FloatRect m_rcListItem; + std::unique_ptr const m_pEdit; + }; + ++ class SelectState { ++ public: ++ enum State { DESELECTING = -1, NORMAL = 0, SELECTING = 1 }; ++ using const_iterator = std::map::const_iterator; ++ ++ SelectState(); ++ ~SelectState(); ++ ++ void Add(int32_t nItemIndex); ++ void Add(int32_t nBeginIndex, int32_t nEndIndex); ++ void Sub(int32_t nItemIndex); ++ void Sub(int32_t nBeginIndex, int32_t nEndIndex); ++ void DeselectAll(); ++ void Done(); ++ ++ const_iterator begin() const { return m_Items.begin(); } ++ const_iterator end() const { return m_Items.end(); } ++ ++ private: ++ std::map m_Items; ++ }; ++ + CFX_PointF InToOut(const CFX_PointF& point) const; + CFX_PointF OutToIn(const CFX_PointF& point) const; + CFX_FloatRect InToOut(const CFX_FloatRect& rect) const; +@@ -153,18 +162,23 @@ class CPWL_ListCtrl { + + CFX_FloatRect m_rcPlate; + CFX_FloatRect m_rcContent; +- UnownedPtr m_pNotify; +- bool m_bNotifyFlag; + CFX_PointF m_ptScrollPos; +- CPLST_Select m_aSelItems; // for multiple +- int32_t m_nSelItem; // for single +- int32_t m_nFootIndex; // for multiple +- bool m_bCtrlSel; // for multiple +- int32_t m_nCaretIndex; // for multiple ++ float m_fFontSize = 0.0f; ++ ++ // For single: ++ int32_t m_nSelItem = -1; ++ ++ // For multiple: ++ SelectState m_SelectState; ++ int32_t m_nFootIndex = -1; ++ int32_t m_nCaretIndex = -1; ++ bool m_bCtrlSel = false; ++ ++ bool m_bMultiple = false; ++ bool m_bNotifyFlag = false; ++ UnownedPtr m_pNotify; + std::vector> m_ListItems; +- float m_fFontSize; + UnownedPtr m_pFontMap; +- bool m_bMultiple; + }; + +-#endif // FPDFSDK_PWL_CPWL_LIST_IMPL_H_ ++#endif // FPDFSDK_PWL_CPWL_LIST_CTRL_H_ +diff --git a/fpdfsdk/pwl/cpwl_sbbutton.cpp b/fpdfsdk/pwl/cpwl_sbbutton.cpp +new file mode 100644 +index 000000000..889ce5e76 +--- /dev/null ++++ b/fpdfsdk/pwl/cpwl_sbbutton.cpp +@@ -0,0 +1,148 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "fpdfsdk/pwl/cpwl_sbbutton.h" ++ ++#include ++#include ++ ++#include "core/fxge/cfx_renderdevice.h" ++ ++CPWL_SBButton::CPWL_SBButton( ++ const CreateParams& cp, ++ std::unique_ptr pAttachedData, ++ Type eButtonType) ++ : CPWL_Wnd(cp, std::move(pAttachedData)), m_eSBButtonType(eButtonType) { ++ GetCreationParams()->eCursorType = IPWL_FillerNotify::CursorStyle::kArrow; ++} ++ ++CPWL_SBButton::~CPWL_SBButton() = default; ++ ++void CPWL_SBButton::DrawThisAppearance(CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device) { ++ if (!IsVisible()) ++ return; ++ ++ CFX_FloatRect rectWnd = GetWindowRect(); ++ if (rectWnd.IsEmpty()) ++ return; ++ ++ CFX_PointF ptCenter = GetCenterPoint(); ++ int32_t nTransparency = GetTransparency(); ++ ++ // draw border ++ pDevice->DrawStrokeRect(mtUser2Device, rectWnd, ++ ArgbEncode(nTransparency, 100, 100, 100), 0.0f); ++ pDevice->DrawStrokeRect(mtUser2Device, rectWnd.GetDeflated(0.5f, 0.5f), ++ ArgbEncode(nTransparency, 255, 255, 255), 1.0f); ++ ++ if (m_eSBButtonType != Type::kPosButton) { ++ // draw background ++ pDevice->DrawShadow(mtUser2Device, rectWnd.GetDeflated(1.0f, 1.0f), ++ nTransparency, 80, 220); ++ // draw arrow ++ if (rectWnd.top - rectWnd.bottom > 6.0f) { ++ float fX = rectWnd.left + 1.5f; ++ float fY = rectWnd.bottom; ++ std::vector pts; ++ static constexpr float kOffsetsX[] = {2.5f, 2.5f, 4.5f, 6.5f, ++ 6.5f, 4.5f, 2.5f}; ++ static constexpr float kOffsetsY[] = {5.0f, 6.0f, 4.0f, 6.0f, ++ 5.0f, 3.0f, 5.0f}; ++ static constexpr float kOffsetsMinY[] = {4.0f, 3.0f, 5.0f, 3.0f, ++ 4.0f, 6.0f, 4.0f}; ++ static_assert(std::size(kOffsetsX) == std::size(kOffsetsY), ++ "Wrong offset count"); ++ static_assert(std::size(kOffsetsX) == std::size(kOffsetsMinY), ++ "Wrong offset count"); ++ const float* pOffsetsY = ++ m_eSBButtonType == Type::kMinButton ? kOffsetsMinY : kOffsetsY; ++ for (size_t i = 0; i < std::size(kOffsetsX); ++i) ++ pts.push_back(CFX_PointF(fX + kOffsetsX[i], fY + pOffsetsY[i])); ++ pDevice->DrawFillArea(mtUser2Device, pts, ++ ArgbEncode(nTransparency, 255, 255, 255)); ++ } ++ return; ++ } ++ ++ // draw shadow effect ++ CFX_PointF ptTop = CFX_PointF(rectWnd.left, rectWnd.top - 1.0f); ++ CFX_PointF ptBottom = CFX_PointF(rectWnd.left, rectWnd.bottom + 1.0f); ++ ++ ptTop.x += 1.5f; ++ ptBottom.x += 1.5f; ++ ++ const FX_COLORREF refs[] = {ArgbEncode(nTransparency, 210, 210, 210), ++ ArgbEncode(nTransparency, 220, 220, 220), ++ ArgbEncode(nTransparency, 240, 240, 240), ++ ArgbEncode(nTransparency, 240, 240, 240), ++ ArgbEncode(nTransparency, 210, 210, 210), ++ ArgbEncode(nTransparency, 180, 180, 180), ++ ArgbEncode(nTransparency, 150, 150, 150), ++ ArgbEncode(nTransparency, 150, 150, 150), ++ ArgbEncode(nTransparency, 180, 180, 180), ++ ArgbEncode(nTransparency, 210, 210, 210)}; ++ for (FX_COLORREF ref : refs) { ++ pDevice->DrawStrokeLine(&mtUser2Device, ptTop, ptBottom, ref, 1.0f); ++ ++ ptTop.x += 1.0f; ++ ptBottom.x += 1.0f; ++ } ++ ++ // draw friction ++ if (rectWnd.Height() <= 8.0f) ++ return; ++ ++ FX_COLORREF crStroke = ArgbEncode(nTransparency, 120, 120, 120); ++ float nFrictionWidth = 5.0f; ++ float nFrictionHeight = 5.5f; ++ CFX_PointF ptLeft = CFX_PointF(ptCenter.x - nFrictionWidth / 2.0f, ++ ptCenter.y - nFrictionHeight / 2.0f + 0.5f); ++ CFX_PointF ptRight = CFX_PointF(ptCenter.x + nFrictionWidth / 2.0f, ++ ptCenter.y - nFrictionHeight / 2.0f + 0.5f); ++ ++ for (size_t i = 0; i < 3; ++i) { ++ pDevice->DrawStrokeLine(&mtUser2Device, ptLeft, ptRight, crStroke, 1.0f); ++ ptLeft.y += 2.0f; ++ ptRight.y += 2.0f; ++ } ++} ++ ++bool CPWL_SBButton::OnLButtonDown(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnLButtonDown(nFlag, point); ++ ++ if (CPWL_Wnd* pParent = GetParentWindow()) ++ pParent->NotifyLButtonDown(this, point); ++ ++ m_bMouseDown = true; ++ SetCapture(); ++ ++ return true; ++} ++ ++bool CPWL_SBButton::OnLButtonUp(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnLButtonUp(nFlag, point); ++ ++ if (CPWL_Wnd* pParent = GetParentWindow()) ++ pParent->NotifyLButtonUp(this, point); ++ ++ m_bMouseDown = false; ++ ReleaseCapture(); ++ ++ return true; ++} ++ ++bool CPWL_SBButton::OnMouseMove(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnMouseMove(nFlag, point); ++ ++ if (CPWL_Wnd* pParent = GetParentWindow()) ++ pParent->NotifyMouseMove(this, point); ++ ++ return true; ++} +diff --git a/fpdfsdk/pwl/cpwl_sbbutton.h b/fpdfsdk/pwl/cpwl_sbbutton.h +new file mode 100644 +index 000000000..9b4abc876 +--- /dev/null ++++ b/fpdfsdk/pwl/cpwl_sbbutton.h +@@ -0,0 +1,37 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef FPDFSDK_PWL_CPWL_SBBUTTON_H_ ++#define FPDFSDK_PWL_CPWL_SBBUTTON_H_ ++ ++#include ++ ++#include "fpdfsdk/pwl/cpwl_wnd.h" ++#include "fpdfsdk/pwl/ipwl_fillernotify.h" ++ ++class CPWL_SBButton final : public CPWL_Wnd { ++ public: ++ enum class Type : uint8_t { kMinButton, kMaxButton, kPosButton }; ++ ++ CPWL_SBButton(const CreateParams& cp, ++ std::unique_ptr pAttachedData, ++ Type eButtonType); ++ ~CPWL_SBButton() override; ++ ++ // CPWL_Wnd ++ void DrawThisAppearance(CFX_RenderDevice* pDevice, ++ const CFX_Matrix& mtUser2Device) override; ++ bool OnLButtonDown(Mask nFlag, ++ const CFX_PointF& point) override; ++ bool OnLButtonUp(Mask nFlag, const CFX_PointF& point) override; ++ bool OnMouseMove(Mask nFlag, const CFX_PointF& point) override; ++ ++ private: ++ const Type m_eSBButtonType; ++ bool m_bMouseDown = false; ++}; ++ ++#endif // FPDFSDK_PWL_CPWL_SBBUTTON_H_ +diff --git a/fpdfsdk/pwl/cpwl_scroll_bar.cpp b/fpdfsdk/pwl/cpwl_scroll_bar.cpp +index bf4d1da98..00a94189d 100644 +--- a/fpdfsdk/pwl/cpwl_scroll_bar.cpp ++++ b/fpdfsdk/pwl/cpwl_scroll_bar.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,26 +6,25 @@ + + #include "fpdfsdk/pwl/cpwl_scroll_bar.h" + ++#include ++ + #include + #include + #include +-#include + +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_fillrenderoptions.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/cfx_renderdevice.h" + #include "fpdfsdk/pwl/cpwl_wnd.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + namespace { + + constexpr float kButtonWidth = 9.0f; + constexpr float kPosButtonMinWidth = 2.0f; +-constexpr float kScrollBarTriangleHalfLength = 2.0f; + + } // namespace + +-#define PWL_DEFAULT_HEAVYGRAYCOLOR CFX_Color(CFX_Color::kGray, 0.50) +- + void PWL_FLOATRANGE::Reset() { + fMin = 0.0f; + fMax = 0.0f; +@@ -37,8 +36,8 @@ void PWL_FLOATRANGE::Set(float min, float max) { + } + + bool PWL_FLOATRANGE::In(float x) const { +- return (IsFloatBigger(x, fMin) || IsFloatEqual(x, fMin)) && +- (IsFloatSmaller(x, fMax) || IsFloatEqual(x, fMax)); ++ return (FXSYS_IsFloatBigger(x, fMin) || FXSYS_IsFloatEqual(x, fMin)) && ++ (FXSYS_IsFloatSmaller(x, fMax) || FXSYS_IsFloatEqual(x, fMax)); + } + + float PWL_FLOATRANGE::GetWidth() const { +@@ -60,9 +59,9 @@ void PWL_SCROLL_PRIVATEDATA::Default() { + void PWL_SCROLL_PRIVATEDATA::SetScrollRange(float min, float max) { + ScrollRange.Set(min, max); + +- if (IsFloatSmaller(fScrollPos, ScrollRange.fMin)) ++ if (FXSYS_IsFloatSmaller(fScrollPos, ScrollRange.fMin)) + fScrollPos = ScrollRange.fMin; +- if (IsFloatBigger(fScrollPos, ScrollRange.fMax)) ++ if (FXSYS_IsFloatBigger(fScrollPos, ScrollRange.fMax)) + fScrollPos = ScrollRange.fMax; + } + +@@ -106,205 +105,11 @@ void PWL_SCROLL_PRIVATEDATA::SubBig() { + SetPos(ScrollRange.fMin); + } + +-CPWL_SBButton::CPWL_SBButton( +- const CreateParams& cp, +- std::unique_ptr pAttachedData, +- PWL_SCROLLBAR_TYPE eScrollBarType, +- PWL_SBBUTTON_TYPE eButtonType) +- : CPWL_Wnd(cp, std::move(pAttachedData)), +- m_eScrollBarType(eScrollBarType), +- m_eSBButtonType(eButtonType) { +- GetCreationParams()->eCursorType = FXCT_ARROW; +-} +- +-CPWL_SBButton::~CPWL_SBButton() = default; +- +-void CPWL_SBButton::DrawThisAppearance(CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device) { +- if (!IsVisible()) +- return; +- +- CFX_FloatRect rectWnd = GetWindowRect(); +- if (rectWnd.IsEmpty()) +- return; +- +- CFX_PointF ptCenter = GetCenterPoint(); +- int32_t nTransparency = GetTransparency(); +- +- if (m_eScrollBarType == SBT_HSCROLL) { +- CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device); +- +- CFX_PointF pt1; +- CFX_PointF pt2; +- CFX_PointF pt3; +- static constexpr float kScrollBarTriangleQuarterLength = +- kScrollBarTriangleHalfLength * 0.5; +- if (m_eSBButtonType == PSBT_MIN) { +- pt1 = +- CFX_PointF(ptCenter.x - kScrollBarTriangleQuarterLength, ptCenter.y); +- pt2 = CFX_PointF(ptCenter.x + kScrollBarTriangleQuarterLength, +- ptCenter.y + kScrollBarTriangleHalfLength); +- pt3 = CFX_PointF(ptCenter.x + kScrollBarTriangleQuarterLength, +- ptCenter.y - kScrollBarTriangleHalfLength); +- } else if (m_eSBButtonType == PSBT_MAX) { +- pt1 = +- CFX_PointF(ptCenter.x + kScrollBarTriangleQuarterLength, ptCenter.y); +- pt2 = CFX_PointF(ptCenter.x - kScrollBarTriangleQuarterLength, +- ptCenter.y + kScrollBarTriangleHalfLength); +- pt3 = CFX_PointF(ptCenter.x - kScrollBarTriangleQuarterLength, +- ptCenter.y - kScrollBarTriangleHalfLength); +- } +- +- if (rectWnd.right - rectWnd.left > kScrollBarTriangleHalfLength * 2 && +- rectWnd.top - rectWnd.bottom > kScrollBarTriangleHalfLength) { +- CFX_PathData path; +- path.AppendPoint(pt1, FXPT_TYPE::MoveTo, false); +- path.AppendPoint(pt2, FXPT_TYPE::LineTo, false); +- path.AppendPoint(pt3, FXPT_TYPE::LineTo, false); +- path.AppendPoint(pt1, FXPT_TYPE::LineTo, false); +- +- pDevice->DrawPath(&path, &mtUser2Device, nullptr, +- PWL_DEFAULT_BLACKCOLOR.ToFXColor(nTransparency), 0, +- FXFILL_ALTERNATE); +- } +- return; +- } +- +- // draw border +- pDevice->DrawStrokeRect(mtUser2Device, rectWnd, +- ArgbEncode(nTransparency, 100, 100, 100), 0.0f); +- pDevice->DrawStrokeRect(mtUser2Device, rectWnd.GetDeflated(0.5f, 0.5f), +- ArgbEncode(nTransparency, 255, 255, 255), 1.0f); +- +- if (m_eSBButtonType != PSBT_POS) { +- // draw background +- if (IsEnabled()) { +- pDevice->DrawShadow(mtUser2Device, true, false, +- rectWnd.GetDeflated(1.0f, 1.0f), nTransparency, 80, +- 220); +- } else { +- pDevice->DrawFillRect(&mtUser2Device, rectWnd.GetDeflated(1.0f, 1.0f), +- ArgbEncode(255, 255, 255, 255)); +- } +- +- // draw arrow +- if (rectWnd.top - rectWnd.bottom > 6.0f) { +- float fX = rectWnd.left + 1.5f; +- float fY = rectWnd.bottom; +- std::vector pts; +- static constexpr float kOffsetsX[] = {2.5f, 2.5f, 4.5f, 6.5f, +- 6.5f, 4.5f, 2.5f}; +- static constexpr float kOffsetsY[] = {5.0f, 6.0f, 4.0f, 6.0f, +- 5.0f, 3.0f, 5.0f}; +- static constexpr float kOffsetsMinY[] = {4.0f, 3.0f, 5.0f, 3.0f, +- 4.0f, 6.0f, 4.0f}; +- static_assert(FX_ArraySize(kOffsetsX) == FX_ArraySize(kOffsetsY), +- "Wrong offset count"); +- static_assert(FX_ArraySize(kOffsetsX) == FX_ArraySize(kOffsetsMinY), +- "Wrong offset count"); +- const float* pOffsetsY = +- m_eSBButtonType == PSBT_MIN ? kOffsetsMinY : kOffsetsY; +- for (size_t i = 0; i < FX_ArraySize(kOffsetsX); ++i) +- pts.push_back(CFX_PointF(fX + kOffsetsX[i], fY + pOffsetsY[i])); +- pDevice->DrawFillArea(mtUser2Device, pts, +- IsEnabled() +- ? ArgbEncode(nTransparency, 255, 255, 255) +- : PWL_DEFAULT_HEAVYGRAYCOLOR.ToFXColor(255)); +- } +- return; +- } +- +- if (IsEnabled()) { +- // draw shadow effect +- CFX_PointF ptTop = CFX_PointF(rectWnd.left, rectWnd.top - 1.0f); +- CFX_PointF ptBottom = CFX_PointF(rectWnd.left, rectWnd.bottom + 1.0f); +- +- ptTop.x += 1.5f; +- ptBottom.x += 1.5f; +- +- const FX_COLORREF refs[] = {ArgbEncode(nTransparency, 210, 210, 210), +- ArgbEncode(nTransparency, 220, 220, 220), +- ArgbEncode(nTransparency, 240, 240, 240), +- ArgbEncode(nTransparency, 240, 240, 240), +- ArgbEncode(nTransparency, 210, 210, 210), +- ArgbEncode(nTransparency, 180, 180, 180), +- ArgbEncode(nTransparency, 150, 150, 150), +- ArgbEncode(nTransparency, 150, 150, 150), +- ArgbEncode(nTransparency, 180, 180, 180), +- ArgbEncode(nTransparency, 210, 210, 210)}; +- for (FX_COLORREF ref : refs) { +- pDevice->DrawStrokeLine(&mtUser2Device, ptTop, ptBottom, ref, 1.0f); +- +- ptTop.x += 1.0f; +- ptBottom.x += 1.0f; +- } +- } else { +- pDevice->DrawFillRect(&mtUser2Device, rectWnd.GetDeflated(0.5f, 0.5f), +- ArgbEncode(255, 255, 255, 255)); +- } +- +- // draw friction +- if (rectWnd.Height() <= 8.0f) +- return; +- +- FX_COLORREF crStroke = ArgbEncode(nTransparency, 120, 120, 120); +- if (!IsEnabled()) +- crStroke = PWL_DEFAULT_HEAVYGRAYCOLOR.ToFXColor(255); +- +- float nFrictionWidth = 5.0f; +- float nFrictionHeight = 5.5f; +- +- CFX_PointF ptLeft = CFX_PointF(ptCenter.x - nFrictionWidth / 2.0f, +- ptCenter.y - nFrictionHeight / 2.0f + 0.5f); +- CFX_PointF ptRight = CFX_PointF(ptCenter.x + nFrictionWidth / 2.0f, +- ptCenter.y - nFrictionHeight / 2.0f + 0.5f); +- +- for (size_t i = 0; i < 3; ++i) { +- pDevice->DrawStrokeLine(&mtUser2Device, ptLeft, ptRight, crStroke, 1.0f); +- ptLeft.y += 2.0f; +- ptRight.y += 2.0f; +- } +-} +- +-bool CPWL_SBButton::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnLButtonDown(point, nFlag); +- +- if (CPWL_Wnd* pParent = GetParentWindow()) +- pParent->NotifyLButtonDown(this, point); +- +- m_bMouseDown = true; +- SetCapture(); +- +- return true; +-} +- +-bool CPWL_SBButton::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnLButtonUp(point, nFlag); +- +- if (CPWL_Wnd* pParent = GetParentWindow()) +- pParent->NotifyLButtonUp(this, point); +- +- m_bMouseDown = false; +- ReleaseCapture(); +- +- return true; +-} +- +-bool CPWL_SBButton::OnMouseMove(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnMouseMove(point, nFlag); +- +- if (CPWL_Wnd* pParent = GetParentWindow()) +- pParent->NotifyMouseMove(this, point); +- +- return true; +-} +- + CPWL_ScrollBar::CPWL_ScrollBar( + const CreateParams& cp, +- std::unique_ptr pAttachedData, +- PWL_SCROLLBAR_TYPE sbType) +- : CPWL_Wnd(cp, std::move(pAttachedData)), m_sbType(sbType) { +- GetCreationParams()->eCursorType = FXCT_ARROW; ++ std::unique_ptr pAttachedData) ++ : CPWL_Wnd(cp, std::move(pAttachedData)) { ++ GetCreationParams()->eCursorType = IPWL_FillerNotify::CursorStyle::kArrow; + } + + CPWL_ScrollBar::~CPWL_ScrollBar() = default; +@@ -314,63 +119,34 @@ void CPWL_ScrollBar::OnDestroy() { + // subclasses, implement the virtual OnDestroy method that does the + // cleanup first, then invokes the superclass OnDestroy ... gee, + // like a dtor would. +- m_pMinButton.Release(); +- m_pMaxButton.Release(); +- m_pPosButton.Release(); ++ m_pMinButton.ExtractAsDangling(); ++ m_pMaxButton.ExtractAsDangling(); ++ m_pPosButton.ExtractAsDangling(); + CPWL_Wnd::OnDestroy(); + } + + bool CPWL_ScrollBar::RePosChildWnd() { + CFX_FloatRect rcClient = GetClientRect(); +- CFX_FloatRect rcMinButton, rcMaxButton; +- float fBWidth = 0; +- +- switch (m_sbType) { +- case SBT_HSCROLL: +- if (rcClient.right - rcClient.left > +- kButtonWidth * 2 + kPosButtonMinWidth + 2) { +- rcMinButton = CFX_FloatRect(rcClient.left, rcClient.bottom, +- rcClient.left + kButtonWidth, rcClient.top); +- rcMaxButton = +- CFX_FloatRect(rcClient.right - kButtonWidth, rcClient.bottom, +- rcClient.right, rcClient.top); +- } else { +- fBWidth = (rcClient.right - rcClient.left - kPosButtonMinWidth - 2) / 2; +- +- if (fBWidth > 0) { +- rcMinButton = CFX_FloatRect(rcClient.left, rcClient.bottom, +- rcClient.left + fBWidth, rcClient.top); +- rcMaxButton = CFX_FloatRect(rcClient.right - fBWidth, rcClient.bottom, +- rcClient.right, rcClient.top); +- } else { +- if (!SetVisible(false)) +- return false; +- } +- } +- break; +- case SBT_VSCROLL: +- if (IsFloatBigger(rcClient.top - rcClient.bottom, +- kButtonWidth * 2 + kPosButtonMinWidth + 2)) { +- rcMinButton = CFX_FloatRect(rcClient.left, rcClient.top - kButtonWidth, +- rcClient.right, rcClient.top); +- rcMaxButton = +- CFX_FloatRect(rcClient.left, rcClient.bottom, rcClient.right, +- rcClient.bottom + kButtonWidth); +- } else { +- fBWidth = (rcClient.top - rcClient.bottom - kPosButtonMinWidth - 2) / 2; +- +- if (IsFloatBigger(fBWidth, 0)) { +- rcMinButton = CFX_FloatRect(rcClient.left, rcClient.top - fBWidth, +- rcClient.right, rcClient.top); +- rcMaxButton = +- CFX_FloatRect(rcClient.left, rcClient.bottom, rcClient.right, +- rcClient.bottom + fBWidth); +- } else { +- if (!SetVisible(false)) +- return false; +- } +- } +- break; ++ CFX_FloatRect rcMinButton; ++ CFX_FloatRect rcMaxButton; ++ if (FXSYS_IsFloatBigger(rcClient.top - rcClient.bottom, ++ kButtonWidth * 2 + kPosButtonMinWidth + 2)) { ++ rcMinButton = CFX_FloatRect(rcClient.left, rcClient.top - kButtonWidth, ++ rcClient.right, rcClient.top); ++ rcMaxButton = CFX_FloatRect(rcClient.left, rcClient.bottom, rcClient.right, ++ rcClient.bottom + kButtonWidth); ++ } else { ++ float fBWidth = ++ (rcClient.top - rcClient.bottom - kPosButtonMinWidth - 2) / 2; ++ if (FXSYS_IsFloatBigger(fBWidth, 0)) { ++ rcMinButton = CFX_FloatRect(rcClient.left, rcClient.top - fBWidth, ++ rcClient.right, rcClient.top); ++ rcMaxButton = CFX_FloatRect(rcClient.left, rcClient.bottom, ++ rcClient.right, rcClient.bottom + fBWidth); ++ } else { ++ if (!SetVisible(false)) ++ return false; ++ } + } + + ObservedPtr thisObserved(this); +@@ -384,10 +160,8 @@ bool CPWL_ScrollBar::RePosChildWnd() { + if (!thisObserved) + return false; + } +- if (!MovePosButton(false)) +- return false; + +- return true; ++ return MovePosButton(false); + } + + void CPWL_ScrollBar::DrawThisAppearance(CFX_RenderDevice* pDevice, +@@ -410,8 +184,9 @@ void CPWL_ScrollBar::DrawThisAppearance(CFX_RenderDevice* pDevice, + } + } + +-bool CPWL_ScrollBar::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnLButtonDown(point, nFlag); ++bool CPWL_ScrollBar::OnLButtonDown(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnLButtonDown(nFlag, point); + + if (HasFlag(PWS_AUTOTRANSPARENT)) { + if (GetTransparency() != 255) { +@@ -421,27 +196,15 @@ bool CPWL_ScrollBar::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { + } + } + +- CFX_FloatRect rcMinArea, rcMaxArea; +- + if (m_pPosButton && m_pPosButton->IsVisible()) { + CFX_FloatRect rcClient = GetClientRect(); + CFX_FloatRect rcPosButton = m_pPosButton->GetWindowRect(); +- +- switch (m_sbType) { +- case SBT_HSCROLL: +- rcMinArea = CFX_FloatRect(rcClient.left + kButtonWidth, rcClient.bottom, +- rcPosButton.left, rcClient.top); +- rcMaxArea = CFX_FloatRect(rcPosButton.right, rcClient.bottom, +- rcClient.right - kButtonWidth, rcClient.top); +- +- break; +- case SBT_VSCROLL: +- rcMinArea = CFX_FloatRect(rcClient.left, rcPosButton.top, +- rcClient.right, rcClient.top - kButtonWidth); +- rcMaxArea = CFX_FloatRect(rcClient.left, rcClient.bottom + kButtonWidth, +- rcClient.right, rcPosButton.bottom); +- break; +- } ++ CFX_FloatRect rcMinArea = ++ CFX_FloatRect(rcClient.left, rcPosButton.top, rcClient.right, ++ rcClient.top - kButtonWidth); ++ CFX_FloatRect rcMaxArea = ++ CFX_FloatRect(rcClient.left, rcClient.bottom + kButtonWidth, ++ rcClient.right, rcPosButton.bottom); + + rcMinArea.Normalize(); + rcMaxArea.Normalize(); +@@ -464,12 +227,13 @@ bool CPWL_ScrollBar::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { + return true; + } + +-bool CPWL_ScrollBar::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { +- CPWL_Wnd::OnLButtonUp(point, nFlag); ++bool CPWL_ScrollBar::OnLButtonUp(Mask nFlag, ++ const CFX_PointF& point) { ++ CPWL_Wnd::OnLButtonUp(nFlag, point); + + if (HasFlag(PWS_AUTOTRANSPARENT)) { +- if (GetTransparency() != PWL_SCROLLBAR_TRANSPARENCY) { +- SetTransparency(PWL_SCROLLBAR_TRANSPARENCY); ++ if (GetTransparency() != kTransparency) { ++ SetTransparency(kTransparency); + if (!InvalidateRect(nullptr)) + return true; + } +@@ -492,14 +256,7 @@ void CPWL_ScrollBar::SetScrollInfo(const PWL_SCROLL_INFO& info) { + } + + void CPWL_ScrollBar::SetScrollPosition(float pos) { +- switch (m_sbType) { +- case SBT_HSCROLL: +- pos = pos - m_OriginInfo.fContentMin; +- break; +- case SBT_VSCROLL: +- pos = m_OriginInfo.fContentMax - pos; +- break; +- } ++ pos = m_OriginInfo.fContentMax - pos; + SetScrollPos(pos); + } + +@@ -533,29 +290,28 @@ void CPWL_ScrollBar::NotifyMouseMove(CPWL_Wnd* child, const CFX_PointF& pos) { + void CPWL_ScrollBar::CreateButtons(const CreateParams& cp) { + CreateParams scp = cp; + scp.dwBorderWidth = 2; +- scp.nBorderStyle = BorderStyle::BEVELED; +- scp.dwFlags = +- PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PWS_BACKGROUND | PWS_NOREFRESHCLIP; ++ scp.nBorderStyle = BorderStyle::kBeveled; ++ scp.dwFlags = PWS_VISIBLE | PWS_BORDER | PWS_BACKGROUND | PWS_NOREFRESHCLIP; + + if (!m_pMinButton) { +- auto pButton = pdfium::MakeUnique(scp, CloneAttachedData(), +- m_sbType, PSBT_MIN); ++ auto pButton = std::make_unique( ++ scp, CloneAttachedData(), CPWL_SBButton::Type::kMinButton); + m_pMinButton = pButton.get(); + AddChild(std::move(pButton)); + m_pMinButton->Realize(); + } + + if (!m_pMaxButton) { +- auto pButton = pdfium::MakeUnique(scp, CloneAttachedData(), +- m_sbType, PSBT_MAX); ++ auto pButton = std::make_unique( ++ scp, CloneAttachedData(), CPWL_SBButton::Type::kMaxButton); + m_pMaxButton = pButton.get(); + AddChild(std::move(pButton)); + m_pMaxButton->Realize(); + } + + if (!m_pPosButton) { +- auto pButton = pdfium::MakeUnique(scp, CloneAttachedData(), +- m_sbType, PSBT_POS); ++ auto pButton = std::make_unique( ++ scp, CloneAttachedData(), CPWL_SBButton::Type::kPosButton); + m_pPosButton = pButton.get(); + ObservedPtr thisObserved(this); + if (m_pPosButton->SetVisible(false) && thisObserved) { +@@ -566,10 +322,7 @@ void CPWL_ScrollBar::CreateButtons(const CreateParams& cp) { + } + + float CPWL_ScrollBar::GetScrollBarWidth() const { +- if (!IsVisible()) +- return 0; +- +- return PWL_SCROLLBAR_WIDTH; ++ return IsVisible() ? kWidth : 0.0f; + } + + void CPWL_ScrollBar::SetScrollRange(float fMin, +@@ -582,7 +335,7 @@ void CPWL_ScrollBar::SetScrollRange(float fMin, + m_sData.SetScrollRange(fMin, fMax); + m_sData.SetClientWidth(fClientWidth); + +- if (IsFloatSmaller(m_sData.ScrollRange.GetWidth(), 0.0f)) { ++ if (FXSYS_IsFloatSmaller(m_sData.ScrollRange.GetWidth(), 0.0f)) { + m_pPosButton->SetVisible(false); + // Note, |this| may no longer be viable at this point. If more work needs + // to be done, check thisObserved. +@@ -600,7 +353,7 @@ void CPWL_ScrollBar::SetScrollRange(float fMin, + void CPWL_ScrollBar::SetScrollPos(float fPos) { + float fOldPos = m_sData.fScrollPos; + m_sData.SetPos(fPos); +- if (!IsFloatEqual(m_sData.fScrollPos, fOldPos)) { ++ if (!FXSYS_IsFloatEqual(m_sData.fScrollPos, fOldPos)) { + MovePosButton(true); + // Note, |this| may no longer be viable at this point. If more work needs + // to be done, check the return value of MovePosButton(). +@@ -613,53 +366,25 @@ void CPWL_ScrollBar::SetScrollStep(float fBigStep, float fSmallStep) { + } + + bool CPWL_ScrollBar::MovePosButton(bool bRefresh) { +- ASSERT(m_pMinButton); +- ASSERT(m_pMaxButton); ++ DCHECK(m_pMinButton); ++ DCHECK(m_pMaxButton); + + if (m_pPosButton->IsVisible()) { +- CFX_FloatRect rcClient; +- CFX_FloatRect rcPosArea, rcPosButton; +- +- rcClient = GetClientRect(); +- rcPosArea = GetScrollArea(); +- +- float fLeft, fRight, fTop, fBottom; +- +- switch (m_sbType) { +- case SBT_HSCROLL: +- fLeft = TrueToFace(m_sData.fScrollPos); +- fRight = TrueToFace(m_sData.fScrollPos + m_sData.fClientWidth); +- +- if (fRight - fLeft < kPosButtonMinWidth) +- fRight = fLeft + kPosButtonMinWidth; +- +- if (fRight > rcPosArea.right) { +- fRight = rcPosArea.right; +- fLeft = fRight - kPosButtonMinWidth; +- } +- +- rcPosButton = +- CFX_FloatRect(fLeft, rcPosArea.bottom, fRight, rcPosArea.top); ++ CFX_FloatRect rcPosArea = GetScrollArea(); ++ float fBottom = TrueToFace(m_sData.fScrollPos + m_sData.fClientWidth); ++ float fTop = TrueToFace(m_sData.fScrollPos); + +- break; +- case SBT_VSCROLL: +- fBottom = TrueToFace(m_sData.fScrollPos + m_sData.fClientWidth); +- fTop = TrueToFace(m_sData.fScrollPos); ++ if (FXSYS_IsFloatSmaller(fTop - fBottom, kPosButtonMinWidth)) ++ fBottom = fTop - kPosButtonMinWidth; + +- if (IsFloatSmaller(fTop - fBottom, kPosButtonMinWidth)) +- fBottom = fTop - kPosButtonMinWidth; +- +- if (IsFloatSmaller(fBottom, rcPosArea.bottom)) { +- fBottom = rcPosArea.bottom; +- fTop = fBottom + kPosButtonMinWidth; +- } +- +- rcPosButton = +- CFX_FloatRect(rcPosArea.left, fBottom, rcPosArea.right, fTop); +- +- break; ++ if (FXSYS_IsFloatSmaller(fBottom, rcPosArea.bottom)) { ++ fBottom = rcPosArea.bottom; ++ fTop = fBottom + kPosButtonMinWidth; + } + ++ CFX_FloatRect rcPosButton = ++ CFX_FloatRect(rcPosArea.left, fBottom, rcPosArea.right, fTop); ++ + ObservedPtr thisObserved(this); + m_pPosButton->Move(rcPosButton, true, bRefresh); + if (!thisObserved) +@@ -676,7 +401,7 @@ void CPWL_ScrollBar::OnMinButtonLBDown(const CFX_PointF& point) { + + NotifyScrollWindow(); + m_bMinOrMax = true; +- m_pTimer = pdfium::MakeUnique(GetTimerHandler(), this, 100); ++ m_pTimer = std::make_unique(GetTimerHandler(), this, 100); + } + + void CPWL_ScrollBar::OnMinButtonLBUp(const CFX_PointF& point) {} +@@ -690,7 +415,7 @@ void CPWL_ScrollBar::OnMaxButtonLBDown(const CFX_PointF& point) { + + NotifyScrollWindow(); + m_bMinOrMax = false; +- m_pTimer = pdfium::MakeUnique(GetTimerHandler(), this, 100); ++ m_pTimer = std::make_unique(GetTimerHandler(), this, 100); + } + + void CPWL_ScrollBar::OnMaxButtonLBUp(const CFX_PointF& point) {} +@@ -702,89 +427,44 @@ void CPWL_ScrollBar::OnPosButtonLBDown(const CFX_PointF& point) { + + if (m_pPosButton) { + CFX_FloatRect rcPosButton = m_pPosButton->GetWindowRect(); +- +- switch (m_sbType) { +- case SBT_HSCROLL: +- m_nOldPos = point.x; +- m_fOldPosButton = rcPosButton.left; +- break; +- case SBT_VSCROLL: +- m_nOldPos = point.y; +- m_fOldPosButton = rcPosButton.top; +- break; +- } ++ m_nOldPos = point.y; ++ m_fOldPosButton = rcPosButton.top; + } + } + + void CPWL_ScrollBar::OnPosButtonLBUp(const CFX_PointF& point) { +- if (m_bMouseDown) { +- if (!m_bNotifyForever) +- NotifyScrollWindow(); +- } + m_bMouseDown = false; + } + + void CPWL_ScrollBar::OnPosButtonMouseMove(const CFX_PointF& point) { +- float fOldScrollPos = m_sData.fScrollPos; +- +- float fNewPos = 0; +- +- switch (m_sbType) { +- case SBT_HSCROLL: +- if (fabs(point.x - m_nOldPos) < 1) +- return; +- fNewPos = FaceToTrue(m_fOldPosButton + point.x - m_nOldPos); +- break; +- case SBT_VSCROLL: +- if (fabs(point.y - m_nOldPos) < 1) +- return; +- fNewPos = FaceToTrue(m_fOldPosButton + point.y - m_nOldPos); +- break; +- } ++ if (fabs(point.y - m_nOldPos) < 1) ++ return; + ++ float fOldScrollPos = m_sData.fScrollPos; ++ float fNewPos = FaceToTrue(m_fOldPosButton + point.y - m_nOldPos); + if (m_bMouseDown) { +- switch (m_sbType) { +- case SBT_HSCROLL: +- +- if (IsFloatSmaller(fNewPos, m_sData.ScrollRange.fMin)) { +- fNewPos = m_sData.ScrollRange.fMin; +- } +- +- if (IsFloatBigger(fNewPos, m_sData.ScrollRange.fMax)) { +- fNewPos = m_sData.ScrollRange.fMax; +- } +- +- m_sData.SetPos(fNewPos); +- +- break; +- case SBT_VSCROLL: +- +- if (IsFloatSmaller(fNewPos, m_sData.ScrollRange.fMin)) { +- fNewPos = m_sData.ScrollRange.fMin; +- } +- +- if (IsFloatBigger(fNewPos, m_sData.ScrollRange.fMax)) { +- fNewPos = m_sData.ScrollRange.fMax; +- } +- +- m_sData.SetPos(fNewPos); ++ if (FXSYS_IsFloatSmaller(fNewPos, m_sData.ScrollRange.fMin)) { ++ fNewPos = m_sData.ScrollRange.fMin; ++ } + +- break; ++ if (FXSYS_IsFloatBigger(fNewPos, m_sData.ScrollRange.fMax)) { ++ fNewPos = m_sData.ScrollRange.fMax; + } + +- if (!IsFloatEqual(fOldScrollPos, m_sData.fScrollPos)) { ++ m_sData.SetPos(fNewPos); ++ ++ if (!FXSYS_IsFloatEqual(fOldScrollPos, m_sData.fScrollPos)) { + if (!MovePosButton(true)) + return; + +- if (m_bNotifyForever) +- NotifyScrollWindow(); ++ NotifyScrollWindow(); + } + } + } + + void CPWL_ScrollBar::NotifyScrollWindow() { + CPWL_Wnd* pParent = GetParentWindow(); +- if (!pParent || m_sbType != SBT_VSCROLL) ++ if (!pParent) + return; + + pParent->ScrollWindowVertically(m_OriginInfo.fContentMax - +@@ -793,90 +473,41 @@ void CPWL_ScrollBar::NotifyScrollWindow() { + + CFX_FloatRect CPWL_ScrollBar::GetScrollArea() const { + CFX_FloatRect rcClient = GetClientRect(); +- CFX_FloatRect rcArea; +- + if (!m_pMinButton || !m_pMaxButton) + return rcClient; + + CFX_FloatRect rcMin = m_pMinButton->GetWindowRect(); + CFX_FloatRect rcMax = m_pMaxButton->GetWindowRect(); +- +- float fMinWidth = rcMin.Width(); + float fMinHeight = rcMin.Height(); +- float fMaxWidth = rcMax.Width(); + float fMaxHeight = rcMax.Height(); + +- switch (m_sbType) { +- case SBT_HSCROLL: +- if (rcClient.right - rcClient.left > fMinWidth + fMaxWidth + 2) { +- rcArea = CFX_FloatRect(rcClient.left + fMinWidth + 1, rcClient.bottom, +- rcClient.right - fMaxWidth - 1, rcClient.top); +- } else { +- rcArea = CFX_FloatRect(rcClient.left + fMinWidth + 1, rcClient.bottom, +- rcClient.left + fMinWidth + 1, rcClient.top); +- } +- break; +- case SBT_VSCROLL: +- if (rcClient.top - rcClient.bottom > fMinHeight + fMaxHeight + 2) { +- rcArea = CFX_FloatRect(rcClient.left, rcClient.bottom + fMinHeight + 1, +- rcClient.right, rcClient.top - fMaxHeight - 1); +- } else { +- rcArea = +- CFX_FloatRect(rcClient.left, rcClient.bottom + fMinHeight + 1, +- rcClient.right, rcClient.bottom + fMinHeight + 1); +- } +- break; ++ CFX_FloatRect rcArea; ++ if (rcClient.top - rcClient.bottom > fMinHeight + fMaxHeight + 2) { ++ rcArea = CFX_FloatRect(rcClient.left, rcClient.bottom + fMinHeight + 1, ++ rcClient.right, rcClient.top - fMaxHeight - 1); ++ } else { ++ rcArea = CFX_FloatRect(rcClient.left, rcClient.bottom + fMinHeight + 1, ++ rcClient.right, rcClient.bottom + fMinHeight + 1); + } + + rcArea.Normalize(); +- + return rcArea; + } + + float CPWL_ScrollBar::TrueToFace(float fTrue) { +- CFX_FloatRect rcPosArea; +- rcPosArea = GetScrollArea(); +- ++ CFX_FloatRect rcPosArea = GetScrollArea(); + float fFactWidth = m_sData.ScrollRange.GetWidth() + m_sData.fClientWidth; + fFactWidth = fFactWidth == 0 ? 1 : fFactWidth; +- +- float fFace = 0; +- +- switch (m_sbType) { +- case SBT_HSCROLL: +- fFace = rcPosArea.left + +- fTrue * (rcPosArea.right - rcPosArea.left) / fFactWidth; +- break; +- case SBT_VSCROLL: +- fFace = rcPosArea.top - +- fTrue * (rcPosArea.top - rcPosArea.bottom) / fFactWidth; +- break; +- } +- +- return fFace; ++ return rcPosArea.top - ++ fTrue * (rcPosArea.top - rcPosArea.bottom) / fFactWidth; + } + + float CPWL_ScrollBar::FaceToTrue(float fFace) { +- CFX_FloatRect rcPosArea; +- rcPosArea = GetScrollArea(); +- ++ CFX_FloatRect rcPosArea = GetScrollArea(); + float fFactWidth = m_sData.ScrollRange.GetWidth() + m_sData.fClientWidth; + fFactWidth = fFactWidth == 0 ? 1 : fFactWidth; +- +- float fTrue = 0; +- +- switch (m_sbType) { +- case SBT_HSCROLL: +- fTrue = (fFace - rcPosArea.left) * fFactWidth / +- (rcPosArea.right - rcPosArea.left); +- break; +- case SBT_VSCROLL: +- fTrue = (rcPosArea.top - fFace) * fFactWidth / +- (rcPosArea.top - rcPosArea.bottom); +- break; +- } +- +- return fTrue; ++ return (rcPosArea.top - fFace) * fFactWidth / ++ (rcPosArea.top - rcPosArea.bottom); + } + + void CPWL_ScrollBar::CreateChildWnd(const CreateParams& cp) { +diff --git a/fpdfsdk/pwl/cpwl_scroll_bar.h b/fpdfsdk/pwl/cpwl_scroll_bar.h +index f6bb2b923..6cca8fba5 100644 +--- a/fpdfsdk/pwl/cpwl_scroll_bar.h ++++ b/fpdfsdk/pwl/cpwl_scroll_bar.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,6 +11,7 @@ + + #include "core/fxcrt/cfx_timer.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "fpdfsdk/pwl/cpwl_sbbutton.h" + #include "fpdfsdk/pwl/cpwl_wnd.h" + + struct PWL_SCROLL_INFO { +@@ -38,32 +39,6 @@ struct PWL_SCROLL_INFO { + float fSmallStep; + }; + +-enum PWL_SCROLLBAR_TYPE { SBT_HSCROLL, SBT_VSCROLL }; +- +-enum PWL_SBBUTTON_TYPE { PSBT_MIN, PSBT_MAX, PSBT_POS }; +- +-class CPWL_SBButton final : public CPWL_Wnd { +- public: +- CPWL_SBButton( +- const CreateParams& cp, +- std::unique_ptr pAttachedData, +- PWL_SCROLLBAR_TYPE eScrollBarType, +- PWL_SBBUTTON_TYPE eButtonType); +- ~CPWL_SBButton() override; +- +- // CPWL_Wnd +- void DrawThisAppearance(CFX_RenderDevice* pDevice, +- const CFX_Matrix& mtUser2Device) override; +- bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override; +- bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; +- bool OnMouseMove(const CFX_PointF& point, uint32_t nFlag) override; +- +- private: +- PWL_SCROLLBAR_TYPE m_eScrollBarType; +- PWL_SBBUTTON_TYPE m_eSBButtonType; +- bool m_bMouseDown = false; +-}; +- + struct PWL_FLOATRANGE { + public: + PWL_FLOATRANGE() = default; +@@ -116,10 +91,12 @@ struct PWL_SCROLL_PRIVATEDATA { + + class CPWL_ScrollBar final : public CPWL_Wnd, public CFX_Timer::CallbackIface { + public: ++ static constexpr float kWidth = 12.0f; ++ static constexpr uint8_t kTransparency = 150; ++ + CPWL_ScrollBar( + const CreateParams& cp, +- std::unique_ptr pAttachedData, +- PWL_SCROLLBAR_TYPE sbType); ++ std::unique_ptr pAttachedData); + ~CPWL_ScrollBar() override; + + // CPWL_Wnd: +@@ -127,8 +104,9 @@ class CPWL_ScrollBar final : public CPWL_Wnd, public CFX_Timer::CallbackIface { + bool RePosChildWnd() override; + void DrawThisAppearance(CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device) override; +- bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override; +- bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; ++ bool OnLButtonDown(Mask nFlag, ++ const CFX_PointF& point) override; ++ bool OnLButtonUp(Mask nFlag, const CFX_PointF& point) override; + void SetScrollInfo(const PWL_SCROLL_INFO& info) override; + void SetScrollPosition(float pos) override; + void NotifyLButtonDown(CPWL_Wnd* child, const CFX_PointF& pos) override; +@@ -140,9 +118,6 @@ class CPWL_ScrollBar final : public CPWL_Wnd, public CFX_Timer::CallbackIface { + void OnTimerFired() override; + + float GetScrollBarWidth() const; +- PWL_SCROLLBAR_TYPE GetScrollBarType() const { return m_sbType; } +- +- void SetNotifyForever(bool bForever) { m_bNotifyForever = bForever; } + + private: + void SetScrollRange(float fMin, float fMax, float fClientWidth); +@@ -171,7 +146,6 @@ class CPWL_ScrollBar final : public CPWL_Wnd, public CFX_Timer::CallbackIface { + float TrueToFace(float); + float FaceToTrue(float); + +- PWL_SCROLLBAR_TYPE m_sbType; + PWL_SCROLL_INFO m_OriginInfo; + UnownedPtr m_pMinButton; + UnownedPtr m_pMaxButton; +@@ -180,7 +154,6 @@ class CPWL_ScrollBar final : public CPWL_Wnd, public CFX_Timer::CallbackIface { + PWL_SCROLL_PRIVATEDATA m_sData; + bool m_bMouseDown = false; + bool m_bMinOrMax = false; +- bool m_bNotifyForever = true; + float m_nOldPos = 0.0f; + float m_fOldPosButton = 0.0f; + }; +diff --git a/fpdfsdk/pwl/cpwl_special_button.cpp b/fpdfsdk/pwl/cpwl_special_button.cpp +index 3fbf9193d..17f209acc 100644 +--- a/fpdfsdk/pwl/cpwl_special_button.cpp ++++ b/fpdfsdk/pwl/cpwl_special_button.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,7 +13,7 @@ + + CPWL_PushButton::CPWL_PushButton( + const CreateParams& cp, +- std::unique_ptr pAttachedData) ++ std::unique_ptr pAttachedData) + : CPWL_Button(cp, std::move(pAttachedData)) {} + + CPWL_PushButton::~CPWL_PushButton() = default; +@@ -25,12 +25,13 @@ CFX_FloatRect CPWL_PushButton::GetFocusRect() const { + + CPWL_CheckBox::CPWL_CheckBox( + const CreateParams& cp, +- std::unique_ptr pAttachedData) ++ std::unique_ptr pAttachedData) + : CPWL_Button(cp, std::move(pAttachedData)) {} + + CPWL_CheckBox::~CPWL_CheckBox() = default; + +-bool CPWL_CheckBox::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { ++bool CPWL_CheckBox::OnLButtonUp(Mask nFlag, ++ const CFX_PointF& point) { + if (IsReadOnly()) + return false; + +@@ -38,19 +39,23 @@ bool CPWL_CheckBox::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { + return true; + } + +-bool CPWL_CheckBox::OnChar(uint16_t nChar, uint32_t nFlag) { ++bool CPWL_CheckBox::OnChar(uint16_t nChar, Mask nFlag) { ++ if (IsReadOnly()) ++ return false; ++ + SetCheck(!IsChecked()); + return true; + } + + CPWL_RadioButton::CPWL_RadioButton( + const CreateParams& cp, +- std::unique_ptr pAttachedData) ++ std::unique_ptr pAttachedData) + : CPWL_Button(cp, std::move(pAttachedData)) {} + + CPWL_RadioButton::~CPWL_RadioButton() = default; + +-bool CPWL_RadioButton::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { ++bool CPWL_RadioButton::OnLButtonUp(Mask nFlag, ++ const CFX_PointF& point) { + if (IsReadOnly()) + return false; + +@@ -58,7 +63,10 @@ bool CPWL_RadioButton::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { + return true; + } + +-bool CPWL_RadioButton::OnChar(uint16_t nChar, uint32_t nFlag) { ++bool CPWL_RadioButton::OnChar(uint16_t nChar, Mask nFlag) { ++ if (IsReadOnly()) ++ return false; ++ + SetCheck(true); + return true; + } +diff --git a/fpdfsdk/pwl/cpwl_special_button.h b/fpdfsdk/pwl/cpwl_special_button.h +index 3d9fa254e..8720ef11d 100644 +--- a/fpdfsdk/pwl/cpwl_special_button.h ++++ b/fpdfsdk/pwl/cpwl_special_button.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -15,7 +15,7 @@ class CPWL_PushButton final : public CPWL_Button { + public: + CPWL_PushButton( + const CreateParams& cp, +- std::unique_ptr pAttachedData); ++ std::unique_ptr pAttachedData); + ~CPWL_PushButton() override; + + // CPWL_Button: +@@ -26,12 +26,12 @@ class CPWL_CheckBox final : public CPWL_Button { + public: + CPWL_CheckBox( + const CreateParams& cp, +- std::unique_ptr pAttachedData); ++ std::unique_ptr pAttachedData); + ~CPWL_CheckBox() override; + + // CPWL_Button: +- bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; +- bool OnChar(uint16_t nChar, uint32_t nFlag) override; ++ bool OnLButtonUp(Mask nFlag, const CFX_PointF& point) override; ++ bool OnChar(uint16_t nChar, Mask nFlag) override; + + bool IsChecked() const { return m_bChecked; } + void SetCheck(bool bCheck) { m_bChecked = bCheck; } +@@ -44,12 +44,12 @@ class CPWL_RadioButton final : public CPWL_Button { + public: + CPWL_RadioButton( + const CreateParams& cp, +- std::unique_ptr pAttachedData); ++ std::unique_ptr pAttachedData); + ~CPWL_RadioButton() override; + + // CPWL_Button +- bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; +- bool OnChar(uint16_t nChar, uint32_t nFlag) override; ++ bool OnLButtonUp(Mask nFlag, const CFX_PointF& point) override; ++ bool OnChar(uint16_t nChar, Mask nFlag) override; + + bool IsChecked() const { return m_bChecked; } + void SetCheck(bool bCheck) { m_bChecked = bCheck; } +diff --git a/fpdfsdk/pwl/cpwl_special_button_embeddertest.cpp b/fpdfsdk/pwl/cpwl_special_button_embeddertest.cpp +new file mode 100644 +index 000000000..c12f0370b +--- /dev/null ++++ b/fpdfsdk/pwl/cpwl_special_button_embeddertest.cpp +@@ -0,0 +1,147 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "fpdfsdk/cpdfsdk_annotiterator.h" ++#include "fpdfsdk/cpdfsdk_formfillenvironment.h" ++#include "fpdfsdk/cpdfsdk_helpers.h" ++#include "fpdfsdk/cpdfsdk_widget.h" ++#include "fpdfsdk/formfiller/cffl_formfield.h" ++#include "fpdfsdk/pwl/cpwl_special_button.h" ++#include "fpdfsdk/pwl/cpwl_wnd.h" ++#include "testing/embedder_test.h" ++ ++class CPWLSpecialButtonEmbedderTest : public EmbedderTest { ++ protected: ++ void SetUp() override { ++ EmbedderTest::SetUp(); ++ CreateAndInitializeFormPDF(); ++ } ++ ++ void TearDown() override { ++ UnloadPage(page_); ++ EmbedderTest::TearDown(); ++ } ++ ++ void CreateAndInitializeFormPDF() { ++ ASSERT_TRUE(OpenDocument("click_form.pdf")); ++ ++ page_ = LoadPage(0); ++ ASSERT_TRUE(page_); ++ ++ formfill_env_ = CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle()); ++ CPDFSDK_AnnotIterator it(formfill_env_->GetPageViewAtIndex(0), ++ {CPDF_Annot::Subtype::WIDGET}); ++ ++ // Read only check box. ++ widget_readonly_checkbox_ = ToCPDFSDKWidget(it.GetFirstAnnot()); ++ ASSERT_TRUE(widget_readonly_checkbox_); ++ ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, ++ widget_readonly_checkbox_->GetAnnotSubtype()); ++ ++ // Check box. ++ widget_checkbox_ = ++ ToCPDFSDKWidget(it.GetNextAnnot(widget_readonly_checkbox_)); ++ ASSERT_TRUE(widget_checkbox_); ++ ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, widget_checkbox_->GetAnnotSubtype()); ++ ++ // Read only radio button. ++ widget_readonly_radiobutton_ = ++ ToCPDFSDKWidget(it.GetNextAnnot(widget_checkbox_)); ++ ASSERT_TRUE(widget_readonly_radiobutton_); ++ ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, ++ widget_readonly_radiobutton_->GetAnnotSubtype()); ++ ++ // Tabbing three times from read only radio button to unselected normal ++ // radio button. ++ widget_radiobutton_ = widget_readonly_radiobutton_; ++ ASSERT_TRUE(widget_radiobutton_); ++ for (int i = 0; i < 3; i++) { ++ widget_radiobutton_ = ++ ToCPDFSDKWidget(it.GetNextAnnot(widget_radiobutton_)); ++ ASSERT_TRUE(widget_radiobutton_); ++ } ++ ++ ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, ++ widget_radiobutton_->GetAnnotSubtype()); ++ } ++ ++ void FormFillerAndWindowSetup(CPDFSDK_Widget* widget) { ++ CFFL_InteractiveFormFiller* interactive_formfiller = ++ formfill_env_->GetInteractiveFormFiller(); ++ { ++ ObservedPtr observed(widget); ++ EXPECT_TRUE(interactive_formfiller->OnSetFocus(observed, {})); ++ } ++ ++ form_filler_ = interactive_formfiller->GetFormFieldForTesting(widget); ++ ASSERT_TRUE(form_filler_); ++ ++ window_ = form_filler_->CreateOrUpdatePWLWindow( ++ formfill_env_->GetPageViewAtIndex(0)); ++ ASSERT_TRUE(window_); ++ } ++ ++ CPDFSDK_Widget* GetCPDFSDKWidgetCheckBox() const { return widget_checkbox_; } ++ CPDFSDK_Widget* GetCPDFSDKWidgetReadOnlyCheckBox() const { ++ return widget_readonly_checkbox_; ++ } ++ CPDFSDK_Widget* GetCPDFSDKWidgetRadioButton() const { ++ return widget_radiobutton_; ++ } ++ CPDFSDK_Widget* GetCPDFSDKWidgetReadOnlyRadioButton() const { ++ return widget_readonly_radiobutton_; ++ } ++ CPDFSDK_FormFillEnvironment* GetCPDFSDKFormFillEnv() const { ++ return formfill_env_; ++ } ++ CPWL_Wnd* GetWindow() const { return window_; } ++ ++ private: ++ FPDF_PAGE page_; ++ CFFL_FormField* form_filler_; ++ CPDFSDK_Widget* widget_checkbox_; ++ CPDFSDK_Widget* widget_readonly_checkbox_; ++ CPDFSDK_Widget* widget_radiobutton_; ++ CPDFSDK_Widget* widget_readonly_radiobutton_; ++ CPDFSDK_FormFillEnvironment* formfill_env_; ++ CPWL_Wnd* window_; ++}; ++ ++TEST_F(CPWLSpecialButtonEmbedderTest, EnterOnReadOnlyCheckBox) { ++ FormFillerAndWindowSetup(GetCPDFSDKWidgetReadOnlyCheckBox()); ++ CPWL_CheckBox* check_box = static_cast(GetWindow()); ++ EXPECT_TRUE(check_box->IsChecked()); ++ EXPECT_TRUE(GetCPDFSDKFormFillEnv()->GetInteractiveFormFiller()->OnChar( ++ GetCPDFSDKWidgetReadOnlyCheckBox(), '\r', {})); ++ EXPECT_TRUE(check_box->IsChecked()); ++} ++ ++TEST_F(CPWLSpecialButtonEmbedderTest, EnterOnCheckBox) { ++ FormFillerAndWindowSetup(GetCPDFSDKWidgetCheckBox()); ++ CPWL_CheckBox* check_box = static_cast(GetWindow()); ++ EXPECT_TRUE(GetCPDFSDKFormFillEnv()->GetInteractiveFormFiller()->OnChar( ++ GetCPDFSDKWidgetCheckBox(), '\r', {})); ++ EXPECT_TRUE(check_box->IsChecked()); ++ ++ EXPECT_TRUE(GetCPDFSDKFormFillEnv()->GetInteractiveFormFiller()->OnChar( ++ GetCPDFSDKWidgetCheckBox(), '\r', {})); ++ EXPECT_FALSE(check_box->IsChecked()); ++} ++ ++TEST_F(CPWLSpecialButtonEmbedderTest, EnterOnReadOnlyRadioButton) { ++ FormFillerAndWindowSetup(GetCPDFSDKWidgetReadOnlyRadioButton()); ++ CPWL_RadioButton* radio_button = static_cast(GetWindow()); ++ EXPECT_FALSE(radio_button->IsChecked()); ++ EXPECT_TRUE(GetCPDFSDKFormFillEnv()->GetInteractiveFormFiller()->OnChar( ++ GetCPDFSDKWidgetReadOnlyRadioButton(), '\r', {})); ++ EXPECT_FALSE(radio_button->IsChecked()); ++} ++ ++TEST_F(CPWLSpecialButtonEmbedderTest, EnterOnRadioButton) { ++ FormFillerAndWindowSetup(GetCPDFSDKWidgetRadioButton()); ++ CPWL_RadioButton* radio_button = static_cast(GetWindow()); ++ EXPECT_TRUE(GetCPDFSDKFormFillEnv()->GetInteractiveFormFiller()->OnChar( ++ GetCPDFSDKWidgetRadioButton(), '\r', {})); ++ EXPECT_TRUE(radio_button->IsChecked()); ++} +diff --git a/fpdfsdk/pwl/cpwl_wnd.cpp b/fpdfsdk/pwl/cpwl_wnd.cpp +index 0ba0e56c6..755fcbb65 100644 +--- a/fpdfsdk/pwl/cpwl_wnd.cpp ++++ b/fpdfsdk/pwl/cpwl_wnd.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,16 +6,18 @@ + + #include "fpdfsdk/pwl/cpwl_wnd.h" + +-#include + #include + #include + #include + ++#include "build/build_config.h" ++#include "core/fxcrt/stl_util.h" + #include "core/fxge/cfx_renderdevice.h" + #include "fpdfsdk/pwl/cpwl_scroll_bar.h" + #include "public/fpdf_fwlevent.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/containers/contains.h" + + namespace { + +@@ -23,8 +25,22 @@ constexpr float kDefaultFontSize = 9.0f; + + } // namespace + +-CPWL_Wnd::CreateParams::CreateParams() +- : fFontSize(kDefaultFontSize), sDash(3, 0, 0) {} ++// static ++const CFX_Color CPWL_Wnd::kDefaultBlackColor = ++ CFX_Color(CFX_Color::Type::kGray, 0); ++ ++// static ++const CFX_Color CPWL_Wnd::kDefaultWhiteColor = ++ CFX_Color(CFX_Color::Type::kGray, 1); ++ ++CPWL_Wnd::CreateParams::CreateParams(CFX_Timer::HandlerIface* timer_handler, ++ IPWL_FillerNotify* filler_notify, ++ ProviderIface* provider) ++ : pTimerHandler(timer_handler), ++ pFillerNotify(filler_notify), ++ pProvider(provider), ++ fFontSize(kDefaultFontSize), ++ sDash(3, 0, 0) {} + + CPWL_Wnd::CreateParams::CreateParams(const CreateParams& other) = default; + +@@ -32,15 +48,15 @@ CPWL_Wnd::CreateParams::~CreateParams() = default; + + class CPWL_MsgControl final : public Observable { + public: +- explicit CPWL_MsgControl(CPWL_Wnd* pWnd) : m_pCreatedWnd(pWnd) {} +- ~CPWL_MsgControl() {} ++ explicit CPWL_MsgControl(const CPWL_Wnd* pWnd) : m_pCreatedWnd(pWnd) {} ++ ~CPWL_MsgControl() = default; + + bool IsWndCreated(const CPWL_Wnd* pWnd) const { + return m_pCreatedWnd == pWnd; + } + + bool IsWndCaptureMouse(const CPWL_Wnd* pWnd) const { +- return pWnd && pdfium::ContainsValue(m_aMousePath, pWnd); ++ return pWnd && pdfium::Contains(m_MousePaths, pWnd); + } + + bool IsMainCaptureKeyboard(const CPWL_Wnd* pWnd) const { +@@ -48,84 +64,82 @@ class CPWL_MsgControl final : public Observable { + } + + bool IsWndCaptureKeyboard(const CPWL_Wnd* pWnd) const { +- return pWnd && pdfium::ContainsValue(m_aKeyboardPath, pWnd); ++ return pWnd && pdfium::Contains(m_KeyboardPaths, pWnd); + } + + void SetFocus(CPWL_Wnd* pWnd) { +- m_aKeyboardPath.clear(); +- if (!pWnd) +- return; +- ++ m_KeyboardPaths = pWnd->GetAncestors(); + m_pMainKeyboardWnd = pWnd; +- CPWL_Wnd* pParent = pWnd; +- while (pParent) { +- m_aKeyboardPath.push_back(pParent); +- pParent = pParent->GetParentWindow(); +- } ++ + // Note, pWnd may get destroyed in the OnSetFocus call. + pWnd->OnSetFocus(); + } + + void KillFocus() { + ObservedPtr observed_ptr(this); +- if (!m_aKeyboardPath.empty()) +- if (CPWL_Wnd* pWnd = m_aKeyboardPath[0]) ++ if (!m_KeyboardPaths.empty()) { ++ CPWL_Wnd* pWnd = m_KeyboardPaths.front(); ++ if (pWnd) + pWnd->OnKillFocus(); ++ } + if (!observed_ptr) + return; + + m_pMainKeyboardWnd = nullptr; +- m_aKeyboardPath.clear(); +- } +- +- void SetCapture(CPWL_Wnd* pWnd) { +- m_aMousePath.clear(); +- if (pWnd) { +- CPWL_Wnd* pParent = pWnd; +- while (pParent) { +- m_aMousePath.push_back(pParent); +- pParent = pParent->GetParentWindow(); +- } +- } ++ m_KeyboardPaths.clear(); + } + +- void ReleaseCapture() { m_aMousePath.clear(); } ++ void SetCapture(CPWL_Wnd* pWnd) { m_MousePaths = pWnd->GetAncestors(); } + +- CPWL_Wnd* GetFocusedWindow() const { return m_pMainKeyboardWnd.Get(); } ++ void ReleaseCapture() { m_MousePaths.clear(); } + + private: +- std::vector m_aMousePath; +- std::vector m_aKeyboardPath; +- UnownedPtr m_pCreatedWnd; +- UnownedPtr m_pMainKeyboardWnd; ++ std::vector> m_MousePaths; ++ std::vector> m_KeyboardPaths; ++ UnownedPtr m_pCreatedWnd; ++ UnownedPtr m_pMainKeyboardWnd; + }; + + // static +-bool CPWL_Wnd::IsSHIFTKeyDown(uint32_t nFlag) { ++bool CPWL_Wnd::IsSHIFTKeyDown(Mask nFlag) { + return !!(nFlag & FWL_EVENTFLAG_ShiftKey); + } + + // static +-bool CPWL_Wnd::IsCTRLKeyDown(uint32_t nFlag) { ++bool CPWL_Wnd::IsCTRLKeyDown(Mask nFlag) { + return !!(nFlag & FWL_EVENTFLAG_ControlKey); + } + + // static +-bool CPWL_Wnd::IsALTKeyDown(uint32_t nFlag) { ++bool CPWL_Wnd::IsALTKeyDown(Mask nFlag) { + return !!(nFlag & FWL_EVENTFLAG_AltKey); + } + ++// static ++bool CPWL_Wnd::IsMETAKeyDown(Mask nFlag) { ++ return !!(nFlag & FWL_EVENTFLAG_MetaKey); ++} ++ ++// static ++bool CPWL_Wnd::IsPlatformShortcutKey(Mask nFlag) { ++#if BUILDFLAG(IS_APPLE) ++ return IsMETAKeyDown(nFlag); ++#else ++ return IsCTRLKeyDown(nFlag); ++#endif ++} ++ + CPWL_Wnd::CPWL_Wnd( + const CreateParams& cp, +- std::unique_ptr pAttachedData) ++ std::unique_ptr pAttachedData) + : m_CreationParams(cp), m_pAttachedData(std::move(pAttachedData)) {} + + CPWL_Wnd::~CPWL_Wnd() { +- ASSERT(!m_bCreated); ++ DCHECK(!m_bCreated); + } + + void CPWL_Wnd::Realize() { +- ASSERT(!m_bCreated); ++ DCHECK(!m_bCreated); + + m_CreationParams.rcRectWnd.Normalize(); + m_rcWindow = m_CreationParams.rcRectWnd; +@@ -152,11 +166,6 @@ void CPWL_Wnd::OnCreated() {} + + void CPWL_Wnd::OnDestroy() {} + +-void CPWL_Wnd::InvalidateFocusHandler(FocusHandlerIface* handler) { +- if (m_CreationParams.pFocusHandler == handler) +- m_CreationParams.pFocusHandler = nullptr; +-} +- + void CPWL_Wnd::InvalidateProvider(ProviderIface* provider) { + if (m_CreationParams.pProvider.Get() == provider) + m_CreationParams.pProvider.Reset(); +@@ -241,17 +250,11 @@ void CPWL_Wnd::DrawThisAppearance(CFX_RenderDevice* pDevice, + void CPWL_Wnd::DrawChildAppearance(CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device) { + for (const auto& pChild : m_Children) { +- CFX_Matrix mt = pChild->GetChildMatrix(); +- if (mt.IsIdentity()) { +- pChild->DrawAppearance(pDevice, mtUser2Device); +- continue; +- } +- mt.Concat(mtUser2Device); +- pChild->DrawAppearance(pDevice, mt); ++ pChild->DrawAppearance(pDevice, mtUser2Device); + } + } + +-bool CPWL_Wnd::InvalidateRect(CFX_FloatRect* pRect) { ++bool CPWL_Wnd::InvalidateRect(const CFX_FloatRect* pRect) { + if (!IsValid()) + return true; + +@@ -266,49 +269,56 @@ bool CPWL_Wnd::InvalidateRect(CFX_FloatRect* pRect) { + CFX_FloatRect rcWin = PWLtoWnd(rcRefresh); + rcWin.Inflate(1, 1); + rcWin.Normalize(); +- GetSystemHandler()->InvalidateRect(m_pAttachedData.get(), rcWin); ++ GetFillerNotify()->InvalidateRect(m_pAttachedData.get(), rcWin); + return !!thisObserved; + } + +-#define PWL_IMPLEMENT_KEY_METHOD(key_method_name) \ +- bool CPWL_Wnd::key_method_name(uint16_t nChar, uint32_t nFlag) { \ +- if (!IsValid() || !IsVisible() || !IsEnabled()) \ +- return false; \ +- if (!IsWndCaptureKeyboard(this)) \ +- return false; \ +- for (const auto& pChild : m_Children) { \ +- if (IsWndCaptureKeyboard(pChild.get())) \ +- return pChild->key_method_name(nChar, nFlag); \ +- } \ +- return false; \ ++bool CPWL_Wnd::OnKeyDown(FWL_VKEYCODE nKeyCode, Mask nFlag) { ++ if (!IsValid() || !IsVisible()) ++ return false; ++ if (!IsWndCaptureKeyboard(this)) ++ return false; ++ for (const auto& pChild : m_Children) { ++ if (IsWndCaptureKeyboard(pChild.get())) ++ return pChild->OnKeyDown(nKeyCode, nFlag); + } ++ return false; ++} + +-PWL_IMPLEMENT_KEY_METHOD(OnKeyDown) +-PWL_IMPLEMENT_KEY_METHOD(OnChar) +-#undef PWL_IMPLEMENT_KEY_METHOD +- +-#define PWL_IMPLEMENT_MOUSE_METHOD(mouse_method_name) \ +- bool CPWL_Wnd::mouse_method_name(const CFX_PointF& point, uint32_t nFlag) { \ +- if (!IsValid() || !IsVisible() || !IsEnabled()) \ +- return false; \ +- if (IsWndCaptureMouse(this)) { \ +- for (const auto& pChild : m_Children) { \ +- if (IsWndCaptureMouse(pChild.get())) { \ +- return pChild->mouse_method_name(pChild->ParentToChild(point), \ +- nFlag); \ +- } \ +- } \ +- SetCursor(); \ +- return false; \ +- } \ +- for (const auto& pChild : m_Children) { \ +- if (pChild->WndHitTest(pChild->ParentToChild(point))) { \ +- return pChild->mouse_method_name(pChild->ParentToChild(point), nFlag); \ +- } \ +- } \ +- if (WndHitTest(point)) \ +- SetCursor(); \ +- return false; \ ++bool CPWL_Wnd::OnChar(uint16_t nChar, Mask nFlag) { ++ if (!IsValid() || !IsVisible()) ++ return false; ++ if (!IsWndCaptureKeyboard(this)) ++ return false; ++ for (const auto& pChild : m_Children) { ++ if (IsWndCaptureKeyboard(pChild.get())) ++ return pChild->OnChar(nChar, nFlag); ++ } ++ return false; ++} ++ ++#define PWL_IMPLEMENT_MOUSE_METHOD(mouse_method_name) \ ++ bool CPWL_Wnd::mouse_method_name(Mask nFlag, \ ++ const CFX_PointF& point) { \ ++ if (!IsValid() || !IsVisible()) \ ++ return false; \ ++ if (IsWndCaptureMouse(this)) { \ ++ for (const auto& pChild : m_Children) { \ ++ if (IsWndCaptureMouse(pChild.get())) { \ ++ return pChild->mouse_method_name(nFlag, point); \ ++ } \ ++ } \ ++ SetCursor(); \ ++ return false; \ ++ } \ ++ for (const auto& pChild : m_Children) { \ ++ if (pChild->WndHitTest(point)) { \ ++ return pChild->mouse_method_name(nFlag, point); \ ++ } \ ++ } \ ++ if (WndHitTest(point)) \ ++ SetCursor(); \ ++ return false; \ + } + + PWL_IMPLEMENT_MOUSE_METHOD(OnLButtonDblClk) +@@ -318,11 +328,12 @@ PWL_IMPLEMENT_MOUSE_METHOD(OnMouseMove) + #undef PWL_IMPLEMENT_MOUSE_METHOD + + // Unlike their FWL counterparts, PWL windows don't handle right clicks. +-bool CPWL_Wnd::OnRButtonDown(const CFX_PointF& point, uint32_t nFlag) { ++bool CPWL_Wnd::OnRButtonDown(Mask nFlag, ++ const CFX_PointF& point) { + return false; + } + +-bool CPWL_Wnd::OnRButtonUp(const CFX_PointF& point, uint32_t nFlag) { ++bool CPWL_Wnd::OnRButtonUp(Mask nFlag, const CFX_PointF& point) { + return false; + } + +@@ -334,8 +345,14 @@ WideString CPWL_Wnd::GetSelectedText() { + return WideString(); + } + ++void CPWL_Wnd::ReplaceAndKeepSelection(const WideString& text) {} ++ + void CPWL_Wnd::ReplaceSelection(const WideString& text) {} + ++bool CPWL_Wnd::SelectAllText() { ++ return false; ++} ++ + bool CPWL_Wnd::CanUndo() { + return false; + } +@@ -352,10 +369,10 @@ bool CPWL_Wnd::Redo() { + return false; + } + +-bool CPWL_Wnd::OnMouseWheel(short zDelta, ++bool CPWL_Wnd::OnMouseWheel(Mask nFlag, + const CFX_PointF& point, +- uint32_t nFlag) { +- if (!IsValid() || !IsVisible() || !IsEnabled()) ++ const CFX_Vector& delta) { ++ if (!IsValid() || !IsVisible()) + return false; + + SetCursor(); +@@ -364,21 +381,21 @@ bool CPWL_Wnd::OnMouseWheel(short zDelta, + + for (const auto& pChild : m_Children) { + if (IsWndCaptureKeyboard(pChild.get())) +- return pChild->OnMouseWheel(zDelta, pChild->ParentToChild(point), nFlag); ++ return pChild->OnMouseWheel(nFlag, point, delta); + } + return false; + } + + void CPWL_Wnd::AddChild(std::unique_ptr pWnd) { +- ASSERT(!pWnd->m_pParent); ++ DCHECK(!pWnd->m_pParent); + pWnd->m_pParent = this; + m_Children.push_back(std::move(pWnd)); + } + + void CPWL_Wnd::RemoveChild(CPWL_Wnd* pWnd) { +- ASSERT(pWnd->m_pParent == this); +- auto it = std::find(m_Children.begin(), m_Children.end(), +- pdfium::FakeUniquePtr(pWnd)); ++ DCHECK_EQ(pWnd->m_pParent, this); ++ auto it = ++ std::find(m_Children.begin(), m_Children.end(), MakeFakeUniquePtr(pWnd)); + if (it == m_Children.end()) + return; + +@@ -429,18 +446,10 @@ void CPWL_Wnd::RemoveFlag(uint32_t dwFlags) { + m_CreationParams.dwFlags &= ~dwFlags; + } + +-void CPWL_Wnd::AddFlag(uint32_t dwFlags) { +- m_CreationParams.dwFlags |= dwFlags; +-} +- + CFX_Color CPWL_Wnd::GetBackgroundColor() const { + return m_CreationParams.sBackgroundColor; + } + +-void CPWL_Wnd::SetBackgroundColor(const CFX_Color& color) { +- m_CreationParams.sBackgroundColor = color; +-} +- + CFX_Color CPWL_Wnd::GetTextColor() const { + return m_CreationParams.sTextColor; + } +@@ -449,11 +458,6 @@ BorderStyle CPWL_Wnd::GetBorderStyle() const { + return m_CreationParams.nBorderStyle; + } + +-void CPWL_Wnd::SetBorderStyle(BorderStyle nBorderStyle) { +- if (HasFlag(PWS_BORDER)) +- m_CreationParams.nBorderStyle = nBorderStyle; +-} +- + int32_t CPWL_Wnd::GetBorderWidth() const { + return HasFlag(PWS_BORDER) ? m_CreationParams.dwBorderWidth : 0; + } +@@ -471,7 +475,7 @@ const CPWL_Dash& CPWL_Wnd::GetBorderDash() const { + } + + CPWL_ScrollBar* CPWL_Wnd::GetVScrollBar() const { +- return HasFlag(PWS_VSCROLL) ? m_pVScrollBar.Get() : nullptr; ++ return HasFlag(PWS_VSCROLL) ? m_pVScrollBar : nullptr; + } + + void CPWL_Wnd::CreateScrollBar(const CreateParams& cp) { +@@ -483,14 +487,12 @@ void CPWL_Wnd::CreateVScrollBar(const CreateParams& cp) { + return; + + CreateParams scp = cp; +- scp.dwFlags = +- PWS_CHILD | PWS_BACKGROUND | PWS_AUTOTRANSPARENT | PWS_NOREFRESHCLIP; +- scp.sBackgroundColor = PWL_DEFAULT_WHITECOLOR; +- scp.eCursorType = FXCT_ARROW; +- scp.nTransparency = PWL_SCROLLBAR_TRANSPARENCY; +- +- auto pBar = +- pdfium::MakeUnique(scp, CloneAttachedData(), SBT_VSCROLL); ++ scp.dwFlags = PWS_BACKGROUND | PWS_AUTOTRANSPARENT | PWS_NOREFRESHCLIP; ++ scp.sBackgroundColor = kDefaultWhiteColor; ++ scp.eCursorType = IPWL_FillerNotify::CursorStyle::kArrow; ++ scp.nTransparency = CPWL_ScrollBar::kTransparency; ++ ++ auto pBar = std::make_unique(scp, CloneAttachedData()); + m_pVScrollBar = pBar.get(); + AddChild(std::move(pBar)); + m_pVScrollBar->Realize(); +@@ -528,11 +530,19 @@ void CPWL_Wnd::OnSetFocus() {} + + void CPWL_Wnd::OnKillFocus() {} + +-std::unique_ptr CPWL_Wnd::CloneAttachedData() ++std::unique_ptr CPWL_Wnd::CloneAttachedData() + const { + return m_pAttachedData ? m_pAttachedData->Clone() : nullptr; + } + ++std::vector> CPWL_Wnd::GetAncestors() { ++ std::vector> results; ++ for (CPWL_Wnd* pWnd = this; pWnd; pWnd = pWnd->GetParentWindow()) { ++ results.emplace_back(pWnd); ++ } ++ return results; ++} ++ + bool CPWL_Wnd::WndHitTest(const CFX_PointF& point) const { + return IsValid() && IsVisible() && GetWindowRect().Contains(point); + } +@@ -588,7 +598,7 @@ bool CPWL_Wnd::RePosChildWnd() { + rcContent.Normalize(); + } + CFX_FloatRect rcVScroll = +- CFX_FloatRect(rcContent.right - PWL_SCROLLBAR_WIDTH, rcContent.bottom, ++ CFX_FloatRect(rcContent.right - CPWL_ScrollBar::kWidth, rcContent.bottom, + rcContent.right - 1.0f, rcContent.top); + + ObservedPtr thisObserved(this); +@@ -603,7 +613,7 @@ void CPWL_Wnd::CreateChildWnd(const CreateParams& cp) {} + + void CPWL_Wnd::SetCursor() { + if (IsValid()) +- GetSystemHandler()->SetCursor(GetCreationParams()->eCursorType); ++ GetFillerNotify()->SetCursor(GetCreationParams()->eCursorType); + } + + void CPWL_Wnd::CreateMsgControl() { +@@ -659,10 +669,10 @@ void CPWL_Wnd::SetFontSize(float fFontSize) { + + CFX_Color CPWL_Wnd::GetBorderLeftTopColor(BorderStyle nBorderStyle) const { + switch (nBorderStyle) { +- case BorderStyle::BEVELED: +- return CFX_Color(CFX_Color::kGray, 1); +- case BorderStyle::INSET: +- return CFX_Color(CFX_Color::kGray, 0.5f); ++ case BorderStyle::kBeveled: ++ return CFX_Color(CFX_Color::Type::kGray, 1); ++ case BorderStyle::kInset: ++ return CFX_Color(CFX_Color::Type::kGray, 0.5f); + default: + return CFX_Color(); + } +@@ -670,10 +680,10 @@ CFX_Color CPWL_Wnd::GetBorderLeftTopColor(BorderStyle nBorderStyle) const { + + CFX_Color CPWL_Wnd::GetBorderRightBottomColor(BorderStyle nBorderStyle) const { + switch (nBorderStyle) { +- case BorderStyle::BEVELED: ++ case BorderStyle::kBeveled: + return GetBackgroundColor() / 2.0f; +- case BorderStyle::INSET: +- return CFX_Color(CFX_Color::kGray, 0.75f); ++ case BorderStyle::kInset: ++ return CFX_Color(CFX_Color::Type::kGray, 0.75f); + default: + return CFX_Color(); + } +@@ -691,7 +701,7 @@ void CPWL_Wnd::SetTransparency(int32_t nTransparency) { + } + + CFX_Matrix CPWL_Wnd::GetWindowMatrix() const { +- CFX_Matrix mt = GetChildToRoot(); ++ CFX_Matrix mt; + if (ProviderIface* pProvider = GetProvider()) + mt.Concat(pProvider->GetWindowMatrix(GetAttachedData())); + return mt; +@@ -701,61 +711,3 @@ CFX_FloatRect CPWL_Wnd::PWLtoWnd(const CFX_FloatRect& rect) const { + CFX_Matrix mt = GetWindowMatrix(); + return mt.TransformRect(rect); + } +- +-CFX_PointF CPWL_Wnd::ParentToChild(const CFX_PointF& point) const { +- CFX_Matrix mt = GetChildMatrix(); +- if (mt.IsIdentity()) +- return point; +- +- CFX_Matrix inverse = mt.GetInverse(); +- if (!inverse.IsIdentity()) +- mt = inverse; +- return mt.Transform(point); +-} +- +-CFX_FloatRect CPWL_Wnd::ParentToChild(const CFX_FloatRect& rect) const { +- CFX_Matrix mt = GetChildMatrix(); +- if (mt.IsIdentity()) +- return rect; +- +- CFX_Matrix inverse = mt.GetInverse(); +- if (!inverse.IsIdentity()) +- mt = inverse; +- +- return mt.TransformRect(rect); +-} +- +-CFX_Matrix CPWL_Wnd::GetChildToRoot() const { +- CFX_Matrix mt; +- if (HasFlag(PWS_CHILD)) { +- const CPWL_Wnd* pParent = this; +- while (pParent) { +- mt.Concat(pParent->GetChildMatrix()); +- pParent = pParent->GetParentWindow(); +- } +- } +- return mt; +-} +- +-CFX_Matrix CPWL_Wnd::GetChildMatrix() const { +- return HasFlag(PWS_CHILD) ? m_CreationParams.mtChild : CFX_Matrix(); +-} +- +-void CPWL_Wnd::SetChildMatrix(const CFX_Matrix& mt) { +- m_CreationParams.mtChild = mt; +-} +- +-const CPWL_Wnd* CPWL_Wnd::GetFocused() const { +- CPWL_MsgControl* pMsgCtrl = GetMsgControl(); +- return pMsgCtrl ? pMsgCtrl->GetFocusedWindow() : nullptr; +-} +- +-void CPWL_Wnd::EnableWindow(bool bEnable) { +- if (m_bEnabled == bEnable) +- return; +- +- for (const auto& pChild : m_Children) +- pChild->EnableWindow(bEnable); +- +- m_bEnabled = bEnable; +-} +diff --git a/fpdfsdk/pwl/cpwl_wnd.h b/fpdfsdk/pwl/cpwl_wnd.h +index 6e8f0736c..b10b59a5e 100644 +--- a/fpdfsdk/pwl/cpwl_wnd.h ++++ b/fpdfsdk/pwl/cpwl_wnd.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,11 +11,14 @@ + #include + + #include "core/fxcrt/cfx_timer.h" ++#include "core/fxcrt/mask.h" + #include "core/fxcrt/observed_ptr.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "core/fxcrt/widestring.h" + #include "core/fxge/cfx_color.h" + #include "core/fxge/cfx_renderdevice.h" +-#include "fpdfsdk/pwl/ipwl_systemhandler.h" ++#include "fpdfsdk/pwl/ipwl_fillernotify.h" ++#include "public/fpdf_fwlevent.h" + + class CPWL_Edit; + class CPWL_MsgControl; +@@ -24,10 +27,8 @@ class IPVT_FontMap; + struct PWL_SCROLL_INFO; + + // window styles +-#define PWS_CHILD 0x80000000L + #define PWS_BORDER 0x40000000L + #define PWS_BACKGROUND 0x20000000L +-#define PWS_HSCROLL 0x10000000L + #define PWS_VSCROLL 0x08000000L + #define PWS_VISIBLE 0x04000000L + #define PWS_READONLY 0x01000000L +@@ -42,16 +43,13 @@ struct PWL_SCROLL_INFO; + #define PES_RIGHT 0x0008L + #define PES_MIDDLE 0x0010L + #define PES_TOP 0x0020L +-#define PES_BOTTOM 0x0040L + #define PES_CENTER 0x0080L + #define PES_CHARARRAY 0x0100L + #define PES_AUTOSCROLL 0x0200L + #define PES_AUTORETURN 0x0400L + #define PES_UNDO 0x0800L + #define PES_RICH 0x1000L +-#define PES_SPELLCHECK 0x2000L + #define PES_TEXTOVERFLOW 0x4000L +-#define PES_NOREAD 0x8000L + + // listbox styles + #define PLBS_MULTIPLESEL 0x0001L +@@ -60,14 +58,6 @@ struct PWL_SCROLL_INFO; + // combobox styles + #define PCBS_ALLOWCUSTOMTEXT 0x0001L + +-// Cursor style. These must match the values in public/fpdf_formfill.h +-#define FXCT_ARROW 0 +-#define FXCT_NESW 1 +-#define FXCT_NWSE 2 +-#define FXCT_VBEAM 3 +-#define FXCT_HBEAM 4 +-#define FXCT_HAND 5 +- + struct CPWL_Dash { + CPWL_Dash() : nDash(0), nGap(0), nPhase(0) {} + CPWL_Dash(int32_t dash, int32_t gap, int32_t phase) +@@ -84,76 +74,84 @@ struct CPWL_Dash { + int32_t nPhase; + }; + +-#define PWL_SCROLLBAR_WIDTH 12.0f +-#define PWL_SCROLLBAR_TRANSPARENCY 150 +-#define PWL_DEFAULT_BLACKCOLOR CFX_Color(CFX_Color::kGray, 0) +-#define PWL_DEFAULT_WHITECOLOR CFX_Color(CFX_Color::kGray, 1) +- + class CPWL_Wnd : public Observable { + public: ++ static const CFX_Color kDefaultBlackColor; ++ static const CFX_Color kDefaultWhiteColor; ++ + class ProviderIface : public Observable { + public: + virtual ~ProviderIface() = default; + + // get a matrix which map user space to CWnd client space + virtual CFX_Matrix GetWindowMatrix( +- const IPWL_SystemHandler::PerWindowData* pAttached) = 0; +- }; ++ const IPWL_FillerNotify::PerWindowData* pAttached) = 0; + +- class FocusHandlerIface { +- public: +- virtual ~FocusHandlerIface() = default; +- virtual void OnSetFocus(CPWL_Edit* pEdit) = 0; ++ virtual void OnSetFocusForEdit(CPWL_Edit* pEdit) = 0; + }; + ++ // Caller-provided options for window creation. + class CreateParams { + public: +- CreateParams(); ++ CreateParams(CFX_Timer::HandlerIface* timer_handler, ++ IPWL_FillerNotify* filler_notify, ++ ProviderIface* provider); + CreateParams(const CreateParams& other); + ~CreateParams(); + +- CFX_FloatRect rcRectWnd; // required +- UnownedPtr pTimerHandler; // required +- UnownedPtr pSystemHandler; // required +- UnownedPtr pFontMap; // required +- ObservedPtr pProvider; // required +- UnownedPtr pFocusHandler; // optional +- uint32_t dwFlags = 0; // optional +- CFX_Color sBackgroundColor; // optional +- BorderStyle nBorderStyle = BorderStyle::SOLID; // optional +- int32_t dwBorderWidth = 1; // optional +- CFX_Color sBorderColor; // optional +- CFX_Color sTextColor; // optional +- int32_t nTransparency = 255; // optional +- float fFontSize; // optional +- CPWL_Dash sDash; // optional +- CPWL_MsgControl* pMsgControl = nullptr; // ignore +- int32_t eCursorType = FXCT_ARROW; // ignore +- CFX_Matrix mtChild; // ignore ++ // Required: ++ CFX_FloatRect rcRectWnd; ++ ObservedPtr const pTimerHandler; ++ UnownedPtr const pFillerNotify; ++ UnownedPtr pFontMap; ++ ObservedPtr pProvider; ++ ++ // Optional: ++ uint32_t dwFlags = 0; ++ CFX_Color sBackgroundColor; ++ BorderStyle nBorderStyle = BorderStyle::kSolid; ++ int32_t dwBorderWidth = 1; ++ CFX_Color sBorderColor; ++ CFX_Color sTextColor; ++ int32_t nTransparency = 255; ++ float fFontSize; ++ CPWL_Dash sDash; ++ ++ // Ignore, used internally only: ++ CPWL_MsgControl* pMsgControl = nullptr; ++ IPWL_FillerNotify::CursorStyle eCursorType = ++ IPWL_FillerNotify::CursorStyle::kArrow; + }; + +- static bool IsSHIFTKeyDown(uint32_t nFlag); +- static bool IsCTRLKeyDown(uint32_t nFlag); +- static bool IsALTKeyDown(uint32_t nFlag); ++ static bool IsSHIFTKeyDown(Mask nFlag); ++ static bool IsCTRLKeyDown(Mask nFlag); ++ static bool IsALTKeyDown(Mask nFlag); ++ static bool IsMETAKeyDown(Mask nFlag); ++ ++ // Selects between IsCTRLKeyDown() and IsMETAKeyDown() depending on platform. ++ static bool IsPlatformShortcutKey(Mask nFlag); + + CPWL_Wnd(const CreateParams& cp, +- std::unique_ptr pAttachedData); ++ std::unique_ptr pAttachedData); + virtual ~CPWL_Wnd(); + + // Returns |true| iff this instance is still allocated. +- virtual bool InvalidateRect(CFX_FloatRect* pRect); +- +- virtual bool OnKeyDown(uint16_t nChar, uint32_t nFlag); +- virtual bool OnChar(uint16_t nChar, uint32_t nFlag); +- virtual bool OnLButtonDblClk(const CFX_PointF& point, uint32_t nFlag); +- virtual bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag); +- virtual bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag); +- virtual bool OnRButtonDown(const CFX_PointF& point, uint32_t nFlag); +- virtual bool OnRButtonUp(const CFX_PointF& point, uint32_t nFlag); +- virtual bool OnMouseMove(const CFX_PointF& point, uint32_t nFlag); +- virtual bool OnMouseWheel(short zDelta, ++ virtual bool InvalidateRect(const CFX_FloatRect* pRect); ++ ++ virtual bool OnKeyDown(FWL_VKEYCODE nKeyCode, Mask nFlag); ++ virtual bool OnChar(uint16_t nChar, Mask nFlag); ++ virtual bool OnLButtonDblClk(Mask nFlag, ++ const CFX_PointF& point); ++ virtual bool OnLButtonDown(Mask nFlag, ++ const CFX_PointF& point); ++ virtual bool OnLButtonUp(Mask nFlag, const CFX_PointF& point); ++ virtual bool OnRButtonDown(Mask nFlag, ++ const CFX_PointF& point); ++ virtual bool OnRButtonUp(Mask nFlag, const CFX_PointF& point); ++ virtual bool OnMouseMove(Mask nFlag, const CFX_PointF& point); ++ virtual bool OnMouseWheel(Mask nFlag, + const CFX_PointF& point, +- uint32_t nFlag); ++ const CFX_Vector& delta); + virtual void SetScrollInfo(const PWL_SCROLL_INFO& info); + virtual void SetScrollPosition(float pos); + virtual void ScrollWindowVertically(float pos); +@@ -171,7 +169,9 @@ class CPWL_Wnd : public Observable { + + virtual WideString GetText(); + virtual WideString GetSelectedText(); ++ virtual void ReplaceAndKeepSelection(const WideString& text); + virtual void ReplaceSelection(const WideString& text); ++ virtual bool SelectAllText(); + + virtual bool CanUndo(); + virtual bool CanRedo(); +@@ -181,80 +181,43 @@ class CPWL_Wnd : public Observable { + virtual CFX_FloatRect GetFocusRect() const; + virtual CFX_FloatRect GetClientRect() const; + ++ virtual void OnSetFocus(); ++ virtual void OnKillFocus(); ++ + void AddChild(std::unique_ptr pWnd); + void RemoveChild(CPWL_Wnd* pWnd); + void Realize(); + void Destroy(); + bool Move(const CFX_FloatRect& rcNew, bool bReset, bool bRefresh); + +- void InvalidateFocusHandler(FocusHandlerIface* handler); + void InvalidateProvider(ProviderIface* provider); +- void SetCapture(); +- void ReleaseCapture(); + void DrawAppearance(CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device); + +- CFX_Color GetBackgroundColor() const; +- void SetBackgroundColor(const CFX_Color& color); +- CFX_Color GetBorderColor() const; +- CFX_Color GetTextColor() const; +- void SetTextColor(const CFX_Color& color); +- CFX_Color GetBorderLeftTopColor(BorderStyle nBorderStyle) const; +- CFX_Color GetBorderRightBottomColor(BorderStyle nBorderStyle) const; +- +- void SetBorderStyle(BorderStyle nBorderStyle); +- BorderStyle GetBorderStyle() const; +- const CPWL_Dash& GetBorderDash() const; +- + int32_t GetBorderWidth() const; +- int32_t GetInnerBorderWidth() const; + CFX_FloatRect GetWindowRect() const; +- CFX_PointF GetCenterPoint() const; + + bool IsVisible() const { return m_bVisible; } + bool HasFlag(uint32_t dwFlags) const; +- void AddFlag(uint32_t dwFlags); + void RemoveFlag(uint32_t dwFlags); +- + void SetClipRect(const CFX_FloatRect& rect); +- const CFX_FloatRect& GetClipRect() const; + +- CPWL_Wnd* GetParentWindow() const { return m_pParent.Get(); } +- const IPWL_SystemHandler::PerWindowData* GetAttachedData() const { ++ IPWL_FillerNotify::PerWindowData* GetAttachedData() const { + return m_pAttachedData.get(); + } +- std::unique_ptr CloneAttachedData() const; ++ std::unique_ptr CloneAttachedData() const; ++ std::vector> GetAncestors(); + + bool WndHitTest(const CFX_PointF& point) const; + bool ClientHitTest(const CFX_PointF& point) const; + bool IsCaptureMouse() const; + +- void EnableWindow(bool bEnable); +- bool IsEnabled() const { return m_bEnabled; } +- const CPWL_Wnd* GetFocused() const; + bool IsFocused() const; + bool IsReadOnly() const; +- CPWL_ScrollBar* GetVScrollBar() const; +- +- IPVT_FontMap* GetFontMap() const { return m_CreationParams.pFontMap.Get(); } +- ProviderIface* GetProvider() const { +- return m_CreationParams.pProvider.Get(); +- } +- FocusHandlerIface* GetFocusHandler() const { +- return m_CreationParams.pFocusHandler.Get(); +- } + +- int32_t GetTransparency(); + void SetTransparency(int32_t nTransparency); +- +- CFX_Matrix GetChildToRoot() const; +- CFX_Matrix GetChildMatrix() const; +- void SetChildMatrix(const CFX_Matrix& mt); + CFX_Matrix GetWindowMatrix() const; + +- virtual void OnSetFocus(); +- virtual void OnKillFocus(); +- + protected: + virtual void CreateChildWnd(const CreateParams& cp); + +@@ -267,37 +230,46 @@ class CPWL_Wnd : public Observable { + virtual void OnCreated(); + virtual void OnDestroy(); + +- bool IsNotifying() const { return m_bNotifying; } + bool IsValid() const { return m_bCreated; } + CreateParams* GetCreationParams() { return &m_CreationParams; } +- TimerHandlerIface* GetTimerHandler() const { ++ ProviderIface* GetProvider() const { ++ return m_CreationParams.pProvider.Get(); ++ } ++ CFX_Timer::HandlerIface* GetTimerHandler() const { + return m_CreationParams.pTimerHandler.Get(); + } +- IPWL_SystemHandler* GetSystemHandler() const { +- return m_CreationParams.pSystemHandler.Get(); ++ IPWL_FillerNotify* GetFillerNotify() const { ++ return m_CreationParams.pFillerNotify; + } + ++ CPWL_Wnd* GetParentWindow() const { return m_pParent; } ++ CPWL_ScrollBar* GetVScrollBar() const; ++ + // Returns |true| iff this instance is still allocated. + bool InvalidateRectMove(const CFX_FloatRect& rcOld, + const CFX_FloatRect& rcNew); + ++ void SetCapture(); ++ void ReleaseCapture(); + bool IsWndCaptureMouse(const CPWL_Wnd* pWnd) const; + bool IsWndCaptureKeyboard(const CPWL_Wnd* pWnd) const; + +- static bool IsCTRLpressed(uint32_t nFlag) { +- return CPWL_Wnd::IsCTRLKeyDown(nFlag); +- } +- static bool IsSHIFTpressed(uint32_t nFlag) { +- return CPWL_Wnd::IsSHIFTKeyDown(nFlag); +- } +- static bool IsALTpressed(uint32_t nFlag) { +- return CPWL_Wnd::IsALTKeyDown(nFlag); +- } ++ CFX_Color GetBackgroundColor() const; ++ CFX_Color GetBorderColor() const; ++ CFX_Color GetTextColor() const; ++ CFX_Color GetBorderLeftTopColor(BorderStyle nBorderStyle) const; ++ CFX_Color GetBorderRightBottomColor(BorderStyle nBorderStyle) const; ++ BorderStyle GetBorderStyle() const; ++ const CPWL_Dash& GetBorderDash() const; + +- private: +- CFX_PointF ParentToChild(const CFX_PointF& point) const; +- CFX_FloatRect ParentToChild(const CFX_FloatRect& rect) const; ++ int32_t GetTransparency(); ++ int32_t GetInnerBorderWidth() const; ++ CFX_PointF GetCenterPoint() const; ++ const CFX_FloatRect& GetClipRect() const; + ++ IPVT_FontMap* GetFontMap() const { return m_CreationParams.pFontMap; } ++ ++ private: + void DrawChildAppearance(CFX_RenderDevice* pDevice, + const CFX_Matrix& mtUser2Device); + +@@ -309,11 +281,10 @@ class CPWL_Wnd : public Observable { + void AdjustStyle(); + void CreateMsgControl(); + void DestroyMsgControl(); +- + CPWL_MsgControl* GetMsgControl() const; + + CreateParams m_CreationParams; +- std::unique_ptr m_pAttachedData; ++ std::unique_ptr m_pAttachedData; + UnownedPtr m_pParent; + std::vector> m_Children; + UnownedPtr m_pVScrollBar; +@@ -321,8 +292,6 @@ class CPWL_Wnd : public Observable { + CFX_FloatRect m_rcClip; + bool m_bCreated = false; + bool m_bVisible = false; +- bool m_bNotifying = false; +- bool m_bEnabled = true; + }; + + #endif // FPDFSDK_PWL_CPWL_WND_H_ +diff --git a/fpdfsdk/pwl/ipwl_fillernotify.h b/fpdfsdk/pwl/ipwl_fillernotify.h +new file mode 100644 +index 000000000..8a56a1063 +--- /dev/null ++++ b/fpdfsdk/pwl/ipwl_fillernotify.h +@@ -0,0 +1,69 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef FPDFSDK_PWL_IPWL_FILLERNOTIFY_H_ ++#define FPDFSDK_PWL_IPWL_FILLERNOTIFY_H_ ++ ++#include ++#include ++ ++#include "core/fxcrt/mask.h" ++#include "core/fxcrt/widestring.h" ++#include "public/fpdf_fwlevent.h" ++ ++class CFX_FloatRect; ++ ++class IPWL_FillerNotify { ++ public: ++ // These must match the values in public/fpdf_formfill.h ++ enum CursorStyle { ++ kArrow = 0, ++ kNESW = 1, ++ kNWSE = 2, ++ kVBeam = 3, ++ kHBeam = 4, ++ kHand = 5, ++ }; ++ ++ class PerWindowData { ++ public: ++ virtual ~PerWindowData() = default; ++ virtual std::unique_ptr Clone() const = 0; ++ }; ++ ++ virtual ~IPWL_FillerNotify() = default; ++ ++ virtual void InvalidateRect(PerWindowData* pWidgetData, ++ const CFX_FloatRect& rect) = 0; ++ virtual void OutputSelectedRect(PerWindowData* pWidgetData, ++ const CFX_FloatRect& rect) = 0; ++ virtual bool IsSelectionImplemented() const = 0; ++ virtual void SetCursor(CursorStyle nCursorStyle) = 0; ++ ++ // Must write to |bBottom| and |fPopupRet|. ++ virtual void QueryWherePopup(const PerWindowData* pAttached, ++ float fPopupMin, ++ float fPopupMax, ++ bool* bBottom, ++ float* fPopupRet) = 0; ++ ++ virtual std::pair OnBeforeKeyStroke( ++ const PerWindowData* pAttached, ++ WideString& strChange, ++ const WideString& strChangeEx, ++ int nSelStart, ++ int nSelEnd, ++ bool bKeyDown, ++ Mask nFlag) = 0; ++ ++ virtual bool OnPopupPreOpen(const PerWindowData* pAttached, ++ Mask nFlag) = 0; ++ ++ virtual bool OnPopupPostOpen(const PerWindowData* pAttached, ++ Mask nFlag) = 0; ++}; ++ ++#endif // FPDFSDK_PWL_IPWL_FILLERNOTIFY_H_ +diff --git a/fpdfsdk/pwl/ipwl_systemhandler.h b/fpdfsdk/pwl/ipwl_systemhandler.h +deleted file mode 100644 +index 8a14d8eb8..000000000 +--- a/fpdfsdk/pwl/ipwl_systemhandler.h ++++ /dev/null +@@ -1,35 +0,0 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef FPDFSDK_PWL_IPWL_SYSTEMHANDLER_H_ +-#define FPDFSDK_PWL_IPWL_SYSTEMHANDLER_H_ +- +-#include +- +-#include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" +- +-class CFFL_FormFiller; +- +-class IPWL_SystemHandler { +- public: +- class PerWindowData { +- public: +- virtual ~PerWindowData() = default; +- virtual std::unique_ptr Clone() const = 0; +- }; +- +- virtual ~IPWL_SystemHandler() = default; +- +- virtual void InvalidateRect(PerWindowData* pWidgetData, +- const CFX_FloatRect& rect) = 0; +- virtual void OutputSelectedRect(CFFL_FormFiller* pFormFiller, +- const CFX_FloatRect& rect) = 0; +- virtual bool IsSelectionImplemented() const = 0; +- virtual void SetCursor(int32_t nCursorType) = 0; +-}; +- +-#endif // FPDFSDK_PWL_IPWL_SYSTEMHANDLER_H_ +diff --git a/fxbarcode/BC_Library.cpp b/fxbarcode/BC_Library.cpp +index 393f3297e..aee337234 100644 +--- a/fxbarcode/BC_Library.cpp ++++ b/fxbarcode/BC_Library.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/BC_Library.h b/fxbarcode/BC_Library.h +index 5f1af4d61..81d5a88fe 100644 +--- a/fxbarcode/BC_Library.h ++++ b/fxbarcode/BC_Library.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,33 +9,33 @@ + + #include + +-enum BC_TEXT_LOC : uint8_t { +- BC_TEXT_LOC_NONE = 0, +- BC_TEXT_LOC_ABOVE, +- BC_TEXT_LOC_BELOW, +- BC_TEXT_LOC_ABOVEEMBED, +- BC_TEXT_LOC_BELOWEMBED ++enum class BC_TEXT_LOC : uint8_t { ++ kNone = 0, ++ kAbove, ++ kBelow, ++ kAboveEmbed, ++ kBelowEmbed, + }; + +-enum BC_CHAR_ENCODING : uint8_t { +- CHAR_ENCODING_UTF8 = 0, +- CHAR_ENCODING_UNICODE ++enum class BC_CHAR_ENCODING : uint8_t { ++ kUTF8 = 0, ++ kUnicode, + }; + +-enum BC_TYPE : int8_t { +- BC_UNKNOWN = -1, +- BC_CODE39 = 0, +- BC_CODABAR, +- BC_CODE128, +- BC_CODE128_B, +- BC_CODE128_C, +- BC_EAN8, +- BC_UPCA, +- BC_EAN13, +- BC_QR_CODE, +- BC_PDF417, +- BC_DATAMATRIX, +- BC_LAST = BC_DATAMATRIX ++enum class BC_TYPE : int8_t { ++ kUnknown = -1, ++ kCode39 = 0, ++ kCodabar, ++ kCode128, ++ kCode128B, ++ kCode128C, ++ kEAN8, ++ kUPCA, ++ kEAN13, ++ kQRCode, ++ kPDF417, ++ kDataMatrix, ++ kLast = kDataMatrix + }; + + void BC_Library_Init(); +diff --git a/fxbarcode/BC_TwoDimWriter.cpp b/fxbarcode/BC_TwoDimWriter.cpp +index b88574cec..73eb29e9e 100644 +--- a/fxbarcode/BC_TwoDimWriter.cpp ++++ b/fxbarcode/BC_TwoDimWriter.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,13 +9,14 @@ + #include + + #include "core/fxcrt/fx_safe_types.h" ++#include "core/fxge/cfx_fillrenderoptions.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/cfx_renderdevice.h" + #include "fxbarcode/BC_Writer.h" + #include "fxbarcode/common/BC_CommonBitMatrix.h" ++#include "third_party/base/check.h" + #include "third_party/base/numerics/safe_math.h" +-#include "third_party/base/ptr_util.h" + + CBC_TwoDimWriter::CBC_TwoDimWriter(bool bFixedSize) + : m_bFixedSize(bFixedSize) {} +@@ -71,9 +72,7 @@ bool CBC_TwoDimWriter::RenderResult(pdfium::span code, + m_leftPadding = std::max((m_Width - m_outputWidth) / 2, 0); + m_topPadding = std::max((m_Height - m_outputHeight) / 2, 0); + +- m_output = pdfium::MakeUnique(); +- m_output->Init(m_inputWidth, m_inputHeight); +- ++ m_output = std::make_unique(m_inputWidth, m_inputHeight); + for (int32_t y = 0; y < m_inputHeight; ++y) { + for (int32_t x = 0; x < m_inputWidth; ++x) { + if (code[x + y * m_inputWidth] == 1) +@@ -84,23 +83,23 @@ bool CBC_TwoDimWriter::RenderResult(pdfium::span code, + } + + void CBC_TwoDimWriter::RenderDeviceResult(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) { +- ASSERT(m_output); ++ const CFX_Matrix& matrix) { ++ DCHECK(m_output); + + CFX_GraphStateData stateData; +- CFX_PathData path; ++ CFX_Path path; + path.AppendRect(0, 0, m_Width, m_Height); +- device->DrawPath(&path, matrix, &stateData, kBackgroundColor, +- kBackgroundColor, FXFILL_ALTERNATE); ++ device->DrawPath(path, &matrix, &stateData, kBackgroundColor, ++ kBackgroundColor, CFX_FillRenderOptions::EvenOddOptions()); + int32_t leftPos = m_leftPadding; + int32_t topPos = m_topPadding; + +- CFX_Matrix matri = *matrix; ++ CFX_Matrix matri = matrix; + if (m_Width < m_outputWidth && m_Height < m_outputHeight) { + CFX_Matrix matriScale(static_cast(m_Width) / m_outputWidth, 0.0, 0.0, + static_cast(m_Height) / m_outputHeight, 0.0, + 0.0); +- matriScale.Concat(*matrix); ++ matriScale.Concat(matrix); + matri = matriScale; + } + +@@ -115,12 +114,13 @@ void CBC_TwoDimWriter::RenderDeviceResult(CFX_RenderDevice* device, + int start_y_output = y + 1; + int end_y_output = y + 2; + +- CFX_PathData rect; ++ CFX_Path rect; + rect.AppendRect(leftPos + start_x_output * m_multiX, + topPos + start_y_output * m_multiY, + leftPos + end_x_output * m_multiX, + topPos + end_y_output * m_multiY); +- device->DrawPath(&rect, &matri, &data, kBarColor, 0, FXFILL_WINDING); ++ device->DrawPath(rect, &matri, &data, kBarColor, 0, ++ CFX_FillRenderOptions::WindingOptions()); + } + } + } +diff --git a/fxbarcode/BC_TwoDimWriter.h b/fxbarcode/BC_TwoDimWriter.h +index a887ded7f..3f9e4a7db 100644 +--- a/fxbarcode/BC_TwoDimWriter.h ++++ b/fxbarcode/BC_TwoDimWriter.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -24,7 +24,7 @@ class CBC_TwoDimWriter : public CBC_Writer { + bool RenderResult(pdfium::span code, + int32_t codeWidth, + int32_t codeHeight); +- void RenderDeviceResult(CFX_RenderDevice* device, const CFX_Matrix* matrix); ++ void RenderDeviceResult(CFX_RenderDevice* device, const CFX_Matrix& matrix); + + int32_t error_correction_level() const { return m_iCorrectionLevel; } + +diff --git a/fxbarcode/BC_Writer.cpp b/fxbarcode/BC_Writer.cpp +index 9f77ffbc0..48d02bb56 100644 +--- a/fxbarcode/BC_Writer.cpp ++++ b/fxbarcode/BC_Writer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,9 +10,8 @@ CBC_Writer::CBC_Writer() = default; + + CBC_Writer::~CBC_Writer() = default; + +-bool CBC_Writer::SetCharEncoding(int32_t encoding) { ++void CBC_Writer::SetCharEncoding(BC_CHAR_ENCODING encoding) { + m_CharEncoding = encoding; +- return true; + } + + bool CBC_Writer::SetModuleHeight(int32_t moduleHeight) { +@@ -31,19 +30,15 @@ bool CBC_Writer::SetModuleWidth(int32_t moduleWidth) { + return true; + } + +-bool CBC_Writer::SetHeight(int32_t height) { ++void CBC_Writer::SetHeight(int32_t height) { + m_Height = height; +- return true; + } + +-bool CBC_Writer::SetWidth(int32_t width) { ++void CBC_Writer::SetWidth(int32_t width) { + m_Width = width; +- return true; + } + +-bool CBC_Writer::SetTextLocation(BC_TEXT_LOC location) { +- return false; +-} ++void CBC_Writer::SetTextLocation(BC_TEXT_LOC location) {} + + bool CBC_Writer::SetWideNarrowRatio(int8_t ratio) { + return false; +diff --git a/fxbarcode/BC_Writer.h b/fxbarcode/BC_Writer.h +index cf7f43bcd..be418aa36 100644 +--- a/fxbarcode/BC_Writer.h ++++ b/fxbarcode/BC_Writer.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,19 +7,21 @@ + #ifndef FXBARCODE_BC_WRITER_H_ + #define FXBARCODE_BC_WRITER_H_ + +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" + #include "fxbarcode/BC_Library.h" + + class CBC_Writer { + public: + CBC_Writer(); + virtual ~CBC_Writer(); +- virtual bool SetCharEncoding(int32_t encoding); +- virtual bool SetModuleHeight(int32_t moduleHeight); +- virtual bool SetModuleWidth(int32_t moduleWidth); +- virtual bool SetHeight(int32_t height); +- virtual bool SetWidth(int32_t width); +- virtual bool SetTextLocation(BC_TEXT_LOC location); ++ ++ void SetCharEncoding(BC_CHAR_ENCODING encoding); ++ bool SetModuleHeight(int32_t moduleHeight); ++ bool SetModuleWidth(int32_t moduleWidth); ++ void SetHeight(int32_t height); ++ void SetWidth(int32_t width); ++ ++ virtual void SetTextLocation(BC_TEXT_LOC location); + virtual bool SetWideNarrowRatio(int8_t ratio); + virtual bool SetStartChar(char start); + virtual bool SetEndChar(char end); +@@ -29,12 +31,12 @@ class CBC_Writer { + static const FX_ARGB kBarColor = 0xff000000; + static const FX_ARGB kBackgroundColor = 0xffffffff; + +- int32_t m_CharEncoding = 0; + int32_t m_ModuleHeight = 1; + int32_t m_ModuleWidth = 1; + int32_t m_Height = 320; + int32_t m_Width = 640; +- FXDIB_Format m_colorSpace = FXDIB_Argb; ++ FXDIB_Format m_colorSpace = FXDIB_Format::kArgb; ++ BC_CHAR_ENCODING m_CharEncoding = BC_CHAR_ENCODING::kUTF8; + }; + + #endif // FXBARCODE_BC_WRITER_H_ +diff --git a/fxbarcode/BUILD.gn b/fxbarcode/BUILD.gn +index 2b10e95d1..0ca11c9e1 100644 +--- a/fxbarcode/BUILD.gn ++++ b/fxbarcode/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -131,14 +131,13 @@ source_set("fxbarcode") { + "qrcode/BC_QRCoderMode.h", + "qrcode/BC_QRCoderVersion.cpp", + "qrcode/BC_QRCoderVersion.h", +- "utils.h", + ] + deps = [ + "../core/fxcrt", + "../core/fxge", + "../third_party:bigint", + ] +- configs += [ "../:pdfium_core_config" ] ++ configs += [ "../:pdfium_strict_config" ] + visibility = [ "../*" ] + } + +diff --git a/fxbarcode/cbc_codabar.cpp b/fxbarcode/cbc_codabar.cpp +index ad4d21c8c..cb4b8758b 100644 +--- a/fxbarcode/cbc_codabar.cpp ++++ b/fxbarcode/cbc_codabar.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -23,33 +23,27 @@ + + #include + +-#include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/fx_coordinates.h" + #include "fxbarcode/oned/BC_OnedCodaBarWriter.h" +-#include "third_party/base/ptr_util.h" + + CBC_Codabar::CBC_Codabar() +- : CBC_OneCode(pdfium::MakeUnique()) {} ++ : CBC_OneCode(std::make_unique()) {} + +-CBC_Codabar::~CBC_Codabar() {} ++CBC_Codabar::~CBC_Codabar() = default; + + bool CBC_Codabar::Encode(WideStringView contents) { +- if (contents.IsEmpty() || contents.GetLength() > kMaxInputLengthBytes) ++ auto* pWriter = GetOnedCodaBarWriter(); ++ if (!pWriter->CheckContentValidity(contents)) + return false; + +- BCFORMAT format = BCFORMAT_CODABAR; +- int32_t outWidth = 0; +- int32_t outHeight = 0; +- m_renderContents = GetOnedCodaBarWriter()->FilterContents(contents); ++ m_renderContents = pWriter->FilterContents(contents); + ByteString byteString = m_renderContents.ToUTF8(); +- auto* pWriter = GetOnedCodaBarWriter(); +- std::unique_ptr data( +- pWriter->Encode(byteString, format, outWidth, outHeight)); +- return data && pWriter->RenderResult(m_renderContents.AsStringView(), +- data.get(), outWidth); ++ return pWriter->RenderResult(m_renderContents.AsStringView(), ++ pWriter->Encode(byteString)); + } + + bool CBC_Codabar::RenderDevice(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) { ++ const CFX_Matrix& matrix) { + auto* pWriter = GetOnedCodaBarWriter(); + WideString renderCon = + pWriter->encodedContents(m_renderContents.AsStringView()); +@@ -57,7 +51,7 @@ bool CBC_Codabar::RenderDevice(CFX_RenderDevice* device, + } + + BC_TYPE CBC_Codabar::GetType() { +- return BC_CODABAR; ++ return BC_TYPE::kCodabar; + } + + CBC_OnedCodaBarWriter* CBC_Codabar::GetOnedCodaBarWriter() { +diff --git a/fxbarcode/cbc_codabar.h b/fxbarcode/cbc_codabar.h +index a3e1df4a0..7b7448614 100644 +--- a/fxbarcode/cbc_codabar.h ++++ b/fxbarcode/cbc_codabar.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,12 @@ + #ifndef FXBARCODE_CBC_CODABAR_H_ + #define FXBARCODE_CBC_CODABAR_H_ + +-#include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/widestring.h" ++#include "fxbarcode/BC_Library.h" + #include "fxbarcode/cbc_onecode.h" + + class CBC_OnedCodaBarWriter; ++class CFX_Matrix; + + class CBC_Codabar final : public CBC_OneCode { + public: +@@ -22,7 +23,7 @@ class CBC_Codabar final : public CBC_OneCode { + BC_TYPE GetType() override; + bool Encode(WideStringView contents) override; + bool RenderDevice(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) override; ++ const CFX_Matrix& matrix) override; + + private: + CBC_OnedCodaBarWriter* GetOnedCodaBarWriter(); +diff --git a/fxbarcode/cbc_code128.cpp b/fxbarcode/cbc_code128.cpp +index c363c940d..8b2b65dee 100644 +--- a/fxbarcode/cbc_code128.cpp ++++ b/fxbarcode/cbc_code128.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -23,43 +23,37 @@ + + #include + +-#include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/fx_coordinates.h" + #include "fxbarcode/oned/BC_OnedCode128Writer.h" +-#include "third_party/base/ptr_util.h" + + CBC_Code128::CBC_Code128(BC_TYPE type) +- : CBC_OneCode(pdfium::MakeUnique(type)) {} ++ : CBC_OneCode(std::make_unique(type)) {} + +-CBC_Code128::~CBC_Code128() {} ++CBC_Code128::~CBC_Code128() = default; + + bool CBC_Code128::Encode(WideStringView contents) { +- if (contents.IsEmpty() || contents.GetLength() > kMaxInputLengthBytes) ++ auto* pWriter = GetOnedCode128Writer(); ++ if (!pWriter->CheckContentValidity(contents)) + return false; + +- BCFORMAT format = BCFORMAT_CODE_128; +- int32_t outWidth = 0; +- int32_t outHeight = 0; +- auto* pWriter = GetOnedCode128Writer(); + WideString content(contents); +- if (contents.GetLength() % 2 && pWriter->GetType() == BC_CODE128_C) ++ if (contents.GetLength() % 2 && pWriter->GetType() == BC_TYPE::kCode128C) + content += '0'; + + m_renderContents = pWriter->FilterContents(content.AsStringView()); + ByteString byteString = m_renderContents.ToUTF8(); +- std::unique_ptr data( +- pWriter->Encode(byteString, format, outWidth, outHeight)); +- return data && pWriter->RenderResult(m_renderContents.AsStringView(), +- data.get(), outWidth); ++ return pWriter->RenderResult(m_renderContents.AsStringView(), ++ pWriter->Encode(byteString)); + } + + bool CBC_Code128::RenderDevice(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) { ++ const CFX_Matrix& matrix) { + return GetOnedCode128Writer()->RenderDeviceResult( + device, matrix, m_renderContents.AsStringView()); + } + + BC_TYPE CBC_Code128::GetType() { +- return BC_CODE128; ++ return BC_TYPE::kCode128; + } + + CBC_OnedCode128Writer* CBC_Code128::GetOnedCode128Writer() { +diff --git a/fxbarcode/cbc_code128.h b/fxbarcode/cbc_code128.h +index 2ccf07d40..81710c8d1 100644 +--- a/fxbarcode/cbc_code128.h ++++ b/fxbarcode/cbc_code128.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,12 @@ + #ifndef FXBARCODE_CBC_CODE128_H_ + #define FXBARCODE_CBC_CODE128_H_ + +-#include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/widestring.h" ++#include "fxbarcode/BC_Library.h" + #include "fxbarcode/cbc_onecode.h" + + class CBC_OnedCode128Writer; ++class CFX_Matrix; + + class CBC_Code128 final : public CBC_OneCode { + public: +@@ -22,7 +23,7 @@ class CBC_Code128 final : public CBC_OneCode { + BC_TYPE GetType() override; + bool Encode(WideStringView contents) override; + bool RenderDevice(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) override; ++ const CFX_Matrix& matrix) override; + + private: + CBC_OnedCode128Writer* GetOnedCode128Writer(); +diff --git a/fxbarcode/cbc_code39.cpp b/fxbarcode/cbc_code39.cpp +index 2138d1854..229048ebb 100644 +--- a/fxbarcode/cbc_code39.cpp ++++ b/fxbarcode/cbc_code39.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -23,34 +23,28 @@ + + #include + +-#include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/fx_coordinates.h" + #include "fxbarcode/oned/BC_OnedCode39Writer.h" +-#include "third_party/base/ptr_util.h" + + CBC_Code39::CBC_Code39() +- : CBC_OneCode(pdfium::MakeUnique()) {} ++ : CBC_OneCode(std::make_unique()) {} + +-CBC_Code39::~CBC_Code39() {} ++CBC_Code39::~CBC_Code39() = default; + + bool CBC_Code39::Encode(WideStringView contents) { +- if (contents.IsEmpty() || contents.GetLength() > kMaxInputLengthBytes) ++ auto* pWriter = GetOnedCode39Writer(); ++ if (!pWriter->CheckContentValidity(contents)) + return false; + +- BCFORMAT format = BCFORMAT_CODE_39; +- int32_t outWidth = 0; +- int32_t outHeight = 0; +- auto* pWriter = GetOnedCode39Writer(); + WideString filtercontents = pWriter->FilterContents(contents); + m_renderContents = pWriter->RenderTextContents(contents); + ByteString byteString = filtercontents.ToUTF8(); +- std::unique_ptr data( +- pWriter->Encode(byteString, format, outWidth, outHeight)); +- return data && pWriter->RenderResult(m_renderContents.AsStringView(), +- data.get(), outWidth); ++ return pWriter->RenderResult(m_renderContents.AsStringView(), ++ pWriter->Encode(byteString)); + } + + bool CBC_Code39::RenderDevice(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) { ++ const CFX_Matrix& matrix) { + auto* pWriter = GetOnedCode39Writer(); + WideString renderCon; + if (!pWriter->encodedContents(m_renderContents.AsStringView(), &renderCon)) +@@ -59,7 +53,7 @@ bool CBC_Code39::RenderDevice(CFX_RenderDevice* device, + } + + BC_TYPE CBC_Code39::GetType() { +- return BC_CODE39; ++ return BC_TYPE::kCode39; + } + + CBC_OnedCode39Writer* CBC_Code39::GetOnedCode39Writer() { +diff --git a/fxbarcode/cbc_code39.h b/fxbarcode/cbc_code39.h +index ab4829213..0767bd7ec 100644 +--- a/fxbarcode/cbc_code39.h ++++ b/fxbarcode/cbc_code39.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,13 @@ + #ifndef FXBARCODE_CBC_CODE39_H_ + #define FXBARCODE_CBC_CODE39_H_ + +-#include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/widestring.h" ++#include "fxbarcode/BC_Library.h" + #include "fxbarcode/cbc_onecode.h" + + class CBC_OnedCode39Writer; ++class CFX_Matrix; ++class CFX_RenderDevice; + + class CBC_Code39 final : public CBC_OneCode { + public: +@@ -23,7 +24,7 @@ class CBC_Code39 final : public CBC_OneCode { + BC_TYPE GetType() override; + bool Encode(WideStringView contents) override; + bool RenderDevice(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) override; ++ const CFX_Matrix& matrix) override; + + private: + CBC_OnedCode39Writer* GetOnedCode39Writer(); +diff --git a/fxbarcode/cbc_codebase.cpp b/fxbarcode/cbc_codebase.cpp +index 305d09b9c..78403c79e 100644 +--- a/fxbarcode/cbc_codebase.cpp ++++ b/fxbarcode/cbc_codebase.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -28,10 +28,10 @@ + CBC_CodeBase::CBC_CodeBase(std::unique_ptr pWriter) + : m_pBCWriter(std::move(pWriter)) {} + +-CBC_CodeBase::~CBC_CodeBase() {} ++CBC_CodeBase::~CBC_CodeBase() = default; + +-bool CBC_CodeBase::SetTextLocation(BC_TEXT_LOC location) { +- return m_pBCWriter->SetTextLocation(location); ++void CBC_CodeBase::SetTextLocation(BC_TEXT_LOC location) { ++ m_pBCWriter->SetTextLocation(location); + } + + bool CBC_CodeBase::SetWideNarrowRatio(int8_t ratio) { +@@ -50,8 +50,8 @@ bool CBC_CodeBase::SetErrorCorrectionLevel(int32_t level) { + return m_pBCWriter->SetErrorCorrectionLevel(level); + } + +-bool CBC_CodeBase::SetCharEncoding(int32_t encoding) { +- return m_pBCWriter->SetCharEncoding(encoding); ++void CBC_CodeBase::SetCharEncoding(BC_CHAR_ENCODING encoding) { ++ m_pBCWriter->SetCharEncoding(encoding); + } + + bool CBC_CodeBase::SetModuleHeight(int32_t moduleHeight) { +@@ -62,10 +62,10 @@ bool CBC_CodeBase::SetModuleWidth(int32_t moduleWidth) { + return m_pBCWriter->SetModuleWidth(moduleWidth); + } + +-bool CBC_CodeBase::SetHeight(int32_t height) { ++void CBC_CodeBase::SetHeight(int32_t height) { + return m_pBCWriter->SetHeight(height); + } + +-bool CBC_CodeBase::SetWidth(int32_t width) { ++void CBC_CodeBase::SetWidth(int32_t width) { + return m_pBCWriter->SetWidth(width); + } +diff --git a/fxbarcode/cbc_codebase.h b/fxbarcode/cbc_codebase.h +index d3e21dc3a..250cbf3ce 100644 +--- a/fxbarcode/cbc_codebase.h ++++ b/fxbarcode/cbc_codebase.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,16 +7,15 @@ + #ifndef FXBARCODE_CBC_CODEBASE_H_ + #define FXBARCODE_CBC_CODEBASE_H_ + ++#include ++ + #include + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxcrt/widestring.h" ++#include "core/fxge/dib/fx_dib.h" + #include "fxbarcode/BC_Library.h" + +-class CBC_Reader; + class CBC_Writer; +-class CFX_DIBitmap; + class CFX_Matrix; + class CFX_RenderDevice; + +@@ -28,18 +27,18 @@ class CBC_CodeBase { + virtual BC_TYPE GetType() = 0; + virtual bool Encode(WideStringView contents) = 0; + virtual bool RenderDevice(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) = 0; ++ const CFX_Matrix& matrix) = 0; + +- bool SetTextLocation(BC_TEXT_LOC location); ++ void SetTextLocation(BC_TEXT_LOC location); + bool SetWideNarrowRatio(int8_t ratio); + bool SetStartChar(char start); + bool SetEndChar(char end); + bool SetErrorCorrectionLevel(int32_t level); +- bool SetCharEncoding(int32_t encoding); ++ void SetCharEncoding(BC_CHAR_ENCODING encoding); + bool SetModuleHeight(int32_t moduleHeight); + bool SetModuleWidth(int32_t moduleWidth); +- bool SetHeight(int32_t height); +- bool SetWidth(int32_t width); ++ void SetHeight(int32_t height); ++ void SetWidth(int32_t width); + + protected: + std::unique_ptr m_pBCWriter; +diff --git a/fxbarcode/cbc_datamatrix.cpp b/fxbarcode/cbc_datamatrix.cpp +index e8695d2b4..e9a747b1f 100644 +--- a/fxbarcode/cbc_datamatrix.cpp ++++ b/fxbarcode/cbc_datamatrix.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -21,13 +21,16 @@ + + #include "fxbarcode/cbc_datamatrix.h" + +-#include ++#include + ++#include ++ ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_coordinates.h" + #include "fxbarcode/datamatrix/BC_DataMatrixWriter.h" +-#include "third_party/base/ptr_util.h" + + CBC_DataMatrix::CBC_DataMatrix() +- : CBC_CodeBase(pdfium::MakeUnique()) {} ++ : CBC_CodeBase(std::make_unique()) {} + + CBC_DataMatrix::~CBC_DataMatrix() = default; + +@@ -35,19 +38,19 @@ bool CBC_DataMatrix::Encode(WideStringView contents) { + int32_t width; + int32_t height; + auto* pWriter = GetDataMatrixWriter(); +- std::vector data = ++ DataVector data = + pWriter->Encode(WideString(contents), &width, &height); + return pWriter->RenderResult(data, width, height); + } + + bool CBC_DataMatrix::RenderDevice(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) { ++ const CFX_Matrix& matrix) { + GetDataMatrixWriter()->RenderDeviceResult(device, matrix); + return true; + } + + BC_TYPE CBC_DataMatrix::GetType() { +- return BC_DATAMATRIX; ++ return BC_TYPE::kDataMatrix; + } + + CBC_DataMatrixWriter* CBC_DataMatrix::GetDataMatrixWriter() { +diff --git a/fxbarcode/cbc_datamatrix.h b/fxbarcode/cbc_datamatrix.h +index c504c959a..940bad364 100644 +--- a/fxbarcode/cbc_datamatrix.h ++++ b/fxbarcode/cbc_datamatrix.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,12 @@ + #ifndef FXBARCODE_CBC_DATAMATRIX_H_ + #define FXBARCODE_CBC_DATAMATRIX_H_ + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/widestring.h" + #include "fxbarcode/cbc_codebase.h" + + class CBC_DataMatrixWriter; ++class CFX_Matrix; ++class CFX_RenderDevice; + + class CBC_DataMatrix final : public CBC_CodeBase { + public: +@@ -21,7 +22,7 @@ class CBC_DataMatrix final : public CBC_CodeBase { + // CBC_OneCode: + bool Encode(WideStringView contents) override; + bool RenderDevice(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) override; ++ const CFX_Matrix& matrix) override; + BC_TYPE GetType() override; + + private: +diff --git a/fxbarcode/cbc_ean13.cpp b/fxbarcode/cbc_ean13.cpp +index 934e61cdc..956d2dfb2 100644 +--- a/fxbarcode/cbc_ean13.cpp ++++ b/fxbarcode/cbc_ean13.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -24,19 +24,13 @@ + #include + + #include "fxbarcode/oned/BC_OnedEAN13Writer.h" +-#include "third_party/base/ptr_util.h" + +-CBC_EAN13::CBC_EAN13() +- : CBC_EANCode(pdfium::MakeUnique()) {} ++CBC_EAN13::CBC_EAN13() : CBC_EANCode(std::make_unique()) {} + + CBC_EAN13::~CBC_EAN13() = default; + + BC_TYPE CBC_EAN13::GetType() { +- return BC_EAN13; +-} +- +-BCFORMAT CBC_EAN13::GetFormat() const { +- return BCFORMAT_EAN_13; ++ return BC_TYPE::kEAN13; + } + + size_t CBC_EAN13::GetMaxLength() const { +diff --git a/fxbarcode/cbc_ean13.h b/fxbarcode/cbc_ean13.h +index a82a03fff..bba673ed1 100644 +--- a/fxbarcode/cbc_ean13.h ++++ b/fxbarcode/cbc_ean13.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,10 @@ + #ifndef FXBARCODE_CBC_EAN13_H_ + #define FXBARCODE_CBC_EAN13_H_ + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" +-#include "fxbarcode/cbc_eancode.h" ++#include + +-class CBC_OnedEAN13Writer; ++#include "fxbarcode/BC_Library.h" ++#include "fxbarcode/cbc_eancode.h" + + class CBC_EAN13 final : public CBC_EANCode { + public: +@@ -20,7 +19,6 @@ class CBC_EAN13 final : public CBC_EANCode { + + // CBC_EANCode: + BC_TYPE GetType() override; +- BCFORMAT GetFormat() const override; + size_t GetMaxLength() const override; + }; + +diff --git a/fxbarcode/cbc_ean8.cpp b/fxbarcode/cbc_ean8.cpp +index 84ed672d2..b3ee746cc 100644 +--- a/fxbarcode/cbc_ean8.cpp ++++ b/fxbarcode/cbc_ean8.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -24,18 +24,13 @@ + #include + + #include "fxbarcode/oned/BC_OnedEAN8Writer.h" +-#include "third_party/base/ptr_util.h" + +-CBC_EAN8::CBC_EAN8() : CBC_EANCode(pdfium::MakeUnique()) {} ++CBC_EAN8::CBC_EAN8() : CBC_EANCode(std::make_unique()) {} + + CBC_EAN8::~CBC_EAN8() = default; + + BC_TYPE CBC_EAN8::GetType() { +- return BC_EAN8; +-} +- +-BCFORMAT CBC_EAN8::GetFormat() const { +- return BCFORMAT_EAN_8; ++ return BC_TYPE::kEAN8; + } + + size_t CBC_EAN8::GetMaxLength() const { +diff --git a/fxbarcode/cbc_ean8.h b/fxbarcode/cbc_ean8.h +index 57c06fa04..d96282a1a 100644 +--- a/fxbarcode/cbc_ean8.h ++++ b/fxbarcode/cbc_ean8.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,10 @@ + #ifndef FXBARCODE_CBC_EAN8_H_ + #define FXBARCODE_CBC_EAN8_H_ + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" +-#include "fxbarcode/cbc_eancode.h" ++#include + +-class CBC_OnedEAN8Writer; ++#include "fxbarcode/BC_Library.h" ++#include "fxbarcode/cbc_eancode.h" + + class CBC_EAN8 final : public CBC_EANCode { + public: +@@ -20,7 +19,6 @@ class CBC_EAN8 final : public CBC_EANCode { + + // CBC_EANCode: + BC_TYPE GetType() override; +- BCFORMAT GetFormat() const override; + size_t GetMaxLength() const override; + }; + +diff --git a/fxbarcode/cbc_eancode.cpp b/fxbarcode/cbc_eancode.cpp +index 6e44f8122..261b75324 100644 +--- a/fxbarcode/cbc_eancode.cpp ++++ b/fxbarcode/cbc_eancode.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,7 +8,7 @@ + + #include + +-#include "core/fxcrt/fx_memory_wrappers.h" ++#include "fxbarcode/BC_Library.h" + #include "fxbarcode/oned/BC_OnedEANWriter.h" + + CBC_EANCode::CBC_EANCode(std::unique_ptr pWriter) +@@ -21,24 +21,19 @@ CBC_OneDimEANWriter* CBC_EANCode::GetOneDimEANWriter() { + } + + bool CBC_EANCode::Encode(WideStringView contents) { +- if (contents.IsEmpty() || contents.GetLength() > kMaxInputLengthBytes) ++ auto* pWriter = GetOneDimEANWriter(); ++ if (!pWriter->CheckContentValidity(contents)) + return false; + +- BCFORMAT format = GetFormat(); +- int32_t out_width = 0; +- int32_t out_height = 0; + m_renderContents = Preprocess(contents); + ByteString str = m_renderContents.ToUTF8(); +- auto* pWriter = GetOneDimEANWriter(); + pWriter->InitEANWriter(); +- std::unique_ptr data( +- pWriter->Encode(str, format, out_width, out_height)); +- return data && pWriter->RenderResult(m_renderContents.AsStringView(), +- data.get(), out_width); ++ return pWriter->RenderResult(m_renderContents.AsStringView(), ++ pWriter->Encode(str)); + } + + bool CBC_EANCode::RenderDevice(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) { ++ const CFX_Matrix& matrix) { + return GetOneDimEANWriter()->RenderDeviceResult( + device, matrix, m_renderContents.AsStringView()); + } +diff --git a/fxbarcode/cbc_eancode.h b/fxbarcode/cbc_eancode.h +index 572bb4250..1a295463e 100644 +--- a/fxbarcode/cbc_eancode.h ++++ b/fxbarcode/cbc_eancode.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,9 +9,8 @@ + + #include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" + #include "fxbarcode/cbc_onecode.h" +-#include "fxbarcode/utils.h" + + class CBC_OneDimEANWriter; + +@@ -20,13 +19,12 @@ class CBC_EANCode : public CBC_OneCode { + explicit CBC_EANCode(std::unique_ptr pWriter); + ~CBC_EANCode() override; + +- virtual BCFORMAT GetFormat() const = 0; + virtual size_t GetMaxLength() const = 0; + + // CBC_EANCode: + bool Encode(WideStringView contents) override; + bool RenderDevice(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) override; ++ const CFX_Matrix& matrix) override; + + protected: + CBC_OneDimEANWriter* GetOneDimEANWriter(); +diff --git a/fxbarcode/cbc_onecode.cpp b/fxbarcode/cbc_onecode.cpp +index 05501bd59..220e36cf9 100644 +--- a/fxbarcode/cbc_onecode.cpp ++++ b/fxbarcode/cbc_onecode.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -28,15 +28,7 @@ + CBC_OneCode::CBC_OneCode(std::unique_ptr pWriter) + : CBC_CodeBase(std::move(pWriter)) {} + +-CBC_OneCode::~CBC_OneCode() {} +- +-bool CBC_OneCode::CheckContentValidity(WideStringView contents) { +- return GetOneDimWriter()->CheckContentValidity(contents); +-} +- +-WideString CBC_OneCode::FilterContents(WideStringView contents) { +- return GetOneDimWriter()->FilterContents(contents); +-} ++CBC_OneCode::~CBC_OneCode() = default; + + void CBC_OneCode::SetPrintChecksum(bool checksum) { + GetOneDimWriter()->SetPrintChecksum(checksum); +diff --git a/fxbarcode/cbc_onecode.h b/fxbarcode/cbc_onecode.h +index 26aa9430f..1eb313ff5 100644 +--- a/fxbarcode/cbc_onecode.h ++++ b/fxbarcode/cbc_onecode.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,11 @@ + #ifndef FXBARCODE_CBC_ONECODE_H_ + #define FXBARCODE_CBC_ONECODE_H_ + ++#include ++ + #include + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxge/dib/fx_dib.h" + #include "fxbarcode/cbc_codebase.h" + + class CBC_OneDimWriter; +@@ -18,16 +19,9 @@ class CFX_Font; + + class CBC_OneCode : public CBC_CodeBase { + public: +- // Limit the size of 1D barcodes. Typical 1D barcodes are short so this should +- // be sufficient for most use cases. +- static constexpr size_t kMaxInputLengthBytes = 8192; +- + explicit CBC_OneCode(std::unique_ptr pWriter); + ~CBC_OneCode() override; + +- virtual bool CheckContentValidity(WideStringView contents); +- virtual WideString FilterContents(WideStringView contents); +- + void SetPrintChecksum(bool checksum); + void SetDataLength(int32_t length); + void SetCalChecksum(bool calc); +diff --git a/fxbarcode/cbc_pdf417i.cpp b/fxbarcode/cbc_pdf417i.cpp +index f91f5c616..cb237a9f6 100644 +--- a/fxbarcode/cbc_pdf417i.cpp ++++ b/fxbarcode/cbc_pdf417i.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -21,10 +21,12 @@ + + #include "fxbarcode/cbc_pdf417i.h" + +-#include ++#include + ++#include ++ ++#include "core/fxcrt/data_vector.h" + #include "fxbarcode/pdf417/BC_PDF417Writer.h" +-#include "third_party/base/ptr_util.h" + + namespace { + +@@ -35,9 +37,9 @@ constexpr size_t kMaxPDF417InputLengthBytes = 2710; + } // namespace + + CBC_PDF417I::CBC_PDF417I() +- : CBC_CodeBase(pdfium::MakeUnique()) {} ++ : CBC_CodeBase(std::make_unique()) {} + +-CBC_PDF417I::~CBC_PDF417I() {} ++CBC_PDF417I::~CBC_PDF417I() = default; + + bool CBC_PDF417I::Encode(WideStringView contents) { + if (contents.GetLength() > kMaxPDF417InputLengthBytes) +@@ -46,18 +48,18 @@ bool CBC_PDF417I::Encode(WideStringView contents) { + int32_t width; + int32_t height; + auto* pWriter = GetPDF417Writer(); +- std::vector data = pWriter->Encode(contents, &width, &height); ++ DataVector data = pWriter->Encode(contents, &width, &height); + return pWriter->RenderResult(data, width, height); + } + + bool CBC_PDF417I::RenderDevice(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) { ++ const CFX_Matrix& matrix) { + GetPDF417Writer()->RenderDeviceResult(device, matrix); + return true; + } + + BC_TYPE CBC_PDF417I::GetType() { +- return BC_PDF417; ++ return BC_TYPE::kPDF417; + } + + CBC_PDF417Writer* CBC_PDF417I::GetPDF417Writer() { +diff --git a/fxbarcode/cbc_pdf417i.h b/fxbarcode/cbc_pdf417i.h +index ab773f96e..d46b4ab20 100644 +--- a/fxbarcode/cbc_pdf417i.h ++++ b/fxbarcode/cbc_pdf417i.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,12 @@ + #ifndef FXBARCODE_CBC_PDF417I_H_ + #define FXBARCODE_CBC_PDF417I_H_ + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/widestring.h" + #include "fxbarcode/cbc_codebase.h" + + class CBC_PDF417Writer; ++class CFX_Matrix; ++class CFX_RenderDevice; + + class CBC_PDF417I final : public CBC_CodeBase { + public: +@@ -21,7 +22,7 @@ class CBC_PDF417I final : public CBC_CodeBase { + // CBC_CodeBase: + bool Encode(WideStringView contents) override; + bool RenderDevice(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) override; ++ const CFX_Matrix& matrix) override; + BC_TYPE GetType() override; + + private: +diff --git a/fxbarcode/cbc_pdf417i_unittest.cpp b/fxbarcode/cbc_pdf417i_unittest.cpp +index 94e69fbaa..dd71b6e9e 100644 +--- a/fxbarcode/cbc_pdf417i_unittest.cpp ++++ b/fxbarcode/cbc_pdf417i_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/cbc_qrcode.cpp b/fxbarcode/cbc_qrcode.cpp +index 5e60fa324..841cf9e67 100644 +--- a/fxbarcode/cbc_qrcode.cpp ++++ b/fxbarcode/cbc_qrcode.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -21,13 +21,14 @@ + + #include "fxbarcode/cbc_qrcode.h" + +-#include ++#include + ++#include ++ ++#include "core/fxcrt/data_vector.h" + #include "fxbarcode/qrcode/BC_QRCodeWriter.h" +-#include "third_party/base/ptr_util.h" + +-CBC_QRCode::CBC_QRCode() +- : CBC_CodeBase(pdfium::MakeUnique()) {} ++CBC_QRCode::CBC_QRCode() : CBC_CodeBase(std::make_unique()) {} + + CBC_QRCode::~CBC_QRCode() = default; + +@@ -35,19 +36,19 @@ bool CBC_QRCode::Encode(WideStringView contents) { + int32_t width; + int32_t height; + CBC_QRCodeWriter* pWriter = GetQRCodeWriter(); +- std::vector data = pWriter->Encode( ++ DataVector data = pWriter->Encode( + contents, pWriter->error_correction_level(), &width, &height); + return pWriter->RenderResult(data, width, height); + } + + bool CBC_QRCode::RenderDevice(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) { ++ const CFX_Matrix& matrix) { + GetQRCodeWriter()->RenderDeviceResult(device, matrix); + return true; + } + + BC_TYPE CBC_QRCode::GetType() { +- return BC_QR_CODE; ++ return BC_TYPE::kQRCode; + } + + CBC_QRCodeWriter* CBC_QRCode::GetQRCodeWriter() { +diff --git a/fxbarcode/cbc_qrcode.h b/fxbarcode/cbc_qrcode.h +index c541b7ef5..fcb4302ec 100644 +--- a/fxbarcode/cbc_qrcode.h ++++ b/fxbarcode/cbc_qrcode.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,12 @@ + #ifndef FXBARCODE_CBC_QRCODE_H_ + #define FXBARCODE_CBC_QRCODE_H_ + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/widestring.h" + #include "fxbarcode/cbc_codebase.h" + + class CBC_QRCodeWriter; ++class CFX_Matrix; ++class CFX_RenderDevice; + + class CBC_QRCode final : public CBC_CodeBase { + public: +@@ -21,7 +22,7 @@ class CBC_QRCode final : public CBC_CodeBase { + // CBC_CodeBase: + bool Encode(WideStringView contents) override; + bool RenderDevice(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) override; ++ const CFX_Matrix& matrix) override; + BC_TYPE GetType() override; + + private: +diff --git a/fxbarcode/cbc_upca.cpp b/fxbarcode/cbc_upca.cpp +index 3a3e87fd5..665c76c06 100644 +--- a/fxbarcode/cbc_upca.cpp ++++ b/fxbarcode/cbc_upca.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -24,18 +24,13 @@ + #include + + #include "fxbarcode/oned/BC_OnedUPCAWriter.h" +-#include "third_party/base/ptr_util.h" + +-CBC_UPCA::CBC_UPCA() : CBC_EANCode(pdfium::MakeUnique()) {} ++CBC_UPCA::CBC_UPCA() : CBC_EANCode(std::make_unique()) {} + + CBC_UPCA::~CBC_UPCA() = default; + + BC_TYPE CBC_UPCA::GetType() { +- return BC_UPCA; +-} +- +-BCFORMAT CBC_UPCA::GetFormat() const { +- return BCFORMAT_UPC_A; ++ return BC_TYPE::kUPCA; + } + + size_t CBC_UPCA::GetMaxLength() const { +diff --git a/fxbarcode/cbc_upca.h b/fxbarcode/cbc_upca.h +index bf71fc90d..f9193364f 100644 +--- a/fxbarcode/cbc_upca.h ++++ b/fxbarcode/cbc_upca.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,10 @@ + #ifndef FXBARCODE_CBC_UPCA_H_ + #define FXBARCODE_CBC_UPCA_H_ + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" +-#include "fxbarcode/cbc_eancode.h" ++#include + +-class CBC_OnedUPCAWriter; ++#include "fxbarcode/BC_Library.h" ++#include "fxbarcode/cbc_eancode.h" + + class CBC_UPCA final : public CBC_EANCode { + public: +@@ -20,7 +19,6 @@ class CBC_UPCA final : public CBC_EANCode { + + // CBC_EANCode: + BC_TYPE GetType() override; +- BCFORMAT GetFormat() const override; + size_t GetMaxLength() const override; + }; + +diff --git a/fxbarcode/cfx_barcode.cpp b/fxbarcode/cfx_barcode.cpp +index 54addad64..f7cd5f60d 100644 +--- a/fxbarcode/cfx_barcode.cpp ++++ b/fxbarcode/cfx_barcode.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -18,46 +18,47 @@ + #include "fxbarcode/cbc_pdf417i.h" + #include "fxbarcode/cbc_qrcode.h" + #include "fxbarcode/cbc_upca.h" +-#include "fxbarcode/utils.h" ++#include "third_party/base/notreached.h" + #include "third_party/base/ptr_util.h" + + namespace { + + std::unique_ptr CreateBarCodeEngineObject(BC_TYPE type) { + switch (type) { +- case BC_CODE39: +- return pdfium::MakeUnique(); +- case BC_CODABAR: +- return pdfium::MakeUnique(); +- case BC_CODE128: +- return pdfium::MakeUnique(BC_CODE128_B); +- case BC_CODE128_B: +- return pdfium::MakeUnique(BC_CODE128_B); +- case BC_CODE128_C: +- return pdfium::MakeUnique(BC_CODE128_C); +- case BC_EAN8: +- return pdfium::MakeUnique(); +- case BC_UPCA: +- return pdfium::MakeUnique(); +- case BC_EAN13: +- return pdfium::MakeUnique(); +- case BC_QR_CODE: +- return pdfium::MakeUnique(); +- case BC_PDF417: +- return pdfium::MakeUnique(); +- case BC_DATAMATRIX: +- return pdfium::MakeUnique(); +- case BC_UNKNOWN: +- default: ++ case BC_TYPE::kCode39: ++ return std::make_unique(); ++ case BC_TYPE::kCodabar: ++ return std::make_unique(); ++ case BC_TYPE::kCode128: ++ return std::make_unique(BC_TYPE::kCode128B); ++ case BC_TYPE::kCode128B: ++ return std::make_unique(BC_TYPE::kCode128B); ++ case BC_TYPE::kCode128C: ++ return std::make_unique(BC_TYPE::kCode128C); ++ case BC_TYPE::kEAN8: ++ return std::make_unique(); ++ case BC_TYPE::kUPCA: ++ return std::make_unique(); ++ case BC_TYPE::kEAN13: ++ return std::make_unique(); ++ case BC_TYPE::kQRCode: ++ return std::make_unique(); ++ case BC_TYPE::kPDF417: ++ return std::make_unique(); ++ case BC_TYPE::kDataMatrix: ++ return std::make_unique(); ++ case BC_TYPE::kUnknown: + return nullptr; + } ++ NOTREACHED(); ++ return nullptr; + } + + } // namespace + +-CFX_Barcode::CFX_Barcode() {} ++CFX_Barcode::CFX_Barcode() = default; + +-CFX_Barcode::~CFX_Barcode() {} ++CFX_Barcode::~CFX_Barcode() = default; + + std::unique_ptr CFX_Barcode::Create(BC_TYPE type) { + auto barcode = pdfium::WrapUnique(new CFX_Barcode()); // Private ctor. +@@ -66,11 +67,12 @@ std::unique_ptr CFX_Barcode::Create(BC_TYPE type) { + } + + BC_TYPE CFX_Barcode::GetType() { +- return m_pBCEngine ? m_pBCEngine->GetType() : BC_UNKNOWN; ++ return m_pBCEngine ? m_pBCEngine->GetType() : BC_TYPE::kUnknown; + } + +-bool CFX_Barcode::SetCharEncoding(BC_CHAR_ENCODING encoding) { +- return m_pBCEngine && m_pBCEngine->SetCharEncoding(encoding); ++void CFX_Barcode::SetCharEncoding(BC_CHAR_ENCODING encoding) { ++ if (m_pBCEngine) ++ m_pBCEngine->SetCharEncoding(encoding); + } + + bool CFX_Barcode::SetModuleHeight(int32_t moduleHeight) { +@@ -81,129 +83,138 @@ bool CFX_Barcode::SetModuleWidth(int32_t moduleWidth) { + return m_pBCEngine && m_pBCEngine->SetModuleWidth(moduleWidth); + } + +-bool CFX_Barcode::SetHeight(int32_t height) { +- return m_pBCEngine && m_pBCEngine->SetHeight(height); ++void CFX_Barcode::SetHeight(int32_t height) { ++ if (m_pBCEngine) ++ m_pBCEngine->SetHeight(height); + } + +-bool CFX_Barcode::SetWidth(int32_t width) { +- return m_pBCEngine && m_pBCEngine->SetWidth(width); ++void CFX_Barcode::SetWidth(int32_t width) { ++ if (m_pBCEngine) ++ m_pBCEngine->SetWidth(width); + } + + bool CFX_Barcode::SetPrintChecksum(bool checksum) { ++ if (!m_pBCEngine) ++ return false; ++ + switch (GetType()) { +- case BC_CODE39: +- case BC_CODABAR: +- case BC_CODE128: +- case BC_CODE128_B: +- case BC_CODE128_C: +- case BC_EAN8: +- case BC_EAN13: +- case BC_UPCA: +- return m_pBCEngine ? (static_cast(m_pBCEngine.get()) +- ->SetPrintChecksum(checksum), +- true) +- : false; ++ case BC_TYPE::kCode39: ++ case BC_TYPE::kCodabar: ++ case BC_TYPE::kCode128: ++ case BC_TYPE::kCode128B: ++ case BC_TYPE::kCode128C: ++ case BC_TYPE::kEAN8: ++ case BC_TYPE::kEAN13: ++ case BC_TYPE::kUPCA: ++ static_cast(m_pBCEngine.get())->SetPrintChecksum(checksum); ++ return true; + default: + return false; + } + } + + bool CFX_Barcode::SetDataLength(int32_t length) { ++ if (!m_pBCEngine) ++ return false; ++ + switch (GetType()) { +- case BC_CODE39: +- case BC_CODABAR: +- case BC_CODE128: +- case BC_CODE128_B: +- case BC_CODE128_C: +- case BC_EAN8: +- case BC_EAN13: +- case BC_UPCA: +- return m_pBCEngine ? (static_cast(m_pBCEngine.get()) +- ->SetDataLength(length), +- true) +- : false; ++ case BC_TYPE::kCode39: ++ case BC_TYPE::kCodabar: ++ case BC_TYPE::kCode128: ++ case BC_TYPE::kCode128B: ++ case BC_TYPE::kCode128C: ++ case BC_TYPE::kEAN8: ++ case BC_TYPE::kEAN13: ++ case BC_TYPE::kUPCA: ++ static_cast(m_pBCEngine.get())->SetDataLength(length); ++ return true; + default: + return false; + } + } + + bool CFX_Barcode::SetCalChecksum(bool state) { ++ if (!m_pBCEngine) ++ return false; ++ + switch (GetType()) { +- case BC_CODE39: +- case BC_CODABAR: +- case BC_CODE128: +- case BC_CODE128_B: +- case BC_CODE128_C: +- case BC_EAN8: +- case BC_EAN13: +- case BC_UPCA: +- return m_pBCEngine ? (static_cast(m_pBCEngine.get()) +- ->SetCalChecksum(state), +- true) +- : false; ++ case BC_TYPE::kCode39: ++ case BC_TYPE::kCodabar: ++ case BC_TYPE::kCode128: ++ case BC_TYPE::kCode128B: ++ case BC_TYPE::kCode128C: ++ case BC_TYPE::kEAN8: ++ case BC_TYPE::kEAN13: ++ case BC_TYPE::kUPCA: ++ static_cast(m_pBCEngine.get())->SetCalChecksum(state); ++ return true; + default: + return false; + } + } + + bool CFX_Barcode::SetFont(CFX_Font* pFont) { ++ if (!m_pBCEngine) ++ return false; ++ + switch (GetType()) { +- case BC_CODE39: +- case BC_CODABAR: +- case BC_CODE128: +- case BC_CODE128_B: +- case BC_CODE128_C: +- case BC_EAN8: +- case BC_EAN13: +- case BC_UPCA: +- return m_pBCEngine +- ? static_cast(m_pBCEngine.get())->SetFont(pFont) +- : false; ++ case BC_TYPE::kCode39: ++ case BC_TYPE::kCodabar: ++ case BC_TYPE::kCode128: ++ case BC_TYPE::kCode128B: ++ case BC_TYPE::kCode128C: ++ case BC_TYPE::kEAN8: ++ case BC_TYPE::kEAN13: ++ case BC_TYPE::kUPCA: ++ return static_cast(m_pBCEngine.get())->SetFont(pFont); + default: + return false; + } + } + + bool CFX_Barcode::SetFontSize(float size) { ++ if (!m_pBCEngine) ++ return false; ++ + switch (GetType()) { +- case BC_CODE39: +- case BC_CODABAR: +- case BC_CODE128: +- case BC_CODE128_B: +- case BC_CODE128_C: +- case BC_EAN8: +- case BC_EAN13: +- case BC_UPCA: +- return m_pBCEngine ? (static_cast(m_pBCEngine.get()) +- ->SetFontSize(size), +- true) +- : false; ++ case BC_TYPE::kCode39: ++ case BC_TYPE::kCodabar: ++ case BC_TYPE::kCode128: ++ case BC_TYPE::kCode128B: ++ case BC_TYPE::kCode128C: ++ case BC_TYPE::kEAN8: ++ case BC_TYPE::kEAN13: ++ case BC_TYPE::kUPCA: ++ static_cast(m_pBCEngine.get())->SetFontSize(size); ++ return true; + default: + return false; + } + } + + bool CFX_Barcode::SetFontColor(FX_ARGB color) { ++ if (!m_pBCEngine) ++ return false; ++ + switch (GetType()) { +- case BC_CODE39: +- case BC_CODABAR: +- case BC_CODE128: +- case BC_CODE128_B: +- case BC_CODE128_C: +- case BC_EAN8: +- case BC_EAN13: +- case BC_UPCA: +- return m_pBCEngine ? (static_cast(m_pBCEngine.get()) +- ->SetFontColor(color), +- true) +- : false; ++ case BC_TYPE::kCode39: ++ case BC_TYPE::kCodabar: ++ case BC_TYPE::kCode128: ++ case BC_TYPE::kCode128B: ++ case BC_TYPE::kCode128C: ++ case BC_TYPE::kEAN8: ++ case BC_TYPE::kEAN13: ++ case BC_TYPE::kUPCA: ++ static_cast(m_pBCEngine.get())->SetFontColor(color); ++ return true; + default: + return false; + } + } + +-bool CFX_Barcode::SetTextLocation(BC_TEXT_LOC location) { +- return m_pBCEngine && m_pBCEngine->SetTextLocation(location); ++void CFX_Barcode::SetTextLocation(BC_TEXT_LOC location) { ++ if (m_pBCEngine) ++ m_pBCEngine->SetTextLocation(location); + } + + bool CFX_Barcode::SetWideNarrowRatio(int8_t ratio) { +@@ -227,6 +238,6 @@ bool CFX_Barcode::Encode(WideStringView contents) { + } + + bool CFX_Barcode::RenderDevice(CFX_RenderDevice* device, +- const CFX_Matrix* matrix) { ++ const CFX_Matrix& matrix) { + return m_pBCEngine && m_pBCEngine->RenderDevice(device, matrix); + } +diff --git a/fxbarcode/cfx_barcode.h b/fxbarcode/cfx_barcode.h +index 14204ea34..dda54e948 100644 +--- a/fxbarcode/cfx_barcode.h ++++ b/fxbarcode/cfx_barcode.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,12 @@ + #ifndef FXBARCODE_CFX_BARCODE_H_ + #define FXBARCODE_CFX_BARCODE_H_ + ++#include ++ + #include + +-#include "core/fxcrt/fx_coordinates.h" +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxcrt/widestring.h" ++#include "core/fxge/dib/fx_dib.h" + #include "fxbarcode/BC_Library.h" + + class CBC_CodeBase; +@@ -28,15 +28,13 @@ class CFX_Barcode { + BC_TYPE GetType(); + bool Encode(WideStringView contents); + +- bool RenderDevice(CFX_RenderDevice* device, const CFX_Matrix* matrix); +- +- bool SetCharEncoding(BC_CHAR_ENCODING encoding); ++ bool RenderDevice(CFX_RenderDevice* device, const CFX_Matrix& matrix); + ++ void SetCharEncoding(BC_CHAR_ENCODING encoding); + bool SetModuleHeight(int32_t moduleHeight); + bool SetModuleWidth(int32_t moduleWidth); +- +- bool SetHeight(int32_t height); +- bool SetWidth(int32_t width); ++ void SetHeight(int32_t height); ++ void SetWidth(int32_t width); + + bool SetPrintChecksum(bool checksum); + bool SetDataLength(int32_t length); +@@ -46,8 +44,7 @@ class CFX_Barcode { + bool SetFontSize(float size); + bool SetFontColor(FX_ARGB color); + +- bool SetTextLocation(BC_TEXT_LOC location); +- ++ void SetTextLocation(BC_TEXT_LOC location); + bool SetWideNarrowRatio(int8_t ratio); + bool SetStartChar(char start); + bool SetEndChar(char end); +diff --git a/fxbarcode/cfx_barcode_unittest.cpp b/fxbarcode/cfx_barcode_unittest.cpp +index 7569bf286..d7a43c263 100644 +--- a/fxbarcode/cfx_barcode_unittest.cpp ++++ b/fxbarcode/cfx_barcode_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,19 +16,18 @@ + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/utils/bitmap_saver.h" + #include "testing/utils/hash.h" +-#include "third_party/base/ptr_util.h" + + class BarcodeTest : public testing::Test { + public: + void SetUp() override { + BC_Library_Init(); + +- auto device = pdfium::MakeUnique(); ++ auto device = std::make_unique(); + auto bitmap = pdfium::MakeRetain(); +- if (bitmap->Create(640, 480, FXDIB_Rgb32)) ++ if (bitmap->Create(640, 480, FXDIB_Format::kRgb32)) + bitmap_ = bitmap; + ASSERT_TRUE(bitmap_); +- ASSERT_TRUE(device->Attach(bitmap_, false, nullptr, false)); ++ ASSERT_TRUE(device->Attach(bitmap_)); + device_ = std::move(device); + } + +@@ -47,13 +46,10 @@ class BarcodeTest : public testing::Test { + barcode_->SetWidth(418); + } + +- bool RenderDevice() { +- return barcode_->RenderDevice(device_.get(), &matrix_); +- } ++ bool RenderDevice() { return barcode_->RenderDevice(device_.get(), matrix_); } + + std::string BitmapChecksum() { +- return GenerateMD5Base16(bitmap_->GetBuffer(), +- bitmap_->GetPitch() * bitmap_->GetHeight()); ++ return GenerateMD5Base16(bitmap_->GetBuffer()); + } + + // Manually insert calls to this as needed for debugging. +@@ -69,168 +65,176 @@ class BarcodeTest : public testing::Test { + }; + + // https://crbug.com/pdfium/738 +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) ++#if defined(_SKIA_SUPPORT_) + #define MAYBE_Code39 DISABLED_Code39 + #else + #define MAYBE_Code39 Code39 + #endif + TEST_F(BarcodeTest, MAYBE_Code39) { +- Create(BC_CODE39); ++ Create(BC_TYPE::kCode39); + EXPECT_TRUE(barcode()->Encode(L"CLAMS")); + RenderDevice(); + EXPECT_EQ("cd4cd3f36da38ff58d9f621827018903", BitmapChecksum()); + } + + // https://crbug.com/pdfium/738 +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) ++#if defined(_SKIA_SUPPORT_) + #define MAYBE_CodaBar DISABLED_CodaBar + #else + #define MAYBE_CodaBar CodaBar + #endif + TEST_F(BarcodeTest, MAYBE_CodaBar) { +- Create(BC_CODABAR); ++ Create(BC_TYPE::kCodabar); + EXPECT_TRUE(barcode()->Encode(L"$123-456")); + RenderDevice(); + EXPECT_EQ("5fad4fc19f099001a0fe83c89430c977", BitmapChecksum()); + } + +-TEST_F(BarcodeTest, DISABLED_CodaBarLetters) { +- Create(BC_CODABAR); ++TEST_F(BarcodeTest, CodaBarLetters) { ++ Create(BC_TYPE::kCodabar); + EXPECT_FALSE(barcode()->Encode(L"clams")); + } + + // https://crbug.com/pdfium/738 +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) ++#if defined(_SKIA_SUPPORT_) + #define MAYBE_Code128 DISABLED_Code128 + #else + #define MAYBE_Code128 Code128 + #endif + TEST_F(BarcodeTest, MAYBE_Code128) { +- Create(BC_CODE128); ++ Create(BC_TYPE::kCode128); + EXPECT_TRUE(barcode()->Encode(L"Clams")); + RenderDevice(); + EXPECT_EQ("6351f0f6e997050e4658bbb4777aef74", BitmapChecksum()); + } + + // https://crbug.com/pdfium/738 +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) ++#if defined(_SKIA_SUPPORT_) + #define MAYBE_Code128B DISABLED_Code128B + #else + #define MAYBE_Code128B Code128B + #endif + TEST_F(BarcodeTest, MAYBE_Code128B) { +- Create(BC_CODE128_B); ++ Create(BC_TYPE::kCode128B); + EXPECT_TRUE(barcode()->Encode(L"Clams")); + RenderDevice(); + EXPECT_EQ("6351f0f6e997050e4658bbb4777aef74", BitmapChecksum()); + } + + // https://crbug.com/pdfium/738 +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) ++#if defined(_SKIA_SUPPORT_) + #define MAYBE_Code128C DISABLED_Code128C + #else + #define MAYBE_Code128C Code128C + #endif + TEST_F(BarcodeTest, MAYBE_Code128C) { +- Create(BC_CODE128_C); ++ Create(BC_TYPE::kCode128C); + EXPECT_TRUE(barcode()->Encode(L"123456")); + RenderDevice(); + EXPECT_EQ("fba730a807ba6363f9bd2bc7f8c56d1f", BitmapChecksum()); + } + +-TEST_F(BarcodeTest, DISABLED_Code128CLetters) { +- Create(BC_CODE128_C); +- EXPECT_FALSE(barcode()->Encode(L"clams")); ++// https://crbug.com/pdfium/738 ++#if defined(_SKIA_SUPPORT_) ++#define MAYBE_Code128CLetters DISABLED_Code128CLetters ++#else ++#define MAYBE_Code128CLetters Code128CLetters ++#endif ++TEST_F(BarcodeTest, MAYBE_Code128CLetters) { ++ Create(BC_TYPE::kCode128C); ++ EXPECT_TRUE(barcode()->Encode(L"clams")); ++ RenderDevice(); ++ EXPECT_EQ("6284ec8503d5a948c9518108da33cdd3", BitmapChecksum()); + } + + // https://crbug.com/pdfium/738 +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) ++#if defined(_SKIA_SUPPORT_) + #define MAYBE_Ean8 DISABLED_Ean8 + #else + #define MAYBE_Ean8 Ean8 + #endif + TEST_F(BarcodeTest, MAYBE_Ean8) { +- Create(BC_EAN8); ++ Create(BC_TYPE::kEAN8); + EXPECT_TRUE(barcode()->Encode(L"123456")); + RenderDevice(); + EXPECT_EQ("aff88491ac46ca6217d780d185300cde", BitmapChecksum()); + } + +-TEST_F(BarcodeTest, DISABLED_Ean8Letters) { +- Create(BC_EAN8); ++TEST_F(BarcodeTest, Ean8Letters) { ++ Create(BC_TYPE::kEAN8); + EXPECT_FALSE(barcode()->Encode(L"clams")); + } + + // https://crbug.com/pdfium/738 +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) ++#if defined(_SKIA_SUPPORT_) + #define MAYBE_UPCA DISABLED_UPCA + #else + #define MAYBE_UPCA UPCA + #endif + TEST_F(BarcodeTest, MAYBE_UPCA) { +- Create(BC_UPCA); ++ Create(BC_TYPE::kUPCA); + EXPECT_TRUE(barcode()->Encode(L"123456")); + RenderDevice(); + EXPECT_EQ("fe26a5714cff7ffe3f9b02183efc435b", BitmapChecksum()); + } + +-TEST_F(BarcodeTest, DISABLED_UPCALetters) { +- Create(BC_UPCA); ++TEST_F(BarcodeTest, UPCALetters) { ++ Create(BC_TYPE::kUPCA); + EXPECT_FALSE(barcode()->Encode(L"clams")); + } + + // https://crbug.com/pdfium/738 +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) ++#if defined(_SKIA_SUPPORT_) + #define MAYBE_Ean13 DISABLED_Ean13 + #else + #define MAYBE_Ean13 Ean13 + #endif + TEST_F(BarcodeTest, MAYBE_Ean13) { +- Create(BC_EAN13); ++ Create(BC_TYPE::kEAN13); + EXPECT_TRUE(barcode()->Encode(L"123456")); + RenderDevice(); + EXPECT_EQ("72d2190b98d635c32834bf67552e561e", BitmapChecksum()); + } + +-TEST_F(BarcodeTest, DISABLED_Ean13Letters) { +- Create(BC_EAN13); ++TEST_F(BarcodeTest, Ean13Letters) { ++ Create(BC_TYPE::kEAN13); + EXPECT_FALSE(barcode()->Encode(L"clams")); + } + + // https://crbug.com/pdfium/738 +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) ++#if defined(_SKIA_SUPPORT_) + #define MAYBE_Pdf417 DISABLED_Pdf417 + #else + #define MAYBE_Pdf417 Pdf417 + #endif + TEST_F(BarcodeTest, MAYBE_Pdf417) { +- Create(BC_PDF417); ++ Create(BC_TYPE::kPDF417); + EXPECT_TRUE(barcode()->Encode(L"clams")); + RenderDevice(); + EXPECT_EQ("191e35d11613901b7d5d51033689aa89", BitmapChecksum()); + } + + // https://crbug.com/pdfium/738 +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) ++#if defined(_SKIA_SUPPORT_) + #define MAYBE_DataMatrix DISABLED_DataMatrix + #else + #define MAYBE_DataMatrix DataMatrix + #endif + TEST_F(BarcodeTest, MAYBE_DataMatrix) { +- Create(BC_DATAMATRIX); ++ Create(BC_TYPE::kDataMatrix); + EXPECT_TRUE(barcode()->Encode(L"clams")); + RenderDevice(); + EXPECT_EQ("5e5cd9a680b86fcd4ffd53ed36e3c980", BitmapChecksum()); + } + + // https://crbug.com/pdfium/738 +-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_) ++#if defined(_SKIA_SUPPORT_) + #define MAYBE_QrCode DISABLED_QrCode + #else + #define MAYBE_QrCode QrCode + #endif + TEST_F(BarcodeTest, MAYBE_QrCode) { +- Create(BC_QR_CODE); ++ Create(BC_TYPE::kQRCode); + EXPECT_TRUE(barcode()->Encode(L"clams")); + RenderDevice(); + EXPECT_EQ("4751c6e0f67749fabe24f787128decee", BitmapChecksum()); +diff --git a/fxbarcode/common/BC_CommonBitMatrix.cpp b/fxbarcode/common/BC_CommonBitMatrix.cpp +index 4cf07ed7d..92dd9170b 100644 +--- a/fxbarcode/common/BC_CommonBitMatrix.cpp ++++ b/fxbarcode/common/BC_CommonBitMatrix.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,32 +22,24 @@ + + #include "fxbarcode/common/BC_CommonBitMatrix.h" + +-#include +-#include ++#include "core/fxcrt/fixed_zeroed_data_vector.h" ++#include "third_party/base/check_op.h" + +-#include "third_party/base/stl_util.h" +- +-CBC_CommonBitMatrix::CBC_CommonBitMatrix() {} +- +-void CBC_CommonBitMatrix::Init(int32_t width, int32_t height) { +- m_width = width; +- m_height = height; +- m_rowSize = (width + 31) >> 5; +- m_bits = pdfium::Vector2D(m_rowSize, m_height); ++CBC_CommonBitMatrix::CBC_CommonBitMatrix(size_t width, size_t height) ++ : m_height(height), m_rowSize((width + 31) >> 5) { ++ static constexpr int32_t kMaxBits = 1024 * 1024 * 1024; // 1 Gb. ++ CHECK_LT(m_rowSize, kMaxBits / m_height); ++ m_bits = FixedZeroedDataVector(m_rowSize * m_height); + } + + CBC_CommonBitMatrix::~CBC_CommonBitMatrix() = default; + +-bool CBC_CommonBitMatrix::Get(int32_t x, int32_t y) const { +- int32_t offset = y * m_rowSize + (x >> 5); +- if (offset >= m_rowSize * m_height || offset < 0) +- return false; +- return ((((uint32_t)m_bits[offset]) >> (x & 0x1f)) & 1) != 0; ++bool CBC_CommonBitMatrix::Get(size_t x, size_t y) const { ++ size_t offset = y * m_rowSize + (x >> 5); ++ return ((m_bits.span()[offset] >> (x & 0x1f)) & 1) != 0; + } + +-void CBC_CommonBitMatrix::Set(int32_t x, int32_t y) { +- int32_t offset = y * m_rowSize + (x >> 5); +- ASSERT(offset >= 0); +- ASSERT(offset < m_rowSize * m_height); +- m_bits[offset] |= 1 << (x & 0x1f); ++void CBC_CommonBitMatrix::Set(size_t x, size_t y) { ++ size_t offset = y * m_rowSize + (x >> 5); ++ m_bits.writable_span()[offset] |= 1u << (x & 0x1f); + } +diff --git a/fxbarcode/common/BC_CommonBitMatrix.h b/fxbarcode/common/BC_CommonBitMatrix.h +index ce40ade8e..3dabe8ae4 100644 +--- a/fxbarcode/common/BC_CommonBitMatrix.h ++++ b/fxbarcode/common/BC_CommonBitMatrix.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,27 +7,23 @@ + #ifndef FXBARCODE_COMMON_BC_COMMONBITMATRIX_H_ + #define FXBARCODE_COMMON_BC_COMMONBITMATRIX_H_ + +-#include ++#include ++#include + +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/fixed_zeroed_data_vector.h" + + class CBC_CommonBitMatrix { + public: +- CBC_CommonBitMatrix(); ++ CBC_CommonBitMatrix(size_t width, size_t height); + ~CBC_CommonBitMatrix(); + +- void Init(int32_t width, int32_t height); +- +- bool Get(int32_t x, int32_t y) const; +- void Set(int32_t x, int32_t y); +- int32_t GetWidth() const { return m_width; } +- int32_t GetHeight() const { return m_height; } ++ bool Get(size_t x, size_t y) const; ++ void Set(size_t x, size_t y); + + private: +- int32_t m_width = 0; +- int32_t m_height = 0; +- int32_t m_rowSize = 0; +- std::vector m_bits; ++ const size_t m_height; ++ const size_t m_rowSize; ++ FixedZeroedDataVector m_bits; + }; + + #endif // FXBARCODE_COMMON_BC_COMMONBITMATRIX_H_ +diff --git a/fxbarcode/common/BC_CommonByteMatrix.cpp b/fxbarcode/common/BC_CommonByteMatrix.cpp +index a05fde014..ee02b45e8 100644 +--- a/fxbarcode/common/BC_CommonByteMatrix.cpp ++++ b/fxbarcode/common/BC_CommonByteMatrix.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -23,28 +23,37 @@ + + #include + #include +-#include "third_party/base/stl_util.h" ++#include + +-CBC_CommonByteMatrix::CBC_CommonByteMatrix(int32_t width, int32_t height) ++#include "core/fxcrt/data_vector.h" ++#include "third_party/base/check_op.h" ++ ++CBC_CommonByteMatrix::CBC_CommonByteMatrix(size_t width, size_t height) + : m_width(width), m_height(height) { +- m_bytes = pdfium::Vector2D(m_height, m_width); +- clear(0xff); ++ static constexpr size_t kMaxBytes = 256 * 1024 * 1024; // 256 MB. ++ static constexpr uint8_t kDefaultFill = 0xff; ++ CHECK_LT(m_width, kMaxBytes / m_height); ++ m_bytes.resize(m_width * m_height, kDefaultFill); + } + + CBC_CommonByteMatrix::~CBC_CommonByteMatrix() = default; + +-uint8_t CBC_CommonByteMatrix::Get(int32_t x, int32_t y) const { +- return m_bytes[y * m_width + x]; ++DataVector CBC_CommonByteMatrix::TakeArray() { ++ return std::move(m_bytes); + } + +-void CBC_CommonByteMatrix::Set(int32_t x, int32_t y, int32_t value) { +- m_bytes[y * m_width + x] = (uint8_t)value; ++uint8_t CBC_CommonByteMatrix::Get(size_t x, size_t y) const { ++ const size_t offset = y * m_width + x; ++ CHECK_LT(offset, m_bytes.size()); ++ return m_bytes[offset]; + } + +-void CBC_CommonByteMatrix::Set(int32_t x, int32_t y, uint8_t value) { +- m_bytes[y * m_width + x] = value; ++void CBC_CommonByteMatrix::Set(size_t x, size_t y, uint8_t value) { ++ const size_t offset = y * m_width + x; ++ CHECK_LT(offset, m_bytes.size()); ++ m_bytes[offset] = value; + } + +-void CBC_CommonByteMatrix::clear(uint8_t value) { ++void CBC_CommonByteMatrix::Fill(uint8_t value) { + std::fill(std::begin(m_bytes), std::end(m_bytes), value); + } +diff --git a/fxbarcode/common/BC_CommonByteMatrix.h b/fxbarcode/common/BC_CommonByteMatrix.h +index c5a66a3e8..6ce611998 100644 +--- a/fxbarcode/common/BC_CommonByteMatrix.h ++++ b/fxbarcode/common/BC_CommonByteMatrix.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,29 +9,27 @@ + + #include + +-#include +- +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/data_vector.h" + #include "third_party/base/span.h" + + class CBC_CommonByteMatrix final { + public: +- CBC_CommonByteMatrix(int32_t width, int32_t height); ++ CBC_CommonByteMatrix(size_t width, size_t height); + ~CBC_CommonByteMatrix(); + +- int32_t GetWidth() const { return m_width; } +- int32_t GetHeight() const { return m_height; } ++ size_t GetWidth() const { return m_width; } ++ size_t GetHeight() const { return m_height; } + pdfium::span GetArray() const { return m_bytes; } +- uint8_t Get(int32_t x, int32_t y) const; ++ DataVector TakeArray(); + +- void Set(int32_t x, int32_t y, int32_t value); +- void Set(int32_t x, int32_t y, uint8_t value); +- void clear(uint8_t value); ++ uint8_t Get(size_t x, size_t y) const; ++ void Set(size_t x, size_t y, uint8_t value); ++ void Fill(uint8_t value); + + private: +- int32_t m_width; +- int32_t m_height; +- std::vector m_bytes; ++ const size_t m_width; ++ const size_t m_height; ++ DataVector m_bytes; + }; + + #endif // FXBARCODE_COMMON_BC_COMMONBYTEMATRIX_H_ +diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomon.cpp b/fxbarcode/common/reedsolomon/BC_ReedSolomon.cpp +index f19886e6b..c1785f2f2 100644 +--- a/fxbarcode/common/reedsolomon/BC_ReedSolomon.cpp ++++ b/fxbarcode/common/reedsolomon/BC_ReedSolomon.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -27,23 +27,21 @@ + + #include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h" + #include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h" +-#include "third_party/base/ptr_util.h" + + CBC_ReedSolomonEncoder::CBC_ReedSolomonEncoder(CBC_ReedSolomonGF256* field) + : m_field(field) { +- m_cachedGenerators.push_back(pdfium::MakeUnique( +- m_field.Get(), std::vector{1})); ++ m_cachedGenerators.push_back(std::make_unique( ++ m_field, std::vector{1})); + } + +-CBC_ReedSolomonEncoder::~CBC_ReedSolomonEncoder() {} ++CBC_ReedSolomonEncoder::~CBC_ReedSolomonEncoder() = default; + + CBC_ReedSolomonGF256Poly* CBC_ReedSolomonEncoder::BuildGenerator( + size_t degree) { + if (degree >= m_cachedGenerators.size()) { + CBC_ReedSolomonGF256Poly* lastGenerator = m_cachedGenerators.back().get(); + for (size_t d = m_cachedGenerators.size(); d <= degree; ++d) { +- CBC_ReedSolomonGF256Poly temp_poly(m_field.Get(), +- {1, m_field->Exp(d - 1)}); ++ CBC_ReedSolomonGF256Poly temp_poly(m_field, {1, m_field->Exp(d - 1)}); + auto nextGenerator = lastGenerator->Multiply(&temp_poly); + if (!nextGenerator) + return nullptr; +@@ -72,7 +70,7 @@ bool CBC_ReedSolomonEncoder::Encode(std::vector* toEncode, + for (size_t x = 0; x < dataBytes; x++) + infoCoefficients[x] = (*toEncode)[x]; + +- CBC_ReedSolomonGF256Poly info(m_field.Get(), infoCoefficients); ++ CBC_ReedSolomonGF256Poly info(m_field, infoCoefficients); + auto infoTemp = info.MultiplyByMonomial(ecBytes, 1); + if (!infoTemp) + return false; +diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomon.h b/fxbarcode/common/reedsolomon/BC_ReedSolomon.h +index 772ca3c04..fd8954ad2 100644 +--- a/fxbarcode/common/reedsolomon/BC_ReedSolomon.h ++++ b/fxbarcode/common/reedsolomon/BC_ReedSolomon.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.cpp b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.cpp +index f2d86b3bb..31e6c89b5 100644 +--- a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.cpp ++++ b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -25,7 +25,6 @@ + #include + + #include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h" +-#include "third_party/base/ptr_util.h" + + CBC_ReedSolomonGF256::CBC_ReedSolomonGF256(int32_t primitive) { + int32_t x = 1; +@@ -43,13 +42,13 @@ CBC_ReedSolomonGF256::CBC_ReedSolomonGF256(int32_t primitive) { + } + + void CBC_ReedSolomonGF256::Init() { +- m_zero = pdfium::MakeUnique( +- this, std::vector{0}); +- m_one = pdfium::MakeUnique(this, +- std::vector{1}); ++ m_zero = ++ std::make_unique(this, std::vector{0}); ++ m_one = ++ std::make_unique(this, std::vector{1}); + } + +-CBC_ReedSolomonGF256::~CBC_ReedSolomonGF256() {} ++CBC_ReedSolomonGF256::~CBC_ReedSolomonGF256() = default; + + std::unique_ptr CBC_ReedSolomonGF256::BuildMonomial( + int32_t degree, +@@ -62,7 +61,7 @@ std::unique_ptr CBC_ReedSolomonGF256::BuildMonomial( + + std::vector coefficients(degree + 1); + coefficients[0] = coefficient; +- return pdfium::MakeUnique(this, coefficients); ++ return std::make_unique(this, coefficients); + } + + // static +@@ -74,9 +73,9 @@ int32_t CBC_ReedSolomonGF256::Exp(int32_t a) { + return m_expTable[a]; + } + +-Optional CBC_ReedSolomonGF256::Inverse(int32_t a) { ++absl::optional CBC_ReedSolomonGF256::Inverse(int32_t a) { + if (a == 0) +- return {}; ++ return absl::nullopt; + return m_expTable[255 - m_logTable[a]]; + } + +diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h +index 30f552438..b4b4da81e 100644 +--- a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h ++++ b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,7 +9,7 @@ + + #include + +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CBC_ReedSolomonGF256Poly; + +@@ -25,7 +25,7 @@ class CBC_ReedSolomonGF256 { + int32_t coefficient); + static int32_t AddOrSubtract(int32_t a, int32_t b); + int32_t Exp(int32_t a); +- Optional Inverse(int32_t a); ++ absl::optional Inverse(int32_t a); + int32_t Multiply(int32_t a, int32_t b); + void Init(); + +diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.cpp b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.cpp +index 43395022c..449d0a3ec 100644 +--- a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.cpp ++++ b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -26,16 +26,16 @@ + #include + + #include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/stl_util.h" + #include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" + + CBC_ReedSolomonGF256Poly::CBC_ReedSolomonGF256Poly( + CBC_ReedSolomonGF256* field, + const std::vector& coefficients) + : m_field(field) { +- ASSERT(m_field); +- ASSERT(!coefficients.empty()); ++ DCHECK(m_field); ++ DCHECK(!coefficients.empty()); + if (coefficients.size() == 1 || coefficients.front() != 0) { + m_coefficients = coefficients; + return; +@@ -62,7 +62,7 @@ const std::vector& CBC_ReedSolomonGF256Poly::GetCoefficients() const { + } + + int32_t CBC_ReedSolomonGF256Poly::GetDegree() const { +- return pdfium::CollectionSize(m_coefficients) - 1; ++ return fxcrt::CollectionSize(m_coefficients) - 1; + } + + bool CBC_ReedSolomonGF256Poly::IsZero() const { +@@ -75,8 +75,7 @@ int32_t CBC_ReedSolomonGF256Poly::GetCoefficients(int32_t degree) const { + + std::unique_ptr CBC_ReedSolomonGF256Poly::Clone() + const { +- return pdfium::MakeUnique(m_field.Get(), +- m_coefficients); ++ return std::make_unique(m_field, m_coefficients); + } + + std::unique_ptr +@@ -100,7 +99,7 @@ CBC_ReedSolomonGF256Poly::AddOrSubtract(const CBC_ReedSolomonGF256Poly* other) { + sumDiff[i] = CBC_ReedSolomonGF256::AddOrSubtract( + smallerCoefficients[i - lengthDiff], largerCoefficients[i]); + } +- return pdfium::MakeUnique(m_field.Get(), sumDiff); ++ return std::make_unique(m_field, sumDiff); + } + + std::unique_ptr CBC_ReedSolomonGF256Poly::Multiply( +@@ -120,7 +119,7 @@ std::unique_ptr CBC_ReedSolomonGF256Poly::Multiply( + product[i + j], m_field->Multiply(aCoeff, bCoefficients[j])); + } + } +- return pdfium::MakeUnique(m_field.Get(), product); ++ return std::make_unique(m_field, product); + } + + std::unique_ptr +@@ -136,7 +135,7 @@ CBC_ReedSolomonGF256Poly::MultiplyByMonomial(int32_t degree, + for (size_t i = 0; i < size; i++) + product[i] = m_field->Multiply(m_coefficients[i], coefficient); + +- return pdfium::MakeUnique(m_field.Get(), product); ++ return std::make_unique(m_field, product); + } + + std::unique_ptr CBC_ReedSolomonGF256Poly::Divide( +@@ -152,7 +151,7 @@ std::unique_ptr CBC_ReedSolomonGF256Poly::Divide( + return nullptr; + + int32_t denominatorLeadingTerm = other->GetCoefficients(other->GetDegree()); +- Optional inverseDenominatorLeadingTeam = ++ absl::optional inverseDenominatorLeadingTeam = + m_field->Inverse(denominatorLeadingTerm); + if (!inverseDenominatorLeadingTeam.has_value()) + return nullptr; +diff --git a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h +index 7c3c7fc2f..e274c76d5 100644 +--- a/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h ++++ b/fxbarcode/common/reedsolomon/BC_ReedSolomonGF256Poly.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/datamatrix/BC_ASCIIEncoder.cpp b/fxbarcode/datamatrix/BC_ASCIIEncoder.cpp +index ed51f817c..333fffe31 100644 +--- a/fxbarcode/datamatrix/BC_ASCIIEncoder.cpp ++++ b/fxbarcode/datamatrix/BC_ASCIIEncoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -27,15 +27,15 @@ + #include "fxbarcode/datamatrix/BC_EncoderContext.h" + #include "fxbarcode/datamatrix/BC_HighLevelEncoder.h" + #include "fxbarcode/datamatrix/BC_SymbolInfo.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + namespace { + +-Optional EncodeASCIIDigits(wchar_t digit1, wchar_t digit2) { ++absl::optional EncodeASCIIDigits(wchar_t digit1, wchar_t digit2) { + if (!FXSYS_IsDecimalDigit(digit1) || !FXSYS_IsDecimalDigit(digit2)) { + // This could potentially return 0 as a sentinel value. Then this function +- // can just return wchar_t instead of Optional. +- return {}; ++ // can just return wchar_t instead of absl::optional. ++ return absl::nullopt; + } + return static_cast((digit1 - 48) * 10 + (digit2 - 48) + 130); + } +@@ -67,12 +67,12 @@ CBC_HighLevelEncoder::Encoding CBC_ASCIIEncoder::GetEncodingMode() { + bool CBC_ASCIIEncoder::Encode(CBC_EncoderContext* context) { + size_t n = DetermineConsecutiveDigitCount(context->m_msg, context->m_pos); + if (n >= 2) { +- Optional code = EncodeASCIIDigits( ++ absl::optional code = EncodeASCIIDigits( + context->m_msg[context->m_pos], context->m_msg[context->m_pos + 1]); +- if (!code) ++ if (!code.has_value()) + return false; + +- context->writeCodeword(*code); ++ context->writeCodeword(code.value()); + context->m_pos += 2; + return true; + } +diff --git a/fxbarcode/datamatrix/BC_ASCIIEncoder.h b/fxbarcode/datamatrix/BC_ASCIIEncoder.h +index 8b3c2de5d..59f44b9e3 100644 +--- a/fxbarcode/datamatrix/BC_ASCIIEncoder.h ++++ b/fxbarcode/datamatrix/BC_ASCIIEncoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/datamatrix/BC_Base256Encoder.cpp b/fxbarcode/datamatrix/BC_Base256Encoder.cpp +index ea70f682b..53eacf3c4 100644 +--- a/fxbarcode/datamatrix/BC_Base256Encoder.cpp ++++ b/fxbarcode/datamatrix/BC_Base256Encoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -73,7 +73,7 @@ bool CBC_Base256Encoder::Encode(CBC_EncoderContext* context) { + if (!context->UpdateSymbolInfo(currentSize)) + return false; + +- bool mustPad = (context->m_symbolInfo->dataCapacity() - currentSize) > 0; ++ bool mustPad = (context->m_symbolInfo->data_capacity() - currentSize) > 0; + if (context->hasMoreCharacters() || mustPad) { + if (dataCount <= 249) { + buffer.SetAt(0, static_cast(dataCount)); +diff --git a/fxbarcode/datamatrix/BC_Base256Encoder.h b/fxbarcode/datamatrix/BC_Base256Encoder.h +index 4897cc53e..04929c898 100644 +--- a/fxbarcode/datamatrix/BC_Base256Encoder.h ++++ b/fxbarcode/datamatrix/BC_Base256Encoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/datamatrix/BC_C40Encoder.cpp b/fxbarcode/datamatrix/BC_C40Encoder.cpp +index 9f20a3587..c054cfc4e 100644 +--- a/fxbarcode/datamatrix/BC_C40Encoder.cpp ++++ b/fxbarcode/datamatrix/BC_C40Encoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,12 +22,15 @@ + + #include "fxbarcode/datamatrix/BC_C40Encoder.h" + ++#include ++ + #include "core/fxcrt/fx_extension.h" + #include "fxbarcode/common/BC_CommonBitMatrix.h" + #include "fxbarcode/datamatrix/BC_Encoder.h" + #include "fxbarcode/datamatrix/BC_EncoderContext.h" + #include "fxbarcode/datamatrix/BC_HighLevelEncoder.h" + #include "fxbarcode/datamatrix/BC_SymbolInfo.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -39,7 +42,7 @@ WideString EncodeToC40Codewords(const WideString& sb) { + wchar_t cw[2]; + cw[0] = static_cast(v / 256); + cw[1] = static_cast(v % 256); +- return WideString(cw, FX_ArraySize(cw)); ++ return WideString(cw, std::size(cw)); + } + + } // namespace +@@ -67,7 +70,7 @@ bool CBC_C40Encoder::Encode(CBC_EncoderContext* context) { + return false; + + int32_t available = +- context->m_symbolInfo->dataCapacity() - curCodewordCount; ++ context->m_symbolInfo->data_capacity() - curCodewordCount; + if (!context->hasMoreCharacters()) { + if ((buffer.GetLength() % 3) == 2) { + if (available < 2 || available > 2) { +@@ -112,7 +115,7 @@ bool CBC_C40Encoder::HandleEOD(CBC_EncoderContext* context, + if (!context->UpdateSymbolInfo(curCodewordCount)) + return false; + +- int32_t available = context->m_symbolInfo->dataCapacity() - curCodewordCount; ++ int32_t available = context->m_symbolInfo->data_capacity() - curCodewordCount; + if (rest == 2) { + *buffer += (wchar_t)'\0'; + while (buffer->GetLength() >= 3) +@@ -149,7 +152,7 @@ int32_t CBC_C40Encoder::EncodeChar(wchar_t c, WideString* sb) { + *sb += (wchar_t)(c - 48 + 4); + return 1; + } +- if ((c >= 'A') && (c <= 'Z')) { ++ if (FXSYS_IsUpperASCII(c)) { + *sb += (wchar_t)(c - 65 + 14); + return 1; + } +@@ -190,7 +193,7 @@ int32_t CBC_C40Encoder::EncodeChar(wchar_t c, WideString* sb) { + int32_t CBC_C40Encoder::BacktrackOneCharacter(CBC_EncoderContext* context, + WideString* buffer, + int32_t lastCharSize) { +- ASSERT(lastCharSize >= 0); ++ DCHECK(lastCharSize >= 0); + + if (context->m_pos < 1) + return -1; +diff --git a/fxbarcode/datamatrix/BC_C40Encoder.h b/fxbarcode/datamatrix/BC_C40Encoder.h +index 8bea47ba5..a0ec9f45a 100644 +--- a/fxbarcode/datamatrix/BC_C40Encoder.h ++++ b/fxbarcode/datamatrix/BC_C40Encoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.cpp b/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.cpp +index 682d095bb..f22a154ad 100644 +--- a/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.cpp ++++ b/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -21,14 +21,21 @@ + */ + + #include "fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h" ++ + #include "fxbarcode/datamatrix/BC_Encoder.h" + #include "fxbarcode/datamatrix/BC_SymbolInfo.h" + ++namespace { ++ ++constexpr CBC_SymbolInfo::Data kSymbolDatum = {1558, 620, -1, 62, 22, 22, 36}; ++ ++} // namespace ++ + CBC_DataMatrixSymbolInfo144::CBC_DataMatrixSymbolInfo144() +- : CBC_SymbolInfo(1558, 620, 22, 22, 36, -1, 62) {} ++ : CBC_SymbolInfo(&kSymbolDatum) {} + +-CBC_DataMatrixSymbolInfo144::~CBC_DataMatrixSymbolInfo144() {} ++CBC_DataMatrixSymbolInfo144::~CBC_DataMatrixSymbolInfo144() = default; + +-size_t CBC_DataMatrixSymbolInfo144::getInterleavedBlockCount() const { ++size_t CBC_DataMatrixSymbolInfo144::GetInterleavedBlockCount() const { + return 10; + } +diff --git a/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h b/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h +index 2f445578b..4ff532edc 100644 +--- a/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h ++++ b/fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -15,7 +15,7 @@ class CBC_DataMatrixSymbolInfo144 final : public CBC_SymbolInfo { + ~CBC_DataMatrixSymbolInfo144() override; + + // CBC_SymbolInfo: +- size_t getInterleavedBlockCount() const override; ++ size_t GetInterleavedBlockCount() const override; + }; + + #endif // FXBARCODE_DATAMATRIX_BC_DATAMATRIXSYMBOLINFO144_H_ +diff --git a/fxbarcode/datamatrix/BC_DataMatrixWriter.cpp b/fxbarcode/datamatrix/BC_DataMatrixWriter.cpp +index 1d2b39e26..afc15eaf1 100644 +--- a/fxbarcode/datamatrix/BC_DataMatrixWriter.cpp ++++ b/fxbarcode/datamatrix/BC_DataMatrixWriter.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,8 +22,11 @@ + + #include "fxbarcode/datamatrix/BC_DataMatrixWriter.h" + ++#include ++ + #include + ++#include "core/fxcrt/data_vector.h" + #include "fxbarcode/BC_TwoDimWriter.h" + #include "fxbarcode/BC_Writer.h" + #include "fxbarcode/common/BC_CommonBitMatrix.h" +@@ -41,28 +44,27 @@ + #include "fxbarcode/datamatrix/BC_SymbolInfo.h" + #include "fxbarcode/datamatrix/BC_TextEncoder.h" + #include "fxbarcode/datamatrix/BC_X12Encoder.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" + + namespace { + +-std::unique_ptr encodeLowLevel( ++std::unique_ptr EncodeLowLevel( + CBC_DefaultPlacement* placement, + const CBC_SymbolInfo* symbolInfo) { +- int32_t symbolWidth = symbolInfo->getSymbolDataWidth(); +- ASSERT(symbolWidth); +- int32_t symbolHeight = symbolInfo->getSymbolDataHeight(); +- ASSERT(symbolHeight); +- int32_t width = symbolInfo->getSymbolWidth(); +- ASSERT(width); +- int32_t height = symbolInfo->getSymbolHeight(); +- ASSERT(height); +- +- auto matrix = pdfium::MakeUnique(width, height); ++ int32_t symbolWidth = symbolInfo->GetSymbolDataWidth(); ++ DCHECK(symbolWidth); ++ int32_t symbolHeight = symbolInfo->GetSymbolDataHeight(); ++ DCHECK(symbolHeight); ++ int32_t width = symbolInfo->GetSymbolWidth(); ++ DCHECK(width); ++ int32_t height = symbolInfo->GetSymbolHeight(); ++ DCHECK(height); ++ ++ auto matrix = std::make_unique(width, height); + int32_t matrixY = 0; + for (int32_t y = 0; y < symbolHeight; y++) { + int32_t matrixX; +- if ((y % symbolInfo->matrixHeight()) == 0) { ++ if ((y % symbolInfo->matrix_height()) == 0) { + matrixX = 0; + for (int32_t x = 0; x < width; x++) { + matrix->Set(matrixX, matrixY, x % 2 == 0); +@@ -72,19 +74,19 @@ std::unique_ptr encodeLowLevel( + } + matrixX = 0; + for (int32_t x = 0; x < symbolWidth; x++) { +- if (x % symbolInfo->matrixWidth() == 0) { ++ if (x % symbolInfo->matrix_width() == 0) { + matrix->Set(matrixX, matrixY, true); + matrixX++; + } +- matrix->Set(matrixX, matrixY, placement->getBit(x, y)); ++ matrix->Set(matrixX, matrixY, placement->GetBit(x, y)); + matrixX++; +- if (x % symbolInfo->matrixWidth() == symbolInfo->matrixWidth() - 1) { ++ if (x % symbolInfo->matrix_width() == symbolInfo->matrix_width() - 1) { + matrix->Set(matrixX, matrixY, y % 2 == 0); + matrixX++; + } + } + matrixY++; +- if (y % symbolInfo->matrixHeight() == symbolInfo->matrixHeight() - 1) { ++ if (y % symbolInfo->matrix_height() == symbolInfo->matrix_height() - 1) { + matrixX = 0; + for (int32_t x = 0; x < width; x++) { + matrix->Set(matrixX, matrixY, true); +@@ -107,40 +109,34 @@ bool CBC_DataMatrixWriter::SetErrorCorrectionLevel(int32_t level) { + return true; + } + +-std::vector CBC_DataMatrixWriter::Encode(const WideString& contents, +- int32_t* pOutWidth, +- int32_t* pOutHeight) { +- std::vector results; ++DataVector CBC_DataMatrixWriter::Encode(const WideString& contents, ++ int32_t* pOutWidth, ++ int32_t* pOutHeight) { + WideString encoded = CBC_HighLevelEncoder::EncodeHighLevel(contents); + if (encoded.IsEmpty()) +- return results; ++ return DataVector(); + + const CBC_SymbolInfo* pSymbolInfo = + CBC_SymbolInfo::Lookup(encoded.GetLength(), false); + if (!pSymbolInfo) +- return results; ++ return DataVector(); + + WideString codewords = + CBC_ErrorCorrection::EncodeECC200(encoded, pSymbolInfo); + if (codewords.IsEmpty()) +- return results; ++ return DataVector(); + +- int32_t width = pSymbolInfo->getSymbolDataWidth(); +- ASSERT(width); +- int32_t height = pSymbolInfo->getSymbolDataHeight(); +- ASSERT(height); ++ int32_t width = pSymbolInfo->GetSymbolDataWidth(); ++ DCHECK(width); ++ int32_t height = pSymbolInfo->GetSymbolDataHeight(); ++ DCHECK(height); + + auto placement = +- pdfium::MakeUnique(codewords, width, height); +- placement->place(); +- auto bytematrix = encodeLowLevel(placement.get(), pSymbolInfo); +- if (!bytematrix) +- return results; ++ std::make_unique(codewords, width, height); ++ auto bytematrix = EncodeLowLevel(placement.get(), pSymbolInfo); ++ DCHECK(bytematrix); + + *pOutWidth = bytematrix->GetWidth(); + *pOutHeight = bytematrix->GetHeight(); +- results = pdfium::Vector2D(*pOutWidth, *pOutHeight); +- memcpy(results.data(), bytematrix->GetArray().data(), +- *pOutWidth * *pOutHeight); +- return results; ++ return bytematrix->TakeArray(); + } +diff --git a/fxbarcode/datamatrix/BC_DataMatrixWriter.h b/fxbarcode/datamatrix/BC_DataMatrixWriter.h +index f95612ecd..ba6c59c54 100644 +--- a/fxbarcode/datamatrix/BC_DataMatrixWriter.h ++++ b/fxbarcode/datamatrix/BC_DataMatrixWriter.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,8 +7,10 @@ + #ifndef FXBARCODE_DATAMATRIX_BC_DATAMATRIXWRITER_H_ + #define FXBARCODE_DATAMATRIX_BC_DATAMATRIXWRITER_H_ + +-#include ++#include + ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/widestring.h" + #include "fxbarcode/BC_TwoDimWriter.h" + + class CBC_DataMatrixWriter final : public CBC_TwoDimWriter { +@@ -16,9 +18,9 @@ class CBC_DataMatrixWriter final : public CBC_TwoDimWriter { + CBC_DataMatrixWriter(); + ~CBC_DataMatrixWriter() override; + +- std::vector Encode(const WideString& contents, +- int32_t* pOutWidth, +- int32_t* pOutHeight); ++ DataVector Encode(const WideString& contents, ++ int32_t* pOutWidth, ++ int32_t* pOutHeight); + + // CBC_TwoDimWriter + bool SetErrorCorrectionLevel(int32_t level) override; +diff --git a/fxbarcode/datamatrix/BC_DataMatrixWriter_unittest.cpp b/fxbarcode/datamatrix/BC_DataMatrixWriter_unittest.cpp +index fa5d26c18..9613107fd 100644 +--- a/fxbarcode/datamatrix/BC_DataMatrixWriter_unittest.cpp ++++ b/fxbarcode/datamatrix/BC_DataMatrixWriter_unittest.cpp +@@ -1,12 +1,12 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fxbarcode/datamatrix/BC_DataMatrixWriter.h" + +-#include ++#include + +-#include "core/fxcrt/fx_memory.h" ++#include "core/fxcrt/data_vector.h" + #include "testing/gtest/include/gtest/gtest.h" + + class CBC_DataMatrixWriterTest : public testing::Test { +@@ -40,11 +40,11 @@ TEST_F(CBC_DataMatrixWriterTest, Encode) { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + // clang-format on +- std::vector data = writer.Encode(L"", &width, &height); +- ASSERT_EQ(FX_ArraySize(kExpectedData), data.size()); ++ DataVector data = writer.Encode(L"", &width, &height); ++ ASSERT_EQ(std::size(kExpectedData), data.size()); + ASSERT_EQ(kExpectedDimension, width); + ASSERT_EQ(kExpectedDimension, height); +- for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i) ++ for (size_t i = 0; i < std::size(kExpectedData); ++i) + EXPECT_EQ(kExpectedData[i], data[i]) << i; + } + { +@@ -67,11 +67,11 @@ TEST_F(CBC_DataMatrixWriterTest, Encode) { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + // clang-format on +- std::vector data = writer.Encode(L"helloworld", &width, &height); +- ASSERT_EQ(FX_ArraySize(kExpectedData), data.size()); ++ DataVector data = writer.Encode(L"helloworld", &width, &height); ++ ASSERT_EQ(std::size(kExpectedData), data.size()); + ASSERT_EQ(kExpectedDimension, width); + ASSERT_EQ(kExpectedDimension, height); +- for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i) ++ for (size_t i = 0; i < std::size(kExpectedData); ++i) + EXPECT_EQ(kExpectedData[i], data[i]) << i; + } + { +@@ -90,11 +90,11 @@ TEST_F(CBC_DataMatrixWriterTest, Encode) { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + // clang-format on +- std::vector data = writer.Encode(L"12345", &width, &height); +- ASSERT_EQ(FX_ArraySize(kExpectedData), data.size()); ++ DataVector data = writer.Encode(L"12345", &width, &height); ++ ASSERT_EQ(std::size(kExpectedData), data.size()); + ASSERT_EQ(kExpectedDimension, width); + ASSERT_EQ(kExpectedDimension, height); +- for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i) ++ for (size_t i = 0; i < std::size(kExpectedData); ++i) + EXPECT_EQ(kExpectedData[i], data[i]) << i; + } + { +@@ -121,16 +121,16 @@ TEST_F(CBC_DataMatrixWriterTest, Encode) { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + // clang-format on +- std::vector data = ++ DataVector data = + writer.Encode(L"abcdefghijklmnopqrst", &width, &height); +- ASSERT_EQ(FX_ArraySize(kExpectedData), data.size()); ++ ASSERT_EQ(std::size(kExpectedData), data.size()); + ASSERT_EQ(kExpectedDimension, width); + ASSERT_EQ(kExpectedDimension, height); +- for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i) ++ for (size_t i = 0; i < std::size(kExpectedData); ++i) + EXPECT_EQ(kExpectedData[i], data[i]) << i; + } + { +- std::vector data = writer.Encode(L"hello world", &width, &height); ++ DataVector data = writer.Encode(L"hello world", &width, &height); + ASSERT_TRUE(data.empty()); + } + } +@@ -147,7 +147,7 @@ TEST_F(CBC_DataMatrixWriterTest, EncodeLimitAlphaNumeric) { + + { + static constexpr int kExpectedDimension = 144; +- std::vector data = writer.Encode(input.c_str(), &width, &height); ++ DataVector data = writer.Encode(input.c_str(), &width, &height); + EXPECT_EQ(20736u, data.size()); + EXPECT_EQ(kExpectedDimension, width); + EXPECT_EQ(kExpectedDimension, height); +@@ -158,7 +158,7 @@ TEST_F(CBC_DataMatrixWriterTest, EncodeLimitAlphaNumeric) { + { + width = -1; + height = -1; +- std::vector data = writer.Encode(input.c_str(), &width, &height); ++ DataVector data = writer.Encode(input.c_str(), &width, &height); + EXPECT_EQ(0u, data.size()); + EXPECT_EQ(-1, width); + EXPECT_EQ(-1, height); +@@ -177,7 +177,7 @@ TEST_F(CBC_DataMatrixWriterTest, EncodeLimitNumbers) { + + { + static constexpr int kExpectedDimension = 144; +- std::vector data = writer.Encode(input.c_str(), &width, &height); ++ DataVector data = writer.Encode(input.c_str(), &width, &height); + EXPECT_EQ(20736u, data.size()); + EXPECT_EQ(kExpectedDimension, width); + EXPECT_EQ(kExpectedDimension, height); +@@ -188,7 +188,7 @@ TEST_F(CBC_DataMatrixWriterTest, EncodeLimitNumbers) { + { + width = -1; + height = -1; +- std::vector data = writer.Encode(input.c_str(), &width, &height); ++ DataVector data = writer.Encode(input.c_str(), &width, &height); + EXPECT_EQ(0u, data.size()); + EXPECT_EQ(-1, width); + EXPECT_EQ(-1, height); +diff --git a/fxbarcode/datamatrix/BC_DefaultPlacement.cpp b/fxbarcode/datamatrix/BC_DefaultPlacement.cpp +index 24d81f1bb..4e772ddcc 100644 +--- a/fxbarcode/datamatrix/BC_DefaultPlacement.cpp ++++ b/fxbarcode/datamatrix/BC_DefaultPlacement.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,62 +22,81 @@ + + #include "fxbarcode/datamatrix/BC_DefaultPlacement.h" + ++#include ++ + #include + ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_2d_size.h" + #include "fxbarcode/datamatrix/BC_Encoder.h" ++#include "third_party/base/check_op.h" ++ ++namespace { ++ ++size_t GetIndex(size_t col, size_t row, size_t num_cols) { ++ return row * num_cols + col; ++} ++ ++} // namespace + + CBC_DefaultPlacement::CBC_DefaultPlacement(WideString codewords, + int32_t numcols, + int32_t numrows) + : m_codewords(std::move(codewords)), + m_numrows(numrows), +- m_numcols(numcols) { +- m_bits.resize(numcols * numrows); +- for (int32_t i = 0; i < numcols * numrows; i++) { +- m_bits[i] = (uint8_t)2; +- } ++ m_numcols(numcols), ++ m_bits(Fx2DSizeOrDie(numcols, numrows), 2) { ++ CHECK_GT(m_numrows, 0); ++ CHECK_GT(m_numcols, 0); ++ Init(); + } + +-CBC_DefaultPlacement::~CBC_DefaultPlacement() {} ++CBC_DefaultPlacement::~CBC_DefaultPlacement() = default; + +-int32_t CBC_DefaultPlacement::getNumrows() { +- return m_numrows; +-} +-int32_t CBC_DefaultPlacement::getNumcols() { +- return m_numcols; +-} +-std::vector& CBC_DefaultPlacement::getBits() { +- return m_bits; +-} +-bool CBC_DefaultPlacement::getBit(int32_t col, int32_t row) { +- return m_bits[row * m_numcols + col] == 1; ++bool CBC_DefaultPlacement::GetBit(int32_t col, int32_t row) const { ++ CHECK_GE(col, 0); ++ CHECK_GE(row, 0); ++ CHECK_LT(col, m_numcols); ++ CHECK_LT(row, m_numrows); ++ return m_bits[GetIndex(col, row, m_numcols)] == 1; + } +-void CBC_DefaultPlacement::setBit(int32_t col, int32_t row, bool bit) { +- m_bits[row * m_numcols + col] = bit ? (uint8_t)1 : (uint8_t)0; ++ ++void CBC_DefaultPlacement::SetBit(int32_t col, int32_t row, bool bit) { ++ DCHECK_GE(col, 0); ++ DCHECK_GE(row, 0); ++ DCHECK_LT(col, m_numcols); ++ DCHECK_LT(row, m_numrows); ++ m_bits[GetIndex(col, row, m_numcols)] = bit ? 1 : 0; + } +-bool CBC_DefaultPlacement::hasBit(int32_t col, int32_t row) { +- return m_bits[row * m_numcols + col] != 2; ++ ++bool CBC_DefaultPlacement::HasBit(int32_t col, int32_t row) const { ++ DCHECK_GE(col, 0); ++ DCHECK_GE(row, 0); ++ DCHECK_LT(col, m_numcols); ++ DCHECK_LT(row, m_numrows); ++ return m_bits[GetIndex(col, row, m_numcols)] != 2; + } +-void CBC_DefaultPlacement::place() { ++ ++void CBC_DefaultPlacement::Init() { + int32_t pos = 0; + int32_t row = 4; + int32_t col = 0; + do { + if ((row == m_numrows) && (col == 0)) { +- corner1(pos++); ++ SetCorner1(pos++); + } + if ((row == m_numrows - 2) && (col == 0) && ((m_numcols % 4) != 0)) { +- corner2(pos++); ++ SetCorner2(pos++); + } + if ((row == m_numrows - 2) && (col == 0) && (m_numcols % 8 == 4)) { +- corner3(pos++); ++ SetCorner3(pos++); + } + if ((row == m_numrows + 4) && (col == 2) && ((m_numcols % 8) == 0)) { +- corner4(pos++); ++ SetCorner4(pos++); + } + do { +- if ((row < m_numrows) && (col >= 0) && !hasBit(col, row)) { +- utah(row, col, pos++); ++ if ((row < m_numrows) && (col >= 0) && !HasBit(col, row)) { ++ SetUtah(row, col, pos++); + } + row -= 2; + col += 2; +@@ -85,8 +104,8 @@ void CBC_DefaultPlacement::place() { + row++; + col += 3; + do { +- if ((row >= 0) && (col < m_numcols) && !hasBit(col, row)) { +- utah(row, col, pos++); ++ if ((row >= 0) && (col < m_numcols) && !HasBit(col, row)) { ++ SetUtah(row, col, pos++); + } + row += 2; + col -= 2; +@@ -94,15 +113,16 @@ void CBC_DefaultPlacement::place() { + row += 3; + col++; + } while ((row < m_numrows) || (col < m_numcols)); +- if (!hasBit(m_numcols - 1, m_numrows - 1)) { +- setBit(m_numcols - 1, m_numrows - 1, true); +- setBit(m_numcols - 2, m_numrows - 2, true); ++ if (!HasBit(m_numcols - 1, m_numrows - 1)) { ++ SetBit(m_numcols - 1, m_numrows - 1, true); ++ SetBit(m_numcols - 2, m_numrows - 2, true); + } + } +-void CBC_DefaultPlacement::module(int32_t row, +- int32_t col, +- int32_t pos, +- int32_t bit) { ++ ++void CBC_DefaultPlacement::SetModule(int32_t row, ++ int32_t col, ++ int32_t pos, ++ int32_t bit) { + if (row < 0) { + row += m_numrows; + col += 4 - ((m_numrows + 4) % 8); +@@ -113,55 +133,60 @@ void CBC_DefaultPlacement::module(int32_t row, + } + int32_t v = m_codewords[pos]; + v &= 1 << (8 - bit); +- setBit(col, row, v != 0); ++ SetBit(col, row, v != 0); + } +-void CBC_DefaultPlacement::utah(int32_t row, int32_t col, int32_t pos) { +- module(row - 2, col - 2, pos, 1); +- module(row - 2, col - 1, pos, 2); +- module(row - 1, col - 2, pos, 3); +- module(row - 1, col - 1, pos, 4); +- module(row - 1, col, pos, 5); +- module(row, col - 2, pos, 6); +- module(row, col - 1, pos, 7); +- module(row, col, pos, 8); ++ ++void CBC_DefaultPlacement::SetUtah(int32_t row, int32_t col, int32_t pos) { ++ SetModule(row - 2, col - 2, pos, 1); ++ SetModule(row - 2, col - 1, pos, 2); ++ SetModule(row - 1, col - 2, pos, 3); ++ SetModule(row - 1, col - 1, pos, 4); ++ SetModule(row - 1, col, pos, 5); ++ SetModule(row, col - 2, pos, 6); ++ SetModule(row, col - 1, pos, 7); ++ SetModule(row, col, pos, 8); + } +-void CBC_DefaultPlacement::corner1(int32_t pos) { +- module(m_numrows - 1, 0, pos, 1); +- module(m_numrows - 1, 1, pos, 2); +- module(m_numrows - 1, 2, pos, 3); +- module(0, m_numcols - 2, pos, 4); +- module(0, m_numcols - 1, pos, 5); +- module(1, m_numcols - 1, pos, 6); +- module(2, m_numcols - 1, pos, 7); +- module(3, m_numcols - 1, pos, 8); ++ ++void CBC_DefaultPlacement::SetCorner1(int32_t pos) { ++ SetModule(m_numrows - 1, 0, pos, 1); ++ SetModule(m_numrows - 1, 1, pos, 2); ++ SetModule(m_numrows - 1, 2, pos, 3); ++ SetModule(0, m_numcols - 2, pos, 4); ++ SetModule(0, m_numcols - 1, pos, 5); ++ SetModule(1, m_numcols - 1, pos, 6); ++ SetModule(2, m_numcols - 1, pos, 7); ++ SetModule(3, m_numcols - 1, pos, 8); + } +-void CBC_DefaultPlacement::corner2(int32_t pos) { +- module(m_numrows - 3, 0, pos, 1); +- module(m_numrows - 2, 0, pos, 2); +- module(m_numrows - 1, 0, pos, 3); +- module(0, m_numcols - 4, pos, 4); +- module(0, m_numcols - 3, pos, 5); +- module(0, m_numcols - 2, pos, 6); +- module(0, m_numcols - 1, pos, 7); +- module(1, m_numcols - 1, pos, 8); ++ ++void CBC_DefaultPlacement::SetCorner2(int32_t pos) { ++ SetModule(m_numrows - 3, 0, pos, 1); ++ SetModule(m_numrows - 2, 0, pos, 2); ++ SetModule(m_numrows - 1, 0, pos, 3); ++ SetModule(0, m_numcols - 4, pos, 4); ++ SetModule(0, m_numcols - 3, pos, 5); ++ SetModule(0, m_numcols - 2, pos, 6); ++ SetModule(0, m_numcols - 1, pos, 7); ++ SetModule(1, m_numcols - 1, pos, 8); + } +-void CBC_DefaultPlacement::corner3(int32_t pos) { +- module(m_numrows - 3, 0, pos, 1); +- module(m_numrows - 2, 0, pos, 2); +- module(m_numrows - 1, 0, pos, 3); +- module(0, m_numcols - 2, pos, 4); +- module(0, m_numcols - 1, pos, 5); +- module(1, m_numcols - 1, pos, 6); +- module(2, m_numcols - 1, pos, 7); +- module(3, m_numcols - 1, pos, 8); ++ ++void CBC_DefaultPlacement::SetCorner3(int32_t pos) { ++ SetModule(m_numrows - 3, 0, pos, 1); ++ SetModule(m_numrows - 2, 0, pos, 2); ++ SetModule(m_numrows - 1, 0, pos, 3); ++ SetModule(0, m_numcols - 2, pos, 4); ++ SetModule(0, m_numcols - 1, pos, 5); ++ SetModule(1, m_numcols - 1, pos, 6); ++ SetModule(2, m_numcols - 1, pos, 7); ++ SetModule(3, m_numcols - 1, pos, 8); + } +-void CBC_DefaultPlacement::corner4(int32_t pos) { +- module(m_numrows - 1, 0, pos, 1); +- module(m_numrows - 1, m_numcols - 1, pos, 2); +- module(0, m_numcols - 3, pos, 3); +- module(0, m_numcols - 2, pos, 4); +- module(0, m_numcols - 1, pos, 5); +- module(1, m_numcols - 3, pos, 6); +- module(1, m_numcols - 2, pos, 7); +- module(1, m_numcols - 1, pos, 8); ++ ++void CBC_DefaultPlacement::SetCorner4(int32_t pos) { ++ SetModule(m_numrows - 1, 0, pos, 1); ++ SetModule(m_numrows - 1, m_numcols - 1, pos, 2); ++ SetModule(0, m_numcols - 3, pos, 3); ++ SetModule(0, m_numcols - 2, pos, 4); ++ SetModule(0, m_numcols - 1, pos, 5); ++ SetModule(1, m_numcols - 3, pos, 6); ++ SetModule(1, m_numcols - 2, pos, 7); ++ SetModule(1, m_numcols - 1, pos, 8); + } +diff --git a/fxbarcode/datamatrix/BC_DefaultPlacement.h b/fxbarcode/datamatrix/BC_DefaultPlacement.h +index 823d58796..7a5b201de 100644 +--- a/fxbarcode/datamatrix/BC_DefaultPlacement.h ++++ b/fxbarcode/datamatrix/BC_DefaultPlacement.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,34 +7,34 @@ + #ifndef FXBARCODE_DATAMATRIX_BC_DEFAULTPLACEMENT_H_ + #define FXBARCODE_DATAMATRIX_BC_DEFAULTPLACEMENT_H_ + +-#include ++#include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/widestring.h" + + class CBC_DefaultPlacement final { + public: + CBC_DefaultPlacement(WideString codewords, int32_t numcols, int32_t numrows); + ~CBC_DefaultPlacement(); + +- int32_t getNumrows(); +- int32_t getNumcols(); +- std::vector& getBits(); +- bool getBit(int32_t col, int32_t row); +- void setBit(int32_t col, int32_t row, bool bit); +- bool hasBit(int32_t col, int32_t row); +- void place(); ++ bool GetBit(int32_t col, int32_t row) const; + + private: +- WideString m_codewords; +- int32_t m_numrows; +- int32_t m_numcols; +- std::vector m_bits; +- void module(int32_t row, int32_t col, int32_t pos, int32_t bit); +- void utah(int32_t row, int32_t col, int32_t pos); +- void corner1(int32_t pos); +- void corner2(int32_t pos); +- void corner3(int32_t pos); +- void corner4(int32_t pos); ++ void Init(); ++ void SetModule(int32_t row, int32_t col, int32_t pos, int32_t bit); ++ void SetUtah(int32_t row, int32_t col, int32_t pos); ++ void SetCorner1(int32_t pos); ++ void SetCorner2(int32_t pos); ++ void SetCorner3(int32_t pos); ++ void SetCorner4(int32_t pos); ++ ++ void SetBit(int32_t col, int32_t row, bool bit); ++ bool HasBit(int32_t col, int32_t row) const; ++ ++ const WideString m_codewords; ++ const int32_t m_numrows; ++ const int32_t m_numcols; ++ DataVector m_bits; + }; + + #endif // FXBARCODE_DATAMATRIX_BC_DEFAULTPLACEMENT_H_ +diff --git a/fxbarcode/datamatrix/BC_EdifactEncoder.cpp b/fxbarcode/datamatrix/BC_EdifactEncoder.cpp +index bd86a951e..1b98f6776 100644 +--- a/fxbarcode/datamatrix/BC_EdifactEncoder.cpp ++++ b/fxbarcode/datamatrix/BC_EdifactEncoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -32,17 +32,17 @@ + + namespace { + +-WideString EncodeToEdifactCodewords(const WideString& sb, int32_t startPos) { +- int32_t len = sb.GetLength() - startPos; ++WideString EncodeToEdifactCodewords(const WideString& sb) { ++ size_t len = sb.GetLength(); + if (len == 0) + return WideString(); + +- wchar_t c1 = sb[startPos]; +- wchar_t c2 = len >= 2 ? sb[startPos + 1] : 0; +- wchar_t c3 = len >= 3 ? sb[startPos + 2] : 0; +- wchar_t c4 = len >= 4 ? sb[startPos + 3] : 0; ++ wchar_t c1 = sb[0]; ++ wchar_t c2 = len >= 2 ? sb[1] : 0; ++ wchar_t c3 = len >= 3 ? sb[2] : 0; ++ wchar_t c4 = len >= 4 ? sb[3] : 0; + int32_t v = (c1 << 18) + (c2 << 12) + (c3 << 6) + c4; +- constexpr int32_t kBuflen = 3; ++ constexpr size_t kBuflen = 3; + wchar_t cw[kBuflen]; + cw[0] = static_cast((v >> 16) & 255); + cw[1] = static_cast((v >> 8) & 255); +@@ -62,14 +62,14 @@ bool HandleEOD(CBC_EncoderContext* context, const WideString& buffer) { + return false; + + int32_t available = +- context->m_symbolInfo->dataCapacity() - context->getCodewordCount(); ++ context->m_symbolInfo->data_capacity() - context->getCodewordCount(); + int32_t remaining = context->getRemainingCharacters(); + if (remaining == 0 && available <= 2) + return true; + } + + int32_t restChars = count - 1; +- WideString encoded = EncodeToEdifactCodewords(buffer, 0); ++ WideString encoded = EncodeToEdifactCodewords(buffer); + if (encoded.IsEmpty()) + return false; + +@@ -80,7 +80,7 @@ bool HandleEOD(CBC_EncoderContext* context, const WideString& buffer) { + return false; + + int32_t available = +- context->m_symbolInfo->dataCapacity() - context->getCodewordCount(); ++ context->m_symbolInfo->data_capacity() - context->getCodewordCount(); + if (available >= 3) { + restInAscii = false; + if (!context->UpdateSymbolInfo(context->getCodewordCount() + +@@ -134,7 +134,7 @@ bool CBC_EdifactEncoder::Encode(CBC_EncoderContext* context) { + context->m_pos++; + size_t count = buffer.GetLength(); + if (count >= 4) { +- WideString encoded = EncodeToEdifactCodewords(buffer, 0); ++ WideString encoded = EncodeToEdifactCodewords(buffer); + if (encoded.IsEmpty()) + return false; + +diff --git a/fxbarcode/datamatrix/BC_EdifactEncoder.h b/fxbarcode/datamatrix/BC_EdifactEncoder.h +index 0bd8e5701..1fb88f012 100644 +--- a/fxbarcode/datamatrix/BC_EdifactEncoder.h ++++ b/fxbarcode/datamatrix/BC_EdifactEncoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/datamatrix/BC_Encoder.cpp b/fxbarcode/datamatrix/BC_Encoder.cpp +index 9403904f3..c1e7097d3 100644 +--- a/fxbarcode/datamatrix/BC_Encoder.cpp ++++ b/fxbarcode/datamatrix/BC_Encoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,5 +6,6 @@ + + #include "fxbarcode/datamatrix/BC_Encoder.h" + +-CBC_Encoder::CBC_Encoder() {} +-CBC_Encoder::~CBC_Encoder() {} ++CBC_Encoder::CBC_Encoder() = default; ++ ++CBC_Encoder::~CBC_Encoder() = default; +diff --git a/fxbarcode/datamatrix/BC_Encoder.h b/fxbarcode/datamatrix/BC_Encoder.h +index 0ea22abcf..ecba977ae 100644 +--- a/fxbarcode/datamatrix/BC_Encoder.h ++++ b/fxbarcode/datamatrix/BC_Encoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/datamatrix/BC_EncoderContext.cpp b/fxbarcode/datamatrix/BC_EncoderContext.cpp +index 3fbfee8e9..d59d1262a 100644 +--- a/fxbarcode/datamatrix/BC_EncoderContext.cpp ++++ b/fxbarcode/datamatrix/BC_EncoderContext.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -24,6 +24,7 @@ + + #include + ++#include "core/fxcrt/fx_string.h" + #include "fxbarcode/common/BC_CommonBitMatrix.h" + #include "fxbarcode/datamatrix/BC_Encoder.h" + #include "fxbarcode/datamatrix/BC_SymbolInfo.h" +@@ -90,7 +91,7 @@ bool CBC_EncoderContext::UpdateSymbolInfo() { + } + + bool CBC_EncoderContext::UpdateSymbolInfo(size_t len) { +- if (!m_symbolInfo || len > m_symbolInfo->dataCapacity()) { ++ if (!m_symbolInfo || len > m_symbolInfo->data_capacity()) { + m_symbolInfo = CBC_SymbolInfo::Lookup(len, m_bAllowRectangular); + if (!m_symbolInfo) + return false; +diff --git a/fxbarcode/datamatrix/BC_EncoderContext.h b/fxbarcode/datamatrix/BC_EncoderContext.h +index e09b63a71..329ebb291 100644 +--- a/fxbarcode/datamatrix/BC_EncoderContext.h ++++ b/fxbarcode/datamatrix/BC_EncoderContext.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/datamatrix/BC_ErrorCorrection.cpp b/fxbarcode/datamatrix/BC_ErrorCorrection.cpp +index 9c6ff7dec..135c39e80 100644 +--- a/fxbarcode/datamatrix/BC_ErrorCorrection.cpp ++++ b/fxbarcode/datamatrix/BC_ErrorCorrection.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,11 +22,15 @@ + + #include "fxbarcode/datamatrix/BC_ErrorCorrection.h" + ++#include ++ + #include + #include + ++#include "core/fxcrt/fixed_zeroed_data_vector.h" + #include "fxbarcode/datamatrix/BC_Encoder.h" + #include "fxbarcode/datamatrix/BC_SymbolInfo.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -141,10 +145,10 @@ constexpr uint8_t ALOG[256] = { + 0}; + + WideString CreateECCBlock(const WideString& codewords, size_t numECWords) { +- ASSERT(numECWords > 0); ++ DCHECK(numECWords > 0); + + const size_t len = codewords.GetLength(); +- static constexpr size_t kFactorTableNum = FX_ArraySize(FACTOR_SETS); ++ static constexpr size_t kFactorTableNum = std::size(FACTOR_SETS); + size_t table = 0; + while (table < kFactorTableNum && FACTOR_SETS[table] != numECWords) + ++table; +@@ -152,30 +156,31 @@ WideString CreateECCBlock(const WideString& codewords, size_t numECWords) { + if (table >= kFactorTableNum) + return WideString(); + +- std::vector ecc(numECWords); ++ FixedZeroedDataVector ecc(numECWords); ++ pdfium::span ecc_span = ecc.writable_span(); + for (size_t i = 0; i < len; ++i) { +- uint16_t m = ecc[numECWords - 1] ^ codewords[i]; ++ uint16_t m = ecc_span[numECWords - 1] ^ codewords[i]; + for (int32_t j = numECWords - 1; j > 0; --j) { + if (m != 0 && FACTORS[table][j] != 0) { +- ecc[j] = static_cast( +- ecc[j - 1] ^ ALOG[(LOG[m] + LOG[FACTORS[table][j]]) % 255]); ++ ecc_span[j] = static_cast( ++ ecc_span[j - 1] ^ ALOG[(LOG[m] + LOG[FACTORS[table][j]]) % 255]); + } else { +- ecc[j] = ecc[j - 1]; ++ ecc_span[j] = ecc_span[j - 1]; + } + } + if (m != 0 && FACTORS[table][0] != 0) { +- ecc[0] = ++ ecc_span[0] = + static_cast(ALOG[(LOG[m] + LOG[FACTORS[table][0]]) % 255]); + } else { +- ecc[0] = 0; ++ ecc_span[0] = 0; + } + } + WideString strecc; + strecc.Reserve(numECWords); + for (size_t i = 0; i < numECWords; ++i) +- strecc.InsertAtBack(static_cast(ecc[numECWords - i - 1])); ++ strecc.InsertAtBack(static_cast(ecc_span[numECWords - i - 1])); + +- ASSERT(!strecc.IsEmpty()); ++ DCHECK(!strecc.IsEmpty()); + return strecc; + } + +@@ -183,13 +188,13 @@ WideString CreateECCBlock(const WideString& codewords, size_t numECWords) { + + WideString CBC_ErrorCorrection::EncodeECC200(const WideString& codewords, + const CBC_SymbolInfo* symbolInfo) { +- if (codewords.GetLength() != symbolInfo->dataCapacity()) ++ if (codewords.GetLength() != symbolInfo->data_capacity()) + return WideString(); + + WideString sb = codewords; +- size_t blockCount = symbolInfo->getInterleavedBlockCount(); ++ size_t blockCount = symbolInfo->GetInterleavedBlockCount(); + if (blockCount == 1) { +- WideString ecc = CreateECCBlock(codewords, symbolInfo->errorCodewords()); ++ WideString ecc = CreateECCBlock(codewords, symbolInfo->error_codewords()); + if (ecc.IsEmpty()) + return WideString(); + sb += ecc; +@@ -198,8 +203,8 @@ WideString CBC_ErrorCorrection::EncodeECC200(const WideString& codewords, + std::vector errorSizes(blockCount); + std::vector startPos(blockCount); + for (size_t i = 0; i < blockCount; ++i) { +- dataSizes[i] = symbolInfo->getDataLengthForInterleavedBlock(); +- errorSizes[i] = symbolInfo->getErrorLengthForInterleavedBlock(); ++ dataSizes[i] = symbolInfo->GetDataLengthForInterleavedBlock(); ++ errorSizes[i] = symbolInfo->GetErrorLengthForInterleavedBlock(); + startPos[i] = i > 0 ? startPos[i - 1] + dataSizes[i] : 0; + } + +@@ -211,9 +216,9 @@ WideString CBC_ErrorCorrection::EncodeECC200(const WideString& codewords, + + for (size_t block = 0; block < blockCount; ++block) { + WideString temp; +- if (symbolInfo->dataCapacity() > block) +- temp.Reserve((symbolInfo->dataCapacity() - block / blockCount) + 1); +- for (size_t d = block; d < symbolInfo->dataCapacity(); d += blockCount) ++ if (symbolInfo->data_capacity() > block) ++ temp.Reserve((symbolInfo->data_capacity() - block / blockCount) + 1); ++ for (size_t d = block; d < symbolInfo->data_capacity(); d += blockCount) + temp.InsertAtBack(static_cast(codewords[d])); + + WideString ecc = CreateECCBlock(temp, errorSizes[block]); +@@ -222,10 +227,10 @@ WideString CBC_ErrorCorrection::EncodeECC200(const WideString& codewords, + + for (size_t pos = 0, i = block; i < errorSizes[block] * blockCount; + ++pos, i += blockCount) { +- sb.SetAt(symbolInfo->dataCapacity() + i, ecc[pos]); ++ sb.SetAt(symbolInfo->data_capacity() + i, ecc[pos]); + } + } + } +- ASSERT(!sb.IsEmpty()); ++ DCHECK(!sb.IsEmpty()); + return sb; + } +diff --git a/fxbarcode/datamatrix/BC_ErrorCorrection.h b/fxbarcode/datamatrix/BC_ErrorCorrection.h +index 676093fcf..53ffc6855 100644 +--- a/fxbarcode/datamatrix/BC_ErrorCorrection.h ++++ b/fxbarcode/datamatrix/BC_ErrorCorrection.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/datamatrix/BC_HighLevelEncoder.cpp b/fxbarcode/datamatrix/BC_HighLevelEncoder.cpp +index fbc426276..ee52de960 100644 +--- a/fxbarcode/datamatrix/BC_HighLevelEncoder.cpp ++++ b/fxbarcode/datamatrix/BC_HighLevelEncoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -39,7 +39,7 @@ + #include "fxbarcode/datamatrix/BC_SymbolInfo.h" + #include "fxbarcode/datamatrix/BC_TextEncoder.h" + #include "fxbarcode/datamatrix/BC_X12Encoder.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -91,11 +91,11 @@ int32_t GetMinimumCount(const std::array& mins) { + } + + bool IsNativeC40(wchar_t ch) { +- return (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z'); ++ return (ch == ' ') || (ch >= '0' && ch <= '9') || FXSYS_IsUpperASCII(ch); + } + + bool IsNativeText(wchar_t ch) { +- return (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z'); ++ return (ch == ' ') || (ch >= '0' && ch <= '9') || FXSYS_IsLowerASCII(ch); + } + + bool IsX12TermSep(wchar_t ch) { +@@ -104,7 +104,7 @@ bool IsX12TermSep(wchar_t ch) { + + bool IsNativeX12(wchar_t ch) { + return IsX12TermSep(ch) || (ch == ' ') || (ch >= '0' && ch <= '9') || +- (ch >= 'A' && ch <= 'Z'); ++ FXSYS_IsUpperASCII(ch); + } + + bool IsNativeEDIFACT(wchar_t ch) { +@@ -112,7 +112,7 @@ bool IsNativeEDIFACT(wchar_t ch) { + } + + size_t EncoderIndex(CBC_HighLevelEncoder::Encoding encoding) { +- ASSERT(encoding != CBC_HighLevelEncoder::Encoding::UNKNOWN); ++ DCHECK(encoding != CBC_HighLevelEncoder::Encoding::UNKNOWN); + return static_cast(encoding); + } + +@@ -145,12 +145,12 @@ WideString CBC_HighLevelEncoder::EncodeHighLevel(const WideString& msg) { + } + + std::vector> encoders; +- encoders.push_back(pdfium::MakeUnique()); +- encoders.push_back(pdfium::MakeUnique()); +- encoders.push_back(pdfium::MakeUnique()); +- encoders.push_back(pdfium::MakeUnique()); +- encoders.push_back(pdfium::MakeUnique()); +- encoders.push_back(pdfium::MakeUnique()); ++ encoders.push_back(std::make_unique()); ++ encoders.push_back(std::make_unique()); ++ encoders.push_back(std::make_unique()); ++ encoders.push_back(std::make_unique()); ++ encoders.push_back(std::make_unique()); ++ encoders.push_back(std::make_unique()); + Encoding encodingMode = Encoding::ASCII; + while (context.hasMoreCharacters()) { + if (!encoders[EncoderIndex(encodingMode)]->Encode(&context)) +@@ -165,7 +165,7 @@ WideString CBC_HighLevelEncoder::EncodeHighLevel(const WideString& msg) { + if (!context.UpdateSymbolInfo()) + return WideString(); + +- size_t capacity = context.m_symbolInfo->dataCapacity(); ++ size_t capacity = context.m_symbolInfo->data_capacity(); + if (len < capacity) { + if (encodingMode != Encoding::ASCII && encodingMode != Encoding::BASE256) + context.writeCodeword(0x00fe); +@@ -177,7 +177,7 @@ WideString CBC_HighLevelEncoder::EncodeHighLevel(const WideString& msg) { + while (codewords.GetLength() < capacity) + codewords += Randomize253State(kPad, codewords.GetLength() + 1); + +- ASSERT(!codewords.IsEmpty()); ++ DCHECK(!codewords.IsEmpty()); + return codewords; + } + +diff --git a/fxbarcode/datamatrix/BC_HighLevelEncoder.h b/fxbarcode/datamatrix/BC_HighLevelEncoder.h +index 14f64e275..97f61cfe0 100644 +--- a/fxbarcode/datamatrix/BC_HighLevelEncoder.h ++++ b/fxbarcode/datamatrix/BC_HighLevelEncoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,8 +7,6 @@ + #ifndef FXBARCODE_DATAMATRIX_BC_HIGHLEVELENCODER_H_ + #define FXBARCODE_DATAMATRIX_BC_HIGHLEVELENCODER_H_ + +-#include +- + #include "core/fxcrt/widestring.h" + + class CBC_HighLevelEncoder { +diff --git a/fxbarcode/datamatrix/BC_SymbolInfo.cpp b/fxbarcode/datamatrix/BC_SymbolInfo.cpp +index d80afc29d..05efd5654 100644 +--- a/fxbarcode/datamatrix/BC_SymbolInfo.cpp ++++ b/fxbarcode/datamatrix/BC_SymbolInfo.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,9 +22,12 @@ + + #include "fxbarcode/datamatrix/BC_SymbolInfo.h" + ++#include ++ + #include "fxbarcode/common/BC_CommonBitMatrix.h" + #include "fxbarcode/datamatrix/BC_DataMatrixSymbolInfo144.h" + #include "fxbarcode/datamatrix/BC_Encoder.h" ++#include "third_party/base/notreached.h" + + namespace { + +@@ -36,94 +39,62 @@ CBC_SymbolInfo* g_symbols[kSymbolsCount] = { + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; + ++constexpr CBC_SymbolInfo::Data kSymbolData[] = { ++ {3, 5, 3, 5, 8, 8, 1}, {5, 7, 5, 7, 10, 10, 1}, ++ {5, 7, 5, 7, 16, 6, 1}, {8, 10, 8, 10, 12, 12, 1}, ++ {10, 11, 10, 11, 14, 6, 2}, {12, 12, 12, 12, 14, 14, 1}, ++ {16, 14, 16, 14, 24, 10, 1}, {18, 14, 18, 14, 16, 16, 1}, ++ {22, 18, 22, 18, 18, 18, 1}, {22, 18, 22, 18, 16, 10, 2}, ++ {30, 20, 30, 20, 20, 20, 1}, {32, 24, 32, 24, 16, 14, 2}, ++ {36, 24, 36, 24, 22, 22, 1}, {44, 28, 44, 28, 24, 24, 1}, ++ {49, 28, 49, 28, 22, 14, 2}, {62, 36, 62, 36, 14, 14, 4}, ++ {86, 42, 86, 42, 16, 16, 4}, {114, 48, 114, 48, 18, 18, 4}, ++ {144, 56, 144, 56, 20, 20, 4}, {174, 68, 174, 68, 22, 22, 4}, ++ {204, 84, 102, 42, 24, 24, 4}, {280, 112, 140, 56, 14, 14, 16}, ++ {368, 144, 92, 36, 16, 16, 16}, {456, 192, 114, 48, 18, 18, 16}, ++ {576, 224, 144, 56, 20, 20, 16}, {696, 272, 174, 68, 22, 22, 16}, ++ {816, 336, 136, 56, 24, 24, 16}, {1050, 408, 175, 68, 18, 18, 36}, ++ {1304, 496, 163, 62, 20, 20, 36}}; ++ ++constexpr size_t kSymbolDataSize = std::size(kSymbolData); ++static_assert(kSymbolDataSize + 1 == kSymbolsCount, "Wrong kSymbolDataSize"); ++ + } // namespace + ++// static + void CBC_SymbolInfo::Initialize() { +- g_symbols[0] = new CBC_SymbolInfo(3, 5, 8, 8, 1); +- g_symbols[1] = new CBC_SymbolInfo(5, 7, 10, 10, 1); +- g_symbols[2] = new CBC_SymbolInfo(5, 7, 16, 6, 1); +- g_symbols[3] = new CBC_SymbolInfo(8, 10, 12, 12, 1); +- g_symbols[4] = new CBC_SymbolInfo(10, 11, 14, 6, 2); +- g_symbols[5] = new CBC_SymbolInfo(12, 12, 14, 14, 1); +- g_symbols[6] = new CBC_SymbolInfo(16, 14, 24, 10, 1); +- g_symbols[7] = new CBC_SymbolInfo(18, 14, 16, 16, 1); +- g_symbols[8] = new CBC_SymbolInfo(22, 18, 18, 18, 1); +- g_symbols[9] = new CBC_SymbolInfo(22, 18, 16, 10, 2); +- g_symbols[10] = new CBC_SymbolInfo(30, 20, 20, 20, 1); +- g_symbols[11] = new CBC_SymbolInfo(32, 24, 16, 14, 2); +- g_symbols[12] = new CBC_SymbolInfo(36, 24, 22, 22, 1); +- g_symbols[13] = new CBC_SymbolInfo(44, 28, 24, 24, 1); +- g_symbols[14] = new CBC_SymbolInfo(49, 28, 22, 14, 2); +- g_symbols[15] = new CBC_SymbolInfo(62, 36, 14, 14, 4); +- g_symbols[16] = new CBC_SymbolInfo(86, 42, 16, 16, 4); +- g_symbols[17] = new CBC_SymbolInfo(114, 48, 18, 18, 4); +- g_symbols[18] = new CBC_SymbolInfo(144, 56, 20, 20, 4); +- g_symbols[19] = new CBC_SymbolInfo(174, 68, 22, 22, 4); +- g_symbols[20] = new CBC_SymbolInfo(204, 84, 24, 24, 4, 102, 42); +- g_symbols[21] = new CBC_SymbolInfo(280, 112, 14, 14, 16, 140, 56); +- g_symbols[22] = new CBC_SymbolInfo(368, 144, 16, 16, 16, 92, 36); +- g_symbols[23] = new CBC_SymbolInfo(456, 192, 18, 18, 16, 114, 48); +- g_symbols[24] = new CBC_SymbolInfo(576, 224, 20, 20, 16, 144, 56); +- g_symbols[25] = new CBC_SymbolInfo(696, 272, 22, 22, 16, 174, 68); +- g_symbols[26] = new CBC_SymbolInfo(816, 336, 24, 24, 16, 136, 56); +- g_symbols[27] = new CBC_SymbolInfo(1050, 408, 18, 18, 36, 175, 68); +- g_symbols[28] = new CBC_SymbolInfo(1304, 496, 20, 20, 36, 163, 62); +- g_symbols[29] = new CBC_DataMatrixSymbolInfo144(); ++ for (size_t i = 0; i < kSymbolDataSize; ++i) ++ g_symbols[i] = new CBC_SymbolInfo(&kSymbolData[i]); ++ g_symbols[kSymbolDataSize] = new CBC_DataMatrixSymbolInfo144(); + } + ++// static + void CBC_SymbolInfo::Finalize() { +- for (size_t i = 0; i < kSymbolsCount; i++) { ++ for (size_t i = 0; i < kSymbolsCount; ++i) { + delete g_symbols[i]; + g_symbols[i] = nullptr; + } + } + +-CBC_SymbolInfo::CBC_SymbolInfo(size_t dataCapacity, +- size_t errorCodewords, +- int32_t matrixWidth, +- int32_t matrixHeight, +- int32_t dataRegions) +- : CBC_SymbolInfo(dataCapacity, +- errorCodewords, +- matrixWidth, +- matrixHeight, +- dataRegions, +- dataCapacity, +- errorCodewords) {} +- +-CBC_SymbolInfo::CBC_SymbolInfo(size_t dataCapacity, +- size_t errorCodewords, +- int32_t matrixWidth, +- int32_t matrixHeight, +- int32_t dataRegions, +- size_t rsBlockData, +- size_t rsBlockError) +- : m_rectangular(matrixWidth != matrixHeight), +- m_dataCapacity(dataCapacity), +- m_errorCodewords(errorCodewords), +- m_matrixWidth(matrixWidth), +- m_matrixHeight(matrixHeight), +- m_dataRegions(dataRegions), +- m_rsBlockData(rsBlockData), +- m_rsBlockError(rsBlockError) {} ++CBC_SymbolInfo::CBC_SymbolInfo(const Data* data) : data_(data) {} + + CBC_SymbolInfo::~CBC_SymbolInfo() = default; + +-const CBC_SymbolInfo* CBC_SymbolInfo::Lookup(size_t iDataCodewords, +- bool bAllowRectangular) { +- for (size_t i = 0; i < kSymbolsCount; i++) { ++const CBC_SymbolInfo* CBC_SymbolInfo::Lookup(size_t data_codewords, ++ bool allow_rectangular) { ++ for (size_t i = 0; i < kSymbolsCount; ++i) { + CBC_SymbolInfo* symbol = g_symbols[i]; +- if (symbol->m_rectangular && !bAllowRectangular) ++ if (symbol->is_rectangular() && !allow_rectangular) + continue; + +- if (iDataCodewords <= symbol->dataCapacity()) ++ if (data_codewords <= symbol->data_capacity()) + return symbol; + } + return nullptr; + } + +-int32_t CBC_SymbolInfo::getHorizontalDataRegions() const { +- switch (m_dataRegions) { ++int32_t CBC_SymbolInfo::GetHorizontalDataRegions() const { ++ switch (data_->data_regions) { + case 1: + return 1; + case 2: +@@ -140,8 +111,8 @@ int32_t CBC_SymbolInfo::getHorizontalDataRegions() const { + } + } + +-int32_t CBC_SymbolInfo::getVerticalDataRegions() const { +- switch (m_dataRegions) { ++int32_t CBC_SymbolInfo::GetVerticalDataRegions() const { ++ switch (data_->data_regions) { + case 1: + return 1; + case 2: +@@ -158,34 +129,30 @@ int32_t CBC_SymbolInfo::getVerticalDataRegions() const { + } + } + +-int32_t CBC_SymbolInfo::getSymbolDataWidth() const { +- return getHorizontalDataRegions() * m_matrixWidth; +-} +- +-int32_t CBC_SymbolInfo::getSymbolDataHeight() const { +- return getVerticalDataRegions() * m_matrixHeight; ++int32_t CBC_SymbolInfo::GetSymbolDataWidth() const { ++ return GetHorizontalDataRegions() * data_->matrix_width; + } + +-int32_t CBC_SymbolInfo::getSymbolWidth() const { +- return getSymbolDataWidth() + (getHorizontalDataRegions() * 2); ++int32_t CBC_SymbolInfo::GetSymbolDataHeight() const { ++ return GetVerticalDataRegions() * data_->matrix_height; + } + +-int32_t CBC_SymbolInfo::getSymbolHeight() const { +- return getSymbolDataHeight() + (getVerticalDataRegions() * 2); ++int32_t CBC_SymbolInfo::GetSymbolWidth() const { ++ return GetSymbolDataWidth() + (GetHorizontalDataRegions() * 2); + } + +-size_t CBC_SymbolInfo::getCodewordCount() const { +- return m_dataCapacity + m_errorCodewords; ++int32_t CBC_SymbolInfo::GetSymbolHeight() const { ++ return GetSymbolDataHeight() + (GetVerticalDataRegions() * 2); + } + +-size_t CBC_SymbolInfo::getInterleavedBlockCount() const { +- return m_dataCapacity / m_rsBlockData; ++size_t CBC_SymbolInfo::GetInterleavedBlockCount() const { ++ return data_->data_capacity / data_->rs_block_data; + } + +-size_t CBC_SymbolInfo::getDataLengthForInterleavedBlock() const { +- return m_rsBlockData; ++size_t CBC_SymbolInfo::GetDataLengthForInterleavedBlock() const { ++ return data_->rs_block_data; + } + +-size_t CBC_SymbolInfo::getErrorLengthForInterleavedBlock() const { +- return m_rsBlockError; ++size_t CBC_SymbolInfo::GetErrorLengthForInterleavedBlock() const { ++ return data_->rs_block_error; + } +diff --git a/fxbarcode/datamatrix/BC_SymbolInfo.h b/fxbarcode/datamatrix/BC_SymbolInfo.h +index 07e563dcf..9aa9ed76f 100644 +--- a/fxbarcode/datamatrix/BC_SymbolInfo.h ++++ b/fxbarcode/datamatrix/BC_SymbolInfo.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,59 +7,54 @@ + #ifndef FXBARCODE_DATAMATRIX_BC_SYMBOLINFO_H_ + #define FXBARCODE_DATAMATRIX_BC_SYMBOLINFO_H_ + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include ++#include ++ ++#include "core/fxcrt/unowned_ptr.h" + + class CBC_SymbolInfo { + public: +- CBC_SymbolInfo(size_t dataCapacity, +- size_t errorCodewords, +- int32_t matrixWidth, +- int32_t matrixHeight, +- int32_t dataRegions); ++ struct Data { ++ int16_t data_capacity; ++ int16_t error_codewords; ++ int16_t rs_block_data; ++ int8_t rs_block_error; ++ int8_t matrix_width; ++ int8_t matrix_height; ++ int8_t data_regions; ++ }; ++ + virtual ~CBC_SymbolInfo(); + + static void Initialize(); + static void Finalize(); +- static void overrideSymbolSet(CBC_SymbolInfo* override); +- static const CBC_SymbolInfo* Lookup(size_t iDataCodewords, +- bool bAllowRectangular); +- +- int32_t getSymbolDataWidth() const; +- int32_t getSymbolDataHeight() const; +- int32_t getSymbolWidth() const; +- int32_t getSymbolHeight() const; +- size_t getCodewordCount() const; +- virtual size_t getInterleavedBlockCount() const; +- size_t getDataLengthForInterleavedBlock() const; +- size_t getErrorLengthForInterleavedBlock() const; +- +- size_t dataCapacity() const { return m_dataCapacity; } +- size_t errorCodewords() const { return m_errorCodewords; } +- int32_t matrixWidth() const { return m_matrixWidth; } +- int32_t matrixHeight() const { return m_matrixHeight; } ++ static const CBC_SymbolInfo* Lookup(size_t data_codewords, ++ bool allow_rectangular); ++ ++ int32_t GetSymbolDataWidth() const; ++ int32_t GetSymbolDataHeight() const; ++ int32_t GetSymbolWidth() const; ++ int32_t GetSymbolHeight() const; ++ virtual size_t GetInterleavedBlockCount() const; ++ size_t GetDataLengthForInterleavedBlock() const; ++ size_t GetErrorLengthForInterleavedBlock() const; ++ ++ size_t data_capacity() const { return data_->data_capacity; } ++ size_t error_codewords() const { return data_->error_codewords; } ++ int32_t matrix_width() const { return data_->matrix_width; } ++ int32_t matrix_height() const { return data_->matrix_height; } + + protected: +- CBC_SymbolInfo(size_t dataCapacity, +- size_t errorCodewords, +- int32_t matrixWidth, +- int32_t matrixHeight, +- int32_t dataRegions, +- size_t rsBlockData, +- size_t rsBlockError); ++ explicit CBC_SymbolInfo(const Data* data); + + private: +- int32_t getHorizontalDataRegions() const; +- int32_t getVerticalDataRegions() const; ++ int32_t GetHorizontalDataRegions() const; ++ int32_t GetVerticalDataRegions() const; ++ bool is_rectangular() const { ++ return data_->matrix_width != data_->matrix_height; ++ } + +- const bool m_rectangular; +- const size_t m_dataCapacity; +- const size_t m_errorCodewords; +- const int32_t m_matrixWidth; +- const int32_t m_matrixHeight; +- const int32_t m_dataRegions; +- const size_t m_rsBlockData; +- const size_t m_rsBlockError; ++ UnownedPtr const data_; + }; + + #endif // FXBARCODE_DATAMATRIX_BC_SYMBOLINFO_H_ +diff --git a/fxbarcode/datamatrix/BC_TextEncoder.cpp b/fxbarcode/datamatrix/BC_TextEncoder.cpp +index bf87afa80..f5d2bd5f4 100644 +--- a/fxbarcode/datamatrix/BC_TextEncoder.cpp ++++ b/fxbarcode/datamatrix/BC_TextEncoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -47,7 +47,7 @@ int32_t CBC_TextEncoder::EncodeChar(wchar_t c, WideString* sb) { + *sb += (wchar_t)(c - 48 + 4); + return 1; + } +- if (c >= 'a' && c <= 'z') { ++ if (FXSYS_IsLowerASCII(c)) { + *sb += (wchar_t)(c - 97 + 14); + return 1; + } +@@ -76,7 +76,7 @@ int32_t CBC_TextEncoder::EncodeChar(wchar_t c, WideString* sb) { + *sb += (wchar_t)(c - 96); + return 2; + } +- if (c >= 'A' && c <= 'Z') { ++ if (FXSYS_IsUpperASCII(c)) { + *sb += (wchar_t)'\2'; + *sb += (wchar_t)(c - 65 + 1); + return 2; +diff --git a/fxbarcode/datamatrix/BC_TextEncoder.h b/fxbarcode/datamatrix/BC_TextEncoder.h +index 40914b309..00a4abf40 100644 +--- a/fxbarcode/datamatrix/BC_TextEncoder.h ++++ b/fxbarcode/datamatrix/BC_TextEncoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/datamatrix/BC_X12Encoder.cpp b/fxbarcode/datamatrix/BC_X12Encoder.cpp +index 37180eac7..100bd90d5 100644 +--- a/fxbarcode/datamatrix/BC_X12Encoder.cpp ++++ b/fxbarcode/datamatrix/BC_X12Encoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -67,7 +67,7 @@ bool CBC_X12Encoder::HandleEOD(CBC_EncoderContext* context, + return false; + + int32_t available = +- context->m_symbolInfo->dataCapacity() - context->getCodewordCount(); ++ context->m_symbolInfo->data_capacity() - context->getCodewordCount(); + size_t count = buffer->GetLength(); + if (count == 2) { + context->writeCodeword(CBC_HighLevelEncoder::X12_UNLATCH); +@@ -94,7 +94,7 @@ int32_t CBC_X12Encoder::EncodeChar(wchar_t c, WideString* sb) { + *sb += (wchar_t)'\3'; + else if (FXSYS_IsDecimalDigit(c)) + *sb += (wchar_t)(c - 48 + 4); +- else if (c >= 'A' && c <= 'Z') ++ else if (FXSYS_IsUpperASCII(c)) + *sb += (wchar_t)(c - 65 + 14); + else + return 0; +diff --git a/fxbarcode/datamatrix/BC_X12Encoder.h b/fxbarcode/datamatrix/BC_X12Encoder.h +index aa258a90c..7f5e9b075 100644 +--- a/fxbarcode/datamatrix/BC_X12Encoder.h ++++ b/fxbarcode/datamatrix/BC_X12Encoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/oned/BC_OneDimWriter.cpp b/fxbarcode/oned/BC_OneDimWriter.cpp +index 5a02cdfaf..3320c4796 100644 +--- a/fxbarcode/oned/BC_OneDimWriter.cpp ++++ b/fxbarcode/oned/BC_OneDimWriter.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,20 +22,33 @@ + + #include "fxbarcode/oned/BC_OneDimWriter.h" + ++#include ++ + #include + #include + #include + + #include "build/build_config.h" + #include "core/fxge/cfx_defaultrenderdevice.h" ++#include "core/fxge/cfx_fillrenderoptions.h" + #include "core/fxge/cfx_font.h" + #include "core/fxge/cfx_graphstatedata.h" +-#include "core/fxge/cfx_pathdata.h" ++#include "core/fxge/cfx_path.h" + #include "core/fxge/cfx_renderdevice.h" + #include "core/fxge/cfx_unicodeencodingex.h" + #include "core/fxge/text_char_pos.h" + #include "fxbarcode/BC_Writer.h" + ++// static ++bool CBC_OneDimWriter::HasValidContentSize(WideStringView contents) { ++ // Limit the size of 1D barcodes. Typical 1D barcodes are short so this should ++ // be sufficient for most use cases. ++ static constexpr size_t kMaxInputLengthBytes = 8192; ++ ++ size_t size = contents.GetLength(); ++ return size > 0 && size <= kMaxInputLengthBytes; ++} ++ + CBC_OneDimWriter::CBC_OneDimWriter() = default; + + CBC_OneDimWriter::~CBC_OneDimWriter() = default; +@@ -72,36 +85,20 @@ void CBC_OneDimWriter::SetFontColor(FX_ARGB color) { + m_fontColor = color; + } + +-uint8_t* CBC_OneDimWriter::EncodeWithHint(const ByteString& contents, +- BCFORMAT format, +- int32_t& outWidth, +- int32_t& outHeight, +- int32_t hints) { +- outHeight = 1; +- return EncodeImpl(contents, outWidth); +-} +- +-uint8_t* CBC_OneDimWriter::Encode(const ByteString& contents, +- BCFORMAT format, +- int32_t& outWidth, +- int32_t& outHeight) { +- return EncodeWithHint(contents, format, outWidth, outHeight, 0); +-} +- +-int32_t CBC_OneDimWriter::AppendPattern(uint8_t* target, +- int32_t pos, +- const int8_t* pattern, +- int32_t patternLength, +- bool startColor) { ++pdfium::span CBC_OneDimWriter::AppendPattern( ++ pdfium::span target, ++ pdfium::span pattern, ++ bool startColor) { + bool color = startColor; +- int32_t numAdded = 0; +- for (int32_t i = 0; i < patternLength; i++) { +- for (int32_t j = 0; j < pattern[i]; j++) ++ size_t added = 0; ++ size_t pos = 0; ++ for (const int8_t pattern_value : pattern) { ++ for (int32_t i = 0; i < pattern_value; ++i) + target[pos++] = color ? 1 : 0; +- numAdded += pattern[i]; ++ added += pattern_value; + color = !color; + } +- return numAdded; ++ return target.subspan(added); + } + + void CBC_OneDimWriter::CalcTextInfo(const ByteString& text, +@@ -119,7 +116,7 @@ void CBC_OneDimWriter::CalcTextInfo(const ByteString& text, + for (size_t i = 0; i < length; ++i) { + charcodes[i] = encoding->CharCodeFromUnicode(text[i]); + int32_t glyph_code = encoding->GlyphFromCharCode(charcodes[i]); +- uint32_t glyph_value = cFont->GetGlyphWidth(glyph_code); ++ int glyph_value = cFont->GetGlyphWidth(glyph_code); + float temp = glyph_value * fontSize / 1000.0; + charWidth += temp; + } +@@ -135,7 +132,7 @@ void CBC_OneDimWriter::CalcTextInfo(const ByteString& text, + charPos[0].m_Origin = CFX_PointF(penX + left, penY + top); + charPos[0].m_GlyphIndex = encoding->GlyphFromCharCode(charcodes[0]); + charPos[0].m_FontCharWidth = cFont->GetGlyphWidth(charPos[0].m_GlyphIndex); +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + charPos[0].m_ExtGID = charPos[0].m_GlyphIndex; + #endif + penX += (float)(charPos[0].m_FontCharWidth) * (float)fontSize / 1000.0f; +@@ -143,7 +140,7 @@ void CBC_OneDimWriter::CalcTextInfo(const ByteString& text, + charPos[i].m_Origin = CFX_PointF(penX + left, penY + top); + charPos[i].m_GlyphIndex = encoding->GlyphFromCharCode(charcodes[i]); + charPos[i].m_FontCharWidth = cFont->GetGlyphWidth(charPos[i].m_GlyphIndex); +-#if defined(OS_MACOSX) ++#if BUILDFLAG(IS_APPLE) + charPos[i].m_ExtGID = charPos[i].m_GlyphIndex; + #endif + penX += (float)(charPos[i].m_FontCharWidth) * (float)fontSize / 1000.0f; +@@ -151,37 +148,34 @@ void CBC_OneDimWriter::CalcTextInfo(const ByteString& text, + } + + void CBC_OneDimWriter::ShowDeviceChars(CFX_RenderDevice* device, +- const CFX_Matrix* matrix, ++ const CFX_Matrix& matrix, + const ByteString str, + float geWidth, + TextCharPos* pCharPos, + float locX, + float locY, + int32_t barWidth) { +- int32_t iFontSize = (int32_t)fabs(m_fFontSize); ++ int32_t iFontSize = static_cast(fabs(m_fFontSize)); + int32_t iTextHeight = iFontSize + 1; + CFX_FloatRect rect((float)locX, (float)locY, (float)(locX + geWidth), + (float)(locY + iTextHeight)); + if (geWidth != m_Width) { + rect.right -= 1; + } +- FX_RECT re = matrix->TransformRect(rect).GetOuterRect(); ++ FX_RECT re = matrix.TransformRect(rect).GetOuterRect(); + device->FillRect(re, kBackgroundColor); + CFX_Matrix affine_matrix(1.0, 0.0, 0.0, -1.0, (float)locX, + (float)(locY + iFontSize)); +- if (matrix) { +- affine_matrix.Concat(*matrix); +- } +- device->DrawNormalText(str.GetLength(), pCharPos, m_pFont.Get(), ++ affine_matrix.Concat(matrix); ++ device->DrawNormalText(pdfium::make_span(pCharPos, str.GetLength()), m_pFont, + static_cast(iFontSize), affine_matrix, +- m_fontColor, FXTEXT_CLEARTYPE); ++ m_fontColor, GetTextRenderOptions()); + } + + bool CBC_OneDimWriter::ShowChars(WideStringView contents, + CFX_RenderDevice* device, +- const CFX_Matrix* matrix, +- int32_t barWidth, +- int32_t multiple) { ++ const CFX_Matrix& matrix, ++ int32_t barWidth) { + if (!device || !m_pFont) + return false; + +@@ -189,39 +183,38 @@ bool CBC_OneDimWriter::ShowChars(WideStringView contents, + std::vector charpos(str.GetLength()); + float charsLen = 0; + float geWidth = 0; +- if (m_locTextLoc == BC_TEXT_LOC_ABOVEEMBED || +- m_locTextLoc == BC_TEXT_LOC_BELOWEMBED) { ++ if (m_locTextLoc == BC_TEXT_LOC::kAboveEmbed || ++ m_locTextLoc == BC_TEXT_LOC::kBelowEmbed) { + geWidth = 0; +- } else if (m_locTextLoc == BC_TEXT_LOC_ABOVE || +- m_locTextLoc == BC_TEXT_LOC_BELOW) { ++ } else if (m_locTextLoc == BC_TEXT_LOC::kAbove || ++ m_locTextLoc == BC_TEXT_LOC::kBelow) { + geWidth = (float)barWidth; + } +- int32_t iFontSize = (int32_t)fabs(m_fFontSize); ++ int32_t iFontSize = static_cast(fabs(m_fFontSize)); + int32_t iTextHeight = iFontSize + 1; +- CalcTextInfo(str, charpos.data(), m_pFont.Get(), geWidth, iFontSize, +- charsLen); ++ CalcTextInfo(str, charpos.data(), m_pFont, geWidth, iFontSize, charsLen); + if (charsLen < 1) + return true; + + int32_t locX = 0; + int32_t locY = 0; + switch (m_locTextLoc) { +- case BC_TEXT_LOC_ABOVEEMBED: +- locX = (int32_t)(barWidth - charsLen) / 2; ++ case BC_TEXT_LOC::kAboveEmbed: ++ locX = static_cast(barWidth - charsLen) / 2; + locY = 0; + geWidth = charsLen; + break; +- case BC_TEXT_LOC_ABOVE: ++ case BC_TEXT_LOC::kAbove: + locX = 0; + locY = 0; + geWidth = (float)barWidth; + break; +- case BC_TEXT_LOC_BELOWEMBED: +- locX = (int32_t)(barWidth - charsLen) / 2; ++ case BC_TEXT_LOC::kBelowEmbed: ++ locX = static_cast(barWidth - charsLen) / 2; + locY = m_Height - iTextHeight; + geWidth = charsLen; + break; +- case BC_TEXT_LOC_BELOW: ++ case BC_TEXT_LOC::kBelow: + default: + locX = 0; + locY = m_Height - iTextHeight; +@@ -234,72 +227,57 @@ bool CBC_OneDimWriter::ShowChars(WideStringView contents, + } + + bool CBC_OneDimWriter::RenderDeviceResult(CFX_RenderDevice* device, +- const CFX_Matrix* matrix, ++ const CFX_Matrix& matrix, + WideStringView contents) { + if (m_output.empty()) + return false; + + CFX_GraphStateData stateData; +- CFX_PathData path; ++ CFX_Path path; + path.AppendRect(0, 0, static_cast(m_Width), + static_cast(m_Height)); +- device->DrawPath(&path, matrix, &stateData, kBackgroundColor, +- kBackgroundColor, FXFILL_ALTERNATE); ++ device->DrawPath(path, &matrix, &stateData, kBackgroundColor, ++ kBackgroundColor, CFX_FillRenderOptions::EvenOddOptions()); + CFX_Matrix scaledMatrix(m_outputHScale, 0.0, 0.0, + static_cast(m_Height), 0.0, 0.0); +- scaledMatrix.Concat(*matrix); ++ scaledMatrix.Concat(matrix); + for (const auto& rect : m_output) { + CFX_GraphStateData data; +- device->DrawPath(&rect, &scaledMatrix, &data, kBarColor, 0, FXFILL_WINDING); ++ device->DrawPath(rect, &scaledMatrix, &data, kBarColor, 0, ++ CFX_FillRenderOptions::WindingOptions()); + } + +- return m_locTextLoc == BC_TEXT_LOC_NONE || !contents.Contains(' ') || +- ShowChars(contents, device, matrix, m_barWidth, m_multiple); ++ return m_locTextLoc == BC_TEXT_LOC::kNone || !contents.Contains(' ') || ++ ShowChars(contents, device, matrix, m_barWidth); + } + + bool CBC_OneDimWriter::RenderResult(WideStringView contents, +- uint8_t* code, +- int32_t codeLength) { +- if (codeLength < 1) ++ pdfium::span code) { ++ if (code.empty()) + return false; + + m_ModuleHeight = std::max(m_ModuleHeight, 20); +- const int32_t codeOldLength = codeLength; ++ const size_t original_codelength = code.size(); + const int32_t leftPadding = m_bLeftPadding ? 7 : 0; + const int32_t rightPadding = m_bRightPadding ? 7 : 0; +- codeLength += leftPadding; +- codeLength += rightPadding; ++ const size_t codelength = code.size() + leftPadding + rightPadding; + m_outputHScale = +- m_Width > 0 ? static_cast(m_Width) / static_cast(codeLength) ++ m_Width > 0 ? static_cast(m_Width) / static_cast(codelength) + : 1.0; +- m_multiple = 1; +- const int32_t outputWidth = codeLength; + m_barWidth = m_Width; + + m_output.clear(); +- m_output.reserve(codeOldLength * m_multiple); +- for (int32_t inputX = 0, outputX = leftPadding * m_multiple; +- inputX < codeOldLength; ++inputX, outputX += m_multiple) { +- if (code[inputX] != 1) ++ m_output.reserve(original_codelength); ++ for (size_t i = 0; i < original_codelength; ++i) { ++ if (code[i] != 1) + continue; + +- if (outputX >= outputWidth) +- return true; +- +- if (outputX + m_multiple > outputWidth && outputWidth - outputX > 0) { +- RenderVerticalBars(outputX, outputWidth - outputX); ++ size_t output_index = i + leftPadding; ++ if (output_index >= codelength) + return true; +- } +- +- RenderVerticalBars(outputX, m_multiple); +- } +- return true; +-} + +-void CBC_OneDimWriter::RenderVerticalBars(int32_t outputX, int32_t width) { +- for (int i = 0; i < width; ++i) { +- float x = outputX + i; + m_output.emplace_back(); +- m_output.back().AppendRect(x, 0.0f, x + 1, 1.0f); ++ m_output.back().AppendRect(output_index, 0.0f, output_index + 1, 1.0f); + } ++ return true; + } +diff --git a/fxbarcode/oned/BC_OneDimWriter.h b/fxbarcode/oned/BC_OneDimWriter.h +index 3e0ccc930..49957b5c2 100644 +--- a/fxbarcode/oned/BC_OneDimWriter.h ++++ b/fxbarcode/oned/BC_OneDimWriter.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,60 +7,60 @@ + #ifndef FXBARCODE_ONED_BC_ONEDIMWRITER_H_ + #define FXBARCODE_ONED_BC_ONEDIMWRITER_H_ + ++#include ++#include ++ + #include + ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_string.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "core/fxge/cfx_textrenderoptions.h" + #include "fxbarcode/BC_Library.h" + #include "fxbarcode/BC_Writer.h" +-#include "fxbarcode/utils.h" ++#include "third_party/base/span.h" + + class CFX_Font; +-class CFX_PathData; ++class CFX_Matrix; ++class CFX_Path; + class CFX_RenderDevice; + class TextCharPos; + + class CBC_OneDimWriter : public CBC_Writer { + public: ++ static constexpr CFX_TextRenderOptions GetTextRenderOptions() { ++ return CFX_TextRenderOptions(CFX_TextRenderOptions::kLcd); ++ } ++ static bool HasValidContentSize(WideStringView contents); ++ + CBC_OneDimWriter(); + ~CBC_OneDimWriter() override; + + virtual bool RenderResult(WideStringView contents, +- uint8_t* code, +- int32_t codeLength); ++ pdfium::span code); + virtual bool CheckContentValidity(WideStringView contents) = 0; + virtual WideString FilterContents(WideStringView contents) = 0; +- virtual void SetPrintChecksum(bool checksum); + virtual void SetDataLength(int32_t length); +- virtual void SetCalcChecksum(bool state); +- virtual void SetFontSize(float size); +- virtual void SetFontStyle(int32_t style); +- virtual void SetFontColor(FX_ARGB color); +- +- uint8_t* Encode(const ByteString& contents, +- BCFORMAT format, +- int32_t& outWidth, +- int32_t& outHeight); ++ ++ void SetPrintChecksum(bool checksum); ++ void SetCalcChecksum(bool state); ++ void SetFontSize(float size); ++ void SetFontStyle(int32_t style); ++ void SetFontColor(FX_ARGB color); ++ ++ virtual DataVector Encode(const ByteString& contents) = 0; + bool RenderDeviceResult(CFX_RenderDevice* device, +- const CFX_Matrix* matrix, ++ const CFX_Matrix& matrix, + WideStringView contents); + bool SetFont(CFX_Font* cFont); + + protected: +- virtual uint8_t* EncodeWithHint(const ByteString& contents, +- BCFORMAT format, +- int32_t& outWidth, +- int32_t& outHeight, +- int32_t hints); +- virtual uint8_t* EncodeImpl(const ByteString& contents, +- int32_t& outLength) = 0; + virtual bool ShowChars(WideStringView contents, + CFX_RenderDevice* device, +- const CFX_Matrix* matrix, +- int32_t barWidth, +- int32_t multiple); ++ const CFX_Matrix& matrix, ++ int32_t barWidth); + void ShowDeviceChars(CFX_RenderDevice* device, +- const CFX_Matrix* matrix, ++ const CFX_Matrix& matrix, + const ByteString str, + float geWidth, + TextCharPos* pCharPos, +@@ -73,13 +73,9 @@ class CBC_OneDimWriter : public CBC_Writer { + float geWidth, + int32_t fontSize, + float& charsLen); +- int32_t AppendPattern(uint8_t* target, +- int32_t pos, +- const int8_t* pattern, +- int32_t patternLength, +- bool startColor); +- +- void RenderVerticalBars(int32_t outputX, int32_t width); ++ pdfium::span AppendPattern(pdfium::span target, ++ pdfium::span pattern, ++ bool startColor); + + bool m_bPrintChecksum = true; + bool m_bCalcChecksum = false; +@@ -90,14 +86,13 @@ class CBC_OneDimWriter : public CBC_Writer { + float m_fFontSize = 10.0f; + int32_t m_iFontStyle = 0; + uint32_t m_fontColor = 0xff000000; +- BC_TEXT_LOC m_locTextLoc = BC_TEXT_LOC_BELOWEMBED; ++ BC_TEXT_LOC m_locTextLoc = BC_TEXT_LOC::kBelowEmbed; + + int32_t m_iDataLenth = 0; + size_t m_iContentLen = 0; + +- std::vector m_output; ++ std::vector m_output; + int32_t m_barWidth; +- int32_t m_multiple; + float m_outputHScale; + }; + +diff --git a/fxbarcode/oned/BC_OnedCodaBarWriter.cpp b/fxbarcode/oned/BC_OnedCodaBarWriter.cpp +index c4334c6de..6be75ad91 100644 +--- a/fxbarcode/oned/BC_OnedCodaBarWriter.cpp ++++ b/fxbarcode/oned/BC_OnedCodaBarWriter.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,10 +22,14 @@ + + #include "fxbarcode/oned/BC_OnedCodaBarWriter.h" + ++#include ++ ++#include "core/fxcrt/fx_2d_size.h" ++#include "core/fxcrt/fx_extension.h" + #include "fxbarcode/BC_Writer.h" + #include "fxbarcode/common/BC_CommonBitMatrix.h" + #include "fxbarcode/oned/BC_OneDimWriter.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/containers/contains.h" + + namespace { + +@@ -36,16 +40,25 @@ const char kOnedCodaAlphabet[] = {'0', '1', '2', '3', '4', '5', '6', '7', + const int8_t kOnedCodaCharacterEncoding[] = { + 0x03, 0x06, 0x09, 0x60, 0x12, 0x42, 0x21, 0x24, 0x30, 0x48, 0x0c, + 0x18, 0x45, 0x51, 0x54, 0x15, 0x1A, 0x29, 0x0B, 0x0E, 0x1A, 0x29}; +-static_assert(FX_ArraySize(kOnedCodaCharacterEncoding) == 22, "Wrong size"); +-static_assert(FX_ArraySize(kOnedCodaCharacterEncoding) == +- FX_ArraySize(kOnedCodaAlphabet), ++static_assert(std::size(kOnedCodaCharacterEncoding) == 22, "Wrong size"); ++static_assert(std::size(kOnedCodaCharacterEncoding) == ++ std::size(kOnedCodaAlphabet), + "Wrong size"); + + const char kStartEndChars[] = {'A', 'B', 'C', 'D', 'T', 'N', '*', 'E', + 'a', 'b', 'c', 'd', 't', 'n', 'e'}; +-const char kCOntentChars[] = {'0', '1', '2', '3', '4', '5', '6', '7', ++const char kContentChars[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '-', '$', '/', ':', '+', '.'}; + ++bool IsValidChar(wchar_t ch, bool isContent) { ++ if (ch > 0x7F) ++ return false; ++ ++ char narrow_ch = static_cast(ch); ++ return pdfium::Contains(kContentChars, narrow_ch) || ++ (isContent && pdfium::Contains(kStartEndChars, narrow_ch)); ++} ++ + } // namespace + + CBC_OnedCodaBarWriter::CBC_OnedCodaBarWriter() = default; +@@ -53,7 +66,7 @@ CBC_OnedCodaBarWriter::CBC_OnedCodaBarWriter() = default; + CBC_OnedCodaBarWriter::~CBC_OnedCodaBarWriter() = default; + + bool CBC_OnedCodaBarWriter::SetStartChar(char start) { +- if (!pdfium::ContainsValue(kStartEndChars, start)) ++ if (!pdfium::Contains(kStartEndChars, start)) + return false; + + m_chStart = start; +@@ -61,7 +74,7 @@ bool CBC_OnedCodaBarWriter::SetStartChar(char start) { + } + + bool CBC_OnedCodaBarWriter::SetEndChar(char end) { +- if (!pdfium::ContainsValue(kStartEndChars, end)) ++ if (!pdfium::Contains(kStartEndChars, end)) + return false; + + m_chEnd = end; +@@ -72,12 +85,8 @@ void CBC_OnedCodaBarWriter::SetDataLength(int32_t length) { + m_iDataLenth = length + 2; + } + +-bool CBC_OnedCodaBarWriter::SetTextLocation(BC_TEXT_LOC location) { +- if (location < BC_TEXT_LOC_NONE || location > BC_TEXT_LOC_BELOWEMBED) { +- return false; +- } ++void CBC_OnedCodaBarWriter::SetTextLocation(BC_TEXT_LOC location) { + m_locTextLoc = location; +- return true; + } + + bool CBC_OnedCodaBarWriter::SetWideNarrowRatio(int8_t ratio) { +@@ -88,19 +97,10 @@ bool CBC_OnedCodaBarWriter::SetWideNarrowRatio(int8_t ratio) { + return true; + } + +-bool CBC_OnedCodaBarWriter::FindChar(wchar_t ch, bool isContent) { +- if (ch > 0x7F) +- return false; +- +- char narrow_ch = static_cast(ch); +- return pdfium::ContainsValue(kCOntentChars, narrow_ch) || +- (isContent && pdfium::ContainsValue(kStartEndChars, narrow_ch)); +-} +- + bool CBC_OnedCodaBarWriter::CheckContentValidity(WideStringView contents) { +- return std::all_of( +- contents.begin(), contents.end(), +- [this](const wchar_t& ch) { return this->FindChar(ch, false); }); ++ return HasValidContentSize(contents) && ++ std::all_of(contents.begin(), contents.end(), ++ [](const wchar_t& ch) { return IsValidChar(ch, false); }); + } + + WideString CBC_OnedCodaBarWriter::FilterContents(WideStringView contents) { +@@ -113,36 +113,22 @@ WideString CBC_OnedCodaBarWriter::FilterContents(WideStringView contents) { + index++; + continue; + } +- if (!FindChar(ch, true)) ++ if (!IsValidChar(ch, true)) + continue; + filtercontents += ch; + } + return filtercontents; + } + +-uint8_t* CBC_OnedCodaBarWriter::EncodeWithHint(const ByteString& contents, +- BCFORMAT format, +- int32_t& outWidth, +- int32_t& outHeight, +- int32_t hints) { +- if (format != BCFORMAT_CODABAR) +- return nullptr; +- return CBC_OneDimWriter::EncodeWithHint(contents, format, outWidth, outHeight, +- hints); +-} +- +-uint8_t* CBC_OnedCodaBarWriter::EncodeImpl(const ByteString& contents, +- int32_t& outLength) { ++DataVector CBC_OnedCodaBarWriter::Encode(const ByteString& contents) { + ByteString data = m_chStart + contents + m_chEnd; + m_iContentLen = data.GetLength(); +- uint8_t* result = FX_Alloc2D(uint8_t, m_iWideNarrRatio * 7, data.GetLength()); ++ DataVector result( ++ Fx2DSizeOrDie(m_iWideNarrRatio * 7, data.GetLength())); + char ch; + int32_t position = 0; + for (size_t index = 0; index < data.GetLength(); index++) { +- ch = data[index]; +- if (((ch >= 'a') && (ch <= 'z'))) { +- ch = ch - 32; +- } ++ ch = FXSYS_ToUpperASCII(data[index]); + switch (ch) { + case 'T': + ch = 'A'; +@@ -160,7 +146,7 @@ uint8_t* CBC_OnedCodaBarWriter::EncodeImpl(const ByteString& contents, + break; + } + int8_t code = 0; +- for (size_t i = 0; i < FX_ArraySize(kOnedCodaAlphabet); i++) { ++ for (size_t i = 0; i < std::size(kOnedCodaAlphabet); i++) { + if (ch == kOnedCodaAlphabet[i]) { + code = kOnedCodaCharacterEncoding[i]; + break; +@@ -185,7 +171,7 @@ uint8_t* CBC_OnedCodaBarWriter::EncodeImpl(const ByteString& contents, + position++; + } + } +- outLength = position; ++ result.resize(position); + return result; + } + +@@ -196,8 +182,7 @@ WideString CBC_OnedCodaBarWriter::encodedContents(WideStringView contents) { + } + + bool CBC_OnedCodaBarWriter::RenderResult(WideStringView contents, +- uint8_t* code, +- int32_t codeLength) { ++ pdfium::span code) { + return CBC_OneDimWriter::RenderResult( +- encodedContents(contents).AsStringView(), code, codeLength); ++ encodedContents(contents).AsStringView(), code); + } +diff --git a/fxbarcode/oned/BC_OnedCodaBarWriter.h b/fxbarcode/oned/BC_OnedCodaBarWriter.h +index 6feb380b5..32d2f59c8 100644 +--- a/fxbarcode/oned/BC_OnedCodaBarWriter.h ++++ b/fxbarcode/oned/BC_OnedCodaBarWriter.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,8 +7,9 @@ + #ifndef FXBARCODE_ONED_BC_ONEDCODABARWRITER_H_ + #define FXBARCODE_ONED_BC_ONEDCODABARWRITER_H_ + ++#include ++ + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" + #include "fxbarcode/BC_Library.h" + #include "fxbarcode/oned/BC_OneDimWriter.h" + +@@ -17,26 +18,18 @@ class CBC_OnedCodaBarWriter final : public CBC_OneDimWriter { + CBC_OnedCodaBarWriter(); + ~CBC_OnedCodaBarWriter() override; + +- // CBC_OneDimWriter +- uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override; +- uint8_t* EncodeWithHint(const ByteString& contents, +- BCFORMAT format, +- int32_t& outWidth, +- int32_t& outHeight, +- int32_t hints) override; ++ // CBC_OneDimWriter: ++ DataVector Encode(const ByteString& contents) override; + bool RenderResult(WideStringView contents, +- uint8_t* code, +- int32_t codeLength) override; ++ pdfium::span code) override; + bool CheckContentValidity(WideStringView contents) override; + WideString FilterContents(WideStringView contents) override; + void SetDataLength(int32_t length) override; +- bool SetTextLocation(BC_TEXT_LOC location) override; ++ void SetTextLocation(BC_TEXT_LOC location) override; + bool SetWideNarrowRatio(int8_t ratio) override; + bool SetStartChar(char start) override; + bool SetEndChar(char end) override; + +- virtual bool FindChar(wchar_t ch, bool isContent); +- + WideString encodedContents(WideStringView contents); + + private: +diff --git a/fxbarcode/oned/BC_OnedCodaBarWriter_unittest.cpp b/fxbarcode/oned/BC_OnedCodaBarWriter_unittest.cpp +index 89b0ac852..1f7744b6d 100644 +--- a/fxbarcode/oned/BC_OnedCodaBarWriter_unittest.cpp ++++ b/fxbarcode/oned/BC_OnedCodaBarWriter_unittest.cpp +@@ -1,58 +1,39 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fxbarcode/oned/BC_OnedCodaBarWriter.h" + ++#include ++ ++#include "core/fxcrt/data_vector.h" + #include "testing/gtest/include/gtest/gtest.h" + + namespace { + +-// 3 wide and 4 narrow modules per delimiter. One space between them. +-constexpr int kModulesForDelimiters = (3 * 2 + 4) * 2 + 1; +- +-// 2 wide and 5 narrow modules per number, '_' or '$'. 1 space between chars. +-constexpr int kModulesPerNumber = 2 * 2 + 5 + 1; +- +-// 3 wide and 4 narrow modules per number, '_' or '$'. 1 space between chars. +-constexpr int kModulesPerPunctuation = 3 * 2 + 4 + 1; +- + TEST(OnedCodaBarWriterTest, Encode) { + CBC_OnedCodaBarWriter writer; +- int32_t width; +- int32_t height; + +- uint8_t* encoded = writer.Encode("", BCFORMAT_CODABAR, width, height); +- EXPECT_EQ(1, height); +- EXPECT_EQ(kModulesForDelimiters, width); +- const char* expected = ++ static const char kExpected1[] = + "# ## # # " // A Start + "# # # ##"; // B End +- for (size_t i = 0; i < strlen(expected); i++) { +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- } +- FX_Free(encoded); ++ DataVector encoded = writer.Encode(""); ++ ASSERT_EQ(strlen(kExpected1), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected1); i++) ++ EXPECT_EQ(kExpected1[i] != ' ', !!encoded[i]) << i; + +- encoded = writer.Encode("123", BCFORMAT_CODABAR, width, height); +- EXPECT_EQ(1, height); +- EXPECT_EQ(kModulesForDelimiters + 3 * kModulesPerNumber, width); +- expected = ++ static const char kExpected2[] = + "# ## # # " // A Start + "# # ## # " // 1 + "# # # ## " // 2 + "## # # # " // 3 + "# # # ##"; // B End +- for (size_t i = 0; i < strlen(expected); i++) { +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- } +- FX_Free(encoded); ++ encoded = writer.Encode("123"); ++ ASSERT_EQ(strlen(kExpected2), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected2); i++) ++ EXPECT_EQ(kExpected2[i] != ' ', !!encoded[i]) << i; + +- encoded = writer.Encode("-$./:+", BCFORMAT_CODABAR, width, height); +- EXPECT_EQ(1, height); +- EXPECT_EQ(kModulesForDelimiters + 2 * kModulesPerNumber + +- 4 * kModulesPerPunctuation, +- width); +- expected = ++ static const char kExpected3[] = + "# ## # # " // A Start + "# # ## # " // - + "# ## # # " // $ +@@ -61,17 +42,12 @@ TEST(OnedCodaBarWriterTest, Encode) { + "## # ## ## " // : + "# ## ## ## " // + + "# # # ##"; // B End +- for (size_t i = 0; i < strlen(expected); i++) { +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- } +- FX_Free(encoded); ++ encoded = writer.Encode("-$./:+"); ++ ASSERT_EQ(strlen(kExpected3), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected3); i++) ++ EXPECT_EQ(kExpected3[i] != ' ', !!encoded[i]) << i; + +- encoded = writer.Encode("456.987987987/001", BCFORMAT_CODABAR, width, height); +- EXPECT_EQ(1, height); +- EXPECT_EQ(kModulesForDelimiters + 15 * kModulesPerNumber + +- 2 * kModulesPerPunctuation, +- width); +- expected = ++ static const char kExpected4[] = + "# ## # # " // A Start + "# ## # # " // 4 + "## # # # " // 5 +@@ -91,16 +67,14 @@ TEST(OnedCodaBarWriterTest, Encode) { + "# # # ## " // 0 + "# # ## # " // 1 + "# # # ##"; // B End +- for (size_t i = 0; i < strlen(expected); i++) { +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- } +- FX_Free(encoded); ++ encoded = writer.Encode("456.987987987/001"); ++ ASSERT_EQ(strlen(kExpected4), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected4); i++) ++ EXPECT_EQ(kExpected4[i] != ' ', !!encoded[i]) << i; + } + + TEST(OnedCodaBarWriterTest, SetDelimiters) { + CBC_OnedCodaBarWriter writer; +- int32_t width; +- int32_t height; + + EXPECT_TRUE(writer.SetStartChar('A')); + EXPECT_TRUE(writer.SetStartChar('B')); +@@ -131,19 +105,16 @@ TEST(OnedCodaBarWriterTest, SetDelimiters) { + writer.SetStartChar('N'); + writer.SetEndChar('*'); + +- uint8_t* encoded = writer.Encode("987", BCFORMAT_CODABAR, width, height); +- EXPECT_EQ(1, height); +- EXPECT_EQ(kModulesForDelimiters + 3 * kModulesPerNumber, width); +- const char* expected = ++ static const char kExpected[] = + "# # # ## " // N (same as B) Start + "## # # # " // 9 + "# ## # # " // 8 + "# # ## # " // 7 + "# # # ##"; // * (same as C) End +- for (size_t i = 0; i < strlen(expected); i++) { +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- } +- FX_Free(encoded); ++ DataVector encoded = writer.Encode("987"); ++ ASSERT_EQ(strlen(kExpected), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected); i++) ++ EXPECT_EQ(kExpected[i] != ' ', !!encoded[i]) << i; + } + + } // namespace +diff --git a/fxbarcode/oned/BC_OnedCode128Writer.cpp b/fxbarcode/oned/BC_OnedCode128Writer.cpp +index b01abb5b9..0dafaa027 100644 +--- a/fxbarcode/oned/BC_OnedCode128Writer.cpp ++++ b/fxbarcode/oned/BC_OnedCode128Writer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,18 +22,20 @@ + + #include "fxbarcode/oned/BC_OnedCode128Writer.h" + +-#include ++#include ++ + #include + + #include "core/fxcrt/fx_memory_wrappers.h" + #include "fxbarcode/BC_Writer.h" + #include "fxbarcode/oned/BC_OneDimWriter.h" ++#include "third_party/base/check.h" + + namespace { + + constexpr size_t kPatternSize = 7; + +-const int8_t CODE_PATTERNS[107][kPatternSize] = { ++const uint8_t kCodePatterns[107][kPatternSize] = { + {2, 1, 2, 2, 2, 2, 0}, {2, 2, 2, 1, 2, 2, 0}, {2, 2, 2, 2, 2, 1, 0}, + {1, 2, 1, 2, 2, 3, 0}, {1, 2, 1, 3, 2, 2, 0}, {1, 3, 1, 2, 2, 2, 0}, + {1, 2, 2, 2, 1, 3, 0}, {1, 2, 2, 3, 1, 2, 0}, {1, 3, 2, 2, 1, 2, 0}, +@@ -75,26 +77,28 @@ const int32_t CODE_START_B = 104; + const int32_t CODE_START_C = 105; + const int32_t CODE_STOP = 106; + ++bool IsInOnedCode128Alphabet(wchar_t ch) { ++ int32_t index = static_cast(ch); ++ return index >= 32 && index <= 126 && index != 34; ++} ++ + } // namespace + + CBC_OnedCode128Writer::CBC_OnedCode128Writer(BC_TYPE type) + : m_codeFormat(type) { +- ASSERT(m_codeFormat == BC_CODE128_B || m_codeFormat == BC_CODE128_C); ++ DCHECK(m_codeFormat == BC_TYPE::kCode128B || ++ m_codeFormat == BC_TYPE::kCode128C); + } + + CBC_OnedCode128Writer::~CBC_OnedCode128Writer() = default; + + bool CBC_OnedCode128Writer::CheckContentValidity(WideStringView contents) { +- for (const auto& ch : contents) { +- int32_t patternIndex = static_cast(ch); +- if (patternIndex < 32 || patternIndex > 126 || patternIndex == 34) +- return false; +- } +- return true; ++ return HasValidContentSize(contents) && ++ std::all_of(contents.begin(), contents.end(), IsInOnedCode128Alphabet); + } + + WideString CBC_OnedCode128Writer::FilterContents(WideStringView contents) { +- const wchar_t limit = m_codeFormat == BC_CODE128_B ? 126 : 106; ++ const wchar_t limit = m_codeFormat == BC_TYPE::kCode128B ? 126 : 106; + + WideString filtered; + filtered.Reserve(contents.GetLength()); +@@ -110,33 +114,17 @@ WideString CBC_OnedCode128Writer::FilterContents(WideStringView contents) { + return filtered; + } + +-bool CBC_OnedCode128Writer::SetTextLocation(BC_TEXT_LOC location) { +- if (location < BC_TEXT_LOC_NONE || location > BC_TEXT_LOC_BELOWEMBED) { +- return false; +- } ++void CBC_OnedCode128Writer::SetTextLocation(BC_TEXT_LOC location) { + m_locTextLoc = location; +- return true; +-} +- +-uint8_t* CBC_OnedCode128Writer::EncodeWithHint(const ByteString& contents, +- BCFORMAT format, +- int32_t& outWidth, +- int32_t& outHeight, +- int32_t hints) { +- if (format != BCFORMAT_CODE_128) +- return nullptr; +- return CBC_OneDimWriter::EncodeWithHint(contents, format, outWidth, outHeight, +- hints); + } + +-uint8_t* CBC_OnedCode128Writer::EncodeImpl(const ByteString& contents, +- int32_t& outLength) { ++DataVector CBC_OnedCode128Writer::Encode(const ByteString& contents) { + if (contents.GetLength() < 1 || contents.GetLength() > 80) +- return nullptr; ++ return DataVector(); + + std::vector patterns; + int32_t checkSum = 0; +- if (m_codeFormat == BC_CODE128_B) ++ if (m_codeFormat == BC_TYPE::kCode128B) + checkSum = Encode128B(contents, &patterns); + else + checkSum = Encode128C(contents, &patterns); +@@ -147,18 +135,17 @@ uint8_t* CBC_OnedCode128Writer::EncodeImpl(const ByteString& contents, + m_iContentLen = contents.GetLength() + 3; + int32_t codeWidth = 0; + for (const auto& patternIndex : patterns) { +- const int8_t* pattern = CODE_PATTERNS[patternIndex]; ++ const uint8_t* pattern = kCodePatterns[patternIndex]; + for (size_t i = 0; i < kPatternSize; ++i) + codeWidth += pattern[i]; + } +- outLength = codeWidth; +- std::unique_ptr result(FX_Alloc(uint8_t, outLength)); +- int32_t pos = 0; +- for (size_t i = 0; i < patterns.size(); ++i) { +- const int8_t* pattern = CODE_PATTERNS[patterns[i]]; +- pos += AppendPattern(result.get(), pos, pattern, kPatternSize, true); ++ DataVector result(codeWidth); ++ auto result_span = pdfium::make_span(result); ++ for (const int32_t pattern_index : patterns) { ++ const uint8_t* pattern = kCodePatterns[pattern_index]; ++ result_span = AppendPattern(result_span, {pattern, kPatternSize}, true); + } +- return result.release(); ++ return result; + } + + // static +@@ -185,12 +172,12 @@ int32_t CBC_OnedCode128Writer::Encode128C(const ByteString& contents, + while (position < contents.GetLength()) { + int32_t patternIndex; + char ch = contents[position]; +- if (std::isdigit(ch)) { ++ if (isdigit(ch)) { + patternIndex = FXSYS_atoi( + contents.Substr(position, contents.IsValidIndex(position + 1) ? 2 : 1) + .c_str()); + ++position; +- if (position < contents.GetLength() && std::isdigit(contents[position])) ++ if (position < contents.GetLength() && isdigit(contents[position])) + ++position; + } else { + patternIndex = static_cast(ch); +diff --git a/fxbarcode/oned/BC_OnedCode128Writer.h b/fxbarcode/oned/BC_OnedCode128Writer.h +index 370c02509..121c02bbc 100644 +--- a/fxbarcode/oned/BC_OnedCode128Writer.h ++++ b/fxbarcode/oned/BC_OnedCode128Writer.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,12 @@ + #ifndef FXBARCODE_ONED_BC_ONEDCODE128WRITER_H_ + #define FXBARCODE_ONED_BC_ONEDCODE128WRITER_H_ + ++#include ++ + #include + + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "fxbarcode/BC_Library.h" + #include "fxbarcode/oned/BC_OneDimWriter.h" + + class CBC_OnedCode128Writer final : public CBC_OneDimWriter { +@@ -25,15 +27,10 @@ class CBC_OnedCode128Writer final : public CBC_OneDimWriter { + std::vector* patterns); + + // CBC_OneDimWriter +- uint8_t* EncodeWithHint(const ByteString& contents, +- BCFORMAT format, +- int32_t& outWidth, +- int32_t& outHeight, +- int32_t hints) override; +- uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override; ++ DataVector Encode(const ByteString& contents) override; + bool CheckContentValidity(WideStringView contents) override; + WideString FilterContents(WideStringView contents) override; +- bool SetTextLocation(BC_TEXT_LOC location) override; ++ void SetTextLocation(BC_TEXT_LOC location) override; + + BC_TYPE GetType() const { return m_codeFormat; } + +diff --git a/fxbarcode/oned/BC_OnedCode128Writer_unittest.cpp b/fxbarcode/oned/BC_OnedCode128Writer_unittest.cpp +index 19d91f9eb..ee1d9b1f0 100644 +--- a/fxbarcode/oned/BC_OnedCode128Writer_unittest.cpp ++++ b/fxbarcode/oned/BC_OnedCode128Writer_unittest.cpp +@@ -1,9 +1,11 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fxbarcode/oned/BC_OnedCode128Writer.h" + ++#include ++ + #include "testing/gtest/include/gtest/gtest.h" + + namespace { +@@ -31,7 +33,7 @@ TEST(OnedCode128WriterTest, Encode128B) { + {"321ABC", 722, {104, 19, 18, 17, 33, 34, 35}, 7}, + {"XYZ", 448, {104, 56, 57, 58}, 4}, + }; +- for (size_t i = 0; i < FX_ArraySize(kTestCases); ++i) { ++ for (size_t i = 0; i < std::size(kTestCases); ++i) { + FXSYS_snprintf(buf, sizeof(buf) - 1, "Test case %zu", i); + SCOPED_TRACE(buf); + const TestCase& test_case = kTestCases[i]; +@@ -64,7 +66,7 @@ TEST(OnedCode128WriterTest, Encode128C) { + {"321ABC", 933, {105, 32, 1, 65, 66, 67}, 6}, + {"XYZ", 641, {105, 88, 89, 90}, 4}, + }; +- for (size_t i = 0; i < FX_ArraySize(kTestCases); ++i) { ++ for (size_t i = 0; i < std::size(kTestCases); ++i) { + FXSYS_snprintf(buf, sizeof(buf) - 1, "Test case %zu", i); + SCOPED_TRACE(buf); + const TestCase& test_case = kTestCases[i]; +@@ -83,20 +85,20 @@ TEST(OnedCode128WriterTest, Encode128C) { + + TEST(OnedCode128WriterTest, CheckContentValidity) { + { +- CBC_OnedCode128Writer writer(BC_CODE128_B); +- EXPECT_TRUE(writer.CheckContentValidity(L"")); ++ CBC_OnedCode128Writer writer(BC_TYPE::kCode128B); + EXPECT_TRUE(writer.CheckContentValidity(L"foo")); + EXPECT_TRUE(writer.CheckContentValidity(L"xyz")); ++ EXPECT_FALSE(writer.CheckContentValidity(L"")); + EXPECT_FALSE(writer.CheckContentValidity(L"\"")); + EXPECT_FALSE(writer.CheckContentValidity(L"f\x10oo")); + EXPECT_FALSE(writer.CheckContentValidity(L"bar\x7F")); + EXPECT_FALSE(writer.CheckContentValidity(L"qux\x88")); + } + { +- CBC_OnedCode128Writer writer(BC_CODE128_C); +- EXPECT_TRUE(writer.CheckContentValidity(L"")); ++ CBC_OnedCode128Writer writer(BC_TYPE::kCode128C); + EXPECT_TRUE(writer.CheckContentValidity(L"foo")); + EXPECT_TRUE(writer.CheckContentValidity(L"xyz")); ++ EXPECT_FALSE(writer.CheckContentValidity(L"")); + EXPECT_FALSE(writer.CheckContentValidity(L"\"")); + EXPECT_FALSE(writer.CheckContentValidity(L"f\x10oo")); + EXPECT_FALSE(writer.CheckContentValidity(L"bar\x7F")); +@@ -106,7 +108,7 @@ TEST(OnedCode128WriterTest, CheckContentValidity) { + + TEST(OnedCode128WriterTest, FilterContents) { + { +- CBC_OnedCode128Writer writer(BC_CODE128_B); ++ CBC_OnedCode128Writer writer(BC_TYPE::kCode128B); + EXPECT_STREQ(L"", writer.FilterContents(L"").c_str()); + EXPECT_STREQ(L"foo", writer.FilterContents(L"foo\x10").c_str()); + EXPECT_STREQ(L"fool", writer.FilterContents(L"foo\x10l").c_str()); +@@ -115,7 +117,7 @@ TEST(OnedCode128WriterTest, FilterContents) { + EXPECT_STREQ(L"bar", writer.FilterContents(L"bar\x10\x7F\x88").c_str()); + } + { +- CBC_OnedCode128Writer writer(BC_CODE128_C); ++ CBC_OnedCode128Writer writer(BC_TYPE::kCode128C); + EXPECT_STREQ(L"", writer.FilterContents(L"").c_str()); + EXPECT_STREQ(L"f", writer.FilterContents(L"foo\x10").c_str()); + EXPECT_STREQ(L"f", writer.FilterContents(L"foo\x10l").c_str()); +diff --git a/fxbarcode/oned/BC_OnedCode39Writer.cpp b/fxbarcode/oned/BC_OnedCode39Writer.cpp +index 97ee881f1..2f781c27f 100644 +--- a/fxbarcode/oned/BC_OnedCode39Writer.cpp ++++ b/fxbarcode/oned/BC_OnedCode39Writer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -23,6 +23,7 @@ + #include "fxbarcode/oned/BC_OnedCode39Writer.h" + + #include ++#include + #include + + #include "core/fxcrt/fx_extension.h" +@@ -37,13 +38,13 @@ const char kOnedCode39Alphabet[] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', + 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '*', '$', '/', '+', '%'}; +-constexpr size_t kOnedCode39AlphabetLen = FX_ArraySize(kOnedCode39Alphabet); ++constexpr size_t kOnedCode39AlphabetLen = std::size(kOnedCode39Alphabet); + + const char kOnedCode39Checksum[] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', + 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%'}; +-static_assert(FX_ArraySize(kOnedCode39Checksum) == 43, "Wrong size"); ++static_assert(std::size(kOnedCode39Checksum) == 43, "Wrong size"); + + const int16_t kOnedCode39CharacterEncoding[] = { + 0x0034, 0x0121, 0x0061, 0x0160, 0x0031, 0x0130, 0x0070, 0x0025, 0x0124, +@@ -51,7 +52,7 @@ const int16_t kOnedCode39CharacterEncoding[] = { + 0x004C, 0x001C, 0x0103, 0x0043, 0x0142, 0x0013, 0x0112, 0x0052, 0x0007, + 0x0106, 0x0046, 0x0016, 0x0181, 0x00C1, 0x01C0, 0x0091, 0x0190, 0x00D0, + 0x0085, 0x0184, 0x00C4, 0x0094, 0x00A8, 0x00A2, 0x008A, 0x002A}; +-static_assert(FX_ArraySize(kOnedCode39CharacterEncoding) == 44, "Wrong size"); ++static_assert(std::size(kOnedCode39CharacterEncoding) == 44, "Wrong size"); + + bool IsInOnedCode39Alphabet(wchar_t ch) { + return FXSYS_IsDecimalDigit(ch) || (ch >= L'A' && ch <= L'Z') || ch == L'-' || +@@ -59,6 +60,26 @@ bool IsInOnedCode39Alphabet(wchar_t ch) { + ch == L'+' || ch == L'%'; + } + ++char CalcCheckSum(const ByteString& contents) { ++ if (contents.GetLength() > 80) ++ return '*'; ++ ++ int32_t checksum = 0; ++ for (const auto& c : contents) { ++ size_t j = 0; ++ for (; j < kOnedCode39AlphabetLen; j++) { ++ if (kOnedCode39Alphabet[j] == c) { ++ if (c != '*') ++ checksum += j; ++ break; ++ } ++ } ++ if (j >= kOnedCode39AlphabetLen) ++ return '*'; ++ } ++ return kOnedCode39Checksum[checksum % std::size(kOnedCode39Checksum)]; ++} ++ + } // namespace + + CBC_OnedCode39Writer::CBC_OnedCode39Writer() = default; +@@ -66,7 +87,8 @@ CBC_OnedCode39Writer::CBC_OnedCode39Writer() = default; + CBC_OnedCode39Writer::~CBC_OnedCode39Writer() = default; + + bool CBC_OnedCode39Writer::CheckContentValidity(WideStringView contents) { +- return std::all_of(contents.begin(), contents.end(), IsInOnedCode39Alphabet); ++ return HasValidContentSize(contents) && ++ std::all_of(contents.begin(), contents.end(), IsInOnedCode39Alphabet); + } + + WideString CBC_OnedCode39Writer::FilterContents(WideStringView contents) { +@@ -105,13 +127,10 @@ WideString CBC_OnedCode39Writer::RenderTextContents(WideStringView contents) { + return renderContents; + } + +-bool CBC_OnedCode39Writer::SetTextLocation(BC_TEXT_LOC location) { +- if (location < BC_TEXT_LOC_NONE || location > BC_TEXT_LOC_BELOWEMBED) { +- return false; +- } ++void CBC_OnedCode39Writer::SetTextLocation(BC_TEXT_LOC location) { + m_locTextLoc = location; +- return true; + } ++ + bool CBC_OnedCode39Writer::SetWideNarrowRatio(int8_t ratio) { + if (ratio < 2 || ratio > 3) + return false; +@@ -120,75 +139,45 @@ bool CBC_OnedCode39Writer::SetWideNarrowRatio(int8_t ratio) { + return true; + } + +-uint8_t* CBC_OnedCode39Writer::EncodeWithHint(const ByteString& contents, +- BCFORMAT format, +- int32_t& outWidth, +- int32_t& outHeight, +- int32_t hints) { +- if (format != BCFORMAT_CODE_39) +- return nullptr; +- return CBC_OneDimWriter::EncodeWithHint(contents, format, outWidth, outHeight, +- hints); +-} +- +-void CBC_OnedCode39Writer::ToIntArray(int16_t a, int8_t* toReturn) { +- for (int32_t i = 0; i < 9; i++) { +- toReturn[i] = (a & (1 << i)) == 0 ? 1 : m_iWideNarrRatio; +- } +-} +- +-char CBC_OnedCode39Writer::CalcCheckSum(const ByteString& contents) { +- if (contents.GetLength() > 80) +- return '*'; +- +- int32_t checksum = 0; +- for (const auto& c : contents) { +- size_t j = 0; +- for (; j < kOnedCode39AlphabetLen; j++) { +- if (kOnedCode39Alphabet[j] == c) { +- if (c != '*') +- checksum += j; +- break; +- } +- } +- if (j >= kOnedCode39AlphabetLen) +- return '*'; ++void CBC_OnedCode39Writer::ToIntArray(int16_t value, ++ uint8_t array[kArraySize]) const { ++ for (size_t i = 0; i < kArraySize; i++) { ++ array[i] = (value & (1 << i)) == 0 ? 1 : m_iWideNarrRatio; + } +- return kOnedCode39Checksum[checksum % FX_ArraySize(kOnedCode39Checksum)]; + } + +-uint8_t* CBC_OnedCode39Writer::EncodeImpl(const ByteString& contents, +- int32_t& outlength) { ++DataVector CBC_OnedCode39Writer::Encode(const ByteString& contents) { + char checksum = CalcCheckSum(contents); + if (checksum == '*') +- return nullptr; ++ return DataVector(); + +- int8_t widths[9] = {0}; +- int32_t wideStrideNum = 3; +- int32_t narrStrideNum = 9 - wideStrideNum; ++ uint8_t widths[kArraySize] = {0}; ++ constexpr int32_t kWideStrideNum = 3; ++ constexpr int32_t kNarrowStrideNum = kArraySize - kWideStrideNum; + ByteString encodedContents = contents; + if (m_bCalcChecksum) + encodedContents += checksum; + m_iContentLen = encodedContents.GetLength(); +- int32_t codeWidth = (wideStrideNum * m_iWideNarrRatio + narrStrideNum) * 2 + +- 1 + m_iContentLen; ++ size_t code_width = ++ (kWideStrideNum * m_iWideNarrRatio + kNarrowStrideNum) * 2 + 1 + ++ m_iContentLen; + for (size_t j = 0; j < m_iContentLen; j++) { + for (size_t i = 0; i < kOnedCode39AlphabetLen; i++) { + if (kOnedCode39Alphabet[i] != encodedContents[j]) + continue; + + ToIntArray(kOnedCode39CharacterEncoding[i], widths); +- for (size_t k = 0; k < 9; k++) +- codeWidth += widths[k]; ++ for (size_t k = 0; k < kArraySize; k++) ++ code_width += widths[k]; + } + } +- outlength = codeWidth; +- std::unique_ptr result(FX_Alloc(uint8_t, codeWidth)); ++ DataVector result(code_width); ++ auto result_span = pdfium::make_span(result); + ToIntArray(kOnedCode39CharacterEncoding[39], widths); +- int32_t pos = AppendPattern(result.get(), 0, widths, 9, true); ++ result_span = AppendPattern(result_span, widths, true); + +- int8_t narrowWhite[] = {1}; +- pos += AppendPattern(result.get(), pos, narrowWhite, 1, false); ++ static constexpr uint8_t kNarrowWhite[] = {1}; ++ result_span = AppendPattern(result_span, kNarrowWhite, false); + + for (int32_t l = m_iContentLen - 1; l >= 0; l--) { + for (size_t i = 0; i < kOnedCode39AlphabetLen; i++) { +@@ -196,20 +185,19 @@ uint8_t* CBC_OnedCode39Writer::EncodeImpl(const ByteString& contents, + continue; + + ToIntArray(kOnedCode39CharacterEncoding[i], widths); +- pos += AppendPattern(result.get(), pos, widths, 9, true); ++ result_span = AppendPattern(result_span, widths, true); + } +- pos += AppendPattern(result.get(), pos, narrowWhite, 1, false); ++ result_span = AppendPattern(result_span, kNarrowWhite, false); + } + ToIntArray(kOnedCode39CharacterEncoding[39], widths); +- pos += AppendPattern(result.get(), pos, widths, 9, true); ++ AppendPattern(result_span, widths, true); + +- auto* result_ptr = result.get(); +- for (int32_t i = 0; i < codeWidth / 2; i++) { +- result_ptr[i] ^= result_ptr[codeWidth - 1 - i]; +- result_ptr[codeWidth - 1 - i] ^= result_ptr[i]; +- result_ptr[i] ^= result_ptr[codeWidth - 1 - i]; ++ for (size_t i = 0; i < code_width / 2; i++) { ++ result[i] ^= result[code_width - 1 - i]; ++ result[code_width - 1 - i] ^= result[i]; ++ result[i] ^= result[code_width - 1 - i]; + } +- return result.release(); ++ return result; + } + + bool CBC_OnedCode39Writer::encodedContents(WideStringView contents, +@@ -229,11 +217,9 @@ bool CBC_OnedCode39Writer::encodedContents(WideStringView contents, + } + + bool CBC_OnedCode39Writer::RenderResult(WideStringView contents, +- uint8_t* code, +- int32_t codeLength) { ++ pdfium::span code) { + WideString encodedCon; + if (!encodedContents(contents, &encodedCon)) + return false; +- return CBC_OneDimWriter::RenderResult(encodedCon.AsStringView(), code, +- codeLength); ++ return CBC_OneDimWriter::RenderResult(encodedCon.AsStringView(), code); + } +diff --git a/fxbarcode/oned/BC_OnedCode39Writer.h b/fxbarcode/oned/BC_OnedCode39Writer.h +index 525573a53..932d703ad 100644 +--- a/fxbarcode/oned/BC_OnedCode39Writer.h ++++ b/fxbarcode/oned/BC_OnedCode39Writer.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,8 @@ + #ifndef FXBARCODE_ONED_BC_ONEDCODE39WRITER_H_ + #define FXBARCODE_ONED_BC_ONEDCODE39WRITER_H_ + ++#include ++ + #include "fxbarcode/BC_Library.h" + #include "fxbarcode/oned/BC_OneDimWriter.h" + +@@ -16,26 +18,21 @@ class CBC_OnedCode39Writer final : public CBC_OneDimWriter { + ~CBC_OnedCode39Writer() override; + + // CBC_OneDimWriter +- uint8_t* EncodeWithHint(const ByteString& contents, +- BCFORMAT format, +- int32_t& outWidth, +- int32_t& outHeight, +- int32_t hints) override; +- uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override; ++ DataVector Encode(const ByteString& contents) override; + bool RenderResult(WideStringView contents, +- uint8_t* code, +- int32_t codeLength) override; ++ pdfium::span code) override; + bool CheckContentValidity(WideStringView contents) override; + WideString FilterContents(WideStringView contents) override; +- bool SetTextLocation(BC_TEXT_LOC location) override; ++ void SetTextLocation(BC_TEXT_LOC location) override; + bool SetWideNarrowRatio(int8_t ratio) override; + + WideString RenderTextContents(WideStringView contents); + bool encodedContents(WideStringView contents, WideString* result); + + private: +- void ToIntArray(int16_t a, int8_t* toReturn); +- char CalcCheckSum(const ByteString& contents); ++ static constexpr size_t kArraySize = 9; ++ ++ void ToIntArray(int16_t value, uint8_t array[kArraySize]) const; + + int8_t m_iWideNarrRatio = 3; + }; +diff --git a/fxbarcode/oned/BC_OnedCode39Writer_unittest.cpp b/fxbarcode/oned/BC_OnedCode39Writer_unittest.cpp +index e263d5995..b0645f4a4 100644 +--- a/fxbarcode/oned/BC_OnedCode39Writer_unittest.cpp ++++ b/fxbarcode/oned/BC_OnedCode39Writer_unittest.cpp +@@ -1,24 +1,16 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fxbarcode/oned/BC_OnedCode39Writer.h" + +-#include ++#include + ++#include "core/fxcrt/data_vector.h" + #include "testing/gtest/include/gtest/gtest.h" + + namespace { + +-// 3 wide and 6 narrow modules per char. 1 space between chars. +-constexpr int MODULES_PER_CHAR = 3 * 3 + 6 + 1; +- +-// '*' is added as the first and last char. +-const int DELIMITER_CHARS = 2; +- +-// Last char may serve as checksum. +-const int CHECKSUM_CHARS = 1; +- + TEST(OnedCode39WriterTest, SetWideNarrowRatio) { + // Code 39 barcodes encode strings of any size into modules in a + // unidimensional disposition. +@@ -35,13 +27,7 @@ TEST(OnedCode39WriterTest, SetWideNarrowRatio) { + + writer.SetWideNarrowRatio(3); + +- int32_t width; +- int32_t height; +- uint8_t* encoded; +- const char* expected; +- +- encoded = writer.Encode("PDFIUM", BCFORMAT_CODE_39, width, height); +- expected = ++ static const char kExpected1[] = + "# # ### ### # " // * Start + "# ### ### # # " // P + "# # ### # ### " // D +@@ -50,14 +36,14 @@ TEST(OnedCode39WriterTest, SetWideNarrowRatio) { + "### # # # ### " // U + "### ### # # # " // M + "# # ### ### #"; // * End +- for (size_t i = 0; i < strlen(expected); i++) +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- FX_Free(encoded); ++ DataVector encoded = writer.Encode("PDFIUM"); ++ ASSERT_EQ(strlen(kExpected1), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected1); i++) ++ EXPECT_EQ(kExpected1[i] != ' ', !!encoded[i]) << i; + + writer.SetWideNarrowRatio(2); + +- encoded = writer.Encode("PDFIUM", BCFORMAT_CODE_39, width, height); +- expected = ++ static const char kExpected2[] = + "# # ## ## # " // * Start + "# ## ## # # " // P + "# # ## # ## " // D +@@ -66,45 +52,35 @@ TEST(OnedCode39WriterTest, SetWideNarrowRatio) { + "## # # # ## " // U + "## ## # # # " // M + "# # ## ## #"; // * End +- for (size_t i = 0; i < strlen(expected); i++) +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- FX_Free(encoded); ++ encoded = writer.Encode("PDFIUM"); ++ ASSERT_EQ(strlen(kExpected2), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected2); i++) ++ EXPECT_EQ(kExpected2[i] != ' ', !!encoded[i]) << i; + } + + TEST(OnedCode39WriterTest, Encode) { + CBC_OnedCode39Writer writer; +- int32_t width; +- int32_t height; +- uint8_t* encoded; +- const char* expected; +- +- encoded = writer.Encode("", BCFORMAT_CODE_39, width, height); +- EXPECT_EQ(1, height); +- EXPECT_EQ((0 + DELIMITER_CHARS) * MODULES_PER_CHAR - 1, width); +- expected = ++ ++ static const char kExpected1[] = + "# # ### ### # " // * Start + "# # ### ### #"; // * End +- for (size_t i = 0; i < strlen(expected); i++) +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- FX_Free(encoded); +- +- encoded = writer.Encode("123", BCFORMAT_CODE_39, width, height); +- EXPECT_EQ(1, height); +- EXPECT_EQ((3 + DELIMITER_CHARS) * MODULES_PER_CHAR - 1, width); +- expected = ++ DataVector encoded = writer.Encode(""); ++ ASSERT_EQ(strlen(kExpected1), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected1); i++) ++ EXPECT_EQ(kExpected1[i] != ' ', !!encoded[i]) << i; ++ ++ static const char kExpected2[] = + "# # ### ### # " // * Start + "### # # # ### " // 1 + "# ### # # ### " // 2 + "### ### # # # " // 3 + "# # ### ### #"; // * End +- for (size_t i = 0; i < strlen(expected); i++) +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- FX_Free(encoded); +- +- encoded = writer.Encode("PDFIUM", BCFORMAT_CODE_39, width, height); +- EXPECT_EQ(1, height); +- EXPECT_EQ((6 + DELIMITER_CHARS) * MODULES_PER_CHAR - 1, width); +- expected = ++ encoded = writer.Encode("123"); ++ ASSERT_EQ(strlen(kExpected2), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected2); i++) ++ EXPECT_EQ(kExpected2[i] != ' ', !!encoded[i]) << i; ++ ++ static const char kExpected3[] = + "# # ### ### # " // * Start + "# ### ### # # " // P + "# # ### # ### " // D +@@ -113,14 +89,12 @@ TEST(OnedCode39WriterTest, Encode) { + "### # # # ### " // U + "### ### # # # " // M + "# # ### ### #"; // * End +- for (size_t i = 0; i < strlen(expected); i++) +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- FX_Free(encoded); +- +- encoded = writer.Encode("A -$%./+Z", BCFORMAT_CODE_39, width, height); +- EXPECT_EQ(1, height); +- EXPECT_EQ((9 + DELIMITER_CHARS) * MODULES_PER_CHAR - 1, width); +- expected = ++ encoded = writer.Encode("PDFIUM"); ++ ASSERT_EQ(strlen(kExpected3), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected3); i++) ++ EXPECT_EQ(kExpected3[i] != ' ', !!encoded[i]) << i; ++ ++ static const char kExpected4[] = + "# # ### ### # " // * Start + "### # # # ### " // A + "# ### # ### # " // Space +@@ -132,40 +106,29 @@ TEST(OnedCode39WriterTest, Encode) { + "# # # # # " // + + "# ### ### # # " // Z + "# # ### ### #"; // * End +- for (size_t i = 0; i < strlen(expected); i++) +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- FX_Free(encoded); ++ encoded = writer.Encode("A -$%./+Z"); ++ ASSERT_EQ(strlen(kExpected4), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected4); i++) ++ EXPECT_EQ(kExpected4[i] != ' ', !!encoded[i]) << i; + } + + TEST(OnedCode39WriterTest, Checksum) { + CBC_OnedCode39Writer writer; +- int32_t width; +- int32_t height; +- uint8_t* encoded; +- const char* expected; +- + writer.SetCalcChecksum(true); + +- encoded = writer.Encode("123", BCFORMAT_CODE_39, width, height); +- EXPECT_EQ(1, height); +- EXPECT_EQ((3 + CHECKSUM_CHARS + DELIMITER_CHARS) * MODULES_PER_CHAR - 1, +- width); +- expected = ++ static const char kExpected1[] = + "# # ### ### # " // * Start + "### # # # ### " // 1 (1) + "# ### # # ### " // 2 (2) + "### ### # # # " // 3 (3) + "# ### ### # # " // 6 (6 = (1 + 2 + 3) % 43) + "# # ### ### #"; // * End +- for (size_t i = 0; i < strlen(expected); i++) +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- FX_Free(encoded); +- +- encoded = writer.Encode("PDFIUM", BCFORMAT_CODE_39, width, height); +- EXPECT_EQ(1, height); +- EXPECT_EQ((6 + CHECKSUM_CHARS + DELIMITER_CHARS) * MODULES_PER_CHAR - 1, +- width); +- expected = ++ DataVector encoded = writer.Encode("123"); ++ ASSERT_EQ(strlen(kExpected1), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected1); i++) ++ EXPECT_EQ(kExpected1[i] != ' ', !!encoded[i]) << i; ++ ++ static const char kExpected2[] = + "# # ### ### # " // * Start + "# ### ### # # " // P (25) + "# # ### # ### " // D (13) +@@ -175,9 +138,10 @@ TEST(OnedCode39WriterTest, Checksum) { + "### ### # # # " // M (22) + "### # # ### # " // . (37 = (25 + 13 + 15 + 18 + 30 + 22) % 43) + "# # ### ### #"; // * End +- for (size_t i = 0; i < strlen(expected); i++) +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- FX_Free(encoded); ++ encoded = writer.Encode("PDFIUM"); ++ ASSERT_EQ(strlen(kExpected2), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected2); i++) ++ EXPECT_EQ(kExpected2[i] != ' ', !!encoded[i]) << i; + } + + } // namespace +diff --git a/fxbarcode/oned/BC_OnedEAN13Writer.cpp b/fxbarcode/oned/BC_OnedEAN13Writer.cpp +index f2e88ddaf..325d4247c 100644 +--- a/fxbarcode/oned/BC_OnedEAN13Writer.cpp ++++ b/fxbarcode/oned/BC_OnedEAN13Writer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,8 +22,9 @@ + + #include "fxbarcode/oned/BC_OnedEAN13Writer.h" + ++#include ++ + #include +-#include + #include + #include + +@@ -39,12 +40,12 @@ namespace { + + const int8_t kFirstDigitEncodings[10] = {0x00, 0x0B, 0x0D, 0xE, 0x13, + 0x19, 0x1C, 0x15, 0x16, 0x1A}; +-const int8_t kOnedEAN13StartPattern[3] = {1, 1, 1}; +-const int8_t kOnedEAN13MiddlePattern[5] = {1, 1, 1, 1, 1}; +-const int8_t kOnedEAN13LPattern[10][4] = { ++const uint8_t kOnedEAN13StartPattern[3] = {1, 1, 1}; ++const uint8_t kOnedEAN13MiddlePattern[5] = {1, 1, 1, 1, 1}; ++const uint8_t kOnedEAN13LPattern[10][4] = { + {3, 2, 1, 1}, {2, 2, 2, 1}, {2, 1, 2, 2}, {1, 4, 1, 1}, {1, 1, 3, 2}, + {1, 2, 3, 1}, {1, 1, 1, 4}, {1, 3, 1, 2}, {1, 2, 1, 3}, {3, 1, 1, 2}}; +-const int8_t L_AND_G_PATTERNS[20][4] = { ++const uint8_t kOnedEAN13LGPattern[20][4] = { + {3, 2, 1, 1}, {2, 2, 2, 1}, {2, 1, 2, 2}, {1, 4, 1, 1}, {1, 1, 3, 2}, + {1, 2, 3, 1}, {1, 1, 1, 4}, {1, 3, 1, 2}, {1, 2, 1, 3}, {3, 1, 1, 2}, + {1, 1, 2, 3}, {1, 2, 2, 2}, {2, 2, 1, 2}, {1, 1, 4, 1}, {2, 3, 1, 1}, +@@ -56,10 +57,11 @@ CBC_OnedEAN13Writer::CBC_OnedEAN13Writer() { + m_bLeftPadding = true; + m_codeWidth = 3 + (7 * 6) + 5 + (7 * 6) + 3; + } +-CBC_OnedEAN13Writer::~CBC_OnedEAN13Writer() {} ++CBC_OnedEAN13Writer::~CBC_OnedEAN13Writer() = default; + + bool CBC_OnedEAN13Writer::CheckContentValidity(WideStringView contents) { +- return std::all_of(contents.begin(), contents.end(), ++ return HasValidContentSize(contents) && ++ std::all_of(contents.begin(), contents.end(), + [](wchar_t c) { return FXSYS_IsDecimalDigit(c); }); + } + +@@ -83,134 +85,110 @@ int32_t CBC_OnedEAN13Writer::CalcChecksum(const ByteString& contents) { + return EANCalcChecksum(contents); + } + +-uint8_t* CBC_OnedEAN13Writer::EncodeWithHint(const ByteString& contents, +- BCFORMAT format, +- int32_t& outWidth, +- int32_t& outHeight, +- int32_t hints) { +- if (format != BCFORMAT_EAN_13) +- return nullptr; +- return CBC_OneDimWriter::EncodeWithHint(contents, format, outWidth, outHeight, +- hints); +-} +- +-uint8_t* CBC_OnedEAN13Writer::EncodeImpl(const ByteString& contents, +- int32_t& outLength) { ++DataVector CBC_OnedEAN13Writer::Encode(const ByteString& contents) { + if (contents.GetLength() != 13) +- return nullptr; ++ return DataVector(); + + m_iDataLenth = 13; + int32_t firstDigit = FXSYS_DecimalCharToInt(contents.Front()); + int32_t parities = kFirstDigitEncodings[firstDigit]; +- outLength = m_codeWidth; +- std::unique_ptr result( +- FX_Alloc(uint8_t, m_codeWidth)); +- int32_t pos = 0; +- pos += AppendPattern(result.get(), pos, kOnedEAN13StartPattern, 3, true); +- +- int32_t i = 0; +- for (i = 1; i <= 6; i++) { ++ DataVector result(m_codeWidth); ++ auto result_span = pdfium::make_span(result); ++ result_span = AppendPattern(result_span, kOnedEAN13StartPattern, true); ++ ++ for (int i = 1; i <= 6; i++) { + int32_t digit = FXSYS_DecimalCharToInt(contents[i]); + if ((parities >> (6 - i) & 1) == 1) { + digit += 10; + } +- pos += AppendPattern(result.get(), pos, L_AND_G_PATTERNS[digit], 4, false); ++ result_span = AppendPattern(result_span, kOnedEAN13LGPattern[digit], false); + } +- pos += AppendPattern(result.get(), pos, kOnedEAN13MiddlePattern, 5, false); ++ result_span = AppendPattern(result_span, kOnedEAN13MiddlePattern, false); + +- for (i = 7; i <= 12; i++) { ++ for (int i = 7; i <= 12; i++) { + int32_t digit = FXSYS_DecimalCharToInt(contents[i]); +- pos += AppendPattern(result.get(), pos, kOnedEAN13LPattern[digit], 4, true); ++ result_span = AppendPattern(result_span, kOnedEAN13LPattern[digit], true); + } +- pos += AppendPattern(result.get(), pos, kOnedEAN13StartPattern, 3, true); +- return result.release(); ++ AppendPattern(result_span, kOnedEAN13StartPattern, true); ++ return result; + } + + bool CBC_OnedEAN13Writer::ShowChars(WideStringView contents, + CFX_RenderDevice* device, +- const CFX_Matrix* matrix, +- int32_t barWidth, +- int32_t multiple) { ++ const CFX_Matrix& matrix, ++ int32_t barWidth) { + if (!device) + return false; + +- int32_t leftPadding = 7 * multiple; +- int32_t leftPosition = 3 * multiple + leftPadding; ++ constexpr float kLeftPosition = 10.0f; + ByteString str = FX_UTF8Encode(contents); + size_t length = str.GetLength(); + std::vector charpos(length); +- int32_t iFontSize = (int32_t)fabs(m_fFontSize); ++ int32_t iFontSize = static_cast(fabs(m_fFontSize)); + int32_t iTextHeight = iFontSize + 1; + ByteString tempStr = str.Substr(1, 6); +- int32_t strWidth = multiple * 42; ++ constexpr int32_t kWidth = 42; + + CFX_Matrix matr(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0); +- CFX_FloatRect rect((float)leftPosition, (float)(m_Height - iTextHeight), +- (float)(leftPosition + strWidth - 0.5), (float)m_Height); +- matr.Concat(*matrix); ++ CFX_FloatRect rect(kLeftPosition, (float)(m_Height - iTextHeight), ++ kLeftPosition + kWidth - 0.5, (float)m_Height); ++ matr.Concat(matrix); + FX_RECT re = matr.TransformRect(rect).GetOuterRect(); + device->FillRect(re, kBackgroundColor); +- CFX_FloatRect rect1( +- (float)(leftPosition + 47 * multiple), (float)(m_Height - iTextHeight), +- (float)(leftPosition + 47 * multiple + strWidth - 0.5), (float)m_Height); ++ CFX_FloatRect rect1(kLeftPosition + 47, (float)(m_Height - iTextHeight), ++ kLeftPosition + 47 + kWidth - 0.5, (float)m_Height); + CFX_Matrix matr1(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0); +- matr1.Concat(*matrix); ++ matr1.Concat(matrix); + re = matr1.TransformRect(rect1).GetOuterRect(); + device->FillRect(re, kBackgroundColor); +- int32_t strWidth1 = multiple * 7; + CFX_Matrix matr2(m_outputHScale, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); +- CFX_FloatRect rect2(0.0f, (float)(m_Height - iTextHeight), +- (float)strWidth1 - 0.5f, (float)m_Height); +- matr2.Concat(*matrix); ++ CFX_FloatRect rect2(0.0f, (float)(m_Height - iTextHeight), 6.5f, ++ (float)m_Height); ++ matr2.Concat(matrix); + re = matr2.TransformRect(rect2).GetOuterRect(); + device->FillRect(re, kBackgroundColor); + +- float blank = 0.0; ++ float blank = 0.0f; + length = tempStr.GetLength(); +- strWidth = (int32_t)(strWidth * m_outputHScale); ++ int32_t strWidth = static_cast(kWidth * m_outputHScale); + +- CalcTextInfo(tempStr, &charpos[1], m_pFont.Get(), (float)strWidth, iFontSize, ++ CalcTextInfo(tempStr, &charpos[1], m_pFont, (float)strWidth, iFontSize, + blank); + { + CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0, +- (float)leftPosition * m_outputHScale, ++ kLeftPosition * m_outputHScale, + (float)(m_Height - iTextHeight) + iFontSize); +- if (matrix) +- affine_matrix1.Concat(*matrix); +- device->DrawNormalText(length, &charpos[1], m_pFont.Get(), +- static_cast(iFontSize), affine_matrix1, +- m_fontColor, FXTEXT_CLEARTYPE); ++ affine_matrix1.Concat(matrix); ++ device->DrawNormalText(pdfium::make_span(charpos).subspan(1, length), ++ m_pFont, static_cast(iFontSize), ++ affine_matrix1, m_fontColor, GetTextRenderOptions()); + } + tempStr = str.Substr(7, 6); + length = tempStr.GetLength(); +- CalcTextInfo(tempStr, &charpos[7], m_pFont.Get(), (float)strWidth, iFontSize, ++ CalcTextInfo(tempStr, &charpos[7], m_pFont, (float)strWidth, iFontSize, + blank); + { +- CFX_Matrix affine_matrix1( +- 1.0, 0.0, 0.0, -1.0, +- (float)(leftPosition + 47 * multiple) * m_outputHScale, +- (float)(m_Height - iTextHeight + iFontSize)); +- if (matrix) +- affine_matrix1.Concat(*matrix); +- device->DrawNormalText(length, &charpos[7], m_pFont.Get(), +- static_cast(iFontSize), affine_matrix1, +- m_fontColor, FXTEXT_CLEARTYPE); ++ CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0, ++ (kLeftPosition + 47) * m_outputHScale, ++ (float)(m_Height - iTextHeight + iFontSize)); ++ affine_matrix1.Concat(matrix); ++ device->DrawNormalText(pdfium::make_span(charpos).subspan(7, length), ++ m_pFont, static_cast(iFontSize), ++ affine_matrix1, m_fontColor, GetTextRenderOptions()); + } + tempStr = str.First(1); + length = tempStr.GetLength(); +- strWidth = multiple * 7; +- strWidth = (int32_t)(strWidth * m_outputHScale); ++ strWidth = 7 * static_cast(strWidth * m_outputHScale); + +- CalcTextInfo(tempStr, charpos.data(), m_pFont.Get(), (float)strWidth, +- iFontSize, blank); ++ CalcTextInfo(tempStr, charpos.data(), m_pFont, (float)strWidth, iFontSize, ++ blank); + { + CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0, 0.0, + (float)(m_Height - iTextHeight + iFontSize)); +- if (matrix) +- affine_matrix1.Concat(*matrix); +- device->DrawNormalText(length, charpos.data(), m_pFont.Get(), ++ affine_matrix1.Concat(matrix); ++ device->DrawNormalText(pdfium::make_span(charpos).first(length), m_pFont, + static_cast(iFontSize), affine_matrix1, +- m_fontColor, FXTEXT_CLEARTYPE); ++ m_fontColor, GetTextRenderOptions()); + } + return true; + } +diff --git a/fxbarcode/oned/BC_OnedEAN13Writer.h b/fxbarcode/oned/BC_OnedEAN13Writer.h +index 88fa9cb0b..8c7ff1cd0 100644 +--- a/fxbarcode/oned/BC_OnedEAN13Writer.h ++++ b/fxbarcode/oned/BC_OnedEAN13Writer.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,12 @@ + #ifndef FXBARCODE_ONED_BC_ONEDEAN13WRITER_H_ + #define FXBARCODE_ONED_BC_ONEDEAN13WRITER_H_ + ++#include ++ + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "fxbarcode/BC_Library.h" + #include "fxbarcode/oned/BC_OnedEANWriter.h" + +-class CFX_DIBitmap; + class CFX_RenderDevice; + + class CBC_OnedEAN13Writer final : public CBC_OneDimEANWriter { +@@ -20,12 +21,7 @@ class CBC_OnedEAN13Writer final : public CBC_OneDimEANWriter { + ~CBC_OnedEAN13Writer() override; + + // CBC_OneDimEANWriter: +- uint8_t* EncodeWithHint(const ByteString& contents, +- BCFORMAT format, +- int32_t& outWidth, +- int32_t& outHeight, +- int32_t hints) override; +- uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override; ++ DataVector Encode(const ByteString& contents) override; + bool CheckContentValidity(WideStringView contents) override; + WideString FilterContents(WideStringView contents) override; + int32_t CalcChecksum(const ByteString& contents) override; +@@ -33,9 +29,8 @@ class CBC_OnedEAN13Writer final : public CBC_OneDimEANWriter { + private: + bool ShowChars(WideStringView contents, + CFX_RenderDevice* device, +- const CFX_Matrix* matrix, +- int32_t barWidth, +- int32_t multiple) override; ++ const CFX_Matrix& matrix, ++ int32_t barWidth) override; + + int32_t m_codeWidth; + }; +diff --git a/fxbarcode/oned/BC_OnedEAN13Writer_unittest.cpp b/fxbarcode/oned/BC_OnedEAN13Writer_unittest.cpp +index 55746ba3c..e4119e5eb 100644 +--- a/fxbarcode/oned/BC_OnedEAN13Writer_unittest.cpp ++++ b/fxbarcode/oned/BC_OnedEAN13Writer_unittest.cpp +@@ -1,9 +1,12 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fxbarcode/oned/BC_OnedEAN13Writer.h" + ++#include ++ ++#include "core/fxcrt/data_vector.h" + #include "testing/gtest/include/gtest/gtest.h" + + namespace { +@@ -11,35 +14,15 @@ namespace { + TEST(OnedEAN13WriterTest, Encode) { + CBC_OnedEAN13Writer writer; + writer.InitEANWriter(); +- int32_t width; +- int32_t height; +- uint8_t* encoded; +- const char* expected; + + // EAN-13 barcodes encode 13-digit numbers into 95 modules in a unidimensional + // disposition. +- encoded = writer.Encode("", BCFORMAT_EAN_13, width, height); +- EXPECT_EQ(nullptr, encoded); +- FX_Free(encoded); +- +- encoded = writer.Encode("123", BCFORMAT_EAN_13, width, height); +- EXPECT_EQ(nullptr, encoded); +- FX_Free(encoded); +- +- encoded = writer.Encode("123456789012", BCFORMAT_EAN_13, width, height); +- EXPECT_EQ(nullptr, encoded); +- FX_Free(encoded); ++ EXPECT_TRUE(writer.Encode("").empty()); ++ EXPECT_TRUE(writer.Encode("123").empty()); ++ EXPECT_TRUE(writer.Encode("123456789012").empty()); ++ EXPECT_TRUE(writer.Encode("12345678901234").empty()); + +- encoded = writer.Encode("12345678901234", BCFORMAT_EAN_13, width, height); +- EXPECT_EQ(nullptr, encoded); +- FX_Free(encoded); +- +- encoded = writer.Encode("1234567890128", BCFORMAT_EAN_13, width, height); +- EXPECT_NE(nullptr, encoded); +- EXPECT_EQ(1, height); +- EXPECT_EQ(95, width); +- +- expected = ++ static const char kExpected1[] = + "# #" // Start + // 1 implicit by LLGLGG in next 6 digits + " # ##" // 2 L +@@ -56,17 +39,11 @@ TEST(OnedEAN13WriterTest, Encode) { + "## ## " // 2 R + "# # " // 8 R + "# #"; // End +- for (int i = 0; i < 95; i++) { +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- } +- FX_Free(encoded); +- +- encoded = writer.Encode("7776665554440", BCFORMAT_EAN_13, width, height); +- EXPECT_NE(nullptr, encoded); +- EXPECT_EQ(1, height); +- EXPECT_EQ(95, width); ++ DataVector encoded = writer.Encode("1234567890128"); ++ for (size_t i = 0; i < strlen(kExpected1); i++) ++ EXPECT_EQ(kExpected1[i] != ' ', !!encoded[i]) << i; + +- expected = ++ static const char kExpected2[] = + "# #" // Start + // 7 implicit by LGLGLG in next 6 digits + " ### ##" // 7 L +@@ -83,10 +60,10 @@ TEST(OnedEAN13WriterTest, Encode) { + "# ### " // 4 R + "### # " // 0 R + "# #"; // End +- for (int i = 0; i < 95; i++) { +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- } +- FX_Free(encoded); ++ encoded = writer.Encode("7776665554440"); ++ ASSERT_EQ(strlen(kExpected2), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected2); i++) ++ EXPECT_EQ(kExpected2[i] != ' ', !!encoded[i]) << i; + } + + TEST(OnedEAN13WriterTest, Checksum) { +diff --git a/fxbarcode/oned/BC_OnedEAN8Writer.cpp b/fxbarcode/oned/BC_OnedEAN8Writer.cpp +index 62c760c3f..6317c9baf 100644 +--- a/fxbarcode/oned/BC_OnedEAN8Writer.cpp ++++ b/fxbarcode/oned/BC_OnedEAN8Writer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,8 +22,9 @@ + + #include "fxbarcode/oned/BC_OnedEAN8Writer.h" + ++#include ++ + #include +-#include + #include + #include + +@@ -38,9 +39,9 @@ + + namespace { + +-const int8_t kOnedEAN8StartPattern[3] = {1, 1, 1}; +-const int8_t kOnedEAN8MiddlePattern[5] = {1, 1, 1, 1, 1}; +-const int8_t kOnedEAN8LPattern[10][4] = { ++const uint8_t kOnedEAN8StartPattern[3] = {1, 1, 1}; ++const uint8_t kOnedEAN8MiddlePattern[5] = {1, 1, 1, 1, 1}; ++const uint8_t kOnedEAN8LPattern[10][4] = { + {3, 2, 1, 1}, {2, 2, 2, 1}, {2, 1, 2, 2}, {1, 4, 1, 1}, {1, 1, 3, 2}, + {1, 2, 3, 1}, {1, 1, 1, 4}, {1, 3, 1, 2}, {1, 2, 1, 3}, {3, 1, 1, 2}}; + +@@ -56,16 +57,14 @@ void CBC_OnedEAN8Writer::SetDataLength(int32_t length) { + m_iDataLenth = 8; + } + +-bool CBC_OnedEAN8Writer::SetTextLocation(BC_TEXT_LOC location) { +- if (location == BC_TEXT_LOC_BELOWEMBED) { ++void CBC_OnedEAN8Writer::SetTextLocation(BC_TEXT_LOC location) { ++ if (location == BC_TEXT_LOC::kBelowEmbed) + m_locTextLoc = location; +- return true; +- } +- return false; + } + + bool CBC_OnedEAN8Writer::CheckContentValidity(WideStringView contents) { +- return std::all_of(contents.begin(), contents.end(), ++ return HasValidContentSize(contents) && ++ std::all_of(contents.begin(), contents.end(), + [](wchar_t c) { return FXSYS_IsDecimalDigit(c); }); + } + +@@ -89,103 +88,84 @@ int32_t CBC_OnedEAN8Writer::CalcChecksum(const ByteString& contents) { + return EANCalcChecksum(contents); + } + +-uint8_t* CBC_OnedEAN8Writer::EncodeWithHint(const ByteString& contents, +- BCFORMAT format, +- int32_t& outWidth, +- int32_t& outHeight, +- int32_t hints) { +- if (format != BCFORMAT_EAN_8) +- return nullptr; +- return CBC_OneDimWriter::EncodeWithHint(contents, format, outWidth, outHeight, +- hints); +-} +- +-uint8_t* CBC_OnedEAN8Writer::EncodeImpl(const ByteString& contents, +- int32_t& outLength) { ++DataVector CBC_OnedEAN8Writer::Encode(const ByteString& contents) { + if (contents.GetLength() != 8) +- return nullptr; ++ return {}; + +- outLength = m_codeWidth; +- std::unique_ptr result( +- FX_Alloc(uint8_t, m_codeWidth)); +- int32_t pos = 0; +- pos += AppendPattern(result.get(), pos, kOnedEAN8StartPattern, 3, true); ++ DataVector result(m_codeWidth); ++ auto result_span = pdfium::make_span(result); ++ result_span = AppendPattern(result_span, kOnedEAN8StartPattern, true); + +- int32_t i = 0; +- for (i = 0; i <= 3; i++) { ++ for (int i = 0; i <= 3; i++) { + int32_t digit = FXSYS_DecimalCharToInt(contents[i]); +- pos += AppendPattern(result.get(), pos, kOnedEAN8LPattern[digit], 4, false); ++ result_span = AppendPattern(result_span, kOnedEAN8LPattern[digit], false); + } +- pos += AppendPattern(result.get(), pos, kOnedEAN8MiddlePattern, 5, false); ++ result_span = AppendPattern(result_span, kOnedEAN8MiddlePattern, false); + +- for (i = 4; i <= 7; i++) { ++ for (int i = 4; i <= 7; i++) { + int32_t digit = FXSYS_DecimalCharToInt(contents[i]); +- pos += AppendPattern(result.get(), pos, kOnedEAN8LPattern[digit], 4, true); ++ result_span = AppendPattern(result_span, kOnedEAN8LPattern[digit], true); + } +- pos += AppendPattern(result.get(), pos, kOnedEAN8StartPattern, 3, true); +- return result.release(); ++ AppendPattern(result_span, kOnedEAN8StartPattern, true); ++ return result; + } + + bool CBC_OnedEAN8Writer::ShowChars(WideStringView contents, + CFX_RenderDevice* device, +- const CFX_Matrix* matrix, +- int32_t barWidth, +- int32_t multiple) { ++ const CFX_Matrix& matrix, ++ int32_t barWidth) { + if (!device) + return false; + +- int32_t leftPosition = 3 * multiple; ++ constexpr float kLeftPosition = 3.0f; + ByteString str = FX_UTF8Encode(contents); + size_t iLength = str.GetLength(); + std::vector charpos(iLength); + ByteString tempStr = str.First(4); + size_t iLen = tempStr.GetLength(); +- int32_t strWidth = 7 * multiple * 4; +- float blank = 0.0; ++ constexpr int32_t kWidth = 28; ++ float blank = 0.0f; + +- int32_t iFontSize = (int32_t)fabs(m_fFontSize); ++ int32_t iFontSize = static_cast(fabs(m_fFontSize)); + int32_t iTextHeight = iFontSize + 1; + + CFX_Matrix matr(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0); +- CFX_FloatRect rect((float)leftPosition, (float)(m_Height - iTextHeight), +- (float)(leftPosition + strWidth - 0.5), (float)m_Height); +- matr.Concat(*matrix); ++ CFX_FloatRect rect(kLeftPosition, (float)(m_Height - iTextHeight), ++ kLeftPosition + kWidth - 0.5, (float)m_Height); ++ matr.Concat(matrix); + FX_RECT re = matr.TransformRect(rect).GetOuterRect(); + device->FillRect(re, kBackgroundColor); + CFX_Matrix matr1(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0); +- CFX_FloatRect rect1( +- (float)(leftPosition + 33 * multiple), (float)(m_Height - iTextHeight), +- (float)(leftPosition + 33 * multiple + strWidth - 0.5), (float)m_Height); +- matr1.Concat(*matrix); ++ CFX_FloatRect rect1(kLeftPosition + 33, (float)(m_Height - iTextHeight), ++ kLeftPosition + 33 + kWidth - 0.5, (float)m_Height); ++ matr1.Concat(matrix); + re = matr1.TransformRect(rect1).GetOuterRect(); + device->FillRect(re, kBackgroundColor); +- strWidth = (int32_t)(strWidth * m_outputHScale); ++ int32_t strWidth = static_cast(kWidth * m_outputHScale); + +- CalcTextInfo(tempStr, charpos.data(), m_pFont.Get(), (float)strWidth, +- iFontSize, blank); ++ CalcTextInfo(tempStr, charpos.data(), m_pFont, (float)strWidth, iFontSize, ++ blank); + { + CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0, +- (float)leftPosition * m_outputHScale, ++ kLeftPosition * m_outputHScale, + (float)(m_Height - iTextHeight + iFontSize)); +- affine_matrix1.Concat(*matrix); +- device->DrawNormalText(iLen, charpos.data(), m_pFont.Get(), ++ affine_matrix1.Concat(matrix); ++ device->DrawNormalText(pdfium::make_span(charpos).first(iLen), m_pFont, + static_cast(iFontSize), affine_matrix1, +- m_fontColor, FXTEXT_CLEARTYPE); ++ m_fontColor, GetTextRenderOptions()); + } + tempStr = str.Substr(4, 4); + iLen = tempStr.GetLength(); +- CalcTextInfo(tempStr, &charpos[4], m_pFont.Get(), (float)strWidth, iFontSize, ++ CalcTextInfo(tempStr, &charpos[4], m_pFont, (float)strWidth, iFontSize, + blank); + { +- CFX_Matrix affine_matrix1( +- 1.0, 0.0, 0.0, -1.0, +- (float)(leftPosition + 33 * multiple) * m_outputHScale, +- (float)(m_Height - iTextHeight + iFontSize)); +- if (matrix) +- affine_matrix1.Concat(*matrix); +- device->DrawNormalText(iLen, &charpos[4], m_pFont.Get(), ++ CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0, ++ (kLeftPosition + 33) * m_outputHScale, ++ (float)(m_Height - iTextHeight + iFontSize)); ++ affine_matrix1.Concat(matrix); ++ device->DrawNormalText(pdfium::make_span(charpos).subspan(4, iLen), m_pFont, + static_cast(iFontSize), affine_matrix1, +- m_fontColor, FXTEXT_CLEARTYPE); ++ m_fontColor, GetTextRenderOptions()); + } + return true; + } +diff --git a/fxbarcode/oned/BC_OnedEAN8Writer.h b/fxbarcode/oned/BC_OnedEAN8Writer.h +index b71dbb964..3f633e64e 100644 +--- a/fxbarcode/oned/BC_OnedEAN8Writer.h ++++ b/fxbarcode/oned/BC_OnedEAN8Writer.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,12 +7,13 @@ + #ifndef FXBARCODE_ONED_BC_ONEDEAN8WRITER_H_ + #define FXBARCODE_ONED_BC_ONEDEAN8WRITER_H_ + ++#include ++ + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" + #include "fxbarcode/BC_Library.h" + #include "fxbarcode/oned/BC_OnedEANWriter.h" + +-class CFX_DIBitmap; ++class CFX_Matrix; + class CFX_RenderDevice; + + class CBC_OnedEAN8Writer final : public CBC_OneDimEANWriter { +@@ -21,24 +22,18 @@ class CBC_OnedEAN8Writer final : public CBC_OneDimEANWriter { + ~CBC_OnedEAN8Writer() override; + + // CBC_OneDimEANWriter: +- uint8_t* EncodeWithHint(const ByteString& contents, +- BCFORMAT format, +- int32_t& outWidth, +- int32_t& outHeight, +- int32_t hints) override; +- uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override; ++ DataVector Encode(const ByteString& contents) override; + bool CheckContentValidity(WideStringView contents) override; + WideString FilterContents(WideStringView contents) override; + void SetDataLength(int32_t length) override; +- bool SetTextLocation(BC_TEXT_LOC location) override; ++ void SetTextLocation(BC_TEXT_LOC location) override; + int32_t CalcChecksum(const ByteString& contents) override; + + private: + bool ShowChars(WideStringView contents, + CFX_RenderDevice* device, +- const CFX_Matrix* matrix, +- int32_t barWidth, +- int32_t multiple) override; ++ const CFX_Matrix& matrix, ++ int32_t barWidth) override; + + static constexpr int32_t kDefaultCodeWidth = 3 + (7 * 4) + 5 + (7 * 4) + 3; + int32_t m_codeWidth = kDefaultCodeWidth; +diff --git a/fxbarcode/oned/BC_OnedEAN8Writer_unittest.cpp b/fxbarcode/oned/BC_OnedEAN8Writer_unittest.cpp +index ae1b8237a..36d961d22 100644 +--- a/fxbarcode/oned/BC_OnedEAN8Writer_unittest.cpp ++++ b/fxbarcode/oned/BC_OnedEAN8Writer_unittest.cpp +@@ -1,9 +1,12 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fxbarcode/oned/BC_OnedEAN8Writer.h" + ++#include ++ ++#include "core/fxcrt/data_vector.h" + #include "testing/gtest/include/gtest/gtest.h" + + namespace { +@@ -11,35 +14,15 @@ namespace { + TEST(OnedEAN8WriterTest, Encode) { + CBC_OnedEAN8Writer writer; + writer.InitEANWriter(); +- int32_t width; +- int32_t height; +- uint8_t* encoded; +- const char* expected; + + // EAN-8 barcodes encode 8-digit numbers into 67 modules in a unidimensional + // disposition. +- encoded = writer.Encode("", BCFORMAT_EAN_8, width, height); +- EXPECT_EQ(nullptr, encoded); +- FX_Free(encoded); +- +- encoded = writer.Encode("123", BCFORMAT_EAN_8, width, height); +- EXPECT_EQ(nullptr, encoded); +- FX_Free(encoded); +- +- encoded = writer.Encode("1234567", BCFORMAT_EAN_8, width, height); +- EXPECT_EQ(nullptr, encoded); +- FX_Free(encoded); ++ EXPECT_TRUE(writer.Encode("").empty()); ++ EXPECT_TRUE(writer.Encode("123").empty()); ++ EXPECT_TRUE(writer.Encode("1234567").empty()); ++ EXPECT_TRUE(writer.Encode("123456789").empty()); + +- encoded = writer.Encode("123456789", BCFORMAT_EAN_8, width, height); +- EXPECT_EQ(nullptr, encoded); +- FX_Free(encoded); +- +- encoded = writer.Encode("12345670", BCFORMAT_EAN_8, width, height); +- EXPECT_NE(nullptr, encoded); +- EXPECT_EQ(1, height); +- EXPECT_EQ(67, width); +- +- expected = ++ static const char kExpected1[] = + "# #" // Start + " ## #" // 1 L + " # ##" // 2 L +@@ -51,17 +34,12 @@ TEST(OnedEAN8WriterTest, Encode) { + "# # " // 7 R + "### # " // 0 R + "# #"; // End +- for (int i = 0; i < 67; i++) { +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- } +- FX_Free(encoded); +- +- encoded = writer.Encode("99441104", BCFORMAT_EAN_8, width, height); +- EXPECT_NE(nullptr, encoded); +- EXPECT_EQ(1, height); +- EXPECT_EQ(67, width); ++ DataVector encoded = writer.Encode("12345670"); ++ ASSERT_EQ(strlen(kExpected1), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected1); i++) ++ EXPECT_EQ(kExpected1[i] != ' ', !!encoded[i]) << i; + +- expected = ++ static const char kExpected2[] = + "# #" // Start + " # ##" // 9 L + " # ##" // 9 L +@@ -73,10 +51,10 @@ TEST(OnedEAN8WriterTest, Encode) { + "### # " // 0 R + "# ### " // 4 R + "# #"; // End +- for (int i = 0; i < 67; i++) { +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- } +- FX_Free(encoded); ++ encoded = writer.Encode("99441104"); ++ ASSERT_EQ(strlen(kExpected2), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected2); i++) ++ EXPECT_EQ(kExpected2[i] != ' ', !!encoded[i]) << i; + } + + TEST(OnedEAN8WriterTest, Checksum) { +diff --git a/fxbarcode/oned/BC_OnedEANChecksum.cpp b/fxbarcode/oned/BC_OnedEANChecksum.cpp +index b290c6292..c1fbab495 100644 +--- a/fxbarcode/oned/BC_OnedEANChecksum.cpp ++++ b/fxbarcode/oned/BC_OnedEANChecksum.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/oned/BC_OnedEANChecksum.h b/fxbarcode/oned/BC_OnedEANChecksum.h +index 5dd24bc26..60b801c06 100644 +--- a/fxbarcode/oned/BC_OnedEANChecksum.h ++++ b/fxbarcode/oned/BC_OnedEANChecksum.h +@@ -1,11 +1,11 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #ifndef FXBARCODE_ONED_BC_ONEDEANCHECKSUM_H_ + #define FXBARCODE_ONED_BC_ONEDEANCHECKSUM_H_ + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/bytestring.h" + + int32_t EANCalcChecksum(const ByteString& contents); + +diff --git a/fxbarcode/oned/BC_OnedEANWriter.cpp b/fxbarcode/oned/BC_OnedEANWriter.cpp +index f43b9adfb..34bcf9e9e 100644 +--- a/fxbarcode/oned/BC_OnedEANWriter.cpp ++++ b/fxbarcode/oned/BC_OnedEANWriter.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/oned/BC_OnedEANWriter.h b/fxbarcode/oned/BC_OnedEANWriter.h +index 5d8016705..a871edf47 100644 +--- a/fxbarcode/oned/BC_OnedEANWriter.h ++++ b/fxbarcode/oned/BC_OnedEANWriter.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/oned/BC_OnedUPCAWriter.cpp b/fxbarcode/oned/BC_OnedUPCAWriter.cpp +index 666b03739..55a6a0233 100644 +--- a/fxbarcode/oned/BC_OnedUPCAWriter.cpp ++++ b/fxbarcode/oned/BC_OnedUPCAWriter.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,6 +22,9 @@ + + #include "fxbarcode/oned/BC_OnedUPCAWriter.h" + ++#include ++ ++#include + #include + + #include "core/fxcrt/fx_extension.h" +@@ -30,21 +33,18 @@ + #include "fxbarcode/BC_Writer.h" + #include "fxbarcode/oned/BC_OneDimWriter.h" + #include "fxbarcode/oned/BC_OnedEAN13Writer.h" +-#include "third_party/base/ptr_util.h" + + CBC_OnedUPCAWriter::CBC_OnedUPCAWriter() { + m_bLeftPadding = true; + m_bRightPadding = true; + } + +-CBC_OnedUPCAWriter::~CBC_OnedUPCAWriter() {} ++CBC_OnedUPCAWriter::~CBC_OnedUPCAWriter() = default; + + bool CBC_OnedUPCAWriter::CheckContentValidity(WideStringView contents) { +- for (size_t i = 0; i < contents.GetLength(); ++i) { +- if (contents[i] < '0' || contents[i] > '9') +- return false; +- } +- return true; ++ return HasValidContentSize(contents) && ++ std::all_of(contents.begin(), contents.end(), ++ [](wchar_t c) { return FXSYS_IsDecimalDigit(c); }); + } + + WideString CBC_OnedUPCAWriter::FilterContents(WideStringView contents) { +@@ -64,7 +64,7 @@ WideString CBC_OnedUPCAWriter::FilterContents(WideStringView contents) { + } + + void CBC_OnedUPCAWriter::InitEANWriter() { +- m_subWriter = pdfium::MakeUnique(); ++ m_subWriter = std::make_unique(); + } + + int32_t CBC_OnedUPCAWriter::CalcChecksum(const ByteString& contents) { +@@ -84,132 +84,104 @@ int32_t CBC_OnedUPCAWriter::CalcChecksum(const ByteString& contents) { + return checksum; + } + +-uint8_t* CBC_OnedUPCAWriter::EncodeWithHint(const ByteString& contents, +- BCFORMAT format, +- int32_t& outWidth, +- int32_t& outHeight, +- int32_t hints) { +- if (format != BCFORMAT_UPC_A) +- return nullptr; +- ++DataVector CBC_OnedUPCAWriter::Encode(const ByteString& contents) { + ByteString toEAN13String = '0' + contents; + m_iDataLenth = 13; +- return m_subWriter->EncodeWithHint(toEAN13String, BCFORMAT_EAN_13, outWidth, +- outHeight, hints); +-} +- +-uint8_t* CBC_OnedUPCAWriter::EncodeImpl(const ByteString& contents, +- int32_t& outLength) { +- return nullptr; ++ return m_subWriter->Encode(toEAN13String); + } + + bool CBC_OnedUPCAWriter::ShowChars(WideStringView contents, + CFX_RenderDevice* device, +- const CFX_Matrix* matrix, +- int32_t barWidth, +- int32_t multiple) { ++ const CFX_Matrix& matrix, ++ int32_t barWidth) { + if (!device) + return false; + +- int32_t leftPadding = 7 * multiple; +- int32_t leftPosition = 10 * multiple + leftPadding; ++ constexpr float kLeftPosition = 17.0f; + ByteString str = FX_UTF8Encode(contents); + size_t length = str.GetLength(); + std::vector charpos(length); + ByteString tempStr = str.Substr(1, 5); +- float strWidth = (float)35 * multiple; +- float blank = 0.0; ++ constexpr float kWidth = 35.0f; ++ float blank = 0.0f; + + length = tempStr.GetLength(); +- int32_t iFontSize = (int32_t)fabs(m_fFontSize); ++ int32_t iFontSize = static_cast(fabs(m_fFontSize)); + int32_t iTextHeight = iFontSize + 1; + + CFX_Matrix matr(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0); +- CFX_FloatRect rect((float)leftPosition, (float)(m_Height - iTextHeight), +- (float)(leftPosition + strWidth - 0.5), (float)m_Height); +- matr.Concat(*matrix); ++ CFX_FloatRect rect(kLeftPosition, (float)(m_Height - iTextHeight), ++ kLeftPosition + kWidth - 0.5, (float)m_Height); ++ matr.Concat(matrix); + FX_RECT re = matr.TransformRect(rect).GetOuterRect(); + device->FillRect(re, kBackgroundColor); + CFX_Matrix matr1(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0); +- CFX_FloatRect rect1((float)(leftPosition + 40 * multiple), +- (float)(m_Height - iTextHeight), +- (float)((leftPosition + 40 * multiple) + strWidth - 0.5), +- (float)m_Height); +- matr1.Concat(*matrix); ++ CFX_FloatRect rect1(kLeftPosition + 40, (float)(m_Height - iTextHeight), ++ kLeftPosition + 40 + kWidth - 0.5, (float)m_Height); ++ matr1.Concat(matrix); + re = matr1.TransformRect(rect1).GetOuterRect(); + device->FillRect(re, kBackgroundColor); +- float strWidth1 = (float)multiple * 7; ++ constexpr float kWidth1 = 7.0f; + CFX_Matrix matr2(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0); +- CFX_FloatRect rect2(0.0, (float)(m_Height - iTextHeight), +- (float)strWidth1 - 1, (float)m_Height); +- matr2.Concat(*matrix); ++ CFX_FloatRect rect2(0.0, (float)(m_Height - iTextHeight), kWidth1 - 1, ++ (float)m_Height); ++ matr2.Concat(matrix); + re = matr2.TransformRect(rect2).GetOuterRect(); + device->FillRect(re, kBackgroundColor); + CFX_Matrix matr3(m_outputHScale, 0.0, 0.0, 1.0, 0.0, 0.0); +- CFX_FloatRect rect3((float)(leftPosition + 85 * multiple), +- (float)(m_Height - iTextHeight), +- (float)((leftPosition + 85 * multiple) + strWidth1 - 0.5), +- (float)m_Height); +- matr3.Concat(*matrix); ++ CFX_FloatRect rect3(kLeftPosition + 85, (float)(m_Height - iTextHeight), ++ kLeftPosition + 85 + kWidth1 - 0.5, (float)m_Height); ++ matr3.Concat(matrix); + re = matr3.TransformRect(rect3).GetOuterRect(); + device->FillRect(re, kBackgroundColor); +- strWidth = strWidth * m_outputHScale; ++ float strWidth = kWidth * m_outputHScale; + +- CalcTextInfo(tempStr, &charpos[1], m_pFont.Get(), strWidth, iFontSize, blank); ++ CalcTextInfo(tempStr, &charpos[1], m_pFont, strWidth, iFontSize, blank); + { + CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0, +- (float)leftPosition * m_outputHScale, ++ kLeftPosition * m_outputHScale, + (float)(m_Height - iTextHeight + iFontSize)); +- if (matrix) +- affine_matrix1.Concat(*matrix); +- device->DrawNormalText(length, &charpos[1], m_pFont.Get(), +- static_cast(iFontSize), affine_matrix1, +- m_fontColor, FXTEXT_CLEARTYPE); ++ affine_matrix1.Concat(matrix); ++ device->DrawNormalText(pdfium::make_span(charpos).subspan(1, length), ++ m_pFont, static_cast(iFontSize), ++ affine_matrix1, m_fontColor, GetTextRenderOptions()); + } + tempStr = str.Substr(6, 5); + length = tempStr.GetLength(); +- CalcTextInfo(tempStr, &charpos[6], m_pFont.Get(), strWidth, iFontSize, blank); ++ CalcTextInfo(tempStr, &charpos[6], m_pFont, strWidth, iFontSize, blank); + { +- CFX_Matrix affine_matrix1( +- 1.0, 0.0, 0.0, -1.0, +- (float)(leftPosition + 40 * multiple) * m_outputHScale, +- (float)(m_Height - iTextHeight + iFontSize)); +- if (matrix) +- affine_matrix1.Concat(*matrix); +- device->DrawNormalText(length, &charpos[6], m_pFont.Get(), +- static_cast(iFontSize), affine_matrix1, +- m_fontColor, FXTEXT_CLEARTYPE); ++ CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0, ++ (kLeftPosition + 40) * m_outputHScale, ++ (float)(m_Height - iTextHeight + iFontSize)); ++ affine_matrix1.Concat(matrix); ++ device->DrawNormalText(pdfium::make_span(charpos).subspan(6, length), ++ m_pFont, static_cast(iFontSize), ++ affine_matrix1, m_fontColor, GetTextRenderOptions()); + } + tempStr = str.First(1); + length = tempStr.GetLength(); +- strWidth = (float)multiple * 7; +- strWidth = strWidth * m_outputHScale; ++ strWidth = 7 * m_outputHScale; + +- CalcTextInfo(tempStr, charpos.data(), m_pFont.Get(), strWidth, iFontSize, +- blank); ++ CalcTextInfo(tempStr, charpos.data(), m_pFont, strWidth, iFontSize, blank); + { + CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0, 0, + (float)(m_Height - iTextHeight + iFontSize)); +- if (matrix) +- affine_matrix1.Concat(*matrix); +- device->DrawNormalText(length, charpos.data(), m_pFont.Get(), ++ affine_matrix1.Concat(matrix); ++ device->DrawNormalText(pdfium::make_span(charpos).first(length), m_pFont, + static_cast(iFontSize), affine_matrix1, +- m_fontColor, FXTEXT_CLEARTYPE); ++ m_fontColor, GetTextRenderOptions()); + } + tempStr = str.Substr(11, 1); + length = tempStr.GetLength(); +- CalcTextInfo(tempStr, &charpos[11], m_pFont.Get(), strWidth, iFontSize, +- blank); ++ CalcTextInfo(tempStr, &charpos[11], m_pFont, strWidth, iFontSize, blank); + { +- CFX_Matrix affine_matrix1( +- 1.0, 0.0, 0.0, -1.0, +- (float)(leftPosition + 85 * multiple) * m_outputHScale, +- (float)(m_Height - iTextHeight + iFontSize)); +- if (matrix) +- affine_matrix1.Concat(*matrix); +- device->DrawNormalText(length, &charpos[11], m_pFont.Get(), +- static_cast(iFontSize), affine_matrix1, +- m_fontColor, FXTEXT_CLEARTYPE); ++ CFX_Matrix affine_matrix1(1.0, 0.0, 0.0, -1.0, ++ (kLeftPosition + 85) * m_outputHScale, ++ (float)(m_Height - iTextHeight + iFontSize)); ++ affine_matrix1.Concat(matrix); ++ device->DrawNormalText(pdfium::make_span(charpos).subspan(11, length), ++ m_pFont, static_cast(iFontSize), ++ affine_matrix1, m_fontColor, GetTextRenderOptions()); + } + return true; + } +diff --git a/fxbarcode/oned/BC_OnedUPCAWriter.h b/fxbarcode/oned/BC_OnedUPCAWriter.h +index 991dfe694..7d8beff90 100644 +--- a/fxbarcode/oned/BC_OnedUPCAWriter.h ++++ b/fxbarcode/oned/BC_OnedUPCAWriter.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,11 +10,10 @@ + #include + + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "fxbarcode/BC_Library.h" + #include "fxbarcode/oned/BC_OnedEANWriter.h" + + class CBC_OnedEAN13Writer; +-class CFX_DIBitmap; + class CFX_Matrix; + class CFX_RenderDevice; + +@@ -24,12 +23,7 @@ class CBC_OnedUPCAWriter final : public CBC_OneDimEANWriter { + ~CBC_OnedUPCAWriter() override; + + // CBC_OneDimEANWriter: +- uint8_t* EncodeWithHint(const ByteString& contents, +- BCFORMAT format, +- int32_t& outWidth, +- int32_t& outHeight, +- int32_t hints) override; +- uint8_t* EncodeImpl(const ByteString& contents, int32_t& outLength) override; ++ DataVector Encode(const ByteString& contents) override; + bool CheckContentValidity(WideStringView contents) override; + WideString FilterContents(WideStringView contents) override; + void InitEANWriter() override; +@@ -38,9 +32,8 @@ class CBC_OnedUPCAWriter final : public CBC_OneDimEANWriter { + private: + bool ShowChars(WideStringView contents, + CFX_RenderDevice* device, +- const CFX_Matrix* matrix, +- int32_t barWidth, +- int32_t multiple) override; ++ const CFX_Matrix& matrix, ++ int32_t barWidth) override; + + std::unique_ptr m_subWriter; + }; +diff --git a/fxbarcode/oned/BC_OnedUPCAWriter_unittest.cpp b/fxbarcode/oned/BC_OnedUPCAWriter_unittest.cpp +index c5acd23f7..71d10e128 100644 +--- a/fxbarcode/oned/BC_OnedUPCAWriter_unittest.cpp ++++ b/fxbarcode/oned/BC_OnedUPCAWriter_unittest.cpp +@@ -1,9 +1,12 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fxbarcode/oned/BC_OnedUPCAWriter.h" + ++#include ++ ++#include "core/fxcrt/data_vector.h" + #include "testing/gtest/include/gtest/gtest.h" + + namespace { +@@ -11,29 +14,15 @@ namespace { + TEST(OnedUPCAWriterTest, Encode) { + CBC_OnedUPCAWriter writer; + writer.InitEANWriter(); +- int32_t width; +- int32_t height; + + // UPCA barcodes encode 12-digit numbers into 95 modules in a unidimensional + // disposition. +- uint8_t* encoded = writer.Encode("", BCFORMAT_UPC_A, width, height); +- EXPECT_EQ(nullptr, encoded); +- FX_Free(encoded); +- +- encoded = writer.Encode("123", BCFORMAT_UPC_A, width, height); +- EXPECT_EQ(nullptr, encoded); +- FX_Free(encoded); +- +- encoded = writer.Encode("12345678901", BCFORMAT_UPC_A, width, height); +- EXPECT_EQ(nullptr, encoded); +- FX_Free(encoded); +- +- encoded = writer.Encode("1234567890123", BCFORMAT_UPC_A, width, height); +- EXPECT_EQ(nullptr, encoded); +- FX_Free(encoded); ++ EXPECT_TRUE(writer.Encode("").empty()); ++ EXPECT_TRUE(writer.Encode("123").empty()); ++ EXPECT_TRUE(writer.Encode("12345678901").empty()); ++ EXPECT_TRUE(writer.Encode("1234567890123").empty()); + +- encoded = writer.Encode("123456789012", BCFORMAT_UPC_A, width, height); +- const char* expected = ++ static const char kExpected1[] = + "# #" // Start + " ## #" // 1 L + " # ##" // 2 L +@@ -49,16 +38,13 @@ TEST(OnedUPCAWriterTest, Encode) { + "## ## " // 1 R + "## ## " // 2 R + "# #"; // End +- EXPECT_NE(nullptr, encoded); +- EXPECT_EQ(1, height); +- EXPECT_EQ(static_cast(strlen(expected)), width); +- for (size_t i = 0; i < strlen(expected); i++) { +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- } +- FX_Free(encoded); ++ DataVector encoded = writer.Encode("123456789012"); ++ ASSERT_EQ(strlen(kExpected1), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected1); i++) ++ EXPECT_EQ(kExpected1[i] != ' ', !!encoded[i]) << i; + +- encoded = writer.Encode("777666555440", BCFORMAT_UPC_A, width, height); +- expected = ++ encoded = writer.Encode("777666555440"); ++ static const char kExpected2[] = + "# #" // Start + " ### ##" // 7 L + " ### ##" // 7 L +@@ -74,13 +60,9 @@ TEST(OnedUPCAWriterTest, Encode) { + "# ### " // 4 R + "### # " // 0 R + "# #"; // End +- EXPECT_NE(nullptr, encoded); +- EXPECT_EQ(1, height); +- EXPECT_EQ(static_cast(strlen(expected)), width); +- for (size_t i = 0; i < strlen(expected); i++) { +- EXPECT_EQ(expected[i] != ' ', !!encoded[i]) << i; +- } +- FX_Free(encoded); ++ ASSERT_EQ(strlen(kExpected2), encoded.size()); ++ for (size_t i = 0; i < strlen(kExpected2); i++) ++ EXPECT_EQ(kExpected2[i] != ' ', !!encoded[i]) << i; + } + + TEST(OnedUPCAWriterTest, Checksum) { +diff --git a/fxbarcode/pdf417/BC_PDF417.cpp b/fxbarcode/pdf417/BC_PDF417.cpp +index b9ba3902e..d48b98ae7 100644 +--- a/fxbarcode/pdf417/BC_PDF417.cpp ++++ b/fxbarcode/pdf417/BC_PDF417.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,15 +22,16 @@ + + #include "fxbarcode/pdf417/BC_PDF417.h" + ++#include ++ + #include "fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h" + #include "fxbarcode/pdf417/BC_PDF417BarcodeRow.h" + #include "fxbarcode/pdf417/BC_PDF417ErrorCorrection.h" + #include "fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h" +-#include "third_party/base/ptr_util.h" + + namespace { + +-const uint16_t g_CodewordTable[3][929] = { ++const uint16_t kCodewordTable[3][929] = { + {0xd5c0, 0xeaf0, 0xf57c, 0xd4e0, 0xea78, 0xf53e, 0xa8c0, 0xd470, 0xa860, + 0x5040, 0xa830, 0x5020, 0xadc0, 0xd6f0, 0xeb7c, 0xace0, 0xd678, 0xeb3e, + 0x58c0, 0xac70, 0x5860, 0x5dc0, 0xaef0, 0xd77c, 0x5ce0, 0xae78, 0xd73e, +@@ -345,7 +346,7 @@ const uint16_t g_CodewordTable[3][929] = { + 0x0fb2, 0xc7ea}}; + + int32_t Get17BitCodeword(int i, int j) { +- return (0x10000 | g_CodewordTable[i][j]); ++ return (0x10000 | kCodewordTable[i][j]); + } + + } // namespace +@@ -365,7 +366,7 @@ bool CBC_PDF417::GenerateBarcodeLogic(WideStringView msg, + if (errorCorrectionCodeWords < 0) + return false; + +- Optional high_level = ++ absl::optional high_level = + CBC_PDF417HighLevelEncoder::EncodeHighLevel(msg); + if (!high_level.has_value()) + return false; +@@ -390,13 +391,14 @@ bool CBC_PDF417::GenerateBarcodeLogic(WideStringView msg, + sb += (wchar_t)900; + + WideString dataCodewords(sb); +- Optional ec = CBC_PDF417ErrorCorrection::GenerateErrorCorrection( +- dataCodewords, errorCorrectionLevel); ++ absl::optional ec = ++ CBC_PDF417ErrorCorrection::GenerateErrorCorrection(dataCodewords, ++ errorCorrectionLevel); + if (!ec.has_value()) + return false; + + WideString fullCodewords = dataCodewords + ec.value(); +- m_barcodeMatrix = pdfium::MakeUnique(cols, rows); ++ m_barcodeMatrix = std::make_unique(cols, rows); + encodeLowLevel(fullCodewords, cols, rows, errorCorrectionLevel, + m_barcodeMatrix.get()); + return true; +@@ -433,19 +435,19 @@ void CBC_PDF417::encodeChar(int32_t pattern, + CBC_BarcodeRow* logic) { + int32_t map = 1 << (len - 1); + bool last = ((pattern & map) != 0); +- int32_t width = 0; ++ size_t width = 0; + for (int32_t i = 0; i < len; i++) { + bool black = ((pattern & map) != 0); + if (last == black) { + width++; + } else { +- logic->addBar(last, width); ++ logic->AddBar(last, width); + last = black; + width = 1; + } + map >>= 1; + } +- logic->addBar(last, width); ++ logic->AddBar(last, width); + } + + void CBC_PDF417::encodeLowLevel(WideString fullCodewords, +diff --git a/fxbarcode/pdf417/BC_PDF417.h b/fxbarcode/pdf417/BC_PDF417.h +index b445ede5f..0312deb3f 100644 +--- a/fxbarcode/pdf417/BC_PDF417.h ++++ b/fxbarcode/pdf417/BC_PDF417.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,7 +10,7 @@ + #include + #include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" + + class CBC_BarcodeRow; + class CBC_BarcodeMatrix; +diff --git a/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.cpp b/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.cpp +index 70e7cd959..b6f407d41 100644 +--- a/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.cpp ++++ b/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -21,24 +21,26 @@ + */ + + #include "fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h" ++ ++#include ++ ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/span_util.h" + #include "fxbarcode/pdf417/BC_PDF417BarcodeRow.h" +-#include "third_party/base/ptr_util.h" + + CBC_BarcodeMatrix::CBC_BarcodeMatrix(size_t width, size_t height) + : m_width((width + 4) * 17 + 1), m_height(height) { + m_matrix.resize(m_height); + for (size_t i = 0; i < m_height; ++i) +- m_matrix[i] = pdfium::MakeUnique(m_width); ++ m_matrix[i] = std::make_unique(m_width); + } + +-CBC_BarcodeMatrix::~CBC_BarcodeMatrix() {} ++CBC_BarcodeMatrix::~CBC_BarcodeMatrix() = default; + +-std::vector CBC_BarcodeMatrix::toBitArray() { +- std::vector bitArray(m_width * m_height); +- for (size_t i = 0; i < m_height; ++i) { +- std::vector& bytearray = m_matrix[i]->getRow(); +- for (size_t j = 0; j < m_width; ++j) +- bitArray[i * m_width + j] = bytearray[j]; +- } +- return bitArray; ++DataVector CBC_BarcodeMatrix::toBitArray() { ++ DataVector bit_array(m_width * m_height); ++ pdfium::span bit_array_span(bit_array); ++ for (size_t i = 0; i < m_height; ++i) ++ fxcrt::spancpy(bit_array_span.subspan(i * m_width), m_matrix[i]->GetRow()); ++ return bit_array; + } +diff --git a/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h b/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h +index d059cee25..2ed8834f6 100644 +--- a/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h ++++ b/fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,9 +7,13 @@ + #ifndef FXBARCODE_PDF417_BC_PDF417BARCODEMATRIX_H_ + #define FXBARCODE_PDF417_BC_PDF417BARCODEMATRIX_H_ + ++#include ++ + #include + #include + ++#include "core/fxcrt/data_vector.h" ++ + class CBC_BarcodeRow; + + class CBC_BarcodeMatrix final { +@@ -20,7 +24,7 @@ class CBC_BarcodeMatrix final { + CBC_BarcodeRow* getRow(size_t row) const { return m_matrix[row].get(); } + size_t getWidth() const { return m_width; } + size_t getHeight() const { return m_height; } +- std::vector toBitArray(); ++ DataVector toBitArray(); + + private: + std::vector> m_matrix; +diff --git a/fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp b/fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp +index 65c27de7e..48e4128af 100644 +--- a/fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp ++++ b/fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,18 +22,16 @@ + + #include "fxbarcode/pdf417/BC_PDF417BarcodeRow.h" + +-#include ++#include "core/fxcrt/span_util.h" ++#include "third_party/base/check_op.h" + +-CBC_BarcodeRow::CBC_BarcodeRow(size_t width) +- : m_row(width), m_currentLocation(0) {} ++CBC_BarcodeRow::CBC_BarcodeRow(size_t width) : row_(width) {} + +-CBC_BarcodeRow::~CBC_BarcodeRow() {} ++CBC_BarcodeRow::~CBC_BarcodeRow() = default; + +-void CBC_BarcodeRow::addBar(bool black, int32_t width) { +- std::fill_n(m_row.begin() + m_currentLocation, width, black ? 1 : 0); +- m_currentLocation += width; +-} +- +-std::vector& CBC_BarcodeRow::getRow() { +- return m_row; ++void CBC_BarcodeRow::AddBar(bool black, size_t width) { ++ pdfium::span available = row_.writable_span().subspan(offset_); ++ CHECK_LE(width, available.size()); ++ fxcrt::spanset(available.first(width), black ? 1 : 0); ++ offset_ += width; + } +diff --git a/fxbarcode/pdf417/BC_PDF417BarcodeRow.h b/fxbarcode/pdf417/BC_PDF417BarcodeRow.h +index 07ebbd770..160275eea 100644 +--- a/fxbarcode/pdf417/BC_PDF417BarcodeRow.h ++++ b/fxbarcode/pdf417/BC_PDF417BarcodeRow.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,19 +9,20 @@ + + #include + +-#include ++#include "core/fxcrt/fixed_zeroed_data_vector.h" ++#include "third_party/base/span.h" + + class CBC_BarcodeRow final { + public: + explicit CBC_BarcodeRow(size_t width); + ~CBC_BarcodeRow(); + +- void addBar(bool black, int32_t width); +- std::vector& getRow(); ++ void AddBar(bool black, size_t width); ++ pdfium::span GetRow() const { return row_; } + + private: +- std::vector m_row; +- int32_t m_currentLocation; ++ FixedZeroedDataVector row_; ++ size_t offset_ = 0; + }; + + #endif // FXBARCODE_PDF417_BC_PDF417BARCODEROW_H_ +diff --git a/fxbarcode/pdf417/BC_PDF417ErrorCorrection.cpp b/fxbarcode/pdf417/BC_PDF417ErrorCorrection.cpp +index f910d4a13..509f8aa00 100644 +--- a/fxbarcode/pdf417/BC_PDF417ErrorCorrection.cpp ++++ b/fxbarcode/pdf417/BC_PDF417ErrorCorrection.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,9 +22,9 @@ + + #include "fxbarcode/pdf417/BC_PDF417ErrorCorrection.h" + +-#include ++#include + +-#include "core/fxcrt/fx_memory_wrappers.h" ++#include "core/fxcrt/data_vector.h" + + namespace { + +@@ -133,14 +133,14 @@ int32_t CBC_PDF417ErrorCorrection::GetErrorCorrectionCodewordCount( + } + + // static +-Optional CBC_PDF417ErrorCorrection::GenerateErrorCorrection( ++absl::optional CBC_PDF417ErrorCorrection::GenerateErrorCorrection( + const WideString& dataCodewords, + int32_t errorCorrectionLevel) { + int32_t k = GetErrorCorrectionCodewordCount(errorCorrectionLevel); + if (k < 0) +- return {}; ++ return absl::nullopt; + +- std::vector> ech(k); ++ DataVector ech(k); + size_t sld = dataCodewords.GetLength(); + for (size_t i = 0; i < sld; i++) { + int32_t t1 = (dataCodewords[i] + ech[k - 1]) % 929; +diff --git a/fxbarcode/pdf417/BC_PDF417ErrorCorrection.h b/fxbarcode/pdf417/BC_PDF417ErrorCorrection.h +index f5aa89880..ccb2fa061 100644 +--- a/fxbarcode/pdf417/BC_PDF417ErrorCorrection.h ++++ b/fxbarcode/pdf417/BC_PDF417ErrorCorrection.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,8 +9,8 @@ + + #include + +-#include "core/fxcrt/fx_string.h" +-#include "third_party/base/optional.h" ++#include "core/fxcrt/widestring.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + class CBC_PDF417ErrorCorrection { + public: +@@ -18,7 +18,7 @@ class CBC_PDF417ErrorCorrection { + ~CBC_PDF417ErrorCorrection() = delete; + + static int32_t GetErrorCorrectionCodewordCount(int32_t errorCorrectionLevel); +- static Optional GenerateErrorCorrection( ++ static absl::optional GenerateErrorCorrection( + const WideString& dataCodewords, + int32_t errorCorrectionLevel); + }; +diff --git a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.cpp b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.cpp +index 09cac1b37..7affbc814 100644 +--- a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.cpp ++++ b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -23,6 +23,7 @@ + #include "fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h" + + #include "core/fxcrt/fx_extension.h" ++#include "core/fxcrt/fx_string.h" + #include "third_party/bigint/BigIntegerLibrary.hh" + + namespace { +@@ -52,11 +53,11 @@ constexpr int8_t kPunctuation[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 21, 27, 9, -1}; + + bool IsAlphaUpperOrSpace(wchar_t ch) { +- return ch == ' ' || (ch >= 'A' && ch <= 'Z'); ++ return ch == ' ' || FXSYS_IsUpperASCII(ch); + } + + bool IsAlphaLowerOrSpace(wchar_t ch) { +- return ch == ' ' || (ch >= 'a' && ch <= 'z'); ++ return ch == ' ' || FXSYS_IsLowerASCII(ch); + } + + bool IsMixed(wchar_t ch) { +@@ -76,20 +77,19 @@ bool IsText(wchar_t ch) { + } // namespace + + // static +-Optional CBC_PDF417HighLevelEncoder::EncodeHighLevel( ++absl::optional CBC_PDF417HighLevelEncoder::EncodeHighLevel( + WideStringView msg) { +- ByteString bytes = FX_UTF8Encode(msg); ++ const ByteString bytes = FX_UTF8Encode(msg); + size_t len = bytes.GetLength(); + WideString result; + result.Reserve(len); + for (size_t i = 0; i < len; i++) { + wchar_t ch = bytes[i] & 0xff; + if (ch == '?' && bytes[i] != '?') +- return {}; ++ return absl::nullopt; + + result += ch; + } +- std::vector byteArr(bytes.begin(), bytes.end()); + len = result.GetLength(); + WideString sb; + sb.Reserve(len); +@@ -115,18 +115,18 @@ Optional CBC_PDF417HighLevelEncoder::EncodeHighLevel( + textSubMode = EncodeText(result, p, t, textSubMode, &sb); + p += t; + } else { +- Optional b = +- DetermineConsecutiveBinaryCount(result, &byteArr, p); +- if (!b) +- return {}; ++ absl::optional b = ++ DetermineConsecutiveBinaryCount(result, bytes.raw_span(), p); ++ if (!b.has_value()) ++ return absl::nullopt; + + size_t b_value = b.value(); + if (b_value == 0) + b_value = 1; + if (b_value == 1 && encodingMode == EncodingMode::kText) { +- EncodeBinary(byteArr, p, 1, EncodingMode::kText, &sb); ++ EncodeBinary(bytes.raw_span(), p, 1, EncodingMode::kText, &sb); + } else { +- EncodeBinary(byteArr, p, b_value, encodingMode, &sb); ++ EncodeBinary(bytes.raw_span(), p, b_value, encodingMode, &sb); + encodingMode = EncodingMode::kByte; + textSubMode = SubMode::kAlpha; + } +@@ -352,9 +352,10 @@ size_t CBC_PDF417HighLevelEncoder::DetermineConsecutiveTextCount( + return idx - startpos; + } + +-Optional CBC_PDF417HighLevelEncoder::DetermineConsecutiveBinaryCount( ++absl::optional ++CBC_PDF417HighLevelEncoder::DetermineConsecutiveBinaryCount( + WideString msg, +- std::vector* bytes, ++ pdfium::span bytes, + size_t startpos) { + size_t len = msg.GetLength(); + size_t idx = startpos; +@@ -382,8 +383,8 @@ Optional CBC_PDF417HighLevelEncoder::DetermineConsecutiveBinaryCount( + if (textCount >= 5) + return idx - startpos; + ch = msg[idx]; +- if ((*bytes)[idx] == 63 && ch != '?') +- return {}; ++ if (bytes[idx] == 63 && ch != '?') ++ return absl::nullopt; + idx++; + } + return idx - startpos; +diff --git a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h +index df1cd54f6..16b80daa4 100644 +--- a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h ++++ b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,11 +7,9 @@ + #ifndef FXBARCODE_PDF417_BC_PDF417HIGHLEVELENCODER_H_ + #define FXBARCODE_PDF417_BC_PDF417HIGHLEVELENCODER_H_ + +-#include +- +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" + #include "fxbarcode/pdf417/BC_PDF417.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + #include "third_party/base/span.h" + + class CBC_PDF417HighLevelEncoder { +@@ -19,7 +17,7 @@ class CBC_PDF417HighLevelEncoder { + CBC_PDF417HighLevelEncoder() = delete; + ~CBC_PDF417HighLevelEncoder() = delete; + +- static Optional EncodeHighLevel(WideStringView msg); ++ static absl::optional EncodeHighLevel(WideStringView msg); + + private: + enum class EncodingMode { kUnknown = 0, kText, kByte, kNumeric }; +@@ -42,9 +40,9 @@ class CBC_PDF417HighLevelEncoder { + WideString* sb); + static size_t DetermineConsecutiveDigitCount(WideString msg, size_t startpos); + static size_t DetermineConsecutiveTextCount(WideString msg, size_t startpos); +- static Optional DetermineConsecutiveBinaryCount( ++ static absl::optional DetermineConsecutiveBinaryCount( + WideString msg, +- std::vector* bytes, ++ pdfium::span bytes, + size_t startpos); + + friend class PDF417HighLevelEncoderTest_ConsecutiveBinaryCount_Test; +diff --git a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder_unittest.cpp b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder_unittest.cpp +index 4ca5c8ba3..7f665ac3d 100644 +--- a/fxbarcode/pdf417/BC_PDF417HighLevelEncoder_unittest.cpp ++++ b/fxbarcode/pdf417/BC_PDF417HighLevelEncoder_unittest.cpp +@@ -1,9 +1,11 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fxbarcode/pdf417/BC_PDF417HighLevelEncoder.h" + ++#include ++ + #include "testing/gtest/include/gtest/gtest.h" + + TEST(PDF417HighLevelEncoderTest, EncodeHighLevel) { +@@ -37,11 +39,11 @@ TEST(PDF417HighLevelEncoderTest, EncodeHighLevel) { + {L"0000000000000", L"\x0386\x000f\x00d9\x017b\x000b\x0064", 6}, + }; + +- for (size_t i = 0; i < FX_ArraySize(kEncodeHighLevelCases); ++i) { ++ for (size_t i = 0; i < std::size(kEncodeHighLevelCases); ++i) { + const EncodeHighLevelCase& testcase = kEncodeHighLevelCases[i]; + WideStringView input(testcase.input); + WideString expected(testcase.expected, testcase.expected_length); +- Optional result = ++ absl::optional result = + CBC_PDF417HighLevelEncoder::EncodeHighLevel(input); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(expected, result.value()) << " for case number " << i; +@@ -81,7 +83,7 @@ TEST(PDF417HighLevelEncoderTest, EncodeBinary) { + L"\u039c\u00c9\u031f\u012a\u00d2\u02d0", 6}, + }; + +- for (size_t i = 0; i < FX_ArraySize(kEncodeBinaryCases); ++i) { ++ for (size_t i = 0; i < std::size(kEncodeBinaryCases); ++i) { + const EncodeBinaryCase& testcase = kEncodeBinaryCases[i]; + std::vector input_array; + size_t input_length = strlen(testcase.input); +@@ -148,7 +150,7 @@ TEST(PDF417HighLevelEncoderTest, EncodeNumeric) { + 18}, + }; + +- for (size_t i = 0; i < FX_ArraySize(kEncodeNumericCases); ++i) { ++ for (size_t i = 0; i < std::size(kEncodeNumericCases); ++i) { + const EncodeNumericCase& testcase = kEncodeNumericCases[i]; + WideString input(testcase.input); + WideString expected(testcase.expected, testcase.expected_length); +@@ -193,7 +195,7 @@ TEST(PDF417HighLevelEncoderTest, ConsecutiveDigitCount) { + {L"123FOO45678", 6, 5}, + }; + +- for (size_t i = 0; i < FX_ArraySize(kConsecutiveDigitCases); ++i) { ++ for (size_t i = 0; i < std::size(kConsecutiveDigitCases); ++i) { + const ConsecutiveDigitCase& testcase = kConsecutiveDigitCases[i]; + WideString input(testcase.input); + int actual_count = +@@ -253,7 +255,7 @@ TEST(PDF417HighLevelEncoderTest, ConsecutiveTextCount) { + {L"XXX121XXX12345678901234", 0, 9}, + }; + +- for (size_t i = 0; i < FX_ArraySize(kConsecutiveTextCases); ++i) { ++ for (size_t i = 0; i < std::size(kConsecutiveTextCases); ++i) { + const ConsecutiveTextCase& testcase = kConsecutiveTextCases[i]; + WideString input(testcase.input); + int actual_count = +diff --git a/fxbarcode/pdf417/BC_PDF417Writer.cpp b/fxbarcode/pdf417/BC_PDF417Writer.cpp +index f9f36246a..a7ed45295 100644 +--- a/fxbarcode/pdf417/BC_PDF417Writer.cpp ++++ b/fxbarcode/pdf417/BC_PDF417Writer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,14 +22,17 @@ + + #include "fxbarcode/pdf417/BC_PDF417Writer.h" + ++#include ++ + #include + #include + ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/stl_util.h" + #include "fxbarcode/BC_TwoDimWriter.h" + #include "fxbarcode/common/BC_CommonBitMatrix.h" + #include "fxbarcode/pdf417/BC_PDF417.h" + #include "fxbarcode/pdf417/BC_PDF417BarcodeMatrix.h" +-#include "third_party/base/stl_util.h" + + CBC_PDF417Writer::CBC_PDF417Writer() : CBC_TwoDimWriter(false) {} + +@@ -43,10 +46,9 @@ bool CBC_PDF417Writer::SetErrorCorrectionLevel(int32_t level) { + return true; + } + +-std::vector CBC_PDF417Writer::Encode(WideStringView contents, +- int32_t* pOutWidth, +- int32_t* pOutHeight) { +- std::vector results; ++DataVector CBC_PDF417Writer::Encode(WideStringView contents, ++ int32_t* pOutWidth, ++ int32_t* pOutHeight) { + CBC_PDF417 encoder; + int32_t col = (m_Width / m_ModuleWidth - 69) / 17; + int32_t row = m_Height / (m_ModuleWidth * 20); +@@ -57,10 +59,10 @@ std::vector CBC_PDF417Writer::Encode(WideStringView contents, + else if (row >= 3 && row <= 90) + encoder.setDimensions(30, 1, row, row); + if (!encoder.GenerateBarcodeLogic(contents, error_correction_level())) +- return results; ++ return DataVector(); + + CBC_BarcodeMatrix* barcodeMatrix = encoder.getBarcodeMatrix(); +- std::vector matrixData = barcodeMatrix->toBitArray(); ++ DataVector matrixData = barcodeMatrix->toBitArray(); + int32_t matrixWidth = barcodeMatrix->getWidth(); + int32_t matrixHeight = barcodeMatrix->getHeight(); + +@@ -70,15 +72,13 @@ std::vector CBC_PDF417Writer::Encode(WideStringView contents, + } + *pOutWidth = matrixWidth; + *pOutHeight = matrixHeight; +- results = pdfium::Vector2D(*pOutWidth, *pOutHeight); +- memcpy(results.data(), matrixData.data(), *pOutWidth * *pOutHeight); +- return results; ++ return matrixData; + } + +-void CBC_PDF417Writer::RotateArray(std::vector* bitarray, ++void CBC_PDF417Writer::RotateArray(DataVector* bitarray, + int32_t height, + int32_t width) { +- std::vector temp = *bitarray; ++ DataVector temp = *bitarray; + for (int32_t ii = 0; ii < height; ii++) { + int32_t inverseii = height - ii - 1; + for (int32_t jj = 0; jj < width; jj++) { +diff --git a/fxbarcode/pdf417/BC_PDF417Writer.h b/fxbarcode/pdf417/BC_PDF417Writer.h +index 10f069aeb..dc1f1f44b 100644 +--- a/fxbarcode/pdf417/BC_PDF417Writer.h ++++ b/fxbarcode/pdf417/BC_PDF417Writer.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,11 @@ + #ifndef FXBARCODE_PDF417_BC_PDF417WRITER_H_ + #define FXBARCODE_PDF417_BC_PDF417WRITER_H_ + +-#include ++#include ++#include + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/widestring.h" + #include "fxbarcode/BC_TwoDimWriter.h" + + class CBC_PDF417Writer final : public CBC_TwoDimWriter { +@@ -18,15 +19,15 @@ class CBC_PDF417Writer final : public CBC_TwoDimWriter { + CBC_PDF417Writer(); + ~CBC_PDF417Writer() override; + +- std::vector Encode(WideStringView contents, +- int32_t* pOutWidth, +- int32_t* pOutHeight); ++ DataVector Encode(WideStringView contents, ++ int32_t* pOutWidth, ++ int32_t* pOutHeight); + + // CBC_TwoDimWriter + bool SetErrorCorrectionLevel(int32_t level) override; + + private: +- void RotateArray(std::vector* bitarray, ++ void RotateArray(DataVector* bitarray, + int32_t width, + int32_t height); + }; +diff --git a/fxbarcode/pdf417/BC_PDF417Writer_unittest.cpp b/fxbarcode/pdf417/BC_PDF417Writer_unittest.cpp +index 1c09d7875..d67d8b839 100644 +--- a/fxbarcode/pdf417/BC_PDF417Writer_unittest.cpp ++++ b/fxbarcode/pdf417/BC_PDF417Writer_unittest.cpp +@@ -1,11 +1,12 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fxbarcode/pdf417/BC_PDF417Writer.h" + +-#include ++#include + ++#include "core/fxcrt/data_vector.h" + #include "testing/gtest/include/gtest/gtest.h" + + class CBC_PDF417WriterTest : public testing::Test { +@@ -413,11 +414,11 @@ TEST_F(CBC_PDF417WriterTest, Encode) { + 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, + 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1}; +- std::vector data = writer.Encode(L"", &width, &height); +- ASSERT_EQ(FX_ArraySize(kExpectedData), data.size()); ++ DataVector data = writer.Encode(L"", &width, &height); ++ ASSERT_EQ(std::size(kExpectedData), data.size()); + ASSERT_EQ(kExpectedWidth, width); + ASSERT_EQ(kExpectedHeight, height); +- for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i) ++ for (size_t i = 0; i < std::size(kExpectedData); ++i) + EXPECT_EQ(kExpectedData[i], data[i]) << i; + } + { +@@ -810,11 +811,11 @@ TEST_F(CBC_PDF417WriterTest, Encode) { + 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, + 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1}; +- std::vector data = writer.Encode(L"hello world", &width, &height); +- ASSERT_EQ(FX_ArraySize(kExpectedData), data.size()); ++ DataVector data = writer.Encode(L"hello world", &width, &height); ++ ASSERT_EQ(std::size(kExpectedData), data.size()); + ASSERT_EQ(kExpectedWidth, width); + ASSERT_EQ(kExpectedHeight, height); +- for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i) ++ for (size_t i = 0; i < std::size(kExpectedData); ++i) + EXPECT_EQ(kExpectedData[i], data[i]) << i; + } + } +diff --git a/fxbarcode/qrcode/BC_QRCodeWriter.cpp b/fxbarcode/qrcode/BC_QRCodeWriter.cpp +index 1eead6236..8796ffbec 100644 +--- a/fxbarcode/qrcode/BC_QRCodeWriter.cpp ++++ b/fxbarcode/qrcode/BC_QRCodeWriter.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,6 +22,11 @@ + + #include "fxbarcode/qrcode/BC_QRCodeWriter.h" + ++#include ++ ++#include ++ ++#include "core/fxcrt/data_vector.h" + #include "fxbarcode/common/BC_CommonByteMatrix.h" + #include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h" + #include "fxbarcode/qrcode/BC_QRCoder.h" +@@ -29,7 +34,6 @@ + #include "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h" + #include "fxbarcode/qrcode/BC_QRCoderMode.h" + #include "fxbarcode/qrcode/BC_QRCoderVersion.h" +-#include "third_party/base/stl_util.h" + + CBC_QRCodeWriter::CBC_QRCodeWriter() : CBC_TwoDimWriter(true) {} + +@@ -43,11 +47,10 @@ bool CBC_QRCodeWriter::SetErrorCorrectionLevel(int32_t level) { + return true; + } + +-std::vector CBC_QRCodeWriter::Encode(WideStringView contents, +- int32_t ecLevel, +- int32_t* pOutWidth, +- int32_t* pOutHeight) { +- std::vector results; ++DataVector CBC_QRCodeWriter::Encode(WideStringView contents, ++ int32_t ecLevel, ++ int32_t* pOutWidth, ++ int32_t* pOutHeight) { + CBC_QRCoderErrorCorrectionLevel* ec = nullptr; + switch (ecLevel) { + case 0: +@@ -63,16 +66,14 @@ std::vector CBC_QRCodeWriter::Encode(WideStringView contents, + ec = CBC_QRCoderErrorCorrectionLevel::H; + break; + default: +- return results; ++ return DataVector(); + } + CBC_QRCoder qr; + if (!CBC_QRCoderEncoder::Encode(contents, ec, &qr)) +- return results; ++ return DataVector(); + + *pOutWidth = qr.GetMatrixWidth(); + *pOutHeight = qr.GetMatrixWidth(); +- results = pdfium::Vector2D(*pOutWidth, *pOutHeight); +- memcpy(results.data(), qr.GetMatrix()->GetArray().data(), +- *pOutWidth * *pOutHeight); +- return results; ++ std::unique_ptr matrix = qr.TakeMatrix(); ++ return matrix->TakeArray(); + } +diff --git a/fxbarcode/qrcode/BC_QRCodeWriter.h b/fxbarcode/qrcode/BC_QRCodeWriter.h +index 3f2cca021..84a5525dc 100644 +--- a/fxbarcode/qrcode/BC_QRCodeWriter.h ++++ b/fxbarcode/qrcode/BC_QRCodeWriter.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,8 +7,10 @@ + #ifndef FXBARCODE_QRCODE_BC_QRCODEWRITER_H_ + #define FXBARCODE_QRCODE_BC_QRCODEWRITER_H_ + +-#include ++#include + ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/widestring.h" + #include "fxbarcode/BC_TwoDimWriter.h" + + class CBC_QRCodeWriter final : public CBC_TwoDimWriter { +@@ -16,10 +18,10 @@ class CBC_QRCodeWriter final : public CBC_TwoDimWriter { + CBC_QRCodeWriter(); + ~CBC_QRCodeWriter() override; + +- std::vector Encode(WideStringView contents, +- int32_t ecLevel, +- int32_t* pOutWidth, +- int32_t* pOutHeight); ++ DataVector Encode(WideStringView contents, ++ int32_t ecLevel, ++ int32_t* pOutWidth, ++ int32_t* pOutHeight); + + // CBC_TwoDimWriter + bool SetErrorCorrectionLevel(int32_t level) override; +diff --git a/fxbarcode/qrcode/BC_QRCodeWriter_unittest.cpp b/fxbarcode/qrcode/BC_QRCodeWriter_unittest.cpp +index 709d4971c..3136cb340 100644 +--- a/fxbarcode/qrcode/BC_QRCodeWriter_unittest.cpp ++++ b/fxbarcode/qrcode/BC_QRCodeWriter_unittest.cpp +@@ -1,12 +1,12 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fxbarcode/qrcode/BC_QRCodeWriter.h" + +-#include ++#include + +-#include "core/fxcrt/fx_memory.h" ++#include "core/fxcrt/data_vector.h" + #include "testing/gtest/include/gtest/gtest.h" + + class CBC_QRCodeWriterTest : public testing::Test { +@@ -51,11 +51,11 @@ TEST_F(CBC_QRCodeWriterTest, Encode) { + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1 + }; + // clang-format on +- std::vector data = writer.Encode(L"", 0, &width, &height); +- ASSERT_EQ(FX_ArraySize(kExpectedData), data.size()); ++ DataVector data = writer.Encode(L"", 0, &width, &height); ++ ASSERT_EQ(std::size(kExpectedData), data.size()); + ASSERT_EQ(kExpectedDimension, width); + ASSERT_EQ(kExpectedDimension, height); +- for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i) ++ for (size_t i = 0; i < std::size(kExpectedData); ++i) + EXPECT_EQ(kExpectedData[i], data[i]) << i; + } + { +@@ -85,11 +85,11 @@ TEST_F(CBC_QRCodeWriterTest, Encode) { + 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1 + }; + // clang-format on +- std::vector data = writer.Encode(L"", 1, &width, &height); +- ASSERT_EQ(FX_ArraySize(kExpectedData), data.size()); ++ DataVector data = writer.Encode(L"", 1, &width, &height); ++ ASSERT_EQ(std::size(kExpectedData), data.size()); + ASSERT_EQ(kExpectedDimension, width); + ASSERT_EQ(kExpectedDimension, height); +- for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i) ++ for (size_t i = 0; i < std::size(kExpectedData); ++i) + EXPECT_EQ(kExpectedData[i], data[i]) << i; + } + { +@@ -119,11 +119,11 @@ TEST_F(CBC_QRCodeWriterTest, Encode) { + 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0 + }; + // clang-format on +- std::vector data = writer.Encode(L"", 2, &width, &height); +- ASSERT_EQ(FX_ArraySize(kExpectedData), data.size()); ++ DataVector data = writer.Encode(L"", 2, &width, &height); ++ ASSERT_EQ(std::size(kExpectedData), data.size()); + ASSERT_EQ(kExpectedDimension, width); + ASSERT_EQ(kExpectedDimension, height); +- for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i) ++ for (size_t i = 0; i < std::size(kExpectedData); ++i) + EXPECT_EQ(kExpectedData[i], data[i]) << i; + } + { +@@ -153,11 +153,11 @@ TEST_F(CBC_QRCodeWriterTest, Encode) { + 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0 + }; + // clang-format on +- std::vector data = writer.Encode(L"", 3, &width, &height); +- ASSERT_EQ(FX_ArraySize(kExpectedData), data.size()); ++ DataVector data = writer.Encode(L"", 3, &width, &height); ++ ASSERT_EQ(std::size(kExpectedData), data.size()); + ASSERT_EQ(kExpectedDimension, width); + ASSERT_EQ(kExpectedDimension, height); +- for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i) ++ for (size_t i = 0; i < std::size(kExpectedData); ++i) + EXPECT_EQ(kExpectedData[i], data[i]) << i; + } + { +@@ -187,12 +187,12 @@ TEST_F(CBC_QRCodeWriterTest, Encode) { + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0 + }; + // clang-format on +- std::vector data = ++ DataVector data = + writer.Encode(L"hello world", 0, &width, &height); +- ASSERT_EQ(FX_ArraySize(kExpectedData), data.size()); ++ ASSERT_EQ(std::size(kExpectedData), data.size()); + ASSERT_EQ(kExpectedDimension, width); + ASSERT_EQ(kExpectedDimension, height); +- for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i) ++ for (size_t i = 0; i < std::size(kExpectedData); ++i) + EXPECT_EQ(kExpectedData[i], data[i]) << i; + } + { +@@ -222,12 +222,12 @@ TEST_F(CBC_QRCodeWriterTest, Encode) { + 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0 + }; + // clang-format on +- std::vector data = ++ DataVector data = + writer.Encode(L"hello world", 1, &width, &height); +- ASSERT_EQ(FX_ArraySize(kExpectedData), data.size()); ++ ASSERT_EQ(std::size(kExpectedData), data.size()); + ASSERT_EQ(kExpectedDimension, width); + ASSERT_EQ(kExpectedDimension, height); +- for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i) ++ for (size_t i = 0; i < std::size(kExpectedData); ++i) + EXPECT_EQ(kExpectedData[i], data[i]) << i; + } + { +@@ -260,12 +260,12 @@ TEST_F(CBC_QRCodeWriterTest, Encode) { + 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, + 1}; +- std::vector data = ++ DataVector data = + writer.Encode(L"hello world", 2, &width, &height); +- ASSERT_EQ(FX_ArraySize(kExpectedData), data.size()); ++ ASSERT_EQ(std::size(kExpectedData), data.size()); + ASSERT_EQ(kExpectedDimension, width); + ASSERT_EQ(kExpectedDimension, height); +- for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i) ++ for (size_t i = 0; i < std::size(kExpectedData); ++i) + EXPECT_EQ(kExpectedData[i], data[i]) << i; + } + { +@@ -298,12 +298,12 @@ TEST_F(CBC_QRCodeWriterTest, Encode) { + 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, + 1}; +- std::vector data = ++ DataVector data = + writer.Encode(L"hello world", 3, &width, &height); +- ASSERT_EQ(FX_ArraySize(kExpectedData), data.size()); ++ ASSERT_EQ(std::size(kExpectedData), data.size()); + ASSERT_EQ(kExpectedDimension, width); + ASSERT_EQ(kExpectedDimension, height); +- for (size_t i = 0; i < FX_ArraySize(kExpectedData); ++i) ++ for (size_t i = 0; i < std::size(kExpectedData); ++i) + EXPECT_EQ(kExpectedData[i], data[i]) << i; + } + } +diff --git a/fxbarcode/qrcode/BC_QRCoder.cpp b/fxbarcode/qrcode/BC_QRCoder.cpp +index 46f80fcd9..6525f1e83 100644 +--- a/fxbarcode/qrcode/BC_QRCoder.cpp ++++ b/fxbarcode/qrcode/BC_QRCoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -26,13 +26,14 @@ + #include "fxbarcode/qrcode/BC_QRCoder.h" + #include "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h" + #include "fxbarcode/qrcode/BC_QRCoderMode.h" ++#include "third_party/base/numerics/safe_conversions.h" + + CBC_QRCoder::CBC_QRCoder() = default; + + CBC_QRCoder::~CBC_QRCoder() = default; + + const CBC_QRCoderErrorCorrectionLevel* CBC_QRCoder::GetECLevel() const { +- return m_ecLevel.Get(); ++ return m_ecLevel; + } + + int32_t CBC_QRCoder::GetVersion() const { +@@ -59,8 +60,8 @@ int32_t CBC_QRCoder::GetNumRSBlocks() const { + return m_numRSBlocks; + } + +-const CBC_CommonByteMatrix* CBC_QRCoder::GetMatrix() const { +- return m_matrix.get(); ++std::unique_ptr CBC_QRCoder::TakeMatrix() { ++ return std::move(m_matrix); + } + + bool CBC_QRCoder::IsValid() const { +@@ -69,7 +70,8 @@ bool CBC_QRCoder::IsValid() const { + m_numECBytes != -1 && m_numRSBlocks != -1 && + IsValidMaskPattern(m_maskPattern) && + m_numTotalBytes == m_numDataBytes + m_numECBytes && m_matrix && +- m_matrixWidth == m_matrix->GetWidth() && ++ m_matrixWidth == ++ pdfium::base::checked_cast(m_matrix->GetWidth()) && + m_matrix->GetWidth() == m_matrix->GetHeight(); + } + +diff --git a/fxbarcode/qrcode/BC_QRCoder.h b/fxbarcode/qrcode/BC_QRCoder.h +index ec9bb09ec..e120b516e 100644 +--- a/fxbarcode/qrcode/BC_QRCoder.h ++++ b/fxbarcode/qrcode/BC_QRCoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,7 +12,6 @@ + #include "core/fxcrt/unowned_ptr.h" + + class CBC_QRCoderErrorCorrectionLevel; +-class CBC_QRCoderMode; + class CBC_CommonByteMatrix; + + class CBC_QRCoder final { +@@ -31,7 +30,7 @@ class CBC_QRCoder final { + int32_t GetNumTotalBytes() const; + int32_t GetNumDataBytes() const; + int32_t GetNumRSBlocks() const; +- const CBC_CommonByteMatrix* GetMatrix() const; ++ std::unique_ptr TakeMatrix(); + + bool IsValid() const; + +diff --git a/fxbarcode/qrcode/BC_QRCoderBitVector.cpp b/fxbarcode/qrcode/BC_QRCoderBitVector.cpp +index 121fa926c..4561a1568 100644 +--- a/fxbarcode/qrcode/BC_QRCoderBitVector.cpp ++++ b/fxbarcode/qrcode/BC_QRCoderBitVector.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -23,7 +23,7 @@ + #include "fxbarcode/qrcode/BC_QRCoderBitVector.h" + + #include "core/fxcrt/fx_system.h" +-#include "third_party/base/logging.h" ++#include "third_party/base/check.h" + + CBC_QRCoderBitVector::CBC_QRCoderBitVector() = default; + +@@ -44,7 +44,7 @@ size_t CBC_QRCoderBitVector::Size() const { + } + + void CBC_QRCoderBitVector::AppendBit(int32_t bit) { +- ASSERT(bit == 0 || bit == 1); ++ DCHECK(bit == 0 || bit == 1); + int32_t numBitsInLastByte = m_sizeInBits & 0x7; + if (numBitsInLastByte == 0) { + AppendByte(0); +@@ -55,8 +55,8 @@ void CBC_QRCoderBitVector::AppendBit(int32_t bit) { + } + + void CBC_QRCoderBitVector::AppendBits(int32_t value, int32_t numBits) { +- ASSERT(numBits > 0); +- ASSERT(numBits <= 32); ++ DCHECK(numBits > 0); ++ DCHECK(numBits <= 32); + + int32_t numBitsLeft = numBits; + while (numBitsLeft > 0) { +@@ -79,14 +79,14 @@ bool CBC_QRCoderBitVector::XOR(const CBC_QRCoderBitVector* other) { + if (m_sizeInBits != other->Size()) + return false; + +- const auto* pOther = other->GetArray(); ++ pdfium::span other_span = other->GetArray(); + for (size_t i = 0; i < sizeInBytes(); ++i) +- m_array[i] ^= pOther[i]; ++ m_array[i] ^= other_span[i]; + return true; + } + +-const uint8_t* CBC_QRCoderBitVector::GetArray() const { +- return m_array.data(); ++pdfium::span CBC_QRCoderBitVector::GetArray() const { ++ return m_array; + } + + void CBC_QRCoderBitVector::AppendByte(int8_t value) { +diff --git a/fxbarcode/qrcode/BC_QRCoderBitVector.h b/fxbarcode/qrcode/BC_QRCoderBitVector.h +index 9b8af810c..1bfa24bb6 100644 +--- a/fxbarcode/qrcode/BC_QRCoderBitVector.h ++++ b/fxbarcode/qrcode/BC_QRCoderBitVector.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,14 +10,15 @@ + #include + #include + +-#include ++#include "core/fxcrt/data_vector.h" ++#include "third_party/base/span.h" + + class CBC_QRCoderBitVector { + public: + CBC_QRCoderBitVector(); + ~CBC_QRCoderBitVector(); + +- const uint8_t* GetArray() const; ++ pdfium::span GetArray() const; + int32_t At(size_t index) const; + size_t Size() const; + size_t sizeInBytes() const; +@@ -31,7 +32,7 @@ class CBC_QRCoderBitVector { + void AppendByte(int8_t value); + + size_t m_sizeInBits = 0; +- std::vector m_array; ++ DataVector m_array; + }; + + #endif // FXBARCODE_QRCODE_BC_QRCODERBITVECTOR_H_ +diff --git a/fxbarcode/qrcode/BC_QRCoderECBlocks.cpp b/fxbarcode/qrcode/BC_QRCoderECBlocks.cpp +index 28f46882d..a32c4098f 100644 +--- a/fxbarcode/qrcode/BC_QRCoderECBlocks.cpp ++++ b/fxbarcode/qrcode/BC_QRCoderECBlocks.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -27,7 +27,7 @@ + CBC_QRCoderECBlocks::CBC_QRCoderECBlocks(const CBC_QRCoderECBlockData& data) + : m_data(data) {} + +-CBC_QRCoderECBlocks::~CBC_QRCoderECBlocks() {} ++CBC_QRCoderECBlocks::~CBC_QRCoderECBlocks() = default; + + int32_t CBC_QRCoderECBlocks::GetECCodeWordsPerBlock() const { + return m_data.ecCodeWordsPerBlock; +diff --git a/fxbarcode/qrcode/BC_QRCoderECBlocks.h b/fxbarcode/qrcode/BC_QRCoderECBlocks.h +index 2583b12e7..2fd6a3fe7 100644 +--- a/fxbarcode/qrcode/BC_QRCoderECBlocks.h ++++ b/fxbarcode/qrcode/BC_QRCoderECBlocks.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/qrcode/BC_QRCoderECBlocksData.cpp b/fxbarcode/qrcode/BC_QRCoderECBlocksData.cpp +index 26ff74837..13b320e6b 100644 +--- a/fxbarcode/qrcode/BC_QRCoderECBlocksData.cpp ++++ b/fxbarcode/qrcode/BC_QRCoderECBlocksData.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,7 +22,9 @@ + + #include "fxbarcode/qrcode/BC_QRCoderECBlocksData.h" + +-const CBC_QRCoderECBlockData g_ECBData[40][4] = { ++namespace fxbarcode { ++ ++const CBC_QRCoderECBlockData kECBData[40][4] = { + {{7, 1, 19, 0, 0}, {10, 1, 16, 0, 0}, {13, 1, 13, 0, 0}, {17, 1, 9, 0, 0}}, + {{10, 1, 34, 0, 0}, + {16, 1, 28, 0, 0}, +@@ -178,3 +180,5 @@ const CBC_QRCoderECBlockData g_ECBData[40][4] = { + {30, 34, 24, 34, 25}, + {30, 20, 15, 61, 16}}, + }; ++ ++} // namespace fxbarcode +diff --git a/fxbarcode/qrcode/BC_QRCoderECBlocksData.h b/fxbarcode/qrcode/BC_QRCoderECBlocksData.h +index 7ed4ca468..05972792f 100644 +--- a/fxbarcode/qrcode/BC_QRCoderECBlocksData.h ++++ b/fxbarcode/qrcode/BC_QRCoderECBlocksData.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -17,6 +17,10 @@ struct CBC_QRCoderECBlockData { + uint8_t dataCodeWords2; + }; + +-extern const CBC_QRCoderECBlockData g_ECBData[40][4]; ++namespace fxbarcode { ++ ++extern const CBC_QRCoderECBlockData kECBData[40][4]; ++ ++} // namespace fxbarcode + + #endif // FXBARCODE_QRCODE_BC_QRCODERECBLOCKSDATA_H_ +diff --git a/fxbarcode/qrcode/BC_QRCoderEncoder.cpp b/fxbarcode/qrcode/BC_QRCoderEncoder.cpp +index be02f5bef..b55255d86 100644 +--- a/fxbarcode/qrcode/BC_QRCoderEncoder.cpp ++++ b/fxbarcode/qrcode/BC_QRCoderEncoder.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,11 +22,16 @@ + + #include "fxbarcode/qrcode/BC_QRCoderEncoder.h" + ++#include ++ + #include + #include + #include + #include + ++#include "core/fxcrt/data_vector.h" ++#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/span_util.h" + #include "fxbarcode/common/BC_CommonByteMatrix.h" + #include "fxbarcode/common/reedsolomon/BC_ReedSolomon.h" + #include "fxbarcode/common/reedsolomon/BC_ReedSolomonGF256.h" +@@ -37,8 +42,9 @@ + #include "fxbarcode/qrcode/BC_QRCoderMatrixUtil.h" + #include "fxbarcode/qrcode/BC_QRCoderMode.h" + #include "fxbarcode/qrcode/BC_QRCoderVersion.h" +-#include "third_party/base/optional.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" + + using ModeStringPair = std::pair; + +@@ -47,12 +53,12 @@ namespace { + CBC_ReedSolomonGF256* g_QRCodeField = nullptr; + + struct QRCoderBlockPair { +- std::vector data; +- std::vector ecc; ++ DataVector data; ++ DataVector ecc; + }; + + // This is a mapping for an ASCII table, starting at an index of 32. +-const int8_t g_alphaNumericTable[] = { ++const int8_t kAlphaNumericTable[] = { + 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 32-47 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 48-63 + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 64-79 +@@ -62,9 +68,9 @@ int32_t GetAlphaNumericCode(int32_t code) { + if (code < 32) + return -1; + size_t code_index = static_cast(code - 32); +- if (code_index >= FX_ArraySize(g_alphaNumericTable)) ++ if (code_index >= std::size(kAlphaNumericTable)) + return -1; +- return g_alphaNumericTable[code_index]; ++ return kAlphaNumericTable[code_index]; + } + + bool AppendNumericBytes(const ByteString& content, CBC_QRCoderBitVector* bits) { +@@ -113,55 +119,14 @@ bool AppendAlphaNumericBytes(const ByteString& content, + return true; + } + +-bool AppendGBKBytes(const ByteString& content, CBC_QRCoderBitVector* bits) { +- size_t length = content.GetLength(); +- uint32_t value = 0; +- for (size_t i = 0; i < length; i += 2) { +- value = (uint32_t)(content[i] << 8 | content[i + 1]); +- if (value <= 0xAAFE && value >= 0xA1A1) +- value -= 0xA1A1; +- else if (value <= 0xFAFE && value >= 0xB0A1) +- value -= 0xA6A1; +- else +- return false; +- +- value = (uint32_t)((value >> 8) * 0x60) + (uint32_t)(value & 0xff); +- bits->AppendBits(value, 13); +- } +- return true; +-} +- +-bool Append8BitBytes(const ByteString& content, +- CBC_QRCoderBitVector* bits, +- ByteString encoding) { +- for (size_t i = 0; i < content.GetLength(); i++) +- bits->AppendBits(content[i], 8); +- return true; +-} +- +-bool AppendKanjiBytes(const ByteString& content, CBC_QRCoderBitVector* bits) { +- std::vector bytes; +- uint32_t value = 0; +- // TODO(thestig): This is wrong, as |bytes| is empty. +- for (size_t i = 0; i < bytes.size(); i += 2) { +- value = (uint32_t)((content[i] << 8) | content[i + 1]); +- if (value <= 0x9ffc && value >= 0x8140) +- value -= 0x8140; +- else if (value <= 0xebbf && value >= 0xe040) +- value -= 0xc140; +- else +- return false; +- +- value = (uint32_t)((value >> 8) * 0xc0) + (uint32_t)(value & 0xff); +- bits->AppendBits(value, 13); +- } ++bool Append8BitBytes(const ByteString& content, CBC_QRCoderBitVector* bits) { ++ for (char c : content) ++ bits->AppendBits(c, 8); + return true; + } + + void AppendModeInfo(CBC_QRCoderMode* mode, CBC_QRCoderBitVector* bits) { + bits->AppendBits(mode->GetBits(), 4); +- if (mode == CBC_QRCoderMode::sGBK) +- bits->AppendBits(1, 4); + } + + bool AppendLengthInfo(int32_t numLetters, +@@ -177,26 +142,19 @@ bool AppendLengthInfo(int32_t numLetters, + if (numBits > ((1 << numBits) - 1)) + return true; + +- if (mode == CBC_QRCoderMode::sGBK) +- bits->AppendBits(numLetters / 2, numBits); + bits->AppendBits(numLetters, numBits); + return true; + } + + bool AppendBytes(const ByteString& content, + CBC_QRCoderMode* mode, +- CBC_QRCoderBitVector* bits, +- ByteString encoding) { ++ CBC_QRCoderBitVector* bits) { + if (mode == CBC_QRCoderMode::sNUMERIC) + return AppendNumericBytes(content, bits); + if (mode == CBC_QRCoderMode::sALPHANUMERIC) + return AppendAlphaNumericBytes(content, bits); + if (mode == CBC_QRCoderMode::sBYTE) +- return Append8BitBytes(content, bits, encoding); +- if (mode == CBC_QRCoderMode::sKANJI) +- return AppendKanjiBytes(content, bits); +- if (mode == CBC_QRCoderMode::sGBK) +- return AppendGBKBytes(content, bits); ++ return Append8BitBytes(content, bits); + return false; + } + +@@ -224,19 +182,19 @@ bool InitQRCode(int32_t numInputBytes, + return false; + } + +-std::vector GenerateECBytes(pdfium::span dataBytes, +- size_t numEcBytesInBlock) { ++DataVector GenerateECBytes(pdfium::span dataBytes, ++ size_t numEcBytesInBlock) { + // If |numEcBytesInBlock| is 0, the encoder will fail anyway. +- ASSERT(numEcBytesInBlock > 0); ++ DCHECK(numEcBytesInBlock > 0); + std::vector toEncode(dataBytes.size() + numEcBytesInBlock); + std::copy(dataBytes.begin(), dataBytes.end(), toEncode.begin()); + +- std::vector ecBytes; ++ DataVector ecBytes; + CBC_ReedSolomonEncoder encoder(g_QRCodeField); + if (encoder.Encode(&toEncode, numEcBytesInBlock)) { +- ecBytes = std::vector(toEncode.begin() + dataBytes.size(), +- toEncode.end()); +- ASSERT(ecBytes.size() == static_cast(numEcBytesInBlock)); ++ ecBytes = DataVector(toEncode.begin() + dataBytes.size(), ++ toEncode.end()); ++ DCHECK_EQ(ecBytes.size(), static_cast(numEcBytesInBlock)); + } + return ecBytes; + } +@@ -248,7 +206,7 @@ int32_t CalculateMaskPenalty(CBC_CommonByteMatrix* matrix) { + CBC_QRCoderMaskUtil::ApplyMaskPenaltyRule4(matrix); + } + +-Optional ChooseMaskPattern( ++absl::optional ChooseMaskPattern( + CBC_QRCoderBitVector* bits, + const CBC_QRCoderErrorCorrectionLevel* ecLevel, + int32_t version, +@@ -259,7 +217,7 @@ Optional ChooseMaskPattern( + maskPattern++) { + if (!CBC_QRCoderMatrixUtil::BuildMatrix(bits, ecLevel, version, maskPattern, + matrix)) { +- return {}; ++ return absl::nullopt; + } + int32_t penalty = CalculateMaskPenalty(matrix); + if (penalty < minPenalty) { +@@ -320,10 +278,7 @@ bool TerminateBits(int32_t numDataBytes, CBC_QRCoderBitVector* bits) { + return bits->Size() == capacity; + } + +-CBC_QRCoderMode* ChooseMode(const ByteString& content, ByteString encoding) { +- if (encoding.Compare("SHIFT_JIS") == 0) +- return CBC_QRCoderMode::sKANJI; +- ++CBC_QRCoderMode* ChooseMode(const ByteString& content) { + bool hasNumeric = false; + bool hasAlphaNumeric = false; + for (size_t i = 0; i < content.GetLength(); i++) { +@@ -347,8 +302,8 @@ bool InterleaveWithECBytes(CBC_QRCoderBitVector* bits, + int32_t numDataBytes, + int32_t numRSBlocks, + CBC_QRCoderBitVector* result) { +- ASSERT(numTotalBytes >= 0); +- ASSERT(numDataBytes >= 0); ++ DCHECK(numTotalBytes >= 0); ++ DCHECK(numDataBytes >= 0); + if (bits->sizeInBytes() != static_cast(numDataBytes)) + return false; + +@@ -365,11 +320,11 @@ bool InterleaveWithECBytes(CBC_QRCoderBitVector* bits, + if (numDataBytesInBlock < 0 || numEcBytesInBlock <= 0) + return false; + +- std::vector dataBytes(numDataBytesInBlock); +- memcpy(dataBytes.data(), bits->GetArray() + dataBytesOffset, +- numDataBytesInBlock); +- std::vector ecBytes = +- GenerateECBytes(dataBytes, numEcBytesInBlock); ++ DataVector dataBytes(numDataBytesInBlock); ++ fxcrt::spancpy( ++ pdfium::make_span(dataBytes), ++ bits->GetArray().subspan(dataBytesOffset, numDataBytesInBlock)); ++ DataVector ecBytes = GenerateECBytes(dataBytes, numEcBytesInBlock); + if (ecBytes.empty()) + return false; + +@@ -384,14 +339,14 @@ bool InterleaveWithECBytes(CBC_QRCoderBitVector* bits, + + for (size_t x = 0; x < maxNumDataBytes; x++) { + for (size_t j = 0; j < blocks.size(); j++) { +- const std::vector& dataBytes = blocks[j].data; ++ const DataVector& dataBytes = blocks[j].data; + if (x < dataBytes.size()) + result->AppendBits(dataBytes[x], 8); + } + } + for (size_t y = 0; y < maxNumEcBytes; y++) { + for (size_t l = 0; l < blocks.size(); l++) { +- const std::vector& ecBytes = blocks[l].ecc; ++ const DataVector& ecBytes = blocks[l].ecc; + if (y < ecBytes.size()) + result->AppendBits(ecBytes[y], 8); + } +@@ -417,11 +372,10 @@ void CBC_QRCoderEncoder::Finalize() { + bool CBC_QRCoderEncoder::Encode(WideStringView content, + const CBC_QRCoderErrorCorrectionLevel* ecLevel, + CBC_QRCoder* qrCode) { +- ByteString encoding = "utf8"; + ByteString utf8Data = FX_UTF8Encode(content); +- CBC_QRCoderMode* mode = ChooseMode(utf8Data, encoding); ++ CBC_QRCoderMode* mode = ChooseMode(utf8Data); + CBC_QRCoderBitVector dataBits; +- if (!AppendBytes(utf8Data, mode, &dataBits, encoding)) ++ if (!AppendBytes(utf8Data, mode, &dataBits)) + return false; + int32_t numInputBytes = dataBits.sizeInBytes(); + if (!InitQRCode(numInputBytes, ecLevel, qrCode)) +@@ -444,14 +398,14 @@ bool CBC_QRCoderEncoder::Encode(WideStringView content, + return false; + } + +- auto matrix = pdfium::MakeUnique( ++ auto matrix = std::make_unique( + qrCode->GetMatrixWidth(), qrCode->GetMatrixWidth()); +- Optional maskPattern = ChooseMaskPattern( ++ absl::optional maskPattern = ChooseMaskPattern( + &finalBits, qrCode->GetECLevel(), qrCode->GetVersion(), matrix.get()); +- if (!maskPattern) ++ if (!maskPattern.has_value()) + return false; + +- qrCode->SetMaskPattern(*maskPattern); ++ qrCode->SetMaskPattern(maskPattern.value()); + if (!CBC_QRCoderMatrixUtil::BuildMatrix( + &finalBits, qrCode->GetECLevel(), qrCode->GetVersion(), + qrCode->GetMaskPattern(), matrix.get())) { +diff --git a/fxbarcode/qrcode/BC_QRCoderEncoder.h b/fxbarcode/qrcode/BC_QRCoderEncoder.h +index a3af1fc1d..8677dd5a8 100644 +--- a/fxbarcode/qrcode/BC_QRCoderEncoder.h ++++ b/fxbarcode/qrcode/BC_QRCoderEncoder.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,7 @@ + #ifndef FXBARCODE_QRCODE_BC_QRCODERENCODER_H_ + #define FXBARCODE_QRCODE_BC_QRCODERENCODER_H_ + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" + + class CBC_QRCoder; + class CBC_QRCoderErrorCorrectionLevel; +diff --git a/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.cpp b/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.cpp +index a0e5c2b9b..4f89fba1f 100644 +--- a/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.cpp ++++ b/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h b/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h +index 9a572a5ef..6cb8bb4b7 100644 +--- a/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h ++++ b/fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp b/fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp +index 9776ddc9f..f518c8b01 100644 +--- a/fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp ++++ b/fxbarcode/qrcode/BC_QRCoderMaskUtil.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -25,6 +25,8 @@ + #include "fxbarcode/common/BC_CommonByteMatrix.h" + #include "fxbarcode/qrcode/BC_QRCoder.h" + #include "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h" ++#include "third_party/base/check.h" ++#include "third_party/base/notreached.h" + + namespace { + +@@ -33,13 +35,13 @@ int32_t ApplyMaskPenaltyRule1Internal(CBC_CommonByteMatrix* matrix, + int32_t penalty = 0; + int32_t numSameBitCells = 0; + int32_t prevBit = -1; +- int32_t width = matrix->GetWidth(); +- int32_t height = matrix->GetHeight(); +- int32_t iLimit = isHorizontal ? height : width; +- int32_t jLimit = isHorizontal ? width : height; ++ size_t width = matrix->GetWidth(); ++ size_t height = matrix->GetHeight(); ++ size_t iLimit = isHorizontal ? height : width; ++ size_t jLimit = isHorizontal ? width : height; + pdfium::span array = matrix->GetArray(); +- for (int32_t i = 0; i < iLimit; ++i) { +- for (int32_t j = 0; j < jLimit; ++j) { ++ for (size_t i = 0; i < iLimit; ++i) { ++ for (size_t j = 0; j < jLimit; ++j) { + int32_t bit = isHorizontal ? array[i * width + j] : array[j * width + i]; + if (bit == prevBit) { + numSameBitCells += 1; +@@ -72,10 +74,10 @@ int32_t CBC_QRCoderMaskUtil::ApplyMaskPenaltyRule2( + CBC_CommonByteMatrix* matrix) { + int32_t penalty = 0; + pdfium::span array = matrix->GetArray(); +- int32_t width = matrix->GetWidth(); +- int32_t height = matrix->GetHeight(); +- for (int32_t y = 0; y < height - 1; y++) { +- for (int32_t x = 0; x < width - 1; x++) { ++ size_t width = matrix->GetWidth(); ++ size_t height = matrix->GetHeight(); ++ for (size_t y = 0; y + 1 < height; y++) { ++ for (size_t x = 0; x + 1 < width; x++) { + int32_t value = array[y * width + x]; + if (value == array[y * width + x + 1] && + value == array[(y + 1) * width + x] && +@@ -92,22 +94,20 @@ int32_t CBC_QRCoderMaskUtil::ApplyMaskPenaltyRule3( + CBC_CommonByteMatrix* matrix) { + int32_t penalty = 0; + pdfium::span array = matrix->GetArray(); +- int32_t width = matrix->GetWidth(); +- int32_t height = matrix->GetHeight(); +- for (int32_t y = 0; y < height; ++y) { +- for (int32_t x = 0; x < width; ++x) { +- if (x == 0 && +- ((y >= 0 && y <= 6) || (y >= height - 7 && y <= height - 1))) { ++ size_t width = matrix->GetWidth(); ++ size_t height = matrix->GetHeight(); ++ for (size_t y = 0; y < height; ++y) { ++ for (size_t x = 0; x < width; ++x) { ++ if (x == 0 && (y <= 6 || (y + 7 >= height && y + 1 <= height))) { + continue; + } +- if (x == width - 7 && (y >= 0 && y <= 6)) { ++ if (x + 7 == width && y <= 6) { + continue; + } +- if (y == 0 && +- ((x >= 0 && x <= 6) || (x >= width - 7 && x <= width - 1))) { ++ if (y == 0 && (x <= 6 || (x + 7 >= width && x + 1 <= width))) { + continue; + } +- if (y == height - 7 && (x >= 0 && x <= 6)) { ++ if (y + 7 == height && x <= 6) { + continue; + } + if (x + 6 < width && array[y * width + x] == 1 && +@@ -117,7 +117,7 @@ int32_t CBC_QRCoderMaskUtil::ApplyMaskPenaltyRule3( + ((x + 10 < width && array[y * width + x + 7] == 0 && + array[y * width + x + 8] == 0 && array[y * width + x + 9] == 0 && + array[y * width + x + 10] == 0) || +- (x - 4 >= 0 && array[y * width + x - 1] == 0 && ++ (x >= 4 && array[y * width + x - 1] == 0 && + array[y * width + x - 2] == 0 && array[y * width + x - 3] == 0 && + array[y * width + x - 4] == 0))) { + penalty += 40; +@@ -130,7 +130,7 @@ int32_t CBC_QRCoderMaskUtil::ApplyMaskPenaltyRule3( + array[(y + 8) * width + x] == 0 && + array[(y + 9) * width + x] == 0 && + array[(y + 10) * width + x] == 0) || +- (y - 4 >= 0 && array[(y - 1) * width + x] == 0 && ++ (y >= 4 && array[(y - 1) * width + x] == 0 && + array[(y - 2) * width + x] == 0 && + array[(y - 3) * width + x] == 0 && + array[(y - 4) * width + x] == 0))) { +@@ -146,15 +146,15 @@ int32_t CBC_QRCoderMaskUtil::ApplyMaskPenaltyRule4( + CBC_CommonByteMatrix* matrix) { + int32_t numDarkCells = 0; + pdfium::span array = matrix->GetArray(); +- int32_t width = matrix->GetWidth(); +- int32_t height = matrix->GetHeight(); +- for (int32_t y = 0; y < height; ++y) { +- for (int32_t x = 0; x < width; ++x) { ++ size_t width = matrix->GetWidth(); ++ size_t height = matrix->GetHeight(); ++ for (size_t y = 0; y < height; ++y) { ++ for (size_t x = 0; x < width; ++x) { + if (array[y * width + x] == 1) + numDarkCells += 1; + } + } +- int32_t numTotalCells = matrix->GetHeight() * matrix->GetWidth(); ++ size_t numTotalCells = matrix->GetHeight() * matrix->GetWidth(); + double darkRatio = static_cast(numDarkCells) / numTotalCells; + return abs(static_cast(darkRatio * 100 - 50) / 5) * 5 * 10; + } +@@ -163,9 +163,10 @@ int32_t CBC_QRCoderMaskUtil::ApplyMaskPenaltyRule4( + bool CBC_QRCoderMaskUtil::GetDataMaskBit(int32_t maskPattern, + int32_t x, + int32_t y) { +- ASSERT(CBC_QRCoder::IsValidMaskPattern(maskPattern)); ++ DCHECK(CBC_QRCoder::IsValidMaskPattern(maskPattern)); + +- int32_t intermediate = 0, temp = 0; ++ int32_t intermediate = 0; ++ int32_t temp = 0; + switch (maskPattern) { + case 0: + intermediate = (y + x) & 0x1; +diff --git a/fxbarcode/qrcode/BC_QRCoderMaskUtil.h b/fxbarcode/qrcode/BC_QRCoderMaskUtil.h +index b2da2a8b8..e7415376e 100644 +--- a/fxbarcode/qrcode/BC_QRCoderMaskUtil.h ++++ b/fxbarcode/qrcode/BC_QRCoderMaskUtil.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/qrcode/BC_QRCoderMatrixUtil.cpp b/fxbarcode/qrcode/BC_QRCoderMatrixUtil.cpp +index 2c6b05118..60fcd51ba 100644 +--- a/fxbarcode/qrcode/BC_QRCoderMatrixUtil.cpp ++++ b/fxbarcode/qrcode/BC_QRCoderMatrixUtil.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,12 +22,15 @@ + + #include "fxbarcode/qrcode/BC_QRCoderMatrixUtil.h" + +-#include "core/fxcrt/fx_memory.h" ++#include ++ + #include "fxbarcode/common/BC_CommonByteMatrix.h" + #include "fxbarcode/qrcode/BC_QRCoder.h" + #include "fxbarcode/qrcode/BC_QRCoderBitVector.h" + #include "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h" + #include "fxbarcode/qrcode/BC_QRCoderMaskUtil.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" + + namespace { + +@@ -104,7 +107,7 @@ bool EmbedDataBits(CBC_QRCoderBitVector* dataBits, + if (x == 6) + x -= 1; + +- while (y >= 0 && y < matrix->GetHeight()) { ++ while (y >= 0 && y < static_cast(matrix->GetHeight())) { + if (y == 6) { + y += direction; + continue; +@@ -121,7 +124,7 @@ bool EmbedDataBits(CBC_QRCoderBitVector* dataBits, + } else { + bit = 0; + } +- ASSERT(CBC_QRCoder::IsValidMaskPattern(maskPattern)); ++ DCHECK(CBC_QRCoder::IsValidMaskPattern(maskPattern)); + if (CBC_QRCoderMaskUtil::GetDataMaskBit(maskPattern, xx, y)) + bit ^= 0x01; + matrix->Set(xx, y, bit); +@@ -159,7 +162,7 @@ bool MakeTypeInfoBits(const CBC_QRCoderErrorCorrectionLevel* ecLevel, + if (!bits->XOR(&maskBits)) + return false; + +- ASSERT(bits->Size() == 15); ++ DCHECK_EQ(bits->Size(), 15); + return true; + } + +@@ -167,7 +170,7 @@ void MakeVersionInfoBits(int32_t version, CBC_QRCoderBitVector* bits) { + bits->AppendBits(version, 6); + int32_t bchCode = CalculateBCHCode(version, VERSION_INFO_POLY); + bits->AppendBits(bchCode, 12); +- ASSERT(bits->Size() == 18); ++ DCHECK_EQ(bits->Size(), 18); + } + + bool EmbedTypeInfo(const CBC_QRCoderErrorCorrectionLevel* ecLevel, +@@ -213,8 +216,8 @@ void MaybeEmbedVersionInfo(int32_t version, CBC_CommonByteMatrix* matrix) { + } + + bool EmbedTimingPatterns(CBC_CommonByteMatrix* matrix) { +- for (int32_t i = 8; i < matrix->GetWidth() - 8; i++) { +- int32_t bit = (i + 1) % 2; ++ for (size_t i = 8; i + 8 < matrix->GetWidth(); i++) { ++ const uint8_t bit = static_cast((i + 1) % 2); + if (!IsValidValue(matrix->Get(i, 6))) + return false; + +@@ -331,7 +334,7 @@ bool MaybeEmbedPositionAdjustmentPatterns(int32_t version, + return true; + + const size_t index = version - 2; +- if (index >= FX_ArraySize(kPositionAdjustmentPatternCoordinates)) ++ if (index >= std::size(kPositionAdjustmentPatternCoordinates)) + return false; + + const auto* coordinates = &kPositionAdjustmentPatternCoordinates[index][0]; +@@ -376,7 +379,7 @@ bool CBC_QRCoderMatrixUtil::BuildMatrix( + if (!dataBits || !matrix) + return false; + +- matrix->clear(0xff); ++ matrix->Fill(0xff); + + if (!EmbedBasicPatterns(version, matrix)) + return false; +diff --git a/fxbarcode/qrcode/BC_QRCoderMatrixUtil.h b/fxbarcode/qrcode/BC_QRCoderMatrixUtil.h +index 4bef5e1a3..bf498eb90 100644 +--- a/fxbarcode/qrcode/BC_QRCoderMatrixUtil.h ++++ b/fxbarcode/qrcode/BC_QRCoderMatrixUtil.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/qrcode/BC_QRCoderMode.cpp b/fxbarcode/qrcode/BC_QRCoderMode.cpp +index 8648f8c8a..2e1585d31 100644 +--- a/fxbarcode/qrcode/BC_QRCoderMode.cpp ++++ b/fxbarcode/qrcode/BC_QRCoderMode.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -25,17 +25,11 @@ + #include + + #include "core/fxcrt/fx_system.h" ++#include "third_party/base/check.h" + + CBC_QRCoderMode* CBC_QRCoderMode::sBYTE = nullptr; + CBC_QRCoderMode* CBC_QRCoderMode::sNUMERIC = nullptr; + CBC_QRCoderMode* CBC_QRCoderMode::sALPHANUMERIC = nullptr; +-CBC_QRCoderMode* CBC_QRCoderMode::sKANJI = nullptr; +-CBC_QRCoderMode* CBC_QRCoderMode::sECI = nullptr; +-CBC_QRCoderMode* CBC_QRCoderMode::sGBK = nullptr; +-CBC_QRCoderMode* CBC_QRCoderMode::sTERMINATOR = nullptr; +-CBC_QRCoderMode* CBC_QRCoderMode::sFNC1_FIRST_POSITION = nullptr; +-CBC_QRCoderMode* CBC_QRCoderMode::sFNC1_SECOND_POSITION = nullptr; +-CBC_QRCoderMode* CBC_QRCoderMode::sSTRUCTURED_APPEND = nullptr; + + CBC_QRCoderMode::CBC_QRCoderMode(std::vector charCountBits, + int32_t bits) +@@ -46,14 +40,7 @@ CBC_QRCoderMode::~CBC_QRCoderMode() = default; + void CBC_QRCoderMode::Initialize() { + sBYTE = new CBC_QRCoderMode({8, 16, 16}, 0x4); + sALPHANUMERIC = new CBC_QRCoderMode({9, 11, 13}, 0x2); +- sECI = new CBC_QRCoderMode(std::vector(), 0x7); +- sKANJI = new CBC_QRCoderMode({8, 10, 12}, 0x8); + sNUMERIC = new CBC_QRCoderMode({10, 12, 14}, 0x1); +- sGBK = new CBC_QRCoderMode({8, 10, 12}, 0x0D); +- sTERMINATOR = new CBC_QRCoderMode(std::vector(), 0x00); +- sFNC1_FIRST_POSITION = new CBC_QRCoderMode(std::vector(), 0x05); +- sFNC1_SECOND_POSITION = new CBC_QRCoderMode(std::vector(), 0x09); +- sSTRUCTURED_APPEND = new CBC_QRCoderMode(std::vector(), 0x03); + } + + void CBC_QRCoderMode::Finalize() { +@@ -61,22 +48,8 @@ void CBC_QRCoderMode::Finalize() { + sBYTE = nullptr; + delete sALPHANUMERIC; + sALPHANUMERIC = nullptr; +- delete sECI; +- sECI = nullptr; +- delete sKANJI; +- sKANJI = nullptr; + delete sNUMERIC; + sNUMERIC = nullptr; +- delete sGBK; +- sGBK = nullptr; +- delete sTERMINATOR; +- sTERMINATOR = nullptr; +- delete sFNC1_FIRST_POSITION; +- sFNC1_FIRST_POSITION = nullptr; +- delete sFNC1_SECOND_POSITION; +- sFNC1_SECOND_POSITION = nullptr; +- delete sSTRUCTURED_APPEND; +- sSTRUCTURED_APPEND = nullptr; + } + + int32_t CBC_QRCoderMode::GetBits() const { +@@ -96,6 +69,6 @@ int32_t CBC_QRCoderMode::GetCharacterCountBits(int32_t number) const { + offset = 2; + + int32_t result = m_characterCountBitsForVersions[offset]; +- ASSERT(result != 0); ++ DCHECK(result != 0); + return result; + } +diff --git a/fxbarcode/qrcode/BC_QRCoderMode.h b/fxbarcode/qrcode/BC_QRCoderMode.h +index b61d751a6..0e12d7d95 100644 +--- a/fxbarcode/qrcode/BC_QRCoderMode.h ++++ b/fxbarcode/qrcode/BC_QRCoderMode.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -24,13 +24,6 @@ class CBC_QRCoderMode final { + static CBC_QRCoderMode* sBYTE; + static CBC_QRCoderMode* sNUMERIC; + static CBC_QRCoderMode* sALPHANUMERIC; +- static CBC_QRCoderMode* sKANJI; +- static CBC_QRCoderMode* sECI; +- static CBC_QRCoderMode* sGBK; +- static CBC_QRCoderMode* sTERMINATOR; +- static CBC_QRCoderMode* sFNC1_FIRST_POSITION; +- static CBC_QRCoderMode* sFNC1_SECOND_POSITION; +- static CBC_QRCoderMode* sSTRUCTURED_APPEND; + + private: + CBC_QRCoderMode(std::vector charCountBits, int32_t bits); +diff --git a/fxbarcode/qrcode/BC_QRCoderVersion.cpp b/fxbarcode/qrcode/BC_QRCoderVersion.cpp +index 9263ee718..aa7c2c88e 100644 +--- a/fxbarcode/qrcode/BC_QRCoderVersion.cpp ++++ b/fxbarcode/qrcode/BC_QRCoderVersion.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -29,7 +29,6 @@ + #include "fxbarcode/qrcode/BC_QRCoderBitVector.h" + #include "fxbarcode/qrcode/BC_QRCoderECBlocksData.h" + #include "fxbarcode/qrcode/BC_QRCoderErrorCorrectionLevel.h" +-#include "third_party/base/ptr_util.h" + + namespace { + +@@ -40,10 +39,10 @@ std::vector>* g_VERSION = nullptr; + CBC_QRCoderVersion::CBC_QRCoderVersion(int32_t versionNumber, + const CBC_QRCoderECBlockData data[4]) + : m_versionNumber(versionNumber) { +- m_ecBlocksArray[0] = pdfium::MakeUnique(data[0]); +- m_ecBlocksArray[1] = pdfium::MakeUnique(data[1]); +- m_ecBlocksArray[2] = pdfium::MakeUnique(data[2]); +- m_ecBlocksArray[3] = pdfium::MakeUnique(data[3]); ++ m_ecBlocksArray[0] = std::make_unique(data[0]); ++ m_ecBlocksArray[1] = std::make_unique(data[1]); ++ m_ecBlocksArray[2] = std::make_unique(data[2]); ++ m_ecBlocksArray[3] = std::make_unique(data[3]); + m_totalCodeWords = m_ecBlocksArray[0]->GetTotalDataCodeWords(); + } + +@@ -66,7 +65,7 @@ const CBC_QRCoderVersion* CBC_QRCoderVersion::GetVersionForNumber( + if (g_VERSION->empty()) { + for (int i = 0; i < kMaxVersion; ++i) { + g_VERSION->push_back( +- pdfium::MakeUnique(i + 1, g_ECBData[i])); ++ std::make_unique(i + 1, fxbarcode::kECBData[i])); + } + } + if (versionNumber < 1 || versionNumber > kMaxVersion) +diff --git a/fxbarcode/qrcode/BC_QRCoderVersion.h b/fxbarcode/qrcode/BC_QRCoderVersion.h +index b94a9e77b..76ad09a62 100644 +--- a/fxbarcode/qrcode/BC_QRCoderVersion.h ++++ b/fxbarcode/qrcode/BC_QRCoderVersion.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxbarcode/utils.h b/fxbarcode/utils.h +deleted file mode 100644 +index fe3caa6c1..000000000 +--- a/fxbarcode/utils.h ++++ /dev/null +@@ -1,25 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef FXBARCODE_UTILS_H_ +-#define FXBARCODE_UTILS_H_ +- +-enum BCFORMAT { +- BCFORMAT_UNSPECIFY = -1, +- BCFORMAT_CODABAR, +- BCFORMAT_CODE_39, +- BCFORMAT_CODE_128, +- BCFORMAT_CODE_128B, +- BCFORMAT_CODE_128C, +- BCFORMAT_EAN_8, +- BCFORMAT_UPC_A, +- BCFORMAT_EAN_13, +- BCFORMAT_PDF_417, +- BCFORMAT_DATAMATRIX, +- BCFORMAT_QR_CODE +-}; +- +-#endif // FXBARCODE_UTILS_H_ +diff --git a/fxjs/Android.bp b/fxjs/Android.bp +index 2dc3861ab..a3a03d932 100644 +--- a/fxjs/Android.bp ++++ b/fxjs/Android.bp +@@ -13,11 +13,8 @@ cc_library_static { + + visibility: ["//external/pdfium:__subpackages__"], + +- header_libs: [ +- "libpdfium-constants", +- ], +- + static_libs: [ ++ "libpdfium-constants", + "libpdfium-fdrm", + "libpdfium-page", + "libpdfium-parser", +diff --git a/fxjs/BUILD.gn b/fxjs/BUILD.gn +index 2fb819cf7..e3f62ff9c 100644 +--- a/fxjs/BUILD.gn ++++ b/fxjs/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -15,8 +15,9 @@ source_set("fxjs") { + "ijs_runtime.cpp", + "ijs_runtime.h", + ] +- configs += [ "../:pdfium_core_config" ] ++ configs += [ "../:pdfium_strict_config" ] + deps = [ "../core/fxcrt" ] ++ public_deps = [] + visibility = [ "../*" ] + + if (pdf_enable_v8) { +@@ -27,6 +28,8 @@ source_set("fxjs") { + "cfx_keyvalue.h", + "cfx_v8.cpp", + "cfx_v8.h", ++ "cfx_v8_array_buffer_allocator.cpp", ++ "cfx_v8_array_buffer_allocator.h", + "cfxjs_engine.cpp", + "cfxjs_engine.h", + "cjs_annot.cpp", +@@ -49,8 +52,6 @@ source_set("fxjs") { + "cjs_event.h", + "cjs_event_context.cpp", + "cjs_event_context.h", +- "cjs_eventrecorder.cpp", +- "cjs_eventrecorder.h", + "cjs_field.cpp", + "cjs_field.h", + "cjs_font.cpp", +@@ -89,6 +90,8 @@ source_set("fxjs") { + "cjs_zoomtype.h", + "fx_date_helpers.cpp", + "fx_date_helpers.h", ++ "fxv8.cpp", ++ "fxv8.h", + "global_timer.cpp", + "global_timer.h", + "js_define.cpp", +@@ -108,12 +111,13 @@ source_set("fxjs") { + "//v8:v8_libplatform", + ] + configs += [ "//v8:external_startup_data" ] +- public_deps = [ "//v8" ] ++ public_deps += [ ++ "../core/fxcrt", ++ "//v8", ++ ] + + if (pdf_enable_xfa) { + sources += [ +- "xfa/cfxjse_arguments.cpp", +- "xfa/cfxjse_arguments.h", + "xfa/cfxjse_class.cpp", + "xfa/cfxjse_class.h", + "xfa/cfxjse_context.cpp", +@@ -124,6 +128,10 @@ source_set("fxjs") { + "xfa/cfxjse_formcalc_context.h", + "xfa/cfxjse_isolatetracker.cpp", + "xfa/cfxjse_isolatetracker.h", ++ "xfa/cfxjse_mapmodule.cpp", ++ "xfa/cfxjse_mapmodule.h", ++ "xfa/cfxjse_nodehelper.cpp", ++ "xfa/cfxjse_nodehelper.h", + "xfa/cfxjse_resolveprocessor.cpp", + "xfa/cfxjse_resolveprocessor.h", + "xfa/cfxjse_runtimedata.cpp", +@@ -203,27 +211,65 @@ source_set("fxjs") { + "xfa/jse_define.h", + ] + deps += [ +- "../xfa/fgas", +- "../xfa/fxfa/fm2js", ++ ":gc", ++ "../xfa/fgas/crt", ++ "../xfa/fxfa/formcalc", + ] + } + } + } + ++if (pdf_enable_v8) { ++ if (pdf_enable_xfa) { ++ source_set("gc") { ++ sources = [ ++ "gc/container_trace.h", ++ "gc/gced_tree_node.h", ++ "gc/gced_tree_node_mixin.h", ++ "gc/heap.cpp", ++ "gc/heap.h", ++ ] ++ configs += [ "../:pdfium_strict_config" ] ++ deps = [ ++ "../core/fxcrt", ++ "//v8:v8_libplatform", ++ ] ++ public_deps = [ "//v8:cppgc" ] ++ } ++ } ++} ++ + if (pdf_enable_v8) { + pdfium_unittest_source_set("unittests") { + sources = [ + "cfx_globaldata_unittest.cpp", + "cfx_v8_unittest.cpp", +- "cfx_v8_unittest.h", + "cfxjs_engine_unittest.cpp", + "cjs_publicmethods_unittest.cpp", + "cjs_util_unittest.cpp", + "fx_date_helpers_unittest.cpp", + ] + configs = [ "//v8:external_startup_data" ] +- deps = [ ":fxjs" ] ++ deps = [ ++ ":fxjs", ++ "../core/fxcrt:unit_test_support", ++ ] + pdfium_root_dir = "../" ++ if (pdf_enable_xfa) { ++ sources += [ ++ "gc/container_trace_unittest.cpp", ++ "gc/gced_tree_node_mixin_unittest.cpp", ++ "gc/gced_tree_node_unittest.cpp", ++ "gc/heap_unittest.cpp", ++ "gc/move_unittest.cpp", ++ "xfa/cfxjse_formcalc_context_unittest.cpp", ++ "xfa/cfxjse_mapmodule_unittest.cpp", ++ ] ++ deps += [ ++ ":gc", ++ "../xfa/fxfa/parser", ++ ] ++ } + } + + pdfium_embeddertest_source_set("embeddertests") { +@@ -237,7 +283,6 @@ if (pdf_enable_v8) { + "../fpdfsdk", + ] + pdfium_root_dir = "../" +- + if (pdf_enable_xfa) { + sources += [ + "xfa/cfxjse_app_embeddertest.cpp", +@@ -245,8 +290,12 @@ if (pdf_enable_v8) { + "xfa/cfxjse_value_embeddertest.cpp", + "xfa/cjx_hostpseudomodel_embeddertest.cpp", + "xfa/cjx_list_embeddertest.cpp", ++ "xfa/cjx_object_embeddertest.cpp", ++ ] ++ deps += [ ++ ":gc", ++ "../xfa/fxfa", + ] +- deps += [ "../xfa/fxfa" ] + } + } + } +diff --git a/fxjs/README b/fxjs/README +index a1cfe322b..59b55a2a5 100644 +--- a/fxjs/README ++++ b/fxjs/README +@@ -20,20 +20,20 @@ To distinguish these cases, we use two internal slots for all bound + objects, regardless of the FXJS/FXJSE distinction. Slot 0 is the + tag and contains either: + kPerObjectDataTag for FXJS objects, or +- g_FXJSEHostObjectTag for FXJSE Host objects, or +- g_FXJSEProxyObjectTag for a global proxy object under FXJSE, or ++ kFXJSEHostObjectTag for FXJSE Host objects, or ++ kFXJSEProxyObjectTag for a global proxy object under FXJSE, or + One of 4 specific FXJSE_CLASS_DESCRIPTOR globals for FXJSE classes: +- GlobalClassDescriptor +- NormalClassDescriptor +- VariablesClassDescriptor +- formcalc_fm2js_descriptor ++ kGlobalClassDescriptor ++ kNormalClassDescriptor ++ kVariablesClassDescriptor ++ kFormCalcDescriptor + + Slot 1's contents are determined by these tags: + kPerObjectDataTag means an aligned pointer to CFXJS_PerObjectData. +- g_FXJSEHostObjectTag means an aligned pointer to CFXJSE_HostObject. +- g_FXJSEProxyObjectTag means nullptr, and to check the prototype instead. ++ kFXJSEHostObjectTag means an aligned pointer to CFXJSE_HostObject. ++ kFXJSEProxyObjectTag means nullptr, and to check the prototype instead. + A FXJSE_CLASS_DESCRIPTOR pointer means to expect an actual v8 function +- object (or a string naming that function), and not an aligned pointer. ++ object (or a string naming that function), and not an aligned pointer. + + Because PDFium uses V8 for various unrelated purposes, there may be up to + four v8::Contexts (JS Global Objects) associated with each document. One is +diff --git a/fxjs/cfx_globaldata.cpp b/fxjs/cfx_globaldata.cpp +index 9131f02cc..59b43c1d6 100644 +--- a/fxjs/cfx_globaldata.cpp ++++ b/fxjs/cfx_globaldata.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,8 +9,8 @@ + #include + + #include "core/fdrm/fx_crypt.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "core/fxcrt/stl_util.h" ++#include "third_party/base/numerics/safe_conversions.h" + + namespace { + +@@ -40,44 +40,41 @@ bool TrimPropName(ByteString* sPropName) { + + void MakeNameTypeString(const ByteString& name, + CFX_Value::DataType eType, +- CFX_BinaryBuf* result) { +- uint32_t dwNameLen = (uint32_t)name.GetLength(); +- result->AppendBlock(&dwNameLen, sizeof(uint32_t)); ++ BinaryBuffer* result) { ++ uint32_t dwNameLen = pdfium::base::checked_cast(name.GetLength()); ++ result->AppendUint32(dwNameLen); + result->AppendString(name); +- +- uint16_t wType = static_cast(eType); +- result->AppendBlock(&wType, sizeof(uint16_t)); ++ result->AppendUint16(static_cast(eType)); + } + + bool MakeByteString(const ByteString& name, + const CFX_KeyValue& pData, +- CFX_BinaryBuf* result) { ++ BinaryBuffer* result) { + switch (pData.nType) { +- case CFX_Value::DataType::NUMBER: { ++ case CFX_Value::DataType::kNumber: { + MakeNameTypeString(name, pData.nType, result); +- double dData = pData.dData; +- result->AppendBlock(&dData, sizeof(double)); ++ result->AppendDouble(pData.dData); + return true; + } +- case CFX_Value::DataType::BOOLEAN: { ++ case CFX_Value::DataType::kBoolean: { + MakeNameTypeString(name, pData.nType, result); +- uint16_t wData = static_cast(pData.bData); +- result->AppendBlock(&wData, sizeof(uint16_t)); ++ result->AppendUint16(static_cast(pData.bData)); + return true; + } +- case CFX_Value::DataType::STRING: { ++ case CFX_Value::DataType::kString: { + MakeNameTypeString(name, pData.nType, result); +- uint32_t dwDataLen = (uint32_t)pData.sData.GetLength(); +- result->AppendBlock(&dwDataLen, sizeof(uint32_t)); ++ uint32_t dwDataLen = ++ pdfium::base::checked_cast(pData.sData.GetLength()); ++ result->AppendUint32(dwDataLen); + result->AppendString(pData.sData); + return true; + } +- case CFX_Value::DataType::NULLOBJ: { ++ case CFX_Value::DataType::kNull: { + MakeNameTypeString(name, pData.nType, result); + return true; + } + // Arrays don't get persisted per JS spec page 484. +- case CFX_Value::DataType::OBJECT: ++ case CFX_Value::DataType::kObject: + default: + break; + } +@@ -135,13 +132,13 @@ void CFX_GlobalData::SetGlobalVariableNumber(ByteString sPropName, + + CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName); + if (pData) { +- pData->data.nType = CFX_Value::DataType::NUMBER; ++ pData->data.nType = CFX_Value::DataType::kNumber; + pData->data.dData = dData; + return; + } +- auto pNewData = pdfium::MakeUnique(); ++ auto pNewData = std::make_unique(); + pNewData->data.sKey = std::move(sPropName); +- pNewData->data.nType = CFX_Value::DataType::NUMBER; ++ pNewData->data.nType = CFX_Value::DataType::kNumber; + pNewData->data.dData = dData; + m_arrayGlobalData.push_back(std::move(pNewData)); + } +@@ -153,13 +150,13 @@ void CFX_GlobalData::SetGlobalVariableBoolean(ByteString sPropName, + + CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName); + if (pData) { +- pData->data.nType = CFX_Value::DataType::BOOLEAN; ++ pData->data.nType = CFX_Value::DataType::kBoolean; + pData->data.bData = bData; + return; + } +- auto pNewData = pdfium::MakeUnique(); ++ auto pNewData = std::make_unique(); + pNewData->data.sKey = std::move(sPropName); +- pNewData->data.nType = CFX_Value::DataType::BOOLEAN; ++ pNewData->data.nType = CFX_Value::DataType::kBoolean; + pNewData->data.bData = bData; + m_arrayGlobalData.push_back(std::move(pNewData)); + } +@@ -171,13 +168,13 @@ void CFX_GlobalData::SetGlobalVariableString(ByteString sPropName, + + CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName); + if (pData) { +- pData->data.nType = CFX_Value::DataType::STRING; ++ pData->data.nType = CFX_Value::DataType::kString; + pData->data.sData = sData; + return; + } +- auto pNewData = pdfium::MakeUnique(); ++ auto pNewData = std::make_unique(); + pNewData->data.sKey = std::move(sPropName); +- pNewData->data.nType = CFX_Value::DataType::STRING; ++ pNewData->data.nType = CFX_Value::DataType::kString; + pNewData->data.sData = sData; + m_arrayGlobalData.push_back(std::move(pNewData)); + } +@@ -190,13 +187,13 @@ void CFX_GlobalData::SetGlobalVariableObject( + + CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName); + if (pData) { +- pData->data.nType = CFX_Value::DataType::OBJECT; ++ pData->data.nType = CFX_Value::DataType::kObject; + pData->data.objData = std::move(array); + return; + } +- auto pNewData = pdfium::MakeUnique(); ++ auto pNewData = std::make_unique(); + pNewData->data.sKey = std::move(sPropName); +- pNewData->data.nType = CFX_Value::DataType::OBJECT; ++ pNewData->data.nType = CFX_Value::DataType::kObject; + pNewData->data.objData = std::move(array); + m_arrayGlobalData.push_back(std::move(pNewData)); + } +@@ -207,12 +204,12 @@ void CFX_GlobalData::SetGlobalVariableNull(ByteString sPropName) { + + CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName); + if (pData) { +- pData->data.nType = CFX_Value::DataType::NULLOBJ; ++ pData->data.nType = CFX_Value::DataType::kNull; + return; + } +- auto pNewData = pdfium::MakeUnique(); ++ auto pNewData = std::make_unique(); + pNewData->data.sKey = std::move(sPropName); +- pNewData->data.nType = CFX_Value::DataType::NULLOBJ; ++ pNewData->data.nType = CFX_Value::DataType::kNull; + m_arrayGlobalData.push_back(std::move(pNewData)); + } + +@@ -242,7 +239,7 @@ bool CFX_GlobalData::DeleteGlobalVariable(ByteString sPropName) { + } + + int32_t CFX_GlobalData::GetSize() const { +- return pdfium::CollectionSize(m_arrayGlobalData); ++ return fxcrt::CollectionSize(m_arrayGlobalData); + } + + CFX_GlobalData::Element* CFX_GlobalData::GetAt(int index) { +@@ -258,7 +255,7 @@ bool CFX_GlobalData::LoadGlobalPersistentVariables() { + bool ret; + { + // Span can't outlive call to BufferDone(). +- Optional> buffer = m_pDelegate->LoadBuffer(); ++ absl::optional> buffer = m_pDelegate->LoadBuffer(); + if (!buffer.has_value() || buffer.value().empty()) + return false; + +@@ -312,7 +309,7 @@ bool CFX_GlobalData::LoadGlobalPersistentVariablesFromBuffer( + p += sizeof(uint16_t); + + switch (wDataType) { +- case CFX_Value::DataType::NUMBER: { ++ case CFX_Value::DataType::kNumber: { + double dData = 0; + switch (wVersion) { + case 1: { +@@ -328,13 +325,13 @@ bool CFX_GlobalData::LoadGlobalPersistentVariablesFromBuffer( + SetGlobalVariableNumber(sEntry, dData); + SetGlobalVariablePersistent(sEntry, true); + } break; +- case CFX_Value::DataType::BOOLEAN: { ++ case CFX_Value::DataType::kBoolean: { + uint16_t wData = *((uint16_t*)p); + p += sizeof(uint16_t); + SetGlobalVariableBoolean(sEntry, (bool)(wData == 1)); + SetGlobalVariablePersistent(sEntry, true); + } break; +- case CFX_Value::DataType::STRING: { ++ case CFX_Value::DataType::kString: { + uint32_t dwLength = *((uint32_t*)p); + p += sizeof(uint32_t); + if (p + dwLength > buffer.end()) +@@ -344,11 +341,11 @@ bool CFX_GlobalData::LoadGlobalPersistentVariablesFromBuffer( + SetGlobalVariablePersistent(sEntry, true); + p += sizeof(char) * dwLength; + } break; +- case CFX_Value::DataType::NULLOBJ: { ++ case CFX_Value::DataType::kNull: { + SetGlobalVariableNull(sEntry); + SetGlobalVariablePersistent(sEntry, true); + } break; +- case CFX_Value::DataType::OBJECT: ++ case CFX_Value::DataType::kObject: + default: + // Arrays aren't allowed in these buffers, nor are unrecoginzed tags. + return false; +@@ -362,12 +359,12 @@ bool CFX_GlobalData::SaveGlobalPersisitentVariables() { + return false; + + uint32_t nCount = 0; +- CFX_BinaryBuf sData; ++ BinaryBuffer sData; + for (const auto& pElement : m_arrayGlobalData) { + if (!pElement->bPersistent) + continue; + +- CFX_BinaryBuf sElement; ++ BinaryBuffer sElement; + if (!MakeByteString(pElement->data.sKey, pElement->data, &sElement)) + continue; + +@@ -378,20 +375,17 @@ bool CFX_GlobalData::SaveGlobalPersisitentVariables() { + nCount++; + } + +- CFX_BinaryBuf sFile; +- uint16_t wType = kMagic; +- uint16_t wVersion = 2; +- sFile.AppendBlock(&wType, sizeof(uint16_t)); +- sFile.AppendBlock(&wVersion, sizeof(uint16_t)); +- sFile.AppendBlock(&nCount, sizeof(uint32_t)); ++ BinaryBuffer sFile; ++ sFile.AppendUint16(kMagic); ++ sFile.AppendUint16(kMaxVersion); ++ sFile.AppendUint32(nCount); + +- uint32_t dwSize = sData.GetSize(); +- sFile.AppendBlock(&dwSize, sizeof(uint32_t)); ++ uint32_t dwSize = pdfium::base::checked_cast(sData.GetSize()); ++ sFile.AppendUint32(dwSize); + sFile.AppendSpan(sData.GetSpan()); + +- CRYPT_ArcFourCryptBlock(sFile.GetSpan(), kRC4KEY); +- +- return m_pDelegate->StoreBuffer({sFile.GetBuffer(), sFile.GetSize()}); ++ CRYPT_ArcFourCryptBlock(sFile.GetMutableSpan(), kRC4KEY); ++ return m_pDelegate->StoreBuffer(sFile.GetSpan()); + } + + CFX_GlobalData::Element::Element() = default; +diff --git a/fxjs/cfx_globaldata.h b/fxjs/cfx_globaldata.h +index 421a3b488..6b9426593 100644 +--- a/fxjs/cfx_globaldata.h ++++ b/fxjs/cfx_globaldata.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,22 +10,20 @@ + #include + #include + +-#include "core/fxcrt/cfx_binarybuf.h" ++#include "core/fxcrt/binary_buffer.h" + #include "core/fxcrt/unowned_ptr.h" + #include "fxjs/cfx_keyvalue.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + #include "third_party/base/span.h" + +-class CPDFSDK_FormFillEnvironment; +- + class CFX_GlobalData { + public: + class Delegate { + public: +- virtual ~Delegate() {} ++ virtual ~Delegate() = default; + + virtual bool StoreBuffer(pdfium::span pBuffer) = 0; +- virtual Optional> LoadBuffer() = 0; ++ virtual absl::optional> LoadBuffer() = 0; + virtual void BufferDone() = 0; + }; + +@@ -35,7 +33,7 @@ class CFX_GlobalData { + ~Element(); + + CFX_KeyValue data; +- bool bPersistent; ++ bool bPersistent = false; + }; + + static CFX_GlobalData* GetRetainedInstance(Delegate* pDelegate); +@@ -66,16 +64,8 @@ class CFX_GlobalData { + bool LoadGlobalPersistentVariables(); + bool LoadGlobalPersistentVariablesFromBuffer(pdfium::span buffer); + bool SaveGlobalPersisitentVariables(); +- + iterator FindGlobalVariable(const ByteString& sPropname); + +- void LoadFileBuffer(const wchar_t* sFilePath, +- uint8_t*& pBuffer, +- int32_t& nLength); +- void WriteFileBuffer(const wchar_t* sFilePath, +- const char* pBuffer, +- int32_t nLength); +- + size_t m_RefCount = 0; + UnownedPtr const m_pDelegate; + std::vector> m_arrayGlobalData; +diff --git a/fxjs/cfx_globaldata_unittest.cpp b/fxjs/cfx_globaldata_unittest.cpp +index 3eb0ac45b..812170183 100644 +--- a/fxjs/cfx_globaldata_unittest.cpp ++++ b/fxjs/cfx_globaldata_unittest.cpp +@@ -1,12 +1,15 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fxjs/cfx_globaldata.h" + ++#include ++ + #include + #include + ++#include "core/fxcrt/data_vector.h" + #include "testing/gtest/include/gtest/gtest.h" + + namespace { +@@ -14,39 +17,40 @@ namespace { + class TestDelegate : public CFX_GlobalData::Delegate { + public: + TestDelegate() = default; +- ~TestDelegate() override {} ++ ~TestDelegate() override = default; + + bool StoreBuffer(pdfium::span buffer) override { +- last_buffer_ = std::vector(buffer.begin(), buffer.end()); ++ last_buffer_ = DataVector(buffer.begin(), buffer.end()); + return true; + } +- Optional> LoadBuffer() override { ++ absl::optional> LoadBuffer() override { + return pdfium::span(last_buffer_); + } + void BufferDone() override { +- last_buffer_ = std::vector(); // Catch misuse after done. ++ // Catch misuse after done. ++ last_buffer_ = DataVector(); + } + +- std::vector last_buffer_; ++ DataVector last_buffer_; + }; + + } // namespace + + TEST(CFXGlobalData, GetSafety) { + CFX_GlobalData* pInstance = CFX_GlobalData::GetRetainedInstance(nullptr); +- EXPECT_EQ(nullptr, pInstance->GetGlobalVariable("nonesuch")); +- EXPECT_EQ(nullptr, pInstance->GetAt(-1)); +- EXPECT_EQ(nullptr, pInstance->GetAt(0)); +- EXPECT_EQ(nullptr, pInstance->GetAt(1)); ++ EXPECT_FALSE(pInstance->GetGlobalVariable("nonesuch")); ++ EXPECT_FALSE(pInstance->GetAt(-1)); ++ EXPECT_FALSE(pInstance->GetAt(0)); ++ EXPECT_FALSE(pInstance->GetAt(1)); + + pInstance->SetGlobalVariableNumber("double", 2.0); + pInstance->SetGlobalVariableString("string", "clams"); + +- EXPECT_EQ(nullptr, pInstance->GetGlobalVariable("nonesuch")); +- EXPECT_EQ(nullptr, pInstance->GetAt(-1)); ++ EXPECT_FALSE(pInstance->GetGlobalVariable("nonesuch")); ++ EXPECT_FALSE(pInstance->GetAt(-1)); + EXPECT_EQ(pInstance->GetGlobalVariable("double"), pInstance->GetAt(0)); + EXPECT_EQ(pInstance->GetGlobalVariable("string"), pInstance->GetAt(1)); +- EXPECT_EQ(nullptr, pInstance->GetAt(2)); ++ EXPECT_FALSE(pInstance->GetAt(2)); + + ASSERT_TRUE(pInstance->Release()); + } +@@ -71,25 +75,25 @@ TEST(CFXGlobalData, StoreReload) { + auto* element = pInstance->GetAt(0); + ASSERT_TRUE(element); + EXPECT_EQ("double", element->data.sKey); +- EXPECT_EQ(CFX_Value::DataType::NUMBER, element->data.nType); ++ EXPECT_EQ(CFX_Value::DataType::kNumber, element->data.nType); + EXPECT_EQ(2.0, element->data.dData); + + element = pInstance->GetAt(1); + ASSERT_TRUE(element); + EXPECT_EQ("string", element->data.sKey); +- EXPECT_EQ(CFX_Value::DataType::STRING, element->data.nType); ++ EXPECT_EQ(CFX_Value::DataType::kString, element->data.nType); + EXPECT_EQ("clams", element->data.sData); + + element = pInstance->GetAt(2); + ASSERT_TRUE(element); + EXPECT_EQ("boolean", element->data.sKey); +- EXPECT_EQ(CFX_Value::DataType::BOOLEAN, element->data.nType); ++ EXPECT_EQ(CFX_Value::DataType::kBoolean, element->data.nType); + EXPECT_EQ(true, element->data.bData); + + element = pInstance->GetAt(3); + ASSERT_TRUE(element); + EXPECT_EQ("null", element->data.sKey); +- EXPECT_EQ(CFX_Value::DataType::NULLOBJ, element->data.nType); ++ EXPECT_EQ(CFX_Value::DataType::kNull, element->data.nType); + + // Arrays don't get persisted. + element = pInstance->GetAt(4); +@@ -113,25 +117,25 @@ TEST(CFXGlobalData, ResetValues) { + auto* element = pInstance->GetAt(0); + ASSERT_TRUE(element); + EXPECT_EQ("double", element->data.sKey); +- EXPECT_EQ(CFX_Value::DataType::NUMBER, element->data.nType); ++ EXPECT_EQ(CFX_Value::DataType::kNumber, element->data.nType); + EXPECT_EQ(2.0, element->data.dData); + + element = pInstance->GetAt(1); + ASSERT_TRUE(element); + EXPECT_EQ("string", element->data.sKey); +- EXPECT_EQ(CFX_Value::DataType::STRING, element->data.nType); ++ EXPECT_EQ(CFX_Value::DataType::kString, element->data.nType); + EXPECT_EQ("clams", element->data.sData); + + element = pInstance->GetAt(2); + ASSERT_TRUE(element); + EXPECT_EQ("boolean", element->data.sKey); +- EXPECT_EQ(CFX_Value::DataType::BOOLEAN, element->data.nType); ++ EXPECT_EQ(CFX_Value::DataType::kBoolean, element->data.nType); + EXPECT_EQ(true, element->data.bData); + + element = pInstance->GetAt(3); + ASSERT_TRUE(element); + EXPECT_EQ("null", element->data.sKey); +- EXPECT_EQ(CFX_Value::DataType::NULLOBJ, element->data.nType); ++ EXPECT_EQ(CFX_Value::DataType::kNull, element->data.nType); + + ASSERT_TRUE(pInstance->Release()); + } +diff --git a/fxjs/cfx_keyvalue.cpp b/fxjs/cfx_keyvalue.cpp +index d1c7fae7b..a0ee7c2eb 100644 +--- a/fxjs/cfx_keyvalue.cpp ++++ b/fxjs/cfx_keyvalue.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxjs/cfx_keyvalue.h b/fxjs/cfx_keyvalue.h +index b66dc120d..a6ff52a2e 100644 +--- a/fxjs/cfx_keyvalue.h ++++ b/fxjs/cfx_keyvalue.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,26 +10,26 @@ + #include + #include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/bytestring.h" + + class CFX_KeyValue; + + class CFX_Value { + public: + enum class DataType : uint8_t { +- NUMBER = 0, +- BOOLEAN, +- STRING, +- OBJECT, +- NULLOBJ ++ kNumber = 0, ++ kBoolean, ++ kString, ++ kObject, ++ kNull + }; + + CFX_Value(); + ~CFX_Value(); + +- DataType nType = DataType::NULLOBJ; +- bool bData; +- double dData; ++ DataType nType = DataType::kNull; ++ bool bData = false; ++ double dData = 0.0; + ByteString sData; + std::vector> objData; + }; +diff --git a/fxjs/cfx_v8.cpp b/fxjs/cfx_v8.cpp +index 72bf36131..422e28a3d 100644 +--- a/fxjs/cfx_v8.cpp ++++ b/fxjs/cfx_v8.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,8 +6,8 @@ + + #include "fxjs/cfx_v8.h" + +-#include "core/fxcrt/fx_memory.h" +-#include "third_party/base/allocator/partition_allocator/partition_alloc.h" ++#include "fxjs/fxv8.h" ++#include "v8/include/v8-isolate.h" + + CFX_V8::CFX_V8(v8::Isolate* isolate) : m_pIsolate(isolate) {} + +@@ -16,103 +16,68 @@ CFX_V8::~CFX_V8() = default; + v8::Local CFX_V8::GetObjectProperty( + v8::Local pObj, + ByteStringView bsUTF8PropertyName) { +- if (pObj.IsEmpty()) +- return v8::Local(); +- v8::Local val; +- if (!pObj->Get(m_pIsolate->GetCurrentContext(), NewString(bsUTF8PropertyName)) +- .ToLocal(&val)) +- return v8::Local(); +- return val; ++ return fxv8::ReentrantGetObjectPropertyHelper(GetIsolate(), pObj, ++ bsUTF8PropertyName); + } + + std::vector CFX_V8::GetObjectPropertyNames( + v8::Local pObj) { +- if (pObj.IsEmpty()) +- return std::vector(); +- +- v8::Local val; +- v8::Local context = m_pIsolate->GetCurrentContext(); +- if (!pObj->GetPropertyNames(context).ToLocal(&val)) +- return std::vector(); +- +- std::vector result; +- for (uint32_t i = 0; i < val->Length(); ++i) { +- result.push_back(ToWideString(val->Get(context, i).ToLocalChecked())); +- } +- +- return result; ++ return fxv8::ReentrantGetObjectPropertyNamesHelper(GetIsolate(), pObj); + } + +-bool CFX_V8::PutObjectProperty(v8::Local pObj, ++void CFX_V8::PutObjectProperty(v8::Local pObj, + ByteStringView bsUTF8PropertyName, + v8::Local pPut) { +- ASSERT(!pPut.IsEmpty()); +- if (pObj.IsEmpty()) +- return false; +- +- v8::Local name = NewString(bsUTF8PropertyName); +- return pObj->Set(m_pIsolate->GetCurrentContext(), name, pPut).IsJust(); ++ fxv8::ReentrantPutObjectPropertyHelper(GetIsolate(), pObj, bsUTF8PropertyName, ++ pPut); + } + + void CFX_V8::DisposeIsolate() { + if (m_pIsolate) +- m_pIsolate.Release()->Dispose(); ++ m_pIsolate.ExtractAsDangling()->Dispose(); + } + + v8::Local CFX_V8::NewArray() { +- return v8::Array::New(GetIsolate()); ++ return fxv8::NewArrayHelper(GetIsolate()); + } + + v8::Local CFX_V8::NewObject() { +- return v8::Object::New(GetIsolate()); ++ return fxv8::NewObjectHelper(GetIsolate()); + } + +-bool CFX_V8::PutArrayElement(v8::Local pArray, +- unsigned index, ++void CFX_V8::PutArrayElement(v8::Local pArray, ++ size_t index, + v8::Local pValue) { +- ASSERT(!pValue.IsEmpty()); +- if (pArray.IsEmpty()) +- return false; +- return pArray->Set(m_pIsolate->GetCurrentContext(), index, pValue).IsJust(); ++ fxv8::ReentrantPutArrayElementHelper(GetIsolate(), pArray, index, pValue); + } + + v8::Local CFX_V8::GetArrayElement(v8::Local pArray, +- unsigned index) { +- if (pArray.IsEmpty()) +- return v8::Local(); +- v8::Local val; +- if (!pArray->Get(m_pIsolate->GetCurrentContext(), index).ToLocal(&val)) +- return v8::Local(); +- return val; ++ size_t index) { ++ return fxv8::ReentrantGetArrayElementHelper(GetIsolate(), pArray, index); + } + +-unsigned CFX_V8::GetArrayLength(v8::Local pArray) { +- if (pArray.IsEmpty()) +- return 0; +- return pArray->Length(); ++size_t CFX_V8::GetArrayLength(v8::Local pArray) { ++ return fxv8::GetArrayLengthHelper(pArray); + } + + v8::Local CFX_V8::NewNumber(int number) { +- return v8::Int32::New(GetIsolate(), number); ++ return fxv8::NewNumberHelper(GetIsolate(), number); + } + + v8::Local CFX_V8::NewNumber(double number) { +- return v8::Number::New(GetIsolate(), number); ++ return fxv8::NewNumberHelper(GetIsolate(), number); + } + + v8::Local CFX_V8::NewNumber(float number) { +- return v8::Number::New(GetIsolate(), number); ++ return fxv8::NewNumberHelper(GetIsolate(), number); + } + + v8::Local CFX_V8::NewBoolean(bool b) { +- return v8::Boolean::New(GetIsolate(), b); ++ return fxv8::NewBooleanHelper(GetIsolate(), b); + } + + v8::Local CFX_V8::NewString(ByteStringView str) { +- v8::Isolate* pIsolate = m_pIsolate ? GetIsolate() : v8::Isolate::GetCurrent(); +- return v8::String::NewFromUtf8(pIsolate, str.unterminated_c_str(), +- v8::NewStringType::kNormal, str.GetLength()) +- .ToLocalChecked(); ++ return fxv8::NewStringHelper(GetIsolate(), str); + } + + v8::Local CFX_V8::NewString(WideStringView str) { +@@ -123,95 +88,45 @@ v8::Local CFX_V8::NewString(WideStringView str) { + } + + v8::Local CFX_V8::NewNull() { +- return v8::Null(GetIsolate()); ++ return fxv8::NewNullHelper(GetIsolate()); + } + + v8::Local CFX_V8::NewUndefined() { +- return v8::Undefined(GetIsolate()); ++ return fxv8::NewUndefinedHelper(GetIsolate()); + } + + v8::Local CFX_V8::NewDate(double d) { +- return v8::Date::New(m_pIsolate->GetCurrentContext(), d) +- .ToLocalChecked() +- .As(); ++ return fxv8::NewDateHelper(GetIsolate(), d); + } + + int CFX_V8::ToInt32(v8::Local pValue) { +- if (pValue.IsEmpty()) +- return 0; +- v8::Local context = m_pIsolate->GetCurrentContext(); +- v8::MaybeLocal maybe_int32 = pValue->ToInt32(context); +- if (maybe_int32.IsEmpty()) +- return 0; +- return maybe_int32.ToLocalChecked()->Value(); ++ return fxv8::ReentrantToInt32Helper(GetIsolate(), pValue); + } + + bool CFX_V8::ToBoolean(v8::Local pValue) { +- if (pValue.IsEmpty()) +- return false; +- return pValue->BooleanValue(m_pIsolate.Get()); ++ return fxv8::ReentrantToBooleanHelper(GetIsolate(), pValue); + } + + double CFX_V8::ToDouble(v8::Local pValue) { +- if (pValue.IsEmpty()) +- return 0.0; +- v8::Local context = m_pIsolate->GetCurrentContext(); +- v8::MaybeLocal maybe_number = pValue->ToNumber(context); +- if (maybe_number.IsEmpty()) +- return 0.0; +- return maybe_number.ToLocalChecked()->Value(); ++ return fxv8::ReentrantToDoubleHelper(GetIsolate(), pValue); + } + + WideString CFX_V8::ToWideString(v8::Local pValue) { +- if (pValue.IsEmpty()) +- return WideString(); +- v8::Local context = m_pIsolate->GetCurrentContext(); +- v8::MaybeLocal maybe_string = pValue->ToString(context); +- if (maybe_string.IsEmpty()) +- return WideString(); +- v8::String::Utf8Value s(GetIsolate(), maybe_string.ToLocalChecked()); +- return WideString::FromUTF8(ByteStringView(*s, s.length())); ++ return fxv8::ReentrantToWideStringHelper(GetIsolate(), pValue); + } + + ByteString CFX_V8::ToByteString(v8::Local pValue) { +- if (pValue.IsEmpty()) +- return ByteString(); +- v8::Local context = m_pIsolate->GetCurrentContext(); +- v8::MaybeLocal maybe_string = pValue->ToString(context); +- if (maybe_string.IsEmpty()) +- return ByteString(); +- v8::String::Utf8Value s(GetIsolate(), maybe_string.ToLocalChecked()); +- return ByteString(*s); ++ return fxv8::ReentrantToByteStringHelper(GetIsolate(), pValue); + } + + v8::Local CFX_V8::ToObject(v8::Local pValue) { +- if (pValue.IsEmpty() || !pValue->IsObject()) +- return v8::Local(); +- v8::Local context = m_pIsolate->GetCurrentContext(); +- return pValue->ToObject(context).ToLocalChecked(); ++ return fxv8::ReentrantToObjectHelper(GetIsolate(), pValue); + } + + v8::Local CFX_V8::ToArray(v8::Local pValue) { +- if (pValue.IsEmpty() || !pValue->IsArray()) +- return v8::Local(); +- v8::Local context = m_pIsolate->GetCurrentContext(); +- return v8::Local::Cast(pValue->ToObject(context).ToLocalChecked()); +-} +- +-void* CFX_V8ArrayBufferAllocator::Allocate(size_t length) { +- if (length > kMaxAllowedBytes) +- return nullptr; +- return GetArrayBufferPartitionAllocator().root()->AllocFlags( +- pdfium::base::PartitionAllocZeroFill, length, "CFX_V8ArrayBuffer"); +-} +- +-void* CFX_V8ArrayBufferAllocator::AllocateUninitialized(size_t length) { +- if (length > kMaxAllowedBytes) +- return nullptr; +- return GetArrayBufferPartitionAllocator().root()->Alloc(length, +- "CFX_V8ArrayBuffer"); ++ return fxv8::ReentrantToArrayHelper(GetIsolate(), pValue); + } + +-void CFX_V8ArrayBufferAllocator::Free(void* data, size_t length) { +- GetArrayBufferPartitionAllocator().root()->Free(data); ++void CFX_V8IsolateDeleter::operator()(v8::Isolate* ptr) { ++ ptr->Dispose(); + } +diff --git a/fxjs/cfx_v8.h b/fxjs/cfx_v8.h +index cb152acbb..7b2ddc948 100644 +--- a/fxjs/cfx_v8.h ++++ b/fxjs/cfx_v8.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,18 +7,20 @@ + #ifndef FXJS_CFX_V8_H_ + #define FXJS_CFX_V8_H_ + ++#include ++ + #include + + #include "core/fxcrt/fx_string.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "v8/include/v8.h" ++#include "v8/include/v8-forward.h" + + class CFX_V8 { + public: + explicit CFX_V8(v8::Isolate* pIsolate); + virtual ~CFX_V8(); + +- v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); } ++ v8::Isolate* GetIsolate() const { return m_pIsolate; } + + v8::Local NewNull(); + v8::Local NewUndefined(); +@@ -41,18 +43,18 @@ class CFX_V8 { + v8::Local ToArray(v8::Local pValue); + + // Arrays. +- unsigned GetArrayLength(v8::Local pArray); ++ size_t GetArrayLength(v8::Local pArray); + v8::Local GetArrayElement(v8::Local pArray, +- unsigned index); +- bool PutArrayElement(v8::Local pArray, +- unsigned index, ++ size_t index); ++ void PutArrayElement(v8::Local pArray, ++ size_t index, + v8::Local pValue); + + // Objects. + std::vector GetObjectPropertyNames(v8::Local pObj); + v8::Local GetObjectProperty(v8::Local pObj, + ByteStringView bsUTF8PropertyName); +- bool PutObjectProperty(v8::Local pObj, ++ void PutObjectProperty(v8::Local pObj, + ByteStringView bsUTF8PropertyName, + v8::Local pValue); + +@@ -64,16 +66,9 @@ class CFX_V8 { + UnownedPtr m_pIsolate; + }; + +-class CFX_V8ArrayBufferAllocator final : public v8::ArrayBuffer::Allocator { +- static const size_t kMaxAllowedBytes = 0x10000000; +- void* Allocate(size_t length) override; +- void* AllocateUninitialized(size_t length) override; +- void Free(void* data, size_t length) override; +-}; +- + // Use with std::unique_ptr to dispose of isolates correctly. + struct CFX_V8IsolateDeleter { +- inline void operator()(v8::Isolate* ptr) { ptr->Dispose(); } ++ void operator()(v8::Isolate* ptr); + }; + + #endif // FXJS_CFX_V8_H_ +diff --git a/fxjs/cfx_v8_array_buffer_allocator.cpp b/fxjs/cfx_v8_array_buffer_allocator.cpp +new file mode 100644 +index 000000000..c0ddad087 +--- /dev/null ++++ b/fxjs/cfx_v8_array_buffer_allocator.cpp +@@ -0,0 +1,53 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "fxjs/cfx_v8_array_buffer_allocator.h" ++ ++#include "core/fxcrt/fx_memory.h" ++ ++CFX_V8ArrayBufferAllocator::CFX_V8ArrayBufferAllocator() = default; ++ ++CFX_V8ArrayBufferAllocator::~CFX_V8ArrayBufferAllocator() = default; ++ ++// NOTE: Under V8 sandbox mode, defer NewDefaultAllocator() call until ++// first use, since V8 must be initialized first for it to succeed, but ++// we need the allocator in order to initialize V8. ++ ++void* CFX_V8ArrayBufferAllocator::Allocate(size_t length) { ++ if (length > kMaxAllowedBytes) ++ return nullptr; ++#ifdef V8_ENABLE_SANDBOX ++ if (!wrapped_) { ++ wrapped_.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator()); ++ } ++ return wrapped_->Allocate(length); ++#else // V8_ENABLE_SANDBOX ++ return FX_ArrayBufferAllocate(length); ++#endif // V8_ENABLE_SANDBOX ++} ++ ++void* CFX_V8ArrayBufferAllocator::AllocateUninitialized(size_t length) { ++ if (length > kMaxAllowedBytes) ++ return nullptr; ++#ifdef V8_ENABLE_SANDBOX ++ if (!wrapped_) { ++ wrapped_.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator()); ++ } ++ return wrapped_->AllocateUninitialized(length); ++#else // V8_ENABLE_SANDBOX ++ return FX_ArrayBufferAllocateUninitialized(length); ++#endif ++} ++ ++void CFX_V8ArrayBufferAllocator::Free(void* data, size_t length) { ++#ifdef V8_ENABLE_SANDBOX ++ if (wrapped_) { ++ wrapped_->Free(data, length); ++ } ++#else // V8_ENABLE_SANDBOX ++ FX_ArrayBufferFree(data); ++#endif ++} +diff --git a/fxjs/cfx_v8_array_buffer_allocator.h b/fxjs/cfx_v8_array_buffer_allocator.h +new file mode 100644 +index 000000000..a6daef046 +--- /dev/null ++++ b/fxjs/cfx_v8_array_buffer_allocator.h +@@ -0,0 +1,35 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef FXJS_CFX_V8_ARRAY_BUFFER_ALLOCATOR_H_ ++#define FXJS_CFX_V8_ARRAY_BUFFER_ALLOCATOR_H_ ++ ++#include ++ ++#include ++ ++#include "v8/include/v8-array-buffer.h" ++ ++class CFX_V8ArrayBufferAllocator final : public v8::ArrayBuffer::Allocator { ++ public: ++ static const size_t kMaxAllowedBytes = 0x10000000; ++ ++ CFX_V8ArrayBufferAllocator(); ++ CFX_V8ArrayBufferAllocator(const CFX_V8ArrayBufferAllocator&) = delete; ++ CFX_V8ArrayBufferAllocator(CFX_V8ArrayBufferAllocator&&) = delete; ++ ~CFX_V8ArrayBufferAllocator() override; ++ ++ void* Allocate(size_t length) override; ++ void* AllocateUninitialized(size_t length) override; ++ void Free(void* data, size_t length) override; ++ ++#ifdef V8_ENABLE_SANDBOX ++ private: ++ std::unique_ptr wrapped_; ++#endif // V8_ENABLE_SANDBOX ++}; ++ ++#endif // FXJS_CFX_V8_ARRAY_BUFFER_ALLOCATOR_H_ +diff --git a/fxjs/cfx_v8_unittest.cpp b/fxjs/cfx_v8_unittest.cpp +index 7b9ecb5de..0a2dca8c2 100644 +--- a/fxjs/cfx_v8_unittest.cpp ++++ b/fxjs/cfx_v8_unittest.cpp +@@ -1,39 +1,43 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include "fxjs/cfx_v8_unittest.h" ++#include "fxjs/cfx_v8.h" ++ ++#include + + #include + +-#include "fxjs/cfx_v8.h" ++#include "testing/fxv8_unittest.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" ++#include "v8/include/v8-container.h" ++#include "v8/include/v8-context.h" ++#include "v8/include/v8-date.h" ++#include "v8/include/v8-isolate.h" + + namespace { + bool getter_sentinel = false; + bool setter_sentinel = false; + } // namespace + +-void FXV8UnitTest::V8IsolateDeleter::operator()(v8::Isolate* ptr) const { +- ptr->Dispose(); +-} +- +-FXV8UnitTest::FXV8UnitTest() = default; +- +-FXV8UnitTest::~FXV8UnitTest() = default; ++class CFXV8UnitTest : public FXV8UnitTest { ++ public: ++ CFXV8UnitTest() = default; ++ ~CFXV8UnitTest() override = default; + +-void FXV8UnitTest::SetUp() { +- array_buffer_allocator_ = pdfium::MakeUnique(); ++ // FXV8UnitTest: ++ void SetUp() override { ++ FXV8UnitTest::SetUp(); ++ cfx_v8_ = std::make_unique(isolate()); ++ } + +- v8::Isolate::CreateParams params; +- params.array_buffer_allocator = array_buffer_allocator_.get(); +- isolate_.reset(v8::Isolate::New(params)); ++ CFX_V8* cfx_v8() const { return cfx_v8_.get(); } + +- cfx_v8_ = pdfium::MakeUnique(isolate_.get()); +-} ++ protected: ++ std::unique_ptr cfx_v8_; ++}; + +-TEST_F(FXV8UnitTest, EmptyLocal) { ++TEST_F(CFXV8UnitTest, EmptyLocal) { + v8::Isolate::Scope isolate_scope(isolate()); + v8::HandleScope handle_scope(isolate()); + v8::Context::Scope context_scope(v8::Context::New(isolate())); +@@ -50,18 +54,18 @@ TEST_F(FXV8UnitTest, EmptyLocal) { + // Can't set properties on empty objects, but does not fault. + v8::Local marker = cfx_v8()->NewNumber(2); + v8::Local empty_object; +- EXPECT_FALSE(cfx_v8()->PutObjectProperty(empty_object, "clams", marker)); ++ cfx_v8()->PutObjectProperty(empty_object, "clams", marker); + EXPECT_TRUE(cfx_v8()->GetObjectProperty(empty_object, "clams").IsEmpty()); + EXPECT_EQ(0u, cfx_v8()->GetObjectPropertyNames(empty_object).size()); + + // Can't set elements in empty arrays, but does not fault. + v8::Local empty_array; +- EXPECT_FALSE(cfx_v8()->PutArrayElement(empty_array, 0, marker)); ++ cfx_v8()->PutArrayElement(empty_array, 0, marker); + EXPECT_TRUE(cfx_v8()->GetArrayElement(empty_array, 0).IsEmpty()); + EXPECT_EQ(0u, cfx_v8()->GetArrayLength(empty_array)); + } + +-TEST_F(FXV8UnitTest, NewNull) { ++TEST_F(CFXV8UnitTest, NewNull) { + v8::Isolate::Scope isolate_scope(isolate()); + v8::HandleScope handle_scope(isolate()); + v8::Context::Scope context_scope(v8::Context::New(isolate())); +@@ -76,7 +80,7 @@ TEST_F(FXV8UnitTest, NewNull) { + EXPECT_TRUE(cfx_v8()->ToArray(nullz).IsEmpty()); + } + +-TEST_F(FXV8UnitTest, NewUndefined) { ++TEST_F(CFXV8UnitTest, NewUndefined) { + v8::Isolate::Scope isolate_scope(isolate()); + v8::HandleScope handle_scope(isolate()); + v8::Context::Scope context_scope(v8::Context::New(isolate())); +@@ -84,14 +88,14 @@ TEST_F(FXV8UnitTest, NewUndefined) { + auto undef = cfx_v8()->NewUndefined(); + EXPECT_FALSE(cfx_v8()->ToBoolean(undef)); + EXPECT_EQ(0, cfx_v8()->ToInt32(undef)); +- EXPECT_TRUE(std::isnan(cfx_v8()->ToDouble(undef))); ++ EXPECT_TRUE(isnan(cfx_v8()->ToDouble(undef))); + EXPECT_EQ("undefined", cfx_v8()->ToByteString(undef)); + EXPECT_EQ(L"undefined", cfx_v8()->ToWideString(undef)); + EXPECT_TRUE(cfx_v8()->ToObject(undef).IsEmpty()); + EXPECT_TRUE(cfx_v8()->ToArray(undef).IsEmpty()); + } + +-TEST_F(FXV8UnitTest, NewBoolean) { ++TEST_F(CFXV8UnitTest, NewBoolean) { + v8::Isolate::Scope isolate_scope(isolate()); + v8::HandleScope handle_scope(isolate()); + v8::Context::Scope context_scope(v8::Context::New(isolate())); +@@ -115,7 +119,7 @@ TEST_F(FXV8UnitTest, NewBoolean) { + EXPECT_TRUE(cfx_v8()->ToArray(boolz).IsEmpty()); + } + +-TEST_F(FXV8UnitTest, NewNumber) { ++TEST_F(CFXV8UnitTest, NewNumber) { + v8::Isolate::Scope isolate_scope(isolate()); + v8::HandleScope handle_scope(isolate()); + v8::Context::Scope context_scope(v8::Context::New(isolate())); +@@ -130,7 +134,7 @@ TEST_F(FXV8UnitTest, NewNumber) { + EXPECT_TRUE(cfx_v8()->ToArray(num).IsEmpty()); + } + +-TEST_F(FXV8UnitTest, NewString) { ++TEST_F(CFXV8UnitTest, NewString) { + v8::Isolate::Scope isolate_scope(isolate()); + v8::HandleScope handle_scope(isolate()); + v8::Context::Scope context_scope(v8::Context::New(isolate())); +@@ -154,7 +158,7 @@ TEST_F(FXV8UnitTest, NewString) { + EXPECT_TRUE(cfx_v8()->ToArray(str2).IsEmpty()); + } + +-TEST_F(FXV8UnitTest, NewDate) { ++TEST_F(CFXV8UnitTest, NewDate) { + v8::Isolate::Scope isolate_scope(isolate()); + v8::HandleScope handle_scope(isolate()); + v8::Context::Scope context_scope(v8::Context::New(isolate())); +@@ -169,7 +173,7 @@ TEST_F(FXV8UnitTest, NewDate) { + EXPECT_TRUE(cfx_v8()->ToArray(date).IsEmpty()); + } + +-TEST_F(FXV8UnitTest, NewArray) { ++TEST_F(CFXV8UnitTest, NewArray) { + v8::Isolate::Scope isolate_scope(isolate()); + v8::HandleScope handle_scope(isolate()); + v8::Context::Scope context_scope(v8::Context::New(isolate())); +@@ -180,7 +184,7 @@ TEST_F(FXV8UnitTest, NewArray) { + EXPECT_TRUE(cfx_v8()->GetArrayElement(array, 2)->IsUndefined()); + EXPECT_EQ(0u, cfx_v8()->GetArrayLength(array)); + +- EXPECT_TRUE(cfx_v8()->PutArrayElement(array, 3, cfx_v8()->NewNumber(12))); ++ cfx_v8()->PutArrayElement(array, 3, cfx_v8()->NewNumber(12)); + EXPECT_FALSE(cfx_v8()->GetArrayElement(array, 2).IsEmpty()); + EXPECT_TRUE(cfx_v8()->GetArrayElement(array, 2)->IsUndefined()); + EXPECT_FALSE(cfx_v8()->GetArrayElement(array, 3).IsEmpty()); +@@ -196,7 +200,7 @@ TEST_F(FXV8UnitTest, NewArray) { + EXPECT_TRUE(cfx_v8()->ToArray(array)->IsArray()); + } + +-TEST_F(FXV8UnitTest, NewObject) { ++TEST_F(CFXV8UnitTest, NewObject) { + v8::Isolate::Scope isolate_scope(isolate()); + v8::HandleScope handle_scope(isolate()); + v8::Context::Scope context_scope(v8::Context::New(isolate())); +@@ -208,8 +212,7 @@ TEST_F(FXV8UnitTest, NewObject) { + EXPECT_TRUE(cfx_v8()->GetObjectProperty(object, "clams")->IsUndefined()); + EXPECT_EQ(0u, cfx_v8()->GetObjectPropertyNames(object).size()); + +- EXPECT_TRUE( +- cfx_v8()->PutObjectProperty(object, "clams", cfx_v8()->NewNumber(12))); ++ cfx_v8()->PutObjectProperty(object, "clams", cfx_v8()->NewNumber(12)); + EXPECT_FALSE(cfx_v8()->GetObjectProperty(object, "clams").IsEmpty()); + EXPECT_TRUE(cfx_v8()->GetObjectProperty(object, "clams")->IsNumber()); + EXPECT_EQ(1u, cfx_v8()->GetObjectPropertyNames(object).size()); +@@ -224,7 +227,7 @@ TEST_F(FXV8UnitTest, NewObject) { + EXPECT_TRUE(cfx_v8()->ToArray(object).IsEmpty()); + } + +-TEST_F(FXV8UnitTest, ThrowFromGetter) { ++TEST_F(CFXV8UnitTest, ThrowFromGetter) { + v8::Isolate::Scope isolate_scope(isolate()); + v8::HandleScope handle_scope(isolate()); + v8::Local context = v8::Context::New(isolate()); +@@ -246,7 +249,7 @@ TEST_F(FXV8UnitTest, ThrowFromGetter) { + EXPECT_TRUE(getter_sentinel); + } + +-TEST_F(FXV8UnitTest, ThrowFromSetter) { ++TEST_F(CFXV8UnitTest, ThrowFromSetter) { + v8::Isolate::Scope isolate_scope(isolate()); + v8::HandleScope handle_scope(isolate()); + v8::Local context = v8::Context::New(isolate()); +@@ -264,6 +267,6 @@ TEST_F(FXV8UnitTest, ThrowFromSetter) { + }) + .FromJust()); + setter_sentinel = false; +- EXPECT_FALSE(cfx_v8()->PutObjectProperty(object, "clams", name)); ++ cfx_v8()->PutObjectProperty(object, "clams", name); + EXPECT_TRUE(setter_sentinel); + } +diff --git a/fxjs/cfxjs_engine.cpp b/fxjs/cfxjs_engine.cpp +index c4d46a29f..35653c980 100644 +--- a/fxjs/cfxjs_engine.cpp ++++ b/fxjs/cfxjs_engine.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,17 +8,23 @@ + + #include + #include +-#include + ++#include "core/fxcrt/stl_util.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "fxjs/cfx_v8_array_buffer_allocator.h" + #include "fxjs/cjs_object.h" ++#include "fxjs/fxv8.h" + #include "fxjs/xfa/cfxjse_runtimedata.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "v8/include/v8-context.h" ++#include "v8/include/v8-exception.h" ++#include "v8/include/v8-isolate.h" ++#include "v8/include/v8-message.h" ++#include "v8/include/v8-primitive.h" ++#include "v8/include/v8-script.h" + #include "v8/include/v8-util.h" + +-class CFXJS_PerObjectData; +- + namespace { + + unsigned int g_embedderDataSlot = 1u; +@@ -26,7 +32,14 @@ v8::Isolate* g_isolate = nullptr; + size_t g_isolate_ref_count = 0; + CFX_V8ArrayBufferAllocator* g_arrayBufferAllocator = nullptr; + v8::Global* g_DefaultGlobalObjectTemplate = nullptr; ++ ++// Only the address matters, values are for humans debugging. ASLR should ++// ensure that these values are unlikely to arise otherwise. Keep these ++// wchar_t to prevent the compiler from doing something clever, like ++// aligning them on a byte boundary to save space, which would make them ++// incompatible for use as V8 aligned pointers. + const wchar_t kPerObjectDataTag[] = L"CFXJS_PerObjectData"; ++const wchar_t kPerIsolateDataTag[] = L"FXJS_PerIsolateData"; + + void* GetAlignedPointerForPerObjectDataTag() { + return const_cast(static_cast(kPerObjectDataTag)); +@@ -42,6 +55,41 @@ std::pair GetLineAndColumnFromError(v8::Local message, + + } // namespace + ++class CFXJS_PerObjectData { ++ public: ++ ~CFXJS_PerObjectData() = default; ++ ++ static void SetNewDataInObject(uint32_t nObjDefnID, ++ v8::Local pObj) { ++ if (pObj->InternalFieldCount() == 2) { ++ pObj->SetAlignedPointerInInternalField( ++ 0, GetAlignedPointerForPerObjectDataTag()); ++ pObj->SetAlignedPointerInInternalField( ++ 1, new CFXJS_PerObjectData(nObjDefnID)); ++ } ++ } ++ ++ static CFXJS_PerObjectData* GetFromObject(v8::Local pObj) { ++ if (pObj.IsEmpty() || pObj->InternalFieldCount() != 2 || ++ pObj->GetAlignedPointerFromInternalField(0) != ++ GetAlignedPointerForPerObjectDataTag()) { ++ return nullptr; ++ } ++ return static_cast( ++ pObj->GetAlignedPointerFromInternalField(1)); ++ } ++ ++ uint32_t GetObjDefnID() const { return m_ObjDefnID; } ++ CJS_Object* GetPrivate() { return m_pPrivate.get(); } ++ void SetPrivate(std::unique_ptr p) { m_pPrivate = std::move(p); } ++ ++ private: ++ explicit CFXJS_PerObjectData(uint32_t nObjDefnID) : m_ObjDefnID(nObjDefnID) {} ++ ++ const uint32_t m_ObjDefnID; ++ std::unique_ptr m_pPrivate; ++}; ++ + // Global weak map to save dynamic objects. + class V8TemplateMapTraits final + : public v8::StdMapTraits { +@@ -84,8 +132,9 @@ class V8TemplateMap { + explicit V8TemplateMap(v8::Isolate* isolate) : m_map(isolate) {} + ~V8TemplateMap() = default; + +- void SetAndMakeWeak(WeakCallbackDataType* key, v8::Local handle) { +- ASSERT(!m_map.Contains(key)); ++ void SetAndMakeWeak(v8::Local handle) { ++ WeakCallbackDataType* key = CFXJS_PerObjectData::GetFromObject(handle); ++ DCHECK(!m_map.Contains(key)); + + // Inserting an object into a GlobalValueMap with the appropriate traits + // has the side-effect of making the object weak deep in the guts of V8, +@@ -93,41 +142,12 @@ class V8TemplateMap { + m_map.Set(key, handle); + } + +- friend class V8TemplateMapTraits; ++ MapType* GetMap() { return &m_map; } + + private: + MapType m_map; + }; + +-class CFXJS_PerObjectData { +- public: +- explicit CFXJS_PerObjectData(int nObjDefID) : m_ObjDefID(nObjDefID) {} +- +- ~CFXJS_PerObjectData() = default; +- +- static void SetInObject(CFXJS_PerObjectData* pData, +- v8::Local pObj) { +- if (pObj->InternalFieldCount() == 2) { +- pObj->SetAlignedPointerInInternalField( +- 0, GetAlignedPointerForPerObjectDataTag()); +- pObj->SetAlignedPointerInInternalField(1, pData); +- } +- } +- +- static CFXJS_PerObjectData* GetFromObject(v8::Local pObj) { +- if (pObj.IsEmpty() || pObj->InternalFieldCount() != 2 || +- pObj->GetAlignedPointerFromInternalField(0) != +- GetAlignedPointerForPerObjectDataTag()) { +- return nullptr; +- } +- return static_cast( +- pObj->GetAlignedPointerFromInternalField(1)); +- } +- +- const int m_ObjDefID; +- std::unique_ptr m_pPrivate; +-}; +- + class CFXJS_ObjDefinition { + public: + CFXJS_ObjDefinition(v8::Isolate* isolate, +@@ -142,42 +162,37 @@ class CFXJS_ObjDefinition { + m_pIsolate(isolate) { + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); +- v8::Local fun = v8::FunctionTemplate::New(isolate); +- fun->InstanceTemplate()->SetInternalFieldCount(2); +- fun->SetCallHandler(CallHandler, v8::Number::New(isolate, eObjType)); ++ v8::Local fn = v8::FunctionTemplate::New(isolate); ++ fn->InstanceTemplate()->SetInternalFieldCount(2); ++ fn->InstanceTemplate()->SetImmutableProto(); ++ fn->SetCallHandler(CallHandler, v8::Number::New(isolate, eObjType)); + if (eObjType == FXJSOBJTYPE_GLOBAL) { +- fun->InstanceTemplate()->Set( +- v8::Symbol::GetToStringTag(isolate), +- v8::String::NewFromUtf8(isolate, "global", v8::NewStringType::kNormal) +- .ToLocalChecked()); ++ fn->InstanceTemplate()->Set(v8::Symbol::GetToStringTag(isolate), ++ fxv8::NewStringHelper(isolate, "global")); + } +- m_FunctionTemplate.Reset(isolate, fun); +- m_Signature.Reset(isolate, v8::Signature::New(isolate, fun)); ++ m_FunctionTemplate.Reset(isolate, fn); ++ m_Signature.Reset(isolate, v8::Signature::New(isolate, fn)); + } + + static void CallHandler(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + if (!info.IsConstructCall()) { +- isolate->ThrowException( +- v8::String::NewFromUtf8(isolate, "illegal constructor", +- v8::NewStringType::kNormal) +- .ToLocalChecked()); ++ fxv8::ThrowExceptionHelper(isolate, "illegal constructor"); + return; + } + if (info.Data().As()->Value() != FXJSOBJTYPE_DYNAMIC) { +- isolate->ThrowException( +- v8::String::NewFromUtf8(isolate, "not a dynamic object", +- v8::NewStringType::kNormal) +- .ToLocalChecked()); ++ fxv8::ThrowExceptionHelper(isolate, "not a dynamic object"); + return; + } + v8::Local holder = info.Holder(); +- ASSERT(holder->InternalFieldCount() == 2); ++ DCHECK_EQ(holder->InternalFieldCount(), 2); + holder->SetAlignedPointerInInternalField(0, nullptr); + holder->SetAlignedPointerInInternalField(1, nullptr); + } + +- v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); } ++ FXJSOBJTYPE GetObjType() const { return m_ObjType; } ++ const char* GetObjName() const { return m_ObjName; } ++ v8::Isolate* GetIsolate() const { return m_pIsolate; } + + void DefineConst(const char* sConstName, v8::Local pDefault) { + GetInstanceTemplate()->Set(GetIsolate(), sConstName, pDefault); +@@ -197,12 +212,14 @@ class CFXJS_ObjDefinition { + GetInstanceTemplate()->Set(sMethodName, fun, v8::ReadOnly); + } + +- void DefineAllProperties(v8::GenericNamedPropertyQueryCallback pPropQurey, +- v8::GenericNamedPropertyGetterCallback pPropGet, +- v8::GenericNamedPropertySetterCallback pPropPut, +- v8::GenericNamedPropertyDeleterCallback pPropDel) { ++ void DefineAllProperties( ++ v8::GenericNamedPropertyQueryCallback pPropQurey, ++ v8::GenericNamedPropertyGetterCallback pPropGet, ++ v8::GenericNamedPropertySetterCallback pPropPut, ++ v8::GenericNamedPropertyDeleterCallback pPropDel, ++ v8::GenericNamedPropertyEnumeratorCallback pPropEnum) { + GetInstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( +- pPropGet, pPropPut, pPropQurey, pPropDel, nullptr, ++ pPropGet, pPropPut, pPropQurey, pPropDel, pPropEnum, + v8::Local(), + v8::PropertyHandlerFlags::kOnlyInterceptStrings)); + } +@@ -219,7 +236,20 @@ class CFXJS_ObjDefinition { + return scope.Escape(m_Signature.Get(GetIsolate())); + } + +- const char* const m_ObjName; ++ void RunConstructor(CFXJS_Engine* pEngine, ++ v8::Local obj, ++ v8::Local proxy) { ++ if (m_pConstructor) ++ m_pConstructor(pEngine, obj, proxy); ++ } ++ ++ void RunDestructor(v8::Local obj) { ++ if (m_pDestructor) ++ m_pDestructor(obj); ++ } ++ ++ private: ++ UnownedPtr const m_ObjName; + const FXJSOBJTYPE m_ObjType; + const CFXJS_Engine::Constructor m_pConstructor; + const CFXJS_Engine::Destructor m_pDestructor; +@@ -231,18 +261,16 @@ class CFXJS_ObjDefinition { + static v8::Local GetGlobalObjectTemplate( + v8::Isolate* pIsolate) { + FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(pIsolate); +- for (int i = 0; i < pIsolateData->MaxObjDefinitionID(); ++i) { ++ for (uint32_t i = 1; i <= pIsolateData->CurrentMaxObjDefinitionID(); ++i) { + CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(i); +- if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) ++ if (pObjDef->GetObjType() == FXJSOBJTYPE_GLOBAL) + return pObjDef->GetInstanceTemplate(); + } + if (!g_DefaultGlobalObjectTemplate) { + v8::Local hGlobalTemplate = + v8::ObjectTemplate::New(pIsolate); +- hGlobalTemplate->Set( +- v8::Symbol::GetToStringTag(pIsolate), +- v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal) +- .ToLocalChecked()); ++ hGlobalTemplate->Set(v8::Symbol::GetToStringTag(pIsolate), ++ fxv8::NewStringHelper(pIsolate, "global")); + g_DefaultGlobalObjectTemplate = + new v8::Global(pIsolate, hGlobalTemplate); + } +@@ -255,15 +283,14 @@ void V8TemplateMapTraits::Dispose(v8::Isolate* isolate, + v8::Local obj = value.Get(isolate); + if (obj.IsEmpty()) + return; +- int id = CFXJS_Engine::GetObjDefnID(obj); +- if (id == -1) ++ uint32_t id = CFXJS_Engine::GetObjDefnID(obj); ++ if (id == 0) + return; + FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(isolate); + CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(id); + if (!pObjDef) + return; +- if (pObjDef->m_pDestructor) +- pObjDef->m_pDestructor(obj); ++ pObjDef->RunDestructor(obj); + CFXJS_Engine::FreeObjectPrivate(obj); + } + +@@ -273,16 +300,16 @@ void V8TemplateMapTraits::DisposeWeak( + } + + V8TemplateMapTraits::MapType* V8TemplateMapTraits::MapFromWeakCallbackInfo( +- const v8::WeakCallbackInfo& data) { +- V8TemplateMap* pMap = +- FXJS_PerIsolateData::Get(data.GetIsolate())->m_pDynamicObjsMap.get(); +- return pMap ? &pMap->m_map : nullptr; ++ const v8::WeakCallbackInfo& info) { ++ auto* pIsolateData = FXJS_PerIsolateData::Get(info.GetIsolate()); ++ V8TemplateMap* pObjsMap = pIsolateData->GetDynamicObjsMap(); ++ return pObjsMap ? pObjsMap->GetMap() : nullptr; + } + + void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate) { + if (g_isolate) { +- ASSERT(g_embedderDataSlot == embedderDataSlot); +- ASSERT(g_isolate == pIsolate); ++ DCHECK_EQ(g_embedderDataSlot, embedderDataSlot); ++ DCHECK_EQ(g_isolate, pIsolate); + return; + } + g_embedderDataSlot = embedderDataSlot; +@@ -290,7 +317,7 @@ void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate) { + } + + void FXJS_Release() { +- ASSERT(!g_isolate || g_isolate_ref_count == 0); ++ DCHECK(!g_isolate || g_isolate_ref_count == 0); + delete g_DefaultGlobalObjectTemplate; + g_DefaultGlobalObjectTemplate = nullptr; + g_isolate = nullptr; +@@ -317,8 +344,6 @@ size_t FXJS_GlobalIsolateRefCount() { + return g_isolate_ref_count; + } + +-FXJS_PerIsolateData::~FXJS_PerIsolateData() {} +- + // static + void FXJS_PerIsolateData::SetUp(v8::Isolate* pIsolate) { + if (!pIsolate->GetData(g_embedderDataSlot)) +@@ -327,26 +352,33 @@ void FXJS_PerIsolateData::SetUp(v8::Isolate* pIsolate) { + + // static + FXJS_PerIsolateData* FXJS_PerIsolateData::Get(v8::Isolate* pIsolate) { +- return static_cast( +- pIsolate->GetData(g_embedderDataSlot)); +-} +- +-int FXJS_PerIsolateData::MaxObjDefinitionID() const { +- return pdfium::CollectionSize(m_ObjectDefnArray); ++ auto* result = ++ static_cast(pIsolate->GetData(g_embedderDataSlot)); ++ CHECK(result->m_Tag == kPerIsolateDataTag); ++ return result; + } + + FXJS_PerIsolateData::FXJS_PerIsolateData(v8::Isolate* pIsolate) +- : m_pDynamicObjsMap(new V8TemplateMap(pIsolate)) {} ++ : m_Tag(kPerIsolateDataTag), ++ m_pDynamicObjsMap(std::make_unique(pIsolate)) {} ++ ++FXJS_PerIsolateData::~FXJS_PerIsolateData() = default; ++ ++uint32_t FXJS_PerIsolateData::CurrentMaxObjDefinitionID() const { ++ return fxcrt::CollectionSize(m_ObjectDefnArray); ++} + +-CFXJS_ObjDefinition* FXJS_PerIsolateData::ObjDefinitionForID(int id) const { +- return (id >= 0 && id < MaxObjDefinitionID()) ? m_ObjectDefnArray[id].get() +- : nullptr; ++CFXJS_ObjDefinition* FXJS_PerIsolateData::ObjDefinitionForID( ++ uint32_t id) const { ++ return id > 0 && id <= CurrentMaxObjDefinitionID() ++ ? m_ObjectDefnArray[id - 1].get() ++ : nullptr; + } + +-int FXJS_PerIsolateData::AssignIDForObjDefinition( ++uint32_t FXJS_PerIsolateData::AssignIDForObjDefinition( + std::unique_ptr pDefn) { + m_ObjectDefnArray.push_back(std::move(pDefn)); +- return m_ObjectDefnArray.size() - 1; ++ return CurrentMaxObjDefinitionID(); + } + + CFXJS_Engine::CFXJS_Engine() : CFX_V8(nullptr) {} +@@ -356,9 +388,9 @@ CFXJS_Engine::CFXJS_Engine(v8::Isolate* pIsolate) : CFX_V8(pIsolate) {} + CFXJS_Engine::~CFXJS_Engine() = default; + + // static +-int CFXJS_Engine::GetObjDefnID(v8::Local pObj) { ++uint32_t CFXJS_Engine::GetObjDefnID(v8::Local pObj) { + CFXJS_PerObjectData* pData = CFXJS_PerObjectData::GetFromObject(pObj); +- return pData ? pData->m_ObjDefID : -1; ++ return pData ? pData->GetObjDefnID() : 0; + } + + // static +@@ -366,9 +398,8 @@ void CFXJS_Engine::SetObjectPrivate(v8::Local pObj, + std::unique_ptr p) { + CFXJS_PerObjectData* pPerObjectData = + CFXJS_PerObjectData::GetFromObject(pObj); +- if (!pPerObjectData) +- return; +- pPerObjectData->m_pPrivate = std::move(p); ++ if (pPerObjectData) ++ pPerObjectData->SetPrivate(std::move(p)); + } + + // static +@@ -379,20 +410,20 @@ void CFXJS_Engine::FreeObjectPrivate(v8::Local pObj) { + delete pData; + } + +-int CFXJS_Engine::DefineObj(const char* sObjName, +- FXJSOBJTYPE eObjType, +- CFXJS_Engine::Constructor pConstructor, +- CFXJS_Engine::Destructor pDestructor) { ++uint32_t CFXJS_Engine::DefineObj(const char* sObjName, ++ FXJSOBJTYPE eObjType, ++ CFXJS_Engine::Constructor pConstructor, ++ CFXJS_Engine::Destructor pDestructor) { + v8::Isolate::Scope isolate_scope(GetIsolate()); + v8::HandleScope handle_scope(GetIsolate()); + FXJS_PerIsolateData::SetUp(GetIsolate()); + FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate()); + return pIsolateData->AssignIDForObjDefinition( +- pdfium::MakeUnique(GetIsolate(), sObjName, eObjType, +- pConstructor, pDestructor)); ++ std::make_unique(GetIsolate(), sObjName, eObjType, ++ pConstructor, pDestructor)); + } + +-void CFXJS_Engine::DefineObjMethod(int nObjDefnID, ++void CFXJS_Engine::DefineObjMethod(uint32_t nObjDefnID, + const char* sMethodName, + v8::FunctionCallback pMethodCall) { + v8::Isolate::Scope isolate_scope(GetIsolate()); +@@ -402,7 +433,7 @@ void CFXJS_Engine::DefineObjMethod(int nObjDefnID, + pObjDef->DefineMethod(NewString(sMethodName), pMethodCall); + } + +-void CFXJS_Engine::DefineObjProperty(int nObjDefnID, ++void CFXJS_Engine::DefineObjProperty(uint32_t nObjDefnID, + const char* sPropName, + v8::AccessorGetterCallback pPropGet, + v8::AccessorSetterCallback pPropPut) { +@@ -414,19 +445,21 @@ void CFXJS_Engine::DefineObjProperty(int nObjDefnID, + } + + void CFXJS_Engine::DefineObjAllProperties( +- int nObjDefnID, ++ uint32_t nObjDefnID, + v8::GenericNamedPropertyQueryCallback pPropQurey, + v8::GenericNamedPropertyGetterCallback pPropGet, + v8::GenericNamedPropertySetterCallback pPropPut, +- v8::GenericNamedPropertyDeleterCallback pPropDel) { ++ v8::GenericNamedPropertyDeleterCallback pPropDel, ++ v8::GenericNamedPropertyEnumeratorCallback pPropEnum) { + v8::Isolate::Scope isolate_scope(GetIsolate()); + v8::HandleScope handle_scope(GetIsolate()); + FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate()); + CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(nObjDefnID); +- pObjDef->DefineAllProperties(pPropQurey, pPropGet, pPropPut, pPropDel); ++ pObjDef->DefineAllProperties(pPropQurey, pPropGet, pPropPut, pPropDel, ++ pPropEnum); + } + +-void CFXJS_Engine::DefineObjConst(int nObjDefnID, ++void CFXJS_Engine::DefineObjConst(uint32_t nObjDefnID, + const char* sConstName, + v8::Local pDefault) { + v8::Isolate::Scope isolate_scope(GetIsolate()); +@@ -486,24 +519,15 @@ void CFXJS_Engine::InitializeEngine() { + + v8::Context::Scope context_scope(v8Context); + FXJS_PerIsolateData* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate()); +- int maxID = pIsolateData->MaxObjDefinitionID(); ++ uint32_t maxID = pIsolateData->CurrentMaxObjDefinitionID(); + m_StaticObjects.resize(maxID + 1); +- for (int i = 0; i < maxID; ++i) { ++ for (uint32_t i = 1; i <= maxID; ++i) { + CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(i); +- if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) { +- CFXJS_PerObjectData::SetInObject(new CFXJS_PerObjectData(i), +- v8Context->Global() +- ->GetPrototype() +- ->ToObject(v8Context) +- .ToLocalChecked()); +- if (pObjDef->m_pConstructor) { +- pObjDef->m_pConstructor(this, v8Context->Global() +- ->GetPrototype() +- ->ToObject(v8Context) +- .ToLocalChecked()); +- } +- } else if (pObjDef->m_ObjType == FXJSOBJTYPE_STATIC) { +- v8::Local pObjName = NewString(pObjDef->m_ObjName); ++ if (pObjDef->GetObjType() == FXJSOBJTYPE_GLOBAL) { ++ CFXJS_PerObjectData::SetNewDataInObject(i, pThis); ++ pObjDef->RunConstructor(this, pThis, pThisProxy); ++ } else if (pObjDef->GetObjType() == FXJSOBJTYPE_STATIC) { ++ v8::Local pObjName = NewString(pObjDef->GetObjName()); + v8::Local obj = NewFXJSBoundObject(i, FXJSOBJTYPE_STATIC); + if (!obj.IsEmpty()) { + v8Context->Global()->Set(v8Context, pObjName, obj).FromJust(); +@@ -525,10 +549,10 @@ void CFXJS_Engine::ReleaseEngine() { + + m_ConstArrays.clear(); + +- for (int i = 0; i < pIsolateData->MaxObjDefinitionID(); ++i) { ++ for (uint32_t i = 1; i <= pIsolateData->CurrentMaxObjDefinitionID(); ++i) { + CFXJS_ObjDefinition* pObjDef = pIsolateData->ObjDefinitionForID(i); + v8::Local pObj; +- if (pObjDef->m_ObjType == FXJSOBJTYPE_GLOBAL) { ++ if (pObjDef->GetObjType() == FXJSOBJTYPE_GLOBAL) { + pObj = + context->Global()->GetPrototype()->ToObject(context).ToLocalChecked(); + } else if (!m_StaticObjects[i].IsEmpty()) { +@@ -536,8 +560,7 @@ void CFXJS_Engine::ReleaseEngine() { + m_StaticObjects[i].Reset(); + } + if (!pObj.IsEmpty()) { +- if (pObjDef->m_pDestructor) +- pObjDef->m_pDestructor(pObj); ++ pObjDef->RunDestructor(pObj); + FreeObjectPrivate(pObj); + } + } +@@ -551,7 +574,7 @@ void CFXJS_Engine::ReleaseEngine() { + GetIsolate()->SetData(g_embedderDataSlot, nullptr); + } + +-Optional CFXJS_Engine::Execute( ++absl::optional CFXJS_Engine::Execute( + const WideString& script) { + v8::Isolate::Scope isolate_scope(GetIsolate()); + v8::TryCatch try_catch(GetIsolate()); +@@ -576,10 +599,10 @@ Optional CFXJS_Engine::Execute( + std::tie(line, column) = GetLineAndColumnFromError(msg, context); + return IJS_Runtime::JS_Error(line, column, WideString::FromUTF8(*error)); + } +- return pdfium::nullopt; ++ return absl::nullopt; + } + +-v8::Local CFXJS_Engine::NewFXJSBoundObject(int nObjDefnID, ++v8::Local CFXJS_Engine::NewFXJSBoundObject(uint32_t nObjDefnID, + FXJSOBJTYPE type) { + v8::Isolate::Scope isolate_scope(GetIsolate()); + v8::Local context = GetIsolate()->GetCurrentContext(); +@@ -595,15 +618,13 @@ v8::Local CFXJS_Engine::NewFXJSBoundObject(int nObjDefnID, + if (!pObjDef->GetInstanceTemplate()->NewInstance(context).ToLocal(&obj)) + return v8::Local(); + +- CFXJS_PerObjectData* pObjData = new CFXJS_PerObjectData(nObjDefnID); +- CFXJS_PerObjectData::SetInObject(pObjData, obj); +- if (pObjDef->m_pConstructor) +- pObjDef->m_pConstructor(this, obj); +- ++ CFXJS_PerObjectData::SetNewDataInObject(nObjDefnID, obj); ++ pObjDef->RunConstructor(this, obj, obj); + if (type == FXJSOBJTYPE_DYNAMIC) { + auto* pIsolateData = FXJS_PerIsolateData::Get(GetIsolate()); +- if (pIsolateData->m_pDynamicObjsMap) +- pIsolateData->m_pDynamicObjsMap->SetAndMakeWeak(pObjData, obj); ++ V8TemplateMap* pObjsMap = pIsolateData->GetDynamicObjsMap(); ++ if (pObjsMap) ++ pObjsMap->SetAndMakeWeak(obj); + } + return obj; + } +@@ -619,7 +640,7 @@ v8::Local CFXJS_Engine::GetThisObj() { + } + + void CFXJS_Engine::Error(const WideString& message) { +- GetIsolate()->ThrowException(NewString(message.AsStringView())); ++ fxv8::ThrowExceptionHelper(GetIsolate(), message.AsStringView()); + } + + v8::Local CFXJS_Engine::GetV8Context() { +@@ -627,10 +648,11 @@ v8::Local CFXJS_Engine::GetV8Context() { + } + + // static +-CJS_Object* CFXJS_Engine::GetObjectPrivate(v8::Local pObj) { ++CJS_Object* CFXJS_Engine::GetObjectPrivate(v8::Isolate* pIsolate, ++ v8::Local pObj) { + auto* pData = CFXJS_PerObjectData::GetFromObject(pObj); + if (pData) +- return pData->m_pPrivate.get(); ++ return pData->GetPrivate(); + + if (pObj.IsEmpty()) + return nullptr; +@@ -645,16 +667,16 @@ CJS_Object* CFXJS_Engine::GetObjectPrivate(v8::Local pObj) { + if (!pProtoData) + return nullptr; + +- auto* pIsolateData = FXJS_PerIsolateData::Get(v8::Isolate::GetCurrent()); ++ auto* pIsolateData = FXJS_PerIsolateData::Get(pIsolate); + if (!pIsolateData) + return nullptr; + + CFXJS_ObjDefinition* pObjDef = +- pIsolateData->ObjDefinitionForID(pProtoData->m_ObjDefID); +- if (!pObjDef || pObjDef->m_ObjType != FXJSOBJTYPE_GLOBAL) ++ pIsolateData->ObjDefinitionForID(pProtoData->GetObjDefnID()); ++ if (!pObjDef || pObjDef->GetObjType() != FXJSOBJTYPE_GLOBAL) + return nullptr; + +- return pProtoData->m_pPrivate.get(); ++ return pProtoData->GetPrivate(); + } + + v8::Local CFXJS_Engine::GetConstArray(const WideString& name) { +diff --git a/fxjs/cfxjs_engine.h b/fxjs/cfxjs_engine.h +index 933c2508e..b0011fc02 100644 +--- a/fxjs/cfxjs_engine.h ++++ b/fxjs/cfxjs_engine.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -17,21 +17,21 @@ + #include + #include + #include ++#include + #include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" + #include "fxjs/cfx_v8.h" + #include "fxjs/ijs_runtime.h" +-#include "v8/include/v8.h" ++#include "v8/include/v8-forward.h" ++#include "v8/include/v8-function-callback.h" ++#include "v8/include/v8-persistent-handle.h" ++#include "v8/include/v8-template.h" + + class CFXJS_ObjDefinition; + class CJS_Object; + class V8TemplateMap; + +-// CFXJS_ENGINE places no restrictions on this class; it merely passes it +-// on to caller-provided methods. +-class IJS_EventContext; // A description of the event that caused JS execution. +- + enum FXJSOBJTYPE { + FXJSOBJTYPE_DYNAMIC = 0, // Created by native method and returned to JS. + FXJSOBJTYPE_STATIC, // Created by init and hung off of global object. +@@ -51,16 +51,22 @@ class FXJS_PerIsolateData { + static void SetUp(v8::Isolate* pIsolate); + static FXJS_PerIsolateData* Get(v8::Isolate* pIsolate); + +- int MaxObjDefinitionID() const; +- CFXJS_ObjDefinition* ObjDefinitionForID(int id) const; +- int AssignIDForObjDefinition(std::unique_ptr pDefn); ++ uint32_t CurrentMaxObjDefinitionID() const; ++ CFXJS_ObjDefinition* ObjDefinitionForID(uint32_t id) const; ++ uint32_t AssignIDForObjDefinition(std::unique_ptr pDefn); ++ V8TemplateMap* GetDynamicObjsMap() { return m_pDynamicObjsMap.get(); } ++ ExtensionIface* GetExtension() { return m_pExtension.get(); } ++ void SetExtension(std::unique_ptr extension) { ++ m_pExtension = std::move(extension); ++ } ++ ++ private: ++ explicit FXJS_PerIsolateData(v8::Isolate* pIsolate); + ++ const wchar_t* const m_Tag; // Raw, always a literal. + std::vector> m_ObjectDefnArray; + std::unique_ptr m_pDynamicObjsMap; +- std::unique_ptr m_pFXJSERuntimeData; +- +- protected: +- explicit FXJS_PerIsolateData(v8::Isolate* pIsolate); ++ std::unique_ptr m_pExtension; + }; + + void FXJS_Initialize(unsigned int embedderDataSlot, v8::Isolate* pIsolate); +@@ -79,35 +85,39 @@ class CFXJS_Engine : public CFX_V8 { + explicit CFXJS_Engine(v8::Isolate* pIsolate); + ~CFXJS_Engine() override; + +- using Constructor = +- std::function obj)>; ++ using Constructor = std::function obj, ++ v8::Local proxy)>; + using Destructor = std::function obj)>; + +- static int GetObjDefnID(v8::Local pObj); +- static CJS_Object* GetObjectPrivate(v8::Local pObj); ++ static uint32_t GetObjDefnID(v8::Local pObj); ++ static CJS_Object* GetObjectPrivate(v8::Isolate* pIsolate, ++ v8::Local pObj); + static void SetObjectPrivate(v8::Local pObj, + std::unique_ptr p); + static void FreeObjectPrivate(v8::Local pObj); + +- // Always returns a valid, newly-created objDefnID. +- int DefineObj(const char* sObjName, +- FXJSOBJTYPE eObjType, +- Constructor pConstructor, +- Destructor pDestructor); ++ // Always returns a valid (i.e. non-zero), newly-created objDefnID. ++ uint32_t DefineObj(const char* sObjName, ++ FXJSOBJTYPE eObjType, ++ Constructor pConstructor, ++ Destructor pDestructor); + +- void DefineObjMethod(int nObjDefnID, ++ void DefineObjMethod(uint32_t nObjDefnID, + const char* sMethodName, + v8::FunctionCallback pMethodCall); +- void DefineObjProperty(int nObjDefnID, ++ void DefineObjProperty(uint32_t nObjDefnID, + const char* sPropName, + v8::AccessorGetterCallback pPropGet, + v8::AccessorSetterCallback pPropPut); +- void DefineObjAllProperties(int nObjDefnID, +- v8::GenericNamedPropertyQueryCallback pPropQurey, +- v8::GenericNamedPropertyGetterCallback pPropGet, +- v8::GenericNamedPropertySetterCallback pPropPut, +- v8::GenericNamedPropertyDeleterCallback pPropDel); +- void DefineObjConst(int nObjDefnID, ++ void DefineObjAllProperties( ++ uint32_t nObjDefnID, ++ v8::GenericNamedPropertyQueryCallback pPropQurey, ++ v8::GenericNamedPropertyGetterCallback pPropGet, ++ v8::GenericNamedPropertySetterCallback pPropPut, ++ v8::GenericNamedPropertyDeleterCallback pPropDel, ++ v8::GenericNamedPropertyEnumeratorCallback pPropEnum); ++ void DefineObjConst(uint32_t nObjDefnID, + const char* sConstName, + v8::Local pDefault); + void DefineGlobalMethod(const char* sMethodName, +@@ -120,10 +130,11 @@ class CFXJS_Engine : public CFX_V8 { + void ReleaseEngine(); + + // Called after FXJS_InitializeEngine call made. +- Optional Execute(const WideString& script); ++ absl::optional Execute(const WideString& script); + + v8::Local GetThisObj(); +- v8::Local NewFXJSBoundObject(int nObjDefnID, FXJSOBJTYPE type); ++ v8::Local NewFXJSBoundObject(uint32_t nObjDefnID, ++ FXJSOBJTYPE type); + void Error(const WideString& message); + + v8::Local GetV8Context(); +diff --git a/fxjs/cfxjs_engine_embeddertest.cpp b/fxjs/cfxjs_engine_embeddertest.cpp +index c18171eae..70e84abec 100644 +--- a/fxjs/cfxjs_engine_embeddertest.cpp ++++ b/fxjs/cfxjs_engine_embeddertest.cpp +@@ -1,11 +1,16 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fxjs/cfxjs_engine.h" + ++#include "testing/external_engine_embedder_test.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "testing/js_embedder_test.h" ++#include "v8/include/v8-context.h" ++#include "v8/include/v8-isolate.h" ++#include "v8/include/v8-local-handle.h" ++#include "v8/include/v8-object.h" ++#include "v8/include/v8-value.h" + + namespace { + +@@ -19,7 +24,7 @@ const wchar_t kScript2[] = L"fred = 8"; + + } // namespace + +-using CFXJSEngineEmbedderTest = JSEmbedderTest; ++class CFXJSEngineEmbedderTest : public ExternalEngineEmbedderTest {}; + + void CheckAssignmentInEngineContext(CFXJS_Engine* current_engine, + double expected) { +@@ -35,7 +40,8 @@ TEST_F(CFXJSEngineEmbedderTest, Getters) { + v8::HandleScope handle_scope(isolate()); + v8::Context::Scope context_scope(GetV8Context()); + +- Optional err = engine()->Execute(WideString(kScript1)); ++ absl::optional err = ++ engine()->Execute(WideString(kScript1)); + EXPECT_FALSE(err); + CheckAssignmentInEngineContext(engine(), kExpected1); + } +@@ -52,7 +58,7 @@ TEST_F(CFXJSEngineEmbedderTest, MultipleEngines) { + + v8::Context::Scope context_scope(GetV8Context()); + { +- Optional err = ++ absl::optional err = + engine()->Execute(WideString(kScript0)); + EXPECT_FALSE(err); + CheckAssignmentInEngineContext(engine(), kExpected0); +@@ -60,7 +66,8 @@ TEST_F(CFXJSEngineEmbedderTest, MultipleEngines) { + { + // engine1 executing in engine1's context doesn't affect main. + v8::Context::Scope context_scope1(engine1.GetV8Context()); +- Optional err = engine1.Execute(WideString(kScript1)); ++ absl::optional err = ++ engine1.Execute(WideString(kScript1)); + EXPECT_FALSE(err); + CheckAssignmentInEngineContext(engine(), kExpected0); + CheckAssignmentInEngineContext(&engine1, kExpected1); +@@ -68,7 +75,8 @@ TEST_F(CFXJSEngineEmbedderTest, MultipleEngines) { + { + // engine1 executing in engine2's context doesn't affect engine1. + v8::Context::Scope context_scope2(engine2.GetV8Context()); +- Optional err = engine1.Execute(WideString(kScript2)); ++ absl::optional err = ++ engine1.Execute(WideString(kScript2)); + EXPECT_FALSE(err); + CheckAssignmentInEngineContext(engine(), kExpected0); + CheckAssignmentInEngineContext(&engine1, kExpected1); +@@ -83,7 +91,7 @@ TEST_F(CFXJSEngineEmbedderTest, JSCompileError) { + v8::HandleScope handle_scope(isolate()); + v8::Context::Scope context_scope(GetV8Context()); + +- Optional err = ++ absl::optional err = + engine()->Execute(L"functoon(x) { return x+1; }"); + EXPECT_TRUE(err); + EXPECT_STREQ(L"SyntaxError: Unexpected token '{'", err->exception.c_str()); +@@ -96,11 +104,12 @@ TEST_F(CFXJSEngineEmbedderTest, JSRuntimeError) { + v8::HandleScope handle_scope(isolate()); + v8::Context::Scope context_scope(GetV8Context()); + +- Optional err = ++ absl::optional err = + engine()->Execute(L"let a = 3;\nundefined.colour"); + EXPECT_TRUE(err); +- EXPECT_EQ(L"TypeError: Cannot read property 'colour' of undefined", +- err->exception); ++ EXPECT_EQ( ++ L"TypeError: Cannot read properties of undefined (reading 'colour')", ++ err->exception); + EXPECT_EQ(2, err->line); + EXPECT_EQ(10, err->column); + } +diff --git a/fxjs/cfxjs_engine_unittest.cpp b/fxjs/cfxjs_engine_unittest.cpp +index e0f730128..8d6c36beb 100644 +--- a/fxjs/cfxjs_engine_unittest.cpp ++++ b/fxjs/cfxjs_engine_unittest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,22 +6,23 @@ + + #include + +-#include "fxjs/cfx_v8_unittest.h" + #include "fxjs/cjs_object.h" ++#include "testing/fxv8_unittest.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "third_party/base/ptr_util.h" ++#include "v8/include/v8-context.h" ++#include "v8/include/v8-isolate.h" + + class FXJSEngineUnitTest : public FXV8UnitTest { + public: + FXJSEngineUnitTest() = default; + ~FXJSEngineUnitTest() override = default; + ++ // FXV8UnitTest: + void SetUp() override { + FXV8UnitTest::SetUp(); + FXJS_Initialize(1, isolate()); +- engine_ = pdfium::MakeUnique(isolate()); ++ engine_ = std::make_unique(isolate()); + } +- + void TearDown() override { FXJS_Release(); } + + CFXJS_Engine* engine() const { return engine_.get(); } +@@ -36,15 +37,22 @@ static bool temp_created = false; + static bool temp_destroyed = false; + + TEST_F(FXJSEngineUnitTest, GC) { ++ // Reset variables since there might be multiple iterations. ++ perm_created = false; ++ perm_destroyed = false; ++ temp_created = false; ++ temp_destroyed = false; ++ + v8::Isolate::Scope isolate_scope(isolate()); + v8::HandleScope handle_scope(isolate()); + +- // Object: 0 ++ // Object: 1 + engine()->DefineObj( + "perm", FXJSOBJTYPE_DYNAMIC, +- [](CFXJS_Engine* pEngine, v8::Local obj) { ++ [](CFXJS_Engine* pEngine, v8::Local obj, ++ v8::Local proxy) { + pEngine->SetObjectPrivate(obj, +- pdfium::MakeUnique(obj, nullptr)); ++ std::make_unique(proxy, nullptr)); + perm_created = true; + }, + [](v8::Local obj) { +@@ -52,12 +60,13 @@ TEST_F(FXJSEngineUnitTest, GC) { + CFXJS_Engine::SetObjectPrivate(obj, nullptr); + }); + +- // Object: 1 ++ // Object: 2 + engine()->DefineObj( + "temp", FXJSOBJTYPE_DYNAMIC, +- [](CFXJS_Engine* pEngine, v8::Local obj) { ++ [](CFXJS_Engine* pEngine, v8::Local obj, ++ v8::Local proxy) { + pEngine->SetObjectPrivate(obj, +- pdfium::MakeUnique(obj, nullptr)); ++ std::make_unique(proxy, nullptr)); + temp_created = true; + }, + [](v8::Local obj) { +@@ -69,7 +78,7 @@ TEST_F(FXJSEngineUnitTest, GC) { + + v8::Context::Scope context_scope(engine()->GetV8Context()); + v8::Local perm = +- engine()->NewFXJSBoundObject(0, FXJSOBJTYPE_DYNAMIC); ++ engine()->NewFXJSBoundObject(1, FXJSOBJTYPE_DYNAMIC); + EXPECT_FALSE(perm.IsEmpty()); + EXPECT_TRUE(perm_created); + EXPECT_FALSE(perm_destroyed); +@@ -77,13 +86,13 @@ TEST_F(FXJSEngineUnitTest, GC) { + { + v8::HandleScope inner_handle_scope(isolate()); + v8::Local temp = +- engine()->NewFXJSBoundObject(1, FXJSOBJTYPE_DYNAMIC); ++ engine()->NewFXJSBoundObject(2, FXJSOBJTYPE_DYNAMIC); + EXPECT_FALSE(temp.IsEmpty()); + EXPECT_TRUE(temp_created); + EXPECT_FALSE(temp_destroyed); + } + +- Optional err = engine()->Execute(L"gc();"); ++ absl::optional err = engine()->Execute(L"gc();"); + EXPECT_FALSE(err); + + EXPECT_TRUE(perm_created); +diff --git a/fxjs/cjs_annot.cpp b/fxjs/cjs_annot.cpp +index 292e58682..cee6111d8 100644 +--- a/fxjs/cjs_annot.cpp ++++ b/fxjs/cjs_annot.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,6 @@ + #include "fxjs/cjs_annot.h" + + #include "constants/annotation_flags.h" +-#include "fpdfsdk/cpdfsdk_baannot.h" + #include "fxjs/cjs_event_context.h" + #include "fxjs/cjs_object.h" + #include "fxjs/js_define.h" +@@ -18,12 +17,12 @@ const JSPropertySpec CJS_Annot::PropertySpecs[] = { + {"name", get_name_static, set_name_static}, + {"type", get_type_static, set_type_static}}; + +-int CJS_Annot::ObjDefnID = -1; ++uint32_t CJS_Annot::ObjDefnID = 0; + + const char CJS_Annot::kName[] = "Annot"; + + // static +-int CJS_Annot::GetObjDefnID() { ++uint32_t CJS_Annot::GetObjDefnID() { + return ObjDefnID; + } + +@@ -47,7 +46,7 @@ CJS_Result CJS_Annot::get_hidden(CJS_Runtime* pRuntime) { + if (!m_pAnnot) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- CPDF_Annot* pPDFAnnot = m_pAnnot->AsBAAnnot()->GetPDFAnnot(); ++ CPDF_Annot* pPDFAnnot = m_pAnnot->GetPDFAnnot(); + return CJS_Result::Success(pRuntime->NewBoolean(pPDFAnnot->IsHidden())); + } + +@@ -56,7 +55,7 @@ CJS_Result CJS_Annot::set_hidden(CJS_Runtime* pRuntime, + // May invalidate m_pAnnot. + bool bHidden = pRuntime->ToBoolean(vp); + +- CPDFSDK_BAAnnot* pBAAnnot = ToBAAnnot(m_pAnnot.Get()); ++ CPDFSDK_BAAnnot* pBAAnnot = m_pAnnot.Get(); + if (!pBAAnnot) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +@@ -77,7 +76,7 @@ CJS_Result CJS_Annot::set_hidden(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Annot::get_name(CJS_Runtime* pRuntime) { +- CPDFSDK_BAAnnot* pBAAnnot = ToBAAnnot(m_pAnnot.Get()); ++ CPDFSDK_BAAnnot* pBAAnnot = m_pAnnot.Get(); + if (!pBAAnnot) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +@@ -89,7 +88,7 @@ CJS_Result CJS_Annot::set_name(CJS_Runtime* pRuntime, v8::Local vp) { + // May invalidate m_pAnnot. + WideString annotName = pRuntime->ToWideString(vp); + +- CPDFSDK_BAAnnot* pBAAnnot = ToBAAnnot(m_pAnnot.Get()); ++ CPDFSDK_BAAnnot* pBAAnnot = m_pAnnot.Get(); + if (!pBAAnnot) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +@@ -98,14 +97,12 @@ CJS_Result CJS_Annot::set_name(CJS_Runtime* pRuntime, v8::Local vp) { + } + + CJS_Result CJS_Annot::get_type(CJS_Runtime* pRuntime) { +- CPDFSDK_BAAnnot* pBAAnnot = ToBAAnnot(m_pAnnot.Get()); ++ CPDFSDK_BAAnnot* pBAAnnot = m_pAnnot.Get(); + if (!pBAAnnot) + return CJS_Result::Failure(JSMessage::kBadObjectError); + + return CJS_Result::Success(pRuntime->NewString( +- WideString::FromDefANSI( +- CPDF_Annot::AnnotSubtypeToString(pBAAnnot->GetAnnotSubtype()) +- .AsStringView()) ++ CPDF_Annot::AnnotSubtypeToString(pBAAnnot->GetAnnotSubtype()) + .AsStringView())); + } + +diff --git a/fxjs/cjs_annot.h b/fxjs/cjs_annot.h +index ceb261523..0e5340727 100644 +--- a/fxjs/cjs_annot.h ++++ b/fxjs/cjs_annot.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,14 +7,13 @@ + #ifndef FXJS_CJS_ANNOT_H_ + #define FXJS_CJS_ANNOT_H_ + ++#include "fpdfsdk/cpdfsdk_baannot.h" + #include "fxjs/cjs_object.h" + #include "fxjs/js_define.h" + +-class CPDFSDK_BAAnnot; +- + class CJS_Annot final : public CJS_Object { + public: +- static int GetObjDefnID(); ++ static uint32_t GetObjDefnID(); + static void DefineJSObjects(CFXJS_Engine* pEngine); + + CJS_Annot(v8::Local pObject, CJS_Runtime* pRuntime); +@@ -27,7 +26,7 @@ class CJS_Annot final : public CJS_Object { + JS_STATIC_PROP(type, type, CJS_Annot) + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const char kName[]; + static const JSPropertySpec PropertySpecs[]; + +@@ -40,7 +39,7 @@ class CJS_Annot final : public CJS_Object { + CJS_Result get_type(CJS_Runtime* pRuntime); + CJS_Result set_type(CJS_Runtime* pRuntime, v8::Local vp); + +- ObservedPtr m_pAnnot; ++ ObservedPtr m_pAnnot; + }; + + #endif // FXJS_CJS_ANNOT_H_ +diff --git a/fxjs/cjs_app.cpp b/fxjs/cjs_app.cpp +index 992fd985a..0784e7edd 100644 +--- a/fxjs/cjs_app.cpp ++++ b/fxjs/cjs_app.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,24 +6,33 @@ + + #include "fxjs/cjs_app.h" + ++#include ++ ++#include + #include + ++#include "core/fxcrt/fixed_zeroed_data_vector.h" ++#include "core/fxcrt/stl_util.h" ++#include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fpdfsdk/cpdfsdk_interactiveform.h" + #include "fxjs/cjs_document.h" + #include "fxjs/cjs_timerobj.h" + #include "fxjs/global_timer.h" + #include "fxjs/ijs_event_context.h" + #include "fxjs/js_resources.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "v8/include/v8-container.h" ++ ++namespace { + +-#define JS_STR_VIEWERTYPE L"pdfium" +-#define JS_STR_VIEWERVARIATION L"Full" +-#define JS_STR_PLATFORM L"WIN" +-#define JS_STR_LANGUAGE L"ENU" +-#define JS_NUM_VIEWERVERSION 8 +-#define JS_NUM_VIEWERVERSION_XFA 11 +-#define JS_NUM_FORMSVERSION 7 ++constexpr wchar_t kStrViewerType[] = L"pdfium"; ++constexpr wchar_t kStrViewerVariation[] = L"Full"; ++constexpr wchar_t kStrPlatform[] = L"WIN"; ++constexpr wchar_t kStrLanguage[] = L"ENU"; ++constexpr int kNumViewerVersion = 8; ++constexpr int kNumViewerVersionXfa = 11; ++constexpr int kNumFormsVersion = 7; ++ ++} // namespace + + const JSPropertySpec CJS_App::PropertySpecs[] = { + {"activeDocs", get_active_docs_static, set_active_docs_static}, +@@ -64,12 +73,12 @@ const JSMethodSpec CJS_App::MethodSpecs[] = { + {"setInterval", setInterval_static}, + {"setTimeOut", setTimeOut_static}}; + +-int CJS_App::ObjDefnID = -1; ++uint32_t CJS_App::ObjDefnID = 0; + + const char CJS_App::kName[] = "app"; + + // static +-int CJS_App::GetObjDefnID() { ++uint32_t CJS_App::GetObjDefnID() { + return ObjDefnID; + } + +@@ -88,7 +97,7 @@ CJS_App::~CJS_App() = default; + + CJS_Result CJS_App::get_active_docs(CJS_Runtime* pRuntime) { + v8::Local pObj = pRuntime->GetThisObj(); +- auto pJSDocument = JSGetObject(pObj); ++ auto pJSDocument = JSGetObject(pRuntime->GetIsolate(), pObj); + if (!pJSDocument) + return CJS_Result::Failure(JSMessage::kObjectTypeError); + v8::Local aDocs = pRuntime->NewArray(); +@@ -117,7 +126,7 @@ CJS_Result CJS_App::set_calculate(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_App::get_forms_version(CJS_Runtime* pRuntime) { +- return CJS_Result::Success(pRuntime->NewNumber(JS_NUM_FORMSVERSION)); ++ return CJS_Result::Success(pRuntime->NewNumber(kNumFormsVersion)); + } + + CJS_Result CJS_App::set_forms_version(CJS_Runtime* pRuntime, +@@ -126,7 +135,7 @@ CJS_Result CJS_App::set_forms_version(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_App::get_viewer_type(CJS_Runtime* pRuntime) { +- return CJS_Result::Success(pRuntime->NewString(JS_STR_VIEWERTYPE)); ++ return CJS_Result::Success(pRuntime->NewString(kStrViewerType)); + } + + CJS_Result CJS_App::set_viewer_type(CJS_Runtime* pRuntime, +@@ -135,7 +144,7 @@ CJS_Result CJS_App::set_viewer_type(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_App::get_viewer_variation(CJS_Runtime* pRuntime) { +- return CJS_Result::Success(pRuntime->NewString(JS_STR_VIEWERVARIATION)); ++ return CJS_Result::Success(pRuntime->NewString(kStrViewerVariation)); + } + + CJS_Result CJS_App::set_viewer_variation(CJS_Runtime* pRuntime, +@@ -147,8 +156,8 @@ CJS_Result CJS_App::get_viewer_version(CJS_Runtime* pRuntime) { + CPDF_Document::Extension* pContext = + pRuntime->GetFormFillEnv()->GetDocExtension(); + int version = pContext && pContext->ContainsExtensionForm() +- ? JS_NUM_VIEWERVERSION_XFA +- : JS_NUM_VIEWERVERSION; ++ ? kNumViewerVersionXfa ++ : kNumViewerVersion; + return CJS_Result::Success(pRuntime->NewNumber(version)); + } + +@@ -164,7 +173,7 @@ CJS_Result CJS_App::get_platform(CJS_Runtime* pRuntime) { + if (!platform.IsEmpty()) + return CJS_Result::Success(pRuntime->NewString(platform.AsStringView())); + } +- return CJS_Result::Success(pRuntime->NewString(JS_STR_PLATFORM)); ++ return CJS_Result::Success(pRuntime->NewString(kStrPlatform)); + } + + CJS_Result CJS_App::set_platform(CJS_Runtime* pRuntime, +@@ -179,7 +188,7 @@ CJS_Result CJS_App::get_language(CJS_Runtime* pRuntime) { + if (!language.IsEmpty()) + return CJS_Result::Success(pRuntime->NewString(language.AsStringView())); + } +- return CJS_Result::Success(pRuntime->NewString(JS_STR_LANGUAGE)); ++ return CJS_Result::Success(pRuntime->NewString(kStrLanguage)); + } + + CJS_Result CJS_App::set_language(CJS_Runtime* pRuntime, +@@ -250,7 +259,7 @@ CJS_Result CJS_App::alert(CJS_Runtime* pRuntime, + swTitle = JSGetStringFromID(JSMessage::kAlert); + + pRuntime->BeginBlock(); +- pFormFillEnv->KillFocusAnnot(0); ++ pFormFillEnv->KillFocusAnnot({}); + v8::Local ret = pRuntime->NewNumber( + pFormFillEnv->JS_appAlert(swMsg, swTitle, iType, iIcon)); + pRuntime->EndBlock(); +@@ -302,7 +311,7 @@ CJS_Result CJS_App::setInterval( + return CJS_Result::Failure(JSMessage::kInvalidInputError); + + uint32_t dwInterval = params.size() > 1 ? pRuntime->ToInt32(params[1]) : 1000; +- auto timerRef = pdfium::MakeUnique( ++ auto timerRef = std::make_unique( + this, pRuntime, GlobalTimer::Type::kRepeating, script, dwInterval, 0); + GlobalTimer* pTimerRef = timerRef.get(); + m_Timers.insert(std::move(timerRef)); +@@ -312,8 +321,8 @@ CJS_Result CJS_App::setInterval( + if (pRetObj.IsEmpty()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- auto* pJS_TimerObj = +- static_cast(CFXJS_Engine::GetObjectPrivate(pRetObj)); ++ auto* pJS_TimerObj = static_cast( ++ CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pRetObj)); + + pJS_TimerObj->SetTimer(pTimerRef); + return CJS_Result::Success(pRetObj); +@@ -330,9 +339,9 @@ CJS_Result CJS_App::setTimeOut( + return CJS_Result::Failure(JSMessage::kInvalidInputError); + + uint32_t dwTimeOut = params.size() > 1 ? pRuntime->ToInt32(params[1]) : 1000; +- auto timerRef = pdfium::MakeUnique(this, pRuntime, +- GlobalTimer::Type::kOneShot, +- script, dwTimeOut, dwTimeOut); ++ auto timerRef = ++ std::make_unique(this, pRuntime, GlobalTimer::Type::kOneShot, ++ script, dwTimeOut, dwTimeOut); + GlobalTimer* pTimerRef = timerRef.get(); + m_Timers.insert(std::move(timerRef)); + +@@ -341,8 +350,8 @@ CJS_Result CJS_App::setTimeOut( + if (pRetObj.IsEmpty()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- auto* pJS_TimerObj = +- static_cast(CFXJS_Engine::GetObjectPrivate(pRetObj)); ++ auto* pJS_TimerObj = static_cast( ++ CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pRetObj)); + + pJS_TimerObj->SetTimer(pTimerRef); + return CJS_Result::Success(pRetObj); +@@ -374,7 +383,7 @@ void CJS_App::ClearTimerCommon(CJS_Runtime* pRuntime, + return; + + v8::Local pObj = pRuntime->ToObject(param); +- auto pTimer = JSGetObject(pObj); ++ auto pTimer = JSGetObject(pRuntime->GetIsolate(), pObj); + if (!pTimer) + return; + +@@ -394,7 +403,7 @@ void CJS_App::TimerProc(GlobalTimer* pTimer) { + } + + void CJS_App::CancelProc(GlobalTimer* pTimer) { +- m_Timers.erase(pdfium::FakeUniquePtr(pTimer)); ++ m_Timers.erase(fxcrt::MakeFakeUniquePtr(pTimer)); + } + + void CJS_App::RunJsScript(CJS_Runtime* pRuntime, const WideString& wsScript) { +@@ -453,8 +462,8 @@ CJS_Result CJS_App::mailMsg(CJS_Runtime* pRuntime, + cMsg = pRuntime->ToWideString(newParams[5]); + + pRuntime->BeginBlock(); +- pRuntime->GetFormFillEnv()->JS_docmailForm(nullptr, 0, bUI, cTo, cSubject, +- cCc, cBcc, cMsg); ++ pRuntime->GetFormFillEnv()->JS_docmailForm(pdfium::span(), bUI, ++ cTo, cSubject, cCc, cBcc, cMsg); + pRuntime->EndBlock(); + return CJS_Result::Success(); + } +@@ -541,18 +550,20 @@ CJS_Result CJS_App::response(CJS_Runtime* pRuntime, + if (IsExpandedParamKnown(newParams[4])) + swLabel = pRuntime->ToWideString(newParams[4]); + +- const int MAX_INPUT_BYTES = 2048; +- std::vector pBuff(MAX_INPUT_BYTES + 2); +- int nLengthBytes = pRuntime->GetFormFillEnv()->JS_appResponse( +- swQuestion, swTitle, swDefault, swLabel, bPassword, pBuff.data(), +- MAX_INPUT_BYTES); +- +- if (nLengthBytes < 0 || nLengthBytes > MAX_INPUT_BYTES) ++ constexpr int kMaxWideChars = 1024; ++ constexpr int kMaxBytes = kMaxWideChars * sizeof(uint16_t); ++ FixedZeroedDataVector buffer(kMaxWideChars); ++ pdfium::span buffer_span = buffer.writable_span(); ++ int byte_length = pRuntime->GetFormFillEnv()->JS_appResponse( ++ swQuestion, swTitle, swDefault, swLabel, bPassword, ++ pdfium::as_writable_bytes(buffer_span)); ++ if (byte_length < 0 || byte_length > kMaxBytes) + return CJS_Result::Failure(JSMessage::kParamTooLongError); + ++ buffer_span = buffer_span.first( ++ std::min(kMaxWideChars, byte_length / sizeof(uint16_t))); + return CJS_Result::Success(pRuntime->NewString( +- WideString::FromUTF16LE(reinterpret_cast(pBuff.data()), +- nLengthBytes / sizeof(uint16_t)) ++ WideString::FromUTF16LE(buffer_span.data(), buffer_span.size()) + .AsStringView())); + } + +diff --git a/fxjs/cjs_app.h b/fxjs/cjs_app.h +index 524a7f02f..23b5c1fd5 100644 +--- a/fxjs/cjs_app.h ++++ b/fxjs/cjs_app.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -19,7 +19,7 @@ class GlobalTimer; + + class CJS_App final : public CJS_Object { + public: +- static int GetObjDefnID(); ++ static uint32_t GetObjDefnID(); + static void DefineJSObjects(CFXJS_Engine* pEngine); + + CJS_App(v8::Local pObject, CJS_Runtime* pRuntime); +@@ -66,7 +66,7 @@ class CJS_App final : public CJS_Object { + JS_STATIC_METHOD(setTimeOut, CJS_App) + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const char kName[]; + static const JSPropertySpec PropertySpecs[]; + static const JSMethodSpec MethodSpecs[]; +diff --git a/fxjs/cjs_border.cpp b/fxjs/cjs_border.cpp +index 35204d771..b8edd09ca 100644 +--- a/fxjs/cjs_border.cpp ++++ b/fxjs/cjs_border.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,7 +13,7 @@ const JSConstSpec CJS_Border::ConstSpecs[] = { + {"i", JSConstSpec::String, 0, "inset"}, + {"u", JSConstSpec::String, 0, "underline"}}; + +-int CJS_Border::ObjDefnID = -1; ++uint32_t CJS_Border::ObjDefnID = 0; + + // static + void CJS_Border::DefineJSObjects(CFXJS_Engine* pEngine) { +diff --git a/fxjs/cjs_border.h b/fxjs/cjs_border.h +index 0a306f7f8..29a925148 100644 +--- a/fxjs/cjs_border.h ++++ b/fxjs/cjs_border.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,7 +16,7 @@ class CJS_Border final : public CJS_Object { + CJS_Border() = delete; + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const JSConstSpec ConstSpecs[]; + }; + +diff --git a/fxjs/cjs_color.cpp b/fxjs/cjs_color.cpp +index 2b75e7527..22b31fcac 100644 +--- a/fxjs/cjs_color.cpp ++++ b/fxjs/cjs_color.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,10 +11,11 @@ + + #include "core/fxge/cfx_color.h" + #include "fxjs/cjs_event_context.h" +-#include "fxjs/cjs_eventrecorder.h" + #include "fxjs/cjs_object.h" + #include "fxjs/cjs_runtime.h" ++#include "fxjs/fxv8.h" + #include "fxjs/js_define.h" ++#include "v8/include/v8-container.h" + + const JSPropertySpec CJS_Color::PropertySpecs[] = { + {"black", get_black_static, set_black_static}, +@@ -33,11 +34,11 @@ const JSPropertySpec CJS_Color::PropertySpecs[] = { + const JSMethodSpec CJS_Color::MethodSpecs[] = {{"convert", convert_static}, + {"equal", equal_static}}; + +-int CJS_Color::ObjDefnID = -1; ++uint32_t CJS_Color::ObjDefnID = 0; + const char CJS_Color::kName[] = "color"; + + // static +-int CJS_Color::GetObjDefnID() { ++uint32_t CJS_Color::GetObjDefnID() { + return ObjDefnID; + } + +@@ -54,23 +55,23 @@ v8::Local CJS_Color::ConvertPWLColorToArray(CJS_Runtime* pRuntime, + const CFX_Color& color) { + v8::Local array; + switch (color.nColorType) { +- case CFX_Color::kTransparent: ++ case CFX_Color::Type::kTransparent: + array = pRuntime->NewArray(); + pRuntime->PutArrayElement(array, 0, pRuntime->NewString("T")); + break; +- case CFX_Color::kGray: ++ case CFX_Color::Type::kGray: + array = pRuntime->NewArray(); + pRuntime->PutArrayElement(array, 0, pRuntime->NewString("G")); + pRuntime->PutArrayElement(array, 1, pRuntime->NewNumber(color.fColor1)); + break; +- case CFX_Color::kRGB: ++ case CFX_Color::Type::kRGB: + array = pRuntime->NewArray(); + pRuntime->PutArrayElement(array, 0, pRuntime->NewString("RGB")); + pRuntime->PutArrayElement(array, 1, pRuntime->NewNumber(color.fColor1)); + pRuntime->PutArrayElement(array, 2, pRuntime->NewNumber(color.fColor2)); + pRuntime->PutArrayElement(array, 3, pRuntime->NewNumber(color.fColor3)); + break; +- case CFX_Color::kCMYK: ++ case CFX_Color::Type::kCMYK: + array = pRuntime->NewArray(); + pRuntime->PutArrayElement(array, 0, pRuntime->NewString("CMYK")); + pRuntime->PutArrayElement(array, 1, pRuntime->NewNumber(color.fColor1)); +@@ -85,14 +86,14 @@ v8::Local CJS_Color::ConvertPWLColorToArray(CJS_Runtime* pRuntime, + // static + CFX_Color CJS_Color::ConvertArrayToPWLColor(CJS_Runtime* pRuntime, + v8::Local array) { +- int nArrayLen = pRuntime->GetArrayLength(array); +- if (nArrayLen < 1) ++ size_t nArrayLen = pRuntime->GetArrayLength(array); ++ if (nArrayLen == 0) + return CFX_Color(); + + WideString sSpace = + pRuntime->ToWideString(pRuntime->GetArrayElement(array, 0)); + if (sSpace.EqualsASCII("T")) +- return CFX_Color(CFX_Color::kTransparent); ++ return CFX_Color(CFX_Color::Type::kTransparent); + + float d1 = 0; + if (nArrayLen > 1) { +@@ -100,7 +101,7 @@ CFX_Color CJS_Color::ConvertArrayToPWLColor(CJS_Runtime* pRuntime, + pRuntime->ToDouble(pRuntime->GetArrayElement(array, 1))); + } + if (sSpace.EqualsASCII("G")) +- return CFX_Color(CFX_Color::kGray, d1); ++ return CFX_Color(CFX_Color::Type::kGray, d1); + + float d2 = 0; + float d3 = 0; +@@ -113,7 +114,7 @@ CFX_Color CJS_Color::ConvertArrayToPWLColor(CJS_Runtime* pRuntime, + pRuntime->ToDouble(pRuntime->GetArrayElement(array, 3))); + } + if (sSpace.EqualsASCII("RGB")) +- return CFX_Color(CFX_Color::kRGB, d1, d2, d3); ++ return CFX_Color(CFX_Color::Type::kRGB, d1, d2, d3); + + float d4 = 0; + if (nArrayLen > 4) { +@@ -121,25 +122,25 @@ CFX_Color CJS_Color::ConvertArrayToPWLColor(CJS_Runtime* pRuntime, + pRuntime->ToDouble(pRuntime->GetArrayElement(array, 4))); + } + if (sSpace.EqualsASCII("CMYK")) +- return CFX_Color(CFX_Color::kCMYK, d1, d2, d3, d4); ++ return CFX_Color(CFX_Color::Type::kCMYK, d1, d2, d3, d4); + + return CFX_Color(); + } + + CJS_Color::CJS_Color(v8::Local pObject, CJS_Runtime* pRuntime) + : CJS_Object(pObject, pRuntime), +- m_crTransparent(CFX_Color::kTransparent), +- m_crBlack(CFX_Color::kGray, 0), +- m_crWhite(CFX_Color::kGray, 1), +- m_crRed(CFX_Color::kRGB, 1, 0, 0), +- m_crGreen(CFX_Color::kRGB, 0, 1, 0), +- m_crBlue(CFX_Color::kRGB, 0, 0, 1), +- m_crCyan(CFX_Color::kCMYK, 1, 0, 0, 0), +- m_crMagenta(CFX_Color::kCMYK, 0, 1, 0, 0), +- m_crYellow(CFX_Color::kCMYK, 0, 0, 1, 0), +- m_crDKGray(CFX_Color::kGray, 0.25), +- m_crGray(CFX_Color::kGray, 0.5), +- m_crLTGray(CFX_Color::kGray, 0.75) {} ++ m_crTransparent(CFX_Color::Type::kTransparent), ++ m_crBlack(CFX_Color::Type::kGray, 0), ++ m_crWhite(CFX_Color::Type::kGray, 1), ++ m_crRed(CFX_Color::Type::kRGB, 1, 0, 0), ++ m_crGreen(CFX_Color::Type::kRGB, 0, 1, 0), ++ m_crBlue(CFX_Color::Type::kRGB, 0, 0, 1), ++ m_crCyan(CFX_Color::Type::kCMYK, 1, 0, 0, 0), ++ m_crMagenta(CFX_Color::Type::kCMYK, 0, 1, 0, 0), ++ m_crYellow(CFX_Color::Type::kCMYK, 0, 0, 1, 0), ++ m_crDKGray(CFX_Color::Type::kGray, 0.25), ++ m_crGray(CFX_Color::Type::kGray, 0.5), ++ m_crLTGray(CFX_Color::Type::kGray, 0.75) {} + + CJS_Color::~CJS_Color() = default; + +@@ -273,19 +274,19 @@ CJS_Result CJS_Color::convert(CJS_Runtime* pRuntime, + if (params.size() < 2) + return CJS_Result::Failure(JSMessage::kParamError); + +- if (params[0].IsEmpty() || !params[0]->IsArray()) ++ if (!fxv8::IsArray(params[0])) + return CJS_Result::Failure(JSMessage::kTypeError); + + WideString sDestSpace = pRuntime->ToWideString(params[1]); +- int nColorType = CFX_Color::kTransparent; ++ CFX_Color::Type nColorType = CFX_Color::Type::kTransparent; + if (sDestSpace.EqualsASCII("T")) +- nColorType = CFX_Color::kTransparent; ++ nColorType = CFX_Color::Type::kTransparent; + else if (sDestSpace.EqualsASCII("G")) +- nColorType = CFX_Color::kGray; ++ nColorType = CFX_Color::Type::kGray; + else if (sDestSpace.EqualsASCII("RGB")) +- nColorType = CFX_Color::kRGB; ++ nColorType = CFX_Color::Type::kRGB; + else if (sDestSpace.EqualsASCII("CMYK")) +- nColorType = CFX_Color::kCMYK; ++ nColorType = CFX_Color::Type::kCMYK; + + CFX_Color color = + ConvertArrayToPWLColor(pRuntime, pRuntime->ToArray(params[0])); +@@ -302,10 +303,8 @@ CJS_Result CJS_Color::equal(CJS_Runtime* pRuntime, + if (params.size() < 2) + return CJS_Result::Failure(JSMessage::kParamError); + +- if (params[0].IsEmpty() || !params[0]->IsArray() || params[1].IsEmpty() || +- !params[1]->IsArray()) { ++ if (!fxv8::IsArray(params[0]) || !fxv8::IsArray(params[1])) + return CJS_Result::Failure(JSMessage::kTypeError); +- } + + CFX_Color color1 = + ConvertArrayToPWLColor(pRuntime, pRuntime->ToArray(params[0])); +@@ -313,7 +312,7 @@ CJS_Result CJS_Color::equal(CJS_Runtime* pRuntime, + ConvertArrayToPWLColor(pRuntime, pRuntime->ToArray(params[1])); + + // Relies on higher values having more components. +- int32_t best = std::max(color1.nColorType, color2.nColorType); ++ CFX_Color::Type best = std::max(color1.nColorType, color2.nColorType); + return CJS_Result::Success(pRuntime->NewBoolean( + color1.ConvertColorType(best) == color2.ConvertColorType(best))); + } +diff --git a/fxjs/cjs_color.h b/fxjs/cjs_color.h +index 03aa7357a..b768709dd 100644 +--- a/fxjs/cjs_color.h ++++ b/fxjs/cjs_color.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,12 +9,13 @@ + + #include + ++#include "core/fxge/cfx_color.h" + #include "fxjs/cjs_object.h" + #include "fxjs/js_define.h" + + class CJS_Color final : public CJS_Object { + public: +- static int GetObjDefnID(); ++ static uint32_t GetObjDefnID(); + static void DefineJSObjects(CFXJS_Engine* pEngine); + static v8::Local ConvertPWLColorToArray(CJS_Runtime* pRuntime, + const CFX_Color& color); +@@ -41,7 +42,7 @@ class CJS_Color final : public CJS_Object { + JS_STATIC_METHOD(equal, CJS_Color) + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const char kName[]; + static const JSPropertySpec PropertySpecs[]; + static const JSMethodSpec MethodSpecs[]; +diff --git a/fxjs/cjs_console.cpp b/fxjs/cjs_console.cpp +index c696bbdcf..91c2ca01f 100644 +--- a/fxjs/cjs_console.cpp ++++ b/fxjs/cjs_console.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,7 +9,6 @@ + #include + + #include "fxjs/cjs_event_context.h" +-#include "fxjs/cjs_eventrecorder.h" + #include "fxjs/cjs_object.h" + #include "fxjs/js_define.h" + +@@ -18,11 +17,11 @@ const JSMethodSpec CJS_Console::MethodSpecs[] = {{"clear", clear_static}, + {"println", println_static}, + {"show", show_static}}; + +-int CJS_Console::ObjDefnID = -1; ++uint32_t CJS_Console::ObjDefnID = 0; + const char CJS_Console::kName[] = "console"; + + // static +-int CJS_Console::GetObjDefnID() { ++uint32_t CJS_Console::GetObjDefnID() { + return ObjDefnID; + } + +diff --git a/fxjs/cjs_console.h b/fxjs/cjs_console.h +index 2bc118a40..5608d55a4 100644 +--- a/fxjs/cjs_console.h ++++ b/fxjs/cjs_console.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ + + class CJS_Console final : public CJS_Object { + public: +- static int GetObjDefnID(); ++ static uint32_t GetObjDefnID(); + static void DefineJSObjects(CFXJS_Engine* pEngine); + + CJS_Console(v8::Local pObject, CJS_Runtime* pRuntime); +@@ -26,7 +26,7 @@ class CJS_Console final : public CJS_Object { + JS_STATIC_METHOD(show, CJS_Console) + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const char kName[]; + static const JSMethodSpec MethodSpecs[]; + +diff --git a/fxjs/cjs_delaydata.cpp b/fxjs/cjs_delaydata.cpp +index d7e1f7800..12b34fa75 100644 +--- a/fxjs/cjs_delaydata.cpp ++++ b/fxjs/cjs_delaydata.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,4 +9,4 @@ + CJS_DelayData::CJS_DelayData(FIELD_PROP prop, int idx, const WideString& name) + : eProp(prop), nControlIndex(idx), sFieldName(name) {} + +-CJS_DelayData::~CJS_DelayData() {} ++CJS_DelayData::~CJS_DelayData() = default; +diff --git a/fxjs/cjs_delaydata.h b/fxjs/cjs_delaydata.h +index f4c782014..861dbabf3 100644 +--- a/fxjs/cjs_delaydata.h ++++ b/fxjs/cjs_delaydata.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -20,9 +20,9 @@ struct CJS_DelayData { + + FIELD_PROP eProp; + int nControlIndex; ++ int32_t num = 0; ++ bool b = false; + WideString sFieldName; +- int32_t num; +- bool b; + ByteString bytestring; + WideString widestring; + CFX_FloatRect rect; +diff --git a/fxjs/cjs_display.cpp b/fxjs/cjs_display.cpp +index 71d6c024f..0e0ee2697 100644 +--- a/fxjs/cjs_display.cpp ++++ b/fxjs/cjs_display.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,7 +12,7 @@ const JSConstSpec CJS_Display::ConstSpecs[] = { + {"noPrint", JSConstSpec::Number, 2, 0}, + {"noView", JSConstSpec::Number, 3, 0}}; + +-int CJS_Display::ObjDefnID = -1; ++uint32_t CJS_Display::ObjDefnID = 0; + + // static + void CJS_Display::DefineJSObjects(CFXJS_Engine* pEngine) { +diff --git a/fxjs/cjs_display.h b/fxjs/cjs_display.h +index 59b6e9c01..7de536855 100644 +--- a/fxjs/cjs_display.h ++++ b/fxjs/cjs_display.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,7 +16,7 @@ class CJS_Display final : public CJS_Object { + CJS_Display() = delete; + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const JSConstSpec ConstSpecs[]; + }; + +diff --git a/fxjs/cjs_document.cpp b/fxjs/cjs_document.cpp +index 9294f179e..5a842cec1 100644 +--- a/fxjs/cjs_document.cpp ++++ b/fxjs/cjs_document.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,18 +6,22 @@ + + #include "fxjs/cjs_document.h" + ++#include ++ + #include + ++#include "constants/access_permissions.h" ++#include "core/fpdfapi/page/cpdf_pageimagecache.h" + #include "core/fpdfapi/page/cpdf_pageobject.h" + #include "core/fpdfapi/page/cpdf_textobject.h" + #include "core/fpdfapi/parser/cpdf_array.h" + #include "core/fpdfapi/parser/cpdf_dictionary.h" + #include "core/fpdfapi/parser/cpdf_name.h" + #include "core/fpdfapi/parser/cpdf_string.h" +-#include "core/fpdfapi/render/cpdf_pagerendercache.h" + #include "core/fpdfdoc/cpdf_interactiveform.h" + #include "core/fpdfdoc/cpdf_nametree.h" + #include "fpdfsdk/cpdfsdk_annotiteration.h" ++#include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fpdfsdk/cpdfsdk_interactiveform.h" + #include "fpdfsdk/cpdfsdk_pageview.h" + #include "fxjs/cjs_annot.h" +@@ -27,6 +31,8 @@ + #include "fxjs/cjs_field.h" + #include "fxjs/cjs_icon.h" + #include "fxjs/js_resources.h" ++#include "third_party/base/check.h" ++#include "v8/include/v8-container.h" + + const JSPropertySpec CJS_Document::PropertySpecs[] = { + {"ADBE", get_ADBE_static, set_ADBE_static}, +@@ -108,11 +114,11 @@ const JSMethodSpec CJS_Document::MethodSpecs[] = { + {"submitForm", submitForm_static}, + {"syncAnnotScan", syncAnnotScan_static}}; + +-int CJS_Document::ObjDefnID = -1; ++uint32_t CJS_Document::ObjDefnID = 0; + const char CJS_Document::kName[] = "Document"; + + // static +-int CJS_Document::GetObjDefnID() { ++uint32_t CJS_Document::GetObjDefnID() { + return ObjDefnID; + } + +@@ -255,8 +261,8 @@ CJS_Result CJS_Document::getField( + if (pFieldObj.IsEmpty()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- auto* pJSField = +- static_cast(CFXJS_Engine::GetObjectPrivate(pFieldObj)); ++ auto* pJSField = static_cast( ++ CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pFieldObj)); + if (!pJSField) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +@@ -340,8 +346,8 @@ CJS_Result CJS_Document::mailDoc( + cMsg = pRuntime->ToWideString(newParams[5]); + + pRuntime->BeginBlock(); +- m_pFormFillEnv->JS_docmailForm(nullptr, 0, bUI, cTo, cSubject, cCc, cBcc, +- cMsg); ++ m_pFormFillEnv->JS_docmailForm(pdfium::span(), bUI, cTo, ++ cSubject, cCc, cBcc, cMsg); + pRuntime->EndBlock(); + return CJS_Result::Success(); + } +@@ -354,7 +360,9 @@ CJS_Result CJS_Document::mailForm( + const std::vector>& params) { + if (!m_pFormFillEnv) + return CJS_Result::Failure(JSMessage::kBadObjectError); +- if (!m_pFormFillEnv->GetPermissions(FPDFPERM_EXTRACT_ACCESS)) ++ ++ using pdfium::access_permissions::kExtractForAccessibility; ++ if (!m_pFormFillEnv->HasPermissions(kExtractForAccessibility)) + return CJS_Result::Failure(JSMessage::kPermissionError); + + CPDFSDK_InteractiveForm* pInteractiveForm = GetSDKInteractiveForm(); +@@ -389,10 +397,9 @@ CJS_Result CJS_Document::mailForm( + if (IsExpandedParamKnown(newParams[5])) + cMsg = pRuntime->ToWideString(newParams[5]); + +- std::vector mutable_buf(sTextBuf.begin(), sTextBuf.end()); + pRuntime->BeginBlock(); +- m_pFormFillEnv->JS_docmailForm(mutable_buf.data(), mutable_buf.size(), bUI, +- cTo, cSubject, cCc, cBcc, cMsg); ++ m_pFormFillEnv->JS_docmailForm(sTextBuf.raw_span(), bUI, cTo, cSubject, cCc, ++ cBcc, cMsg); + pRuntime->EndBlock(); + return CJS_Result::Success(); + } +@@ -439,8 +446,7 @@ CJS_Result CJS_Document::print( + if (!m_pFormFillEnv) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- CJS_EventRecorder* pHandler = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pHandler = pRuntime->GetCurrentEventContext(); + if (!pHandler->IsUserGesture()) + return CJS_Result::Failure(JSMessage::kUserGestureRequiredError); + +@@ -460,30 +466,30 @@ CJS_Result CJS_Document::removeField( + if (!m_pFormFillEnv) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- if (!(m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY) || +- m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM))) { ++ if (!m_pFormFillEnv->HasPermissions( ++ pdfium::access_permissions::kModifyContent | ++ pdfium::access_permissions::kModifyAnnotation)) { + return CJS_Result::Failure(JSMessage::kPermissionError); + } + + WideString sFieldName = pRuntime->ToWideString(params[0]); + CPDFSDK_InteractiveForm* pInteractiveForm = GetSDKInteractiveForm(); +- std::vector> widgets; ++ std::vector> widgets; + pInteractiveForm->GetWidgets(sFieldName, &widgets); + if (widgets.empty()) + return CJS_Result::Success(); + +- for (const auto& pAnnot : widgets) { +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot.Get()); ++ for (const auto& pWidget : widgets) { + if (!pWidget) + continue; + + IPDF_Page* pPage = pWidget->GetPage(); +- ASSERT(pPage); ++ DCHECK(pPage); + + // If there is currently no pageview associated with the page being used + // do not create one. We may be in the process of tearing down the document + // and creating a new pageview at this point will cause bad things. +- CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(pPage, false); ++ CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(pPage); + if (!pPageView) + continue; + +@@ -506,15 +512,17 @@ CJS_Result CJS_Document::resetForm( + const std::vector>& params) { + if (!m_pFormFillEnv) + return CJS_Result::Failure(JSMessage::kBadObjectError); +- if (!(m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY) || +- m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM) || +- m_pFormFillEnv->GetPermissions(FPDFPERM_FILL_FORM))) { ++ ++ if (!m_pFormFillEnv->HasPermissions( ++ pdfium::access_permissions::kModifyContent | ++ pdfium::access_permissions::kModifyAnnotation | ++ pdfium::access_permissions::kFillForm)) { + return CJS_Result::Failure(JSMessage::kPermissionError); + } + + CPDF_InteractiveForm* pPDFForm = GetCoreInteractiveForm(); + if (params.empty()) { +- pPDFForm->ResetForm(NotificationOption::kNotify); ++ pPDFForm->ResetForm(); + m_pFormFillEnv->SetChangeMark(); + return CJS_Result::Success(); + } +@@ -531,12 +539,13 @@ CJS_Result CJS_Document::resetForm( + for (size_t i = 0; i < pRuntime->GetArrayLength(array); ++i) { + WideString swVal = + pRuntime->ToWideString(pRuntime->GetArrayElement(array, i)); +- for (int j = 0, jsz = pPDFForm->CountFields(swVal); j < jsz; ++j) ++ const size_t jsz = pPDFForm->CountFields(swVal); ++ for (size_t j = 0; j < jsz; ++j) + aFields.push_back(pPDFForm->GetField(j, swVal)); + } + + if (!aFields.empty()) { +- pPDFForm->ResetForm(aFields, true, NotificationOption::kNotify); ++ pPDFForm->ResetForm(aFields, true); + m_pFormFillEnv->SetChangeMark(); + } + +@@ -565,8 +574,7 @@ CJS_Result CJS_Document::submitForm( + if (!m_pFormFillEnv) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- CJS_EventRecorder* pHandler = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pHandler = pRuntime->GetCurrentEventContext(); + if (!pHandler->IsUserGesture()) + return CJS_Result::Failure(JSMessage::kUserGestureRequiredError); + +@@ -597,7 +605,7 @@ CJS_Result CJS_Document::submitForm( + if (pRuntime->GetArrayLength(aFields) == 0 && bEmpty) { + if (pPDFForm->CheckRequiredFields(nullptr, true)) { + pRuntime->BeginBlock(); +- GetSDKInteractiveForm()->SubmitForm(strURL, false); ++ GetSDKInteractiveForm()->SubmitForm(strURL); + pRuntime->EndBlock(); + } + return CJS_Result::Success(); +@@ -607,7 +615,8 @@ CJS_Result CJS_Document::submitForm( + for (size_t i = 0; i < pRuntime->GetArrayLength(aFields); ++i) { + WideString sName = + pRuntime->ToWideString(pRuntime->GetArrayElement(aFields, i)); +- for (int j = 0, jsz = pPDFForm->CountFields(sName); j < jsz; ++j) { ++ const size_t jsz = pPDFForm->CountFields(sName); ++ for (size_t j = 0; j < jsz; ++j) { + CPDF_FormField* pField = pPDFForm->GetField(j, sName); + if (!bEmpty && pField->GetValue().IsEmpty()) + continue; +@@ -643,14 +652,16 @@ CJS_Result CJS_Document::get_author(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Document::set_author(CJS_Runtime* pRuntime, + v8::Local vp) { +- return setPropertyInternal(pRuntime, vp, "Author"); ++ // Read-only. ++ return CJS_Result::Success(); + } + + CJS_Result CJS_Document::get_info(CJS_Runtime* pRuntime) { + if (!m_pFormFillEnv) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- const auto* pDictionary = m_pFormFillEnv->GetPDFDocument()->GetInfo(); ++ RetainPtr pDictionary = ++ m_pFormFillEnv->GetPDFDocument()->GetInfo(); + if (!pDictionary) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +@@ -685,11 +696,10 @@ CJS_Result CJS_Document::get_info(CJS_Runtime* pRuntime) { + pRuntime->NewString(cwTrapped.AsStringView())); + + // PutObjectProperty() calls below may re-enter JS and change info dict. +- auto pCopy = pDictionary->Clone(); +- CPDF_DictionaryLocker locker(ToDictionary(pCopy.Get())); ++ CPDF_DictionaryLocker locker(ToDictionary(pDictionary->Clone())); + for (const auto& it : locker) { + const ByteString& bsKey = it.first; +- CPDF_Object* pValueObj = it.second.Get(); ++ const RetainPtr& pValueObj = it.second; + if (pValueObj->IsString() || pValueObj->IsName()) { + pRuntime->PutObjectProperty( + pObj, bsKey.AsStringView(), +@@ -716,38 +726,23 @@ CJS_Result CJS_Document::getPropertyInternal(CJS_Runtime* pRuntime, + if (!m_pFormFillEnv) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- CPDF_Dictionary* pDictionary = m_pFormFillEnv->GetPDFDocument()->GetInfo(); ++ RetainPtr pDictionary = ++ m_pFormFillEnv->GetPDFDocument()->GetInfo(); + if (!pDictionary) + return CJS_Result::Failure(JSMessage::kBadObjectError); ++ + return CJS_Result::Success(pRuntime->NewString( + pDictionary->GetUnicodeTextFor(propName).AsStringView())); + } + +-CJS_Result CJS_Document::setPropertyInternal(CJS_Runtime* pRuntime, +- v8::Local vp, +- const ByteString& propName) { +- if (!m_pFormFillEnv) +- return CJS_Result::Failure(JSMessage::kBadObjectError); +- +- CPDF_Dictionary* pDictionary = m_pFormFillEnv->GetPDFDocument()->GetInfo(); +- if (!pDictionary) +- return CJS_Result::Failure(JSMessage::kBadObjectError); +- +- if (!m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY)) +- return CJS_Result::Failure(JSMessage::kPermissionError); +- +- pDictionary->SetNewFor(propName, pRuntime->ToWideString(vp)); +- m_pFormFillEnv->SetChangeMark(); +- return CJS_Result::Success(); +-} +- + CJS_Result CJS_Document::get_creation_date(CJS_Runtime* pRuntime) { + return getPropertyInternal(pRuntime, "CreationDate"); + } + + CJS_Result CJS_Document::set_creation_date(CJS_Runtime* pRuntime, + v8::Local vp) { +- return setPropertyInternal(pRuntime, vp, "CreationDate"); ++ // Read-only. ++ return CJS_Result::Success(); + } + + CJS_Result CJS_Document::get_creator(CJS_Runtime* pRuntime) { +@@ -756,7 +751,8 @@ CJS_Result CJS_Document::get_creator(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Document::set_creator(CJS_Runtime* pRuntime, + v8::Local vp) { +- return setPropertyInternal(pRuntime, vp, "Creator"); ++ // Read-only. ++ return CJS_Result::Success(); + } + + CJS_Result CJS_Document::get_delay(CJS_Runtime* pRuntime) { +@@ -769,7 +765,9 @@ CJS_Result CJS_Document::set_delay(CJS_Runtime* pRuntime, + v8::Local vp) { + if (!m_pFormFillEnv) + return CJS_Result::Failure(JSMessage::kBadObjectError); +- if (!m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY)) ++ ++ using pdfium::access_permissions::kModifyContent; ++ if (!m_pFormFillEnv->HasPermissions(kModifyContent)) + return CJS_Result::Failure(JSMessage::kPermissionError); + + m_bDelay = pRuntime->ToBoolean(vp); +@@ -792,7 +790,8 @@ CJS_Result CJS_Document::get_keywords(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Document::set_keywords(CJS_Runtime* pRuntime, + v8::Local vp) { +- return setPropertyInternal(pRuntime, vp, "Keywords"); ++ // Read-only. ++ return CJS_Result::Success(); + } + + CJS_Result CJS_Document::get_mod_date(CJS_Runtime* pRuntime) { +@@ -801,7 +800,8 @@ CJS_Result CJS_Document::get_mod_date(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Document::set_mod_date(CJS_Runtime* pRuntime, + v8::Local vp) { +- return setPropertyInternal(pRuntime, vp, "ModDate"); ++ // Read-only. ++ return CJS_Result::Success(); + } + + CJS_Result CJS_Document::get_producer(CJS_Runtime* pRuntime) { +@@ -810,7 +810,8 @@ CJS_Result CJS_Document::get_producer(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Document::set_producer(CJS_Runtime* pRuntime, + v8::Local vp) { +- return setPropertyInternal(pRuntime, vp, "Producer"); ++ // Read-only. ++ return CJS_Result::Success(); + } + + CJS_Result CJS_Document::get_subject(CJS_Runtime* pRuntime) { +@@ -819,7 +820,8 @@ CJS_Result CJS_Document::get_subject(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Document::set_subject(CJS_Runtime* pRuntime, + v8::Local vp) { +- return setPropertyInternal(pRuntime, vp, "Subject"); ++ // Read-only. ++ return CJS_Result::Success(); + } + + CJS_Result CJS_Document::get_title(CJS_Runtime* pRuntime) { +@@ -830,9 +832,8 @@ CJS_Result CJS_Document::get_title(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Document::set_title(CJS_Runtime* pRuntime, + v8::Local vp) { +- if (!m_pFormFillEnv) +- return CJS_Result::Failure(JSMessage::kBadObjectError); +- return setPropertyInternal(pRuntime, vp, "Title"); ++ // Read-only. ++ return CJS_Result::Success(); + } + + CJS_Result CJS_Document::get_num_pages(CJS_Runtime* pRuntime) { +@@ -935,9 +936,10 @@ CJS_Result CJS_Document::get_document_file_name(CJS_Runtime* pRuntime) { + if (wsFilePath[i - 1] == L'\\' || wsFilePath[i - 1] == L'/') + break; + } +- if (i > 0 && i < wsFilePath.GetLength()) +- return CJS_Result::Success(pRuntime->NewString(wsFilePath.c_str() + i)); +- ++ if (i > 0 && i < wsFilePath.GetLength()) { ++ return CJS_Result::Success( ++ pRuntime->NewString(wsFilePath.AsStringView().Substr(i))); ++ } + return CJS_Result::Success(pRuntime->NewString("")); + } + +@@ -1005,13 +1007,13 @@ CJS_Result CJS_Document::getAnnot( + + int nPageNo = pRuntime->ToInt32(params[0]); + WideString swAnnotName = pRuntime->ToWideString(params[1]); +- CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(nPageNo); ++ CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageViewAtIndex(nPageNo); + if (!pPageView) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- CPDFSDK_AnnotIteration annotIteration(pPageView, false); ++ CPDFSDK_AnnotIteration annot_iteration(pPageView); + CPDFSDK_BAAnnot* pSDKBAAnnot = nullptr; +- for (const auto& pSDKAnnotCur : annotIteration) { ++ for (const auto& pSDKAnnotCur : annot_iteration) { + auto* pBAAnnot = pSDKAnnotCur->AsBAAnnot(); + if (pBAAnnot && pBAAnnot->GetAnnotName() == swAnnotName) { + pSDKBAAnnot = pBAAnnot; +@@ -1026,8 +1028,8 @@ CJS_Result CJS_Document::getAnnot( + if (pObj.IsEmpty()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- auto* pJS_Annot = +- static_cast(CFXJS_Engine::GetObjectPrivate(pObj)); ++ auto* pJS_Annot = static_cast( ++ CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj)); + if (!pJS_Annot) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +@@ -1047,12 +1049,12 @@ CJS_Result CJS_Document::getAnnots( + int nPageNo = m_pFormFillEnv->GetPageCount(); + v8::Local annots = pRuntime->NewArray(); + for (int i = 0; i < nPageNo; ++i) { +- CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageView(i); ++ CPDFSDK_PageView* pPageView = m_pFormFillEnv->GetPageViewAtIndex(i); + if (!pPageView) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- CPDFSDK_AnnotIteration annotIteration(pPageView, false); +- for (const auto& pSDKAnnotCur : annotIteration) { ++ CPDFSDK_AnnotIteration annot_iteration(pPageView); ++ for (const auto& pSDKAnnotCur : annot_iteration) { + if (!pSDKAnnotCur) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +@@ -1061,8 +1063,8 @@ CJS_Result CJS_Document::getAnnots( + if (pObj.IsEmpty()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- auto* pJS_Annot = +- static_cast(CFXJS_Engine::GetObjectPrivate(pObj)); ++ auto* pJS_Annot = static_cast( ++ CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj)); + pJS_Annot->SetSDKAnnot(pSDKAnnotCur->AsBAAnnot()); + pRuntime->PutArrayElement( + annots, i, +@@ -1107,7 +1109,7 @@ CJS_Result CJS_Document::addIcon( + return CJS_Result::Failure(JSMessage::kTypeError); + + v8::Local pObj = pRuntime->ToObject(params[1]); +- if (!JSGetObject(pObj)) ++ if (!JSGetObject(pRuntime->GetIsolate(), pObj)) + return CJS_Result::Failure(JSMessage::kTypeError); + + WideString swIconName = pRuntime->ToWideString(params[0]); +@@ -1129,8 +1131,8 @@ CJS_Result CJS_Document::get_icons(CJS_Runtime* pRuntime) { + if (pObj.IsEmpty()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- auto* pJS_Icon = +- static_cast(CFXJS_Engine::GetObjectPrivate(pObj)); ++ auto* pJS_Icon = static_cast( ++ CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj)); + pJS_Icon->SetIconName(name); + pRuntime->PutArrayElement(Icons, i++, + pJS_Icon +@@ -1161,7 +1163,8 @@ CJS_Result CJS_Document::getIcon( + if (pObj.IsEmpty()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- auto* pJSIcon = static_cast(CFXJS_Engine::GetObjectPrivate(pObj)); ++ auto* pJSIcon = static_cast( ++ CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj)); + if (!pJSIcon) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +@@ -1198,9 +1201,10 @@ CJS_Result CJS_Document::calculateNow( + if (!m_pFormFillEnv) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- if (!(m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY) || +- m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM) || +- m_pFormFillEnv->GetPermissions(FPDFPERM_FILL_FORM))) { ++ if (!m_pFormFillEnv->HasPermissions( ++ pdfium::access_permissions::kModifyContent | ++ pdfium::access_permissions::kModifyAnnotation | ++ pdfium::access_permissions::kFillForm)) { + return CJS_Result::Failure(JSMessage::kPermissionError); + } + +@@ -1222,7 +1226,9 @@ CJS_Result CJS_Document::getPageNthWord( + const std::vector>& params) { + if (!m_pFormFillEnv) + return CJS_Result::Failure(JSMessage::kBadObjectError); +- if (!m_pFormFillEnv->GetPermissions(FPDFPERM_EXTRACT_ACCESS)) ++ ++ using pdfium::access_permissions::kExtractForAccessibility; ++ if (!m_pFormFillEnv->HasPermissions(kExtractForAccessibility)) + return CJS_Result::Failure(JSMessage::kPermissionError); + + // TODO(tsepez): check maximum allowable params. +@@ -1235,12 +1241,13 @@ CJS_Result CJS_Document::getPageNthWord( + if (nPageNo < 0 || nPageNo >= pDocument->GetPageCount()) + return CJS_Result::Failure(JSMessage::kValueError); + +- CPDF_Dictionary* pPageDict = pDocument->GetPageDictionary(nPageNo); ++ RetainPtr pPageDict = ++ pDocument->GetMutablePageDictionary(nPageNo); + if (!pPageDict) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- auto page = pdfium::MakeRetain(pDocument, pPageDict); +- page->SetRenderCache(pdfium::MakeUnique(page.Get())); ++ auto page = pdfium::MakeRetain(pDocument, std::move(pPageDict)); ++ page->AddPageImageCache(); + page->ParseContent(); + + int nWords = 0; +@@ -1267,8 +1274,11 @@ CJS_Result CJS_Document::getPageNthWordQuads( + const std::vector>& params) { + if (!m_pFormFillEnv) + return CJS_Result::Failure(JSMessage::kBadObjectError); +- if (!m_pFormFillEnv->GetPermissions(FPDFPERM_EXTRACT_ACCESS)) ++ ++ using pdfium::access_permissions::kExtractForAccessibility; ++ if (!m_pFormFillEnv->HasPermissions(kExtractForAccessibility)) + return CJS_Result::Failure(JSMessage::kBadObjectError); ++ + return CJS_Result::Failure(JSMessage::kNotSupportedError); + } + +@@ -1277,7 +1287,9 @@ CJS_Result CJS_Document::getPageNumWords( + const std::vector>& params) { + if (!m_pFormFillEnv) + return CJS_Result::Failure(JSMessage::kBadObjectError); +- if (!m_pFormFillEnv->GetPermissions(FPDFPERM_EXTRACT_ACCESS)) ++ ++ using pdfium::access_permissions::kExtractForAccessibility; ++ if (!m_pFormFillEnv->HasPermissions(kExtractForAccessibility)) + return CJS_Result::Failure(JSMessage::kPermissionError); + + int nPageNo = params.size() > 0 ? pRuntime->ToInt32(params[0]) : 0; +@@ -1285,12 +1297,13 @@ CJS_Result CJS_Document::getPageNumWords( + if (nPageNo < 0 || nPageNo >= pDocument->GetPageCount()) + return CJS_Result::Failure(JSMessage::kValueError); + +- CPDF_Dictionary* pPageDict = pDocument->GetPageDictionary(nPageNo); ++ RetainPtr pPageDict = ++ pDocument->GetMutablePageDictionary(nPageNo); + if (!pPageDict) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- auto page = pdfium::MakeRetain(pDocument, pPageDict); +- page->SetRenderCache(pdfium::MakeUnique(page.Get())); ++ auto page = pdfium::MakeRetain(pDocument, std::move(pPageDict)); ++ page->AddPageImageCache(); + page->ParseContent(); + + int nWords = 0; +@@ -1370,23 +1383,16 @@ CJS_Result CJS_Document::gotoNamedDest( + return CJS_Result::Failure(JSMessage::kBadObjectError); + + CPDF_Document* pDocument = m_pFormFillEnv->GetPDFDocument(); +- CPDF_NameTree nameTree(pDocument, "Dests"); +- CPDF_Array* destArray = +- nameTree.LookupNamedDest(pDocument, pRuntime->ToWideString(params[0])); +- if (!destArray) ++ RetainPtr dest_array = CPDF_NameTree::LookupNamedDest( ++ pDocument, pRuntime->ToByteString(params[0])); ++ if (!dest_array) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- CPDF_Dest dest(destArray); +- const CPDF_Array* arrayObject = dest.GetArray(); +- std::vector scrollPositionArray; +- if (arrayObject) { +- for (size_t i = 2; i < arrayObject->size(); i++) +- scrollPositionArray.push_back(arrayObject->GetNumberAt(i)); +- } ++ CPDF_Dest dest(std::move(dest_array)); ++ std::vector positions = dest.GetScrollPositionArray(); + pRuntime->BeginBlock(); + m_pFormFillEnv->DoGoToAction(dest.GetDestPageIndex(pDocument), +- dest.GetZoomMode(), scrollPositionArray.data(), +- scrollPositionArray.size()); ++ dest.GetZoomMode(), positions); + pRuntime->EndBlock(); + return CJS_Result::Success(); + } +diff --git a/fxjs/cjs_document.h b/fxjs/cjs_document.h +index 49448fc27..d410a5eba 100644 +--- a/fxjs/cjs_document.h ++++ b/fxjs/cjs_document.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -17,12 +17,11 @@ + + class CPDFSDK_InteractiveForm; + class CPDF_InteractiveForm; +-class CPDF_TextObject; + struct CJS_DelayData; + + class CJS_Document final : public CJS_Object, public Observable { + public: +- static int GetObjDefnID(); ++ static uint32_t GetObjDefnID(); + static void DefineJSObjects(CFXJS_Engine* pEngine); + + CJS_Document(v8::Local pObject, CJS_Runtime* pRuntime); +@@ -112,7 +111,7 @@ class CJS_Document final : public CJS_Object, public Observable { + JS_STATIC_METHOD(syncAnnotScan, CJS_Document) + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const char kName[]; + static const JSPropertySpec PropertySpecs[]; + static const JSMethodSpec MethodSpecs[]; +@@ -303,9 +302,6 @@ class CJS_Document final : public CJS_Object, public Observable { + + CJS_Result getPropertyInternal(CJS_Runtime* pRuntime, + const ByteString& propName); +- CJS_Result setPropertyInternal(CJS_Runtime* pRuntime, +- v8::Local vp, +- const ByteString& propName); + + CPDF_InteractiveForm* GetCoreInteractiveForm(); + CPDFSDK_InteractiveForm* GetSDKInteractiveForm(); +diff --git a/fxjs/cjs_event.cpp b/fxjs/cjs_event.cpp +index a16ee6c53..6b8903838 100644 +--- a/fxjs/cjs_event.cpp ++++ b/fxjs/cjs_event.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,6 @@ + #include "fxjs/cjs_event.h" + + #include "fxjs/cjs_event_context.h" +-#include "fxjs/cjs_eventrecorder.h" + #include "fxjs/cjs_field.h" + #include "fxjs/cjs_object.h" + #include "fxjs/js_define.h" +@@ -34,11 +33,11 @@ const JSPropertySpec CJS_Event::PropertySpecs[] = { + {"value", get_value_static, set_value_static}, + {"willCommit", get_will_commit_static, set_will_commit_static}}; + +-int CJS_Event::ObjDefnID = -1; ++uint32_t CJS_Event::ObjDefnID = 0; + const char CJS_Event::kName[] = "event"; + + // static +-int CJS_Event::GetObjDefnID() { ++uint32_t CJS_Event::GetObjDefnID() { + return ObjDefnID; + } + +@@ -55,27 +54,22 @@ CJS_Event::CJS_Event(v8::Local pObject, CJS_Runtime* pRuntime) + CJS_Event::~CJS_Event() = default; + + CJS_Result CJS_Event::get_change(CJS_Runtime* pRuntime) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + return CJS_Result::Success( + pRuntime->NewString(pEvent->Change().AsStringView())); + } + + CJS_Result CJS_Event::set_change(CJS_Runtime* pRuntime, + v8::Local vp) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); +- + if (vp->IsString()) { +- WideString& wChange = pEvent->Change(); +- wChange = pRuntime->ToWideString(vp); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); ++ pEvent->Change() = pRuntime->ToWideString(vp); + } + return CJS_Result::Success(); + } + + CJS_Result CJS_Event::get_change_ex(CJS_Runtime* pRuntime) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + return CJS_Result::Success( + pRuntime->NewString(pEvent->ChangeEx().AsStringView())); + } +@@ -86,8 +80,7 @@ CJS_Result CJS_Event::set_change_ex(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Event::get_commit_key(CJS_Runtime* pRuntime) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + return CJS_Result::Success(pRuntime->NewNumber(pEvent->CommitKey())); + } + +@@ -97,8 +90,7 @@ CJS_Result CJS_Event::set_commit_key(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Event::get_field_full(CJS_Runtime* pRuntime) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + if (pEvent->Name() != "Keystroke") + return CJS_Result::Failure(L"unrecognized event"); + +@@ -111,8 +103,7 @@ CJS_Result CJS_Event::set_field_full(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Event::get_key_down(CJS_Runtime* pRuntime) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + return CJS_Result::Success(pRuntime->NewBoolean(pEvent->KeyDown())); + } + +@@ -122,8 +113,7 @@ CJS_Result CJS_Event::set_key_down(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Event::get_modifier(CJS_Runtime* pRuntime) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + return CJS_Result::Success(pRuntime->NewBoolean(pEvent->Modifier())); + } + +@@ -133,8 +123,7 @@ CJS_Result CJS_Event::set_modifier(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Event::get_name(CJS_Runtime* pRuntime) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + return CJS_Result::Success(pRuntime->NewString(pEvent->Name())); + } + +@@ -143,14 +132,12 @@ CJS_Result CJS_Event::set_name(CJS_Runtime* pRuntime, v8::Local vp) { + } + + CJS_Result CJS_Event::get_rc(CJS_Runtime* pRuntime) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + return CJS_Result::Success(pRuntime->NewBoolean(pEvent->Rc())); + } + + CJS_Result CJS_Event::set_rc(CJS_Runtime* pRuntime, v8::Local vp) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + pEvent->Rc() = pRuntime->ToBoolean(vp); + return CJS_Result::Success(); + } +@@ -183,8 +170,7 @@ CJS_Result CJS_Event::set_rich_value(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Event::get_sel_end(CJS_Runtime* pRuntime) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + if (pEvent->Name() != "Keystroke") + return CJS_Result::Success(); + +@@ -193,8 +179,7 @@ CJS_Result CJS_Event::get_sel_end(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Event::set_sel_end(CJS_Runtime* pRuntime, + v8::Local vp) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + if (pEvent->Name() == "Keystroke") + pEvent->SetSelEnd(pRuntime->ToInt32(vp)); + +@@ -202,8 +187,7 @@ CJS_Result CJS_Event::set_sel_end(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Event::get_sel_start(CJS_Runtime* pRuntime) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + if (pEvent->Name() != "Keystroke") + return CJS_Result::Success(); + +@@ -212,8 +196,7 @@ CJS_Result CJS_Event::get_sel_start(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Event::set_sel_start(CJS_Runtime* pRuntime, + v8::Local vp) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + if (pEvent->Name() == "Keystroke") + pEvent->SetSelStart(pRuntime->ToInt32(vp)); + +@@ -221,8 +204,7 @@ CJS_Result CJS_Event::set_sel_start(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Event::get_shift(CJS_Runtime* pRuntime) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + return CJS_Result::Success(pRuntime->NewBoolean(pEvent->Shift())); + } + +@@ -256,8 +238,7 @@ CJS_Result CJS_Event::set_target(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Event::get_target_name(CJS_Runtime* pRuntime) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + return CJS_Result::Success( + pRuntime->NewString(pEvent->TargetName().AsStringView())); + } +@@ -268,8 +249,7 @@ CJS_Result CJS_Event::set_target_name(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Event::get_type(CJS_Runtime* pRuntime) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + return CJS_Result::Success(pRuntime->NewString(pEvent->Type())); + } + +@@ -278,8 +258,7 @@ CJS_Result CJS_Event::set_type(CJS_Runtime* pRuntime, v8::Local vp) { + } + + CJS_Result CJS_Event::get_value(CJS_Runtime* pRuntime) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + if (pEvent->Type() != "Field") + return CJS_Result::Failure(L"Bad event type."); + +@@ -292,8 +271,7 @@ CJS_Result CJS_Event::get_value(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Event::set_value(CJS_Runtime* pRuntime, + v8::Local vp) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + if (pEvent->Type() != "Field") + return CJS_Result::Failure(L"Bad event type."); + +@@ -311,8 +289,7 @@ CJS_Result CJS_Event::set_value(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Event::get_will_commit(CJS_Runtime* pRuntime) { +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + + return CJS_Result::Success(pRuntime->NewBoolean(pEvent->WillCommit())); + } +diff --git a/fxjs/cjs_event.h b/fxjs/cjs_event.h +index b2fa4dc5d..fc9bec2df 100644 +--- a/fxjs/cjs_event.h ++++ b/fxjs/cjs_event.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,7 +12,7 @@ + + class CJS_Event final : public CJS_Object { + public: +- static int GetObjDefnID(); ++ static uint32_t GetObjDefnID(); + static void DefineJSObjects(CFXJS_Engine* pEngine); + + CJS_Event(v8::Local pObject, CJS_Runtime* pRuntime); +@@ -40,7 +40,7 @@ class CJS_Event final : public CJS_Object { + JS_STATIC_PROP(willCommit, will_commit, CJS_Event) + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const char kName[]; + static const JSPropertySpec PropertySpecs[]; + +diff --git a/fxjs/cjs_event_context.cpp b/fxjs/cjs_event_context.cpp +index 27e8120ac..65eee9dd2 100644 +--- a/fxjs/cjs_event_context.cpp ++++ b/fxjs/cjs_event_context.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,27 +6,23 @@ + + #include "fxjs/cjs_event_context.h" + ++#include "core/fpdfdoc/cpdf_formfield.h" + #include "core/fxcrt/autorestorer.h" +-#include "fxjs/cjs_eventrecorder.h" ++#include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fxjs/cjs_field.h" + #include "fxjs/cjs_runtime.h" + #include "fxjs/js_define.h" + #include "fxjs/js_resources.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" ++#include "v8/include/v8-context.h" ++#include "v8/include/v8-isolate.h" + + CJS_EventContext::CJS_EventContext(CJS_Runtime* pRuntime) +- : m_pRuntime(pRuntime), +- m_pEventRecorder(pdfium::MakeUnique()) { +- ASSERT(pRuntime); +-} ++ : m_pRuntime(pRuntime), m_pFormFillEnv(pRuntime->GetFormFillEnv()) {} + + CJS_EventContext::~CJS_EventContext() = default; + +-CPDFSDK_FormFillEnvironment* CJS_EventContext::GetFormFillEnv() { +- return m_pRuntime->GetFormFillEnv(); +-} +- +-Optional CJS_EventContext::RunScript( ++absl::optional CJS_EventContext::RunScript( + const WideString& script) { + v8::Isolate::Scope isolate_scope(m_pRuntime->GetIsolate()); + v8::HandleScope handle_scope(m_pRuntime->GetIsolate()); +@@ -41,20 +37,19 @@ Optional CJS_EventContext::RunScript( + AutoRestorer restorer(&m_bBusy); + m_bBusy = true; + +- ASSERT(m_pEventRecorder->IsValid()); +- CJS_Runtime::FieldEvent event(m_pEventRecorder->TargetName(), +- m_pEventRecorder->EventType()); ++ DCHECK(IsValid()); ++ CJS_Runtime::FieldEvent event(TargetName(), EventKind()); + if (!m_pRuntime->AddEventToSet(event)) { + return IJS_Runtime::JS_Error( + 1, 1, JSGetStringFromID(JSMessage::kDuplicateEventError)); + } + +- Optional err; ++ absl::optional err; + if (script.GetLength() > 0) + err = m_pRuntime->ExecuteScript(script); + + m_pRuntime->RemoveEventFromSet(event); +- m_pEventRecorder->Destroy(); ++ Destroy(); + return err; + } + +@@ -69,17 +64,14 @@ CJS_Field* CJS_EventContext::SourceField() { + if (pFieldObj.IsEmpty()) + return nullptr; + +- auto* pFormFillEnv = m_pEventRecorder->GetFormFillEnvironment(); +- if (!pFormFillEnv) +- pFormFillEnv = GetFormFillEnv(); +- +- auto* pJSDocument = +- static_cast(CFXJS_Engine::GetObjectPrivate(pDocObj)); ++ auto* pFormFillEnv = GetFormFillEnv(); ++ auto* pJSDocument = static_cast( ++ CFXJS_Engine::GetObjectPrivate(m_pRuntime->GetIsolate(), pDocObj)); + pJSDocument->SetFormFillEnv(pFormFillEnv); + +- auto* pJSField = +- static_cast(CFXJS_Engine::GetObjectPrivate(pFieldObj)); +- pJSField->AttachField(pJSDocument, m_pEventRecorder->SourceName()); ++ auto* pJSField = static_cast( ++ CFXJS_Engine::GetObjectPrivate(m_pRuntime->GetIsolate(), pFieldObj)); ++ pJSField->AttachField(pJSDocument, SourceName()); + return pJSField; + } + +@@ -94,137 +86,149 @@ CJS_Field* CJS_EventContext::TargetField() { + if (pFieldObj.IsEmpty()) + return nullptr; + +- auto* pFormFillEnv = m_pEventRecorder->GetFormFillEnvironment(); +- if (!pFormFillEnv) +- pFormFillEnv = GetFormFillEnv(); +- +- auto* pJSDocument = +- static_cast(CFXJS_Engine::GetObjectPrivate(pDocObj)); ++ auto* pFormFillEnv = GetFormFillEnv(); ++ auto* pJSDocument = static_cast( ++ CFXJS_Engine::GetObjectPrivate(m_pRuntime->GetIsolate(), pDocObj)); + pJSDocument->SetFormFillEnv(pFormFillEnv); + +- auto* pJSField = +- static_cast(CFXJS_Engine::GetObjectPrivate(pFieldObj)); +- pJSField->AttachField(pJSDocument, m_pEventRecorder->TargetName()); ++ auto* pJSField = static_cast( ++ CFXJS_Engine::GetObjectPrivate(m_pRuntime->GetIsolate(), pFieldObj)); ++ pJSField->AttachField(pJSDocument, TargetName()); + return pJSField; + } + +-void CJS_EventContext::OnApp_Init() { +- m_pEventRecorder->OnApp_Init(); +-} +- +-void CJS_EventContext::OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const WideString& strTargetName) { +- m_pEventRecorder->OnDoc_Open(pFormFillEnv, strTargetName); ++void CJS_EventContext::OnDoc_Open(const WideString& strTargetName) { ++ Initialize(Kind::kDocOpen); ++ m_strTargetName = strTargetName; + } + +-void CJS_EventContext::OnDoc_WillPrint( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- m_pEventRecorder->OnDoc_WillPrint(pFormFillEnv); ++void CJS_EventContext::OnDoc_WillPrint() { ++ Initialize(Kind::kDocWillPrint); + } + +-void CJS_EventContext::OnDoc_DidPrint( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- m_pEventRecorder->OnDoc_DidPrint(pFormFillEnv); ++void CJS_EventContext::OnDoc_DidPrint() { ++ Initialize(Kind::kDocDidPrint); + } + +-void CJS_EventContext::OnDoc_WillSave( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- m_pEventRecorder->OnDoc_WillSave(pFormFillEnv); ++void CJS_EventContext::OnDoc_WillSave() { ++ Initialize(Kind::kDocWillSave); + } + +-void CJS_EventContext::OnDoc_DidSave( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- m_pEventRecorder->OnDoc_DidSave(pFormFillEnv); ++void CJS_EventContext::OnDoc_DidSave() { ++ Initialize(Kind::kDocDidSave); + } + +-void CJS_EventContext::OnDoc_WillClose( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- m_pEventRecorder->OnDoc_WillClose(pFormFillEnv); ++void CJS_EventContext::OnDoc_WillClose() { ++ Initialize(Kind::kDocWillClose); + } + +-void CJS_EventContext::OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- m_pEventRecorder->OnPage_Open(pFormFillEnv); ++void CJS_EventContext::OnPage_Open() { ++ Initialize(Kind::kPageOpen); + } + +-void CJS_EventContext::OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- m_pEventRecorder->OnPage_Close(pFormFillEnv); ++void CJS_EventContext::OnPage_Close() { ++ Initialize(Kind::kPageClose); + } + +-void CJS_EventContext::OnPage_InView( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- m_pEventRecorder->OnPage_InView(pFormFillEnv); ++void CJS_EventContext::OnPage_InView() { ++ Initialize(Kind::kPageInView); + } + +-void CJS_EventContext::OnPage_OutView( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- m_pEventRecorder->OnPage_OutView(pFormFillEnv); +-} +- +-void CJS_EventContext::OnField_MouseDown(bool bModifier, +- bool bShift, +- CPDF_FormField* pTarget) { +- m_pEventRecorder->OnField_MouseDown(bModifier, bShift, pTarget); ++void CJS_EventContext::OnPage_OutView() { ++ Initialize(Kind::kPageOutView); + } + + void CJS_EventContext::OnField_MouseEnter(bool bModifier, + bool bShift, + CPDF_FormField* pTarget) { +- m_pEventRecorder->OnField_MouseEnter(bModifier, bShift, pTarget); ++ Initialize(Kind::kFieldMouseEnter); ++ m_bModifier = bModifier; ++ m_bShift = bShift; ++ m_strTargetName = pTarget->GetFullName(); + } + + void CJS_EventContext::OnField_MouseExit(bool bModifier, + bool bShift, + CPDF_FormField* pTarget) { +- m_pEventRecorder->OnField_MouseExit(bModifier, bShift, pTarget); ++ Initialize(Kind::kFieldMouseExit); ++ m_bModifier = bModifier; ++ m_bShift = bShift; ++ m_strTargetName = pTarget->GetFullName(); ++} ++ ++void CJS_EventContext::OnField_MouseDown(bool bModifier, ++ bool bShift, ++ CPDF_FormField* pTarget) { ++ Initialize(Kind::kFieldMouseDown); ++ m_bModifier = bModifier; ++ m_bShift = bShift; ++ m_strTargetName = pTarget->GetFullName(); + } + + void CJS_EventContext::OnField_MouseUp(bool bModifier, + bool bShift, + CPDF_FormField* pTarget) { +- m_pEventRecorder->OnField_MouseUp(bModifier, bShift, pTarget); ++ Initialize(Kind::kFieldMouseUp); ++ m_bModifier = bModifier; ++ m_bShift = bShift; ++ m_strTargetName = pTarget->GetFullName(); + } + + void CJS_EventContext::OnField_Focus(bool bModifier, + bool bShift, + CPDF_FormField* pTarget, +- WideString* Value) { +- m_pEventRecorder->OnField_Focus(bModifier, bShift, pTarget, Value); ++ WideString* pValue) { ++ DCHECK(pValue); ++ Initialize(Kind::kFieldFocus); ++ m_bModifier = bModifier; ++ m_bShift = bShift; ++ m_strTargetName = pTarget->GetFullName(); ++ m_pValue = pValue; + } + + void CJS_EventContext::OnField_Blur(bool bModifier, + bool bShift, + CPDF_FormField* pTarget, +- WideString* Value) { +- m_pEventRecorder->OnField_Blur(bModifier, bShift, pTarget, Value); +-} +- +-void CJS_EventContext::OnField_Calculate(CPDF_FormField* pSource, +- CPDF_FormField* pTarget, +- WideString* pValue, +- bool* pRc) { +- m_pEventRecorder->OnField_Calculate(pSource, pTarget, pValue, pRc); +-} +- +-void CJS_EventContext::OnField_Format(CPDF_FormField* pTarget, +- WideString* Value) { +- m_pEventRecorder->OnField_Format(pTarget, Value); ++ WideString* pValue) { ++ DCHECK(pValue); ++ Initialize(Kind::kFieldBlur); ++ m_bModifier = bModifier; ++ m_bShift = bShift; ++ m_strTargetName = pTarget->GetFullName(); ++ m_pValue = pValue; + } + + void CJS_EventContext::OnField_Keystroke(WideString* strChange, + const WideString& strChangeEx, +- bool bKeyDown, ++ bool KeyDown, + bool bModifier, +- int* nSelEnd, +- int* nSelStart, ++ int* pSelEnd, ++ int* pSelStart, + bool bShift, + CPDF_FormField* pTarget, +- WideString* Value, ++ WideString* pValue, + bool bWillCommit, + bool bFieldFull, +- bool* bRc) { +- m_pEventRecorder->OnField_Keystroke( +- strChange, strChangeEx, bKeyDown, bModifier, nSelEnd, nSelStart, bShift, +- pTarget, Value, bWillCommit, bFieldFull, bRc); ++ bool* pbRc) { ++ DCHECK(pValue); ++ DCHECK(pbRc); ++ DCHECK(pSelStart); ++ DCHECK(pSelEnd); ++ ++ Initialize(Kind::kFieldKeystroke); ++ m_nCommitKey = 0; ++ m_pWideStrChange = strChange; ++ m_WideStrChangeEx = strChangeEx; ++ m_bKeyDown = KeyDown; ++ m_bModifier = bModifier; ++ m_pISelEnd = pSelEnd; ++ m_pISelStart = pSelStart; ++ m_bShift = bShift; ++ m_strTargetName = pTarget->GetFullName(); ++ m_pValue = pValue; ++ m_bWillCommit = bWillCommit; ++ m_pbRc = pbRc; ++ m_bFieldFull = bFieldFull; + } + + void CJS_EventContext::OnField_Validate(WideString* strChange, +@@ -233,94 +237,190 @@ void CJS_EventContext::OnField_Validate(WideString* strChange, + bool bModifier, + bool bShift, + CPDF_FormField* pTarget, +- WideString* Value, +- bool* bRc) { +- m_pEventRecorder->OnField_Validate(strChange, strChangeEx, bKeyDown, +- bModifier, bShift, pTarget, Value, bRc); +-} +- +-void CJS_EventContext::OnScreen_Focus(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- m_pEventRecorder->OnScreen_Focus(bModifier, bShift, pScreen); +-} +- +-void CJS_EventContext::OnScreen_Blur(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- m_pEventRecorder->OnScreen_Blur(bModifier, bShift, pScreen); +-} +- +-void CJS_EventContext::OnScreen_Open(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- m_pEventRecorder->OnScreen_Open(bModifier, bShift, pScreen); +-} +- +-void CJS_EventContext::OnScreen_Close(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- m_pEventRecorder->OnScreen_Close(bModifier, bShift, pScreen); +-} +- +-void CJS_EventContext::OnScreen_MouseDown(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- m_pEventRecorder->OnScreen_MouseDown(bModifier, bShift, pScreen); +-} +- +-void CJS_EventContext::OnScreen_MouseUp(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- m_pEventRecorder->OnScreen_MouseUp(bModifier, bShift, pScreen); ++ WideString* pValue, ++ bool* pbRc) { ++ DCHECK(pValue); ++ DCHECK(pbRc); ++ Initialize(Kind::kFieldValidate); ++ m_pWideStrChange = strChange; ++ m_WideStrChangeEx = strChangeEx; ++ m_bKeyDown = bKeyDown; ++ m_bModifier = bModifier; ++ m_bShift = bShift; ++ m_strTargetName = pTarget->GetFullName(); ++ m_pValue = pValue; ++ m_pbRc = pbRc; + } + +-void CJS_EventContext::OnScreen_MouseEnter(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- m_pEventRecorder->OnScreen_MouseEnter(bModifier, bShift, pScreen); ++void CJS_EventContext::OnField_Calculate(CPDF_FormField* pSource, ++ CPDF_FormField* pTarget, ++ WideString* pValue, ++ bool* pRc) { ++ DCHECK(pValue); ++ DCHECK(pRc); ++ Initialize(Kind::kFieldCalculate); ++ if (pSource) ++ m_strSourceName = pSource->GetFullName(); ++ m_strTargetName = pTarget->GetFullName(); ++ m_pValue = pValue; ++ m_pbRc = pRc; + } + +-void CJS_EventContext::OnScreen_MouseExit(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- m_pEventRecorder->OnScreen_MouseExit(bModifier, bShift, pScreen); ++void CJS_EventContext::OnField_Format(CPDF_FormField* pTarget, ++ WideString* pValue) { ++ DCHECK(pValue); ++ Initialize(Kind::kFieldFormat); ++ m_nCommitKey = 0; ++ m_strTargetName = pTarget->GetFullName(); ++ m_pValue = pValue; ++ m_bWillCommit = true; + } + +-void CJS_EventContext::OnScreen_InView(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- m_pEventRecorder->OnScreen_InView(bModifier, bShift, pScreen); ++void CJS_EventContext::OnExternal_Exec() { ++ Initialize(Kind::kExternalExec); ++} ++ ++void CJS_EventContext::Initialize(Kind kind) { ++ m_eKind = kind; ++ m_strTargetName.clear(); ++ m_strSourceName.clear(); ++ m_pWideStrChange = nullptr; ++ m_WideStrChangeDu.clear(); ++ m_WideStrChangeEx.clear(); ++ m_nCommitKey = -1; ++ m_bKeyDown = false; ++ m_bModifier = false; ++ m_bShift = false; ++ m_pISelEnd = nullptr; ++ m_nSelEndDu = 0; ++ m_pISelStart = nullptr; ++ m_nSelStartDu = 0; ++ m_bWillCommit = false; ++ m_pValue = nullptr; ++ m_bFieldFull = false; ++ m_pbRc = nullptr; ++ m_bRcDu = false; ++ m_bValid = true; ++} ++ ++void CJS_EventContext::Destroy() { ++ m_bValid = false; ++} ++ ++bool CJS_EventContext::IsUserGesture() const { ++ switch (m_eKind) { ++ case Kind::kFieldMouseDown: ++ case Kind::kFieldMouseUp: ++ case Kind::kFieldKeystroke: ++ return true; ++ default: ++ return false; ++ } + } + +-void CJS_EventContext::OnScreen_OutView(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- m_pEventRecorder->OnScreen_OutView(bModifier, bShift, pScreen); ++WideString& CJS_EventContext::Change() { ++ return m_pWideStrChange ? *m_pWideStrChange : m_WideStrChangeDu; ++} ++ ++ByteStringView CJS_EventContext::Name() const { ++ switch (m_eKind) { ++ case Kind::kDocDidPrint: ++ return "DidPrint"; ++ case Kind::kDocDidSave: ++ return "DidSave"; ++ case Kind::kDocOpen: ++ return "Open"; ++ case Kind::kDocWillClose: ++ return "WillClose"; ++ case Kind::kDocWillPrint: ++ return "WillPrint"; ++ case Kind::kDocWillSave: ++ return "WillSave"; ++ case Kind::kExternalExec: ++ return "Exec"; ++ case Kind::kFieldFocus: ++ return "Focus"; ++ case Kind::kFieldBlur: ++ return "Blur"; ++ case Kind::kFieldMouseDown: ++ return "Mouse Down"; ++ case Kind::kFieldMouseUp: ++ return "Mouse Up"; ++ case Kind::kFieldMouseEnter: ++ return "Mouse Enter"; ++ case Kind::kFieldMouseExit: ++ return "Mouse Exit"; ++ case Kind::kFieldCalculate: ++ return "Calculate"; ++ case Kind::kFieldFormat: ++ return "Format"; ++ case Kind::kFieldKeystroke: ++ return "Keystroke"; ++ case Kind::kFieldValidate: ++ return "Validate"; ++ case Kind::kPageOpen: ++ return "Open"; ++ case Kind::kPageClose: ++ return "Close"; ++ case Kind::kPageInView: ++ return "InView"; ++ case Kind::kPageOutView: ++ return "OutView"; ++ default: ++ return ""; ++ } + } + +-void CJS_EventContext::OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) { +- m_pEventRecorder->OnBookmark_MouseUp(pBookMark); ++ByteStringView CJS_EventContext::Type() const { ++ switch (m_eKind) { ++ case Kind::kDocDidPrint: ++ case Kind::kDocDidSave: ++ case Kind::kDocOpen: ++ case Kind::kDocWillClose: ++ case Kind::kDocWillPrint: ++ case Kind::kDocWillSave: ++ return "Doc"; ++ case Kind::kExternalExec: ++ return "External"; ++ case Kind::kFieldBlur: ++ case Kind::kFieldFocus: ++ case Kind::kFieldMouseDown: ++ case Kind::kFieldMouseUp: ++ case Kind::kFieldMouseEnter: ++ case Kind::kFieldMouseExit: ++ case Kind::kFieldCalculate: ++ case Kind::kFieldFormat: ++ case Kind::kFieldKeystroke: ++ case Kind::kFieldValidate: ++ return "Field"; ++ case Kind::kPageOpen: ++ case Kind::kPageClose: ++ case Kind::kPageInView: ++ case Kind::kPageOutView: ++ return "Page"; ++ default: ++ return ""; ++ } + } + +-void CJS_EventContext::OnLink_MouseUp( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- m_pEventRecorder->OnLink_MouseUp(pFormFillEnv); ++bool& CJS_EventContext::Rc() { ++ return m_pbRc ? *m_pbRc : m_bRcDu; + } + +-void CJS_EventContext::OnConsole_Exec() { +- m_pEventRecorder->OnConsole_Exec(); ++int CJS_EventContext::SelEnd() const { ++ return m_pISelEnd ? *m_pISelEnd : m_nSelEndDu; + } + +-void CJS_EventContext::OnExternal_Exec() { +- m_pEventRecorder->OnExternal_Exec(); ++int CJS_EventContext::SelStart() const { ++ return m_pISelStart ? *m_pISelStart : m_nSelStartDu; + } + +-void CJS_EventContext::OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- m_pEventRecorder->OnBatchExec(pFormFillEnv); ++void CJS_EventContext::SetSelEnd(int value) { ++ int& target = m_pISelEnd ? *m_pISelEnd : m_nSelEndDu; ++ target = value; + } + +-void CJS_EventContext::OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const WideString& strTargetName) { +- m_pEventRecorder->OnMenu_Exec(pFormFillEnv, strTargetName); ++void CJS_EventContext::SetSelStart(int value) { ++ int& target = m_pISelStart ? *m_pISelStart : m_nSelStartDu; ++ target = value; + } +diff --git a/fxjs/cjs_event_context.h b/fxjs/cjs_event_context.h +index 6a953e6c7..a0b266bb5 100644 +--- a/fxjs/cjs_event_context.h ++++ b/fxjs/cjs_event_context.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,37 +7,58 @@ + #ifndef FXJS_CJS_EVENT_CONTEXT_H_ + #define FXJS_CJS_EVENT_CONTEXT_H_ + +-#include +- + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/observed_ptr.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fxjs/ijs_event_context.h" + +-class CJS_EventRecorder; + class CJS_Field; + class CJS_Runtime; +-class CPDFSDK_FormFillEnvironment; + + class CJS_EventContext final : public IJS_EventContext { + public: ++ enum class Kind : uint8_t { ++ kUnknown, ++ kDocOpen, ++ kDocWillPrint, ++ kDocDidPrint, ++ kDocWillSave, ++ kDocDidSave, ++ kDocWillClose, ++ kPageOpen, ++ kPageClose, ++ kPageInView, ++ kPageOutView, ++ kFieldMouseDown, ++ kFieldMouseUp, ++ kFieldMouseEnter, ++ kFieldMouseExit, ++ kFieldFocus, ++ kFieldBlur, ++ kFieldKeystroke, ++ kFieldValidate, ++ kFieldCalculate, ++ kFieldFormat, ++ kExternalExec, ++ }; ++ + explicit CJS_EventContext(CJS_Runtime* pRuntime); + ~CJS_EventContext() override; + + // IJS_EventContext +- Optional RunScript(const WideString& script) override; +- void OnApp_Init() override; +- void OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const WideString& strTargetName) override; +- void OnDoc_WillPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) override; +- void OnDoc_DidPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) override; +- void OnDoc_WillSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) override; +- void OnDoc_DidSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) override; +- void OnDoc_WillClose(CPDFSDK_FormFillEnvironment* pFormFillEnv) override; +- void OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) override; +- void OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv) override; +- void OnPage_InView(CPDFSDK_FormFillEnvironment* pFormFillEnv) override; +- void OnPage_OutView(CPDFSDK_FormFillEnvironment* pFormFillEnv) override; ++ absl::optional RunScript( ++ const WideString& script) override; ++ void OnDoc_Open(const WideString& strTargetName) override; ++ void OnDoc_WillPrint() override; ++ void OnDoc_DidPrint() override; ++ void OnDoc_WillSave() override; ++ void OnDoc_DidSave() override; ++ void OnDoc_WillClose() override; ++ void OnPage_Open() override; ++ void OnPage_Close() override; ++ void OnPage_InView() override; ++ void OnPage_OutView() override; + void OnField_MouseDown(bool bModifier, + bool bShift, + CPDF_FormField* pTarget) override; +@@ -83,54 +104,72 @@ class CJS_EventContext final : public IJS_EventContext { + CPDF_FormField* pTarget, + WideString* Value, + bool* bRc) override; +- void OnScreen_Focus(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override; +- void OnScreen_Blur(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override; +- void OnScreen_Open(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override; +- void OnScreen_Close(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override; +- void OnScreen_MouseDown(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override; +- void OnScreen_MouseUp(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override; +- void OnScreen_MouseEnter(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override; +- void OnScreen_MouseExit(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override; +- void OnScreen_InView(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override; +- void OnScreen_OutView(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override; +- void OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) override; +- void OnLink_MouseUp(CPDFSDK_FormFillEnvironment* pFormFillEnv) override; +- void OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const WideString& strTargetName) override; +- void OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv) override; +- void OnConsole_Exec() override; + void OnExternal_Exec() override; + +- CJS_Runtime* GetJSRuntime() const { return m_pRuntime.Get(); } +- CJS_EventRecorder* GetEventRecorder() const { return m_pEventRecorder.get(); } +- CPDFSDK_FormFillEnvironment* GetFormFillEnv(); ++ CJS_Runtime* GetJSRuntime() const { return m_pRuntime; } ++ CPDFSDK_FormFillEnvironment* GetFormFillEnv() const { ++ return m_pFormFillEnv.Get(); ++ } + CJS_Field* SourceField(); + CJS_Field* TargetField(); + ++ Kind EventKind() const { return m_eKind; } ++ bool IsValid() const { return m_bValid; } ++ bool IsUserGesture() const; ++ WideString& Change(); ++ WideString ChangeEx() const { return m_WideStrChangeEx; } ++ WideString SourceName() const { return m_strSourceName; } ++ WideString TargetName() const { return m_strTargetName; } ++ int CommitKey() const { return m_nCommitKey; } ++ bool FieldFull() const { return m_bFieldFull; } ++ bool KeyDown() const { return m_bKeyDown; } ++ bool Modifier() const { return m_bModifier; } ++ ByteStringView Name() const; ++ ByteStringView Type() const; ++ bool& Rc(); ++ int SelEnd() const; ++ int SelStart() const; ++ void SetSelEnd(int value); ++ void SetSelStart(int value); ++ bool Shift() const { return m_bShift; } ++ bool HasValue() const { return !!m_pValue; } ++ WideString& Value() { return *m_pValue; } ++ bool WillCommit() const { return m_bWillCommit; } ++ ++ void SetValueForTest(WideString* pStr) { m_pValue = pStr; } ++ void SetRCForTest(bool* pRC) { m_pbRc = pRC; } ++ void SetStrChangeForTest(WideString* pStrChange) { ++ m_pWideStrChange = pStrChange; ++ } ++ void ResetWillCommitForTest() { m_bWillCommit = false; } ++ + private: ++ void Initialize(Kind kind); ++ void Destroy(); ++ + UnownedPtr const m_pRuntime; +- std::unique_ptr m_pEventRecorder; ++ ObservedPtr m_pFormFillEnv; ++ Kind m_eKind = Kind::kUnknown; + bool m_bBusy = false; ++ bool m_bValid = false; ++ UnownedPtr m_pValue; ++ WideString m_strSourceName; ++ WideString m_strTargetName; ++ WideString m_WideStrChangeDu; ++ WideString m_WideStrChangeEx; ++ UnownedPtr m_pWideStrChange; ++ int m_nCommitKey = -1; ++ bool m_bKeyDown = false; ++ bool m_bModifier = false; ++ bool m_bShift = false; ++ int m_nSelEndDu = 0; ++ int m_nSelStartDu = 0; ++ UnownedPtr m_pISelEnd; ++ UnownedPtr m_pISelStart; ++ bool m_bWillCommit = false; ++ bool m_bFieldFull = false; ++ bool m_bRcDu = false; ++ UnownedPtr m_pbRc; + }; + + #endif // FXJS_CJS_EVENT_CONTEXT_H_ +diff --git a/fxjs/cjs_event_context_stub.cpp b/fxjs/cjs_event_context_stub.cpp +index 82530e4c7..b8d051808 100644 +--- a/fxjs/cjs_event_context_stub.cpp ++++ b/fxjs/cjs_event_context_stub.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,11 @@ + + #include "fxjs/cjs_event_context_stub.h" + +-Optional CJS_EventContextStub::RunScript( ++CJS_EventContextStub::CJS_EventContextStub() = default; ++ ++CJS_EventContextStub::~CJS_EventContextStub() = default; ++ ++absl::optional CJS_EventContextStub::RunScript( + const WideString& script) { + return IJS_Runtime::JS_Error(1, 1, L"JavaScript support not present"); + } +diff --git a/fxjs/cjs_event_context_stub.h b/fxjs/cjs_event_context_stub.h +index 06d018472..5c9b50a09 100644 +--- a/fxjs/cjs_event_context_stub.h ++++ b/fxjs/cjs_event_context_stub.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,24 +11,23 @@ + + class CJS_EventContextStub final : public IJS_EventContext { + public: +- CJS_EventContextStub() {} +- ~CJS_EventContextStub() override {} ++ CJS_EventContextStub(); ++ ~CJS_EventContextStub() override; + + // IJS_EventContext: +- Optional RunScript(const WideString& script) override; ++ absl::optional RunScript( ++ const WideString& script) override; + +- void OnApp_Init() override {} +- void OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const WideString& strTargetName) override {} +- void OnDoc_WillPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {} +- void OnDoc_DidPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {} +- void OnDoc_WillSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {} +- void OnDoc_DidSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {} +- void OnDoc_WillClose(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {} +- void OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {} +- void OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {} +- void OnPage_InView(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {} +- void OnPage_OutView(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {} ++ void OnDoc_Open(const WideString& strTargetName) override {} ++ void OnDoc_WillPrint() override {} ++ void OnDoc_DidPrint() override {} ++ void OnDoc_WillSave() override {} ++ void OnDoc_DidSave() override {} ++ void OnDoc_WillClose() override {} ++ void OnPage_Open() override {} ++ void OnPage_Close() override {} ++ void OnPage_InView() override {} ++ void OnPage_OutView() override {} + void OnField_MouseDown(bool bModifier, + bool bShift, + CPDF_FormField* pTarget) override {} +@@ -74,42 +73,6 @@ class CJS_EventContextStub final : public IJS_EventContext { + CPDF_FormField* pTarget, + WideString* Value, + bool* bRc) override {} +- void OnScreen_Focus(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override {} +- void OnScreen_Blur(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override {} +- void OnScreen_Open(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override {} +- void OnScreen_Close(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override {} +- void OnScreen_MouseDown(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override {} +- void OnScreen_MouseUp(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override {} +- void OnScreen_MouseEnter(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override {} +- void OnScreen_MouseExit(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override {} +- void OnScreen_InView(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override {} +- void OnScreen_OutView(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) override {} +- void OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) override {} +- void OnLink_MouseUp(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {} +- void OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const WideString&) override {} +- void OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv) override {} +- void OnConsole_Exec() override {} + void OnExternal_Exec() override {} + }; + +diff --git a/fxjs/cjs_eventrecorder.cpp b/fxjs/cjs_eventrecorder.cpp +deleted file mode 100644 +index 59febf052..000000000 +--- a/fxjs/cjs_eventrecorder.cpp ++++ /dev/null +@@ -1,558 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "fxjs/cjs_eventrecorder.h" +- +-#include "core/fpdfdoc/cpdf_bookmark.h" +-#include "core/fpdfdoc/cpdf_formfield.h" +- +-CJS_EventRecorder::CJS_EventRecorder() = default; +- +-CJS_EventRecorder::~CJS_EventRecorder() = default; +- +-void CJS_EventRecorder::OnApp_Init() { +- Initialize(JET_APP_INIT); +-} +- +-void CJS_EventRecorder::OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const WideString& strTargetName) { +- Initialize(JET_DOC_OPEN); +- m_pTargetFormFillEnv.Reset(pFormFillEnv); +- m_strTargetName = strTargetName; +-} +- +-void CJS_EventRecorder::OnDoc_WillPrint( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- Initialize(JET_DOC_WILLPRINT); +- m_pTargetFormFillEnv.Reset(pFormFillEnv); +-} +- +-void CJS_EventRecorder::OnDoc_DidPrint( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- Initialize(JET_DOC_DIDPRINT); +- m_pTargetFormFillEnv.Reset(pFormFillEnv); +-} +- +-void CJS_EventRecorder::OnDoc_WillSave( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- Initialize(JET_DOC_WILLSAVE); +- m_pTargetFormFillEnv.Reset(pFormFillEnv); +-} +- +-void CJS_EventRecorder::OnDoc_DidSave( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- Initialize(JET_DOC_DIDSAVE); +- m_pTargetFormFillEnv.Reset(pFormFillEnv); +-} +- +-void CJS_EventRecorder::OnDoc_WillClose( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- Initialize(JET_DOC_WILLCLOSE); +- m_pTargetFormFillEnv.Reset(pFormFillEnv); +-} +- +-void CJS_EventRecorder::OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- Initialize(JET_PAGE_OPEN); +- m_pTargetFormFillEnv.Reset(pFormFillEnv); +-} +- +-void CJS_EventRecorder::OnPage_Close( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- Initialize(JET_PAGE_CLOSE); +- m_pTargetFormFillEnv.Reset(pFormFillEnv); +-} +- +-void CJS_EventRecorder::OnPage_InView( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- Initialize(JET_PAGE_INVIEW); +- m_pTargetFormFillEnv.Reset(pFormFillEnv); +-} +- +-void CJS_EventRecorder::OnPage_OutView( +- CPDFSDK_FormFillEnvironment* pFormFillEnv) { +- Initialize(JET_PAGE_OUTVIEW); +- m_pTargetFormFillEnv.Reset(pFormFillEnv); +-} +- +-void CJS_EventRecorder::OnField_MouseEnter(bool bModifier, +- bool bShift, +- CPDF_FormField* pTarget) { +- Initialize(JET_FIELD_MOUSEENTER); +- +- m_bModifier = bModifier; +- m_bShift = bShift; +- +- m_strTargetName = pTarget->GetFullName(); +-} +- +-void CJS_EventRecorder::OnField_MouseExit(bool bModifier, +- bool bShift, +- CPDF_FormField* pTarget) { +- Initialize(JET_FIELD_MOUSEEXIT); +- +- m_bModifier = bModifier; +- m_bShift = bShift; +- m_strTargetName = pTarget->GetFullName(); +-} +- +-void CJS_EventRecorder::OnField_MouseDown(bool bModifier, +- bool bShift, +- CPDF_FormField* pTarget) { +- Initialize(JET_FIELD_MOUSEDOWN); +- m_eEventType = JET_FIELD_MOUSEDOWN; +- +- m_bModifier = bModifier; +- m_bShift = bShift; +- m_strTargetName = pTarget->GetFullName(); +-} +- +-void CJS_EventRecorder::OnField_MouseUp(bool bModifier, +- bool bShift, +- CPDF_FormField* pTarget) { +- Initialize(JET_FIELD_MOUSEUP); +- +- m_bModifier = bModifier; +- m_bShift = bShift; +- m_strTargetName = pTarget->GetFullName(); +-} +- +-void CJS_EventRecorder::OnField_Focus(bool bModifier, +- bool bShift, +- CPDF_FormField* pTarget, +- WideString* pValue) { +- ASSERT(pValue); +- Initialize(JET_FIELD_FOCUS); +- +- m_bModifier = bModifier; +- m_bShift = bShift; +- m_strTargetName = pTarget->GetFullName(); +- m_pValue = pValue; +-} +- +-void CJS_EventRecorder::OnField_Blur(bool bModifier, +- bool bShift, +- CPDF_FormField* pTarget, +- WideString* pValue) { +- ASSERT(pValue); +- Initialize(JET_FIELD_BLUR); +- +- m_bModifier = bModifier; +- m_bShift = bShift; +- m_strTargetName = pTarget->GetFullName(); +- m_pValue = pValue; +-} +- +-void CJS_EventRecorder::OnField_Keystroke(WideString* strChange, +- const WideString& strChangeEx, +- bool KeyDown, +- bool bModifier, +- int* pSelEnd, +- int* pSelStart, +- bool bShift, +- CPDF_FormField* pTarget, +- WideString* pValue, +- bool bWillCommit, +- bool bFieldFull, +- bool* pbRc) { +- ASSERT(pValue); +- ASSERT(pbRc); +- ASSERT(pSelStart); +- ASSERT(pSelEnd); +- +- Initialize(JET_FIELD_KEYSTROKE); +- +- m_nCommitKey = 0; +- m_pWideStrChange = strChange; +- m_WideStrChangeEx = strChangeEx; +- m_bKeyDown = KeyDown; +- m_bModifier = bModifier; +- m_pISelEnd = pSelEnd; +- m_pISelStart = pSelStart; +- m_bShift = bShift; +- m_strTargetName = pTarget->GetFullName(); +- m_pValue = pValue; +- m_bWillCommit = bWillCommit; +- m_pbRc = pbRc; +- m_bFieldFull = bFieldFull; +-} +- +-void CJS_EventRecorder::OnField_Validate(WideString* strChange, +- const WideString& strChangeEx, +- bool bKeyDown, +- bool bModifier, +- bool bShift, +- CPDF_FormField* pTarget, +- WideString* pValue, +- bool* pbRc) { +- ASSERT(pValue); +- ASSERT(pbRc); +- +- Initialize(JET_FIELD_VALIDATE); +- +- m_pWideStrChange = strChange; +- m_WideStrChangeEx = strChangeEx; +- m_bKeyDown = bKeyDown; +- m_bModifier = bModifier; +- m_bShift = bShift; +- m_strTargetName = pTarget->GetFullName(); +- m_pValue = pValue; +- m_pbRc = pbRc; +-} +- +-void CJS_EventRecorder::OnField_Calculate(CPDF_FormField* pSource, +- CPDF_FormField* pTarget, +- WideString* pValue, +- bool* pRc) { +- ASSERT(pValue); +- ASSERT(pRc); +- +- Initialize(JET_FIELD_CALCULATE); +- +- if (pSource) +- m_strSourceName = pSource->GetFullName(); +- m_strTargetName = pTarget->GetFullName(); +- m_pValue = pValue; +- m_pbRc = pRc; +-} +- +-void CJS_EventRecorder::OnField_Format(CPDF_FormField* pTarget, +- WideString* pValue) { +- ASSERT(pValue); +- Initialize(JET_FIELD_FORMAT); +- +- m_nCommitKey = 0; +- m_strTargetName = pTarget->GetFullName(); +- m_pValue = pValue; +- m_bWillCommit = true; +-} +- +-void CJS_EventRecorder::OnScreen_Focus(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- Initialize(JET_SCREEN_FOCUS); +- +- m_bModifier = bModifier; +- m_bShift = bShift; +- m_pTargetAnnot.Reset(pScreen); +-} +- +-void CJS_EventRecorder::OnScreen_Blur(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- Initialize(JET_SCREEN_BLUR); +- +- m_bModifier = bModifier; +- m_bShift = bShift; +- m_pTargetAnnot.Reset(pScreen); +-} +- +-void CJS_EventRecorder::OnScreen_Open(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- Initialize(JET_SCREEN_OPEN); +- +- m_bModifier = bModifier; +- m_bShift = bShift; +- m_pTargetAnnot.Reset(pScreen); +-} +- +-void CJS_EventRecorder::OnScreen_Close(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- Initialize(JET_SCREEN_CLOSE); +- +- m_bModifier = bModifier; +- m_bShift = bShift; +- m_pTargetAnnot.Reset(pScreen); +-} +- +-void CJS_EventRecorder::OnScreen_MouseDown(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- Initialize(JET_SCREEN_MOUSEDOWN); +- +- m_bModifier = bModifier; +- m_bShift = bShift; +- m_pTargetAnnot.Reset(pScreen); +-} +- +-void CJS_EventRecorder::OnScreen_MouseUp(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- Initialize(JET_SCREEN_MOUSEUP); +- +- m_bModifier = bModifier; +- m_bShift = bShift; +- m_pTargetAnnot.Reset(pScreen); +-} +- +-void CJS_EventRecorder::OnScreen_MouseEnter(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- Initialize(JET_SCREEN_MOUSEENTER); +- +- m_bModifier = bModifier; +- m_bShift = bShift; +- m_pTargetAnnot.Reset(pScreen); +-} +- +-void CJS_EventRecorder::OnScreen_MouseExit(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- Initialize(JET_SCREEN_MOUSEEXIT); +- +- m_bModifier = bModifier; +- m_bShift = bShift; +- m_pTargetAnnot.Reset(pScreen); +-} +- +-void CJS_EventRecorder::OnScreen_InView(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- Initialize(JET_SCREEN_INVIEW); +- m_bModifier = bModifier; +- m_bShift = bShift; +- m_pTargetAnnot.Reset(pScreen); +-} +- +-void CJS_EventRecorder::OnScreen_OutView(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) { +- Initialize(JET_SCREEN_OUTVIEW); +- m_bModifier = bModifier; +- m_bShift = bShift; +- m_pTargetAnnot.Reset(pScreen); +-} +- +-void CJS_EventRecorder::OnLink_MouseUp( +- CPDFSDK_FormFillEnvironment* pTargetFormFillEnv) { +- Initialize(JET_LINK_MOUSEUP); +- m_pTargetFormFillEnv.Reset(pTargetFormFillEnv); +-} +- +-void CJS_EventRecorder::OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) { +- Initialize(JET_BOOKMARK_MOUSEUP); +- m_pTargetBookMark = pBookMark; +-} +- +-void CJS_EventRecorder::OnMenu_Exec( +- CPDFSDK_FormFillEnvironment* pTargetFormFillEnv, +- const WideString& strTargetName) { +- Initialize(JET_MENU_EXEC); +- m_pTargetFormFillEnv.Reset(pTargetFormFillEnv); +- m_strTargetName = strTargetName; +-} +- +-void CJS_EventRecorder::OnExternal_Exec() { +- Initialize(JET_EXTERNAL_EXEC); +-} +- +-void CJS_EventRecorder::OnBatchExec( +- CPDFSDK_FormFillEnvironment* pTargetFormFillEnv) { +- Initialize(JET_BATCH_EXEC); +- m_pTargetFormFillEnv.Reset(pTargetFormFillEnv); +-} +- +-void CJS_EventRecorder::OnConsole_Exec() { +- Initialize(JET_CONSOLE_EXEC); +-} +- +-void CJS_EventRecorder::Initialize(JS_EVENT_T type) { +- m_eEventType = type; +- m_strTargetName.clear(); +- m_strSourceName.clear(); +- m_pWideStrChange = nullptr; +- m_WideStrChangeDu.clear(); +- m_WideStrChangeEx.clear(); +- m_nCommitKey = -1; +- m_bKeyDown = false; +- m_bModifier = false; +- m_bShift = false; +- m_pISelEnd = nullptr; +- m_nSelEndDu = 0; +- m_pISelStart = nullptr; +- m_nSelStartDu = 0; +- m_bWillCommit = false; +- m_pValue = nullptr; +- m_bFieldFull = false; +- m_pbRc = nullptr; +- m_bRcDu = false; +- m_pTargetBookMark = nullptr; +- m_pTargetFormFillEnv.Reset(); +- m_pTargetAnnot.Reset(); +- m_bValid = true; +-} +- +-void CJS_EventRecorder::Destroy() { +- m_bValid = false; +-} +- +-bool CJS_EventRecorder::IsUserGesture() const { +- switch (m_eEventType) { +- case JET_FIELD_MOUSEDOWN: +- case JET_FIELD_MOUSEUP: +- case JET_SCREEN_MOUSEDOWN: +- case JET_SCREEN_MOUSEUP: +- case JET_BOOKMARK_MOUSEUP: +- case JET_LINK_MOUSEUP: +- case JET_FIELD_KEYSTROKE: +- return true; +- default: +- return false; +- } +-} +- +-WideString& CJS_EventRecorder::Change() { +- return m_pWideStrChange ? *m_pWideStrChange : m_WideStrChangeDu; +-} +- +-ByteStringView CJS_EventRecorder::Name() const { +- switch (m_eEventType) { +- case JET_APP_INIT: +- return "Init"; +- case JET_BATCH_EXEC: +- return "Exec"; +- case JET_BOOKMARK_MOUSEUP: +- return "Mouse Up"; +- case JET_CONSOLE_EXEC: +- return "Exec"; +- case JET_DOC_DIDPRINT: +- return "DidPrint"; +- case JET_DOC_DIDSAVE: +- return "DidSave"; +- case JET_DOC_OPEN: +- return "Open"; +- case JET_DOC_WILLCLOSE: +- return "WillClose"; +- case JET_DOC_WILLPRINT: +- return "WillPrint"; +- case JET_DOC_WILLSAVE: +- return "WillSave"; +- case JET_EXTERNAL_EXEC: +- return "Exec"; +- case JET_FIELD_FOCUS: +- case JET_SCREEN_FOCUS: +- return "Focus"; +- case JET_FIELD_BLUR: +- case JET_SCREEN_BLUR: +- return "Blur"; +- case JET_FIELD_MOUSEDOWN: +- case JET_SCREEN_MOUSEDOWN: +- return "Mouse Down"; +- case JET_FIELD_MOUSEUP: +- case JET_SCREEN_MOUSEUP: +- return "Mouse Up"; +- case JET_FIELD_MOUSEENTER: +- case JET_SCREEN_MOUSEENTER: +- return "Mouse Enter"; +- case JET_FIELD_MOUSEEXIT: +- case JET_SCREEN_MOUSEEXIT: +- return "Mouse Exit"; +- case JET_FIELD_CALCULATE: +- return "Calculate"; +- case JET_FIELD_FORMAT: +- return "Format"; +- case JET_FIELD_KEYSTROKE: +- return "Keystroke"; +- case JET_FIELD_VALIDATE: +- return "Validate"; +- case JET_LINK_MOUSEUP: +- return "Mouse Up"; +- case JET_MENU_EXEC: +- return "Exec"; +- case JET_PAGE_OPEN: +- case JET_SCREEN_OPEN: +- return "Open"; +- case JET_PAGE_CLOSE: +- case JET_SCREEN_CLOSE: +- return "Close"; +- case JET_SCREEN_INVIEW: +- case JET_PAGE_INVIEW: +- return "InView"; +- case JET_PAGE_OUTVIEW: +- case JET_SCREEN_OUTVIEW: +- return "OutView"; +- default: +- return ""; +- } +-} +- +-ByteStringView CJS_EventRecorder::Type() const { +- switch (m_eEventType) { +- case JET_APP_INIT: +- return "App"; +- case JET_BATCH_EXEC: +- return "Batch"; +- case JET_BOOKMARK_MOUSEUP: +- return "BookMark"; +- case JET_CONSOLE_EXEC: +- return "Console"; +- case JET_DOC_DIDPRINT: +- case JET_DOC_DIDSAVE: +- case JET_DOC_OPEN: +- case JET_DOC_WILLCLOSE: +- case JET_DOC_WILLPRINT: +- case JET_DOC_WILLSAVE: +- return "Doc"; +- case JET_EXTERNAL_EXEC: +- return "External"; +- case JET_FIELD_BLUR: +- case JET_FIELD_FOCUS: +- case JET_FIELD_MOUSEDOWN: +- case JET_FIELD_MOUSEENTER: +- case JET_FIELD_MOUSEEXIT: +- case JET_FIELD_MOUSEUP: +- case JET_FIELD_CALCULATE: +- case JET_FIELD_FORMAT: +- case JET_FIELD_KEYSTROKE: +- case JET_FIELD_VALIDATE: +- return "Field"; +- case JET_SCREEN_FOCUS: +- case JET_SCREEN_BLUR: +- case JET_SCREEN_OPEN: +- case JET_SCREEN_CLOSE: +- case JET_SCREEN_MOUSEDOWN: +- case JET_SCREEN_MOUSEUP: +- case JET_SCREEN_MOUSEENTER: +- case JET_SCREEN_MOUSEEXIT: +- case JET_SCREEN_INVIEW: +- case JET_SCREEN_OUTVIEW: +- return "Screen"; +- case JET_LINK_MOUSEUP: +- return "Link"; +- case JET_MENU_EXEC: +- return "Menu"; +- case JET_PAGE_OPEN: +- case JET_PAGE_CLOSE: +- case JET_PAGE_INVIEW: +- case JET_PAGE_OUTVIEW: +- return "Page"; +- default: +- return ""; +- } +-} +- +-bool& CJS_EventRecorder::Rc() { +- return m_pbRc ? *m_pbRc : m_bRcDu; +-} +- +-int CJS_EventRecorder::SelEnd() const { +- return m_pISelEnd ? *m_pISelEnd : m_nSelEndDu; +-} +- +-int CJS_EventRecorder::SelStart() const { +- return m_pISelStart ? *m_pISelStart : m_nSelStartDu; +-} +- +-void CJS_EventRecorder::SetSelEnd(int value) { +- int& target = m_pISelEnd ? *m_pISelEnd : m_nSelEndDu; +- target = value; +-} +- +-void CJS_EventRecorder::SetSelStart(int value) { +- int& target = m_pISelStart ? *m_pISelStart : m_nSelStartDu; +- target = value; +-} +diff --git a/fxjs/cjs_eventrecorder.h b/fxjs/cjs_eventrecorder.h +deleted file mode 100644 +index acf985685..000000000 +--- a/fxjs/cjs_eventrecorder.h ++++ /dev/null +@@ -1,201 +0,0 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef FXJS_CJS_EVENTRECORDER_H_ +-#define FXJS_CJS_EVENTRECORDER_H_ +- +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" +-#include "core/fxcrt/unowned_ptr.h" +-#include "fpdfsdk/cpdfsdk_annot.h" +-#include "fpdfsdk/cpdfsdk_formfillenvironment.h" +- +-class CPDF_Bookmark; +-class CPDF_FormField; +- +-enum JS_EVENT_T { +- JET_UNKNOWN, +- JET_APP_INIT, +- JET_DOC_OPEN, +- JET_DOC_WILLPRINT, +- JET_DOC_DIDPRINT, +- JET_DOC_WILLSAVE, +- JET_DOC_DIDSAVE, +- JET_DOC_WILLCLOSE, +- JET_PAGE_OPEN, +- JET_PAGE_CLOSE, +- JET_PAGE_INVIEW, +- JET_PAGE_OUTVIEW, +- JET_FIELD_MOUSEDOWN, +- JET_FIELD_MOUSEUP, +- JET_FIELD_MOUSEENTER, +- JET_FIELD_MOUSEEXIT, +- JET_FIELD_FOCUS, +- JET_FIELD_BLUR, +- JET_FIELD_KEYSTROKE, +- JET_FIELD_VALIDATE, +- JET_FIELD_CALCULATE, +- JET_FIELD_FORMAT, +- JET_SCREEN_FOCUS, +- JET_SCREEN_BLUR, +- JET_SCREEN_OPEN, +- JET_SCREEN_CLOSE, +- JET_SCREEN_MOUSEDOWN, +- JET_SCREEN_MOUSEUP, +- JET_SCREEN_MOUSEENTER, +- JET_SCREEN_MOUSEEXIT, +- JET_SCREEN_INVIEW, +- JET_SCREEN_OUTVIEW, +- JET_BATCH_EXEC, +- JET_MENU_EXEC, +- JET_CONSOLE_EXEC, +- JET_EXTERNAL_EXEC, +- JET_BOOKMARK_MOUSEUP, +- JET_LINK_MOUSEUP +-}; +- +-class CJS_EventRecorder { +- public: +- CJS_EventRecorder(); +- ~CJS_EventRecorder(); +- +- void OnApp_Init(); +- +- void OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const WideString& strTargetName); +- void OnDoc_WillPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv); +- void OnDoc_DidPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv); +- void OnDoc_WillSave(CPDFSDK_FormFillEnvironment* pFormFillEnv); +- void OnDoc_DidSave(CPDFSDK_FormFillEnvironment* pFormFillEnv); +- void OnDoc_WillClose(CPDFSDK_FormFillEnvironment* pFormFillEnv); +- +- void OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv); +- void OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv); +- void OnPage_InView(CPDFSDK_FormFillEnvironment* pFormFillEnv); +- void OnPage_OutView(CPDFSDK_FormFillEnvironment* pFormFillEnv); +- +- void OnField_Calculate(CPDF_FormField* pSource, +- CPDF_FormField* pTarget, +- WideString* Value, +- bool* pbRc); +- void OnField_Format(CPDF_FormField* pTarget, WideString* Value); +- void OnField_Keystroke(WideString* strChange, +- const WideString& strChangeEx, +- bool KeyDown, +- bool bModifier, +- int* nSelEnd, +- int* nSelStart, +- bool bShift, +- CPDF_FormField* pTarget, +- WideString* Value, +- bool bWillCommit, +- bool bFieldFull, +- bool* bRc); +- void OnField_Validate(WideString* strChange, +- const WideString& strChangeEx, +- bool bKeyDown, +- bool bModifier, +- bool bShift, +- CPDF_FormField* pTarget, +- WideString* Value, +- bool* bRc); +- void OnField_MouseDown(bool bModifier, bool bShift, CPDF_FormField* pTarget); +- void OnField_MouseEnter(bool bModifier, bool bShift, CPDF_FormField* pTarget); +- void OnField_MouseExit(bool bModifier, bool bShift, CPDF_FormField* pTarget); +- void OnField_MouseUp(bool bModifier, bool bShift, CPDF_FormField* pTarget); +- void OnField_Blur(bool bModifier, +- bool bShift, +- CPDF_FormField* pTarget, +- WideString* Value); +- void OnField_Focus(bool bModifier, +- bool bShift, +- CPDF_FormField* pTarget, +- WideString* Value); +- +- void OnScreen_Focus(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen); +- void OnScreen_Blur(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen); +- void OnScreen_Open(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen); +- void OnScreen_Close(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen); +- void OnScreen_MouseDown(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen); +- void OnScreen_MouseUp(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen); +- void OnScreen_MouseEnter(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen); +- void OnScreen_MouseExit(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen); +- void OnScreen_InView(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen); +- void OnScreen_OutView(bool bModifier, bool bShift, CPDFSDK_Annot* pScreen); +- +- void OnBookmark_MouseUp(CPDF_Bookmark* pBookMark); +- void OnLink_MouseUp(CPDFSDK_FormFillEnvironment* pFormFillEnv); +- +- void OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const WideString& strTargetName); +- void OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv); +- void OnConsole_Exec(); +- void OnExternal_Exec(); +- +- void Destroy(); +- +- JS_EVENT_T EventType() const { return m_eEventType; } +- bool IsValid() const { return m_bValid; } +- bool IsUserGesture() const; +- WideString& Change(); +- WideString ChangeEx() const { return m_WideStrChangeEx; } +- WideString SourceName() const { return m_strSourceName; } +- WideString TargetName() const { return m_strTargetName; } +- int CommitKey() const { return m_nCommitKey; } +- bool FieldFull() const { return m_bFieldFull; } +- bool KeyDown() const { return m_bKeyDown; } +- bool Modifier() const { return m_bModifier; } +- ByteStringView Name() const; +- ByteStringView Type() const; +- bool& Rc(); +- int SelEnd() const; +- int SelStart() const; +- void SetSelEnd(int value); +- void SetSelStart(int value); +- bool Shift() const { return m_bShift; } +- bool HasValue() const { return !!m_pValue; } +- WideString& Value() { return *m_pValue; } +- bool WillCommit() const { return m_bWillCommit; } +- CPDFSDK_FormFillEnvironment* GetFormFillEnvironment() const { +- return m_pTargetFormFillEnv.Get(); +- } +- +- void SetValueForTest(WideString* pStr) { m_pValue = pStr; } +- void SetRCForTest(bool* pRC) { m_pbRc = pRC; } +- void SetStrChangeForTest(WideString* pStrChange) { +- m_pWideStrChange = pStrChange; +- } +- void ResetWillCommitForTest() { m_bWillCommit = false; } +- +- private: +- void Initialize(JS_EVENT_T type); +- +- JS_EVENT_T m_eEventType = JET_UNKNOWN; +- bool m_bValid = false; +- UnownedPtr m_pValue; +- WideString m_strSourceName; +- WideString m_strTargetName; +- WideString m_WideStrChangeDu; +- WideString m_WideStrChangeEx; +- UnownedPtr m_pWideStrChange; +- int m_nCommitKey = -1; +- bool m_bKeyDown = false; +- bool m_bModifier = false; +- bool m_bShift = false; +- int m_nSelEndDu = 0; +- int m_nSelStartDu = 0; +- UnownedPtr m_pISelEnd; +- UnownedPtr m_pISelStart; +- bool m_bWillCommit = false; +- bool m_bFieldFull = false; +- bool m_bRcDu = false; +- UnownedPtr m_pbRc; +- UnownedPtr m_pTargetBookMark; +- ObservedPtr m_pTargetFormFillEnv; +- ObservedPtr m_pTargetAnnot; +-}; +- +-#endif // FXJS_CJS_EVENTRECORDER_H_ +diff --git a/fxjs/cjs_field.cpp b/fxjs/cjs_field.cpp +index b6aa9bbc5..53b3449a1 100644 +--- a/fxjs/cjs_field.cpp ++++ b/fxjs/cjs_field.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,12 +10,14 @@ + #include + #include + ++#include "constants/access_permissions.h" + #include "constants/annotation_flags.h" + #include "constants/form_flags.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfdoc/cpdf_formcontrol.h" + #include "core/fpdfdoc/cpdf_formfield.h" + #include "core/fpdfdoc/cpdf_interactiveform.h" +-#include "fpdfsdk/cpdfsdk_helpers.h" ++#include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fpdfsdk/cpdfsdk_interactiveform.h" + #include "fpdfsdk/cpdfsdk_pageview.h" + #include "fpdfsdk/cpdfsdk_widget.h" +@@ -23,11 +25,22 @@ + #include "fxjs/cjs_delaydata.h" + #include "fxjs/cjs_document.h" + #include "fxjs/cjs_icon.h" ++#include "fxjs/fxv8.h" + #include "fxjs/js_resources.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/check.h" ++#include "third_party/base/notreached.h" ++#include "v8/include/v8-container.h" + + namespace { + ++constexpr wchar_t kCheckSelector = L'4'; ++constexpr wchar_t kCircleSelector = L'l'; ++constexpr wchar_t kCrossSelector = L'8'; ++constexpr wchar_t kDiamondSelector = L'u'; ++constexpr wchar_t kSquareSelector = L'n'; ++constexpr wchar_t kStarSelector = L'H'; ++ + bool IsCheckBoxOrRadioButton(const CPDF_FormField* pFormField) { + return pFormField->GetFieldType() == FormFieldType::kCheckBox || + pFormField->GetFieldType() == FormFieldType::kRadioButton; +@@ -45,121 +58,99 @@ bool IsComboBoxOrTextField(const CPDF_FormField* pFormField) { + + void UpdateFormField(CPDFSDK_FormFillEnvironment* pFormFillEnv, + CPDF_FormField* pFormField, +- bool bChangeMark, +- bool bResetAP, +- bool bRefresh) { ++ bool bResetAP) { + CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm(); +- + if (bResetAP) { +- std::vector> widgets; ++ std::vector> widgets; + pForm->GetWidgets(pFormField, &widgets); + + if (IsComboBoxOrTextField(pFormField)) { +- for (auto& pObserved : widgets) { +- if (pObserved) { +- Optional sValue = +- ToCPDFSDKWidget(pObserved.Get())->OnFormat(); +- if (pObserved) { // Not redundant, may be clobbered by OnFormat. +- ToCPDFSDKWidget(pObserved.Get())->ResetAppearance(sValue, false); ++ for (auto& pWidget : widgets) { ++ if (pWidget) { ++ absl::optional sValue = pWidget->OnFormat(); ++ if (pWidget) { // Not redundant, may be clobbered by OnFormat. ++ pWidget->ResetAppearance(sValue, CPDFSDK_Widget::kValueUnchanged); + } + } + } + } else { +- for (auto& pObserved : widgets) { +- if (pObserved) +- ToCPDFSDKWidget(pObserved.Get())->ResetAppearance({}, false); ++ for (auto& pWidget : widgets) { ++ if (pWidget) { ++ pWidget->ResetAppearance(absl::nullopt, ++ CPDFSDK_Widget::kValueUnchanged); ++ } + } + } + } + +- if (bRefresh) { +- // Refresh the widget list. The calls in |bResetAP| may have caused widgets +- // to be removed from the list. We need to call |GetWidgets| again to be +- // sure none of the widgets have been deleted. +- std::vector> widgets; +- pForm->GetWidgets(pFormField, &widgets); +- +- // TODO(dsinclair): Determine if all widgets share the same +- // CPDFSDK_InteractiveForm. If that's the case, we can move the code to +- // |GetFormFillEnv| out of the loop. +- for (auto& pObserved : widgets) { +- if (pObserved) { +- CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pObserved.Get()); +- pWidget->GetInteractiveForm()->GetFormFillEnv()->UpdateAllViews( +- nullptr, pWidget); +- } +- } ++ // Refresh the widget list. The calls in |bResetAP| may have caused widgets ++ // to be removed from the list. We need to call |GetWidgets| again to be ++ // sure none of the widgets have been deleted. ++ std::vector> widgets; ++ pForm->GetWidgets(pFormField, &widgets); ++ for (auto& pWidget : widgets) { ++ if (pWidget) ++ pFormFillEnv->UpdateAllViews(pWidget.Get()); + } +- +- if (bChangeMark) +- pFormFillEnv->SetChangeMark(); ++ pFormFillEnv->SetChangeMark(); + } + + void UpdateFormControl(CPDFSDK_FormFillEnvironment* pFormFillEnv, + CPDF_FormControl* pFormControl, +- bool bChangeMark, +- bool bResetAP, +- bool bRefresh) { +- ASSERT(pFormControl); +- ++ bool bResetAP) { ++ DCHECK(pFormControl); + CPDFSDK_InteractiveForm* pForm = pFormFillEnv->GetInteractiveForm(); + CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl); +- + if (pWidget) { + ObservedPtr observed_widget(pWidget); + if (bResetAP) { + FormFieldType fieldType = pWidget->GetFieldType(); + if (fieldType == FormFieldType::kComboBox || + fieldType == FormFieldType::kTextField) { +- Optional sValue = pWidget->OnFormat(); ++ absl::optional sValue = pWidget->OnFormat(); + if (!observed_widget) + return; +- pWidget->ResetAppearance(sValue, false); ++ pWidget->ResetAppearance(sValue, CPDFSDK_Widget::kValueUnchanged); + } else { +- pWidget->ResetAppearance({}, false); ++ pWidget->ResetAppearance(absl::nullopt, ++ CPDFSDK_Widget::kValueUnchanged); + } + if (!observed_widget) + return; + } +- +- if (bRefresh) { +- CPDFSDK_InteractiveForm* pWidgetForm = pWidget->GetInteractiveForm(); +- pWidgetForm->GetFormFillEnv()->UpdateAllViews(nullptr, pWidget); +- } ++ pFormFillEnv->UpdateAllViews(pWidget); + } +- +- if (bChangeMark) +- pFormFillEnv->SetChangeMark(); ++ pFormFillEnv->SetChangeMark(); + } + +-// note: iControlNo = -1, means not a widget. +-void ParseFieldName(const WideString& strFieldNameParsed, +- WideString& strFieldName, +- int& iControlNo) { +- auto reverse_it = strFieldNameParsed.rbegin(); +- while (reverse_it != strFieldNameParsed.rend()) { ++struct FieldNameData { ++ FieldNameData(WideString field_name_in, int control_index_in) ++ : field_name(field_name_in), control_index(control_index_in) {} ++ ++ WideString field_name; ++ int control_index; ++}; ++ ++absl::optional ParseFieldName(const WideString& field_name) { ++ auto reverse_it = field_name.rbegin(); ++ while (reverse_it != field_name.rend()) { + if (*reverse_it == L'.') + break; + ++reverse_it; + } +- if (reverse_it == strFieldNameParsed.rend()) { +- strFieldName = strFieldNameParsed; +- iControlNo = -1; +- return; ++ if (reverse_it == field_name.rend()) { ++ return absl::nullopt; + } +- WideString suffixal = +- strFieldNameParsed.Last(reverse_it - strFieldNameParsed.rbegin()); +- iControlNo = FXSYS_wtoi(suffixal.c_str()); +- if (iControlNo == 0) { ++ WideString suffixal = field_name.Last(reverse_it - field_name.rbegin()); ++ int control_index = FXSYS_wtoi(suffixal.c_str()); ++ if (control_index == 0) { + suffixal.TrimRight(L' '); + if (suffixal != L"0") { +- strFieldName = strFieldNameParsed; +- iControlNo = -1; +- return; ++ return absl::nullopt; + } + } +- strFieldName = +- strFieldNameParsed.First(strFieldNameParsed.rend() - reverse_it - 1); ++ return FieldNameData(field_name.First(field_name.rend() - reverse_it - 1), ++ control_index); + } + + std::vector GetFormFieldsForName( +@@ -168,13 +159,37 @@ std::vector GetFormFieldsForName( + std::vector fields; + CPDFSDK_InteractiveForm* pReaderForm = pFormFillEnv->GetInteractiveForm(); + CPDF_InteractiveForm* pForm = pReaderForm->GetInteractiveForm(); +- for (int i = 0, sz = pForm->CountFields(csFieldName); i < sz; ++i) { +- if (CPDF_FormField* pFormField = pForm->GetField(i, csFieldName)) ++ const size_t sz = pForm->CountFields(csFieldName); ++ for (size_t i = 0; i < sz; ++i) { ++ CPDF_FormField* pFormField = pForm->GetField(i, csFieldName); ++ if (pFormField) + fields.push_back(pFormField); + } + return fields; + } + ++CFX_Color GetFormControlColor(CPDF_FormControl* pFormControl, ++ const ByteString& entry) { ++ switch (pFormControl->GetColorARGB(entry).color_type) { ++ case CFX_Color::Type::kTransparent: ++ return CFX_Color(CFX_Color::Type::kTransparent); ++ case CFX_Color::Type::kGray: ++ return CFX_Color(CFX_Color::Type::kGray, ++ pFormControl->GetOriginalColorComponent(0, entry)); ++ case CFX_Color::Type::kRGB: ++ return CFX_Color(CFX_Color::Type::kRGB, ++ pFormControl->GetOriginalColorComponent(0, entry), ++ pFormControl->GetOriginalColorComponent(1, entry), ++ pFormControl->GetOriginalColorComponent(2, entry)); ++ case CFX_Color::Type::kCMYK: ++ return CFX_Color(CFX_Color::Type::kCMYK, ++ pFormControl->GetOriginalColorComponent(0, entry), ++ pFormControl->GetOriginalColorComponent(1, entry), ++ pFormControl->GetOriginalColorComponent(2, entry), ++ pFormControl->GetOriginalColorComponent(3, entry)); ++ } ++} ++ + bool SetWidgetDisplayStatus(CPDFSDK_Widget* pWidget, int value) { + if (!pWidget) + return false; +@@ -217,20 +232,20 @@ bool SetWidgetDisplayStatus(CPDFSDK_Widget* pWidget, int value) { + void SetBorderStyle(CPDFSDK_FormFillEnvironment* pFormFillEnv, + const WideString& swFieldName, + int nControlIndex, +- const ByteString& string) { +- ASSERT(pFormFillEnv); +- +- BorderStyle nBorderStyle = BorderStyle::SOLID; +- if (string == "solid") +- nBorderStyle = BorderStyle::SOLID; +- else if (string == "beveled") +- nBorderStyle = BorderStyle::BEVELED; +- else if (string == "dashed") +- nBorderStyle = BorderStyle::DASH; +- else if (string == "inset") +- nBorderStyle = BorderStyle::INSET; +- else if (string == "underline") +- nBorderStyle = BorderStyle::UNDERLINE; ++ const ByteString& bsString) { ++ DCHECK(pFormFillEnv); ++ ++ BorderStyle nBorderStyle = BorderStyle::kSolid; ++ if (bsString == "solid") ++ nBorderStyle = BorderStyle::kSolid; ++ else if (bsString == "beveled") ++ nBorderStyle = BorderStyle::kBeveled; ++ else if (bsString == "dashed") ++ nBorderStyle = BorderStyle::kDash; ++ else if (bsString == "inset") ++ nBorderStyle = BorderStyle::kInset; ++ else if (bsString == "underline") ++ nBorderStyle = BorderStyle::kUnderline; + else + return; + +@@ -250,7 +265,7 @@ void SetBorderStyle(CPDFSDK_FormFillEnvironment* pFormFillEnv, + } + } + if (bSet) +- UpdateFormField(pFormFillEnv, pFormField, true, true, true); ++ UpdateFormField(pFormFillEnv, pFormField, true); + } else { + if (nControlIndex >= pFormField->CountControls()) + return; +@@ -260,7 +275,7 @@ void SetBorderStyle(CPDFSDK_FormFillEnvironment* pFormFillEnv, + if (pWidget) { + if (pWidget->GetBorderStyle() != nBorderStyle) { + pWidget->SetBorderStyle(nBorderStyle); +- UpdateFormControl(pFormFillEnv, pFormControl, true, true, true); ++ UpdateFormControl(pFormFillEnv, pFormControl, true); + } + } + } +@@ -272,7 +287,7 @@ void SetCurrentValueIndices(CPDFSDK_FormFillEnvironment* pFormFillEnv, + const WideString& swFieldName, + int nControlIndex, + const std::vector& array) { +- ASSERT(pFormFillEnv); ++ DCHECK(pFormFillEnv); + std::vector FieldArray = + GetFormFieldsForName(pFormFillEnv, swFieldName); + +@@ -287,11 +302,11 @@ void SetCurrentValueIndices(CPDFSDK_FormFillEnvironment* pFormFillEnv, + break; + if (array[i] < static_cast(pFormField->CountOptions()) && + !pFormField->IsItemSelected(array[i])) { +- pFormField->SetItemSelection(array[i], true, ++ pFormField->SetItemSelection(array[i], + NotificationOption::kDoNotNotify); + } + } +- UpdateFormField(pFormFillEnv, pFormField, true, true, true); ++ UpdateFormField(pFormFillEnv, pFormField, true); + } + } + +@@ -307,7 +322,7 @@ void SetDisplay(CPDFSDK_FormFillEnvironment* pFormFillEnv, + bool bAnySet = false; + for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) { + CPDF_FormControl* pFormControl = pFormField->GetControl(i); +- ASSERT(pFormControl); ++ DCHECK(pFormControl); + + CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl); + if (SetWidgetDisplayStatus(pWidget, number)) +@@ -315,7 +330,7 @@ void SetDisplay(CPDFSDK_FormFillEnvironment* pFormFillEnv, + } + + if (bAnySet) +- UpdateFormField(pFormFillEnv, pFormField, true, false, true); ++ UpdateFormField(pFormFillEnv, pFormField, false); + } else { + if (nControlIndex >= pFormField->CountControls()) + return; +@@ -326,7 +341,7 @@ void SetDisplay(CPDFSDK_FormFillEnvironment* pFormFillEnv, + + CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl); + if (SetWidgetDisplayStatus(pWidget, number)) +- UpdateFormControl(pFormFillEnv, pFormControl, true, false, true); ++ UpdateFormControl(pFormFillEnv, pFormControl, false); + } + } + } +@@ -351,7 +366,7 @@ void SetLineWidth(CPDFSDK_FormFillEnvironment* pFormFillEnv, + bool bSet = false; + for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) { + CPDF_FormControl* pFormControl = pFormField->GetControl(i); +- ASSERT(pFormControl); ++ DCHECK(pFormControl); + + if (CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl)) { + if (number != pWidget->GetBorderWidth()) { +@@ -361,7 +376,7 @@ void SetLineWidth(CPDFSDK_FormFillEnvironment* pFormFillEnv, + } + } + if (bSet) +- UpdateFormField(pFormFillEnv, pFormField, true, true, true); ++ UpdateFormField(pFormFillEnv, pFormField, true); + } else { + if (nControlIndex >= pFormField->CountControls()) + return; +@@ -370,7 +385,7 @@ void SetLineWidth(CPDFSDK_FormFillEnvironment* pFormFillEnv, + if (CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl)) { + if (number != pWidget->GetBorderWidth()) { + pWidget->SetBorderWidth(number); +- UpdateFormControl(pFormFillEnv, pFormControl, true, true, true); ++ UpdateFormControl(pFormFillEnv, pFormControl, true); + } + } + } +@@ -390,7 +405,7 @@ void SetRect(CPDFSDK_FormFillEnvironment* pFormFillEnv, + bool bSet = false; + for (int i = 0, sz = pFormField->CountControls(); i < sz; ++i) { + CPDF_FormControl* pFormControl = pFormField->GetControl(i); +- ASSERT(pFormControl); ++ DCHECK(pFormControl); + + if (CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl)) { + CFX_FloatRect crRect = rect; +@@ -410,7 +425,7 @@ void SetRect(CPDFSDK_FormFillEnvironment* pFormFillEnv, + } + + if (bSet) +- UpdateFormField(pFormFillEnv, pFormField, true, true, true); ++ UpdateFormField(pFormFillEnv, pFormField, true); + + continue; + } +@@ -428,7 +443,7 @@ void SetRect(CPDFSDK_FormFillEnvironment* pFormFillEnv, + if (crRect.left != rcOld.left || crRect.right != rcOld.right || + crRect.top != rcOld.top || crRect.bottom != rcOld.bottom) { + pWidget->SetRect(crRect); +- UpdateFormControl(pFormFillEnv, pFormControl, true, true, true); ++ UpdateFormControl(pFormFillEnv, pFormControl, true); + } + } + } +@@ -440,7 +455,7 @@ void SetFieldValue(CPDFSDK_FormFillEnvironment* pFormFillEnv, + const WideString& swFieldName, + int nControlIndex, + const std::vector& strArray) { +- ASSERT(pFormFillEnv); ++ DCHECK(pFormFillEnv); + if (strArray.empty()) + return; + +@@ -448,7 +463,7 @@ void SetFieldValue(CPDFSDK_FormFillEnvironment* pFormFillEnv, + GetFormFieldsForName(pFormFillEnv, swFieldName); + + for (CPDF_FormField* pFormField : FieldArray) { +- if (pFormField->GetFullName().Compare(swFieldName) != 0) ++ if (pFormField->GetFullName() != swFieldName) + continue; + + switch (pFormField->GetFieldType()) { +@@ -456,14 +471,14 @@ void SetFieldValue(CPDFSDK_FormFillEnvironment* pFormFillEnv, + case FormFieldType::kComboBox: + if (pFormField->GetValue() != strArray[0]) { + pFormField->SetValue(strArray[0], NotificationOption::kNotify); +- UpdateFormField(pFormFillEnv, pFormField, true, false, true); ++ UpdateFormField(pFormFillEnv, pFormField, false); + } + break; + case FormFieldType::kCheckBox: + case FormFieldType::kRadioButton: + if (pFormField->GetValue() != strArray[0]) { + pFormField->SetValue(strArray[0], NotificationOption::kNotify); +- UpdateFormField(pFormFillEnv, pFormField, true, false, true); ++ UpdateFormField(pFormFillEnv, pFormField, false); + } + break; + case FormFieldType::kListBox: { +@@ -479,10 +494,9 @@ void SetFieldValue(CPDFSDK_FormFillEnvironment* pFormFillEnv, + for (const auto& str : strArray) { + int index = pFormField->FindOption(str); + if (!pFormField->IsItemSelected(index)) +- pFormField->SetItemSelection(index, true, +- NotificationOption::kNotify); ++ pFormField->SetItemSelection(index, NotificationOption::kNotify); + } +- UpdateFormField(pFormFillEnv, pFormField, true, false, true); ++ UpdateFormField(pFormFillEnv, pFormField, false); + } + break; + } +@@ -492,6 +506,19 @@ void SetFieldValue(CPDFSDK_FormFillEnvironment* pFormFillEnv, + } + } + ++wchar_t GetSelectorFromCaptionForFieldType(const WideString& caption, ++ CPDF_FormField::Type type) { ++ if (!caption.IsEmpty()) ++ return caption[0]; ++ ++ switch (type) { ++ case CPDF_FormField::kRadioButton: ++ return kCircleSelector; ++ default: ++ return kCheckSelector; ++ } ++} ++ + } // namespace + + const JSPropertySpec CJS_Field::PropertySpecs[] = { +@@ -585,11 +612,11 @@ const JSMethodSpec CJS_Field::MethodSpecs[] = { + {"signatureSign", signatureSign_static}, + {"signatureValidate", signatureValidate_static}}; + +-int CJS_Field::ObjDefnID = -1; ++uint32_t CJS_Field::ObjDefnID = 0; + const char CJS_Field::kName[] = "Field"; + + // static +-int CJS_Field::GetObjDefnID() { ++uint32_t CJS_Field::GetObjDefnID() { + return ObjDefnID; + } + +@@ -610,9 +637,10 @@ bool CJS_Field::AttachField(CJS_Document* pDocument, + const WideString& csFieldName) { + m_pJSDoc.Reset(pDocument); + m_pFormFillEnv.Reset(pDocument->GetFormFillEnv()); +- m_bCanSet = m_pFormFillEnv->GetPermissions(FPDFPERM_FILL_FORM) || +- m_pFormFillEnv->GetPermissions(FPDFPERM_ANNOT_FORM) || +- m_pFormFillEnv->GetPermissions(FPDFPERM_MODIFY); ++ m_bCanSet = m_pFormFillEnv->HasPermissions( ++ pdfium::access_permissions::kFillForm | ++ pdfium::access_permissions::kModifyAnnotation | ++ pdfium::access_permissions::kModifyContent); + + CPDFSDK_InteractiveForm* pRDForm = m_pFormFillEnv->GetInteractiveForm(); + CPDF_InteractiveForm* pForm = pRDForm->GetInteractiveForm(); +@@ -620,14 +648,12 @@ bool CJS_Field::AttachField(CJS_Document* pDocument, + swFieldNameTemp.Replace(L"..", L"."); + + if (pForm->CountFields(swFieldNameTemp) <= 0) { +- WideString strFieldName; +- int iControlNo = -1; +- ParseFieldName(swFieldNameTemp, strFieldName, iControlNo); +- if (iControlNo == -1) ++ absl::optional parsed_data = ParseFieldName(swFieldNameTemp); ++ if (!parsed_data.has_value()) + return false; + +- m_FieldName = strFieldName; +- m_nFormControlIndex = iControlNo; ++ m_FieldName = parsed_data.value().field_name; ++ m_nFormControlIndex = parsed_data.value().control_index; + return true; + } + +@@ -656,7 +682,7 @@ CPDF_FormControl* CJS_Field::GetSmartFieldControl(CPDF_FormField* pFormField) { + } + + CJS_Result CJS_Field::get_alignment(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -682,7 +708,7 @@ CJS_Result CJS_Field::get_alignment(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_alignment(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + +@@ -690,7 +716,7 @@ CJS_Result CJS_Field::set_alignment(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Field::get_border_style(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -702,15 +728,15 @@ CJS_Result CJS_Field::get_border_style(CJS_Runtime* pRuntime) { + return CJS_Result::Failure(JSMessage::kBadObjectError); + + switch (pWidget->GetBorderStyle()) { +- case BorderStyle::SOLID: ++ case BorderStyle::kSolid: + return CJS_Result::Success(pRuntime->NewString("solid")); +- case BorderStyle::DASH: ++ case BorderStyle::kDash: + return CJS_Result::Success(pRuntime->NewString("dashed")); +- case BorderStyle::BEVELED: ++ case BorderStyle::kBeveled: + return CJS_Result::Success(pRuntime->NewString("beveled")); +- case BorderStyle::INSET: ++ case BorderStyle::kInset: + return CJS_Result::Success(pRuntime->NewString("inset")); +- case BorderStyle::UNDERLINE: ++ case BorderStyle::kUnderline: + return CJS_Result::Success(pRuntime->NewString("underline")); + } + return CJS_Result::Success(pRuntime->NewString("")); +@@ -718,11 +744,11 @@ CJS_Result CJS_Field::get_border_style(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_border_style(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + +- ByteString byte_str = pRuntime->ToWideString(vp).ToDefANSI(); ++ ByteString byte_str = pRuntime->ToByteString(vp); + if (m_bDelay) { + AddDelay_String(FP_BORDERSTYLE, byte_str); + } else { +@@ -733,7 +759,7 @@ CJS_Result CJS_Field::set_border_style(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Field::get_button_align_x(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -753,14 +779,14 @@ CJS_Result CJS_Field::get_button_align_x(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_button_align_x(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); + } + + CJS_Result CJS_Field::get_button_align_y(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -780,14 +806,14 @@ CJS_Result CJS_Field::get_button_align_y(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_button_align_y(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); + } + + CJS_Result CJS_Field::get_button_fit_bounds(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -806,14 +832,14 @@ CJS_Result CJS_Field::get_button_fit_bounds(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_button_fit_bounds(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); + } + + CJS_Result CJS_Field::get_button_position(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -832,14 +858,14 @@ CJS_Result CJS_Field::get_button_position(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_button_position(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kBadObjectError); + return CJS_Result::Success(); + } + + CJS_Result CJS_Field::get_button_scale_how(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -852,20 +878,20 @@ CJS_Result CJS_Field::get_button_scale_how(CJS_Runtime* pRuntime) { + if (!pFormControl) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- return CJS_Result::Success(pRuntime->NewBoolean( +- pFormControl->GetIconFit().IsProportionalScale() ? 0 : 1)); ++ return CJS_Result::Success( ++ pRuntime->NewBoolean(!pFormControl->GetIconFit().IsProportionalScale())); + } + + CJS_Result CJS_Field::set_button_scale_how(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); + } + + CJS_Result CJS_Field::get_button_scale_when(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -879,34 +905,27 @@ CJS_Result CJS_Field::get_button_scale_when(CJS_Runtime* pRuntime) { + return CJS_Result::Failure(JSMessage::kBadObjectError); + + CPDF_IconFit IconFit = pFormControl->GetIconFit(); +- int ScaleM = IconFit.GetScaleMethod(); +- switch (ScaleM) { +- case CPDF_IconFit::Always: ++ CPDF_IconFit::ScaleMethod scale_method = IconFit.GetScaleMethod(); ++ switch (scale_method) { ++ case CPDF_IconFit::ScaleMethod::kAlways: ++ case CPDF_IconFit::ScaleMethod::kBigger: ++ case CPDF_IconFit::ScaleMethod::kNever: ++ case CPDF_IconFit::ScaleMethod::kSmaller: + return CJS_Result::Success( +- pRuntime->NewNumber(static_cast(CPDF_IconFit::Always))); +- case CPDF_IconFit::Bigger: +- return CJS_Result::Success( +- pRuntime->NewNumber(static_cast(CPDF_IconFit::Bigger))); +- case CPDF_IconFit::Never: +- return CJS_Result::Success( +- pRuntime->NewNumber(static_cast(CPDF_IconFit::Never))); +- case CPDF_IconFit::Smaller: +- return CJS_Result::Success( +- pRuntime->NewNumber(static_cast(CPDF_IconFit::Smaller))); ++ pRuntime->NewNumber(static_cast(scale_method))); + } +- return CJS_Result::Success(); + } + + CJS_Result CJS_Field::set_button_scale_when(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); + } + + CJS_Result CJS_Field::get_calc_order_index(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -917,20 +936,20 @@ CJS_Result CJS_Field::get_calc_order_index(CJS_Runtime* pRuntime) { + + CPDFSDK_InteractiveForm* pRDForm = m_pFormFillEnv->GetInteractiveForm(); + CPDF_InteractiveForm* pForm = pRDForm->GetInteractiveForm(); +- return CJS_Result::Success(pRuntime->NewNumber( +- static_cast(pForm->FindFieldInCalculationOrder(pFormField)))); ++ return CJS_Result::Success( ++ pRuntime->NewNumber(pForm->FindFieldInCalculationOrder(pFormField))); + } + + CJS_Result CJS_Field::set_calc_order_index(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); + } + + CJS_Result CJS_Field::get_char_limit(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -944,14 +963,14 @@ CJS_Result CJS_Field::get_char_limit(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_char_limit(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); + } + + CJS_Result CJS_Field::get_comb(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -965,14 +984,14 @@ CJS_Result CJS_Field::get_comb(CJS_Runtime* pRuntime) { + } + + CJS_Result CJS_Field::set_comb(CJS_Runtime* pRuntime, v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); + } + + CJS_Result CJS_Field::get_commit_on_sel_change(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -988,7 +1007,7 @@ CJS_Result CJS_Field::get_commit_on_sel_change(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_commit_on_sel_change(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); +@@ -1027,7 +1046,7 @@ CJS_Result CJS_Field::set_current_value_indices(CJS_Runtime* pRuntime, + std::vector array; + if (vp->IsNumber()) { + array.push_back(pRuntime->ToInt32(vp)); +- } else if (!vp.IsEmpty() && vp->IsArray()) { ++ } else if (fxv8::IsArray(vp)) { + v8::Local SelArray = pRuntime->ToArray(vp); + for (size_t i = 0; i < pRuntime->GetArrayLength(SelArray); i++) { + array.push_back( +@@ -1054,7 +1073,7 @@ CJS_Result CJS_Field::set_default_style(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Field::get_default_value(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -1071,14 +1090,14 @@ CJS_Result CJS_Field::get_default_value(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_default_value(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); + } + + CJS_Result CJS_Field::get_do_not_scroll(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -1093,14 +1112,14 @@ CJS_Result CJS_Field::get_do_not_scroll(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_do_not_scroll(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); + } + + CJS_Result CJS_Field::get_do_not_spell_check(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -1116,21 +1135,12 @@ CJS_Result CJS_Field::get_do_not_spell_check(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_do_not_spell_check(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); + } + +-void CJS_Field::SetDelay(bool bDelay) { +- m_bDelay = bDelay; +- +- if (m_bDelay) +- return; +- if (m_pJSDoc) +- m_pJSDoc->DoFieldDelay(m_FieldName, m_nFormControlIndex); +-} +- + CJS_Result CJS_Field::get_delay(CJS_Runtime* pRuntime) { + return CJS_Result::Success(pRuntime->NewBoolean(m_bDelay)); + } +@@ -1253,7 +1263,7 @@ CJS_Result CJS_Field::set_export_values(CJS_Runtime* pRuntime, + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + +- if (vp.IsEmpty() || !vp->IsArray()) ++ if (!fxv8::IsArray(vp)) + return CJS_Result::Failure(JSMessage::kBadObjectError); + + return CJS_Result::Success(); +@@ -1295,30 +1305,7 @@ CJS_Result CJS_Field::get_fill_color(CJS_Runtime* pRuntime) { + if (!pFormControl) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- int iColorType; +- pFormControl->GetBackgroundColor(iColorType); +- +- CFX_Color color; +- if (iColorType == CFX_Color::kTransparent) { +- color = CFX_Color(CFX_Color::kTransparent); +- } else if (iColorType == CFX_Color::kGray) { +- color = CFX_Color(CFX_Color::kGray, +- pFormControl->GetOriginalBackgroundColor(0)); +- } else if (iColorType == CFX_Color::kRGB) { +- color = +- CFX_Color(CFX_Color::kRGB, pFormControl->GetOriginalBackgroundColor(0), +- pFormControl->GetOriginalBackgroundColor(1), +- pFormControl->GetOriginalBackgroundColor(2)); +- } else if (iColorType == CFX_Color::kCMYK) { +- color = +- CFX_Color(CFX_Color::kCMYK, pFormControl->GetOriginalBackgroundColor(0), +- pFormControl->GetOriginalBackgroundColor(1), +- pFormControl->GetOriginalBackgroundColor(2), +- pFormControl->GetOriginalBackgroundColor(3)); +- } else { +- return CJS_Result::Failure(JSMessage::kValueError); +- } +- ++ CFX_Color color = GetFormControlColor(pFormControl, pdfium::appearance::kBG); + v8::Local array = + CJS_Color::ConvertPWLColorToArray(pRuntime, color); + if (array.IsEmpty()) +@@ -1333,7 +1320,7 @@ CJS_Result CJS_Field::set_fill_color(CJS_Runtime* pRuntime, + return CJS_Result::Failure(JSMessage::kBadObjectError); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); +- if (vp.IsEmpty() || !vp->IsArray()) ++ if (!fxv8::IsArray(vp)) + return CJS_Result::Failure(JSMessage::kBadObjectError); + return CJS_Result::Success(); + } +@@ -1369,7 +1356,7 @@ CJS_Result CJS_Field::set_hidden(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Field::get_highlight(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -1384,15 +1371,15 @@ CJS_Result CJS_Field::get_highlight(CJS_Runtime* pRuntime) { + + int eHM = pFormControl->GetHighlightingMode(); + switch (eHM) { +- case CPDF_FormControl::None: ++ case CPDF_FormControl::kNone: + return CJS_Result::Success(pRuntime->NewString("none")); +- case CPDF_FormControl::Push: ++ case CPDF_FormControl::kPush: + return CJS_Result::Success(pRuntime->NewString("push")); +- case CPDF_FormControl::Invert: ++ case CPDF_FormControl::kInvert: + return CJS_Result::Success(pRuntime->NewString("invert")); +- case CPDF_FormControl::Outline: ++ case CPDF_FormControl::kOutline: + return CJS_Result::Success(pRuntime->NewString("outline")); +- case CPDF_FormControl::Toggle: ++ case CPDF_FormControl::kToggle: + return CJS_Result::Success(pRuntime->NewString("toggle")); + } + return CJS_Result::Success(); +@@ -1400,7 +1387,7 @@ CJS_Result CJS_Field::get_highlight(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_highlight(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); +@@ -1441,7 +1428,7 @@ CJS_Result CJS_Field::set_line_width(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Field::get_multiline(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -1456,14 +1443,14 @@ CJS_Result CJS_Field::get_multiline(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_multiline(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); + } + + CJS_Result CJS_Field::get_multiple_selection(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) + return CJS_Result::Failure(JSMessage::kBadObjectError); +@@ -1478,7 +1465,7 @@ CJS_Result CJS_Field::get_multiple_selection(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_multiple_selection(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); +@@ -1517,25 +1504,20 @@ CJS_Result CJS_Field::get_page(CJS_Runtime* pRuntime) { + if (!pFormField) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- std::vector> widgets; ++ std::vector> widgets; + m_pFormFillEnv->GetInteractiveForm()->GetWidgets(pFormField, &widgets); + if (widgets.empty()) + return CJS_Result::Success(pRuntime->NewNumber(-1)); + + v8::Local PageArray = pRuntime->NewArray(); + int i = 0; +- for (const auto& pObserved : widgets) { +- if (!pObserved) +- return CJS_Result::Failure(JSMessage::kBadObjectError); +- +- auto* pWidget = ToCPDFSDKWidget(pObserved.Get()); +- CPDFSDK_PageView* pPageView = pWidget->GetPageView(); +- if (!pPageView) ++ for (const auto& pWidget : widgets) { ++ if (!pWidget) + return CJS_Result::Failure(JSMessage::kBadObjectError); + + pRuntime->PutArrayElement( + PageArray, i, +- pRuntime->NewNumber(static_cast(pPageView->GetPageIndex()))); ++ pRuntime->NewNumber(pWidget->GetPageView()->GetPageIndex())); + ++i; + } + return CJS_Result::Success(PageArray); +@@ -1546,7 +1528,7 @@ CJS_Result CJS_Field::set_page(CJS_Runtime* pRuntime, v8::Local vp) { + } + + CJS_Result CJS_Field::get_password(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -1561,7 +1543,7 @@ CJS_Result CJS_Field::get_password(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_password(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); +@@ -1611,7 +1593,7 @@ CJS_Result CJS_Field::set_print(CJS_Runtime* pRuntime, + } + + if (bSet) +- UpdateFormField(m_pFormFillEnv.Get(), pFormField, true, false, true); ++ UpdateFormField(m_pFormFillEnv.Get(), pFormField, false); + + continue; + } +@@ -1631,8 +1613,7 @@ CJS_Result CJS_Field::set_print(CJS_Runtime* pRuntime, + if (dwFlags != pWidget->GetFlags()) { + pWidget->SetFlags(dwFlags); + UpdateFormControl(m_pFormFillEnv.Get(), +- pFormField->GetControl(m_nFormControlIndex), true, +- false, true); ++ pFormField->GetControl(m_nFormControlIndex), false); + } + } + } +@@ -1674,11 +1655,21 @@ CJS_Result CJS_Field::get_readonly(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_readonly(CJS_Runtime* pRuntime, + v8::Local vp) { +- std::vector FieldArray = GetFormFields(); +- if (FieldArray.empty()) ++ CPDF_FormField* pFormField = GetFirstFormField(); ++ if (!pFormField) + return CJS_Result::Failure(JSMessage::kBadObjectError); ++ + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); ++ ++ const bool bReadOnly = pRuntime->ToBoolean(vp); ++ const uint32_t dwFlags = pFormField->GetFieldFlags(); ++ const uint32_t dwNewFlags = bReadOnly ++ ? (dwFlags | pdfium::form_flags::kReadOnly) ++ : (dwFlags & ~pdfium::form_flags::kReadOnly); ++ if (dwNewFlags != dwFlags) ++ pFormField->SetFieldFlags(dwNewFlags); ++ + return CJS_Result::Success(); + } + +@@ -1709,24 +1700,23 @@ CJS_Result CJS_Field::get_rect(CJS_Runtime* pRuntime) { + CJS_Result CJS_Field::set_rect(CJS_Runtime* pRuntime, v8::Local vp) { + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); +- if (vp.IsEmpty() || !vp->IsArray()) ++ if (!fxv8::IsArray(vp)) + return CJS_Result::Failure(JSMessage::kValueError); + + v8::Local rcArray = pRuntime->ToArray(vp); + if (pRuntime->GetArrayLength(rcArray) < 4) + return CJS_Result::Failure(JSMessage::kValueError); + +- float pArray[4]; +- pArray[0] = static_cast( ++ float f0 = static_cast( + pRuntime->ToInt32(pRuntime->GetArrayElement(rcArray, 0))); +- pArray[1] = static_cast( ++ float f1 = static_cast( + pRuntime->ToInt32(pRuntime->GetArrayElement(rcArray, 1))); +- pArray[2] = static_cast( ++ float f2 = static_cast( + pRuntime->ToInt32(pRuntime->GetArrayElement(rcArray, 2))); +- pArray[3] = static_cast( ++ float f3 = static_cast( + pRuntime->ToInt32(pRuntime->GetArrayElement(rcArray, 3))); + +- CFX_FloatRect crRect(pArray); ++ CFX_FloatRect crRect(f0, f1, f2, f3); + if (m_bDelay) { + AddDelay_Rect(FP_RECT, crRect); + } else { +@@ -1758,7 +1748,7 @@ CJS_Result CJS_Field::set_required(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Field::get_rich_text(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -1773,7 +1763,7 @@ CJS_Result CJS_Field::get_rich_text(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_rich_text(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); +@@ -1789,7 +1779,7 @@ CJS_Result CJS_Field::set_rich_value(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Field::get_rotation(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -1804,7 +1794,7 @@ CJS_Result CJS_Field::get_rotation(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_rotation(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); +@@ -1828,28 +1818,7 @@ CJS_Result CJS_Field::get_stroke_color(CJS_Runtime* pRuntime) { + if (!pFormControl) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- int iColorType; +- pFormControl->GetBorderColor(iColorType); +- +- CFX_Color color; +- if (iColorType == CFX_Color::kTransparent) { +- color = CFX_Color(CFX_Color::kTransparent); +- } else if (iColorType == CFX_Color::kGray) { +- color = +- CFX_Color(CFX_Color::kGray, pFormControl->GetOriginalBorderColor(0)); +- } else if (iColorType == CFX_Color::kRGB) { +- color = CFX_Color(CFX_Color::kRGB, pFormControl->GetOriginalBorderColor(0), +- pFormControl->GetOriginalBorderColor(1), +- pFormControl->GetOriginalBorderColor(2)); +- } else if (iColorType == CFX_Color::kCMYK) { +- color = CFX_Color(CFX_Color::kCMYK, pFormControl->GetOriginalBorderColor(0), +- pFormControl->GetOriginalBorderColor(1), +- pFormControl->GetOriginalBorderColor(2), +- pFormControl->GetOriginalBorderColor(3)); +- } else { +- return CJS_Result::Failure(JSMessage::kObjectTypeError); +- } +- ++ CFX_Color color = GetFormControlColor(pFormControl, pdfium::appearance::kBC); + v8::Local array = + CJS_Color::ConvertPWLColorToArray(pRuntime, color); + if (array.IsEmpty()) +@@ -1861,13 +1830,13 @@ CJS_Result CJS_Field::set_stroke_color(CJS_Runtime* pRuntime, + v8::Local vp) { + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); +- if (vp.IsEmpty() || !vp->IsArray()) ++ if (!fxv8::IsArray(vp)) + return CJS_Result::Failure(JSMessage::kBadObjectError); + return CJS_Result::Success(); + } + + CJS_Result CJS_Field::get_style(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -1880,37 +1849,37 @@ CJS_Result CJS_Field::get_style(CJS_Runtime* pRuntime) { + if (!pFormControl) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- WideString csWCaption = pFormControl->GetNormalCaption(); +- wchar_t selector = !csWCaption.IsEmpty() ? csWCaption[0] : L'4'; ++ wchar_t selector = GetSelectorFromCaptionForFieldType( ++ pFormControl->GetNormalCaption(), pFormControl->GetType()); + + ByteString csBCaption; + switch (selector) { +- case L'l': ++ case kCircleSelector: + csBCaption = "circle"; + break; +- case L'8': ++ case kCrossSelector: + csBCaption = "cross"; + break; +- case L'u': ++ case kDiamondSelector: + csBCaption = "diamond"; + break; +- case L'n': ++ case kSquareSelector: + csBCaption = "square"; + break; +- case L'H': ++ case kStarSelector: + csBCaption = "star"; + break; +- default: // L'4' ++ case kCheckSelector: ++ default: + csBCaption = "check"; + break; + } +- return CJS_Result::Success(pRuntime->NewString( +- WideString::FromDefANSI(csBCaption.AsStringView()).AsStringView())); ++ return CJS_Result::Success(pRuntime->NewString(csBCaption.AsStringView())); + } + + CJS_Result CJS_Field::set_style(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); +@@ -1934,21 +1903,21 @@ CJS_Result CJS_Field::get_text_color(CJS_Runtime* pRuntime) { + if (!pFormControl) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- Optional iColorType; +- FX_ARGB color; + CPDF_DefaultAppearance FieldAppearance = pFormControl->GetDefaultAppearance(); +- std::tie(iColorType, color) = FieldAppearance.GetColor(); ++ absl::optional maybe_type_argb_pair = ++ FieldAppearance.GetColorARGB(); + + CFX_Color crRet; +- if (!iColorType || *iColorType == CFX_Color::kTransparent) { +- crRet = CFX_Color(CFX_Color::kTransparent); +- } else { ++ if (maybe_type_argb_pair.has_value() && ++ maybe_type_argb_pair.value().color_type != ++ CFX_Color::Type::kTransparent) { + int32_t a; + int32_t r; + int32_t g; + int32_t b; +- std::tie(a, r, g, b) = ArgbDecode(color); +- crRet = CFX_Color(CFX_Color::kRGB, r / 255.0f, g / 255.0f, b / 255.0f); ++ std::tie(a, r, g, b) = ArgbDecode(maybe_type_argb_pair.value().argb); ++ crRet = ++ CFX_Color(CFX_Color::Type::kRGB, r / 255.0f, g / 255.0f, b / 255.0f); + } + + v8::Local array = +@@ -1962,13 +1931,13 @@ CJS_Result CJS_Field::set_text_color(CJS_Runtime* pRuntime, + v8::Local vp) { + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); +- if (vp.IsEmpty() || !vp->IsArray()) ++ if (!fxv8::IsArray(vp)) + return CJS_Result::Failure(JSMessage::kBadObjectError); + return CJS_Result::Success(); + } + + CJS_Result CJS_Field::get_text_font(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -1986,7 +1955,8 @@ CJS_Result CJS_Field::get_text_font(CJS_Runtime* pRuntime) { + return CJS_Result::Failure(JSMessage::kObjectTypeError); + } + +- Optional wsFontName = pFormControl->GetDefaultControlFontName(); ++ absl::optional wsFontName = ++ pFormControl->GetDefaultControlFontName(); + if (!wsFontName.has_value()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +@@ -1996,17 +1966,17 @@ CJS_Result CJS_Field::get_text_font(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_text_font(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); +- if (pRuntime->ToWideString(vp).ToDefANSI().IsEmpty()) ++ if (pRuntime->ToByteString(vp).IsEmpty()) + return CJS_Result::Failure(JSMessage::kValueError); + return CJS_Result::Success(); + } + + CJS_Result CJS_Field::get_text_size(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -2024,7 +1994,7 @@ CJS_Result CJS_Field::get_text_size(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_text_size(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); +@@ -2062,7 +2032,7 @@ CJS_Result CJS_Field::set_type(CJS_Runtime* pRuntime, v8::Local vp) { + } + + CJS_Result CJS_Field::get_user_name(CJS_Runtime* pRuntime) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + + CPDF_FormField* pFormField = GetFirstFormField(); + if (!pFormField) +@@ -2074,7 +2044,7 @@ CJS_Result CJS_Field::get_user_name(CJS_Runtime* pRuntime) { + + CJS_Result CJS_Field::set_user_name(CJS_Runtime* pRuntime, + v8::Local vp) { +- ASSERT(m_pFormFillEnv); ++ DCHECK(m_pFormFillEnv); + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + return CJS_Result::Success(); +@@ -2102,7 +2072,7 @@ CJS_Result CJS_Field::get_value(CJS_Runtime* pRuntime) { + iIndex = pFormField->GetSelectedIndex(i); + ElementValue = pRuntime->NewString( + pFormField->GetOptionValue(iIndex).AsStringView()); +- if (wcslen(pRuntime->ToWideString(ElementValue).c_str()) == 0) { ++ if (pRuntime->ToWideString(ElementValue).IsEmpty()) { + ElementValue = pRuntime->NewString( + pFormField->GetOptionLabel(iIndex).AsStringView()); + } +@@ -2143,7 +2113,7 @@ CJS_Result CJS_Field::set_value(CJS_Runtime* pRuntime, + return CJS_Result::Failure(JSMessage::kReadOnlyError); + + std::vector strArray; +- if (!vp.IsEmpty() && vp->IsArray()) { ++ if (fxv8::IsArray(vp)) { + v8::Local ValueArray = pRuntime->ToArray(vp); + for (size_t i = 0; i < pRuntime->GetArrayLength(ValueArray); i++) { + strArray.push_back( +@@ -2214,7 +2184,7 @@ CJS_Result CJS_Field::browseForFileToSubmit( + WideString wsFileName = m_pFormFillEnv->JS_fieldBrowse(); + if (!wsFileName.IsEmpty()) { + pFormField->SetValue(wsFileName, NotificationOption::kDoNotNotify); +- UpdateFormField(m_pFormFillEnv.Get(), pFormField, true, true, true); ++ UpdateFormField(m_pFormFillEnv.Get(), pFormField, true); + } + return CJS_Result::Success(); + } +@@ -2225,8 +2195,7 @@ CJS_Result CJS_Field::buttonGetCaption( + CJS_Runtime* pRuntime, + const std::vector>& params) { + int nface = 0; +- int iSize = params.size(); +- if (iSize >= 1) ++ if (params.size() >= 1) + nface = pRuntime->ToInt32(params[0]); + + CPDF_FormField* pFormField = GetFirstFormField(); +@@ -2280,7 +2249,8 @@ CJS_Result CJS_Field::buttonGetIcon( + if (pObj.IsEmpty()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- auto* pJS_Icon = static_cast(CFXJS_Engine::GetObjectPrivate(pObj)); ++ auto* pJS_Icon = static_cast( ++ CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj)); + return pJS_Icon ? CJS_Result::Success(pJS_Icon->ToV8Object()) + : CJS_Result::Failure(JSMessage::kBadObjectError); + } +@@ -2306,8 +2276,8 @@ CJS_Result CJS_Field::buttonSetIcon( + CJS_Result CJS_Field::checkThisBox( + CJS_Runtime* pRuntime, + const std::vector>& params) { +- int iSize = params.size(); +- if (iSize < 1) ++ const size_t nSize = params.size(); ++ if (nSize == 0) + return CJS_Result::Failure(JSMessage::kParamError); + + if (!m_bCanSet) +@@ -2315,7 +2285,7 @@ CJS_Result CJS_Field::checkThisBox( + + int nWidget = pRuntime->ToInt32(params[0]); + bool bCheckit = true; +- if (iSize >= 2) ++ if (nSize >= 2) + bCheckit = pRuntime->ToBoolean(params[1]); + + CPDF_FormField* pFormField = GetFirstFormField(); +@@ -2331,7 +2301,7 @@ CJS_Result CJS_Field::checkThisBox( + // TODO(weili): Check whether anything special needed for radio button. + // (When pFormField->GetFieldType() == FormFieldType::kRadioButton.) + pFormField->CheckControl(nWidget, bCheckit, NotificationOption::kNotify); +- UpdateFormField(m_pFormFillEnv.Get(), pFormField, true, true, true); ++ UpdateFormField(m_pFormFillEnv.Get(), pFormField, true); + return CJS_Result::Success(); + } + +@@ -2347,8 +2317,7 @@ CJS_Result CJS_Field::defaultIsChecked( + if (!m_bCanSet) + return CJS_Result::Failure(JSMessage::kReadOnlyError); + +- int iSize = params.size(); +- if (iSize < 1) ++ if (params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); + + CPDF_FormField* pFormField = GetFirstFormField(); +@@ -2378,7 +2347,7 @@ CJS_Result CJS_Field::getArray( + + std::vector> swSort; + for (CPDF_FormField* pFormField : FieldArray) { +- swSort.push_back(pdfium::MakeUnique(pFormField->GetFullName())); ++ swSort.push_back(std::make_unique(pFormField->GetFullName())); + } + + std::sort(swSort.begin(), swSort.end(), +@@ -2393,8 +2362,8 @@ CJS_Result CJS_Field::getArray( + if (pObj.IsEmpty()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- auto* pJSField = +- static_cast(CFXJS_Engine::GetObjectPrivate(pObj)); ++ auto* pJSField = static_cast( ++ CFXJS_Engine::GetObjectPrivate(pRuntime->GetIsolate(), pObj)); + pJSField->AttachField(m_pJSDoc.Get(), *pStr); + pRuntime->PutArrayElement(FormFieldArray, j++, + pJSField +@@ -2407,13 +2376,13 @@ CJS_Result CJS_Field::getArray( + CJS_Result CJS_Field::getItemAt( + CJS_Runtime* pRuntime, + const std::vector>& params) { +- int iSize = params.size(); ++ const size_t nSize = params.size(); + int nIdx = -1; +- if (iSize >= 1) ++ if (nSize >= 1) + nIdx = pRuntime->ToInt32(params[0]); + + bool bExport = true; +- if (iSize >= 2) ++ if (nSize >= 2) + bExport = pRuntime->ToBoolean(params[1]); + + CPDF_FormField* pFormField = GetFirstFormField(); +@@ -2509,18 +2478,16 @@ CJS_Result CJS_Field::setFocus( + if (nCount == 1) { + pWidget = pForm->GetWidget(pFormField->GetControl(0)); + } else { +- IPDF_Page* pPage = IPDFPageFromFPDFPage(m_pFormFillEnv->GetCurrentPage()); ++ IPDF_Page* pPage = m_pFormFillEnv->GetCurrentPage(); + if (!pPage) + return CJS_Result::Failure(JSMessage::kBadObjectError); +- if (CPDFSDK_PageView* pCurPageView = +- m_pFormFillEnv->GetPageView(pPage, true)) { +- for (int32_t i = 0; i < nCount; i++) { +- if (CPDFSDK_Widget* pTempWidget = +- pForm->GetWidget(pFormField->GetControl(i))) { +- if (pTempWidget->GetPDFPage() == pCurPageView->GetPDFPage()) { +- pWidget = pTempWidget; +- break; +- } ++ CPDFSDK_PageView* pCurPageView = m_pFormFillEnv->GetOrCreatePageView(pPage); ++ for (int32_t i = 0; i < nCount; i++) { ++ if (CPDFSDK_Widget* pTempWidget = ++ pForm->GetWidget(pFormField->GetControl(i))) { ++ if (pTempWidget->GetPDFPage() == pCurPageView->GetPDFPage()) { ++ pWidget = pTempWidget; ++ break; + } + } + } +@@ -2528,7 +2495,7 @@ CJS_Result CJS_Field::setFocus( + + if (pWidget) { + ObservedPtr pObserved(pWidget); +- m_pFormFillEnv->SetFocusAnnot(&pObserved); ++ m_pFormFillEnv->SetFocusAnnot(pObserved); + } + + return CJS_Result::Success(); +@@ -2581,30 +2548,39 @@ CJS_Result CJS_Field::signatureValidate( + return CJS_Result::Failure(JSMessage::kNotSupportedError); + } + ++void CJS_Field::SetDelay(bool bDelay) { ++ m_bDelay = bDelay; ++ if (m_bDelay) ++ return; ++ ++ if (m_pJSDoc) ++ m_pJSDoc->DoFieldDelay(m_FieldName, m_nFormControlIndex); ++} ++ + void CJS_Field::AddDelay_Int(FIELD_PROP prop, int32_t n) { + auto pNewData = +- pdfium::MakeUnique(prop, m_nFormControlIndex, m_FieldName); ++ std::make_unique(prop, m_nFormControlIndex, m_FieldName); + pNewData->num = n; + m_pJSDoc->AddDelayData(std::move(pNewData)); + } + + void CJS_Field::AddDelay_Bool(FIELD_PROP prop, bool b) { + auto pNewData = +- pdfium::MakeUnique(prop, m_nFormControlIndex, m_FieldName); ++ std::make_unique(prop, m_nFormControlIndex, m_FieldName); + pNewData->b = b; + m_pJSDoc->AddDelayData(std::move(pNewData)); + } + + void CJS_Field::AddDelay_String(FIELD_PROP prop, const ByteString& str) { + auto pNewData = +- pdfium::MakeUnique(prop, m_nFormControlIndex, m_FieldName); ++ std::make_unique(prop, m_nFormControlIndex, m_FieldName); + pNewData->bytestring = str; + m_pJSDoc->AddDelayData(std::move(pNewData)); + } + + void CJS_Field::AddDelay_Rect(FIELD_PROP prop, const CFX_FloatRect& rect) { + auto pNewData = +- pdfium::MakeUnique(prop, m_nFormControlIndex, m_FieldName); ++ std::make_unique(prop, m_nFormControlIndex, m_FieldName); + pNewData->rect = rect; + m_pJSDoc->AddDelayData(std::move(pNewData)); + } +@@ -2612,7 +2588,7 @@ void CJS_Field::AddDelay_Rect(FIELD_PROP prop, const CFX_FloatRect& rect) { + void CJS_Field::AddDelay_WordArray(FIELD_PROP prop, + const std::vector& array) { + auto pNewData = +- pdfium::MakeUnique(prop, m_nFormControlIndex, m_FieldName); ++ std::make_unique(prop, m_nFormControlIndex, m_FieldName); + pNewData->wordarray = array; + m_pJSDoc->AddDelayData(std::move(pNewData)); + } +@@ -2620,14 +2596,14 @@ void CJS_Field::AddDelay_WordArray(FIELD_PROP prop, + void CJS_Field::AddDelay_WideStringArray(FIELD_PROP prop, + const std::vector& array) { + auto pNewData = +- pdfium::MakeUnique(prop, m_nFormControlIndex, m_FieldName); ++ std::make_unique(prop, m_nFormControlIndex, m_FieldName); + pNewData->widestringarray = array; + m_pJSDoc->AddDelayData(std::move(pNewData)); + } + + void CJS_Field::DoDelay(CPDFSDK_FormFillEnvironment* pFormFillEnv, + CJS_DelayData* pData) { +- ASSERT(pFormFillEnv); ++ DCHECK(pFormFillEnv); + switch (pData->eProp) { + case FP_BORDERSTYLE: + SetBorderStyle(pFormFillEnv, pData->sFieldName, pData->nControlIndex, +diff --git a/fxjs/cjs_field.h b/fxjs/cjs_field.h +index d772e66a5..7a662779b 100644 +--- a/fxjs/cjs_field.h ++++ b/fxjs/cjs_field.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,8 +13,8 @@ + #include "fxjs/cjs_object.h" + #include "fxjs/js_define.h" + ++class CFX_FloatRect; + class CPDF_FormControl; +-class CPDFSDK_Widget; + struct CJS_DelayData; + + enum FIELD_PROP { +@@ -29,7 +29,7 @@ enum FIELD_PROP { + + class CJS_Field final : public CJS_Object { + public: +- static int GetObjDefnID(); ++ static uint32_t GetObjDefnID(); + static void DefineJSObjects(CFXJS_Engine* pEngine); + static void DoDelay(CPDFSDK_FormFillEnvironment* pFormFillEnv, + CJS_DelayData* pData); +@@ -123,7 +123,7 @@ class CJS_Field final : public CJS_Object { + CJS_Result set_text_color(CJS_Runtime* pRuntime, v8::Local vp); + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const char kName[]; + static const JSPropertySpec PropertySpecs[]; + static const JSMethodSpec MethodSpecs[]; +@@ -349,11 +349,11 @@ class CJS_Field final : public CJS_Object { + CJS_Result signatureValidate(CJS_Runtime* pRuntime, + const std::vector>& params); + +- void SetDelay(bool bDelay); + std::vector GetFormFields() const; + CPDF_FormField* GetFirstFormField() const; + CPDF_FormControl* GetSmartFieldControl(CPDF_FormField* pFormField); + ++ void SetDelay(bool bDelay); + void AddDelay_Int(FIELD_PROP prop, int32_t n); + void AddDelay_Bool(FIELD_PROP prop, bool b); + void AddDelay_String(FIELD_PROP prop, const ByteString& str); +@@ -361,7 +361,6 @@ class CJS_Field final : public CJS_Object { + void AddDelay_WordArray(FIELD_PROP prop, const std::vector& array); + void AddDelay_WideStringArray(FIELD_PROP prop, + const std::vector& array); +- + void DoDelay(); + + ObservedPtr m_pJSDoc; +diff --git a/fxjs/cjs_font.cpp b/fxjs/cjs_font.cpp +index 0e6e94bed..fcf69ac02 100644 +--- a/fxjs/cjs_font.cpp ++++ b/fxjs/cjs_font.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,7 +22,7 @@ const JSConstSpec CJS_Font::ConstSpecs[] = { + {"Symbol", JSConstSpec::String, 0, "Symbol"}, + {"ZapfD", JSConstSpec::String, 0, "ZapfDingbats"}}; + +-int CJS_Font::ObjDefnID = -1; ++uint32_t CJS_Font::ObjDefnID = 0; + + // static + void CJS_Font::DefineJSObjects(CFXJS_Engine* pEngine) { +diff --git a/fxjs/cjs_font.h b/fxjs/cjs_font.h +index d3cf1592d..a4ecca164 100644 +--- a/fxjs/cjs_font.h ++++ b/fxjs/cjs_font.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,7 +16,7 @@ class CJS_Font final : public CJS_Object { + CJS_Font() = delete; + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const JSConstSpec ConstSpecs[]; + }; + +diff --git a/fxjs/cjs_global.cpp b/fxjs/cjs_global.cpp +index 8ac31815b..101dee2cb 100644 +--- a/fxjs/cjs_global.cpp ++++ b/fxjs/cjs_global.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,6 @@ + + #include "fxjs/cjs_global.h" + +-#include + #include + #include + #include +@@ -15,110 +14,20 @@ + #include "fxjs/cfx_globaldata.h" + #include "fxjs/cfx_keyvalue.h" + #include "fxjs/cjs_event_context.h" +-#include "fxjs/cjs_eventrecorder.h" + #include "fxjs/cjs_object.h" ++#include "fxjs/fxv8.h" + #include "fxjs/js_define.h" + #include "fxjs/js_resources.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" ++#include "v8/include/v8-isolate.h" + + namespace { + +-WideString PropFromV8Prop(v8::Isolate* pIsolate, +- v8::Local property) { +- v8::String::Utf8Value utf8_value(pIsolate, property); +- return WideString::FromUTF8(ByteStringView(*utf8_value, utf8_value.length())); +-} +- +-template +-void JSSpecialPropQuery(const char*, +- v8::Local property, +- const v8::PropertyCallbackInfo& info) { +- auto pObj = JSGetObject(info.Holder()); +- if (!pObj) +- return; +- +- CJS_Runtime* pRuntime = pObj->GetRuntime(); +- if (!pRuntime) +- return; +- +- CJS_Result result = +- pObj->QueryProperty(PropFromV8Prop(info.GetIsolate(), property).c_str()); +- +- info.GetReturnValue().Set(!result.HasError() ? 4 : 0); +-} +- +-template +-void JSSpecialPropGet(const char* class_name, +- v8::Local property, +- const v8::PropertyCallbackInfo& info) { +- auto pObj = JSGetObject(info.Holder()); +- if (!pObj) +- return; +- +- CJS_Runtime* pRuntime = pObj->GetRuntime(); +- if (!pRuntime) +- return; +- +- CJS_Result result = pObj->GetProperty( +- pRuntime, PropFromV8Prop(info.GetIsolate(), property).c_str()); +- +- if (result.HasError()) { +- pRuntime->Error( +- JSFormatErrorString(class_name, "GetProperty", result.Error())); +- return; +- } +- if (result.HasReturn()) +- info.GetReturnValue().Set(result.Return()); +-} +- +-template +-void JSSpecialPropPut(const char* class_name, +- v8::Local property, +- v8::Local value, +- const v8::PropertyCallbackInfo& info) { +- auto pObj = JSGetObject(info.Holder()); +- if (!pObj) +- return; +- +- CJS_Runtime* pRuntime = pObj->GetRuntime(); +- if (!pRuntime) +- return; +- +- CJS_Result result = pObj->SetProperty( +- pRuntime, PropFromV8Prop(info.GetIsolate(), property).c_str(), value); +- +- if (result.HasError()) { +- pRuntime->Error( +- JSFormatErrorString(class_name, "PutProperty", result.Error())); +- } +-} +- +-template +-void JSSpecialPropDel(const char* class_name, +- v8::Local property, +- const v8::PropertyCallbackInfo& info) { +- auto pObj = JSGetObject(info.Holder()); +- if (!pObj) +- return; +- +- CJS_Runtime* pRuntime = pObj->GetRuntime(); +- if (!pRuntime) +- return; +- +- CJS_Result result = pObj->DelProperty( +- pRuntime, PropFromV8Prop(info.GetIsolate(), property).c_str()); +- if (result.HasError()) { +- // TODO(dsinclair): Should this set the pRuntime->Error result? +- // ByteString cbName = +- // ByteString::Format("%s.%s", class_name, "DelProperty"); +- } +-} +- +-template +-v8::Local GetV8StringFromProperty(v8::Local property, +- const T& info) { +- return property->ToString(info.GetIsolate()->GetCurrentContext()) +- .ToLocalChecked(); ++ByteString ByteStringFromV8Name(v8::Isolate* pIsolate, ++ v8::Local name) { ++ CHECK(name->IsString()); ++ return fxv8::ToByteString(pIsolate, name.As()); + } + + } // namespace +@@ -130,7 +39,7 @@ CJS_Global::JSGlobalData::~JSGlobalData() = default; + const JSMethodSpec CJS_Global::MethodSpecs[] = { + {"setPersistent", setPersistent_static}}; + +-int CJS_Global::ObjDefnID = -1; ++uint32_t CJS_Global::ObjDefnID = 0; + + // static + void CJS_Global::setPersistent_static( +@@ -143,24 +52,36 @@ void CJS_Global::setPersistent_static( + void CJS_Global::queryprop_static( + v8::Local property, + const v8::PropertyCallbackInfo& info) { +- ASSERT(property->IsString()); +- JSSpecialPropQuery( +- "global", +- v8::Local::New(info.GetIsolate(), +- GetV8StringFromProperty(property, info)), +- info); ++ auto pObj = JSGetObject(info.GetIsolate(), info.Holder()); ++ if (!pObj) ++ return; ++ ++ ByteString bsProp = ByteStringFromV8Name(info.GetIsolate(), property); ++ if (pObj->HasProperty(bsProp)) ++ info.GetReturnValue().Set(static_cast(v8::PropertyAttribute::None)); + } + + // static + void CJS_Global::getprop_static( + v8::Local property, + const v8::PropertyCallbackInfo& info) { +- ASSERT(property->IsString()); +- JSSpecialPropGet( +- "global", +- v8::Local::New(info.GetIsolate(), +- GetV8StringFromProperty(property, info)), +- info); ++ auto pObj = JSGetObject(info.GetIsolate(), info.Holder()); ++ if (!pObj) ++ return; ++ ++ CJS_Runtime* pRuntime = pObj->GetRuntime(); ++ if (!pRuntime) ++ return; ++ ++ ByteString bsProp = ByteStringFromV8Name(info.GetIsolate(), property); ++ CJS_Result result = pObj->GetProperty(pRuntime, bsProp); ++ if (result.HasError()) { ++ pRuntime->Error( ++ JSFormatErrorString("global", "GetProperty", result.Error())); ++ return; ++ } ++ if (result.HasReturn()) ++ info.GetReturnValue().Set(result.Return()); + } + + // static +@@ -168,35 +89,60 @@ void CJS_Global::putprop_static( + v8::Local property, + v8::Local value, + const v8::PropertyCallbackInfo& info) { +- ASSERT(property->IsString()); +- JSSpecialPropPut( +- "global", +- v8::Local::New(info.GetIsolate(), +- GetV8StringFromProperty(property, info)), +- value, info); ++ auto pObj = JSGetObject(info.GetIsolate(), info.Holder()); ++ if (!pObj) ++ return; ++ ++ CJS_Runtime* pRuntime = pObj->GetRuntime(); ++ if (!pRuntime) ++ return; ++ ++ ByteString bsProp = ByteStringFromV8Name(info.GetIsolate(), property); ++ CJS_Result result = pObj->SetProperty(pRuntime, bsProp, value); ++ if (result.HasError()) { ++ pRuntime->Error( ++ JSFormatErrorString("global", "PutProperty", result.Error())); ++ return; ++ } ++ info.GetReturnValue().Set(value); + } + + // static + void CJS_Global::delprop_static( + v8::Local property, + const v8::PropertyCallbackInfo& info) { +- ASSERT(property->IsString()); +- JSSpecialPropDel( +- "global", +- v8::Local::New(info.GetIsolate(), +- GetV8StringFromProperty(property, info)), +- info); ++ auto pObj = JSGetObject(info.GetIsolate(), info.Holder()); ++ if (!pObj) ++ return; ++ ++ ByteString bsProp = ByteStringFromV8Name(info.GetIsolate(), property); ++ if (pObj->DelProperty(bsProp)) ++ info.GetReturnValue().Set(true); ++} ++ ++void CJS_Global::enumprop_static( ++ const v8::PropertyCallbackInfo& info) { ++ auto pObj = JSGetObject(info.GetIsolate(), info.Holder()); ++ if (!pObj) ++ return; ++ ++ CJS_Runtime* pRuntime = pObj->GetRuntime(); ++ if (!pRuntime) ++ return; ++ ++ pObj->EnumProperties(pRuntime, info); + } + + // static + void CJS_Global::DefineAllProperties(CFXJS_Engine* pEngine) { + pEngine->DefineObjAllProperties( + ObjDefnID, CJS_Global::queryprop_static, CJS_Global::getprop_static, +- CJS_Global::putprop_static, CJS_Global::delprop_static); ++ CJS_Global::putprop_static, CJS_Global::delprop_static, ++ CJS_Global::enumprop_static); + } + + // static +-int CJS_Global::GetObjDefnID() { ++uint32_t CJS_Global::GetObjDefnID() { + return ObjDefnID; + } + +@@ -209,38 +155,32 @@ void CJS_Global::DefineJSObjects(CFXJS_Engine* pEngine) { + } + + CJS_Global::CJS_Global(v8::Local pObject, CJS_Runtime* pRuntime) +- : CJS_Object(pObject, pRuntime) { +- CPDFSDK_FormFillEnvironment* pFormFillEnv = GetRuntime()->GetFormFillEnv(); +- m_pFormFillEnv.Reset(pFormFillEnv); +- m_pGlobalData = CFX_GlobalData::GetRetainedInstance(nullptr); ++ : CJS_Object(pObject, pRuntime), ++ m_pGlobalData(CFX_GlobalData::GetRetainedInstance(nullptr)) { + UpdateGlobalPersistentVariables(); + } + + CJS_Global::~CJS_Global() { + DestroyGlobalPersisitentVariables(); +- m_pGlobalData->Release(); ++ m_pGlobalData.ExtractAsDangling()->Release(); + } + +-CJS_Result CJS_Global::QueryProperty(const wchar_t* propname) { +- if (WideString(propname).EqualsASCII("setPersistent")) +- return CJS_Result::Success(); +- +- return CJS_Result::Failure(JSMessage::kUnknownProperty); ++bool CJS_Global::HasProperty(const ByteString& propname) { ++ return pdfium::Contains(m_MapGlobal, propname); + } + +-CJS_Result CJS_Global::DelProperty(CJS_Runtime* pRuntime, +- const wchar_t* propname) { +- auto it = m_MapGlobal.find(WideString(propname).ToDefANSI()); ++bool CJS_Global::DelProperty(const ByteString& propname) { ++ auto it = m_MapGlobal.find(propname); + if (it == m_MapGlobal.end()) +- return CJS_Result::Failure(JSMessage::kUnknownProperty); ++ return false; + + it->second->bDeleted = true; +- return CJS_Result::Success(); ++ return true; + } + + CJS_Result CJS_Global::GetProperty(CJS_Runtime* pRuntime, +- const wchar_t* propname) { +- auto it = m_MapGlobal.find(WideString(propname).ToDefANSI()); ++ const ByteString& propname) { ++ auto it = m_MapGlobal.find(propname); + if (it == m_MapGlobal.end()) + return CJS_Result::Success(); + +@@ -249,17 +189,17 @@ CJS_Result CJS_Global::GetProperty(CJS_Runtime* pRuntime, + return CJS_Result::Success(); + + switch (pData->nType) { +- case CFX_Value::DataType::NUMBER: ++ case CFX_Value::DataType::kNumber: + return CJS_Result::Success(pRuntime->NewNumber(pData->dData)); +- case CFX_Value::DataType::BOOLEAN: ++ case CFX_Value::DataType::kBoolean: + return CJS_Result::Success(pRuntime->NewBoolean(pData->bData)); +- case CFX_Value::DataType::STRING: +- return CJS_Result::Success(pRuntime->NewString( +- WideString::FromDefANSI(pData->sData.AsStringView()).AsStringView())); +- case CFX_Value::DataType::OBJECT: ++ case CFX_Value::DataType::kString: ++ return CJS_Result::Success( ++ pRuntime->NewString(pData->sData.AsStringView())); ++ case CFX_Value::DataType::kObject: + return CJS_Result::Success( + v8::Local::New(pRuntime->GetIsolate(), pData->pData)); +- case CFX_Value::DataType::NULLOBJ: ++ case CFX_Value::DataType::kNull: + return CJS_Result::Success(pRuntime->NewNull()); + default: + break; +@@ -268,46 +208,60 @@ CJS_Result CJS_Global::GetProperty(CJS_Runtime* pRuntime, + } + + CJS_Result CJS_Global::SetProperty(CJS_Runtime* pRuntime, +- const wchar_t* propname, ++ const ByteString& propname, + v8::Local vp) { +- ByteString sPropName = WideString(propname).ToDefANSI(); + if (vp->IsNumber()) { +- return SetGlobalVariables(sPropName, CFX_Value::DataType::NUMBER, ++ return SetGlobalVariables(propname, CFX_Value::DataType::kNumber, + pRuntime->ToDouble(vp), false, ByteString(), + v8::Local(), false); + } + if (vp->IsBoolean()) { +- return SetGlobalVariables(sPropName, CFX_Value::DataType::BOOLEAN, 0, ++ return SetGlobalVariables(propname, CFX_Value::DataType::kBoolean, 0, + pRuntime->ToBoolean(vp), ByteString(), + v8::Local(), false); + } + if (vp->IsString()) { +- return SetGlobalVariables(sPropName, CFX_Value::DataType::STRING, 0, false, +- pRuntime->ToWideString(vp).ToDefANSI(), ++ return SetGlobalVariables(propname, CFX_Value::DataType::kString, 0, false, ++ pRuntime->ToByteString(vp), + v8::Local(), false); + } + if (vp->IsObject()) { +- return SetGlobalVariables(sPropName, CFX_Value::DataType::OBJECT, 0, false, ++ return SetGlobalVariables(propname, CFX_Value::DataType::kObject, 0, false, + ByteString(), pRuntime->ToObject(vp), false); + } + if (vp->IsNull()) { +- return SetGlobalVariables(sPropName, CFX_Value::DataType::NULLOBJ, 0, false, ++ return SetGlobalVariables(propname, CFX_Value::DataType::kNull, 0, false, + ByteString(), v8::Local(), false); + } + if (vp->IsUndefined()) { +- DelProperty(pRuntime, propname); ++ DelProperty(propname); + return CJS_Result::Success(); + } + return CJS_Result::Failure(JSMessage::kObjectTypeError); + } + ++void CJS_Global::EnumProperties( ++ CJS_Runtime* pRuntime, ++ const v8::PropertyCallbackInfo& info) { ++ v8::Local result = pRuntime->NewArray(); ++ int idx = 0; ++ for (const auto& it : m_MapGlobal) { ++ if (it.second->bDeleted) ++ continue; ++ v8::Local name = pRuntime->NewString(it.first.AsStringView()); ++ pRuntime->PutArrayElement(result, idx, name); ++ ++idx; ++ } ++ info.GetReturnValue().Set(result); ++} ++ + CJS_Result CJS_Global::setPersistent( + CJS_Runtime* pRuntime, + const std::vector>& params) { + if (params.size() != 2) + return CJS_Result::Failure(JSMessage::kParamError); + +- auto it = m_MapGlobal.find(pRuntime->ToWideString(params[0]).ToDefANSI()); ++ auto it = m_MapGlobal.find(pRuntime->ToByteString(params[0])); + if (it == m_MapGlobal.end() || it->second->bDeleted) + return CJS_Result::Failure(JSMessage::kGlobalNotFoundError); + +@@ -323,47 +277,44 @@ void CJS_Global::UpdateGlobalPersistentVariables() { + for (int i = 0, sz = m_pGlobalData->GetSize(); i < sz; i++) { + CFX_GlobalData::Element* pData = m_pGlobalData->GetAt(i); + switch (pData->data.nType) { +- case CFX_Value::DataType::NUMBER: +- SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::NUMBER, ++ case CFX_Value::DataType::kNumber: ++ SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kNumber, + pData->data.dData, false, ByteString(), +- v8::Local(), pData->bPersistent == 1); ++ v8::Local(), pData->bPersistent); + pRuntime->PutObjectProperty(ToV8Object(), + pData->data.sKey.AsStringView(), + pRuntime->NewNumber(pData->data.dData)); + break; +- case CFX_Value::DataType::BOOLEAN: +- SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::BOOLEAN, 0, ++ case CFX_Value::DataType::kBoolean: ++ SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kBoolean, 0, + pData->data.bData == 1, ByteString(), +- v8::Local(), pData->bPersistent == 1); ++ v8::Local(), pData->bPersistent); + pRuntime->PutObjectProperty( + ToV8Object(), pData->data.sKey.AsStringView(), + pRuntime->NewBoolean(pData->data.bData == 1)); + break; +- case CFX_Value::DataType::STRING: +- SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::STRING, 0, ++ case CFX_Value::DataType::kString: ++ SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kString, 0, + false, pData->data.sData, v8::Local(), +- pData->bPersistent == 1); ++ pData->bPersistent); + pRuntime->PutObjectProperty( + ToV8Object(), pData->data.sKey.AsStringView(), +- pRuntime->NewString( +- WideString::FromUTF8(pData->data.sData.AsStringView()) +- .AsStringView())); ++ pRuntime->NewString(pData->data.sData.AsStringView())); + break; +- case CFX_Value::DataType::OBJECT: { ++ case CFX_Value::DataType::kObject: { + v8::Local pObj = pRuntime->NewObject(); + if (!pObj.IsEmpty()) { + PutObjectProperty(pObj, &pData->data); +- SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::OBJECT, 0, +- false, ByteString(), pObj, +- pData->bPersistent == 1); ++ SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kObject, 0, ++ false, ByteString(), pObj, pData->bPersistent); + pRuntime->PutObjectProperty(ToV8Object(), + pData->data.sKey.AsStringView(), pObj); + } + } break; +- case CFX_Value::DataType::NULLOBJ: +- SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::NULLOBJ, 0, ++ case CFX_Value::DataType::kNull: ++ SetGlobalVariables(pData->data.sKey, CFX_Value::DataType::kNull, 0, + false, ByteString(), v8::Local(), +- pData->bPersistent == 1); ++ pData->bPersistent); + pRuntime->PutObjectProperty( + ToV8Object(), pData->data.sKey.AsStringView(), pRuntime->NewNull()); + break; +@@ -371,7 +322,11 @@ void CJS_Global::UpdateGlobalPersistentVariables() { + } + } + +-void CJS_Global::CommitGlobalPersisitentVariables(CJS_Runtime* pRuntime) { ++void CJS_Global::CommitGlobalPersisitentVariables() { ++ CJS_Runtime* pRuntime = GetRuntime(); ++ if (!pRuntime) ++ return; ++ + for (const auto& iter : m_MapGlobal) { + ByteString name = iter.first; + JSGlobalData* pData = iter.second.get(); +@@ -380,27 +335,26 @@ void CJS_Global::CommitGlobalPersisitentVariables(CJS_Runtime* pRuntime) { + continue; + } + switch (pData->nType) { +- case CFX_Value::DataType::NUMBER: ++ case CFX_Value::DataType::kNumber: + m_pGlobalData->SetGlobalVariableNumber(name, pData->dData); + m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent); + break; +- case CFX_Value::DataType::BOOLEAN: ++ case CFX_Value::DataType::kBoolean: + m_pGlobalData->SetGlobalVariableBoolean(name, pData->bData); + m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent); + break; +- case CFX_Value::DataType::STRING: ++ case CFX_Value::DataType::kString: + m_pGlobalData->SetGlobalVariableString(name, pData->sData); + m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent); + break; +- case CFX_Value::DataType::OBJECT: { +- std::vector> array; ++ case CFX_Value::DataType::kObject: { + v8::Local obj = +- v8::Local::New(GetIsolate(), pData->pData); +- ObjectToArray(pRuntime, obj, &array); +- m_pGlobalData->SetGlobalVariableObject(name, std::move(array)); ++ v8::Local::New(pRuntime->GetIsolate(), pData->pData); ++ m_pGlobalData->SetGlobalVariableObject(name, ++ ObjectToArray(pRuntime, obj)); + m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent); + } break; +- case CFX_Value::DataType::NULLOBJ: ++ case CFX_Value::DataType::kNull: + m_pGlobalData->SetGlobalVariableNull(name); + m_pGlobalData->SetGlobalVariablePersistent(name, pData->bPersistent); + break; +@@ -408,55 +362,56 @@ void CJS_Global::CommitGlobalPersisitentVariables(CJS_Runtime* pRuntime) { + } + } + +-void CJS_Global::ObjectToArray( ++std::vector> CJS_Global::ObjectToArray( + CJS_Runtime* pRuntime, +- v8::Local pObj, +- std::vector>* pArray) { ++ v8::Local pObj) { ++ std::vector> array; + std::vector pKeyList = pRuntime->GetObjectPropertyNames(pObj); + for (const auto& ws : pKeyList) { + ByteString sKey = ws.ToUTF8(); + v8::Local v = + pRuntime->GetObjectProperty(pObj, sKey.AsStringView()); + if (v->IsNumber()) { +- auto pObjElement = pdfium::MakeUnique(); +- pObjElement->nType = CFX_Value::DataType::NUMBER; ++ auto pObjElement = std::make_unique(); ++ pObjElement->nType = CFX_Value::DataType::kNumber; + pObjElement->sKey = sKey; + pObjElement->dData = pRuntime->ToDouble(v); +- pArray->push_back(std::move(pObjElement)); ++ array.push_back(std::move(pObjElement)); + continue; + } + if (v->IsBoolean()) { +- auto pObjElement = pdfium::MakeUnique(); +- pObjElement->nType = CFX_Value::DataType::BOOLEAN; ++ auto pObjElement = std::make_unique(); ++ pObjElement->nType = CFX_Value::DataType::kBoolean; + pObjElement->sKey = sKey; + pObjElement->dData = pRuntime->ToBoolean(v); +- pArray->push_back(std::move(pObjElement)); ++ array.push_back(std::move(pObjElement)); + continue; + } + if (v->IsString()) { +- ByteString sValue = pRuntime->ToWideString(v).ToDefANSI(); +- auto pObjElement = pdfium::MakeUnique(); +- pObjElement->nType = CFX_Value::DataType::STRING; ++ ByteString sValue = pRuntime->ToByteString(v); ++ auto pObjElement = std::make_unique(); ++ pObjElement->nType = CFX_Value::DataType::kString; + pObjElement->sKey = sKey; + pObjElement->sData = sValue; +- pArray->push_back(std::move(pObjElement)); ++ array.push_back(std::move(pObjElement)); + continue; + } + if (v->IsObject()) { +- auto pObjElement = pdfium::MakeUnique(); +- pObjElement->nType = CFX_Value::DataType::OBJECT; ++ auto pObjElement = std::make_unique(); ++ pObjElement->nType = CFX_Value::DataType::kObject; + pObjElement->sKey = sKey; +- ObjectToArray(pRuntime, pRuntime->ToObject(v), &pObjElement->objData); +- pArray->push_back(std::move(pObjElement)); ++ pObjElement->objData = ObjectToArray(pRuntime, pRuntime->ToObject(v)); ++ array.push_back(std::move(pObjElement)); + continue; + } + if (v->IsNull()) { +- auto pObjElement = pdfium::MakeUnique(); +- pObjElement->nType = CFX_Value::DataType::NULLOBJ; ++ auto pObjElement = std::make_unique(); ++ pObjElement->nType = CFX_Value::DataType::kNull; + pObjElement->sKey = sKey; +- pArray->push_back(std::move(pObjElement)); ++ array.push_back(std::move(pObjElement)); + } + } ++ return array; + } + + void CJS_Global::PutObjectProperty(v8::Local pObj, +@@ -468,22 +423,20 @@ void CJS_Global::PutObjectProperty(v8::Local pObj, + for (size_t i = 0; i < pData->objData.size(); ++i) { + CFX_KeyValue* pObjData = pData->objData.at(i).get(); + switch (pObjData->nType) { +- case CFX_Value::DataType::NUMBER: ++ case CFX_Value::DataType::kNumber: + pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(), + pRuntime->NewNumber(pObjData->dData)); + break; +- case CFX_Value::DataType::BOOLEAN: ++ case CFX_Value::DataType::kBoolean: + pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(), + pRuntime->NewBoolean(pObjData->bData == 1)); + break; +- case CFX_Value::DataType::STRING: ++ case CFX_Value::DataType::kString: + pRuntime->PutObjectProperty( + pObj, pObjData->sKey.AsStringView(), +- pRuntime->NewString( +- WideString::FromUTF8(pObjData->sData.AsStringView()) +- .AsStringView())); ++ pRuntime->NewString(pObjData->sData.AsStringView())); + break; +- case CFX_Value::DataType::OBJECT: { ++ case CFX_Value::DataType::kObject: { + v8::Local pNewObj = pRuntime->NewObject(); + if (!pNewObj.IsEmpty()) { + PutObjectProperty(pNewObj, pObjData); +@@ -491,7 +444,7 @@ void CJS_Global::PutObjectProperty(v8::Local pObj, + pNewObj); + } + } break; +- case CFX_Value::DataType::NULLOBJ: ++ case CFX_Value::DataType::kNull: + pRuntime->PutObjectProperty(pObj, pObjData->sKey.AsStringView(), + pRuntime->NewNull()); + break; +@@ -518,25 +471,25 @@ CJS_Result CJS_Global::SetGlobalVariables(const ByteString& propname, + JSGlobalData* pTemp = it->second.get(); + if (pTemp->bDeleted || pTemp->nType != nType) { + pTemp->dData = 0; +- pTemp->bData = 0; ++ pTemp->bData = false; + pTemp->sData.clear(); + pTemp->nType = nType; + } + pTemp->bDeleted = false; + switch (nType) { +- case CFX_Value::DataType::NUMBER: ++ case CFX_Value::DataType::kNumber: + pTemp->dData = dData; + break; +- case CFX_Value::DataType::BOOLEAN: ++ case CFX_Value::DataType::kBoolean: + pTemp->bData = bData; + break; +- case CFX_Value::DataType::STRING: ++ case CFX_Value::DataType::kString: + pTemp->sData = sData; + break; +- case CFX_Value::DataType::OBJECT: ++ case CFX_Value::DataType::kObject: + pTemp->pData.Reset(pData->GetIsolate(), pData); + break; +- case CFX_Value::DataType::NULLOBJ: ++ case CFX_Value::DataType::kNull: + break; + default: + return CJS_Result::Failure(JSMessage::kObjectTypeError); +@@ -544,30 +497,30 @@ CJS_Result CJS_Global::SetGlobalVariables(const ByteString& propname, + return CJS_Result::Success(); + } + +- auto pNewData = pdfium::MakeUnique(); ++ auto pNewData = std::make_unique(); + switch (nType) { +- case CFX_Value::DataType::NUMBER: +- pNewData->nType = CFX_Value::DataType::NUMBER; ++ case CFX_Value::DataType::kNumber: ++ pNewData->nType = CFX_Value::DataType::kNumber; + pNewData->dData = dData; + pNewData->bPersistent = bDefaultPersistent; + break; +- case CFX_Value::DataType::BOOLEAN: +- pNewData->nType = CFX_Value::DataType::BOOLEAN; ++ case CFX_Value::DataType::kBoolean: ++ pNewData->nType = CFX_Value::DataType::kBoolean; + pNewData->bData = bData; + pNewData->bPersistent = bDefaultPersistent; + break; +- case CFX_Value::DataType::STRING: +- pNewData->nType = CFX_Value::DataType::STRING; ++ case CFX_Value::DataType::kString: ++ pNewData->nType = CFX_Value::DataType::kString; + pNewData->sData = sData; + pNewData->bPersistent = bDefaultPersistent; + break; +- case CFX_Value::DataType::OBJECT: +- pNewData->nType = CFX_Value::DataType::OBJECT; ++ case CFX_Value::DataType::kObject: ++ pNewData->nType = CFX_Value::DataType::kObject; + pNewData->pData.Reset(pData->GetIsolate(), pData); + pNewData->bPersistent = bDefaultPersistent; + break; +- case CFX_Value::DataType::NULLOBJ: +- pNewData->nType = CFX_Value::DataType::NULLOBJ; ++ case CFX_Value::DataType::kNull: ++ pNewData->nType = CFX_Value::DataType::kNull; + pNewData->bPersistent = bDefaultPersistent; + break; + default: +diff --git a/fxjs/cjs_global.h b/fxjs/cjs_global.h +index 2c891885f..944c03084 100644 +--- a/fxjs/cjs_global.h ++++ b/fxjs/cjs_global.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,15 +11,26 @@ + #include + #include + ++#include "core/fxcrt/unowned_ptr.h" + #include "fxjs/cfx_keyvalue.h" + #include "fxjs/cjs_object.h" + #include "fxjs/cjs_result.h" + + class CFX_GlobalData; + ++// The CJS_Global object is not the V8 global object (i.e. it is not |this| ++// in JavaScript outside of a bound function call). It is a facility for ++// sharing data amongst documents and persisting data within a document ++// between sessions. It is only partially implemented due to security and ++// privacy concerns. It provides access via properties in the usual manner, ++// execpt that these are stored on the C++ side rather than in V8 itself. ++// It is a static object that is available as "global" property of the V8 ++// global object and can be manipulated from JavaScript as |global['foo']| ++// for example. ++ + class CJS_Global final : public CJS_Object { + public: +- static int GetObjDefnID(); ++ static uint32_t GetObjDefnID(); + static void DefineJSObjects(CFXJS_Engine* pEngine); + static void DefineAllProperties(CFXJS_Engine* pEngine); + +@@ -33,6 +44,7 @@ class CJS_Global final : public CJS_Object { + const v8::PropertyCallbackInfo& info); + static void delprop_static(v8::Local property, + const v8::PropertyCallbackInfo& info); ++ static void enumprop_static(const v8::PropertyCallbackInfo& info); + + static void setPersistent_static( + const v8::FunctionCallbackInfo& info); +@@ -40,16 +52,6 @@ class CJS_Global final : public CJS_Object { + CJS_Global(v8::Local pObject, CJS_Runtime* pRuntime); + ~CJS_Global() override; + +- CJS_Result DelProperty(CJS_Runtime* pRuntime, const wchar_t* propname); +- +- CJS_Result setPersistent(CJS_Runtime* pRuntime, +- const std::vector>& params); +- CJS_Result QueryProperty(const wchar_t* propname); +- CJS_Result GetProperty(CJS_Runtime* pRuntime, const wchar_t* propname); +- CJS_Result SetProperty(CJS_Runtime* pRuntime, +- const wchar_t* propname, +- v8::Local vp); +- + private: + struct JSGlobalData : public CFX_Value { + public: +@@ -61,11 +63,12 @@ class CJS_Global final : public CJS_Object { + bool bDeleted = false; + }; + +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const JSMethodSpec MethodSpecs[]; + + void UpdateGlobalPersistentVariables(); +- void CommitGlobalPersisitentVariables(CJS_Runtime* pRuntime); ++ // TODO(crbug.com/pdfium/926): This method is never called. ++ void CommitGlobalPersisitentVariables(); + void DestroyGlobalPersisitentVariables(); + CJS_Result SetGlobalVariables(const ByteString& propname, + CFX_Value::DataType nType, +@@ -74,15 +77,23 @@ class CJS_Global final : public CJS_Object { + const ByteString& sData, + v8::Local pData, + bool bDefaultPersistent); +- void ObjectToArray(CJS_Runtime* pRuntime, +- v8::Local pObj, +- std::vector>* pArray); ++ std::vector> ObjectToArray( ++ CJS_Runtime* pRuntime, ++ v8::Local pObj); + void PutObjectProperty(v8::Local obj, CFX_KeyValue* pData); ++ CJS_Result setPersistent(CJS_Runtime* pRuntime, ++ const std::vector>& params); ++ bool HasProperty(const ByteString& propname); ++ bool DelProperty(const ByteString& propname); ++ CJS_Result GetProperty(CJS_Runtime* pRuntime, const ByteString& propname); ++ CJS_Result SetProperty(CJS_Runtime* pRuntime, ++ const ByteString& propname, ++ v8::Local vp); ++ void EnumProperties(CJS_Runtime* pRuntime, ++ const v8::PropertyCallbackInfo& info); + + std::map> m_MapGlobal; +- WideString m_sFilePath; +- CFX_GlobalData* m_pGlobalData; +- ObservedPtr m_pFormFillEnv; ++ UnownedPtr m_pGlobalData; + }; + + #endif // FXJS_CJS_GLOBAL_H_ +diff --git a/fxjs/cjs_globalarrays.cpp b/fxjs/cjs_globalarrays.cpp +index 9cf540e41..3dbf62c91 100644 +--- a/fxjs/cjs_globalarrays.cpp ++++ b/fxjs/cjs_globalarrays.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,21 +6,32 @@ + + #include "fxjs/cjs_globalarrays.h" + +-#define GLOBAL_ARRAY(rt, name, ...) \ +- { \ +- static const wchar_t* const values[] = {__VA_ARGS__}; \ +- v8::Local array = (rt)->NewArray(); \ +- v8::Local ctx = (rt)->GetIsolate()->GetCurrentContext(); \ +- for (size_t i = 0; i < FX_ArraySize(values); ++i) \ +- array->Set(ctx, i, (rt)->NewString(values[i])).FromJust(); \ +- (rt)->SetConstArray((name), array); \ +- (rt)->DefineGlobalConst( \ +- (name), [](const v8::FunctionCallbackInfo& info) { \ +- CJS_Object* pObj = CFXJS_Engine::GetObjectPrivate(info.Holder()); \ +- CJS_Runtime* pCurrentRuntime = pObj->GetRuntime(); \ +- if (pCurrentRuntime) \ +- info.GetReturnValue().Set(pCurrentRuntime->GetConstArray(name)); \ +- }); \ ++#include ++ ++#include "third_party/base/numerics/safe_conversions.h" ++#include "v8/include/v8-container.h" ++#include "v8/include/v8-isolate.h" ++ ++#define GLOBAL_ARRAY(rt, name, ...) \ ++ { \ ++ static const wchar_t* const values[] = {__VA_ARGS__}; \ ++ v8::Local array = (rt)->NewArray(); \ ++ v8::Local ctx = (rt)->GetIsolate()->GetCurrentContext(); \ ++ for (size_t i = 0; i < std::size(values); ++i) { \ ++ array \ ++ ->Set(ctx, pdfium::base::checked_cast(i), \ ++ (rt)->NewString(values[i])) \ ++ .FromJust(); \ ++ } \ ++ (rt)->SetConstArray((name), array); \ ++ (rt)->DefineGlobalConst( \ ++ (name), [](const v8::FunctionCallbackInfo& info) { \ ++ CJS_Object* pObj = CFXJS_Engine::GetObjectPrivate(info.GetIsolate(), \ ++ info.Holder()); \ ++ CJS_Runtime* pCurrentRuntime = pObj->GetRuntime(); \ ++ if (pCurrentRuntime) \ ++ info.GetReturnValue().Set(pCurrentRuntime->GetConstArray(name)); \ ++ }); \ + } + + // static +diff --git a/fxjs/cjs_globalarrays.h b/fxjs/cjs_globalarrays.h +index 244db2ede..726e3a7e4 100644 +--- a/fxjs/cjs_globalarrays.h ++++ b/fxjs/cjs_globalarrays.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxjs/cjs_globalconsts.cpp b/fxjs/cjs_globalconsts.cpp +index cb6bd33c4..854ded817 100644 +--- a/fxjs/cjs_globalconsts.cpp ++++ b/fxjs/cjs_globalconsts.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,14 +6,13 @@ + + #include "fxjs/cjs_globalconsts.h" + +-#define GLOBAL_STRING(rt, name, value) \ +- (rt)->DefineGlobalConst( \ +- (name), [](const v8::FunctionCallbackInfo& info) { \ +- const char* pStr = (value); \ +- info.GetReturnValue().Set( \ +- v8::String::NewFromUtf8(info.GetIsolate(), pStr, \ +- v8::NewStringType::kNormal, strlen(pStr)) \ +- .ToLocalChecked()); \ ++#include "fxjs/fxv8.h" ++ ++#define GLOBAL_STRING(rt, name, value) \ ++ (rt)->DefineGlobalConst( \ ++ (name), [](const v8::FunctionCallbackInfo& info) { \ ++ info.GetReturnValue().Set( \ ++ fxv8::NewStringHelper(info.GetIsolate(), (value))); \ + }) + + // static +diff --git a/fxjs/cjs_globalconsts.h b/fxjs/cjs_globalconsts.h +index 71c689b90..f553f3ea2 100644 +--- a/fxjs/cjs_globalconsts.h ++++ b/fxjs/cjs_globalconsts.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxjs/cjs_highlight.cpp b/fxjs/cjs_highlight.cpp +index 4ada397cc..174685d9c 100644 +--- a/fxjs/cjs_highlight.cpp ++++ b/fxjs/cjs_highlight.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,7 +12,7 @@ const JSConstSpec CJS_Highlight::ConstSpecs[] = { + {"p", JSConstSpec::String, 0, "push"}, + {"o", JSConstSpec::String, 0, "outline"}}; + +-int CJS_Highlight::ObjDefnID = -1; ++uint32_t CJS_Highlight::ObjDefnID = 0; + + // static + void CJS_Highlight::DefineJSObjects(CFXJS_Engine* pEngine) { +diff --git a/fxjs/cjs_highlight.h b/fxjs/cjs_highlight.h +index efec12714..ad6881189 100644 +--- a/fxjs/cjs_highlight.h ++++ b/fxjs/cjs_highlight.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,7 +16,7 @@ class CJS_Highlight final : public CJS_Object { + CJS_Highlight() = delete; + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const JSConstSpec ConstSpecs[]; + }; + +diff --git a/fxjs/cjs_icon.cpp b/fxjs/cjs_icon.cpp +index 80be5c8f1..d3b9da44b 100644 +--- a/fxjs/cjs_icon.cpp ++++ b/fxjs/cjs_icon.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,11 +9,11 @@ + const JSPropertySpec CJS_Icon::PropertySpecs[] = { + {"name", get_name_static, set_name_static}}; + +-int CJS_Icon::ObjDefnID = -1; ++uint32_t CJS_Icon::ObjDefnID = 0; + const char CJS_Icon::kName[] = "Icon"; + + // static +-int CJS_Icon::GetObjDefnID() { ++uint32_t CJS_Icon::GetObjDefnID() { + return ObjDefnID; + } + +diff --git a/fxjs/cjs_icon.h b/fxjs/cjs_icon.h +index 321f5081e..54dc1cb5f 100644 +--- a/fxjs/cjs_icon.h ++++ b/fxjs/cjs_icon.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,7 +12,7 @@ + + class CJS_Icon final : public CJS_Object { + public: +- static int GetObjDefnID(); ++ static uint32_t GetObjDefnID(); + static void DefineJSObjects(CFXJS_Engine* pEngine); + + CJS_Icon(v8::Local pObject, CJS_Runtime* pRuntime); +@@ -24,7 +24,7 @@ class CJS_Icon final : public CJS_Object { + JS_STATIC_PROP(name, name, CJS_Icon) + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const char kName[]; + static const JSPropertySpec PropertySpecs[]; + +diff --git a/fxjs/cjs_object.cpp b/fxjs/cjs_object.cpp +index e13a5095d..f27d37dca 100644 +--- a/fxjs/cjs_object.cpp ++++ b/fxjs/cjs_object.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,11 +10,11 @@ + + // static + void CJS_Object::DefineConsts(CFXJS_Engine* pEngine, +- int objId, ++ uint32_t nObjDefnID, + pdfium::span consts) { + for (const auto& item : consts) { + pEngine->DefineObjConst( +- objId, item.pName, ++ nObjDefnID, item.pName, + item.eType == JSConstSpec::Number + ? pEngine->NewNumber(item.number).As() + : pEngine->NewString(item.pStr).As()); +@@ -23,23 +23,22 @@ void CJS_Object::DefineConsts(CFXJS_Engine* pEngine, + + // static + void CJS_Object::DefineProps(CFXJS_Engine* pEngine, +- int objId, ++ uint32_t nObjDefnID, + pdfium::span props) { + for (const auto& item : props) +- pEngine->DefineObjProperty(objId, item.pName, item.pPropGet, item.pPropPut); ++ pEngine->DefineObjProperty(nObjDefnID, item.pName, item.pPropGet, ++ item.pPropPut); + } + + // static + void CJS_Object::DefineMethods(CFXJS_Engine* pEngine, +- int objId, ++ uint32_t nObjDefnID, + pdfium::span methods) { + for (const auto& item : methods) +- pEngine->DefineObjMethod(objId, item.pName, item.pMethodCall); ++ pEngine->DefineObjMethod(nObjDefnID, item.pName, item.pMethodCall); + } + + CJS_Object::CJS_Object(v8::Local pObject, CJS_Runtime* pRuntime) +- : m_pIsolate(pObject->GetIsolate()), +- m_pV8Object(GetIsolate(), pObject), +- m_pRuntime(pRuntime) {} ++ : m_pV8Object(pObject->GetIsolate(), pObject), m_pRuntime(pRuntime) {} + +-CJS_Object::~CJS_Object() {} ++CJS_Object::~CJS_Object() = default; +diff --git a/fxjs/cjs_object.h b/fxjs/cjs_object.h +index 5da72ff83..c53568eee 100644 +--- a/fxjs/cjs_object.h ++++ b/fxjs/cjs_object.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -36,24 +36,24 @@ struct JSMethodSpec { + class CJS_Object { + public: + static void DefineConsts(CFXJS_Engine* pEngine, +- int objId, ++ uint32_t nObjDefnID, + pdfium::span consts); + static void DefineProps(CFXJS_Engine* pEngine, +- int objId, ++ uint32_t nObjDefnID, + pdfium::span consts); + static void DefineMethods(CFXJS_Engine* pEngine, +- int objId, ++ uint32_t nObjDefnID, + pdfium::span consts); + + CJS_Object(v8::Local pObject, CJS_Runtime* pRuntime); + virtual ~CJS_Object(); + +- v8::Local ToV8Object() { return m_pV8Object.Get(GetIsolate()); } +- v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); } ++ v8::Local ToV8Object() { ++ return m_pV8Object.Get(GetRuntime()->GetIsolate()); ++ } + CJS_Runtime* GetRuntime() const { return m_pRuntime.Get(); } + + private: +- UnownedPtr m_pIsolate; + v8::Global m_pV8Object; + ObservedPtr m_pRuntime; + }; +diff --git a/fxjs/cjs_position.cpp b/fxjs/cjs_position.cpp +index dc9594ef5..ccf4fe6d7 100644 +--- a/fxjs/cjs_position.cpp ++++ b/fxjs/cjs_position.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -15,7 +15,7 @@ const JSConstSpec CJS_Position::ConstSpecs[] = { + {"textIconH", JSConstSpec::Number, 5, 0}, + {"overlay", JSConstSpec::Number, 6, 0}}; + +-int CJS_Position::ObjDefnID = -1; ++uint32_t CJS_Position::ObjDefnID = 0; + + // static + void CJS_Position::DefineJSObjects(CFXJS_Engine* pEngine) { +diff --git a/fxjs/cjs_position.h b/fxjs/cjs_position.h +index ae951bf69..14097a971 100644 +--- a/fxjs/cjs_position.h ++++ b/fxjs/cjs_position.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,7 +16,7 @@ class CJS_Position final : public CJS_Object { + CJS_Position() = delete; + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const JSConstSpec ConstSpecs[]; + }; + +diff --git a/fxjs/cjs_publicmethods.cpp b/fxjs/cjs_publicmethods.cpp +index 8eed97d44..07fe8c598 100644 +--- a/fxjs/cjs_publicmethods.cpp ++++ b/fxjs/cjs_publicmethods.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,9 +6,9 @@ + + #include "fxjs/cjs_publicmethods.h" + ++#include ++ + #include +-#include +-#include + #include + #include + #include +@@ -18,15 +18,16 @@ + #include + + #include "build/build_config.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" + #include "core/fpdfdoc/cpdf_formcontrol.h" + #include "core/fpdfdoc/cpdf_interactiveform.h" + #include "core/fxcrt/fx_extension.h" ++#include "core/fxcrt/fx_string_wrappers.h" + #include "core/fxge/cfx_color.h" + #include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fpdfsdk/cpdfsdk_interactiveform.h" + #include "fxjs/cjs_color.h" + #include "fxjs/cjs_event_context.h" +-#include "fxjs/cjs_eventrecorder.h" + #include "fxjs/cjs_field.h" + #include "fxjs/cjs_object.h" + #include "fxjs/cjs_runtime.h" +@@ -34,7 +35,10 @@ + #include "fxjs/fx_date_helpers.h" + #include "fxjs/js_define.h" + #include "fxjs/js_resources.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/check.h" ++#include "third_party/base/numerics/safe_conversions.h" ++#include "v8/include/v8-container.h" + + // static + const JSMethodSpec CJS_PublicMethods::GlobalFunctionSpecs[] = { +@@ -64,7 +68,7 @@ const JSMethodSpec CJS_PublicMethods::GlobalFunctionSpecs[] = { + + namespace { + +-#if !defined(OS_ANDROID) ++#if !BUILDFLAG(IS_ANDROID) + constexpr double kDoubleCorrect = 0.000000000000001; + #endif + +@@ -93,14 +97,17 @@ T StrTrim(const T& str) { + return result; + } + +-void AlertIfPossible(CJS_EventContext* pContext, const WideString& swMsg) { ++void AlertIfPossible(CJS_EventContext* pContext, ++ const WideString& wsCaller, ++ const WideString& wsMsg) { + CPDFSDK_FormFillEnvironment* pFormFillEnv = pContext->GetFormFillEnv(); +- if (pFormFillEnv) +- pFormFillEnv->JS_appAlert(swMsg, WideString(), JSPLATFORM_ALERT_BUTTON_OK, ++ if (pFormFillEnv) { ++ pFormFillEnv->JS_appAlert(wsMsg, wsCaller, JSPLATFORM_ALERT_BUTTON_OK, + JSPLATFORM_ALERT_ICON_STATUS); ++ } + } + +-#if !defined(OS_ANDROID) ++#if !BUILDFLAG(IS_ANDROID) + ByteString CalculateString(double dValue, + int iDec, + int* iDec2, +@@ -112,16 +119,17 @@ ByteString CalculateString(double dValue, + // Make sure the number of precision characters will fit. + iDec = std::min(iDec, std::numeric_limits::digits10); + +- std::stringstream ss; ++ fxcrt::ostringstream ss; + ss << std::fixed << std::setprecision(iDec) << dValue; +- std::string value = ss.str(); ++ fxcrt::string value = ss.str(); + size_t pos = value.find('.'); +- *iDec2 = pos == std::string::npos ? value.size() : static_cast(pos); ++ *iDec2 = pdfium::base::checked_cast( ++ pos == fxcrt::string::npos ? value.size() : pos); + return ByteString(value.c_str()); + } + #endif + +-WideString CalcMergedString(const CJS_EventRecorder* event, ++WideString CalcMergedString(const CJS_EventContext* event, + const WideString& value, + const WideString& change) { + WideString prefix = value.First(event->SelStart()); +@@ -136,7 +144,8 @@ template >&)> + void JSGlobalFunc(const char* func_name_string, + const v8::FunctionCallbackInfo& info) { +- CJS_Object* pObj = CFXJS_Engine::GetObjectPrivate(info.Holder()); ++ CJS_Object* pObj = ++ CFXJS_Engine::GetObjectPrivate(info.GetIsolate(), info.Holder()); + if (!pObj) + return; + +@@ -171,13 +180,13 @@ bool IsDigitSeparatorOrDecimalMark(int c) { + return c == '.' || c == ','; + } + +-#if !defined(OS_ANDROID) ++#if !BUILDFLAG(IS_ANDROID) + bool IsStyleWithDigitSeparator(int style) { + return style == 0 || style == 2; + } + + char DigitSeparatorForStyle(int style) { +- ASSERT(IsStyleWithDigitSeparator(style)); ++ DCHECK(IsStyleWithDigitSeparator(style)); + return style == 0 ? ',' : '.'; + } + +@@ -194,7 +203,7 @@ char DecimalMarkForStyle(int style) { + return IsStyleWithCommaDecimalMark(style) ? ',' : '.'; + } + +-#if !defined(OS_ANDROID) ++#if !BUILDFLAG(IS_ANDROID) + void NormalizeDecimalMark(ByteString* str) { + str->Replace(",", "."); + } +@@ -204,20 +213,20 @@ void NormalizeDecimalMarkW(WideString* str) { + str->Replace(L",", L"."); + } + +-Optional ApplyNamedOperation(const wchar_t* sFunction, +- double dValue1, +- double dValue2) { +- if (FXSYS_wcsicmp(sFunction, L"AVG") == 0 || +- FXSYS_wcsicmp(sFunction, L"SUM") == 0) { ++absl::optional ApplyNamedOperation(const WideString& wsFunction, ++ double dValue1, ++ double dValue2) { ++ if (wsFunction.EqualsASCIINoCase("AVG") || ++ wsFunction.EqualsASCIINoCase("SUM")) { + return dValue1 + dValue2; + } +- if (FXSYS_wcsicmp(sFunction, L"PRD") == 0) ++ if (wsFunction.EqualsASCIINoCase("PRD")) + return dValue1 * dValue2; +- if (FXSYS_wcsicmp(sFunction, L"MIN") == 0) ++ if (wsFunction.EqualsASCIINoCase("MIN")) + return std::min(dValue1, dValue2); +- if (FXSYS_wcsicmp(sFunction, L"MAX") == 0) ++ if (wsFunction.EqualsASCIINoCase("MAX")) + return std::max(dValue1, dValue2); +- return {}; ++ return absl::nullopt; + } + + } // namespace +@@ -313,14 +322,13 @@ bool CJS_PublicMethods::IsReservedMaskChar(wchar_t ch) { + v8::Local CJS_PublicMethods::AF_MakeArrayFromList( + CJS_Runtime* pRuntime, + v8::Local val) { +- ASSERT(!val.IsEmpty()); ++ DCHECK(!val.IsEmpty()); + if (val->IsArray()) + return pRuntime->ToArray(val); + +- ASSERT(val->IsString()); +- WideString wsStr = pRuntime->ToWideString(val); +- ByteString t = wsStr.ToDefANSI(); +- const char* p = t.c_str(); ++ DCHECK(val->IsString()); ++ ByteString bsVal = pRuntime->ToByteString(val); ++ const char* p = bsVal.c_str(); + + int nIndex = 0; + v8::Local StrArray = pRuntime->NewArray(); +@@ -343,7 +351,8 @@ v8::Local CJS_PublicMethods::AF_MakeArrayFromList( + return StrArray; + } + +-double CJS_PublicMethods::ParseDate(const WideString& value, ++double CJS_PublicMethods::ParseDate(v8::Isolate* isolate, ++ const WideString& value, + bool* bWrongFormat) { + double dt = FX_GetDateTime(); + int nYear = FX_GetYearFromTime(dt); +@@ -417,26 +426,28 @@ double CJS_PublicMethods::ParseDate(const WideString& value, + } + + // TODO(thestig): Should we set |bWrongFormat| to false here too? +- return JS_DateParse(WideString::Format(L"%d/%d/%d %d:%d:%d", nMonth, nDay, +- nYear, nHour, nMin, nSec)); ++ return JS_DateParse( ++ isolate, WideString::Format(L"%d/%d/%d %d:%d:%d", nMonth, nDay, nYear, ++ nHour, nMin, nSec)); + } + +-double CJS_PublicMethods::ParseDateUsingFormat(const WideString& value, ++double CJS_PublicMethods::ParseDateUsingFormat(v8::Isolate* isolate, ++ const WideString& value, + const WideString& format, + bool* bWrongFormat) { +- double dRet = std::nan(""); ++ double dRet = nan(""); + fxjs::ConversionStatus status = FX_ParseDateUsingFormat(value, format, &dRet); + if (status == fxjs::ConversionStatus::kSuccess) + return dRet; + + if (status == fxjs::ConversionStatus::kBadDate) { +- dRet = JS_DateParse(value); +- if (!std::isnan(dRet)) ++ dRet = JS_DateParse(isolate, value); ++ if (!isnan(dRet)) + return dRet; + } + + bool bBadFormat = false; +- dRet = ParseDate(value, &bBadFormat); ++ dRet = ParseDate(isolate, value, &bBadFormat); + if (bWrongFormat) + *bWrongFormat = bBadFormat; + +@@ -475,23 +486,23 @@ WideString CJS_PublicMethods::PrintDateUsingFormat(double dDate, + sPart += c; + break; + case 'm': +- sPart = WideString::Format(L"%d", nMonth); ++ sPart = WideString::FormatInteger(nMonth); + break; + case 'd': +- sPart = WideString::Format(L"%d", nDay); ++ sPart = WideString::FormatInteger(nDay); + break; + case 'H': +- sPart = WideString::Format(L"%d", nHour); ++ sPart = WideString::FormatInteger(nHour); + break; + case 'h': + sPart = +- WideString::Format(L"%d", nHour > 12 ? nHour - 12 : nHour); ++ WideString::FormatInteger(nHour > 12 ? nHour - 12 : nHour); + break; + case 'M': +- sPart = WideString::Format(L"%d", nMin); ++ sPart = WideString::FormatInteger(nMin); + break; + case 's': +- sPart = WideString::Format(L"%d", nSec); ++ sPart = WideString::FormatInteger(nSec); + break; + case 't': + sPart += nHour > 12 ? 'p' : 'a'; +@@ -582,17 +593,16 @@ WideString CJS_PublicMethods::PrintDateUsingFormat(double dDate, + CJS_Result CJS_PublicMethods::AFNumber_Format( + CJS_Runtime* pRuntime, + const std::vector>& params) { +-#if !defined(OS_ANDROID) ++#if !BUILDFLAG(IS_ANDROID) + if (params.size() != 6) + return CJS_Result::Failure(JSMessage::kParamError); + + CJS_EventContext* pEventContext = pRuntime->GetCurrentEventContext(); +- CJS_EventRecorder* pEvent = pEventContext->GetEventRecorder(); +- if (!pEvent->HasValue()) ++ if (!pEventContext->HasValue()) + return CJS_Result::Failure(WideString::FromASCII("No event handler")); + +- WideString& Value = pEvent->Value(); +- ByteString strValue = StrTrim(Value.ToDefANSI()); ++ WideString& Value = pEventContext->Value(); ++ ByteString strValue = StrTrim(Value.ToUTF8()); + if (strValue.IsEmpty()) + return CJS_Result::Success(); + +@@ -621,7 +631,7 @@ CJS_Result CJS_PublicMethods::AFNumber_Format( + iDec2 = 1; + } + } +- ASSERT(iDec2 >= 0); ++ DCHECK(iDec2 >= 0); + + // Processing separator style + if (static_cast(iDec2) < strValue.GetLength()) { +@@ -638,7 +648,7 @@ CJS_Result CJS_PublicMethods::AFNumber_Format( + } + + // Processing currency string +- Value = WideString::FromDefANSI(strValue.AsStringView()); ++ Value = WideString::FromUTF8(strValue.AsStringView()); + if (bCurrencyPrepend) + Value = wstrCurrency + Value; + else +@@ -694,24 +704,23 @@ CJS_Result CJS_PublicMethods::AFNumber_Keystroke( + return CJS_Result::Failure(JSMessage::kParamError); + + CJS_EventContext* pContext = pRuntime->GetCurrentEventContext(); +- CJS_EventRecorder* pEvent = pContext->GetEventRecorder(); +- if (!pEvent->HasValue()) ++ if (!pContext->HasValue()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +- WideString& val = pEvent->Value(); +- WideString& wstrChange = pEvent->Change(); ++ WideString& val = pContext->Value(); ++ WideString& wstrChange = pContext->Change(); + WideString wstrValue = val; + +- if (pEvent->WillCommit()) { ++ if (pContext->WillCommit()) { + WideString swTemp = StrTrim(wstrValue); + if (swTemp.IsEmpty()) + return CJS_Result::Success(); + + NormalizeDecimalMarkW(&swTemp); + if (!IsNumber(swTemp)) { +- pEvent->Rc() = false; ++ pContext->Rc() = false; + WideString sError = JSGetStringFromID(JSMessage::kInvalidInputError); +- AlertIfPossible(pContext, sError); ++ AlertIfPossible(pContext, L"AFNumber_Keystroke", sError); + return CJS_Result::Failure(sError); + } + // It happens after the last keystroke and before validating, +@@ -719,16 +728,16 @@ CJS_Result CJS_PublicMethods::AFNumber_Keystroke( + } + + WideString wstrSelected; +- if (pEvent->SelStart() != -1) { +- wstrSelected = wstrValue.Substr(pEvent->SelStart(), +- pEvent->SelEnd() - pEvent->SelStart()); ++ if (pContext->SelStart() != -1) { ++ wstrSelected = wstrValue.Substr(pContext->SelStart(), ++ pContext->SelEnd() - pContext->SelStart()); + } + + bool bHasSign = wstrValue.Contains(L'-') && !wstrSelected.Contains(L'-'); + if (bHasSign) { + // can't insert "change" in front of sign position. +- if (!wstrSelected.IsEmpty() && pEvent->SelStart() == 0) { +- pEvent->Rc() = false; ++ if (!wstrSelected.IsEmpty() && pContext->SelStart() == 0) { ++ pContext->Rc() = false; + return CJS_Result::Success(); + } + } +@@ -740,7 +749,7 @@ CJS_Result CJS_PublicMethods::AFNumber_Keystroke( + for (size_t i = 0; i < wstrChange.GetLength(); ++i) { + if (wstrChange[i] == cSep) { + if (bHasSep) { +- pEvent->Rc() = false; ++ pContext->Rc() = false; + return CJS_Result::Success(); + } + bHasSep = true; +@@ -748,16 +757,16 @@ CJS_Result CJS_PublicMethods::AFNumber_Keystroke( + } + if (wstrChange[i] == L'-') { + if (bHasSign) { +- pEvent->Rc() = false; ++ pContext->Rc() = false; + return CJS_Result::Success(); + } + // sign's position is not correct + if (i != 0) { +- pEvent->Rc() = false; ++ pContext->Rc() = false; + return CJS_Result::Success(); + } +- if (pEvent->SelStart() != 0) { +- pEvent->Rc() = false; ++ if (pContext->SelStart() != 0) { ++ pContext->Rc() = false; + return CJS_Result::Success(); + } + bHasSign = true; +@@ -765,12 +774,12 @@ CJS_Result CJS_PublicMethods::AFNumber_Keystroke( + } + + if (!FXSYS_IsDecimalDigit(wstrChange[i])) { +- pEvent->Rc() = false; ++ pContext->Rc() = false; + return CJS_Result::Success(); + } + } + +- val = CalcMergedString(pEvent, wstrValue, wstrChange); ++ val = CalcMergedString(pContext, wstrValue, wstrChange); + return CJS_Result::Success(); + } + +@@ -778,12 +787,11 @@ CJS_Result CJS_PublicMethods::AFNumber_Keystroke( + CJS_Result CJS_PublicMethods::AFPercent_Format( + CJS_Runtime* pRuntime, + const std::vector>& params) { +-#if !defined(OS_ANDROID) ++#if !BUILDFLAG(IS_ANDROID) + if (params.size() < 2) + return CJS_Result::Failure(JSMessage::kParamError); + +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + if (!pEvent->HasValue()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +@@ -799,8 +807,7 @@ CJS_Result CJS_PublicMethods::AFPercent_Format( + + // When the |iDec| value is too big, Acrobat will just return "%". + static constexpr int kDecLimit = 512; +- // TODO(thestig): Calculate this once C++14 can be used to declare variables +- // in constexpr functions. ++ // This count must be in sync with |kDecLimit|. + static constexpr size_t kDigitsInDecLimit = 3; + WideString& Value = pEvent->Value(); + if (iDec > kDecLimit) { +@@ -808,7 +815,7 @@ CJS_Result CJS_PublicMethods::AFPercent_Format( + return CJS_Result::Success(); + } + +- ByteString strValue = StrTrim(Value.ToDefANSI()); ++ ByteString strValue = StrTrim(Value.ToUTF8()); + if (strValue.IsEmpty()) + strValue = "0"; + +@@ -839,7 +846,7 @@ CJS_Result CJS_PublicMethods::AFPercent_Format( + strValue.ReleaseBuffer(szNewSize); + + // for processing separator style +- Optional mark_pos = strValue.Find('.'); ++ absl::optional mark_pos = strValue.Find('.'); + if (mark_pos.has_value()) { + char mark = DecimalMarkForStyle(iSepStyle); + if (mark != '.') +@@ -859,7 +866,7 @@ CJS_Result CJS_PublicMethods::AFPercent_Format( + strValue.InsertAtFront('%'); + else + strValue.InsertAtBack('%'); +- Value = WideString::FromDefANSI(strValue.AsStringView()); ++ Value = WideString::FromUTF8(strValue.AsStringView()); + #endif + return CJS_Result::Success(); + } +@@ -878,8 +885,7 @@ CJS_Result CJS_PublicMethods::AFDate_FormatEx( + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + +- CJS_EventContext* pContext = pRuntime->GetCurrentEventContext(); +- CJS_EventRecorder* pEvent = pContext->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + if (!pEvent->HasValue()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +@@ -892,15 +898,16 @@ CJS_Result CJS_PublicMethods::AFDate_FormatEx( + double dDate; + if (strValue.Contains(L"GMT")) { + // e.g. "Tue Aug 11 14:24:16 GMT+08002009" +- dDate = ParseDateAsGMT(strValue); ++ dDate = ParseDateAsGMT(pRuntime->GetIsolate(), strValue); + } else { +- dDate = ParseDateUsingFormat(strValue, sFormat, nullptr); ++ dDate = ParseDateUsingFormat(pRuntime->GetIsolate(), strValue, sFormat, ++ nullptr); + } + +- if (std::isnan(dDate)) { ++ if (isnan(dDate)) { + WideString swMsg = WideString::Format( + JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str()); +- AlertIfPossible(pContext, swMsg); ++ AlertIfPossible(pEvent, L"AFDate_FormatEx", swMsg); + return CJS_Result::Failure(JSMessage::kParseDateError); + } + +@@ -908,7 +915,8 @@ CJS_Result CJS_PublicMethods::AFDate_FormatEx( + return CJS_Result::Success(); + } + +-double CJS_PublicMethods::ParseDateAsGMT(const WideString& strValue) { ++double CJS_PublicMethods::ParseDateAsGMT(v8::Isolate* isolate, ++ const WideString& strValue) { + std::vector wsArray; + WideString sTemp; + for (const auto& c : strValue) { +@@ -924,9 +932,9 @@ double CJS_PublicMethods::ParseDateAsGMT(const WideString& strValue) { + + int nMonth = 1; + sTemp = wsArray[1]; +- for (size_t i = 0; i < FX_ArraySize(fxjs::kMonths); ++i) { +- if (sTemp.Compare(fxjs::kMonths[i]) == 0) { +- nMonth = i + 1; ++ for (size_t i = 0; i < std::size(fxjs::kMonths); ++i) { ++ if (sTemp == fxjs::kMonths[i]) { ++ nMonth = static_cast(i) + 1; + break; + } + } +@@ -938,8 +946,8 @@ double CJS_PublicMethods::ParseDateAsGMT(const WideString& strValue) { + int nYear = StringToFloat(wsArray[7].AsStringView()); + double dRet = FX_MakeDate(FX_MakeDay(nYear, nMonth - 1, nDay), + FX_MakeTime(nHour, nMin, nSec, 0)); +- if (std::isnan(dRet)) +- dRet = JS_DateParse(strValue); ++ if (isnan(dRet)) ++ dRet = JS_DateParse(isolate, strValue); + + return dRet; + } +@@ -953,8 +961,7 @@ CJS_Result CJS_PublicMethods::AFDate_KeystrokeEx( + "AFDate_KeystrokeEx's parameter size not correct")); + } + +- CJS_EventContext* pContext = pRuntime->GetCurrentEventContext(); +- CJS_EventRecorder* pEvent = pContext->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + if (!pEvent->WillCommit()) + return CJS_Result::Success(); + +@@ -967,11 +974,12 @@ CJS_Result CJS_PublicMethods::AFDate_KeystrokeEx( + + bool bWrongFormat = false; + WideString sFormat = pRuntime->ToWideString(params[0]); +- double dRet = ParseDateUsingFormat(strValue, sFormat, &bWrongFormat); +- if (bWrongFormat || std::isnan(dRet)) { ++ double dRet = ParseDateUsingFormat(pRuntime->GetIsolate(), strValue, sFormat, ++ &bWrongFormat); ++ if (bWrongFormat || isnan(dRet)) { + WideString swMsg = WideString::Format( + JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str()); +- AlertIfPossible(pContext, swMsg); ++ AlertIfPossible(pEvent, L"AFDate_KeystrokeEx", swMsg); + pEvent->Rc() = false; + } + return CJS_Result::Success(); +@@ -983,8 +991,8 @@ CJS_Result CJS_PublicMethods::AFDate_Format( + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + +- int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]), +- FX_ArraySize(kDateFormats)); ++ int iIndex = ++ WithinBoundsOrZero(pRuntime->ToInt32(params[0]), std::size(kDateFormats)); + std::vector> newParams; + newParams.push_back(pRuntime->NewString(kDateFormats[iIndex])); + return AFDate_FormatEx(pRuntime, newParams); +@@ -997,8 +1005,8 @@ CJS_Result CJS_PublicMethods::AFDate_Keystroke( + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + +- int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]), +- FX_ArraySize(kDateFormats)); ++ int iIndex = ++ WithinBoundsOrZero(pRuntime->ToInt32(params[0]), std::size(kDateFormats)); + std::vector> newParams; + newParams.push_back(pRuntime->NewString(kDateFormats[iIndex])); + return AFDate_KeystrokeEx(pRuntime, newParams); +@@ -1011,8 +1019,8 @@ CJS_Result CJS_PublicMethods::AFTime_Format( + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + +- int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]), +- FX_ArraySize(kTimeFormats)); ++ int iIndex = ++ WithinBoundsOrZero(pRuntime->ToInt32(params[0]), std::size(kTimeFormats)); + std::vector> newParams; + newParams.push_back(pRuntime->NewString(kTimeFormats[iIndex])); + return AFDate_FormatEx(pRuntime, newParams); +@@ -1024,8 +1032,8 @@ CJS_Result CJS_PublicMethods::AFTime_Keystroke( + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + +- int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]), +- FX_ArraySize(kTimeFormats)); ++ int iIndex = ++ WithinBoundsOrZero(pRuntime->ToInt32(params[0]), std::size(kTimeFormats)); + std::vector> newParams; + newParams.push_back(pRuntime->NewString(kTimeFormats[iIndex])); + return AFDate_KeystrokeEx(pRuntime, newParams); +@@ -1050,8 +1058,7 @@ CJS_Result CJS_PublicMethods::AFSpecial_Format( + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + if (!pEvent->HasValue()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +@@ -1086,8 +1093,7 @@ CJS_Result CJS_PublicMethods::AFSpecial_KeystrokeEx( + if (params.size() < 1) + return CJS_Result::Failure(JSMessage::kParamError); + +- CJS_EventContext* pContext = pRuntime->GetCurrentEventContext(); +- CJS_EventRecorder* pEvent = pContext->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + if (!pEvent->HasValue()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +@@ -1101,7 +1107,7 @@ CJS_Result CJS_PublicMethods::AFSpecial_KeystrokeEx( + return CJS_Result::Success(); + + if (valEvent.GetLength() > wstrMask.GetLength()) { +- AlertIfPossible(pContext, ++ AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx", + JSGetStringFromID(JSMessage::kParamTooLongError)); + pEvent->Rc() = false; + return CJS_Result::Success(); +@@ -1113,7 +1119,7 @@ CJS_Result CJS_PublicMethods::AFSpecial_KeystrokeEx( + break; + } + if (iIndex != wstrMask.GetLength()) { +- AlertIfPossible(pContext, ++ AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx", + JSGetStringFromID(JSMessage::kInvalidInputError)); + pEvent->Rc() = false; + } +@@ -1129,20 +1135,22 @@ CJS_Result CJS_PublicMethods::AFSpecial_KeystrokeEx( + size_t combined_len = valEvent.GetLength() + wChange.GetLength() + + pEvent->SelStart() - pEvent->SelEnd(); + if (combined_len > wstrMask.GetLength()) { +- AlertIfPossible(pContext, JSGetStringFromID(JSMessage::kParamTooLongError)); ++ AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx", ++ JSGetStringFromID(JSMessage::kParamTooLongError)); + pEvent->Rc() = false; + return CJS_Result::Success(); + } + + if (iIndexMask >= wstrMask.GetLength() && !wChange.IsEmpty()) { +- AlertIfPossible(pContext, JSGetStringFromID(JSMessage::kParamTooLongError)); ++ AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx", ++ JSGetStringFromID(JSMessage::kParamTooLongError)); + pEvent->Rc() = false; + return CJS_Result::Success(); + } + + for (size_t i = 0; i < wChange.GetLength(); ++i) { + if (iIndexMask >= wstrMask.GetLength()) { +- AlertIfPossible(pContext, ++ AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx", + JSGetStringFromID(JSMessage::kParamTooLongError)); + pEvent->Rc() = false; + return CJS_Result::Success(); +@@ -1168,8 +1176,7 @@ CJS_Result CJS_PublicMethods::AFSpecial_Keystroke( + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + +- CJS_EventRecorder* pEvent = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + if (!pEvent->HasValue()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + +@@ -1203,19 +1210,17 @@ CJS_Result CJS_PublicMethods::AFMergeChange( + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + +- CJS_EventRecorder* pEventRecorder = +- pRuntime->GetCurrentEventContext()->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + + WideString swValue; +- if (pEventRecorder->HasValue()) +- swValue = pEventRecorder->Value(); ++ if (pEvent->HasValue()) ++ swValue = pEvent->Value(); + +- if (pEventRecorder->WillCommit()) ++ if (pEvent->WillCommit()) + return CJS_Result::Success(pRuntime->NewString(swValue.AsStringView())); + + return CJS_Result::Success(pRuntime->NewString( +- CalcMergedString(pEventRecorder, swValue, pEventRecorder->Change()) +- .AsStringView())); ++ CalcMergedString(pEvent, swValue, pEvent->Change()).AsStringView())); + } + + CJS_Result CJS_PublicMethods::AFParseDateEx( +@@ -1226,11 +1231,13 @@ CJS_Result CJS_PublicMethods::AFParseDateEx( + + WideString sValue = pRuntime->ToWideString(params[0]); + WideString sFormat = pRuntime->ToWideString(params[1]); +- double dDate = ParseDateUsingFormat(sValue, sFormat, nullptr); +- if (std::isnan(dDate)) { ++ double dDate = ++ ParseDateUsingFormat(pRuntime->GetIsolate(), sValue, sFormat, nullptr); ++ if (isnan(dDate)) { + WideString swMsg = WideString::Format( + JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str()); +- AlertIfPossible(pRuntime->GetCurrentEventContext(), swMsg); ++ AlertIfPossible(pRuntime->GetCurrentEventContext(), L"AFParseDateEx", ++ swMsg); + return CJS_Result::Failure(JSMessage::kParseDateError); + } + return CJS_Result::Success(pRuntime->NewNumber(dDate)); +@@ -1245,10 +1252,10 @@ CJS_Result CJS_PublicMethods::AFSimple( + WideString sFunction = pRuntime->ToWideString(params[0]); + double arg1 = pRuntime->ToDouble(params[1]); + double arg2 = pRuntime->ToDouble(params[2]); +- if (std::isnan(arg1) || std::isnan(arg2)) ++ if (isnan(arg1) || isnan(arg2)) + return CJS_Result::Failure(JSMessage::kValueError); + +- Optional result = ApplyNamedOperation(sFunction.c_str(), arg1, arg2); ++ absl::optional result = ApplyNamedOperation(sFunction, arg1, arg2); + if (!result.has_value()) + return CJS_Result::Failure(JSMessage::kValueError); + +@@ -1347,8 +1354,8 @@ CJS_Result CJS_PublicMethods::AFSimple_Calculate( + wcscmp(sFunction.c_str(), L"MAX") == 0)) { + dValue = dTemp; + } +- Optional dResult = +- ApplyNamedOperation(sFunction.c_str(), dValue, dTemp); ++ absl::optional dResult = ++ ApplyNamedOperation(sFunction, dValue, dTemp); + if (!dResult.has_value()) + return CJS_Result::Failure(JSMessage::kValueError); + +@@ -1360,13 +1367,11 @@ CJS_Result CJS_PublicMethods::AFSimple_Calculate( + if (wcscmp(sFunction.c_str(), L"AVG") == 0 && nFieldsCount > 0) + dValue /= nFieldsCount; + +- dValue = floor(dValue * FXSYS_pow(10, 6) + 0.49) / FXSYS_pow(10, 6); ++ dValue = floor(dValue * powf(10, 6) + 0.49) / powf(10, 6); + + CJS_EventContext* pContext = pRuntime->GetCurrentEventContext(); +- if (pContext->GetEventRecorder()->HasValue()) { +- pContext->GetEventRecorder()->Value() = +- pRuntime->ToWideString(pRuntime->NewNumber(dValue)); +- } ++ if (pContext->HasValue()) ++ pContext->Value() = pRuntime->ToWideString(pRuntime->NewNumber(dValue)); + + return CJS_Result::Success(); + } +@@ -1379,15 +1384,14 @@ CJS_Result CJS_PublicMethods::AFRange_Validate( + if (params.size() != 4) + return CJS_Result::Failure(JSMessage::kParamError); + +- CJS_EventContext* pContext = pRuntime->GetCurrentEventContext(); +- CJS_EventRecorder* pEvent = pContext->GetEventRecorder(); ++ CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext(); + if (!pEvent->HasValue()) + return CJS_Result::Failure(JSMessage::kBadObjectError); + + if (pEvent->Value().IsEmpty()) + return CJS_Result::Success(); + +- double dEentValue = atof(pEvent->Value().ToDefANSI().c_str()); ++ double dEventValue = atof(pEvent->Value().ToUTF8().c_str()); + bool bGreaterThan = pRuntime->ToBoolean(params[0]); + double dGreaterThan = pRuntime->ToDouble(params[1]); + bool bLessThan = pRuntime->ToBoolean(params[2]); +@@ -1395,25 +1399,25 @@ CJS_Result CJS_PublicMethods::AFRange_Validate( + WideString swMsg; + + if (bGreaterThan && bLessThan) { +- if (dEentValue < dGreaterThan || dEentValue > dLessThan) ++ if (dEventValue < dGreaterThan || dEventValue > dLessThan) + swMsg = WideString::Format( + JSGetStringFromID(JSMessage::kRangeBetweenError).c_str(), + pRuntime->ToWideString(params[1]).c_str(), + pRuntime->ToWideString(params[3]).c_str()); + } else if (bGreaterThan) { +- if (dEentValue < dGreaterThan) ++ if (dEventValue < dGreaterThan) + swMsg = WideString::Format( + JSGetStringFromID(JSMessage::kRangeGreaterError).c_str(), + pRuntime->ToWideString(params[1]).c_str()); + } else if (bLessThan) { +- if (dEentValue > dLessThan) ++ if (dEventValue > dLessThan) + swMsg = WideString::Format( + JSGetStringFromID(JSMessage::kRangeLessError).c_str(), + pRuntime->ToWideString(params[3]).c_str()); + } + + if (!swMsg.IsEmpty()) { +- AlertIfPossible(pContext, swMsg); ++ AlertIfPossible(pEvent, L"AFRange_Validate", swMsg); + pEvent->Rc() = false; + } + return CJS_Result::Success(); +diff --git a/fxjs/cjs_publicmethods.h b/fxjs/cjs_publicmethods.h +index 53a4dccc5..ddf13cdac 100644 +--- a/fxjs/cjs_publicmethods.h ++++ b/fxjs/cjs_publicmethods.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -18,9 +18,12 @@ class CJS_PublicMethods final : public CJS_Object { + + static void DefineJSObjects(CFXJS_Engine* pEngine); + +- static double ParseDate(const WideString& value, bool* bWrongFormat); +- static double ParseDateAsGMT(const WideString& value); +- static double ParseDateUsingFormat(const WideString& value, ++ static double ParseDate(v8::Isolate* isolate, ++ const WideString& value, ++ bool* bWrongFormat); ++ static double ParseDateAsGMT(v8::Isolate* isolate, const WideString& value); ++ static double ParseDateUsingFormat(v8::Isolate* isolate, ++ const WideString& value, + const WideString& format, + bool* bWrongFormat); + +diff --git a/fxjs/cjs_publicmethods_embeddertest.cpp b/fxjs/cjs_publicmethods_embeddertest.cpp +index d35f0cccd..5a1a6e7cc 100644 +--- a/fxjs/cjs_publicmethods_embeddertest.cpp ++++ b/fxjs/cjs_publicmethods_embeddertest.cpp +@@ -1,16 +1,22 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include ++#include ++ + #include + + #include "core/fxcrt/fx_string.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "fxjs/cjs_event_context.h" + #include "fxjs/cjs_publicmethods.h" ++#include "testing/external_engine_embedder_test.h" + #include "testing/gtest/include/gtest/gtest.h" +-#include "testing/js_embedder_test.h" ++#include "v8/include/v8-container.h" ++#include "v8/include/v8-context.h" ++#include "v8/include/v8-isolate.h" ++#include "v8/include/v8-local-handle.h" ++#include "v8/include/v8-value.h" + + namespace { + +@@ -20,7 +26,7 @@ double RoundDownDate(double date) { + + } // namespace + +-class CJS_PublicMethodsEmbedderTest : public JSEmbedderTest {}; ++class CJS_PublicMethodsEmbedderTest : public ExternalEngineEmbedderTest {}; + + TEST_F(CJS_PublicMethodsEmbedderTest, ParseDateUsingFormat) { + v8::Isolate::Scope isolate_scope(isolate()); +@@ -31,80 +37,80 @@ TEST_F(CJS_PublicMethodsEmbedderTest, ParseDateUsingFormat) { + + // 1968 + bWrongFormat = false; +- date = CJS_PublicMethods::ParseDateUsingFormat(L"06/25/1968", L"mm/dd/yyyy", +- &bWrongFormat); ++ date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"06/25/1968", ++ L"mm/dd/yyyy", &bWrongFormat); + date = RoundDownDate(date); + EXPECT_DOUBLE_EQ(-47865600000, date); + EXPECT_FALSE(bWrongFormat); + + // 1968 + bWrongFormat = false; +- date = CJS_PublicMethods::ParseDateUsingFormat(L"25061968", L"ddmmyyyy", +- &bWrongFormat); ++ date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"25061968", ++ L"ddmmyyyy", &bWrongFormat); + date = RoundDownDate(date); + EXPECT_DOUBLE_EQ(-47865600000, date); + EXPECT_FALSE(bWrongFormat); + + // 1968 + bWrongFormat = false; +- date = CJS_PublicMethods::ParseDateUsingFormat(L"19680625", L"yyyymmdd", +- &bWrongFormat); ++ date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"19680625", ++ L"yyyymmdd", &bWrongFormat); + date = RoundDownDate(date); + EXPECT_DOUBLE_EQ(-47865600000, date); + EXPECT_FALSE(bWrongFormat); + + // 1985 + bWrongFormat = false; +- date = CJS_PublicMethods::ParseDateUsingFormat(L"31121985", L"ddmmyyyy", +- &bWrongFormat); ++ date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"31121985", ++ L"ddmmyyyy", &bWrongFormat); + date = RoundDownDate(date); + EXPECT_DOUBLE_EQ(504835200000.0, date); + EXPECT_FALSE(bWrongFormat); + + // 2085, the other '85. + bWrongFormat = false; +- date = CJS_PublicMethods::ParseDateUsingFormat(L"311285", L"ddmmyy", +- &bWrongFormat); ++ date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"311285", ++ L"ddmmyy", &bWrongFormat); + date = RoundDownDate(date); + EXPECT_DOUBLE_EQ(3660595200000.0, date); + EXPECT_FALSE(bWrongFormat); + + // 1995 + bWrongFormat = false; +- date = CJS_PublicMethods::ParseDateUsingFormat(L"01021995", L"ddmmyyyy", +- &bWrongFormat); ++ date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"01021995", ++ L"ddmmyyyy", &bWrongFormat); + date = RoundDownDate(date); + EXPECT_DOUBLE_EQ(791596800000.0, date); + EXPECT_FALSE(bWrongFormat); + + // 2095, the other '95. + bWrongFormat = false; +- date = CJS_PublicMethods::ParseDateUsingFormat(L"010295", L"ddmmyy", +- &bWrongFormat); ++ date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"010295", ++ L"ddmmyy", &bWrongFormat); + date = RoundDownDate(date); + EXPECT_DOUBLE_EQ(3947356800000.0, date); + EXPECT_FALSE(bWrongFormat); + + // 2005 + bWrongFormat = false; +- date = CJS_PublicMethods::ParseDateUsingFormat(L"01022005", L"ddmmyyyy", +- &bWrongFormat); ++ date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"01022005", ++ L"ddmmyyyy", &bWrongFormat); + date = RoundDownDate(date); + EXPECT_DOUBLE_EQ(1107216000000.0, date); + EXPECT_FALSE(bWrongFormat); + + // 2005 + bWrongFormat = false; +- date = CJS_PublicMethods::ParseDateUsingFormat(L"010205", L"ddmmyy", +- &bWrongFormat); ++ date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"010205", ++ L"ddmmyy", &bWrongFormat); + date = RoundDownDate(date); + EXPECT_DOUBLE_EQ(1107216000000.0, date); + EXPECT_FALSE(bWrongFormat); + + // 2005 in a different format. https://crbug.com/436572 + bWrongFormat = false; +- date = CJS_PublicMethods::ParseDateUsingFormat(L"050201", L"yymmdd", +- &bWrongFormat); ++ date = CJS_PublicMethods::ParseDateUsingFormat(isolate(), L"050201", ++ L"yymmdd", &bWrongFormat); + date = RoundDownDate(date); + EXPECT_DOUBLE_EQ(1107216000000.0, date); + EXPECT_FALSE(bWrongFormat); +@@ -182,7 +188,7 @@ TEST_F(CJS_PublicMethodsEmbedderTest, AFSimple_CalculateSum) { + v8::HandleScope handle_scope(isolate()); + v8::Context::Scope context_scope(GetV8Context()); + +- EXPECT_TRUE(OpenDocument("calculate.pdf")); ++ ASSERT_TRUE(OpenDocument("calculate.pdf")); + auto* page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -191,8 +197,7 @@ TEST_F(CJS_PublicMethodsEmbedderTest, AFSimple_CalculateSum) { + runtime.NewEventContext(); + + WideString result; +- runtime.GetCurrentEventContext()->GetEventRecorder()->SetValueForTest( +- &result); ++ runtime.GetCurrentEventContext()->SetValueForTest(&result); + + auto ary = runtime.NewArray(); + runtime.PutArrayElement(ary, 0, runtime.NewString("Calc1_A")); +@@ -205,8 +210,7 @@ TEST_F(CJS_PublicMethodsEmbedderTest, AFSimple_CalculateSum) { + CJS_Result ret = CJS_PublicMethods::AFSimple_Calculate(&runtime, params); + UnloadPage(page); + +- runtime.GetCurrentEventContext()->GetEventRecorder()->SetValueForTest( +- nullptr); ++ runtime.GetCurrentEventContext()->SetValueForTest(nullptr); + + ASSERT_TRUE(!ret.HasError()); + ASSERT_TRUE(!ret.HasReturn()); +@@ -218,7 +222,7 @@ TEST_F(CJS_PublicMethodsEmbedderTest, AFNumber_Keystroke) { + v8::HandleScope handle_scope(isolate()); + v8::Context::Scope context_scope(GetV8Context()); + +- EXPECT_TRUE(OpenDocument("calculate.pdf")); ++ ASSERT_TRUE(OpenDocument("calculate.pdf")); + auto* page = LoadPage(0); + ASSERT_TRUE(page); + +@@ -226,7 +230,7 @@ TEST_F(CJS_PublicMethodsEmbedderTest, AFNumber_Keystroke) { + CPDFSDKFormFillEnvironmentFromFPDFFormHandle(form_handle())); + runtime.NewEventContext(); + +- auto* handler = runtime.GetCurrentEventContext()->GetEventRecorder(); ++ auto* handler = runtime.GetCurrentEventContext(); + + bool valid = true; + WideString result = L"-10"; +diff --git a/fxjs/cjs_publicmethods_unittest.cpp b/fxjs/cjs_publicmethods_unittest.cpp +index 1d7241849..a5b9314b3 100644 +--- a/fxjs/cjs_publicmethods_unittest.cpp ++++ b/fxjs/cjs_publicmethods_unittest.cpp +@@ -1,9 +1,11 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fxjs/cjs_publicmethods.h" + ++#include ++ + #include "testing/gtest/include/gtest/gtest.h" + + TEST(CJS_PublicMethods, IsNumber) { +@@ -42,7 +44,7 @@ TEST(CJS_PublicMethods, IsNumber) { + {L"0123", true}, + {L"9876123", true}, + }; +- for (size_t i = 0; i < FX_ArraySize(test_data); ++i) { ++ for (size_t i = 0; i < std::size(test_data); ++i) { + EXPECT_EQ(test_data[i].expected, + CJS_PublicMethods::IsNumber(test_data[i].input)) + << "for case " << i; +diff --git a/fxjs/cjs_result.cpp b/fxjs/cjs_result.cpp +index 92f2b0bb8..8bed63363 100644 +--- a/fxjs/cjs_result.cpp ++++ b/fxjs/cjs_result.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include "fxjs/cjs_result.h" + +-CJS_Result::CJS_Result() {} ++CJS_Result::CJS_Result() = default; + + CJS_Result::CJS_Result(v8::Local ret) : return_(ret) {} + +diff --git a/fxjs/cjs_result.h b/fxjs/cjs_result.h +index 5e1ed7cee..993a4dc13 100644 +--- a/fxjs/cjs_result.h ++++ b/fxjs/cjs_result.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,12 +8,12 @@ + #define FXJS_CJS_RESULT_H_ + + #include "fxjs/js_resources.h" +-#include "third_party/base/optional.h" +-#include "v8/include/v8.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "v8/include/v8-forward.h" + + class CJS_Result { + public: +- // Wrap constructors with static methods so we can apply WARN_UNUSED_RESULT, ++ // Wrap constructors with static methods so we can apply [[nodiscard]], + // otherwise we can't catch places where someone mistakenly writes: + // + // if (error) +@@ -24,14 +24,14 @@ class CJS_Result { + // if (error) + // return CJS_Result(JS_ERROR_CODE); + // +- static CJS_Result Success() WARN_UNUSED_RESULT { return CJS_Result(); } +- static CJS_Result Success(v8::Local value) WARN_UNUSED_RESULT { ++ [[nodiscard]] static CJS_Result Success() { return CJS_Result(); } ++ [[nodiscard]] static CJS_Result Success(v8::Local value) { + return CJS_Result(value); + } +- static CJS_Result Failure(const WideString& str) WARN_UNUSED_RESULT { ++ [[nodiscard]] static CJS_Result Failure(const WideString& str) { + return CJS_Result(str); + } +- static CJS_Result Failure(JSMessage id) WARN_UNUSED_RESULT { ++ [[nodiscard]] static CJS_Result Failure(JSMessage id) { + return CJS_Result(id); + } + +@@ -39,7 +39,7 @@ class CJS_Result { + ~CJS_Result(); + + bool HasError() const { return error_.has_value(); } +- WideString Error() const { return error_.value(); } ++ const WideString& Error() const { return error_.value(); } + + bool HasReturn() const { return !return_.IsEmpty(); } + v8::Local Return() const { return return_; } +@@ -50,7 +50,7 @@ class CJS_Result { + explicit CJS_Result(const WideString&); // Error with custom message. + explicit CJS_Result(JSMessage id); // Error with stock message. + +- Optional error_; ++ absl::optional error_; + v8::Local return_; + }; + +diff --git a/fxjs/cjs_runtime.cpp b/fxjs/cjs_runtime.cpp +index 919d47a5e..f7d76da77 100644 +--- a/fxjs/cjs_runtime.cpp ++++ b/fxjs/cjs_runtime.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,8 @@ + + #include "fxjs/cjs_runtime.h" + ++#include ++ + #include + + #include "fpdfsdk/cpdfsdk_formfillenvironment.h" +@@ -19,7 +21,6 @@ + #include "fxjs/cjs_document.h" + #include "fxjs/cjs_event.h" + #include "fxjs/cjs_event_context.h" +-#include "fxjs/cjs_eventrecorder.h" + #include "fxjs/cjs_field.h" + #include "fxjs/cjs_font.h" + #include "fxjs/cjs_global.h" +@@ -36,14 +37,20 @@ + #include "fxjs/cjs_timerobj.h" + #include "fxjs/cjs_util.h" + #include "fxjs/cjs_zoomtype.h" ++#include "fxjs/fxv8.h" + #include "fxjs/js_define.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check_op.h" ++#include "v8/include/v8-context.h" ++#include "v8/include/v8-exception.h" ++#include "v8/include/v8-isolate.h" + + CJS_Runtime::CJS_Runtime(CPDFSDK_FormFillEnvironment* pFormFillEnv) + : m_pFormFillEnv(pFormFillEnv) { + v8::Isolate* pIsolate = nullptr; + IPDF_JSPLATFORM* pPlatform = m_pFormFillEnv->GetFormFillInfo()->m_pJsPlatform; + if (pPlatform->version <= 2) { ++ // Backwards compatibility - JS now initialized earlier in more modern ++ // JSPLATFORM versions. + unsigned int embedderDataSlot = 0; + v8::Isolate* pExternalIsolate = nullptr; + if (pPlatform->version == 2) { +@@ -120,12 +127,12 @@ void CJS_Runtime::DefineJSObjects() { + } + + IJS_EventContext* CJS_Runtime::NewEventContext() { +- m_EventContextArray.push_back(pdfium::MakeUnique(this)); ++ m_EventContextArray.push_back(std::make_unique(this)); + return m_EventContextArray.back().get(); + } + + void CJS_Runtime::ReleaseEventContext(IJS_EventContext* pContext) { +- ASSERT(pContext == m_EventContextArray.back().get()); ++ DCHECK_EQ(pContext, m_EventContextArray.back().get()); + m_EventContextArray.pop_back(); + } + +@@ -134,7 +141,7 @@ CJS_EventContext* CJS_Runtime::GetCurrentEventContext() const { + : m_EventContextArray.back().get(); + } + +-TimerHandlerIface* CJS_Runtime::GetTimerHandler() const { ++CFX_Timer::HandlerIface* CJS_Runtime::GetTimerHandler() const { + return m_pFormFillEnv ? m_pFormFillEnv->GetTimerHandler() : nullptr; + } + +@@ -148,7 +155,7 @@ void CJS_Runtime::SetFormFillEnvToDocument() { + if (pThis.IsEmpty()) + return; + +- auto pJSDocument = JSGetObject(pThis); ++ auto pJSDocument = JSGetObject(GetIsolate(), pThis); + if (!pJSDocument) + return; + +@@ -159,7 +166,7 @@ CPDFSDK_FormFillEnvironment* CJS_Runtime::GetFormFillEnv() const { + return m_pFormFillEnv.Get(); + } + +-Optional CJS_Runtime::ExecuteScript( ++absl::optional CJS_Runtime::ExecuteScript( + const WideString& script) { + return Execute(script); + } +@@ -176,22 +183,16 @@ CJS_Runtime* CJS_Runtime::AsCJSRuntime() { + return this; + } + +-bool CJS_Runtime::GetValueByNameFromGlobalObject(ByteStringView utf8Name, +- v8::Local* pValue) { ++v8::Local CJS_Runtime::GetValueByNameFromGlobalObject( ++ ByteStringView utf8Name) { + v8::Isolate::Scope isolate_scope(GetIsolate()); + v8::Local context = GetV8Context(); + v8::Context::Scope context_scope(context); +- v8::Local str = +- v8::String::NewFromUtf8(GetIsolate(), utf8Name.unterminated_c_str(), +- v8::NewStringType::kNormal, utf8Name.GetLength()) +- .ToLocalChecked(); +- v8::MaybeLocal maybe_propvalue = +- context->Global()->Get(context, str); +- if (maybe_propvalue.IsEmpty()) +- return false; +- +- *pValue = maybe_propvalue.ToLocalChecked(); +- return true; ++ v8::Local str = fxv8::NewStringHelper(GetIsolate(), utf8Name); ++ v8::MaybeLocal maybe_value = context->Global()->Get(context, str); ++ if (maybe_value.IsEmpty()) ++ return v8::Local(); ++ return maybe_value.ToLocalChecked(); + } + + bool CJS_Runtime::SetValueByNameInGlobalObject(ByteStringView utf8Name, +@@ -203,10 +204,7 @@ bool CJS_Runtime::SetValueByNameInGlobalObject(ByteStringView utf8Name, + v8::Isolate::Scope isolate_scope(pIsolate); + v8::Local context = GetV8Context(); + v8::Context::Scope context_scope(context); +- v8::Local str = +- v8::String::NewFromUtf8(pIsolate, utf8Name.unterminated_c_str(), +- v8::NewStringType::kNormal, utf8Name.GetLength()) +- .ToLocalChecked(); ++ v8::Local str = fxv8::NewStringHelper(pIsolate, utf8Name); + v8::Maybe result = context->Global()->Set(context, str, pValue); + return result.IsJust() && result.FromJust(); + } +@@ -215,22 +213,21 @@ v8::Local CJS_Runtime::MaybeCoerceToNumber( + v8::Local value) { + bool bAllowNaN = false; + if (value->IsString()) { +- ByteString bstr = ToWideString(value).ToDefANSI(); ++ ByteString bstr = fxv8::ToByteString(GetIsolate(), value.As()); + if (bstr.IsEmpty()) + return value; + if (bstr == "NaN") + bAllowNaN = true; + } + +- v8::Isolate* pIsolate = GetIsolate(); +- v8::TryCatch try_catch(pIsolate); ++ v8::TryCatch try_catch(GetIsolate()); + v8::MaybeLocal maybeNum = +- value->ToNumber(pIsolate->GetCurrentContext()); ++ value->ToNumber(GetIsolate()->GetCurrentContext()); + if (maybeNum.IsEmpty()) + return value; + + v8::Local num = maybeNum.ToLocalChecked(); +- if (std::isnan(num->Value()) && !bAllowNaN) ++ if (isnan(num->Value()) && !bAllowNaN) + return value; + + return num; +diff --git a/fxjs/cjs_runtime.h b/fxjs/cjs_runtime.h +index b3f8ef68f..a3569f6d3 100644 +--- a/fxjs/cjs_runtime.h ++++ b/fxjs/cjs_runtime.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,20 +12,19 @@ + #include + #include + ++#include "core/fxcrt/cfx_timer.h" + #include "core/fxcrt/observed_ptr.h" +-#include "core/fxcrt/timerhandler_iface.h" + #include "fxjs/cfxjs_engine.h" +-#include "fxjs/cjs_eventrecorder.h" ++#include "fxjs/cjs_event_context.h" + #include "fxjs/ijs_runtime.h" + +-class CJS_EventContext; + class CPDFSDK_FormFillEnvironment; + + class CJS_Runtime final : public IJS_Runtime, + public CFXJS_Engine, + public Observable { + public: +- using FieldEvent = std::pair; ++ using FieldEvent = std::pair; + + explicit CJS_Runtime(CPDFSDK_FormFillEnvironment* pFormFillEnv); + ~CJS_Runtime() override; +@@ -35,11 +34,11 @@ class CJS_Runtime final : public IJS_Runtime, + IJS_EventContext* NewEventContext() override; + void ReleaseEventContext(IJS_EventContext* pContext) override; + CPDFSDK_FormFillEnvironment* GetFormFillEnv() const override; +- Optional ExecuteScript( ++ absl::optional ExecuteScript( + const WideString& script) override; + + CJS_EventContext* GetCurrentEventContext() const; +- TimerHandlerIface* GetTimerHandler() const; ++ CFX_Timer::HandlerIface* GetTimerHandler() const; + + // Returns true if the event isn't already found in the set. + bool AddEventToSet(const FieldEvent& event); +@@ -53,8 +52,7 @@ class CJS_Runtime final : public IJS_Runtime, + // value will be returned, otherwise |value| is returned. + v8::Local MaybeCoerceToNumber(v8::Local value); + +- bool GetValueByNameFromGlobalObject(ByteStringView utf8Name, +- v8::Local* pValue); ++ v8::Local GetValueByNameFromGlobalObject(ByteStringView utf8Name); + bool SetValueByNameInGlobalObject(ByteStringView utf8Name, + v8::Local pValue); + +diff --git a/fxjs/cjs_runtimestub.cpp b/fxjs/cjs_runtimestub.cpp +index 6e313de58..0a68a17f6 100644 +--- a/fxjs/cjs_runtimestub.cpp ++++ b/fxjs/cjs_runtimestub.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,6 @@ + #include "fxjs/cjs_runtimestub.h" + + #include "fxjs/cjs_event_context_stub.h" +-#include "third_party/base/ptr_util.h" + + CJS_RuntimeStub::CJS_RuntimeStub(CPDFSDK_FormFillEnvironment* pFormFillEnv) + : m_pFormFillEnv(pFormFillEnv) {} +@@ -16,21 +15,21 @@ CJS_RuntimeStub::~CJS_RuntimeStub() = default; + + IJS_EventContext* CJS_RuntimeStub::NewEventContext() { + if (!m_pContext) +- m_pContext = pdfium::MakeUnique(); ++ m_pContext = std::make_unique(); + return m_pContext.get(); + } + + void CJS_RuntimeStub::ReleaseEventContext(IJS_EventContext* pContext) {} + + CPDFSDK_FormFillEnvironment* CJS_RuntimeStub::GetFormFillEnv() const { +- return m_pFormFillEnv.Get(); ++ return m_pFormFillEnv; + } + + CJS_Runtime* CJS_RuntimeStub::AsCJSRuntime() { + return nullptr; + } + +-Optional CJS_RuntimeStub::ExecuteScript( ++absl::optional CJS_RuntimeStub::ExecuteScript( + const WideString& script) { +- return pdfium::nullopt; ++ return absl::nullopt; + } +diff --git a/fxjs/cjs_runtimestub.h b/fxjs/cjs_runtimestub.h +index 2b5e71349..c58e2c44f 100644 +--- a/fxjs/cjs_runtimestub.h ++++ b/fxjs/cjs_runtimestub.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,8 +9,8 @@ + + #include + +-#include "core/fxcrt/fx_string.h" + #include "core/fxcrt/unowned_ptr.h" ++#include "core/fxcrt/widestring.h" + #include "fxjs/ijs_runtime.h" + + class CPDFSDK_FormFillEnvironment; +@@ -27,7 +27,7 @@ class CJS_RuntimeStub final : public IJS_Runtime { + void ReleaseEventContext(IJS_EventContext* pContext) override; + CPDFSDK_FormFillEnvironment* GetFormFillEnv() const override; + +- Optional ExecuteScript( ++ absl::optional ExecuteScript( + const WideString& script) override; + + private: +diff --git a/fxjs/cjs_scalehow.cpp b/fxjs/cjs_scalehow.cpp +index 999949cda..b64da9fbe 100644 +--- a/fxjs/cjs_scalehow.cpp ++++ b/fxjs/cjs_scalehow.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,7 +10,7 @@ const JSConstSpec CJS_ScaleHow::ConstSpecs[] = { + {"proportional", JSConstSpec::Number, 0, 0}, + {"anamorphic", JSConstSpec::Number, 1, 0}}; + +-int CJS_ScaleHow::ObjDefnID = -1; ++uint32_t CJS_ScaleHow::ObjDefnID = 0; + + // static + void CJS_ScaleHow::DefineJSObjects(CFXJS_Engine* pEngine) { +diff --git a/fxjs/cjs_scalehow.h b/fxjs/cjs_scalehow.h +index f8c7eb536..b5ab3e584 100644 +--- a/fxjs/cjs_scalehow.h ++++ b/fxjs/cjs_scalehow.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,7 +16,7 @@ class CJS_ScaleHow final : public CJS_Object { + CJS_ScaleHow() = delete; + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const JSConstSpec ConstSpecs[]; + }; + +diff --git a/fxjs/cjs_scalewhen.cpp b/fxjs/cjs_scalewhen.cpp +index 5f3915377..e8a3dd4b6 100644 +--- a/fxjs/cjs_scalewhen.cpp ++++ b/fxjs/cjs_scalewhen.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,7 +12,7 @@ const JSConstSpec CJS_ScaleWhen::ConstSpecs[] = { + {"tooBig", JSConstSpec::Number, 2, 0}, + {"tooSmall", JSConstSpec::Number, 3, 0}}; + +-int CJS_ScaleWhen::ObjDefnID = -1; ++uint32_t CJS_ScaleWhen::ObjDefnID = 0; + + // static + void CJS_ScaleWhen::DefineJSObjects(CFXJS_Engine* pEngine) { +diff --git a/fxjs/cjs_scalewhen.h b/fxjs/cjs_scalewhen.h +index ef046f93b..43ab5418a 100644 +--- a/fxjs/cjs_scalewhen.h ++++ b/fxjs/cjs_scalewhen.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,7 +16,7 @@ class CJS_ScaleWhen final : public CJS_Object { + CJS_ScaleWhen() = delete; + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const JSConstSpec ConstSpecs[]; + }; + +diff --git a/fxjs/cjs_style.cpp b/fxjs/cjs_style.cpp +index c068702f1..a43fa7dd0 100644 +--- a/fxjs/cjs_style.cpp ++++ b/fxjs/cjs_style.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ const JSConstSpec CJS_Style::ConstSpecs[] = { + {"st", JSConstSpec::String, 0, "star"}, + {"sq", JSConstSpec::String, 0, "square"}}; + +-int CJS_Style::ObjDefnID = -1; ++uint32_t CJS_Style::ObjDefnID = 0; + + // static + void CJS_Style::DefineJSObjects(CFXJS_Engine* pEngine) { +diff --git a/fxjs/cjs_style.h b/fxjs/cjs_style.h +index 6e3ee2fbc..02449a66c 100644 +--- a/fxjs/cjs_style.h ++++ b/fxjs/cjs_style.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,7 +16,7 @@ class CJS_Style final : public CJS_Object { + CJS_Style() = delete; + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const JSConstSpec ConstSpecs[]; + }; + +diff --git a/fxjs/cjs_timerobj.cpp b/fxjs/cjs_timerobj.cpp +index 5fa1ac332..9dc8cfa6e 100644 +--- a/fxjs/cjs_timerobj.cpp ++++ b/fxjs/cjs_timerobj.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,10 +9,10 @@ + #include "fxjs/global_timer.h" + #include "fxjs/js_define.h" + +-int CJS_TimerObj::ObjDefnID = -1; ++uint32_t CJS_TimerObj::ObjDefnID = 0; + + // static +-int CJS_TimerObj::GetObjDefnID() { ++uint32_t CJS_TimerObj::GetObjDefnID() { + return ObjDefnID; + } + +diff --git a/fxjs/cjs_timerobj.h b/fxjs/cjs_timerobj.h +index 69effa638..a82ecd144 100644 +--- a/fxjs/cjs_timerobj.h ++++ b/fxjs/cjs_timerobj.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -13,7 +13,7 @@ class GlobalTimer; + + class CJS_TimerObj final : public CJS_Object { + public: +- static int GetObjDefnID(); ++ static uint32_t GetObjDefnID(); + static void DefineJSObjects(CFXJS_Engine* pEngine); + + CJS_TimerObj(v8::Local pObject, CJS_Runtime* pRuntime); +@@ -23,7 +23,7 @@ class CJS_TimerObj final : public CJS_Object { + int GetTimerID() const { return m_nTimerID; } + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + + int m_nTimerID = 0; // Weak reference to GlobalTimer through global map. + }; +diff --git a/fxjs/cjs_util.cpp b/fxjs/cjs_util.cpp +index 1f95c3162..812e12bb7 100644 +--- a/fxjs/cjs_util.cpp ++++ b/fxjs/cjs_util.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,25 +6,27 @@ + + #include "fxjs/cjs_util.h" + ++#include + #include + + #include +-#include +-#include ++#include + #include + + #include "build/build_config.h" + #include "core/fxcrt/fx_extension.h" + #include "fxjs/cjs_event_context.h" +-#include "fxjs/cjs_eventrecorder.h" + #include "fxjs/cjs_object.h" + #include "fxjs/cjs_publicmethods.h" + #include "fxjs/cjs_runtime.h" + #include "fxjs/fx_date_helpers.h" ++#include "fxjs/fxv8.h" + #include "fxjs/js_define.h" + #include "fxjs/js_resources.h" ++#include "third_party/base/check_op.h" ++#include "v8/include/v8-date.h" + +-#if defined(OS_ANDROID) ++#if BUILDFLAG(IS_ANDROID) + #include + #endif + +@@ -40,8 +42,8 @@ struct TbConvert { + // Map PDF-style directives lacking direct wcsftime directives to + // the value with which they will be replaced. + struct TbConvertAdditional { +- const wchar_t* lpszJSMark; +- int iValue; ++ wchar_t js_mark; ++ int value; + }; + + const TbConvert TbConvertTable[] = { +@@ -49,7 +51,7 @@ const TbConvert TbConvertTable[] = { + {L"ddd", L"%a"}, {L"dd", L"%d"}, {L"yyyy", L"%Y"}, {L"yy", L"%y"}, + {L"HH", L"%H"}, {L"hh", L"%I"}, {L"MM", L"%M"}, {L"ss", L"%S"}, + {L"TT", L"%p"}, +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + {L"tt", L"%p"}, {L"h", L"%#I"}, + #else + {L"tt", L"%P"}, {L"h", L"%l"}, +@@ -75,11 +77,11 @@ const JSMethodSpec CJS_Util::MethodSpecs[] = { + {"scand", scand_static}, + {"byteToChar", byteToChar_static}}; + +-int CJS_Util::ObjDefnID = -1; ++uint32_t CJS_Util::ObjDefnID = 0; + const char CJS_Util::kName[] = "util"; + + // static +-int CJS_Util::GetObjDefnID() { ++uint32_t CJS_Util::GetObjDefnID() { + return ObjDefnID; + } + +@@ -109,7 +111,8 @@ CJS_Result CJS_Util::printf(CJS_Runtime* pRuntime, + { + size_t offset = 0; + while (true) { +- Optional offset_end = unsafe_fmt_string.Find(L"%", offset + 1); ++ absl::optional offset_end = ++ unsafe_fmt_string.Find(L"%", offset + 1); + if (!offset_end.has_value()) { + unsafe_conversion_specifiers.push_back( + unsafe_fmt_string.Last(unsafe_fmt_string.GetLength() - offset)); +@@ -132,14 +135,14 @@ CJS_Result CJS_Util::printf(CJS_Runtime* pRuntime, + + WideString segment; + switch (ParseDataType(&fmt)) { +- case UTIL_INT: ++ case DataType::kInt: + segment = WideString::Format(fmt.c_str(), pRuntime->ToInt32(params[i])); + break; +- case UTIL_DOUBLE: ++ case DataType::kDouble: + segment = + WideString::Format(fmt.c_str(), pRuntime->ToDouble(params[i])); + break; +- case UTIL_STRING: ++ case DataType::kString: + segment = WideString::Format(fmt.c_str(), + pRuntime->ToWideString(params[i]).c_str()); + break; +@@ -163,11 +166,11 @@ CJS_Result CJS_Util::printd(CJS_Runtime* pRuntime, + if (iSize < 2) + return CJS_Result::Failure(JSMessage::kParamError); + +- if (params[1].IsEmpty() || !params[1]->IsDate()) ++ if (!fxv8::IsDate(params[1])) + return CJS_Result::Failure(JSMessage::kSecondParamNotDateError); + + v8::Local v8_date = params[1].As(); +- if (v8_date.IsEmpty() || std::isnan(pRuntime->ToDouble(v8_date))) ++ if (v8_date.IsEmpty() || isnan(pRuntime->ToDouble(v8_date))) + return CJS_Result::Failure(JSMessage::kSecondParamInvalidDateError); + + double date = FX_LocalTime(pRuntime->ToDouble(v8_date)); +@@ -209,18 +212,19 @@ CJS_Result CJS_Util::printd(CJS_Runtime* pRuntime, + + // Convert PDF-style format specifiers to wcsftime specifiers. Remove any + // pre-existing %-directives before inserting our own. +- std::basic_string cFormat = +- pRuntime->ToWideString(params[0]).c_str(); ++ std::wstring cFormat = pRuntime->ToWideString(params[0]).c_str(); + cFormat.erase(std::remove(cFormat.begin(), cFormat.end(), '%'), + cFormat.end()); + +- for (size_t i = 0; i < FX_ArraySize(TbConvertTable); ++i) { +- int iStart = 0; +- int iEnd; +- while ((iEnd = cFormat.find(TbConvertTable[i].lpszJSMark, iStart)) != -1) { +- cFormat.replace(iEnd, wcslen(TbConvertTable[i].lpszJSMark), ++ for (size_t i = 0; i < std::size(TbConvertTable); ++i) { ++ size_t nFound = 0; ++ while (true) { ++ nFound = cFormat.find(TbConvertTable[i].lpszJSMark, nFound); ++ if (nFound == std::wstring::npos) ++ break; ++ ++ cFormat.replace(nFound, wcslen(TbConvertTable[i].lpszJSMark), + TbConvertTable[i].lpszCppMark); +- iStart = iEnd; + } + } + +@@ -228,24 +232,24 @@ CJS_Result CJS_Util::printd(CJS_Runtime* pRuntime, + return CJS_Result::Failure(JSMessage::kValueError); + + const TbConvertAdditional cTableAd[] = { +- {L"m", month}, {L"d", day}, +- {L"H", hour}, {L"h", hour > 12 ? hour - 12 : hour}, +- {L"M", min}, {L"s", sec}, ++ {L'm', month}, {L'd', day}, ++ {L'H', hour}, {L'h', hour > 12 ? hour - 12 : hour}, ++ {L'M', min}, {L's', sec}, + }; + +- for (size_t i = 0; i < FX_ArraySize(cTableAd); ++i) { +- int iStart = 0; +- int iEnd; +- while ((iEnd = cFormat.find(cTableAd[i].lpszJSMark, iStart)) != -1) { +- if (iEnd > 0) { +- if (cFormat[iEnd - 1] == L'%') { +- iStart = iEnd + 1; +- continue; +- } ++ for (size_t i = 0; i < std::size(cTableAd); ++i) { ++ size_t nFound = 0; ++ while (true) { ++ nFound = cFormat.find(cTableAd[i].js_mark, nFound); ++ if (nFound == std::wstring::npos) ++ break; ++ ++ if (nFound != 0 && cFormat[nFound - 1] == L'%') { ++ ++nFound; ++ continue; + } +- cFormat.replace(iEnd, wcslen(cTableAd[i].lpszJSMark), +- WideString::Format(L"%d", cTableAd[i].iValue).c_str()); +- iStart = iEnd; ++ cFormat.replace(nFound, 1, ++ WideString::FormatInteger(cTableAd[i].value).c_str()); + } + } + +@@ -373,8 +377,9 @@ CJS_Result CJS_Util::scand(CJS_Runtime* pRuntime, + WideString sDate = pRuntime->ToWideString(params[1]); + double dDate = FX_GetDateTime(); + if (sDate.GetLength() > 0) +- dDate = CJS_PublicMethods::ParseDateUsingFormat(sDate, sFormat, nullptr); +- if (std::isnan(dDate)) ++ dDate = CJS_PublicMethods::ParseDateUsingFormat(pRuntime->GetIsolate(), ++ sDate, sFormat, nullptr); ++ if (isnan(dDate)) + return CJS_Result::Success(pRuntime->NewUndefined()); + + return CJS_Result::Success(pRuntime->NewDate(dDate)); +@@ -395,80 +400,80 @@ CJS_Result CJS_Util::byteToChar( + } + + // static +-int CJS_Util::ParseDataType(WideString* sFormat) { +- enum State { BEFORE, FLAGS, WIDTH, PRECISION, SPECIFIER, AFTER }; ++CJS_Util::DataType CJS_Util::ParseDataType(WideString* sFormat) { ++ enum State { kBefore, kFlags, kWidth, kPrecision, kSpecifier, kAfter }; + +- int result = -1; +- State state = BEFORE; ++ DataType result = DataType::kInvalid; ++ State state = kBefore; + size_t precision_digits = 0; + size_t i = 0; + while (i < sFormat->GetLength()) { + wchar_t c = (*sFormat)[i]; + switch (state) { +- case BEFORE: ++ case kBefore: + if (c == L'%') +- state = FLAGS; ++ state = kFlags; + break; +- case FLAGS: ++ case kFlags: + if (c == L'+' || c == L'-' || c == L'#' || c == L' ') { + // Stay in same state. + } else { +- state = WIDTH; ++ state = kWidth; + continue; // Re-process same character. + } + break; +- case WIDTH: ++ case kWidth: + if (c == L'*') +- return -1; ++ return DataType::kInvalid; + if (FXSYS_IsDecimalDigit(c)) { + // Stay in same state. + } else if (c == L'.') { +- state = PRECISION; ++ state = kPrecision; + } else { +- state = SPECIFIER; ++ state = kSpecifier; + continue; // Re-process same character. + } + break; +- case PRECISION: ++ case kPrecision: + if (c == L'*') +- return -1; ++ return DataType::kInvalid; + if (FXSYS_IsDecimalDigit(c)) { + // Stay in same state. + ++precision_digits; + } else { +- state = SPECIFIER; ++ state = kSpecifier; + continue; // Re-process same character. + } + break; +- case SPECIFIER: ++ case kSpecifier: + if (c == L'c' || c == L'C' || c == L'd' || c == L'i' || c == L'o' || + c == L'u' || c == L'x' || c == L'X') { +- result = UTIL_INT; ++ result = DataType::kInt; + } else if (c == L'e' || c == L'E' || c == L'f' || c == L'g' || + c == L'G') { +- result = UTIL_DOUBLE; ++ result = DataType::kDouble; + } else if (c == L's' || c == L'S') { + // Map s to S since we always deal internally with wchar_t strings. + // TODO(tsepez): Probably 100% borked. %S is not a standard + // conversion. + sFormat->SetAt(i, L'S'); +- result = UTIL_STRING; ++ result = DataType::kString; + } else { +- return -1; ++ return DataType::kInvalid; + } +- state = AFTER; ++ state = kAfter; + break; +- case AFTER: ++ case kAfter: + if (c == L'%') +- return -1; ++ return DataType::kInvalid; + // Stay in same state until string exhausted. + break; + } + ++i; + } + // See https://crbug.com/740166 +- if (result == UTIL_INT && precision_digits > 2) +- return -1; ++ if (result == DataType::kInt && precision_digits > 2) ++ return DataType::kInvalid; + + return result; + } +diff --git a/fxjs/cjs_util.h b/fxjs/cjs_util.h +index 6f55b3a5a..45e58e067 100644 +--- a/fxjs/cjs_util.h ++++ b/fxjs/cjs_util.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,15 +12,18 @@ + #include "core/fxcrt/widestring.h" + #include "fxjs/cjs_object.h" + #include "fxjs/js_define.h" +- +-// Return values for ParseDataType() below. +-#define UTIL_INT 0 +-#define UTIL_DOUBLE 1 +-#define UTIL_STRING 2 ++#include "v8/include/v8-forward.h" + + class CJS_Util final : public CJS_Object { + public: +- static int GetObjDefnID(); ++ enum class DataType { ++ kInvalid = -1, ++ kInt = 0, ++ kDouble = 1, ++ kString = 2, ++ }; ++ ++ static uint32_t GetObjDefnID(); + static void DefineJSObjects(CFXJS_Engine* pEngine); + + CJS_Util(v8::Local pObject, CJS_Runtime* pRuntime); +@@ -33,7 +36,7 @@ class CJS_Util final : public CJS_Object { + // byte-by-byte. + // + // Exposed for testing. +- static int ParseDataType(WideString* sFormat); ++ static DataType ParseDataType(WideString* sFormat); + + // Exposed for testing. + static WideString StringPrintx(const WideString& cFormat, +@@ -46,7 +49,7 @@ class CJS_Util final : public CJS_Object { + JS_STATIC_METHOD(byteToChar, CJS_Util) + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const char kName[]; + static const JSMethodSpec MethodSpecs[]; + +diff --git a/fxjs/cjs_util_unittest.cpp b/fxjs/cjs_util_unittest.cpp +index d405746a7..b9252684d 100644 +--- a/fxjs/cjs_util_unittest.cpp ++++ b/fxjs/cjs_util_unittest.cpp +@@ -1,110 +1,112 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fxjs/cjs_util.h" + ++#include ++ + #include "testing/gtest/include/gtest/gtest.h" + + TEST(CJS_Util, ParseDataType) { + struct ParseDataTypeCase { + const wchar_t* const input_string; +- const int expected; ++ const CJS_Util::DataType expected; + }; + + // Commented out tests follow the spec but are not passing. + const ParseDataTypeCase cases[] = { + // Not conversions +- {L"", -1}, +- {L"d", -1}, ++ {L"", CJS_Util::DataType::kInvalid}, ++ {L"d", CJS_Util::DataType::kInvalid}, + + // Simple cases +- {L"%d", UTIL_INT}, +- {L"%x", UTIL_INT}, +- {L"%f", UTIL_DOUBLE}, +- {L"%s", UTIL_STRING}, ++ {L"%d", CJS_Util::DataType::kInt}, ++ {L"%x", CJS_Util::DataType::kInt}, ++ {L"%f", CJS_Util::DataType::kDouble}, ++ {L"%s", CJS_Util::DataType::kString}, + + // nDecSep Not implemented +- // {L"%,0d", UTIL_INT}, +- // {L"%,1d", UTIL_INT}, +- // {L"%,2d", UTIL_INT}, +- // {L"%,3d", UTIL_INT}, ++ // {L"%,0d", CJS_Util::DataType::kInt}, ++ // {L"%,1d", CJS_Util::DataType::kInt}, ++ // {L"%,2d", CJS_Util::DataType::kInt}, ++ // {L"%,3d", CJS_Util::DataType::kInt}, + // {L"%,4d", -1}, + // {L"%,d", -1}, + + // cFlags("+ 0#"") are only valid for numeric conversions. +- {L"%+d", UTIL_INT}, +- {L"%+x", UTIL_INT}, +- {L"%+f", UTIL_DOUBLE}, ++ {L"%+d", CJS_Util::DataType::kInt}, ++ {L"%+x", CJS_Util::DataType::kInt}, ++ {L"%+f", CJS_Util::DataType::kDouble}, + // {L"%+s", -1}, +- {L"% d", UTIL_INT}, +- {L"% x", UTIL_INT}, +- {L"% f", UTIL_DOUBLE}, ++ {L"% d", CJS_Util::DataType::kInt}, ++ {L"% x", CJS_Util::DataType::kInt}, ++ {L"% f", CJS_Util::DataType::kDouble}, + // {L"% s", -1}, +- {L"%0d", UTIL_INT}, +- {L"%0x", UTIL_INT}, +- {L"%0f", UTIL_DOUBLE}, ++ {L"%0d", CJS_Util::DataType::kInt}, ++ {L"%0x", CJS_Util::DataType::kInt}, ++ {L"%0f", CJS_Util::DataType::kDouble}, + // {L"%0s", -1}, +- {L"%#d", UTIL_INT}, +- {L"%#x", UTIL_INT}, +- {L"%#f", UTIL_DOUBLE}, ++ {L"%#d", CJS_Util::DataType::kInt}, ++ {L"%#x", CJS_Util::DataType::kInt}, ++ {L"%#f", CJS_Util::DataType::kDouble}, + // {L"%#s", -1}, + + // nWidth should work. for all conversions, can be combined with cFlags=0 + // for numbers. +- {L"%5d", UTIL_INT}, +- {L"%05d", UTIL_INT}, +- {L"%5x", UTIL_INT}, +- {L"%05x", UTIL_INT}, +- {L"%5f", UTIL_DOUBLE}, +- {L"%05f", UTIL_DOUBLE}, +- {L"%5s", UTIL_STRING}, ++ {L"%5d", CJS_Util::DataType::kInt}, ++ {L"%05d", CJS_Util::DataType::kInt}, ++ {L"%5x", CJS_Util::DataType::kInt}, ++ {L"%05x", CJS_Util::DataType::kInt}, ++ {L"%5f", CJS_Util::DataType::kDouble}, ++ {L"%05f", CJS_Util::DataType::kDouble}, ++ {L"%5s", CJS_Util::DataType::kString}, + // {L"%05s", -1}, + + // nPrecision should only work for float + // {L"%.5d", -1}, + // {L"%.5x", -1}, +- {L"%.5f", UTIL_DOUBLE}, ++ {L"%.5f", CJS_Util::DataType::kDouble}, + // {L"%.5s", -1}, + // {L"%.14d", -1}, + // {L"%.14x", -1}, +- {L"%.14f", UTIL_DOUBLE}, ++ {L"%.14f", CJS_Util::DataType::kDouble}, + // {L"%.14s", -1}, + // {L"%.f", -1}, + + // See https://crbug.com/740166 + // nPrecision too large (> 260) causes crashes in Windows. + // Avoid this by limiting to two digits +- {L"%.1d", UTIL_INT}, +- {L"%.10d", UTIL_INT}, +- {L"%.100d", -1}, ++ {L"%.1d", CJS_Util::DataType::kInt}, ++ {L"%.10d", CJS_Util::DataType::kInt}, ++ {L"%.100d", CJS_Util::DataType::kInvalid}, + + // Unexpected characters +- {L"%ad", -1}, +- {L"%bx", -1}, +- // {L"%cf", -1}, +- // {L"%es", -1}, +- // {L"%gd", -1}, +- {L"%hx", -1}, +- // {L"%if", -1}, +- {L"%js", -1}, +- {L"%@d", -1}, +- {L"%~x", -1}, +- {L"%[f", -1}, +- {L"%\0s", -1}, +- {L"%\nd", -1}, +- {L"%\rx", -1}, +- // {L"%%f", -1}, +- // {L"% s", -1}, ++ {L"%ad", CJS_Util::DataType::kInvalid}, ++ {L"%bx", CJS_Util::DataType::kInvalid}, ++ // {L"%cf", CJS_Util::DataType::kInvalid}, ++ // {L"%es", CJS_Util::DataType::kInvalid}, ++ // {L"%gd", CJS_Util::DataType::kInvalid}, ++ {L"%hx", CJS_Util::DataType::kInvalid}, ++ // {L"%if", CJS_Util::DataType::kInvalid}, ++ {L"%js", CJS_Util::DataType::kInvalid}, ++ {L"%@d", CJS_Util::DataType::kInvalid}, ++ {L"%~x", CJS_Util::DataType::kInvalid}, ++ {L"%[f", CJS_Util::DataType::kInvalid}, ++ {L"%\0s", CJS_Util::DataType::kInvalid}, ++ {L"%\nd", CJS_Util::DataType::kInvalid}, ++ {L"%\rx", CJS_Util::DataType::kInvalid}, ++ // {L"%%f", CJS_Util::DataType::kInvalid}, ++ // {L"% s", CJS_Util::DataType::kInvalid}, + + // Combine multiple valid components +- {L"%+6d", UTIL_INT}, +- {L"% 7x", UTIL_INT}, +- {L"%#9.3f", UTIL_DOUBLE}, +- {L"%10s", UTIL_STRING}, ++ {L"%+6d", CJS_Util::DataType::kInt}, ++ {L"% 7x", CJS_Util::DataType::kInt}, ++ {L"%#9.3f", CJS_Util::DataType::kDouble}, ++ {L"%10s", CJS_Util::DataType::kString}, + }; + +- for (size_t i = 0; i < FX_ArraySize(cases); i++) { ++ for (size_t i = 0; i < std::size(cases); i++) { + WideString input(cases[i].input_string); + EXPECT_EQ(cases[i].expected, CJS_Util::ParseDataType(&input)) + << cases[i].input_string; +diff --git a/fxjs/cjs_zoomtype.cpp b/fxjs/cjs_zoomtype.cpp +index cdaa2d5cf..b978468f6 100644 +--- a/fxjs/cjs_zoomtype.cpp ++++ b/fxjs/cjs_zoomtype.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -15,7 +15,7 @@ const JSConstSpec CJS_Zoomtype::ConstSpecs[] = { + {"pref", JSConstSpec::String, 0, "Preferred"}, + {"refW", JSConstSpec::String, 0, "ReflowWidth"}}; + +-int CJS_Zoomtype::ObjDefnID = -1; ++uint32_t CJS_Zoomtype::ObjDefnID = 0; + + // static + void CJS_Zoomtype::DefineJSObjects(CFXJS_Engine* pEngine) { +diff --git a/fxjs/cjs_zoomtype.h b/fxjs/cjs_zoomtype.h +index de268cd71..2efd49ac9 100644 +--- a/fxjs/cjs_zoomtype.h ++++ b/fxjs/cjs_zoomtype.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,7 +16,7 @@ class CJS_Zoomtype final : public CJS_Object { + CJS_Zoomtype() = delete; + + private: +- static int ObjDefnID; ++ static uint32_t ObjDefnID; + static const JSConstSpec ConstSpecs[]; + }; + +diff --git a/fxjs/fx_date_helpers.cpp b/fxjs/fx_date_helpers.cpp +index 5256fb1a5..3d7d1f386 100644 +--- a/fxjs/fx_date_helpers.cpp ++++ b/fxjs/fx_date_helpers.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,9 +6,11 @@ + + #include "fxjs/fx_date_helpers.h" + ++#include + #include ++#include + +-#include ++#include + + #include "build/build_config.h" + #include "core/fxcrt/fx_extension.h" +@@ -36,7 +38,7 @@ double GetLocalTZA() { + time_t t = 0; + FXSYS_time(&t); + FXSYS_localtime(&t); +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + // In gcc 'timezone' is a global variable declared in time.h. In VC++, that + // variable was removed in VC++ 2015, with _get_timezone replacing it. + long timezone = 0; +@@ -113,9 +115,9 @@ int MonthFromTime(double t) { + // Check for February onwards. + static constexpr int kCumulativeDaysInMonths[] = { + 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; +- for (size_t i = 0; i < FX_ArraySize(kCumulativeDaysInMonths); ++i) { ++ for (size_t i = 0; i < std::size(kCumulativeDaysInMonths); ++i) { + if (day < kCumulativeDaysInMonths[i]) +- return i + 1; ++ return static_cast(i) + 1; + } + + return -1; +@@ -159,7 +161,7 @@ int DateFromTime(double t) { + size_t FindSubWordLength(const WideString& str, size_t nStart) { + pdfium::span data = str.span(); + size_t i = nStart; +- while (i < data.size() && std::iswalnum(data[i])) ++ while (i < data.size() && iswalnum(data[i])) + ++i; + return i - nStart; + } +@@ -247,7 +249,7 @@ double FX_MakeDay(int nYear, int nMonth, int nDate) { + double mn = Mod(m, 12); + double t = TimeFromYearMonth(static_cast(ym), static_cast(mn)); + if (YearFromTime(t) != ym || MonthFromTime(t) != mn || DateFromTime(t) != 1) +- return std::nan(""); ++ return nan(""); + + return Day(t) + dt - 1; + } +@@ -261,8 +263,8 @@ double FX_MakeTime(int nHour, int nMin, int nSec, int nMs) { + } + + double FX_MakeDate(double day, double time) { +- if (!std::isfinite(day) || !std::isfinite(time)) +- return std::nan(""); ++ if (!isfinite(day) || !isfinite(time)) ++ return nan(""); + + return day * 86400000 + time; + } +@@ -433,9 +435,9 @@ ConversionStatus FX_ParseDateUsingFormat(const WideString& value, + nSkip = FindSubWordLength(value, j); + if (nSkip == KMonthAbbreviationLength) { + WideString sMonth = value.Substr(j, KMonthAbbreviationLength); +- for (size_t m = 0; m < FX_ArraySize(kMonths); ++m) { ++ for (size_t m = 0; m < std::size(kMonths); ++m) { + if (sMonth.CompareNoCase(kMonths[m]) == 0) { +- nMonth = m + 1; ++ nMonth = static_cast(m) + 1; + i += 3; + j += nSkip; + bFind = true; +@@ -470,11 +472,11 @@ ConversionStatus FX_ParseDateUsingFormat(const WideString& value, + if (nSkip <= kLongestFullMonthLength) { + WideString sMonth = value.Substr(j, nSkip); + sMonth.MakeLower(); +- for (size_t m = 0; m < FX_ArraySize(kFullMonths); ++m) { ++ for (size_t m = 0; m < std::size(kFullMonths); ++m) { + WideString sFullMonths = WideString(kFullMonths[m]); + sFullMonths.MakeLower(); +- if (sFullMonths.Contains(sMonth.c_str())) { +- nMonth = m + 1; ++ if (sFullMonths.Contains(sMonth.AsStringView())) { ++ nMonth = static_cast(m) + 1; + i += 4; + j += nSkip; + bFind = true; +@@ -482,7 +484,6 @@ ConversionStatus FX_ParseDateUsingFormat(const WideString& value, + } + } + } +- + if (!bFind) { + nMonth = FX_ParseStringInteger(value, j, &nSkip, 4); + i += 4; +@@ -541,7 +542,7 @@ ConversionStatus FX_ParseDateUsingFormat(const WideString& value, + + dt = FX_MakeDate(FX_MakeDay(nYear, nMonth - 1, nDay), + FX_MakeTime(nHour, nMin, nSec, 0)); +- if (std::isnan(dt)) ++ if (isnan(dt)) + return ConversionStatus::kBadDate; + + *result = dt; +diff --git a/fxjs/fx_date_helpers.h b/fxjs/fx_date_helpers.h +index 365769483..6f6f4039f 100644 +--- a/fxjs/fx_date_helpers.h ++++ b/fxjs/fx_date_helpers.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,7 +9,7 @@ + + #include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" + + namespace fxjs { + +diff --git a/fxjs/fx_date_helpers_unittest.cpp b/fxjs/fx_date_helpers_unittest.cpp +index fb756b9f7..cc95719bc 100644 +--- a/fxjs/fx_date_helpers_unittest.cpp ++++ b/fxjs/fx_date_helpers_unittest.cpp +@@ -1,9 +1,10 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fxjs/fx_date_helpers.h" + ++#include "core/fxcrt/fake_time_test.h" + #include "testing/gtest/include/gtest/gtest.h" + + namespace { +@@ -12,6 +13,8 @@ constexpr double kMilliSecondsInADay = 1000 * 60 * 60 * 24; + + } // namespace + ++using fxjs::ConversionStatus; ++ + TEST(FX_DateHelper, GetYearFromTime) { + static constexpr struct { + double time_ms; +@@ -106,3 +109,47 @@ TEST(FX_DateHelper, GetMonthFromTime) { + << test.time_ms; + } + } ++ ++using FXDateHelperFakeTimeTest = FakeTimeTest; ++ ++TEST_F(FXDateHelperFakeTimeTest, ParseDateUsingFormatWithEmptyParams) { ++ double result = 0.0; ++ EXPECT_EQ(ConversionStatus::kSuccess, ++ FX_ParseDateUsingFormat(L"", L"", &result)); ++ EXPECT_DOUBLE_EQ(1'587'654'321'000, result); ++ EXPECT_EQ(ConversionStatus::kSuccess, ++ FX_ParseDateUsingFormat(L"value", L"", &result)); ++ EXPECT_DOUBLE_EQ(1'587'654'321'000, result); ++ EXPECT_EQ(ConversionStatus::kSuccess, ++ FX_ParseDateUsingFormat(L"", L"format", &result)); ++ EXPECT_DOUBLE_EQ(1'587'654'321'000, result); ++} ++ ++TEST_F(FXDateHelperFakeTimeTest, ParseDateUsingFormatForValidMonthDay) { ++ double result = 0.0; ++ EXPECT_EQ(ConversionStatus::kSuccess, ++ FX_ParseDateUsingFormat(L"01/02/2000", L"mm/dd/yyyy", &result)); ++ EXPECT_DOUBLE_EQ(946'825'521'000, result); ++ EXPECT_EQ(ConversionStatus::kSuccess, ++ FX_ParseDateUsingFormat(L"1/2/2000", L"m/d/yyyy", &result)); ++ EXPECT_DOUBLE_EQ(946'825'521'000, result); ++ EXPECT_EQ(ConversionStatus::kSuccess, ++ FX_ParseDateUsingFormat(L"1-2-2000", L"m-d-yyyy", &result)); ++ EXPECT_DOUBLE_EQ(946'825'521'000, result); ++ EXPECT_EQ(ConversionStatus::kSuccess, ++ FX_ParseDateUsingFormat(L"2-1-2000", L"d-m-yyyy", &result)); ++ EXPECT_DOUBLE_EQ(946'825'521'000, result); ++ ++ EXPECT_EQ(ConversionStatus::kSuccess, ++ FX_ParseDateUsingFormat(L"11/12/2000", L"mm/dd/yyyy", &result)); ++ EXPECT_DOUBLE_EQ(973'955'121'000, result); ++ EXPECT_EQ(ConversionStatus::kSuccess, ++ FX_ParseDateUsingFormat(L"11/12/2000", L"m/d/yyyy", &result)); ++ EXPECT_DOUBLE_EQ(973'955'121'000, result); ++ EXPECT_EQ(ConversionStatus::kSuccess, ++ FX_ParseDateUsingFormat(L"11-12-2000", L"m-d-yyyy", &result)); ++ EXPECT_DOUBLE_EQ(973'955'121'000, result); ++ EXPECT_EQ(ConversionStatus::kSuccess, ++ FX_ParseDateUsingFormat(L"12-11-2000", L"d-m-yyyy", &result)); ++ EXPECT_DOUBLE_EQ(973'955'121'000, result); ++} +diff --git a/fxjs/fxv8.cpp b/fxjs/fxv8.cpp +new file mode 100644 +index 000000000..bbf1ec64f +--- /dev/null ++++ b/fxjs/fxv8.cpp +@@ -0,0 +1,337 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "fxjs/fxv8.h" ++ ++#include "third_party/base/numerics/safe_conversions.h" ++#include "v8/include/v8-container.h" ++#include "v8/include/v8-date.h" ++#include "v8/include/v8-exception.h" ++#include "v8/include/v8-isolate.h" ++#include "v8/include/v8-primitive.h" ++#include "v8/include/v8-value.h" ++ ++namespace fxv8 { ++ ++bool IsUndefined(v8::Local value) { ++ return !value.IsEmpty() && value->IsUndefined(); ++} ++ ++bool IsNull(v8::Local value) { ++ return !value.IsEmpty() && value->IsNull(); ++} ++ ++bool IsBoolean(v8::Local value) { ++ return !value.IsEmpty() && value->IsBoolean(); ++} ++ ++bool IsString(v8::Local value) { ++ return !value.IsEmpty() && value->IsString(); ++} ++ ++bool IsNumber(v8::Local value) { ++ return !value.IsEmpty() && value->IsNumber(); ++} ++ ++bool IsInteger(v8::Local value) { ++ return !value.IsEmpty() && value->IsInt32(); ++} ++ ++bool IsObject(v8::Local value) { ++ return !value.IsEmpty() && value->IsObject(); ++} ++ ++bool IsArray(v8::Local value) { ++ return !value.IsEmpty() && value->IsArray(); ++} ++ ++bool IsDate(v8::Local value) { ++ return !value.IsEmpty() && value->IsDate(); ++} ++ ++bool IsFunction(v8::Local value) { ++ return !value.IsEmpty() && value->IsFunction(); ++} ++ ++v8::Local NewNullHelper(v8::Isolate* pIsolate) { ++ return v8::Null(pIsolate); ++} ++ ++v8::Local NewUndefinedHelper(v8::Isolate* pIsolate) { ++ return v8::Undefined(pIsolate); ++} ++ ++v8::Local NewNumberHelper(v8::Isolate* pIsolate, int number) { ++ return v8::Int32::New(pIsolate, number); ++} ++ ++v8::Local NewNumberHelper(v8::Isolate* pIsolate, double number) { ++ return v8::Number::New(pIsolate, number); ++} ++ ++v8::Local NewNumberHelper(v8::Isolate* pIsolate, float number) { ++ return v8::Number::New(pIsolate, number); ++} ++ ++v8::Local NewBooleanHelper(v8::Isolate* pIsolate, bool b) { ++ return v8::Boolean::New(pIsolate, b); ++} ++ ++v8::Local NewStringHelper(v8::Isolate* pIsolate, ++ ByteStringView str) { ++ return v8::String::NewFromUtf8( ++ pIsolate, str.unterminated_c_str(), v8::NewStringType::kNormal, ++ pdfium::base::checked_cast(str.GetLength())) ++ .ToLocalChecked(); ++} ++ ++v8::Local NewStringHelper(v8::Isolate* pIsolate, ++ WideStringView str) { ++ return NewStringHelper(pIsolate, FX_UTF8Encode(str).AsStringView()); ++} ++ ++v8::Local NewArrayHelper(v8::Isolate* pIsolate) { ++ return v8::Array::New(pIsolate); ++} ++ ++v8::Local NewArrayHelper(v8::Isolate* pIsolate, ++ pdfium::span> values) { ++ v8::Local result = NewArrayHelper(pIsolate); ++ for (size_t i = 0; i < values.size(); ++i) { ++ fxv8::ReentrantPutArrayElementHelper( ++ pIsolate, result, i, ++ values[i].IsEmpty() ? fxv8::NewUndefinedHelper(pIsolate) : values[i]); ++ } ++ return result; ++} ++ ++v8::Local NewObjectHelper(v8::Isolate* pIsolate) { ++ return v8::Object::New(pIsolate); ++} ++ ++v8::Local NewDateHelper(v8::Isolate* pIsolate, double d) { ++ return v8::Date::New(pIsolate->GetCurrentContext(), d) ++ .ToLocalChecked() ++ .As(); ++} ++ ++WideString ToWideString(v8::Isolate* pIsolate, v8::Local pValue) { ++ v8::String::Utf8Value s(pIsolate, pValue); ++ return WideString::FromUTF8(ByteStringView(*s, s.length())); ++} ++ ++ByteString ToByteString(v8::Isolate* pIsolate, v8::Local pValue) { ++ v8::String::Utf8Value s(pIsolate, pValue); ++ return ByteString(*s, s.length()); ++} ++ ++int ReentrantToInt32Helper(v8::Isolate* pIsolate, v8::Local pValue) { ++ if (pValue.IsEmpty()) ++ return 0; ++ v8::TryCatch squash_exceptions(pIsolate); ++ return pValue->Int32Value(pIsolate->GetCurrentContext()).FromMaybe(0); ++} ++ ++bool ReentrantToBooleanHelper(v8::Isolate* pIsolate, ++ v8::Local pValue) { ++ if (pValue.IsEmpty()) ++ return false; ++ v8::TryCatch squash_exceptions(pIsolate); ++ return pValue->BooleanValue(pIsolate); ++} ++ ++float ReentrantToFloatHelper(v8::Isolate* pIsolate, ++ v8::Local pValue) { ++ return static_cast(ReentrantToDoubleHelper(pIsolate, pValue)); ++} ++ ++double ReentrantToDoubleHelper(v8::Isolate* pIsolate, ++ v8::Local pValue) { ++ if (pValue.IsEmpty()) ++ return 0.0; ++ v8::TryCatch squash_exceptions(pIsolate); ++ return pValue->NumberValue(pIsolate->GetCurrentContext()).FromMaybe(0.0); ++} ++ ++WideString ReentrantToWideStringHelper(v8::Isolate* pIsolate, ++ v8::Local pValue) { ++ if (pValue.IsEmpty()) ++ return WideString(); ++ ++ v8::TryCatch squash_exceptions(pIsolate); ++ v8::MaybeLocal maybe_string = ++ pValue->ToString(pIsolate->GetCurrentContext()); ++ if (maybe_string.IsEmpty()) ++ return WideString(); ++ ++ return ToWideString(pIsolate, maybe_string.ToLocalChecked()); ++} ++ ++ByteString ReentrantToByteStringHelper(v8::Isolate* pIsolate, ++ v8::Local pValue) { ++ if (pValue.IsEmpty()) ++ return ByteString(); ++ ++ v8::TryCatch squash_exceptions(pIsolate); ++ v8::MaybeLocal maybe_string = ++ pValue->ToString(pIsolate->GetCurrentContext()); ++ if (maybe_string.IsEmpty()) ++ return ByteString(); ++ ++ return ToByteString(pIsolate, maybe_string.ToLocalChecked()); ++} ++ ++v8::Local ReentrantToObjectHelper(v8::Isolate* pIsolate, ++ v8::Local pValue) { ++ if (!fxv8::IsObject(pValue)) ++ return v8::Local(); ++ ++ v8::TryCatch squash_exceptions(pIsolate); ++ v8::Local context = pIsolate->GetCurrentContext(); ++ return pValue->ToObject(context).ToLocalChecked(); ++} ++ ++v8::Local ReentrantToArrayHelper(v8::Isolate* pIsolate, ++ v8::Local pValue) { ++ if (!fxv8::IsArray(pValue)) ++ return v8::Local(); ++ ++ v8::TryCatch squash_exceptions(pIsolate); ++ v8::Local context = pIsolate->GetCurrentContext(); ++ return v8::Local::Cast(pValue->ToObject(context).ToLocalChecked()); ++} ++ ++v8::Local ReentrantGetObjectPropertyHelper( ++ v8::Isolate* pIsolate, ++ v8::Local pObj, ++ ByteStringView bsUTF8PropertyName) { ++ if (pObj.IsEmpty()) ++ return v8::Local(); ++ ++ v8::TryCatch squash_exceptions(pIsolate); ++ v8::Local val; ++ if (!pObj->Get(pIsolate->GetCurrentContext(), ++ NewStringHelper(pIsolate, bsUTF8PropertyName)) ++ .ToLocal(&val)) { ++ return v8::Local(); ++ } ++ return val; ++} ++ ++std::vector ReentrantGetObjectPropertyNamesHelper( ++ v8::Isolate* pIsolate, ++ v8::Local pObj) { ++ if (pObj.IsEmpty()) ++ return std::vector(); ++ ++ v8::TryCatch squash_exceptions(pIsolate); ++ v8::Local val; ++ v8::Local context = pIsolate->GetCurrentContext(); ++ if (!pObj->GetPropertyNames(context).ToLocal(&val)) ++ return std::vector(); ++ ++ std::vector result; ++ for (uint32_t i = 0; i < val->Length(); ++i) { ++ result.push_back(ReentrantToWideStringHelper( ++ pIsolate, val->Get(context, i).ToLocalChecked())); ++ } ++ return result; ++} ++ ++bool ReentrantHasObjectOwnPropertyHelper(v8::Isolate* pIsolate, ++ v8::Local pObj, ++ ByteStringView bsUTF8PropertyName) { ++ if (pObj.IsEmpty()) ++ return false; ++ ++ v8::TryCatch squash_exceptions(pIsolate); ++ v8::Local pContext = pIsolate->GetCurrentContext(); ++ v8::Local hKey = ++ fxv8::NewStringHelper(pIsolate, bsUTF8PropertyName); ++ return pObj->HasRealNamedProperty(pContext, hKey).FromJust(); ++} ++ ++bool ReentrantSetObjectOwnPropertyHelper(v8::Isolate* pIsolate, ++ v8::Local pObj, ++ ByteStringView bsUTF8PropertyName, ++ v8::Local pValue) { ++ if (pObj.IsEmpty() || pValue.IsEmpty()) ++ return false; ++ ++ v8::TryCatch squash_exceptions(pIsolate); ++ v8::Local name = NewStringHelper(pIsolate, bsUTF8PropertyName); ++ return pObj->DefineOwnProperty(pIsolate->GetCurrentContext(), name, pValue) ++ .FromMaybe(false); ++} ++ ++bool ReentrantPutObjectPropertyHelper(v8::Isolate* pIsolate, ++ v8::Local pObj, ++ ByteStringView bsUTF8PropertyName, ++ v8::Local pPut) { ++ if (pObj.IsEmpty() || pPut.IsEmpty()) ++ return false; ++ ++ v8::TryCatch squash_exceptions(pIsolate); ++ v8::Local name = NewStringHelper(pIsolate, bsUTF8PropertyName); ++ v8::Maybe result = pObj->Set(pIsolate->GetCurrentContext(), name, pPut); ++ return result.IsJust() && result.FromJust(); ++} ++ ++void ReentrantDeleteObjectPropertyHelper(v8::Isolate* pIsolate, ++ v8::Local pObj, ++ ByteStringView bsUTF8PropertyName) { ++ v8::TryCatch squash_exceptions(pIsolate); ++ pObj->Delete(pIsolate->GetCurrentContext(), ++ fxv8::NewStringHelper(pIsolate, bsUTF8PropertyName)) ++ .FromJust(); ++} ++ ++bool ReentrantPutArrayElementHelper(v8::Isolate* pIsolate, ++ v8::Local pArray, ++ size_t index, ++ v8::Local pValue) { ++ if (pArray.IsEmpty()) ++ return false; ++ ++ v8::TryCatch squash_exceptions(pIsolate); ++ v8::Maybe result = ++ pArray->Set(pIsolate->GetCurrentContext(), ++ pdfium::base::checked_cast(index), pValue); ++ return result.IsJust() && result.FromJust(); ++} ++ ++v8::Local ReentrantGetArrayElementHelper(v8::Isolate* pIsolate, ++ v8::Local pArray, ++ size_t index) { ++ if (pArray.IsEmpty()) ++ return v8::Local(); ++ ++ v8::TryCatch squash_exceptions(pIsolate); ++ v8::Local val; ++ if (!pArray ++ ->Get(pIsolate->GetCurrentContext(), ++ pdfium::base::checked_cast(index)) ++ .ToLocal(&val)) { ++ return v8::Local(); ++ } ++ return val; ++} ++ ++size_t GetArrayLengthHelper(v8::Local pArray) { ++ if (pArray.IsEmpty()) ++ return 0; ++ return pArray->Length(); ++} ++ ++void ThrowExceptionHelper(v8::Isolate* pIsolate, ByteStringView str) { ++ pIsolate->ThrowException(NewStringHelper(pIsolate, str)); ++} ++ ++void ThrowExceptionHelper(v8::Isolate* pIsolate, WideStringView str) { ++ pIsolate->ThrowException(NewStringHelper(pIsolate, str)); ++} ++ ++} // namespace fxv8 +diff --git a/fxjs/fxv8.h b/fxjs/fxv8.h +new file mode 100644 +index 000000000..5ebd7f5cc +--- /dev/null ++++ b/fxjs/fxv8.h +@@ -0,0 +1,110 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef FXJS_FXV8_H_ ++#define FXJS_FXV8_H_ ++ ++#include ++ ++#include ++ ++#include "core/fxcrt/fx_string.h" ++#include "third_party/base/span.h" ++#include "v8/include/v8-forward.h" ++ ++// The fxv8 functions soften up the interface to the V8 API. In particular, ++// PDFium uses size_t for sizes and indices, but V8 mostly uses ints, so ++// these routines perform checked conversions. ++ ++namespace fxv8 { ++ ++// These first check for empty locals. ++bool IsUndefined(v8::Local value); ++bool IsNull(v8::Local value); ++bool IsBoolean(v8::Local value); ++bool IsString(v8::Local value); ++bool IsNumber(v8::Local value); ++bool IsInteger(v8::Local value); ++bool IsObject(v8::Local value); ++bool IsArray(v8::Local value); ++bool IsDate(v8::Local value); ++bool IsFunction(v8::Local value); ++ ++v8::Local NewNullHelper(v8::Isolate* pIsolate); ++v8::Local NewUndefinedHelper(v8::Isolate* pIsolate); ++v8::Local NewNumberHelper(v8::Isolate* pIsolate, int number); ++v8::Local NewNumberHelper(v8::Isolate* pIsolate, double number); ++v8::Local NewNumberHelper(v8::Isolate* pIsolate, float number); ++v8::Local NewBooleanHelper(v8::Isolate* pIsolate, bool b); ++v8::Local NewStringHelper(v8::Isolate* pIsolate, ++ ByteStringView str); ++v8::Local NewStringHelper(v8::Isolate* pIsolate, ++ WideStringView str); ++v8::Local NewArrayHelper(v8::Isolate* pIsolate); ++v8::Local NewArrayHelper(v8::Isolate* pIsolate, ++ pdfium::span> values); ++v8::Local NewObjectHelper(v8::Isolate* pIsolate); ++v8::Local NewDateHelper(v8::Isolate* pIsolate, double d); ++ ++// Conversion to PDFium type without re-entry from known v8 type. ++WideString ToWideString(v8::Isolate* pIsolate, v8::Local pValue); ++ByteString ToByteString(v8::Isolate* pIsolate, v8::Local pValue); ++ ++// Conversion to PDFium type with possible re-entry for coercion. ++int32_t ReentrantToInt32Helper(v8::Isolate* pIsolate, ++ v8::Local pValue); ++bool ReentrantToBooleanHelper(v8::Isolate* pIsolate, ++ v8::Local pValue); ++float ReentrantToFloatHelper(v8::Isolate* pIsolate, ++ v8::Local pValue); ++double ReentrantToDoubleHelper(v8::Isolate* pIsolate, ++ v8::Local pValue); ++WideString ReentrantToWideStringHelper(v8::Isolate* pIsolate, ++ v8::Local pValue); ++ByteString ReentrantToByteStringHelper(v8::Isolate* pIsolate, ++ v8::Local pValue); ++v8::Local ReentrantToObjectHelper(v8::Isolate* pIsolate, ++ v8::Local pValue); ++v8::Local ReentrantToArrayHelper(v8::Isolate* pIsolate, ++ v8::Local pValue); ++ ++v8::Local ReentrantGetObjectPropertyHelper( ++ v8::Isolate* pIsolate, ++ v8::Local pObj, ++ ByteStringView bsUTF8PropertyName); ++std::vector ReentrantGetObjectPropertyNamesHelper( ++ v8::Isolate* pIsolate, ++ v8::Local pObj); ++bool ReentrantHasObjectOwnPropertyHelper(v8::Isolate* pIsolate, ++ v8::Local pObj, ++ ByteStringView bsUTF8PropertyName); ++bool ReentrantSetObjectOwnPropertyHelper(v8::Isolate* pIsolate, ++ v8::Local pObj, ++ ByteStringView bsUTF8PropertyName, ++ v8::Local pValue); ++bool ReentrantPutObjectPropertyHelper(v8::Isolate* pIsolate, ++ v8::Local pObj, ++ ByteStringView bsUTF8PropertyName, ++ v8::Local pPut); ++void ReentrantDeleteObjectPropertyHelper(v8::Isolate* pIsolate, ++ v8::Local pObj, ++ ByteStringView bsUTF8PropertyName); ++ ++bool ReentrantPutArrayElementHelper(v8::Isolate* pIsolate, ++ v8::Local pArray, ++ size_t index, ++ v8::Local pValue); ++v8::Local ReentrantGetArrayElementHelper(v8::Isolate* pIsolate, ++ v8::Local pArray, ++ size_t index); ++size_t GetArrayLengthHelper(v8::Local pArray); ++ ++void ThrowExceptionHelper(v8::Isolate* pIsolate, ByteStringView str); ++void ThrowExceptionHelper(v8::Isolate* pIsolate, WideStringView str); ++ ++} // namespace fxv8 ++ ++#endif // FXJS_FXV8_H_ +diff --git a/fxjs/gc/container_trace.h b/fxjs/gc/container_trace.h +new file mode 100644 +index 000000000..dfe4a7465 +--- /dev/null ++++ b/fxjs/gc/container_trace.h +@@ -0,0 +1,66 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef FXJS_GC_CONTAINER_TRACE_H_ ++#define FXJS_GC_CONTAINER_TRACE_H_ ++ ++#include ++#include ++#include ++#include ++ ++#include "v8/include/cppgc/member.h" ++#include "v8/include/cppgc/visitor.h" ++ ++namespace fxgc { ++ ++template ++void ContainerTrace(V* visitor, const std::list>& container) { ++ for (const auto& item : container) ++ visitor->Trace(item); ++} ++ ++template ++void ContainerTrace(V* visitor, ++ const std::map, U>& container) { ++ for (const auto& item : container) { ++ visitor->Trace(item.first); ++ } ++} ++ ++template ++void ContainerTrace(V* visitor, ++ const std::map>& container) { ++ for (const auto& item : container) ++ visitor->Trace(item.second); ++} ++ ++template ++void ContainerTrace( ++ V* visitor, ++ const std::map, cppgc::Member>& container) { ++ for (const auto& item : container) { ++ visitor->Trace(item.first); ++ visitor->Trace(item.second); ++ } ++} ++ ++template ++void ContainerTrace(V* visitor, const std::set>& container) { ++ for (const auto& item : container) ++ visitor->Trace(item); ++} ++ ++template ++void ContainerTrace(V* visitor, ++ const std::vector>& container) { ++ for (const auto& item : container) ++ visitor->Trace(item); ++} ++ ++} // namespace fxgc ++ ++using fxgc::ContainerTrace; ++ ++#endif // FXJS_GC_CONTAINER_TRACE_H_ +diff --git a/fxjs/gc/container_trace_unittest.cpp b/fxjs/gc/container_trace_unittest.cpp +new file mode 100644 +index 000000000..9273afe5c +--- /dev/null ++++ b/fxjs/gc/container_trace_unittest.cpp +@@ -0,0 +1,89 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "fxjs/gc/container_trace.h" ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "testing/gtest/include/gtest/gtest.h" ++#include "v8/include/cppgc/member.h" ++ ++namespace { ++ ++class Thing : public cppgc::GarbageCollected { ++ public: ++ void Trace(cppgc::Visitor* visitor) const {} ++}; ++ ++class CountingVisitor { ++ public: ++ CountingVisitor() = default; ++ ++ void Trace(const void* that) { ++call_count_; } ++ int call_count() const { return call_count_; } ++ ++ private: ++ int call_count_ = 0; ++}; ++ ++} // namespace ++ ++TEST(ContainerTrace, ActualListTrace) { ++ std::list> thing; ++ thing.emplace_back(nullptr); ++ ++ CountingVisitor cv; ++ ContainerTrace(&cv, thing); ++ EXPECT_EQ(1, cv.call_count()); ++} ++ ++TEST(ContainerTrace, ActualMapTraceFirst) { ++ std::map, int> thing; ++ thing[nullptr] = 42; ++ ++ CountingVisitor cv; ++ ContainerTrace(&cv, thing); ++ EXPECT_EQ(1, cv.call_count()); ++} ++ ++TEST(ContainerTrace, ActualMapTraceSecond) { ++ std::map> thing; ++ thing[42] = nullptr; ++ ++ CountingVisitor cv; ++ ContainerTrace(&cv, thing); ++ EXPECT_EQ(1, cv.call_count()); ++} ++ ++TEST(ContainerTrace, ActualMapTraceBoth) { ++ std::map, cppgc::Member> thing; ++ thing[nullptr] = nullptr; ++ ++ CountingVisitor cv; ++ ContainerTrace(&cv, thing); ++ EXPECT_EQ(2, cv.call_count()); ++} ++ ++TEST(ContainerTrace, ActualSetTrace) { ++ std::set> thing; ++ thing.insert(nullptr); ++ ++ CountingVisitor cv; ++ ContainerTrace(&cv, thing); ++ EXPECT_EQ(1, cv.call_count()); ++} ++ ++TEST(ContainerTrace, ActualVectorTrace) { ++ std::vector> thing; ++ thing.emplace_back(nullptr); ++ ++ CountingVisitor cv; ++ ContainerTrace(&cv, thing); ++ EXPECT_EQ(1, cv.call_count()); ++} +diff --git a/fxjs/gc/gced_tree_node.h b/fxjs/gc/gced_tree_node.h +new file mode 100644 +index 000000000..fed5e73ac +--- /dev/null ++++ b/fxjs/gc/gced_tree_node.h +@@ -0,0 +1,47 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef FXJS_GC_GCED_TREE_NODE_H_ ++#define FXJS_GC_GCED_TREE_NODE_H_ ++ ++#include "core/fxcrt/tree_node.h" ++#include "v8/include/cppgc/garbage-collected.h" ++#include "v8/include/cppgc/member.h" ++#include "v8/include/cppgc/visitor.h" ++ ++namespace fxjs { ++ ++// For DOM/XML-ish trees, where references outside the tree are Persistent<>. ++template ++class GCedTreeNode : public cppgc::GarbageCollected>, ++ public fxcrt::TreeNodeBase { ++ public: ++ virtual void Trace(cppgc::Visitor* visitor) const { ++ visitor->Trace(m_pParent); ++ visitor->Trace(m_pFirstChild); ++ visitor->Trace(m_pLastChild); ++ visitor->Trace(m_pNextSibling); ++ visitor->Trace(m_pPrevSibling); ++ } ++ ++ protected: ++ GCedTreeNode() = default; ++ GCedTreeNode(const GCedTreeNode& that) = delete; ++ GCedTreeNode& operator=(const GCedTreeNode& that) = delete; ++ ++ private: ++ friend class fxcrt::TreeNodeBase; ++ ++ cppgc::Member m_pParent; ++ cppgc::Member m_pFirstChild; ++ cppgc::Member m_pLastChild; ++ cppgc::Member m_pNextSibling; ++ cppgc::Member m_pPrevSibling; ++}; ++ ++} // namespace fxjs ++ ++using fxjs::GCedTreeNode; ++ ++#endif // FXJS_GC_GCED_TREE_NODE_H_ +diff --git a/fxjs/gc/gced_tree_node_mixin.h b/fxjs/gc/gced_tree_node_mixin.h +new file mode 100644 +index 000000000..2f160e06d +--- /dev/null ++++ b/fxjs/gc/gced_tree_node_mixin.h +@@ -0,0 +1,48 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef FXJS_GC_GCED_TREE_NODE_MIXIN_H_ ++#define FXJS_GC_GCED_TREE_NODE_MIXIN_H_ ++ ++#include "core/fxcrt/tree_node.h" ++#include "v8/include/cppgc/garbage-collected.h" ++#include "v8/include/cppgc/member.h" ++#include "v8/include/cppgc/visitor.h" ++ ++namespace fxjs { ++ ++// For DOM/XML-ish trees, where references outside the tree are Persistent<>, ++// usable by classes that are already garbage collected themselves. ++template ++class GCedTreeNodeMixin : public cppgc::GarbageCollectedMixin, ++ public fxcrt::TreeNodeBase { ++ public: ++ virtual void Trace(cppgc::Visitor* visitor) const { ++ visitor->Trace(m_pParent); ++ visitor->Trace(m_pFirstChild); ++ visitor->Trace(m_pLastChild); ++ visitor->Trace(m_pNextSibling); ++ visitor->Trace(m_pPrevSibling); ++ } ++ ++ protected: ++ GCedTreeNodeMixin() = default; ++ GCedTreeNodeMixin(const GCedTreeNodeMixin& that) = delete; ++ GCedTreeNodeMixin& operator=(const GCedTreeNodeMixin& that) = delete; ++ ++ private: ++ friend class fxcrt::TreeNodeBase; ++ ++ cppgc::Member m_pParent; ++ cppgc::Member m_pFirstChild; ++ cppgc::Member m_pLastChild; ++ cppgc::Member m_pNextSibling; ++ cppgc::Member m_pPrevSibling; ++}; ++ ++} // namespace fxjs ++ ++using fxjs::GCedTreeNodeMixin; ++ ++#endif // FXJS_GC_GCED_TREE_NODE_MIXIN_H_ +diff --git a/fxjs/gc/gced_tree_node_mixin_unittest.cpp b/fxjs/gc/gced_tree_node_mixin_unittest.cpp +new file mode 100644 +index 000000000..2cd098ed6 +--- /dev/null ++++ b/fxjs/gc/gced_tree_node_mixin_unittest.cpp +@@ -0,0 +1,149 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "fxjs/gc/gced_tree_node_mixin.h" ++ ++#include ++ ++#include "core/fxcrt/observed_ptr.h" ++#include "fxjs/gc/heap.h" ++#include "testing/fxgc_unittest.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "testing/v8_test_environment.h" ++#include "v8/include/cppgc/allocation.h" ++#include "v8/include/cppgc/persistent.h" ++ ++namespace { ++ ++class ObservableGCedTreeNodeMixinForTest ++ : public cppgc::GarbageCollected, ++ public GCedTreeNodeMixin, ++ public Observable { ++ public: ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; ++ ++ // GCedTreeNodeMixin: ++ void Trace(cppgc::Visitor* visitor) const override { ++ GCedTreeNodeMixin::Trace(visitor); ++ } ++ ++ private: ++ ObservableGCedTreeNodeMixinForTest() = default; ++}; ++ ++} // namespace ++ ++class GCedTreeNodeMixinUnitTest : public FXGCUnitTest { ++ public: ++ GCedTreeNodeMixinUnitTest() = default; ++ ~GCedTreeNodeMixinUnitTest() override = default; ++ ++ // FXGCUnitTest: ++ void TearDown() override { ++ root_ = nullptr; // Can't (yet) outlive |FXGCUnitTest::heap_|. ++ FXGCUnitTest::TearDown(); ++ } ++ ++ ObservableGCedTreeNodeMixinForTest* root() const { return root_; } ++ void CreateRoot() { root_ = CreateNode(); } ++ ++ ObservableGCedTreeNodeMixinForTest* CreateNode() { ++ return cppgc::MakeGarbageCollected( ++ heap()->GetAllocationHandle()); ++ } ++ ++ void AddClutterToFront(ObservableGCedTreeNodeMixinForTest* parent) { ++ for (int i = 0; i < 4; ++i) { ++ parent->AppendFirstChild( ++ cppgc::MakeGarbageCollected( ++ heap()->GetAllocationHandle())); ++ } ++ } ++ ++ void AddClutterToBack(ObservableGCedTreeNodeMixinForTest* parent) { ++ for (int i = 0; i < 4; ++i) { ++ parent->AppendLastChild( ++ cppgc::MakeGarbageCollected( ++ heap()->GetAllocationHandle())); ++ } ++ } ++ ++ private: ++ cppgc::Persistent root_; ++}; ++ ++TEST_F(GCedTreeNodeMixinUnitTest, OneRefence) { ++ CreateRoot(); ++ ObservedPtr watcher(root()); ++ ForceGCAndPump(); ++ EXPECT_TRUE(watcher); ++} ++ ++TEST_F(GCedTreeNodeMixinUnitTest, NoReferences) { ++ ObservedPtr watcher(CreateNode()); ++ ForceGCAndPump(); ++ EXPECT_FALSE(watcher); ++} ++ ++TEST_F(GCedTreeNodeMixinUnitTest, FirstHasParent) { ++ CreateRoot(); ++ ObservedPtr watcher(CreateNode()); ++ root()->AppendFirstChild(watcher.Get()); ++ ForceGCAndPump(); ++ ASSERT_TRUE(root()); ++ EXPECT_TRUE(watcher); ++ root()->RemoveChild(watcher.Get()); ++ ForceGCAndPump(); ++ ASSERT_TRUE(root()); ++ EXPECT_FALSE(watcher); ++ ++ // Now add some clutter. ++ watcher.Reset(CreateNode()); ++ root()->AppendFirstChild(watcher.Get()); ++ AddClutterToFront(root()); ++ AddClutterToBack(root()); ++ ForceGCAndPump(); ++ ASSERT_TRUE(root()); ++ EXPECT_TRUE(watcher); ++ root()->RemoveChild(watcher.Get()); ++ ForceGCAndPump(); ++ EXPECT_TRUE(root()); ++ EXPECT_FALSE(watcher); ++} ++ ++TEST_F(GCedTreeNodeMixinUnitTest, RemoveSelf) { ++ CreateRoot(); ++ ObservedPtr watcher(CreateNode()); ++ root()->AppendFirstChild(watcher.Get()); ++ ForceGCAndPump(); ++ EXPECT_TRUE(root()); ++ ASSERT_TRUE(watcher); ++ watcher->RemoveSelfIfParented(); ++ ForceGCAndPump(); ++ EXPECT_TRUE(root()); ++ EXPECT_FALSE(watcher); ++} ++ ++TEST_F(GCedTreeNodeMixinUnitTest, InsertBeforeAfter) { ++ CreateRoot(); ++ AddClutterToFront(root()); ++ ObservedPtr watcher(CreateNode()); ++ root()->AppendFirstChild(watcher.Get()); ++ root()->InsertBefore(root()->GetFirstChild(), root()->GetLastChild()); ++ root()->InsertAfter(root()->GetLastChild(), root()->GetFirstChild()); ++ ForceGCAndPump(); ++ ASSERT_TRUE(root()); ++ EXPECT_TRUE(watcher); ++ root()->RemoveChild(watcher.Get()); ++ ForceGCAndPump(); ++ EXPECT_TRUE(root()); ++ EXPECT_FALSE(watcher); ++} ++ ++TEST_F(GCedTreeNodeMixinUnitTest, AsMapKey) { ++ std::map, int> score; ++ ObservableGCedTreeNodeMixinForTest* node = CreateNode(); ++ score[node] = 100; ++ EXPECT_EQ(100, score[node]); ++} +diff --git a/fxjs/gc/gced_tree_node_unittest.cpp b/fxjs/gc/gced_tree_node_unittest.cpp +new file mode 100644 +index 000000000..a456a313f +--- /dev/null ++++ b/fxjs/gc/gced_tree_node_unittest.cpp +@@ -0,0 +1,143 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "fxjs/gc/gced_tree_node.h" ++ ++#include ++ ++#include "core/fxcrt/observed_ptr.h" ++#include "fxjs/gc/heap.h" ++#include "testing/fxgc_unittest.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "testing/v8_test_environment.h" ++#include "v8/include/cppgc/allocation.h" ++#include "v8/include/cppgc/persistent.h" ++ ++namespace { ++ ++class ObservableGCedTreeNodeForTest ++ : public GCedTreeNode, ++ public Observable { ++ public: ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; ++ ++ private: ++ ObservableGCedTreeNodeForTest() = default; ++}; ++ ++} // namespace ++ ++class GCedTreeNodeUnitTest : public FXGCUnitTest { ++ public: ++ GCedTreeNodeUnitTest() = default; ++ ~GCedTreeNodeUnitTest() override = default; ++ ++ // FXGCUnitTest: ++ void TearDown() override { ++ root_ = nullptr; // Can't (yet) outlive |FXGCUnitTest::heap_|. ++ FXGCUnitTest::TearDown(); ++ } ++ ++ ObservableGCedTreeNodeForTest* root() const { return root_; } ++ void CreateRoot() { root_ = CreateNode(); } ++ ++ ObservableGCedTreeNodeForTest* CreateNode() { ++ return cppgc::MakeGarbageCollected( ++ heap()->GetAllocationHandle()); ++ } ++ ++ void AddClutterToFront(ObservableGCedTreeNodeForTest* parent) { ++ for (int i = 0; i < 4; ++i) { ++ parent->AppendFirstChild( ++ cppgc::MakeGarbageCollected( ++ heap()->GetAllocationHandle())); ++ } ++ } ++ ++ void AddClutterToBack(ObservableGCedTreeNodeForTest* parent) { ++ for (int i = 0; i < 4; ++i) { ++ parent->AppendLastChild( ++ cppgc::MakeGarbageCollected( ++ heap()->GetAllocationHandle())); ++ } ++ } ++ ++ private: ++ cppgc::Persistent root_; ++}; ++ ++TEST_F(GCedTreeNodeUnitTest, OneRefence) { ++ CreateRoot(); ++ ObservedPtr watcher(root()); ++ ForceGCAndPump(); ++ EXPECT_TRUE(watcher); ++} ++ ++TEST_F(GCedTreeNodeUnitTest, NoReferences) { ++ ObservedPtr watcher(CreateNode()); ++ ForceGCAndPump(); ++ EXPECT_FALSE(watcher); ++} ++ ++TEST_F(GCedTreeNodeUnitTest, FirstHasParent) { ++ CreateRoot(); ++ ObservedPtr watcher(CreateNode()); ++ root()->AppendFirstChild(watcher.Get()); ++ ForceGCAndPump(); ++ ASSERT_TRUE(root()); ++ EXPECT_TRUE(watcher); ++ root()->RemoveChild(watcher.Get()); ++ ForceGCAndPump(); ++ ASSERT_TRUE(root()); ++ EXPECT_FALSE(watcher); ++ ++ // Now add some clutter. ++ watcher.Reset(CreateNode()); ++ root()->AppendFirstChild(watcher.Get()); ++ AddClutterToFront(root()); ++ AddClutterToBack(root()); ++ ForceGCAndPump(); ++ ASSERT_TRUE(root()); ++ EXPECT_TRUE(watcher); ++ root()->RemoveChild(watcher.Get()); ++ ForceGCAndPump(); ++ EXPECT_TRUE(root()); ++ EXPECT_FALSE(watcher); ++} ++ ++TEST_F(GCedTreeNodeUnitTest, RemoveSelf) { ++ CreateRoot(); ++ ObservedPtr watcher(CreateNode()); ++ root()->AppendFirstChild(watcher.Get()); ++ ForceGCAndPump(); ++ EXPECT_TRUE(root()); ++ ASSERT_TRUE(watcher); ++ watcher->RemoveSelfIfParented(); ++ ForceGCAndPump(); ++ EXPECT_TRUE(root()); ++ EXPECT_FALSE(watcher); ++} ++ ++TEST_F(GCedTreeNodeUnitTest, InsertBeforeAfter) { ++ CreateRoot(); ++ AddClutterToFront(root()); ++ ObservedPtr watcher(CreateNode()); ++ root()->AppendFirstChild(watcher.Get()); ++ root()->InsertBefore(root()->GetFirstChild(), root()->GetLastChild()); ++ root()->InsertAfter(root()->GetLastChild(), root()->GetFirstChild()); ++ ForceGCAndPump(); ++ ASSERT_TRUE(root()); ++ EXPECT_TRUE(watcher); ++ root()->RemoveChild(watcher.Get()); ++ ForceGCAndPump(); ++ EXPECT_TRUE(root()); ++ EXPECT_FALSE(watcher); ++} ++ ++TEST_F(GCedTreeNodeUnitTest, AsMapKey) { ++ std::map, int> score; ++ ObservableGCedTreeNodeForTest* node = CreateNode(); ++ score[node] = 100; ++ EXPECT_EQ(100, score[node]); ++} +diff --git a/fxjs/gc/heap.cpp b/fxjs/gc/heap.cpp +new file mode 100644 +index 000000000..301018fe0 +--- /dev/null ++++ b/fxjs/gc/heap.cpp +@@ -0,0 +1,98 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "fxjs/gc/heap.h" ++ ++#include ++ ++#include "core/fxcrt/fx_system.h" ++#include "third_party/base/check.h" ++#include "v8/include/cppgc/heap.h" ++ ++namespace { ++ ++size_t g_platform_ref_count = 0; ++v8::Platform* g_platform = nullptr; ++v8::Isolate* g_isolate = nullptr; ++ ++} // namespace ++ ++// Taken from v8/samples/cppgc/cppgc-for-v8-embedders.cc. ++// Adaptper that makes the global v8::Platform compatible with a ++// cppgc::Platform. ++class CFXGC_Platform final : public cppgc::Platform { ++ public: ++ CFXGC_Platform() = default; ++ ~CFXGC_Platform() override = default; ++ ++ cppgc::PageAllocator* GetPageAllocator() override { ++ return g_platform->GetPageAllocator(); ++ } ++ ++ double MonotonicallyIncreasingTime() override { ++ return g_platform->MonotonicallyIncreasingTime(); ++ } ++ ++ std::shared_ptr GetForegroundTaskRunner() override { ++ // V8's default platform creates a new task runner when passed the ++ // v8::Isolate pointer the first time. For non-default platforms this will ++ // require getting the appropriate task runner. ++ return g_platform->GetForegroundTaskRunner(g_isolate); ++ } ++ ++ std::unique_ptr PostJob( ++ cppgc::TaskPriority priority, ++ std::unique_ptr job_task) override { ++ return g_platform->PostJob(priority, std::move(job_task)); ++ } ++}; ++ ++void FXGC_Initialize(v8::Platform* platform, v8::Isolate* isolate) { ++ if (platform) { ++ DCHECK(!g_platform); ++ g_platform = platform; ++ g_isolate = isolate; ++ } ++} ++ ++void FXGC_Release() { ++ if (g_platform && g_platform_ref_count == 0) { ++ g_platform = nullptr; ++ g_isolate = nullptr; ++ } ++} ++ ++FXGCScopedHeap FXGC_CreateHeap() { ++ // If XFA is included at compile-time, but JS is disabled at run-time, ++ // we may still attempt to build a CPDFXFA_Context which will want a ++ // heap. But we can't make one because JS is disabled. ++ // TODO(tsepez): Stop the context from even being created. ++ if (!g_platform) ++ return nullptr; ++ ++ ++g_platform_ref_count; ++ auto heap = cppgc::Heap::Create( ++ std::make_shared(), ++ cppgc::Heap::HeapOptions{ ++ {}, ++ cppgc::Heap::StackSupport::kNoConservativeStackScan, ++ cppgc::Heap::MarkingType::kAtomic, ++ cppgc::Heap::SweepingType::kIncrementalAndConcurrent, ++ {}}); ++ return FXGCScopedHeap(heap.release()); ++} ++ ++void FXGC_ForceGarbageCollection(cppgc::Heap* heap) { ++ heap->ForceGarbageCollectionSlow("FXGC", "ForceGarbageCollection", ++ cppgc::Heap::StackState::kNoHeapPointers); ++} ++ ++void FXGCHeapDeleter::operator()(cppgc::Heap* heap) { ++ DCHECK(heap); ++ DCHECK(g_platform_ref_count > 0); ++ --g_platform_ref_count; ++ ++ FXGC_ForceGarbageCollection(heap); ++ delete heap; ++} +diff --git a/fxjs/gc/heap.h b/fxjs/gc/heap.h +new file mode 100644 +index 000000000..1e2bf6159 +--- /dev/null ++++ b/fxjs/gc/heap.h +@@ -0,0 +1,36 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef FXJS_GC_HEAP_H_ ++#define FXJS_GC_HEAP_H_ ++ ++#include ++ ++#include "v8/include/cppgc/allocation.h" ++ ++namespace cppgc { ++class Heap; ++} // namespace cppgc ++ ++namespace v8 { ++class Isolate; ++class Platform; ++} // namespace v8 ++ ++struct FXGCHeapDeleter { ++ void operator()(cppgc::Heap* heap); ++}; ++ ++using FXGCScopedHeap = std::unique_ptr; ++ ++void FXGC_Initialize(v8::Platform* platform, v8::Isolate* isolate); ++void FXGC_Release(); ++FXGCScopedHeap FXGC_CreateHeap(); ++void FXGC_ForceGarbageCollection(cppgc::Heap* heap); ++ ++#define CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED \ ++ template \ ++ friend class cppgc::MakeGarbageCollectedTrait ++ ++#endif // FXJS_GC_HEAP_H_ +diff --git a/fxjs/gc/heap_unittest.cpp b/fxjs/gc/heap_unittest.cpp +new file mode 100644 +index 000000000..aa90d0075 +--- /dev/null ++++ b/fxjs/gc/heap_unittest.cpp +@@ -0,0 +1,175 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "fxjs/gc/heap.h" ++ ++#include ++#include ++ ++#include "testing/fxgc_unittest.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "testing/v8_test_environment.h" ++#include "third_party/base/containers/contains.h" ++#include "v8/include/cppgc/allocation.h" ++#include "v8/include/cppgc/persistent.h" ++ ++namespace { ++ ++class PseudoCollectible : public cppgc::GarbageCollected { ++ public: ++ static void ClearCounts() { ++ s_live_.clear(); ++ s_dead_.clear(); ++ } ++ static size_t LiveCount() { return s_live_.size(); } ++ static size_t DeadCount() { return s_dead_.size(); } ++ ++ PseudoCollectible() { s_live_.insert(this); } ++ virtual ~PseudoCollectible() { ++ s_live_.erase(this); ++ s_dead_.insert(this); ++ } ++ ++ bool IsLive() const { return pdfium::Contains(s_live_, this); } ++ ++ virtual void Trace(cppgc::Visitor* visitor) const {} ++ ++ private: ++ static std::set s_live_; ++ static std::set s_dead_; ++}; ++ ++std::set PseudoCollectible::s_live_; ++std::set PseudoCollectible::s_dead_; ++ ++class CollectibleHolder { ++ public: ++ explicit CollectibleHolder(PseudoCollectible* holdee) : holdee_(holdee) {} ++ ~CollectibleHolder() = default; ++ ++ PseudoCollectible* holdee() const { return holdee_; } ++ ++ private: ++ cppgc::Persistent holdee_; ++}; ++ ++class Bloater : public cppgc::GarbageCollected { ++ public: ++ void Trace(cppgc::Visitor* visitor) const {} ++ uint8_t bloat_[65536]; ++}; ++ ++} // namespace ++ ++class HeapUnitTest : public FXGCUnitTest { ++ public: ++ HeapUnitTest() = default; ++ ~HeapUnitTest() override = default; ++ ++ // FXGCUnitTest: ++ void TearDown() override { ++ PseudoCollectible::ClearCounts(); ++ FXGCUnitTest::TearDown(); ++ } ++}; ++ ++TEST_F(HeapUnitTest, SeveralHeaps) { ++ FXGCScopedHeap heap1 = FXGC_CreateHeap(); ++ EXPECT_TRUE(heap1); ++ ++ FXGCScopedHeap heap2 = FXGC_CreateHeap(); ++ EXPECT_TRUE(heap2); ++ ++ FXGCScopedHeap heap3 = FXGC_CreateHeap(); ++ EXPECT_TRUE(heap3); ++ ++ // Test manually destroying the heap. ++ heap3.reset(); ++ EXPECT_FALSE(heap3); ++ heap3.reset(); ++ EXPECT_FALSE(heap3); ++} ++ ++TEST_F(HeapUnitTest, NoReferences) { ++ FXGCScopedHeap heap1 = FXGC_CreateHeap(); ++ ASSERT_TRUE(heap1); ++ { ++ auto holder = std::make_unique( ++ cppgc::MakeGarbageCollected( ++ heap1->GetAllocationHandle())); ++ ++ EXPECT_TRUE(holder->holdee()->IsLive()); ++ EXPECT_EQ(1u, PseudoCollectible::LiveCount()); ++ EXPECT_EQ(0u, PseudoCollectible::DeadCount()); ++ } ++ FXGC_ForceGarbageCollection(heap1.get()); ++ EXPECT_EQ(0u, PseudoCollectible::LiveCount()); ++ EXPECT_EQ(1u, PseudoCollectible::DeadCount()); ++} ++ ++TEST_F(HeapUnitTest, HasReferences) { ++ FXGCScopedHeap heap1 = FXGC_CreateHeap(); ++ ASSERT_TRUE(heap1); ++ { ++ auto holder = std::make_unique( ++ cppgc::MakeGarbageCollected( ++ heap1->GetAllocationHandle())); ++ ++ EXPECT_TRUE(holder->holdee()->IsLive()); ++ EXPECT_EQ(1u, PseudoCollectible::LiveCount()); ++ EXPECT_EQ(0u, PseudoCollectible::DeadCount()); ++ ++ FXGC_ForceGarbageCollection(heap1.get()); ++ EXPECT_TRUE(holder->holdee()->IsLive()); ++ EXPECT_EQ(1u, PseudoCollectible::LiveCount()); ++ EXPECT_EQ(0u, PseudoCollectible::DeadCount()); ++ } ++} ++ ++// TODO(tsepez): enable when CPPGC fixes this segv. ++TEST_F(HeapUnitTest, DISABLED_DeleteHeapHasReferences) { ++ FXGCScopedHeap heap1 = FXGC_CreateHeap(); ++ ASSERT_TRUE(heap1); ++ { ++ auto holder = std::make_unique( ++ cppgc::MakeGarbageCollected( ++ heap1->GetAllocationHandle())); ++ ++ EXPECT_TRUE(holder->holdee()->IsLive()); ++ EXPECT_EQ(1u, PseudoCollectible::LiveCount()); ++ EXPECT_EQ(0u, PseudoCollectible::DeadCount()); ++ ++ heap1.reset(); ++ ++ // Maybe someday magically nulled by heap destruction. ++ EXPECT_FALSE(holder->holdee()); ++ EXPECT_EQ(1u, PseudoCollectible::LiveCount()); ++ EXPECT_EQ(0u, PseudoCollectible::DeadCount()); ++ } ++} ++ ++TEST_F(HeapUnitTest, DeleteHeapNoReferences) { ++ FXGCScopedHeap heap1 = FXGC_CreateHeap(); ++ ASSERT_TRUE(heap1); ++ { ++ auto holder = std::make_unique( ++ cppgc::MakeGarbageCollected( ++ heap1->GetAllocationHandle())); ++ ++ EXPECT_TRUE(holder->holdee()->IsLive()); ++ EXPECT_EQ(1u, PseudoCollectible::LiveCount()); ++ EXPECT_EQ(0u, PseudoCollectible::DeadCount()); ++ } ++ heap1.reset(); ++ EXPECT_EQ(0u, PseudoCollectible::LiveCount()); ++ EXPECT_EQ(1u, PseudoCollectible::DeadCount()); ++} ++ ++TEST_F(HeapUnitTest, Bloat) { ++ ASSERT_TRUE(heap()); ++ for (int i = 0; i < 100000; ++i) { ++ cppgc::MakeGarbageCollected(heap()->GetAllocationHandle()); ++ Pump(); // Do not force GC, must happen implicitly when space required. ++ } ++} +diff --git a/fxjs/gc/move_unittest.cpp b/fxjs/gc/move_unittest.cpp +new file mode 100644 +index 000000000..3d5a80959 +--- /dev/null ++++ b/fxjs/gc/move_unittest.cpp +@@ -0,0 +1,62 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include ++ ++#include "fxjs/gc/heap.h" ++#include "testing/fxgc_unittest.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "v8/include/cppgc/member.h" ++#include "v8/include/cppgc/persistent.h" ++ ++namespace { ++ ++class HeapObject : public cppgc::GarbageCollected { ++ public: ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; ++ ++ void Trace(cppgc::Visitor* visitor) const { ++ visitor->Trace(frick_); ++ visitor->Trace(frack_); ++ } ++ ++ cppgc::Member frick_; ++ cppgc::Member frack_; ++ ++ private: ++ HeapObject() = default; ++}; ++ ++class CppObject { ++ public: ++ CppObject() = default; ++ ++ cppgc::Persistent click_; ++ cppgc::Persistent clack_; ++}; ++ ++} // namespace ++ ++class MoveUnitTest : public FXGCUnitTest {}; ++ ++TEST_F(MoveUnitTest, Member) { ++ // Moving a Member<> leaves the moved-from object as null. ++ auto* obj = ++ cppgc::MakeGarbageCollected(heap()->GetAllocationHandle()); ++ obj->frick_ = obj; ++ obj->frack_ = std::move(obj->frick_); ++ EXPECT_FALSE(obj->frick_); ++ EXPECT_EQ(obj, obj->frack_); ++} ++ ++TEST_F(MoveUnitTest, Persistent) { ++ // Moving a Persistent<> leaves the moved-from object as null. ++ auto* obj = ++ cppgc::MakeGarbageCollected(heap()->GetAllocationHandle()); ++ CppObject outsider; ++ outsider.click_ = obj; ++ outsider.clack_ = std::move(outsider.click_); ++ EXPECT_FALSE(outsider.click_); ++ EXPECT_EQ(obj, outsider.clack_); ++} +diff --git a/fxjs/global_timer.cpp b/fxjs/global_timer.cpp +index b8375081f..035aff84d 100644 +--- a/fxjs/global_timer.cpp ++++ b/fxjs/global_timer.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,8 +8,10 @@ + + #include + +-#include "core/fxcrt/timerhandler_iface.h" ++#include "core/fxcrt/cfx_timer.h" + #include "fxjs/cjs_app.h" ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" + #include "third_party/base/no_destructor.h" + + namespace { +@@ -34,8 +36,10 @@ GlobalTimer::GlobalTimer(CJS_App* pObj, + m_swJScript(script), + m_pRuntime(pRuntime), + m_pEmbedApp(pObj) { +- if (HasValidID()) ++ if (HasValidID()) { ++ DCHECK(!pdfium::Contains(GetGlobalTimerMap(), m_nTimerID)); + GetGlobalTimerMap()[m_nTimerID] = this; ++ } + } + + GlobalTimer::~GlobalTimer() { +@@ -45,6 +49,7 @@ GlobalTimer::~GlobalTimer() { + if (m_pRuntime && m_pRuntime->GetTimerHandler()) + m_pRuntime->GetTimerHandler()->KillTimer(m_nTimerID); + ++ DCHECK(pdfium::Contains(GetGlobalTimerMap(), m_nTimerID)); + GetGlobalTimerMap().erase(m_nTimerID); + } + +@@ -84,5 +89,5 @@ void GlobalTimer::Cancel(int32_t nTimerID) { + } + + bool GlobalTimer::HasValidID() const { +- return m_nTimerID != TimerHandlerIface::kInvalidTimerID; ++ return m_nTimerID != CFX_Timer::HandlerIface::kInvalidTimerID; + } +diff --git a/fxjs/global_timer.h b/fxjs/global_timer.h +index ef6bbb63b..106c9e6f7 100644 +--- a/fxjs/global_timer.h ++++ b/fxjs/global_timer.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,7 @@ + #ifndef FXJS_GLOBAL_TIMER_H_ + #define FXJS_GLOBAL_TIMER_H_ + ++#include "core/fxcrt/unowned_ptr.h" + #include "fxjs/cjs_runtime.h" + + class CJS_App; +@@ -44,7 +45,7 @@ class GlobalTimer { + const uint32_t m_dwTimeOut; + const WideString m_swJScript; + ObservedPtr m_pRuntime; +- CJS_App* const m_pEmbedApp; ++ UnownedPtr const m_pEmbedApp; + }; + + #endif // FXJS_GLOBAL_TIMER_H_ +diff --git a/fxjs/ijs_event_context.h b/fxjs/ijs_event_context.h +index e2c640780..8e9d23c8c 100644 +--- a/fxjs/ijs_event_context.h ++++ b/fxjs/ijs_event_context.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,15 +7,11 @@ + #ifndef FXJS_IJS_EVENT_CONTEXT_H_ + #define FXJS_IJS_EVENT_CONTEXT_H_ + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/widestring.h" + #include "fxjs/ijs_runtime.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + +-class CPDF_Bookmark; + class CPDF_FormField; +-class CPDFSDK_Annot; +-class CPDFSDK_FormFillEnvironment; + + // Records the details of an event and triggers JS execution for it. There + // can be more than one of these at any given time, as JS callbacks to C++ +@@ -24,23 +20,20 @@ class IJS_EventContext { + public: + virtual ~IJS_EventContext() = default; + +- virtual Optional RunScript( ++ virtual absl::optional RunScript( + const WideString& script) = 0; + +- virtual void OnApp_Init() = 0; ++ virtual void OnDoc_Open(const WideString& strTargetName) = 0; ++ virtual void OnDoc_WillPrint() = 0; ++ virtual void OnDoc_DidPrint() = 0; ++ virtual void OnDoc_WillSave() = 0; ++ virtual void OnDoc_DidSave() = 0; ++ virtual void OnDoc_WillClose() = 0; + +- virtual void OnDoc_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const WideString& strTargetName) = 0; +- virtual void OnDoc_WillPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0; +- virtual void OnDoc_DidPrint(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0; +- virtual void OnDoc_WillSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0; +- virtual void OnDoc_DidSave(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0; +- virtual void OnDoc_WillClose(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0; +- +- virtual void OnPage_Open(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0; +- virtual void OnPage_Close(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0; +- virtual void OnPage_InView(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0; +- virtual void OnPage_OutView(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0; ++ virtual void OnPage_Open() = 0; ++ virtual void OnPage_Close() = 0; ++ virtual void OnPage_InView() = 0; ++ virtual void OnPage_OutView() = 0; + + virtual void OnField_MouseDown(bool bModifier, + bool bShift, +@@ -88,44 +81,6 @@ class IJS_EventContext { + WideString* Value, + bool* bRc) = 0; + +- virtual void OnScreen_Focus(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) = 0; +- virtual void OnScreen_Blur(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) = 0; +- virtual void OnScreen_Open(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) = 0; +- virtual void OnScreen_Close(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) = 0; +- virtual void OnScreen_MouseDown(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) = 0; +- virtual void OnScreen_MouseUp(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) = 0; +- virtual void OnScreen_MouseEnter(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) = 0; +- virtual void OnScreen_MouseExit(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) = 0; +- virtual void OnScreen_InView(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) = 0; +- virtual void OnScreen_OutView(bool bModifier, +- bool bShift, +- CPDFSDK_Annot* pScreen) = 0; +- +- virtual void OnBookmark_MouseUp(CPDF_Bookmark* pBookMark) = 0; +- virtual void OnLink_MouseUp(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0; +- +- virtual void OnMenu_Exec(CPDFSDK_FormFillEnvironment* pFormFillEnv, +- const WideString&) = 0; +- virtual void OnBatchExec(CPDFSDK_FormFillEnvironment* pFormFillEnv) = 0; +- virtual void OnConsole_Exec() = 0; + virtual void OnExternal_Exec() = 0; + }; + +diff --git a/fxjs/ijs_runtime.cpp b/fxjs/ijs_runtime.cpp +index 34a846e9e..030852483 100644 +--- a/fxjs/ijs_runtime.cpp ++++ b/fxjs/ijs_runtime.cpp +@@ -1,36 +1,46 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "fxjs/ijs_runtime.h" + + #include "fxjs/cjs_runtimestub.h" +-#include "third_party/base/ptr_util.h" + + #ifdef PDF_ENABLE_V8 ++#include "fpdfsdk/cpdfsdk_formfillenvironment.h" + #include "fxjs/cfxjs_engine.h" + #include "fxjs/cjs_runtime.h" +-#endif ++#ifdef PDF_ENABLE_XFA ++#include "fxjs/gc/heap.h" ++#endif // PDF_ENABLE_XFA ++#endif // PDF_ENABLE_V8 + + IJS_Runtime::ScopedEventContext::ScopedEventContext(IJS_Runtime* pRuntime) + : m_pRuntime(pRuntime), m_pContext(pRuntime->NewEventContext()) {} + + IJS_Runtime::ScopedEventContext::~ScopedEventContext() { +- m_pRuntime->ReleaseEventContext(m_pContext.Release()); ++ m_pRuntime->ReleaseEventContext(m_pContext.ExtractAsDangling()); + } + + // static +-void IJS_Runtime::Initialize(unsigned int slot, void* isolate) { ++void IJS_Runtime::Initialize(unsigned int slot, void* isolate, void* platform) { + #ifdef PDF_ENABLE_V8 + FXJS_Initialize(slot, static_cast(isolate)); +-#endif ++#ifdef PDF_ENABLE_XFA ++ FXGC_Initialize(static_cast(platform), ++ static_cast(isolate)); ++#endif // PDF_ENABLE_XFA ++#endif // PDF_ENABLE_V8 + } + + // static + void IJS_Runtime::Destroy() { + #ifdef PDF_ENABLE_V8 ++#ifdef PDF_ENABLE_XFA ++ FXGC_Release(); ++#endif // PDF_ENABLE_XFA + FXJS_Release(); +-#endif ++#endif // PDF_ENABLE_V8 + } + + // static +@@ -38,9 +48,9 @@ std::unique_ptr IJS_Runtime::Create( + CPDFSDK_FormFillEnvironment* pFormFillEnv) { + #ifdef PDF_ENABLE_V8 + if (pFormFillEnv->IsJSPlatformPresent()) +- return pdfium::MakeUnique(pFormFillEnv); ++ return std::make_unique(pFormFillEnv); + #endif +- return pdfium::MakeUnique(pFormFillEnv); ++ return std::make_unique(pFormFillEnv); + } + + IJS_Runtime::~IJS_Runtime() = default; +diff --git a/fxjs/ijs_runtime.h b/fxjs/ijs_runtime.h +index ca103aa9a..d2ce19f74 100644 +--- a/fxjs/ijs_runtime.h ++++ b/fxjs/ijs_runtime.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,12 +9,11 @@ + + #include + +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" ++#include "core/fxcrt/fx_memory.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "third_party/base/optional.h" ++#include "core/fxcrt/widestring.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + +-class CFXJSE_Value; + class CJS_Runtime; + class CPDFSDK_FormFillEnvironment; + class IJS_EventContext; +@@ -34,18 +33,20 @@ class IJS_Runtime { + + class ScopedEventContext { + public: ++ FX_STACK_ALLOCATED(); ++ + explicit ScopedEventContext(IJS_Runtime* pRuntime); + ~ScopedEventContext(); + +- IJS_EventContext* Get() const { return m_pContext.Get(); } +- IJS_EventContext* operator->() const { return m_pContext.Get(); } ++ IJS_EventContext* Get() const { return m_pContext; } ++ IJS_EventContext* operator->() const { return m_pContext; } + + private: + UnownedPtr const m_pRuntime; + UnownedPtr m_pContext; + }; + +- static void Initialize(unsigned int slot, void* isolate); ++ static void Initialize(unsigned int slot, void* isolate, void* platform); + static void Destroy(); + static std::unique_ptr Create( + CPDFSDK_FormFillEnvironment* pFormFillEnv); +@@ -56,7 +57,7 @@ class IJS_Runtime { + virtual IJS_EventContext* NewEventContext() = 0; + virtual void ReleaseEventContext(IJS_EventContext* pContext) = 0; + virtual CPDFSDK_FormFillEnvironment* GetFormFillEnv() const = 0; +- virtual Optional ExecuteScript(const WideString& script) = 0; ++ virtual absl::optional ExecuteScript(const WideString& script) = 0; + + protected: + IJS_Runtime() = default; +diff --git a/fxjs/js_define.cpp b/fxjs/js_define.cpp +index 0c0c02cdc..b19c2e205 100644 +--- a/fxjs/js_define.cpp ++++ b/fxjs/js_define.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,8 +6,10 @@ + + #include "fxjs/js_define.h" + ++#include ++#include ++ + #include +-#include + #include + #include + +@@ -15,50 +17,46 @@ + #include "fxjs/cjs_document.h" + #include "fxjs/cjs_object.h" + #include "fxjs/fx_date_helpers.h" ++#include "fxjs/fxv8.h" ++#include "third_party/base/check.h" ++#include "v8/include/v8-context.h" ++#include "v8/include/v8-function.h" ++#include "v8/include/v8-isolate.h" + + void JSDestructor(v8::Local obj) { + CFXJS_Engine::SetObjectPrivate(obj, nullptr); + } + +-double JS_DateParse(const WideString& str) { +- v8::Isolate* pIsolate = v8::Isolate::GetCurrent(); ++double JS_DateParse(v8::Isolate* pIsolate, const WideString& str) { + v8::Isolate::Scope isolate_scope(pIsolate); + v8::HandleScope scope(pIsolate); + + v8::Local context = pIsolate->GetCurrentContext(); + + // Use the built-in object method. +- v8::Local v = +- context->Global() +- ->Get(context, v8::String::NewFromUtf8(pIsolate, "Date", +- v8::NewStringType::kNormal) +- .ToLocalChecked()) +- .ToLocalChecked(); +- if (v->IsObject()) { +- v8::Local o = v->ToObject(context).ToLocalChecked(); +- v = o->Get(context, v8::String::NewFromUtf8(pIsolate, "parse", +- v8::NewStringType::kNormal) +- .ToLocalChecked()) +- .ToLocalChecked(); +- if (v->IsFunction()) { +- v8::Local funC = v8::Local::Cast(v); +- const int argc = 1; +- v8::Local timeStr = +- v8::String::NewFromUtf8(pIsolate, +- FX_UTF8Encode(str.AsStringView()).c_str(), +- v8::NewStringType::kNormal) +- .ToLocalChecked(); +- v8::Local argv[argc] = {timeStr}; +- v = funC->Call(context, context->Global(), argc, argv).ToLocalChecked(); +- if (v->IsNumber()) { +- double date = v->ToNumber(context).ToLocalChecked()->Value(); +- if (!std::isfinite(date)) +- return date; +- return FX_LocalTime(date); +- } +- } +- } +- return 0; ++ v8::MaybeLocal maybe_value = ++ context->Global()->Get(context, fxv8::NewStringHelper(pIsolate, "Date")); ++ ++ v8::Local value; ++ if (!maybe_value.ToLocal(&value) || !value->IsObject()) ++ return 0; ++ ++ v8::Local obj = value.As(); ++ maybe_value = obj->Get(context, fxv8::NewStringHelper(pIsolate, "parse")); ++ if (!maybe_value.ToLocal(&value) || !value->IsFunction()) ++ return 0; ++ ++ v8::Local func = value.As(); ++ static constexpr int argc = 1; ++ v8::Local argv[argc] = { ++ fxv8::NewStringHelper(pIsolate, str.AsStringView()), ++ }; ++ maybe_value = func->Call(context, context->Global(), argc, argv); ++ if (!maybe_value.ToLocal(&value) || !value->IsNumber()) ++ return 0; ++ ++ double date = value.As()->Value(); ++ return isfinite(date) ? FX_LocalTime(date) : date; + } + + std::vector> ExpandKeywordParams( +@@ -66,7 +64,7 @@ std::vector> ExpandKeywordParams( + const std::vector>& originals, + size_t nKeywords, + ...) { +- ASSERT(nKeywords); ++ DCHECK(nKeywords); + + std::vector> result(nKeywords, v8::Local()); + size_t size = std::min(originals.size(), nKeywords); +diff --git a/fxjs/js_define.h b/fxjs/js_define.h +index 2c15c9f4e..5b6573acc 100644 +--- a/fxjs/js_define.h ++++ b/fxjs/js_define.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,7 @@ + #ifndef FXJS_JS_DEFINE_H_ + #define FXJS_JS_DEFINE_H_ + ++#include + #include + + #include "core/fxcrt/unowned_ptr.h" +@@ -14,11 +15,10 @@ + #include "fxjs/cjs_result.h" + #include "fxjs/cjs_runtime.h" + #include "fxjs/js_resources.h" +-#include "third_party/base/ptr_util.h" + + class CJS_Object; + +-double JS_DateParse(const WideString& str); ++double JS_DateParse(v8::Isolate* pIsolate, const WideString& str); + + // Some JS methods have the bizarre convention that they may also be called + // with a single argument which is an object containing the actual arguments +@@ -42,20 +42,22 @@ bool IsExpandedParamKnown(v8::Local value); + // to construct native object state. + + template +-static void JSConstructor(CFXJS_Engine* pEngine, v8::Local obj) { ++static void JSConstructor(CFXJS_Engine* pEngine, ++ v8::Local obj, ++ v8::Local proxy) { + pEngine->SetObjectPrivate( +- obj, pdfium::MakeUnique(obj, static_cast(pEngine))); ++ obj, std::make_unique(proxy, static_cast(pEngine))); + } + + // CJS_Object has virtual dtor, template not required. + void JSDestructor(v8::Local obj); + + template +-UnownedPtr JSGetObject(v8::Local obj) { ++UnownedPtr JSGetObject(v8::Isolate* isolate, v8::Local obj) { + if (CFXJS_Engine::GetObjDefnID(obj) != C::GetObjDefnID()) + return nullptr; + +- CJS_Object* pJSObj = CFXJS_Engine::GetObjectPrivate(obj); ++ CJS_Object* pJSObj = CFXJS_Engine::GetObjectPrivate(isolate, obj); + if (!pJSObj) + return nullptr; + +@@ -67,7 +69,7 @@ void JSPropGetter(const char* prop_name_string, + const char* class_name_string, + v8::Local property, + const v8::PropertyCallbackInfo& info) { +- auto pObj = JSGetObject(info.Holder()); ++ auto pObj = JSGetObject(info.GetIsolate(), info.Holder()); + if (!pObj) + return; + +@@ -75,7 +77,7 @@ void JSPropGetter(const char* prop_name_string, + if (!pRuntime) + return; + +- CJS_Result result = (pObj.Get()->*M)(pRuntime); ++ CJS_Result result = (pObj.get()->*M)(pRuntime); + if (result.HasError()) { + pRuntime->Error(JSFormatErrorString(class_name_string, prop_name_string, + result.Error())); +@@ -92,7 +94,7 @@ void JSPropSetter(const char* prop_name_string, + v8::Local property, + v8::Local value, + const v8::PropertyCallbackInfo& info) { +- auto pObj = JSGetObject(info.Holder()); ++ auto pObj = JSGetObject(info.GetIsolate(), info.Holder()); + if (!pObj) + return; + +@@ -100,7 +102,7 @@ void JSPropSetter(const char* prop_name_string, + if (!pRuntime) + return; + +- CJS_Result result = (pObj.Get()->*M)(pRuntime, value); ++ CJS_Result result = (pObj.get()->*M)(pRuntime, value); + if (result.HasError()) { + pRuntime->Error(JSFormatErrorString(class_name_string, prop_name_string, + result.Error())); +@@ -113,7 +115,7 @@ template & info) { +- auto pObj = JSGetObject(info.Holder()); ++ auto pObj = JSGetObject(info.GetIsolate(), info.Holder()); + if (!pObj) + return; + +@@ -125,7 +127,7 @@ void JSMethod(const char* method_name_string, + for (unsigned int i = 0; i < (unsigned int)info.Length(); i++) + parameters.push_back(info[i]); + +- CJS_Result result = (pObj.Get()->*M)(pRuntime, parameters); ++ CJS_Result result = (pObj.get()->*M)(pRuntime, parameters); + if (result.HasError()) { + pRuntime->Error(JSFormatErrorString(class_name_string, method_name_string, + result.Error())); +diff --git a/fxjs/js_resources.cpp b/fxjs/js_resources.cpp +index 3bc8df375..c13361034 100644 +--- a/fxjs/js_resources.cpp ++++ b/fxjs/js_resources.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,8 @@ + + #include "fxjs/js_resources.h" + ++#include "third_party/base/notreached.h" ++ + WideString JSGetStringFromID(JSMessage msg) { + const char* msg_string = ""; + switch (msg) { +@@ -81,12 +83,15 @@ WideString JSGetStringFromID(JSMessage msg) { + case JSMessage::kUserGestureRequiredError: + msg_string = "User gesture required."; + break; +- case JSMessage::kTooManyOccurances: +- msg_string = "Too many occurances."; ++ case JSMessage::kTooManyOccurrences: ++ msg_string = "Too many occurrences."; + break; + case JSMessage::kUnknownMethod: + msg_string = "Unknown method."; + break; ++ case JSMessage::kWouldBeCyclic: ++ msg_string = "Operation would create a cycle."; ++ break; + default: + NOTREACHED(); + break; +@@ -97,10 +102,10 @@ WideString JSGetStringFromID(JSMessage msg) { + WideString JSFormatErrorString(const char* class_name, + const char* property_name, + const WideString& details) { +- WideString result = WideString::FromDefANSI(class_name); ++ WideString result = WideString::FromUTF8(class_name); + if (property_name) { + result += L"."; +- result += WideString::FromDefANSI(property_name); ++ result += WideString::FromUTF8(property_name); + } + result += L": "; + result += details; +diff --git a/fxjs/js_resources.h b/fxjs/js_resources.h +index 8a3086288..0bc6be097 100644 +--- a/fxjs/js_resources.h ++++ b/fxjs/js_resources.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -33,8 +33,9 @@ enum class JSMessage { + kUnknownProperty, + kInvalidSetError, + kUserGestureRequiredError, +- kTooManyOccurances, ++ kTooManyOccurrences, + kUnknownMethod, ++ kWouldBeCyclic, + }; + + WideString JSGetStringFromID(JSMessage msg); +diff --git a/fxjs/xfa/cfxjse_app_embeddertest.cpp b/fxjs/xfa/cfxjse_app_embeddertest.cpp +index 628f56e1f..0e6fa45c4 100644 +--- a/fxjs/xfa/cfxjse_app_embeddertest.cpp ++++ b/fxjs/xfa/cfxjse_app_embeddertest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxjs/xfa/cfxjse_arguments.cpp b/fxjs/xfa/cfxjse_arguments.cpp +deleted file mode 100644 +index 8bfc8e374..000000000 +--- a/fxjs/xfa/cfxjse_arguments.cpp ++++ /dev/null +@@ -1,59 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#include "fxjs/xfa/cfxjse_arguments.h" +- +-#include "fxjs/xfa/cfxjse_context.h" +-#include "fxjs/xfa/cfxjse_value.h" +-#include "third_party/base/ptr_util.h" +- +-CFXJSE_Arguments::CFXJSE_Arguments( +- const v8::FunctionCallbackInfo* pInfo, +- CFXJSE_Value* pRetValue) +- : m_pInfo(pInfo), m_pRetValue(pRetValue) {} +- +-CFXJSE_Arguments::~CFXJSE_Arguments() {} +- +-int32_t CFXJSE_Arguments::GetLength() const { +- return m_pInfo->Length(); +-} +- +-std::unique_ptr CFXJSE_Arguments::GetValue(int32_t index) const { +- auto pArgValue = pdfium::MakeUnique(v8::Isolate::GetCurrent()); +- pArgValue->ForceSetValue((*m_pInfo)[index]); +- return pArgValue; +-} +- +-bool CFXJSE_Arguments::GetBoolean(int32_t index) const { +- return (*m_pInfo)[index]->BooleanValue(m_pInfo->GetIsolate()); +-} +- +-int32_t CFXJSE_Arguments::GetInt32(int32_t index) const { +- return static_cast( +- (*m_pInfo)[index] +- ->NumberValue(m_pInfo->GetIsolate()->GetCurrentContext()) +- .FromMaybe(0.0)); +-} +- +-float CFXJSE_Arguments::GetFloat(int32_t index) const { +- return static_cast( +- (*m_pInfo)[index] +- ->NumberValue(m_pInfo->GetIsolate()->GetCurrentContext()) +- .FromMaybe(0.0)); +-} +- +-ByteString CFXJSE_Arguments::GetUTF8String(int32_t index) const { +- v8::Isolate* isolate = m_pInfo->GetIsolate(); +- v8::Local info = (*m_pInfo)[index]; +- v8::Local hString = +- info->ToString(isolate->GetCurrentContext()).ToLocalChecked(); +- v8::String::Utf8Value szStringVal(isolate, hString); +- return ByteString(*szStringVal); +-} +- +-CFXJSE_Value* CFXJSE_Arguments::GetReturnValue() const { +- return m_pRetValue.Get(); +-} +diff --git a/fxjs/xfa/cfxjse_arguments.h b/fxjs/xfa/cfxjse_arguments.h +deleted file mode 100644 +index e048bc2d5..000000000 +--- a/fxjs/xfa/cfxjse_arguments.h ++++ /dev/null +@@ -1,37 +0,0 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com +- +-#ifndef FXJS_XFA_CFXJSE_ARGUMENTS_H_ +-#define FXJS_XFA_CFXJSE_ARGUMENTS_H_ +- +-#include +- +-#include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/unowned_ptr.h" +-#include "v8/include/v8.h" +- +-class CFXJSE_Value; +- +-class CFXJSE_Arguments { +- public: +- CFXJSE_Arguments(const v8::FunctionCallbackInfo* pInfo, +- CFXJSE_Value* pRetValue); +- ~CFXJSE_Arguments(); +- +- int32_t GetLength() const; +- std::unique_ptr GetValue(int32_t index) const; +- bool GetBoolean(int32_t index) const; +- int32_t GetInt32(int32_t index) const; +- float GetFloat(int32_t index) const; +- ByteString GetUTF8String(int32_t index) const; +- CFXJSE_Value* GetReturnValue() const; +- +- private: +- UnownedPtr> const m_pInfo; +- UnownedPtr const m_pRetValue; +-}; +- +-#endif // FXJS_XFA_CFXJSE_ARGUMENTS_H_ +diff --git a/fxjs/xfa/cfxjse_class.cpp b/fxjs/xfa/cfxjse_class.cpp +index f6caf07f9..26b4666be 100644 +--- a/fxjs/xfa/cfxjse_class.cpp ++++ b/fxjs/xfa/cfxjse_class.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,12 +10,21 @@ + #include + + #include "fxjs/cjs_result.h" ++#include "fxjs/fxv8.h" + #include "fxjs/js_resources.h" +-#include "fxjs/xfa/cfxjse_arguments.h" + #include "fxjs/xfa/cfxjse_context.h" + #include "fxjs/xfa/cfxjse_isolatetracker.h" + #include "fxjs/xfa/cfxjse_value.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "v8/include/v8-container.h" ++#include "v8/include/v8-external.h" ++#include "v8/include/v8-function-callback.h" ++#include "v8/include/v8-function.h" ++#include "v8/include/v8-isolate.h" ++#include "v8/include/v8-object.h" ++#include "v8/include/v8-primitive.h" ++#include "v8/include/v8-template.h" + + using pdfium::fxjse::kClassTag; + using pdfium::fxjse::kFuncTag; +@@ -34,19 +43,12 @@ FXJSE_CLASS_DESCRIPTOR* AsClassDescriptor(void* ptr) { + + void V8FunctionCallback_Wrapper( + const v8::FunctionCallbackInfo& info) { +- const FXJSE_FUNCTION_DESCRIPTOR* lpFunctionInfo = ++ const FXJSE_FUNCTION_DESCRIPTOR* pFunctionInfo = + AsFunctionDescriptor(info.Data().As()->Value()); +- if (!lpFunctionInfo) ++ if (!pFunctionInfo) + return; + +- ByteStringView szFunctionName(lpFunctionInfo->name); +- auto lpThisValue = pdfium::MakeUnique(info.GetIsolate()); +- lpThisValue->ForceSetValue(info.Holder()); +- auto lpRetValue = pdfium::MakeUnique(info.GetIsolate()); +- CFXJSE_Arguments impl(&info, lpRetValue.get()); +- lpFunctionInfo->callbackProc(lpThisValue.get(), szFunctionName, impl); +- if (!lpRetValue->DirectGetValue().IsEmpty()) +- info.GetReturnValue().Set(lpRetValue->DirectGetValue()); ++ pFunctionInfo->callbackProc(CFXJSE_HostObject::FromV8(info.Holder()), info); + } + + void V8ConstructorCallback_Wrapper( +@@ -54,30 +56,28 @@ void V8ConstructorCallback_Wrapper( + if (!info.IsConstructCall()) + return; + +- const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition = ++ const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor = + AsClassDescriptor(info.Data().As()->Value()); +- if (!lpClassDefinition) ++ if (!pClassDescriptor) + return; + +- ASSERT(info.Holder()->InternalFieldCount() == 2); ++ DCHECK_EQ(info.Holder()->InternalFieldCount(), 2); + info.Holder()->SetAlignedPointerInInternalField(0, nullptr); + info.Holder()->SetAlignedPointerInInternalField(1, nullptr); + } + + void Context_GlobalObjToString( + const v8::FunctionCallbackInfo& info) { +- const FXJSE_CLASS_DESCRIPTOR* lpClass = ++ const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor = + AsClassDescriptor(info.Data().As()->Value()); +- if (!lpClass) ++ if (!pClassDescriptor) + return; + +- if (info.This() == info.Holder() && lpClass->name) { +- ByteString szStringVal = ByteString::Format("[object %s]", lpClass->name); ++ if (info.This() == info.Holder() && pClassDescriptor->name) { ++ ByteString szStringVal = ++ ByteString::Format("[object %s]", pClassDescriptor->name); + info.GetReturnValue().Set( +- v8::String::NewFromUtf8(info.GetIsolate(), szStringVal.c_str(), +- v8::NewStringType::kNormal, +- szStringVal.GetLength()) +- .ToLocalChecked()); ++ fxv8::NewStringHelper(info.GetIsolate(), szStringVal.AsStringView())); + return; + } + v8::Local local_str = +@@ -95,10 +95,10 @@ void DynPropGetterAdapter_MethodCallback( + + auto* pClassDescriptor = static_cast( + hCallBackInfo->GetAlignedPointerFromInternalField(0)); +- if (pClassDescriptor != &GlobalClassDescriptor && +- pClassDescriptor != &NormalClassDescriptor && +- pClassDescriptor != &VariablesClassDescriptor && +- pClassDescriptor != &kFormCalcFM2JSDescriptor) { ++ if (pClassDescriptor != &kGlobalClassDescriptor && ++ pClassDescriptor != &kNormalClassDescriptor && ++ pClassDescriptor != &kVariablesClassDescriptor && ++ pClassDescriptor != &kFormCalcDescriptor) { + return; + } + +@@ -114,9 +114,7 @@ void DynPropGetterAdapter_MethodCallback( + if (result.HasError()) { + WideString err = JSFormatErrorString(pClassDescriptor->name, *szPropName, + result.Error()); +- v8::MaybeLocal str = v8::String::NewFromUtf8( +- info.GetIsolate(), err.ToDefANSI().c_str(), v8::NewStringType::kNormal); +- info.GetIsolate()->ThrowException(str.ToLocalChecked()); ++ fxv8::ThrowExceptionHelper(info.GetIsolate(), err.AsStringView()); + return; + } + +@@ -124,39 +122,39 @@ void DynPropGetterAdapter_MethodCallback( + info.GetReturnValue().Set(result.Return()); + } + +-void DynPropGetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass, +- CFXJSE_Value* pObject, ++void DynPropGetterAdapter(v8::Isolate* pIsolate, ++ const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor, ++ v8::Local pObject, + ByteStringView szPropName, + CFXJSE_Value* pValue) { +- ASSERT(lpClass); +- +- int32_t nPropType = +- lpClass->dynPropTypeGetter == nullptr +- ? FXJSE_ClassPropType_Property +- : lpClass->dynPropTypeGetter(pObject, szPropName, false); +- if (nPropType == FXJSE_ClassPropType_Property) { +- if (lpClass->dynPropGetter) +- lpClass->dynPropGetter(pObject, szPropName, pValue); +- } else if (nPropType == FXJSE_ClassPropType_Method) { +- if (lpClass->dynMethodCall && pValue) { +- v8::Isolate* pIsolate = pValue->GetIsolate(); ++ DCHECK(pClassDescriptor); ++ ++ FXJSE_ClassPropType nPropType = ++ pClassDescriptor->dynPropTypeGetter ++ ? pClassDescriptor->dynPropTypeGetter(pIsolate, pObject, szPropName, ++ false) ++ : FXJSE_ClassPropType::kProperty; ++ if (nPropType == FXJSE_ClassPropType::kProperty) { ++ if (pClassDescriptor->dynPropGetter) { ++ pValue->ForceSetValue(pIsolate, pClassDescriptor->dynPropGetter( ++ pIsolate, pObject, szPropName)); ++ } ++ } else if (nPropType == FXJSE_ClassPropType::kMethod) { ++ if (pClassDescriptor->dynMethodCall && pValue) { + v8::HandleScope hscope(pIsolate); + v8::Local hCallBackInfoTemplate = + v8::ObjectTemplate::New(pIsolate); + hCallBackInfoTemplate->SetInternalFieldCount(2); + v8::Local hCallBackInfo = +- hCallBackInfoTemplate +- ->NewInstance(pValue->GetIsolate()->GetCurrentContext()) ++ hCallBackInfoTemplate->NewInstance(pIsolate->GetCurrentContext()) + .ToLocalChecked(); + hCallBackInfo->SetAlignedPointerInInternalField( +- 0, const_cast(lpClass)); ++ 0, const_cast(pClassDescriptor)); + hCallBackInfo->SetInternalField( +- 1, v8::String::NewFromUtf8( +- pIsolate, reinterpret_cast(szPropName.raw_str()), +- v8::NewStringType::kNormal, szPropName.GetLength()) +- .ToLocalChecked()); ++ 1, fxv8::NewStringHelper(pIsolate, szPropName)); + pValue->ForceSetValue( +- v8::Function::New(pValue->GetIsolate()->GetCurrentContext(), ++ pIsolate, ++ v8::Function::New(pIsolate->GetCurrentContext(), + DynPropGetterAdapter_MethodCallback, hCallBackInfo, + 0, v8::ConstructorBehavior::kThrow) + .ToLocalChecked()); +@@ -164,47 +162,49 @@ void DynPropGetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass, + } + } + +-void DynPropSetterAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass, +- CFXJSE_Value* pObject, ++void DynPropSetterAdapter(v8::Isolate* pIsolate, ++ const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor, ++ v8::Local pObject, + ByteStringView szPropName, + CFXJSE_Value* pValue) { +- ASSERT(lpClass); +- int32_t nPropType = +- lpClass->dynPropTypeGetter == nullptr +- ? FXJSE_ClassPropType_Property +- : lpClass->dynPropTypeGetter(pObject, szPropName, false); +- if (nPropType != FXJSE_ClassPropType_Method) { +- if (lpClass->dynPropSetter) +- lpClass->dynPropSetter(pObject, szPropName, pValue); ++ DCHECK(pClassDescriptor); ++ FXJSE_ClassPropType nPropType = ++ pClassDescriptor->dynPropTypeGetter ++ ? pClassDescriptor->dynPropTypeGetter(pIsolate, pObject, szPropName, ++ false) ++ : FXJSE_ClassPropType::kProperty; ++ if (nPropType != FXJSE_ClassPropType::kMethod) { ++ if (pClassDescriptor->dynPropSetter) { ++ pClassDescriptor->dynPropSetter(pIsolate, pObject, szPropName, ++ pValue->GetValue(pIsolate)); ++ } + } + } + +-bool DynPropQueryAdapter(const FXJSE_CLASS_DESCRIPTOR* lpClass, +- CFXJSE_Value* pObject, ++bool DynPropQueryAdapter(v8::Isolate* pIsolate, ++ const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor, ++ v8::Local pObject, + ByteStringView szPropName) { +- ASSERT(lpClass); +- int32_t nPropType = +- lpClass->dynPropTypeGetter == nullptr +- ? FXJSE_ClassPropType_Property +- : lpClass->dynPropTypeGetter(pObject, szPropName, true); +- return nPropType != FXJSE_ClassPropType_None; ++ FXJSE_ClassPropType nPropType = pClassDescriptor->dynPropTypeGetter ++ ? pClassDescriptor->dynPropTypeGetter( ++ pIsolate, pObject, szPropName, true) ++ : FXJSE_ClassPropType::kProperty; ++ return nPropType != FXJSE_ClassPropType::kNone; + } + + void NamedPropertyQueryCallback( + v8::Local property, + const v8::PropertyCallbackInfo& info) { +- v8::Local thisObject = info.Holder(); +- const FXJSE_CLASS_DESCRIPTOR* lpClass = ++ const FXJSE_CLASS_DESCRIPTOR* pClass = + AsClassDescriptor(info.Data().As()->Value()); +- if (!lpClass) ++ if (!pClass) + return; + + v8::HandleScope scope(info.GetIsolate()); + v8::String::Utf8Value szPropName(info.GetIsolate(), property); + ByteStringView szFxPropName(*szPropName, szPropName.length()); +- auto lpThisValue = pdfium::MakeUnique(info.GetIsolate()); +- lpThisValue->ForceSetValue(thisObject); +- if (DynPropQueryAdapter(lpClass, lpThisValue.get(), szFxPropName)) { ++ if (DynPropQueryAdapter(info.GetIsolate(), pClass, info.Holder(), ++ szFxPropName)) { + info.GetReturnValue().Set(v8::DontDelete); + return; + } +@@ -215,40 +215,33 @@ void NamedPropertyQueryCallback( + void NamedPropertyGetterCallback( + v8::Local property, + const v8::PropertyCallbackInfo& info) { +- v8::Local thisObject = info.Holder(); +- const FXJSE_CLASS_DESCRIPTOR* lpClass = ++ const FXJSE_CLASS_DESCRIPTOR* pClass = + AsClassDescriptor(info.Data().As()->Value()); +- if (!lpClass) ++ if (!pClass) + return; + + v8::String::Utf8Value szPropName(info.GetIsolate(), property); + ByteStringView szFxPropName(*szPropName, szPropName.length()); +- auto lpThisValue = pdfium::MakeUnique(info.GetIsolate()); +- lpThisValue->ForceSetValue(thisObject); +- auto lpNewValue = pdfium::MakeUnique(info.GetIsolate()); +- DynPropGetterAdapter(lpClass, lpThisValue.get(), szFxPropName, +- lpNewValue.get()); +- info.GetReturnValue().Set(lpNewValue->DirectGetValue()); ++ auto pNewValue = std::make_unique(); ++ DynPropGetterAdapter(info.GetIsolate(), pClass, info.Holder(), szFxPropName, ++ pNewValue.get()); ++ info.GetReturnValue().Set(pNewValue->DirectGetValue()); + } + + void NamedPropertySetterCallback( + v8::Local property, + v8::Local value, + const v8::PropertyCallbackInfo& info) { +- v8::Local thisObject = info.Holder(); +- const FXJSE_CLASS_DESCRIPTOR* lpClass = ++ const FXJSE_CLASS_DESCRIPTOR* pClass = + AsClassDescriptor(info.Data().As()->Value()); +- if (!lpClass) ++ if (!pClass) + return; + + v8::String::Utf8Value szPropName(info.GetIsolate(), property); + ByteStringView szFxPropName(*szPropName, szPropName.length()); +- auto lpThisValue = pdfium::MakeUnique(info.GetIsolate()); +- lpThisValue->ForceSetValue(thisObject); +- auto lpNewValue = pdfium::MakeUnique(info.GetIsolate()); +- lpNewValue->ForceSetValue(value); +- DynPropSetterAdapter(lpClass, lpThisValue.get(), szFxPropName, +- lpNewValue.get()); ++ auto pNewValue = std::make_unique(info.GetIsolate(), value); ++ DynPropSetterAdapter(info.GetIsolate(), pClass, info.Holder(), szFxPropName, ++ pNewValue.get()); + info.GetReturnValue().Set(value); + } + +@@ -258,86 +251,89 @@ void NamedPropertyEnumeratorCallback( + } + + void SetUpNamedPropHandler(v8::Isolate* pIsolate, +- v8::Local* pObjectTemplate, +- const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition) { ++ v8::Local pObjectTemplate, ++ const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor) { + v8::NamedPropertyHandlerConfiguration configuration( +- lpClassDefinition->dynPropGetter ? NamedPropertyGetterCallback : nullptr, +- lpClassDefinition->dynPropSetter ? NamedPropertySetterCallback : nullptr, +- lpClassDefinition->dynPropTypeGetter ? NamedPropertyQueryCallback +- : nullptr, ++ pClassDescriptor->dynPropGetter ? NamedPropertyGetterCallback : nullptr, ++ pClassDescriptor->dynPropSetter ? NamedPropertySetterCallback : nullptr, ++ pClassDescriptor->dynPropTypeGetter ? NamedPropertyQueryCallback ++ : nullptr, + nullptr, NamedPropertyEnumeratorCallback, + v8::External::New(pIsolate, +- const_cast(lpClassDefinition)), ++ const_cast(pClassDescriptor)), + v8::PropertyHandlerFlags::kNonMasking); +- (*pObjectTemplate)->SetHandler(configuration); ++ pObjectTemplate->SetHandler(configuration); + } + + } // namespace + + // static + CFXJSE_Class* CFXJSE_Class::Create( +- CFXJSE_Context* lpContext, +- const FXJSE_CLASS_DESCRIPTOR* lpClassDefinition, ++ CFXJSE_Context* pContext, ++ const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor, + bool bIsJSGlobal) { +- if (!lpContext || !lpClassDefinition) ++ if (!pContext || !pClassDescriptor) + return nullptr; + + CFXJSE_Class* pExistingClass = +- lpContext->GetClassByName(lpClassDefinition->name); ++ pContext->GetClassByName(pClassDescriptor->name); + if (pExistingClass) + return pExistingClass; + +- v8::Isolate* pIsolate = lpContext->GetIsolate(); +- auto pClass = pdfium::MakeUnique(lpContext); +- pClass->m_szClassName = lpClassDefinition->name; +- pClass->m_lpClassDefinition = lpClassDefinition; ++ v8::Isolate* pIsolate = pContext->GetIsolate(); ++ auto pClass = std::make_unique(pContext); ++ pClass->m_szClassName = pClassDescriptor->name; ++ pClass->m_pClassDescriptor = pClassDescriptor; + CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); + v8::Local hFunctionTemplate = v8::FunctionTemplate::New( + pIsolate, bIsJSGlobal ? 0 : V8ConstructorCallback_Wrapper, +- v8::External::New( +- pIsolate, const_cast(lpClassDefinition))); +- hFunctionTemplate->SetClassName( +- v8::String::NewFromUtf8(pIsolate, lpClassDefinition->name, +- v8::NewStringType::kNormal) +- .ToLocalChecked()); ++ v8::External::New(pIsolate, ++ const_cast(pClassDescriptor))); ++ v8::Local classname = ++ fxv8::NewStringHelper(pIsolate, pClassDescriptor->name); ++ hFunctionTemplate->SetClassName(classname); ++ hFunctionTemplate->PrototypeTemplate()->Set( ++ v8::Symbol::GetToStringTag(pIsolate), classname, ++ static_cast(v8::ReadOnly | v8::DontEnum)); + hFunctionTemplate->InstanceTemplate()->SetInternalFieldCount(2); + v8::Local hObjectTemplate = + hFunctionTemplate->InstanceTemplate(); +- SetUpNamedPropHandler(pIsolate, &hObjectTemplate, lpClassDefinition); ++ SetUpNamedPropHandler(pIsolate, hObjectTemplate, pClassDescriptor); + +- if (lpClassDefinition->methNum) { +- for (int32_t i = 0; i < lpClassDefinition->methNum; i++) { ++ if (pClassDescriptor->methNum) { ++ for (int32_t i = 0; i < pClassDescriptor->methNum; i++) { + v8::Local fun = v8::FunctionTemplate::New( + pIsolate, V8FunctionCallback_Wrapper, + v8::External::New(pIsolate, const_cast( +- lpClassDefinition->methods + i))); ++ pClassDescriptor->methods + i))); + fun->RemovePrototype(); + hObjectTemplate->Set( +- v8::String::NewFromUtf8(pIsolate, lpClassDefinition->methods[i].name, +- v8::NewStringType::kNormal) +- .ToLocalChecked(), ++ fxv8::NewStringHelper(pIsolate, pClassDescriptor->methods[i].name), + fun, + static_cast(v8::ReadOnly | v8::DontDelete)); + } + } + + if (bIsJSGlobal) { +- v8::Local fun = v8::FunctionTemplate::New( ++ v8::Local fn = v8::FunctionTemplate::New( + pIsolate, Context_GlobalObjToString, + v8::External::New( +- pIsolate, const_cast(lpClassDefinition))); +- fun->RemovePrototype(); +- hObjectTemplate->Set(v8::String::NewFromUtf8(pIsolate, "toString", +- v8::NewStringType::kNormal) +- .ToLocalChecked(), +- fun); ++ pIsolate, const_cast(pClassDescriptor))); ++ fn->RemovePrototype(); ++ hObjectTemplate->Set(fxv8::NewStringHelper(pIsolate, "toString"), fn); + } +- pClass->m_hTemplate.Reset(lpContext->GetIsolate(), hFunctionTemplate); ++ pClass->m_hTemplate.Reset(pContext->GetIsolate(), hFunctionTemplate); + CFXJSE_Class* pResult = pClass.get(); +- lpContext->AddClass(std::move(pClass)); ++ pContext->AddClass(std::move(pClass)); + return pResult; + } + +-CFXJSE_Class::CFXJSE_Class(CFXJSE_Context* lpContext) : m_pContext(lpContext) {} ++CFXJSE_Class::CFXJSE_Class(const CFXJSE_Context* pContext) ++ : m_pContext(pContext) {} + +-CFXJSE_Class::~CFXJSE_Class() {} ++CFXJSE_Class::~CFXJSE_Class() = default; ++ ++v8::Local CFXJSE_Class::GetTemplate( ++ v8::Isolate* pIsolate) { ++ return v8::Local::New(pIsolate, m_hTemplate); ++} +diff --git a/fxjs/xfa/cfxjse_class.h b/fxjs/xfa/cfxjse_class.h +index f5bc23283..5f4d752eb 100644 +--- a/fxjs/xfa/cfxjse_class.h ++++ b/fxjs/xfa/cfxjse_class.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,31 +9,29 @@ + + #include "core/fxcrt/unowned_ptr.h" + #include "fxjs/xfa/fxjse.h" +-#include "v8/include/v8.h" ++#include "v8/include/v8-forward.h" ++#include "v8/include/v8-persistent-handle.h" + + class CFXJSE_Context; +-class CFXJSE_Value; + struct FXJSE_CLASS_DESCRIPTOR; + + class CFXJSE_Class { + public: + static CFXJSE_Class* Create(CFXJSE_Context* pContext, +- const FXJSE_CLASS_DESCRIPTOR* lpClassDefintion, ++ const FXJSE_CLASS_DESCRIPTOR* pClassDescriptor, + bool bIsJSGlobal); + +- explicit CFXJSE_Class(CFXJSE_Context* lpContext); ++ explicit CFXJSE_Class(const CFXJSE_Context* pContext); + ~CFXJSE_Class(); + +- CFXJSE_Context* GetContext() const { return m_pContext.Get(); } +- v8::Global& GetTemplate() { return m_hTemplate; } ++ bool IsName(ByteStringView name) const { return name == m_szClassName; } ++ const CFXJSE_Context* GetContext() const { return m_pContext; } ++ v8::Local GetTemplate(v8::Isolate* pIsolate); + + protected: +- friend class CFXJSE_Context; +- friend class CFXJSE_Value; +- + ByteString m_szClassName; +- UnownedPtr m_lpClassDefinition; +- UnownedPtr const m_pContext; ++ UnownedPtr m_pClassDescriptor; ++ UnownedPtr const m_pContext; + v8::Global m_hTemplate; + }; + +diff --git a/fxjs/xfa/cfxjse_context.cpp b/fxjs/xfa/cfxjse_context.cpp +index 1ff141762..970cb3d7a 100644 +--- a/fxjs/xfa/cfxjse_context.cpp ++++ b/fxjs/xfa/cfxjse_context.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,11 +9,20 @@ + #include + + #include "fxjs/cfxjs_engine.h" ++#include "fxjs/fxv8.h" + #include "fxjs/xfa/cfxjse_class.h" + #include "fxjs/xfa/cfxjse_isolatetracker.h" + #include "fxjs/xfa/cfxjse_runtimedata.h" + #include "fxjs/xfa/cfxjse_value.h" ++#include "fxjs/xfa/cjx_object.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" + #include "third_party/base/ptr_util.h" ++#include "v8/include/v8-exception.h" ++#include "v8/include/v8-function.h" ++#include "v8/include/v8-message.h" ++#include "v8/include/v8-script.h" ++#include "xfa/fxfa/parser/cxfa_thisproxy.h" + + namespace { + +@@ -51,9 +60,12 @@ const char szConsoleScript[] = + " this.log(...args);\n" + "};"; + +-// Only address matters, values are for humans debuging here. +-char g_FXJSEHostObjectTag[] = "FXJSE Host Object"; +-char g_FXJSEProxyObjectTag[] = "FXJSE Proxy Object"; ++// Only address matters, values are for humans debuging here. Keep these ++// wchar_t to prevent the compiler from doing something clever, like ++// aligning them on a byte boundary to save space, which would make them ++// incompatible for use as V8 aligned pointers. ++const wchar_t kFXJSEHostObjectTag[] = L"FXJSE Host Object"; ++const wchar_t kFXJSEProxyObjectTag[] = L"FXJSE Proxy Object"; + + v8::Local CreateReturnValue(v8::Isolate* pIsolate, + v8::TryCatch* trycatch) { +@@ -68,23 +80,18 @@ v8::Local CreateReturnValue(v8::Isolate* pIsolate, + v8::Local context = pIsolate->GetCurrentContext(); + v8::Local hException = trycatch->Exception(); + if (hException->IsObject()) { +- v8::Local hNameStr = +- v8::String::NewFromUtf8(pIsolate, "name", v8::NewStringType::kNormal) +- .ToLocalChecked(); ++ v8::Local hNameStr = fxv8::NewStringHelper(pIsolate, "name"); + v8::Local hValue = + hException.As()->Get(context, hNameStr).ToLocalChecked(); + if (hValue->IsString() || hValue->IsStringObject()) { + hReturnValue->Set(context, 0, hValue).FromJust(); + } else { + v8::Local hErrorStr = +- v8::String::NewFromUtf8(pIsolate, "Error", v8::NewStringType::kNormal) +- .ToLocalChecked(); ++ fxv8::NewStringHelper(pIsolate, "Error"); + hReturnValue->Set(context, 0, hErrorStr).FromJust(); + } +- + v8::Local hMessageStr = +- v8::String::NewFromUtf8(pIsolate, "message", v8::NewStringType::kNormal) +- .ToLocalChecked(); ++ fxv8::NewStringHelper(pIsolate, "message"); + hValue = + hException.As()->Get(context, hMessageStr).ToLocalChecked(); + if (hValue->IsString() || hValue->IsStringObject()) +@@ -92,9 +99,7 @@ v8::Local CreateReturnValue(v8::Isolate* pIsolate, + else + hReturnValue->Set(context, 1, hMessage->Get()).FromJust(); + } else { +- v8::Local hErrorStr = +- v8::String::NewFromUtf8(pIsolate, "Error", v8::NewStringType::kNormal) +- .ToLocalChecked(); ++ v8::Local hErrorStr = fxv8::NewStringHelper(pIsolate, "Error"); + hReturnValue->Set(context, 0, hErrorStr).FromJust(); + hReturnValue->Set(context, 1, hMessage->Get()).FromJust(); + } +@@ -111,65 +116,48 @@ v8::Local CreateReturnValue(v8::Isolate* pIsolate, + return hReturnValue; + } + +-class CFXJSE_ScopeUtil_IsolateHandleContext { +- public: +- explicit CFXJSE_ScopeUtil_IsolateHandleContext(CFXJSE_Context* pContext) +- : m_parent(pContext->GetIsolate()), m_cscope(pContext->GetContext()) {} +- CFXJSE_ScopeUtil_IsolateHandleContext( +- const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete; +- CFXJSE_ScopeUtil_IsolateHandleContext& operator=( +- const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete; +- +- private: +- void* operator new(size_t size) = delete; +- void operator delete(void*, size_t) = delete; +- +- CFXJSE_ScopeUtil_IsolateHandle m_parent; +- v8::Context::Scope m_cscope; +-}; +- + void FXJSE_UpdateProxyBinding(v8::Local hObject) { +- ASSERT(!hObject.IsEmpty()); +- ASSERT(hObject->InternalFieldCount() == 2); +- hObject->SetAlignedPointerInInternalField(0, g_FXJSEProxyObjectTag); ++ DCHECK(!hObject.IsEmpty()); ++ DCHECK_EQ(hObject->InternalFieldCount(), 2); ++ hObject->SetAlignedPointerInInternalField( ++ 0, const_cast(kFXJSEProxyObjectTag)); + hObject->SetAlignedPointerInInternalField(1, nullptr); + } + + } // namespace + + void FXJSE_UpdateObjectBinding(v8::Local hObject, +- CFXJSE_HostObject* lpNewBinding) { +- ASSERT(!hObject.IsEmpty()); +- ASSERT(hObject->InternalFieldCount() == 2); +- hObject->SetAlignedPointerInInternalField(0, g_FXJSEHostObjectTag); +- hObject->SetAlignedPointerInInternalField(1, lpNewBinding); ++ CFXJSE_HostObject* pNewBinding) { ++ DCHECK(!hObject.IsEmpty()); ++ DCHECK_EQ(hObject->InternalFieldCount(), 2); ++ hObject->SetAlignedPointerInInternalField( ++ 0, const_cast(kFXJSEHostObjectTag)); ++ hObject->SetAlignedPointerInInternalField(1, pNewBinding); + } + + void FXJSE_ClearObjectBinding(v8::Local hObject) { +- ASSERT(!hObject.IsEmpty()); +- ASSERT(hObject->InternalFieldCount() == 2); ++ DCHECK(!hObject.IsEmpty()); ++ DCHECK_EQ(hObject->InternalFieldCount(), 2); + hObject->SetAlignedPointerInInternalField(0, nullptr); + hObject->SetAlignedPointerInInternalField(1, nullptr); + } + +-CFXJSE_HostObject* FXJSE_RetrieveObjectBinding( +- v8::Local hJSObject) { +- ASSERT(!hJSObject.IsEmpty()); +- if (!hJSObject->IsObject()) ++CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(v8::Local hValue) { ++ if (!fxv8::IsObject(hValue)) + return nullptr; + +- v8::Local hObject = hJSObject; ++ v8::Local hObject = hValue.As(); + if (hObject->InternalFieldCount() != 2 || +- hObject->GetAlignedPointerFromInternalField(0) == g_FXJSEProxyObjectTag) { ++ hObject->GetAlignedPointerFromInternalField(0) == kFXJSEProxyObjectTag) { + v8::Local hProtoObject = hObject->GetPrototype(); +- if (hProtoObject.IsEmpty() || !hProtoObject->IsObject()) ++ if (!fxv8::IsObject(hProtoObject)) + return nullptr; + + hObject = hProtoObject.As(); + if (hObject->InternalFieldCount() != 2) + return nullptr; + } +- if (hObject->GetAlignedPointerFromInternalField(0) != g_FXJSEHostObjectTag) ++ if (hObject->GetAlignedPointerFromInternalField(0) != kFXJSEHostObjectTag) + return nullptr; + + return static_cast( +@@ -180,31 +168,27 @@ CFXJSE_HostObject* FXJSE_RetrieveObjectBinding( + std::unique_ptr CFXJSE_Context::Create( + v8::Isolate* pIsolate, + const FXJSE_CLASS_DESCRIPTOR* pGlobalClass, +- CFXJSE_HostObject* pGlobalObject) { ++ CFXJSE_HostObject* pGlobalObject, ++ CXFA_ThisProxy* pProxy) { + CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); +- auto pContext = pdfium::MakeUnique(pIsolate); + ++ // Private constructor. ++ auto pContext = pdfium::WrapUnique(new CFXJSE_Context(pIsolate, pProxy)); + v8::Local hObjectTemplate; + if (pGlobalClass) { + CFXJSE_Class* pGlobalClassObj = + CFXJSE_Class::Create(pContext.get(), pGlobalClass, true); +- ASSERT(pGlobalClassObj); +- v8::Local hFunctionTemplate = +- v8::Local::New(pIsolate, +- pGlobalClassObj->m_hTemplate); +- hObjectTemplate = hFunctionTemplate->InstanceTemplate(); ++ hObjectTemplate = ++ pGlobalClassObj->GetTemplate(pIsolate)->InstanceTemplate(); + } else { + hObjectTemplate = v8::ObjectTemplate::New(pIsolate); + hObjectTemplate->SetInternalFieldCount(2); + } +- hObjectTemplate->Set( +- v8::Symbol::GetToStringTag(pIsolate), +- v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal) +- .ToLocalChecked()); ++ hObjectTemplate->Set(v8::Symbol::GetToStringTag(pIsolate), ++ fxv8::NewStringHelper(pIsolate, "global")); + + v8::Local hNewContext = + v8::Context::New(pIsolate, nullptr, hObjectTemplate); +- + v8::Local pThisProxy = hNewContext->Global(); + FXJSE_UpdateProxyBinding(pThisProxy); + +@@ -212,25 +196,25 @@ std::unique_ptr CFXJSE_Context::Create( + FXJSE_UpdateObjectBinding(pThis, pGlobalObject); + + v8::Local hRootContext = v8::Local::New( +- pIsolate, CFXJSE_RuntimeData::Get(pIsolate)->m_hRootContext); ++ pIsolate, CFXJSE_RuntimeData::Get(pIsolate)->GetRootContext()); + hNewContext->SetSecurityToken(hRootContext->GetSecurityToken()); + pContext->m_hContext.Reset(pIsolate, hNewContext); + return pContext; + } + +-CFXJSE_Context::CFXJSE_Context(v8::Isolate* pIsolate) : m_pIsolate(pIsolate) {} ++CFXJSE_Context::CFXJSE_Context(v8::Isolate* pIsolate, CXFA_ThisProxy* pProxy) ++ : m_pIsolate(pIsolate), m_pProxy(pProxy) {} + +-CFXJSE_Context::~CFXJSE_Context() {} ++CFXJSE_Context::~CFXJSE_Context() = default; + +-std::unique_ptr CFXJSE_Context::GetGlobalObject() { +- auto pValue = pdfium::MakeUnique(GetIsolate()); +- CFXJSE_ScopeUtil_IsolateHandleContext scope(this); ++v8::Local CFXJSE_Context::GetGlobalObject() { ++ v8::Isolate::Scope isolate_scope(GetIsolate()); ++ v8::EscapableHandleScope handle_scope(GetIsolate()); + v8::Local hContext = + v8::Local::New(GetIsolate(), m_hContext); +- v8::Local hGlobalObject = ++ v8::Local result = + hContext->Global()->GetPrototype().As(); +- pValue->ForceSetValue(hGlobalObject); +- return pValue; ++ return handle_scope.Escape(result); + } + + v8::Local CFXJSE_Context::GetContext() { +@@ -245,64 +229,58 @@ CFXJSE_Class* CFXJSE_Context::GetClassByName(ByteStringView szName) const { + auto pClass = + std::find_if(m_rgClasses.begin(), m_rgClasses.end(), + [szName](const std::unique_ptr& item) { +- return szName == item->m_szClassName; ++ return item->IsName(szName); + }); + return pClass != m_rgClasses.end() ? pClass->get() : nullptr; + } + + void CFXJSE_Context::EnableCompatibleMode() { +- ExecuteScript(szCompatibleModeScript, nullptr, nullptr); +- ExecuteScript(szConsoleScript, nullptr, nullptr); ++ ExecuteScript(szCompatibleModeScript, nullptr, v8::Local()); ++ ExecuteScript(szConsoleScript, nullptr, v8::Local()); + } + +-bool CFXJSE_Context::ExecuteScript(const char* szScript, +- CFXJSE_Value* lpRetValue, +- CFXJSE_Value* lpNewThisObject) { ++bool CFXJSE_Context::ExecuteScript(ByteStringView bsScript, ++ CFXJSE_Value* pRetValue, ++ v8::Local hNewThis) { + CFXJSE_ScopeUtil_IsolateHandleContext scope(this); + v8::Local hContext = GetIsolate()->GetCurrentContext(); + v8::TryCatch trycatch(GetIsolate()); + v8::Local hScriptString = +- v8::String::NewFromUtf8(GetIsolate(), szScript, +- v8::NewStringType::kNormal) +- .ToLocalChecked(); +- if (!lpNewThisObject) { ++ fxv8::NewStringHelper(GetIsolate(), bsScript); ++ if (hNewThis.IsEmpty()) { + v8::Local hScript; + if (v8::Script::Compile(hContext, hScriptString).ToLocal(&hScript)) { +- ASSERT(!trycatch.HasCaught()); ++ CHECK(!trycatch.HasCaught()); + v8::Local hValue; + if (hScript->Run(hContext).ToLocal(&hValue)) { +- ASSERT(!trycatch.HasCaught()); +- if (lpRetValue) +- lpRetValue->ForceSetValue(hValue); ++ CHECK(!trycatch.HasCaught()); ++ if (pRetValue) ++ pRetValue->ForceSetValue(GetIsolate(), hValue); + return true; + } + } +- if (lpRetValue) +- lpRetValue->ForceSetValue(CreateReturnValue(GetIsolate(), &trycatch)); ++ if (pRetValue) { ++ pRetValue->ForceSetValue(GetIsolate(), ++ CreateReturnValue(GetIsolate(), &trycatch)); ++ } + return false; + } + +- v8::Local hNewThis = v8::Local::New( +- GetIsolate(), lpNewThisObject->DirectGetValue()); +- ASSERT(!hNewThis.IsEmpty()); +- v8::Local hEval = +- v8::String::NewFromUtf8(GetIsolate(), +- "(function () { return eval(arguments[0]); })", +- v8::NewStringType::kNormal) +- .ToLocalChecked(); ++ v8::Local hEval = fxv8::NewStringHelper( ++ GetIsolate(), "(function () { return eval(arguments[0]); })"); + v8::Local hWrapper = + v8::Script::Compile(hContext, hEval).ToLocalChecked(); + v8::Local hWrapperValue; + if (hWrapper->Run(hContext).ToLocal(&hWrapperValue)) { +- ASSERT(!trycatch.HasCaught()); ++ CHECK(!trycatch.HasCaught()); ++ CHECK(hWrapperValue->IsFunction()); + v8::Local hWrapperFn = hWrapperValue.As(); + v8::Local rgArgs[] = {hScriptString}; + v8::Local hValue; +- if (hWrapperFn->Call(hContext, hNewThis.As(), 1, rgArgs) +- .ToLocal(&hValue)) { +- ASSERT(!trycatch.HasCaught()); +- if (lpRetValue) +- lpRetValue->ForceSetValue(hValue); ++ if (hWrapperFn->Call(hContext, hNewThis, 1, rgArgs).ToLocal(&hValue)) { ++ DCHECK(!trycatch.HasCaught()); ++ if (pRetValue) ++ pRetValue->ForceSetValue(GetIsolate(), hValue); + return true; + } + } +@@ -321,7 +299,9 @@ bool CFXJSE_Context::ExecuteScript(const char* szScript, + } + #endif // NDEBUG + +- if (lpRetValue) +- lpRetValue->ForceSetValue(CreateReturnValue(GetIsolate(), &trycatch)); ++ if (pRetValue) { ++ pRetValue->ForceSetValue(GetIsolate(), ++ CreateReturnValue(GetIsolate(), &trycatch)); ++ } + return false; + } +diff --git a/fxjs/xfa/cfxjse_context.h b/fxjs/xfa/cfxjse_context.h +index b519e77b8..4217b2b92 100644 +--- a/fxjs/xfa/cfxjse_context.h ++++ b/fxjs/xfa/cfxjse_context.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,14 +10,16 @@ + #include + #include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/bytestring.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "v8/include/v8.h" ++#include "v8/include/cppgc/persistent.h" ++#include "v8/include/v8-forward.h" ++#include "v8/include/v8-persistent-handle.h" + +-class CFXJS_Engine; + class CFXJSE_Class; + class CFXJSE_HostObject; + class CFXJSE_Value; ++class CXFA_ThisProxy; + struct FXJSE_CLASS_DESCRIPTOR; + + class CFXJSE_Context { +@@ -25,34 +27,39 @@ class CFXJSE_Context { + static std::unique_ptr Create( + v8::Isolate* pIsolate, + const FXJSE_CLASS_DESCRIPTOR* pGlobalClass, +- CFXJSE_HostObject* pGlobalObject); ++ CFXJSE_HostObject* pGlobalObject, ++ CXFA_ThisProxy* pProxy); + +- explicit CFXJSE_Context(v8::Isolate* pIsolate); + ~CFXJSE_Context(); + +- v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); } ++ v8::Isolate* GetIsolate() const { return m_pIsolate; } + v8::Local GetContext(); +- std::unique_ptr GetGlobalObject(); ++ v8::Local GetGlobalObject(); ++ + void AddClass(std::unique_ptr pClass); + CFXJSE_Class* GetClassByName(ByteStringView szName) const; + void EnableCompatibleMode(); +- bool ExecuteScript(const char* szScript, +- CFXJSE_Value* lpRetValue, +- CFXJSE_Value* lpNewThisObject); ++ ++ // Note: `pNewThisObject` may be empty. ++ bool ExecuteScript(ByteStringView bsScript, ++ CFXJSE_Value* pRetValue, ++ v8::Local pNewThisObject); + + private: ++ CFXJSE_Context(v8::Isolate* pIsolate, CXFA_ThisProxy* pProxy); + CFXJSE_Context(const CFXJSE_Context&) = delete; + CFXJSE_Context& operator=(const CFXJSE_Context&) = delete; + + v8::Global m_hContext; + UnownedPtr m_pIsolate; + std::vector> m_rgClasses; ++ cppgc::Persistent m_pProxy; + }; + + void FXJSE_UpdateObjectBinding(v8::Local hObject, +- CFXJSE_HostObject* lpNewBinding); ++ CFXJSE_HostObject* pNewBinding); + + void FXJSE_ClearObjectBinding(v8::Local hJSObject); +-CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(v8::Local hJSObject); ++CFXJSE_HostObject* FXJSE_RetrieveObjectBinding(v8::Local hValue); + + #endif // FXJS_XFA_CFXJSE_CONTEXT_H_ +diff --git a/fxjs/xfa/cfxjse_engine.cpp b/fxjs/xfa/cfxjse_engine.cpp +index 9be249808..601ffb42d 100644 +--- a/fxjs/xfa/cfxjse_engine.cpp ++++ b/fxjs/xfa/cfxjse_engine.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,34 +9,38 @@ + #include + + #include "core/fxcrt/autorestorer.h" +-#include "core/fxcrt/cfx_widetextbuf.h" + #include "core/fxcrt/fx_extension.h" ++#include "core/fxcrt/stl_util.h" ++#include "core/fxcrt/widetext_buffer.h" + #include "fxjs/cjs_runtime.h" ++#include "fxjs/fxv8.h" + #include "fxjs/xfa/cfxjse_class.h" + #include "fxjs/xfa/cfxjse_context.h" + #include "fxjs/xfa/cfxjse_formcalc_context.h" ++#include "fxjs/xfa/cfxjse_isolatetracker.h" ++#include "fxjs/xfa/cfxjse_nodehelper.h" + #include "fxjs/xfa/cfxjse_resolveprocessor.h" + #include "fxjs/xfa/cfxjse_value.h" + #include "fxjs/xfa/cjx_object.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/containers/contains.h" ++#include "v8/include/v8-function-callback.h" ++#include "v8/include/v8-function.h" ++#include "v8/include/v8-object.h" + #include "xfa/fxfa/cxfa_eventparam.h" + #include "xfa/fxfa/cxfa_ffdoc.h" + #include "xfa/fxfa/cxfa_ffnotify.h" + #include "xfa/fxfa/parser/cxfa_document.h" + #include "xfa/fxfa/parser/cxfa_localemgr.h" + #include "xfa/fxfa/parser/cxfa_node.h" +-#include "xfa/fxfa/parser/cxfa_nodehelper.h" + #include "xfa/fxfa/parser/cxfa_object.h" + #include "xfa/fxfa/parser/cxfa_thisproxy.h" +-#include "xfa/fxfa/parser/cxfa_treelist.h" ++#include "xfa/fxfa/parser/cxfa_variables.h" + #include "xfa/fxfa/parser/xfa_basic_data.h" +-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h" + #include "xfa/fxfa/parser/xfa_utils.h" + + using pdfium::fxjse::kClassTag; + +-const FXJSE_CLASS_DESCRIPTOR GlobalClassDescriptor = { ++const FXJSE_CLASS_DESCRIPTOR kGlobalClassDescriptor = { + kClassTag, // tag + "Root", // name + nullptr, // methods +@@ -47,7 +51,7 @@ const FXJSE_CLASS_DESCRIPTOR GlobalClassDescriptor = { + CFXJSE_Engine::NormalMethodCall, + }; + +-const FXJSE_CLASS_DESCRIPTOR NormalClassDescriptor = { ++const FXJSE_CLASS_DESCRIPTOR kNormalClassDescriptor = { + kClassTag, // tag + "XFAObject", // name + nullptr, // methods +@@ -58,7 +62,7 @@ const FXJSE_CLASS_DESCRIPTOR NormalClassDescriptor = { + CFXJSE_Engine::NormalMethodCall, + }; + +-const FXJSE_CLASS_DESCRIPTOR VariablesClassDescriptor = { ++const FXJSE_CLASS_DESCRIPTOR kVariablesClassDescriptor = { + kClassTag, // tag + "XFAScriptObject", // name + nullptr, // methods +@@ -73,28 +77,46 @@ namespace { + + const char kFormCalcRuntime[] = "pfm_rt"; + +-CXFA_ThisProxy* ToThisProxy(CFXJSE_Value* pValue) { +- CFXJSE_HostObject* pHostObject = pValue->ToHostObject(); +- return pHostObject ? ToThisProxy(pHostObject->AsCXFAObject()) : nullptr; +-} +- + } // namespace + ++CFXJSE_Engine::ResolveResult::ResolveResult() = default; ++ ++CFXJSE_Engine::ResolveResult::ResolveResult(const ResolveResult& that) = ++ default; ++ ++CFXJSE_Engine::ResolveResult& CFXJSE_Engine::ResolveResult::operator=( ++ const ResolveResult& that) = default; ++ ++CFXJSE_Engine::ResolveResult::~ResolveResult() = default; ++ + // static + CXFA_Object* CFXJSE_Engine::ToObject( + const v8::FunctionCallbackInfo& info) { +- if (!info.Holder()->IsObject()) ++ return ToObject(info.GetIsolate(), info.Holder()); ++} ++ ++// static ++CXFA_Object* CFXJSE_Engine::ToObject(v8::Isolate* pIsolate, ++ v8::Local value) { ++ if (!value->IsObject()) + return nullptr; + +- CFXJSE_HostObject* pHostObj = +- FXJSE_RetrieveObjectBinding(info.Holder().As()); +- return pHostObj ? pHostObj->AsCXFAObject() : nullptr; ++ return ToObject(FXJSE_RetrieveObjectBinding(value.As())); + } + + // static. +-CXFA_Object* CFXJSE_Engine::ToObject(CFXJSE_Value* pValue) { +- CFXJSE_HostObject* pHostObj = pValue->ToHostObject(); +- return pHostObj ? pHostObj->AsCXFAObject() : nullptr; ++CXFA_Object* CFXJSE_Engine::ToObject(v8::Isolate* pIsolate, ++ CFXJSE_Value* pValue) { ++ return ToObject(pValue->ToHostObject(pIsolate)); ++} ++ ++// static ++CXFA_Object* CFXJSE_Engine::ToObject(CFXJSE_HostObject* pHostObj) { ++ if (!pHostObj) ++ return nullptr; ++ ++ CJX_Object* pJSObject = pHostObj->AsCJXObject(); ++ return pJSObject ? pJSObject->GetXFAObject() : nullptr; + } + + CFXJSE_Engine::CFXJSE_Engine(CXFA_Document* pDocument, +@@ -103,311 +125,352 @@ CFXJSE_Engine::CFXJSE_Engine(CXFA_Document* pDocument, + m_pSubordinateRuntime(fxjs_runtime), + m_pDocument(pDocument), + m_JsContext(CFXJSE_Context::Create(fxjs_runtime->GetIsolate(), +- &GlobalClassDescriptor, +- pDocument->GetRoot())), +- m_ResolveProcessor(pdfium::MakeUnique()) { ++ &kGlobalClassDescriptor, ++ pDocument->GetRoot()->JSObject(), ++ nullptr)), ++ m_NodeHelper(std::make_unique()), ++ m_ResolveProcessor( ++ std::make_unique(this, m_NodeHelper.get())) { + RemoveBuiltInObjs(m_JsContext.get()); + m_JsContext->EnableCompatibleMode(); + + // Don't know if this can happen before we remove the builtin objs and set + // compatibility mode. + m_pJsClass = +- CFXJSE_Class::Create(m_JsContext.get(), &NormalClassDescriptor, false); ++ CFXJSE_Class::Create(m_JsContext.get(), &kNormalClassDescriptor, false); + } + + CFXJSE_Engine::~CFXJSE_Engine() { +- for (const auto& pair : m_mapVariableToContext) +- delete ToThisProxy(pair.second->GetGlobalObject().get()); ++ // This is what ensures that the v8 object bound to a CJX_Object ++ // no longer retains that binding since it will outlive that object. ++ CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext()); ++ for (const auto& pair : m_mapObjectToObject) { ++ const v8::Global& binding = pair.second; ++ FXJSE_ClearObjectBinding(v8::Local::New(GetIsolate(), binding)); ++ } ++} ++ ++CFXJSE_Engine::EventParamScope::EventParamScope(CFXJSE_Engine* pEngine, ++ CXFA_Node* pTarget, ++ CXFA_EventParam* pEventParam) ++ : m_pEngine(pEngine), ++ m_pPrevTarget(pEngine->GetEventTarget()), ++ m_pPrevEventParam(pEngine->GetEventParam()) { ++ m_pEngine->m_pTarget = pTarget; ++ m_pEngine->m_eventParam = pEventParam; ++} + +- for (const auto& pair : m_mapObjectToValue) +- pair.second->ClearHostObject(); ++CFXJSE_Engine::EventParamScope::~EventParamScope() { ++ m_pEngine->m_pTarget = m_pPrevTarget; ++ m_pEngine->m_eventParam = m_pPrevEventParam; + } + + bool CFXJSE_Engine::RunScript(CXFA_Script::Type eScriptType, + WideStringView wsScript, + CFXJSE_Value* hRetValue, + CXFA_Object* pThisObject) { +- ByteString btScript; ++ CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext()); + AutoRestorer typeRestorer(&m_eScriptType); + m_eScriptType = eScriptType; ++ ++ ByteString btScript; + if (eScriptType == CXFA_Script::Type::Formcalc) { +- if (!m_FM2JSContext) { +- m_FM2JSContext = pdfium::MakeUnique( ++ if (!m_FormCalcContext) { ++ m_FormCalcContext = std::make_unique( + GetIsolate(), m_JsContext.get(), m_pDocument.Get()); + } +- CFX_WideTextBuf wsJavaScript; +- if (!CFXJSE_FormCalcContext::Translate(wsScript, &wsJavaScript)) { +- hRetValue->SetUndefined(); ++ absl::optional wsJavaScript = ++ CFXJSE_FormCalcContext::Translate(m_pDocument->GetHeap(), wsScript); ++ if (!wsJavaScript.has_value()) { ++ hRetValue->SetUndefined(GetIsolate()); + return false; + } +- btScript = FX_UTF8Encode(wsJavaScript.AsStringView()); ++ btScript = FX_UTF8Encode(wsJavaScript.value().AsStringView()); + } else { + btScript = FX_UTF8Encode(wsScript); + } +- AutoRestorer> nodeRestorer(&m_pThisObject); ++ AutoRestorer> nodeRestorer(&m_pThisObject); + m_pThisObject = pThisObject; + +- CFXJSE_Value* pValue = +- pThisObject ? GetOrCreateJSBindingFromMap(pThisObject) : nullptr; +- IJS_Runtime::ScopedEventContext ctx(m_pSubordinateRuntime.Get()); +- return m_JsContext->ExecuteScript(btScript.c_str(), hRetValue, pValue); ++ v8::Local pThisBinding; ++ if (pThisObject) ++ pThisBinding = GetOrCreateJSBindingFromMap(pThisObject); ++ ++ IJS_Runtime::ScopedEventContext ctx(m_pSubordinateRuntime); ++ return m_JsContext->ExecuteScript(btScript.AsStringView(), hRetValue, ++ pThisBinding); + } + + bool CFXJSE_Engine::QueryNodeByFlag(CXFA_Node* refNode, + WideStringView propname, +- CFXJSE_Value* pValue, +- uint32_t dwFlag, +- bool bSetting) { ++ v8::Local* pValue, ++ Mask dwFlag) { + if (!refNode) + return false; + +- XFA_RESOLVENODE_RS resolveRs; +- if (!ResolveObjects(refNode, propname, &resolveRs, dwFlag, nullptr)) ++ absl::optional maybeResult = ++ ResolveObjects(refNode, propname, dwFlag); ++ if (!maybeResult.has_value()) + return false; +- if (resolveRs.dwFlags == XFA_ResolveNode_RSType_Nodes) { +- pValue->Assign( +- GetOrCreateJSBindingFromMap(resolveRs.objects.front().Get())); ++ ++ if (maybeResult.value().type == ResolveResult::Type::kNodes) { ++ *pValue = ++ GetOrCreateJSBindingFromMap(maybeResult.value().objects.front().Get()); + return true; + } +- if (resolveRs.dwFlags == XFA_ResolveNode_RSType_Attribute && +- resolveRs.script_attribute.callback) { +- CJX_Object* jsObject = resolveRs.objects.front()->JSObject(); +- (*resolveRs.script_attribute.callback)( +- jsObject, pValue, bSetting, resolveRs.script_attribute.attribute); ++ if (maybeResult.value().type == ResolveResult::Type::kAttribute && ++ maybeResult.value().script_attribute.callback) { ++ CJX_Object* jsObject = maybeResult.value().objects.front()->JSObject(); ++ (*maybeResult.value().script_attribute.callback)( ++ GetIsolate(), jsObject, pValue, false, ++ maybeResult.value().script_attribute.attribute); ++ } ++ return true; ++} ++ ++bool CFXJSE_Engine::UpdateNodeByFlag(CXFA_Node* refNode, ++ WideStringView propname, ++ v8::Local pValue, ++ Mask dwFlag) { ++ if (!refNode) ++ return false; ++ ++ absl::optional maybeResult = ++ ResolveObjects(refNode, propname, dwFlag); ++ if (!maybeResult.has_value()) ++ return false; ++ ++ if (maybeResult.value().type == ResolveResult::Type::kAttribute && ++ maybeResult.value().script_attribute.callback) { ++ CJX_Object* jsObject = maybeResult.value().objects.front()->JSObject(); ++ (*maybeResult.value().script_attribute.callback)( ++ GetIsolate(), jsObject, &pValue, true, ++ maybeResult.value().script_attribute.attribute); + } + return true; + } + + // static +-void CFXJSE_Engine::GlobalPropertySetter(CFXJSE_Value* pObject, ++void CFXJSE_Engine::GlobalPropertySetter(v8::Isolate* pIsolate, ++ v8::Local pObject, + ByteStringView szPropName, +- CFXJSE_Value* pValue) { +- CXFA_Object* lpOrginalNode = ToObject(pObject); +- CXFA_Document* pDoc = lpOrginalNode->GetDocument(); +- CFXJSE_Engine* lpScriptContext = pDoc->GetScriptContext(); +- CXFA_Node* pRefNode = ToNode(lpScriptContext->GetThisObject()); +- if (lpOrginalNode->IsThisProxy()) +- pRefNode = ToNode(lpScriptContext->GetVariablesThis(lpOrginalNode, false)); ++ v8::Local pValue) { ++ CXFA_Object* pOriginalNode = ToObject(pIsolate, pObject); ++ CXFA_Document* pDoc = pOriginalNode->GetDocument(); ++ CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext(); ++ CXFA_Node* pRefNode = ToNode(pScriptContext->GetThisObject()); ++ if (pOriginalNode->IsThisProxy()) ++ pRefNode = ToNode(pScriptContext->GetVariablesThis(pOriginalNode)); + + WideString wsPropName = WideString::FromUTF8(szPropName); +- if (lpScriptContext->QueryNodeByFlag( ++ if (pScriptContext->UpdateNodeByFlag( + pRefNode, wsPropName.AsStringView(), pValue, +- XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings | +- XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties | +- XFA_RESOLVENODE_Attributes, +- true)) { ++ Mask{ ++ XFA_ResolveFlag::kParent, XFA_ResolveFlag::kSiblings, ++ XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kProperties, ++ XFA_ResolveFlag::kAttributes})) { + return; + } +- if (lpOrginalNode->IsThisProxy() && pValue && pValue->IsUndefined()) { +- pObject->DeleteObjectProperty(szPropName); ++ if (pOriginalNode->IsThisProxy() && fxv8::IsUndefined(pValue)) { ++ fxv8::ReentrantDeleteObjectPropertyHelper(pScriptContext->GetIsolate(), ++ pObject, szPropName); + return; + } + CXFA_FFNotify* pNotify = pDoc->GetNotify(); + if (!pNotify) + return; + +- CXFA_FFDoc* hDoc = pNotify->GetHDOC(); +- auto* pCJSRuntime = +- static_cast(hDoc->GetDocEnvironment()->GetIJSRuntime(hDoc)); ++ CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); ++ auto* pCJSRuntime = static_cast(hDoc->GetIJSRuntime()); + if (!pCJSRuntime) + return; + +- v8::HandleScope handle_scope(lpScriptContext->GetIsolate()); + IJS_Runtime::ScopedEventContext pContext(pCJSRuntime); +- pCJSRuntime->SetValueByNameInGlobalObject( +- szPropName, v8::Local::New(lpScriptContext->GetIsolate(), +- pValue->DirectGetValue())); ++ pCJSRuntime->SetValueByNameInGlobalObject(szPropName, pValue); + } + + // static +-void CFXJSE_Engine::GlobalPropertyGetter(CFXJSE_Value* pObject, +- ByteStringView szPropName, +- CFXJSE_Value* pValue) { +- CXFA_Object* pOriginalObject = ToObject(pObject); ++v8::Local CFXJSE_Engine::GlobalPropertyGetter( ++ v8::Isolate* pIsolate, ++ v8::Local pObject, ++ ByteStringView szPropName) { ++ CXFA_Object* pOriginalObject = ToObject(pIsolate, pObject); + CXFA_Document* pDoc = pOriginalObject->GetDocument(); +- CFXJSE_Engine* lpScriptContext = pDoc->GetScriptContext(); ++ CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext(); + WideString wsPropName = WideString::FromUTF8(szPropName); + +- pValue->SetUndefined(); // Assume failure. +- if (lpScriptContext->GetType() == CXFA_Script::Type::Formcalc) { +- if (szPropName == kFormCalcRuntime) { +- lpScriptContext->m_FM2JSContext->GlobalPropertyGetter(pValue); +- return; +- } +- XFA_HashCode uHashCode = static_cast( +- FX_HashCode_GetW(wsPropName.AsStringView(), false)); ++ // Assume failure. ++ v8::Local pValue = fxv8::NewUndefinedHelper(pIsolate); ++ ++ if (pScriptContext->GetType() == CXFA_Script::Type::Formcalc) { ++ if (szPropName == kFormCalcRuntime) ++ return pScriptContext->m_FormCalcContext->GlobalPropertyGetter(); ++ ++ XFA_HashCode uHashCode = ++ static_cast(FX_HashCode_GetW(wsPropName.AsStringView())); + if (uHashCode != XFA_HASHCODE_Layout) { + CXFA_Object* pObj = +- lpScriptContext->GetDocument()->GetXFAObject(uHashCode); +- if (pObj) { +- pValue->Assign(lpScriptContext->GetOrCreateJSBindingFromMap(pObj)); +- return; +- } ++ pScriptContext->GetDocument()->GetXFAObject(uHashCode); ++ if (pObj) ++ return pScriptContext->GetOrCreateJSBindingFromMap(pObj); + } + } + +- CXFA_Node* pRefNode = ToNode(lpScriptContext->GetThisObject()); +- if (pOriginalObject->IsThisProxy()) { +- pRefNode = +- ToNode(lpScriptContext->GetVariablesThis(pOriginalObject, false)); +- } +- if (lpScriptContext->QueryNodeByFlag( +- pRefNode, wsPropName.AsStringView(), pValue, +- XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties | +- XFA_RESOLVENODE_Attributes, +- false)) { +- return; ++ CXFA_Node* pRefNode = ToNode(pScriptContext->GetThisObject()); ++ if (pOriginalObject->IsThisProxy()) ++ pRefNode = ToNode(pScriptContext->GetVariablesThis(pOriginalObject)); ++ ++ if (pScriptContext->QueryNodeByFlag( ++ pRefNode, wsPropName.AsStringView(), &pValue, ++ Mask{XFA_ResolveFlag::kChildren, ++ XFA_ResolveFlag::kProperties, ++ XFA_ResolveFlag::kAttributes})) { ++ return pValue; + } +- if (lpScriptContext->QueryNodeByFlag( +- pRefNode, wsPropName.AsStringView(), pValue, +- XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings, false)) { +- return; ++ if (pScriptContext->QueryNodeByFlag( ++ pRefNode, wsPropName.AsStringView(), &pValue, ++ Mask{XFA_ResolveFlag::kParent, ++ XFA_ResolveFlag::kSiblings})) { ++ return pValue; + } + + CXFA_Object* pScriptObject = +- lpScriptContext->GetVariablesThis(pOriginalObject, true); +- if (pScriptObject && lpScriptContext->QueryVariableValue( +- pScriptObject->AsNode(), szPropName, pValue, true)) { +- return; ++ pScriptContext->GetVariablesScript(pOriginalObject); ++ if (pScriptObject && pScriptContext->QueryVariableValue( ++ CXFA_Script::FromNode(pScriptObject->AsNode()), ++ szPropName, &pValue)) { ++ return pValue; + } + + CXFA_FFNotify* pNotify = pDoc->GetNotify(); + if (!pNotify) +- return; ++ return pValue; + +- CXFA_FFDoc* hDoc = pNotify->GetHDOC(); +- auto* pCJSRuntime = +- static_cast(hDoc->GetDocEnvironment()->GetIJSRuntime(hDoc)); ++ CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); ++ auto* pCJSRuntime = static_cast(hDoc->GetIJSRuntime()); + if (!pCJSRuntime) +- return; ++ return pValue; + +- v8::HandleScope handle_scope(lpScriptContext->GetIsolate()); + IJS_Runtime::ScopedEventContext pContext(pCJSRuntime); +- v8::Local temp_value; +- if (!pCJSRuntime->GetValueByNameFromGlobalObject(szPropName, &temp_value)) +- return; +- +- if (temp_value.IsEmpty()) +- return; ++ v8::Local temp_value = ++ pCJSRuntime->GetValueByNameFromGlobalObject(szPropName); + +- pValue->ForceSetValue(temp_value); ++ return !temp_value.IsEmpty() ? temp_value : pValue; + } + +-int32_t CFXJSE_Engine::GlobalPropTypeGetter(CFXJSE_Value* pOriginalValue, +- ByteStringView szPropName, +- bool bQueryIn) { +- CXFA_Object* pObject = ToObject(pOriginalValue); ++// static ++FXJSE_ClassPropType CFXJSE_Engine::GlobalPropTypeGetter( ++ v8::Isolate* pIsolate, ++ v8::Local pHolder, ++ ByteStringView szPropName, ++ bool bQueryIn) { ++ CXFA_Object* pObject = ToObject(pIsolate, pHolder); + if (!pObject) +- return FXJSE_ClassPropType_None; ++ return FXJSE_ClassPropType::kNone; + +- CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext(); +- pObject = lpScriptContext->GetVariablesThis(pObject, false); ++ CFXJSE_Engine* pScriptContext = pObject->GetDocument()->GetScriptContext(); ++ pObject = pScriptContext->GetVariablesThis(pObject); + WideString wsPropName = WideString::FromUTF8(szPropName); + if (pObject->JSObject()->HasMethod(wsPropName)) +- return FXJSE_ClassPropType_Method; ++ return FXJSE_ClassPropType::kMethod; + +- return FXJSE_ClassPropType_Property; ++ return FXJSE_ClassPropType::kProperty; + } + + // static +-void CFXJSE_Engine::NormalPropertyGetter(CFXJSE_Value* pOriginalValue, +- ByteStringView szPropName, +- CFXJSE_Value* pReturnValue) { +- pReturnValue->SetUndefined(); // Assume failure. +- CXFA_Object* pOriginalObject = ToObject(pOriginalValue); ++v8::Local CFXJSE_Engine::NormalPropertyGetter( ++ v8::Isolate* pIsolate, ++ v8::Local pHolder, ++ ByteStringView szPropName) { ++ CXFA_Object* pOriginalObject = ToObject(pIsolate, pHolder); + if (!pOriginalObject) +- return; ++ return fxv8::NewUndefinedHelper(pIsolate); + +- WideString wsPropName = WideString::FromUTF8(szPropName); +- CFXJSE_Engine* lpScriptContext = ++ CFXJSE_Engine* pScriptContext = + pOriginalObject->GetDocument()->GetScriptContext(); +- CXFA_Object* pObject = +- lpScriptContext->GetVariablesThis(pOriginalObject, false); ++ ++ WideString wsPropName = WideString::FromUTF8(szPropName); + if (wsPropName.EqualsASCII("xfa")) { +- CFXJSE_Value* pValue = lpScriptContext->GetOrCreateJSBindingFromMap( +- lpScriptContext->GetDocument()->GetRoot()); +- pReturnValue->Assign(pValue); +- return; ++ return pScriptContext->GetOrCreateJSBindingFromMap( ++ pScriptContext->GetDocument()->GetRoot()); + } + +- bool bRet = lpScriptContext->QueryNodeByFlag( +- ToNode(pObject), wsPropName.AsStringView(), pReturnValue, +- XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties | +- XFA_RESOLVENODE_Attributes, +- false); +- if (bRet) +- return; +- +- if (pObject == lpScriptContext->GetThisObject() || +- (lpScriptContext->GetType() == CXFA_Script::Type::Javascript && +- !lpScriptContext->IsStrictScopeInJavaScript())) { +- bRet = lpScriptContext->QueryNodeByFlag( +- ToNode(pObject), wsPropName.AsStringView(), pReturnValue, +- XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings, false); ++ v8::Local pReturnValue = fxv8::NewUndefinedHelper(pIsolate); ++ CXFA_Object* pObject = pScriptContext->GetVariablesThis(pOriginalObject); ++ CXFA_Node* pRefNode = ToNode(pObject); ++ if (pScriptContext->QueryNodeByFlag( ++ pRefNode, wsPropName.AsStringView(), &pReturnValue, ++ Mask{XFA_ResolveFlag::kChildren, ++ XFA_ResolveFlag::kProperties, ++ XFA_ResolveFlag::kAttributes})) { ++ return pReturnValue; ++ } ++ if (pObject == pScriptContext->GetThisObject() || ++ (pScriptContext->GetType() == CXFA_Script::Type::Javascript && ++ !pScriptContext->IsStrictScopeInJavaScript())) { ++ if (pScriptContext->QueryNodeByFlag( ++ pRefNode, wsPropName.AsStringView(), &pReturnValue, ++ Mask{XFA_ResolveFlag::kParent, ++ XFA_ResolveFlag::kSiblings})) { ++ return pReturnValue; ++ } + } +- if (bRet) +- return; +- + CXFA_Object* pScriptObject = +- lpScriptContext->GetVariablesThis(pOriginalObject, true); ++ pScriptContext->GetVariablesScript(pOriginalObject); + if (!pScriptObject) +- return; +- +- bRet = lpScriptContext->QueryVariableValue(ToNode(pScriptObject), szPropName, +- pReturnValue, true); +- if (bRet) +- return; ++ return pReturnValue; + +- Optional info = XFA_GetScriptAttributeByName( ++ if (pScriptContext->QueryVariableValue( ++ CXFA_Script::FromNode(pScriptObject->AsNode()), szPropName, ++ &pReturnValue)) { ++ return pReturnValue; ++ } ++ absl::optional info = XFA_GetScriptAttributeByName( + pObject->GetElementType(), wsPropName.AsStringView()); + if (info.has_value()) { +- CJX_Object* jsObject = pObject->JSObject(); +- (*info.value().callback)(jsObject, pReturnValue, false, +- info.value().attribute); +- return; ++ (*info.value().callback)(pIsolate, pObject->JSObject(), &pReturnValue, ++ false, info.value().attribute); ++ return pReturnValue; + } + + CXFA_FFNotify* pNotify = pObject->GetDocument()->GetNotify(); + if (!pNotify) +- return; ++ return pReturnValue; + +- CXFA_FFDoc* hDoc = pNotify->GetHDOC(); +- auto* pCJSRuntime = +- static_cast(hDoc->GetDocEnvironment()->GetIJSRuntime(hDoc)); ++ CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); ++ auto* pCJSRuntime = static_cast(hDoc->GetIJSRuntime()); + if (!pCJSRuntime) +- return; ++ return pReturnValue; + +- v8::HandleScope handle_scope(lpScriptContext->GetIsolate()); + IJS_Runtime::ScopedEventContext pContext(pCJSRuntime); +- v8::Local temp_local; +- if (!pCJSRuntime->GetValueByNameFromGlobalObject(szPropName, &temp_local)) +- return; +- +- if (temp_local.IsEmpty()) +- return; ++ v8::Local temp_local = ++ pCJSRuntime->GetValueByNameFromGlobalObject(szPropName); + +- pReturnValue->ForceSetValue(temp_local); ++ return !temp_local.IsEmpty() ? temp_local : pReturnValue; + } + + // static +-void CFXJSE_Engine::NormalPropertySetter(CFXJSE_Value* pOriginalValue, ++void CFXJSE_Engine::NormalPropertySetter(v8::Isolate* pIsolate, ++ v8::Local pHolder, + ByteStringView szPropName, +- CFXJSE_Value* pReturnValue) { +- CXFA_Object* pOriginalObject = ToObject(pOriginalValue); ++ v8::Local pValue) { ++ CXFA_Object* pOriginalObject = ToObject(pIsolate, pHolder); + if (!pOriginalObject) + return; + +- CFXJSE_Engine* lpScriptContext = ++ CFXJSE_Engine* pScriptContext = + pOriginalObject->GetDocument()->GetScriptContext(); +- CXFA_Object* pObject = +- lpScriptContext->GetVariablesThis(pOriginalObject, false); ++ if (pScriptContext->IsResolvingNodes()) ++ return; ++ ++ CXFA_Object* pObject = pScriptContext->GetVariablesThis(pOriginalObject); + WideString wsPropName = WideString::FromUTF8(szPropName); + WideStringView wsPropNameView = wsPropName.AsStringView(); +- Optional info = ++ absl::optional info = + XFA_GetScriptAttributeByName(pObject->GetElementType(), wsPropNameView); + if (info.has_value()) { + CJX_Object* jsObject = pObject->JSObject(); +- (*info.value().callback)(jsObject, pReturnValue, true, ++ (*info.value().callback)(pIsolate, jsObject, &pValue, true, + info.value().attribute); + return; + } +@@ -430,7 +493,7 @@ void CFXJSE_Engine::NormalPropertySetter(CFXJSE_Value* pOriginalValue, + info = XFA_GetScriptAttributeByName(pPropOrChild->GetElementType(), + L"{default}"); + if (info.has_value()) { +- pPropOrChild->JSObject()->ScriptSomDefaultValue(pReturnValue, true, ++ pPropOrChild->JSObject()->ScriptSomDefaultValue(pIsolate, &pValue, true, + XFA_Attribute::Unknown); + return; + } +@@ -438,32 +501,36 @@ void CFXJSE_Engine::NormalPropertySetter(CFXJSE_Value* pOriginalValue, + } + + CXFA_Object* pScriptObject = +- lpScriptContext->GetVariablesThis(pOriginalObject, true); ++ pScriptContext->GetVariablesScript(pOriginalObject); + if (pScriptObject) { +- lpScriptContext->QueryVariableValue(ToNode(pScriptObject), szPropName, +- pReturnValue, false); ++ pScriptContext->UpdateVariableValue( ++ CXFA_Script::FromNode(pScriptObject->AsNode()), szPropName, pValue); + } + } + +-int32_t CFXJSE_Engine::NormalPropTypeGetter(CFXJSE_Value* pOriginalValue, +- ByteStringView szPropName, +- bool bQueryIn) { +- CXFA_Object* pObject = ToObject(pOriginalValue); ++FXJSE_ClassPropType CFXJSE_Engine::NormalPropTypeGetter( ++ v8::Isolate* pIsolate, ++ v8::Local pHolder, ++ ByteStringView szPropName, ++ bool bQueryIn) { ++ CXFA_Object* pObject = ToObject(pIsolate, pHolder); + if (!pObject) +- return FXJSE_ClassPropType_None; ++ return FXJSE_ClassPropType::kNone; + +- CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext(); +- pObject = lpScriptContext->GetVariablesThis(pObject, false); ++ CFXJSE_Engine* pScriptContext = pObject->GetDocument()->GetScriptContext(); ++ pObject = pScriptContext->GetVariablesThis(pObject); + XFA_Element eType = pObject->GetElementType(); + WideString wsPropName = WideString::FromUTF8(szPropName); + if (pObject->JSObject()->HasMethod(wsPropName)) +- return FXJSE_ClassPropType_Method; ++ return FXJSE_ClassPropType::kMethod; + +- if (bQueryIn && +- !XFA_GetScriptAttributeByName(eType, wsPropName.AsStringView())) { +- return FXJSE_ClassPropType_None; ++ if (bQueryIn) { ++ absl::optional maybe_info = ++ XFA_GetScriptAttributeByName(eType, wsPropName.AsStringView()); ++ if (!maybe_info.has_value()) ++ return FXJSE_ClassPropType::kNone; + } +- return FXJSE_ClassPropType_Property; ++ return FXJSE_ClassPropType::kProperty; + } + + CJS_Result CFXJSE_Engine::NormalMethodCall( +@@ -473,8 +540,8 @@ CJS_Result CFXJSE_Engine::NormalMethodCall( + if (!pObject) + return CJS_Result::Failure(L"no Holder() present."); + +- CFXJSE_Engine* lpScriptContext = pObject->GetDocument()->GetScriptContext(); +- pObject = lpScriptContext->GetVariablesThis(pObject, false); ++ CFXJSE_Engine* pScriptContext = pObject->GetDocument()->GetScriptContext(); ++ pObject = pScriptContext->GetVariablesThis(pObject); + + std::vector> parameters; + for (int i = 0; i < info.Length(); i++) +@@ -491,154 +558,184 @@ CXFA_Script::Type CFXJSE_Engine::GetType() { + return m_eScriptType; + } + +-CFXJSE_Context* CFXJSE_Engine::CreateVariablesContext(CXFA_Node* pScriptNode, ++void CFXJSE_Engine::AddObjectToUpArray(CXFA_Node* pNode) { ++ m_upObjectArray.push_back(pNode); ++} ++ ++CXFA_Node* CFXJSE_Engine::LastObjectFromUpArray() { ++ return !m_upObjectArray.empty() ? m_upObjectArray.back() : nullptr; ++} ++ ++CFXJSE_Context* CFXJSE_Engine::CreateVariablesContext(CXFA_Script* pScriptNode, + CXFA_Node* pSubform) { + if (!pScriptNode || !pSubform) + return nullptr; + +- auto pNewContext = +- CFXJSE_Context::Create(GetIsolate(), &VariablesClassDescriptor, +- new CXFA_ThisProxy(pSubform, pScriptNode)); ++ auto* proxy = cppgc::MakeGarbageCollected( ++ pScriptNode->GetDocument()->GetHeap()->GetAllocationHandle(), pSubform, ++ pScriptNode); ++ auto pNewContext = CFXJSE_Context::Create( ++ GetIsolate(), &kVariablesClassDescriptor, proxy->JSObject(), proxy); + RemoveBuiltInObjs(pNewContext.get()); + pNewContext->EnableCompatibleMode(); + CFXJSE_Context* pResult = pNewContext.get(); +- m_mapVariableToContext[pScriptNode] = std::move(pNewContext); ++ m_mapVariableToContext[pScriptNode->JSObject()] = std::move(pNewContext); + return pResult; + } + +-CXFA_Object* CFXJSE_Engine::GetVariablesThis(CXFA_Object* pObject, +- bool bScriptNode) { ++CXFA_Object* CFXJSE_Engine::GetVariablesThis(CXFA_Object* pObject) { + CXFA_ThisProxy* pProxy = ToThisProxy(pObject); +- if (!pProxy) +- return pObject; ++ return pProxy ? pProxy->GetThisNode() : pObject; ++} + +- return bScriptNode ? pProxy->GetScriptNode() : pProxy->GetThisNode(); ++CXFA_Object* CFXJSE_Engine::GetVariablesScript(CXFA_Object* pObject) { ++ CXFA_ThisProxy* pProxy = ToThisProxy(pObject); ++ return pProxy ? pProxy->GetScriptNode() : pObject; + } + +-bool CFXJSE_Engine::RunVariablesScript(CXFA_Node* pScriptNode) { ++void CFXJSE_Engine::RunVariablesScript(CXFA_Script* pScriptNode) { + if (!pScriptNode) +- return false; +- +- if (pScriptNode->GetElementType() != XFA_Element::Script) +- return true; ++ return; + +- CXFA_Node* pParent = pScriptNode->GetParent(); +- if (!pParent || pParent->GetElementType() != XFA_Element::Variables) +- return false; ++ auto* pParent = CXFA_Variables::FromNode(pScriptNode->GetParent()); ++ if (!pParent) ++ return; + +- auto it = m_mapVariableToContext.find(pScriptNode); ++ auto it = m_mapVariableToContext.find(pScriptNode->JSObject()); + if (it != m_mapVariableToContext.end() && it->second) +- return true; ++ return; + + CXFA_Node* pTextNode = pScriptNode->GetFirstChild(); + if (!pTextNode) +- return false; ++ return; + +- Optional wsScript = ++ absl::optional wsScript = + pTextNode->JSObject()->TryCData(XFA_Attribute::Value, true); +- if (!wsScript) +- return false; ++ if (!wsScript.has_value()) ++ return; + + ByteString btScript = wsScript->ToUTF8(); +- auto hRetValue = pdfium::MakeUnique(GetIsolate()); ++ auto hRetValue = std::make_unique(); + CXFA_Node* pThisObject = pParent->GetParent(); + CFXJSE_Context* pVariablesContext = + CreateVariablesContext(pScriptNode, pThisObject); +- AutoRestorer> nodeRestorer(&m_pThisObject); ++ AutoRestorer> nodeRestorer(&m_pThisObject); + m_pThisObject = pThisObject; +- return pVariablesContext->ExecuteScript(btScript.c_str(), hRetValue.get(), +- nullptr); ++ pVariablesContext->ExecuteScript(btScript.AsStringView(), hRetValue.get(), ++ v8::Local()); + } + +-bool CFXJSE_Engine::QueryVariableValue(CXFA_Node* pScriptNode, +- ByteStringView szPropName, +- CFXJSE_Value* pValue, +- bool bGetter) { +- if (!pScriptNode || pScriptNode->GetElementType() != XFA_Element::Script) +- return false; ++CFXJSE_Context* CFXJSE_Engine::VariablesContextForScriptNode( ++ CXFA_Script* pScriptNode) { ++ if (!pScriptNode) ++ return nullptr; ++ ++ auto* variablesNode = CXFA_Variables::FromNode(pScriptNode->GetParent()); ++ if (!variablesNode) ++ return nullptr; ++ ++ auto it = m_mapVariableToContext.find(pScriptNode->JSObject()); ++ return it != m_mapVariableToContext.end() ? it->second.get() : nullptr; ++} + +- CXFA_Node* variablesNode = pScriptNode->GetParent(); +- if (!variablesNode || +- variablesNode->GetElementType() != XFA_Element::Variables) ++bool CFXJSE_Engine::QueryVariableValue(CXFA_Script* pScriptNode, ++ ByteStringView szPropName, ++ v8::Local* pValue) { ++ CFXJSE_Context* pVariableContext = VariablesContextForScriptNode(pScriptNode); ++ if (!pVariableContext) + return false; + +- auto it = m_mapVariableToContext.find(pScriptNode); +- if (it == m_mapVariableToContext.end() || !it->second) ++ v8::Local pObject = pVariableContext->GetGlobalObject(); ++ if (!fxv8::ReentrantHasObjectOwnPropertyHelper(GetIsolate(), pObject, ++ szPropName)) { + return false; ++ } + +- CFXJSE_Context* pVariableContext = it->second.get(); +- std::unique_ptr pObject = pVariableContext->GetGlobalObject(); +- auto hVariableValue = pdfium::MakeUnique(GetIsolate()); +- if (!bGetter) { +- pObject->SetObjectOwnProperty(szPropName, pValue); +- return true; ++ v8::Local hVariableValue = ++ fxv8::ReentrantGetObjectPropertyHelper(GetIsolate(), pObject, szPropName); ++ if (fxv8::IsFunction(hVariableValue)) { ++ v8::Local maybeFunc = CFXJSE_Value::NewBoundFunction( ++ GetIsolate(), hVariableValue.As(), pObject); ++ if (!maybeFunc.IsEmpty()) ++ *pValue = maybeFunc; ++ } else { ++ *pValue = hVariableValue; + } ++ return true; ++} + +- if (!pObject->HasObjectOwnProperty(szPropName, false)) ++bool CFXJSE_Engine::UpdateVariableValue(CXFA_Script* pScriptNode, ++ ByteStringView szPropName, ++ v8::Local pValue) { ++ CFXJSE_Context* pVariableContext = VariablesContextForScriptNode(pScriptNode); ++ if (!pVariableContext) + return false; + +- pObject->GetObjectProperty(szPropName, hVariableValue.get()); +- if (hVariableValue->IsFunction()) +- pValue->SetFunctionBind(hVariableValue.get(), pObject.get()); +- else if (bGetter) +- pValue->Assign(hVariableValue.get()); +- else +- hVariableValue.get()->Assign(pValue); ++ v8::Local pObject = pVariableContext->GetGlobalObject(); ++ fxv8::ReentrantSetObjectOwnPropertyHelper(GetIsolate(), pObject, szPropName, ++ pValue); + return true; + } + +-void CFXJSE_Engine::RemoveBuiltInObjs(CFXJSE_Context* pContext) const { +- const ByteStringView kObjNames[2] = {"Number", "Date"}; +- std::unique_ptr pObject = pContext->GetGlobalObject(); +- auto hProp = pdfium::MakeUnique(GetIsolate()); +- for (const auto& obj : kObjNames) { +- if (pObject->GetObjectProperty(obj, hProp.get())) +- pObject->DeleteObjectProperty(obj); +- } ++void CFXJSE_Engine::RemoveBuiltInObjs(CFXJSE_Context* pContext) { ++ CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext()); ++ v8::Local pObject = pContext->GetGlobalObject(); ++ fxv8::ReentrantDeleteObjectPropertyHelper(GetIsolate(), pObject, "Number"); ++ fxv8::ReentrantDeleteObjectPropertyHelper(GetIsolate(), pObject, "Date"); ++} ++ ++absl::optional CFXJSE_Engine::ResolveObjects( ++ CXFA_Object* refObject, ++ WideStringView wsExpression, ++ Mask dwStyles) { ++ return ResolveObjectsWithBindNode(refObject, wsExpression, dwStyles, nullptr); + } + +-bool CFXJSE_Engine::ResolveObjects(CXFA_Object* refObject, +- WideStringView wsExpression, +- XFA_RESOLVENODE_RS* resolveNodeRS, +- uint32_t dwStyles, +- CXFA_Node* bindNode) { ++absl::optional ++CFXJSE_Engine::ResolveObjectsWithBindNode(CXFA_Object* refObject, ++ WideStringView wsExpression, ++ Mask dwStyles, ++ CXFA_Node* bindNode) { + if (wsExpression.IsEmpty()) +- return false; ++ return absl::nullopt; + +- if (m_eScriptType != CXFA_Script::Type::Formcalc || +- (dwStyles & (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings))) { ++ AutoRestorer resolving_restorer(&m_bResolvingNodes); ++ m_bResolvingNodes = true; ++ ++ const bool bParentOrSiblings = ++ !!(dwStyles & Mask{XFA_ResolveFlag::kParent, ++ XFA_ResolveFlag::kSiblings}); ++ if (m_eScriptType != CXFA_Script::Type::Formcalc || bParentOrSiblings) + m_upObjectArray.clear(); +- } +- if (refObject && refObject->IsNode() && +- (dwStyles & (XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings))) { ++ if (refObject && refObject->IsNode() && bParentOrSiblings) + m_upObjectArray.push_back(refObject->AsNode()); +- } + ++ ResolveResult result; + bool bNextCreate = false; +- CXFA_NodeHelper* pNodeHelper = m_ResolveProcessor->GetNodeHelper(); +- if (dwStyles & XFA_RESOLVENODE_CreateNode) +- pNodeHelper->SetCreateNodeType(bindNode); ++ if (dwStyles & XFA_ResolveFlag::kCreateNode) ++ m_NodeHelper->SetCreateNodeType(bindNode); + +- pNodeHelper->m_pCreateParent = nullptr; +- pNodeHelper->m_iCurAllStart = -1; ++ m_NodeHelper->m_pCreateParent = nullptr; ++ m_NodeHelper->m_iCurAllStart = -1; + +- CFXJSE_ResolveNodeData rndFind(this); ++ CFXJSE_ResolveProcessor::NodeData rndFind; + int32_t nStart = 0; + int32_t nLevel = 0; + +- std::vector> findObjects; ++ std::vector> findObjects; + findObjects.emplace_back(refObject ? refObject : m_pDocument->GetRoot()); + int32_t nNodes = 0; ++ CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext()); + while (true) { +- nNodes = pdfium::CollectionSize(findObjects); ++ nNodes = fxcrt::CollectionSize(findObjects); + int32_t i = 0; + rndFind.m_dwStyles = dwStyles; + m_ResolveProcessor->SetCurStart(nStart); + nStart = m_ResolveProcessor->GetFilter(wsExpression, nStart, rndFind); + if (nStart < 1) { +- if ((dwStyles & XFA_RESOLVENODE_CreateNode) && !bNextCreate) { ++ if ((dwStyles & XFA_ResolveFlag::kCreateNode) && !bNextCreate) { + CXFA_Node* pDataNode = nullptr; +- nStart = pNodeHelper->m_iCurAllStart; ++ nStart = m_NodeHelper->m_iCurAllStart; + if (nStart != -1) { + pDataNode = m_pDocument->GetNotBindNode(findObjects); + if (pDataNode) { +@@ -652,9 +749,9 @@ bool CFXJSE_Engine::ResolveObjects(CXFA_Object* refObject, + findObjects.emplace_back(pDataNode); + break; + } +- dwStyles |= XFA_RESOLVENODE_Bind; ++ dwStyles |= XFA_ResolveFlag::kBind; + findObjects.clear(); +- findObjects.emplace_back(pNodeHelper->m_pAllStartParent.Get()); ++ findObjects.emplace_back(m_NodeHelper->m_pAllStartParent.Get()); + continue; + } + break; +@@ -662,62 +759,64 @@ bool CFXJSE_Engine::ResolveObjects(CXFA_Object* refObject, + if (bNextCreate) { + int32_t checked_length = + pdfium::base::checked_cast(wsExpression.GetLength()); +- if (pNodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition, +- nStart == checked_length, this)) { ++ if (m_NodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition, ++ nStart == checked_length, this)) { + continue; + } + break; + } +- std::vector> retObjects; ++ std::vector> retObjects; + while (i < nNodes) { + bool bDataBind = false; +- if (((dwStyles & XFA_RESOLVENODE_Bind) || +- (dwStyles & XFA_RESOLVENODE_CreateNode)) && ++ if (((dwStyles & XFA_ResolveFlag::kBind) || ++ (dwStyles & XFA_ResolveFlag::kCreateNode)) && + nNodes > 1) { +- CFXJSE_ResolveNodeData rndBind(nullptr); ++ CFXJSE_ResolveProcessor::NodeData rndBind; + m_ResolveProcessor->GetFilter(wsExpression, nStart, rndBind); +- m_ResolveProcessor->SetIndexDataBind(rndBind.m_wsCondition, i, nNodes); ++ i = m_ResolveProcessor->IndexForDataBind(rndBind.m_wsCondition, nNodes); + bDataBind = true; + } + rndFind.m_CurObject = findObjects[i++].Get(); + rndFind.m_nLevel = nLevel; +- rndFind.m_dwFlag = XFA_ResolveNode_RSType_Nodes; +- if (!m_ResolveProcessor->Resolve(rndFind)) ++ rndFind.m_Result.type = ResolveResult::Type::kNodes; ++ if (!m_ResolveProcessor->Resolve(GetIsolate(), rndFind)) + continue; + +- if (rndFind.m_dwFlag == XFA_ResolveNode_RSType_Attribute && +- rndFind.m_ScriptAttribute.callback && ++ if (rndFind.m_Result.type == ResolveResult::Type::kAttribute && ++ rndFind.m_Result.script_attribute.callback && + nStart < + pdfium::base::checked_cast(wsExpression.GetLength())) { +- auto pValue = pdfium::MakeUnique(GetIsolate()); +- CJX_Object* jsObject = rndFind.m_Objects.front()->JSObject(); +- (*rndFind.m_ScriptAttribute.callback)( +- jsObject, pValue.get(), false, rndFind.m_ScriptAttribute.attribute); +- if (!pValue->IsEmpty()) +- rndFind.m_Objects.front() = ToObject(pValue.get()); ++ v8::Local pValue; ++ CJX_Object* jsObject = rndFind.m_Result.objects.front()->JSObject(); ++ (*rndFind.m_Result.script_attribute.callback)( ++ GetIsolate(), jsObject, &pValue, false, ++ rndFind.m_Result.script_attribute.attribute); ++ if (!pValue.IsEmpty()) { ++ rndFind.m_Result.objects.front() = ToObject(GetIsolate(), pValue); ++ } + } + if (!m_upObjectArray.empty()) + m_upObjectArray.pop_back(); +- retObjects.insert(retObjects.end(), rndFind.m_Objects.begin(), +- rndFind.m_Objects.end()); +- rndFind.m_Objects.clear(); ++ retObjects.insert(retObjects.end(), rndFind.m_Result.objects.begin(), ++ rndFind.m_Result.objects.end()); ++ rndFind.m_Result.objects.clear(); + if (bDataBind) + break; + } + findObjects.clear(); + +- nNodes = pdfium::CollectionSize(retObjects); ++ nNodes = fxcrt::CollectionSize(retObjects); + if (nNodes < 1) { +- if (dwStyles & XFA_RESOLVENODE_CreateNode) { ++ if (dwStyles & XFA_ResolveFlag::kCreateNode) { + bNextCreate = true; +- if (!pNodeHelper->m_pCreateParent) { +- pNodeHelper->m_pCreateParent = ToNode(rndFind.m_CurObject.Get()); +- pNodeHelper->m_iCreateCount = 1; ++ if (!m_NodeHelper->m_pCreateParent) { ++ m_NodeHelper->m_pCreateParent = ToNode(rndFind.m_CurObject); ++ m_NodeHelper->m_iCreateCount = 1; + } + int32_t checked_length = + pdfium::base::checked_cast(wsExpression.GetLength()); +- if (pNodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition, +- nStart == checked_length, this)) { ++ if (m_NodeHelper->CreateNode(rndFind.m_wsName, rndFind.m_wsCondition, ++ nStart == checked_length, this)) { + continue; + } + } +@@ -725,101 +824,95 @@ bool CFXJSE_Engine::ResolveObjects(CXFA_Object* refObject, + } + + findObjects = std::move(retObjects); +- rndFind.m_Objects.clear(); +- if (nLevel == 0) +- dwStyles &= ~(XFA_RESOLVENODE_Parent | XFA_RESOLVENODE_Siblings); +- ++ rndFind.m_Result.objects.clear(); ++ if (nLevel == 0) { ++ dwStyles.Clear(XFA_ResolveFlag::kParent); ++ dwStyles.Clear(XFA_ResolveFlag::kSiblings); ++ } + nLevel++; + } + + if (!bNextCreate) { +- resolveNodeRS->dwFlags = rndFind.m_dwFlag; ++ result.type = rndFind.m_Result.type; + if (nNodes > 0) { +- resolveNodeRS->objects.insert(resolveNodeRS->objects.end(), +- findObjects.begin(), findObjects.end()); ++ result.objects.insert(result.objects.end(), findObjects.begin(), ++ findObjects.end()); + } +- if (rndFind.m_dwFlag == XFA_ResolveNode_RSType_Attribute) { +- resolveNodeRS->script_attribute = rndFind.m_ScriptAttribute; +- return true; ++ if (rndFind.m_Result.type == ResolveResult::Type::kAttribute) { ++ result.script_attribute = rndFind.m_Result.script_attribute; ++ return result; + } + } +- if (dwStyles & (XFA_RESOLVENODE_CreateNode | XFA_RESOLVENODE_Bind | +- XFA_RESOLVENODE_BindNew)) { +- if (pNodeHelper->m_pCreateParent) +- resolveNodeRS->objects.emplace_back(pNodeHelper->m_pCreateParent.Get()); ++ if ((dwStyles & XFA_ResolveFlag::kCreateNode) || ++ (dwStyles & XFA_ResolveFlag::kBind) || ++ (dwStyles & XFA_ResolveFlag::kBindNew)) { ++ if (m_NodeHelper->m_pCreateParent) ++ result.objects.emplace_back(m_NodeHelper->m_pCreateParent.Get()); + else +- pNodeHelper->CreateNodeForCondition(rndFind.m_wsCondition); ++ m_NodeHelper->CreateNodeForCondition(rndFind.m_wsCondition); + +- resolveNodeRS->dwFlags = pNodeHelper->m_iCreateFlag; +- if (resolveNodeRS->dwFlags == XFA_ResolveNode_RSType_CreateNodeOne) { +- if (pNodeHelper->m_iCurAllStart != -1) +- resolveNodeRS->dwFlags = XFA_ResolveNode_RSType_CreateNodeMidAll; ++ result.type = m_NodeHelper->m_iCreateFlag; ++ if (result.type == ResolveResult::Type::kCreateNodeOne) { ++ if (m_NodeHelper->m_iCurAllStart != -1) ++ result.type = ResolveResult::Type::kCreateNodeMidAll; + } + +- if (!bNextCreate && (dwStyles & XFA_RESOLVENODE_CreateNode)) +- resolveNodeRS->dwFlags = XFA_ResolveNode_RSType_ExistNodes; ++ if (!bNextCreate && (dwStyles & XFA_ResolveFlag::kCreateNode)) ++ result.type = ResolveResult::Type::kExistNodes; + +- return !resolveNodeRS->objects.empty(); ++ if (result.objects.empty()) ++ return absl::nullopt; ++ ++ return result; + } +- return nNodes > 0; +-} ++ if (nNodes == 0) ++ return absl::nullopt; + +-void CFXJSE_Engine::AddToCacheList(std::unique_ptr pList) { +- m_CacheList.push_back(std::move(pList)); ++ return result; + } + +-CFXJSE_Value* CFXJSE_Engine::GetOrCreateJSBindingFromMap(CXFA_Object* pObject) { +- if (pObject->IsNode()) +- RunVariablesScript(pObject->AsNode()); +- +- auto iter = m_mapObjectToValue.find(pObject); +- if (iter != m_mapObjectToValue.end()) +- return iter->second.get(); +- +- auto jsValue = pdfium::MakeUnique(GetIsolate()); +- jsValue->SetHostObject(pObject, m_pJsClass.Get()); ++v8::Local CFXJSE_Engine::GetOrCreateJSBindingFromMap( ++ CXFA_Object* pObject) { ++ RunVariablesScript(CXFA_Script::FromNode(pObject->AsNode())); + +- CFXJSE_Value* pValue = jsValue.get(); +- m_mapObjectToValue.insert(std::make_pair(pObject, std::move(jsValue))); +- return pValue; +-} ++ CJX_Object* pCJXObject = pObject->JSObject(); ++ auto iter = m_mapObjectToObject.find(pCJXObject); ++ if (iter != m_mapObjectToObject.end()) ++ return v8::Local::New(GetIsolate(), iter->second); + +-void CFXJSE_Engine::RemoveJSBindingFromMap(CXFA_Object* pObject) { +- auto iter = m_mapObjectToValue.find(pObject); +- if (iter == m_mapObjectToValue.end()) +- return; ++ v8::Local binding = pCJXObject->NewBoundV8Object( ++ GetIsolate(), m_pJsClass->GetTemplate(GetIsolate())); + +- iter->second->ClearHostObject(); +- m_mapObjectToValue.erase(iter); ++ m_mapObjectToObject[pCJXObject].Reset(GetIsolate(), binding); ++ return binding; + } + +-void CFXJSE_Engine::SetNodesOfRunScript(std::vector* pArray) { ++void CFXJSE_Engine::SetNodesOfRunScript( ++ std::vector>* pArray) { + m_pScriptNodeArray = pArray; + } + + void CFXJSE_Engine::AddNodesOfRunScript(CXFA_Node* pNode) { +- if (m_pScriptNodeArray && !pdfium::ContainsValue(*m_pScriptNodeArray, pNode)) +- m_pScriptNodeArray->push_back(pNode); ++ if (m_pScriptNodeArray && !pdfium::Contains(*m_pScriptNodeArray, pNode)) ++ m_pScriptNodeArray->emplace_back(pNode); + } + + CXFA_Object* CFXJSE_Engine::ToXFAObject(v8::Local obj) { +- if (obj.IsEmpty() || !obj->IsObject()) ++ if (!fxv8::IsObject(obj)) + return nullptr; + + CFXJSE_HostObject* pHostObj = + FXJSE_RetrieveObjectBinding(obj.As()); +- return pHostObj ? pHostObj->AsCXFAObject() : nullptr; ++ if (!pHostObj) ++ return nullptr; ++ ++ CJX_Object* pJSObject = pHostObj->AsCJXObject(); ++ return pJSObject ? pJSObject->GetXFAObject() : nullptr; + } + +-v8::Local CFXJSE_Engine::NewXFAObject( +- CXFA_Object* obj, +- v8::Global& tmpl) { ++v8::Local CFXJSE_Engine::NewNormalXFAObject(CXFA_Object* obj) { + v8::EscapableHandleScope scope(GetIsolate()); +- v8::Local klass = +- v8::Local::New(GetIsolate(), tmpl); +- v8::Local object = klass->InstanceTemplate() +- ->NewInstance(m_JsContext->GetContext()) +- .ToLocalChecked(); +- FXJSE_UpdateObjectBinding(object, obj); ++ v8::Local object = obj->JSObject()->NewBoundV8Object( ++ GetIsolate(), GetJseNormalClass()->GetTemplate(GetIsolate())); + return scope.Escape(object); + } +diff --git a/fxjs/xfa/cfxjse_engine.h b/fxjs/xfa/cfxjse_engine.h +index addf0cc58..c6a95c311 100644 +--- a/fxjs/xfa/cfxjse_engine.h ++++ b/fxjs/xfa/cfxjse_engine.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,133 +9,210 @@ + + #include + #include ++#include + #include + ++#include "core/fxcrt/mask.h" + #include "core/fxcrt/unowned_ptr.h" + #include "fxjs/cfx_v8.h" +-#include "v8/include/v8.h" ++#include "v8/include/cppgc/persistent.h" ++#include "v8/include/v8-forward.h" ++#include "v8/include/v8-persistent-handle.h" + #include "xfa/fxfa/cxfa_eventparam.h" + #include "xfa/fxfa/parser/cxfa_document.h" + #include "xfa/fxfa/parser/cxfa_script.h" +-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h" ++#include "xfa/fxfa/parser/xfa_basic_data.h" + + class CFXJSE_Class; + class CFXJSE_Context; + class CFXJSE_FormCalcContext; ++class CFXJSE_HostObject; ++class CFXJSE_NodeHelper; + class CFXJSE_ResolveProcessor; ++class CFXJSE_Value; + class CJS_Runtime; +-class CXFA_List; +- +-// Flags for |dwStyles| argument to CFXJSE_Engine::ResolveObjects(). +-#define XFA_RESOLVENODE_Children 0x0001 +-#define XFA_RESOLVENODE_TagName 0x0002 +-#define XFA_RESOLVENODE_Attributes 0x0004 +-#define XFA_RESOLVENODE_Properties 0x0008 +-#define XFA_RESOLVENODE_Siblings 0x0020 +-#define XFA_RESOLVENODE_Parent 0x0040 +-#define XFA_RESOLVENODE_AnyChild 0x0080 +-#define XFA_RESOLVENODE_ALL 0x0100 +-#define XFA_RESOLVENODE_CreateNode 0x0400 +-#define XFA_RESOLVENODE_Bind 0x0800 +-#define XFA_RESOLVENODE_BindNew 0x1000 ++ ++enum class XFA_ResolveFlag : uint16_t { ++ kChildren = 1 << 0, ++ kTagName = 1 << 1, ++ kAttributes = 1 << 2, ++ kProperties = 1 << 3, ++ kSiblings = 1 << 5, ++ kParent = 1 << 6, ++ kAnyChild = 1 << 7, ++ kALL = 1 << 8, ++ kCreateNode = 1 << 10, ++ kBind = 1 << 11, ++ kBindNew = 1 << 12, ++}; + + class CFXJSE_Engine final : public CFX_V8 { + public: ++ class ResolveResult { ++ CPPGC_STACK_ALLOCATED(); // Allow raw/unowned pointers. ++ ++ public: ++ enum class Type { ++ kNodes = 0, ++ kAttribute, ++ kCreateNodeOne, ++ kCreateNodeAll, ++ kCreateNodeMidAll, ++ kExistNodes, ++ }; ++ ++ ResolveResult(); ++ ResolveResult(const ResolveResult& that); ++ ResolveResult& operator=(const ResolveResult& that); ++ ~ResolveResult(); ++ ++ Type type = Type::kNodes; ++ XFA_SCRIPTATTRIBUTEINFO script_attribute = {}; ++ ++ // Vector of Member would be correct for stack-based vectors, if ++ // STL worked with cppgc. ++ std::vector> objects; ++ }; ++ + static CXFA_Object* ToObject(const v8::FunctionCallbackInfo& info); +- static CXFA_Object* ToObject(CFXJSE_Value* pValue); +- static void GlobalPropertyGetter(CFXJSE_Value* pObject, +- ByteStringView szPropName, +- CFXJSE_Value* pValue); +- static void GlobalPropertySetter(CFXJSE_Value* pObject, ++ static CXFA_Object* ToObject(v8::Isolate* pIsolate, ++ v8::Local value); ++ static CXFA_Object* ToObject(v8::Isolate* pIsolate, CFXJSE_Value* pValue); ++ static CXFA_Object* ToObject(CFXJSE_HostObject* pHostObj); ++ static v8::Local GlobalPropertyGetter( ++ v8::Isolate* pIsolate, ++ v8::Local pObject, ++ ByteStringView szPropName); ++ static void GlobalPropertySetter(v8::Isolate* pIsolate, ++ v8::Local pObject, + ByteStringView szPropName, +- CFXJSE_Value* pValue); +- static void NormalPropertyGetter(CFXJSE_Value* pObject, ++ v8::Local pValue); ++ static v8::Local NormalPropertyGetter( ++ v8::Isolate* pIsolate, ++ v8::Local pObject, ++ ByteStringView szPropName); ++ static void NormalPropertySetter(v8::Isolate* pIsolate, ++ v8::Local pObject, + ByteStringView szPropName, +- CFXJSE_Value* pValue); +- static void NormalPropertySetter(CFXJSE_Value* pObject, +- ByteStringView szPropName, +- CFXJSE_Value* pValue); ++ v8::Local pValue); + static CJS_Result NormalMethodCall( + const v8::FunctionCallbackInfo& info, + const WideString& functionName); +- static int32_t NormalPropTypeGetter(CFXJSE_Value* pObject, +- ByteStringView szPropName, +- bool bQueryIn); +- static int32_t GlobalPropTypeGetter(CFXJSE_Value* pObject, +- ByteStringView szPropName, +- bool bQueryIn); ++ static FXJSE_ClassPropType NormalPropTypeGetter(v8::Isolate* pIsolate, ++ v8::Local pObject, ++ ByteStringView szPropName, ++ bool bQueryIn); ++ static FXJSE_ClassPropType GlobalPropTypeGetter(v8::Isolate* pIsolate, ++ v8::Local pObject, ++ ByteStringView szPropName, ++ bool bQueryIn); + + CFXJSE_Engine(CXFA_Document* pDocument, CJS_Runtime* fxjs_runtime); + ~CFXJSE_Engine() override; + +- void SetEventParam(CXFA_EventParam* param) { m_eventParam = param; } +- CXFA_EventParam* GetEventParam() const { return m_eventParam.Get(); } ++ class EventParamScope { ++ CPPGC_STACK_ALLOCATED(); ++ ++ public: ++ EventParamScope(CFXJSE_Engine* pEngine, ++ CXFA_Node* pTarget, ++ CXFA_EventParam* pEventParam); ++ ~EventParamScope(); ++ ++ private: ++ UnownedPtr m_pEngine; ++ UnownedPtr m_pPrevTarget; ++ UnownedPtr m_pPrevEventParam; ++ }; ++ friend class EventParamScope; ++ ++ CXFA_Node* GetEventTarget() const { return m_pTarget; } ++ CXFA_EventParam* GetEventParam() const { return m_eventParam; } + bool RunScript(CXFA_Script::Type eScriptType, + WideStringView wsScript, + CFXJSE_Value* pRetValue, + CXFA_Object* pThisObject); + +- bool ResolveObjects(CXFA_Object* refObject, +- WideStringView wsExpression, +- XFA_RESOLVENODE_RS* resolveNodeRS, +- uint32_t dwStyles, +- CXFA_Node* bindNode); ++ absl::optional ResolveObjects(CXFA_Object* refObject, ++ WideStringView wsExpression, ++ Mask dwStyles); ++ ++ absl::optional ResolveObjectsWithBindNode( ++ CXFA_Object* refObject, ++ WideStringView wsExpression, ++ Mask dwStyles, ++ CXFA_Node* bindNode); + +- CFXJSE_Value* GetOrCreateJSBindingFromMap(CXFA_Object* pObject); +- void RemoveJSBindingFromMap(CXFA_Object* pObject); ++ v8::Local GetOrCreateJSBindingFromMap(CXFA_Object* pObject); + +- void AddToCacheList(std::unique_ptr pList); +- CXFA_Object* GetThisObject() const { return m_pThisObject.Get(); } ++ CXFA_Object* GetThisObject() const { return m_pThisObject; } ++ CFXJSE_Class* GetJseNormalClass() const { return m_pJsClass; } ++ CXFA_Document* GetDocument() const { return m_pDocument.Get(); } + +- void SetNodesOfRunScript(std::vector* pArray); ++ void SetNodesOfRunScript(std::vector>* pArray); + void AddNodesOfRunScript(CXFA_Node* pNode); +- CFXJSE_Class* GetJseNormalClass() const { return m_pJsClass.Get(); } + + void SetRunAtType(XFA_AttributeValue eRunAt) { m_eRunAtType = eRunAt; } + bool IsRunAtClient() { return m_eRunAtType != XFA_AttributeValue::Server; } + + CXFA_Script::Type GetType(); +- std::vector* GetUpObjectArray() { return &m_upObjectArray; } +- CXFA_Document* GetDocument() const { return m_pDocument.Get(); } ++ ++ void AddObjectToUpArray(CXFA_Node* pNode); ++ CXFA_Node* LastObjectFromUpArray(); + + CXFA_Object* ToXFAObject(v8::Local obj); +- v8::Local NewXFAObject(CXFA_Object* obj, +- v8::Global& tmpl); ++ v8::Local NewNormalXFAObject(CXFA_Object* obj); ++ ++ bool IsResolvingNodes() const { return m_bResolvingNodes; } ++ ++ CFXJSE_Context* GetJseContextForTest() const { return GetJseContext(); } + + private: +- CFXJSE_Context* CreateVariablesContext(CXFA_Node* pScriptNode, ++ CFXJSE_Context* GetJseContext() const { return m_JsContext.get(); } ++ CFXJSE_Context* CreateVariablesContext(CXFA_Script* pScriptNode, + CXFA_Node* pSubform); +- void RemoveBuiltInObjs(CFXJSE_Context* pContext) const; ++ void RemoveBuiltInObjs(CFXJSE_Context* pContext); + bool QueryNodeByFlag(CXFA_Node* refNode, + WideStringView propname, +- CFXJSE_Value* pValue, +- uint32_t dwFlag, +- bool bSetting); ++ v8::Local* pValue, ++ Mask dwFlag); ++ bool UpdateNodeByFlag(CXFA_Node* refNode, ++ WideStringView propname, ++ v8::Local pValue, ++ Mask dwFlag); + bool IsStrictScopeInJavaScript(); +- CXFA_Object* GetVariablesThis(CXFA_Object* pObject, bool bScriptNode); +- bool QueryVariableValue(CXFA_Node* pScriptNode, ++ CXFA_Object* GetVariablesThis(CXFA_Object* pObject); ++ CXFA_Object* GetVariablesScript(CXFA_Object* pObject); ++ CFXJSE_Context* VariablesContextForScriptNode(CXFA_Script* pScriptNode); ++ bool QueryVariableValue(CXFA_Script* pScriptNode, + ByteStringView szPropName, +- CFXJSE_Value* pValue, +- bool bGetter); +- bool RunVariablesScript(CXFA_Node* pScriptNode); ++ v8::Local* pValue); ++ bool UpdateVariableValue(CXFA_Script* pScriptNode, ++ ByteStringView szPropName, ++ v8::Local pValue); ++ void RunVariablesScript(CXFA_Script* pScriptNode); + + UnownedPtr const m_pSubordinateRuntime; +- UnownedPtr const m_pDocument; ++ cppgc::WeakPersistent const m_pDocument; + std::unique_ptr m_JsContext; + UnownedPtr m_pJsClass; + CXFA_Script::Type m_eScriptType = CXFA_Script::Type::Unknown; +- std::map> m_mapObjectToValue; +- std::map> ++ // |m_mapObjectToValue| is what ensures the v8 object bound to a ++ // CJX_Object remains valid for the lifetime of the engine. ++ std::map, v8::Global> ++ m_mapObjectToObject; ++ std::map, std::unique_ptr> + m_mapVariableToContext; ++ cppgc::Persistent m_pTarget; + UnownedPtr m_eventParam; +- std::vector m_upObjectArray; +- // CacheList holds the List items so we can clean them up when we're done. +- std::vector> m_CacheList; +- UnownedPtr> m_pScriptNodeArray; ++ std::vector> m_upObjectArray; ++ UnownedPtr>> m_pScriptNodeArray; ++ std::unique_ptr const m_NodeHelper; + std::unique_ptr const m_ResolveProcessor; +- std::unique_ptr m_FM2JSContext; +- UnownedPtr m_pThisObject; ++ std::unique_ptr m_FormCalcContext; ++ cppgc::Persistent m_pThisObject; + XFA_AttributeValue m_eRunAtType = XFA_AttributeValue::Client; ++ bool m_bResolvingNodes = false; + }; + + #endif // FXJS_XFA_CFXJSE_ENGINE_H_ +diff --git a/fxjs/xfa/cfxjse_formcalc_context.cpp b/fxjs/xfa/cfxjse_formcalc_context.cpp +index 7a6796c48..4fc19d7d8 100644 +--- a/fxjs/xfa/cfxjse_formcalc_context.cpp ++++ b/fxjs/xfa/cfxjse_formcalc_context.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,31 +6,47 @@ + + #include "fxjs/xfa/cfxjse_formcalc_context.h" + ++#include ++#include ++#include ++#include ++ + #include +-#include +-#include ++#include ++#include + #include ++#include + +-#include "core/fxcrt/cfx_widetextbuf.h" ++#include "core/fxcrt/cfx_datetime.h" ++#include "core/fxcrt/data_vector.h" + #include "core/fxcrt/fx_extension.h" + #include "core/fxcrt/fx_random.h" +-#include "fxjs/xfa/cfxjse_arguments.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/widetext_buffer.h" ++#include "fxjs/fxv8.h" + #include "fxjs/xfa/cfxjse_class.h" + #include "fxjs/xfa/cfxjse_context.h" + #include "fxjs/xfa/cfxjse_engine.h" + #include "fxjs/xfa/cfxjse_value.h" + #include "fxjs/xfa/cjx_object.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/cxx17_backports.h" ++#include "third_party/base/numerics/safe_conversions.h" ++#include "v8/include/v8-container.h" ++#include "v8/include/v8-function-callback.h" ++#include "v8/include/v8-object.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fgas/crt/cfgas_decimal.h" +-#include "xfa/fgas/crt/locale_iface.h" + #include "xfa/fxfa/cxfa_ffnotify.h" +-#include "xfa/fxfa/fm2js/cxfa_fmparser.h" +-#include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h" ++#include "xfa/fxfa/formcalc/cxfa_fmparser.h" ++#include "xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.h" + #include "xfa/fxfa/parser/cxfa_document.h" + #include "xfa/fxfa/parser/cxfa_localevalue.h" + #include "xfa/fxfa/parser/cxfa_node.h" ++#include "xfa/fxfa/parser/cxfa_thisproxy.h" + #include "xfa/fxfa/parser/cxfa_timezoneprovider.h" ++#include "xfa/fxfa/parser/gced_locale_iface.h" + #include "xfa/fxfa/parser/xfa_utils.h" + + using pdfium::fxjse::kClassTag; +@@ -38,13 +54,17 @@ using pdfium::fxjse::kFuncTag; + + namespace { + ++// Maximum number of characters Acrobat can fit in a text box. ++constexpr int kMaxCharCount = 15654908; ++ + const double kFinancialPrecision = 0.00000001; + + const wchar_t kStrCode[] = L"0123456789abcdef"; + + struct XFA_FMHtmlReserveCode { +- uint32_t m_uCode; +- const char* m_htmlReserve; ++ uint16_t m_uCode; ++ // Inline string data reduces size for small strings. ++ const char m_htmlReserve[10]; + }; + + // Sorted by |m_htmlReserve|. +@@ -181,7 +201,7 @@ const XFA_FMHtmlReserveCode kReservesForEncode[] = { + {9824, "spades"}, {9827, "clubs"}, {9829, "hearts"}, {9830, "diams"}, + }; + +-const FXJSE_FUNCTION_DESCRIPTOR kFormCalcFM2JSFunctions[] = { ++const FXJSE_FUNCTION_DESCRIPTOR kFormCalcFunctions[] = { + {kFuncTag, "Abs", CFXJSE_FormCalcContext::Abs}, + {kFuncTag, "Avg", CFXJSE_FormCalcContext::Avg}, + {kFuncTag, "Ceil", CFXJSE_FormCalcContext::Ceil}, +@@ -280,7 +300,7 @@ const uint8_t kAltTableDate[] = { + 255, 2, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 1, 255, 255, 255, 255, 255, 255, 255, 255, + }; +-static_assert(FX_ArraySize(kAltTableDate) == L'a' - L'A' + 1, ++static_assert(std::size(kAltTableDate) == L'a' - L'A' + 1, + "Invalid kAltTableDate size."); + + const uint8_t kAltTableTime[] = { +@@ -288,7 +308,7 @@ const uint8_t kAltTableTime[] = { + 255, 6, 255, 255, 255, 255, 255, 7, 255, 255, 255, + 255, 255, 1, 17, 255, 255, 255, 255, 255, 255, 255, + }; +-static_assert(FX_ArraySize(kAltTableTime) == L'a' - L'A' + 1, ++static_assert(std::size(kAltTableTime) == L'a' - L'A' + 1, + "Invalid kAltTableTime size."); + + void AlternateDateTimeSymbols(WideString* pPattern, +@@ -322,37 +342,34 @@ void AlternateDateTimeSymbols(WideString* pPattern, + } + } + +-std::pair PatternStringType(ByteStringView bsPattern) { ++std::pair PatternStringType( ++ ByteStringView bsPattern) { + WideString wsPattern = WideString::FromUTF8(bsPattern); + if (L"datetime" == wsPattern.First(8)) +- return {true, XFA_VT_DATETIME}; ++ return {true, CXFA_LocaleValue::ValueType::kDateTime}; + if (L"date" == wsPattern.First(4)) { + auto pos = wsPattern.Find(L"time"); +- uint32_t type = +- pos.has_value() && pos.value() != 0 ? XFA_VT_DATETIME : XFA_VT_DATE; +- return {true, type}; ++ if (pos.has_value() && pos.value() != 0) ++ return {true, CXFA_LocaleValue::ValueType::kDateTime}; ++ return {true, CXFA_LocaleValue::ValueType::kDate}; + } + if (L"time" == wsPattern.First(4)) +- return {true, XFA_VT_TIME}; ++ return {true, CXFA_LocaleValue::ValueType::kTime}; + if (L"text" == wsPattern.First(4)) +- return {true, XFA_VT_TEXT}; ++ return {true, CXFA_LocaleValue::ValueType::kText}; + if (L"num" == wsPattern.First(3)) { +- uint32_t type; +- if (L"integer" == wsPattern.Substr(4, 7)) { +- type = XFA_VT_INTEGER; +- } else if (L"decimal" == wsPattern.Substr(4, 7)) { +- type = XFA_VT_DECIMAL; +- } else if (L"currency" == wsPattern.Substr(4, 8)) { +- type = XFA_VT_FLOAT; +- } else if (L"percent" == wsPattern.Substr(4, 7)) { +- type = XFA_VT_FLOAT; +- } else { +- type = XFA_VT_FLOAT; +- } +- return {true, type}; +- } +- +- uint32_t type = XFA_VT_NULL; ++ if (L"integer" == wsPattern.Substr(4, 7)) ++ return {true, CXFA_LocaleValue::ValueType::kInteger}; ++ if (L"decimal" == wsPattern.Substr(4, 7)) ++ return {true, CXFA_LocaleValue::ValueType::kDecimal}; ++ if (L"currency" == wsPattern.Substr(4, 8)) ++ return {true, CXFA_LocaleValue::ValueType::kFloat}; ++ if (L"percent" == wsPattern.Substr(4, 7)) ++ return {true, CXFA_LocaleValue::ValueType::kFloat}; ++ return {true, CXFA_LocaleValue::ValueType::kFloat}; ++ } ++ ++ CXFA_LocaleValue::ValueType type = CXFA_LocaleValue::ValueType::kNull; + wsPattern.MakeLower(); + const wchar_t* pData = wsPattern.c_str(); + int32_t iLength = wsPattern.GetLength(); +@@ -371,11 +388,11 @@ std::pair PatternStringType(ByteStringView bsPattern) { + } + + if (wsPatternChar == 'h' || wsPatternChar == 'k') +- return {false, XFA_VT_TIME}; ++ return {false, CXFA_LocaleValue::ValueType::kTime}; + if (wsPatternChar == 'x' || wsPatternChar == 'o' || wsPatternChar == '0') +- return {false, XFA_VT_TEXT}; ++ return {false, CXFA_LocaleValue::ValueType::kText}; + if (wsPatternChar == 'v' || wsPatternChar == '8' || wsPatternChar == '$') +- return {false, XFA_VT_FLOAT}; ++ return {false, CXFA_LocaleValue::ValueType::kFloat}; + if (wsPatternChar == 'y' || wsPatternChar == 'j') { + iIndex++; + wchar_t timePatternChar; +@@ -387,35 +404,31 @@ std::pair PatternStringType(ByteStringView bsPattern) { + continue; + } + if (!bSingleQuotation && timePatternChar == 't') +- return {false, XFA_VT_DATETIME}; ++ return {false, CXFA_LocaleValue::ValueType::kDateTime}; + iIndex++; + } +- return {false, XFA_VT_DATE}; ++ return {false, CXFA_LocaleValue::ValueType::kDate}; + } + + if (wsPatternChar == 'a') { +- type = XFA_VT_TEXT; ++ type = CXFA_LocaleValue::ValueType::kText; + } else if (wsPatternChar == 'z' || wsPatternChar == 's' || + wsPatternChar == 'e' || wsPatternChar == ',' || + wsPatternChar == '.') { +- type = XFA_VT_FLOAT; ++ type = CXFA_LocaleValue::ValueType::kFloat; + } + iIndex++; + } +- +- if (type == XFA_VT_NULL) +- type = XFA_VT_TEXT | XFA_VT_FLOAT; + return {false, type}; + } + +-CFXJSE_FormCalcContext* ToFormCalcContext(CFXJSE_Value* pValue) { +- CFXJSE_HostObject* pHostObj = pValue->ToHostObject(); ++CFXJSE_FormCalcContext* ToFormCalcContext(CFXJSE_HostObject* pHostObj) { + return pHostObj ? pHostObj->AsFormCalcContext() : nullptr; + } + +-LocaleIface* LocaleFromString(CXFA_Document* pDoc, +- CXFA_LocaleMgr* pMgr, +- ByteStringView bsLocale) { ++GCedLocaleIface* LocaleFromString(CXFA_Document* pDoc, ++ CXFA_LocaleMgr* pMgr, ++ ByteStringView bsLocale) { + if (!bsLocale.IsEmpty()) + return pMgr->GetLocaleByName(WideString::FromUTF8(bsLocale)); + +@@ -427,21 +440,21 @@ WideString FormatFromString(LocaleIface* pLocale, ByteStringView bsFormat) { + if (!bsFormat.IsEmpty()) + return WideString::FromUTF8(bsFormat); + +- return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Default); ++ return pLocale->GetDatePattern(LocaleIface::DateTimeSubcategory::kDefault); + } + +-FX_LOCALEDATETIMESUBCATEGORY SubCategoryFromInt(int32_t iStyle) { ++LocaleIface::DateTimeSubcategory SubCategoryFromInt(int32_t iStyle) { + switch (iStyle) { + case 1: +- return FX_LOCALEDATETIMESUBCATEGORY_Short; ++ return LocaleIface::DateTimeSubcategory::kShort; + case 3: +- return FX_LOCALEDATETIMESUBCATEGORY_Long; ++ return LocaleIface::DateTimeSubcategory::kLong; + case 4: +- return FX_LOCALEDATETIMESUBCATEGORY_Full; ++ return LocaleIface::DateTimeSubcategory::kFull; + case 0: + case 2: + default: +- return FX_LOCALEDATETIMESUBCATEGORY_Medium; ++ return LocaleIface::DateTimeSubcategory::kMedium; + } + } + +@@ -455,7 +468,7 @@ ByteString GetLocalDateTimeFormat(CXFA_Document* pDoc, + if (!pLocale) + return ByteString(); + +- FX_LOCALEDATETIMESUBCATEGORY category = SubCategoryFromInt(iStyle); ++ LocaleIface::DateTimeSubcategory category = SubCategoryFromInt(iStyle); + WideString wsLocal = bIsDate ? pLocale->GetDatePattern(category) + : pLocale->GetTimePattern(category); + if (!bStandard) +@@ -469,7 +482,7 @@ bool IsWhitespace(char c) { + } + + bool IsPartOfNumber(char ch) { +- return std::isdigit(ch) || ch == '-' || ch == '.'; ++ return isdigit(ch) || ch == '-' || ch == '.'; + } + + bool IsPartOfNumberW(wchar_t ch) { +@@ -517,7 +530,7 @@ bool IsIsoDateFormat(pdfium::span pData, + char szYear[5]; + szYear[4] = '\0'; + for (int32_t i = 0; i < 4; ++i) { +- if (!std::isdigit(pData[i])) ++ if (!isdigit(pData[i])) + return false; + + szYear[i] = pData[i]; +@@ -530,7 +543,7 @@ bool IsIsoDateFormat(pdfium::span pData, + iStyle = pData[4] == '-' ? 1 : 0; + + size_t iPosOff = iStyle == 0 ? 4 : 5; +- if (!std::isdigit(pData[iPosOff]) || !std::isdigit(pData[iPosOff + 1])) ++ if (!isdigit(pData[iPosOff]) || !isdigit(pData[iPosOff + 1])) + return false; + + char szBuffer[3] = {}; +@@ -549,7 +562,7 @@ bool IsIsoDateFormat(pdfium::span pData, + if (pData.size() == 7) + return true; + } +- if (!std::isdigit(pData[iPosOff]) || !std::isdigit(pData[iPosOff + 1])) ++ if (!isdigit(pData[iPosOff]) || !isdigit(pData[iPosOff + 1])) + return false; + + szBuffer[0] = pData[iPosOff]; +@@ -595,7 +608,7 @@ bool IsIsoTimeFormat(pdfium::span pData, + size_t iZone = 0; + size_t i = 0; + while (i < pData.size()) { +- if (!std::isdigit(pData[i]) && pData[i] != ':') { ++ if (!isdigit(pData[i]) && pData[i] != ':') { + iZone = i; + break; + } +@@ -608,11 +621,11 @@ bool IsIsoTimeFormat(pdfium::span pData, + size_t iPos = 0; + size_t iIndex = 0; + while (iIndex < iZone) { +- if (!std::isdigit(pData[iIndex])) ++ if (!isdigit(pData[iIndex])) + return false; + + szBuffer[0] = pData[iIndex]; +- if (!std::isdigit(pData[iIndex + 1])) ++ if (!isdigit(pData[iIndex + 1])) + return false; + + szBuffer[1] = pData[iIndex + 1]; +@@ -654,7 +667,7 @@ bool IsIsoTimeFormat(pdfium::span pData, + char szMilliSeconds[kSubSecondLength + 1]; + for (int j = 0; j < kSubSecondLength; ++j) { + char c = pData[iIndex + j]; +- if (!std::isdigit(c)) ++ if (!isdigit(c)) + return false; + szMilliSeconds[j] = c; + } +@@ -682,11 +695,11 @@ bool IsIsoTimeFormat(pdfium::span pData, + } + iPos = 0; + while (iIndex < pData.size()) { +- if (!std::isdigit(pData[iIndex])) ++ if (!isdigit(pData[iIndex])) + return false; + + szBuffer[0] = pData[iIndex]; +- if (!std::isdigit(pData[iIndex + 1])) ++ if (!isdigit(pData[iIndex + 1])) + return false; + + szBuffer[1] = pData[iIndex + 1]; +@@ -728,45 +741,29 @@ bool IsIsoDateTimeFormat(pdfium::span pData, + int32_t* pMilliSecond, + int32_t* pZoneHour, + int32_t* pZoneMinute) { +- int32_t& iYear = *pYear; +- int32_t& iMonth = *pMonth; +- int32_t& iDay = *pDay; +- int32_t& iHour = *pHour; +- int32_t& iMinute = *pMinute; +- int32_t& iSecond = *pSecond; +- int32_t& iMilliSecond = *pMilliSecond; +- int32_t& iZoneHour = *pZoneHour; +- int32_t& iZoneMinute = *pZoneMinute; +- +- iYear = 0; +- iMonth = 0; +- iDay = 0; +- iHour = 0; +- iMinute = 0; +- iSecond = 0; +- +- if (pData.empty()) +- return false; ++ *pYear = 0; ++ *pMonth = 0; ++ *pDay = 0; ++ *pHour = 0; ++ *pMinute = 0; ++ *pSecond = 0; + + size_t iIndex = 0; +- while (pData[iIndex] != 'T' && pData[iIndex] != 't') { +- if (iIndex >= pData.size()) +- return false; ++ while (iIndex < pData.size()) { ++ if (pData[iIndex] == 'T' || pData[iIndex] == 't') ++ break; + ++iIndex; + } +- if (iIndex != 8 && iIndex != 10) ++ if (iIndex == pData.size() || (iIndex != 8 && iIndex != 10)) + return false; + +- int32_t iStyle = -1; +- if (!IsIsoDateFormat(pData.subspan(0, iIndex), &iStyle, &iYear, &iMonth, +- &iDay)) { +- return false; +- } +- if (pData[iIndex] != 'T' && pData[iIndex] != 't') +- return true; ++ pdfium::span pDateSpan = pData.subspan(0, iIndex); ++ pdfium::span pTimeSpan = pData.subspan(iIndex + 1); + +- return IsIsoTimeFormat(pData.subspan(iIndex + 1), &iHour, &iMinute, &iSecond, +- &iMilliSecond, &iZoneHour, &iZoneMinute); ++ int32_t iStyle = -1; ++ return IsIsoDateFormat(pDateSpan, &iStyle, pYear, pMonth, pDay) && ++ IsIsoTimeFormat(pTimeSpan, pHour, pMinute, pSecond, pMilliSecond, ++ pZoneHour, pZoneMinute); + } + + int32_t DateString2Num(ByteStringView bsDate) { +@@ -820,7 +817,7 @@ int32_t DateString2Num(ByteStringView bsDate) { + ++dDays; + ++i; + } +- return (int32_t)dDays; ++ return static_cast(dDays); + } + + void GetLocalTimeZone(int32_t* pHour, int32_t* pMin, int32_t* pSec) { +@@ -869,7 +866,7 @@ bool HTMLCode2STR(uint32_t iCode, WideString* wsHTMLReserve) { + WideString DecodeURL(const WideString& wsURL) { + const wchar_t* pData = wsURL.c_str(); + size_t iLen = wsURL.GetLength(); +- CFX_WideTextBuf wsResultBuf; ++ WideTextBuffer wsResultBuf; + for (size_t i = 0; i < iLen; ++i) { + wchar_t ch = pData[i]; + if ('%' != ch) { +@@ -891,14 +888,13 @@ WideString DecodeURL(const WideString& wsURL) { + } + wsResultBuf.AppendChar(chTemp); + } +- wsResultBuf.AppendChar(0); + return wsResultBuf.MakeString(); + } + + WideString DecodeMLInternal(const WideString& wsHTML, bool bIsHTML) { + const wchar_t* pData = wsHTML.c_str(); + size_t iLen = wsHTML.GetLength(); +- CFX_WideTextBuf wsResultBuf; ++ WideTextBuffer wsResultBuf; + for (size_t i = 0; i < iLen; ++i) { + wchar_t ch = pData[i]; + if (ch != '&') { +@@ -959,8 +955,6 @@ WideString DecodeMLInternal(const WideString& wsHTML, bool bIsHTML) { + wsResultBuf.AppendChar('>'); + } + } +- +- wsResultBuf.AppendChar(0); + return wsResultBuf.MakeString(); + } + +@@ -980,13 +974,13 @@ WideString EncodeURL(const ByteString& bsURL) { + '\'', '(', ')', ','}; + + WideString wsURL = WideString::FromUTF8(bsURL.AsStringView()); +- CFX_WideTextBuf wsResultBuf; ++ WideTextBuffer wsResultBuf; + wchar_t szEncode[4]; + szEncode[0] = '%'; + szEncode[3] = 0; + for (wchar_t ch : wsURL) { + size_t i = 0; +- size_t iCount = FX_ArraySize(kStrUnsafe); ++ size_t iCount = std::size(kStrUnsafe); + while (i < iCount) { + if (ch == kStrUnsafe[i]) { + int32_t iIndex = ch / 16; +@@ -1001,7 +995,7 @@ WideString EncodeURL(const ByteString& bsURL) { + continue; + + i = 0; +- iCount = FX_ArraySize(kStrReserved); ++ iCount = std::size(kStrReserved); + while (i < iCount) { + if (ch == kStrReserved[i]) { + int32_t iIndex = ch / 16; +@@ -1016,7 +1010,7 @@ WideString EncodeURL(const ByteString& bsURL) { + continue; + + i = 0; +- iCount = FX_ArraySize(kStrSpecial); ++ iCount = std::size(kStrSpecial); + while (i < iCount) { + if (ch == kStrSpecial[i]) { + wsResultBuf.AppendChar(ch); +@@ -1066,7 +1060,6 @@ WideString EncodeURL(const ByteString& bsURL) { + } + } + } +- wsResultBuf.AppendChar(0); + return wsResultBuf.MakeString(); + } + +@@ -1076,7 +1069,7 @@ WideString EncodeHTML(const ByteString& bsHTML) { + szEncode[0] = '&'; + szEncode[1] = '#'; + szEncode[2] = 'x'; +- CFX_WideTextBuf wsResultBuf; ++ WideTextBuffer wsResultBuf; + for (uint32_t ch : wsHTML) { + WideString htmlReserve; + if (HTMLCode2STR(ch, &htmlReserve)) { +@@ -1106,13 +1099,12 @@ WideString EncodeHTML(const ByteString& bsHTML) { + // TODO(tsepez): Handle codepoint not in BMP. + } + } +- wsResultBuf.AppendChar(0); + return wsResultBuf.MakeString(); + } + + WideString EncodeXML(const ByteString& bsXML) { + WideString wsXML = WideString::FromUTF8(bsXML.AsStringView()); +- CFX_WideTextBuf wsResultBuf; ++ WideTextBuffer wsResultBuf; + wchar_t szEncode[9]; + szEncode[0] = '&'; + szEncode[1] = '#'; +@@ -1171,25 +1163,21 @@ WideString EncodeXML(const ByteString& bsXML) { + } + } + } +- wsResultBuf.AppendChar(0); + return wsResultBuf.MakeString(); + } + + ByteString TrillionUS(ByteStringView bsData) { +- static const ByteStringView pUnits[] = {"zero", "one", "two", "three", +- "four", "five", "six", "seven", +- "eight", "nine"}; +- static const ByteStringView pCapUnits[] = {"Zero", "One", "Two", "Three", +- "Four", "Five", "Six", "Seven", +- "Eight", "Nine"}; +- static const ByteStringView pTens[] = { ++ static const char kUnits[][6] = {"zero", "one", "two", "three", "four", ++ "five", "six", "seven", "eight", "nine"}; ++ static const char kCapUnits[][6] = {"Zero", "One", "Two", "Three", "Four", ++ "Five", "Six", "Seven", "Eight", "Nine"}; ++ static const char kTens[][10] = { + "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", + "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"}; +- static const ByteStringView pLastTens[] = {"Twenty", "Thirty", "Forty", +- "Fifty", "Sixty", "Seventy", +- "Eighty", "Ninety"}; +- static const ByteStringView pComm[] = {" Hundred ", " Thousand ", " Million ", +- " Billion ", "Trillion"}; ++ static const char kLastTens[][8] = {"Twenty", "Thirty", "Forty", "Fifty", ++ "Sixty", "Seventy", "Eighty", "Ninety"}; ++ static const char kComm[][11] = {" Hundred ", " Thousand ", " Million ", ++ " Billion ", "Trillion"}; + const char* pData = bsData.unterminated_c_str(); + int32_t iLength = bsData.GetLength(); + int32_t iComm = 0; +@@ -1206,89 +1194,86 @@ ByteString TrillionUS(ByteStringView bsData) { + if (iFirstCount == 0) + iFirstCount = 3; + +- std::ostringstream strBuf; ++ ByteString strBuf; + int32_t iIndex = 0; + if (iFirstCount == 3) { + if (pData[iIndex] != '0') { +- strBuf << pCapUnits[pData[iIndex] - '0']; +- strBuf << pComm[0]; ++ strBuf += kCapUnits[pData[iIndex] - '0']; ++ strBuf += kComm[0]; + } + if (pData[iIndex + 1] == '0') { +- strBuf << pCapUnits[pData[iIndex + 2] - '0']; ++ strBuf += kCapUnits[pData[iIndex + 2] - '0']; + } else { + if (pData[iIndex + 1] > '1') { +- strBuf << pLastTens[pData[iIndex + 1] - '2']; +- strBuf << "-"; +- strBuf << pUnits[pData[iIndex + 2] - '0']; ++ strBuf += kLastTens[pData[iIndex + 1] - '2']; ++ strBuf += "-"; ++ strBuf += kUnits[pData[iIndex + 2] - '0']; + } else if (pData[iIndex + 1] == '1') { +- strBuf << pTens[pData[iIndex + 2] - '0']; ++ strBuf += kTens[pData[iIndex + 2] - '0']; + } else if (pData[iIndex + 1] == '0') { +- strBuf << pCapUnits[pData[iIndex + 2] - '0']; ++ strBuf += kCapUnits[pData[iIndex + 2] - '0']; + } + } + iIndex += 3; + } else if (iFirstCount == 2) { + if (pData[iIndex] == '0') { +- strBuf << pCapUnits[pData[iIndex + 1] - '0']; ++ strBuf += kCapUnits[pData[iIndex + 1] - '0']; + } else { + if (pData[iIndex] > '1') { +- strBuf << pLastTens[pData[iIndex] - '2']; +- strBuf << "-"; +- strBuf << pUnits[pData[iIndex + 1] - '0']; ++ strBuf += kLastTens[pData[iIndex] - '2']; ++ strBuf += "-"; ++ strBuf += kUnits[pData[iIndex + 1] - '0']; + } else if (pData[iIndex] == '1') { +- strBuf << pTens[pData[iIndex + 1] - '0']; ++ strBuf += kTens[pData[iIndex + 1] - '0']; + } else if (pData[iIndex] == '0') { +- strBuf << pCapUnits[pData[iIndex + 1] - '0']; ++ strBuf += kCapUnits[pData[iIndex + 1] - '0']; + } + } + iIndex += 2; + } else if (iFirstCount == 1) { +- strBuf << pCapUnits[pData[iIndex] - '0']; ++ strBuf += kCapUnits[pData[iIndex] - '0']; + ++iIndex; + } + if (iLength > 3 && iFirstCount > 0) { +- strBuf << pComm[iComm]; ++ strBuf += kComm[iComm]; + --iComm; + } + while (iIndex < iLength) { + if (pData[iIndex] != '0') { +- strBuf << pCapUnits[pData[iIndex] - '0']; +- strBuf << pComm[0]; ++ strBuf += kCapUnits[pData[iIndex] - '0']; ++ strBuf += kComm[0]; + } + if (pData[iIndex + 1] == '0') { +- strBuf << pCapUnits[pData[iIndex + 2] - '0']; ++ strBuf += kCapUnits[pData[iIndex + 2] - '0']; + } else { + if (pData[iIndex + 1] > '1') { +- strBuf << pLastTens[pData[iIndex + 1] - '2']; +- strBuf << "-"; +- strBuf << pUnits[pData[iIndex + 2] - '0']; ++ strBuf += kLastTens[pData[iIndex + 1] - '2']; ++ strBuf += "-"; ++ strBuf += kUnits[pData[iIndex + 2] - '0']; + } else if (pData[iIndex + 1] == '1') { +- strBuf << pTens[pData[iIndex + 2] - '0']; ++ strBuf += kTens[pData[iIndex + 2] - '0']; + } else if (pData[iIndex + 1] == '0') { +- strBuf << pCapUnits[pData[iIndex + 2] - '0']; ++ strBuf += kCapUnits[pData[iIndex + 2] - '0']; + } + } + if (iIndex < iLength - 3) { +- strBuf << pComm[iComm]; ++ strBuf += kComm[iComm]; + --iComm; + } + iIndex += 3; + } +- return ByteString(strBuf); ++ return strBuf; + } + +-ByteString WordUS(const ByteString& bsData, int32_t iStyle) { +- const char* pData = bsData.c_str(); +- int32_t iLength = bsData.GetLength(); +- if (iStyle < 0 || iStyle > 2) { ++ByteString WordUS(ByteStringView bsData, int32_t iStyle) { ++ if (iStyle < 0 || iStyle > 2) + return ByteString(); +- } +- +- std::ostringstream strBuf; + ++ int32_t iLength = bsData.GetLength(); ++ ByteString strBuf; + int32_t iIndex = 0; + while (iIndex < iLength) { +- if (pData[iIndex] == '.') ++ if (bsData[iIndex] == '.') + break; + ++iIndex; + } +@@ -1299,535 +1284,641 @@ ByteString WordUS(const ByteString& bsData, int32_t iStyle) { + if (!iCount && iInteger - iIndex > 0) + iCount = 12; + +- strBuf << TrillionUS(ByteStringView(pData + iIndex, iCount)); ++ strBuf += TrillionUS(bsData.Substr(iIndex, iCount)); + iIndex += iCount; + if (iIndex < iInteger) +- strBuf << " Trillion "; ++ strBuf += " Trillion "; + } + + if (iStyle > 0) +- strBuf << " Dollars"; ++ strBuf += " Dollars"; + + if (iStyle > 1 && iInteger < iLength) { +- strBuf << " And "; ++ strBuf += " And "; + iIndex = iInteger + 1; + while (iIndex < iLength) { + int32_t iCount = (iLength - iIndex) % 12; + if (!iCount && iLength - iIndex > 0) + iCount = 12; + +- strBuf << TrillionUS(ByteStringView(pData + iIndex, iCount)); ++ strBuf += TrillionUS(bsData.Substr(iIndex, iCount)); + iIndex += iCount; + if (iIndex < iLength) +- strBuf << " Trillion "; ++ strBuf += " Trillion "; + } +- strBuf << " Cents"; ++ strBuf += " Cents"; + } +- return ByteString(strBuf); ++ return strBuf; + } + +-} // namespace ++v8::Local GetObjectDefaultValue(v8::Isolate* pIsolate, ++ v8::Local pObject) { ++ CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pIsolate, pObject)); ++ if (!pNode) ++ return fxv8::NewNullHelper(pIsolate); + +-const FXJSE_CLASS_DESCRIPTOR kFormCalcFM2JSDescriptor = { +- kClassTag, // tag +- "XFA_FM2JS_FormCalcClass", // name +- kFormCalcFM2JSFunctions, // methods +- FX_ArraySize(kFormCalcFM2JSFunctions), // number of methods +- nullptr, // dynamic prop type +- nullptr, // dynamic prop getter +- nullptr, // dynamic prop setter +- nullptr, // dynamic prop method call +-}; ++ v8::Local value; ++ pNode->JSObject()->ScriptSomDefaultValue(pIsolate, &value, false, ++ XFA_Attribute::Unknown); ++ return value; ++} + +-// static +-void CFXJSE_FormCalcContext::Abs(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Abs"); +- return; ++bool SetObjectDefaultValue(v8::Isolate* pIsolate, ++ v8::Local pObject, ++ v8::Local hNewValue) { ++ CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pIsolate, pObject)); ++ if (!pNode) ++ return false; ++ ++ pNode->JSObject()->ScriptSomDefaultValue(pIsolate, &hNewValue, true, ++ XFA_Attribute::Unknown); ++ return true; ++} ++ ++v8::Local GetExtractedValue(v8::Isolate* pIsolate, ++ v8::Local pValue) { ++ if (pValue.IsEmpty()) ++ return v8::Local(); ++ ++ if (fxv8::IsArray(pValue)) { ++ v8::Local arr = pValue.As(); ++ uint32_t iLength = fxv8::GetArrayLengthHelper(arr); ++ if (iLength < 3) ++ return fxv8::NewUndefinedHelper(pIsolate); ++ ++ v8::Local propertyValue = ++ fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1); ++ v8::Local jsValue = ++ fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 2); ++ if (!fxv8::IsObject(jsValue)) ++ return fxv8::NewUndefinedHelper(pIsolate); ++ ++ v8::Local jsObjectValue = jsValue.As(); ++ if (fxv8::IsNull(propertyValue)) ++ return GetObjectDefaultValue(pIsolate, jsObjectValue); ++ ++ ByteString bsName = ++ fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue); ++ return fxv8::ReentrantGetObjectPropertyHelper(pIsolate, jsObjectValue, ++ bsName.AsStringView()); + } + +- std::unique_ptr argOne = args.GetValue(0); +- if (ValueIsNull(pThis, argOne.get())) { +- args.GetReturnValue()->SetNull(); +- return; ++ if (fxv8::IsObject(pValue)) ++ return GetObjectDefaultValue(pIsolate, pValue.As()); ++ ++ return pValue; ++} ++ ++v8::Local GetSimpleValue( ++ const v8::FunctionCallbackInfo& info, ++ uint32_t index) { ++ DCHECK(index < static_cast(info.Length())); ++ return GetExtractedValue(info.GetIsolate(), info[index]); ++} ++ ++bool ValueIsNull(v8::Isolate* pIsolate, v8::Local arg) { ++ v8::Local extracted = GetExtractedValue(pIsolate, arg); ++ return extracted.IsEmpty() || fxv8::IsNull(extracted); ++} ++ ++int32_t ValueToInteger(v8::Isolate* pIsolate, v8::Local arg) { ++ v8::Local extracted = GetExtractedValue(pIsolate, arg); ++ if (extracted.IsEmpty()) ++ return 0; ++ ++ if (fxv8::IsObject(extracted) || fxv8::IsArray(extracted)) ++ return ValueToInteger(pIsolate, extracted); ++ ++ if (fxv8::IsString(extracted)) { ++ ByteString bsValue = fxv8::ReentrantToByteStringHelper(pIsolate, extracted); ++ return FXSYS_atoi(bsValue.c_str()); + } + +- double dValue = ValueToDouble(pThis, argOne.get()); +- if (dValue < 0) +- dValue = -dValue; ++ return fxv8::ReentrantToInt32Helper(pIsolate, extracted); ++} ++ ++float ValueToFloat(v8::Isolate* pIsolate, v8::Local arg) { ++ v8::Local extracted = GetExtractedValue(pIsolate, arg); ++ if (extracted.IsEmpty()) ++ return 0.0f; ++ ++ if (fxv8::IsUndefined(extracted)) ++ return 0.0f; ++ ++ if (fxv8::IsObject(extracted) || fxv8::IsArray(extracted)) ++ return ValueToFloat(pIsolate, extracted); + +- args.GetReturnValue()->SetDouble(dValue); ++ if (fxv8::IsString(extracted)) { ++ ByteString bsValue = fxv8::ReentrantToByteStringHelper(pIsolate, extracted); ++ return strtof(bsValue.c_str(), nullptr); ++ } ++ ++ return fxv8::ReentrantToFloatHelper(pIsolate, extracted); + } + +-// static +-void CFXJSE_FormCalcContext::Avg(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); +- if (argc < 1) { +- args.GetReturnValue()->SetNull(); +- return; ++double ValueToDouble(v8::Isolate* pIsolate, v8::Local arg) { ++ v8::Local extracted = GetExtractedValue(pIsolate, arg); ++ if (extracted.IsEmpty()) ++ return 0.0; ++ ++ if (fxv8::IsUndefined(extracted)) ++ return 0.0; ++ ++ if (fxv8::IsObject(extracted) || fxv8::IsArray(extracted)) ++ return ValueToDouble(pIsolate, extracted); ++ ++ if (fxv8::IsString(extracted)) { ++ ByteString bsValue = fxv8::ReentrantToByteStringHelper(pIsolate, extracted); ++ return strtod(bsValue.c_str(), nullptr); + } + +- v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime(); +- uint32_t uCount = 0; +- double dSum = 0.0; +- for (int32_t i = 0; i < argc; i++) { +- std::unique_ptr argValue = args.GetValue(i); +- if (argValue->IsNull()) +- continue; ++ return fxv8::ReentrantToDoubleHelper(pIsolate, extracted); ++} + +- if (!argValue->IsArray()) { +- dSum += ValueToDouble(pThis, argValue.get()); +- uCount++; +- continue; ++absl::optional ExtractDouble(v8::Isolate* pIsolate, ++ v8::Local src) { ++ if (src.IsEmpty()) ++ return 0.0; ++ ++ if (!fxv8::IsArray(src)) ++ return ValueToDouble(pIsolate, src); ++ ++ v8::Local arr = src.As(); ++ uint32_t iLength = fxv8::GetArrayLengthHelper(arr); ++ if (iLength < 3) ++ return absl::nullopt; ++ ++ v8::Local propertyValue = ++ fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1); ++ v8::Local jsValue = ++ fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 2); ++ if (fxv8::IsNull(propertyValue) || !fxv8::IsObject(jsValue)) ++ return ValueToDouble(pIsolate, jsValue); ++ ++ ByteString bsName = ++ fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue); ++ return ValueToDouble( ++ pIsolate, fxv8::ReentrantGetObjectPropertyHelper( ++ pIsolate, jsValue.As(), bsName.AsStringView())); ++} ++ ++ByteString ValueToUTF8String(v8::Isolate* pIsolate, v8::Local arg) { ++ if (arg.IsEmpty()) ++ return ByteString(); ++ ++ if (fxv8::IsNull(arg) || fxv8::IsUndefined(arg)) ++ return ByteString(); ++ ++ if (fxv8::IsBoolean(arg)) ++ return fxv8::ReentrantToBooleanHelper(pIsolate, arg) ? "1" : "0"; ++ ++ return fxv8::ReentrantToByteStringHelper(pIsolate, arg); ++} ++ ++bool SimpleValueCompare(v8::Isolate* pIsolate, ++ v8::Local firstValue, ++ v8::Local secondValue) { ++ if (firstValue.IsEmpty()) ++ return false; ++ ++ if (fxv8::IsString(firstValue)) { ++ const ByteString first = ValueToUTF8String(pIsolate, firstValue); ++ const ByteString second = ValueToUTF8String(pIsolate, secondValue); ++ return first == second; ++ } ++ if (fxv8::IsNumber(firstValue)) { ++ const float first = ValueToFloat(pIsolate, firstValue); ++ const float second = ValueToFloat(pIsolate, secondValue); ++ return first == second; ++ } ++ if (fxv8::IsBoolean(firstValue)) { ++ const bool first = fxv8::ReentrantToBooleanHelper(pIsolate, firstValue); ++ const bool second = fxv8::ReentrantToBooleanHelper(pIsolate, secondValue); ++ return first == second; ++ } ++ return fxv8::IsNull(firstValue) && fxv8::IsNull(secondValue); ++} ++ ++std::vector> UnfoldArgs( ++ const v8::FunctionCallbackInfo& info) { ++ std::vector> results; ++ v8::Isolate* pIsolate = info.GetIsolate(); ++ for (int i = 1; i < info.Length(); ++i) { ++ v8::Local arg = info[i]; ++ if (fxv8::IsArray(arg)) { ++ v8::Local arr = arg.As(); ++ uint32_t iLength = fxv8::GetArrayLengthHelper(arr); ++ if (iLength < 3) ++ continue; ++ ++ v8::Local propertyValue = ++ fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1); ++ ++ for (uint32_t j = 2; j < iLength; j++) { ++ v8::Local jsValue = ++ fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, j); ++ ++ if (!fxv8::IsObject(jsValue)) { ++ results.push_back(fxv8::NewUndefinedHelper(pIsolate)); ++ } else if (fxv8::IsNull(propertyValue)) { ++ results.push_back( ++ GetObjectDefaultValue(pIsolate, jsValue.As())); ++ } else { ++ ByteString bsName = ++ fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue); ++ results.push_back(fxv8::ReentrantGetObjectPropertyHelper( ++ pIsolate, jsValue.As(), bsName.AsStringView())); ++ } ++ } ++ } else if (fxv8::IsObject(arg)) { ++ results.push_back(GetObjectDefaultValue(pIsolate, arg.As())); ++ } else { ++ results.push_back(arg); + } ++ } ++ return results; ++} + +- auto lengthValue = pdfium::MakeUnique(pIsolate); +- argValue->GetObjectProperty("length", lengthValue.get()); +- int32_t iLength = lengthValue->ToInteger(); +- +- if (iLength > 2) { +- auto propertyValue = pdfium::MakeUnique(pIsolate); +- argValue->GetObjectPropertyByIdx(1, propertyValue.get()); +- +- auto jsObjectValue = pdfium::MakeUnique(pIsolate); +- if (propertyValue->IsNull()) { +- for (int32_t j = 2; j < iLength; j++) { +- argValue->GetObjectPropertyByIdx(j, jsObjectValue.get()); +- auto defaultPropValue = pdfium::MakeUnique(pIsolate); +- GetObjectDefaultValue(jsObjectValue.get(), defaultPropValue.get()); +- if (defaultPropValue->IsNull()) +- continue; +- +- dSum += ValueToDouble(pThis, defaultPropValue.get()); +- uCount++; ++// Returns empty value on failure. ++v8::Local GetObjectForName(CFXJSE_HostObject* pHostObject, ++ ByteStringView bsAccessorName) { ++ CXFA_Document* pDoc = ToFormCalcContext(pHostObject)->GetDocument(); ++ if (!pDoc) ++ return v8::Local(); ++ ++ CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext(); ++ absl::optional maybeResult = ++ pScriptContext->ResolveObjects( ++ pScriptContext->GetThisObject(), ++ WideString::FromUTF8(bsAccessorName).AsStringView(), ++ Mask{ ++ XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kProperties, ++ XFA_ResolveFlag::kSiblings, XFA_ResolveFlag::kParent}); ++ if (!maybeResult.has_value() || ++ maybeResult.value().type != CFXJSE_Engine::ResolveResult::Type::kNodes || ++ maybeResult.value().objects.empty()) { ++ return v8::Local(); ++ } ++ return pScriptContext->GetOrCreateJSBindingFromMap( ++ maybeResult.value().objects.front().Get()); ++} ++ ++absl::optional ResolveObjects( ++ CFXJSE_HostObject* pHostObject, ++ v8::Local pRefValue, ++ ByteStringView bsSomExp, ++ bool bDotAccessor, ++ bool bHasNoResolveName) { ++ CXFA_Document* pDoc = ToFormCalcContext(pHostObject)->GetDocument(); ++ if (!pDoc) ++ return absl::nullopt; ++ ++ v8::Isolate* pIsolate = ToFormCalcContext(pHostObject)->GetIsolate(); ++ WideString wsSomExpression = WideString::FromUTF8(bsSomExp); ++ CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext(); ++ CXFA_Object* pNode = nullptr; ++ Mask dwFlags; ++ if (bDotAccessor) { ++ if (fxv8::IsNull(pRefValue)) { ++ pNode = pScriptContext->GetThisObject(); ++ dwFlags = {XFA_ResolveFlag::kSiblings, XFA_ResolveFlag::kParent}; ++ } else { ++ pNode = CFXJSE_Engine::ToObject(pIsolate, pRefValue); ++ if (!pNode) ++ return absl::nullopt; ++ ++ if (bHasNoResolveName) { ++ WideString wsName; ++ if (CXFA_Node* pXFANode = pNode->AsNode()) { ++ absl::optional ret = ++ pXFANode->JSObject()->TryAttribute(XFA_Attribute::Name, false); ++ if (ret.has_value()) ++ wsName = ret.value(); + } ++ if (wsName.IsEmpty()) ++ wsName = L"#" + WideString::FromASCII(pNode->GetClassName()); ++ ++ wsSomExpression = wsName + wsSomExpression; ++ dwFlags = XFA_ResolveFlag::kSiblings; + } else { +- for (int32_t j = 2; j < iLength; j++) { +- argValue->GetObjectPropertyByIdx(j, jsObjectValue.get()); +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- jsObjectValue->GetObjectProperty( +- propertyValue->ToString().AsStringView(), newPropertyValue.get()); +- if (newPropertyValue->IsNull()) +- continue; +- +- dSum += ValueToDouble(pThis, newPropertyValue.get()); +- uCount++; +- } ++ dwFlags = (bsSomExp == "*") ++ ? Mask{XFA_ResolveFlag::kChildren} ++ : Mask{XFA_ResolveFlag::kChildren, ++ XFA_ResolveFlag::kAttributes, ++ XFA_ResolveFlag::kProperties}; + } + } ++ } else { ++ pNode = CFXJSE_Engine::ToObject(pIsolate, pRefValue); ++ dwFlags = XFA_ResolveFlag::kAnyChild; + } +- if (uCount == 0) { +- args.GetReturnValue()->SetNull(); +- return; ++ return pScriptContext->ResolveObjects(pNode, wsSomExpression.AsStringView(), ++ dwFlags); ++} ++ ++std::vector> ParseResolveResult( ++ CFXJSE_HostObject* pHostObject, ++ const CFXJSE_Engine::ResolveResult& resolveNodeRS, ++ v8::Local pParentValue, ++ bool* bAttribute) { ++ std::vector> resultValues; ++ CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pHostObject); ++ v8::Isolate* pIsolate = pContext->GetIsolate(); ++ ++ if (resolveNodeRS.type == CFXJSE_Engine::ResolveResult::Type::kNodes) { ++ *bAttribute = false; ++ CFXJSE_Engine* pScriptContext = pContext->GetDocument()->GetScriptContext(); ++ for (auto& pObject : resolveNodeRS.objects) { ++ resultValues.push_back( ++ pScriptContext->GetOrCreateJSBindingFromMap(pObject.Get())); ++ } ++ return resultValues; ++ } ++ ++ *bAttribute = true; ++ if (resolveNodeRS.script_attribute.callback && ++ resolveNodeRS.script_attribute.eValueType == XFA_ScriptType::Object) { ++ for (auto& pObject : resolveNodeRS.objects) { ++ v8::Local pValue; ++ CJX_Object* jsObject = pObject->JSObject(); ++ (*resolveNodeRS.script_attribute.callback)( ++ pIsolate, jsObject, &pValue, false, ++ resolveNodeRS.script_attribute.attribute); ++ resultValues.push_back(pValue); ++ *bAttribute = false; ++ } ++ } ++ if (*bAttribute && fxv8::IsObject(pParentValue)) ++ resultValues.push_back(pParentValue); ++ ++ return resultValues; ++} ++ ++// Returns 0 if the provided `arg` is an invalid payment period count. ++int GetValidatedPaymentPeriods(v8::Isolate* isolate, v8::Local arg) { ++ double periods = ValueToDouble(isolate, arg); ++ if (periods < 1 || ++ periods > static_cast(std::numeric_limits::max())) { ++ return 0; + } + +- args.GetReturnValue()->SetDouble(dSum / uCount); ++ return static_cast(periods); + } + ++} // namespace ++ ++const FXJSE_CLASS_DESCRIPTOR kFormCalcDescriptor = { ++ kClassTag, // tag ++ "XFA_FormCalcClass", // name ++ kFormCalcFunctions, // methods ++ std::size(kFormCalcFunctions), // number of methods ++ nullptr, // dynamic prop type ++ nullptr, // dynamic prop getter ++ nullptr, // dynamic prop setter ++ nullptr, // dynamic prop method call ++}; ++ + // static +-void CFXJSE_FormCalcContext::Ceil(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Ceil"); ++void CFXJSE_FormCalcContext::Abs( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 1) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Abs"); + return; + } + +- std::unique_ptr argValue = GetSimpleValue(pThis, args, 0); +- if (ValueIsNull(pThis, argValue.get())) { +- args.GetReturnValue()->SetNull(); ++ if (ValueIsNull(info.GetIsolate(), info[0])) { ++ info.GetReturnValue().SetNull(); + return; + } ++ double dValue = ValueToDouble(info.GetIsolate(), info[0]); ++ if (dValue < 0) ++ dValue = -dValue; + +- args.GetReturnValue()->SetFloat(ceil(ValueToFloat(pThis, argValue.get()))); ++ info.GetReturnValue().Set(dValue); + } + + // static +-void CFXJSE_FormCalcContext::Count(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- v8::Isolate* pIsolate = pContext->GetScriptRuntime(); +- int32_t iCount = 0; +- for (int32_t i = 0; i < args.GetLength(); i++) { +- std::unique_ptr argValue = args.GetValue(i); +- if (argValue->IsNull()) +- continue; ++void CFXJSE_FormCalcContext::Avg( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ uint32_t uCount = 0; ++ double dSum = 0.0; ++ auto fn = [&uCount, &dSum](v8::Isolate* pIsolate, ++ v8::Local pValue) { ++ dSum += ValueToDouble(pIsolate, pValue); ++ uCount++; ++ }; ++ if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/false)) ++ return; + +- if (argValue->IsArray()) { +- auto lengthValue = pdfium::MakeUnique(pIsolate); +- argValue->GetObjectProperty("length", lengthValue.get()); ++ if (uCount == 0) { ++ info.GetReturnValue().SetNull(); ++ return; ++ } ++ info.GetReturnValue().Set(dSum / uCount); ++} + +- int32_t iLength = lengthValue->ToInteger(); +- if (iLength <= 2) { +- pContext->ThrowArgumentMismatchException(); +- return; +- } ++// static ++void CFXJSE_FormCalcContext::Ceil( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 1) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Ceil"); ++ return; ++ } + +- auto propertyValue = pdfium::MakeUnique(pIsolate); +- auto jsObjectValue = pdfium::MakeUnique(pIsolate); +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- argValue->GetObjectPropertyByIdx(1, propertyValue.get()); +- argValue->GetObjectPropertyByIdx(2, jsObjectValue.get()); +- if (propertyValue->IsNull()) { +- for (int32_t j = 2; j < iLength; j++) { +- argValue->GetObjectPropertyByIdx(j, jsObjectValue.get()); +- GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get()); +- if (!newPropertyValue->IsNull()) +- iCount++; +- } +- } else { +- for (int32_t j = 2; j < iLength; j++) { +- argValue->GetObjectPropertyByIdx(j, jsObjectValue.get()); +- jsObjectValue->GetObjectProperty( +- propertyValue->ToString().AsStringView(), newPropertyValue.get()); +- iCount += newPropertyValue->IsNull() ? 0 : 1; +- } +- } +- } else if (argValue->IsObject()) { +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- GetObjectDefaultValue(argValue.get(), newPropertyValue.get()); +- if (!newPropertyValue->IsNull()) +- iCount++; +- } else { +- iCount++; +- } ++ v8::Local argValue = GetSimpleValue(info, 0); ++ if (ValueIsNull(info.GetIsolate(), argValue)) { ++ info.GetReturnValue().SetNull(); ++ return; + } +- args.GetReturnValue()->SetInteger(iCount); ++ ++ info.GetReturnValue().Set(ceil(ValueToFloat(info.GetIsolate(), argValue))); + } + + // static +-void CFXJSE_FormCalcContext::Floor(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Floor"); ++void CFXJSE_FormCalcContext::Count( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ uint32_t iCount = 0; ++ auto fn = [&iCount](v8::Isolate* pIsolate, v8::Local pvalue) { ++ ++iCount; ++ }; ++ if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true)) ++ return; ++ ++ info.GetReturnValue().Set(iCount); ++} ++ ++// static ++void CFXJSE_FormCalcContext::Floor( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 1) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Floor"); + return; + } + +- std::unique_ptr argValue = GetSimpleValue(pThis, args, 0); +- if (ValueIsNull(pThis, argValue.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argValue = GetSimpleValue(info, 0); ++ if (ValueIsNull(info.GetIsolate(), argValue)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- args.GetReturnValue()->SetFloat(floor(ValueToFloat(pThis, argValue.get()))); ++ info.GetReturnValue().Set(floor(ValueToFloat(info.GetIsolate(), argValue))); + } + + // static +-void CFXJSE_FormCalcContext::Max(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- v8::Isolate* pIsolate = pContext->GetScriptRuntime(); ++void CFXJSE_FormCalcContext::Max( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + uint32_t uCount = 0; + double dMaxValue = 0.0; +- for (int32_t i = 0; i < args.GetLength(); i++) { +- std::unique_ptr argValue = args.GetValue(i); +- if (argValue->IsNull()) +- continue; +- +- if (argValue->IsArray()) { +- auto lengthValue = pdfium::MakeUnique(pIsolate); +- argValue->GetObjectProperty("length", lengthValue.get()); +- int32_t iLength = lengthValue->ToInteger(); +- if (iLength <= 2) { +- pContext->ThrowArgumentMismatchException(); +- return; +- } +- +- auto propertyValue = pdfium::MakeUnique(pIsolate); +- auto jsObjectValue = pdfium::MakeUnique(pIsolate); +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- argValue->GetObjectPropertyByIdx(1, propertyValue.get()); +- argValue->GetObjectPropertyByIdx(2, jsObjectValue.get()); +- if (propertyValue->IsNull()) { +- for (int32_t j = 2; j < iLength; j++) { +- argValue->GetObjectPropertyByIdx(j, jsObjectValue.get()); +- GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get()); +- if (newPropertyValue->IsNull()) +- continue; +- +- uCount++; +- double dValue = ValueToDouble(pThis, newPropertyValue.get()); +- dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue); +- } +- } else { +- for (int32_t j = 2; j < iLength; j++) { +- argValue->GetObjectPropertyByIdx(j, jsObjectValue.get()); +- jsObjectValue->GetObjectProperty( +- propertyValue->ToString().AsStringView(), newPropertyValue.get()); +- if (newPropertyValue->IsNull()) +- continue; +- +- uCount++; +- double dValue = ValueToDouble(pThis, newPropertyValue.get()); +- dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue); +- } +- } +- } else if (argValue->IsObject()) { +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- GetObjectDefaultValue(argValue.get(), newPropertyValue.get()); +- if (newPropertyValue->IsNull()) +- continue; ++ auto fn = [&uCount, &dMaxValue](v8::Isolate* pIsolate, ++ v8::Local pValue) { ++ ++uCount; ++ double dValue = ValueToDouble(pIsolate, pValue); ++ dMaxValue = uCount == 1 ? dValue : std::max(dMaxValue, dValue); ++ }; ++ if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true)) ++ return; + +- uCount++; +- double dValue = ValueToDouble(pThis, newPropertyValue.get()); +- dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue); +- } else { +- uCount++; +- double dValue = ValueToDouble(pThis, argValue.get()); +- dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue); +- } +- } + if (uCount == 0) { +- args.GetReturnValue()->SetNull(); ++ info.GetReturnValue().SetNull(); + return; + } +- +- args.GetReturnValue()->SetDouble(dMaxValue); ++ info.GetReturnValue().Set(dMaxValue); + } + + // static +-void CFXJSE_FormCalcContext::Min(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- v8::Isolate* pIsolate = pContext->GetScriptRuntime(); ++void CFXJSE_FormCalcContext::Min( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + uint32_t uCount = 0; + double dMinValue = 0.0; +- for (int32_t i = 0; i < args.GetLength(); i++) { +- std::unique_ptr argValue = args.GetValue(i); +- if (argValue->IsNull()) +- continue; +- +- if (argValue->IsArray()) { +- auto lengthValue = pdfium::MakeUnique(pIsolate); +- argValue->GetObjectProperty("length", lengthValue.get()); +- int32_t iLength = lengthValue->ToInteger(); +- if (iLength <= 2) { +- pContext->ThrowArgumentMismatchException(); +- return; +- } +- +- auto propertyValue = pdfium::MakeUnique(pIsolate); +- auto jsObjectValue = pdfium::MakeUnique(pIsolate); +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- argValue->GetObjectPropertyByIdx(1, propertyValue.get()); +- argValue->GetObjectPropertyByIdx(2, jsObjectValue.get()); +- if (propertyValue->IsNull()) { +- for (int32_t j = 2; j < iLength; j++) { +- argValue->GetObjectPropertyByIdx(j, jsObjectValue.get()); +- GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get()); +- if (newPropertyValue->IsNull()) +- continue; +- +- uCount++; +- double dValue = ValueToDouble(pThis, newPropertyValue.get()); +- dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue); +- } +- } else { +- for (int32_t j = 2; j < iLength; j++) { +- argValue->GetObjectPropertyByIdx(j, jsObjectValue.get()); +- jsObjectValue->GetObjectProperty( +- propertyValue->ToString().AsStringView(), newPropertyValue.get()); +- if (newPropertyValue->IsNull()) +- continue; +- +- uCount++; +- double dValue = ValueToDouble(pThis, newPropertyValue.get()); +- dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue); +- } +- } +- } else if (argValue->IsObject()) { +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- GetObjectDefaultValue(argValue.get(), newPropertyValue.get()); +- if (newPropertyValue->IsNull()) +- continue; ++ auto fn = [&uCount, &dMinValue](v8::Isolate* pIsolate, ++ v8::Local pValue) { ++ ++uCount; ++ double dValue = ValueToDouble(pIsolate, pValue); ++ dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue); ++ }; ++ if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true)) ++ return; + +- uCount++; +- double dValue = ValueToDouble(pThis, newPropertyValue.get()); +- dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue); +- } else { +- uCount++; +- double dValue = ValueToDouble(pThis, argValue.get()); +- dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue); +- } +- } + if (uCount == 0) { +- args.GetReturnValue()->SetNull(); ++ info.GetReturnValue().SetNull(); + return; + } +- +- args.GetReturnValue()->SetDouble(dMinValue); ++ info.GetReturnValue().Set(dMinValue); + } + + // static +-void CFXJSE_FormCalcContext::Mod(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::Mod( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 2) { +- pContext->ThrowParamCountMismatchException(L"Mod"); ++ if (info.Length() != 2) { ++ pContext->ThrowParamCountMismatchException("Mod"); + return; + } + +- std::unique_ptr argOne = args.GetValue(0); +- std::unique_ptr argTwo = args.GetValue(1); +- if (argOne->IsNull() || argTwo->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ if (fxv8::IsNull(info[0]) || fxv8::IsNull(info[1])) { ++ info.GetReturnValue().SetNull(); + return; + } + +- bool argOneResult; +- double dDividend = ExtractDouble(pThis, argOne.get(), &argOneResult); +- bool argTwoResult; +- double dDivisor = ExtractDouble(pThis, argTwo.get(), &argTwoResult); +- if (!argOneResult || !argTwoResult) { ++ absl::optional maybe_dividend = ++ ExtractDouble(info.GetIsolate(), info[0]); ++ absl::optional maybe_divisor = ++ ExtractDouble(info.GetIsolate(), info[1]); ++ if (!maybe_dividend.has_value() || !maybe_divisor.has_value()) { + pContext->ThrowArgumentMismatchException(); + return; + } + +- if (dDivisor == 0.0) { ++ double dividend = maybe_dividend.value(); ++ double divisor = maybe_divisor.value(); ++ if (divisor == 0.0) { + pContext->ThrowDivideByZeroException(); + return; + } + +- args.GetReturnValue()->SetDouble(dDividend - +- dDivisor * (int32_t)(dDividend / dDivisor)); ++ info.GetReturnValue().Set(dividend - ++ divisor * static_cast(dividend / divisor)); + } + + // static +-void CFXJSE_FormCalcContext::Round(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::Round( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- int32_t argc = args.GetLength(); ++ int32_t argc = info.Length(); + if (argc < 1 || argc > 2) { +- pContext->ThrowParamCountMismatchException(L"Round"); ++ pContext->ThrowParamCountMismatchException("Round"); + return; + } + +- std::unique_ptr argOne = args.GetValue(0); +- if (argOne->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ if (fxv8::IsNull(info[0])) { ++ info.GetReturnValue().SetNull(); + return; + } + +- bool dValueRet; +- double dValue = ExtractDouble(pThis, argOne.get(), &dValueRet); +- if (!dValueRet) { ++ absl::optional maybe_value = ++ ExtractDouble(info.GetIsolate(), info[0]); ++ if (!maybe_value.has_value()) { + pContext->ThrowArgumentMismatchException(); + return; + } + ++ double dValue = maybe_value.value(); + uint8_t uPrecision = 0; + if (argc > 1) { +- std::unique_ptr argTwo = args.GetValue(1); +- if (argTwo->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ if (fxv8::IsNull(info[1])) { ++ info.GetReturnValue().SetNull(); + return; + } +- +- bool dPrecisionRet; +- double dPrecision = ExtractDouble(pThis, argTwo.get(), &dPrecisionRet); +- if (!dPrecisionRet) { ++ absl::optional maybe_precision = ++ ExtractDouble(info.GetIsolate(), info[1]); ++ if (!maybe_precision.has_value()) { + pContext->ThrowArgumentMismatchException(); + return; + } +- ++ double dPrecision = maybe_precision.value(); + uPrecision = static_cast(pdfium::clamp(dPrecision, 0.0, 12.0)); + } + + CFGAS_Decimal decimalValue(static_cast(dValue), uPrecision); +- args.GetReturnValue()->SetDouble(decimalValue.ToDouble()); ++ info.GetReturnValue().Set(decimalValue.ToDouble()); + } + + // static +-void CFXJSE_FormCalcContext::Sum(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); +- if (argc == 0) { +- args.GetReturnValue()->SetNull(); +- return; +- } +- +- CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- v8::Isolate* pIsolate = pContext->GetScriptRuntime(); ++void CFXJSE_FormCalcContext::Sum( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + uint32_t uCount = 0; + double dSum = 0.0; +- for (int32_t i = 0; i < argc; i++) { +- std::unique_ptr argValue = args.GetValue(i); +- if (argValue->IsNull()) +- continue; +- +- if (argValue->IsArray()) { +- auto lengthValue = pdfium::MakeUnique(pIsolate); +- argValue->GetObjectProperty("length", lengthValue.get()); +- int32_t iLength = lengthValue->ToInteger(); +- if (iLength <= 2) { +- pContext->ThrowArgumentMismatchException(); +- return; +- } +- +- auto propertyValue = pdfium::MakeUnique(pIsolate); +- argValue->GetObjectPropertyByIdx(1, propertyValue.get()); +- auto jsObjectValue = pdfium::MakeUnique(pIsolate); +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- if (propertyValue->IsNull()) { +- for (int32_t j = 2; j < iLength; j++) { +- argValue->GetObjectPropertyByIdx(j, jsObjectValue.get()); +- GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get()); +- if (newPropertyValue->IsNull()) +- continue; +- +- dSum += ValueToDouble(pThis, jsObjectValue.get()); +- uCount++; +- } +- } else { +- for (int32_t j = 2; j < iLength; j++) { +- argValue->GetObjectPropertyByIdx(j, jsObjectValue.get()); +- jsObjectValue->GetObjectProperty( +- propertyValue->ToString().AsStringView(), newPropertyValue.get()); +- if (newPropertyValue->IsNull()) +- continue; +- +- dSum += ValueToDouble(pThis, newPropertyValue.get()); +- uCount++; +- } +- } +- } else if (argValue->IsObject()) { +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- GetObjectDefaultValue(argValue.get(), newPropertyValue.get()); +- if (newPropertyValue->IsNull()) +- continue; ++ auto fn = [&uCount, &dSum](v8::Isolate* pIsolate, ++ v8::Local pValue) { ++ ++uCount; ++ dSum += ValueToDouble(pIsolate, pValue); ++ }; ++ if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true)) ++ return; + +- dSum += ValueToDouble(pThis, argValue.get()); +- uCount++; +- } else { +- dSum += ValueToDouble(pThis, argValue.get()); +- uCount++; +- } +- } + if (uCount == 0) { +- args.GetReturnValue()->SetNull(); ++ info.GetReturnValue().SetNull(); + return; + } +- +- args.GetReturnValue()->SetDouble(dSum); ++ info.GetReturnValue().Set(dSum); + } + + // static +-void CFXJSE_FormCalcContext::Date(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 0) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Date"); ++void CFXJSE_FormCalcContext::Date( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 0) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Date"); + return; + } + +@@ -1835,140 +1926,141 @@ void CFXJSE_FormCalcContext::Date(CFXJSE_Value* pThis, + FXSYS_time(¤tTime); + struct tm* pTmStruct = gmtime(¤tTime); + +- args.GetReturnValue()->SetInteger(DateString2Num( ++ info.GetReturnValue().Set(DateString2Num( + ByteString::Format("%d%02d%02d", pTmStruct->tm_year + 1900, + pTmStruct->tm_mon + 1, pTmStruct->tm_mday) + .AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Date2Num(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::Date2Num( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc < 1 || argc > 3) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Date2Num"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Date2Num"); + return; + } + +- std::unique_ptr dateValue = GetSimpleValue(pThis, args, 0); +- if (ValueIsNull(pThis, dateValue.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local dateValue = GetSimpleValue(info, 0); ++ if (ValueIsNull(info.GetIsolate(), dateValue)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- ByteString bsDate = ValueToUTF8String(dateValue.get()); ++ ByteString bsDate = ValueToUTF8String(info.GetIsolate(), dateValue); + ByteString bsFormat; + if (argc > 1) { +- std::unique_ptr formatValue = GetSimpleValue(pThis, args, 1); +- if (ValueIsNull(pThis, formatValue.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local formatValue = GetSimpleValue(info, 1); ++ if (ValueIsNull(info.GetIsolate(), formatValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- bsFormat = ValueToUTF8String(formatValue.get()); ++ bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue); + } + + ByteString bsLocale; + if (argc > 2) { +- std::unique_ptr localeValue = GetSimpleValue(pThis, args, 2); +- if (ValueIsNull(pThis, localeValue.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local localeValue = GetSimpleValue(info, 2); ++ if (ValueIsNull(info.GetIsolate(), localeValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- bsLocale = ValueToUTF8String(localeValue.get()); ++ bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue); + } + + ByteString bsIsoDate = + Local2IsoDate(pThis, bsDate.AsStringView(), bsFormat.AsStringView(), + bsLocale.AsStringView()); +- args.GetReturnValue()->SetInteger(DateString2Num(bsIsoDate.AsStringView())); ++ info.GetReturnValue().Set(DateString2Num(bsIsoDate.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::DateFmt(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::DateFmt( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc > 2) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Date2Num"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Date2Num"); + return; + } + + int32_t iStyle = 0; + if (argc > 0) { +- std::unique_ptr argStyle = GetSimpleValue(pThis, args, 0); +- if (argStyle->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local infotyle = GetSimpleValue(info, 0); ++ if (fxv8::IsNull(infotyle)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- iStyle = (int32_t)ValueToFloat(pThis, argStyle.get()); ++ iStyle = static_cast(ValueToFloat(info.GetIsolate(), infotyle)); + if (iStyle < 0 || iStyle > 4) + iStyle = 0; + } + + ByteString bsLocale; + if (argc > 1) { +- std::unique_ptr argLocale = GetSimpleValue(pThis, args, 1); +- if (argLocale->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argLocale = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(argLocale)) { ++ info.GetReturnValue().SetNull(); + return; + } +- bsLocale = ValueToUTF8String(argLocale.get()); ++ bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale); + } + + ByteString bsFormat = + GetStandardDateFormat(pThis, iStyle, bsLocale.AsStringView()); +- args.GetReturnValue()->SetString(bsFormat.AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::IsoDate2Num(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"IsoDate2Num"); ++void CFXJSE_FormCalcContext::IsoDate2Num( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 1) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("IsoDate2Num"); + return; + } +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- if (argOne->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ if (fxv8::IsNull(argOne)) { ++ info.GetReturnValue().SetNull(); + return; + } +- ByteString bsArg = ValueToUTF8String(argOne.get()); +- args.GetReturnValue()->SetInteger(DateString2Num(bsArg.AsStringView())); ++ ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne); ++ info.GetReturnValue().Set(DateString2Num(bsArg.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::IsoTime2Num(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::IsoTime2Num( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 1) { +- pContext->ThrowParamCountMismatchException(L"IsoTime2Num"); ++ if (info.Length() != 1) { ++ pContext->ThrowParamCountMismatchException("IsoTime2Num"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- if (ValueIsNull(pThis, argOne.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ if (ValueIsNull(info.GetIsolate(), argOne)) { ++ info.GetReturnValue().SetNull(); + return; + } + + CXFA_Document* pDoc = pContext->GetDocument(); + CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr(); +- ByteString bsArg = ValueToUTF8String(argOne.get()); ++ ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne); + auto pos = bsArg.Find('T', 0); + if (!pos.has_value() || pos.value() == bsArg.GetLength() - 1) { +- args.GetReturnValue()->SetInteger(0); ++ info.GetReturnValue().Set(0); + return; + } + bsArg = bsArg.Last(bsArg.GetLength() - (pos.value() + 1)); + +- CXFA_LocaleValue timeValue(XFA_VT_TIME, ++ CXFA_LocaleValue timeValue(CXFA_LocaleValue::ValueType::kTime, + WideString::FromUTF8(bsArg.AsStringView()), pMgr); + if (!timeValue.IsValid()) { +- args.GetReturnValue()->SetInteger(0); ++ info.GetReturnValue().Set(0); + return; + } + +@@ -1981,7 +2073,7 @@ void CFXJSE_FormCalcContext::IsoTime2Num(CFXJSE_Value* pThis, + // TODO(dsinclair): See if there is other time conversion code in pdfium and + // consolidate. + int32_t mins = hour * 60 + min; +- mins -= (pMgr->GetDefLocale()->GetTimeZone().tzHour * 60); ++ mins -= pMgr->GetDefLocale()->GetTimeZoneInMinutes(); + while (mins > 1440) + mins -= 1440; + while (mins < 0) +@@ -1989,123 +2081,126 @@ void CFXJSE_FormCalcContext::IsoTime2Num(CFXJSE_Value* pThis, + hour = mins / 60; + min = mins % 60; + +- args.GetReturnValue()->SetInteger(hour * 3600000 + min * 60000 + +- second * 1000 + milSecond + 1); ++ info.GetReturnValue().Set(hour * 3600000 + min * 60000 + second * 1000 + ++ milSecond + 1); + } + + // static +-void CFXJSE_FormCalcContext::LocalDateFmt(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::LocalDateFmt( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc > 2) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"LocalDateFmt"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("LocalDateFmt"); + return; + } + + int32_t iStyle = 0; + if (argc > 0) { +- std::unique_ptr argStyle = GetSimpleValue(pThis, args, 0); +- if (argStyle->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local infotyle = GetSimpleValue(info, 0); ++ if (fxv8::IsNull(infotyle)) { ++ info.GetReturnValue().SetNull(); + return; + } +- iStyle = (int32_t)ValueToFloat(pThis, argStyle.get()); ++ iStyle = static_cast(ValueToFloat(info.GetIsolate(), infotyle)); + if (iStyle > 4 || iStyle < 0) + iStyle = 0; + } + + ByteString bsLocale; + if (argc > 1) { +- std::unique_ptr argLocale = GetSimpleValue(pThis, args, 1); +- if (argLocale->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argLocale = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(argLocale)) { ++ info.GetReturnValue().SetNull(); + return; + } +- bsLocale = ValueToUTF8String(argLocale.get()); ++ bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale); + } + + ByteString bsFormat = + GetLocalDateFormat(pThis, iStyle, bsLocale.AsStringView(), false); +- args.GetReturnValue()->SetString(bsFormat.AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::LocalTimeFmt(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::LocalTimeFmt( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc > 2) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"LocalTimeFmt"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("LocalTimeFmt"); + return; + } + + int32_t iStyle = 0; + if (argc > 0) { +- std::unique_ptr argStyle = GetSimpleValue(pThis, args, 0); +- if (argStyle->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local infotyle = GetSimpleValue(info, 0); ++ if (fxv8::IsNull(infotyle)) { ++ info.GetReturnValue().SetNull(); + return; + } +- iStyle = (int32_t)ValueToFloat(pThis, argStyle.get()); ++ iStyle = static_cast(ValueToFloat(info.GetIsolate(), infotyle)); + if (iStyle > 4 || iStyle < 0) + iStyle = 0; + } + + ByteString bsLocale; + if (argc > 1) { +- std::unique_ptr argLocale = GetSimpleValue(pThis, args, 1); +- if (argLocale->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argLocale = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(argLocale)) { ++ info.GetReturnValue().SetNull(); + return; + } +- bsLocale = ValueToUTF8String(argLocale.get()); ++ bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale); + } + + ByteString bsFormat = + GetLocalTimeFormat(pThis, iStyle, bsLocale.AsStringView(), false); +- args.GetReturnValue()->SetString(bsFormat.AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Num2Date(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::Num2Date( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc < 1 || argc > 3) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Num2Date"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Num2Date"); + return; + } + +- std::unique_ptr dateValue = GetSimpleValue(pThis, args, 0); +- if (ValueIsNull(pThis, dateValue.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local dateValue = GetSimpleValue(info, 0); ++ if (ValueIsNull(info.GetIsolate(), dateValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- int32_t dDate = (int32_t)ValueToFloat(pThis, dateValue.get()); ++ int32_t dDate = ++ static_cast(ValueToFloat(info.GetIsolate(), dateValue)); + if (dDate < 1) { +- args.GetReturnValue()->SetNull(); ++ info.GetReturnValue().SetNull(); + return; + } + + ByteString bsFormat; + if (argc > 1) { +- std::unique_ptr formatValue = GetSimpleValue(pThis, args, 1); +- if (ValueIsNull(pThis, formatValue.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local formatValue = GetSimpleValue(info, 1); ++ if (ValueIsNull(info.GetIsolate(), formatValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- bsFormat = ValueToUTF8String(formatValue.get()); ++ bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue); + } + + ByteString bsLocale; + if (argc > 2) { +- std::unique_ptr localeValue = GetSimpleValue(pThis, args, 2); +- if (ValueIsNull(pThis, localeValue.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local localeValue = GetSimpleValue(info, 2); ++ if (ValueIsNull(info.GetIsolate(), localeValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- bsLocale = ValueToUTF8String(localeValue.get()); ++ bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue); + } + + int32_t iYear = 1900; +@@ -2204,242 +2299,247 @@ void CFXJSE_FormCalcContext::Num2Date(CFXJSE_Value* pThis, + pThis, + ByteString::Format("%d%02d%02d", iYear + i, iMonth, iDay).AsStringView(), + bsFormat.AsStringView(), bsLocale.AsStringView()); +- args.GetReturnValue()->SetString(bsLocalDate.AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), bsLocalDate.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Num2GMTime(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::Num2GMTime( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc < 1 || argc > 3) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Num2GMTime"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Num2GMTime"); + return; + } + +- std::unique_ptr timeValue = GetSimpleValue(pThis, args, 0); +- if (timeValue->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local timeValue = GetSimpleValue(info, 0); ++ if (fxv8::IsNull(timeValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- int32_t iTime = (int32_t)ValueToFloat(pThis, timeValue.get()); ++ int32_t iTime = ++ static_cast(ValueToFloat(info.GetIsolate(), timeValue)); + if (abs(iTime) < 1.0) { +- args.GetReturnValue()->SetNull(); ++ info.GetReturnValue().SetNull(); + return; + } + + ByteString bsFormat; + if (argc > 1) { +- std::unique_ptr formatValue = GetSimpleValue(pThis, args, 1); +- if (formatValue->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local formatValue = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(formatValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- bsFormat = ValueToUTF8String(formatValue.get()); ++ bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue); + } + + ByteString bsLocale; + if (argc > 2) { +- std::unique_ptr localeValue = GetSimpleValue(pThis, args, 2); +- if (localeValue->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local localeValue = GetSimpleValue(info, 2); ++ if (fxv8::IsNull(localeValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- bsLocale = ValueToUTF8String(localeValue.get()); ++ bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue); + } + + ByteString bsGMTTime = Num2AllTime(pThis, iTime, bsFormat.AsStringView(), + bsLocale.AsStringView(), true); +- args.GetReturnValue()->SetString(bsGMTTime.AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), bsGMTTime.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Num2Time(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::Num2Time( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc < 1 || argc > 3) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Num2Time"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Num2Time"); + return; + } + +- std::unique_ptr timeValue = GetSimpleValue(pThis, args, 0); +- if (timeValue->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local timeValue = GetSimpleValue(info, 0); ++ if (fxv8::IsNull(timeValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- float fTime = ValueToFloat(pThis, timeValue.get()); ++ float fTime = ValueToFloat(info.GetIsolate(), timeValue); + if (fabs(fTime) < 1.0) { +- args.GetReturnValue()->SetNull(); ++ info.GetReturnValue().SetNull(); + return; + } + + ByteString bsFormat; + if (argc > 1) { +- std::unique_ptr formatValue = GetSimpleValue(pThis, args, 1); +- if (formatValue->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local formatValue = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(formatValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- bsFormat = ValueToUTF8String(formatValue.get()); ++ bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue); + } + + ByteString bsLocale; + if (argc > 2) { +- std::unique_ptr localeValue = GetSimpleValue(pThis, args, 2); +- if (localeValue->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local localeValue = GetSimpleValue(info, 2); ++ if (fxv8::IsNull(localeValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- bsLocale = ValueToUTF8String(localeValue.get()); ++ bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue); + } + + ByteString bsLocalTime = + Num2AllTime(pThis, static_cast(fTime), bsFormat.AsStringView(), + bsLocale.AsStringView(), false); +- args.GetReturnValue()->SetString(bsLocalTime.AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), bsLocalTime.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Time(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 0) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Time"); ++void CFXJSE_FormCalcContext::Time( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 0) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Time"); + return; + } + + time_t now; + FXSYS_time(&now); +- + struct tm* pGmt = gmtime(&now); +- args.GetReturnValue()->SetInteger( ++ info.GetReturnValue().Set( + (pGmt->tm_hour * 3600 + pGmt->tm_min * 60 + pGmt->tm_sec) * 1000); + } + + // static +-void CFXJSE_FormCalcContext::Time2Num(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::Time2Num( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc < 1 || argc > 3) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Time2Num"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Time2Num"); + return; + } + + ByteString bsTime; +- std::unique_ptr timeValue = GetSimpleValue(pThis, args, 0); +- if (ValueIsNull(pThis, timeValue.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local timeValue = GetSimpleValue(info, 0); ++ if (ValueIsNull(info.GetIsolate(), timeValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- bsTime = ValueToUTF8String(timeValue.get()); ++ bsTime = ValueToUTF8String(info.GetIsolate(), timeValue); + + ByteString bsFormat; + if (argc > 1) { +- std::unique_ptr formatValue = GetSimpleValue(pThis, args, 1); +- if (ValueIsNull(pThis, formatValue.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local formatValue = GetSimpleValue(info, 1); ++ if (ValueIsNull(info.GetIsolate(), formatValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- bsFormat = ValueToUTF8String(formatValue.get()); ++ bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue); + } + + ByteString bsLocale; + if (argc > 2) { +- std::unique_ptr localeValue = GetSimpleValue(pThis, args, 2); +- if (ValueIsNull(pThis, localeValue.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local localeValue = GetSimpleValue(info, 2); ++ if (ValueIsNull(info.GetIsolate(), localeValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- bsLocale = ValueToUTF8String(localeValue.get()); ++ bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue); + } + + CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument(); + CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr(); +- LocaleIface* pLocale = nullptr; +- if (bsLocale.IsEmpty()) { +- CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject()); +- pLocale = pThisNode->GetLocale(); +- } else { ++ GCedLocaleIface* pLocale = nullptr; ++ if (!bsLocale.IsEmpty()) { + pLocale = + pMgr->GetLocaleByName(WideString::FromUTF8(bsLocale.AsStringView())); + } ++ if (!pLocale) { ++ CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject()); ++ pLocale = pThisNode->GetLocale(); ++ } + + WideString wsFormat; +- if (bsFormat.IsEmpty()) +- wsFormat = pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Default); +- else ++ if (bsFormat.IsEmpty()) { ++ wsFormat = ++ pLocale->GetTimePattern(LocaleIface::DateTimeSubcategory::kDefault); ++ } else { + wsFormat = WideString::FromUTF8(bsFormat.AsStringView()); +- ++ } + wsFormat = L"time{" + wsFormat + L"}"; +- CXFA_LocaleValue localeValue(XFA_VT_TIME, ++ CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kTime, + WideString::FromUTF8(bsTime.AsStringView()), + wsFormat, pLocale, pMgr); + if (!localeValue.IsValid()) { +- args.GetReturnValue()->SetInteger(0); ++ info.GetReturnValue().Set(0); + return; + } + + CFX_DateTime uniTime = localeValue.GetTime(); + int32_t hour = uniTime.GetHour(); +- int32_t min = uniTime.GetMinute(); +- int32_t second = uniTime.GetSecond(); +- int32_t milSecond = uniTime.GetMillisecond(); +- int32_t mins = hour * 60 + min; ++ int32_t minute = uniTime.GetMinute(); ++ const int32_t second = uniTime.GetSecond(); ++ const int32_t millisecond = uniTime.GetMillisecond(); + +- mins -= (CXFA_TimeZoneProvider().GetTimeZone().tzHour * 60); +- while (mins > 1440) +- mins -= 1440; +- +- while (mins < 0) +- mins += 1440; ++ constexpr int kMinutesInDay = 24 * 60; ++ int32_t minutes_with_tz = ++ hour * 60 + minute - CXFA_TimeZoneProvider().GetTimeZoneInMinutes(); ++ minutes_with_tz %= kMinutesInDay; ++ if (minutes_with_tz < 0) ++ minutes_with_tz += kMinutesInDay; + +- hour = mins / 60; +- min = mins % 60; +- args.GetReturnValue()->SetInteger(hour * 3600000 + min * 60000 + +- second * 1000 + milSecond + 1); ++ hour = minutes_with_tz / 60; ++ minute = minutes_with_tz % 60; ++ info.GetReturnValue().Set(hour * 3600000 + minute * 60000 + second * 1000 + ++ millisecond + 1); + } + + // static +-void CFXJSE_FormCalcContext::TimeFmt(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::TimeFmt( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc > 2) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"TimeFmt"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("TimeFmt"); + return; + } + + int32_t iStyle = 0; + if (argc > 0) { +- std::unique_ptr argStyle = GetSimpleValue(pThis, args, 0); +- if (argStyle->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local infotyle = GetSimpleValue(info, 0); ++ if (fxv8::IsNull(infotyle)) { ++ info.GetReturnValue().SetNull(); + return; + } +- iStyle = (int32_t)ValueToFloat(pThis, argStyle.get()); ++ iStyle = static_cast(ValueToFloat(info.GetIsolate(), infotyle)); + if (iStyle > 4 || iStyle < 0) + iStyle = 0; + } + + ByteString bsLocale; + if (argc > 1) { +- std::unique_ptr argLocale = GetSimpleValue(pThis, args, 1); +- if (argLocale->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argLocale = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(argLocale)) { ++ info.GetReturnValue().SetNull(); + return; + } +- bsLocale = ValueToUTF8String(argLocale.get()); ++ bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale); + } + + ByteString bsFormat = + GetStandardTimeFormat(pThis, iStyle, bsLocale.AsStringView()); +- args.GetReturnValue()->SetString(bsFormat.AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView())); + } + + // static +-ByteString CFXJSE_FormCalcContext::Local2IsoDate(CFXJSE_Value* pThis, ++ByteString CFXJSE_FormCalcContext::Local2IsoDate(CFXJSE_HostObject* pThis, + ByteStringView bsDate, + ByteStringView bsFormat, + ByteStringView bsLocale) { +@@ -2448,21 +2548,22 @@ ByteString CFXJSE_FormCalcContext::Local2IsoDate(CFXJSE_Value* pThis, + return ByteString(); + + CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr(); +- LocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale); ++ GCedLocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale); + if (!pLocale) + return ByteString(); + + WideString wsFormat = FormatFromString(pLocale, bsFormat); +- CFX_DateTime dt = CXFA_LocaleValue(XFA_VT_DATE, WideString::FromUTF8(bsDate), +- wsFormat, pLocale, pMgr) +- .GetDate(); ++ CFX_DateTime dt = ++ CXFA_LocaleValue(CXFA_LocaleValue::ValueType::kDate, ++ WideString::FromUTF8(bsDate), wsFormat, pLocale, pMgr) ++ .GetDate(); + + return ByteString::Format("%4d-%02d-%02d", dt.GetYear(), dt.GetMonth(), + dt.GetDay()); + } + + // static +-ByteString CFXJSE_FormCalcContext::IsoDate2Local(CFXJSE_Value* pThis, ++ByteString CFXJSE_FormCalcContext::IsoDate2Local(CFXJSE_HostObject* pThis, + ByteStringView bsDate, + ByteStringView bsFormat, + ByteStringView bsLocale) { +@@ -2471,19 +2572,20 @@ ByteString CFXJSE_FormCalcContext::IsoDate2Local(CFXJSE_Value* pThis, + return ByteString(); + + CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr(); +- LocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale); ++ GCedLocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale); + if (!pLocale) + return ByteString(); + + WideString wsFormat = FormatFromString(pLocale, bsFormat); + WideString wsRet; +- CXFA_LocaleValue(XFA_VT_DATE, WideString::FromUTF8(bsDate), pMgr) +- .FormatPatterns(wsRet, wsFormat, pLocale, XFA_VALUEPICTURE_Display); ++ CXFA_LocaleValue(CXFA_LocaleValue::ValueType::kDate, ++ WideString::FromUTF8(bsDate), pMgr) ++ .FormatPatterns(wsRet, wsFormat, pLocale, XFA_ValuePicture::kDisplay); + return wsRet.ToUTF8(); + } + + // static +-ByteString CFXJSE_FormCalcContext::IsoTime2Local(CFXJSE_Value* pThis, ++ByteString CFXJSE_FormCalcContext::IsoTime2Local(CFXJSE_HostObject* pThis, + ByteStringView bsTime, + ByteStringView bsFormat, + ByteStringView bsLocale) { +@@ -2492,21 +2594,22 @@ ByteString CFXJSE_FormCalcContext::IsoTime2Local(CFXJSE_Value* pThis, + return ByteString(); + + CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr(); +- LocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale); ++ GCedLocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale); + if (!pLocale) + return ByteString(); + + WideString wsFormat = { + L"time{", FormatFromString(pLocale, bsFormat).AsStringView(), L"}"}; +- CXFA_LocaleValue widgetValue(XFA_VT_TIME, WideString::FromUTF8(bsTime), pMgr); ++ CXFA_LocaleValue widgetValue(CXFA_LocaleValue::ValueType::kTime, ++ WideString::FromUTF8(bsTime), pMgr); + WideString wsRet; + widgetValue.FormatPatterns(wsRet, wsFormat, pLocale, +- XFA_VALUEPICTURE_Display); ++ XFA_ValuePicture::kDisplay); + return wsRet.ToUTF8(); + } + + // static +-ByteString CFXJSE_FormCalcContext::GetLocalDateFormat(CFXJSE_Value* pThis, ++ByteString CFXJSE_FormCalcContext::GetLocalDateFormat(CFXJSE_HostObject* pThis, + int32_t iStyle, + ByteStringView bsLocale, + bool bStandard) { +@@ -2519,7 +2622,7 @@ ByteString CFXJSE_FormCalcContext::GetLocalDateFormat(CFXJSE_Value* pThis, + } + + // static +-ByteString CFXJSE_FormCalcContext::GetLocalTimeFormat(CFXJSE_Value* pThis, ++ByteString CFXJSE_FormCalcContext::GetLocalTimeFormat(CFXJSE_HostObject* pThis, + int32_t iStyle, + ByteStringView bsLocale, + bool bStandard) { +@@ -2533,7 +2636,7 @@ ByteString CFXJSE_FormCalcContext::GetLocalTimeFormat(CFXJSE_Value* pThis, + + // static + ByteString CFXJSE_FormCalcContext::GetStandardDateFormat( +- CFXJSE_Value* pThis, ++ CFXJSE_HostObject* pThis, + int32_t iStyle, + ByteStringView bsLocale) { + return GetLocalDateFormat(pThis, iStyle, bsLocale, true); +@@ -2541,14 +2644,14 @@ ByteString CFXJSE_FormCalcContext::GetStandardDateFormat( + + // static + ByteString CFXJSE_FormCalcContext::GetStandardTimeFormat( +- CFXJSE_Value* pThis, ++ CFXJSE_HostObject* pThis, + int32_t iStyle, + ByteStringView bsLocale) { + return GetLocalTimeFormat(pThis, iStyle, bsLocale, true); + } + + // static +-ByteString CFXJSE_FormCalcContext::Num2AllTime(CFXJSE_Value* pThis, ++ByteString CFXJSE_FormCalcContext::Num2AllTime(CFXJSE_HostObject* pThis, + int32_t iTime, + ByteStringView bsFormat, + ByteStringView bsLocale, +@@ -2577,28 +2680,29 @@ ByteString CFXJSE_FormCalcContext::Num2AllTime(CFXJSE_Value* pThis, + } + + // static +-void CFXJSE_FormCalcContext::Apr(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::Apr( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 3) { +- pContext->ThrowParamCountMismatchException(L"Apr"); ++ if (info.Length() != 3) { ++ pContext->ThrowParamCountMismatchException("Apr"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- std::unique_ptr argThree = GetSimpleValue(pThis, args, 2); +- if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) || +- ValueIsNull(pThis, argThree.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ v8::Local argThree = GetSimpleValue(info, 2); ++ if (ValueIsNull(info.GetIsolate(), argOne) || ++ ValueIsNull(info.GetIsolate(), argTwo) || ++ ValueIsNull(info.GetIsolate(), argThree)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- double nPrincipal = ValueToDouble(pThis, argOne.get()); +- double nPayment = ValueToDouble(pThis, argTwo.get()); +- double nPeriods = ValueToDouble(pThis, argThree.get()); +- if (nPrincipal <= 0 || nPayment <= 0 || nPeriods <= 0) { ++ double nPrincipal = ValueToDouble(info.GetIsolate(), argOne); ++ double nPayment = ValueToDouble(info.GetIsolate(), argTwo); ++ int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree); ++ if (nPrincipal <= 0 || nPayment <= 0 || nPeriods == 0) { + pContext->ThrowArgumentMismatchException(); + return; + } +@@ -2615,7 +2719,7 @@ void CFXJSE_FormCalcContext::Apr(CFXJSE_Value* pThis, + (r * nTemp * nPeriods * (nTemp / (1 + r)))) / + ((nTemp - 1) * (nTemp - 1)); + if (nDerivative == 0) { +- args.GetReturnValue()->SetNull(); ++ info.GetReturnValue().SetNull(); + return; + } + +@@ -2626,63 +2730,65 @@ void CFXJSE_FormCalcContext::Apr(CFXJSE_Value* pThis, + } + nRet = r * nTemp / (nTemp - 1) - nPayment / nPrincipal; + } +- args.GetReturnValue()->SetDouble(r * 12); ++ info.GetReturnValue().Set(r * 12); + } + + // static +-void CFXJSE_FormCalcContext::CTerm(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::CTerm( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 3) { +- pContext->ThrowParamCountMismatchException(L"CTerm"); ++ if (info.Length() != 3) { ++ pContext->ThrowParamCountMismatchException("CTerm"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- std::unique_ptr argThree = GetSimpleValue(pThis, args, 2); +- if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) || +- ValueIsNull(pThis, argThree.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ v8::Local argThree = GetSimpleValue(info, 2); ++ if (ValueIsNull(info.GetIsolate(), argOne) || ++ ValueIsNull(info.GetIsolate(), argTwo) || ++ ValueIsNull(info.GetIsolate(), argThree)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- float nRate = ValueToFloat(pThis, argOne.get()); +- float nFutureValue = ValueToFloat(pThis, argTwo.get()); +- float nInitAmount = ValueToFloat(pThis, argThree.get()); ++ float nRate = ValueToFloat(info.GetIsolate(), argOne); ++ float nFutureValue = ValueToFloat(info.GetIsolate(), argTwo); ++ float nInitAmount = ValueToFloat(info.GetIsolate(), argThree); + if ((nRate <= 0) || (nFutureValue <= 0) || (nInitAmount <= 0)) { + pContext->ThrowArgumentMismatchException(); + return; + } + +- args.GetReturnValue()->SetFloat(log((float)(nFutureValue / nInitAmount)) / +- log((float)(1 + nRate))); ++ info.GetReturnValue().Set(log((float)(nFutureValue / nInitAmount)) / ++ log((float)(1 + nRate))); + } + + // static +-void CFXJSE_FormCalcContext::FV(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::FV( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 3) { +- pContext->ThrowParamCountMismatchException(L"FV"); ++ if (info.Length() != 3) { ++ pContext->ThrowParamCountMismatchException("FV"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- std::unique_ptr argThree = GetSimpleValue(pThis, args, 2); +- if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) || +- ValueIsNull(pThis, argThree.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ v8::Local argThree = GetSimpleValue(info, 2); ++ if (ValueIsNull(info.GetIsolate(), argOne) || ++ ValueIsNull(info.GetIsolate(), argTwo) || ++ ValueIsNull(info.GetIsolate(), argThree)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- double nAmount = ValueToDouble(pThis, argOne.get()); +- double nRate = ValueToDouble(pThis, argTwo.get()); +- double nPeriod = ValueToDouble(pThis, argThree.get()); +- if ((nRate < 0) || (nPeriod <= 0) || (nAmount <= 0)) { ++ double nAmount = ValueToDouble(info.GetIsolate(), argOne); ++ double nRate = ValueToDouble(info.GetIsolate(), argTwo); ++ int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree); ++ if (nAmount <= 0 || nRate < 0 || nPeriods == 0) { + pContext->ThrowArgumentMismatchException(); + return; + } +@@ -2690,44 +2796,46 @@ void CFXJSE_FormCalcContext::FV(CFXJSE_Value* pThis, + double dResult = 0; + if (nRate) { + double nTemp = 1; +- for (int i = 0; i < nPeriod; ++i) { ++ for (int i = 0; i < nPeriods; ++i) { + nTemp *= 1 + nRate; + } + dResult = nAmount * (nTemp - 1) / nRate; + } else { +- dResult = nAmount * nPeriod; ++ dResult = nAmount * nPeriods; + } + +- args.GetReturnValue()->SetDouble(dResult); ++ info.GetReturnValue().Set(dResult); + } + + // static +-void CFXJSE_FormCalcContext::IPmt(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::IPmt( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 5) { +- pContext->ThrowParamCountMismatchException(L"IPmt"); ++ if (info.Length() != 5) { ++ pContext->ThrowParamCountMismatchException("IPmt"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- std::unique_ptr argThree = GetSimpleValue(pThis, args, 2); +- std::unique_ptr argFour = GetSimpleValue(pThis, args, 3); +- std::unique_ptr argFive = GetSimpleValue(pThis, args, 4); +- if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) || +- ValueIsNull(pThis, argThree.get()) || ValueIsNull(pThis, argFour.get()) || +- ValueIsNull(pThis, argFive.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ v8::Local argThree = GetSimpleValue(info, 2); ++ v8::Local argFour = GetSimpleValue(info, 3); ++ v8::Local argFive = GetSimpleValue(info, 4); ++ if (ValueIsNull(info.GetIsolate(), argOne) || ++ ValueIsNull(info.GetIsolate(), argTwo) || ++ ValueIsNull(info.GetIsolate(), argThree) || ++ ValueIsNull(info.GetIsolate(), argFour) || ++ ValueIsNull(info.GetIsolate(), argFive)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- float nPrincipalAmount = ValueToFloat(pThis, argOne.get()); +- float nRate = ValueToFloat(pThis, argTwo.get()); +- float nPayment = ValueToFloat(pThis, argThree.get()); +- float nFirstMonth = ValueToFloat(pThis, argFour.get()); +- float nNumberOfMonths = ValueToFloat(pThis, argFive.get()); ++ float nPrincipalAmount = ValueToFloat(info.GetIsolate(), argOne); ++ float nRate = ValueToFloat(info.GetIsolate(), argTwo); ++ float nPayment = ValueToFloat(info.GetIsolate(), argThree); ++ float nFirstMonth = ValueToFloat(info.GetIsolate(), argFour); ++ float nNumberOfMonths = ValueToFloat(info.GetIsolate(), argFive); + if ((nPrincipalAmount <= 0) || (nRate <= 0) || (nPayment <= 0) || + (nFirstMonth < 0) || (nNumberOfMonths < 0)) { + pContext->ThrowArgumentMismatchException(); +@@ -2735,14 +2843,15 @@ void CFXJSE_FormCalcContext::IPmt(CFXJSE_Value* pThis, + } + + float nRateOfMonth = nRate / 12; +- int32_t iNums = +- (int32_t)((log10((float)(nPayment / nPrincipalAmount)) - +- log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) / +- log10((float)(1 + nRateOfMonth))); +- int32_t iEnd = std::min((int32_t)(nFirstMonth + nNumberOfMonths - 1), iNums); ++ int32_t iNums = static_cast( ++ (log10((float)(nPayment / nPrincipalAmount)) - ++ log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) / ++ log10((float)(1 + nRateOfMonth))); ++ int32_t iEnd = ++ std::min(static_cast(nFirstMonth + nNumberOfMonths - 1), iNums); + + if (nPayment < nPrincipalAmount * nRateOfMonth) { +- args.GetReturnValue()->SetFloat(0); ++ info.GetReturnValue().Set(0); + return; + } + +@@ -2755,114 +2864,113 @@ void CFXJSE_FormCalcContext::IPmt(CFXJSE_Value* pThis, + nSum += nPrincipalAmount * nRateOfMonth; + nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth; + } +- args.GetReturnValue()->SetFloat(nSum); ++ info.GetReturnValue().Set(nSum); + } + + // static +-void CFXJSE_FormCalcContext::NPV(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::NPV( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- int32_t argc = args.GetLength(); +- if (argc < 3) { +- pContext->ThrowParamCountMismatchException(L"NPV"); ++ int32_t argc = info.Length(); ++ if (argc < 2) { ++ pContext->ThrowParamCountMismatchException("NPV"); + return; + } + +- std::vector> argValues; +- for (int32_t i = 0; i < argc; i++) { +- argValues.push_back(GetSimpleValue(pThis, args, i)); +- if (ValueIsNull(pThis, argValues[i].get())) { +- args.GetReturnValue()->SetNull(); +- return; +- } ++ v8::Local argValue = GetSimpleValue(info, 0); ++ if (ValueIsNull(info.GetIsolate(), argValue)) { ++ info.GetReturnValue().SetNull(); ++ return; + } + +- double nRate = ValueToDouble(pThis, argValues[0].get()); ++ double nRate = ValueToDouble(info.GetIsolate(), argValue); + if (nRate <= 0) { + pContext->ThrowArgumentMismatchException(); + return; + } + +- std::vector data(argc - 1); +- for (int32_t i = 1; i < argc; i++) +- data.push_back(ValueToDouble(pThis, argValues[i].get())); ++ std::vector data; ++ for (int32_t i = 1; i < argc; i++) { ++ argValue = GetSimpleValue(info, i); ++ if (ValueIsNull(info.GetIsolate(), argValue)) { ++ info.GetReturnValue().SetNull(); ++ return; ++ } ++ data.push_back(ValueToDouble(info.GetIsolate(), argValue)); ++ } + + double nSum = 0; +- int32_t iIndex = 0; +- for (int32_t i = 0; i < argc - 1; i++) { +- double nTemp = 1; +- for (int32_t j = 0; j <= i; j++) +- nTemp *= 1 + nRate; +- +- double nNum = data[iIndex++]; +- nSum += nNum / nTemp; ++ double nDivisor = 1.0 + nRate; ++ while (!data.empty()) { ++ nSum += data.back(); ++ nSum /= nDivisor; ++ data.pop_back(); + } +- args.GetReturnValue()->SetDouble(nSum); ++ info.GetReturnValue().Set(nSum); + } + + // static +-void CFXJSE_FormCalcContext::Pmt(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::Pmt( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 3) { +- pContext->ThrowParamCountMismatchException(L"Pmt"); ++ if (info.Length() != 3) { ++ pContext->ThrowParamCountMismatchException("Pmt"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- std::unique_ptr argThree = GetSimpleValue(pThis, args, 2); +- if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) || +- ValueIsNull(pThis, argThree.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ v8::Local argThree = GetSimpleValue(info, 2); ++ if (ValueIsNull(info.GetIsolate(), argOne) || ++ ValueIsNull(info.GetIsolate(), argTwo) || ++ ValueIsNull(info.GetIsolate(), argThree)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- float nPrincipal = ValueToFloat(pThis, argOne.get()); +- float nRate = ValueToFloat(pThis, argTwo.get()); +- float nPeriods = ValueToFloat(pThis, argThree.get()); +- if ((nPrincipal <= 0) || (nRate <= 0) || (nPeriods <= 0)) { ++ double nPrincipal = ValueToDouble(info.GetIsolate(), argOne); ++ double nRate = ValueToDouble(info.GetIsolate(), argTwo); ++ int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree); ++ if (nPrincipal <= 0 || nRate <= 0 || nPeriods == 0) { + pContext->ThrowArgumentMismatchException(); + return; + } + +- float nTmp = 1 + nRate; +- float nSum = nTmp; +- for (int32_t i = 0; i < nPeriods - 1; ++i) +- nSum *= nTmp; +- +- args.GetReturnValue()->SetFloat((nPrincipal * nRate * nSum) / (nSum - 1)); ++ double nSum = pow(1.0 + nRate, nPeriods); ++ info.GetReturnValue().Set((nPrincipal * nRate * nSum) / (nSum - 1)); + } + + // static +-void CFXJSE_FormCalcContext::PPmt(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::PPmt( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 5) { +- pContext->ThrowParamCountMismatchException(L"PPmt"); ++ if (info.Length() != 5) { ++ pContext->ThrowParamCountMismatchException("PPmt"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- std::unique_ptr argThree = GetSimpleValue(pThis, args, 2); +- std::unique_ptr argFour = GetSimpleValue(pThis, args, 3); +- std::unique_ptr argFive = GetSimpleValue(pThis, args, 4); +- if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) || +- ValueIsNull(pThis, argThree.get()) || ValueIsNull(pThis, argFour.get()) || +- ValueIsNull(pThis, argFive.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ v8::Local argThree = GetSimpleValue(info, 2); ++ v8::Local argFour = GetSimpleValue(info, 3); ++ v8::Local argFive = GetSimpleValue(info, 4); ++ if (ValueIsNull(info.GetIsolate(), argOne) || ++ ValueIsNull(info.GetIsolate(), argTwo) || ++ ValueIsNull(info.GetIsolate(), argThree) || ++ ValueIsNull(info.GetIsolate(), argFour) || ++ ValueIsNull(info.GetIsolate(), argFive)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- float nPrincipalAmount = ValueToFloat(pThis, argOne.get()); +- float nRate = ValueToFloat(pThis, argTwo.get()); +- float nPayment = ValueToFloat(pThis, argThree.get()); +- float nFirstMonth = ValueToFloat(pThis, argFour.get()); +- float nNumberOfMonths = ValueToFloat(pThis, argFive.get()); ++ float nPrincipalAmount = ValueToFloat(info.GetIsolate(), argOne); ++ float nRate = ValueToFloat(info.GetIsolate(), argTwo); ++ float nPayment = ValueToFloat(info.GetIsolate(), argThree); ++ float nFirstMonth = ValueToFloat(info.GetIsolate(), argFour); ++ float nNumberOfMonths = ValueToFloat(info.GetIsolate(), argFive); + if ((nPrincipalAmount <= 0) || (nRate <= 0) || (nPayment <= 0) || + (nFirstMonth < 0) || (nNumberOfMonths < 0)) { + pContext->ThrowArgumentMismatchException(); +@@ -2870,11 +2978,12 @@ void CFXJSE_FormCalcContext::PPmt(CFXJSE_Value* pThis, + } + + float nRateOfMonth = nRate / 12; +- int32_t iNums = +- (int32_t)((log10((float)(nPayment / nPrincipalAmount)) - +- log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) / +- log10((float)(1 + nRateOfMonth))); +- int32_t iEnd = std::min((int32_t)(nFirstMonth + nNumberOfMonths - 1), iNums); ++ int32_t iNums = static_cast( ++ (log10((float)(nPayment / nPrincipalAmount)) - ++ log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) / ++ log10((float)(1 + nRateOfMonth))); ++ int32_t iEnd = ++ std::min(static_cast(nFirstMonth + nNumberOfMonths - 1), iNums); + if (nPayment < nPrincipalAmount * nRateOfMonth) { + pContext->ThrowArgumentMismatchException(); + return; +@@ -2891,126 +3000,124 @@ void CFXJSE_FormCalcContext::PPmt(CFXJSE_Value* pThis, + nSum += nTemp; + nPrincipalAmount -= nTemp; + } +- args.GetReturnValue()->SetFloat(nSum); ++ info.GetReturnValue().Set(nSum); + } + + // static +-void CFXJSE_FormCalcContext::PV(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::PV( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 3) { +- pContext->ThrowParamCountMismatchException(L"PV"); ++ if (info.Length() != 3) { ++ pContext->ThrowParamCountMismatchException("PV"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- std::unique_ptr argThree = GetSimpleValue(pThis, args, 2); +- if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) || +- ValueIsNull(pThis, argThree.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ v8::Local argThree = GetSimpleValue(info, 2); ++ if (ValueIsNull(info.GetIsolate(), argOne) || ++ ValueIsNull(info.GetIsolate(), argTwo) || ++ ValueIsNull(info.GetIsolate(), argThree)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- double nAmount = ValueToDouble(pThis, argOne.get()); +- double nRate = ValueToDouble(pThis, argTwo.get()); +- double nPeriod = ValueToDouble(pThis, argThree.get()); +- if ((nAmount <= 0) || (nRate < 0) || (nPeriod <= 0)) { ++ double nAmount = ValueToDouble(info.GetIsolate(), argOne); ++ double nRate = ValueToDouble(info.GetIsolate(), argTwo); ++ int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree); ++ if (nAmount <= 0 || nRate < 0 || nPeriods == 0) { + pContext->ThrowArgumentMismatchException(); + return; + } + +- double nTemp = 1; +- for (int32_t i = 0; i < nPeriod; ++i) +- nTemp *= 1 + nRate; +- +- nTemp = 1 / nTemp; +- args.GetReturnValue()->SetDouble(nAmount * ((1 - nTemp) / nRate)); ++ double nTemp = 1 / pow(1.0 + nRate, nPeriods); ++ info.GetReturnValue().Set(nAmount * ((1.0 - nTemp) / nRate)); + } + + // static +-void CFXJSE_FormCalcContext::Rate(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::Rate( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 3) { +- pContext->ThrowParamCountMismatchException(L"Rate"); ++ if (info.Length() != 3) { ++ pContext->ThrowParamCountMismatchException("Rate"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- std::unique_ptr argThree = GetSimpleValue(pThis, args, 2); +- if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) || +- ValueIsNull(pThis, argThree.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ v8::Local argThree = GetSimpleValue(info, 2); ++ if (ValueIsNull(info.GetIsolate(), argOne) || ++ ValueIsNull(info.GetIsolate(), argTwo) || ++ ValueIsNull(info.GetIsolate(), argThree)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- float nFuture = ValueToFloat(pThis, argOne.get()); +- float nPresent = ValueToFloat(pThis, argTwo.get()); +- float nTotalNumber = ValueToFloat(pThis, argThree.get()); +- if ((nFuture <= 0) || (nPresent < 0) || (nTotalNumber <= 0)) { ++ float nFuture = ValueToFloat(info.GetIsolate(), argOne); ++ float nPresent = ValueToFloat(info.GetIsolate(), argTwo); ++ int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree); ++ if (nFuture <= 0 || nPresent < 0 || nPeriods == 0) { + pContext->ThrowArgumentMismatchException(); + return; + } + +- args.GetReturnValue()->SetFloat( +- FXSYS_pow((float)(nFuture / nPresent), (float)(1 / nTotalNumber)) - 1); ++ info.GetReturnValue().Set(powf(nFuture / nPresent, 1.0f / nPeriods) - 1.0f); + } + + // static +-void CFXJSE_FormCalcContext::Term(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::Term( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 3) { +- pContext->ThrowParamCountMismatchException(L"Term"); ++ if (info.Length() != 3) { ++ pContext->ThrowParamCountMismatchException("Term"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- std::unique_ptr argThree = GetSimpleValue(pThis, args, 2); +- if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) || +- ValueIsNull(pThis, argThree.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ v8::Local argThree = GetSimpleValue(info, 2); ++ if (ValueIsNull(info.GetIsolate(), argOne) || ++ ValueIsNull(info.GetIsolate(), argTwo) || ++ ValueIsNull(info.GetIsolate(), argThree)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- float nMount = ValueToFloat(pThis, argOne.get()); +- float nRate = ValueToFloat(pThis, argTwo.get()); +- float nFuture = ValueToFloat(pThis, argThree.get()); ++ float nMount = ValueToFloat(info.GetIsolate(), argOne); ++ float nRate = ValueToFloat(info.GetIsolate(), argTwo); ++ float nFuture = ValueToFloat(info.GetIsolate(), argThree); + if ((nMount <= 0) || (nRate <= 0) || (nFuture <= 0)) { + pContext->ThrowArgumentMismatchException(); + return; + } + +- args.GetReturnValue()->SetFloat(log((float)(nFuture / nMount * nRate) + 1) / +- log((float)(1 + nRate))); ++ info.GetReturnValue().Set(log((float)(nFuture / nMount * nRate) + 1) / ++ log((float)(1 + nRate))); + } + + // static +-void CFXJSE_FormCalcContext::Choose(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::Choose( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- int32_t argc = args.GetLength(); ++ int32_t argc = info.Length(); + if (argc < 2) { +- pContext->ThrowParamCountMismatchException(L"Choose"); ++ pContext->ThrowParamCountMismatchException("Choose"); + return; + } + +- std::unique_ptr argOne = args.GetValue(0); +- if (ValueIsNull(pThis, argOne.get())) { +- args.GetReturnValue()->SetNull(); ++ if (ValueIsNull(info.GetIsolate(), info[0])) { ++ info.GetReturnValue().SetNull(); + return; + } + +- int32_t iIndex = (int32_t)ValueToFloat(pThis, argOne.get()); ++ int32_t iIndex = ++ static_cast(ValueToFloat(info.GetIsolate(), info[0])); + if (iIndex < 1) { +- args.GetReturnValue()->SetString(""); ++ info.GetReturnValue().SetEmptyString(); + return; + } + +@@ -3018,268 +3125,261 @@ void CFXJSE_FormCalcContext::Choose(CFXJSE_Value* pThis, + bool bStopCounterFlags = false; + int32_t iArgIndex = 1; + int32_t iValueIndex = 0; +- v8::Isolate* pIsolate = pContext->GetScriptRuntime(); + while (!bFound && !bStopCounterFlags && (iArgIndex < argc)) { +- std::unique_ptr argIndexValue = args.GetValue(iArgIndex); +- if (argIndexValue->IsArray()) { +- auto lengthValue = pdfium::MakeUnique(pIsolate); +- argIndexValue->GetObjectProperty("length", lengthValue.get()); +- int32_t iLength = lengthValue->ToInteger(); ++ v8::Local argIndexValue = info[iArgIndex]; ++ if (fxv8::IsArray(argIndexValue)) { ++ v8::Local arr = argIndexValue.As(); ++ uint32_t iLength = fxv8::GetArrayLengthHelper(arr); + if (iLength > 3) + bStopCounterFlags = true; + + iValueIndex += (iLength - 2); + if (iValueIndex >= iIndex) { +- auto propertyValue = pdfium::MakeUnique(pIsolate); +- auto jsObjectValue = pdfium::MakeUnique(pIsolate); +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- argIndexValue->GetObjectPropertyByIdx(1, propertyValue.get()); +- argIndexValue->GetObjectPropertyByIdx( +- (iLength - 1) - (iValueIndex - iIndex), jsObjectValue.get()); +- if (propertyValue->IsNull()) { +- GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get()); +- } else { +- jsObjectValue->GetObjectProperty( +- propertyValue->ToString().AsStringView(), newPropertyValue.get()); ++ v8::Local propertyValue = ++ fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 1); ++ v8::Local jsValue = fxv8::ReentrantGetArrayElementHelper( ++ info.GetIsolate(), arr, (iLength - 1) - (iValueIndex - iIndex)); ++ v8::Local newPropertyValue; ++ if (fxv8::IsObject(jsValue)) { ++ v8::Local jsObjectValue = jsValue.As(); ++ if (fxv8::IsNull(propertyValue)) { ++ newPropertyValue = ++ GetObjectDefaultValue(info.GetIsolate(), jsObjectValue); ++ } else { ++ ByteString bsName = fxv8::ReentrantToByteStringHelper( ++ info.GetIsolate(), propertyValue); ++ newPropertyValue = fxv8::ReentrantGetObjectPropertyHelper( ++ info.GetIsolate(), jsObjectValue, bsName.AsStringView()); ++ } + } +- ByteString bsChosen = ValueToUTF8String(newPropertyValue.get()); +- args.GetReturnValue()->SetString(bsChosen.AsStringView()); ++ ByteString bsChosen = ++ ValueToUTF8String(info.GetIsolate(), newPropertyValue); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), bsChosen.AsStringView())); + bFound = true; + } + } else { + iValueIndex++; + if (iValueIndex == iIndex) { +- ByteString bsChosen = ValueToUTF8String(argIndexValue.get()); +- args.GetReturnValue()->SetString(bsChosen.AsStringView()); ++ ByteString bsChosen = ++ ValueToUTF8String(info.GetIsolate(), argIndexValue); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), bsChosen.AsStringView())); + bFound = true; + } + } + iArgIndex++; + } + if (!bFound) +- args.GetReturnValue()->SetString(""); ++ info.GetReturnValue().SetEmptyString(); + } + + // static +-void CFXJSE_FormCalcContext::Exists(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Exists"); ++void CFXJSE_FormCalcContext::Exists( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 1) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Exists"); + return; + } +- args.GetReturnValue()->SetInteger(args.GetValue(0)->IsObject()); ++ info.GetReturnValue().Set(fxv8::IsObject(info[0])); + } + + // static +-void CFXJSE_FormCalcContext::HasValue(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"HasValue"); ++void CFXJSE_FormCalcContext::HasValue( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 1) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("HasValue"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- if (!argOne->IsString()) { +- args.GetReturnValue()->SetInteger(argOne->IsNumber() || +- argOne->IsBoolean()); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ if (!fxv8::IsString(argOne)) { ++ info.GetReturnValue().Set( ++ static_cast(fxv8::IsNumber(argOne) || fxv8::IsBoolean(argOne))); + return; + } + +- ByteString bsValue = argOne->ToString(); ++ ByteString bsValue = ++ fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argOne); + bsValue.TrimLeft(); +- args.GetReturnValue()->SetInteger(!bsValue.IsEmpty()); ++ info.GetReturnValue().Set(static_cast(!bsValue.IsEmpty())); + } + + // static +-void CFXJSE_FormCalcContext::Oneof(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() < 2) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Oneof"); ++void CFXJSE_FormCalcContext::Oneof( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() < 2) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Oneof"); + return; + } + +- bool bFlags = false; +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::vector> parameterValues = +- unfoldArgs(pThis, args); +- for (const auto& value : parameterValues) { +- if (simpleValueCompare(pThis, argOne.get(), value.get())) { +- bFlags = true; +- break; ++ v8::Local argOne = GetSimpleValue(info, 0); ++ for (const auto& value : UnfoldArgs(info)) { ++ if (SimpleValueCompare(info.GetIsolate(), argOne, value)) { ++ info.GetReturnValue().Set(1); ++ return; + } + } +- +- args.GetReturnValue()->SetInteger(bFlags); ++ info.GetReturnValue().Set(0); + } + + // static +-void CFXJSE_FormCalcContext::Within(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 3) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Within"); ++void CFXJSE_FormCalcContext::Within( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 3) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Within"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- if (argOne->IsNull()) { +- args.GetReturnValue()->SetUndefined(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ if (fxv8::IsNull(argOne)) { ++ info.GetReturnValue().SetUndefined(); + return; + } + +- std::unique_ptr argLow = GetSimpleValue(pThis, args, 1); +- std::unique_ptr argHigh = GetSimpleValue(pThis, args, 2); +- if (argOne->IsNumber()) { +- float oneNumber = ValueToFloat(pThis, argOne.get()); +- float lowNumber = ValueToFloat(pThis, argLow.get()); +- float heightNumber = ValueToFloat(pThis, argHigh.get()); +- args.GetReturnValue()->SetInteger((oneNumber >= lowNumber) && +- (oneNumber <= heightNumber)); ++ v8::Local argLow = GetSimpleValue(info, 1); ++ v8::Local argHigh = GetSimpleValue(info, 2); ++ if (fxv8::IsNumber(argOne)) { ++ float oneNumber = ValueToFloat(info.GetIsolate(), argOne); ++ float lowNumber = ValueToFloat(info.GetIsolate(), argLow); ++ float heightNumber = ValueToFloat(info.GetIsolate(), argHigh); ++ info.GetReturnValue().Set(static_cast((oneNumber >= lowNumber) && ++ (oneNumber <= heightNumber))); + return; + } + +- ByteString bsOne = ValueToUTF8String(argOne.get()); +- ByteString bsLow = ValueToUTF8String(argLow.get()); +- ByteString bsHeight = ValueToUTF8String(argHigh.get()); +- args.GetReturnValue()->SetInteger( +- (bsOne.Compare(bsLow.AsStringView()) >= 0) && +- (bsOne.Compare(bsHeight.AsStringView()) <= 0)); ++ ByteString bsOne = ValueToUTF8String(info.GetIsolate(), argOne); ++ ByteString bsLow = ValueToUTF8String(info.GetIsolate(), argLow); ++ ByteString bsHeight = ValueToUTF8String(info.GetIsolate(), argHigh); ++ info.GetReturnValue().Set( ++ static_cast((bsOne.Compare(bsLow.AsStringView()) >= 0) && ++ (bsOne.Compare(bsHeight.AsStringView()) <= 0))); + } + + // static +-void CFXJSE_FormCalcContext::If(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 3) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"If"); ++void CFXJSE_FormCalcContext::If( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 3) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("If"); + return; + } + +- args.GetReturnValue()->Assign(GetSimpleValue(pThis, args, 0)->ToBoolean() +- ? GetSimpleValue(pThis, args, 1).get() +- : GetSimpleValue(pThis, args, 2).get()); ++ const bool condition = fxv8::ReentrantToBooleanHelper( ++ info.GetIsolate(), GetSimpleValue(info, 0)); ++ ++ info.GetReturnValue().Set(GetSimpleValue(info, condition ? 1 : 2)); + } + + // static +-void CFXJSE_FormCalcContext::Eval(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::Eval( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 1) { +- pContext->ThrowParamCountMismatchException(L"Eval"); ++ if (info.Length() != 1) { ++ pContext->ThrowParamCountMismatchException("Eval"); + return; + } + +- v8::Isolate* pIsolate = pContext->GetScriptRuntime(); +- std::unique_ptr scriptValue = GetSimpleValue(pThis, args, 0); +- ByteString bsUtf8Script = ValueToUTF8String(scriptValue.get()); ++ v8::Isolate* pIsolate = pContext->GetIsolate(); ++ v8::Local scriptValue = GetSimpleValue(info, 0); ++ ByteString bsUtf8Script = ValueToUTF8String(info.GetIsolate(), scriptValue); + if (bsUtf8Script.IsEmpty()) { +- args.GetReturnValue()->SetNull(); ++ info.GetReturnValue().SetNull(); + return; + } + +- CFX_WideTextBuf wsJavaScriptBuf; +- if (!CFXJSE_FormCalcContext::Translate( +- WideString::FromUTF8(bsUtf8Script.AsStringView()).AsStringView(), +- &wsJavaScriptBuf)) { ++ WideString wsCalcScript = WideString::FromUTF8(bsUtf8Script.AsStringView()); ++ absl::optional wsJavaScriptBuf = ++ CFXJSE_FormCalcContext::Translate(pContext->GetDocument()->GetHeap(), ++ wsCalcScript.AsStringView()); ++ if (!wsJavaScriptBuf.has_value()) { + pContext->ThrowCompilerErrorException(); + return; + } ++ std::unique_ptr pNewContext = ++ CFXJSE_Context::Create(pIsolate, nullptr, nullptr, nullptr); + +- std::unique_ptr pNewContext( +- CFXJSE_Context::Create(pIsolate, nullptr, nullptr)); +- +- auto returnValue = pdfium::MakeUnique(pIsolate); +- pNewContext->ExecuteScript( +- FX_UTF8Encode(wsJavaScriptBuf.AsStringView()).c_str(), returnValue.get(), +- nullptr); ++ auto returnValue = std::make_unique(); ++ ByteString bsScript = FX_UTF8Encode(wsJavaScriptBuf.value().AsStringView()); ++ pNewContext->ExecuteScript(bsScript.AsStringView(), returnValue.get(), ++ v8::Local()); + +- args.GetReturnValue()->Assign(returnValue.get()); ++ info.GetReturnValue().Set(returnValue->DirectGetValue()); + } + + // static +-void CFXJSE_FormCalcContext::Ref(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::Ref( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- v8::Isolate* pIsolate = pContext->GetScriptRuntime(); +- if (args.GetLength() != 1) { +- pContext->ThrowParamCountMismatchException(L"Ref"); ++ if (info.Length() != 1) { ++ pContext->ThrowParamCountMismatchException("Ref"); + return; + } + +- std::unique_ptr argOne = args.GetValue(0); +- if (!argOne->IsArray() && !argOne->IsObject() && !argOne->IsBoolean() && +- !argOne->IsString() && !argOne->IsNull() && !argOne->IsNumber()) { +- pContext->ThrowArgumentMismatchException(); ++ v8::Local argOne = info[0]; ++ if (fxv8::IsBoolean(argOne) || fxv8::IsString(argOne) || ++ fxv8::IsNumber(argOne)) { ++ info.GetReturnValue().Set(argOne); + return; + } + +- if (argOne->IsBoolean() || argOne->IsString() || argOne->IsNumber()) { +- args.GetReturnValue()->Assign(argOne.get()); +- return; +- } +- +- std::vector> values; +- for (int32_t i = 0; i < 3; i++) +- values.push_back(pdfium::MakeUnique(pIsolate)); +- ++ std::vector> values(3); + int intVal = 3; +- if (argOne->IsNull()) { ++ if (fxv8::IsNull(argOne)) { + // TODO(dsinclair): Why is this 4 when the others are all 3? + intVal = 4; +- values[2]->SetNull(); +- } else if (argOne->IsArray()) { +-#ifndef NDEBUG +- auto lengthValue = pdfium::MakeUnique(pIsolate); +- argOne->GetObjectProperty("length", lengthValue.get()); +- ASSERT(lengthValue->ToInteger() >= 3); +-#endif +- +- auto propertyValue = pdfium::MakeUnique(pIsolate); +- auto jsObjectValue = pdfium::MakeUnique(pIsolate); +- argOne->GetObjectPropertyByIdx(1, propertyValue.get()); +- argOne->GetObjectPropertyByIdx(2, jsObjectValue.get()); +- if (!propertyValue->IsNull() || jsObjectValue->IsNull()) { ++ values[2] = fxv8::NewNullHelper(info.GetIsolate()); ++ } else if (fxv8::IsArray(argOne)) { ++ v8::Local arr = argOne.As(); ++ v8::Local propertyValue = ++ fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 1); ++ v8::Local jsObjectValue = ++ fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2); ++ if (!fxv8::IsNull(propertyValue) || fxv8::IsNull(jsObjectValue)) { + pContext->ThrowArgumentMismatchException(); + return; + } +- +- values[2]->Assign(jsObjectValue.get()); +- } else if (argOne->IsObject()) { +- values[2]->Assign(argOne.get()); ++ values[2] = jsObjectValue; ++ } else if (fxv8::IsObject(argOne)) { ++ values[2] = argOne; ++ } else { ++ pContext->ThrowArgumentMismatchException(); ++ return; + } + +- values[0]->SetInteger(intVal); +- values[1]->SetNull(); +- args.GetReturnValue()->SetArray(values); ++ values[0] = fxv8::NewNumberHelper(info.GetIsolate(), intVal); ++ values[1] = fxv8::NewNullHelper(info.GetIsolate()); ++ info.GetReturnValue().Set(fxv8::NewArrayHelper(info.GetIsolate(), values)); + } + + // static +-void CFXJSE_FormCalcContext::UnitType(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"UnitType"); ++void CFXJSE_FormCalcContext::UnitType( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 1) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("UnitType"); + return; + } + +- std::unique_ptr unitspanValue = GetSimpleValue(pThis, args, 0); +- if (unitspanValue->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local unitspanValue = GetSimpleValue(info, 0); ++ if (fxv8::IsNull(unitspanValue)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- ByteString bsUnitspan = ValueToUTF8String(unitspanValue.get()); ++ ByteString bsUnitspan = ValueToUTF8String(info.GetIsolate(), unitspanValue); + if (bsUnitspan.IsEmpty()) { +- args.GetReturnValue()->SetString("in"); ++ info.GetReturnValue().SetEmptyString(); + return; + } + +- enum XFA_FM2JS_VALUETYPE_ParserStatus { ++ enum XFA_FormCalc_VALUETYPE_ParserStatus { + VALUETYPE_START, + VALUETYPE_HAVEINVALIDCHAR, + VALUETYPE_HAVEDIGIT, +@@ -3298,7 +3398,7 @@ void CFXJSE_FormCalcContext::UnitType(CFXJSE_Value* pThis, + while (IsWhitespace(pData[u])) + u++; + +- XFA_FM2JS_VALUETYPE_ParserStatus eParserStatus = VALUETYPE_START; ++ XFA_FormCalc_VALUETYPE_ParserStatus eParserStatus = VALUETYPE_START; + wchar_t typeChar; + // TODO(dsinclair): Cleanup this parser, figure out what the various checks + // are for. +@@ -3348,43 +3448,43 @@ void CFXJSE_FormCalcContext::UnitType(CFXJSE_Value* pThis, + } + switch (eParserStatus) { + case VALUETYPE_ISCM: +- args.GetReturnValue()->SetString("cm"); ++ info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "cm")); + break; + case VALUETYPE_ISMM: +- args.GetReturnValue()->SetString("mm"); ++ info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "mm")); + break; + case VALUETYPE_ISPT: +- args.GetReturnValue()->SetString("pt"); ++ info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "pt")); + break; + case VALUETYPE_ISMP: +- args.GetReturnValue()->SetString("mp"); ++ info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "mp")); + break; + default: +- args.GetReturnValue()->SetString("in"); ++ info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "in")); + break; + } + } + + // static +-void CFXJSE_FormCalcContext::UnitValue(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::UnitValue( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc < 1 || argc > 2) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"UnitValue"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("UnitValue"); + return; + } + +- std::unique_ptr unitspanValue = GetSimpleValue(pThis, args, 0); +- if (unitspanValue->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local unitspanValue = GetSimpleValue(info, 0); ++ if (fxv8::IsNull(unitspanValue)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- ByteString bsUnitspan = ValueToUTF8String(unitspanValue.get()); ++ ByteString bsUnitspan = ValueToUTF8String(info.GetIsolate(), unitspanValue); + const char* pData = bsUnitspan.c_str(); + if (!pData) { +- args.GetReturnValue()->SetInteger(0); ++ info.GetReturnValue().Set(0); + return; + } + +@@ -3416,15 +3516,15 @@ void CFXJSE_FormCalcContext::UnitValue(CFXJSE_Value* pThis, + + ByteString bsUnit; + if (argc > 1) { +- std::unique_ptr unitValue = GetSimpleValue(pThis, args, 1); +- ByteString bsUnitTemp = ValueToUTF8String(unitValue.get()); ++ v8::Local unitValue = GetSimpleValue(info, 1); ++ ByteString bsUnitTemp = ValueToUTF8String(info.GetIsolate(), unitValue); + const char* pChar = bsUnitTemp.c_str(); + size_t uVal = 0; + while (IsWhitespace(pChar[uVal])) + ++uVal; + + while (uVal < bsUnitTemp.GetLength()) { +- if (!std::isdigit(pChar[uVal]) && pChar[uVal] != '.') ++ if (!isdigit(pChar[uVal]) && pChar[uVal] != '.') + break; + ++uVal; + } +@@ -3501,99 +3601,102 @@ void CFXJSE_FormCalcContext::UnitValue(CFXJSE_Value* pThis, + else + dResult = dFirstNumber / 72000; + } +- args.GetReturnValue()->SetDouble(dResult); ++ info.GetReturnValue().Set(dResult); + } + + // static +-void CFXJSE_FormCalcContext::At(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 2) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"At"); ++void CFXJSE_FormCalcContext::At( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 2) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("At"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ if (ValueIsNull(info.GetIsolate(), argOne) || ++ ValueIsNull(info.GetIsolate(), argTwo)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- ByteString stringTwo = ValueToUTF8String(argTwo.get()); ++ ByteString stringTwo = ValueToUTF8String(info.GetIsolate(), argTwo); + if (stringTwo.IsEmpty()) { +- args.GetReturnValue()->SetInteger(1); ++ info.GetReturnValue().Set(1); + return; + } + +- ByteString stringOne = ValueToUTF8String(argOne.get()); ++ ByteString stringOne = ValueToUTF8String(info.GetIsolate(), argOne); + auto pos = stringOne.Find(stringTwo.AsStringView()); +- args.GetReturnValue()->SetInteger(pos.has_value() ? pos.value() + 1 : 0); ++ info.GetReturnValue().Set( ++ static_cast(pos.has_value() ? pos.value() + 1 : 0)); + } + + // static +-void CFXJSE_FormCalcContext::Concat(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::Concat( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc < 1) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Concat"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Concat"); + return; + } + + ByteString bsResult; + bool bAllNull = true; + for (int32_t i = 0; i < argc; i++) { +- std::unique_ptr value = GetSimpleValue(pThis, args, i); +- if (ValueIsNull(pThis, value.get())) ++ v8::Local value = GetSimpleValue(info, i); ++ if (ValueIsNull(info.GetIsolate(), value)) + continue; + + bAllNull = false; +- bsResult += ValueToUTF8String(value.get()); ++ bsResult += ValueToUTF8String(info.GetIsolate(), value); + } + + if (bAllNull) { +- args.GetReturnValue()->SetNull(); ++ info.GetReturnValue().SetNull(); + return; + } +- +- args.GetReturnValue()->SetString(bsResult.AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), bsResult.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Decode(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::Decode( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc < 1 || argc > 2) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Decode"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Decode"); + return; + } + + if (argc == 1) { +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- if (ValueIsNull(pThis, argOne.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ if (ValueIsNull(info.GetIsolate(), argOne)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- WideString decoded = DecodeURL( +- WideString::FromUTF8(ValueToUTF8String(argOne.get()).AsStringView())); +- +- args.GetReturnValue()->SetString( +- FX_UTF8Encode(decoded.AsStringView()).AsStringView()); ++ WideString decoded = DecodeURL(WideString::FromUTF8( ++ ValueToUTF8String(info.GetIsolate(), argOne).AsStringView())); ++ auto result = FX_UTF8Encode(decoded.AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ if (ValueIsNull(info.GetIsolate(), argOne) || ++ ValueIsNull(info.GetIsolate(), argTwo)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- ByteString bsToDecode = ValueToUTF8String(argOne.get()); +- ByteString bsIdentify = ValueToUTF8String(argTwo.get()); ++ ByteString bsToDecode = ValueToUTF8String(info.GetIsolate(), argOne); ++ ByteString bsIdentify = ValueToUTF8String(info.GetIsolate(), argTwo); + WideString decoded; + + WideString wsToDecode = WideString::FromUTF8(bsToDecode.AsStringView()); +@@ -3605,42 +3708,45 @@ void CFXJSE_FormCalcContext::Decode(CFXJSE_Value* pThis, + else + decoded = DecodeURL(wsToDecode); + +- args.GetReturnValue()->SetString( +- FX_UTF8Encode(decoded.AsStringView()).AsStringView()); ++ auto result = FX_UTF8Encode(decoded.AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Encode(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::Encode( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc < 1 || argc > 2) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Encode"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Encode"); + return; + } + + if (argc == 1) { +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- if (ValueIsNull(pThis, argOne.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ if (ValueIsNull(info.GetIsolate(), argOne)) { ++ info.GetReturnValue().SetNull(); + return; + } +- +- WideString encoded = EncodeURL(ValueToUTF8String(argOne.get())); +- args.GetReturnValue()->SetString( +- FX_UTF8Encode(encoded.AsStringView()).AsStringView()); ++ WideString encoded = ++ EncodeURL(ValueToUTF8String(info.GetIsolate(), argOne)); ++ auto result = FX_UTF8Encode(encoded.AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ if (ValueIsNull(info.GetIsolate(), argOne) || ++ ValueIsNull(info.GetIsolate(), argTwo)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- ByteString bsToEncode = ValueToUTF8String(argOne.get()); +- ByteString bsIdentify = ValueToUTF8String(argTwo.get()); ++ ByteString bsToEncode = ValueToUTF8String(info.GetIsolate(), argOne); ++ ByteString bsIdentify = ValueToUTF8String(info.GetIsolate(), argTwo); + WideString encoded; + if (bsIdentify.EqualNoCase("html")) + encoded = EncodeHTML(bsToEncode); +@@ -3649,42 +3755,43 @@ void CFXJSE_FormCalcContext::Encode(CFXJSE_Value* pThis, + else + encoded = EncodeURL(bsToEncode); + +- args.GetReturnValue()->SetString( +- FX_UTF8Encode(encoded.AsStringView()).AsStringView()); ++ auto result = FX_UTF8Encode(encoded.AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Format(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::Format( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() < 2) { +- pContext->ThrowParamCountMismatchException(L"Format"); ++ if (info.Length() < 2) { ++ pContext->ThrowParamCountMismatchException("Format"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- ByteString bsPattern = ValueToUTF8String(argOne.get()); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ ByteString bsPattern = ValueToUTF8String(info.GetIsolate(), argOne); + +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- ByteString bsValue = ValueToUTF8String(argTwo.get()); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ ByteString bsValue = ValueToUTF8String(info.GetIsolate(), argTwo); + + CXFA_Document* pDoc = pContext->GetDocument(); + CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr(); + CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject()); +- LocaleIface* pLocale = pThisNode->GetLocale(); ++ GCedLocaleIface* pLocale = pThisNode->GetLocale(); + WideString wsPattern = WideString::FromUTF8(bsPattern.AsStringView()); + WideString wsValue = WideString::FromUTF8(bsValue.AsStringView()); + bool bPatternIsString; +- uint32_t dwPatternType; ++ CXFA_LocaleValue::ValueType dwPatternType; + std::tie(bPatternIsString, dwPatternType) = + PatternStringType(bsPattern.AsStringView()); + if (!bPatternIsString) { + switch (dwPatternType) { +- case XFA_VT_DATETIME: { ++ case CXFA_LocaleValue::ValueType::kDateTime: { + auto iTChar = wsPattern.Find(L'T'); + if (!iTChar.has_value()) { +- args.GetReturnValue()->SetString(""); ++ info.GetReturnValue().SetEmptyString(); + return; + } + WideString wsDatePattern(L"date{"); +@@ -3695,28 +3802,28 @@ void CFXJSE_FormCalcContext::Format(CFXJSE_Value* pThis, + wsPattern.Last(wsPattern.GetLength() - (iTChar.value() + 1)) + L"}"; + wsPattern = wsDatePattern + wsTimePattern; + } break; +- case XFA_VT_DATE: { ++ case CXFA_LocaleValue::ValueType::kDate: { + wsPattern = L"date{" + wsPattern + L"}"; + } break; +- case XFA_VT_TIME: { ++ case CXFA_LocaleValue::ValueType::kTime: { + wsPattern = L"time{" + wsPattern + L"}"; + } break; +- case XFA_VT_TEXT: { ++ case CXFA_LocaleValue::ValueType::kText: { + wsPattern = L"text{" + wsPattern + L"}"; + } break; +- case XFA_VT_FLOAT: { ++ case CXFA_LocaleValue::ValueType::kFloat: { + wsPattern = L"num{" + wsPattern + L"}"; + } break; + default: { + WideString wsTestPattern = L"num{" + wsPattern + L"}"; +- CXFA_LocaleValue tempLocaleValue(XFA_VT_FLOAT, wsValue, wsTestPattern, +- pLocale, pMgr); ++ CXFA_LocaleValue tempLocaleValue(CXFA_LocaleValue::ValueType::kFloat, ++ wsValue, wsTestPattern, pLocale, pMgr); + if (tempLocaleValue.IsValid()) { + wsPattern = std::move(wsTestPattern); +- dwPatternType = XFA_VT_FLOAT; ++ dwPatternType = CXFA_LocaleValue::ValueType::kFloat; + } else { + wsPattern = L"text{" + wsPattern + L"}"; +- dwPatternType = XFA_VT_TEXT; ++ dwPatternType = CXFA_LocaleValue::ValueType::kText; + } + } break; + } +@@ -3725,73 +3832,74 @@ void CFXJSE_FormCalcContext::Format(CFXJSE_Value* pThis, + pMgr); + WideString wsRet; + if (!localeValue.FormatPatterns(wsRet, wsPattern, pLocale, +- XFA_VALUEPICTURE_Display)) { +- args.GetReturnValue()->SetString(""); ++ XFA_ValuePicture::kDisplay)) { ++ info.GetReturnValue().SetEmptyString(); + return; + } +- +- args.GetReturnValue()->SetString(wsRet.ToUTF8().AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), wsRet.ToUTF8().AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Left(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 2) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Left"); ++void CFXJSE_FormCalcContext::Left( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 2) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Left"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- if ((ValueIsNull(pThis, argOne.get())) || +- (ValueIsNull(pThis, argTwo.get()))) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ if ((ValueIsNull(info.GetIsolate(), argOne)) || ++ (ValueIsNull(info.GetIsolate(), argTwo))) { ++ info.GetReturnValue().SetNull(); + return; + } + +- ByteString bsSource = ValueToUTF8String(argOne.get()); +- int32_t count = std::max(0, ValueToInteger(pThis, argTwo.get())); +- args.GetReturnValue()->SetString(bsSource.First(count).AsStringView()); ++ ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne); ++ int32_t count = std::max(0, ValueToInteger(info.GetIsolate(), argTwo)); ++ info.GetReturnValue().Set(fxv8::NewStringHelper( ++ info.GetIsolate(), bsSource.First(count).AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Len(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Len"); ++void CFXJSE_FormCalcContext::Len( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 1) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Len"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- if (ValueIsNull(pThis, argOne.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ if (ValueIsNull(info.GetIsolate(), argOne)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- ByteString bsSource = ValueToUTF8String(argOne.get()); +- args.GetReturnValue()->SetInteger(bsSource.GetLength()); ++ ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne); ++ info.GetReturnValue().Set(static_cast(bsSource.GetLength())); + } + + // static +-void CFXJSE_FormCalcContext::Lower(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::Lower( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc < 1 || argc > 2) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Lower"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Lower"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- if (ValueIsNull(pThis, argOne.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ if (ValueIsNull(info.GetIsolate(), argOne)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- CFX_WideTextBuf szLowBuf; +- ByteString bsArg = ValueToUTF8String(argOne.get()); ++ WideTextBuffer szLowBuf; ++ ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne); + WideString wsArg = WideString::FromUTF8(bsArg.AsStringView()); + for (wchar_t ch : wsArg) { + if ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0xC0 && ch <= 0xDE)) +@@ -3800,78 +3908,79 @@ void CFXJSE_FormCalcContext::Lower(CFXJSE_Value* pThis, + ch += 1; + szLowBuf.AppendChar(ch); + } +- szLowBuf.AppendChar(0); +- +- args.GetReturnValue()->SetString( +- FX_UTF8Encode(szLowBuf.AsStringView()).AsStringView()); ++ auto result = FX_UTF8Encode(szLowBuf.AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Ltrim(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Ltrim"); ++void CFXJSE_FormCalcContext::Ltrim( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 1) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Ltrim"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- if (ValueIsNull(pThis, argOne.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ if (ValueIsNull(info.GetIsolate(), argOne)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- ByteString bsSource = ValueToUTF8String(argOne.get()); ++ ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne); + bsSource.TrimLeft(); +- args.GetReturnValue()->SetString(bsSource.AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), bsSource.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Parse(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::Parse( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 2) { +- pContext->ThrowParamCountMismatchException(L"Parse"); ++ if (info.Length() != 2) { ++ pContext->ThrowParamCountMismatchException("Parse"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- if (ValueIsNull(pThis, argTwo.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ if (ValueIsNull(info.GetIsolate(), argTwo)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- ByteString bsPattern = ValueToUTF8String(argOne.get()); +- ByteString bsValue = ValueToUTF8String(argTwo.get()); ++ ByteString bsPattern = ValueToUTF8String(info.GetIsolate(), argOne); ++ ByteString bsValue = ValueToUTF8String(info.GetIsolate(), argTwo); + CXFA_Document* pDoc = pContext->GetDocument(); + CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr(); + CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject()); +- LocaleIface* pLocale = pThisNode->GetLocale(); ++ GCedLocaleIface* pLocale = pThisNode->GetLocale(); + WideString wsPattern = WideString::FromUTF8(bsPattern.AsStringView()); + WideString wsValue = WideString::FromUTF8(bsValue.AsStringView()); + bool bPatternIsString; +- uint32_t dwPatternType; ++ CXFA_LocaleValue::ValueType dwPatternType; + std::tie(bPatternIsString, dwPatternType) = + PatternStringType(bsPattern.AsStringView()); + if (bPatternIsString) { + CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale, + pMgr); + if (!localeValue.IsValid()) { +- args.GetReturnValue()->SetString(""); ++ info.GetReturnValue().SetEmptyString(); + return; + } +- args.GetReturnValue()->SetString( +- localeValue.GetValue().ToUTF8().AsStringView()); ++ auto result = localeValue.GetValue().ToUTF8(); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); + return; + } + + switch (dwPatternType) { +- case XFA_VT_DATETIME: { ++ case CXFA_LocaleValue::ValueType::kDateTime: { + auto iTChar = wsPattern.Find(L'T'); + if (!iTChar.has_value()) { +- args.GetReturnValue()->SetString(""); ++ info.GetReturnValue().SetEmptyString(); + return; + } + WideString wsDatePattern(L"date{" + wsPattern.First(iTChar.value()) + +@@ -3883,243 +3992,228 @@ void CFXJSE_FormCalcContext::Parse(CFXJSE_Value* pThis, + CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale, + pMgr); + if (!localeValue.IsValid()) { +- args.GetReturnValue()->SetString(""); ++ info.GetReturnValue().SetEmptyString(); + return; + } +- args.GetReturnValue()->SetString( +- localeValue.GetValue().ToUTF8().AsStringView()); ++ auto result = localeValue.GetValue().ToUTF8(); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); + return; + } +- case XFA_VT_DATE: { ++ case CXFA_LocaleValue::ValueType::kDate: { + wsPattern = L"date{" + wsPattern + L"}"; + CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale, + pMgr); + if (!localeValue.IsValid()) { +- args.GetReturnValue()->SetString(""); ++ info.GetReturnValue().SetEmptyString(); + return; + } +- args.GetReturnValue()->SetString( +- localeValue.GetValue().ToUTF8().AsStringView()); ++ auto result = localeValue.GetValue().ToUTF8(); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); + return; + } +- case XFA_VT_TIME: { ++ case CXFA_LocaleValue::ValueType::kTime: { + wsPattern = L"time{" + wsPattern + L"}"; + CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale, + pMgr); + if (!localeValue.IsValid()) { +- args.GetReturnValue()->SetString(""); ++ info.GetReturnValue().SetEmptyString(); + return; + } +- args.GetReturnValue()->SetString( +- localeValue.GetValue().ToUTF8().AsStringView()); ++ auto result = localeValue.GetValue().ToUTF8(); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); + return; + } +- case XFA_VT_TEXT: { ++ case CXFA_LocaleValue::ValueType::kText: { + wsPattern = L"text{" + wsPattern + L"}"; +- CXFA_LocaleValue localeValue(XFA_VT_TEXT, wsValue, wsPattern, pLocale, +- pMgr); ++ CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kText, wsValue, ++ wsPattern, pLocale, pMgr); + if (!localeValue.IsValid()) { +- args.GetReturnValue()->SetString(""); ++ info.GetReturnValue().SetEmptyString(); + return; + } +- args.GetReturnValue()->SetString( +- localeValue.GetValue().ToUTF8().AsStringView()); ++ auto result = localeValue.GetValue().ToUTF8(); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); + return; + } +- case XFA_VT_FLOAT: { ++ case CXFA_LocaleValue::ValueType::kFloat: { + wsPattern = L"num{" + wsPattern + L"}"; +- CXFA_LocaleValue localeValue(XFA_VT_FLOAT, wsValue, wsPattern, pLocale, +- pMgr); ++ CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kFloat, wsValue, ++ wsPattern, pLocale, pMgr); + if (!localeValue.IsValid()) { +- args.GetReturnValue()->SetString(""); ++ info.GetReturnValue().SetEmptyString(); + return; + } +- args.GetReturnValue()->SetDouble(localeValue.GetDoubleNum()); ++ info.GetReturnValue().Set(localeValue.GetDoubleNum()); + return; + } + default: { + { + WideString wsTestPattern = L"num{" + wsPattern + L"}"; +- CXFA_LocaleValue localeValue(XFA_VT_FLOAT, wsValue, wsTestPattern, +- pLocale, pMgr); ++ CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kFloat, ++ wsValue, wsTestPattern, pLocale, pMgr); + if (localeValue.IsValid()) { +- args.GetReturnValue()->SetDouble(localeValue.GetDoubleNum()); ++ info.GetReturnValue().Set(localeValue.GetDoubleNum()); + return; + } + } + + { + WideString wsTestPattern = L"text{" + wsPattern + L"}"; +- CXFA_LocaleValue localeValue(XFA_VT_TEXT, wsValue, wsTestPattern, +- pLocale, pMgr); ++ CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kText, ++ wsValue, wsTestPattern, pLocale, pMgr); + if (localeValue.IsValid()) { +- args.GetReturnValue()->SetString( +- localeValue.GetValue().ToUTF8().AsStringView()); ++ auto result = localeValue.GetValue().ToUTF8(); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); + return; + } + } +- args.GetReturnValue()->SetString(""); ++ info.GetReturnValue().SetEmptyString(); + return; + } + } + } + + // static +-void CFXJSE_FormCalcContext::Replace(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::Replace( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc < 2 || argc > 3) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Replace"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Replace"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ v8::Local argTwo = GetSimpleValue(info, 1); + ByteString bsOne; + ByteString bsTwo; +- if (!ValueIsNull(pThis, argOne.get()) && !ValueIsNull(pThis, argTwo.get())) { +- bsOne = ValueToUTF8String(argOne.get()); +- bsTwo = ValueToUTF8String(argTwo.get()); ++ if (!ValueIsNull(info.GetIsolate(), argOne) && ++ !ValueIsNull(info.GetIsolate(), argTwo)) { ++ bsOne = ValueToUTF8String(info.GetIsolate(), argOne); ++ bsTwo = ValueToUTF8String(info.GetIsolate(), argTwo); + } + + ByteString bsThree; + if (argc > 2) { +- std::unique_ptr argThree = GetSimpleValue(pThis, args, 2); +- bsThree = ValueToUTF8String(argThree.get()); ++ v8::Local argThree = GetSimpleValue(info, 2); ++ bsThree = ValueToUTF8String(info.GetIsolate(), argThree); + } + +- size_t iFindLen = bsTwo.GetLength(); +- std::ostringstream szResult; +- size_t iFindIndex = 0; +- for (size_t u = 0; u < bsOne.GetLength(); ++u) { +- char ch = static_cast(bsOne[u]); +- if (ch != static_cast(bsTwo[iFindIndex])) { +- szResult << ch; +- continue; +- } +- +- size_t iTemp = u + 1; +- ++iFindIndex; +- while (iFindIndex < iFindLen) { +- uint8_t chTemp = bsOne[iTemp]; +- if (chTemp != bsTwo[iFindIndex]) { +- iFindIndex = 0; +- break; +- } +- +- ++iTemp; +- ++iFindIndex; +- } +- if (iFindIndex == iFindLen) { +- szResult << bsThree; +- u += iFindLen - 1; +- iFindIndex = 0; +- } else { +- szResult << ch; +- } +- } +- szResult << '\0'; +- args.GetReturnValue()->SetString(ByteStringView(szResult.str().c_str())); ++ bsOne.Replace(bsTwo.AsStringView(), bsThree.AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), bsOne.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Right(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 2) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Right"); ++void CFXJSE_FormCalcContext::Right( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 2) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Right"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- if ((ValueIsNull(pThis, argOne.get())) || +- (ValueIsNull(pThis, argTwo.get()))) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ if ((ValueIsNull(info.GetIsolate(), argOne)) || ++ (ValueIsNull(info.GetIsolate(), argTwo))) { ++ info.GetReturnValue().SetNull(); + return; + } + +- ByteString bsSource = ValueToUTF8String(argOne.get()); +- int32_t count = std::max(0, ValueToInteger(pThis, argTwo.get())); +- args.GetReturnValue()->SetString(bsSource.Last(count).AsStringView()); ++ ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne); ++ int32_t count = std::max(0, ValueToInteger(info.GetIsolate(), argTwo)); ++ info.GetReturnValue().Set(fxv8::NewStringHelper( ++ info.GetIsolate(), bsSource.Last(count).AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Rtrim(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Rtrim"); ++void CFXJSE_FormCalcContext::Rtrim( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 1) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Rtrim"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- if (ValueIsNull(pThis, argOne.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ if (ValueIsNull(info.GetIsolate(), argOne)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- ByteString bsSource = ValueToUTF8String(argOne.get()); ++ ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne); + bsSource.TrimRight(); +- args.GetReturnValue()->SetString(bsSource.AsStringView()); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), bsSource.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Space(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Space"); ++void CFXJSE_FormCalcContext::Space( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 1) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Space"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- if (argOne->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ if (fxv8::IsNull(argOne)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- int32_t count = std::max(0, ValueToInteger(pThis, argOne.get())); +- std::ostringstream spaceString; +- int32_t index = 0; +- while (index < count) { +- spaceString << ' '; +- index++; ++ int count = std::max(0, ValueToInteger(info.GetIsolate(), argOne)); ++ if (count > kMaxCharCount) { ++ ToFormCalcContext(pThis)->ThrowException("String too long."); ++ return; + } +- spaceString << '\0'; +- args.GetReturnValue()->SetString(ByteStringView(spaceString.str().c_str())); ++ DataVector space_string(count, ' '); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), ByteStringView(space_string))); + } + + // static +-void CFXJSE_FormCalcContext::Str(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::Str( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc < 1 || argc > 3) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Str"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Str"); + return; + } + +- std::unique_ptr numberValue = GetSimpleValue(pThis, args, 0); +- if (numberValue->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local numberValue = GetSimpleValue(info, 0); ++ if (fxv8::IsNull(numberValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- float fNumber = ValueToFloat(pThis, numberValue.get()); ++ float fNumber = ValueToFloat(info.GetIsolate(), numberValue); + +- int32_t iWidth = 10; ++ constexpr int32_t kDefaultWidth = 10; ++ int32_t iWidth = kDefaultWidth; + if (argc > 1) { +- std::unique_ptr widthValue = GetSimpleValue(pThis, args, 1); +- iWidth = static_cast(ValueToFloat(pThis, widthValue.get())); ++ v8::Local widthValue = GetSimpleValue(info, 1); ++ iWidth = static_cast(ValueToFloat(info.GetIsolate(), widthValue)); ++ if (iWidth > kMaxCharCount) { ++ ToFormCalcContext(pThis)->ThrowException("String too long."); ++ return; ++ } + } + +- int32_t iPrecision = 0; ++ constexpr int32_t kDefaultPrecision = 0; ++ int32_t iPrecision = kDefaultPrecision; + if (argc > 2) { +- std::unique_ptr precisionValue = +- GetSimpleValue(pThis, args, 2); +- iPrecision = std::max( +- 0, static_cast(ValueToFloat(pThis, precisionValue.get()))); ++ constexpr int32_t kMaxPrecision = 15; ++ v8::Local precision_value = GetSimpleValue(info, 2); ++ iPrecision = std::max(0, static_cast(ValueToFloat( ++ info.GetIsolate(), precision_value))); ++ iPrecision = std::min(iPrecision, kMaxPrecision); + } + + ByteString bsFormat = "%"; +@@ -4140,34 +4234,31 @@ void CFXJSE_FormCalcContext::Str(CFXJSE_Value* pThis, + ++u; + } + +- std::ostringstream resultBuf; + if (u > iWidth || (iPrecision + u) >= iWidth) { +- int32_t i = 0; +- while (i < iWidth) { +- resultBuf << '*'; +- ++i; +- } +- resultBuf << '\0'; +- args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str())); ++ DataVector stars(std::max(iWidth, 0), '*'); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), ByteStringView(stars))); + return; + } + ++ ByteString resultBuf; + if (u == iLength) { + if (iLength > iWidth) { + int32_t i = 0; + while (i < iWidth) { +- resultBuf << '*'; ++ resultBuf += '*'; + ++i; + } + } else { + int32_t i = 0; + while (i < iWidth - iLength) { +- resultBuf << ' '; ++ resultBuf += ' '; + ++i; + } +- resultBuf << pData; ++ resultBuf += pData; + } +- args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str())); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), resultBuf.AsStringView())); + return; + } + +@@ -4177,16 +4268,16 @@ void CFXJSE_FormCalcContext::Str(CFXJSE_Value* pThis, + + int32_t i = 0; + while (i < iLeavingSpace) { +- resultBuf << ' '; ++ resultBuf += ' '; + ++i; + } + i = 0; + while (i < u) { +- resultBuf << pData[i]; ++ resultBuf += pData[i]; + ++i; + } + if (iPrecision != 0) +- resultBuf << '.'; ++ resultBuf += '.'; + + u++; + i = 0; +@@ -4194,221 +4285,213 @@ void CFXJSE_FormCalcContext::Str(CFXJSE_Value* pThis, + if (i >= iPrecision) + break; + +- resultBuf << pData[u]; ++ resultBuf += pData[u]; + ++i; + ++u; + } + while (i < iPrecision) { +- resultBuf << '0'; ++ resultBuf += '0'; + ++i; + } +- resultBuf << '\0'; +- args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str())); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), resultBuf.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Stuff(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::Stuff( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc < 3 || argc > 4) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Stuff"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Stuff"); + return; + } + +- ByteString bsSource; +- ByteString bsInsert; +- int32_t iLength = 0; +- int32_t iStart = 0; ++ v8::Local sourceValue = GetSimpleValue(info, 0); ++ v8::Local startValue = GetSimpleValue(info, 1); ++ v8::Local deleteValue = GetSimpleValue(info, 2); ++ if (fxv8::IsNull(sourceValue) || fxv8::IsNull(startValue) || ++ fxv8::IsNull(deleteValue)) { ++ info.GetReturnValue().SetNull(); ++ return; ++ } ++ ++ int32_t iStart = 1; // one-based character indexing. + int32_t iDelete = 0; +- std::unique_ptr sourceValue = GetSimpleValue(pThis, args, 0); +- std::unique_ptr startValue = GetSimpleValue(pThis, args, 1); +- std::unique_ptr deleteValue = GetSimpleValue(pThis, args, 2); +- if (!sourceValue->IsNull() && !startValue->IsNull() && +- !deleteValue->IsNull()) { +- bsSource = ValueToUTF8String(sourceValue.get()); +- iLength = bsSource.GetLength(); ++ ByteString bsSource = ValueToUTF8String(info.GetIsolate(), sourceValue); ++ int32_t iLength = pdfium::base::checked_cast(bsSource.GetLength()); ++ if (iLength) { + iStart = pdfium::clamp( +- static_cast(ValueToFloat(pThis, startValue.get())), 1, ++ static_cast(ValueToFloat(info.GetIsolate(), startValue)), 1, + iLength); +- iDelete = std::max( +- 0, static_cast(ValueToFloat(pThis, deleteValue.get()))); ++ iDelete = pdfium::clamp( ++ static_cast(ValueToFloat(info.GetIsolate(), deleteValue)), 0, ++ iLength - iStart + 1); + } + ++ ByteString bsInsert; + if (argc > 3) { +- std::unique_ptr insertValue = GetSimpleValue(pThis, args, 3); +- bsInsert = ValueToUTF8String(insertValue.get()); ++ v8::Local insertValue = GetSimpleValue(info, 3); ++ bsInsert = ValueToUTF8String(info.GetIsolate(), insertValue); + } + +- --iStart; +- std::ostringstream szResult; +- int32_t i = 0; +- while (i < iStart) { +- szResult << static_cast(bsSource[i]); +- ++i; +- } +- szResult << bsInsert.AsStringView(); +- i = iStart + iDelete; +- while (i < iLength) { +- szResult << static_cast(bsSource[i]); +- ++i; +- } +- szResult << '\0'; +- args.GetReturnValue()->SetString(ByteStringView(szResult.str().c_str())); ++ --iStart; // now zero-based. ++ ByteString bsResult = {bsSource.AsStringView().First(iStart), ++ bsInsert.AsStringView(), ++ bsSource.AsStringView().Substr(iStart + iDelete)}; ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), bsResult.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Substr(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 3) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Substr"); ++void CFXJSE_FormCalcContext::Substr( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 3) { ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Substr"); + return; + } + +- std::unique_ptr string_value = GetSimpleValue(pThis, args, 0); +- std::unique_ptr start_value = GetSimpleValue(pThis, args, 1); +- std::unique_ptr end_value = GetSimpleValue(pThis, args, 2); +- if (ValueIsNull(pThis, string_value.get()) || +- ValueIsNull(pThis, start_value.get()) || +- ValueIsNull(pThis, end_value.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local string_value = GetSimpleValue(info, 0); ++ v8::Local start_value = GetSimpleValue(info, 1); ++ v8::Local end_value = GetSimpleValue(info, 2); ++ if (ValueIsNull(info.GetIsolate(), string_value) || ++ ValueIsNull(info.GetIsolate(), start_value) || ++ ValueIsNull(info.GetIsolate(), end_value)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- ByteString bsSource = ValueToUTF8String(string_value.get()); ++ ByteString bsSource = ValueToUTF8String(info.GetIsolate(), string_value); + size_t iLength = bsSource.GetLength(); + if (iLength == 0) { +- args.GetReturnValue()->SetString(""); ++ info.GetReturnValue().SetEmptyString(); + return; + } + + // |start_value| is 1-based. Assume first character if |start_value| is less + // than 1, per spec. Subtract 1 since |iStart| is 0-based. +- size_t iStart = std::max(ValueToInteger(pThis, start_value.get()), 1) - 1; ++ size_t iStart = ++ std::max(ValueToInteger(info.GetIsolate(), start_value), 1) - 1; + if (iStart >= iLength) { +- args.GetReturnValue()->SetString(""); ++ info.GetReturnValue().SetEmptyString(); + return; + } + + // Negative values are treated as 0. Can't clamp() due to sign mismatches. +- size_t iCount = std::max(ValueToInteger(pThis, end_value.get()), 0); ++ size_t iCount = std::max(ValueToInteger(info.GetIsolate(), end_value), 0); + iCount = std::min(iCount, iLength - iStart); +- args.GetReturnValue()->SetString( +- bsSource.Substr(iStart, iCount).AsStringView()); ++ info.GetReturnValue().Set(fxv8::NewStringHelper( ++ info.GetIsolate(), bsSource.Substr(iStart, iCount).AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Uuid(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::Uuid( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc < 0 || argc > 1) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Uuid"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Uuid"); + return; + } + + int32_t iNum = 0; + if (argc > 0) { +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- iNum = static_cast(ValueToFloat(pThis, argOne.get())); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ iNum = static_cast(ValueToFloat(info.GetIsolate(), argOne)); + } +- args.GetReturnValue()->SetString(GUIDString(!!iNum).AsStringView()); ++ info.GetReturnValue().Set(fxv8::NewStringHelper( ++ info.GetIsolate(), GUIDString(!!iNum).AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Upper(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::Upper( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc < 1 || argc > 2) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"Upper"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Upper"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- if (ValueIsNull(pThis, argOne.get())) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ if (ValueIsNull(info.GetIsolate(), argOne)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- CFX_WideTextBuf upperStringBuf; +- ByteString bsArg = ValueToUTF8String(argOne.get()); ++ ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne); + WideString wsArg = WideString::FromUTF8(bsArg.AsStringView()); +- const wchar_t* pData = wsArg.c_str(); +- size_t i = 0; +- while (i < wsArg.GetLength()) { +- int32_t ch = pData[i]; ++ WideString upperStringBuf; ++ upperStringBuf.Reserve(wsArg.GetLength()); ++ for (wchar_t ch : wsArg) { + if ((ch >= 0x61 && ch <= 0x7A) || (ch >= 0xE0 && ch <= 0xFE)) + ch -= 32; + else if (ch == 0x101 || ch == 0x103 || ch == 0x105) + ch -= 1; + +- upperStringBuf.AppendChar(ch); +- ++i; ++ upperStringBuf += ch; + } +- upperStringBuf.AppendChar(0); +- +- args.GetReturnValue()->SetString( +- FX_UTF8Encode(upperStringBuf.AsStringView()).AsStringView()); ++ info.GetReturnValue().Set(fxv8::NewStringHelper( ++ info.GetIsolate(), ++ FX_UTF8Encode(upperStringBuf.AsStringView()).AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::WordNum(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- int32_t argc = args.GetLength(); ++void CFXJSE_FormCalcContext::WordNum( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ int32_t argc = info.Length(); + if (argc < 1 || argc > 3) { +- ToFormCalcContext(pThis)->ThrowParamCountMismatchException(L"WordNum"); ++ ToFormCalcContext(pThis)->ThrowParamCountMismatchException("WordNum"); + return; + } + +- std::unique_ptr numberValue = GetSimpleValue(pThis, args, 0); +- if (numberValue->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local numberValue = GetSimpleValue(info, 0); ++ if (fxv8::IsNull(numberValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- float fNumber = ValueToFloat(pThis, numberValue.get()); ++ float fNumber = ValueToFloat(info.GetIsolate(), numberValue); + + int32_t iIdentifier = 0; + if (argc > 1) { +- std::unique_ptr identifierValue = +- GetSimpleValue(pThis, args, 1); +- if (identifierValue->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local identifierValue = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(identifierValue)) { ++ info.GetReturnValue().SetNull(); + return; + } + iIdentifier = +- static_cast(ValueToFloat(pThis, identifierValue.get())); ++ static_cast(ValueToFloat(info.GetIsolate(), identifierValue)); + } + + ByteString bsLocale; + if (argc > 2) { +- std::unique_ptr localeValue = GetSimpleValue(pThis, args, 2); +- if (localeValue->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local localeValue = GetSimpleValue(info, 2); ++ if (fxv8::IsNull(localeValue)) { ++ info.GetReturnValue().SetNull(); + return; + } +- bsLocale = ValueToUTF8String(localeValue.get()); ++ bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue); + } + +- if (std::isnan(fNumber) || fNumber < 0.0f || +- fNumber > 922337203685477550.0f) { +- args.GetReturnValue()->SetString("*"); ++ if (isnan(fNumber) || fNumber < 0.0f || fNumber > 922337203685477550.0f) { ++ info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "*")); + return; + } +- +- args.GetReturnValue()->SetString( +- WordUS(ByteString::Format("%.2f", fNumber), iIdentifier).AsStringView()); ++ ByteString bsFormatted = ByteString::Format("%.2f", fNumber); ++ ByteString bsWorded = WordUS(bsFormatted.AsStringView(), iIdentifier); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), bsWorded.AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Get(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::Get( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 1) { +- pContext->ThrowParamCountMismatchException(L"Get"); ++ if (info.Length() != 1) { ++ pContext->ThrowParamCountMismatchException("Get"); + return; + } + +@@ -4416,31 +4499,34 @@ void CFXJSE_FormCalcContext::Get(CFXJSE_Value* pThis, + if (!pDoc) + return; + +- IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider(); ++ CXFA_FFApp::CallbackIface* pAppProvider = pDoc->GetNotify()->GetAppProvider(); + if (!pAppProvider) + return; + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- ByteString bsUrl = ValueToUTF8String(argOne.get()); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ ByteString bsUrl = ValueToUTF8String(info.GetIsolate(), argOne); + RetainPtr pFile = + pAppProvider->DownloadURL(WideString::FromUTF8(bsUrl.AsStringView())); + if (!pFile) + return; + +- int32_t size = pFile->GetSize(); +- std::vector dataBuf(size); +- pFile->ReadBlock(dataBuf.data(), size); +- args.GetReturnValue()->SetString(ByteStringView(dataBuf)); ++ FX_FILESIZE size = pFile->GetSize(); ++ DataVector dataBuf(size); ++ ++ // TODO(tsepez): check return value? ++ (void)pFile->ReadBlock(dataBuf); ++ info.GetReturnValue().Set( ++ fxv8::NewStringHelper(info.GetIsolate(), ByteStringView(dataBuf))); + } + + // static +-void CFXJSE_FormCalcContext::Post(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::Post( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- int32_t argc = args.GetLength(); ++ int32_t argc = info.Length(); + if (argc < 2 || argc > 5) { +- pContext->ThrowParamCountMismatchException(L"Post"); ++ pContext->ThrowParamCountMismatchException("Post"); + return; + } + +@@ -4448,32 +4534,32 @@ void CFXJSE_FormCalcContext::Post(CFXJSE_Value* pThis, + if (!pDoc) + return; + +- IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider(); ++ CXFA_FFApp::CallbackIface* pAppProvider = pDoc->GetNotify()->GetAppProvider(); + if (!pAppProvider) + return; + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- ByteString bsURL = ValueToUTF8String(argOne.get()); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ ByteString bsURL = ValueToUTF8String(info.GetIsolate(), argOne); + +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- ByteString bsData = ValueToUTF8String(argTwo.get()); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ ByteString bsData = ValueToUTF8String(info.GetIsolate(), argTwo); + + ByteString bsContentType; + if (argc > 2) { +- std::unique_ptr argThree = GetSimpleValue(pThis, args, 2); +- bsContentType = ValueToUTF8String(argThree.get()); ++ v8::Local argThree = GetSimpleValue(info, 2); ++ bsContentType = ValueToUTF8String(info.GetIsolate(), argThree); + } + + ByteString bsEncode; + if (argc > 3) { +- std::unique_ptr argFour = GetSimpleValue(pThis, args, 3); +- bsEncode = ValueToUTF8String(argFour.get()); ++ v8::Local argFour = GetSimpleValue(info, 3); ++ bsEncode = ValueToUTF8String(info.GetIsolate(), argFour); + } + + ByteString bsHeader; + if (argc > 4) { +- std::unique_ptr argFive = GetSimpleValue(pThis, args, 4); +- bsHeader = ValueToUTF8String(argFive.get()); ++ v8::Local argFive = GetSimpleValue(info, 4); ++ bsHeader = ValueToUTF8String(info.GetIsolate(), argFive); + } + + WideString decodedResponse; +@@ -4486,17 +4572,18 @@ void CFXJSE_FormCalcContext::Post(CFXJSE_Value* pThis, + pContext->ThrowServerDeniedException(); + return; + } +- args.GetReturnValue()->SetString(decodedResponse.ToUTF8().AsStringView()); ++ info.GetReturnValue().Set(fxv8::NewStringHelper( ++ info.GetIsolate(), decodedResponse.ToUTF8().AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::Put(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::Put( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- int32_t argc = args.GetLength(); ++ int32_t argc = info.Length(); + if (argc < 2 || argc > 3) { +- pContext->ThrowParamCountMismatchException(L"Put"); ++ pContext->ThrowParamCountMismatchException("Put"); + return; + } + +@@ -4504,22 +4591,21 @@ void CFXJSE_FormCalcContext::Put(CFXJSE_Value* pThis, + if (!pDoc) + return; + +- IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider(); ++ CXFA_FFApp::CallbackIface* pAppProvider = pDoc->GetNotify()->GetAppProvider(); + if (!pAppProvider) + return; + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- ByteString bsURL = ValueToUTF8String(argOne.get()); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ ByteString bsURL = ValueToUTF8String(info.GetIsolate(), argOne); + +- std::unique_ptr argTwo = GetSimpleValue(pThis, args, 1); +- ByteString bsData = ValueToUTF8String(argTwo.get()); ++ v8::Local argTwo = GetSimpleValue(info, 1); ++ ByteString bsData = ValueToUTF8String(info.GetIsolate(), argTwo); + + ByteString bsEncode; + if (argc > 2) { +- std::unique_ptr argThree = GetSimpleValue(pThis, args, 2); +- bsEncode = ValueToUTF8String(argThree.get()); ++ v8::Local argThree = GetSimpleValue(info, 2); ++ bsEncode = ValueToUTF8String(info.GetIsolate(), argThree); + } +- + if (!pAppProvider->PutRequestURL( + WideString::FromUTF8(bsURL.AsStringView()), + WideString::FromUTF8(bsData.AsStringView()), +@@ -4527,869 +4613,641 @@ void CFXJSE_FormCalcContext::Put(CFXJSE_Value* pThis, + pContext->ThrowServerDeniedException(); + return; + } +- +- args.GetReturnValue()->SetString(""); ++ info.GetReturnValue().SetEmptyString(); + } + + // static +-void CFXJSE_FormCalcContext::assign_value_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::assign_value_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ v8::Isolate* pIsolate = info.GetIsolate(); + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 2) { ++ if (info.Length() != 2) { + pContext->ThrowCompilerErrorException(); + return; + } +- +- std::unique_ptr lValue = args.GetValue(0); +- std::unique_ptr rValue = GetSimpleValue(pThis, args, 1); +- if (lValue->IsArray()) { +- v8::Isolate* pIsolate = pContext->GetScriptRuntime(); +- auto leftLengthValue = pdfium::MakeUnique(pIsolate); +- lValue->GetObjectProperty("length", leftLengthValue.get()); +- int32_t iLeftLength = leftLengthValue->ToInteger(); +- auto jsObjectValue = pdfium::MakeUnique(pIsolate); +- auto propertyValue = pdfium::MakeUnique(pIsolate); +- lValue->GetObjectPropertyByIdx(1, propertyValue.get()); +- if (propertyValue->IsNull()) { +- for (int32_t i = 2; i < iLeftLength; i++) { +- lValue->GetObjectPropertyByIdx(i, jsObjectValue.get()); +- if (!SetObjectDefaultValue(jsObjectValue.get(), rValue.get())) { ++ ByteStringView bsFuncName("asgn_val_op"); ++ v8::Local lValue = info[0]; ++ v8::Local rValue = GetSimpleValue(info, 1); ++ if (fxv8::IsArray(lValue)) { ++ v8::Local arr = lValue.As(); ++ uint32_t iLeftLength = fxv8::GetArrayLengthHelper(arr); ++ v8::Local propertyValue = ++ fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1); ++ for (uint32_t i = 2; i < iLeftLength; i++) { ++ v8::Local jsValue = ++ fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, i); ++ if (!fxv8::IsObject(jsValue)) { ++ pContext->ThrowNoDefaultPropertyException(bsFuncName); ++ return; ++ } ++ v8::Local jsObjectValue = jsValue.As(); ++ if (fxv8::IsNull(propertyValue)) { ++ if (!SetObjectDefaultValue(pIsolate, jsObjectValue, rValue)) { + pContext->ThrowNoDefaultPropertyException(bsFuncName); + return; + } +- } +- } else { +- for (int32_t i = 2; i < iLeftLength; i++) { +- lValue->GetObjectPropertyByIdx(i, jsObjectValue.get()); +- jsObjectValue->SetObjectProperty( +- propertyValue->ToString().AsStringView(), rValue.get()); ++ } else { ++ fxv8::ReentrantPutObjectPropertyHelper( ++ pIsolate, jsObjectValue, ++ fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue) ++ .AsStringView(), ++ rValue); + } + } +- } else if (lValue->IsObject()) { +- if (!SetObjectDefaultValue(lValue.get(), rValue.get())) { ++ } else if (fxv8::IsObject(lValue)) { ++ if (!SetObjectDefaultValue(pIsolate, lValue.As(), rValue)) { + pContext->ThrowNoDefaultPropertyException(bsFuncName); + return; + } + } +- args.GetReturnValue()->Assign(rValue.get()); ++ info.GetReturnValue().Set(rValue); + } + + // static +-void CFXJSE_FormCalcContext::logical_or_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 2) { ++void CFXJSE_FormCalcContext::logical_or_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 2) { + ToFormCalcContext(pThis)->ThrowCompilerErrorException(); + return; + } + +- std::unique_ptr argFirst = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argSecond = GetSimpleValue(pThis, args, 1); +- if (argFirst->IsNull() && argSecond->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argFirst = GetSimpleValue(info, 0); ++ v8::Local argSecond = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- float first = ValueToFloat(pThis, argFirst.get()); +- float second = ValueToFloat(pThis, argSecond.get()); +- args.GetReturnValue()->SetInteger((first || second) ? 1 : 0); ++ float first = ValueToFloat(info.GetIsolate(), argFirst); ++ float second = ValueToFloat(info.GetIsolate(), argSecond); ++ info.GetReturnValue().Set(static_cast(first || second)); + } + + // static +-void CFXJSE_FormCalcContext::logical_and_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 2) { ++void CFXJSE_FormCalcContext::logical_and_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 2) { + ToFormCalcContext(pThis)->ThrowCompilerErrorException(); + return; + } + +- std::unique_ptr argFirst = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argSecond = GetSimpleValue(pThis, args, 1); +- if (argFirst->IsNull() && argSecond->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argFirst = GetSimpleValue(info, 0); ++ v8::Local argSecond = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- float first = ValueToFloat(pThis, argFirst.get()); +- float second = ValueToFloat(pThis, argSecond.get()); +- args.GetReturnValue()->SetInteger((first && second) ? 1 : 0); ++ float first = ValueToFloat(info.GetIsolate(), argFirst); ++ float second = ValueToFloat(info.GetIsolate(), argSecond); ++ info.GetReturnValue().Set(static_cast(first && second)); + } + + // static +-void CFXJSE_FormCalcContext::equality_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 2) { ++void CFXJSE_FormCalcContext::equality_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 2) { + ToFormCalcContext(pThis)->ThrowCompilerErrorException(); + return; + } + +- if (fm_ref_equal(pThis, args)) { +- args.GetReturnValue()->SetInteger(1); ++ if (fm_ref_equal(pThis, info)) { ++ info.GetReturnValue().Set(1); + return; + } + +- std::unique_ptr argFirst = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argSecond = GetSimpleValue(pThis, args, 1); +- if (argFirst->IsNull() || argSecond->IsNull()) { +- args.GetReturnValue()->SetInteger( +- (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0); ++ v8::Local argFirst = GetSimpleValue(info, 0); ++ v8::Local argSecond = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) { ++ info.GetReturnValue().Set( ++ static_cast(fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond))); + return; + } + +- if (argFirst->IsString() && argSecond->IsString()) { +- args.GetReturnValue()->SetInteger(argFirst->ToString() == +- argSecond->ToString()); ++ if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) { ++ info.GetReturnValue().Set(static_cast( ++ fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst) == ++ fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond))); + return; + } + +- double first = ValueToDouble(pThis, argFirst.get()); +- double second = ValueToDouble(pThis, argSecond.get()); +- args.GetReturnValue()->SetInteger((first == second) ? 1 : 0); ++ double first = ValueToDouble(info.GetIsolate(), argFirst); ++ double second = ValueToDouble(info.GetIsolate(), argSecond); ++ info.GetReturnValue().Set(static_cast(first == second)); + } + + // static +-void CFXJSE_FormCalcContext::notequality_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 2) { ++void CFXJSE_FormCalcContext::notequality_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 2) { + ToFormCalcContext(pThis)->ThrowCompilerErrorException(); + return; + } + +- if (fm_ref_equal(pThis, args)) { +- args.GetReturnValue()->SetInteger(0); ++ if (fm_ref_equal(pThis, info)) { ++ info.GetReturnValue().Set(0); + return; + } + +- std::unique_ptr argFirst = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argSecond = GetSimpleValue(pThis, args, 1); +- if (argFirst->IsNull() || argSecond->IsNull()) { +- args.GetReturnValue()->SetInteger( +- (argFirst->IsNull() && argSecond->IsNull()) ? 0 : 1); ++ v8::Local argFirst = GetSimpleValue(info, 0); ++ v8::Local argSecond = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) { ++ info.GetReturnValue().Set( ++ static_cast(!fxv8::IsNull(argFirst) || !fxv8::IsNull(argSecond))); + return; + } + +- if (argFirst->IsString() && argSecond->IsString()) { +- args.GetReturnValue()->SetInteger(argFirst->ToString() != +- argSecond->ToString()); ++ if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) { ++ info.GetReturnValue().Set(static_cast( ++ fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst) != ++ fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond))); + return; + } + +- double first = ValueToDouble(pThis, argFirst.get()); +- double second = ValueToDouble(pThis, argSecond.get()); +- args.GetReturnValue()->SetInteger(first != second); ++ double first = ValueToDouble(info.GetIsolate(), argFirst); ++ double second = ValueToDouble(info.GetIsolate(), argSecond); ++ info.GetReturnValue().Set(static_cast(first != second)); + } + + // static +-bool CFXJSE_FormCalcContext::fm_ref_equal(CFXJSE_Value* pThis, +- CFXJSE_Arguments& args) { +- std::unique_ptr argFirst = args.GetValue(0); +- std::unique_ptr argSecond = args.GetValue(1); +- if (!argFirst->IsArray() || !argSecond->IsArray()) ++bool CFXJSE_FormCalcContext::fm_ref_equal( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ v8::Local argFirst = info[0]; ++ v8::Local argSecond = info[1]; ++ if (!fxv8::IsArray(argFirst) || !fxv8::IsArray(argSecond)) + return false; + +- v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime(); +- auto firstFlagValue = pdfium::MakeUnique(pIsolate); +- auto secondFlagValue = pdfium::MakeUnique(pIsolate); +- argFirst->GetObjectPropertyByIdx(0, firstFlagValue.get()); +- argSecond->GetObjectPropertyByIdx(0, secondFlagValue.get()); +- if (firstFlagValue->ToInteger() != 3 || secondFlagValue->ToInteger() != 3) ++ v8::Local firstArr = argFirst.As(); ++ v8::Local secondArr = argSecond.As(); ++ v8::Local firstFlag = ++ fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), firstArr, 0); ++ v8::Local secondFlag = ++ fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), secondArr, 0); ++ if (fxv8::ReentrantToInt32Helper(info.GetIsolate(), firstFlag) != 3 || ++ fxv8::ReentrantToInt32Helper(info.GetIsolate(), secondFlag) != 3) { + return false; ++ } ++ ++ v8::Local firstValue = ++ fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), firstArr, 2); ++ v8::Local secondValue = ++ fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), secondArr, 2); + +- auto firstJSObject = pdfium::MakeUnique(pIsolate); +- auto secondJSObject = pdfium::MakeUnique(pIsolate); +- argFirst->GetObjectPropertyByIdx(2, firstJSObject.get()); +- argSecond->GetObjectPropertyByIdx(2, secondJSObject.get()); +- if (firstJSObject->IsNull() || secondJSObject->IsNull()) ++ if (fxv8::IsNull(firstValue) || fxv8::IsNull(secondValue)) + return false; + +- return firstJSObject->ToHostObject() == secondJSObject->ToHostObject(); ++ return FXJSE_RetrieveObjectBinding(firstValue) == ++ FXJSE_RetrieveObjectBinding(secondValue); + } + + // static +-void CFXJSE_FormCalcContext::less_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 2) { ++void CFXJSE_FormCalcContext::less_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 2) { + ToFormCalcContext(pThis)->ThrowCompilerErrorException(); + return; + } + +- std::unique_ptr argFirst = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argSecond = GetSimpleValue(pThis, args, 1); +- if (argFirst->IsNull() || argSecond->IsNull()) { +- args.GetReturnValue()->SetInteger(0); ++ v8::Local argFirst = GetSimpleValue(info, 0); ++ v8::Local argSecond = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) { ++ info.GetReturnValue().Set(0); + return; + } + +- if (argFirst->IsString() && argSecond->IsString()) { +- int result = +- argFirst->ToString().Compare(argSecond->ToString().AsStringView()) < 0; +- args.GetReturnValue()->SetInteger(result); ++ if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) { ++ ByteString bs1 = ++ fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst); ++ ByteString bs2 = ++ fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond); ++ info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) < 0); + return; + } + +- double first = ValueToDouble(pThis, argFirst.get()); +- double second = ValueToDouble(pThis, argSecond.get()); +- args.GetReturnValue()->SetInteger((first < second) ? 1 : 0); ++ double first = ValueToDouble(info.GetIsolate(), argFirst); ++ double second = ValueToDouble(info.GetIsolate(), argSecond); ++ info.GetReturnValue().Set(static_cast(first < second)); + } + + // static +-void CFXJSE_FormCalcContext::lessequal_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 2) { ++void CFXJSE_FormCalcContext::lessequal_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 2) { + ToFormCalcContext(pThis)->ThrowCompilerErrorException(); + return; + } + +- std::unique_ptr argFirst = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argSecond = GetSimpleValue(pThis, args, 1); +- if (argFirst->IsNull() || argSecond->IsNull()) { +- args.GetReturnValue()->SetInteger( +- (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0); ++ v8::Local argFirst = GetSimpleValue(info, 0); ++ v8::Local argSecond = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) { ++ info.GetReturnValue().Set( ++ static_cast(fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond))); + return; + } + +- if (argFirst->IsString() && argSecond->IsString()) { +- int result = +- argFirst->ToString().Compare(argSecond->ToString().AsStringView()) <= 0; +- args.GetReturnValue()->SetInteger(result); ++ if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) { ++ auto bs1 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst); ++ auto bs2 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond); ++ info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) <= 0); + return; + } + +- double first = ValueToDouble(pThis, argFirst.get()); +- double second = ValueToDouble(pThis, argSecond.get()); +- args.GetReturnValue()->SetInteger((first <= second) ? 1 : 0); ++ double first = ValueToDouble(info.GetIsolate(), argFirst); ++ double second = ValueToDouble(info.GetIsolate(), argSecond); ++ info.GetReturnValue().Set(static_cast(first <= second)); + } + + // static +-void CFXJSE_FormCalcContext::greater_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 2) { ++void CFXJSE_FormCalcContext::greater_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 2) { + ToFormCalcContext(pThis)->ThrowCompilerErrorException(); + return; + } + +- std::unique_ptr argFirst = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argSecond = GetSimpleValue(pThis, args, 1); +- if (argFirst->IsNull() || argSecond->IsNull()) { +- args.GetReturnValue()->SetInteger(0); ++ v8::Local argFirst = GetSimpleValue(info, 0); ++ v8::Local argSecond = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) { ++ info.GetReturnValue().Set(0); + return; + } + +- if (argFirst->IsString() && argSecond->IsString()) { +- int result = +- argFirst->ToString().Compare(argSecond->ToString().AsStringView()) > 0; +- args.GetReturnValue()->SetInteger(result); ++ if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) { ++ auto bs1 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst); ++ auto bs2 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond); ++ info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) > 0); + return; + } + +- double first = ValueToDouble(pThis, argFirst.get()); +- double second = ValueToDouble(pThis, argSecond.get()); +- args.GetReturnValue()->SetInteger((first > second) ? 1 : 0); ++ double first = ValueToDouble(info.GetIsolate(), argFirst); ++ double second = ValueToDouble(info.GetIsolate(), argSecond); ++ info.GetReturnValue().Set(static_cast(first > second)); + } + + // static +-void CFXJSE_FormCalcContext::greaterequal_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 2) { ++void CFXJSE_FormCalcContext::greaterequal_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 2) { + ToFormCalcContext(pThis)->ThrowCompilerErrorException(); + return; + } + +- std::unique_ptr argFirst = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argSecond = GetSimpleValue(pThis, args, 1); +- if (argFirst->IsNull() || argSecond->IsNull()) { +- args.GetReturnValue()->SetInteger( +- (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0); ++ v8::Local argFirst = GetSimpleValue(info, 0); ++ v8::Local argSecond = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) { ++ info.GetReturnValue().Set( ++ static_cast(fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond))); + return; + } + +- if (argFirst->IsString() && argSecond->IsString()) { +- int result = +- argFirst->ToString().Compare(argSecond->ToString().AsStringView()) >= 0; +- args.GetReturnValue()->SetInteger(result); ++ if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) { ++ auto bs1 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst); ++ auto bs2 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond); ++ info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) >= 0); + return; + } + +- double first = ValueToDouble(pThis, argFirst.get()); +- double second = ValueToDouble(pThis, argSecond.get()); +- args.GetReturnValue()->SetInteger((first >= second) ? 1 : 0); ++ double first = ValueToDouble(info.GetIsolate(), argFirst); ++ double second = ValueToDouble(info.GetIsolate(), argSecond); ++ info.GetReturnValue().Set(static_cast(first >= second)); + } + + // static +-void CFXJSE_FormCalcContext::plus_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 2) { ++void CFXJSE_FormCalcContext::plus_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 2) { + ToFormCalcContext(pThis)->ThrowCompilerErrorException(); + return; + } + +- std::unique_ptr argFirst = args.GetValue(0); +- std::unique_ptr argSecond = args.GetValue(1); +- if (ValueIsNull(pThis, argFirst.get()) && +- ValueIsNull(pThis, argSecond.get())) { +- args.GetReturnValue()->SetNull(); ++ if (ValueIsNull(info.GetIsolate(), info[0]) && ++ ValueIsNull(info.GetIsolate(), info[1])) { ++ info.GetReturnValue().SetNull(); + return; + } + +- double first = ValueToDouble(pThis, argFirst.get()); +- double second = ValueToDouble(pThis, argSecond.get()); +- args.GetReturnValue()->SetDouble(first + second); ++ const double first = ValueToDouble(info.GetIsolate(), info[0]); ++ const double second = ValueToDouble(info.GetIsolate(), info[1]); ++ info.GetReturnValue().Set(first + second); + } + + // static +-void CFXJSE_FormCalcContext::minus_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 2) { ++void CFXJSE_FormCalcContext::minus_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 2) { + ToFormCalcContext(pThis)->ThrowCompilerErrorException(); + return; + } + +- std::unique_ptr argFirst = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argSecond = GetSimpleValue(pThis, args, 1); +- if (argFirst->IsNull() && argSecond->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argFirst = GetSimpleValue(info, 0); ++ v8::Local argSecond = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- double first = ValueToDouble(pThis, argFirst.get()); +- double second = ValueToDouble(pThis, argSecond.get()); +- args.GetReturnValue()->SetDouble(first - second); ++ double first = ValueToDouble(info.GetIsolate(), argFirst); ++ double second = ValueToDouble(info.GetIsolate(), argSecond); ++ info.GetReturnValue().Set(first - second); + } + + // static +-void CFXJSE_FormCalcContext::multiple_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 2) { ++void CFXJSE_FormCalcContext::multiple_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 2) { + ToFormCalcContext(pThis)->ThrowCompilerErrorException(); + return; + } + +- std::unique_ptr argFirst = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argSecond = GetSimpleValue(pThis, args, 1); +- if (argFirst->IsNull() && argSecond->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argFirst = GetSimpleValue(info, 0); ++ v8::Local argSecond = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- double first = ValueToDouble(pThis, argFirst.get()); +- double second = ValueToDouble(pThis, argSecond.get()); +- args.GetReturnValue()->SetDouble(first * second); ++ double first = ValueToDouble(info.GetIsolate(), argFirst); ++ double second = ValueToDouble(info.GetIsolate(), argSecond); ++ info.GetReturnValue().Set(first * second); + } + + // static +-void CFXJSE_FormCalcContext::divide_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::divide_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 2) { ++ if (info.Length() != 2) { + pContext->ThrowCompilerErrorException(); + return; + } + +- std::unique_ptr argFirst = GetSimpleValue(pThis, args, 0); +- std::unique_ptr argSecond = GetSimpleValue(pThis, args, 1); +- if (argFirst->IsNull() && argSecond->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argFirst = GetSimpleValue(info, 0); ++ v8::Local argSecond = GetSimpleValue(info, 1); ++ if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- double second = ValueToDouble(pThis, argSecond.get()); ++ double second = ValueToDouble(info.GetIsolate(), argSecond); + if (second == 0.0) { + pContext->ThrowDivideByZeroException(); + return; + } + +- double first = ValueToDouble(pThis, argFirst.get()); +- args.GetReturnValue()->SetDouble(first / second); ++ double first = ValueToDouble(info.GetIsolate(), argFirst); ++ info.GetReturnValue().Set(first / second); + } + + // static +-void CFXJSE_FormCalcContext::positive_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { ++void CFXJSE_FormCalcContext::positive_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 1) { + ToFormCalcContext(pThis)->ThrowCompilerErrorException(); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- if (argOne->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ if (fxv8::IsNull(argOne)) { ++ info.GetReturnValue().SetNull(); + return; + } +- args.GetReturnValue()->SetDouble(0.0 + ValueToDouble(pThis, argOne.get())); ++ info.GetReturnValue().Set(0.0 + ValueToDouble(info.GetIsolate(), argOne)); + } + + // static +-void CFXJSE_FormCalcContext::negative_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { ++void CFXJSE_FormCalcContext::negative_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 1) { + ToFormCalcContext(pThis)->ThrowCompilerErrorException(); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- if (argOne->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ if (fxv8::IsNull(argOne)) { ++ info.GetReturnValue().SetNull(); + return; + } +- args.GetReturnValue()->SetDouble(0.0 - ValueToDouble(pThis, argOne.get())); ++ info.GetReturnValue().Set(0.0 - ValueToDouble(info.GetIsolate(), argOne)); + } + + // static +-void CFXJSE_FormCalcContext::logical_not_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { ++void CFXJSE_FormCalcContext::logical_not_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 1) { + ToFormCalcContext(pThis)->ThrowCompilerErrorException(); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- if (argOne->IsNull()) { +- args.GetReturnValue()->SetNull(); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ if (fxv8::IsNull(argOne)) { ++ info.GetReturnValue().SetNull(); + return; + } + +- double first = ValueToDouble(pThis, argOne.get()); +- args.GetReturnValue()->SetInteger((first == 0.0) ? 1 : 0); ++ double first = ValueToDouble(info.GetIsolate(), argOne); ++ info.GetReturnValue().Set((first == 0.0) ? 1 : 0); + } + + // static +-void CFXJSE_FormCalcContext::dot_accessor(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- DotAccessorCommon(pThis, bsFuncName, args, /*bDotAccessor=*/true); ++void CFXJSE_FormCalcContext::dot_accessor( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ DotAccessorCommon(pThis, info, /*bDotAccessor=*/true); + } + + // static +-void CFXJSE_FormCalcContext::dotdot_accessor(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- DotAccessorCommon(pThis, bsFuncName, args, /*bDotAccessor=*/false); ++void CFXJSE_FormCalcContext::dotdot_accessor( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ DotAccessorCommon(pThis, info, /*bDotAccessor=*/false); + } + + // static +-void CFXJSE_FormCalcContext::eval_translation(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::eval_translation( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 1) { +- pContext->ThrowParamCountMismatchException(L"Eval"); ++ if (info.Length() != 1) { ++ pContext->ThrowParamCountMismatchException("Eval"); + return; + } + +- std::unique_ptr argOne = GetSimpleValue(pThis, args, 0); +- ByteString bsArg = ValueToUTF8String(argOne.get()); ++ v8::Local argOne = GetSimpleValue(info, 0); ++ ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne); + if (bsArg.IsEmpty()) { + pContext->ThrowArgumentMismatchException(); + return; + } + +- WideString wsScript = WideString::FromUTF8(bsArg.AsStringView()); +- CFX_WideTextBuf wsJavaScriptBuf; +- if (!CFXJSE_FormCalcContext::Translate(wsScript.AsStringView(), +- &wsJavaScriptBuf)) { ++ WideString wsCalcScript = WideString::FromUTF8(bsArg.AsStringView()); ++ absl::optional wsJavaScriptBuf = ++ CFXJSE_FormCalcContext::Translate(pContext->GetDocument()->GetHeap(), ++ wsCalcScript.AsStringView()); ++ if (!wsJavaScriptBuf.has_value()) { + pContext->ThrowCompilerErrorException(); + return; + } +- +- args.GetReturnValue()->SetString( +- FX_UTF8Encode(wsJavaScriptBuf.AsStringView()).AsStringView()); ++ info.GetReturnValue().Set(fxv8::NewStringHelper( ++ info.GetIsolate(), ++ FX_UTF8Encode(wsJavaScriptBuf.value().AsStringView()).AsStringView())); + } + + // static +-void CFXJSE_FormCalcContext::is_fm_object(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { +- args.GetReturnValue()->SetBoolean(false); +- return; +- } +- +- std::unique_ptr argOne = args.GetValue(0); +- args.GetReturnValue()->SetBoolean(argOne->IsObject()); ++void CFXJSE_FormCalcContext::is_fm_object( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ const bool result = info.Length() == 1 && fxv8::IsObject(info[0]); ++ info.GetReturnValue().Set(result); + } + + // static +-void CFXJSE_FormCalcContext::is_fm_array(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { +- args.GetReturnValue()->SetBoolean(false); +- return; +- } +- +- std::unique_ptr argOne = args.GetValue(0); +- args.GetReturnValue()->SetBoolean(argOne->IsArray()); ++void CFXJSE_FormCalcContext::is_fm_array( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ const bool result = info.Length() == 1 && fxv8::IsArray(info[0]); ++ info.GetReturnValue().Set(result); + } + + // static +-void CFXJSE_FormCalcContext::get_fm_value(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::get_fm_value( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 1) { ++ if (info.Length() != 1) { + pContext->ThrowCompilerErrorException(); + return; + } + +- std::unique_ptr argOne = args.GetValue(0); +- if (argOne->IsArray()) { +- v8::Isolate* pIsolate = pContext->GetScriptRuntime(); +- auto propertyValue = pdfium::MakeUnique(pIsolate); +- auto jsObjectValue = pdfium::MakeUnique(pIsolate); +- argOne->GetObjectPropertyByIdx(1, propertyValue.get()); +- argOne->GetObjectPropertyByIdx(2, jsObjectValue.get()); +- if (propertyValue->IsNull()) { +- GetObjectDefaultValue(jsObjectValue.get(), args.GetReturnValue()); ++ v8::Local argOne = info[0]; ++ if (fxv8::IsArray(argOne)) { ++ v8::Local arr = argOne.As(); ++ v8::Local propertyValue = ++ fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 1); ++ v8::Local jsValue = ++ fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2); ++ if (!fxv8::IsObject(jsValue)) { ++ info.GetReturnValue().Set(fxv8::NewUndefinedHelper(info.GetIsolate())); + return; + } +- +- jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(), +- args.GetReturnValue()); ++ v8::Local jsObjectValue = jsValue.As(); ++ if (fxv8::IsNull(propertyValue)) { ++ info.GetReturnValue().Set( ++ GetObjectDefaultValue(info.GetIsolate(), jsObjectValue)); ++ return; ++ } ++ ByteString bsName = ++ fxv8::ReentrantToByteStringHelper(info.GetIsolate(), propertyValue); ++ info.GetReturnValue().Set(fxv8::ReentrantGetObjectPropertyHelper( ++ info.GetIsolate(), jsObjectValue, bsName.AsStringView())); + return; + } + +- if (argOne->IsObject()) { +- GetObjectDefaultValue(argOne.get(), args.GetReturnValue()); ++ if (fxv8::IsObject(argOne)) { ++ v8::Local obj = argOne.As(); ++ info.GetReturnValue().Set(GetObjectDefaultValue(info.GetIsolate(), obj)); + return; + } + +- args.GetReturnValue()->Assign(argOne.get()); ++ info.GetReturnValue().Set(argOne); + } + + // static +-void CFXJSE_FormCalcContext::get_fm_jsobj(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- if (args.GetLength() != 1) { ++void CFXJSE_FormCalcContext::get_fm_jsobj( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ if (info.Length() != 1) { + ToFormCalcContext(pThis)->ThrowCompilerErrorException(); + return; + } + +- std::unique_ptr argOne = args.GetValue(0); +- if (!argOne->IsArray()) { +- args.GetReturnValue()->Assign(argOne.get()); ++ v8::Local argOne = info[0]; ++ if (!fxv8::IsArray(argOne)) { ++ info.GetReturnValue().Set(argOne); + return; + } + +-#ifndef NDEBUG +- CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- v8::Isolate* pIsolate = pContext->GetScriptRuntime(); +- auto lengthValue = pdfium::MakeUnique(pIsolate); +- argOne->GetObjectProperty("length", lengthValue.get()); +- ASSERT(lengthValue->ToInteger() >= 3); +-#endif +- +- argOne->GetObjectPropertyByIdx(2, args.GetReturnValue()); ++ v8::Local arr = argOne.As(); ++ info.GetReturnValue().Set( ++ fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2)); + } + + // static +-void CFXJSE_FormCalcContext::fm_var_filter(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { ++void CFXJSE_FormCalcContext::fm_var_filter( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- if (args.GetLength() != 1) { ++ if (info.Length() != 1) { + pContext->ThrowCompilerErrorException(); + return; + } + +- v8::Isolate* pIsolate = pContext->GetScriptRuntime(); +- std::unique_ptr argOne = args.GetValue(0); +- if (!argOne->IsArray()) { +- std::unique_ptr simpleValue = GetSimpleValue(pThis, args, 0); +- args.GetReturnValue()->Assign(simpleValue.get()); ++ v8::Local argOne = info[0]; ++ if (!fxv8::IsArray(argOne)) { ++ info.GetReturnValue().Set(GetSimpleValue(info, 0)); + return; + } + +-#ifndef NDEBUG +- auto lengthValue = pdfium::MakeUnique(pIsolate); +- argOne->GetObjectProperty("length", lengthValue.get()); +- ASSERT(lengthValue->ToInteger() >= 3); +-#endif +- +- auto flagsValue = pdfium::MakeUnique(pIsolate); +- argOne->GetObjectPropertyByIdx(0, flagsValue.get()); +- int32_t iFlags = flagsValue->ToInteger(); ++ v8::Local arr = argOne.As(); ++ v8::Local flagsValue = ++ fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 0); ++ int32_t iFlags = fxv8::ReentrantToInt32Helper(info.GetIsolate(), flagsValue); + if (iFlags != 3 && iFlags != 4) { +- std::unique_ptr simpleValue = GetSimpleValue(pThis, args, 0); +- args.GetReturnValue()->Assign(simpleValue.get()); ++ info.GetReturnValue().Set(GetSimpleValue(info, 0)); + return; + } + + if (iFlags == 4) { +- std::vector> values; +- for (int32_t i = 0; i < 3; i++) +- values.push_back(pdfium::MakeUnique(pIsolate)); +- +- values[0]->SetInteger(3); +- values[1]->SetNull(); +- values[2]->SetNull(); +- args.GetReturnValue()->SetArray(values); ++ std::vector> values(3); ++ values[0] = fxv8::NewNumberHelper(info.GetIsolate(), 3); ++ values[1] = fxv8::NewNullHelper(info.GetIsolate()); ++ values[2] = fxv8::NewNullHelper(info.GetIsolate()); ++ info.GetReturnValue().Set(fxv8::NewArrayHelper(info.GetIsolate(), values)); + return; + } + +- auto objectValue = pdfium::MakeUnique(pIsolate); +- argOne->GetObjectPropertyByIdx(2, objectValue.get()); +- if (objectValue->IsNull()) { ++ v8::Local objectValue = ++ fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2); ++ if (fxv8::IsNull(objectValue)) { + pContext->ThrowCompilerErrorException(); + return; + } +- args.GetReturnValue()->Assign(argOne.get()); +-} +- +-// static +-void CFXJSE_FormCalcContext::concat_fm_object(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args) { +- v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime(); +- uint32_t iLength = 0; +- int32_t argc = args.GetLength(); +- std::vector> argValues; +- for (int32_t i = 0; i < argc; i++) { +- argValues.push_back(args.GetValue(i)); +- if (argValues[i]->IsArray()) { +- auto lengthValue = pdfium::MakeUnique(pIsolate); +- argValues[i]->GetObjectProperty("length", lengthValue.get()); +- int32_t length = lengthValue->ToInteger(); +- iLength = iLength + ((length > 2) ? (length - 2) : 0); +- } +- ++iLength; +- } +- +- std::vector> returnValues; +- for (int32_t i = 0; i < (int32_t)iLength; i++) +- returnValues.push_back(pdfium::MakeUnique(pIsolate)); +- +- int32_t index = 0; +- for (int32_t i = 0; i < argc; i++) { +- if (argValues[i]->IsArray()) { +- auto lengthValue = pdfium::MakeUnique(pIsolate); +- argValues[i]->GetObjectProperty("length", lengthValue.get()); +- +- int32_t length = lengthValue->ToInteger(); +- for (int32_t j = 2; j < length; j++) { +- argValues[i]->GetObjectPropertyByIdx(j, returnValues[index].get()); +- index++; +- } +- } +- returnValues[index]->Assign(argValues[i].get()); +- index++; +- } +- args.GetReturnValue()->SetArray(returnValues); +-} +- +-// static +-std::unique_ptr CFXJSE_FormCalcContext::GetSimpleValue( +- CFXJSE_Value* pThis, +- CFXJSE_Arguments& args, +- uint32_t index) { +- v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime(); +- ASSERT(index < (uint32_t)args.GetLength()); +- +- std::unique_ptr argIndex = args.GetValue(index); +- if (!argIndex->IsArray() && !argIndex->IsObject()) +- return argIndex; +- +- if (argIndex->IsArray()) { +- auto lengthValue = pdfium::MakeUnique(pIsolate); +- argIndex->GetObjectProperty("length", lengthValue.get()); +- int32_t iLength = lengthValue->ToInteger(); +- auto simpleValue = pdfium::MakeUnique(pIsolate); +- if (iLength < 3) { +- simpleValue.get()->SetUndefined(); +- return simpleValue; +- } +- +- auto propertyValue = pdfium::MakeUnique(pIsolate); +- auto jsObjectValue = pdfium::MakeUnique(pIsolate); +- argIndex->GetObjectPropertyByIdx(1, propertyValue.get()); +- argIndex->GetObjectPropertyByIdx(2, jsObjectValue.get()); +- if (propertyValue->IsNull()) { +- GetObjectDefaultValue(jsObjectValue.get(), simpleValue.get()); +- return simpleValue; +- } +- +- jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(), +- simpleValue.get()); +- return simpleValue; +- } +- +- auto defaultValue = pdfium::MakeUnique(pIsolate); +- GetObjectDefaultValue(argIndex.get(), defaultValue.get()); +- return defaultValue; +-} +- +-// static +-bool CFXJSE_FormCalcContext::ValueIsNull(CFXJSE_Value* pThis, +- CFXJSE_Value* arg) { +- if (!arg || arg->IsNull()) +- return true; +- +- if (!arg->IsArray() && !arg->IsObject()) +- return false; +- +- v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime(); +- if (arg->IsArray()) { +- int32_t iLength = hvalue_get_array_length(pThis, arg); +- if (iLength < 3) +- return true; +- +- auto propertyValue = pdfium::MakeUnique(pIsolate); +- auto jsObjectValue = pdfium::MakeUnique(pIsolate); +- arg->GetObjectPropertyByIdx(1, propertyValue.get()); +- arg->GetObjectPropertyByIdx(2, jsObjectValue.get()); +- if (propertyValue->IsNull()) { +- auto defaultValue = pdfium::MakeUnique(pIsolate); +- GetObjectDefaultValue(jsObjectValue.get(), defaultValue.get()); +- return defaultValue->IsNull(); +- } +- +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(), +- newPropertyValue.get()); +- return newPropertyValue->IsNull(); +- } +- +- auto defaultValue = pdfium::MakeUnique(pIsolate); +- GetObjectDefaultValue(arg, defaultValue.get()); +- return defaultValue->IsNull(); +-} +- +-// static +-int32_t CFXJSE_FormCalcContext::hvalue_get_array_length(CFXJSE_Value* pThis, +- CFXJSE_Value* arg) { +- if (!arg || !arg->IsArray()) +- return 0; +- +- v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime(); +- auto lengthValue = pdfium::MakeUnique(pIsolate); +- arg->GetObjectProperty("length", lengthValue.get()); +- return lengthValue->ToInteger(); +-} +- +-// static +-bool CFXJSE_FormCalcContext::simpleValueCompare(CFXJSE_Value* pThis, +- CFXJSE_Value* firstValue, +- CFXJSE_Value* secondValue) { +- if (!firstValue) +- return false; +- +- if (firstValue->IsString()) { +- ByteString bsFirst = ValueToUTF8String(firstValue); +- ByteString bsSecond = ValueToUTF8String(secondValue); +- return bsFirst == bsSecond; +- } +- if (firstValue->IsNumber()) { +- float first = ValueToFloat(pThis, firstValue); +- float second = ValueToFloat(pThis, secondValue); +- return first == second; +- } +- if (firstValue->IsBoolean()) +- return firstValue->ToBoolean() == secondValue->ToBoolean(); +- +- return firstValue->IsNull() && secondValue && secondValue->IsNull(); ++ info.GetReturnValue().Set(argOne); + } + + // static +-std::vector> CFXJSE_FormCalcContext::unfoldArgs( +- CFXJSE_Value* pThis, +- CFXJSE_Arguments& args) { +- std::vector> results; +- +- int32_t iCount = 0; +- v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime(); +- int32_t argc = args.GetLength(); +- std::vector> argsValue; +- static constexpr int kStart = 1; +- for (int32_t i = 0; i < argc - kStart; i++) { +- argsValue.push_back(args.GetValue(i + kStart)); +- if (argsValue[i]->IsArray()) { +- auto lengthValue = pdfium::MakeUnique(pIsolate); +- argsValue[i]->GetObjectProperty("length", lengthValue.get()); +- int32_t iLength = lengthValue->ToInteger(); +- iCount += ((iLength > 2) ? (iLength - 2) : 0); +- } else { +- ++iCount; +- } +- } +- +- for (int32_t i = 0; i < iCount; i++) +- results.push_back(pdfium::MakeUnique(pIsolate)); +- +- int32_t index = 0; +- for (int32_t i = 0; i < argc - kStart; i++) { +- if (argsValue[i]->IsArray()) { +- auto lengthValue = pdfium::MakeUnique(pIsolate); +- argsValue[i]->GetObjectProperty("length", lengthValue.get()); +- int32_t iLength = lengthValue->ToInteger(); +- if (iLength < 3) +- continue; +- +- auto propertyValue = pdfium::MakeUnique(pIsolate); +- auto jsObjectValue = pdfium::MakeUnique(pIsolate); +- argsValue[i]->GetObjectPropertyByIdx(1, propertyValue.get()); +- if (propertyValue->IsNull()) { +- for (int32_t j = 2; j < iLength; j++) { +- argsValue[i]->GetObjectPropertyByIdx(j, jsObjectValue.get()); +- GetObjectDefaultValue(jsObjectValue.get(), results[index].get()); +- index++; +- } +- } else { +- for (int32_t j = 2; j < iLength; j++) { +- argsValue[i]->GetObjectPropertyByIdx(j, jsObjectValue.get()); +- jsObjectValue->GetObjectProperty( +- propertyValue->ToString().AsStringView(), results[index].get()); +- index++; +- } ++void CFXJSE_FormCalcContext::concat_fm_object( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info) { ++ v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetIsolate(); ++ std::vector> returnValues; ++ for (int i = 0; i < info.Length(); ++i) { ++ if (fxv8::IsArray(info[i])) { ++ v8::Local arr = info[i].As(); ++ uint32_t length = fxv8::GetArrayLengthHelper(arr); ++ for (uint32_t j = 2; j < length; j++) { ++ returnValues.push_back( ++ fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, j)); + } +- } else if (argsValue[i]->IsObject()) { +- GetObjectDefaultValue(argsValue[i].get(), results[index].get()); +- index++; +- } else { +- results[index]->Assign(argsValue[i].get()); +- index++; + } ++ returnValues.push_back(info[i]); + } +- return results; +-} +- +-// static +-void CFXJSE_FormCalcContext::GetObjectDefaultValue( +- CFXJSE_Value* pValue, +- CFXJSE_Value* pDefaultValue) { +- CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pValue)); +- if (!pNode) { +- pDefaultValue->SetNull(); +- return; +- } +- pNode->JSObject()->ScriptSomDefaultValue(pDefaultValue, false, +- XFA_Attribute::Unknown); +-} +- +-// static +-bool CFXJSE_FormCalcContext::SetObjectDefaultValue(CFXJSE_Value* pValue, +- CFXJSE_Value* hNewValue) { +- CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pValue)); +- if (!pNode) +- return false; +- +- pNode->JSObject()->ScriptSomDefaultValue(hNewValue, true, +- XFA_Attribute::Unknown); +- return true; ++ info.GetReturnValue().Set(fxv8::NewArrayHelper(pIsolate, returnValues)); + } + + // static +@@ -5400,6 +5258,8 @@ ByteString CFXJSE_FormCalcContext::GenerateSomExpression(ByteStringView bsName, + if (bIsStar) + return ByteString(bsName, "[*]"); + ++ // `iIndexFlags` values are the same as enum class ++ // `CXFA_FMIndexExpression::AccessorIndex` values. + if (iIndexFlags == 0) + return ByteString(bsName); + +@@ -5410,311 +5270,53 @@ ByteString CFXJSE_FormCalcContext::GenerateSomExpression(ByteStringView bsName, + + const bool bNegative = iIndexValue < 0; + ByteString bsSomExp(bsName); +- if (iIndexFlags == 2) ++ if (iIndexFlags == 2) { + bsSomExp += bNegative ? "[-" : "[+"; +- else +- bsSomExp += bNegative ? "[" : "[-"; +- iIndexValue = bNegative ? 0 - iIndexValue : iIndexValue; +- bsSomExp += ByteString::FormatInteger(iIndexValue); +- bsSomExp += "]"; +- return bsSomExp; +-} +- +-// static +-bool CFXJSE_FormCalcContext::GetObjectForName(CFXJSE_Value* pThis, +- CFXJSE_Value* accessorValue, +- ByteStringView bsAccessorName) { +- CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument(); +- if (!pDoc) +- return false; +- +- CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext(); +- XFA_RESOLVENODE_RS resolveNodeRS; +- uint32_t dwFlags = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties | +- XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent; +- bool bRet = pScriptContext->ResolveObjects( +- pScriptContext->GetThisObject(), +- WideString::FromUTF8(bsAccessorName).AsStringView(), &resolveNodeRS, +- dwFlags, nullptr); +- if (bRet && resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) { +- accessorValue->Assign(pScriptContext->GetOrCreateJSBindingFromMap( +- resolveNodeRS.objects.front().Get())); +- return true; +- } +- return false; +-} +- +-// static +-bool CFXJSE_FormCalcContext::ResolveObjects(CFXJSE_Value* pThis, +- CFXJSE_Value* pRefValue, +- ByteStringView bsSomExp, +- XFA_RESOLVENODE_RS* resolveNodeRS, +- bool bDotAccessor, +- bool bHasNoResolveName) { +- CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument(); +- if (!pDoc) +- return false; +- +- WideString wsSomExpression = WideString::FromUTF8(bsSomExp); +- CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext(); +- CXFA_Object* pNode = nullptr; +- uint32_t dFlags = 0UL; +- if (bDotAccessor) { +- if (pRefValue && pRefValue->IsNull()) { +- pNode = pScriptContext->GetThisObject(); +- dFlags = XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent; +- } else { +- pNode = CFXJSE_Engine::ToObject(pRefValue); +- if (!pNode) +- return false; +- +- if (bHasNoResolveName) { +- WideString wsName; +- if (CXFA_Node* pXFANode = pNode->AsNode()) { +- Optional ret = +- pXFANode->JSObject()->TryAttribute(XFA_Attribute::Name, false); +- if (ret) +- wsName = *ret; +- } +- if (wsName.IsEmpty()) +- wsName = L"#" + WideString::FromASCII(pNode->GetClassName()); +- +- wsSomExpression = wsName + wsSomExpression; +- dFlags = XFA_RESOLVENODE_Siblings; +- } else { +- dFlags = (bsSomExp == "*") +- ? (XFA_RESOLVENODE_Children) +- : (XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes | +- XFA_RESOLVENODE_Properties); +- } +- } + } else { +- pNode = CFXJSE_Engine::ToObject(pRefValue); +- dFlags = XFA_RESOLVENODE_AnyChild; +- } +- return pScriptContext->ResolveObjects(pNode, wsSomExpression.AsStringView(), +- resolveNodeRS, dFlags, nullptr); +-} +- +-// static +-void CFXJSE_FormCalcContext::ParseResolveResult( +- CFXJSE_Value* pThis, +- const XFA_RESOLVENODE_RS& resolveNodeRS, +- CFXJSE_Value* pParentValue, +- std::vector>* resultValues, +- bool* bAttribute) { +- ASSERT(bAttribute); +- +- resultValues->clear(); +- +- CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- v8::Isolate* pIsolate = pContext->GetScriptRuntime(); +- +- if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) { +- *bAttribute = false; +- CFXJSE_Engine* pScriptContext = pContext->GetDocument()->GetScriptContext(); +- for (auto& pObject : resolveNodeRS.objects) { +- resultValues->push_back(pdfium::MakeUnique(pIsolate)); +- resultValues->back()->Assign( +- pScriptContext->GetOrCreateJSBindingFromMap(pObject.Get())); +- } +- return; +- } +- +- *bAttribute = true; +- if (resolveNodeRS.script_attribute.callback && +- resolveNodeRS.script_attribute.eValueType == XFA_ScriptType::Object) { +- for (auto& pObject : resolveNodeRS.objects) { +- auto pValue = pdfium::MakeUnique(pIsolate); +- CJX_Object* jsObject = pObject->JSObject(); +- (*resolveNodeRS.script_attribute.callback)( +- jsObject, pValue.get(), false, +- resolveNodeRS.script_attribute.attribute); +- resultValues->push_back(std::move(pValue)); +- *bAttribute = false; +- } +- } +- if (!*bAttribute) +- return; +- if (!pParentValue || !pParentValue->IsObject()) +- return; +- +- resultValues->push_back(pdfium::MakeUnique(pIsolate)); +- resultValues->back()->Assign(pParentValue); +-} +- +-// static +-int32_t CFXJSE_FormCalcContext::ValueToInteger(CFXJSE_Value* pThis, +- CFXJSE_Value* pValue) { +- if (!pValue || pValue->IsEmpty()) +- return 0; +- +- v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime(); +- if (pValue->IsArray()) { +- auto propertyValue = pdfium::MakeUnique(pIsolate); +- auto jsObjectValue = pdfium::MakeUnique(pIsolate); +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- pValue->GetObjectPropertyByIdx(1, propertyValue.get()); +- pValue->GetObjectPropertyByIdx(2, jsObjectValue.get()); +- if (propertyValue->IsNull()) { +- GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get()); +- return ValueToInteger(pThis, newPropertyValue.get()); +- } +- +- jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(), +- newPropertyValue.get()); +- return ValueToInteger(pThis, newPropertyValue.get()); +- } +- if (pValue->IsObject()) { +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- GetObjectDefaultValue(pValue, newPropertyValue.get()); +- return ValueToInteger(pThis, newPropertyValue.get()); +- } +- if (pValue->IsString()) +- return FXSYS_atoi(pValue->ToString().c_str()); +- return pValue->ToInteger(); +-} +- +-// static +-float CFXJSE_FormCalcContext::ValueToFloat(CFXJSE_Value* pThis, +- CFXJSE_Value* arg) { +- if (!arg) +- return 0.0f; +- +- v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime(); +- if (arg->IsArray()) { +- auto propertyValue = pdfium::MakeUnique(pIsolate); +- auto jsObjectValue = pdfium::MakeUnique(pIsolate); +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- arg->GetObjectPropertyByIdx(1, propertyValue.get()); +- arg->GetObjectPropertyByIdx(2, jsObjectValue.get()); +- if (propertyValue->IsNull()) { +- GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get()); +- return ValueToFloat(pThis, newPropertyValue.get()); +- } +- jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(), +- newPropertyValue.get()); +- return ValueToFloat(pThis, newPropertyValue.get()); +- } +- if (arg->IsObject()) { +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- GetObjectDefaultValue(arg, newPropertyValue.get()); +- return ValueToFloat(pThis, newPropertyValue.get()); +- } +- if (arg->IsString()) +- return strtof(arg->ToString().c_str(), nullptr); +- if (arg->IsUndefined() || arg->IsEmpty()) +- return 0.0f; +- return arg->ToFloat(); +-} +- +-// static +-double CFXJSE_FormCalcContext::ValueToDouble(CFXJSE_Value* pThis, +- CFXJSE_Value* arg) { +- if (!arg) +- return 0; +- +- v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime(); +- if (arg->IsArray()) { +- auto propertyValue = pdfium::MakeUnique(pIsolate); +- auto jsObjectValue = pdfium::MakeUnique(pIsolate); +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- arg->GetObjectPropertyByIdx(1, propertyValue.get()); +- arg->GetObjectPropertyByIdx(2, jsObjectValue.get()); +- if (propertyValue->IsNull()) { +- GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get()); +- return ValueToDouble(pThis, newPropertyValue.get()); +- } +- jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(), +- newPropertyValue.get()); +- return ValueToDouble(pThis, newPropertyValue.get()); +- } +- if (arg->IsObject()) { +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- GetObjectDefaultValue(arg, newPropertyValue.get()); +- return ValueToDouble(pThis, newPropertyValue.get()); +- } +- if (arg->IsString()) +- return strtod(arg->ToString().c_str(), nullptr); +- if (arg->IsUndefined() || arg->IsEmpty()) +- return 0; +- return arg->ToDouble(); +-} +- +-// static. +-double CFXJSE_FormCalcContext::ExtractDouble(CFXJSE_Value* pThis, +- CFXJSE_Value* src, +- bool* ret) { +- ASSERT(ret); +- *ret = true; +- +- if (!src) +- return 0; +- +- if (!src->IsArray()) +- return ValueToDouble(pThis, src); +- +- v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetScriptRuntime(); +- auto lengthValue = pdfium::MakeUnique(pIsolate); +- src->GetObjectProperty("length", lengthValue.get()); +- int32_t iLength = lengthValue->ToInteger(); +- if (iLength <= 2) { +- *ret = false; +- return 0.0; ++ DCHECK_EQ(iIndexFlags, 3); ++ bsSomExp += bNegative ? "[" : "[-"; + } + +- auto propertyValue = pdfium::MakeUnique(pIsolate); +- auto jsObjectValue = pdfium::MakeUnique(pIsolate); +- src->GetObjectPropertyByIdx(1, propertyValue.get()); +- src->GetObjectPropertyByIdx(2, jsObjectValue.get()); +- if (propertyValue->IsNull()) +- return ValueToDouble(pThis, jsObjectValue.get()); +- +- auto newPropertyValue = pdfium::MakeUnique(pIsolate); +- jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(), +- newPropertyValue.get()); +- return ValueToDouble(pThis, newPropertyValue.get()); +-} +- +-// static +-ByteString CFXJSE_FormCalcContext::ValueToUTF8String(CFXJSE_Value* arg) { +- if (!arg || arg->IsNull() || arg->IsUndefined() || arg->IsEmpty()) +- return ByteString(); +- if (arg->IsBoolean()) +- return arg->ToBoolean() ? "1" : "0"; +- return arg->ToString(); ++ FX_SAFE_INT32 safe_index = iIndexValue; ++ if (bNegative) ++ safe_index = -safe_index; ++ bsSomExp += ByteString::FormatInteger(safe_index.ValueOrDefault(0)); ++ bsSomExp += "]"; ++ return bsSomExp; + } + +-// static. +-bool CFXJSE_FormCalcContext::Translate(WideStringView wsFormcalc, +- CFX_WideTextBuf* wsJavascript) { +- if (wsFormcalc.IsEmpty()) { +- wsJavascript->Clear(); +- return true; +- } ++absl::optional CFXJSE_FormCalcContext::Translate( ++ cppgc::Heap* pHeap, ++ WideStringView wsFormcalc) { ++ if (wsFormcalc.IsEmpty()) ++ return WideTextBuffer(); + +- CXFA_FMParser parser(wsFormcalc); +- std::unique_ptr ast = parser.Parse(); ++ CXFA_FMLexer lexer(wsFormcalc); ++ CXFA_FMParser parser(pHeap, &lexer); ++ CXFA_FMAST* ast = parser.Parse(); + if (!ast || parser.HasError()) +- return false; ++ return absl::nullopt; + + CXFA_FMToJavaScriptDepth::Reset(); +- if (!ast->ToJavaScript(wsJavascript)) +- return false; ++ absl::optional wsJavaScript = ast->ToJavaScript(); ++ if (!wsJavaScript.has_value()) ++ return absl::nullopt; + +- wsJavascript->AppendChar(0); +- return !CXFA_IsTooBig(wsJavascript); ++ if (CXFA_IsTooBig(wsJavaScript.value())) ++ return absl::nullopt; ++ ++ return wsJavaScript; + } + +-CFXJSE_FormCalcContext::CFXJSE_FormCalcContext(v8::Isolate* pScriptIsolate, ++CFXJSE_FormCalcContext::CFXJSE_FormCalcContext(v8::Isolate* pIsolate, + CFXJSE_Context* pScriptContext, + CXFA_Document* pDoc) +- : m_pIsolate(pScriptIsolate), +- m_pValue(pdfium::MakeUnique(pScriptIsolate)), +- m_pDocument(pDoc) { +- m_pValue->SetHostObject( +- this, +- CFXJSE_Class::Create(pScriptContext, &kFormCalcFM2JSDescriptor, false)); ++ : m_pIsolate(pIsolate), m_pDocument(pDoc) { ++ m_Value.Reset(m_pIsolate, ++ NewBoundV8Object( ++ m_pIsolate, CFXJSE_Class::Create( ++ pScriptContext, &kFormCalcDescriptor, false) ++ ->GetTemplate(m_pIsolate))); + } + + CFXJSE_FormCalcContext::~CFXJSE_FormCalcContext() = default; +@@ -5723,18 +5325,18 @@ CFXJSE_FormCalcContext* CFXJSE_FormCalcContext::AsFormCalcContext() { + return this; + } + +-void CFXJSE_FormCalcContext::GlobalPropertyGetter(CFXJSE_Value* pValue) { +- pValue->Assign(m_pValue.get()); ++v8::Local CFXJSE_FormCalcContext::GlobalPropertyGetter() { ++ return v8::Local::New(m_pIsolate, m_Value); + } + + // static +-void CFXJSE_FormCalcContext::DotAccessorCommon(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args, +- bool bDotAccessor) { ++void CFXJSE_FormCalcContext::DotAccessorCommon( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info, ++ bool bDotAccessor) { + CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); +- v8::Isolate* pIsolate = pContext->GetScriptRuntime(); +- int32_t argc = args.GetLength(); ++ v8::Isolate* pIsolate = pContext->GetIsolate(); ++ int32_t argc = info.Length(); + if (argc < 4 || argc > 5) { + pContext->ThrowCompilerErrorException(); + return; +@@ -5744,148 +5346,207 @@ void CFXJSE_FormCalcContext::DotAccessorCommon(CFXJSE_Value* pThis, + int32_t iIndexValue = 0; + if (argc > 4) { + bIsStar = false; +- iIndexValue = ValueToInteger(pThis, args.GetValue(4).get()); ++ iIndexValue = ValueToInteger(info.GetIsolate(), info[4]); + } + +- const ByteString bsName = args.GetUTF8String(2); ++ const ByteString bsName = ++ fxv8::ReentrantToByteStringHelper(info.GetIsolate(), info[2]); + const bool bHasNoResolveName = bDotAccessor && bsName.IsEmpty(); + ByteString bsSomExp = GenerateSomExpression( +- bsName.AsStringView(), args.GetInt32(3), iIndexValue, bIsStar); +- +- std::unique_ptr argAccessor = args.GetValue(0); +- if (argAccessor->IsArray()) { +- auto pLengthValue = pdfium::MakeUnique(pIsolate); +- argAccessor->GetObjectProperty("length", pLengthValue.get()); +- int32_t iLength = pLengthValue->ToInteger(); ++ bsName.AsStringView(), ++ fxv8::ReentrantToInt32Helper(info.GetIsolate(), info[3]), iIndexValue, ++ bIsStar); ++ ++ v8::Local argAccessor = info[0]; ++ if (fxv8::IsArray(argAccessor)) { ++ v8::Local arr = argAccessor.As(); ++ uint32_t iLength = fxv8::GetArrayLengthHelper(arr); + if (iLength < 3) { + pContext->ThrowArgumentMismatchException(); + return; + } + +- int32_t iCounter = 0; +- auto hJSObjValue = pdfium::MakeUnique(pIsolate); +- std::vector>> resolveValues( +- iLength - 2); ++ std::vector>> resolveValues(iLength - 2); + bool bAttribute = false; +- for (int32_t i = 2; i < iLength; i++) { +- argAccessor->GetObjectPropertyByIdx(i, hJSObjValue.get()); +- XFA_RESOLVENODE_RS resolveNodeRS; +- if (ResolveObjects(pThis, hJSObjValue.get(), bsSomExp.AsStringView(), +- &resolveNodeRS, bDotAccessor, bHasNoResolveName)) { +- ParseResolveResult(pThis, resolveNodeRS, hJSObjValue.get(), +- &resolveValues[i - 2], &bAttribute); +- iCounter += resolveValues[i - 2].size(); ++ bool bAllEmpty = true; ++ for (uint32_t i = 2; i < iLength; i++) { ++ v8::Local hJSObjValue = ++ fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, i); ++ absl::optional maybeResult = ++ ResolveObjects(pThis, hJSObjValue, bsSomExp.AsStringView(), ++ bDotAccessor, bHasNoResolveName); ++ if (maybeResult.has_value()) { ++ resolveValues[i - 2] = ParseResolveResult(pThis, maybeResult.value(), ++ hJSObjValue, &bAttribute); ++ bAllEmpty = bAllEmpty && resolveValues[i - 2].empty(); + } + } +- if (iCounter < 1) { +- pContext->ThrowPropertyNotInObjectException( +- WideString::FromUTF8(bsName.AsStringView()), +- WideString::FromUTF8(bsSomExp.AsStringView())); ++ if (bAllEmpty) { ++ pContext->ThrowPropertyNotInObjectException(bsName.AsStringView(), ++ bsSomExp.AsStringView()); + return; + } + +- std::vector> values; +- for (int32_t i = 0; i < iCounter + 2; i++) +- values.push_back(pdfium::MakeUnique(pIsolate)); ++ std::vector> values; ++ values.push_back(fxv8::NewNumberHelper(pIsolate, 1)); ++ values.push_back( ++ bAttribute ? fxv8::NewStringHelper(pIsolate, bsName.AsStringView()) ++ .As() ++ : fxv8::NewNullHelper(pIsolate).As()); ++ for (uint32_t i = 0; i < iLength - 2; i++) { ++ for (size_t j = 0; j < resolveValues[i].size(); j++) ++ values.push_back(resolveValues[i][j]); ++ } ++ info.GetReturnValue().Set(fxv8::NewArrayHelper(pIsolate, values)); ++ return; ++ } ++ ++ absl::optional maybeResult; ++ ByteString bsAccessorName = ++ fxv8::ReentrantToByteStringHelper(info.GetIsolate(), info[1]); ++ if (fxv8::IsObject(argAccessor) || ++ (fxv8::IsNull(argAccessor) && bsAccessorName.IsEmpty())) { ++ maybeResult = ResolveObjects(pThis, argAccessor, bsSomExp.AsStringView(), ++ bDotAccessor, bHasNoResolveName); ++ } else if (!fxv8::IsObject(argAccessor) && !bsAccessorName.IsEmpty()) { ++ v8::Local obj = ++ GetObjectForName(pThis, bsAccessorName.AsStringView()); ++ if (!obj.IsEmpty()) { ++ argAccessor = obj; ++ maybeResult = ResolveObjects(pThis, argAccessor, bsSomExp.AsStringView(), ++ bDotAccessor, bHasNoResolveName); ++ } ++ } ++ if (!maybeResult.has_value()) { ++ pContext->ThrowPropertyNotInObjectException(bsName.AsStringView(), ++ bsSomExp.AsStringView()); ++ return; ++ } + +- values[0]->SetInteger(1); +- if (bAttribute) +- values[1]->SetString(bsName.AsStringView()); +- else +- values[1]->SetNull(); ++ bool bAttribute = false; ++ std::vector> resolveValues = ++ ParseResolveResult(pThis, maybeResult.value(), argAccessor, &bAttribute); + +- int32_t iIndex = 2; +- for (int32_t i = 0; i < iLength - 2; i++) { +- for (size_t j = 0; j < resolveValues[i].size(); j++) { +- values[iIndex]->Assign(resolveValues[i][j].get()); +- iIndex++; ++ std::vector> values(resolveValues.size() + 2); ++ values[0] = fxv8::NewNumberHelper(pIsolate, 1); ++ values[1] = bAttribute ++ ? fxv8::NewStringHelper(pIsolate, bsName.AsStringView()) ++ .As() ++ : fxv8::NewNullHelper(pIsolate).As(); ++ ++ for (size_t i = 0; i < resolveValues.size(); i++) ++ values[i + 2] = resolveValues[i]; ++ ++ info.GetReturnValue().Set(fxv8::NewArrayHelper(pIsolate, values)); ++} ++ ++bool CFXJSE_FormCalcContext::ApplyToExpansion( ++ std::function)> fn, ++ const v8::FunctionCallbackInfo& info, ++ bool bStrict) { ++ v8::Isolate* pIsolate = info.GetIsolate(); ++ for (int32_t i = 0; i < info.Length(); i++) { ++ v8::Local argValue = info[i]; ++ if (fxv8::IsArray(argValue)) { ++ if (!ApplyToArray(pIsolate, fn, argValue.As()) && bStrict) { ++ ThrowArgumentMismatchException(); ++ return false; + } ++ } else if (fxv8::IsObject(argValue)) { ++ ApplyToObject(pIsolate, fn, argValue.As()); ++ } else if (!fxv8::IsNull(argValue)) { ++ fn(pIsolate, argValue); + } +- args.GetReturnValue()->SetArray(values); +- return; + } ++ return true; ++} + +- XFA_RESOLVENODE_RS resolveNodeRS; +- bool bRet = false; +- ByteString bsAccessorName = args.GetUTF8String(1); +- if (argAccessor->IsObject() || +- (argAccessor->IsNull() && bsAccessorName.IsEmpty())) { +- bRet = ResolveObjects(pThis, argAccessor.get(), bsSomExp.AsStringView(), +- &resolveNodeRS, bDotAccessor, bHasNoResolveName); +- } else if (!argAccessor->IsObject() && !bsAccessorName.IsEmpty() && +- GetObjectForName(pThis, argAccessor.get(), +- bsAccessorName.AsStringView())) { +- bRet = ResolveObjects(pThis, argAccessor.get(), bsSomExp.AsStringView(), +- &resolveNodeRS, bDotAccessor, bHasNoResolveName); +- } +- if (!bRet) { +- pContext->ThrowPropertyNotInObjectException( +- WideString::FromUTF8(bsName.AsStringView()), +- WideString::FromUTF8(bsSomExp.AsStringView())); +- return; +- } ++bool CFXJSE_FormCalcContext::ApplyToArray( ++ v8::Isolate* pIsolate, ++ std::function)> fn, ++ v8::Local pArray) { ++ uint32_t iLength = fxv8::GetArrayLengthHelper(pArray); ++ if (iLength < 3) ++ return false; + +- std::vector> resolveValues; +- bool bAttribute = false; +- ParseResolveResult(pThis, resolveNodeRS, argAccessor.get(), &resolveValues, +- &bAttribute); ++ v8::Local propertyValue = ++ fxv8::ReentrantGetArrayElementHelper(pIsolate, pArray, 1); + +- std::vector> values; +- for (size_t i = 0; i < resolveValues.size() + 2; i++) +- values.push_back(pdfium::MakeUnique(pIsolate)); ++ ByteString bsName; ++ const bool nullprop = fxv8::IsNull(propertyValue); ++ if (!nullprop) ++ bsName = fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue); + +- values[0]->SetInteger(1); +- if (bAttribute) +- values[1]->SetString(bsName.AsStringView()); +- else +- values[1]->SetNull(); ++ for (uint32_t j = 2; j < iLength; j++) { ++ v8::Local jsValue = ++ fxv8::ReentrantGetArrayElementHelper(pIsolate, pArray, j); ++ if (!fxv8::IsObject(jsValue)) ++ continue; + +- for (size_t i = 0; i < resolveValues.size(); i++) +- values[i + 2]->Assign(resolveValues[i].get()); ++ v8::Local jsObjectValue = jsValue.As(); ++ v8::Local newPropertyValue = ++ nullprop ? GetObjectDefaultValue(pIsolate, jsObjectValue) ++ : fxv8::ReentrantGetObjectPropertyHelper( ++ pIsolate, jsObjectValue, bsName.AsStringView()); ++ if (!fxv8::IsNull(newPropertyValue)) ++ fn(pIsolate, newPropertyValue); ++ } ++ return true; ++} + +- args.GetReturnValue()->SetArray(values); ++void CFXJSE_FormCalcContext::ApplyToObject( ++ v8::Isolate* pIsolate, ++ std::function)> fn, ++ v8::Local pObject) { ++ v8::Local newPropertyValue = ++ GetObjectDefaultValue(pIsolate, pObject); ++ if (!fxv8::IsNull(newPropertyValue)) ++ fn(pIsolate, newPropertyValue); + } + + void CFXJSE_FormCalcContext::ThrowNoDefaultPropertyException( + ByteStringView name) const { +- ThrowException(WideString::FromUTF8(name) + +- WideString::FromASCII(" doesn't have a default property.")); ++ ByteString msg(name); ++ msg += " doesn't have a default property."; ++ ThrowException(msg.AsStringView()); + } + + void CFXJSE_FormCalcContext::ThrowCompilerErrorException() const { +- ThrowException(WideString::FromASCII("Compiler error.")); ++ ThrowException("Compiler error."); + } + + void CFXJSE_FormCalcContext::ThrowDivideByZeroException() const { +- ThrowException(WideString::FromASCII("Divide by zero.")); ++ ThrowException("Divide by zero."); + } + + void CFXJSE_FormCalcContext::ThrowServerDeniedException() const { +- ThrowException(WideString::FromASCII("Server does not permit operation.")); ++ ThrowException("Server does not permit operation."); + } + + void CFXJSE_FormCalcContext::ThrowPropertyNotInObjectException( +- const WideString& name, +- const WideString& exp) const { +- ThrowException( +- WideString::FromASCII("An attempt was made to reference property '") + +- name + WideString::FromASCII("' of a non-object in SOM expression ") + +- exp + L"."); ++ ByteStringView name, ++ ByteStringView exp) const { ++ ByteString msg("An attempt was made to reference property '"); ++ msg += name; ++ msg += "' of a non-object in SOM expression "; ++ msg += exp; ++ msg += "."; ++ ThrowException(msg.AsStringView()); + } + + void CFXJSE_FormCalcContext::ThrowParamCountMismatchException( +- const WideString& method) const { +- ThrowException( +- WideString::FromASCII("Incorrect number of parameters calling method '") + +- method + L"'."); ++ ByteStringView method) const { ++ ByteString msg("Incorrect number of parameters calling method '"); ++ msg += method; ++ msg += "'."; ++ ThrowException(msg.AsStringView()); + } + + void CFXJSE_FormCalcContext::ThrowArgumentMismatchException() const { +- ThrowException(WideString::FromASCII( +- "Argument mismatch in property or function argument.")); ++ ThrowException("Argument mismatch in property or function argument."); + } + +-void CFXJSE_FormCalcContext::ThrowException(const WideString& str) const { +- ASSERT(!str.IsEmpty()); +- FXJSE_ThrowMessage(str.ToUTF8().AsStringView()); ++void CFXJSE_FormCalcContext::ThrowException(ByteStringView str) const { ++ DCHECK(!str.IsEmpty()); ++ FXJSE_ThrowMessage(GetIsolate(), str); + } +diff --git a/fxjs/xfa/cfxjse_formcalc_context.h b/fxjs/xfa/cfxjse_formcalc_context.h +index cf1bc9588..3c7d2715c 100644 +--- a/fxjs/xfa/cfxjse_formcalc_context.h ++++ b/fxjs/xfa/cfxjse_formcalc_context.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,21 +7,27 @@ + #ifndef FXJS_XFA_CFXJSE_FORMCALC_CONTEXT_H_ + #define FXJS_XFA_CFXJSE_FORMCALC_CONTEXT_H_ + +-#include +-#include ++#include + +-#include "core/fxcrt/unowned_ptr.h" ++#include ++ ++#include "core/fxcrt/widetext_buffer.h" + #include "fxjs/xfa/fxjse.h" +-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "v8/include/cppgc/persistent.h" ++#include "v8/include/v8-forward.h" ++#include "v8/include/v8-persistent-handle.h" + +-class CFXJSE_Arguments; + class CFXJSE_Context; +-class CFX_WideTextBuf; + class CXFA_Document; + ++namespace cppgc { ++class Heap; ++} // namespace cppgc ++ + class CFXJSE_FormCalcContext final : public CFXJSE_HostObject { + public: +- CFXJSE_FormCalcContext(v8::Isolate* pScriptIsolate, ++ CFXJSE_FormCalcContext(v8::Isolate* pIsolate, + CFXJSE_Context* pScriptContext, + CXFA_Document* pDoc); + ~CFXJSE_FormCalcContext() override; +@@ -29,387 +35,279 @@ class CFXJSE_FormCalcContext final : public CFXJSE_HostObject { + // CFXJSE_HostObject: + CFXJSE_FormCalcContext* AsFormCalcContext() override; + +- static void Abs(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Avg(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Ceil(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Count(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Floor(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Max(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Min(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Mod(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Round(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Sum(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Date(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Date2Num(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void DateFmt(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void IsoDate2Num(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void IsoTime2Num(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void LocalDateFmt(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void LocalTimeFmt(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Num2Date(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Num2GMTime(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Num2Time(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Time(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Time2Num(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void TimeFmt(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); ++ static void Abs(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Avg(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Ceil(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Count(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Floor(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Max(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Min(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Mod(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Round(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Sum(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Date(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Date2Num(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void DateFmt(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void IsoDate2Num(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void IsoTime2Num(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void LocalDateFmt(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void LocalTimeFmt(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Num2Date(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Num2GMTime(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Num2Time(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Time(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Time2Num(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void TimeFmt(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); + +- static ByteString Local2IsoDate(CFXJSE_Value* pThis, ++ static ByteString Local2IsoDate(CFXJSE_HostObject* pThis, + ByteStringView bsDate, + ByteStringView bsFormat, + ByteStringView bsLocale); +- static ByteString IsoDate2Local(CFXJSE_Value* pThis, ++ static ByteString IsoDate2Local(CFXJSE_HostObject* pThis, + ByteStringView bsDate, + ByteStringView bsFormat, + ByteStringView bsLocale); +- static ByteString IsoTime2Local(CFXJSE_Value* pThis, ++ static ByteString IsoTime2Local(CFXJSE_HostObject* pThis, + ByteStringView bsTime, + ByteStringView bsFormat, + ByteStringView bsLocale); +- static ByteString GetLocalDateFormat(CFXJSE_Value* pThis, ++ static ByteString GetLocalDateFormat(CFXJSE_HostObject* pThis, + int32_t iStyle, + ByteStringView bsLocale, + bool bStandard); +- static ByteString GetLocalTimeFormat(CFXJSE_Value* pThis, ++ static ByteString GetLocalTimeFormat(CFXJSE_HostObject* pThis, + int32_t iStyle, + ByteStringView bsLocale, + bool bStandard); +- static ByteString GetStandardDateFormat(CFXJSE_Value* pThis, ++ static ByteString GetStandardDateFormat(CFXJSE_HostObject* pThis, + int32_t iStyle, + ByteStringView bsLocale); +- static ByteString GetStandardTimeFormat(CFXJSE_Value* pThis, ++ static ByteString GetStandardTimeFormat(CFXJSE_HostObject* pThis, + int32_t iStyle, + ByteStringView bsLocale); +- static ByteString Num2AllTime(CFXJSE_Value* pThis, ++ static ByteString Num2AllTime(CFXJSE_HostObject* pThis, + int32_t iTime, + ByteStringView bsFormat, + ByteStringView bsLocale, + bool bGM); + +- static void Apr(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void CTerm(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void FV(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void IPmt(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void NPV(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Pmt(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void PPmt(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void PV(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Rate(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Term(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Choose(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Exists(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void HasValue(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Oneof(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Within(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void If(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Eval(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Ref(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void UnitType(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void UnitValue(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); ++ static void Apr(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void CTerm(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void FV(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void IPmt(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void NPV(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Pmt(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void PPmt(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void PV(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Rate(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Term(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Choose(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Exists(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void HasValue(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Oneof(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Within(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void If(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Eval(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Ref(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void UnitType(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void UnitValue(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); + +- static void At(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Concat(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Decode(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Encode(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Format(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Left(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Len(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Lower(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Ltrim(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Parse(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Replace(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Right(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Rtrim(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Space(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Str(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Stuff(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Substr(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Uuid(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Upper(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void WordNum(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); ++ static void At(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Concat(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Decode(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Encode(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Format(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Left(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Len(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Lower(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Ltrim(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Parse(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Replace(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Right(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Rtrim(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Space(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Str(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Stuff(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Substr(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Uuid(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Upper(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void WordNum(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); + +- static void Get(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Post(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void Put(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void assign_value_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void logical_or_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void logical_and_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void equality_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void notequality_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static bool fm_ref_equal(CFXJSE_Value* pThis, CFXJSE_Arguments& args); +- static void less_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void lessequal_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void greater_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void greaterequal_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void plus_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void minus_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void multiple_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void divide_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void positive_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void negative_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void logical_not_operator(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void dot_accessor(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void dotdot_accessor(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void eval_translation(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void is_fm_object(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void is_fm_array(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void get_fm_value(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void get_fm_jsobj(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void fm_var_filter(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); +- static void concat_fm_object(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args); ++ static void Get(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Post(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void Put(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void assign_value_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void logical_or_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void logical_and_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void equality_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void notequality_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static bool fm_ref_equal(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void less_operator(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void lessequal_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void greater_operator(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void greaterequal_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void plus_operator(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void minus_operator(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void multiple_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void divide_operator(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void positive_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void negative_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void logical_not_operator( ++ CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void dot_accessor(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void dotdot_accessor(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void eval_translation(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void is_fm_object(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void is_fm_array(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void get_fm_value(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void get_fm_jsobj(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void fm_var_filter(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++ static void concat_fm_object(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); + +- static int32_t hvalue_get_array_length(CFXJSE_Value* pThis, +- CFXJSE_Value* arg); +- static bool simpleValueCompare(CFXJSE_Value* pThis, +- CFXJSE_Value* firstValue, +- CFXJSE_Value* secondValue); +- static std::vector> unfoldArgs( +- CFXJSE_Value* pThis, +- CFXJSE_Arguments& args); +- static void GetObjectDefaultValue(CFXJSE_Value* pObjectValue, +- CFXJSE_Value* pDefaultValue); +- static bool SetObjectDefaultValue(CFXJSE_Value* pObjectValue, +- CFXJSE_Value* pNewValue); + static ByteString GenerateSomExpression(ByteStringView bsName, + int32_t iIndexFlags, + int32_t iIndexValue, + bool bIsStar); +- static bool GetObjectForName(CFXJSE_Value* pThis, +- CFXJSE_Value* accessorValue, +- ByteStringView bsAccessorName); +- static bool ResolveObjects(CFXJSE_Value* pThis, +- CFXJSE_Value* pParentValue, +- ByteStringView bsSomExp, +- XFA_RESOLVENODE_RS* resolveNodeRS, +- bool bdotAccessor, +- bool bHasNoResolveName); +- static void ParseResolveResult( +- CFXJSE_Value* pThis, +- const XFA_RESOLVENODE_RS& resolveNodeRS, +- CFXJSE_Value* pParentValue, +- std::vector>* resultValues, +- bool* bAttribute); +- +- static std::unique_ptr GetSimpleValue(CFXJSE_Value* pThis, +- CFXJSE_Arguments& args, +- uint32_t index); +- static bool ValueIsNull(CFXJSE_Value* pThis, CFXJSE_Value* pValue); +- static int32_t ValueToInteger(CFXJSE_Value* pThis, CFXJSE_Value* pValue); +- static float ValueToFloat(CFXJSE_Value* pThis, CFXJSE_Value* pValue); +- static double ValueToDouble(CFXJSE_Value* pThis, CFXJSE_Value* pValue); +- static ByteString ValueToUTF8String(CFXJSE_Value* pValue); +- static double ExtractDouble(CFXJSE_Value* pThis, +- CFXJSE_Value* src, +- bool* ret); ++ static absl::optional Translate(cppgc::Heap* pHeap, ++ WideStringView wsFormcalc); + +- static bool Translate(WideStringView wsFormcalc, +- CFX_WideTextBuf* wsJavascript); +- +- void GlobalPropertyGetter(CFXJSE_Value* pValue); ++ v8::Local GlobalPropertyGetter(); ++ v8::Isolate* GetIsolate() const { return m_pIsolate; } ++ CXFA_Document* GetDocument() const { return m_pDocument.Get(); } + + private: +- static void DotAccessorCommon(CFXJSE_Value* pThis, +- ByteStringView bsFuncName, +- CFXJSE_Arguments& args, ++ static void DotAccessorCommon(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info, + bool bDotAccessor); + +- v8::Isolate* GetScriptRuntime() const { return m_pIsolate.Get(); } +- CXFA_Document* GetDocument() const { return m_pDocument.Get(); } ++ bool ApplyToExpansion( ++ std::function)> fn, ++ const v8::FunctionCallbackInfo& info, ++ bool bStrict); ++ ++ bool ApplyToArray(v8::Isolate* pIsolate, ++ std::function)> fn, ++ v8::Local pArray); ++ ++ void ApplyToObject(v8::Isolate* pIsolate, ++ std::function)> fn, ++ v8::Local pObject); + ++ void ThrowArgumentMismatchException() const; + void ThrowNoDefaultPropertyException(ByteStringView name) const; + void ThrowCompilerErrorException() const; + void ThrowDivideByZeroException() const; + void ThrowServerDeniedException() const; +- void ThrowPropertyNotInObjectException(const WideString& name, +- const WideString& exp) const; +- void ThrowArgumentMismatchException() const; +- void ThrowParamCountMismatchException(const WideString& method) const; +- void ThrowException(const WideString& str) const; ++ void ThrowPropertyNotInObjectException(ByteStringView name, ++ ByteStringView exp) const; ++ void ThrowParamCountMismatchException(ByteStringView method) const; ++ void ThrowException(ByteStringView str) const; + +- UnownedPtr m_pIsolate; +- std::unique_ptr m_pValue; +- UnownedPtr const m_pDocument; ++ UnownedPtr const m_pIsolate; ++ v8::Global m_Value; ++ cppgc::WeakPersistent const m_pDocument; + }; + + #endif // FXJS_XFA_CFXJSE_FORMCALC_CONTEXT_H_ +diff --git a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp +index 19fc73012..1478bf920 100644 +--- a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp ++++ b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp +@@ -1,11 +1,17 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include ++ ++#include "fxjs/fxv8.h" + #include "fxjs/xfa/cfxjse_engine.h" ++#include "fxjs/xfa/cfxjse_isolatetracker.h" + #include "fxjs/xfa/cfxjse_value.h" + #include "testing/gtest/include/gtest/gtest.h" ++#include "testing/scoped_set_tz.h" + #include "testing/xfa_js_embedder_test.h" ++#include "third_party/base/cxx17_backports.h" + #include "xfa/fxfa/cxfa_eventparam.h" + + class CFXJSE_FormCalcContextEmbedderTest : public XFAJSEmbedderTest { +@@ -14,8 +20,85 @@ class CFXJSE_FormCalcContextEmbedderTest : public XFAJSEmbedderTest { + ~CFXJSE_FormCalcContextEmbedderTest() override = default; + + protected: +- bool ExecuteExpectNull(ByteStringView input) { +- return Execute(input) && GetValue()->IsNull(); ++ CFXJSE_Context* GetJseContext() { ++ return GetScriptContext()->GetJseContextForTest(); ++ } ++ ++ void ExecuteExpectError(ByteStringView input) { ++ EXPECT_FALSE(Execute(input)) << "Program: " << input; ++ } ++ ++ void ExecuteExpectNull(ByteStringView input) { ++ EXPECT_TRUE(Execute(input)) << "Program: " << input; ++ ++ CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext()); ++ EXPECT_TRUE(fxv8::IsNull(GetValue())) << "Program: " << input; ++ } ++ ++ void ExecuteExpectBool(ByteStringView input, bool expected) { ++ EXPECT_TRUE(Execute(input)) << "Program: " << input; ++ ++ CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext()); ++ v8::Local value = GetValue(); ++ ++ // Yes, bools might be integers, somehow. ++ EXPECT_TRUE(fxv8::IsBoolean(value) || fxv8::IsInteger(value)) ++ << "Program: " << input; ++ EXPECT_EQ(expected, fxv8::ReentrantToBooleanHelper(isolate(), value)) ++ << "Program: " << input; ++ } ++ ++ void ExecuteExpectInt32(ByteStringView input, int32_t expected) { ++ EXPECT_TRUE(Execute(input)) << "Program: " << input; ++ ++ CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext()); ++ v8::Local value = GetValue(); ++ EXPECT_TRUE(fxv8::IsInteger(value)) << "Program: " << input; ++ EXPECT_EQ(expected, fxv8::ReentrantToInt32Helper(isolate(), value)) ++ << "Program: " << input; ++ } ++ ++ void ExecuteExpectFloat(ByteStringView input, float expected) { ++ EXPECT_TRUE(Execute(input)) << "Program: " << input; ++ ++ CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext()); ++ v8::Local value = GetValue(); ++ EXPECT_TRUE(fxv8::IsNumber(value)); ++ EXPECT_FLOAT_EQ(expected, fxv8::ReentrantToFloatHelper(isolate(), value)) ++ << "Program: " << input; ++ } ++ ++ void ExecuteExpectFloatNear(ByteStringView input, float expected) { ++ constexpr float kPrecision = 0.000001f; ++ ++ EXPECT_TRUE(Execute(input)) << "Program: " << input; ++ ++ CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext()); ++ v8::Local value = GetValue(); ++ EXPECT_TRUE(fxv8::IsNumber(value)); ++ EXPECT_NEAR(expected, fxv8::ReentrantToFloatHelper(isolate(), value), ++ kPrecision) ++ << "Program: " << input; ++ } ++ ++ void ExecuteExpectNaN(ByteStringView input) { ++ EXPECT_TRUE(Execute(input)) << "Program: " << input; ++ ++ CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext()); ++ v8::Local value = GetValue(); ++ EXPECT_TRUE(fxv8::IsNumber(value)); ++ EXPECT_TRUE(isnan(fxv8::ReentrantToDoubleHelper(isolate(), value))); ++ } ++ ++ void ExecuteExpectString(ByteStringView input, const char* expected) { ++ EXPECT_TRUE(Execute(input)) << "Program: " << input; ++ ++ CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext()); ++ v8::Local value = GetValue(); ++ EXPECT_TRUE(fxv8::IsString(value)); ++ EXPECT_STREQ(expected, ++ fxv8::ReentrantToByteStringHelper(isolate(), value).c_str()) ++ << "Program: " << input; + } + }; + +@@ -32,309 +115,167 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, TranslateEmpty) { + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, TranslateNumber) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); +- +- const char input[] = "123"; +- EXPECT_TRUE(Execute(input)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(123, value->ToInteger()) << "Program: " << input; ++ ExecuteExpectInt32("123", 123); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Numeric) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- int result; +- } tests[] = {{"123 + 456", 579}, +- {"2 - 3 * 10 / 2 + 7", -6}, +- {"10 * 3 + 5 * 4", 50}, +- {"(5 - \"abc\") * 3", 15}, +- {"\"100\" / 10e1", 1}, +- {"5 + null + 3", 8}, +- // {"if (\"abc\") then\n" +- // " 10\n" +- // "else\n" +- // " 20\n" +- // "endif", +- // 20}, +- // {"3 / 0 + 1", 0}, +- {"-(17)", -17}, +- {"-(-17)", 17}, +- {"+(17)", 17}, +- {"+(-17)", -17}, +- {"if (1 < 2) then\n1\nendif", 1}, +- {"if (\"abc\" > \"def\") then\n" +- " 1 and 0\n" +- "else\n" +- " 0\n" +- "endif", +- 0}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(tests[i].result, value->ToInteger()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectInt32("123 + 456", 579); ++ ExecuteExpectInt32("2 - 3 * 10 / 2 + 7", -6); ++ ExecuteExpectInt32("10 * 3 + 5 * 4", 50); ++ ExecuteExpectInt32("(5 - \"abc\") * 3", 15); ++ ExecuteExpectInt32("\"100\" / 10e1", 1); ++ ExecuteExpectInt32("5 + null + 3", 8); ++#if 0 ++ // TODO(thestig): Investigate these cases. ++ ExecuteExpectInt32( ++ "if (\"abc\") then\n" ++ " 10\n" ++ "else\n" ++ " 20\n" ++ "endif", ++ 20); ++ ExecuteExpectInt32("3 / 0 + 1", 0); ++#endif ++ ExecuteExpectInt32("-(17)", -17); ++ ExecuteExpectInt32("-(-17)", 17); ++ ExecuteExpectInt32("+(17)", 17); ++ ExecuteExpectInt32("+(-17)", -17); ++ ExecuteExpectInt32("if (1 < 2) then\n1\nendif", 1); ++ ExecuteExpectInt32( ++ "if (\"abc\" > \"def\") then\n" ++ " 1 and 0\n" ++ "else\n" ++ " 0\n" ++ "endif", ++ 0); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Strings) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = { +- {"\"abc\"", "abc"}, +- {"concat(\"The total is \", 2, \" dollars and \", 57, \" cents.\")", +- "The total is 2 dollars and 57 cents."}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("\"abc\"", "abc"); ++ ExecuteExpectString( ++ "concat(\"The total is \", 2, \" dollars and \", 57, \" cents.\")", ++ "The total is 2 dollars and 57 cents."); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Booleans) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- bool result; +- } tests[] = {{"0 and 1 or 2 > 1", true}, +- {"2 < 3 not 1 == 1", false}, +- {"\"abc\" | 2", true}, +- {"1 or 0", true}, +- {"0 | 0", false}, +- {"0 or 1 | 0 or 0", true}, +- {"1 and 0", false}, +- // {"0 & 0", true}, // TODO(dsinclair) Confirm with Reader. +- {"0 and 1 & 0 and 0", false}, +- {"not(\"true\")", true}, +- {"not(1)", false}, +- {"3 == 3", true}, +- {"3 <> 4", true}, +- {"\"abc\" eq \"def\"", false}, +- {"\"def\" ne \"abc\"", true}, +- {"5 + 5 == 10", true}, +- {"5 + 5 <> \"10\"", false}, +- {"3 < 3", false}, +- {"3 > 4", false}, +- {"\"abc\" <= \"def\"", true}, +- {"\"def\" > \"abc\"", true}, +- {"12 >= 12", true}, +- {"\"true\" < \"false\"", false}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program; +- EXPECT_EQ(tests[i].result, value->ToBoolean()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectBool("0 and 1 or 2 > 1", true); ++ ExecuteExpectBool("2 < 3 not 1 == 1", false); ++ ExecuteExpectBool("\"abc\" | 2", true); ++ ExecuteExpectBool("1 or 0", true); ++ ExecuteExpectBool("0 | 0", false); ++ ExecuteExpectBool("0 or 1 | 0 or 0", true); ++ ExecuteExpectBool("1 and 0", false); ++ ExecuteExpectBool("0 and 1 & 0 and 0", false); ++ ExecuteExpectBool("not(\"true\")", true); ++ ExecuteExpectBool("not(1)", false); ++ ExecuteExpectBool("3 == 3", true); ++ ExecuteExpectBool("3 <> 4", true); ++ ExecuteExpectBool("\"abc\" eq \"def\"", false); ++ ExecuteExpectBool("\"def\" ne \"abc\"", true); ++ ExecuteExpectBool("5 + 5 == 10", true); ++ ExecuteExpectBool("5 + 5 <> \"10\"", false); ++ ExecuteExpectBool("3 < 3", false); ++ ExecuteExpectBool("3 > 4", false); ++ ExecuteExpectBool("\"abc\" <= \"def\"", true); ++ ExecuteExpectBool("\"def\" > \"abc\"", true); ++ ExecuteExpectBool("12 >= 12", true); ++ ExecuteExpectBool("\"true\" < \"false\"", false); ++#if 0 ++ // TODO(thestig): Investigate this case. ++ // Confirm with Reader. ++ ExecuteExpectBool("0 & 0", true); ++#endif + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Abs) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- float result; +- } tests[] = {{"Abs(1.03)", 1.03f}, {"Abs(-1.03)", 1.03f}, {"Abs(0)", 0.0f}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsNumber()); +- EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectFloat("Abs(1.03)", 1.03f); ++ ExecuteExpectFloat("Abs(-1.03)", 1.03f); ++ ExecuteExpectFloat("Abs(0)", 0.0f); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Avg) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- float result; +- } tests[] = {{"Avg(0, 32, 16)", 16.0f}, {"Avg(2.5, 17, null)", 9.75f}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsNumber()); +- EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectFloat("Avg(0, 32, 16)", 16.0f); ++ ExecuteExpectFloat("Avg(2.5, 17, null)", 9.75f); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Ceil) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- int result; +- } tests[] = {{"Ceil(2.5875)", 3}, {"Ceil(-5.9)", -5}, {"Ceil(\"abc\")", 0}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(tests[i].result, value->ToInteger()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectInt32("Ceil(2.5875)", 3); ++ ExecuteExpectInt32("Ceil(-5.9)", -5); ++ ExecuteExpectInt32("Ceil(\"abc\")", 0); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Count) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- int result; +- } tests[] = {{"Count(\"Tony\", \"Blue\", 41)", 3}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(tests[i].result, value->ToInteger()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectInt32("Count(\"Tony\", \"Blue\", 41)", 3); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Floor) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- int result; +- } tests[] = {{"Floor(21.3409873)", 21}, +- {"Floor(5.999965342)", 5}, +- {"Floor(3.2 * 15)", 48}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(tests[i].result, value->ToInteger()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectInt32("Floor(21.3409873)", 21); ++ ExecuteExpectInt32("Floor(5.999965342)", 5); ++ ExecuteExpectInt32("Floor(3.2 * 15)", 48); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Max) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- int result; +- } tests[] = {{"Max(234, 15, 107)", 234}, +- {"Max(\"abc\", 15, \"Tony Blue\")", 15}, +- {"Max(\"abc\")", 0}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(tests[i].result, value->ToInteger()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectInt32("Max(234, 15, 107)", 234); ++ ExecuteExpectInt32("Max(\"abc\", 15, \"Tony Blue\")", 15); ++ ExecuteExpectInt32("Max(\"abc\")", 0); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Min) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- int result; +- } tests[] = {{"Min(234, 15, 107)", 15}, +- // TODO(dsinclair): Verify with Reader; I believe this should +- // have a return of 0. +- // {"Min(\"abc\", 15, \"Tony Blue\")", 15}, +- {"Min(\"abc\")", 0}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(tests[i].result, value->ToInteger()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectInt32("Min(234, 15, 107)", 15); ++#if 0 ++ // TODO(thestig): Investigate these cases. ++ // Verify with Reader; This should have a return value of 0. ++ ExecuteExpectInt32("Min(\"abc\", 15, \"Tony Blue\")", 15); ++#endif ++ ExecuteExpectInt32("Min(\"abc\")", 0); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Mod) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- int result; +- } tests[] = {{"Mod(64, -3)", 1}, {"Mod(-13, 3)", -1}, {"Mod(\"abc\", 2)", 0}}; ++ ExecuteExpectInt32("Mod(64, -3)", 1); ++ ExecuteExpectInt32("Mod(-13, 3)", -1); ++ ExecuteExpectInt32("Mod(\"abc\", 2)", 0); + +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(tests[i].result, value->ToInteger()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectNaN("Mod(10, NaN)"); ++ ExecuteExpectNaN("Mod(10, Infinity)"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Round) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- float result; +- } tests[] = {{"Round(12.389764537, 4)", 12.3898f}, +- {"Round(20/3, 2)", 6.67f}, +- {"Round(8.9897, \"abc\")", 9.0f}, +- {"Round(FV(400, 0.10/12, 30*12), 2)", 904195.17f}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsNumber()) << "Program: " << tests[i].program; +- EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectFloat("Round(12.389764537, 4)", 12.3898f); ++ ExecuteExpectFloat("Round(20/3, 2)", 6.67f); ++ ExecuteExpectFloat("Round(8.9897, \"abc\")", 9.0f); ++ ExecuteExpectFloat("Round(FV(400, 0.10/12, 30*12), 2)", 904195.17f); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Sum) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- int result; +- } tests[] = {{"Sum(2, 4, 6, 8)", 20}, +- {"Sum(-2, 4, -6, 8)", 4}, +- {"Sum(4, 16, \"abc\", 19)", 39}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(tests[i].result, value->ToInteger()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectInt32("Sum(2, 4, 6, 8)", 20); ++ ExecuteExpectInt32("Sum(-2, 4, -6, 8)", 4); ++ ExecuteExpectInt32("Sum(4, 16, \"abc\", 19)", 39); + } + + // TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Date) { +@@ -346,7 +287,7 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, Sum) { + + // EXPECT_TRUE(Execute("Date()")); + +-// CFXJSE_Value* value = GetValue(); ++// v8::Local value = GetValue(); + // EXPECT_TRUE(value->IsNumber()); + // EXPECT_EQ(days, value->ToInteger()); + // } +@@ -354,203 +295,100 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, Sum) { + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Date2Num) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- int result; +- } tests[] = { +- // {"Date2Num(\"Mar 15, 1996\")", 35138}, +- {"Date2Num(\"1/1/1900\", \"D/M/YYYY\")", 1}, +- {"Date2Num(\"03/15/96\", \"MM/DD/YY\")", 35138}, +- // {"Date2Num(\"Aug 1, 1996\", \"MMM D, YYYY\")", 35277}, +- {"Date2Num(\"96-08-20\", \"YY-MM-DD\", \"fr_FR\")", 35296}, +- {"Date2Num(\"1/3/00\", \"D/M/YY\") - Date2Num(\"1/2/00\", \"D/M/YY\")", +- 29}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(tests[i].result, value->ToInteger()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectInt32("Date2Num(\"1/1/1900\", \"D/M/YYYY\")", 1); ++ ExecuteExpectInt32("Date2Num(\"03/15/96\", \"MM/DD/YY\")", 35138); ++ ExecuteExpectInt32("Date2Num(\"96-08-20\", \"YY-MM-DD\", \"fr_FR\")", 35296); ++ ExecuteExpectInt32( ++ "Date2Num(\"1/3/00\", \"D/M/YY\") - Date2Num(\"1/2/00\", \"D/M/YY\")", ++ 29); ++#if 0 ++ // TODO(thestig): Investigate these cases. ++ ExecuteExpectInt32("Date2Num(\"Mar 15, 1996\")", 35138); ++ ExecuteExpectInt32("Date2Num(\"Aug 1, 1996\", \"MMM D, YYYY\")", 35277); ++#endif + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, DateFmt) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = { +- // {"DateFmt(1)", "M/D/YY"}, +- // {"DateFmt(2, \"fr_CA\")", "YY-MM-DD"}, +- {"DateFmt(3, \"de_DE\")", "D. MMMM YYYY"}, +- // {"DateFmt(4, \"fr_FR\")", "EEE D' MMMM YYYY"} +- }; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("DateFmt(3, \"de_DE\")", "D. MMMM YYYY"); ++#if 0 ++ // TODO(thestig): Investigate these cases. ++ ExecuteExpectString("DateFmt(1)", "M/D/YY"); ++ ExecuteExpectString("DateFmt(2, \"fr_CA\")", "YY-MM-DD"); ++ ExecuteExpectString("DateFmt(4, \"fr_FR\")", "EEE D' MMMM YYYY"); ++#endif + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, IsoDate2Num) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- int result; +- } tests[] = {{"IsoDate2Num(\"1900\")", 1}, +- {"IsoDate2Num(\"1900-01\")", 1}, +- {"IsoDate2Num(\"1900-01-01\")", 1}, +- {"IsoDate2Num(\"19960315T20:20:20\")", 35138}, +- {"IsoDate2Num(\"2000-03-01\") - IsoDate2Num(\"20000201\")", 29}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(tests[i].result, value->ToInteger()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectInt32("IsoDate2Num(\"1900\")", 1); ++ ExecuteExpectInt32("IsoDate2Num(\"1900-01\")", 1); ++ ExecuteExpectInt32("IsoDate2Num(\"1900-01-01\")", 1); ++ ExecuteExpectInt32("IsoDate2Num(\"19960315T20:20:20\")", 35138); ++ ExecuteExpectInt32("IsoDate2Num(\"2000-03-01\") - IsoDate2Num(\"20000201\")", ++ 29); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_IsoTime2Num) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- int result; +- } tests[] = {{"IsoTime2Num(\"00:00:00Z\")", 1}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(tests[i].result, value->ToInteger()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectInt32("IsoTime2Num(\"00:00:00Z\")", 1); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, LocalDateFmt) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {// {"LocalDateFmt(1, \"de_DE\")", "tt.MM.uu"}, +- // {"LocalDateFmt(2, \"fr_CA\")", "aa-MM-jj"}, +- {"LocalDateFmt(3, \"de_CH\")", "t. MMMM jjjj"}, +- {"LocalDateFmt(4, \"fr_FR\")", "EEEE j MMMM aaaa"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("LocalDateFmt(3, \"de_CH\")", "t. MMMM jjjj"); ++ ExecuteExpectString("LocalDateFmt(4, \"fr_FR\")", "EEEE j MMMM aaaa"); ++#if 0 ++ // TODO(thestig): Investigate these cases. ++ ExecuteExpectString("LocalDateFmt(1, \"de_DE\")", "tt.MM.uu"); ++ ExecuteExpectString("LocalDateFmt(2, \"fr_CA\")", "aa-MM-jj"); ++#endif + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_LocalTimeFmt) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"LocalTimeFmt(1, \"de_DE\")", "HH:mm"}, +- {"LocalTimeFmt(2, \"fr_CA\")", "HH:mm::ss"}, +- {"LocalTimeFmt(3, \"de_CH\")", "HH:mm:ss z"}, +- {"LocalTimeFmt(4, \"fr_FR\")", "HH' h 'mm z"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("LocalTimeFmt(1, \"de_DE\")", "HH:mm"); ++ ExecuteExpectString("LocalTimeFmt(2, \"fr_CA\")", "HH:mm::ss"); ++ ExecuteExpectString("LocalTimeFmt(3, \"de_CH\")", "HH:mm:ss z"); ++ ExecuteExpectString("LocalTimeFmt(4, \"fr_FR\")", "HH' h 'mm z"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Num2Date) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = { +- {"Num2Date(1, \"DD/MM/YYYY\")", "01/01/1900"}, +- {"Num2Date(35139, \"DD-MMM-YYYY\", \"de_DE\")", "16-Mrz-1996"}, +- // {"Num2Date(Date2Num(\"Mar 15, 2000\") - Date2Num(\"98-03-15\", " +- // "\"YY-MM-DD\", \"fr_CA\"))", +- // "Jan 1, 1902"} +- }; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()) << "Program: " << tests[i].program; +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("Num2Date(1, \"DD/MM/YYYY\")", "01/01/1900"); ++ ExecuteExpectString("Num2Date(35139, \"DD-MMM-YYYY\", \"de_DE\")", ++ "16-Mrz-1996"); ++#if 0 ++ // TODO(thestig): Investigate this case. ++ ExecuteExpectString( ++ "Num2Date(Date2Num(\"Mar 15, 2000\") - Date2Num(\"98-03-15\", " ++ "\"YY-MM-DD\", \"fr_CA\"))", ++ "Jan 1, 1902"); ++#endif + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Num2GMTime) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {// Broken on Windows only. +- {"Num2GMTime(1, \"HH:MM:SS\")", "00:00:00"}, +- // Below broken on other platforms. +- {"Num2GMTime(65593001, \"HH:MM:SS Z\")", "18:13:13 GMT"}, +- {"Num2GMTime(43993001, TimeFmt(4, \"de_DE\"), \"de_DE\")", +- "12.13 Uhr GMT"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ // Broken on Windows only. ++ ExecuteExpectString("Num2GMTime(1, \"HH:MM:SS\")", "00:00:00"); ++ // Below broken on other platforms. ++ ExecuteExpectString("Num2GMTime(65593001, \"HH:MM:SS Z\")", "18:13:13 GMT"); ++ ExecuteExpectString("Num2GMTime(43993001, TimeFmt(4, \"de_DE\"), \"de_DE\")", ++ "12.13 Uhr GMT"); + } + + // TODO(dsinclair): Broken on Mac ... + TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Num2Time) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"Num2Time(1, \"HH:MM:SS\")", "00:00:00"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("Num2Time(1, \"HH:MM:SS\")", "00:00:00"); + } + + // TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Time) { +@@ -561,7 +399,7 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Num2Time) { + + // EXPECT_TRUE(Execute("Time()")); + +-// CFXJSE_Value* value = GetValue(); ++// v8::Local value = GetValue(); + // EXPECT_TRUE(value->IsInteger()); + // EXPECT_EQ(tp.tv_sec * 1000L + tp.tv_usec / 1000, value->ToInteger()) + // << "Program: Time()"; +@@ -570,653 +408,352 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Num2Time) { + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Time2Num) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- int result; +- } tests[] = { +- // {"Time2Num(\"00:00:00 GMT\", \"HH:MM:SS Z\")", 1}, +- {"Time2Num(\"13:13:13 GMT\", \"HH:MM:SS Z\", \"fr_FR\")", 47593001}}; ++ ExecuteExpectInt32("Time2Num(\"00:00:00 GMT\", \"HH:MM:SS Z\")", 1); ++ ExecuteExpectInt32("Time2Num(\"00:00:01 GMT\", \"HH:MM:SS Z\")", 1001); ++ ExecuteExpectInt32("Time2Num(\"00:01:00 GMT\", \"HH:MM:SS Z\")", 60001); ++ ExecuteExpectInt32("Time2Num(\"01:00:00 GMT\", \"HH:MM:SS Z\")", 3600001); ++ ExecuteExpectInt32("Time2Num(\"23:59:59 GMT\", \"HH:MM:SS Z\")", 86399001); ++ // https://crbug.com/pdfium/1257 ++ ExecuteExpectInt32("Time2Num(\"\", \"\", 1)", 0); ++ ExecuteExpectInt32("Time2Num(\"13:13:13 GMT\", \"HH:MM:SS Z\", \"fr_FR\")", ++ 47593001); ++} + +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); ++TEST_F(CFXJSE_FormCalcContextEmbedderTest, Time2NumWithTZ) { ++ ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(tests[i].result, value->ToInteger()) +- << "Program: " << tests[i].program; ++ static constexpr const char* kTimeZones[] = { ++ "UTC+14", "UTC-14", "UTC+9:30", "UTC-0:30", ++ "UTC+0:30", "UTC-0:01", "UTC+0:01"}; ++ for (const char* tz : kTimeZones) { ++ ScopedSetTZ scoped_set_tz(tz); ++ ExecuteExpectInt32("Time2Num(\"00:00:00 GMT\", \"HH:MM:SS Z\")", 1); ++ ExecuteExpectInt32("Time2Num(\"11:59:59 GMT\", \"HH:MM:SS Z\")", 43199001); ++ ExecuteExpectInt32("Time2Num(\"12:00:00 GMT\", \"HH:MM:SS Z\")", 43200001); ++ ExecuteExpectInt32("Time2Num(\"23:59:59 GMT\", \"HH:MM:SS Z\")", 86399001); ++ } ++ { ++ ScopedSetTZ scoped_set_tz("UTC-3:00"); ++ ExecuteExpectInt32("Time2Num(\"1:13:13 PM\")", 36793001); ++ ExecuteExpectInt32( ++ "Time2Num(\"13:13:13 GMT\", \"HH:MM:SS Z\") - " ++ "Time2Num(\"13:13:13\", \"HH:MM:SS\")", ++ 10800000); + } + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, TimeFmt) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = { +- // {"TimeFmt(1)", "h::MM A"}, +- {"TimeFmt(2, \"fr_CA\")", "HH:MM:SS"}, +- {"TimeFmt(3, \"fr_FR\")", "HH:MM:SS Z"}, +- // {"TimeFmt(4, \"de_DE\")", "H.MM' Uhr 'Z"} +- }; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("TimeFmt(2, \"fr_CA\")", "HH:MM:SS"); ++ ExecuteExpectString("TimeFmt(3, \"fr_FR\")", "HH:MM:SS Z"); ++#if 0 ++ // TODO(thestig): Investigate these cases. ++ ExecuteExpectString("TimeFmt(1)", "h::MM A"); ++ ExecuteExpectString("TimeFmt(4, \"de_DE\")", "H.MM' Uhr 'Z"); ++#endif + } + +-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Apr) { ++TEST_F(CFXJSE_FormCalcContextEmbedderTest, Apr) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- float result; +- } tests[] = {{"Apr(35000, 269.50, 360)", 0.08515404566f}, +- {"Apr(210000 * 0.75, 850 + 110, 25 * 26)", 0.07161332404f}}; ++ ExecuteExpectFloatNear("Apr(35000, 269.50, 360)", 0.08515404566f); ++ ExecuteExpectFloatNear("Apr(210000 * 0.75, 850 + 110, 25 * 26)", ++ 0.07161332404f); + +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsNumber()); +- EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectError("Apr(2, 2, 2147483648)"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, CTerm) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- float result; +- } tests[] = { +- // {"CTerm(0.02, 1000, 100)", 116.2767474515f}, +- {"CTerm(0.10, 500000, 12000)", 39.13224648502f}, +- // {"CTerm(0.0275 + 0.0025, 1000000, 55000 * 0.10)", 176.02226044975f} +- }; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsNumber()); +- EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectFloat("CTerm(0.10, 500000, 12000)", 39.13224648502f); ++#if 0 ++ // TODO(thestig): Investigate these cases. ++ ExecuteExpectFloat("CTerm(0.02, 1000, 100)", 116.2767474515f); ++ ExecuteExpectFloat("CTerm(0.0275 + 0.0025, 1000000, 55000 * 0.10)", ++ 176.02226044975f); ++#endif + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, FV) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- float result; +- } tests[] = {{"FV(400, 0.10 / 12, 30 * 12)", 904195.16991842445f}, +- {"FV(1000, 0.075 / 4, 10 * 4)", 58791.96145535981f}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); ++ ExecuteExpectFloat("FV(400, 0.10 / 12, 30 * 12)", 904195.16991842445f); ++ ExecuteExpectFloat("FV(1000, 0.075 / 4, 10 * 4)", 58791.96145535981f); + +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsNumber()); +- EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectError("FV(2, 2, 2147483648)"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, IPmt) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- float result; +- } tests[] = {{"IPmt(30000, 0.085, 295.50, 7, 3)", 624.8839283142f}, +- {"IPmt(160000, 0.0475, 980, 24, 12)", 7103.80833569485f}, +- {"IPmt(15000, 0.065, 65.50, 15, 1)", 0.0f}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsNumber()); +- EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectFloat("IPmt(30000, 0.085, 295.50, 7, 3)", 624.8839283142f); ++ ExecuteExpectFloat("IPmt(160000, 0.0475, 980, 24, 12)", 7103.80833569485f); ++ ExecuteExpectFloat("IPmt(15000, 0.065, 65.50, 15, 1)", 0.0f); + } + +-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_NPV) { ++TEST_F(CFXJSE_FormCalcContextEmbedderTest, NPV) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- float result; +- } tests[] = {{"NPV(0.065, 5000)", 4694.83568075117f}, +- {"NPV(0.10, 500, 1500, 4000, 10000)", 11529.60863329007f}, +- {"NPV(0.0275 / 12, 50, 60, 40, 100, 25)", 273.14193838457f}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsNumber()) << "Program: " << tests[i].program; +- EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectFloat("NPV(0.065, 5000)", 4694.83568075117f); ++ ExecuteExpectFloat("NPV(0.10, 500, 1500, 4000, 10000)", 11529.60863329007f); ++ ExecuteExpectFloat("NPV(0.0275 / 12, 50, 60, 40, 100, 25)", 273.14193838457f); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Pmt) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- float result; +- } tests[] = {// {"Pmt(150000, 0.0475 / 12, 25 * 12)", 855.17604207164f}, +- {"Pmt(25000, 0.085, 12)", 3403.82145169876f}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); ++ ExecuteExpectFloat("Pmt(25000, 0.085, 12)", 3403.82145169876f); ++ ExecuteExpectFloat("Pmt(5000, 0.01, 1)", 5050); ++ ExecuteExpectFloat("Pmt(5000, 0.01, 1.5)", 5050); ++ ExecuteExpectFloat("Pmt(30000.00, .085 / 12, 12 * 12)", 333.01666929435f); ++ ExecuteExpectFloat("Pmt(10000, .08 / 12, 10)", 1037.03208935916f); ++ ExecuteExpectFloat("Pmt(150000, 0.0475 / 12, 25 * 12)", 855.17604207164f); + +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsNumber()); +- EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat()) +- << "Program: " << tests[i].program; +- } ++ // https://crbug.com/1293179 ++ ExecuteExpectError("Pmt(2, 2, 99999997952)"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, PPmt) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- float result; +- } tests[] = { +- {"PPmt(30000, 0.085, 295.50, 7, 3)", 261.6160716858f}, +- {"PPmt(160000, 0.0475, 980, 24, 12)", 4656.19166430515f}, +- // {"PPmt(15000, 0.065, 65.50, 15, 1)", 0.0f} +- }; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsNumber()); +- EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectFloat("PPmt(30000, 0.085, 295.50, 7, 3)", 261.6160716858f); ++ ExecuteExpectFloat("PPmt(160000, 0.0475, 980, 24, 12)", 4656.19166430515f); ++#if 0 ++ // TODO(thestig): Investigate this case. ++ ExecuteExpectFloat("PPmt(15000, 0.065, 65.50, 15, 1)", 0.0f); ++#endif + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, PV) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- float result; +- } tests[] = { +- {"PV(400, 0.10 / 12, 30 * 12)", 45580.32799074439f}, +- // {"PV(1000, 0.075 / 4, 10 * 4)", 58791.96145535981f} +- }; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); ++ ExecuteExpectFloat("PV(400, 0.10 / 12, 30 * 12)", 45580.32799074439f); ++ ExecuteExpectFloat("PV(1000, 0.075 / 4, 10 * 4)", 27964.88770467326f); + +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsNumber()); +- EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat()) +- << "Program: " << tests[i].program; +- } ++ // https://crbug.com/1296840 ++ ExecuteExpectError("PV(2, 2, 2147483648)"); + } + +-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Rate) { ++TEST_F(CFXJSE_FormCalcContextEmbedderTest, Rate) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- float result; +- } tests[] = {{"Rate(12000, 8000, 5)", 0.0844717712f}, +- {"Rate(10000, 0.25 * 5000, 4 * 12)", 0.04427378243f}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); ++ ExecuteExpectFloatNear("Rate(12000, 8000, 5)", 0.0844717712f); ++ ExecuteExpectFloatNear("Rate(10000, 0.25 * 5000, 4 * 12)", 0.04427378243f); + +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsNumber()); +- EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectError("Rate(2, 2, 2147483648)"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Term) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- float result; +- } tests[] = {// {"Term(475, .05, 1500)", 3.00477517728f}, +- {"Term(2500, 0.0275 + 0.0025, 5000)", 1.97128786369f}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsNumber()); +- EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectFloat("Term(2500, 0.0275 + 0.0025, 5000)", 1.97128786369f); ++#if 0 ++ // TODO(thestig): Investigate this case. ++ ExecuteExpectFloat("Term(475, .05, 1500)", 3.00477517728f); ++#endif + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Choose) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = { +- {"Choose(3, \"Taxes\", \"Price\", \"Person\", \"Teller\")", "Person"}, +- {"Choose(2, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)", "9"}, +- {"Choose(20/3, \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\")", +- "F"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("Choose(3, \"Taxes\", \"Price\", \"Person\", \"Teller\")", ++ "Person"); ++ ExecuteExpectString("Choose(2, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)", "9"); ++ ExecuteExpectString( ++ "Choose(20/3, \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\")", ++ "F"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Exists) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); +- +- EXPECT_TRUE(Execute("Exists(\"hello world\")")); +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_FALSE(value->ToBoolean()); ++ ExecuteExpectBool("Exists(\"hello world\")", false); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, HasValue) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- bool result; +- } tests[] = {{"HasValue(2)", true}, {"HasValue(\" \")", false}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program; +- EXPECT_EQ(tests[i].result, value->ToBoolean()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectBool("HasValue(2)", true); ++ ExecuteExpectBool("HasValue(\" \")", false); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Oneof) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- bool result; +- } tests[] = { +- {"Oneof(3, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)", true}, +- {"Oneof(\"John\", \"Bill\", \"Gary\", \"Joan\", \"John\", \"Lisa\")", +- true}, +- {"Oneof(3, 1, 25)", false}, +- {"Oneof(3, 3, null)", true}, +- {"Oneof(3, null, null)", false}, +- }; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program; +- EXPECT_EQ(tests[i].result, value->ToBoolean()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectBool("Oneof(3, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)", true); ++ ExecuteExpectBool( ++ "Oneof(\"John\", \"Bill\", \"Gary\", \"Joan\", \"John\", \"Lisa\")", ++ true); ++ ExecuteExpectBool("Oneof(3, 1, 25)", false); ++ ExecuteExpectBool("Oneof(3, 3, null)", true); ++ ExecuteExpectBool("Oneof(3, null, null)", false); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Within) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- bool result; +- } tests[] = {{"Within(\"C\", \"A\", \"D\")", true}, +- {"Within(1.5, 0, 2)", true}, +- {"Within(-1, 0, 2)", false}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()) << "Program: " << tests[i].program; +- EXPECT_EQ(tests[i].result, value->ToBoolean()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectBool("Within(\"C\", \"A\", \"D\")", true); ++ ExecuteExpectBool("Within(1.5, 0, 2)", true); ++ ExecuteExpectBool("Within(-1, 0, 2)", false); + } + +-TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Eval) { ++TEST_F(CFXJSE_FormCalcContextEmbedderTest, Eval) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- int result; +- } tests[] = {{"eval(\"10*3+5*4\")", 50}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(tests[i].result, value->ToInteger()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectInt32("eval(\"10*3+5*4\")", 50); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Null) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"Null()", "null"}, +- {"Concat(\"ABC\", Null(), \"DEF\")", "ABCDEF"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } +- +- EXPECT_TRUE(Execute("Null() + 5")); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(5, value->ToInteger()); ++ ExecuteExpectString("Null()", "null"); ++ ExecuteExpectString("Concat(\"ABC\", Null(), \"DEF\")", "ABCDEF"); ++ ExecuteExpectInt32("Null() + 5", 5); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Ref) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"Ref(\"10*3+5*4\")", "10*3+5*4"}, {"Ref(\"hello\")", "hello"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("Ref(\"10*3+5*4\")", "10*3+5*4"); ++ ExecuteExpectString("Ref(\"hello\")", "hello"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, UnitType) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"UnitType(\"36 in\")", "in"}, +- {"UnitType(\"2.54centimeters\")", "cm"}, +- {"UnitType(\"picas\")", "pt"}, +- {"UnitType(\"2.cm\")", "cm"}, +- {"UnitType(\"2.zero cm\")", "in"}, +- {"UnitType(\"kilometers\")", "in"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("UnitType(\"36 in\")", "in"); ++ ExecuteExpectString("UnitType(\"2.54centimeters\")", "cm"); ++ ExecuteExpectString("UnitType(\"picas\")", "pt"); ++ ExecuteExpectString("UnitType(\"2.cm\")", "cm"); ++ ExecuteExpectString("UnitType(\"2.zero cm\")", "in"); ++ ExecuteExpectString("UnitType(\"kilometers\")", "in"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, UnitValue) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- float result; +- } tests[] = { +- {"UnitValue(\"2in\")", 2.0f}, {"UnitValue(\"2in\", \"cm\")", 5.08f}, +- // {"UnitValue(\"6\", \"pt\")", 432f}, +- // {"UnitType(\"A\", \"cm\")", 0.0f}, +- // {"UnitType(\"5.08cm\", \"kilograms\")", 2.0f} +- }; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsNumber()); +- EXPECT_FLOAT_EQ(tests[i].result, value->ToFloat()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectFloat("UnitValue(\"2in\")", 2.0f); ++ ExecuteExpectFloat("UnitValue(\"2in\", \"cm\")", 5.08f); ++#if 0 ++ // TODO(thestig): Investigate these cases. ++ // Should the UnitType cases move into the UnitType test case? ++ ExecuteExpectFloat("UnitValue(\"6\", \"pt\")", 432f); ++ ExecuteExpectFloat("UnitType(\"A\", \"cm\")", 0.0f); ++ ExecuteExpectFloat("UnitType(\"5.08cm\", \"kilograms\")", 2.0f); ++#endif + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, At) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- int result; +- } tests[] = {{"At(\"ABCDEFGH\", \"AB\")", 1}, +- {"At(\"ABCDEFGH\", \"F\")", 6}, +- {"At(23412931298471, 29)", 5}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(tests[i].result, value->ToInteger()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectInt32("At(\"ABCDEFGH\", \"AB\")", 1); ++ ExecuteExpectInt32("At(\"ABCDEFGH\", \"F\")", 6); ++ ExecuteExpectInt32("At(23412931298471, 29)", 5); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Concat) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"Concat(\"ABC\", \"DEF\")", "ABCDEF"}, +- {"Concat(\"Tony\", Space(1), \"Blue\")", "Tony Blue"}, +- {"Concat(\"You owe \", WordNum(1154.67, 2), \".\")", +- "You owe One Thousand One Hundred Fifty-four Dollars And " +- "Sixty-seven Cents."}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("Concat(\"ABC\", \"DEF\")", "ABCDEF"); ++ ExecuteExpectString("Concat(\"Tony\", Space(1), \"Blue\")", "Tony Blue"); ++ ExecuteExpectString("Concat(\"You owe \", WordNum(1154.67, 2), \".\")", ++ "You owe One Thousand One Hundred Fifty-four Dollars And " ++ "Sixty-seven Cents."); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Decode) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = { +- // HTML +- {R"(Decode("", "html"))", ""}, +- {R"(Decode("abcÂxyz", "html"))", "abc\xC3\x82xyz"}, +- {R"(Decode("abc&NoneSuchButVeryLongIndeed;", "html"))", "abc"}, +- {R"(Decode("AÆÁ", "html"))", "A\xC3\x86\xC3\x81"}, +- {R"(Decode("xyz&#", "html"))", "xyz"}, +- {R"(Decode("|&zzzzzz;|", "html"))", "||"}, +- +- // XML +- {R"(Decode("", "xml"))", ""}, +- {R"(Decode("~!@#$%%^&*()_+|`", "xml"))", "~!@#$%%^&*()_+|`"}, +- {R"(Decode("abc&nonesuchbutverylongindeed;", "xml"))", "abc"}, +- {R"(Decode(""E<>[].'", "xml"))", "\"E<>[].'"}, +- {R"(Decode("xyz&#", "xml"))", "xyz"}, +- {R"(Decode("|&zzzzzz;|", "xml"))", "||"}, +- +- // URL +- {R"(Decode("", "url"))", ""}, +- {R"(Decode("~%26^&*()_+|`{", "url"))", "~&^&*()_+|`{"}, +- {R"(Decode("~%26^&*()_+|`{", "mbogo"))", "~&^&*()_+|`{"}, +- {R"(Decode("~%26^&*()_+|`{"))", "~&^&*()_+|`{"}, +- {R"(Decode("~%~~"))", ""}, +- {R"(Decode("?%~"))", ""}, +- {R"(Decode("?%"))", "?"}, +- }; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ // HTML ++ ExecuteExpectString(R"(Decode("", "html"))", ""); ++ ExecuteExpectString(R"(Decode("abcÂxyz", "html"))", "abc\xC3\x82xyz"); ++ ExecuteExpectString(R"(Decode("abc&NoneSuchButVeryLongIndeed;", "html"))", ++ "abc"); ++ ExecuteExpectString(R"(Decode("AÆÁ", "html"))", ++ "A\xC3\x86\xC3\x81"); ++ ExecuteExpectString(R"(Decode("xyz&#", "html"))", "xyz"); ++ ExecuteExpectString(R"(Decode("|&zzzzzz;|", "html"))", "||"); ++ ++ // XML ++ ExecuteExpectString(R"(Decode("", "xml"))", ""); ++ ExecuteExpectString(R"(Decode("~!@#$%%^&*()_+|`", "xml"))", ++ "~!@#$%%^&*()_+|`"); ++ ExecuteExpectString(R"(Decode("abc&nonesuchbutverylongindeed;", "xml"))", ++ "abc"); ++ ExecuteExpectString(R"(Decode(""E<>[].'", "xml"))", ++ "\"E<>[].'"); ++ ExecuteExpectString(R"(Decode("xyz&#", "xml"))", "xyz"); ++ ExecuteExpectString(R"(Decode("|&zzzzzz;|", "xml"))", "||"); ++ ++ // URL ++ ExecuteExpectString(R"(Decode("", "url"))", ""); ++ ExecuteExpectString(R"(Decode("~%26^&*()_+|`{", "url"))", "~&^&*()_+|`{"); ++ ExecuteExpectString(R"(Decode("~%26^&*()_+|`{", "mbogo"))", "~&^&*()_+|`{"); ++ ExecuteExpectString(R"(Decode("~%26^&*()_+|`{"))", "~&^&*()_+|`{"); ++ ExecuteExpectString(R"(Decode("~%~~"))", ""); ++ ExecuteExpectString(R"(Decode("?%~"))", ""); ++ ExecuteExpectString(R"(Decode("?%"))", "?"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Encode) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = { +- {"Encode(\"X/~&^*<=>?|\")", "X%2f%7e%26%5e*%3c%3d%3e%3f%7c"}, +- {"Encode(\"X/~&^*<=>?|\", \"mbogo\")", "X%2f%7e%26%5e*%3c%3d%3e%3f%7c"}, +- {"Encode(\"X/~&^*<=>?|\", \"url\")", "X%2f%7e%26%5e*%3c%3d%3e%3f%7c"}, +- {"Encode(\"X/~&^*<=>?|\", \"xml\")", "X/~&^*<=>?|"}, +- {"Encode(\"X/~&^*<=>?|\", \"html\")", "X/~&^*<=>?|"}, +- +- {"Encode(\"\\u0022\\u00f5\\ufed0\", \"url\")", "%22%f5%fe%d0"}, +- {"Encode(\"\\u0022\\u00f4\\ufed0\", \"xml\")", ""ôﻐ"}, +- {"Encode(\"\\u0022\\u00f5\\ufed0\", \"html\")", ""õﻐ"}, +- +-#if !defined(OS_WIN) +- // Windows wchar_t isn't wide enough to handle these anyways. +- // TODO(tsepez): fix surrogate encodings. +- {"Encode(\"\\uD83D\\uDCA9\", \"url\")", "%01%f4%a9"}, +- {"Encode(\"\\uD83D\\uDCA9\", \"xml\")", ""}, +- {"Encode(\"\\uD83D\\uDCA9\", \"html\")", ""}, +-#endif // !defined(OS_WIN) +- }; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("Encode(\"X/~&^*<=>?|\")", ++ "X%2f%7e%26%5e*%3c%3d%3e%3f%7c"); ++ ExecuteExpectString("Encode(\"X/~&^*<=>?|\", \"mbogo\")", ++ "X%2f%7e%26%5e*%3c%3d%3e%3f%7c"); ++ ExecuteExpectString("Encode(\"X/~&^*<=>?|\", \"url\")", ++ "X%2f%7e%26%5e*%3c%3d%3e%3f%7c"); ++ ExecuteExpectString("Encode(\"X/~&^*<=>?|\", \"xml\")", ++ "X/~&^*<=>?|"); ++ ExecuteExpectString("Encode(\"X/~&^*<=>?|\", \"html\")", ++ "X/~&^*<=>?|"); ++ ++ ExecuteExpectString("Encode(\"\\u0022\\u00f5\\ufed0\", \"url\")", ++ "%22%f5%fe%d0"); ++ ExecuteExpectString("Encode(\"\\u0022\\u00f4\\ufed0\", \"xml\")", ++ ""ôﻐ"); ++ ExecuteExpectString("Encode(\"\\u0022\\u00f5\\ufed0\", \"html\")", ++ ""õﻐ"); ++ ++#if !BUILDFLAG(IS_WIN) ++ // Windows wchar_t isn't wide enough to handle these anyways. ++ // TODO(tsepez): fix surrogate encodings. ++ ExecuteExpectString("Encode(\"\\uD83D\\uDCA9\", \"url\")", "%01%f4%a9"); ++ ExecuteExpectString("Encode(\"\\uD83D\\uDCA9\", \"xml\")", ""); ++ ExecuteExpectString("Encode(\"\\uD83D\\uDCA9\", \"html\")", ""); ++#endif // !BUILDFLAG(IS_WIN) + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Format) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"Format(\"MMM D, YYYY\", \"20020901\")", "Sep 1, 2002"}, +- {"Format(\"$9,999,999.99\", 1234567.89)", "$1,234,567.89"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("Format(\"MMM D, YYYY\", \"20020901\")", "Sep 1, 2002"); ++ ExecuteExpectString("Format(\"$9,999,999.99\", 1234567.89)", "$1,234,567.89"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Left) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"Left(\"ABCDEFGH\", 3)", "ABC"}, +- {"Left(\"Tony Blue\", 5)", "Tony "}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("Left(\"ABCDEFGH\", 3)", "ABC"); ++ ExecuteExpectString("Left(\"Tony Blue\", 5)", "Tony "); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Len) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- int result; +- } tests[] = { +- {"Len(\"ABCDEFGH\")", 8}, {"Len(4)", 1}, {"Len(Str(4.532, 6, 4))", 6}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsInteger()); +- EXPECT_EQ(tests[i].result, value->ToInteger()) +- << "Program: " << tests[i].program; +- } ++ ExecuteExpectInt32("Len(\"ABCDEFGH\")", 8); ++ ExecuteExpectInt32("Len(4)", 1); ++ ExecuteExpectInt32("Len(Str(4.532, 6, 4))", 6); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Lower) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"Lower(\"ABC\")", "abc"}, +- {"Lower(\"21 Main St.\")", "21 main st."}, +- {"Lower(15)", "15"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("Lower(\"ABC\")", "abc"); ++ ExecuteExpectString("Lower(\"21 Main St.\")", "21 main st."); ++ ExecuteExpectString("Lower(15)", "15"); + } + + // This is testing for an OOB read, so will likely only fail under ASAN. +@@ -1232,311 +769,262 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, bug_854623) { + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Ltrim) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"Ltrim(\" ABCD\")", "ABCD"}, +- {"Ltrim(Rtrim(\" Tony Blue \"))", "Tony Blue"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("Ltrim(\" ABCD\")", "ABCD"); ++ ExecuteExpectString("Ltrim(Rtrim(\" Tony Blue \"))", "Tony Blue"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, DISABLED_Parse) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"Parse(\"MMM D, YYYY\", \"Sep 1, 2002\")", "2002-09-01"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } +- +- EXPECT_TRUE(Execute("Parse(\"$9,999,999.99\", \"$1,234,567.89\")")); +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsNumber()); +- EXPECT_FLOAT_EQ(1234567.89f, value->ToFloat()); ++ ExecuteExpectString("Parse(\"MMM D, YYYY\", \"Sep 1, 2002\")", "2002-09-01"); ++ ExecuteExpectFloat("Parse(\"$9,999,999.99\", \"$1,234,567.89\")", ++ 1234567.89f); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Replace) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"Replace(\"Tony Blue\", \"Tony\", \"Chris\")", "Chris Blue"}, +- {"Replace(\"ABCDEFGH\", \"D\")", "ABCEFGH"}, +- {"Replace(\"ABCDEFGH\", \"d\")", "ABCDEFGH"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("Replace(\"Tony Blue\", \"Tony\", \"Chris\")", ++ "Chris Blue"); ++ ExecuteExpectString("Replace(\"ABCDEFGH\", \"D\")", "ABCEFGH"); ++ ExecuteExpectString("Replace(\"ABCDEFGH\", \"d\")", "ABCDEFGH"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Right) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"Right(\"ABCDEFGH\", 3)", "FGH"}, +- {"Right(\"Tony Blue\", 5)", " Blue"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("Right(\"ABCDEFGH\", 3)", "FGH"); ++ ExecuteExpectString("Right(\"Tony Blue\", 5)", " Blue"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Rtrim) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"Rtrim(\"ABCD \")", "ABCD"}, +- {"Rtrim(\"Tony Blue \t\")", "Tony Blue"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("Rtrim(\"ABCD \")", "ABCD"); ++ ExecuteExpectString("Rtrim(\"Tony Blue \t\")", "Tony Blue"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Space) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"Space(5)", " "}, +- {"Concat(\"Tony\", Space(1), \"Blue\")", "Tony Blue"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); ++ ExecuteExpectString("Space(5)", " "); ++ ExecuteExpectString("Concat(\"Tony\", Space(1), \"Blue\")", "Tony Blue"); + +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ // Error cases. ++ ExecuteExpectError("Space(15654909)"); ++ ExecuteExpectError("Space(99999999)"); ++ ExecuteExpectError("Space()"); ++ ExecuteExpectError("Space(1, 2)"); ++ ExecuteExpectNull("Space( $)"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Str) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"Str(2.456)", " 2"}, +- {"Str(4.532, 6, 4)", "4.5320"}, +- {"Str(234.458, 4)", " 234"}, +- {"Str(31.2345, 4, 2)", "****"}}; ++ ExecuteExpectString("Str(2.456)", " 2"); ++ ExecuteExpectString("Str(4.532, 6, 4)", "4.5320"); ++ ExecuteExpectString("Str(234.458, 4)", " 234"); ++ ExecuteExpectString("Str(31.2345, 4, 2)", "****"); + +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); ++ // Test maximum "n3" precision value. ++ ExecuteExpectString("Str(-765, 19, 14)", "-765.00000000000000"); ++ ExecuteExpectString("Str(-765, 20, 15)", "-765.000000000000000"); ++ ExecuteExpectString("Str(-765, 21, 16)", " -765.000000000000000"); + +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ // Error cases. ++ ExecuteExpectError("Str()"); ++ ExecuteExpectError("Str(1, 2, 3, 4)"); ++ ExecuteExpectError("Str(42, 15654909)"); ++ ExecuteExpectNull("Str( $)"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Stuff) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"Stuff(\"TonyBlue\", 5, 0, \" \")", "Tony Blue"}, +- {"Stuff(\"ABCDEFGH\", 4, 2)", "ABCFGH"}, +- {"Stuff(\"members-list@myweb.com\", 0, 0, \"cc:\")", +- "cc:members-list@myweb.com"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ // Test wrong number of parameters. ++ ExecuteExpectError("Stuff(1, 2)"); ++ ExecuteExpectError("Stuff(1, 2, 3, 4, 5)"); ++ ++ // Test null arguments. ++ ExecuteExpectNull("Stuff(null, 0, 4)"); ++ ExecuteExpectNull("Stuff(\"ABCDEFG\", null, 4)"); ++ ExecuteExpectNull("Stuff(\"ABCDEFG\", 0, null)"); ++ ++ // Insertions. ++ ExecuteExpectString("Stuff(\"\", 0, 0, \"clams\")", "clams"); ++ ExecuteExpectString("Stuff(\"TonyBlue\", 5, 0, \" \")", "Tony Blue"); ++ ++ // Deletions. ++ ExecuteExpectString("Stuff(\"A\", 1, 0)", "A"); ++ ExecuteExpectString("Stuff(\"A\", 1, 1)", ""); ++ ExecuteExpectString("Stuff(\"ABCDEFGH\", 4, 2)", "ABCFGH"); ++ ExecuteExpectString("Stuff(\"ABCDEFGH\", 7, 2)", "ABCDEF"); ++ ++ // Test index clamping. ++ ExecuteExpectString("Stuff(\"ABCDEFGH\", -400, 400)", ""); ++ ++ // Need significant amount of text to test start + count overflow due to ++ // intermediate float representation of count not being able to hold ++ // INT_MAX. ++ ExecuteExpectString( ++ "Stuff(\"" ++ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900" ++ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900" ++ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900" ++ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900" ++ "\", 133, 2147483520)", ++ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900" ++ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900" ++ "abcd"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Substr) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + + // Test wrong number of parameters. +- EXPECT_FALSE(Execute("Substr()")); +- EXPECT_FALSE(Execute("Substr(1)")); +- EXPECT_FALSE(Execute("Substr(1, 2)")); +- EXPECT_FALSE(Execute("Substr(1, 2, 3, 4)")); ++ ExecuteExpectError("Substr()"); ++ ExecuteExpectError("Substr(1)"); ++ ExecuteExpectError("Substr(1, 2)"); ++ ExecuteExpectError("Substr(1, 2, 3, 4)"); + + // Test null input. +- EXPECT_TRUE(ExecuteExpectNull("Substr(null, 0, 4)")); +- EXPECT_TRUE(ExecuteExpectNull("Substr(\"ABCDEFG\", null, 4)")); +- EXPECT_TRUE(ExecuteExpectNull("Substr(\"ABCDEFG\", 0, null)")); +- EXPECT_TRUE(ExecuteExpectNull("Substr(null, null, 4)")); +- EXPECT_TRUE(ExecuteExpectNull("Substr(null, 0, null)")); +- EXPECT_TRUE(ExecuteExpectNull("Substr(\"ABCDEFG\", null, null)")); +- EXPECT_TRUE(ExecuteExpectNull("Substr(null, null, null)")); +- +- struct { +- const char* program; +- const char* result; +- } static const kTests[] = {{"Substr(\"ABCDEFG\", -1, 4)", "ABCD"}, +- {"Substr(\"ABCDEFG\", 0, 4)", "ABCD"}, +- {"Substr(\"ABCDEFG\", 3, 4)", "CDEF"}, +- {"Substr(\"ABCDEFG\", 4, 4)", "DEFG"}, +- {"Substr(\"ABCDEFG\", 5, 4)", "EFG"}, +- {"Substr(\"ABCDEFG\", 6, 4)", "FG"}, +- {"Substr(\"ABCDEFG\", 7, 4)", "G"}, +- {"Substr(\"ABCDEFG\", 8, 4)", ""}, +- {"Substr(\"ABCDEFG\", 5, -1)", ""}, +- {"Substr(\"ABCDEFG\", 5, 0)", ""}, +- {"Substr(\"ABCDEFG\", 5, 1)", "E"}, +- {"Substr(\"abcdefghi\", 5, 3)", "efg"}, +- {"Substr(3214, 2, 1)", "2"}, +- {"Substr(\"21 Waterloo St.\", 4, 5)", "Water"}}; +- +- for (const auto& test : kTests) { +- EXPECT_TRUE(Execute(test.program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(test.result, value->ToString().c_str()) +- << "Program: " << test.program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectNull("Substr(null, 0, 4)"); ++ ExecuteExpectNull("Substr(\"ABCDEFG\", null, 4)"); ++ ExecuteExpectNull("Substr(\"ABCDEFG\", 0, null)"); ++ ExecuteExpectNull("Substr(null, null, 4)"); ++ ExecuteExpectNull("Substr(null, 0, null)"); ++ ExecuteExpectNull("Substr(\"ABCDEFG\", null, null)"); ++ ExecuteExpectNull("Substr(null, null, null)"); ++ ++ ExecuteExpectString("Substr(\"ABCDEFG\", -1, 4)", "ABCD"); ++ ExecuteExpectString("Substr(\"ABCDEFG\", 0, 4)", "ABCD"); ++ ExecuteExpectString("Substr(\"ABCDEFG\", 3, 4)", "CDEF"); ++ ExecuteExpectString("Substr(\"ABCDEFG\", 4, 4)", "DEFG"); ++ ExecuteExpectString("Substr(\"ABCDEFG\", 5, 4)", "EFG"); ++ ExecuteExpectString("Substr(\"ABCDEFG\", 6, 4)", "FG"); ++ ExecuteExpectString("Substr(\"ABCDEFG\", 7, 4)", "G"); ++ ExecuteExpectString("Substr(\"ABCDEFG\", 8, 4)", ""); ++ ExecuteExpectString("Substr(\"ABCDEFG\", 5, -1)", ""); ++ ExecuteExpectString("Substr(\"ABCDEFG\", 5, 0)", ""); ++ ExecuteExpectString("Substr(\"ABCDEFG\", 5, 1)", "E"); ++ ExecuteExpectString("Substr(\"abcdefghi\", 5, 3)", "efg"); ++ ExecuteExpectString("Substr(3214, 2, 1)", "2"); ++ ExecuteExpectString("Substr(\"21 Waterloo St.\", 4, 5)", "Water"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Uuid) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); +- + EXPECT_TRUE(Execute("Uuid()")); + +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); ++ CFXJSE_ScopeUtil_IsolateHandleContext scope(GetJseContext()); ++ v8::Local value = GetValue(); ++ EXPECT_TRUE(fxv8::IsString(value)); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Upper) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = {{"Upper(\"abc\")", "ABC"}, +- {"Upper(\"21 Main St.\")", "21 MAIN ST."}, +- {"Upper(15)", "15"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ ExecuteExpectString("Upper(\"abc\")", "ABC"); ++ ExecuteExpectString("Upper(\"21 Main St.\")", "21 MAIN ST."); ++ ExecuteExpectString("Upper(15)", "15"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, WordNum) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- struct { +- const char* program; +- const char* result; +- } tests[] = { +- // {"WordNum(123.45)", +- // "One Hundred and Twenty-three"}, // This looks like it's wrong in the +- // // Formcalc document. +- // {"WordNum(123.45, 1)", "One Hundred and Twenty-three Dollars"}, +- {"WordNum(1154.67, 2)", +- "One Thousand One Hundred Fifty-four Dollars And Sixty-seven Cents"}, +- {"WordNum(43, 2)", "Forty-three Dollars And Zero Cents"}}; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_TRUE(Execute(tests[i].program)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ(tests[i].result, value->ToString().c_str()) +- << "Program: " << tests[i].program << " Result: '" << value->ToString() +- << "'"; +- } ++ // Wrong number of parameters. ++ ExecuteExpectError("WordNum()"); ++ ExecuteExpectError("WordNum(1, 2, 3, 4)"); ++ ++ // Normal format codes. ++ ExecuteExpectString("WordNum(123.45)", "One Hundred Twenty-three"); ++ ExecuteExpectString("WordNum(123.45, 0)", "One Hundred Twenty-three"); ++ ExecuteExpectString("WordNum(123.45, 1)", "One Hundred Twenty-three Dollars"); ++ ExecuteExpectString("WordNum(123.45, 2)", ++ "One Hundred Twenty-three Dollars And Forty-five Cents"); ++ ++ // Invalid format code. ++ ExecuteExpectString("WordNum(123.45, -1)", ""); ++ ExecuteExpectString("WordNum(123.45, 3)", ""); ++ ++ // Locale string is ignored. ++ ExecuteExpectString("WordNum(123.45, 0, \"zh_CN\")", ++ "One Hundred Twenty-three"); ++ ++ // Zero (and near zero) values. ++ ExecuteExpectString("WordNum(0, 0)", "Zero"); ++ ExecuteExpectString("WordNum(0, 1)", "Zero Dollars"); ++ ExecuteExpectString("WordNum(0, 2)", "Zero Dollars And Zero Cents"); ++ ExecuteExpectString("WordNum(0.12, 0)", "Zero"); ++ ExecuteExpectString("WordNum(0.12, 1)", "Zero Dollars"); ++ ExecuteExpectString("WordNum(0.12, 2)", "Zero Dollars And Twelve Cents"); ++ ++ // Negative values. ++ ExecuteExpectString("WordNum(-1, 0)", "*"); ++ ExecuteExpectString("WordNum(-1, 1)", "*"); ++ ExecuteExpectString("WordNum(-1, 2)", "*"); ++ ++ // Test larger values ++ // TODO(tsepez): check on "Thousand Zero" ++ ExecuteExpectString("WordNum(1.234e+6)", ++ "One Million Two Hundred Thirty-four Thousand Zero"); ++ ++ // TODO(tsepez): check on "Zero Thousand Zero" ++ ExecuteExpectString( ++ "WordNum(1.234e+9)", ++ "One Billion Two Hundred Thirty-four Million Zero Thousand Zero"); ++ ++ // TODO(tsepez): check on "Zero Million" ++ ExecuteExpectString( ++ "WordNum(1.234e+12)", ++ "One Trillion Two Hundred Thirty-four Billion Zero Million Nineteen " ++ "Thousand Four Hundred Fifty-six"); ++ ++ ExecuteExpectString( ++ "WordNum(1.234e+15)", ++ "One Thousand Two Hundred Thirty-three Trillion Nine Hundred Ninety-nine " ++ "Billion Nine Hundred Thirty-eight Million Seven Hundred Fifteen " ++ "Thousand " ++ "Six Hundred Forty-eight"); ++ ++ // Out-of-range. ++ ExecuteExpectString("WordNum(1.234e+18)", "*"); ++ ExecuteExpectString("WordNum(1.234e+21)", "*"); ++ ExecuteExpectString("WordNum(1.234e+24)", "*"); ++ ExecuteExpectString("WordNum(1.234e+30)", "*"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Get) { +- // TODO(dsinclair): Is this supported? ++ ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); ++ ExecuteExpectString("Get(\"https://example.com\")", "secrets"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Post) { +- // TODO(dsinclair): Is this supported? ++ ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); ++ ExecuteExpectString( ++ "Post(\"http://example.com\", \"secret stuff\", \"text/plain\")", ++ "posted"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, Put) { +- // TODO(dsinclair): Is this supported? ++ ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); ++ ExecuteExpectString("Put(\"http://example.com\", \"secret stuff\")", ""); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, InvalidFunctions) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- const char* const tests[] = { +- "F()", +- "()", +- "()()()", +- "Round(2.0)()", +- }; +- +- for (size_t i = 0; i < FX_ArraySize(tests); ++i) { +- EXPECT_FALSE(ExecuteSilenceFailure(tests[i])); +- } ++ EXPECT_FALSE(ExecuteSilenceFailure("F()")); ++ EXPECT_FALSE(ExecuteSilenceFailure("()")); ++ EXPECT_FALSE(ExecuteSilenceFailure("()()()")); ++ EXPECT_FALSE(ExecuteSilenceFailure("Round(2.0)()")); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, MethodCall) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + + const char test[] = {"$form.form1.TextField11.getAttribute(\"h\")"}; +- EXPECT_TRUE(Execute(test)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ("12.7mm", value->ToString().c_str()); ++ ExecuteExpectString(test, "12.7mm"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, GetXFAEventChange) { +@@ -1546,15 +1034,10 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, GetXFAEventChange) { + params.m_wsChange = L"changed"; + + CFXJSE_Engine* context = GetScriptContext(); +- context->SetEventParam(¶ms); ++ CFXJSE_Engine::EventParamScope event_scope(context, nullptr, ¶ms); + + const char test[] = {"xfa.event.change"}; +- EXPECT_TRUE(Execute(test)); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsString()); +- EXPECT_STREQ("changed", value->ToString().c_str()); +- context->SetEventParam(nullptr); ++ ExecuteExpectString(test, "changed"); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, SetXFAEventChange) { +@@ -1562,12 +1045,11 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, SetXFAEventChange) { + + CXFA_EventParam params; + CFXJSE_Engine* context = GetScriptContext(); +- context->SetEventParam(¶ms); ++ CFXJSE_Engine::EventParamScope event_scope(context, nullptr, ¶ms); + + const char test[] = {"xfa.event.change = \"changed\""}; + EXPECT_TRUE(Execute(test)); + EXPECT_EQ(L"changed", params.m_wsChange); +- context->SetEventParam(nullptr); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, SetXFAEventFullTextFails) { +@@ -1577,12 +1059,11 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, SetXFAEventFullTextFails) { + params.m_wsFullText = L"Original Full Text"; + + CFXJSE_Engine* context = GetScriptContext(); +- context->SetEventParam(¶ms); ++ CFXJSE_Engine::EventParamScope event_scope(context, nullptr, ¶ms); + + const char test[] = {"xfa.event.fullText = \"Changed Full Text\""}; + EXPECT_TRUE(Execute(test)); + EXPECT_EQ(L"Original Full Text", params.m_wsFullText); +- context->SetEventParam(nullptr); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, EventChangeSelection) { +@@ -1594,7 +1075,7 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, EventChangeSelection) { + params.m_iSelEnd = 3; + + CFXJSE_Engine* context = GetScriptContext(); +- context->SetEventParam(¶ms); ++ CFXJSE_Engine::EventParamScope event_scope(context, nullptr, ¶ms); + + // Moving end to start works fine. + EXPECT_TRUE(Execute("xfa.event.selEnd = \"1\"")); +@@ -1637,8 +1118,6 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, EventChangeSelection) { + EXPECT_TRUE(Execute("xfa.event.selStart = \"20\"")); + EXPECT_EQ(4, params.m_iSelStart); + EXPECT_EQ(4, params.m_iSelEnd); +- +- context->SetEventParam(nullptr); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, XFAEventCancelAction) { +@@ -1648,18 +1127,10 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, XFAEventCancelAction) { + params.m_bCancelAction = false; + + CFXJSE_Engine* context = GetScriptContext(); +- context->SetEventParam(¶ms); +- +- EXPECT_TRUE(Execute("xfa.event.cancelAction")); +- +- CFXJSE_Value* value = GetValue(); +- EXPECT_TRUE(value->IsBoolean()); +- EXPECT_FALSE(value->ToBoolean()); +- ++ CFXJSE_Engine::EventParamScope event_scope(context, nullptr, ¶ms); ++ ExecuteExpectBool("xfa.event.cancelAction", false); + EXPECT_TRUE(Execute("xfa.event.cancelAction = \"true\"")); + EXPECT_TRUE(params.m_bCancelAction); +- +- context->SetEventParam(nullptr); + } + + TEST_F(CFXJSE_FormCalcContextEmbedderTest, ComplexTextChangeEvent) { +@@ -1672,7 +1143,7 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, ComplexTextChangeEvent) { + params.m_iSelEnd = 3; + + CFXJSE_Engine* context = GetScriptContext(); +- context->SetEventParam(¶ms); ++ CFXJSE_Engine::EventParamScope event_scope(context, nullptr, ¶ms); + + EXPECT_EQ(L"abcd", params.m_wsPrevText); + EXPECT_EQ(L"agd", params.GetNewText()); +@@ -1697,13 +1168,10 @@ TEST_F(CFXJSE_FormCalcContextEmbedderTest, ComplexTextChangeEvent) { + EXPECT_EQ(L"axyzbcd", params.GetNewText()); + EXPECT_EQ(1, params.m_iSelStart); + EXPECT_EQ(1, params.m_iSelEnd); +- +- context->SetEventParam(nullptr); + } + + // Should not crash. + TEST_F(CFXJSE_FormCalcContextEmbedderTest, BUG_1223) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); +- +- EXPECT_FALSE(Execute("!.somExpression=0")); ++ EXPECT_TRUE(Execute("!.somExpression=0")); + } +diff --git a/fxjs/xfa/cfxjse_formcalc_context_unittest.cpp b/fxjs/xfa/cfxjse_formcalc_context_unittest.cpp +new file mode 100644 +index 000000000..2500e3f25 +--- /dev/null ++++ b/fxjs/xfa/cfxjse_formcalc_context_unittest.cpp +@@ -0,0 +1,74 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "fxjs/xfa/cfxjse_formcalc_context.h" ++ ++#include "core/fxcrt/bytestring.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++TEST(FormCalcContextTest, GenerateSomExpression) { ++ ByteString result = ++ CFXJSE_FormCalcContext::GenerateSomExpression("", 0, 0, /*bIsStar=*/true); ++ EXPECT_EQ(result, "[*]"); ++ ++ result = CFXJSE_FormCalcContext::GenerateSomExpression("foo", 0, 0, ++ /*bIsStar=*/true); ++ EXPECT_EQ(result, "foo[*]"); ++ ++ result = CFXJSE_FormCalcContext::GenerateSomExpression("foo", 0, 0, ++ /*bIsStar=*/false); ++ EXPECT_EQ(result, "foo"); ++ ++ result = CFXJSE_FormCalcContext::GenerateSomExpression("fu", 1, 0, ++ /*bIsStar=*/false); ++ EXPECT_EQ(result, "fu[0]"); ++ ++ result = CFXJSE_FormCalcContext::GenerateSomExpression("food", 1, 99, ++ /*bIsStar=*/false); ++ EXPECT_EQ(result, "food[99]"); ++ ++ result = CFXJSE_FormCalcContext::GenerateSomExpression("foot", 1, -65, ++ /*bIsStar=*/false); ++ EXPECT_EQ(result, "foot[-65]"); ++ ++ result = CFXJSE_FormCalcContext::GenerateSomExpression("football", 2, 0, ++ /*bIsStar=*/false); ++ EXPECT_EQ(result, "football[0]"); ++ ++ result = CFXJSE_FormCalcContext::GenerateSomExpression("foosball", 2, 123, ++ /*bIsStar=*/false); ++ EXPECT_EQ(result, "foosball[+123]"); ++ ++ result = CFXJSE_FormCalcContext::GenerateSomExpression("bar", 2, -654, ++ /*bIsStar=*/false); ++ EXPECT_EQ(result, "bar[-654]"); ++ ++ result = CFXJSE_FormCalcContext::GenerateSomExpression("barb", 2, 2147483647, ++ /*bIsStar=*/false); ++ EXPECT_EQ(result, "barb[+2147483647]"); ++ ++ result = CFXJSE_FormCalcContext::GenerateSomExpression( ++ "bart", 2, -2147483648, /*bIsStar=*/false); ++ EXPECT_EQ(result, "bart[-0]"); ++ ++ result = CFXJSE_FormCalcContext::GenerateSomExpression("bark", 3, 0, ++ /*bIsStar=*/false); ++ EXPECT_EQ(result, "bark[0]"); ++ ++ result = CFXJSE_FormCalcContext::GenerateSomExpression("bard", 3, 357, ++ /*bIsStar=*/false); ++ EXPECT_EQ(result, "bard[-357]"); ++ ++ result = CFXJSE_FormCalcContext::GenerateSomExpression("bars", 3, -9876, ++ /*bIsStar=*/false); ++ EXPECT_EQ(result, "bars[9876]"); ++ ++ result = CFXJSE_FormCalcContext::GenerateSomExpression("cars", 3, 2147483647, ++ /*bIsStar=*/false); ++ EXPECT_EQ(result, "cars[-2147483647]"); ++ ++ result = CFXJSE_FormCalcContext::GenerateSomExpression( ++ "mars", 3, -2147483648, /*bIsStar=*/false); ++ EXPECT_EQ(result, "mars[0]"); ++} +diff --git a/fxjs/xfa/cfxjse_isolatetracker.cpp b/fxjs/xfa/cfxjse_isolatetracker.cpp +index 6a29e44cb..939553de1 100644 +--- a/fxjs/xfa/cfxjse_isolatetracker.cpp ++++ b/fxjs/xfa/cfxjse_isolatetracker.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,20 +6,38 @@ + + #include "fxjs/xfa/cfxjse_isolatetracker.h" + ++#include "fxjs/xfa/cfxjse_context.h" + #include "fxjs/xfa/cfxjse_runtimedata.h" + + CFXJSE_ScopeUtil_IsolateHandle::CFXJSE_ScopeUtil_IsolateHandle( + v8::Isolate* pIsolate) +- : m_iscope(pIsolate), m_hscope(pIsolate) {} ++ : isolate_scope_(pIsolate), handle_scope_(pIsolate) {} + + CFXJSE_ScopeUtil_IsolateHandle::~CFXJSE_ScopeUtil_IsolateHandle() = default; + ++CFXJSE_ScopeUtil_Context::CFXJSE_ScopeUtil_Context(CFXJSE_Context* pContext) ++ : context_scope_(pContext->GetContext()) {} ++ ++CFXJSE_ScopeUtil_Context::~CFXJSE_ScopeUtil_Context() = default; ++ ++CFXJSE_ScopeUtil_IsolateHandleContext::CFXJSE_ScopeUtil_IsolateHandleContext( ++ CFXJSE_Context* pContext) ++ : isolate_handle_(pContext->GetIsolate()), context_(pContext) {} ++ ++CFXJSE_ScopeUtil_IsolateHandleContext:: ++ ~CFXJSE_ScopeUtil_IsolateHandleContext() = default; ++ ++CFXJSE_ScopeUtil_RootContext::CFXJSE_ScopeUtil_RootContext( ++ v8::Isolate* pIsolate) ++ : context_scope_(v8::Local::New( ++ pIsolate, ++ CFXJSE_RuntimeData::Get(pIsolate)->GetRootContext())) {} ++ ++CFXJSE_ScopeUtil_RootContext::~CFXJSE_ScopeUtil_RootContext() = default; ++ + CFXJSE_ScopeUtil_IsolateHandleRootContext:: + CFXJSE_ScopeUtil_IsolateHandleRootContext(v8::Isolate* pIsolate) +- : CFXJSE_ScopeUtil_IsolateHandle(pIsolate), +- m_cscope(v8::Local::New( +- pIsolate, +- CFXJSE_RuntimeData::Get(pIsolate)->m_hRootContext)) {} ++ : isolate_handle_(pIsolate), root_context_(pIsolate) {} + + CFXJSE_ScopeUtil_IsolateHandleRootContext:: + ~CFXJSE_ScopeUtil_IsolateHandleRootContext() = default; +diff --git a/fxjs/xfa/cfxjse_isolatetracker.h b/fxjs/xfa/cfxjse_isolatetracker.h +index 020142d3f..7353ac09b 100644 +--- a/fxjs/xfa/cfxjse_isolatetracker.h ++++ b/fxjs/xfa/cfxjse_isolatetracker.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,10 +7,16 @@ + #ifndef FXJS_XFA_CFXJSE_ISOLATETRACKER_H_ + #define FXJS_XFA_CFXJSE_ISOLATETRACKER_H_ + +-#include "v8/include/v8.h" ++#include "core/fxcrt/fx_memory.h" ++#include "v8/include/v8-context.h" ++#include "v8/include/v8-isolate.h" ++ ++class CFXJSE_Context; + + class CFXJSE_ScopeUtil_IsolateHandle { + public: ++ FX_STACK_ALLOCATED(); ++ + explicit CFXJSE_ScopeUtil_IsolateHandle(v8::Isolate* pIsolate); + CFXJSE_ScopeUtil_IsolateHandle(const CFXJSE_ScopeUtil_IsolateHandle&) = + delete; +@@ -19,16 +25,57 @@ class CFXJSE_ScopeUtil_IsolateHandle { + ~CFXJSE_ScopeUtil_IsolateHandle(); + + private: +- void* operator new(size_t size) = delete; +- void operator delete(void*, size_t) = delete; ++ v8::Isolate::Scope isolate_scope_; ++ v8::HandleScope handle_scope_; ++}; ++ ++class CFXJSE_ScopeUtil_Context { ++ public: ++ FX_STACK_ALLOCATED(); ++ ++ explicit CFXJSE_ScopeUtil_Context(CFXJSE_Context* pContext); ++ CFXJSE_ScopeUtil_Context(const CFXJSE_ScopeUtil_Context&) = delete; ++ CFXJSE_ScopeUtil_Context& operator=(const CFXJSE_ScopeUtil_Context&) = delete; ++ ~CFXJSE_ScopeUtil_Context(); ++ ++ private: ++ v8::Context::Scope context_scope_; ++}; ++ ++class CFXJSE_ScopeUtil_IsolateHandleContext { ++ public: ++ FX_STACK_ALLOCATED(); ++ ++ explicit CFXJSE_ScopeUtil_IsolateHandleContext(CFXJSE_Context* pContext); ++ CFXJSE_ScopeUtil_IsolateHandleContext( ++ const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete; ++ CFXJSE_ScopeUtil_IsolateHandleContext& operator=( ++ const CFXJSE_ScopeUtil_IsolateHandleContext&) = delete; ++ ~CFXJSE_ScopeUtil_IsolateHandleContext(); ++ ++ private: ++ CFXJSE_ScopeUtil_IsolateHandle isolate_handle_; ++ CFXJSE_ScopeUtil_Context context_; ++}; ++ ++class CFXJSE_ScopeUtil_RootContext { ++ public: ++ FX_STACK_ALLOCATED(); + +- v8::Isolate::Scope m_iscope; +- v8::HandleScope m_hscope; ++ explicit CFXJSE_ScopeUtil_RootContext(v8::Isolate* pIsolate); ++ CFXJSE_ScopeUtil_RootContext(const CFXJSE_ScopeUtil_RootContext&) = delete; ++ CFXJSE_ScopeUtil_RootContext& operator=(const CFXJSE_ScopeUtil_RootContext&) = ++ delete; ++ ~CFXJSE_ScopeUtil_RootContext(); ++ ++ private: ++ v8::Context::Scope context_scope_; + }; + +-class CFXJSE_ScopeUtil_IsolateHandleRootContext final +- : public CFXJSE_ScopeUtil_IsolateHandle { ++class CFXJSE_ScopeUtil_IsolateHandleRootContext { + public: ++ FX_STACK_ALLOCATED(); ++ + explicit CFXJSE_ScopeUtil_IsolateHandleRootContext(v8::Isolate* pIsolate); + CFXJSE_ScopeUtil_IsolateHandleRootContext( + const CFXJSE_ScopeUtil_IsolateHandleRootContext&) = delete; +@@ -37,10 +84,8 @@ class CFXJSE_ScopeUtil_IsolateHandleRootContext final + ~CFXJSE_ScopeUtil_IsolateHandleRootContext(); + + private: +- void* operator new(size_t size) = delete; +- void operator delete(void*, size_t) = delete; +- +- v8::Context::Scope m_cscope; ++ CFXJSE_ScopeUtil_IsolateHandle isolate_handle_; ++ CFXJSE_ScopeUtil_RootContext root_context_; + }; + + #endif // FXJS_XFA_CFXJSE_ISOLATETRACKER_H_ +diff --git a/fxjs/xfa/cfxjse_mapmodule.cpp b/fxjs/xfa/cfxjse_mapmodule.cpp +new file mode 100644 +index 000000000..7e656b8ad +--- /dev/null ++++ b/fxjs/xfa/cfxjse_mapmodule.cpp +@@ -0,0 +1,78 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "fxjs/xfa/cfxjse_mapmodule.h" ++ ++#include "third_party/base/containers/contains.h" ++#include "xfa/fxfa/parser/cxfa_measurement.h" ++ ++CFXJSE_MapModule::CFXJSE_MapModule() = default; ++ ++CFXJSE_MapModule::~CFXJSE_MapModule() = default; ++ ++void CFXJSE_MapModule::SetValue(uint32_t key, int32_t value) { ++ m_StringMap.erase(key); ++ m_MeasurementMap.erase(key); ++ m_ValueMap[key] = value; ++} ++ ++void CFXJSE_MapModule::SetString(uint32_t key, const WideString& wsString) { ++ m_ValueMap.erase(key); ++ m_MeasurementMap.erase(key); ++ m_StringMap[key] = wsString; ++} ++ ++void CFXJSE_MapModule::SetMeasurement(uint32_t key, ++ const CXFA_Measurement& measurement) { ++ m_ValueMap.erase(key); ++ m_StringMap.erase(key); ++ m_MeasurementMap[key] = measurement; ++} ++ ++absl::optional CFXJSE_MapModule::GetValue(uint32_t key) const { ++ auto it = m_ValueMap.find(key); ++ if (it == m_ValueMap.end()) ++ return absl::nullopt; ++ return it->second; ++} ++ ++absl::optional CFXJSE_MapModule::GetString(uint32_t key) const { ++ auto it = m_StringMap.find(key); ++ if (it == m_StringMap.end()) ++ return absl::nullopt; ++ return it->second; ++} ++ ++absl::optional CFXJSE_MapModule::GetMeasurement( ++ uint32_t key) const { ++ auto it = m_MeasurementMap.find(key); ++ if (it == m_MeasurementMap.end()) ++ return absl::nullopt; ++ return it->second; ++} ++ ++bool CFXJSE_MapModule::HasKey(uint32_t key) const { ++ return pdfium::Contains(m_ValueMap, key) || ++ pdfium::Contains(m_StringMap, key) || ++ pdfium::Contains(m_MeasurementMap, key); ++} ++ ++void CFXJSE_MapModule::RemoveKey(uint32_t key) { ++ m_ValueMap.erase(key); ++ m_StringMap.erase(key); ++ m_MeasurementMap.erase(key); ++} ++ ++void CFXJSE_MapModule::MergeDataFrom(const CFXJSE_MapModule* pSrc) { ++ for (const auto& pair : pSrc->m_ValueMap) ++ SetValue(pair.first, pair.second); ++ ++ for (const auto& pair : pSrc->m_StringMap) ++ SetString(pair.first, pair.second); ++ ++ for (const auto& pair : pSrc->m_MeasurementMap) ++ SetMeasurement(pair.first, pair.second); ++} +diff --git a/fxjs/xfa/cfxjse_mapmodule.h b/fxjs/xfa/cfxjse_mapmodule.h +new file mode 100644 +index 000000000..9c757500f +--- /dev/null ++++ b/fxjs/xfa/cfxjse_mapmodule.h +@@ -0,0 +1,44 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#ifndef FXJS_XFA_CFXJSE_MAPMODULE_H_ ++#define FXJS_XFA_CFXJSE_MAPMODULE_H_ ++ ++#include ++ ++#include ++ ++#include "core/fxcrt/widestring.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++ ++class CXFA_Measurement; ++ ++class CFXJSE_MapModule { ++ public: ++ CFXJSE_MapModule(); ++ ~CFXJSE_MapModule(); ++ ++ CFXJSE_MapModule(const CFXJSE_MapModule& that) = delete; ++ CFXJSE_MapModule& operator=(const CFXJSE_MapModule& that) = delete; ++ ++ void SetValue(uint32_t key, int32_t value); ++ void SetString(uint32_t key, const WideString& wsString); ++ void SetMeasurement(uint32_t key, const CXFA_Measurement& measurement); ++ absl::optional GetValue(uint32_t key) const; ++ absl::optional GetString(uint32_t key) const; ++ absl::optional GetMeasurement(uint32_t key) const; ++ bool HasKey(uint32_t key) const; ++ void RemoveKey(uint32_t key); ++ void MergeDataFrom(const CFXJSE_MapModule* pSrc); ++ ++ private: ++ // keyed by result of GetMapKey_*(). ++ std::map m_ValueMap; ++ std::map m_StringMap; ++ std::map m_MeasurementMap; ++}; ++ ++#endif // FXJS_XFA_CFXJSE_MAPMODULE_H_ +diff --git a/fxjs/xfa/cfxjse_mapmodule_unittest.cpp b/fxjs/xfa/cfxjse_mapmodule_unittest.cpp +new file mode 100644 +index 000000000..3119b58f1 +--- /dev/null ++++ b/fxjs/xfa/cfxjse_mapmodule_unittest.cpp +@@ -0,0 +1,124 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com ++ ++#include "fxjs/xfa/cfxjse_mapmodule.h" ++ ++#include "core/fxcrt/fx_string.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++#include "xfa/fxfa/parser/cxfa_measurement.h" ++ ++TEST(CFXJSEMapModule, EmptyModule) { ++ CFXJSE_MapModule module; ++ EXPECT_FALSE(module.HasKey(1)); ++ EXPECT_FALSE(module.HasKey(2)); ++ EXPECT_FALSE(module.HasKey(3)); ++ EXPECT_FALSE(module.GetValue(1).has_value()); ++ EXPECT_FALSE(module.GetString(2).has_value()); ++ EXPECT_FALSE(module.GetMeasurement(3).has_value()); ++} ++ ++TEST(CFXJSEMapModule, InsertDelete) { ++ const int value = 101; ++ WideString str(L"foo"); ++ CXFA_Measurement measure(L"1 pt"); ++ CFXJSE_MapModule module; ++ ++ module.SetValue(100, value); ++ module.SetString(200, str); ++ module.SetMeasurement(300, measure); ++ EXPECT_TRUE(module.HasKey(100)); ++ EXPECT_TRUE(module.HasKey(200)); ++ EXPECT_TRUE(module.HasKey(300)); ++ ++ EXPECT_EQ(module.GetValue(100).value(), value); ++ EXPECT_FALSE(module.GetString(100).has_value()); ++ EXPECT_FALSE(module.GetMeasurement(100).has_value()); ++ ++ EXPECT_FALSE(module.GetValue(200).has_value()); ++ EXPECT_EQ(module.GetString(200).value(), str); ++ EXPECT_FALSE(module.GetMeasurement(200).has_value()); ++ ++ EXPECT_FALSE(module.GetValue(300).has_value()); ++ EXPECT_FALSE(module.GetString(300).has_value()); ++ EXPECT_EQ(module.GetMeasurement(300).value().GetUnit(), measure.GetUnit()); ++ EXPECT_EQ(module.GetMeasurement(300).value().GetValue(), measure.GetValue()); ++ ++ module.RemoveKey(100); ++ module.RemoveKey(200); ++ module.RemoveKey(300); ++ EXPECT_FALSE(module.HasKey(100)); ++ EXPECT_FALSE(module.HasKey(200)); ++ EXPECT_FALSE(module.HasKey(300)); ++ EXPECT_FALSE(module.GetValue(100).has_value()); ++ EXPECT_FALSE(module.GetString(200).has_value()); ++ EXPECT_FALSE(module.GetMeasurement(200).has_value()); ++} ++ ++TEST(CFXJSEMapModule, KeyCollision) { ++ const int value = 37; ++ WideString str(L"foo"); ++ CXFA_Measurement measure(L"1 pt"); ++ CFXJSE_MapModule module; ++ ++ module.SetValue(100, value); ++ EXPECT_TRUE(module.HasKey(100)); ++ EXPECT_EQ(module.GetValue(100).value(), value); ++ EXPECT_FALSE(module.GetString(100).has_value()); ++ EXPECT_FALSE(module.GetMeasurement(100).has_value()); ++ ++ module.SetString(100, str); ++ EXPECT_TRUE(module.HasKey(100)); ++ EXPECT_FALSE(module.GetValue(100).has_value()); ++ EXPECT_EQ(module.GetString(100).value(), str); ++ EXPECT_FALSE(module.GetMeasurement(100).has_value()); ++ ++ module.SetMeasurement(100, measure); ++ EXPECT_FALSE(module.GetValue(100).has_value()); ++ EXPECT_FALSE(module.GetString(100).has_value()); ++ EXPECT_EQ(module.GetMeasurement(100).value().GetUnit(), measure.GetUnit()); ++ ++ module.SetValue(100, value); ++ EXPECT_TRUE(module.HasKey(100)); ++ EXPECT_EQ(module.GetValue(100).value(), value); ++ EXPECT_FALSE(module.GetString(100).has_value()); ++ EXPECT_FALSE(module.GetMeasurement(100).has_value()); ++} ++ ++TEST(CFXJSEMapModule, MergeData) { ++ const int value1 = 42; ++ const int value2 = -1999; ++ WideString string1(L"foo"); ++ WideString string2(L"foo"); ++ CXFA_Measurement measure1(L"1 pt"); ++ CXFA_Measurement measure2(L"2 mm"); ++ CFXJSE_MapModule module1; ++ CFXJSE_MapModule module2; ++ ++ module1.SetValue(100, value1); ++ module1.SetValue(101, value1); ++ module1.SetString(200, string1); ++ module1.SetString(201, string1); ++ module1.SetMeasurement(300, measure1); ++ module1.SetMeasurement(301, measure1); ++ ++ module2.SetString(100, string2); ++ module2.SetMeasurement(200, measure2); ++ module2.SetValue(300, value2); ++ ++ module1.MergeDataFrom(&module2); ++ EXPECT_EQ(module1.GetString(100).value(), string2); ++ EXPECT_EQ(module1.GetValue(101).value(), value1); ++ EXPECT_EQ(module1.GetMeasurement(200).value().GetUnit(), measure2.GetUnit()); ++ EXPECT_EQ(module1.GetString(201).value(), string1); ++ EXPECT_EQ(module1.GetValue(300).value(), value2); ++ EXPECT_EQ(module1.GetMeasurement(301).value().GetUnit(), measure1.GetUnit()); ++ ++ // module2 is undisturbed. ++ EXPECT_EQ(module2.GetString(100).value(), string2); ++ EXPECT_EQ(module2.GetMeasurement(200).value().GetUnit(), measure2.GetUnit()); ++ EXPECT_EQ(module2.GetValue(300).value(), value2); ++} +diff --git a/xfa/fxfa/parser/cxfa_nodehelper.cpp b/fxjs/xfa/cfxjse_nodehelper.cpp +similarity index 77% +rename from xfa/fxfa/parser/cxfa_nodehelper.cpp +rename to fxjs/xfa/cfxjse_nodehelper.cpp +index dde90bc5a..de24aa41a 100644 +--- a/xfa/fxfa/parser/cxfa_nodehelper.cpp ++++ b/fxjs/xfa/cfxjse_nodehelper.cpp +@@ -1,12 +1,10 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#include "xfa/fxfa/parser/cxfa_nodehelper.h" +- +-#include ++#include "fxjs/xfa/cfxjse_nodehelper.h" + + #include "core/fxcrt/fx_extension.h" + #include "fxjs/xfa/cfxjse_engine.h" +@@ -15,19 +13,18 @@ + #include "xfa/fxfa/parser/cxfa_localemgr.h" + #include "xfa/fxfa/parser/cxfa_node.h" + #include "xfa/fxfa/parser/xfa_basic_data.h" +-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h" + #include "xfa/fxfa/parser/xfa_utils.h" + +-CXFA_NodeHelper::CXFA_NodeHelper() = default; ++CFXJSE_NodeHelper::CFXJSE_NodeHelper() = default; + +-CXFA_NodeHelper::~CXFA_NodeHelper() = default; ++CFXJSE_NodeHelper::~CFXJSE_NodeHelper() = default; + +-bool CXFA_NodeHelper::CreateNodeForCondition(const WideString& wsCondition) { ++bool CFXJSE_NodeHelper::CreateNodeForCondition(const WideString& wsCondition) { + size_t szLen = wsCondition.GetLength(); + WideString wsIndex(L"0"); + bool bAll = false; + if (szLen == 0) { +- m_iCreateFlag = XFA_ResolveNode_RSType_CreateNodeOne; ++ m_iCreateFlag = CFXJSE_Engine::ResolveResult::Type::kCreateNodeOne; + return false; + } + if (wsCondition[0] != '[') +@@ -45,9 +42,9 @@ bool CXFA_NodeHelper::CreateNodeForCondition(const WideString& wsCondition) { + } + if (bAll) { + wsIndex = L"1"; +- m_iCreateFlag = XFA_ResolveNode_RSType_CreateNodeAll; ++ m_iCreateFlag = CFXJSE_Engine::ResolveResult::Type::kCreateNodeAll; + } else { +- m_iCreateFlag = XFA_ResolveNode_RSType_CreateNodeOne; ++ m_iCreateFlag = CFXJSE_Engine::ResolveResult::Type::kCreateNodeOne; + wsIndex = wsCondition.Substr(i, szLen - 1 - i); + } + int32_t iCount = wsIndex.GetInteger(); +@@ -58,10 +55,10 @@ bool CXFA_NodeHelper::CreateNodeForCondition(const WideString& wsCondition) { + return true; + } + +-bool CXFA_NodeHelper::CreateNode(const WideString& wsName, +- const WideString& wsCondition, +- bool bLastNode, +- CFXJSE_Engine* pScriptContext) { ++bool CFXJSE_NodeHelper::CreateNode(const WideString& wsName, ++ const WideString& wsCondition, ++ bool bLastNode, ++ CFXJSE_Engine* pScriptContext) { + if (!m_pCreateParent) + return false; + +@@ -106,8 +103,8 @@ bool CXFA_NodeHelper::CreateNode(const WideString& wsName, + for (size_t i = 0; i < m_iCreateCount; ++i) { + CXFA_Node* pNewNode = m_pCreateParent->CreateSamePacketNode(eClassType); + if (pNewNode) { +- pNewNode->JSObject()->SetAttribute(XFA_Attribute::Name, wsNameView, +- false); ++ pNewNode->JSObject()->SetAttributeByEnum(XFA_Attribute::Name, ++ WideString(wsNameView), false); + pNewNode->CreateXMLMappingNode(); + m_pCreateParent->InsertChildAndNotify(pNewNode, nullptr); + if (i == m_iCreateCount - 1) { +@@ -123,7 +120,7 @@ bool CXFA_NodeHelper::CreateNode(const WideString& wsName, + return bResult; + } + +-void CXFA_NodeHelper::SetCreateNodeType(CXFA_Node* refNode) { ++void CFXJSE_NodeHelper::SetCreateNodeType(CXFA_Node* refNode) { + if (!refNode) + return; + +diff --git a/xfa/fxfa/parser/cxfa_nodehelper.h b/fxjs/xfa/cfxjse_nodehelper.h +similarity index 54% +rename from xfa/fxfa/parser/cxfa_nodehelper.h +rename to fxjs/xfa/cfxjse_nodehelper.h +index 66e7e6654..7afc10cbb 100644 +--- a/xfa/fxfa/parser/cxfa_nodehelper.h ++++ b/fxjs/xfa/cfxjse_nodehelper.h +@@ -1,25 +1,23 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +-#ifndef XFA_FXFA_PARSER_CXFA_NODEHELPER_H_ +-#define XFA_FXFA_PARSER_CXFA_NODEHELPER_H_ ++#ifndef FXJS_XFA_CFXJSE_NODEHELPER_H_ ++#define FXJS_XFA_CFXJSE_NODEHELPER_H_ + +-#include +- +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/widestring.h" ++#include "fxjs/xfa/cfxjse_engine.h" ++#include "v8/include/cppgc/persistent.h" + #include "xfa/fxfa/fxfa_basic.h" +-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h" + +-class CFXJSE_Engine; + class CXFA_Node; + +-class CXFA_NodeHelper { ++class CFXJSE_NodeHelper { + public: +- CXFA_NodeHelper(); +- ~CXFA_NodeHelper(); ++ CFXJSE_NodeHelper(); ++ ~CFXJSE_NodeHelper(); + + bool CreateNode(const WideString& wsName, + const WideString& wsCondition, +@@ -29,11 +27,12 @@ class CXFA_NodeHelper { + void SetCreateNodeType(CXFA_Node* refNode); + + XFA_Element m_eLastCreateType = XFA_Element::DataValue; +- XFA_ResolveNode_RSType m_iCreateFlag = XFA_ResolveNode_RSType_CreateNodeOne; ++ CFXJSE_Engine::ResolveResult::Type m_iCreateFlag = ++ CFXJSE_Engine::ResolveResult::Type::kCreateNodeOne; + size_t m_iCreateCount = 0; + int32_t m_iCurAllStart = -1; +- UnownedPtr m_pCreateParent; +- UnownedPtr m_pAllStartParent; ++ cppgc::Persistent m_pCreateParent; ++ cppgc::Persistent m_pAllStartParent; + }; + +-#endif // XFA_FXFA_PARSER_CXFA_NODEHELPER_H_ ++#endif // FXJS_XFA_CFXJSE_NODEHELPER_H_ +diff --git a/fxjs/xfa/cfxjse_resolveprocessor.cpp b/fxjs/xfa/cfxjse_resolveprocessor.cpp +index 89883d65f..bfe571ca5 100644 +--- a/fxjs/xfa/cfxjse_resolveprocessor.cpp ++++ b/fxjs/xfa/cfxjse_resolveprocessor.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,114 +12,87 @@ + + #include "core/fxcrt/fx_extension.h" + #include "fxjs/xfa/cfxjse_engine.h" ++#include "fxjs/xfa/cfxjse_nodehelper.h" + #include "fxjs/xfa/cfxjse_value.h" + #include "fxjs/xfa/cjx_object.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/containers/contains.h" + #include "xfa/fxfa/parser/cxfa_document.h" + #include "xfa/fxfa/parser/cxfa_localemgr.h" + #include "xfa/fxfa/parser/cxfa_node.h" +-#include "xfa/fxfa/parser/cxfa_nodehelper.h" + #include "xfa/fxfa/parser/cxfa_object.h" + #include "xfa/fxfa/parser/cxfa_occur.h" +-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h" + #include "xfa/fxfa/parser/xfa_utils.h" + +-namespace { +- +-void DoPredicateFilter(WideString wsCondition, +- size_t iFoundCount, +- CFXJSE_ResolveNodeData* pRnd) { +- ASSERT(iFoundCount == pRnd->m_Objects.size()); +- WideString wsExpression; +- CXFA_Script::Type eLangType = CXFA_Script::Type::Unknown; +- if (wsCondition.First(2).EqualsASCII(".[") && wsCondition.Back() == L']') +- eLangType = CXFA_Script::Type::Formcalc; +- else if (wsCondition.First(2).EqualsASCII(".(") && wsCondition.Back() == L')') +- eLangType = CXFA_Script::Type::Javascript; +- else +- return; +- +- wsExpression = wsCondition.Substr(2, wsCondition.GetLength() - 3); +- for (size_t i = iFoundCount; i > 0; --i) { +- auto pRetValue = +- pdfium::MakeUnique(pRnd->m_pSC->GetIsolate()); +- bool bRet = +- pRnd->m_pSC->RunScript(eLangType, wsExpression.AsStringView(), +- pRetValue.get(), pRnd->m_Objects[i - 1].Get()); +- if (!bRet || !pRetValue->ToBoolean()) +- pRnd->m_Objects.erase(pRnd->m_Objects.begin() + i - 1); +- } +-} +- +-} // namespace +- +-CFXJSE_ResolveProcessor::CFXJSE_ResolveProcessor() +- : m_pNodeHelper(pdfium::MakeUnique()) {} ++CFXJSE_ResolveProcessor::CFXJSE_ResolveProcessor(CFXJSE_Engine* pEngine, ++ CFXJSE_NodeHelper* pHelper) ++ : m_pEngine(pEngine), m_pNodeHelper(pHelper) {} + + CFXJSE_ResolveProcessor::~CFXJSE_ResolveProcessor() = default; + +-bool CFXJSE_ResolveProcessor::Resolve(CFXJSE_ResolveNodeData& rnd) { ++bool CFXJSE_ResolveProcessor::Resolve(v8::Isolate* pIsolate, NodeData& rnd) { + if (!rnd.m_CurObject) + return false; + + if (!rnd.m_CurObject->IsNode()) { +- if (rnd.m_dwStyles & XFA_RESOLVENODE_Attributes) { +- return ResolveForAttributeRs(rnd.m_CurObject.Get(), rnd, ++ if (rnd.m_dwStyles & XFA_ResolveFlag::kAttributes) { ++ return ResolveForAttributeRs(rnd.m_CurObject, &rnd.m_Result, + rnd.m_wsName.AsStringView()); + } + return false; + } +- if (rnd.m_dwStyles & XFA_RESOLVENODE_AnyChild) +- return ResolveAnyChild(rnd); ++ if (rnd.m_dwStyles & XFA_ResolveFlag::kAnyChild) ++ return ResolveAnyChild(pIsolate, rnd); + + if (rnd.m_wsName.GetLength()) { + wchar_t wch = rnd.m_wsName[0]; + switch (wch) { + case '$': +- return ResolveDollar(rnd); ++ return ResolveDollar(pIsolate, rnd); + case '!': +- return ResolveExcalmatory(rnd); ++ return ResolveExcalmatory(pIsolate, rnd); + case '#': +- return ResolveNumberSign(rnd); ++ return ResolveNumberSign(pIsolate, rnd); + case '*': + return ResolveAsterisk(rnd); + // TODO(dsinclair): We could probably remove this. + case '.': +- return ResolveAnyChild(rnd); ++ return ResolveAnyChild(pIsolate, rnd); + default: + break; + } + } + if (rnd.m_uHashName == XFA_HASHCODE_This && rnd.m_nLevel == 0) { +- rnd.m_Objects.emplace_back(rnd.m_pSC->GetThisObject()); ++ rnd.m_Result.objects.emplace_back(m_pEngine->GetThisObject()); + return true; + } + if (rnd.m_CurObject->GetElementType() == XFA_Element::Xfa) { + CXFA_Object* pObjNode = +- rnd.m_pSC->GetDocument()->GetXFAObject(rnd.m_uHashName); ++ m_pEngine->GetDocument()->GetXFAObject(rnd.m_uHashName); + if (pObjNode) { +- rnd.m_Objects.emplace_back(pObjNode); ++ rnd.m_Result.objects.emplace_back(pObjNode); + } else if (rnd.m_uHashName == XFA_HASHCODE_Xfa) { +- rnd.m_Objects.push_back(rnd.m_CurObject); +- } else if ((rnd.m_dwStyles & XFA_RESOLVENODE_Attributes) && +- ResolveForAttributeRs(rnd.m_CurObject.Get(), rnd, ++ rnd.m_Result.objects.emplace_back(rnd.m_CurObject); ++ } else if ((rnd.m_dwStyles & XFA_ResolveFlag::kAttributes) && ++ ResolveForAttributeRs(rnd.m_CurObject, &rnd.m_Result, + rnd.m_wsName.AsStringView())) { + return true; + } +- if (!rnd.m_Objects.empty()) +- FilterCondition(rnd.m_wsCondition, &rnd); ++ if (!rnd.m_Result.objects.empty()) ++ FilterCondition(pIsolate, rnd.m_wsCondition, &rnd); + +- return !rnd.m_Objects.empty(); ++ return !rnd.m_Result.objects.empty(); + } +- if (!ResolveNormal(rnd) && rnd.m_uHashName == XFA_HASHCODE_Xfa) +- rnd.m_Objects.emplace_back(rnd.m_pSC->GetDocument()->GetRoot()); ++ if (!ResolveNormal(pIsolate, rnd) && rnd.m_uHashName == XFA_HASHCODE_Xfa) ++ rnd.m_Result.objects.emplace_back(m_pEngine->GetDocument()->GetRoot()); + +- return !rnd.m_Objects.empty(); ++ return !rnd.m_Result.objects.empty(); + } + +-bool CFXJSE_ResolveProcessor::ResolveAnyChild(CFXJSE_ResolveNodeData& rnd) { +- CXFA_Node* pParent = ToNode(rnd.m_CurObject.Get()); ++bool CFXJSE_ResolveProcessor::ResolveAnyChild(v8::Isolate* pIsolate, ++ NodeData& rnd) { ++ CXFA_Node* pParent = ToNode(rnd.m_CurObject); + if (!pParent) + return false; + +@@ -134,128 +107,136 @@ bool CFXJSE_ResolveProcessor::ResolveAnyChild(CFXJSE_ResolveNodeData& rnd) { + return false; + + if (wsCondition.IsEmpty()) { +- rnd.m_Objects.emplace_back(pChild); ++ rnd.m_Result.objects.emplace_back(pChild); + return true; + } + + std::vector nodes; +- for (const auto& pObject : rnd.m_Objects) ++ for (const auto& pObject : rnd.m_Result.objects) + nodes.push_back(pObject->AsNode()); + + std::vector siblings = pChild->GetSiblings(bClassName); + nodes.insert(nodes.end(), siblings.begin(), siblings.end()); +- rnd.m_Objects = +- std::vector>(nodes.begin(), nodes.end()); +- FilterCondition(wsCondition, &rnd); +- return !rnd.m_Objects.empty(); ++ rnd.m_Result.objects = ++ std::vector>(nodes.begin(), nodes.end()); ++ FilterCondition(pIsolate, wsCondition, &rnd); ++ return !rnd.m_Result.objects.empty(); + } + +-bool CFXJSE_ResolveProcessor::ResolveDollar(CFXJSE_ResolveNodeData& rnd) { ++bool CFXJSE_ResolveProcessor::ResolveDollar(v8::Isolate* pIsolate, ++ NodeData& rnd) { + WideString wsName = rnd.m_wsName; + WideString wsCondition = rnd.m_wsCondition; +- int32_t iNameLen = wsName.GetLength(); +- if (iNameLen == 1) { +- rnd.m_Objects.push_back(rnd.m_CurObject); ++ size_t nNameLen = wsName.GetLength(); ++ if (nNameLen == 1) { ++ rnd.m_Result.objects.emplace_back(rnd.m_CurObject); + return true; + } + if (rnd.m_nLevel > 0) + return false; + ++ CXFA_Document* pDocument = m_pEngine->GetDocument(); + XFA_HashCode dwNameHash = static_cast( +- FX_HashCode_GetW(wsName.AsStringView().Last(iNameLen - 1), false)); ++ FX_HashCode_GetW(wsName.AsStringView().Last(nNameLen - 1))); + if (dwNameHash == XFA_HASHCODE_Xfa) { +- rnd.m_Objects.emplace_back(rnd.m_pSC->GetDocument()->GetRoot()); ++ rnd.m_Result.objects.emplace_back(pDocument->GetRoot()); + } else { +- CXFA_Object* pObjNode = rnd.m_pSC->GetDocument()->GetXFAObject(dwNameHash); ++ CXFA_Object* pObjNode = pDocument->GetXFAObject(dwNameHash); + if (pObjNode) +- rnd.m_Objects.emplace_back(pObjNode); ++ rnd.m_Result.objects.emplace_back(pObjNode); + } +- if (!rnd.m_Objects.empty()) +- FilterCondition(wsCondition, &rnd); +- return !rnd.m_Objects.empty(); ++ if (!rnd.m_Result.objects.empty()) ++ FilterCondition(pIsolate, wsCondition, &rnd); ++ return !rnd.m_Result.objects.empty(); + } + +-bool CFXJSE_ResolveProcessor::ResolveExcalmatory(CFXJSE_ResolveNodeData& rnd) { ++bool CFXJSE_ResolveProcessor::ResolveExcalmatory(v8::Isolate* pIsolate, ++ NodeData& rnd) { + if (rnd.m_nLevel > 0) + return false; + + CXFA_Node* datasets = +- ToNode(rnd.m_pSC->GetDocument()->GetXFAObject(XFA_HASHCODE_Datasets)); ++ ToNode(m_pEngine->GetDocument()->GetXFAObject(XFA_HASHCODE_Datasets)); + if (!datasets) + return false; + +- CFXJSE_ResolveNodeData rndFind(rnd.m_pSC.Get()); ++ NodeData rndFind; + rndFind.m_CurObject = datasets; + rndFind.m_wsName = rnd.m_wsName.Last(rnd.m_wsName.GetLength() - 1); + rndFind.m_uHashName = static_cast( +- FX_HashCode_GetW(rndFind.m_wsName.AsStringView(), false)); ++ FX_HashCode_GetW(rndFind.m_wsName.AsStringView())); + rndFind.m_nLevel = rnd.m_nLevel + 1; +- rndFind.m_dwStyles = XFA_RESOLVENODE_Children; ++ rndFind.m_dwStyles = XFA_ResolveFlag::kChildren; + rndFind.m_wsCondition = rnd.m_wsCondition; +- Resolve(rndFind); ++ Resolve(pIsolate, rndFind); + +- rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(), +- rndFind.m_Objects.end()); +- return !rnd.m_Objects.empty(); ++ rnd.m_Result.objects.insert(rnd.m_Result.objects.end(), ++ rndFind.m_Result.objects.begin(), ++ rndFind.m_Result.objects.end()); ++ return !rnd.m_Result.objects.empty(); + } + +-bool CFXJSE_ResolveProcessor::ResolveNumberSign(CFXJSE_ResolveNodeData& rnd) { ++bool CFXJSE_ResolveProcessor::ResolveNumberSign(v8::Isolate* pIsolate, ++ NodeData& rnd) { + WideString wsName = rnd.m_wsName.Last(rnd.m_wsName.GetLength() - 1); + WideString wsCondition = rnd.m_wsCondition; +- CXFA_Node* curNode = ToNode(rnd.m_CurObject.Get()); +- if (ResolveForAttributeRs(curNode, rnd, wsName.AsStringView())) ++ CXFA_Node* curNode = ToNode(rnd.m_CurObject); ++ if (ResolveForAttributeRs(curNode, &rnd.m_Result, wsName.AsStringView())) + return true; + +- CFXJSE_ResolveNodeData rndFind(rnd.m_pSC.Get()); ++ NodeData rndFind; + rndFind.m_nLevel = rnd.m_nLevel + 1; + rndFind.m_dwStyles = rnd.m_dwStyles; +- rndFind.m_dwStyles |= XFA_RESOLVENODE_TagName; +- rndFind.m_dwStyles &= ~XFA_RESOLVENODE_Attributes; ++ rndFind.m_dwStyles |= XFA_ResolveFlag::kTagName; ++ rndFind.m_dwStyles.Clear(XFA_ResolveFlag::kAttributes); + rndFind.m_wsName = std::move(wsName); + rndFind.m_uHashName = static_cast( +- FX_HashCode_GetW(rndFind.m_wsName.AsStringView(), false)); ++ FX_HashCode_GetW(rndFind.m_wsName.AsStringView())); + rndFind.m_wsCondition = wsCondition; + rndFind.m_CurObject = curNode; +- ResolveNormal(rndFind); +- if (rndFind.m_Objects.empty()) ++ ResolveNormal(pIsolate, rndFind); ++ if (rndFind.m_Result.objects.empty()) + return false; + + if (wsCondition.IsEmpty() && +- pdfium::ContainsValue(rndFind.m_Objects, curNode)) { +- rnd.m_Objects.emplace_back(curNode); ++ pdfium::Contains(rndFind.m_Result.objects, curNode)) { ++ rnd.m_Result.objects.emplace_back(curNode); + } else { +- rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(), +- rndFind.m_Objects.end()); ++ rnd.m_Result.objects.insert(rnd.m_Result.objects.end(), ++ rndFind.m_Result.objects.begin(), ++ rndFind.m_Result.objects.end()); + } +- return !rnd.m_Objects.empty(); ++ return !rnd.m_Result.objects.empty(); + } + +-bool CFXJSE_ResolveProcessor::ResolveForAttributeRs(CXFA_Object* curNode, +- CFXJSE_ResolveNodeData& rnd, +- WideStringView strAttr) { +- Optional info = ++bool CFXJSE_ResolveProcessor::ResolveForAttributeRs( ++ CXFA_Object* curNode, ++ CFXJSE_Engine::ResolveResult* rnd, ++ WideStringView strAttr) { ++ absl::optional info = + XFA_GetScriptAttributeByName(curNode->GetElementType(), strAttr); + if (!info.has_value()) + return false; + +- rnd.m_ScriptAttribute = info.value(); +- rnd.m_Objects.emplace_back(curNode); +- rnd.m_dwFlag = XFA_ResolveNode_RSType_Attribute; ++ rnd->type = CFXJSE_Engine::ResolveResult::Type::kAttribute; ++ rnd->script_attribute = info.value(); ++ rnd->objects.emplace_back(curNode); + return true; + } + +-bool CFXJSE_ResolveProcessor::ResolveNormal(CFXJSE_ResolveNodeData& rnd) { ++bool CFXJSE_ResolveProcessor::ResolveNormal(v8::Isolate* pIsolate, ++ NodeData& rnd) { + if (rnd.m_nLevel > 32 || !rnd.m_CurObject->IsNode()) + return false; + + CXFA_Node* curNode = rnd.m_CurObject->AsNode(); +- size_t nNum = rnd.m_Objects.size(); +- uint32_t dwStyles = rnd.m_dwStyles; ++ size_t nNum = rnd.m_Result.objects.size(); ++ Mask dwStyles = rnd.m_dwStyles; + WideString& wsName = rnd.m_wsName; + XFA_HashCode uNameHash = rnd.m_uHashName; + WideString& wsCondition = rnd.m_wsCondition; + +- CFXJSE_ResolveNodeData rndFind(rnd.m_pSC.Get()); ++ NodeData rndFind; + rndFind.m_wsName = rnd.m_wsName; + rndFind.m_wsCondition = rnd.m_wsCondition; + rndFind.m_nLevel = rnd.m_nLevel + 1; +@@ -280,36 +261,37 @@ bool CFXJSE_ResolveProcessor::ResolveNormal(CFXJSE_ResolveNodeData& rnd) { + else + children.push_back(pChild); + } +- if ((dwStyles & XFA_RESOLVENODE_Properties) && pVariablesNode) { ++ if ((dwStyles & XFA_ResolveFlag::kProperties) && pVariablesNode) { + if (pVariablesNode->GetClassHashCode() == uNameHash) { +- rnd.m_Objects.emplace_back(pVariablesNode); ++ rnd.m_Result.objects.emplace_back(pVariablesNode); + } else { + rndFind.m_CurObject = pVariablesNode; + SetStylesForChild(dwStyles, rndFind); + WideString wsSaveCondition = std::move(rndFind.m_wsCondition); +- ResolveNormal(rndFind); ++ ResolveNormal(pIsolate, rndFind); + rndFind.m_wsCondition = std::move(wsSaveCondition); +- rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(), +- rndFind.m_Objects.end()); +- rndFind.m_Objects.clear(); ++ rnd.m_Result.objects.insert(rnd.m_Result.objects.end(), ++ rndFind.m_Result.objects.begin(), ++ rndFind.m_Result.objects.end()); ++ rndFind.m_Result.objects.clear(); + } +- if (rnd.m_Objects.size() > nNum) { +- FilterCondition(wsCondition, &rnd); +- return !rnd.m_Objects.empty(); ++ if (rnd.m_Result.objects.size() > nNum) { ++ FilterCondition(pIsolate, wsCondition, &rnd); ++ return !rnd.m_Result.objects.empty(); + } + } + +- if (dwStyles & XFA_RESOLVENODE_Children) { ++ if (dwStyles & XFA_ResolveFlag::kChildren) { + bool bSetFlag = false; +- if (pPageSetNode && (dwStyles & XFA_RESOLVENODE_Properties)) ++ if (pPageSetNode && (dwStyles & XFA_ResolveFlag::kProperties)) + children.push_back(pPageSetNode); + + for (CXFA_Node* child : children) { +- if (dwStyles & XFA_RESOLVENODE_TagName) { ++ if (dwStyles & XFA_ResolveFlag::kTagName) { + if (child->GetClassHashCode() == uNameHash) +- rnd.m_Objects.emplace_back(child); ++ rnd.m_Result.objects.emplace_back(child); + } else if (child->GetNameHash() == uNameHash) { +- rnd.m_Objects.emplace_back(child); ++ rnd.m_Result.objects.emplace_back(child); + } + + if (child->GetElementType() != XFA_Element::PageSet && +@@ -321,54 +303,55 @@ bool CFXJSE_ResolveProcessor::ResolveNormal(CFXJSE_ResolveNodeData& rnd) { + rndFind.m_CurObject = child; + + WideString wsSaveCondition = std::move(rndFind.m_wsCondition); +- ResolveNormal(rndFind); ++ ResolveNormal(pIsolate, rndFind); + rndFind.m_wsCondition = std::move(wsSaveCondition); +- rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(), +- rndFind.m_Objects.end()); +- rndFind.m_Objects.clear(); ++ rnd.m_Result.objects.insert(rnd.m_Result.objects.end(), ++ rndFind.m_Result.objects.begin(), ++ rndFind.m_Result.objects.end()); ++ rndFind.m_Result.objects.clear(); + } + } +- if (rnd.m_Objects.size() > nNum) { +- if (!(dwStyles & XFA_RESOLVENODE_ALL)) { ++ if (rnd.m_Result.objects.size() > nNum) { ++ if (!(dwStyles & XFA_ResolveFlag::kALL)) { + std::vector upArrayNodes; + if (curNode->IsTransparent()) { +- CXFA_Node* pCurrent = ToNode(rnd.m_Objects.front().Get()); ++ CXFA_Node* pCurrent = ToNode(rnd.m_Result.objects.front().Get()); + if (pCurrent) { + upArrayNodes = +- pCurrent->GetSiblings(!!(dwStyles & XFA_RESOLVENODE_TagName)); ++ pCurrent->GetSiblings(!!(dwStyles & XFA_ResolveFlag::kTagName)); + } + } +- if (upArrayNodes.size() > rnd.m_Objects.size()) { +- CXFA_Object* pSaveObject = rnd.m_Objects.front().Get(); +- rnd.m_Objects = std::vector>( ++ if (upArrayNodes.size() > rnd.m_Result.objects.size()) { ++ CXFA_Object* pSaveObject = rnd.m_Result.objects.front().Get(); ++ rnd.m_Result.objects = std::vector>( + upArrayNodes.begin(), upArrayNodes.end()); +- rnd.m_Objects.front() = pSaveObject; ++ rnd.m_Result.objects.front() = pSaveObject; + } + } +- FilterCondition(wsCondition, &rnd); +- return !rnd.m_Objects.empty(); ++ FilterCondition(pIsolate, wsCondition, &rnd); ++ return !rnd.m_Result.objects.empty(); + } + } +- if (dwStyles & XFA_RESOLVENODE_Attributes) { +- if (ResolveForAttributeRs(curNode, rnd, wsName.AsStringView())) +- return 1; ++ if (dwStyles & XFA_ResolveFlag::kAttributes) { ++ if (ResolveForAttributeRs(curNode, &rnd.m_Result, wsName.AsStringView())) ++ return true; + } +- if (dwStyles & XFA_RESOLVENODE_Properties) { ++ if (dwStyles & XFA_ResolveFlag::kProperties) { + for (CXFA_Node* pChildProperty : properties) { + if (pChildProperty->IsUnnamed()) { + if (pChildProperty->GetClassHashCode() == uNameHash) +- rnd.m_Objects.emplace_back(pChildProperty); ++ rnd.m_Result.objects.emplace_back(pChildProperty); + continue; + } + if (pChildProperty->GetNameHash() == uNameHash && + pChildProperty->GetElementType() != XFA_Element::Extras && + pChildProperty->GetElementType() != XFA_Element::Items) { +- rnd.m_Objects.emplace_back(pChildProperty); ++ rnd.m_Result.objects.emplace_back(pChildProperty); + } + } +- if (rnd.m_Objects.size() > nNum) { +- FilterCondition(wsCondition, &rnd); +- return !rnd.m_Objects.empty(); ++ if (rnd.m_Result.objects.size() > nNum) { ++ FilterCondition(pIsolate, wsCondition, &rnd); ++ return !rnd.m_Result.objects.empty(); + } + + CXFA_Node* pProp = nullptr; +@@ -388,8 +371,8 @@ bool CFXJSE_ResolveProcessor::ResolveNormal(CFXJSE_ResolveNodeData& rnd) { + } + } + if (pProp) { +- rnd.m_Objects.emplace_back(pProp); +- return !rnd.m_Objects.empty(); ++ rnd.m_Result.objects.emplace_back(pProp); ++ return !rnd.m_Result.objects.empty(); + } + } + +@@ -397,35 +380,35 @@ bool CFXJSE_ResolveProcessor::ResolveNormal(CFXJSE_ResolveNodeData& rnd) { + uint32_t uCurClassHash = curNode->GetClassHashCode(); + if (!parentNode) { + if (uCurClassHash == uNameHash) { +- rnd.m_Objects.emplace_back(curNode); +- FilterCondition(wsCondition, &rnd); +- if (!rnd.m_Objects.empty()) ++ rnd.m_Result.objects.emplace_back(curNode); ++ FilterCondition(pIsolate, wsCondition, &rnd); ++ if (!rnd.m_Result.objects.empty()) + return true; + } + return false; + } + +- if (dwStyles & XFA_RESOLVENODE_Siblings) { ++ if (dwStyles & XFA_ResolveFlag::kSiblings) { + CXFA_Node* child = parentNode->GetFirstChild(); +- uint32_t dwSubStyles = +- XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties; +- if (dwStyles & XFA_RESOLVENODE_TagName) +- dwSubStyles |= XFA_RESOLVENODE_TagName; +- if (dwStyles & XFA_RESOLVENODE_ALL) +- dwSubStyles |= XFA_RESOLVENODE_ALL; ++ Mask dwSubStyles = {XFA_ResolveFlag::kChildren, ++ XFA_ResolveFlag::kProperties}; ++ if (dwStyles & XFA_ResolveFlag::kTagName) ++ dwSubStyles |= XFA_ResolveFlag::kTagName; ++ if (dwStyles & XFA_ResolveFlag::kALL) ++ dwSubStyles |= XFA_ResolveFlag::kALL; + + rndFind.m_dwStyles = dwSubStyles; + while (child) { + if (child == curNode) { +- if (dwStyles & XFA_RESOLVENODE_TagName) { ++ if (dwStyles & XFA_ResolveFlag::kTagName) { + if (uCurClassHash == uNameHash) +- rnd.m_Objects.emplace_back(curNode); ++ rnd.m_Result.objects.emplace_back(curNode); + } else { + if (child->GetNameHash() == uNameHash) { +- rnd.m_Objects.emplace_back(curNode); ++ rnd.m_Result.objects.emplace_back(curNode); + if (rnd.m_nLevel == 0 && wsCondition.IsEmpty()) { +- rnd.m_Objects.clear(); +- rnd.m_Objects.emplace_back(curNode); ++ rnd.m_Result.objects.clear(); ++ rnd.m_Result.objects.emplace_back(curNode); + return true; + } + } +@@ -434,11 +417,11 @@ bool CFXJSE_ResolveProcessor::ResolveNormal(CFXJSE_ResolveNodeData& rnd) { + continue; + } + +- if (dwStyles & XFA_RESOLVENODE_TagName) { ++ if (dwStyles & XFA_ResolveFlag::kTagName) { + if (child->GetClassHashCode() == uNameHash) +- rnd.m_Objects.emplace_back(child); ++ rnd.m_Result.objects.emplace_back(child); + } else if (child->GetNameHash() == uNameHash) { +- rnd.m_Objects.emplace_back(child); ++ rnd.m_Result.objects.emplace_back(child); + } + + bool bInnerSearch = false; +@@ -453,70 +436,74 @@ bool CFXJSE_ResolveProcessor::ResolveNormal(CFXJSE_ResolveNodeData& rnd) { + if (bInnerSearch) { + rndFind.m_CurObject = child; + WideString wsOriginCondition = std::move(rndFind.m_wsCondition); +- uint32_t dwOriginStyle = rndFind.m_dwStyles; +- rndFind.m_dwStyles = dwOriginStyle | XFA_RESOLVENODE_ALL; +- ResolveNormal(rndFind); ++ Mask dwOriginStyle = rndFind.m_dwStyles; ++ rndFind.m_dwStyles = dwOriginStyle | XFA_ResolveFlag::kALL; ++ ResolveNormal(pIsolate, rndFind); + rndFind.m_dwStyles = dwOriginStyle; + rndFind.m_wsCondition = std::move(wsOriginCondition); +- rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(), +- rndFind.m_Objects.end()); +- rndFind.m_Objects.clear(); ++ rnd.m_Result.objects.insert(rnd.m_Result.objects.end(), ++ rndFind.m_Result.objects.begin(), ++ rndFind.m_Result.objects.end()); ++ rndFind.m_Result.objects.clear(); + } + child = child->GetNextSibling(); + } +- if (rnd.m_Objects.size() > nNum) { ++ if (rnd.m_Result.objects.size() > nNum) { + if (parentNode->IsTransparent()) { + std::vector upArrayNodes; +- CXFA_Node* pCurrent = ToNode(rnd.m_Objects.front().Get()); ++ CXFA_Node* pCurrent = ToNode(rnd.m_Result.objects.front().Get()); + if (pCurrent) { + upArrayNodes = +- pCurrent->GetSiblings(!!(dwStyles & XFA_RESOLVENODE_TagName)); ++ pCurrent->GetSiblings(!!(dwStyles & XFA_ResolveFlag::kTagName)); + } +- if (upArrayNodes.size() > rnd.m_Objects.size()) { +- CXFA_Object* pSaveObject = rnd.m_Objects.front().Get(); +- rnd.m_Objects = std::vector>( ++ if (upArrayNodes.size() > rnd.m_Result.objects.size()) { ++ CXFA_Object* pSaveObject = rnd.m_Result.objects.front().Get(); ++ rnd.m_Result.objects = std::vector>( + upArrayNodes.begin(), upArrayNodes.end()); +- rnd.m_Objects.front() = pSaveObject; ++ rnd.m_Result.objects.front() = pSaveObject; + } + } +- FilterCondition(wsCondition, &rnd); +- return !rnd.m_Objects.empty(); ++ FilterCondition(pIsolate, wsCondition, &rnd); ++ return !rnd.m_Result.objects.empty(); + } + } + +- if (dwStyles & XFA_RESOLVENODE_Parent) { +- uint32_t dwSubStyles = XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent | +- XFA_RESOLVENODE_Properties; +- if (dwStyles & XFA_RESOLVENODE_TagName) +- dwSubStyles |= XFA_RESOLVENODE_TagName; +- if (dwStyles & XFA_RESOLVENODE_ALL) +- dwSubStyles |= XFA_RESOLVENODE_ALL; ++ if (dwStyles & XFA_ResolveFlag::kParent) { ++ Mask dwSubStyles = {XFA_ResolveFlag::kSiblings, ++ XFA_ResolveFlag::kParent, ++ XFA_ResolveFlag::kProperties}; ++ if (dwStyles & XFA_ResolveFlag::kTagName) ++ dwSubStyles |= XFA_ResolveFlag::kTagName; ++ if (dwStyles & XFA_ResolveFlag::kALL) ++ dwSubStyles |= XFA_ResolveFlag::kALL; + ++ m_pEngine->AddObjectToUpArray(parentNode); + rndFind.m_dwStyles = dwSubStyles; + rndFind.m_CurObject = parentNode; +- rnd.m_pSC->GetUpObjectArray()->push_back(parentNode); +- ResolveNormal(rndFind); +- rnd.m_Objects.insert(rnd.m_Objects.end(), rndFind.m_Objects.begin(), +- rndFind.m_Objects.end()); +- rndFind.m_Objects.clear(); +- if (rnd.m_Objects.size() > nNum) ++ ResolveNormal(pIsolate, rndFind); ++ rnd.m_Result.objects.insert(rnd.m_Result.objects.end(), ++ rndFind.m_Result.objects.begin(), ++ rndFind.m_Result.objects.end()); ++ rndFind.m_Result.objects.clear(); ++ if (rnd.m_Result.objects.size() > nNum) + return true; + } + return false; + } + +-bool CFXJSE_ResolveProcessor::ResolveAsterisk(CFXJSE_ResolveNodeData& rnd) { +- CXFA_Node* curNode = ToNode(rnd.m_CurObject.Get()); ++bool CFXJSE_ResolveProcessor::ResolveAsterisk(NodeData& rnd) { ++ CXFA_Node* curNode = ToNode(rnd.m_CurObject); + std::vector array = curNode->GetNodeListWithFilter( +- XFA_NODEFILTER_Children | XFA_NODEFILTER_Properties); +- rnd.m_Objects.insert(rnd.m_Objects.end(), array.begin(), array.end()); +- return !rnd.m_Objects.empty(); ++ {XFA_NodeFilter::kChildren, XFA_NodeFilter::kProperties}); ++ rnd.m_Result.objects.insert(rnd.m_Result.objects.end(), array.begin(), ++ array.end()); ++ return !rnd.m_Result.objects.empty(); + } + + int32_t CFXJSE_ResolveProcessor::GetFilter(WideStringView wsExpression, + int32_t nStart, +- CFXJSE_ResolveNodeData& rnd) { +- ASSERT(nStart > -1); ++ NodeData& rnd) { ++ DCHECK(nStart > -1); + + int32_t iLength = wsExpression.GetLength(); + if (nStart >= iLength) +@@ -541,7 +528,7 @@ int32_t CFXJSE_ResolveProcessor::GetFilter(WideStringView wsExpression, + wCur = pSrc[nStart++]; + if (wCur == '.') { + if (nNameCount == 0) { +- rnd.m_dwStyles |= XFA_RESOLVENODE_AnyChild; ++ rnd.m_dwStyles |= XFA_ResolveFlag::kAnyChild; + continue; + } + if (wPrev == '\\') { +@@ -589,14 +576,14 @@ int32_t CFXJSE_ResolveProcessor::GetFilter(WideStringView wsExpression, + wsName.Trim(); + wsCondition.Trim(); + rnd.m_uHashName = +- static_cast(FX_HashCode_GetW(wsName.AsStringView(), false)); ++ static_cast(FX_HashCode_GetW(wsName.AsStringView())); + return nStart; + } + + void CFXJSE_ResolveProcessor::ConditionArray(size_t iCurIndex, + WideString wsCondition, + size_t iFoundCount, +- CFXJSE_ResolveNodeData* pRnd) { ++ NodeData* pRnd) { + size_t iLen = wsCondition.GetLength(); + bool bRelative = false; + bool bAll = false; +@@ -613,18 +600,18 @@ void CFXJSE_ResolveProcessor::ConditionArray(size_t iCurIndex, + break; + } + if (bAll) { +- if (pRnd->m_dwStyles & XFA_RESOLVENODE_CreateNode) { +- if (pRnd->m_dwStyles & XFA_RESOLVENODE_Bind) { +- m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject.Get()); ++ if (pRnd->m_dwStyles & XFA_ResolveFlag::kCreateNode) { ++ if (pRnd->m_dwStyles & XFA_ResolveFlag::kBind) { ++ m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject); + m_pNodeHelper->m_iCreateCount = 1; +- pRnd->m_Objects.clear(); ++ pRnd->m_Result.objects.clear(); + m_pNodeHelper->m_iCurAllStart = -1; + m_pNodeHelper->m_pAllStartParent = nullptr; + } else if (m_pNodeHelper->m_iCurAllStart == -1) { + m_pNodeHelper->m_iCurAllStart = m_iCurStart; +- m_pNodeHelper->m_pAllStartParent = ToNode(pRnd->m_CurObject.Get()); ++ m_pNodeHelper->m_pAllStartParent = ToNode(pRnd->m_CurObject); + } +- } else if (pRnd->m_dwStyles & XFA_RESOLVENODE_BindNew) { ++ } else if (pRnd->m_dwStyles & XFA_ResolveFlag::kBindNew) { + if (m_pNodeHelper->m_iCurAllStart == -1) + m_pNodeHelper->m_iCurAllStart = m_iCurStart; + } +@@ -638,51 +625,51 @@ void CFXJSE_ResolveProcessor::ConditionArray(size_t iCurIndex, + iIndex += iCurIndex; + + if (iIndex < 0 || static_cast(iIndex) >= iFoundCount) { +- if (pRnd->m_dwStyles & XFA_RESOLVENODE_CreateNode) { +- m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject.Get()); ++ if (pRnd->m_dwStyles & XFA_ResolveFlag::kCreateNode) { ++ m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject); + m_pNodeHelper->m_iCreateCount = iIndex - iFoundCount + 1; + } +- pRnd->m_Objects.clear(); ++ pRnd->m_Result.objects.clear(); + } else { +- pRnd->m_Objects = +- std::vector>(1, pRnd->m_Objects[iIndex]); ++ pRnd->m_Result.objects = std::vector>( ++ 1, pRnd->m_Result.objects[iIndex]); + } + } + +-void CFXJSE_ResolveProcessor::FilterCondition(WideString wsCondition, +- CFXJSE_ResolveNodeData* pRnd) { ++void CFXJSE_ResolveProcessor::FilterCondition(v8::Isolate* pIsolate, ++ WideString wsCondition, ++ NodeData* pRnd) { + size_t iCurIndex = 0; +- const std::vector* pArray = pRnd->m_pSC->GetUpObjectArray(); +- if (!pArray->empty()) { +- CXFA_Node* pNode = pArray->back(); +- bool bIsProperty = pNode->IsProperty(); +- bool bIsClassIndex = ++ CXFA_Node* pNode = m_pEngine->LastObjectFromUpArray(); ++ if (pNode) { ++ const bool bIsProperty = pNode->IsProperty(); ++ const bool bIsClassIndex = + pNode->IsUnnamed() || + (bIsProperty && pNode->GetElementType() != XFA_Element::PageSet); + iCurIndex = pNode->GetIndex(bIsProperty, bIsClassIndex); + } + +- size_t iFoundCount = pRnd->m_Objects.size(); ++ size_t iFoundCount = pRnd->m_Result.objects.size(); + wsCondition.Trim(); + +- int32_t iLen = wsCondition.GetLength(); +- if (!iLen) { +- if (pRnd->m_dwStyles & XFA_RESOLVENODE_ALL) ++ const size_t nLen = wsCondition.GetLength(); ++ if (nLen == 0) { ++ if (pRnd->m_dwStyles & XFA_ResolveFlag::kALL) + return; + if (iFoundCount == 1) + return; + + if (iFoundCount <= iCurIndex) { +- if (pRnd->m_dwStyles & XFA_RESOLVENODE_CreateNode) { +- m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject.Get()); ++ if (pRnd->m_dwStyles & XFA_ResolveFlag::kCreateNode) { ++ m_pNodeHelper->m_pCreateParent = ToNode(pRnd->m_CurObject); + m_pNodeHelper->m_iCreateCount = iCurIndex - iFoundCount + 1; + } +- pRnd->m_Objects.clear(); ++ pRnd->m_Result.objects.clear(); + return; + } + +- pRnd->m_Objects = +- std::vector>(1, pRnd->m_Objects[iCurIndex]); ++ pRnd->m_Result.objects = std::vector>( ++ 1, pRnd->m_Result.objects[iCurIndex]); + return; + } + +@@ -692,8 +679,8 @@ void CFXJSE_ResolveProcessor::FilterCondition(WideString wsCondition, + ConditionArray(iCurIndex, wsCondition, iFoundCount, pRnd); + return; + case '.': +- if (iLen > 1 && (wsCondition[1] == '[' || wsCondition[1] == '(')) +- DoPredicateFilter(wsCondition, iFoundCount, pRnd); ++ if (nLen > 1 && (wsCondition[1] == '[' || wsCondition[1] == '(')) ++ DoPredicateFilter(pIsolate, wsCondition, iFoundCount, pRnd); + return; + case '(': + case '"': +@@ -702,34 +689,50 @@ void CFXJSE_ResolveProcessor::FilterCondition(WideString wsCondition, + } + } + +-void CFXJSE_ResolveProcessor::SetStylesForChild(uint32_t dwParentStyles, +- CFXJSE_ResolveNodeData& rnd) { +- uint32_t dwSubStyles = XFA_RESOLVENODE_Children; +- if (dwParentStyles & XFA_RESOLVENODE_TagName) +- dwSubStyles |= XFA_RESOLVENODE_TagName; +- +- dwSubStyles &= ~XFA_RESOLVENODE_Parent; +- dwSubStyles &= ~XFA_RESOLVENODE_Siblings; +- dwSubStyles &= ~XFA_RESOLVENODE_Properties; +- dwSubStyles |= XFA_RESOLVENODE_ALL; ++void CFXJSE_ResolveProcessor::SetStylesForChild( ++ Mask dwParentStyles, ++ NodeData& rnd) { ++ Mask dwSubStyles = {XFA_ResolveFlag::kChildren, ++ XFA_ResolveFlag::kALL}; ++ if (dwParentStyles & XFA_ResolveFlag::kTagName) ++ dwSubStyles |= XFA_ResolveFlag::kTagName; + rnd.m_dwStyles = dwSubStyles; + } + +-void CFXJSE_ResolveProcessor::SetIndexDataBind(WideString& wsNextCondition, +- int32_t& iIndex, +- int32_t iCount) { +- if (m_pNodeHelper->CreateNodeForCondition(wsNextCondition)) { +- if (m_pNodeHelper->m_eLastCreateType == XFA_Element::DataGroup) { +- iIndex = 0; +- } else { +- iIndex = iCount - 1; +- } +- } else { +- iIndex = iCount - 1; ++int32_t CFXJSE_ResolveProcessor::IndexForDataBind( ++ const WideString& wsNextCondition, ++ int32_t iCount) { ++ if (m_pNodeHelper->CreateNodeForCondition(wsNextCondition) && ++ m_pNodeHelper->m_eLastCreateType == XFA_Element::DataGroup) { ++ return 0; ++ } ++ return iCount - 1; ++} ++ ++void CFXJSE_ResolveProcessor::DoPredicateFilter(v8::Isolate* pIsolate, ++ WideString wsCondition, ++ size_t iFoundCount, ++ NodeData* pRnd) { ++ DCHECK_EQ(iFoundCount, pRnd->m_Result.objects.size()); ++ CXFA_Script::Type eLangType = CXFA_Script::Type::Unknown; ++ if (wsCondition.First(2).EqualsASCII(".[") && wsCondition.Back() == L']') ++ eLangType = CXFA_Script::Type::Formcalc; ++ else if (wsCondition.First(2).EqualsASCII(".(") && wsCondition.Back() == L')') ++ eLangType = CXFA_Script::Type::Javascript; ++ else ++ return; ++ ++ WideString wsExpression = wsCondition.Substr(2, wsCondition.GetLength() - 3); ++ for (size_t i = iFoundCount; i > 0; --i) { ++ auto pRetValue = std::make_unique(); ++ bool bRet = m_pEngine->RunScript(eLangType, wsExpression.AsStringView(), ++ pRetValue.get(), ++ pRnd->m_Result.objects[i - 1].Get()); ++ if (!bRet || !pRetValue->ToBoolean(pIsolate)) ++ pRnd->m_Result.objects.erase(pRnd->m_Result.objects.begin() + i - 1); + } + } + +-CFXJSE_ResolveNodeData::CFXJSE_ResolveNodeData(CFXJSE_Engine* pSC) +- : m_pSC(pSC) {} ++CFXJSE_ResolveProcessor::NodeData::NodeData() = default; + +-CFXJSE_ResolveNodeData::~CFXJSE_ResolveNodeData() = default; ++CFXJSE_ResolveProcessor::NodeData::~NodeData() = default; +diff --git a/fxjs/xfa/cfxjse_resolveprocessor.h b/fxjs/xfa/cfxjse_resolveprocessor.h +index 6e4fcba73..f7125c9cf 100644 +--- a/fxjs/xfa/cfxjse_resolveprocessor.h ++++ b/fxjs/xfa/cfxjse_resolveprocessor.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,69 +8,69 @@ + #define FXJS_XFA_CFXJSE_RESOLVEPROCESSOR_H_ + + #include +-#include + +-#include "core/fxcrt/fx_string.h" ++#include "core/fxcrt/unowned_ptr.h" ++#include "core/fxcrt/widestring.h" + #include "fxjs/xfa/cfxjse_engine.h" ++#include "v8/include/cppgc/macros.h" + #include "xfa/fxfa/fxfa_basic.h" + #include "xfa/fxfa/parser/xfa_basic_data.h" +-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h" + +-class CXFA_NodeHelper; ++class CFXJSE_NodeHelper; + +-class CFXJSE_ResolveNodeData { ++class CFXJSE_ResolveProcessor { + public: +- explicit CFXJSE_ResolveNodeData(CFXJSE_Engine* pSC); +- ~CFXJSE_ResolveNodeData(); ++ class NodeData { ++ CPPGC_STACK_ALLOCATED(); // Allows Raw/Unowned pointers. + +- UnownedPtr const m_pSC; +- UnownedPtr m_CurObject; +- WideString m_wsName; +- WideString m_wsCondition; +- XFA_HashCode m_uHashName = XFA_HASHCODE_None; +- int32_t m_nLevel = 0; +- uint32_t m_dwStyles = XFA_RESOLVENODE_Children; +- XFA_ResolveNode_RSType m_dwFlag = XFA_ResolveNode_RSType_Nodes; +- std::vector> m_Objects; +- XFA_SCRIPTATTRIBUTEINFO m_ScriptAttribute; +-}; ++ public: ++ NodeData(); ++ ~NodeData(); + +-class CFXJSE_ResolveProcessor { +- public: +- CFXJSE_ResolveProcessor(); ++ UnownedPtr m_CurObject; // Ok, stack-only. ++ WideString m_wsName; ++ WideString m_wsCondition; ++ XFA_HashCode m_uHashName = XFA_HASHCODE_None; ++ int32_t m_nLevel = 0; ++ Mask m_dwStyles = XFA_ResolveFlag::kChildren; ++ CFXJSE_Engine::ResolveResult m_Result; ++ }; ++ ++ CFXJSE_ResolveProcessor(CFXJSE_Engine* pEngine, CFXJSE_NodeHelper* pHelper); + ~CFXJSE_ResolveProcessor(); + +- bool Resolve(CFXJSE_ResolveNodeData& rnd); +- int32_t GetFilter(WideStringView wsExpression, +- int32_t nStart, +- CFXJSE_ResolveNodeData& rnd); +- void SetIndexDataBind(WideString& wsNextCondition, +- int32_t& iIndex, +- int32_t iCount); ++ bool Resolve(v8::Isolate* pIsolate, NodeData& rnd); ++ int32_t GetFilter(WideStringView wsExpression, int32_t nStart, NodeData& rnd); ++ int32_t IndexForDataBind(const WideString& wsNextCondition, int32_t iCount); + void SetCurStart(int32_t start) { m_iCurStart = start; } + +- CXFA_NodeHelper* GetNodeHelper() { return m_pNodeHelper.get(); } +- + private: + bool ResolveForAttributeRs(CXFA_Object* curNode, +- CFXJSE_ResolveNodeData& rnd, ++ CFXJSE_Engine::ResolveResult* rnd, + WideStringView strAttr); +- bool ResolveAnyChild(CFXJSE_ResolveNodeData& rnd); +- bool ResolveDollar(CFXJSE_ResolveNodeData& rnd); +- bool ResolveExcalmatory(CFXJSE_ResolveNodeData& rnd); +- bool ResolveNumberSign(CFXJSE_ResolveNodeData& rnd); +- bool ResolveAsterisk(CFXJSE_ResolveNodeData& rnd); +- bool ResolveNormal(CFXJSE_ResolveNodeData& rnd); +- void SetStylesForChild(uint32_t dwParentStyles, CFXJSE_ResolveNodeData& rnd); ++ bool ResolveAnyChild(v8::Isolate* pIsolate, NodeData& rnd); ++ bool ResolveDollar(v8::Isolate* pIsolate, NodeData& rnd); ++ bool ResolveExcalmatory(v8::Isolate* pIsolate, NodeData& rnd); ++ bool ResolveNumberSign(v8::Isolate* pIsolate, NodeData& rnd); ++ bool ResolveAsterisk(NodeData& rnd); ++ bool ResolveNormal(v8::Isolate* pIsolate, NodeData& rnd); ++ void SetStylesForChild(Mask dwParentStyles, NodeData& rnd); + + void ConditionArray(size_t iCurIndex, + WideString wsCondition, + size_t iFoundCount, +- CFXJSE_ResolveNodeData* pRnd); +- void FilterCondition(WideString wsCondition, CFXJSE_ResolveNodeData* pRnd); ++ NodeData* pRnd); ++ void FilterCondition(v8::Isolate* pIsolate, ++ WideString wsCondition, ++ NodeData* pRnd); ++ void DoPredicateFilter(v8::Isolate* pIsolate, ++ WideString wsCondition, ++ size_t iFoundCount, ++ NodeData* pRnd); + + int32_t m_iCurStart = 0; +- std::unique_ptr const m_pNodeHelper; ++ UnownedPtr const m_pEngine; ++ UnownedPtr const m_pNodeHelper; + }; + + #endif // FXJS_XFA_CFXJSE_RESOLVEPROCESSOR_H_ +diff --git a/fxjs/xfa/cfxjse_runtimedata.cpp b/fxjs/xfa/cfxjse_runtimedata.cpp +index 0478e3e6c..b6a24c4b6 100644 +--- a/fxjs/xfa/cfxjse_runtimedata.cpp ++++ b/fxjs/xfa/cfxjse_runtimedata.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,7 +9,15 @@ + #include + + #include "fxjs/cfxjs_engine.h" ++#include "fxjs/fxv8.h" + #include "fxjs/xfa/cfxjse_isolatetracker.h" ++#include "third_party/base/check_op.h" ++#include "v8/include/v8-context.h" ++#include "v8/include/v8-external.h" ++#include "v8/include/v8-isolate.h" ++#include "v8/include/v8-object.h" ++#include "v8/include/v8-primitive.h" ++#include "v8/include/v8-template.h" + + CFXJSE_RuntimeData::CFXJSE_RuntimeData() = default; + +@@ -19,25 +27,21 @@ std::unique_ptr CFXJSE_RuntimeData::Create( + v8::Isolate* pIsolate) { + std::unique_ptr pRuntimeData(new CFXJSE_RuntimeData()); + CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); +- + v8::Local hFuncTemplate = + v8::FunctionTemplate::New(pIsolate); + + v8::Local hGlobalTemplate = + hFuncTemplate->InstanceTemplate(); +- hGlobalTemplate->Set( +- v8::Symbol::GetToStringTag(pIsolate), +- v8::String::NewFromUtf8(pIsolate, "global", v8::NewStringType::kNormal) +- .ToLocalChecked()); ++ hGlobalTemplate->Set(v8::Symbol::GetToStringTag(pIsolate), ++ fxv8::NewStringHelper(pIsolate, "global")); + + v8::Local hContext = + v8::Context::New(pIsolate, 0, hGlobalTemplate); + +- ASSERT(hContext->Global()->InternalFieldCount() == 0); +- ASSERT(hContext->Global() +- ->GetPrototype() +- .As() +- ->InternalFieldCount() == 0); ++ DCHECK_EQ(hContext->Global()->InternalFieldCount(), 0); ++ DCHECK_EQ( ++ hContext->Global()->GetPrototype().As()->InternalFieldCount(), ++ 0); + + hContext->SetSecurityToken(v8::External::New(pIsolate, pIsolate)); + pRuntimeData->m_hRootContextGlobalTemplate.Reset(pIsolate, hFuncTemplate); +@@ -47,9 +51,8 @@ std::unique_ptr CFXJSE_RuntimeData::Create( + + CFXJSE_RuntimeData* CFXJSE_RuntimeData::Get(v8::Isolate* pIsolate) { + FXJS_PerIsolateData::SetUp(pIsolate); +- + FXJS_PerIsolateData* pData = FXJS_PerIsolateData::Get(pIsolate); +- if (!pData->m_pFXJSERuntimeData) +- pData->m_pFXJSERuntimeData = CFXJSE_RuntimeData::Create(pIsolate); +- return static_cast(pData->m_pFXJSERuntimeData.get()); ++ if (!pData->GetExtension()) ++ pData->SetExtension(CFXJSE_RuntimeData::Create(pIsolate)); ++ return static_cast(pData->GetExtension()); + } +diff --git a/fxjs/xfa/cfxjse_runtimedata.h b/fxjs/xfa/cfxjse_runtimedata.h +index 148b01ec0..67925a0f0 100644 +--- a/fxjs/xfa/cfxjse_runtimedata.h ++++ b/fxjs/xfa/cfxjse_runtimedata.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,25 +10,26 @@ + #include + + #include "fxjs/cfxjs_engine.h" +-#include "v8/include/v8.h" ++#include "v8/include/v8-forward.h" ++#include "v8/include/v8-persistent-handle.h" + +-class CFXJSE_RuntimeData : public FXJS_PerIsolateData::ExtensionIface { ++class CFXJSE_RuntimeData final : public FXJS_PerIsolateData::ExtensionIface { + public: ++ CFXJSE_RuntimeData(const CFXJSE_RuntimeData&) = delete; ++ CFXJSE_RuntimeData& operator=(const CFXJSE_RuntimeData&) = delete; + ~CFXJSE_RuntimeData() override; + + static CFXJSE_RuntimeData* Get(v8::Isolate* pIsolate); + +- v8::Global m_hRootContextGlobalTemplate; +- v8::Global m_hRootContext; +- +- protected: +- CFXJSE_RuntimeData(); ++ const v8::Global& GetRootContext() { return m_hRootContext; } + ++ private: + static std::unique_ptr Create(v8::Isolate* pIsolate); + +- private: +- CFXJSE_RuntimeData(const CFXJSE_RuntimeData&) = delete; +- CFXJSE_RuntimeData& operator=(const CFXJSE_RuntimeData&) = delete; ++ CFXJSE_RuntimeData(); ++ ++ v8::Global m_hRootContextGlobalTemplate; ++ v8::Global m_hRootContext; + }; + + #endif // FXJS_XFA_CFXJSE_RUNTIMEDATA_H_ +diff --git a/fxjs/xfa/cfxjse_value.cpp b/fxjs/xfa/cfxjse_value.cpp +index 1d4cec47f..112a648fd 100644 +--- a/fxjs/xfa/cfxjse_value.cpp ++++ b/fxjs/xfa/cfxjse_value.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,9 +8,16 @@ + + #include + ++#include "fxjs/fxv8.h" + #include "fxjs/xfa/cfxjse_class.h" + #include "fxjs/xfa/cfxjse_context.h" + #include "fxjs/xfa/cfxjse_isolatetracker.h" ++#include "third_party/base/check.h" ++#include "v8/include/v8-container.h" ++#include "v8/include/v8-exception.h" ++#include "v8/include/v8-function.h" ++#include "v8/include/v8-primitive.h" ++#include "v8/include/v8-script.h" + + namespace { + +@@ -26,15 +33,18 @@ double ftod(float fNumber) { + if (nErrExp >= 0) + return fNumber; + +- double dwError = pow(2.0, nErrExp), dwErrorHalf = dwError / 2; +- double dNumber = fNumber, dNumberAbs = fabs(fNumber); +- double dNumberAbsMin = dNumberAbs - dwErrorHalf, +- dNumberAbsMax = dNumberAbs + dwErrorHalf; ++ double dwError = pow(2.0, nErrExp); ++ double dwErrorHalf = dwError / 2; ++ double dNumber = fNumber; ++ double dNumberAbs = fabs(fNumber); ++ double dNumberAbsMin = dNumberAbs - dwErrorHalf; ++ double dNumberAbsMax = dNumberAbs + dwErrorHalf; + int32_t iErrPos = 0; + if (floor(dNumberAbsMin) == floor(dNumberAbsMax)) { + dNumberAbsMin = fmod(dNumberAbsMin, 1.0); + dNumberAbsMax = fmod(dNumberAbsMax, 1.0); +- int32_t iErrPosMin = 1, iErrPosMax = 38; ++ int32_t iErrPosMin = 1; ++ int32_t iErrPosMax = 38; + do { + int32_t iMid = (iErrPosMin + iErrPosMax) / 2; + double dPow = pow(10.0, iMid); +@@ -53,228 +63,140 @@ double ftod(float fNumber) { + + } // namespace + +-void FXJSE_ThrowMessage(ByteStringView utf8Message) { +- v8::Isolate* pIsolate = v8::Isolate::GetCurrent(); +- ASSERT(pIsolate); +- ++void FXJSE_ThrowMessage(v8::Isolate* pIsolate, ByteStringView utf8Message) { ++ DCHECK(pIsolate); + CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); +- v8::Local hMessage = +- v8::String::NewFromUtf8(pIsolate, utf8Message.unterminated_c_str(), +- v8::NewStringType::kNormal, +- utf8Message.GetLength()) +- .ToLocalChecked(); ++ v8::Local hMessage = fxv8::NewStringHelper(pIsolate, utf8Message); + v8::Local hError = v8::Exception::Error(hMessage); + pIsolate->ThrowException(hError); + } + +-CFXJSE_Value::CFXJSE_Value(v8::Isolate* pIsolate) : m_pIsolate(pIsolate) {} ++CFXJSE_Value::CFXJSE_Value() = default; + +-CFXJSE_Value::~CFXJSE_Value() {} ++CFXJSE_Value::CFXJSE_Value(v8::Isolate* pIsolate, v8::Local value) { ++ ForceSetValue(pIsolate, value); ++} + +-CFXJSE_HostObject* CFXJSE_Value::ToHostObject() const { +- CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate()); +- v8::Local pValue = +- v8::Local::New(GetIsolate(), m_hValue); +- ASSERT(!pValue.IsEmpty()); +- if (!pValue->IsObject()) +- return nullptr; ++CFXJSE_Value::~CFXJSE_Value() = default; + +- return FXJSE_RetrieveObjectBinding(pValue.As()); ++CFXJSE_HostObject* CFXJSE_Value::ToHostObject(v8::Isolate* pIsolate) const { ++ CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); ++ return CFXJSE_HostObject::FromV8( ++ v8::Local::New(pIsolate, m_hValue)); + } + +-void CFXJSE_Value::SetHostObject(CFXJSE_HostObject* lpObject, ++void CFXJSE_Value::SetHostObject(v8::Isolate* pIsolate, ++ CFXJSE_HostObject* pObject, + CFXJSE_Class* pClass) { +- CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate()); +- v8::Local hClass = +- v8::Local::New(GetIsolate(), pClass->m_hTemplate); +- v8::Local hObject = +- hClass->InstanceTemplate() +- ->NewInstance(GetIsolate()->GetCurrentContext()) +- .ToLocalChecked(); +- FXJSE_UpdateObjectBinding(hObject, lpObject); +- m_hValue.Reset(GetIsolate(), hObject); +-} +- +-void CFXJSE_Value::ClearHostObject() { +- CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate()); +- FXJSE_ClearObjectBinding(m_hValue.Get(GetIsolate()).As()); +- v8::Local hValue = v8::Null(GetIsolate()); +- m_hValue.Reset(GetIsolate(), hValue); ++ CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); ++ m_hValue.Reset(pIsolate, pObject->NewBoundV8Object( ++ pIsolate, pClass->GetTemplate(pIsolate))); + } + + void CFXJSE_Value::SetArray( ++ v8::Isolate* pIsolate, + const std::vector>& values) { +- CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate()); +- v8::Local hArrayObject = +- v8::Array::New(GetIsolate(), values.size()); +- v8::Local context = GetIsolate()->GetCurrentContext(); +- uint32_t count = 0; ++ CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); ++ std::vector> local_values; ++ local_values.reserve(values.size()); + for (auto& v : values) { + if (v->IsEmpty()) +- v->SetUndefined(); +- hArrayObject +- ->Set( +- context, count++, +- v8::Local::New(GetIsolate(), v.get()->DirectGetValue())) +- .FromJust(); ++ local_values.push_back(fxv8::NewUndefinedHelper(pIsolate)); ++ else ++ local_values.push_back(v->GetValue(pIsolate)); + } +- m_hValue.Reset(GetIsolate(), hArrayObject); ++ v8::Local hArrayObject = ++ v8::Array::New(pIsolate, local_values.data(), local_values.size()); ++ m_hValue.Reset(pIsolate, hArrayObject); + } + +-void CFXJSE_Value::SetFloat(float fFloat) { +- CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate()); +- v8::Local pValue = v8::Number::New(GetIsolate(), ftod(fFloat)); +- m_hValue.Reset(GetIsolate(), pValue); ++void CFXJSE_Value::SetFloat(v8::Isolate* pIsolate, float fFloat) { ++ CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); ++ m_hValue.Reset(pIsolate, fxv8::NewNumberHelper(pIsolate, ftod(fFloat))); + } + +-bool CFXJSE_Value::SetObjectProperty(ByteStringView szPropName, +- CFXJSE_Value* lpPropValue) { +- ASSERT(lpPropValue); +- if (lpPropValue->IsEmpty()) ++bool CFXJSE_Value::SetObjectProperty(v8::Isolate* pIsolate, ++ ByteStringView szPropName, ++ CFXJSE_Value* pPropValue) { ++ if (pPropValue->IsEmpty()) + return false; + +- CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate()); +- v8::Local hObject = +- v8::Local::New(GetIsolate(), m_hValue); +- if (!hObject->IsObject()) +- return false; +- +- v8::Local hPropName = +- v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(), +- v8::NewStringType::kNormal, +- szPropName.GetLength()) +- .ToLocalChecked(); +- v8::Local hPropValue = +- v8::Local::New(GetIsolate(), lpPropValue->DirectGetValue()); +- v8::Maybe result = hObject.As()->Set( +- GetIsolate()->GetCurrentContext(), hPropName, hPropValue); +- return result.IsJust() && result.FromJust(); +-} +- +-bool CFXJSE_Value::GetObjectProperty(ByteStringView szPropName, +- CFXJSE_Value* lpPropValue) { +- ASSERT(lpPropValue); +- CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate()); +- v8::Local hObject = +- v8::Local::New(GetIsolate(), m_hValue); ++ CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); ++ v8::Local hObject = GetValue(pIsolate); + if (!hObject->IsObject()) + return false; + +- v8::Local hPropName = +- v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(), +- v8::NewStringType::kNormal, +- szPropName.GetLength()) +- .ToLocalChecked(); +- v8::Local hPropValue = +- hObject.As() +- ->Get(GetIsolate()->GetCurrentContext(), hPropName) +- .ToLocalChecked(); +- lpPropValue->ForceSetValue(hPropValue); +- return true; ++ return fxv8::ReentrantPutObjectPropertyHelper( ++ pIsolate, hObject.As(), szPropName, ++ pPropValue->GetValue(pIsolate)); + } + +-bool CFXJSE_Value::GetObjectPropertyByIdx(uint32_t uPropIdx, +- CFXJSE_Value* lpPropValue) { +- CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate()); +- v8::Local hObject = +- v8::Local::New(GetIsolate(), m_hValue); ++bool CFXJSE_Value::GetObjectProperty(v8::Isolate* pIsolate, ++ ByteStringView szPropName, ++ CFXJSE_Value* pPropValue) { ++ CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); ++ v8::Local hObject = GetValue(pIsolate); + if (!hObject->IsObject()) + return false; + +- v8::Local hPropValue = +- hObject.As() +- ->Get(GetIsolate()->GetCurrentContext(), uPropIdx) +- .ToLocalChecked(); +- lpPropValue->ForceSetValue(hPropValue); ++ pPropValue->ForceSetValue( ++ pIsolate, fxv8::ReentrantGetObjectPropertyHelper( ++ pIsolate, hObject.As(), szPropName)); + return true; + } + +-bool CFXJSE_Value::DeleteObjectProperty(ByteStringView szPropName) { +- CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate()); +- v8::Local hObject = +- v8::Local::New(GetIsolate(), m_hValue); +- if (!hObject->IsObject()) ++bool CFXJSE_Value::GetObjectPropertyByIdx(v8::Isolate* pIsolate, ++ uint32_t uPropIdx, ++ CFXJSE_Value* pPropValue) { ++ CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); ++ v8::Local hObject = GetValue(pIsolate); ++ if (!hObject->IsArray()) + return false; + +- v8::Local hPropName = +- v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(), +- v8::NewStringType::kNormal, +- szPropName.GetLength()) +- .ToLocalChecked(); +- return hObject.As() +- ->Delete(GetIsolate()->GetCurrentContext(), hPropName) +- .FromJust(); ++ pPropValue->ForceSetValue(pIsolate, ++ fxv8::ReentrantGetArrayElementHelper( ++ pIsolate, hObject.As(), uPropIdx)); ++ return true; + } + +-bool CFXJSE_Value::HasObjectOwnProperty(ByteStringView szPropName, +- bool bUseTypeGetter) { +- CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate()); +- v8::Local hObject = +- v8::Local::New(GetIsolate(), m_hValue); +- if (!hObject->IsObject()) +- return false; ++void CFXJSE_Value::DeleteObjectProperty(v8::Isolate* pIsolate, ++ ByteStringView szPropName) { ++ CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); ++ v8::Local hObject = v8::Local::New(pIsolate, m_hValue); ++ if (hObject->IsObject()) { ++ fxv8::ReentrantDeleteObjectPropertyHelper( ++ pIsolate, hObject.As(), szPropName); ++ } ++} + +- v8::Local hKey = +- v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(), +- v8::NewStringType::kNormal, +- szPropName.GetLength()) +- .ToLocalChecked(); +- return hObject.As() +- ->HasRealNamedProperty(GetIsolate()->GetCurrentContext(), hKey) +- .FromJust() || +- (bUseTypeGetter && +- hObject.As() +- ->HasOwnProperty(GetIsolate()->GetCurrentContext(), hKey) +- .FromMaybe(false)); +-} +- +-bool CFXJSE_Value::SetObjectOwnProperty(ByteStringView szPropName, +- CFXJSE_Value* lpPropValue) { +- ASSERT(lpPropValue); +- CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate()); +- v8::Local hObject = +- v8::Local::New(GetIsolate(), m_hValue); ++bool CFXJSE_Value::SetObjectOwnProperty(v8::Isolate* pIsolate, ++ ByteStringView szPropName, ++ CFXJSE_Value* pPropValue) { ++ CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); ++ v8::Local hObject = v8::Local::New(pIsolate, m_hValue); + if (!hObject->IsObject()) + return false; + +- v8::Local hPropName = +- v8::String::NewFromUtf8(GetIsolate(), szPropName.unterminated_c_str(), +- v8::NewStringType::kNormal, +- szPropName.GetLength()) +- .ToLocalChecked(); + v8::Local pValue = +- v8::Local::New(GetIsolate(), lpPropValue->m_hValue); +- return hObject.As() +- ->DefineOwnProperty(GetIsolate()->GetCurrentContext(), hPropName, pValue) +- .FromMaybe(false); ++ v8::Local::New(pIsolate, pPropValue->m_hValue); ++ return fxv8::ReentrantSetObjectOwnPropertyHelper( ++ pIsolate, hObject.As(), szPropName, pValue); + } + +-bool CFXJSE_Value::SetFunctionBind(CFXJSE_Value* lpOldFunction, +- CFXJSE_Value* lpNewThis) { +- ASSERT(lpOldFunction); +- ASSERT(lpNewThis); ++v8::Local CFXJSE_Value::NewBoundFunction( ++ v8::Isolate* pIsolate, ++ v8::Local hOldFunction, ++ v8::Local hNewThis) { ++ DCHECK(!hOldFunction.IsEmpty()); ++ DCHECK(!hNewThis.IsEmpty()); + +- CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate()); ++ CFXJSE_ScopeUtil_RootContext scope(pIsolate); + v8::Local rgArgs[2]; +- v8::Local hOldFunction = +- v8::Local::New(GetIsolate(), lpOldFunction->DirectGetValue()); +- if (hOldFunction.IsEmpty() || !hOldFunction->IsFunction()) +- return false; +- + rgArgs[0] = hOldFunction; +- v8::Local hNewThis = +- v8::Local::New(GetIsolate(), lpNewThis->DirectGetValue()); +- if (hNewThis.IsEmpty()) +- return false; +- + rgArgs[1] = hNewThis; +- v8::Local hBinderFuncSource = +- v8::String::NewFromUtf8(GetIsolate(), +- "(function (oldfunction, newthis) { return " +- "oldfunction.bind(newthis); })", +- v8::NewStringType::kNormal) +- .ToLocalChecked(); +- v8::Local hContext = GetIsolate()->GetCurrentContext(); ++ v8::Local hBinderFuncSource = fxv8::NewStringHelper( ++ pIsolate, "(function (fn, obj) { return fn.bind(obj); })"); ++ v8::Local hContext = pIsolate->GetCurrentContext(); + v8::Local hBinderFunc = + v8::Script::Compile(hContext, hBinderFuncSource) + .ToLocalChecked() +@@ -284,187 +206,159 @@ bool CFXJSE_Value::SetFunctionBind(CFXJSE_Value* lpOldFunction, + v8::Local hBoundFunction = + hBinderFunc->Call(hContext, hContext->Global(), 2, rgArgs) + .ToLocalChecked(); +- if (hBoundFunction.IsEmpty() || !hBoundFunction->IsFunction()) +- return false; ++ if (!fxv8::IsFunction(hBoundFunction)) ++ return v8::Local(); + +- m_hValue.Reset(GetIsolate(), hBoundFunction); +- return true; ++ return hBoundFunction.As(); ++} ++ ++v8::Local CFXJSE_Value::GetValue(v8::Isolate* pIsolate) const { ++ return v8::Local::New(pIsolate, m_hValue); + } + + bool CFXJSE_Value::IsEmpty() const { + return m_hValue.IsEmpty(); + } + +-bool CFXJSE_Value::IsUndefined() const { ++bool CFXJSE_Value::IsUndefined(v8::Isolate* pIsolate) const { + if (IsEmpty()) + return false; + +- CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate()); +- v8::Local hValue = +- v8::Local::New(GetIsolate(), m_hValue); ++ CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); ++ v8::Local hValue = v8::Local::New(pIsolate, m_hValue); + return hValue->IsUndefined(); + } + +-bool CFXJSE_Value::IsNull() const { ++bool CFXJSE_Value::IsNull(v8::Isolate* pIsolate) const { + if (IsEmpty()) + return false; + +- CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate()); +- v8::Local hValue = +- v8::Local::New(GetIsolate(), m_hValue); ++ CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); ++ v8::Local hValue = v8::Local::New(pIsolate, m_hValue); + return hValue->IsNull(); + } + +-bool CFXJSE_Value::IsBoolean() const { ++bool CFXJSE_Value::IsBoolean(v8::Isolate* pIsolate) const { + if (IsEmpty()) + return false; + +- CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate()); +- v8::Local hValue = +- v8::Local::New(GetIsolate(), m_hValue); ++ CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); ++ v8::Local hValue = v8::Local::New(pIsolate, m_hValue); + return hValue->IsBoolean(); + } + +-bool CFXJSE_Value::IsString() const { ++bool CFXJSE_Value::IsString(v8::Isolate* pIsolate) const { + if (IsEmpty()) + return false; + +- CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate()); +- v8::Local hValue = +- v8::Local::New(GetIsolate(), m_hValue); ++ CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); ++ v8::Local hValue = v8::Local::New(pIsolate, m_hValue); + return hValue->IsString(); + } + +-bool CFXJSE_Value::IsNumber() const { ++bool CFXJSE_Value::IsNumber(v8::Isolate* pIsolate) const { + if (IsEmpty()) + return false; + +- CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate()); +- v8::Local hValue = +- v8::Local::New(GetIsolate(), m_hValue); ++ CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); ++ v8::Local hValue = v8::Local::New(pIsolate, m_hValue); + return hValue->IsNumber(); + } + +-bool CFXJSE_Value::IsInteger() const { ++bool CFXJSE_Value::IsInteger(v8::Isolate* pIsolate) const { + if (IsEmpty()) + return false; + +- CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate()); +- v8::Local hValue = +- v8::Local::New(GetIsolate(), m_hValue); ++ CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); ++ v8::Local hValue = v8::Local::New(pIsolate, m_hValue); + return hValue->IsInt32(); + } + +-bool CFXJSE_Value::IsObject() const { ++bool CFXJSE_Value::IsObject(v8::Isolate* pIsolate) const { + if (IsEmpty()) + return false; + +- CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate()); +- v8::Local hValue = +- v8::Local::New(GetIsolate(), m_hValue); ++ CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); ++ v8::Local hValue = v8::Local::New(pIsolate, m_hValue); + return hValue->IsObject(); + } + +-bool CFXJSE_Value::IsArray() const { ++bool CFXJSE_Value::IsArray(v8::Isolate* pIsolate) const { + if (IsEmpty()) + return false; + +- CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate()); +- v8::Local hValue = +- v8::Local::New(GetIsolate(), m_hValue); ++ CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); ++ v8::Local hValue = v8::Local::New(pIsolate, m_hValue); + return hValue->IsArray(); + } + +-bool CFXJSE_Value::IsFunction() const { ++bool CFXJSE_Value::IsFunction(v8::Isolate* pIsolate) const { + if (IsEmpty()) + return false; + +- CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate()); +- v8::Local hValue = +- v8::Local::New(GetIsolate(), m_hValue); ++ CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); ++ v8::Local hValue = v8::Local::New(pIsolate, m_hValue); + return hValue->IsFunction(); + } + +-bool CFXJSE_Value::ToBoolean() const { +- ASSERT(!IsEmpty()); +- CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate()); +- v8::Local hValue = +- v8::Local::New(GetIsolate(), m_hValue); +- return hValue->BooleanValue(GetIsolate()); ++bool CFXJSE_Value::ToBoolean(v8::Isolate* pIsolate) const { ++ DCHECK(!IsEmpty()); ++ CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); ++ return fxv8::ReentrantToBooleanHelper( ++ pIsolate, v8::Local::New(pIsolate, m_hValue)); + } + +-float CFXJSE_Value::ToFloat() const { +- ASSERT(!IsEmpty()); +- CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate()); +- v8::Local hValue = +- v8::Local::New(GetIsolate(), m_hValue); +- return static_cast( +- hValue->NumberValue(GetIsolate()->GetCurrentContext()).FromMaybe(0.0)); ++float CFXJSE_Value::ToFloat(v8::Isolate* pIsolate) const { ++ return static_cast(ToDouble(pIsolate)); + } + +-double CFXJSE_Value::ToDouble() const { +- ASSERT(!IsEmpty()); +- CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate()); +- v8::Local hValue = +- v8::Local::New(GetIsolate(), m_hValue); +- return hValue->NumberValue(GetIsolate()->GetCurrentContext()).FromMaybe(0.0); ++double CFXJSE_Value::ToDouble(v8::Isolate* pIsolate) const { ++ DCHECK(!IsEmpty()); ++ CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); ++ return fxv8::ReentrantToDoubleHelper( ++ pIsolate, v8::Local::New(pIsolate, m_hValue)); + } + +-int32_t CFXJSE_Value::ToInteger() const { +- ASSERT(!IsEmpty()); +- CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate()); +- v8::Local hValue = +- v8::Local::New(GetIsolate(), m_hValue); +- return static_cast( +- hValue->NumberValue(GetIsolate()->GetCurrentContext()).FromMaybe(0.0)); ++int32_t CFXJSE_Value::ToInteger(v8::Isolate* pIsolate) const { ++ DCHECK(!IsEmpty()); ++ CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); ++ return fxv8::ReentrantToInt32Helper( ++ pIsolate, v8::Local::New(pIsolate, m_hValue)); + } + +-ByteString CFXJSE_Value::ToString() const { +- ASSERT(!IsEmpty()); +- CFXJSE_ScopeUtil_IsolateHandleRootContext scope(GetIsolate()); +- v8::Local hValue = +- v8::Local::New(GetIsolate(), m_hValue); +- v8::Local hString = +- hValue->ToString(GetIsolate()->GetCurrentContext()).ToLocalChecked(); +- v8::String::Utf8Value hStringVal(GetIsolate(), hString); +- return ByteString(*hStringVal); ++ByteString CFXJSE_Value::ToString(v8::Isolate* pIsolate) const { ++ DCHECK(!IsEmpty()); ++ CFXJSE_ScopeUtil_IsolateHandleRootContext scope(pIsolate); ++ return fxv8::ReentrantToByteStringHelper( ++ pIsolate, v8::Local::New(pIsolate, m_hValue)); + } + +-void CFXJSE_Value::SetUndefined() { +- CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate()); +- v8::Local hValue = v8::Undefined(GetIsolate()); +- m_hValue.Reset(GetIsolate(), hValue); ++void CFXJSE_Value::SetUndefined(v8::Isolate* pIsolate) { ++ CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); ++ m_hValue.Reset(pIsolate, fxv8::NewUndefinedHelper(pIsolate)); + } + +-void CFXJSE_Value::SetNull() { +- CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate()); +- v8::Local hValue = v8::Null(GetIsolate()); +- m_hValue.Reset(GetIsolate(), hValue); ++void CFXJSE_Value::SetNull(v8::Isolate* pIsolate) { ++ CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); ++ m_hValue.Reset(pIsolate, fxv8::NewNullHelper(pIsolate)); + } + +-void CFXJSE_Value::SetBoolean(bool bBoolean) { +- CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate()); +- v8::Local hValue = v8::Boolean::New(GetIsolate(), !!bBoolean); +- m_hValue.Reset(GetIsolate(), hValue); ++void CFXJSE_Value::SetBoolean(v8::Isolate* pIsolate, bool bBoolean) { ++ CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); ++ m_hValue.Reset(pIsolate, fxv8::NewBooleanHelper(pIsolate, bBoolean)); + } + +-void CFXJSE_Value::SetInteger(int32_t nInteger) { +- CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate()); +- v8::Local hValue = v8::Integer::New(GetIsolate(), nInteger); +- m_hValue.Reset(GetIsolate(), hValue); ++void CFXJSE_Value::SetInteger(v8::Isolate* pIsolate, int32_t nInteger) { ++ CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); ++ m_hValue.Reset(pIsolate, fxv8::NewNumberHelper(pIsolate, nInteger)); + } + +-void CFXJSE_Value::SetDouble(double dDouble) { +- CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate()); +- v8::Local hValue = v8::Number::New(GetIsolate(), dDouble); +- m_hValue.Reset(GetIsolate(), hValue); ++void CFXJSE_Value::SetDouble(v8::Isolate* pIsolate, double dDouble) { ++ CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); ++ m_hValue.Reset(pIsolate, fxv8::NewNumberHelper(pIsolate, dDouble)); + } + +-void CFXJSE_Value::SetString(ByteStringView szString) { +- CFXJSE_ScopeUtil_IsolateHandle scope(GetIsolate()); +- v8::Local hValue = +- v8::String::NewFromUtf8(GetIsolate(), szString.unterminated_c_str(), +- v8::NewStringType::kNormal, szString.GetLength()) +- .ToLocalChecked(); +- m_hValue.Reset(GetIsolate(), hValue); ++void CFXJSE_Value::SetString(v8::Isolate* pIsolate, ByteStringView szString) { ++ CFXJSE_ScopeUtil_IsolateHandle scope(pIsolate); ++ m_hValue.Reset(pIsolate, fxv8::NewStringHelper(pIsolate, szString)); + } +diff --git a/fxjs/xfa/cfxjse_value.h b/fxjs/xfa/cfxjse_value.h +index 44cc58ca4..b8b66879b 100644 +--- a/fxjs/xfa/cfxjse_value.h ++++ b/fxjs/xfa/cfxjse_value.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,84 +7,91 @@ + #ifndef FXJS_XFA_CFXJSE_VALUE_H_ + #define FXJS_XFA_CFXJSE_VALUE_H_ + ++#include ++ + #include + #include + + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" + #include "core/fxcrt/unowned_ptr.h" +-#include "v8/include/v8.h" ++#include "third_party/base/check.h" ++#include "v8/include/v8-forward.h" ++#include "v8/include/v8-persistent-handle.h" + + class CFXJSE_Class; + class CFXJSE_HostObject; + + class CFXJSE_Value { + public: +- explicit CFXJSE_Value(v8::Isolate* pIsolate); ++ CFXJSE_Value(); ++ CFXJSE_Value(v8::Isolate* pIsolate, v8::Local value); + ~CFXJSE_Value(); + + bool IsEmpty() const; +- bool IsUndefined() const; +- bool IsNull() const; +- bool IsBoolean() const; +- bool IsString() const; +- bool IsNumber() const; +- bool IsInteger() const; +- bool IsObject() const; +- bool IsArray() const; +- bool IsFunction() const; +- bool ToBoolean() const; +- float ToFloat() const; +- double ToDouble() const; +- int32_t ToInteger() const; +- ByteString ToString() const; +- WideString ToWideString() const { +- return WideString::FromUTF8(ToString().AsStringView()); ++ bool IsUndefined(v8::Isolate* pIsolate) const; ++ bool IsNull(v8::Isolate* pIsolate) const; ++ bool IsBoolean(v8::Isolate* pIsolate) const; ++ bool IsString(v8::Isolate* pIsolate) const; ++ bool IsNumber(v8::Isolate* pIsolate) const; ++ bool IsInteger(v8::Isolate* pIsolate) const; ++ bool IsObject(v8::Isolate* pIsolate) const; ++ bool IsArray(v8::Isolate* pIsolate) const; ++ bool IsFunction(v8::Isolate* pIsolate) const; ++ bool ToBoolean(v8::Isolate* pIsolate) const; ++ float ToFloat(v8::Isolate* pIsolate) const; ++ double ToDouble(v8::Isolate* pIsolate) const; ++ int32_t ToInteger(v8::Isolate* pIsolate) const; ++ ByteString ToString(v8::Isolate* pIsolate) const; ++ WideString ToWideString(v8::Isolate* pIsolate) const { ++ return WideString::FromUTF8(ToString(pIsolate).AsStringView()); + } +- CFXJSE_HostObject* ToHostObject() const; +- +- void SetUndefined(); +- void SetNull(); +- void SetBoolean(bool bBoolean); +- void SetInteger(int32_t nInteger); +- void SetDouble(double dDouble); +- void SetString(ByteStringView szString); +- void SetFloat(float fFloat); +- +- void SetHostObject(CFXJSE_HostObject* lpObject, CFXJSE_Class* pClass); +- void ClearHostObject(); +- +- void SetArray(const std::vector>& values); +- +- bool GetObjectProperty(ByteStringView szPropName, CFXJSE_Value* lpPropValue); +- bool SetObjectProperty(ByteStringView szPropName, CFXJSE_Value* lpPropValue); +- bool GetObjectPropertyByIdx(uint32_t uPropIdx, CFXJSE_Value* lpPropValue); +- bool DeleteObjectProperty(ByteStringView szPropName); +- bool HasObjectOwnProperty(ByteStringView szPropName, bool bUseTypeGetter); +- bool SetObjectOwnProperty(ByteStringView szPropName, +- CFXJSE_Value* lpPropValue); +- bool SetFunctionBind(CFXJSE_Value* lpOldFunction, CFXJSE_Value* lpNewThis); +- +- v8::Isolate* GetIsolate() const { return m_pIsolate.Get(); } ++ CFXJSE_HostObject* ToHostObject(v8::Isolate* pIsolate) const; ++ ++ void SetUndefined(v8::Isolate* pIsolate); ++ void SetNull(v8::Isolate* pIsolate); ++ void SetBoolean(v8::Isolate* pIsolate, bool bBoolean); ++ void SetInteger(v8::Isolate* pIsolate, int32_t nInteger); ++ void SetDouble(v8::Isolate* pIsolate, double dDouble); ++ void SetString(v8::Isolate* pIsolate, ByteStringView szString); ++ void SetFloat(v8::Isolate* pIsolate, float fFloat); ++ ++ void SetHostObject(v8::Isolate* pIsolate, ++ CFXJSE_HostObject* pObject, ++ CFXJSE_Class* pClass); ++ ++ void SetArray(v8::Isolate* pIsolate, ++ const std::vector>& values); ++ ++ bool GetObjectProperty(v8::Isolate* pIsolate, ++ ByteStringView szPropName, ++ CFXJSE_Value* pPropValue); ++ bool SetObjectProperty(v8::Isolate* pIsolate, ++ ByteStringView szPropName, ++ CFXJSE_Value* pPropValue); ++ bool GetObjectPropertyByIdx(v8::Isolate* pIsolate, ++ uint32_t uPropIdx, ++ CFXJSE_Value* pPropValue); ++ void DeleteObjectProperty(v8::Isolate* pIsolate, ByteStringView szPropName); ++ bool SetObjectOwnProperty(v8::Isolate* pIsolate, ++ ByteStringView szPropName, ++ CFXJSE_Value* pPropValue); ++ ++ // Return empty local on error. ++ static v8::Local NewBoundFunction( ++ v8::Isolate* pIsolate, ++ v8::Local hOldFunction, ++ v8::Local lpNewThis); ++ ++ v8::Local GetValue(v8::Isolate* pIsolate) const; + const v8::Global& DirectGetValue() const { return m_hValue; } +- void ForceSetValue(v8::Local hValue) { +- m_hValue.Reset(GetIsolate(), hValue); +- } +- void Assign(const CFXJSE_Value* lpValue) { +- ASSERT(lpValue); +- if (lpValue) { +- m_hValue.Reset(GetIsolate(), lpValue->m_hValue); +- } else { +- m_hValue.Reset(); +- } ++ void ForceSetValue(v8::Isolate* pIsolate, v8::Local hValue) { ++ m_hValue.Reset(pIsolate, hValue); + } + + private: +- CFXJSE_Value() = delete; + CFXJSE_Value(const CFXJSE_Value&) = delete; + CFXJSE_Value& operator=(const CFXJSE_Value&) = delete; + +- UnownedPtr const m_pIsolate; + v8::Global m_hValue; + }; + +diff --git a/fxjs/xfa/cfxjse_value_embeddertest.cpp b/fxjs/xfa/cfxjse_value_embeddertest.cpp +index e9c39c12d..c1e810b7b 100644 +--- a/fxjs/xfa/cfxjse_value_embeddertest.cpp ++++ b/fxjs/xfa/cfxjse_value_embeddertest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,34 +11,33 @@ + #include "fxjs/xfa/cfxjse_engine.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "testing/xfa_js_embedder_test.h" +-#include "third_party/base/ptr_util.h" + + class CFXJSE_ValueEmbedderTest : public XFAJSEmbedderTest {}; + + TEST_F(CFXJSE_ValueEmbedderTest, Empty) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + +- auto pValue = pdfium::MakeUnique(GetIsolate()); ++ auto pValue = std::make_unique(); + EXPECT_TRUE(pValue->IsEmpty()); +- EXPECT_FALSE(pValue->IsUndefined()); +- EXPECT_FALSE(pValue->IsNull()); +- EXPECT_FALSE(pValue->IsBoolean()); +- EXPECT_FALSE(pValue->IsString()); +- EXPECT_FALSE(pValue->IsNumber()); +- EXPECT_FALSE(pValue->IsObject()); +- EXPECT_FALSE(pValue->IsArray()); +- EXPECT_FALSE(pValue->IsFunction()); ++ EXPECT_FALSE(pValue->IsUndefined(isolate())); ++ EXPECT_FALSE(pValue->IsNull(isolate())); ++ EXPECT_FALSE(pValue->IsBoolean(isolate())); ++ EXPECT_FALSE(pValue->IsString(isolate())); ++ EXPECT_FALSE(pValue->IsNumber(isolate())); ++ EXPECT_FALSE(pValue->IsObject(isolate())); ++ EXPECT_FALSE(pValue->IsArray(isolate())); ++ EXPECT_FALSE(pValue->IsFunction(isolate())); + } + + TEST_F(CFXJSE_ValueEmbedderTest, EmptyArrayInsert) { + ASSERT_TRUE(OpenDocument("simple_xfa.pdf")); + + // Test inserting empty values into arrays. +- auto pValue = pdfium::MakeUnique(GetIsolate()); ++ auto pValue = std::make_unique(); + std::vector> vec; + vec.push_back(std::move(pValue)); + +- CFXJSE_Value array(GetIsolate()); +- array.SetArray(vec); +- EXPECT_TRUE(array.IsArray()); ++ CFXJSE_Value array; ++ array.SetArray(isolate(), vec); ++ EXPECT_TRUE(array.IsArray(isolate())); + } +diff --git a/fxjs/xfa/cjx_boolean.cpp b/fxjs/xfa/cjx_boolean.cpp +index d44e03936..a7e6f8b9b 100644 +--- a/fxjs/xfa/cjx_boolean.cpp ++++ b/fxjs/xfa/cjx_boolean.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,9 @@ + + #include "fxjs/xfa/cjx_boolean.h" + ++#include "fxjs/fxv8.h" + #include "fxjs/xfa/cfxjse_value.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fxfa/parser/cxfa_boolean.h" + + CJX_Boolean::CJX_Boolean(CXFA_Boolean* node) : CJX_Object(node) {} +@@ -17,30 +19,33 @@ bool CJX_Boolean::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-void CJX_Boolean::defaultValue(CFXJSE_Value* pValue, ++void CJX_Boolean::defaultValue(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (!bSetting) { +- pValue->SetBoolean(GetContent(true).EqualsASCII("1")); ++ *pValue = ++ fxv8::NewBooleanHelper(pIsolate, GetContent(true).EqualsASCII("1")); + return; + } + + ByteString newValue; +- if (pValue && !(pValue->IsNull() || pValue->IsUndefined())) +- newValue = pValue->ToString(); ++ if (pValue && !(fxv8::IsNull(*pValue) || fxv8::IsUndefined(*pValue))) ++ newValue = fxv8::ReentrantToByteStringHelper(pIsolate, *pValue); + + int32_t iValue = FXSYS_atoi(newValue.c_str()); + WideString wsNewValue(iValue == 0 ? L"0" : L"1"); + WideString wsFormatValue(wsNewValue); +- CXFA_Node* pContainerNode = ToNode(GetXFAObject())->GetContainerNode(); ++ CXFA_Node* pContainerNode = GetXFANode()->GetContainerNode(); + if (pContainerNode) + wsFormatValue = pContainerNode->GetFormatDataValue(wsNewValue); + + SetContent(wsNewValue, wsFormatValue, true, true, true); + } + +-void CJX_Boolean::value(CFXJSE_Value* pValue, ++void CJX_Boolean::value(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- defaultValue(pValue, bSetting, eAttribute); ++ defaultValue(pIsolate, pValue, bSetting, eAttribute); + } +diff --git a/fxjs/xfa/cjx_boolean.h b/fxjs/xfa/cjx_boolean.h +index d2a85baf0..7fc2111d9 100644 +--- a/fxjs/xfa/cjx_boolean.h ++++ b/fxjs/xfa/cjx_boolean.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Boolean; + + class CJX_Boolean final : public CJX_Object { + public: +- explicit CJX_Boolean(CXFA_Boolean* node); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Boolean() override; + + // CJX_Object: +@@ -24,6 +24,8 @@ class CJX_Boolean final : public CJX_Object { + JSE_PROP(value); + + private: ++ explicit CJX_Boolean(CXFA_Boolean* node); ++ + using Type__ = CJX_Boolean; + using ParentType__ = CJX_Object; + +diff --git a/fxjs/xfa/cjx_container.cpp b/fxjs/xfa/cjx_container.cpp +index 44b1ff8a9..d6cabac8a 100644 +--- a/fxjs/xfa/cjx_container.cpp ++++ b/fxjs/xfa/cjx_container.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,6 +11,8 @@ + #include "fxjs/xfa/cfxjse_class.h" + #include "fxjs/xfa/cfxjse_engine.h" + #include "fxjs/xfa/cfxjse_value.h" ++#include "v8/include/cppgc/allocation.h" ++#include "v8/include/v8-object.h" + #include "xfa/fxfa/parser/cxfa_arraynodelist.h" + #include "xfa/fxfa/parser/cxfa_document.h" + #include "xfa/fxfa/parser/cxfa_field.h" +@@ -23,23 +25,24 @@ CJX_Container::CJX_Container(CXFA_Node* node) : CJX_Node(node) { + DefineMethods(MethodSpecs); + } + +-CJX_Container::~CJX_Container() {} ++CJX_Container::~CJX_Container() = default; + + bool CJX_Container::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + + CJS_Result CJX_Container::getDelta( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return CJS_Result::Success(); + } + + CJS_Result CJX_Container::getDeltas( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { +- auto* pEngine = static_cast(runtime); +- return CJS_Result::Success(pEngine->NewXFAObject( +- new CXFA_ArrayNodeList(GetDocument()), +- GetDocument()->GetScriptContext()->GetJseNormalClass()->GetTemplate())); ++ CXFA_Document* pDoc = GetDocument(); ++ auto* pList = cppgc::MakeGarbageCollected( ++ pDoc->GetHeap()->GetAllocationHandle(), pDoc); ++ pDoc->GetNodeOwner()->PersistList(pList); ++ return CJS_Result::Success(runtime->NewNormalXFAObject(pList)); + } +diff --git a/fxjs/xfa/cjx_container.h b/fxjs/xfa/cjx_container.h +index 51675e563..b44dd72ce 100644 +--- a/fxjs/xfa/cjx_container.h ++++ b/fxjs/xfa/cjx_container.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Node; + + class CJX_Container : public CJX_Node { + public: +- explicit CJX_Container(CXFA_Node* node); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Container() override; + + // CJX_Object: +@@ -23,6 +23,9 @@ class CJX_Container : public CJX_Node { + JSE_METHOD(getDelta); + JSE_METHOD(getDeltas); + ++ protected: ++ explicit CJX_Container(CXFA_Node* node); ++ + private: + using Type__ = CJX_Container; + using ParentType__ = CJX_Node; +diff --git a/fxjs/xfa/cjx_datawindow.cpp b/fxjs/xfa/cjx_datawindow.cpp +index d16514081..10f83656b 100644 +--- a/fxjs/xfa/cjx_datawindow.cpp ++++ b/fxjs/xfa/cjx_datawindow.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -22,48 +22,52 @@ CJX_DataWindow::CJX_DataWindow(CScript_DataWindow* window) + DefineMethods(MethodSpecs); + } + +-CJX_DataWindow::~CJX_DataWindow() {} ++CJX_DataWindow::~CJX_DataWindow() = default; + + bool CJX_DataWindow::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + + CJS_Result CJX_DataWindow::moveCurrentRecord( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return CJS_Result::Success(); + } + + CJS_Result CJX_DataWindow::record( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return CJS_Result::Success(); + } + + CJS_Result CJX_DataWindow::gotoRecord( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return CJS_Result::Success(); + } + + CJS_Result CJX_DataWindow::isRecordGroup( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return CJS_Result::Success(); + } + +-void CJX_DataWindow::recordsBefore(CFXJSE_Value* pValue, ++void CJX_DataWindow::recordsBefore(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) {} + +-void CJX_DataWindow::currentRecordNumber(CFXJSE_Value* pValue, ++void CJX_DataWindow::currentRecordNumber(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) {} + +-void CJX_DataWindow::recordsAfter(CFXJSE_Value* pValue, ++void CJX_DataWindow::recordsAfter(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) {} + +-void CJX_DataWindow::isDefined(CFXJSE_Value* pValue, ++void CJX_DataWindow::isDefined(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) {} +diff --git a/fxjs/xfa/cjx_datawindow.h b/fxjs/xfa/cjx_datawindow.h +index 6f3ad542e..df5092d5c 100644 +--- a/fxjs/xfa/cjx_datawindow.h ++++ b/fxjs/xfa/cjx_datawindow.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,12 +11,11 @@ + #include "fxjs/xfa/jse_define.h" + #include "xfa/fxfa/fxfa_basic.h" + +-class CFXJSE_Value; + class CScript_DataWindow; + + class CJX_DataWindow final : public CJX_Object { + public: +- explicit CJX_DataWindow(CScript_DataWindow* window); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_DataWindow() override; + + // CJX_Object: +@@ -33,6 +32,8 @@ class CJX_DataWindow final : public CJX_Object { + JSE_PROP(recordsBefore); + + private: ++ explicit CJX_DataWindow(CScript_DataWindow* window); ++ + using Type__ = CJX_DataWindow; + using ParentType__ = CJX_Object; + +diff --git a/fxjs/xfa/cjx_delta.cpp b/fxjs/xfa/cjx_delta.cpp +index 3ad20a6fa..266ddefb4 100644 +--- a/fxjs/xfa/cjx_delta.cpp ++++ b/fxjs/xfa/cjx_delta.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -18,13 +18,13 @@ CJX_Delta::CJX_Delta(CXFA_Delta* delta) : CJX_Object(delta) { + DefineMethods(MethodSpecs); + } + +-CJX_Delta::~CJX_Delta() {} ++CJX_Delta::~CJX_Delta() = default; + + bool CJX_Delta::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-CJS_Result CJX_Delta::restore(CFX_V8* runtime, ++CJS_Result CJX_Delta::restore(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -32,14 +32,17 @@ CJS_Result CJX_Delta::restore(CFX_V8* runtime, + return CJS_Result::Success(); + } + +-void CJX_Delta::currentValue(CFXJSE_Value* pValue, ++void CJX_Delta::currentValue(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) {} + +-void CJX_Delta::savedValue(CFXJSE_Value* pValue, ++void CJX_Delta::savedValue(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) {} + +-void CJX_Delta::target(CFXJSE_Value* pValue, ++void CJX_Delta::target(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) {} +diff --git a/fxjs/xfa/cjx_delta.h b/fxjs/xfa/cjx_delta.h +index 716dd1e3f..186255823 100644 +--- a/fxjs/xfa/cjx_delta.h ++++ b/fxjs/xfa/cjx_delta.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Delta; + + class CJX_Delta final : public CJX_Object { + public: +- explicit CJX_Delta(CXFA_Delta* delta); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Delta() override; + + // CJX_Object: +@@ -27,6 +27,8 @@ class CJX_Delta final : public CJX_Object { + JSE_PROP(target); + + private: ++ explicit CJX_Delta(CXFA_Delta* delta); ++ + using Type__ = CJX_Delta; + using ParentType__ = CJX_Object; + +diff --git a/fxjs/xfa/cjx_desc.cpp b/fxjs/xfa/cjx_desc.cpp +index 2bbff2b7b..792f076c3 100644 +--- a/fxjs/xfa/cjx_desc.cpp ++++ b/fxjs/xfa/cjx_desc.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,6 +11,7 @@ + #include "fxjs/cfx_v8.h" + #include "fxjs/js_resources.h" + #include "fxjs/xfa/cfxjse_value.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fxfa/parser/cxfa_desc.h" + + const CJX_MethodSpec CJX_Desc::MethodSpecs[] = {{"metadata", metadata_static}}; +@@ -19,13 +20,13 @@ CJX_Desc::CJX_Desc(CXFA_Desc* desc) : CJX_Node(desc) { + DefineMethods(MethodSpecs); + } + +-CJX_Desc::~CJX_Desc() {} ++CJX_Desc::~CJX_Desc() = default; + + bool CJX_Desc::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-CJS_Result CJX_Desc::metadata(CFX_V8* runtime, ++CJS_Result CJX_Desc::metadata(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 0 && params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +diff --git a/fxjs/xfa/cjx_desc.h b/fxjs/xfa/cjx_desc.h +index 62cdec8b0..5474d5685 100644 +--- a/fxjs/xfa/cjx_desc.h ++++ b/fxjs/xfa/cjx_desc.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Desc; + + class CJX_Desc final : public CJX_Node { + public: +- explicit CJX_Desc(CXFA_Desc* desc); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Desc() override; + + // CJX_Object: +@@ -23,6 +23,8 @@ class CJX_Desc final : public CJX_Node { + JSE_METHOD(metadata); + + private: ++ explicit CJX_Desc(CXFA_Desc* desc); ++ + using Type__ = CJX_Desc; + using ParentType__ = CJX_Node; + +diff --git a/fxjs/xfa/cjx_draw.cpp b/fxjs/xfa/cjx_draw.cpp +index 4e8ddc1af..82307b06d 100644 +--- a/fxjs/xfa/cjx_draw.cpp ++++ b/fxjs/xfa/cjx_draw.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,11 @@ + + #include "fxjs/xfa/cjx_draw.h" + ++#include "fxjs/fxv8.h" + #include "fxjs/xfa/cfxjse_value.h" ++#include "third_party/base/check.h" ++#include "v8/include/v8-primitive.h" ++#include "v8/include/v8-value.h" + #include "xfa/fxfa/parser/cxfa_draw.h" + + CJX_Draw::CJX_Draw(CXFA_Draw* node) : CJX_Container(node) {} +@@ -17,32 +21,33 @@ bool CJX_Draw::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-void CJX_Draw::rawValue(CFXJSE_Value* pValue, ++void CJX_Draw::rawValue(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- defaultValue(pValue, bSetting, eAttribute); ++ defaultValue(pIsolate, pValue, bSetting, eAttribute); + } + +-void CJX_Draw::defaultValue(CFXJSE_Value* pValue, ++void CJX_Draw::defaultValue(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (!bSetting) { +- WideString content = GetContent(true); +- if (content.IsEmpty()) +- pValue->SetNull(); +- else +- pValue->SetString(content.ToUTF8().AsStringView()); +- ++ ByteString content = GetContent(true).ToUTF8(); ++ *pValue = content.IsEmpty() ++ ? fxv8::NewNullHelper(pIsolate).As() ++ : fxv8::NewStringHelper(pIsolate, content.AsStringView()) ++ .As(); + return; + } + +- if (!pValue || !pValue->IsString()) ++ if (!pValue || !fxv8::IsString(*pValue)) + return; + +- ASSERT(GetXFANode()->IsWidgetReady()); ++ DCHECK(GetXFANode()->IsWidgetReady()); + if (GetXFANode()->GetFFWidgetType() != XFA_FFWidgetType::kText) + return; + +- WideString wsNewValue = pValue->ToWideString(); ++ WideString wsNewValue = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue); + SetContent(wsNewValue, wsNewValue, true, true, true); + } +diff --git a/fxjs/xfa/cjx_draw.h b/fxjs/xfa/cjx_draw.h +index bf3c3a774..a813f15e1 100644 +--- a/fxjs/xfa/cjx_draw.h ++++ b/fxjs/xfa/cjx_draw.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Draw; + + class CJX_Draw final : public CJX_Container { + public: +- explicit CJX_Draw(CXFA_Draw* node); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Draw() override; + + // CJX_Object: +@@ -24,6 +24,8 @@ class CJX_Draw final : public CJX_Container { + JSE_PROP(rawValue); + + private: ++ explicit CJX_Draw(CXFA_Draw* node); ++ + using Type__ = CJX_Draw; + using ParentType__ = CJX_Container; + +diff --git a/fxjs/xfa/cjx_encrypt.cpp b/fxjs/xfa/cjx_encrypt.cpp +index f50133d1e..51e02d6d1 100644 +--- a/fxjs/xfa/cjx_encrypt.cpp ++++ b/fxjs/xfa/cjx_encrypt.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,6 +16,7 @@ bool CJX_Encrypt::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-void CJX_Encrypt::format(CFXJSE_Value* pValue, ++void CJX_Encrypt::format(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) {} +diff --git a/fxjs/xfa/cjx_encrypt.h b/fxjs/xfa/cjx_encrypt.h +index 826bc1ffd..c7d4a46c9 100644 +--- a/fxjs/xfa/cjx_encrypt.h ++++ b/fxjs/xfa/cjx_encrypt.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Encrypt; + + class CJX_Encrypt final : public CJX_Node { + public: +- explicit CJX_Encrypt(CXFA_Encrypt* node); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Encrypt() override; + + // CJX_Object: +@@ -23,6 +23,8 @@ class CJX_Encrypt final : public CJX_Node { + JSE_PROP(format); + + private: ++ explicit CJX_Encrypt(CXFA_Encrypt* node); ++ + using Type__ = CJX_Encrypt; + using ParentType__ = CJX_Node; + +diff --git a/fxjs/xfa/cjx_eventpseudomodel.cpp b/fxjs/xfa/cjx_eventpseudomodel.cpp +index d037eb739..72309dc4f 100644 +--- a/fxjs/xfa/cjx_eventpseudomodel.cpp ++++ b/fxjs/xfa/cjx_eventpseudomodel.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,37 +9,48 @@ + #include + #include + ++#include "fxjs/fxv8.h" + #include "fxjs/xfa/cfxjse_engine.h" +-#include "fxjs/xfa/cfxjse_value.h" ++#include "third_party/base/notreached.h" ++#include "third_party/base/numerics/safe_conversions.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fxfa/cxfa_eventparam.h" + #include "xfa/fxfa/cxfa_ffnotify.h" +-#include "xfa/fxfa/cxfa_ffwidgethandler.h" + #include "xfa/fxfa/parser/cscript_eventpseudomodel.h" + + namespace { + +-void StringProperty(CFXJSE_Value* pReturn, WideString* wsValue, bool bSetting) { ++void StringProperty(v8::Isolate* pIsolate, ++ v8::Local* pReturn, ++ WideString* wsValue, ++ bool bSetting) { + if (bSetting) { +- *wsValue = pReturn->ToWideString(); ++ *wsValue = fxv8::ReentrantToWideStringHelper(pIsolate, *pReturn); + return; + } +- pReturn->SetString(wsValue->ToUTF8().AsStringView()); ++ *pReturn = fxv8::NewStringHelper(pIsolate, wsValue->ToUTF8().AsStringView()); + } + +-void IntegerProperty(CFXJSE_Value* pReturn, int32_t* iValue, bool bSetting) { ++void IntegerProperty(v8::Isolate* pIsolate, ++ v8::Local* pReturn, ++ int32_t* iValue, ++ bool bSetting) { + if (bSetting) { +- *iValue = pReturn->ToInteger(); ++ *iValue = fxv8::ReentrantToInt32Helper(pIsolate, *pReturn); + return; + } +- pReturn->SetInteger(*iValue); ++ *pReturn = fxv8::NewNumberHelper(pIsolate, *iValue); + } + +-void BooleanProperty(CFXJSE_Value* pReturn, bool* bValue, bool bSetting) { ++void BooleanProperty(v8::Isolate* pIsolate, ++ v8::Local* pReturn, ++ bool* bValue, ++ bool bSetting) { + if (bSetting) { +- *bValue = pReturn->ToBoolean(); ++ *bValue = fxv8::ReentrantToBooleanHelper(pIsolate, *pReturn); + return; + } +- pReturn->SetBoolean(*bValue); ++ *pReturn = fxv8::NewBooleanHelper(pIsolate, *bValue); + } + + } // namespace +@@ -53,55 +64,63 @@ CJX_EventPseudoModel::CJX_EventPseudoModel(CScript_EventPseudoModel* model) + DefineMethods(MethodSpecs); + } + +-CJX_EventPseudoModel::~CJX_EventPseudoModel() {} ++CJX_EventPseudoModel::~CJX_EventPseudoModel() = default; + + bool CJX_EventPseudoModel::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-void CJX_EventPseudoModel::cancelAction(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::cancelAction(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- Property(pValue, XFA_Event::CancelAction, bSetting); ++ Property(pIsolate, pValue, XFA_Event::CancelAction, bSetting); + } + +-void CJX_EventPseudoModel::change(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::change(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- Property(pValue, XFA_Event::Change, bSetting); ++ Property(pIsolate, pValue, XFA_Event::Change, bSetting); + } + +-void CJX_EventPseudoModel::commitKey(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::commitKey(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- Property(pValue, XFA_Event::CommitKey, bSetting); ++ Property(pIsolate, pValue, XFA_Event::CommitKey, bSetting); + } + +-void CJX_EventPseudoModel::fullText(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::fullText(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- Property(pValue, XFA_Event::FullText, bSetting); ++ Property(pIsolate, pValue, XFA_Event::FullText, bSetting); + } + +-void CJX_EventPseudoModel::keyDown(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::keyDown(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- Property(pValue, XFA_Event::Keydown, bSetting); ++ Property(pIsolate, pValue, XFA_Event::Keydown, bSetting); + } + +-void CJX_EventPseudoModel::modifier(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::modifier(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- Property(pValue, XFA_Event::Modifier, bSetting); ++ Property(pIsolate, pValue, XFA_Event::Modifier, bSetting); + } + +-void CJX_EventPseudoModel::newContentType(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::newContentType(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- Property(pValue, XFA_Event::NewContentType, bSetting); ++ Property(pIsolate, pValue, XFA_Event::NewContentType, bSetting); + } + +-void CJX_EventPseudoModel::newText(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::newText(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) +@@ -112,65 +131,75 @@ void CJX_EventPseudoModel::newText(CFXJSE_Value* pValue, + if (!pEventParam) + return; + +- pValue->SetString(pEventParam->GetNewText().ToUTF8().AsStringView()); ++ *pValue = fxv8::NewStringHelper( ++ pIsolate, pEventParam->GetNewText().ToUTF8().AsStringView()); + } + +-void CJX_EventPseudoModel::prevContentType(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::prevContentType(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- Property(pValue, XFA_Event::PreviousContentType, bSetting); ++ Property(pIsolate, pValue, XFA_Event::PreviousContentType, bSetting); + } + +-void CJX_EventPseudoModel::prevText(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::prevText(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- Property(pValue, XFA_Event::PreviousText, bSetting); ++ Property(pIsolate, pValue, XFA_Event::PreviousText, bSetting); + } + +-void CJX_EventPseudoModel::reenter(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::reenter(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- Property(pValue, XFA_Event::Reenter, bSetting); ++ Property(pIsolate, pValue, XFA_Event::Reenter, bSetting); + } + +-void CJX_EventPseudoModel::selEnd(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::selEnd(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- Property(pValue, XFA_Event::SelectionEnd, bSetting); ++ Property(pIsolate, pValue, XFA_Event::SelectionEnd, bSetting); + } + +-void CJX_EventPseudoModel::selStart(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::selStart(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- Property(pValue, XFA_Event::SelectionStart, bSetting); ++ Property(pIsolate, pValue, XFA_Event::SelectionStart, bSetting); + } + +-void CJX_EventPseudoModel::shift(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::shift(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- Property(pValue, XFA_Event::Shift, bSetting); ++ Property(pIsolate, pValue, XFA_Event::Shift, bSetting); + } + +-void CJX_EventPseudoModel::soapFaultCode(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::soapFaultCode(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- Property(pValue, XFA_Event::SoapFaultCode, bSetting); ++ Property(pIsolate, pValue, XFA_Event::SoapFaultCode, bSetting); + } + +-void CJX_EventPseudoModel::soapFaultString(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::soapFaultString(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- Property(pValue, XFA_Event::SoapFaultString, bSetting); ++ Property(pIsolate, pValue, XFA_Event::SoapFaultString, bSetting); + } + +-void CJX_EventPseudoModel::target(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::target(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- Property(pValue, XFA_Event::Target, bSetting); ++ Property(pIsolate, pValue, XFA_Event::Target, bSetting); + } + + CJS_Result CJX_EventPseudoModel::emit( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext(); + CXFA_EventParam* pEventParam = pScriptContext->GetEventParam(); +@@ -181,16 +210,12 @@ CJS_Result CJX_EventPseudoModel::emit( + if (!pNotify) + return CJS_Result::Success(); + +- CXFA_FFWidgetHandler* pWidgetHandler = pNotify->GetWidgetHandler(); +- if (!pWidgetHandler) +- return CJS_Result::Success(); +- +- pWidgetHandler->ProcessEvent(pEventParam->m_pTarget.Get(), pEventParam); ++ pNotify->HandleWidgetEvent(pScriptContext->GetEventTarget(), pEventParam); + return CJS_Result::Success(); + } + + CJS_Result CJX_EventPseudoModel::reset( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext(); + CXFA_EventParam* pEventParam = pScriptContext->GetEventParam(); +@@ -200,7 +225,8 @@ CJS_Result CJX_EventPseudoModel::reset( + return CJS_Result::Success(); + } + +-void CJX_EventPseudoModel::Property(CFXJSE_Value* pValue, ++void CJX_EventPseudoModel::Property(v8::Isolate* pIsolate, ++ v8::Local* pValue, + XFA_Event dwFlag, + bool bSetting) { + // Only the cancelAction, selStart, selEnd and change properties are writable. +@@ -217,65 +243,70 @@ void CJX_EventPseudoModel::Property(CFXJSE_Value* pValue, + + switch (dwFlag) { + case XFA_Event::CancelAction: +- BooleanProperty(pValue, &pEventParam->m_bCancelAction, bSetting); ++ BooleanProperty(pIsolate, pValue, &pEventParam->m_bCancelAction, ++ bSetting); + break; + case XFA_Event::Change: +- StringProperty(pValue, &pEventParam->m_wsChange, bSetting); ++ StringProperty(pIsolate, pValue, &pEventParam->m_wsChange, bSetting); + break; + case XFA_Event::CommitKey: +- IntegerProperty(pValue, &pEventParam->m_iCommitKey, bSetting); ++ IntegerProperty(pIsolate, pValue, &pEventParam->m_iCommitKey, bSetting); + break; + case XFA_Event::FullText: +- StringProperty(pValue, &pEventParam->m_wsFullText, bSetting); ++ StringProperty(pIsolate, pValue, &pEventParam->m_wsFullText, bSetting); + break; + case XFA_Event::Keydown: +- BooleanProperty(pValue, &pEventParam->m_bKeyDown, bSetting); ++ BooleanProperty(pIsolate, pValue, &pEventParam->m_bKeyDown, bSetting); + break; + case XFA_Event::Modifier: +- BooleanProperty(pValue, &pEventParam->m_bModifier, bSetting); ++ BooleanProperty(pIsolate, pValue, &pEventParam->m_bModifier, bSetting); + break; + case XFA_Event::NewContentType: +- StringProperty(pValue, &pEventParam->m_wsNewContentType, bSetting); ++ StringProperty(pIsolate, pValue, &pEventParam->m_wsNewContentType, ++ bSetting); + break; + case XFA_Event::NewText: + NOTREACHED(); + break; + case XFA_Event::PreviousContentType: +- StringProperty(pValue, &pEventParam->m_wsPrevContentType, bSetting); ++ StringProperty(pIsolate, pValue, &pEventParam->m_wsPrevContentType, ++ bSetting); + break; + case XFA_Event::PreviousText: +- StringProperty(pValue, &pEventParam->m_wsPrevText, bSetting); ++ StringProperty(pIsolate, pValue, &pEventParam->m_wsPrevText, bSetting); + break; + case XFA_Event::Reenter: +- BooleanProperty(pValue, &pEventParam->m_bReenter, bSetting); ++ BooleanProperty(pIsolate, pValue, &pEventParam->m_bReenter, bSetting); + break; + case XFA_Event::SelectionEnd: +- IntegerProperty(pValue, &pEventParam->m_iSelEnd, bSetting); ++ IntegerProperty(pIsolate, pValue, &pEventParam->m_iSelEnd, bSetting); + + pEventParam->m_iSelEnd = std::max(0, pEventParam->m_iSelEnd); +- pEventParam->m_iSelEnd = +- std::min(static_cast(pEventParam->m_iSelEnd), +- pEventParam->m_wsPrevText.GetLength()); ++ pEventParam->m_iSelEnd = std::min( ++ pEventParam->m_iSelEnd, pdfium::base::checked_cast( ++ pEventParam->m_wsPrevText.GetLength())); + pEventParam->m_iSelStart = + std::min(pEventParam->m_iSelStart, pEventParam->m_iSelEnd); + break; + case XFA_Event::SelectionStart: +- IntegerProperty(pValue, &pEventParam->m_iSelStart, bSetting); ++ IntegerProperty(pIsolate, pValue, &pEventParam->m_iSelStart, bSetting); + pEventParam->m_iSelStart = std::max(0, pEventParam->m_iSelStart); +- pEventParam->m_iSelStart = +- std::min(static_cast(pEventParam->m_iSelStart), +- pEventParam->m_wsPrevText.GetLength()); ++ pEventParam->m_iSelStart = std::min( ++ pEventParam->m_iSelStart, pdfium::base::checked_cast( ++ pEventParam->m_wsPrevText.GetLength())); + pEventParam->m_iSelEnd = + std::max(pEventParam->m_iSelStart, pEventParam->m_iSelEnd); + break; + case XFA_Event::Shift: +- BooleanProperty(pValue, &pEventParam->m_bShift, bSetting); ++ BooleanProperty(pIsolate, pValue, &pEventParam->m_bShift, bSetting); + break; + case XFA_Event::SoapFaultCode: +- StringProperty(pValue, &pEventParam->m_wsSoapFaultCode, bSetting); ++ StringProperty(pIsolate, pValue, &pEventParam->m_wsSoapFaultCode, ++ bSetting); + break; + case XFA_Event::SoapFaultString: +- StringProperty(pValue, &pEventParam->m_wsSoapFaultString, bSetting); ++ StringProperty(pIsolate, pValue, &pEventParam->m_wsSoapFaultString, ++ bSetting); + break; + case XFA_Event::Target: + default: +diff --git a/fxjs/xfa/cjx_eventpseudomodel.h b/fxjs/xfa/cjx_eventpseudomodel.h +index 6f3cc84a8..3be881a13 100644 +--- a/fxjs/xfa/cjx_eventpseudomodel.h ++++ b/fxjs/xfa/cjx_eventpseudomodel.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,7 +10,6 @@ + #include "fxjs/xfa/cjx_object.h" + #include "fxjs/xfa/jse_define.h" + +-class CFXJSE_Value; + class CScript_EventPseudoModel; + + enum class XFA_Event { +@@ -35,7 +34,7 @@ enum class XFA_Event { + + class CJX_EventPseudoModel final : public CJX_Object { + public: +- explicit CJX_EventPseudoModel(CScript_EventPseudoModel* model); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_EventPseudoModel() override; + + // CJX_Object: +@@ -63,13 +62,18 @@ class CJX_EventPseudoModel final : public CJX_Object { + JSE_PROP(target); + + private: ++ explicit CJX_EventPseudoModel(CScript_EventPseudoModel* model); ++ + using Type__ = CJX_EventPseudoModel; + using ParentType__ = CJX_Object; + + static const TypeTag static_type__ = TypeTag::EventPseudoModel; + static const CJX_MethodSpec MethodSpecs[]; + +- void Property(CFXJSE_Value* pValue, XFA_Event dwFlag, bool bSetting); ++ void Property(v8::Isolate* pIsolate, ++ v8::Local* pValue, ++ XFA_Event dwFlag, ++ bool bSetting); + }; + + #endif // FXJS_XFA_CJX_EVENTPSEUDOMODEL_H_ +diff --git a/fxjs/xfa/cjx_exclgroup.cpp b/fxjs/xfa/cjx_exclgroup.cpp +index 00bf53929..11e6982e2 100644 +--- a/fxjs/xfa/cjx_exclgroup.cpp ++++ b/fxjs/xfa/cjx_exclgroup.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,9 +8,11 @@ + + #include + ++#include "fxjs/fxv8.h" + #include "fxjs/js_resources.h" + #include "fxjs/xfa/cfxjse_engine.h" +-#include "fxjs/xfa/cfxjse_value.h" ++#include "v8/include/v8-object.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fxfa/cxfa_eventparam.h" + #include "xfa/fxfa/cxfa_ffnotify.h" + #include "xfa/fxfa/fxfa.h" +@@ -28,14 +30,14 @@ CJX_ExclGroup::CJX_ExclGroup(CXFA_ExclGroup* group) : CJX_Node(group) { + DefineMethods(MethodSpecs); + } + +-CJX_ExclGroup::~CJX_ExclGroup() {} ++CJX_ExclGroup::~CJX_ExclGroup() = default; + + bool CJX_ExclGroup::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + + CJS_Result CJX_ExclGroup::execEvent( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -46,7 +48,7 @@ CJS_Result CJX_ExclGroup::execEvent( + } + + CJS_Result CJX_ExclGroup::execInitialize( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -59,7 +61,7 @@ CJS_Result CJX_ExclGroup::execInitialize( + } + + CJS_Result CJX_ExclGroup::execCalculate( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -72,7 +74,7 @@ CJS_Result CJX_ExclGroup::execCalculate( + } + + CJS_Result CJX_ExclGroup::execValidate( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -88,7 +90,7 @@ CJS_Result CJX_ExclGroup::execValidate( + } + + CJS_Result CJX_ExclGroup::selectedMember( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -102,20 +104,18 @@ CJS_Result CJX_ExclGroup::selectedMember( + pReturnNode = node->GetSelectedMember(); + } else { + pReturnNode = node->SetSelectedMember( +- runtime->ToWideString(params[0]).AsStringView(), true); ++ runtime->ToWideString(params[0]).AsStringView()); + } + if (!pReturnNode) + return CJS_Result::Success(runtime->NewNull()); + +- CFXJSE_Value* value = +- GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap( +- pReturnNode); +- + return CJS_Result::Success( +- value->DirectGetValue().Get(runtime->GetIsolate())); ++ GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap( ++ pReturnNode)); + } + +-void CJX_ExclGroup::defaultValue(CFXJSE_Value* pValue, ++void CJX_ExclGroup::defaultValue(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_Node* node = GetXFANode(); +@@ -123,33 +123,37 @@ void CJX_ExclGroup::defaultValue(CFXJSE_Value* pValue, + return; + + if (bSetting) { +- node->SetSelectedMemberByValue(pValue->ToWideString().AsStringView(), true, +- true, true); ++ node->SetSelectedMemberByValue( ++ fxv8::ReentrantToWideStringHelper(pIsolate, *pValue).AsStringView(), ++ true, true, true); + return; + } + + WideString wsValue = GetContent(true); + XFA_VERSION curVersion = GetDocument()->GetCurVersionMode(); + if (wsValue.IsEmpty() && curVersion >= XFA_VERSION_300) { +- pValue->SetNull(); ++ *pValue = fxv8::NewNullHelper(pIsolate); + return; + } +- pValue->SetString(wsValue.ToUTF8().AsStringView()); ++ *pValue = fxv8::NewStringHelper(pIsolate, wsValue.ToUTF8().AsStringView()); + } + +-void CJX_ExclGroup::rawValue(CFXJSE_Value* pValue, ++void CJX_ExclGroup::rawValue(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- defaultValue(pValue, bSetting, eAttribute); ++ defaultValue(pIsolate, pValue, bSetting, eAttribute); + } + +-void CJX_ExclGroup::transient(CFXJSE_Value* pValue, ++void CJX_ExclGroup::transient(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) {} + +-void CJX_ExclGroup::errorText(CFXJSE_Value* pValue, ++void CJX_ExclGroup::errorText(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + } +diff --git a/fxjs/xfa/cjx_exclgroup.h b/fxjs/xfa/cjx_exclgroup.h +index b45980807..f6a983cd2 100644 +--- a/fxjs/xfa/cjx_exclgroup.h ++++ b/fxjs/xfa/cjx_exclgroup.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_ExclGroup; + + class CJX_ExclGroup final : public CJX_Node { + public: +- explicit CJX_ExclGroup(CXFA_ExclGroup* group); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_ExclGroup() override; + + // CJX_Object: +@@ -32,6 +32,8 @@ class CJX_ExclGroup final : public CJX_Node { + JSE_PROP(transient); + + private: ++ explicit CJX_ExclGroup(CXFA_ExclGroup* group); ++ + using Type__ = CJX_ExclGroup; + using ParentType__ = CJX_Node; + +diff --git a/fxjs/xfa/cjx_extras.cpp b/fxjs/xfa/cjx_extras.cpp +index a2e2eeada..74cdbba9a 100644 +--- a/fxjs/xfa/cjx_extras.cpp ++++ b/fxjs/xfa/cjx_extras.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,6 +16,7 @@ bool CJX_Extras::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-void CJX_Extras::type(CFXJSE_Value* pValue, ++void CJX_Extras::type(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) {} +diff --git a/fxjs/xfa/cjx_extras.h b/fxjs/xfa/cjx_extras.h +index 072357576..6a1b2129d 100644 +--- a/fxjs/xfa/cjx_extras.h ++++ b/fxjs/xfa/cjx_extras.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Extras; + + class CJX_Extras final : public CJX_Node { + public: +- explicit CJX_Extras(CXFA_Extras* node); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Extras() override; + + // CJX_Object: +@@ -23,6 +23,8 @@ class CJX_Extras final : public CJX_Node { + JSE_PROP(type); + + private: ++ explicit CJX_Extras(CXFA_Extras* node); ++ + using Type__ = CJX_Extras; + using ParentType__ = CJX_Node; + +diff --git a/fxjs/xfa/cjx_field.cpp b/fxjs/xfa/cjx_field.cpp +index ef334a3f7..8d158dc40 100644 +--- a/fxjs/xfa/cjx_field.cpp ++++ b/fxjs/xfa/cjx_field.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,8 +9,10 @@ + #include + + #include "fxjs/cfx_v8.h" ++#include "fxjs/fxv8.h" + #include "fxjs/js_resources.h" +-#include "fxjs/xfa/cfxjse_value.h" ++#include "third_party/base/numerics/safe_conversions.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fgas/crt/cfgas_decimal.h" + #include "xfa/fxfa/cxfa_eventparam.h" + #include "xfa/fxfa/cxfa_ffnotify.h" +@@ -37,14 +39,14 @@ CJX_Field::CJX_Field(CXFA_Field* field) : CJX_Container(field) { + DefineMethods(MethodSpecs); + } + +-CJX_Field::~CJX_Field() {} ++CJX_Field::~CJX_Field() = default; + + bool CJX_Field::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + + CJS_Result CJX_Field::clearItems( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + CXFA_Node* node = GetXFANode(); + if (node->IsWidgetReady()) +@@ -53,7 +55,7 @@ CJS_Result CJX_Field::clearItems( + } + + CJS_Result CJX_Field::execEvent( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -69,7 +71,7 @@ CJS_Result CJX_Field::execEvent( + } + + CJS_Result CJX_Field::execInitialize( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -83,7 +85,7 @@ CJS_Result CJX_Field::execInitialize( + } + + CJS_Result CJX_Field::deleteItem( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -97,7 +99,7 @@ CJS_Result CJX_Field::deleteItem( + } + + CJS_Result CJX_Field::getSaveItem( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -110,8 +112,8 @@ CJS_Result CJX_Field::getSaveItem( + if (!node->IsWidgetReady()) + return CJS_Result::Success(runtime->NewNull()); + +- Optional value = node->GetChoiceListItem(iIndex, true); +- if (!value) ++ absl::optional value = node->GetChoiceListItem(iIndex, true); ++ if (!value.has_value()) + return CJS_Result::Success(runtime->NewNull()); + + return CJS_Result::Success( +@@ -119,7 +121,7 @@ CJS_Result CJX_Field::getSaveItem( + } + + CJS_Result CJX_Field::boundItem( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -135,7 +137,7 @@ CJS_Result CJX_Field::boundItem( + } + + CJS_Result CJX_Field::getItemState( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -149,7 +151,7 @@ CJS_Result CJX_Field::getItemState( + } + + CJS_Result CJX_Field::execCalculate( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -163,7 +165,7 @@ CJS_Result CJX_Field::execCalculate( + } + + CJS_Result CJX_Field::getDisplayItem( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -176,8 +178,8 @@ CJS_Result CJX_Field::getDisplayItem( + if (!node->IsWidgetReady()) + return CJS_Result::Success(runtime->NewNull()); + +- Optional value = node->GetChoiceListItem(iIndex, false); +- if (!value) ++ absl::optional value = node->GetChoiceListItem(iIndex, false); ++ if (!value.has_value()) + return CJS_Result::Success(runtime->NewNull()); + + return CJS_Result::Success( +@@ -185,7 +187,7 @@ CJS_Result CJX_Field::getDisplayItem( + } + + CJS_Result CJX_Field::setItemState( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 2) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -196,16 +198,16 @@ CJS_Result CJX_Field::setItemState( + + int32_t iIndex = runtime->ToInt32(params[0]); + if (runtime->ToInt32(params[1]) != 0) { +- node->SetItemState(iIndex, true, true, true, true); ++ node->SetItemState(iIndex, true, true, true); + return CJS_Result::Success(); + } + if (node->GetItemState(iIndex)) +- node->SetItemState(iIndex, false, true, true, true); ++ node->SetItemState(iIndex, false, true, true); + + return CJS_Result::Success(); + } + +-CJS_Result CJX_Field::addItem(CFX_V8* runtime, ++CJS_Result CJX_Field::addItem(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1 && params.size() != 2) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -227,7 +229,7 @@ CJS_Result CJX_Field::addItem(CFX_V8* runtime, + } + + CJS_Result CJX_Field::execValidate( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -242,7 +244,8 @@ CJS_Result CJX_Field::execValidate( + runtime->NewBoolean(iRet != XFA_EventError::kError)); + } + +-void CJX_Field::defaultValue(CFXJSE_Value* pValue, ++void CJX_Field::defaultValue(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_Node* xfaNode = GetXFANode(); +@@ -252,12 +255,12 @@ void CJX_Field::defaultValue(CFXJSE_Value* pValue, + if (bSetting) { + if (pValue) { + xfaNode->SetPreNull(xfaNode->IsNull()); +- xfaNode->SetIsNull(pValue->IsNull()); ++ xfaNode->SetIsNull(fxv8::IsNull(*pValue)); + } + + WideString wsNewText; +- if (pValue && !(pValue->IsNull() || pValue->IsUndefined())) +- wsNewText = pValue->ToWideString(); ++ if (pValue && !(fxv8::IsNull(*pValue) || fxv8::IsUndefined(*pValue))) ++ wsNewText = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue); + if (xfaNode->GetUIChildNode()->GetElementType() == XFA_Element::NumericEdit) + wsNewText = xfaNode->NumericLimit(wsNewText); + +@@ -272,7 +275,7 @@ void CJX_Field::defaultValue(CFXJSE_Value* pValue, + + WideString content = GetContent(true); + if (content.IsEmpty()) { +- pValue->SetNull(); ++ *pValue = fxv8::NewNullHelper(pIsolate); + return; + } + +@@ -282,24 +285,27 @@ void CJX_Field::defaultValue(CFXJSE_Value* pValue, + if (xfaNode->GetUIChildNode()->GetElementType() == + XFA_Element::NumericEdit && + (pNode->JSObject()->GetInteger(XFA_Attribute::FracDigits) == -1)) { +- pValue->SetString(content.ToUTF8().AsStringView()); ++ *pValue = ++ fxv8::NewStringHelper(pIsolate, content.ToUTF8().AsStringView()); + } else { + CFGAS_Decimal decimal(content.AsStringView()); +- pValue->SetFloat(decimal.ToFloat()); ++ *pValue = fxv8::NewNumberHelper(pIsolate, decimal.ToFloat()); + } + } else if (pNode && pNode->GetElementType() == XFA_Element::Integer) { +- pValue->SetInteger(FXSYS_wtoi(content.c_str())); ++ *pValue = fxv8::NewNumberHelper(pIsolate, FXSYS_wtoi(content.c_str())); + } else if (pNode && pNode->GetElementType() == XFA_Element::Boolean) { +- pValue->SetBoolean(FXSYS_wtoi(content.c_str()) != 0); ++ *pValue = ++ fxv8::NewBooleanHelper(pIsolate, FXSYS_wtoi(content.c_str()) != 0); + } else if (pNode && pNode->GetElementType() == XFA_Element::Float) { + CFGAS_Decimal decimal(content.AsStringView()); +- pValue->SetFloat(decimal.ToFloat()); ++ *pValue = fxv8::NewNumberHelper(pIsolate, decimal.ToFloat()); + } else { +- pValue->SetString(content.ToUTF8().AsStringView()); ++ *pValue = fxv8::NewStringHelper(pIsolate, content.ToUTF8().AsStringView()); + } + } + +-void CJX_Field::editValue(CFXJSE_Value* pValue, ++void CJX_Field::editValue(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_Node* node = GetXFANode(); +@@ -307,20 +313,24 @@ void CJX_Field::editValue(CFXJSE_Value* pValue, + return; + + if (bSetting) { +- node->SetValue(XFA_VALUEPICTURE_Edit, pValue->ToWideString()); ++ node->SetValue(XFA_ValuePicture::kEdit, ++ fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); + return; + } +- pValue->SetString( +- node->GetValue(XFA_VALUEPICTURE_Edit).ToUTF8().AsStringView()); ++ *pValue = fxv8::NewStringHelper( ++ pIsolate, ++ node->GetValue(XFA_ValuePicture::kEdit).ToUTF8().AsStringView()); + } + +-void CJX_Field::formatMessage(CFXJSE_Value* pValue, ++void CJX_Field::formatMessage(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- ScriptSomMessage(pValue, bSetting, XFA_SOM_FormatMessage); ++ ScriptSomMessage(pIsolate, pValue, bSetting, SOMMessageType::kFormatMessage); + } + +-void CJX_Field::formattedValue(CFXJSE_Value* pValue, ++void CJX_Field::formattedValue(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_Node* node = GetXFANode(); +@@ -328,40 +338,44 @@ void CJX_Field::formattedValue(CFXJSE_Value* pValue, + return; + + if (bSetting) { +- node->SetValue(XFA_VALUEPICTURE_Display, pValue->ToWideString()); ++ node->SetValue(XFA_ValuePicture::kDisplay, ++ fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); + return; + } +- pValue->SetString( +- node->GetValue(XFA_VALUEPICTURE_Display).ToUTF8().AsStringView()); ++ *pValue = fxv8::NewStringHelper( ++ pIsolate, ++ node->GetValue(XFA_ValuePicture::kDisplay).ToUTF8().AsStringView()); + } + +-void CJX_Field::length(CFXJSE_Value* pValue, ++void CJX_Field::length(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } + + CXFA_Node* node = GetXFANode(); +- if (!node->IsWidgetReady()) { +- pValue->SetInteger(0); +- return; +- } +- pValue->SetInteger(node->CountChoiceListItems(true)); ++ *pValue = fxv8::NewNumberHelper( ++ pIsolate, node->IsWidgetReady() ? pdfium::base::checked_cast( ++ node->CountChoiceListItems(true)) ++ : 0); + } + +-void CJX_Field::parentSubform(CFXJSE_Value* pValue, ++void CJX_Field::parentSubform(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } +- pValue->SetNull(); ++ *pValue = fxv8::NewNullHelper(pIsolate); + } + +-void CJX_Field::selectedIndex(CFXJSE_Value* pValue, ++void CJX_Field::selectedIndex(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_Node* node = GetXFANode(); +@@ -369,21 +383,22 @@ void CJX_Field::selectedIndex(CFXJSE_Value* pValue, + return; + + if (!bSetting) { +- pValue->SetInteger(node->GetSelectedItem(0)); ++ *pValue = fxv8::NewNumberHelper(pIsolate, node->GetSelectedItem(0)); + return; + } + +- int32_t iIndex = pValue->ToInteger(); ++ int32_t iIndex = fxv8::ReentrantToInt32Helper(pIsolate, *pValue); + if (iIndex == -1) { + node->ClearAllSelections(); + return; + } + +- node->SetItemState(iIndex, true, true, true, true); ++ node->SetItemState(iIndex, true, true, true); + } + +-void CJX_Field::rawValue(CFXJSE_Value* pValue, ++void CJX_Field::rawValue(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- defaultValue(pValue, bSetting, eAttribute); ++ defaultValue(pIsolate, pValue, bSetting, eAttribute); + } +diff --git a/fxjs/xfa/cjx_field.h b/fxjs/xfa/cjx_field.h +index 6e16e7af9..5628fc47b 100644 +--- a/fxjs/xfa/cjx_field.h ++++ b/fxjs/xfa/cjx_field.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Field; + + class CJX_Field final : public CJX_Container { + public: +- explicit CJX_Field(CXFA_Field* field); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Field() override; + + // CJX_Object: +@@ -43,6 +43,8 @@ class CJX_Field final : public CJX_Container { + JSE_PROP(selectedIndex); + + private: ++ explicit CJX_Field(CXFA_Field* field); ++ + using Type__ = CJX_Field; + using ParentType__ = CJX_Container; + +diff --git a/fxjs/xfa/cjx_form.cpp b/fxjs/xfa/cjx_form.cpp +index 07e86028d..4dab966f6 100644 +--- a/fxjs/xfa/cjx_form.cpp ++++ b/fxjs/xfa/cjx_form.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,9 +8,12 @@ + + #include + ++#include "fxjs/fxv8.h" + #include "fxjs/js_resources.h" + #include "fxjs/xfa/cfxjse_engine.h" +-#include "fxjs/xfa/cfxjse_value.h" ++#include "v8/include/cppgc/allocation.h" ++#include "v8/include/v8-object.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fxfa/cxfa_eventparam.h" + #include "xfa/fxfa/cxfa_ffnotify.h" + #include "xfa/fxfa/parser/cxfa_arraynodelist.h" +@@ -29,43 +32,42 @@ CJX_Form::CJX_Form(CXFA_Form* form) : CJX_Model(form) { + DefineMethods(MethodSpecs); + } + +-CJX_Form::~CJX_Form() {} ++CJX_Form::~CJX_Form() = default; + + bool CJX_Form::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + + CJS_Result CJX_Form::formNodes( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + +- CXFA_Node* pDataNode = +- ToNode(static_cast(runtime)->ToXFAObject(params[0])); ++ CXFA_Node* pDataNode = ToNode(runtime->ToXFAObject(params[0])); + if (!pDataNode) + return CJS_Result::Failure(JSMessage::kValueError); + +- CXFA_ArrayNodeList* pFormNodes = new CXFA_ArrayNodeList(GetDocument()); +- CFXJSE_Value* value = +- GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap( +- pFormNodes); ++ CXFA_Document* pDoc = GetDocument(); ++ auto* pFormNodes = cppgc::MakeGarbageCollected( ++ pDoc->GetHeap()->GetAllocationHandle(), pDoc); ++ pDoc->GetNodeOwner()->PersistList(pFormNodes); + +- return CJS_Result::Success( +- value->DirectGetValue().Get(runtime->GetIsolate())); ++ v8::Local value = runtime->GetOrCreateJSBindingFromMap(pFormNodes); ++ return CJS_Result::Success(value); + } + +-CJS_Result CJX_Form::remerge(CFX_V8* runtime, ++CJS_Result CJX_Form::remerge(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); + +- GetDocument()->DoDataRemerge(true); ++ GetDocument()->DoDataRemerge(); + return CJS_Result::Success(); + } + + CJS_Result CJX_Form::execInitialize( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -78,7 +80,7 @@ CJS_Result CJX_Form::execInitialize( + } + + CJS_Result CJX_Form::recalculate( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + CXFA_EventParam* pEventParam = + GetDocument()->GetScriptContext()->GetEventParam(); +@@ -100,7 +102,7 @@ CJS_Result CJX_Form::recalculate( + } + + CJS_Result CJX_Form::execCalculate( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -113,7 +115,7 @@ CJS_Result CJX_Form::execCalculate( + } + + CJS_Result CJX_Form::execValidate( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 0) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -128,15 +130,20 @@ CJS_Result CJX_Form::execValidate( + runtime->NewBoolean(iRet != XFA_EventError::kError)); + } + +-void CJX_Form::checksumS(CFXJSE_Value* pValue, ++void CJX_Form::checksumS(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- SetAttribute(XFA_Attribute::Checksum, pValue->ToWideString().AsStringView(), +- false); ++ SetAttributeByEnum(XFA_Attribute::Checksum, ++ fxv8::ReentrantToWideStringHelper(pIsolate, *pValue), ++ false); + return; + } + +- Optional checksum = TryAttribute(XFA_Attribute::Checksum, false); +- pValue->SetString(checksum ? checksum->ToUTF8().AsStringView() : ""); ++ absl::optional checksum = ++ TryAttribute(XFA_Attribute::Checksum, false); ++ *pValue = fxv8::NewStringHelper( ++ pIsolate, ++ checksum.has_value() ? checksum.value().ToUTF8().AsStringView() : ""); + } +diff --git a/fxjs/xfa/cjx_form.h b/fxjs/xfa/cjx_form.h +index c7b1ce36b..05ea04c12 100644 +--- a/fxjs/xfa/cjx_form.h ++++ b/fxjs/xfa/cjx_form.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Form; + + class CJX_Form final : public CJX_Model { + public: +- explicit CJX_Form(CXFA_Form* form); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Form() override; + + // CJX_Object: +@@ -30,6 +30,8 @@ class CJX_Form final : public CJX_Model { + JSE_PROP(checksumS); + + private: ++ explicit CJX_Form(CXFA_Form* form); ++ + using Type__ = CJX_Form; + using ParentType__ = CJX_Model; + +diff --git a/fxjs/xfa/cjx_handler.cpp b/fxjs/xfa/cjx_handler.cpp +index 21be88c58..d8caeca82 100644 +--- a/fxjs/xfa/cjx_handler.cpp ++++ b/fxjs/xfa/cjx_handler.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,6 +16,7 @@ bool CJX_Handler::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-void CJX_Handler::version(CFXJSE_Value* pValue, ++void CJX_Handler::version(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) {} +diff --git a/fxjs/xfa/cjx_handler.h b/fxjs/xfa/cjx_handler.h +index 348eaf140..e5cd2cba5 100644 +--- a/fxjs/xfa/cjx_handler.h ++++ b/fxjs/xfa/cjx_handler.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Handler; + + class CJX_Handler final : public CJX_TextNode { + public: +- explicit CJX_Handler(CXFA_Handler* node); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Handler() override; + + // CJX_Object: +@@ -23,6 +23,8 @@ class CJX_Handler final : public CJX_TextNode { + JSE_PROP(version); + + private: ++ explicit CJX_Handler(CXFA_Handler* node); ++ + using Type__ = CJX_Handler; + using ParentType__ = CJX_TextNode; + +diff --git a/fxjs/xfa/cjx_hostpseudomodel.cpp b/fxjs/xfa/cjx_hostpseudomodel.cpp +index c45495c2f..597db3025 100644 +--- a/fxjs/xfa/cjx_hostpseudomodel.cpp ++++ b/fxjs/xfa/cjx_hostpseudomodel.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,34 +6,33 @@ + + #include "fxjs/xfa/cjx_hostpseudomodel.h" + +-#include + #include + ++#include "fxjs/fxv8.h" + #include "fxjs/js_resources.h" + #include "fxjs/xfa/cfxjse_engine.h" +-#include "fxjs/xfa/cfxjse_value.h" ++#include "third_party/base/check.h" ++#include "v8/include/v8-object.h" + #include "xfa/fxfa/cxfa_ffdoc.h" + #include "xfa/fxfa/cxfa_ffnotify.h" + #include "xfa/fxfa/parser/cscript_hostpseudomodel.h" + #include "xfa/fxfa/parser/cxfa_node.h" +-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h" + + namespace { + +-int32_t FilterName(WideStringView wsExpression, +- int32_t nStart, +- WideString& wsFilter) { +- ASSERT(nStart > -1); +- int32_t iLength = wsExpression.GetLength(); +- if (nStart >= iLength) +- return iLength; ++size_t FilterName(WideStringView wsExpression, ++ size_t nStart, ++ WideString& wsFilter) { ++ const size_t nLength = wsExpression.GetLength(); ++ if (nStart >= nLength) ++ return nLength; + +- int32_t nCount = 0; ++ size_t nCount = 0; + { + // Span's lifetime must end before ReleaseBuffer() below. +- pdfium::span pBuf = wsFilter.GetBuffer(iLength - nStart); ++ pdfium::span pBuf = wsFilter.GetBuffer(nLength - nStart); + const wchar_t* pSrc = wsExpression.unterminated_c_str(); +- while (nStart < iLength) { ++ while (nStart < nLength) { + wchar_t wCur = pSrc[nStart++]; + if (wCur == ',') + break; +@@ -70,13 +69,14 @@ CJX_HostPseudoModel::CJX_HostPseudoModel(CScript_HostPseudoModel* model) + DefineMethods(MethodSpecs); + } + +-CJX_HostPseudoModel::~CJX_HostPseudoModel() {} ++CJX_HostPseudoModel::~CJX_HostPseudoModel() = default; + + bool CJX_HostPseudoModel::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-void CJX_HostPseudoModel::appType(CFXJSE_Value* pValue, ++void CJX_HostPseudoModel::appType(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); +@@ -84,44 +84,47 @@ void CJX_HostPseudoModel::appType(CFXJSE_Value* pValue, + return; + + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } +- pValue->SetString("Exchange"); ++ *pValue = fxv8::NewStringHelper(pIsolate, "Exchange"); + } + +-void CJX_HostPseudoModel::calculationsEnabled(CFXJSE_Value* pValue, ++void CJX_HostPseudoModel::calculationsEnabled(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); + if (!pNotify) + return; + +- CXFA_FFDoc* hDoc = pNotify->GetHDOC(); ++ CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); + if (bSetting) { +- hDoc->GetDocEnvironment()->SetCalculationsEnabled(hDoc, +- pValue->ToBoolean()); ++ hDoc->SetCalculationsEnabled( ++ fxv8::ReentrantToBooleanHelper(pIsolate, *pValue)); + return; + } +- pValue->SetBoolean(hDoc->GetDocEnvironment()->IsCalculationsEnabled(hDoc)); ++ *pValue = fxv8::NewBooleanHelper(pIsolate, hDoc->IsCalculationsEnabled()); + } + +-void CJX_HostPseudoModel::currentPage(CFXJSE_Value* pValue, ++void CJX_HostPseudoModel::currentPage(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); + if (!pNotify) + return; + +- CXFA_FFDoc* hDoc = pNotify->GetHDOC(); ++ CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); + if (bSetting) { +- hDoc->GetDocEnvironment()->SetCurrentPage(hDoc, pValue->ToInteger()); ++ hDoc->SetCurrentPage(fxv8::ReentrantToInt32Helper(pIsolate, *pValue)); + return; + } +- pValue->SetInteger(hDoc->GetDocEnvironment()->GetCurrentPage(hDoc)); ++ *pValue = fxv8::NewNumberHelper(pIsolate, hDoc->GetCurrentPage()); + } + +-void CJX_HostPseudoModel::language(CFXJSE_Value* pValue, ++void CJX_HostPseudoModel::language(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); +@@ -129,29 +132,33 @@ void CJX_HostPseudoModel::language(CFXJSE_Value* pValue, + return; + + if (bSetting) { +- ThrowException(WideString::FromASCII("Unable to set language value.")); ++ ThrowException(pIsolate, ++ WideString::FromASCII("Unable to set language value.")); + return; + } +- pValue->SetString( +- pNotify->GetAppProvider()->GetLanguage().ToUTF8().AsStringView()); ++ ByteString lang = pNotify->GetAppProvider()->GetLanguage().ToUTF8(); ++ *pValue = fxv8::NewStringHelper(pIsolate, lang.AsStringView()); + } + +-void CJX_HostPseudoModel::numPages(CFXJSE_Value* pValue, ++void CJX_HostPseudoModel::numPages(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); + if (!pNotify) + return; + +- CXFA_FFDoc* hDoc = pNotify->GetHDOC(); ++ CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); + if (bSetting) { +- ThrowException(WideString::FromASCII("Unable to set numPages value.")); ++ ThrowException(pIsolate, ++ WideString::FromASCII("Unable to set numPages value.")); + return; + } +- pValue->SetInteger(hDoc->GetDocEnvironment()->CountPages(hDoc)); ++ *pValue = fxv8::NewNumberHelper(pIsolate, hDoc->CountPages()); + } + +-void CJX_HostPseudoModel::platform(CFXJSE_Value* pValue, ++void CJX_HostPseudoModel::platform(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); +@@ -159,14 +166,16 @@ void CJX_HostPseudoModel::platform(CFXJSE_Value* pValue, + return; + + if (bSetting) { +- ThrowException(WideString::FromASCII("Unable to set platform value.")); ++ ThrowException(pIsolate, ++ WideString::FromASCII("Unable to set platform value.")); + return; + } +- pValue->SetString( +- pNotify->GetAppProvider()->GetPlatform().ToUTF8().AsStringView()); ++ ByteString plat = pNotify->GetAppProvider()->GetPlatform().ToUTF8(); ++ *pValue = fxv8::NewStringHelper(pIsolate, plat.AsStringView()); + } + +-void CJX_HostPseudoModel::title(CFXJSE_Value* pValue, ++void CJX_HostPseudoModel::title(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (!GetDocument()->GetScriptContext()->IsRunAtClient()) +@@ -176,58 +185,63 @@ void CJX_HostPseudoModel::title(CFXJSE_Value* pValue, + if (!pNotify) + return; + +- CXFA_FFDoc* hDoc = pNotify->GetHDOC(); ++ CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); + if (bSetting) { +- hDoc->GetDocEnvironment()->SetTitle(hDoc, pValue->ToWideString()); ++ hDoc->SetTitle(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); + return; + } + +- WideString wsTitle; +- hDoc->GetDocEnvironment()->GetTitle(hDoc, wsTitle); +- pValue->SetString(wsTitle.ToUTF8().AsStringView()); ++ ByteString bsTitle = hDoc->GetTitle().ToUTF8(); ++ *pValue = fxv8::NewStringHelper(pIsolate, bsTitle.AsStringView()); + } + +-void CJX_HostPseudoModel::validationsEnabled(CFXJSE_Value* pValue, ++void CJX_HostPseudoModel::validationsEnabled(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); + if (!pNotify) + return; + +- CXFA_FFDoc* hDoc = pNotify->GetHDOC(); ++ CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); + if (bSetting) { +- hDoc->GetDocEnvironment()->SetValidationsEnabled(hDoc, pValue->ToBoolean()); ++ hDoc->SetValidationsEnabled( ++ fxv8::ReentrantToBooleanHelper(pIsolate, *pValue)); + return; + } + +- bool bEnabled = hDoc->GetDocEnvironment()->IsValidationsEnabled(hDoc); +- pValue->SetBoolean(bEnabled); ++ *pValue = fxv8::NewBooleanHelper(pIsolate, hDoc->IsValidationsEnabled()); + } + +-void CJX_HostPseudoModel::variation(CFXJSE_Value* pValue, ++void CJX_HostPseudoModel::variation(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (!GetDocument()->GetScriptContext()->IsRunAtClient()) + return; + + if (bSetting) { +- ThrowException(WideString::FromASCII("Unable to set variation value.")); ++ ThrowException(pIsolate, ++ WideString::FromASCII("Unable to set variation value.")); + return; + } +- pValue->SetString("Full"); ++ *pValue = fxv8::NewStringHelper(pIsolate, "Full"); + } + +-void CJX_HostPseudoModel::version(CFXJSE_Value* pValue, ++void CJX_HostPseudoModel::version(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowException(WideString::FromASCII("Unable to set version value.")); ++ ThrowException(pIsolate, ++ WideString::FromASCII("Unable to set version value.")); + return; + } +- pValue->SetString("11"); ++ *pValue = fxv8::NewStringHelper(pIsolate, "11"); + } + +-void CJX_HostPseudoModel::name(CFXJSE_Value* pValue, ++void CJX_HostPseudoModel::name(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); +@@ -235,15 +249,15 @@ void CJX_HostPseudoModel::name(CFXJSE_Value* pValue, + return; + + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } +- pValue->SetString( +- pNotify->GetAppProvider()->GetAppName().ToUTF8().AsStringView()); ++ ByteString bsName = pNotify->GetAppProvider()->GetAppName().ToUTF8(); ++ *pValue = fxv8::NewStringHelper(pIsolate, bsName.AsStringView()); + } + + CJS_Result CJX_HostPseudoModel::gotoURL( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!GetDocument()->GetScriptContext()->IsRunAtClient()) + return CJS_Result::Success(); +@@ -255,14 +269,12 @@ CJS_Result CJX_HostPseudoModel::gotoURL( + if (!pNotify) + return CJS_Result::Success(); + +- CXFA_FFDoc* hDoc = pNotify->GetHDOC(); +- WideString URL = runtime->ToWideString(params[0]); +- hDoc->GetDocEnvironment()->GotoURL(hDoc, URL); ++ pNotify->GetFFDoc()->GotoURL(runtime->ToWideString(params[0])); + return CJS_Result::Success(); + } + + CJS_Result CJX_HostPseudoModel::openList( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!GetDocument()->GetScriptContext()->IsRunAtClient()) + return CJS_Result::Success(); +@@ -276,26 +288,25 @@ CJS_Result CJX_HostPseudoModel::openList( + + CXFA_Node* pNode = nullptr; + if (params[0]->IsObject()) { +- pNode = +- ToNode(static_cast(runtime)->ToXFAObject(params[0])); ++ pNode = ToNode(runtime->ToXFAObject(params[0])); + } else if (params[0]->IsString()) { + CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext(); + CXFA_Object* pObject = pScriptContext->GetThisObject(); + if (!pObject) + return CJS_Result::Success(); + +- uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Parent | +- XFA_RESOLVENODE_Siblings; +- XFA_RESOLVENODE_RS resolveNodeRS; +- bool bRet = pScriptContext->ResolveObjects( +- pObject, runtime->ToWideString(params[0]).AsStringView(), +- &resolveNodeRS, dwFlag, nullptr); +- if (!bRet || !resolveNodeRS.objects.front()->IsNode()) ++ constexpr Mask kFlags = {XFA_ResolveFlag::kChildren, ++ XFA_ResolveFlag::kParent, ++ XFA_ResolveFlag::kSiblings}; ++ absl::optional maybeResult = ++ pScriptContext->ResolveObjects( ++ pObject, runtime->ToWideString(params[0]).AsStringView(), kFlags); ++ if (!maybeResult.has_value() || ++ !maybeResult.value().objects.front()->IsNode()) { + return CJS_Result::Success(); +- +- pNode = resolveNodeRS.objects.front()->AsNode(); ++ } ++ pNode = maybeResult.value().objects.front()->AsNode(); + } +- + if (pNode) + pNotify->OpenDropDownList(pNode); + +@@ -303,7 +314,7 @@ CJS_Result CJX_HostPseudoModel::openList( + } + + CJS_Result CJX_HostPseudoModel::response( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.empty() || params.size() > 4) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -335,13 +346,13 @@ CJS_Result CJX_HostPseudoModel::response( + } + + CJS_Result CJX_HostPseudoModel::documentInBatch( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return CJS_Result::Success(runtime->NewNumber(0)); + } + + CJS_Result CJX_HostPseudoModel::resetData( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() > 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -359,26 +370,27 @@ CJS_Result CJX_HostPseudoModel::resetData( + return CJS_Result::Success(); + } + +- int32_t iStart = 0; + WideString wsName; + CXFA_Node* pNode = nullptr; +- int32_t iExpLength = expression.GetLength(); +- while (iStart < iExpLength) { +- iStart = FilterName(expression.AsStringView(), iStart, wsName); ++ size_t nStart = 0; ++ const size_t nExpLength = expression.GetLength(); ++ while (nStart < nExpLength) { ++ nStart = FilterName(expression.AsStringView(), nStart, wsName); + CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext(); + CXFA_Object* pObject = pScriptContext->GetThisObject(); + if (!pObject) + return CJS_Result::Success(); + +- uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Parent | +- XFA_RESOLVENODE_Siblings; +- XFA_RESOLVENODE_RS resolveNodeRS; +- bool bRet = pScriptContext->ResolveObjects(pObject, wsName.AsStringView(), +- &resolveNodeRS, dwFlag, nullptr); +- if (!bRet || !resolveNodeRS.objects.front()->IsNode()) ++ constexpr Mask kFlags = {XFA_ResolveFlag::kChildren, ++ XFA_ResolveFlag::kParent, ++ XFA_ResolveFlag::kSiblings}; ++ absl::optional maybeResult = ++ pScriptContext->ResolveObjects(pObject, wsName.AsStringView(), kFlags); ++ if (!maybeResult.has_value() || ++ !maybeResult.value().objects.front()->IsNode()) + continue; + +- pNode = resolveNodeRS.objects.front()->AsNode(); ++ pNode = maybeResult.value().objects.front()->AsNode(); + pNotify->ResetData(pNode->IsWidgetReady() ? pNode : nullptr); + } + if (!pNode) +@@ -388,7 +400,7 @@ CJS_Result CJX_HostPseudoModel::resetData( + } + + CJS_Result CJX_HostPseudoModel::beep( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!GetDocument()->GetScriptContext()->IsRunAtClient()) + return CJS_Result::Success(); +@@ -409,7 +421,7 @@ CJS_Result CJX_HostPseudoModel::beep( + } + + CJS_Result CJX_HostPseudoModel::setFocus( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!GetDocument()->GetScriptContext()->IsRunAtClient()) + return CJS_Result::Success(); +@@ -424,24 +436,24 @@ CJS_Result CJX_HostPseudoModel::setFocus( + CXFA_Node* pNode = nullptr; + if (params.size() >= 1) { + if (params[0]->IsObject()) { +- pNode = +- ToNode(static_cast(runtime)->ToXFAObject(params[0])); ++ pNode = ToNode(runtime->ToXFAObject(params[0])); + } else if (params[0]->IsString()) { + CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext(); + CXFA_Object* pObject = pScriptContext->GetThisObject(); + if (!pObject) + return CJS_Result::Success(); + +- uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Parent | +- XFA_RESOLVENODE_Siblings; +- XFA_RESOLVENODE_RS resolveNodeRS; +- bool bRet = pScriptContext->ResolveObjects( +- pObject, runtime->ToWideString(params[0]).AsStringView(), +- &resolveNodeRS, dwFlag, nullptr); +- if (!bRet || !resolveNodeRS.objects.front()->IsNode()) ++ constexpr Mask kFlags = {XFA_ResolveFlag::kChildren, ++ XFA_ResolveFlag::kParent, ++ XFA_ResolveFlag::kSiblings}; ++ absl::optional maybeResult = ++ pScriptContext->ResolveObjects( ++ pObject, runtime->ToWideString(params[0]).AsStringView(), kFlags); ++ if (!maybeResult.has_value() || ++ !maybeResult.value().objects.front()->IsNode()) { + return CJS_Result::Success(); +- +- pNode = resolveNodeRS.objects.front()->AsNode(); ++ } ++ pNode = maybeResult.value().objects.front()->AsNode(); + } + } + pNotify->SetFocusWidgetNode(pNode); +@@ -449,7 +461,7 @@ CJS_Result CJX_HostPseudoModel::setFocus( + } + + CJS_Result CJX_HostPseudoModel::getFocus( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); + if (!pNotify) +@@ -459,15 +471,14 @@ CJS_Result CJX_HostPseudoModel::getFocus( + if (!pNode) + return CJS_Result::Success(); + +- CFXJSE_Value* value = ++ v8::Local value = + GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode); + +- return CJS_Result::Success( +- value->DirectGetValue().Get(runtime->GetIsolate())); ++ return CJS_Result::Success(value); + } + + CJS_Result CJX_HostPseudoModel::messageBox( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!GetDocument()->GetScriptContext()->IsRunAtClient()) + return CJS_Result::Success(); +@@ -507,13 +518,13 @@ CJS_Result CJX_HostPseudoModel::messageBox( + } + + CJS_Result CJX_HostPseudoModel::documentCountInBatch( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return CJS_Result::Success(runtime->NewNumber(0)); + } + + CJS_Result CJX_HostPseudoModel::print( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!GetDocument()->GetScriptContext()->IsRunAtClient()) + return CJS_Result::Success(); +@@ -525,30 +536,28 @@ CJS_Result CJX_HostPseudoModel::print( + if (!pNotify) + return CJS_Result::Success(); + +- uint32_t dwOptions = 0; ++ Mask dwOptions; + if (runtime->ToBoolean(params[0])) +- dwOptions |= XFA_PRINTOPT_ShowDialog; ++ dwOptions |= XFA_PrintOpt::kShowDialog; + if (runtime->ToBoolean(params[3])) +- dwOptions |= XFA_PRINTOPT_CanCancel; ++ dwOptions |= XFA_PrintOpt::kCanCancel; + if (runtime->ToBoolean(params[4])) +- dwOptions |= XFA_PRINTOPT_ShrinkPage; ++ dwOptions |= XFA_PrintOpt::kShrinkPage; + if (runtime->ToBoolean(params[5])) +- dwOptions |= XFA_PRINTOPT_AsImage; ++ dwOptions |= XFA_PrintOpt::kAsImage; + if (runtime->ToBoolean(params[6])) +- dwOptions |= XFA_PRINTOPT_ReverseOrder; ++ dwOptions |= XFA_PrintOpt::kReverseOrder; + if (runtime->ToBoolean(params[7])) +- dwOptions |= XFA_PRINTOPT_PrintAnnot; ++ dwOptions |= XFA_PrintOpt::kPrintAnnot; + + int32_t nStartPage = runtime->ToInt32(params[1]); + int32_t nEndPage = runtime->ToInt32(params[2]); +- +- CXFA_FFDoc* hDoc = pNotify->GetHDOC(); +- hDoc->GetDocEnvironment()->Print(hDoc, nStartPage, nEndPage, dwOptions); ++ pNotify->GetFFDoc()->Print(nStartPage, nEndPage, dwOptions); + return CJS_Result::Success(); + } + + CJS_Result CJX_HostPseudoModel::importData( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.empty() || params.size() > 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -557,7 +566,7 @@ CJS_Result CJX_HostPseudoModel::importData( + } + + CJS_Result CJX_HostPseudoModel::exportData( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.empty() || params.size() > 2) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -574,39 +583,36 @@ CJS_Result CJX_HostPseudoModel::exportData( + if (params.size() >= 2) + XDP = runtime->ToBoolean(params[1]); + +- CXFA_FFDoc* hDoc = pNotify->GetHDOC(); +- hDoc->GetDocEnvironment()->ExportData(hDoc, filePath, XDP); ++ pNotify->GetFFDoc()->ExportData(filePath, XDP); + return CJS_Result::Success(); + } + + CJS_Result CJX_HostPseudoModel::pageUp( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); + if (!pNotify) + return CJS_Result::Success(); + +- CXFA_FFDoc* hDoc = pNotify->GetHDOC(); +- int32_t nCurPage = hDoc->GetDocEnvironment()->GetCurrentPage(hDoc); +- int32_t nNewPage = 0; ++ CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); ++ int32_t nCurPage = hDoc->GetCurrentPage(); + if (nCurPage <= 1) + return CJS_Result::Success(); + +- nNewPage = nCurPage - 1; +- hDoc->GetDocEnvironment()->SetCurrentPage(hDoc, nNewPage); ++ hDoc->SetCurrentPage(nCurPage - 1); + return CJS_Result::Success(); + } + + CJS_Result CJX_HostPseudoModel::pageDown( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); + if (!pNotify) + return CJS_Result::Success(); + +- CXFA_FFDoc* hDoc = pNotify->GetHDOC(); +- int32_t nCurPage = hDoc->GetDocEnvironment()->GetCurrentPage(hDoc); +- int32_t nPageCount = hDoc->GetDocEnvironment()->CountPages(hDoc); ++ CXFA_FFDoc* hDoc = pNotify->GetFFDoc(); ++ int32_t nCurPage = hDoc->GetCurrentPage(); ++ int32_t nPageCount = hDoc->CountPages(); + if (!nPageCount || nCurPage == nPageCount) + return CJS_Result::Success(); + +@@ -616,6 +622,6 @@ CJS_Result CJX_HostPseudoModel::pageDown( + else + nNewPage = nCurPage + 1; + +- hDoc->GetDocEnvironment()->SetCurrentPage(hDoc, nNewPage); ++ hDoc->SetCurrentPage(nNewPage); + return CJS_Result::Success(); + } +diff --git a/fxjs/xfa/cjx_hostpseudomodel.h b/fxjs/xfa/cjx_hostpseudomodel.h +index cdc293df8..7c1b3b520 100644 +--- a/fxjs/xfa/cjx_hostpseudomodel.h ++++ b/fxjs/xfa/cjx_hostpseudomodel.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,12 +11,11 @@ + #include "fxjs/xfa/jse_define.h" + #include "xfa/fxfa/fxfa_basic.h" + +-class CFXJSE_Value; + class CScript_HostPseudoModel; + + class CJX_HostPseudoModel final : public CJX_Object { + public: +- explicit CJX_HostPseudoModel(CScript_HostPseudoModel* model); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_HostPseudoModel() override; + + // CJX_Object: +@@ -54,6 +53,8 @@ class CJX_HostPseudoModel final : public CJX_Object { + JSE_PROP(name); + + private: ++ explicit CJX_HostPseudoModel(CScript_HostPseudoModel* model); ++ + using Type__ = CJX_HostPseudoModel; + using ParentType__ = CJX_Object; + +diff --git a/fxjs/xfa/cjx_hostpseudomodel_embeddertest.cpp b/fxjs/xfa/cjx_hostpseudomodel_embeddertest.cpp +index 1d4289845..86e0902ad 100644 +--- a/fxjs/xfa/cjx_hostpseudomodel_embeddertest.cpp ++++ b/fxjs/xfa/cjx_hostpseudomodel_embeddertest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxjs/xfa/cjx_instancemanager.cpp b/fxjs/xfa/cjx_instancemanager.cpp +index 160c4a573..18faf93b8 100644 +--- a/fxjs/xfa/cjx_instancemanager.cpp ++++ b/fxjs/xfa/cjx_instancemanager.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,14 +9,18 @@ + #include + #include + ++#include "fxjs/fxv8.h" + #include "fxjs/js_resources.h" + #include "fxjs/xfa/cfxjse_engine.h" +-#include "fxjs/xfa/cfxjse_value.h" ++#include "third_party/base/notreached.h" ++#include "v8/include/v8-object.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fxfa/cxfa_ffdoc.h" + #include "xfa/fxfa/cxfa_ffnotify.h" + #include "xfa/fxfa/parser/cxfa_document.h" + #include "xfa/fxfa/parser/cxfa_instancemanager.h" + #include "xfa/fxfa/parser/cxfa_occur.h" ++#include "xfa/fxfa/parser/cxfa_subform.h" + + const CJX_MethodSpec CJX_InstanceManager::MethodSpecs[] = { + {"addInstance", addInstance_static}, +@@ -30,23 +34,24 @@ CJX_InstanceManager::CJX_InstanceManager(CXFA_InstanceManager* mgr) + DefineMethods(MethodSpecs); + } + +-CJX_InstanceManager::~CJX_InstanceManager() {} ++CJX_InstanceManager::~CJX_InstanceManager() = default; + + bool CJX_InstanceManager::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-int32_t CJX_InstanceManager::SetInstances(int32_t iDesired) { ++int32_t CJX_InstanceManager::SetInstances(v8::Isolate* pIsolate, ++ int32_t iDesired) { + CXFA_Occur* occur = GetXFANode()->GetOccurIfExists(); + int32_t iMin = occur ? occur->GetMin() : CXFA_Occur::kDefaultMin; + if (iDesired < iMin) { +- ThrowTooManyOccurancesException(L"min"); ++ ThrowTooManyOccurrencesException(pIsolate, L"min"); + return 1; + } + + int32_t iMax = occur ? occur->GetMax() : CXFA_Occur::kDefaultMax; + if (iMax >= 0 && iDesired > iMax) { +- ThrowTooManyOccurancesException(L"max"); ++ ThrowTooManyOccurrencesException(pIsolate, L"max"); + return 2; + } + +@@ -61,13 +66,13 @@ int32_t CJX_InstanceManager::SetInstances(int32_t iDesired) { + ? wsInstManagerName + : wsInstManagerName.Last(wsInstManagerName.GetLength() - 1)); + uint32_t dInstanceNameHash = +- FX_HashCode_GetW(wsInstanceName.AsStringView(), false); ++ FX_HashCode_GetW(wsInstanceName.AsStringView()); + CXFA_Node* pPrevSibling = iDesired == 0 + ? GetXFANode() + : GetXFANode()->GetItemIfExists(iDesired - 1); + if (!pPrevSibling) { + // TODO(dsinclair): Better error? +- ThrowIndexOutOfBoundsException(); ++ ThrowIndexOutOfBoundsException(pIsolate); + return 0; + } + +@@ -102,15 +107,16 @@ int32_t CJX_InstanceManager::SetInstances(int32_t iDesired) { + pNotify->RunNodeInitialize(pNewInstance); + } + } +- GetDocument()->GetLayoutProcessor()->AddChangedContainer( +- ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form))); ++ GetDocument()->GetLayoutProcessor()->SetHasChangedContainer(); + return 0; + } + +-int32_t CJX_InstanceManager::MoveInstance(int32_t iTo, int32_t iFrom) { ++int32_t CJX_InstanceManager::MoveInstance(v8::Isolate* pIsolate, ++ int32_t iTo, ++ int32_t iFrom) { + int32_t iCount = GetXFANode()->GetCount(); + if (iFrom > iCount || iTo > iCount - 1) { +- ThrowIndexOutOfBoundsException(); ++ ThrowIndexOutOfBoundsException(pIsolate); + return 1; + } + if (iFrom < 0 || iTo < 0 || iFrom == iTo) +@@ -118,21 +124,20 @@ int32_t CJX_InstanceManager::MoveInstance(int32_t iTo, int32_t iFrom) { + + CXFA_Node* pMoveInstance = GetXFANode()->GetItemIfExists(iFrom); + if (!pMoveInstance) { +- ThrowIndexOutOfBoundsException(); ++ ThrowIndexOutOfBoundsException(pIsolate); + return 1; + } + + GetXFANode()->RemoveItem(pMoveInstance, false); + GetXFANode()->InsertItem(pMoveInstance, iTo, iCount - 1, true); +- GetDocument()->GetLayoutProcessor()->AddChangedContainer( +- ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form))); ++ GetDocument()->GetLayoutProcessor()->SetHasChangedContainer(); + return 0; + } + + CJS_Result CJX_InstanceManager::moveInstance( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { +- CXFA_Document* doc = static_cast(runtime)->GetDocument(); ++ CXFA_Document* doc = runtime->GetDocument(); + if (doc->GetFormType() != FormType::kXFAFull) + return CJS_Result::Failure(JSMessage::kNotSupportedError); + +@@ -141,29 +146,28 @@ CJS_Result CJX_InstanceManager::moveInstance( + + int32_t iFrom = runtime->ToInt32(params[0]); + int32_t iTo = runtime->ToInt32(params[1]); +- MoveInstance(iTo, iFrom); ++ MoveInstance(runtime->GetIsolate(), iTo, iFrom); + + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); + if (!pNotify) + return CJS_Result::Success(); + +- CXFA_Node* pToInstance = GetXFANode()->GetItemIfExists(iTo); +- if (pToInstance && pToInstance->GetElementType() == XFA_Element::Subform) ++ CXFA_Node* pXFA = GetXFANode(); ++ auto* pToInstance = CXFA_Subform::FromNode(pXFA->GetItemIfExists(iTo)); ++ if (pToInstance) + pNotify->RunSubformIndexChange(pToInstance); + +- CXFA_Node* pFromInstance = GetXFANode()->GetItemIfExists(iFrom); +- if (pFromInstance && +- pFromInstance->GetElementType() == XFA_Element::Subform) { ++ auto* pFromInstance = CXFA_Subform::FromNode(pXFA->GetItemIfExists(iFrom)); ++ if (pFromInstance) + pNotify->RunSubformIndexChange(pFromInstance); +- } + + return CJS_Result::Success(); + } + + CJS_Result CJX_InstanceManager::removeInstance( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { +- CXFA_Document* doc = static_cast(runtime)->GetDocument(); ++ CXFA_Document* doc = runtime->GetDocument(); + if (doc->GetFormType() != FormType::kXFAFull) + return CJS_Result::Failure(JSMessage::kNotSupportedError); + +@@ -178,7 +182,7 @@ CJS_Result CJX_InstanceManager::removeInstance( + CXFA_Occur* occur = GetXFANode()->GetOccurIfExists(); + int32_t iMin = occur ? occur->GetMin() : CXFA_Occur::kDefaultMin; + if (iCount - 1 < iMin) +- return CJS_Result::Failure(JSMessage::kTooManyOccurances); ++ return CJS_Result::Failure(JSMessage::kTooManyOccurrences); + + CXFA_Node* pRemoveInstance = GetXFANode()->GetItemIfExists(iIndex); + if (!pRemoveInstance) +@@ -188,37 +192,35 @@ CJS_Result CJX_InstanceManager::removeInstance( + + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); + if (pNotify) { ++ CXFA_Node* pXFA = GetXFANode(); + for (int32_t i = iIndex; i < iCount - 1; i++) { +- CXFA_Node* pSubformInstance = GetXFANode()->GetItemIfExists(i); +- if (pSubformInstance && +- pSubformInstance->GetElementType() == XFA_Element::Subform) { ++ auto* pSubformInstance = CXFA_Subform::FromNode(pXFA->GetItemIfExists(i)); ++ if (pSubformInstance) + pNotify->RunSubformIndexChange(pSubformInstance); +- } + } + } +- GetDocument()->GetLayoutProcessor()->AddChangedContainer( +- ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form))); ++ GetDocument()->GetLayoutProcessor()->SetHasChangedContainer(); + return CJS_Result::Success(); + } + + CJS_Result CJX_InstanceManager::setInstances( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { +- CXFA_Document* doc = static_cast(runtime)->GetDocument(); ++ CXFA_Document* doc = runtime->GetDocument(); + if (doc->GetFormType() != FormType::kXFAFull) + return CJS_Result::Failure(JSMessage::kNotSupportedError); + + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + +- SetInstances(runtime->ToInt32(params[0])); ++ SetInstances(runtime->GetIsolate(), runtime->ToInt32(params[0])); + return CJS_Result::Success(); + } + + CJS_Result CJX_InstanceManager::addInstance( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { +- CXFA_Document* doc = static_cast(runtime)->GetDocument(); ++ CXFA_Document* doc = runtime->GetDocument(); + if (doc->GetFormType() != FormType::kXFAFull) + return CJS_Result::Failure(JSMessage::kNotSupportedError); + +@@ -233,7 +235,7 @@ CJS_Result CJX_InstanceManager::addInstance( + CXFA_Occur* occur = GetXFANode()->GetOccurIfExists(); + int32_t iMax = occur ? occur->GetMax() : CXFA_Occur::kDefaultMax; + if (iMax >= 0 && iCount >= iMax) +- return CJS_Result::Failure(JSMessage::kTooManyOccurances); ++ return CJS_Result::Failure(JSMessage::kTooManyOccurrences); + + CXFA_Node* pNewInstance = GetXFANode()->CreateInstanceIfPossible(fFlags); + if (!pNewInstance) +@@ -244,22 +246,18 @@ CJS_Result CJX_InstanceManager::addInstance( + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); + if (pNotify) { + pNotify->RunNodeInitialize(pNewInstance); +- GetDocument()->GetLayoutProcessor()->AddChangedContainer( +- ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form))); ++ GetDocument()->GetLayoutProcessor()->SetHasChangedContainer(); + } + +- CFXJSE_Value* value = +- GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap( +- pNewInstance); +- + return CJS_Result::Success( +- value->DirectGetValue().Get(runtime->GetIsolate())); ++ GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap( ++ pNewInstance)); + } + + CJS_Result CJX_InstanceManager::insertInstance( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { +- CXFA_Document* doc = static_cast(runtime)->GetDocument(); ++ CXFA_Document* doc = runtime->GetDocument(); + if (doc->GetFormType() != FormType::kXFAFull) + return CJS_Result::Failure(JSMessage::kNotSupportedError); + +@@ -289,46 +287,47 @@ CJS_Result CJX_InstanceManager::insertInstance( + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); + if (pNotify) { + pNotify->RunNodeInitialize(pNewInstance); +- GetDocument()->GetLayoutProcessor()->AddChangedContainer( +- ToNode(GetDocument()->GetXFAObject(XFA_HASHCODE_Form))); ++ GetDocument()->GetLayoutProcessor()->SetHasChangedContainer(); + } + +- CFXJSE_Value* value = +- GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap( +- pNewInstance); +- + return CJS_Result::Success( +- value->DirectGetValue().Get(runtime->GetIsolate())); ++ GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap( ++ pNewInstance)); + } + +-void CJX_InstanceManager::max(CFXJSE_Value* pValue, ++void CJX_InstanceManager::max(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } + CXFA_Occur* occur = GetXFANode()->GetOccurIfExists(); +- pValue->SetInteger(occur ? occur->GetMax() : CXFA_Occur::kDefaultMax); ++ *pValue = fxv8::NewNumberHelper( ++ pIsolate, occur ? occur->GetMax() : CXFA_Occur::kDefaultMax); + } + +-void CJX_InstanceManager::min(CFXJSE_Value* pValue, ++void CJX_InstanceManager::min(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } + CXFA_Occur* occur = GetXFANode()->GetOccurIfExists(); +- pValue->SetInteger(occur ? occur->GetMin() : CXFA_Occur::kDefaultMin); ++ *pValue = fxv8::NewNumberHelper( ++ pIsolate, occur ? occur->GetMin() : CXFA_Occur::kDefaultMin); + } + +-void CJX_InstanceManager::count(CFXJSE_Value* pValue, ++void CJX_InstanceManager::count(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- SetInstances(pValue->ToInteger()); ++ SetInstances(pIsolate, fxv8::ReentrantToInt32Helper(pIsolate, *pValue)); + return; + } +- pValue->SetInteger(GetXFANode()->GetCount()); ++ *pValue = fxv8::NewNumberHelper(pIsolate, GetXFANode()->GetCount()); + } +diff --git a/fxjs/xfa/cjx_instancemanager.h b/fxjs/xfa/cjx_instancemanager.h +index 3fae9ab8b..96210e8b2 100644 +--- a/fxjs/xfa/cjx_instancemanager.h ++++ b/fxjs/xfa/cjx_instancemanager.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,12 +9,13 @@ + + #include "fxjs/xfa/cjx_node.h" + #include "fxjs/xfa/jse_define.h" ++#include "v8/include/v8-forward.h" + + class CXFA_InstanceManager; + + class CJX_InstanceManager final : public CJX_Node { + public: +- explicit CJX_InstanceManager(CXFA_InstanceManager* mgr); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_InstanceManager() override; + + // CJX_Object: +@@ -30,16 +31,18 @@ class CJX_InstanceManager final : public CJX_Node { + JSE_PROP(max); + JSE_PROP(min); + +- int32_t MoveInstance(int32_t iTo, int32_t iFrom); ++ int32_t MoveInstance(v8::Isolate* pIsolate, int32_t iTo, int32_t iFrom); + + private: ++ explicit CJX_InstanceManager(CXFA_InstanceManager* mgr); ++ + using Type__ = CJX_InstanceManager; + using ParentType__ = CJX_Node; + + static const TypeTag static_type__ = TypeTag::InstanceManager; + static const CJX_MethodSpec MethodSpecs[]; + +- int32_t SetInstances(int32_t iDesired); ++ int32_t SetInstances(v8::Isolate* pIsolate, int32_t iDesired); + }; + + #endif // FXJS_XFA_CJX_INSTANCEMANAGER_H_ +diff --git a/fxjs/xfa/cjx_layoutpseudomodel.cpp b/fxjs/xfa/cjx_layoutpseudomodel.cpp +index 35852a856..484d883e9 100644 +--- a/fxjs/xfa/cjx_layoutpseudomodel.cpp ++++ b/fxjs/xfa/cjx_layoutpseudomodel.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,12 +10,13 @@ + #include + + #include "core/fxcrt/fx_coordinates.h" ++#include "fxjs/fxv8.h" + #include "fxjs/js_resources.h" + #include "fxjs/xfa/cfxjse_class.h" + #include "fxjs/xfa/cfxjse_engine.h" +-#include "fxjs/xfa/cfxjse_value.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/containers/contains.h" ++#include "v8/include/cppgc/allocation.h" ++#include "v8/include/v8-object.h" + #include "xfa/fxfa/cxfa_ffnotify.h" + #include "xfa/fxfa/layout/cxfa_contentlayoutitem.h" + #include "xfa/fxfa/layout/cxfa_layoutitem.h" +@@ -56,36 +57,39 @@ CJX_LayoutPseudoModel::CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model) + DefineMethods(MethodSpecs); + } + +-CJX_LayoutPseudoModel::~CJX_LayoutPseudoModel() {} ++CJX_LayoutPseudoModel::~CJX_LayoutPseudoModel() = default; + + bool CJX_LayoutPseudoModel::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-void CJX_LayoutPseudoModel::ready(CFXJSE_Value* pValue, ++void CJX_LayoutPseudoModel::ready(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); + if (!pNotify) + return; + if (bSetting) { +- ThrowException(WideString::FromASCII("Unable to set ready value.")); ++ ThrowException(pIsolate, ++ WideString::FromASCII("Unable to set ready value.")); + return; + } + +- int32_t iStatus = pNotify->GetLayoutStatus(); +- pValue->SetBoolean(iStatus >= 2); ++ CXFA_FFDocView::LayoutStatus iStatus = pNotify->GetLayoutStatus(); ++ const bool bReady = iStatus != CXFA_FFDocView::LayoutStatus::kNone && ++ iStatus != CXFA_FFDocView::LayoutStatus::kStart; ++ *pValue = fxv8::NewBooleanHelper(pIsolate, bReady); + } + +-CJS_Result CJX_LayoutPseudoModel::HWXY( +- CFX_V8* runtime, ++CJS_Result CJX_LayoutPseudoModel::DoHWXYInternal( ++ CFXJSE_Engine* runtime, + const std::vector>& params, +- XFA_LAYOUTMODEL_HWXY layoutModel) { ++ HWXY layoutModel) { + if (params.empty() || params.size() > 3) + return CJS_Result::Failure(JSMessage::kParamError); + +- CXFA_Node* pNode = +- ToNode(static_cast(runtime)->ToXFAObject(params[0])); ++ CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0])); + if (!pNode) + return CJS_Result::Success(); + +@@ -111,18 +115,18 @@ CJS_Result CJX_LayoutPseudoModel::HWXY( + return CJS_Result::Success(runtime->NewNumber(0.0)); + + CXFA_Measurement measure; +- CFX_RectF rtRect = pLayoutItem->GetRect(true); ++ CFX_RectF rtRect = pLayoutItem->GetRelativeRect(); + switch (layoutModel) { +- case XFA_LAYOUTMODEL_H: ++ case HWXY::kH: + measure.Set(rtRect.height, XFA_Unit::Pt); + break; +- case XFA_LAYOUTMODEL_W: ++ case HWXY::kW: + measure.Set(rtRect.width, XFA_Unit::Pt); + break; +- case XFA_LAYOUTMODEL_X: ++ case HWXY::kX: + measure.Set(rtRect.left, XFA_Unit::Pt); + break; +- case XFA_LAYOUTMODEL_Y: ++ case HWXY::kY: + measure.Set(rtRect.top, XFA_Unit::Pt); + break; + } +@@ -137,64 +141,63 @@ CJS_Result CJX_LayoutPseudoModel::HWXY( + } + + CJS_Result CJX_LayoutPseudoModel::h( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { +- return HWXY(runtime, params, XFA_LAYOUTMODEL_H); ++ return DoHWXYInternal(runtime, params, HWXY::kH); + } + + CJS_Result CJX_LayoutPseudoModel::w( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { +- return HWXY(runtime, params, XFA_LAYOUTMODEL_W); ++ return DoHWXYInternal(runtime, params, HWXY::kW); + } + + CJS_Result CJX_LayoutPseudoModel::x( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { +- return HWXY(runtime, params, XFA_LAYOUTMODEL_X); ++ return DoHWXYInternal(runtime, params, HWXY::kX); + } + + CJS_Result CJX_LayoutPseudoModel::y( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { +- return HWXY(runtime, params, XFA_LAYOUTMODEL_Y); ++ return DoHWXYInternal(runtime, params, HWXY::kY); + } + +-CJS_Result CJX_LayoutPseudoModel::NumberedPageCount(CFX_V8* runtime, +- bool bNumbered) { ++CJS_Result CJX_LayoutPseudoModel::AllPageCount(CFXJSE_Engine* runtime) { ++ auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument()); ++ return CJS_Result::Success(runtime->NewNumber(pDocLayout->CountPages())); ++} ++ ++CJS_Result CJX_LayoutPseudoModel::NumberedPageCount(CFXJSE_Engine* runtime) { + auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument()); + int32_t iPageCount = 0; + int32_t iPageNum = pDocLayout->CountPages(); +- if (bNumbered) { +- for (int32_t i = 0; i < iPageNum; i++) { +- CXFA_ViewLayoutItem* pLayoutPage = pDocLayout->GetPage(i); +- if (!pLayoutPage) +- continue; +- +- CXFA_Node* pMasterPage = pLayoutPage->GetMasterPage(); +- if (pMasterPage->JSObject()->GetInteger(XFA_Attribute::Numbered)) +- iPageCount++; +- } +- } else { +- iPageCount = iPageNum; ++ for (int32_t i = 0; i < iPageNum; i++) { ++ CXFA_ViewLayoutItem* pLayoutPage = pDocLayout->GetPage(i); ++ if (!pLayoutPage) ++ continue; ++ ++ CXFA_Node* pMasterPage = pLayoutPage->GetMasterPage(); ++ if (pMasterPage->JSObject()->GetInteger(XFA_Attribute::Numbered)) ++ iPageCount++; + } + return CJS_Result::Success(runtime->NewNumber(iPageCount)); + } + + CJS_Result CJX_LayoutPseudoModel::pageCount( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { +- return NumberedPageCount(runtime, true); ++ return NumberedPageCount(runtime); + } + + CJS_Result CJX_LayoutPseudoModel::pageSpan( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + +- CXFA_Node* pNode = +- ToNode(static_cast(runtime)->ToXFAObject(params[0])); ++ CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0])); + if (!pNode) + return CJS_Result::Success(); + +@@ -211,7 +214,7 @@ CJS_Result CJX_LayoutPseudoModel::pageSpan( + } + + CJS_Result CJX_LayoutPseudoModel::page( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return PageInternals(runtime, params, false); + } +@@ -261,7 +264,7 @@ std::vector CJX_LayoutPseudoModel::GetObjArray( + eType != XFA_Element::Subform && eType != XFA_Element::Area) { + continue; + } +- if (pdfium::ContainsValue(formItems, pItemChild->GetFormNode())) ++ if (pdfium::Contains(formItems, pItemChild->GetFormNode())) + continue; + + formItems.insert(pItemChild->GetFormNode()); +@@ -282,7 +285,7 @@ std::vector CJX_LayoutPseudoModel::GetObjArray( + eType != XFA_Element::Subform && eType != XFA_Element::Area) { + continue; + } +- if (pdfium::ContainsValue(formItems, pItemChild->GetFormNode())) ++ if (pdfium::Contains(formItems, pItemChild->GetFormNode())) + continue; + + formItems.insert(pItemChild->GetFormNode()); +@@ -317,7 +320,7 @@ std::vector CJX_LayoutPseudoModel::GetObjArray( + continue; + if (pItemChild->GetFormNode()->GetElementType() != eType) + continue; +- if (pdfium::ContainsValue(formItems, pItemChild->GetFormNode())) ++ if (pdfium::Contains(formItems, pItemChild->GetFormNode())) + continue; + + formItems.insert(pItemChild->GetFormNode()); +@@ -334,7 +337,7 @@ std::vector CJX_LayoutPseudoModel::GetObjArray( + continue; + if (pItemChild->GetFormNode()->GetElementType() != eType) + continue; +- if (pdfium::ContainsValue(formItems, pItemChild->GetFormNode())) ++ if (pdfium::Contains(formItems, pItemChild->GetFormNode())) + continue; + + formItems.insert(pItemChild->GetFormNode()); +@@ -348,7 +351,7 @@ std::vector CJX_LayoutPseudoModel::GetObjArray( + } + + CJS_Result CJX_LayoutPseudoModel::pageContent( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.empty() || params.size() > 3) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -369,59 +372,57 @@ CJS_Result CJX_LayoutPseudoModel::pageContent( + if (!pNotify) + return CJS_Result::Success(); + +- auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument()); +- auto pArrayNodeList = pdfium::MakeUnique(GetDocument()); ++ CXFA_Document* pDoc = GetDocument(); ++ auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(pDoc); ++ auto* pArrayNodeList = cppgc::MakeGarbageCollected( ++ pDoc->GetHeap()->GetAllocationHandle(), pDoc); ++ pDoc->GetNodeOwner()->PersistList(pArrayNodeList); + pArrayNodeList->SetArrayNodeList( + GetObjArray(pDocLayout, iIndex, wsType, bOnPageArea)); +- +- // TODO(dsinclair): Who owns the array once we release it? Won't this leak? +- return CJS_Result::Success(static_cast(runtime)->NewXFAObject( +- pArrayNodeList.release(), +- GetDocument()->GetScriptContext()->GetJseNormalClass()->GetTemplate())); ++ return CJS_Result::Success(runtime->NewNormalXFAObject(pArrayNodeList)); + } + + CJS_Result CJX_LayoutPseudoModel::absPageCount( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { +- return NumberedPageCount(runtime, false); ++ return AllPageCount(runtime); + } + + CJS_Result CJX_LayoutPseudoModel::absPageCountInBatch( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return CJS_Result::Success(runtime->NewNumber(0)); + } + + CJS_Result CJX_LayoutPseudoModel::sheetCountInBatch( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return CJS_Result::Success(runtime->NewNumber(0)); + } + + CJS_Result CJX_LayoutPseudoModel::relayout( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + CXFA_Node* pRootNode = GetDocument()->GetRoot(); + auto* pLayoutProcessor = GetDocument()->GetLayoutProcessor(); + CXFA_Form* pFormRoot = + pRootNode->GetFirstChildByClass(XFA_Element::Form); + if (pFormRoot) { +- CXFA_Node* pContentRootNode = pFormRoot->GetFirstChild(); +- if (pContentRootNode) +- pLayoutProcessor->AddChangedContainer(pContentRootNode); ++ if (pFormRoot->GetFirstChild()) ++ pLayoutProcessor->SetHasChangedContainer(); + } +- pLayoutProcessor->SetForceRelayout(true); ++ pLayoutProcessor->SetForceRelayout(); + return CJS_Result::Success(); + } + + CJS_Result CJX_LayoutPseudoModel::absPageSpan( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return pageSpan(runtime, params); + } + + CJS_Result CJX_LayoutPseudoModel::absPageInBatch( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -430,7 +431,7 @@ CJS_Result CJX_LayoutPseudoModel::absPageInBatch( + } + + CJS_Result CJX_LayoutPseudoModel::sheetInBatch( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -439,38 +440,37 @@ CJS_Result CJX_LayoutPseudoModel::sheetInBatch( + } + + CJS_Result CJX_LayoutPseudoModel::sheet( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return PageInternals(runtime, params, true); + } + + CJS_Result CJX_LayoutPseudoModel::relayoutPageArea( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return CJS_Result::Success(); + } + + CJS_Result CJX_LayoutPseudoModel::sheetCount( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { +- return NumberedPageCount(runtime, false); ++ return AllPageCount(runtime); + } + + CJS_Result CJX_LayoutPseudoModel::absPage( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return PageInternals(runtime, params, true); + } + + CJS_Result CJX_LayoutPseudoModel::PageInternals( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params, + bool bAbsPage) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + +- CXFA_Node* pNode = +- ToNode(static_cast(runtime)->ToXFAObject(params[0])); ++ CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0])); + if (!pNode) + return CJS_Result::Success(runtime->NewNumber(0)); + +diff --git a/fxjs/xfa/cjx_layoutpseudomodel.h b/fxjs/xfa/cjx_layoutpseudomodel.h +index d5f0cbada..80cf310e6 100644 +--- a/fxjs/xfa/cjx_layoutpseudomodel.h ++++ b/fxjs/xfa/cjx_layoutpseudomodel.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -12,21 +12,13 @@ + #include "fxjs/xfa/cjx_object.h" + #include "fxjs/xfa/jse_define.h" + +-enum XFA_LAYOUTMODEL_HWXY { +- XFA_LAYOUTMODEL_H, +- XFA_LAYOUTMODEL_W, +- XFA_LAYOUTMODEL_X, +- XFA_LAYOUTMODEL_Y +-}; +- +-class CFXJSE_Value; + class CScript_LayoutPseudoModel; + class CXFA_LayoutProcessor; + class CXFA_Node; + + class CJX_LayoutPseudoModel final : public CJX_Object { + public: +- explicit CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_LayoutPseudoModel() override; + + // CJX_Object: +@@ -55,21 +47,26 @@ class CJX_LayoutPseudoModel final : public CJX_Object { + JSE_PROP(ready); + + private: ++ enum class HWXY { kH, kW, kX, kY }; ++ ++ explicit CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model); ++ + using Type__ = CJX_LayoutPseudoModel; + using ParentType__ = CJX_Object; + + static const TypeTag static_type__ = TypeTag::LayoutPseudoModel; + static const CJX_MethodSpec MethodSpecs[]; + +- CJS_Result NumberedPageCount(CFX_V8* runtime, bool bNumbered); +- CJS_Result HWXY(CFX_V8* runtime, +- const std::vector>& params, +- XFA_LAYOUTMODEL_HWXY layoutModel); ++ CJS_Result AllPageCount(CFXJSE_Engine* runtime); ++ CJS_Result NumberedPageCount(CFXJSE_Engine* runtime); ++ CJS_Result DoHWXYInternal(CFXJSE_Engine* runtime, ++ const std::vector>& params, ++ HWXY layoutModel); + std::vector GetObjArray(CXFA_LayoutProcessor* pDocLayout, + int32_t iPageNo, + const WideString& wsType, + bool bOnPageArea); +- CJS_Result PageInternals(CFX_V8* runtime, ++ CJS_Result PageInternals(CFXJSE_Engine* runtime, + const std::vector>& params, + bool bAbsPage); + }; +diff --git a/fxjs/xfa/cjx_list.cpp b/fxjs/xfa/cjx_list.cpp +index 7c0cf23f5..8c4735dbf 100644 +--- a/fxjs/xfa/cjx_list.cpp ++++ b/fxjs/xfa/cjx_list.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,11 +8,13 @@ + + #include + ++#include "fxjs/fxv8.h" + #include "fxjs/js_resources.h" + #include "fxjs/xfa/cfxjse_class.h" + #include "fxjs/xfa/cfxjse_engine.h" +-#include "fxjs/xfa/cfxjse_value.h" + #include "third_party/base/numerics/safe_conversions.h" ++#include "v8/include/v8-object.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fxfa/parser/cxfa_document.h" + #include "xfa/fxfa/parser/cxfa_list.h" + #include "xfa/fxfa/parser/cxfa_node.h" +@@ -26,7 +28,7 @@ CJX_List::CJX_List(CXFA_List* list) : CJX_Object(list) { + DefineMethods(MethodSpecs); + } + +-CJX_List::~CJX_List() {} ++CJX_List::~CJX_List() = default; + + bool CJX_List::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); +@@ -36,45 +38,43 @@ CXFA_List* CJX_List::GetXFAList() { + return ToList(GetXFAObject()); + } + +-CJS_Result CJX_List::append(CFX_V8* runtime, ++CJS_Result CJX_List::append(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + +- auto* pNode = +- ToNode(static_cast(runtime)->ToXFAObject(params[0])); ++ auto* pNode = ToNode(runtime->ToXFAObject(params[0])); + if (!pNode) + return CJS_Result::Failure(JSMessage::kValueError); + +- GetXFAList()->Append(pNode); ++ if (!GetXFAList()->Append(pNode)) ++ return CJS_Result::Failure(JSMessage::kWouldBeCyclic); ++ + return CJS_Result::Success(); + } + +-CJS_Result CJX_List::insert(CFX_V8* runtime, ++CJS_Result CJX_List::insert(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 2) + return CJS_Result::Failure(JSMessage::kParamError); + +- auto* pNewNode = +- ToNode(static_cast(runtime)->ToXFAObject(params[0])); ++ auto* pNewNode = ToNode(runtime->ToXFAObject(params[0])); + if (!pNewNode) + return CJS_Result::Failure(JSMessage::kValueError); + +- auto* pBeforeNode = +- ToNode(static_cast(runtime)->ToXFAObject(params[1])); ++ auto* pBeforeNode = ToNode(runtime->ToXFAObject(params[1])); + if (!GetXFAList()->Insert(pNewNode, pBeforeNode)) + return CJS_Result::Failure(JSMessage::kValueError); + + return CJS_Result::Success(); + } + +-CJS_Result CJX_List::remove(CFX_V8* runtime, ++CJS_Result CJX_List::remove(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + +- auto* pNode = +- ToNode(static_cast(runtime)->ToXFAObject(params[0])); ++ auto* pNode = ToNode(runtime->ToXFAObject(params[0])); + if (!pNode) + return CJS_Result::Failure(JSMessage::kValueError); + +@@ -82,7 +82,7 @@ CJS_Result CJX_List::remove(CFX_V8* runtime, + return CJS_Result::Success(); + } + +-CJS_Result CJX_List::item(CFX_V8* runtime, ++CJS_Result CJX_List::item(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -92,18 +92,18 @@ CJS_Result CJX_List::item(CFX_V8* runtime, + if (index < 0 || cast_index >= GetXFAList()->GetLength()) + return CJS_Result::Failure(JSMessage::kInvalidInputError); + +- return CJS_Result::Success(static_cast(runtime)->NewXFAObject( +- GetXFAList()->Item(cast_index), +- GetDocument()->GetScriptContext()->GetJseNormalClass()->GetTemplate())); ++ return CJS_Result::Success( ++ runtime->NewNormalXFAObject(GetXFAList()->Item(cast_index))); + } + +-void CJX_List::length(CFXJSE_Value* pValue, ++void CJX_List::length(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } +- pValue->SetInteger( +- pdfium::base::checked_cast(GetXFAList()->GetLength())); ++ *pValue = fxv8::NewNumberHelper( ++ pIsolate, pdfium::base::checked_cast(GetXFAList()->GetLength())); + } +diff --git a/fxjs/xfa/cjx_list.h b/fxjs/xfa/cjx_list.h +index 842b85948..4471867c6 100644 +--- a/fxjs/xfa/cjx_list.h ++++ b/fxjs/xfa/cjx_list.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_List; + + class CJX_List : public CJX_Object { + public: +- explicit CJX_List(CXFA_List* list); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_List() override; + + // CJX_Object: +@@ -27,6 +27,9 @@ class CJX_List : public CJX_Object { + + JSE_PROP(length); + ++ protected: ++ explicit CJX_List(CXFA_List* list); ++ + private: + using Type__ = CJX_List; + using ParentType__ = CJX_Object; +diff --git a/fxjs/xfa/cjx_list_embeddertest.cpp b/fxjs/xfa/cjx_list_embeddertest.cpp +index 02450dc79..17d2de434 100644 +--- a/fxjs/xfa/cjx_list_embeddertest.cpp ++++ b/fxjs/xfa/cjx_list_embeddertest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/fxjs/xfa/cjx_logpseudomodel.cpp b/fxjs/xfa/cjx_logpseudomodel.cpp +index 8cb9ee7aa..a100697d1 100644 +--- a/fxjs/xfa/cjx_logpseudomodel.cpp ++++ b/fxjs/xfa/cjx_logpseudomodel.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -23,14 +23,14 @@ CJX_LogPseudoModel::CJX_LogPseudoModel(CScript_LogPseudoModel* model) + DefineMethods(MethodSpecs); + } + +-CJX_LogPseudoModel::~CJX_LogPseudoModel() {} ++CJX_LogPseudoModel::~CJX_LogPseudoModel() = default; + + bool CJX_LogPseudoModel::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + + CJS_Result CJX_LogPseudoModel::message( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + // Uncomment to allow using xfa.log.message(""); from JS. + // fprintf(stderr, "LOG\n"); +@@ -43,25 +43,25 @@ CJS_Result CJX_LogPseudoModel::message( + } + + CJS_Result CJX_LogPseudoModel::traceEnabled( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return CJS_Result::Success(); + } + + CJS_Result CJX_LogPseudoModel::traceActivate( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return CJS_Result::Success(); + } + + CJS_Result CJX_LogPseudoModel::traceDeactivate( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return CJS_Result::Success(); + } + + CJS_Result CJX_LogPseudoModel::trace( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return CJS_Result::Success(); + } +diff --git a/fxjs/xfa/cjx_logpseudomodel.h b/fxjs/xfa/cjx_logpseudomodel.h +index fda3bb912..030d1df8e 100644 +--- a/fxjs/xfa/cjx_logpseudomodel.h ++++ b/fxjs/xfa/cjx_logpseudomodel.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,7 +16,7 @@ class CScript_LogPseudoModel; + // xfa_basic_data_element_script is removed. + class CJX_LogPseudoModel final : public CJX_Object { + public: +- explicit CJX_LogPseudoModel(CScript_LogPseudoModel* model); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_LogPseudoModel() override; + + // CJX_Object: +@@ -29,6 +29,8 @@ class CJX_LogPseudoModel final : public CJX_Object { + JSE_METHOD(trace); + + private: ++ explicit CJX_LogPseudoModel(CScript_LogPseudoModel* model); ++ + using Type__ = CJX_LogPseudoModel; + using ParentType__ = CJX_Object; + +diff --git a/fxjs/xfa/cjx_manifest.cpp b/fxjs/xfa/cjx_manifest.cpp +index f94232edf..7d4e6e653 100644 +--- a/fxjs/xfa/cjx_manifest.cpp ++++ b/fxjs/xfa/cjx_manifest.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,6 +11,7 @@ + #include "fxjs/cfx_v8.h" + #include "fxjs/js_resources.h" + #include "fxjs/xfa/cfxjse_value.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fxfa/parser/cxfa_manifest.h" + + const CJX_MethodSpec CJX_Manifest::MethodSpecs[] = { +@@ -20,14 +21,14 @@ CJX_Manifest::CJX_Manifest(CXFA_Manifest* manifest) : CJX_Node(manifest) { + DefineMethods(MethodSpecs); + } + +-CJX_Manifest::~CJX_Manifest() {} ++CJX_Manifest::~CJX_Manifest() = default; + + bool CJX_Manifest::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + + CJS_Result CJX_Manifest::evaluate( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +diff --git a/fxjs/xfa/cjx_manifest.h b/fxjs/xfa/cjx_manifest.h +index 8380ac675..0f956489c 100644 +--- a/fxjs/xfa/cjx_manifest.h ++++ b/fxjs/xfa/cjx_manifest.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Manifest; + + class CJX_Manifest final : public CJX_Node { + public: +- explicit CJX_Manifest(CXFA_Manifest* manifest); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Manifest() override; + + // CJX_Object: +@@ -23,6 +23,8 @@ class CJX_Manifest final : public CJX_Node { + JSE_METHOD(evaluate); + + private: ++ explicit CJX_Manifest(CXFA_Manifest* manifest); ++ + using Type__ = CJX_Manifest; + using ParentType__ = CJX_Node; + +diff --git a/fxjs/xfa/cjx_model.cpp b/fxjs/xfa/cjx_model.cpp +index 4fa0a2e28..21a767d75 100644 +--- a/fxjs/xfa/cjx_model.cpp ++++ b/fxjs/xfa/cjx_model.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,6 +11,7 @@ + #include "fxjs/js_resources.h" + #include "fxjs/xfa/cfxjse_engine.h" + #include "fxjs/xfa/cfxjse_value.h" ++#include "v8/include/v8-object.h" + #include "xfa/fxfa/parser/cxfa_delta.h" + #include "xfa/fxfa/parser/cxfa_document.h" + #include "xfa/fxfa/parser/xfa_basic_data.h" +@@ -24,20 +25,20 @@ CJX_Model::CJX_Model(CXFA_Node* node) : CJX_Node(node) { + DefineMethods(MethodSpecs); + } + +-CJX_Model::~CJX_Model() {} ++CJX_Model::~CJX_Model() = default; + + bool CJX_Model::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + + CJS_Result CJX_Model::clearErrorList( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + return CJS_Result::Success(); + } + + CJS_Result CJX_Model::createNode( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.empty() || params.size() > 3) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -60,21 +61,19 @@ CJS_Result CJX_Model::createNode( + if (!pNewNode->HasAttribute(XFA_Attribute::Name)) + return CJS_Result::Failure(JSMessage::kParamError); + +- pNewNode->JSObject()->SetAttribute(XFA_Attribute::Name, name.AsStringView(), +- true); ++ pNewNode->JSObject()->SetAttributeByEnum(XFA_Attribute::Name, name, true); + if (pNewNode->GetPacketType() == XFA_PacketType::Datasets) + pNewNode->CreateXMLMappingNode(); + } + +- CFXJSE_Value* value = ++ v8::Local value = + GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNewNode); + +- return CJS_Result::Success( +- value->DirectGetValue().Get(runtime->GetIsolate())); ++ return CJS_Result::Success(value); + } + + CJS_Result CJX_Model::isCompatibleNS( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -87,10 +86,12 @@ CJS_Result CJX_Model::isCompatibleNS( + runtime->NewBoolean(TryNamespace().value_or(WideString()) == nameSpace)); + } + +-void CJX_Model::context(CFXJSE_Value* pValue, ++void CJX_Model::context(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) {} + +-void CJX_Model::aliasNode(CFXJSE_Value* pValue, ++void CJX_Model::aliasNode(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) {} +diff --git a/fxjs/xfa/cjx_model.h b/fxjs/xfa/cjx_model.h +index 9fd54f51f..7b0e17eea 100644 +--- a/fxjs/xfa/cjx_model.h ++++ b/fxjs/xfa/cjx_model.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Node; + + class CJX_Model : public CJX_Node { + public: +- explicit CJX_Model(CXFA_Node* obj); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Model() override; + + // CJX_Object: +@@ -27,6 +27,9 @@ class CJX_Model : public CJX_Node { + JSE_PROP(aliasNode); + JSE_PROP(context); + ++ protected: ++ explicit CJX_Model(CXFA_Node* obj); ++ + private: + using Type__ = CJX_Model; + using ParentType__ = CJX_Node; +diff --git a/fxjs/xfa/cjx_node.cpp b/fxjs/xfa/cjx_node.cpp +index 3877f72a9..0685107a2 100644 +--- a/fxjs/xfa/cjx_node.cpp ++++ b/fxjs/xfa/cjx_node.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,25 +11,27 @@ + #include + + #include "core/fxcrt/cfx_memorystream.h" ++#include "core/fxcrt/cfx_read_only_string_stream.h" + #include "core/fxcrt/fx_codepage.h" + #include "core/fxcrt/xml/cfx_xmldocument.h" +-#include "core/fxcrt/xml/cfx_xmlnode.h" ++#include "core/fxcrt/xml/cfx_xmlelement.h" ++#include "core/fxcrt/xml/cfx_xmlparser.h" ++#include "fxjs/fxv8.h" + #include "fxjs/js_resources.h" + #include "fxjs/xfa/cfxjse_engine.h" +-#include "fxjs/xfa/cfxjse_value.h" +-#include "third_party/base/ptr_util.h" ++#include "v8/include/v8-object.h" + #include "xfa/fxfa/cxfa_eventparam.h" + #include "xfa/fxfa/cxfa_ffdoc.h" + #include "xfa/fxfa/cxfa_ffnotify.h" + #include "xfa/fxfa/parser/cxfa_document.h" +-#include "xfa/fxfa/parser/cxfa_document_parser.h" ++#include "xfa/fxfa/parser/cxfa_document_builder.h" + #include "xfa/fxfa/parser/cxfa_node.h" + #include "xfa/fxfa/parser/xfa_basic_data.h" + #include "xfa/fxfa/parser/xfa_utils.h" + + namespace { + +-enum class EventAppliesToo : uint8_t { ++enum class EventAppliesTo : uint8_t { + kNone = 0, + kAll = 1, + kAllNonRecursive = 2, +@@ -40,90 +42,47 @@ enum class EventAppliesToo : uint8_t { + kChoiceList = 7 + }; + +-struct XFA_ExecEventParaInfo { +- public: ++struct ExecEventParaInfo { + uint32_t m_uHash; // hashed as wide string. + XFA_EVENTTYPE m_eventType; +- EventAppliesToo m_validFlags; ++ EventAppliesTo m_validFlags; + }; + + #undef PARA +-#define PARA(a, b, c, d) a, c, d +-const XFA_ExecEventParaInfo gs_eventParaInfos[] = { +- {PARA(0x109d7ce7, +- "mouseEnter", +- XFA_EVENT_MouseEnter, +- EventAppliesToo::kField)}, +- {PARA(0x1bfc72d9, +- "preOpen", +- XFA_EVENT_PreOpen, +- EventAppliesToo::kChoiceList)}, +- {PARA(0x2196a452, +- "initialize", +- XFA_EVENT_Initialize, +- EventAppliesToo::kAll)}, +- {PARA(0x27410f03, +- "mouseExit", +- XFA_EVENT_MouseExit, +- EventAppliesToo::kField)}, +- {PARA(0x36f1c6d8, +- "preSign", +- XFA_EVENT_PreSign, +- EventAppliesToo::kSignature)}, +- {PARA(0x4731d6ba, +- "exit", +- XFA_EVENT_Exit, +- EventAppliesToo::kAllNonRecursive)}, +- {PARA(0x7233018a, "validate", XFA_EVENT_Validate, EventAppliesToo::kAll)}, +- {PARA(0x8808385e, +- "indexChange", +- XFA_EVENT_IndexChange, +- EventAppliesToo::kSubform)}, +- {PARA(0x891f4606, +- "change", +- XFA_EVENT_Change, +- EventAppliesToo::kFieldOrExclusion)}, +- {PARA(0x9f693b21, +- "mouseDown", +- XFA_EVENT_MouseDown, +- EventAppliesToo::kField)}, +- {PARA(0xcdce56b3, +- "full", +- XFA_EVENT_Full, +- EventAppliesToo::kFieldOrExclusion)}, +- {PARA(0xd576d08e, "mouseUp", XFA_EVENT_MouseUp, EventAppliesToo::kField)}, +- {PARA(0xd95657a6, +- "click", +- XFA_EVENT_Click, +- EventAppliesToo::kFieldOrExclusion)}, +- {PARA(0xdbfbe02e, "calculate", XFA_EVENT_Calculate, EventAppliesToo::kAll)}, +- {PARA(0xe25fa7b8, +- "postOpen", +- XFA_EVENT_PostOpen, +- EventAppliesToo::kChoiceList)}, +- {PARA(0xe28dce7e, +- "enter", +- XFA_EVENT_Enter, +- EventAppliesToo::kAllNonRecursive)}, +- {PARA(0xfd54fbb7, +- "postSign", +- XFA_EVENT_PostSign, +- EventAppliesToo::kSignature)}, ++#define PARA(a, b, c, d) a, c, EventAppliesTo::d ++const ExecEventParaInfo kExecEventParaInfoTable[] = { ++ {PARA(0x109d7ce7, "mouseEnter", XFA_EVENT_MouseEnter, kField)}, ++ {PARA(0x1bfc72d9, "preOpen", XFA_EVENT_PreOpen, kChoiceList)}, ++ {PARA(0x2196a452, "initialize", XFA_EVENT_Initialize, kAll)}, ++ {PARA(0x27410f03, "mouseExit", XFA_EVENT_MouseExit, kField)}, ++ {PARA(0x36f1c6d8, "preSign", XFA_EVENT_PreSign, kSignature)}, ++ {PARA(0x4731d6ba, "exit", XFA_EVENT_Exit, kAllNonRecursive)}, ++ {PARA(0x7233018a, "validate", XFA_EVENT_Validate, kAll)}, ++ {PARA(0x8808385e, "indexChange", XFA_EVENT_IndexChange, kSubform)}, ++ {PARA(0x891f4606, "change", XFA_EVENT_Change, kFieldOrExclusion)}, ++ {PARA(0x9f693b21, "mouseDown", XFA_EVENT_MouseDown, kField)}, ++ {PARA(0xcdce56b3, "full", XFA_EVENT_Full, kFieldOrExclusion)}, ++ {PARA(0xd576d08e, "mouseUp", XFA_EVENT_MouseUp, kField)}, ++ {PARA(0xd95657a6, "click", XFA_EVENT_Click, kFieldOrExclusion)}, ++ {PARA(0xdbfbe02e, "calculate", XFA_EVENT_Calculate, kAll)}, ++ {PARA(0xe25fa7b8, "postOpen", XFA_EVENT_PostOpen, kChoiceList)}, ++ {PARA(0xe28dce7e, "enter", XFA_EVENT_Enter, kAllNonRecursive)}, ++ {PARA(0xfd54fbb7, "postSign", XFA_EVENT_PostSign, kSignature)}, + }; + #undef PARA + +-const XFA_ExecEventParaInfo* GetEventParaInfoByName( ++const ExecEventParaInfo* GetExecEventParaInfoByName( + WideStringView wsEventName) { + if (wsEventName.IsEmpty()) + return nullptr; + +- uint32_t uHash = FX_HashCode_GetW(wsEventName, false); ++ uint32_t uHash = FX_HashCode_GetW(wsEventName); + auto* result = std::lower_bound( +- std::begin(gs_eventParaInfos), std::end(gs_eventParaInfos), uHash, +- [](const XFA_ExecEventParaInfo& iter, const uint16_t& hash) { ++ std::begin(kExecEventParaInfoTable), std::end(kExecEventParaInfoTable), ++ uHash, [](const ExecEventParaInfo& iter, const uint16_t& hash) { + return iter.m_uHash < hash; + }); +- if (result != std::end(gs_eventParaInfos) && result->m_uHash == uHash) ++ if (result != std::end(kExecEventParaInfoTable) && result->m_uHash == uHash) + return result; + return nullptr; + } +@@ -153,11 +112,7 @@ bool CJX_Node::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-CXFA_Node* CJX_Node::GetXFANode() const { +- return ToNode(GetXFAObject()); +-} +- +-CJS_Result CJX_Node::applyXSL(CFX_V8* runtime, ++CJS_Result CJX_Node::applyXSL(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -167,7 +122,7 @@ CJS_Result CJX_Node::applyXSL(CFX_V8* runtime, + } + + CJS_Result CJX_Node::assignNode( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.empty() || params.size() > 3) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -176,33 +131,30 @@ CJS_Result CJX_Node::assignNode( + return CJS_Result::Success(); + } + +-CJS_Result CJX_Node::clone(CFX_V8* runtime, ++CJS_Result CJX_Node::clone(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + + CXFA_Node* pCloneNode = GetXFANode()->Clone(runtime->ToBoolean(params[0])); +- CFXJSE_Value* value = +- GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap( +- pCloneNode); +- + return CJS_Result::Success( +- value->DirectGetValue().Get(runtime->GetIsolate())); ++ GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap( ++ pCloneNode)); + } + + CJS_Result CJX_Node::getAttribute( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + + WideString expression = runtime->ToWideString(params[0]); + return CJS_Result::Success(runtime->NewString( +- GetAttribute(expression.AsStringView()).ToUTF8().AsStringView())); ++ GetAttributeByString(expression.AsStringView()).ToUTF8().AsStringView())); + } + + CJS_Result CJX_Node::getElement( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.empty() || params.size() > 2) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -217,21 +169,18 @@ CJS_Result CJX_Node::getElement( + if (!pNode) + return CJS_Result::Success(runtime->NewNull()); + +- CFXJSE_Value* value = +- GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode); +- + return CJS_Result::Success( +- value->DirectGetValue().Get(runtime->GetIsolate())); ++ GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode)); + } + + CJS_Result CJX_Node::isPropertySpecified( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.empty() || params.size() > 3) + return CJS_Result::Failure(JSMessage::kParamError); + + WideString expression = runtime->ToWideString(params[0]); +- Optional attr = ++ absl::optional attr = + XFA_GetAttributeByName(expression.AsStringView()); + if (attr.has_value() && HasAttribute(attr.value().attribute)) + return CJS_Result::Success(runtime->NewBoolean(true)); +@@ -252,7 +201,7 @@ CJS_Result CJX_Node::isPropertySpecified( + return CJS_Result::Success(runtime->NewBoolean(bHas)); + } + +-CJS_Result CJX_Node::loadXML(CFX_V8* runtime, ++CJS_Result CJX_Node::loadXML(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.empty() || params.size() > 3) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -265,18 +214,23 @@ CJS_Result CJX_Node::loadXML(CFX_V8* runtime, + if (params.size() >= 2) + bIgnoreRoot = runtime->ToBoolean(params[1]); + +- bool bOverwrite = 0; ++ bool bOverwrite = false; + if (params.size() >= 3) + bOverwrite = runtime->ToBoolean(params[2]); + +- auto pParser = pdfium::MakeUnique(GetDocument()); +- CFX_XMLNode* pXMLNode = pParser->ParseXMLData(expression); ++ auto stream = ++ pdfium::MakeRetain(std::move(expression)); ++ ++ CFX_XMLParser parser(stream); ++ std::unique_ptr xml_doc = parser.Parse(); ++ CXFA_DocumentBuilder builder(GetDocument()); ++ CFX_XMLNode* pXMLNode = builder.Build(xml_doc.get()); + if (!pXMLNode) + return CJS_Result::Success(); + + CFX_XMLDocument* top_xml_doc = +- GetXFANode()->GetDocument()->GetNotify()->GetHDOC()->GetXMLDocument(); +- top_xml_doc->AppendNodesFrom(pParser->GetXMLDoc().get()); ++ GetXFANode()->GetDocument()->GetNotify()->GetFFDoc()->GetXMLDocument(); ++ top_xml_doc->AppendNodesFrom(xml_doc.get()); + + if (bIgnoreRoot && + (pXMLNode->GetType() != CFX_XMLNode::Type::kElement || +@@ -288,7 +242,7 @@ CJS_Result CJX_Node::loadXML(CFX_V8* runtime, + WideString wsContentType = GetCData(XFA_Attribute::ContentType); + if (!wsContentType.IsEmpty()) { + pFakeRoot->JSObject()->SetCData(XFA_Attribute::ContentType, +- WideString(wsContentType), false, false); ++ WideString(wsContentType)); + } + + CFX_XMLNode* pFakeXMLRoot = pFakeRoot->GetXMLMappingNode(); +@@ -317,8 +271,8 @@ CJS_Result CJX_Node::loadXML(CFX_V8* runtime, + pFakeXMLRoot->AppendLastChild(pXMLNode); + } + +- pParser->ConstructXFANode(pFakeRoot, pFakeXMLRoot); +- pFakeRoot = pParser->GetRootNode(); ++ builder.ConstructXFANode(pFakeRoot, pFakeXMLRoot); ++ pFakeRoot = builder.GetRootNode(); + if (!pFakeRoot) + return CJS_Result::Success(); + +@@ -330,7 +284,7 @@ CJS_Result CJX_Node::loadXML(CFX_V8* runtime, + CXFA_Node* pItem = pNewChild->GetNextSibling(); + pFakeRoot->RemoveChildAndNotify(pNewChild, true); + GetXFANode()->InsertChildAndNotify(index++, pNewChild); +- pNewChild->SetFlagAndNotify(XFA_NodeFlag_Initialized); ++ pNewChild->SetInitializedFlagAndNotify(); + pNewChild = pItem; + } + +@@ -358,7 +312,7 @@ CJS_Result CJX_Node::loadXML(CFX_V8* runtime, + CXFA_Node* pItem = pChild->GetNextSibling(); + pFakeRoot->RemoveChildAndNotify(pChild, true); + GetXFANode()->InsertChildAndNotify(pChild, nullptr); +- pChild->SetFlagAndNotify(XFA_NodeFlag_Initialized); ++ pChild->SetInitializedFlagAndNotify(); + pChild = pItem; + } + } +@@ -366,19 +320,19 @@ CJS_Result CJX_Node::loadXML(CFX_V8* runtime, + if (pFakeXMLRoot) { + pFakeRoot->SetXMLMappingNode(std::move(pFakeXMLRoot)); + } +- pFakeRoot->SetFlag(XFA_NodeFlag_HasRemovedChildren); ++ pFakeRoot->SetFlag(XFA_NodeFlag::kHasRemovedChildren); + + return CJS_Result::Success(); + } + + CJS_Result CJX_Node::saveFilteredXML( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + // TODO(weili): Check whether we need to implement this, pdfium:501. + return CJS_Result::Success(); + } + +-CJS_Result CJX_Node::saveXML(CFX_V8* runtime, ++CJS_Result CJX_Node::saveXML(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() > 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -416,12 +370,12 @@ CJS_Result CJX_Node::saveXML(CFX_V8* runtime, + pElement->Save(pMemoryStream); + } + +- return CJS_Result::Success(runtime->NewString( +- ByteStringView(pMemoryStream->GetBuffer(), pMemoryStream->GetSize()))); ++ return CJS_Result::Success( ++ runtime->NewString(ByteStringView(pMemoryStream->GetSpan()))); + } + + CJS_Result CJX_Node::setAttribute( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 2) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -432,12 +386,12 @@ CJS_Result CJX_Node::setAttribute( + WideString attribute = runtime->ToWideString(params[1]); + + // Pass them to our method, however, in the more usual manner. +- SetAttribute(attribute.AsStringView(), attributeValue.AsStringView(), true); ++ SetAttributeByString(attribute.AsStringView(), attributeValue); + return CJS_Result::Success(); + } + + CJS_Result CJX_Node::setElement( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1 && params.size() != 2) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -446,66 +400,75 @@ CJS_Result CJX_Node::setElement( + return CJS_Result::Success(); + } + +-void CJX_Node::ns(CFXJSE_Value* pValue, ++void CJX_Node::ns(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } +- pValue->SetString( +- TryNamespace().value_or(WideString()).ToUTF8().AsStringView()); ++ *pValue = fxv8::NewStringHelper( ++ pIsolate, TryNamespace().value_or(WideString()).ToUTF8().AsStringView()); + } + +-void CJX_Node::model(CFXJSE_Value* pValue, ++void CJX_Node::model(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); ++ return; ++ } ++ CXFA_Node* pModel = GetXFANode()->GetModelNode(); ++ if (!pModel) { ++ *pValue = fxv8::NewNullHelper(pIsolate); + return; + } +- pValue->Assign(GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap( +- GetXFANode()->GetModelNode())); ++ *pValue = ++ GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pModel); + } + +-void CJX_Node::isContainer(CFXJSE_Value* pValue, ++void CJX_Node::isContainer(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } +- pValue->SetBoolean(GetXFANode()->IsContainerNode()); ++ *pValue = fxv8::NewBooleanHelper(pIsolate, GetXFANode()->IsContainerNode()); + } + +-void CJX_Node::isNull(CFXJSE_Value* pValue, ++void CJX_Node::isNull(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } + if (GetXFANode()->GetElementType() == XFA_Element::Subform) { +- pValue->SetBoolean(false); ++ *pValue = fxv8::NewBooleanHelper(pIsolate, false); + return; + } +- pValue->SetBoolean(GetContent(false).IsEmpty()); ++ *pValue = fxv8::NewBooleanHelper(pIsolate, GetContent(false).IsEmpty()); + } + +-void CJX_Node::oneOfChild(CFXJSE_Value* pValue, ++void CJX_Node::oneOfChild(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } + + std::vector properties = +- GetXFANode()->GetNodeListWithFilter(XFA_NODEFILTER_OneOfProperty); ++ GetXFANode()->GetNodeListWithFilter(XFA_NodeFilter::kOneOfProperty); + if (!properties.empty()) { +- pValue->Assign( +- GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap( +- properties.front())); ++ *pValue = GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap( ++ properties.front()); + } + } + +@@ -515,26 +478,26 @@ XFA_EventError CJX_Node::execSingleEventByName(WideStringView wsEventName, + if (!pNotify) + return XFA_EventError::kNotExist; + +- const XFA_ExecEventParaInfo* eventParaInfo = +- GetEventParaInfoByName(wsEventName); ++ const ExecEventParaInfo* eventParaInfo = ++ GetExecEventParaInfoByName(wsEventName); + if (!eventParaInfo) + return XFA_EventError::kNotExist; + + switch (eventParaInfo->m_validFlags) { +- case EventAppliesToo::kNone: ++ case EventAppliesTo::kNone: + return XFA_EventError::kNotExist; +- case EventAppliesToo::kAll: +- case EventAppliesToo::kAllNonRecursive: ++ case EventAppliesTo::kAll: ++ case EventAppliesTo::kAllNonRecursive: + return pNotify->ExecEventByDeepFirst( + GetXFANode(), eventParaInfo->m_eventType, false, +- eventParaInfo->m_validFlags == EventAppliesToo::kAll); +- case EventAppliesToo::kSubform: ++ eventParaInfo->m_validFlags == EventAppliesTo::kAll); ++ case EventAppliesTo::kSubform: + if (eType != XFA_Element::Subform) + return XFA_EventError::kNotExist; + + return pNotify->ExecEventByDeepFirst( + GetXFANode(), eventParaInfo->m_eventType, false, false); +- case EventAppliesToo::kFieldOrExclusion: { ++ case EventAppliesTo::kFieldOrExclusion: { + if (eType != XFA_Element::ExclGroup && eType != XFA_Element::Field) + return XFA_EventError::kNotExist; + +@@ -548,13 +511,13 @@ XFA_EventError CJX_Node::execSingleEventByName(WideStringView wsEventName, + return pNotify->ExecEventByDeepFirst( + GetXFANode(), eventParaInfo->m_eventType, false, false); + } +- case EventAppliesToo::kField: ++ case EventAppliesTo::kField: + if (eType != XFA_Element::Field) + return XFA_EventError::kNotExist; + + return pNotify->ExecEventByDeepFirst( + GetXFANode(), eventParaInfo->m_eventType, false, false); +- case EventAppliesToo::kSignature: { ++ case EventAppliesTo::kSignature: { + if (!GetXFANode()->IsWidgetReady()) + return XFA_EventError::kNotExist; + if (GetXFANode()->GetUIChildNode()->GetElementType() != +@@ -564,7 +527,7 @@ XFA_EventError CJX_Node::execSingleEventByName(WideStringView wsEventName, + return pNotify->ExecEventByDeepFirst( + GetXFANode(), eventParaInfo->m_eventType, false, false); + } +- case EventAppliesToo::kChoiceList: { ++ case EventAppliesTo::kChoiceList: { + if (!GetXFANode()->IsWidgetReady()) + return XFA_EventError::kNotExist; + if (GetXFANode()->GetUIChildNode()->GetElementType() != +diff --git a/fxjs/xfa/cjx_node.h b/fxjs/xfa/cjx_node.h +index 1cbceb627..bc8eb3fe6 100644 +--- a/fxjs/xfa/cjx_node.h ++++ b/fxjs/xfa/cjx_node.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,7 +16,7 @@ class CXFA_Node; + + class CJX_Node : public CJX_Tree { + public: +- explicit CJX_Node(CXFA_Node* node); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Node() override; + + // CJX_Object: +@@ -40,9 +40,9 @@ class CJX_Node : public CJX_Tree { + JSE_PROP(ns); + JSE_PROP(oneOfChild); + +- CXFA_Node* GetXFANode() const; +- + protected: ++ explicit CJX_Node(CXFA_Node* node); ++ + XFA_EventError execSingleEventByName(WideStringView wsEventName, + XFA_Element eType); + +diff --git a/fxjs/xfa/cjx_object.cpp b/fxjs/xfa/cjx_object.cpp +index 64006b35e..9d6cdf9bb 100644 +--- a/fxjs/xfa/cjx_object.cpp ++++ b/fxjs/xfa/cjx_object.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,20 +8,27 @@ + + #include + #include ++#include + + #include "core/fxcrt/fx_extension.h" ++#include "core/fxcrt/fx_memory.h" + #include "core/fxcrt/xml/cfx_xmlelement.h" + #include "core/fxcrt/xml/cfx_xmltext.h" + #include "fxjs/cjs_result.h" ++#include "fxjs/fxv8.h" ++#include "fxjs/gc/container_trace.h" + #include "fxjs/xfa/cfxjse_engine.h" +-#include "fxjs/xfa/cfxjse_value.h" ++#include "fxjs/xfa/cfxjse_mapmodule.h" + #include "fxjs/xfa/cjx_boolean.h" + #include "fxjs/xfa/cjx_draw.h" + #include "fxjs/xfa/cjx_field.h" + #include "fxjs/xfa/cjx_instancemanager.h" +-#include "third_party/base/compiler_specific.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" ++#include "third_party/base/check.h" ++#include "third_party/base/check_op.h" ++#include "third_party/base/containers/contains.h" ++#include "v8/include/v8-forward.h" ++#include "v8/include/v8-object.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fgas/crt/cfgas_decimal.h" + #include "xfa/fxfa/cxfa_ffnotify.h" + #include "xfa/fxfa/cxfa_ffwidget.h" +@@ -44,33 +51,19 @@ + + namespace { + +-void XFA_DeleteWideString(void* pData) { +- delete static_cast(pData); +-} +- +-void XFA_CopyWideString(void*& pData) { +- if (!pData) +- return; +- pData = new WideString(*reinterpret_cast(pData)); +-} +- +-const XFA_MAPDATABLOCKCALLBACKINFO deleteWideStringCallBack = { +- XFA_DeleteWideString, XFA_CopyWideString}; +- + enum XFA_KEYTYPE { + XFA_KEYTYPE_Custom, + XFA_KEYTYPE_Element, + }; + +-void* GetMapKey_Custom(WideStringView wsKey) { +- uint32_t dwKey = FX_HashCode_GetW(wsKey, false); +- return (void*)(uintptr_t)((dwKey << 1) | XFA_KEYTYPE_Custom); ++uint32_t GetMapKey_Custom(WideStringView wsKey) { ++ uint32_t dwKey = FX_HashCode_GetW(wsKey); ++ return ((dwKey << 1) | XFA_KEYTYPE_Custom); + } + +-void* GetMapKey_Element(XFA_Element eType, XFA_Attribute eAttribute) { +- return (void*)(uintptr_t)((static_cast(eType) << 16) | +- (static_cast(eAttribute) << 8) | +- XFA_KEYTYPE_Element); ++uint32_t GetMapKey_Element(XFA_Element eType, XFA_Attribute eAttribute) { ++ return ((static_cast(eType) << 16) | ++ (static_cast(eAttribute) << 8) | XFA_KEYTYPE_Element); + } + + std::tuple StrToRGB(const WideString& strRGB) { +@@ -106,25 +99,18 @@ std::tuple StrToRGB(const WideString& strRGB) { + + } // namespace + +-struct XFA_MAPDATABLOCK { +- uint8_t* GetData() const { return (uint8_t*)this + sizeof(XFA_MAPDATABLOCK); } +- +- const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo; +- int32_t iBytes; +-}; +- +-struct XFA_MAPMODULEDATA { +- XFA_MAPMODULEDATA() {} +- ~XFA_MAPMODULEDATA() {} ++CJX_Object::CJX_Object(CXFA_Object* obj) : object_(obj) {} + +- std::map m_ValueMap; +- std::map m_BufferMap; +-}; ++CJX_Object::~CJX_Object() = default; + +-CJX_Object::CJX_Object(CXFA_Object* obj) : object_(obj) {} ++CJX_Object* CJX_Object::AsCJXObject() { ++ return this; ++} + +-CJX_Object::~CJX_Object() { +- ClearMapModuleBuffer(); ++void CJX_Object::Trace(cppgc::Visitor* visitor) const { ++ visitor->Trace(object_); ++ visitor->Trace(layout_item_); ++ visitor->Trace(calc_data_); + } + + bool CJX_Object::DynamicTypeIs(TypeTag eType) const { +@@ -140,19 +126,24 @@ CXFA_Document* CJX_Object::GetDocument() const { + return object_->GetDocument(); + } + +-void CJX_Object::className(CFXJSE_Value* pValue, ++CXFA_Node* CJX_Object::GetXFANode() const { ++ return ToNode(GetXFAObject()); ++} ++ ++void CJX_Object::className(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } +- pValue->SetString(GetXFAObject()->GetClassName()); ++ *pValue = fxv8::NewStringHelper(pIsolate, GetXFAObject()->GetClassName()); + } + + int32_t CJX_Object::Subform_and_SubformSet_InstanceIndex() { + int32_t index = 0; +- for (CXFA_Node* pNode = ToNode(GetXFAObject())->GetPrevSibling(); pNode; ++ for (CXFA_Node* pNode = GetXFANode()->GetPrevSibling(); pNode; + pNode = pNode->GetPrevSibling()) { + if ((pNode->GetElementType() != XFA_Element::Subform) && + (pNode->GetElementType() != XFA_Element::SubformSet)) { +@@ -164,7 +155,7 @@ int32_t CJX_Object::Subform_and_SubformSet_InstanceIndex() { + } + + bool CJX_Object::HasMethod(const WideString& func) const { +- return pdfium::ContainsKey(method_specs_, func.ToUTF8()); ++ return pdfium::Contains(method_specs_, func.ToUTF8()); + } + + CJS_Result CJX_Object::RunMethod( +@@ -178,176 +169,176 @@ CJS_Result CJX_Object::RunMethod( + params); + } + +-void CJX_Object::ThrowTooManyOccurancesException(const WideString& obj) const { +- ThrowException(WideString::FromASCII("The element [") + obj + +- WideString::FromASCII( +- "] has violated its allowable number of occurrences.")); ++void CJX_Object::ThrowTooManyOccurrencesException(v8::Isolate* pIsolate, ++ const WideString& obj) const { ++ ThrowException( ++ pIsolate, WideString::FromASCII("The element [") + obj + ++ WideString::FromASCII( ++ "] has violated its allowable number of occurrences.")); + } + +-void CJX_Object::ThrowInvalidPropertyException() const { +- ThrowException(WideString::FromASCII("Invalid property set operation.")); ++void CJX_Object::ThrowInvalidPropertyException(v8::Isolate* pIsolate) const { ++ ThrowException(pIsolate, ++ WideString::FromASCII("Invalid property set operation.")); + } + +-void CJX_Object::ThrowIndexOutOfBoundsException() const { +- ThrowException(WideString::FromASCII("Index value is out of bounds.")); ++void CJX_Object::ThrowIndexOutOfBoundsException(v8::Isolate* pIsolate) const { ++ ThrowException(pIsolate, ++ WideString::FromASCII("Index value is out of bounds.")); + } + + void CJX_Object::ThrowParamCountMismatchException( ++ v8::Isolate* pIsolate, + const WideString& method) const { + ThrowException( ++ pIsolate, + WideString::FromASCII("Incorrect number of parameters calling method '") + +- method + WideString::FromASCII("'.")); ++ method + WideString::FromASCII("'.")); + } + +-void CJX_Object::ThrowArgumentMismatchException() const { +- ThrowException(WideString::FromASCII( +- "Argument mismatch in property or function argument.")); ++void CJX_Object::ThrowArgumentMismatchException(v8::Isolate* pIsolate) const { ++ ThrowException(pIsolate, ++ WideString::FromASCII( ++ "Argument mismatch in property or function argument.")); + } + +-void CJX_Object::ThrowException(const WideString& str) const { +- ASSERT(!str.IsEmpty()); +- FXJSE_ThrowMessage(str.ToUTF8().AsStringView()); ++void CJX_Object::ThrowException(v8::Isolate* pIsolate, ++ const WideString& str) const { ++ DCHECK(!str.IsEmpty()); ++ FXJSE_ThrowMessage(pIsolate, str.ToUTF8().AsStringView()); + } + +-bool CJX_Object::HasAttribute(XFA_Attribute eAttr) { +- void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); +- return HasMapModuleKey(pKey); ++bool CJX_Object::HasAttribute(XFA_Attribute eAttr) const { ++ uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); ++ return HasMapModuleKey(key); + } + +-void CJX_Object::SetAttribute(XFA_Attribute eAttr, +- WideStringView wsValue, +- bool bNotify) { +- switch (ToNode(GetXFAObject())->GetAttributeType(eAttr)) { ++void CJX_Object::SetAttributeByEnum(XFA_Attribute eAttr, ++ const WideString& wsValue, ++ bool bNotify) { ++ switch (GetXFANode()->GetAttributeType(eAttr)) { + case XFA_AttributeType::Enum: { +- Optional item = XFA_GetAttributeValueByName(wsValue); ++ absl::optional item = ++ XFA_GetAttributeValueByName(wsValue.AsStringView()); + SetEnum(eAttr, +- item ? *item : *(ToNode(GetXFAObject())->GetDefaultEnum(eAttr)), ++ item.has_value() ? item.value() ++ : GetXFANode()->GetDefaultEnum(eAttr).value(), + bNotify); + break; + } + case XFA_AttributeType::CData: +- SetCData(eAttr, WideString(wsValue), bNotify, false); ++ SetCDataImpl(eAttr, WideString(wsValue), bNotify, false); + break; + case XFA_AttributeType::Boolean: + SetBoolean(eAttr, !wsValue.EqualsASCII("0"), bNotify); + break; + case XFA_AttributeType::Integer: + SetInteger(eAttr, +- FXSYS_roundf(FXSYS_wcstof(wsValue.unterminated_c_str(), +- wsValue.GetLength(), nullptr)), ++ FXSYS_roundf(FXSYS_wcstof(wsValue.c_str(), wsValue.GetLength(), ++ nullptr)), + bNotify); + break; + case XFA_AttributeType::Measure: +- SetMeasure(eAttr, CXFA_Measurement(wsValue), bNotify); ++ SetMeasure(eAttr, CXFA_Measurement(wsValue.AsStringView()), bNotify); + break; + default: + break; + } + } + +-void CJX_Object::SetMapModuleString(void* pKey, WideStringView wsValue) { +- SetMapModuleBuffer(pKey, const_cast(wsValue.unterminated_c_str()), +- wsValue.GetLength() * sizeof(wchar_t), nullptr); +-} +- +-void CJX_Object::SetAttribute(WideStringView wsAttr, +- WideStringView wsValue, +- bool bNotify) { +- Optional attr = XFA_GetAttributeByName(wsAttr); ++void CJX_Object::SetAttributeByString(WideStringView wsAttr, ++ const WideString& wsValue) { ++ absl::optional attr = XFA_GetAttributeByName(wsAttr); + if (attr.has_value()) { +- SetAttribute(attr.value().attribute, wsValue, bNotify); ++ SetAttributeByEnum(attr.value().attribute, wsValue, true); + return; + } +- void* pKey = GetMapKey_Custom(wsAttr); +- SetMapModuleString(pKey, wsValue); ++ uint32_t key = GetMapKey_Custom(wsAttr); ++ SetMapModuleString(key, wsValue); + } + +-WideString CJX_Object::GetAttribute(WideStringView attr) { +- return TryAttribute(attr, true).value_or(WideString()); ++WideString CJX_Object::GetAttributeByString(WideStringView attr) const { ++ absl::optional result; ++ absl::optional enum_attr = XFA_GetAttributeByName(attr); ++ if (enum_attr.has_value()) ++ result = TryAttribute(enum_attr.value().attribute, true); ++ else ++ result = GetMapModuleStringFollowingChain(GetMapKey_Custom(attr)); ++ return result.value_or(WideString()); + } + +-WideString CJX_Object::GetAttribute(XFA_Attribute attr) { ++WideString CJX_Object::GetAttributeByEnum(XFA_Attribute attr) const { + return TryAttribute(attr, true).value_or(WideString()); + } + +-Optional CJX_Object::TryAttribute(XFA_Attribute eAttr, +- bool bUseDefault) { +- switch (ToNode(GetXFAObject())->GetAttributeType(eAttr)) { ++absl::optional CJX_Object::TryAttribute(XFA_Attribute eAttr, ++ bool bUseDefault) const { ++ switch (GetXFANode()->GetAttributeType(eAttr)) { + case XFA_AttributeType::Enum: { +- Optional value = TryEnum(eAttr, bUseDefault); +- if (!value) +- return {}; +- return WideString::FromASCII(XFA_AttributeValueToName(*value)); ++ absl::optional value = TryEnum(eAttr, bUseDefault); ++ if (!value.has_value()) ++ return absl::nullopt; ++ return WideString::FromASCII(XFA_AttributeValueToName(value.value())); + } + case XFA_AttributeType::CData: + return TryCData(eAttr, bUseDefault); + + case XFA_AttributeType::Boolean: { +- Optional value = TryBoolean(eAttr, bUseDefault); +- if (!value) +- return {}; +- return WideString(*value ? L"1" : L"0"); ++ absl::optional value = TryBoolean(eAttr, bUseDefault); ++ if (!value.has_value()) ++ return absl::nullopt; ++ return WideString(value.value() ? L"1" : L"0"); + } + case XFA_AttributeType::Integer: { +- Optional iValue = TryInteger(eAttr, bUseDefault); +- if (!iValue) +- return {}; +- return WideString::Format(L"%d", *iValue); ++ absl::optional iValue = TryInteger(eAttr, bUseDefault); ++ if (!iValue.has_value()) ++ return absl::nullopt; ++ return WideString::FormatInteger(iValue.value()); + } + case XFA_AttributeType::Measure: { +- Optional value = TryMeasure(eAttr, bUseDefault); +- if (!value) +- return {}; +- ++ absl::optional value = TryMeasure(eAttr, bUseDefault); ++ if (!value.has_value()) ++ return absl::nullopt; + return value->ToString(); + } + default: + break; + } +- return {}; +-} +- +-Optional CJX_Object::TryAttribute(WideStringView wsAttr, +- bool bUseDefault) { +- Optional attr = XFA_GetAttributeByName(wsAttr); +- if (attr.has_value()) +- return TryAttribute(attr.value().attribute, bUseDefault); +- return GetMapModuleString(GetMapKey_Custom(wsAttr)); ++ return absl::nullopt; + } + + void CJX_Object::RemoveAttribute(WideStringView wsAttr) { +- void* pKey = GetMapKey_Custom(wsAttr); +- if (pKey) +- RemoveMapModuleKey(pKey); ++ RemoveMapModuleKey(GetMapKey_Custom(wsAttr)); + } + +-Optional CJX_Object::TryBoolean(XFA_Attribute eAttr, bool bUseDefault) { +- void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); +- Optional value = GetMapModuleValue(pKey); ++absl::optional CJX_Object::TryBoolean(XFA_Attribute eAttr, ++ bool bUseDefault) const { ++ uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); ++ absl::optional value = GetMapModuleValueFollowingChain(key); + if (value.has_value()) + return !!value.value(); + if (!bUseDefault) +- return {}; +- return ToNode(GetXFAObject())->GetDefaultBoolean(eAttr); ++ return absl::nullopt; ++ return GetXFANode()->GetDefaultBoolean(eAttr); + } + + void CJX_Object::SetBoolean(XFA_Attribute eAttr, bool bValue, bool bNotify) { +- CFX_XMLElement* elem = SetValue(eAttr, (void*)(uintptr_t)bValue, bNotify); ++ CFX_XMLElement* elem = SetValue(eAttr, static_cast(bValue), bNotify); + if (elem) { + elem->SetAttribute(WideString::FromASCII(XFA_AttributeToName(eAttr)), + bValue ? L"1" : L"0"); + } + } + +-bool CJX_Object::GetBoolean(XFA_Attribute eAttr) { ++bool CJX_Object::GetBoolean(XFA_Attribute eAttr) const { + return TryBoolean(eAttr, true).value_or(false); + } + + void CJX_Object::SetInteger(XFA_Attribute eAttr, int32_t iValue, bool bNotify) { +- CFX_XMLElement* elem = SetValue(eAttr, (void*)(uintptr_t)iValue, bNotify); ++ CFX_XMLElement* elem = SetValue(eAttr, iValue, bNotify); + if (elem) { + elem->SetAttribute(WideString::FromASCII(XFA_AttributeToName(eAttr)), +- WideString::Format(L"%d", iValue)); ++ WideString::FormatInteger(iValue)); + } + } + +@@ -355,34 +346,32 @@ int32_t CJX_Object::GetInteger(XFA_Attribute eAttr) const { + return TryInteger(eAttr, true).value_or(0); + } + +-Optional CJX_Object::TryInteger(XFA_Attribute eAttr, +- bool bUseDefault) const { +- void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); +- Optional value = GetMapModuleValue(pKey); ++absl::optional CJX_Object::TryInteger(XFA_Attribute eAttr, ++ bool bUseDefault) const { ++ uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); ++ absl::optional value = GetMapModuleValueFollowingChain(key); + if (value.has_value()) +- return static_cast(reinterpret_cast(value.value())); ++ return value.value(); + if (!bUseDefault) +- return {}; +- return ToNode(GetXFAObject())->GetDefaultInteger(eAttr); ++ return absl::nullopt; ++ return GetXFANode()->GetDefaultInteger(eAttr); + } + +-Optional CJX_Object::TryEnum(XFA_Attribute eAttr, +- bool bUseDefault) const { +- void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); +- Optional value = GetMapModuleValue(pKey); +- if (value.has_value()) { +- return static_cast( +- reinterpret_cast(value.value())); +- } ++absl::optional CJX_Object::TryEnum(XFA_Attribute eAttr, ++ bool bUseDefault) const { ++ uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); ++ absl::optional value = GetMapModuleValueFollowingChain(key); ++ if (value.has_value()) ++ return static_cast(value.value()); + if (!bUseDefault) +- return {}; +- return ToNode(GetXFAObject())->GetDefaultEnum(eAttr); ++ return absl::nullopt; ++ return GetXFANode()->GetDefaultEnum(eAttr); + } + + void CJX_Object::SetEnum(XFA_Attribute eAttr, + XFA_AttributeValue eValue, + bool bNotify) { +- CFX_XMLElement* elem = SetValue(eAttr, (void*)(uintptr_t)eValue, bNotify); ++ CFX_XMLElement* elem = SetValue(eAttr, static_cast(eValue), bNotify); + if (elem) { + elem->SetAttribute(WideString::FromASCII(XFA_AttributeToName(eAttr)), + WideString::FromASCII(XFA_AttributeValueToName(eValue))); +@@ -394,33 +383,36 @@ XFA_AttributeValue CJX_Object::GetEnum(XFA_Attribute eAttr) const { + } + + void CJX_Object::SetMeasure(XFA_Attribute eAttr, +- CXFA_Measurement mValue, ++ const CXFA_Measurement& mValue, + bool bNotify) { +- void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); +- OnChanging(eAttr, bNotify); +- SetMapModuleBuffer(pKey, &mValue, sizeof(CXFA_Measurement), nullptr); +- OnChanged(eAttr, bNotify, false); +-} +- +-Optional CJX_Object::TryMeasure(XFA_Attribute eAttr, +- bool bUseDefault) const { +- void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); +- void* pValue; +- int32_t iBytes; +- if (GetMapModuleBuffer(pKey, &pValue, &iBytes) && +- iBytes == sizeof(CXFA_Measurement)) { +- return *static_cast(pValue); +- } ++ // Can't short-circuit update here when the value is the same since it ++ // might have come from further up the chain from where we are setting it. ++ uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); ++ if (bNotify) ++ OnChanging(eAttr); ++ SetMapModuleMeasurement(key, mValue); ++ if (bNotify) ++ OnChanged(eAttr, false); ++} ++ ++absl::optional CJX_Object::TryMeasure( ++ XFA_Attribute eAttr, ++ bool bUseDefault) const { ++ uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); ++ absl::optional result = ++ GetMapModuleMeasurementFollowingChain(key); ++ if (result.has_value()) ++ return result.value(); + if (!bUseDefault) +- return {}; +- return ToNode(GetXFAObject())->GetDefaultMeasurement(eAttr); ++ return absl::nullopt; ++ return GetXFANode()->GetDefaultMeasurement(eAttr); + } + +-Optional CJX_Object::TryMeasureAsFloat(XFA_Attribute attr) const { +- Optional measure = TryMeasure(attr, false); +- if (measure) +- return measure->ToUnit(XFA_Unit::Pt); +- return {}; ++absl::optional CJX_Object::TryMeasureAsFloat(XFA_Attribute attr) const { ++ absl::optional measure = TryMeasure(attr, false); ++ if (!measure.has_value()) ++ return absl::nullopt; ++ return measure->ToUnit(XFA_Unit::Pt); + } + + CXFA_Measurement CJX_Object::GetMeasure(XFA_Attribute eAttr) const { +@@ -428,30 +420,33 @@ CXFA_Measurement CJX_Object::GetMeasure(XFA_Attribute eAttr) const { + } + + float CJX_Object::GetMeasureInUnit(XFA_Attribute eAttr, XFA_Unit unit) const { +- auto measure = TryMeasure(eAttr, true).value_or(CXFA_Measurement()); +- return measure.ToUnit(unit); ++ return GetMeasure(eAttr).ToUnit(unit); + } + + WideString CJX_Object::GetCData(XFA_Attribute eAttr) const { + return TryCData(eAttr, true).value_or(WideString()); + } + +-void CJX_Object::SetCData(XFA_Attribute eAttr, +- const WideString& wsValue, +- bool bNotify, +- bool bScriptModify) { +- CXFA_Node* xfaObj = ToNode(GetXFAObject()); +- void* pKey = GetMapKey_Element(xfaObj->GetElementType(), eAttr); +- OnChanging(eAttr, bNotify); +- if (eAttr == XFA_Attribute::Value) { +- WideString* pClone = new WideString(wsValue); +- SetUserData(pKey, pClone, &deleteWideStringCallBack); +- } else { +- SetMapModuleString(pKey, wsValue.AsStringView()); ++void CJX_Object::SetCData(XFA_Attribute eAttr, const WideString& wsValue) { ++ return SetCDataImpl(eAttr, wsValue, false, false); ++} ++ ++void CJX_Object::SetCDataImpl(XFA_Attribute eAttr, ++ const WideString& wsValue, ++ bool bNotify, ++ bool bScriptModify) { ++ CXFA_Node* xfaObj = GetXFANode(); ++ uint32_t key = GetMapKey_Element(xfaObj->GetElementType(), eAttr); ++ absl::optional old_value = GetMapModuleString(key); ++ if (!old_value.has_value() || old_value.value() != wsValue) { ++ if (bNotify) ++ OnChanging(eAttr); ++ SetMapModuleString(key, wsValue); + if (eAttr == XFA_Attribute::Name) + xfaObj->UpdateNameHash(); ++ if (bNotify) ++ OnChanged(eAttr, bScriptModify); + } +- OnChanged(eAttr, bNotify, bScriptModify); + + if (!xfaObj->IsNeedSavingXMLNode() || eAttr == XFA_Attribute::QualifiedName || + eAttr == XFA_Attribute::BindingNode) { +@@ -479,57 +474,55 @@ void CJX_Object::SetCData(XFA_Attribute eAttr, + } + + void CJX_Object::SetAttributeValue(const WideString& wsValue, +- const WideString& wsXMLValue, +- bool bNotify, +- bool bScriptModify) { +- auto* xfaObj = ToNode(GetXFAObject()); +- void* pKey = +- GetMapKey_Element(xfaObj->GetElementType(), XFA_Attribute::Value); +- +- OnChanging(XFA_Attribute::Value, bNotify); +- WideString* pClone = new WideString(wsValue); +- +- SetUserData(pKey, pClone, &deleteWideStringCallBack); +- OnChanged(XFA_Attribute::Value, bNotify, bScriptModify); +- +- if (!xfaObj->IsNeedSavingXMLNode()) +- return; +- +- xfaObj->SetToXML(wsXMLValue); ++ const WideString& wsXMLValue) { ++ SetAttributeValueImpl(wsValue, wsXMLValue, false, false); + } + +-Optional CJX_Object::TryCData(XFA_Attribute eAttr, +- bool bUseDefault) const { +- void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); +- if (eAttr == XFA_Attribute::Value) { +- void* pData; +- int32_t iBytes = 0; +- WideString* pStr = nullptr; +- if (GetMapModuleBuffer(pKey, &pData, &iBytes) && iBytes == sizeof(void*)) { +- memcpy(&pData, pData, iBytes); +- pStr = reinterpret_cast(pData); +- } +- if (pStr) +- return *pStr; +- } else { +- Optional value = GetMapModuleString(pKey); +- if (value.has_value()) +- return value; ++void CJX_Object::SetAttributeValueImpl(const WideString& wsValue, ++ const WideString& wsXMLValue, ++ bool bNotify, ++ bool bScriptModify) { ++ auto* xfaObj = GetXFANode(); ++ uint32_t key = ++ GetMapKey_Element(xfaObj->GetElementType(), XFA_Attribute::Value); ++ absl::optional old_value = GetMapModuleString(key); ++ if (!old_value.has_value() || old_value.value() != wsValue) { ++ if (bNotify) ++ OnChanging(XFA_Attribute::Value); ++ SetMapModuleString(key, wsValue); ++ if (bNotify) ++ OnChanged(XFA_Attribute::Value, bScriptModify); ++ if (xfaObj->IsNeedSavingXMLNode()) ++ xfaObj->SetToXML(wsXMLValue); + } ++} ++ ++absl::optional CJX_Object::TryCData(XFA_Attribute eAttr, ++ bool bUseDefault) const { ++ uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); ++ absl::optional value = GetMapModuleStringFollowingChain(key); ++ if (value.has_value()) ++ return value; ++ + if (!bUseDefault) +- return {}; +- return ToNode(GetXFAObject())->GetDefaultCData(eAttr); ++ return absl::nullopt; ++ ++ return GetXFANode()->GetDefaultCData(eAttr); + } + + CFX_XMLElement* CJX_Object::SetValue(XFA_Attribute eAttr, +- void* pValue, ++ int32_t value, + bool bNotify) { +- void* pKey = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); +- OnChanging(eAttr, bNotify); +- SetMapModuleValue(pKey, pValue); +- OnChanged(eAttr, bNotify, false); +- +- CXFA_Node* pNode = ToNode(GetXFAObject()); ++ uint32_t key = GetMapKey_Element(GetXFAObject()->GetElementType(), eAttr); ++ absl::optional old_value = GetMapModuleValue(key); ++ if (!old_value.has_value() || old_value.value() != value) { ++ if (bNotify) ++ OnChanging(eAttr); ++ SetMapModuleValue(key, value); ++ if (bNotify) ++ OnChanged(eAttr, false); ++ } ++ CXFA_Node* pNode = GetXFANode(); + return pNode->IsNeedSavingXMLNode() ? ToXMLElement(pNode->GetXMLMappingNode()) + : nullptr; + } +@@ -541,9 +534,9 @@ void CJX_Object::SetContent(const WideString& wsContent, + bool bSyncData) { + CXFA_Node* pNode = nullptr; + CXFA_Node* pBindNode = nullptr; +- switch (ToNode(GetXFAObject())->GetObjectType()) { ++ switch (GetXFANode()->GetObjectType()) { + case XFA_ObjectType::ContainerNode: { +- if (XFA_FieldIsMultiListBox(ToNode(GetXFAObject()))) { ++ if (XFA_FieldIsMultiListBox(GetXFANode())) { + CXFA_Value* pValue = + GetOrCreateProperty(0, XFA_Element::Value); + if (!pValue) +@@ -551,11 +544,11 @@ void CJX_Object::SetContent(const WideString& wsContent, + + CXFA_Node* pChildValue = pValue->GetFirstChild(); + pChildValue->JSObject()->SetCData(XFA_Attribute::ContentType, +- L"text/xml", false, false); ++ L"text/xml"); + pChildValue->JSObject()->SetContent(wsContent, wsContent, bNotify, + bScriptModify, false); + +- CXFA_Node* pBind = ToNode(GetXFAObject())->GetBindData(); ++ CXFA_Node* pBind = GetXFANode()->GetBindData(); + if (bSyncData && pBind) { + std::vector wsSaveTextArray = + fxcrt::Split(wsContent, L'\n'); +@@ -573,8 +566,8 @@ void CJX_Object::SetContent(const WideString& wsContent, + while (iAddNodes-- > 0) { + CXFA_Node* pValueNodes = + pBind->CreateSamePacketNode(XFA_Element::DataValue); +- pValueNodes->JSObject()->SetCData(XFA_Attribute::Name, L"value", +- false, false); ++ pValueNodes->JSObject()->SetCData(XFA_Attribute::Name, ++ L"value"); + pValueNodes->CreateXMLMappingNode(); + pBind->InsertChildAndNotify(pValueNodes, nullptr); + } +@@ -585,15 +578,15 @@ void CJX_Object::SetContent(const WideString& wsContent, + } + valueNodes = pBind->GetNodeListForType(XFA_Element::DataValue); + } +- ASSERT(valueNodes.size() == wsSaveTextArray.size()); ++ DCHECK_EQ(valueNodes.size(), wsSaveTextArray.size()); + size_t i = 0; + for (CXFA_Node* pValueNode : valueNodes) { +- pValueNode->JSObject()->SetAttributeValue( +- wsSaveTextArray[i], wsSaveTextArray[i], false, false); ++ pValueNode->JSObject()->SetAttributeValue(wsSaveTextArray[i], ++ wsSaveTextArray[i]); + i++; + } + for (auto* pArrayNode : pBind->GetBindItemsCopy()) { +- if (pArrayNode != ToNode(GetXFAObject())) { ++ if (pArrayNode != GetXFANode()) { + pArrayNode->JSObject()->SetContent(wsContent, wsContent, bNotify, + bScriptModify, false); + } +@@ -601,8 +594,8 @@ void CJX_Object::SetContent(const WideString& wsContent, + } + break; + } +- if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::ExclGroup) { +- pNode = ToNode(GetXFAObject()); ++ if (GetXFANode()->GetElementType() == XFA_Element::ExclGroup) { ++ pNode = GetXFANode(); + } else { + CXFA_Value* pValue = + GetOrCreateProperty(0, XFA_Element::Value); +@@ -610,16 +603,17 @@ void CJX_Object::SetContent(const WideString& wsContent, + break; + + CXFA_Node* pChildValue = pValue->GetFirstChild(); +- ASSERT(pChildValue); +- pChildValue->JSObject()->SetContent(wsContent, wsContent, bNotify, +- bScriptModify, false); ++ if (pChildValue) { ++ pChildValue->JSObject()->SetContent(wsContent, wsContent, bNotify, ++ bScriptModify, false); ++ } + } +- pBindNode = ToNode(GetXFAObject())->GetBindData(); ++ pBindNode = GetXFANode()->GetBindData(); + if (pBindNode && bSyncData) { + pBindNode->JSObject()->SetContent(wsContent, wsXMLValue, bNotify, + bScriptModify, false); + for (auto* pArrayNode : pBindNode->GetBindItemsCopy()) { +- if (pArrayNode != ToNode(GetXFAObject())) { ++ if (pArrayNode != GetXFANode()) { + pArrayNode->JSObject()->SetContent(wsContent, wsContent, bNotify, + true, false); + } +@@ -630,27 +624,23 @@ void CJX_Object::SetContent(const WideString& wsContent, + } + case XFA_ObjectType::ContentNode: { + WideString wsContentType; +- if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::ExData) { +- Optional ret = ++ if (GetXFANode()->GetElementType() == XFA_Element::ExData) { ++ absl::optional ret = + TryAttribute(XFA_Attribute::ContentType, false); +- if (ret) +- wsContentType = *ret; ++ if (ret.has_value()) ++ wsContentType = ret.value(); + if (wsContentType.EqualsASCII("text/html")) { + wsContentType.clear(); +- SetAttribute(XFA_Attribute::ContentType, wsContentType.AsStringView(), +- false); ++ SetAttributeByEnum(XFA_Attribute::ContentType, wsContentType, false); + } + } + +- CXFA_Node* pContentRawDataNode = ToNode(GetXFAObject())->GetFirstChild(); ++ CXFA_Node* pContentRawDataNode = GetXFANode()->GetFirstChild(); + if (!pContentRawDataNode) { +- pContentRawDataNode = +- ToNode(GetXFAObject()) +- ->CreateSamePacketNode(wsContentType.EqualsASCII("text/xml") +- ? XFA_Element::Sharpxml +- : XFA_Element::Sharptext); +- ToNode(GetXFAObject()) +- ->InsertChildAndNotify(pContentRawDataNode, nullptr); ++ pContentRawDataNode = GetXFANode()->CreateSamePacketNode( ++ wsContentType.EqualsASCII("text/xml") ? XFA_Element::Sharpxml ++ : XFA_Element::Sharptext); ++ GetXFANode()->InsertChildAndNotify(pContentRawDataNode, nullptr); + } + pContentRawDataNode->JSObject()->SetContent( + wsContent, wsXMLValue, bNotify, bScriptModify, bSyncData); +@@ -658,13 +648,12 @@ void CJX_Object::SetContent(const WideString& wsContent, + } + case XFA_ObjectType::NodeC: + case XFA_ObjectType::TextNode: +- pNode = ToNode(GetXFAObject()); ++ pNode = GetXFANode(); + break; + case XFA_ObjectType::NodeV: +- pNode = ToNode(GetXFAObject()); +- if (bSyncData && +- ToNode(GetXFAObject())->GetPacketType() == XFA_PacketType::Form) { +- CXFA_Node* pParent = ToNode(GetXFAObject())->GetParent(); ++ pNode = GetXFANode(); ++ if (bSyncData && GetXFANode()->GetPacketType() == XFA_PacketType::Form) { ++ CXFA_Node* pParent = GetXFANode()->GetParent(); + if (pParent) { + pParent = pParent->GetParent(); + } +@@ -681,16 +670,16 @@ void CJX_Object::SetContent(const WideString& wsContent, + } + break; + default: +- if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::DataValue) { +- pNode = ToNode(GetXFAObject()); +- pBindNode = ToNode(GetXFAObject()); ++ if (GetXFANode()->GetElementType() == XFA_Element::DataValue) { ++ pNode = GetXFANode(); ++ pBindNode = GetXFANode(); + } + break; + } + if (!pNode) + return; + +- SetAttributeValue(wsContent, wsXMLValue, bNotify, bScriptModify); ++ SetAttributeValueImpl(wsContent, wsXMLValue, bNotify, bScriptModify); + if (pBindNode && bSyncData) { + for (auto* pArrayNode : pBindNode->GetBindItemsCopy()) { + pArrayNode->JSObject()->SetContent(wsContent, wsContent, bNotify, +@@ -699,39 +688,39 @@ void CJX_Object::SetContent(const WideString& wsContent, + } + } + +-WideString CJX_Object::GetContent(bool bScriptModify) { ++WideString CJX_Object::GetContent(bool bScriptModify) const { + return TryContent(bScriptModify, true).value_or(WideString()); + } + +-Optional CJX_Object::TryContent(bool bScriptModify, bool bProto) { ++absl::optional CJX_Object::TryContent(bool bScriptModify, ++ bool bProto) const { + CXFA_Node* pNode = nullptr; +- switch (ToNode(GetXFAObject())->GetObjectType()) { ++ switch (GetXFANode()->GetObjectType()) { + case XFA_ObjectType::ContainerNode: +- if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::ExclGroup) { +- pNode = ToNode(GetXFAObject()); ++ if (GetXFANode()->GetElementType() == XFA_Element::ExclGroup) { ++ pNode = GetXFANode(); + } else { + CXFA_Value* pValue = +- ToNode(GetXFAObject()) +- ->GetChild(0, XFA_Element::Value, false); ++ GetXFANode()->GetChild(0, XFA_Element::Value, false); + if (!pValue) +- return {}; ++ return absl::nullopt; + + CXFA_Node* pChildValue = pValue->GetFirstChild(); +- if (pChildValue && XFA_FieldIsMultiListBox(ToNode(GetXFAObject()))) { +- pChildValue->JSObject()->SetAttribute(XFA_Attribute::ContentType, +- L"text/xml", false); ++ if (pChildValue && XFA_FieldIsMultiListBox(GetXFANode())) { ++ pChildValue->JSObject()->SetAttributeByEnum( ++ XFA_Attribute::ContentType, L"text/xml", false); + } +- if (pChildValue) +- return pChildValue->JSObject()->TryContent(bScriptModify, bProto); +- return {}; ++ if (!pChildValue) ++ return absl::nullopt; ++ return pChildValue->JSObject()->TryContent(bScriptModify, bProto); + } + break; + case XFA_ObjectType::ContentNode: { +- CXFA_Node* pContentRawDataNode = ToNode(GetXFAObject())->GetFirstChild(); ++ CXFA_Node* pContentRawDataNode = GetXFANode()->GetFirstChild(); + if (!pContentRawDataNode) { + XFA_Element element = XFA_Element::Sharptext; +- if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::ExData) { +- Optional contentType = ++ if (GetXFANode()->GetElementType() == XFA_Element::ExData) { ++ absl::optional contentType = + TryAttribute(XFA_Attribute::ContentType, false); + if (contentType.has_value()) { + if (contentType.value().EqualsASCII("text/html")) +@@ -740,349 +729,270 @@ Optional CJX_Object::TryContent(bool bScriptModify, bool bProto) { + element = XFA_Element::Sharpxml; + } + } +- pContentRawDataNode = +- ToNode(GetXFAObject())->CreateSamePacketNode(element); +- ToNode(GetXFAObject()) +- ->InsertChildAndNotify(pContentRawDataNode, nullptr); ++ pContentRawDataNode = GetXFANode()->CreateSamePacketNode(element); ++ GetXFANode()->InsertChildAndNotify(pContentRawDataNode, nullptr); + } + return pContentRawDataNode->JSObject()->TryContent(bScriptModify, true); + } + case XFA_ObjectType::NodeC: + case XFA_ObjectType::NodeV: + case XFA_ObjectType::TextNode: +- pNode = ToNode(GetXFAObject()); +- FALLTHROUGH; ++ pNode = GetXFANode(); ++ [[fallthrough]]; + default: +- if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::DataValue) +- pNode = ToNode(GetXFAObject()); ++ if (GetXFANode()->GetElementType() == XFA_Element::DataValue) ++ pNode = GetXFANode(); + break; + } + if (pNode) { + if (bScriptModify) { + CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext(); +- pScriptContext->AddNodesOfRunScript(ToNode(GetXFAObject())); ++ pScriptContext->AddNodesOfRunScript(GetXFANode()); + } + return TryCData(XFA_Attribute::Value, false); + } +- return {}; ++ return absl::nullopt; + } + +-Optional CJX_Object::TryNamespace() { +- if (ToNode(GetXFAObject())->IsModelNode() || +- ToNode(GetXFAObject())->GetElementType() == XFA_Element::Packet) { +- CFX_XMLNode* pXMLNode = ToNode(GetXFAObject())->GetXMLMappingNode(); ++absl::optional CJX_Object::TryNamespace() const { ++ if (GetXFANode()->IsModelNode() || ++ GetXFANode()->GetElementType() == XFA_Element::Packet) { ++ CFX_XMLNode* pXMLNode = GetXFANode()->GetXMLMappingNode(); + CFX_XMLElement* element = ToXMLElement(pXMLNode); + if (!element) +- return {}; ++ return absl::nullopt; + + return element->GetNamespaceURI(); + } + +- if (ToNode(GetXFAObject())->GetPacketType() != XFA_PacketType::Datasets) +- return ToNode(GetXFAObject())->GetModelNode()->JSObject()->TryNamespace(); ++ if (GetXFANode()->GetPacketType() != XFA_PacketType::Datasets) ++ return GetXFANode()->GetModelNode()->JSObject()->TryNamespace(); + +- CFX_XMLNode* pXMLNode = ToNode(GetXFAObject())->GetXMLMappingNode(); ++ CFX_XMLNode* pXMLNode = GetXFANode()->GetXMLMappingNode(); + CFX_XMLElement* element = ToXMLElement(pXMLNode); + if (!element) +- return {}; ++ return absl::nullopt; + +- if (ToNode(GetXFAObject())->GetElementType() == XFA_Element::DataValue && ++ if (GetXFANode()->GetElementType() == XFA_Element::DataValue && + GetEnum(XFA_Attribute::Contains) == XFA_AttributeValue::MetaData) { + WideString wsNamespace; + if (!XFA_FDEExtension_ResolveNamespaceQualifier( + element, GetCData(XFA_Attribute::QualifiedName), &wsNamespace)) { +- return {}; ++ return absl::nullopt; + } + return wsNamespace; + } + return element->GetNamespaceURI(); + } + +-std::pair CJX_Object::GetPropertyInternal( +- int32_t index, +- XFA_Element eProperty) const { +- return ToNode(GetXFAObject())->GetProperty(index, eProperty); ++CXFA_Node* CJX_Object::GetPropertyInternal(int32_t index, ++ XFA_Element eProperty) const { ++ return GetXFANode()->GetProperty(index, eProperty).first; + } + + CXFA_Node* CJX_Object::GetOrCreatePropertyInternal(int32_t index, + XFA_Element eProperty) { +- return ToNode(GetXFAObject())->GetOrCreateProperty(index, eProperty); ++ return GetXFANode()->GetOrCreateProperty(index, eProperty); ++} ++ ++CFXJSE_MapModule* CJX_Object::CreateMapModule() { ++ if (!map_module_) ++ map_module_ = std::make_unique(); ++ return map_module_.get(); ++} ++ ++CFXJSE_MapModule* CJX_Object::GetMapModule() const { ++ return map_module_.get(); ++} ++ ++void CJX_Object::SetMapModuleValue(uint32_t key, int32_t value) { ++ CreateMapModule()->SetValue(key, value); ++} ++ ++void CJX_Object::SetMapModuleString(uint32_t key, const WideString& wsValue) { ++ CreateMapModule()->SetString(key, wsValue); + } + +-void CJX_Object::SetUserData( +- void* pKey, +- void* pData, +- const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo) { +- ASSERT(pCallbackInfo); +- SetMapModuleBuffer(pKey, &pData, sizeof(void*), pCallbackInfo); ++void CJX_Object::SetMapModuleMeasurement(uint32_t key, ++ const CXFA_Measurement& value) { ++ CreateMapModule()->SetMeasurement(key, value); + } + +-XFA_MAPMODULEDATA* CJX_Object::CreateMapModuleData() { +- if (!map_module_data_) +- map_module_data_ = pdfium::MakeUnique(); +- return map_module_data_.get(); ++absl::optional CJX_Object::GetMapModuleValue(uint32_t key) const { ++ CFXJSE_MapModule* pModule = GetMapModule(); ++ if (!pModule) ++ return absl::nullopt; ++ return pModule->GetValue(key); + } + +-XFA_MAPMODULEDATA* CJX_Object::GetMapModuleData() const { +- return map_module_data_.get(); ++absl::optional CJX_Object::GetMapModuleString(uint32_t key) const { ++ CFXJSE_MapModule* pModule = GetMapModule(); ++ if (!pModule) ++ return absl::nullopt; ++ return pModule->GetString(key); + } + +-void CJX_Object::SetMapModuleValue(void* pKey, void* pValue) { +- CreateMapModuleData()->m_ValueMap[pKey] = pValue; ++absl::optional CJX_Object::GetMapModuleMeasurement( ++ uint32_t key) const { ++ CFXJSE_MapModule* pModule = GetMapModule(); ++ if (!pModule) ++ return absl::nullopt; ++ return pModule->GetMeasurement(key); + } + +-Optional CJX_Object::GetMapModuleValue(void* pKey) const { ++absl::optional CJX_Object::GetMapModuleValueFollowingChain( ++ uint32_t key) const { + std::set visited; +- for (const CXFA_Node* pNode = ToNode(GetXFAObject()); pNode; ++ for (const CXFA_Node* pNode = GetXFANode(); pNode; + pNode = pNode->GetTemplateNodeIfExists()) { + if (!visited.insert(pNode).second) + break; + +- XFA_MAPMODULEDATA* pModule = pNode->JSObject()->GetMapModuleData(); +- if (pModule) { +- auto it = pModule->m_ValueMap.find(pKey); +- if (it != pModule->m_ValueMap.end()) +- return it->second; +- } ++ absl::optional result = pNode->JSObject()->GetMapModuleValue(key); ++ if (result.has_value()) ++ return result; ++ + if (pNode->GetPacketType() == XFA_PacketType::Datasets) + break; + } +- return {}; +-} +- +-Optional CJX_Object::GetMapModuleString(void* pKey) const { +- void* pRawValue; +- int32_t iBytes; +- if (!GetMapModuleBuffer(pKey, &pRawValue, &iBytes)) +- return {}; +- +- // Defensive measure: no out-of-bounds pointers even if zero length. +- int32_t iChars = iBytes / sizeof(wchar_t); +- return WideString(iChars ? static_cast(pRawValue) : nullptr, +- iChars); +-} +- +-void CJX_Object::SetMapModuleBuffer( +- void* pKey, +- void* pValue, +- int32_t iBytes, +- const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo) { +- XFA_MAPDATABLOCK*& pBuffer = CreateMapModuleData()->m_BufferMap[pKey]; +- if (!pBuffer) { +- pBuffer = reinterpret_cast( +- FX_Alloc(uint8_t, sizeof(XFA_MAPDATABLOCK) + iBytes)); +- } else if (pBuffer->iBytes != iBytes) { +- if (pBuffer->pCallbackInfo && pBuffer->pCallbackInfo->pFree) +- pBuffer->pCallbackInfo->pFree(*(void**)pBuffer->GetData()); +- +- pBuffer = reinterpret_cast( +- FX_Realloc(uint8_t, pBuffer, sizeof(XFA_MAPDATABLOCK) + iBytes)); +- } else if (pBuffer->pCallbackInfo && pBuffer->pCallbackInfo->pFree) { +- pBuffer->pCallbackInfo->pFree( +- *reinterpret_cast(pBuffer->GetData())); +- } +- if (!pBuffer) +- return; +- +- pBuffer->pCallbackInfo = pCallbackInfo; +- pBuffer->iBytes = iBytes; +- memcpy(pBuffer->GetData(), pValue, iBytes); ++ return absl::nullopt; + } + +-bool CJX_Object::GetMapModuleBuffer(void* pKey, +- void** pValue, +- int32_t* pBytes) const { ++absl::optional CJX_Object::GetMapModuleStringFollowingChain( ++ uint32_t key) const { + std::set visited; +- XFA_MAPDATABLOCK* pBuffer = nullptr; +- for (const CXFA_Node* pNode = ToNode(GetXFAObject()); pNode; ++ for (const CXFA_Node* pNode = GetXFANode(); pNode; + pNode = pNode->GetTemplateNodeIfExists()) { + if (!visited.insert(pNode).second) + break; + +- XFA_MAPMODULEDATA* pModule = pNode->JSObject()->GetMapModuleData(); +- if (pModule) { +- auto it = pModule->m_BufferMap.find(pKey); +- if (it != pModule->m_BufferMap.end()) { +- pBuffer = it->second; +- break; +- } +- } ++ absl::optional result = ++ pNode->JSObject()->GetMapModuleString(key); ++ if (result.has_value()) ++ return result; ++ + if (pNode->GetPacketType() == XFA_PacketType::Datasets) + break; + } +- if (!pBuffer) +- return false; +- +- *pValue = pBuffer->GetData(); +- *pBytes = pBuffer->iBytes; +- return true; ++ return absl::nullopt; + } + +-bool CJX_Object::HasMapModuleKey(void* pKey) { +- XFA_MAPMODULEDATA* pModule = GetMapModuleData(); +- return pModule && (pdfium::ContainsKey(pModule->m_ValueMap, pKey) || +- pdfium::ContainsKey(pModule->m_BufferMap, pKey)); +-} +- +-void CJX_Object::ClearMapModuleBuffer() { +- XFA_MAPMODULEDATA* pModule = GetMapModuleData(); +- if (!pModule) +- return; ++absl::optional ++CJX_Object::GetMapModuleMeasurementFollowingChain(uint32_t key) const { ++ std::set visited; ++ for (const CXFA_Node* pNode = GetXFANode(); pNode; ++ pNode = pNode->GetTemplateNodeIfExists()) { ++ if (!visited.insert(pNode).second) ++ break; + +- for (auto& pair : pModule->m_BufferMap) { +- XFA_MAPDATABLOCK* pBuffer = pair.second; +- if (pBuffer) { +- if (pBuffer->pCallbackInfo && pBuffer->pCallbackInfo->pFree) +- pBuffer->pCallbackInfo->pFree(*(void**)pBuffer->GetData()); ++ absl::optional result = ++ pNode->JSObject()->GetMapModuleMeasurement(key); ++ if (result.has_value()) ++ return result; + +- FX_Free(pBuffer); +- } ++ if (pNode->GetPacketType() == XFA_PacketType::Datasets) ++ break; + } +- pModule->m_BufferMap.clear(); +- pModule->m_ValueMap.clear(); ++ return absl::nullopt; + } + +-void CJX_Object::RemoveMapModuleKey(void* pKey) { +- ASSERT(pKey); +- +- XFA_MAPMODULEDATA* pModule = GetMapModuleData(); +- if (!pModule) +- return; +- +- auto it = pModule->m_BufferMap.find(pKey); +- if (it != pModule->m_BufferMap.end()) { +- XFA_MAPDATABLOCK* pBuffer = it->second; +- if (pBuffer) { +- if (pBuffer->pCallbackInfo && pBuffer->pCallbackInfo->pFree) +- pBuffer->pCallbackInfo->pFree(*(void**)pBuffer->GetData()); ++bool CJX_Object::HasMapModuleKey(uint32_t key) const { ++ CFXJSE_MapModule* pModule = GetMapModule(); ++ return pModule && pModule->HasKey(key); ++} + +- FX_Free(pBuffer); +- } +- pModule->m_BufferMap.erase(it); +- } +- pModule->m_ValueMap.erase(pKey); +- return; ++void CJX_Object::RemoveMapModuleKey(uint32_t key) { ++ CFXJSE_MapModule* pModule = GetMapModule(); ++ if (pModule) ++ pModule->RemoveKey(key); + } + +-void CJX_Object::MergeAllData(CXFA_Object* pDstModule) { +- XFA_MAPMODULEDATA* pDstModuleData = +- ToNode(pDstModule)->JSObject()->CreateMapModuleData(); +- XFA_MAPMODULEDATA* pSrcModuleData = GetMapModuleData(); +- if (!pSrcModuleData) ++void CJX_Object::MergeAllData(CXFA_Object* pDstObj) { ++ CFXJSE_MapModule* pDstModule = ToNode(pDstObj)->JSObject()->CreateMapModule(); ++ CFXJSE_MapModule* pSrcModule = GetMapModule(); ++ if (!pSrcModule) + return; + +- for (const auto& pair : pSrcModuleData->m_ValueMap) +- pDstModuleData->m_ValueMap[pair.first] = pair.second; +- +- for (const auto& pair : pSrcModuleData->m_BufferMap) { +- XFA_MAPDATABLOCK* pSrcBuffer = pair.second; +- XFA_MAPDATABLOCK*& pDstBuffer = pDstModuleData->m_BufferMap[pair.first]; +- if (pSrcBuffer->pCallbackInfo && pSrcBuffer->pCallbackInfo->pFree && +- !pSrcBuffer->pCallbackInfo->pCopy) { +- if (pDstBuffer) { +- pDstBuffer->pCallbackInfo->pFree(*(void**)pDstBuffer->GetData()); +- pDstModuleData->m_BufferMap.erase(pair.first); +- } +- continue; +- } +- if (!pDstBuffer) { +- pDstBuffer = (XFA_MAPDATABLOCK*)FX_Alloc( +- uint8_t, sizeof(XFA_MAPDATABLOCK) + pSrcBuffer->iBytes); +- } else if (pDstBuffer->iBytes != pSrcBuffer->iBytes) { +- if (pDstBuffer->pCallbackInfo && pDstBuffer->pCallbackInfo->pFree) { +- pDstBuffer->pCallbackInfo->pFree(*(void**)pDstBuffer->GetData()); +- } +- pDstBuffer = (XFA_MAPDATABLOCK*)FX_Realloc( +- uint8_t, pDstBuffer, sizeof(XFA_MAPDATABLOCK) + pSrcBuffer->iBytes); +- } else if (pDstBuffer->pCallbackInfo && pDstBuffer->pCallbackInfo->pFree) { +- pDstBuffer->pCallbackInfo->pFree(*(void**)pDstBuffer->GetData()); +- } +- if (!pDstBuffer) +- continue; +- +- pDstBuffer->pCallbackInfo = pSrcBuffer->pCallbackInfo; +- pDstBuffer->iBytes = pSrcBuffer->iBytes; +- memcpy(pDstBuffer->GetData(), pSrcBuffer->GetData(), pSrcBuffer->iBytes); +- if (pDstBuffer->pCallbackInfo && pDstBuffer->pCallbackInfo->pCopy) { +- pDstBuffer->pCallbackInfo->pCopy(*(void**)pDstBuffer->GetData()); +- } +- } ++ pDstModule->MergeDataFrom(pSrcModule); + } + +-void CJX_Object::MoveBufferMapData(CXFA_Object* pDstModule) { +- if (!pDstModule) ++void CJX_Object::MoveBufferMapData(CXFA_Object* pDstObj) { ++ if (!pDstObj) + return; + +- bool bNeedMove = true; +- if (pDstModule->GetElementType() != GetXFAObject()->GetElementType()) +- bNeedMove = false; ++ if (pDstObj->GetElementType() == GetXFAObject()->GetElementType()) ++ ToNode(pDstObj)->JSObject()->TakeCalcDataFrom(this); + +- if (bNeedMove) +- ToNode(pDstModule)->JSObject()->SetCalcData(ReleaseCalcData()); +- if (!pDstModule->IsNodeV()) ++ if (!pDstObj->IsNodeV()) + return; + +- WideString wsValue = ToNode(pDstModule)->JSObject()->GetContent(false); ++ WideString wsValue = ToNode(pDstObj)->JSObject()->GetContent(false); + WideString wsFormatValue(wsValue); +- CXFA_Node* pNode = ToNode(pDstModule)->GetContainerNode(); ++ CXFA_Node* pNode = ToNode(pDstObj)->GetContainerNode(); + if (pNode) + wsFormatValue = pNode->GetFormatDataValue(wsValue); + +- ToNode(pDstModule) +- ->JSObject() +- ->SetContent(wsValue, wsFormatValue, true, true, true); ++ ToNode(pDstObj)->JSObject()->SetContent(wsValue, wsFormatValue, true, true, ++ true); + } + +-void CJX_Object::MoveBufferMapData(CXFA_Object* pSrcModule, +- CXFA_Object* pDstModule) { +- if (!pSrcModule || !pDstModule) ++void CJX_Object::MoveBufferMapData(CXFA_Object* pSrcObj, CXFA_Object* pDstObj) { ++ if (!pSrcObj || !pDstObj) + return; + +- CXFA_Node* pSrcChild = ToNode(pSrcModule)->GetFirstChild(); +- CXFA_Node* pDstChild = ToNode(pDstModule)->GetFirstChild(); ++ CXFA_Node* pSrcChild = ToNode(pSrcObj)->GetFirstChild(); ++ CXFA_Node* pDstChild = ToNode(pDstObj)->GetFirstChild(); + while (pSrcChild && pDstChild) { + MoveBufferMapData(pSrcChild, pDstChild); +- + pSrcChild = pSrcChild->GetNextSibling(); + pDstChild = pDstChild->GetNextSibling(); + } +- ToNode(pSrcModule)->JSObject()->MoveBufferMapData(pDstModule); ++ ToNode(pSrcObj)->JSObject()->MoveBufferMapData(pDstObj); + } + +-void CJX_Object::OnChanging(XFA_Attribute eAttr, bool bNotify) { +- if (!bNotify || !ToNode(GetXFAObject())->IsInitialized()) ++void CJX_Object::OnChanging(XFA_Attribute eAttr) { ++ if (!GetXFANode()->IsInitialized()) + return; + + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); +- if (pNotify) +- pNotify->OnValueChanging(ToNode(GetXFAObject()), eAttr); ++ if (!pNotify) ++ return; ++ ++ pNotify->OnValueChanging(GetXFANode(), eAttr); + } + +-void CJX_Object::OnChanged(XFA_Attribute eAttr, +- bool bNotify, +- bool bScriptModify) { +- if (bNotify && ToNode(GetXFAObject())->IsInitialized()) +- ToNode(GetXFAObject())->SendAttributeChangeMessage(eAttr, bScriptModify); ++void CJX_Object::OnChanged(XFA_Attribute eAttr, bool bScriptModify) { ++ if (!GetXFANode()->IsInitialized()) ++ return; ++ ++ GetXFANode()->SendAttributeChangeMessage(eAttr, bScriptModify); + } + +-void CJX_Object::SetCalcData(std::unique_ptr data) { +- calc_data_ = std::move(data); ++CJX_Object::CalcData* CJX_Object::GetOrCreateCalcData(cppgc::Heap* heap) { ++ if (!calc_data_) { ++ calc_data_ = ++ cppgc::MakeGarbageCollected(heap->GetAllocationHandle()); ++ } ++ return calc_data_; + } + +-std::unique_ptr CJX_Object::ReleaseCalcData() { +- return std::move(calc_data_); ++void CJX_Object::TakeCalcDataFrom(CJX_Object* that) { ++ calc_data_ = that->calc_data_; ++ that->calc_data_ = nullptr; + } + +-void CJX_Object::ScriptAttributeString(CFXJSE_Value* pValue, ++void CJX_Object::ScriptAttributeString(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (!bSetting) { +- pValue->SetString(GetAttribute(eAttribute).ToUTF8().AsStringView()); ++ *pValue = fxv8::NewStringHelper( ++ pIsolate, GetAttributeByEnum(eAttribute).ToUTF8().AsStringView()); + return; + } + +- WideString wsValue = pValue->ToWideString(); +- SetAttribute(eAttribute, wsValue.AsStringView(), true); ++ WideString wsValue = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue); ++ SetAttributeByEnum(eAttribute, wsValue, true); + if (eAttribute != XFA_Attribute::Use || + GetXFAObject()->GetElementType() != XFA_Element::Desc) { + return; +@@ -1100,33 +1010,34 @@ void CJX_Object::ScriptAttributeString(CFXJSE_Value* pValue, + WideString wsSOM; + if (!wsValue.IsEmpty()) { + if (wsValue[0] == '#') +- wsID = wsValue.Substr(1, wsValue.GetLength() - 1); ++ wsID = wsValue.Substr(1); + else + wsSOM = std::move(wsValue); + } + + CXFA_Node* pProtoNode = nullptr; + if (!wsSOM.IsEmpty()) { +- XFA_RESOLVENODE_RS resolveNodeRS; +- bool bRet = GetDocument()->GetScriptContext()->ResolveObjects( +- pProtoRoot, wsSOM.AsStringView(), &resolveNodeRS, +- XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes | +- XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent | +- XFA_RESOLVENODE_Siblings, +- nullptr); +- if (bRet && resolveNodeRS.objects.front()->IsNode()) +- pProtoNode = resolveNodeRS.objects.front()->AsNode(); +- ++ absl::optional maybeResult = ++ GetDocument()->GetScriptContext()->ResolveObjects( ++ pProtoRoot, wsSOM.AsStringView(), ++ Mask{ ++ XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes, ++ XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kParent, ++ XFA_ResolveFlag::kSiblings}); ++ if (maybeResult.has_value() && ++ maybeResult.value().objects.front()->IsNode()) { ++ pProtoNode = maybeResult.value().objects.front()->AsNode(); ++ } + } else if (!wsID.IsEmpty()) { + pProtoNode = GetDocument()->GetNodeByID(pProtoRoot, wsID.AsStringView()); + } +- if (!pProtoNode) ++ if (!pProtoNode || pProtoNode->GetPacketType() != XFA_PacketType::Template) + return; + +- CXFA_Node* pHeadChild = ToNode(GetXFAObject())->GetFirstChild(); ++ CXFA_Node* pHeadChild = GetXFANode()->GetFirstChild(); + while (pHeadChild) { + CXFA_Node* pSibling = pHeadChild->GetNextSibling(); +- ToNode(GetXFAObject())->RemoveChildAndNotify(pHeadChild, true); ++ GetXFANode()->RemoveChildAndNotify(pHeadChild, true); + pHeadChild = pSibling; + } + +@@ -1135,32 +1046,37 @@ void CJX_Object::ScriptAttributeString(CFXJSE_Value* pValue, + while (pHeadChild) { + CXFA_Node* pSibling = pHeadChild->GetNextSibling(); + pProtoForm->RemoveChildAndNotify(pHeadChild, true); +- ToNode(GetXFAObject())->InsertChildAndNotify(pHeadChild, nullptr); ++ GetXFANode()->InsertChildAndNotify(pHeadChild, nullptr); + pHeadChild = pSibling; + } + } + +-void CJX_Object::ScriptAttributeBool(CFXJSE_Value* pValue, ++void CJX_Object::ScriptAttributeBool(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- SetBoolean(eAttribute, pValue->ToBoolean(), true); ++ SetBoolean(eAttribute, fxv8::ReentrantToBooleanHelper(pIsolate, *pValue), ++ true); + return; + } +- pValue->SetString(GetBoolean(eAttribute) ? "1" : "0"); ++ *pValue = fxv8::NewStringHelper(pIsolate, GetBoolean(eAttribute) ? "1" : "0"); + } + +-void CJX_Object::ScriptAttributeInteger(CFXJSE_Value* pValue, ++void CJX_Object::ScriptAttributeInteger(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- SetInteger(eAttribute, pValue->ToInteger(), true); ++ SetInteger(eAttribute, fxv8::ReentrantToInt32Helper(pIsolate, *pValue), ++ true); + return; + } +- pValue->SetInteger(GetInteger(eAttribute)); ++ *pValue = fxv8::NewNumberHelper(pIsolate, GetInteger(eAttribute)); + } + +-void CJX_Object::ScriptSomFontColor(CFXJSE_Value* pValue, ++void CJX_Object::ScriptSomFontColor(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_Font* font = ToNode(object_.Get())->GetOrCreateFontIfPossible(); +@@ -1171,7 +1087,8 @@ void CJX_Object::ScriptSomFontColor(CFXJSE_Value* pValue, + int32_t r; + int32_t g; + int32_t b; +- std::tie(r, g, b) = StrToRGB(pValue->ToWideString()); ++ std::tie(r, g, b) = ++ StrToRGB(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); + FX_ARGB color = ArgbEncode(0xff, r, g, b); + font->SetColor(color); + return; +@@ -1182,10 +1099,12 @@ void CJX_Object::ScriptSomFontColor(CFXJSE_Value* pValue, + int32_t g; + int32_t b; + std::tie(a, r, g, b) = ArgbDecode(font->GetColor()); +- pValue->SetString(ByteString::Format("%d,%d,%d", r, g, b).AsStringView()); ++ *pValue = fxv8::NewStringHelper( ++ pIsolate, ByteString::Format("%d,%d,%d", r, g, b).AsStringView()); + } + +-void CJX_Object::ScriptSomFillColor(CFXJSE_Value* pValue, ++void CJX_Object::ScriptSomFillColor(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_Border* border = ToNode(object_.Get())->GetOrCreateBorderIfPossible(); +@@ -1197,23 +1116,25 @@ void CJX_Object::ScriptSomFillColor(CFXJSE_Value* pValue, + int32_t r; + int32_t g; + int32_t b; +- std::tie(r, g, b) = StrToRGB(pValue->ToWideString()); ++ std::tie(r, g, b) = ++ StrToRGB(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); + FX_ARGB color = ArgbEncode(0xff, r, g, b); + borderfill->SetColor(color); + return; + } + +- FX_ARGB color = borderfill->GetColor(false); ++ FX_ARGB color = borderfill->GetFillColor(); + int32_t a; + int32_t r; + int32_t g; + int32_t b; + std::tie(a, r, g, b) = ArgbDecode(color); +- pValue->SetString( +- WideString::Format(L"%d,%d,%d", r, g, b).ToUTF8().AsStringView()); ++ *pValue = fxv8::NewStringHelper( ++ pIsolate, ByteString::Format("%d,%d,%d", r, g, b).AsStringView()); + } + +-void CJX_Object::ScriptSomBorderColor(CFXJSE_Value* pValue, ++void CJX_Object::ScriptSomBorderColor(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_Border* border = ToNode(object_.Get())->GetOrCreateBorderIfPossible(); +@@ -1222,7 +1143,8 @@ void CJX_Object::ScriptSomBorderColor(CFXJSE_Value* pValue, + int32_t r = 0; + int32_t g = 0; + int32_t b = 0; +- std::tie(r, g, b) = StrToRGB(pValue->ToWideString()); ++ std::tie(r, g, b) = ++ StrToRGB(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); + FX_ARGB rgb = ArgbEncode(100, r, g, b); + for (int32_t i = 0; i < iSize; ++i) { + CXFA_Edge* edge = border->GetEdgeIfExists(i); +@@ -1240,11 +1162,12 @@ void CJX_Object::ScriptSomBorderColor(CFXJSE_Value* pValue, + int32_t g; + int32_t b; + std::tie(a, r, g, b) = ArgbDecode(color); +- pValue->SetString( +- WideString::Format(L"%d,%d,%d", r, g, b).ToUTF8().AsStringView()); ++ *pValue = fxv8::NewStringHelper( ++ pIsolate, ByteString::Format("%d,%d,%d", r, g, b).AsStringView()); + } + +-void CJX_Object::ScriptSomBorderWidth(CFXJSE_Value* pValue, ++void CJX_Object::ScriptSomBorderWidth(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_Border* border = ToNode(object_.Get())->GetOrCreateBorderIfPossible(); +@@ -1252,24 +1175,26 @@ void CJX_Object::ScriptSomBorderWidth(CFXJSE_Value* pValue, + CXFA_Edge* edge = border->GetEdgeIfExists(0); + CXFA_Measurement thickness = + edge ? edge->GetMSThickness() : CXFA_Measurement(0.5, XFA_Unit::Pt); +- pValue->SetString(thickness.ToString().ToUTF8().AsStringView()); ++ *pValue = fxv8::NewStringHelper( ++ pIsolate, thickness.ToString().ToUTF8().AsStringView()); + return; + } + + if (pValue->IsEmpty()) + return; + +- WideString wsThickness = pValue->ToWideString(); +- for (int32_t i = 0; i < border->CountEdges(); ++i) { ++ WideString wsThickness = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue); ++ for (size_t i = 0; i < border->CountEdges(); ++i) { + CXFA_Edge* edge = border->GetEdgeIfExists(i); + if (edge) + edge->SetMSThickness(CXFA_Measurement(wsThickness.AsStringView())); + } + } + +-void CJX_Object::ScriptSomMessage(CFXJSE_Value* pValue, ++void CJX_Object::ScriptSomMessage(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, +- XFA_SOM_MESSAGETYPE iMessageType) { ++ SOMMessageType iMessageType) { + bool bNew = false; + CXFA_Validate* validate = ToNode(object_.Get())->GetValidateIfExists(); + if (!validate) { +@@ -1280,16 +1205,17 @@ void CJX_Object::ScriptSomMessage(CFXJSE_Value* pValue, + if (bSetting) { + if (validate) { + switch (iMessageType) { +- case XFA_SOM_ValidationMessage: +- validate->SetScriptMessageText(pValue->ToWideString()); ++ case SOMMessageType::kValidationMessage: ++ validate->SetScriptMessageText( ++ fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); + break; +- case XFA_SOM_FormatMessage: +- validate->SetFormatMessageText(pValue->ToWideString()); ++ case SOMMessageType::kFormatMessage: ++ validate->SetFormatMessageText( ++ fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); + break; +- case XFA_SOM_MandatoryMessage: +- validate->SetNullMessageText(pValue->ToWideString()); +- break; +- default: ++ case SOMMessageType::kMandatoryMessage: ++ validate->SetNullMessageText( ++ fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); + break; + } + } +@@ -1299,98 +1225,100 @@ void CJX_Object::ScriptSomMessage(CFXJSE_Value* pValue, + if (!pNotify) + return; + +- pNotify->AddCalcValidate(ToNode(GetXFAObject())); ++ pNotify->AddCalcValidate(GetXFANode()); + } + return; + } + + if (!validate) { + // TODO(dsinclair): Better error message? +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } + + WideString wsMessage; + switch (iMessageType) { +- case XFA_SOM_ValidationMessage: ++ case SOMMessageType::kValidationMessage: + wsMessage = validate->GetScriptMessageText(); + break; +- case XFA_SOM_FormatMessage: ++ case SOMMessageType::kFormatMessage: + wsMessage = validate->GetFormatMessageText(); + break; +- case XFA_SOM_MandatoryMessage: ++ case SOMMessageType::kMandatoryMessage: + wsMessage = validate->GetNullMessageText(); + break; +- default: +- break; + } +- pValue->SetString(wsMessage.ToUTF8().AsStringView()); ++ *pValue = fxv8::NewStringHelper(pIsolate, wsMessage.ToUTF8().AsStringView()); + } + +-void CJX_Object::ScriptSomValidationMessage(CFXJSE_Value* pValue, ++void CJX_Object::ScriptSomValidationMessage(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- ScriptSomMessage(pValue, bSetting, XFA_SOM_ValidationMessage); ++ ScriptSomMessage(pIsolate, pValue, bSetting, ++ SOMMessageType::kValidationMessage); + } + +-void CJX_Object::ScriptSomMandatoryMessage(CFXJSE_Value* pValue, ++void CJX_Object::ScriptSomMandatoryMessage(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { +- ScriptSomMessage(pValue, bSetting, XFA_SOM_MandatoryMessage); ++ ScriptSomMessage(pIsolate, pValue, bSetting, ++ SOMMessageType::kMandatoryMessage); + } + +-void CJX_Object::ScriptSomDefaultValue(CFXJSE_Value* pValue, ++void CJX_Object::ScriptSomDefaultValue(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute /* unused */) { +- XFA_Element eType = ToNode(GetXFAObject())->GetElementType(); ++ XFA_Element eType = GetXFANode()->GetElementType(); + + // TODO(dsinclair): This should look through the properties on the node to see + // if defaultValue is defined and, if so, call that one. Just have to make + // sure that those defaultValue calls don't call back to this one .... + if (eType == XFA_Element::Field) { +- static_cast(this)->defaultValue(pValue, bSetting, ++ static_cast(this)->defaultValue(pIsolate, pValue, bSetting, + XFA_Attribute::Unknown); + return; + } + if (eType == XFA_Element::Draw) { +- static_cast(this)->defaultValue(pValue, bSetting, ++ static_cast(this)->defaultValue(pIsolate, pValue, bSetting, + XFA_Attribute::Unknown); + return; + } + if (eType == XFA_Element::Boolean) { +- static_cast(this)->defaultValue(pValue, bSetting, ++ static_cast(this)->defaultValue(pIsolate, pValue, bSetting, + XFA_Attribute::Unknown); + return; + } + + if (bSetting) { + WideString wsNewValue; +- if (pValue && +- !(pValue->IsEmpty() || pValue->IsNull() || pValue->IsUndefined())) { +- wsNewValue = pValue->ToWideString(); ++ if (pValue && !(pValue->IsEmpty() || fxv8::IsNull(*pValue) || ++ fxv8::IsUndefined(*pValue))) { ++ wsNewValue = fxv8::ReentrantToWideStringHelper(pIsolate, *pValue); + } + +- WideString wsFormatValue(wsNewValue); ++ WideString wsFormatValue = wsNewValue; + CXFA_Node* pContainerNode = nullptr; +- if (ToNode(GetXFAObject())->GetPacketType() == XFA_PacketType::Datasets) { ++ if (GetXFANode()->GetPacketType() == XFA_PacketType::Datasets) { + WideString wsPicture; +- for (auto* pFormNode : ToNode(GetXFAObject())->GetBindItemsCopy()) { ++ for (auto* pFormNode : GetXFANode()->GetBindItemsCopy()) { + if (!pFormNode || pFormNode->HasRemovedChildren()) + continue; + + pContainerNode = pFormNode->GetContainerNode(); + if (pContainerNode) { + wsPicture = +- pContainerNode->GetPictureContent(XFA_VALUEPICTURE_DataBind); ++ pContainerNode->GetPictureContent(XFA_ValuePicture::kDataBind); + } + if (!wsPicture.IsEmpty()) + break; + + pContainerNode = nullptr; + } +- } else if (ToNode(GetXFAObject())->GetPacketType() == +- XFA_PacketType::Form) { +- pContainerNode = ToNode(GetXFAObject())->GetContainerNode(); ++ } else if (GetXFANode()->GetPacketType() == XFA_PacketType::Form) { ++ pContainerNode = GetXFANode()->GetContainerNode(); + } + + if (pContainerNode) +@@ -1403,52 +1331,55 @@ void CJX_Object::ScriptSomDefaultValue(CFXJSE_Value* pValue, + WideString content = GetContent(true); + if (content.IsEmpty() && eType != XFA_Element::Text && + eType != XFA_Element::SubmitUrl) { +- pValue->SetNull(); ++ *pValue = fxv8::NewNullHelper(pIsolate); + } else if (eType == XFA_Element::Integer) { +- pValue->SetInteger(FXSYS_wtoi(content.c_str())); ++ *pValue = fxv8::NewNumberHelper(pIsolate, FXSYS_wtoi(content.c_str())); + } else if (eType == XFA_Element::Float || eType == XFA_Element::Decimal) { + CFGAS_Decimal decimal(content.AsStringView()); +- pValue->SetFloat(decimal.ToFloat()); ++ *pValue = fxv8::NewNumberHelper(pIsolate, decimal.ToFloat()); + } else { +- pValue->SetString(content.ToUTF8().AsStringView()); ++ *pValue = fxv8::NewStringHelper(pIsolate, content.ToUTF8().AsStringView()); + } + } + +-void CJX_Object::ScriptSomDefaultValue_Read(CFXJSE_Value* pValue, ++void CJX_Object::ScriptSomDefaultValue_Read(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } + + WideString content = GetContent(true); + if (content.IsEmpty()) { +- pValue->SetNull(); ++ *pValue = fxv8::NewNullHelper(pIsolate); + return; + } +- pValue->SetString(content.ToUTF8().AsStringView()); ++ *pValue = fxv8::NewStringHelper(pIsolate, content.ToUTF8().AsStringView()); + } + +-void CJX_Object::ScriptSomDataNode(CFXJSE_Value* pValue, ++void CJX_Object::ScriptSomDataNode(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } + +- CXFA_Node* pDataNode = ToNode(GetXFAObject())->GetBindData(); ++ CXFA_Node* pDataNode = GetXFANode()->GetBindData(); + if (!pDataNode) { +- pValue->SetNull(); ++ *pValue = fxv8::NewNullHelper(pIsolate); + return; + } + +- pValue->Assign(GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap( +- pDataNode)); ++ *pValue = ++ GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pDataNode); + } + +-void CJX_Object::ScriptSomMandatory(CFXJSE_Value* pValue, ++void CJX_Object::ScriptSomMandatory(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_Validate* validate = +@@ -1457,25 +1388,28 @@ void CJX_Object::ScriptSomMandatory(CFXJSE_Value* pValue, + return; + + if (bSetting) { +- validate->SetNullTest(pValue->ToWideString()); ++ validate->SetNullTest(fxv8::ReentrantToWideStringHelper(pIsolate, *pValue)); + return; + } + +- pValue->SetString(XFA_AttributeValueToName(validate->GetNullTest())); ++ *pValue = fxv8::NewStringHelper( ++ pIsolate, XFA_AttributeValueToName(validate->GetNullTest())); + } + +-void CJX_Object::ScriptSomInstanceIndex(CFXJSE_Value* pValue, ++void CJX_Object::ScriptSomInstanceIndex(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (!bSetting) { +- pValue->SetInteger(Subform_and_SubformSet_InstanceIndex()); ++ *pValue = ++ fxv8::NewNumberHelper(pIsolate, Subform_and_SubformSet_InstanceIndex()); + return; + } + +- int32_t iTo = pValue->ToInteger(); ++ int32_t iTo = fxv8::ReentrantToInt32Helper(pIsolate, *pValue); + int32_t iFrom = Subform_and_SubformSet_InstanceIndex(); + CXFA_Node* pManagerNode = nullptr; +- for (CXFA_Node* pNode = ToNode(GetXFAObject())->GetPrevSibling(); pNode; ++ for (CXFA_Node* pNode = GetXFANode()->GetPrevSibling(); pNode; + pNode = pNode->GetPrevSibling()) { + if (pNode->GetElementType() == XFA_Element::InstanceManager) { + pManagerNode = pNode; +@@ -1486,23 +1420,31 @@ void CJX_Object::ScriptSomInstanceIndex(CFXJSE_Value* pValue, + return; + + auto* mgr = static_cast(pManagerNode->JSObject()); +- mgr->MoveInstance(iTo, iFrom); ++ mgr->MoveInstance(pIsolate, iTo, iFrom); + CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); + if (!pNotify) + return; + +- CXFA_Node* pToInstance = pManagerNode->GetItemIfExists(iTo); +- if (pToInstance && pToInstance->GetElementType() == XFA_Element::Subform) { ++ auto* pToInstance = ++ CXFA_Subform::FromNode(pManagerNode->GetItemIfExists(iTo)); ++ if (pToInstance) + pNotify->RunSubformIndexChange(pToInstance); +- } + +- CXFA_Node* pFromInstance = pManagerNode->GetItemIfExists(iFrom); +- if (pFromInstance && +- pFromInstance->GetElementType() == XFA_Element::Subform) { ++ auto* pFromInstance = ++ CXFA_Subform::FromNode(pManagerNode->GetItemIfExists(iFrom)); ++ if (pFromInstance) + pNotify->RunSubformIndexChange(pFromInstance); +- } + } + +-void CJX_Object::ScriptSubmitFormatMode(CFXJSE_Value* pValue, ++void CJX_Object::ScriptSubmitFormatMode(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) {} ++ ++CJX_Object::CalcData::CalcData() = default; ++ ++CJX_Object::CalcData::~CalcData() = default; ++ ++void CJX_Object::CalcData::Trace(cppgc::Visitor* visitor) const { ++ ContainerTrace(visitor, m_Globals); ++} +diff --git a/fxjs/xfa/cjx_object.h b/fxjs/xfa/cjx_object.h +index 0633b5f9d..caa1a5e5b 100644 +--- a/fxjs/xfa/cjx_object.h ++++ b/fxjs/xfa/cjx_object.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,53 +9,41 @@ + + #include + #include +-#include + #include + +-#include "core/fxcrt/unowned_ptr.h" + #include "core/fxcrt/widestring.h" ++#include "fxjs/gc/heap.h" ++#include "fxjs/xfa/fxjse.h" + #include "fxjs/xfa/jse_define.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + #include "third_party/base/span.h" ++#include "v8/include/cppgc/garbage-collected.h" ++#include "v8/include/cppgc/member.h" ++#include "v8/include/v8-forward.h" + #include "xfa/fxfa/fxfa_basic.h" +-#include "xfa/fxfa/parser/cxfa_measurement.h" + ++class CFXJSE_Engine; ++class CFXJSE_MapModule; + class CFX_XMLElement; +-class CFXJSE_Value; +-class CFX_V8; + class CJX_Object; +-class CXFA_CalcData; + class CXFA_Document; + class CXFA_LayoutItem; ++class CXFA_Measurement; + class CXFA_Node; + class CXFA_Object; +-struct XFA_MAPMODULEDATA; + +-typedef CJS_Result (*CJX_MethodCall)( +- CJX_Object* obj, +- CFX_V8* runtime, +- const std::vector>& params); ++using CJX_MethodCall = ++ CJS_Result (*)(CJX_Object* obj, ++ CFXJSE_Engine* runtime, ++ const std::vector>& params); + + struct CJX_MethodSpec { + const char* pName; + CJX_MethodCall pMethodCall; + }; + +-typedef void (*PD_CALLBACK_FREEDATA)(void* pData); +-typedef void (*PD_CALLBACK_DUPLICATEDATA)(void*& pData); +- +-struct XFA_MAPDATABLOCKCALLBACKINFO { +- PD_CALLBACK_FREEDATA pFree; +- PD_CALLBACK_DUPLICATEDATA pCopy; +-}; +- +-enum XFA_SOM_MESSAGETYPE { +- XFA_SOM_ValidationMessage, +- XFA_SOM_FormatMessage, +- XFA_SOM_MandatoryMessage +-}; +- +-class CJX_Object { ++class CJX_Object : public cppgc::GarbageCollected, ++ public CFXJSE_HostObject { + public: + // Corresponds 1:1 with CJX_ subclasses. + enum class TypeTag { +@@ -96,51 +84,66 @@ class CJX_Object { + Xfa, + }; + +- explicit CJX_Object(CXFA_Object* obj); +- virtual ~CJX_Object(); ++ class CalcData : public cppgc::GarbageCollected { ++ public: ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; ++ ~CalcData(); ++ ++ void Trace(cppgc::Visitor* visitor) const; + ++ std::vector> m_Globals; ++ ++ private: ++ CalcData(); ++ }; ++ ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; ++ ~CJX_Object() override; ++ ++ // CFXJSE_HostObject: ++ CJX_Object* AsCJXObject() override; ++ ++ virtual void Trace(cppgc::Visitor* visitor) const; + virtual bool DynamicTypeIs(TypeTag eType) const; + + JSE_PROP(className); + + CXFA_Document* GetDocument() const; +- CXFA_Object* GetXFAObject() const { return object_.Get(); } ++ CXFA_Node* GetXFANode() const; ++ CXFA_Object* GetXFAObject() const { return object_; } + + void SetCalcRecursionCount(size_t count) { calc_recursion_count_ = count; } + size_t GetCalcRecursionCount() const { return calc_recursion_count_; } + +- void SetLayoutItem(CXFA_LayoutItem* item) { layout_item_.Reset(item); } +- CXFA_LayoutItem* GetLayoutItem() const { return layout_item_.Get(); } ++ void SetLayoutItem(CXFA_LayoutItem* item) { layout_item_ = item; } ++ CXFA_LayoutItem* GetLayoutItem() const { return layout_item_; } + + bool HasMethod(const WideString& func) const; + CJS_Result RunMethod(const WideString& func, + const std::vector>& params); + +- bool HasAttribute(XFA_Attribute eAttr); +- void SetAttribute(XFA_Attribute eAttr, WideStringView wsValue, bool bNotify); +- void SetAttribute(WideStringView wsAttr, +- WideStringView wsValue, +- bool bNotify); ++ bool HasAttribute(XFA_Attribute eAttr) const; ++ WideString GetAttributeByString(WideStringView attr) const; ++ WideString GetAttributeByEnum(XFA_Attribute attr) const; ++ absl::optional TryAttribute(XFA_Attribute eAttr, ++ bool bUseDefault) const; ++ void SetAttributeByEnum(XFA_Attribute eAttr, ++ const WideString& wsValue, ++ bool bNotify); ++ void SetAttributeByString(WideStringView wsAttr, const WideString& wsValue); + void RemoveAttribute(WideStringView wsAttr); +- WideString GetAttribute(WideStringView attr); +- WideString GetAttribute(XFA_Attribute attr); +- Optional TryAttribute(WideStringView wsAttr, bool bUseDefault); +- Optional TryAttribute(XFA_Attribute eAttr, bool bUseDefault); + +- Optional TryContent(bool bScriptModify, bool bProto); ++ WideString GetContent(bool bScriptModify) const; ++ absl::optional TryContent(bool bScriptModify, bool bProto) const; + void SetContent(const WideString& wsContent, + const WideString& wsXMLValue, + bool bNotify, + bool bScriptModify, + bool bSyncData); +- WideString GetContent(bool bScriptModify); + + template + T* GetProperty(int32_t index, XFA_Element eType) const { +- CXFA_Node* node; +- int32_t count; +- std::tie(node, count) = GetPropertyInternal(index, eType); +- return static_cast(node); ++ return static_cast(GetPropertyInternal(index, eType)); + } + template + T* GetOrCreateProperty(int32_t index, XFA_Element eType) { +@@ -148,9 +151,7 @@ class CJX_Object { + } + + void SetAttributeValue(const WideString& wsValue, +- const WideString& wsXMLValue, +- bool bNotify, +- bool bScriptModify); ++ const WideString& wsXMLValue); + + // Not actual properties, but invoked as property handlers to cover + // a broad range of underlying properties. +@@ -170,105 +171,113 @@ class CJX_Object { + JSE_PROP(ScriptSomInstanceIndex); + JSE_PROP(ScriptSubmitFormatMode); + +- void ScriptSomMessage(CFXJSE_Value* pValue, +- bool bSetting, +- XFA_SOM_MESSAGETYPE iMessageType); +- +- Optional TryNamespace(); ++ absl::optional TryNamespace() const; + +- Optional TryInteger(XFA_Attribute eAttr, bool bUseDefault) const; +- void SetInteger(XFA_Attribute eAttr, int32_t iValue, bool bNotify); + int32_t GetInteger(XFA_Attribute eAttr) const; ++ absl::optional TryInteger(XFA_Attribute eAttr, ++ bool bUseDefault) const; ++ void SetInteger(XFA_Attribute eAttr, int32_t iValue, bool bNotify); + +- Optional TryCData(XFA_Attribute eAttr, bool bUseDefault) const; +- void SetCData(XFA_Attribute eAttr, +- const WideString& wsValue, +- bool bNotify, +- bool bScriptModify); + WideString GetCData(XFA_Attribute eAttr) const; ++ absl::optional TryCData(XFA_Attribute eAttr, ++ bool bUseDefault) const; ++ void SetCData(XFA_Attribute eAttr, const WideString& wsValue); + +- Optional TryEnum(XFA_Attribute eAttr, +- bool bUseDefault) const; +- void SetEnum(XFA_Attribute eAttr, XFA_AttributeValue eValue, bool bNotify); + XFA_AttributeValue GetEnum(XFA_Attribute eAttr) const; ++ absl::optional TryEnum(XFA_Attribute eAttr, ++ bool bUseDefault) const; ++ void SetEnum(XFA_Attribute eAttr, XFA_AttributeValue eValue, bool bNotify); + +- Optional TryBoolean(XFA_Attribute eAttr, bool bUseDefault); ++ bool GetBoolean(XFA_Attribute eAttr) const; ++ absl::optional TryBoolean(XFA_Attribute eAttr, bool bUseDefault) const; + void SetBoolean(XFA_Attribute eAttr, bool bValue, bool bNotify); +- bool GetBoolean(XFA_Attribute eAttr); + +- Optional TryMeasure(XFA_Attribute eAttr, +- bool bUseDefault) const; +- Optional TryMeasureAsFloat(XFA_Attribute attr) const; +- void SetMeasure(XFA_Attribute eAttr, CXFA_Measurement mValue, bool bNotify); + CXFA_Measurement GetMeasure(XFA_Attribute eAttr) const; + float GetMeasureInUnit(XFA_Attribute eAttr, XFA_Unit unit) const; ++ absl::optional TryMeasure(XFA_Attribute eAttr, ++ bool bUseDefault) const; ++ absl::optional TryMeasureAsFloat(XFA_Attribute attr) const; ++ void SetMeasure(XFA_Attribute eAttr, ++ const CXFA_Measurement& mValue, ++ bool bNotify); ++ ++ void MergeAllData(CXFA_Object* pDstObj); ++ ++ CalcData* GetCalcData() const { return calc_data_; } ++ CalcData* GetOrCreateCalcData(cppgc::Heap* heap); ++ void TakeCalcDataFrom(CJX_Object* that); ++ ++ void ThrowInvalidPropertyException(v8::Isolate* pIsolate) const; ++ void ThrowArgumentMismatchException(v8::Isolate* pIsolate) const; ++ void ThrowIndexOutOfBoundsException(v8::Isolate* pIsolate) const; ++ void ThrowParamCountMismatchException(v8::Isolate* pIsolate, ++ const WideString& method) const; ++ void ThrowTooManyOccurrencesException(v8::Isolate* pIsolate, ++ const WideString& obj) const; + +- void MergeAllData(CXFA_Object* pDstModule); +- +- void SetCalcData(std::unique_ptr data); +- CXFA_CalcData* GetCalcData() const { return calc_data_.get(); } +- std::unique_ptr ReleaseCalcData(); +- +- int32_t InstanceManager_SetInstances(int32_t iDesired); +- int32_t InstanceManager_MoveInstance(int32_t iTo, int32_t iFrom); ++ protected: ++ enum class SOMMessageType { ++ kValidationMessage, ++ kFormatMessage, ++ kMandatoryMessage ++ }; + +- void ThrowInvalidPropertyException() const; +- void ThrowArgumentMismatchException() const; +- void ThrowIndexOutOfBoundsException() const; +- void ThrowParamCountMismatchException(const WideString& method) const; +- void ThrowTooManyOccurancesException(const WideString& obj) const; ++ explicit CJX_Object(CXFA_Object* obj); + +- protected: ++ void ScriptSomMessage(v8::Isolate* pIsolate, ++ v8::Local* pValue, ++ bool bSetting, ++ SOMMessageType iMessageType); ++ void SetAttributeValueImpl(const WideString& wsValue, ++ const WideString& wsXMLValue, ++ bool bNotify, ++ bool bScriptModify); ++ void SetCDataImpl(XFA_Attribute eAttr, ++ const WideString& wsValue, ++ bool bNotify, ++ bool bScriptModify); + void DefineMethods(pdfium::span methods); +- void MoveBufferMapData(CXFA_Object* pSrcModule, CXFA_Object* pDstModule); +- void SetMapModuleString(void* pKey, WideStringView wsValue); +- void ThrowException(const WideString& str) const; ++ void MoveBufferMapData(CXFA_Object* pSrcObj, CXFA_Object* pDstObj); ++ void ThrowException(v8::Isolate* pIsolate, const WideString& str) const; + + private: + using Type__ = CJX_Object; + static const TypeTag static_type__ = TypeTag::Object; + +- std::pair GetPropertyInternal(int32_t index, +- XFA_Element eType) const; ++ CXFA_Node* GetPropertyInternal(int32_t index, XFA_Element eType) const; + CXFA_Node* GetOrCreatePropertyInternal(int32_t index, XFA_Element eType); + +- void OnChanged(XFA_Attribute eAttr, bool bNotify, bool bScriptModify); +- void OnChanging(XFA_Attribute eAttr, bool bNotify); +- void SetUserData(void* pKey, +- void* pData, +- const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo); ++ void OnChanging(XFA_Attribute eAttr); ++ void OnChanged(XFA_Attribute eAttr, bool bScriptModify); + + // Returns a pointer to the XML node that needs to be updated with the new + // attribute value. |nullptr| if no update is needed. +- CFX_XMLElement* SetValue(XFA_Attribute eAttr, void* pValue, bool bNotify); ++ CFX_XMLElement* SetValue(XFA_Attribute eAttr, int32_t value, bool bNotify); + int32_t Subform_and_SubformSet_InstanceIndex(); + +- XFA_MAPMODULEDATA* CreateMapModuleData(); +- XFA_MAPMODULEDATA* GetMapModuleData() const; +- void SetMapModuleValue(void* pKey, void* pValue); +- Optional GetMapModuleValue(void* pKey) const; +- Optional GetMapModuleString(void* pKey) const; +- void SetMapModuleBuffer(void* pKey, +- void* pValue, +- int32_t iBytes, +- const XFA_MAPDATABLOCKCALLBACKINFO* pCallbackInfo); +- bool GetMapModuleBuffer(void* pKey, void** pValue, int32_t* pBytes) const; +- bool HasMapModuleKey(void* pKey); +- void ClearMapModuleBuffer(); +- void RemoveMapModuleKey(void* pKey); +- void MoveBufferMapData(CXFA_Object* pDstModule); +- +- UnownedPtr object_; +- UnownedPtr layout_item_; +- std::unique_ptr map_module_data_; +- std::unique_ptr calc_data_; ++ CFXJSE_MapModule* CreateMapModule(); ++ CFXJSE_MapModule* GetMapModule() const; ++ void SetMapModuleValue(uint32_t key, int32_t value); ++ void SetMapModuleString(uint32_t key, const WideString& wsValue); ++ void SetMapModuleMeasurement(uint32_t key, const CXFA_Measurement& value); ++ absl::optional GetMapModuleValue(uint32_t key) const; ++ absl::optional GetMapModuleString(uint32_t key) const; ++ absl::optional GetMapModuleMeasurement(uint32_t key) const; ++ absl::optional GetMapModuleValueFollowingChain(uint32_t key) const; ++ absl::optional GetMapModuleStringFollowingChain( ++ uint32_t key) const; ++ absl::optional GetMapModuleMeasurementFollowingChain( ++ uint32_t key) const; ++ bool HasMapModuleKey(uint32_t key) const; ++ void RemoveMapModuleKey(uint32_t key); ++ void MoveBufferMapData(CXFA_Object* pDstObj); ++ ++ cppgc::Member object_; ++ cppgc::Member layout_item_; ++ cppgc::Member calc_data_; ++ std::unique_ptr map_module_; + std::map method_specs_; + size_t calc_recursion_count_ = 0; + }; + +-typedef void (*XFA_ATTRIBUTE_CALLBACK)(CJX_Object* pNode, +- CFXJSE_Value* pValue, +- bool bSetting, +- XFA_Attribute eAttribute); +- + #endif // FXJS_XFA_CJX_OBJECT_H_ +diff --git a/fxjs/xfa/cjx_object_embeddertest.cpp b/fxjs/xfa/cjx_object_embeddertest.cpp +new file mode 100644 +index 000000000..b433665ff +--- /dev/null ++++ b/fxjs/xfa/cjx_object_embeddertest.cpp +@@ -0,0 +1,20 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "fxjs/xfa/cjx_object.h" ++ ++#include "testing/gtest/include/gtest/gtest.h" ++#include "testing/xfa_js_embedder_test.h" ++ ++class CJX_ObjectEmbedderTest : public XFAJSEmbedderTest {}; ++ ++// Should not crash, but document is not valid. ++TEST_F(CJX_ObjectEmbedderTest, Bug1327884) { ++ ASSERT_FALSE(OpenDocument("bug_1327884.pdf")); ++} ++ ++// Should not CHECK(), but document is uninteresting. ++TEST_F(CJX_ObjectEmbedderTest, Bug1333298) { ++ ASSERT_TRUE(OpenDocument("bug_1333298.pdf")); ++} +diff --git a/fxjs/xfa/cjx_occur.cpp b/fxjs/xfa/cjx_occur.cpp +index de8942dc6..657b23074 100644 +--- a/fxjs/xfa/cjx_occur.cpp ++++ b/fxjs/xfa/cjx_occur.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,9 +6,9 @@ + + #include "fxjs/xfa/cjx_occur.h" + +-#include +- ++#include "fxjs/fxv8.h" + #include "fxjs/xfa/cfxjse_value.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fxfa/parser/cxfa_occur.h" + + CJX_Occur::CJX_Occur(CXFA_Occur* node) : CJX_Node(node) {} +@@ -19,24 +19,27 @@ bool CJX_Occur::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-void CJX_Occur::max(CFXJSE_Value* pValue, ++void CJX_Occur::max(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_Occur* occur = static_cast(GetXFANode()); + if (!bSetting) { +- pValue->SetInteger(occur->GetMax()); ++ *pValue = fxv8::NewNumberHelper(pIsolate, occur->GetMax()); + return; + } +- occur->SetMax(pValue->ToInteger()); ++ occur->SetMax(fxv8::ReentrantToInt32Helper(pIsolate, *pValue)); + } + +-void CJX_Occur::min(CFXJSE_Value* pValue, ++// NOLINTNEXTLINE(build/include_what_you_use) ++void CJX_Occur::min(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CXFA_Occur* occur = static_cast(GetXFANode()); + if (!bSetting) { +- pValue->SetInteger(occur->GetMin()); ++ *pValue = fxv8::NewNumberHelper(pIsolate, occur->GetMin()); + return; + } +- occur->SetMin(pValue->ToInteger()); ++ occur->SetMin(fxv8::ReentrantToInt32Helper(pIsolate, *pValue)); + } +diff --git a/fxjs/xfa/cjx_occur.h b/fxjs/xfa/cjx_occur.h +index 8b912b601..06887ddb9 100644 +--- a/fxjs/xfa/cjx_occur.h ++++ b/fxjs/xfa/cjx_occur.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Occur; + + class CJX_Occur final : public CJX_Node { + public: +- explicit CJX_Occur(CXFA_Occur* node); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Occur() override; + + // CJX_Object: +@@ -24,6 +24,8 @@ class CJX_Occur final : public CJX_Node { + JSE_PROP(min); + + private: ++ explicit CJX_Occur(CXFA_Occur* node); ++ + using Type__ = CJX_Occur; + using ParentType__ = CJX_Node; + +diff --git a/fxjs/xfa/cjx_packet.cpp b/fxjs/xfa/cjx_packet.cpp +index 2c8b67df9..e77f165da 100644 +--- a/fxjs/xfa/cjx_packet.cpp ++++ b/fxjs/xfa/cjx_packet.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,10 +10,12 @@ + #include + + #include "core/fxcrt/xml/cfx_xmldocument.h" ++#include "core/fxcrt/xml/cfx_xmlelement.h" + #include "core/fxcrt/xml/cfx_xmltext.h" + #include "fxjs/cfx_v8.h" ++#include "fxjs/fxv8.h" + #include "fxjs/js_resources.h" +-#include "fxjs/xfa/cfxjse_value.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fxfa/cxfa_ffdoc.h" + #include "xfa/fxfa/cxfa_ffnotify.h" + #include "xfa/fxfa/parser/cxfa_packet.h" +@@ -27,14 +29,14 @@ CJX_Packet::CJX_Packet(CXFA_Packet* packet) : CJX_Node(packet) { + DefineMethods(MethodSpecs); + } + +-CJX_Packet::~CJX_Packet() {} ++CJX_Packet::~CJX_Packet() = default; + + bool CJX_Packet::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + + CJS_Result CJX_Packet::getAttribute( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -49,7 +51,7 @@ CJS_Result CJX_Packet::getAttribute( + } + + CJS_Result CJX_Packet::setAttribute( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 2) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -63,21 +65,20 @@ CJS_Result CJX_Packet::setAttribute( + } + + CJS_Result CJX_Packet::removeAttribute( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + + CFX_XMLElement* pElement = ToXMLElement(GetXFANode()->GetXMLMappingNode()); +- if (pElement) { +- WideString name = runtime->ToWideString(params[0]); +- if (pElement->HasAttribute(name)) +- pElement->RemoveAttribute(name); +- } ++ if (pElement) ++ pElement->RemoveAttribute(runtime->ToWideString(params[0])); ++ + return CJS_Result::Success(runtime->NewNull()); + } + +-void CJX_Packet::content(CFXJSE_Value* pValue, ++void CJX_Packet::content(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + CFX_XMLElement* element = ToXMLElement(GetXFANode()->GetXMLMappingNode()); +@@ -87,9 +88,10 @@ void CJX_Packet::content(CFXJSE_Value* pValue, + GetXFANode() + ->GetDocument() + ->GetNotify() +- ->GetHDOC() ++ ->GetFFDoc() + ->GetXMLDocument() +- ->CreateNode(pValue->ToWideString())); ++ ->CreateNode( ++ fxv8::ReentrantToWideStringHelper(pIsolate, *pValue))); + } + return; + } +@@ -98,5 +100,5 @@ void CJX_Packet::content(CFXJSE_Value* pValue, + if (element) + wsTextData = element->GetTextData(); + +- pValue->SetString(wsTextData.ToUTF8().AsStringView()); ++ *pValue = fxv8::NewStringHelper(pIsolate, wsTextData.ToUTF8().AsStringView()); + } +diff --git a/fxjs/xfa/cjx_packet.h b/fxjs/xfa/cjx_packet.h +index df4a987e2..4b4687c4b 100644 +--- a/fxjs/xfa/cjx_packet.h ++++ b/fxjs/xfa/cjx_packet.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Packet; + + class CJX_Packet final : public CJX_Node { + public: +- explicit CJX_Packet(CXFA_Packet* packet); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Packet() override; + + // CJX_Object: +@@ -27,6 +27,8 @@ class CJX_Packet final : public CJX_Node { + JSE_PROP(content); + + private: ++ explicit CJX_Packet(CXFA_Packet* packet); ++ + using Type__ = CJX_Packet; + using ParentType__ = CJX_Node; + +diff --git a/fxjs/xfa/cjx_script.cpp b/fxjs/xfa/cjx_script.cpp +index 482a57ea1..2c02b2bf6 100644 +--- a/fxjs/xfa/cjx_script.cpp ++++ b/fxjs/xfa/cjx_script.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,8 @@ + + #include "fxjs/xfa/cjx_script.h" + +-#include "fxjs/xfa/cfxjse_value.h" ++#include "fxjs/fxv8.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fxfa/parser/cxfa_script.h" + + CJX_Script::CJX_Script(CXFA_Script* node) : CJX_Node(node) {} +@@ -17,12 +18,13 @@ bool CJX_Script::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-void CJX_Script::stateless(CFXJSE_Value* pValue, ++void CJX_Script::stateless(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } +- pValue->SetString(FX_UTF8Encode(WideStringView(L"0", 1)).AsStringView()); ++ *pValue = fxv8::NewStringHelper(pIsolate, "0"); + } +diff --git a/fxjs/xfa/cjx_script.h b/fxjs/xfa/cjx_script.h +index af7c9858a..272ec2a3e 100644 +--- a/fxjs/xfa/cjx_script.h ++++ b/fxjs/xfa/cjx_script.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Script; + + class CJX_Script final : public CJX_Node { + public: +- explicit CJX_Script(CXFA_Script* node); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Script() override; + + // CJX_Object: +@@ -23,6 +23,8 @@ class CJX_Script final : public CJX_Node { + JSE_PROP(stateless); + + private: ++ explicit CJX_Script(CXFA_Script* node); ++ + using Type__ = CJX_Script; + using ParentType__ = CJX_Node; + +diff --git a/fxjs/xfa/cjx_signaturepseudomodel.cpp b/fxjs/xfa/cjx_signaturepseudomodel.cpp +index 97ecd6e38..a740cb811 100644 +--- a/fxjs/xfa/cjx_signaturepseudomodel.cpp ++++ b/fxjs/xfa/cjx_signaturepseudomodel.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,9 +8,10 @@ + + #include + +-#include "fxjs/cfx_v8.h" + #include "fxjs/js_resources.h" ++#include "fxjs/xfa/cfxjse_engine.h" + #include "fxjs/xfa/cfxjse_value.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fxfa/parser/cscript_signaturepseudomodel.h" + + const CJX_MethodSpec CJX_SignaturePseudoModel::MethodSpecs[] = { +@@ -25,14 +26,14 @@ CJX_SignaturePseudoModel::CJX_SignaturePseudoModel( + DefineMethods(MethodSpecs); + } + +-CJX_SignaturePseudoModel::~CJX_SignaturePseudoModel() {} ++CJX_SignaturePseudoModel::~CJX_SignaturePseudoModel() = default; + + bool CJX_SignaturePseudoModel::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + + CJS_Result CJX_SignaturePseudoModel::verifySignature( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.empty() || params.size() > 4) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -41,7 +42,7 @@ CJS_Result CJX_SignaturePseudoModel::verifySignature( + } + + CJS_Result CJX_SignaturePseudoModel::sign( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() < 3 || params.size() > 7) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -50,7 +51,7 @@ CJS_Result CJX_SignaturePseudoModel::sign( + } + + CJS_Result CJX_SignaturePseudoModel::enumerate( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -59,7 +60,7 @@ CJS_Result CJX_SignaturePseudoModel::enumerate( + } + + CJS_Result CJX_SignaturePseudoModel::clear( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.empty() || params.size() > 2) + return CJS_Result::Failure(JSMessage::kParamError); +diff --git a/fxjs/xfa/cjx_signaturepseudomodel.h b/fxjs/xfa/cjx_signaturepseudomodel.h +index ed7723852..6cf3460b9 100644 +--- a/fxjs/xfa/cjx_signaturepseudomodel.h ++++ b/fxjs/xfa/cjx_signaturepseudomodel.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CScript_SignaturePseudoModel; + + class CJX_SignaturePseudoModel final : public CJX_Object { + public: +- explicit CJX_SignaturePseudoModel(CScript_SignaturePseudoModel* model); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_SignaturePseudoModel() override; + + // CJX_Object: +@@ -26,6 +26,8 @@ class CJX_SignaturePseudoModel final : public CJX_Object { + JSE_METHOD(clear); + + private: ++ explicit CJX_SignaturePseudoModel(CScript_SignaturePseudoModel* model); ++ + using Type__ = CJX_SignaturePseudoModel; + using ParentType__ = CJX_Object; + +diff --git a/fxjs/xfa/cjx_source.cpp b/fxjs/xfa/cjx_source.cpp +index f6d3187e4..33a11ff18 100644 +--- a/fxjs/xfa/cjx_source.cpp ++++ b/fxjs/xfa/cjx_source.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -35,13 +35,13 @@ CJX_Source::CJX_Source(CXFA_Source* src) : CJX_Node(src) { + DefineMethods(MethodSpecs); + } + +-CJX_Source::~CJX_Source() {} ++CJX_Source::~CJX_Source() = default; + + bool CJX_Source::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-CJS_Result CJX_Source::next(CFX_V8* runtime, ++CJS_Result CJX_Source::next(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -50,7 +50,7 @@ CJS_Result CJX_Source::next(CFX_V8* runtime, + } + + CJS_Result CJX_Source::cancelBatch( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -58,7 +58,7 @@ CJS_Result CJX_Source::cancelBatch( + return CJS_Result::Success(); + } + +-CJS_Result CJX_Source::first(CFX_V8* runtime, ++CJS_Result CJX_Source::first(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -67,7 +67,7 @@ CJS_Result CJX_Source::first(CFX_V8* runtime, + } + + CJS_Result CJX_Source::updateBatch( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -76,7 +76,7 @@ CJS_Result CJX_Source::updateBatch( + } + + CJS_Result CJX_Source::previous( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -84,7 +84,7 @@ CJS_Result CJX_Source::previous( + return CJS_Result::Success(); + } + +-CJS_Result CJX_Source::isBOF(CFX_V8* runtime, ++CJS_Result CJX_Source::isBOF(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -92,7 +92,7 @@ CJS_Result CJX_Source::isBOF(CFX_V8* runtime, + return CJS_Result::Success(); + } + +-CJS_Result CJX_Source::isEOF(CFX_V8* runtime, ++CJS_Result CJX_Source::isEOF(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -100,7 +100,7 @@ CJS_Result CJX_Source::isEOF(CFX_V8* runtime, + return CJS_Result::Success(); + } + +-CJS_Result CJX_Source::cancel(CFX_V8* runtime, ++CJS_Result CJX_Source::cancel(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -108,7 +108,7 @@ CJS_Result CJX_Source::cancel(CFX_V8* runtime, + return CJS_Result::Success(); + } + +-CJS_Result CJX_Source::update(CFX_V8* runtime, ++CJS_Result CJX_Source::update(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -116,7 +116,7 @@ CJS_Result CJX_Source::update(CFX_V8* runtime, + return CJS_Result::Success(); + } + +-CJS_Result CJX_Source::open(CFX_V8* runtime, ++CJS_Result CJX_Source::open(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -125,7 +125,7 @@ CJS_Result CJX_Source::open(CFX_V8* runtime, + } + + CJS_Result CJX_Source::deleteItem( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -133,7 +133,7 @@ CJS_Result CJX_Source::deleteItem( + return CJS_Result::Success(); + } + +-CJS_Result CJX_Source::addNew(CFX_V8* runtime, ++CJS_Result CJX_Source::addNew(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -142,7 +142,7 @@ CJS_Result CJX_Source::addNew(CFX_V8* runtime, + } + + CJS_Result CJX_Source::requery( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -150,7 +150,7 @@ CJS_Result CJX_Source::requery( + return CJS_Result::Success(); + } + +-CJS_Result CJX_Source::resync(CFX_V8* runtime, ++CJS_Result CJX_Source::resync(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -158,7 +158,7 @@ CJS_Result CJX_Source::resync(CFX_V8* runtime, + return CJS_Result::Success(); + } + +-CJS_Result CJX_Source::close(CFX_V8* runtime, ++CJS_Result CJX_Source::close(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -166,7 +166,7 @@ CJS_Result CJX_Source::close(CFX_V8* runtime, + return CJS_Result::Success(); + } + +-CJS_Result CJX_Source::last(CFX_V8* runtime, ++CJS_Result CJX_Source::last(CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -175,7 +175,7 @@ CJS_Result CJX_Source::last(CFX_V8* runtime, + } + + CJS_Result CJX_Source::hasDataChanged( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -183,6 +183,7 @@ CJS_Result CJX_Source::hasDataChanged( + return CJS_Result::Success(); + } + +-void CJX_Source::db(CFXJSE_Value* pValue, ++void CJX_Source::db(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) {} +diff --git a/fxjs/xfa/cjx_source.h b/fxjs/xfa/cjx_source.h +index cd70601fe..a3e7fdab7 100644 +--- a/fxjs/xfa/cjx_source.h ++++ b/fxjs/xfa/cjx_source.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Source; + + class CJX_Source final : public CJX_Node { + public: +- explicit CJX_Source(CXFA_Source* src); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Source() override; + + // CJX_Object: +@@ -41,6 +41,8 @@ class CJX_Source final : public CJX_Node { + JSE_PROP(db); + + private: ++ explicit CJX_Source(CXFA_Source* src); ++ + using Type__ = CJX_Source; + using ParentType__ = CJX_Node; + +diff --git a/fxjs/xfa/cjx_subform.cpp b/fxjs/xfa/cjx_subform.cpp +index 8a72b5b57..5a4efc004 100644 +--- a/fxjs/xfa/cjx_subform.cpp ++++ b/fxjs/xfa/cjx_subform.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,9 +9,10 @@ + #include + + #include "fxjs/cfx_v8.h" ++#include "fxjs/fxv8.h" + #include "fxjs/js_resources.h" + #include "fxjs/xfa/cfxjse_engine.h" +-#include "fxjs/xfa/cfxjse_value.h" ++#include "v8/include/v8-object.h" + #include "xfa/fxfa/cxfa_eventparam.h" + #include "xfa/fxfa/cxfa_ffnotify.h" + #include "xfa/fxfa/fxfa.h" +@@ -28,14 +29,14 @@ CJX_Subform::CJX_Subform(CXFA_Node* node) : CJX_Container(node) { + DefineMethods(MethodSpecs); + } + +-CJX_Subform::~CJX_Subform() {} ++CJX_Subform::~CJX_Subform() = default; + + bool CJX_Subform::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + + CJS_Result CJX_Subform::execEvent( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -46,7 +47,7 @@ CJS_Result CJX_Subform::execEvent( + } + + CJS_Result CJX_Subform::execInitialize( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -59,7 +60,7 @@ CJS_Result CJX_Subform::execInitialize( + } + + CJS_Result CJX_Subform::execCalculate( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -72,7 +73,7 @@ CJS_Result CJX_Subform::execCalculate( + } + + CJS_Result CJX_Subform::execValidate( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -87,29 +88,34 @@ CJS_Result CJX_Subform::execValidate( + runtime->NewBoolean(iRet != XFA_EventError::kError)); + } + +-void CJX_Subform::locale(CFXJSE_Value* pValue, ++void CJX_Subform::locale(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- SetCData(XFA_Attribute::Locale, pValue->ToWideString(), true, true); ++ SetCDataImpl(XFA_Attribute::Locale, ++ fxv8::ReentrantToWideStringHelper(pIsolate, *pValue), true, ++ true); + return; + } + + WideString wsLocaleName = GetXFANode()->GetLocaleName().value_or(L""); +- pValue->SetString(wsLocaleName.ToUTF8().AsStringView()); ++ *pValue = ++ fxv8::NewStringHelper(pIsolate, wsLocaleName.ToUTF8().AsStringView()); + } + +-void CJX_Subform::instanceManager(CFXJSE_Value* pValue, ++void CJX_Subform::instanceManager(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } + + WideString wsName = GetCData(XFA_Attribute::Name); + CXFA_Node* pInstanceMgr = nullptr; +- for (CXFA_Node* pNode = ToNode(GetXFAObject())->GetPrevSibling(); pNode; ++ for (CXFA_Node* pNode = GetXFANode()->GetPrevSibling(); pNode; + pNode = pNode->GetPrevSibling()) { + if (pNode->GetElementType() == XFA_Element::InstanceManager) { + WideString wsInstMgrName = +@@ -121,11 +127,9 @@ void CJX_Subform::instanceManager(CFXJSE_Value* pValue, + break; + } + } +- if (!pInstanceMgr) { +- pValue->SetNull(); +- return; +- } +- +- pValue->Assign(GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap( +- pInstanceMgr)); ++ *pValue = pInstanceMgr ? GetDocument() ++ ->GetScriptContext() ++ ->GetOrCreateJSBindingFromMap(pInstanceMgr) ++ .As() ++ : fxv8::NewNullHelper(pIsolate).As(); + } +diff --git a/fxjs/xfa/cjx_subform.h b/fxjs/xfa/cjx_subform.h +index 83ac66e68..c33712ffe 100644 +--- a/fxjs/xfa/cjx_subform.h ++++ b/fxjs/xfa/cjx_subform.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,11 +10,11 @@ + #include "fxjs/xfa/cjx_container.h" + #include "fxjs/xfa/jse_define.h" + +-class CXFA_Delta; ++class CXFA_Node; + + class CJX_Subform final : public CJX_Container { + public: +- explicit CJX_Subform(CXFA_Node* container); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Subform() override; + + // CJX_Object: +@@ -29,6 +29,8 @@ class CJX_Subform final : public CJX_Container { + JSE_PROP(locale); + + private: ++ explicit CJX_Subform(CXFA_Node* container); ++ + using Type__ = CJX_Subform; + using ParentType__ = CJX_Container; + +diff --git a/fxjs/xfa/cjx_template.cpp b/fxjs/xfa/cjx_template.cpp +index b3d9d9046..e3779e310 100644 +--- a/fxjs/xfa/cjx_template.cpp ++++ b/fxjs/xfa/cjx_template.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,6 +11,7 @@ + #include "fxjs/cfx_v8.h" + #include "fxjs/js_resources.h" + #include "fxjs/xfa/cfxjse_value.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fxfa/parser/cxfa_document.h" + #include "xfa/fxfa/parser/cxfa_template.h" + +@@ -26,14 +27,14 @@ CJX_Template::CJX_Template(CXFA_Template* tmpl) : CJX_Model(tmpl) { + DefineMethods(MethodSpecs); + } + +-CJX_Template::~CJX_Template() {} ++CJX_Template::~CJX_Template() = default; + + bool CJX_Template::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + + CJS_Result CJX_Template::formNodes( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -42,17 +43,17 @@ CJS_Result CJX_Template::formNodes( + } + + CJS_Result CJX_Template::remerge( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); + +- GetDocument()->DoDataRemerge(true); ++ GetDocument()->DoDataRemerge(); + return CJS_Result::Success(); + } + + CJS_Result CJX_Template::execInitialize( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -62,7 +63,7 @@ CJS_Result CJX_Template::execInitialize( + } + + CJS_Result CJX_Template::recalculate( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -71,7 +72,7 @@ CJS_Result CJX_Template::recalculate( + } + + CJS_Result CJX_Template::execCalculate( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -81,7 +82,7 @@ CJS_Result CJX_Template::execCalculate( + } + + CJS_Result CJX_Template::execValidate( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty()) + return CJS_Result::Failure(JSMessage::kParamError); +diff --git a/fxjs/xfa/cjx_template.h b/fxjs/xfa/cjx_template.h +index d39645947..caf1b7445 100644 +--- a/fxjs/xfa/cjx_template.h ++++ b/fxjs/xfa/cjx_template.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Template; + + class CJX_Template final : public CJX_Model { + public: +- explicit CJX_Template(CXFA_Template* tmpl); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Template() override; + + // CJX_Object: +@@ -31,6 +31,8 @@ class CJX_Template final : public CJX_Model { + JSE_METHOD(remerge); + + private: ++ explicit CJX_Template(CXFA_Template* tmpl); ++ + using Type__ = CJX_Template; + using ParentType__ = CJX_Model; + +diff --git a/fxjs/xfa/cjx_textnode.cpp b/fxjs/xfa/cjx_textnode.cpp +index 756a71dcd..2bdb41450 100644 +--- a/fxjs/xfa/cjx_textnode.cpp ++++ b/fxjs/xfa/cjx_textnode.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,20 +11,22 @@ + + CJX_TextNode::CJX_TextNode(CXFA_Node* node) : CJX_Node(node) {} + +-CJX_TextNode::~CJX_TextNode() {} ++CJX_TextNode::~CJX_TextNode() = default; + + bool CJX_TextNode::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-void CJX_TextNode::defaultValue(CFXJSE_Value* pValue, ++void CJX_TextNode::defaultValue(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute attr) { +- ScriptSomDefaultValue(pValue, bSetting, attr); ++ ScriptSomDefaultValue(pIsolate, pValue, bSetting, attr); + } + +-void CJX_TextNode::value(CFXJSE_Value* pValue, ++void CJX_TextNode::value(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute attr) { +- ScriptSomDefaultValue(pValue, bSetting, attr); ++ ScriptSomDefaultValue(pIsolate, pValue, bSetting, attr); + } +diff --git a/fxjs/xfa/cjx_textnode.h b/fxjs/xfa/cjx_textnode.h +index 6d7465882..dbef71369 100644 +--- a/fxjs/xfa/cjx_textnode.h ++++ b/fxjs/xfa/cjx_textnode.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Node; + + class CJX_TextNode : public CJX_Node { + public: +- explicit CJX_TextNode(CXFA_Node* node); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_TextNode() override; + + // CJX_Object: +@@ -23,6 +23,9 @@ class CJX_TextNode : public CJX_Node { + JSE_PROP(defaultValue); /* {default} */ + JSE_PROP(value); + ++ protected: ++ explicit CJX_TextNode(CXFA_Node* node); ++ + private: + using Type__ = CJX_TextNode; + using ParentType__ = CJX_Node; +diff --git a/fxjs/xfa/cjx_tree.cpp b/fxjs/xfa/cjx_tree.cpp +index cc13b7aad..ec562cedd 100644 +--- a/fxjs/xfa/cjx_tree.cpp ++++ b/fxjs/xfa/cjx_tree.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,16 +8,19 @@ + + #include + ++#include "fxjs/fxv8.h" + #include "fxjs/js_resources.h" ++#include "fxjs/xfa/cfxjse_class.h" + #include "fxjs/xfa/cfxjse_engine.h" +-#include "fxjs/xfa/cfxjse_value.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/numerics/safe_conversions.h" ++#include "v8/include/cppgc/allocation.h" ++#include "v8/include/v8-object.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fxfa/parser/cxfa_arraynodelist.h" + #include "xfa/fxfa/parser/cxfa_attachnodelist.h" + #include "xfa/fxfa/parser/cxfa_document.h" + #include "xfa/fxfa/parser/cxfa_node.h" + #include "xfa/fxfa/parser/cxfa_object.h" +-#include "xfa/fxfa/parser/xfa_resolvenode_rs.h" + + const CJX_MethodSpec CJX_Tree::MethodSpecs[] = { + {"resolveNode", resolveNode_static}, +@@ -27,58 +30,56 @@ CJX_Tree::CJX_Tree(CXFA_Object* obj) : CJX_Object(obj) { + DefineMethods(MethodSpecs); + } + +-CJX_Tree::~CJX_Tree() {} ++CJX_Tree::~CJX_Tree() = default; + + bool CJX_Tree::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + + CJS_Result CJX_Tree::resolveNode( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); + +- WideString expression = runtime->ToWideString(params[0]); ++ WideString wsExpression = runtime->ToWideString(params[0]); + CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext(); +- CXFA_Object* refNode = GetXFAObject(); +- if (refNode->GetElementType() == XFA_Element::Xfa) +- refNode = pScriptContext->GetThisObject(); +- +- uint32_t dwFlag = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes | +- XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent | +- XFA_RESOLVENODE_Siblings; +- XFA_RESOLVENODE_RS resolveNodeRS; +- if (!pScriptContext->ResolveObjects(ToNode(refNode), +- expression.AsStringView(), &resolveNodeRS, +- dwFlag, nullptr)) { ++ CXFA_Object* pRefNode = GetXFAObject(); ++ if (pRefNode->GetElementType() == XFA_Element::Xfa) ++ pRefNode = pScriptContext->GetThisObject(); ++ ++ absl::optional maybeResult = ++ pScriptContext->ResolveObjects( ++ ToNode(pRefNode), wsExpression.AsStringView(), ++ Mask{ ++ XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes, ++ XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kParent, ++ XFA_ResolveFlag::kSiblings}); ++ if (!maybeResult.has_value()) + return CJS_Result::Success(runtime->NewNull()); +- } +- +- if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) { +- CXFA_Object* pObject = resolveNodeRS.objects.front().Get(); +- CFXJSE_Value* value = +- GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pObject); + ++ if (maybeResult.value().type == CFXJSE_Engine::ResolveResult::Type::kNodes) { + return CJS_Result::Success( +- value->DirectGetValue().Get(runtime->GetIsolate())); ++ GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap( ++ maybeResult.value().objects.front().Get())); + } + +- if (!resolveNodeRS.script_attribute.callback || +- resolveNodeRS.script_attribute.eValueType != XFA_ScriptType::Object) { ++ if (!maybeResult.value().script_attribute.callback || ++ maybeResult.value().script_attribute.eValueType != ++ XFA_ScriptType::Object) { + return CJS_Result::Success(runtime->NewNull()); + } + +- auto pValue = pdfium::MakeUnique(pScriptContext->GetIsolate()); +- CJX_Object* jsObject = resolveNodeRS.objects.front()->JSObject(); +- (*resolveNodeRS.script_attribute.callback)( +- jsObject, pValue.get(), false, resolveNodeRS.script_attribute.attribute); +- return CJS_Result::Success( +- pValue->DirectGetValue().Get(runtime->GetIsolate())); ++ v8::Local pValue; ++ CJX_Object* jsObject = maybeResult.value().objects.front()->JSObject(); ++ (*maybeResult.value().script_attribute.callback)( ++ runtime->GetIsolate(), jsObject, &pValue, false, ++ maybeResult.value().script_attribute.attribute); ++ return CJS_Result::Success(pValue); + } + + CJS_Result CJX_Tree::resolveNodes( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -88,146 +89,166 @@ CJS_Result CJX_Tree::resolveNodes( + refNode = GetDocument()->GetScriptContext()->GetThisObject(); + + CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext(); +- auto pValue = pdfium::MakeUnique(pScriptContext->GetIsolate()); +- ResolveNodeList(pValue.get(), runtime->ToWideString(params[0]), +- XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes | +- XFA_RESOLVENODE_Properties | XFA_RESOLVENODE_Parent | +- XFA_RESOLVENODE_Siblings, +- ToNode(refNode)); +- return CJS_Result::Success( +- pValue->DirectGetValue().Get(runtime->GetIsolate())); ++ const Mask kFlags = { ++ XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes, ++ XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kParent, ++ XFA_ResolveFlag::kSiblings}; ++ return CJS_Result::Success(ResolveNodeList(pScriptContext->GetIsolate(), ++ runtime->ToWideString(params[0]), ++ kFlags, ToNode(refNode))); + } + +-void CJX_Tree::all(CFXJSE_Value* pValue, ++void CJX_Tree::all(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } +- +- uint32_t dwFlag = XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_ALL; +- WideString wsExpression = GetAttribute(XFA_Attribute::Name) + L"[*]"; +- ResolveNodeList(pValue, wsExpression, dwFlag, nullptr); ++ const Mask kFlags = {XFA_ResolveFlag::kSiblings, ++ XFA_ResolveFlag::kALL}; ++ WideString wsExpression = GetAttributeByEnum(XFA_Attribute::Name) + L"[*]"; ++ *pValue = ResolveNodeList(pIsolate, wsExpression, kFlags, nullptr); + } + +-void CJX_Tree::classAll(CFXJSE_Value* pValue, ++void CJX_Tree::classAll(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } +- ++ const Mask kFlags = {XFA_ResolveFlag::kSiblings, ++ XFA_ResolveFlag::kALL}; + WideString wsExpression = + L"#" + WideString::FromASCII(GetXFAObject()->GetClassName()) + L"[*]"; +- ResolveNodeList(pValue, wsExpression, +- XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_ALL, nullptr); ++ *pValue = ResolveNodeList(pIsolate, wsExpression, kFlags, nullptr); + } + +-void CJX_Tree::nodes(CFXJSE_Value* pValue, ++void CJX_Tree::nodes(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { + WideString wsMessage = L"Unable to set "; +- FXJSE_ThrowMessage(wsMessage.ToUTF8().AsStringView()); ++ FXJSE_ThrowMessage(pIsolate, wsMessage.ToUTF8().AsStringView()); + return; + } + +- CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext(); +- CXFA_AttachNodeList* pNodeList = +- new CXFA_AttachNodeList(GetDocument(), ToNode(GetXFAObject())); +- pValue->SetHostObject(pNodeList, pScriptContext->GetJseNormalClass()); ++ CXFA_Document* pDoc = GetDocument(); ++ auto* pNodeList = cppgc::MakeGarbageCollected( ++ pDoc->GetHeap()->GetAllocationHandle(), pDoc, GetXFANode()); ++ pDoc->GetNodeOwner()->PersistList(pNodeList); ++ ++ CFXJSE_Engine* pEngine = pDoc->GetScriptContext(); ++ *pValue = pNodeList->JSObject()->NewBoundV8Object( ++ pIsolate, pEngine->GetJseNormalClass()->GetTemplate(pIsolate)); + } + +-void CJX_Tree::parent(CFXJSE_Value* pValue, ++void CJX_Tree::parent(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } + +- CXFA_Node* pParent = ToNode(GetXFAObject())->GetParent(); +- if (!pParent) { +- pValue->SetNull(); +- return; +- } +- +- pValue->Assign( +- GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pParent)); ++ CXFA_Node* pParent = GetXFANode()->GetParent(); ++ *pValue = pParent ? GetDocument() ++ ->GetScriptContext() ++ ->GetOrCreateJSBindingFromMap(pParent) ++ .As() ++ : fxv8::NewNullHelper(pIsolate).As(); + } + +-void CJX_Tree::index(CFXJSE_Value* pValue, ++void CJX_Tree::index(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } + +- CXFA_Node* pNode = ToNode(GetXFAObject()); ++ CXFA_Node* pNode = GetXFANode(); + size_t iIndex = pNode ? pNode->GetIndexByName() : 0; +- pValue->SetInteger(pdfium::base::checked_cast(iIndex)); ++ *pValue = fxv8::NewNumberHelper(pIsolate, ++ pdfium::base::checked_cast(iIndex)); + } + +-void CJX_Tree::classIndex(CFXJSE_Value* pValue, ++void CJX_Tree::classIndex(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } + +- CXFA_Node* pNode = ToNode(GetXFAObject()); ++ CXFA_Node* pNode = GetXFANode(); + size_t iIndex = pNode ? pNode->GetIndexByClassName() : 0; +- pValue->SetInteger(pdfium::base::checked_cast(iIndex)); ++ *pValue = fxv8::NewNumberHelper(pIsolate, ++ pdfium::base::checked_cast(iIndex)); + } + +-void CJX_Tree::somExpression(CFXJSE_Value* pValue, ++void CJX_Tree::somExpression(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) { +- ThrowInvalidPropertyException(); ++ ThrowInvalidPropertyException(pIsolate); + return; + } + +- WideString wsSOMExpression = GetXFAObject()->GetSOMExpression(); +- pValue->SetString(wsSOMExpression.ToUTF8().AsStringView()); ++ ByteString bsSOMExpression = GetXFAObject()->GetSOMExpression().ToUTF8(); ++ *pValue = fxv8::NewStringHelper(pIsolate, bsSOMExpression.AsStringView()); + } + +-void CJX_Tree::ResolveNodeList(CFXJSE_Value* pValue, +- WideString wsExpression, +- uint32_t dwFlag, +- CXFA_Node* refNode) { ++v8::Local CJX_Tree::ResolveNodeList(v8::Isolate* pIsolate, ++ WideString wsExpression, ++ Mask dwFlag, ++ CXFA_Node* refNode) { + if (!refNode) +- refNode = ToNode(GetXFAObject()); +- +- XFA_RESOLVENODE_RS resolveNodeRS; +- CFXJSE_Engine* pScriptContext = GetDocument()->GetScriptContext(); +- pScriptContext->ResolveObjects(refNode, wsExpression.AsStringView(), +- &resolveNodeRS, dwFlag, nullptr); +- CXFA_ArrayNodeList* pNodeList = new CXFA_ArrayNodeList(GetDocument()); +- if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) { +- for (auto& pObject : resolveNodeRS.objects) { +- if (pObject->IsNode()) +- pNodeList->Append(pObject->AsNode()); +- } +- } else { +- if (resolveNodeRS.script_attribute.callback && +- resolveNodeRS.script_attribute.eValueType == XFA_ScriptType::Object) { +- for (auto& pObject : resolveNodeRS.objects) { +- auto innerValue = +- pdfium::MakeUnique(pScriptContext->GetIsolate()); +- CJX_Object* jsObject = pObject->JSObject(); +- (*resolveNodeRS.script_attribute.callback)( +- jsObject, innerValue.get(), false, +- resolveNodeRS.script_attribute.attribute); +- CXFA_Object* obj = CFXJSE_Engine::ToObject(innerValue.get()); +- if (obj->IsNode()) +- pNodeList->Append(obj->AsNode()); ++ refNode = GetXFANode(); ++ ++ CXFA_Document* pDoc = GetDocument(); ++ auto* pNodeList = cppgc::MakeGarbageCollected( ++ pDoc->GetHeap()->GetAllocationHandle(), pDoc); ++ pDoc->GetNodeOwner()->PersistList(pNodeList); ++ ++ CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext(); ++ absl::optional maybeResult = ++ pScriptContext->ResolveObjects(refNode, wsExpression.AsStringView(), ++ dwFlag); ++ ++ if (maybeResult.has_value()) { ++ if (maybeResult.value().type == ++ CFXJSE_Engine::ResolveResult::Type::kNodes) { ++ for (auto& pObject : maybeResult.value().objects) { ++ if (pObject->IsNode()) ++ pNodeList->Append(pObject->AsNode()); ++ } ++ } else { ++ if (maybeResult.value().script_attribute.callback && ++ maybeResult.value().script_attribute.eValueType == ++ XFA_ScriptType::Object) { ++ for (auto& pObject : maybeResult.value().objects) { ++ v8::Local innerValue; ++ CJX_Object* jsObject = pObject->JSObject(); ++ (*maybeResult.value().script_attribute.callback)( ++ pIsolate, jsObject, &innerValue, false, ++ maybeResult.value().script_attribute.attribute); ++ CXFA_Object* obj = ++ CFXJSE_Engine::ToObject(pScriptContext->GetIsolate(), innerValue); ++ if (obj->IsNode()) ++ pNodeList->Append(obj->AsNode()); ++ } + } + } + } +- pValue->SetHostObject(pNodeList, pScriptContext->GetJseNormalClass()); ++ return pNodeList->JSObject()->NewBoundV8Object( ++ pIsolate, pScriptContext->GetJseNormalClass()->GetTemplate(pIsolate)); + } +diff --git a/fxjs/xfa/cjx_tree.h b/fxjs/xfa/cjx_tree.h +index cbef88844..42c522e0c 100644 +--- a/fxjs/xfa/cjx_tree.h ++++ b/fxjs/xfa/cjx_tree.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,6 +7,7 @@ + #ifndef FXJS_XFA_CJX_TREE_H_ + #define FXJS_XFA_CJX_TREE_H_ + ++#include "fxjs/xfa/cfxjse_engine.h" + #include "fxjs/xfa/cjx_object.h" + #include "fxjs/xfa/jse_define.h" + +@@ -15,7 +16,7 @@ class CXFA_Node; + + class CJX_Tree : public CJX_Object { + public: +- explicit CJX_Tree(CXFA_Object* obj); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Tree() override; + + // CJX_Object: +@@ -32,6 +33,9 @@ class CJX_Tree : public CJX_Object { + JSE_PROP(parent); + JSE_PROP(somExpression); + ++ protected: ++ explicit CJX_Tree(CXFA_Object* obj); ++ + private: + using Type__ = CJX_Tree; + using ParentType__ = CJX_Object; +@@ -39,10 +43,10 @@ class CJX_Tree : public CJX_Object { + static const TypeTag static_type__ = TypeTag::Tree; + static const CJX_MethodSpec MethodSpecs[]; + +- void ResolveNodeList(CFXJSE_Value* pValue, +- WideString wsExpression, +- uint32_t dwFlag, +- CXFA_Node* refNode); ++ v8::Local ResolveNodeList(v8::Isolate* pIsolate, ++ WideString wsExpression, ++ Mask dwFlag, ++ CXFA_Node* refNode); + }; + + #endif // FXJS_XFA_CJX_TREE_H_ +diff --git a/fxjs/xfa/cjx_treelist.cpp b/fxjs/xfa/cjx_treelist.cpp +index 51bbb0ac5..e2e4a21a2 100644 +--- a/fxjs/xfa/cjx_treelist.cpp ++++ b/fxjs/xfa/cjx_treelist.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,6 +11,7 @@ + #include "fxjs/js_resources.h" + #include "fxjs/xfa/cfxjse_engine.h" + #include "fxjs/xfa/cfxjse_value.h" ++#include "v8/include/v8-object.h" + #include "xfa/fxfa/parser/cxfa_document.h" + #include "xfa/fxfa/parser/cxfa_node.h" + #include "xfa/fxfa/parser/cxfa_treelist.h" +@@ -22,7 +23,7 @@ CJX_TreeList::CJX_TreeList(CXFA_TreeList* list) : CJX_List(list) { + DefineMethods(MethodSpecs); + } + +-CJX_TreeList::~CJX_TreeList() {} ++CJX_TreeList::~CJX_TreeList() = default; + + bool CJX_TreeList::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); +@@ -33,7 +34,7 @@ CXFA_TreeList* CJX_TreeList::GetXFATreeList() { + } + + CJS_Result CJX_TreeList::namedItem( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +@@ -43,9 +44,6 @@ CJS_Result CJX_TreeList::namedItem( + if (!pNode) + return CJS_Result::Success(); + +- CFXJSE_Value* value = +- GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode); +- + return CJS_Result::Success( +- value->DirectGetValue().Get(runtime->GetIsolate())); ++ GetDocument()->GetScriptContext()->GetOrCreateJSBindingFromMap(pNode)); + } +diff --git a/fxjs/xfa/cjx_treelist.h b/fxjs/xfa/cjx_treelist.h +index 294ab2661..a5dbaaa94 100644 +--- a/fxjs/xfa/cjx_treelist.h ++++ b/fxjs/xfa/cjx_treelist.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_TreeList; + + class CJX_TreeList final : public CJX_List { + public: +- explicit CJX_TreeList(CXFA_TreeList* list); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_TreeList() override; + + // CJX_Object: +@@ -23,6 +23,8 @@ class CJX_TreeList final : public CJX_List { + JSE_METHOD(namedItem); + + private: ++ explicit CJX_TreeList(CXFA_TreeList* list); ++ + using Type__ = CJX_TreeList; + using ParentType__ = CJX_List; + +diff --git a/fxjs/xfa/cjx_wsdlconnection.cpp b/fxjs/xfa/cjx_wsdlconnection.cpp +index 782123232..1f7f45ed2 100644 +--- a/fxjs/xfa/cjx_wsdlconnection.cpp ++++ b/fxjs/xfa/cjx_wsdlconnection.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,6 +11,7 @@ + #include "fxjs/cfx_v8.h" + #include "fxjs/js_resources.h" + #include "fxjs/xfa/cfxjse_value.h" ++#include "v8/include/v8-primitive.h" + #include "xfa/fxfa/parser/cxfa_wsdlconnection.h" + + const CJX_MethodSpec CJX_WsdlConnection::MethodSpecs[] = { +@@ -21,14 +22,14 @@ CJX_WsdlConnection::CJX_WsdlConnection(CXFA_WsdlConnection* connection) + DefineMethods(MethodSpecs); + } + +-CJX_WsdlConnection::~CJX_WsdlConnection() {} ++CJX_WsdlConnection::~CJX_WsdlConnection() = default; + + bool CJX_WsdlConnection::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + + CJS_Result CJX_WsdlConnection::execute( +- CFX_V8* runtime, ++ CFXJSE_Engine* runtime, + const std::vector>& params) { + if (!params.empty() && params.size() != 1) + return CJS_Result::Failure(JSMessage::kParamError); +diff --git a/fxjs/xfa/cjx_wsdlconnection.h b/fxjs/xfa/cjx_wsdlconnection.h +index bd5db40a7..870669589 100644 +--- a/fxjs/xfa/cjx_wsdlconnection.h ++++ b/fxjs/xfa/cjx_wsdlconnection.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_WsdlConnection; + + class CJX_WsdlConnection final : public CJX_Node { + public: +- explicit CJX_WsdlConnection(CXFA_WsdlConnection* connection); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_WsdlConnection() override; + + // CJX_Object: +@@ -23,6 +23,8 @@ class CJX_WsdlConnection final : public CJX_Node { + JSE_METHOD(execute); + + private: ++ explicit CJX_WsdlConnection(CXFA_WsdlConnection* connection); ++ + using Type__ = CJX_WsdlConnection; + using ParentType__ = CJX_Node; + +diff --git a/fxjs/xfa/cjx_xfa.cpp b/fxjs/xfa/cjx_xfa.cpp +index 08c6a8063..5248693c8 100644 +--- a/fxjs/xfa/cjx_xfa.cpp ++++ b/fxjs/xfa/cjx_xfa.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,8 +6,9 @@ + + #include "fxjs/xfa/cjx_xfa.h" + ++#include "fxjs/fxv8.h" + #include "fxjs/xfa/cfxjse_engine.h" +-#include "fxjs/xfa/cfxjse_value.h" ++#include "v8/include/v8-object.h" + #include "xfa/fxfa/parser/cxfa_document.h" + #include "xfa/fxfa/parser/cxfa_xfa.h" + +@@ -19,7 +20,8 @@ bool CJX_Xfa::DynamicTypeIs(TypeTag eType) const { + return eType == static_type__ || ParentType__::DynamicTypeIs(eType); + } + +-void CJX_Xfa::thisValue(CFXJSE_Value* pValue, ++void CJX_Xfa::thisValue(v8::Isolate* pIsolate, ++ v8::Local* pValue, + bool bSetting, + XFA_Attribute eAttribute) { + if (bSetting) +@@ -27,9 +29,7 @@ void CJX_Xfa::thisValue(CFXJSE_Value* pValue, + + auto* pScriptContext = GetDocument()->GetScriptContext(); + CXFA_Object* pThis = pScriptContext->GetThisObject(); +- if (!pThis) { +- pValue->SetNull(); +- return; +- } +- pValue->Assign(pScriptContext->GetOrCreateJSBindingFromMap(pThis)); ++ *pValue = ++ pThis ? pScriptContext->GetOrCreateJSBindingFromMap(pThis).As() ++ : fxv8::NewNullHelper(pIsolate); + } +diff --git a/fxjs/xfa/cjx_xfa.h b/fxjs/xfa/cjx_xfa.h +index 63b0f5fb9..031425d3d 100644 +--- a/fxjs/xfa/cjx_xfa.h ++++ b/fxjs/xfa/cjx_xfa.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,7 +14,7 @@ class CXFA_Xfa; + + class CJX_Xfa final : public CJX_Model { + public: +- explicit CJX_Xfa(CXFA_Xfa* node); ++ CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; + ~CJX_Xfa() override; + + // CJX_Object: +@@ -23,6 +23,8 @@ class CJX_Xfa final : public CJX_Model { + JSE_PROP(thisValue); /* this */ + + private: ++ explicit CJX_Xfa(CXFA_Xfa* node); ++ + using Type__ = CJX_Xfa; + using ParentType__ = CJX_Model; + +diff --git a/fxjs/xfa/fxjse.cpp b/fxjs/xfa/fxjse.cpp +index ff6176b6f..d03ca7e1f 100644 +--- a/fxjs/xfa/fxjse.cpp ++++ b/fxjs/xfa/fxjse.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,12 @@ + + #include "fxjs/xfa/fxjse.h" + ++#include "fxjs/fxv8.h" ++#include "fxjs/xfa/cfxjse_context.h" ++#include "v8/include/v8-isolate.h" ++#include "v8/include/v8-object.h" ++#include "v8/include/v8-template.h" ++ + namespace pdfium { + namespace fxjse { + +@@ -15,6 +21,14 @@ const char kClassTag[] = "class descriptor tag"; + } // namespace fxjse + } // namespace pdfium + ++// static ++CFXJSE_HostObject* CFXJSE_HostObject::FromV8(v8::Local arg) { ++ if (!fxv8::IsObject(arg)) ++ return nullptr; ++ ++ return FXJSE_RetrieveObjectBinding(arg.As()); ++} ++ + CFXJSE_HostObject::CFXJSE_HostObject() = default; + + CFXJSE_HostObject::~CFXJSE_HostObject() = default; +@@ -23,6 +37,17 @@ CFXJSE_FormCalcContext* CFXJSE_HostObject::AsFormCalcContext() { + return nullptr; + } + +-CXFA_Object* CFXJSE_HostObject::AsCXFAObject() { ++CJX_Object* CFXJSE_HostObject::AsCJXObject() { + return nullptr; + } ++ ++v8::Local CFXJSE_HostObject::NewBoundV8Object( ++ v8::Isolate* pIsolate, ++ v8::Local tmpl) { ++ v8::Local hObject = ++ tmpl->InstanceTemplate() ++ ->NewInstance(pIsolate->GetCurrentContext()) ++ .ToLocalChecked(); ++ FXJSE_UpdateObjectBinding(hObject, this); ++ return hObject; ++} +diff --git a/fxjs/xfa/fxjse.h b/fxjs/xfa/fxjse.h +index 5aa6b397f..b020ce685 100644 +--- a/fxjs/xfa/fxjse.h ++++ b/fxjs/xfa/fxjse.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,79 +7,90 @@ + #ifndef FXJS_XFA_FXJSE_H_ + #define FXJS_XFA_FXJSE_H_ + ++#include ++ + #include "core/fxcrt/fx_string.h" +-#include "core/fxcrt/fx_system.h" +-#include "v8/include/v8.h" ++#include "v8/include/v8-forward.h" + + namespace pdfium { + namespace fxjse { + ++// These are strings by design. With ASLR, their addresses should be random, so ++// it should be very unlikely for an object to accidentally have the same tag. + extern const char kFuncTag[]; + extern const char kClassTag[]; + + } // namespace fxjse + } // namespace pdfium + +-class CFXJSE_Arguments; + class CFXJSE_FormCalcContext; +-class CFXJSE_Value; + class CJS_Result; +-class CXFA_Object; ++class CJX_Object; ++ ++enum class FXJSE_ClassPropType { ++ kNone, ++ kProperty, ++ kMethod, ++}; + + // C++ object which is retrieved from v8 object's slot. + class CFXJSE_HostObject { + public: ++ static CFXJSE_HostObject* FromV8(v8::Local arg); + virtual ~CFXJSE_HostObject(); + + // Two subclasses. + virtual CFXJSE_FormCalcContext* AsFormCalcContext(); +- virtual CXFA_Object* AsCXFAObject(); ++ virtual CJX_Object* AsCJXObject(); ++ ++ v8::Local NewBoundV8Object(v8::Isolate* pIsolate, ++ v8::Local tmpl); + + protected: + CFXJSE_HostObject(); + }; + +-typedef CJS_Result (*FXJSE_MethodCallback)( +- const v8::FunctionCallbackInfo& info, +- const WideString& functionName); +-typedef void (*FXJSE_FuncCallback)(CFXJSE_Value* pThis, +- ByteStringView szFuncName, +- CFXJSE_Arguments& args); +-typedef void (*FXJSE_PropAccessor)(CFXJSE_Value* pObject, +- ByteStringView szPropName, +- CFXJSE_Value* pValue); +-typedef int32_t (*FXJSE_PropTypeGetter)(CFXJSE_Value* pObject, +- ByteStringView szPropName, +- bool bQueryIn); +- +-enum FXJSE_ClassPropTypes { +- FXJSE_ClassPropType_None, +- FXJSE_ClassPropType_Property, +- FXJSE_ClassPropType_Method +-}; ++using FXJSE_MethodCallback = ++ CJS_Result (*)(const v8::FunctionCallbackInfo& info, ++ const WideString& functionName); ++using FXJSE_FuncCallback = ++ void (*)(CFXJSE_HostObject* pThis, ++ const v8::FunctionCallbackInfo& info); ++using FXJSE_PropGetter = v8::Local (*)(v8::Isolate* pIsolate, ++ v8::Local pObject, ++ ByteStringView szPropName); ++using FXJSE_PropSetter = void (*)(v8::Isolate* pIsolate, ++ v8::Local pObject, ++ ByteStringView szPropName, ++ v8::Local pValue); ++using FXJSE_PropTypeGetter = ++ FXJSE_ClassPropType (*)(v8::Isolate* pIsolate, ++ v8::Local pObject, ++ ByteStringView szPropName, ++ bool bQueryIn); + + struct FXJSE_FUNCTION_DESCRIPTOR { +- const char* tag; // pdfium::kFuncTag always. ++ const char* tag; // `pdfium::fxjse::kFuncTag` always. + const char* name; + FXJSE_FuncCallback callbackProc; + }; + + struct FXJSE_CLASS_DESCRIPTOR { +- const char* tag; // pdfium::kClassTag always. ++ const char* tag; // `pdfium::fxjse::kClassTag` always. + const char* name; + const FXJSE_FUNCTION_DESCRIPTOR* methods; + int32_t methNum; + FXJSE_PropTypeGetter dynPropTypeGetter; +- FXJSE_PropAccessor dynPropGetter; +- FXJSE_PropAccessor dynPropSetter; ++ FXJSE_PropGetter dynPropGetter; ++ FXJSE_PropSetter dynPropSetter; + FXJSE_MethodCallback dynMethodCall; + }; + +-extern const FXJSE_CLASS_DESCRIPTOR GlobalClassDescriptor; +-extern const FXJSE_CLASS_DESCRIPTOR NormalClassDescriptor; +-extern const FXJSE_CLASS_DESCRIPTOR VariablesClassDescriptor; +-extern const FXJSE_CLASS_DESCRIPTOR kFormCalcFM2JSDescriptor; ++extern const FXJSE_CLASS_DESCRIPTOR kGlobalClassDescriptor; ++extern const FXJSE_CLASS_DESCRIPTOR kNormalClassDescriptor; ++extern const FXJSE_CLASS_DESCRIPTOR kVariablesClassDescriptor; ++extern const FXJSE_CLASS_DESCRIPTOR kFormCalcDescriptor; + +-void FXJSE_ThrowMessage(ByteStringView utf8Message); ++void FXJSE_ThrowMessage(v8::Isolate* pIsolate, ByteStringView utf8Message); + + #endif // FXJS_XFA_FXJSE_H_ +diff --git a/fxjs/xfa/jse_define.h b/fxjs/xfa/jse_define.h +index 26405c9e7..080060105 100644 +--- a/fxjs/xfa/jse_define.h ++++ b/fxjs/xfa/jse_define.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,25 +11,28 @@ + + #include "fxjs/cjs_result.h" + +-class CFX_V8; ++class CFXJSE_Engine; + + #define JSE_METHOD(method_name) \ + static CJS_Result method_name##_static( \ +- CJX_Object* node, CFX_V8* runtime, \ ++ CJX_Object* node, CFXJSE_Engine* runtime, \ + const std::vector>& params) { \ + if (!node->DynamicTypeIs(static_type__)) \ + return CJS_Result::Failure(JSMessage::kBadObjectError); \ + return static_cast(node)->method_name(runtime, params); \ + } \ +- CJS_Result method_name(CFX_V8* runtime, \ ++ CJS_Result method_name(CFXJSE_Engine* runtime, \ + const std::vector>& params) + +-#define JSE_PROP(prop_name) \ +- static void prop_name##_static(CJX_Object* node, CFXJSE_Value* value, \ +- bool setting, XFA_Attribute attribute) { \ +- if (node->DynamicTypeIs(static_type__)) \ +- static_cast(node)->prop_name(value, setting, attribute); \ +- } \ +- void prop_name(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute) ++#define JSE_PROP(prop_name) \ ++ static void prop_name##_static(v8::Isolate* pIsolate, CJX_Object* node, \ ++ v8::Local* value, bool setting, \ ++ XFA_Attribute attribute) { \ ++ if (node->DynamicTypeIs(static_type__)) \ ++ static_cast(node)->prop_name(pIsolate, value, setting, \ ++ attribute); \ ++ } \ ++ void prop_name(v8::Isolate* pIsolate, v8::Local* pValue, \ ++ bool bSetting, XFA_Attribute eAttribute) + + #endif // FXJS_XFA_JSE_DEFINE_H_ +diff --git a/navbar.md b/navbar.md +index c12cf1108..386ef7f53 100644 +--- a/navbar.md ++++ b/navbar.md +@@ -1,5 +1,5 @@ + +diff --git a/pdfium.gni b/pdfium.gni +index 00a3886e7..9c6ae8d64 100644 +--- a/pdfium.gni ++++ b/pdfium.gni +@@ -1,4 +1,4 @@ +-# Copyright 2015 PDFium Authors. All rights reserved. ++# Copyright 2015 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -33,19 +33,21 @@ declare_args() { + # If XFA, also support png codec. Ignored if not XFA. + pdf_enable_xfa_png = true + +- # If XFA, also support png codec. Ignored if not XFA. ++ # If XFA, also support tiff codec. Ignored if not XFA. + pdf_enable_xfa_tiff = true + +- # Build PDFium against Skia (experimental) rather than AGG. Use Skia to draw +- # everything. +- pdf_use_skia = pdf_use_skia_override ++ # Build PDFium using C++20 if set to true. Otherwise builds with C++17. ++ # There is no "pdf_use_cxx20_override" variable because this is only used in ++ # standalone PDFium, and not when PDFium is embedded in our projects. ++ pdf_use_cxx20 = (is_win && is_component_build) || is_fuchsia + +- # Build PDFium against Skia (experimental) rather than AGG. Use Skia to draw +- # paths. +- pdf_use_skia_paths = pdf_use_skia_paths_override ++ # Build PDFium with PartitionAlloc as the memory allocator. ++ pdf_use_partition_alloc = pdf_use_partition_alloc_override + +- # Build PDFium with or without experimental win32 GDI APIs. +- pdf_use_win32_gdi = pdf_use_win32_gdi_override ++ # Build PDFium to use Skia (experimental) for all PDFium graphics. ++ # If enabled, coexists in build with AGG graphics and the default ++ # renderer is selectable at runtime. ++ pdf_use_skia = pdf_use_skia_override + + # Build PDFium standalone + pdf_is_standalone = false +@@ -56,9 +58,6 @@ declare_args() { + # Enable callgrind for performance profiling + enable_callgrind = false + +- # Don't build against bundled zlib. +- use_system_zlib = false +- + # Don't build against bundled lcms2. + use_system_lcms2 = false + +@@ -67,10 +66,15 @@ declare_args() { + + # Don't build against bundled libpng. + use_system_libpng = false +-} + +-if (pdf_use_skia && pdf_use_skia_paths) { +- assert(false, "Enable at most ONE of pdf_use_skia and pdf_use_skia_paths") ++ # Don't build against bundled libtiff. ++ use_system_libtiff = false ++ ++ # Don't build against bundled zlib. ++ use_system_zlib = false ++ ++ # Enable SSE2 for MSVC builds. Ignored if it's not a MSVC build. ++ msvc_use_sse2 = true + } + + assert(!pdf_is_complete_lib || !is_component_build, +diff --git a/public/PRESUBMIT.py b/public/PRESUBMIT.py +index 5081823e8..be018588f 100644 +--- a/public/PRESUBMIT.py ++++ b/public/PRESUBMIT.py +@@ -1,4 +1,4 @@ +-# Copyright 2017 The PDFium Authors. All rights reserved. ++# Copyright 2017 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -8,12 +8,15 @@ See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts + for more details about the presubmit API built into depot_tools. + """ + ++USE_PYTHON3 = True ++ ++ + def _CheckPublicHeaders(input_api, output_api): + """Checks that the public headers match the API tests.""" + src_path = input_api.os_path.dirname(input_api.PresubmitLocalPath()) + check_script = input_api.os_path.join( + src_path, 'testing' , 'tools' , 'api_check.py') +- cmd = [input_api.python_executable, check_script] ++ cmd = [input_api.python3_executable, check_script] + try: + input_api.subprocess.check_output(cmd) + return [] +@@ -24,11 +27,11 @@ def _CheckPublicHeaders(input_api, output_api): + + def CheckChangeOnUpload(input_api, output_api): + results = [] +- results += _CheckPublicHeaders(input_api, output_api) ++ results.extend(_CheckPublicHeaders(input_api, output_api)) + return results + + + def CheckChangeOnCommit(input_api, output_api): + results = [] +- results += _CheckPublicHeaders(input_api, output_api) ++ results.extend(_CheckPublicHeaders(input_api, output_api)) + return results +diff --git a/public/README b/public/README +index b07d0f306..5a8fd4988 100644 +--- a/public/README ++++ b/public/README +@@ -4,6 +4,8 @@ The header files in this directory are the only ones that should ever be + included by an embedder of PDFium. If there arises a need for functionality + beyond what is present here, then a new API must be added here to provide it. + ++Documentation for the API is within the headers. Start by reading fpdfview.h. ++ + These header files must be entirely contained in this directory; they must + never include other header files from outside of it. + +diff --git a/public/cpp/fpdf_deleters.h b/public/cpp/fpdf_deleters.h +index 633ddf5e3..d58f8d593 100644 +--- a/public/cpp/fpdf_deleters.h ++++ b/public/cpp/fpdf_deleters.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/public/cpp/fpdf_scopers.h b/public/cpp/fpdf_scopers.h +index ff57c1b48..fd9ce6997 100644 +--- a/public/cpp/fpdf_scopers.h ++++ b/public/cpp/fpdf_scopers.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/public/fpdf_annot.h b/public/fpdf_annot.h +index 58da80987..abcef6bc4 100644 +--- a/public/fpdf_annot.h ++++ b/public/fpdf_annot.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,8 +10,6 @@ + // NOLINTNEXTLINE(build/include) + #include "fpdfview.h" + +-// NOLINTNEXTLINE(build/include) +-#include "fpdf_doc.h" + // NOLINTNEXTLINE(build/include) + #include "fpdf_formfill.h" + +@@ -47,6 +45,7 @@ extern "C" { + #define FPDF_ANNOT_THREED 25 + #define FPDF_ANNOT_RICHMEDIA 26 + #define FPDF_ANNOT_XFAWIDGET 27 ++#define FPDF_ANNOT_REDACT 28 + + // Refer to PDF Reference (6th edition) table 8.16 for all annotation flags. + #define FPDF_ANNOT_FLAG_NONE 0 +@@ -81,6 +80,17 @@ extern "C" { + // interactive form choice fields. + #define FPDF_FORMFLAG_CHOICE_COMBO (1 << 17) + #define FPDF_FORMFLAG_CHOICE_EDIT (1 << 18) ++#define FPDF_FORMFLAG_CHOICE_MULTI_SELECT (1 << 21) ++ ++// Additional actions type of form field: ++// K, on key stroke, JavaScript action. ++// F, on format, JavaScript action. ++// V, on validate, JavaScript action. ++// C, on calculate, JavaScript action. ++#define FPDF_ANNOT_AACTION_KEY_STROKE 12 ++#define FPDF_ANNOT_AACTION_FORMAT 13 ++#define FPDF_ANNOT_AACTION_VALIDATE 14 ++#define FPDF_ANNOT_AACTION_CALCULATE 15 + + typedef enum FPDFANNOT_COLORTYPE { + FPDFANNOT_COLORTYPE_Color = 0, +@@ -89,8 +99,19 @@ typedef enum FPDFANNOT_COLORTYPE { + + // Experimental API. + // Check if an annotation subtype is currently supported for creation. +-// Currently supported subtypes: circle, highlight, ink, popup, square, +-// squiggly, stamp, strikeout, text, and underline. ++// Currently supported subtypes: ++// - circle ++// - freetext ++// - highlight ++// - ink ++// - link ++// - popup ++// - square, ++// - squiggly ++// - stamp ++// - strikeout ++// - text ++// - underline + // + // subtype - the subtype to be checked. + // +@@ -193,6 +214,34 @@ FPDFAnnot_IsObjectSupportedSubtype(FPDF_ANNOTATION_SUBTYPE subtype); + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFAnnot_UpdateObject(FPDF_ANNOTATION annot, FPDF_PAGEOBJECT obj); + ++// Experimental API. ++// Add a new InkStroke, represented by an array of points, to the InkList of ++// |annot|. The API creates an InkList if one doesn't already exist in |annot|. ++// This API works only for ink annotations. Please refer to ISO 32000-1:2008 ++// spec, section 12.5.6.13. ++// ++// annot - handle to an annotation. ++// points - pointer to a FS_POINTF array representing input points. ++// point_count - number of elements in |points| array. This should not exceed ++// the maximum value that can be represented by an int32_t). ++// ++// Returns the 0-based index at which the new InkStroke is added in the InkList ++// of the |annot|. Returns -1 on failure. ++FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_AddInkStroke(FPDF_ANNOTATION annot, ++ const FS_POINTF* points, ++ size_t point_count); ++ ++// Experimental API. ++// Removes an InkList in |annot|. ++// This API works only for ink annotations. ++// ++// annot - handle to an annotation. ++// ++// Return true on successful removal of /InkList entry from context of the ++// non-null ink |annot|. Returns false on failure. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFAnnot_RemoveInkList(FPDF_ANNOTATION annot); ++ + // Experimental API. + // Add |obj| to |annot|. |obj| must have been created by + // FPDFPageObj_CreateNew{Path|Rect}() or FPDFPageObj_New{Text|Image}Obj(), and +@@ -365,6 +414,121 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetRect(FPDF_ANNOTATION annot, + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetRect(FPDF_ANNOTATION annot, + FS_RECTF* rect); + ++// Experimental API. ++// Get the vertices of a polygon or polyline annotation. |buffer| is an array of ++// points of the annotation. If |length| is less than the returned length, or ++// |annot| or |buffer| is NULL, |buffer| will not be modified. ++// ++// annot - handle to an annotation, as returned by e.g. FPDFPage_GetAnnot() ++// buffer - buffer for holding the points. ++// length - length of the buffer in points. ++// ++// Returns the number of points if the annotation is of type polygon or ++// polyline, 0 otherwise. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFAnnot_GetVertices(FPDF_ANNOTATION annot, ++ FS_POINTF* buffer, ++ unsigned long length); ++ ++// Experimental API. ++// Get the number of paths in the ink list of an ink annotation. ++// ++// annot - handle to an annotation, as returned by e.g. FPDFPage_GetAnnot() ++// ++// Returns the number of paths in the ink list if the annotation is of type ink, ++// 0 otherwise. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFAnnot_GetInkListCount(FPDF_ANNOTATION annot); ++ ++// Experimental API. ++// Get a path in the ink list of an ink annotation. |buffer| is an array of ++// points of the path. If |length| is less than the returned length, or |annot| ++// or |buffer| is NULL, |buffer| will not be modified. ++// ++// annot - handle to an annotation, as returned by e.g. FPDFPage_GetAnnot() ++// path_index - index of the path ++// buffer - buffer for holding the points. ++// length - length of the buffer in points. ++// ++// Returns the number of points of the path if the annotation is of type ink, 0 ++// otherwise. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFAnnot_GetInkListPath(FPDF_ANNOTATION annot, ++ unsigned long path_index, ++ FS_POINTF* buffer, ++ unsigned long length); ++ ++// Experimental API. ++// Get the starting and ending coordinates of a line annotation. ++// ++// annot - handle to an annotation, as returned by e.g. FPDFPage_GetAnnot() ++// start - starting point ++// end - ending point ++// ++// Returns true if the annotation is of type line, |start| and |end| are not ++// NULL, false otherwise. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetLine(FPDF_ANNOTATION annot, ++ FS_POINTF* start, ++ FS_POINTF* end); ++ ++// Experimental API. ++// Set the characteristics of the annotation's border (rounded rectangle). ++// ++// annot - handle to an annotation ++// horizontal_radius - horizontal corner radius, in default user space units ++// vertical_radius - vertical corner radius, in default user space units ++// border_width - border width, in default user space units ++// ++// Returns true if setting the border for |annot| succeeds, false otherwise. ++// ++// If |annot| contains an appearance stream that overrides the border values, ++// then the appearance stream will be removed on success. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetBorder(FPDF_ANNOTATION annot, ++ float horizontal_radius, ++ float vertical_radius, ++ float border_width); ++ ++// Experimental API. ++// Get the characteristics of the annotation's border (rounded rectangle). ++// ++// annot - handle to an annotation ++// horizontal_radius - horizontal corner radius, in default user space units ++// vertical_radius - vertical corner radius, in default user space units ++// border_width - border width, in default user space units ++// ++// Returns true if |horizontal_radius|, |vertical_radius| and |border_width| are ++// not NULL, false otherwise. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFAnnot_GetBorder(FPDF_ANNOTATION annot, ++ float* horizontal_radius, ++ float* vertical_radius, ++ float* border_width); ++ ++// Experimental API. ++// Get the JavaScript of an event of the annotation's additional actions. ++// |buffer| is only modified if |buflen| is large enough to hold the whole ++// JavaScript string. If |buflen| is smaller, the total size of the JavaScript ++// is still returned, but nothing is copied. If there is no JavaScript for ++// |event| in |annot|, an empty string is written to |buf| and 2 is returned, ++// denoting the size of the null terminator in the buffer. On other errors, ++// nothing is written to |buffer| and 0 is returned. ++// ++// hHandle - handle to the form fill module, returned by ++// FPDFDOC_InitFormFillEnvironment(). ++// annot - handle to an interactive form annotation. ++// event - event type, one of the FPDF_ANNOT_AACTION_* values. ++// buffer - buffer for holding the value string, encoded in UTF-16LE. ++// buflen - length of the buffer in bytes. ++// ++// Returns the length of the string value in bytes, including the 2-byte ++// null terminator. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFAnnot_GetFormAdditionalActionJavaScript(FPDF_FORMHANDLE hHandle, ++ FPDF_ANNOTATION annot, ++ int event, ++ FPDF_WCHAR* buffer, ++ unsigned long buflen); ++ + // Experimental API. + // Check if |annot|'s dictionary has |key| as a key. + // +@@ -557,6 +721,26 @@ FPDFAnnot_GetFormFieldName(FPDF_FORMHANDLE hHandle, + FPDF_WCHAR* buffer, + unsigned long buflen); + ++// Experimental API. ++// Gets the alternate name of |annot|, which is an interactive form annotation. ++// |buffer| is only modified if |buflen| is longer than the length of contents. ++// In case of error, nothing will be added to |buffer| and the return value will ++// be 0. Note that return value of empty string is 2 for "\0\0". ++// ++// hHandle - handle to the form fill module, returned by ++// FPDFDOC_InitFormFillEnvironment(). ++// annot - handle to an interactive form annotation. ++// buffer - buffer for holding the alternate name string, encoded in ++// UTF-16LE. ++// buflen - length of the buffer in bytes. ++// ++// Returns the length of the string value in bytes. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFAnnot_GetFormFieldAlternateName(FPDF_FORMHANDLE hHandle, ++ FPDF_ANNOTATION annot, ++ FPDF_WCHAR* buffer, ++ unsigned long buflen); ++ + // Experimental API. + // Gets the form field type of |annot|, which is an interactive form annotation. + // +@@ -627,6 +811,22 @@ FPDFAnnot_GetOptionLabel(FPDF_FORMHANDLE hHandle, + FPDF_WCHAR* buffer, + unsigned long buflen); + ++// Experimental API. ++// Determine whether or not the option at |index| in |annot|'s "Opt" dictionary ++// is selected. Intended for use with listbox and combobox widget annotations. ++// ++// handle - handle to the form fill module, returned by ++// FPDFDOC_InitFormFillEnvironment. ++// annot - handle to an annotation. ++// index - numeric index of the option in the "Opt" array. ++// ++// Returns true if the option at |index| in |annot|'s "Opt" dictionary is ++// selected, false otherwise. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFAnnot_IsOptionSelected(FPDF_FORMHANDLE handle, ++ FPDF_ANNOTATION annot, ++ int index); ++ + // Experimental API. + // Get the float value of the font size for an |annot| with variable text. + // If 0, the font is to be auto-sized: its size is computed as a function of +@@ -656,6 +856,118 @@ FPDFAnnot_GetFontSize(FPDF_FORMHANDLE hHandle, + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_IsChecked(FPDF_FORMHANDLE hHandle, + FPDF_ANNOTATION annot); + ++// Experimental API. ++// Set the list of focusable annotation subtypes. Annotations of subtype ++// FPDF_ANNOT_WIDGET are by default focusable. New subtypes set using this API ++// will override the existing subtypes. ++// ++// hHandle - handle to the form fill module, returned by ++// FPDFDOC_InitFormFillEnvironment. ++// subtypes - list of annotation subtype which can be tabbed over. ++// count - total number of annotation subtype in list. ++// Returns true if list of annotation subtype is set successfully, false ++// otherwise. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFAnnot_SetFocusableSubtypes(FPDF_FORMHANDLE hHandle, ++ const FPDF_ANNOTATION_SUBTYPE* subtypes, ++ size_t count); ++ ++// Experimental API. ++// Get the count of focusable annotation subtypes as set by host ++// for a |hHandle|. ++// ++// hHandle - handle to the form fill module, returned by ++// FPDFDOC_InitFormFillEnvironment. ++// Returns the count of focusable annotation subtypes or -1 on error. ++// Note : Annotations of type FPDF_ANNOT_WIDGET are by default focusable. ++FPDF_EXPORT int FPDF_CALLCONV ++FPDFAnnot_GetFocusableSubtypesCount(FPDF_FORMHANDLE hHandle); ++ ++// Experimental API. ++// Get the list of focusable annotation subtype as set by host. ++// ++// hHandle - handle to the form fill module, returned by ++// FPDFDOC_InitFormFillEnvironment. ++// subtypes - receives the list of annotation subtype which can be tabbed ++// over. Caller must have allocated |subtypes| more than or ++// equal to the count obtained from ++// FPDFAnnot_GetFocusableSubtypesCount() API. ++// count - size of |subtypes|. ++// Returns true on success and set list of annotation subtype to |subtypes|, ++// false otherwise. ++// Note : Annotations of type FPDF_ANNOT_WIDGET are by default focusable. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFAnnot_GetFocusableSubtypes(FPDF_FORMHANDLE hHandle, ++ FPDF_ANNOTATION_SUBTYPE* subtypes, ++ size_t count); ++ ++// Experimental API. ++// Gets FPDF_LINK object for |annot|. Intended to use for link annotations. ++// ++// annot - handle to an annotation. ++// ++// Returns FPDF_LINK from the FPDF_ANNOTATION and NULL on failure, ++// if the input annot is NULL or input annot's subtype is not link. ++FPDF_EXPORT FPDF_LINK FPDF_CALLCONV FPDFAnnot_GetLink(FPDF_ANNOTATION annot); ++ ++// Experimental API. ++// Gets the count of annotations in the |annot|'s control group. ++// A group of interactive form annotations is collectively called a form ++// control group. Here, |annot|, an interactive form annotation, should be ++// either a radio button or a checkbox. ++// ++// hHandle - handle to the form fill module, returned by ++// FPDFDOC_InitFormFillEnvironment. ++// annot - handle to an annotation. ++// ++// Returns number of controls in its control group or -1 on error. ++FPDF_EXPORT int FPDF_CALLCONV ++FPDFAnnot_GetFormControlCount(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot); ++ ++// Experimental API. ++// Gets the index of |annot| in |annot|'s control group. ++// A group of interactive form annotations is collectively called a form ++// control group. Here, |annot|, an interactive form annotation, should be ++// either a radio button or a checkbox. ++// ++// hHandle - handle to the form fill module, returned by ++// FPDFDOC_InitFormFillEnvironment. ++// annot - handle to an annotation. ++// ++// Returns index of a given |annot| in its control group or -1 on error. ++FPDF_EXPORT int FPDF_CALLCONV ++FPDFAnnot_GetFormControlIndex(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot); ++ ++// Experimental API. ++// Gets the export value of |annot| which is an interactive form annotation. ++// Intended for use with radio button and checkbox widget annotations. ++// |buffer| is only modified if |buflen| is longer than the length of contents. ++// In case of error, nothing will be added to |buffer| and the return value ++// will be 0. Note that return value of empty string is 2 for "\0\0". ++// ++// hHandle - handle to the form fill module, returned by ++// FPDFDOC_InitFormFillEnvironment(). ++// annot - handle to an interactive form annotation. ++// buffer - buffer for holding the value string, encoded in UTF-16LE. ++// buflen - length of the buffer in bytes. ++// ++// Returns the length of the string value in bytes. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFAnnot_GetFormFieldExportValue(FPDF_FORMHANDLE hHandle, ++ FPDF_ANNOTATION annot, ++ FPDF_WCHAR* buffer, ++ unsigned long buflen); ++ ++// Experimental API. ++// Add a URI action to |annot|, overwriting the existing action, if any. ++// ++// annot - handle to a link annotation. ++// uri - the URI to be set, encoded in 7-bit ASCII. ++// ++// Returns true if successful. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetURI(FPDF_ANNOTATION annot, ++ const char* uri); ++ + #ifdef __cplusplus + } // extern "C" + #endif // __cplusplus +diff --git a/public/fpdf_attachment.h b/public/fpdf_attachment.h +index 791a90b70..d25bddab6 100644 +--- a/public/fpdf_attachment.h ++++ b/public/fpdf_attachment.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -146,22 +146,31 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFAttachment_SetFile(FPDF_ATTACHMENT attachment, + FPDF_DOCUMENT document, + const void* contents, +- const unsigned long len); ++ unsigned long len); + + // Experimental API. +-// Get the file data of |attachment|. |buffer| is only modified if |buflen| is +-// longer than the length of the file. On errors, |buffer| is unmodified and the +-// returned length is 0. ++// Get the file data of |attachment|. ++// When the attachment file data is readable, true is returned, and |out_buflen| ++// is updated to indicate the file data size. |buffer| is only modified if ++// |buflen| is non-null and long enough to contain the entire file data. Callers ++// must check both the return value and the input |buflen| is no less than the ++// returned |out_buflen| before using the data. ++// ++// Otherwise, when the attachment file data is unreadable or when |out_buflen| ++// is null, false is returned and |buffer| and |out_buflen| remain unmodified. + // + // attachment - handle to an attachment. + // buffer - buffer for holding the file data from |attachment|. + // buflen - length of the buffer in bytes. ++// out_buflen - pointer to the variable that will receive the minimum buffer ++// size to contain the file data of |attachment|. + // +-// Returns the length of the file. +-FPDF_EXPORT unsigned long FPDF_CALLCONV ++// Returns true on success, false otherwise. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFAttachment_GetFile(FPDF_ATTACHMENT attachment, + void* buffer, +- unsigned long buflen); ++ unsigned long buflen, ++ unsigned long* out_buflen); + + #ifdef __cplusplus + } // extern "C" +diff --git a/public/fpdf_catalog.h b/public/fpdf_catalog.h +index 48d27756f..1a48411a4 100644 +--- a/public/fpdf_catalog.h ++++ b/public/fpdf_catalog.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/public/fpdf_dataavail.h b/public/fpdf_dataavail.h +index ad70b7f49..004d9beb3 100644 +--- a/public/fpdf_dataavail.h ++++ b/public/fpdf_dataavail.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -50,7 +50,6 @@ typedef struct _FX_FILEAVAIL { + size_t offset, + size_t size); + } FX_FILEAVAIL; +-typedef void* FPDF_AVAIL; + + // Create a document availability provider. + // +diff --git a/public/fpdf_doc.h b/public/fpdf_doc.h +index 565314eba..b073f4d2c 100644 +--- a/public/fpdf_doc.h ++++ b/public/fpdf_doc.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -24,6 +24,8 @@ extern "C" { + #define PDFACTION_URI 3 + // Launch an application or open a file. + #define PDFACTION_LAUNCH 4 ++// Go to a destination in an embedded file. ++#define PDFACTION_EMBEDDEDGOTO 5 + + // View destination fit types. See pdfmark reference v9, page 48. + #define PDFDEST_VIEW_UNKNOWN_MODE 0 +@@ -36,16 +38,12 @@ extern "C" { + #define PDFDEST_VIEW_FITBH 7 + #define PDFDEST_VIEW_FITBV 8 + +-typedef struct _FS_QUADPOINTSF { +- FS_FLOAT x1; +- FS_FLOAT y1; +- FS_FLOAT x2; +- FS_FLOAT y2; +- FS_FLOAT x3; +- FS_FLOAT y3; +- FS_FLOAT x4; +- FS_FLOAT y4; +-} FS_QUADPOINTSF; ++// The file identifier entry type. See section 14.4 "File Identifiers" of the ++// ISO 32000-1:2008 spec. ++typedef enum { ++ FILEIDTYPE_PERMANENT = 0, ++ FILEIDTYPE_CHANGING = 1 ++} FPDF_FILEIDTYPE; + + // Get the first child of |bookmark|, or the first top-level bookmark item. + // +@@ -65,6 +63,9 @@ FPDFBookmark_GetFirstChild(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark); + // + // Returns a handle to the next sibling of |bookmark|, or NULL if this is the + // last bookmark at this level. ++// ++// Note that the caller is responsible for handling circular bookmark ++// references, as may arise from malformed documents. + FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV + FPDFBookmark_GetNextSibling(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark); + +@@ -86,6 +87,18 @@ FPDFBookmark_GetTitle(FPDF_BOOKMARK bookmark, + void* buffer, + unsigned long buflen); + ++// Experimental API. ++// Get the number of chlidren of |bookmark|. ++// ++// bookmark - handle to the bookmark. ++// ++// Returns a signed integer that represents the number of sub-items the given ++// bookmark has. If the value is positive, child items shall be shown by default ++// (open state). If the value is negative, child items shall be hidden by ++// default (closed state). Please refer to PDF 32000-1:2008, Table 153. ++// Returns 0 if the bookmark has no children or is invalid. ++FPDF_EXPORT int FPDF_CALLCONV FPDFBookmark_GetCount(FPDF_BOOKMARK bookmark); ++ + // Find the bookmark with |title| in |document|. + // + // document - handle to the document. +@@ -103,7 +116,7 @@ FPDFBookmark_Find(FPDF_DOCUMENT document, FPDF_WIDESTRING title); + // document - handle to the document. + // bookmark - handle to the bookmark. + // +-// Returns the handle to the destination data, NULL if no destination is ++// Returns the handle to the destination data, or NULL if no destination is + // associated with |bookmark|. + FPDF_EXPORT FPDF_DEST FPDF_CALLCONV + FPDFBookmark_GetDest(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark); +@@ -113,8 +126,11 @@ FPDFBookmark_GetDest(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark); + // bookmark - handle to the bookmark. + // + // Returns the handle to the action data, or NULL if no action is associated +-// with |bookmark|. When NULL is returned, FPDFBookmark_GetDest() should be +-// called to get the |bookmark| destination data. ++// with |bookmark|. ++// If this function returns a valid handle, it is valid as long as |bookmark| is ++// valid. ++// If this function returns NULL, FPDFBookmark_GetDest() should be called to get ++// the |bookmark| destination data. + FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV + FPDFBookmark_GetAction(FPDF_BOOKMARK bookmark); + +@@ -173,8 +189,18 @@ FPDFAction_GetFilePath(FPDF_ACTION action, void* buffer, unsigned long buflen); + // character, or 0 on error, typically because the arguments were bad or the + // action was of the wrong type. + // +-// The |buffer| is always encoded in 7-bit ASCII. If |buflen| is less than the +-// returned length, or |buffer| is NULL, |buffer| will not be modified. ++// The |buffer| may contain badly encoded data. The caller should validate the ++// output. e.g. Check to see if it is UTF-8. ++// ++// If |buflen| is less than the returned length, or |buffer| is NULL, |buffer| ++// will not be modified. ++// ++// Historically, the documentation for this API claimed |buffer| is always ++// encoded in 7-bit ASCII, but did not actually enforce it. ++// https://pdfium.googlesource.com/pdfium.git/+/d609e84cee2e14a18333247485af91df48a40592 ++// added that enforcement, but that did not work well for real world PDFs that ++// used UTF-8. As of this writing, this API reverted back to its original ++// behavior prior to commit d609e84cee. + FPDF_EXPORT unsigned long FPDF_CALLCONV + FPDFAction_GetURIPath(FPDF_DOCUMENT document, + FPDF_ACTION action, +@@ -190,8 +216,8 @@ FPDFAction_GetURIPath(FPDF_DOCUMENT document, + FPDF_EXPORT int FPDF_CALLCONV FPDFDest_GetDestPageIndex(FPDF_DOCUMENT document, + FPDF_DEST dest); + ++// Experimental API. + // Get the view (fit type) specified by |dest|. +-// Experimental API. Subject to change. + // + // dest - handle to the destination. + // pNumParams - receives the number of view parameters, which is at most 4. +@@ -270,6 +296,8 @@ FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFLink_GetDest(FPDF_DOCUMENT document, + // link - handle to the link. + // + // Returns a handle to the action associated to |link|, or NULL if no action. ++// If this function returns a valid handle, it is valid as long as |link| is ++// valid. + FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDFLink_GetAction(FPDF_LINK link); + + // Enumerates all the link annotations in |page|. +@@ -284,6 +312,17 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_Enumerate(FPDF_PAGE page, + int* start_pos, + FPDF_LINK* link_annot); + ++// Experimental API. ++// Gets FPDF_ANNOTATION object for |link_annot|. ++// ++// page - handle to the page in which FPDF_LINK object is present. ++// link_annot - handle to link annotation. ++// ++// Returns FPDF_ANNOTATION from the FPDF_LINK and NULL on failure, ++// if the input link annot or page is NULL. ++FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV ++FPDFLink_GetAnnot(FPDF_PAGE page, FPDF_LINK link_annot); ++ + // Get the rectangle for |link_annot|. + // + // link_annot - handle to the link annotation. +@@ -312,6 +351,40 @@ FPDFLink_GetQuadPoints(FPDF_LINK link_annot, + int quad_index, + FS_QUADPOINTSF* quad_points); + ++// Experimental API ++// Gets an additional-action from |page|. ++// ++// page - handle to the page, as returned by FPDF_LoadPage(). ++// aa_type - the type of the page object's addtional-action, defined ++// in public/fpdf_formfill.h ++// ++// Returns the handle to the action data, or NULL if there is no ++// additional-action of type |aa_type|. ++// If this function returns a valid handle, it is valid as long as |page| is ++// valid. ++FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDF_GetPageAAction(FPDF_PAGE page, ++ int aa_type); ++ ++// Experimental API. ++// Get the file identifer defined in the trailer of |document|. ++// ++// document - handle to the document. ++// id_type - the file identifier type to retrieve. ++// buffer - a buffer for the file identifier. May be NULL. ++// buflen - the length of the buffer, in bytes. May be 0. ++// ++// Returns the number of bytes in the file identifier, including the NUL ++// terminator. ++// ++// The |buffer| is always a byte string. The |buffer| is followed by a NUL ++// terminator. If |buflen| is less than the returned length, or |buffer| is ++// NULL, |buffer| will not be modified. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDF_GetFileIdentifier(FPDF_DOCUMENT document, ++ FPDF_FILEIDTYPE id_type, ++ void* buffer, ++ unsigned long buflen); ++ + // Get meta-data |tag| content from |document|. + // + // document - handle to the document. +diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h +index 8e0d2343f..8df2e3bd9 100644 +--- a/public/fpdf_edit.h ++++ b/public/fpdf_edit.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -63,12 +63,16 @@ + #define FPDF_LINEJOIN_ROUND 1 + #define FPDF_LINEJOIN_BEVEL 2 + ++// See FPDF_SetPrintMode() for descriptions. + #define FPDF_PRINTMODE_EMF 0 + #define FPDF_PRINTMODE_TEXTONLY 1 + #define FPDF_PRINTMODE_POSTSCRIPT2 2 + #define FPDF_PRINTMODE_POSTSCRIPT3 3 + #define FPDF_PRINTMODE_POSTSCRIPT2_PASSTHROUGH 4 + #define FPDF_PRINTMODE_POSTSCRIPT3_PASSTHROUGH 5 ++#define FPDF_PRINTMODE_EMF_IMAGE_MASKS 6 ++#define FPDF_PRINTMODE_POSTSCRIPT3_TYPE42 7 ++#define FPDF_PRINTMODE_POSTSCRIPT3_TYPE42_PASSTHROUGH 8 + + typedef struct FPDF_IMAGEOBJ_METADATA { + // The image width in pixels. +@@ -245,6 +249,36 @@ FPDFPageObj_Transform(FPDF_PAGEOBJECT page_object, + double e, + double f); + ++// Experimental API. ++// Get the transform matrix of a page object. ++// ++// page_object - handle to a page object. ++// matrix - pointer to struct to receive the matrix value. ++// ++// The matrix is composed as: ++// |a c e| ++// |b d f| ++// and used to scale, rotate, shear and translate the page object. ++// ++// Returns TRUE on success. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFPageObj_GetMatrix(FPDF_PAGEOBJECT page_object, FS_MATRIX* matrix); ++ ++// Experimental API. ++// Set the transform matrix of a page object. ++// ++// page_object - handle to a page object. ++// matrix - pointer to struct with the matrix value. ++// ++// The matrix is composed as: ++// |a c e| ++// |b d f| ++// and can be used to scale, rotate, shear and translate the page object. ++// ++// Returns TRUE on success. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFPageObj_SetMatrix(FPDF_PAGEOBJECT path, const FS_MATRIX* matrix); ++ + // Transform all annotations in |page|. + // + // page - handle to a page. +@@ -556,32 +590,8 @@ FPDFImageObj_LoadJpegFileInline(FPDF_PAGE* pages, + FPDF_PAGEOBJECT image_object, + FPDF_FILEACCESS* file_access); + +-// Experimental API. +-// Get the transform matrix of an image object. ++// TODO(thestig): Start deprecating this once FPDFPageObj_SetMatrix() is stable. + // +-// image_object - handle to an image object. +-// a - matrix value. +-// b - matrix value. +-// c - matrix value. +-// d - matrix value. +-// e - matrix value. +-// f - matrix value. +-// +-// The matrix is composed as: +-// |a c e| +-// |b d f| +-// and used to scale, rotate, shear and translate the image. +-// +-// Returns TRUE on success. +-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +-FPDFImageObj_GetMatrix(FPDF_PAGEOBJECT image_object, +- double* a, +- double* b, +- double* c, +- double* d, +- double* e, +- double* f); +- + // Set the transform matrix of |image_object|. + // + // image_object - handle to an image object. +@@ -621,9 +631,11 @@ FPDFImageObj_SetBitmap(FPDF_PAGE* pages, + FPDF_PAGEOBJECT image_object, + FPDF_BITMAP bitmap); + +-// Get a bitmap rasterisation of |image_object|. The returned bitmap will be +-// owned by the caller, and FPDFBitmap_Destroy() must be called on the returned +-// bitmap when it is no longer needed. ++// Get a bitmap rasterization of |image_object|. FPDFImageObj_GetBitmap() only ++// operates on |image_object| and does not take the associated image mask into ++// account. It also ignores the matrix for |image_object|. ++// The returned bitmap will be owned by the caller, and FPDFBitmap_Destroy() ++// must be called on the returned bitmap when it is no longer needed. + // + // image_object - handle to an image object. + // +@@ -631,6 +643,24 @@ FPDFImageObj_SetBitmap(FPDF_PAGE* pages, + FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV + FPDFImageObj_GetBitmap(FPDF_PAGEOBJECT image_object); + ++// Experimental API. ++// Get a bitmap rasterization of |image_object| that takes the image mask and ++// image matrix into account. To render correctly, the caller must provide the ++// |document| associated with |image_object|. If there is a |page| associated ++// with |image_object|, the caller should provide that as well. ++// The returned bitmap will be owned by the caller, and FPDFBitmap_Destroy() ++// must be called on the returned bitmap when it is no longer needed. ++// ++// document - handle to a document associated with |image_object|. ++// page - handle to an optional page associated with |image_object|. ++// image_object - handle to an image object. ++// ++// Returns the bitmap or NULL on failure. ++FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV ++FPDFImageObj_GetRenderedBitmap(FPDF_DOCUMENT document, ++ FPDF_PAGE page, ++ FPDF_PAGEOBJECT image_object); ++ + // Get the decoded image data of |image_object|. The decoded data is the + // uncompressed image data, i.e. the raw image data after having all filters + // applied. |buffer| is only modified if |buflen| is longer than the length of +@@ -701,6 +731,19 @@ FPDFImageObj_GetImageMetadata(FPDF_PAGEOBJECT image_object, + FPDF_PAGE page, + FPDF_IMAGEOBJ_METADATA* metadata); + ++// Experimental API. ++// Get the image size in pixels. Faster method to get only image size. ++// ++// image_object - handle to an image object. ++// width - receives the image width in pixels; must not be NULL. ++// height - receives the image height in pixels; must not be NULL. ++// ++// Returns true if successful. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFImageObj_GetImagePixelSize(FPDF_PAGEOBJECT image_object, ++ unsigned int* width, ++ unsigned int* height); ++ + // Create a new path object at an initial position. + // + // x - initial horizontal position. +@@ -731,7 +774,7 @@ FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV FPDFPageObj_CreateNewRect(float x, + // right - pointer where the right coordinate will be stored + // top - pointer where the top coordinate will be stored + // +-// Returns TRUE on success. ++// On success, returns TRUE and fills in the 4 coordinates. + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFPageObj_GetBounds(FPDF_PAGEOBJECT page_object, + float* left, +@@ -739,6 +782,25 @@ FPDFPageObj_GetBounds(FPDF_PAGEOBJECT page_object, + float* right, + float* top); + ++// Experimental API. ++// Get the quad points that bounds |page_object|. ++// ++// page_object - handle to a page object. ++// quad_points - pointer where the quadrilateral points will be stored. ++// ++// On success, returns TRUE and fills in |quad_points|. ++// ++// Similar to FPDFPageObj_GetBounds(), this returns the bounds of a page ++// object. When the object is rotated by a non-multiple of 90 degrees, this API ++// returns a tighter bound that cannot be represented with just the 4 sides of ++// a rectangle. ++// ++// Currently only works the following |page_object| types: FPDF_PAGEOBJ_TEXT and ++// FPDF_PAGEOBJ_IMAGE. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFPageObj_GetRotatedBounds(FPDF_PAGEOBJECT page_object, ++ FS_QUADPOINTSF* quad_points); ++ + // Set the blend mode of |page_object|. + // + // page_object - handle to a page object. +@@ -792,7 +854,6 @@ FPDFPageObj_GetStrokeColor(FPDF_PAGEOBJECT page_object, + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFPageObj_SetStrokeWidth(FPDF_PAGEOBJECT page_object, float width); + +-// Experimental API. + // Get the stroke width of a page object. + // + // path - the handle to the page object. +@@ -875,6 +936,62 @@ FPDFPageObj_GetFillColor(FPDF_PAGEOBJECT page_object, + unsigned int* A); + + // Experimental API. ++// Get the line dash |phase| of |page_object|. ++// ++// page_object - handle to a page object. ++// phase - pointer where the dashing phase will be stored. ++// ++// Returns TRUE on success. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFPageObj_GetDashPhase(FPDF_PAGEOBJECT page_object, float* phase); ++ ++// Experimental API. ++// Set the line dash phase of |page_object|. ++// ++// page_object - handle to a page object. ++// phase - line dash phase. ++// ++// Returns TRUE on success. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFPageObj_SetDashPhase(FPDF_PAGEOBJECT page_object, float phase); ++ ++// Experimental API. ++// Get the line dash array of |page_object|. ++// ++// page_object - handle to a page object. ++// ++// Returns the line dash array size or -1 on failure. ++FPDF_EXPORT int FPDF_CALLCONV ++FPDFPageObj_GetDashCount(FPDF_PAGEOBJECT page_object); ++ ++// Experimental API. ++// Get the line dash array of |page_object|. ++// ++// page_object - handle to a page object. ++// dash_array - pointer where the dashing array will be stored. ++// dash_count - number of elements in |dash_array|. ++// ++// Returns TRUE on success. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFPageObj_GetDashArray(FPDF_PAGEOBJECT page_object, ++ float* dash_array, ++ size_t dash_count); ++ ++// Experimental API. ++// Set the line dash array of |page_object|. ++// ++// page_object - handle to a page object. ++// dash_array - the dash array. ++// dash_count - number of elements in |dash_array|. ++// phase - the line dash phase. ++// ++// Returns TRUE on success. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFPageObj_SetDashArray(FPDF_PAGEOBJECT page_object, ++ const float* dash_array, ++ size_t dash_count, ++ float phase); ++ + // Get number of segments inside |path|. + // + // path - handle to a path. +@@ -885,7 +1002,6 @@ FPDFPageObj_GetFillColor(FPDF_PAGEOBJECT page_object, + // Returns the number of objects in |path| or -1 on failure. + FPDF_EXPORT int FPDF_CALLCONV FPDFPath_CountSegments(FPDF_PAGEOBJECT path); + +-// Experimental API. + // Get segment in |path| at |index|. + // + // path - handle to a path. +@@ -895,7 +1011,6 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFPath_CountSegments(FPDF_PAGEOBJECT path); + FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV + FPDFPath_GetPathSegment(FPDF_PAGEOBJECT path, int index); + +-// Experimental API. + // Get coordinates of |segment|. + // + // segment - handle to a segment. +@@ -906,7 +1021,6 @@ FPDFPath_GetPathSegment(FPDF_PAGEOBJECT path, int index); + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFPathSegment_GetPoint(FPDF_PATHSEGMENT segment, float* x, float* y); + +-// Experimental API. + // Get type of |segment|. + // + // segment - handle to a segment. +@@ -915,7 +1029,6 @@ FPDFPathSegment_GetPoint(FPDF_PATHSEGMENT segment, float* x, float* y); + // FPDF_SEGMENT_UNKNOWN on error. + FPDF_EXPORT int FPDF_CALLCONV FPDFPathSegment_GetType(FPDF_PATHSEGMENT segment); + +-// Experimental API. + // Gets if the |segment| closes the current subpath of a given path. + // + // segment - handle to a segment. +@@ -991,7 +1104,6 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_SetDrawMode(FPDF_PAGEOBJECT path, + int fillmode, + FPDF_BOOL stroke); + +-// Experimental API. + // Get the drawing mode of a path. + // + // path - the handle to the path object. +@@ -1003,36 +1115,6 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_GetDrawMode(FPDF_PAGEOBJECT path, + int* fillmode, + FPDF_BOOL* stroke); + +-// Experimental API. +-// Get the transform matrix of a path. +-// +-// path - handle to a path. +-// matrix - pointer to struct to receive the matrix value. +-// +-// The matrix is composed as: +-// |a c e| +-// |b d f| +-// and used to scale, rotate, shear and translate the path. +-// +-// Returns TRUE on success. +-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_GetMatrix(FPDF_PAGEOBJECT path, +- FS_MATRIX* matrix); +- +-// Experimental API. +-// Set the transform matrix of a path. +-// +-// path - handle to a path. +-// matrix - pointer to struct with the matrix value. +-// +-// The matrix is composed as: +-// |a c e| +-// |b d f| +-// and can be used to scale, rotate, shear and translate the path. +-// +-// Returns TRUE on success. +-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_SetMatrix(FPDF_PAGEOBJECT path, +- const FS_MATRIX* matrix); +- + // Create a new text object using one of the standard PDF fonts. + // + // document - handle to the document. +@@ -1045,7 +1127,7 @@ FPDFPageObj_NewTextObj(FPDF_DOCUMENT document, + FPDF_BYTESTRING font, + float font_size); + +-// Set the text for a textobject. If it had text, it will be replaced. ++// Set the text for a text object. If it had text, it will be replaced. + // + // text_object - handle to the text object. + // text - the UTF-16LE encoded string containing the text to be added. +@@ -1054,6 +1136,20 @@ FPDFPageObj_NewTextObj(FPDF_DOCUMENT document, + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFText_SetText(FPDF_PAGEOBJECT text_object, FPDF_WIDESTRING text); + ++// Experimental API. ++// Set the text using charcodes for a text object. If it had text, it will be ++// replaced. ++// ++// text_object - handle to the text object. ++// charcodes - pointer to an array of charcodes to be added. ++// count - number of elements in |charcodes|. ++// ++// Returns TRUE on success ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFText_SetCharcodes(FPDF_PAGEOBJECT text_object, ++ const uint32_t* charcodes, ++ size_t count); ++ + // Returns a font object loaded from a stream of data. The font is loaded + // into the document. + // +@@ -1087,29 +1183,15 @@ FPDF_EXPORT FPDF_FONT FPDF_CALLCONV FPDFText_LoadFont(FPDF_DOCUMENT document, + FPDF_EXPORT FPDF_FONT FPDF_CALLCONV + FPDFText_LoadStandardFont(FPDF_DOCUMENT document, FPDF_BYTESTRING font); + +-// Experimental API. +-// Get the transform matrix of a text object. +-// +-// text - handle to a text. +-// matrix - pointer to struct with the matrix value. +-// +-// The matrix is composed as: +-// |a c e| +-// |b d f| +-// and used to scale, rotate, shear and translate the text. +-// +-// Returns TRUE on success. +-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFTextObj_GetMatrix(FPDF_PAGEOBJECT text, +- FS_MATRIX* matrix); +- +-// Experimental API. + // Get the font size of a text object. + // + // text - handle to a text. ++// size - pointer to the font size of the text object, measured in points ++// (about 1/72 inch) + // +-// Returns the font size of the text object, measured in points (about 1/72 +-// inch) on success; 0 on failure. +-FPDF_EXPORT float FPDF_CALLCONV FPDFTextObj_GetFontSize(FPDF_PAGEOBJECT text); ++// Returns TRUE on success. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDFTextObj_GetFontSize(FPDF_PAGEOBJECT text, float* size); + + // Close a loaded PDF font. + // +@@ -1128,7 +1210,6 @@ FPDFPageObj_CreateTextObj(FPDF_DOCUMENT document, + FPDF_FONT font, + float font_size); + +-// Experimental API. + // Get the text rendering mode of a text object. + // + // text - the handle to the text object. +@@ -1150,25 +1231,6 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDFTextObj_SetTextRenderMode(FPDF_PAGEOBJECT text, + FPDF_TEXT_RENDERMODE render_mode); + +-// Experimental API. +-// Get the font name of a text object. +-// +-// text - the handle to the text object. +-// buffer - the address of a buffer that receives the font name. +-// length - the size, in bytes, of |buffer|. +-// +-// Returns the number of bytes in the font name (including the trailing NUL +-// character) on success, 0 on error. +-// +-// Regardless of the platform, the |buffer| is always in UTF-8 encoding. +-// If |length| is less than the returned length, or |buffer| is NULL, |buffer| +-// will not be modified. +-FPDF_EXPORT unsigned long FPDF_CALLCONV +-FPDFTextObj_GetFontName(FPDF_PAGEOBJECT text, +- void* buffer, +- unsigned long length); +- +-// Experimental API. + // Get the text of a text object. + // + // text_object - the handle to the text object. +@@ -1185,10 +1247,193 @@ FPDFTextObj_GetFontName(FPDF_PAGEOBJECT text, + FPDF_EXPORT unsigned long FPDF_CALLCONV + FPDFTextObj_GetText(FPDF_PAGEOBJECT text_object, + FPDF_TEXTPAGE text_page, +- void* buffer, ++ FPDF_WCHAR* buffer, + unsigned long length); + + // Experimental API. ++// Get a bitmap rasterization of |text_object|. To render correctly, the caller ++// must provide the |document| associated with |text_object|. If there is a ++// |page| associated with |text_object|, the caller should provide that as well. ++// The returned bitmap will be owned by the caller, and FPDFBitmap_Destroy() ++// must be called on the returned bitmap when it is no longer needed. ++// ++// document - handle to a document associated with |text_object|. ++// page - handle to an optional page associated with |text_object|. ++// text_object - handle to a text object. ++// scale - the scaling factor, which must be greater than 0. ++// ++// Returns the bitmap or NULL on failure. ++FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV ++FPDFTextObj_GetRenderedBitmap(FPDF_DOCUMENT document, ++ FPDF_PAGE page, ++ FPDF_PAGEOBJECT text_object, ++ float scale); ++ ++// Experimental API. ++// Get the font of a text object. ++// ++// text - the handle to the text object. ++// ++// Returns a handle to the font object held by |text| which retains ownership. ++FPDF_EXPORT FPDF_FONT FPDF_CALLCONV FPDFTextObj_GetFont(FPDF_PAGEOBJECT text); ++ ++// Experimental API. ++// Get the font name of a font. ++// ++// font - the handle to the font object. ++// buffer - the address of a buffer that receives the font name. ++// length - the size, in bytes, of |buffer|. ++// ++// Returns the number of bytes in the font name (including the trailing NUL ++// character) on success, 0 on error. ++// ++// Regardless of the platform, the |buffer| is always in UTF-8 encoding. ++// If |length| is less than the returned length, or |buffer| is NULL, |buffer| ++// will not be modified. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFFont_GetFontName(FPDF_FONT font, char* buffer, unsigned long length); ++ ++// Experimental API. ++// Get the decoded data from the |font| object. ++// ++// font - The handle to the font object. (Required) ++// buffer - The address of a buffer that receives the font data. ++// buflen - Length of the buffer. ++// out_buflen - Pointer to variable that will receive the minimum buffer size ++// to contain the font data. Not filled if the return value is ++// FALSE. (Required) ++// ++// Returns TRUE on success. In which case, |out_buflen| will be filled, and ++// |buffer| will be filled if it is large enough. Returns FALSE if any of the ++// required parameters are null. ++// ++// The decoded data is the uncompressed font data. i.e. the raw font data after ++// having all stream filters applied, when the data is embedded. ++// ++// If the font is not embedded, then this API will instead return the data for ++// the substitution font it is using. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetFontData(FPDF_FONT font, ++ uint8_t* buffer, ++ size_t buflen, ++ size_t* out_buflen); ++ ++// Experimental API. ++// Get whether |font| is embedded or not. ++// ++// font - the handle to the font object. ++// ++// Returns 1 if the font is embedded, 0 if it not, and -1 on failure. ++FPDF_EXPORT int FPDF_CALLCONV FPDFFont_GetIsEmbedded(FPDF_FONT font); ++ ++// Experimental API. ++// Get the descriptor flags of a font. ++// ++// font - the handle to the font object. ++// ++// Returns the bit flags specifying various characteristics of the font as ++// defined in ISO 32000-1:2008, table 123, -1 on failure. ++FPDF_EXPORT int FPDF_CALLCONV FPDFFont_GetFlags(FPDF_FONT font); ++ ++// Experimental API. ++// Get the font weight of a font. ++// ++// font - the handle to the font object. ++// ++// Returns the font weight, -1 on failure. ++// Typical values are 400 (normal) and 700 (bold). ++FPDF_EXPORT int FPDF_CALLCONV FPDFFont_GetWeight(FPDF_FONT font); ++ ++// Experimental API. ++// Get the italic angle of a font. ++// ++// font - the handle to the font object. ++// angle - pointer where the italic angle will be stored ++// ++// The italic angle of a |font| is defined as degrees counterclockwise ++// from vertical. For a font that slopes to the right, this will be negative. ++// ++// Returns TRUE on success; |angle| unmodified on failure. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetItalicAngle(FPDF_FONT font, ++ int* angle); ++ ++// Experimental API. ++// Get ascent distance of a font. ++// ++// font - the handle to the font object. ++// font_size - the size of the |font|. ++// ascent - pointer where the font ascent will be stored ++// ++// Ascent is the maximum distance in points above the baseline reached by the ++// glyphs of the |font|. One point is 1/72 inch (around 0.3528 mm). ++// ++// Returns TRUE on success; |ascent| unmodified on failure. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetAscent(FPDF_FONT font, ++ float font_size, ++ float* ascent); ++ ++// Experimental API. ++// Get descent distance of a font. ++// ++// font - the handle to the font object. ++// font_size - the size of the |font|. ++// descent - pointer where the font descent will be stored ++// ++// Descent is the maximum distance in points below the baseline reached by the ++// glyphs of the |font|. One point is 1/72 inch (around 0.3528 mm). ++// ++// Returns TRUE on success; |descent| unmodified on failure. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetDescent(FPDF_FONT font, ++ float font_size, ++ float* descent); ++ ++// Experimental API. ++// Get the width of a glyph in a font. ++// ++// font - the handle to the font object. ++// glyph - the glyph. ++// font_size - the size of the font. ++// width - pointer where the glyph width will be stored ++// ++// Glyph width is the distance from the end of the prior glyph to the next ++// glyph. This will be the vertical distance for vertical writing. ++// ++// Returns TRUE on success; |width| unmodified on failure. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetGlyphWidth(FPDF_FONT font, ++ uint32_t glyph, ++ float font_size, ++ float* width); ++ ++// Experimental API. ++// Get the glyphpath describing how to draw a font glyph. ++// ++// font - the handle to the font object. ++// glyph - the glyph being drawn. ++// font_size - the size of the font. ++// ++// Returns the handle to the segment, or NULL on faiure. ++FPDF_EXPORT FPDF_GLYPHPATH FPDF_CALLCONV FPDFFont_GetGlyphPath(FPDF_FONT font, ++ uint32_t glyph, ++ float font_size); ++ ++// Experimental API. ++// Get number of segments inside glyphpath. ++// ++// glyphpath - handle to a glyph path. ++// ++// Returns the number of objects in |glyphpath| or -1 on failure. ++FPDF_EXPORT int FPDF_CALLCONV ++FPDFGlyphPath_CountGlyphSegments(FPDF_GLYPHPATH glyphpath); ++ ++// Experimental API. ++// Get segment in glyphpath at index. ++// ++// glyphpath - handle to a glyph path. ++// index - the index of a segment. ++// ++// Returns the handle to the segment, or NULL on faiure. ++FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV ++FPDFGlyphPath_GetGlyphPathSegment(FPDF_GLYPHPATH glyphpath, int index); ++ + // Get number of page objects inside |form_object|. + // + // form_object - handle to a form object. +@@ -1197,7 +1442,6 @@ FPDFTextObj_GetText(FPDF_PAGEOBJECT text_object, + FPDF_EXPORT int FPDF_CALLCONV + FPDFFormObj_CountObjects(FPDF_PAGEOBJECT form_object); + +-// Experimental API. + // Get page object in |form_object| at |index|. + // + // form_object - handle to a form object. +@@ -1207,21 +1451,6 @@ FPDFFormObj_CountObjects(FPDF_PAGEOBJECT form_object); + FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV + FPDFFormObj_GetObject(FPDF_PAGEOBJECT form_object, unsigned long index); + +-// Experimental API. +-// Get the transform matrix of a form object. +-// +-// form_object - handle to a form. +-// matrix - pointer to struct to receive the matrix value. +-// +-// The matrix is composed as: +-// |a c e| +-// |b d f| +-// and used to scale, rotate, shear and translate the form object. +-// +-// Returns TRUE on success. +-FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +-FPDFFormObj_GetMatrix(FPDF_PAGEOBJECT form_object, FS_MATRIX* matrix); +- + #ifdef __cplusplus + } // extern "C" + #endif // __cplusplus +diff --git a/public/fpdf_ext.h b/public/fpdf_ext.h +index b1784dd66..068a977c1 100644 +--- a/public/fpdf_ext.h ++++ b/public/fpdf_ext.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/public/fpdf_flatten.h b/public/fpdf_flatten.h +index 614540e15..aba5186ba 100644 +--- a/public/fpdf_flatten.h ++++ b/public/fpdf_flatten.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/public/fpdf_formfill.h b/public/fpdf_formfill.h +index 95c24b4c4..2bcf7f7c1 100644 +--- a/public/fpdf_formfill.h ++++ b/public/fpdf_formfill.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -307,7 +307,9 @@ typedef struct _IPDF_JsPlatform { + int length); + + /* +- * Pointer to FPDF_FORMFILLINFO interface. ++ * Pointer for embedder-specific data. Unused by PDFium, and despite ++ * its name, can be any data the embedder desires, though traditionally ++ * a FPDF_FORMFILLINFO interface. + */ + void* m_pFormfillinfo; + +@@ -374,8 +376,17 @@ typedef struct _FPDF_SYSTEMTIME { + + typedef struct _FPDF_FORMFILLINFO { + /* +- * Version number of the interface. Currently must be 1 (when PDFium is built +- * without the XFA module) or must be 2 (when built with the XFA module). ++ * Version number of the interface. ++ * Version 1 contains stable interfaces. Version 2 has additional ++ * experimental interfaces. ++ * When PDFium is built without the XFA module, version can be 1 or 2. ++ * With version 1, only stable interfaces are called. With version 2, ++ * additional experimental interfaces are also called. ++ * When PDFium is built with the XFA module, version must be 2. ++ * All the XFA related interfaces are experimental. If PDFium is built with ++ * the XFA module and version 1 then none of the XFA related interfaces ++ * would be called. When PDFium is built with XFA module then the version ++ * must be 2. + */ + int version; + +@@ -570,8 +581,8 @@ typedef struct _FPDF_FORMFILLINFO { + * action, the implementation needs to load the page. + */ + FPDF_PAGE (*FFI_GetPage)(struct _FPDF_FORMFILLINFO* pThis, +- FPDF_DOCUMENT document, +- int nPageIndex); ++ FPDF_DOCUMENT document, ++ int nPageIndex); + + /* + * Method: FFI_GetCurrentPage +@@ -579,18 +590,19 @@ typedef struct _FPDF_FORMFILLINFO { + * Interface Version: + * 1 + * Implementation Required: +- * yes ++ * Yes when V8 support is present, otherwise unused. + * Parameters: + * pThis - Pointer to the interface structure itself. + * document - Handle to document. Returned by FPDF_LoadDocument(). + * Return value: + * Handle to the page. Returned by FPDF_LoadPage(). + * Comments: +- * The implementation is expected to keep track of the current page, +- * e.g. the current page can be the one that is most visible on screen. ++ * PDFium doesn't keep keep track of the "current page" (e.g. the one ++ * that is most visible on screen), so it must ask the embedder for ++ * this information. + */ + FPDF_PAGE (*FFI_GetCurrentPage)(struct _FPDF_FORMFILLINFO* pThis, +- FPDF_DOCUMENT document); ++ FPDF_DOCUMENT document); + + /* + * Method: FFI_GetRotation +@@ -627,8 +639,9 @@ typedef struct _FPDF_FORMFILLINFO { + * Return value: + * None. + * Comments: +- * See the named actions description of <> +- * for more details. ++ * See ISO 32000-1:2008, section 12.6.4.11 for descriptions of the ++ * standard named actions, but note that a document may supply any ++ * name of its choosing. + */ + void (*FFI_ExecuteNamedAction)(struct _FPDF_FORMFILLINFO* pThis, + FPDF_BYTESTRING namedAction); +@@ -671,6 +684,10 @@ typedef struct _FPDF_FORMFILLINFO { + * Return value: + * None. + * Comments: ++ * If the embedder is version 2 or higher and have implementation for ++ * FFI_DoURIActionWithKeyboardModifier, then ++ * FFI_DoURIActionWithKeyboardModifier takes precedence over ++ * FFI_DoURIAction. + * See the URI actions description of <> + * for more details. + */ +@@ -1066,9 +1083,59 @@ typedef struct _FPDF_FORMFILLINFO { + * TRUE indicates success, otherwise FALSE. + */ + FPDF_BOOL (*FFI_PutRequestURL)(struct _FPDF_FORMFILLINFO* pThis, +- FPDF_WIDESTRING wsURL, +- FPDF_WIDESTRING wsData, +- FPDF_WIDESTRING wsEncode); ++ FPDF_WIDESTRING wsURL, ++ FPDF_WIDESTRING wsData, ++ FPDF_WIDESTRING wsEncode); ++ ++ /* ++ * Method: FFI_OnFocusChange ++ * Called when the focused annotation is updated. ++ * Interface Version: ++ * Ignored if |version| < 2. ++ * Implementation Required: ++ * No ++ * Parameters: ++ * param - Pointer to the interface structure itself. ++ * annot - The focused annotation. ++ * page_index - Index number of the page which contains the ++ * focused annotation. 0 for the first page. ++ * Return value: ++ * None. ++ * Comments: ++ * This callback function is useful for implementing any view based ++ * action such as scrolling the annotation rect into view. The ++ * embedder should not copy and store the annot as its scope is ++ * limited to this call only. ++ */ ++ void (*FFI_OnFocusChange)(struct _FPDF_FORMFILLINFO* param, ++ FPDF_ANNOTATION annot, ++ int page_index); ++ ++ /** ++ * Method: FFI_DoURIActionWithKeyboardModifier ++ * Ask the implementation to navigate to a uniform resource identifier ++ * with the specified modifiers. ++ * Interface Version: ++ * Ignored if |version| < 2. ++ * Implementation Required: ++ * No ++ * Parameters: ++ * param - Pointer to the interface structure itself. ++ * uri - A byte string which indicates the uniform ++ * resource identifier, terminated by 0. ++ * modifiers - Keyboard modifier that indicates which of ++ * the virtual keys are down, if any. ++ * Return value: ++ * None. ++ * Comments: ++ * If the embedder who is version 2 and does not implement this API, ++ * then a call will be redirected to FFI_DoURIAction. ++ * See the URI actions description of <> ++ * for more details. ++ */ ++ void(*FFI_DoURIActionWithKeyboardModifier)(struct _FPDF_FORMFILLINFO* param, ++ FPDF_BYTESTRING uri, ++ int modifiers); + } FPDF_FORMFILLINFO; + + /* +@@ -1076,11 +1143,13 @@ typedef struct _FPDF_FORMFILLINFO { + * Initialize form fill environment. + * Parameters: + * document - Handle to document from FPDF_LoadDocument(). +- * pFormFillInfo - Pointer to a FPDF_FORMFILLINFO structure. ++ * formInfo - Pointer to a FPDF_FORMFILLINFO structure. + * Return Value: + * Handle to the form fill module, or NULL on failure. + * Comments: + * This function should be called before any form fill operation. ++ * The FPDF_FORMFILLINFO passed in via |formInfo| must remain valid until ++ * the returned FPDF_FORMHANDLE is closed. + */ + FPDF_EXPORT FPDF_FORMHANDLE FPDF_CALLCONV + FPDFDOC_InitFormFillEnvironment(FPDF_DOCUMENT document, +@@ -1106,7 +1175,7 @@ FPDFDOC_ExitFormFillEnvironment(FPDF_FORMHANDLE hHandle); + * functions. Should be invoked after user successfully loaded a + * PDF page, and FPDFDOC_InitFormFillEnvironment() has been invoked. + * Parameters: +- * hHandle - Handle to the form fill module, as eturned by ++ * hHandle - Handle to the form fill module, as returned by + * FPDFDOC_InitFormFillEnvironment(). + * Return Value: + * None. +@@ -1238,6 +1307,39 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnMouseMove(FPDF_FORMHANDLE hHandle, + double page_x, + double page_y); + ++/* ++ * Experimental API ++ * Function: FORM_OnMouseWheel ++ * Call this member function when the user scrolls the mouse wheel. ++ * Parameters: ++ * hHandle - Handle to the form fill module, as returned by ++ * FPDFDOC_InitFormFillEnvironment(). ++ * page - Handle to the page, as returned by FPDF_LoadPage(). ++ * modifier - Indicates whether various virtual keys are down. ++ * page_coord - Specifies the coordinates of the cursor in PDF user ++ * space. ++ * delta_x - Specifies the amount of wheel movement on the x-axis, ++ * in units of platform-agnostic wheel deltas. Negative ++ * values mean left. ++ * delta_y - Specifies the amount of wheel movement on the y-axis, ++ * in units of platform-agnostic wheel deltas. Negative ++ * values mean down. ++ * Return Value: ++ * True indicates success; otherwise false. ++ * Comments: ++ * For |delta_x| and |delta_y|, the caller must normalize ++ * platform-specific wheel deltas. e.g. On Windows, a delta value of 240 ++ * for a WM_MOUSEWHEEL event normalizes to 2, since Windows defines ++ * WHEEL_DELTA as 120. ++ */ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnMouseWheel( ++ FPDF_FORMHANDLE hHandle, ++ FPDF_PAGE page, ++ int modifier, ++ const FS_POINTF* page_coord, ++ int delta_x, ++ int delta_y); ++ + /* + * Function: FORM_OnFocus + * This function focuses the form annotation at a given point. If the +@@ -1266,7 +1368,7 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnFocus(FPDF_FORMHANDLE hHandle, + * Call this member function when the user presses the left + * mouse button. + * Parameters: +- * hHandle - Handle to the form fill module. as returned by ++ * hHandle - Handle to the form fill module, as returned by + * FPDFDOC_InitFormFillEnvironment(). + * page - Handle to the page, as returned by FPDF_LoadPage(). + * modifier - Indicates whether various virtual keys are down. +@@ -1302,7 +1404,7 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonDown(FPDF_FORMHANDLE hHandle, + * Parameters: + * hHandle - Handle to the form fill module, as returned by + * FPDFDOC_InitFormFillEnvironment(). +- * page - Handle to the page. as returned by FPDF_LoadPage(). ++ * page - Handle to the page, as returned by FPDF_LoadPage(). + * modifier - Indicates whether various virtual keys are down. + * page_x - Specifies the x-coordinate of the cursor in device. + * page_y - Specifies the y-coordinate of the cursor in device. +@@ -1358,9 +1460,10 @@ FORM_OnLButtonDoubleClick(FPDF_FORMHANDLE hHandle, + * hHandle - Handle to the form fill module, aseturned by + * FPDFDOC_InitFormFillEnvironment(). + * page - Handle to the page, as returned by FPDF_LoadPage(). +- * nKeyCode - Indicates whether various virtual keys are down. +- * modifier - Contains the scan code, key-transition code, +- * previous key state, and context code. ++ * nKeyCode - The virtual-key code of the given key (see ++ * fpdf_fwlevent.h for virtual key codes). ++ * modifier - Mask of key flags (see fpdf_fwlevent.h for key ++ * flag values). + * Return Value: + * True indicates success; otherwise false. + */ +@@ -1376,11 +1479,15 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyDown(FPDF_FORMHANDLE hHandle, + * hHandle - Handle to the form fill module, as returned by + * FPDFDOC_InitFormFillEnvironment(). + * page - Handle to the page, as returned by FPDF_LoadPage(). +- * nKeyCode - The virtual-key code of the given key. +- * modifier - Contains the scan code, key-transition code, +- * previous key state, and context code. ++ * nKeyCode - The virtual-key code of the given key (see ++ * fpdf_fwlevent.h for virtual key codes). ++ * modifier - Mask of key flags (see fpdf_fwlevent.h for key ++ * flag values). + * Return Value: + * True indicates success; otherwise false. ++ * Comments: ++ * Currently unimplemented and always returns false. PDFium reserves this ++ * API and may implement it in the future on an as-needed basis. + */ + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyUp(FPDF_FORMHANDLE hHandle, + FPDF_PAGE page, +@@ -1392,12 +1499,12 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyUp(FPDF_FORMHANDLE hHandle, + * Call this member function when a keystroke translates to a + * nonsystem character. + * Parameters: +- * hHandle - Handle to the form fill module, as returned by ++ * hHandle - Handle to the form fill module, as returned by + * FPDFDOC_InitFormFillEnvironment(). +- * page - Handle to the page, as returned by FPDF_LoadPage(). +- * nChar - The character code value of the key. +- * modifier - Contains the scan code, key-transition code, +- * previous key state, and context code. ++ * page - Handle to the page, as returned by FPDF_LoadPage(). ++ * nChar - The character code value itself. ++ * modifier - Mask of key flags (see fpdf_fwlevent.h for key ++ * flag values). + * Return Value: + * True indicates success; otherwise false. + */ +@@ -1434,7 +1541,7 @@ FORM_GetFocusedText(FPDF_FORMHANDLE hHandle, + * Call this function to obtain selected text within a form text + * field or form combobox text field. + * Parameters: +- * hHandle - Handle to the form fill module, asr eturned by ++ * hHandle - Handle to the form fill module, as returned by + * FPDFDOC_InitFormFillEnvironment(). + * page - Handle to the page, as returned by FPDF_LoadPage(). + * buffer - Buffer for holding the selected text, encoded in +@@ -1452,13 +1559,36 @@ FORM_GetSelectedText(FPDF_FORMHANDLE hHandle, + void* buffer, + unsigned long buflen); + ++/* ++ * Experimental API ++ * Function: FORM_ReplaceAndKeepSelection ++ * Call this function to replace the selected text in a form ++ * text field or user-editable form combobox text field with another ++ * text string (which can be empty or non-empty). If there is no ++ * selected text, this function will append the replacement text after ++ * the current caret position. After the insertion, the inserted text ++ * will be selected. ++ * Parameters: ++ * hHandle - Handle to the form fill module, as returned by ++ * FPDFDOC_InitFormFillEnvironment(). ++ * page - Handle to the page, as Returned by FPDF_LoadPage(). ++ * wsText - The text to be inserted, in UTF-16LE format. ++ * Return Value: ++ * None. ++ */ ++FPDF_EXPORT void FPDF_CALLCONV ++FORM_ReplaceAndKeepSelection(FPDF_FORMHANDLE hHandle, ++ FPDF_PAGE page, ++ FPDF_WIDESTRING wsText); ++ + /* + * Function: FORM_ReplaceSelection + * Call this function to replace the selected text in a form + * text field or user-editable form combobox text field with another + * text string (which can be empty or non-empty). If there is no + * selected text, this function will append the replacement text after +- * the current caret position. ++ * the current caret position. After the insertion, the selection range ++ * will be set to empty. + * Parameters: + * hHandle - Handle to the form fill module, as returned by + * FPDFDOC_InitFormFillEnvironment(). +@@ -1471,6 +1601,21 @@ FPDF_EXPORT void FPDF_CALLCONV FORM_ReplaceSelection(FPDF_FORMHANDLE hHandle, + FPDF_PAGE page, + FPDF_WIDESTRING wsText); + ++/* ++ * Experimental API ++ * Function: FORM_SelectAllText ++ * Call this function to select all the text within the currently focused ++ * form text field or form combobox text field. ++ * Parameters: ++ * hHandle - Handle to the form fill module, as returned by ++ * FPDFDOC_InitFormFillEnvironment(). ++ * page - Handle to the page, as returned by FPDF_LoadPage(). ++ * Return Value: ++ * Whether the operation succeeded or not. ++ */ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FORM_SelectAllText(FPDF_FORMHANDLE hHandle, FPDF_PAGE page); ++ + /* + * Function: FORM_CanUndo + * Find out if it is possible for the current focused widget in a given +@@ -1503,7 +1648,7 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_CanRedo(FPDF_FORMHANDLE hHandle, + * Function: FORM_Undo + * Make the current focussed widget perform an undo operation. + * Parameters: +- * hHandle - Handle to the form fill module. as returned by ++ * hHandle - Handle to the form fill module, as returned by + * FPDFDOC_InitFormFillEnvironment(). + * page - Handle to the page, as returned by FPDF_LoadPage(). + * Return Value: +@@ -1518,7 +1663,7 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_Undo(FPDF_FORMHANDLE hHandle, + * Parameters: + * hHandle - Handle to the form fill module, as returned by + * FPDFDOC_InitFormFillEnvironment(). +- * page - Handle to the page, as eturned by FPDF_LoadPage(). ++ * page - Handle to the page, as returned by FPDF_LoadPage(). + * Return Value: + * True if the redo operation succeeded. + */ +@@ -1539,6 +1684,50 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_Redo(FPDF_FORMHANDLE hHandle, + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FORM_ForceToKillFocus(FPDF_FORMHANDLE hHandle); + ++/* ++ * Experimental API. ++ * Function: FORM_GetFocusedAnnot. ++ * Call this member function to get the currently focused annotation. ++ * Parameters: ++ * handle - Handle to the form fill module, as returned by ++ * FPDFDOC_InitFormFillEnvironment(). ++ * page_index - Buffer to hold the index number of the page which ++ * contains the focused annotation. 0 for the first page. ++ * Can't be NULL. ++ * annot - Buffer to hold the focused annotation. Can't be NULL. ++ * Return Value: ++ * On success, return true and write to the out parameters. Otherwise return ++ * false and leave the out parameters unmodified. ++ * Comments: ++ * Not currently supported for XFA forms - will report no focused ++ * annotation. ++ * Must call FPDFPage_CloseAnnot() when the annotation returned in |annot| ++ * by this function is no longer needed. ++ * This will return true and set |page_index| to -1 and |annot| to NULL, if ++ * there is no focused annotation. ++ */ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FORM_GetFocusedAnnot(FPDF_FORMHANDLE handle, ++ int* page_index, ++ FPDF_ANNOTATION* annot); ++ ++/* ++ * Experimental API. ++ * Function: FORM_SetFocusedAnnot. ++ * Call this member function to set the currently focused annotation. ++ * Parameters: ++ * handle - Handle to the form fill module, as returned by ++ * FPDFDOC_InitFormFillEnvironment(). ++ * annot - Handle to an annotation. ++ * Return Value: ++ * True indicates success; otherwise false. ++ * Comments: ++ * |annot| can't be NULL. To kill focus, use FORM_ForceToKillFocus() ++ * instead. ++ */ ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FORM_SetFocusedAnnot(FPDF_FORMHANDLE handle, FPDF_ANNOTATION annot); ++ + // Form Field Types + // The names of the defines are stable, but the specific values associated with + // them are not, so do not hardcode their values. +@@ -1723,7 +1912,7 @@ FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLDraw(FPDF_FORMHANDLE hHandle, + int rotate, + int flags); + +-#ifdef _SKIA_SUPPORT_ ++#if defined(_SKIA_SUPPORT_) + FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLRecord(FPDF_FORMHANDLE hHandle, + FPDF_RECORDER recorder, + FPDF_PAGE page, +diff --git a/public/fpdf_fwlevent.h b/public/fpdf_fwlevent.h +index c8593e944..e61606d38 100644 +--- a/public/fpdf_fwlevent.h ++++ b/public/fpdf_fwlevent.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/public/fpdf_javascript.h b/public/fpdf_javascript.h +index 19f38106c..2b0240563 100644 +--- a/public/fpdf_javascript.h ++++ b/public/fpdf_javascript.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/public/fpdf_ppo.h b/public/fpdf_ppo.h +index e9f3f6663..1734bc682 100644 +--- a/public/fpdf_ppo.h ++++ b/public/fpdf_ppo.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,15 +14,38 @@ + extern "C" { + #endif + ++// Experimental API. ++// Import pages to a FPDF_DOCUMENT. ++// ++// dest_doc - The destination document for the pages. ++// src_doc - The document to be imported. ++// page_indices - An array of page indices to be imported. The first page is ++// zero. If |page_indices| is NULL, all pages from |src_doc| ++// are imported. ++// length - The length of the |page_indices| array. ++// index - The page index at which to insert the first imported page ++// into |dest_doc|. The first page is zero. ++// ++// Returns TRUE on success. Returns FALSE if any pages in |page_indices| is ++// invalid. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDF_ImportPagesByIndex(FPDF_DOCUMENT dest_doc, ++ FPDF_DOCUMENT src_doc, ++ const int* page_indices, ++ unsigned long length, ++ int index); ++ + // Import pages to a FPDF_DOCUMENT. + // + // dest_doc - The destination document for the pages. + // src_doc - The document to be imported. +-// pagerange - A page range string, Such as "1,3,5-7". If |pagerange| is NULL, +-// all pages from |src_doc| are imported. +-// index - The page index to insert at. ++// pagerange - A page range string, Such as "1,3,5-7". The first page is one. ++// If |pagerange| is NULL, all pages from |src_doc| are imported. ++// index - The page index at which to insert the first imported page into ++// |dest_doc|. The first page is zero. + // +-// Returns TRUE on success. ++// Returns TRUE on success. Returns FALSE if any pages in |pagerange| is ++// invalid or if |pagerange| cannot be read. + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_ImportPages(FPDF_DOCUMENT dest_doc, + FPDF_DOCUMENT src_doc, + FPDF_BYTESTRING pagerange, +@@ -52,6 +75,30 @@ FPDF_ImportNPagesToOne(FPDF_DOCUMENT src_doc, + size_t num_pages_on_x_axis, + size_t num_pages_on_y_axis); + ++// Experimental API. ++// Create a template to generate form xobjects from |src_doc|'s page at ++// |src_page_index|, for use in |dest_doc|. ++// ++// Returns a handle on success, or NULL on failure. Caller owns the newly ++// created object. ++FPDF_EXPORT FPDF_XOBJECT FPDF_CALLCONV ++FPDF_NewXObjectFromPage(FPDF_DOCUMENT dest_doc, ++ FPDF_DOCUMENT src_doc, ++ int src_page_index); ++ ++// Experimental API. ++// Close an FPDF_XOBJECT handle created by FPDF_NewXObjectFromPage(). ++// FPDF_PAGEOBJECTs created from the FPDF_XOBJECT handle are not affected. ++FPDF_EXPORT void FPDF_CALLCONV FPDF_CloseXObject(FPDF_XOBJECT xobject); ++ ++// Experimental API. ++// Create a new form object from an FPDF_XOBJECT object. ++// ++// Returns a new form object on success, or NULL on failure. Caller owns the ++// newly created object. ++FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV ++FPDF_NewFormObjectFromXObject(FPDF_XOBJECT xobject); ++ + // Copy the viewer preferences from |src_doc| into |dest_doc|. + // + // dest_doc - Document to write the viewer preferences into. +diff --git a/public/fpdf_progressive.h b/public/fpdf_progressive.h +index 99803b874..b146d48c3 100644 +--- a/public/fpdf_progressive.h ++++ b/public/fpdf_progressive.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -46,6 +46,51 @@ typedef struct _IFSDK_PAUSE { + void* user; + } IFSDK_PAUSE; + ++// Experimental API. ++// Function: FPDF_RenderPageBitmapWithColorScheme_Start ++// Start to render page contents to a device independent bitmap ++// progressively with a specified color scheme for the content. ++// Parameters: ++// bitmap - Handle to the device independent bitmap (as the ++// output buffer). Bitmap handle can be created by ++// FPDFBitmap_Create function. ++// page - Handle to the page as returned by FPDF_LoadPage ++// function. ++// start_x - Left pixel position of the display area in the ++// bitmap coordinate. ++// start_y - Top pixel position of the display area in the ++// bitmap coordinate. ++// size_x - Horizontal size (in pixels) for displaying the ++// page. ++// size_y - Vertical size (in pixels) for displaying the page. ++// rotate - Page orientation: 0 (normal), 1 (rotated 90 ++// degrees clockwise), 2 (rotated 180 degrees), ++// 3 (rotated 90 degrees counter-clockwise). ++// flags - 0 for normal display, or combination of flags ++// defined in fpdfview.h. With FPDF_ANNOT flag, it ++// renders all annotations that does not require ++// user-interaction, which are all annotations except ++// widget and popup annotations. ++// color_scheme - Color scheme to be used in rendering the |page|. ++// If null, this function will work similar to ++// FPDF_RenderPageBitmap_Start(). ++// pause - The IFSDK_PAUSE interface. A callback mechanism ++// allowing the page rendering process. ++// Return value: ++// Rendering Status. See flags for progressive process status for the ++// details. ++FPDF_EXPORT int FPDF_CALLCONV ++FPDF_RenderPageBitmapWithColorScheme_Start(FPDF_BITMAP bitmap, ++ FPDF_PAGE page, ++ int start_x, ++ int start_y, ++ int size_x, ++ int size_y, ++ int rotate, ++ int flags, ++ const FPDF_COLORSCHEME* color_scheme, ++ IFSDK_PAUSE* pause); ++ + // Function: FPDF_RenderPageBitmap_Start + // Start to render page contents to a device independent bitmap + // progressively. +@@ -73,7 +118,6 @@ typedef struct _IFSDK_PAUSE { + // Return value: + // Rendering Status. See flags for progressive process status for the + // details. +-// + FPDF_EXPORT int FPDF_CALLCONV FPDF_RenderPageBitmap_Start(FPDF_BITMAP bitmap, + FPDF_PAGE page, + int start_x, +diff --git a/public/fpdf_save.h b/public/fpdf_save.h +index 527d9428f..800d4e758 100644 +--- a/public/fpdf_save.h ++++ b/public/fpdf_save.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/public/fpdf_searchex.h b/public/fpdf_searchex.h +index 4b67786ff..9c980dbea 100644 +--- a/public/fpdf_searchex.h ++++ b/public/fpdf_searchex.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/public/fpdf_signature.h b/public/fpdf_signature.h +new file mode 100644 +index 000000000..9a075e5f8 +--- /dev/null ++++ b/public/fpdf_signature.h +@@ -0,0 +1,155 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef PUBLIC_FPDF_SIGNATURE_H_ ++#define PUBLIC_FPDF_SIGNATURE_H_ ++ ++// NOLINTNEXTLINE(build/include) ++#include "fpdfview.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif // __cplusplus ++ ++// Experimental API. ++// Function: FPDF_GetSignatureCount ++// Get total number of signatures in the document. ++// Parameters: ++// document - Handle to document. Returned by FPDF_LoadDocument(). ++// Return value: ++// Total number of signatures in the document on success, -1 on error. ++FPDF_EXPORT int FPDF_CALLCONV FPDF_GetSignatureCount(FPDF_DOCUMENT document); ++ ++// Experimental API. ++// Function: FPDF_GetSignatureObject ++// Get the Nth signature of the document. ++// Parameters: ++// document - Handle to document. Returned by FPDF_LoadDocument(). ++// index - Index into the array of signatures of the document. ++// Return value: ++// Returns the handle to the signature, or NULL on failure. The caller ++// does not take ownership of the returned FPDF_SIGNATURE. Instead, it ++// remains valid until FPDF_CloseDocument() is called for the document. ++FPDF_EXPORT FPDF_SIGNATURE FPDF_CALLCONV ++FPDF_GetSignatureObject(FPDF_DOCUMENT document, int index); ++ ++// Experimental API. ++// Function: FPDFSignatureObj_GetContents ++// Get the contents of a signature object. ++// Parameters: ++// signature - Handle to the signature object. Returned by ++// FPDF_GetSignatureObject(). ++// buffer - The address of a buffer that receives the contents. ++// length - The size, in bytes, of |buffer|. ++// Return value: ++// Returns the number of bytes in the contents on success, 0 on error. ++// ++// For public-key signatures, |buffer| is either a DER-encoded PKCS#1 binary or ++// a DER-encoded PKCS#7 binary. If |length| is less than the returned length, or ++// |buffer| is NULL, |buffer| will not be modified. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFSignatureObj_GetContents(FPDF_SIGNATURE signature, ++ void* buffer, ++ unsigned long length); ++ ++// Experimental API. ++// Function: FPDFSignatureObj_GetByteRange ++// Get the byte range of a signature object. ++// Parameters: ++// signature - Handle to the signature object. Returned by ++// FPDF_GetSignatureObject(). ++// buffer - The address of a buffer that receives the ++// byte range. ++// length - The size, in ints, of |buffer|. ++// Return value: ++// Returns the number of ints in the byte range on ++// success, 0 on error. ++// ++// |buffer| is an array of pairs of integers (starting byte offset, ++// length in bytes) that describes the exact byte range for the digest ++// calculation. If |length| is less than the returned length, or ++// |buffer| is NULL, |buffer| will not be modified. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFSignatureObj_GetByteRange(FPDF_SIGNATURE signature, ++ int* buffer, ++ unsigned long length); ++ ++// Experimental API. ++// Function: FPDFSignatureObj_GetSubFilter ++// Get the encoding of the value of a signature object. ++// Parameters: ++// signature - Handle to the signature object. Returned by ++// FPDF_GetSignatureObject(). ++// buffer - The address of a buffer that receives the encoding. ++// length - The size, in bytes, of |buffer|. ++// Return value: ++// Returns the number of bytes in the encoding name (including the ++// trailing NUL character) on success, 0 on error. ++// ++// The |buffer| is always encoded in 7-bit ASCII. If |length| is less than the ++// returned length, or |buffer| is NULL, |buffer| will not be modified. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFSignatureObj_GetSubFilter(FPDF_SIGNATURE signature, ++ char* buffer, ++ unsigned long length); ++ ++// Experimental API. ++// Function: FPDFSignatureObj_GetReason ++// Get the reason (comment) of the signature object. ++// Parameters: ++// signature - Handle to the signature object. Returned by ++// FPDF_GetSignatureObject(). ++// buffer - The address of a buffer that receives the reason. ++// length - The size, in bytes, of |buffer|. ++// Return value: ++// Returns the number of bytes in the reason on success, 0 on error. ++// ++// Regardless of the platform, the |buffer| is always in UTF-16LE encoding. The ++// string is terminated by a UTF16 NUL character. If |length| is less than the ++// returned length, or |buffer| is NULL, |buffer| will not be modified. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFSignatureObj_GetReason(FPDF_SIGNATURE signature, ++ void* buffer, ++ unsigned long length); ++ ++// Experimental API. ++// Function: FPDFSignatureObj_GetTime ++// Get the time of signing of a signature object. ++// Parameters: ++// signature - Handle to the signature object. Returned by ++// FPDF_GetSignatureObject(). ++// buffer - The address of a buffer that receives the time. ++// length - The size, in bytes, of |buffer|. ++// Return value: ++// Returns the number of bytes in the encoding name (including the ++// trailing NUL character) on success, 0 on error. ++// ++// The |buffer| is always encoded in 7-bit ASCII. If |length| is less than the ++// returned length, or |buffer| is NULL, |buffer| will not be modified. ++// ++// The format of time is expected to be D:YYYYMMDDHHMMSS+XX'YY', i.e. it's ++// percision is seconds, with timezone information. This value should be used ++// only when the time of signing is not available in the (PKCS#7 binary) ++// signature. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDFSignatureObj_GetTime(FPDF_SIGNATURE signature, ++ char* buffer, ++ unsigned long length); ++ ++// Experimental API. ++// Function: FPDFSignatureObj_GetDocMDPPermission ++// Get the DocMDP permission of a signature object. ++// Parameters: ++// signature - Handle to the signature object. Returned by ++// FPDF_GetSignatureObject(). ++// Return value: ++// Returns the permission (1, 2 or 3) on success, 0 on error. ++FPDF_EXPORT unsigned int FPDF_CALLCONV ++FPDFSignatureObj_GetDocMDPPermission(FPDF_SIGNATURE signature); ++ ++#ifdef __cplusplus ++} // extern "C" ++#endif // __cplusplus ++ ++#endif // PUBLIC_FPDF_SIGNATURE_H_ +diff --git a/public/fpdf_structtree.h b/public/fpdf_structtree.h +index a8083d595..a6c158a51 100644 +--- a/public/fpdf_structtree.h ++++ b/public/fpdf_structtree.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -62,7 +62,7 @@ FPDF_StructTree_GetChildAtIndex(FPDF_STRUCTTREE struct_tree, int index); + // buffer - A buffer for output the alt text. May be NULL. + // buflen - The length of the buffer, in bytes. May be 0. + // Return value: +-// The number of bytes in the title, including the terminating NUL ++// The number of bytes in the alt text, including the terminating NUL + // character. The number of bytes is returned regardless of the + // |buffer| and |buflen| parameters. + // Comments: +@@ -75,6 +75,91 @@ FPDF_StructElement_GetAltText(FPDF_STRUCTELEMENT struct_element, + void* buffer, + unsigned long buflen); + ++// Experimental API. ++// Function: FPDF_StructElement_GetActualText ++// Get the actual text for a given element. ++// Parameters: ++// struct_element - Handle to the struct element. ++// buffer - A buffer for output the actual text. May be NULL. ++// buflen - The length of the buffer, in bytes. May be 0. ++// Return value: ++// The number of bytes in the actual text, including the terminating ++// NUL character. The number of bytes is returned regardless of the ++// |buffer| and |buflen| parameters. ++// Comments: ++// Regardless of the platform, the |buffer| is always in UTF-16LE ++// encoding. The string is terminated by a UTF16 NUL character. If ++// |buflen| is less than the required length, or |buffer| is NULL, ++// |buffer| will not be modified. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDF_StructElement_GetActualText(FPDF_STRUCTELEMENT struct_element, ++ void* buffer, ++ unsigned long buflen); ++ ++// Function: FPDF_StructElement_GetID ++// Get the ID for a given element. ++// Parameters: ++// struct_element - Handle to the struct element. ++// buffer - A buffer for output the ID string. May be NULL. ++// buflen - The length of the buffer, in bytes. May be 0. ++// Return value: ++// The number of bytes in the ID string, including the terminating NUL ++// character. The number of bytes is returned regardless of the ++// |buffer| and |buflen| parameters. ++// Comments: ++// Regardless of the platform, the |buffer| is always in UTF-16LE ++// encoding. The string is terminated by a UTF16 NUL character. If ++// |buflen| is less than the required length, or |buffer| is NULL, ++// |buffer| will not be modified. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDF_StructElement_GetID(FPDF_STRUCTELEMENT struct_element, ++ void* buffer, ++ unsigned long buflen); ++ ++// Experimental API. ++// Function: FPDF_StructElement_GetLang ++// Get the case-insensitive IETF BCP 47 language code for an element. ++// Parameters: ++// struct_element - Handle to the struct element. ++// buffer - A buffer for output the lang string. May be NULL. ++// buflen - The length of the buffer, in bytes. May be 0. ++// Return value: ++// The number of bytes in the ID string, including the terminating NUL ++// character. The number of bytes is returned regardless of the ++// |buffer| and |buflen| parameters. ++// Comments: ++// Regardless of the platform, the |buffer| is always in UTF-16LE ++// encoding. The string is terminated by a UTF16 NUL character. If ++// |buflen| is less than the required length, or |buffer| is NULL, ++// |buffer| will not be modified. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDF_StructElement_GetLang(FPDF_STRUCTELEMENT struct_element, ++ void* buffer, ++ unsigned long buflen); ++ ++// Experimental API. ++// Function: FPDF_StructElement_GetStringAttribute ++// Get a struct element attribute of type "name" or "string". ++// Parameters: ++// struct_element - Handle to the struct element. ++// attr_name - The name of the attribute to retrieve. ++// buffer - A buffer for output. May be NULL. ++// buflen - The length of the buffer, in bytes. May be 0. ++// Return value: ++// The number of bytes in the attribute value, including the ++// terminating NUL character. The number of bytes is returned ++// regardless of the |buffer| and |buflen| parameters. ++// Comments: ++// Regardless of the platform, the |buffer| is always in UTF-16LE ++// encoding. The string is terminated by a UTF16 NUL character. If ++// |buflen| is less than the required length, or |buffer| is NULL, ++// |buffer| will not be modified. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDF_StructElement_GetStringAttribute(FPDF_STRUCTELEMENT struct_element, ++ FPDF_BYTESTRING attr_name, ++ void* buffer, ++ unsigned long buflen); ++ + // Function: FPDF_StructElement_GetMarkedContentID + // Get the marked content ID for a given element. + // Parameters: +@@ -89,8 +174,8 @@ FPDF_StructElement_GetMarkedContentID(FPDF_STRUCTELEMENT struct_element); + // Get the type (/S) for a given element. + // Parameters: + // struct_element - Handle to the struct element. +-// buffer - A buffer for output. May be NULL. +-// buflen - The length of the buffer, in bytes. May be 0. ++// buffer - A buffer for output. May be NULL. ++// buflen - The length of the buffer, in bytes. May be 0. + // Return value: + // The number of bytes in the type, including the terminating NUL + // character. The number of bytes is returned regardless of the +@@ -105,6 +190,27 @@ FPDF_StructElement_GetType(FPDF_STRUCTELEMENT struct_element, + void* buffer, + unsigned long buflen); + ++// Experimental API. ++// Function: FPDF_StructElement_GetObjType ++// Get the object type (/Type) for a given element. ++// Parameters: ++// struct_element - Handle to the struct element. ++// buffer - A buffer for output. May be NULL. ++// buflen - The length of the buffer, in bytes. May be 0. ++// Return value: ++// The number of bytes in the object type, including the terminating ++// NUL character. The number of bytes is returned regardless of the ++// |buffer| and |buflen| parameters. ++// Comments: ++// Regardless of the platform, the |buffer| is always in UTF-16LE ++// encoding. The string is terminated by a UTF16 NUL character. If ++// |buflen| is less than the required length, or |buffer| is NULL, ++// |buffer| will not be modified. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDF_StructElement_GetObjType(FPDF_STRUCTELEMENT struct_element, ++ void* buffer, ++ unsigned long buflen); ++ + // Function: FPDF_StructElement_GetTitle + // Get the title (/T) for a given element. + // Parameters: +@@ -137,8 +243,8 @@ FPDF_StructElement_CountChildren(FPDF_STRUCTELEMENT struct_element); + // Function: FPDF_StructElement_GetChildAtIndex + // Get a child in the structure element. + // Parameters: +-// struct_tree - Handle to the struct element. +-// index - The index for the child, 0-based. ++// struct_element - Handle to the struct element. ++// index - The index for the child, 0-based. + // Return value: + // The child at the n-th index or NULL on error. + // Comments: +@@ -148,6 +254,199 @@ FPDF_EXPORT FPDF_STRUCTELEMENT FPDF_CALLCONV + FPDF_StructElement_GetChildAtIndex(FPDF_STRUCTELEMENT struct_element, + int index); + ++// Experimental API. ++// Function: FPDF_StructElement_GetParent ++// Get the parent of the structure element. ++// Parameters: ++// struct_element - Handle to the struct element. ++// Return value: ++// The parent structure element or NULL on error. ++// Comments: ++// If structure element is StructTreeRoot, then this function will ++// return NULL. ++FPDF_EXPORT FPDF_STRUCTELEMENT FPDF_CALLCONV ++FPDF_StructElement_GetParent(FPDF_STRUCTELEMENT struct_element); ++ ++// Function: FPDF_StructElement_GetAttributeCount ++// Count the number of attributes for the structure element. ++// Parameters: ++// struct_element - Handle to the struct element. ++// Return value: ++// The number of attributes, or -1 on error. ++FPDF_EXPORT int FPDF_CALLCONV ++FPDF_StructElement_GetAttributeCount(FPDF_STRUCTELEMENT struct_element); ++ ++// Experimental API. ++// Function: FPDF_StructElement_GetAttributeAtIndex ++// Get an attribute object in the structure element. ++// Parameters: ++// struct_element - Handle to the struct element. ++// index - The index for the attribute object, 0-based. ++// Return value: ++// The attribute object at the n-th index or NULL on error. ++// Comments: ++// If the attribute object exists but is not a dict, then this ++// function will return NULL. This will also return NULL for out of ++// bounds indices. ++FPDF_EXPORT FPDF_STRUCTELEMENT_ATTR FPDF_CALLCONV ++FPDF_StructElement_GetAttributeAtIndex(FPDF_STRUCTELEMENT struct_element, int index); ++ ++// Experimental API. ++// Function: FPDF_StructElement_Attr_GetCount ++// Count the number of attributes in a structure element attribute map. ++// Parameters: ++// struct_attribute - Handle to the struct element attribute. ++// Return value: ++// The number of attributes, or -1 on error. ++FPDF_EXPORT int FPDF_CALLCONV ++FPDF_StructElement_Attr_GetCount(FPDF_STRUCTELEMENT_ATTR struct_attribute); ++ ++ ++// Experimental API. ++// Function: FPDF_StructElement_Attr_GetName ++// Get the name of an attribute in a structure element attribute map. ++// Parameters: ++// struct_attribute - Handle to the struct element attribute. ++// index - The index of attribute in the map. ++// buffer - A buffer for output. May be NULL. This is only ++// modified if |buflen| is longer than the length ++// of the key. Optional, pass null to just ++// retrieve the size of the buffer needed. ++// buflen - The length of the buffer. ++// out_buflen - A pointer to variable that will receive the ++// minimum buffer size to contain the key. Not ++// filled if FALSE is returned. ++// Return value: ++// TRUE if the operation was successful, FALSE otherwise. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDF_StructElement_Attr_GetName(FPDF_STRUCTELEMENT_ATTR struct_attribute, ++ int index, ++ void* buffer, ++ unsigned long buflen, ++ unsigned long* out_buflen); ++ ++// Experimental API. ++// Function: FPDF_StructElement_Attr_GetType ++// Get the type of an attribute in a structure element attribute map. ++// Parameters: ++// struct_attribute - Handle to the struct element attribute. ++// name - The attribute name. ++// Return value: ++// Returns the type of the value, or FPDF_OBJECT_UNKNOWN in case of ++// failure. ++FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV ++FPDF_StructElement_Attr_GetType(FPDF_STRUCTELEMENT_ATTR struct_attribute, ++ FPDF_BYTESTRING name); ++ ++// Experimental API. ++// Function: FPDF_StructElement_Attr_GetBooleanValue ++// Get the value of a boolean attribute in an attribute map by name as ++// FPDF_BOOL. FPDF_StructElement_Attr_GetType() should have returned ++// FPDF_OBJECT_BOOLEAN for this property. ++// Parameters: ++// struct_attribute - Handle to the struct element attribute. ++// name - The attribute name. ++// out_value - A pointer to variable that will receive the ++// value. Not filled if false is returned. ++// Return value: ++// Returns TRUE if the name maps to a boolean value, FALSE otherwise. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDF_StructElement_Attr_GetBooleanValue( ++ FPDF_STRUCTELEMENT_ATTR struct_attribute, ++ FPDF_BYTESTRING name, ++ FPDF_BOOL* out_value); ++ ++// Experimental API. ++// Function: FPDF_StructElement_Attr_GetNumberValue ++// Get the value of a number attribute in an attribute map by name as ++// float. FPDF_StructElement_Attr_GetType() should have returned ++// FPDF_OBJECT_NUMBER for this property. ++// Parameters: ++// struct_attribute - Handle to the struct element attribute. ++// name - The attribute name. ++// out_value - A pointer to variable that will receive the ++// value. Not filled if false is returned. ++// Return value: ++// Returns TRUE if the name maps to a number value, FALSE otherwise. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDF_StructElement_Attr_GetNumberValue(FPDF_STRUCTELEMENT_ATTR struct_attribute, ++ FPDF_BYTESTRING name, ++ float* out_value); ++ ++// Experimental API. ++// Function: FPDF_StructElement_Attr_GetStringValue ++// Get the value of a string attribute in an attribute map by name as ++// string. FPDF_StructElement_Attr_GetType() should have returned ++// FPDF_OBJECT_STRING or FPDF_OBJECT_NAME for this property. ++// Parameters: ++// struct_attribute - Handle to the struct element attribute. ++// name - The attribute name. ++// buffer - A buffer for holding the returned key in ++// UTF-16LE. This is only modified if |buflen| is ++// longer than the length of the key. Optional, ++// pass null to just retrieve the size of the ++// buffer needed. ++// buflen - The length of the buffer. ++// out_buflen - A pointer to variable that will receive the ++// minimum buffer size to contain the key. Not ++// filled if FALSE is returned. ++// Return value: ++// Returns TRUE if the name maps to a string value, FALSE otherwise. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDF_StructElement_Attr_GetStringValue(FPDF_STRUCTELEMENT_ATTR struct_attribute, ++ FPDF_BYTESTRING name, ++ void* buffer, ++ unsigned long buflen, ++ unsigned long* out_buflen); ++ ++// Experimental API. ++// Function: FPDF_StructElement_Attr_GetBlobValue ++// Get the value of a blob attribute in an attribute map by name as ++// string. ++// Parameters: ++// struct_attribute - Handle to the struct element attribute. ++// name - The attribute name. ++// buffer - A buffer for holding the returned value. This ++// is only modified if |buflen| is at least as ++// long as the length of the value. Optional, pass ++// null to just retrieve the size of the buffer ++// needed. ++// buflen - The length of the buffer. ++// out_buflen - A pointer to variable that will receive the ++// minimum buffer size to contain the key. Not ++// filled if FALSE is returned. ++// Return value: ++// Returns TRUE if the name maps to a string value, FALSE otherwise. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV ++FPDF_StructElement_Attr_GetBlobValue(FPDF_STRUCTELEMENT_ATTR struct_attribute, ++ FPDF_BYTESTRING name, ++ void* buffer, ++ unsigned long buflen, ++ unsigned long* out_buflen); ++ ++// Experimental API. ++// Function: FPDF_StructElement_GetMarkedContentIdCount ++// Get the count of marked content ids for a given element. ++// Parameters: ++// struct_element - Handle to the struct element. ++// Return value: ++// The count of marked content ids or -1 if none exists. ++FPDF_EXPORT int FPDF_CALLCONV ++FPDF_StructElement_GetMarkedContentIdCount(FPDF_STRUCTELEMENT struct_element); ++ ++// Experimental API. ++// Function: FPDF_StructElement_GetMarkedContentIdAtIndex ++// Get the marked content id at a given index for a given element. ++// Parameters: ++// struct_element - Handle to the struct element. ++// index - The index of the marked content id, 0-based. ++// Return value: ++// The marked content ID of the element. If no ID exists, returns ++// -1. ++FPDF_EXPORT int FPDF_CALLCONV ++FPDF_StructElement_GetMarkedContentIdAtIndex(FPDF_STRUCTELEMENT struct_element, ++ int index); ++ + #ifdef __cplusplus + } // extern "C" + #endif +diff --git a/public/fpdf_sysfontinfo.h b/public/fpdf_sysfontinfo.h +index 936de962f..7dcf25de5 100644 +--- a/public/fpdf_sysfontinfo.h ++++ b/public/fpdf_sysfontinfo.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -19,8 +19,12 @@ + #define FXFONT_HANGEUL_CHARSET 129 + #define FXFONT_GB2312_CHARSET 134 + #define FXFONT_CHINESEBIG5_CHARSET 136 ++#define FXFONT_GREEK_CHARSET 161 ++#define FXFONT_VIETNAMESE_CHARSET 163 ++#define FXFONT_HEBREW_CHARSET 177 + #define FXFONT_ARABIC_CHARSET 178 + #define FXFONT_CYRILLIC_CHARSET 204 ++#define FXFONT_THAI_CHARSET 222 + #define FXFONT_EASTERNEUROPEAN_CHARSET 238 + + /* Font pitch and family flags */ +diff --git a/public/fpdf_text.h b/public/fpdf_text.h +index 65554e4ce..a5566a0cd 100644 +--- a/public/fpdf_text.h ++++ b/public/fpdf_text.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -74,6 +74,36 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountChars(FPDF_TEXTPAGE text_page); + FPDF_EXPORT unsigned int FPDF_CALLCONV + FPDFText_GetUnicode(FPDF_TEXTPAGE text_page, int index); + ++// Experimental API. ++// Function: FPDFText_IsGenerated ++// Get if a character in a page is generated by PDFium. ++// Parameters: ++// text_page - Handle to a text page information structure. ++// Returned by FPDFText_LoadPage function. ++// index - Zero-based index of the character. ++// Return value: ++// 1 if the character is generated by PDFium. ++// 0 if the character is not generated by PDFium. ++// -1 if there was an error. ++// ++FPDF_EXPORT int FPDF_CALLCONV ++FPDFText_IsGenerated(FPDF_TEXTPAGE text_page, int index); ++ ++// Experimental API. ++// Function: FPDFText_HasUnicodeMapError ++// Get if a character in a page has an invalid unicode mapping. ++// Parameters: ++// text_page - Handle to a text page information structure. ++// Returned by FPDFText_LoadPage function. ++// index - Zero-based index of the character. ++// Return value: ++// 1 if the character has an invalid unicode mapping. ++// 0 if the character has no known unicode mapping issues. ++// -1 if there was an error. ++// ++FPDF_EXPORT int FPDF_CALLCONV ++FPDFText_HasUnicodeMapError(FPDF_TEXTPAGE text_page, int index); ++ + // Function: FPDFText_GetFontSize + // Get the font size of a particular character. + // Parameters: +@@ -341,6 +371,10 @@ FPDFText_GetCharIndexAtPos(FPDF_TEXTPAGE text_page, + // trailing terminator. + // Comments: + // This function ignores characters without unicode information. ++// It returns all characters on the page, even those that are not ++// visible when the page has a cropbox. To filter out the characters ++// outside of the cropbox, use FPDF_GetPageBoundingBox() and ++// FPDFText_GetCharBox(). + // + FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetText(FPDF_TEXTPAGE text_page, + int start_index, +@@ -348,20 +382,22 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFText_GetText(FPDF_TEXTPAGE text_page, + unsigned short* result); + + // Function: FPDFText_CountRects +-// Count number of rectangular areas occupied by a segment of texts. ++// Counts number of rectangular areas occupied by a segment of text, ++// and caches the result for subsequent FPDFText_GetRect() calls. + // Parameters: + // text_page - Handle to a text page information structure. + // Returned by FPDFText_LoadPage function. +-// start_index - Index for the start characters. +-// count - Number of characters. ++// start_index - Index for the start character. ++// count - Number of characters, or -1 for all remaining. + // Return value: +-// Number of rectangles. Zero for error. ++// Number of rectangles, 0 if text_page is null, or -1 on bad ++// start_index. + // Comments: + // This function, along with FPDFText_GetRect can be used by + // applications to detect the position on the page for a text segment, +-// so proper areas can be highlighted. FPDFTEXT will automatically +-// merge small character boxes into bigger one if those characters +-// are on the same line and use same font settings. ++// so proper areas can be highlighted. The FPDFText_* functions will ++// automatically merge small character boxes into bigger one if those ++// characters are on the same line and use same font settings. + // + FPDF_EXPORT int FPDF_CALLCONV FPDFText_CountRects(FPDF_TEXTPAGE text_page, + int start_index, +diff --git a/public/fpdf_thumbnail.h b/public/fpdf_thumbnail.h +index 8ee858035..27b6d4971 100644 +--- a/public/fpdf_thumbnail.h ++++ b/public/fpdf_thumbnail.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/public/fpdf_transformpage.h b/public/fpdf_transformpage.h +index 38ef1130a..d5c5daaf8 100644 +--- a/public/fpdf_transformpage.h ++++ b/public/fpdf_transformpage.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -226,9 +226,9 @@ FPDFPageObj_TransformClipPath(FPDF_PAGEOBJECT page_object, + // page object - Handle to a page object. Returned by e.g. + // FPDFPage_GetObject(). + // +-// Caller does not take ownership of the returned FPDF_CLIPPATH. Instead, it +-// remains valid until FPDF_ClosePage() is called for the page containing +-// page_object. ++// Returns the handle to the clip path, or NULL on failure. The caller does not ++// take ownership of the returned FPDF_CLIPPATH. Instead, it remains valid until ++// FPDF_ClosePage() is called for the page containing |page_object|. + FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV + FPDFPageObj_GetClipPath(FPDF_PAGEOBJECT page_object); + +@@ -257,9 +257,9 @@ FPDFClipPath_CountPathSegments(FPDF_CLIPPATH clip_path, int path_index); + // path_index - the index of a path. + // segment_index - the index of a segment. + // +-// Returns the handle to the segment, or NULL on failure. The caller does not +-// take ownership of the returned FPDF_CLIPPATH. Instead, it remains valid until +-// FPDF_ClosePage() is called for the page containing page_object. ++// Returns the handle to the segment, or NULL on failure. The caller does not ++// take ownership of the returned FPDF_PATHSEGMENT. Instead, it remains valid ++// until FPDF_ClosePage() is called for the page containing |clip_path|. + FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV + FPDFClipPath_GetPathSegment(FPDF_CLIPPATH clip_path, + int path_index, +diff --git a/public/fpdfview.h b/public/fpdfview.h +index 0542b0541..206bfe2d5 100644 +--- a/public/fpdfview.h ++++ b/public/fpdfview.h +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -50,10 +50,11 @@ typedef enum { + FPDF_TEXTRENDERMODE_LAST = FPDF_TEXTRENDERMODE_CLIP, + } FPDF_TEXT_RENDERMODE; + +-// PDF types - use incomplete types (never completed) just for API type safety. ++// PDF types - use incomplete types (never completed) to force API type safety. + typedef struct fpdf_action_t__* FPDF_ACTION; + typedef struct fpdf_annotation_t__* FPDF_ANNOTATION; + typedef struct fpdf_attachment_t__* FPDF_ATTACHMENT; ++typedef struct fpdf_avail_t__* FPDF_AVAIL; + typedef struct fpdf_bitmap_t__* FPDF_BITMAP; + typedef struct fpdf_bookmark_t__* FPDF_BOOKMARK; + typedef struct fpdf_clippath_t__* FPDF_CLIPPATH; +@@ -61,20 +62,24 @@ typedef struct fpdf_dest_t__* FPDF_DEST; + typedef struct fpdf_document_t__* FPDF_DOCUMENT; + typedef struct fpdf_font_t__* FPDF_FONT; + typedef struct fpdf_form_handle_t__* FPDF_FORMHANDLE; ++typedef const struct fpdf_glyphpath_t__* FPDF_GLYPHPATH; + typedef struct fpdf_javascript_action_t* FPDF_JAVASCRIPT_ACTION; + typedef struct fpdf_link_t__* FPDF_LINK; + typedef struct fpdf_page_t__* FPDF_PAGE; + typedef struct fpdf_pagelink_t__* FPDF_PAGELINK; + typedef struct fpdf_pageobject_t__* FPDF_PAGEOBJECT; // (text, path, etc.) + typedef struct fpdf_pageobjectmark_t__* FPDF_PAGEOBJECTMARK; +-typedef struct fpdf_pagerange_t__* FPDF_PAGERANGE; ++typedef const struct fpdf_pagerange_t__* FPDF_PAGERANGE; + typedef const struct fpdf_pathsegment_t* FPDF_PATHSEGMENT; +-typedef void* FPDF_RECORDER; // Passed into skia. ++typedef void* FPDF_RECORDER; // Passed into Skia as a SkPictureRecorder. + typedef struct fpdf_schhandle_t__* FPDF_SCHHANDLE; ++typedef const struct fpdf_signature_t__* FPDF_SIGNATURE; + typedef struct fpdf_structelement_t__* FPDF_STRUCTELEMENT; ++typedef const struct fpdf_structelement_attr_t__* FPDF_STRUCTELEMENT_ATTR; + typedef struct fpdf_structtree_t__* FPDF_STRUCTTREE; + typedef struct fpdf_textpage_t__* FPDF_TEXTPAGE; + typedef struct fpdf_widget_t__* FPDF_WIDGET; ++typedef struct fpdf_xobject_t__* FPDF_XOBJECT; + + // Basic data types + typedef int FPDF_BOOL; +@@ -99,7 +104,7 @@ typedef const char* FPDF_BYTESTRING; + + // FPDFSDK always uses UTF-16LE encoded wide strings, each character uses 2 + // bytes (except surrogation), with the low byte first. +-typedef const unsigned short* FPDF_WIDESTRING; ++typedef const FPDF_WCHAR* FPDF_WIDESTRING; + + // Structure for persisting a string beyond the duration of a callback. + // Note: although represented as a char*, string may be interpreted as +@@ -168,6 +173,17 @@ typedef struct FS_POINTF_ { + // Const Pointer to FS_POINTF structure. + typedef const FS_POINTF* FS_LPCPOINTF; + ++typedef struct _FS_QUADPOINTSF { ++ FS_FLOAT x1; ++ FS_FLOAT y1; ++ FS_FLOAT x2; ++ FS_FLOAT y2; ++ FS_FLOAT x3; ++ FS_FLOAT y3; ++ FS_FLOAT x4; ++ FS_FLOAT y4; ++} FS_QUADPOINTSF; ++ + // Annotation enums. + typedef int FPDF_ANNOTATION_SUBTYPE; + typedef int FPDF_ANNOT_APPEARANCEMODE; +@@ -218,6 +234,15 @@ extern "C" { + // future. + FPDF_EXPORT void FPDF_CALLCONV FPDF_InitLibrary(); + ++// PDF renderer types - Experimental. ++// Selection of 2D graphics library to use for rendering to FPDF_BITMAPs. ++typedef enum { ++ // Anti-Grain Geometry - https://sourceforge.net/projects/agg/ ++ FPDF_RENDERERTYPE_AGG = 0, ++ // Skia - https://skia.org/ ++ FPDF_RENDERERTYPE_SKIA = 1, ++} FPDF_RENDERER_TYPE; ++ + // Process-wide options for initializing the library. + typedef struct FPDF_LIBRARY_CONFIG_ { + // Version number of the interface. Currently must be 2. +@@ -232,7 +257,7 @@ typedef struct FPDF_LIBRARY_CONFIG_ { + + // Version 2. + +- // pointer to the v8::Isolate to use, or NULL to force PDFium to create one. ++ // Pointer to the v8::Isolate to use, or NULL to force PDFium to create one. + void* m_pIsolate; + + // The embedder data slot to use in the v8::Isolate to store PDFium's +@@ -240,6 +265,22 @@ typedef struct FPDF_LIBRARY_CONFIG_ { + // [0, |v8::Internals::kNumIsolateDataLots|). Note that 0 is fine for most + // embedders. + unsigned int m_v8EmbedderSlot; ++ ++ // Version 3 - Experimental. ++ ++ // Pointer to the V8::Platform to use. ++ void* m_pPlatform; ++ ++ // Version 4 - Experimental. ++ ++ // Explicit specification of core renderer to use. |m_RendererType| must be ++ // a valid value for |FPDF_LIBRARY_CONFIG| versions of this level or higher, ++ // or else the initialization will fail with an immediate crash. ++ // Note that use of a specified |FPDF_RENDERER_TYPE| value for which the ++ // corresponding render library is not included in the build will similarly ++ // fail with an immediate crash. ++ FPDF_RENDERER_TYPE m_RendererType; ++ + } FPDF_LIBRARY_CONFIG; + + // Function: FPDF_InitLibraryWithConfig +@@ -282,37 +323,9 @@ FPDF_EXPORT void FPDF_CALLCONV FPDF_SetSandBoxPolicy(FPDF_DWORD policy, + FPDF_BOOL enable); + + #if defined(_WIN32) +-#if defined(PDFIUM_PRINT_TEXT_WITH_GDI) +-// Pointer to a helper function to make |font| with |text| of |text_length| +-// accessible when printing text with GDI. This is useful in sandboxed +-// environments where PDFium's access to GDI may be restricted. +-typedef void (*PDFiumEnsureTypefaceCharactersAccessible)(const LOGFONT* font, +- const wchar_t* text, +- size_t text_length); +- +-// Function: FPDF_SetTypefaceAccessibleFunc +-// Set the function pointer that makes GDI fonts available in sandboxed +-// environments. Experimental API. +-// Parameters: +-// func - A function pointer. See description above. +-// Return value: +-// None. +-FPDF_EXPORT void FPDF_CALLCONV +-FPDF_SetTypefaceAccessibleFunc(PDFiumEnsureTypefaceCharactersAccessible func); +- +-// Function: FPDF_SetPrintTextWithGDI +-// Set whether to use GDI to draw fonts when printing on Windows. +-// Experimental API. +-// Parameters: +-// use_gdi - Set to true to enable printing text with GDI. +-// Return value: +-// None. +-FPDF_EXPORT void FPDF_CALLCONV FPDF_SetPrintTextWithGDI(FPDF_BOOL use_gdi); +-#endif // PDFIUM_PRINT_TEXT_WITH_GDI +- ++// Experimental API. + // Function: FPDF_SetPrintMode + // Set printing mode when printing on Windows. +-// Experimental API. + // Parameters: + // mode - FPDF_PRINTMODE_EMF to output EMF (default) + // FPDF_PRINTMODE_TEXTONLY to output text only (for charstream +@@ -325,6 +338,14 @@ FPDF_EXPORT void FPDF_CALLCONV FPDF_SetPrintTextWithGDI(FPDF_BOOL use_gdi); + // PostScript via ExtEscape() in PASSTHROUGH mode. + // FPDF_PRINTMODE_POSTSCRIPT3_PASSTHROUGH to output level 3 + // PostScript via ExtEscape() in PASSTHROUGH mode. ++// FPDF_PRINTMODE_EMF_IMAGE_MASKS to output EMF, with more ++// efficient processing of documents containing image masks. ++// FPDF_PRINTMODE_POSTSCRIPT3_TYPE42 to output level 3 ++// PostScript with embedded Type 42 fonts, when applicable, into ++// EMF as a series of GDI comments. ++// FPDF_PRINTMODE_POSTSCRIPT3_TYPE42_PASSTHROUGH to output level ++// 3 PostScript with embedded Type 42 fonts, when applicable, ++// via ExtEscape() in PASSTHROUGH mode. + // Return value: + // True if successful, false if unsuccessful (typically invalid input). + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SetPrintMode(int mode); +@@ -344,6 +365,8 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SetPrintMode(int mode); + // If this function fails, you can use FPDF_GetLastError() to retrieve + // the reason why it failed. + // ++// The encoding for |file_path| is UTF-8. ++// + // The encoding for |password| can be either UTF-8 or Latin-1. PDFs, + // depending on the security handler revision, will only accept one or + // the other encoding. If |password|'s encoding and the PDF's expected +@@ -376,6 +399,33 @@ FPDF_LoadDocument(FPDF_STRING file_path, FPDF_BYTESTRING password); + FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV + FPDF_LoadMemDocument(const void* data_buf, int size, FPDF_BYTESTRING password); + ++// Experimental API. ++// Function: FPDF_LoadMemDocument64 ++// Open and load a PDF document from memory. ++// Parameters: ++// data_buf - Pointer to a buffer containing the PDF document. ++// size - Number of bytes in the PDF document. ++// password - A string used as the password for the PDF file. ++// If no password is needed, empty or NULL can be used. ++// Return value: ++// A handle to the loaded document, or NULL on failure. ++// Comments: ++// The memory buffer must remain valid when the document is open. ++// The loaded document can be closed by FPDF_CloseDocument. ++// If this function fails, you can use FPDF_GetLastError() to retrieve ++// the reason why it failed. ++// ++// See the comments for FPDF_LoadDocument() regarding the encoding for ++// |password|. ++// Notes: ++// If PDFium is built with the XFA module, the application should call ++// FPDF_LoadXFA() function after the PDF document loaded to support XFA ++// fields defined in the fpdfformfill.h file. ++FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV ++FPDF_LoadMemDocument64(const void* data_buf, ++ size_t size, ++ FPDF_BYTESTRING password); ++ + // Structure for custom file access. + typedef struct { + // File length, in bytes. +@@ -551,12 +601,13 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_GetFileVersion(FPDF_DOCUMENT doc, + // A 32-bit integer indicating error code as defined above. + // Comments: + // If the previous SDK call succeeded, the return value of this +-// function is not defined. ++// function is not defined. This function only works in conjunction ++// with APIs that mention FPDF_GetLastError() in their documentation. + FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetLastError(); + ++// Experimental API. + // Function: FPDF_DocumentHasValidCrossReferenceTable + // Whether the document's cross reference table is valid or not. +-// Experimental API. + // Parameters: + // document - Handle to a document. Returned by FPDF_LoadDocument. + // Return value: +@@ -569,6 +620,25 @@ FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetLastError(); + FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV + FPDF_DocumentHasValidCrossReferenceTable(FPDF_DOCUMENT document); + ++// Experimental API. ++// Function: FPDF_GetTrailerEnds ++// Get the byte offsets of trailer ends. ++// Parameters: ++// document - Handle to document. Returned by FPDF_LoadDocument(). ++// buffer - The address of a buffer that receives the ++// byte offsets. ++// length - The size, in ints, of |buffer|. ++// Return value: ++// Returns the number of ints in the buffer on success, 0 on error. ++// ++// |buffer| is an array of integers that describes the exact byte offsets of the ++// trailer ends in the document. If |length| is less than the returned length, ++// or |document| or |buffer| is NULL, |buffer| will not be modified. ++FPDF_EXPORT unsigned long FPDF_CALLCONV ++FPDF_GetTrailerEnds(FPDF_DOCUMENT document, ++ unsigned int* buffer, ++ unsigned long length); ++ + // Function: FPDF_GetDocPermission + // Get file permission flags of the document. + // Parameters: +@@ -707,7 +777,8 @@ FPDF_EXPORT int FPDF_CALLCONV FPDF_GetPageSizeByIndex(FPDF_DOCUMENT document, + // + // Set if annotations are to be rendered. + #define FPDF_ANNOT 0x01 +-// Set if using text rendering optimized for LCD display. ++// Set if using text rendering optimized for LCD display. This flag will only ++// take effect if anti-aliasing is enabled for text. + #define FPDF_LCD_TEXT 0x02 + // Don't use the native text output available on some platforms + #define FPDF_NO_NATIVETEXT 0x04 +@@ -723,7 +794,8 @@ FPDF_EXPORT int FPDF_CALLCONV FPDF_GetPageSizeByIndex(FPDF_DOCUMENT document, + #define FPDF_RENDER_FORCEHALFTONE 0x400 + // Render for printing. + #define FPDF_PRINTING 0x800 +-// Set to disable anti-aliasing on text. ++// Set to disable anti-aliasing on text. This flag will also disable LCD ++// optimization for text rendering. + #define FPDF_RENDER_NO_SMOOTHTEXT 0x1000 + // Set to disable anti-aliasing on images. + #define FPDF_RENDER_NO_SMOOTHIMAGE 0x2000 +@@ -732,6 +804,19 @@ FPDF_EXPORT int FPDF_CALLCONV FPDF_GetPageSizeByIndex(FPDF_DOCUMENT document, + // Set whether to render in a reverse Byte order, this flag is only used when + // rendering to a bitmap. + #define FPDF_REVERSE_BYTE_ORDER 0x10 ++// Set whether fill paths need to be stroked. This flag is only used when ++// FPDF_COLORSCHEME is passed in, since with a single fill color for paths the ++// boundaries of adjacent fill paths are less visible. ++#define FPDF_CONVERT_FILL_TO_STROKE 0x20 ++ ++// Struct for color scheme. ++// Each should be a 32-bit value specifying the color, in 8888 ARGB format. ++typedef struct FPDF_COLORSCHEME_ { ++ FPDF_DWORD path_fill_color; ++ FPDF_DWORD path_stroke_color; ++ FPDF_DWORD text_fill_color; ++ FPDF_DWORD text_stroke_color; ++} FPDF_COLORSCHEME; + + #ifdef _WIN32 + // Function: FPDF_RenderPage +@@ -825,7 +910,17 @@ FPDF_RenderPageBitmapWithMatrix(FPDF_BITMAP bitmap, + const FS_RECTF* clipping, + int flags); + +-#ifdef _SKIA_SUPPORT_ ++#if defined(_SKIA_SUPPORT_) ++// Experimental API. ++// Function: FPDF_RenderPageSkp ++// Render contents of a page to a Skia SkPictureRecorder. ++// Parameters: ++// page - Handle to the page. ++// size_x - Horizontal size (in pixels) for displaying the page. ++// size_y - Vertical size (in pixels) for displaying the page. ++// Return value: ++// The SkPictureRecorder that holds the rendering of the page, or NULL ++// on failure. Caller takes ownership of the returned result. + FPDF_EXPORT FPDF_RECORDER FPDF_CALLCONV FPDF_RenderPageSkp(FPDF_PAGE page, + int size_x, + int size_y); +@@ -993,9 +1088,15 @@ FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_Create(int width, + // above. + // first_scan - A pointer to the first byte of the first line if + // using an external buffer. If this parameter is NULL, +-// then the a new buffer will be created. +-// stride - Number of bytes for each scan line, for external +-// buffer only. ++// then a new buffer will be created. ++// stride - Number of bytes for each scan line. The value must ++// be 0 or greater. When the value is 0, ++// FPDFBitmap_CreateEx() will automatically calculate ++// the appropriate value using |width| and |format|. ++// When using an external buffer, it is recommended for ++// the caller to pass in the value. ++// When not using an external buffer, it is recommended ++// for the caller to pass in 0. + // Return value: + // The bitmap handle, or NULL if parameter error or out of memory. + // Comments: +@@ -1004,9 +1105,11 @@ FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_Create(int width, + // function can be used in any place that a FPDF_BITMAP handle is + // required. + // +-// If an external buffer is used, then the application should destroy +-// the buffer by itself. FPDFBitmap_Destroy function will not destroy +-// the buffer. ++// If an external buffer is used, then the caller should destroy the ++// buffer. FPDFBitmap_Destroy() will not destroy the buffer. ++// ++// It is recommended to use FPDFBitmap_GetStride() to get the stride ++// value. + FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_CreateEx(int width, + int height, + int format, +@@ -1070,8 +1173,7 @@ FPDF_EXPORT void FPDF_CALLCONV FPDFBitmap_FillRect(FPDF_BITMAP bitmap, + // then manipulate any color and/or alpha values for any pixels in the + // bitmap. + // +-// The data is in BGRA format. Where the A maybe unused if alpha was +-// not specified. ++// Use FPDFBitmap_GetFormat() to find out the format of the data. + FPDF_EXPORT void* FPDF_CALLCONV FPDFBitmap_GetBuffer(FPDF_BITMAP bitmap); + + // Function: FPDFBitmap_GetWidth +@@ -1142,9 +1244,9 @@ FPDF_VIEWERREF_GetNumCopies(FPDF_DOCUMENT document); + FPDF_EXPORT FPDF_PAGERANGE FPDF_CALLCONV + FPDF_VIEWERREF_GetPrintPageRange(FPDF_DOCUMENT document); + ++// Experimental API. + // Function: FPDF_VIEWERREF_GetPrintPageRangeCount + // Returns the number of elements in a FPDF_PAGERANGE. +-// Experimental API. + // Parameters: + // pagerange - Handle to the page range. + // Return value: +@@ -1152,9 +1254,9 @@ FPDF_VIEWERREF_GetPrintPageRange(FPDF_DOCUMENT document); + FPDF_EXPORT size_t FPDF_CALLCONV + FPDF_VIEWERREF_GetPrintPageRangeCount(FPDF_PAGERANGE pagerange); + ++// Experimental API. + // Function: FPDF_VIEWERREF_GetPrintPageRangeElement + // Returns an element from a FPDF_PAGERANGE. +-// Experimental API. + // Parameters: + // pagerange - Handle to the page range. + // index - Index of the element. +@@ -1240,6 +1342,65 @@ FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDF_GetNamedDest(FPDF_DOCUMENT document, + void* buffer, + long* buflen); + ++// Experimental API. ++// Function: FPDF_GetXFAPacketCount ++// Get the number of valid packets in the XFA entry. ++// Parameters: ++// document - Handle to the document. ++// Return value: ++// The number of valid packets, or -1 on error. ++FPDF_EXPORT int FPDF_CALLCONV FPDF_GetXFAPacketCount(FPDF_DOCUMENT document); ++ ++// Experimental API. ++// Function: FPDF_GetXFAPacketName ++// Get the name of a packet in the XFA array. ++// Parameters: ++// document - Handle to the document. ++// index - Index number of the packet. 0 for the first packet. ++// buffer - Buffer for holding the name of the XFA packet. ++// buflen - Length of |buffer| in bytes. ++// Return value: ++// The length of the packet name in bytes, or 0 on error. ++// ++// |document| must be valid and |index| must be in the range [0, N), where N is ++// the value returned by FPDF_GetXFAPacketCount(). ++// |buffer| is only modified if it is non-NULL and |buflen| is greater than or ++// equal to the length of the packet name. The packet name includes a ++// terminating NUL character. |buffer| is unmodified on error. ++FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetXFAPacketName( ++ FPDF_DOCUMENT document, ++ int index, ++ void* buffer, ++ unsigned long buflen); ++ ++// Experimental API. ++// Function: FPDF_GetXFAPacketContent ++// Get the content of a packet in the XFA array. ++// Parameters: ++// document - Handle to the document. ++// index - Index number of the packet. 0 for the first packet. ++// buffer - Buffer for holding the content of the XFA packet. ++// buflen - Length of |buffer| in bytes. ++// out_buflen - Pointer to the variable that will receive the minimum ++// buffer size needed to contain the content of the XFA ++// packet. ++// Return value: ++// Whether the operation succeeded or not. ++// ++// |document| must be valid and |index| must be in the range [0, N), where N is ++// the value returned by FPDF_GetXFAPacketCount(). |out_buflen| must not be ++// NULL. When the aforementioned arguments are valid, the operation succeeds, ++// and |out_buflen| receives the content size. |buffer| is only modified if ++// |buffer| is non-null and long enough to contain the content. Callers must ++// check both the return value and the input |buflen| is no less than the ++// returned |out_buflen| before using the data in |buffer|. ++FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_GetXFAPacketContent( ++ FPDF_DOCUMENT document, ++ int index, ++ void* buffer, ++ unsigned long buflen, ++ unsigned long* out_buflen); ++ + #ifdef PDF_ENABLE_V8 + // Function: FPDF_GetRecommendedV8Flags + // Returns a space-separated string of command line flags that are +@@ -1251,6 +1412,21 @@ FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDF_GetNamedDest(FPDF_DOCUMENT document, + // NUL-terminated string of the form "--flag1 --flag2". + // The caller must not attempt to modify or free the result. + FPDF_EXPORT const char* FPDF_CALLCONV FPDF_GetRecommendedV8Flags(); ++ ++// Experimental API. ++// Function: FPDF_GetArrayBufferAllocatorSharedInstance() ++// Helper function for initializing V8 isolates that will ++// use PDFium's internal memory management. ++// Parameters: ++// None. ++// Return Value: ++// Pointer to a suitable v8::ArrayBuffer::Allocator, returned ++// as void for C compatibility. ++// Notes: ++// Use is optional, but allows external creation of isolates ++// matching the ones PDFium will make when none is provided ++// via |FPDF_LIBRARY_CONFIG::m_pIsolate|. ++FPDF_EXPORT void* FPDF_CALLCONV FPDF_GetArrayBufferAllocatorSharedInstance(); + #endif // PDF_ENABLE_V8 + + #ifdef PDF_ENABLE_XFA +diff --git a/samples/BUILD.gn b/samples/BUILD.gn +index 581d05a40..ccf7ff85d 100644 +--- a/samples/BUILD.gn ++++ b/samples/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2014 PDFium Authors. All rights reserved. ++# Copyright 2014 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -13,15 +13,10 @@ group("samples") { + config("pdfium_samples_config") { + cflags = [] + ldflags = [] +- defines = [ +- "PNG_PREFIX", +- "PNG_USE_READ_MACROS", +- ] ++ defines = [] + include_dirs = [ ".." ] + if (pdf_use_skia) { + defines += [ "PDF_ENABLE_SKIA" ] +- } else if (pdf_use_skia_paths) { +- defines += [ "PDF_ENABLE_SKIA_PATHS" ] + } + if (is_asan) { + defines += [ "PDF_ENABLE_ASAN" ] +@@ -30,6 +25,9 @@ config("pdfium_samples_config") { + if (enable_callgrind) { + defines += [ "ENABLE_CALLGRIND" ] + } ++ if (build_with_chromium) { ++ defines += [ "BUILD_WITH_CHROMIUM" ] ++ } + } + + executable("pdfium_test") { +@@ -50,7 +48,7 @@ executable("pdfium_test") { + deps = [ + "../:pdfium_public_headers", + "../fpdfsdk", +- "../testing/:test_support", ++ "../testing:test_support", + "../testing/image_diff", + "../third_party:pdfium_base", + "//build/win:default_exe_manifest", +@@ -65,7 +63,14 @@ executable("pdfium_test") { + include_dirs = [ "//v8" ] + configs += [ "//v8:external_startup_data" ] + } +- if (pdf_use_skia || pdf_use_skia_paths) { ++ if (pdf_use_skia) { + deps += [ "//skia" ] ++ if (build_with_chromium) { ++ sources += [ ++ "chromium_support/discardable_memory_allocator.cc", ++ "chromium_support/discardable_memory_allocator.h", ++ ] ++ deps += [ "//base/test:test_support" ] ++ } + } + } +diff --git a/samples/chromium_support/DEPS b/samples/chromium_support/DEPS +new file mode 100644 +index 000000000..75f092651 +--- /dev/null ++++ b/samples/chromium_support/DEPS +@@ -0,0 +1,3 @@ ++include_rules = [ ++ '+base/test/test_discardable_memory_allocator.h', ++] +diff --git a/samples/chromium_support/discardable_memory_allocator.cc b/samples/chromium_support/discardable_memory_allocator.cc +new file mode 100644 +index 000000000..f89bb1c5c +--- /dev/null ++++ b/samples/chromium_support/discardable_memory_allocator.cc +@@ -0,0 +1,16 @@ ++// Copyright 2023 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "samples/chromium_support/discardable_memory_allocator.h" ++ ++#include "base/test/test_discardable_memory_allocator.h" ++ ++namespace chromium_support { ++ ++void InitializeDiscardableMemoryAllocator() { ++ static base::TestDiscardableMemoryAllocator test_memory_allocator; ++ base::DiscardableMemoryAllocator::SetInstance(&test_memory_allocator); ++} ++ ++} // namespace chromium_support +diff --git a/samples/chromium_support/discardable_memory_allocator.h b/samples/chromium_support/discardable_memory_allocator.h +new file mode 100644 +index 000000000..1f91f70e4 +--- /dev/null ++++ b/samples/chromium_support/discardable_memory_allocator.h +@@ -0,0 +1,14 @@ ++// Copyright 2023 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef SAMPLES_CHROMIUM_SUPPORT_DISCARDABLE_MEMORY_ALLOCATOR_H_ ++#define SAMPLES_CHROMIUM_SUPPORT_DISCARDABLE_MEMORY_ALLOCATOR_H_ ++ ++namespace chromium_support { ++ ++void InitializeDiscardableMemoryAllocator(); ++ ++} // namespace chromium_support ++ ++#endif // SAMPLES_CHROMIUM_SUPPORT_DISCARDABLE_MEMORY_ALLOCATOR_H_ +diff --git a/samples/pdfium_test.cc b/samples/pdfium_test.cc +index dc539dd34..2693a5627 100644 +--- a/samples/pdfium_test.cc ++++ b/samples/pdfium_test.cc +@@ -1,12 +1,13 @@ +-// Copyright (c) 2010 The Chromium Authors. All rights reserved. ++// Copyright 2010 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include ++#include + #include + #include + #include + ++#include + #include + #include + #include +@@ -14,14 +15,10 @@ + #include + #include + +-#if defined PDF_ENABLE_SKIA && !defined _SKIA_SUPPORT_ ++#if defined(PDF_ENABLE_SKIA) && !defined(_SKIA_SUPPORT_) + #define _SKIA_SUPPORT_ + #endif + +-#if defined PDF_ENABLE_SKIA_PATHS && !defined _SKIA_SUPPORT_PATHS_ +-#define _SKIA_SUPPORT_PATHS_ +-#endif +- + #include "public/cpp/fpdf_scopers.h" + #include "public/fpdf_annot.h" + #include "public/fpdf_attachment.h" +@@ -36,28 +33,48 @@ + #include "samples/pdfium_test_dump_helper.h" + #include "samples/pdfium_test_event_helper.h" + #include "samples/pdfium_test_write_helper.h" ++#include "testing/command_line_helpers.h" ++#include "testing/font_renamer.h" + #include "testing/fx_string_testhelpers.h" + #include "testing/test_loader.h" + #include "testing/utils/file_util.h" + #include "testing/utils/hash.h" + #include "testing/utils/path_service.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/optional.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" + + #ifdef _WIN32 ++#include ++#include + #include + #else + #include +-#endif ++#endif // _WIN32 + + #ifdef ENABLE_CALLGRIND + #include + #endif // ENABLE_CALLGRIND + ++#ifdef PDF_ENABLE_SKIA ++#include "third_party/skia/include/core/SkCanvas.h" // nogncheck ++#include "third_party/skia/include/core/SkColor.h" // nogncheck ++#include "third_party/skia/include/core/SkPicture.h" // nogncheck ++#include "third_party/skia/include/core/SkPictureRecorder.h" // nogncheck ++#include "third_party/skia/include/core/SkPixmap.h" // nogncheck ++#include "third_party/skia/include/core/SkRefCnt.h" // nogncheck ++#include "third_party/skia/include/core/SkSurface.h" // nogncheck ++ ++#ifdef BUILD_WITH_CHROMIUM ++#include "samples/chromium_support/discardable_memory_allocator.h" // nogncheck ++#endif ++#endif // PDF_ENABLE_SKIA ++ + #ifdef PDF_ENABLE_V8 + #include "testing/v8_initializer.h" + #include "v8/include/libplatform/libplatform.h" +-#include "v8/include/v8.h" ++#include "v8/include/v8-array-buffer.h" ++#include "v8/include/v8-isolate.h" ++#include "v8/include/v8-platform.h" ++#include "v8/include/v8-snapshot.h" + #endif // PDF_ENABLE_V8 + + #ifdef _WIN32 +@@ -66,7 +83,7 @@ + #define R_OK 4 + #endif + +-// wordexp is a POSIX function that is only available on OSX and non-Android ++// wordexp is a POSIX function that is only available on macOS and non-Android + // Linux platforms. + #if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__)) + #define WORDEXP_AVAILABLE +@@ -76,27 +93,28 @@ + #include + #endif // WORDEXP_AVAILABLE + +-enum OutputFormat { +- OUTPUT_NONE, +- OUTPUT_PAGEINFO, +- OUTPUT_STRUCTURE, +- OUTPUT_TEXT, +- OUTPUT_PPM, +- OUTPUT_PNG, +- OUTPUT_ANNOT, ++namespace { ++ ++enum class OutputFormat { ++ kNone, ++ kPageInfo, ++ kStructure, ++ kText, ++ kPpm, ++ kPng, ++ kAnnot, + #ifdef _WIN32 +- OUTPUT_BMP, +- OUTPUT_EMF, +- OUTPUT_PS2, +- OUTPUT_PS3, ++ kBmp, ++ kEmf, ++ kPs2, ++ kPs3, ++ kPs3Type42, + #endif + #ifdef PDF_ENABLE_SKIA +- OUTPUT_SKP, ++ kSkp, + #endif + }; + +-namespace { +- + struct Options { + Options() = default; + +@@ -108,6 +126,8 @@ struct Options { + bool lcd_text = false; + bool no_nativetext = false; + bool grayscale = false; ++ bool forced_color = false; ++ bool fill_to_stroke = false; + bool limit_cache = false; + bool force_halftone = false; + bool printing = false; +@@ -117,11 +137,14 @@ struct Options { + bool reverse_byte_order = false; + bool save_attachments = false; + bool save_images = false; ++ bool save_rendered_images = false; + bool save_thumbnails = false; + bool save_thumbnails_decoded = false; + bool save_thumbnails_raw = false; ++ absl::optional use_renderer_type; + #ifdef PDF_ENABLE_V8 + bool disable_javascript = false; ++ std::string js_flags; // Extra flags to pass to v8 init. + #ifdef PDF_ENABLE_XFA + bool disable_xfa = false; + #endif // PDF_ENABLE_XFA +@@ -134,7 +157,8 @@ struct Options { + #if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__)) + bool linux_no_system_fonts = false; + #endif +- OutputFormat output_format = OUTPUT_NONE; ++ bool croscore_font_names = false; ++ OutputFormat output_format = OutputFormat::kNone; + std::string password; + std::string scale_factor_as_string; + std::string exe_path; +@@ -153,6 +177,8 @@ int PageRenderFlagsFromOptions(const Options& options) { + flags |= FPDF_NO_NATIVETEXT; + if (options.grayscale) + flags |= FPDF_GRAYSCALE; ++ if (options.fill_to_stroke) ++ flags |= FPDF_CONVERT_FILL_TO_STROKE; + if (options.limit_cache) + flags |= FPDF_RENDER_LIMITEDIMAGECACHE; + if (options.force_halftone) +@@ -170,7 +196,7 @@ int PageRenderFlagsFromOptions(const Options& options) { + return flags; + } + +-Optional ExpandDirectoryPath(const std::string& path) { ++absl::optional ExpandDirectoryPath(const std::string& path) { + #if defined(WORDEXP_AVAILABLE) + wordexp_t expansion; + if (wordexp(path.c_str(), &expansion, 0) != 0 || expansion.we_wordc < 1) { +@@ -179,7 +205,7 @@ Optional ExpandDirectoryPath(const std::string& path) { + } + // Need to contruct the return value before hand, since wordfree will + // deallocate |expansion|. +- Optional ret_val = {expansion.we_wordv[0]}; ++ absl::optional ret_val = {expansion.we_wordv[0]}; + wordfree(&expansion); + return ret_val; + #else +@@ -187,7 +213,7 @@ Optional ExpandDirectoryPath(const std::string& path) { + #endif // WORDEXP_AVAILABLE + } + +-Optional GetCustomFontPath(const Options& options) { ++absl::optional GetCustomFontPath(const Options& options) { + #if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__)) + // Set custom font path to an empty path. This avoids the fallback to default + // font paths. +@@ -197,7 +223,7 @@ Optional GetCustomFontPath(const Options& options) { + + // No custom font path. Use default. + if (options.font_directory.empty()) +- return pdfium::nullopt; ++ return absl::nullopt; + + // Set custom font path to |options.font_directory|. + return options.font_directory.c_str(); +@@ -218,13 +244,18 @@ FPDF_FORMFILLINFO_PDFiumTest* ToPDFiumTestFormFillInfo( + return static_cast(form_fill_info); + } + +-void OutputMD5Hash(const char* file_name, const uint8_t* buffer, int len) { ++void OutputMD5Hash(const char* file_name, pdfium::span output) { + // Get the MD5 hash and write it to stdout. +- std::string hash = GenerateMD5Base16(buffer, len); ++ std::string hash = GenerateMD5Base16(output); + printf("MD5:%s:%s\n", file_name, hash.c_str()); + } + + #ifdef PDF_ENABLE_V8 ++ ++struct V8IsolateDeleter { ++ inline void operator()(v8::Isolate* ptr) { ptr->Dispose(); } ++}; ++ + // These example JS platform callback handlers are entirely optional, + // and exist here to show the flow of information from a document back + // to the embedder. +@@ -328,6 +359,22 @@ int ExampleFieldBrowse(IPDF_JSPLATFORM*, void* file_path, int length) { + } + #endif // PDF_ENABLE_V8 + ++#ifdef PDF_ENABLE_XFA ++FPDF_BOOL ExamplePopupMenu(FPDF_FORMFILLINFO* pInfo, ++ FPDF_PAGE page, ++ FPDF_WIDGET always_null, ++ int flags, ++ float x, ++ float y) { ++ printf("Popup: x=%2.1f, y=%2.1f, flags=0x%x\n", x, y, flags); ++ return true; ++} ++#endif // PDF_ENABLE_XFA ++ ++void ExampleNamedAction(FPDF_FORMFILLINFO* pInfo, FPDF_BYTESTRING name) { ++ printf("Execute named action: %s\n", name); ++} ++ + void ExampleUnsupportedHandler(UNSUPPORT_INFO*, int type) { + std::string feature = "Unknown"; + switch (type) { +@@ -372,17 +419,6 @@ void ExampleUnsupportedHandler(UNSUPPORT_INFO*, int type) { + printf("Unsupported feature: %s.\n", feature.c_str()); + } + +-// |arg| is expected to be "--key=value", and |key| is "--key=". +-bool ParseSwitchKeyValue(const std::string& arg, +- const std::string& key, +- std::string* value) { +- if (arg.size() <= key.size() || arg.compare(0, key.size(), key) != 0) +- return false; +- +- *value = arg.substr(key.size()); +- return true; +-} +- + bool ParseCommandLine(const std::vector& args, + Options* options, + std::vector* files) { +@@ -410,6 +446,10 @@ bool ParseCommandLine(const std::vector& args, + options->no_nativetext = true; + } else if (cur_arg == "--grayscale") { + options->grayscale = true; ++ } else if (cur_arg == "--forced-color") { ++ options->forced_color = true; ++ } else if (cur_arg == "--fill-to-stroke") { ++ options->fill_to_stroke = true; + } else if (cur_arg == "--limit-cache") { + options->limit_cache = true; + } else if (cur_arg == "--force-halftone") { +@@ -427,16 +467,51 @@ bool ParseCommandLine(const std::vector& args, + } else if (cur_arg == "--save-attachments") { + options->save_attachments = true; + } else if (cur_arg == "--save-images") { ++ if (options->save_rendered_images) { ++ fprintf(stderr, ++ "--save-rendered-images conflicts with --save-images\n"); ++ return false; ++ } + options->save_images = true; ++ } else if (cur_arg == "--save-rendered-images") { ++ if (options->save_images) { ++ fprintf(stderr, ++ "--save-images conflicts with --save-rendered-images\n"); ++ return false; ++ } ++ options->save_rendered_images = true; + } else if (cur_arg == "--save-thumbs") { + options->save_thumbnails = true; + } else if (cur_arg == "--save-thumbs-dec") { + options->save_thumbnails_decoded = true; + } else if (cur_arg == "--save-thumbs-raw") { + options->save_thumbnails_raw = true; ++#if defined(_SKIA_SUPPORT_) ++ } else if (ParseSwitchKeyValue(cur_arg, "--use-renderer=", &value)) { ++ if (options->use_renderer_type.has_value()) { ++ fprintf(stderr, "Duplicate --use-renderer argument\n"); ++ return false; ++ } ++ if (value == "agg") { ++ options->use_renderer_type = FPDF_RENDERERTYPE_AGG; ++ } else if (value == "skia") { ++ options->use_renderer_type = FPDF_RENDERERTYPE_SKIA; ++ } else { ++ fprintf(stderr, ++ "Invalid --use-renderer argument, value must be one of agg or " ++ "skia\n"); ++ return false; ++ } ++#endif // defined(_SKIA_SUPPORT_) + #ifdef PDF_ENABLE_V8 + } else if (cur_arg == "--disable-javascript") { + options->disable_javascript = true; ++ } else if (ParseSwitchKeyValue(cur_arg, "--js-flags=", &value)) { ++ if (!options->js_flags.empty()) { ++ fprintf(stderr, "Duplicate --js-flags argument\n"); ++ return false; ++ } ++ options->js_flags = value; + #ifdef PDF_ENABLE_XFA + } else if (cur_arg == "--disable-xfa") { + options->disable_xfa = true; +@@ -450,37 +525,39 @@ bool ParseCommandLine(const std::vector& args, + } else if (cur_arg == "--no-system-fonts") { + options->linux_no_system_fonts = true; + #endif ++ } else if (cur_arg == "--croscore-font-names") { ++ options->croscore_font_names = true; + } else if (cur_arg == "--ppm") { +- if (options->output_format != OUTPUT_NONE) { ++ if (options->output_format != OutputFormat::kNone) { + fprintf(stderr, "Duplicate or conflicting --ppm argument\n"); + return false; + } +- options->output_format = OUTPUT_PPM; ++ options->output_format = OutputFormat::kPpm; + } else if (cur_arg == "--png") { +- if (options->output_format != OUTPUT_NONE) { ++ if (options->output_format != OutputFormat::kNone) { + fprintf(stderr, "Duplicate or conflicting --png argument\n"); + return false; + } +- options->output_format = OUTPUT_PNG; ++ options->output_format = OutputFormat::kPng; + } else if (cur_arg == "--txt") { +- if (options->output_format != OUTPUT_NONE) { ++ if (options->output_format != OutputFormat::kNone) { + fprintf(stderr, "Duplicate or conflicting --txt argument\n"); + return false; + } +- options->output_format = OUTPUT_TEXT; ++ options->output_format = OutputFormat::kText; + } else if (cur_arg == "--annot") { +- if (options->output_format != OUTPUT_NONE) { ++ if (options->output_format != OutputFormat::kNone) { + fprintf(stderr, "Duplicate or conflicting --annot argument\n"); + return false; + } +- options->output_format = OUTPUT_ANNOT; ++ options->output_format = OutputFormat::kAnnot; + #ifdef PDF_ENABLE_SKIA + } else if (cur_arg == "--skp") { +- if (options->output_format != OUTPUT_NONE) { ++ if (options->output_format != OutputFormat::kNone) { + fprintf(stderr, "Duplicate or conflicting --skp argument\n"); + return false; + } +- options->output_format = OUTPUT_SKP; ++ options->output_format = OutputFormat::kSkp; + #endif // PDF_ENABLE_SKIA + } else if (ParseSwitchKeyValue(cur_arg, "--font-dir=", &value)) { + if (!options->font_directory.empty()) { +@@ -488,8 +565,8 @@ bool ParseCommandLine(const std::vector& args, + return false; + } + std::string path = value; +- auto expanded_path = ExpandDirectoryPath(path); +- if (!expanded_path) { ++ absl::optional expanded_path = ExpandDirectoryPath(path); ++ if (!expanded_path.has_value()) { + fprintf(stderr, "Failed to expand --font-dir, %s\n", path.c_str()); + return false; + } +@@ -504,29 +581,35 @@ bool ParseCommandLine(const std::vector& args, + + #ifdef _WIN32 + } else if (cur_arg == "--emf") { +- if (options->output_format != OUTPUT_NONE) { ++ if (options->output_format != OutputFormat::kNone) { + fprintf(stderr, "Duplicate or conflicting --emf argument\n"); + return false; + } +- options->output_format = OUTPUT_EMF; ++ options->output_format = OutputFormat::kEmf; + } else if (cur_arg == "--ps2") { +- if (options->output_format != OUTPUT_NONE) { ++ if (options->output_format != OutputFormat::kNone) { + fprintf(stderr, "Duplicate or conflicting --ps2 argument\n"); + return false; + } +- options->output_format = OUTPUT_PS2; ++ options->output_format = OutputFormat::kPs2; + } else if (cur_arg == "--ps3") { +- if (options->output_format != OUTPUT_NONE) { ++ if (options->output_format != OutputFormat::kNone) { + fprintf(stderr, "Duplicate or conflicting --ps3 argument\n"); + return false; + } +- options->output_format = OUTPUT_PS3; ++ options->output_format = OutputFormat::kPs3; ++ } else if (cur_arg == "--ps3-type42") { ++ if (options->output_format != OutputFormat::kNone) { ++ fprintf(stderr, "Duplicate or conflicting --ps3-type42 argument\n"); ++ return false; ++ } ++ options->output_format = OutputFormat::kPs3Type42; + } else if (cur_arg == "--bmp") { +- if (options->output_format != OUTPUT_NONE) { ++ if (options->output_format != OutputFormat::kNone) { + fprintf(stderr, "Duplicate or conflicting --bmp argument\n"); + return false; + } +- options->output_format = OUTPUT_BMP; ++ options->output_format = OutputFormat::kBmp; + #endif // _WIN32 + + #ifdef PDF_ENABLE_V8 +@@ -537,8 +620,8 @@ bool ParseCommandLine(const std::vector& args, + return false; + } + std::string path = value; +- auto expanded_path = ExpandDirectoryPath(path); +- if (!expanded_path) { ++ absl::optional expanded_path = ExpandDirectoryPath(path); ++ if (!expanded_path.has_value()) { + fprintf(stderr, "Failed to expand --bin-dir, %s\n", path.c_str()); + return false; + } +@@ -559,17 +642,17 @@ bool ParseCommandLine(const std::vector& args, + } + options->scale_factor_as_string = value; + } else if (cur_arg == "--show-pageinfo") { +- if (options->output_format != OUTPUT_NONE) { ++ if (options->output_format != OutputFormat::kNone) { + fprintf(stderr, "Duplicate or conflicting --show-pageinfo argument\n"); + return false; + } +- options->output_format = OUTPUT_PAGEINFO; ++ options->output_format = OutputFormat::kPageInfo; + } else if (cur_arg == "--show-structure") { +- if (options->output_format != OUTPUT_NONE) { ++ if (options->output_format != OutputFormat::kNone) { + fprintf(stderr, "Duplicate or conflicting --show-structure argument\n"); + return false; + } +- options->output_format = OUTPUT_STRUCTURE; ++ options->output_format = OutputFormat::kStructure; + } else if (ParseSwitchKeyValue(cur_arg, "--pages=", &value)) { + if (options->pages) { + fprintf(stderr, "Duplicate --pages argument\n"); +@@ -577,7 +660,7 @@ bool ParseCommandLine(const std::vector& args, + } + options->pages = true; + const std::string pages_string = value; +- size_t first_dash = pages_string.find("-"); ++ size_t first_dash = pages_string.find('-'); + if (first_dash == std::string::npos) { + std::stringstream(pages_string) >> options->first_page; + options->last_page = options->first_page; +@@ -681,31 +764,291 @@ FPDF_BOOL NeedToPauseNow(IFSDK_PAUSE* p) { + return true; + } + +-bool RenderPage(const std::string& name, +- FPDF_DOCUMENT doc, +- FPDF_FORMHANDLE form, +- FPDF_FORMFILLINFO_PDFiumTest* form_fill_info, +- const int page_index, +- const Options& options, +- const std::string& events) { ++// Renderer for a single page. ++class PageRenderer { ++ public: ++ virtual ~PageRenderer() = default; ++ ++ // Returns `true` if the rendered output exists. Must call `Finish()` first. ++ virtual bool HasOutput() const = 0; ++ ++ // Starts rendering the page, returning `false` on failure. ++ virtual bool Start() = 0; ++ ++ // Continues rendering the page, returning `false` when complete. ++ virtual bool Continue() { return false; } ++ ++ // Finishes rendering the page. ++ virtual void Finish(FPDF_FORMHANDLE form) = 0; ++ ++ // Writes rendered output to a file, returning `false` on failure. ++ virtual bool Write(const std::string& name, int page_index, bool md5) = 0; ++ ++ protected: ++ PageRenderer(FPDF_PAGE page, int width, int height, int flags) ++ : page_(page), width_(width), height_(height), flags_(flags) {} ++ ++ FPDF_PAGE page() { return page_; } ++ int width() const { return width_; } ++ int height() const { return height_; } ++ int flags() const { return flags_; } ++ ++ private: ++ FPDF_PAGE page_; ++ int width_; ++ int height_; ++ int flags_; ++}; ++ ++// Page renderer with bitmap output. ++class BitmapPageRenderer : public PageRenderer { ++ public: ++ // Function type that writes a bitmap to an image file. The function returns ++ // the name of the image file on success, or an empty name on failure. ++ // ++ // Intended for use with some of the `pdfium_test_write_helper.h` functions. ++ using BitmapWriter = std::string (*)(const char* pdf_name, ++ int num, ++ void* buffer, ++ int stride, ++ int width, ++ int height); ++ ++ bool HasOutput() const override { return !!bitmap_; } ++ ++ bool Start() override { ++ bool alpha = FPDFPage_HasTransparency(page()); ++ bitmap_.reset(FPDFBitmap_Create(/*width=*/width(), /*height=*/height(), ++ /*alpha=*/alpha)); ++ if (!bitmap_) ++ return false; ++ ++ FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF; ++ FPDFBitmap_FillRect(bitmap(), /*left=*/0, /*top=*/0, /*width=*/width(), ++ /*height=*/height(), /*color=*/fill_color); ++ return true; ++ } ++ ++ void Finish(FPDF_FORMHANDLE form) override { ++ FPDF_FFLDraw(form, bitmap(), page(), /*start_x=*/0, /*start_y=*/0, ++ /*size_x=*/width(), /*size_y=*/height(), /*rotate=*/0, ++ /*flags=*/flags()); ++ Idle(); ++ } ++ ++ bool Write(const std::string& name, int page_index, bool md5) override { ++ if (!writer_) ++ return false; ++ ++ int stride = FPDFBitmap_GetStride(bitmap()); ++ void* buffer = FPDFBitmap_GetBuffer(bitmap()); ++ std::string image_file_name = ++ writer_(name.c_str(), /*num=*/page_index, buffer, /*stride=*/stride, ++ /*width=*/width(), /*height=*/height()); ++ if (image_file_name.empty()) ++ return false; ++ ++ if (md5) { ++ // Write the filename and the MD5 of the buffer to stdout. ++ OutputMD5Hash(image_file_name.c_str(), ++ {static_cast(buffer), ++ static_cast(stride) * height()}); ++ } ++ return true; ++ } ++ ++ protected: ++ BitmapPageRenderer(FPDF_PAGE page, ++ int width, ++ int height, ++ int flags, ++ const std::function& idler, ++ BitmapWriter writer) ++ : PageRenderer(page, /*width=*/width, /*height=*/height, /*flags=*/flags), ++ idler_(idler), ++ writer_(writer) {} ++ ++ void Idle() const { idler_(); } ++ FPDF_BITMAP bitmap() { return bitmap_.get(); } ++ ++ private: ++ const std::function& idler_; ++ BitmapWriter writer_; ++ ScopedFPDFBitmap bitmap_; ++}; ++ ++// Bitmap page renderer completing in a single operation. ++class OneShotBitmapPageRenderer : public BitmapPageRenderer { ++ public: ++ OneShotBitmapPageRenderer(FPDF_PAGE page, ++ int width, ++ int height, ++ int flags, ++ const std::function& idler, ++ BitmapWriter writer) ++ : BitmapPageRenderer(page, ++ /*width=*/width, ++ /*height=*/height, ++ /*flags=*/flags, ++ idler, ++ writer) {} ++ ++ bool Start() override { ++ if (!BitmapPageRenderer::Start()) ++ return false; ++ ++ // Note, client programs probably want to use this method instead of the ++ // progressive calls. The progressive calls are if you need to pause the ++ // rendering to update the UI, the PDF renderer will break when possible. ++ FPDF_RenderPageBitmap(bitmap(), page(), /*start_x=*/0, /*start_y=*/0, ++ /*size_x=*/width(), /*size_y=*/height(), /*rotate=*/0, ++ /*flags=*/flags()); ++ return true; ++ } ++}; ++ ++// Bitmap page renderer completing over multiple operations. ++class ProgressiveBitmapPageRenderer : public BitmapPageRenderer { ++ public: ++ ProgressiveBitmapPageRenderer(FPDF_PAGE page, ++ int width, ++ int height, ++ int flags, ++ const std::function& idler, ++ BitmapWriter writer, ++ const FPDF_COLORSCHEME* color_scheme) ++ : BitmapPageRenderer(page, ++ /*width=*/width, ++ /*height=*/height, ++ /*flags=*/flags, ++ idler, ++ writer), ++ color_scheme_(color_scheme) { ++ pause_.version = 1; ++ pause_.NeedToPauseNow = &NeedToPauseNow; ++ } ++ ++ bool Start() override { ++ if (!BitmapPageRenderer::Start()) ++ return false; ++ ++ if (FPDF_RenderPageBitmapWithColorScheme_Start( ++ bitmap(), page(), /*start_x=*/0, /*start_y=*/0, /*size_x=*/width(), ++ /*size_y=*/height(), /*rotate=*/0, /*flags=*/flags(), color_scheme_, ++ &pause_) == FPDF_RENDER_TOBECONTINUED) { ++ to_be_continued_ = true; ++ } ++ return true; ++ } ++ ++ bool Continue() override { ++ if (to_be_continued_) { ++ to_be_continued_ = (FPDF_RenderPage_Continue(page(), &pause_) == ++ FPDF_RENDER_TOBECONTINUED); ++ } ++ return to_be_continued_; ++ } ++ ++ void Finish(FPDF_FORMHANDLE form) override { ++ BitmapPageRenderer::Finish(form); ++ FPDF_RenderPage_Close(page()); ++ Idle(); ++ } ++ ++ private: ++ const FPDF_COLORSCHEME* color_scheme_; ++ IFSDK_PAUSE pause_; ++ bool to_be_continued_ = false; ++}; ++ ++#ifdef PDF_ENABLE_SKIA ++class SkPicturePageRenderer : public PageRenderer { ++ public: ++ SkPicturePageRenderer(FPDF_PAGE page, int width, int height, int flags) ++ : PageRenderer(page, ++ /*width=*/width, ++ /*height=*/height, ++ /*flags=*/flags) {} ++ ++ bool HasOutput() const override { return !!picture_; } ++ ++ bool Start() override { ++ recorder_.reset(reinterpret_cast( ++ FPDF_RenderPageSkp(page(), /*size_x=*/width(), /*size_y=*/height()))); ++ return !!recorder_; ++ } ++ ++ void Finish(FPDF_FORMHANDLE form) override { ++ FPDF_FFLRecord(form, reinterpret_cast(recorder_.get()), ++ page(), /*start_x=*/0, /*start_y=*/0, /*size_x=*/width(), ++ /*size_y=*/height(), /*rotate=*/0, /*flags=*/0); ++ ++ picture_ = recorder_->finishRecordingAsPicture(); ++ recorder_.reset(); ++ } ++ ++ bool Write(const std::string& name, int page_index, bool md5) override { ++ std::string image_file_name = WriteSkp(name.c_str(), page_index, *picture_); ++ if (image_file_name.empty()) ++ return false; ++ ++ if (md5) { ++ // Play back the `SkPicture` so we can take a hash of the result. ++ sk_sp surface = SkSurface::MakeRasterN32Premul( ++ /*width=*/width(), /*height=*/height()); ++ if (!surface) ++ return false; ++ ++ // Must clear to white before replay to match initial `CFX_DIBitmap`. ++ surface->getCanvas()->clear(SK_ColorWHITE); ++ surface->getCanvas()->drawPicture(picture_); ++ ++ // Write the filename and the MD5 of the buffer to stdout. ++ SkPixmap pixmap; ++ if (!surface->peekPixels(&pixmap)) ++ return false; ++ ++ OutputMD5Hash(image_file_name.c_str(), ++ {static_cast(pixmap.addr()), ++ pixmap.computeByteSize()}); ++ } ++ return true; ++ } ++ ++ private: ++ std::unique_ptr recorder_; ++ sk_sp picture_; ++}; ++#endif // PDF_ENABLE_SKIA ++ ++bool ProcessPage(const std::string& name, ++ FPDF_DOCUMENT doc, ++ FPDF_FORMHANDLE form, ++ FPDF_FORMFILLINFO_PDFiumTest* form_fill_info, ++ const int page_index, ++ const Options& options, ++ const std::string& events, ++ const std::function& idler) { + FPDF_PAGE page = GetPageForIndex(form_fill_info, doc, page_index); + if (!page) + return false; + if (options.send_events) +- SendPageEvents(form, page, events); ++ SendPageEvents(form, page, events, idler); + if (options.save_images) + WriteImages(page, name.c_str(), page_index); ++ if (options.save_rendered_images) ++ WriteRenderedImages(doc, page, name.c_str(), page_index); + if (options.save_thumbnails) + WriteThumbnail(page, name.c_str(), page_index); + if (options.save_thumbnails_decoded) + WriteDecodedThumbnailStream(page, name.c_str(), page_index); + if (options.save_thumbnails_raw) + WriteRawThumbnailStream(page, name.c_str(), page_index); +- if (options.output_format == OUTPUT_PAGEINFO) { ++ if (options.output_format == OutputFormat::kPageInfo) { + DumpPageInfo(page, page_index); + return true; + } +- if (options.output_format == OUTPUT_STRUCTURE) { ++ if (options.output_format == OutputFormat::kStructure) { + DumpPageStructure(page, page_index); + return true; + } +@@ -715,108 +1058,109 @@ bool RenderPage(const std::string& name, + if (!options.scale_factor_as_string.empty()) + std::stringstream(options.scale_factor_as_string) >> scale; + +- auto width = static_cast(FPDF_GetPageWidthF(page) * scale); +- auto height = static_cast(FPDF_GetPageHeightF(page) * scale); +- int alpha = FPDFPage_HasTransparency(page) ? 1 : 0; +- ScopedFPDFBitmap bitmap(FPDFBitmap_Create(width, height, alpha)); ++ int width = static_cast(FPDF_GetPageWidthF(page) * scale); ++ int height = static_cast(FPDF_GetPageHeightF(page) * scale); ++ int flags = PageRenderFlagsFromOptions(options); + +- if (bitmap) { +- FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF; +- FPDFBitmap_FillRect(bitmap.get(), 0, 0, width, height, fill_color); ++ std::unique_ptr renderer; ++#ifdef PDF_ENABLE_SKIA ++ if (options.output_format == OutputFormat::kSkp) { ++ renderer = std::make_unique( ++ page, /*width=*/width, /*height=*/height, /*flags=*/flags); ++ } else { ++#else ++ { ++#endif // PDF_ENABLE_SKIA ++ BitmapPageRenderer::BitmapWriter writer; ++ switch (options.output_format) { ++#ifdef _WIN32 ++ case OutputFormat::kBmp: ++ writer = WriteBmp; ++ break; ++#endif // _WIN32 ++ ++ case OutputFormat::kPng: ++ writer = WritePng; ++ break; ++ ++ case OutputFormat::kPpm: ++ writer = WritePpm; ++ break; ++ ++ default: ++ // Other formats won't write the output to a file, but still rasterize. ++ writer = nullptr; ++ break; ++ } + +- int flags = PageRenderFlagsFromOptions(options); + if (options.render_oneshot) { +- // Note, client programs probably want to use this method instead of the +- // progressive calls. The progressive calls are if you need to pause the +- // rendering to update the UI, the PDF renderer will break when possible. +- FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, width, height, 0, flags); ++ renderer = std::make_unique( ++ page, /*width=*/width, /*height=*/height, /*flags=*/flags, idler, ++ writer); + } else { +- IFSDK_PAUSE pause; +- pause.version = 1; +- pause.NeedToPauseNow = &NeedToPauseNow; +- +- int rv = FPDF_RenderPageBitmap_Start(bitmap.get(), page, 0, 0, width, +- height, 0, flags, &pause); +- while (rv == FPDF_RENDER_TOBECONTINUED) +- rv = FPDF_RenderPage_Continue(page, &pause); ++ // Client programs will be setting these values when rendering. ++ // This is a sample color scheme with distinct colors. ++ // Used only when `options.forced_color` is true. ++ FPDF_COLORSCHEME color_scheme; ++ color_scheme.path_fill_color = 0xFFFF0000; ++ color_scheme.path_stroke_color = 0xFF00FF00; ++ color_scheme.text_fill_color = 0xFF0000FF; ++ color_scheme.text_stroke_color = 0xFF00FFFF; ++ ++ renderer = std::make_unique( ++ page, /*width=*/width, /*height=*/height, /*flags=*/flags, idler, ++ writer, options.forced_color ? &color_scheme : nullptr); + } ++ } + +- FPDF_FFLDraw(form, bitmap.get(), page, 0, 0, width, height, 0, flags); +- +- if (!options.render_oneshot) +- FPDF_RenderPage_Close(page); +- +- int stride = FPDFBitmap_GetStride(bitmap.get()); +- void* buffer = FPDFBitmap_GetBuffer(bitmap.get()); ++ if (renderer->Start()) { ++ while (renderer->Continue()) ++ continue; ++ renderer->Finish(form); + +- std::string image_file_name; + switch (options.output_format) { + #ifdef _WIN32 +- case OUTPUT_BMP: +- image_file_name = +- WriteBmp(name.c_str(), page_index, buffer, stride, width, height); +- break; +- +- case OUTPUT_EMF: ++ case OutputFormat::kEmf: + WriteEmf(page, name.c_str(), page_index); + break; + +- case OUTPUT_PS2: +- case OUTPUT_PS3: ++ case OutputFormat::kPs2: ++ case OutputFormat::kPs3: + WritePS(page, name.c_str(), page_index); + break; +-#endif +- case OUTPUT_TEXT: +- WriteText(page, name.c_str(), page_index); +- break; +- +- case OUTPUT_ANNOT: +- WriteAnnot(page, name.c_str(), page_index); +- break; ++#endif // _WIN32 + +- case OUTPUT_PNG: +- image_file_name = +- WritePng(name.c_str(), page_index, buffer, stride, width, height); ++ case OutputFormat::kText: ++ WriteText(text_page.get(), name.c_str(), page_index); + break; + +- case OUTPUT_PPM: +- image_file_name = +- WritePpm(name.c_str(), page_index, buffer, stride, width, height); ++ case OutputFormat::kAnnot: ++ WriteAnnot(page, name.c_str(), page_index); + break; + +-#ifdef PDF_ENABLE_SKIA +- case OUTPUT_SKP: { +- std::unique_ptr recorder( +- reinterpret_cast( +- FPDF_RenderPageSkp(page, width, height))); +- FPDF_FFLRecord(form, recorder.get(), page, 0, 0, width, height, 0, 0); +- image_file_name = WriteSkp(name.c_str(), page_index, recorder.get()); +- } break; +-#endif + default: ++ renderer->Write(name, page_index, /*md5=*/options.md5); + break; + } +- +- // Write the filename and the MD5 of the buffer to stdout if we wrote a +- // file. +- if (options.md5 && !image_file_name.empty()) { +- OutputMD5Hash(image_file_name.c_str(), +- static_cast(buffer), stride * height); +- } + } else { + fprintf(stderr, "Page was too large to be rendered.\n"); + } + + FORM_DoPageAAction(page, form, FPDFPAGE_AACTION_CLOSE); ++ idler(); ++ + FORM_OnBeforeClosePage(page, form); +- return !!bitmap; ++ idler(); ++ ++ return renderer->HasOutput(); + } + +-void RenderPdf(const std::string& name, +- const char* buf, +- size_t len, +- const Options& options, +- const std::string& events) { ++void ProcessPdf(const std::string& name, ++ const char* buf, ++ size_t len, ++ const Options& options, ++ const std::string& events, ++ const std::function& idler) { + TestLoader loader({buf, len}); + + FPDF_FILEACCESS file_access = {}; +@@ -905,9 +1249,11 @@ void RenderPdf(const std::string& name, + form_callbacks.version = 2; + form_callbacks.xfa_disabled = + options.disable_xfa || options.disable_javascript; ++ form_callbacks.FFI_PopupMenu = ExamplePopupMenu; + #else // PDF_ENABLE_XFA + form_callbacks.version = 1; + #endif // PDF_ENABLE_XFA ++ form_callbacks.FFI_ExecuteNamedAction = ExampleNamedAction; + form_callbacks.FFI_GetPage = GetPageForIndex; + + #ifdef PDF_ENABLE_V8 +@@ -935,14 +1281,16 @@ void RenderPdf(const std::string& name, + FORM_DoDocumentOpenAction(form.get()); + + #if _WIN32 +- if (options.output_format == OUTPUT_PS2) ++ if (options.output_format == OutputFormat::kPs2) + FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT2); +- else if (options.output_format == OUTPUT_PS3) ++ else if (options.output_format == OutputFormat::kPs3) + FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT3); ++ else if (options.output_format == OutputFormat::kPs3Type42) ++ FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT3_TYPE42); + #endif + + int page_count = FPDF_GetPageCount(doc.get()); +- int rendered_pages = 0; ++ int processed_pages = 0; + int bad_pages = 0; + int first_page = options.pages ? options.first_page : 0; + int last_page = options.pages ? options.last_page + 1 : page_count; +@@ -958,101 +1306,107 @@ void RenderPdf(const std::string& name, + return; + } + } +- if (RenderPage(name, doc.get(), form.get(), &form_callbacks, i, options, +- events)) { +- ++rendered_pages; ++ if (ProcessPage(name, doc.get(), form.get(), &form_callbacks, i, options, ++ events, idler)) { ++ ++processed_pages; + } else { + ++bad_pages; + } ++ idler(); + } + + FORM_DoDocumentAAction(form.get(), FPDFDOC_AACTION_WC); +- fprintf(stderr, "Rendered %d pages.\n", rendered_pages); ++ idler(); ++ ++ fprintf(stderr, "Processed %d pages.\n", processed_pages); + if (bad_pages) + fprintf(stderr, "Skipped %d bad pages.\n", bad_pages); + } + + void ShowConfig() { + std::string config; +- std::string maybe_comma; ++ [[maybe_unused]] auto append_config = [&config](const char* name) { ++ if (!config.empty()) ++ config += ','; ++ config += name; ++ }; ++ + #ifdef PDF_ENABLE_V8 +- config.append(maybe_comma); +- config.append("V8"); +- maybe_comma = ","; +-#endif // PDF_ENABLE_V8 ++ append_config("V8"); ++#endif + #ifdef V8_USE_EXTERNAL_STARTUP_DATA +- config.append(maybe_comma); +- config.append("V8_EXTERNAL"); +- maybe_comma = ","; +-#endif // V8_USE_EXTERNAL_STARTUP_DATA ++ append_config("V8_EXTERNAL"); ++#endif + #ifdef PDF_ENABLE_XFA +- config.append(maybe_comma); +- config.append("XFA"); +- maybe_comma = ","; +-#endif // PDF_ENABLE_XFA ++ append_config("XFA"); ++#endif + #ifdef PDF_ENABLE_ASAN +- config.append(maybe_comma); +- config.append("ASAN"); +- maybe_comma = ","; +-#endif // PDF_ENABLE_ASAN +-#if defined(PDF_ENABLE_SKIA) +- config.append(maybe_comma); +- config.append("SKIA"); +- maybe_comma = ","; +-#elif defined(PDF_ENABLE_SKIA_PATHS) +- config.append(maybe_comma); +- config.append("SKIAPATHS"); +- maybe_comma = ","; ++ append_config("ASAN"); ++#endif ++#ifdef PDF_ENABLE_SKIA ++ append_config("SKIA"); + #endif + printf("%s\n", config.c_str()); + } + + constexpr char kUsageString[] = + "Usage: pdfium_test [OPTION] [FILE]...\n" +- " --show-config - print build options and exit\n" +- " --show-metadata - print the file metadata\n" +- " --show-pageinfo - print information about pages\n" +- " --show-structure - print the structure elements from the document\n" +- " --send-events - send input described by .evt file\n" +- " --mem-document - load document with FPDF_LoadMemDocument()\n" +- " --render-oneshot - render image without using progressive renderer\n" +- " --lcd-text - render text optimized for LCD displays\n" +- " --no-nativetext - render without using the native text output\n" +- " --grayscale - render grayscale output\n" +- " --limit-cache - render limiting image cache size\n" +- " --force-halftone - render forcing halftone\n" +- " --printing - render as if for printing\n" +- " --no-smoothtext - render disabling text anti-aliasing\n" +- " --no-smoothimage - render disabling image anti-alisasing\n" +- " --no-smoothpath - render disabling path anti-aliasing\n" +- " --reverse-byte-order - render to BGRA, if supported by the output " ++ " --show-config - print build options and exit\n" ++ " --show-metadata - print the file metadata\n" ++ " --show-pageinfo - print information about pages\n" ++ " --show-structure - print the structure elements from the " ++ "document\n" ++ " --send-events - send input described by .evt file\n" ++ " --mem-document - load document with FPDF_LoadMemDocument()\n" ++ " --render-oneshot - render image without using progressive " ++ "renderer\n" ++ " --lcd-text - render text optimized for LCD displays\n" ++ " --no-nativetext - render without using the native text output\n" ++ " --grayscale - render grayscale output\n" ++ " --forced-color - render in forced color mode\n" ++ " --fill-to-stroke - render fill as stroke in forced color mode\n" ++ " --limit-cache - render limiting image cache size\n" ++ " --force-halftone - render forcing halftone\n" ++ " --printing - render as if for printing\n" ++ " --no-smoothtext - render disabling text anti-aliasing\n" ++ " --no-smoothimage - render disabling image anti-alisasing\n" ++ " --no-smoothpath - render disabling path anti-aliasing\n" ++ " --reverse-byte-order - render to BGRA, if supported by the output " + "format\n" +- " --save-attachments - write embedded attachments " ++ " --save-attachments - write embedded attachments " + ".attachment.\n" +- " --save-images - write embedded images " ++ " --save-images - write raw embedded images " ++ "...png\n" ++ " --save-rendered-images - write embedded images as rendered on the page " + "...png\n" +- " --save-thumbs - write page thumbnails " ++ " --save-thumbs - write page thumbnails " + ".thumbnail..png\n" +- " --save-thumbs-dec - write page thumbnails' decoded stream data" ++ " --save-thumbs-dec - write page thumbnails' decoded stream data" + ".thumbnail.decoded..png\n" +- " --save-thumbs-raw - write page thumbnails' raw stream data" ++ " --save-thumbs-raw - write page thumbnails' raw stream data" + ".thumbnail.raw..png\n" ++#if defined(_SKIA_SUPPORT_) ++ " --use-renderer - renderer to use, one of [agg | skia]\n" ++#endif + #ifdef PDF_ENABLE_V8 +- " --disable-javascript - do not execute JS in PDF files\n" ++ " --disable-javascript - do not execute JS in PDF files\n" ++ " --js-flags= - additional flags to pass to V8\n" + #ifdef PDF_ENABLE_XFA +- " --disable-xfa - do not process XFA forms\n" ++ " --disable-xfa - do not process XFA forms\n" + #endif // PDF_ENABLE_XFA + #endif // PDF_ENABLE_V8 + #ifdef ENABLE_CALLGRIND +- " --callgrind-delim - delimit interesting section when using " ++ " --callgrind-delim - delimit interesting section when using " + "callgrind\n" + #endif + #if defined(__APPLE__) || (defined(__linux__) && !defined(__ANDROID__)) +- " --no-system-fonts - do not use system fonts, overrides --font-dir\n" ++ " --no-system-fonts - do not use system fonts, overrides --font-dir\n" + #endif +- " --bin-dir= - override path to v8 external data\n" +- " --font-dir= - override path to external fonts\n" +- " --scale= - scale output size by number (e.g. 0.5)\n" ++ " --croscore-font-names - use Croscore font names\n" ++ " --bin-dir= - override path to v8 external data\n" ++ " --font-dir= - override path to external fonts\n" ++ " --scale= - scale output size by number (e.g. 0.5)\n" ++ " --password= - password to decrypt the PDF with\n" + " --pages=(-) - only render the given 0-based page(s)\n" + #ifdef _WIN32 + " --bmp - write page images ..bmp\n" +@@ -1061,6 +1415,8 @@ constexpr char kUsageString[] = + "..ps\n" + " --ps3 - write page raw PostScript (Lvl 3) " + "..ps\n" ++ " --ps3-type42 - write page raw PostScript (Lvl 3 with Type 42 fonts) " ++ "..ps\n" + #endif + " --txt - write page text in UTF32-LE ..txt\n" + " --png - write page images ..png\n" +@@ -1073,9 +1429,25 @@ constexpr char kUsageString[] = + " --time= - Seconds since the epoch to set system time.\n" + ""; + ++void SetUpErrorHandling() { ++#ifdef _WIN32 ++ // Suppress various Windows error reporting mechanisms that can pop up dialog ++ // boxes and cause the program to hang. ++ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | ++ SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); ++ _set_error_mode(_OUT_TO_STDERR); ++ _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); ++ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); ++ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); ++#endif // _WIN32 ++} ++ + } // namespace + + int main(int argc, const char* argv[]) { ++ SetUpErrorHandling(); ++ setlocale(LC_CTYPE, "en_US.UTF-8"); // For printf() of high-characters. ++ + std::vector args(argv, argv + argc); + Options options; + std::vector files; +@@ -1094,28 +1466,62 @@ int main(int argc, const char* argv[]) { + return 1; + } + ++ const FPDF_RENDERER_TYPE renderer_type = ++ options.use_renderer_type.value_or(GetDefaultRendererType()); ++#if defined(PDF_ENABLE_SKIA) && defined(BUILD_WITH_CHROMIUM) ++ if (renderer_type == FPDF_RENDERERTYPE_SKIA) { ++ // Needed to support Chromium's copy of Skia, which uses a ++ // DiscardableMemoryAllocator. ++ chromium_support::InitializeDiscardableMemoryAllocator(); ++ } ++#endif ++ ++ FPDF_LIBRARY_CONFIG config; ++ config.version = 4; ++ config.m_pUserFontPaths = nullptr; ++ config.m_pIsolate = nullptr; ++ config.m_v8EmbedderSlot = 0; ++ config.m_pPlatform = nullptr; ++ config.m_RendererType = renderer_type; ++ ++ std::function idler = []() {}; + #ifdef PDF_ENABLE_V8 +- std::unique_ptr platform; + #ifdef V8_USE_EXTERNAL_STARTUP_DATA + v8::StartupData snapshot; ++#endif // V8_USE_EXTERNAL_STARTUP_DATA ++ std::unique_ptr platform; ++ std::unique_ptr isolate; + if (!options.disable_javascript) { ++#ifdef V8_USE_EXTERNAL_STARTUP_DATA + platform = InitializeV8ForPDFiumWithStartupData( +- options.exe_path, options.bin_directory, &snapshot); +- } ++ options.exe_path, options.js_flags, options.bin_directory, &snapshot); + #else // V8_USE_EXTERNAL_STARTUP_DATA +- if (!options.disable_javascript) +- platform = InitializeV8ForPDFium(options.exe_path); ++ platform = InitializeV8ForPDFium(options.exe_path, options.js_flags); + #endif // V8_USE_EXTERNAL_STARTUP_DATA ++ if (!platform) { ++ fprintf(stderr, "V8 initialization failed.\n"); ++ return 1; ++ } ++ config.m_pPlatform = platform.get(); ++ ++ v8::Isolate::CreateParams params; ++ params.array_buffer_allocator = static_cast( ++ FPDF_GetArrayBufferAllocatorSharedInstance()); ++ isolate.reset(v8::Isolate::New(params)); ++ config.m_pIsolate = isolate.get(); ++ ++ idler = [&platform, &isolate]() { ++ int task_count = 0; ++ while (v8::platform::PumpMessageLoop(platform.get(), isolate.get())) ++ ++task_count; ++ if (task_count) ++ fprintf(stderr, "Pumped %d tasks\n", task_count); ++ }; ++ } + #endif // PDF_ENABLE_V8 + +- FPDF_LIBRARY_CONFIG config; +- config.version = 2; +- config.m_pUserFontPaths = nullptr; +- config.m_pIsolate = nullptr; +- config.m_v8EmbedderSlot = 0; +- + const char* path_array[2] = {nullptr, nullptr}; +- Optional custom_font_path = GetCustomFontPath(options); ++ absl::optional custom_font_path = GetCustomFontPath(options); + if (custom_font_path.has_value()) { + path_array[0] = custom_font_path.value(); + config.m_pUserFontPaths = path_array; +@@ -1123,6 +1529,10 @@ int main(int argc, const char* argv[]) { + + FPDF_InitLibraryWithConfig(&config); + ++ std::unique_ptr font_renamer; ++ if (options.croscore_font_names) ++ font_renamer = std::make_unique(); ++ + UNSUPPORT_INFO unsupported_info = {}; + unsupported_info.version = 1; + unsupported_info.FSDK_UnSupport_Handler = ExampleUnsupportedHandler; +@@ -1143,7 +1553,7 @@ int main(int argc, const char* argv[]) { + GetFileContents(filename.c_str(), &file_length); + if (!file_contents) + continue; +- fprintf(stderr, "Rendering PDF file %s.\n", filename.c_str()); ++ fprintf(stderr, "Processing PDF file %s.\n", filename.c_str()); + + #ifdef ENABLE_CALLGRIND + if (options.callgrind_delimiters) +@@ -1169,7 +1579,9 @@ int main(int argc, const char* argv[]) { + } + } + } +- RenderPdf(filename, file_contents.get(), file_length, options, events); ++ ++ ProcessPdf(filename, file_contents.get(), file_length, options, events, ++ idler); + + #ifdef ENABLE_CALLGRIND + if (options.callgrind_delimiters) +@@ -1181,7 +1593,8 @@ int main(int argc, const char* argv[]) { + + #ifdef PDF_ENABLE_V8 + if (!options.disable_javascript) { +- v8::V8::ShutdownPlatform(); ++ isolate.reset(); ++ ShutdownV8ForPDFium(); + #ifdef V8_USE_EXTERNAL_STARTUP_DATA + free(const_cast(snapshot.data)); + #endif // V8_USE_EXTERNAL_STARTUP_DATA +diff --git a/samples/pdfium_test_dump_helper.cc b/samples/pdfium_test_dump_helper.cc +index 047a670a4..4d45390c7 100644 +--- a/samples/pdfium_test_dump_helper.cc ++++ b/samples/pdfium_test_dump_helper.cc +@@ -1,18 +1,20 @@ +-// Copyright 2018 The PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "samples/pdfium_test_dump_helper.h" + ++#include + #include + + #include + #include +-#include ++#include + #include + #include + + #include "public/cpp/fpdf_scopers.h" ++#include "public/fpdf_doc.h" + #include "public/fpdf_transformpage.h" + #include "testing/fx_string_testhelpers.h" + +@@ -43,24 +45,125 @@ void DumpBoxInfo(GetBoxInfoFunc func, + rect.left, rect.bottom, rect.right, rect.top); + } + ++void DumpStructureElementAttributes(FPDF_STRUCTELEMENT_ATTR attr, int indent) { ++ static const size_t kBufSize = 1024; ++ int count = FPDF_StructElement_Attr_GetCount(attr); ++ for (int i = 0; i < count; i++) { ++ char name[kBufSize] = {}; ++ unsigned long len = ULONG_MAX; ++ if (!FPDF_StructElement_Attr_GetName(attr, i, name, sizeof(name), &len)) { ++ printf("%*s FPDF_StructElement_Attr_GetName failed for %d\n", indent, "", ++ i); ++ continue; ++ } ++ ++ FPDF_OBJECT_TYPE type = FPDF_StructElement_Attr_GetType(attr, name); ++ if (type == FPDF_OBJECT_BOOLEAN) { ++ int value; ++ if (!FPDF_StructElement_Attr_GetBooleanValue(attr, name, &value)) { ++ printf("%*s %s: Failed FPDF_StructElement_Attr_GetBooleanValue\n", ++ indent, "", name); ++ continue; ++ } ++ printf("%*s %s: %d\n", indent, "", name, value); ++ } else if (type == FPDF_OBJECT_NUMBER) { ++ float value; ++ if (!FPDF_StructElement_Attr_GetNumberValue(attr, name, &value)) { ++ printf("%*s %s: Failed FPDF_StructElement_Attr_GetNumberValue\n", ++ indent, "", name); ++ continue; ++ } ++ printf("%*s %s: %f\n", indent, "", name, value); ++ } else if (type == FPDF_OBJECT_STRING || type == FPDF_OBJECT_NAME) { ++ unsigned short buffer[kBufSize] = {}; ++ if (!FPDF_StructElement_Attr_GetStringValue(attr, name, buffer, ++ sizeof(buffer), &len)) { ++ printf("%*s %s: Failed FPDF_StructElement_Attr_GetStringValue\n", ++ indent, "", name); ++ continue; ++ } ++ printf("%*s %s: %ls\n", indent, "", name, ++ ConvertToWString(buffer, len).c_str()); ++ } else if (type == FPDF_OBJECT_UNKNOWN) { ++ printf("%*s %s: FPDF_OBJECT_UNKNOWN\n", indent, "", name); ++ } else { ++ printf("%*s %s: NOT_YET_IMPLEMENTED: %d\n", indent, "", name, type); ++ } ++ } ++} ++ + } // namespace + + void DumpChildStructure(FPDF_STRUCTELEMENT child, int indent) { + static const size_t kBufSize = 1024; + unsigned short buf[kBufSize]; + unsigned long len = FPDF_StructElement_GetType(child, buf, kBufSize); +- printf("%*s%ls", indent * 2, "", ConvertToWString(buf, len).c_str()); ++ if (len > 0) ++ printf("%*s S: %ls\n", indent * 2, "", ConvertToWString(buf, len).c_str()); ++ ++ int attr_count = FPDF_StructElement_GetAttributeCount(child); ++ for (int i = 0; i < attr_count; i++) { ++ FPDF_STRUCTELEMENT_ATTR child_attr = ++ FPDF_StructElement_GetAttributeAtIndex(child, i); ++ if (!child_attr) ++ continue; ++ printf("%*s A[%d]:\n", indent * 2, "", i); ++ DumpStructureElementAttributes(child_attr, indent * 2 + 2); ++ } + + memset(buf, 0, sizeof(buf)); +- len = FPDF_StructElement_GetTitle(child, buf, kBufSize); +- if (len > 0) +- printf(": '%ls'", ConvertToWString(buf, len).c_str()); ++ len = FPDF_StructElement_GetActualText(child, buf, kBufSize); ++ if (len > 0) { ++ printf("%*s ActualText: %ls\n", indent * 2, "", ++ ConvertToWString(buf, len).c_str()); ++ } + + memset(buf, 0, sizeof(buf)); + len = FPDF_StructElement_GetAltText(child, buf, kBufSize); ++ if (len > 0) { ++ printf("%*s AltText: %ls\n", indent * 2, "", ++ ConvertToWString(buf, len).c_str()); ++ } ++ ++ memset(buf, 0, sizeof(buf)); ++ len = FPDF_StructElement_GetID(child, buf, kBufSize); + if (len > 0) +- printf(" (%ls)", ConvertToWString(buf, len).c_str()); +- printf("\n"); ++ printf("%*s ID: %ls\n", indent * 2, "", ConvertToWString(buf, len).c_str()); ++ ++ memset(buf, 0, sizeof(buf)); ++ len = FPDF_StructElement_GetLang(child, buf, kBufSize); ++ if (len > 0) { ++ printf("%*s Lang: %ls\n", indent * 2, "", ++ ConvertToWString(buf, len).c_str()); ++ } ++ ++ int mcid = FPDF_StructElement_GetMarkedContentID(child); ++ if (mcid != -1) ++ printf("%*s MCID: %d\n", indent * 2, "", mcid); ++ ++ FPDF_STRUCTELEMENT parent = FPDF_StructElement_GetParent(child); ++ if (parent) { ++ memset(buf, 0, sizeof(buf)); ++ len = FPDF_StructElement_GetID(parent, buf, kBufSize); ++ if (len > 0) { ++ printf("%*s Parent ID: %ls\n", indent * 2, "", ++ ConvertToWString(buf, len).c_str()); ++ } ++ } ++ ++ memset(buf, 0, sizeof(buf)); ++ len = FPDF_StructElement_GetTitle(child, buf, kBufSize); ++ if (len > 0) { ++ printf("%*s Title: %ls\n", indent * 2, "", ++ ConvertToWString(buf, len).c_str()); ++ } ++ ++ memset(buf, 0, sizeof(buf)); ++ len = FPDF_StructElement_GetObjType(child, buf, kBufSize); ++ if (len > 0) { ++ printf("%*s Type: %ls\n", indent * 2, "", ++ ConvertToWString(buf, len).c_str()); ++ } + + for (int i = 0; i < FPDF_StructElement_CountChildren(child); ++i) { + FPDF_STRUCTELEMENT sub_child = FPDF_StructElement_GetChildAtIndex(child, i); +diff --git a/samples/pdfium_test_dump_helper.h b/samples/pdfium_test_dump_helper.h +index 17cbd8f7b..727731e73 100644 +--- a/samples/pdfium_test_dump_helper.h ++++ b/samples/pdfium_test_dump_helper.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 The PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/samples/pdfium_test_event_helper.cc b/samples/pdfium_test_event_helper.cc +index a3ddbef8a..d55057ee8 100644 +--- a/samples/pdfium_test_event_helper.cc ++++ b/samples/pdfium_test_event_helper.cc +@@ -1,4 +1,4 @@ +-// Copyright 2018 The PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -14,6 +14,19 @@ + #include "testing/fx_string_testhelpers.h" + + namespace { ++ ++uint32_t GetModifiers(std::string modifiers_string) { ++ uint32_t modifiers = 0; ++ if (modifiers_string.find("shift") != std::string::npos) ++ modifiers |= FWL_EVENTFLAG_ShiftKey; ++ if (modifiers_string.find("control") != std::string::npos) ++ modifiers |= FWL_EVENTFLAG_ControlKey; ++ if (modifiers_string.find("alt") != std::string::npos) ++ modifiers |= FWL_EVENTFLAG_AltKey; ++ ++ return modifiers; ++} ++ + void SendCharCodeEvent(FPDF_FORMHANDLE form, + FPDF_PAGE page, + const std::vector& tokens) { +@@ -22,39 +35,28 @@ void SendCharCodeEvent(FPDF_FORMHANDLE form, + return; + } + +- int keycode = atoi(tokens[1].c_str()); +- FORM_OnChar(form, page, keycode, 0); ++ int charcode = atoi(tokens[1].c_str()); ++ FORM_OnChar(form, page, charcode, 0); + } + + void SendKeyCodeEvent(FPDF_FORMHANDLE form, + FPDF_PAGE page, + const std::vector& tokens) { +- if (tokens.size() != 2) { ++ if (tokens.size() < 2 || tokens.size() > 3) { + fprintf(stderr, "keycode: bad args\n"); + return; + } + + int keycode = atoi(tokens[1].c_str()); +- FORM_OnKeyDown(form, page, keycode, 0); +- FORM_OnKeyUp(form, page, keycode, 0); +-} +- +-uint32_t GetModifiers(std::string modifiers_string) { +- int modifiers = 0; +- if (modifiers_string.find("shift") != std::string::npos) +- modifiers |= FWL_EVENTFLAG_ShiftKey; +- if (modifiers_string.find("control") != std::string::npos) +- modifiers |= FWL_EVENTFLAG_ControlKey; +- if (modifiers_string.find("alt") != std::string::npos) +- modifiers |= FWL_EVENTFLAG_AltKey; +- +- return modifiers; ++ uint32_t modifiers = tokens.size() >= 3 ? GetModifiers(tokens[2]) : 0; ++ FORM_OnKeyDown(form, page, keycode, modifiers); ++ FORM_OnKeyUp(form, page, keycode, modifiers); + } + + void SendMouseDownEvent(FPDF_FORMHANDLE form, + FPDF_PAGE page, + const std::vector& tokens) { +- if (tokens.size() != 4 && tokens.size() != 5) { ++ if (tokens.size() < 4 && tokens.size() > 5) { + fprintf(stderr, "mousedown: bad args\n"); + return; + } +@@ -74,7 +76,7 @@ void SendMouseDownEvent(FPDF_FORMHANDLE form, + void SendMouseUpEvent(FPDF_FORMHANDLE form, + FPDF_PAGE page, + const std::vector& tokens) { +- if (tokens.size() != 4 && tokens.size() != 5) { ++ if (tokens.size() < 4 && tokens.size() > 5) { + fprintf(stderr, "mouseup: bad args\n"); + return; + } +@@ -93,7 +95,7 @@ void SendMouseUpEvent(FPDF_FORMHANDLE form, + void SendMouseDoubleClickEvent(FPDF_FORMHANDLE form, + FPDF_PAGE page, + const std::vector& tokens) { +- if (tokens.size() != 4 && tokens.size() != 5) { ++ if (tokens.size() < 4 && tokens.size() > 5) { + fprintf(stderr, "mousedoubleclick: bad args\n"); + return; + } +@@ -121,6 +123,22 @@ void SendMouseMoveEvent(FPDF_FORMHANDLE form, + FORM_OnMouseMove(form, page, 0, x, y); + } + ++void SendMouseWheelEvent(FPDF_FORMHANDLE form, ++ FPDF_PAGE page, ++ const std::vector& tokens) { ++ if (tokens.size() < 5 && tokens.size() > 6) { ++ fprintf(stderr, "mousewheel: bad args\n"); ++ return; ++ } ++ ++ const FS_POINTF point = {static_cast(atoi(tokens[1].c_str())), ++ static_cast(atoi(tokens[2].c_str()))}; ++ int delta_x = atoi(tokens[3].c_str()); ++ int delta_y = atoi(tokens[4].c_str()); ++ int modifiers = tokens.size() >= 6 ? GetModifiers(tokens[5]) : 0; ++ FORM_OnMouseWheel(form, page, modifiers, &point, delta_x, delta_y); ++} ++ + void SendFocusEvent(FPDF_FORMHANDLE form, + FPDF_PAGE page, + const std::vector& tokens) { +@@ -133,13 +151,15 @@ void SendFocusEvent(FPDF_FORMHANDLE form, + int y = atoi(tokens[2].c_str()); + FORM_OnFocus(form, page, 0, x, y); + } ++ + } // namespace + + void SendPageEvents(FPDF_FORMHANDLE form, + FPDF_PAGE page, +- const std::string& events) { ++ const std::string& events, ++ const std::function& idler) { + auto lines = StringSplit(events, '\n'); +- for (auto line : lines) { ++ for (const auto& line : lines) { + auto command = StringSplit(line, '#'); + if (command[0].empty()) + continue; +@@ -156,10 +176,13 @@ void SendPageEvents(FPDF_FORMHANDLE form, + SendMouseDoubleClickEvent(form, page, tokens); + } else if (tokens[0] == "mousemove") { + SendMouseMoveEvent(form, page, tokens); ++ } else if (tokens[0] == "mousewheel") { ++ SendMouseWheelEvent(form, page, tokens); + } else if (tokens[0] == "focus") { + SendFocusEvent(form, page, tokens); + } else { + fprintf(stderr, "Unrecognized event: %s\n", tokens[0].c_str()); + } ++ idler(); + } + } +diff --git a/samples/pdfium_test_event_helper.h b/samples/pdfium_test_event_helper.h +index e823369e7..2f2825a18 100644 +--- a/samples/pdfium_test_event_helper.h ++++ b/samples/pdfium_test_event_helper.h +@@ -1,10 +1,11 @@ +-// Copyright 2018 The PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #ifndef SAMPLES_PDFIUM_TEST_EVENT_HELPER_H_ + #define SAMPLES_PDFIUM_TEST_EVENT_HELPER_H_ + ++#include + #include + + #include "public/fpdf_formfill.h" +@@ -12,6 +13,7 @@ + + void SendPageEvents(FPDF_FORMHANDLE form, + FPDF_PAGE page, +- const std::string& events); ++ const std::string& events, ++ const std::function& idler); + + #endif // SAMPLES_PDFIUM_TEST_EVENT_HELPER_H_ +diff --git a/samples/pdfium_test_write_helper.cc b/samples/pdfium_test_write_helper.cc +index 122d783b3..7ac49d702 100644 +--- a/samples/pdfium_test_write_helper.cc ++++ b/samples/pdfium_test_write_helper.cc +@@ -1,4 +1,4 @@ +-// Copyright 2018 The PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + + #include + +-#include ++#include + #include + #include + #include +@@ -18,7 +18,12 @@ + #include "public/fpdf_thumbnail.h" + #include "testing/fx_string_testhelpers.h" + #include "testing/image_diff/image_diff_png.h" +-#include "third_party/base/logging.h" ++#include "third_party/base/notreached.h" ++ ++#ifdef PDF_ENABLE_SKIA ++#include "third_party/skia/include/core/SkPicture.h" // nogncheck ++#include "third_party/skia/include/core/SkStream.h" // nogncheck ++#endif + + namespace { + +@@ -175,6 +180,36 @@ int CALLBACK EnhMetaFileProc(HDC hdc, + } + #endif // _WIN32 + ++std::string GeneratePageOutputFilename(const char* pdf_name, ++ int page_num, ++ const char* extension) { ++ std::ostringstream stream; ++ stream << pdf_name << "." << page_num << "." << extension; ++ std::string filename = stream.str(); ++ if (filename.size() >= 256) { ++ fprintf(stderr, "Filename %s is too long\n", filename.c_str()); ++ return std::string(); ++ } ++ ++ return filename; ++} ++ ++std::string GenerateImageOutputFilename(const char* pdf_name, ++ int page_num, ++ int image_num, ++ const char* extension) { ++ std::ostringstream stream; ++ stream << pdf_name << "." << page_num << "." << image_num << "." << extension; ++ std::string filename = stream.str(); ++ if (filename.size() >= 256) { ++ fprintf(stderr, "Filename %s for saving image is too long.\n", ++ filename.c_str()); ++ return std::string(); ++ } ++ ++ return filename; ++} ++ + } // namespace + + std::string WritePpm(const char* pdf_name, +@@ -183,20 +218,25 @@ std::string WritePpm(const char* pdf_name, + int stride, + int width, + int height) { +- if (!CheckDimensions(stride, width, height)) ++ if (!CheckDimensions(stride, width, height)) { + return ""; ++ } + + int out_len = width * height; +- if (out_len > INT_MAX / 3) ++ if (out_len > INT_MAX / 3) { + return ""; ++ } + + out_len *= 3; + +- char filename[256]; +- snprintf(filename, sizeof(filename), "%s.%d.ppm", pdf_name, num); +- FILE* fp = fopen(filename, "wb"); +- if (!fp) +- return ""; ++ std::string filename = GeneratePageOutputFilename(pdf_name, num, "ppm"); ++ if (filename.empty()) { ++ return std::string(); ++ } ++ FILE* fp = fopen(filename.c_str(), "wb"); ++ if (!fp) { ++ return std::string(); ++ } + + fprintf(fp, "P6\n# PDF test render\n%d %d\n255\n", width, height); + // Source data is B, G, R, unused. +@@ -215,42 +255,37 @@ std::string WritePpm(const char* pdf_name, + dest_line[(w * 3) + 2] = src_line[w * 4]; + } + } +- if (fwrite(result.data(), out_len, 1, fp) != 1) +- fprintf(stderr, "Failed to write to %s\n", filename); ++ if (fwrite(result.data(), out_len, 1, fp) != 1) { ++ fprintf(stderr, "Failed to write to %s\n", filename.c_str()); ++ } + + fclose(fp); +- return std::string(filename); ++ return filename; + } + +-void WriteText(FPDF_PAGE page, const char* pdf_name, int num) { +- char filename[256]; +- int chars_formatted = +- snprintf(filename, sizeof(filename), "%s.%d.txt", pdf_name, num); +- if (chars_formatted < 0 || +- static_cast(chars_formatted) >= sizeof(filename)) { +- fprintf(stderr, "Filename %s is too long\n", filename); ++void WriteText(FPDF_TEXTPAGE textpage, const char* pdf_name, int num) { ++ std::string filename = GeneratePageOutputFilename(pdf_name, num, "txt"); ++ if (filename.empty()) { + return; + } +- +- FILE* fp = fopen(filename, "w"); ++ FILE* fp = fopen(filename.c_str(), "w"); + if (!fp) { +- fprintf(stderr, "Failed to open %s for output\n", filename); ++ fprintf(stderr, "Failed to open %s for output\n", filename.c_str()); + return; + } + + // Output in UTF32-LE. + uint32_t bom = 0x0000FEFF; + if (fwrite(&bom, sizeof(bom), 1, fp) != 1) { +- fprintf(stderr, "Failed to write to %s\n", filename); ++ fprintf(stderr, "Failed to write to %s\n", filename.c_str()); + (void)fclose(fp); + return; + } + +- ScopedFPDFTextPage textpage(FPDFText_LoadPage(page)); +- for (int i = 0; i < FPDFText_CountChars(textpage.get()); i++) { +- uint32_t c = FPDFText_GetUnicode(textpage.get(), i); ++ for (int i = 0; i < FPDFText_CountChars(textpage); i++) { ++ uint32_t c = FPDFText_GetUnicode(textpage, i); + if (fwrite(&c, sizeof(c), 1, fp) != 1) { +- fprintf(stderr, "Failed to write to %s\n", filename); ++ fprintf(stderr, "Failed to write to %s\n", filename.c_str()); + break; + } + } +@@ -259,18 +294,13 @@ void WriteText(FPDF_PAGE page, const char* pdf_name, int num) { + + void WriteAnnot(FPDF_PAGE page, const char* pdf_name, int num) { + // Open the output text file. +- char filename[256]; +- int chars_formatted = +- snprintf(filename, sizeof(filename), "%s.%d.annot.txt", pdf_name, num); +- if (chars_formatted < 0 || +- static_cast(chars_formatted) >= sizeof(filename)) { +- fprintf(stderr, "Filename %s is too long\n", filename); ++ std::string filename = GeneratePageOutputFilename(pdf_name, num, "annot.txt"); ++ if (filename.empty()) { + return; + } +- +- FILE* fp = fopen(filename, "w"); ++ FILE* fp = fopen(filename.c_str(), "w"); + if (!fp) { +- fprintf(stderr, "Failed to open %s for output\n", filename); ++ fprintf(stderr, "Failed to open %s for output\n", filename.c_str()); + return; + } + +@@ -380,8 +410,9 @@ std::string WritePng(const char* pdf_name, + int stride, + int width, + int height) { +- if (!CheckDimensions(stride, width, height)) ++ if (!CheckDimensions(stride, width, height)) { + return ""; ++ } + + auto input = + pdfium::make_span(static_cast(buffer), stride * height); +@@ -392,28 +423,24 @@ std::string WritePng(const char* pdf_name, + return ""; + } + +- char filename[256]; +- int chars_formatted = +- snprintf(filename, sizeof(filename), "%s.%d.png", pdf_name, num); +- if (chars_formatted < 0 || +- static_cast(chars_formatted) >= sizeof(filename)) { +- fprintf(stderr, "Filename %s is too long\n", filename); +- return ""; ++ std::string filename = GeneratePageOutputFilename(pdf_name, num, "png"); ++ if (filename.empty()) { ++ return std::string(); + } +- +- FILE* fp = fopen(filename, "wb"); ++ FILE* fp = fopen(filename.c_str(), "wb"); + if (!fp) { +- fprintf(stderr, "Failed to open %s for output\n", filename); +- return ""; ++ fprintf(stderr, "Failed to open %s for output\n", filename.c_str()); ++ return std::string(); + } + + size_t bytes_written = + fwrite(&png_encoding.front(), 1, png_encoding.size(), fp); +- if (bytes_written != png_encoding.size()) +- fprintf(stderr, "Failed to write to %s\n", filename); ++ if (bytes_written != png_encoding.size()) { ++ fprintf(stderr, "Failed to write to %s\n", filename.c_str()); ++ } + + (void)fclose(fp); +- return std::string(filename); ++ return filename; + } + + #ifdef _WIN32 +@@ -423,18 +450,23 @@ std::string WriteBmp(const char* pdf_name, + int stride, + int width, + int height) { +- if (!CheckDimensions(stride, width, height)) +- return ""; ++ if (!CheckDimensions(stride, width, height)) { ++ return std::string(); ++ } + + int out_len = stride * height; +- if (out_len > INT_MAX / 3) +- return ""; ++ if (out_len > INT_MAX / 3) { ++ return std::string(); ++ } + +- char filename[256]; +- snprintf(filename, sizeof(filename), "%s.%d.bmp", pdf_name, num); +- FILE* fp = fopen(filename, "wb"); +- if (!fp) +- return ""; ++ std::string filename = GeneratePageOutputFilename(pdf_name, num, "bmp"); ++ if (filename.empty()) { ++ return std::string(); ++ } ++ FILE* fp = fopen(filename.c_str(), "wb"); ++ if (!fp) { ++ return std::string(); ++ } + + BITMAPINFO bmi = {}; + bmi.bmiHeader.biSize = sizeof(bmi) - sizeof(RGBQUAD); +@@ -453,17 +485,19 @@ std::string WriteBmp(const char* pdf_name, + if (fwrite(&file_header, sizeof(file_header), 1, fp) != 1 || + fwrite(&bmi, bmi.bmiHeader.biSize, 1, fp) != 1 || + fwrite(buffer, out_len, 1, fp) != 1) { +- fprintf(stderr, "Failed to write to %s\n", filename); ++ fprintf(stderr, "Failed to write to %s\n", filename.c_str()); + } + fclose(fp); +- return std::string(filename); ++ return filename; + } + + void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) { +- char filename[256]; +- snprintf(filename, sizeof(filename), "%s.%d.emf", pdf_name, num); ++ std::string filename = GeneratePageOutputFilename(pdf_name, num, "emf"); ++ if (filename.empty()) { ++ return; ++ } + +- HDC dc = CreateEnhMetaFileA(nullptr, filename, nullptr, nullptr); ++ HDC dc = CreateEnhMetaFileA(nullptr, filename.c_str(), nullptr, nullptr); + + int width = static_cast(FPDF_GetPageWidthF(page)); + int height = static_cast(FPDF_GetPageHeightF(page)); +@@ -482,9 +516,11 @@ void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) { + } + + void WritePS(FPDF_PAGE page, const char* pdf_name, int num) { +- char filename[256]; +- snprintf(filename, sizeof(filename), "%s.%d.ps", pdf_name, num); +- FILE* fp = fopen(filename, "wb"); ++ std::string filename = GeneratePageOutputFilename(pdf_name, num, "ps"); ++ if (filename.empty()) { ++ return; ++ } ++ FILE* fp = fopen(filename.c_str(), "wb"); + if (!fp) + return; + +@@ -505,7 +541,7 @@ void WritePS(FPDF_PAGE page, const char* pdf_name, int num) { + const char* data = reinterpret_cast(comment->Data); + uint16_t size = *reinterpret_cast(data); + if (fwrite(data + sizeof(uint16_t), size, 1, fp) != 1) { +- fprintf(stderr, "Failed to write to %s\n", filename); ++ fprintf(stderr, "Failed to write to %s\n", filename.c_str()); + break; + } + } +@@ -515,23 +551,14 @@ void WritePS(FPDF_PAGE page, const char* pdf_name, int num) { + #endif // _WIN32 + + #ifdef PDF_ENABLE_SKIA +-std::string WriteSkp(const char* pdf_name, +- int num, +- SkPictureRecorder* recorder) { +- char filename[256]; +- int chars_formatted = +- snprintf(filename, sizeof(filename), "%s.%d.skp", pdf_name, num); +- +- if (chars_formatted < 0 || +- static_cast(chars_formatted) >= sizeof(filename)) { +- fprintf(stderr, "Filename %s is too long\n", filename); +- return ""; ++std::string WriteSkp(const char* pdf_name, int num, const SkPicture& picture) { ++ std::string filename = GeneratePageOutputFilename(pdf_name, num, "skp"); ++ if (filename.empty()) { ++ return filename; + } +- +- sk_sp picture(recorder->finishRecordingAsPicture()); +- SkFILEWStream wStream(filename); +- picture->serialize(&wStream); +- return std::string(filename); ++ SkFILEWStream wStream(filename.c_str()); ++ picture.serialize(&wStream); ++ return filename; + } + #endif + +@@ -635,20 +662,25 @@ void WriteAttachments(FPDF_DOCUMENT doc, const std::string& name) { + } + + // Retrieve the attachment. +- length_bytes = FPDFAttachment_GetFile(attachment, nullptr, 0); ++ if (!FPDFAttachment_GetFile(attachment, nullptr, 0, &length_bytes)) { ++ fprintf(stderr, "Failed to retrieve attachment \"%s\".\n", ++ attachment_name.c_str()); ++ continue; ++ } ++ + std::vector data_buf(length_bytes); + if (length_bytes) { +- unsigned long actual_length_bytes = +- FPDFAttachment_GetFile(attachment, data_buf.data(), length_bytes); +- if (actual_length_bytes != length_bytes) +- data_buf.clear(); +- } +- if (data_buf.empty()) { +- fprintf(stderr, "Attachment \"%s\" is empty.\n", attachment_name.c_str()); +- continue; ++ unsigned long actual_length_bytes; ++ if (!FPDFAttachment_GetFile(attachment, data_buf.data(), length_bytes, ++ &actual_length_bytes)) { ++ fprintf(stderr, "Failed to retrieve attachment \"%s\".\n", ++ attachment_name.c_str()); ++ continue; ++ } + } + +- // Write the attachment file. ++ // Write the attachment file. Since a PDF document could have 0-byte files ++ // as attachments, we should allow saving the 0-byte attachments to files. + WriteBufferToFile(data_buf.data(), length_bytes, save_name, "attachment"); + } + } +@@ -656,8 +688,9 @@ void WriteAttachments(FPDF_DOCUMENT doc, const std::string& name) { + void WriteImages(FPDF_PAGE page, const char* pdf_name, int page_num) { + for (int i = 0; i < FPDFPage_CountObjects(page); ++i) { + FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i); +- if (FPDFPageObj_GetType(obj) != FPDF_PAGEOBJ_IMAGE) ++ if (FPDFPageObj_GetType(obj) != FPDF_PAGEOBJ_IMAGE) { + continue; ++ } + + ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj)); + if (!bitmap) { +@@ -666,12 +699,45 @@ void WriteImages(FPDF_PAGE page, const char* pdf_name, int page_num) { + continue; + } + +- char filename[256]; +- int chars_formatted = snprintf(filename, sizeof(filename), "%s.%d.%d.png", +- pdf_name, page_num, i); +- if (chars_formatted < 0 || +- static_cast(chars_formatted) >= sizeof(filename)) { +- fprintf(stderr, "Filename %s for saving image is too long.\n", filename); ++ std::string filename = ++ GenerateImageOutputFilename(pdf_name, page_num, i, "png"); ++ if (filename.empty()) { ++ continue; ++ } ++ ++ std::vector png_encoding = EncodeBitmapToPng(std::move(bitmap)); ++ if (png_encoding.empty()) { ++ fprintf(stderr, ++ "Failed to convert image object #%d, on page #%d to png.\n", ++ i + 1, page_num + 1); ++ continue; ++ } ++ ++ WriteBufferToFile(&png_encoding.front(), png_encoding.size(), ++ filename.c_str(), "image"); ++ } ++} ++ ++void WriteRenderedImages(FPDF_DOCUMENT doc, ++ FPDF_PAGE page, ++ const char* pdf_name, ++ int page_num) { ++ for (int i = 0; i < FPDFPage_CountObjects(page); ++i) { ++ FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, i); ++ if (FPDFPageObj_GetType(obj) != FPDF_PAGEOBJ_IMAGE) { ++ continue; ++ } ++ ++ ScopedFPDFBitmap bitmap(FPDFImageObj_GetRenderedBitmap(doc, page, obj)); ++ if (!bitmap) { ++ fprintf(stderr, "Image object #%d on page #%d has an empty bitmap.\n", ++ i + 1, page_num + 1); ++ continue; ++ } ++ ++ std::string filename = ++ GenerateImageOutputFilename(pdf_name, page_num, i, "png"); ++ if (filename.empty()) { + continue; + } + +@@ -683,8 +749,8 @@ void WriteImages(FPDF_PAGE page, const char* pdf_name, int page_num) { + continue; + } + +- WriteBufferToFile(&png_encoding.front(), png_encoding.size(), filename, +- "image"); ++ WriteBufferToFile(&png_encoding.front(), png_encoding.size(), ++ filename.c_str(), "image"); + } + } + +diff --git a/samples/pdfium_test_write_helper.h b/samples/pdfium_test_write_helper.h +index b53d5551a..ffeba1f55 100644 +--- a/samples/pdfium_test_write_helper.h ++++ b/samples/pdfium_test_write_helper.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 The PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -10,8 +10,7 @@ + #include "public/fpdfview.h" + + #ifdef PDF_ENABLE_SKIA +-#include "third_party/skia/include/core/SkPictureRecorder.h" +-#include "third_party/skia/include/core/SkStream.h" ++class SkPicture; + #endif + + std::string WritePpm(const char* pdf_name, +@@ -20,7 +19,7 @@ std::string WritePpm(const char* pdf_name, + int stride, + int width, + int height); +-void WriteText(FPDF_PAGE page, const char* pdf_name, int num); ++void WriteText(FPDF_TEXTPAGE textpage, const char* pdf_name, int num); + void WriteAnnot(FPDF_PAGE page, const char* pdf_name, int num); + std::string WritePng(const char* pdf_name, + int num, +@@ -41,13 +40,15 @@ void WritePS(FPDF_PAGE page, const char* pdf_name, int num); + #endif // _WIN32 + + #ifdef PDF_ENABLE_SKIA +-std::string WriteSkp(const char* pdf_name, +- int num, +- SkPictureRecorder* recorder); ++std::string WriteSkp(const char* pdf_name, int num, const SkPicture& picture); + #endif // PDF_ENABLE_SKIA + + void WriteAttachments(FPDF_DOCUMENT doc, const std::string& name); + void WriteImages(FPDF_PAGE page, const char* pdf_name, int page_num); ++void WriteRenderedImages(FPDF_DOCUMENT doc, ++ FPDF_PAGE page, ++ const char* pdf_name, ++ int page_num); + void WriteDecodedThumbnailStream(FPDF_PAGE page, + const char* pdf_name, + int page_num); +diff --git a/samples/simple_no_v8.c b/samples/simple_no_v8.c +new file mode 100644 +index 000000000..7e9d7b94c +--- /dev/null ++++ b/samples/simple_no_v8.c +@@ -0,0 +1,52 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// No-frills example of how to initialize and call into a PDFium environment, ++// from C. The PDFium API is compatible with C (the C++ internals are hidden ++// beneath it). ++ ++#include ++ ++#include "public/fpdf_edit.h" ++#include "public/fpdf_formfill.h" ++#include "public/fpdfview.h" ++ ++int main(int argc, const char* argv[]) { ++ // The PDF library must be initialized before creating a document. ++ FPDF_LIBRARY_CONFIG config; ++ memset(&config, 0, sizeof(config)); ++ config.version = 3; ++ FPDF_InitLibraryWithConfig(&config); ++ ++ // The document must be created before creating a form-fill environment. ++ // Typically use FPDF_LoadDocument() for pre-existing documents. Here, we ++ // create a new blank document for simplicity. ++ FPDF_DOCUMENT doc = FPDF_CreateNewDocument(); ++ ++ FPDF_FORMFILLINFO formfillinfo; ++ memset(&formfillinfo, 0, sizeof(formfillinfo)); ++ formfillinfo.version = 1; ++ ++ FPDF_FORMHANDLE form_handle = ++ FPDFDOC_InitFormFillEnvironment(doc, &formfillinfo); ++ ++ // Typically use FPDF_LoadPage() for pre-existing pages. Here, we ++ // create a new blank page for simplicity. ++ FPDF_PAGE page = FPDFPage_New(doc, 0, 640.0, 480.0); ++ FORM_OnAfterLoadPage(page, form_handle); ++ FORM_DoPageAAction(page, form_handle, FPDFPAGE_AACTION_OPEN); ++ ++ // Do actual work with the page here. ++ ++ FORM_DoPageAAction(page, form_handle, FPDFPAGE_AACTION_CLOSE); ++ FORM_OnBeforeClosePage(page, form_handle); ++ FPDF_ClosePage(page); ++ ++ FORM_DoDocumentAAction(form_handle, FPDFDOC_AACTION_WC); ++ FPDFDOC_ExitFormFillEnvironment(form_handle); ++ FPDF_CloseDocument(doc); ++ FPDF_DestroyLibrary(); ++ ++ return 0; ++} +diff --git a/samples/simple_with_v8.cc b/samples/simple_with_v8.cc +new file mode 100644 +index 000000000..028669f39 +--- /dev/null ++++ b/samples/simple_with_v8.cc +@@ -0,0 +1,78 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// No-frills example of how to initialize and call into a PDFium environment ++// including V8 support (but not XFA) from C++. Since the V8 API is in C++, ++// C++ is required in this case (not just C). ++ ++#include ++ ++#include "public/fpdf_edit.h" ++#include "public/fpdf_formfill.h" ++#include "public/fpdfview.h" ++#include "v8/include/libplatform/libplatform.h" ++#include "v8/include/v8-array-buffer.h" ++#include "v8/include/v8-initialization.h" ++#include "v8/include/v8-isolate.h" ++ ++int main(int argc, const char* argv[]) { ++ // V8 must be initialized before the PDFium library if using V8. ++ v8::V8::InitializeICUDefaultLocation(argv[0]); ++ v8::V8::InitializeExternalStartupData(argv[0]); ++ v8::Platform* platform = v8::platform::NewDefaultPlatform().release(); ++ v8::V8::InitializePlatform(platform); ++ v8::V8::Initialize(); ++ ++ v8::Isolate::CreateParams params; ++ params.array_buffer_allocator = static_cast( ++ FPDF_GetArrayBufferAllocatorSharedInstance()); ++ v8::Isolate* isolate = v8::Isolate::New(params); ++ ++ // The PDF library must be initialized before creating a document. ++ FPDF_LIBRARY_CONFIG config; ++ memset(&config, 0, sizeof(config)); ++ config.version = 3; ++ config.m_pIsolate = isolate; ++ config.m_pPlatform = platform; ++ FPDF_InitLibraryWithConfig(&config); ++ ++ // The document must be created before creating a form-fill environment. ++ // Typically use FPDF_LoadDocument() for pre-existing documents. Here, we ++ // create a new blank document for simplicity. ++ FPDF_DOCUMENT doc = FPDF_CreateNewDocument(); ++ ++ IPDF_JSPLATFORM jsplatform; ++ memset(&jsplatform, 0, sizeof(jsplatform)); ++ ++ FPDF_FORMFILLINFO formfillinfo; ++ memset(&formfillinfo, 0, sizeof(formfillinfo)); ++ formfillinfo.version = 1; ++ formfillinfo.m_pJsPlatform = &jsplatform; ++ ++ FPDF_FORMHANDLE form_handle = ++ FPDFDOC_InitFormFillEnvironment(doc, &formfillinfo); ++ ++ // Typically use FPDF_LoadPage() for pre-existing pages. Here, we ++ // create a new blank page for simplicity. ++ FPDF_PAGE page = FPDFPage_New(doc, 0, 640.0, 480.0); ++ FORM_OnAfterLoadPage(page, form_handle); ++ FORM_DoPageAAction(page, form_handle, FPDFPAGE_AACTION_OPEN); ++ ++ // Do actual work with the page here. ++ ++ FORM_DoPageAAction(page, form_handle, FPDFPAGE_AACTION_CLOSE); ++ FORM_OnBeforeClosePage(page, form_handle); ++ FPDF_ClosePage(page); ++ ++ FORM_DoDocumentAAction(form_handle, FPDFDOC_AACTION_WC); ++ FPDFDOC_ExitFormFillEnvironment(form_handle); ++ FPDF_CloseDocument(doc); ++ FPDF_DestroyLibrary(); ++ ++ isolate->Dispose(); ++ v8::V8::ShutdownPlatform(); ++ delete platform; ++ ++ return 0; ++} +diff --git a/skia/BUILD.gn b/skia/BUILD.gn +index d49587c51..ff91366fe 100644 +--- a/skia/BUILD.gn ++++ b/skia/BUILD.gn +@@ -1,12 +1,13 @@ +-# Copyright (c) 2013 The Chromium Authors. All rights reserved. ++# Copyright 2013 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + + import("//build/config/features.gni") + import("//build/config/ui.gni") + import("//testing/test.gni") ++import("//third_party/skia/gn/codec.gni") + import("//third_party/skia/gn/shared_sources.gni") +-import("//third_party/skia/third_party/skcms/skcms.gni") ++import("//third_party/skia/modules/skcms/skcms.gni") + import("features.gni") + + if (current_cpu == "arm") { +@@ -16,14 +17,17 @@ if (current_cpu == "mipsel" || current_cpu == "mips64el") { + import("//build/config/mips.gni") + } + +-skia_support_gpu = !is_ios ++skia_use_ganesh_backend = !is_ios + skia_support_pdf = false + + # External-facing config for dependent code. + config("skia_config") { + include_dirs = [ "//third_party/skia" ] + +- defines = [ "SK_USER_CONFIG_HEADER=\"../../skia/config/SkUserConfig.h\"" ] ++ defines = [ ++ "SK_ENCODE_PNG", ++ "SK_USER_CONFIG_HEADER=\"../../skia/config/SkPdfiumUserConfig.h\"", ++ ] + + if (is_win) { + defines += [ "SK_FREETYPE_MINIMUM_RUNTIME_VERSION=(((FREETYPE_MAJOR) * 0x01000000) | ((FREETYPE_MINOR) * 0x00010000) | ((FREETYPE_PATCH) * 0x00000100))" ] +@@ -36,10 +40,8 @@ config("skia_config") { + ] + } + +- if (skia_support_gpu) { +- defines += [ "SK_SUPPORT_GPU=1" ] +- } else { +- defines += [ "SK_SUPPORT_GPU=0" ] ++ if (skia_use_ganesh_backend) { ++ defines += [ "SK_GANESH" ] + } + + if (skia_use_gl) { +@@ -64,11 +66,11 @@ config("skia_config") { + + # Internal-facing config for Skia library code. + config("skia_library_config") { +- defines = [] +- +- if (is_component_build) { +- defines += [ "SKIA_IMPLEMENTATION=1" ] +- } ++ # Turn on SK_API to export Skia's public API ++ defines = [ ++ "IS_SKIA_IMPL=1", ++ "SKIA_IMPLEMENTATION=1", ++ ] + + if (current_cpu == "arm") { + if (arm_use_neon) { +@@ -79,7 +81,7 @@ config("skia_library_config") { + } + + # Settings for text blitting, chosen to approximate the system browser. +- if (is_linux) { ++ if (is_linux || is_chromeos) { + defines += [ + "SK_GAMMA_EXPONENT=1.2", + "SK_GAMMA_CONTRAST=0.2", +@@ -131,12 +133,9 @@ config("skia_library_config") { + # assembly code contains flow control(jmp or jcc) statements. + + "/wd4800", # forcing value to bool 'true/false'(assigning int to bool). +- "/wd5041", # out-of-line definition for constexpr static data member is not needed and is deprecated in C++17 ++ "/wd5041", # out-of-line definition for constexpr static data member is ++ # not needed and is deprecated in C++17 + ] +- cflags_cc = [ "/std:c++17" ] +- } else { +- cflags_cc = [ "-std=c++17" ] +- cflags_objcc = [ "-std=c++17" ] + } + } + +@@ -152,98 +151,68 @@ source_set("skcms") { + # LLVM automatically sets the equivalent of GCC's -mfp16-format=ieee on ARM + # builds by default, while GCC itself does not. We need it to enable support + # for half-precision floating point data types used by SKCMS on ARM. +- if (is_linux && !is_clang && current_cpu == "arm") { ++ if ((is_linux || is_chromeos) && !is_clang && current_cpu == "arm") { + cflags += [ "-mfp16-format=ieee" ] + } + +- public = [ "//third_party/skia/include/third_party/skcms/skcms.h" ] +- include_dirs = [ "//third_party/skia/include/third_party/skcms" ] +- sources = +- rebase_path(skcms_sources, ".", "//third_party/skia/third_party/skcms") ++ public = ++ rebase_path(skcms_public_headers, ".", "//third_party/skia/modules/skcms") ++ sources = rebase_path(skcms_sources, ".", "//third_party/skia/modules/skcms") + } + + component("skia") { ++ deps = [] ++ + sources = [ + # PDFium sources. +- "config/SkUserConfig.h", ++ "config/SkPdfiumUserConfig.h", + "ext/google_logging.cc", + ] + + # The skia sources values are relative to the skia_dir, so we need to rebase. ++ sources += skia_core_public + sources += skia_sksl_gpu_sources + sources += skia_sksl_sources + sources += skia_utils_sources ++ sources += skia_codec_core ++ sources += skia_codec_decode_bmp ++ sources += skia_encode_srcs ++ sources += skia_encode_png_srcs + sources += [ +- "//third_party/skia/src/codec/SkMasks.cpp", + "//third_party/skia/src/fonts/SkFontMgr_indirect.cpp", + "//third_party/skia/src/fonts/SkRemotableFontMgr.cpp", +- "//third_party/skia/src/images/SkImageEncoder.cpp", +- "//third_party/skia/src/ports/SkFontHost_FreeType.cpp", +- "//third_party/skia/src/ports/SkFontHost_FreeType_common.cpp", +- "//third_party/skia/src/ports/SkFontHost_win.cpp", +- "//third_party/skia/src/ports/SkFontMgr_android.cpp", +- "//third_party/skia/src/ports/SkFontMgr_android_factory.cpp", +- "//third_party/skia/src/ports/SkFontMgr_android_parser.cpp", + "//third_party/skia/src/ports/SkGlobalInitialization_default.cpp", + "//third_party/skia/src/ports/SkImageGenerator_none.cpp", +- "//third_party/skia/src/ports/SkOSFile_posix.cpp", + "//third_party/skia/src/ports/SkOSFile_stdio.cpp", +- "//third_party/skia/src/ports/SkOSFile_win.cpp", +- "//third_party/skia/src/ports/SkRemotableFontMgr_win_dw.cpp", +- "//third_party/skia/src/ports/SkScalerContext_win_dw.cpp", +- "//third_party/skia/src/ports/SkTLS_pthread.cpp", +- "//third_party/skia/src/ports/SkTLS_win.cpp", +- "//third_party/skia/src/ports/SkTypeface_win_dw.cpp", + "//third_party/skia/src/sfnt/SkOTTable_name.cpp", + "//third_party/skia/src/sfnt/SkOTUtils.cpp", + +- #mac +- "//third_party/skia/src/utils/mac/SkStream_mac.cpp", +- + #pdfium + "//third_party/skia/src/ports/SkDiscardableMemory_none.cpp", +- "//third_party/skia/src/ports/SkFontMgr_custom.cpp", +- "//third_party/skia/src/ports/SkFontMgr_custom_empty.cpp", +- "//third_party/skia/src/ports/SkFontMgr_custom_empty_factory.cpp", + "//third_party/skia/src/ports/SkMemory_malloc.cpp", + ] + + # This and skia_opts are really the same conceptual target so share headers. + allow_circular_includes_from = [ ":skia_opts" ] + +- if (current_cpu == "arm") { +- sources += [ "//third_party/skia/src/core/SkUtilsArm.cpp" ] +- } +- + # GPU +- if (skia_support_gpu) { +- sources += skia_gpu_sources ++ if (skia_use_ganesh_backend) { ++ sources += skia_gpu_public ++ sources += skia_ganesh_private + sources += skia_null_gpu_sources ++ sources += skia_shared_gpu_sources + if (skia_use_gl) { +- sources += skia_gl_gpu_sources ++ sources += skia_gpu_gl_public ++ sources += skia_gpu_gl_private + } + } + + # Remove unused util files include in utils.gni + sources -= [ + "//third_party/skia/src/utils/SkCamera.cpp", +- "//third_party/skia/src/utils/SkFrontBufferedStream.cpp", +- "//third_party/skia/src/utils/SkInterpolator.cpp", + "//third_party/skia/src/utils/SkParsePath.cpp", + ] + +- if (is_win) { +- sources -= [ +- # Keeping _win.cpp +- "//third_party/skia/src/utils/SkThreadUtils_pthread.cpp", +- ] +- } else { +- sources -= [ +- # Keeping _pthread.cpp +- "//third_party/skia/src/utils/SkThreadUtils_win.cpp", +- ] +- } +- + # need separate win section to handle chromes auto gn filter + # (build/config/BUILDCONFIG.gn) + if (is_win) { +@@ -255,36 +224,97 @@ component("skia") { + ] + } + +- # Fixup skia library sources. +- if (is_win) { +- sources -= [ +- "//third_party/skia/src/ports/SkOSFile_posix.cpp", +- "//third_party/skia/src/ports/SkTLS_pthread.cpp", ++ # Select Skia ports. ++ ++ # FreeType is needed everywhere (except on iOS), on Linux and Android as main ++ # font backend, on Windows and Mac as fallback backend for Variations. ++ if (!is_ios) { ++ sources += [ ++ "//third_party/skia/src/ports/SkFontHost_FreeType.cpp", ++ "//third_party/skia/src/ports/SkFontHost_FreeType_common.cpp", + ] +- } else { +- sources -= [ ++ } ++ ++ if (is_win) { ++ sources += [ + "//third_party/skia/src/ports/SkFontHost_win.cpp", ++ ++ # TODO(crbug.com/pdfium/1830) Consider using SkFontMgr_win_dw_factory ++ # instead of SkFontMgr_custom_empty (which is what the embedder test ++ # hashes are based upon). ++ "//third_party/skia/src/ports/SkFontMgr_custom.cpp", ++ "//third_party/skia/src/ports/SkFontMgr_custom_empty.cpp", ++ "//third_party/skia/src/ports/SkFontMgr_custom_empty_factory.cpp", ++ "//third_party/skia/src/ports/SkFontMgr_win_dw.cpp", + "//third_party/skia/src/ports/SkOSFile_win.cpp", + "//third_party/skia/src/ports/SkRemotableFontMgr_win_dw.cpp", + "//third_party/skia/src/ports/SkScalerContext_win_dw.cpp", +- "//third_party/skia/src/ports/SkTLS_win.cpp", + "//third_party/skia/src/ports/SkTypeface_win_dw.cpp", + ] ++ } else { ++ sources += [ "//third_party/skia/src/ports/SkOSFile_posix.cpp" ] + } +- if (!is_android) { +- sources -= [ ++ ++ frameworks = [] ++ if (is_apple) { ++ sources += [ ++ # TODO(crbug.com/pdfium/1830) Consider using SkFontMgr_mac_ct instead of ++ # SkFontMgr_custom_empty (which is what the embedder test hashes are ++ # based upon). ++ "//third_party/skia/src/ports/SkFontMgr_custom.cpp", ++ "//third_party/skia/src/ports/SkFontMgr_custom_empty.cpp", ++ "//third_party/skia/src/ports/SkFontMgr_custom_empty_factory.cpp", ++ "//third_party/skia/src/ports/SkScalerContext_mac_ct.cpp", ++ "//third_party/skia/src/ports/SkScalerContext_mac_ct.h", ++ "//third_party/skia/src/ports/SkTypeface_mac_ct.cpp", ++ "//third_party/skia/src/ports/SkTypeface_mac_ct.h", ++ ] ++ frameworks += [ ++ "CoreFoundation.framework", ++ "CoreGraphics.framework", ++ "CoreText.framework", ++ ] ++ } ++ ++ if (is_linux || is_chromeos) { ++ sources += [ ++ "//third_party/skia/src/ports/SkFontConfigInterface.cpp", ++ "//third_party/skia/src/ports/SkFontConfigInterface_direct.cpp", ++ "//third_party/skia/src/ports/SkFontConfigInterface_direct_factory.cpp", ++ "//third_party/skia/src/ports/SkFontMgr_FontConfigInterface.cpp", ++ "//third_party/skia/src/ports/SkFontMgr_custom.cpp", ++ "//third_party/skia/src/ports/SkFontMgr_custom_empty.cpp", ++ "//third_party/skia/src/ports/SkFontMgr_custom_empty_factory.cpp", ++ ] ++ } ++ ++ if (is_android) { ++ sources += [ ++ # Unlike Chromium, standalone PDFium on Linux and Chrome OS does not ++ # require these files, since PDFium does not perform Android emulation. ++ # Note that this requires expat. + "//third_party/skia/src/ports/SkFontMgr_android.cpp", + "//third_party/skia/src/ports/SkFontMgr_android_factory.cpp", + "//third_party/skia/src/ports/SkFontMgr_android_parser.cpp", + ] + } +- if (!is_linux && !is_android && !is_win && !is_mac) { +- sources -= [ +- "//third_party/skia/src/ports/SkFontHost_FreeType.cpp", +- "//third_party/skia/src/ports/SkFontHost_FreeType_common.cpp", ++ ++ if (is_fuchsia) { ++ sources += [ ++ # TODO(crbug.com/pdfium/2019): Consider using SkFontMgr_fuchsia.cpp ++ # instead of SkFontMgr_custom_empty.cpp. + "//third_party/skia/src/ports/SkFontMgr_custom.cpp", ++ "//third_party/skia/src/ports/SkFontMgr_custom_empty.cpp", + "//third_party/skia/src/ports/SkFontMgr_custom_empty_factory.cpp", + ] ++ deps += [ ++ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.fonts:fuchsia.fonts_hlcpp", ++ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.io:fuchsia.io_hlcpp", ++ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.sys:fuchsia.sys_hlcpp", ++ "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp", ++ "//third_party/fuchsia-sdk/sdk/pkg/zx", ++ "//third_party/icu:icuuc", ++ ] + } + + if (is_clang && !is_nacl) { +@@ -302,25 +332,26 @@ component("skia") { + ] + public_configs = [ ":skia_config" ] + +- deps = [ ++ deps += [ + ":skcms", + ":skia_opts", ++ "../third_party:png", + "../third_party:zlib", + "//:freetype_common", + ] + public_deps = [ ":skia_core_and_effects" ] + +- if (is_linux) { +- deps += [ "//third_party/icu:icuuc" ] ++ if (is_linux || is_chromeos) { ++ deps += [ ++ "//third_party:fontconfig", ++ "//third_party/icu:icuuc", ++ ] + } + + if (is_android) { +- set_sources_assignment_filter([]) +- set_sources_assignment_filter(sources_assignment_filter) + deps += [ +- "//third_party/android_sdk:cpu_features", ++ "//third_party/android_ndk:cpu_features", + "//third_party/expat", +- "//third_party/freetype-android:freetype", + ] + } + +@@ -330,14 +361,7 @@ component("skia") { + } + + if (is_ios) { +- libs = [ "ImageIO.framework" ] +- set_sources_assignment_filter([]) +- sources += [ +- "//third_party/skia/src/ports/SkFontHost_mac.cpp", +- "//third_party/skia/src/utils/mac/SkCreateCGImageRef.cpp", +- "//third_party/skia/src/utils/mac/SkStream_mac.cpp", +- ] +- set_sources_assignment_filter(sources_assignment_filter) ++ frameworks += [ "ImageIO.framework" ] + } + + if (skia_support_pdf) { +@@ -374,17 +398,15 @@ template("skia_source_set") { + + if (is_win) { + cflags_cc = [ +- "/std:c++17", +- "/wd5041", # out-of-line definition for constexpr static data member is not needed and is deprecated in C++17 ++ "/wd5041", # out-of-line definition for constexpr static data member is ++ # not needed and is deprecated in C++17 + ] +- } else { +- cflags_cc = [ "-std=c++17" ] + } + } + } + + skia_source_set("skia_core_and_effects") { +- defines = skia_core_defines ++ defines = [] + sources = skia_core_sources + sources += skia_effects_sources + sources += skia_effects_imagefilter_sources +@@ -392,7 +414,14 @@ skia_source_set("skia_core_and_effects") { + visibility = [ ":skia" ] + } + +-# Separated out so it can be compiled with different flags for SSE. ++# Bits that involve special vector-y hardware. ++if (current_cpu == "arm64") { ++ skia_source_set("skia_opts_crc32") { ++ sources = skia_opts.crc32_sources ++ cflags = [ "-march=armv8-a+crc" ] ++ visibility = [ ":skia_opts" ] ++ } ++} + if (current_cpu == "x86" || current_cpu == "x64") { + skia_source_set("skia_opts_sse3") { + sources = skia_opts.ssse3_sources +@@ -404,16 +433,6 @@ if (current_cpu == "x86" || current_cpu == "x64") { + } + visibility = [ ":skia_opts" ] + } +- skia_source_set("skia_opts_sse41") { +- sources = skia_opts.sse41_sources +- if (!is_win || is_clang) { +- cflags = [ "-msse4.1" ] +- } +- if (is_win) { +- defines = [ "SK_CPU_SSE_LEVEL=41" ] +- } +- visibility = [ ":skia_opts" ] +- } + skia_source_set("skia_opts_sse42") { + sources = skia_opts.sse42_sources + if (!is_win || is_clang) { +@@ -450,6 +469,16 @@ if (current_cpu == "x86" || current_cpu == "x64") { + } + visibility = [ ":skia_opts" ] + } ++ skia_source_set("skia_opts_skx") { ++ sources = skia_opts.skx_sources ++ if (!is_win) { ++ cflags = [ "-march=skylake-avx512" ] ++ } ++ if (is_win) { ++ cflags = [ "/arch:AVX512" ] ++ } ++ visibility = [ ":skia_opts" ] ++ } + } + + skia_source_set("skia_opts") { +@@ -457,12 +486,11 @@ skia_source_set("skia_opts") { + defines = [] + + if (current_cpu == "x86" || current_cpu == "x64") { +- sources = skia_opts.sse2_sources + deps = [ + ":skia_opts_avx", + ":skia_opts_hsw", ++ ":skia_opts_skx", + ":skia_opts_sse3", +- ":skia_opts_sse41", + ":skia_opts_sse42", + ] + } else if (current_cpu == "arm") { +@@ -471,10 +499,7 @@ skia_source_set("skia_opts") { + cflags += [ "-fomit-frame-pointer" ] + + if (arm_version >= 7) { +- sources = skia_opts.armv7_sources + if (arm_use_neon || arm_optionally_use_neon) { +- sources += skia_opts.neon_sources +- + # Root build config sets -mfpu=$arm_fpu, which we expect to be neon + # when running this. + if (!arm_use_neon) { +@@ -482,24 +507,13 @@ skia_source_set("skia_opts") { + cflags += [ "-mfpu=neon" ] + } + } +- } else { +- sources = skia_opts.none_sources + } + } else if (current_cpu == "arm64") { +- sources = skia_opts.arm64_sources +- } else if (current_cpu == "mipsel") { +- cflags += [ "-fomit-frame-pointer" ] +- +- if (mips_dsp_rev >= 1) { +- sources = skia_opts.mips_dsp_sources +- } else { +- sources = skia_opts.none_sources +- } +- } else if (current_cpu == "mips64el") { ++ deps = [ ":skia_opts_crc32" ] ++ } else if (current_cpu == "mipsel" || current_cpu == "mips64el") { + cflags += [ "-fomit-frame-pointer" ] +- sources = skia_opts.none_sources + } else { +- assert(false, "Need to port cpu specific stuff from gn/BUILDCONFIG.gn") ++ assert(false, "Unsupported target CPU " + current_cpu) + } + + if (is_android && !is_debug) { +diff --git a/skia/OWNERS b/skia/OWNERS +new file mode 100644 +index 000000000..e1922a503 +--- /dev/null ++++ b/skia/OWNERS +@@ -0,0 +1,2 @@ ++kjlubick@google.com ++bungeman@google.com +\ No newline at end of file +diff --git a/skia/config/SkUserConfig.h b/skia/config/SkPdfiumUserConfig.h +similarity index 87% +rename from skia/config/SkUserConfig.h +rename to skia/config/SkPdfiumUserConfig.h +index 679a19c40..e035d09ce 100644 +--- a/skia/config/SkUserConfig.h ++++ b/skia/config/SkPdfiumUserConfig.h +@@ -1,12 +1,9 @@ +- + /* +- * Copyright 2006 The Android Open Source Project +- * ++ * Copyright 2006 The PDFium Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +- + #ifndef SKIA_CONFIG_SKUSERCONFIG_H_ + #define SKIA_CONFIG_SKUSERCONFIG_H_ + +@@ -110,14 +107,15 @@ + // #endif + + +-/* Determines whether to build code that supports the GPU backend. Some classes ++/* Determines whether to build code that supports the Ganesh GPU backend. Some classes + that are not GPU-specific, such as SkShader subclasses, have optional code +- that is used allows them to interact with the GPU backend. If you'd like to +- omit this code set SK_SUPPORT_GPU to 0. This also allows you to omit the gpu +- directories from your include search path when you're not building the GPU +- backend. Defaults to 1 (build the GPU code). +- */ +-// #define SK_SUPPORT_GPU 1 ++ that is used to interact with this GPU backend. If you'd like to ++ include this code, include -DSK_GANESH in your cflags or uncomment below. ++ Defaults to not set (No Ganesh GPU backend). ++ This define affects the ABI of Skia, so make sure it matches the client which uses ++ the compiled version of Skia. ++*/ ++// #define SK_GANESH + + /* Skia makes use of histogram logging macros to trace the frequency of + * events. By default, Skia provides no-op versions of these macros. +@@ -126,6 +124,7 @@ + */ + // #define SK_HISTOGRAM_BOOLEAN(name, value) + // #define SK_HISTOGRAM_ENUMERATION(name, value, boundary_value) ++#include "third_party/base/component_export.h" + + // ===== Begin Chrome-specific definitions ===== + +@@ -134,12 +133,21 @@ + + #define GR_MAX_OFFSCREEN_AA_DIM 512 + ++// Handle exporting using base/component_export.h ++#define SK_API COMPONENT_EXPORT(SKIA) ++ + // Log the file and line number for assertions. ++#if defined(SK_BUILD_FOR_WIN) && !defined(__clang__) ++// String formatting with this toolchain not supported. ++#define SkDebugf(...) SkDebugf_FileLineOnly(__FILE__, __LINE__) ++SK_API void SkDebugf_FileLineOnly(const char* file, int line); ++#else + #define SkDebugf(...) SkDebugf_FileLine(__FILE__, __LINE__, __VA_ARGS__) + SK_API void SkDebugf_FileLine(const char* file, + int line, + const char* format, + ...); ++#endif + + #if !defined(ANDROID) // On Android, we use the skia default settings. + #define SK_A32_SHIFT 24 +@@ -249,6 +257,15 @@ SK_API void SkDebugf_FileLine(const char* file, + + #define SK_DISABLE_TILE_IMAGE_FILTER_OPTIMIZATION + ++#if defined(SK_BUILD_FOR_WIN) && !defined(__clang__) ++#define SK_ABORT(format, ...) \ ++ SkAbort_FileLine(__FILE__, __LINE__, format, ##__VA_ARGS__) ++[[noreturn]] SK_API void SkAbort_FileLine(const char* file, ++ int line, ++ const char* format, ++ ...); ++#endif ++ + // ===== End Chrome-specific definitions ===== + + #endif // SKIA_CONFIG_SKUSERCONFIG_H_ +diff --git a/skia/ext/google_logging.cc b/skia/ext/google_logging.cc +index 10d76741d..b5003ad22 100644 +--- a/skia/ext/google_logging.cc ++++ b/skia/ext/google_logging.cc +@@ -1,4 +1,4 @@ +-// Copyright (c) 2012 The Chromium Authors. All rights reserved. ++// Copyright 2012 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,6 +11,10 @@ + + #include "third_party/skia/include/core/SkTypes.h" + ++#if defined(SK_BUILD_FOR_WIN) && !defined(__clang__) ++#include ++#endif ++ + void SkDebugf_FileLine(const char* file, int line, const char* format, ...) { + va_list ap; + va_start(ap, format); +@@ -19,3 +23,24 @@ void SkDebugf_FileLine(const char* file, int line, const char* format, ...) { + vfprintf(stderr, format, ap); + va_end(ap); + } ++ ++#if defined(SK_BUILD_FOR_WIN) && !defined(__clang__) ++ ++void SkDebugf_FileLineOnly(const char* file, int line) { ++ fprintf(stderr, "%s:%d\n", file, line); ++} ++ ++void SkAbort_FileLine(const char* file, int line, const char* format, ...) { ++ va_list ap; ++ va_start(ap, format); ++ ++ fprintf(stderr, "%s:%d ", file, line); ++ vfprintf(stderr, format, ap); ++ va_end(ap); ++ ++ sk_abort_no_print(); ++ // Extra safety abort(). ++ abort(); ++} ++ ++#endif // defined(SK_BUILD_FOR_WIN) && !defined(__clang__) +diff --git a/skia/features.gni b/skia/features.gni +index 47771d1b3..7306075fc 100644 +--- a/skia/features.gni ++++ b/skia/features.gni +@@ -1,4 +1,4 @@ +-# Copyright 2019 The PDFium Authors. All rights reserved. ++# Copyright 2019 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +diff --git a/testing/BUILD.gn b/testing/BUILD.gn +index 787aa6df7..034de3c72 100644 +--- a/testing/BUILD.gn ++++ b/testing/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -7,16 +7,23 @@ import("../pdfium.gni") + source_set("test_support") { + testonly = true + sources = [ ++ "command_line_helpers.cpp", ++ "command_line_helpers.h", ++ "font_renamer.cpp", ++ "font_renamer.h", + "fx_string_testhelpers.cpp", + "fx_string_testhelpers.h", + "invalid_seekable_read_stream.cpp", + "invalid_seekable_read_stream.h", + "pseudo_retainable.h", ++ "scoped_set_tz.cpp", ++ "scoped_set_tz.h", + "string_write_stream.cpp", + "string_write_stream.h", ++ "test_fonts.cpp", ++ "test_fonts.h", + "test_loader.cpp", + "test_loader.h", +- "test_support.cpp", + "test_support.h", + "utils/bitmap_saver.cpp", + "utils/bitmap_saver.h", +@@ -24,10 +31,12 @@ source_set("test_support") { + "utils/file_util.h", + "utils/hash.cpp", + "utils/hash.h", +- "utils/path_service.cpp", +- "utils/path_service.h", + ] + data = [ "resources/" ] ++ public_deps = [ ++ ":path_service", ++ "//third_party/test_fonts", ++ ] + deps = [ + "../:pdfium_public_headers", + "../core/fdrm", +@@ -35,9 +44,11 @@ source_set("test_support") { + "../core/fxge", + "image_diff", + ] +- configs += [ "../:pdfium_core_config" ] ++ configs += [ ++ "../:pdfium_strict_config", ++ "../:pdfium_noshorten_config", ++ ] + visibility = [ "../*" ] +- + if (pdf_enable_v8) { + sources += [ + "v8_initializer.cpp", +@@ -51,25 +62,92 @@ source_set("test_support") { + } + } + +-source_set("unit_test_support") { ++source_set("path_service") { + testonly = true +- sources = [] +- deps = [] +- +- configs += [ "../:pdfium_core_config" ] ++ sources = [ ++ "utils/path_service.cpp", ++ "utils/path_service.h", ++ ] ++ deps = [ "../core/fxcrt" ] ++ configs += [ ++ "../:pdfium_strict_config", ++ "../:pdfium_noshorten_config", ++ ] + visibility = [ "../*" ] ++} + ++source_set("test_environments") { ++ testonly = true ++ sources = [ ++ "pdf_test_environment.cpp", ++ "pdf_test_environment.h", ++ ] ++ deps = [ ++ ":test_support", ++ "../core/fxcrt", ++ "../core/fxge", ++ "//testing/gtest", ++ ] ++ configs += [ ++ "../:pdfium_strict_config", ++ "../:pdfium_noshorten_config", ++ ] ++ if (pdf_enable_v8) { ++ sources += [ ++ "v8_test_environment.cpp", ++ "v8_test_environment.h", ++ ] ++ deps += [ ++ "../fxjs", ++ "//v8", ++ "//v8:v8_libplatform", ++ ] ++ configs += [ "//v8:external_startup_data" ] ++ } + if (pdf_enable_xfa) { + sources += [ +- "xfa_unit_test_support.cpp", +- "xfa_unit_test_support.h", ++ "xfa_test_environment.cpp", ++ "xfa_test_environment.h", + ] + deps += [ +- "../:pdfium", +- "../core/fxge", +- "../xfa/fgas", ++ "../fxjs:gc", ++ "../xfa/fgas/font", ++ ] ++ } ++} ++ ++source_set("unit_test_support") { ++ testonly = true ++ sources = [] ++ deps = [] ++ configs += [ ++ "../:pdfium_strict_config", ++ "../:pdfium_noshorten_config", ++ ] ++ public_deps = [ ++ ":test_environments", ++ ":test_support", ++ ] ++ if (pdf_enable_v8) { ++ sources += [ ++ "fxv8_unittest.cpp", ++ "fxv8_unittest.h", ++ ] ++ deps += [ ++ "../fxjs", + "//testing/gtest", + ] ++ configs += [ "//v8:external_startup_data" ] ++ if (pdf_enable_xfa) { ++ sources += [ ++ "fxgc_unittest.cpp", ++ "fxgc_unittest.h", ++ ] ++ deps += [ ++ "../fxjs:gc", ++ "//testing/gtest", ++ ] ++ } + } + } + +@@ -78,6 +156,10 @@ source_set("embedder_test_support") { + sources = [ + "embedder_test.cpp", + "embedder_test.h", ++ "embedder_test_constants.cpp", ++ "embedder_test_constants.h", ++ "embedder_test_environment.cpp", ++ "embedder_test_environment.h", + "embedder_test_mock_delegate.h", + "embedder_test_timer_handling_delegate.h", + "fake_file_access.cpp", +@@ -85,26 +167,37 @@ source_set("embedder_test_support") { + "range_set.cpp", + "range_set.h", + ] +- + deps = [ +- ":test_support", + "../:pdfium_public_headers", + "../core/fdrm", + "../core/fxcrt", ++ "../core/fxge", + "../third_party:pdfium_base", + "//testing/gmock", + "//testing/gtest", + ] +- configs += [ "../:pdfium_core_config" ] ++ public_deps = [ ++ ":test_environments", ++ ":test_support", ++ ] ++ configs += [ ++ "../:pdfium_strict_config", ++ "../:pdfium_noshorten_config", ++ ] + visibility = [ "../*" ] +- + if (pdf_enable_v8) { + sources += [ ++ "external_engine_embedder_test.cpp", ++ "external_engine_embedder_test.h", + "js_embedder_test.cpp", + "js_embedder_test.h", + ] +- deps += [ "../fxjs" ] +- ++ deps += [ ++ "../fxjs", ++ "//v8", ++ "//v8:v8_libplatform", ++ ] ++ configs += [ "//v8:external_startup_data" ] + if (pdf_enable_xfa) { + sources += [ + "xfa_js_embedder_test.cpp", +@@ -119,3 +212,7 @@ source_set("embedder_test_support") { + } + } + } ++ ++# Dummy group to keep satisfy references from //build. ++group("test_scripts_shared") { ++} +diff --git a/testing/SUPPRESSIONS b/testing/SUPPRESSIONS +index b3059e9cf..b50d3ce4f 100644 +--- a/testing/SUPPRESSIONS ++++ b/testing/SUPPRESSIONS +@@ -1,4 +1,4 @@ +-# Copyright 2016 The PDFium Authors. All rights reserved. ++# Copyright 2016 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + # +@@ -10,6 +10,7 @@ + # Column 1: platform: *, win, mac, linux + # Column 2: v8 support: *, nov8, v8 + # Column 3: xfa support: *, noxfa, xfa ++# Column 4: rendering support: *, agg, skia + # + # All columns on a line on a line must match, but filenames may be repeated + # on subsequent lines to suppress more cases. Within each column, any one of +@@ -21,337 +22,379 @@ + # + # Corpus tests + # +-050_extra_m.pdf mac,win * * +-12.pdf mac * * +-1_10_watermark.pdf * * * +-1_1_textbox.pdf * * * +-1_2_typewriter.pdf * * * +-1_3_callout.pdf * * * +-1_matrix.pdf mac * * +-1m_diff_lsjdf.pdf mac * * +-1m_same_xxxx.pdf mac * * +-2_11_stamp3.pdf mac * * +-2_6_textbox.pdf * * * +-2_color_calrgb.pdf mac * * +-2_color_indexed.pdf mac * * +-3_4_textbox.pdf * * * +-3_interpolate_image.pdf mac * * +-3bigpreview.pdf mac,win * * +-4_35.pdf mac * * +-4_39.pdf mac * * +-5.1.pdf mac * * +-5.2.pdf * * * +-5.5_simple_font.pdf mac,win * * +-8.2_name_dest_f_dest.pdf mac * * +-8.2_outline.pdf mac * * +-8.3_presentation.pdf mac * * +-FRC_10_8.2.2__T8.3_original_file.pdf * * * +-FRC_11_8.2.2__T8.3_first_last_exchange.pdf * * * +-FRC_12_8.2.2__T8.3_first_outline_obj_ID.pdf * * * +-FRC_13_8.2.2__T8.3_Count_edit300.pdf * * * +-FRC_14_8.2.2__T8.3_Count_edit0.pdf * * * +-FRC_15_8.2.2__T8.3_Count_edit1.pdf * * * +-FRC_16_8.2.2__T8.3_Count_edit_1.pdf * * * +-FRC_1_8.2.2__T8.3_First_empty.pdf * * * +-FRC_2_8.2.2__T8.3_Last_empty.pdf * * * +-FRC_3.5_P__3616_Password_1.pdf * * * +-FRC_3_8.2.2_Type_empty.pdf * * * +-FRC_4_8.2.2__T8.3_Count_empty.pdf * * * +-FRC_5_8.2.2__T8.3_First_remove.pdf * * * +-FRC_6_8.2.2__T8.3_Last_remove.pdf * * * +-FRC_7_8.2.2_Type_remove.pdf * * * +-FRC_8.4.1_Annotations_M.pdf * * * +-FRC_8.4.1_Annotations_NM.pdf * * * +-FRC_8.5_Page_C_SubmitForm.pdf * * * +-FRC_8.5_Page_PI_ResetForm_Phantom.pdf * * * +-FRC_8.5_Widget_F.pdf * nov8 * +-FRC_8_8.2.2__T8.3_Count_remove.pdf * * * +-FRC_9_8.2.2__T8.3_remove_first_item.pdf * * * +-action.pdf * * * +-action_execute_a_menu_item.pdf mac * * +-action_hide_show_form.pdf mac * * +-action_on_focus.pdf mac * * +-action_open_a_file.pdf mac * * +-action_pdf_save_close.pdf mac * * +-action_reset.pdf mac * * +-action_run_javascript.pdf mac * * +-action_submit_a_form.pdf mac * * +-all_trigger_alert.pdf * * * +-all_trigger_browsefordoc.pdf * * * +-all_trigger_mailmsg.pdf * * * +-all_trigger_newdoc.pdf * * * +-all_trigger_print.pdf * * * +-all_trigger_run_js_lunchurl.pdf mac * * +-all_trigger_run_js_maildoc.pdf mac * * +-annotation_highlight_author_content.pdf mac * * +-annotation_highlight_long_content.pdf mac * * +-annotation_highlight_no_author.pdf mac * * +-app_launchurl.pdf mac * * +-appstoredescription3.1_en_updated.pdf mac * * +-bookmark.pdf * * * +-bookmarkgetcolor.pdf mac * * +-bug_0_length_line.pdf mac * * +-bug_0_width_line.pdf mac * * +-bug_440132.pdf mac * * +-bug_white_space.pdf mac * * +-calcorderindex_test.pdf * * * +-calculate_average.pdf mac * * +-calculate_order.pdf * * * +-calculate_sum_a_b_c.pdf mac * * +-calculate_validate.pdf mac * * +-calculate_validate.pdf * nov8 * +-ch_1.pdf * * * +-check_box.pdf * * * +-color.pdf mac * * +-colorspace.pdf mac * * +-colorspace_test1.pdf mac * * +-combo_box.pdf * * * +-combo_box_format.pdf mac * * +-date.pdf mac * * +-edit_transform.pdf mac * * +-en_contact.pdf mac * * +-en_diy.pdf mac * * +-en_foxit.pdf mac * * +-en_fqa2.pdf mac * * +-en_introduce.pdf mac * * +-en_tem.pdf mac * * +-en_uicase_.pdf mac * * +-event.change.pdf mac * * +-event.changeex.pdf mac * * +-event.keydown.pdf mac * * +-event.keydown_1_.pdf mac * * +-event.type_name.pdf mac * * +-event.value.pdf mac * * +-event_change.pdf mac * * +-example_001.pdf mac * * +-example_002.pdf mac,win * * +-example_003.pdf mac,win * * +-example_004.pdf mac * * +-example_005.pdf mac * * +-example_006.pdf mac,win * * +-example_007.pdf mac * * +-example_008.pdf mac * * +-example_009.pdf mac * * +-example_010.pdf mac * * +-example_011.pdf mac * * +-example_012.pdf mac * * +-example_013.pdf mac * * +-example_014.pdf mac * * +-example_015.pdf mac * * +-example_016.pdf mac * * +-example_017.pdf mac * * +-example_018.pdf mac * * +-example_019.pdf mac * * +-example_020.pdf mac * * +-example_021.pdf mac * * +-example_022.pdf mac * * +-example_023.pdf mac * * +-example_024.pdf mac * * +-example_025.pdf mac * * +-example_026.pdf mac * * +-example_027.pdf mac * * +-example_028.pdf mac * * +-example_029.pdf mac * * +-example_030.pdf mac * * +-example_031.pdf mac * * +-example_032.pdf mac * * +-example_033.pdf mac,win * * +-example_034.pdf mac * * +-example_035.pdf mac,win * * +-example_036.pdf mac,win * * +-example_037.pdf mac * * +-example_038.pdf mac,win * * +-example_039.pdf mac * * +-example_040.pdf mac * * +-example_041.pdf mac,win * * +-example_042.pdf mac * * +-example_043.pdf mac * * +-example_044.pdf mac * * +-example_045.pdf mac,win * * +-example_046.pdf mac,win * * +-example_047.pdf mac * * +-example_048.pdf mac * * +-example_049.pdf mac * * +-example_050.pdf mac * * +-example_051.pdf mac * * +-example_052.pdf mac * * +-example_053.pdf mac * * +-example_054.pdf mac * * +-example_055.pdf mac,win * * +-example_056.pdf mac * * +-example_057.pdf mac * * +-example_058.pdf mac * * +-example_059.pdf mac,win * * +-example_060.pdf mac * * +-example_061.pdf mac,win * * +-example_062.pdf mac * * +-example_063.pdf mac * * +-example_064.pdf mac * * +-example_065.pdf mac * * +-fillform.pdf * * * +-form_action_trigger.pdf * * * +-form_button_sign_url.pdf * * * +-form_combo_sign_url.pdf * * * +-form_combobox0.pdf * * * +-form_combobox_actioin_goto.pdf * * * +-form_combobox_date.pdf mac * * +-form_combobox_date.pdf * nov8 * +-form_combobox_date1.pdf * * * +-form_combobox_date2.pdf mac * * +-form_combobox_date2.pdf * nov8 * +-form_combobox_importform.pdf * * * +-form_combobox_num.pdf mac * * +-form_combobox_num.pdf * nov8 * +-form_combobox_per.pdf mac * * +-form_combobox_per.pdf * nov8 * +-form_combobox_plus.pdf mac * * +-form_combobox_plus.pdf * nov8 * +-form_combobox_product.pdf mac * * +-form_combobox_product.pdf * nov8 * +-form_combobox_resetform.pdf * * * +-form_combobox_time.pdf mac * * +-form_combobox_time.pdf * nov8 * +-form_list.pdf * * * +-form_list1.pdf * * * +-form_same.pdf mac * * +-form_text_sign_url.pdf * * * +-format_combo_box.pdf mac * * +-format_combo_box.pdf * nov8 * +-format_custom_format.pdf linux,mac * * +-format_custom_format.pdf * nov8 * +-format_custom_keystroke.pdf * * * +-format_date.pdf * nov8 * +-format_number.pdf mac * * +-format_percentage.pdf mac * * +-format_special.pdf * nov8 * +-format_text_color.pdf mac * * +-formfeild.pdf * * * +-getarray.pdf mac * * +-javascriptaction.pdf * * * +-jetman_std.pdf mac * * +-jetman_std_fixed.pdf mac * * +-js_calculate.pdf * * * +-list_box.pdf * * * +-negative.pdf mac * * +-new_certify1.pdf mac * * +-new_signature1.pdf mac * * +-new_signature2.pdf mac * * +-new_stamp4.pdf mac * * +-new_stamp5.pdf mac * * +-new_textmarkup1.pdf mac * * +-new_textmarkup1_hidden.pdf mac * * +-new_textmarkup2.pdf mac * * +-new_textmarkup2_hidden.pdf mac * * +-new_textmarkup4.pdf mac * * +-new_textmarkup4_hidden.pdf mac * * +-new_textmarkup5.pdf mac * * +-new_textmarkup5_hidden.pdf mac * * +-new_textmarkup6.pdf mac * * +-new_textmarkup7.pdf mac * * +-new_textmarkup7_hidden.pdf mac * * +-new_textmarkup8.pdf mac * * +-new_textmarkup8_hidden.pdf mac * * +-number.pdf * * * +-octest.pdf mac * * +-open_a_weblink.pdf mac * * +-path_10_jd.pdf mac * * +-path_5_pattern.pdf mac * * +-path_6_graphics4.5.5.pdf mac * * +-path_7.pdf mac * * +-path_9.pdf mac * * +-percentage.pdf mac * * +-push_button.pdf * * * +-quick_start_guide.pdf mac * * +-radio_button.pdf * * * +-run_custom_validate_script.pdf * * * +-show_1.pdf mac * * +-signature.pdf * * * +-signature_4.pdf * * * +-simplified_field_notation.pdf mac * * +-special.pdf mac * * +-submit_form.pdf mac * * +-test_app_beep.pdf * * * +-test_control.pdf * * * +-test_m.pdf mac,win * * +-text_field.pdf * * * +-text_field_font_input_decimal_point.pdf mac * * +-text_field_multiline_line_spacing.pdf mac * * +-thread_action.pdf mac * * +-time.pdf mac * * +-transformation.pdf mac * * +-transparent.pdf mac * * +-whats_new_in_v3.0.pdf mac * * +-widget_javascript.pdf mac * * +-zh_file1.pdf mac * * +-zh_function_list.pdf mac * * +-zh_shared_document.pdf mac * * ++12.pdf mac * * agg ++1_1_textbox.pdf * * * * ++1_2_typewriter.pdf * * * * ++1_3_callout.pdf * * * * ++1_matrix.pdf mac * * agg ++1m_diff_lsjdf.pdf mac * * agg ++1m_same_xxxx.pdf mac * * agg ++2_11_stamp3.pdf mac * * agg ++2_6_textbox.pdf * * * * ++2_color_calrgb.pdf mac * * agg ++2_color_indexed.pdf mac * * agg ++3_4_textbox.pdf * * * * ++3_interpolate_image.pdf mac * * agg ++3bigpreview.pdf mac * * agg ++4_35.pdf mac * * agg ++4_39.pdf mac * * agg ++5.1.pdf mac * * agg ++5.2.pdf * * * * ++5.5_simple_font.pdf mac * * agg ++8.2_name_dest_f_dest.pdf mac * * agg ++8.2_outline.pdf mac * * agg ++8.3_presentation.pdf mac * * agg ++FRC_10_8.2.2__T8.3_original_file.pdf * * * * ++FRC_11_8.2.2__T8.3_first_last_exchange.pdf * * * * ++FRC_12_8.2.2__T8.3_first_outline_obj_ID.pdf * * * * ++FRC_13_8.2.2__T8.3_Count_edit300.pdf * * * * ++FRC_14_8.2.2__T8.3_Count_edit0.pdf * * * * ++FRC_15_8.2.2__T8.3_Count_edit1.pdf * * * * ++FRC_16_8.2.2__T8.3_Count_edit_1.pdf * * * * ++FRC_1_8.2.2__T8.3_First_empty.pdf * * * * ++FRC_2_8.2.2__T8.3_Last_empty.pdf * * * * ++FRC_3.5_P__3616_Password_1.pdf * * * * ++FRC_3_8.2.2_Type_empty.pdf * * * * ++FRC_4_8.2.2__T8.3_Count_empty.pdf * * * * ++FRC_5_8.2.2__T8.3_First_remove.pdf * * * * ++FRC_6_8.2.2__T8.3_Last_remove.pdf * * * * ++FRC_7_8.2.2_Type_remove.pdf * * * * ++FRC_8.4.1_Annotations_M.pdf * * * * ++FRC_8.4.1_Annotations_NM.pdf * * * * ++FRC_8.5_Page_C_SubmitForm.pdf * * * * ++FRC_8.5_Page_PI_ResetForm_Phantom.pdf * * * * ++FRC_8.5_Widget_F.pdf * nov8 * * ++FRC_8_8.2.2__T8.3_Count_remove.pdf * * * * ++FRC_9_8.2.2__T8.3_remove_first_item.pdf * * * * ++action.pdf * * * * ++action_execute_a_menu_item.pdf mac * * agg ++action_hide_show_form.pdf mac * * agg ++action_on_focus.pdf mac * * agg ++action_open_a_file.pdf mac * * agg ++action_pdf_save_close.pdf mac * * agg ++action_reset.pdf mac * * agg ++action_run_javascript.pdf mac * * agg ++action_submit_a_form.pdf mac * * agg ++all_trigger_alert.pdf * * * * ++all_trigger_mailmsg.pdf * * * * ++all_trigger_print.pdf * * * * ++all_trigger_run_js_lunchurl.pdf mac * * agg ++all_trigger_run_js_maildoc.pdf mac * * agg ++annotation_highlight_author_content.pdf mac * * agg ++annotation_highlight_long_content.pdf mac * * agg ++annotation_highlight_no_author.pdf mac * * agg ++app_launchurl.pdf mac * * agg ++appstoredescription3.1_en_updated.pdf mac * * agg ++bookmark.pdf * * * * ++bookmarkgetcolor.pdf mac * * agg ++bug_0_length_line.pdf mac * * agg ++ ++# TODO(pdfium:1812): Remove after associated bug is fixed ++bug_0_length_line.pdf * * * skia ++ ++bug_0_width_line.pdf mac * * agg ++bug_440132.pdf mac * * agg ++bug_white_space.pdf mac * * agg ++calcorderindex_test.pdf * * * * ++calculate_average.pdf mac * * agg ++calculate_order.pdf * * * * ++calculate_sum_a_b_c.pdf mac * * agg ++calculate_validate.pdf mac * * agg ++calculate_validate.pdf * nov8 * * ++ch_1.pdf * * * * ++check_box.pdf * * * * ++color.pdf mac * * agg ++colorspace.pdf mac * * agg ++colorspace_test1.pdf mac * * agg ++combo_box.pdf * * * * ++combo_box_format.pdf mac * * agg ++date.pdf mac * * agg ++edit_transform.pdf mac * * agg ++en_contact.pdf mac * * agg ++en_diy.pdf mac * * agg ++en_foxit.pdf mac * * agg ++en_fqa2.pdf mac * * agg ++en_introduce.pdf mac * * agg ++en_tem.pdf mac * * agg ++en_uicase_.pdf mac * * agg ++event.change.pdf mac * * agg ++event.changeex.pdf mac * * agg ++event.keydown.pdf mac * * agg ++event.keydown_1_.pdf mac * * agg ++event.type_name.pdf mac * * agg ++event.value.pdf mac * * agg ++event_change.pdf mac * * agg ++example_001.pdf mac * * agg ++example_002.pdf mac * * agg ++example_003.pdf mac * * agg ++example_004.pdf mac * * agg ++example_005.pdf mac * * agg ++example_006.pdf mac * * agg ++example_007.pdf mac * * agg ++example_008.pdf mac * * agg ++example_009.pdf mac * * agg ++example_010.pdf mac * * agg ++example_011.pdf mac * * agg ++example_012.pdf mac * * agg ++example_013.pdf mac * * agg ++example_014.pdf mac * * agg ++example_015.pdf mac * * agg ++example_016.pdf mac * * agg ++example_017.pdf mac * * agg ++example_018.pdf mac * * agg ++example_019.pdf mac * * agg ++example_020.pdf mac * * agg ++example_021.pdf mac * * agg ++example_022.pdf mac * * agg ++example_023.pdf mac * * agg ++example_024.pdf mac * * agg ++example_025.pdf mac * * agg ++example_026.pdf mac * * agg ++example_027.pdf mac * * agg ++example_028.pdf mac * * agg ++example_029.pdf mac * * agg ++example_030.pdf mac * * agg ++example_031.pdf mac * * agg ++example_032.pdf mac * * agg ++example_033.pdf mac * * agg ++example_034.pdf mac * * agg ++example_035.pdf mac * * agg ++example_036.pdf mac * * agg ++example_037.pdf mac * * agg ++example_038.pdf mac * * agg ++example_039.pdf mac * * agg ++example_040.pdf mac * * agg ++example_041.pdf mac * * agg ++example_042.pdf mac * * agg ++example_043.pdf mac * * agg ++example_044.pdf mac * * agg ++example_045.pdf mac * * agg ++example_046.pdf mac * * agg ++example_047.pdf mac * * agg ++example_048.pdf mac * * agg ++example_049.pdf mac * * agg ++example_050.pdf mac * * agg ++example_051.pdf mac * * agg ++example_052.pdf mac * * agg ++example_053.pdf mac * * agg ++example_054.pdf mac * * agg ++example_055.pdf mac * * agg ++example_056.pdf mac * * agg ++example_057.pdf mac * * agg ++example_058.pdf mac * * agg ++example_059.pdf mac * * agg ++example_060.pdf mac * * agg ++example_061.pdf mac * * agg ++example_062.pdf mac * * agg ++example_063.pdf mac * * agg ++example_064.pdf mac * * agg ++example_065.pdf mac * * agg ++fillform.pdf * * * * ++form_action_trigger.pdf * * * * ++form_button_sign_url.pdf * * * * ++form_combo_sign_url.pdf * * * * ++form_combobox0.pdf * * * * ++form_combobox_actioin_goto.pdf * * * * ++form_combobox_date.pdf mac * * agg ++form_combobox_date.pdf * nov8 * * ++form_combobox_date1.pdf * * * * ++form_combobox_date2.pdf mac * * agg ++form_combobox_date2.pdf * nov8 * * ++form_combobox_importform.pdf * * * * ++form_combobox_num.pdf mac * * agg ++form_combobox_num.pdf * nov8 * * ++form_combobox_per.pdf mac * * agg ++form_combobox_per.pdf * nov8 * * ++form_combobox_plus.pdf mac * * agg ++form_combobox_plus.pdf * nov8 * * ++form_combobox_product.pdf mac * * agg ++form_combobox_product.pdf * nov8 * * ++form_combobox_resetform.pdf * * * * ++form_combobox_time.pdf mac * * agg ++form_combobox_time.pdf * nov8 * * ++form_list.pdf * * * * ++form_list1.pdf * * * * ++form_same.pdf mac * * agg ++form_text_sign_url.pdf * * * * ++format_combo_box.pdf mac * * agg ++format_combo_box.pdf * nov8 * * ++format_custom_format.pdf * nov8 * * ++format_custom_keystroke.pdf * * * * ++format_date.pdf * nov8 * * ++format_number.pdf mac * * agg ++format_percentage.pdf mac * * agg ++format_special.pdf * nov8 * * ++format_text_color.pdf mac * * agg ++formfield.pdf * * * * ++getarray.pdf mac * * agg ++ ++# TODO(pdfium:489): Remove after associated bug is fixed. ++gradient_many_stops.pdf * * * agg ++ ++javascriptaction.pdf * * * * ++jetman_std.pdf mac * * agg ++jetman_std_fixed.pdf mac * * agg ++js_calculate.pdf * * * * ++list_box.pdf * * * * ++negative.pdf mac * * agg ++new_certify1.pdf mac * * agg ++new_signature1.pdf mac * * agg ++new_signature2.pdf mac * * agg ++new_stamp4.pdf mac * * agg ++new_stamp5.pdf mac * * agg ++new_textmarkup1.pdf mac * * agg ++new_textmarkup1_hidden.pdf mac * * agg ++new_textmarkup2.pdf mac * * agg ++new_textmarkup2_hidden.pdf mac * * agg ++new_textmarkup4.pdf mac * * agg ++new_textmarkup4_hidden.pdf mac * * agg ++new_textmarkup5.pdf mac * * agg ++new_textmarkup5_hidden.pdf mac * * agg ++new_textmarkup6.pdf mac * * agg ++new_textmarkup7.pdf mac * * agg ++new_textmarkup7_hidden.pdf mac * * agg ++new_textmarkup8.pdf mac * * agg ++new_textmarkup8_hidden.pdf mac * * agg ++number.pdf * * * * ++octest.pdf mac * * agg ++open_a_weblink.pdf mac * * agg ++path_10_jd.pdf mac * * agg ++path_5_pattern.pdf mac * * agg ++path_6_graphics4.5.5.pdf mac * * agg ++path_7.pdf mac * * agg ++path_9.pdf mac * * agg ++percentage.pdf mac * * agg ++push_button.pdf * * * * ++quick_start_guide.pdf mac * * agg ++radio_button.pdf * * * * ++run_custom_validate_script.pdf * * * * ++show_1.pdf mac * * agg ++signature.pdf * * * * ++signature_4.pdf * * * * ++simplified_field_notation.pdf mac * * agg ++special.pdf mac * * agg ++submit_form.pdf mac * * agg ++test_app_beep.pdf * * * * ++test_control.pdf * * * * ++text_field.pdf * * * * ++text_field_font_input_decimal_point.pdf mac * * agg ++text_field_multiline_line_spacing.pdf mac * * agg ++thread_action.pdf mac * * agg ++time.pdf mac * * agg ++transformation.pdf mac * * agg ++transparent.pdf mac * * agg ++whats_new_in_v3.0.pdf mac * * agg ++widget_javascript.pdf mac * * agg ++ ++# TODO(pdfium:1990): Remove after associated bug is fixed ++xfermodes2.pdf * * * agg ++ ++zh_file1.pdf mac * * agg ++zh_function_list.pdf mac * * agg ++zh_shared_document.pdf mac * * agg + + # TODO(hnakashima): These might never have been run. Go over them and fix. + +-FRC_8.5_E&X.pdf * * * +-FRC_8.5_O&PO_GoToE.pdf * * * +-FRC_8.5_OpenAction&O_URI.pdf * * * +-FRC_8.5_PC&C_GoToE_T_T.pdf * * * +-FRC_8.5_PC_GoToE_T_R&P&A.pdf * * * +-FRC_8.5_PO_GoToE_T_R&N.pdf * * * ++FRC_8.5_E&X.pdf * * * * ++FRC_8.5_O&PO_GoToE.pdf * * * * ++FRC_8.5_OpenAction&O_URI.pdf * * * * ++FRC_8.5_PC&C_GoToE_T_T.pdf * * * * ++FRC_8.5_PC_GoToE_T_R&P&A.pdf * * * * ++FRC_8.5_PO_GoToE_T_R&N.pdf * * * * + + # xfa_specific + +-Choose.pdf * * * +-data_binding.pdf * * * ++Choose.pdf * * * * ++data_binding.pdf * * * * + # TODO(npm): Add proper evt for MouseEvents. +-MouseEvents_enter.pdf * * * +-MouseEvents_exit.pdf * * * +-Oneof3.pdf * * * +-Sum.pdf * * * +-TimeField.pdf win,linux * * +-Test_CheckBox.pdf * * * +-Test_DateField_locale_zh_HK.pdf mac,win * * ++MouseEvents_enter.pdf * * * * ++MouseEvents_exit.pdf * * * * ++Oneof3.pdf * * * * ++Sum.pdf * * * * ++Test_CheckBox.pdf * * * * + + # + # JavaScript tests + # +-bug_679642.in * * noxfa +-bug_679643.in * * noxfa +-bug_735912.in * * noxfa ++bug_679642.in * * noxfa * ++bug_679643.in * * noxfa * ++bug_735912.in * * noxfa * ++ ++# JS tests in nov8 mode expect empty results. This one will ++# not be empty as the callback is not js-based. ++named_action.in * nov8 * * + + # xfa_specific + + # TODO(pdfium:1106): Remove after associated bug is fixed +-resolve_nodes_1.pdf * * * ++resolve_nodes_1.pdf * * * * + + # + # Pixel tests + # +-bug_492.in * nov8 * + +-# TODO(pdfium:304): Remove after associated bug is fixed +-bug_304.pdf * * * ++# TODO(pdfium:1747): Remove after associated bug is fixed ++bug_1258634.in * * * * + + # TODO(pdfium:1331): Remove after associated bug is fixed +-bug_1331.in * * * ++bug_1331.in * * * * + +-# TODO(pdfium:1461): Remove after associated bug is fixed +-bug_1402.in win * * ++# TODO(chromium:1356149): Remove after associated bug is fixed ++bug_1356149.in mac * * agg + + # TODO(pdfium:1457): Remove after associated bug is fixed +-bug_1457.in * * * ++bug_1457.in * * * * ++ ++# TODO(pdfium:1519): Remove after associated bug is fixed ++bug_1519.in * * * * ++ ++# TODO(pdfium:1571): Remove after associated bug is fixed ++bug_1571.in * * * * ++ ++# TODO(pdfium:1723): Remove after associated bug is fixed ++bug_1723.in * * * * ++ ++# TODO(pdfium:1972): Remove after associated bug is fixed ++bug_1972_1.in * * * agg ++bug_1972_2.in * * * agg ++bug_1972_3.in * * * agg ++ ++# TODO(pdfium:1973): Remove after associated bug is fixed ++bug_1973.in * * * * ++ ++# TODO(pdfium:2001): Remove after associated bug is fixed ++bug_2001.pdf * * * * ++ ++# TODO(chromium:237527): Remove after associated bug is fixed ++bug_237527_1.in * * * * + + # TODO(chromium:451366): Remove after associated bug is fixed +-bug_451366.in * * * ++bug_451366.in * * * agg ++ ++# TODO(pdfium:492): Remove after associated bug is fixed ++bug_492.in * nov8 * * + +-# TODO(chromium:1012369): Remove after associated bug is fixed +-bug_1012369.in * * * ++# TODO(chromium:725555, skia:9265): Remove after associated bug is fixed ++bug_725555.in * * * skia ++ ++# TODO(chromium:983289): Remove after associated bug is fixed ++bug_983289.in * * * agg ++ ++# TODO(pdfium:1747): Remove after associated bug is fixed ++jpxdecode.in * * * * ++jpxdecode_without_bitspercomponent.in * * * * ++jpxdecode_without_colorspace.in * * * * ++ ++# TODO(chromium:1028991): Remove after associated bug is fixed ++reset_button.in * * * * + + # xfa_specific + ++# TODO(pdfium:1095): Remove after associated bug is fixed ++bug_997412.in win * * * + # TODO(pdfium:1107): Remove after associated bug is fixed +-standard_symbols.pdf * * * +-# TODO(pdfium:1168): Remove after associated bug is fixed +-xfa_bmp_image.in * * * ++standard_symbols.pdf * * * * + # TODO(pdfium:1095): Remove after associated bug is fixed +-xfa_example.in win * * +-# TODO(pdfium:1167): Remove after associated bug is fixed +-xfa_gif_image.in * * * ++xfa_example.in win * * * + # TODO(pdfium:1095): Remove after associated bug is fixed +-xfa_textfield.in win * * ++xfa_textfield.in win * * * +diff --git a/testing/SUPPRESSIONS_EXACT_MATCHING b/testing/SUPPRESSIONS_EXACT_MATCHING +new file mode 100644 +index 000000000..7c260d1f5 +--- /dev/null ++++ b/testing/SUPPRESSIONS_EXACT_MATCHING +@@ -0,0 +1,28 @@ ++# Copyright 2023 The PDFium Authors ++# Use of this source code is governed by a BSD-style license that can be ++# found in the LICENSE file. ++# ++# List of tests to use fuzzy instead of exact matching, one per line. ++# There are four space-separated columns per line ++# Each column (except column 0) can contain a comma-separated list of values. ++# ++# Column 0: test file name ++# Column 1: platform: *, win, mac, linux ++# Column 2: v8 support: *, nov8, v8 ++# Column 3: xfa support: *, noxfa, xfa ++# Column 4: rendering support: *, agg, skia ++# ++# All columns on a line on a line must match, but filenames may be repeated ++# on subsequent lines to suppress more cases. Within each column, any one of ++# the comma-separated values must match in order for the colum to "match". ++# The filenames and keywords are case-sensitive. ++# ++# Try to keep the file alphabetized within each category of test. ++ ++# ++# Corpus tests ++# ++ ++# Device-specific ColorBurn differences (see pdfium:1959). ++xfermodes2.pdf * * * skia ++xfermodes3.pdf * * * skia +diff --git a/testing/SUPPRESSIONS_IMAGE_DIFF b/testing/SUPPRESSIONS_IMAGE_DIFF +index cdb2d66f9..e5017568d 100644 +--- a/testing/SUPPRESSIONS_IMAGE_DIFF ++++ b/testing/SUPPRESSIONS_IMAGE_DIFF +@@ -1,4 +1,4 @@ +-# Copyright 2017 The PDFium Authors. All rights reserved. ++# Copyright 2017 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + # +@@ -10,6 +10,7 @@ + # Column 1: platform: *, win, mac, linux + # Column 2: v8 support: *, nov8, v8 + # Column 3: xfa support: *, noxfa, xfa ++# Column 4: rendering support: *, agg, skia + # + # All columns on a line on a line must match, but filenames may be repeated + # on subsequent lines to suppress more cases. Within each column, any one of +@@ -21,12 +22,12 @@ + # + # Corpus tests + # +-FRC_3.5_CF_Strf_stmf_DefaultCryptFilter.pdf * * * +-FRC_3.5_EncryptMetadata_T.pdf * * * +-FRC_3.5_Encrypt_is_damage.pdf * * * +-FRC_3.5_Filter_PubSec_SubFilter_s5.pdf * * * +-FRC_3.5_Filter_PubSec_Sub_SubFilter_s4.pdf * * * +-MouseEvents.pdf * * * +-Oneof.pdf * * * +-bug_651304.pdf * * * +-outline.pdf * * * ++FRC_3.5_CF_Strf_stmf_DefaultCryptFilter.pdf * * * * ++FRC_3.5_EncryptMetadata_T.pdf * * * * ++FRC_3.5_Encrypt_is_damage.pdf * * * * ++FRC_3.5_Filter_PubSec_SubFilter_s5.pdf * * * * ++FRC_3.5_Filter_PubSec_Sub_SubFilter_s4.pdf * * * * ++MouseEvents.pdf * * * * ++Oneof.pdf * * * * ++bug_651304.pdf * * * * ++outline.pdf * * * * +diff --git a/testing/command_line_helpers.cpp b/testing/command_line_helpers.cpp +new file mode 100644 +index 000000000..758f27851 +--- /dev/null ++++ b/testing/command_line_helpers.cpp +@@ -0,0 +1,23 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "testing/command_line_helpers.h" ++ ++bool ParseSwitchKeyValue(const std::string& arg, ++ const std::string& key, ++ std::string* value) { ++ if (arg.size() <= key.size() || arg.compare(0, key.size(), key) != 0) ++ return false; ++ ++ *value = arg.substr(key.size()); ++ return true; ++} ++ ++FPDF_RENDERER_TYPE GetDefaultRendererType() { ++#if defined(_SKIA_SUPPORT_) ++ return FPDF_RENDERERTYPE_SKIA; ++#else ++ return FPDF_RENDERERTYPE_AGG; ++#endif ++} +diff --git a/testing/command_line_helpers.h b/testing/command_line_helpers.h +new file mode 100644 +index 000000000..cc134cbaf +--- /dev/null ++++ b/testing/command_line_helpers.h +@@ -0,0 +1,23 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef TESTING_COMMAND_LINE_HELPERS_H_ ++#define TESTING_COMMAND_LINE_HELPERS_H_ ++ ++#include ++ ++#include "public/fpdfview.h" ++ ++// Extract the value from a keyed command line argument. ++// `arg` is expected to be "--key=value", and `key` is "--key=". ++bool ParseSwitchKeyValue(const std::string& arg, ++ const std::string& key, ++ std::string* value); ++ ++// Identifies the compile-time default 2D graphics library to use for rendering ++// to FPDF_BITMAPs. Used as part of support to override the renderer at runtime ++// based upon command line options. ++FPDF_RENDERER_TYPE GetDefaultRendererType(); ++ ++#endif // TESTING_COMMAND_LINE_HELPERS_H_ +diff --git a/testing/embedder_test.cpp b/testing/embedder_test.cpp +index 9bf4a183d..366088d18 100644 +--- a/testing/embedder_test.cpp ++++ b/testing/embedder_test.cpp +@@ -1,13 +1,10 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "testing/embedder_test.h" + +-#include +- +-#include +-#include ++#include + #include + #include + #include +@@ -19,20 +16,17 @@ + #include "public/fpdf_edit.h" + #include "public/fpdf_text.h" + #include "public/fpdfview.h" ++#include "testing/embedder_test_environment.h" + #include "testing/gmock/include/gmock/gmock.h" + #include "testing/test_loader.h" + #include "testing/utils/bitmap_saver.h" + #include "testing/utils/file_util.h" + #include "testing/utils/hash.h" + #include "testing/utils/path_service.h" +-#include "third_party/base/logging.h" +-#include "third_party/base/ptr_util.h" +-#include "third_party/base/stl_util.h" +- +-#ifdef PDF_ENABLE_V8 +-#include "v8/include/v8-platform.h" +-#include "v8/include/v8.h" +-#endif // PDF_ENABLE_V8 ++#include "third_party/base/check.h" ++#include "third_party/base/containers/contains.h" ++#include "third_party/base/notreached.h" ++#include "third_party/base/numerics/safe_conversions.h" + + namespace { + +@@ -40,7 +34,7 @@ int GetBitmapBytesPerPixel(FPDF_BITMAP bitmap) { + return EmbedderTest::BytesPerPixelForFormat(FPDFBitmap_GetFormat(bitmap)); + } + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + int CALLBACK GetRecordProc(HDC hdc, + HANDLETABLE* handle_table, + const ENHMETARECORD* record, +@@ -50,12 +44,224 @@ int CALLBACK GetRecordProc(HDC hdc, + records.push_back(record); + return 1; + } +-#endif // defined(OS_WIN) ++#endif // BUILDFLAG(IS_WIN) ++ ++// These "jump" into the delegate to do actual testing. ++void UnsupportedHandlerTrampoline(UNSUPPORT_INFO* info, int type) { ++ auto* delegate = static_cast(info)->GetDelegate(); ++ delegate->UnsupportedHandler(type); ++} ++ ++int AlertTrampoline(IPDF_JSPLATFORM* platform, ++ FPDF_WIDESTRING message, ++ FPDF_WIDESTRING title, ++ int type, ++ int icon) { ++ auto* delegate = static_cast(platform)->GetDelegate(); ++ return delegate->Alert(message, title, type, icon); ++} ++ ++int SetTimerTrampoline(FPDF_FORMFILLINFO* info, int msecs, TimerCallback fn) { ++ auto* delegate = static_cast(info)->GetDelegate(); ++ return delegate->SetTimer(msecs, fn); ++} ++ ++void KillTimerTrampoline(FPDF_FORMFILLINFO* info, int id) { ++ auto* delegate = static_cast(info)->GetDelegate(); ++ return delegate->KillTimer(id); ++} ++ ++FPDF_PAGE GetPageTrampoline(FPDF_FORMFILLINFO* info, ++ FPDF_DOCUMENT document, ++ int page_index) { ++ auto* delegate = static_cast(info)->GetDelegate(); ++ return delegate->GetPage(info, document, page_index); ++} ++ ++void DoURIActionTrampoline(FPDF_FORMFILLINFO* info, FPDF_BYTESTRING uri) { ++ auto* delegate = static_cast(info)->GetDelegate(); ++ return delegate->DoURIAction(uri); ++} ++ ++void DoGoToActionTrampoline(FPDF_FORMFILLINFO* info, ++ int page_index, ++ int zoom_mode, ++ float* pos_array, ++ int array_size) { ++ auto* delegate = static_cast(info)->GetDelegate(); ++ return delegate->DoGoToAction(info, page_index, zoom_mode, pos_array, ++ array_size); ++} ++ ++void OnFocusChangeTrampoline(FPDF_FORMFILLINFO* info, ++ FPDF_ANNOTATION annot, ++ int page_index) { ++ auto* delegate = static_cast(info)->GetDelegate(); ++ return delegate->OnFocusChange(info, annot, page_index); ++} ++ ++void DoURIActionWithKeyboardModifierTrampoline(FPDF_FORMFILLINFO* info, ++ FPDF_BYTESTRING uri, ++ int modifiers) { ++ auto* delegate = static_cast(info)->GetDelegate(); ++ return delegate->DoURIActionWithKeyboardModifier(info, uri, modifiers); ++} ++ ++// These do nothing (but must return a reasonable default value). ++void InvalidateStub(FPDF_FORMFILLINFO* pThis, ++ FPDF_PAGE page, ++ double left, ++ double top, ++ double right, ++ double bottom) {} ++ ++void OutputSelectedRectStub(FPDF_FORMFILLINFO* pThis, ++ FPDF_PAGE page, ++ double left, ++ double top, ++ double right, ++ double bottom) {} ++ ++void SetCursorStub(FPDF_FORMFILLINFO* pThis, int nCursorType) {} ++ ++FPDF_SYSTEMTIME GetLocalTimeStub(FPDF_FORMFILLINFO* pThis) { ++ return {122, 11, 6, 28, 12, 59, 59, 500}; ++} ++ ++void OnChangeStub(FPDF_FORMFILLINFO* pThis) {} ++ ++FPDF_PAGE GetCurrentPageStub(FPDF_FORMFILLINFO* pThis, FPDF_DOCUMENT document) { ++ return GetPageTrampoline(pThis, document, 0); ++} ++ ++int GetRotationStub(FPDF_FORMFILLINFO* pThis, FPDF_PAGE page) { ++ return 0; ++} ++ ++void ExecuteNamedActionStub(FPDF_FORMFILLINFO* pThis, FPDF_BYTESTRING name) {} ++ ++void SetTextFieldFocusStub(FPDF_FORMFILLINFO* pThis, ++ FPDF_WIDESTRING value, ++ FPDF_DWORD valueLen, ++ FPDF_BOOL is_focus) {} ++ ++#ifdef PDF_ENABLE_XFA ++void DisplayCaretStub(FPDF_FORMFILLINFO* pThis, ++ FPDF_PAGE page, ++ FPDF_BOOL bVisible, ++ double left, ++ double top, ++ double right, ++ double bottom) {} ++ ++int GetCurrentPageIndexStub(FPDF_FORMFILLINFO* pThis, FPDF_DOCUMENT document) { ++ return 0; ++} ++ ++void SetCurrentPageStub(FPDF_FORMFILLINFO* pThis, ++ FPDF_DOCUMENT document, ++ int iCurPage) {} ++ ++void GotoURLStub(FPDF_FORMFILLINFO* pThis, ++ FPDF_DOCUMENT document, ++ FPDF_WIDESTRING wsURL) {} ++ ++void GetPageViewRectStub(FPDF_FORMFILLINFO* pThis, ++ FPDF_PAGE page, ++ double* left, ++ double* top, ++ double* right, ++ double* bottom) { ++ *left = 0.0; ++ *top = 0.0; ++ *right = 512.0; ++ *bottom = 512.0; ++} ++ ++void PageEventStub(FPDF_FORMFILLINFO* pThis, ++ int page_count, ++ FPDF_DWORD event_type) {} ++ ++FPDF_BOOL PopupMenuStub(FPDF_FORMFILLINFO* pThis, ++ FPDF_PAGE page, ++ FPDF_WIDGET hWidget, ++ int menuFlag, ++ float x, ++ float y) { ++ return true; ++} ++ ++FPDF_FILEHANDLER* OpenFileStub(FPDF_FORMFILLINFO* pThis, ++ int fileFlag, ++ FPDF_WIDESTRING wsURL, ++ const char* mode) { ++ return nullptr; ++} ++ ++void EmailToStub(FPDF_FORMFILLINFO* pThis, ++ FPDF_FILEHANDLER* fileHandler, ++ FPDF_WIDESTRING pTo, ++ FPDF_WIDESTRING pSubject, ++ FPDF_WIDESTRING pCC, ++ FPDF_WIDESTRING pBcc, ++ FPDF_WIDESTRING pMsg) {} ++ ++void UploadToStub(FPDF_FORMFILLINFO* pThis, ++ FPDF_FILEHANDLER* fileHandler, ++ int fileFlag, ++ FPDF_WIDESTRING uploadTo) {} ++ ++int GetPlatformStub(FPDF_FORMFILLINFO* pThis, void* platform, int length) { ++ return 0; ++} ++ ++int GetLanguageStub(FPDF_FORMFILLINFO* pThis, void* language, int length) { ++ return 0; ++} ++ ++FPDF_FILEHANDLER* DownloadFromURLStub(FPDF_FORMFILLINFO* pThis, ++ FPDF_WIDESTRING URL) { ++ static const char kString[] = "secrets"; ++ static FPDF_FILEHANDLER kFakeFileHandler = { ++ nullptr, ++ [](void*) -> void {}, ++ [](void*) -> FPDF_DWORD { return sizeof(kString); }, ++ [](void*, FPDF_DWORD off, void* buffer, FPDF_DWORD size) -> FPDF_RESULT { ++ memcpy(buffer, kString, std::min(size, sizeof(kString))); ++ return 0; ++ }, ++ [](void*, FPDF_DWORD, const void*, FPDF_DWORD) -> FPDF_RESULT { ++ return -1; ++ }, ++ [](void*) -> FPDF_RESULT { return 0; }, ++ [](void*, FPDF_DWORD) -> FPDF_RESULT { return 0; }}; ++ return &kFakeFileHandler; ++} ++ ++FPDF_BOOL PostRequestURLStub(FPDF_FORMFILLINFO* pThis, ++ FPDF_WIDESTRING wsURL, ++ FPDF_WIDESTRING wsData, ++ FPDF_WIDESTRING wsContentType, ++ FPDF_WIDESTRING wsEncode, ++ FPDF_WIDESTRING wsHeader, ++ FPDF_BSTR* response) { ++ const char kString[] = "p\0o\0s\0t\0e\0d\0"; ++ FPDF_BStr_Set(response, kString, sizeof(kString) - 1); ++ return true; ++} ++ ++FPDF_BOOL PutRequestURLStub(FPDF_FORMFILLINFO* pThis, ++ FPDF_WIDESTRING wsURL, ++ FPDF_WIDESTRING wsData, ++ FPDF_WIDESTRING wsEncode) { ++ return true; ++} ++#endif // PDF_ENABLE_XFA + + } // namespace + + EmbedderTest::EmbedderTest() +- : default_delegate_(pdfium::MakeUnique()), ++ : default_delegate_(std::make_unique()), + delegate_(default_delegate_.get()) { + FPDF_FILEWRITE::version = 1; + FPDF_FILEWRITE::WriteBlock = WriteBlockCallback; +@@ -64,52 +270,31 @@ EmbedderTest::EmbedderTest() + EmbedderTest::~EmbedderTest() = default; + + void EmbedderTest::SetUp() { +- FPDF_LIBRARY_CONFIG config; +- config.version = 2; +- config.m_pUserFontPaths = nullptr; +- config.m_v8EmbedderSlot = 0; +- config.m_pIsolate = external_isolate_; +- FPDF_InitLibraryWithConfig(&config); +- + UNSUPPORT_INFO* info = static_cast(this); + memset(info, 0, sizeof(UNSUPPORT_INFO)); + info->version = 1; + info->FSDK_UnSupport_Handler = UnsupportedHandlerTrampoline; + FSDK_SetUnSpObjProcessHandler(info); +- +- saved_document_ = nullptr; + } + + void EmbedderTest::TearDown() { + // Use an EXPECT_EQ() here and continue to let TearDown() finish as cleanly as +- // possible. This can fail when an ASSERT test fails in a test case. ++ // possible. This can fail when an DCHECK test fails in a test case. + EXPECT_EQ(0U, page_map_.size()); + EXPECT_EQ(0U, saved_page_map_.size()); +- +- if (document_) { +- FORM_DoDocumentAAction(form_handle_, FPDFDOC_AACTION_WC); ++ if (document()) + CloseDocument(); +- } +- +- FPDFAvail_Destroy(avail_); +- FPDF_DestroyLibrary(); +- loader_.reset(); + } + +-#ifdef PDF_ENABLE_V8 +-void EmbedderTest::SetExternalIsolate(void* isolate) { +- external_isolate_ = static_cast(isolate); ++void EmbedderTest::CreateEmptyDocument() { ++ CreateEmptyDocumentWithoutFormFillEnvironment(); ++ form_handle_.reset(SetupFormFillEnvironment( ++ document(), JavaScriptOption::kEnableJavaScript)); + } +-#endif // PDF_ENABLE_V8 +- +-bool EmbedderTest::CreateEmptyDocument() { +- document_ = FPDF_CreateNewDocument(); +- if (!document_) +- return false; + +- form_handle_ = +- SetupFormFillEnvironment(document_, JavaScriptOption::kEnableJavaScript); +- return true; ++void EmbedderTest::CreateEmptyDocumentWithoutFormFillEnvironment() { ++ document_.reset(FPDF_CreateNewDocument()); ++ DCHECK(document_); + } + + bool EmbedderTest::OpenDocument(const std::string& filename) { +@@ -150,7 +335,7 @@ bool EmbedderTest::OpenDocumentWithOptions(const std::string& filename, + return false; + + EXPECT_TRUE(!loader_); +- loader_ = pdfium::MakeUnique( ++ loader_ = std::make_unique( + pdfium::make_span(file_contents_.get(), file_length_)); + + memset(&file_access_, 0, sizeof(file_access_)); +@@ -158,7 +343,7 @@ bool EmbedderTest::OpenDocumentWithOptions(const std::string& filename, + file_access_.m_GetBlock = TestLoader::GetBlock; + file_access_.m_Param = loader_.get(); + +- fake_file_access_ = pdfium::MakeUnique(&file_access_); ++ fake_file_access_ = std::make_unique(&file_access_); + return OpenDocumentHelper(password, linearize_option, javascript_option, + fake_file_access_.get(), &document_, &avail_, + &form_handle_); +@@ -168,42 +353,45 @@ bool EmbedderTest::OpenDocumentHelper(const char* password, + LinearizeOption linearize_option, + JavaScriptOption javascript_option, + FakeFileAccess* network_simulator, +- FPDF_DOCUMENT* document, +- FPDF_AVAIL* avail, +- FPDF_FORMHANDLE* form_handle) { ++ ScopedFPDFDocument* document, ++ ScopedFPDFAvail* avail, ++ ScopedFPDFFormHandle* form_handle) { + network_simulator->AddSegment(0, 1024); + network_simulator->SetRequestedDataAvailable(); +- *avail = FPDFAvail_Create(network_simulator->GetFileAvail(), +- network_simulator->GetFileAccess()); +- if (FPDFAvail_IsLinearized(*avail) == PDF_LINEARIZED) { ++ avail->reset(FPDFAvail_Create(network_simulator->GetFileAvail(), ++ network_simulator->GetFileAccess())); ++ FPDF_AVAIL avail_ptr = avail->get(); ++ FPDF_DOCUMENT document_ptr = nullptr; ++ if (FPDFAvail_IsLinearized(avail_ptr) == PDF_LINEARIZED) { + int32_t nRet = PDF_DATA_NOTAVAIL; + while (nRet == PDF_DATA_NOTAVAIL) { + network_simulator->SetRequestedDataAvailable(); +- nRet = +- FPDFAvail_IsDocAvail(*avail, network_simulator->GetDownloadHints()); ++ nRet = FPDFAvail_IsDocAvail(avail_ptr, ++ network_simulator->GetDownloadHints()); + } + if (nRet == PDF_DATA_ERROR) + return false; + +- *document = FPDFAvail_GetDocument(*avail, password); +- if (!*document) ++ document->reset(FPDFAvail_GetDocument(avail_ptr, password)); ++ document_ptr = document->get(); ++ if (!document_ptr) + return false; + + nRet = PDF_DATA_NOTAVAIL; + while (nRet == PDF_DATA_NOTAVAIL) { + network_simulator->SetRequestedDataAvailable(); +- nRet = +- FPDFAvail_IsFormAvail(*avail, network_simulator->GetDownloadHints()); ++ nRet = FPDFAvail_IsFormAvail(avail_ptr, ++ network_simulator->GetDownloadHints()); + } + if (nRet == PDF_FORM_ERROR) + return false; + +- int page_count = FPDF_GetPageCount(*document); ++ int page_count = FPDF_GetPageCount(document_ptr); + for (int i = 0; i < page_count; ++i) { + nRet = PDF_DATA_NOTAVAIL; + while (nRet == PDF_DATA_NOTAVAIL) { + network_simulator->SetRequestedDataAvailable(); +- nRet = FPDFAvail_IsPageAvail(*avail, i, ++ nRet = FPDFAvail_IsPageAvail(avail_ptr, i, + network_simulator->GetDownloadHints()); + } + if (nRet == PDF_DATA_ERROR) +@@ -213,27 +401,31 @@ bool EmbedderTest::OpenDocumentHelper(const char* password, + if (linearize_option == LinearizeOption::kMustLinearize) + return false; + network_simulator->SetWholeFileAvailable(); +- *document = +- FPDF_LoadCustomDocument(network_simulator->GetFileAccess(), password); +- if (!*document) ++ document->reset( ++ FPDF_LoadCustomDocument(network_simulator->GetFileAccess(), password)); ++ document_ptr = document->get(); ++ if (!document_ptr) + return false; + } +- *form_handle = SetupFormFillEnvironment(*document, javascript_option); ++ form_handle->reset(SetupFormFillEnvironment(document_ptr, javascript_option)); + +- int doc_type = FPDF_GetFormType(*document); ++ int doc_type = FPDF_GetFormType(document_ptr); + if (doc_type == FORMTYPE_XFA_FULL || doc_type == FORMTYPE_XFA_FOREGROUND) +- FPDF_LoadXFA(*document); ++ FPDF_LoadXFA(document_ptr); + +- (void)FPDF_GetDocPermissions(*document); ++ (void)FPDF_GetDocPermissions(document_ptr); + return true; + } + + void EmbedderTest::CloseDocument() { +- FPDFDOC_ExitFormFillEnvironment(form_handle_); +- form_handle_ = nullptr; +- +- FPDF_CloseDocument(document_); +- document_ = nullptr; ++ FORM_DoDocumentAAction(form_handle(), FPDFDOC_AACTION_WC); ++ form_handle_.reset(); ++ document_.reset(); ++ avail_.reset(); ++ fake_file_access_.reset(); ++ memset(&file_access_, 0, sizeof(file_access_)); ++ loader_.reset(); ++ file_contents_.reset(); + } + + FPDF_FORMHANDLE EmbedderTest::SetupFormFillEnvironment( +@@ -241,21 +433,46 @@ FPDF_FORMHANDLE EmbedderTest::SetupFormFillEnvironment( + JavaScriptOption javascript_option) { + IPDF_JSPLATFORM* platform = static_cast(this); + memset(platform, '\0', sizeof(IPDF_JSPLATFORM)); +- platform->version = 2; ++ platform->version = 3; + platform->app_alert = AlertTrampoline; +- platform->m_isolate = external_isolate_; + + FPDF_FORMFILLINFO* formfillinfo = static_cast(this); + memset(formfillinfo, 0, sizeof(FPDF_FORMFILLINFO)); +-#ifdef PDF_ENABLE_XFA +- formfillinfo->version = 2; +-#else // PDF_ENABLE_XFA +- formfillinfo->version = 1; +-#endif // PDF_ENABLE_XFA ++ formfillinfo->version = form_fill_info_version_; ++ formfillinfo->FFI_Invalidate = InvalidateStub; ++ formfillinfo->FFI_OutputSelectedRect = OutputSelectedRectStub; ++ formfillinfo->FFI_SetCursor = SetCursorStub; + formfillinfo->FFI_SetTimer = SetTimerTrampoline; + formfillinfo->FFI_KillTimer = KillTimerTrampoline; ++ formfillinfo->FFI_GetLocalTime = GetLocalTimeStub; ++ formfillinfo->FFI_OnChange = OnChangeStub; + formfillinfo->FFI_GetPage = GetPageTrampoline; ++ formfillinfo->FFI_GetCurrentPage = GetCurrentPageStub; ++ formfillinfo->FFI_GetRotation = GetRotationStub; ++ formfillinfo->FFI_ExecuteNamedAction = ExecuteNamedActionStub; ++ formfillinfo->FFI_SetTextFieldFocus = SetTextFieldFocusStub; + formfillinfo->FFI_DoURIAction = DoURIActionTrampoline; ++ formfillinfo->FFI_DoGoToAction = DoGoToActionTrampoline; ++#ifdef PDF_ENABLE_XFA ++ formfillinfo->FFI_DisplayCaret = DisplayCaretStub; ++ formfillinfo->FFI_GetCurrentPageIndex = GetCurrentPageIndexStub; ++ formfillinfo->FFI_SetCurrentPage = SetCurrentPageStub; ++ formfillinfo->FFI_GotoURL = GotoURLStub; ++ formfillinfo->FFI_GetPageViewRect = GetPageViewRectStub; ++ formfillinfo->FFI_PageEvent = PageEventStub; ++ formfillinfo->FFI_PopupMenu = PopupMenuStub; ++ formfillinfo->FFI_OpenFile = OpenFileStub; ++ formfillinfo->FFI_EmailTo = EmailToStub; ++ formfillinfo->FFI_UploadTo = UploadToStub; ++ formfillinfo->FFI_GetPlatform = GetPlatformStub; ++ formfillinfo->FFI_GetLanguage = GetLanguageStub; ++ formfillinfo->FFI_DownloadFromURL = DownloadFromURLStub; ++ formfillinfo->FFI_PostRequestURL = PostRequestURLStub; ++ formfillinfo->FFI_PutRequestURL = PutRequestURLStub; ++#endif // PDF_ENABLE_XFA ++ formfillinfo->FFI_OnFocusChange = OnFocusChangeTrampoline; ++ formfillinfo->FFI_DoURIActionWithKeyboardModifier = ++ DoURIActionWithKeyboardModifierTrampoline; + + if (javascript_option == JavaScriptOption::kEnableJavaScript) + formfillinfo->m_pJsPlatform = platform; +@@ -267,22 +484,22 @@ FPDF_FORMHANDLE EmbedderTest::SetupFormFillEnvironment( + } + + void EmbedderTest::DoOpenActions() { +- ASSERT(form_handle_); +- FORM_DoDocumentJSAction(form_handle_); +- FORM_DoDocumentOpenAction(form_handle_); ++ DCHECK(form_handle()); ++ FORM_DoDocumentJSAction(form_handle()); ++ FORM_DoDocumentOpenAction(form_handle()); + } + + int EmbedderTest::GetFirstPageNum() { +- int first_page = FPDFAvail_GetFirstPageNum(document_); +- (void)FPDFAvail_IsPageAvail(avail_, first_page, ++ int first_page = FPDFAvail_GetFirstPageNum(document()); ++ (void)FPDFAvail_IsPageAvail(avail(), first_page, + fake_file_access_->GetDownloadHints()); + return first_page; + } + + int EmbedderTest::GetPageCount() { +- int page_count = FPDF_GetPageCount(document_); ++ int page_count = FPDF_GetPageCount(document()); + for (int i = 0; i < page_count; ++i) +- (void)FPDFAvail_IsPageAvail(avail_, i, ++ (void)FPDFAvail_IsPageAvail(avail(), i, + fake_file_access_->GetDownloadHints()); + return page_count; + } +@@ -296,17 +513,17 @@ FPDF_PAGE EmbedderTest::LoadPageNoEvents(int page_number) { + } + + FPDF_PAGE EmbedderTest::LoadPageCommon(int page_number, bool do_events) { +- ASSERT(form_handle_); +- ASSERT(page_number >= 0); +- ASSERT(!pdfium::ContainsKey(page_map_, page_number)); ++ DCHECK(form_handle()); ++ DCHECK(page_number >= 0); ++ DCHECK(!pdfium::Contains(page_map_, page_number)); + +- FPDF_PAGE page = FPDF_LoadPage(document_, page_number); ++ FPDF_PAGE page = FPDF_LoadPage(document(), page_number); + if (!page) + return nullptr; + + if (do_events) { +- FORM_OnAfterLoadPage(page, form_handle_); +- FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_OPEN); ++ FORM_OnAfterLoadPage(page, form_handle()); ++ FORM_DoPageAAction(page, form_handle(), FPDFPAGE_AACTION_OPEN); + } + page_map_[page_number] = page; + return page; +@@ -321,15 +538,15 @@ void EmbedderTest::UnloadPageNoEvents(FPDF_PAGE page) { + } + + void EmbedderTest::UnloadPageCommon(FPDF_PAGE page, bool do_events) { +- ASSERT(form_handle_); ++ DCHECK(form_handle()); + int page_number = GetPageNumberForLoadedPage(page); + if (page_number < 0) { + NOTREACHED(); + return; + } + if (do_events) { +- FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_CLOSE); +- FORM_OnBeforeClosePage(page, form_handle_); ++ FORM_DoPageAAction(page, form_handle(), FPDFPAGE_AACTION_CLOSE); ++ FORM_OnBeforeClosePage(page, form_handle()); + } + FPDF_ClosePage(page); + page_map_.erase(page_number); +@@ -350,7 +567,7 @@ ScopedFPDFBitmap EmbedderTest::RenderLoadedPageWithFlags(FPDF_PAGE page, + NOTREACHED(); + return nullptr; + } +- return RenderPageWithFlags(page, form_handle_, flags); ++ return RenderPageWithFlags(page, form_handle(), flags); + } + + ScopedFPDFBitmap EmbedderTest::RenderSavedPage(FPDF_PAGE page) { +@@ -363,7 +580,7 @@ ScopedFPDFBitmap EmbedderTest::RenderSavedPageWithFlags(FPDF_PAGE page, + NOTREACHED(); + return nullptr; + } +- return RenderPageWithFlags(page, saved_form_handle_, flags); ++ return RenderPageWithFlags(page, saved_form_handle(), flags); + } + + // static +@@ -386,7 +603,7 @@ ScopedFPDFBitmap EmbedderTest::RenderPage(FPDF_PAGE page) { + return RenderPageWithFlags(page, nullptr, 0); + } + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + // static + std::vector EmbedderTest::RenderPageWithFlagsToEmf(FPDF_PAGE page, + int flags) { +@@ -406,7 +623,7 @@ std::vector EmbedderTest::RenderPageWithFlagsToEmf(FPDF_PAGE page, + FPDF_RenderPage(dc, page, 0, 0, width, height, 0, flags); + + HENHMETAFILE emf = CloseEnhMetaFile(dc); +- size_t size_in_bytes = GetEnhMetaFileBits(emf, 0, nullptr); ++ UINT size_in_bytes = GetEnhMetaFileBits(emf, 0, nullptr); + std::vector buffer(size_in_bytes); + GetEnhMetaFileBits(emf, size_in_bytes, buffer.data()); + DeleteEnhMetaFile(emf); +@@ -417,7 +634,8 @@ std::vector EmbedderTest::RenderPageWithFlagsToEmf(FPDF_PAGE page, + std::string EmbedderTest::GetPostScriptFromEmf( + pdfium::span emf_data) { + // This comes from Emf::InitFromData() in Chromium. +- HENHMETAFILE emf = SetEnhMetaFileBits(emf_data.size(), emf_data.data()); ++ HENHMETAFILE emf = SetEnhMetaFileBits( ++ pdfium::base::checked_cast(emf_data.size()), emf_data.data()); + if (!emf) + return std::string(); + +@@ -446,7 +664,7 @@ std::string EmbedderTest::GetPostScriptFromEmf( + DeleteEnhMetaFile(emf); + return ps_data; + } +-#endif // defined(OS_WIN) ++#endif // BUILDFLAG(IS_WIN) + + FPDF_DOCUMENT EmbedderTest::OpenSavedDocument() { + return OpenSavedDocumentWithPassword(nullptr); +@@ -471,51 +689,48 @@ int EmbedderTest::BytesPerPixelForFormat(int format) { + FPDF_DOCUMENT EmbedderTest::OpenSavedDocumentWithPassword( + const char* password) { + memset(&saved_file_access_, 0, sizeof(saved_file_access_)); +- saved_file_access_.m_FileLen = data_string_.size(); ++ saved_file_access_.m_FileLen = ++ pdfium::base::checked_cast(data_string_.size()); + saved_file_access_.m_GetBlock = GetBlockFromString; + // Copy data to prevent clearing it before saved document close. + saved_document_file_data_ = data_string_; + saved_file_access_.m_Param = &saved_document_file_data_; + + saved_fake_file_access_ = +- pdfium::MakeUnique(&saved_file_access_); ++ std::make_unique(&saved_file_access_); + + EXPECT_TRUE(OpenDocumentHelper( + password, LinearizeOption::kDefaultLinearize, + JavaScriptOption::kEnableJavaScript, saved_fake_file_access_.get(), + &saved_document_, &saved_avail_, &saved_form_handle_)); +- return saved_document_; ++ return saved_document(); + } + + void EmbedderTest::CloseSavedDocument() { +- ASSERT(saved_document_); ++ DCHECK(saved_document()); + +- FPDFDOC_ExitFormFillEnvironment(saved_form_handle_); +- FPDF_CloseDocument(saved_document_); +- FPDFAvail_Destroy(saved_avail_); +- +- saved_form_handle_ = nullptr; +- saved_document_ = nullptr; +- saved_avail_ = nullptr; ++ saved_form_handle_.reset(); ++ saved_document_.reset(); ++ saved_avail_.reset(); + } + + FPDF_PAGE EmbedderTest::LoadSavedPage(int page_number) { +- ASSERT(saved_form_handle_); +- ASSERT(page_number >= 0); +- ASSERT(!pdfium::ContainsKey(saved_page_map_, page_number)); ++ DCHECK(saved_form_handle()); ++ DCHECK(page_number >= 0); ++ DCHECK(!pdfium::Contains(saved_page_map_, page_number)); + +- FPDF_PAGE page = FPDF_LoadPage(saved_document_, page_number); ++ FPDF_PAGE page = FPDF_LoadPage(saved_document(), page_number); + if (!page) + return nullptr; + +- FORM_OnAfterLoadPage(page, saved_form_handle_); +- FORM_DoPageAAction(page, saved_form_handle_, FPDFPAGE_AACTION_OPEN); ++ FORM_OnAfterLoadPage(page, saved_form_handle()); ++ FORM_DoPageAAction(page, saved_form_handle(), FPDFPAGE_AACTION_OPEN); + saved_page_map_[page_number] = page; + return page; + } + + void EmbedderTest::CloseSavedPage(FPDF_PAGE page) { +- ASSERT(saved_form_handle_); ++ DCHECK(saved_form_handle()); + + int page_number = GetPageNumberForSavedPage(page); + if (page_number < 0) { +@@ -523,8 +738,8 @@ void EmbedderTest::CloseSavedPage(FPDF_PAGE page) { + return; + } + +- FORM_DoPageAAction(page, saved_form_handle_, FPDFPAGE_AACTION_CLOSE); +- FORM_OnBeforeClosePage(page, saved_form_handle_); ++ FORM_DoPageAAction(page, saved_form_handle(), FPDFPAGE_AACTION_CLOSE); ++ FORM_OnBeforeClosePage(page, saved_form_handle()); + FPDF_ClosePage(page); + + saved_page_map_.erase(page_number); +@@ -534,8 +749,8 @@ void EmbedderTest::VerifySavedRendering(FPDF_PAGE page, + int width, + int height, + const char* md5) { +- ASSERT(saved_document_); +- ASSERT(page); ++ DCHECK(saved_document()); ++ DCHECK(page); + + ScopedFPDFBitmap bitmap = RenderSavedPageWithFlags(page, FPDF_ANNOT); + CompareBitmap(bitmap.get(), width, height, md5); +@@ -550,62 +765,25 @@ void EmbedderTest::VerifySavedDocument(int width, int height, const char* md5) { + } + + void EmbedderTest::SetWholeFileAvailable() { +- ASSERT(fake_file_access_); ++ DCHECK(fake_file_access_); + fake_file_access_->SetWholeFileAvailable(); + } + +-FPDF_PAGE EmbedderTest::Delegate::GetPage(FPDF_FORMFILLINFO* info, +- FPDF_DOCUMENT document, +- int page_index) { +- EmbedderTest* test = static_cast(info); +- auto it = test->page_map_.find(page_index); +- return it != test->page_map_.end() ? it->second : nullptr; +-} +- +-// static +-void EmbedderTest::UnsupportedHandlerTrampoline(UNSUPPORT_INFO* info, +- int type) { +- EmbedderTest* test = static_cast(info); +- test->delegate_->UnsupportedHandler(type); +-} +- +-// static +-int EmbedderTest::AlertTrampoline(IPDF_JSPLATFORM* platform, +- FPDF_WIDESTRING message, +- FPDF_WIDESTRING title, +- int type, +- int icon) { +- EmbedderTest* test = static_cast(platform); +- return test->delegate_->Alert(message, title, type, icon); ++void EmbedderTest::SetDocumentFromAvail() { ++ document_.reset(FPDFAvail_GetDocument(avail(), nullptr)); + } + +-// static +-int EmbedderTest::SetTimerTrampoline(FPDF_FORMFILLINFO* info, +- int msecs, +- TimerCallback fn) { +- EmbedderTest* test = static_cast(info); +- return test->delegate_->SetTimer(msecs, fn); +-} +- +-// static +-void EmbedderTest::KillTimerTrampoline(FPDF_FORMFILLINFO* info, int id) { +- EmbedderTest* test = static_cast(info); +- return test->delegate_->KillTimer(id); ++void EmbedderTest::CreateAvail(FX_FILEAVAIL* file_avail, ++ FPDF_FILEACCESS* file) { ++ avail_.reset(FPDFAvail_Create(file_avail, file)); + } + +-// static +-FPDF_PAGE EmbedderTest::GetPageTrampoline(FPDF_FORMFILLINFO* info, ++FPDF_PAGE EmbedderTest::Delegate::GetPage(FPDF_FORMFILLINFO* info, + FPDF_DOCUMENT document, + int page_index) { +- return static_cast(info)->delegate_->GetPage(info, document, +- page_index); +-} +- +-// static +-void EmbedderTest::DoURIActionTrampoline(FPDF_FORMFILLINFO* info, +- FPDF_BYTESTRING uri) { + EmbedderTest* test = static_cast(info); +- return test->delegate_->DoURIAction(uri); ++ auto it = test->page_map_.find(page_index); ++ return it != test->page_map_.end() ? it->second : nullptr; + } + + // static +@@ -625,13 +803,11 @@ std::string EmbedderTest::HashBitmap(FPDF_BITMAP bitmap) { + return CryptToBase16(digest); + } + +-#ifndef NDEBUG + // static + void EmbedderTest::WriteBitmapToPng(FPDF_BITMAP bitmap, + const std::string& filename) { + BitmapSaver::WriteBitmapToPng(bitmap, filename); + } +-#endif + + // static + void EmbedderTest::CompareBitmap(FPDF_BITMAP bitmap, +@@ -650,7 +826,11 @@ void EmbedderTest::CompareBitmap(FPDF_BITMAP bitmap, + if (!expected_md5sum) + return; + +- EXPECT_EQ(expected_md5sum, HashBitmap(bitmap)); ++ std::string actual_md5sum = HashBitmap(bitmap); ++ EXPECT_EQ(expected_md5sum, actual_md5sum); ++ if (EmbedderTestEnvironment::GetInstance()->write_pngs()) { ++ WriteBitmapToPng(bitmap, actual_md5sum + ".png"); ++ } + } + + // static +@@ -693,7 +873,7 @@ int EmbedderTest::GetPageNumberForPage(const PageNumberToHandleMap& page_map, + for (const auto& it : page_map) { + if (it.second == page) { + int page_number = it.first; +- ASSERT(page_number >= 0); ++ DCHECK(page_number >= 0); + return page_number; + } + } +diff --git a/testing/embedder_test.h b/testing/embedder_test.h +index 662333cd7..c73dd6190 100644 +--- a/testing/embedder_test.h ++++ b/testing/embedder_test.h +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -25,6 +25,10 @@ + + class TestLoader; + ++// The loading time of the CFGAS_FontMgr is linear in the number of times it is ++// loaded. So, if a test suite has a lot of tests that need a font manager they ++// can end up executing very, very slowly. ++ + // This class is used to load a PDF document, and then run programatic + // API tests against it. + class EmbedderTest : public ::testing::Test, +@@ -64,6 +68,23 @@ class EmbedderTest : public ::testing::Test, + + // Equivalent to FPDF_FORMFILLINFO::FFI_DoURIAction(). + virtual void DoURIAction(FPDF_BYTESTRING uri) {} ++ ++ // Equivalent to FPDF_FORMFILLINFO::FFI_DoGoToAction(). ++ virtual void DoGoToAction(FPDF_FORMFILLINFO* info, ++ int page_index, ++ int zoom_mode, ++ float* pos_arry, ++ int array_size) {} ++ ++ // Equivalent to FPDF_FORMFILLINFO::FFI_OnFocusChange(). ++ virtual void OnFocusChange(FPDF_FORMFILLINFO* info, ++ FPDF_ANNOTATION annot, ++ int page_index) {} ++ ++ // Equivalent to FPDF_FORMFILLINFO::FFI_DoURIActionWithKeyboardModifier(). ++ virtual void DoURIActionWithKeyboardModifier(FPDF_FORMFILLINFO* info, ++ FPDF_BYTESTRING uri, ++ int modifiers) {} + }; + + EmbedderTest(); +@@ -72,21 +93,30 @@ class EmbedderTest : public ::testing::Test, + void SetUp() override; + void TearDown() override; + +-#ifdef PDF_ENABLE_V8 +- // Call before SetUp to pass shared isolate, otherwise PDFium creates one. +- void SetExternalIsolate(void* isolate); +-#endif // PDF_ENABLE_V8 +- ++ Delegate* GetDelegate() { return delegate_; } + void SetDelegate(Delegate* delegate) { + delegate_ = delegate ? delegate : default_delegate_.get(); + } + +- FPDF_DOCUMENT document() const { return document_; } +- FPDF_FORMHANDLE form_handle() const { return form_handle_; } ++ void SetFormFillInfoVersion(int form_fill_info_version) { ++ form_fill_info_version_ = form_fill_info_version; ++ } ++ ++ void SetDocumentFromAvail(); ++ FPDF_DOCUMENT document() const { return document_.get(); } ++ FPDF_DOCUMENT saved_document() const { return saved_document_.get(); } ++ FPDF_FORMHANDLE form_handle() const { return form_handle_.get(); } ++ FPDF_FORMHANDLE saved_form_handle() const { return saved_form_handle_.get(); } + +- // Create an empty document, and its form fill environment. Returns true +- // on success or false on failure. +- bool CreateEmptyDocument(); ++ // Wrapper for FPDFAvail_Create() to set `avail_`. ++ void CreateAvail(FX_FILEAVAIL* file_avail, FPDF_FILEACCESS* file); ++ FPDF_AVAIL avail() { return avail_.get(); } ++ ++ // Create an empty document, and its form fill environment. ++ void CreateEmptyDocument(); ++ ++ // Create an empty document without a form fill environment. ++ void CreateEmptyDocumentWithoutFormFillEnvironment(); + + // Open the document specified by |filename|, and create its form fill + // environment, or return false on failure. The |filename| is relative to +@@ -171,14 +201,14 @@ class EmbedderTest : public ::testing::Test, + // Simplified form of RenderPageWithFlags() with no handle and no flags. + static ScopedFPDFBitmap RenderPage(FPDF_PAGE page); + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + // Convert |page| into EMF with the specified page rendering |flags|. + static std::vector RenderPageWithFlagsToEmf(FPDF_PAGE page, + int flags); + + // Get the PostScript data from |emf_data|. + static std::string GetPostScriptFromEmf(pdfium::span emf_data); +-#endif // defined(OS_WIN) ++#endif // BUILDFLAG(IS_WIN) + + // Return bytes for each of the FPDFBitmap_* format types. + static int BytesPerPixelForFormat(int format); +@@ -190,9 +220,9 @@ class EmbedderTest : public ::testing::Test, + LinearizeOption linearize_option, + JavaScriptOption javascript_option, + FakeFileAccess* network_simulator, +- FPDF_DOCUMENT* document, +- FPDF_AVAIL* avail, +- FPDF_FORMHANDLE* form_handle); ++ ScopedFPDFDocument* document, ++ ScopedFPDFAvail* avail, ++ ScopedFPDFFormHandle* form_handle); + + FPDF_FORMHANDLE SetupFormFillEnvironment(FPDF_DOCUMENT doc, + JavaScriptOption javascript_option); +@@ -201,11 +231,9 @@ class EmbedderTest : public ::testing::Test, + // any, at the end of a row where the stride is larger than width * bpp. + static std::string HashBitmap(FPDF_BITMAP bitmap); + +-#ifndef NDEBUG + // For debugging purposes. + // Write |bitmap| as a PNG to |filename|. + static void WriteBitmapToPng(FPDF_BITMAP bitmap, const std::string& filename); +-#endif + + // Check |bitmap| to make sure it has the right dimensions and content. + static void CompareBitmap(FPDF_BITMAP bitmap, +@@ -245,45 +273,7 @@ class EmbedderTest : public ::testing::Test, + void ClosePDFFileForWrite(); + #endif + +- std::unique_ptr default_delegate_; +- Delegate* delegate_; +- +- FPDF_DOCUMENT document_ = nullptr; +- FPDF_FORMHANDLE form_handle_ = nullptr; +- FPDF_AVAIL avail_ = nullptr; +- FPDF_FILEACCESS file_access_; // must outlive |avail_|. +- std::unique_ptr fake_file_access_; // must outlive |avail_|. +- +- void* external_isolate_ = nullptr; +- std::unique_ptr loader_; +- size_t file_length_ = 0; +- std::unique_ptr file_contents_; +- PageNumberToHandleMap page_map_; +- +- FPDF_DOCUMENT saved_document_ = nullptr; +- FPDF_FORMHANDLE saved_form_handle_ = nullptr; +- FPDF_AVAIL saved_avail_ = nullptr; +- FPDF_FILEACCESS saved_file_access_; // must outlive |saved_avail_|. +- // must outlive |saved_avail_|. +- std::unique_ptr saved_fake_file_access_; +- PageNumberToHandleMap saved_page_map_; +- + private: +- static void UnsupportedHandlerTrampoline(UNSUPPORT_INFO*, int type); +- static int AlertTrampoline(IPDF_JSPLATFORM* plaform, +- FPDF_WIDESTRING message, +- FPDF_WIDESTRING title, +- int type, +- int icon); +- static int SetTimerTrampoline(FPDF_FORMFILLINFO* info, +- int msecs, +- TimerCallback fn); +- static void KillTimerTrampoline(FPDF_FORMFILLINFO* info, int id); +- static FPDF_PAGE GetPageTrampoline(FPDF_FORMFILLINFO* info, +- FPDF_DOCUMENT document, +- int page_index); +- static void DoURIActionTrampoline(FPDF_FORMFILLINFO* info, +- FPDF_BYTESTRING uri); + static int WriteBlockCallback(FPDF_FILEWRITE* pFileWrite, + const void* data, + unsigned long size); +@@ -301,6 +291,34 @@ class EmbedderTest : public ::testing::Test, + void UnloadPageCommon(FPDF_PAGE page, bool do_events); + FPDF_PAGE LoadPageCommon(int page_number, bool do_events); + ++ std::unique_ptr default_delegate_; ++ Delegate* delegate_; ++ ++#ifdef PDF_ENABLE_XFA ++ int form_fill_info_version_ = 2; ++#else // PDF_ENABLE_XFA ++ int form_fill_info_version_ = 1; ++#endif // PDF_ENABLE_XFA ++ ++ size_t file_length_ = 0; ++ // must outlive `loader_`. ++ std::unique_ptr file_contents_; ++ std::unique_ptr loader_; ++ FPDF_FILEACCESS file_access_; // must outlive `avail_`. ++ std::unique_ptr fake_file_access_; // must outlive `avail_`. ++ ScopedFPDFAvail avail_; ++ ScopedFPDFDocument document_; ++ ScopedFPDFFormHandle form_handle_; ++ PageNumberToHandleMap page_map_; ++ ++ FPDF_FILEACCESS saved_file_access_; // must outlive `saved_avail_`. ++ // must outlive `saved_avail_`. ++ std::unique_ptr saved_fake_file_access_; ++ ScopedFPDFAvail saved_avail_; ++ ScopedFPDFDocument saved_document_; ++ ScopedFPDFFormHandle saved_form_handle_; ++ PageNumberToHandleMap saved_page_map_; ++ + std::string data_string_; + std::string saved_document_file_data_; + std::ofstream filestream_; +diff --git a/testing/embedder_test_constants.cpp b/testing/embedder_test_constants.cpp +new file mode 100644 +index 000000000..9e8caee9f +--- /dev/null ++++ b/testing/embedder_test_constants.cpp +@@ -0,0 +1,68 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "testing/embedder_test_constants.h" ++ ++#include "build/build_config.h" ++#include "core/fxge/cfx_defaultrenderdevice.h" ++ ++namespace pdfium { ++ ++const char* AnnotationStampWithApChecksum() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "a31381406d0b95049e418720750b78dd"; ++#if BUILDFLAG(IS_APPLE) ++ return "0521eaa52fe2aa43aafd3e4495f63f0b"; ++#else ++ return "5f19ddad9d48f5b7b87ee7d92f577db6"; ++#endif ++} ++ ++const char kBlankPage612By792Checksum[] = "1940568c9ba33bac5d0b1ee9558c76b3"; ++ ++const char* Bug890322Checksum() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "793689536cf64fe792c2f241888c0cf3"; ++ return "6c674642154408e877d88c6c082d67e9"; ++} ++ ++const char* HelloWorldChecksum() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "6eef7237f7591f07616e238422086737"; ++#endif ++ return "c1c548442e0e0f949c5550d89bf8ae3b"; ++} ++ ++const char* HelloWorldRemovedChecksum() { ++#if BUILDFLAG(IS_APPLE) ++ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "6e1cae48a2e35c521dee4ca502f48af6"; ++#endif ++ return "4a9b80f675f7f3bf2da1b02f12449e4b"; ++} ++ ++const char* ManyRectanglesChecksum() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "4e7e280c1597222afcb0ee3bb90ec119"; ++ return "b0170c575b65ecb93ebafada0ff0f038"; ++} ++ ++const char* RectanglesChecksum() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "b4e411a6b5ffa59a50efede2efece597"; ++ return "0a90de37f52127619c3dfb642b5fa2fe"; ++} ++ ++const char* TextFormChecksum() { ++ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) ++ return "e6d2eb75f18d773f0dad938b1bb22e23"; ++#if BUILDFLAG(IS_APPLE) ++ return "fa2bf756942a950101fc147fc4ef3f82"; ++#else ++ return "6f86fe1dbed5965d91aec6e0b829e29f"; ++#endif ++} ++ ++} // namespace pdfium +diff --git a/testing/embedder_test_constants.h b/testing/embedder_test_constants.h +new file mode 100644 +index 000000000..306880b8d +--- /dev/null ++++ b/testing/embedder_test_constants.h +@@ -0,0 +1,36 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef TESTING_EMBEDDER_TEST_CONSTANTS_H_ ++#define TESTING_EMBEDDER_TEST_CONSTANTS_H_ ++ ++namespace pdfium { ++ ++// MD5 hash for rendering annotation_stamp_with_ap.pdf with annotations. ++const char* AnnotationStampWithApChecksum(); ++ ++// MD5 hash for rendering a 612x792 blank page. ++extern const char kBlankPage612By792Checksum[]; ++ ++// MD5 hash for rendering bug_890322.pdf. ++const char* Bug890322Checksum(); ++ ++// MD5 hash for rendering hello_world.pdf or bug_455199.pdf. ++const char* HelloWorldChecksum(); ++ ++// MD5 hash for rendering hello_world.pdf after removing "Goodbye, world!". ++const char* HelloWorldRemovedChecksum(); ++ ++// MD5 hash for rendering many_rectangles.pdf. ++const char* ManyRectanglesChecksum(); ++ ++// MD5 hash for rendering rectangles.pdf. ++const char* RectanglesChecksum(); ++ ++// MD5 hash for rendering text_form.pdf. ++const char* TextFormChecksum(); ++ ++} // namespace pdfium ++ ++#endif // TESTING_EMBEDDER_TEST_CONSTANTS_H_ +diff --git a/testing/embedder_test_environment.cpp b/testing/embedder_test_environment.cpp +new file mode 100644 +index 000000000..07e459a56 +--- /dev/null ++++ b/testing/embedder_test_environment.cpp +@@ -0,0 +1,93 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "testing/embedder_test_environment.h" ++ ++#include ++ ++#include "core/fxcrt/fx_system.h" ++#include "public/fpdfview.h" ++#include "testing/command_line_helpers.h" ++#include "third_party/base/check.h" ++ ++#ifdef PDF_ENABLE_V8 ++#include "testing/v8_test_environment.h" ++#endif // PDF_ENABLE_V8 ++ ++namespace { ++ ++EmbedderTestEnvironment* g_environment = nullptr; ++ ++} // namespace ++ ++EmbedderTestEnvironment::EmbedderTestEnvironment() ++ : renderer_type_(GetDefaultRendererType()) { ++ DCHECK(!g_environment); ++ g_environment = this; ++} ++ ++EmbedderTestEnvironment::~EmbedderTestEnvironment() { ++ DCHECK(g_environment); ++ g_environment = nullptr; ++} ++ ++// static ++EmbedderTestEnvironment* EmbedderTestEnvironment::GetInstance() { ++ return g_environment; ++} ++ ++void EmbedderTestEnvironment::SetUp() { ++ FPDF_LIBRARY_CONFIG config; ++ config.version = 4; ++ config.m_pUserFontPaths = nullptr; ++ config.m_v8EmbedderSlot = 0; ++ config.m_pPlatform = nullptr; ++ ++ config.m_pUserFontPaths = test_fonts_.font_paths(); ++ ++#ifdef PDF_ENABLE_V8 ++ config.m_pIsolate = V8TestEnvironment::GetInstance()->isolate(); ++ config.m_pPlatform = V8TestEnvironment::GetInstance()->platform(); ++#else // PDF_ENABLE_V8 ++ config.m_pIsolate = nullptr; ++ config.m_pPlatform = nullptr; ++#endif // PDF_ENABLE_V8 ++ config.m_RendererType = renderer_type_; ++ ++ FPDF_InitLibraryWithConfig(&config); ++ ++ test_fonts_.InstallFontMapper(); ++} ++ ++void EmbedderTestEnvironment::TearDown() { ++ FPDF_DestroyLibrary(); ++} ++ ++void EmbedderTestEnvironment::AddFlags(int argc, char** argv) { ++ for (int i = 1; i < argc; ++i) ++ AddFlag(argv[i]); ++} ++ ++void EmbedderTestEnvironment::AddFlag(const std::string& flag) { ++ if (flag == "--write-pngs") { ++ write_pngs_ = true; ++ return; ++ } ++#if defined(_SKIA_SUPPORT_) ++ std::string value; ++ if (ParseSwitchKeyValue(flag, "--use-renderer=", &value)) { ++ if (value == "agg") { ++ renderer_type_ = FPDF_RENDERERTYPE_AGG; ++ } else if (value == "skia") { ++ renderer_type_ = FPDF_RENDERERTYPE_SKIA; ++ } else { ++ std::cerr << "Invalid --use-renderer argument, value must be one of agg " ++ "or skia\n"; ++ } ++ return; ++ } ++#endif // defined(_SKIA_SUPPORT_) ++ ++ std::cerr << "Unknown flag: " << flag << "\n"; ++} +diff --git a/testing/embedder_test_environment.h b/testing/embedder_test_environment.h +new file mode 100644 +index 000000000..e04c5ae4b +--- /dev/null ++++ b/testing/embedder_test_environment.h +@@ -0,0 +1,39 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef TESTING_EMBEDDER_TEST_ENVIRONMENT_H_ ++#define TESTING_EMBEDDER_TEST_ENVIRONMENT_H_ ++ ++#include ++ ++#include "public/fpdfview.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "testing/test_fonts.h" ++ ++class EmbedderTestEnvironment : public testing::Environment { ++ public: ++ EmbedderTestEnvironment(); ++ ~EmbedderTestEnvironment() override; ++ ++ // Note: GetInstance() does not create one if it does not exist, ++ // so the main program must explicitly add this enviroment. ++ static EmbedderTestEnvironment* GetInstance(); ++ ++ // testing::Environment: ++ void SetUp() override; ++ void TearDown() override; ++ ++ void AddFlags(int argc, char** argv); ++ ++ bool write_pngs() const { return write_pngs_; } ++ ++ private: ++ void AddFlag(const std::string& flag); ++ ++ FPDF_RENDERER_TYPE renderer_type_; ++ bool write_pngs_ = false; ++ TestFonts test_fonts_; ++}; ++ ++#endif // TESTING_EMBEDDER_TEST_ENVIRONMENT_H_ +diff --git a/testing/embedder_test_main.cpp b/testing/embedder_test_main.cpp +index 34dfb0639..53049b694 100644 +--- a/testing/embedder_test_main.cpp ++++ b/testing/embedder_test_main.cpp +@@ -1,90 +1,36 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include +-#include +- ++#include "build/build_config.h" + #include "core/fxcrt/fx_memory.h" ++#include "testing/embedder_test_environment.h" + #include "testing/gmock/include/gmock/gmock.h" + #include "testing/gtest/include/gtest/gtest.h" + + #ifdef PDF_ENABLE_V8 +-#include "testing/v8_initializer.h" +-#include "v8/include/v8-platform.h" +-#include "v8/include/v8.h" +-#endif // PDF_ENABLE_v8 +- +-namespace { +- +-const char* g_exe_path = nullptr; +- +-#ifdef PDF_ENABLE_V8 +-#ifdef V8_USE_EXTERNAL_STARTUP_DATA +-v8::StartupData* g_v8_snapshot = nullptr; +-#endif // V8_USE_EXTERNAL_STARTUP_DATA ++#include "testing/v8_test_environment.h" + #endif // PDF_ENABLE_V8 + +-// The loading time of the CFGAS_FontMgr is linear in the number of times it is +-// loaded. So, if a test suite has a lot of tests that need a font manager they +-// can end up executing very, very slowly. +-class Environment final : public testing::Environment { +- public: +- void SetUp() override { +-#ifdef PDF_ENABLE_V8 +-#ifdef V8_USE_EXTERNAL_STARTUP_DATA +- if (g_v8_snapshot) { +- platform_ = InitializeV8ForPDFiumWithStartupData(g_exe_path, +- std::string(), nullptr); +- } else { +- g_v8_snapshot = new v8::StartupData; +- platform_ = InitializeV8ForPDFiumWithStartupData( +- g_exe_path, std::string(), g_v8_snapshot); +- } +-#else +- platform_ = InitializeV8ForPDFium(g_exe_path); +-#endif // V8_USE_EXTERNAL_STARTUP_DATA +-#endif // FPDF_ENABLE_V8 +- } +- +- void TearDown() override { +-#ifdef PDF_ENABLE_V8 +- v8::V8::ShutdownPlatform(); +-#endif // PDF_ENABLE_V8 +- } ++// Can't use gtest-provided main since we need to create our own ++// testing environment which needs the executable path in order to ++// find the external V8 binary data files. ++int main(int argc, char** argv) { ++ FX_InitializeMemoryAllocators(); + +- private: + #ifdef PDF_ENABLE_V8 +- std::unique_ptr platform_; ++ // The env will be deleted by gtest. ++ AddGlobalTestEnvironment(new V8TestEnvironment(argv[0])); + #endif // PDF_ENABLE_V8 +-}; +- +-Environment* env_ = nullptr; +- +-} // namespace +- +-// Can't use gtest-provided main since we need to stash the path to the +-// executable in order to find the external V8 binary data files. +-int main(int argc, char** argv) { +- g_exe_path = argv[0]; + +- FXMEM_InitializePartitionAlloc(); +- +- env_ = new Environment(); + // The env will be deleted by gtest. +- AddGlobalTestEnvironment(env_); ++ AddGlobalTestEnvironment(new EmbedderTestEnvironment); + + testing::InitGoogleTest(&argc, argv); + testing::InitGoogleMock(&argc, argv); + +- int ret_val = RUN_ALL_TESTS(); +- +-#ifdef PDF_ENABLE_V8 +-#ifdef V8_USE_EXTERNAL_STARTUP_DATA +- if (g_v8_snapshot) +- free(const_cast(g_v8_snapshot->data)); +-#endif // V8_USE_EXTERNAL_STARTUP_DATA +-#endif // PDF_ENABLE_V8 ++ // Anything remaining in argc/argv is an embedder_tests flag. ++ EmbedderTestEnvironment::GetInstance()->AddFlags(argc, argv); + +- return ret_val; ++ return RUN_ALL_TESTS(); + } +diff --git a/testing/embedder_test_mock_delegate.h b/testing/embedder_test_mock_delegate.h +index c3f282073..7309541a9 100644 +--- a/testing/embedder_test_mock_delegate.h ++++ b/testing/embedder_test_mock_delegate.h +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,7 +8,7 @@ + #include "testing/embedder_test.h" + #include "testing/gmock/include/gmock/gmock.h" + +-class EmbedderTestMockDelegate final : public EmbedderTest::Delegate { ++class EmbedderTestMockDelegate : public EmbedderTest::Delegate { + public: + MOCK_METHOD1(UnsupportedHandler, void(int type)); + MOCK_METHOD4( +@@ -16,6 +16,21 @@ class EmbedderTestMockDelegate final : public EmbedderTest::Delegate { + int(FPDF_WIDESTRING message, FPDF_WIDESTRING title, int type, int icon)); + MOCK_METHOD2(SetTimer, int(int msecs, TimerCallback fn)); + MOCK_METHOD1(KillTimer, void(int msecs)); ++ MOCK_METHOD1(DoURIAction, void(FPDF_BYTESTRING uri)); ++ MOCK_METHOD5(DoGoToAction, ++ void(FPDF_FORMFILLINFO* info, ++ int page_index, ++ int zoom_mode, ++ float* pos_array, ++ int array_size)); ++ MOCK_METHOD3(OnFocusChange, ++ void(FPDF_FORMFILLINFO* info, ++ FPDF_ANNOTATION annot, ++ int page_index)); ++ MOCK_METHOD3(DoURIActionWithKeyboardModifier, ++ void(FPDF_FORMFILLINFO* info, ++ FPDF_BYTESTRING uri, ++ int modifiers)); + }; + + #endif // TESTING_EMBEDDER_TEST_MOCK_DELEGATE_H_ +diff --git a/testing/embedder_test_timer_handling_delegate.h b/testing/embedder_test_timer_handling_delegate.h +index a32ad2024..e51125431 100644 +--- a/testing/embedder_test_timer_handling_delegate.h ++++ b/testing/embedder_test_timer_handling_delegate.h +@@ -1,4 +1,4 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -57,7 +57,7 @@ class EmbedderTestTimerHandlingDelegate final : public EmbedderTest::Delegate { + + void AdvanceTime(int increment_msecs) { + fake_elapsed_msecs_ += increment_msecs; +- while (1) { ++ while (true) { + auto iter = expiry_to_timer_map_.begin(); + if (iter == expiry_to_timer_map_.end()) { + break; +diff --git a/testing/external_engine_embedder_test.cpp b/testing/external_engine_embedder_test.cpp +new file mode 100644 +index 000000000..031981df8 +--- /dev/null ++++ b/testing/external_engine_embedder_test.cpp +@@ -0,0 +1,37 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "testing/external_engine_embedder_test.h" ++ ++#include ++ ++#include "fxjs/cfxjs_engine.h" ++#include "testing/v8_test_environment.h" ++#include "v8/include/v8-context.h" ++#include "v8/include/v8-isolate.h" ++#include "v8/include/v8-local-handle.h" ++ ++ExternalEngineEmbedderTest::ExternalEngineEmbedderTest() = default; ++ ++ExternalEngineEmbedderTest::~ExternalEngineEmbedderTest() = default; ++ ++void ExternalEngineEmbedderTest::SetUp() { ++ EmbedderTest::SetUp(); ++ ++ v8::Isolate::Scope isolate_scope(isolate()); ++ v8::HandleScope handle_scope(isolate()); ++ FXJS_PerIsolateData::SetUp(isolate()); ++ m_Engine = std::make_unique(isolate()); ++ m_Engine->InitializeEngine(); ++} ++ ++void ExternalEngineEmbedderTest::TearDown() { ++ m_Engine->ReleaseEngine(); ++ m_Engine.reset(); ++ JSEmbedderTest::TearDown(); ++} ++ ++v8::Local ExternalEngineEmbedderTest::GetV8Context() { ++ return m_Engine->GetV8Context(); ++} +diff --git a/testing/external_engine_embedder_test.h b/testing/external_engine_embedder_test.h +new file mode 100644 +index 000000000..258e12439 +--- /dev/null ++++ b/testing/external_engine_embedder_test.h +@@ -0,0 +1,35 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef TESTING_EXTERNAL_ENGINE_EMBEDDER_TEST_H_ ++#define TESTING_EXTERNAL_ENGINE_EMBEDDER_TEST_H_ ++ ++#include ++ ++#include "testing/js_embedder_test.h" ++#include "v8/include/v8-context.h" ++#include "v8/include/v8-local-handle.h" ++ ++class CFXJS_Engine; ++ ++// Test class that allows creating a FXJS javascript engine without ++// first having to load a document and instantiate a form filler ++// against it. Generally, most tests will want to do the latter. ++class ExternalEngineEmbedderTest : public JSEmbedderTest { ++ public: ++ ExternalEngineEmbedderTest(); ++ ~ExternalEngineEmbedderTest() override; ++ ++ // EmbedderTest: ++ void SetUp() override; ++ void TearDown() override; ++ ++ CFXJS_Engine* engine() const { return m_Engine.get(); } ++ v8::Local GetV8Context(); ++ ++ private: ++ std::unique_ptr m_Engine; ++}; ++ ++#endif // TESTING_EXTERNAL_ENGINE_EMBEDDER_TEST_H_ +diff --git a/testing/fake_file_access.cpp b/testing/fake_file_access.cpp +index 723f77212..4bd8fa164 100644 +--- a/testing/fake_file_access.cpp ++++ b/testing/fake_file_access.cpp +@@ -1,16 +1,13 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "testing/fake_file_access.h" + +-#include +-#include + #include +-#include + + #include "core/fxcrt/fx_system.h" +-#include "third_party/base/ptr_util.h" ++#include "third_party/base/check.h" + + namespace { + +@@ -76,13 +73,13 @@ class DownloadHintsImpl final : public FX_DOWNLOADHINTS { + + FakeFileAccess::FakeFileAccess(FPDF_FILEACCESS* file_access) + : file_access_(file_access), +- file_access_wrapper_(pdfium::MakeUnique(this)), +- file_avail_(pdfium::MakeUnique(this)), +- download_hints_(pdfium::MakeUnique(this)) { +- ASSERT(file_access_); ++ file_access_wrapper_(std::make_unique(this)), ++ file_avail_(std::make_unique(this)), ++ download_hints_(std::make_unique(this)) { ++ DCHECK(file_access_); + } + +-FakeFileAccess::~FakeFileAccess() {} ++FakeFileAccess::~FakeFileAccess() = default; + + FPDF_BOOL FakeFileAccess::IsDataAvail(size_t offset, size_t size) const { + return available_data_.Contains(RangeSet::Range(offset, offset + size)); +diff --git a/testing/fake_file_access.h b/testing/fake_file_access.h +index c8c08e339..6a0f3cf07 100644 +--- a/testing/fake_file_access.h ++++ b/testing/fake_file_access.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/font_renamer.cpp b/testing/font_renamer.cpp +new file mode 100644 +index 000000000..6a3a81c30 +--- /dev/null ++++ b/testing/font_renamer.cpp +@@ -0,0 +1,91 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "testing/font_renamer.h" ++ ++#include ++ ++#include "testing/test_fonts.h" ++ ++namespace { ++ ++FPDF_SYSFONTINFO* GetImpl(FPDF_SYSFONTINFO* info) { ++ return static_cast(info)->impl(); ++} ++ ++void ReleaseImpl(FPDF_SYSFONTINFO* info) { ++ FPDF_SYSFONTINFO* impl = GetImpl(info); ++ impl->Release(impl); ++} ++ ++void EnumFontsImpl(FPDF_SYSFONTINFO* info, void* mapper) { ++ FPDF_SYSFONTINFO* impl = GetImpl(info); ++ impl->EnumFonts(impl, mapper); ++} ++ ++void* MapFontImpl(FPDF_SYSFONTINFO* info, ++ int weight, ++ FPDF_BOOL italic, ++ int charset, ++ int pitch_family, ++ const char* face, ++ FPDF_BOOL* exact) { ++ std::string renamed_face = TestFonts::RenameFont(face); ++ FPDF_SYSFONTINFO* impl = GetImpl(info); ++ return impl->MapFont(impl, weight, italic, charset, pitch_family, ++ renamed_face.c_str(), exact); ++} ++ ++void* GetFontImpl(FPDF_SYSFONTINFO* info, const char* face) { ++ // Any non-null return will do. ++ FPDF_SYSFONTINFO* impl = GetImpl(info); ++ std::string renamed_face = TestFonts::RenameFont(face); ++ return impl->GetFont(impl, renamed_face.c_str()); ++} ++ ++unsigned long GetFontDataImpl(FPDF_SYSFONTINFO* info, ++ void* font, ++ unsigned int table, ++ unsigned char* buffer, ++ unsigned long buf_size) { ++ FPDF_SYSFONTINFO* impl = GetImpl(info); ++ return impl->GetFontData(impl, font, table, buffer, buf_size); ++} ++ ++unsigned long GetFaceNameImpl(FPDF_SYSFONTINFO* info, ++ void* font, ++ char* buffer, ++ unsigned long buf_size) { ++ FPDF_SYSFONTINFO* impl = GetImpl(info); ++ return impl->GetFaceName(impl, font, buffer, buf_size); ++} ++ ++int GetFontCharsetImpl(FPDF_SYSFONTINFO* info, void* font) { ++ FPDF_SYSFONTINFO* impl = GetImpl(info); ++ return impl->GetFontCharset(impl, font); ++} ++ ++void DeleteFontImpl(FPDF_SYSFONTINFO* info, void* font) { ++ FPDF_SYSFONTINFO* impl = GetImpl(info); ++ impl->DeleteFont(impl, font); ++} ++ ++} // namespace ++ ++FontRenamer::FontRenamer() : impl_(FPDF_GetDefaultSystemFontInfo()) { ++ version = 1; ++ Release = ReleaseImpl; ++ EnumFonts = EnumFontsImpl; ++ MapFont = MapFontImpl; ++ GetFont = GetFontImpl; ++ GetFontData = GetFontDataImpl; ++ GetFaceName = GetFaceNameImpl; ++ GetFontCharset = GetFontCharsetImpl; ++ DeleteFont = DeleteFontImpl; ++ FPDF_SetSystemFontInfo(this); ++} ++ ++FontRenamer::~FontRenamer() { ++ FPDF_FreeDefaultSystemFontInfo(impl_.ExtractAsDangling()); ++} +diff --git a/testing/font_renamer.h b/testing/font_renamer.h +new file mode 100644 +index 000000000..2c32c35b1 +--- /dev/null ++++ b/testing/font_renamer.h +@@ -0,0 +1,22 @@ ++// Copyright 2022 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef TESTING_FONT_RENAMER_H_ ++#define TESTING_FONT_RENAMER_H_ ++ ++#include "core/fxcrt/unowned_ptr.h" ++#include "public/fpdf_sysfontinfo.h" ++ ++class FontRenamer final : public FPDF_SYSFONTINFO { ++ public: ++ FontRenamer(); ++ ~FontRenamer(); ++ ++ FPDF_SYSFONTINFO* impl() { return impl_; } ++ ++ private: ++ UnownedPtr impl_; ++}; ++ ++#endif // TESTING_FONT_RENAMER_H_ +diff --git a/testing/free_deleter.h b/testing/free_deleter.h +index 58f40dafa..d18305050 100644 +--- a/testing/free_deleter.h ++++ b/testing/free_deleter.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/fuzzers/BUILD.gn b/testing/fuzzers/BUILD.gn +index 03e0610f8..35624cba1 100644 +--- a/testing/fuzzers/BUILD.gn ++++ b/testing/fuzzers/BUILD.gn +@@ -1,16 +1,16 @@ +-# Copyright 2016 The PDFium Authors. All rights reserved. ++# Copyright 2016 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + ++import("//build_overrides/build.gni") + import("../../pdfium.gni") + + config("fuzzer_config") { +- configs = [ "../..:pdfium_core_config" ] +- +- defines = [ +- "PNG_PREFIX", +- "PNG_USE_READ_MACROS", ++ configs = [ ++ "../..:pdfium_strict_config", ++ "../..:pdfium_noshorten_config", + ] ++ defines = [] + include_dirs = [ "../.." ] + } + +@@ -43,9 +43,9 @@ if (pdf_enable_v8) { + "pdf_cfx_barcode_fuzzer", + "pdf_codec_jpeg_fuzzer", + "pdf_css_fuzzer", +- "pdf_fm2js_fuzzer", + "pdf_formcalc_context_fuzzer", + "pdf_formcalc_fuzzer", ++ "pdf_formcalc_translate_fuzzer", + "pdfium_xfa_fuzzer", + "pdfium_xfa_lpm_fuzzer", + ] +@@ -68,45 +68,96 @@ if (pdf_enable_v8) { + } + if (is_clang) { + # Fuzzers that use FuzzedDataProvider can only be built with Clang. +- fuzzer_list += [ "pdf_nametree_fuzzer" ] ++ fuzzer_list += [ ++ "pdf_cpdf_tounicodemap_fuzzer", ++ "pdf_nametree_fuzzer", ++ ] ++ if (pdf_enable_xfa) { ++ fuzzer_list += [ ++ "pdf_xfa_fdp_fuzzer", ++ "pdf_xfa_raw_fuzzer", ++ "pdf_xfa_xdp_fdp_fuzzer", ++ ] ++ } + } + ++# Note that this only compiles all the fuzzers, to prevent compile breakages. ++# It does not link and create fuzzer executables. That is done in Chromium. + group("fuzzers") { + testonly = true + deps = [] + foreach(fuzzer, fuzzer_list) { + deps += [ ":${fuzzer}_src" ] + } ++ ++ if (is_component_build) { ++ deps += [ ":fuzzer_impls" ] ++ } ++} ++ ++source_set("fuzzer_pdf_templates") { ++ sources = [ "pdf_fuzzer_templates.h" ] + } + + source_set("fuzzer_init") { + testonly = true + sources = [ "pdf_fuzzer_init.cc" ] + include_dirs = [ "../.." ] +- deps = [ "../../:pdfium_public_headers" ] ++ deps = [ ++ "../../:pdfium_public_headers", ++ "../../fpdfsdk", ++ ] ++} ++ ++if (pdf_enable_xfa) { ++ assert(pdf_enable_v8) ++ source_set("fuzzer_xfa_process_state") { ++ testonly = !is_component_build ++ sources = [ ++ "xfa_process_state.cc", ++ "xfa_process_state.h", ++ ] ++ configs += [ ":fuzzer_config" ] ++ deps = [ ++ "../../fxjs:gc", ++ "//v8", ++ ] ++ } + } + + source_set("fuzzer_init_public") { + testonly = true + sources = [ "pdf_fuzzer_init_public.cc" ] + include_dirs = [ "../.." ] +- deps = [ "../../:pdfium_public_headers" ] ++ deps = [ ++ ":fuzzer_utils", ++ "../../:pdfium_public_headers", ++ "../../fpdfsdk", ++ ] + if (pdf_enable_v8) { + configs += [ "//v8:external_startup_data" ] + deps += [ + "../:test_support", ++ "../../fxjs", + "//v8", + "//v8:v8_libplatform", + ] ++ if (pdf_enable_xfa) { ++ deps += [ ":fuzzer_xfa_process_state" ] ++ } + } + } + + if (is_component_build) { + group("fuzzer_impls") { ++ testonly = true + deps = [] + foreach(fuzzer, fuzzer_list) { + deps += [ ":${fuzzer}_impl" ] + } ++ if (pdf_enable_xfa) { ++ deps += [ ":fuzzer_xfa_process_state" ] ++ } + } + } + +@@ -119,6 +170,7 @@ source_set("fuzzer_helper") { + configs += [ ":fuzzer_config" ] + deps = [ + "../../:pdfium_public_headers", ++ "../../fpdfsdk", + "../../third_party:pdfium_base", + ] + } +@@ -137,18 +189,20 @@ source_set("fuzzer_utils") { + } + + template("pdfium_fuzzer") { +- if (defined(invoker.public_fuzzer) && invoker.public_fuzzer) { ++ is_public = defined(invoker.public_fuzzer) && invoker.public_fuzzer ++ if (is_public) { + init_dep = ":fuzzer_init_public" + } else { + init_dep = ":fuzzer_init" + } + if (is_component_build) { + # In component builds, fuzzers are split into "_impl" and "_src" targets. +- # The "_impl" target exports the fuzzer implementation and gets statically +- # linked into the PDFium shared library. The "_src" target is a thin +- # wrapper that imports the fuzzer from PDFium; this gets linked into the +- # real fuzzer executable. In static builds, there's only a single "_src" +- # target that contains the implementation and statically links in PDFium. ++ # The "_impl" target exports the fuzzer implementation. The "_src" target ++ # is a thin wrapper that imports the fuzzer from PDFium; this gets linked ++ # into the real fuzzer executable. The real fuzzer target has to depend on ++ # both the "_impl" and "_src" targets. ++ # In static builds, there's only a single "_src" target that contains the ++ # implementation and statically links in PDFium. + + impl_name = target_name + "_impl" + template_target_name = target_name +@@ -156,7 +210,7 @@ template("pdfium_fuzzer") { + testonly = true + sources = [ "component_fuzzer_template.cc" ] + deps = [ +- "../../:pdfium", ++ "../../:pdfium_public_headers", + init_dep, + ] + configs += [ ":fuzzer_config" ] +@@ -166,16 +220,14 @@ template("pdfium_fuzzer") { + impl_name = target_name + "_src" + } + source_set(impl_name) { ++ testonly = true + sources = invoker.sources ++ defines = [] + deps = [] + if (defined(invoker.deps)) { + deps += invoker.deps + } +- configs -= [ "//build/config/compiler:chromium_code" ] +- configs += [ +- "//build/config/compiler:no_chromium_code", +- ":fuzzer_config", +- ] ++ configs += [ ":fuzzer_config" ] + if (is_component_build) { + # |export| should be consistent with FPDF_EXPORT In public/fpdfview.h. + if (is_win) { +@@ -183,7 +235,7 @@ template("pdfium_fuzzer") { + } else { + export = "__attribute__((visibility(\"default\")))" + } +- defines = [ "LLVMFuzzerTestOneInput=${export} ${template_target_name}" ] ++ defines += [ "LLVMFuzzerTestOneInput=${export} ${template_target_name}" ] + deps += [ "../../:pdfium_public_headers" ] + } else { + testonly = true +@@ -192,6 +244,12 @@ template("pdfium_fuzzer") { + init_dep, + ] + } ++ if (is_public && pdf_enable_xfa) { ++ deps += [ ":fuzzer_xfa_process_state" ] ++ } ++ if (build_with_chromium) { ++ defines += [ "BUILD_WITH_CHROMIUM" ] ++ } + } + } + +@@ -200,6 +258,7 @@ if (pdf_enable_v8) { + sources = [ "pdf_cjs_util_fuzzer.cc" ] + deps = [ + "../../core/fxcrt", ++ "../../fpdfsdk", + "../../fxjs", + ] + } +@@ -207,6 +266,7 @@ if (pdf_enable_v8) { + sources = [ "pdf_fx_date_helpers_fuzzer.cc" ] + deps = [ + "../../core/fxcrt", ++ "../../fpdfsdk", + "../../fxjs", + ] + } +@@ -218,7 +278,7 @@ if (pdf_enable_v8) { + "../../:freetype_common", + "../../core/fxcrt", + "../../core/fxge", +- "../../xfa/fgas", ++ "../../xfa/fgas/font", + "../../xfa/fgas/layout", + "//third_party/icu:icuuc", + ] +@@ -227,10 +287,15 @@ if (pdf_enable_v8) { + pdfium_fuzzer("pdf_cfgas_stringformatter_fuzzer") { + sources = [ "pdf_cfgas_stringformatter_fuzzer.cc" ] + deps = [ ++ ":fuzzer_utils", + "../../core/fxcrt", +- "../../xfa/fgas", ++ "../../fpdfsdk", ++ "../../fxjs:gc", ++ "../../xfa/fgas/crt", ++ "../../xfa/fxfa", + "../../xfa/fxfa/parser", + ] ++ public_fuzzer = true + } + + pdfium_fuzzer("pdf_cfx_barcode_fuzzer") { +@@ -331,12 +396,15 @@ if (pdf_enable_v8) { + ] + } + +- pdfium_fuzzer("pdf_fm2js_fuzzer") { +- sources = [ "pdf_fm2js_fuzzer.cc" ] ++ pdfium_fuzzer("pdf_formcalc_translate_fuzzer") { ++ sources = [ "pdf_formcalc_translate_fuzzer.cc" ] + deps = [ ++ ":fuzzer_utils", + "../../core/fxcrt", ++ "../../fpdfsdk", + "../../fxjs", + ] ++ public_fuzzer = true + } + + pdfium_fuzzer("pdf_formcalc_context_fuzzer") { +@@ -356,9 +424,12 @@ if (pdf_enable_v8) { + pdfium_fuzzer("pdf_formcalc_fuzzer") { + sources = [ "pdf_formcalc_fuzzer.cc" ] + deps = [ ++ ":fuzzer_utils", + "../../core/fxcrt", +- "../../xfa/fxfa/fm2js", ++ "../../fxjs:gc", ++ "../../xfa/fxfa/formcalc", + ] ++ public_fuzzer = true + } + + pdfium_fuzzer("pdfium_xfa_fuzzer") { +@@ -385,6 +456,15 @@ if (pdf_enable_v8) { + } + + if (is_clang) { ++ pdfium_fuzzer("pdf_cpdf_tounicodemap_fuzzer") { ++ sources = [ "pdf_cpdf_tounicodemap_fuzzer.cc" ] ++ deps = [ ++ "../../core/fpdfapi/font", ++ "../../core/fpdfapi/parser", ++ "../../core/fxcrt", ++ ] ++ } ++ + pdfium_fuzzer("pdf_nametree_fuzzer") { + sources = [ "pdf_nametree_fuzzer.cc" ] + deps = [ +@@ -394,6 +474,34 @@ if (is_clang) { + "../../third_party:pdfium_base", + ] + } ++ if (pdf_enable_xfa) { ++ pdfium_fuzzer("pdf_xfa_fdp_fuzzer") { ++ sources = [ "pdf_xfa_fdp_fuzzer.cc" ] ++ deps = [ ++ ":fuzzer_helper", ++ ":fuzzer_pdf_templates", ++ "../../third_party:pdfium_base", ++ ] ++ public_fuzzer = true ++ } ++ pdfium_fuzzer("pdf_xfa_raw_fuzzer") { ++ sources = [ "pdf_xfa_raw_fuzzer.cc" ] ++ deps = [ ++ ":fuzzer_helper", ++ ":fuzzer_pdf_templates", ++ "../../third_party:pdfium_base", ++ ] ++ public_fuzzer = true ++ } ++ pdfium_fuzzer("pdf_xfa_xdp_fdp_fuzzer") { ++ sources = [ "pdf_xfa_xdp_fdp_fuzzer.cc" ] ++ deps = [ ++ ":fuzzer_helper", ++ ":fuzzer_pdf_templates", ++ ] ++ public_fuzzer = true ++ } ++ } + } + + pdfium_fuzzer("pdf_cmap_fuzzer") { +@@ -401,6 +509,7 @@ pdfium_fuzzer("pdf_cmap_fuzzer") { + deps = [ + "../../:freetype_common", + "../../core/fpdfapi/font", ++ "../../core/fxcrt", + "../../third_party:pdfium_base", + ] + } +@@ -485,6 +594,7 @@ pdfium_fuzzer("pdf_scanlinecompositor_fuzzer") { + sources = [ "pdf_scanlinecompositor_fuzzer.cc" ] + deps = [ + ":fuzzer_utils", ++ "../../core/fxcrt", + "../../core/fxge", + "../../third_party:pdfium_base", + ] +@@ -510,5 +620,11 @@ pdfium_fuzzer("pdf_xml_fuzzer") { + pdfium_fuzzer("pdfium_fuzzer") { + sources = [ "pdfium_fuzzer.cc" ] + deps = [ ":fuzzer_helper" ] ++ if (build_with_chromium) { ++ deps += [ ++ "//base", ++ "//base/test:test_support", ++ ] ++ } + public_fuzzer = true + } +diff --git a/testing/fuzzers/DEPS b/testing/fuzzers/DEPS +index fe9eaf63e..d5770448c 100644 +--- a/testing/fuzzers/DEPS ++++ b/testing/fuzzers/DEPS +@@ -1,4 +1,7 @@ + include_rules = [ + '+fxbarcode', + '+xfa', ++ ++ # Only used when the fuzzer is embedded in Chromium. ++ '+base', + ] +diff --git a/testing/fuzzers/component_fuzzer_template.cc b/testing/fuzzers/component_fuzzer_template.cc +index 89883f552..6f65bc1c0 100644 +--- a/testing/fuzzers/component_fuzzer_template.cc ++++ b/testing/fuzzers/component_fuzzer_template.cc +@@ -1,9 +1,9 @@ +-// Copyright 2019 The PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include +-#include ++#include ++#include + + #include "public/fpdfview.h" + +diff --git a/testing/fuzzers/pdf_bidi_fuzzer.cc b/testing/fuzzers/pdf_bidi_fuzzer.cc +index 614df5298..c0ca77688 100644 +--- a/testing/fuzzers/pdf_bidi_fuzzer.cc ++++ b/testing/fuzzers/pdf_bidi_fuzzer.cc +@@ -1,28 +1,30 @@ +-// Copyright 2018 The PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include ++#include + ++#include "core/fxcrt/fx_codepage.h" + #include "core/fxcrt/widestring.h" + #include "core/fxge/cfx_font.h" + #include "core/fxge/fx_font.h" +-#include "third_party/base/ptr_util.h" +-#include "xfa/fgas/font/cfgas_fontmgr.h" + #include "xfa/fgas/font/cfgas_gefont.h" +-#include "xfa/fgas/layout/cfx_char.h" +-#include "xfa/fgas/layout/cfx_rtfbreak.h" ++#include "xfa/fgas/layout/cfgas_char.h" ++#include "xfa/fgas/layout/cfgas_rtfbreak.h" + + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { +- auto fontmgr = pdfium::MakeUnique(); ++ if (size > 8192) ++ return 0; + +- auto font = pdfium::MakeUnique(); +- font->LoadSubst("Arial", true, 0, FXFONT_FW_NORMAL, 0, 0, 0); ++ auto font = std::make_unique(); ++ font->LoadSubst("Arial", true, 0, FXFONT_FW_NORMAL, 0, FX_CodePage::kDefANSI, ++ 0); + assert(font); + +- CFX_RTFBreak rtf_break(FX_LAYOUTSTYLE_ExpandTab); ++ CFGAS_RTFBreak rtf_break(CFGAS_Break::LayoutStyle::kExpandTab); + rtf_break.SetLineBreakTolerance(1); +- rtf_break.SetFont(CFGAS_GEFont::LoadFont(std::move(font), fontmgr.get())); ++ rtf_break.SetFont(CFGAS_GEFont::LoadFont(std::move(font))); + rtf_break.SetFontSize(12); + + WideString input = +@@ -31,8 +33,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + for (wchar_t ch : input) + rtf_break.AppendChar(ch); + +- std::vector chars = ++ std::vector chars = + rtf_break.GetCurrentLineForTesting()->m_LineChars; +- CFX_Char::BidiLine(&chars, chars.size()); ++ CFGAS_Char::BidiLine(&chars, chars.size()); + return 0; + } +diff --git a/testing/fuzzers/pdf_cfgas_stringformatter_fuzzer.cc b/testing/fuzzers/pdf_cfgas_stringformatter_fuzzer.cc +index b6a66633b..12f62061f 100644 +--- a/testing/fuzzers/pdf_cfgas_stringformatter_fuzzer.cc ++++ b/testing/fuzzers/pdf_cfgas_stringformatter_fuzzer.cc +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,17 +6,25 @@ + + #include + +-#include "core/fxcrt/fx_memory.h" ++#include ++ ++#include "core/fxcrt/cfx_datetime.h" + #include "core/fxcrt/fx_string.h" +-#include "third_party/base/ptr_util.h" ++#include "public/fpdfview.h" ++#include "testing/fuzzers/pdfium_fuzzer_util.h" ++#include "testing/fuzzers/xfa_process_state.h" ++#include "v8/include/cppgc/heap.h" ++#include "v8/include/cppgc/persistent.h" + #include "xfa/fxfa/parser/cxfa_localemgr.h" + + namespace { + + const wchar_t* const kLocales[] = {L"en", L"fr", L"jp", L"zh"}; +-const FX_DATETIMETYPE kTypes[] = {FX_DATETIMETYPE_Date, FX_DATETIMETYPE_Time, +- FX_DATETIMETYPE_DateTime, +- FX_DATETIMETYPE_TimeDate}; ++const CFGAS_StringFormatter::DateTimeType kTypes[] = { ++ CFGAS_StringFormatter::DateTimeType::kDate, ++ CFGAS_StringFormatter::DateTimeType::kTime, ++ CFGAS_StringFormatter::DateTimeType::kDateTime, ++ CFGAS_StringFormatter::DateTimeType::kTimeDate}; + + } // namespace + +@@ -24,16 +32,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size < 5 || size > 128) // Big strings are unlikely to help. + return 0; + +- // Static for speed. +- static std::vector> mgrs; +- if (mgrs.empty()) { +- for (const auto* locale : kLocales) +- mgrs.push_back(pdfium::MakeUnique(nullptr, locale)); +- } ++ auto* state = static_cast(FPDF_GetFuzzerPerProcessState()); ++ cppgc::Heap* heap = state->GetHeap(); + + uint8_t test_selector = data[0] % 10; +- uint8_t locale_selector = data[1] % FX_ArraySize(kLocales); +- uint8_t type_selector = data[2] % FX_ArraySize(kTypes); ++ uint8_t locale_selector = data[1] % std::size(kLocales); ++ uint8_t type_selector = data[2] % std::size(kTypes); + data += 3; + size -= 3; + +@@ -44,8 +48,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + WideString value = + WideString::FromLatin1(ByteStringView(data + pattern_len, value_len)); + +- auto fmt = pdfium::MakeUnique( +- mgrs[locale_selector].get(), pattern); ++ auto fmt = std::make_unique(pattern); + + WideString result; + CFX_DateTime dt; +@@ -53,33 +56,55 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + case 0: + fmt->FormatText(value, &result); + break; +- case 1: +- fmt->FormatNum(value, &result); ++ case 1: { ++ auto* mgr = cppgc::MakeGarbageCollected( ++ heap->GetAllocationHandle(), heap, nullptr, ++ kLocales[locale_selector]); ++ fmt->FormatNum(mgr, value, &result); + break; +- case 2: +- fmt->FormatDateTime(value, kTypes[type_selector], &result); ++ } ++ case 2: { ++ auto* mgr = cppgc::MakeGarbageCollected( ++ heap->GetAllocationHandle(), heap, nullptr, ++ kLocales[locale_selector]); ++ fmt->FormatDateTime(mgr, value, kTypes[type_selector], &result); + break; +- case 3: ++ } ++ case 3: { + fmt->FormatNull(&result); + break; +- case 4: ++ } ++ case 4: { + fmt->FormatZero(&result); + break; +- case 5: ++ } ++ case 5: { + fmt->ParseText(value, &result); + break; +- case 6: +- fmt->ParseNum(value, &result); ++ } ++ case 6: { ++ auto* mgr = cppgc::MakeGarbageCollected( ++ heap->GetAllocationHandle(), heap, nullptr, ++ kLocales[locale_selector]); ++ fmt->ParseNum(mgr, value, &result); + break; +- case 7: +- fmt->ParseDateTime(value, kTypes[type_selector], &dt); ++ } ++ case 7: { ++ auto* mgr = cppgc::MakeGarbageCollected( ++ heap->GetAllocationHandle(), heap, nullptr, ++ kLocales[locale_selector]); ++ fmt->ParseDateTime(mgr, value, kTypes[type_selector], &dt); + break; +- case 8: ++ } ++ case 8: { + fmt->ParseNull(value); + break; +- case 9: ++ } ++ case 9: { + fmt->ParseZero(value); + break; ++ } + } ++ state->ForceGCAndPump(); + return 0; + } +diff --git a/testing/fuzzers/pdf_cfx_barcode_fuzzer.cc b/testing/fuzzers/pdf_cfx_barcode_fuzzer.cc +index 7b31b2ba4..35afec9ce 100644 +--- a/testing/fuzzers/pdf_cfx_barcode_fuzzer.cc ++++ b/testing/fuzzers/pdf_cfx_barcode_fuzzer.cc +@@ -1,9 +1,7 @@ +-// Copyright 2017 The PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include +- + #include "core/fxcrt/fx_string.h" + #include "fxbarcode/cfx_barcode.h" + +@@ -11,7 +9,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size < 2 * sizeof(uint16_t)) + return 0; + +- BC_TYPE type = static_cast(data[0] % (BC_LAST + 1)); ++ BC_TYPE type = ++ static_cast(data[0] % (static_cast(BC_TYPE::kLast) + 1)); + + // Only used one byte, but align with uint16_t for string below. + data += sizeof(uint16_t); +diff --git a/testing/fuzzers/pdf_cjs_util_fuzzer.cc b/testing/fuzzers/pdf_cjs_util_fuzzer.cc +index 5ccb65b9d..3885c7b13 100644 +--- a/testing/fuzzers/pdf_cjs_util_fuzzer.cc ++++ b/testing/fuzzers/pdf_cjs_util_fuzzer.cc +@@ -1,4 +1,4 @@ +-// Copyright 2018 The PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/fuzzers/pdf_cmap_fuzzer.cc b/testing/fuzzers/pdf_cmap_fuzzer.cc +index 180a6a71c..d4f9c7043 100644 +--- a/testing/fuzzers/pdf_cmap_fuzzer.cc ++++ b/testing/fuzzers/pdf_cmap_fuzzer.cc +@@ -1,10 +1,11 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include ++#include + + #include "core/fpdfapi/font/cpdf_cmap.h" ++#include "core/fxcrt/retain_ptr.h" + #include "third_party/base/span.h" + + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { +diff --git a/testing/fuzzers/pdf_codec_a85_fuzzer.cc b/testing/fuzzers/pdf_codec_a85_fuzzer.cc +index f9ae1fe13..2b1614da0 100644 +--- a/testing/fuzzers/pdf_codec_a85_fuzzer.cc ++++ b/testing/fuzzers/pdf_codec_a85_fuzzer.cc +@@ -1,16 +1,12 @@ +-// Copyright 2017 The PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include +-#include + + #include "core/fxcodec/basic/basicmodule.h" +-#include "core/fxcrt/fx_memory_wrappers.h" + + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { +- std::unique_ptr dest_buf; +- uint32_t dest_size = 0; +- BasicModule::A85Encode({data, size}, &dest_buf, &dest_size); ++ BasicModule::A85Encode({data, size}); + return 0; + } +diff --git a/testing/fuzzers/pdf_codec_bmp_fuzzer.cc b/testing/fuzzers/pdf_codec_bmp_fuzzer.cc +index 71f9150f5..1bc023aed 100644 +--- a/testing/fuzzers/pdf_codec_bmp_fuzzer.cc ++++ b/testing/fuzzers/pdf_codec_bmp_fuzzer.cc +@@ -1,4 +1,4 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/fuzzers/pdf_codec_fax_fuzzer.cc b/testing/fuzzers/pdf_codec_fax_fuzzer.cc +index d0c29844a..793d18e76 100644 +--- a/testing/fuzzers/pdf_codec_fax_fuzzer.cc ++++ b/testing/fuzzers/pdf_codec_fax_fuzzer.cc +@@ -1,4 +1,4 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -38,7 +38,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + + if (decoder) { + int line = 0; +- while (decoder->GetScanline(line)) ++ while (!decoder->GetScanline(line).empty()) + line++; + } + +diff --git a/testing/fuzzers/pdf_codec_gif_fuzzer.cc b/testing/fuzzers/pdf_codec_gif_fuzzer.cc +index 69129e706..4adbad7b0 100644 +--- a/testing/fuzzers/pdf_codec_gif_fuzzer.cc ++++ b/testing/fuzzers/pdf_codec_gif_fuzzer.cc +@@ -1,4 +1,4 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/fuzzers/pdf_codec_icc_fuzzer.cc b/testing/fuzzers/pdf_codec_icc_fuzzer.cc +index ca0273317..4db79d255 100644 +--- a/testing/fuzzers/pdf_codec_icc_fuzzer.cc ++++ b/testing/fuzzers/pdf_codec_icc_fuzzer.cc +@@ -1,23 +1,21 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include + +-#include "core/fxcodec/icc/iccmodule.h" ++#include "core/fxcodec/icc/icc_transform.h" + #include "third_party/base/span.h" + + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { +- std::unique_ptr transform = +- IccModule::CreateTransformSRGB(pdfium::make_span(data, size)); +- +- if (transform) { +- float src[4]; +- float dst[4]; +- for (int i = 0; i < 4; i++) +- src[i] = 0.5f; +- IccModule::Translate(transform.get(), transform->components(), src, dst); +- } ++ std::unique_ptr transform = ++ fxcodec::IccTransform::CreateTransformSRGB(pdfium::make_span(data, size)); ++ if (!transform) ++ return 0; + ++ const float src[4] = {0.5f, 0.5f, 0.5f, 0.5f}; ++ float dst[4]; ++ transform->Translate(pdfium::make_span(src).first(transform->components()), ++ pdfium::make_span(dst)); + return 0; + } +diff --git a/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc b/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc +index 2878d7c49..59eda7610 100644 +--- a/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc ++++ b/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc +@@ -1,17 +1,16 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include ++#include + + #include "core/fxcodec/jbig2/JBig2_Context.h" + #include "core/fxcodec/jbig2/JBig2_DocumentContext.h" +-#include "core/fxcodec/jbig2/jbig2module.h" ++#include "core/fxcodec/jbig2/jbig2_decoder.h" + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" + #include "testing/fuzzers/pdfium_fuzzer_util.h" +-#include "third_party/base/ptr_util.h" + + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + const size_t kParameterSize = 8; +@@ -32,17 +31,16 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + return 0; + + auto bitmap = pdfium::MakeRetain(); +- if (!bitmap->Create(width, height, FXDIB_1bppRgb)) ++ if (!bitmap->Create(width, height, FXDIB_Format::k1bppRgb)) + return 0; + +- Jbig2Module module; ++ JBig2_DocumentContext document_context; + Jbig2Context jbig2_context; +- std::unique_ptr document_context; +- FXCODEC_STATUS status = module.StartDecode( ++ FXCODEC_STATUS status = Jbig2Decoder::StartDecode( + &jbig2_context, &document_context, width, height, {data, size}, 1, {}, 0, + bitmap->GetBuffer(), bitmap->GetPitch(), nullptr); + +- while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE) +- status = module.ContinueDecode(&jbig2_context, nullptr); ++ while (status == FXCODEC_STATUS::kDecodeToBeContinued) ++ status = Jbig2Decoder::ContinueDecode(&jbig2_context, nullptr); + return 0; + } +diff --git a/testing/fuzzers/pdf_codec_jpeg_fuzzer.cc b/testing/fuzzers/pdf_codec_jpeg_fuzzer.cc +index eaa0889e3..a9f721644 100644 +--- a/testing/fuzzers/pdf_codec_jpeg_fuzzer.cc ++++ b/testing/fuzzers/pdf_codec_jpeg_fuzzer.cc +@@ -1,4 +1,4 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/fuzzers/pdf_codec_png_fuzzer.cc b/testing/fuzzers/pdf_codec_png_fuzzer.cc +index 61a65748f..14d9bd34f 100644 +--- a/testing/fuzzers/pdf_codec_png_fuzzer.cc ++++ b/testing/fuzzers/pdf_codec_png_fuzzer.cc +@@ -1,4 +1,4 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/fuzzers/pdf_codec_rle_fuzzer.cc b/testing/fuzzers/pdf_codec_rle_fuzzer.cc +index 7b40b01b5..70ce5d4af 100644 +--- a/testing/fuzzers/pdf_codec_rle_fuzzer.cc ++++ b/testing/fuzzers/pdf_codec_rle_fuzzer.cc +@@ -1,16 +1,12 @@ +-// Copyright 2017 The PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include +-#include + + #include "core/fxcodec/basic/basicmodule.h" +-#include "core/fxcrt/fx_memory_wrappers.h" + + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { +- std::unique_ptr dest_buf; +- uint32_t dest_size = 0; +- BasicModule::RunLengthEncode({data, size}, &dest_buf, &dest_size); ++ BasicModule::RunLengthEncode({data, size}); + return 0; + } +diff --git a/testing/fuzzers/pdf_codec_tiff_fuzzer.cc b/testing/fuzzers/pdf_codec_tiff_fuzzer.cc +index 187c311fc..6566b6bce 100644 +--- a/testing/fuzzers/pdf_codec_tiff_fuzzer.cc ++++ b/testing/fuzzers/pdf_codec_tiff_fuzzer.cc +@@ -1,4 +1,4 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/fuzzers/pdf_cpdf_tounicodemap_fuzzer.cc b/testing/fuzzers/pdf_cpdf_tounicodemap_fuzzer.cc +new file mode 100644 +index 000000000..5f1856454 +--- /dev/null ++++ b/testing/fuzzers/pdf_cpdf_tounicodemap_fuzzer.cc +@@ -0,0 +1,38 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include ++ ++#include ++#include ++#include ++ ++#include "core/fpdfapi/font/cpdf_tounicodemap.h" ++#include "core/fpdfapi/parser/cpdf_stream.h" ++#include "core/fxcrt/retain_ptr.h" ++ ++extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { ++ static constexpr size_t kParameterSize = sizeof(uint32_t) + sizeof(wchar_t); ++ if (size <= kParameterSize) ++ return 0; ++ ++ // Limit data size to prevent fuzzer timeout. ++ static constexpr size_t kMaxDataSize = 256 * 1024; ++ if (size > kParameterSize + kMaxDataSize) ++ return 0; ++ ++ FuzzedDataProvider data_provider(data, size); ++ uint32_t charcode_to_lookup = data_provider.ConsumeIntegral(); ++ wchar_t char_for_reverse_lookup = data_provider.ConsumeIntegral(); ++ ++ std::vector remaining = ++ data_provider.ConsumeRemainingBytes(); ++ auto stream = pdfium::MakeRetain(); ++ stream->SetData(remaining); ++ ++ auto to_unicode_map = std::make_unique(std::move(stream)); ++ to_unicode_map->Lookup(charcode_to_lookup); ++ to_unicode_map->ReverseLookup(char_for_reverse_lookup); ++ return 0; ++} +diff --git a/testing/fuzzers/pdf_css_fuzzer.cc b/testing/fuzzers/pdf_css_fuzzer.cc +index 5f1471d1c..f11705c5d 100644 +--- a/testing/fuzzers/pdf_css_fuzzer.cc ++++ b/testing/fuzzers/pdf_css_fuzzer.cc +@@ -1,9 +1,7 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include +- + #include "core/fxcrt/css/cfx_css.h" + #include "core/fxcrt/css/cfx_csssyntaxparser.h" + #include "core/fxcrt/fx_string.h" +@@ -16,11 +14,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (input.IsEmpty()) + return 0; + +- CFX_CSSSyntaxParser parser(input.c_str(), input.GetLength()); +- CFX_CSSSyntaxStatus status; ++ CFX_CSSSyntaxParser parser(input.AsStringView()); ++ CFX_CSSSyntaxParser::Status status; + do { + status = parser.DoSyntaxParse(); +- } while (status != CFX_CSSSyntaxStatus::Error && +- status != CFX_CSSSyntaxStatus::EOS); ++ } while (status != CFX_CSSSyntaxParser::Status::kError && ++ status != CFX_CSSSyntaxParser::Status::kEOS); + return 0; + } +diff --git a/testing/fuzzers/pdf_fm2js_fuzzer.cc b/testing/fuzzers/pdf_fm2js_fuzzer.cc +deleted file mode 100644 +index 67daa462d..000000000 +--- a/testing/fuzzers/pdf_fm2js_fuzzer.cc ++++ /dev/null +@@ -1,23 +0,0 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#include +-#include +- +-#include "core/fxcrt/cfx_widetextbuf.h" +-#include "core/fxcrt/fx_safe_types.h" +-#include "core/fxcrt/fx_string.h" +-#include "fxjs/xfa/cfxjse_formcalc_context.h" +- +-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { +- FX_SAFE_SIZE_T safe_size = size; +- if (!safe_size.IsValid()) +- return 0; +- +- CFX_WideTextBuf js; +- WideString input = +- WideString::FromUTF8(ByteStringView(data, safe_size.ValueOrDie())); +- CFXJSE_FormCalcContext::Translate(input.AsStringView(), &js); +- return 0; +-} +diff --git a/testing/fuzzers/pdf_font_fuzzer.cc b/testing/fuzzers/pdf_font_fuzzer.cc +index 7c596307f..a02052ae7 100644 +--- a/testing/fuzzers/pdf_font_fuzzer.cc ++++ b/testing/fuzzers/pdf_font_fuzzer.cc +@@ -1,16 +1,15 @@ +-// Copyright 2017 The PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include +-#include +- + #include "public/cpp/fpdf_scopers.h" + #include "public/fpdf_edit.h" + #include "public/fpdfview.h" + ++static constexpr size_t kMaxFuzzBytes = 1024 * 1024 * 1024; // 1 GB. ++ + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { +- if (size < 2) ++ if (size < 2 || size > kMaxFuzzBytes) + return 0; + + ScopedFPDFDocument doc(FPDF_CreateNewDocument()); +@@ -19,7 +18,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FPDF_BOOL cid = data[1]; + data += 2; + size -= 2; +- ScopedFPDFFont font(FPDFText_LoadFont(doc.get(), data, size, font_type, cid)); ++ ScopedFPDFFont font(FPDFText_LoadFont( ++ doc.get(), data, static_cast(size), font_type, cid)); + if (!font) + return 0; + +diff --git a/testing/fuzzers/pdf_formcalc_context_fuzzer.cc b/testing/fuzzers/pdf_formcalc_context_fuzzer.cc +index e2d73a8d9..638d0861f 100644 +--- a/testing/fuzzers/pdf_formcalc_context_fuzzer.cc ++++ b/testing/fuzzers/pdf_formcalc_context_fuzzer.cc +@@ -1,9 +1,11 @@ +-// Copyright 2019 The PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include + ++#include ++ + #include "core/fxcrt/fx_string.h" + #include "fpdfsdk/cpdfsdk_helpers.h" + #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h" +@@ -11,7 +13,6 @@ + #include "fxjs/xfa/cfxjse_value.h" + #include "public/fpdf_formfill.h" + #include "testing/fuzzers/pdfium_fuzzer_helper.h" +-#include "v8/include/v8.h" + #include "xfa/fxfa/cxfa_eventparam.h" + + namespace { +@@ -522,15 +523,14 @@ class PDFiumFormCalcContextFuzzer : public PDFiumFuzzerHelper { + + CXFA_EventParam params; + params.m_bCancelAction = false; +- script_context->SetEventParam(¶ms); ++ CFXJSE_Engine::EventParamScope param_scope(script_context, nullptr, ++ ¶ms); + ByteStringView data_view(data_, size_); + +- auto value = pdfium::MakeUnique(script_context->GetIsolate()); ++ auto value = std::make_unique(); + script_context->RunScript(CXFA_Script::Type::Formcalc, + WideString::FromUTF8(data_view).AsStringView(), + value.get(), xfa_document->GetRoot()); +- +- script_context->SetEventParam(nullptr); + } + + private: +diff --git a/testing/fuzzers/pdf_formcalc_fuzzer.cc b/testing/fuzzers/pdf_formcalc_fuzzer.cc +index 08e22bb24..d8a2a895e 100644 +--- a/testing/fuzzers/pdf_formcalc_fuzzer.cc ++++ b/testing/fuzzers/pdf_formcalc_fuzzer.cc +@@ -1,16 +1,18 @@ +-// Copyright 2017 The PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include "core/fxcrt/cfx_widetextbuf.h" + #include "core/fxcrt/fx_string.h" +-#include "xfa/fxfa/fm2js/cxfa_fmparser.h" ++#include "testing/fuzzers/pdfium_fuzzer_util.h" ++#include "testing/fuzzers/xfa_process_state.h" ++#include "xfa/fxfa/formcalc/cxfa_fmparser.h" + + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { ++ auto* state = static_cast(FPDF_GetFuzzerPerProcessState()); + WideString input = WideString::FromUTF8(ByteStringView(data, size)); +- +- CXFA_FMParser parser(input.AsStringView()); ++ CXFA_FMLexer lexer(input.AsStringView()); ++ CXFA_FMParser parser(state->GetHeap(), &lexer); + parser.Parse(); +- ++ state->ForceGCAndPump(); + return 0; + } +diff --git a/testing/fuzzers/pdf_formcalc_translate_fuzzer.cc b/testing/fuzzers/pdf_formcalc_translate_fuzzer.cc +new file mode 100644 +index 000000000..82b8188cc +--- /dev/null ++++ b/testing/fuzzers/pdf_formcalc_translate_fuzzer.cc +@@ -0,0 +1,20 @@ ++// Copyright 2016 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include ++#include ++ ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/fx_string.h" ++#include "fxjs/xfa/cfxjse_formcalc_context.h" ++#include "testing/fuzzers/pdfium_fuzzer_util.h" ++#include "testing/fuzzers/xfa_process_state.h" ++ ++extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { ++ auto* state = static_cast(FPDF_GetFuzzerPerProcessState()); ++ WideString input = WideString::FromUTF8(ByteStringView(data, size)); ++ CFXJSE_FormCalcContext::Translate(state->GetHeap(), input.AsStringView()); ++ state->ForceGCAndPump(); ++ return 0; ++} +diff --git a/testing/fuzzers/pdf_fuzzer_init.cc b/testing/fuzzers/pdf_fuzzer_init.cc +index 954eed0dd..9a75dd2ce 100644 +--- a/testing/fuzzers/pdf_fuzzer_init.cc ++++ b/testing/fuzzers/pdf_fuzzer_init.cc +@@ -1,4 +1,4 @@ +-// Copyright 2017 The PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/fuzzers/pdf_fuzzer_init_public.cc b/testing/fuzzers/pdf_fuzzer_init_public.cc +index 5ece0bcd2..3227d47bc 100644 +--- a/testing/fuzzers/pdf_fuzzer_init_public.cc ++++ b/testing/fuzzers/pdf_fuzzer_init_public.cc +@@ -1,24 +1,33 @@ +-// Copyright 2019 The PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include ++#include "testing/fuzzers/pdf_fuzzer_init_public.h" + +-#include ++#include // For memset() + +-#include "public/fpdf_ext.h" ++#include ++ ++#include "testing/fuzzers/pdfium_fuzzer_util.h" + + #ifdef PDF_ENABLE_V8 + #include "testing/free_deleter.h" + #include "testing/v8_initializer.h" + #include "v8/include/v8-platform.h" +-#include "v8/include/v8.h" +-#endif ++#ifdef PDF_ENABLE_XFA ++#include "testing/fuzzers/xfa_process_state.h" ++#include "v8/include/v8-array-buffer.h" ++#include "v8/include/v8-isolate.h" ++#endif // PDF_ENABLE_XFA ++#endif // PDF_ENABLE_V8 + + #ifdef _WIN32 + #include + #elif defined(__APPLE__) + #include ++#elif defined(__Fuchsia__) ++#include ++#include + #else // Linux + #include + #include +@@ -26,10 +35,13 @@ + + namespace { + ++// pdf_fuzzer_init.cc and pdf_fuzzer_init_public.cc are mutually exclusive ++// and should not be built together. ++PDFFuzzerInitPublic* g_instance = new PDFFuzzerInitPublic(); ++ + #ifdef PDF_ENABLE_V8 + std::string ProgramPath() { + std::string result; +- + #ifdef _WIN32 + char path[MAX_PATH]; + DWORD len = GetModuleFileNameA(nullptr, path, MAX_PATH); +@@ -56,41 +68,45 @@ std::string ProgramPath() { + + } // namespace + +-// Initialize the library once for all runs of the fuzzer. +-struct TestCase { +- TestCase() { ++PDFFuzzerInitPublic::PDFFuzzerInitPublic() { + #ifdef PDF_ENABLE_V8 + #ifdef V8_USE_EXTERNAL_STARTUP_DATA +- platform = InitializeV8ForPDFiumWithStartupData( +- ProgramPath(), std::string(), &snapshot_blob); +-#else +- platform = InitializeV8ForPDFium(ProgramPath()); ++ platform_ = InitializeV8ForPDFiumWithStartupData( ++ ProgramPath(), std::string(), std::string(), &snapshot_blob_); ++#else // V8_USE_EXTERNAL_STARTUP_DATA ++ platform_ = InitializeV8ForPDFium(ProgramPath(), std::string()); + #endif // V8_USE_EXTERNAL_STARTUP_DATA ++#ifdef PDF_ENABLE_XFA ++ allocator_.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator()); ++ v8::Isolate::CreateParams create_params; ++ create_params.array_buffer_allocator = allocator_.get(); ++ isolate_.reset(v8::Isolate::New(create_params)); ++#endif // PDF_ENABLE_XFA + #endif // PDF_ENABLE_V8 ++ memset(&config_, '\0', sizeof(config_)); ++ config_.version = 3; ++ config_.m_pUserFontPaths = nullptr; ++ config_.m_pPlatform = nullptr; ++ config_.m_pIsolate = nullptr; ++ config_.m_v8EmbedderSlot = 0; ++#ifdef PDF_ENABLE_V8 ++ config_.m_pPlatform = platform_.get(); ++ config_.m_pIsolate = isolate_.get(); ++#endif // PDF_ENABLE_V8 ++ FPDF_InitLibraryWithConfig(&config_); + +- memset(&config, '\0', sizeof(config)); +- config.version = 2; +- config.m_pUserFontPaths = nullptr; +- config.m_pIsolate = nullptr; +- config.m_v8EmbedderSlot = 0; +- FPDF_InitLibraryWithConfig(&config); +- +- memset(&unsupport_info, '\0', sizeof(unsupport_info)); +- unsupport_info.version = 1; +- unsupport_info.FSDK_UnSupport_Handler = [](UNSUPPORT_INFO*, int) {}; +- FSDK_SetUnSpObjProcessHandler(&unsupport_info); +- } ++ memset(&unsupport_info_, '\0', sizeof(unsupport_info_)); ++ unsupport_info_.version = 1; ++ unsupport_info_.FSDK_UnSupport_Handler = [](UNSUPPORT_INFO*, int) {}; ++ FSDK_SetUnSpObjProcessHandler(&unsupport_info_); + +-#ifdef PDF_ENABLE_V8 +- std::unique_ptr platform; +- v8::StartupData snapshot_blob; ++#ifdef PDF_ENABLE_XFA ++ xfa_process_state_ = ++ std::make_unique(platform_.get(), isolate_.get()); ++ FPDF_SetFuzzerPerProcessState(xfa_process_state_.get()); + #endif ++} + +- FPDF_LIBRARY_CONFIG config; +- UNSUPPORT_INFO unsupport_info; +-}; +- +-// pdf_fuzzer_init.cc and pdfium_fuzzer_init_public.cc are mutually exclusive +-// and should not be built together. They deliberately have the same global +-// variable. +-static TestCase* g_test_case = new TestCase(); ++PDFFuzzerInitPublic::~PDFFuzzerInitPublic() { ++ FPDF_SetFuzzerPerProcessState(nullptr); ++} +diff --git a/testing/fuzzers/pdf_fuzzer_init_public.h b/testing/fuzzers/pdf_fuzzer_init_public.h +new file mode 100644 +index 000000000..4b7f46f35 +--- /dev/null ++++ b/testing/fuzzers/pdf_fuzzer_init_public.h +@@ -0,0 +1,50 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef TESTING_FUZZERS_PDF_FUZZER_INIT_PUBLIC_H_ ++#define TESTING_FUZZERS_PDF_FUZZER_INIT_PUBLIC_H_ ++ ++#include ++ ++#include "public/fpdf_ext.h" ++#include "public/fpdfview.h" ++ ++#ifdef PDF_ENABLE_V8 ++#include "fxjs/cfx_v8.h" ++#include "v8/include/v8-array-buffer.h" ++#include "v8/include/v8-snapshot.h" ++#endif // PDF_ENABLE_V8 ++ ++class XFAProcessState; ++ ++#ifdef PDF_ENABLE_V8 ++namespace v8 { ++class Isolate; ++class Platform; ++} // namespace v8 ++#endif // PDF_ENABLE_V8 ++ ++// Initializes the library once for all runs of the fuzzer. ++class PDFFuzzerInitPublic { ++ public: ++ PDFFuzzerInitPublic(); ++ ~PDFFuzzerInitPublic(); ++ ++ private: ++ FPDF_LIBRARY_CONFIG config_; ++ UNSUPPORT_INFO unsupport_info_; ++#ifdef PDF_ENABLE_V8 ++#ifdef V8_USE_EXTERNAL_STARTUP_DATA ++ v8::StartupData snapshot_blob_; ++#endif // V8_USE_EXTERNAL_STARTUP_DATA ++ std::unique_ptr platform_; ++ std::unique_ptr allocator_; ++ std::unique_ptr isolate_; ++#ifdef PDF_ENABLE_XFA ++ std::unique_ptr xfa_process_state_; ++#endif // PDF_ENABLE_XFA ++#endif // PDF_ENABLE_V8 ++}; ++ ++#endif // TESTING_FUZZERS_PDF_FUZZER_INIT_PUBLIC_H_ +diff --git a/testing/fuzzers/pdf_fuzzer_templates.h b/testing/fuzzers/pdf_fuzzer_templates.h +new file mode 100644 +index 000000000..3fe4ea87f +--- /dev/null ++++ b/testing/fuzzers/pdf_fuzzer_templates.h +@@ -0,0 +1,52 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// File for holding strings representing PDF templates that are used by fuzzers. ++ ++#ifndef TESTING_FUZZERS_PDF_FUZZER_TEMPLATES_H_ ++#define TESTING_FUZZERS_PDF_FUZZER_TEMPLATES_H_ ++ ++constexpr char kSimplePdfTemplate[] = R"(%PDF-1.7 ++1 0 obj ++<> /NeedsRendering true>> ++endobj ++2 0 obj ++<> ++endobj ++3 0 obj ++<> ++endobj ++30 0 obj ++<> ++stream ++$2 ++endstream ++endobj ++trailer ++<> ++%%EOF)"; ++ ++// We define the bytes of the header explicitly to make the values more readable ++constexpr uint8_t kSimplePdfHeader[] = {0x25, 0x50, 0x44, 0x46, 0x2d, ++ 0x31, 0x2e, 0x37, 0x0a, 0x25, ++ 0xa0, 0xf2, 0xa4, 0xf4, 0x0a}; ++ ++constexpr char kCatalog[] = R""(<>>> /NeedsRendering true ++ /Pages 3 0 R /Type /Catalog>>)""; ++ ++constexpr char kSimpleXfaObjWrapper[] = R""(<>)""; ++ ++constexpr char kSimplePagesObj[] = "<>"; ++constexpr char kSimplePageObj[] = ++ "<>"; ++constexpr char kSimplePreamble[] = ++ R""()""; ++constexpr char kSimplePostamble[] = ""; ++ ++#endif // TESTING_FUZZERS_PDF_FUZZER_TEMPLATES_H_ +diff --git a/testing/fuzzers/pdf_fx_date_helpers_fuzzer.cc b/testing/fuzzers/pdf_fx_date_helpers_fuzzer.cc +index e31deccc1..d98fffd3c 100644 +--- a/testing/fuzzers/pdf_fx_date_helpers_fuzzer.cc ++++ b/testing/fuzzers/pdf_fx_date_helpers_fuzzer.cc +@@ -1,9 +1,7 @@ +-// Copyright 2018 The PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include +- + #include "core/fxcrt/widestring.h" + #include "fxjs/fx_date_helpers.h" + +diff --git a/testing/fuzzers/pdf_hint_table_fuzzer.cc b/testing/fuzzers/pdf_hint_table_fuzzer.cc +index 154007497..3743b1af7 100644 +--- a/testing/fuzzers/pdf_hint_table_fuzzer.cc ++++ b/testing/fuzzers/pdf_hint_table_fuzzer.cc +@@ -1,4 +1,4 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -11,7 +11,6 @@ + #include "core/fpdfapi/parser/cpdf_linearized_header.h" + #include "core/fpdfapi/parser/cpdf_number.h" + #include "core/fxcrt/cfx_bitstream.h" +-#include "third_party/base/ptr_util.h" + #include "third_party/base/span.h" + + int32_t GetData(const int32_t** data32, const uint8_t** data, size_t* size) { +@@ -28,7 +27,7 @@ class HintTableForFuzzing final : public CPDF_HintTables { + int shared_hint_table_offset) + : CPDF_HintTables(nullptr, pLinearized), + shared_hint_table_offset_(shared_hint_table_offset) {} +- ~HintTableForFuzzing() {} ++ ~HintTableForFuzzing() = default; + + void Fuzz(const uint8_t* data, size_t size) { + if (shared_hint_table_offset_ <= 0) +@@ -76,9 +75,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + + auto hint_info = pdfium::MakeRetain(); + // Add primary hint stream offset +- hint_info->AddNew(GetData(&data32, &data, &size)); ++ hint_info->AppendNew(GetData(&data32, &data, &size)); + // Add primary hint stream size +- hint_info->AddNew(GetData(&data32, &data, &size)); ++ hint_info->AppendNew(GetData(&data32, &data, &size)); + // Set hint stream info. + linearized_dict->SetFor("H", std::move(hint_info)); + +diff --git a/testing/fuzzers/pdf_jpx_fuzzer.cc b/testing/fuzzers/pdf_jpx_fuzzer.cc +index 3986ae23c..3021d7688 100644 +--- a/testing/fuzzers/pdf_jpx_fuzzer.cc ++++ b/testing/fuzzers/pdf_jpx_fuzzer.cc +@@ -1,17 +1,15 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include + #include +-#include + + #include "core/fpdfapi/page/cpdf_colorspace.h" + #include "core/fxcodec/jpx/cjpx_decoder.h" +-#include "core/fxcodec/jpx/jpxmodule.h" + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" + + namespace { + +@@ -21,19 +19,19 @@ bool CheckImageSize(const CJPX_Decoder::JpxImageInfo& image_info) { + static constexpr uint32_t kMemLimitBytes = 1024 * 1024 * 1024; // 1 GB. + FX_SAFE_UINT32 mem = image_info.width; + mem *= image_info.height; +- mem *= image_info.components; ++ mem *= image_info.channels; + return mem.IsValid() && mem.ValueOrDie() <= kMemLimitBytes; + } + + } // namespace + + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { +- if (size < 1) ++ if (size < 2) + return 0; + +- std::unique_ptr decoder = JpxModule::CreateDecoder( +- {data + 1, size - 1}, +- static_cast(data[0] % 3)); ++ std::unique_ptr decoder = CJPX_Decoder::Create( ++ {data + 2, size - 2}, ++ static_cast(data[0] % 3), data[1]); + if (!decoder) + return 0; + +@@ -52,15 +50,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + return 0; + + FXDIB_Format format; +- if (image_info.components == 1) { +- format = FXDIB_8bppRgb; +- } else if (image_info.components <= 3) { +- format = FXDIB_Rgb; +- } else if (image_info.components == 4) { +- format = FXDIB_Rgb32; ++ if (image_info.channels == 1) { ++ format = FXDIB_Format::k8bppRgb; ++ } else if (image_info.channels <= 3) { ++ format = FXDIB_Format::kRgb; ++ } else if (image_info.channels == 4) { ++ format = FXDIB_Format::kRgb32; + } else { +- image_info.width = (image_info.width * image_info.components + 2) / 3; +- format = FXDIB_Rgb; ++ image_info.width = (image_info.width * image_info.channels + 2) / 3; ++ format = FXDIB_Format::kRgb; + } + auto bitmap = pdfium::MakeRetain(); + if (!bitmap->Create(image_info.width, image_info.height, format)) +@@ -71,8 +69,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + static_cast(bitmap->GetHeight())) + return 0; + +- decoder->Decode(bitmap->GetBuffer(), bitmap->GetPitch(), +- /*swap_rgb=*/false); ++ decoder->Decode(bitmap->GetBuffer(), bitmap->GetPitch(), /*swap_rgb=*/false, ++ GetCompsFromFormat(format)); + + return 0; + } +diff --git a/testing/fuzzers/pdf_lzw_fuzzer.cc b/testing/fuzzers/pdf_lzw_fuzzer.cc +index e4d993e82..38d792973 100644 +--- a/testing/fuzzers/pdf_lzw_fuzzer.cc ++++ b/testing/fuzzers/pdf_lzw_fuzzer.cc +@@ -1,10 +1,13 @@ +-// Copyright 2017 The PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include ++#include ++ + #include + +-#include "core/fxcodec/gif/cfx_lzwdecompressor.h" ++#include "core/fxcodec/gif/lzw_decompressor.h" + #include "third_party/base/numerics/safe_conversions.h" + + // Between 2x and 5x is a standard range for LZW according to a quick +@@ -12,12 +15,14 @@ + constexpr uint32_t kMinCompressionRatio = 2; + constexpr uint32_t kMaxCompressionRatio = 10; + ++static constexpr size_t kMaxFuzzBytes = 1024 * 1024 * 1024; // 1 GB. ++ + void LZWFuzz(const uint8_t* src_buf, +- size_t src_size, ++ uint32_t src_size, + uint8_t color_exp, + uint8_t code_exp) { +- std::unique_ptr decompressor = +- CFX_LZWDecompressor::Create(color_exp, code_exp); ++ std::unique_ptr decompressor = ++ LZWDecompressor::Create(color_exp, code_exp); + if (!decompressor) + return; + +@@ -27,8 +32,9 @@ void LZWFuzz(const uint8_t* src_buf, + // This cast should be safe since the caller is checking for overflow on + // the initial data. + uint32_t dest_size = static_cast(dest_buf.size()); +- if (CFX_GifDecodeStatus::InsufficientDestSize != +- decompressor->Decode(src_buf, src_size, dest_buf.data(), &dest_size)) { ++ decompressor->SetSource(src_buf, src_size); ++ if (LZWDecompressor::Status::kInsufficientDestSize != ++ decompressor->Decode(dest_buf.data(), &dest_size)) { + return; + } + } +@@ -36,7 +42,7 @@ void LZWFuzz(const uint8_t* src_buf, + + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // Need at least 3 bytes to do anything. +- if (size < 3) ++ if (size < 3 || size > kMaxFuzzBytes) + return 0; + + // Normally the GIF would provide the code and color sizes, instead, going +diff --git a/testing/fuzzers/pdf_nametree_fuzzer.cc b/testing/fuzzers/pdf_nametree_fuzzer.cc +index 9a3702456..bc141ed3c 100644 +--- a/testing/fuzzers/pdf_nametree_fuzzer.cc ++++ b/testing/fuzzers/pdf_nametree_fuzzer.cc +@@ -1,4 +1,4 @@ +-// Copyright 2019 The PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -54,22 +54,23 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + + CPDF_StreamParser parser(remaining); + auto dict = pdfium::MakeRetain(); +- CPDF_NameTree name_tree(dict.Get()); ++ std::unique_ptr name_tree = ++ CPDF_NameTree::CreateForTesting(dict.Get()); + for (const auto& name : params.names) { + RetainPtr obj = parser.ReadNextObject( + /*bAllowNestedArray*/ true, /*bInArray=*/false, /*dwRecursionLevel=*/0); + if (!obj) + break; + +- name_tree.AddValueAndName(std::move(obj), name); ++ name_tree->AddValueAndName(std::move(obj), name); + } + + if (params.delete_backwards) { + for (size_t i = params.count; i > 0; --i) +- name_tree.DeleteValueAndName(i); ++ name_tree->DeleteValueAndName(i); + } else { + for (size_t i = 0; i < params.count; ++i) +- name_tree.DeleteValueAndName(0); ++ name_tree->DeleteValueAndName(0); + } + return 0; + } +diff --git a/testing/fuzzers/pdf_psengine_fuzzer.cc b/testing/fuzzers/pdf_psengine_fuzzer.cc +index d72088d4e..5cc2a7608 100644 +--- a/testing/fuzzers/pdf_psengine_fuzzer.cc ++++ b/testing/fuzzers/pdf_psengine_fuzzer.cc +@@ -1,8 +1,8 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include ++#include + + #include "core/fpdfapi/page/cpdf_psengine.h" + #include "third_party/base/span.h" +diff --git a/testing/fuzzers/pdf_scanlinecompositor_fuzzer.cc b/testing/fuzzers/pdf_scanlinecompositor_fuzzer.cc +index 4b2006838..9c6e43427 100644 +--- a/testing/fuzzers/pdf_scanlinecompositor_fuzzer.cc ++++ b/testing/fuzzers/pdf_scanlinecompositor_fuzzer.cc +@@ -1,20 +1,36 @@ +-// Copyright 2019 The PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include ++#include ++ ++#include "core/fxcrt/fx_safe_types.h" + #include "core/fxge/cfx_cliprgn.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "core/fxge/fx_dib.h" ++#include "core/fxge/dib/fx_dib.h" + #include "testing/fuzzers/pdfium_fuzzer_util.h" +-#include "third_party/base/ptr_util.h" + + namespace { + ++// Some unused formats were removed, and their slots have been filled in ++// `FXDIB_Format::kInvalid` to keep the fuzzer input stable. + constexpr FXDIB_Format kFormat[] = { +- FXDIB_Invalid, FXDIB_1bppRgb, FXDIB_8bppRgb, FXDIB_Rgb, +- FXDIB_Rgb32, FXDIB_1bppMask, FXDIB_8bppMask, FXDIB_8bppRgba, +- FXDIB_Rgba, FXDIB_Argb, FXDIB_1bppCmyk, FXDIB_8bppCmyk, +- FXDIB_Cmyk, FXDIB_8bppCmyka, FXDIB_Cmyka}; ++ FXDIB_Format::kInvalid, ++ FXDIB_Format::k1bppRgb, ++ FXDIB_Format::k8bppRgb, ++ FXDIB_Format::kRgb, ++ FXDIB_Format::kRgb32, ++ FXDIB_Format::k1bppMask, ++ FXDIB_Format::k8bppMask, ++ FXDIB_Format::kInvalid /* Was FXDIB_Format::k8bppRgba */, ++ FXDIB_Format::kInvalid /* Was FXDIB_Format::kRgba */, ++ FXDIB_Format::kArgb, ++ FXDIB_Format::kInvalid /* Was FXDIB_Format::k1bppCmyk */, ++ FXDIB_Format::kInvalid /* Was FXDIB_Format::k8bppCmyk */, ++ FXDIB_Format::kInvalid /* Was FXDIB_Format::kCmyk */, ++ FXDIB_Format::kInvalid /* Was FXDIB_Format::k8bppCmyka */, ++ FXDIB_Format::kInvalid /* Was FXDIB_Format::kCmyka */}; + + } // namespace + +@@ -33,27 +49,35 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + + BlendMode blend_mode = static_cast( + data[28] % (static_cast(BlendMode::kLast) + 1)); +- FXDIB_Format dest_format = kFormat[data[29] % FX_ArraySize(kFormat)]; +- FXDIB_Format src_format = kFormat[data[30] % FX_ArraySize(kFormat)]; ++ FXDIB_Format dest_format = kFormat[data[29] % std::size(kFormat)]; ++ FXDIB_Format src_format = kFormat[data[30] % std::size(kFormat)]; + bool is_clip = !(data[31] % 2); + bool is_rgb_byte_order = !(data[32] % 2); + size -= kParameterSize; + data += kParameterSize; + ++ static constexpr uint32_t kMemLimit = 512000000; // 512 MB ++ static constexpr uint32_t kComponents = 4; ++ FX_SAFE_UINT32 mem = width; ++ mem *= height; ++ mem *= kComponents; ++ if (!mem.IsValid() || mem.ValueOrDie() > kMemLimit) ++ return 0; ++ + auto src_bitmap = pdfium::MakeRetain(); + auto dest_bitmap = pdfium::MakeRetain(); + if (!src_bitmap->Create(width, height, src_format) || + !dest_bitmap->Create(width, height, dest_format)) { + return 0; + } +- if (!src_bitmap->GetBuffer() || !dest_bitmap->GetBuffer()) { ++ if (src_bitmap->GetBuffer().empty() || dest_bitmap->GetBuffer().empty()) { + return 0; + } + + std::unique_ptr clip_rgn; + if (is_clip) +- clip_rgn = pdfium::MakeUnique(width, height); +- if (src_bitmap->IsAlphaMask()) { ++ clip_rgn = std::make_unique(width, height); ++ if (src_bitmap->IsMaskFormat()) { + dest_bitmap->CompositeMask(dest_left, dest_top, width, height, src_bitmap, + argb, src_left, src_top, blend_mode, + clip_rgn.get(), is_rgb_byte_order); +diff --git a/testing/fuzzers/pdf_streamparser_fuzzer.cc b/testing/fuzzers/pdf_streamparser_fuzzer.cc +index 2bbda5e24..e3bd7873b 100644 +--- a/testing/fuzzers/pdf_streamparser_fuzzer.cc ++++ b/testing/fuzzers/pdf_streamparser_fuzzer.cc +@@ -1,9 +1,8 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#include +-#include ++#include + + #include "core/fpdfapi/page/cpdf_streamparser.h" + #include "core/fpdfapi/parser/cpdf_object.h" +diff --git a/testing/fuzzers/pdf_xfa_fdp_fuzzer.cc b/testing/fuzzers/pdf_xfa_fdp_fuzzer.cc +new file mode 100644 +index 000000000..c438c89ae +--- /dev/null ++++ b/testing/fuzzers/pdf_xfa_fdp_fuzzer.cc +@@ -0,0 +1,914 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include ++ ++#include ++#include ++ ++#include "public/fpdf_formfill.h" ++#include "testing/fuzzers/pdf_fuzzer_templates.h" ++#include "testing/fuzzers/pdfium_fuzzer_helper.h" ++#include "third_party/base/containers/adapters.h" ++ ++class PDFiumXFAFuzzer : public PDFiumFuzzerHelper { ++ public: ++ PDFiumXFAFuzzer() = default; ++ ~PDFiumXFAFuzzer() override = default; ++ ++ int GetFormCallbackVersion() const override { return 2; } ++ ++ void SetFdp(FuzzedDataProvider* fdp) { fdp_ = fdp; } ++ ++ // Return false if XFA doesn't load as otherwise we're duplicating the work ++ // done by the non-xfa fuzzer. ++ bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc) override { ++ int form_type = FPDF_GetFormType(doc); ++ if (form_type != FORMTYPE_XFA_FULL && form_type != FORMTYPE_XFA_FOREGROUND) ++ return false; ++ return FPDF_LoadXFA(doc); ++ } ++ ++ void FormActionHandler(FPDF_FORMHANDLE form, ++ FPDF_DOCUMENT doc, ++ FPDF_PAGE page) override { ++ if (!fdp_) { ++ return; ++ } ++ char local_buf[50]; ++ int number_of_calls = fdp_->ConsumeIntegralInRange(0, 250); ++ for (int i = 0; i < number_of_calls; i++) { ++ UserInteraction selector = fdp_->ConsumeEnum(); ++ switch (selector) { ++ case kOnLButtonUp: { ++ FORM_OnLButtonUp(form, page, fdp_->ConsumeIntegral(), ++ fdp_->ConsumeIntegralInRange(-100, 1000), ++ fdp_->ConsumeIntegralInRange(-100, 1000)); ++ break; ++ } ++ case kOnRButtonUp: { ++ FORM_OnRButtonUp(form, page, fdp_->ConsumeIntegral(), ++ fdp_->ConsumeIntegralInRange(-100, 1000), ++ fdp_->ConsumeIntegralInRange(-100, 1000)); ++ break; ++ } ++ case kOnLButtonDown: { ++ FORM_OnLButtonDown(form, page, fdp_->ConsumeIntegral(), ++ fdp_->ConsumeIntegralInRange(-100, 1000), ++ fdp_->ConsumeIntegralInRange(-100, 1000)); ++ break; ++ } ++ case kOnRButtonDown: { ++ FORM_OnRButtonDown(form, page, fdp_->ConsumeIntegral(), ++ fdp_->ConsumeIntegralInRange(-100, 1000), ++ fdp_->ConsumeIntegralInRange(-100, 1000)); ++ break; ++ } ++ case kOnChar: { ++ FORM_OnChar(form, page, fdp_->ConsumeIntegral(), ++ fdp_->ConsumeIntegral()); ++ break; ++ } ++ case kOnKeyDown: { ++ FORM_OnKeyDown(form, page, fdp_->ConsumeIntegral(), ++ fdp_->ConsumeIntegral()); ++ break; ++ } ++ case kOnKeyUp: { ++ FORM_OnKeyUp(form, page, fdp_->ConsumeIntegral(), ++ fdp_->ConsumeIntegral()); ++ break; ++ } ++ case kOnLButtonDoubleClick: { ++ FORM_OnLButtonDoubleClick(form, page, fdp_->ConsumeIntegral(), ++ fdp_->ConsumeIntegral(), ++ fdp_->ConsumeIntegral()); ++ break; ++ } ++ case kOnMouseMove: { ++ FORM_OnMouseMove(form, page, fdp_->ConsumeIntegral(), ++ fdp_->ConsumeIntegral(), ++ fdp_->ConsumeIntegral()); ++ break; ++ } ++ case kOnMouseWheel: { ++ const FS_POINTF point = {fdp_->ConsumeFloatingPoint(), ++ fdp_->ConsumeFloatingPoint()}; ++ FORM_OnMouseWheel(form, page, fdp_->ConsumeIntegral(), &point, ++ fdp_->ConsumeIntegral(), ++ fdp_->ConsumeIntegral()); ++ break; ++ } ++ case kOnFocus: { ++ FORM_OnFocus(form, page, fdp_->ConsumeIntegral(), ++ fdp_->ConsumeIntegral(), ++ fdp_->ConsumeIntegral()); ++ break; ++ } ++ case kUndo: { ++ if (FORM_CanUndo(form, page)) { ++ FORM_Undo(form, page); ++ } ++ break; ++ } ++ case kSelectAllText: { ++ FORM_SelectAllText(form, page); ++ break; ++ } ++ case kRedo: { ++ if (FORM_CanRedo(form, page)) { ++ FORM_Redo(form, page); ++ } ++ break; ++ } ++ case kAnnot: { ++ FPDF_ANNOTATION annot = nullptr; ++ int page_index = -2; ++ FORM_GetFocusedAnnot(form, &page_index, &annot); ++ if (annot) { ++ FORM_SetFocusedAnnot(form, annot); ++ } ++ break; ++ } ++ case kSetIndexSelected: { ++ FORM_SetIndexSelected(form, page, fdp_->ConsumeIntegral(), ++ fdp_->ConsumeBool()); ++ break; ++ } ++ case kIsIndexSelected: { ++ FORM_IsIndexSelected(form, page, fdp_->ConsumeIntegral()); ++ break; ++ } ++ case kHasFormFieldAtPoint: { ++ FPDFPage_HasFormFieldAtPoint(form, page, fdp_->ConsumeIntegral(), ++ fdp_->ConsumeIntegral()); ++ break; ++ } ++ case kFormFieldZOrderAtPoint: { ++ FPDFPage_FormFieldZOrderAtPoint(form, page, ++ fdp_->ConsumeIntegral(), ++ fdp_->ConsumeIntegral()); ++ break; ++ } ++ case kGetSelectedText: { ++ FORM_GetSelectedText(form, page, local_buf, sizeof(local_buf)); ++ break; ++ } ++ case kGetFocusedText: { ++ FORM_GetFocusedText(form, page, local_buf, sizeof(local_buf)); ++ break; ++ } ++ default: { ++ break; ++ } ++ } ++ } ++ } ++ ++ private: ++ enum UserInteraction { ++ kOnLButtonUp = 0, ++ kOnRButtonUp, ++ kOnLButtonDown, ++ kOnRButtonDown, ++ kOnChar, ++ kOnKeyDown, ++ kOnKeyUp, ++ kOnLButtonDoubleClick, ++ kOnMouseMove, ++ kOnMouseWheel, ++ kOnFocus, ++ kUndo, ++ kSelectAllText, ++ kRedo, ++ kAnnot, ++ kSetIndexSelected, ++ kIsIndexSelected, ++ kHasFormFieldAtPoint, ++ kFormFieldZOrderAtPoint, ++ kGetSelectedText, ++ kGetFocusedText, ++ kMaxValue = kGetFocusedText ++ }; ++ FuzzedDataProvider* fdp_ = nullptr; ++}; ++ ++// Possible names of an XFA FormCalc script function ++std::string GenXfaFormCalcScriptFuncName(FuzzedDataProvider* data_provider) { ++ static const char* const kXfaScriptFuncs[] = { ++ "Abs", "Apr", "At", "Avg", "Ceil", ++ "Choose", "Concat", "Count", "Cterm", "Date", ++ "Date2Num", "DateFmt", "Decode", "Encode", "Eval", ++ "Exists", "Floor", "Format", "FV", "Get", ++ "HasValue", "If", "Ipmt", "IsoDate2Num", "IsoTime2Num", ++ "Left", "Len", "LocalDateFmt", "LocalTimeFmt", "Lower", ++ "Ltrim", "Max", "Min", "Mod", "NPV", ++ "Num2Date", "Num2GMTime", "Num2Time", "Oneof", "Parse", ++ "Pmt", "Post", "PPmt", "Put", "PV", ++ "Rate", "Ref", "Replace", "Right", "Round", ++ "Rtrim", "Space", "Str", "Stuff", "Substr", ++ "Sum", "Term", "Time", "Time2Num", "TimeFmt", ++ "Translate", "UnitType", "UnitValue", "Upper", "Uuid", ++ "Within", "WordNum", ++ }; ++ ++ size_t elem_selector = data_provider->ConsumeIntegralInRange( ++ 0, std::size(kXfaScriptFuncs) - 1); ++ return kXfaScriptFuncs[elem_selector]; ++} ++ ++std::string MaybeQuote(FuzzedDataProvider* data_provider, std::string body) { ++ if (data_provider->ConsumeIntegralInRange(0, 100) < 20) { ++ return "\"" + body + "\""; ++ } ++ return body; ++} ++ ++// Possible arguments to a XFA script function ++std::string GenXfaScriptParam(FuzzedDataProvider* data_provider) { ++ static const char* const kXfaFuncParams[] = { ++ "$", ++ "-0", ++ "04/13/2019", ++ ".05", ++ "-1", ++ "1", ++ " 1 | 0", ++ "10 * 10 * 10 * 9 * 123", ++ "1024", ++ "10 * a + 9", ++ "1.2131", ++ "[1,2,3]", ++ "%123", ++ "[1,2,3][0]", ++ "123124", ++ "123342123", ++ "13:13:13", ++ "13:13:13 GMT", ++ "19960315T20:20:20", ++ "1 and 1", ++ "1 and 2", ++ "2", ++ "20000201", ++ "2009-06-01T13:45:30", ++ "2009-06-15T01:45:30", ++ "2009-06-15T13:45:30-07:00", ++ "2009-06-15T13:45:30.5275000", ++ " 2 < 3 + 1", ++ "2 + 3 + 9", ++ "3", ++ "3 * 1", ++ "3 -9", ++ "5 < 5", ++ "-99", ++ "99", ++ "9999999", ++ "99999999999", ++ "A", ++ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", ++ "\xc3\x81\xc3\x82\xc3\x83\xc3\x84\xc3\x85\xc3\x86", ++ "", ++ "Â", ++ "ÆÁÂÁ", ++ "Amount[*]", ++ "~!@#$%^&*()_+", ++ "&|", ++ "&apos", ++ "apr", ++ "april", ++ "B", ++ "
", ++ "C", ++ "de_DE", ++ "es_ES", ++ "feb", ++ "febuary", ++ "HH:MM:SS", ++ "", ++ "html", ++ "HTML", ++ "jan", ++ "january", ++ "json", ++ "lkdjfglsdkfgj", ++ "mar", ++ "march", ++ "name[0]", ++ "name1", ++ "name2", ++ "name3", ++ "name4", ++ "name[*].numAmount", ++ """, ++ "Space", ++ "Str", ++ "url", ++ "xhtml", ++ "xml", ++ "XML"", ++ }; ++ ++ size_t elem_selector = data_provider->ConsumeIntegralInRange( ++ 0, std::size(kXfaFuncParams) - 1); ++ return MaybeQuote(data_provider, kXfaFuncParams[elem_selector]); ++} ++ ++// Possible XFA tags ++std::string GenXfaTag(FuzzedDataProvider* data_provider) { ++ static const char* const kXfaElemTags[] = { ++ "accessibleContent", ++ "acrobat", ++ "acrobat", ++ "acrobat7", ++ "ADBE_JSConsole", ++ "ADBE_JSDebugger", ++ "addSilentPrint", ++ "addViewerPreferences", ++ "adjustData", ++ "adobeExtensionLevel", ++ "agent", ++ "alwaysEmbed", ++ "amd", ++ "appearanceFilter", ++ "arc", ++ "area", ++ "assist", ++ "attributes", ++ "autoSave", ++ "barcode", ++ "base", ++ "batchOutput", ++ "behaviorOverride", ++ "bind", ++ "bindItems", ++ "bookend", ++ "boolean", ++ "border", ++ "break", ++ "breakAfter", ++ "breakBefore", ++ "button", ++ "cache", ++ "calculate", ++ "calendarSymbols", ++ "caption", ++ "certificate", ++ "certificates", ++ "change", ++ "checkButton", ++ "choiceList", ++ "color", ++ "comb", ++ "command", ++ "common", ++ "compress", ++ "compression", ++ "compressLogicalStructure", ++ "compressObjectStream", ++ "config", ++ "config", ++ "conformance", ++ "connect", ++ "connectionSet", ++ "connectString", ++ "contentArea", ++ "contentCopy", ++ "copies", ++ "corner", ++ "creator", ++ "currencySymbol", ++ "currencySymbols", ++ "currentPage", ++ "data", ++ "dataGroup", ++ "dataModel", ++ "dataValue", ++ "dataWindow", ++ "date", ++ "datePattern", ++ "datePatterns", ++ "dateTime", ++ "dateTimeEdit", ++ "dateTimeSymbols", ++ "day", ++ "dayNames", ++ "debug", ++ "decimal", ++ "defaultTypeface", ++ "defaultUi", ++ "delete", ++ "delta", ++ "deltas", ++ "desc", ++ "destination", ++ "digestMethod", ++ "digestMethods", ++ "documentAssembly", ++ "draw", ++ "driver", ++ "dSigData", ++ "duplexOption", ++ "dynamicRender", ++ "edge", ++ "effectiveInputPolicy", ++ "effectiveOutputPolicy", ++ "embed", ++ "encoding", ++ "encodings", ++ "encrypt", ++ "encryption", ++ "encryptionLevel", ++ "encryptionMethod", ++ "encryptionMethods", ++ "enforce", ++ "equate", ++ "equateRange", ++ "era", ++ "eraNames", ++ "event", ++ "eventPseudoModel", ++ "exclGroup", ++ "exclude", ++ "excludeNS", ++ "exData", ++ "execute", ++ "exObject", ++ "extras", ++ "field", ++ "fill", ++ "filter", ++ "flipLabel", ++ "float", ++ "font", ++ "fontInfo", ++ "form", ++ "format", ++ "formFieldFilling", ++ "groupParent", ++ "handler", ++ "hostPseudoModel", ++ "hyphenation", ++ "ifEmpty", ++ "image", ++ "imageEdit", ++ "includeXDPContent", ++ "incrementalLoad", ++ "incrementalMerge", ++ "insert", ++ "instanceManager", ++ "integer", ++ "interactive", ++ "issuers", ++ "items", ++ "jog", ++ "keep", ++ "keyUsage", ++ "labelPrinter", ++ "layout", ++ "layoutPseudoModel", ++ "level", ++ "line", ++ "linear", ++ "linearized", ++ "list", ++ "locale", ++ "localeSet", ++ "lockDocument", ++ "log", ++ "logPseudoModel", ++ "manifest", ++ "map", ++ "margin", ++ "mdp", ++ "medium", ++ "mediumInfo", ++ "meridiem", ++ "meridiemNames", ++ "message", ++ "messaging", ++ "mode", ++ "modifyAnnots", ++ "month", ++ "monthNames", ++ "msgId", ++ "nameAttr", ++ "neverEmbed", ++ "numberOfCopies", ++ "numberPattern", ++ "numberPatterns", ++ "numberSymbol", ++ "numberSymbols", ++ "numericEdit", ++ "object", ++ "occur", ++ "oid", ++ "oids", ++ "openAction", ++ "operation", ++ "output", ++ "outputBin", ++ "outputXSL", ++ "overflow", ++ "overprint", ++ "packet", ++ "packets", ++ "pageArea", ++ "pageOffset", ++ "pageRange", ++ "pageSet", ++ "pagination", ++ "paginationOverride", ++ "para", ++ "part", ++ "password", ++ "passwordEdit", ++ "pattern", ++ "pcl", ++ "pdf", ++ "pdfa", ++ "permissions", ++ "pickTrayByPDFSize", ++ "picture", ++ "plaintextMetadata", ++ "presence", ++ "present", ++ "present", ++ "print", ++ "printerName", ++ "printHighQuality", ++ "printScaling", ++ "producer", ++ "proto", ++ "ps", ++ "psMap", ++ "query", ++ "radial", ++ "range", ++ "reason", ++ "reasons", ++ "record", ++ "recordSet", ++ "rectangle", ++ "ref", ++ "relevant", ++ "rename", ++ "renderPolicy", ++ "rootElement", ++ "runScripts", ++ "script", ++ "scriptModel", ++ "select", ++ "setProperty", ++ "severity", ++ "signature", ++ "signatureProperties", ++ "signaturePseudoModel", ++ "signData", ++ "signing", ++ "silentPrint", ++ "soapAction", ++ "soapAddress", ++ "solid", ++ "source", ++ "sourceSet", ++ "speak", ++ "staple", ++ "startNode", ++ "startPage", ++ "stipple", ++ "subform", ++ "subform", ++ "subformSet", ++ "subjectDN", ++ "subjectDNs", ++ "submit", ++ "submitFormat", ++ "submitUrl", ++ "subsetBelow", ++ "suppressBanner", ++ "tagged", ++ "template", ++ "template", ++ "templateCache", ++ "#text", ++ "text", ++ "textedit", ++ "textEdit", ++ "threshold", ++ "time", ++ "timePattern", ++ "timePatterns", ++ "timeStamp", ++ "to", ++ "toolTip", ++ "trace", ++ "transform", ++ "traversal", ++ "traverse", ++ "treeList", ++ "type", ++ "typeface", ++ "typefaces", ++ "ui", ++ "update", ++ "uri", ++ "user", ++ "validate", ++ "validate", ++ "validateApprovalSignatures", ++ "validationMessaging", ++ "value", ++ "variables", ++ "version", ++ "versionControl", ++ "viewerPreferences", ++ "webClient", ++ "whitespace", ++ "window", ++ "wsdlAddress", ++ "wsdlConnection", ++ "xdc", ++ "xdp", ++ "xfa", ++ "#xHTML", ++ "#xml", ++ "xmlConnection", ++ "xsdConnection", ++ "xsl", ++ "zpl", ++ }; ++ ++ size_t elem_selector = data_provider->ConsumeIntegralInRange( ++ 0, std::size(kXfaElemTags) - 1); ++ return kXfaElemTags[elem_selector]; ++} ++ ++// Possible XFA attributes values ++std::string GenXfaTagValue(FuzzedDataProvider* data_provider) { ++ static const char* const kXfaTagVals[] = { ++ "0", "0pt", "-1", ++ "123", "1pt", "203.2mm", ++ "22.1404mm", "255", "256", ++ "321", "5431.21mm", "6.35mm", ++ "8in", "8pt", "application/x-javascript", ++ "bold", "bold", "change", ++ "click", "consumeData", "docReady", ++ "en_US", "form1", "initialize", ++ "italic", "middle", "name2", ++ "name3", "name4", "name5", ++ "onEnter", "Page1", "RadioList[0]", ++ "subform_1", "tb", "Verdana", ++ }; ++ ++ size_t elem_selector = data_provider->ConsumeIntegralInRange( ++ 0, std::size(kXfaTagVals) - 1); ++ return MaybeQuote(data_provider, kXfaTagVals[elem_selector]); ++} ++ ++// possible XFA attributes ++std::string GenXfaTagName(FuzzedDataProvider* data_provider) { ++ static const char* const kXfaTagNames[] = { ++ "activity", "baselineShift", ++ "contentType", "h", ++ "id", "layout", ++ "layout", "leftInset", ++ "locale", "long", ++ "marginLeft", "marginRight", ++ "marginRight", "mergeMode", ++ "name", "ref", ++ "scriptTest", "short", ++ "size", "spaceAbove", ++ "spaceBelow", "startNew", ++ "stock", "textIndent", ++ "timeStamp", "typeface", ++ "uuid", "vAlign", ++ "value", "w", ++ "weight", "x", ++ "y", ++ }; ++ size_t elem_selector = data_provider->ConsumeIntegralInRange( ++ 0, std::size(kXfaTagNames) - 1); ++ return kXfaTagNames[elem_selector]; ++} ++ ++// Will create a simple XFA FormCalc script that calls a single function. ++std::string GenXfaFormCalcScript(FuzzedDataProvider* data_provider) { ++ std::string xfa_string = GenXfaFormCalcScriptFuncName(data_provider); ++ xfa_string += "("; ++ ++ // Generate parameters ++ size_t num_params = data_provider->ConsumeIntegralInRange(0, 3); ++ for (size_t i = 0; i < num_params; i++) { ++ if (i != 0) { ++ xfa_string += ","; ++ } ++ xfa_string += GenXfaScriptParam(data_provider); ++ } ++ xfa_string += ")"; ++ return xfa_string; ++} ++ ++// XFA Javascript logic ++std::string GenXfaName(FuzzedDataProvider* data_provider) { ++ return "name" + std::to_string(data_provider->ConsumeIntegralInRange(0, 25)); ++} ++ ++std::string GetXfaJSPrimitiveType(FuzzedDataProvider* data_provider) { ++ return GenXfaScriptParam(data_provider); ++} ++ ++std::string GenXfaJSRValue(FuzzedDataProvider* data_provider) { ++ if (data_provider->ConsumeBool()) { ++ return GenXfaScriptParam(data_provider); ++ } ++ ++ std::string xfa_string; ++ if (data_provider->ConsumeBool()) { ++ xfa_string += "xfa.form."; ++ } ++ ++ // Handle the possibility of nested names ++ size_t num_nests = data_provider->ConsumeIntegralInRange(1, 3); ++ for (size_t i = 0; i < num_nests; i++) { ++ if (i != 0) { ++ xfa_string += "."; ++ } ++ xfa_string += GenXfaName(data_provider); ++ } ++ return MaybeQuote(data_provider, xfa_string); ++} ++ ++std::string GenXfaJSAssignment(FuzzedDataProvider* data_provider) { ++ return GenXfaName(data_provider) + " = " + GenXfaJSRValue(data_provider); ++} ++ ++std::string GenXfaJSMethodCall(FuzzedDataProvider* data_provider) { ++ static const char* const kXfaJSFuncs[] = { ++ "addItem", ++ "boundItem", ++ "clearItems", ++ "deleteItem", ++ "execCalculate", ++ "execEvent", ++ "execInitialize", ++ "execValidate", ++ "getDisplayItem", ++ "getItemState", ++ "getSaveItem", ++ "exec.form.formNodes", ++ "exec.form.recalculate", ++ "setItemState", ++ "xfa.container.getDelta", ++ "xfa.container.getDeltas", ++ "xfa.event.emit", ++ "xfa.event.reset", ++ "xfa.form.execCalculat", ++ "xfa.form.execInitialize", ++ "xfa.form.execValidate", ++ "xfa.form.remerge", ++ "xfa.host.beep", ++ "xfa.host.documentCountInBatch", ++ "xfa.host.documentInBatch", ++ "xfa.host.exportData", ++ "xfa.host.getFocus", ++ "xfa.host.gotoURL", ++ "xfa.host.importData", ++ "xfa.host.messageBox", ++ "xfa.host.openList", ++ "xfa.host.pageDown", ++ "xfa.host.pageUp", ++ "xfa.host.print", ++ "xfa.host.resetData", ++ "xfa.host.setFocus", ++ "xfa.host.response", ++ "xfa.resolveNode", ++ }; ++ ++ std::string xfa_string = data_provider->PickValueInArray(kXfaJSFuncs); ++ xfa_string += "("; ++ ++ // Get the params ++ size_t param_count = data_provider->ConsumeIntegralInRange(0, 3); ++ for (size_t i = 0; i < param_count; i++) { ++ if (i != 0) { ++ xfa_string += ","; ++ } ++ xfa_string += GenXfaJSRValue(data_provider); ++ } ++ xfa_string += ")"; ++ return xfa_string; ++} ++ ++// This is a simple generator of xfa-based javascript. The function creates ++// simple javascript statements that are related to XFA logic and the goal is ++// not to create fully-fleged javascript programs but rather use simple ++// statements to ensure XFA code is covered. ++enum XFAJSStatement { ++ kAssignment = 0, ++ kJSMethodCall, ++ kJSObjectCall, ++ kMaxValue = kJSObjectCall ++}; ++ ++std::string GenXfaJSScript(FuzzedDataProvider* data_provider) { ++ std::string xfa_string; ++ ++ size_t num_stmts = data_provider->ConsumeIntegralInRange(1, 10); ++ for (size_t i = 0; i < num_stmts; i++) { ++ XFAJSStatement stmt = data_provider->ConsumeEnum(); ++ switch (stmt) { ++ case kAssignment: ++ xfa_string += GenXfaJSAssignment(data_provider); ++ break; ++ case kJSMethodCall: ++ xfa_string += GenXfaJSMethodCall(data_provider); ++ break; ++ case kJSObjectCall: ++ xfa_string += GenXfaName(data_provider); ++ xfa_string += "."; ++ xfa_string += GenXfaJSMethodCall(data_provider); ++ break; ++ } ++ xfa_string += ";\n"; ++ } ++ return xfa_string; ++} ++ ++std::string GenXfacript(FuzzedDataProvider* data_provider) { ++ // Determine if this should be a FormCalc script or Javascript, 50/50 chance ++ // for each. ++ if (data_provider->ConsumeBool()) { ++ return GenXfaFormCalcScript(data_provider); ++ } ++ return GenXfaJSScript(data_provider); ++} ++ ++// Will create a single XFA attributes, with both lhs and rhs. ++std::string getXfaElemAttributes(FuzzedDataProvider* data_provider) { ++ // Generate a set of tags, and a set of values for the tags. ++ return GenXfaTagName(data_provider) + "=" + GenXfaTagValue(data_provider); ++} ++ ++// Creates an XFA structure wrapped in "; ++ ++ // One stack iteration ++ int stack_iterations = data_provider->ConsumeIntegralInRange(1, 3); ++ for (int si = 0; si < stack_iterations; si++) { ++ int elem_count = data_provider->ConsumeIntegralInRange(1, 6); ++ std::vector xml_stack; ++ xml_stack.reserve(elem_count); ++ for (int i = 0; i < elem_count; i++) { ++ std::string tag = GenXfaTag(data_provider); ++ xfa_string += "<" + tag; ++ ++ // in 30% of cases, add attributes ++ if (data_provider->ConsumeIntegralInRange(1, 100) > 70) { ++ size_t attribute_count = data_provider->ConsumeIntegralInRange(1, 5); ++ for (; 0 < attribute_count; attribute_count--) { ++ xfa_string += " " + getXfaElemAttributes(data_provider); ++ } ++ } ++ xfa_string += ">"; ++ ++ // If needed, add a body to the tag ++ if (tag == "script") { ++ xfa_string += GenXfacript(data_provider); ++ } ++ ++ // Push the tag to the stack so we can close it when done ++ xml_stack.push_back(tag); ++ } ++ for (const std::string& tag : pdfium::base::Reversed(xml_stack)) { ++ xfa_string += ""; ++ } ++ } ++ xfa_string += ""; ++ return xfa_string; ++} ++ ++extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { ++ FuzzedDataProvider data_provider(data, size); ++ std::string xfa_string = GenXfaTree(&data_provider); ++ ++ // Add 1 for newline before endstream. ++ std::string xfa_stream_len = std::to_string(xfa_string.size() + 1); ++ ++ // Compose the fuzzer ++ std::string xfa_final_str = std::string(kSimplePdfTemplate); ++ xfa_final_str.replace(xfa_final_str.find("$1"), 2, xfa_stream_len); ++ xfa_final_str.replace(xfa_final_str.find("$2"), 2, xfa_string); ++ ++#ifdef PDFIUM_FUZZER_DUMP ++ for (size_t i = 0; i < xfa_final_str.size(); i++) { ++ putc(xfa_final_str[i], stdout); ++ } ++#endif ++ ++ PDFiumXFAFuzzer fuzzer; ++ fuzzer.SetFdp(&data_provider); ++ fuzzer.RenderPdf(xfa_final_str.c_str(), xfa_final_str.size()); ++ return 0; ++} +diff --git a/testing/fuzzers/pdf_xfa_raw_fuzzer.cc b/testing/fuzzers/pdf_xfa_raw_fuzzer.cc +new file mode 100644 +index 000000000..8dce02f4c +--- /dev/null ++++ b/testing/fuzzers/pdf_xfa_raw_fuzzer.cc +@@ -0,0 +1,101 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include ++ ++#include ++#include ++ ++#include "public/fpdf_formfill.h" ++#include "testing/fuzzers/pdf_fuzzer_templates.h" ++#include "testing/fuzzers/pdfium_fuzzer_helper.h" ++ ++class PDFiumXFAFuzzer : public PDFiumFuzzerHelper { ++ public: ++ PDFiumXFAFuzzer() = default; ++ ~PDFiumXFAFuzzer() override = default; ++ ++ int GetFormCallbackVersion() const override { return 2; } ++ ++ // Return false if XFA doesn't load as otherwise we're duplicating the work ++ // done by the non-xfa fuzzer. ++ bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc) override { ++ int form_type = FPDF_GetFormType(doc); ++ if (form_type != FORMTYPE_XFA_FULL && form_type != FORMTYPE_XFA_FOREGROUND) ++ return false; ++ return FPDF_LoadXFA(doc); ++ } ++}; ++ ++bool IsValidForFuzzing(const uint8_t* data, size_t size) { ++ if (size > 2048) { ++ return false; ++ } ++ ++ const char* ptr = reinterpret_cast(data); ++ bool is_open = false; ++ size_t tag_size = 0; ++ for (size_t i = 0; i < size; i++) { ++ if (!std::isspace(ptr[i]) && !std::isprint(ptr[i])) { ++ return false; ++ } ++ ++ // We do not want any script tags. The reason is this fuzzer ++ // should avoid exploring v8 code. Avoiding anything with "script" ++ // is an over-approximation, in that some inputs may contain "script" ++ // and still be a valid fuzz-case. However, this over-approximation is ++ // used to enforce strict constraints and avoid cases where whitespace ++ // may play a role, or other tags, e.g. "Javascript" will end up triggering ++ // large explorations of v8 code. The alternative we considered were ++ // "') { ++ if (!is_open || tag_size == 0) { ++ return false; ++ } ++ is_open = false; ++ } else if (is_open) { ++ tag_size++; ++ } ++ } ++ // we must close the last bracket. ++ return !is_open; ++} ++ ++extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { ++ // Filter the string to reduce the state space exploration. ++ if (!IsValidForFuzzing(data, size)) { ++ return 0; ++ } ++ std::string xfa_string = ""; ++ xfa_string += std::string(reinterpret_cast(data), size); ++ xfa_string += ""; ++ ++ // Add 1 for newline before endstream. ++ std::string xfa_stream_len = std::to_string(xfa_string.size() + 1); ++ ++ // Compose the fuzzer ++ std::string xfa_final_str = std::string(kSimplePdfTemplate); ++ xfa_final_str.replace(xfa_final_str.find("$1"), 2, xfa_stream_len); ++ xfa_final_str.replace(xfa_final_str.find("$2"), 2, xfa_string); ++ ++#ifdef PDFIUM_FUZZER_DUMP ++ for (size_t i = 0; i < xfa_final_str.size(); i++) { ++ putc(xfa_final_str[i], stdout); ++ } ++#endif ++ ++ PDFiumXFAFuzzer fuzzer; ++ fuzzer.RenderPdf(xfa_final_str.c_str(), xfa_final_str.size()); ++ return 0; ++} +diff --git a/testing/fuzzers/pdf_xfa_xdp_fdp_fuzzer.cc b/testing/fuzzers/pdf_xfa_xdp_fdp_fuzzer.cc +new file mode 100644 +index 000000000..8917ce700 +--- /dev/null ++++ b/testing/fuzzers/pdf_xfa_xdp_fdp_fuzzer.cc +@@ -0,0 +1,206 @@ ++// Copyright 2021 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include ++ ++#include ++ ++#include "public/fpdf_formfill.h" ++#include "testing/fuzzers/pdf_fuzzer_templates.h" ++#include "testing/fuzzers/pdfium_fuzzer_helper.h" ++ ++class PDFiumXDPFuzzer : public PDFiumFuzzerHelper { ++ public: ++ PDFiumXDPFuzzer() = default; ++ ~PDFiumXDPFuzzer() override = default; ++ ++ int GetFormCallbackVersion() const override { return 2; } ++ ++ bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc) override { ++ int form_type = FPDF_GetFormType(doc); ++ if (form_type != FORMTYPE_XFA_FULL && form_type != FORMTYPE_XFA_FOREGROUND) ++ return false; ++ return FPDF_LoadXFA(doc); ++ } ++}; ++ ++struct Tag { ++ const char* tag_name; ++ const char* tag_start; ++ const char* tag_end; ++}; ++ ++const Tag kTagData[]{ ++ {.tag_name = "config", ++ .tag_start = ++ R""()"", ++ .tag_end = ""}, ++ {.tag_name = "template", ++ .tag_start = ++ R""("}, ++ {.tag_name = "sourceSet", ++ .tag_start = ++ R""()"", ++ .tag_end = ""}, ++ {.tag_name = "localeSet", ++ .tag_start = ++ R""()"", ++ .tag_end = ""}, ++ {.tag_name = "dataSet", ++ .tag_start = ++ R""()"", ++ .tag_end = ""}, ++ {.tag_name = "connectionSet", ++ .tag_start = ++ R""()"", ++ .tag_end = ""}, ++ {.tag_name = "xdc", ++ .tag_start = ++ R""()"", ++ .tag_end = ""}, ++ {.tag_name = "signature", ++ .tag_start = R""()"", ++ .tag_end = ""}, ++ {.tag_name = "stylesheet", ++ .tag_start = ++ R""()"", ++ .tag_end = ""}, ++ {.tag_name = "xfdf", ++ .tag_start = ++ R""()"", ++ .tag_end = ""}, ++ {.tag_name = "xmpmeta", ++ .tag_start = ++ R""()"", ++ .tag_end = ""}}; ++ ++std::string CreateObject(int obj_num, const std::string& body) { ++ std::string obj_template = R""($1 0 obj ++$2 ++endobj ++)""; ++ ++ obj_template.replace(obj_template.find("$1"), 2, std::to_string(obj_num)); ++ obj_template.replace(obj_template.find("$2"), 2, body); ++ return obj_template; ++} ++ ++std::string CreateStreamObject(int obj_num, const std::string& body) { ++ std::string obj_template = R""($1 0 obj ++<> ++stream ++$3 ++endstream ++endobj ++)""; ++ ++ obj_template.replace(obj_template.find("$1"), 2, std::to_string(obj_num)); ++ obj_template.replace(obj_template.find("$2"), 2, ++ std::to_string(body.size() + 1)); ++ obj_template.replace(obj_template.find("$3"), 2, body); ++ ++ return obj_template; ++} ++ ++std::string GenXrefEntry(size_t offset) { ++ return std::string(10 - std::to_string(offset).size(), '0') + ++ std::to_string(offset) + " 00000 n\n"; ++} ++ ++std::string GenTagBody(const Tag& tag, FuzzedDataProvider* data_provider) { ++ std::string tag_content = data_provider->ConsumeRandomLengthString(); ++ return tag.tag_start + tag_content + tag.tag_end; ++} ++ ++std::string GenXDPPdfFile(FuzzedDataProvider* data_provider) { ++ std::vector pdf_objects; ++ std::string pdf_header = ++ std::string(reinterpret_cast(kSimplePdfHeader), ++ sizeof(kSimplePdfHeader)); ++ ++ pdf_objects.push_back(CreateObject(1, kCatalog)); ++ ++ std::string xfa_obj = kSimpleXfaObjWrapper; ++ Tag tag1 = data_provider->PickValueInArray(kTagData); ++ Tag tag2 = data_provider->PickValueInArray(kTagData); ++ Tag tag3 = data_provider->PickValueInArray(kTagData); ++ xfa_obj.replace(xfa_obj.find("$1"), 2, tag1.tag_name); ++ xfa_obj.replace(xfa_obj.find("$2"), 2, tag2.tag_name); ++ xfa_obj.replace(xfa_obj.find("$3"), 2, tag3.tag_name); ++ pdf_objects.push_back(CreateObject(2, xfa_obj)); ++ pdf_objects.push_back(CreateObject(3, kSimplePagesObj)); ++ pdf_objects.push_back(CreateObject(4, kSimplePageObj)); ++ ++ // preamble ++ pdf_objects.push_back(CreateStreamObject(5, kSimplePreamble)); ++ ++ // The three XFA tags ++ pdf_objects.push_back(CreateStreamObject(6, GenTagBody(tag1, data_provider))); ++ pdf_objects.push_back(CreateStreamObject(7, GenTagBody(tag2, data_provider))); ++ pdf_objects.push_back(CreateStreamObject(8, GenTagBody(tag3, data_provider))); ++ ++ // postamble ++ pdf_objects.push_back(CreateStreamObject(9, kSimplePostamble)); ++ ++ // Create the xref table ++ std::string xref = R""(xref ++0 10 ++0000000000 65535 f ++)""; ++ ++ // Add xref entries ++ size_t curr_offset = pdf_header.size(); ++ for (const auto& ostr : pdf_objects) { ++ xref += GenXrefEntry(curr_offset); ++ curr_offset += ostr.size(); ++ } ++ ++ std::string footer = R""(trailer ++<> ++startxref ++$1 ++%%EOF)""; ++ footer.replace(footer.find("$1"), 2, std::to_string(curr_offset)); ++ ++ std::string pdf_core; ++ for (const auto& ostr : pdf_objects) { ++ pdf_core += ostr; ++ } ++ ++ // Return the full PDF ++ return pdf_header + pdf_core + xref + footer; ++} ++ ++bool IsValidForFuzzing(const uint8_t* data, size_t size) { ++ if (size > 2048) { ++ return false; ++ } ++ const char* ptr = reinterpret_cast(data); ++ for (size_t i = 0; i < size; i++) { ++ if (!std::isspace(ptr[i]) && !std::isprint(ptr[i])) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { ++ if (!IsValidForFuzzing(data, size)) { ++ return 0; ++ } ++ ++ FuzzedDataProvider data_provider(data, size); ++ std::string xfa_final_str = GenXDPPdfFile(&data_provider); ++ ++#ifdef PDFIUM_FUZZER_DUMP ++ for (size_t i = 0; i < xfa_final_str.size(); i++) { ++ putc(xfa_final_str[i], stdout); ++ } ++#endif ++ ++ PDFiumXDPFuzzer fuzzer; ++ fuzzer.RenderPdf(xfa_final_str.c_str(), xfa_final_str.size()); ++ return 0; ++} +diff --git a/testing/fuzzers/pdf_xml_fuzzer.cc b/testing/fuzzers/pdf_xml_fuzzer.cc +index e858f5be2..31ab6d76a 100644 +--- a/testing/fuzzers/pdf_xml_fuzzer.cc ++++ b/testing/fuzzers/pdf_xml_fuzzer.cc +@@ -1,4 +1,4 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,7 +6,7 @@ + #include + #include + +-#include "core/fxcrt/cfx_readonlymemorystream.h" ++#include "core/fxcrt/cfx_read_only_span_stream.h" + #include "core/fxcrt/fx_safe_types.h" + #include "core/fxcrt/fx_system.h" + #include "core/fxcrt/xml/cfx_xmldocument.h" +@@ -19,8 +19,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (!safe_size.IsValid()) + return 0; + +- auto stream = pdfium::MakeRetain( +- pdfium::make_span(data, size)); ++ auto stream = ++ pdfium::MakeRetain(pdfium::make_span(data, size)); + CFX_XMLParser parser(stream); + std::unique_ptr doc = parser.Parse(); + if (!doc || !doc->GetRoot()) +diff --git a/testing/fuzzers/pdfium_fuzzer.cc b/testing/fuzzers/pdfium_fuzzer.cc +index dc15378ac..7e25fedb0 100644 +--- a/testing/fuzzers/pdfium_fuzzer.cc ++++ b/testing/fuzzers/pdfium_fuzzer.cc +@@ -1,4 +1,4 @@ +-// Copyright 2017 The Chromium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,6 +6,12 @@ + + #include "testing/fuzzers/pdfium_fuzzer_helper.h" + ++#if defined(BUILD_WITH_CHROMIUM) ++#include "base/memory/discardable_memory_allocator.h" ++#include "base/no_destructor.h" ++#include "base/test/test_discardable_memory_allocator.h" ++#endif ++ + class PDFiumFuzzer : public PDFiumFuzzerHelper { + public: + PDFiumFuzzer() = default; +@@ -15,6 +21,12 @@ class PDFiumFuzzer : public PDFiumFuzzerHelper { + }; + + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { ++#if defined(BUILD_WITH_CHROMIUM) ++ static base::NoDestructor ++ test_memory_allocator; ++ base::DiscardableMemoryAllocator::SetInstance(test_memory_allocator.get()); ++#endif ++ + PDFiumFuzzer fuzzer; + fuzzer.RenderPdf(reinterpret_cast(data), size); + return 0; +diff --git a/testing/fuzzers/pdfium_fuzzer_helper.cc b/testing/fuzzers/pdfium_fuzzer_helper.cc +index 266666df0..f011b18b3 100644 +--- a/testing/fuzzers/pdfium_fuzzer_helper.cc ++++ b/testing/fuzzers/pdfium_fuzzer_helper.cc +@@ -1,19 +1,16 @@ +-// Copyright 2017 The Chromium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "testing/fuzzers/pdfium_fuzzer_helper.h" + + #include +-#include +- + #include + #include + #include + #include + #include + +-#include + #include + #include + #include +@@ -23,6 +20,7 @@ + #include "public/fpdf_dataavail.h" + #include "public/fpdf_ext.h" + #include "public/fpdf_text.h" ++#include "third_party/base/notreached.h" + #include "third_party/base/span.h" + + namespace { +@@ -95,7 +93,7 @@ void Add_Segment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {} + std::pair GetRenderingAndFormFlagFromData(const char* data, + size_t len) { + std::string data_str = std::string(data, len); +- std::size_t data_hash = std::hash()(data_str); ++ size_t data_hash = std::hash()(data_str); + + // The largest flag value is 0x4FFF, so just take 16 bits from |data_hash| at + // a time. +@@ -218,6 +216,8 @@ bool PDFiumFuzzerHelper::RenderPage(FPDF_DOCUMENT doc, + FORM_OnAfterLoadPage(page.get(), form); + FORM_DoPageAAction(page.get(), form, FPDFPAGE_AACTION_OPEN); + ++ FormActionHandler(form, doc, page.get()); ++ + const double scale = 1.0; + int width = static_cast(FPDF_GetPageWidthF(page.get()) * scale); + int height = static_cast(FPDF_GetPageHeightF(page.get()) * scale); +diff --git a/testing/fuzzers/pdfium_fuzzer_helper.h b/testing/fuzzers/pdfium_fuzzer_helper.h +index af14941d9..900f55d10 100644 +--- a/testing/fuzzers/pdfium_fuzzer_helper.h ++++ b/testing/fuzzers/pdfium_fuzzer_helper.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 The Chromium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,6 +16,9 @@ class PDFiumFuzzerHelper { + virtual int GetFormCallbackVersion() const = 0; + virtual bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc); + virtual void OnRenderFinished(FPDF_DOCUMENT doc) {} ++ virtual void FormActionHandler(FPDF_FORMHANDLE form, ++ FPDF_DOCUMENT doc, ++ FPDF_PAGE page) {} + + protected: + PDFiumFuzzerHelper(); +diff --git a/testing/fuzzers/pdfium_fuzzer_util.cc b/testing/fuzzers/pdfium_fuzzer_util.cc +index 9238f0eca..f8f828625 100644 +--- a/testing/fuzzers/pdfium_fuzzer_util.cc ++++ b/testing/fuzzers/pdfium_fuzzer_util.cc +@@ -1,9 +1,21 @@ +-// Copyright 2019 The Chromium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "testing/fuzzers/pdfium_fuzzer_util.h" + ++namespace { ++void* g_fuzzer_init_per_process_state = nullptr; ++} // namespace ++ + int GetInteger(const uint8_t* data) { + return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); + } ++ ++FPDF_EXPORT void FPDF_CALLCONV FPDF_SetFuzzerPerProcessState(void* state) { ++ g_fuzzer_init_per_process_state = state; ++} ++ ++FPDF_EXPORT void* FPDF_CALLCONV FPDF_GetFuzzerPerProcessState() { ++ return g_fuzzer_init_per_process_state; ++} +diff --git a/testing/fuzzers/pdfium_fuzzer_util.h b/testing/fuzzers/pdfium_fuzzer_util.h +index d82f44bfe..a37544dde 100644 +--- a/testing/fuzzers/pdfium_fuzzer_util.h ++++ b/testing/fuzzers/pdfium_fuzzer_util.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 The Chromium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,7 +7,19 @@ + + #include + ++#include "public/fpdfview.h" ++ + // Returns an integer from the first 4 bytes of |data|. + int GetInteger(const uint8_t* data); + ++// Plumb access to any context created by fuzzer initialization into ++// the LLVMFuzzerTestOneInput() function, as that function does not ++// allow for additional parameters, nor can it reach back up to the ++// top-level fuzzer shim during a component build (see the comment ++// in BUILD.gn about splitting fuzzers into _impl and _src targets). ++extern "C" { ++FPDF_EXPORT void FPDF_CALLCONV FPDF_SetFuzzerPerProcessState(void* state); ++FPDF_EXPORT void* FPDF_CALLCONV FPDF_GetFuzzerPerProcessState(); ++} // extern "C" ++ + #endif // TESTING_FUZZERS_PDFIUM_FUZZER_UTIL_H_ +diff --git a/testing/fuzzers/pdfium_xfa_fuzzer.cc b/testing/fuzzers/pdfium_xfa_fuzzer.cc +index f9a69d416..1ba78cca8 100644 +--- a/testing/fuzzers/pdfium_xfa_fuzzer.cc ++++ b/testing/fuzzers/pdfium_xfa_fuzzer.cc +@@ -1,4 +1,4 @@ +-// Copyright 2017 The Chromium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.cc b/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.cc +index c4b55b6d9..9f6c78a26 100644 +--- a/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.cc ++++ b/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.cc +@@ -1,4 +1,4 @@ +-// Copyright 2019 The Chromium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h b/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h +index bb6bf1d46..a7c2ec017 100644 +--- a/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h ++++ b/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 The Chromium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/fuzzers/xfa_codec_fuzzer.h b/testing/fuzzers/xfa_codec_fuzzer.h +index 4f5668de2..b14b7d7a0 100644 +--- a/testing/fuzzers/xfa_codec_fuzzer.h ++++ b/testing/fuzzers/xfa_codec_fuzzer.h +@@ -1,4 +1,4 @@ +-// Copyright 2016 The PDFium Authors. All rights reserved. ++// Copyright 2016 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,12 +6,14 @@ + #define TESTING_FUZZERS_XFA_CODEC_FUZZER_H_ + + #include ++#include + + #include "core/fxcodec/fx_codec.h" +-#include "core/fxcodec/progressivedecoder.h" +-#include "core/fxcrt/cfx_readonlymemorystream.h" ++#include "core/fxcodec/progressive_decoder.h" ++#include "core/fxcrt/cfx_read_only_span_stream.h" ++#include "core/fxcrt/fx_safe_types.h" ++#include "core/fxcrt/retain_ptr.h" + #include "core/fxge/dib/cfx_dibitmap.h" +-#include "third_party/base/ptr_util.h" + #include "third_party/base/span.h" + + // Support up to 64 MB. This prevents trivial OOM when MSAN is on and +@@ -21,14 +23,13 @@ const int kXFACodecFuzzerPixelLimit = 64000000; + class XFACodecFuzzer { + public: + static int Fuzz(const uint8_t* data, size_t size, FXCODEC_IMAGE_TYPE type) { +- auto* mgr = fxcodec::ModuleMgr::GetInstance(); +- std::unique_ptr decoder = +- mgr->CreateProgressiveDecoder(); +- auto source = pdfium::MakeRetain( ++ auto decoder = std::make_unique(); ++ auto source = pdfium::MakeRetain( + pdfium::make_span(data, size)); + CFX_DIBAttribute attr; +- FXCODEC_STATUS status = decoder->LoadImageInfo(source, type, &attr, true); +- if (status != FXCODEC_STATUS_FRAME_READY) ++ FXCODEC_STATUS status = ++ decoder->LoadImageInfo(std::move(source), type, &attr, true); ++ if (status != FXCODEC_STATUS::kFrameReady) + return 0; + + // Skipping very large images, since they will take a long time and may lead +@@ -42,16 +43,17 @@ class XFACodecFuzzer { + } + + auto bitmap = pdfium::MakeRetain(); +- bitmap->Create(decoder->GetWidth(), decoder->GetHeight(), FXDIB_Argb); ++ bitmap->Create(decoder->GetWidth(), decoder->GetHeight(), ++ FXDIB_Format::kArgb); + + size_t frames; + std::tie(status, frames) = decoder->GetFrames(); +- if (status != FXCODEC_STATUS_DECODE_READY || frames == 0) ++ if (status != FXCODEC_STATUS::kDecodeReady || frames == 0) + return 0; + + status = decoder->StartDecode(bitmap, 0, 0, bitmap->GetWidth(), + bitmap->GetHeight()); +- while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE) ++ while (status == FXCODEC_STATUS::kDecodeToBeContinued) + status = decoder->ContinueDecode(); + + return 0; +diff --git a/testing/fuzzers/xfa_process_state.cc b/testing/fuzzers/xfa_process_state.cc +new file mode 100644 +index 000000000..f768feae2 +--- /dev/null ++++ b/testing/fuzzers/xfa_process_state.cc +@@ -0,0 +1,25 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "testing/fuzzers/xfa_process_state.h" ++ ++#include "fxjs/gc/heap.h" ++#include "v8/include/libplatform/libplatform.h" ++ ++XFAProcessState::XFAProcessState(v8::Platform* platform, v8::Isolate* isolate) ++ : platform_(platform), isolate_(isolate), heap_(FXGC_CreateHeap()) {} ++ ++XFAProcessState::~XFAProcessState() { ++ FXGC_ForceGarbageCollection(heap_.get()); ++} ++ ++cppgc::Heap* XFAProcessState::GetHeap() const { ++ return heap_.get(); ++} ++ ++void XFAProcessState::ForceGCAndPump() { ++ FXGC_ForceGarbageCollection(heap_.get()); ++ while (v8::platform::PumpMessageLoop(platform_, isolate_)) ++ continue; ++} +diff --git a/testing/fuzzers/xfa_process_state.h b/testing/fuzzers/xfa_process_state.h +new file mode 100644 +index 000000000..12d8a7d38 +--- /dev/null ++++ b/testing/fuzzers/xfa_process_state.h +@@ -0,0 +1,33 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef TESTING_FUZZERS_XFA_PROCESS_STATE_H_ ++#define TESTING_FUZZERS_XFA_PROCESS_STATE_H_ ++ ++#if !defined(PDF_ENABLE_XFA) ++#error "XFA only" ++#endif ++ ++#include "fxjs/gc/heap.h" ++ ++namespace v8 { ++class Isolate; ++class Platform; ++} // namespace v8 ++ ++class XFAProcessState { ++ public: ++ XFAProcessState(v8::Platform* platform, v8::Isolate* isolate); ++ ~XFAProcessState(); ++ ++ cppgc::Heap* GetHeap() const; ++ void ForceGCAndPump(); ++ ++ private: ++ v8::Platform* const platform_; ++ v8::Isolate* const isolate_; ++ FXGCScopedHeap heap_; ++}; ++ ++#endif // TESTING_FUZZERS_XFA_PROCESS_STATE_H_ +diff --git a/testing/fx_string_testhelpers.cpp b/testing/fx_string_testhelpers.cpp +index 4a7bda7d4..8e76a76d2 100644 +--- a/testing/fx_string_testhelpers.cpp ++++ b/testing/fx_string_testhelpers.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,8 +6,11 @@ + + #include + #include ++#include + ++#include "core/fxcrt/cfx_datetime.h" + #include "core/fxcrt/fx_string.h" ++#include "third_party/base/check_op.h" + #include "third_party/base/span.h" + + std::ostream& operator<<(std::ostream& os, const CFX_DateTime& dt) { +@@ -22,7 +25,7 @@ std::ostream& operator<<(std::ostream& os, const CFX_DateTime& dt) { + std::vector StringSplit(const std::string& str, char delimiter) { + std::vector result; + size_t pos = 0; +- while (1) { ++ while (true) { + size_t found = str.find(delimiter, pos); + if (found == std::string::npos) + break; +@@ -48,16 +51,17 @@ std::wstring GetPlatformWString(FPDF_WIDESTRING wstr) { + while (wstr[characters]) + ++characters; + +- std::wstring platform_string(characters, L'\0'); +- for (size_t i = 0; i < characters + 1; ++i) { ++ std::wstring platform_string; ++ platform_string.reserve(characters); ++ for (size_t i = 0; i < characters; ++i) { + const unsigned char* ptr = reinterpret_cast(&wstr[i]); +- platform_string[i] = ptr[0] + 256 * ptr[1]; ++ platform_string.push_back(ptr[0] + 256 * ptr[1]); + } + return platform_string; + } + + ScopedFPDFWideString GetFPDFWideString(const std::wstring& wstr) { +- size_t length = sizeof(uint16_t) * (wstr.length() + 1); ++ size_t length = sizeof(uint16_t) * (wstr.size() + 1); + ScopedFPDFWideString result(static_cast(malloc(length))); + pdfium::span result_span(reinterpret_cast(result.get()), + length); +@@ -72,6 +76,6 @@ ScopedFPDFWideString GetFPDFWideString(const std::wstring& wstr) { + } + + std::vector GetFPDFWideStringBuffer(size_t length_bytes) { +- ASSERT(length_bytes % sizeof(FPDF_WCHAR) == 0); ++ DCHECK_EQ(length_bytes % sizeof(FPDF_WCHAR), 0); + return std::vector(length_bytes / sizeof(FPDF_WCHAR)); + } +diff --git a/testing/fx_string_testhelpers.h b/testing/fx_string_testhelpers.h +index b90838a11..207947c72 100644 +--- a/testing/fx_string_testhelpers.h ++++ b/testing/fx_string_testhelpers.h +@@ -1,19 +1,20 @@ +-// Copyright 2014 PDFium Authors. All rights reserved. ++// Copyright 2014 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #ifndef TESTING_FX_STRING_TESTHELPERS_H_ + #define TESTING_FX_STRING_TESTHELPERS_H_ + ++#include + #include +-#include + #include + #include + +-#include "core/fxcrt/cfx_datetime.h" + #include "public/fpdfview.h" + #include "testing/free_deleter.h" + ++class CFX_DateTime; ++ + // Output stream operator so GTEST macros work with CFX_DateTime objects. + std::ostream& operator<<(std::ostream& os, const CFX_DateTime& dt); + +diff --git a/testing/fxgc_unittest.cpp b/testing/fxgc_unittest.cpp +new file mode 100644 +index 000000000..cc01ab48b +--- /dev/null ++++ b/testing/fxgc_unittest.cpp +@@ -0,0 +1,39 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "testing/fxgc_unittest.h" ++ ++#include "fxjs/gc/heap.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "testing/v8_test_environment.h" ++#include "v8/include/libplatform/libplatform.h" ++ ++FXGCUnitTest::FXGCUnitTest() = default; ++ ++FXGCUnitTest::~FXGCUnitTest() = default; ++ ++void FXGCUnitTest::SetUp() { ++ ::testing::Test::SetUp(); ++ auto* env = V8TestEnvironment::GetInstance(); ++ FXGC_Initialize(env->platform(), env->isolate()); ++ heap_ = FXGC_CreateHeap(); ++ ASSERT_TRUE(heap_); ++} ++ ++void FXGCUnitTest::TearDown() { ++ ForceGCAndPump(); ++ heap_.reset(); ++ FXGC_Release(); ++ ::testing::Test::TearDown(); ++} ++ ++void FXGCUnitTest::ForceGCAndPump() { ++ FXGC_ForceGarbageCollection(heap_.get()); ++ Pump(); ++} ++ ++void FXGCUnitTest::Pump() { ++ V8TestEnvironment::PumpPlatformMessageLoop( ++ V8TestEnvironment::GetInstance()->isolate()); ++} +diff --git a/testing/fxgc_unittest.h b/testing/fxgc_unittest.h +new file mode 100644 +index 000000000..062519092 +--- /dev/null ++++ b/testing/fxgc_unittest.h +@@ -0,0 +1,28 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef TESTING_FXGC_UNITTEST_H_ ++#define TESTING_FXGC_UNITTEST_H_ ++ ++#include "fxjs/gc/heap.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++class FXGCUnitTest : public ::testing::Test { ++ public: ++ FXGCUnitTest(); ++ ~FXGCUnitTest() override; ++ ++ // testing::Test: ++ void SetUp() override; ++ void TearDown() override; ++ ++ cppgc::Heap* heap() const { return heap_.get(); } ++ void ForceGCAndPump(); ++ void Pump(); ++ ++ private: ++ FXGCScopedHeap heap_; ++}; ++ ++#endif // TESTING_FXGC_UNITTEST_H_ +diff --git a/testing/fxv8_unittest.cpp b/testing/fxv8_unittest.cpp +new file mode 100644 +index 000000000..fdd4c0843 +--- /dev/null ++++ b/testing/fxv8_unittest.cpp +@@ -0,0 +1,26 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "testing/fxv8_unittest.h" ++ ++#include ++ ++#include "fxjs/cfx_v8_array_buffer_allocator.h" ++#include "v8/include/v8-isolate.h" ++ ++void FXV8UnitTest::V8IsolateDeleter::operator()(v8::Isolate* ptr) const { ++ ptr->Dispose(); ++} ++ ++FXV8UnitTest::FXV8UnitTest() = default; ++ ++FXV8UnitTest::~FXV8UnitTest() = default; ++ ++void FXV8UnitTest::SetUp() { ++ array_buffer_allocator_ = std::make_unique(); ++ ++ v8::Isolate::CreateParams params; ++ params.array_buffer_allocator = array_buffer_allocator_.get(); ++ isolate_.reset(v8::Isolate::New(params)); ++} +diff --git a/fxjs/cfx_v8_unittest.h b/testing/fxv8_unittest.h +similarity index 74% +rename from fxjs/cfx_v8_unittest.h +rename to testing/fxv8_unittest.h +index e5d4e3f15..0757c7325 100644 +--- a/fxjs/cfx_v8_unittest.h ++++ b/testing/fxv8_unittest.h +@@ -1,9 +1,9 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2020 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +-#ifndef FXJS_CFX_V8_UNITTEST_H_ +-#define FXJS_CFX_V8_UNITTEST_H_ ++#ifndef TESTING_FXV8_UNITTEST_H_ ++#define TESTING_FXV8_UNITTEST_H_ + + #include + +@@ -28,12 +28,10 @@ class FXV8UnitTest : public ::testing::Test { + void SetUp() override; + + v8::Isolate* isolate() const { return isolate_.get(); } +- CFX_V8* cfx_v8() const { return cfx_v8_.get(); } + + protected: + std::unique_ptr array_buffer_allocator_; + std::unique_ptr isolate_; +- std::unique_ptr cfx_v8_; + }; + +-#endif // FXJS_CFX_V8_UNITTEST_H_ ++#endif // TESTING_FXV8_UNITTEST_H_ +diff --git a/testing/gmock/BUILD.gn b/testing/gmock/BUILD.gn +index d7de2e9ce..feb824056 100644 +--- a/testing/gmock/BUILD.gn ++++ b/testing/gmock/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -6,31 +6,14 @@ + # it stabilizes, Chromium code MUST use this target instead of reaching directly + # into //third_party/googletest. + +-import("//build_overrides/build.gni") +- + source_set("gmock") { + testonly = true + sources = [ + "include/gmock/gmock-actions.h", +- "include/gmock/gmock-generated-function-mockers.h", + "include/gmock/gmock-matchers.h", + "include/gmock/gmock.h", + ] +- deps = [ "//third_party/googletest:gmock" ] +- +- # TODO(crbug.com/806952): Depending on gmock_mutant only if build_with_chromium, +- # because gmock_mutant depends on //base which uses C++14. Since gmock is a +- # third_party library used by other projects it should not include C++14 only code. +- if (build_with_chromium) { +- # Allow Chromium targets depending on gmock to #include testing/gmock_mutant.h +- # without triggering a `gn check` error. +- public_deps = [ "//testing:gmock_mutant" ] +- } +- +- public_configs = [ +- "//third_party/googletest:gmock_config", +- "//third_party/googletest:gtest_config", +- ] ++ public_deps = [ "//third_party/googletest:gmock" ] + } + + # The file/directory layout of Google Test is not yet considered stable. Until +diff --git a/testing/gmock/include/gmock/gmock-actions.h b/testing/gmock/include/gmock/gmock-actions.h +index fb193e5dd..21fc6b084 100644 +--- a/testing/gmock/include/gmock/gmock-actions.h ++++ b/testing/gmock/include/gmock/gmock-actions.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 The PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/gmock/include/gmock/gmock-generated-function-mockers.h b/testing/gmock/include/gmock/gmock-generated-function-mockers.h +index 57cbc0a04..8ce51a0f0 100644 +--- a/testing/gmock/include/gmock/gmock-generated-function-mockers.h ++++ b/testing/gmock/include/gmock/gmock-generated-function-mockers.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 The PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/gmock/include/gmock/gmock-matchers.h b/testing/gmock/include/gmock/gmock-matchers.h +index 25d25e908..ca7cf0e39 100644 +--- a/testing/gmock/include/gmock/gmock-matchers.h ++++ b/testing/gmock/include/gmock/gmock-matchers.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 The PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/gmock/include/gmock/gmock.h b/testing/gmock/include/gmock/gmock.h +index a344bcbf5..dfcd2d443 100644 +--- a/testing/gmock/include/gmock/gmock.h ++++ b/testing/gmock/include/gmock/gmock.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 The PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/gtest/BUILD.gn b/testing/gtest/BUILD.gn +index ad0b269eb..f6b98df33 100644 +--- a/testing/gtest/BUILD.gn ++++ b/testing/gtest/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -55,10 +55,7 @@ static_library("gtest") { + sources += [ "../platform_test.h" ] + } + +- if ((is_mac || is_ios) && gtest_include_objc_support) { +- if (is_ios) { +- set_sources_assignment_filter([]) +- } ++ if (is_apple && gtest_include_objc_support) { + sources += [ + "../gtest_mac.h", + "../gtest_mac.mm", +@@ -66,15 +63,10 @@ static_library("gtest") { + if (gtest_include_platform_test) { + sources += [ "../platform_test_mac.mm" ] + } +- set_sources_assignment_filter(sources_assignment_filter) + } + + if (is_ios && gtest_include_ios_coverage) { +- sources += [ +- "../coverage_util_ios.h", +- "../coverage_util_ios.mm", +- ] +- deps = [ ":ios_enable_coverage" ] ++ public_deps += [ ":ios_coverage_utils" ] + } + } + +@@ -87,6 +79,16 @@ source_set("gtest_main") { + } + + if (is_ios) { ++ # These headers are needed in some non test targets for iOS code coverage. So ++ # can not be testonly. ++ source_set("ios_coverage_utils") { ++ sources = [ ++ "../coverage_util_ios.h", ++ "../coverage_util_ios.mm", ++ ] ++ deps = [ ":ios_enable_coverage" ] ++ } ++ + buildflag_header("ios_enable_coverage") { + header = "ios_enable_coverage.h" + flags = [ "IOS_ENABLE_COVERAGE=$use_clang_coverage" ] +diff --git a/testing/gtest/empty.cc b/testing/gtest/empty.cc +index 5186b599d..7cfe79fb0 100644 +--- a/testing/gtest/empty.cc ++++ b/testing/gtest/empty.cc +@@ -1,3 +1,3 @@ +-// Copyright 2018 The PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. +diff --git a/testing/gtest/include/gtest/gtest-death-test.h b/testing/gtest/include/gtest/gtest-death-test.h +new file mode 100644 +index 000000000..6e79c8269 +--- /dev/null ++++ b/testing/gtest/include/gtest/gtest-death-test.h +@@ -0,0 +1,10 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// The file/directory layout of Google Test is not yet considered stable. Until ++// it stabilizes, PDFium code will use forwarding headers in testing/gtest ++// and testing/gmock, instead of directly including files in ++// third_party/googletest. ++ ++#include "third_party/googletest/src/googletest/include/gtest/gtest-death-test.h" +diff --git a/testing/gtest/include/gtest/gtest-message.h b/testing/gtest/include/gtest/gtest-message.h +new file mode 100644 +index 000000000..7859730da +--- /dev/null ++++ b/testing/gtest/include/gtest/gtest-message.h +@@ -0,0 +1,10 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// The file/directory layout of Google Test is not yet considered stable. Until ++// it stabilizes, PDFium code will use forwarding headers in testing/gtest ++// and testing/gmock, instead of directly including files in ++// third_party/googletest. ++ ++#include "third_party/googletest/src/googletest/include/gtest/gtest-message.h" +diff --git a/testing/gtest/include/gtest/gtest-param-test.h b/testing/gtest/include/gtest/gtest-param-test.h +new file mode 100644 +index 000000000..3d681e465 +--- /dev/null ++++ b/testing/gtest/include/gtest/gtest-param-test.h +@@ -0,0 +1,10 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// The file/directory layout of Google Test is not yet considered stable. Until ++// it stabilizes, PDFium code will use forwarding headers in testing/gtest ++// and testing/gmock, instead of directly including files in ++// third_party/googletest. ++ ++#include "third_party/googletest/src/googletest/include/gtest/gtest-param-test.h" +diff --git a/testing/gtest/include/gtest/gtest-spi.h b/testing/gtest/include/gtest/gtest-spi.h +new file mode 100644 +index 000000000..24ba5ee3a +--- /dev/null ++++ b/testing/gtest/include/gtest/gtest-spi.h +@@ -0,0 +1,10 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// The file/directory layout of Google Test is not yet considered stable. Until ++// it stabilizes, PDFium code will use forwarding headers in testing/gtest ++// and testing/gmock, instead of directly including files in ++// third_party/googletest. ++ ++#include "third_party/googletest/src/googletest/include/gtest/gtest-spi.h" +diff --git a/testing/gtest/include/gtest/gtest.h b/testing/gtest/include/gtest/gtest.h +index 9425b25c1..47062803f 100644 +--- a/testing/gtest/include/gtest/gtest.h ++++ b/testing/gtest/include/gtest/gtest.h +@@ -1,9 +1,9 @@ +-// Copyright 2018 The PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // The file/directory layout of Google Test is not yet considered stable. Until +-// it stabilizes, Chromium code will use forwarding headers in testing/gtest ++// it stabilizes, PDFium code will use forwarding headers in testing/gtest + // and testing/gmock, instead of directly including files in + // third_party/googletest. + +diff --git a/testing/gtest/include/gtest/gtest_prod.h b/testing/gtest/include/gtest/gtest_prod.h +index 2d67b4201..703a176a7 100644 +--- a/testing/gtest/include/gtest/gtest_prod.h ++++ b/testing/gtest/include/gtest/gtest_prod.h +@@ -1,9 +1,9 @@ +-// Copyright 2018 The PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + // The file/directory layout of Google Test is not yet considered stable. Until +-// it stabilizes, Chromium code will use forwarding headers in testing/gtest ++// it stabilizes, PDFium code will use forwarding headers in testing/gtest + // and testing/gmock, instead of directly including files in + // third_party/googletest. + +diff --git a/testing/gtest_mac.h b/testing/gtest_mac.h +index 0c0b6555a..95d7d3265 100644 +--- a/testing/gtest_mac.h ++++ b/testing/gtest_mac.h +@@ -1,6 +1,7 @@ +-// Copyright (c) 2010 The Chromium Authors. All rights reserved. ++// Copyright 2010 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. ++ + #ifndef TESTING_GTEST_MAC_H_ + #define TESTING_GTEST_MAC_H_ + #include +diff --git a/testing/gtest_mac.mm b/testing/gtest_mac.mm +index b490f558b..47912c7a1 100644 +--- a/testing/gtest_mac.mm ++++ b/testing/gtest_mac.mm +@@ -1,6 +1,7 @@ +-// Copyright (c) 2010 The Chromium Authors. All rights reserved. ++// Copyright 2010 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. ++ + #import "gtest_mac.h" + #include + #include +diff --git a/testing/image_diff/BUILD.gn b/testing/image_diff/BUILD.gn +index 141769bd9..0f9b71b20 100644 +--- a/testing/image_diff/BUILD.gn ++++ b/testing/image_diff/BUILD.gn +@@ -1,4 +1,4 @@ +-# Copyright 2018 The PDFium Authors. All rights reserved. ++# Copyright 2018 The PDFium Authors + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + +@@ -10,7 +10,10 @@ source_set("image_diff") { + "image_diff_png.cpp", + "image_diff_png.h", + ] +- configs += [ "../../:pdfium_core_config" ] ++ configs += [ ++ "../../:pdfium_strict_config", ++ "../../:pdfium_noshorten_config", ++ ] + deps = [ + "../../third_party:pdfium_base", + "../../third_party:png", +diff --git a/testing/image_diff/DEPS b/testing/image_diff/DEPS +index 4bd2335cd..fcac201c2 100644 +--- a/testing/image_diff/DEPS ++++ b/testing/image_diff/DEPS +@@ -1,5 +1,5 @@ + include_rules = [ +- '+third_party/libpng16', ++ '+third_party/libpng', + '+third_party/zlib', + ] + +diff --git a/testing/image_diff/image_diff.cpp b/testing/image_diff/image_diff.cpp +index c8f9caf7b..a7019211e 100644 +--- a/testing/image_diff/image_diff.cpp ++++ b/testing/image_diff/image_diff.cpp +@@ -1,4 +1,4 @@ +-// Copyright (c) 2011 The Chromium Authors. All rights reserved. ++// Copyright 2011 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,12 +8,12 @@ + // The exact format of this tool's output to stdout is important, to match + // what the run-webkit-tests script expects. + +-#include + #include + #include + #include + + #include ++#include + #include + #include + #include +@@ -21,10 +21,10 @@ + #include "core/fxcrt/fx_memory.h" + #include "testing/image_diff/image_diff_png.h" + #include "testing/utils/path_service.h" +-#include "third_party/base/logging.h" ++#include "third_party/base/cxx17_backports.h" + #include "third_party/base/numerics/safe_conversions.h" + +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + #include + #endif + +@@ -39,8 +39,9 @@ constexpr uint32_t RGBA_ALPHA = 0xff000000; + + class Image { + public: +- Image() : w_(0), h_(0) {} +- Image(const Image& image) : w_(image.w_), h_(image.h_), data_(image.data_) {} ++ Image() = default; ++ Image(const Image& image) = default; ++ Image& operator=(const Image& other) = default; + + bool has_image() const { return w_ > 0 && h_ > 0; } + int w() const { return w_; } +@@ -110,8 +111,8 @@ class Image { + size_t pixel_address(int x, int y) const { return (y * w_ + x) * 4; } + + // Pixel dimensions of the image. +- int w_; +- int h_; ++ int w_ = 0; ++ int h_ = 0; + + std::vector data_; + }; +@@ -143,7 +144,36 @@ void CountImageSizeMismatchAsPixelDifference(const Image& baseline, + *pixels_different += (max_h - h) * max_w; + } + +-float PercentageDifferent(const Image& baseline, const Image& actual) { ++struct UnpackedPixel { ++ explicit UnpackedPixel(uint32_t packed) ++ : red(packed & 0xff), ++ green((packed >> 8) & 0xff), ++ blue((packed >> 16) & 0xff), ++ alpha((packed >> 24) & 0xff) {} ++ ++ uint8_t red; ++ uint8_t green; ++ uint8_t blue; ++ uint8_t alpha; ++}; ++ ++uint8_t ChannelDelta(uint8_t baseline_channel, uint8_t actual_channel) { ++ // No casts are necessary because arithmetic operators implicitly convert ++ // `uint8_t` to `int` first. The final delta is always in the range 0 to 255. ++ return std::abs(baseline_channel - actual_channel); ++} ++ ++uint8_t MaxPixelPerChannelDelta(const UnpackedPixel& baseline_pixel, ++ const UnpackedPixel& actual_pixel) { ++ return std::max({ChannelDelta(baseline_pixel.red, actual_pixel.red), ++ ChannelDelta(baseline_pixel.green, actual_pixel.green), ++ ChannelDelta(baseline_pixel.blue, actual_pixel.blue), ++ ChannelDelta(baseline_pixel.alpha, actual_pixel.alpha)}); ++} ++ ++float PercentageDifferent(const Image& baseline, ++ const Image& actual, ++ uint8_t max_pixel_per_channel_delta) { + int w = std::min(baseline.w(), actual.w()); + int h = std::min(baseline.h(), actual.h()); + +@@ -151,8 +181,17 @@ float PercentageDifferent(const Image& baseline, const Image& actual) { + int pixels_different = 0; + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { +- if (baseline.pixel_at(x, y) != actual.pixel_at(x, y)) ++ const uint32_t baseline_pixel = baseline.pixel_at(x, y); ++ const uint32_t actual_pixel = actual.pixel_at(x, y); ++ if (baseline_pixel == actual_pixel) { ++ continue; ++ } ++ ++ if (MaxPixelPerChannelDelta(UnpackedPixel(baseline_pixel), ++ UnpackedPixel(actual_pixel)) > ++ max_pixel_per_channel_delta) { + ++pixels_different; ++ } + } + } + +@@ -198,23 +237,31 @@ void PrintHelp(const std::string& binary_name) { + fprintf( + stderr, + "Usage:\n" +- " %s OPTIONS \n" +- " Compares two files on disk, returning 0 when they are the same;\n" ++ " %s OPTIONS \n" ++ " Compares two files on disk, returning 0 when they are the same.\n" + " Passing \"--histogram\" additionally calculates a diff of the\n" +- " RGBA value histograms. (which is resistant to shifts in layout)\n" +- " Passing \"--reverse-byte-order\" additionally assumes the compare\n" +- " file has BGRA byte ordering.\n" +- " %s --diff \n" +- " Compares two files on disk, outputs an image that visualizes the\n" +- " difference to \n", +- binary_name.c_str(), binary_name.c_str()); ++ " RGBA value histograms (which is resistant to shifts in layout).\n" ++ " Passing \"--reverse-byte-order\" additionally assumes the\n" ++ " compare file has BGRA byte ordering.\n" ++ " Passing \"--fuzzy\" additionally allows individual pixels to\n" ++ " differ by at most 1 on each channel.\n\n" ++ " %s --diff \n" ++ " Compares two files on disk, and if they differ, outputs an image\n" ++ " to that visualizes the differing pixels as red\n" ++ " dots.\n\n" ++ " %s --subtract \n" ++ " Compares two files on disk, and if they differ, outputs an image\n" ++ " to that visualizes the difference as a scaled\n" ++ " subtraction of pixel values.\n", ++ binary_name.c_str(), binary_name.c_str(), binary_name.c_str()); + } + + int CompareImages(const std::string& binary_name, + const std::string& file1, + const std::string& file2, + bool compare_histograms, +- bool reverse_byte_order) { ++ bool reverse_byte_order, ++ uint8_t max_pixel_per_channel_delta) { + Image actual_image; + Image baseline_image; + +@@ -240,7 +287,8 @@ int CompareImages(const std::string& binary_name, + } + + const char* const diff_name = compare_histograms ? "exact diff" : "diff"; +- float percent = PercentageDifferent(actual_image, baseline_image); ++ float percent = PercentageDifferent(actual_image, baseline_image, ++ max_pixel_per_channel_delta); + const char* const passed = percent > 0.0 ? "failed" : "passed"; + printf("%s: %01.2f%% %s\n", diff_name, percent, passed); + +@@ -280,10 +328,48 @@ bool CreateImageDiff(const Image& image1, const Image& image2, Image* out) { + return same; + } + ++bool SubtractImages(const Image& image1, const Image& image2, Image* out) { ++ int w = std::min(image1.w(), image2.w()); ++ int h = std::min(image1.h(), image2.h()); ++ *out = Image(image1); ++ bool same = (image1.w() == image2.w()) && (image1.h() == image2.h()); ++ ++ for (int y = 0; y < h; ++y) { ++ for (int x = 0; x < w; ++x) { ++ uint32_t pixel1 = image1.pixel_at(x, y); ++ int32_t r1 = pixel1 & 0xff; ++ int32_t g1 = (pixel1 >> 8) & 0xff; ++ int32_t b1 = (pixel1 >> 16) & 0xff; ++ ++ uint32_t pixel2 = image2.pixel_at(x, y); ++ int32_t r2 = pixel2 & 0xff; ++ int32_t g2 = (pixel2 >> 8) & 0xff; ++ int32_t b2 = (pixel2 >> 16) & 0xff; ++ ++ int32_t delta_r = r1 - r2; ++ int32_t delta_g = g1 - g2; ++ int32_t delta_b = b1 - b2; ++ same &= (delta_r == 0 && delta_g == 0 && delta_b == 0); ++ ++ delta_r = pdfium::clamp(128 + delta_r * 8, 0, 255); ++ delta_g = pdfium::clamp(128 + delta_g * 8, 0, 255); ++ delta_b = pdfium::clamp(128 + delta_b * 8, 0, 255); ++ ++ uint32_t new_pixel = RGBA_ALPHA; ++ new_pixel |= delta_r; ++ new_pixel |= (delta_g << 8); ++ new_pixel |= (delta_b << 16); ++ out->set_pixel_at(x, y, new_pixel); ++ } ++ } ++ return same; ++} ++ + int DiffImages(const std::string& binary_name, + const std::string& file1, + const std::string& file2, +- const std::string& out_file) { ++ const std::string& out_file, ++ bool do_subtraction) { + Image actual_image; + Image baseline_image; + +@@ -299,7 +385,9 @@ int DiffImages(const std::string& binary_name, + } + + Image diff_image; +- bool same = CreateImageDiff(baseline_image, actual_image, &diff_image); ++ bool same = do_subtraction ++ ? SubtractImages(baseline_image, actual_image, &diff_image) ++ : CreateImageDiff(baseline_image, actual_image, &diff_image); + if (same) + return kStatusSame; + +@@ -321,11 +409,13 @@ int DiffImages(const std::string& binary_name, + } + + int main(int argc, const char* argv[]) { +- FXMEM_InitializePartitionAlloc(); ++ FX_InitializeMemoryAllocators(); + + bool histograms = false; + bool produce_diff_image = false; ++ bool produce_image_subtraction = false; + bool reverse_byte_order = false; ++ uint8_t max_pixel_per_channel_delta = 0; + std::string filename1; + std::string filename2; + std::string diff_filename; +@@ -343,8 +433,12 @@ int main(int argc, const char* argv[]) { + histograms = true; + } else if (strcmp(arg, "--diff") == 0) { + produce_diff_image = true; ++ } else if (strcmp(arg, "--subtract") == 0) { ++ produce_image_subtraction = true; + } else if (strcmp(arg, "--reverse-byte-order") == 0) { + reverse_byte_order = true; ++ } else if (strcmp(arg, "--fuzzy") == 0) { ++ max_pixel_per_channel_delta = 1; + } + } + if (i < argc) +@@ -354,13 +448,14 @@ int main(int argc, const char* argv[]) { + if (i < argc) + diff_filename = argv[i++]; + +- if (produce_diff_image) { ++ if (produce_diff_image || produce_image_subtraction) { + if (!diff_filename.empty()) { +- return DiffImages(binary_name, filename1, filename2, diff_filename); ++ return DiffImages(binary_name, filename1, filename2, diff_filename, ++ produce_image_subtraction); + } + } else if (!filename2.empty()) { + return CompareImages(binary_name, filename1, filename2, histograms, +- reverse_byte_order); ++ reverse_byte_order, max_pixel_per_channel_delta); + } + + PrintHelp(binary_name); +diff --git a/testing/image_diff/image_diff_png.cpp b/testing/image_diff/image_diff_png.cpp +index dcd9d6979..c2e8b03ca 100644 +--- a/testing/image_diff/image_diff_png.cpp ++++ b/testing/image_diff/image_diff_png.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2013 The Chromium Authors. All rights reserved. ++// Copyright 2013 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -16,8 +16,7 @@ + + #include + +-#include "third_party/base/compiler_specific.h" +-#include "third_party/base/logging.h" ++#include "third_party/base/notreached.h" + + #ifdef USE_SYSTEM_ZLIB + #include +@@ -28,7 +27,7 @@ + #ifdef USE_SYSTEM_LIBPNG + #include + #else +-#include "third_party/libpng16/png.h" ++#include "third_party/libpng/png.h" + #endif + + namespace image_diff_png { +@@ -92,12 +91,12 @@ void ConvertRGBAtoRGB(const uint8_t* rgba, + int pixel_width, + uint8_t* rgb, + bool* is_opaque) { ++ const uint8_t* pixel_in = rgba; ++ uint8_t* pixel_out = rgb; + for (int x = 0; x < pixel_width; x++) { +- const uint8_t* pixel_in = &rgba[x * 4]; +- uint8_t* pixel_out = &rgb[x * 3]; +- pixel_out[0] = pixel_in[0]; +- pixel_out[1] = pixel_in[1]; +- pixel_out[2] = pixel_in[2]; ++ memcpy(pixel_out, pixel_in, 3); ++ pixel_in += 4; ++ pixel_out += 3; + } + } + +@@ -148,13 +147,13 @@ void ConvertRGBtoRGBA(const uint8_t* rgb, + int pixel_width, + uint8_t* rgba, + bool* is_opaque) { ++ const uint8_t* pixel_in = rgb; ++ uint8_t* pixel_out = rgba; + for (int x = 0; x < pixel_width; x++) { +- const uint8_t* pixel_in = &rgb[x * 3]; +- uint8_t* pixel_out = &rgba[x * 4]; +- pixel_out[0] = pixel_in[0]; +- pixel_out[1] = pixel_in[1]; +- pixel_out[2] = pixel_in[2]; ++ memcpy(pixel_out, pixel_in, 3); + pixel_out[3] = 0xff; ++ pixel_in += 3; ++ pixel_out += 4; + } + } + +@@ -428,7 +427,7 @@ void ConvertBGRAtoRGB(const uint8_t* bgra, + #ifdef PNG_TEXT_SUPPORTED + + inline char* strdup(const char* str) { +-#if defined(OS_WIN) ++#if BUILDFLAG(IS_WIN) + return _strdup(str); + #else + return ::strdup(str); +@@ -461,11 +460,11 @@ class CommentWriter { + void AddComment(size_t pos, const Comment& comment) { + png_text_[pos].compression = PNG_TEXT_COMPRESSION_NONE; + // A PNG comment's key can only be 79 characters long. +- if (comment.key.length() > 79) ++ if (comment.key.size() > 79) + return; + png_text_[pos].key = strdup(comment.key.substr(0, 78).c_str()); + png_text_[pos].text = strdup(comment.text.c_str()); +- png_text_[pos].text_length = comment.text.length(); ++ png_text_[pos].text_length = comment.text.size(); + #ifdef PNG_iTXt_SUPPORTED + png_text_[pos].itxt_length = 0; + png_text_[pos].lang = 0; +@@ -571,7 +570,7 @@ std::vector EncodeWithCompressionLevel( + switch (format) { + case FORMAT_BGR: + converter = ConvertBGRtoRGB; +- FALLTHROUGH; ++ [[fallthrough]]; + + case FORMAT_RGB: + input_color_components = 3; +diff --git a/testing/image_diff/image_diff_png.h b/testing/image_diff/image_diff_png.h +index 2c39bf018..370ce3238 100644 +--- a/testing/image_diff/image_diff_png.h ++++ b/testing/image_diff/image_diff_png.h +@@ -1,4 +1,4 @@ +-// Copyright 2013 The Chromium Authors. All rights reserved. ++// Copyright 2013 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/invalid_seekable_read_stream.cpp b/testing/invalid_seekable_read_stream.cpp +index a4116b075..778783cd1 100644 +--- a/testing/invalid_seekable_read_stream.cpp ++++ b/testing/invalid_seekable_read_stream.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -9,9 +9,8 @@ InvalidSeekableReadStream::InvalidSeekableReadStream(FX_FILESIZE data_size) + + InvalidSeekableReadStream::~InvalidSeekableReadStream() = default; + +-bool InvalidSeekableReadStream::ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) { ++bool InvalidSeekableReadStream::ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) { + return false; + } + +diff --git a/testing/invalid_seekable_read_stream.h b/testing/invalid_seekable_read_stream.h +index 9322bc6f3..ae24b1a08 100644 +--- a/testing/invalid_seekable_read_stream.h ++++ b/testing/invalid_seekable_read_stream.h +@@ -1,4 +1,4 @@ +-// Copyright 2019 PDFium Authors. All rights reserved. ++// Copyright 2019 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -6,17 +6,16 @@ + #define TESTING_INVALID_SEEKABLE_READ_STREAM_H_ + + #include "core/fxcrt/fx_stream.h" ++#include "core/fxcrt/retain_ptr.h" + + // A stream used for testing where reads always fail. + class InvalidSeekableReadStream final : public IFX_SeekableReadStream { + public: +- template +- friend RetainPtr pdfium::MakeRetain(Args&&... args); ++ CONSTRUCT_VIA_MAKE_RETAIN; + + // IFX_SeekableReadStream overrides: +- bool ReadBlockAtOffset(void* buffer, +- FX_FILESIZE offset, +- size_t size) override; ++ bool ReadBlockAtOffset(pdfium::span buffer, ++ FX_FILESIZE offset) override; + FX_FILESIZE GetSize() override; + + private: +diff --git a/testing/js_embedder_test.cpp b/testing/js_embedder_test.cpp +index 57e6c4d31..d6f800c3d 100644 +--- a/testing/js_embedder_test.cpp ++++ b/testing/js_embedder_test.cpp +@@ -1,40 +1,15 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #include "testing/js_embedder_test.h" + +-#include "fxjs/cfxjs_engine.h" +-#include "third_party/base/ptr_util.h" ++#include "testing/v8_test_environment.h" + +-JSEmbedderTest::JSEmbedderTest() +- : m_pArrayBufferAllocator( +- pdfium::MakeUnique()) {} ++JSEmbedderTest::JSEmbedderTest() = default; + +-JSEmbedderTest::~JSEmbedderTest() {} ++JSEmbedderTest::~JSEmbedderTest() = default; + +-void JSEmbedderTest::SetUp() { +- v8::Isolate::CreateParams params; +- params.array_buffer_allocator = m_pArrayBufferAllocator.get(); +- m_pIsolate.reset(v8::Isolate::New(params)); +- +- EmbedderTest::SetExternalIsolate(isolate()); +- EmbedderTest::SetUp(); +- +- v8::Isolate::Scope isolate_scope(isolate()); +- v8::HandleScope handle_scope(isolate()); +- FXJS_PerIsolateData::SetUp(isolate()); +- m_Engine = pdfium::MakeUnique(isolate()); +- m_Engine->InitializeEngine(); +-} +- +-void JSEmbedderTest::TearDown() { +- m_Engine->ReleaseEngine(); +- m_Engine.reset(); +- EmbedderTest::TearDown(); +- m_pIsolate.reset(); +-} +- +-v8::Local JSEmbedderTest::GetV8Context() { +- return m_Engine->GetV8Context(); ++v8::Isolate* JSEmbedderTest::isolate() const { ++ return V8TestEnvironment::GetInstance()->isolate(); + } +diff --git a/testing/js_embedder_test.h b/testing/js_embedder_test.h +index dca0bd76c..dda285a33 100644 +--- a/testing/js_embedder_test.h ++++ b/testing/js_embedder_test.h +@@ -1,35 +1,22 @@ +-// Copyright 2015 PDFium Authors. All rights reserved. ++// Copyright 2015 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + + #ifndef TESTING_JS_EMBEDDER_TEST_H_ + #define TESTING_JS_EMBEDDER_TEST_H_ + +-#include +- +-#include "fxjs/cfx_v8.h" + #include "testing/embedder_test.h" +-#include "v8/include/v8.h" + +-class CFXJS_Engine; +-class CFX_V8ArrayBufferAllocator; ++namespace v8 { ++class Isolate; ++} // namespace v8 + + class JSEmbedderTest : public EmbedderTest { + public: + JSEmbedderTest(); + ~JSEmbedderTest() override; + +- void SetUp() override; +- void TearDown() override; +- +- v8::Isolate* isolate() const { return m_pIsolate.get(); } +- CFXJS_Engine* engine() const { return m_Engine.get(); } +- v8::Local GetV8Context(); +- +- private: +- std::unique_ptr m_pArrayBufferAllocator; +- std::unique_ptr m_pIsolate; +- std::unique_ptr m_Engine; ++ v8::Isolate* isolate() const; + }; + + #endif // TESTING_JS_EMBEDDER_TEST_H_ +diff --git a/testing/pdf_test_environment.cpp b/testing/pdf_test_environment.cpp +new file mode 100644 +index 000000000..286502794 +--- /dev/null ++++ b/testing/pdf_test_environment.cpp +@@ -0,0 +1,20 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "testing/pdf_test_environment.h" ++ ++#include "core/fxge/cfx_gemodule.h" ++ ++PDFTestEnvironment::PDFTestEnvironment() = default; ++ ++PDFTestEnvironment::~PDFTestEnvironment() = default; ++ ++// testing::Environment: ++void PDFTestEnvironment::SetUp() { ++ CFX_GEModule::Create(test_fonts_.font_paths()); ++} ++ ++void PDFTestEnvironment::TearDown() { ++ CFX_GEModule::Destroy(); ++} +diff --git a/testing/pdf_test_environment.h b/testing/pdf_test_environment.h +new file mode 100644 +index 000000000..818d8e4e0 +--- /dev/null ++++ b/testing/pdf_test_environment.h +@@ -0,0 +1,24 @@ ++// Copyright 2020 The PDFium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef TESTING_PDF_TEST_ENVIRONMENT_H_ ++#define TESTING_PDF_TEST_ENVIRONMENT_H_ ++ ++#include "testing/gtest/include/gtest/gtest.h" ++#include "testing/test_fonts.h" ++ ++class PDFTestEnvironment : public testing::Environment { ++ public: ++ PDFTestEnvironment(); ++ ~PDFTestEnvironment() override; ++ ++ // testing::Environment: ++ void SetUp() override; ++ void TearDown() override; ++ ++ private: ++ TestFonts test_fonts_; ++}; ++ ++#endif // TESTING_PDF_TEST_ENVIRONMENT_H_ +diff --git a/testing/pseudo_retainable.h b/testing/pseudo_retainable.h +index c4390d697..6dbfffcf8 100644 +--- a/testing/pseudo_retainable.h ++++ b/testing/pseudo_retainable.h +@@ -1,4 +1,4 @@ +-// Copyright 2018 PDFium Authors. All rights reserved. ++// Copyright 2018 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -8,8 +8,8 @@ + class PseudoRetainable { + public: + PseudoRetainable() = default; +- void Retain() { ++retain_count_; } +- void Release() { ++ void Retain() const { ++retain_count_; } ++ void Release() const { + if (++release_count_ == retain_count_) + alive_ = false; + } +@@ -18,9 +18,9 @@ class PseudoRetainable { + int release_count() const { return release_count_; } + + private: +- bool alive_ = true; +- int retain_count_ = 0; +- int release_count_ = 0; ++ mutable bool alive_ = true; ++ mutable int retain_count_ = 0; ++ mutable int release_count_ = 0; + }; + + #endif // TESTING_PSEUDO_RETAINABLE_H_ +diff --git a/testing/range_set.cpp b/testing/range_set.cpp +index 2fc540f17..725fd7d96 100644 +--- a/testing/range_set.cpp ++++ b/testing/range_set.cpp +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +@@ -7,9 +7,11 @@ + #include + + #include "core/fxcrt/fx_system.h" ++#include "third_party/base/check.h" + +-RangeSet::RangeSet() {} +-RangeSet::~RangeSet() {} ++RangeSet::RangeSet() = default; ++ ++RangeSet::~RangeSet() = default; + + bool RangeSet::Contains(const Range& range) const { + if (IsEmptyRange(range)) +@@ -51,15 +53,14 @@ void RangeSet::Union(const Range& range) { + + --end; + +- const int new_start = std::min(start->first, fixed_range.first); +- const int new_end = std::max(end->second, fixed_range.second); +- ++ const size_t new_start = std::min(start->first, fixed_range.first); ++ const size_t new_end = std::max(end->second, fixed_range.second); + ranges_.erase(start, ++end); + ranges_.insert(Range(new_start, new_end)); + } + + void RangeSet::Union(const RangeSet& range_set) { +- ASSERT(&range_set != this); ++ DCHECK(&range_set != this); + for (const auto& it : range_set.ranges()) + Union(it); + } +diff --git a/testing/range_set.h b/testing/range_set.h +index 6ed24bd83..7ba10dfc0 100644 +--- a/testing/range_set.h ++++ b/testing/range_set.h +@@ -1,4 +1,4 @@ +-// Copyright 2017 PDFium Authors. All rights reserved. ++// Copyright 2017 The PDFium Authors + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + +diff --git a/testing/resources/CMYK-alpha.jpf b/testing/resources/CMYK-alpha.jpf +new file mode 100644 +index 0000000000000000000000000000000000000000..538e6d6986c8d4060168be1e337647707335766c +GIT binary patch +literal 713 +zcmZQzVBpCLP*C9IYUg5LV30{GsVvAUFj4@r89;PaK}8Y}gZQ~cMX7~M|NsB^z`)AD +z*ucQR>d?Tzqu{_GB%r`x&LP0SV#UiO!S&KnbaR>aDaUhsg)A)YCw0zTTrcix(^`K+E!hmS+HI +zvCNE=A|MSCV_{%rXJ-U582FR(bBY)ln1M_lAl67uNlgPf3IrH|kb!{-2pNF#AQdb? +z2-d|QRg_w+02C1d(#hOH42&QQCwpgSKff$j^C9vP4hbk6|AQ($uF-U5mn +z0i72O^jPwL|Nnsura%#x$G}W5(Eubt7y?+?85xn${~)l385vobnHd=W2QzR20|m^C +zVGwq3@bCygM*m|OL|8CICoq7bnt|a8(1gjyudr}t98hF9zlDRP14wXG0SRUai1l#t +z;OD0HE3EPj0>D^5!P;v@j)!>^dzudoBT?1hfJO3EBu=MQk%T~J_J&Q@}NhRPqt +zd?Tzqu{_GB%r`x&LP0SV#UiO!S&KnbaR>aDaUhsg)A)YCw0zTTrcix(^`K+7_LmS+HI +zvCNE=A|MSCV_{%nXJ-U582FR(bBY)ln1M_lAXZ9FNlgPQVPIecLIwsVAY=f_gH(XD +zN)@FRD*$OBAf3!D#J~u`Kt2P5WNB$;3cADL66o#+>5&2HK=%evJOw6)?jfMK5zt{A +zKrbc#_x~RV;sF7~OJF9LXaJHR3;`_cjEr#fKM3qkMn+a$mstB +z22cbtFf0Wsn0)*S3undwMTYZRI9NJ>1Vu%cZMZ)}_Ge=Q +zyOJ17VcPu}BDD*`->`5@=a6E$aQqgle1^!M0}ael%<@cE4!>ed3-zK_@wzfS!4d+ +F1OPEAr?LP5 + +literal 0 +HcmV?d00001 + +diff --git a/testing/resources/RGB-alpha.jp2 b/testing/resources/RGB-alpha.jp2 +new file mode 100644 +index 0000000000000000000000000000000000000000..4ab41dac234f2b02d5b0745600fc05ac9698b527 +GIT binary patch +literal 373 +zcmZQzVBpCLP*C9IYUg5LU=T?wsVvAUFj4@r8KAU3kj?;d#WFKeihwi=7_+dm18D{Z +z{^b0eB1SM%07xn&r=+HVl`t?c0wDtf6A&^mFasf26(bwaxa9x-{{tC}z`7vDftgtN +zEbNSoaP&Wjfd{CDk%5VYg@N&Z3fu*R +z?N?aYuduM*XT8r+fBt|_9m9m*4h^ghm*rU<+vQo~;X>@iuimrI_R42pd;Ause1?kM +n1qB}8j(9$4zIxVpu#k@3g#dm}^D6#0SF!!vEYC{q|K9`v2SHbr + +literal 0 +HcmV?d00001 + +diff --git a/testing/resources/RGB.jp2 b/testing/resources/RGB.jp2 +new file mode 100644 +index 0000000000000000000000000000000000000000..7b7d428730a0061adbca5d78c4d517c8a2f3df72 +GIT binary patch +literal 288 +zcmZQzVBpCLP*C9IYUg5LU=T?wsVvAUFj4@r8KAT-kj?;d#WFKeihwi=7&Ei818D{Z +z{^b0eB1SM%07za2Do+0I|38pHA1nq@3}#~CGqW=?LeT#p1|FaSMn)zU76!)uF$`P| +z4jvu>K=gkC1IT3z3^hRU$;Yp-u)ROOg@bke!B32g2R}D8KWCM15MZoltY> ++>> ++endobj ++{{object 2 0}} << ++ /Type /Pages ++ /MediaBox [0 0 612 792] ++ /Kids [3 0 R] ++ /Count 1 ++>> ++endobj ++{{object 3 0}} << ++ /Type /Page ++ /Parent 2 0 R ++ /MediaBox [0 0 612 792] ++ /Annots [4 0 R] ++>> ++endobj ++{{object 4 0}} << ++ /Type /Annot ++ /Subtype /Widget ++ /Rect [85 721 153 735] ++ /FT /Tx ++ /P 3 0 R ++ /T (Widget) ++ /AA << ++ /F << ++ /JS (AFDate_FormatEx\("yyyy-mm-dd"\);) ++ /S /JavaScript ++ >> ++ >> ++>> ++endobj ++{{xref}} ++{{trailer}} ++{{startxref}} ++%%EOF +diff --git a/testing/resources/annot_javascript.pdf b/testing/resources/annot_javascript.pdf +new file mode 100644 +index 000000000..fbeb5df7e +--- /dev/null ++++ b/testing/resources/annot_javascript.pdf +@@ -0,0 +1,53 @@ ++%PDF-1.7 ++% ò¤ô ++1 0 obj << ++ /Type /Catalog ++ /Pages 2 0 R ++ /AcroForm << ++ /Fields [4 0 R] ++ >> ++>> ++endobj ++2 0 obj << ++ /Type /Pages ++ /MediaBox [0 0 612 792] ++ /Kids [3 0 R] ++ /Count 1 ++>> ++endobj ++3 0 obj << ++ /Type /Page ++ /Parent 2 0 R ++ /MediaBox [0 0 612 792] ++ /Annots [4 0 R] ++>> ++endobj ++4 0 obj << ++ /Type /Annot ++ /Subtype /Widget ++ /Rect [85 721 153 735] ++ /FT /Tx ++ /P 3 0 R ++ /T (Widget) ++ /AA << ++ /F << ++ /JS (AFDate_FormatEx\("yyyy-mm-dd"\);) ++ /S /JavaScript ++ >> ++ >> ++>> ++endobj ++xref ++0 5 ++0000000000 65535 f ++0000000015 00000 n ++0000000108 00000 n ++0000000197 00000 n ++0000000292 00000 n ++trailer << ++ /Root 1 0 R ++ /Size 5 ++>> ++startxref ++504 ++%%EOF +diff --git a/testing/resources/link_annots.in b/testing/resources/annots.in +similarity index 90% +rename from testing/resources/link_annots.in +rename to testing/resources/annots.in +index 076069c34..cf575f51a 100644 +--- a/testing/resources/link_annots.in ++++ b/testing/resources/annots.in +@@ -2,6 +2,14 @@ + {{object 1 0}} << + /Type /Catalog + /Pages 2 0 R ++ /AcroForm << ++ /Fields [23 0 R] ++ /DR << ++ /Font << ++ /F1 7 0 R ++ >> ++ >> ++ >> + >> + endobj + {{object 2 0}} << +@@ -17,7 +25,7 @@ endobj + >> + /ProcSet [/PDF /Text /ImageC] + /ExtGState << +- /GS0 23 0 R ++ /GS0 24 0 R + >> + >> + >> +@@ -26,14 +34,14 @@ endobj + /Type /Page + /Parent 2 0 R + /Contents 5 0 R +- /Annots [15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R] ++ /Annots [15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R] + >> + endobj + {{object 4 0}} << + /Type /Page + /Parent 2 0 R + /Contents 6 0 R +- /Annots [15 0 R 16 0 R] ++ /Annots [15 0 R 16 0 R 26 0 R] + >> + endobj + {{object 5 0}} << +@@ -102,7 +110,7 @@ endobj + /Form0 10 0 R + >> + /ExtGState << +- /GS0 24 0 R ++ /GS0 25 0 R + >> + >> + >> +@@ -141,7 +149,7 @@ endobj + /Form0 12 0 R + >> + /ExtGState << +- /GS0 24 0 R ++ /GS0 25 0 R + >> + >> + >> +@@ -180,7 +188,7 @@ endobj + /Form0 14 0 R + >> + /ExtGState << +- /GS0 24 0 R ++ /GS0 25 0 R + >> + >> + >> +@@ -313,13 +321,24 @@ endobj + >> + endobj + {{object 23 0}} << ++ /Type /Annot ++ /Subtype /Widget ++ /FT /Ch ++ /Ff 131072 ++ /T (Combo1) ++ /DA (0 0 0 rg /F1 12 Tf) ++ /Rect [70 350 170 380] ++ /Opt [(Highlight) (Link) (Popup) (Widget)] ++>> ++endobj ++{{object 24 0}} << + /ca 1 + /Type /ExtGState + /CA 1 + /BM /Normal + >> + endobj +-{{object 24 0}} << ++{{object 25 0}} << + /ca 1 + /Type /ExtGState + /CA 1 +@@ -327,6 +346,16 @@ endobj + /BM /Multiply + >> + endobj ++{{object 26 0}} << ++ /Type /Annot ++ /Subtype /Square ++ /Border [0 0 2] ++ /C [1 0 0] ++ /F 4 ++ /P 3 0 R ++ /Rect [50 100 60 120] ++>> ++endobj + {{xref}} + {{trailer}} + {{startxref}} +diff --git a/testing/resources/link_annots.pdf b/testing/resources/annots.pdf +similarity index 81% +rename from testing/resources/link_annots.pdf +rename to testing/resources/annots.pdf +index b964bf56f..745346cda 100644 +--- a/testing/resources/link_annots.pdf ++++ b/testing/resources/annots.pdf +@@ -3,6 +3,14 @@ + 1 0 obj << + /Type /Catalog + /Pages 2 0 R ++ /AcroForm << ++ /Fields [23 0 R] ++ /DR << ++ /Font << ++ /F1 7 0 R ++ >> ++ >> ++ >> + >> + endobj + 2 0 obj << +@@ -18,7 +26,7 @@ endobj + >> + /ProcSet [/PDF /Text /ImageC] + /ExtGState << +- /GS0 23 0 R ++ /GS0 24 0 R + >> + >> + >> +@@ -27,14 +35,14 @@ endobj + /Type /Page + /Parent 2 0 R + /Contents 5 0 R +- /Annots [15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R] ++ /Annots [15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R] + >> + endobj + 4 0 obj << + /Type /Page + /Parent 2 0 R + /Contents 6 0 R +- /Annots [15 0 R 16 0 R] ++ /Annots [15 0 R 16 0 R 26 0 R] + >> + endobj + 5 0 obj << +@@ -103,7 +111,7 @@ endobj + /Form0 10 0 R + >> + /ExtGState << +- /GS0 24 0 R ++ /GS0 25 0 R + >> + >> + >> +@@ -142,7 +150,7 @@ endobj + /Form0 12 0 R + >> + /ExtGState << +- /GS0 24 0 R ++ /GS0 25 0 R + >> + >> + >> +@@ -181,7 +189,7 @@ endobj + /Form0 14 0 R + >> + /ExtGState << +- /GS0 24 0 R ++ /GS0 25 0 R + >> + >> + >> +@@ -314,13 +322,24 @@ endobj + >> + endobj + 23 0 obj << ++ /Type /Annot ++ /Subtype /Widget ++ /FT /Ch ++ /Ff 131072 ++ /T (Combo1) ++ /DA (0 0 0 rg /F1 12 Tf) ++ /Rect [70 350 170 380] ++ /Opt [(Highlight) (Link) (Popup) (Widget)] ++>> ++endobj ++24 0 obj << + /ca 1 + /Type /ExtGState + /CA 1 + /BM /Normal + >> + endobj +-24 0 obj << ++25 0 obj << + /ca 1 + /Type /ExtGState + /CA 1 +@@ -328,37 +347,49 @@ endobj + /BM /Multiply + >> + endobj ++26 0 obj << ++ /Type /Annot ++ /Subtype /Square ++ /Border [0 0 2] ++ /C [1 0 0] ++ /F 4 ++ /P 3 0 R ++ /Rect [50 100 60 120] ++>> ++endobj + xref +-0 25 ++0 27 + 0000000000 65535 f + 0000000015 00000 n +-0000000068 00000 n +-0000000338 00000 n +-0000000475 00000 n +-0000000570 00000 n +-0000001108 00000 n +-0000001345 00000 n +-0000001423 00000 n +-0000001499 00000 n +-0000001749 00000 n +-0000001972 00000 n +-0000002222 00000 n +-0000002442 00000 n +-0000002693 00000 n +-0000002916 00000 n +-0000003056 00000 n +-0000003196 00000 n +-0000003443 00000 n +-0000003727 00000 n +-0000003944 00000 n +-0000004171 00000 n +-0000004269 00000 n +-0000004544 00000 n +-0000004615 00000 n ++0000000169 00000 n ++0000000439 00000 n ++0000000583 00000 n ++0000000685 00000 n ++0000001223 00000 n ++0000001460 00000 n ++0000001538 00000 n ++0000001614 00000 n ++0000001864 00000 n ++0000002087 00000 n ++0000002337 00000 n ++0000002557 00000 n ++0000002808 00000 n ++0000003031 00000 n ++0000003171 00000 n ++0000003311 00000 n ++0000003558 00000 n ++0000003842 00000 n ++0000004059 00000 n ++0000004286 00000 n ++0000004384 00000 n ++0000004659 00000 n ++0000004849 00000 n ++0000004920 00000 n ++0000005006 00000 n + trailer << + /Root 1 0 R +- /Size 25 ++ /Size 27 + >> + startxref +-4701 ++5135 + %%EOF +diff --git a/testing/resources/annots_action_handling.in b/testing/resources/annots_action_handling.in +new file mode 100644 +index 000000000..e5c728bba +--- /dev/null ++++ b/testing/resources/annots_action_handling.in +@@ -0,0 +1,132 @@ ++{{header}} ++{{object 1 0}} << ++ /Type /Catalog ++ /Pages 2 0 R ++ /Names 12 0 R ++ /AcroForm [6 0 R 7 0 R] ++>> ++endobj ++{{object 2 0}} << ++ /Type /Pages ++ /Count 2 ++ /Kids [3 0 R 4 0 R] ++>> ++endobj ++{{object 3 0}} << ++ /Type /Page ++ /Parent 2 0 R ++ /Annots [6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R] ++ /Contents 5 0 R ++ /Tabs /R ++>> ++endobj ++{{object 4 0}} << ++ /Type /Page ++ /Parent 2 0 R ++ /Tabs /R ++>> ++endobj ++{{object 5 0}} << ++ {{streamlen}} ++>> ++stream ++BT ++70 340 Td ++14 Tf ++(External Link ) Tj ++0 -35 Td ++14 Tf ++(Internal Link ) Tj ++0 -35 Td ++14 Tf ++(Link1 to top ) Tj ++0 -35 Td ++14 Tf ++(Link2 to top ) Tj ++ET ++endstream ++endobj ++{{object 6 0}} << ++ /Type /Annot ++ /Subtype /Widget ++ /FT /Tx ++ /Parent 3 0 R ++ /T (TextField) ++ /Rect [69 670 220 690] ++>> ++endobj ++{{object 7 0}} << ++ /Type /Annot ++ /Subtype /Widget ++ /FT /Btn ++ /Rect [69 360 220 380] ++ /A << ++ /URI (https://www.google.com) ++ /S /URI ++ >> ++ /F 4 ++ /T (button) ++ /Ff 65536 ++>> ++endobj ++{{object 8 0}} << ++ /Type /Annot ++ /Subtype /Link ++ /Rect [69 338 180 358] ++ /A << ++ /Type /Action ++ /S /URI ++ /URI (https://cs.chromium.org/) ++ >> ++ /F 4 ++>> ++endobj ++{{object 9 0}} << ++ /Type /Annot ++ /Subtype /Link ++ /Rect [69 305 180 325] ++ /BS << ++ /W 0 ++ >> ++ /Dest [4 0 R /XYZ 200 725 0] ++ /F 4 ++>> ++{{object 10 0}} << ++ /Type /Annot ++ /Subtype /Link ++ /Rect [69 270 180 290] ++ /BS << ++ /W 0 ++ >> ++ /Dest /top ++ /F 4 ++>> ++{{object 11 0}} << ++ /Type /Annot ++ /Subtype /Link ++ /Rect [69 235 180 255] ++ /BS << ++ /W 0 ++ >> ++ /Dest (target10) ++ /F 4 ++>> ++{{object 12 0}} << ++ /Dests 13 0 R ++>> ++endobj ++{{object 13 0}} << ++ /Names [ ++ (target10) 14 0 R ++ /top 14 0 R ++ ] ++>> ++endobj ++{{object 14 0}} << ++ /D [3 0 R /XYZ 100 200 0] ++>> ++endobj ++{{xref}} ++{{trailer}} ++{{startxref}} ++%%EOF +\ No newline at end of file +diff --git a/testing/resources/annots_action_handling.pdf b/testing/resources/annots_action_handling.pdf +new file mode 100644 +index 000000000..b8b6c2aba +--- /dev/null ++++ b/testing/resources/annots_action_handling.pdf +@@ -0,0 +1,153 @@ ++%PDF-1.7 ++% ò¤ô ++1 0 obj << ++ /Type /Catalog ++ /Pages 2 0 R ++ /Names 12 0 R ++ /AcroForm [6 0 R 7 0 R] ++>> ++endobj ++2 0 obj << ++ /Type /Pages ++ /Count 2 ++ /Kids [3 0 R 4 0 R] ++>> ++endobj ++3 0 obj << ++ /Type /Page ++ /Parent 2 0 R ++ /Annots [6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R] ++ /Contents 5 0 R ++ /Tabs /R ++>> ++endobj ++4 0 obj << ++ /Type /Page ++ /Parent 2 0 R ++ /Tabs /R ++>> ++endobj ++5 0 obj << ++ /Length 145 ++>> ++stream ++BT ++70 340 Td ++14 Tf ++(External Link ) Tj ++0 -35 Td ++14 Tf ++(Internal Link ) Tj ++0 -35 Td ++14 Tf ++(Link1 to top ) Tj ++0 -35 Td ++14 Tf ++(Link2 to top ) Tj ++ET ++endstream ++endobj ++6 0 obj << ++ /Type /Annot ++ /Subtype /Widget ++ /FT /Tx ++ /Parent 3 0 R ++ /T (TextField) ++ /Rect [69 670 220 690] ++>> ++endobj ++7 0 obj << ++ /Type /Annot ++ /Subtype /Widget ++ /FT /Btn ++ /Rect [69 360 220 380] ++ /A << ++ /URI (https://www.google.com) ++ /S /URI ++ >> ++ /F 4 ++ /T (button) ++ /Ff 65536 ++>> ++endobj ++8 0 obj << ++ /Type /Annot ++ /Subtype /Link ++ /Rect [69 338 180 358] ++ /A << ++ /Type /Action ++ /S /URI ++ /URI (https://cs.chromium.org/) ++ >> ++ /F 4 ++>> ++endobj ++9 0 obj << ++ /Type /Annot ++ /Subtype /Link ++ /Rect [69 305 180 325] ++ /BS << ++ /W 0 ++ >> ++ /Dest [4 0 R /XYZ 200 725 0] ++ /F 4 ++>> ++10 0 obj << ++ /Type /Annot ++ /Subtype /Link ++ /Rect [69 270 180 290] ++ /BS << ++ /W 0 ++ >> ++ /Dest /top ++ /F 4 ++>> ++11 0 obj << ++ /Type /Annot ++ /Subtype /Link ++ /Rect [69 235 180 255] ++ /BS << ++ /W 0 ++ >> ++ /Dest (target10) ++ /F 4 ++>> ++12 0 obj << ++ /Dests 13 0 R ++>> ++endobj ++13 0 obj << ++ /Names [ ++ (target10) 14 0 R ++ /top 14 0 R ++ ] ++>> ++endobj ++14 0 obj << ++ /D [3 0 R /XYZ 100 200 0] ++>> ++endobj ++xref ++0 15 ++0000000000 65535 f ++0000000015 00000 n ++0000000110 00000 n ++0000000179 00000 n ++0000000309 00000 n ++0000000371 00000 n ++0000000568 00000 n ++0000000691 00000 n ++0000000874 00000 n ++0000001038 00000 n ++0000001170 00000 n ++0000001285 00000 n ++0000001406 00000 n ++0000001444 00000 n ++0000001519 00000 n ++trailer << ++ /Root 1 0 R ++ /Size 15 ++>> ++startxref ++1569 ++%%EOF +\ No newline at end of file +diff --git a/testing/resources/bad_dict_keys.in b/testing/resources/bad_dict_keys.in +new file mode 100644 +index 000000000..3f53c28ee +--- /dev/null ++++ b/testing/resources/bad_dict_keys.in +@@ -0,0 +1,23 @@ ++{{header}} ++{{object 1 0}} << ++ /Type /Catalog ++ /Pages 2 0 R ++>> ++endobj ++{{object 2 0}} << ++ /Type /Pages ++ /MediaBox [0 0 300 300] ++ /Count 1 ++ /Kids [3 0 R] ++ / [3 0 R] ++>> ++endobj ++{{object 3 0}} << ++ /Type /Page ++ /Parent 2 0 R ++>> ++endobj ++{{xref}} ++{{trailer}} ++{{startxref}} ++%%EOF +diff --git a/testing/resources/bad_dict_keys.pdf b/testing/resources/bad_dict_keys.pdf +new file mode 100644 +index 000000000..b55c4341f +--- /dev/null ++++ b/testing/resources/bad_dict_keys.pdf +@@ -0,0 +1,33 @@ ++%PDF-1.7 ++% ò¤ô ++1 0 obj << ++ /Type /Catalog ++ /Pages 2 0 R ++>> ++endobj ++2 0 obj << ++ /Type /Pages ++ /MediaBox [0 0 300 300] ++ /Count 1 ++ /Kids [3 0 R] ++ / [3 0 R] ++>> ++endobj ++3 0 obj << ++ /Type /Page ++ /Parent 2 0 R ++>> ++endobj ++xref ++0 4 ++0000000000 65535 f ++0000000015 00000 n ++0000000068 00000 n ++0000000169 00000 n ++trailer << ++ /Root 1 0 R ++ /Size 4 ++>> ++startxref ++220 ++%%EOF +diff --git a/testing/resources/bigtable_mini.in b/testing/resources/bigtable_mini.in +new file mode 100644 +index 000000000..7e8099264 +--- /dev/null ++++ b/testing/resources/bigtable_mini.in +@@ -0,0 +1,113 @@ ++{{header}} ++{{object 1 0}} << ++ /Type /Catalog ++ /Pages 2 0 R ++>> ++endobj ++{{object 2 0}} << ++ /Type /Pages ++ /Count 1 ++ /Kids [3 0 R] ++>> ++endobj ++{{object 3 0}} << ++ /Type /Page ++ /Parent 2 0 R ++ /Contents 4 0 R ++ /MediaBox [0 0 612 792] ++ /Resources << ++ /ProcSet [/PDF /ImageB /Text] ++ /Font << ++ /F1 5 0 R ++ /F2 6 0 R ++ >> ++ >> ++>> ++endobj ++{{object 4 0}} << ++ {{streamlen}} ++>> ++stream ++q BT ++1 0 0 1 250 667 Tm ++/F2 8.96638 Tf ++-243.635 -15.84 Td ++(f)Tj ++/F1 8.96638 Tf ++4.55491 0 Td ++(f)Tj ++2.87476 0 Td ++(ay)Tj ++7.79253 0 Td ++(,jef)Tj ++11.506 0 Td ++(f,sanjay)Tj ++27.4558 0 Td ++(,wilsonh,k)Tj ++37.1801 0 Td ++(err)Tj ++9.58372 0 Td ++(,m3b,tushar)Tj ++41.8537 0 Td ++(,k)Tj ++11.6349 0 Td ++(es,gruber)Tj ++/F2 8.96638 Tf ++32.9694 0 Td ++(g)Tj ++/F1 8.96638 Tf ++4.55491 0 Td ++(@google.com)Tj ++ET Q ++endstream ++endobj ++{{object 5 0}} << ++ /Type /Font ++ /Subtype /Type1 ++ /BaseFont /Times-Roman ++>> ++endobj ++{{object 6 0}} << ++ /Type /Font ++ /Subtype /Type1 ++ /FirstChar 102 ++ /BaseFont /RFSQHQ+CMSY9 ++ /FontDescriptor 7 0 R ++ /LastChar 103 ++ /Widths [508 508] ++>> ++endobj ++{{object 7 0}} << ++ /Type /FontDescriptor ++ /Ascent 750 ++ /CapHeight 750 ++ /CharSet (/braceleft/braceright) ++ /Descent -250 ++ /Flags 4 ++ /FontBBox [0 -250 440 750] ++ /FontFile3 8 0 R ++ /FontName /RFSQHQ+CMSY9 ++ /ItalicAngle 0 ++ /StemV 65 ++>> ++endobj ++{{object 8 0}} << ++ /Subtype /Type1C ++ /Filter [/ASCII85Decode /FlateDecode] ++ {{streamlen}} ++>> ++stream ++GhQYnA ++43BRuTX#t%4ekD2_c]pSd*g?I_(dmV-o3k<:Veup,U50*Ylr.i;$bHCc:fghe5L>1a\`O%`N>>SLd>,:)GT9aE#>k-A'_h4)!bqJD$jjj@/_bu*BN?.Ud@HV0Y&l1Vg$1F)e^]:6ER3IV(Tbj1;S ++BposQ0Df/MDiTcoYO4BSY\&*%?>$UG7-pseF)R.T'a;@,PN5BX]sWQf<*o6H:RGZ*f^Pj ++qkd,*mVA#1lAJeH\%d!)GAm3[%_gAF5OB8mr>(pE^5_Fj[i?3Bq0X7/D)6E](`9a_gaUoK~> ++endstream ++endobj ++{{xref}} ++{{trailer}} ++{{startxref}} ++%%EOF +diff --git a/testing/resources/bigtable_mini.pdf b/testing/resources/bigtable_mini.pdf +new file mode 100644 +index 000000000..adb3e026d +--- /dev/null ++++ b/testing/resources/bigtable_mini.pdf +@@ -0,0 +1,128 @@ ++%PDF-1.7 ++% ò¤ô ++1 0 obj << ++ /Type /Catalog ++ /Pages 2 0 R ++>> ++endobj ++2 0 obj << ++ /Type /Pages ++ /Count 1 ++ /Kids [3 0 R] ++>> ++endobj ++3 0 obj << ++ /Type /Page ++ /Parent 2 0 R ++ /Contents 4 0 R ++ /MediaBox [0 0 612 792] ++ /Resources << ++ /ProcSet [/PDF /ImageB /Text] ++ /Font << ++ /F1 5 0 R ++ /F2 6 0 R ++ >> ++ >> ++>> ++endobj ++4 0 obj << ++ /Length 374 ++>> ++stream ++q BT ++1 0 0 1 250 667 Tm ++/F2 8.96638 Tf ++-243.635 -15.84 Td ++(f)Tj ++/F1 8.96638 Tf ++4.55491 0 Td ++(f)Tj ++2.87476 0 Td ++(ay)Tj ++7.79253 0 Td ++(,jef)Tj ++11.506 0 Td ++(f,sanjay)Tj ++27.4558 0 Td ++(,wilsonh,k)Tj ++37.1801 0 Td ++(err)Tj ++9.58372 0 Td ++(,m3b,tushar)Tj ++41.8537 0 Td ++(,k)Tj ++11.6349 0 Td ++(es,gruber)Tj ++/F2 8.96638 Tf ++32.9694 0 Td ++(g)Tj ++/F1 8.96638 Tf ++4.55491 0 Td ++(@google.com)Tj ++ET Q ++endstream ++endobj ++5 0 obj << ++ /Type /Font ++ /Subtype /Type1 ++ /BaseFont /Times-Roman ++>> ++endobj ++6 0 obj << ++ /Type /Font ++ /Subtype /Type1 ++ /FirstChar 102 ++ /BaseFont /RFSQHQ+CMSY9 ++ /FontDescriptor 7 0 R ++ /LastChar 103 ++ /Widths [508 508] ++>> ++endobj ++7 0 obj << ++ /Type /FontDescriptor ++ /Ascent 750 ++ /CapHeight 750 ++ /CharSet (/braceleft/braceright) ++ /Descent -250 ++ /Flags 4 ++ /FontBBox [0 -250 440 750] ++ /FontFile3 8 0 R ++ /FontName /RFSQHQ+CMSY9 ++ /ItalicAngle 0 ++ /StemV 65 ++>> ++endobj ++8 0 obj << ++ /Subtype /Type1C ++ /Filter [/ASCII85Decode /FlateDecode] ++ /Length 640 ++>> ++stream ++GhQYnA ++43BRuTX#t%4ekD2_c]pSd*g?I_(dmV-o3k<:Veup,U50*Ylr.i;$bHCc:fghe5L>1a\`O%`N>>SLd>,:)GT9aE#>k-A'_h4)!bqJD$jjj@/_bu*BN?.Ud@HV0Y&l1Vg$1F)e^]:6ER3IV(Tbj1;S ++BposQ0Df/MDiTcoYO4BSY\&*%?>$UG7-pseF)R.T'a;@,PN5BX]sWQf<*o6H:RGZ*f^Pj ++qkd,*mVA#1lAJeH\%d!)GAm3[%_gAF5OB8mr>(pE^5_Fj[i?3Bq0X7/D)6E](`9a_gaUoK~> ++endstream ++endobj ++xref ++0 9 ++0000000000 65535 f ++0000000015 00000 n ++0000000068 00000 n ++0000000131 00000 n ++0000000333 00000 n ++0000000759 00000 n ++0000000837 00000 n ++0000000993 00000 n ++0000001234 00000 n ++trailer << ++ /Root 1 0 R ++ /Size 9 ++>> ++startxref ++1985 ++%%EOF +diff --git a/testing/resources/bookmarks.in b/testing/resources/bookmarks.in +index 793f6ae54..b0489a5f9 100644 +--- a/testing/resources/bookmarks.in ++++ b/testing/resources/bookmarks.in +@@ -2,7 +2,7 @@ + {{object 1 0}} << + /Type /Catalog + /Pages 2 0 R +- /Outlines 14 0 R ++ /Outlines 8 0 R + >> + endobj + {{object 2 0}} << +@@ -19,9 +19,11 @@ endobj + /Type /Page + /Parent 2 0 R + /Resources << +- /Font <> ++ /Font << ++ /F1 5 0 R ++ >> + >> +- /Contents [21 0 R] ++ /Contents [6 0 R] + /MediaBox [0 0 612 792] + >> + endobj +@@ -30,46 +32,24 @@ endobj + /Type /Page + /Parent 2 0 R + /Resources << +- /Font <> ++ /Font << ++ /F1 5 0 R ++ >> + >> +- /Contents [22 0 R] ++ /Contents [7 0 R] + /MediaBox [0 0 612 792] + >> + endobj +-% First bookmark +-{{object 10 0}} << +- /Title (A Good Beginning) +- /Parent 14 0 R +- /Next 11 0 R +- /Dest (foo) +->> +-endobj +-% Last bookmark +-{{object 11 0}} << +- /Title (A Good Ending) +- /Parent 14 0 R +- /Prev 10 0 R +- /Dest (bar) +->> +-endobj +-% Root bookmark +-{{object 14 0}} << +- /Type /Outlines +- /First 10 0 R +- /Last 11 0 R +- /Count 2 +->> +-endobj + % Font resource. +-{{object 15 0}} << ++{{object 5 0}} << + /Type /Font + /Subtype /Type1 + /BaseFont /Arial + >> + endobj + % Content for page 0. +-{{object 21 0}} << +- /Length 0 ++{{object 6 0}} << ++ {{streamlen}} + >> + stream + BT +@@ -79,8 +59,8 @@ ET + endstream + endobj + % Content for page 1. +-{{object 22 0}} << +- /Length 0 ++{{object 7 0}} << ++ {{streamlen}} + >> + stream + BT +@@ -89,6 +69,72 @@ BT + ET + endstream + endobj ++% Root bookmark ++{{object 8 0}} << ++ /Type /Outlines ++ /Count 3 ++ /First 9 0 R ++ /Last 12 0 R ++>> ++endobj ++% First child bookmark (leaf node) ++{{object 9 0}} << ++ /Title (A Good Beginning) ++ /Parent 8 0 R ++ /Next 10 0 R ++ /Dest (foo) ++>> ++endobj ++% Second child bookmark (open) ++{{object 10 0}} << ++ /Title (Open Middle) ++ /Parent 8 0 R ++ /First 11 0 R ++ /Last 11 0 R ++ /Prev 9 0 R ++ /Next 12 0 R ++ /Count 1 ++ /A << ++ /Type /Action ++ /S /URI ++ /URI (https://theplay.test) ++ >> ++>> ++endobj ++% First grandchild bookmark ++{{object 11 0}} << ++ /Title (Open Middle Descendant) ++ /Parent 10 0 R ++ /Dest [3 0 R /XYZ 100 200 0] ++>> ++endobj ++% Third child bookmark (closed) ++{{object 12 0}} << ++ /Title (A Good Closed Ending) ++ /Parent 8 0 R ++ /First 13 0 R ++ /Last 14 0 R ++ /Prev 10 0 R ++ /Count -2 ++ /Dest (bar) ++>> ++endobj ++% Second grandchild bookmark ++{{object 13 0}} << ++ /Title (A Good Closed Ending Descendant) ++ /Parent 12 0 R ++ /Next 14 0 R ++ /Dest (bar) ++>> ++endobj ++% Third grandchild bookmark ++{{object 14 0}} << ++ /Title (A Good Closed Ending Descendant 2) ++ /Parent 12 0 R ++ /Prev 13 0 R ++ /Dest (bar) ++>> ++endobj + {{xref}} + {{trailer}} + {{startxref}} +diff --git a/testing/resources/bookmarks.pdf b/testing/resources/bookmarks.pdf +index 8c2eb5ac7..757f8599c 100644 +--- a/testing/resources/bookmarks.pdf ++++ b/testing/resources/bookmarks.pdf +@@ -3,7 +3,7 @@ + 1 0 obj << + /Type /Catalog + /Pages 2 0 R +- /Outlines 14 0 R ++ /Outlines 8 0 R + >> + endobj + 2 0 obj << +@@ -20,9 +20,11 @@ endobj + /Type /Page + /Parent 2 0 R + /Resources << +- /Font <> ++ /Font << ++ /F1 5 0 R ++ >> + >> +- /Contents [21 0 R] ++ /Contents [6 0 R] + /MediaBox [0 0 612 792] + >> + endobj +@@ -31,46 +33,24 @@ endobj + /Type /Page + /Parent 2 0 R + /Resources << +- /Font <> ++ /Font << ++ /F1 5 0 R ++ >> + >> +- /Contents [22 0 R] ++ /Contents [7 0 R] + /MediaBox [0 0 612 792] + >> + endobj +-% First bookmark +-10 0 obj << +- /Title (A Good Beginning) +- /Parent 14 0 R +- /Next 11 0 R +- /Dest (foo) +->> +-endobj +-% Last bookmark +-11 0 obj << +- /Title (A Good Ending) +- /Parent 14 0 R +- /Prev 10 0 R +- /Dest (bar) +->> +-endobj +-% Root bookmark +-14 0 obj << +- /Type /Outlines +- /First 10 0 R +- /Last 11 0 R +- /Count 2 +->> +-endobj + % Font resource. +-15 0 obj << ++5 0 obj << + /Type /Font + /Subtype /Type1 + /BaseFont /Arial + >> + endobj + % Content for page 0. +-21 0 obj << +- /Length 0 ++6 0 obj << ++ /Length 37 + >> + stream + BT +@@ -80,8 +60,8 @@ ET + endstream + endobj + % Content for page 1. +-22 0 obj << +- /Length 0 ++7 0 obj << ++ /Length 37 + >> + stream + BT +@@ -90,32 +70,93 @@ BT + ET + endstream + endobj ++% Root bookmark ++8 0 obj << ++ /Type /Outlines ++ /Count 3 ++ /First 9 0 R ++ /Last 12 0 R ++>> ++endobj ++% First child bookmark (leaf node) ++9 0 obj << ++ /Title (A Good Beginning) ++ /Parent 8 0 R ++ /Next 10 0 R ++ /Dest (foo) ++>> ++endobj ++% Second child bookmark (open) ++10 0 obj << ++ /Title (Open Middle) ++ /Parent 8 0 R ++ /First 11 0 R ++ /Last 11 0 R ++ /Prev 9 0 R ++ /Next 12 0 R ++ /Count 1 ++ /A << ++ /Type /Action ++ /S /URI ++ /URI (https://theplay.test) ++ >> ++>> ++endobj ++% First grandchild bookmark ++11 0 obj << ++ /Title (Open Middle Descendant) ++ /Parent 10 0 R ++ /Dest [3 0 R /XYZ 100 200 0] ++>> ++endobj ++% Third child bookmark (closed) ++12 0 obj << ++ /Title (A Good Closed Ending) ++ /Parent 8 0 R ++ /First 13 0 R ++ /Last 14 0 R ++ /Prev 10 0 R ++ /Count -2 ++ /Dest (bar) ++>> ++endobj ++% Second grandchild bookmark ++13 0 obj << ++ /Title (A Good Closed Ending Descendant) ++ /Parent 12 0 R ++ /Next 14 0 R ++ /Dest (bar) ++>> ++endobj ++% Third grandchild bookmark ++14 0 obj << ++ /Title (A Good Closed Ending Descendant 2) ++ /Parent 12 0 R ++ /Prev 13 0 R ++ /Dest (bar) ++>> ++endobj + xref +-0 23 ++0 15 + 0000000000 65535 f + 0000000015 00000 n +-0000000087 00000 n +-0000000185 00000 n +-0000000346 00000 n +-0000000000 65535 f +-0000000000 65535 f +-0000000000 65535 f +-0000000000 65535 f +-0000000000 65535 f +-0000000508 00000 n +-0000000620 00000 n +-0000000000 65535 f +-0000000000 65535 f +-0000000729 00000 n +-0000000829 00000 n +-0000000000 65535 f +-0000000000 65535 f +-0000000000 65535 f +-0000000000 65535 f +-0000000000 65535 f +-0000000924 00000 n +-0000001034 00000 n +-trailer<< /Root 1 0 R /Size 23 >> ++0000000086 00000 n ++0000000184 00000 n ++0000000355 00000 n ++0000000527 00000 n ++0000000621 00000 n ++0000000731 00000 n ++0000000835 00000 n ++0000000950 00000 n ++0000001075 00000 n ++0000001310 00000 n ++0000001446 00000 n ++0000001617 00000 n ++0000001756 00000 n ++trailer << ++ /Root 1 0 R ++ /Size 15 ++>> + startxref +-1122 ++1869 + %%EOF +diff --git a/testing/resources/bug_1055869.in b/testing/resources/bug_1055869.in +new file mode 100644 +index 000000000..26cbf5f59 +--- /dev/null ++++ b/testing/resources/bug_1055869.in +@@ -0,0 +1,62 @@ ++{{header}} ++{{include xfa_catalog_1_0.fragment}} ++{{include xfa_object_2_0.fragment}} ++{{include xfa_preamble_3_0.fragment}} ++{{include xfa_config_4_0.fragment}} ++{{object 5 0}} << ++ {{streamlen}} ++>> ++stream ++ ++endstream ++endobj ++{{include xfa_locale_6_0.fragment}} ++{{include xfa_postamble_7_0.fragment}} ++{{include xfa_pages_8_0.fragment}} ++{{xref}} ++{{trailer}} ++{{startxref}} ++%%EOF +diff --git a/testing/resources/bug_1055869.pdf b/testing/resources/bug_1055869.pdf +new file mode 100644 +index 000000000..832d029bc +--- /dev/null ++++ b/testing/resources/bug_1055869.pdf +@@ -0,0 +1,271 @@ ++%PDF-1.7 ++% ò¤ô ++1 0 obj << ++ /AcroForm 2 0 R ++ /Extensions << ++ /ADBE << ++ /BaseVersion /1.7 ++ /ExtensionLevel 8 ++ >> ++ >> ++ /NeedsRendering true ++ /Pages 8 0 R ++ /Type /Catalog ++>> ++endobj ++2 0 obj << ++ /XFA [ ++ (preamble) ++ 3 0 R ++ (config) ++ 4 0 R ++ (template) ++ 5 0 R ++ (localeSet) ++ 6 0 R ++ (postamble) ++ 7 0 R ++ ] ++>> ++endobj ++3 0 obj << ++ /Length 124 ++>> ++stream ++ ++endstream ++endobj ++4 0 obj << ++ /Length 642 ++>> ++stream ++ ++ ++ pdf ++ ++ ++ ++ ++ ++ ++ 1.7 ++ 8 ++ client ++ XFA ++ 1 ++ ++ ++ * ++ ++ pdf ++ ++ ++ ++ ++ required ++ ++ preSubmit ++ ++ ++endstream ++endobj ++5 0 obj << ++ /Length 1361 ++>> ++stream ++ ++endstream ++endobj ++6 0 obj << ++ /Length 3455 ++>> ++stream ++ ++ ++ ++ ++ January ++ February ++ March ++ April ++ May ++ June ++ July ++ August ++ September ++ October ++ November ++ December ++ ++ ++ Jan ++ Feb ++ Mar ++ Apr ++ May ++ Jun ++ Jul ++ Aug ++ Sep ++ Oct ++ Nov ++ Dec ++ ++ ++ Sunday ++ Monday ++ Tuesday ++ Wednesday ++ Thursday ++ Friday ++ Saturday ++ ++ ++ Sun ++ Mon ++ Tue ++ Wed ++ Thu ++ Fri ++ Sat ++ ++ ++ AM ++ PM ++ ++ ++ BC ++ AD ++ ++ ++ ++ EEEE, MMMM D, YYYY ++ MMMM D, YYYY ++ MMM D, YYYY ++ M/D/YY ++ ++ ++ h:MM:SS A Z ++ h:MM:SS A Z ++ h:MM:SS A ++ h:MM A ++ ++ GyMdkHmsSEDFwWahKzZ ++ ++ z,zz9.zzz ++ $z,zz9.99|($z,zz9.99) ++ z,zz9% ++ ++ ++ . ++ , ++ % ++ - ++ 0 ++ ++ ++ $ ++ USD ++ . ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++endstream ++endobj ++7 0 obj << ++ /Length 11 ++>> ++stream ++ ++endstream ++endobj ++8 0 obj << ++ /Type /Pages ++ /Count 1 ++ /Kids [9 0 R] ++>> ++endobj ++9 0 obj << ++ /Type /Page ++ /Parent 8 0 R ++ /MediaBox [0 0 612 792] ++>> ++endobj ++xref ++0 10 ++0000000000 65535 f ++0000000015 00000 n ++0000000199 00000 n ++0000000358 00000 n ++0000000534 00000 n ++0000001228 00000 n ++0000002642 00000 n ++0000006150 00000 n ++0000006212 00000 n ++0000006275 00000 n ++trailer << ++ /Root 1 0 R ++ /Size 10 ++>> ++startxref ++6352 ++%%EOF +diff --git a/testing/resources/bug_1058653.in b/testing/resources/bug_1058653.in +new file mode 100644 +index 000000000..5973c5408 +--- /dev/null ++++ b/testing/resources/bug_1058653.in +@@ -0,0 +1,73 @@ ++{{header}} ++{{include xfa_catalog_1_0.fragment}} ++{{include xfa_object_2_0.fragment}} ++{{include xfa_preamble_3_0.fragment}} ++{{include xfa_config_4_0.fragment}} ++{{object 5 0}} << ++ {{streamlen}} ++>> ++stream ++ ++endstream ++endobj ++{{include xfa_locale_6_0.fragment}} ++{{include xfa_postamble_7_0.fragment}} ++{{include xfa_pages_8_0.fragment}} ++{{xref}} ++{{trailer}} ++{{startxref}} ++%%EOF +diff --git a/testing/resources/bug_1058653.pdf b/testing/resources/bug_1058653.pdf +new file mode 100644 +index 000000000..f278dfb30 +--- /dev/null ++++ b/testing/resources/bug_1058653.pdf +@@ -0,0 +1,282 @@ ++%PDF-1.7 ++% ò¤ô ++1 0 obj << ++ /AcroForm 2 0 R ++ /Extensions << ++ /ADBE << ++ /BaseVersion /1.7 ++ /ExtensionLevel 8 ++ >> ++ >> ++ /NeedsRendering true ++ /Pages 8 0 R ++ /Type /Catalog ++>> ++endobj ++2 0 obj << ++ /XFA [ ++ (preamble) ++ 3 0 R ++ (config) ++ 4 0 R ++ (template) ++ 5 0 R ++ (localeSet) ++ 6 0 R ++ (postamble) ++ 7 0 R ++ ] ++>> ++endobj ++3 0 obj << ++ /Length 124 ++>> ++stream ++ ++endstream ++endobj ++4 0 obj << ++ /Length 642 ++>> ++stream ++ ++ ++ pdf ++ ++ ++ ++ ++ ++ ++ 1.7 ++ 8 ++ client ++ XFA ++ 1 ++ ++ ++ * ++ ++ pdf ++ ++ ++ ++ ++ required ++ ++ preSubmit ++ ++ ++endstream ++endobj ++5 0 obj << ++ /Length 1772 ++>> ++stream ++ ++endstream ++endobj ++6 0 obj << ++ /Length 3455 ++>> ++stream ++ ++ ++ ++ ++ January ++ February ++ March ++ April ++ May ++ June ++ July ++ August ++ September ++ October ++ November ++ December ++ ++ ++ Jan ++ Feb ++ Mar ++ Apr ++ May ++ Jun ++ Jul ++ Aug ++ Sep ++ Oct ++ Nov ++ Dec ++ ++ ++ Sunday ++ Monday ++ Tuesday ++ Wednesday ++ Thursday ++ Friday ++ Saturday ++ ++ ++ Sun ++ Mon ++ Tue ++ Wed ++ Thu ++ Fri ++ Sat ++ ++ ++ AM ++ PM ++ ++ ++ BC ++ AD ++ ++ ++ ++ EEEE, MMMM D, YYYY ++ MMMM D, YYYY ++ MMM D, YYYY ++ M/D/YY ++ ++ ++ h:MM:SS A Z ++ h:MM:SS A Z ++ h:MM:SS A ++ h:MM A ++ ++ GyMdkHmsSEDFwWahKzZ ++ ++ z,zz9.zzz ++ $z,zz9.99|($z,zz9.99) ++ z,zz9% ++ ++ ++ . ++ , ++ % ++ - ++ 0 ++ ++ ++ $ ++ USD ++ . ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++endstream ++endobj ++7 0 obj << ++ /Length 11 ++>> ++stream ++ ++endstream ++endobj ++8 0 obj << ++ /Type /Pages ++ /Count 1 ++ /Kids [9 0 R] ++>> ++endobj ++9 0 obj << ++ /Type /Page ++ /Parent 8 0 R ++ /MediaBox [0 0 612 792] ++>> ++endobj ++xref ++0 10 ++0000000000 65535 f ++0000000015 00000 n ++0000000199 00000 n ++0000000358 00000 n ++0000000534 00000 n ++0000001228 00000 n ++0000003053 00000 n ++0000006561 00000 n ++0000006623 00000 n ++0000006686 00000 n ++trailer << ++ /Root 1 0 R ++ /Size 10 ++>> ++startxref ++6763 ++%%EOF +diff --git a/testing/resources/bug_1124998.pdf b/testing/resources/bug_1124998.pdf +new file mode 100644 +index 0000000000000000000000000000000000000000..4368222efc9ea6604b87478e090056a09209b307 +GIT binary patch +literal 3018 +zcmb_eU5Fc16!r(Cj1T@$@#jG=tLv=jcUEJ)3?6$6MTAiJ_*^Zmc +zgqfQrTLcj+78S%Fe5s-p?1P9>Un*1-t5Bf|#h(WslnN?{RzWPHf_m;GX}Y^b`_O@# +z+%Gg}_pHa7vY@v-^B +zR;0wmiS1K`iCojGwIiQ1pv<;-v&AR@g@Q{T5Sq7 +zm7E6HWfnF3IEHncQ_^Tu(vgb7r3a*J7EiNaneRddP%0Rda26pvS$8-qGB@;CJ}<>Q +zVopPv{p8rGpD$aw$GtZgdg`y$uiiiK#IgG>`PzE)vp+KL>^=GBi?3Y2`EdNh{rfln +z{`}|HY&!VIw+~%@@aXr?y!z}zPk!;~p?^+|9;_a{;mQ41zIx}$D;_&=?dikT!zUiM +zzd3E+`ti4io_+bZLws@iqpLFu`&w@txoL;O0(?;;!{t6i&-xPMjt4u4&~>`awEzP*Bs^ZiGhx(l>Jpy6 +z3-PE0$K10xaj~bjVoCy}GVmwyxxbr{GGZ53EXi8SCg?q`aVKUXTzKAZuvn>t4JSxK +zkpxk~bFC#VPEPnp28smWdW5a4zMFC1bOhh* +zrQi-Dxx%UOpLGa&jVK!(5QDj1m^Ul9LxoWyHg< +zzT$I4Q&x&7M7smkBB#ON-mf5%GGu^^yqaBsOzi@~DktyaylG?HZnslyEfq$~m{^vD +zRf;K9fJ2E_1MYN`Aif#&C5HG5XT*XymDrSW!v^*jFiEMnuc!#_M1E3MkWlO_g)N>N +z8tS1y#X2_(Yygf1?!nB48|JlK7UkqEB$QTMCHA7M1Q(VU}sB2I)vvl6);kjq*Xtttyl6RhqS)B=$F&qg2 +zLp;~6iL%2X^9S+3kQQ=23!B{Mb(T-EdaTa7le#bR%dR`Ya!&3j9;-R6I+r1?3Afjc +zUER-jCy5*$V4Wkf2?=fu1R0;XlX(eUpzp!~4GIy|=A7IXWd>$zA4O61dv&gVwMML} +zrW#dB%&JC=s!GX&)oN8UNL4doP+F`~)vQvg!M_m$RR<;*LP(=ks_VDGnuO-D7{?(hC(y2j%Gl}*3(S7k(RMG;L$XJN4}58BL^DX +z2s%=P=lnekVg<(&hR_p<)&k+FpcbNTi-V_%XfbP;`h;$%P;i>VWnD + +literal 0 +HcmV?d00001 + +diff --git a/testing/resources/bug_1229106.in b/testing/resources/bug_1229106.in +new file mode 100644 +index 000000000..7ffe9bfb1 +--- /dev/null ++++ b/testing/resources/bug_1229106.in +@@ -0,0 +1,68 @@ ++{{header}} ++{{object 1 0}} << ++ /Type /Catalog ++ /Pages 2 0 R ++>> ++endobj ++{{object 2 0}} << ++ /Type /Pages ++ /Count 4 ++ /Kids [3 0 R 3 0 R 5 0 R 5 0 R] ++>> ++endobj ++{{object 3 0}} << ++ /Type /Page ++ /Parent 2 0 R ++ /MediaBox [0 0 612 792] ++ /Rotate 90 ++ /Contents 4 0 R ++>> ++endobj ++{{object 4 0}} << ++ {{streamlen}} ++>> ++stream ++q ++0 1 -1 0 792 0 cm ++0 0 0 rg ++100 400 150 50 re f ++1 0 0 rg ++0 180 100 50 re f ++0 1 0 rg ++0 742 100 50 re f ++0 0 1 rg ++692 742 100 50 re f ++1 0 1 rg ++692 180 100 50 re f ++Q ++endstream ++endobj ++{{object 5 0}} << ++ /Type /Page ++ /Parent 2 0 R ++ /MediaBox [0 0 792 612] ++ /Contents 6 0 R ++>> ++endobj ++{{object 6 0}} << ++ {{streamlen}} ++>> ++stream ++q ++0 0 0 rg ++100 220 150 50 re f ++1 0 0 rg ++0 0 100 50 re f ++0 1 0 rg ++0 562 100 50 re f ++0 0 1 rg ++692 562 100 50 re f ++1 0 1 rg ++692 0 100 50 re f ++Q ++endstream ++endobj ++{{xref}} ++{{trailer}} ++{{startxref}} ++%%EOF +diff --git a/testing/resources/bug_1229106.pdf b/testing/resources/bug_1229106.pdf +new file mode 100644 +index 000000000..e0d625c07 +--- /dev/null ++++ b/testing/resources/bug_1229106.pdf +@@ -0,0 +1,81 @@ ++%PDF-1.7 ++% ò¤ô ++1 0 obj << ++ /Type /Catalog ++ /Pages 2 0 R ++>> ++endobj ++2 0 obj << ++ /Type /Pages ++ /Count 4 ++ /Kids [3 0 R 3 0 R 5 0 R 5 0 R] ++>> ++endobj ++3 0 obj << ++ /Type /Page ++ /Parent 2 0 R ++ /MediaBox [0 0 612 792] ++ /Rotate 90 ++ /Contents 4 0 R ++>> ++endobj ++4 0 obj << ++ /Length 163 ++>> ++stream ++q ++0 1 -1 0 792 0 cm ++0 0 0 rg ++100 400 150 50 re f ++1 0 0 rg ++0 180 100 50 re f ++0 1 0 rg ++0 742 100 50 re f ++0 0 1 rg ++692 742 100 50 re f ++1 0 1 rg ++692 180 100 50 re f ++Q ++endstream ++endobj ++5 0 obj << ++ /Type /Page ++ /Parent 2 0 R ++ /MediaBox [0 0 792 612] ++ /Contents 6 0 R ++>> ++endobj ++6 0 obj << ++ /Length 141 ++>> ++stream ++q ++0 0 0 rg ++100 220 150 50 re f ++1 0 0 rg ++0 0 100 50 re f ++0 1 0 rg ++0 562 100 50 re f ++0 0 1 rg ++692 562 100 50 re f ++1 0 1 rg ++692 0 100 50 re f ++Q ++endstream ++endobj ++xref ++0 7 ++0000000000 65535 f ++0000000015 00000 n ++0000000068 00000 n ++0000000149 00000 n ++0000000257 00000 n ++0000000472 00000 n ++0000000567 00000 n ++trailer << ++ /Root 1 0 R ++ /Size 7 ++>> ++startxref ++760 ++%%EOF +diff --git a/testing/resources/bug_1296920.in b/testing/resources/bug_1296920.in +new file mode 100644 +index 000000000..5c9da584b +--- /dev/null ++++ b/testing/resources/bug_1296920.in +@@ -0,0 +1,115 @@ ++{{header}} ++{{object 1 0}} << ++ /Type /Catalog ++ /Pages 2 0 R ++ /StructTreeRoot 6 0 R ++ /MarkInfo << ++ /Marked true ++ >> ++>> ++endobj ++{{object 2 0}} << ++ /Type /Pages ++ /Count 1 ++ /Kids [3 0 R] ++>> ++endobj ++{{object 3 0}} << ++ /Type /Page ++ /Contents 4 0 R ++ /MediaBox [0 0 100 100] ++ /Parent 2 0 R ++ /Resources << ++ /Font << ++ /F1 5 0 R ++ >> ++ >> ++ /StructParents 0 ++>> ++endobj ++{{object 4 0}} << ++ {{streamlen}} ++>> ++stream ++/P <> BDC ++BT ++/F1 12 Tf ++1 0 0 1 20 50 Tm ++(Hello) Tj ++ET ++EMC ++/P <> BDC ++BT ++/F1 12 Tf ++1 0 0 1 50 50 Tm ++(World) Tj ++ET ++EMC ++endstream ++endobj ++{{object 5 0}} << ++ /Type /Font ++ /Subtype /Type1 ++ /BaseFont /Helvetica ++>> ++endobj ++{{object 6 0}} << ++ /Type /StructTreeRoot ++ /K [9 0 R] ++ /ParentTree 7 0 R ++>> ++endobj ++{{object 7 0}} << ++ /Nums [0 8 0 R 1 10 0 R] ++>> ++endobj ++{{object 8 0}} ++[12 0 R 13 0 R] ++endobj ++{{object 9 0}} << ++ /Type /StructElem ++ /S /Document ++ /P 6 0 R ++ /K [10 0 R] ++>> ++endobj ++{{object 10 0}} << ++ /Type /StructElem ++ /S /Part ++ /P 9 0 R ++ /K [11 0 R] ++>> ++endobj ++{{object 11 0}} << ++ /Type /StructElem ++ /S /Div ++ /P 10 0 R ++ /K [12 0 R 13 0 R 14 0 R] ++>> ++endobj ++{{object 12 0}} << ++ /Type /StructElem ++ /S /P ++ /P 11 0 R ++ /K 1 ++ /Pg 3 0 R ++>> ++endobj ++{{object 13 0}} << ++ /Type /StructElem ++ /S /P ++ /P 11 0 R ++ /K 2 ++ /Pg 3 0 R ++>> ++endobj ++{{object 14 0}} << ++ /Type /StructElem ++ /S /Div ++ /P 11 0 R ++>> ++endobj ++{{xref}} ++{{trailer}} ++{{startxref}} ++%%EOF +diff --git a/testing/resources/bug_1296920.pdf b/testing/resources/bug_1296920.pdf +new file mode 100644 +index 000000000..92d778cbc +--- /dev/null ++++ b/testing/resources/bug_1296920.pdf +@@ -0,0 +1,136 @@ ++%PDF-1.7 ++% ò¤ô ++1 0 obj << ++ /Type /Catalog ++ /Pages 2 0 R ++ /StructTreeRoot 6 0 R ++ /MarkInfo << ++ /Marked true ++ >> ++>> ++endobj ++2 0 obj << ++ /Type /Pages ++ /Count 1 ++ /Kids [3 0 R] ++>> ++endobj ++3 0 obj << ++ /Type /Page ++ /Contents 4 0 R ++ /MediaBox [0 0 100 100] ++ /Parent 2 0 R ++ /Resources << ++ /Font << ++ /F1 5 0 R ++ >> ++ >> ++ /StructParents 0 ++>> ++endobj ++4 0 obj << ++ /Length 134 ++>> ++stream ++/P <> BDC ++BT ++/F1 12 Tf ++1 0 0 1 20 50 Tm ++(Hello) Tj ++ET ++EMC ++/P <> BDC ++BT ++/F1 12 Tf ++1 0 0 1 50 50 Tm ++(World) Tj ++ET ++EMC ++endstream ++endobj ++5 0 obj << ++ /Type /Font ++ /Subtype /Type1 ++ /BaseFont /Helvetica ++>> ++endobj ++6 0 obj << ++ /Type /StructTreeRoot ++ /K [9 0 R] ++ /ParentTree 7 0 R ++>> ++endobj ++7 0 obj << ++ /Nums [0 8 0 R 1 10 0 R] ++>> ++endobj ++8 0 obj ++[12 0 R 13 0 R] ++endobj ++9 0 obj << ++ /Type /StructElem ++ /S /Document ++ /P 6 0 R ++ /K [10 0 R] ++>> ++endobj ++10 0 obj << ++ /Type /StructElem ++ /S /Part ++ /P 9 0 R ++ /K [11 0 R] ++>> ++endobj ++11 0 obj << ++ /Type /StructElem ++ /S /Div ++ /P 10 0 R ++ /K [12 0 R 13 0 R 14 0 R] ++>> ++endobj ++12 0 obj << ++ /Type /StructElem ++ /S /P ++ /P 11 0 R ++ /K 1 ++ /Pg 3 0 R ++>> ++endobj ++13 0 obj << ++ /Type /StructElem ++ /S /P ++ /P 11 0 R ++ /K 2 ++ /Pg 3 0 R ++>> ++endobj ++14 0 obj << ++ /Type /StructElem ++ /S /Div ++ /P 11 0 R ++>> ++endobj ++xref ++0 15 ++0000000000 65535 f ++0000000015 00000 n ++0000000129 00000 n ++0000000192 00000 n ++0000000363 00000 n ++0000000549 00000 n ++0000000625 00000 n ++0000000703 00000 n ++0000000751 00000 n ++0000000782 00000 n ++0000000863 00000 n ++0000000941 00000 n ++0000001033 00000 n ++0000001114 00000 n ++0000001195 00000 n ++trailer << ++ /Root 1 0 R ++ /Size 15 ++>> ++startxref ++1259 ++%%EOF +diff --git a/testing/resources/bug_1301.pdf b/testing/resources/bug_1301.pdf +index 285c7f794..8c6e45fa0 100644 +--- a/testing/resources/bug_1301.pdf ++++ b/testing/resources/bug_1301.pdf +@@ -217,7 +217,7 @@ endobj + endobj + 9 0 obj << + /Type /Page +- /Parent 2 0 R ++ /Parent 8 0 R + /MediaBox [0 0 612 792] + >> + endobj +diff --git a/testing/resources/bug_1302455.in b/testing/resources/bug_1302455.in +new file mode 100644 +index 000000000..1e18f12eb +--- /dev/null ++++ b/testing/resources/bug_1302455.in +@@ -0,0 +1,75 @@ ++{{header}} ++{{object 1 0}} << ++ /Type /Catalog ++ /Pages 2 0 R ++ /AcroForm << ++ /DR 4 0 R ++ /Fields [6 0 R 7 0 R] ++ >> ++>> ++endobj ++{{object 2 0}} << ++ /Type /Pages ++ /Kids [3 0 R] ++ /Count 1 ++>> ++endobj ++{{object 3 0}} << ++ /Type /Page ++ /Parent 2 0 R ++ /Annots [6 0 R 7 0 R] ++ /MediaBox [0 0 300 300] ++ /Resources 4 0 R ++>> ++endobj ++{{object 4 0}} << ++ /Font << ++ /F1 5 0 R ++ >> ++>> ++endobj ++{{object 5 0}} << ++ /Type /Font ++ /Subtype /Type1 ++ /BaseFont /Helvetica ++>> ++endobj ++{{object 6 0}} << ++ /Type /Annot ++ /Subtype /Widget ++ /FT /Tx ++ /AP << ++ /N 8 0 R ++ >> ++ /F 4 ++ /Rect [100 100 200 130] ++ /T (Text Box 1) ++>> ++endobj ++{{object 7 0}} << ++ /Type /Annot ++ /Subtype /Widget ++ /FT /Tx ++ /AP << ++ /N 8 0 R ++ >> ++ /F 4 ++ /Rect [100 160 200 190] ++ /T (Text Box 2) ++>> ++endobj ++{{object 8 0}} << ++ /Type /XObject ++ /Subtype /Form ++ /BBox [0 0 100 30] ++ {{streamlen}} ++>> ++stream ++1 0 0 rg ++0 0 100 30 re f ++endstream ++endobj ++{{xref}} ++{{trailer}} ++{{startxref}} ++%%EOF +diff --git a/testing/resources/bug_1302455.pdf b/testing/resources/bug_1302455.pdf +new file mode 100644 +index 000000000..a9782c4b5 +--- /dev/null ++++ b/testing/resources/bug_1302455.pdf +@@ -0,0 +1,90 @@ ++%PDF-1.7 ++% ò¤ô ++1 0 obj << ++ /Type /Catalog ++ /Pages 2 0 R ++ /AcroForm << ++ /DR 4 0 R ++ /Fields [6 0 R 7 0 R] ++ >> ++>> ++endobj ++2 0 obj << ++ /Type /Pages ++ /Kids [3 0 R] ++ /Count 1 ++>> ++endobj ++3 0 obj << ++ /Type /Page ++ /Parent 2 0 R ++ /Annots [6 0 R 7 0 R] ++ /MediaBox [0 0 300 300] ++ /Resources 4 0 R ++>> ++endobj ++4 0 obj << ++ /Font << ++ /F1 5 0 R ++ >> ++>> ++endobj ++5 0 obj << ++ /Type /Font ++ /Subtype /Type1 ++ /BaseFont /Helvetica ++>> ++endobj ++6 0 obj << ++ /Type /Annot ++ /Subtype /Widget ++ /FT /Tx ++ /AP << ++ /N 8 0 R ++ >> ++ /F 4 ++ /Rect [100 100 200 130] ++ /T (Text Box 1) ++>> ++endobj ++7 0 obj << ++ /Type /Annot ++ /Subtype /Widget ++ /FT /Tx ++ /AP << ++ /N 8 0 R ++ >> ++ /F 4 ++ /Rect [100 160 200 190] ++ /T (Text Box 2) ++>> ++endobj ++8 0 obj << ++ /Type /XObject ++ /Subtype /Form ++ /BBox [0 0 100 30] ++ /Length 25 ++>> ++stream ++1 0 0 rg ++0 0 100 30 re f ++endstream ++endobj ++xref ++0 9 ++0000000000 65535 f ++0000000015 00000 n ++0000000128 00000 n ++0000000191 00000 n ++0000000311 00000 n ++0000000362 00000 n ++0000000438 00000 n ++0000000581 00000 n ++0000000724 00000 n ++trailer << ++ /Root 1 0 R ++ /Size 9 ++>> ++startxref ++855 ++%%EOF +diff --git a/testing/resources/bug_1324189.in b/testing/resources/bug_1324189.in +new file mode 100644 +index 000000000..45fceba28 +--- /dev/null ++++ b/testing/resources/bug_1324189.in +@@ -0,0 +1,16 @@ ++{{header}} ++{{object 1 0}} < ++ /Type /Catalog ++>> ++{{xref}} ++{{trailer}} ++{{startxref}} ++%%EOF ++{{xref}} ++trailer << ++ /Root 1 0 R ++ /Prev -200 ++ {{trailersize}} ++>> ++{{startxref}} ++%%EOF +diff --git a/testing/resources/bug_1324189.pdf b/testing/resources/bug_1324189.pdf +new file mode 100644 +index 000000000..965239883 +--- /dev/null ++++ b/testing/resources/bug_1324189.pdf +@@ -0,0 +1,28 @@ ++%PDF-1.7 ++% ò¤ô ++1 0 obj < ++ /Type /Catalog ++>> ++xref ++0 2 ++0000000000 65535 f ++0000000015 00000 n ++trailer << ++ /Root 1 0 R ++ /Size 2 ++>> ++startxref ++45 ++%%EOF ++xref ++0 2 ++0000000000 65535 f ++0000000015 00000 n ++trailer << ++ /Root 1 0 R ++ /Prev -200 ++ /Size 2 ++>> ++startxref ++151 ++%%EOF +diff --git a/testing/resources/bug_1324503.in b/testing/resources/bug_1324503.in +new file mode 100644 +index 000000000..a46d1420d +--- /dev/null ++++ b/testing/resources/bug_1324503.in +@@ -0,0 +1,16 @@ ++{{header}} ++{{object 1 0}} < ++ /Type /Catalog ++>> ++{{xref}} ++{{trailer}} ++{{startxref}} ++%%EOF ++{{xref}} ++trailer << ++ /Root 1 0 R ++ /XRefStm -1 ++ {{trailersize}} ++>> ++{{startxref}} ++%%EOF +diff --git a/testing/resources/bug_1324503.pdf b/testing/resources/bug_1324503.pdf +new file mode 100644 +index 000000000..2ce742599 +--- /dev/null ++++ b/testing/resources/bug_1324503.pdf +@@ -0,0 +1,28 @@ ++%PDF-1.7 ++% ò¤ô ++1 0 obj < ++ /Type /Catalog ++>> ++xref ++0 2 ++0000000000 65535 f ++0000000015 00000 n ++trailer << ++ /Root 1 0 R ++ /Size 2 ++>> ++startxref ++45 ++%%EOF ++xref ++0 2 ++0000000000 65535 f ++0000000015 00000 n ++trailer << ++ /Root 1 0 R ++ /XRefStm -1 ++ /Size 2 ++>> ++startxref ++151 ++%%EOF +diff --git a/testing/resources/bug_1327884.pdf b/testing/resources/bug_1327884.pdf +new file mode 100644 +index 000000000..0e28c0616 +--- /dev/null ++++ b/testing/resources/bug_1327884.pdf +@@ -0,0 +1,6 @@ ++%PDF ++1 0 obj<>>>2 0 obj< ++30 0 obj<<>>stream ++